From a2f342591712f07923ea53740778724fbe7e2b32 Mon Sep 17 00:00:00 2001 From: spiderr Date: Tue, 31 Jul 2018 23:49:37 -0400 Subject: major reorg and move everything to includes/ and Deny all in .htaccess --- .htaccess | 1 - Date/Calc.php | 1531 --- Date/Human.php | 184 - Date/TimeZone.php | 3632 ----- Date/TimeZoneWindows.php | 109 - Date/index.php | 4 - PHPAsync.php | 236 - bitexcel/BitExcel.php | 365 - bitexcel/BitExcelAsync.php | 59 - cufon/cufon-yui.js | 1379 -- dBug/dBug.php | 531 - daemonize/.htaccess | 1 - daemonize/daemonize.php | 205 - daemonize/daemonize_lib.php | 34 - daemonize/is_up.sh | 3 - datasets/regions/us/class.USStates.php | 82 - diff.php | 1027 -- geocalc/GeoCalc.class.php | 238 - geocalc/README | 148 - getid3/changelog.txt | 2488 ---- getid3/demos/demo.audioinfo.class.php | 319 - getid3/demos/demo.basic.php | 38 - getid3/demos/demo.browse.php | 669 - getid3/demos/demo.cache.dbm.php | 29 - getid3/demos/demo.cache.mysql.php | 29 - getid3/demos/demo.joinmp3.php | 96 - getid3/demos/demo.mimeonly.php | 53 - getid3/demos/demo.mysql.php | 2130 --- getid3/demos/demo.simple.php | 53 - getid3/demos/demo.simple.write.php | 54 - getid3/demos/demo.write.php | 267 - getid3/demos/index.php | 1 - getid3/dependencies.txt | 24 - getid3/getid3/extension.cache.dbm.php | 222 - getid3/getid3/extension.cache.mysql.php | 171 - getid3/getid3/getid3.lib.php | 1323 -- getid3/getid3/getid3.php | 1305 -- getid3/getid3/module.archive.gzip.php | 249 - getid3/getid3/module.archive.rar.php | 32 - getid3/getid3/module.archive.szip.php | 97 - getid3/getid3/module.archive.tar.php | 167 - getid3/getid3/module.archive.zip.php | 415 - getid3/getid3/module.audio-video.asf.php | 1635 --- getid3/getid3/module.audio-video.bink.php | 70 - getid3/getid3/module.audio-video.flv.php | 497 - getid3/getid3/module.audio-video.matroska.php | 78 - getid3/getid3/module.audio-video.mpeg.php | 292 - getid3/getid3/module.audio-video.nsv.php | 224 - getid3/getid3/module.audio-video.quicktime.php | 1302 -- getid3/getid3/module.audio-video.real.php | 528 - getid3/getid3/module.audio-video.riff.php | 1995 --- getid3/getid3/module.audio-video.swf.php | 153 - getid3/getid3/module.audio.aac.php | 538 - getid3/getid3/module.audio.ac3.php | 497 - getid3/getid3/module.audio.au.php | 163 - getid3/getid3/module.audio.avr.php | 125 - getid3/getid3/module.audio.bonk.php | 213 - getid3/getid3/module.audio.flac.php | 309 - getid3/getid3/module.audio.la.php | 227 - getid3/getid3/module.audio.lpac.php | 125 - getid3/getid3/module.audio.midi.php | 520 - getid3/getid3/module.audio.mod.php | 101 - getid3/getid3/module.audio.monkey.php | 202 - getid3/getid3/module.audio.mp3.php | 1937 --- getid3/getid3/module.audio.mpc.php | 296 - getid3/getid3/module.audio.ogg.php | 543 - getid3/getid3/module.audio.optimfrog.php | 408 - getid3/getid3/module.audio.rkau.php | 92 - getid3/getid3/module.audio.shorten.php | 179 - getid3/getid3/module.audio.tta.php | 107 - getid3/getid3/module.audio.voc.php | 205 - getid3/getid3/module.audio.vqf.php | 159 - getid3/getid3/module.audio.wavpack.php | 372 - getid3/getid3/module.graphic.bmp.php | 683 - getid3/getid3/module.graphic.gif.php | 183 - getid3/getid3/module.graphic.jpg.php | 72 - getid3/getid3/module.graphic.pcd.php | 130 - getid3/getid3/module.graphic.png.php | 519 - getid3/getid3/module.graphic.svg.php | 52 - getid3/getid3/module.graphic.tiff.php | 221 - getid3/getid3/module.misc.exe.php | 59 - getid3/getid3/module.misc.iso.php | 386 - getid3/getid3/module.tag.apetag.php | 284 - getid3/getid3/module.tag.id3v1.php | 356 - getid3/getid3/module.tag.id3v2.php | 3130 ----- getid3/getid3/module.tag.lyrics3.php | 271 - getid3/getid3/write.apetag.php | 228 - getid3/getid3/write.id3v1.php | 104 - getid3/getid3/write.id3v2.php | 2038 --- getid3/getid3/write.lyrics3.php | 78 - getid3/getid3/write.metaflac.php | 167 - getid3/getid3/write.php | 592 - getid3/getid3/write.real.php | 295 - getid3/getid3/write.vorbiscomment.php | 124 - getid3/license.commercial.txt | 27 - getid3/license.txt | 340 - getid3/readme.txt | 355 - getid3/structure.txt | 2251 ---- htmlparser/html_parser_inc.php | 492 - htmlparser/htmlgrammar.cmp | 1 - htmlparser/htmlgrammar.dat | 1176 -- htmlparser/htmlgrammarparser.inc | 478 - htmlparser/readme.eng.txt | 19 - htmlparser/rebuildgrammar.php | 13 - htmlpure/Filter/CNBC.php | 56 - htmlpure/Filter/SafeIframe.php | 22 - htmlpure/Filter/YouTube.php | 46 - includes/.htaccess | 1 + includes/Date/Calc.php | 1531 +++ includes/Date/Human.php | 184 + includes/Date/TimeZone.php | 3632 +++++ includes/Date/TimeZoneWindows.php | 109 + includes/Date/index.php | 4 + includes/PHPAsync.php | 236 + includes/bitexcel/BitExcel.php | 365 + includes/bitexcel/BitExcelAsync.php | 59 + includes/cufon/cufon-yui.js | 1379 ++ includes/dBug/dBug.php | 531 + includes/daemonize/.htaccess | 1 + includes/daemonize/daemonize.php | 205 + includes/daemonize/daemonize_lib.php | 34 + includes/daemonize/is_up.sh | 3 + includes/datasets/regions/us/class.USStates.php | 82 + includes/diff.php | 1027 ++ includes/geocalc/GeoCalc.class.php | 238 + includes/geocalc/README | 148 + includes/getid3/changelog.txt | 2488 ++++ includes/getid3/demos/demo.audioinfo.class.php | 319 + includes/getid3/demos/demo.basic.php | 38 + includes/getid3/demos/demo.browse.php | 669 + includes/getid3/demos/demo.cache.dbm.php | 29 + includes/getid3/demos/demo.cache.mysql.php | 29 + includes/getid3/demos/demo.joinmp3.php | 96 + includes/getid3/demos/demo.mimeonly.php | 53 + includes/getid3/demos/demo.mysql.php | 2130 +++ includes/getid3/demos/demo.simple.php | 53 + includes/getid3/demos/demo.simple.write.php | 54 + includes/getid3/demos/demo.write.php | 267 + includes/getid3/demos/index.php | 1 + includes/getid3/dependencies.txt | 24 + includes/getid3/getid3/extension.cache.dbm.php | 222 + includes/getid3/getid3/extension.cache.mysql.php | 171 + includes/getid3/getid3/getid3.lib.php | 1323 ++ includes/getid3/getid3/getid3.php | 1305 ++ includes/getid3/getid3/module.archive.gzip.php | 249 + includes/getid3/getid3/module.archive.rar.php | 32 + includes/getid3/getid3/module.archive.szip.php | 97 + includes/getid3/getid3/module.archive.tar.php | 167 + includes/getid3/getid3/module.archive.zip.php | 415 + includes/getid3/getid3/module.audio-video.asf.php | 1635 +++ includes/getid3/getid3/module.audio-video.bink.php | 70 + includes/getid3/getid3/module.audio-video.flv.php | 497 + .../getid3/getid3/module.audio-video.matroska.php | 78 + includes/getid3/getid3/module.audio-video.mpeg.php | 292 + includes/getid3/getid3/module.audio-video.nsv.php | 224 + .../getid3/getid3/module.audio-video.quicktime.php | 1302 ++ includes/getid3/getid3/module.audio-video.real.php | 528 + includes/getid3/getid3/module.audio-video.riff.php | 1995 +++ includes/getid3/getid3/module.audio-video.swf.php | 153 + includes/getid3/getid3/module.audio.aac.php | 538 + includes/getid3/getid3/module.audio.ac3.php | 497 + includes/getid3/getid3/module.audio.au.php | 163 + includes/getid3/getid3/module.audio.avr.php | 125 + includes/getid3/getid3/module.audio.bonk.php | 213 + includes/getid3/getid3/module.audio.flac.php | 309 + includes/getid3/getid3/module.audio.la.php | 227 + includes/getid3/getid3/module.audio.lpac.php | 125 + includes/getid3/getid3/module.audio.midi.php | 520 + includes/getid3/getid3/module.audio.mod.php | 101 + includes/getid3/getid3/module.audio.monkey.php | 202 + includes/getid3/getid3/module.audio.mp3.php | 1937 +++ includes/getid3/getid3/module.audio.mpc.php | 296 + includes/getid3/getid3/module.audio.ogg.php | 543 + includes/getid3/getid3/module.audio.optimfrog.php | 408 + includes/getid3/getid3/module.audio.rkau.php | 92 + includes/getid3/getid3/module.audio.shorten.php | 179 + includes/getid3/getid3/module.audio.tta.php | 107 + includes/getid3/getid3/module.audio.voc.php | 205 + includes/getid3/getid3/module.audio.vqf.php | 159 + includes/getid3/getid3/module.audio.wavpack.php | 372 + includes/getid3/getid3/module.graphic.bmp.php | 683 + includes/getid3/getid3/module.graphic.gif.php | 183 + includes/getid3/getid3/module.graphic.jpg.php | 72 + includes/getid3/getid3/module.graphic.pcd.php | 130 + includes/getid3/getid3/module.graphic.png.php | 519 + includes/getid3/getid3/module.graphic.svg.php | 52 + includes/getid3/getid3/module.graphic.tiff.php | 221 + includes/getid3/getid3/module.misc.exe.php | 59 + includes/getid3/getid3/module.misc.iso.php | 386 + includes/getid3/getid3/module.tag.apetag.php | 284 + includes/getid3/getid3/module.tag.id3v1.php | 356 + includes/getid3/getid3/module.tag.id3v2.php | 3130 +++++ includes/getid3/getid3/module.tag.lyrics3.php | 271 + includes/getid3/getid3/write.apetag.php | 228 + includes/getid3/getid3/write.id3v1.php | 104 + includes/getid3/getid3/write.id3v2.php | 2038 +++ includes/getid3/getid3/write.lyrics3.php | 78 + includes/getid3/getid3/write.metaflac.php | 167 + includes/getid3/getid3/write.php | 592 + includes/getid3/getid3/write.real.php | 295 + includes/getid3/getid3/write.vorbiscomment.php | 124 + includes/getid3/license.commercial.txt | 27 + includes/getid3/license.txt | 340 + includes/getid3/readme.txt | 355 + includes/getid3/structure.txt | 2251 ++++ includes/htmlparser/html_parser_inc.php | 492 + includes/htmlparser/htmlgrammar.cmp | 1 + includes/htmlparser/htmlgrammar.dat | 1176 ++ includes/htmlparser/htmlgrammarparser.inc | 478 + includes/htmlparser/readme.eng.txt | 19 + includes/htmlparser/rebuildgrammar.php | 13 + includes/htmlpure/Filter/CNBC.php | 56 + includes/htmlpure/Filter/SafeIframe.php | 22 + includes/htmlpure/Filter/YouTube.php | 46 + includes/is_email.php | 529 + includes/jpeg_metadata_tk/COPYING.txt | 340 + includes/jpeg_metadata_tk/EXIF.php | 2764 ++++ includes/jpeg_metadata_tk/EXIF_Makernote.php | 334 + includes/jpeg_metadata_tk/EXIF_Tags.php | 915 ++ includes/jpeg_metadata_tk/Edit_File_Info.php | 575 + includes/jpeg_metadata_tk/IPTC.php | 691 + includes/jpeg_metadata_tk/JFIF.php | 438 + includes/jpeg_metadata_tk/JPEG.php | 973 ++ includes/jpeg_metadata_tk/Makernotes/Pentax.php | 353 + includes/jpeg_metadata_tk/Makernotes/agfa.php | 117 + includes/jpeg_metadata_tk/Makernotes/canon.php | 748 ++ includes/jpeg_metadata_tk/Makernotes/casio.php | 575 + includes/jpeg_metadata_tk/Makernotes/epson.php | 119 + includes/jpeg_metadata_tk/Makernotes/fujifilm.php | 344 + .../jpeg_metadata_tk/Makernotes/konica_minolta.php | 745 ++ includes/jpeg_metadata_tk/Makernotes/kyocera.php | 241 + includes/jpeg_metadata_tk/Makernotes/nikon.php | 731 + includes/jpeg_metadata_tk/Makernotes/olympus.php | 486 + includes/jpeg_metadata_tk/Makernotes/panasonic.php | 292 + includes/jpeg_metadata_tk/Makernotes/ricoh.php | 415 + includes/jpeg_metadata_tk/Makernotes/sony.php | 244 + includes/jpeg_metadata_tk/PIM.php | 279 + includes/jpeg_metadata_tk/Photoshop_File_Info.php | 2498 ++++ includes/jpeg_metadata_tk/Photoshop_IRB.php | 1514 +++ includes/jpeg_metadata_tk/PictureInfo.php | 284 + includes/jpeg_metadata_tk/Toolkit_Version.php | 50 + includes/jpeg_metadata_tk/Unicode.php | 1227 ++ includes/jpeg_metadata_tk/Write_File_Info.php | 196 + includes/jpeg_metadata_tk/XML.php | 396 + includes/jpeg_metadata_tk/XMP.php | 1063 ++ .../documentation/Camera_List_1.0.pdf | Bin 0 -> 79294 bytes .../documentation/Edit_File_Info_Example.php | 248 + .../jpeg_metadata_tk/documentation/Example.php | 218 + .../jpeg_metadata_tk/documentation/TIFFExample.php | 121 + .../jpeg_metadata_tk/documentation/changes.html | 257 + .../jpeg_metadata_tk/documentation/css_terms.html | 171 + .../documentation/edit_write_file_info.html | 272 + .../jpeg_metadata_tk/documentation/example.html | 131 + .../jpeg_metadata_tk/documentation/examples.html | 62 + includes/jpeg_metadata_tk/documentation/exif.html | 284 + includes/jpeg_metadata_tk/documentation/index.html | 60 + includes/jpeg_metadata_tk/documentation/intro.html | 75 + includes/jpeg_metadata_tk/documentation/jfif.html | 237 + includes/jpeg_metadata_tk/documentation/jpeg.html | 303 + .../jpeg_metadata_tk/documentation/photoshop.html | 262 + .../documentation/photoshop_file_info.html | 162 + .../documentation/picture_info.html | 133 + includes/jpeg_metadata_tk/documentation/style.css | 29 + .../documentation/tiffexample.html | 57 + includes/jpeg_metadata_tk/documentation/todo.html | 65 + includes/jpeg_metadata_tk/documentation/xmp.html | 228 + includes/jpeg_metadata_tk/get_JFXX_thumb.php | 109 + includes/jpeg_metadata_tk/get_casio_thumb.php | 126 + includes/jpeg_metadata_tk/get_exif_thumb.php | 98 + includes/jpeg_metadata_tk/get_minolta_thumb.php | 195 + includes/jpeg_metadata_tk/get_ps_thumb.php | 176 + includes/jpeg_metadata_tk/pjmt_utils.php | 150 + includes/mailman.cfg | 15 + includes/mailman_lib.php | 366 + includes/mailman_lib.py | 27 + includes/markdown.php | 1732 +++ includes/mime_lib.php | 32 + includes/mimetypes.php | 133 + includes/pclzip_lib.php | 4018 ++++++ includes/pear/Archive/Tar.php | 1909 +++ includes/pear/Auth.php | 1330 ++ includes/pear/Auth/Anonymous.php | 138 + includes/pear/Auth/Auth.php | 30 + includes/pear/Auth/Container.php | 262 + includes/pear/Auth/Container/Array.php | 161 + includes/pear/Auth/Container/DB.php | 639 + includes/pear/Auth/Container/DBLite.php | 320 + includes/pear/Auth/Container/File.php | 314 + includes/pear/Auth/Container/IMAP.php | 210 + includes/pear/Auth/Container/KADM5.php | 171 + includes/pear/Auth/Container/LDAP.php | 766 ++ includes/pear/Auth/Container/MDB.php | 625 + includes/pear/Auth/Container/MDB2.php | 624 + includes/pear/Auth/Container/Multiple.php | 188 + includes/pear/Auth/Container/NetVPOPMaild.php | 129 + includes/pear/Auth/Container/PEAR.php | 165 + includes/pear/Auth/Container/POP3.php | 145 + includes/pear/Auth/Container/RADIUS.php | 182 + includes/pear/Auth/Container/SAP.php | 179 + includes/pear/Auth/Container/SMBPasswd.php | 182 + includes/pear/Auth/Container/SOAP.php | 229 + includes/pear/Auth/Container/SOAP5.php | 268 + includes/pear/Auth/Container/vpopmail.php | 88 + includes/pear/Auth/Controller.php | 302 + includes/pear/Auth/Frontend/Html.php | 142 + includes/pear/Console/Getopt.php | 360 + includes/pear/HTTP.php | 548 + includes/pear/HTTP/Download.php | 1243 ++ includes/pear/HTTP/Download/Archive.php | 122 + includes/pear/HTTP/Download/PgLOB.php | 177 + includes/pear/HTTP/Header.php | 537 + includes/pear/HTTP/Header/Cache.php | 238 + includes/pear/Image/GraphViz.php | 1005 ++ includes/pear/MIME/Type.php | 598 + includes/pear/MIME/Type/Extension.php | 292 + includes/pear/MIME/Type/Parameter.php | 170 + includes/pear/OS/Guess.php | 338 + includes/pear/PEAR.php | 1040 ++ includes/pear/PEAR/Autoloader.php | 218 + includes/pear/PEAR/Builder.php | 518 + includes/pear/PEAR/ChannelFile.php | 1559 +++ includes/pear/PEAR/ChannelFile/Parser.php | 68 + includes/pear/PEAR/Command.php | 414 + includes/pear/PEAR/Command/Auth.php | 81 + includes/pear/PEAR/Command/Auth.xml | 30 + includes/pear/PEAR/Command/Build.php | 85 + includes/pear/PEAR/Command/Build.xml | 10 + includes/pear/PEAR/Command/Channels.php | 883 ++ includes/pear/PEAR/Command/Channels.xml | 123 + includes/pear/PEAR/Command/Common.php | 273 + includes/pear/PEAR/Command/Config.php | 414 + includes/pear/PEAR/Command/Config.xml | 92 + includes/pear/PEAR/Command/Install.php | 1268 ++ includes/pear/PEAR/Command/Install.xml | 276 + includes/pear/PEAR/Command/Mirror.php | 139 + includes/pear/PEAR/Command/Mirror.xml | 18 + includes/pear/PEAR/Command/Package.php | 1124 ++ includes/pear/PEAR/Command/Package.xml | 237 + includes/pear/PEAR/Command/Pickle.php | 421 + includes/pear/PEAR/Command/Pickle.xml | 36 + includes/pear/PEAR/Command/Registry.php | 1145 ++ includes/pear/PEAR/Command/Registry.xml | 58 + includes/pear/PEAR/Command/Remote.php | 810 ++ includes/pear/PEAR/Command/Remote.xml | 109 + includes/pear/PEAR/Command/Test.php | 337 + includes/pear/PEAR/Command/Test.xml | 54 + includes/pear/PEAR/Common.php | 837 ++ includes/pear/PEAR/Config.php | 2092 +++ includes/pear/PEAR/Dependency2.php | 1362 ++ includes/pear/PEAR/DependencyDB.php | 757 ++ includes/pear/PEAR/Downloader.php | 1766 +++ includes/pear/PEAR/Downloader/Package.php | 1988 +++ includes/pear/PEAR/ErrorStack.php | 985 ++ includes/pear/PEAR/ErrorStack5.php | 921 ++ includes/pear/PEAR/Exception.php | 389 + includes/pear/PEAR/FixPHP5PEARWarnings.php | 7 + includes/pear/PEAR/Frontend.php | 228 + includes/pear/PEAR/Frontend/CLI.php | 751 ++ includes/pear/PEAR/Installer.php | 1753 +++ includes/pear/PEAR/Installer/Role.php | 267 + includes/pear/PEAR/Installer/Role/Cfg.php | 106 + includes/pear/PEAR/Installer/Role/Cfg.xml | 15 + includes/pear/PEAR/Installer/Role/Common.php | 189 + includes/pear/PEAR/Installer/Role/Data.php | 28 + includes/pear/PEAR/Installer/Role/Data.xml | 15 + includes/pear/PEAR/Installer/Role/Doc.php | 28 + includes/pear/PEAR/Installer/Role/Doc.xml | 15 + includes/pear/PEAR/Installer/Role/Ext.php | 28 + includes/pear/PEAR/Installer/Role/Ext.xml | 12 + includes/pear/PEAR/Installer/Role/Php.php | 28 + includes/pear/PEAR/Installer/Role/Php.xml | 15 + includes/pear/PEAR/Installer/Role/Script.php | 28 + includes/pear/PEAR/Installer/Role/Script.xml | 15 + includes/pear/PEAR/Installer/Role/Src.php | 34 + includes/pear/PEAR/Installer/Role/Src.xml | 12 + includes/pear/PEAR/Installer/Role/Test.php | 28 + includes/pear/PEAR/Installer/Role/Test.xml | 15 + includes/pear/PEAR/Installer/Role/Www.php | 28 + includes/pear/PEAR/Installer/Role/Www.xml | 15 + includes/pear/PEAR/PackageFile.php | 503 + includes/pear/PEAR/PackageFile/Generator/v1.php | 1284 ++ includes/pear/PEAR/PackageFile/Generator/v2.php | 893 ++ includes/pear/PEAR/PackageFile/Parser/v1.php | 459 + includes/pear/PEAR/PackageFile/Parser/v2.php | 113 + includes/pear/PEAR/PackageFile/v1.php | 1612 +++ includes/pear/PEAR/PackageFile/v2.php | 2049 +++ includes/pear/PEAR/PackageFile/v2/Validator.php | 2154 +++ includes/pear/PEAR/PackageFile/v2/rw.php | 1607 +++ includes/pear/PEAR/Packager.php | 201 + includes/pear/PEAR/REST.php | 483 + includes/pear/PEAR/REST/10.php | 871 ++ includes/pear/PEAR/REST/11.php | 341 + includes/pear/PEAR/REST/13.php | 299 + includes/pear/PEAR/REST/14.php | 120 + includes/pear/PEAR/Registry.php | 2339 ++++ includes/pear/PEAR/RunTest.php | 973 ++ includes/pear/PEAR/Start.php | 427 + includes/pear/PEAR/Start/CLI.php | 616 + includes/pear/PEAR/Task/Common.php | 202 + includes/pear/PEAR/Task/Postinstallscript.php | 323 + includes/pear/PEAR/Task/Postinstallscript/rw.php | 169 + includes/pear/PEAR/Task/Replace.php | 176 + includes/pear/PEAR/Task/Replace/rw.php | 61 + includes/pear/PEAR/Task/Unixeol.php | 77 + includes/pear/PEAR/Task/Unixeol/rw.php | 56 + includes/pear/PEAR/Task/Windowseol.php | 77 + includes/pear/PEAR/Task/Windowseol/rw.php | 56 + includes/pear/PEAR/Validate.php | 629 + includes/pear/PEAR/Validator/PECL.php | 63 + includes/pear/PEAR/Warning.php | 379 + includes/pear/PEAR/XMLParser.php | 253 + includes/pear/PEAR5.php | 33 + includes/pear/Structures/Graph.php | 154 + .../Structures/Graph/Manipulator/AcyclicTest.php | 136 + .../Graph/Manipulator/TopologicalSorter.php | 153 + includes/pear/Structures/Graph/Node.php | 342 + includes/pear/System.php | 624 + includes/pear/System/Command.php | 598 + includes/pear/Text/Diff.php | 453 + includes/pear/Text/Diff/Engine/native.php | 438 + includes/pear/Text/Diff/Engine/shell.php | 164 + includes/pear/Text/Diff/Engine/string.php | 250 + includes/pear/Text/Diff/Engine/xdiff.php | 66 + includes/pear/Text/Diff/Mapped.php | 55 + includes/pear/Text/Diff/Renderer.php | 237 + includes/pear/Text/Diff/Renderer/context.php | 77 + includes/pear/Text/Diff/Renderer/inline.php | 172 + includes/pear/Text/Diff/Renderer/unified.php | 67 + includes/pear/Text/Diff/ThreeWay.php | 276 + includes/pear/Text/Diff3.php | 276 + includes/pear/Text/Wiki.php | 1550 +++ includes/pear/Text/Wiki/Creole.php | 150 + includes/pear/Text/Wiki/Default.php | 27 + includes/pear/Text/Wiki/Parse.php | 264 + includes/pear/Text/Wiki/Parse/Creole/Address.php | 67 + .../pear/Text/Wiki/Parse/Creole/Blockquote.php | 176 + includes/pear/Text/Wiki/Parse/Creole/Box.php | 81 + includes/pear/Text/Wiki/Parse/Creole/Break.php | 73 + includes/pear/Text/Wiki/Parse/Creole/Center.php | 78 + includes/pear/Text/Wiki/Parse/Creole/Delimiter.php | 68 + includes/pear/Text/Wiki/Parse/Creole/Emphasis.php | 83 + includes/pear/Text/Wiki/Parse/Creole/Footnote.php | 83 + includes/pear/Text/Wiki/Parse/Creole/Heading.php | 97 + includes/pear/Text/Wiki/Parse/Creole/Horiz.php | 58 + includes/pear/Text/Wiki/Parse/Creole/Image.php | 69 + includes/pear/Text/Wiki/Parse/Creole/List.php | 244 + includes/pear/Text/Wiki/Parse/Creole/Newline.php | 60 + includes/pear/Text/Wiki/Parse/Creole/Paragraph.php | 139 + includes/pear/Text/Wiki/Parse/Creole/Prefilter.php | 54 + .../pear/Text/Wiki/Parse/Creole/Preformatted.php | 68 + includes/pear/Text/Wiki/Parse/Creole/Raw.php | 61 + includes/pear/Text/Wiki/Parse/Creole/Strong.php | 84 + includes/pear/Text/Wiki/Parse/Creole/Subscript.php | 75 + .../pear/Text/Wiki/Parse/Creole/Superscript.php | 75 + includes/pear/Text/Wiki/Parse/Creole/Table.php | 207 + includes/pear/Text/Wiki/Parse/Creole/Tighten.php | 37 + includes/pear/Text/Wiki/Parse/Creole/Trim.php | 75 + includes/pear/Text/Wiki/Parse/Creole/Tt.php | 78 + includes/pear/Text/Wiki/Parse/Creole/Underline.php | 83 + includes/pear/Text/Wiki/Parse/Creole/Url.php | 109 + includes/pear/Text/Wiki/Parse/Creole/Wikilink.php | 322 + includes/pear/Text/Wiki/Parse/Default/Anchor.php | 87 + .../pear/Text/Wiki/Parse/Default/Blockquote.php | 180 + includes/pear/Text/Wiki/Parse/Default/Bold.php | 79 + includes/pear/Text/Wiki/Parse/Default/Break.php | 72 + includes/pear/Text/Wiki/Parse/Default/Center.php | 78 + includes/pear/Text/Wiki/Parse/Default/Code.php | 99 + .../pear/Text/Wiki/Parse/Default/Colortext.php | 89 + includes/pear/Text/Wiki/Parse/Default/Deflist.php | 122 + .../pear/Text/Wiki/Parse/Default/Delimiter.php | 80 + includes/pear/Text/Wiki/Parse/Default/Embed.php | 106 + includes/pear/Text/Wiki/Parse/Default/Emphasis.php | 85 + includes/pear/Text/Wiki/Parse/Default/Freelink.php | 134 + includes/pear/Text/Wiki/Parse/Default/Function.php | 141 + includes/pear/Text/Wiki/Parse/Default/Heading.php | 107 + includes/pear/Text/Wiki/Parse/Default/Horiz.php | 70 + includes/pear/Text/Wiki/Parse/Default/Html.php | 75 + includes/pear/Text/Wiki/Parse/Default/Image.php | 136 + includes/pear/Text/Wiki/Parse/Default/Include.php | 100 + .../pear/Text/Wiki/Parse/Default/Interwiki.php | 138 + includes/pear/Text/Wiki/Parse/Default/Italic.php | 85 + includes/pear/Text/Wiki/Parse/Default/List.php | 262 + includes/pear/Text/Wiki/Parse/Default/Newline.php | 75 + .../pear/Text/Wiki/Parse/Default/Paragraph.php | 116 + .../pear/Text/Wiki/Parse/Default/Phplookup.php | 73 + .../pear/Text/Wiki/Parse/Default/Prefilter.php | 84 + includes/pear/Text/Wiki/Parse/Default/Raw.php | 73 + includes/pear/Text/Wiki/Parse/Default/Revise.php | 145 + includes/pear/Text/Wiki/Parse/Default/Smiley.php | 157 + includes/pear/Text/Wiki/Parse/Default/Strong.php | 86 + .../pear/Text/Wiki/Parse/Default/Subscript.php | 79 + .../pear/Text/Wiki/Parse/Default/Superscript.php | 79 + includes/pear/Text/Wiki/Parse/Default/Table.php | 226 + includes/pear/Text/Wiki/Parse/Default/Tighten.php | 49 + includes/pear/Text/Wiki/Parse/Default/Toc.php | 130 + includes/pear/Text/Wiki/Parse/Default/Tt.php | 84 + .../pear/Text/Wiki/Parse/Default/Underline.php | 79 + includes/pear/Text/Wiki/Parse/Default/Url.php | 281 + includes/pear/Text/Wiki/Parse/Default/Wikilink.php | 204 + includes/pear/Text/Wiki/Render.php | 218 + includes/pear/Text/Wiki/Render/Creole.php | 16 + includes/pear/Text/Wiki/Render/Creole/Address.php | 29 + includes/pear/Text/Wiki/Render/Creole/Anchor.php | 23 + .../pear/Text/Wiki/Render/Creole/Blockquote.php | 47 + includes/pear/Text/Wiki/Render/Creole/Bold.php | 23 + includes/pear/Text/Wiki/Render/Creole/Box.php | 30 + includes/pear/Text/Wiki/Render/Creole/Break.php | 24 + includes/pear/Text/Wiki/Render/Creole/Center.php | 30 + includes/pear/Text/Wiki/Render/Creole/Code.php | 23 + .../pear/Text/Wiki/Render/Creole/Colortext.php | 23 + includes/pear/Text/Wiki/Render/Creole/Deflist.php | 23 + .../pear/Text/Wiki/Render/Creole/Delimiter.php | 23 + includes/pear/Text/Wiki/Render/Creole/Embed.php | 29 + includes/pear/Text/Wiki/Render/Creole/Emphasis.php | 23 + includes/pear/Text/Wiki/Render/Creole/Freelink.php | 9 + includes/pear/Text/Wiki/Render/Creole/Function.php | 23 + includes/pear/Text/Wiki/Render/Creole/Heading.php | 16 + includes/pear/Text/Wiki/Render/Creole/Horiz.php | 23 + includes/pear/Text/Wiki/Render/Creole/Html.php | 23 + includes/pear/Text/Wiki/Render/Creole/Image.php | 26 + includes/pear/Text/Wiki/Render/Creole/Include.php | 16 + .../pear/Text/Wiki/Render/Creole/Interwiki.php | 23 + includes/pear/Text/Wiki/Render/Creole/Italic.php | 23 + includes/pear/Text/Wiki/Render/Creole/List.php | 53 + includes/pear/Text/Wiki/Render/Creole/Newline.php | 12 + .../pear/Text/Wiki/Render/Creole/Paragraph.php | 29 + .../pear/Text/Wiki/Render/Creole/Phplookup.php | 25 + .../pear/Text/Wiki/Render/Creole/Prefilter.php | 10 + .../pear/Text/Wiki/Render/Creole/Preformatted.php | 27 + includes/pear/Text/Wiki/Render/Creole/Raw.php | 33 + includes/pear/Text/Wiki/Render/Creole/Revise.php | 23 + includes/pear/Text/Wiki/Render/Creole/Strong.php | 23 + .../pear/Text/Wiki/Render/Creole/Subscript.php | 23 + .../pear/Text/Wiki/Render/Creole/Superscript.php | 23 + includes/pear/Text/Wiki/Render/Creole/Table.php | 70 + includes/pear/Text/Wiki/Render/Creole/Tighten.php | 10 + includes/pear/Text/Wiki/Render/Creole/Toc.php | 23 + includes/pear/Text/Wiki/Render/Creole/Tt.php | 29 + .../pear/Text/Wiki/Render/Creole/Underline.php | 23 + includes/pear/Text/Wiki/Render/Creole/Url.php | 40 + includes/pear/Text/Wiki/Render/Creole/Wikilink.php | 43 + includes/pear/Text/Wiki/Render/Latex.php | 90 + includes/pear/Text/Wiki/Render/Latex/Anchor.php | 33 + .../pear/Text/Wiki/Render/Latex/Blockquote.php | 36 + includes/pear/Text/Wiki/Render/Latex/Bold.php | 28 + includes/pear/Text/Wiki/Render/Latex/Box.php | 54 + includes/pear/Text/Wiki/Render/Latex/Break.php | 24 + includes/pear/Text/Wiki/Render/Latex/Center.php | 33 + includes/pear/Text/Wiki/Render/Latex/Code.php | 26 + includes/pear/Text/Wiki/Render/Latex/Colortext.php | 58 + includes/pear/Text/Wiki/Render/Latex/Deflist.php | 53 + includes/pear/Text/Wiki/Render/Latex/Delimiter.php | 25 + includes/pear/Text/Wiki/Render/Latex/Embed.php | 23 + includes/pear/Text/Wiki/Render/Latex/Emphasis.php | 29 + includes/pear/Text/Wiki/Render/Latex/Font.php | 73 + includes/pear/Text/Wiki/Render/Latex/Freelink.php | 6 + includes/pear/Text/Wiki/Render/Latex/Function.php | 23 + includes/pear/Text/Wiki/Render/Latex/Heading.php | 33 + includes/pear/Text/Wiki/Render/Latex/Horiz.php | 23 + includes/pear/Text/Wiki/Render/Latex/Html.php | 25 + includes/pear/Text/Wiki/Render/Latex/Image.php | 70 + includes/pear/Text/Wiki/Render/Latex/Include.php | 8 + includes/pear/Text/Wiki/Render/Latex/Interwiki.php | 58 + includes/pear/Text/Wiki/Render/Latex/Italic.php | 28 + includes/pear/Text/Wiki/Render/Latex/List.php | 76 + includes/pear/Text/Wiki/Render/Latex/Newline.php | 12 + includes/pear/Text/Wiki/Render/Latex/Page.php | 48 + includes/pear/Text/Wiki/Render/Latex/Paragraph.php | 31 + includes/pear/Text/Wiki/Render/Latex/Phplookup.php | 34 + includes/pear/Text/Wiki/Render/Latex/Plugin.php | 49 + includes/pear/Text/Wiki/Render/Latex/Prefilter.php | 56 + .../pear/Text/Wiki/Render/Latex/Preformatted.php | 48 + includes/pear/Text/Wiki/Render/Latex/Raw.php | 23 + includes/pear/Text/Wiki/Render/Latex/Revise.php | 38 + includes/pear/Text/Wiki/Render/Latex/Smiley.php | 44 + .../pear/Text/Wiki/Render/Latex/Specialchar.php | 54 + includes/pear/Text/Wiki/Render/Latex/Strong.php | 30 + includes/pear/Text/Wiki/Render/Latex/Subscript.php | 54 + .../pear/Text/Wiki/Render/Latex/Superscript.php | 31 + includes/pear/Text/Wiki/Render/Latex/Table.php | 99 + includes/pear/Text/Wiki/Render/Latex/Tighten.php | 9 + includes/pear/Text/Wiki/Render/Latex/Titlebar.php | 54 + includes/pear/Text/Wiki/Render/Latex/Toc.php | 30 + includes/pear/Text/Wiki/Render/Latex/Tt.php | 30 + includes/pear/Text/Wiki/Render/Latex/Underline.php | 30 + includes/pear/Text/Wiki/Render/Latex/Url.php | 41 + includes/pear/Text/Wiki/Render/Latex/Wikilink.php | 60 + includes/pear/Text/Wiki/Render/Plain.php | 16 + includes/pear/Text/Wiki/Render/Plain/Anchor.php | 23 + .../pear/Text/Wiki/Render/Plain/Blockquote.php | 39 + includes/pear/Text/Wiki/Render/Plain/Bold.php | 23 + includes/pear/Text/Wiki/Render/Plain/Box.php | 48 + includes/pear/Text/Wiki/Render/Plain/Break.php | 24 + includes/pear/Text/Wiki/Render/Plain/Center.php | 23 + includes/pear/Text/Wiki/Render/Plain/Code.php | 24 + includes/pear/Text/Wiki/Render/Plain/Colortext.php | 23 + includes/pear/Text/Wiki/Render/Plain/Deflist.php | 59 + includes/pear/Text/Wiki/Render/Plain/Delimiter.php | 23 + includes/pear/Text/Wiki/Render/Plain/Embed.php | 23 + includes/pear/Text/Wiki/Render/Plain/Emphasis.php | 23 + includes/pear/Text/Wiki/Render/Plain/Font.php | 44 + includes/pear/Text/Wiki/Render/Plain/Freelink.php | 23 + includes/pear/Text/Wiki/Render/Plain/Function.php | 39 + includes/pear/Text/Wiki/Render/Plain/Heading.php | 14 + includes/pear/Text/Wiki/Render/Plain/Horiz.php | 23 + includes/pear/Text/Wiki/Render/Plain/Html.php | 24 + includes/pear/Text/Wiki/Render/Plain/Image.php | 22 + includes/pear/Text/Wiki/Render/Plain/Include.php | 8 + includes/pear/Text/Wiki/Render/Plain/Interwiki.php | 29 + includes/pear/Text/Wiki/Render/Plain/Italic.php | 23 + includes/pear/Text/Wiki/Render/Plain/List.php | 68 + includes/pear/Text/Wiki/Render/Plain/Newline.php | 12 + includes/pear/Text/Wiki/Render/Plain/Page.php | 48 + includes/pear/Text/Wiki/Render/Plain/Paragraph.php | 31 + includes/pear/Text/Wiki/Render/Plain/Phplookup.php | 25 + includes/pear/Text/Wiki/Render/Plain/Plugin.php | 49 + includes/pear/Text/Wiki/Render/Plain/Prefilter.php | 24 + .../pear/Text/Wiki/Render/Plain/Preformatted.php | 48 + includes/pear/Text/Wiki/Render/Plain/Raw.php | 23 + includes/pear/Text/Wiki/Render/Plain/Revise.php | 24 + includes/pear/Text/Wiki/Render/Plain/Smiley.php | 44 + .../pear/Text/Wiki/Render/Plain/Specialchar.php | 54 + includes/pear/Text/Wiki/Render/Plain/Strong.php | 24 + includes/pear/Text/Wiki/Render/Plain/Subscript.php | 48 + .../pear/Text/Wiki/Render/Plain/Superscript.php | 23 + includes/pear/Text/Wiki/Render/Plain/Table.php | 65 + includes/pear/Text/Wiki/Render/Plain/Tighten.php | 10 + includes/pear/Text/Wiki/Render/Plain/Titlebar.php | 54 + includes/pear/Text/Wiki/Render/Plain/Toc.php | 39 + includes/pear/Text/Wiki/Render/Plain/Tt.php | 24 + includes/pear/Text/Wiki/Render/Plain/Underline.php | 23 + includes/pear/Text/Wiki/Render/Plain/Url.php | 29 + includes/pear/Text/Wiki/Render/Plain/Wikilink.php | 24 + includes/pear/Text/Wiki/Render/Xhtml.php | 107 + includes/pear/Text/Wiki/Render/Xhtml/Address.php | 54 + includes/pear/Text/Wiki/Render/Xhtml/Anchor.php | 48 + .../pear/Text/Wiki/Render/Xhtml/Blockquote.php | 72 + includes/pear/Text/Wiki/Render/Xhtml/Bold.php | 57 + includes/pear/Text/Wiki/Render/Xhtml/Box.php | 62 + includes/pear/Text/Wiki/Render/Xhtml/Break.php | 52 + includes/pear/Text/Wiki/Render/Xhtml/Center.php | 62 + includes/pear/Text/Wiki/Render/Xhtml/Code.php | 133 + includes/pear/Text/Wiki/Render/Xhtml/Colortext.php | 79 + includes/pear/Text/Wiki/Render/Xhtml/Deflist.php | 87 + includes/pear/Text/Wiki/Render/Xhtml/Delimiter.php | 46 + includes/pear/Text/Wiki/Render/Xhtml/Embed.php | 46 + includes/pear/Text/Wiki/Render/Xhtml/Emphasis.php | 58 + includes/pear/Text/Wiki/Render/Xhtml/Font.php | 83 + includes/pear/Text/Wiki/Render/Xhtml/Freelink.php | 35 + includes/pear/Text/Wiki/Render/Xhtml/Function.php | 108 + includes/pear/Text/Wiki/Render/Xhtml/Heading.php | 88 + includes/pear/Text/Wiki/Render/Xhtml/Horiz.php | 51 + includes/pear/Text/Wiki/Render/Xhtml/Html.php | 47 + includes/pear/Text/Wiki/Render/Xhtml/Image.php | 184 + includes/pear/Text/Wiki/Render/Xhtml/Include.php | 32 + includes/pear/Text/Wiki/Render/Xhtml/Interwiki.php | 103 + includes/pear/Text/Wiki/Render/Xhtml/Italic.php | 57 + includes/pear/Text/Wiki/Render/Xhtml/List.php | 172 + includes/pear/Text/Wiki/Render/Xhtml/Newline.php | 35 + includes/pear/Text/Wiki/Render/Xhtml/Page.php | 46 + includes/pear/Text/Wiki/Render/Xhtml/Paragraph.php | 59 + includes/pear/Text/Wiki/Render/Xhtml/Phplookup.php | 81 + includes/pear/Text/Wiki/Render/Xhtml/Plugin.php | 47 + includes/pear/Text/Wiki/Render/Xhtml/Prefilter.php | 34 + .../pear/Text/Wiki/Render/Xhtml/Preformatted.php | 47 + includes/pear/Text/Wiki/Render/Xhtml/Raw.php | 46 + includes/pear/Text/Wiki/Render/Xhtml/Revise.php | 68 + includes/pear/Text/Wiki/Render/Xhtml/Smiley.php | 74 + .../pear/Text/Wiki/Render/Xhtml/Specialchar.php | 52 + includes/pear/Text/Wiki/Render/Xhtml/Strong.php | 58 + includes/pear/Text/Wiki/Render/Xhtml/Subscript.php | 57 + .../pear/Text/Wiki/Render/Xhtml/Superscript.php | 57 + includes/pear/Text/Wiki/Render/Xhtml/Table.php | 140 + includes/pear/Text/Wiki/Render/Xhtml/Tighten.php | 34 + includes/pear/Text/Wiki/Render/Xhtml/Titlebar.php | 57 + includes/pear/Text/Wiki/Render/Xhtml/Toc.php | 115 + includes/pear/Text/Wiki/Render/Xhtml/Tt.php | 58 + includes/pear/Text/Wiki/Render/Xhtml/Underline.php | 57 + includes/pear/Text/Wiki/Render/Xhtml/Url.php | 131 + includes/pear/Text/Wiki/Render/Xhtml/Wikilink.php | 177 + includes/pear/XML/Util.php | 911 ++ includes/phpcontrib_lib.php | 76 + includes/phpcoord/phpcoord-2.3.php | 785 ++ includes/phpcoord/readme-2.3.txt | 52 + includes/phpcoord/test-2.3.php | 211 + includes/phplot.php | 6688 ++++++++++ includes/phpmailer/README | 102 + includes/phpmailer/class.phpmailer.php | 1721 +++ includes/phpmailer/class.pop3.php | 437 + includes/phpmailer/class.smtp.php | 1062 ++ includes/phpmailer/language/phpmailer.lang-br.php | 23 + includes/phpmailer/language/phpmailer.lang-ca.php | 24 + includes/phpmailer/language/phpmailer.lang-cz.php | 26 + includes/phpmailer/language/phpmailer.lang-de.php | 25 + includes/phpmailer/language/phpmailer.lang-dk.php | 26 + includes/phpmailer/language/phpmailer.lang-en.php | 25 + includes/phpmailer/language/phpmailer.lang-es.php | 25 + includes/phpmailer/language/phpmailer.lang-fi.php | 25 + includes/phpmailer/language/phpmailer.lang-fo.php | 27 + includes/phpmailer/language/phpmailer.lang-fr.php | 26 + includes/phpmailer/language/phpmailer.lang-hu.php | 25 + includes/phpmailer/language/phpmailer.lang-it.php | 29 + includes/phpmailer/language/phpmailer.lang-ja.php | 27 + includes/phpmailer/language/phpmailer.lang-nl.php | 25 + includes/phpmailer/language/phpmailer.lang-no.php | 25 + includes/phpmailer/language/phpmailer.lang-pl.php | 26 + includes/phpmailer/language/phpmailer.lang-ro.php | 24 + includes/phpmailer/language/phpmailer.lang-ru.php | 25 + includes/phpmailer/language/phpmailer.lang-se.php | 26 + includes/phpmailer/language/phpmailer.lang-tr.php | 27 + includes/phpsniff/index.php | 6 + includes/phpsniff/phpSniff.class.php | 907 ++ includes/phpsniff/phpSniff.core.php | 547 + includes/simplepie/idn/idna_convert.class.php | 969 ++ includes/simplepie/idn/npdata.ser | 1 + includes/simplepie/simplepie.inc | 13316 +++++++++++++++++++ includes/spyc/README | 159 + includes/spyc/examples/yaml-dump.php | 25 + includes/spyc/examples/yaml-load.php | 25 + includes/spyc/php4/5to4.php | 16 + includes/spyc/php4/spyc.php4 | 1023 ++ includes/spyc/php4/test.php4 | 162 + includes/spyc/spyc.php | 1024 ++ includes/spyc/spyc.yaml | 196 + includes/spyc/tests/DumpTest.php | 58 + includes/spyc/tests/IndentTest.php | 57 + includes/spyc/tests/ParseTest.php | 305 + includes/spyc/tests/failing1.yaml | 2 + includes/spyc/tests/indent_1.yaml | 53 + includes/spyc/tests/quotes.yaml | 8 + includes/tar.class.php | 467 + includes/template_biticon_updater.sh | 340 + includes/tree.php | 135 + includes/zip_lib.php | 805 ++ is_email.php | 529 - jpeg_metadata_tk/COPYING.txt | 340 - jpeg_metadata_tk/EXIF.php | 2764 ---- jpeg_metadata_tk/EXIF_Makernote.php | 334 - jpeg_metadata_tk/EXIF_Tags.php | 915 -- jpeg_metadata_tk/Edit_File_Info.php | 575 - jpeg_metadata_tk/IPTC.php | 691 - jpeg_metadata_tk/JFIF.php | 438 - jpeg_metadata_tk/JPEG.php | 973 -- jpeg_metadata_tk/Makernotes/Pentax.php | 353 - jpeg_metadata_tk/Makernotes/agfa.php | 117 - jpeg_metadata_tk/Makernotes/canon.php | 748 -- jpeg_metadata_tk/Makernotes/casio.php | 575 - jpeg_metadata_tk/Makernotes/epson.php | 119 - jpeg_metadata_tk/Makernotes/fujifilm.php | 344 - jpeg_metadata_tk/Makernotes/konica_minolta.php | 745 -- jpeg_metadata_tk/Makernotes/kyocera.php | 241 - jpeg_metadata_tk/Makernotes/nikon.php | 731 - jpeg_metadata_tk/Makernotes/olympus.php | 486 - jpeg_metadata_tk/Makernotes/panasonic.php | 292 - jpeg_metadata_tk/Makernotes/ricoh.php | 415 - jpeg_metadata_tk/Makernotes/sony.php | 244 - jpeg_metadata_tk/PIM.php | 279 - jpeg_metadata_tk/Photoshop_File_Info.php | 2498 ---- jpeg_metadata_tk/Photoshop_IRB.php | 1514 --- jpeg_metadata_tk/PictureInfo.php | 284 - jpeg_metadata_tk/Toolkit_Version.php | 50 - jpeg_metadata_tk/Unicode.php | 1227 -- jpeg_metadata_tk/Write_File_Info.php | 196 - jpeg_metadata_tk/XML.php | 396 - jpeg_metadata_tk/XMP.php | 1063 -- jpeg_metadata_tk/documentation/Camera_List_1.0.pdf | Bin 79294 -> 0 bytes .../documentation/Edit_File_Info_Example.php | 248 - jpeg_metadata_tk/documentation/Example.php | 218 - jpeg_metadata_tk/documentation/TIFFExample.php | 121 - jpeg_metadata_tk/documentation/changes.html | 257 - jpeg_metadata_tk/documentation/css_terms.html | 171 - .../documentation/edit_write_file_info.html | 272 - jpeg_metadata_tk/documentation/example.html | 131 - jpeg_metadata_tk/documentation/examples.html | 62 - jpeg_metadata_tk/documentation/exif.html | 284 - jpeg_metadata_tk/documentation/index.html | 60 - jpeg_metadata_tk/documentation/intro.html | 75 - jpeg_metadata_tk/documentation/jfif.html | 237 - jpeg_metadata_tk/documentation/jpeg.html | 303 - jpeg_metadata_tk/documentation/photoshop.html | 262 - .../documentation/photoshop_file_info.html | 162 - jpeg_metadata_tk/documentation/picture_info.html | 133 - jpeg_metadata_tk/documentation/style.css | 29 - jpeg_metadata_tk/documentation/tiffexample.html | 57 - jpeg_metadata_tk/documentation/todo.html | 65 - jpeg_metadata_tk/documentation/xmp.html | 228 - jpeg_metadata_tk/get_JFXX_thumb.php | 109 - jpeg_metadata_tk/get_casio_thumb.php | 126 - jpeg_metadata_tk/get_exif_thumb.php | 98 - jpeg_metadata_tk/get_minolta_thumb.php | 195 - jpeg_metadata_tk/get_ps_thumb.php | 176 - jpeg_metadata_tk/pjmt_utils.php | 150 - mailman.cfg | 15 - mailman_lib.php | 366 - mailman_lib.py | 27 - markdown.php | 1732 --- mime_lib.php | 32 - mimetypes.php | 133 - pclzip_lib.php | 4018 ------ pear/Archive/Tar.php | 1909 --- pear/Auth.php | 1330 -- pear/Auth/Anonymous.php | 138 - pear/Auth/Auth.php | 30 - pear/Auth/Container.php | 262 - pear/Auth/Container/Array.php | 161 - pear/Auth/Container/DB.php | 639 - pear/Auth/Container/DBLite.php | 320 - pear/Auth/Container/File.php | 314 - pear/Auth/Container/IMAP.php | 210 - pear/Auth/Container/KADM5.php | 171 - pear/Auth/Container/LDAP.php | 766 -- pear/Auth/Container/MDB.php | 625 - pear/Auth/Container/MDB2.php | 624 - pear/Auth/Container/Multiple.php | 188 - pear/Auth/Container/NetVPOPMaild.php | 129 - pear/Auth/Container/PEAR.php | 165 - pear/Auth/Container/POP3.php | 145 - pear/Auth/Container/RADIUS.php | 182 - pear/Auth/Container/SAP.php | 179 - pear/Auth/Container/SMBPasswd.php | 182 - pear/Auth/Container/SOAP.php | 229 - pear/Auth/Container/SOAP5.php | 268 - pear/Auth/Container/vpopmail.php | 88 - pear/Auth/Controller.php | 302 - pear/Auth/Frontend/Html.php | 142 - pear/Console/Getopt.php | 360 - pear/HTTP.php | 548 - pear/HTTP/Download.php | 1243 -- pear/HTTP/Download/Archive.php | 122 - pear/HTTP/Download/PgLOB.php | 177 - pear/HTTP/Header.php | 537 - pear/HTTP/Header/Cache.php | 238 - pear/Image/GraphViz.php | 1005 -- pear/MIME/Type.php | 598 - pear/MIME/Type/Extension.php | 292 - pear/MIME/Type/Parameter.php | 170 - pear/OS/Guess.php | 338 - pear/PEAR.php | 1040 -- pear/PEAR/Autoloader.php | 218 - pear/PEAR/Builder.php | 518 - pear/PEAR/ChannelFile.php | 1559 --- pear/PEAR/ChannelFile/Parser.php | 68 - pear/PEAR/Command.php | 414 - pear/PEAR/Command/Auth.php | 81 - pear/PEAR/Command/Auth.xml | 30 - pear/PEAR/Command/Build.php | 85 - pear/PEAR/Command/Build.xml | 10 - pear/PEAR/Command/Channels.php | 883 -- pear/PEAR/Command/Channels.xml | 123 - pear/PEAR/Command/Common.php | 273 - pear/PEAR/Command/Config.php | 414 - pear/PEAR/Command/Config.xml | 92 - pear/PEAR/Command/Install.php | 1268 -- pear/PEAR/Command/Install.xml | 276 - pear/PEAR/Command/Mirror.php | 139 - pear/PEAR/Command/Mirror.xml | 18 - pear/PEAR/Command/Package.php | 1124 -- pear/PEAR/Command/Package.xml | 237 - pear/PEAR/Command/Pickle.php | 421 - pear/PEAR/Command/Pickle.xml | 36 - pear/PEAR/Command/Registry.php | 1145 -- pear/PEAR/Command/Registry.xml | 58 - pear/PEAR/Command/Remote.php | 810 -- pear/PEAR/Command/Remote.xml | 109 - pear/PEAR/Command/Test.php | 337 - pear/PEAR/Command/Test.xml | 54 - pear/PEAR/Common.php | 837 -- pear/PEAR/Config.php | 2092 --- pear/PEAR/Dependency2.php | 1362 -- pear/PEAR/DependencyDB.php | 757 -- pear/PEAR/Downloader.php | 1766 --- pear/PEAR/Downloader/Package.php | 1988 --- pear/PEAR/ErrorStack.php | 985 -- pear/PEAR/ErrorStack5.php | 921 -- pear/PEAR/Exception.php | 389 - pear/PEAR/FixPHP5PEARWarnings.php | 7 - pear/PEAR/Frontend.php | 228 - pear/PEAR/Frontend/CLI.php | 751 -- pear/PEAR/Installer.php | 1753 --- pear/PEAR/Installer/Role.php | 267 - pear/PEAR/Installer/Role/Cfg.php | 106 - pear/PEAR/Installer/Role/Cfg.xml | 15 - pear/PEAR/Installer/Role/Common.php | 189 - pear/PEAR/Installer/Role/Data.php | 28 - pear/PEAR/Installer/Role/Data.xml | 15 - pear/PEAR/Installer/Role/Doc.php | 28 - pear/PEAR/Installer/Role/Doc.xml | 15 - pear/PEAR/Installer/Role/Ext.php | 28 - pear/PEAR/Installer/Role/Ext.xml | 12 - pear/PEAR/Installer/Role/Php.php | 28 - pear/PEAR/Installer/Role/Php.xml | 15 - pear/PEAR/Installer/Role/Script.php | 28 - pear/PEAR/Installer/Role/Script.xml | 15 - pear/PEAR/Installer/Role/Src.php | 34 - pear/PEAR/Installer/Role/Src.xml | 12 - pear/PEAR/Installer/Role/Test.php | 28 - pear/PEAR/Installer/Role/Test.xml | 15 - pear/PEAR/Installer/Role/Www.php | 28 - pear/PEAR/Installer/Role/Www.xml | 15 - pear/PEAR/PackageFile.php | 503 - pear/PEAR/PackageFile/Generator/v1.php | 1284 -- pear/PEAR/PackageFile/Generator/v2.php | 893 -- pear/PEAR/PackageFile/Parser/v1.php | 459 - pear/PEAR/PackageFile/Parser/v2.php | 113 - pear/PEAR/PackageFile/v1.php | 1612 --- pear/PEAR/PackageFile/v2.php | 2049 --- pear/PEAR/PackageFile/v2/Validator.php | 2154 --- pear/PEAR/PackageFile/v2/rw.php | 1607 --- pear/PEAR/Packager.php | 201 - pear/PEAR/REST.php | 483 - pear/PEAR/REST/10.php | 871 -- pear/PEAR/REST/11.php | 341 - pear/PEAR/REST/13.php | 299 - pear/PEAR/REST/14.php | 120 - pear/PEAR/Registry.php | 2339 ---- pear/PEAR/RunTest.php | 973 -- pear/PEAR/Start.php | 427 - pear/PEAR/Start/CLI.php | 616 - pear/PEAR/Task/Common.php | 202 - pear/PEAR/Task/Postinstallscript.php | 323 - pear/PEAR/Task/Postinstallscript/rw.php | 169 - pear/PEAR/Task/Replace.php | 176 - pear/PEAR/Task/Replace/rw.php | 61 - pear/PEAR/Task/Unixeol.php | 77 - pear/PEAR/Task/Unixeol/rw.php | 56 - pear/PEAR/Task/Windowseol.php | 77 - pear/PEAR/Task/Windowseol/rw.php | 56 - pear/PEAR/Validate.php | 629 - pear/PEAR/Validator/PECL.php | 63 - pear/PEAR/Warning.php | 379 - pear/PEAR/XMLParser.php | 253 - pear/PEAR5.php | 33 - pear/Structures/Graph.php | 154 - pear/Structures/Graph/Manipulator/AcyclicTest.php | 136 - .../Graph/Manipulator/TopologicalSorter.php | 153 - pear/Structures/Graph/Node.php | 342 - pear/System.php | 624 - pear/System/Command.php | 598 - pear/Text/Diff.php | 453 - pear/Text/Diff/Engine/native.php | 438 - pear/Text/Diff/Engine/shell.php | 164 - pear/Text/Diff/Engine/string.php | 250 - pear/Text/Diff/Engine/xdiff.php | 66 - pear/Text/Diff/Mapped.php | 55 - pear/Text/Diff/Renderer.php | 237 - pear/Text/Diff/Renderer/context.php | 77 - pear/Text/Diff/Renderer/inline.php | 172 - pear/Text/Diff/Renderer/unified.php | 67 - pear/Text/Diff/ThreeWay.php | 276 - pear/Text/Diff3.php | 276 - pear/Text/Wiki.php | 1550 --- pear/Text/Wiki/Creole.php | 150 - pear/Text/Wiki/Default.php | 27 - pear/Text/Wiki/Parse.php | 264 - pear/Text/Wiki/Parse/Creole/Address.php | 67 - pear/Text/Wiki/Parse/Creole/Blockquote.php | 176 - pear/Text/Wiki/Parse/Creole/Box.php | 81 - pear/Text/Wiki/Parse/Creole/Break.php | 73 - pear/Text/Wiki/Parse/Creole/Center.php | 78 - pear/Text/Wiki/Parse/Creole/Delimiter.php | 68 - pear/Text/Wiki/Parse/Creole/Emphasis.php | 83 - pear/Text/Wiki/Parse/Creole/Footnote.php | 83 - pear/Text/Wiki/Parse/Creole/Heading.php | 97 - pear/Text/Wiki/Parse/Creole/Horiz.php | 58 - pear/Text/Wiki/Parse/Creole/Image.php | 69 - pear/Text/Wiki/Parse/Creole/List.php | 244 - pear/Text/Wiki/Parse/Creole/Newline.php | 60 - pear/Text/Wiki/Parse/Creole/Paragraph.php | 139 - pear/Text/Wiki/Parse/Creole/Prefilter.php | 54 - pear/Text/Wiki/Parse/Creole/Preformatted.php | 68 - pear/Text/Wiki/Parse/Creole/Raw.php | 61 - pear/Text/Wiki/Parse/Creole/Strong.php | 84 - pear/Text/Wiki/Parse/Creole/Subscript.php | 75 - pear/Text/Wiki/Parse/Creole/Superscript.php | 75 - pear/Text/Wiki/Parse/Creole/Table.php | 207 - pear/Text/Wiki/Parse/Creole/Tighten.php | 37 - pear/Text/Wiki/Parse/Creole/Trim.php | 75 - pear/Text/Wiki/Parse/Creole/Tt.php | 78 - pear/Text/Wiki/Parse/Creole/Underline.php | 83 - pear/Text/Wiki/Parse/Creole/Url.php | 109 - pear/Text/Wiki/Parse/Creole/Wikilink.php | 322 - pear/Text/Wiki/Parse/Default/Anchor.php | 87 - pear/Text/Wiki/Parse/Default/Blockquote.php | 180 - pear/Text/Wiki/Parse/Default/Bold.php | 79 - pear/Text/Wiki/Parse/Default/Break.php | 72 - pear/Text/Wiki/Parse/Default/Center.php | 78 - pear/Text/Wiki/Parse/Default/Code.php | 99 - pear/Text/Wiki/Parse/Default/Colortext.php | 89 - pear/Text/Wiki/Parse/Default/Deflist.php | 122 - pear/Text/Wiki/Parse/Default/Delimiter.php | 80 - pear/Text/Wiki/Parse/Default/Embed.php | 106 - pear/Text/Wiki/Parse/Default/Emphasis.php | 85 - pear/Text/Wiki/Parse/Default/Freelink.php | 134 - pear/Text/Wiki/Parse/Default/Function.php | 141 - pear/Text/Wiki/Parse/Default/Heading.php | 107 - pear/Text/Wiki/Parse/Default/Horiz.php | 70 - pear/Text/Wiki/Parse/Default/Html.php | 75 - pear/Text/Wiki/Parse/Default/Image.php | 136 - pear/Text/Wiki/Parse/Default/Include.php | 100 - pear/Text/Wiki/Parse/Default/Interwiki.php | 138 - pear/Text/Wiki/Parse/Default/Italic.php | 85 - pear/Text/Wiki/Parse/Default/List.php | 262 - pear/Text/Wiki/Parse/Default/Newline.php | 75 - pear/Text/Wiki/Parse/Default/Paragraph.php | 116 - pear/Text/Wiki/Parse/Default/Phplookup.php | 73 - pear/Text/Wiki/Parse/Default/Prefilter.php | 84 - pear/Text/Wiki/Parse/Default/Raw.php | 73 - pear/Text/Wiki/Parse/Default/Revise.php | 145 - pear/Text/Wiki/Parse/Default/Smiley.php | 157 - pear/Text/Wiki/Parse/Default/Strong.php | 86 - pear/Text/Wiki/Parse/Default/Subscript.php | 79 - pear/Text/Wiki/Parse/Default/Superscript.php | 79 - pear/Text/Wiki/Parse/Default/Table.php | 226 - pear/Text/Wiki/Parse/Default/Tighten.php | 49 - pear/Text/Wiki/Parse/Default/Toc.php | 130 - pear/Text/Wiki/Parse/Default/Tt.php | 84 - pear/Text/Wiki/Parse/Default/Underline.php | 79 - pear/Text/Wiki/Parse/Default/Url.php | 281 - pear/Text/Wiki/Parse/Default/Wikilink.php | 204 - pear/Text/Wiki/Render.php | 218 - pear/Text/Wiki/Render/Creole.php | 16 - pear/Text/Wiki/Render/Creole/Address.php | 29 - pear/Text/Wiki/Render/Creole/Anchor.php | 23 - pear/Text/Wiki/Render/Creole/Blockquote.php | 47 - pear/Text/Wiki/Render/Creole/Bold.php | 23 - pear/Text/Wiki/Render/Creole/Box.php | 30 - pear/Text/Wiki/Render/Creole/Break.php | 24 - pear/Text/Wiki/Render/Creole/Center.php | 30 - pear/Text/Wiki/Render/Creole/Code.php | 23 - pear/Text/Wiki/Render/Creole/Colortext.php | 23 - pear/Text/Wiki/Render/Creole/Deflist.php | 23 - pear/Text/Wiki/Render/Creole/Delimiter.php | 23 - pear/Text/Wiki/Render/Creole/Embed.php | 29 - pear/Text/Wiki/Render/Creole/Emphasis.php | 23 - pear/Text/Wiki/Render/Creole/Freelink.php | 9 - pear/Text/Wiki/Render/Creole/Function.php | 23 - pear/Text/Wiki/Render/Creole/Heading.php | 16 - pear/Text/Wiki/Render/Creole/Horiz.php | 23 - pear/Text/Wiki/Render/Creole/Html.php | 23 - pear/Text/Wiki/Render/Creole/Image.php | 26 - pear/Text/Wiki/Render/Creole/Include.php | 16 - pear/Text/Wiki/Render/Creole/Interwiki.php | 23 - pear/Text/Wiki/Render/Creole/Italic.php | 23 - pear/Text/Wiki/Render/Creole/List.php | 53 - pear/Text/Wiki/Render/Creole/Newline.php | 12 - pear/Text/Wiki/Render/Creole/Paragraph.php | 29 - pear/Text/Wiki/Render/Creole/Phplookup.php | 25 - pear/Text/Wiki/Render/Creole/Prefilter.php | 10 - pear/Text/Wiki/Render/Creole/Preformatted.php | 27 - pear/Text/Wiki/Render/Creole/Raw.php | 33 - pear/Text/Wiki/Render/Creole/Revise.php | 23 - pear/Text/Wiki/Render/Creole/Strong.php | 23 - pear/Text/Wiki/Render/Creole/Subscript.php | 23 - pear/Text/Wiki/Render/Creole/Superscript.php | 23 - pear/Text/Wiki/Render/Creole/Table.php | 70 - pear/Text/Wiki/Render/Creole/Tighten.php | 10 - pear/Text/Wiki/Render/Creole/Toc.php | 23 - pear/Text/Wiki/Render/Creole/Tt.php | 29 - pear/Text/Wiki/Render/Creole/Underline.php | 23 - pear/Text/Wiki/Render/Creole/Url.php | 40 - pear/Text/Wiki/Render/Creole/Wikilink.php | 43 - pear/Text/Wiki/Render/Latex.php | 90 - pear/Text/Wiki/Render/Latex/Anchor.php | 33 - pear/Text/Wiki/Render/Latex/Blockquote.php | 36 - pear/Text/Wiki/Render/Latex/Bold.php | 28 - pear/Text/Wiki/Render/Latex/Box.php | 54 - pear/Text/Wiki/Render/Latex/Break.php | 24 - pear/Text/Wiki/Render/Latex/Center.php | 33 - pear/Text/Wiki/Render/Latex/Code.php | 26 - pear/Text/Wiki/Render/Latex/Colortext.php | 58 - pear/Text/Wiki/Render/Latex/Deflist.php | 53 - pear/Text/Wiki/Render/Latex/Delimiter.php | 25 - pear/Text/Wiki/Render/Latex/Embed.php | 23 - pear/Text/Wiki/Render/Latex/Emphasis.php | 29 - pear/Text/Wiki/Render/Latex/Font.php | 73 - pear/Text/Wiki/Render/Latex/Freelink.php | 6 - pear/Text/Wiki/Render/Latex/Function.php | 23 - pear/Text/Wiki/Render/Latex/Heading.php | 33 - pear/Text/Wiki/Render/Latex/Horiz.php | 23 - pear/Text/Wiki/Render/Latex/Html.php | 25 - pear/Text/Wiki/Render/Latex/Image.php | 70 - pear/Text/Wiki/Render/Latex/Include.php | 8 - pear/Text/Wiki/Render/Latex/Interwiki.php | 58 - pear/Text/Wiki/Render/Latex/Italic.php | 28 - pear/Text/Wiki/Render/Latex/List.php | 76 - pear/Text/Wiki/Render/Latex/Newline.php | 12 - pear/Text/Wiki/Render/Latex/Page.php | 48 - pear/Text/Wiki/Render/Latex/Paragraph.php | 31 - pear/Text/Wiki/Render/Latex/Phplookup.php | 34 - pear/Text/Wiki/Render/Latex/Plugin.php | 49 - pear/Text/Wiki/Render/Latex/Prefilter.php | 56 - pear/Text/Wiki/Render/Latex/Preformatted.php | 48 - pear/Text/Wiki/Render/Latex/Raw.php | 23 - pear/Text/Wiki/Render/Latex/Revise.php | 38 - pear/Text/Wiki/Render/Latex/Smiley.php | 44 - pear/Text/Wiki/Render/Latex/Specialchar.php | 54 - pear/Text/Wiki/Render/Latex/Strong.php | 30 - pear/Text/Wiki/Render/Latex/Subscript.php | 54 - pear/Text/Wiki/Render/Latex/Superscript.php | 31 - pear/Text/Wiki/Render/Latex/Table.php | 99 - pear/Text/Wiki/Render/Latex/Tighten.php | 9 - pear/Text/Wiki/Render/Latex/Titlebar.php | 54 - pear/Text/Wiki/Render/Latex/Toc.php | 30 - pear/Text/Wiki/Render/Latex/Tt.php | 30 - pear/Text/Wiki/Render/Latex/Underline.php | 30 - pear/Text/Wiki/Render/Latex/Url.php | 41 - pear/Text/Wiki/Render/Latex/Wikilink.php | 60 - pear/Text/Wiki/Render/Plain.php | 16 - pear/Text/Wiki/Render/Plain/Anchor.php | 23 - pear/Text/Wiki/Render/Plain/Blockquote.php | 39 - pear/Text/Wiki/Render/Plain/Bold.php | 23 - pear/Text/Wiki/Render/Plain/Box.php | 48 - pear/Text/Wiki/Render/Plain/Break.php | 24 - pear/Text/Wiki/Render/Plain/Center.php | 23 - pear/Text/Wiki/Render/Plain/Code.php | 24 - pear/Text/Wiki/Render/Plain/Colortext.php | 23 - pear/Text/Wiki/Render/Plain/Deflist.php | 59 - pear/Text/Wiki/Render/Plain/Delimiter.php | 23 - pear/Text/Wiki/Render/Plain/Embed.php | 23 - pear/Text/Wiki/Render/Plain/Emphasis.php | 23 - pear/Text/Wiki/Render/Plain/Font.php | 44 - pear/Text/Wiki/Render/Plain/Freelink.php | 23 - pear/Text/Wiki/Render/Plain/Function.php | 39 - pear/Text/Wiki/Render/Plain/Heading.php | 14 - pear/Text/Wiki/Render/Plain/Horiz.php | 23 - pear/Text/Wiki/Render/Plain/Html.php | 24 - pear/Text/Wiki/Render/Plain/Image.php | 22 - pear/Text/Wiki/Render/Plain/Include.php | 8 - pear/Text/Wiki/Render/Plain/Interwiki.php | 29 - pear/Text/Wiki/Render/Plain/Italic.php | 23 - pear/Text/Wiki/Render/Plain/List.php | 68 - pear/Text/Wiki/Render/Plain/Newline.php | 12 - pear/Text/Wiki/Render/Plain/Page.php | 48 - pear/Text/Wiki/Render/Plain/Paragraph.php | 31 - pear/Text/Wiki/Render/Plain/Phplookup.php | 25 - pear/Text/Wiki/Render/Plain/Plugin.php | 49 - pear/Text/Wiki/Render/Plain/Prefilter.php | 24 - pear/Text/Wiki/Render/Plain/Preformatted.php | 48 - pear/Text/Wiki/Render/Plain/Raw.php | 23 - pear/Text/Wiki/Render/Plain/Revise.php | 24 - pear/Text/Wiki/Render/Plain/Smiley.php | 44 - pear/Text/Wiki/Render/Plain/Specialchar.php | 54 - pear/Text/Wiki/Render/Plain/Strong.php | 24 - pear/Text/Wiki/Render/Plain/Subscript.php | 48 - pear/Text/Wiki/Render/Plain/Superscript.php | 23 - pear/Text/Wiki/Render/Plain/Table.php | 65 - pear/Text/Wiki/Render/Plain/Tighten.php | 10 - pear/Text/Wiki/Render/Plain/Titlebar.php | 54 - pear/Text/Wiki/Render/Plain/Toc.php | 39 - pear/Text/Wiki/Render/Plain/Tt.php | 24 - pear/Text/Wiki/Render/Plain/Underline.php | 23 - pear/Text/Wiki/Render/Plain/Url.php | 29 - pear/Text/Wiki/Render/Plain/Wikilink.php | 24 - pear/Text/Wiki/Render/Xhtml.php | 107 - pear/Text/Wiki/Render/Xhtml/Address.php | 54 - pear/Text/Wiki/Render/Xhtml/Anchor.php | 48 - pear/Text/Wiki/Render/Xhtml/Blockquote.php | 72 - pear/Text/Wiki/Render/Xhtml/Bold.php | 57 - pear/Text/Wiki/Render/Xhtml/Box.php | 62 - pear/Text/Wiki/Render/Xhtml/Break.php | 52 - pear/Text/Wiki/Render/Xhtml/Center.php | 62 - pear/Text/Wiki/Render/Xhtml/Code.php | 133 - pear/Text/Wiki/Render/Xhtml/Colortext.php | 79 - pear/Text/Wiki/Render/Xhtml/Deflist.php | 87 - pear/Text/Wiki/Render/Xhtml/Delimiter.php | 46 - pear/Text/Wiki/Render/Xhtml/Embed.php | 46 - pear/Text/Wiki/Render/Xhtml/Emphasis.php | 58 - pear/Text/Wiki/Render/Xhtml/Font.php | 83 - pear/Text/Wiki/Render/Xhtml/Freelink.php | 35 - pear/Text/Wiki/Render/Xhtml/Function.php | 108 - pear/Text/Wiki/Render/Xhtml/Heading.php | 88 - pear/Text/Wiki/Render/Xhtml/Horiz.php | 51 - pear/Text/Wiki/Render/Xhtml/Html.php | 47 - pear/Text/Wiki/Render/Xhtml/Image.php | 184 - pear/Text/Wiki/Render/Xhtml/Include.php | 32 - pear/Text/Wiki/Render/Xhtml/Interwiki.php | 103 - pear/Text/Wiki/Render/Xhtml/Italic.php | 57 - pear/Text/Wiki/Render/Xhtml/List.php | 172 - pear/Text/Wiki/Render/Xhtml/Newline.php | 35 - pear/Text/Wiki/Render/Xhtml/Page.php | 46 - pear/Text/Wiki/Render/Xhtml/Paragraph.php | 59 - pear/Text/Wiki/Render/Xhtml/Phplookup.php | 81 - pear/Text/Wiki/Render/Xhtml/Plugin.php | 47 - pear/Text/Wiki/Render/Xhtml/Prefilter.php | 34 - pear/Text/Wiki/Render/Xhtml/Preformatted.php | 47 - pear/Text/Wiki/Render/Xhtml/Raw.php | 46 - pear/Text/Wiki/Render/Xhtml/Revise.php | 68 - pear/Text/Wiki/Render/Xhtml/Smiley.php | 74 - pear/Text/Wiki/Render/Xhtml/Specialchar.php | 52 - pear/Text/Wiki/Render/Xhtml/Strong.php | 58 - pear/Text/Wiki/Render/Xhtml/Subscript.php | 57 - pear/Text/Wiki/Render/Xhtml/Superscript.php | 57 - pear/Text/Wiki/Render/Xhtml/Table.php | 140 - pear/Text/Wiki/Render/Xhtml/Tighten.php | 34 - pear/Text/Wiki/Render/Xhtml/Titlebar.php | 57 - pear/Text/Wiki/Render/Xhtml/Toc.php | 115 - pear/Text/Wiki/Render/Xhtml/Tt.php | 58 - pear/Text/Wiki/Render/Xhtml/Underline.php | 57 - pear/Text/Wiki/Render/Xhtml/Url.php | 131 - pear/Text/Wiki/Render/Xhtml/Wikilink.php | 177 - pear/XML/Util.php | 911 -- phpcontrib_lib.php | 76 - phpcoord/phpcoord-2.3.php | 785 -- phpcoord/readme-2.3.txt | 52 - phpcoord/test-2.3.php | 211 - phplot.php | 6688 ---------- phpmailer/README | 102 - phpmailer/class.phpmailer.php | 1721 --- phpmailer/class.pop3.php | 437 - phpmailer/class.smtp.php | 1062 -- phpmailer/language/phpmailer.lang-br.php | 23 - phpmailer/language/phpmailer.lang-ca.php | 24 - phpmailer/language/phpmailer.lang-cz.php | 26 - phpmailer/language/phpmailer.lang-de.php | 25 - phpmailer/language/phpmailer.lang-dk.php | 26 - phpmailer/language/phpmailer.lang-en.php | 25 - phpmailer/language/phpmailer.lang-es.php | 25 - phpmailer/language/phpmailer.lang-fi.php | 25 - phpmailer/language/phpmailer.lang-fo.php | 27 - phpmailer/language/phpmailer.lang-fr.php | 26 - phpmailer/language/phpmailer.lang-hu.php | 25 - phpmailer/language/phpmailer.lang-it.php | 29 - phpmailer/language/phpmailer.lang-ja.php | 27 - phpmailer/language/phpmailer.lang-nl.php | 25 - phpmailer/language/phpmailer.lang-no.php | 25 - phpmailer/language/phpmailer.lang-pl.php | 26 - phpmailer/language/phpmailer.lang-ro.php | 24 - phpmailer/language/phpmailer.lang-ru.php | 25 - phpmailer/language/phpmailer.lang-se.php | 26 - phpmailer/language/phpmailer.lang-tr.php | 27 - phpsniff/index.php | 6 - phpsniff/phpSniff.class.php | 907 -- phpsniff/phpSniff.core.php | 547 - simplepie/idn/idna_convert.class.php | 969 -- simplepie/idn/npdata.ser | 1 - simplepie/simplepie.inc | 13316 ------------------- spyc/README | 159 - spyc/examples/yaml-dump.php | 25 - spyc/examples/yaml-load.php | 25 - spyc/php4/5to4.php | 16 - spyc/php4/spyc.php4 | 1023 -- spyc/php4/test.php4 | 162 - spyc/spyc.php | 1024 -- spyc/spyc.yaml | 196 - spyc/tests/DumpTest.php | 58 - spyc/tests/IndentTest.php | 57 - spyc/tests/ParseTest.php | 305 - spyc/tests/failing1.yaml | 2 - spyc/tests/indent_1.yaml | 53 - spyc/tests/quotes.yaml | 8 - tar.class.php | 467 - template_biticon_updater.sh | 340 - tree.php | 135 - zip_lib.php | 805 -- 1254 files changed, 196940 insertions(+), 196940 deletions(-) delete mode 100644 .htaccess delete mode 100644 Date/Calc.php delete mode 100644 Date/Human.php delete mode 100644 Date/TimeZone.php delete mode 100644 Date/TimeZoneWindows.php delete mode 100644 Date/index.php delete mode 100644 PHPAsync.php delete mode 100644 bitexcel/BitExcel.php delete mode 100644 bitexcel/BitExcelAsync.php delete mode 100644 cufon/cufon-yui.js delete mode 100644 dBug/dBug.php delete mode 100644 daemonize/.htaccess delete mode 100644 daemonize/daemonize.php delete mode 100644 daemonize/daemonize_lib.php delete mode 100755 daemonize/is_up.sh delete mode 100644 datasets/regions/us/class.USStates.php delete mode 100644 diff.php delete mode 100644 geocalc/GeoCalc.class.php delete mode 100644 geocalc/README delete mode 100644 getid3/changelog.txt delete mode 100644 getid3/demos/demo.audioinfo.class.php delete mode 100644 getid3/demos/demo.basic.php delete mode 100644 getid3/demos/demo.browse.php delete mode 100644 getid3/demos/demo.cache.dbm.php delete mode 100644 getid3/demos/demo.cache.mysql.php delete mode 100644 getid3/demos/demo.joinmp3.php delete mode 100644 getid3/demos/demo.mimeonly.php delete mode 100644 getid3/demos/demo.mysql.php delete mode 100644 getid3/demos/demo.simple.php delete mode 100644 getid3/demos/demo.simple.write.php delete mode 100644 getid3/demos/demo.write.php delete mode 100644 getid3/demos/index.php delete mode 100644 getid3/dependencies.txt delete mode 100644 getid3/getid3/extension.cache.dbm.php delete mode 100644 getid3/getid3/extension.cache.mysql.php delete mode 100644 getid3/getid3/getid3.lib.php delete mode 100644 getid3/getid3/getid3.php delete mode 100644 getid3/getid3/module.archive.gzip.php delete mode 100644 getid3/getid3/module.archive.rar.php delete mode 100644 getid3/getid3/module.archive.szip.php delete mode 100644 getid3/getid3/module.archive.tar.php delete mode 100644 getid3/getid3/module.archive.zip.php delete mode 100644 getid3/getid3/module.audio-video.asf.php delete mode 100644 getid3/getid3/module.audio-video.bink.php delete mode 100644 getid3/getid3/module.audio-video.flv.php delete mode 100644 getid3/getid3/module.audio-video.matroska.php delete mode 100644 getid3/getid3/module.audio-video.mpeg.php delete mode 100644 getid3/getid3/module.audio-video.nsv.php delete mode 100644 getid3/getid3/module.audio-video.quicktime.php delete mode 100644 getid3/getid3/module.audio-video.real.php delete mode 100644 getid3/getid3/module.audio-video.riff.php delete mode 100644 getid3/getid3/module.audio-video.swf.php delete mode 100644 getid3/getid3/module.audio.aac.php delete mode 100644 getid3/getid3/module.audio.ac3.php delete mode 100644 getid3/getid3/module.audio.au.php delete mode 100644 getid3/getid3/module.audio.avr.php delete mode 100644 getid3/getid3/module.audio.bonk.php delete mode 100644 getid3/getid3/module.audio.flac.php delete mode 100644 getid3/getid3/module.audio.la.php delete mode 100644 getid3/getid3/module.audio.lpac.php delete mode 100644 getid3/getid3/module.audio.midi.php delete mode 100644 getid3/getid3/module.audio.mod.php delete mode 100644 getid3/getid3/module.audio.monkey.php delete mode 100644 getid3/getid3/module.audio.mp3.php delete mode 100644 getid3/getid3/module.audio.mpc.php delete mode 100644 getid3/getid3/module.audio.ogg.php delete mode 100644 getid3/getid3/module.audio.optimfrog.php delete mode 100644 getid3/getid3/module.audio.rkau.php delete mode 100644 getid3/getid3/module.audio.shorten.php delete mode 100644 getid3/getid3/module.audio.tta.php delete mode 100644 getid3/getid3/module.audio.voc.php delete mode 100644 getid3/getid3/module.audio.vqf.php delete mode 100644 getid3/getid3/module.audio.wavpack.php delete mode 100644 getid3/getid3/module.graphic.bmp.php delete mode 100644 getid3/getid3/module.graphic.gif.php delete mode 100644 getid3/getid3/module.graphic.jpg.php delete mode 100644 getid3/getid3/module.graphic.pcd.php delete mode 100644 getid3/getid3/module.graphic.png.php delete mode 100644 getid3/getid3/module.graphic.svg.php delete mode 100644 getid3/getid3/module.graphic.tiff.php delete mode 100644 getid3/getid3/module.misc.exe.php delete mode 100644 getid3/getid3/module.misc.iso.php delete mode 100644 getid3/getid3/module.tag.apetag.php delete mode 100644 getid3/getid3/module.tag.id3v1.php delete mode 100644 getid3/getid3/module.tag.id3v2.php delete mode 100644 getid3/getid3/module.tag.lyrics3.php delete mode 100644 getid3/getid3/write.apetag.php delete mode 100644 getid3/getid3/write.id3v1.php delete mode 100644 getid3/getid3/write.id3v2.php delete mode 100644 getid3/getid3/write.lyrics3.php delete mode 100644 getid3/getid3/write.metaflac.php delete mode 100644 getid3/getid3/write.php delete mode 100644 getid3/getid3/write.real.php delete mode 100644 getid3/getid3/write.vorbiscomment.php delete mode 100644 getid3/license.commercial.txt delete mode 100644 getid3/license.txt delete mode 100644 getid3/readme.txt delete mode 100644 getid3/structure.txt delete mode 100644 htmlparser/html_parser_inc.php delete mode 100644 htmlparser/htmlgrammar.cmp delete mode 100644 htmlparser/htmlgrammar.dat delete mode 100644 htmlparser/htmlgrammarparser.inc delete mode 100644 htmlparser/readme.eng.txt delete mode 100644 htmlparser/rebuildgrammar.php delete mode 100644 htmlpure/Filter/CNBC.php delete mode 100644 htmlpure/Filter/SafeIframe.php delete mode 100644 htmlpure/Filter/YouTube.php create mode 100644 includes/.htaccess create mode 100644 includes/Date/Calc.php create mode 100644 includes/Date/Human.php create mode 100644 includes/Date/TimeZone.php create mode 100644 includes/Date/TimeZoneWindows.php create mode 100644 includes/Date/index.php create mode 100644 includes/PHPAsync.php create mode 100644 includes/bitexcel/BitExcel.php create mode 100644 includes/bitexcel/BitExcelAsync.php create mode 100644 includes/cufon/cufon-yui.js create mode 100644 includes/dBug/dBug.php create mode 100644 includes/daemonize/.htaccess create mode 100644 includes/daemonize/daemonize.php create mode 100644 includes/daemonize/daemonize_lib.php create mode 100755 includes/daemonize/is_up.sh create mode 100644 includes/datasets/regions/us/class.USStates.php create mode 100644 includes/diff.php create mode 100644 includes/geocalc/GeoCalc.class.php create mode 100644 includes/geocalc/README create mode 100644 includes/getid3/changelog.txt create mode 100644 includes/getid3/demos/demo.audioinfo.class.php create mode 100644 includes/getid3/demos/demo.basic.php create mode 100644 includes/getid3/demos/demo.browse.php create mode 100644 includes/getid3/demos/demo.cache.dbm.php create mode 100644 includes/getid3/demos/demo.cache.mysql.php create mode 100644 includes/getid3/demos/demo.joinmp3.php create mode 100644 includes/getid3/demos/demo.mimeonly.php create mode 100644 includes/getid3/demos/demo.mysql.php create mode 100644 includes/getid3/demos/demo.simple.php create mode 100644 includes/getid3/demos/demo.simple.write.php create mode 100644 includes/getid3/demos/demo.write.php create mode 100644 includes/getid3/demos/index.php create mode 100644 includes/getid3/dependencies.txt create mode 100644 includes/getid3/getid3/extension.cache.dbm.php create mode 100644 includes/getid3/getid3/extension.cache.mysql.php create mode 100644 includes/getid3/getid3/getid3.lib.php create mode 100644 includes/getid3/getid3/getid3.php create mode 100644 includes/getid3/getid3/module.archive.gzip.php create mode 100644 includes/getid3/getid3/module.archive.rar.php create mode 100644 includes/getid3/getid3/module.archive.szip.php create mode 100644 includes/getid3/getid3/module.archive.tar.php create mode 100644 includes/getid3/getid3/module.archive.zip.php create mode 100644 includes/getid3/getid3/module.audio-video.asf.php create mode 100644 includes/getid3/getid3/module.audio-video.bink.php create mode 100644 includes/getid3/getid3/module.audio-video.flv.php create mode 100644 includes/getid3/getid3/module.audio-video.matroska.php create mode 100644 includes/getid3/getid3/module.audio-video.mpeg.php create mode 100644 includes/getid3/getid3/module.audio-video.nsv.php create mode 100644 includes/getid3/getid3/module.audio-video.quicktime.php create mode 100644 includes/getid3/getid3/module.audio-video.real.php create mode 100644 includes/getid3/getid3/module.audio-video.riff.php create mode 100644 includes/getid3/getid3/module.audio-video.swf.php create mode 100644 includes/getid3/getid3/module.audio.aac.php create mode 100644 includes/getid3/getid3/module.audio.ac3.php create mode 100644 includes/getid3/getid3/module.audio.au.php create mode 100644 includes/getid3/getid3/module.audio.avr.php create mode 100644 includes/getid3/getid3/module.audio.bonk.php create mode 100644 includes/getid3/getid3/module.audio.flac.php create mode 100644 includes/getid3/getid3/module.audio.la.php create mode 100644 includes/getid3/getid3/module.audio.lpac.php create mode 100644 includes/getid3/getid3/module.audio.midi.php create mode 100644 includes/getid3/getid3/module.audio.mod.php create mode 100644 includes/getid3/getid3/module.audio.monkey.php create mode 100644 includes/getid3/getid3/module.audio.mp3.php create mode 100644 includes/getid3/getid3/module.audio.mpc.php create mode 100644 includes/getid3/getid3/module.audio.ogg.php create mode 100644 includes/getid3/getid3/module.audio.optimfrog.php create mode 100644 includes/getid3/getid3/module.audio.rkau.php create mode 100644 includes/getid3/getid3/module.audio.shorten.php create mode 100644 includes/getid3/getid3/module.audio.tta.php create mode 100644 includes/getid3/getid3/module.audio.voc.php create mode 100644 includes/getid3/getid3/module.audio.vqf.php create mode 100644 includes/getid3/getid3/module.audio.wavpack.php create mode 100644 includes/getid3/getid3/module.graphic.bmp.php create mode 100644 includes/getid3/getid3/module.graphic.gif.php create mode 100644 includes/getid3/getid3/module.graphic.jpg.php create mode 100644 includes/getid3/getid3/module.graphic.pcd.php create mode 100644 includes/getid3/getid3/module.graphic.png.php create mode 100644 includes/getid3/getid3/module.graphic.svg.php create mode 100644 includes/getid3/getid3/module.graphic.tiff.php create mode 100644 includes/getid3/getid3/module.misc.exe.php create mode 100644 includes/getid3/getid3/module.misc.iso.php create mode 100644 includes/getid3/getid3/module.tag.apetag.php create mode 100644 includes/getid3/getid3/module.tag.id3v1.php create mode 100644 includes/getid3/getid3/module.tag.id3v2.php create mode 100644 includes/getid3/getid3/module.tag.lyrics3.php create mode 100644 includes/getid3/getid3/write.apetag.php create mode 100644 includes/getid3/getid3/write.id3v1.php create mode 100644 includes/getid3/getid3/write.id3v2.php create mode 100644 includes/getid3/getid3/write.lyrics3.php create mode 100644 includes/getid3/getid3/write.metaflac.php create mode 100644 includes/getid3/getid3/write.php create mode 100644 includes/getid3/getid3/write.real.php create mode 100644 includes/getid3/getid3/write.vorbiscomment.php create mode 100644 includes/getid3/license.commercial.txt create mode 100644 includes/getid3/license.txt create mode 100644 includes/getid3/readme.txt create mode 100644 includes/getid3/structure.txt create mode 100644 includes/htmlparser/html_parser_inc.php create mode 100644 includes/htmlparser/htmlgrammar.cmp create mode 100644 includes/htmlparser/htmlgrammar.dat create mode 100644 includes/htmlparser/htmlgrammarparser.inc create mode 100644 includes/htmlparser/readme.eng.txt create mode 100644 includes/htmlparser/rebuildgrammar.php create mode 100644 includes/htmlpure/Filter/CNBC.php create mode 100644 includes/htmlpure/Filter/SafeIframe.php create mode 100644 includes/htmlpure/Filter/YouTube.php create mode 100644 includes/is_email.php create mode 100644 includes/jpeg_metadata_tk/COPYING.txt create mode 100644 includes/jpeg_metadata_tk/EXIF.php create mode 100644 includes/jpeg_metadata_tk/EXIF_Makernote.php create mode 100644 includes/jpeg_metadata_tk/EXIF_Tags.php create mode 100644 includes/jpeg_metadata_tk/Edit_File_Info.php create mode 100644 includes/jpeg_metadata_tk/IPTC.php create mode 100644 includes/jpeg_metadata_tk/JFIF.php create mode 100644 includes/jpeg_metadata_tk/JPEG.php create mode 100644 includes/jpeg_metadata_tk/Makernotes/Pentax.php create mode 100644 includes/jpeg_metadata_tk/Makernotes/agfa.php create mode 100644 includes/jpeg_metadata_tk/Makernotes/canon.php create mode 100644 includes/jpeg_metadata_tk/Makernotes/casio.php create mode 100644 includes/jpeg_metadata_tk/Makernotes/epson.php create mode 100644 includes/jpeg_metadata_tk/Makernotes/fujifilm.php create mode 100644 includes/jpeg_metadata_tk/Makernotes/konica_minolta.php create mode 100644 includes/jpeg_metadata_tk/Makernotes/kyocera.php create mode 100644 includes/jpeg_metadata_tk/Makernotes/nikon.php create mode 100644 includes/jpeg_metadata_tk/Makernotes/olympus.php create mode 100644 includes/jpeg_metadata_tk/Makernotes/panasonic.php create mode 100644 includes/jpeg_metadata_tk/Makernotes/ricoh.php create mode 100644 includes/jpeg_metadata_tk/Makernotes/sony.php create mode 100644 includes/jpeg_metadata_tk/PIM.php create mode 100644 includes/jpeg_metadata_tk/Photoshop_File_Info.php create mode 100644 includes/jpeg_metadata_tk/Photoshop_IRB.php create mode 100644 includes/jpeg_metadata_tk/PictureInfo.php create mode 100644 includes/jpeg_metadata_tk/Toolkit_Version.php create mode 100644 includes/jpeg_metadata_tk/Unicode.php create mode 100644 includes/jpeg_metadata_tk/Write_File_Info.php create mode 100644 includes/jpeg_metadata_tk/XML.php create mode 100644 includes/jpeg_metadata_tk/XMP.php create mode 100644 includes/jpeg_metadata_tk/documentation/Camera_List_1.0.pdf create mode 100644 includes/jpeg_metadata_tk/documentation/Edit_File_Info_Example.php create mode 100644 includes/jpeg_metadata_tk/documentation/Example.php create mode 100644 includes/jpeg_metadata_tk/documentation/TIFFExample.php create mode 100644 includes/jpeg_metadata_tk/documentation/changes.html create mode 100644 includes/jpeg_metadata_tk/documentation/css_terms.html create mode 100644 includes/jpeg_metadata_tk/documentation/edit_write_file_info.html create mode 100644 includes/jpeg_metadata_tk/documentation/example.html create mode 100644 includes/jpeg_metadata_tk/documentation/examples.html create mode 100644 includes/jpeg_metadata_tk/documentation/exif.html create mode 100644 includes/jpeg_metadata_tk/documentation/index.html create mode 100644 includes/jpeg_metadata_tk/documentation/intro.html create mode 100644 includes/jpeg_metadata_tk/documentation/jfif.html create mode 100644 includes/jpeg_metadata_tk/documentation/jpeg.html create mode 100644 includes/jpeg_metadata_tk/documentation/photoshop.html create mode 100644 includes/jpeg_metadata_tk/documentation/photoshop_file_info.html create mode 100644 includes/jpeg_metadata_tk/documentation/picture_info.html create mode 100644 includes/jpeg_metadata_tk/documentation/style.css create mode 100644 includes/jpeg_metadata_tk/documentation/tiffexample.html create mode 100644 includes/jpeg_metadata_tk/documentation/todo.html create mode 100644 includes/jpeg_metadata_tk/documentation/xmp.html create mode 100644 includes/jpeg_metadata_tk/get_JFXX_thumb.php create mode 100644 includes/jpeg_metadata_tk/get_casio_thumb.php create mode 100644 includes/jpeg_metadata_tk/get_exif_thumb.php create mode 100644 includes/jpeg_metadata_tk/get_minolta_thumb.php create mode 100644 includes/jpeg_metadata_tk/get_ps_thumb.php create mode 100644 includes/jpeg_metadata_tk/pjmt_utils.php create mode 100644 includes/mailman.cfg create mode 100644 includes/mailman_lib.php create mode 100644 includes/mailman_lib.py create mode 100644 includes/markdown.php create mode 100644 includes/mime_lib.php create mode 100644 includes/mimetypes.php create mode 100644 includes/pclzip_lib.php create mode 100644 includes/pear/Archive/Tar.php create mode 100644 includes/pear/Auth.php create mode 100644 includes/pear/Auth/Anonymous.php create mode 100644 includes/pear/Auth/Auth.php create mode 100644 includes/pear/Auth/Container.php create mode 100644 includes/pear/Auth/Container/Array.php create mode 100644 includes/pear/Auth/Container/DB.php create mode 100644 includes/pear/Auth/Container/DBLite.php create mode 100644 includes/pear/Auth/Container/File.php create mode 100644 includes/pear/Auth/Container/IMAP.php create mode 100644 includes/pear/Auth/Container/KADM5.php create mode 100644 includes/pear/Auth/Container/LDAP.php create mode 100644 includes/pear/Auth/Container/MDB.php create mode 100644 includes/pear/Auth/Container/MDB2.php create mode 100644 includes/pear/Auth/Container/Multiple.php create mode 100644 includes/pear/Auth/Container/NetVPOPMaild.php create mode 100644 includes/pear/Auth/Container/PEAR.php create mode 100644 includes/pear/Auth/Container/POP3.php create mode 100644 includes/pear/Auth/Container/RADIUS.php create mode 100644 includes/pear/Auth/Container/SAP.php create mode 100644 includes/pear/Auth/Container/SMBPasswd.php create mode 100644 includes/pear/Auth/Container/SOAP.php create mode 100644 includes/pear/Auth/Container/SOAP5.php create mode 100644 includes/pear/Auth/Container/vpopmail.php create mode 100644 includes/pear/Auth/Controller.php create mode 100644 includes/pear/Auth/Frontend/Html.php create mode 100644 includes/pear/Console/Getopt.php create mode 100644 includes/pear/HTTP.php create mode 100644 includes/pear/HTTP/Download.php create mode 100644 includes/pear/HTTP/Download/Archive.php create mode 100644 includes/pear/HTTP/Download/PgLOB.php create mode 100644 includes/pear/HTTP/Header.php create mode 100644 includes/pear/HTTP/Header/Cache.php create mode 100644 includes/pear/Image/GraphViz.php create mode 100644 includes/pear/MIME/Type.php create mode 100644 includes/pear/MIME/Type/Extension.php create mode 100644 includes/pear/MIME/Type/Parameter.php create mode 100644 includes/pear/OS/Guess.php create mode 100644 includes/pear/PEAR.php create mode 100644 includes/pear/PEAR/Autoloader.php create mode 100644 includes/pear/PEAR/Builder.php create mode 100644 includes/pear/PEAR/ChannelFile.php create mode 100644 includes/pear/PEAR/ChannelFile/Parser.php create mode 100644 includes/pear/PEAR/Command.php create mode 100644 includes/pear/PEAR/Command/Auth.php create mode 100644 includes/pear/PEAR/Command/Auth.xml create mode 100644 includes/pear/PEAR/Command/Build.php create mode 100644 includes/pear/PEAR/Command/Build.xml create mode 100644 includes/pear/PEAR/Command/Channels.php create mode 100644 includes/pear/PEAR/Command/Channels.xml create mode 100644 includes/pear/PEAR/Command/Common.php create mode 100644 includes/pear/PEAR/Command/Config.php create mode 100644 includes/pear/PEAR/Command/Config.xml create mode 100644 includes/pear/PEAR/Command/Install.php create mode 100644 includes/pear/PEAR/Command/Install.xml create mode 100644 includes/pear/PEAR/Command/Mirror.php create mode 100644 includes/pear/PEAR/Command/Mirror.xml create mode 100644 includes/pear/PEAR/Command/Package.php create mode 100644 includes/pear/PEAR/Command/Package.xml create mode 100644 includes/pear/PEAR/Command/Pickle.php create mode 100644 includes/pear/PEAR/Command/Pickle.xml create mode 100644 includes/pear/PEAR/Command/Registry.php create mode 100644 includes/pear/PEAR/Command/Registry.xml create mode 100644 includes/pear/PEAR/Command/Remote.php create mode 100644 includes/pear/PEAR/Command/Remote.xml create mode 100644 includes/pear/PEAR/Command/Test.php create mode 100644 includes/pear/PEAR/Command/Test.xml create mode 100644 includes/pear/PEAR/Common.php create mode 100644 includes/pear/PEAR/Config.php create mode 100644 includes/pear/PEAR/Dependency2.php create mode 100644 includes/pear/PEAR/DependencyDB.php create mode 100644 includes/pear/PEAR/Downloader.php create mode 100644 includes/pear/PEAR/Downloader/Package.php create mode 100644 includes/pear/PEAR/ErrorStack.php create mode 100644 includes/pear/PEAR/ErrorStack5.php create mode 100644 includes/pear/PEAR/Exception.php create mode 100644 includes/pear/PEAR/FixPHP5PEARWarnings.php create mode 100644 includes/pear/PEAR/Frontend.php create mode 100644 includes/pear/PEAR/Frontend/CLI.php create mode 100644 includes/pear/PEAR/Installer.php create mode 100644 includes/pear/PEAR/Installer/Role.php create mode 100644 includes/pear/PEAR/Installer/Role/Cfg.php create mode 100644 includes/pear/PEAR/Installer/Role/Cfg.xml create mode 100644 includes/pear/PEAR/Installer/Role/Common.php create mode 100644 includes/pear/PEAR/Installer/Role/Data.php create mode 100644 includes/pear/PEAR/Installer/Role/Data.xml create mode 100644 includes/pear/PEAR/Installer/Role/Doc.php create mode 100644 includes/pear/PEAR/Installer/Role/Doc.xml create mode 100644 includes/pear/PEAR/Installer/Role/Ext.php create mode 100644 includes/pear/PEAR/Installer/Role/Ext.xml create mode 100644 includes/pear/PEAR/Installer/Role/Php.php create mode 100644 includes/pear/PEAR/Installer/Role/Php.xml create mode 100644 includes/pear/PEAR/Installer/Role/Script.php create mode 100644 includes/pear/PEAR/Installer/Role/Script.xml create mode 100644 includes/pear/PEAR/Installer/Role/Src.php create mode 100644 includes/pear/PEAR/Installer/Role/Src.xml create mode 100644 includes/pear/PEAR/Installer/Role/Test.php create mode 100644 includes/pear/PEAR/Installer/Role/Test.xml create mode 100644 includes/pear/PEAR/Installer/Role/Www.php create mode 100644 includes/pear/PEAR/Installer/Role/Www.xml create mode 100644 includes/pear/PEAR/PackageFile.php create mode 100644 includes/pear/PEAR/PackageFile/Generator/v1.php create mode 100644 includes/pear/PEAR/PackageFile/Generator/v2.php create mode 100644 includes/pear/PEAR/PackageFile/Parser/v1.php create mode 100644 includes/pear/PEAR/PackageFile/Parser/v2.php create mode 100644 includes/pear/PEAR/PackageFile/v1.php create mode 100644 includes/pear/PEAR/PackageFile/v2.php create mode 100644 includes/pear/PEAR/PackageFile/v2/Validator.php create mode 100644 includes/pear/PEAR/PackageFile/v2/rw.php create mode 100644 includes/pear/PEAR/Packager.php create mode 100644 includes/pear/PEAR/REST.php create mode 100644 includes/pear/PEAR/REST/10.php create mode 100644 includes/pear/PEAR/REST/11.php create mode 100644 includes/pear/PEAR/REST/13.php create mode 100644 includes/pear/PEAR/REST/14.php create mode 100644 includes/pear/PEAR/Registry.php create mode 100644 includes/pear/PEAR/RunTest.php create mode 100644 includes/pear/PEAR/Start.php create mode 100644 includes/pear/PEAR/Start/CLI.php create mode 100644 includes/pear/PEAR/Task/Common.php create mode 100644 includes/pear/PEAR/Task/Postinstallscript.php create mode 100644 includes/pear/PEAR/Task/Postinstallscript/rw.php create mode 100644 includes/pear/PEAR/Task/Replace.php create mode 100644 includes/pear/PEAR/Task/Replace/rw.php create mode 100644 includes/pear/PEAR/Task/Unixeol.php create mode 100644 includes/pear/PEAR/Task/Unixeol/rw.php create mode 100644 includes/pear/PEAR/Task/Windowseol.php create mode 100644 includes/pear/PEAR/Task/Windowseol/rw.php create mode 100644 includes/pear/PEAR/Validate.php create mode 100644 includes/pear/PEAR/Validator/PECL.php create mode 100644 includes/pear/PEAR/Warning.php create mode 100644 includes/pear/PEAR/XMLParser.php create mode 100644 includes/pear/PEAR5.php create mode 100644 includes/pear/Structures/Graph.php create mode 100644 includes/pear/Structures/Graph/Manipulator/AcyclicTest.php create mode 100644 includes/pear/Structures/Graph/Manipulator/TopologicalSorter.php create mode 100644 includes/pear/Structures/Graph/Node.php create mode 100644 includes/pear/System.php create mode 100644 includes/pear/System/Command.php create mode 100644 includes/pear/Text/Diff.php create mode 100644 includes/pear/Text/Diff/Engine/native.php create mode 100644 includes/pear/Text/Diff/Engine/shell.php create mode 100644 includes/pear/Text/Diff/Engine/string.php create mode 100644 includes/pear/Text/Diff/Engine/xdiff.php create mode 100644 includes/pear/Text/Diff/Mapped.php create mode 100644 includes/pear/Text/Diff/Renderer.php create mode 100644 includes/pear/Text/Diff/Renderer/context.php create mode 100644 includes/pear/Text/Diff/Renderer/inline.php create mode 100644 includes/pear/Text/Diff/Renderer/unified.php create mode 100644 includes/pear/Text/Diff/ThreeWay.php create mode 100644 includes/pear/Text/Diff3.php create mode 100644 includes/pear/Text/Wiki.php create mode 100644 includes/pear/Text/Wiki/Creole.php create mode 100644 includes/pear/Text/Wiki/Default.php create mode 100644 includes/pear/Text/Wiki/Parse.php create mode 100644 includes/pear/Text/Wiki/Parse/Creole/Address.php create mode 100644 includes/pear/Text/Wiki/Parse/Creole/Blockquote.php create mode 100644 includes/pear/Text/Wiki/Parse/Creole/Box.php create mode 100644 includes/pear/Text/Wiki/Parse/Creole/Break.php create mode 100644 includes/pear/Text/Wiki/Parse/Creole/Center.php create mode 100644 includes/pear/Text/Wiki/Parse/Creole/Delimiter.php create mode 100644 includes/pear/Text/Wiki/Parse/Creole/Emphasis.php create mode 100644 includes/pear/Text/Wiki/Parse/Creole/Footnote.php create mode 100644 includes/pear/Text/Wiki/Parse/Creole/Heading.php create mode 100644 includes/pear/Text/Wiki/Parse/Creole/Horiz.php create mode 100644 includes/pear/Text/Wiki/Parse/Creole/Image.php create mode 100644 includes/pear/Text/Wiki/Parse/Creole/List.php create mode 100644 includes/pear/Text/Wiki/Parse/Creole/Newline.php create mode 100644 includes/pear/Text/Wiki/Parse/Creole/Paragraph.php create mode 100644 includes/pear/Text/Wiki/Parse/Creole/Prefilter.php create mode 100644 includes/pear/Text/Wiki/Parse/Creole/Preformatted.php create mode 100644 includes/pear/Text/Wiki/Parse/Creole/Raw.php create mode 100644 includes/pear/Text/Wiki/Parse/Creole/Strong.php create mode 100644 includes/pear/Text/Wiki/Parse/Creole/Subscript.php create mode 100644 includes/pear/Text/Wiki/Parse/Creole/Superscript.php create mode 100644 includes/pear/Text/Wiki/Parse/Creole/Table.php create mode 100644 includes/pear/Text/Wiki/Parse/Creole/Tighten.php create mode 100644 includes/pear/Text/Wiki/Parse/Creole/Trim.php create mode 100644 includes/pear/Text/Wiki/Parse/Creole/Tt.php create mode 100644 includes/pear/Text/Wiki/Parse/Creole/Underline.php create mode 100644 includes/pear/Text/Wiki/Parse/Creole/Url.php create mode 100644 includes/pear/Text/Wiki/Parse/Creole/Wikilink.php create mode 100644 includes/pear/Text/Wiki/Parse/Default/Anchor.php create mode 100644 includes/pear/Text/Wiki/Parse/Default/Blockquote.php create mode 100644 includes/pear/Text/Wiki/Parse/Default/Bold.php create mode 100644 includes/pear/Text/Wiki/Parse/Default/Break.php create mode 100644 includes/pear/Text/Wiki/Parse/Default/Center.php create mode 100644 includes/pear/Text/Wiki/Parse/Default/Code.php create mode 100644 includes/pear/Text/Wiki/Parse/Default/Colortext.php create mode 100644 includes/pear/Text/Wiki/Parse/Default/Deflist.php create mode 100644 includes/pear/Text/Wiki/Parse/Default/Delimiter.php create mode 100644 includes/pear/Text/Wiki/Parse/Default/Embed.php create mode 100644 includes/pear/Text/Wiki/Parse/Default/Emphasis.php create mode 100644 includes/pear/Text/Wiki/Parse/Default/Freelink.php create mode 100644 includes/pear/Text/Wiki/Parse/Default/Function.php create mode 100644 includes/pear/Text/Wiki/Parse/Default/Heading.php create mode 100644 includes/pear/Text/Wiki/Parse/Default/Horiz.php create mode 100644 includes/pear/Text/Wiki/Parse/Default/Html.php create mode 100644 includes/pear/Text/Wiki/Parse/Default/Image.php create mode 100644 includes/pear/Text/Wiki/Parse/Default/Include.php create mode 100644 includes/pear/Text/Wiki/Parse/Default/Interwiki.php create mode 100644 includes/pear/Text/Wiki/Parse/Default/Italic.php create mode 100644 includes/pear/Text/Wiki/Parse/Default/List.php create mode 100644 includes/pear/Text/Wiki/Parse/Default/Newline.php create mode 100644 includes/pear/Text/Wiki/Parse/Default/Paragraph.php create mode 100644 includes/pear/Text/Wiki/Parse/Default/Phplookup.php create mode 100644 includes/pear/Text/Wiki/Parse/Default/Prefilter.php create mode 100644 includes/pear/Text/Wiki/Parse/Default/Raw.php create mode 100644 includes/pear/Text/Wiki/Parse/Default/Revise.php create mode 100644 includes/pear/Text/Wiki/Parse/Default/Smiley.php create mode 100644 includes/pear/Text/Wiki/Parse/Default/Strong.php create mode 100644 includes/pear/Text/Wiki/Parse/Default/Subscript.php create mode 100644 includes/pear/Text/Wiki/Parse/Default/Superscript.php create mode 100644 includes/pear/Text/Wiki/Parse/Default/Table.php create mode 100644 includes/pear/Text/Wiki/Parse/Default/Tighten.php create mode 100644 includes/pear/Text/Wiki/Parse/Default/Toc.php create mode 100644 includes/pear/Text/Wiki/Parse/Default/Tt.php create mode 100644 includes/pear/Text/Wiki/Parse/Default/Underline.php create mode 100644 includes/pear/Text/Wiki/Parse/Default/Url.php create mode 100644 includes/pear/Text/Wiki/Parse/Default/Wikilink.php create mode 100644 includes/pear/Text/Wiki/Render.php create mode 100644 includes/pear/Text/Wiki/Render/Creole.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Address.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Anchor.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Blockquote.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Bold.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Box.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Break.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Center.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Code.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Colortext.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Deflist.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Delimiter.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Embed.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Emphasis.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Freelink.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Function.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Heading.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Horiz.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Html.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Image.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Include.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Interwiki.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Italic.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/List.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Newline.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Paragraph.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Phplookup.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Prefilter.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Preformatted.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Raw.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Revise.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Strong.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Subscript.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Superscript.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Table.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Tighten.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Toc.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Tt.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Underline.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Url.php create mode 100644 includes/pear/Text/Wiki/Render/Creole/Wikilink.php create mode 100644 includes/pear/Text/Wiki/Render/Latex.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Anchor.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Blockquote.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Bold.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Box.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Break.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Center.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Code.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Colortext.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Deflist.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Delimiter.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Embed.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Emphasis.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Font.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Freelink.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Function.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Heading.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Horiz.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Html.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Image.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Include.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Interwiki.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Italic.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/List.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Newline.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Page.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Paragraph.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Phplookup.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Plugin.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Prefilter.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Preformatted.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Raw.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Revise.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Smiley.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Specialchar.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Strong.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Subscript.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Superscript.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Table.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Tighten.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Titlebar.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Toc.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Tt.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Underline.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Url.php create mode 100644 includes/pear/Text/Wiki/Render/Latex/Wikilink.php create mode 100644 includes/pear/Text/Wiki/Render/Plain.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Anchor.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Blockquote.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Bold.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Box.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Break.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Center.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Code.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Colortext.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Deflist.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Delimiter.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Embed.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Emphasis.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Font.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Freelink.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Function.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Heading.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Horiz.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Html.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Image.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Include.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Interwiki.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Italic.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/List.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Newline.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Page.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Paragraph.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Phplookup.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Plugin.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Prefilter.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Preformatted.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Raw.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Revise.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Smiley.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Specialchar.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Strong.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Subscript.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Superscript.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Table.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Tighten.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Titlebar.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Toc.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Tt.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Underline.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Url.php create mode 100644 includes/pear/Text/Wiki/Render/Plain/Wikilink.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Address.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Anchor.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Blockquote.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Bold.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Box.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Break.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Center.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Code.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Colortext.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Deflist.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Delimiter.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Embed.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Emphasis.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Font.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Freelink.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Function.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Heading.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Horiz.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Html.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Image.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Include.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Interwiki.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Italic.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/List.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Newline.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Page.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Paragraph.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Phplookup.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Plugin.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Prefilter.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Preformatted.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Raw.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Revise.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Smiley.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Specialchar.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Strong.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Subscript.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Superscript.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Table.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Tighten.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Titlebar.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Toc.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Tt.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Underline.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Url.php create mode 100644 includes/pear/Text/Wiki/Render/Xhtml/Wikilink.php create mode 100644 includes/pear/XML/Util.php create mode 100644 includes/phpcontrib_lib.php create mode 100644 includes/phpcoord/phpcoord-2.3.php create mode 100644 includes/phpcoord/readme-2.3.txt create mode 100644 includes/phpcoord/test-2.3.php create mode 100644 includes/phplot.php create mode 100644 includes/phpmailer/README create mode 100644 includes/phpmailer/class.phpmailer.php create mode 100644 includes/phpmailer/class.pop3.php create mode 100644 includes/phpmailer/class.smtp.php create mode 100644 includes/phpmailer/language/phpmailer.lang-br.php create mode 100644 includes/phpmailer/language/phpmailer.lang-ca.php create mode 100644 includes/phpmailer/language/phpmailer.lang-cz.php create mode 100644 includes/phpmailer/language/phpmailer.lang-de.php create mode 100644 includes/phpmailer/language/phpmailer.lang-dk.php create mode 100644 includes/phpmailer/language/phpmailer.lang-en.php create mode 100644 includes/phpmailer/language/phpmailer.lang-es.php create mode 100644 includes/phpmailer/language/phpmailer.lang-fi.php create mode 100644 includes/phpmailer/language/phpmailer.lang-fo.php create mode 100644 includes/phpmailer/language/phpmailer.lang-fr.php create mode 100644 includes/phpmailer/language/phpmailer.lang-hu.php create mode 100644 includes/phpmailer/language/phpmailer.lang-it.php create mode 100644 includes/phpmailer/language/phpmailer.lang-ja.php create mode 100644 includes/phpmailer/language/phpmailer.lang-nl.php create mode 100644 includes/phpmailer/language/phpmailer.lang-no.php create mode 100644 includes/phpmailer/language/phpmailer.lang-pl.php create mode 100644 includes/phpmailer/language/phpmailer.lang-ro.php create mode 100644 includes/phpmailer/language/phpmailer.lang-ru.php create mode 100644 includes/phpmailer/language/phpmailer.lang-se.php create mode 100644 includes/phpmailer/language/phpmailer.lang-tr.php create mode 100644 includes/phpsniff/index.php create mode 100644 includes/phpsniff/phpSniff.class.php create mode 100644 includes/phpsniff/phpSniff.core.php create mode 100644 includes/simplepie/idn/idna_convert.class.php create mode 100644 includes/simplepie/idn/npdata.ser create mode 100644 includes/simplepie/simplepie.inc create mode 100644 includes/spyc/README create mode 100644 includes/spyc/examples/yaml-dump.php create mode 100644 includes/spyc/examples/yaml-load.php create mode 100644 includes/spyc/php4/5to4.php create mode 100644 includes/spyc/php4/spyc.php4 create mode 100644 includes/spyc/php4/test.php4 create mode 100644 includes/spyc/spyc.php create mode 100644 includes/spyc/spyc.yaml create mode 100644 includes/spyc/tests/DumpTest.php create mode 100644 includes/spyc/tests/IndentTest.php create mode 100644 includes/spyc/tests/ParseTest.php create mode 100644 includes/spyc/tests/failing1.yaml create mode 100644 includes/spyc/tests/indent_1.yaml create mode 100644 includes/spyc/tests/quotes.yaml create mode 100644 includes/tar.class.php create mode 100644 includes/template_biticon_updater.sh create mode 100644 includes/tree.php create mode 100644 includes/zip_lib.php delete mode 100644 is_email.php delete mode 100644 jpeg_metadata_tk/COPYING.txt delete mode 100644 jpeg_metadata_tk/EXIF.php delete mode 100644 jpeg_metadata_tk/EXIF_Makernote.php delete mode 100644 jpeg_metadata_tk/EXIF_Tags.php delete mode 100644 jpeg_metadata_tk/Edit_File_Info.php delete mode 100644 jpeg_metadata_tk/IPTC.php delete mode 100644 jpeg_metadata_tk/JFIF.php delete mode 100644 jpeg_metadata_tk/JPEG.php delete mode 100644 jpeg_metadata_tk/Makernotes/Pentax.php delete mode 100644 jpeg_metadata_tk/Makernotes/agfa.php delete mode 100644 jpeg_metadata_tk/Makernotes/canon.php delete mode 100644 jpeg_metadata_tk/Makernotes/casio.php delete mode 100644 jpeg_metadata_tk/Makernotes/epson.php delete mode 100644 jpeg_metadata_tk/Makernotes/fujifilm.php delete mode 100644 jpeg_metadata_tk/Makernotes/konica_minolta.php delete mode 100644 jpeg_metadata_tk/Makernotes/kyocera.php delete mode 100644 jpeg_metadata_tk/Makernotes/nikon.php delete mode 100644 jpeg_metadata_tk/Makernotes/olympus.php delete mode 100644 jpeg_metadata_tk/Makernotes/panasonic.php delete mode 100644 jpeg_metadata_tk/Makernotes/ricoh.php delete mode 100644 jpeg_metadata_tk/Makernotes/sony.php delete mode 100644 jpeg_metadata_tk/PIM.php delete mode 100644 jpeg_metadata_tk/Photoshop_File_Info.php delete mode 100644 jpeg_metadata_tk/Photoshop_IRB.php delete mode 100644 jpeg_metadata_tk/PictureInfo.php delete mode 100644 jpeg_metadata_tk/Toolkit_Version.php delete mode 100644 jpeg_metadata_tk/Unicode.php delete mode 100644 jpeg_metadata_tk/Write_File_Info.php delete mode 100644 jpeg_metadata_tk/XML.php delete mode 100644 jpeg_metadata_tk/XMP.php delete mode 100644 jpeg_metadata_tk/documentation/Camera_List_1.0.pdf delete mode 100644 jpeg_metadata_tk/documentation/Edit_File_Info_Example.php delete mode 100644 jpeg_metadata_tk/documentation/Example.php delete mode 100644 jpeg_metadata_tk/documentation/TIFFExample.php delete mode 100644 jpeg_metadata_tk/documentation/changes.html delete mode 100644 jpeg_metadata_tk/documentation/css_terms.html delete mode 100644 jpeg_metadata_tk/documentation/edit_write_file_info.html delete mode 100644 jpeg_metadata_tk/documentation/example.html delete mode 100644 jpeg_metadata_tk/documentation/examples.html delete mode 100644 jpeg_metadata_tk/documentation/exif.html delete mode 100644 jpeg_metadata_tk/documentation/index.html delete mode 100644 jpeg_metadata_tk/documentation/intro.html delete mode 100644 jpeg_metadata_tk/documentation/jfif.html delete mode 100644 jpeg_metadata_tk/documentation/jpeg.html delete mode 100644 jpeg_metadata_tk/documentation/photoshop.html delete mode 100644 jpeg_metadata_tk/documentation/photoshop_file_info.html delete mode 100644 jpeg_metadata_tk/documentation/picture_info.html delete mode 100644 jpeg_metadata_tk/documentation/style.css delete mode 100644 jpeg_metadata_tk/documentation/tiffexample.html delete mode 100644 jpeg_metadata_tk/documentation/todo.html delete mode 100644 jpeg_metadata_tk/documentation/xmp.html delete mode 100644 jpeg_metadata_tk/get_JFXX_thumb.php delete mode 100644 jpeg_metadata_tk/get_casio_thumb.php delete mode 100644 jpeg_metadata_tk/get_exif_thumb.php delete mode 100644 jpeg_metadata_tk/get_minolta_thumb.php delete mode 100644 jpeg_metadata_tk/get_ps_thumb.php delete mode 100644 jpeg_metadata_tk/pjmt_utils.php delete mode 100644 mailman.cfg delete mode 100644 mailman_lib.php delete mode 100644 mailman_lib.py delete mode 100644 markdown.php delete mode 100644 mime_lib.php delete mode 100644 mimetypes.php delete mode 100644 pclzip_lib.php delete mode 100644 pear/Archive/Tar.php delete mode 100644 pear/Auth.php delete mode 100644 pear/Auth/Anonymous.php delete mode 100644 pear/Auth/Auth.php delete mode 100644 pear/Auth/Container.php delete mode 100644 pear/Auth/Container/Array.php delete mode 100644 pear/Auth/Container/DB.php delete mode 100644 pear/Auth/Container/DBLite.php delete mode 100644 pear/Auth/Container/File.php delete mode 100644 pear/Auth/Container/IMAP.php delete mode 100644 pear/Auth/Container/KADM5.php delete mode 100644 pear/Auth/Container/LDAP.php delete mode 100644 pear/Auth/Container/MDB.php delete mode 100644 pear/Auth/Container/MDB2.php delete mode 100644 pear/Auth/Container/Multiple.php delete mode 100644 pear/Auth/Container/NetVPOPMaild.php delete mode 100644 pear/Auth/Container/PEAR.php delete mode 100644 pear/Auth/Container/POP3.php delete mode 100644 pear/Auth/Container/RADIUS.php delete mode 100644 pear/Auth/Container/SAP.php delete mode 100644 pear/Auth/Container/SMBPasswd.php delete mode 100644 pear/Auth/Container/SOAP.php delete mode 100644 pear/Auth/Container/SOAP5.php delete mode 100644 pear/Auth/Container/vpopmail.php delete mode 100644 pear/Auth/Controller.php delete mode 100644 pear/Auth/Frontend/Html.php delete mode 100644 pear/Console/Getopt.php delete mode 100644 pear/HTTP.php delete mode 100644 pear/HTTP/Download.php delete mode 100644 pear/HTTP/Download/Archive.php delete mode 100644 pear/HTTP/Download/PgLOB.php delete mode 100644 pear/HTTP/Header.php delete mode 100644 pear/HTTP/Header/Cache.php delete mode 100644 pear/Image/GraphViz.php delete mode 100644 pear/MIME/Type.php delete mode 100644 pear/MIME/Type/Extension.php delete mode 100644 pear/MIME/Type/Parameter.php delete mode 100644 pear/OS/Guess.php delete mode 100644 pear/PEAR.php delete mode 100644 pear/PEAR/Autoloader.php delete mode 100644 pear/PEAR/Builder.php delete mode 100644 pear/PEAR/ChannelFile.php delete mode 100644 pear/PEAR/ChannelFile/Parser.php delete mode 100644 pear/PEAR/Command.php delete mode 100644 pear/PEAR/Command/Auth.php delete mode 100644 pear/PEAR/Command/Auth.xml delete mode 100644 pear/PEAR/Command/Build.php delete mode 100644 pear/PEAR/Command/Build.xml delete mode 100644 pear/PEAR/Command/Channels.php delete mode 100644 pear/PEAR/Command/Channels.xml delete mode 100644 pear/PEAR/Command/Common.php delete mode 100644 pear/PEAR/Command/Config.php delete mode 100644 pear/PEAR/Command/Config.xml delete mode 100644 pear/PEAR/Command/Install.php delete mode 100644 pear/PEAR/Command/Install.xml delete mode 100644 pear/PEAR/Command/Mirror.php delete mode 100644 pear/PEAR/Command/Mirror.xml delete mode 100644 pear/PEAR/Command/Package.php delete mode 100644 pear/PEAR/Command/Package.xml delete mode 100644 pear/PEAR/Command/Pickle.php delete mode 100644 pear/PEAR/Command/Pickle.xml delete mode 100644 pear/PEAR/Command/Registry.php delete mode 100644 pear/PEAR/Command/Registry.xml delete mode 100644 pear/PEAR/Command/Remote.php delete mode 100644 pear/PEAR/Command/Remote.xml delete mode 100644 pear/PEAR/Command/Test.php delete mode 100644 pear/PEAR/Command/Test.xml delete mode 100644 pear/PEAR/Common.php delete mode 100644 pear/PEAR/Config.php delete mode 100644 pear/PEAR/Dependency2.php delete mode 100644 pear/PEAR/DependencyDB.php delete mode 100644 pear/PEAR/Downloader.php delete mode 100644 pear/PEAR/Downloader/Package.php delete mode 100644 pear/PEAR/ErrorStack.php delete mode 100644 pear/PEAR/ErrorStack5.php delete mode 100644 pear/PEAR/Exception.php delete mode 100644 pear/PEAR/FixPHP5PEARWarnings.php delete mode 100644 pear/PEAR/Frontend.php delete mode 100644 pear/PEAR/Frontend/CLI.php delete mode 100644 pear/PEAR/Installer.php delete mode 100644 pear/PEAR/Installer/Role.php delete mode 100644 pear/PEAR/Installer/Role/Cfg.php delete mode 100644 pear/PEAR/Installer/Role/Cfg.xml delete mode 100644 pear/PEAR/Installer/Role/Common.php delete mode 100644 pear/PEAR/Installer/Role/Data.php delete mode 100644 pear/PEAR/Installer/Role/Data.xml delete mode 100644 pear/PEAR/Installer/Role/Doc.php delete mode 100644 pear/PEAR/Installer/Role/Doc.xml delete mode 100644 pear/PEAR/Installer/Role/Ext.php delete mode 100644 pear/PEAR/Installer/Role/Ext.xml delete mode 100644 pear/PEAR/Installer/Role/Php.php delete mode 100644 pear/PEAR/Installer/Role/Php.xml delete mode 100644 pear/PEAR/Installer/Role/Script.php delete mode 100644 pear/PEAR/Installer/Role/Script.xml delete mode 100644 pear/PEAR/Installer/Role/Src.php delete mode 100644 pear/PEAR/Installer/Role/Src.xml delete mode 100644 pear/PEAR/Installer/Role/Test.php delete mode 100644 pear/PEAR/Installer/Role/Test.xml delete mode 100644 pear/PEAR/Installer/Role/Www.php delete mode 100644 pear/PEAR/Installer/Role/Www.xml delete mode 100644 pear/PEAR/PackageFile.php delete mode 100644 pear/PEAR/PackageFile/Generator/v1.php delete mode 100644 pear/PEAR/PackageFile/Generator/v2.php delete mode 100644 pear/PEAR/PackageFile/Parser/v1.php delete mode 100644 pear/PEAR/PackageFile/Parser/v2.php delete mode 100644 pear/PEAR/PackageFile/v1.php delete mode 100644 pear/PEAR/PackageFile/v2.php delete mode 100644 pear/PEAR/PackageFile/v2/Validator.php delete mode 100644 pear/PEAR/PackageFile/v2/rw.php delete mode 100644 pear/PEAR/Packager.php delete mode 100644 pear/PEAR/REST.php delete mode 100644 pear/PEAR/REST/10.php delete mode 100644 pear/PEAR/REST/11.php delete mode 100644 pear/PEAR/REST/13.php delete mode 100644 pear/PEAR/REST/14.php delete mode 100644 pear/PEAR/Registry.php delete mode 100644 pear/PEAR/RunTest.php delete mode 100644 pear/PEAR/Start.php delete mode 100644 pear/PEAR/Start/CLI.php delete mode 100644 pear/PEAR/Task/Common.php delete mode 100644 pear/PEAR/Task/Postinstallscript.php delete mode 100644 pear/PEAR/Task/Postinstallscript/rw.php delete mode 100644 pear/PEAR/Task/Replace.php delete mode 100644 pear/PEAR/Task/Replace/rw.php delete mode 100644 pear/PEAR/Task/Unixeol.php delete mode 100644 pear/PEAR/Task/Unixeol/rw.php delete mode 100644 pear/PEAR/Task/Windowseol.php delete mode 100644 pear/PEAR/Task/Windowseol/rw.php delete mode 100644 pear/PEAR/Validate.php delete mode 100644 pear/PEAR/Validator/PECL.php delete mode 100644 pear/PEAR/Warning.php delete mode 100644 pear/PEAR/XMLParser.php delete mode 100644 pear/PEAR5.php delete mode 100644 pear/Structures/Graph.php delete mode 100644 pear/Structures/Graph/Manipulator/AcyclicTest.php delete mode 100644 pear/Structures/Graph/Manipulator/TopologicalSorter.php delete mode 100644 pear/Structures/Graph/Node.php delete mode 100644 pear/System.php delete mode 100644 pear/System/Command.php delete mode 100644 pear/Text/Diff.php delete mode 100644 pear/Text/Diff/Engine/native.php delete mode 100644 pear/Text/Diff/Engine/shell.php delete mode 100644 pear/Text/Diff/Engine/string.php delete mode 100644 pear/Text/Diff/Engine/xdiff.php delete mode 100644 pear/Text/Diff/Mapped.php delete mode 100644 pear/Text/Diff/Renderer.php delete mode 100644 pear/Text/Diff/Renderer/context.php delete mode 100644 pear/Text/Diff/Renderer/inline.php delete mode 100644 pear/Text/Diff/Renderer/unified.php delete mode 100644 pear/Text/Diff/ThreeWay.php delete mode 100644 pear/Text/Diff3.php delete mode 100644 pear/Text/Wiki.php delete mode 100644 pear/Text/Wiki/Creole.php delete mode 100644 pear/Text/Wiki/Default.php delete mode 100644 pear/Text/Wiki/Parse.php delete mode 100644 pear/Text/Wiki/Parse/Creole/Address.php delete mode 100644 pear/Text/Wiki/Parse/Creole/Blockquote.php delete mode 100644 pear/Text/Wiki/Parse/Creole/Box.php delete mode 100644 pear/Text/Wiki/Parse/Creole/Break.php delete mode 100644 pear/Text/Wiki/Parse/Creole/Center.php delete mode 100644 pear/Text/Wiki/Parse/Creole/Delimiter.php delete mode 100644 pear/Text/Wiki/Parse/Creole/Emphasis.php delete mode 100644 pear/Text/Wiki/Parse/Creole/Footnote.php delete mode 100644 pear/Text/Wiki/Parse/Creole/Heading.php delete mode 100644 pear/Text/Wiki/Parse/Creole/Horiz.php delete mode 100644 pear/Text/Wiki/Parse/Creole/Image.php delete mode 100644 pear/Text/Wiki/Parse/Creole/List.php delete mode 100644 pear/Text/Wiki/Parse/Creole/Newline.php delete mode 100644 pear/Text/Wiki/Parse/Creole/Paragraph.php delete mode 100644 pear/Text/Wiki/Parse/Creole/Prefilter.php delete mode 100644 pear/Text/Wiki/Parse/Creole/Preformatted.php delete mode 100644 pear/Text/Wiki/Parse/Creole/Raw.php delete mode 100644 pear/Text/Wiki/Parse/Creole/Strong.php delete mode 100644 pear/Text/Wiki/Parse/Creole/Subscript.php delete mode 100644 pear/Text/Wiki/Parse/Creole/Superscript.php delete mode 100644 pear/Text/Wiki/Parse/Creole/Table.php delete mode 100644 pear/Text/Wiki/Parse/Creole/Tighten.php delete mode 100644 pear/Text/Wiki/Parse/Creole/Trim.php delete mode 100644 pear/Text/Wiki/Parse/Creole/Tt.php delete mode 100644 pear/Text/Wiki/Parse/Creole/Underline.php delete mode 100644 pear/Text/Wiki/Parse/Creole/Url.php delete mode 100644 pear/Text/Wiki/Parse/Creole/Wikilink.php delete mode 100644 pear/Text/Wiki/Parse/Default/Anchor.php delete mode 100644 pear/Text/Wiki/Parse/Default/Blockquote.php delete mode 100644 pear/Text/Wiki/Parse/Default/Bold.php delete mode 100644 pear/Text/Wiki/Parse/Default/Break.php delete mode 100644 pear/Text/Wiki/Parse/Default/Center.php delete mode 100644 pear/Text/Wiki/Parse/Default/Code.php delete mode 100644 pear/Text/Wiki/Parse/Default/Colortext.php delete mode 100644 pear/Text/Wiki/Parse/Default/Deflist.php delete mode 100644 pear/Text/Wiki/Parse/Default/Delimiter.php delete mode 100644 pear/Text/Wiki/Parse/Default/Embed.php delete mode 100644 pear/Text/Wiki/Parse/Default/Emphasis.php delete mode 100644 pear/Text/Wiki/Parse/Default/Freelink.php delete mode 100644 pear/Text/Wiki/Parse/Default/Function.php delete mode 100644 pear/Text/Wiki/Parse/Default/Heading.php delete mode 100644 pear/Text/Wiki/Parse/Default/Horiz.php delete mode 100644 pear/Text/Wiki/Parse/Default/Html.php delete mode 100644 pear/Text/Wiki/Parse/Default/Image.php delete mode 100644 pear/Text/Wiki/Parse/Default/Include.php delete mode 100644 pear/Text/Wiki/Parse/Default/Interwiki.php delete mode 100644 pear/Text/Wiki/Parse/Default/Italic.php delete mode 100644 pear/Text/Wiki/Parse/Default/List.php delete mode 100644 pear/Text/Wiki/Parse/Default/Newline.php delete mode 100644 pear/Text/Wiki/Parse/Default/Paragraph.php delete mode 100644 pear/Text/Wiki/Parse/Default/Phplookup.php delete mode 100644 pear/Text/Wiki/Parse/Default/Prefilter.php delete mode 100644 pear/Text/Wiki/Parse/Default/Raw.php delete mode 100644 pear/Text/Wiki/Parse/Default/Revise.php delete mode 100644 pear/Text/Wiki/Parse/Default/Smiley.php delete mode 100644 pear/Text/Wiki/Parse/Default/Strong.php delete mode 100644 pear/Text/Wiki/Parse/Default/Subscript.php delete mode 100644 pear/Text/Wiki/Parse/Default/Superscript.php delete mode 100644 pear/Text/Wiki/Parse/Default/Table.php delete mode 100644 pear/Text/Wiki/Parse/Default/Tighten.php delete mode 100644 pear/Text/Wiki/Parse/Default/Toc.php delete mode 100644 pear/Text/Wiki/Parse/Default/Tt.php delete mode 100644 pear/Text/Wiki/Parse/Default/Underline.php delete mode 100644 pear/Text/Wiki/Parse/Default/Url.php delete mode 100644 pear/Text/Wiki/Parse/Default/Wikilink.php delete mode 100644 pear/Text/Wiki/Render.php delete mode 100644 pear/Text/Wiki/Render/Creole.php delete mode 100644 pear/Text/Wiki/Render/Creole/Address.php delete mode 100644 pear/Text/Wiki/Render/Creole/Anchor.php delete mode 100644 pear/Text/Wiki/Render/Creole/Blockquote.php delete mode 100644 pear/Text/Wiki/Render/Creole/Bold.php delete mode 100644 pear/Text/Wiki/Render/Creole/Box.php delete mode 100644 pear/Text/Wiki/Render/Creole/Break.php delete mode 100644 pear/Text/Wiki/Render/Creole/Center.php delete mode 100644 pear/Text/Wiki/Render/Creole/Code.php delete mode 100644 pear/Text/Wiki/Render/Creole/Colortext.php delete mode 100644 pear/Text/Wiki/Render/Creole/Deflist.php delete mode 100644 pear/Text/Wiki/Render/Creole/Delimiter.php delete mode 100644 pear/Text/Wiki/Render/Creole/Embed.php delete mode 100644 pear/Text/Wiki/Render/Creole/Emphasis.php delete mode 100644 pear/Text/Wiki/Render/Creole/Freelink.php delete mode 100644 pear/Text/Wiki/Render/Creole/Function.php delete mode 100644 pear/Text/Wiki/Render/Creole/Heading.php delete mode 100644 pear/Text/Wiki/Render/Creole/Horiz.php delete mode 100644 pear/Text/Wiki/Render/Creole/Html.php delete mode 100644 pear/Text/Wiki/Render/Creole/Image.php delete mode 100644 pear/Text/Wiki/Render/Creole/Include.php delete mode 100644 pear/Text/Wiki/Render/Creole/Interwiki.php delete mode 100644 pear/Text/Wiki/Render/Creole/Italic.php delete mode 100644 pear/Text/Wiki/Render/Creole/List.php delete mode 100644 pear/Text/Wiki/Render/Creole/Newline.php delete mode 100644 pear/Text/Wiki/Render/Creole/Paragraph.php delete mode 100644 pear/Text/Wiki/Render/Creole/Phplookup.php delete mode 100644 pear/Text/Wiki/Render/Creole/Prefilter.php delete mode 100644 pear/Text/Wiki/Render/Creole/Preformatted.php delete mode 100644 pear/Text/Wiki/Render/Creole/Raw.php delete mode 100644 pear/Text/Wiki/Render/Creole/Revise.php delete mode 100644 pear/Text/Wiki/Render/Creole/Strong.php delete mode 100644 pear/Text/Wiki/Render/Creole/Subscript.php delete mode 100644 pear/Text/Wiki/Render/Creole/Superscript.php delete mode 100644 pear/Text/Wiki/Render/Creole/Table.php delete mode 100644 pear/Text/Wiki/Render/Creole/Tighten.php delete mode 100644 pear/Text/Wiki/Render/Creole/Toc.php delete mode 100644 pear/Text/Wiki/Render/Creole/Tt.php delete mode 100644 pear/Text/Wiki/Render/Creole/Underline.php delete mode 100644 pear/Text/Wiki/Render/Creole/Url.php delete mode 100644 pear/Text/Wiki/Render/Creole/Wikilink.php delete mode 100644 pear/Text/Wiki/Render/Latex.php delete mode 100644 pear/Text/Wiki/Render/Latex/Anchor.php delete mode 100644 pear/Text/Wiki/Render/Latex/Blockquote.php delete mode 100644 pear/Text/Wiki/Render/Latex/Bold.php delete mode 100644 pear/Text/Wiki/Render/Latex/Box.php delete mode 100644 pear/Text/Wiki/Render/Latex/Break.php delete mode 100644 pear/Text/Wiki/Render/Latex/Center.php delete mode 100644 pear/Text/Wiki/Render/Latex/Code.php delete mode 100644 pear/Text/Wiki/Render/Latex/Colortext.php delete mode 100644 pear/Text/Wiki/Render/Latex/Deflist.php delete mode 100644 pear/Text/Wiki/Render/Latex/Delimiter.php delete mode 100644 pear/Text/Wiki/Render/Latex/Embed.php delete mode 100644 pear/Text/Wiki/Render/Latex/Emphasis.php delete mode 100644 pear/Text/Wiki/Render/Latex/Font.php delete mode 100644 pear/Text/Wiki/Render/Latex/Freelink.php delete mode 100644 pear/Text/Wiki/Render/Latex/Function.php delete mode 100644 pear/Text/Wiki/Render/Latex/Heading.php delete mode 100644 pear/Text/Wiki/Render/Latex/Horiz.php delete mode 100644 pear/Text/Wiki/Render/Latex/Html.php delete mode 100644 pear/Text/Wiki/Render/Latex/Image.php delete mode 100644 pear/Text/Wiki/Render/Latex/Include.php delete mode 100644 pear/Text/Wiki/Render/Latex/Interwiki.php delete mode 100644 pear/Text/Wiki/Render/Latex/Italic.php delete mode 100644 pear/Text/Wiki/Render/Latex/List.php delete mode 100644 pear/Text/Wiki/Render/Latex/Newline.php delete mode 100644 pear/Text/Wiki/Render/Latex/Page.php delete mode 100644 pear/Text/Wiki/Render/Latex/Paragraph.php delete mode 100644 pear/Text/Wiki/Render/Latex/Phplookup.php delete mode 100644 pear/Text/Wiki/Render/Latex/Plugin.php delete mode 100644 pear/Text/Wiki/Render/Latex/Prefilter.php delete mode 100644 pear/Text/Wiki/Render/Latex/Preformatted.php delete mode 100644 pear/Text/Wiki/Render/Latex/Raw.php delete mode 100644 pear/Text/Wiki/Render/Latex/Revise.php delete mode 100644 pear/Text/Wiki/Render/Latex/Smiley.php delete mode 100644 pear/Text/Wiki/Render/Latex/Specialchar.php delete mode 100644 pear/Text/Wiki/Render/Latex/Strong.php delete mode 100644 pear/Text/Wiki/Render/Latex/Subscript.php delete mode 100644 pear/Text/Wiki/Render/Latex/Superscript.php delete mode 100644 pear/Text/Wiki/Render/Latex/Table.php delete mode 100644 pear/Text/Wiki/Render/Latex/Tighten.php delete mode 100644 pear/Text/Wiki/Render/Latex/Titlebar.php delete mode 100644 pear/Text/Wiki/Render/Latex/Toc.php delete mode 100644 pear/Text/Wiki/Render/Latex/Tt.php delete mode 100644 pear/Text/Wiki/Render/Latex/Underline.php delete mode 100644 pear/Text/Wiki/Render/Latex/Url.php delete mode 100644 pear/Text/Wiki/Render/Latex/Wikilink.php delete mode 100644 pear/Text/Wiki/Render/Plain.php delete mode 100644 pear/Text/Wiki/Render/Plain/Anchor.php delete mode 100644 pear/Text/Wiki/Render/Plain/Blockquote.php delete mode 100644 pear/Text/Wiki/Render/Plain/Bold.php delete mode 100644 pear/Text/Wiki/Render/Plain/Box.php delete mode 100644 pear/Text/Wiki/Render/Plain/Break.php delete mode 100644 pear/Text/Wiki/Render/Plain/Center.php delete mode 100644 pear/Text/Wiki/Render/Plain/Code.php delete mode 100644 pear/Text/Wiki/Render/Plain/Colortext.php delete mode 100644 pear/Text/Wiki/Render/Plain/Deflist.php delete mode 100644 pear/Text/Wiki/Render/Plain/Delimiter.php delete mode 100644 pear/Text/Wiki/Render/Plain/Embed.php delete mode 100644 pear/Text/Wiki/Render/Plain/Emphasis.php delete mode 100644 pear/Text/Wiki/Render/Plain/Font.php delete mode 100644 pear/Text/Wiki/Render/Plain/Freelink.php delete mode 100644 pear/Text/Wiki/Render/Plain/Function.php delete mode 100644 pear/Text/Wiki/Render/Plain/Heading.php delete mode 100644 pear/Text/Wiki/Render/Plain/Horiz.php delete mode 100644 pear/Text/Wiki/Render/Plain/Html.php delete mode 100644 pear/Text/Wiki/Render/Plain/Image.php delete mode 100644 pear/Text/Wiki/Render/Plain/Include.php delete mode 100644 pear/Text/Wiki/Render/Plain/Interwiki.php delete mode 100644 pear/Text/Wiki/Render/Plain/Italic.php delete mode 100644 pear/Text/Wiki/Render/Plain/List.php delete mode 100644 pear/Text/Wiki/Render/Plain/Newline.php delete mode 100644 pear/Text/Wiki/Render/Plain/Page.php delete mode 100644 pear/Text/Wiki/Render/Plain/Paragraph.php delete mode 100644 pear/Text/Wiki/Render/Plain/Phplookup.php delete mode 100644 pear/Text/Wiki/Render/Plain/Plugin.php delete mode 100644 pear/Text/Wiki/Render/Plain/Prefilter.php delete mode 100644 pear/Text/Wiki/Render/Plain/Preformatted.php delete mode 100644 pear/Text/Wiki/Render/Plain/Raw.php delete mode 100644 pear/Text/Wiki/Render/Plain/Revise.php delete mode 100644 pear/Text/Wiki/Render/Plain/Smiley.php delete mode 100644 pear/Text/Wiki/Render/Plain/Specialchar.php delete mode 100644 pear/Text/Wiki/Render/Plain/Strong.php delete mode 100644 pear/Text/Wiki/Render/Plain/Subscript.php delete mode 100644 pear/Text/Wiki/Render/Plain/Superscript.php delete mode 100644 pear/Text/Wiki/Render/Plain/Table.php delete mode 100644 pear/Text/Wiki/Render/Plain/Tighten.php delete mode 100644 pear/Text/Wiki/Render/Plain/Titlebar.php delete mode 100644 pear/Text/Wiki/Render/Plain/Toc.php delete mode 100644 pear/Text/Wiki/Render/Plain/Tt.php delete mode 100644 pear/Text/Wiki/Render/Plain/Underline.php delete mode 100644 pear/Text/Wiki/Render/Plain/Url.php delete mode 100644 pear/Text/Wiki/Render/Plain/Wikilink.php delete mode 100644 pear/Text/Wiki/Render/Xhtml.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Address.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Anchor.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Blockquote.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Bold.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Box.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Break.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Center.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Code.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Colortext.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Deflist.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Delimiter.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Embed.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Emphasis.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Font.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Freelink.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Function.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Heading.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Horiz.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Html.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Image.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Include.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Interwiki.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Italic.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/List.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Newline.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Page.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Paragraph.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Phplookup.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Plugin.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Prefilter.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Preformatted.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Raw.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Revise.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Smiley.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Specialchar.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Strong.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Subscript.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Superscript.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Table.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Tighten.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Titlebar.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Toc.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Tt.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Underline.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Url.php delete mode 100644 pear/Text/Wiki/Render/Xhtml/Wikilink.php delete mode 100644 pear/XML/Util.php delete mode 100644 phpcontrib_lib.php delete mode 100644 phpcoord/phpcoord-2.3.php delete mode 100644 phpcoord/readme-2.3.txt delete mode 100644 phpcoord/test-2.3.php delete mode 100644 phplot.php delete mode 100644 phpmailer/README delete mode 100644 phpmailer/class.phpmailer.php delete mode 100644 phpmailer/class.pop3.php delete mode 100644 phpmailer/class.smtp.php delete mode 100644 phpmailer/language/phpmailer.lang-br.php delete mode 100644 phpmailer/language/phpmailer.lang-ca.php delete mode 100644 phpmailer/language/phpmailer.lang-cz.php delete mode 100644 phpmailer/language/phpmailer.lang-de.php delete mode 100644 phpmailer/language/phpmailer.lang-dk.php delete mode 100644 phpmailer/language/phpmailer.lang-en.php delete mode 100644 phpmailer/language/phpmailer.lang-es.php delete mode 100644 phpmailer/language/phpmailer.lang-fi.php delete mode 100644 phpmailer/language/phpmailer.lang-fo.php delete mode 100644 phpmailer/language/phpmailer.lang-fr.php delete mode 100644 phpmailer/language/phpmailer.lang-hu.php delete mode 100644 phpmailer/language/phpmailer.lang-it.php delete mode 100644 phpmailer/language/phpmailer.lang-ja.php delete mode 100644 phpmailer/language/phpmailer.lang-nl.php delete mode 100644 phpmailer/language/phpmailer.lang-no.php delete mode 100644 phpmailer/language/phpmailer.lang-pl.php delete mode 100644 phpmailer/language/phpmailer.lang-ro.php delete mode 100644 phpmailer/language/phpmailer.lang-ru.php delete mode 100644 phpmailer/language/phpmailer.lang-se.php delete mode 100644 phpmailer/language/phpmailer.lang-tr.php delete mode 100644 phpsniff/index.php delete mode 100644 phpsniff/phpSniff.class.php delete mode 100644 phpsniff/phpSniff.core.php delete mode 100644 simplepie/idn/idna_convert.class.php delete mode 100644 simplepie/idn/npdata.ser delete mode 100644 simplepie/simplepie.inc delete mode 100644 spyc/README delete mode 100644 spyc/examples/yaml-dump.php delete mode 100644 spyc/examples/yaml-load.php delete mode 100644 spyc/php4/5to4.php delete mode 100644 spyc/php4/spyc.php4 delete mode 100644 spyc/php4/test.php4 delete mode 100644 spyc/spyc.php delete mode 100644 spyc/spyc.yaml delete mode 100644 spyc/tests/DumpTest.php delete mode 100644 spyc/tests/IndentTest.php delete mode 100644 spyc/tests/ParseTest.php delete mode 100644 spyc/tests/failing1.yaml delete mode 100644 spyc/tests/indent_1.yaml delete mode 100644 spyc/tests/quotes.yaml delete mode 100644 tar.class.php delete mode 100644 template_biticon_updater.sh delete mode 100644 tree.php delete mode 100644 zip_lib.php diff --git a/.htaccess b/.htaccess deleted file mode 100644 index 3a42882..0000000 --- a/.htaccess +++ /dev/null @@ -1 +0,0 @@ -Deny from all diff --git a/Date/Calc.php b/Date/Calc.php deleted file mode 100644 index 349f3a8..0000000 --- a/Date/Calc.php +++ /dev/null @@ -1,1531 +0,0 @@ - - */ -class Date_Calc -{ - /** - * Returns the current local date. NOTE: This function - * retrieves the local date using strftime(), which may - * or may not be 32-bit safe on your system. - * - * @param string the strftime() format to return the date - * - * @access public - * - * @return string the current date in specified format - */ - function dateNow($format="%Y%m%d") - { - return(strftime($format,time())); - } // end func dateNow - /** - * Returns true for valid date, false for invalid date. - * - * @param string year in format CCYY - * @param string month in format MM - * @param string day in format DD - * - * @access public - * - * @return boolean true/false - */ - function is_validDate($day, $month, $year) - { - if($year < 0 || $year > 9999) { - return false; - } - if(!checkdate($month,$day,$year)) { - return false; - } - return true; - } // end func is_validDate - /** - * Returns true for a leap year, else false - * - * @param string year in format CCYY - * - * @access public - * - * @return boolean true/false - */ - function isLeapYear($year="") - { - if(empty($year)) { - $year = Date_Calc::dateNow("%Y"); - } - if(strlen($year) != 4) { - return false; - } - if(preg_match("/\D/",$year)) { - return false; - } - return (($year % 4 == 0 && $year % 100 != 0) || $year % 400 == 0); - } // end func isLeapYear - /** - * Determines if given date is a future date from now. - * - * @param string year in format CCYY - * @param string month in format MM - * @param string day in format DD - * - * @access public - * - * @return boolean true/false - */ - function isFutureDate($day,$month,$year) - { - $this_year = Date_Calc::dateNow("%Y"); - $this_month = Date_Calc::dateNow("%m"); - $this_day = Date_Calc::dateNow("%d"); - if($year > $this_year) { - return true; - } elseif($year == $this_year) { - if($month > $this_month) { - return true; - } elseif($month == $this_month) { - if($day > $this_day) { - return true; - } - } - } - return false; - } // end func isFutureDate - /** - * Determines if given date is a past date from now. - * - * @param string year in format CCYY - * @param string month in format MM - * @param string day in format DD - * - * @access public - * - * @return boolean true/false - */ - function isPastDate($day,$month,$year) - { - $this_year = Date_Calc::dateNow("%Y"); - $this_month = Date_Calc::dateNow("%m"); - $this_day = Date_Calc::dateNow("%d"); - if($year < $this_year) { - return true; - } elseif($year == $this_year) { - if($month < $this_month) { - return true; - } elseif($month == $this_month) { - if($day < $this_day) { - return true; - } - } - } - return false; - } // end func isPastDate - /** - * Returns day of week for given date, 0=Sunday - * - * @param string year in format CCYY, default is current local year - * @param string month in format MM, default is current local month - * @param string day in format DD, default is current local day - * - * @access public - * - * @return int $weekday_number - */ - function dayOfWeek($day="",$month="",$year="") - { - if(empty($year)) { - $year = Date_Calc::dateNow("%Y"); - } - if(empty($month)) { - $month = Date_Calc::dateNow("%m"); - } - if(empty($day)) { - $day = Date_Calc::dateNow("%d"); - } - if($month > 2) { - $month -= 2; - } else { - $month += 10; - $year--; - } - $day = ( floor((13 * $month - 1) / 5) + - $day + ($year % 100) + - floor(($year % 100) / 4) + - floor(($year / 100) / 4) - 2 * - floor($year / 100) + 77); - $weekday_number = (($day - 7 * floor($day / 7))); - return $weekday_number; - } // end func dayOfWeek - /** - * Returns week of the year, first Sunday is first day of first week - * - * @param string day in format DD - * @param string month in format MM - * @param string year in format CCYY - * - * @access public - * - * @return integer $week_number - */ - function weekOfYear($day,$month,$year) - { - if(empty($year)) { - $year = Date_Calc::dateNow("%Y"); - } - if(empty($month)) { - $month = Date_Calc::dateNow("%m"); - } - if(empty($day)) { - $day = Date_Calc::dateNow("%d"); - } - $iso = Date_Calc::gregorianToISO($day, $month, $year); - $parts = explode('-',$iso); - $week_number = intval($parts[1]); - return $week_number; - } // end func weekOfYear - /** - * Returns number of days since 31 December of year before given date. - * - * @param string year in format CCYY, default is current local year - * @param string month in format MM, default is current local month - * @param string day in format DD, default is current local day - * - * @access public - * - * @return int $julian - */ - function julianDate($day="",$month="",$year="") - { - if(empty($year)) { - $year = Date_Calc::dateNow("%Y"); - } - if(empty($month)) { - $month = Date_Calc::dateNow("%m"); - } - if(empty($day)) { - $day = Date_Calc::dateNow("%d"); - } - $days = array(0,31,59,90,120,151,181,212,243,273,304,334); - $julian = ($days[$month - 1] + $day); - if($month > 2 && Date_Calc::isLeapYear($year)) { - $julian++; - } - return($julian); - } // end func julianDate - /** - * Returns quarter of the year for given date - * - * @param string year in format CCYY, default current local year - * @param string month in format MM, default current local month - * @param string day in format DD, default current local day - * - * @access public - * - * @return int $year_quarter - */ - function quarterOfYear($day="",$month="",$year="") - { - if(empty($year)) { - $year = Date_Calc::dateNow("%Y"); - } - if(empty($month)) { - $month = Date_Calc::dateNow("%m"); - } - if(empty($day)) { - $day = Date_Calc::dateNow("%d"); - } - $year_quarter = (intval(($month - 1) / 3 + 1)); - return $year_quarter; - } // end func quarterOfYear - /** - * Returns date of begin of next month of given date. - * - * @param string year in format CCYY, default current local year - * @param string month in format MM, default current local month - * @param string day in format DD, default current local day - * @param string format for returned date - * - * @access public - * - * @return string date in given format - */ - function beginOfNextMonth($day="",$month="",$year="",$format="%Y%m%d") - { - if(empty($year)) { - $year = Date_Calc::dateNow("%Y"); - } - if(empty($month)) { - $month = Date_Calc::dateNow("%m"); - } - if(empty($day)) { - $day = Date_Calc::dateNow("%d"); - } - if($month < 12) { - $month++; - $day=1; - } else { - $year++; - $month=1; - $day=1; - } - return Date_Calc::dateFormat($day,$month,$year,$format); - } // end func beginOfNextMonth - /** - * Returns date of the last day of next month of given date. - * - * @param string year in format CCYY, default current local year - * @param string month in format MM, default current local month - * @param string day in format DD, default current local day - * @param string format for returned date - * - * @access public - * - * @return string date in given format - */ - function endOfNextMonth($day="",$month="",$year="",$format="%Y%m%d") - { - if(empty($year)) { - $year = Date_Calc::dateNow("%Y"); - } - if(empty($month)) { - $month = Date_Calc::dateNow("%m"); - } - if(empty($day)) { - $day = Date_Calc::dateNow("%d"); - } - if($month < 12) { - $month++; - } else { - $year++; - $month=1; - } - $day = Date_Calc::daysInMonth($month,$year); - return Date_Calc::dateFormat($day,$month,$year,$format); - } // end func endOfNextMonth - /** - * Returns date of the first day of previous month of given date. - * - * @param string year in format CCYY, default current local year - * @param string month in format MM, default current local month - * @param string day in format DD, default current local day - * @param string format for returned date - * - * @access public - * - * @return string date in given format - */ - function beginOfPrevMonth($day="",$month="",$year="",$format="%Y%m%d") - { - if(empty($year)) { - $year = Date_Calc::dateNow("%Y"); - } - if(empty($month)) { - $month = Date_Calc::dateNow("%m"); - } - if(empty($day)) { - $day = Date_Calc::dateNow("%d"); - } - if($month > 1) { - $month--; - $day=1; - } else { - $year--; - $month=12; - $day=1; - } - return Date_Calc::dateFormat($day,$month,$year,$format); - } // end func beginOfPrevMonth - /** - * Returns date of the last day of previous month for given date. - * - * @param string year in format CCYY, default current local year - * @param string month in format MM, default current local month - * @param string day in format DD, default current local day - * @param string format for returned date - * - * @access public - * - * @return string date in given format - */ - function endOfPrevMonth($day="",$month="",$year="",$format="%Y%m%d") - { - if(empty($year)) { - $year = Date_Calc::dateNow("%Y"); - } - if(empty($month)) { - $month = Date_Calc::dateNow("%m"); - } - if(empty($day)) { - $day = Date_Calc::dateNow("%d"); - } - if($month > 1) { - $month--; - } else { - $year--; - $month=12; - } - $day = Date_Calc::daysInMonth($month,$year); - return Date_Calc::dateFormat($day,$month,$year,$format); - } // end func endOfPrevMonth - /** - * Returns date of the next weekday of given date, - * skipping from Friday to Monday. - * - * @param string year in format CCYY, default current local year - * @param string month in format MM, default current local month - * @param string day in format DD, default current local day - * @param string format for returned date - * - * @access public - * - * @return string date in given format - */ - function nextWeekday($day="",$month="",$year="",$format="%Y%m%d") - { - if(empty($year)) { - $year = Date_Calc::dateNow("%Y"); - } - if(empty($month)) { - $month = Date_Calc::dateNow("%m"); - } - if(empty($day)) { - $day = Date_Calc::dateNow("%d"); - } - $days = Date_Calc::dateToDays($day,$month,$year); - if(Date_Calc::dayOfWeek($day,$month,$year) == 5) { - $days += 3; - } elseif(Date_Calc::dayOfWeek($day,$month,$year) == 6) { - $days += 2; - } else { - $days += 1; - } - return(Date_Calc::daysToDate($days,$format)); - } // end func nextWeekday - /** - * Returns date of the previous weekday, - * skipping from Monday to Friday. - * - * @param string year in format CCYY, default current local year - * @param string month in format MM, default current local month - * @param string day in format DD, default current local day - * @param string format for returned date - * - * @access public - * - * @return string date in given format - */ - function prevWeekday($day="",$month="",$year="",$format="%Y%m%d") - { - if(empty($year)) { - $year = Date_Calc::dateNow("%Y"); - } - if(empty($month)) { - $month = Date_Calc::dateNow("%m"); - } - if(empty($day)) { - $day = Date_Calc::dateNow("%d"); - } - $days = Date_Calc::dateToDays($day,$month,$year); - if(Date_Calc::dayOfWeek($day,$month,$year) == 1) { - $days -= 3; - } elseif(Date_Calc::dayOfWeek($day,$month,$year) == 0) { - $days -= 2; - } else { - $days -= 1; - } - return(Date_Calc::daysToDate($days,$format)); - } // end func prevWeekday - /** - * Returns date of the next specific day of the week - * from the given date. - * - * @param int day of week, 0=Sunday - * @param string year in format CCYY, default current local year - * @param string month in format MM, default current local month - * @param string day in format DD, default current local day - * @param boolean onOrAfter if true and days are same, returns current day - * @param string format for returned date - * - * @access public - * - * @return string date in given format - */ - function nextDayOfWeek($dow,$day='',$month="",$year="",$format="%Y%m%d",$onOrAfter=false) - { - if(empty($year)) { - $year = Date_Calc::dateNow("%Y"); - } - if(empty($month)) { - $month = Date_Calc::dateNow("%m"); - } - if(empty($day)) { - $day = Date_Calc::dateNow("%d"); - } - $days = Date_Calc::dateToDays($day,$month,$year); - $curr_weekday = Date_Calc::dayOfWeek($day,$month,$year); - if($curr_weekday == $dow) { - if(!$onOrAfter) { - $days += 7; - } - } - elseif($curr_weekday > $dow) { - $days += 7 - ( $curr_weekday - $dow ); - } else { - $days += $dow - $curr_weekday; - } - return(Date_Calc::daysToDate($days,$format)); - } // end func nextDayOfWeek - /** - * Returns date of the previous specific day of the week - * from the given date. - * - * @param int day of week, 0=Sunday - * @param string year in format CCYY, default current local year - * @param string month in format MM, default current local month - * @param string day in format DD, default current local day - * @param boolean onOrBefore if true and days are same, returns current day - * @param string format for returned date - * - * @access public - * - * @return string date in given format - */ - function prevDayOfWeek($dow,$day="",$month="",$year="",$format="%Y%m%d",$onOrBefore=false) - { - if(empty($year)) { - $year = Date_Calc::dateNow("%Y"); - } - if(empty($month)) { - $month = Date_Calc::dateNow("%m"); - } - if(empty($day)) { - $day = Date_Calc::dateNow("%d"); - } - $days = Date_Calc::dateToDays($day,$month,$year); - $curr_weekday = Date_Calc::dayOfWeek($day,$month,$year); - if($curr_weekday == $dow) { - if(!$onOrBefore) { - $days -= 7; - } - } - elseif($curr_weekday < $dow) { - $days -= 7 - ( $dow - $curr_weekday ); - } else { - $days -= $curr_weekday - $dow; - } - return(Date_Calc::daysToDate($days,$format)); - } // end func prevDayOfWeek - /** - * Returns date of the next specific day of the week - * on or before the given date. - * - * @param int day of week, 0=Sunday - * @param string year in format CCYY, default current local year - * @param string month in format MM, default current local month - * @param string day in format DD, default current local day - * @param string format for returned date - * - * @access public - * - * @return string date in given format - */ - function nextDayOfWeekOnOrAfter($dow,$day="",$month="",$year="",$format="%Y%m%d") - { - return(Date_Calc::nextDayOfWeek($dow,$day="",$month="",$year="",$format="%Y%m%d",true)); - } // end func nextDayOfWeekOnOrAfter - /** - * Returns date of the previous specific day of the week - * on or before the given date. - * - * @param int day of week, 0=Sunday - * @param string year in format CCYY, default current local year - * @param string month in format MM, default current local month - * @param string day in format DD, default current local day - * @param string format for returned date - * - * @access public - * - * @return string date in given format - */ - function prevDayOfWeekOnOrBefore($dow,$day="",$month="",$year="",$format="%Y%m%d") - { - return(Date_Calc::prevDayOfWeek($dow,$day="",$month="",$year="",$format="%Y%m%d",true)); - } // end func prevDayOfWeekOnOrAfter - /** - * Returns date of day after given date. - * - * @param string year in format CCYY, default current local year - * @param string month in format MM, default current local month - * @param string day in format DD, default current local day - * @param string format for returned date - * - * @access public - * - * @return string date in given format - */ - function nextDay($day="",$month="",$year="",$format="%Y%m%d") - { - if(empty($year)) { - $year = Date_Calc::dateNow("%Y"); - } - if(empty($month)) { - $month = Date_Calc::dateNow("%m"); - } - if(empty($day)) { - $day = Date_Calc::dateNow("%d"); - } - $days = Date_Calc::dateToDays($day,$month,$year); - return(Date_Calc::daysToDate($days + 1,$format)); - } // end func nextDay - /** - * Returns date of day before given date. - * - * @param string year in format CCYY, default current local year - * @param string month in format MM, default current local month - * @param string day in format DD, default current local day - * @param string format for returned date - * - * @access public - * - * @return string date in given format - */ - function prevDay($day="",$month="",$year="",$format="%Y%m%d") - { - if(empty($year)) { - $year = Date_Calc::dateNow("%Y"); - } - if(empty($month)) { - $month = Date_Calc::dateNow("%m"); - } - if(empty($day)) { - $day = Date_Calc::dateNow("%d"); - } - $days = Date_Calc::dateToDays($day,$month,$year); - return(Date_Calc::daysToDate($days - 1,$format)); - } // end func prevDay - /** - * Sets century for 2 digit year. - * 51-99 is 19, else 20 - * - * @param string 2 digit year - * - * @access public - * - * @return string 4 digit year - */ - function defaultCentury($year) - { - if(strlen($year) == 1) { - $year = "0$year"; - } - if($year > 50) { - return( "19$year" ); - } else { - return( "20$year" ); - } - } // end func defaultCentury - /** - * Returns number of days between two given dates. - * - * @param string year in format CCYY - * @param string month in format MM - * @param string day in format DD - * @param string year in format CCYY - * @param string month in format MM - * @param string day in format DD - * - * @access public - * - * @return int absolute number of days between dates, - * -1 if there is an error. - */ - function dateDiff($day1,$month1,$year1,$day2,$month2,$year2) - { - if(!Date_Calc::is_validDate($day1,$month1,$year1)) { - return -1; - } - if(!Date_Calc::is_validDate($day2,$month2,$year2)) { - return -1; - } - return(abs((Date_Calc::dateToDays($day1,$month1,$year1)) - - (Date_Calc::dateToDays($day2,$month2,$year2)))); - } // end func dateDiff - /** - * Compares two dates - * - * @param string $day1 day in format DD - * @param string $month1 month in format MM - * @param string $year1 year in format CCYY - * @param string $day2 day in format DD - * @param string $month2 month in format MM - * @param string $year2 year in format CCYY - * - * @access public - * @return int 0 on equality, 1 if date 1 is greater, -1 if smaller - */ - function compareDates($day1,$month1,$year1,$day2,$month2,$year2) - { - $ndays1 = Date_Calc::dateToDays($day1, $month1, $year1); - $ndays2 = Date_Calc::dateToDays($day2, $month2, $year2); - if ($ndays1 == $ndays2) { - return 0; - } - return ($ndays1 > $ndays2) ? 1 : -1; - } // end func compareDates - /** - * Find the number of days in the given month. - * - * @param string month in format MM, default current local month - * - * @access public - * - * @return int number of days - */ - function daysInMonth($month="",$year="") - { - if(empty($year)) { - $year = Date_Calc::dateNow("%Y"); - } - if(empty($month)) { - $month = Date_Calc::dateNow("%m"); - } - if($month == 2) { - if(Date_Calc::isLeapYear($year)) { - return 29; - } else { - return 28; - } - } elseif($month == 4 or $month == 6 or $month == 9 or $month == 11) { - return 30; - } else { - return 31; - } - } // end func daysInMonth - /** - * Returns the number of rows on a calendar month. Useful for - * determining the number of rows when displaying a typical - * month calendar. - * - * @param string month in format MM, default current local month - * @param string year in format YYCC, default current local year - * - * @access public - * - * @return int number of weeks - */ - function weeksInMonth($month="",$year="") - { - if(empty($year)) { - $year = Date_Calc::dateNow("%Y"); - } - if(empty($month)) { - $month = Date_Calc::dateNow("%m"); - } - if(DATE_CALC_BEGIN_WEEKDAY == 1) { - if(Date_Calc::firstOfMonthWeekday($month,$year) == 0) { - $first_week_days = 1; - } else { - $first_week_days = - 7 - (Date_Calc::firstOfMonthWeekday($month,$year) - 1); - } - } else { - $first_week_days = 7 - Date_Calc::firstOfMonthWeekday($month,$year); - } - return ceil(((Date_Calc::daysInMonth($month,$year) - $first_week_days) / 7) + 1); - } // end func weeksInMonth - /** - * Find the day of the week for the first of the month of given date. - * - * @param string year in format CCYY, default to current local year - * @param string month in format MM, default to current local month - * - * @access public - * - * @return int number of weekday for the first day, 0=Sunday - */ - function firstOfMonthWeekday($month="",$year="") - { - if(empty($year)) { - $year = Date_Calc::dateNow("%Y"); - } - if(empty($month)) { - $month = Date_Calc::dateNow("%m"); - } - return(Date_Calc::dayOfWeek("01",$month,$year)); - } // end func firstOfMonthWeekday - /** - * Return date of first day of month of given date. - * - * @param string year in format CCYY, default current local year - * @param string month in format MM, default current local month - * @param string format for returned date - * - * @access public - * - * @return string date in given format - */ - function beginOfMonth($month="",$year="",$format="%Y%m%d") - { - if(empty($year)) { - $year = Date_Calc::dateNow("%Y"); - } - if(empty($month)) { - $month = Date_Calc::dateNow("%m"); - } - return(Date_Calc::dateFormat("01",$month,$year,$format)); - } // end of func beginOfMonth - /** - * Find the month day of the beginning of week for given date, - * using DATE_CALC_BEGIN_WEEKDAY. (can return weekday of prev month.) - * - * @param string year in format CCYY, default current local year - * @param string month in format MM, default current local month - * @param string day in format DD, default current local day - * @param string format for returned date - * - * @access public - * - * @return string date in given format - */ - function beginOfWeek($day="",$month="",$year="",$format="%Y%m%d") - { - if(empty($year)) { - $year = Date_Calc::dateNow("%Y"); - } - if(empty($month)) { - $month = Date_Calc::dateNow("%m"); - } - if(empty($day)) { - $day = Date_Calc::dateNow("%d"); - } - $this_weekday = Date_Calc::dayOfWeek($day,$month,$year); - if(DATE_CALC_BEGIN_WEEKDAY == 1) { - if($this_weekday == 0) { - $beginOfWeek = Date_Calc::dateToDays($day,$month,$year) - 6; - } else { - $beginOfWeek = Date_Calc::dateToDays($day,$month,$year) - - $this_weekday + 1; - } - } else { - $beginOfWeek = (Date_Calc::dateToDays($day,$month,$year) - - $this_weekday); - } - /* $beginOfWeek = (Date_Calc::dateToDays($day,$month,$year) - - ($this_weekday - DATE_CALC_BEGIN_WEEKDAY)); */ - return(Date_Calc::daysToDate($beginOfWeek,$format)); - } // end of func beginOfWeek - /** - * Find the month day of the end of week for given date, - * using DATE_CALC_BEGIN_WEEKDAY. (can return weekday - * of following month.) - * - * @param string year in format CCYY, default current local year - * @param string month in format MM, default current local month - * @param string day in format DD, default current local day - * @param string format for returned date - * - * @access public - * - * @return string date in given format - */ - function endOfWeek($day="",$month="",$year="",$format="%Y%m%d") - { - if(empty($year)) { - $year = Date_Calc::dateNow("%Y"); - } - if(empty($month)) { - $month = Date_Calc::dateNow("%m"); - } - if(empty($day)) { - $day = Date_Calc::dateNow("%d"); - } - $this_weekday = Date_Calc::dayOfWeek($day,$month,$year); - $last_dayOfWeek = (Date_Calc::dateToDays($day,$month,$year) - + (6 - $this_weekday + DATE_CALC_BEGIN_WEEKDAY)); - return(Date_Calc::daysToDate($last_dayOfWeek,$format)); - } // end func endOfWeek - /** - * Find the month day of the beginning of week after given date, - * using DATE_CALC_BEGIN_WEEKDAY. (can return weekday of prev month.) - * - * @param string year in format CCYY, default current local year - * @param string month in format MM, default current local month - * @param string day in format DD, default current local day - * @param string format for returned date - * - * @access public - * - * @return string date in given format - */ - function beginOfNextWeek($day="",$month="",$year="",$format="%Y%m%d") - { - if(empty($year)) { - $year = Date_Calc::dateNow("%Y"); - } - if(empty($month)) { - $month = Date_Calc::dateNow("%m"); - } - if(empty($day)) { - $day = Date_Calc::dateNow("%d"); - } - $date = Date_Calc::daysToDate(Date_Calc::dateToDays($day+7,$month,$year),"%Y%m%d"); - $next_week_year = substr($date,0,4); - $next_week_month = substr($date,4,2); - $next_week_day = substr($date,6,2); - $this_weekday = - Date_Calc::dayOfWeek($next_week_day,$next_week_month,$next_week_year); - $beginOfWeek = (Date_Calc::dateToDays($next_week_day,$next_week_month,$next_week_year) - - ($this_weekday - DATE_CALC_BEGIN_WEEKDAY)); - return(Date_Calc::daysToDate($beginOfWeek,$format)); - } // end func beginOfNextWeek - /** - * Find the month day of the beginning of week before given date, - * using DATE_CALC_BEGIN_WEEKDAY. (can return weekday of prev month.) - * - * @param string year in format CCYY, default current local year - * @param string month in format MM, default current local month - * @param string day in format DD, default current local day - * @param string format for returned date - * - * @access public - * - * @return string date in given format - */ - function beginOfPrevWeek($day="",$month="",$year="",$format="%Y%m%d") - { - if(empty($year)) { - $year = Date_Calc::dateNow("%Y"); - } - if(empty($month)) { - $month = Date_Calc::dateNow("%m"); - } - if(empty($day)) { - $day = Date_Calc::dateNow("%d"); - } - $date = Date_Calc::daysToDate(Date_Calc::dateToDays($day-7,$month,$year),"%Y%m%d"); - $next_week_year = substr($date,0,4); - $next_week_month = substr($date,4,2); - $next_week_day = substr($date,6,2); - $this_weekday = - Date_Calc::dayOfWeek($next_week_day,$next_week_month,$next_week_year); - $beginOfWeek = (Date_Calc::dateToDays($next_week_day,$next_week_month,$next_week_year) - - ($this_weekday - DATE_CALC_BEGIN_WEEKDAY)); - return(Date_Calc::daysToDate($beginOfWeek,$format)); - } // end func beginOfPrevWeek - /** - * Return an array with days in week - * - * @param string year in format CCYY, default current local year - * @param string month in format MM, default current local month - * @param string day in format DD, default current local day - * @param string format for returned date - * - * @access public - * - * @return array $week[$weekday] - */ - function getCalendarWeek($day="",$month="",$year="",$format="%Y%m%d") - { - if(empty($year)) { - $year = Date_Calc::dateNow("%Y"); - } - if(empty($month)) { - $month = Date_Calc::dateNow("%m"); - } - if(empty($day)) { - $day = Date_Calc::dateNow("%d"); - } - $week_array = array(); - // date for the column of week - $curr_day = Date_Calc::beginOfWeek($day,$month,$year,"%E"); - for($counter=0; $counter <= 6; $counter++) { - $week_array[$counter] = Date_Calc::daysToDate($curr_day,$format); - $curr_day++; - } - return $week_array; - } // end func getCalendarWeek - /** - * Return a set of arrays to construct a calendar month for - * the given date. - * - * @param string year in format CCYY, default current local year - * @param string month in format MM, default current local month - * @param string format for returned date - * - * @access public - * - * @return array $month[$row][$col] - */ - function getCalendarMonth($month="",$year="",$format="%Y%m%d") - { - if(empty($year)) { - $year = Date_Calc::dateNow("%Y"); - } - if(empty($month)) { - $month = Date_Calc::dateNow("%m"); - } - $month_array = array(); - // date for the first row, first column of calendar month - if(DATE_CALC_BEGIN_WEEKDAY == 1) { - if(Date_Calc::firstOfMonthWeekday($month,$year) == 0) { - $curr_day = Date_Calc::dateToDays("01",$month,$year) - 6; - } else { - $curr_day = Date_Calc::dateToDays("01",$month,$year) - - Date_Calc::firstOfMonthWeekday($month,$year) + 1; - } - } else { - $curr_day = (Date_Calc::dateToDays("01",$month,$year) - - Date_Calc::firstOfMonthWeekday($month,$year)); - } - // number of days in this month - $daysInMonth = Date_Calc::daysInMonth($month,$year); - $weeksInMonth = Date_Calc::weeksInMonth($month,$year); - for($row_counter=0; $row_counter < $weeksInMonth; $row_counter++) { - for($column_counter=0; $column_counter <= 6; $column_counter++) { - $month_array[$row_counter][$column_counter] = - Date_Calc::daysToDate($curr_day,$format); - $curr_day++; - } - } - return $month_array; - } // end func getCalendarMonth - /** - * Return a set of arrays to construct a calendar year for - * the given date. - * - * @param string year in format CCYY, default current local year - * @param string format for returned date - * - * @access public - * - * @return array $year[$month][$row][$col] - */ - function getCalendarYear($year="",$format="%Y%m%d") - { - if(empty($year)) { - $year = Date_Calc::dateNow("%Y"); - } - $year_array = array(); - for($curr_month=0; $curr_month <=11; $curr_month++) { - $year_array[$curr_month] = - Date_Calc::getCalendarMonth(sprintf("%02d",$curr_month+1),$year,$format); - } - return $year_array; - } // end func getCalendarYear - /** - * Converts a date to number of days since a - * distant unspecified epoch. - * - * @param string year in format CCYY - * @param string month in format MM - * @param string day in format DD - * - * @access public - * - * @return integer number of days - */ - function dateToDays($day,$month,$year) - { - $century = (int) substr($year,0,2); - $year = (int) substr($year,2,2); - if($month > 2) { - $month -= 3; - } else { - $month += 9; - if($year) { - $year--; - } else { - $year = 99; - $century --; - } - } - return (floor(( 146097 * $century) / 4 ) + - floor(( 1461 * $year) / 4 ) + - floor(( 153 * $month + 2) / 5 ) + - $day + 1721119); - } // end func dateToDays - /** - * Converts number of days to a distant unspecified epoch. - * - * @param int number of days - * @param string format for returned date - * - * @access public - * - * @return string date in specified format - */ - function daysToDate($days,$format="%Y%m%d") - { - $days -= 1721119; - $century = floor(( 4 * $days - 1) / 146097); - $days = floor(4 * $days - 1 - 146097 * $century); - $day = floor($days / 4); - $year = floor(( 4 * $day + 3) / 1461); - $day = floor(4 * $day + 3 - 1461 * $year); - $day = floor(($day + 4) / 4); - $month = floor(( 5 * $day - 3) / 153); - $day = floor(5 * $day - 3 - 153 * $month); - $day = floor(($day + 5) / 5); - if($month < 10) { - $month +=3; - } else { - $month -=9; - if($year++ == 99) { - $year = 0; - $century++; - } - } - $century = sprintf("%02d",$century); - $year = sprintf("%02d",$year); - return(Date_Calc::dateFormat($day,$month,$century.$year,$format)); - } // end func daysToDate - /** - * Calculates the date of the Nth weekday of the month, - * such as the second Saturday of January 2000. - * - * @param string occurance: 1=first, 2=second, 3=third, etc. - * @param string dayOfWeek: 0=Sunday, 1=Monday, etc. - * @param string year in format CCYY - * @param string month in format MM - * @param string format for returned date - * - * @access public - * - * @return string date in given format - */ - function NWeekdayOfMonth($occurance,$dayOfWeek,$month,$year,$format="%Y%m%d") - { - $year = sprintf("%04d",$year); - $month = sprintf("%02d",$month); - $DOW1day = sprintf("%02d",(($occurance - 1) * 7 + 1)); - $DOW1 = Date_Calc::dayOfWeek($DOW1day,$month,$year); - $wdate = ($occurance - 1) * 7 + 1 + - (7 + $dayOfWeek - $DOW1) % 7; - if( $wdate > Date_Calc::daysInMonth($month,$year)) { - return -1; - } else { - return(Date_Calc::dateFormat($wdate,$month,$year,$format)); - } - } // end func NWeekdayOfMonth - /** - * Formats the date in the given format, much like - * strfmt(). This function is used to alleviate the - * problem with 32-bit numbers for dates pre 1970 - * or post 2038, as strfmt() has on most systems. - * Most of the formatting options are compatible. - * - * formatting options: - * - * %a abbreviated weekday name (Sun, Mon, Tue) - * %A full weekday name (Sunday, Monday, Tuesday) - * %b abbreviated month name (Jan, Feb, Mar) - * %B full month name (January, February, March) - * %d day of month (range 00 to 31) - * %e day of month, single digit (range 0 to 31) - * %E number of days since unspecified epoch (integer) - * (%E is useful for passing a date in a URL as - * an integer value. Then simply use - * daysToDate() to convert back to a date.) - * %j day of year (range 001 to 366) - * %m month as decimal number (range 1 to 12) - * %n newline character (\n) - * %t tab character (\t) - * %w weekday as decimal (0 = Sunday) - * %U week number of current year, first sunday as first week - * %y year as decimal (range 00 to 99) - * %Y year as decimal including century (range 0000 to 9999) - * %% literal '%' - * - * @param string year in format CCYY - * @param string month in format MM - * @param string day in format DD - * @param string format for returned date - * - * @access public - * - * @return string date in given format - */ - function dateFormat($day,$month,$year,$format) - { - if(!Date_Calc::is_validDate($day,$month,$year)) { - $year = Date_Calc::dateNow("%Y"); - $month = Date_Calc::dateNow("%m"); - $day = Date_Calc::dateNow("%d"); - } - $output = ""; - for($strpos = 0; $strpos < strlen($format); $strpos++) { - $char = substr($format,$strpos,1); - if($char == "%") { - $nextchar = substr($format,$strpos + 1,1); - switch($nextchar) { - case "a": - $output .= Date_Calc::getWeekdayAbbrname($day,$month,$year); - break; - case "A": - $output .= Date_Calc::getWeekdayFullname($day,$month,$year); - break; - case "b": - $output .= Date_Calc::getMonthAbbrname($month); - break; - case "B": - $output .= Date_Calc::getMonthFullname($month); - break; - case "d": - $output .= sprintf("%02d",$day); - break; - case "e": - $output .= $day; - break; - case "E": - $output .= Date_Calc::dateToDays($day,$month,$year); - break; - case "j": - $output .= Date_Calc::julianDate($day,$month,$year); - break; - case "m": - $output .= sprintf("%02d",$month); - break; - case "n": - $output .= "\n"; - break; - case "t": - $output .= "\t"; - break; - case "w": - $output .= Date_Calc::dayOfWeek($day,$month,$year); - break; - case "U": - $output .= Date_Calc::weekOfYear($day,$month,$year); - break; - case "y": - $output .= substr($year,2,2); - break; - case "Y": - $output .= $year; - break; - case "%": - $output .= "%"; - break; - default: - $output .= $char.$nextchar; - } - $strpos++; - } else { - $output .= $char; - } - } - return $output; - } // end func dateFormat - /** - * Returns the current local year in format CCYY - * - * @access public - * - * @return string year in format CCYY - */ - function getYear() - { - return Date_Calc::dateNow("%Y"); - } // end func getYear - /** - * Returns the current local month in format MM - * - * @access public - * - * @return string month in format MM - */ - function getMonth() - { - return Date_Calc::dateNow("%m"); - } // end func getMonth - /** - * Returns the current local day in format DD - * - * @access public - * - * @return string day in format DD - */ - function getDay() - { - return Date_Calc::dateNow("%d"); - } // end func getDay - /** - * Returns the full month name for the given month - * - * @param string month in format MM - * - * @access public - * - * @return string full month name - */ - function getMonthFullname($month) - { - $month = (int)$month; - if(empty($month)) { - $month = (int) Date_Calc::dateNow("%m"); - } - $month_names = Date_Calc::getMonthNames(); - return $month_names[$month]; - // getMonthNames returns months with correct indexes - //return $month_names[($month - 1)]; - } // end func getMonthFullname - /** - * Returns the abbreviated month name for the given month - * - * @param string month in format MM - * @param int optional length of abbreviation, default is 3 - * - * @access public - * - * @return string abbreviated month name - * @see Date_Calc::getMonthFullname - */ - function getMonthAbbrname($month,$length=3) - { - $month = (int)$month; - if(empty($month)) { - $month = Date_Calc::dateNow("%m"); - } - return mb_substr(Date_Calc::getMonthFullname($month), 0, $length); - } // end func getMonthAbbrname - /** - * Returns the full weekday name for the given date - * - * @param string year in format CCYY, default current local year - * @param string month in format MM, default current local month - * @param string day in format DD, default current local day - * - * @access public - * - * @return string full month name - */ - function getWeekdayFullname($day="",$month="",$year="") - { - if(empty($year)) { - $year = Date_Calc::dateNow("%Y"); - } - if(empty($month)) { - $month = Date_Calc::dateNow("%m"); - } - if(empty($day)) { - $day = Date_Calc::dateNow("%d"); - } - $weekday_names = Date_Calc::getWeekDays(); - $weekday = Date_Calc::dayOfWeek($day,$month,$year); - return $weekday_names[$weekday]; - } // end func getWeekdayFullname - /** - * Returns the abbreviated weekday name for the given date - * - * @param string year in format CCYY, default current local year - * @param string month in format MM, default current local month - * @param string day in format DD, default current local day - * @param int optional length of abbreviation, default is 3 - * - * @access public - * - * @return string full month name - * @see Date_Calc::getWeekdayFullname - */ - function getWeekdayAbbrname($day="",$month="",$year="",$length=3) - { - if(empty($year)) { - $year = Date_Calc::dateNow("%Y"); - } - if(empty($month)) { - $month = Date_Calc::dateNow("%m"); - } - if(empty($day)) { - $day = Date_Calc::dateNow("%d"); - } - return mb_substr(Date_Calc::getWeekdayFullname($day,$month,$year),0,$length); - } // end func getWeekdayFullname - /** - * Returns the numeric month from the month name or an abreviation - * - * Both August and Aug would return 8. - * Month name is case insensitive. - * - * @param string month name - * @return integer month number - */ - function getMonthFromFullName($month) - { - $month = strtolower($month); - $months = Date_Calc::getMonthNames(); - while(list($id, $name) = each($months)) { - if(ereg($month, strtolower($name))) { - return($id); - } - } - return(0); - } // end func getMonthFromFullName - /** - * Returns an array of month names - * - * Used to take advantage of the setlocale function to return - * language specific month names. - * XXX cache values to some global array to avoid preformace - * hits when called more than once. - * - * @returns array An array of month names - */ - function getMonthNames() - { - for($i=1;$i<13;$i++) { - $months[$i] = strftime('%B', mktime(0, 0, 0, $i, 1, 2001)); - } - return($months); - } // end func getMonthNames - /** - * Returns an array of week days - * - * Used to take advantage of the setlocale function to - * return language specific week days - * XXX cache values to some global array to avoid preformace - * hits when called more than once. - * - * @returns array An array of week day names - */ - function getWeekDays() - { - for($i=0;$i<7;$i++) { - $weekdays[$i] = strftime('%A', mktime(0, 0, 0, 1, $i, 2001)); - } - return($weekdays); - } // end func getWeekDays - /** - * Converts from Gregorian Year-Month-Day to ISO YearNumber-WeekNumber-WeekDay - * - * Uses ISO 8601 definitions. - * Algorithm from Rick McCarty, 1999 at http://personal.ecu.edu/mccartyr/ISOwdALG.txt - * - * @param int $year - * @param int $month - * @param int $day - * @return string - * @access public - */ - // Transcribed to PHP by Jesus M. Castagnetto (blame him if it is fubared ;-) - function gregorianToISO($day, $month, $year) { - $mnth = array (0,31,59,90,120,151,181,212,243,273,304,334); - $y_isleap = Date_Calc::isLeapYear($year); - $y_1_isleap = Date_Calc::isLeapYear($year - 1); - $day_of_year_number = $day + $mnth[$month - 1]; - if ($y_isleap && $month > 2) { - $day_of_year_number++; - } - // find Jan 1 weekday (monday = 1, sunday = 7) - $yy = ($year - 1) % 100; - $c = ($year - 1) - $yy; - $g = $yy + intval($yy/4); - $jan1_weekday = 1 + intval((((($c / 100) % 4) * 5) + $g) % 7); - // weekday for year-month-day - $h = $day_of_year_number + ($jan1_weekday - 1); - $weekday = 1 + intval(($h - 1) % 7); - // find if Y M D falls in YearNumber Y-1, WeekNumber 52 or - if ($day_of_year_number <= (8 - $jan1_weekday) && $jan1_weekday > 4){ - $yearnumber = $year - 1; - if ($jan1_weekday == 5 || ($jan1_weekday == 6 && $y_1_isleap)) { - $weeknumber = 53; - } else { - $weeknumber = 52; - } - } else { - $yearnumber = $year; - } - // find if Y M D falls in YearNumber Y+1, WeekNumber 1 - if ($yearnumber == $year) { - if ($y_isleap) { - $i = 366; - } else { - $i = 365; - } - if (($i - $day_of_year_number) < (4 - $weekday)) { - $yearnumber++; - $weeknumber = 1; - } - } - // find if Y M D falls in YearNumber Y, WeekNumber 1 through 53 - if ($yearnumber == $year) { - $j = $day_of_year_number + (7 - $weekday) + ($jan1_weekday - 1); - //$weeknumber = intval($j / 7) + 1; // kludge!!! - JMC - $weeknumber = intval($j / 7); // kludge!!! - JMC - if ($jan1_weekday > 4) { - $weeknumber--; - } - } - // put it all together - if ($weeknumber < 10) - $weeknumber = '0'.$weeknumber; - return "{$yearnumber}-{$weeknumber}-{$weekday}"; - } - /** - * Determines julian date of the given season - * Adapted from previous work in Java by James Mark Hamilton, mhamilton@qwest.net - * - * @author Robert Butler - * - * @param string is VERNALEQUINOX, SUMMERSOLSTICE, AUTUMNALEQUINOX, or WINTERSOLSTICE. - * @param string year in format CCYY, must be a calendar year between -1000BC and 3000AD. - * - * @access public - * - * @return float $juliandate - */ - function dateSeason ($season, $year = '') { - if ($year == '') { - $year = Date_Calc::dateNow('%Y'); - } - if (($year >= -1000) && ($year <= 1000)) { - $y = $year / 1000.0; - if ($season == "VERNALEQUINOX") { - $juliandate = (((((((-0.00071 * $y) - 0.00111) * $y) + 0.06134) * $y) + 365242.1374) * $y) + 1721139.29189; - } else if ($season == "SUMMERSOLSTICE") { - $juliandate = ((((((( 0.00025 * $y) + 0.00907) * $y) - 0.05323) * $y) + 365241.72562) * $y) + 1721233.25401; - } else if ($season == "AUTUMNALEQUINOX") { - $juliandate = ((((((( 0.00074 * $y) - 0.00297) * $y) - 0.11677) * $y) + 365242.49558) * $y) + 1721325.70455; - } else if ($season == "WINTERSOLSTICE") { - $juliandate = (((((((-0.00006 * $y) - 0.00933) * $y) - 0.00769) * $y) + 365242.88257) * $y) + 1721414.39987; - } - } elseif (($year > 1000) && ($year <= 3000)) { - $y = ($year - 2000) / 1000; - if ($season == "VERNALEQUINOX") { - $juliandate = (((((((-0.00057 * $y) - 0.00411) * $y) + 0.05169) * $y) + 365242.37404) * $y) + 2451623.80984; - } else if ($season == "SUMMERSOLSTICE") { - $juliandate = (((((((-0.0003 * $y) + 0.00888) * $y) + 0.00325) * $y) + 365241.62603) * $y) + 2451716.56767; - } else if ($season == "AUTUMNALEQUINOX") { - $juliandate = ((((((( 0.00078 * $y) + 0.00337) * $y) - 0.11575) * $y) + 365242.01767) * $y) + 2451810.21715; - } else if ($season == "WINTERSOLSTICE") { - $juliandate = ((((((( 0.00032 * $y) - 0.00823) * $y) - 0.06223) * $y) + 365242.74049) * $y) + 2451900.05952; - } - } - return ($juliandate); - } // end func dateSeason -} // end class Date_Calc -?> \ No newline at end of file diff --git a/Date/Human.php b/Date/Human.php deleted file mode 100644 index a09caa9..0000000 --- a/Date/Human.php +++ /dev/null @@ -1,184 +0,0 @@ - | -// +----------------------------------------------------------------------+ -// -// $Id$ -// -/** - * Class to convert date strings between Gregorian and Human calendar formats. - * The Human Calendar format has been proposed by Scott Flansburg and can be - * explained as follows: - * The year is made up of 13 months - * Each month has 28 days - * Counting of months starts from 0 (zero) so the months will run from 0 to 12 - * New Years day (00) is a monthless day - * Note: Leap Years are not yet accounted for in the Human Calendar system - * - * @since PHP 4.0.4 - * @author Allan Kent - */ -class Date_Human -{ - /** - * Returns an associative array containing the converted date information - * in 'Human Calendar' format. - * - * @param int day in DD format, default current local day - * @param int month in MM format, default current local month - * @param int year in CCYY format, default to current local year - * - * @access public - * - * @return associative array( - * hdom, // Human Day Of Month, starting at 1 - * hdow, // Human Day Of Week, starting at 1 - * hwom, // Human Week of Month, starting at 1 - * hwoy, // Human Week of Year, starting at 1 - * hmoy, // Human Month of Year, starting at 0 - * ) - * - * If the day is New Years Day, the function will return - * "hdom" => 0 - * "hdow" => 0 - * "hwom" => 0 - * "hwoy" => 0 - * "hmoy" => -1 - * Since 0 is a valid month number under the Human Calendar, I have left - * the month as -1 for New Years Day. - */ - function gregorianToHuman($day=0, $month=0, $year=0) - { - /** - * Check to see if any of the arguments are empty - * If they are then populate the $dateinfo array - * Then check to see which arguments are empty and fill - * those with the current date info - */ - if ((empty($day) || (empty($month)) || empty($year))) { - $dateinfo = getdate(time()); - } - if (empty($day)) { - $day = $dateinfo["mday"]; - } - if (empty($month)) { - $month = $dateinfo["mon"]; - } - if (empty($year)) { - $year = $dateinfo["year"]; - } - /** - * We need to know how many days into the year we are - */ - $dateinfo = getdate(mktime(0, 0, 0, $month, $day, $year)); - $dayofyear = $dateinfo["yday"]; - /** - * Human Calendar starts at 0 for months and the first day of the year - * is designated 00, so we need to start our day of the year at 0 for - * these calculations. - * Also, the day of the month is calculated with a modulus of 28. - * Because a day is 28 days, the last day of the month would have a - * remainder of 0 and not 28 as it should be. Decrementing $dayofyear - * gets around this. - */ - $dayofyear--; - /** - * 28 days in a month... - */ - $humanMonthOfYear = floor($dayofyear / 28); - /** - * If we are in the first month then the day of the month is $dayofyear - * else we need to find the modulus of 28. - */ - if ($humanMonthOfYear == 0) { - $humanDayOfMonth = $dayofyear; - } else { - $humanDayOfMonth = ($dayofyear) % 28; - } - /** - * Day of the week is modulus 7 - */ - $humanDayOfWeek = $dayofyear % 7; - /** - * We can now increment $dayofyear back to it's correct value for - * the remainder of the calculations - */ - $dayofyear++; - /** - * $humanDayOfMonth needs to be incremented now - recall that we fudged - * it a bit by decrementing $dayofyear earlier - * Same goes for $humanDayOfWeek - */ - $humanDayOfMonth++; - $humanDayOfWeek++; - /** - * Week of the month is day of the month divided by 7, rounded up - * Same for week of the year, but use $dayofyear instead $humanDayOfMonth - */ - $humanWeekOfMonth = ceil($humanDayOfMonth / 7); - $humanWeekOfYear = ceil($dayofyear / 7); - /** - * Return an associative array of the values - */ - return array( - "hdom" => $humanDayOfMonth, - "hdow" => $humanDayOfWeek, - "hwom" => $humanWeekOfMonth, - "hwoy" => $humanWeekOfYear, - "hmoy" => $humanMonthOfYear ); - } - /** - * Returns unix timestamp for a given Human Calendar date - * - * @param int day in DD format - * @param int month in MM format - * @param int year in CCYY format, default to current local year - * - * @access public - * - * @return int unix timestamp of date - */ - function HumanToGregorian($day, $month, $year=0) - { - /** - * Check to see if the year has been passed through. - * If not get current year - */ - if (empty($year)) { - $dateinfo = getdate(time()); - $year = $dateinfo["year"]; - } - /** - * We need to get the day of the year that we are currently at so that - * we can work out the Gregorian Month and day - */ - $DayOfYear = $month * 28; - $DayOfYear += $day; - /** - * Human Calendar starts at 0, so we need to increment $DayOfYear - * to take into account the day 00 - */ - $DayOfYear++; - /** - * the mktime() function will correctly calculate the date for out of - * range values, so putting $DayOfYear instead of the day of the month - * will work fine. - */ - $GregorianTimeStamp = mktime(0, 0, 0, 1, $DayOfYear, $year); - return $GregorianTimeStamp; - } -} -?> \ No newline at end of file diff --git a/Date/TimeZone.php b/Date/TimeZone.php deleted file mode 100644 index 57e203b..0000000 --- a/Date/TimeZone.php +++ /dev/null @@ -1,3632 +0,0 @@ - | -// | | -// +----------------------------------------------------------------------+ -// -// $Id$ -// -// Date_TimeZone Class -// -/** - * TimeZone representation class, along with time zone information data. - * - * TimeZone representation class, along with time zone information data. - * The default timezone is set from the first valid timezone id found - * in one of the following places, in this order:
- * 1) global $_DATE_TIMEZONE_DEFAULT
- * 2) system environment variable PHP_TZ
- * 3) system environment variable TZ
- * 4) the result of date('T')
- * If no valid timezone id is found, the default timezone is set to 'UTC'. - * You may also manually set the default timezone by passing a valid id to - * Date_TimeZone::setDefault().
- * - * This class includes time zone data (from zoneinfo) in the form of a global array, $_DATE_TIMEZONE_DATA. - * - * - * @author Baba Buehler - * @package Date - * @access public - * @version 1.0 - */ -class Date_TimeZone -{ - /** - * Time Zone ID of this time zone - * @var string - */ - var $id; - /** - * Long Name of this time zone (ie Central Standard Time) - * @var string - */ - var $longname; - /** - * Short Name of this time zone (ie CST) - * @var string - */ - var $shortname; - /** - * true if this time zone observes daylight savings time - * @var boolean - */ - var $hasdst; - /** - * DST Long Name of this time zone - * @var string - */ - var $dstlongname; - /** - * DST Short Name of this timezone - * @var string - */ - var $dstshortname; - /** - * offset, in milliseconds, of this timezone - * @var int - */ - var $offset; - /** - * System Default Time Zone - * @var object Date_TimeZone - */ - var $default; - /** - * Constructor - * - * Creates a new Date::TimeZone object, representing the time zone - * specified in $id. If the supplied ID is invalid, the created - * time zone is UTC. - * - * @access public - * @param string $id the time zone id - * @return object Date_TimeZone the new Date_TimeZone object - */ - function Date_TimeZone($id) - { - global $_DATE_TIMEZONE_DATA; - if(Date_TimeZone::is_validID($id)) { - $this->id = $id; - $this->longname = $_DATE_TIMEZONE_DATA[$id]['longname']; - $this->shortname = $_DATE_TIMEZONE_DATA[$id]['shortname']; - $this->offset = $_DATE_TIMEZONE_DATA[$id]['offset']; - if($_DATE_TIMEZONE_DATA[$id]['hasdst']) { - $this->hasdst = true; - $this->dstlongname = $_DATE_TIMEZONE_DATA[$id]['dstlongname']; - $this->dstshortname = $_DATE_TIMEZONE_DATA[$id]['dstshortname']; - } else { - $this->hasdst = false; - $this->dstlongname = $this->longname; - $this->dstshortname = $this->shortname; - } - } else { - $this->id = 'UTC'; - $this->longname = $_DATE_TIMEZONE_DATA[$this->id]['longname']; - $this->shortname = $_DATE_TIMEZONE_DATA[$this->id]['shortname']; - $this->hasdst = $_DATE_TIMEZONE_DATA[$this->id]['hasdst']; - $this->offset = $_DATE_TIMEZONE_DATA[$this->id]['offset']; - } - } - /** - * Return a TimeZone object representing the system default time zone - * - * Return a TimeZone object representing the system default time zone, - * which is initialized during the loading of TimeZone.php. - * - * @access public - * @return object Date_TimeZone the default time zone - */ - function getDefault() - { - global $default; - return new Date_TimeZone($default); - } - /** - * Sets the system default time zone to the time zone in $id - * - * Sets the system default time zone to the time zone in $id - * - * @access public - * @param string $id the time zone id to use - */ - function setDefault($id) - { - global $default; - if(Date_TimeZone::is_validID($id)) { - $default = $id; - } - } - /** - * Tests if given id is represented in the $_DATE_TIMEZONE_DATA time zone data - * - * Tests if given id is represented in the $_DATE_TIMEZONE_DATA time zone data - * - * @access public - * @param string $id the id to test - * @return boolean true if the supplied ID is valid - */ - function is_validID($id) - { - global $_DATE_TIMEZONE_DATA; - if(isset($_DATE_TIMEZONE_DATA[$id])) { - return true; - } else { - return false; - } - } - /** - * Is this time zone equal to another - * - * Tests to see if this time zone is equal (ids match) - * to a given Date_TimeZone object. - * - * @access public - * @param object Date_TimeZone $tz the timezone to test - * @return boolean true if this time zone is equal to the supplied time zone - */ - function isEqual($tz) - { - if(strcasecmp($this->id, $tz->id) == 0) { - return true; - } else { - return false; - } - } - /** - * Is this time zone equivalent to another - * - * Tests to see if this time zone is equivalent to - * a given time zone object. Equivalence in this context - * is defined by the two time zones having an equal raw - * offset and an equal setting of "hasdst". This is not true - * equivalence, as the two time zones may have different rules - * for the observance of DST, but this implementation does not - * know DST rules. - * - * @access public - * @param object Date_TimeZone $tz the timezone object to test - * @return boolean true if this time zone is equivalent to the supplied time zone - */ - function isEquivalent($tz) - { - if($this->offset == $tz->offset && $this->hasdst == $tz->hasdst) { - return true; - } else { - return false; - } - } - /** - * Returns true if this zone observes daylight savings time - * - * Returns true if this zone observes daylight savings time - * - * @access public - * @return boolean true if this time zone has DST - */ - function hasDaylightTime() - { - return $this->hasdst; - } - /** - * Is the given date/time in DST for this time zone - * - * Attempts to determine if a given Date object represents a date/time - * that is in DST for this time zone. WARNINGS: this basically attempts to - * "trick" the system into telling us if we're in DST for a given time zone. - * This uses putenv() which may not work in safe mode, and relies on unix time - * which is only valid for dates from 1970 to ~2038. This relies on the - * underlying OS calls, so it may not work on Windows or on a system where - * zoneinfo is not installed or configured properly. - * - * @access public - * @param object Date $date the date/time to test - * @return boolean true if this date is in DST for this time zone - */ - function inDaylightTime($date) - { - $env_tz = ""; - if(getenv("TZ")) { - $env_tz = getenv("TZ"); - } - putenv("TZ=".$this->id); - $ltime = localtime($date->getTime(), true); - putenv("TZ=".$env_tz); - return $ltime['tm_isdst']; - } - /** - * Get the DST offset for this time zone - * - * Returns the DST offset of this time zone, in milliseconds, - * if the zone observes DST, zero otherwise. Currently the - * DST offset is hard-coded to one hour. - * - * @access public - * @return int the DST offset, in milliseconds or zero if the zone does not observe DST - */ - function getDSTSavings() - { - if($this->hasdst) { - return 3600000; - } else { - return 0; - } - } - /** - * Get the DST-corrected offset to UTC for the given date - * - * Attempts to get the offset to UTC for a given date/time, taking into - * account daylight savings time, if the time zone observes it and if - * it is in effect. Please see the WARNINGS on Date::TimeZone::inDaylightTime(). - * - * - * @access public - * @param object Date $date the Date to test - * @return int the corrected offset to UTC in milliseconds - */ - function getOffset($date) - { - if($this->inDaylightTime($date)) { - return $this->offset + $this->getDSTSavings(); - } else { - return $this->offset; - } - } - /** - * Returns the list of valid time zone id strings - * - * Returns the list of valid time zone id strings - * - * @access public - * @return mixed an array of strings with the valid time zone IDs - */ - function getAvailableIDs() - { - global $_DATE_TIMEZONE_DATA; - return array_keys($_DATE_TIMEZONE_DATA); - } - /** - * Returns the id for this time zone - * - * Returns the time zone id for this time zone, i.e. "America/Chicago" - * - * @access public - * @return string the id - */ - function getID() - { - return $this->id; - } - /** - * Returns the long name for this time zone - * - * Returns the long name for this time zone, - * i.e. "Central Standard Time" - * - * @access public - * @return string the long name - */ - function getLongName() - { - return $this->longname; - } - /** - * Returns the short name for this time zone - * - * Returns the short name for this time zone, i.e. "CST" - * - * @access public - * @return string the short name - */ - function getShortName() - { - return $this->shortname; - } - /** - * Returns the DST long name for this time zone - * - * Returns the DST long name for this time zone, i.e. "Central Daylight Time" - * - * @access public - * @return string the daylight savings time long name - */ - function getDSTLongName() - { - return $this->dstlongname; - } - /** - * Returns the DST short name for this time zone - * - * Returns the DST short name for this time zone, i.e. "CDT" - * - * @access public - * @return string the daylight savings time short name - */ - function getDSTShortName() - { - return $this->dstshortname; - } - /** - * Returns the raw (non-DST-corrected) offset from UTC/GMT for this time zone - * - * Returns the raw (non-DST-corrected) offset from UTC/GMT for this time zone - * - * @access public - * @return int the offset, in milliseconds - */ - function getRawOffset() - { - return $this->offset; - } -} // Date_TimeZone -// -// Time Zone Data -// offset is in miliseconds -// -$GLOBALS['_DATE_TIMEZONE_DATA'] = array( - 'Etc/GMT+12' => array( - 'offset' => -43200000, - 'longname' => "GMT-12:00", - 'shortname' => 'GMT-12:00', - 'hasdst' => false ), - 'Etc/GMT+11' => array( - 'offset' => -39600000, - 'longname' => "GMT-11:00", - 'shortname' => 'GMT-11:00', - 'hasdst' => false ), - 'MIT' => array( - 'offset' => -39600000, - 'longname' => "West Samoa Time", - 'shortname' => 'WST', - 'hasdst' => false ), - 'Pacific/Apia' => array( - 'offset' => -39600000, - 'longname' => "West Samoa Time", - 'shortname' => 'WST', - 'hasdst' => false ), - 'Pacific/Midway' => array( - 'offset' => -39600000, - 'longname' => "Samoa Standard Time", - 'shortname' => 'SST', - 'hasdst' => false ), - 'Pacific/Niue' => array( - 'offset' => -39600000, - 'longname' => "Niue Time", - 'shortname' => 'NUT', - 'hasdst' => false ), - 'Pacific/Pago_Pago' => array( - 'offset' => -39600000, - 'longname' => "Samoa Standard Time", - 'shortname' => 'SST', - 'hasdst' => false ), - 'Pacific/Samoa' => array( - 'offset' => -39600000, - 'longname' => "Samoa Standard Time", - 'shortname' => 'SST', - 'hasdst' => false ), - 'US/Samoa' => array( - 'offset' => -39600000, - 'longname' => "Samoa Standard Time", - 'shortname' => 'SST', - 'hasdst' => false ), - 'America/Adak' => array( - 'offset' => -36000000, - 'longname' => "Hawaii-Aleutian Standard Time", - 'shortname' => 'HAST', - 'hasdst' => true, - 'dstlongname' => "Hawaii-Aleutian Daylight Time", - 'dstshortname' => 'HADT' ), - 'America/Atka' => array( - 'offset' => -36000000, - 'longname' => "Hawaii-Aleutian Standard Time", - 'shortname' => 'HAST', - 'hasdst' => true, - 'dstlongname' => "Hawaii-Aleutian Daylight Time", - 'dstshortname' => 'HADT' ), - 'Etc/GMT+10' => array( - 'offset' => -36000000, - 'longname' => "GMT-10:00", - 'shortname' => 'GMT-10:00', - 'hasdst' => false ), - 'HST' => array( - 'offset' => -36000000, - 'longname' => "Hawaii Standard Time", - 'shortname' => 'HST', - 'hasdst' => false ), - 'Pacific/Fakaofo' => array( - 'offset' => -36000000, - 'longname' => "Tokelau Time", - 'shortname' => 'TKT', - 'hasdst' => false ), - 'Pacific/Honolulu' => array( - 'offset' => -36000000, - 'longname' => "Hawaii Standard Time", - 'shortname' => 'HST', - 'hasdst' => false ), - 'Pacific/Johnston' => array( - 'offset' => -36000000, - 'longname' => "Hawaii Standard Time", - 'shortname' => 'HST', - 'hasdst' => false ), - 'Pacific/Rarotonga' => array( - 'offset' => -36000000, - 'longname' => "Cook Is. Time", - 'shortname' => 'CKT', - 'hasdst' => false ), - 'Pacific/Tahiti' => array( - 'offset' => -36000000, - 'longname' => "Tahiti Time", - 'shortname' => 'TAHT', - 'hasdst' => false ), - 'SystemV/HST10' => array( - 'offset' => -36000000, - 'longname' => "Hawaii Standard Time", - 'shortname' => 'HST', - 'hasdst' => false ), - 'US/Aleutian' => array( - 'offset' => -36000000, - 'longname' => "Hawaii-Aleutian Standard Time", - 'shortname' => 'HAST', - 'hasdst' => true, - 'dstlongname' => "Hawaii-Aleutian Daylight Time", - 'dstshortname' => 'HADT' ), - 'US/Hawaii' => array( - 'offset' => -36000000, - 'longname' => "Hawaii Standard Time", - 'shortname' => 'HST', - 'hasdst' => false ), - 'Pacific/Marquesas' => array( - 'offset' => -34200000, - 'longname' => "Marquesas Time", - 'shortname' => 'MART', - 'hasdst' => false ), - 'AST' => array( - 'offset' => -32400000, - 'longname' => "Alaska Standard Time", - 'shortname' => 'AKST', - 'hasdst' => true, - 'dstlongname' => "Alaska Daylight Time", - 'dstshortname' => 'AKDT' ), - 'America/Anchorage' => array( - 'offset' => -32400000, - 'longname' => "Alaska Standard Time", - 'shortname' => 'AKST', - 'hasdst' => true, - 'dstlongname' => "Alaska Daylight Time", - 'dstshortname' => 'AKDT' ), - 'America/Juneau' => array( - 'offset' => -32400000, - 'longname' => "Alaska Standard Time", - 'shortname' => 'AKST', - 'hasdst' => true, - 'dstlongname' => "Alaska Daylight Time", - 'dstshortname' => 'AKDT' ), - 'America/Nome' => array( - 'offset' => -32400000, - 'longname' => "Alaska Standard Time", - 'shortname' => 'AKST', - 'hasdst' => true, - 'dstlongname' => "Alaska Daylight Time", - 'dstshortname' => 'AKDT' ), - 'America/Yakutat' => array( - 'offset' => -32400000, - 'longname' => "Alaska Standard Time", - 'shortname' => 'AKST', - 'hasdst' => true, - 'dstlongname' => "Alaska Daylight Time", - 'dstshortname' => 'AKDT' ), - 'Etc/GMT+9' => array( - 'offset' => -32400000, - 'longname' => "GMT-09:00", - 'shortname' => 'GMT-09:00', - 'hasdst' => false ), - 'Pacific/Gambier' => array( - 'offset' => -32400000, - 'longname' => "Gambier Time", - 'shortname' => 'GAMT', - 'hasdst' => false ), - 'SystemV/YST9' => array( - 'offset' => -32400000, - 'longname' => "Gambier Time", - 'shortname' => 'GAMT', - 'hasdst' => false ), - 'SystemV/YST9YDT' => array( - 'offset' => -32400000, - 'longname' => "Alaska Standard Time", - 'shortname' => 'AKST', - 'hasdst' => true, - 'dstlongname' => "Alaska Daylight Time", - 'dstshortname' => 'AKDT' ), - 'US/Alaska' => array( - 'offset' => -32400000, - 'longname' => "Alaska Standard Time", - 'shortname' => 'AKST', - 'hasdst' => true, - 'dstlongname' => "Alaska Daylight Time", - 'dstshortname' => 'AKDT' ), - 'America/Dawson' => array( - 'offset' => -28800000, - 'longname' => "Pacific Standard Time", - 'shortname' => 'PST', - 'hasdst' => true, - 'dstlongname' => "Pacific Daylight Time", - 'dstshortname' => 'PDT' ), - 'America/Ensenada' => array( - 'offset' => -28800000, - 'longname' => "Pacific Standard Time", - 'shortname' => 'PST', - 'hasdst' => true, - 'dstlongname' => "Pacific Daylight Time", - 'dstshortname' => 'PDT' ), - 'America/Los_Angeles' => array( - 'offset' => -28800000, - 'longname' => "Pacific Standard Time", - 'shortname' => 'PST', - 'hasdst' => true, - 'dstlongname' => "Pacific Daylight Time", - 'dstshortname' => 'PDT' ), - 'America/Tijuana' => array( - 'offset' => -28800000, - 'longname' => "Pacific Standard Time", - 'shortname' => 'PST', - 'hasdst' => true, - 'dstlongname' => "Pacific Daylight Time", - 'dstshortname' => 'PDT' ), - 'America/Vancouver' => array( - 'offset' => -28800000, - 'longname' => "Pacific Standard Time", - 'shortname' => 'PST', - 'hasdst' => true, - 'dstlongname' => "Pacific Daylight Time", - 'dstshortname' => 'PDT' ), - 'America/Whitehorse' => array( - 'offset' => -28800000, - 'longname' => "Pacific Standard Time", - 'shortname' => 'PST', - 'hasdst' => true, - 'dstlongname' => "Pacific Daylight Time", - 'dstshortname' => 'PDT' ), - 'Canada/Pacific' => array( - 'offset' => -28800000, - 'longname' => "Pacific Standard Time", - 'shortname' => 'PST', - 'hasdst' => true, - 'dstlongname' => "Pacific Daylight Time", - 'dstshortname' => 'PDT' ), - 'Canada/Yukon' => array( - 'offset' => -28800000, - 'longname' => "Pacific Standard Time", - 'shortname' => 'PST', - 'hasdst' => true, - 'dstlongname' => "Pacific Daylight Time", - 'dstshortname' => 'PDT' ), - 'Etc/GMT+8' => array( - 'offset' => -28800000, - 'longname' => "GMT-08:00", - 'shortname' => 'GMT-08:00', - 'hasdst' => false ), - 'Mexico/BajaNorte' => array( - 'offset' => -28800000, - 'longname' => "Pacific Standard Time", - 'shortname' => 'PST', - 'hasdst' => true, - 'dstlongname' => "Pacific Daylight Time", - 'dstshortname' => 'PDT' ), - 'PST' => array( - 'offset' => -28800000, - 'longname' => "Pacific Standard Time", - 'shortname' => 'PST', - 'hasdst' => true, - 'dstlongname' => "Pacific Daylight Time", - 'dstshortname' => 'PDT' ), - 'PST8PDT' => array( - 'offset' => -28800000, - 'longname' => "Pacific Standard Time", - 'shortname' => 'PST', - 'hasdst' => true, - 'dstlongname' => "Pacific Daylight Time", - 'dstshortname' => 'PDT' ), - 'Pacific/Pitcairn' => array( - 'offset' => -28800000, - 'longname' => "Pitcairn Standard Time", - 'shortname' => 'PST', - 'hasdst' => false ), - 'SystemV/PST8' => array( - 'offset' => -28800000, - 'longname' => "Pitcairn Standard Time", - 'shortname' => 'PST', - 'hasdst' => false ), - 'SystemV/PST8PDT' => array( - 'offset' => -28800000, - 'longname' => "Pacific Standard Time", - 'shortname' => 'PST', - 'hasdst' => true, - 'dstlongname' => "Pacific Daylight Time", - 'dstshortname' => 'PDT' ), - 'US/Pacific' => array( - 'offset' => -28800000, - 'longname' => "Pacific Standard Time", - 'shortname' => 'PST', - 'hasdst' => true, - 'dstlongname' => "Pacific Daylight Time", - 'dstshortname' => 'PDT' ), - 'US/Pacific-New' => array( - 'offset' => -28800000, - 'longname' => "Pacific Standard Time", - 'shortname' => 'PST', - 'hasdst' => true, - 'dstlongname' => "Pacific Daylight Time", - 'dstshortname' => 'PDT' ), - 'America/Boise' => array( - 'offset' => -25200000, - 'longname' => "Mountain Standard Time", - 'shortname' => 'MST', - 'hasdst' => true, - 'dstlongname' => "Mountain Daylight Time", - 'dstshortname' => 'MDT' ), - 'America/Cambridge_Bay' => array( - 'offset' => -25200000, - 'longname' => "Mountain Standard Time", - 'shortname' => 'MST', - 'hasdst' => true, - 'dstlongname' => "Mountain Daylight Time", - 'dstshortname' => 'MDT' ), - 'America/Chihuahua' => array( - 'offset' => -25200000, - 'longname' => "Mountain Standard Time", - 'shortname' => 'MST', - 'hasdst' => true, - 'dstlongname' => "Mountain Daylight Time", - 'dstshortname' => 'MDT' ), - 'America/Dawson_Creek' => array( - 'offset' => -25200000, - 'longname' => "Mountain Standard Time", - 'shortname' => 'MST', - 'hasdst' => false ), - 'America/Denver' => array( - 'offset' => -25200000, - 'longname' => "Mountain Standard Time", - 'shortname' => 'MST', - 'hasdst' => true, - 'dstlongname' => "Mountain Daylight Time", - 'dstshortname' => 'MDT' ), - 'America/Edmonton' => array( - 'offset' => -25200000, - 'longname' => "Mountain Standard Time", - 'shortname' => 'MST', - 'hasdst' => true, - 'dstlongname' => "Mountain Daylight Time", - 'dstshortname' => 'MDT' ), - 'America/Hermosillo' => array( - 'offset' => -25200000, - 'longname' => "Mountain Standard Time", - 'shortname' => 'MST', - 'hasdst' => false ), - 'America/Inuvik' => array( - 'offset' => -25200000, - 'longname' => "Mountain Standard Time", - 'shortname' => 'MST', - 'hasdst' => true, - 'dstlongname' => "Mountain Daylight Time", - 'dstshortname' => 'MDT' ), - 'America/Mazatlan' => array( - 'offset' => -25200000, - 'longname' => "Mountain Standard Time", - 'shortname' => 'MST', - 'hasdst' => true, - 'dstlongname' => "Mountain Daylight Time", - 'dstshortname' => 'MDT' ), - 'America/Phoenix' => array( - 'offset' => -25200000, - 'longname' => "Mountain Standard Time", - 'shortname' => 'MST', - 'hasdst' => false ), - 'America/Shiprock' => array( - 'offset' => -25200000, - 'longname' => "Mountain Standard Time", - 'shortname' => 'MST', - 'hasdst' => true, - 'dstlongname' => "Mountain Daylight Time", - 'dstshortname' => 'MDT' ), - 'America/Yellowknife' => array( - 'offset' => -25200000, - 'longname' => "Mountain Standard Time", - 'shortname' => 'MST', - 'hasdst' => true, - 'dstlongname' => "Mountain Daylight Time", - 'dstshortname' => 'MDT' ), - 'Canada/Mountain' => array( - 'offset' => -25200000, - 'longname' => "Mountain Standard Time", - 'shortname' => 'MST', - 'hasdst' => true, - 'dstlongname' => "Mountain Daylight Time", - 'dstshortname' => 'MDT' ), - 'Etc/GMT+7' => array( - 'offset' => -25200000, - 'longname' => "GMT-07:00", - 'shortname' => 'GMT-07:00', - 'hasdst' => false ), - 'MST' => array( - 'offset' => -25200000, - 'longname' => "Mountain Standard Time", - 'shortname' => 'MST', - 'hasdst' => true, - 'dstlongname' => "Mountain Daylight Time", - 'dstshortname' => 'MDT' ), - 'MST7MDT' => array( - 'offset' => -25200000, - 'longname' => "Mountain Standard Time", - 'shortname' => 'MST', - 'hasdst' => true, - 'dstlongname' => "Mountain Daylight Time", - 'dstshortname' => 'MDT' ), - 'Mexico/BajaSur' => array( - 'offset' => -25200000, - 'longname' => "Mountain Standard Time", - 'shortname' => 'MST', - 'hasdst' => true, - 'dstlongname' => "Mountain Daylight Time", - 'dstshortname' => 'MDT' ), - 'Navajo' => array( - 'offset' => -25200000, - 'longname' => "Mountain Standard Time", - 'shortname' => 'MST', - 'hasdst' => true, - 'dstlongname' => "Mountain Daylight Time", - 'dstshortname' => 'MDT' ), - 'PNT' => array( - 'offset' => -25200000, - 'longname' => "Mountain Standard Time", - 'shortname' => 'MST', - 'hasdst' => false ), - 'SystemV/MST7' => array( - 'offset' => -25200000, - 'longname' => "Mountain Standard Time", - 'shortname' => 'MST', - 'hasdst' => false ), - 'SystemV/MST7MDT' => array( - 'offset' => -25200000, - 'longname' => "Mountain Standard Time", - 'shortname' => 'MST', - 'hasdst' => true, - 'dstlongname' => "Mountain Daylight Time", - 'dstshortname' => 'MDT' ), - 'US/Arizona' => array( - 'offset' => -25200000, - 'longname' => "Mountain Standard Time", - 'shortname' => 'MST', - 'hasdst' => false ), - 'US/Mountain' => array( - 'offset' => -25200000, - 'longname' => "Mountain Standard Time", - 'shortname' => 'MST', - 'hasdst' => true, - 'dstlongname' => "Mountain Daylight Time", - 'dstshortname' => 'MDT' ), - 'America/Belize' => array( - 'offset' => -21600000, - 'longname' => "Central Standard Time", - 'shortname' => 'CST', - 'hasdst' => false ), - 'America/Cancun' => array( - 'offset' => -21600000, - 'longname' => "Central Standard Time", - 'shortname' => 'CST', - 'hasdst' => true, - 'dstlongname' => "Central Daylight Time", - 'dstshortname' => 'CDT' ), - 'America/Chicago' => array( - 'offset' => -21600000, - 'longname' => "Central Standard Time", - 'shortname' => 'CST', - 'hasdst' => true, - 'dstlongname' => "Central Daylight Time", - 'dstshortname' => 'CDT' ), - 'America/Costa_Rica' => array( - 'offset' => -21600000, - 'longname' => "Central Standard Time", - 'shortname' => 'CST', - 'hasdst' => false ), - 'America/El_Salvador' => array( - 'offset' => -21600000, - 'longname' => "Central Standard Time", - 'shortname' => 'CST', - 'hasdst' => false ), - 'America/Guatemala' => array( - 'offset' => -21600000, - 'longname' => "Central Standard Time", - 'shortname' => 'CST', - 'hasdst' => false ), - 'America/Managua' => array( - 'offset' => -21600000, - 'longname' => "Central Standard Time", - 'shortname' => 'CST', - 'hasdst' => false ), - 'America/Menominee' => array( - 'offset' => -21600000, - 'longname' => "Central Standard Time", - 'shortname' => 'CST', - 'hasdst' => true, - 'dstlongname' => "Central Daylight Time", - 'dstshortname' => 'CDT' ), - 'America/Merida' => array( - 'offset' => -21600000, - 'longname' => "Central Standard Time", - 'shortname' => 'CST', - 'hasdst' => true, - 'dstlongname' => "Central Daylight Time", - 'dstshortname' => 'CDT' ), - 'America/Mexico_City' => array( - 'offset' => -21600000, - 'longname' => "Central Standard Time", - 'shortname' => 'CST', - 'hasdst' => false ), - 'America/Monterrey' => array( - 'offset' => -21600000, - 'longname' => "Central Standard Time", - 'shortname' => 'CST', - 'hasdst' => true, - 'dstlongname' => "Central Daylight Time", - 'dstshortname' => 'CDT' ), - 'America/North_Dakota/Center' => array( - 'offset' => -21600000, - 'longname' => "Central Standard Time", - 'shortname' => 'CST', - 'hasdst' => true, - 'dstlongname' => "Central Daylight Time", - 'dstshortname' => 'CDT' ), - 'America/Rainy_River' => array( - 'offset' => -21600000, - 'longname' => "Central Standard Time", - 'shortname' => 'CST', - 'hasdst' => true, - 'dstlongname' => "Central Daylight Time", - 'dstshortname' => 'CDT' ), - 'America/Rankin_Inlet' => array( - 'offset' => -21600000, - 'longname' => "Eastern Standard Time", - 'shortname' => 'EST', - 'hasdst' => true, - 'dstlongname' => "Eastern Daylight Time", - 'dstshortname' => 'EDT' ), - 'America/Regina' => array( - 'offset' => -21600000, - 'longname' => "Central Standard Time", - 'shortname' => 'CST', - 'hasdst' => false ), - 'America/Swift_Current' => array( - 'offset' => -21600000, - 'longname' => "Central Standard Time", - 'shortname' => 'CST', - 'hasdst' => false ), - 'America/Tegucigalpa' => array( - 'offset' => -21600000, - 'longname' => "Central Standard Time", - 'shortname' => 'CST', - 'hasdst' => false ), - 'America/Winnipeg' => array( - 'offset' => -21600000, - 'longname' => "Central Standard Time", - 'shortname' => 'CST', - 'hasdst' => true, - 'dstlongname' => "Central Daylight Time", - 'dstshortname' => 'CDT' ), - 'CST' => array( - 'offset' => -21600000, - 'longname' => "Central Standard Time", - 'shortname' => 'CST', - 'hasdst' => true, - 'dstlongname' => "Central Daylight Time", - 'dstshortname' => 'CDT' ), - 'CST6CDT' => array( - 'offset' => -21600000, - 'longname' => "Central Standard Time", - 'shortname' => 'CST', - 'hasdst' => true, - 'dstlongname' => "Central Daylight Time", - 'dstshortname' => 'CDT' ), - 'Canada/Central' => array( - 'offset' => -21600000, - 'longname' => "Central Standard Time", - 'shortname' => 'CST', - 'hasdst' => true, - 'dstlongname' => "Central Daylight Time", - 'dstshortname' => 'CDT' ), - 'Canada/East-Saskatchewan' => array( - 'offset' => -21600000, - 'longname' => "Central Standard Time", - 'shortname' => 'CST', - 'hasdst' => false ), - 'Canada/Saskatchewan' => array( - 'offset' => -21600000, - 'longname' => "Central Standard Time", - 'shortname' => 'CST', - 'hasdst' => false ), - 'Chile/EasterIsland' => array( - 'offset' => -21600000, - 'longname' => "Easter Is. Time", - 'shortname' => 'EAST', - 'hasdst' => true, - 'dstlongname' => "Easter Is. Summer Time", - 'dstshortname' => 'EASST' ), - 'Etc/GMT+6' => array( - 'offset' => -21600000, - 'longname' => "GMT-06:00", - 'shortname' => 'GMT-06:00', - 'hasdst' => false ), - 'Mexico/General' => array( - 'offset' => -21600000, - 'longname' => "Central Standard Time", - 'shortname' => 'CST', - 'hasdst' => false ), - 'Pacific/Easter' => array( - 'offset' => -21600000, - 'longname' => "Easter Is. Time", - 'shortname' => 'EAST', - 'hasdst' => true, - 'dstlongname' => "Easter Is. Summer Time", - 'dstshortname' => 'EASST' ), - 'Pacific/Galapagos' => array( - 'offset' => -21600000, - 'longname' => "Galapagos Time", - 'shortname' => 'GALT', - 'hasdst' => false ), - 'SystemV/CST6' => array( - 'offset' => -21600000, - 'longname' => "Central Standard Time", - 'shortname' => 'CST', - 'hasdst' => false ), - 'SystemV/CST6CDT' => array( - 'offset' => -21600000, - 'longname' => "Central Standard Time", - 'shortname' => 'CST', - 'hasdst' => true, - 'dstlongname' => "Central Daylight Time", - 'dstshortname' => 'CDT' ), - 'US/Central' => array( - 'offset' => -21600000, - 'longname' => "Central Standard Time", - 'shortname' => 'CST', - 'hasdst' => true, - 'dstlongname' => "Central Daylight Time", - 'dstshortname' => 'CDT' ), - 'America/Bogota' => array( - 'offset' => -18000000, - 'longname' => "Colombia Time", - 'shortname' => 'COT', - 'hasdst' => false ), - 'America/Cayman' => array( - 'offset' => -18000000, - 'longname' => "Eastern Standard Time", - 'shortname' => 'EST', - 'hasdst' => false ), - 'America/Detroit' => array( - 'offset' => -18000000, - 'longname' => "Eastern Standard Time", - 'shortname' => 'EST', - 'hasdst' => true, - 'dstlongname' => "Eastern Daylight Time", - 'dstshortname' => 'EDT' ), - 'America/Eirunepe' => array( - 'offset' => -18000000, - 'longname' => "Acre Time", - 'shortname' => 'ACT', - 'hasdst' => false ), - 'America/Fort_Wayne' => array( - 'offset' => -18000000, - 'longname' => "Eastern Standard Time", - 'shortname' => 'EST', - 'hasdst' => false ), - 'America/Grand_Turk' => array( - 'offset' => -18000000, - 'longname' => "Eastern Standard Time", - 'shortname' => 'EST', - 'hasdst' => true, - 'dstlongname' => "Eastern Daylight Time", - 'dstshortname' => 'EDT' ), - 'America/Guayaquil' => array( - 'offset' => -18000000, - 'longname' => "Ecuador Time", - 'shortname' => 'ECT', - 'hasdst' => false ), - 'America/Havana' => array( - 'offset' => -18000000, - 'longname' => "Central Standard Time", - 'shortname' => 'CST', - 'hasdst' => true, - 'dstlongname' => "Central Daylight Time", - 'dstshortname' => 'CDT' ), - 'America/Indiana/Indianapolis' => array( - 'offset' => -18000000, - 'longname' => "Eastern Standard Time", - 'shortname' => 'EST', - 'hasdst' => false ), - 'America/Indiana/Knox' => array( - 'offset' => -18000000, - 'longname' => "Eastern Standard Time", - 'shortname' => 'EST', - 'hasdst' => false ), - 'America/Indiana/Marengo' => array( - 'offset' => -18000000, - 'longname' => "Eastern Standard Time", - 'shortname' => 'EST', - 'hasdst' => false ), - 'America/Indiana/Vevay' => array( - 'offset' => -18000000, - 'longname' => "Eastern Standard Time", - 'shortname' => 'EST', - 'hasdst' => false ), - 'America/Indianapolis' => array( - 'offset' => -18000000, - 'longname' => "Eastern Standard Time", - 'shortname' => 'EST', - 'hasdst' => false ), - 'America/Iqaluit' => array( - 'offset' => -18000000, - 'longname' => "Eastern Standard Time", - 'shortname' => 'EST', - 'hasdst' => true, - 'dstlongname' => "Eastern Daylight Time", - 'dstshortname' => 'EDT' ), - 'America/Jamaica' => array( - 'offset' => -18000000, - 'longname' => "Eastern Standard Time", - 'shortname' => 'EST', - 'hasdst' => false ), - 'America/Kentucky/Louisville' => array( - 'offset' => -18000000, - 'longname' => "Eastern Standard Time", - 'shortname' => 'EST', - 'hasdst' => true, - 'dstlongname' => "Eastern Daylight Time", - 'dstshortname' => 'EDT' ), - 'America/Kentucky/Monticello' => array( - 'offset' => -18000000, - 'longname' => "Eastern Standard Time", - 'shortname' => 'EST', - 'hasdst' => true, - 'dstlongname' => "Eastern Daylight Time", - 'dstshortname' => 'EDT' ), - 'America/Knox_IN' => array( - 'offset' => -18000000, - 'longname' => "Eastern Standard Time", - 'shortname' => 'EST', - 'hasdst' => false ), - 'America/Lima' => array( - 'offset' => -18000000, - 'longname' => "Peru Time", - 'shortname' => 'PET', - 'hasdst' => false ), - 'America/Louisville' => array( - 'offset' => -18000000, - 'longname' => "Eastern Standard Time", - 'shortname' => 'EST', - 'hasdst' => true, - 'dstlongname' => "Eastern Daylight Time", - 'dstshortname' => 'EDT' ), - 'America/Montreal' => array( - 'offset' => -18000000, - 'longname' => "Eastern Standard Time", - 'shortname' => 'EST', - 'hasdst' => true, - 'dstlongname' => "Eastern Daylight Time", - 'dstshortname' => 'EDT' ), - 'America/Nassau' => array( - 'offset' => -18000000, - 'longname' => "Eastern Standard Time", - 'shortname' => 'EST', - 'hasdst' => true, - 'dstlongname' => "Eastern Daylight Time", - 'dstshortname' => 'EDT' ), - 'America/New_York' => array( - 'offset' => -18000000, - 'longname' => "Eastern Standard Time", - 'shortname' => 'EST', - 'hasdst' => true, - 'dstlongname' => "Eastern Daylight Time", - 'dstshortname' => 'EDT' ), - 'America/Nipigon' => array( - 'offset' => -18000000, - 'longname' => "Eastern Standard Time", - 'shortname' => 'EST', - 'hasdst' => true, - 'dstlongname' => "Eastern Daylight Time", - 'dstshortname' => 'EDT' ), - 'America/Panama' => array( - 'offset' => -18000000, - 'longname' => "Eastern Standard Time", - 'shortname' => 'EST', - 'hasdst' => false ), - 'America/Pangnirtung' => array( - 'offset' => -18000000, - 'longname' => "Eastern Standard Time", - 'shortname' => 'EST', - 'hasdst' => true, - 'dstlongname' => "Eastern Daylight Time", - 'dstshortname' => 'EDT' ), - 'America/Port-au-Prince' => array( - 'offset' => -18000000, - 'longname' => "Eastern Standard Time", - 'shortname' => 'EST', - 'hasdst' => false ), - 'America/Porto_Acre' => array( - 'offset' => -18000000, - 'longname' => "Acre Time", - 'shortname' => 'ACT', - 'hasdst' => false ), - 'America/Rio_Branco' => array( - 'offset' => -18000000, - 'longname' => "Acre Time", - 'shortname' => 'ACT', - 'hasdst' => false ), - 'America/Thunder_Bay' => array( - 'offset' => -18000000, - 'longname' => "Eastern Standard Time", - 'shortname' => 'EST', - 'hasdst' => true, - 'dstlongname' => "Eastern Daylight Time", - 'dstshortname' => 'EDT' ), - 'Brazil/Acre' => array( - 'offset' => -18000000, - 'longname' => "Acre Time", - 'shortname' => 'ACT', - 'hasdst' => false ), - 'Canada/Eastern' => array( - 'offset' => -18000000, - 'longname' => "Eastern Standard Time", - 'shortname' => 'EST', - 'hasdst' => true, - 'dstlongname' => "Eastern Daylight Time", - 'dstshortname' => 'EDT' ), - 'Cuba' => array( - 'offset' => -18000000, - 'longname' => "Central Standard Time", - 'shortname' => 'CST', - 'hasdst' => true, - 'dstlongname' => "Central Daylight Time", - 'dstshortname' => 'CDT' ), - 'EST' => array( - 'offset' => -18000000, - 'longname' => "Eastern Standard Time", - 'shortname' => 'EST', - 'hasdst' => true, - 'dstlongname' => "Eastern Daylight Time", - 'dstshortname' => 'EDT' ), - 'EST5EDT' => array( - 'offset' => -18000000, - 'longname' => "Eastern Standard Time", - 'shortname' => 'EST', - 'hasdst' => true, - 'dstlongname' => "Eastern Daylight Time", - 'dstshortname' => 'EDT' ), - 'EDT' => array( // Added by AWC - 'offset' => -14400000, // Added by AWC - 'longname' => "Eastern Daylight Time", // Added by AWC - 'shortname' => 'EDT', // Added by AWC - 'hasdst' => false ), // Added by AWC - 'Etc/GMT+5' => array( - 'offset' => -18000000, - 'longname' => "GMT-05:00", - 'shortname' => 'GMT-05:00', - 'hasdst' => false ), - 'IET' => array( - 'offset' => -18000000, - 'longname' => "Eastern Standard Time", - 'shortname' => 'EST', - 'hasdst' => false ), - 'Jamaica' => array( - 'offset' => -18000000, - 'longname' => "Eastern Standard Time", - 'shortname' => 'EST', - 'hasdst' => false ), - 'SystemV/EST5' => array( - 'offset' => -18000000, - 'longname' => "Eastern Standard Time", - 'shortname' => 'EST', - 'hasdst' => false ), - 'SystemV/EST5EDT' => array( - 'offset' => -18000000, - 'longname' => "Eastern Standard Time", - 'shortname' => 'EST', - 'hasdst' => true, - 'dstlongname' => "Eastern Daylight Time", - 'dstshortname' => 'EDT' ), - 'US/East-Indiana' => array( - 'offset' => -18000000, - 'longname' => "Eastern Standard Time", - 'shortname' => 'EST', - 'hasdst' => false ), - 'US/Eastern' => array( - 'offset' => -18000000, - 'longname' => "Eastern Standard Time", - 'shortname' => 'EST', - 'hasdst' => true, - 'dstlongname' => "Eastern Daylight Time", - 'dstshortname' => 'EDT' ), - 'US/Indiana-Starke' => array( - 'offset' => -18000000, - 'longname' => "Eastern Standard Time", - 'shortname' => 'EST', - 'hasdst' => false ), - 'US/Michigan' => array( - 'offset' => -18000000, - 'longname' => "Eastern Standard Time", - 'shortname' => 'EST', - 'hasdst' => true, - 'dstlongname' => "Eastern Daylight Time", - 'dstshortname' => 'EDT' ), - 'America/Anguilla' => array( - 'offset' => -14400000, - 'longname' => "Atlantic Standard Time", - 'shortname' => 'AST', - 'hasdst' => false ), - 'America/Antigua' => array( - 'offset' => -14400000, - 'longname' => "Atlantic Standard Time", - 'shortname' => 'AST', - 'hasdst' => false ), - 'America/Aruba' => array( - 'offset' => -14400000, - 'longname' => "Atlantic Standard Time", - 'shortname' => 'AST', - 'hasdst' => false ), - 'America/Asuncion' => array( - 'offset' => -14400000, - 'longname' => "Paraguay Time", - 'shortname' => 'PYT', - 'hasdst' => true, - 'dstlongname' => "Paraguay Summer Time", - 'dstshortname' => 'PYST' ), - 'America/Barbados' => array( - 'offset' => -14400000, - 'longname' => "Atlantic Standard Time", - 'shortname' => 'AST', - 'hasdst' => false ), - 'America/Boa_Vista' => array( - 'offset' => -14400000, - 'longname' => "Amazon Standard Time", - 'shortname' => 'AMT', - 'hasdst' => false ), - 'America/Caracas' => array( - 'offset' => -14400000, - 'longname' => "Venezuela Time", - 'shortname' => 'VET', - 'hasdst' => false ), - 'America/Cuiaba' => array( - 'offset' => -14400000, - 'longname' => "Amazon Standard Time", - 'shortname' => 'AMT', - 'hasdst' => true, - 'dstlongname' => "Amazon Summer Time", - 'dstshortname' => 'AMST' ), - 'America/Curacao' => array( - 'offset' => -14400000, - 'longname' => "Atlantic Standard Time", - 'shortname' => 'AST', - 'hasdst' => false ), - 'America/Dominica' => array( - 'offset' => -14400000, - 'longname' => "Atlantic Standard Time", - 'shortname' => 'AST', - 'hasdst' => false ), - 'America/Glace_Bay' => array( - 'offset' => -14400000, - 'longname' => "Atlantic Standard Time", - 'shortname' => 'AST', - 'hasdst' => true, - 'dstlongname' => "Atlantic Daylight Time", - 'dstshortname' => 'ADT' ), - 'America/Goose_Bay' => array( - 'offset' => -14400000, - 'longname' => "Atlantic Standard Time", - 'shortname' => 'AST', - 'hasdst' => true, - 'dstlongname' => "Atlantic Daylight Time", - 'dstshortname' => 'ADT' ), - 'America/Grenada' => array( - 'offset' => -14400000, - 'longname' => "Atlantic Standard Time", - 'shortname' => 'AST', - 'hasdst' => false ), - 'America/Guadeloupe' => array( - 'offset' => -14400000, - 'longname' => "Atlantic Standard Time", - 'shortname' => 'AST', - 'hasdst' => false ), - 'America/Guyana' => array( - 'offset' => -14400000, - 'longname' => "Guyana Time", - 'shortname' => 'GYT', - 'hasdst' => false ), - 'America/Halifax' => array( - 'offset' => -14400000, - 'longname' => "Atlantic Standard Time", - 'shortname' => 'AST', - 'hasdst' => true, - 'dstlongname' => "Atlantic Daylight Time", - 'dstshortname' => 'ADT' ), - 'America/La_Paz' => array( - 'offset' => -14400000, - 'longname' => "Bolivia Time", - 'shortname' => 'BOT', - 'hasdst' => false ), - 'America/Manaus' => array( - 'offset' => -14400000, - 'longname' => "Amazon Standard Time", - 'shortname' => 'AMT', - 'hasdst' => false ), - 'America/Martinique' => array( - 'offset' => -14400000, - 'longname' => "Atlantic Standard Time", - 'shortname' => 'AST', - 'hasdst' => false ), - 'America/Montserrat' => array( - 'offset' => -14400000, - 'longname' => "Atlantic Standard Time", - 'shortname' => 'AST', - 'hasdst' => false ), - 'America/Port_of_Spain' => array( - 'offset' => -14400000, - 'longname' => "Atlantic Standard Time", - 'shortname' => 'AST', - 'hasdst' => false ), - 'America/Porto_Velho' => array( - 'offset' => -14400000, - 'longname' => "Amazon Standard Time", - 'shortname' => 'AMT', - 'hasdst' => false ), - 'America/Puerto_Rico' => array( - 'offset' => -14400000, - 'longname' => "Atlantic Standard Time", - 'shortname' => 'AST', - 'hasdst' => false ), - 'America/Santiago' => array( - 'offset' => -14400000, - 'longname' => "Chile Time", - 'shortname' => 'CLT', - 'hasdst' => true, - 'dstlongname' => "Chile Summer Time", - 'dstshortname' => 'CLST' ), - 'America/Santo_Domingo' => array( - 'offset' => -14400000, - 'longname' => "Atlantic Standard Time", - 'shortname' => 'AST', - 'hasdst' => false ), - 'America/St_Kitts' => array( - 'offset' => -14400000, - 'longname' => "Atlantic Standard Time", - 'shortname' => 'AST', - 'hasdst' => false ), - 'America/St_Lucia' => array( - 'offset' => -14400000, - 'longname' => "Atlantic Standard Time", - 'shortname' => 'AST', - 'hasdst' => false ), - 'America/St_Thomas' => array( - 'offset' => -14400000, - 'longname' => "Atlantic Standard Time", - 'shortname' => 'AST', - 'hasdst' => false ), - 'America/St_Vincent' => array( - 'offset' => -14400000, - 'longname' => "Atlantic Standard Time", - 'shortname' => 'AST', - 'hasdst' => false ), - 'America/Thule' => array( - 'offset' => -14400000, - 'longname' => "Atlantic Standard Time", - 'shortname' => 'AST', - 'hasdst' => false ), - 'America/Tortola' => array( - 'offset' => -14400000, - 'longname' => "Atlantic Standard Time", - 'shortname' => 'AST', - 'hasdst' => false ), - 'America/Virgin' => array( - 'offset' => -14400000, - 'longname' => "Atlantic Standard Time", - 'shortname' => 'AST', - 'hasdst' => false ), - 'Antarctica/Palmer' => array( - 'offset' => -14400000, - 'longname' => "Chile Time", - 'shortname' => 'CLT', - 'hasdst' => true, - 'dstlongname' => "Chile Summer Time", - 'dstshortname' => 'CLST' ), - 'Atlantic/Bermuda' => array( - 'offset' => -14400000, - 'longname' => "Atlantic Standard Time", - 'shortname' => 'AST', - 'hasdst' => true, - 'dstlongname' => "Atlantic Daylight Time", - 'dstshortname' => 'ADT' ), - 'Atlantic/Stanley' => array( - 'offset' => -14400000, - 'longname' => "Falkland Is. Time", - 'shortname' => 'FKT', - 'hasdst' => true, - 'dstlongname' => "Falkland Is. Summer Time", - 'dstshortname' => 'FKST' ), - 'Brazil/West' => array( - 'offset' => -14400000, - 'longname' => "Amazon Standard Time", - 'shortname' => 'AMT', - 'hasdst' => false ), - 'Canada/Atlantic' => array( - 'offset' => -14400000, - 'longname' => "Atlantic Standard Time", - 'shortname' => 'AST', - 'hasdst' => true, - 'dstlongname' => "Atlantic Daylight Time", - 'dstshortname' => 'ADT' ), - 'Chile/Continental' => array( - 'offset' => -14400000, - 'longname' => "Chile Time", - 'shortname' => 'CLT', - 'hasdst' => true, - 'dstlongname' => "Chile Summer Time", - 'dstshortname' => 'CLST' ), - 'Etc/GMT+4' => array( - 'offset' => -14400000, - 'longname' => "GMT-04:00", - 'shortname' => 'GMT-04:00', - 'hasdst' => false ), - 'PRT' => array( - 'offset' => -14400000, - 'longname' => "Atlantic Standard Time", - 'shortname' => 'AST', - 'hasdst' => false ), - 'SystemV/AST4' => array( - 'offset' => -14400000, - 'longname' => "Atlantic Standard Time", - 'shortname' => 'AST', - 'hasdst' => false ), - 'SystemV/AST4ADT' => array( - 'offset' => -14400000, - 'longname' => "Atlantic Standard Time", - 'shortname' => 'AST', - 'hasdst' => true, - 'dstlongname' => "Atlantic Daylight Time", - 'dstshortname' => 'ADT' ), - 'America/St_Johns' => array( - 'offset' => -12600000, - 'longname' => "Newfoundland Standard Time", - 'shortname' => 'NST', - 'hasdst' => true, - 'dstlongname' => "Newfoundland Daylight Time", - 'dstshortname' => 'NDT' ), - 'CNT' => array( - 'offset' => -12600000, - 'longname' => "Newfoundland Standard Time", - 'shortname' => 'NST', - 'hasdst' => true, - 'dstlongname' => "Newfoundland Daylight Time", - 'dstshortname' => 'NDT' ), - 'Canada/Newfoundland' => array( - 'offset' => -12600000, - 'longname' => "Newfoundland Standard Time", - 'shortname' => 'NST', - 'hasdst' => true, - 'dstlongname' => "Newfoundland Daylight Time", - 'dstshortname' => 'NDT' ), - 'AGT' => array( - 'offset' => -10800000, - 'longname' => "Argentine Time", - 'shortname' => 'ART', - 'hasdst' => false ), - 'America/Araguaina' => array( - 'offset' => -10800000, - 'longname' => "Brazil Time", - 'shortname' => 'BRT', - 'hasdst' => true, - 'dstlongname' => "Brazil Summer Time", - 'dstshortname' => 'BRST' ), - 'America/Belem' => array( - 'offset' => -10800000, - 'longname' => "Brazil Time", - 'shortname' => 'BRT', - 'hasdst' => false ), - 'America/Buenos_Aires' => array( - 'offset' => -10800000, - 'longname' => "Argentine Time", - 'shortname' => 'ART', - 'hasdst' => false ), - 'America/Catamarca' => array( - 'offset' => -10800000, - 'longname' => "Argentine Time", - 'shortname' => 'ART', - 'hasdst' => false ), - 'America/Cayenne' => array( - 'offset' => -10800000, - 'longname' => "French Guiana Time", - 'shortname' => 'GFT', - 'hasdst' => false ), - 'America/Cordoba' => array( - 'offset' => -10800000, - 'longname' => "Argentine Time", - 'shortname' => 'ART', - 'hasdst' => false ), - 'America/Fortaleza' => array( - 'offset' => -10800000, - 'longname' => "Brazil Time", - 'shortname' => 'BRT', - 'hasdst' => true, - 'dstlongname' => "Brazil Summer Time", - 'dstshortname' => 'BRST' ), - 'America/Godthab' => array( - 'offset' => -10800000, - 'longname' => "Western Greenland Time", - 'shortname' => 'WGT', - 'hasdst' => true, - 'dstlongname' => "Western Greenland Summer Time", - 'dstshortname' => 'WGST' ), - 'America/Jujuy' => array( - 'offset' => -10800000, - 'longname' => "Argentine Time", - 'shortname' => 'ART', - 'hasdst' => false ), - 'America/Maceio' => array( - 'offset' => -10800000, - 'longname' => "Brazil Time", - 'shortname' => 'BRT', - 'hasdst' => true, - 'dstlongname' => "Brazil Summer Time", - 'dstshortname' => 'BRST' ), - 'America/Mendoza' => array( - 'offset' => -10800000, - 'longname' => "Argentine Time", - 'shortname' => 'ART', - 'hasdst' => false ), - 'America/Miquelon' => array( - 'offset' => -10800000, - 'longname' => "Pierre & Miquelon Standard Time", - 'shortname' => 'PMST', - 'hasdst' => true, - 'dstlongname' => "Pierre & Miquelon Daylight Time", - 'dstshortname' => 'PMDT' ), - 'America/Montevideo' => array( - 'offset' => -10800000, - 'longname' => "Uruguay Time", - 'shortname' => 'UYT', - 'hasdst' => false ), - 'America/Paramaribo' => array( - 'offset' => -10800000, - 'longname' => "Suriname Time", - 'shortname' => 'SRT', - 'hasdst' => false ), - 'America/Recife' => array( - 'offset' => -10800000, - 'longname' => "Brazil Time", - 'shortname' => 'BRT', - 'hasdst' => true, - 'dstlongname' => "Brazil Summer Time", - 'dstshortname' => 'BRST' ), - 'America/Rosario' => array( - 'offset' => -10800000, - 'longname' => "Argentine Time", - 'shortname' => 'ART', - 'hasdst' => false ), - 'America/Sao_Paulo' => array( - 'offset' => -10800000, - 'longname' => "Brazil Time", - 'shortname' => 'BRT', - 'hasdst' => true, - 'dstlongname' => "Brazil Summer Time", - 'dstshortname' => 'BRST' ), - 'BET' => array( - 'offset' => -10800000, - 'longname' => "Brazil Time", - 'shortname' => 'BRT', - 'hasdst' => true, - 'dstlongname' => "Brazil Summer Time", - 'dstshortname' => 'BRST' ), - 'Brazil/East' => array( - 'offset' => -10800000, - 'longname' => "Brazil Time", - 'shortname' => 'BRT', - 'hasdst' => true, - 'dstlongname' => "Brazil Summer Time", - 'dstshortname' => 'BRST' ), - 'Etc/GMT+3' => array( - 'offset' => -10800000, - 'longname' => "GMT-03:00", - 'shortname' => 'GMT-03:00', - 'hasdst' => false ), - 'America/Noronha' => array( - 'offset' => -7200000, - 'longname' => "Fernando de Noronha Time", - 'shortname' => 'FNT', - 'hasdst' => false ), - 'Atlantic/South_Georgia' => array( - 'offset' => -7200000, - 'longname' => "South Georgia Standard Time", - 'shortname' => 'GST', - 'hasdst' => false ), - 'Brazil/DeNoronha' => array( - 'offset' => -7200000, - 'longname' => "Fernando de Noronha Time", - 'shortname' => 'FNT', - 'hasdst' => false ), - 'Etc/GMT+2' => array( - 'offset' => -7200000, - 'longname' => "GMT-02:00", - 'shortname' => 'GMT-02:00', - 'hasdst' => false ), - 'America/Scoresbysund' => array( - 'offset' => -3600000, - 'longname' => "Eastern Greenland Time", - 'shortname' => 'EGT', - 'hasdst' => true, - 'dstlongname' => "Eastern Greenland Summer Time", - 'dstshortname' => 'EGST' ), - 'Atlantic/Azores' => array( - 'offset' => -3600000, - 'longname' => "Azores Time", - 'shortname' => 'AZOT', - 'hasdst' => true, - 'dstlongname' => "Azores Summer Time", - 'dstshortname' => 'AZOST' ), - 'Atlantic/Cape_Verde' => array( - 'offset' => -3600000, - 'longname' => "Cape Verde Time", - 'shortname' => 'CVT', - 'hasdst' => false ), - 'Etc/GMT+1' => array( - 'offset' => -3600000, - 'longname' => "GMT-01:00", - 'shortname' => 'GMT-01:00', - 'hasdst' => false ), - 'Africa/Abidjan' => array( - 'offset' => 0, - 'longname' => "Greenwich Mean Time", - 'shortname' => 'GMT', - 'hasdst' => false ), - 'Africa/Accra' => array( - 'offset' => 0, - 'longname' => "Greenwich Mean Time", - 'shortname' => 'GMT', - 'hasdst' => false ), - 'Africa/Bamako' => array( - 'offset' => 0, - 'longname' => "Greenwich Mean Time", - 'shortname' => 'GMT', - 'hasdst' => false ), - 'Africa/Banjul' => array( - 'offset' => 0, - 'longname' => "Greenwich Mean Time", - 'shortname' => 'GMT', - 'hasdst' => false ), - 'Africa/Bissau' => array( - 'offset' => 0, - 'longname' => "Greenwich Mean Time", - 'shortname' => 'GMT', - 'hasdst' => false ), - 'Africa/Casablanca' => array( - 'offset' => 0, - 'longname' => "Western European Time", - 'shortname' => 'WET', - 'hasdst' => false ), - 'Africa/Conakry' => array( - 'offset' => 0, - 'longname' => "Greenwich Mean Time", - 'shortname' => 'GMT', - 'hasdst' => false ), - 'Africa/Dakar' => array( - 'offset' => 0, - 'longname' => "Greenwich Mean Time", - 'shortname' => 'GMT', - 'hasdst' => false ), - 'Africa/El_Aaiun' => array( - 'offset' => 0, - 'longname' => "Western European Time", - 'shortname' => 'WET', - 'hasdst' => false ), - 'Africa/Freetown' => array( - 'offset' => 0, - 'longname' => "Greenwich Mean Time", - 'shortname' => 'GMT', - 'hasdst' => false ), - 'Africa/Lome' => array( - 'offset' => 0, - 'longname' => "Greenwich Mean Time", - 'shortname' => 'GMT', - 'hasdst' => false ), - 'Africa/Monrovia' => array( - 'offset' => 0, - 'longname' => "Greenwich Mean Time", - 'shortname' => 'GMT', - 'hasdst' => false ), - 'Africa/Nouakchott' => array( - 'offset' => 0, - 'longname' => "Greenwich Mean Time", - 'shortname' => 'GMT', - 'hasdst' => false ), - 'Africa/Ouagadougou' => array( - 'offset' => 0, - 'longname' => "Greenwich Mean Time", - 'shortname' => 'GMT', - 'hasdst' => false ), - 'Africa/Sao_Tome' => array( - 'offset' => 0, - 'longname' => "Greenwich Mean Time", - 'shortname' => 'GMT', - 'hasdst' => false ), - 'Africa/Timbuktu' => array( - 'offset' => 0, - 'longname' => "Greenwich Mean Time", - 'shortname' => 'GMT', - 'hasdst' => false ), - 'America/Danmarkshavn' => array( - 'offset' => 0, - 'longname' => "Greenwich Mean Time", - 'shortname' => 'GMT', - 'hasdst' => false ), - 'Atlantic/Canary' => array( - 'offset' => 0, - 'longname' => "Western European Time", - 'shortname' => 'WET', - 'hasdst' => true, - 'dstlongname' => "Western European Summer Time", - 'dstshortname' => 'WEST' ), - 'Atlantic/Faeroe' => array( - 'offset' => 0, - 'longname' => "Western European Time", - 'shortname' => 'WET', - 'hasdst' => true, - 'dstlongname' => "Western European Summer Time", - 'dstshortname' => 'WEST' ), - 'Atlantic/Madeira' => array( - 'offset' => 0, - 'longname' => "Western European Time", - 'shortname' => 'WET', - 'hasdst' => true, - 'dstlongname' => "Western European Summer Time", - 'dstshortname' => 'WEST' ), - 'Atlantic/Reykjavik' => array( - 'offset' => 0, - 'longname' => "Greenwich Mean Time", - 'shortname' => 'GMT', - 'hasdst' => false ), - 'Atlantic/St_Helena' => array( - 'offset' => 0, - 'longname' => "Greenwich Mean Time", - 'shortname' => 'GMT', - 'hasdst' => false ), - 'Eire' => array( - 'offset' => 0, - 'longname' => "Greenwich Mean Time", - 'shortname' => 'GMT', - 'hasdst' => true, - 'dstlongname' => "Irish Summer Time", - 'dstshortname' => 'IST' ), - 'Etc/GMT' => array( - 'offset' => 0, - 'longname' => "GMT+00:00", - 'shortname' => 'GMT+00:00', - 'hasdst' => false ), - 'Etc/GMT+0' => array( - 'offset' => 0, - 'longname' => "GMT+00:00", - 'shortname' => 'GMT+00:00', - 'hasdst' => false ), - 'Etc/GMT-0' => array( - 'offset' => 0, - 'longname' => "GMT+00:00", - 'shortname' => 'GMT+00:00', - 'hasdst' => false ), - 'Etc/GMT0' => array( - 'offset' => 0, - 'longname' => "GMT+00:00", - 'shortname' => 'GMT+00:00', - 'hasdst' => false ), - 'Etc/Greenwich' => array( - 'offset' => 0, - 'longname' => "Greenwich Mean Time", - 'shortname' => 'GMT', - 'hasdst' => false ), - 'Etc/UCT' => array( - 'offset' => 0, - 'longname' => "Coordinated Universal Time", - 'shortname' => 'UTC', - 'hasdst' => false ), - 'Etc/UTC' => array( - 'offset' => 0, - 'longname' => "Coordinated Universal Time", - 'shortname' => 'UTC', - 'hasdst' => false ), - 'Etc/Universal' => array( - 'offset' => 0, - 'longname' => "Coordinated Universal Time", - 'shortname' => 'UTC', - 'hasdst' => false ), - 'Etc/Zulu' => array( - 'offset' => 0, - 'longname' => "Coordinated Universal Time", - 'shortname' => 'UTC', - 'hasdst' => false ), - 'Europe/Belfast' => array( - 'offset' => 0, - 'longname' => "Greenwich Mean Time", - 'shortname' => 'GMT', - 'hasdst' => true, - 'dstlongname' => "British Summer Time", - 'dstshortname' => 'BST' ), - 'Europe/Dublin' => array( - 'offset' => 0, - 'longname' => "Greenwich Mean Time", - 'shortname' => 'GMT', - 'hasdst' => true, - 'dstlongname' => "Irish Summer Time", - 'dstshortname' => 'IST' ), - 'Europe/Lisbon' => array( - 'offset' => 0, - 'longname' => "Western European Time", - 'shortname' => 'WET', - 'hasdst' => true, - 'dstlongname' => "Western European Summer Time", - 'dstshortname' => 'WEST' ), - 'Europe/London' => array( - 'offset' => 0, - 'longname' => "Greenwich Mean Time", - 'shortname' => 'GMT', - 'hasdst' => true, - 'dstlongname' => "British Summer Time", - 'dstshortname' => 'BST' ), - 'GB' => array( - 'offset' => 0, - 'longname' => "Greenwich Mean Time", - 'shortname' => 'GMT', - 'hasdst' => true, - 'dstlongname' => "British Summer Time", - 'dstshortname' => 'BST' ), - 'GB-Eire' => array( - 'offset' => 0, - 'longname' => "Greenwich Mean Time", - 'shortname' => 'GMT', - 'hasdst' => true, - 'dstlongname' => "British Summer Time", - 'dstshortname' => 'BST' ), - 'GMT' => array( - 'offset' => 0, - 'longname' => "Greenwich Mean Time", - 'shortname' => 'GMT', - 'hasdst' => false ), - 'GMT0' => array( - 'offset' => 0, - 'longname' => "GMT+00:00", - 'shortname' => 'GMT+00:00', - 'hasdst' => false ), - 'Greenwich' => array( - 'offset' => 0, - 'longname' => "Greenwich Mean Time", - 'shortname' => 'GMT', - 'hasdst' => false ), - 'Iceland' => array( - 'offset' => 0, - 'longname' => "Greenwich Mean Time", - 'shortname' => 'GMT', - 'hasdst' => false ), - 'Portugal' => array( - 'offset' => 0, - 'longname' => "Western European Time", - 'shortname' => 'WET', - 'hasdst' => true, - 'dstlongname' => "Western European Summer Time", - 'dstshortname' => 'WEST' ), - 'UCT' => array( - 'offset' => 0, - 'longname' => "Coordinated Universal Time", - 'shortname' => 'UTC', - 'hasdst' => false ), - 'UTC' => array( - 'offset' => 0, - 'longname' => "Coordinated Universal Time", - 'shortname' => 'UTC', - 'hasdst' => false ), - 'Universal' => array( - 'offset' => 0, - 'longname' => "Coordinated Universal Time", - 'shortname' => 'UTC', - 'hasdst' => false ), - 'WET' => array( - 'offset' => 0, - 'longname' => "Western European Time", - 'shortname' => 'WET', - 'hasdst' => true, - 'dstlongname' => "Western European Summer Time", - 'dstshortname' => 'WEST' ), - 'Zulu' => array( - 'offset' => 0, - 'longname' => "Coordinated Universal Time", - 'shortname' => 'UTC', - 'hasdst' => false ), - 'Africa/Algiers' => array( - 'offset' => 3600000, - 'longname' => "Central European Time", - 'shortname' => 'CET', - 'hasdst' => false ), - 'Africa/Bangui' => array( - 'offset' => 3600000, - 'longname' => "Western African Time", - 'shortname' => 'WAT', - 'hasdst' => false ), - 'Africa/Brazzaville' => array( - 'offset' => 3600000, - 'longname' => "Western African Time", - 'shortname' => 'WAT', - 'hasdst' => false ), - 'Africa/Ceuta' => array( - 'offset' => 3600000, - 'longname' => "Central European Time", - 'shortname' => 'CET', - 'hasdst' => true, - 'dstlongname' => "Central European Summer Time", - 'dstshortname' => 'CEST' ), - 'Africa/Douala' => array( - 'offset' => 3600000, - 'longname' => "Western African Time", - 'shortname' => 'WAT', - 'hasdst' => false ), - 'Africa/Kinshasa' => array( - 'offset' => 3600000, - 'longname' => "Western African Time", - 'shortname' => 'WAT', - 'hasdst' => false ), - 'Africa/Lagos' => array( - 'offset' => 3600000, - 'longname' => "Western African Time", - 'shortname' => 'WAT', - 'hasdst' => false ), - 'Africa/Libreville' => array( - 'offset' => 3600000, - 'longname' => "Western African Time", - 'shortname' => 'WAT', - 'hasdst' => false ), - 'Africa/Luanda' => array( - 'offset' => 3600000, - 'longname' => "Western African Time", - 'shortname' => 'WAT', - 'hasdst' => false ), - 'Africa/Malabo' => array( - 'offset' => 3600000, - 'longname' => "Western African Time", - 'shortname' => 'WAT', - 'hasdst' => false ), - 'Africa/Ndjamena' => array( - 'offset' => 3600000, - 'longname' => "Western African Time", - 'shortname' => 'WAT', - 'hasdst' => false ), - 'Africa/Niamey' => array( - 'offset' => 3600000, - 'longname' => "Western African Time", - 'shortname' => 'WAT', - 'hasdst' => false ), - 'Africa/Porto-Novo' => array( - 'offset' => 3600000, - 'longname' => "Western African Time", - 'shortname' => 'WAT', - 'hasdst' => false ), - 'Africa/Tunis' => array( - 'offset' => 3600000, - 'longname' => "Central European Time", - 'shortname' => 'CET', - 'hasdst' => false ), - 'Africa/Windhoek' => array( - 'offset' => 3600000, - 'longname' => "Western African Time", - 'shortname' => 'WAT', - 'hasdst' => true, - 'dstlongname' => "Western African Summer Time", - 'dstshortname' => 'WAST' ), - 'Arctic/Longyearbyen' => array( - 'offset' => 3600000, - 'longname' => "Central European Time", - 'shortname' => 'CET', - 'hasdst' => true, - 'dstlongname' => "Central European Summer Time", - 'dstshortname' => 'CEST' ), - 'Atlantic/Jan_Mayen' => array( - 'offset' => 3600000, - 'longname' => "Eastern Greenland Time", - 'shortname' => 'EGT', - 'hasdst' => true, - 'dstlongname' => "Eastern Greenland Summer Time", - 'dstshortname' => 'EGST' ), - 'CET' => array( - 'offset' => 3600000, - 'longname' => "Central European Time", - 'shortname' => 'CET', - 'hasdst' => true, - 'dstlongname' => "Central European Summer Time", - 'dstshortname' => 'CEST' ), - 'ECT' => array( - 'offset' => 3600000, - 'longname' => "Central European Time", - 'shortname' => 'CET', - 'hasdst' => true, - 'dstlongname' => "Central European Summer Time", - 'dstshortname' => 'CEST' ), - 'Etc/GMT-1' => array( - 'offset' => 3600000, - 'longname' => "GMT+01:00", - 'shortname' => 'GMT+01:00', - 'hasdst' => false ), - 'Europe/Amsterdam' => array( - 'offset' => 3600000, - 'longname' => "Central European Time", - 'shortname' => 'CET', - 'hasdst' => true, - 'dstlongname' => "Central European Summer Time", - 'dstshortname' => 'CEST' ), - 'Europe/Andorra' => array( - 'offset' => 3600000, - 'longname' => "Central European Time", - 'shortname' => 'CET', - 'hasdst' => true, - 'dstlongname' => "Central European Summer Time", - 'dstshortname' => 'CEST' ), - 'Europe/Belgrade' => array( - 'offset' => 3600000, - 'longname' => "Central European Time", - 'shortname' => 'CET', - 'hasdst' => true, - 'dstlongname' => "Central European Summer Time", - 'dstshortname' => 'CEST' ), - 'Europe/Berlin' => array( - 'offset' => 3600000, - 'longname' => "Central European Time", - 'shortname' => 'CET', - 'hasdst' => true, - 'dstlongname' => "Central European Summer Time", - 'dstshortname' => 'CEST' ), - 'Europe/Bratislava' => array( - 'offset' => 3600000, - 'longname' => "Central European Time", - 'shortname' => 'CET', - 'hasdst' => true, - 'dstlongname' => "Central European Summer Time", - 'dstshortname' => 'CEST' ), - 'Europe/Brussels' => array( - 'offset' => 3600000, - 'longname' => "Central European Time", - 'shortname' => 'CET', - 'hasdst' => true, - 'dstlongname' => "Central European Summer Time", - 'dstshortname' => 'CEST' ), - 'Europe/Budapest' => array( - 'offset' => 3600000, - 'longname' => "Central European Time", - 'shortname' => 'CET', - 'hasdst' => true, - 'dstlongname' => "Central European Summer Time", - 'dstshortname' => 'CEST' ), - 'Europe/Copenhagen' => array( - 'offset' => 3600000, - 'longname' => "Central European Time", - 'shortname' => 'CET', - 'hasdst' => true, - 'dstlongname' => "Central European Summer Time", - 'dstshortname' => 'CEST' ), - 'Europe/Gibraltar' => array( - 'offset' => 3600000, - 'longname' => "Central European Time", - 'shortname' => 'CET', - 'hasdst' => true, - 'dstlongname' => "Central European Summer Time", - 'dstshortname' => 'CEST' ), - 'Europe/Ljubljana' => array( - 'offset' => 3600000, - 'longname' => "Central European Time", - 'shortname' => 'CET', - 'hasdst' => true, - 'dstlongname' => "Central European Summer Time", - 'dstshortname' => 'CEST' ), - 'Europe/Luxembourg' => array( - 'offset' => 3600000, - 'longname' => "Central European Time", - 'shortname' => 'CET', - 'hasdst' => true, - 'dstlongname' => "Central European Summer Time", - 'dstshortname' => 'CEST' ), - 'Europe/Madrid' => array( - 'offset' => 3600000, - 'longname' => "Central European Time", - 'shortname' => 'CET', - 'hasdst' => true, - 'dstlongname' => "Central European Summer Time", - 'dstshortname' => 'CEST' ), - 'Europe/Malta' => array( - 'offset' => 3600000, - 'longname' => "Central European Time", - 'shortname' => 'CET', - 'hasdst' => true, - 'dstlongname' => "Central European Summer Time", - 'dstshortname' => 'CEST' ), - 'Europe/Monaco' => array( - 'offset' => 3600000, - 'longname' => "Central European Time", - 'shortname' => 'CET', - 'hasdst' => true, - 'dstlongname' => "Central European Summer Time", - 'dstshortname' => 'CEST' ), - 'Europe/Oslo' => array( - 'offset' => 3600000, - 'longname' => "Central European Time", - 'shortname' => 'CET', - 'hasdst' => true, - 'dstlongname' => "Central European Summer Time", - 'dstshortname' => 'CEST' ), - 'Europe/Paris' => array( - 'offset' => 3600000, - 'longname' => "Central European Time", - 'shortname' => 'CET', - 'hasdst' => true, - 'dstlongname' => "Central European Summer Time", - 'dstshortname' => 'CEST' ), - 'Europe/Prague' => array( - 'offset' => 3600000, - 'longname' => "Central European Time", - 'shortname' => 'CET', - 'hasdst' => true, - 'dstlongname' => "Central European Summer Time", - 'dstshortname' => 'CEST' ), - 'Europe/Rome' => array( - 'offset' => 3600000, - 'longname' => "Central European Time", - 'shortname' => 'CET', - 'hasdst' => true, - 'dstlongname' => "Central European Summer Time", - 'dstshortname' => 'CEST' ), - 'Europe/San_Marino' => array( - 'offset' => 3600000, - 'longname' => "Central European Time", - 'shortname' => 'CET', - 'hasdst' => true, - 'dstlongname' => "Central European Summer Time", - 'dstshortname' => 'CEST' ), - 'Europe/Sarajevo' => array( - 'offset' => 3600000, - 'longname' => "Central European Time", - 'shortname' => 'CET', - 'hasdst' => true, - 'dstlongname' => "Central European Summer Time", - 'dstshortname' => 'CEST' ), - 'Europe/Skopje' => array( - 'offset' => 3600000, - 'longname' => "Central European Time", - 'shortname' => 'CET', - 'hasdst' => true, - 'dstlongname' => "Central European Summer Time", - 'dstshortname' => 'CEST' ), - 'Europe/Stockholm' => array( - 'offset' => 3600000, - 'longname' => "Central European Time", - 'shortname' => 'CET', - 'hasdst' => true, - 'dstlongname' => "Central European Summer Time", - 'dstshortname' => 'CEST' ), - 'Europe/Tirane' => array( - 'offset' => 3600000, - 'longname' => "Central European Time", - 'shortname' => 'CET', - 'hasdst' => true, - 'dstlongname' => "Central European Summer Time", - 'dstshortname' => 'CEST' ), - 'Europe/Vaduz' => array( - 'offset' => 3600000, - 'longname' => "Central European Time", - 'shortname' => 'CET', - 'hasdst' => true, - 'dstlongname' => "Central European Summer Time", - 'dstshortname' => 'CEST' ), - 'Europe/Vatican' => array( - 'offset' => 3600000, - 'longname' => "Central European Time", - 'shortname' => 'CET', - 'hasdst' => true, - 'dstlongname' => "Central European Summer Time", - 'dstshortname' => 'CEST' ), - 'Europe/Vienna' => array( - 'offset' => 3600000, - 'longname' => "Central European Time", - 'shortname' => 'CET', - 'hasdst' => true, - 'dstlongname' => "Central European Summer Time", - 'dstshortname' => 'CEST' ), - 'Europe/Warsaw' => array( - 'offset' => 3600000, - 'longname' => "Central European Time", - 'shortname' => 'CET', - 'hasdst' => true, - 'dstlongname' => "Central European Summer Time", - 'dstshortname' => 'CEST' ), - 'Europe/Zagreb' => array( - 'offset' => 3600000, - 'longname' => "Central European Time", - 'shortname' => 'CET', - 'hasdst' => true, - 'dstlongname' => "Central European Summer Time", - 'dstshortname' => 'CEST' ), - 'Europe/Zurich' => array( - 'offset' => 3600000, - 'longname' => "Central European Time", - 'shortname' => 'CET', - 'hasdst' => true, - 'dstlongname' => "Central European Summer Time", - 'dstshortname' => 'CEST' ), - 'MET' => array( - 'offset' => 3600000, - 'longname' => "Middle Europe Time", - 'shortname' => 'MET', - 'hasdst' => true, - 'dstlongname' => "Middle Europe Summer Time", - 'dstshortname' => 'MEST' ), - 'Poland' => array( - 'offset' => 3600000, - 'longname' => "Central European Time", - 'shortname' => 'CET', - 'hasdst' => true, - 'dstlongname' => "Central European Summer Time", - 'dstshortname' => 'CEST' ), - 'ART' => array( - 'offset' => 7200000, - 'longname' => "Eastern European Time", - 'shortname' => 'EET', - 'hasdst' => true, - 'dstlongname' => "Eastern European Summer Time", - 'dstshortname' => 'EEST' ), - 'Africa/Blantyre' => array( - 'offset' => 7200000, - 'longname' => "Central African Time", - 'shortname' => 'CAT', - 'hasdst' => false ), - 'Africa/Bujumbura' => array( - 'offset' => 7200000, - 'longname' => "Central African Time", - 'shortname' => 'CAT', - 'hasdst' => false ), - 'Africa/Cairo' => array( - 'offset' => 7200000, - 'longname' => "Eastern European Time", - 'shortname' => 'EET', - 'hasdst' => true, - 'dstlongname' => "Eastern European Summer Time", - 'dstshortname' => 'EEST' ), - 'Africa/Gaborone' => array( - 'offset' => 7200000, - 'longname' => "Central African Time", - 'shortname' => 'CAT', - 'hasdst' => false ), - 'Africa/Harare' => array( - 'offset' => 7200000, - 'longname' => "Central African Time", - 'shortname' => 'CAT', - 'hasdst' => false ), - 'Africa/Johannesburg' => array( - 'offset' => 7200000, - 'longname' => "South Africa Standard Time", - 'shortname' => 'SAST', - 'hasdst' => false ), - 'Africa/Kigali' => array( - 'offset' => 7200000, - 'longname' => "Central African Time", - 'shortname' => 'CAT', - 'hasdst' => false ), - 'Africa/Lubumbashi' => array( - 'offset' => 7200000, - 'longname' => "Central African Time", - 'shortname' => 'CAT', - 'hasdst' => false ), - 'Africa/Lusaka' => array( - 'offset' => 7200000, - 'longname' => "Central African Time", - 'shortname' => 'CAT', - 'hasdst' => false ), - 'Africa/Maputo' => array( - 'offset' => 7200000, - 'longname' => "Central African Time", - 'shortname' => 'CAT', - 'hasdst' => false ), - 'Africa/Maseru' => array( - 'offset' => 7200000, - 'longname' => "South Africa Standard Time", - 'shortname' => 'SAST', - 'hasdst' => false ), - 'Africa/Mbabane' => array( - 'offset' => 7200000, - 'longname' => "South Africa Standard Time", - 'shortname' => 'SAST', - 'hasdst' => false ), - 'Africa/Tripoli' => array( - 'offset' => 7200000, - 'longname' => "Eastern European Time", - 'shortname' => 'EET', - 'hasdst' => false ), - 'Asia/Amman' => array( - 'offset' => 7200000, - 'longname' => "Eastern European Time", - 'shortname' => 'EET', - 'hasdst' => true, - 'dstlongname' => "Eastern European Summer Time", - 'dstshortname' => 'EEST' ), - 'Asia/Beirut' => array( - 'offset' => 7200000, - 'longname' => "Eastern European Time", - 'shortname' => 'EET', - 'hasdst' => true, - 'dstlongname' => "Eastern European Summer Time", - 'dstshortname' => 'EEST' ), - 'Asia/Damascus' => array( - 'offset' => 7200000, - 'longname' => "Eastern European Time", - 'shortname' => 'EET', - 'hasdst' => true, - 'dstlongname' => "Eastern European Summer Time", - 'dstshortname' => 'EEST' ), - 'Asia/Gaza' => array( - 'offset' => 7200000, - 'longname' => "Eastern European Time", - 'shortname' => 'EET', - 'hasdst' => true, - 'dstlongname' => "Eastern European Summer Time", - 'dstshortname' => 'EEST' ), - 'Asia/Istanbul' => array( - 'offset' => 7200000, - 'longname' => "Eastern European Time", - 'shortname' => 'EET', - 'hasdst' => true, - 'dstlongname' => "Eastern European Summer Time", - 'dstshortname' => 'EEST' ), - 'Asia/Jerusalem' => array( - 'offset' => 7200000, - 'longname' => "Israel Standard Time", - 'shortname' => 'IST', - 'hasdst' => true, - 'dstlongname' => "Israel Daylight Time", - 'dstshortname' => 'IDT' ), - 'Asia/Nicosia' => array( - 'offset' => 7200000, - 'longname' => "Eastern European Time", - 'shortname' => 'EET', - 'hasdst' => true, - 'dstlongname' => "Eastern European Summer Time", - 'dstshortname' => 'EEST' ), - 'Asia/Tel_Aviv' => array( - 'offset' => 7200000, - 'longname' => "Israel Standard Time", - 'shortname' => 'IST', - 'hasdst' => true, - 'dstlongname' => "Israel Daylight Time", - 'dstshortname' => 'IDT' ), - 'CAT' => array( - 'offset' => 7200000, - 'longname' => "Central African Time", - 'shortname' => 'CAT', - 'hasdst' => false ), - 'EET' => array( - 'offset' => 7200000, - 'longname' => "Eastern European Time", - 'shortname' => 'EET', - 'hasdst' => true, - 'dstlongname' => "Eastern European Summer Time", - 'dstshortname' => 'EEST' ), - 'Egypt' => array( - 'offset' => 7200000, - 'longname' => "Eastern European Time", - 'shortname' => 'EET', - 'hasdst' => true, - 'dstlongname' => "Eastern European Summer Time", - 'dstshortname' => 'EEST' ), - 'Etc/GMT-2' => array( - 'offset' => 7200000, - 'longname' => "GMT+02:00", - 'shortname' => 'GMT+02:00', - 'hasdst' => false ), - 'Europe/Athens' => array( - 'offset' => 7200000, - 'longname' => "Eastern European Time", - 'shortname' => 'EET', - 'hasdst' => true, - 'dstlongname' => "Eastern European Summer Time", - 'dstshortname' => 'EEST' ), - 'Europe/Bucharest' => array( - 'offset' => 7200000, - 'longname' => "Eastern European Time", - 'shortname' => 'EET', - 'hasdst' => true, - 'dstlongname' => "Eastern European Summer Time", - 'dstshortname' => 'EEST' ), - 'Europe/Chisinau' => array( - 'offset' => 7200000, - 'longname' => "Eastern European Time", - 'shortname' => 'EET', - 'hasdst' => true, - 'dstlongname' => "Eastern European Summer Time", - 'dstshortname' => 'EEST' ), - 'Europe/Helsinki' => array( - 'offset' => 7200000, - 'longname' => "Eastern European Time", - 'shortname' => 'EET', - 'hasdst' => true, - 'dstlongname' => "Eastern European Summer Time", - 'dstshortname' => 'EEST' ), - 'Europe/Istanbul' => array( - 'offset' => 7200000, - 'longname' => "Eastern European Time", - 'shortname' => 'EET', - 'hasdst' => true, - 'dstlongname' => "Eastern European Summer Time", - 'dstshortname' => 'EEST' ), - 'Europe/Kaliningrad' => array( - 'offset' => 7200000, - 'longname' => "Eastern European Time", - 'shortname' => 'EET', - 'hasdst' => true, - 'dstlongname' => "Eastern European Summer Time", - 'dstshortname' => 'EEST' ), - 'Europe/Kiev' => array( - 'offset' => 7200000, - 'longname' => "Eastern European Time", - 'shortname' => 'EET', - 'hasdst' => true, - 'dstlongname' => "Eastern European Summer Time", - 'dstshortname' => 'EEST' ), - 'Europe/Minsk' => array( - 'offset' => 7200000, - 'longname' => "Eastern European Time", - 'shortname' => 'EET', - 'hasdst' => true, - 'dstlongname' => "Eastern European Summer Time", - 'dstshortname' => 'EEST' ), - 'Europe/Nicosia' => array( - 'offset' => 7200000, - 'longname' => "Eastern European Time", - 'shortname' => 'EET', - 'hasdst' => true, - 'dstlongname' => "Eastern European Summer Time", - 'dstshortname' => 'EEST' ), - 'Europe/Riga' => array( - 'offset' => 7200000, - 'longname' => "Eastern European Time", - 'shortname' => 'EET', - 'hasdst' => true, - 'dstlongname' => "Eastern European Summer Time", - 'dstshortname' => 'EEST' ), - 'Europe/Simferopol' => array( - 'offset' => 7200000, - 'longname' => "Eastern European Time", - 'shortname' => 'EET', - 'hasdst' => true, - 'dstlongname' => "Eastern European Summer Time", - 'dstshortname' => 'EEST' ), - 'Europe/Sofia' => array( - 'offset' => 7200000, - 'longname' => "Eastern European Time", - 'shortname' => 'EET', - 'hasdst' => true, - 'dstlongname' => "Eastern European Summer Time", - 'dstshortname' => 'EEST' ), - 'Europe/Tallinn' => array( - 'offset' => 7200000, - 'longname' => "Eastern European Time", - 'shortname' => 'EET', - 'hasdst' => false ), - 'Europe/Tiraspol' => array( - 'offset' => 7200000, - 'longname' => "Eastern European Time", - 'shortname' => 'EET', - 'hasdst' => true, - 'dstlongname' => "Eastern European Summer Time", - 'dstshortname' => 'EEST' ), - 'Europe/Uzhgorod' => array( - 'offset' => 7200000, - 'longname' => "Eastern European Time", - 'shortname' => 'EET', - 'hasdst' => true, - 'dstlongname' => "Eastern European Summer Time", - 'dstshortname' => 'EEST' ), - 'Europe/Vilnius' => array( - 'offset' => 7200000, - 'longname' => "Eastern European Time", - 'shortname' => 'EET', - 'hasdst' => false ), - 'Europe/Zaporozhye' => array( - 'offset' => 7200000, - 'longname' => "Eastern European Time", - 'shortname' => 'EET', - 'hasdst' => true, - 'dstlongname' => "Eastern European Summer Time", - 'dstshortname' => 'EEST' ), - 'Israel' => array( - 'offset' => 7200000, - 'longname' => "Israel Standard Time", - 'shortname' => 'IST', - 'hasdst' => true, - 'dstlongname' => "Israel Daylight Time", - 'dstshortname' => 'IDT' ), - 'Libya' => array( - 'offset' => 7200000, - 'longname' => "Eastern European Time", - 'shortname' => 'EET', - 'hasdst' => false ), - 'Turkey' => array( - 'offset' => 7200000, - 'longname' => "Eastern European Time", - 'shortname' => 'EET', - 'hasdst' => true, - 'dstlongname' => "Eastern European Summer Time", - 'dstshortname' => 'EEST' ), - 'Africa/Addis_Ababa' => array( - 'offset' => 10800000, - 'longname' => "Eastern African Time", - 'shortname' => 'EAT', - 'hasdst' => false ), - 'Africa/Asmera' => array( - 'offset' => 10800000, - 'longname' => "Eastern African Time", - 'shortname' => 'EAT', - 'hasdst' => false ), - 'Africa/Dar_es_Salaam' => array( - 'offset' => 10800000, - 'longname' => "Eastern African Time", - 'shortname' => 'EAT', - 'hasdst' => false ), - 'Africa/Djibouti' => array( - 'offset' => 10800000, - 'longname' => "Eastern African Time", - 'shortname' => 'EAT', - 'hasdst' => false ), - 'Africa/Kampala' => array( - 'offset' => 10800000, - 'longname' => "Eastern African Time", - 'shortname' => 'EAT', - 'hasdst' => false ), - 'Africa/Khartoum' => array( - 'offset' => 10800000, - 'longname' => "Eastern African Time", - 'shortname' => 'EAT', - 'hasdst' => false ), - 'Africa/Mogadishu' => array( - 'offset' => 10800000, - 'longname' => "Eastern African Time", - 'shortname' => 'EAT', - 'hasdst' => false ), - 'Africa/Nairobi' => array( - 'offset' => 10800000, - 'longname' => "Eastern African Time", - 'shortname' => 'EAT', - 'hasdst' => false ), - 'Antarctica/Syowa' => array( - 'offset' => 10800000, - 'longname' => "Syowa Time", - 'shortname' => 'SYOT', - 'hasdst' => false ), - 'Asia/Aden' => array( - 'offset' => 10800000, - 'longname' => "Arabia Standard Time", - 'shortname' => 'AST', - 'hasdst' => false ), - 'Asia/Baghdad' => array( - 'offset' => 10800000, - 'longname' => "Arabia Standard Time", - 'shortname' => 'AST', - 'hasdst' => true, - 'dstlongname' => "Arabia Daylight Time", - 'dstshortname' => 'ADT' ), - 'Asia/Bahrain' => array( - 'offset' => 10800000, - 'longname' => "Arabia Standard Time", - 'shortname' => 'AST', - 'hasdst' => false ), - 'Asia/Kuwait' => array( - 'offset' => 10800000, - 'longname' => "Arabia Standard Time", - 'shortname' => 'AST', - 'hasdst' => false ), - 'Asia/Qatar' => array( - 'offset' => 10800000, - 'longname' => "Arabia Standard Time", - 'shortname' => 'AST', - 'hasdst' => false ), - 'Asia/Riyadh' => array( - 'offset' => 10800000, - 'longname' => "Arabia Standard Time", - 'shortname' => 'AST', - 'hasdst' => false ), - 'EAT' => array( - 'offset' => 10800000, - 'longname' => "Eastern African Time", - 'shortname' => 'EAT', - 'hasdst' => false ), - 'Etc/GMT-3' => array( - 'offset' => 10800000, - 'longname' => "GMT+03:00", - 'shortname' => 'GMT+03:00', - 'hasdst' => false ), - 'Europe/Moscow' => array( - 'offset' => 10800000, - 'longname' => "Moscow Standard Time", - 'shortname' => 'MSK', - 'hasdst' => true, - 'dstlongname' => "Moscow Daylight Time", - 'dstshortname' => 'MSD' ), - 'Indian/Antananarivo' => array( - 'offset' => 10800000, - 'longname' => "Eastern African Time", - 'shortname' => 'EAT', - 'hasdst' => false ), - 'Indian/Comoro' => array( - 'offset' => 10800000, - 'longname' => "Eastern African Time", - 'shortname' => 'EAT', - 'hasdst' => false ), - 'Indian/Mayotte' => array( - 'offset' => 10800000, - 'longname' => "Eastern African Time", - 'shortname' => 'EAT', - 'hasdst' => false ), - 'W-SU' => array( - 'offset' => 10800000, - 'longname' => "Moscow Standard Time", - 'shortname' => 'MSK', - 'hasdst' => true, - 'dstlongname' => "Moscow Daylight Time", - 'dstshortname' => 'MSD' ), - 'Asia/Riyadh87' => array( - 'offset' => 11224000, - 'longname' => "GMT+03:07", - 'shortname' => 'GMT+03:07', - 'hasdst' => false ), - 'Asia/Riyadh88' => array( - 'offset' => 11224000, - 'longname' => "GMT+03:07", - 'shortname' => 'GMT+03:07', - 'hasdst' => false ), - 'Asia/Riyadh89' => array( - 'offset' => 11224000, - 'longname' => "GMT+03:07", - 'shortname' => 'GMT+03:07', - 'hasdst' => false ), - 'Mideast/Riyadh87' => array( - 'offset' => 11224000, - 'longname' => "GMT+03:07", - 'shortname' => 'GMT+03:07', - 'hasdst' => false ), - 'Mideast/Riyadh88' => array( - 'offset' => 11224000, - 'longname' => "GMT+03:07", - 'shortname' => 'GMT+03:07', - 'hasdst' => false ), - 'Mideast/Riyadh89' => array( - 'offset' => 11224000, - 'longname' => "GMT+03:07", - 'shortname' => 'GMT+03:07', - 'hasdst' => false ), - 'Asia/Tehran' => array( - 'offset' => 12600000, - 'longname' => "Iran Time", - 'shortname' => 'IRT', - 'hasdst' => true, - 'dstlongname' => "Iran Sumer Time", - 'dstshortname' => 'IRST' ), - 'Iran' => array( - 'offset' => 12600000, - 'longname' => "Iran Time", - 'shortname' => 'IRT', - 'hasdst' => true, - 'dstlongname' => "Iran Sumer Time", - 'dstshortname' => 'IRST' ), - 'Asia/Aqtau' => array( - 'offset' => 14400000, - 'longname' => "Aqtau Time", - 'shortname' => 'AQTT', - 'hasdst' => true, - 'dstlongname' => "Aqtau Summer Time", - 'dstshortname' => 'AQTST' ), - 'Asia/Baku' => array( - 'offset' => 14400000, - 'longname' => "Azerbaijan Time", - 'shortname' => 'AZT', - 'hasdst' => true, - 'dstlongname' => "Azerbaijan Summer Time", - 'dstshortname' => 'AZST' ), - 'Asia/Dubai' => array( - 'offset' => 14400000, - 'longname' => "Gulf Standard Time", - 'shortname' => 'GST', - 'hasdst' => false ), - 'Asia/Muscat' => array( - 'offset' => 14400000, - 'longname' => "Gulf Standard Time", - 'shortname' => 'GST', - 'hasdst' => false ), - 'Asia/Tbilisi' => array( - 'offset' => 14400000, - 'longname' => "Georgia Time", - 'shortname' => 'GET', - 'hasdst' => true, - 'dstlongname' => "Georgia Summer Time", - 'dstshortname' => 'GEST' ), - 'Asia/Yerevan' => array( - 'offset' => 14400000, - 'longname' => "Armenia Time", - 'shortname' => 'AMT', - 'hasdst' => true, - 'dstlongname' => "Armenia Summer Time", - 'dstshortname' => 'AMST' ), - 'Etc/GMT-4' => array( - 'offset' => 14400000, - 'longname' => "GMT+04:00", - 'shortname' => 'GMT+04:00', - 'hasdst' => false ), - 'Europe/Samara' => array( - 'offset' => 14400000, - 'longname' => "Samara Time", - 'shortname' => 'SAMT', - 'hasdst' => true, - 'dstlongname' => "Samara Summer Time", - 'dstshortname' => 'SAMST' ), - 'Indian/Mahe' => array( - 'offset' => 14400000, - 'longname' => "Seychelles Time", - 'shortname' => 'SCT', - 'hasdst' => false ), - 'Indian/Mauritius' => array( - 'offset' => 14400000, - 'longname' => "Mauritius Time", - 'shortname' => 'MUT', - 'hasdst' => false ), - 'Indian/Reunion' => array( - 'offset' => 14400000, - 'longname' => "Reunion Time", - 'shortname' => 'RET', - 'hasdst' => false ), - 'NET' => array( - 'offset' => 14400000, - 'longname' => "Armenia Time", - 'shortname' => 'AMT', - 'hasdst' => true, - 'dstlongname' => "Armenia Summer Time", - 'dstshortname' => 'AMST' ), - 'Asia/Kabul' => array( - 'offset' => 16200000, - 'longname' => "Afghanistan Time", - 'shortname' => 'AFT', - 'hasdst' => false ), - 'Asia/Aqtobe' => array( - 'offset' => 18000000, - 'longname' => "Aqtobe Time", - 'shortname' => 'AQTT', - 'hasdst' => true, - 'dstlongname' => "Aqtobe Summer Time", - 'dstshortname' => 'AQTST' ), - 'Asia/Ashgabat' => array( - 'offset' => 18000000, - 'longname' => "Turkmenistan Time", - 'shortname' => 'TMT', - 'hasdst' => false ), - 'Asia/Ashkhabad' => array( - 'offset' => 18000000, - 'longname' => "Turkmenistan Time", - 'shortname' => 'TMT', - 'hasdst' => false ), - 'Asia/Bishkek' => array( - 'offset' => 18000000, - 'longname' => "Kirgizstan Time", - 'shortname' => 'KGT', - 'hasdst' => true, - 'dstlongname' => "Kirgizstan Summer Time", - 'dstshortname' => 'KGST' ), - 'Asia/Dushanbe' => array( - 'offset' => 18000000, - 'longname' => "Tajikistan Time", - 'shortname' => 'TJT', - 'hasdst' => false ), - 'Asia/Karachi' => array( - 'offset' => 18000000, - 'longname' => "Pakistan Time", - 'shortname' => 'PKT', - 'hasdst' => false ), - 'Asia/Samarkand' => array( - 'offset' => 18000000, - 'longname' => "Turkmenistan Time", - 'shortname' => 'TMT', - 'hasdst' => false ), - 'Asia/Tashkent' => array( - 'offset' => 18000000, - 'longname' => "Uzbekistan Time", - 'shortname' => 'UZT', - 'hasdst' => false ), - 'Asia/Yekaterinburg' => array( - 'offset' => 18000000, - 'longname' => "Yekaterinburg Time", - 'shortname' => 'YEKT', - 'hasdst' => true, - 'dstlongname' => "Yekaterinburg Summer Time", - 'dstshortname' => 'YEKST' ), - 'Etc/GMT-5' => array( - 'offset' => 18000000, - 'longname' => "GMT+05:00", - 'shortname' => 'GMT+05:00', - 'hasdst' => false ), - 'Indian/Kerguelen' => array( - 'offset' => 18000000, - 'longname' => "French Southern & Antarctic Lands Time", - 'shortname' => 'TFT', - 'hasdst' => false ), - 'Indian/Maldives' => array( - 'offset' => 18000000, - 'longname' => "Maldives Time", - 'shortname' => 'MVT', - 'hasdst' => false ), - 'PLT' => array( - 'offset' => 18000000, - 'longname' => "Pakistan Time", - 'shortname' => 'PKT', - 'hasdst' => false ), - 'Asia/Calcutta' => array( - 'offset' => 19800000, - 'longname' => "India Standard Time", - 'shortname' => 'IST', - 'hasdst' => false ), - 'IST' => array( - 'offset' => 19800000, - 'longname' => "India Standard Time", - 'shortname' => 'IST', - 'hasdst' => false ), - 'Asia/Katmandu' => array( - 'offset' => 20700000, - 'longname' => "Nepal Time", - 'shortname' => 'NPT', - 'hasdst' => false ), - 'Antarctica/Mawson' => array( - 'offset' => 21600000, - 'longname' => "Mawson Time", - 'shortname' => 'MAWT', - 'hasdst' => false ), - 'Antarctica/Vostok' => array( - 'offset' => 21600000, - 'longname' => "Vostok time", - 'shortname' => 'VOST', - 'hasdst' => false ), - 'Asia/Almaty' => array( - 'offset' => 21600000, - 'longname' => "Alma-Ata Time", - 'shortname' => 'ALMT', - 'hasdst' => true, - 'dstlongname' => "Alma-Ata Summer Time", - 'dstshortname' => 'ALMST' ), - 'Asia/Colombo' => array( - 'offset' => 21600000, - 'longname' => "Sri Lanka Time", - 'shortname' => 'LKT', - 'hasdst' => false ), - 'Asia/Dacca' => array( - 'offset' => 21600000, - 'longname' => "Bangladesh Time", - 'shortname' => 'BDT', - 'hasdst' => false ), - 'Asia/Dhaka' => array( - 'offset' => 21600000, - 'longname' => "Bangladesh Time", - 'shortname' => 'BDT', - 'hasdst' => false ), - 'Asia/Novosibirsk' => array( - 'offset' => 21600000, - 'longname' => "Novosibirsk Time", - 'shortname' => 'NOVT', - 'hasdst' => true, - 'dstlongname' => "Novosibirsk Summer Time", - 'dstshortname' => 'NOVST' ), - 'Asia/Omsk' => array( - 'offset' => 21600000, - 'longname' => "Omsk Time", - 'shortname' => 'OMST', - 'hasdst' => true, - 'dstlongname' => "Omsk Summer Time", - 'dstshortname' => 'OMSST' ), - 'Asia/Thimbu' => array( - 'offset' => 21600000, - 'longname' => "Bhutan Time", - 'shortname' => 'BTT', - 'hasdst' => false ), - 'Asia/Thimphu' => array( - 'offset' => 21600000, - 'longname' => "Bhutan Time", - 'shortname' => 'BTT', - 'hasdst' => false ), - 'BST' => array( - 'offset' => 21600000, - 'longname' => "Bangladesh Time", - 'shortname' => 'BDT', - 'hasdst' => false ), - 'Etc/GMT-6' => array( - 'offset' => 21600000, - 'longname' => "GMT+06:00", - 'shortname' => 'GMT+06:00', - 'hasdst' => false ), - 'Indian/Chagos' => array( - 'offset' => 21600000, - 'longname' => "Indian Ocean Territory Time", - 'shortname' => 'IOT', - 'hasdst' => false ), - 'Asia/Rangoon' => array( - 'offset' => 23400000, - 'longname' => "Myanmar Time", - 'shortname' => 'MMT', - 'hasdst' => false ), - 'Indian/Cocos' => array( - 'offset' => 23400000, - 'longname' => "Cocos Islands Time", - 'shortname' => 'CCT', - 'hasdst' => false ), - 'Antarctica/Davis' => array( - 'offset' => 25200000, - 'longname' => "Davis Time", - 'shortname' => 'DAVT', - 'hasdst' => false ), - 'Asia/Bangkok' => array( - 'offset' => 25200000, - 'longname' => "Indochina Time", - 'shortname' => 'ICT', - 'hasdst' => false ), - 'Asia/Hovd' => array( - 'offset' => 25200000, - 'longname' => "Hovd Time", - 'shortname' => 'HOVT', - 'hasdst' => false ), - 'Asia/Jakarta' => array( - 'offset' => 25200000, - 'longname' => "West Indonesia Time", - 'shortname' => 'WIT', - 'hasdst' => false ), - 'Asia/Krasnoyarsk' => array( - 'offset' => 25200000, - 'longname' => "Krasnoyarsk Time", - 'shortname' => 'KRAT', - 'hasdst' => true, - 'dstlongname' => "Krasnoyarsk Summer Time", - 'dstshortname' => 'KRAST' ), - 'Asia/Phnom_Penh' => array( - 'offset' => 25200000, - 'longname' => "Indochina Time", - 'shortname' => 'ICT', - 'hasdst' => false ), - 'Asia/Pontianak' => array( - 'offset' => 25200000, - 'longname' => "West Indonesia Time", - 'shortname' => 'WIT', - 'hasdst' => false ), - 'Asia/Saigon' => array( - 'offset' => 25200000, - 'longname' => "Indochina Time", - 'shortname' => 'ICT', - 'hasdst' => false ), - 'Asia/Vientiane' => array( - 'offset' => 25200000, - 'longname' => "Indochina Time", - 'shortname' => 'ICT', - 'hasdst' => false ), - 'Etc/GMT-7' => array( - 'offset' => 25200000, - 'longname' => "GMT+07:00", - 'shortname' => 'GMT+07:00', - 'hasdst' => false ), - 'Indian/Christmas' => array( - 'offset' => 25200000, - 'longname' => "Christmas Island Time", - 'shortname' => 'CXT', - 'hasdst' => false ), - 'VST' => array( - 'offset' => 25200000, - 'longname' => "Indochina Time", - 'shortname' => 'ICT', - 'hasdst' => false ), - 'Antarctica/Casey' => array( - 'offset' => 28800000, - 'longname' => "Western Standard Time (Australia)", - 'shortname' => 'WST', - 'hasdst' => false ), - 'Asia/Brunei' => array( - 'offset' => 28800000, - 'longname' => "Brunei Time", - 'shortname' => 'BNT', - 'hasdst' => false ), - 'Asia/Chongqing' => array( - 'offset' => 28800000, - 'longname' => "China Standard Time", - 'shortname' => 'CST', - 'hasdst' => false ), - 'Asia/Chungking' => array( - 'offset' => 28800000, - 'longname' => "China Standard Time", - 'shortname' => 'CST', - 'hasdst' => false ), - 'Asia/Harbin' => array( - 'offset' => 28800000, - 'longname' => "China Standard Time", - 'shortname' => 'CST', - 'hasdst' => false ), - 'Asia/Hong_Kong' => array( - 'offset' => 28800000, - 'longname' => "Hong Kong Time", - 'shortname' => 'HKT', - 'hasdst' => false ), - 'Asia/Irkutsk' => array( - 'offset' => 28800000, - 'longname' => "Irkutsk Time", - 'shortname' => 'IRKT', - 'hasdst' => true, - 'dstlongname' => "Irkutsk Summer Time", - 'dstshortname' => 'IRKST' ), - 'Asia/Kashgar' => array( - 'offset' => 28800000, - 'longname' => "China Standard Time", - 'shortname' => 'CST', - 'hasdst' => false ), - 'Asia/Kuala_Lumpur' => array( - 'offset' => 28800000, - 'longname' => "Malaysia Time", - 'shortname' => 'MYT', - 'hasdst' => false ), - 'Asia/Kuching' => array( - 'offset' => 28800000, - 'longname' => "Malaysia Time", - 'shortname' => 'MYT', - 'hasdst' => false ), - 'Asia/Macao' => array( - 'offset' => 28800000, - 'longname' => "China Standard Time", - 'shortname' => 'CST', - 'hasdst' => false ), - 'Asia/Manila' => array( - 'offset' => 28800000, - 'longname' => "Philippines Time", - 'shortname' => 'PHT', - 'hasdst' => false ), - 'Asia/Shanghai' => array( - 'offset' => 28800000, - 'longname' => "China Standard Time", - 'shortname' => 'CST', - 'hasdst' => false ), - 'Asia/Singapore' => array( - 'offset' => 28800000, - 'longname' => "Singapore Time", - 'shortname' => 'SGT', - 'hasdst' => false ), - 'Asia/Taipei' => array( - 'offset' => 28800000, - 'longname' => "China Standard Time", - 'shortname' => 'CST', - 'hasdst' => false ), - 'Asia/Ujung_Pandang' => array( - 'offset' => 28800000, - 'longname' => "Central Indonesia Time", - 'shortname' => 'CIT', - 'hasdst' => false ), - 'Asia/Ulaanbaatar' => array( - 'offset' => 28800000, - 'longname' => "Ulaanbaatar Time", - 'shortname' => 'ULAT', - 'hasdst' => false ), - 'Asia/Ulan_Bator' => array( - 'offset' => 28800000, - 'longname' => "Ulaanbaatar Time", - 'shortname' => 'ULAT', - 'hasdst' => false ), - 'Asia/Urumqi' => array( - 'offset' => 28800000, - 'longname' => "China Standard Time", - 'shortname' => 'CST', - 'hasdst' => false ), - 'Australia/Perth' => array( - 'offset' => 28800000, - 'longname' => "Western Standard Time (Australia)", - 'shortname' => 'WST', - 'hasdst' => false ), - 'Australia/West' => array( - 'offset' => 28800000, - 'longname' => "Western Standard Time (Australia)", - 'shortname' => 'WST', - 'hasdst' => false ), - 'CTT' => array( - 'offset' => 28800000, - 'longname' => "China Standard Time", - 'shortname' => 'CST', - 'hasdst' => false ), - 'Etc/GMT-8' => array( - 'offset' => 28800000, - 'longname' => "GMT+08:00", - 'shortname' => 'GMT+08:00', - 'hasdst' => false ), - 'Hongkong' => array( - 'offset' => 28800000, - 'longname' => "Hong Kong Time", - 'shortname' => 'HKT', - 'hasdst' => false ), - 'PRC' => array( - 'offset' => 28800000, - 'longname' => "China Standard Time", - 'shortname' => 'CST', - 'hasdst' => false ), - 'Singapore' => array( - 'offset' => 28800000, - 'longname' => "Singapore Time", - 'shortname' => 'SGT', - 'hasdst' => false ), - 'Asia/Choibalsan' => array( - 'offset' => 32400000, - 'longname' => "Choibalsan Time", - 'shortname' => 'CHOT', - 'hasdst' => false ), - 'Asia/Dili' => array( - 'offset' => 32400000, - 'longname' => "East Timor Time", - 'shortname' => 'TPT', - 'hasdst' => false ), - 'Asia/Jayapura' => array( - 'offset' => 32400000, - 'longname' => "East Indonesia Time", - 'shortname' => 'EIT', - 'hasdst' => false ), - 'Asia/Pyongyang' => array( - 'offset' => 32400000, - 'longname' => "Korea Standard Time", - 'shortname' => 'KST', - 'hasdst' => false ), - 'Asia/Seoul' => array( - 'offset' => 32400000, - 'longname' => "Korea Standard Time", - 'shortname' => 'KST', - 'hasdst' => false ), - 'Asia/Tokyo' => array( - 'offset' => 32400000, - 'longname' => "Japan Standard Time", - 'shortname' => 'JST', - 'hasdst' => false ), - 'Asia/Yakutsk' => array( - 'offset' => 32400000, - 'longname' => "Yakutsk Time", - 'shortname' => 'YAKT', - 'hasdst' => true, - 'dstlongname' => "Yaktsk Summer Time", - 'dstshortname' => 'YAKST' ), - 'Etc/GMT-9' => array( - 'offset' => 32400000, - 'longname' => "GMT+09:00", - 'shortname' => 'GMT+09:00', - 'hasdst' => false ), - 'JST' => array( - 'offset' => 32400000, - 'longname' => "Japan Standard Time", - 'shortname' => 'JST', - 'hasdst' => false ), - 'Japan' => array( - 'offset' => 32400000, - 'longname' => "Japan Standard Time", - 'shortname' => 'JST', - 'hasdst' => false ), - 'Pacific/Palau' => array( - 'offset' => 32400000, - 'longname' => "Palau Time", - 'shortname' => 'PWT', - 'hasdst' => false ), - 'ROK' => array( - 'offset' => 32400000, - 'longname' => "Korea Standard Time", - 'shortname' => 'KST', - 'hasdst' => false ), - 'ACT' => array( - 'offset' => 34200000, - 'longname' => "Central Standard Time (Northern Territory)", - 'shortname' => 'CST', - 'hasdst' => false ), - 'Australia/Adelaide' => array( - 'offset' => 34200000, - 'longname' => "Central Standard Time (South Australia)", - 'shortname' => 'CST', - 'hasdst' => true, - 'dstlongname' => "Central Summer Time (South Australia)", - 'dstshortname' => 'CST' ), - 'Australia/Broken_Hill' => array( - 'offset' => 34200000, - 'longname' => "Central Standard Time (S Australia/NSW)", - 'shortname' => 'CST', - 'hasdst' => true, - 'dstlongname' => "Central Summer Time (S Australia/NSW)", - 'dstshortname' => 'CST' ), - 'Australia/Darwin' => array( - 'offset' => 34200000, - 'longname' => "Central Standard Time (Northern Territory)", - 'shortname' => 'CST', - 'hasdst' => false ), - 'Australia/North' => array( - 'offset' => 34200000, - 'longname' => "Central Standard Time (Northern Territory)", - 'shortname' => 'CST', - 'hasdst' => false ), - 'Australia/South' => array( - 'offset' => 34200000, - 'longname' => "Central Standard Time (South Australia)", - 'shortname' => 'CST', - 'hasdst' => true, - 'dstlongname' => "Central Summer Time (South Australia)", - 'dstshortname' => 'CST' ), - 'Australia/Yancowinna' => array( - 'offset' => 34200000, - 'longname' => "Central Standard Time (S Australia/NSW)", - 'shortname' => 'CST', - 'hasdst' => true, - 'dstlongname' => "Central Summer Time (S Australia/NSW)", - 'dstshortname' => 'CST' ), - 'AET' => array( - 'offset' => 36000000, - 'longname' => "Eastern Standard Time (New South Wales)", - 'shortname' => 'EST', - 'hasdst' => true, - 'dstlongname' => "Eastern Summer Time (New South Wales)", - 'dstshortname' => 'EST' ), - 'Antarctica/DumontDUrville' => array( - 'offset' => 36000000, - 'longname' => "Dumont-d'Urville Time", - 'shortname' => 'DDUT', - 'hasdst' => false ), - 'Asia/Sakhalin' => array( - 'offset' => 36000000, - 'longname' => "Sakhalin Time", - 'shortname' => 'SAKT', - 'hasdst' => true, - 'dstlongname' => "Sakhalin Summer Time", - 'dstshortname' => 'SAKST' ), - 'Asia/Vladivostok' => array( - 'offset' => 36000000, - 'longname' => "Vladivostok Time", - 'shortname' => 'VLAT', - 'hasdst' => true, - 'dstlongname' => "Vladivostok Summer Time", - 'dstshortname' => 'VLAST' ), - 'Australia/ACT' => array( - 'offset' => 36000000, - 'longname' => "Eastern Standard Time (New South Wales)", - 'shortname' => 'EST', - 'hasdst' => true, - 'dstlongname' => "Eastern Summer Time (New South Wales)", - 'dstshortname' => 'EST' ), - 'Australia/Brisbane' => array( - 'offset' => 36000000, - 'longname' => "Eastern Standard Time (Queensland)", - 'shortname' => 'EST', - 'hasdst' => false ), - 'Australia/Canberra' => array( - 'offset' => 36000000, - 'longname' => "Eastern Standard Time (New South Wales)", - 'shortname' => 'EST', - 'hasdst' => true, - 'dstlongname' => "Eastern Summer Time (New South Wales)", - 'dstshortname' => 'EST' ), - 'Australia/Hobart' => array( - 'offset' => 36000000, - 'longname' => "Eastern Standard Time (Tasmania)", - 'shortname' => 'EST', - 'hasdst' => true, - 'dstlongname' => "Eastern Summer Time (Tasmania)", - 'dstshortname' => 'EST' ), - 'Australia/Lindeman' => array( - 'offset' => 36000000, - 'longname' => "Eastern Standard Time (Queensland)", - 'shortname' => 'EST', - 'hasdst' => false ), - 'Australia/Melbourne' => array( - 'offset' => 36000000, - 'longname' => "Eastern Standard Time (Victoria)", - 'shortname' => 'EST', - 'hasdst' => true, - 'dstlongname' => "Eastern Summer Time (Victoria)", - 'dstshortname' => 'EST' ), - 'Australia/NSW' => array( - 'offset' => 36000000, - 'longname' => "Eastern Standard Time (New South Wales)", - 'shortname' => 'EST', - 'hasdst' => true, - 'dstlongname' => "Eastern Summer Time (New South Wales)", - 'dstshortname' => 'EST' ), - 'Australia/Queensland' => array( - 'offset' => 36000000, - 'longname' => "Eastern Standard Time (Queensland)", - 'shortname' => 'EST', - 'hasdst' => false ), - 'Australia/Sydney' => array( - 'offset' => 36000000, - 'longname' => "Eastern Standard Time (New South Wales)", - 'shortname' => 'EST', - 'hasdst' => true, - 'dstlongname' => "Eastern Summer Time (New South Wales)", - 'dstshortname' => 'EST' ), - 'Australia/Tasmania' => array( - 'offset' => 36000000, - 'longname' => "Eastern Standard Time (Tasmania)", - 'shortname' => 'EST', - 'hasdst' => true, - 'dstlongname' => "Eastern Summer Time (Tasmania)", - 'dstshortname' => 'EST' ), - 'Australia/Victoria' => array( - 'offset' => 36000000, - 'longname' => "Eastern Standard Time (Victoria)", - 'shortname' => 'EST', - 'hasdst' => true, - 'dstlongname' => "Eastern Summer Time (Victoria)", - 'dstshortname' => 'EST' ), - 'Etc/GMT-10' => array( - 'offset' => 36000000, - 'longname' => "GMT+10:00", - 'shortname' => 'GMT+10:00', - 'hasdst' => false ), - 'Pacific/Guam' => array( - 'offset' => 36000000, - 'longname' => "Chamorro Standard Time", - 'shortname' => 'ChST', - 'hasdst' => false ), - 'Pacific/Port_Moresby' => array( - 'offset' => 36000000, - 'longname' => "Papua New Guinea Time", - 'shortname' => 'PGT', - 'hasdst' => false ), - 'Pacific/Saipan' => array( - 'offset' => 36000000, - 'longname' => "Chamorro Standard Time", - 'shortname' => 'ChST', - 'hasdst' => false ), - 'Pacific/Truk' => array( - 'offset' => 36000000, - 'longname' => "Truk Time", - 'shortname' => 'TRUT', - 'hasdst' => false ), - 'Pacific/Yap' => array( - 'offset' => 36000000, - 'longname' => "Yap Time", - 'shortname' => 'YAPT', - 'hasdst' => false ), - 'Australia/LHI' => array( - 'offset' => 37800000, - 'longname' => "Load Howe Standard Time", - 'shortname' => 'LHST', - 'hasdst' => true, - 'dstlongname' => "Load Howe Summer Time", - 'dstshortname' => 'LHST' ), - 'Australia/Lord_Howe' => array( - 'offset' => 37800000, - 'longname' => "Load Howe Standard Time", - 'shortname' => 'LHST', - 'hasdst' => true, - 'dstlongname' => "Load Howe Summer Time", - 'dstshortname' => 'LHST' ), - 'Asia/Magadan' => array( - 'offset' => 39600000, - 'longname' => "Magadan Time", - 'shortname' => 'MAGT', - 'hasdst' => true, - 'dstlongname' => "Magadan Summer Time", - 'dstshortname' => 'MAGST' ), - 'Etc/GMT-11' => array( - 'offset' => 39600000, - 'longname' => "GMT+11:00", - 'shortname' => 'GMT+11:00', - 'hasdst' => false ), - 'Pacific/Efate' => array( - 'offset' => 39600000, - 'longname' => "Vanuatu Time", - 'shortname' => 'VUT', - 'hasdst' => false ), - 'Pacific/Guadalcanal' => array( - 'offset' => 39600000, - 'longname' => "Solomon Is. Time", - 'shortname' => 'SBT', - 'hasdst' => false ), - 'Pacific/Kosrae' => array( - 'offset' => 39600000, - 'longname' => "Kosrae Time", - 'shortname' => 'KOST', - 'hasdst' => false ), - 'Pacific/Noumea' => array( - 'offset' => 39600000, - 'longname' => "New Caledonia Time", - 'shortname' => 'NCT', - 'hasdst' => false ), - 'Pacific/Ponape' => array( - 'offset' => 39600000, - 'longname' => "Ponape Time", - 'shortname' => 'PONT', - 'hasdst' => false ), - 'SST' => array( - 'offset' => 39600000, - 'longname' => "Solomon Is. Time", - 'shortname' => 'SBT', - 'hasdst' => false ), - 'Pacific/Norfolk' => array( - 'offset' => 41400000, - 'longname' => "Norfolk Time", - 'shortname' => 'NFT', - 'hasdst' => false ), - 'Antarctica/McMurdo' => array( - 'offset' => 43200000, - 'longname' => "New Zealand Standard Time", - 'shortname' => 'NZST', - 'hasdst' => true, - 'dstlongname' => "New Zealand Daylight Time", - 'dstshortname' => 'NZDT' ), - 'Antarctica/South_Pole' => array( - 'offset' => 43200000, - 'longname' => "New Zealand Standard Time", - 'shortname' => 'NZST', - 'hasdst' => true, - 'dstlongname' => "New Zealand Daylight Time", - 'dstshortname' => 'NZDT' ), - 'Asia/Anadyr' => array( - 'offset' => 43200000, - 'longname' => "Anadyr Time", - 'shortname' => 'ANAT', - 'hasdst' => true, - 'dstlongname' => "Anadyr Summer Time", - 'dstshortname' => 'ANAST' ), - 'Asia/Kamchatka' => array( - 'offset' => 43200000, - 'longname' => "Petropavlovsk-Kamchatski Time", - 'shortname' => 'PETT', - 'hasdst' => true, - 'dstlongname' => "Petropavlovsk-Kamchatski Summer Time", - 'dstshortname' => 'PETST' ), - 'Etc/GMT-12' => array( - 'offset' => 43200000, - 'longname' => "GMT+12:00", - 'shortname' => 'GMT+12:00', - 'hasdst' => false ), - 'Kwajalein' => array( - 'offset' => 43200000, - 'longname' => "Marshall Islands Time", - 'shortname' => 'MHT', - 'hasdst' => false ), - 'NST' => array( - 'offset' => 43200000, - 'longname' => "New Zealand Standard Time", - 'shortname' => 'NZST', - 'hasdst' => true, - 'dstlongname' => "New Zealand Daylight Time", - 'dstshortname' => 'NZDT' ), - 'NZ' => array( - 'offset' => 43200000, - 'longname' => "New Zealand Standard Time", - 'shortname' => 'NZST', - 'hasdst' => true, - 'dstlongname' => "New Zealand Daylight Time", - 'dstshortname' => 'NZDT' ), - 'Pacific/Auckland' => array( - 'offset' => 43200000, - 'longname' => "New Zealand Standard Time", - 'shortname' => 'NZST', - 'hasdst' => true, - 'dstlongname' => "New Zealand Daylight Time", - 'dstshortname' => 'NZDT' ), - 'Pacific/Fiji' => array( - 'offset' => 43200000, - 'longname' => "Fiji Time", - 'shortname' => 'FJT', - 'hasdst' => false ), - 'Pacific/Funafuti' => array( - 'offset' => 43200000, - 'longname' => "Tuvalu Time", - 'shortname' => 'TVT', - 'hasdst' => false ), - 'Pacific/Kwajalein' => array( - 'offset' => 43200000, - 'longname' => "Marshall Islands Time", - 'shortname' => 'MHT', - 'hasdst' => false ), - 'Pacific/Majuro' => array( - 'offset' => 43200000, - 'longname' => "Marshall Islands Time", - 'shortname' => 'MHT', - 'hasdst' => false ), - 'Pacific/Nauru' => array( - 'offset' => 43200000, - 'longname' => "Nauru Time", - 'shortname' => 'NRT', - 'hasdst' => false ), - 'Pacific/Tarawa' => array( - 'offset' => 43200000, - 'longname' => "Gilbert Is. Time", - 'shortname' => 'GILT', - 'hasdst' => false ), - 'Pacific/Wake' => array( - 'offset' => 43200000, - 'longname' => "Wake Time", - 'shortname' => 'WAKT', - 'hasdst' => false ), - 'Pacific/Wallis' => array( - 'offset' => 43200000, - 'longname' => "Wallis & Futuna Time", - 'shortname' => 'WFT', - 'hasdst' => false ), - 'NZ-CHAT' => array( - 'offset' => 45900000, - 'longname' => "Chatham Standard Time", - 'shortname' => 'CHAST', - 'hasdst' => true, - 'dstlongname' => "Chatham Daylight Time", - 'dstshortname' => 'CHADT' ), - 'Pacific/Chatham' => array( - 'offset' => 45900000, - 'longname' => "Chatham Standard Time", - 'shortname' => 'CHAST', - 'hasdst' => true, - 'dstlongname' => "Chatham Daylight Time", - 'dstshortname' => 'CHADT' ), - 'Etc/GMT-13' => array( - 'offset' => 46800000, - 'longname' => "GMT+13:00", - 'shortname' => 'GMT+13:00', - 'hasdst' => false ), - 'Pacific/Enderbury' => array( - 'offset' => 46800000, - 'longname' => "Phoenix Is. Time", - 'shortname' => 'PHOT', - 'hasdst' => false ), - 'Pacific/Tongatapu' => array( - 'offset' => 46800000, - 'longname' => "Tonga Time", - 'shortname' => 'TOT', - 'hasdst' => false ), - 'Etc/GMT-14' => array( - 'offset' => 50400000, - 'longname' => "GMT+14:00", - 'shortname' => 'GMT+14:00', - 'hasdst' => false ), - 'Pacific/Kiritimati' => array( - 'offset' => 50400000, - 'longname' => "Line Is. Time", - 'shortname' => 'LINT', - 'hasdst' => false ) -); -// -// Initialize default timezone -// First try _DATE_TIMEZONE_DEFAULT global, -// then PHP_TZ environment var, then TZ environment var -// -if(isset($_DATE_TIMEZONE_DEFAULT) - && Date_TimeZone::is_validID($_DATE_TIMEZONE_DEFAULT) -) { - Date_TimeZone::setDefault($_DATE_TIMEZONE_DEFAULT); -} elseif (getenv('PHP_TZ') && Date_TimeZone::is_validID(getenv('PHP_TZ'))) { - Date_TimeZone::setDefault(getenv('PHP_TZ')); -} elseif (getenv('TZ') && Date_TimeZone::is_validID(getenv('TZ'))) { - Date_TimeZone::setDefault(getenv('TZ')); -} elseif (Date_TimeZone::is_validID(date('T'))) { - Date_TimeZone::setDefault(date('T')); -} elseif (substr(php_uname(), 0, 7) == "Windows") { - include_once('TimeZoneWindows.php'); - if (isset($_DATE_TIMEZONE_DATA_WINDOWS[date('T')])) { - Date_TimeZone::setDefault($_DATE_TIMEZONE_DATA_WINDOWS[date('T')]); - } else { - Date_TimeZone::setDefault('UTC'); - } -} else { - Date_TimeZone::setDefault('UTC'); -} -// -// END -?> \ No newline at end of file diff --git a/Date/TimeZoneWindows.php b/Date/TimeZoneWindows.php deleted file mode 100644 index d5fdc67..0000000 --- a/Date/TimeZoneWindows.php +++ /dev/null @@ -1,109 +0,0 @@ - | -// | | -// +----------------------------------------------------------------------+ -// -// $Id$ -// -// Date_TimeZone Class Windows Support File -// -/** - * This class includes Windows time zone data (from zoneinfo) in the form of a global array, - * $_DATE_TIMEZONE_DATA_WINDOWS. - * - * @author Ross Smith - * @package Date - * @access public - * @version 1.0 - */ -$GLOBALS['_DATE_TIMEZONE_DATA_WINDOWS'] = array( - 'Dateline Standard Time' => 'Etc/GMT+12', # (GMT-12:00) Eniwetok, Kwajalein Dateline Daylight Time - 'Samoa Standard Time' => 'Pacific/Samoa', # (GMT-11:00) Midway Island, Samoa Samoa Daylight Time - 'Hawaiian Standard Time' => 'HST', # (GMT-10:00) Hawaii Hawaiian Daylight Time - 'Alaskan Standard Time' => 'AST', # (GMT-09:00) Alaska Alaskan Daylight Time - 'Pacific Standard Time' => 'PST', # (GMT-08:00) Pacific Time (US & Canada); Tijuana Pacific Daylight Time - 'Mountain Standard Time' => 'MST', # (GMT-07:00) Mountain Time (US & Canada) Mountain Daylight Time - 'US Mountain Standard Time' => 'US/Mountain', # (GMT-07:00) Arizona US Mountain Daylight Time - 'Canada Central Standard Time' => 'Canada/Central', # (GMT-06:00) Saskatchewan Canada Central Daylight Time - 'Mexico Standard Time' => 'Mexico/General', # (GMT-06:00) Mexico City Mexico Daylight Time - 'Central Standard Time' => 'CST', # (GMT-06:00) Central Time (US & Canada) Central Daylight Time - 'Central America Standard Time' => 'CST', # (GMT-06:00) Central America Central America Daylight Time - 'US Eastern Standard Time' => 'EST', # (GMT-05:00) Indiana (East) US Eastern Daylight Time - 'Eastern Standard Time' => 'EST', # (GMT-05:00) Eastern Time (US & Canada) Eastern Daylight Time - 'SA Pacific Standard Time' => 'EST', # (GMT-05:00) Bogota, Lima, Quito SA Pacific Daylight Time - 'Pacific SA Standard Time' => 'America/Anguilla', # (GMT-04:00) Santiago Pacific SA Daylight Time - 'SA Western Standard Time' => 'America/Anguilla', # (GMT-04:00) Caracas, La Paz SA Western Daylight Time - 'Atlantic Standard Time' => 'America/Anguilla', # (GMT-04:00) Atlantic Time (Canada) Atlantic Daylight Time - 'Newfoundland Standard Time' => 'America/St_Johns', # (GMT-03:30) Newfoundland Newfoundland Daylight Time - 'Greenland Standard Time' => 'America/Godthab', # (GMT-03:00) Greenland Greenland Daylight Time - 'SA Eastern Standard Time' => 'America/Araguaina', # (GMT-03:00) Buenos Aires, Georgetown SA Eastern Daylight Time - 'E. South America Standard Time' => 'America/Araguaina', # (GMT-03:00) Brasilia E. South America Daylight Time - 'Mid-Atlantic Standard Time' => 'Atlantic/South_Georgia', # (GMT-02:00) Mid-Atlantic Mid-Atlantic Daylight Time - 'Cape Verde Standard Time' => 'Atlantic/Cape_Verde', # (GMT-01:00) Cape Verde Is. Cape Verde Daylight Time - 'Azores Standard Time' => 'Atlantic/Azores', # (GMT-01:00) Azores Azores Daylight Time - 'Greenwich Standard Time' => 'GMT', # (GMT+00:00) Casablanca, Monrovia Greenwich Daylight Time - 'GMT Standard Time' => 'GMT', # (GMT+00:00) Greenwich Mean Time : Dublin, Edinburgh, Lisbon, London GMT Daylight Time - 'W. Europe Standard Time' => 'ECT', # (GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna W. Europe Daylight Time - 'Central Europe Standard Time' => 'ECT', # (GMT+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague Central Europe Daylight Time - 'Romance Standard Time' => 'ECT', # (GMT+01:00) Brussels, Copenhagen, Madrid, Paris Romance Daylight Time - 'Central European Standard Time' => 'ECT', # (GMT+01:00) Sarajevo, Skopje, Sofija, Vilnius, Warsaw, Zagreb Central European Daylight Time - 'W. Central Africa Standard Time' => 'ECT', # (GMT+01:00) West Central Africa W. Central Africa Daylight Time - 'GTB Standard Time' => 'ART', # (GMT+02:00) Athens, Istanbul, Minsk GTB Daylight Time - 'E. Europe Standard Time' => 'EET', # (GMT+02:00) Bucharest E. Europe Daylight Time - 'Egypt Standard Time' => 'Egypt', # (GMT+02:00) Cairo Egypt Daylight Time - 'South Africa Standard Time' => 'Africa/Johannesburg', # (GMT+02:00) Harare, Pretoria South Africa Daylight Time - 'FLE Standard Time' => 'ART', # (GMT+02:00) Helsinki, Riga, Tallinn FLE Daylight Time - 'Jerusalem Standard Time' => 'Israel', # (GMT+02:00) Jerusalem Jerusalem Daylight Time - 'Arabic Standard Time' => 'Asia/Aden', # (GMT+03:00) Baghdad Arabic Daylight Time - 'Arab Standard Time' => 'Asia/Riyadh', # (GMT+03:00) Kuwait, Riyadh Arab Daylight Time - 'Russian Standard Time' => 'Europe/Moscow', # (GMT+03:00) Moscow, St. Petersburg, Volgograd Russian Daylight Time - 'E. Africa Standard Time' => 'EAT', # (GMT+03:00) Nairobi E. Africa Daylight Time - 'Iran Standard Time' => 'Asia/Tehran', # (GMT+03:30) Tehran Iran Daylight Time - 'Arabian Standard Time' => 'Asia/Dubai', # (GMT+04:00) Abu Dhabi, Muscat Arabian Daylight Time - 'Caucasus Standard Time' => 'Asia/Baku', # (GMT+04:00) Baku, Tbilisi, Yerevan Caucasus Daylight Time - 'Afghanistan Standard Time' => 'Asia/Kabul', # (GMT+04:30) Kabul Afghanistan Daylight Time - 'Ekaterinburg Standard Time' => 'Asia/Yekaterinburg', # (GMT+05:00) Ekaterinburg Ekaterinburg Daylight Time - 'West Asia Standard Time' => 'PLT', # (GMT+05:00) Islamabad, Karachi, Tashkent West Asia Daylight Time - 'India Standard Time' => 'IST', # (GMT+05:30) Calcutta, Chennai, Mumbai, New Delhi India Daylight Time - 'Nepal Standard Time' => 'Asia/Katmandu', # (GMT+05:45) Kathmandu Nepal Daylight Time - 'N. Central Asia Standard Time' => 'Asia/Novosibirsk', # (GMT+06:00) Almaty, Novosibirsk N. Central Asia Daylight Time - 'Central Asia Standard Time' => 'Asia/Dacca', # (GMT+06:00) Astana, Dhaka Central Asia Daylight Time - 'Sri Lanka Standard Time' => 'Asia/Colombo', # (GMT+06:00) Sri Jayawardenepura Sri Lanka Daylight Time - 'Myanmar Standard Time' => 'Asia/Rangoon', # (GMT+06:30) Rangoon Myanmar Daylight Time - 'SE Asia Standard Time' => 'Asia/Bangkok', # (GMT+07:00) Bangkok, Hanoi, Jakarta SE Asia Daylight Time - 'North Asia Standard Time' => 'Asia/Krasnoyarsk', # (GMT+07:00) Krasnoyarsk North Asia Daylight Time - 'China Standard Time' => 'Asia/Chongqing', # (GMT+08:00) Beijing, Chongqing, Hong Kong, Urumqi China Daylight Time - 'North Asia East Standard Time' => 'Asia/Irkutsk', # (GMT+08:00) Irkutsk, Ulaan Bataar North Asia East Daylight Time - 'Malay Peninsula Standard Time' => 'Asia/Kuala_Lumpur', # (GMT+08:00) Kuala Lumpur, Singapore Malay Peninsula Daylight Time - 'W. Australia Standard Time' => 'Australia/Perth', # (GMT+08:00) Perth W. Australia Daylight Time - 'Taipei Standard Time' => 'Asia/Taipei', # (GMT+08:00) Taipei Taipei Daylight Time - 'Tokyo Standard Time' => 'Asia/Tokyo', # (GMT+09:00) Osaka, Sapporo, Tokyo Tokyo Daylight Time - 'Korea Standard Time' => 'Asia/Seoul', # (GMT+09:00) Seoul Korea Daylight Time - 'Yakutsk Standard Time' => 'Asia/Yakutsk', # (GMT+09:00) Yakutsk Yakutsk Daylight Time - 'Cen. Australia Standard Time' => 'Australia/Adelaide', # (GMT+09:30) Adelaide Cen. Australia Daylight Time - 'AUS Central Standard Time' => 'Australia/Darwin', # (GMT+09:30) Darwin AUS Central Daylight Time - 'E. Australia Standard Time' => 'Australia/Brisbane', # (GMT+10:00) Brisbane E. Australia Daylight Time - 'AUS Eastern Standard Time' => 'Australia/Sydney', # (GMT+10:00) Canberra, Melbourne, Sydney AUS Eastern Daylight Time - 'West Pacific Standard Time' => 'Pacific/Guam', # (GMT+10:00) Guam, Port Moresby West Pacific Daylight Time - 'Tasmania Standard Time' => 'Australia/Hobart', # (GMT+10:00) Hobart Tasmania Daylight Time - 'Vladivostok Standard Time' => 'Asia/Vladivostok', # (GMT+10:00) Vladivostok Vladivostok Daylight Time - 'Central Pacific Standard Time' => 'Pacific/Noumea', # (GMT+11:00) Magadan, Solomon Is., New Caledonia Central Pacific Daylight Time - 'New Zealand Standard Time' => 'NST', # (GMT+12:00) Auckland, Wellington New Zealand Daylight Time - 'Fiji Standard Time' => 'Pacific/Fiji', # (GMT+12:00) Fiji, Kamchatka, Marshall Is. Fiji Daylight Time - 'Tonga Standard Time' => 'Pacific/Tongatapu', # (GMT+13:00) Nuku'alofa Tonga Daylight Time -); -?> \ No newline at end of file diff --git a/Date/index.php b/Date/index.php deleted file mode 100644 index 80f6d40..0000000 --- a/Date/index.php +++ /dev/null @@ -1,4 +0,0 @@ - \ No newline at end of file diff --git a/PHPAsync.php b/PHPAsync.php deleted file mode 100644 index 9249bb3..0000000 --- a/PHPAsync.php +++ /dev/null @@ -1,236 +0,0 @@ -asyncProcess = new PHPAsync(); - $async->runProcess( - $yourObject, - 'doSomeWork', - 'getBufferContent', - 'getLogHtml', - ); - - in YourClass::doSomeWork send notifications to the async log like so - - $this->asyncProcess->updateStatus( ); a numeric value, do not include "%" - * - * @package kernel - */ - -/** - * Setup - */ -require_once( UTIL_PKG_PATH.'phpcontrib_lib.php' ); - -// written for bitweaver environment, but you can override -if( !defined( 'PHPASYNC_TEMP_DIR' ) ){ - define( 'PHPASYNC_TEMP_DIR', TEMP_PKG_PATH.'phpasync' ); -} - -/** - * @package kernel - */ -class PHPAsync extends BitBase{ - private $mPid; - - private $mLogFile; - - private $mUpdateLogHandler; - - private $mFileHandle; - - private $mStatus; // pct complete - - // default config - public $mConfig = array(); - - // constructor - public function __construct( $pPidId = NULL, $pConfig = array() ){ - if( !empty( $pPidId ) ){ - // set id - $this->mPid = $pPidId; - - // log file path - $this->mLogFile = PHPASYNC_TEMP_DIR.'/'.$this->mPid; - } - - // set default config values - $this->mConfig = array( - 'append_log' => FALSE, - 'max_execution_time' => "1200", - 'memory_limit' => '128M', - 'no-gzip' => 1, - 'zlib.output_compression' => 0, - 'ignore_user_abort' => FALSE, - ); - - // override default config - if( !empty( $pConfig ) ){ - extract_to( $pConfig, $this->mConfig, EXTR_IF_EXISTS ); - } - - parent::__construct(); - } - - // runProcess - /** - * wraps a class instance method as a async process - * @param $pObject - The object instance whose process is being wrapped - * @param $pProcessHandler - The process being wrapped to run in the background - * @param $pOutputHandler - The output handler is invoked to get whatever page content should be returned to the user before the background process begins - * @param $pUpdateLogHandler - The update log handler is invoked when updateStatus is called by the wrapped process. the object can push customize content into the log by defining this callback - */ - public function runProcess( $pObject, $pProcessHandler, $pProcessHash = NULL, $pOutputHandler, $pUpdateLogHandler = NULL ){ - global $gBitSystem, $gBitSmarty; - - // create file tracking id - $this->mLogFile = $this->genLogFile(); - - $this->mPid = substr( $this->mLogFile, strlen(PHPASYNC_TEMP_DIR.'/') ); - - $this->mProcessObject = $pObject; - - // register log callback - if( !empty( $pUpdateLogHandler ) ){ - $this->mUpdateLogHandler = $pUpdateLogHandler; - } - - // override some apache settings that might screw up flushing the buffer - @apache_setenv('no-gzip', $this->getConfig('no-gzip') ); - @ini_set('zlib.output_compression', $this->getConfig('zlib.output_compression') ); - - // set time and memory - ini_set('max_execution_time', $this->getConfig('max_execution_time') ); - ini_set('memory_limit', $this->getConfig('memory_limit') ); - - // allow disallow process abort - ignore_user_abort( $this->getConfig('ignore_user_abort') ); - - // create the file for writing - $this->mFileHandle = fopen($this->mLogFile, 'wrx+'); - if (! $this->mFileHandle){ - $gBitSystem->fatalError( "Error in PHPAsync: could not create log file - process aborted. Please notify your website developer." ); - } - - // convenience make the pid available to smarty as most output handlers will want access to it - $gBitSmarty->assign( 'pid', $this->mPid ); - - // output Buffered content - $this->outputBuffer( $pObject->$pOutputHandler() ); - - // run the background process - $pObject->$pProcessHandler( $pProcessHash ); - - // clean up - sleep(20); // number of seconds the temp file should remain available for status check before it is deleted - - fclose($this->mFileHandle); - - // delete the progress file - unlink($this->mLogFile); - - //return true if completed - return true; - } - - /** - * this is our output buffer - * send it anything and it will - * send it to the browser - */ - public function outputBuffer( $pContent ){ - ob_start(); - - echo( $pContent ); - - $size = ob_get_length(); - header("Content-Length: $size"); - header('Connection: close'); - - ob_end_flush(); - // ob_flush(); -- worked fine in preliminary test and was suggested by web sample, but causes warning here - flush(); - session_write_close(); - } - - /** - * notifies our running process - * of the percentage of a task completed - */ - public function updateStatus( $pPct, $pLogText = NULL ){ - global $gBitSystem; - - // crap - hack to store pct and msg - no other way to know without storing this info in table or session or something - $logText = !empty( $pLogText )?$pPct.":".$pLogText:$pPct; - - if( (int)$pPct >= 100 ){ - $this->mStatus = 100; - }else{ - $this->mStatus = $pPct; - } - - // if a custom log message handler is registered get the log message from it - if( $func = $this->mUpdateLogHandler ){ - $this->mProcessObject->$func( $pPct ); - } - // rewind to the beginning of the log file if append is false - if( $this->getConfig( 'append_log' ) == FALSE ){ - rewind( $this->mFileHandle ); - ftruncate( $this->mFileHandle, 0 ); //filesize($this->mLogFile)); - } - // update the log file - if( fwrite( $this->mFileHandle, $logText ) == false ){ - $gBitSystem->fatalError( "Error in PHPAsync: Write to log file failed - process aborted. " . error_get_last() ); - exit; - } - } - - /** - * Retrieves the content of the log file. To be used by request checking on the status of the process - */ - public function getStatus(){ - if ( file_exists( $this->mLogFile ) ){ - if(! $progress = file_get_contents($this->mLogFile)){ - header('HTTP/1.1 500 Internal Server Error'); - exit; - } - // crap = hack to get pct and msg - see related hack in updateStatus - $delimpos = strpos( $progress, ':' ); - $pct = substr( $progress, 0, $delimpos ); - $log = substr( $progress, $delimpos+1 ); - return array( 'pct_complete' => $pct, 'log' => $log ); - }else{ - $this->setError( 'get_status', 'log file not found' ); - return FALSE; - } - } - - public function getPidId(){ - if( !empty( $this->mPid ) ){ - return $this->mPid; - } - return NULL; - } - - private function genLogFile(){ - if( !is_dir( PHPASYNC_TEMP_DIR )) { - mkdir_p( PHPASYNC_TEMP_DIR ); - } - return tempnam( PHPASYNC_TEMP_DIR, ''); - } - - private function setLogFile(){ - if( !empty( $this->mPid ) ){ - $this->mLogFile = PHPASYNC_TEMP_DIR.'/'.$this->mPid; - } - } - - private function getConfig( $pKey ){ - if( isset( $this->mConfig[$pKey] ) ){ - return $this->mConfig[$pKey]; - } - return NULL; - } -} diff --git a/bitexcel/BitExcel.php b/bitexcel/BitExcel.php deleted file mode 100644 index 19aa2f8..0000000 --- a/bitexcel/BitExcel.php +++ /dev/null @@ -1,365 +0,0 @@ -mConfig = array( - 'cache_method' => 'cache_in_memory', - 'auto_name_doc' => TRUE, - 'auto_sanitize_doc_name' => TRUE, - ); - - // override default config - if( !empty( $pConfig ) ){ - extract_to( $pConfig, $this->mConfig, EXTR_IF_EXISTS ); - } - - } - - private function configExcel( $pParamHash ){ - // config PHPExcel - - // cache method - switch( $this->getConfig( 'cache_method' ) ){ - case 'cache_to_discISAM': - $cacheMethod = PHPExcel_CachedObjectStorageFactory::cache_to_discISAM; - break; - case 'cache_in_memory_serialized': - $cacheMethod = PHPExcel_CachedObjectStorageFactory::cache_in_memory_serialized; - break; - case 'cache_in_memory': - default: - $cacheMethod = PHPExcel_CachedObjectStorageFactory::cache_in_memory; - break; - } - PHPExcel_Settings::setCacheStorageMethod($cacheMethod); - - // create excel object - if( !isset( $this->mPHPExcel ) ){ - $this->mPHPExcel = new PHPExcel(); - } - - // set doc properties - $this->mPHPExcel->getProperties()->setTitle( $pParamHash['workbook']['title'] ); - // $this->mPHPExcel->getProperties()->setCreator("Maarten Balliauw"); - // $this->mPHPExcel->getProperties()->setLastModifiedBy("Maarten Balliauw"); - // $this->mPHPExcel->getProperties()->setSubject("Office 2007 XLSX Test Document"); - // $this->mPHPExcel->getProperties()->setDescription("Test document for Office 2007 XLSX, generated using PHP classes."); - } - - /** - * write Excel 2007 file - */ - public function writeWorkbook( $pParamHash ){ - if( $this->verifyWorkbook( $pParamHash ) ){ - // init the excel object - $this->configExcel( $pParamHash ); - - if( !is_dir( EXCEL_TEMP_PATH ) ){ - mkdir_p( EXCEL_TEMP_PATH ); - } - - /* - $pParamHash['workbook'] = array( - 'title' => // document title used inside the document - 'doc_name' => // document doc name - 'worksheets' => array( - array( // worksheet - 'title' => 'foo', // worksheet title - 'rows' => array( - array( // row - => , - => array( , , ), - => array( , array() ), - ), - array( ... ), - array( ... ), - ) - ) - ), - array( ... ) // worksheet - ) - ) - - */ - - // prep doc name - // auto gen doc name - if( $this->getConfig( 'auto_name_doc' ) ){ - $docName = "somedocName"; - // defined doc name - }else{ - // get doc name from - $docName = $pParamHash['workbook']['doc_name']; - // sanitize name - if( $this->getConfig( 'auto_sanitize_doc_name' ) ){ - // replace white spaces - $docName = str_replace( ' ', '-', $docName ); - // strip nasty chars - $docName = preg_replace( '/[&$\?\*\%:\/\\\]/', '', $docName ); - } - } - - // validate filename - we validate in all cases to be certain - // a little hack so we can use validator - $dataHash = array(); - $array1 = array( 'filename' => array( 'excel_file_name' => array(), ), ); - $array2 = array( 'excel_file_name' => $docName ); - LibertyValidator::validate( - $array1, - $array2, - $this, - $dataHash - ); - - // construct the workbook - if( count( $this->getErrors() ) == 0 ){ - foreach( $pParamHash['workbook']['worksheets'] as $index=>$worksheetData ){ - // first sheet is automatically generated, for all others create it - if( $index > 0 ){ - // create sheet - $index = $this->mPHPExcel->getIndex( $this->mPHPExcel->createSheet() ); - } - $this->initWorksheet( $index, $worksheetData ); - $this->updateWorksheet( $index, $worksheetData ); - } - - } - - // no errors then write the doc - if( count( $this->getErrors() ) == 0 ){ - $fullDocName = $docName.'.xlsx'; - $xlsxFile = EXCEL_TEMP_PATH.$fullDocName; - // echo date('H:i:s') . " Write to Excel2007 format\n"; - $objWriter = new PHPExcel_Writer_Excel2007( $this->mPHPExcel ); - $objWriter->save( $xlsxFile ); - // return the doc reference - return $xlsxFile; - } - - // default fail - return FALSE; - } - } - - /** - * updateWorkbook - * adds data to an existing workbook - * @param pFileName - name of the workbook (without the extension) - * @param pParamHash['title'] - name of the worksheet to insert data into - * @param pParamHash['rows'] - data to insert - * @return bool - */ - public function updateWorkbook( $pFileName, $pParamHash ){ - $xlsxFile = EXCEL_TEMP_PATH.$pFileName.".xlsx"; - if( !empty( $pParamHash['title'] ) && !empty( $pParamHash['rows'] ) ){ - if( file_exists( $xlsxFile ) ){ - require_once( EXTERNALS_PKG_PATH.'phpexcel/Classes/PHPExcel/IOFactory.php' ); - if( $this->mPHPExcel = PHPExcel_IOFactory::load( $xlsxFile ) ){ - // @TODO support adding any new sheet - for now we assume top level data has an existing sheet - $index = $this->mPHPExcel->getIndex( $this->mPHPExcel->getSheetByName( $pParmaHash['title'] ) ); - $this->updateWorksheet( $index, $pParamHash ); - // return file path - return $xlsxFile; - }else{ - $this->setError( 'workbook', 'The workbook document failed to load' ); - } - }else{ - $this->setError( 'workbook', 'The workbook document could not be found' ); - } - }else{ - $this->setError( 'worksheet', 'No title or rows data provided' ); - } - return FALSE; - } - - public function saveWorkbook(){ - $xlsxFile = EXCEL_TEMP_PATH.$docName.'.xlsx'; - // echo date('H:i:s') . " Write to Excel2007 format\n"; - $objWriter = new PHPExcel_Writer_Excel2007( $this->mPHPExcel ); - $objWriter->save( $xlsxFile ); - // return the doc reference - return $xlsxFile; - } - - private function verifyWorkbook( $pParamHash ){ - if( empty( $pParamHash['workbook'] ) ){ - $this->setError( 'workbook', 'No workbook data provided' ); - } - if( empty( $pParamHash['workbook']['title'] ) ){ - $this->setError( 'workbook', 'No workbook title provided' ); - } - if( !$this->getConfig('auto_name_doc') && empty( $pParamHash['workbook']['doc_name'] ) ){ - $this->setError( 'workbook', 'No document name provided' ); - } - - // @TODO verify each worksheet has a title and rows - - return ( count($this->getErrors() ) == 0); - } - - private function initWorksheet( $pIndex, $pParamHash ){ - $this->mPHPExcel->setActiveSheetIndex($pIndex); - - // create title - // sanitize sheet titles - $sheetTitle = preg_replace( '/[\[\]\?\*:\/\\\]/', '', $pParamHash['title'] ); - $this->mPHPExcel->getActiveSheet()->setTitle( $sheetTitle ); - - // create column headers from first row of data - $headings = array_keys( ( is_array( current( $pParamHash['rows'] ) )?current($pParamHash['rows']):$pParamHash['rows'] ) ); - - // naturally in excel rows start at 1 - $row = 1; - $this->insertWorksheetRow( $pIndex, $headings, $row ); - } - - private function updateWorksheet( $pIndex, $pParamHash ){ - $this->mPHPExcel->setActiveSheetIndex($pIndex); - - // get next available row - $row = $this->mPHPExcel->getActiveSheet()->getHighestRow();; - $row++; - - // fill in rows - foreach( $pParamHash['rows'] as $rowData ){ - // reset the col and val - $col = 0; - $val = NULL; - // write row - $this->insertWorksheetRow( $pIndex, $rowData, $row ); - $row++; - } - } - - /** - * inserts a row into the active worksheet - * be sure you have set the active worksheet to the one you - * want to insert into - */ - private function insertWorksheetRow( $pIndex, $pParamHash, $pRow=NULL ){ - $this->mPHPExcel->setActiveSheetIndex($pIndex); - - $row = $pRow; - // undefined row get row autoamtically - if( is_null( $pRow ) ){ - if( $row = $this->mPHPExcel->getActiveSheet()->getHighestRow() ){ - $row++; - }else{ - // default first row - $row = 1; - } - } - // insert the data hash - // key of hash can not always be trusted to be numeric so manage col value - $col = 0; - foreach( $pParamHash as $key=>$val ){ - // string insert into cel - if( is_numeric( $val ) || is_string( $val ) || $val == '' ){ - $this->mPHPExcel->getActiveSheet()->setCellValueByColumnAndRow($col, $row, $val); - // array add to worksheet - }elseif( is_array( $val ) ){ - - $hasArrays = array_map( 'is_array', $val ); - $isNumeric = array_map( 'is_numeric', array_keys( $val ) ); - - // array values contain arrays or the keys are strings - if( in_array( TRUE, $hasArrays ) || in_array( FALSE, $isNumeric ) ){ - - $worksheetData = array( 'title'=>$key,'rows'=>$val ); - - // create a worksheet for these values if it doesnt already exist - $sheets = $this->mPHPExcel->getSheetNames(); - // if keys are numeric proceed with filling out sheet - if( !in_array( $key, $sheets ) ){ - $index = $this->mPHPExcel->getIndex( $this->mPHPExcel->createSheet() ); - $this->initWorksheet( $index, $worksheetData ); - // get the existing worksheet; - }else{ - $index = $this->mPHPExcel->getIndex( $this->mPHPExcel->getSheetByName($key) ); - } - - // entried nested in an array we update - if( in_array( TRUE, $hasArrays ) ) { - $this->mPHPExcel->setActiveSheetIndex($index); - $remoteRow1 = $this->mPHPExcel->getActiveSheet()->getHighestRow() + 1; - $this->updateWorksheet( $index, $worksheetData ); - $remoteRow = $this->mPHPExcel->getActiveSheet()->getHighestRow(); // last row inserted - $remoteRowLink = "A".$remoteRow; - if( $remoteRow1 != $remoteRow ){ - $remoteRowLink = "A".$remoteRow1.':A'.$remoteRow; - $remoteRow = $remoteRow1.':'.$remoteRow; - } - // single entry we insert - }else{ - $this->insertWorksheetRow( $index, $val ); - $remoteRow = $this->mPHPExcel->getActiveSheet()->getHighestRow(); // last row inserted - $remoteRowLink = "A".$remoteRow; - } - - // the active sheet was switched so switch back - $this->mPHPExcel->setActiveSheetIndex($pIndex); - $this->mPHPExcel->getActiveSheet()->setCellValueByColumnAndRow($col, $row, tra('See sheet:').$key.' '.tra('row').$remoteRow ); - // create a link to the related sheet - $sheetLink = "sheet://'".$key."'!".$remoteRowLink; - $this->mPHPExcel->getActiveSheet()->getCellByColumnAndRow($col, $row)->getHyperlink()->setUrl($sheetLink); - $this->mPHPExcel->getActiveSheet()->getCellByColumnAndRow($col, $row)->getHyperlink()->setTooltip(tra('Click for details')); - }else{ - // array does not contain arrays and keys are numeric - $val2 = implode( ',', $val ); - $this->mPHPExcel->getActiveSheet()->setCellValueByColumnAndRow($col, $row, $val2); - } - } - $col++; - } - } - - private function getConfig( $pKey ){ - if( isset( $this->mConfig[$pKey] ) ){ - return $this->mConfig[$pKey]; - } - return NULL; - } - -} - diff --git a/bitexcel/BitExcelAsync.php b/bitexcel/BitExcelAsync.php deleted file mode 100644 index ddff6a0..0000000 --- a/bitexcel/BitExcelAsync.php +++ /dev/null @@ -1,59 +0,0 @@ - TRUE, - 'memory_limit' => '256M', - ); - $this->mAsync = new PHPAsync( NULL, $config ); - $this->mAsync->runProcess( $this, 'writeWorkbook', $pParamHash, 'getUpdateInitOutput' ); - } - - public function writeWorkbook( $pParamHash ){ - if( $rslt = parent::writeWorkbook( $pParamHash ) ){ - // notify async - $this->mAsync->updateStatus( $rslt ); - }else{ - $this->mAsync->updateStatus( 'There was a problem' ); - } - } - - public function getUpdateInitOutput(){ - return tra( "Generating Export File... please be patient" ); - } - - public function getUpdateStatus( $pPidId ){ - $this->mAsync = new PHPAsync( $pPidId ); - if( $status = $this->mAsync->getStatus() ){ - return $status; - } - return 'Error: '.$this->mAsync->getErrorValue('get_status'); - } -} diff --git a/cufon/cufon-yui.js b/cufon/cufon-yui.js deleted file mode 100644 index 6443cb8..0000000 --- a/cufon/cufon-yui.js +++ /dev/null @@ -1,1379 +0,0 @@ -/*! - * Copyright (c) 2010 Simo Kinnunen. - * Licensed under the MIT license. - * - * @version 1.10 - */ - -var Cufon = (function() { - - var api = function() { - return api.replace.apply(null, arguments); - }; - - var DOM = api.DOM = { - - ready: (function() { - - var complete = false, readyStatus = { loaded: 1, complete: 1 }; - - var queue = [], perform = function() { - if (complete) return; - complete = true; - for (var fn; fn = queue.shift(); fn()); - }; - - // Gecko, Opera, WebKit r26101+ - - if (document.addEventListener) { - document.addEventListener('DOMContentLoaded', perform, false); - window.addEventListener('pageshow', perform, false); // For cached Gecko pages - } - - // Old WebKit, Internet Explorer - - if (!window.opera && document.readyState) (function() { - readyStatus[document.readyState] ? perform() : setTimeout(arguments.callee, 10); - })(); - - // Internet Explorer - - if (document.readyState && document.createStyleSheet) (function() { - try { - document.body.doScroll('left'); - perform(); - } - catch (e) { - setTimeout(arguments.callee, 1); - } - })(); - - addEvent(window, 'load', perform); // Fallback - - return function(listener) { - if (!arguments.length) perform(); - else complete ? listener() : queue.push(listener); - }; - - })(), - - root: function() { - return document.documentElement || document.body; - } - - }; - - var CSS = api.CSS = { - - Size: function(value, base) { - - this.value = parseFloat(value); - this.unit = String(value).match(/[a-z%]*$/)[0] || 'px'; - - this.convert = function(value) { - return value / base * this.value; - }; - - this.convertFrom = function(value) { - return value / this.value * base; - }; - - this.toString = function() { - return this.value + this.unit; - }; - - }, - - addClass: function(el, className) { - var current = el.className; - el.className = current + (current && ' ') + className; - return el; - }, - - color: cached(function(value) { - var parsed = {}; - parsed.color = value.replace(/^rgba\((.*?),\s*([\d.]+)\)/, function($0, $1, $2) { - parsed.opacity = parseFloat($2); - return 'rgb(' + $1 + ')'; - }); - return parsed; - }), - - // has no direct CSS equivalent. - // @see http://msdn.microsoft.com/en-us/library/system.windows.fontstretches.aspx - fontStretch: cached(function(value) { - if (typeof value == 'number') return value; - if (/%$/.test(value)) return parseFloat(value) / 100; - return { - 'ultra-condensed': 0.5, - 'extra-condensed': 0.625, - condensed: 0.75, - 'semi-condensed': 0.875, - 'semi-expanded': 1.125, - expanded: 1.25, - 'extra-expanded': 1.5, - 'ultra-expanded': 2 - }[value] || 1; - }), - - getStyle: function(el) { - var view = document.defaultView; - if (view && view.getComputedStyle) return new Style(view.getComputedStyle(el, null)); - if (el.currentStyle) return new Style(el.currentStyle); - return new Style(el.style); - }, - - gradient: cached(function(value) { - var gradient = { - id: value, - type: value.match(/^-([a-z]+)-gradient\(/)[1], - stops: [] - }, colors = value.substr(value.indexOf('(')).match(/([\d.]+=)?(#[a-f0-9]+|[a-z]+\(.*?\)|[a-z]+)/ig); - for (var i = 0, l = colors.length, stop; i < l; ++i) { - stop = colors[i].split('=', 2).reverse(); - gradient.stops.push([ stop[1] || i / (l - 1), stop[0] ]); - } - return gradient; - }), - - quotedList: cached(function(value) { - // doesn't work properly with empty quoted strings (""), but - // it's not worth the extra code. - var list = [], re = /\s*((["'])([\s\S]*?[^\\])\2|[^,]+)\s*/g, match; - while (match = re.exec(value)) list.push(match[3] || match[1]); - return list; - }), - - recognizesMedia: cached(function(media) { - var el = document.createElement('style'), sheet, container, supported; - el.type = 'text/css'; - el.media = media; - try { // this is cached anyway - el.appendChild(document.createTextNode('/**/')); - } catch (e) {} - container = elementsByTagName('head')[0]; - container.insertBefore(el, container.firstChild); - sheet = (el.sheet || el.styleSheet); - supported = sheet && !sheet.disabled; - container.removeChild(el); - return supported; - }), - - removeClass: function(el, className) { - var re = RegExp('(?:^|\\s+)' + className + '(?=\\s|$)', 'g'); - el.className = el.className.replace(re, ''); - return el; - }, - - supports: function(property, value) { - var checker = document.createElement('span').style; - if (checker[property] === undefined) return false; - checker[property] = value; - return checker[property] === value; - }, - - textAlign: function(word, style, position, wordCount) { - if (style.get('textAlign') == 'right') { - if (position > 0) word = ' ' + word; - } - else if (position < wordCount - 1) word += ' '; - return word; - }, - - textShadow: cached(function(value) { - if (value == 'none') return null; - var shadows = [], currentShadow = {}, result, offCount = 0; - var re = /(#[a-f0-9]+|[a-z]+\(.*?\)|[a-z]+)|(-?[\d.]+[a-z%]*)|,/ig; - while (result = re.exec(value)) { - if (result[0] == ',') { - shadows.push(currentShadow); - currentShadow = {}; - offCount = 0; - } - else if (result[1]) { - currentShadow.color = result[1]; - } - else { - currentShadow[[ 'offX', 'offY', 'blur' ][offCount++]] = result[2]; - } - } - shadows.push(currentShadow); - return shadows; - }), - - textTransform: (function() { - var map = { - uppercase: function(s) { - return s.toUpperCase(); - }, - lowercase: function(s) { - return s.toLowerCase(); - }, - capitalize: function(s) { - return s.replace(/(?:^|\s)./g, function($0) { - return $0.toUpperCase(); - }); - } - }; - return function(text, style) { - var transform = map[style.get('textTransform')]; - return transform ? transform(text) : text; - }; - })(), - - whiteSpace: (function() { - var ignore = { - inline: 1, - 'inline-block': 1, - 'run-in': 1 - }; - var wsStart = /^\s+/, wsEnd = /\s+$/; - return function(text, style, node, previousElement, simple) { - if (simple) return text.replace(wsStart, '').replace(wsEnd, ''); // @fixme too simple - if (previousElement) { - if (previousElement.nodeName.toLowerCase() == 'br') { - text = text.replace(wsStart, ''); - } - } - if (ignore[style.get('display')]) return text; - if (!node.previousSibling) text = text.replace(wsStart, ''); - if (!node.nextSibling) text = text.replace(wsEnd, ''); - return text; - }; - })() - - }; - - CSS.ready = (function() { - - // don't do anything in Safari 2 (it doesn't recognize any media type) - var complete = !CSS.recognizesMedia('all'), hasLayout = false; - - var queue = [], perform = function() { - complete = true; - for (var fn; fn = queue.shift(); fn()); - }; - - var links = elementsByTagName('link'), styles = elementsByTagName('style'); - - function isContainerReady(el) { - return el.disabled || isSheetReady(el.sheet, el.media || 'screen'); - } - - function isSheetReady(sheet, media) { - // in Opera sheet.disabled is true when it's still loading, - // even though link.disabled is false. they stay in sync if - // set manually. - if (!CSS.recognizesMedia(media || 'all')) return true; - if (!sheet || sheet.disabled) return false; - try { - var rules = sheet.cssRules, rule; - if (rules) { - // needed for Safari 3 and Chrome 1.0. - // in standards-conforming browsers cssRules contains @-rules. - // Chrome 1.0 weirdness: rules[] - // returns the last rule, so a for loop is the only option. - search: for (var i = 0, l = rules.length; rule = rules[i], i < l; ++i) { - switch (rule.type) { - case 2: // @charset - break; - case 3: // @import - if (!isSheetReady(rule.styleSheet, rule.media.mediaText)) return false; - break; - default: - // only @charset can precede @import - break search; - } - } - } - } - catch (e) {} // probably a style sheet from another domain - return true; - } - - function allStylesLoaded() { - // Internet Explorer's style sheet model, there's no need to do anything - if (document.createStyleSheet) return true; - // standards-compliant browsers - var el, i; - for (i = 0; el = links[i]; ++i) { - if (el.rel.toLowerCase() == 'stylesheet' && !isContainerReady(el)) return false; - } - for (i = 0; el = styles[i]; ++i) { - if (!isContainerReady(el)) return false; - } - return true; - } - - DOM.ready(function() { - // getComputedStyle returns null in Gecko if used in an iframe with display: none - if (!hasLayout) hasLayout = CSS.getStyle(document.body).isUsable(); - if (complete || (hasLayout && allStylesLoaded())) perform(); - else setTimeout(arguments.callee, 10); - }); - - return function(listener) { - if (complete) listener(); - else queue.push(listener); - }; - - })(); - - function Font(data) { - - var face = this.face = data.face, wordSeparators = { - '\u0020': 1, - '\u00a0': 1, - '\u3000': 1 - }; - - this.glyphs = (function(glyphs) { - var key, fallbacks = { - '\u2011': '\u002d', - '\u00ad': '\u2011' - }; - for (key in fallbacks) { - if (!hasOwnProperty(fallbacks, key)) continue; - if (!glyphs[key]) glyphs[key] = glyphs[fallbacks[key]]; - } - return glyphs; - })(data.glyphs); - - this.w = data.w; - this.baseSize = parseInt(face['units-per-em'], 10); - - this.family = face['font-family'].toLowerCase(); - this.weight = face['font-weight']; - this.style = face['font-style'] || 'normal'; - - this.viewBox = (function () { - var parts = face.bbox.split(/\s+/); - var box = { - minX: parseInt(parts[0], 10), - minY: parseInt(parts[1], 10), - maxX: parseInt(parts[2], 10), - maxY: parseInt(parts[3], 10) - }; - box.width = box.maxX - box.minX; - box.height = box.maxY - box.minY; - box.toString = function() { - return [ this.minX, this.minY, this.width, this.height ].join(' '); - }; - return box; - })(); - - this.ascent = -parseInt(face.ascent, 10); - this.descent = -parseInt(face.descent, 10); - - this.height = -this.ascent + this.descent; - - this.spacing = function(chars, letterSpacing, wordSpacing) { - var glyphs = this.glyphs, glyph, - kerning, k, - jumps = [], - width = 0, w, - i = -1, j = -1, chr; - while (chr = chars[++i]) { - glyph = glyphs[chr] || this.missingGlyph; - if (!glyph) continue; - if (kerning) { - width -= k = kerning[chr] || 0; - jumps[j] -= k; - } - w = glyph.w; - if (isNaN(w)) w = +this.w; // may have been a String in old fonts - if (w > 0) { - w += letterSpacing; - if (wordSeparators[chr]) w += wordSpacing; - } - width += jumps[++j] = ~~w; // get rid of decimals - kerning = glyph.k; - } - jumps.total = width; - return jumps; - }; - - } - - function FontFamily() { - - var styles = {}, mapping = { - oblique: 'italic', - italic: 'oblique' - }; - - this.add = function(font) { - (styles[font.style] || (styles[font.style] = {}))[font.weight] = font; - }; - - this.get = function(style, weight) { - var weights = styles[style] || styles[mapping[style]] - || styles.normal || styles.italic || styles.oblique; - if (!weights) return null; - // we don't have to worry about "bolder" and "lighter" - // because IE's currentStyle returns a numeric value for it, - // and other browsers use the computed value anyway - weight = { - normal: 400, - bold: 700 - }[weight] || parseInt(weight, 10); - if (weights[weight]) return weights[weight]; - // http://www.w3.org/TR/CSS21/fonts.html#propdef-font-weight - // Gecko uses x99/x01 for lighter/bolder - var up = { - 1: 1, - 99: 0 - }[weight % 100], alts = [], min, max; - if (up === undefined) up = weight > 400; - if (weight == 500) weight = 400; - for (var alt in weights) { - if (!hasOwnProperty(weights, alt)) continue; - alt = parseInt(alt, 10); - if (!min || alt < min) min = alt; - if (!max || alt > max) max = alt; - alts.push(alt); - } - if (weight < min) weight = min; - if (weight > max) weight = max; - alts.sort(function(a, b) { - return (up - ? (a >= weight && b >= weight) ? a < b : a > b - : (a <= weight && b <= weight) ? a > b : a < b) ? -1 : 1; - }); - return weights[alts[0]]; - }; - - } - - function HoverHandler() { - - function contains(node, anotherNode) { - try { - if (node.contains) return node.contains(anotherNode); - return node.compareDocumentPosition(anotherNode) & 16; - } - catch(e) {} // probably a XUL element such as a scrollbar - return false; - } - - function onOverOut(e) { - var related = e.relatedTarget; - // there might be no relatedTarget if the element is right next - // to the window frame - if (related && contains(this, related)) return; - trigger(this, e.type == 'mouseover'); - } - - function onEnterLeave(e) { - trigger(this, e.type == 'mouseenter'); - } - - function trigger(el, hoverState) { - // A timeout is needed so that the event can actually "happen" - // before replace is triggered. This ensures that styles are up - // to date. - setTimeout(function() { - var options = sharedStorage.get(el).options; - api.replace(el, hoverState ? merge(options, options.hover) : options, true); - }, 10); - } - - this.attach = function(el) { - if (el.onmouseenter === undefined) { - addEvent(el, 'mouseover', onOverOut); - addEvent(el, 'mouseout', onOverOut); - } - else { - addEvent(el, 'mouseenter', onEnterLeave); - addEvent(el, 'mouseleave', onEnterLeave); - } - }; - - } - - function ReplaceHistory() { - - var list = [], map = {}; - - function filter(keys) { - var values = [], key; - for (var i = 0; key = keys[i]; ++i) values[i] = list[map[key]]; - return values; - } - - this.add = function(key, args) { - map[key] = list.push(args) - 1; - }; - - this.repeat = function() { - var snapshot = arguments.length ? filter(arguments) : list, args; - for (var i = 0; args = snapshot[i++];) api.replace(args[0], args[1], true); - }; - - } - - function Storage() { - - var map = {}, at = 0; - - function identify(el) { - return el.cufid || (el.cufid = ++at); - } - - this.get = function(el) { - var id = identify(el); - return map[id] || (map[id] = {}); - }; - - } - - function Style(style) { - - var custom = {}, sizes = {}; - - this.extend = function(styles) { - for (var property in styles) { - if (hasOwnProperty(styles, property)) custom[property] = styles[property]; - } - return this; - }; - - this.get = function(property) { - return custom[property] != undefined ? custom[property] : style[property]; - }; - - this.getSize = function(property, base) { - return sizes[property] || (sizes[property] = new CSS.Size(this.get(property), base)); - }; - - this.isUsable = function() { - return !!style; - }; - - } - - function addEvent(el, type, listener) { - if (el.addEventListener) { - el.addEventListener(type, listener, false); - } - else if (el.attachEvent) { - el.attachEvent('on' + type, function() { - return listener.call(el, window.event); - }); - } - } - - function attach(el, options) { - var storage = sharedStorage.get(el); - if (storage.options) return el; - if (options.hover && options.hoverables[el.nodeName.toLowerCase()]) { - hoverHandler.attach(el); - } - storage.options = options; - return el; - } - - function cached(fun) { - var cache = {}; - return function(key) { - if (!hasOwnProperty(cache, key)) cache[key] = fun.apply(null, arguments); - return cache[key]; - }; - } - - function getFont(el, style) { - var families = CSS.quotedList(style.get('fontFamily').toLowerCase()), family; - for (var i = 0; family = families[i]; ++i) { - if (fonts[family]) return fonts[family].get(style.get('fontStyle'), style.get('fontWeight')); - } - return null; - } - - function elementsByTagName(query) { - return document.getElementsByTagName(query); - } - - function hasOwnProperty(obj, property) { - return obj.hasOwnProperty(property); - } - - function merge() { - var merged = {}, arg, key; - for (var i = 0, l = arguments.length; arg = arguments[i], i < l; ++i) { - for (key in arg) { - if (hasOwnProperty(arg, key)) merged[key] = arg[key]; - } - } - return merged; - } - - function process(font, text, style, options, node, el) { - var fragment = document.createDocumentFragment(), processed; - if (text === '') return fragment; - var separate = options.separate; - var parts = text.split(separators[separate]), needsAligning = (separate == 'words'); - if (needsAligning && HAS_BROKEN_REGEXP) { - // @todo figure out a better way to do this - if (/^\s/.test(text)) parts.unshift(''); - if (/\s$/.test(text)) parts.push(''); - } - for (var i = 0, l = parts.length; i < l; ++i) { - processed = engines[options.engine](font, - needsAligning ? CSS.textAlign(parts[i], style, i, l) : parts[i], - style, options, node, el, i < l - 1); - if (processed) fragment.appendChild(processed); - } - return fragment; - } - - function replaceElement(el, options) { - var name = el.nodeName.toLowerCase(); - if (options.ignore[name]) return; - if (options.onBeforeReplace) options.onBeforeReplace(el, options); - if (el.className.indexOf('sudo') > -1 ) el = el.parentNode; - var replace = !options.textless[name], simple = (options.trim === 'simple'); - var style = CSS.getStyle(attach(el, options)).extend(options); - // may cause issues if the element contains other elements - // with larger fontSize, however such cases are rare and can - // be fixed by using a more specific selector - if (parseFloat(style.get('fontSize')) === 0) return; - var font = getFont(el, style), node, type, next, anchor, text, lastElement; - var isShy = options.softHyphens, anyShy = false, pos, shy, reShy = /\u00ad/g; - var modifyText = options.modifyText; - if (!font) return; - for (node = el.firstChild; node; node = next) { - type = node.nodeType; - next = node.nextSibling; - if (replace && type == 3) { - if (isShy && el.nodeName.toLowerCase() != TAG_SHY) { - pos = node.data.indexOf('\u00ad'); - if (pos >= 0) { - node.splitText(pos); - next = node.nextSibling; - next.deleteData(0, 1); - shy = document.createElement(TAG_SHY); - shy.appendChild(document.createTextNode('\u00ad')); - el.insertBefore(shy, next); - next = shy; - anyShy = true; - } - } - // Node.normalize() is broken in IE 6, 7, 8 - if (anchor) { - anchor.appendData(node.data); - el.removeChild(node); - } - else anchor = node; - if (next) continue; - } - if (anchor) { - text = anchor.data; - if (!isShy) text = text.replace(reShy, ''); - text = CSS.whiteSpace(text, style, anchor, lastElement, simple); - // modify text only on the first replace - if (modifyText) text = modifyText(text, anchor, el, options); - el.replaceChild(process(font, text, style, options, node, el), anchor); - anchor = null; - } - if (type == 1) { - if (node.firstChild) { - if (node.nodeName.toLowerCase() == 'cufon') { - engines[options.engine](font, null, style, options, node, el); - } - else arguments.callee(node, options); - } - lastElement = node; - } - } - if (isShy && anyShy) { - updateShy(el); - if (!trackingShy) addEvent(window, 'resize', updateShyOnResize); - trackingShy = true; - } - if (options.onAfterReplace) options.onAfterReplace(el, options); - } - - function updateShy(context) { - var shys, shy, parent, glue, newGlue, next, prev, i; - shys = context.getElementsByTagName(TAG_SHY); - // unfortunately there doesn't seem to be any easy - // way to avoid having to loop through the shys twice. - for (i = 0; shy = shys[i]; ++i) { - shy.className = C_SHY_DISABLED; - glue = parent = shy.parentNode; - if (glue.nodeName.toLowerCase() != TAG_GLUE) { - newGlue = document.createElement(TAG_GLUE); - newGlue.appendChild(shy.previousSibling); - parent.insertBefore(newGlue, shy); - newGlue.appendChild(shy); - } - else { - // get rid of double glue (edge case fix) - glue = glue.parentNode; - if (glue.nodeName.toLowerCase() == TAG_GLUE) { - parent = glue.parentNode; - while (glue.firstChild) { - parent.insertBefore(glue.firstChild, glue); - } - parent.removeChild(glue); - } - } - } - for (i = 0; shy = shys[i]; ++i) { - shy.className = ''; - glue = shy.parentNode; - parent = glue.parentNode; - next = glue.nextSibling || parent.nextSibling; - // make sure we're comparing same types - prev = (next.nodeName.toLowerCase() == TAG_GLUE) ? glue : shy.previousSibling; - if (prev.offsetTop >= next.offsetTop) { - shy.className = C_SHY_DISABLED; - if (prev.offsetTop < next.offsetTop) { - // we have an annoying edge case, double the glue - newGlue = document.createElement(TAG_GLUE); - parent.insertBefore(newGlue, glue); - newGlue.appendChild(glue); - newGlue.appendChild(next); - } - } - } - } - - function updateShyOnResize() { - if (ignoreResize) return; // needed for IE - CSS.addClass(DOM.root(), C_VIEWPORT_RESIZING); - clearTimeout(shyTimer); - shyTimer = setTimeout(function() { - ignoreResize = true; - CSS.removeClass(DOM.root(), C_VIEWPORT_RESIZING); - updateShy(document); - ignoreResize = false; - }, 100); - } - - var HAS_BROKEN_REGEXP = ' '.split(/\s+/).length == 0; - var TAG_GLUE = 'cufonglue'; - var TAG_SHY = 'cufonshy'; - var C_SHY_DISABLED = 'cufon-shy-disabled'; - var C_VIEWPORT_RESIZING = 'cufon-viewport-resizing'; - - var sharedStorage = new Storage(); - var hoverHandler = new HoverHandler(); - var replaceHistory = new ReplaceHistory(); - var initialized = false; - var trackingShy = false; - var shyTimer; - var ignoreResize = false; - - var engines = {}, fonts = {}, defaultOptions = { - autoDetect: false, - engine: null, - //fontScale: 1, - //fontScaling: false, - forceHitArea: false, - hover: false, - hoverables: { - a: true - }, - ignore: { - applet: 1, - canvas: 1, - col: 1, - colgroup: 1, - head: 1, - iframe: 1, - map: 1, - noscript: 1, - optgroup: 1, - option: 1, - script: 1, - select: 1, - style: 1, - textarea: 1, - title: 1, - pre: 1 - }, - modifyText: null, - onAfterReplace: null, - onBeforeReplace: null, - printable: true, - //rotation: 0, - //selectable: false, - selector: ( - window.Sizzle - || (window.jQuery && function(query) { return jQuery(query); }) // avoid noConflict issues - || (window.dojo && dojo.query) - || (window.glow && glow.dom && glow.dom.get) - || (window.Ext && Ext.query) - || (window.YAHOO && YAHOO.util && YAHOO.util.Selector && YAHOO.util.Selector.query) - || (window.$$ && function(query) { return $$(query); }) - || (window.$ && function(query) { return $(query); }) - || (document.querySelectorAll && function(query) { return document.querySelectorAll(query); }) - || elementsByTagName - ), - separate: 'words', // 'none' and 'characters' are also accepted - softHyphens: true, - textless: { - dl: 1, - html: 1, - ol: 1, - table: 1, - tbody: 1, - thead: 1, - tfoot: 1, - tr: 1, - ul: 1 - }, - textShadow: 'none', - trim: 'advanced' - }; - - var separators = { - // The first pattern may cause unicode characters above - // code point 255 to be removed in Safari 3.0. Luckily enough - // Safari 3.0 does not include non-breaking spaces in \s, so - // we can just use a simple alternative pattern. - words: /\s/.test('\u00a0') ? /[^\S\u00a0]+/ : /\s+/, - characters: '', - none: /^/ - }; - - api.now = function() { - DOM.ready(); - return api; - }; - - api.refresh = function() { - replaceHistory.repeat.apply(replaceHistory, arguments); - return api; - }; - - api.registerEngine = function(id, engine) { - if (!engine) return api; - engines[id] = engine; - return api.set('engine', id); - }; - - api.registerFont = function(data) { - if (!data) return api; - var font = new Font(data), family = font.family; - if (!fonts[family]) fonts[family] = new FontFamily(); - fonts[family].add(font); - return api.set('fontFamily', '"' + family + '"'); - }; - - api.replace = function(elements, options, ignoreHistory) { - options = merge(defaultOptions, options); - if (!options.engine) return api; // there's no browser support so we'll just stop here - if (!initialized) { - CSS.addClass(DOM.root(), 'cufon-active cufon-loading'); - CSS.ready(function() { - // fires before any replace() calls, but it doesn't really matter - CSS.addClass(CSS.removeClass(DOM.root(), 'cufon-loading'), 'cufon-ready'); - }); - initialized = true; - } - if (options.hover) options.forceHitArea = true; - if (options.autoDetect) delete options.fontFamily; - if (typeof options.textShadow == 'string') { - options.textShadow = CSS.textShadow(options.textShadow); - } - if (typeof options.color == 'string' && /^-/.test(options.color)) { - options.textGradient = CSS.gradient(options.color); - } - else delete options.textGradient; - if (!ignoreHistory) replaceHistory.add(elements, arguments); - if (elements.nodeType || typeof elements == 'string') elements = [ elements ]; - CSS.ready(function() { - for (var i = 0, l = elements.length; i < l; ++i) { - var el = elements[i]; - if (typeof el == 'string') api.replace(options.selector(el), options, true); - else replaceElement(el, options); - } - }); - return api; - }; - - api.set = function(option, value) { - defaultOptions[option] = value; - return api; - }; - - return api; - -})(); - -Cufon.registerEngine('vml', (function() { - - var ns = document.namespaces; - if (!ns) return; - ns.add('cvml', 'urn:schemas-microsoft-com:vml'); - ns = null; - - var check = document.createElement('cvml:shape'); - check.style.behavior = 'url(#default#VML)'; - if (!check.coordsize) return; // VML isn't supported - check = null; - - var HAS_BROKEN_LINEHEIGHT = (document.documentMode || 0) < 8; - - document.write(('').replace(/;/g, '!important;')); - - function getFontSizeInPixels(el, value) { - return getSizeInPixels(el, /(?:em|ex|%)$|^[a-z-]+$/i.test(value) ? '1em' : value); - } - - // Original by Dead Edwards. - // Combined with getFontSizeInPixels it also works with relative units. - function getSizeInPixels(el, value) { - if (!isNaN(value) || /px$/i.test(value)) return parseFloat(value); - var style = el.style.left, runtimeStyle = el.runtimeStyle.left; - el.runtimeStyle.left = el.currentStyle.left; - el.style.left = value.replace('%', 'em'); - var result = el.style.pixelLeft; - el.style.left = style; - el.runtimeStyle.left = runtimeStyle; - return result; - } - - function getSpacingValue(el, style, size, property) { - var key = 'computed' + property, value = style[key]; - if (isNaN(value)) { - value = style.get(property); - style[key] = value = (value == 'normal') ? 0 : ~~size.convertFrom(getSizeInPixels(el, value)); - } - return value; - } - - var fills = {}; - - function gradientFill(gradient) { - var id = gradient.id; - if (!fills[id]) { - var stops = gradient.stops, fill = document.createElement('cvml:fill'), colors = []; - fill.type = 'gradient'; - fill.angle = 180; - fill.focus = '0'; - fill.method = 'none'; - fill.color = stops[0][1]; - for (var j = 1, k = stops.length - 1; j < k; ++j) { - colors.push(stops[j][0] * 100 + '% ' + stops[j][1]); - } - fill.colors = colors.join(','); - fill.color2 = stops[k][1]; - fills[id] = fill; - } - return fills[id]; - } - - return function(font, text, style, options, node, el, hasNext) { - - var redraw = (text === null); - - if (redraw) text = node.alt; - - var viewBox = font.viewBox; - - var size = style.computedFontSize || (style.computedFontSize = new Cufon.CSS.Size(getFontSizeInPixels(el, style.get('fontSize')) + 'px', font.baseSize)); - - var wrapper, canvas; - - if (redraw) { - wrapper = node; - canvas = node.firstChild; - } - else { - wrapper = document.createElement('cufon'); - wrapper.className = 'cufon cufon-vml'; - wrapper.alt = text; - - canvas = document.createElement('cufoncanvas'); - wrapper.appendChild(canvas); - - if (options.printable) { - var print = document.createElement('cufontext'); - print.appendChild(document.createTextNode(text)); - wrapper.appendChild(print); - } - - // ie6, for some reason, has trouble rendering the last VML element in the document. - // we can work around this by injecting a dummy element where needed. - // @todo find a better solution - if (!hasNext) wrapper.appendChild(document.createElement('cvml:shape')); - } - - var wStyle = wrapper.style; - var cStyle = canvas.style; - - var height = size.convert(viewBox.height), roundedHeight = Math.ceil(height); - var roundingFactor = roundedHeight / height; - var stretchFactor = roundingFactor * Cufon.CSS.fontStretch(style.get('fontStretch')); - var minX = viewBox.minX, minY = viewBox.minY; - - cStyle.height = roundedHeight; - cStyle.top = Math.round(size.convert(minY - font.ascent)); - cStyle.left = Math.round(size.convert(minX)); - - wStyle.height = size.convert(font.height) + 'px'; - - var color = style.get('color'); - var chars = Cufon.CSS.textTransform(text, style).split(''); - - var jumps = font.spacing(chars, - getSpacingValue(el, style, size, 'letterSpacing'), - getSpacingValue(el, style, size, 'wordSpacing') - ); - - if (!jumps.length) return null; - - var width = jumps.total; - var fullWidth = -minX + width + (viewBox.width - jumps[jumps.length - 1]); - - var shapeWidth = size.convert(fullWidth * stretchFactor), roundedShapeWidth = Math.round(shapeWidth); - - var coordSize = fullWidth + ',' + viewBox.height, coordOrigin; - var stretch = 'r' + coordSize + 'ns'; - - var fill = options.textGradient && gradientFill(options.textGradient); - - var glyphs = font.glyphs, offsetX = 0; - var shadows = options.textShadow; - var i = -1, j = 0, chr; - - while (chr = chars[++i]) { - - var glyph = glyphs[chars[i]] || font.missingGlyph, shape; - if (!glyph) continue; - - if (redraw) { - // some glyphs may be missing so we can't use i - shape = canvas.childNodes[j]; - while (shape.firstChild) shape.removeChild(shape.firstChild); // shadow, fill - } - else { - shape = document.createElement('cvml:shape'); - canvas.appendChild(shape); - } - - shape.stroked = 'f'; - shape.coordsize = coordSize; - shape.coordorigin = coordOrigin = (minX - offsetX) + ',' + minY; - shape.path = (glyph.d ? 'm' + glyph.d + 'xe' : '') + 'm' + coordOrigin + stretch; - shape.fillcolor = color; - - if (fill) shape.appendChild(fill.cloneNode(false)); - - // it's important to not set top/left or IE8 will grind to a halt - var sStyle = shape.style; - sStyle.width = roundedShapeWidth; - sStyle.height = roundedHeight; - - if (shadows) { - // due to the limitations of the VML shadow element there - // can only be two visible shadows. opacity is shared - // for all shadows. - var shadow1 = shadows[0], shadow2 = shadows[1]; - var color1 = Cufon.CSS.color(shadow1.color), color2; - var shadow = document.createElement('cvml:shadow'); - shadow.on = 't'; - shadow.color = color1.color; - shadow.offset = shadow1.offX + ',' + shadow1.offY; - if (shadow2) { - color2 = Cufon.CSS.color(shadow2.color); - shadow.type = 'double'; - shadow.color2 = color2.color; - shadow.offset2 = shadow2.offX + ',' + shadow2.offY; - } - shadow.opacity = color1.opacity || (color2 && color2.opacity) || 1; - shape.appendChild(shadow); - } - - offsetX += jumps[j++]; - } - - // addresses flickering issues on :hover - - var cover = shape.nextSibling, coverFill, vStyle; - - if (options.forceHitArea) { - - if (!cover) { - cover = document.createElement('cvml:rect'); - cover.stroked = 'f'; - cover.className = 'cufon-vml-cover'; - coverFill = document.createElement('cvml:fill'); - coverFill.opacity = 0; - cover.appendChild(coverFill); - canvas.appendChild(cover); - } - - vStyle = cover.style; - - vStyle.width = roundedShapeWidth; - vStyle.height = roundedHeight; - - } - else if (cover) canvas.removeChild(cover); - - wStyle.width = Math.max(Math.ceil(size.convert(width * stretchFactor)), 0); - - if (HAS_BROKEN_LINEHEIGHT) { - - var yAdjust = style.computedYAdjust; - - if (yAdjust === undefined) { - var lineHeight = style.get('lineHeight'); - if (lineHeight == 'normal') lineHeight = '1em'; - else if (!isNaN(lineHeight)) lineHeight += 'em'; // no unit - style.computedYAdjust = yAdjust = 0.5 * (getSizeInPixels(el, lineHeight) - parseFloat(wStyle.height)); - } - - if (yAdjust) { - wStyle.marginTop = Math.ceil(yAdjust) + 'px'; - wStyle.marginBottom = yAdjust + 'px'; - } - - } - - return wrapper; - - }; - -})()); - -Cufon.registerEngine('canvas', (function() { - - // Safari 2 doesn't support .apply() on native methods - - var check = document.createElement('canvas'); - if (!check || !check.getContext || !check.getContext.apply) return; - check = null; - - var HAS_INLINE_BLOCK = Cufon.CSS.supports('display', 'inline-block'); - - // Firefox 2 w/ non-strict doctype (almost standards mode) - var HAS_BROKEN_LINEHEIGHT = !HAS_INLINE_BLOCK && (document.compatMode == 'BackCompat' || /frameset|transitional/i.test(document.doctype.publicId)); - - var styleSheet = document.createElement('style'); - styleSheet.type = 'text/css'; - styleSheet.appendChild(document.createTextNode(( - 'cufon{text-indent:0;}' + - '@media screen,projection{' + - 'cufon{display:inline;display:inline-block;position:relative;vertical-align:middle;' + - (HAS_BROKEN_LINEHEIGHT - ? '' - : 'font-size:1px;line-height:1px;') + - '}cufon cufontext{display:-moz-inline-box;display:inline-block;width:0;height:0;text-align:left;text-indent:-10000in;}' + - (HAS_INLINE_BLOCK - ? 'cufon canvas{position:relative;}' - : 'cufon canvas{position:absolute;}') + - 'cufonshy.cufon-shy-disabled,.cufon-viewport-resizing cufonshy{display:none;}' + - 'cufonglue{white-space:nowrap;display:inline-block;}' + - '.cufon-viewport-resizing cufonglue{white-space:normal;}' + - '}' + - '@media print{' + - 'cufon{padding:0;}' + // Firefox 2 - 'cufon canvas{display:none;}' + - '}' - ).replace(/;/g, '!important;'))); - document.getElementsByTagName('head')[0].appendChild(styleSheet); - - function generateFromVML(path, context) { - var atX = 0, atY = 0; - var code = [], re = /([mrvxe])([^a-z]*)/g, match; - generate: for (var i = 0; match = re.exec(path); ++i) { - var c = match[2].split(','); - switch (match[1]) { - case 'v': - code[i] = { m: 'bezierCurveTo', a: [ atX + ~~c[0], atY + ~~c[1], atX + ~~c[2], atY + ~~c[3], atX += ~~c[4], atY += ~~c[5] ] }; - break; - case 'r': - code[i] = { m: 'lineTo', a: [ atX += ~~c[0], atY += ~~c[1] ] }; - break; - case 'm': - code[i] = { m: 'moveTo', a: [ atX = ~~c[0], atY = ~~c[1] ] }; - break; - case 'x': - code[i] = { m: 'closePath' }; - break; - case 'e': - break generate; - } - context[code[i].m].apply(context, code[i].a); - } - return code; - } - - function interpret(code, context) { - for (var i = 0, l = code.length; i < l; ++i) { - var line = code[i]; - context[line.m].apply(context, line.a); - } - } - - return function(font, text, style, options, node, el) { - - var redraw = (text === null); - - if (redraw) text = node.getAttribute('alt'); - - var viewBox = font.viewBox; - - var size = style.getSize('fontSize', font.baseSize); - - var expandTop = 0, expandRight = 0, expandBottom = 0, expandLeft = 0; - var shadows = options.textShadow, shadowOffsets = []; - if (shadows) { - for (var i = shadows.length; i--;) { - var shadow = shadows[i]; - var x = size.convertFrom(parseFloat(shadow.offX)); - var y = size.convertFrom(parseFloat(shadow.offY)); - shadowOffsets[i] = [ x, y ]; - if (y < expandTop) expandTop = y; - if (x > expandRight) expandRight = x; - if (y > expandBottom) expandBottom = y; - if (x < expandLeft) expandLeft = x; - } - } - - var chars = Cufon.CSS.textTransform(text, style).split(''); - - var jumps = font.spacing(chars, - ~~size.convertFrom(parseFloat(style.get('letterSpacing')) || 0), - ~~size.convertFrom(parseFloat(style.get('wordSpacing')) || 0) - ); - - if (!jumps.length) return null; // there's nothing to render - - var width = jumps.total; - - expandRight += viewBox.width - jumps[jumps.length - 1]; - expandLeft += viewBox.minX; - - var wrapper, canvas; - - if (redraw) { - wrapper = node; - canvas = node.firstChild; - } - else { - wrapper = document.createElement('cufon'); - wrapper.className = 'cufon cufon-canvas'; - wrapper.setAttribute('alt', text); - - canvas = document.createElement('canvas'); - wrapper.appendChild(canvas); - - if (options.printable) { - var print = document.createElement('cufontext'); - print.appendChild(document.createTextNode(text)); - wrapper.appendChild(print); - } - } - - var wStyle = wrapper.style; - var cStyle = canvas.style; - - var height = size.convert(viewBox.height); - var roundedHeight = Math.ceil(height); - var roundingFactor = roundedHeight / height; - var stretchFactor = roundingFactor * Cufon.CSS.fontStretch(style.get('fontStretch')); - var stretchedWidth = width * stretchFactor; - - var canvasWidth = Math.ceil(size.convert(stretchedWidth + expandRight - expandLeft)); - var canvasHeight = Math.ceil(size.convert(viewBox.height - expandTop + expandBottom)); - - canvas.width = canvasWidth; - canvas.height = canvasHeight; - - // needed for WebKit and full page zoom - cStyle.width = canvasWidth + 'px'; - cStyle.height = canvasHeight + 'px'; - - // minY has no part in canvas.height - expandTop += viewBox.minY; - - cStyle.top = Math.round(size.convert(expandTop - font.ascent)) + 'px'; - cStyle.left = Math.round(size.convert(expandLeft)) + 'px'; - - var wrapperWidth = Math.max(Math.ceil(size.convert(stretchedWidth)), 0) + 'px'; - - if (HAS_INLINE_BLOCK) { - wStyle.width = wrapperWidth; - wStyle.height = size.convert(font.height) + 'px'; - } - else { - wStyle.paddingLeft = wrapperWidth; - wStyle.paddingBottom = (size.convert(font.height) - 1) + 'px'; - } - - var g = canvas.getContext('2d'), scale = height / viewBox.height; - - // proper horizontal scaling is performed later - g.scale(scale, scale * roundingFactor); - g.translate(-expandLeft, -expandTop); - g.save(); - - function renderText() { - var glyphs = font.glyphs, glyph, i = -1, j = -1, chr; - g.scale(stretchFactor, 1); - while (chr = chars[++i]) { - var glyph = glyphs[chars[i]] || font.missingGlyph; - if (!glyph) continue; - if (glyph.d) { - g.beginPath(); - if (glyph.code) interpret(glyph.code, g); - else glyph.code = generateFromVML('m' + glyph.d, g); - g.fill(); - } - g.translate(jumps[++j], 0); - } - g.restore(); - } - - if (shadows) { - for (var i = shadows.length; i--;) { - var shadow = shadows[i]; - g.save(); - g.fillStyle = shadow.color; - g.translate.apply(g, shadowOffsets[i]); - renderText(); - } - } - - var gradient = options.textGradient; - if (gradient) { - var stops = gradient.stops, fill = g.createLinearGradient(0, viewBox.minY, 0, viewBox.maxY); - for (var i = 0, l = stops.length; i < l; ++i) { - fill.addColorStop.apply(fill, stops[i]); - } - g.fillStyle = fill; - } - else g.fillStyle = style.get('color'); - - renderText(); - - return wrapper; - - }; - -})()); \ No newline at end of file diff --git a/dBug/dBug.php b/dBug/dBug.php deleted file mode 100644 index ed0ffa2..0000000 --- a/dBug/dBug.php +++ /dev/null @@ -1,531 +0,0 @@ -initJSandCSS(); - } - $arrAccept=array("array","object","xml"); //array of variable types that can be "forced" - $this->bCollapsed = $bCollapsed; - if(in_array($forceType,$arrAccept)) - $this->{"varIs".ucfirst($forceType)}($var); - else - $this->checkType($var); - } - - //get variable name - function getVariableName() { - $arrBacktrace = debug_backtrace(); - - //possible 'included' functions - $arrInclude = array("include","include_once","require","require_once"); - - //check for any included/required files. if found, get array of the last included file (they contain the right line numbers) - for($i=count($arrBacktrace)-1; $i>=0; $i--) { - $arrCurrent = $arrBacktrace[$i]; - if(array_key_exists("function", $arrCurrent) && - (in_array($arrCurrent["function"], $arrInclude) || (0 != strcasecmp($arrCurrent["function"], "dbug")))) - continue; - - $arrFile = $arrCurrent; - - break; - } - - if(isset($arrFile)) { - $arrLines = file($arrFile["file"]); - $code = $arrLines[($arrFile["line"]-1)]; - - //find call to dBug class - preg_match('/\bnew dBug\s*\(\s*(.+)\s*\);/i', $code, $arrMatches); - - return $arrMatches[1]; - } - return ""; - } - - //create the main table header - function makeTableHeader($type,$header,$colspan=2) { - if(!$this->bInitialized) { - $header = $this->getVariableName() . " (" . $header . ")"; - $this->bInitialized = true; - } - $str_i = ($this->bCollapsed) ? "style=\"font-style:italic\" " : ""; - - echo " - - - "; - } - - //create the table row header - function makeTDHeader($type,$header) { - $str_d = ($this->bCollapsed) ? " style=\"display:none\"" : ""; - echo " - - \n"; - } - - //error - function error($type) { - $error="Error: Variable cannot be a"; - // this just checks if the type starts with a vowel or "x" and displays either "a" or "an" - if(in_array(substr($type,0,1),array("a","e","i","o","u","x"))) - $error.="n"; - return ($error." ".$type." type"); - } - - //check variable type - function checkType($var) { - switch(gettype($var)) { - case "resource": - $this->varIsResource($var); - break; - case "object": - $this->varIsObject($var); - break; - case "array": - $this->varIsArray($var); - break; - case "NULL": - $this->varIsNULL(); - break; - case "boolean": - $this->varIsBoolean($var); - break; - default: - $var=($var=="") ? "[empty string]" : $var; - echo "
".$header."
".$header.""; - } - - //close table row - function closeTDRow() { - return "
\n\n\n
".htmlentities($var)."
\n"; - break; - } - } - - //if variable is a NULL type - function varIsNULL() { - echo "NULL"; - } - - //if variable is a boolean type - function varIsBoolean($var) { - $var=($var==1) ? "TRUE" : "FALSE"; - echo $var; - } - - //if variable is an array type - function varIsArray($var) { - $var_ser = serialize($var); - array_push($this->arrHistory, $var_ser); - - $this->makeTableHeader("array", (empty($var)?"empty array":"array")); - if(is_array($var)) { - foreach($var as $key=>$value) { - $this->makeTDHeader("array",$key); - - //check for recursion - if(is_array($value)) { - $var_ser = serialize($value); - if(in_array($var_ser, $this->arrHistory, TRUE)) - $value = "*RECURSION*"; - } - - if(in_array(gettype($value),$this->arrType)) - $this->checkType($value); - else { - $value=(trim($value)=="") ? "[empty string]" : $value; - echo htmlentities($value); - } - echo $this->closeTDRow(); - } - } - else echo "".$this->error("array").$this->closeTDRow(); - array_pop($this->arrHistory); - echo ""; - } - - //if variable is an object type - function varIsObject($var) { - $var_ser = serialize($var); - array_push($this->arrHistory, $var_ser); - $this->makeTableHeader("object","object"); - - if(is_object($var)) { - $arrObjVars=get_object_vars($var); - foreach($arrObjVars as $key=>$value) { - - $value=(!is_object($value) && !is_array($value) && trim($value)=="") ? "[empty string]" : $value; - $this->makeTDHeader("object",$key); - - //check for recursion - if(is_object($value)||is_array($value)) { - $var_ser = serialize($value); - if(in_array($var_ser, $this->arrHistory, TRUE)) { - $value = (is_object($value)) ? "*RECURSION* -> $".get_class($value) : "*RECURSION*"; - - } - } - if(in_array(gettype($value),$this->arrType)) - $this->checkType($value); - else echo $value; - echo $this->closeTDRow(); - } - $arrObjMethods=get_class_methods(get_class($var)); - foreach($arrObjMethods as $key=>$value) { - $this->makeTDHeader("object",$value); - echo "[function]".$this->closeTDRow(); - } - } - else echo "".$this->error("object").$this->closeTDRow(); - array_pop($this->arrHistory); - echo ""; - } - - //if variable is a resource type - function varIsResource($var) { - $this->makeTableHeader("resourceC","resource",1); - echo "\n\n"; - switch(get_resource_type($var)) { - case "fbsql result": - case "mssql result": - case "msql query": - case "pgsql result": - case "sybase-db result": - case "sybase-ct result": - case "mysql result": - $db=current(explode(" ",get_resource_type($var))); - $this->varIsDBResource($var,$db); - break; - case "gd": - $this->varIsGDResource($var); - break; - case "xml": - $this->varIsXmlResource($var); - break; - default: - echo get_resource_type($var).$this->closeTDRow(); - break; - } - echo $this->closeTDRow()."\n"; - } - - //if variable is a database resource type - function varIsDBResource($var,$db="mysql") { - if($db == "pgsql") - $db = "pg"; - if($db == "sybase-db" || $db == "sybase-ct") - $db = "sybase"; - $arrFields = array("name","type","flags"); - $numrows=call_user_func($db."_num_rows",$var); - $numfields=call_user_func($db."_num_fields",$var); - $this->makeTableHeader("resource",$db." result",$numfields+1); - echo " "; - for($i=0;$i<$numfields;$i++) { - $field_header = ""; - for($j=0; $j".$field_name.""; - } - echo ""; - for($i=0;$i<$numrows;$i++) { - $row=call_user_func($db."_fetch_array",$var,constant(strtoupper($db)."_ASSOC")); - echo "\n"; - echo "".($i+1).""; - for($k=0;$k<$numfields;$k++) { - $tempField=$field[$k]->name; - $fieldrow=$row[($field[$k]->name)]; - $fieldrow=($fieldrow=="") ? "[empty string]" : $fieldrow; - echo "".$fieldrow."\n"; - } - echo "\n"; - } - echo ""; - if($numrows>0) - call_user_func($db."_data_seek",$var,0); - } - - //if variable is an image/gd resource type - function varIsGDResource($var) { - $this->makeTableHeader("resource","gd",2); - $this->makeTDHeader("resource","Width"); - echo imagesx($var).$this->closeTDRow(); - $this->makeTDHeader("resource","Height"); - echo imagesy($var).$this->closeTDRow(); - $this->makeTDHeader("resource","Colors"); - echo imagecolorstotal($var).$this->closeTDRow(); - echo ""; - } - - //if variable is an xml type - function varIsXml($var) { - $this->varIsXmlResource($var); - } - - //if variable is an xml resource type - function varIsXmlResource($var) { - $xml_parser=xml_parser_create(); - xml_parser_set_option($xml_parser,XML_OPTION_CASE_FOLDING,0); - xml_set_element_handler($xml_parser,array(&$this,"xmlStartElement"),array(&$this,"xmlEndElement")); - xml_set_character_data_handler($xml_parser,array(&$this,"xmlCharacterData")); - xml_set_default_handler($xml_parser,array(&$this,"xmlDefaultHandler")); - - $this->makeTableHeader("xml","xml document",2); - $this->makeTDHeader("xml","xmlRoot"); - - //attempt to open xml file - $bFile=(!($fp=@fopen($var,"r"))) ? false : true; - - //read xml file - if($bFile) { - while($data=str_replace("\n","",fread($fp,4096))) - $this->xmlParse($xml_parser,$data,feof($fp)); - } - //if xml is not a file, attempt to read it as a string - else { - if(!is_string($var)) { - echo $this->error("xml").$this->closeTDRow()."\n"; - return; - } - $data=$var; - $this->xmlParse($xml_parser,$data,1); - } - - echo $this->closeTDRow()."\n"; - - } - - //parse xml - function xmlParse($xml_parser,$data,$bFinal) { - if (!xml_parse($xml_parser,$data,$bFinal)) { - die(sprintf("XML error: %s at line %d\n", - xml_error_string(xml_get_error_code($xml_parser)), - xml_get_current_line_number($xml_parser))); - } - } - - //xml: inititiated when a start tag is encountered - function xmlStartElement($parser,$name,$attribs) { - $this->xmlAttrib[$this->xmlCount]=$attribs; - $this->xmlName[$this->xmlCount]=$name; - $this->xmlSData[$this->xmlCount]='$this->makeTableHeader("xml","xml element",2);'; - $this->xmlSData[$this->xmlCount].='$this->makeTDHeader("xml","xmlName");'; - $this->xmlSData[$this->xmlCount].='echo "'.$this->xmlName[$this->xmlCount].'".$this->closeTDRow();'; - $this->xmlSData[$this->xmlCount].='$this->makeTDHeader("xml","xmlAttributes");'; - if(count($attribs)>0) - $this->xmlSData[$this->xmlCount].='$this->varIsArray($this->xmlAttrib['.$this->xmlCount.']);'; - else - $this->xmlSData[$this->xmlCount].='echo " ";'; - $this->xmlSData[$this->xmlCount].='echo $this->closeTDRow();'; - $this->xmlCount++; - } - - //xml: initiated when an end tag is encountered - function xmlEndElement($parser,$name) { - for($i=0;$i<$this->xmlCount;$i++) { - eval($this->xmlSData[$i]); - $this->makeTDHeader("xml","xmlText"); - echo (!empty($this->xmlCData[$i])) ? $this->xmlCData[$i] : " "; - echo $this->closeTDRow(); - $this->makeTDHeader("xml","xmlComment"); - echo (!empty($this->xmlDData[$i])) ? $this->xmlDData[$i] : " "; - echo $this->closeTDRow(); - $this->makeTDHeader("xml","xmlChildren"); - unset($this->xmlCData[$i],$this->xmlDData[$i]); - } - echo $this->closeTDRow(); - echo ""; - $this->xmlCount=0; - } - - //xml: initiated when text between tags is encountered - function xmlCharacterData($parser,$data) { - $count=$this->xmlCount-1; - if(!empty($this->xmlCData[$count])) - $this->xmlCData[$count].=$data; - else - $this->xmlCData[$count]=$data; - } - - //xml: initiated when a comment or other miscellaneous texts is encountered - function xmlDefaultHandler($parser,$data) { - //strip '' off comments - $data=str_replace(array("<!--","-->"),"",htmlspecialchars($data)); - $count=$this->xmlCount-1; - if(!empty($this->xmlDData[$count])) - $this->xmlDData[$count].=$data; - else - $this->xmlDData[$count]=$data; - } - - function initJSandCSS() { - echo << - /* code modified from ColdFusion's cfdump code */ - function dBug_toggleRow(source) { - var target = (document.all) ? source.parentElement.cells[1] : source.parentNode.lastChild; - dBug_toggleTarget(target,dBug_toggleSource(source)); - } - - function dBug_toggleSource(source) { - if (source.style.fontStyle=='italic') { - source.style.fontStyle='normal'; - source.title='click to collapse'; - return 'open'; - } else { - source.style.fontStyle='italic'; - source.title='click to expand'; - return 'closed'; - } - } - - function dBug_toggleTarget(target,switchToState) { - target.style.display = (switchToState=='open') ? '' : 'none'; - } - - function dBug_toggleTable(source) { - var switchToState=dBug_toggleSource(source); - if(document.all) { - var table=source.parentElement.parentElement; - for(var i=1;i - - -SCRIPTS; - } - -} -?> diff --git a/daemonize/.htaccess b/daemonize/.htaccess deleted file mode 100644 index 3a42882..0000000 --- a/daemonize/.htaccess +++ /dev/null @@ -1 +0,0 @@ -Deny from all diff --git a/daemonize/daemonize.php b/daemonize/daemonize.php deleted file mode 100644 index 1f51708..0000000 --- a/daemonize/daemonize.php +++ /dev/null @@ -1,205 +0,0 @@ -&1 >> /path/to/logfile -Could be trivially modified to take the script and other globals -as arguments. But that seems a bit pointless to me. -*/ - -function usage_die( $pError ) { - die( "ERROR: $pError\nUsage: /path/to/bin/php /path/to/script/daemonize.php /path/to/script/daemon.php [--debug] [--nohup] [--pidfile=/path/to/lock/daemon.pid]\n\n" ); -} - -$PHP = $_SERVER['_']; # Remember to change the #! line above, too. -if( empty( $argv[1] ) || !file_exists( $argv[1] ) ) { - usage_die( 'daemon script not found' ); -} -$daemonScript = $argv[1]; - -$debug = FALSE; -if( !empty( $argv[2] ) ) { - // convert any remaining arguments into local variables - for( $i=2; $i < count( $argv ); $i++ ) { - $arg = preg_replace( '/^[-]*/', '', $argv[$i] ); - @list( $name, $val ) = @split( '=', $arg ); - ${$name} = (!empty( $val ) ? $val : TRUE); - } -} - - -if( empty( $pidfile ) ) { - $pidfile = '/var/lock/php-' . basename( $daemonScript,'.php' ).'.pid'; -} - -# Must contain any of nohup, perl, daemonise.pl, is_up.php and this script -# That you intend to use. -# Get the following values from "kill -l" or /usr/include/linux/signal.h -# Should be correct for most unixes. -$SIGTERM = 15; -$SIGKILL = 9; - -# If we've self-bootstrapped, then load the bot and run with it. -if ( !empty( $nohup ) ) { - to_log("arg detected, bootstrapping daemon through require."); - require( $daemonScript ); - to_log("killing daemon child."); - exit(0); -} - -# If we're already started, then don't bother running. -if( empty( $nohup ) ) { - $test = 0; - $lines = array(0, 0); - $pid_data = "unread"; - - #to_log("Testing for pre-existing... $pidfile"); # Can get spammy. - if (file_exists($pidfile)) { - $pid_data = file_get_contents($pidfile); - $lines = explode("\n", $pid_data); - if (!empty($lines[0]) && is_numeric($lines[0]) && !empty($lines[1]) && is_numeric($lines[1])) { - # Kill hung processes. - if ($lines[1] + 300 < time()) { // If it's an OLD pidfile... - to_log("$pidfile OLD pidfile found from $lines[1] - ".date('d/M/Y:H:i:s O',$lines[1]).": $pid_data"); - zero_file($pidfile); - - # If found, kill. If it won't die, kill it harder. Then give up. - # In practice, it can take a bit longer than 20 seconds to die, but it - # tries again in a minute's time, and works. - if (is_alive($lines[0])) { - to_log("killing and waiting a few secs..."); - posix_kill($lines[0], $SIGTERM); - # Loop for 10 seconds, or until it dies. - for ($i=0; $i<20 && is_alive($lines[0]); $i++) { - sleep(1); - if ($i == 10) { # After ten seconds, kill it harder. - to_log("Failed to kill it. Killing it harder!"); - posix_kill($lines[0], $SIGKILL); - } - } - if (is_alive($lines[0])) { - to_log("Failed to kill it. Giving up."); - exit(0); - } - } - } else { - #to_log("Young pidfile found."); # Can get spammy. - } - if (is_alive($lines[0])) { -// to_log( "DAEMON $lines[0] : $daemonScript running. Last seen at ".date('d/M/Y:H:i:s O', $lines[1] ) ); - exit(0); - } else { - to_log("$pidfile pidfile $lines[0] found, but process is dead."); - } - } - else { - to_log("$pidfile Bad format pidfile found, zeroing, restarting..."); - zero_file($pidfile); - } - } - else { - to_log("pidfile not found: $pidfile"); - } - to_log("NOT found running."); -} - -# If pcntl_fork() doesn't exist, we need to load ourselves in the background, then die. -if (!function_exists("pcntl_fork")) { - to_log("no pcntl, calling self with nohup and param"); - system("nohup $PHP $argv[1] -nohup &", $return); - if ($return == 0) { exit(0); } - # If there was a problem, we have one more ace in the hole - perl! - to_log("nohup failed with return $return, calling self with perl"); - system("perl ".dirname( $argv[0] )."daemonize.pl &", $return); - to_log("killing daemon parent with return $return"); - exit(0); -} - -# If we've got the right functions to play with, all the above becomes moot. -to_log("we have pcntl! Doing it all ourselves!"); - -# Replace file handles -$fh_unused = array(STDIN, STDOUT); - -ob_implicit_flush(); - -# Daemon Rule 1) Fork and exit the parent. -$pid = pcntl_fork(); -if ($pid == -1) { - die("could not fork"); -} -else if ($pid) { - exit(); # Kill the parent -} - -# Daemon Rule 2) become session leader, pg leader, no term -$session_id = posix_setsid(); -if (!$session_id) { - die("Could not detach from terminal."); -} - -# Daemon Rule 3) cd to / -if (!chdir('/')) { die("Could not cd to rootfs"); } - -# Daemon Rule 4) set file creation mask to 0 -$oldmask = umask(00); - -# Daemon Rule 5) Close unneeded file handles -foreach ($fh_unused as $fh) { - if (!fclose($fh)) { die("Unable to close $fh"); } -} - -# Daemon Rule 6) Set up signal handlers where necessary. -pcntl_signal(SIGTERM, "sig_handler"); -pcntl_signal(SIGHUP, "sig_handler"); - - -function sig_handler($signo) { - switch ($signo) { - case SIGTERM: - # handle shutdown tasks - die("Caught thud SIGTERM"); - break; - case SIGHUP: - # handle restart tasks - die("Caught thud SIGHUP"); - break; - default: - # handle all other signals - } -} - -# Meat of the daemon goes here. -to_log("requiring $daemonScript"); -chdir( dirname( $daemonScript ) ); -require_once( $daemonScript ); - -exit(0); - - -# Access functions. -function is_alive($pid) { - $output = array(); - exec( dirname( __FILE__ )."/is_up.sh $pid", $output ); - $result = $output[0]; - return (0 < $result); -} - -function to_log($string) { - error_log( date( 'd/M/Y:H:i:s O' )." - $string"); -} - -function zero_file($pidfile) { - $fp = fopen($pidfile, "w"); - fwrite($fp, ""); # Zero the pidfile. - fclose($fp); -} -?> diff --git a/daemonize/daemonize_lib.php b/daemonize/daemonize_lib.php deleted file mode 100644 index 6880c43..0000000 --- a/daemonize/daemonize_lib.php +++ /dev/null @@ -1,34 +0,0 @@ -loadStates(); - } - - public static function getDataset(){ - if( !isset( self::$uniqueInstance ) ){ - self::$uniqueInstance = new USStates(); - } - return self::$uniqueInstance->mStates; - } - - public function loadStates(){ - if ( empty( $this->mStates ) ) { - $this->mStates = array( - "AL" => tra("Alabama"), - "AK" => tra("Alaska"), - "AZ" => tra("Arizona"), - "AR" => tra("Arkansas"), - "CA" => tra("California"), - "CO" => tra("Colorado"), - "CT" => tra("Connecticut"), - "DE" => tra("Delaware"), - "DC" => tra("District of Columbia"), - "FL" => tra("Florida"), - "GA" => tra("Georgia"), - "HI" => tra("Hawaii"), - "ID" => tra("Idaho"), - "IL" => tra("Illinois"), - "IN" => tra("Indiana"), - "IA" => tra("Iowa"), - "KS" => tra("Kansas"), - "KY" => tra("Kentucky"), - "LA" => tra("Louisiana"), - "ME" => tra("Maine"), - "MD" => tra("Maryland"), - "MA" => tra("Massachusetts"), - "MI" => tra("Michigan"), - "MN" => tra("Minnesota"), - "MS" => tra("Mississippi"), - "MO" => tra("Missouri"), - "MT" => tra("Montana"), - "NE" => tra("Nebraska"), - "NV" => tra("Nevada"), - "NH" => tra("New Hampshire"), - "NJ" => tra("New Jersey"), - "NM" => tra("New Mexico"), - "NY" => tra("New York"), - "NC" => tra("North Carolina"), - "ND" => tra("North Dakota"), - "OH" => tra("Ohio"), - "OK" => tra("Oklahoma"), - "OR" => tra("Oregon"), - "PA" => tra("Pennsylvania"), - "PR" => tra("Puerto Rico"), - "RI" => tra("Rhode Island"), - "SC" => tra("South Carolina"), - "SD" => tra("South Dakota"), - "TN" => tra("Tennessee"), - "TX" => tra("Texas"), - "UT" => tra("Utah"), - "VT" => tra("Vermont"), - "VA" => tra("Virginia"), - "WA" => tra("Washington"), - "WV" => tra("West Virginia"), - "WI" => tra("Wisconsin"), - "WY" => tra("Wyoming"), - ); - } - return count( $this->mStates ); - } -} diff --git a/diff.php b/diff.php deleted file mode 100644 index 8b57d32..0000000 --- a/diff.php +++ /dev/null @@ -1,1027 +0,0 @@ - - * All Rights Reserved. See below for details and a complete list of authors. - * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See http://www.gnu.org/copyleft/lesser.html for details - * - * $Id$ - * @package util - */ - -/** - * required setup - */ -define('USE_ASSERTS', function_exists('assert')); -/** - * Class used internally by WikiDiff to actually compute the diffs. - * - * The algorithm used here is mostly lifted from the perl module - * Algorithm::Diff (version 1.06) by Ned Konz, which is available at: - * http://www.perl.com/CPAN/authors/id/N/NE/NEDKONZ/Algorithm-Diff-1.06.zip - * - * More ideas are taken from: - * http://www.ics.uci.edu/~eppstein/161/960229.html - * - * Some ideas are (and a bit of code) are from from analyze.c, from GNU - * diffutils-2.7, which can be found at: - * ftp://gnudist.gnu.org/pub/gnu/diffutils/diffutils-2.7.tar.gz - * - * Finally, some ideas (subdivision by NCHUNKS > 2, and some optimizations) - * are my own. - * @package util - * @subpackage WikiDiffEngine - */ -class _WikiDiffEngine -{ - public $edits; // List of editing operation to convert XV to YV. - public $xv = array(), $yv = array(); - public $xchanged, $ychanged; - function _WikiDiffEngine ($from_lines, $to_lines) - { - $n_from = sizeof($from_lines); - $n_to = sizeof($to_lines); - $endskip = 0; - // Ignore differences in line endings - for ($i = 0; $i < $n_from; $i++) - { - $from_lines[$i] = rtrim($from_lines[$i]); - } - for ($i = 0; $i < $n_to; $i++) - { - $to_lines[$i] = rtrim($to_lines[$i]); - } - // Ignore trailing and leading matching lines. - while ($n_from > 0 && $n_to > 0) - { - if ($from_lines[$n_from - 1] != $to_lines[$n_to - 1]) - break; - $n_from--; - $n_to--; - $endskip++; - } - for ( $skip = 0; $skip < min($n_from, $n_to); $skip++) - if ($from_lines[$skip] != $to_lines[$skip]) - break; - $n_from -= $skip; - $n_to -= $skip; - $xlines = array(); - $ylines = array(); - // Ignore lines which do not exist in both files. - for ($x = 0; $x < $n_from; $x++) - $xhash[$from_lines[$x + $skip]] = 1; - for ($y = 0; $y < $n_to; $y++) - { - $line = $to_lines[$y + $skip]; - $ylines[] = $line; - if ( ($this->ychanged[$y] = empty($xhash[$line])) ) - continue; - $yhash[$line] = 1; - $this->yv[] = $line; - $this->yind[] = $y; - } - for ($x = 0; $x < $n_from; $x++) - { - $line = $from_lines[$x + $skip]; - $xlines[] = $line; - if ( ($this->xchanged[$x] = empty($yhash[$line])) ) - continue; - $this->xv[] = $line; - $this->xind[] = $x; - } - // Find the LCS. - $this->_compareseq(0, sizeof($this->xv), 0, sizeof($this->yv)); - // Merge edits when possible - $this->_shift_boundaries($xlines, $this->xchanged, $this->ychanged); - $this->_shift_boundaries($ylines, $this->ychanged, $this->xchanged); - // Compute the edit operations. - $this->edits = array(); - if ($skip) - $this->edits[] = $skip; - $x = 0; - $y = 0; - while ($x < $n_from || $y < $n_to) - { - USE_ASSERTS && assert($y < $n_to || $this->xchanged[$x]); - USE_ASSERTS && assert($x < $n_from || $this->ychanged[$y]); - // Skip matching "snake". - $x0 = $x; - $ncopy = 0; - while ( $x < $n_from && $y < $n_to - && !$this->xchanged[$x] && !$this->ychanged[$y]) - { - ++$x; - ++$y; - ++$ncopy; - } - if ($x > $x0) - $this->edits[] = $x - $x0; - // Find deletes. - $x0 = $x; - $ndelete = 0; - while ($x < $n_from && $this->xchanged[$x]) - { - ++$x; - ++$ndelete; - } - if ($x > $x0) - $this->edits[] = -($x - $x0); - // Find adds. - if (isset($this->ychanged[$y]) && $this->ychanged[$y]) - { - $adds = array(); - while ($y < $n_to && $this->ychanged[$y]) - $adds[] = "" . $ylines[$y++]; - $this->edits[] = $adds; - } - } - if ($endskip != 0) - $this->edits[] = $endskip; - } - /* Divide the Largest Common Subsequence (LCS) of the sequences - * [XOFF, XLIM) and [YOFF, YLIM) into NCHUNKS approximately equally - * sized segments. - * - * Returns (LCS, PTS). LCS is the length of the LCS. PTS is an - * array of NCHUNKS+1 (X, Y) indexes giving the diving points between - * sub sequences. The first sub-sequence is contained in [X0, X1), - * [Y0, Y1), the second in [X1, X2), [Y1, Y2) and so on. Note - * that (X0, Y0) == (XOFF, YOFF) and - * (X[NCHUNKS], Y[NCHUNKS]) == (XLIM, YLIM). - * - * This function assumes that the first lines of the specified portions - * of the two files do not match, and likewise that the last lines do not - * match. The caller must trim matching lines from the beginning and end - * of the portions it is going to specify. - */ - function _diag ($xoff, $xlim, $yoff, $ylim, $nchunks) - { - $flip = false; - if ($xlim - $xoff > $ylim - $yoff) - { - // Things seems faster (I'm not sure I understand why) - // when the shortest sequence in X. - $flip = true; - list ($xoff, $xlim, $yoff, $ylim) - = array( $yoff, $ylim, $xoff, $xlim); - } - if ($flip) - for ($i = $ylim - 1; $i >= $yoff; $i--) - $ymatches[$this->xv[$i]][] = $i; - else - for ($i = $ylim - 1; $i >= $yoff; $i--) - $ymatches[$this->yv[$i]][] = $i; - $this->lcs = 0; - $this->seq[0]= $yoff - 1; - $this->in_seq = array(); - $ymids[0] = array(); - $numer = $xlim - $xoff + $nchunks - 1; - $x = $xoff; - for ($chunk = 0; $chunk < $nchunks; $chunk++) - { - if ($chunk > 0) - for ($i = 0; $i <= $this->lcs; $i++) - $ymids[$i][$chunk-1] = $this->seq[$i]; - $x1 = $xoff + (int)(($numer + ($xlim-$xoff)*$chunk) / $nchunks); - for ( ; $x < $x1; $x++) - { - $matches = $ymatches[$flip ? $this->yv[$x] : $this->xv[$x]]; - if (!$matches) - continue; - reset($matches); - while (list ($junk, $y) = each($matches)) - if (empty($this->in_seq[$y])) - { - $k = $this->_lcs_pos($y); - USE_ASSERTS && assert($k > 0); - $ymids[$k] = $ymids[$k-1]; - break; - } - while (list ($junk, $y) = each($matches)) - { - if ($y > $this->seq[$k-1]) - { - USE_ASSERTS && assert($y < $this->seq[$k]); - // Optimization: this is a common case: - // next match is just replacing previous match. - $this->in_seq[$this->seq[$k]] = false; - $this->seq[$k] = $y; - $this->in_seq[$y] = 1; - } - else if (empty($this->in_seq[$y])) - { - $k = $this->_lcs_pos($y); - USE_ASSERTS && assert($k > 0); - $ymids[$k] = $ymids[$k-1]; - } - } - } - } - $seps[] = $flip ? array($yoff, $xoff) : array($xoff, $yoff); - $ymid = $ymids[$this->lcs]; - for ($n = 0; $n < $nchunks - 1; $n++) - { - $x1 = $xoff + (int)(($numer + ($xlim - $xoff) * $n) / $nchunks); - $y1 = $ymid[$n] + 1; - $seps[] = $flip ? array($y1, $x1) : array($x1, $y1); - } - $seps[] = $flip ? array($ylim, $xlim) : array($xlim, $ylim); - return array($this->lcs, $seps); - } - function _lcs_pos ($ypos) - { - $end = $this->lcs; - if ($end == 0 || $ypos > $this->seq[$end]) - { - $this->seq[++$this->lcs] = $ypos; - $this->in_seq[$ypos] = 1; - return $this->lcs; - } - $beg = 1; - while ($beg < $end) - { - $mid = (int)(($beg + $end) / 2); - if ( $ypos > $this->seq[$mid] ) - $beg = $mid + 1; - else - $end = $mid; - } - USE_ASSERTS && assert($ypos != $this->seq[$end]); - $this->in_seq[$this->seq[$end]] = false; - $this->seq[$end] = $ypos; - $this->in_seq[$ypos] = 1; - return $end; - } - /* Find LCS of two sequences. - * - * The results are recorded in the vectors $this->{x,y}changed[], by - * storing a 1 in the element for each line that is an insertion - * or deletion (ie. is not in the LCS). - * - * The subsequence of file 0 is [XOFF, XLIM) and likewise for file 1. - * - * Note that XLIM, YLIM are exclusive bounds. - * All line numbers are origin-0 and discarded lines are not counted. - */ - function _compareseq ($xoff, $xlim, $yoff, $ylim) - { - // Slide down the bottom initial diagonal. - while ($xoff < $xlim && $yoff < $ylim - && $this->xv[$xoff] == $this->yv[$yoff]) - { - ++$xoff; - ++$yoff; - } - // Slide up the top initial diagonal. - while ($xlim > $xoff && $ylim > $yoff - && $this->xv[$xlim - 1] == $this->yv[$ylim - 1]) - { - --$xlim; - --$ylim; - } - if ($xoff == $xlim || $yoff == $ylim) - $lcs = 0; - else - { - // This is ad hoc but seems to work well. - //$nchunks = sqrt(min($xlim - $xoff, $ylim - $yoff) / 2.5); - //$nchunks = max(2,min(8,(int)$nchunks)); - $nchunks = min(7, $xlim - $xoff, $ylim - $yoff) + 1; - list ($lcs, $seps) - = $this->_diag($xoff,$xlim,$yoff, $ylim,$nchunks); - } - if ($lcs == 0) - { - // X and Y sequences have no common subsequence: - // mark all changed. - while ($yoff < $ylim) - $this->ychanged[$this->yind[$yoff++]] = 1; - while ($xoff < $xlim) - $this->xchanged[$this->xind[$xoff++]] = 1; - } - else - { - // Use the partitions to split this problem into subproblems. - reset($seps); - $pt1 = $seps[0]; - while ($pt2 = next($seps)) - { - $this->_compareseq ($pt1[0], $pt2[0], $pt1[1], $pt2[1]); - $pt1 = $pt2; - } - } - } - /* Adjust inserts/deletes of identical lines to join changes - * as much as possible. - * - * We do something when a run of changed lines include a - * line at one end and has an excluded, identical line at the other. - * We are free to choose which identical line is included. - * `compareseq' usually chooses the one at the beginning, - * but usually it is cleaner to consider the following identical line - * to be the "change". - * - * This is extracted verbatim from analyze.c (GNU diffutils-2.7). - */ - function _shift_boundaries ($lines, &$changed, $other_changed) - { - $i = 0; - $j = 0; - USE_ASSERTS && assert('sizeof($lines) == sizeof($changed)'); - $len = sizeof($lines); - $other_len = sizeof($other_changed); - while (1) - { - /* - * Scan forwards to find beginning of another run of changes. - * Also keep track of the corresponding point in the other file. - * - * Throughout this code, $i and $j are adjusted together so that - * the first $i elements of $changed and the first $j elements - * of $other_changed both contain the same number of zeros - * (unchanged lines). - * Furthermore, $j is always kept so that $j == $other_len or - * $other_changed[$j] == false. - */ - while ($j < $other_len && $other_changed[$j]) - $j++; - while ($i < $len && ! $changed[$i]) - { - USE_ASSERTS && assert('$j < $other_len && ! $other_changed[$j]'); - $i++; $j++; - while ($j < $other_len && $other_changed[$j]) - $j++; - } - if ($i == $len) - break; - $start = $i; - // Find the end of this run of changes. - while (++$i < $len && $changed[$i]) - continue; - do - { - /* - * Record the length of this run of changes, so that - * we can later determine whether the run has grown. - */ - $runlength = $i - $start; - /* - * Move the changed region back, so long as the - * previous unchanged line matches the last changed one. - * This merges with previous changed regions. - */ - while ($start > 0 && $lines[$start - 1] == $lines[$i - 1]) - { - $changed[--$start] = 1; - $changed[--$i] = false; - while ($start > 0 && $changed[$start - 1]) - $start--; - USE_ASSERTS && assert('$j > 0'); - while ($other_changed[--$j]) - continue; - USE_ASSERTS && assert('$j >= 0 && !$other_changed[$j]'); - } - /* - * Set CORRESPONDING to the end of the changed run, at the last - * point where it corresponds to a changed run in the other file. - * CORRESPONDING == LEN means no such point has been found. - */ - $corresponding = $j < $other_len ? $i : $len; - /* - * Move the changed region forward, so long as the - * first changed line matches the following unchanged one. - * This merges with following changed regions. - * Do this second, so that if there are no merges, - * the changed region is moved forward as far as possible. - */ - while ($i < $len && $lines[$start] == $lines[$i]) - { - $changed[$start++] = false; - $changed[$i++] = 1; - while ($i < $len && $changed[$i]) - $i++; - USE_ASSERTS && assert('$j < $other_len && ! $other_changed[$j]'); - $j++; - if ($j < $other_len && $other_changed[$j]) - { - $corresponding = $i; - while ($j < $other_len && $other_changed[$j]) - $j++; - } - } - } - while ($runlength != $i - $start); - /* - * If possible, move the fully-merged run of changes - * back to a corresponding run in the other file. - */ - while ($corresponding < $i) - { - $changed[--$start] = 1; - $changed[--$i] = 0; - USE_ASSERTS && assert('$j > 0'); - while ($other_changed[--$j]) - continue; - USE_ASSERTS && assert('$j >= 0 && !$other_changed[$j]'); - } - } - } -} -/** - * Class representing a diff between two files. - * - * @package wiki - * @subpackage WikiDiff - */ -class WikiDiff -{ - public $edits; - /** - * Compute diff between files (or deserialize serialized WikiDiff.) - */ - function WikiDiff($from_lines = false, $to_lines = false) - { - if ($from_lines && $to_lines) - { - $compute = new _WikiDiffEngine($from_lines, $to_lines); - $this->edits = $compute->edits; - } - else if ($from_lines) - { - // $from_lines is not really from_lines, but rather - // a serialized WikiDiff. - $this->edits = unserialize($from_lines); - } - else - { - $this->edits = array(); - } - } - /** - * Compute reversed WikiDiff. - * - * SYNOPSIS: - * - * $diff = new WikiDiff($lines1, $lines2); - * $rev = $diff->reverse($lines1); - * - * // reconstruct $lines1 from $lines2: - * $out = $rev->apply($lines2); - */ - function reverse ($from_lines) - { - $x = 0; - $rev = new WikiDiff; - for ( reset($this->edits); - $edit = current($this->edits); - next($this->edits) ) - { - if (is_array($edit)) - { // Was an add, turn it into a delete. - $nadd = sizeof($edit); - USE_ASSERTS && assert ($nadd > 0); - $edit = -$nadd; - } - else if ($edit > 0) - { - // Was a copy --- just pass it through. } - $x += $edit; - } - else if ($edit < 0) - { // Was a delete, turn it into an add. - $ndelete = -$edit; - $edit = array(); - while ($ndelete-- > 0) - $edit[] = "" . $from_lines[$x++]; - } - else die("assertion error"); - $rev->edits[] = $edit; - } - return $rev; - } - /** - * Compose (concatenate) WikiDiffs. - * - * SYNOPSIS: - * - * $diff1 = new WikiDiff($lines1, $lines2); - * $diff2 = new WikiDiff($lines2, $lines3); - * $comp = $diff1->compose($diff2); - * - * // reconstruct $lines3 from $lines1: - * $out = $comp->apply($lines1); - */ - function compose ($that) - { - reset($this->edits); - reset($that->edits); - $comp = new WikiDiff; - $left = current($this->edits); - $right = current($that->edits); - while ($left || $right) - { - if (!is_array($left) && $left < 0) - { // Left op is a delete. - $newop = $left; - $left = next($this->edits); - } - else if (is_array($right)) - { // Right op is an add. - $newop = $right; - $right = next($that->edits); - } - else if (!$left || !$right) - die ("assertion error"); - else if (!is_array($left) && $left > 0) - { // Left op is a copy. - if ($left <= abs($right)) - { - $newop = $right > 0 ? $left : -$left; - $right -= $newop; - if ($right == 0) - $right = next($that->edits); - $left = next($this->edits); - } - else - { - $newop = $right; - $left -= abs($right); - $right = next($that->edits); - } - } - else - { // Left op is an add. - if (!is_array($left)) die('assertion error'); - $nleft = sizeof($left); - if ($nleft <= abs($right)) - { - if ($right > 0) - { // Right op is copy - $newop = $left; - $right -= $nleft; - } - else // Right op is delete - { - $newop = false; - $right += $nleft; - } - if ($right == 0) - $right = next($that->edits); - $left = next($this->edits); - } - else - { - unset($newop); - if ($right > 0) - for ($i = 0; $i < $right; $i++) - $newop[] = $left[$i]; - $tmp = array(); - for ($i = abs($right); $i < $nleft; $i++) - $tmp[] = $left[$i]; - $left = $tmp; - $right = next($that->edits); - } - } - if (!$op) - { - $op = $newop; - continue; - } - if (! $newop) - continue; - if (is_array($op) && is_array($newop)) - { - // Both $op and $newop are adds. - for ($i = 0; $i < sizeof($newop); $i++) - $op[] = $newop[$i]; - } - else if (($op > 0 && $newop > 0) || ($op < 0 && $newop < 0)) - { // $op and $newop are both either deletes or copies. - $op += $newop; - } - else - { - $comp->edits[] = $op; - $op = $newop; - } - } - if ($op) - $comp->edits[] = $op; - return $comp; - } - /* Debugging only: - function _dump () - { - echo "
    "; - for (reset($this->edits); - $edit = current($this->edits); - next($this->edits)) - { - echo "
  1. "; - if ($edit > 0) - echo "Copy $edit"; - else if ($edit < 0) - echo "Delete " . -$edit; - else if (is_array($edit)) - { - echo "Add " . sizeof($edit) . "
      "; - for ($i = 0; $i < sizeof($edit); $i++) - echo "
    • " . htmlspecialchars($edit[$i]); - echo "
    "; - } - else - die("assertion error"); - } - echo "
"; - } - */ - /** - * Apply a WikiDiff to a set of lines. - * - * SYNOPSIS: - * - * $diff = new WikiDiff($lines1, $lines2); - * - * // reconstruct $lines2 from $lines1: - * $out = $diff->apply($lines1); - */ - function apply ($from_lines) - { - $x = 0; - $xlim = sizeof($from_lines); - for ( reset($this->edits); - $edit = current($this->edits); - next($this->edits) ) - { - if (is_array($edit)) - { - reset($edit); - while (list ($junk, $line) = each($edit)) - $output[] = $line; - } - else if ($edit > 0) - while ($edit--) - $output[] = $from_lines[$x++]; - else - $x += -$edit; - } - if ($x != $xlim) - ExitWiki(sprintf(tra ("WikiDiff::apply: line count mismatch: %s != %s"), $x, $xlim)); - return $output; - } - /** - * Serialize a WikiDiff. - * - * SYNOPSIS: - * - * $diff = new WikiDiff($lines1, $lines2); - * $string = $diff->serialize; - * - * // recover WikiDiff from serialized version: - * $diff2 = new WikiDiff($string); - */ - function serialize () - { - return serialize($this->edits); - } - /** - * Return true if two files were equal. - */ - function isEmpty () - { - if (sizeof($this->edits) > 1) - return false; - if (sizeof($this->edits) == 0) - return true; - // Test for: only edit is a copy. - return !is_array($this->edits[0]) && $this->edits[0] > 0; - } - /** - * Compute the length of the Longest Common Subsequence (LCS). - * - * This is mostly for diagnostic purposed. - */ - function lcs () - { - $lcs = 0; - for (reset($this->edits); - $edit = current($this->edits); - next($this->edits)) - { - if (!is_array($edit) && $edit > 0) - $lcs += $edit; - } - return $lcs; - } - /** - * Check a WikiDiff for validity. - * - * This is here only for debugging purposes. - */ - function _check ($from_lines, $to_lines) - { - $test = $this->apply($from_lines); - if (serialize($test) != serialize($to_lines)) - ExitWiki(tra ("WikiDiff::_check: failed")); - reset($this->edits); - $prev = current($this->edits); - $prevtype = is_array($prev) ? 'a' : ($prev > 0 ? 'c' : 'd'); - while ($edit = next($this->edits)) - { - $type = is_array($edit) ? 'a' : ($edit > 0 ? 'c' : 'd'); - if ( $prevtype == $type ) - ExitWiki(tra ("WikiDiff::_check: edit sequence is non-optimal")); - $prevtype = $type; - } - $lcs = $this->lcs(); - printf ("" . tra ("WikiDiff Okay: LCS = %s") . "\n", $lcs); - } -} -/** - * A class to format a WikiDiff as HTML. - * - * Usage: - * - * $diff = new WikiDiff($lines1, $lines2); // compute diff. - * - * $fmt = new WikiDiffFormatter; - * echo $fmt->format($diff, $lines1); // Output HTMLified standard diff. - * - * or to output reverse diff (diff's that would take $lines2 to $lines1): - * - * $fmt = new WikiDiffFormatter('reversed'); - * echo $fmt->format($diff, $lines1); - * - * @package wiki - * @subpackage WikiDiffFormatter - */ -class WikiDiffFormatter -{ - public $context_lines; - public $do_reverse_diff; - public $context_prefix, $deletes_prefix, $adds_prefix; - function WikiDiffFormatter ($reverse = false) - { - $this->do_reverse_diff = $reverse; - $this->context_lines = 0; - $this->context_prefix = '  '; - $this->deletes_prefix = '< '; - $this->adds_prefix = '> '; - } - function format ($diff, $from_lines) - { - $html = '
'; - $html .= $this->_format($diff->edits, $from_lines); - $html .= "
\n"; - return $html; - } - function _format ($edits, $from_lines) - { - $html = ''; - $x = 0; $y = 0; - $xlim = sizeof($from_lines); - reset($edits); - while ($edit = current($edits)) - { - if (!is_array($edit) && $edit >= 0) - { // Edit op is a copy. - $ncopy = $edit; - } - else - { - $ncopy = 0; - if (empty($hunk)) - { - // Start of an output hunk. - $xoff = max(0, $x - $this->context_lines); - $yoff = $xoff + $y - $x; - if ($xoff < $x) - { - // Get leading context. - $context = array(); - for ($i = $xoff; $i < $x; $i++) - $context[] = $from_lines[$i]; - $hunk['c'] = $context; - } - } - if (is_array($edit)) - { // Edit op is an add. - $y += sizeof($edit); - $hunk[$this->do_reverse_diff ? 'd' : 'a'] = $edit; - } - else - { // Edit op is a delete - $deletes = array(); - while ($edit++ < 0) - $deletes[] = $from_lines[$x++]; - $hunk[$this->do_reverse_diff ? 'a' : 'd'] = $deletes; - } - } - $next = next($edits); - if (!empty($hunk)) - { - if ( !$next || $ncopy > 2 * $this->context_lines) - { - // End of an output hunk. - $hunks[] = $hunk; - unset($hunk); - $xend = min($x + $this->context_lines, $xlim); - if ($x < $xend) - { - // Get trailing context. - $context = array(); - for ($i = $x; $i < $xend; $i++) - $context[] = $from_lines[$i]; - $hunks[] = array('c' => $context); - } - $xlen = $xend - $xoff; - $ylen = $xend + $y - $x - $yoff; - $xbeg = $xlen ? $xoff + 1 : $xoff; - $ybeg = $ylen ? $yoff + 1 : $yoff; - if ($this->do_reverse_diff) - list ($xbeg, $xlen, $ybeg, $ylen) - = array($ybeg, $ylen, $xbeg, $xlen); - $html .= $this->_emit_diff($xbeg,$xlen,$ybeg,$ylen, - $hunks); - unset($hunks); - } - else if ($ncopy) - { - $hunks[] = $hunk; - // Copy context. - $context = array(); - for ($i = $x; $i < $x + $ncopy; $i++) - $context[] = $from_lines[$i]; - $hunk = array('c' => $context); - } - } - $x += $ncopy; - $y += $ncopy; - } - return $html; - } - function _emit_lines($lines, $prefix, $color) - { - $html = ''; - reset($lines); - while (list ($junk, $line) = each($lines)) - { - $html .= "$prefix"; - $html .= "" . htmlspecialchars($line) . "\n"; - } - return $html; - } - function _emit_diff ($xbeg,$xlen,$ybeg,$ylen,$hunks) - { - $html = '
' - . '\n
' - . $this->_diff_header($xbeg, $xlen, $ybeg, $ylen) - . "
\n" - . '
'; - $prefix = array('c' => $this->context_prefix, - 'a' => $this->adds_prefix, - 'd' => $this->deletes_prefix); - $color = array('c' => '#ffffff', - 'a' => '#ccffcc', - 'd' => '#ffcccc'); - for (reset($hunks); $hunk = current($hunks); next($hunks)) - { - if (!empty($hunk['c'])) - $html .= $this->_emit_lines($hunk['c'], - $this->context_prefix, '#ffffff'); - if (!empty($hunk['d'])) - $html .= $this->_emit_lines($hunk['d'], - $this->deletes_prefix, '#ffcccc'); - if (!empty($hunk['a'])) - $html .= $this->_emit_lines($hunk['a'], - $this->adds_prefix, '#ccffcc'); - } - $html .= "
\n"; - return $html; - } - function _diff_header ($xbeg,$xlen,$ybeg,$ylen) - { - $what = $xlen ? ($ylen ? 'c' : 'd') : 'a'; - $xlen = $xlen > 1 ? "," . ($xbeg + $xlen - 1) : ''; - $ylen = $ylen > 1 ? "," . ($ybeg + $ylen - 1) : ''; - return "$xbeg$xlen$what$ybeg$ylen"; - } -} -/** - * A class to format a WikiDiff as a pretty HTML unified diff. - * - * Usage: - * - * $diff = new WikiDiff($lines1, $lines2); // compute diff. - * - * $fmt = new WikiUnifiedDiffFormatter; - * echo $fmt->format($diff, $lines1); // Output HTMLified unified diff. - * - * @package wiki - * @subpackage WikiUnifiedDiffFormatter - */ - -class WikiUnifiedDiffFormatter extends WikiDiffFormatter -{ - function WikiUnifiedDiffFormatter ($reverse = false, $context_lines = 3) - { - $this->do_reverse_diff = $reverse; - $this->context_lines = $context_lines; - $this->context_prefix = ' '; - $this->deletes_prefix = '-'; - $this->adds_prefix = '+'; - } - function _diff_header ($xbeg,$xlen,$ybeg,$ylen) - { - $xlen = $xlen == 1 ? '' : ",$xlen"; - $ylen = $ylen == 1 ? '' : ",$ylen"; - return "@@ -$xbeg$xlen +$ybeg$ylen @@"; - } -} -///////////////////////////////////////////////////////////////// -/* -if ($diff) -{ - if (get_magic_quotes_gpc()) { - $diff = stripslashes($diff); - } - $pagename = $diff; - $wiki = RetrievePage($dbi, $pagename, $WikiPageStore); -// $dba = OpenDataBase($ArchivePageStore); - $archive= RetrievePage($dbi, $pagename, $ArchivePageStore); - $html = ''; - if (is_array($wiki)) { - $html .= ""; - } else { - $html .= ""; - } - $html .= "\n"; - $html .= ''; - if (is_array($archive)) { - $html .= ""; - } else { - $html .= ""; - } - $html .= "
'; - $html .= tra ("Current page:"); - $html .= '"; - $html .= sprintf(tra ("version %s"), $wiki['version']); - $html .= ""; - $html .= sprintf(tra ("last modified on %s"), - date($datetimeformat, $wiki['lastmodified'])); - $html .= ""; - $html .= sprintf (tra ("by %s"), $wiki['author']); - $html .= ""; - $html .= tra ("None"); - $html .= "
'; - $html .= tra ("Archived page:"); - $html .= '"; - $html .= sprintf(tra ("version %s"), $archive['version']); - $html .= ""; - $html .= sprintf(tra ("last modified on %s"), - date($datetimeformat, $archive['lastmodified'])); - $html .= ""; - $html .= sprintf(tra ("by %s"), $archive['author']); - $html .= ""; - $html .= tra ("None"); - $html .= "

\n"; - if (is_array($wiki) && is_array($archive)) - { - $diff = new WikiDiff($archive['content'], $wiki['content']); - if ($diff->isEmpty()) { - $html .= '


[' . tra ("Versions are identical") . ']'; - } else { - //$fmt = new WikiDiffFormatter; - $fmt = new WikiUnifiedDiffFormatter; - $html .= $fmt->format($diff, $archive['content']); - } - } - GeneratePage('MESSAGE', $html, sprintf(tra ("Diff of %s."), - htmlspecialchars($pagename)), 0); -} -*/ - - - // \todo remove html hardcoded in diff2 - function diff2($page1, $page2) { - $page1 = split("\n", $page1); - $page2 = split("\n", $page2); - $z = new WikiDiff($page1, $page2); - if ($z->isEmpty()) { - $html = '

[' . tra("Versions are identical"). ']

'; - } else { - //$fmt = new WikiDiffFormatter; - $fmt = new WikiUnifiedDiffFormatter; - $html = $fmt->format($z, $page1); - } - return $html; - } - -?> diff --git a/geocalc/GeoCalc.class.php b/geocalc/GeoCalc.class.php deleted file mode 100644 index 2015f0b..0000000 --- a/geocalc/GeoCalc.class.php +++ /dev/null @@ -1,238 +0,0 @@ -GCDistance(38.9333,-94.3253,38.9314,-94.4876) . "
\n"; - * print "GCAzimuth: " . $oGC->GCAzimuth(38.9333,-94.3253,38.9314,-94.4876) . "
\n"; - * print "ApproxDistance: " . $oGC->ApproxDistance(38.9333,-94.3253,38.9314,-94.4876) . "
\n"; - * print "EllipsoidDistance: " . $oGC->EllipsoidDistance(38.80126649,-94.44590241,43.3368,-96.8755) . "

\n"; - * print "EllipsoidDistance In Miles: " . ConvKilometersToMiles($oGC->EllipsoidDistance(38.80126649,-94.44590241,43.3368,-96.8755)); - * - * @package geocalc - */ - -/** - * @package geocalc - */ -class GeoCalc { - - var $PI = 3.14159265359; - var $TWOPI = 6.28318530718; - var $DE2RA = 0.01745329252; - var $RA2DE = 57.2957795129; - var $ERAD = 6378.135; - var $ERADM = 6378135.0; - var $AVG_ERAD = 6371.0; - var $EPS = 0.000000000005; - var $KM2MI = 0.621371; - var $FLATTENING = 0; - - function GeoCalc() { - $this->FLATTENING = 1.0/298.26; // Earth flattening - // (WGS 1972) - return; - } - - function GCDistance($lat1, $lon1, $lat2, $lon2) { - $lat1 *= $this->DE2RA; - $lon1 *= $this->DE2RA; - $lat2 *= $this->DE2RA; - $lon2 *= $this->DE2RA; - $d = sin($lat1)*sin($lat2) + cos($lat1)*cos($lat2)*cos($lon1 - $lon2); - return ($this->AVG_ERAD * acos($d)); - } - - - function GCAzimuth($lat1, $lon1, $lat2, $lon2) { - $result = 0.0; - - $ilat1 = intval(0.50 + $lat1 * 360000.0); - $ilat2 = intval(0.50 + $lat2 * 360000.0); - $ilon1 = intval(0.50 + $lon1 * 360000.0); - $ilon2 = intval(0.50 + $lon2 * 360000.0); - - $lat1 *= $this->DE2RA; - $lon1 *= $this->DE2RA; - $lat2 *= $this->DE2RA; - $lon2 *= $this->DE2RA; - - if (($ilat1 == $ilat2) && ($ilon1 == $ilon2)) { - return result; - } - else if ($ilat1 == $ilat2) { - if ($ilon1 > $ilon2) - $result = 90.0; - else - $result = 270.0; - } - else if ($ilon1 == $ilon2) { - if ($ilat1 > $ilat2) - $result = 180.0; - } - else { - $c = acos(sin($lat2)*sin($lat1) + cos($lat2)*cos($lat1)*cos(($lon2-$lon1))); - $A = asin(cos($lat2)*sin(($lon2-$lon1))/sin($c)); - $result = ($A * $this->RA2DE); - - - if (($ilat2 > $ilat1) && ($ilon2 > $ilon1)) { - $result = $result; - } - else if (($ilat2 < $ilat1) && ($ilon2 < $ilon1)) { - $result = 180.0 - $result; - } - else if (($ilat2 < $ilat1) && ($ilon2 > $ilon1)) { - $result = 180.0 - $result; - } - else if (($ilat2 > $ilat1) && ($ilon2 < $ilon1)) { - $result += 360.0; - } - } - - return $result; - } - - function ApproxDistance($lat1, $lon1, $lat2, $lon2) { - $lat1 = $this->DE2RA * $lat1; - $lon1 = -$this->DE2RA * $lon1; - $lat2 = $this->DE2RA * $lat2; - $lon2 = -$this->DE2RA * $lon2; - - $F = ($lat1 + $lat2) / 2.0; - $G = ($lat1 - $lat2) / 2.0; - $L = ($lon1 - $lon2) / 2.0; - - $sing = sin($G); - $cosl = cos($L); - $cosf = cos($F); - $sinl = sin($L); - $sinf = sin($F); - $cosg = cos($G); - - $S = $sing*$sing*$cosl*$cosl + $cosf*$cosf*$sinl*$sinl; - $C = $cosg*$cosg*$cosl*$cosl + $sinf*$sinf*$sinl*$sinl; - $W = atan2(sqrt($S),sqrt($C)); - $R = sqrt(($S*$C))/$W; - $H1 = (3 * $R - 1.0) / (2.0 * $C); - $H2 = (3 * $R + 1.0) / (2.0 * $S); - $D = 2 * $W * $this->ERAD; - $return = ($D * (1 + $this->FLATTENING * $H1 * $sinf*$sinf*$cosg*$cosg - $this->FLATTENING*$H2*$cosf*$cosf*$sing*$sing)); - return $return; - } - - function EllipsoidDistance($lat1, $lon1, $lat2, $lon2) { - $distance = 0.0; - $faz = 0.0; - $baz = 0.0; - $r = 1.0 - $this->FLATTENING; - $tu1 = 0.0; - $tu2 = 0.0; - $cu1 = 0.0; - $su1 = 0.0; - $cu2 = 0.0; - $x = 0.0; - $sx = 0.0; - $cx = 0.0; - $sy = 0.0; - $cy = 0.0; - $y = 0.0; - $sa = 0.0; - $c2a = 0.0; - $cz = 0.0; - $e = 0.0; - $c = 0.0; - $d = 0.0; - - $cosy1 = 0.0; - $cosy2 = 0.0; - - if(($lon1 == $lon2) && ($lat1 == $lat2)) - return $distance; - $lon1 *= $this->DE2RA; - $lon2 *= $this->DE2RA; - $lat1 *= $this->DE2RA; - $lat2 *= $this->DE2RA; - - $cosy1 = cos($lat1); - $cosy2 = cos($lat2); - - if($cosy1 == 0.0) $cosy1 = 0.0000000001; - if($cosy2 == 0.0) $cosy2 = 0.0000000001; - - $tu1 = $r * sin($lat1) / $cosy1; - $tu2 = $r * sin($lat2) / $cosy2; - $cu1 = 1.0 / sqrt($tu1 * $tu1 + 1.0); - $su1 = $cu1 * $tu1; - $cu2 = 1.0 / sqrt($tu2 * $tu2 + 1.0); - $x = $lon2 - $lon1; - - $distance = $cu1 * $cu2; - $baz = $distance * $tu2; - $faz = $baz * $tu1; - - while(abs($d - $x) > $this->EPS) { - $sx = sin($x); - $cx = cos($x); - $tu1 = $cu2 * $sx; - $tu2 = $baz - $su1 * $cu2 * $cx; - $sy = sqrt($tu1 * $tu1 + $tu2 * $tu2); - $cy = $distance * $cx + $faz; - $y = atan2($sy, $cy); - $sa = $distance * $sx / $sy; - $c2a = - $sa * $sa + 1.0; - $cz = $faz + $faz; - if($c2a > 0.0) $cz = - $cz / $c2a + $cy; - $e = $cz * $cz * 2.0 - 1.0; - $c = ((-3.0 * $c2a + 4.0) * $this->FLATTENING + 4.0) * $c2a * $this->FLATTENING / 16.0; - $d = $x; - $x = (($e * $cy * $c + $cz) * $sy * $c + $y) * $sa; - $x = (1.0 - $c) * $x * $this->FLATTENING + $lon2 - $lon1; - } - - $x = sqrt((1.0 / $r / $r - 1.0) * $c2a + 1.0) + 1.0; - $x = ($x - 2.0) / $x; - $c = 1.0 - $x; - $c = ($x * $x / 4.0 + 1.0) / $c; - $d = (0.375 * $x * $x - 1.0) * $x; - $x = $e * $cy; - $distance = 1.0 - $e - $e; - $distance = (((($sy * $sy * 4.0 - 3.0) * $distance * $cz * $d / 6.0 - $x) * $d / 4.0 + $cz) * $sy * $d + $y) * $c * $this->ERAD * $r; - - return $distance; - } - - function getKmPerLonAtLat($dLatitude) { - // Thanks to Eric Iverson for this correction! Must convert degrees to radians... - $dLatitude *= $this->DE2RA; - return 111.321 * cos($dLatitude); - } - - function getLonPerKmAtLat($dLatitude) { - return 1 / $this->getKmPerLonAtLat($dLatitude); - } - - function getKmPerLat() { - return 111.000; - } - - function getLatPerKm() { - return 1 / $this->getKmPerLat(); - } - -} - -function ConvKilometersToMiles($dValue) { - return $dValue / 1.609344; -} - - -?> \ No newline at end of file diff --git a/geocalc/README b/geocalc/README deleted file mode 100644 index 36391e8..0000000 --- a/geocalc/README +++ /dev/null @@ -1,148 +0,0 @@ -GEOCALC-PHP -Geographic Distance and Azimuth Calculations in PHP -Version 1.2 - - -ABOUT THIS LIBRARY -This code was "ported" to PHP from Visual C++ by Steven Brendtro -of www.imaginerc.com. The original article and source code, written -by andyf4i can be found on CodeGuru.com at the following address: - -http://www.codeguru.com/Cpp/Cpp/algorithms/article.php/c5115/ - - -VERSION HISTORY -Version 1.0 - Initial release -Version 1.1 - Bug in getKMPerLonAtLat() fixed. - Thanks to Eric Iverson for finding it. -Version 1.2 - Bug in getLonPerKMAtLat() fixed. - - -FILES INCLUDED IN THIS RELEASE -GeoCalc.class.php - The actual PHP class -README - This file - - -ADDITIONAL PACKAGES -Also available on SourceForge.net under the GEOCALC-PHP project, -you can find a ZIP Code database (for FREE!) that you can use -for distance calculations between ZIP Codes. - -For increased speed when performing distance calculations using -MySQL, be sure to check out GEOCALC-UDF at SourceForge.net. -This UDF (User Defined Function) is designed for MySQL and has -been known to reduce query time up to 1000%! - - -METHODS - -# Basic Methods: -GeoCalc(); # Default Constructor - -GCDistance($lat1, $lon1, $lat2, $lon2); - # Using the Great Circle formula, calculate - # the distance in kilometers between - # Latitude/Longitude 1 and Latitude/Longitude 2 - -GCAzimuth($lat1, $lon1, $lat2, $lon2); - # Using the Great Circle formula, calculate the - # azimuth between Latitude/Longitude 1 and - # Latitude/Longitude 2 - -ApproxDistance($lat1, $lon1, $lat2, $lon2); - # Using the Ellipsoidal Approximation formula, - # calculate the distance in kilometers between - # Latitude/Longitude 1 and Latitude/Longitude 2 - -EllipsoidDistance($lat1, $lon1, $lat2, $lon2); - # Using the Ellipsoidal Distance formula, calculate - # the distance in kilometers between Latitude/Longitude 1 - # and Latitude/Longitude 2. This formula is the most - # accurate of the formulas. - -# Helper Methods - -getKmPerLonAtLat($dLatitude); - # Get the number of Kilometers per degree longitude - # at the given latitude. - -getLonPerKmAtLat($dLatitude); - # Get the number of degrees longitude per kilometer at the - # given latitude. - -getKmPerLat(); - # Get the number of kilometers per degree latitude (average - # of 111.0 kilometers per degree) - -getLatPerKm(); - # Get the number of degrees latitude per kilometer (average - # of 1/111 degrees per kilometer) - - -# Helper Function (not a method in the class, but a function) - -ConvKilometersToMiles($dValue); - # Convert a distance from kilometers to miles - # (km / 1.609344 = miles). - - -EXAMPLES - -Here are some example uses for this class: - - include_once("GeoCalc.class.php"); - - $oGC = new GeoCalc(); - - // Great Circle Distance - $dDist = $oGC->GCDistance(38.9333,-94.3253,38.9314,-94.4876); - - // Great Circle Azimuth - $dDist = $oGC->GCAzimuth(38.9333,-94.3253,38.9314,-94.4876); - - // Approximate Ellipsoid Distance - $dDist = $oGC->ApproxDistance(38.9333,-94.3253,38.9314,-94.4876); - - // Accurate Ellipsoid Distance - $dDist = $oGC->EllipsoidDistance(38.9333,-94.3253,38.9314,-94.4876); - - // Convert distance from kilometers to miles - $dDistMiles = ConvKilometersToMiles($dDist); - - // Advanced Calculation: - // The following will search for ZIP codes - // within a radius (roughly calculated) - - // Define the center of the search bounds... - $dLongitude = -94.44590241; - $dLatitude = 38.7996; - - // Define the maximum search distance - $dRadius = 100.00; // in kilometers - - // Calculate the boundary distance in degrees longitude / latitude - $dAddLat = $oGC->getLatPerKm() * $dRadius; - $dAddLon = $oGC->getLonPerKmAtLat($dLatitude) * $dRadius; - - // Calculate the boundaries - $dNorthBounds = $dLatitude + $dAddLat; - $dSouthBounds = $dLatitude - $dAddLat; - $dWestBounds = $dLongitude - $dAddLon; - $dEastBounds = $dLongitude + $dAddLon; - - print "Center Longitude: $dLongitude\n"; - print "Center Latitude: $dLatitude\n"; - print "Radius: $dRadius kilometers\n"; - - print "North Bounds: $dNorthBounds\n"; - print "South Bounds: $dSouthBounds\n"; - print "East Bounds: $dEastBounds\n"; - print "West Bounds: $dWestBounds\n"; - - // Sample SQL query stament based on above boundaries: - $strQuery = "SELECT * FROM PostalCodes " . - "WHERE Latitude > $dSouthBounds " . - "AND Latitude < $dNorthBounds " . - "AND Longitude > $dWestBounds " . - "AND Longitude < $dEastBounds"; - diff --git a/getid3/changelog.txt b/getid3/changelog.txt deleted file mode 100644 index a0be611..0000000 --- a/getid3/changelog.txt +++ /dev/null @@ -1,2488 +0,0 @@ -///////////////////////////////////////////////////////////////// -/// getID3() by James Heinrich // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// // -// changelog.txt - part of getID3() // -// See readme.txt for more details // -// /// -///////////////////////////////////////////////////////////////// - - » denotes a major feature addition/change - ¤ denotes a change in the returned structure -* Bugfix: denotes a fixed bug - -Version History -=============== - -1.7.7: [2006-06-25] Allan Hansen - * Bugfix: AAC static bitrate cache wrong result when parsing - several files. - * Bugfix: Do not return NULL video bitrate for ASF v3. - * Bugfix: getid3_lib::GetImageSize() broken => JPG module broken. - * Bugfix: Encoder options should now be returned with correct - "--alt-preset n" / "--alt-preset cbr n" when scanning more files. - * Bugfix: Shorten module not escapeshellarg() filenames (UNIX only). - * Bugfix: Filenames not escapeshellarg() for md5_data and - sha1_data (UNIX only). - * Bugfix: UNIX: head and tail called with -cNNN instead of "-c NNN". - » Added detection support for PDF and MS Office documents - (*.doc, *.xls, *.pps, etc) (thanks zeromassmediaØgmail*com) - ¤ Bugfix: ID3v2 "TDRC" frame now used as "year" in comments if TYER - unavailable (TYER is deprecated in ID3v2.4) - (thanks matthiasØpanczyk*org) - ¤ Removed GETID3_OS_DIRSLASH, replaced with DIRECTORY_SEPARATOR - * Bugfix: added LAME preset guessing for presets 410,420,440,490 - (thanks adminØlogbud*com) - * Bugfix: Added escapeshellarg() call in getid3_lib::hash_data - (thanks towbØgmx*net) - » TAR module no longer reads entire file into memory - » FLV module no longer reads entire file into memory - * Bugfix: added LAME preset guessing for presets 410,420,440,490 - (thanks adminØlogbud*com) - * Bugfix: Added escapeshellarg() call in getid3_lib::hash_data - (thanks towbØgmx*net) - * Bugfix: Error message when padding in FLAC files were used up. - * Bugfix: Shorten module not working under windows. - ¤ Bugfix: gmmktime() instead of mktime(). - ¤ Using gmmktime() instead of mktime() in ISO, ZIP, PNG and RIFF - modules to avoid E_STRICT notices with PHP5.1+. - * Bugfix: ['comments_html'] and ['comments'] contains different - value when having multiple tags (one of them ID3v1) and the - long field names. - -1.7.6: [2006-03-12] James Heinrich - * Rewrote getid3_lib::GetDataImageSize() to use GetImageSize() - instead of using code by filØrezox*com - * Bugfix: incorrect dimensions from disabled Quicktime tracks - (thanks m-1Øgmx*net) - * Bugfix: ['codec'] key warning in module.audio-video.asf.php - (thanks niel*archerØblueyonder*co*uk) - * Bugfix: undefined array in write.php - (thanks drewishØkatherinehouse*com) - * Bugfix: DeleteAPEtag() incorrectly failing when no tag present - (thanks drewishØkatherinehouse*com) - * Bugfix: ID3v2 writing frames with URL fields failing when URL - is not in ISO-8859-1 (thanks drewishØkatherinehouse*com) - * Bugfix: PHP notices on bad ID3v2 frames - (thanks cw264701Øohiou*edu) - * Bugfix: audio & video bitrates sometimes wrong in ASF files - (thanks kris_kauperØexcite*com) - -1.7.5: [2005-12-29] James Heinrich - » Added FLV (FLash Video) support -- new file: - module.audio-video.flv.php - (thanks Seth Kaufman for code) - » Real tags can now be written (previous Real tag writing - code was not supposed to be in public releases, as it - was not complete) - » GETID3_HELPERAPPSDIR now autodetected under Windows - ¤ ASF lyrics now returned under [comments][lyrics] - * Bugfix: removed "--lowpass xxxxx" info from guessed - LAME presets when source frequency <= 32kHz - * Bugfix: ID3v2 extended header errors - * Bugfix: missing ob_end_clean() in write.id3v2.php - (thanks rasherØgmail*com) - -1.7.4: [2005-05-04] James Heinrich - ¤ Added ['quicktime']['hinting'] key (boolean) - (thanks jonØwebignition*net) - * Bugfix: major UTF-8 to UTF-16/ISO-8859-1 conversion - bug (empty string returned) when using iconv_fallback - (thanks chrisØfmgp*com) - * Bugfix: Missing 'lossless' key in RIFF-WAV - (thanks bobbfwedØcomcast*net) - -1.7.3: [2005-04-22] James Heinrich - » Added TAR support -- new file: module.archive.tar.php - (thanks Mike Mozolin for code!) - » Added GZIP support -- new file: module.archive.gzip.php - (thanks Mike Mozolin for code!) - * Bugfix: demo.browse.php now displays embedded images - internally instead of passing local filename as IMG - SRC (should allow demo.browse.php to correctly show - embedded images over a network) - (thanks patpowermanØhotmail*com) - * Bugfix: minor UTF-8 display issues in demo.browse.php - * Bugfix: demo.browse.php now works even if the evil - setting magic_quotes_gpc is turned on - (thanks patpowermanØhotmail*com) - * Bugfix: incorrect MIDI playtime for some files - (thanks joelØoneporpoise*com) - * Bugfix: 'url_source' typo in module.tag.id3v2.php - (thanks richardlynchØusers*sourceforge*net) - * Bugfix: Quicktime 'mvhd' matrix values were wrong - (thanks webØbobbymac*net) - ¤ ID3v2 now returns xx/yy for ['track'] (if - available), with xx in ['tracknum'] and yy in - ['totaltracks']. Previously ['tracknum'] was not - available and ['track'] had only xx. - Bugfixes and improvements to /demo/demo.mysql.php: - - remix/version parsed from tags and stored in - database, can be used when renaming files - - track number can be used for renaming files - - -1.7.2: [2004-10-18] Allan Hansen - » Added support for WavPack v4.0+ - (thanks ahØartemis*dk) - » Removed code for parsing EXE files - (thanks ahØartemis*dk) - Removed file: module.misc.exe.php - * Bugfix: Large ID3v2 tags inside ASF not parsed - properly under PHP5. - * Bugfix: Certain Wavpack3 files failed under PHP5 due - to new undocumented tmpfile() limit (same problem as - above). - * Bugfix: New iTunes crashes PHP - temp fix - no tags - on those files. - * Bugfix: ['nsv']['NSVs']['framerate_index'] might be - wrong (thanks ahØartemis*dk) - * Bugfix: transparent color was wrong from truecolor - PNG (thanks ahØartemis*dk) - * Bugfix: Changed MPC SV7 header size from 30 to 28, - this will change hash values for MPC files - (thanks ahØartemis*dk) - * Bugfix: Changed MPC SV4-6 header size from 28 to 8, - this will change hash values for MPC files - (thanks ahØartemis*dk) - ¤ Trim/unset wavpack encoder_options to match 2.0.0b2 - output. - ¤ Commented-out unknown/unused values in NSV and ISO - modules (thanks ahØartemis*dk) - - -1.7.1b1: [July-26-2004] James Heinrich - » Added support for Apple Lossless Audio Codec - » Added support for RealAudio Lossless - » Added support for TTA v3 - » Added support for TIFF - New file: /getid3/module.graphic.tiff.php - » Modified iconv_fallback to work with UTF-8, UTF-16, UTF-16LE, - UTF-16BE and ISO-8859-1 even if iconv() and/or XML support is - not available. This means that iconv() is no longer required - for most users of getID3() - (thanks Jeremia, khleeØbitpass*com) - » Added support for Monkey's Audio v3.98+ (thanks ahØartemis*dk) - » Included new demo showing most-basic getID3() usage - New file: /demos/demo.basic.php - * Bugfix: LAME3.94+ presets cached incorrectly if multiple files - are scanned in one batch and first file is LAME3.93 or earlier - (thanks enoyandØyahoo*com) - * Bugfix: Added warning if compressed ID3v2 frame decompression - fails. (thanks Mike Billings) - * Bugfix: Assorted small fixes to ensure compatability with PHP5 - * Bugfix: ID3v1 genre "Blues" could not be written - (thanks Jeremia) - * Bugfix: ['bitrate_mode'] typo in module.audio-video.real.php - (thanks asukakenjiØusers*sourceforge*net) - * Bugfix: ['zip']['files'] is now populated with filenames even - if End Of Central Directory couldn't be parsed - * Bugfix: ['audio']['lossless'] was incorrect for FLAC - (thanks WaldoMonster) - * Bugfix: MD5 File was incorrect in directory browse mode for - /demo/getid3.browse.php - * Bugfix: PHP v5 compatability changes (float array keys, fread() - calls with zero data length) - (thanks getid3Øjsc*pp*ru) - * Bugfix: was dying if on compressed ID3v2 frames if - gzuncompress() function was unavailable - * Bugfix: ['vqf']['COMM'] was always empty - * Bugfix: MIDI playtime was missing for single-track MIDI files - * Bugfix: removed � characters from ['comments_html'] - (thanks p*quaedackersØplanet*nl) - * Bugfix: improved MIDI playtime accuracy - (thanks joelØoneporpoise*com) - * Bugfix: BMP subtypes 4 and 5 were not being identified - * Bugfix: frame_rate in AVI was incorrectly truncated to integer - * Bugfix: FLAC cuesheet track index was incorrect - (thanks tetsuo*yokozukaØoperamail*com) - ¤ ['quicktime']['display_scale'] now contains the playback scale - multiplier for QuickTime movies - a movie set to playback at - double-size will have "2" here. Other values are "1" and "0.5" - ¤ Added LAME preset guessing for --preset medium with v3.90.3 - (thanks phwipØfish*co*uk) - ¤ Added $encoding_id3v1 to allow for ID3v1 encodings other than - the standard ISO-8859-1 - ¤ Default AVI video bitrate_mode is now 'vbr' - (thanks eltoderØpisem*net) - Force getID3() to abort if Shorten files have ID3 or APE tags - (thanks ahØartemis*dk) - Editable textbox for parent directory in demo.browse.php - (thanks eltoderØpisem*net) - - -1.7.0-hotfix [2004-03-17] Allan Hansen - (hotfix version released by Allan Hansen) - * Bugfix: PHP 4.1.x compatiblity - fgets($fp) => fgets($fp, 1024) - * Bugfix: Added default charset to TextEncodingNameLookup() in - module.tag.id3v2.php - Ø Removed option_no_iconv - iconv() support is only a requirement for WMA/WMW/ASF, and for - destination encodings other than ISO-8859-1 and UTF-8, iconv is - not needed otherwise. New 'iconv_req' in GetFileFormatArray() - only set for WMA/WMV/ASF. analyze() now refuses to analyse - WMA/ASF file if iconv is not present. - iconv_fallback() only dies on internal errors not missing iconv() - - -1.7.0: [January-19-2004] James Heinrich - » Added support for RIFF/CDXA files (MPEG video in RIFF container - format (thanks chrisØdigitekdesign*com) - » Added support for TTA v2 (thanks ahØartemis*dk) - ¤ ID3v2 unsynchronisation scheme disabled by default because most - tag-reading programs cannot read unsynchronised tags. Can be - overridden by setting id3v2_use_unsynchronisation to true. - (thanks mikeØdelusion*org) - ¤ extention.*.php renamed to extension.*.php - (thanks tp62Øcornell*edu) - ¤ /demo/demo.check.php renamed to /demo/demo.browse.php - ¤ Added id3v2_paddedlength configuration parameter to WriteTags() - and renamed tag_language to id3v2_tag_language - ¤ MPEG audio layers are now represented as 1, 2 or 3 instead of - 'I', 'II', or 'III' - ¤ Added [audio][wformattag] and [video][fourcc] for WAV and AVI - ¤ Added [audio][streams] which contains one entry for each audio - stream present in the file (usually only one). The data is a - copy of what is usually found in [audio]. If there are multiple - audio streams then [audio] will contain a sum of the bitrates - of all audio streams, and the data format of the first stream - (if streams are of different data types) - ¤ Added BruteForce mode to mp3 scanning. Disabled by default as - it is extremely slow and only files that are broken enough to - not really play will gain any benefit from this. - ¤ Suppress '--resample xxxxx' appended to encoder options for mp3 - with low-quality presets for default sampling frequencies - ¤ Enhanced LAME preset guessing for pre-3.93 with a better lookup - table, --resample/--lowpass guessing (thanks phwipØfish*co*uk) - ¤ RIFF files with non-MP3 contents no longer have - [audio][encoder_options] set - ¤ Added [audio][encoder_options] to audio formats where possible - (including LiteWave, LPAC, OptimFROG, TTA) - ¤ Moved [quantization] and [max_prediction_order] from - [lpac][flags] to just [lpac] - ¤ WavPack flags are now parsed into [wavpack][flags] - * Bugfix: APEtags with ReplayGain information stored with comma- - seperated decimal values (ie "0,95" instead of "0.95") were - giving wrong peak and gain values - * Bugfix: Filesize > 2GB not always detected correctly - * Bugfix: Some ID3v2 frames had data key unset incorrectly - (thanks chrisØdigitekdesign*com) - * Bugfix: Warnings on empty-strings-only comments - * Bugfix: ID3v2 tag writing may have had incorrect padding length - if padded length less than current ID3v2 tag length and - merge_existing_data is false (thanks mikeØdelusion*org) - * Bugfix: hash_data() for SHA1 was broken under Windows - * Bugfix: BigEndian2Float()/LittleEndian2Float() were broken - * Bugfix: LAME header calculated track peaks were incorrect for - LAME3.94a15 and earlier - * Bugfix: AVIs with VBR MP3 audio data reported incorrect bitrate - and bitrate_mode - * Bugfix: AVIs sometimes had incorrect or missing video and total - bitrates - * Bugifx: AVIs sometimes had incorrect ['avdataend'] and - therefore also incorrect data hashes (md5_data, sha1_data) - * Bugfix: ID3v1 genreid no longer returned for Unknown genre - * Bugfix: ID3v1 SCMPX genres were broken - Modified LAME header parsing to correctly process peak track - value for LAME3.94a16+ (thanks Gabriel) - md5_file() and sha1_file() now work under Windows in PHP < 4.2.0 - and 4.3.0 respectively with helper apps - Default md5_data() tempfile location is now system temp directory - instead of same directory as file (thanks towbØtiscali*de) - Improved list of RIFF ['INFO'] comment key translations - More helpful error message when GETID3_HELPERAPPSDIR has spaces - /demo/demo.browse.php now autogets both MD5 and SHA1 hashes for - files < 50MB - Replaced PHP_OS comparisons with GETID3_OS_ISWINDOWS define - (thanks necroticØusers*sourceforge*net) - - -1.7.0b5: [December-29-2003] James Heinrich - » Windows only: Various binary files are now required for some - file formats, especially for tag writing, as well as md5sum - (and other) calculations. These binaries are now stored in the - directory defined as GETID3_HELPERAPPSDIR in getid3.php - (default is /helperapps/ parallel to /getid3/). - Note: This directory must not have any spaces in the pathname. - All neccesary files are available as a seperate download. - See /helperapps/readme.txt for more information - New file: /helperapps/readme.txt - » Unified tag-writing interface for all tag formats - New file: /getid3/write.php - /getid3/write.apetag.php - /getid3/write.id3v1.php - /getid3/write.id3v2.php - /getid3/write.lyrics3.php - /getid3/write.metaflac.php - /getid3/write.vorbiscomment.php - » Added support for Shorten - requires shorten binary (head.exe - is also required under Windows). - New file: /getid3/module.audio.shorten.php - » Added support for RKAU - New file: /getid3/module.audio.rkau.php - » Added (minimal) support for SZIP - New file: /getid3/module.archive.szip.php - » Added MySQL caching extention (thanks ahØartemis*dk) - New file: /getid3/extention.cache.mysql.php - » Added DBM caching extention (thanks ahØartemis*dk) - New file: /getid3/extention.cache.dbm.php - » Added sha1_data hash option (thanks ahØartemis*dk) - » Added option to allow getID3() to skip ID3v2 without parsing it - for faster scanning when ID3v2 data is not required. If you - want to enable this feature delete /getid3/module.tag.id3v2.php - (thanks ahØartemis*dk) - ¤ 8-bit WAV data now calculates MD5 checksums as normal, not - converting to signed data as before, so stored md5_data_source - in FLAC files will no longer match md5_data for the equivalent - decoded 8-bit WAV. A warning will be generated for 8-bit FLAC - files - ¤ Added option_no_iconv option to allow getID3() to work - partially without iconv() support enabled in PHP - (thanks ahØartemis*dk) - ¤ All '*_ascii' keys removed for ASF/WMA/WMV files - ¤ All 'ascii*' keys removed for ID3v2 tags - ¤ Ogg filetypes now return MIME of "application/ogg" instead of - the previous "application/x-ogg" - (thanks blakewattersØusers*sourceforge*net) - ¤ Force contents of ['id3v2']['comments'] to UTF-8 format from - whatever encoding each frame may have (text encoding can vary - from frame to frame in ID3v2) - ¤ MP3Gain information from APE tags suppressed from ['tags'] and - parsed into ['replay_gain'] - ¤ ReplayGain information (all formats) changed from "Radio" and - "Audiophile" to "Track" and "Album" respectively - ¤ ['volume'] and ['max_noclip_gain'] are now available in both - ['replay_gain']['track'] and ['replay_gain']['album'] for all - formats that calculate ReplayGain. - ¤ ['video']['total_frames'] is available for AVIs - ¤ All parsed ID3v2 frame data is now in ['id3v2'][XXXX][#] - (previously some frame types would have numeric array keys if - multiple instances of that frame type were allowed and other - frame types would not) - ¤ ASF/WMA "WM/Picture" images are now parsed in the same manner - as ID3v2 with the image (ex JPEG) data returned in [data] - rather than [value] - * Bugfix: Optional tag processing options were being ignored (ie - ID3v1 still processed even if option_tag_id3v1 == false) - (thanks ahØartemis*dk) - * Bugfix: fixed MultiByteCharString2HTML() for UTF-8 - * Bugfix: Quicktime files not always reporting video frame_rate - * Bugfix: False ID3v1 synch patterns in APE or Lyrics3 tags are - now detected and incorrect ID3v1 data not returned - (thanks sebastian_maresØusers*sourceforge*net for the idea) - * Bugfix: WMA9 Lossless now reported as lossless - * Bugfix: two typos in ID3v1 genre list - * Bugfix: MPEG-2/2.5 ABR/VBR MP3 files had doubled playtime - * Bugfix: MPEG-2/2.5 LayerII (ie MP2: 24/22.05/16kHz) files were - not detected due to incorrect frame length calculation - * Bugfix: MPEG LayerI files were not detected due to incorrect - frame length calculation (must be multiple of slot length) - Added alternative md5_data via system call - twice as fast. Needs - "getID3()-WindowsSupport" to work under Windows. - (thanks ahØartemis*dk) - ID3v2.4 compressed frames are now supported - php_uname() calls changed to use PHP_OS constant - Added SCMPX extensions to ID3v1 genres (0xF0-0xFE) - Obfuscated contributor email address in changelog and sourcecode - Added memory-saving EmbeddedLookup() function for lookup tables - in RIFF and ID3v2 modules (thanks ahØartemis*dk) - Major memory patches to many modules by using - $var = &$INFO_ARRAY_AT_SOME_INDEX - in place of large multi-dimensional array declarations. - Memory saved: RIFF: ~200kB; ID3v2: ~475kB; ASF: ~50kB etc. - (thanks ahØartemis*dk) - - -1.7.0b4: [November-19-2003] James Heinrich - » Support added for MPC files with old SV4-SV6 structure - » RealVideo now properly supported with resolution, framerate, etc - (thanks jcsston) - » RealAudio files with old-style file format (v2-v4) are now - fully supported - » Support added for DolbyDigital WAV files (thanks ahØartemis*dk) - ¤ ['RIFF'] is now ['riff'] to conform to make all root key names - lowercase - ¤ ['OFR'] is now ['ofr'] to conform to make all root key names - lowercase - ¤ ['tags_html'] is now available as a copy of ['tags'] but - with all text replaced with an HTML version of all characters - above chr(127), translated according to whatever the encoding - of the source tag is, in the HTML form Ӓ - ¤ CopyTagsToComments() is now available in getid3_lib - ¤ QuicktimeVR files now return a ['video']['dataformat'] of - 'quicktimevr' instead of 'quicktime' (thanks gtsØtsu*biz) - ¤ Quicktime video files with DivX, Xvid, 3ivx or MPEG4 video - streams now return those names as ['video']['dataformat'] - ¤ MPEG video files are now identified with ['video']['codec'] set - to either 'MPEG-1' or 'MPEG-2' (rather than just 'MPEG'). If you - see a file wrongly identified, please report it! - (thanks fccHandler) - ¤ All bitrate values in ['mpeg']['audio'] is now reported in bps - rather than kbps (ie 128000 instead of 128) for consistancy - ¤ AVIs with MP2 audio now report ['audio']['dataformat'] as 'mp2' - rather than 'wav' (thanks metalbrainØnetian*com) - ¤ Added ['md5_data_source'] for OptimFROG - ¤ AC3 in RIFF-WAV now identified with ['audio']['dataformat'] - returning 'ac3' - ¤ WavPack ['extra_bc'] now returned as integer - ¤ WavPack ['extras'] now returned as 3-element array of integers - ¤ MP3 ['audio']['encoder options'] now returns 'VBR' or 'CBR' only - (no bitrate) if no LAME preset is used, or 'VBR q??' where ?? is - a number 0-100 for Fraunhofer-encoded VBR MP3s - * Bugfix: VBR MP3s could have incorrect bitrate reported - * Bugfix: Quicktime files with MP4 audio were not returning - ['video']['dataformat'] (thanks robØmassive-interactive*nl) - * Bugfix: strpad vs str_pad typo in module.riff.php - (thanks nicojunØusers*sourceforge*net) - * Bugfix: ReplayGain information was often wrong for MPC files - * Bugfix: MD5 and other post-TAIL chunks were not being processed - in module.audio.optimfrog.php - * Bugfix: Undefined variable in table_var_dump() in demo/check.php - * Bugfix: QuickTime files now only return information in [audio] - or [video] if those exist in the file - * Bugfix: WavPack no longer tries to read entire compressed data - chunk - * Bugfix: Properly handle VBR MP3s with "Info" (rather than - "Xing") header frame. foobar2000 adds this to MP3 files when - "Fix MP3 Header" function is used (thanks ahØartemis*dk) - * Bugfix: Fraunhofer VBRI headers for MP3s were assuming 2-byte - entries for TOC rather than using stride, and were ignoring the - scaling value. (thanks sebastianØmaresweb*net) - Several QuickTime atoms have been added to an exclusion list - because they have been observed, but I have no idea what they - are supposed to do so I can't add real support for them, but - they should not generate warnings (robØmassive-interactive*nl) - Old MPC encoder (before v1.06) was return as v0.00, now returned - as 'Buschmann v1.7.0-v1.7.9 or Klemm v0.90-v1.05' - (thanks ahØartemis*dk) - Added check for magic_quotes_runtime and code to disable it if - neccesary (thanks stefan*kischkelØt-online*de) - Added 3ivx fourCCs to module.audio-video.quicktime.php - MP3 and AC3 streams are now parsed when contained inside RIFF-WAV - or RIFF-AVI container formats - Better detection of named presets in LAME 3.93/3.94 - - -1.7.0b3: [October-17-2003] James Heinrich - » AC-3 (aka Dolby Digital) is now supported. - New file: /getid3/module.audio.ac3.php - * Bugfix: ID3v2-writing function Unsynchronise() was broken, which - made ID3v2 tag containing binary data (typically pictures) get - corrupted. (thanks t*coombesØbendigo*vic*gov*au, - i*kuehlbornØndh*net, mikeØdelusion*org, mikeØftl*com) - * Bugfix: Zip comments now returned as array instead of string, - as they're supposed to be. - * Bugfix: Quicktime/MP4 files may have reported extremely low - bitrates (thanks spunkØdasspunk*com) - Improved double-ID3v1 check to prevent false detection when string - "TAG" is present in APE or Lyrics3 - Fixed /demo/simple.php - Fixed /demo/joinmp3.php - Fixed /demo/mimeonly.php - Fixed /demo/write.php - - -1.7.0b2: [October-15-2003] James Heinrich - » TTA Lossless Audio Compressor format now supported. - (http://tta.iszf.irk.ru) - New file: /getid3/module.graphic.tta.php - » PhotoCD (PCD) format now supported. Image data for the three - lowest resolutions (192x128, 384x256, 768x512) can be optionally - extracted. - New file: /getid3/module.graphic.pcd.php - ¤ RIFF-MP3 files now should return the same ['md5_data'] as the - identical MP3 file outside the RIFF container - ¤ Name of LAME preset used (if available, needs LAME v3.90+) - returned in ['mpeg']['audio']['LAME']['preset_used'] and also as - part of ['audio']['encoder_options'] - ¤ VQF module now sets ['audio']['encoder_options'] to i.e. CBR96 - ¤ MP3 module now sets ['audio']['encoder_options'] on CBR files - and all LAME-encoded files - ¤ MPC module now sets ['audio']['encoder_options'] - ¤ Monkey module now sets ['audio']['encoder_options'] - ¤ AAC module now sets ['audio']['encoder_options'] to profile name - ¤ ASF module now sets ['audio']['encoder_options'] - ¤ Ogg module adds ['audio']['encoder_options'] -b 128 on - Ogg Vorbis 1.0+ ABR files - ¤ Ogg module adds ['audio']['encoder_options'] -q N on - Ogg Vorbis 1.0+ VBR files 44k/48k sample rate/stereo files only. - ¤ Ogg module ['audio']['encoder_options'] "Nominal birate: 80k" to - other Ogg Vorbis files. - ¤ ID3v2 track number now returned as string (with leading zeros, - if present in data) rather than integer (thanks Plamen) - ¤ ASF module returns ['asf']['comments']['encoding_time_unix'] if - available (from WM/EncodingTime) - ¤ Fixed /demo/mysql.php and added some new features: - - encoder options - - ID3v2 "Encoded By" - - non-empty comments - - total entries in database summary (totals & averages) - - database version update - * Bugfix: 'UNICODE' iconv() charset changed to 'UTF-16LE' or - 'UTF-16BE' as appropriate - * Bugfix: iconv_fallback() function created in case iconv() fails - * Bugfix: fixed MD5 calls in demo/check.php - * Bugfix: reenabled detection of APE + Lyrics3 tags in same file - * Bugfix: ASF module now returns ID3v1 genre as string instead of - number - patch from Eugene Toder. - * Bugfix: ASF module now reads non-standard field names, - i.e. "date" as well as WM/Year - patch from Eugene Toder. - * Bugfix: ASF module now returns genre as-is if it is not a - standard ID3v1 genre (thanks wonderboy) - * Bugfix: Eliminated false-synch problem in MP3 module - * Bugfix: Fixed missing root ['bitrate'] for most formats - * Bugfix: ['audio']['compression_ration'] missing for MPC - (thanks WaldoMonster) - * Bugfix: NSV module died in 1.7.0b1 - * Bugfix: ASF module died in 1.7.0b1 when WM/Picture preset - * Bugfix: ASF tracknumber incorrect when specified by WM/Track - rather than WM/TrackNumber (thanks jgriffiniiiØhotmail*com) - * Bugfix: MPEG audio+video playtime should now be pretty accurate - (ie within 0.1% variation at most) - (thanks mgrimmØhealthtvchannel*org) - * Bugfix: ID3v2 not being copied to ['tags'] in some cases - * Bugfix: LAME CBR files with Info tag were being incorrectly - flagged as VBR (thanks Jojo) - * Bugfix: LAME tag not being detected for LAME 3.90 (original) - Changed regex pattern match for MP3 to include 3rd byte for more - reliable/accurate pattern matching - Added duplicate-ID3v1 tag checking (two ID3v1 tags, one after the - other) that has been known to occur with iTunes - (thanks towbØtiscali*de) - Added instructions for enabling iconv() support under Windows - Removed some unneccesary debugging code - Suppressed duplicate PHP warnings for missing include files - Included some missing dependencies in various files - /demo/audioinfo.class.php now copies ['audio']['encoder_options'] - - -1.7.0b1: [2003-09-28] Allan Hansen - This beta version was not made by James Heinrich. It was made by - Allan Hansen - please send bug reports on this - beta directly to me. - - James Heinrich will release 1.7.0 final, but it may take some time - to work out the bugs from the major rewrite. - - This version could be called getID3lite. It makes a lot of checks - optional and makes it easy to remove support for undesired formats - - It also is more library-like. Older versions of getID3() declared - an incredible amount of global scope functions and defined several - constants. 1.7.0beta1 still declares constants, but they are all - prepended by GETID3_. It declares no global scope functions - they - are all wrapped into classes. - - » Made getID3() depend on iconv library: compile PHP --with-iconv - » Created new directory structure - Moved all demos to demos/ - Moved all getID3() files to getid3/ - Renamed most files to module.something - Changed header in all module.something to explain what they do - Simply remove all modules you don't need - Wrapped all modules into classes - * Bugfix: Implemented misc patches from Eugene Toder - * Bugfix: Implemented misc patches from "six" - ¤ Added root key 'encoding' - ¤ Added prefix GETID3_ to all defined constants. - ¤ Wrapped getid3.php into getid3 class - ¤ Wrapped getid3.functions.php into getid3_lib class - Removed unused functions - Moved several functions away from getid3.functions.php and - into the files where they are actually used. - Renamed getid3.functions.php to getid3.lib.php - Moved getid3.rgad.php functions into getid3_lib - Moved getid3.getimagesize.php funcitons ingo getid3_lib - ¤ Moved getid3.ogginfo.php into ogg module - ¤ Combined GetTagOnly() and GetAllFileInfo() in method analyze - ¤ Removed redundant and unuseful root keys - 'file_modified_time' == filemtime($filename) - 'md5_file' == md5_file($filename) - 'exist' == file_exists($filename) - ¤ Changed root key ['tags'] from array of string to array of array - of comments. - Simplified code for detecting base path. - Removed ob_ from InitializeFilepointerArray(). That was really a - ugly HACK to get output from fopen. If user want the reason, - he should open the file himself! - Checking for APE tags before lyrics3 - makes Lyrics3 not depend - on APE tag. It seems to work on my test file. - Changed ['error'] and ['warning'] in multiple files to append to - array instead of appending to string. That simplified code in - getid3.php too. - Simplified clean-up procedure: simply remove all empty root keys - Setting tags in individual modules instead of main getid3.php - Made Bonk and ASF modules non-dependent on id3 modules - id3 - optional. - Rewrote HandleAllTags() - simplified and convert comments to - desired encoding. - Replaced all calls to RoughTranslateUnicodeToASCII() in ASF module - with a TrimConvert() method. This uses iconv() for conversion. - It also converts from UNICODE instead of UTF-16BE, as the spec - says it should. - Replaced all calls to RoughTranslateUnicodeToASCII() in id3v2 - module with iconv(). id3v2 module also reads - $ThisFileInfo['encoding'] and converts all comments to this - format. All other formats just add their comments in their - native charset, but every comment field in id3v2 can have a - different encoding, so this is needed. - Did same thing as above with ISO module. However - it does not - work. I need to find out how to specify big-endian unicode != - UNICODING encoding name given to iconv(). - Built-in assume mp3 format in getid3.php - Temporarily nuked root key ['comments'] and CopyCommentsToRoot() - Updated demo/audioinfo.class.php - Updated demo/check.php - some thing don't work! - Other demos are out of order! - - -1.6.5: [October-06-2003] James Heinrich - » Added support for LiteWave (thanks supportØclearjump*com) - Ø Split out speedup info from ['OFR']['OFR']['compression'] into - ['OFR']['OFR']['speedup'] - Ø If EXIF functions for JPEG not available, now warning not error - Ø ID3v2 track number now returned as string (with leading zeros, - if present in data) rather than integer (thanks Plamen) - * Bugfix: now correctly parses cbSize element of WAVEFORMATEX - structure (thanks supportØclearjump*com) - * Bugfix: ASF module now reads non-standard field names, - i.e. "date" as well as WM/Year - patch from Eugene Toder. - * Bugfix: ASF module now returns genre as-is if it is not a - standard ID3v1 genre (thanks wonderboy) - * Bugfix: ['audio']['compression_ration'] missing for MPC - (thanks WaldoMonster) - Prevent infinite loop in MP3 histogram if framelength == 0 - Added wFormatTag values 0x00FF and 0x2001 - 0x2005 - (thanks steveØheadbands*com) - Added "twos" and "sowt" FourCCs for Mac AIFC - - -1.6.4: [June-30-2003] James Heinrich - » Added support for free-format MP3s - (thanks Sebastian Mares for the idea) - » Compressed (Flash 6+) SWF files are now handled properly - (thanks alan*cheungØalumni*ust*hk) - » Added DeleteLyrics3() to getid3.lyrics3.php - » Added FixID3v1Padding() to getid3.putid3.php - » Added new simple MP3-splicing sample file - (thanks tommybobØmailandnews*com for the idea) - New file: getid3.demo.joinmp3.php - » Moved all contents of getid3.putid3.php into either - getid3.id3v1.php or getid3.id3v2.php or getid3.functions.php as - appropriate - Removed file: getid3.putid3.php - ¤ ['error'] and ['warning'] keys now return as arrays, not strings - ¤ New root key for all files: ['file_modified_time'] (UNIX time) - ¤ getid3.demo.scandir.php renamed to getid3.demo.mysql.php - ¤ New demo file returns the MIME type only for a single file - (thanks adminØe-tones*co*uk for the idea) - New file: getid3.demo.mimeonly.php - ¤ Added check for valid ID3v1 padding (strings should be padded - with null characters but some taggers incorrectly use spaces). - A warning will be generated if padding is invalid. New boolean - key ['id3v1']['padding_valid'] indicates padding validity. - ¤ CleanUpGetAllMP3info() removes more useless root keys for - unknown-format files - ¤ Extended LAME information in ['mpeg']['audio']['LAME'] is now - only returned for LAME v3.90+ - ¤ LAME-encoded MP3s now return - ['mpeg']['audio']['LAME']['long_version'] as well as - ['mpeg']['audio']['LAME']['short_version'] - these are identical - in LAME v3.90+ but older versions will report longer more - detailed version information if available - ¤ New Lyrics3 values: ['lyrics3']['raw']['offset_start'] and - ['lyrics3']['raw']['offset_end'] - ¤ New optional parameter on getAPEtagFilepointer() to scan from a - defined offset rather than end-of-file to allow scanning of APE - tags before Lyrics3 tags - ¤ ['tag_offset_start'] and ['tag_offset_end'] are now present in - ['ape'], ['lyrics3'], ['id3v1'] and ['id3v2'] - ¤ Numerous changes to the returned structure and content for La - files, including parsing the seektable (if applicable) and - parsing RIFF data occuring after the end of the compressed audio - data (notably RIFF comments) - (thanks mikeØbevin*de) - ¤ getSWFHeaderFilepointer() now has optional 3rd parameter - $ReturnAllTagData (default == false) which if set to true will - return data on all tags in ['swf']['tags'] - ¤ ['swf']['bgcolor'] now returns the 6-character string - representing the background color in HTML hex color format - (thanks ubergeekØubergeek*tv) - ¤ ['swf']['header']['frame_delay'] is no longer returned - ¤ getQuicktimeHeaderFilepointer() now has two additional optional - parameters: $ReturnAtomData (default == true) and - $ParseAllPossibleAtoms (default == false). Setting - $ReturnAtomData to false will reduce the size of the returned - data array by unsetting ['quicktime']['moov'] before returning. - Leaving $ParseAllPossibleAtoms as false now suppresses parsing - of several atom types that contain very large tables of data - that are not typically useful. Atom type suppressed are: - stts, stss, stsc, stsz, and stco - (thanks ubergeekØubergeek*tv) - ¤ ['fileformat'] no longer set to 'id3' if ID3v1 or ID3v2 tag - detected but no other data format recognized - * Bugfix: La files now return the correct values for - ['avdataoffset'] and ['avdataend'] and therefore the correct - values for ['md5_data'] - note that ['md5_data'] values will not - match values from previous versions of getID3() - the previous - versions were incorrect - (thanks mikeØbevin*de) - * Bugfix: A temporary file was being created in the web server's - root directory (not DocumentRoot) each time ['md5_data'] was - calculated, and not removed due to lack of permissions. Temp - file is now created (as it was supposed to be) in the directory - of the file being examined, or the system temp directory, and - properly removed when done. - * Bugfix: Several incorrect values were being returned inside - ['mpeg']['audio']['LAME'] (thanks bouvigneØmp3-tech*org) - * Bugfix: SWF frame rates values were usually incorrect. - (thanks alan.cheungØalumni*ust*hk and ubergeekØubergeek*tv) - * Bugfix: ID3v2.2 files always flagged 4 bytes of invalid padding - (thanks marcaØmac*com) - * Bugfix: Lyrics3 without ID3v1 was not working properly - * Bugfix: Lyrics3, APE & ID3v1 can all now exist in the same file. - A warning is issued if APE comes after Lyrics3 (because Lyrics3- - aware taggers probably are not APE-aware and therefore won't be - able to find the Lyrics3 tag) (thanks mp3gainØhotmail*com) - * Bugfix: WriteAPEtag() now writes the APE tag before any Lyrics3 - tags (if present) and removes any incorrect ones that are after - existing Lyrics3 tags (thanks mp3gainØhotmail*com) - * Bugfix: RIFF-WAVE file with incorrect NumberOfSamples values in - the 'fact' chunk no longer cause incorrect playtime calculation - (thanks stprasadØindusnetworks*com) - * Bugfix: getid3.demo.simple.php had undefined variables if the - file needed to be deep-scanned with assumeFormat - * Bugfix: fixed previously-incorrect ['avdataend'] values for APE - and Lyrics3 tags in some cases, which in some cases means that - ['md5_data'] is different than previously (now correct) - Much-improved detection of AAC-ADTS, which also means MP3 - format detection should now be nearly twice as fast - Truncated AVIs and WAVs are now reported - Number of new features and bugfixes in getid3.demo.mysql.php - Quicktime 'meta' atoms now parsed, so Quicktime MP4 files can now - return artist, title, album, etc (thanks spunkØdasspunk*com) - Consolidated all comments processing functions (processing the - ['comments'] and ['tags'] keys) into HandleAllTags() which now - also checks to ensure that APE tags are really better than ID3v2 - before using them in ['comments'] - Known issue with Meracl ID3 Tag Writer v1.3.4 truncating last byte - of MP3 file when appending new ID3v1 tag now specifically noted - (rather than generic Probably Truncated File message) - getid3.demo.mysql.php now stores last-modified time for each file - getid3.demo.mysql.php is now case-sensitive for filenames - getid3.demo.mysql.php can generate M3U playlists of any of the - groups of files it can select (duplicate filenames, tag types, - etc.) - getid3.demo.mysql.php can now find mismatched tag contents and - filenames - getid3.demo.check.php now shows total number of errors & warnings - GetFileFormatArray() now matches actual patterns for MP3 files - based on the first two bytes of the file, rather than just the - first one - Simplified DeleteAPEtag() and made it work properly with Lyrics3 - - -1.6.3: [May-17-2003] James Heinrich - » Added support for Bonk (thanks ahØartemis*dk) - New file: getid3.bonk.php - » Added support for AVR (thanks ahØartemis*dk) - New file: getid3.avr.php - ¤ Contents of getid3.id3.php moved to getid3.id3v1.php - Removed file: getid3.id3.php - ¤ Contents of getid3.frames.php moved to getid3.id3v2.php - Removed file: getid3.frames.php - ¤ Returned data structure documentation improved and updated and - now stored in getid3.structure.txt rather than getid3.readme.txt - New file: getid3.structure.txt - ¤ Now including the GNU General Public License in the distribution - as getid3.license.txt - New file: getid3.license.txt - ¤ Added new, optional, parameter to WriteAPEtag() (and also - GenerateAPEtag()) which must be set to TRUE if the values you - are passing are already UTF8-encoded, otherwise all data is - encoded to UTF8 by default. For all ASCII/ANSI data this value - should be left at the defaul value of FALSE. - ¤ Added third, optional, parameter to getID3v2Filepointer() - - $StartingOffset (default == 0) which can parse an ID3v2 tag - in a file at a position other than the start-of-file. - ¤ ['video']['pixel_aspect_ratio'] now returned when known - ¤ AVI files with WMA audio now return ['audio']['dataformat'] - of 'wma' rather than 'wav' - ¤ ASF-WMA files now return the artist value from WM/AlbumArtist - in ['comments']['artist'] (thanks msibbaldØsaebauld*com) - ¤ ASF-WMA files now return the 'author' value from - ['asf']['content_description'] in ['comments']['artist'] - instead of ['comments']['author'] - ¤ ASF-WMA files now return the 'description' value from - ['asf']['content_description'] in ['comments']['comment'] - instead of ['comments']['description'] - * Bugfix: APE tag writing with multiple values for a tag (more - than one ARTIST for example) was not being correctly written - (thanks ahØartemis*dk) - * Bugfix: CreateDeepArray() was returning an empty-string key as - the top-level returned value - ['iso']['files'] now directly - contains the file listing without an empty array in between. - * Bugfix: ID3v2 genreid was not being returned in some cases. - * Bugfix: APEv1 tags would generate error messages - * Bugfix: APE tags would sometimes show phantom second entry for - each item (title, artist, etc) with no data - * Bugfix: APE tag writing was not UTF8-encoding the data - - non-ASCII characters (above chr(127)) were being incorrectly - stored (thanks ahØartemis*dk) - * Bugfix: getid3.demo.scandir.php had undefined function error - * Bugfix: getid3.demo.scandir.php would not display list of files - with no tags - Added link to getid3.demo.check.php from list of specific-tags - files in getid3.demo.scandir.php - - -1.6.2: [May-04-2003] James Heinrich - » New official mirror site for getID3() - http://www.getid3.org - » Added basic support for SWF (Flash) (thanks n8n8Øyahoo*com) - New file: getid3.swf.php - » Added experimental support for parsing the audio portion of - MPEG-video files. I don't have any actual documentation for - this, so this part is experimental and not guaranteed accurate, - but it seems to be working OK as far as I have been able to test - it. Bug reports (or even better - documentation!) are welcome at - info@getid3.org - » Added new simple directory-scanning sample file - New file: getid3.demo.simple.php - » getid3.demo.write.php now writes APE tags as well. - ¤ Renamed getid3.write.php to getid3.demo.write.php - ¤ Renamed audioinfo.class.php to getid3.demo.audioinfo.class.php - ¤ getid3.php now automatically includes the getid3.functions.php - function library file, no need to include it seperately. - ¤ getLyrics3Filepointer() has been changed to be consistant with - all the other similar function structures - the parameters have - changed. The old function has been renamed to getLyrics3Data() - ¤ Added DeleteAPEtag() function to getid3.ape.php - ¤ HandleID3v1Tag() now only handles ID3v1. Lyrics3 processing is - now done by HandleLyrics3Tag() - ¤ If BitrateHistogram is enabled in getOnlyMPEGaudioInfo() it now - also returns ['mpeg']['audio']['version_distribution'] showing - the number of frames of each MPEG version (1, 2 or 2.5) - all - frames *should* be of the same MPEG version - ¤ getID3v1Filepointer() always returns TRUE now, even if it didn't - find a valid ID3v1 tag - ¤ getOnlyMPEGaudioInfo() now looks for MPEG sync in the first 128k - bytes rather than the first 64k bytes - ¤ Added dummy function GetAllMP3info() to generate warning not to - use that deprecated function. - ¤ ['video']['codec'] is now 'MPEG' for all MPEG video files (this - will change to 'MPEG-1' or 'MPEG-2' as soon as I figure out how - to determine that) (thanks jigalØspill*nl) - ¤ ['mpeg']['audio']['LAME']['mp3_gain'] renamed to - ['mpeg']['audio']['LAME']['mp3_gain_db'] (gain in dB) - ¤ Added ['mpeg']['audio']['LAME']['mp3_gain_factor'] (gain as a - multiplication factor) - ¤ Added support for Preset and Surround Info bytes from LAME VBR - tag (http://gabriel.mp3-tech.org/mp3infotag.html) - * Bugfix: APE tag writing would put the string 'Array' for all - values rather than the actual data (thanks ahØartemis*dk) - * Bugfix: Warning now generated for VBR MPEG-video files because - getID3() cannot determine average bitrate. If you know of - documentation that would tell me how to do this, please email - info@getid3.org - * Bugfix: Replay Gain values from Vorbis comments are now - returned in ['replay_gain'] (and not in ['comments']) - (thanks ahØartemis*dk) - * Bugfix: Replay Gain values from APE comments are now correctly - returned in ['replay_gain'] (thanks ahØartemis*dk) - * Bugfix: getid3.demo.check.php is now case-insensitive when - assuming a format for a corrupted file if standard detection - does not identify the file type. - * Bugfix: RIFF comments were overwriting/suppressing ID3 comments - for RIFF-MP3 files (thanks wmØwofuer*com) - * Bugfix: RIFF-MP3 files with 'RMP3' chunks instead of 'WAVE' were - not being correctly identified. - * Bugfix: ID3v2 padding shorter than the length of an ID3v2 frame - header was not correctly detected - * Bugfix: getid3.demo.check.php now does in-depth scanning for MP2 - and MP1 files the same as for MP3 files based on file extension - if a MPEG-audio structure isn't found immediately at the start - of the file - * Bugfix: removed condition where RIFF-WAV was being scanned for - MPEG-audio signature when it shouldn't be present (non-MP3 WAV) - * Bugfix: ASF files were not always showing correct audio datatype - * Bugfix: array_merge_clobber() and array_merge_noclobber() were - not being conditionally defined in getid3.functions.php - (thanks rich.martinØreden-anders*com) - * Bugfix: stream_numbers was not being correctly returned in - bitrate_mutual_exclusion_object chunks of ASF files - * Bugfix: Added support for 24kHz and 12kHz audio in ASF files - * Bugfix: Removed possible undefined offset error in MP3s where - cannot find synch before end of file - * Bugfix: Removed potential out-of-memory crash situation when - parsing Real files with chunks larger than the available memory - (thanks jigalØspill*nl) - * Bugfix: ID3v1 was incorrectly taking precedence over ID3v2 in - the ['comments'] array (thanks lionelflØwanadoo*fr) - * Bugfix: No longer calculates overall bitrate and playtime for - VBR MPEG video files based on the audio bitrate. - * Bugfix: AssumeFormat was not working properly - Added summary footer line to getid3.demo.check.php - Added '.mpeg' to the list of assume-format-from-filenames list in - getid3.demo.check.php - MPEG-video files now more reliably detected - A number of additional features have been added to - getid3.demo.scandir.php - Added many RIFF-AVI audio types and fourcc video types to the - lookup functions in getid3.riff.php - Now identifes files with Lyrics3 v1 tags that are of incorrect - length (v1 Lyrics3 is supposed to be 5100 bytes long, but - [unknown program] writes variable-length tags (which is illegal - for Lyrics3 v1)). getID3() now correctly parses these tags and - issues a warning. - Split GetFileFormat() to GetFileFormat() and GetFileFormatArray() - HTML colors in getid3.demo.check.php are now defined as constant - variables at the top of the file (if you want to change them) - Added support for OptimFROG v4.50x (non-alpha) (new header fields) - (thanks floringhidoØyahoo*com) - Added support for Lossless Audio v0.4 (thanks mikeØbevin*de) - - -1.6.1: [March-03-2003] James Heinrich - » Added support for writing APE v2. - WriteAPEtag() in getid3.ape.php - NOTE: APE v1 writing support will *not* be added to future - versions of getID3() - (thanks ahØartemis*dk and adamØphysco*com for the idea) - » Added support for AIFF (Audio Interchange File Format) including - AIFF, AIFC and 8SVX (thanks ahØartemis*dk for the idea) - Removed file: getid3.aiff.php - » Added support for OptimFROG (v4.50a and v4.2x) - (thanks ahØartemis*dk for the idea) - New file: getid3.optimfrog.php - » Added support for WavPack (thanks ahØartemis*dk for the idea) - » Added support for LPAC (thanks ahØartemis*dk for the idea) - » Added support for NeXT/Sun .au format - New file: getid3.au.php - » Added support for Creative SoundBlaster VOC format - New file: getid3.voc.php - » Added support for the BWF (Broadcast Wave File) RIFF chunks - "bext" and "MEXT" (thanks Ryan and njhØsurgeradio*co*uk) - » Added support for the CART (Broadcast Wave File) RIFF chunks - (thanks Ryan) - » Added getid3.demo.scandir.php - a sample recursive scanning demo - that scans every file in a given directory, and all sub- - directories, and stores the resulting data in MySQL database, - and then displays a list of duplicate files based on md5_data - ¤ ['md5_data_source'] now contains the MD5 value for the original - uncompressed data for formats that store that information - (currently only FLAC v0.5+). ['md5_data'] (if chosen to be - calculated) will contain the calculated MD5 value for the - compressed file. To check if 2 files are identical in every way, - including all comments: compare ['md5_file']. To check if two - files were compressed from the same source file: compare - ['md5_data_source']. To check if the compressed audio/video data - of two files is identical, even if comments or even the - container file format is different (MP3 in RIFF container, - FLAC in Ogg container, etc): compare ['md5_data']. - ¤ ['md5_data'] for 8-bit WAV files is now calculated based on a - converted version of the data from unsigned to signed (MSB - inverted) to match the MD5 value calculated by FLAC - ¤ New optional parameter added to GetAllFileInfo() - - $MD5dataIfMD5SourceKnown (default: false). If false the md5_data - value will NOT be calculated for files (such as FLAC) that have - ['md5_data_source'] set, even if $MD5data == true. - (thanks ahØartemis*dk) - ¤ getid3.check.php renamed to getid3.demo.check.php - ¤ Added GetTagOnly() function to getid3.php - similar to - GetAllFileInfo() except only takes a filename as a parameter and - only returns ID3v2, APE, Lyrics3 and ID3v1 tag information - no - attempt is made to parse the data contents of the file at all. - (thanks Phil for the idea) - ¤ Added ['audio']['lossless'] and ['video']['lossless'] for all - formats (when known). Both are boolean values - true means the - data is lossless-compressed, false means the data is lossy- - compressed. - ¤ Added ['audio']['compression_ratio'] and/or - ['video']['compression_ratio'] for all formats. Returns a number - (usually) less than 1, where 1 represents no compression and 0.5 - represents a compressed file half the size of the original file - ¤ Added ['video']['bits_per_sample'] to all video formats (when - known) - ¤ Added ['video']['frame_rate'] to all video formats (when known) - ¤ ['fileformat'] set to 'mp1' or 'mp2' instead of 'mp3' when - ['audio']['dataformat'] is one of those (thanks ahØartemis*dk) - ¤ Added 4th parameter to md5_data(), $invertsign, which will invert - the MSB of each byte before MD5'ing. This is needed for 8-bit - WAV files because FLAC calculates the stored MD5 value on - signed data rather than the original byte values. ['md5_data'] - of an 8-bit WAV will now match the ['md5_data_source'] value - (thanks lichvarmØphoenix*inf*upol*cz) - ¤ ['ape']['items']['data'] and ['ape']['items']['data_ascii'] now - contains an array of values, if the tag contains UTF-8 text (as - opposed to binary data) - ¤ ['mpeg']['audio']['bitratemode'] renamed to - ['mpeg']['audio']['bitrate_mode'] - * Bugfix: Removed potential bug that could replace all MP3 file - contents with only the new ID3v2 tag in getid3.putid3.php - * Bugfix: md5_data values calculated for RIFF (WAV, AVI) files - were incorrect (thanks ahØartemis*dk) - * Bugfix: MP3 data in an MP4 wrapper fileformat could not identify - bitrate (thanks ahØartemis*dk) - * Bugfix: ['audio'] and/or ['video'] keys would sometimes get - removed even if not empty - * Bugfix: Prevented creation of null entries in - ['RIFF']['WAVE']['INFO'] if a comment entry was not present - * Bugfix: Potential infinite-loop condition in getid3.ogg.php - (thanks afshin.behniaØsbcglobal*net) - * Bugfix: Ogg files with illegal ID3v1 (and/or APE or Lyrics3) - tags were not finding the last Ogg page - (thanks afshin.behniaØsbcglobal*net) - * Bugfix: replay-gain values not properly set from LAME tag - * Bugfix: RIFF-MP3 had incorrect md5_data - * Bugfix: the LAME DLL CBR problem of not re-writing the LAME - frame at the beginning of the data is now detected for MP3s - with ID3v2 tags as well - * Bugfix: APE tags with multiple values (ie multiple entries in - the "artist" tag) are now shown properly in ['ape']['items'] - * Bugfix: fixed condition where APE tag with no ID3v1 tag could be - mistaken for APE tag with ID3v1 (and incorrectly parsed) - * Bugfix: added warning if ID3v2 frame has zero-length data - (thanks cmassetØclubinternet*fr) - * Bugfix: getid3.frames.php looking for non-existant key in USER - frames - Improved detection of RIFF-MP3 data. [unknown program] encodes - RIFF-WAV data with a chunk name of 'RMP3' instead of the - standard 'RIFF' - Encoder now returned in both ['comments'] and ['audio']['encoder'] - for RIFF-WAV files with an INFO.ISFT chunk - Generate a warning for FLAC files encoded with v0.3 or v0.4 - because audio_signature is not calculated during encoding - (thanks ahØartemis*dk) - Modified getid3.check.php to display md5_data_source as well as - md5_file and md5_data if display-MD5 mode is selected - Modified getid3.check.php to assume-format based on file extension - in browse mode if fileformat is found to be 'id3' (formerly only - if the fileformat was null) - Changed scaling of BitrateColor() from representing 1-256kbps to - representing 1-768kbps for better display of high-bitrate files, - specifically lossless-compressed CD-audio (FLAC, LA, etc) - - -1.6.0: [January-30-2003] James Heinrich - » Added support for OggFLAC (FLAC data stored in an Ogg container) - (thanks ahØartemis*dk for the idea) - » Added support for Speex (the data stored in an Ogg container) - » Comments are now available in the root 2-dimensional array - ['comments'] - each entry in this array will contain one or more - strings. For example, if there are two artists then - ['comments']['artist'][0] will contain the first one and - ['comments']['artist'][1] the other. All keys are forced - lowercase. Comments will be stored in the ['comments'] array in - this order of precedence: - 1) Native format tags (ASF, VQF, NSV, RIFF, Quicktime, Vorbis) - 2) APE tags - 3) ID3v2 - 4) Lyrics3 - 5) ID3v1 - Lower-priority tags will not overwrite or append existing values - of higher-priority tags (for example, 'artist' in ID3v1 will be - ignored if already specified in APE), but missing values will be - filled in (for example, if 'album' is specified in ID3v2 but not - in APE, it will be included in the ['comments'] array). - Note: Root keys (['title'], ['artist'], etc) are NOT available - in this or future versions of getID3(). - (thanks ahØartemis*dk) - » MD5 hashes are now available for all formats for both the entire - file (['md5_file']) and the portion of the file containing only - the audio/video data, stripped of all prepended/appended tags - like ID3v2, ID3v1, APE, etc - ['md5_data'] - (thanks ahØartemis*dk for alternate md5_file() function that - runs on UNIX system running PHP < 4.2.0) - NOTE: Ogg files require the use of vorbiscomment to obtain the - md5_data value. vorbiscomment must be downloaded from - http://www.vorbis.com/download.psp and placed in the getID3() - directory. All Ogg formats (Vorbis, OggFLAC, Speex) are affected - by this problem, but only OggVorbis files can be processed with - vorbiscomment. OggFLAC and Speex files will be processed by - getID3(), but this may result in an incorrect value for md5_data - in the event that VorbisComments are larger than 1 page (4-8kB). - NOTE: md5_data for Ogg will not work if PHP is running in Safe - Mode - » There is now a wrapper class available, written by Allan Hansen, - which should simplify extracting most common basic information - (such as format, bitrate, comments). - New file: audioinfo.class.php - » OggWrite() in getid3.ogginfo.php has been replaced with a new - version that uses vorbiscomment to write the comments, because - of a reported bug that can corrupt OggVorbis files such they - cannot be played. - NOTE: Ogg comment writing now requires the use of vorbiscomment - which must be downloaded from http://www.vorbis.com/download.psp - and placed in the getID3() directory. - NOTE: Ogg comment writing will not work if PHP is running in - Safe Mode - ¤ New root key ['tags'] is now always returned for all formats. - It is an array that may contain any of: - * Native format tags: 'vqf', 'riff', 'vorbiscomment', 'asf', - 'nsv', 'real', 'midi', 'zip', 'quicktime' - * Appended data tags: 'ape', 'lyrics3', 'id3v2', 'id3v1' - ¤ New root key ['audio'] is an array containing any or all of: - codec, channels, channelmode, bitrate, bits_per_sample, - dataformat, bitrate_mode, sample_rate, encoder - Note: This replaces several root keys, including: - bitrate_audio, bits_per_sample, frequency, channels - ¤ New root key ['video'] is an array containing any or all of: - bitrate_mode, bitrate, codec, resolution_x, resolution_y, - resolution_y, frame_rate, encoder - Note: This replaces several root keys, including: - bitrate_video, resolution_x, resolution_y, frame_rate - ¤ ['id3']['id3v1'] has moved to ['id3v1'] - ¤ ['id3']['id3v2'] has moved to ['id3v2'] - ¤ ['audiodataoffset'] and ['audiodataend'] have been renamed to - ['avdataoffset'] and ['avdataend'] respectively - ¤ GetAllMP3info() has been changed to GetAllFileInfo() with a - different parameter list ($allowedFormats is no longer a - parameter). Check your code where you're calling - GetAllMP3Info() - you will need to change both the function - name and the parameter list if you pass more than 2 parameters - ¤ All formats now return ['audio']['dataformat'] and/or - ['video']['dataformat'] where appropriate - this goes along with - ['fileformat'] - ['fileformat'] will return the actual structure - of the file, whereas ['dataformat'] will return the format of - the data inside that structure. For example, an Ogg file can - contain Vobis data (normal), or it can contain FLAC data in the - Ogg container format. In that case, ['fileformat'] would be - 'ogg', but ['dataformat'] would be 'flac'. - Note: this means that WAV and AVI files now return a - ['fileformat'] of 'riff' rather than 'wav' or 'avi'. - ¤ ['filesize'] is no longer returned for files larger than 2GB - because PHP does not support large file access. Attempting to - parse a file larger than 2GB will result in a message stored in - ['error'] and ['filesize'] not set. - ¤ APEtag, ID3v1, and ID3v2 are now supported on ALL multimedia - files - even if illegal by format. Ogg will return warning if - ID3/APE tags are present. (thanks ahØartemis*dk) - ¤ All files: non-critical errors are now returned in the root key - ['warning'] rather than ['error'] (only critical errors that - prevent getID3() from correctly parsing the file are returned in - ['error'] (thanks ahØartemis*dk) - ¤ Renamed all references to $MP3fileInfo to $ThisFileInfo - ¤ Joliet now supported for ISO-9660. - ['iso']['supplementary_volume_descriptor'] is now returned, if - available, and ['iso']['files'] will contain ASCII equivalents - of the Unicode directory structure & filenames stored. - ¤ Moved Monkey's Audio code from getid3.ape.php to seperate file. - New file: getid3.monkey.php - ¤ Added new keys for ISO-9660: ['name_ascii'] for directories, - ['file_identifier_ascii'] for files - ¤ Added root key ['track'] for CD-audio files - ¤ Ogg/Vorbis-comment files now have comments returned inside - ['ogg']['comments_common'] as an array of strings, rather than - simple strings in ['ogg'] - ¤ Quicktime files now have comments returned inside - ['quicktime']['comments'] as an array of strings, rather than - simple strings in ['quicktime'] - ¤ ['mime_type'] is a new root key returned for all supported - formats (thanks ahØartemis*dk) - ¤ ['fileformat'] now returns 'mp1' instead of 'mp3' for MPEG-1 - layer-I audio files (thanks ahØartemis*dk) - ¤ ['mpeg']['audio']['bitratemode'] now returns lowercase - ¤ MPEG-4 audio files which consist of MP3 data wrapped in a - Quicktime fileformat will now return the usual data in - ['mpeg']['audio'] - ¤ Type-1 DV AVIs are now supported - ¤ DV AVIs will return 1 or 2 in ['RIFF']['video'][x]['dv_type'] - ¤ Changed ['fileformat'] from 'mpg' to 'mpeg' for MPEG video files - ¤ ASF comments are now stored in ['asf']['comments'] instead of - ['asf'] - ¤ RealMedia chunk data is now returned inside ['real']['chunks'] - instead of ['real'] - ¤ ['replay_gain'] now properly populated from APE tags - ¤ Added support for ASF_Old_ASF_Index_Object in ASF files - (thanks ahØartemis*dk) - ¤ AAC-ADTS files now return ['aac']['bitrate_distribution'] - ¤ ParseVorbisComments() has been replaced with - ParseVorbisCommentsFilepointer() (with different parameters) - ¤ All references to any key ['frequency'] are now ['sample_rate'] - ¤ Moved ID3v2 comments from ['id3v2'] into common root - ['comments'] structure, and now returns more values than before - * Bugfix: ['iso']['files'] and ['zip']['files'] could potentially - contain duplicate entries (in a numeric-indexed array) for files - if the directory structure specifies files multiple times. - Entries are now guaranteed unique, with the last entry for the - file overwriting any former ones. - * Bugfix: RIFF parsing had numerous issues, including: - - large AVIs would take a very very long time to parse - - chunks with odd (not even) sizes would cause the parser fail - - video and/or audio codecs not always identified - The ParseRIFF() function has been completely rewritten and fixes - all known issues with RIFF parsing. Users are, however, - encouraged to double-check output of any parsed (AVI/WAV/CDDA) - files. - * Bugfix: Modified getid3.riff.php to return correct total - bitrates for AVIs with multiple audio streams - * Bugfix: GetFileFormat() was not creating array structure - correctly (thanks ahØartemis*dk) - * Bugfix: LAME tag for MP3s can only specify up to 255kbps, so any - files with actual CBR bitrate of >=256 were reported incorrectly - * Bugfix: Lyrics3 synched lyrics were not being correctly returned - * Bugfix: CreateDeepArray() was broken for non-nested cases, which - meant ZIP and ISO ['files'] structures were broken - * Bugfix: Incorrect pattern matching for ZIP files meant no zip - files were being detected as such - * Bugfix: AAC-ADIF was returning an incorrect number of channels - (too few) in some cases (thanks ahØartemis*dk) - * Bugfix: Vorbis comments were returning an incorrect value for - ['dataoffset'] in some cases - * Bugfix: MPEG video ['marker_bit'] and ['vbv_buffer_size'] were - incorrect - * Bugfix: ['playtime_string'] could potentially have a value of - x minutes and 60 seconds (ie 3:60 instead of 4:00) - Added support for FLAC cuesheets (FLAC 1.1.0+) - (thanks ahØartemis*dk) - Improved parsing speed in MP3, MP2 and AAC (thanks ahØartemis*dk) - Extra error-checking added to try and identify corrupt files for - most audio formats (thanks ahØartemis*dk) - More accurate playtime calculation for RealMedia - (thanks ahØartemis*dk) - Changed all relevant files to use ['audiodataoffset'] and - ['audiodataend'] rather than ['filesize'] where appropriate - (thanks ahØartemis*dk) - Added text encoding type 255 as a duplicate of UTF-16BE but with - Big-Endian rather than Little-Endian byte order - Added many RIFF-AVI audio types and fourcc video types to the - lookup functions in getid3.riff.php - Added numerous new known GUIDs to getid3.asf.php - Added PoweredBygetID3() function to easily get a "powered by" - string with the current getID3() version. - Added "Morgan Multimedia Motion JPEG2000" (MJ2C), "DivX v5" (DX50) - and "XviD" (XVID) codecs to list of known codecs in - getid3.riff.php - Changed GETID3_INCLUDEPATH path seperators to forced / - (from \ for Windows) - Modified getid3.check.php to only change \ directory seperators to - / on Windows operating systems - Modified getid3.check.php to handle larger-than-2GB files (which - now do not return a filesize) - Modified getid3.check.php to handle ['dataformat_audio'] and - ['dataformat_video'] - Modified getid3.check.php to show a list of present tags in one - column rather than one column for each of ID3v1, ID3v2, etc - Modified getid3.check.php to show MD5 values. Initially disabled - but can be enabled for a directory with a click. md5_file is - always calculated when displaying detailed info about a single - file; md5_data is calculated if the file is < 50MB - Modified getid3.check.php to show errors and warnings. Details are - visible with a mouseover or a click. - Changed getid3.check.php to use SafeStripSlashes instead of a - manual conditional directory name replacement for special - characters - Added sample recursive scanning sample code to getid3.readme.txt - (thanks lipisinØmail*ru for the idea) - - -1.5.7: [January-10-2003] James Heinrich - » Added support for ISO 9660 (CD-ROM image) format. Most-useful - data is directory structure returned under ['iso']['files'] - Note: Only ISO-9660 supported, not (yet) Joliet extension - (thanks nebula_djØsofthome*net for the idea) - New file: getid3.iso.php - ¤ ZIP files are now parsed by getID3() itself without relying on - built-in PHP functions and/or ZZipLib support. - (thanks Vince for the idea) - ¤ ZIP files now return a simple directory listing with filename - and filesize info only under ['zip']['files']. - Note: empty subdirectories will note appear in here, only files - and non-empty subdirectories. Information for all entries, - including empty subdirectories, is available under - ['zip']['central_directory'] (or under ['zip']['entries'] if the - Central Directory cannot be located (usually due to a trucated - file). - ¤ RIFF-WAV files with MP3 data (or MP3s with RIFF headers, if you - want to think of it that way) now have the MPEG audio portion - scanned and the usual data returned in ['mpeg']['audio'] if the - RIFF audio codec has wFormatTag of "85" (identified by getID3() - as "MPEG Layer 3") - (thanks ahØartemis*dk for the idea) - ¤ EXIF data (if present) is returned for JPEG files under - ['jpg']['exif'] (thanks nebula_djØsofthome*net) - ¤ ['filepath'] now returned for all files with the directory part - of the full filename. - ¤ ['filenamepath'] is now returned for all files (equivalent to - ['filepath'].'/'.['filename']) - * Bugfix: ['id3']['id3v2'][]['dataoffset'] was wrong - * Bugfix: MP3s tagged with iTunes have an invalid comment field - frame name ('COM ' - should be 'COMM') but the data is valid - otherwise; the frame is now renamed to 'COMM' and parsed - normally (with the error noted in ['error']) - (thanks kheller2Ømac*com for the sample file) - * Bugfix: Some ASF/WMA audio files were not being identified as - any format (thanks ahØartemis*dk) - * Bugfix: Warning now generated and ASCII format assumed for - invalid text encoding values in ID3v2 - * Bugfix: Changed ZIP detection pattern from 'PK' to 'PK\x04\x03' - * Bugfix: Ogg/FLAC files with large Vorbis comments were dying in - an infinite loop with lots of error messages due to missing $fd - parameter on ParseVorbisComments() (thanks ahØartemis*dk) - * Bugfix: ['data'] and ['image_mime'] were being returned for all - Ogg comments even if they were not images for versions of PHP - that have image_type_to_mime_type() built in (ie PHP 4.3.0+) - - -1.5.6: [December-31-2002] James Heinrich - » Added support for NSV (Nullsoft Streaming Video) - (www.nullsoft.com/nsv/) - (thanks demonØsoundplanet*com for the idea) - New file: getid3.nsv.php - » Added support for CD-audio track files (track01.cda etc) - ¤ Added standard ['frame_rate'] root value when known (AVI, NSV, - MPEG-video) - ¤ ASF files now report ['fileformat'] of: - 'wmv' when Windows Media Video codec v7/v8/v9 is used; - 'wma' when any 'Windows Media Audio' named audio codec is used - and no video stream is present; - 'asf' in all other cases (audio-only, video-only, or both) - ¤ Removed support for ZIP functions (will be rewritten to not - require ZZIPlib support in future versions) - ¤ Added function SafeStripSlashes() as a drop-in replacement for - stripslashes(), but that only strips slashes if magic_quotes_gpc - is set - ¤ Removed support for remote file scanning (HTTP / FTP) - ¤ Added ['aac']['frames'] (number of AAC frames in file) - ¤ Added ['mpeg']['audio']['frame_count'] when a bitrate histogram - is created - ¤ Average bitrate for VBR MP3/MP2 is calculated from actual counts - of frames of various bitrates (rather than relying on the header - values or filesize) when a bitrate histogram is created - ¤ RecursiveFrameScanning() split out into seperate function - (getid3.mp3.php) - ¤ Removed old function getMP3header() from getid3.mp3.php - ¤ Changed default MPEG_VALID_CHECK_FRAMES (number of mp3 frames - scanned to ensure a valid audio sequence has been located) from - 10 to 25. This means scanning will be slightly slower, but more - reliable/accurate - * Bugfix: ID3v2.2 - valid frame names not correctly detected - (thanks maeckerØweb*de for the sample file) - * Bugfix: ID3v2.2 - valid padding not correctly detected - (thanks maeckerØweb*de for the sample file) - * Bugfix: MIDI files with flat key signatures were not being - correctly reported (thanks alexleeisØshaw*ca for sample file) - * Bugfix: now returns message in ['error'] if file does not exist - * Bugfix: ['RIFF']['video'][x]['codec'] wasn't always being - correctly populated - * Bugfix: ['bitrate'] was incorrect for multi-stream RealMedia - * Bugfix: ['playtime_seconds'] was sometimes null or incorrect - for multi-stream RealMedia - * Bugfix: ChannelTypeID was incorrect in RVA2 ID3v2.4 frames - * Bugfix: Fixed potential divide-by-zero error for corrupt FLAC - files (thanks ahØartemis*dk) - * Bugfix: AAC-ADTS was not returning ['bitrate_mode'] unless - $ReturnExtendedInfo was TRUE (thanks ahØartemis*dk) - * Bugfix: LAME-encoded CBR MP3s now properly identified as CBR - with correct bitrate (thanks ahØartemis*dk) - * Bugfix: VBR MP2 (or headerless MP3) is now identified as VBR - rather than CBR. Note: to obtain VBR bitrate for headerless - files, the entire file is scanned and a histogram distribution - of bitrates is created, and the average bitrate calculated from - that. (thanks ahØartemis*dk for sample file) - Added support for DSIZ chunks in VQF, and checks to make sure size - of audio data matches DSIZ value, if present - (thanks ahØartemis*dk for sample file) - Rewrote GetAllMP3info() - removed some unneccesary code, changed - format-detection routine from ParseAsThisFormat() to - GetFileFormat() to allow for more flexible format parsing - (needed for ISO CD-ROM images, helpful for Quicktime and others) - Changed references in all files from string-cast indexes: ["$i"] - to non-cast indexes: [$i] where appropriate - Put a sans-serif 9pt style on all text in getid3.check.php - getAACADTSheaderFilepointer() now return TRUE if synch is lost - after the first frame has been successfully parsed (previously - it would return FALSE if synch was lost at any time, meaning the - file is most likely MP3, which was incorrect) - (thanks ahØartemis*dk for sample file) - Speed improvement code changes to getid3.mp3.php (up to 24% faster - in some cases) (thanks ahØartemis*dk for the code) - Changed all include_once() to require_once() - - -1.5.5: [November-25-2002] James Heinrich - » Added support for La (Lossless Audio - www.lossless-audio.com) - (thanks ahØartemis*dk for the idea) - New file: getid3.la.php - ¤ Moved lookup functions from getid3.lookup.php to the files where - they are used. - New file: getid3.id3.php - New file: getid3.rgad.php - Removed file: getid3.lookup.php - ¤ getID3v1Filepointer() returns FALSE if ID3v1 tag not found - ¤ Added new paramter "ReturnExtendedInfo" to the function - getAACADTSheaderFilepointer() in getid3.aac.php which now - defaults to FALSE - if TRUE then the data for every frame is - returned (containing aac_frame_length, adts_buffer_fullness and - num_raw_data_blocks, which aren't usually very useful). Speed - improvement with FALSE is about 35%. - ¤ Now returns fopen() errors in ['error'], for example if a remote - file is not accessible. - ¤ Changed default number of MP3 audio frames to scan to determine - if a valid stream has been found from 5 to 10, now also defined - as a constant at the top of getid3.mp3.php This will result in - slightly slower MP3 parsing, but greater reliability in - detecting false/invalid/corrupted VBR headers. - ¤ fopen() errors now displayed in getid3.putid3.php - (thanks miguel.dieckmannØhamburg*de) - ¤ Added 4th parameter to decodeMPEGaudioHeader() $ScanAsCBR which - will force an MP3 audio frame sequence to be force-scanned in - CBR mode. You should never need to call this directly, it's only - used internally to scan for MP3 files that have an illegal VBR - header with CBR data. (thanks fletchØpobox*com) - * Bugfix: ASF_Marker_Object in getid3.asf.php was always returning - an error in non-existant "reserved_1" and failing - * Bugfix: VBR bitrate calculations in getid3.mp3.php only occur if - ['mpeg']['audio']['VBR_frames'] is defined. - (thanks fletchØpobox*com) - * Bugfix: getid3.putid3.php no longer deletes original MP3 if - ID3v2 tag writing fails (thanks miguel*dieckmannØhamburg*de) - * Bugfix: incorrect order of if-statement error messages in - getid3.putid3.php (thanks miguel*dieckmannØhamburg*de) - getid3.asf.php now notes the error and continues parsing rather - than failing when it encounters an error parsing a chunk - Now actually scan 1000 frames for AAC ADTS as reported in the - v1.5.4 changelog, rather than 100. (thanks ahØartemis*dk) - Improved scanning speed in getAACADTSheaderFilepointer() by ~30% - (thanks ahØartemis*dk for the fix) - Added FileSizeNiceDisplay() function to getid3.functions.php for - formatting filesize output in kB, MB, GB, etc. - - -1.5.4: [October-07-2002] James Heinrich - » Added support for Quicktime. - New file: getid3.quicktime.php - » Added support for AAC files, both ADTS and ADIF header formats. - ADIF format is a pain because it's very similar to standard MP3 - header format, and it's hard to distinguish between the two. I - have tried to make the detection accurate, but I have a limited - number of AAC test files to play with so if you have an AAC file - that gets detected as MP3/MP2 (or vice-versa), please send me - the details via email at getid3Øsilisoftware*com - ADTS format is very slow to parse because to get the bitrate of - VBR files the whole file has to be stepped through frame by - frame (getID3() scans up to the first 1000 frames and assumes - that to be close enough). - Note: I would suggest commenting out support for AAC (see top of - GetAllMP3info() function in getid3.php) unless you need it. - (thanks jfaulØgmx*de for the idea and sample Delphi source code) - New file: getid3.aac.php - » Added bitrate distribution analysis option for MP3 VBR files. A - new boolean parameter for getOnlyMPEGaudioInfo() enabled this - feature which steps through the MP3 file frame by frame and - counts how many frames of each bitrate exist. This information - is returned in ['mpeg']['audio']['bitrate_distribution'] - Caution: this feature is very inefficient for large files and - takes a very long time and does lots of disk I/O. Use with care. - ¤ Changed layout of allowedFormats in GetAllMP3info() function in - getid3.php to allow easy removal of support for any of the - supported format. As stated above, I recommend commenting out - AAC unless needed. - ¤ Added ['flac']['compressed_audio_bytes'], - ['flac']['uncompressed_audio_bytes'], and - ['flac']['compression_ratio'] - ¤ Replaced FXPT2DOT30toFloat() function with FixedPoint2_30() - * Bugfix: getid3.mpc.php was slightly miscalculating the number of - samples, therefore also bitrate and playtime - (thanks ahØartemis*dk for the fix) - * Bugfix: MonkeyCompressionLevelNameLookup() didn't know about - 'insane' compression (thanks ahØartemis*dk for the fix) - * Bugfix: MonkeySamplesPerFrame() was incorrect for MAC v3.95+ - (thanks ahØartemis*dk for the fix) - * Bugfix: getid3.check.php wasn't processing the assumeFormat - directive when (register_globals == off) - * Bugfix: detecting of synch pattern for MP3 files with invalid - data at the beginning wasn't always correct, also meant possible - incorrect bitrate/duration/etc info for such corrupt files. - getid3.functions.php now includes a replacement utf8_decode() - function for those PHP installations that are not configured - with the --with-xml option. (thanks stephaneØtekartists*com) - - -1.5.3: [September-30-2002] James Heinrich - » Added support for VQF. (thanks mtØmansonthomas*com for the idea) - New file: getid3.vqf.php - » Added support for FLAC. Comments, if present, are returned under - ['ogg'] because they follow the Ogg Vorbis structure standard. - New file: getid3.flac.php - ¤ OS/2-format bitmaps are now correctly interpreted. The format of - the bitmap is now returned in ['bmp']['type_os'] and - ['bmp']['type_version']. OS/2 bitmaps can be v1 or v2, Windows - can be v1, v4 or v5 - - -1.5.2: [September-25-2002] James Heinrich - » Support for RealMedia (audio & video) added - Note: only tested on G2 and v5 audio and video files - if anyone - has older and/or newer sample files, please test it and/or send - me the sample files. - (thanks stephaneØtekartists*com for idea) - New file: getid3.real.php - » Support for BMP added. Palette and pixel data can optionally be - extracted as well - this is slow and generally unneccesary, but - the option is there if you need it. Also includes PlotBMP() - which will take the extracted pixel data and output it as a true - color PNG. This function requires GD v2.0+ - Note: Untested on 16-bit and 32-bit BMPs because I couldn't find - any sample files - if you know of a program that can create such - files, please email getid3Øsilisoftware*com - Note: Support for RGB (uncompressed), RLE8 and RLE4 is included - and tested. BITFIELDS support is also included for 16- & 32-bit - formats, but it's untested, so if anybody has any test files - please send them to getid3Øsilisoftware*com - Note: Support currently only for Windows-format BMPs, and trying - to parse an OS/2-format bitmap leads to unpredictable/invalid - results. - New file: getid3.bmp.php - » PNG now fully parsed, including all information chunks - New file: getid3.png.php - ¤ Support for GIF/JPG/PNG moved to seperate files and expanded, - including standard ['resolution_x'] and ['resolution_y'] as well - as more thorough parsing of header information - New file: getid3.gif.php - New file: getid3.jpg.php - table_var_dump() simplified and now outputs {-style character - entities for characters outside the normal alphanumeric range - CleanOggCommentName() changed to a regular expression - (thanks chris-getid3Øbolt*cx for rewriting the function) - - -1.5.1: [September-20-2002] James Heinrich - » Added support for MPEGplus/Musepack SV7. ['fileformat'] is 'SV7' - for version 7 files (versions 4, 5 ,6 and 8 are not supported - yet, but will be of ['fileformat'] SV4, SV5, SV6 and SV8) when - they are supported (thanks Christian Fritz for the idea) - New file: getid3.mpc.php - ¤ ['bitrate_audio'], ['bitrate_video'], ['bitrate_mode'], - ['channels'], ['resolution_x'], and ['resolution_y'] keys added - for all appropriate formats - ¤ Ogg files with a COVERART comment now save and display the - attached image the same way as is done with ID3v2 APICs - ¤ ['ogg']['comments'][n]['data'] and - ['ogg']['comments'][n]['dataoffset'] is now returned for all - comments. ['ogg']['comments'][n]['data'] is only useful if - the field is supposed to contain binary data. It is a - base64_decode()'d version of ['value']. - ['ogg']['comments'][n]['dataoffset'] is the byte offset in the - file at which the 'COMMENTNAME=value string' starts, not the - start of just 'value' - ¤ ['ogg']['comments'][n]['image_mime'] is now returned if - ['ogg']['comments'][n]['data'] contains valid image data. - ¤ More than 3 Ogg pages may now be read in, if the comment data - is longer than 1 page (usually about 4kB) - ¤ ['fileformat'] is now 'mp2' rather than 'mp3' if it's MPEG-1, - Layer-II audio - ¤ ASF bitrates now calculated even if stream_bitrate_properties - object not present - ¤ ['asf']['stream_properties_object'] is now a numeric-key array - with one entry for each stream - the key being the stream number - ¤ ['replay_gain'] is returned for all audio formats that support - it (MP3-LAME, ID3v2, Ogg) (thanks Christian Fritz for the idea) - ¤ ['mpeg']['audio']['LAME']['RGAD']['radio_replay_gain'] is now - ['mpeg']['audio']['LAME']['RGAD']['radio'] (same for audiophile) - ¤ ASF/WMA files now use WM/Track to get track number from if - WM/TrackNumber is not available (thanks stephaneØtekartists*com) - ¤ ASF/WMV files now returns ['year'] and ['asf']['year'] - ¤ ASV/WMV files now use ['content_description']['description'] for - the ['comment'] field (thanks stephaneØtekartists*com) - ¤ ['track'] is now always returned as an integer - * Bugfix: Ogg comments that are larger than one data page (usually - about 4kB) are now correctly parsed (thanks Christian Fritz) - * Bugfix: Ogg comment data is now UTF8-decoded - * Bugfix: Ogg comment writing now UTF8-encodes the data - * Bugfix: playtime for ASF files was off by (usually - between 3 and 12 seconds) - * Bugfix: ['asf']['stream_properties_objects']['flags'] data was - possibly incorrect - * Bugfix: ASF Padding Object was overwriting - Stream Bitrate Properties Object data (now returned correctly in - ['asf']['padding_object'] - * Bugfix: ASF Marker Object Reserved_2 field was incorrect - * Bugfix: ASF Bitrate Mutual Exclusion Object had incorrect stream - numbers - Warning displayed if incorrectly-formatted Ogg comment is present - (known to be an issue with CDex v1.40, but fixed by v1.50b7) - (thanks Christian Fritz) - Ogg comment writing now checks for valid comment names - Added bitrate column in getid3.check.php, and added some formatting - (font, colour) - Performance tweaks using bitwise math instead of binary string - operations - - -1.5.0: [September-18-2002] James Heinrich - » Ogg comment writing support added. getid3.write.php has been - updated to allow for writing comment tags to both MP3 and Ogg. - Big thanks to Chris Bolt for writing the - OggWrite() function and offering it for inclusion in getID3() - New file: getid3.ogginfo.php - » Support for Monkey's Audio and APE tag added. - (thanks Christian Fritz for the idea) - New file: getid3.ape.php - ['fileformat'] now returns 'mac' for Monkey's Audio files, or - 'ape' for files with an APE tag (Monkey's Audio or other format) - » getid3.thumbnail.php has been removed from the distribution and - the table_var_dump() function now outputs APICs as seperate - files in the same directory as the analyzed file. This should - make the image-displaying more reliable as well as reduce - complexity. The naming convention for the images is - filename.ext.[byte offset of APIC data].[jpg|gif|png] - If anybody still has any problems with corrupted images please - let me know at getid3Øsilisoftware*com - » Support for extended Xing/LAME tag - (see http://users.belgacom.net/gc247244/extra/tag.html) - Data is returned in ['mpeg']['audio']['LAME'] - ¤ ['ogg']['tracknumber'] has been renamed to ['ogg']['track'] and - ['track'] is now returned in the root of the array - ¤ ['ogg']['pageheader'][n]['flag'] has been renamed to - ['ogg']['pageheader'][n]['flags'] and the unprocessed flag byte - is available in ['ogg']['pageheader'][n]['flags_raw'] - ¤ ['frequency'] is now returned for WAVE files in the root of the - array (thanks danielØelectroteque*org) - ¤ ASF files now return codec, bitrate, resolution, etc information - under ['asf']['video_media'] or ['asf']['audio_media'] - * Bugfix: RVA2 and EQU2 writing in getid3.putid3.php were - incorrectly writing Volume Adjustment field - * Bugfix: EQU2 in getid3.frames.php was reading Volume Adjustment - as unsigned integer instead of signed integer - * Bugfix: handling of remote files over HTTP & FTP was broken - (thanks Vince) - * Bugfix: incorrect handling of some ASF packets - ASF/Windows Media format now more fully parsed, including Index - Objects - Added several new fourCC video codecs - - -1.4.3: [September-15-2002] James Heinrich - » Now parses ASF / WMV / WMA files - ¤ New file: getid3.asf.php - * Bugfix: RoughTranslateUnicodeToASCII() would return nothing - if didn't find a terminator it was expecting - Added FILETIMEtoUNIXtime() function (for converting 64-bit - Microsoft FILETIME timestamps, used in ASF files and elsewhere, - to UNIX Epoch timestamps) - Added GUIDtoBytestring() and BytestringToGUID() functions - - -1.4.2: [September-12-2002] James Heinrich - » getID3() now requires PHP v4.1.0 or higher because it now is - designed to work with register_globals = off and the new auto- - globals ($_GET, $_SERVER, etc). - * Bugfix: VBR MP3 files with Fraunhofer-style VBR header were not - being correctly detected in most cases - (thanks dkushnerØoddcast*com and mikeØftl*com for sample files) - * Bugfix: IsValidTextEncoding() was broken - * Bugfix: Add stripslashes($EditorFilename) to getid3.write.php - (writing was broken for files with ' or " in the filename) - (thanks mikeØftl*com and kthejoker) - * Bugfix: If there is garbage data between a valid VBR header - frame and a sequence of valid MPEG-audio frames the VBR data is - no longer discarded. (thanks to mikeØftl*com for sample - Fraunhofer-style VBR file produced with MusicMatch v7.2) - ¤ Changed variable system to work with (register_globals = off) - ¤ Moved relevant code into seperate PlaytimeString() function - ¤ Added nl2br() to table_var_dump() for cleaner output - ¤ Now returns the following keys from Fraunhofer-VBR files: - ['VBR_seek_offsets'], ['VBR_seek_offsets_stride'], - ['VBR_offsets_relative'] and ['VBR_offsets_absolute'] - ¤ Added ID3v1matchesID3v2() function and implemented in - getid3.check.php (thanks to "Guest" in the forums for the idea) - Changed amount of data read in getid3.getimagesize.php from 10kB - to entire file. (thanks mikeØftl*com) - Wrapped function_exists() checks around function definitions in - getid3.functions.php - Fixed a lot of E_WARNING and E_NOTICE situations, especially in - ID3-writing code (getid3.putid3.php, etc) - Added checks to make sure all needed data is available for writing - ID3v2 tags - - -1.4.1b5: [May-30-2002] James Heinrich - * Bugfix: Unsynchronise() was broken, now fixed - (thanks mikeØftl*com) - * Bugfix: GenerateID3v2Tag() now correctly uses non-synchsafe - integers for frame size descriptors in ID3v2.3 and ID3v2.2 - (thanks mikeØftl*com) - ¤ Added ['artist'], ['title'], etc keys to root of returned - array to provide a common place to access any returned info - from any file type. Currently gets info from ID3v1, ID3v2, - Ogg, and RIFF/WAVE. Possible returned keys are: - title, artist, album, year, genre, comment, track - ¤ Modified LookupGenre() function to search for either genre based - on numeric ID, or now reverse lookup as well - ¤ Added ['artist'], ['title'], etc keys to ['RIFF'] information - if info tags are present - Added functionality to attach a picture to the ID3v2 tag in - getid3.write.php - Sorted genres into alphabetical order (special 3 at end of list) - in getid3.write.php - Changed the comment-edit field in getid3.write.php to a multi-line - '; - - echo 'Picture
(ID3v2 only)
'; - echo ''; - echo ' '; - echo ''; - - } else { - - echo 'Error'.FixTextFields($Filename).' does not exist'; - - } - echo ''; - -} - -?> - - \ No newline at end of file diff --git a/getid3/demos/index.php b/getid3/demos/index.php deleted file mode 100644 index 13783f0..0000000 --- a/getid3/demos/index.php +++ /dev/null @@ -1 +0,0 @@ -In this directory are a number of examples of how to use getID3() - if you don't know what to run, take a look at demo.browse.php \ No newline at end of file diff --git a/getid3/dependencies.txt b/getid3/dependencies.txt deleted file mode 100644 index 34a72ba..0000000 --- a/getid3/dependencies.txt +++ /dev/null @@ -1,24 +0,0 @@ -///////////////////////////////////////////////////////////////// -/// getID3() by James Heinrich // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// // -// dependencies.txt - part of getID3() // -// See readme.txt for more details // -// /// -///////////////////////////////////////////////////////////////// - -lyrics3 depends on apetag (optional) -ogg depends on flac -id3v2 depends on id3v1 -apetag depends on id3v1 (optional, writing only) -bonk depends on id3v2 (optional) -riff depends on mp3 -mpeg depends on mp3 -quicktime depends on mp3 -flac depends on ogg -optimfrog depends on riff -la depends on riff -lpac depends on riff -asf depends on riff, id3v1 (optional) diff --git a/getid3/getid3/extension.cache.dbm.php b/getid3/getid3/extension.cache.dbm.php deleted file mode 100644 index 051bb1f..0000000 --- a/getid3/getid3/extension.cache.dbm.php +++ /dev/null @@ -1,222 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// // -// extension.cache.dbm.php - part of getID3() // -// Please see readme.txt for more information // -// /// -///////////////////////////////////////////////////////////////// -// // -// This extension written by Allan Hansen // -// /// -///////////////////////////////////////////////////////////////// - - -/** -* This is a caching extension for getID3(). It works the exact same -* way as the getID3 class, but return cached information very fast -* -* Example: -* -* Normal getID3 usage (example): -* -* require_once 'getid3/getid3.php'; -* $getID3 = new getID3; -* $getID3->encoding = 'UTF-8'; -* $info1 = $getID3->analyze('file1.flac'); -* $info2 = $getID3->analyze('file2.wv'); -* -* getID3_cached usage: -* -* require_once 'getid3/getid3.php'; -* require_once 'getid3/getid3/extension.cache.dbm.php'; -* $getID3 = new getID3_cached('db3', '/tmp/getid3_cache.dbm', -* '/tmp/getid3_cache.lock'); -* $getID3->encoding = 'UTF-8'; -* $info1 = $getID3->analyze('file1.flac'); -* $info2 = $getID3->analyze('file2.wv'); -* -* -* Supported Cache Types -* -* SQL Databases: (use extension.cache.mysql) -* -* cache_type cache_options -* ------------------------------------------------------------------- -* mysql host, database, username, password -* -* -* DBM-Style Databases: (this extension) -* -* cache_type cache_options -* ------------------------------------------------------------------- -* gdbm dbm_filename, lock_filename -* ndbm dbm_filename, lock_filename -* db2 dbm_filename, lock_filename -* db3 dbm_filename, lock_filename -* db4 dbm_filename, lock_filename (PHP5 required) -* -* PHP must have write access to both dbm_filename and lock_filename. -* -* -* Recommended Cache Types -* -* Infrequent updates, many reads any DBM -* Frequent updates mysql -*/ - - -class getID3_cached_dbm extends getID3 -{ - - // public: constructor - see top of this file for cache type and cache_options - function getID3_cached_dbm($cache_type, $dbm_filename, $lock_filename) { - - // Check for dba extension - if (!extension_loaded('dba')) { - die('PHP is not compiled with dba support, required to use DBM style cache.'); - } - - // Check for specific dba driver - if (function_exists('dba_handlers')) { // PHP 4.3.0+ - if (!in_array('db3', dba_handlers())) { - die('PHP is not compiled --with '.$cache_type.' support, required to use DBM style cache.'); - } - } - else { // PHP <= 4.2.3 - ob_start(); // nasty, buy the only way to check... - phpinfo(); - $contents = ob_get_contents(); - ob_end_clean(); - if (!strstr($contents, $cache_type)) { - die('PHP is not compiled --with '.$cache_type.' support, required to use DBM style cache.'); - } - } - - // Create lock file if needed - if (!file_exists($lock_filename)) { - if (!touch($lock_filename)) { - die('failed to create lock file: ' . $lock_filename); - } - } - - // Open lock file for writing - if (!is_writeable($lock_filename)) { - die('lock file: ' . $lock_filename . ' is not writable'); - } - $this->lock = fopen($lock_filename, 'w'); - - // Acquire exclusive write lock to lock file - flock($this->lock, LOCK_EX); - - // Create dbm-file if needed - if (!file_exists($dbm_filename)) { - if (!touch($dbm_filename)) { - die('failed to create dbm file: ' . $dbm_filename); - } - } - - // Try to open dbm file for writing - $this->dba = @dba_open($dbm_filename, 'w', $cache_type); - if (!$this->dba) { - - // Failed - create new dbm file - $this->dba = dba_open($dbm_filename, 'n', $cache_type); - - if (!$this->dba) { - die('failed to create dbm file: ' . $dbm_filename); - } - - // Insert getID3 version number - dba_insert(GETID3_VERSION, GETID3_VERSION, $this->dba); - } - - // Init misc values - $this->cache_type = $cache_type; - $this->dbm_filename = $dbm_filename; - - // Register destructor - register_shutdown_function(array($this, '__destruct')); - - // Check version number and clear cache if changed - if (dba_fetch(GETID3_VERSION, $this->dba) != GETID3_VERSION) { - $this->clear_cache(); - } - - parent::getID3(); - } - - - - // public: destuctor - function __destruct() { - - // Close dbm file - @dba_close($this->dba); - - // Release exclusive lock - @flock($this->lock, LOCK_UN); - - // Close lock file - @fclose($this->lock); - } - - - - // public: clear cache - function clear_cache() { - - // Close dbm file - dba_close($this->dba); - - // Create new dbm file - $this->dba = dba_open($this->dbm_filename, 'n', $this->cache_type); - - if (!$this->dba) { - die('failed to clear cache/recreate dbm file: ' . $this->dbm_filename); - } - - // Insert getID3 version number - dba_insert(GETID3_VERSION, GETID3_VERSION, $this->dba); - - // Reregister shutdown function - register_shutdown_function(array($this, '__destruct')); - } - - - - // public: analyze file - function analyze($filename) { - - if (file_exists($filename)) { - - // Calc key filename::mod_time::size - should be unique - $key = $filename . '::' . filemtime($filename) . '::' . filesize($filename); - - // Loopup key - $result = dba_fetch($key, $this->dba); - - // Hit - if ($result !== false) { - return unserialize($result); - } - } - - // Miss - $result = parent::analyze($filename); - - // Save result - if (file_exists($filename)) { - dba_insert($key, serialize($result), $this->dba); - } - - return $result; - } - -} - - -?> \ No newline at end of file diff --git a/getid3/getid3/extension.cache.mysql.php b/getid3/getid3/extension.cache.mysql.php deleted file mode 100644 index 40ea688..0000000 --- a/getid3/getid3/extension.cache.mysql.php +++ /dev/null @@ -1,171 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// // -// extension.cache.mysql.php - part of getID3() // -// Please see readme.txt for more information // -// /// -///////////////////////////////////////////////////////////////// -// // -// This extension written by Allan Hansen // -// /// -///////////////////////////////////////////////////////////////// - - -/** -* This is a caching extension for getID3(). It works the exact same -* way as the getID3 class, but return cached information very fast -* -* Example: (see also demo.cache.mysql.php in /demo/) -* -* Normal getID3 usage (example): -* -* require_once 'getid3/getid3.php'; -* $getID3 = new getID3; -* $getID3->encoding = 'UTF-8'; -* $info1 = $getID3->analyze('file1.flac'); -* $info2 = $getID3->analyze('file2.wv'); -* -* getID3_cached usage: -* -* require_once 'getid3/getid3.php'; -* require_once 'getid3/getid3/extension.cache.mysql.php'; -* $getID3 = new getID3_cached_mysql('localhost', 'database', -* 'username', 'password'); -* $getID3->encoding = 'UTF-8'; -* $info1 = $getID3->analyze('file1.flac'); -* $info2 = $getID3->analyze('file2.wv'); -* -* -* Supported Cache Types (this extension) -* -* SQL Databases: -* -* cache_type cache_options -* ------------------------------------------------------------------- -* mysql host, database, username, password -* -* -* DBM-Style Databases: (use extension.cache.dbm) -* -* cache_type cache_options -* ------------------------------------------------------------------- -* gdbm dbm_filename, lock_filename -* ndbm dbm_filename, lock_filename -* db2 dbm_filename, lock_filename -* db3 dbm_filename, lock_filename -* db4 dbm_filename, lock_filename (PHP5 required) -* -* PHP must have write access to both dbm_filename and lock_filename. -* -* -* Recommended Cache Types -* -* Infrequent updates, many reads any DBM -* Frequent updates mysql -*/ - - -class getID3_cached_mysql extends getID3 -{ - - // private vars - var $cursor; - var $connection; - - - // public: constructor - see top of this file for cache type and cache_options - function getID3_cached_mysql($host, $database, $username, $password) { - - // Check for mysql support - if (!function_exists('mysql_pconnect')) { - die('PHP not compiled with mysql support.'); - } - - // Connect to database - $this->connection = mysql_pconnect($host, $username, $password); - if (!$this->connection) { - die('mysql_pconnect() failed - check permissions and spelling.'); - } - - // Select database - if (!mysql_select_db($database, $this->connection)) { - die('Cannot use database '.$database); - } - - // Create cache table if not exists - $this->create_table(); - - // Check version number and clear cache if changed - $this->cursor = mysql_query("SELECT `value` FROM `getid3_cache` WHERE (`filename` = '".GETID3_VERSION."') AND (`filesize` = '-1') AND (`filetime` = '-1') AND (`analyzetime` = '-1')", $this->connection); - list($version) = @mysql_fetch_array($this->cursor); - if ($version != GETID3_VERSION) { - $this->clear_cache(); - } - - parent::getID3(); - } - - - - // public: clear cache - function clear_cache() { - - $this->cursor = mysql_query("DELETE FROM `getid3_cache`", $this->connection); - $this->cursor = mysql_query("INSERT INTO `getid3_cache` VALUES ('".GETID3_VERSION."', -1, -1, -1, '".GETID3_VERSION."')", $this->connection); - } - - - - // public: analyze file - function analyze($filename) { - - if (file_exists($filename)) { - - // Short-hands - $filetime = filemtime($filename); - $filesize = filesize($filename); - $filenam2 = mysql_escape_string($filename); - - // Loopup file - $this->cursor = mysql_query("SELECT `value` FROM `getid3_cache` WHERE (`filename`='".$filenam2."') AND (`filesize`='".$filesize."') AND (`filetime`='".$filetime."')", $this->connection); - list($result) = @mysql_fetch_array($this->cursor); - - // Hit - if ($result) { - return unserialize($result); - } - } - - // Miss - $result = parent::analyze($filename); - - // Save result - if (file_exists($filename)) { - $res2 = mysql_escape_string(serialize($result)); - $this->cursor = mysql_query("INSERT INTO `getid3_cache` (`filename`, `filesize`, `filetime`, `analyzetime`, `value`) VALUES ('".$filenam2."', '".$filesize."', '".$filetime."', '".time()."', '".$res2."')", $this->connection); - } - return $result; - } - - - - // private: (re)create sql table - function create_table($drop = false) { - - $this->cursor = mysql_query("CREATE TABLE IF NOT EXISTS `getid3_cache` ( - `filename` VARCHAR(255) NOT NULL DEFAULT '', - `filesize` INT(11) NOT NULL DEFAULT '0', - `filetime` INT(11) NOT NULL DEFAULT '0', - `analyzetime` INT(11) NOT NULL DEFAULT '0', - `value` TEXT NOT NULL, - PRIMARY KEY (`filename`,`filesize`,`filetime`)) TYPE=MyISAM", $this->connection); - echo mysql_error($this->connection); - } -} - - -?> \ No newline at end of file diff --git a/getid3/getid3/getid3.lib.php b/getid3/getid3/getid3.lib.php deleted file mode 100644 index 40728d1..0000000 --- a/getid3/getid3/getid3.lib.php +++ /dev/null @@ -1,1323 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// // -// getid3.lib.php - part of getID3() // -// See readme.txt for more details // -// /// -///////////////////////////////////////////////////////////////// - - -class getid3_lib -{ - - function PrintHexBytes($string, $hex=true, $spaces=true, $htmlsafe=true) { - $returnstring = ''; - for ($i = 0; $i < strlen($string); $i++) { - if ($hex) { - $returnstring .= str_pad(dechex(ord($string{$i})), 2, '0', STR_PAD_LEFT); - } else { - $returnstring .= ' '.(ereg("[\x20-\x7E]", $string{$i}) ? $string{$i} : '¤'); - } - if ($spaces) { - $returnstring .= ' '; - } - } - if ($htmlsafe) { - $returnstring = htmlentities($returnstring); - } - return $returnstring; - } - - function SafeStripSlashes($text) { - if (get_magic_quotes_gpc()) { - return stripslashes($text); - } - return $text; - } - - - function trunc($floatnumber) { - // truncates a floating-point number at the decimal point - // returns int (if possible, otherwise float) - if ($floatnumber >= 1) { - $truncatednumber = floor($floatnumber); - } elseif ($floatnumber <= -1) { - $truncatednumber = ceil($floatnumber); - } else { - $truncatednumber = 0; - } - if ($truncatednumber <= 1073741824) { // 2^30 - $truncatednumber = (int) $truncatednumber; - } - return $truncatednumber; - } - - - function CastAsInt($floatnum) { - // convert to float if not already - $floatnum = (float) $floatnum; - - // convert a float to type int, only if possible - if (getid3_lib::trunc($floatnum) == $floatnum) { - // it's not floating point - if ($floatnum <= 1073741824) { // 2^30 - // it's within int range - $floatnum = (int) $floatnum; - } - } - return $floatnum; - } - - - function DecimalBinary2Float($binarynumerator) { - $numerator = getid3_lib::Bin2Dec($binarynumerator); - $denominator = getid3_lib::Bin2Dec('1'.str_repeat('0', strlen($binarynumerator))); - return ($numerator / $denominator); - } - - - function NormalizeBinaryPoint($binarypointnumber, $maxbits=52) { - // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html - if (strpos($binarypointnumber, '.') === false) { - $binarypointnumber = '0.'.$binarypointnumber; - } elseif ($binarypointnumber{0} == '.') { - $binarypointnumber = '0'.$binarypointnumber; - } - $exponent = 0; - while (($binarypointnumber{0} != '1') || (substr($binarypointnumber, 1, 1) != '.')) { - if (substr($binarypointnumber, 1, 1) == '.') { - $exponent--; - $binarypointnumber = substr($binarypointnumber, 2, 1).'.'.substr($binarypointnumber, 3); - } else { - $pointpos = strpos($binarypointnumber, '.'); - $exponent += ($pointpos - 1); - $binarypointnumber = str_replace('.', '', $binarypointnumber); - $binarypointnumber = $binarypointnumber{0}.'.'.substr($binarypointnumber, 1); - } - } - $binarypointnumber = str_pad(substr($binarypointnumber, 0, $maxbits + 2), $maxbits + 2, '0', STR_PAD_RIGHT); - return array('normalized'=>$binarypointnumber, 'exponent'=>(int) $exponent); - } - - - function Float2BinaryDecimal($floatvalue) { - // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html - $maxbits = 128; // to how many bits of precision should the calculations be taken? - $intpart = getid3_lib::trunc($floatvalue); - $floatpart = abs($floatvalue - $intpart); - $pointbitstring = ''; - while (($floatpart != 0) && (strlen($pointbitstring) < $maxbits)) { - $floatpart *= 2; - $pointbitstring .= (string) getid3_lib::trunc($floatpart); - $floatpart -= getid3_lib::trunc($floatpart); - } - $binarypointnumber = decbin($intpart).'.'.$pointbitstring; - return $binarypointnumber; - } - - - function Float2String($floatvalue, $bits) { - // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee-expl.html - switch ($bits) { - case 32: - $exponentbits = 8; - $fractionbits = 23; - break; - - case 64: - $exponentbits = 11; - $fractionbits = 52; - break; - - default: - return false; - break; - } - if ($floatvalue >= 0) { - $signbit = '0'; - } else { - $signbit = '1'; - } - $normalizedbinary = getid3_lib::NormalizeBinaryPoint(getid3_lib::Float2BinaryDecimal($floatvalue), $fractionbits); - $biasedexponent = pow(2, $exponentbits - 1) - 1 + $normalizedbinary['exponent']; // (127 or 1023) +/- exponent - $exponentbitstring = str_pad(decbin($biasedexponent), $exponentbits, '0', STR_PAD_LEFT); - $fractionbitstring = str_pad(substr($normalizedbinary['normalized'], 2), $fractionbits, '0', STR_PAD_RIGHT); - - return getid3_lib::BigEndian2String(getid3_lib::Bin2Dec($signbit.$exponentbitstring.$fractionbitstring), $bits % 8, false); - } - - - function LittleEndian2Float($byteword) { - return getid3_lib::BigEndian2Float(strrev($byteword)); - } - - - function BigEndian2Float($byteword) { - // ANSI/IEEE Standard 754-1985, Standard for Binary Floating Point Arithmetic - // http://www.psc.edu/general/software/packages/ieee/ieee.html - // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee.html - - $bitword = getid3_lib::BigEndian2Bin($byteword); - $signbit = $bitword{0}; - - switch (strlen($byteword) * 8) { - case 32: - $exponentbits = 8; - $fractionbits = 23; - break; - - case 64: - $exponentbits = 11; - $fractionbits = 52; - break; - - case 80: - // 80-bit Apple SANE format - // http://www.mactech.com/articles/mactech/Vol.06/06.01/SANENormalized/ - $exponentstring = substr($bitword, 1, 15); - $isnormalized = intval($bitword{16}); - $fractionstring = substr($bitword, 17, 63); - $exponent = pow(2, getid3_lib::Bin2Dec($exponentstring) - 16383); - $fraction = $isnormalized + getid3_lib::DecimalBinary2Float($fractionstring); - $floatvalue = $exponent * $fraction; - if ($signbit == '1') { - $floatvalue *= -1; - } - return $floatvalue; - break; - - default: - return false; - break; - } - $exponentstring = substr($bitword, 1, $exponentbits); - $fractionstring = substr($bitword, $exponentbits + 1, $fractionbits); - $exponent = getid3_lib::Bin2Dec($exponentstring); - $fraction = getid3_lib::Bin2Dec($fractionstring); - - if (($exponent == (pow(2, $exponentbits) - 1)) && ($fraction != 0)) { - // Not a Number - $floatvalue = false; - } elseif (($exponent == (pow(2, $exponentbits) - 1)) && ($fraction == 0)) { - if ($signbit == '1') { - $floatvalue = '-infinity'; - } else { - $floatvalue = '+infinity'; - } - } elseif (($exponent == 0) && ($fraction == 0)) { - if ($signbit == '1') { - $floatvalue = -0; - } else { - $floatvalue = 0; - } - $floatvalue = ($signbit ? 0 : -0); - } elseif (($exponent == 0) && ($fraction != 0)) { - // These are 'unnormalized' values - $floatvalue = pow(2, (-1 * (pow(2, $exponentbits - 1) - 2))) * getid3_lib::DecimalBinary2Float($fractionstring); - if ($signbit == '1') { - $floatvalue *= -1; - } - } elseif ($exponent != 0) { - $floatvalue = pow(2, ($exponent - (pow(2, $exponentbits - 1) - 1))) * (1 + getid3_lib::DecimalBinary2Float($fractionstring)); - if ($signbit == '1') { - $floatvalue *= -1; - } - } - return (float) $floatvalue; - } - - - function BigEndian2Int($byteword, $synchsafe=false, $signed=false) { - $intvalue = 0; - $bytewordlen = strlen($byteword); - for ($i = 0; $i < $bytewordlen; $i++) { - if ($synchsafe) { // disregard MSB, effectively 7-bit bytes - $intvalue = $intvalue | (ord($byteword{$i}) & 0x7F) << (($bytewordlen - 1 - $i) * 7); - } else { - $intvalue += ord($byteword{$i}) * pow(256, ($bytewordlen - 1 - $i)); - } - } - if ($signed && !$synchsafe) { - // synchsafe ints are not allowed to be signed - switch ($bytewordlen) { - case 1: - case 2: - case 3: - case 4: - $signmaskbit = 0x80 << (8 * ($bytewordlen - 1)); - if ($intvalue & $signmaskbit) { - $intvalue = 0 - ($intvalue & ($signmaskbit - 1)); - } - break; - - default: - die('ERROR: Cannot have signed integers larger than 32-bits in getid3_lib::BigEndian2Int()'); - break; - } - } - return getid3_lib::CastAsInt($intvalue); - } - - - function LittleEndian2Int($byteword, $signed=false) { - return getid3_lib::BigEndian2Int(strrev($byteword), false, $signed); - } - - - function BigEndian2Bin($byteword) { - $binvalue = ''; - $bytewordlen = strlen($byteword); - for ($i = 0; $i < $bytewordlen; $i++) { - $binvalue .= str_pad(decbin(ord($byteword{$i})), 8, '0', STR_PAD_LEFT); - } - return $binvalue; - } - - - function BigEndian2String($number, $minbytes=1, $synchsafe=false, $signed=false) { - if ($number < 0) { - return false; - } - $maskbyte = (($synchsafe || $signed) ? 0x7F : 0xFF); - $intstring = ''; - if ($signed) { - if ($minbytes > 4) { - die('ERROR: Cannot have signed integers larger than 32-bits in getid3_lib::BigEndian2String()'); - } - $number = $number & (0x80 << (8 * ($minbytes - 1))); - } - while ($number != 0) { - $quotient = ($number / ($maskbyte + 1)); - $intstring = chr(ceil(($quotient - floor($quotient)) * $maskbyte)).$intstring; - $number = floor($quotient); - } - return str_pad($intstring, $minbytes, "\x00", STR_PAD_LEFT); - } - - - function Dec2Bin($number) { - while ($number >= 256) { - $bytes[] = (($number / 256) - (floor($number / 256))) * 256; - $number = floor($number / 256); - } - $bytes[] = $number; - $binstring = ''; - for ($i = 0; $i < count($bytes); $i++) { - $binstring = (($i == count($bytes) - 1) ? decbin($bytes[$i]) : str_pad(decbin($bytes[$i]), 8, '0', STR_PAD_LEFT)).$binstring; - } - return $binstring; - } - - - function Bin2Dec($binstring, $signed=false) { - $signmult = 1; - if ($signed) { - if ($binstring{0} == '1') { - $signmult = -1; - } - $binstring = substr($binstring, 1); - } - $decvalue = 0; - for ($i = 0; $i < strlen($binstring); $i++) { - $decvalue += ((int) substr($binstring, strlen($binstring) - $i - 1, 1)) * pow(2, $i); - } - return getid3_lib::CastAsInt($decvalue * $signmult); - } - - - function Bin2String($binstring) { - // return 'hi' for input of '0110100001101001' - $string = ''; - $binstringreversed = strrev($binstring); - for ($i = 0; $i < strlen($binstringreversed); $i += 8) { - $string = chr(getid3_lib::Bin2Dec(strrev(substr($binstringreversed, $i, 8)))).$string; - } - return $string; - } - - - function LittleEndian2String($number, $minbytes=1, $synchsafe=false) { - $intstring = ''; - while ($number > 0) { - if ($synchsafe) { - $intstring = $intstring.chr($number & 127); - $number >>= 7; - } else { - $intstring = $intstring.chr($number & 255); - $number >>= 8; - } - } - return str_pad($intstring, $minbytes, "\x00", STR_PAD_RIGHT); - } - - - function array_merge_clobber($array1, $array2) { - // written by kcØhireability*com - // taken from http://www.php.net/manual/en/function.array-merge-recursive.php - if (!is_array($array1) || !is_array($array2)) { - return false; - } - $newarray = $array1; - foreach ($array2 as $key => $val) { - if (is_array($val) && isset($newarray[$key]) && is_array($newarray[$key])) { - $newarray[$key] = getid3_lib::array_merge_clobber($newarray[$key], $val); - } else { - $newarray[$key] = $val; - } - } - return $newarray; - } - - - function array_merge_noclobber($array1, $array2) { - if (!is_array($array1) || !is_array($array2)) { - return false; - } - $newarray = $array1; - foreach ($array2 as $key => $val) { - if (is_array($val) && isset($newarray[$key]) && is_array($newarray[$key])) { - $newarray[$key] = getid3_lib::array_merge_noclobber($newarray[$key], $val); - } elseif (!isset($newarray[$key])) { - $newarray[$key] = $val; - } - } - return $newarray; - } - - - function fileextension($filename, $numextensions=1) { - if (strstr($filename, '.')) { - $reversedfilename = strrev($filename); - $offset = 0; - for ($i = 0; $i < $numextensions; $i++) { - $offset = strpos($reversedfilename, '.', $offset + 1); - if ($offset === false) { - return ''; - } - } - return strrev(substr($reversedfilename, 0, $offset)); - } - return ''; - } - - - function PlaytimeString($playtimeseconds) { - $contentseconds = round((($playtimeseconds / 60) - floor($playtimeseconds / 60)) * 60); - $contentminutes = floor($playtimeseconds / 60); - if ($contentseconds >= 60) { - $contentseconds -= 60; - $contentminutes++; - } - return intval($contentminutes).':'.str_pad($contentseconds, 2, 0, STR_PAD_LEFT); - } - - - function image_type_to_mime_type($imagetypeid) { - // only available in PHP v4.3.0+ - static $image_type_to_mime_type = array(); - if (empty($image_type_to_mime_type)) { - $image_type_to_mime_type[1] = 'image/gif'; // GIF - $image_type_to_mime_type[2] = 'image/jpeg'; // JPEG - $image_type_to_mime_type[3] = 'image/png'; // PNG - $image_type_to_mime_type[4] = 'application/x-shockwave-flash'; // Flash - $image_type_to_mime_type[5] = 'image/psd'; // PSD - $image_type_to_mime_type[6] = 'image/bmp'; // BMP - $image_type_to_mime_type[7] = 'image/tiff'; // TIFF: little-endian (Intel) - $image_type_to_mime_type[8] = 'image/tiff'; // TIFF: big-endian (Motorola) - //$image_type_to_mime_type[9] = 'image/jpc'; // JPC - //$image_type_to_mime_type[10] = 'image/jp2'; // JPC - //$image_type_to_mime_type[11] = 'image/jpx'; // JPC - //$image_type_to_mime_type[12] = 'image/jb2'; // JPC - $image_type_to_mime_type[13] = 'application/x-shockwave-flash'; // Shockwave - $image_type_to_mime_type[14] = 'image/iff'; // IFF - } - return (isset($image_type_to_mime_type[$imagetypeid]) ? $image_type_to_mime_type[$imagetypeid] : 'application/octet-stream'); - } - - - function DateMac2Unix($macdate) { - // Macintosh timestamp: seconds since 00:00h January 1, 1904 - // UNIX timestamp: seconds since 00:00h January 1, 1970 - return getid3_lib::CastAsInt($macdate - 2082844800); - } - - - function FixedPoint8_8($rawdata) { - return getid3_lib::BigEndian2Int(substr($rawdata, 0, 1)) + (float) (getid3_lib::BigEndian2Int(substr($rawdata, 1, 1)) / pow(2, 8)); - } - - - function FixedPoint16_16($rawdata) { - return getid3_lib::BigEndian2Int(substr($rawdata, 0, 2)) + (float) (getid3_lib::BigEndian2Int(substr($rawdata, 2, 2)) / pow(2, 16)); - } - - - function FixedPoint2_30($rawdata) { - $binarystring = getid3_lib::BigEndian2Bin($rawdata); - return getid3_lib::Bin2Dec(substr($binarystring, 0, 2)) + (float) (getid3_lib::Bin2Dec(substr($binarystring, 2, 30)) / 1073741824); - } - - - function CreateDeepArray($ArrayPath, $Separator, $Value) { - // assigns $Value to a nested array path: - // $foo = getid3_lib::CreateDeepArray('/path/to/my', '/', 'file.txt') - // is the same as: - // $foo = array('path'=>array('to'=>'array('my'=>array('file.txt')))); - // or - // $foo['path']['to']['my'] = 'file.txt'; - while ($ArrayPath && ($ArrayPath{0} == $Separator)) { - $ArrayPath = substr($ArrayPath, 1); - } - if (($pos = strpos($ArrayPath, $Separator)) !== false) { - $ReturnedArray[substr($ArrayPath, 0, $pos)] = getid3_lib::CreateDeepArray(substr($ArrayPath, $pos + 1), $Separator, $Value); - } else { - $ReturnedArray[$ArrayPath] = $Value; - } - return $ReturnedArray; - } - - function array_max($arraydata, $returnkey=false) { - $maxvalue = false; - $maxkey = false; - foreach ($arraydata as $key => $value) { - if (!is_array($value)) { - if ($value > $maxvalue) { - $maxvalue = $value; - $maxkey = $key; - } - } - } - return ($returnkey ? $maxkey : $maxvalue); - } - - function array_min($arraydata, $returnkey=false) { - $minvalue = false; - $minkey = false; - foreach ($arraydata as $key => $value) { - if (!is_array($value)) { - if ($value > $minvalue) { - $minvalue = $value; - $minkey = $key; - } - } - } - return ($returnkey ? $minkey : $minvalue); - } - - - function md5_file($file) { - - // md5_file() exists in PHP 4.2.0+. - if (function_exists('md5_file')) { - return md5_file($file); - } - - if (GETID3_OS_ISWINDOWS) { - - $RequiredFiles = array('cygwin1.dll', 'md5sum.exe'); - foreach ($RequiredFiles as $required_file) { - if (!is_readable(GETID3_HELPERAPPSDIR.$required_file)) { - die(implode(' and ', $RequiredFiles).' are required in '.GETID3_HELPERAPPSDIR.' for getid3_lib::md5_file() to function under Windows in PHP < v4.2.0'); - } - } - $commandline = GETID3_HELPERAPPSDIR.'md5sum.exe "'.str_replace('/', DIRECTORY_SEPARATOR, $file).'"'; - if (ereg("^[\\]?([0-9a-f]{32})", strtolower(`$commandline`), $r)) { - return $r[1]; - } - - } else { - - // The following works under UNIX only - $file = str_replace('`', '\\`', $file); - if (ereg("^([0-9a-f]{32})[ \t\n\r]", `md5sum "$file"`, $r)) { - return $r[1]; - } - - } - return false; - } - - - function sha1_file($file) { - - // sha1_file() exists in PHP 4.3.0+. - if (function_exists('sha1_file')) { - return sha1_file($file); - } - - $file = str_replace('`', '\\`', $file); - - if (GETID3_OS_ISWINDOWS) { - - $RequiredFiles = array('cygwin1.dll', 'sha1sum.exe'); - foreach ($RequiredFiles as $required_file) { - if (!is_readable(GETID3_HELPERAPPSDIR.$required_file)) { - die(implode(' and ', $RequiredFiles).' are required in '.GETID3_HELPERAPPSDIR.' for getid3_lib::sha1_file() to function under Windows in PHP < v4.3.0'); - } - } - $commandline = GETID3_HELPERAPPSDIR.'sha1sum.exe "'.str_replace('/', DIRECTORY_SEPARATOR, $file).'"'; - if (ereg("^sha1=([0-9a-f]{40})", strtolower(`$commandline`), $r)) { - return $r[1]; - } - - } else { - - $commandline = 'sha1sum '.escapeshellarg($file).''; - if (ereg("^([0-9a-f]{40})[ \t\n\r]", strtolower(`$commandline`), $r)) { - return $r[1]; - } - - } - - return false; - } - - - // Allan Hansen - // getid3_lib::md5_data() - returns md5sum for a file from startuing position to absolute end position - function hash_data($file, $offset, $end, $algorithm) { - - switch ($algorithm) { - case 'md5': - $hash_function = 'md5_file'; - $unix_call = 'md5sum'; - $windows_call = 'md5sum.exe'; - $hash_length = 32; - break; - - case 'sha1': - $hash_function = 'sha1_file'; - $unix_call = 'sha1sum'; - $windows_call = 'sha1sum.exe'; - $hash_length = 40; - break; - - default: - die('Invalid algorithm ('.$algorithm.') in getid3_lib::hash_data()'); - break; - } - $size = $end - $offset; - while (true) { - if (GETID3_OS_ISWINDOWS) { - - // It seems that sha1sum.exe for Windows only works on physical files, does not accept piped data - // Fall back to create-temp-file method: - if ($algorithm == 'sha1') { - break; - } - - $RequiredFiles = array('cygwin1.dll', 'head.exe', 'tail.exe', $windows_call); - foreach ($RequiredFiles as $required_file) { - if (!is_readable(GETID3_HELPERAPPSDIR.$required_file)) { - // helper apps not available - fall back to old method - break; - } - } - $commandline = GETID3_HELPERAPPSDIR.'head.exe -c '.$end.' "'.escapeshellarg(str_replace('/', DIRECTORY_SEPARATOR, $file)).'" | '; - $commandline .= GETID3_HELPERAPPSDIR.'tail.exe -c '.$size.' | '; - $commandline .= GETID3_HELPERAPPSDIR.$windows_call; - - } else { - - $commandline = 'head -c'.$end.' '.escapeshellarg($file).' | '; - $commandline .= 'tail -c'.$size.' | '; - $commandline .= $unix_call; - - } - if ((bool) ini_get('safe_mode')) { - $ThisFileInfo['warning'][] = 'PHP running in Safe Mode - backtick operator not available, using slower non-system-call '.$algorithm.' algorithm'; - break; - } - return substr(`$commandline`, 0, $hash_length); - } - - // try to create a temporary file in the system temp directory - invalid dirname should force to system temp dir - if (($data_filename = tempnam('*', 'getID3')) === false) { - // can't find anywhere to create a temp file, just die - return false; - } - - // Init - $result = false; - - // copy parts of file - if ($fp = @fopen($file, 'rb')) { - - if ($fp_data = @fopen($data_filename, 'wb')) { - - fseek($fp, $offset, SEEK_SET); - $byteslefttowrite = $end - $offset; - while (($byteslefttowrite > 0) && ($buffer = fread($fp, GETID3_FREAD_BUFFER_SIZE))) { - $byteswritten = fwrite($fp_data, $buffer, $byteslefttowrite); - $byteslefttowrite -= $byteswritten; - } - fclose($fp_data); - $result = getid3_lib::$hash_function($data_filename); - - } - fclose($fp); - } - unlink($data_filename); - return $result; - } - - - function iconv_fallback_int_utf8($charval) { - if ($charval < 128) { - // 0bbbbbbb - $newcharstring = chr($charval); - } elseif ($charval < 2048) { - // 110bbbbb 10bbbbbb - $newcharstring = chr(($charval >> 6) | 0xC0); - $newcharstring .= chr(($charval & 0x3F) | 0x80); - } elseif ($charval < 65536) { - // 1110bbbb 10bbbbbb 10bbbbbb - $newcharstring = chr(($charval >> 12) | 0xE0); - $newcharstring .= chr(($charval >> 6) | 0xC0); - $newcharstring .= chr(($charval & 0x3F) | 0x80); - } else { - // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb - $newcharstring = chr(($charval >> 18) | 0xF0); - $newcharstring .= chr(($charval >> 12) | 0xC0); - $newcharstring .= chr(($charval >> 6) | 0xC0); - $newcharstring .= chr(($charval & 0x3F) | 0x80); - } - return $newcharstring; - } - - // ISO-8859-1 => UTF-8 - function iconv_fallback_iso88591_utf8($string, $bom=false) { - if (function_exists('utf8_encode')) { - return utf8_encode($string); - } - // utf8_encode() unavailable, use getID3()'s iconv_fallback() conversions (possibly PHP is compiled without XML support) - $newcharstring = ''; - if ($bom) { - $newcharstring .= "\xEF\xBB\xBF"; - } - for ($i = 0; $i < strlen($string); $i++) { - $charval = ord($string{$i}); - $newcharstring .= getid3_lib::iconv_fallback_int_utf8($charval); - } - return $newcharstring; - } - - // ISO-8859-1 => UTF-16BE - function iconv_fallback_iso88591_utf16be($string, $bom=false) { - $newcharstring = ''; - if ($bom) { - $newcharstring .= "\xFE\xFF"; - } - for ($i = 0; $i < strlen($string); $i++) { - $newcharstring .= "\x00".$string{$i}; - } - return $newcharstring; - } - - // ISO-8859-1 => UTF-16LE - function iconv_fallback_iso88591_utf16le($string, $bom=false) { - $newcharstring = ''; - if ($bom) { - $newcharstring .= "\xFF\xFE"; - } - for ($i = 0; $i < strlen($string); $i++) { - $newcharstring .= $string{$i}."\x00"; - } - return $newcharstring; - } - - // ISO-8859-1 => UTF-16LE (BOM) - function iconv_fallback_iso88591_utf16($string) { - return getid3_lib::iconv_fallback_iso88591_utf16le($string, true); - } - - // UTF-8 => ISO-8859-1 - function iconv_fallback_utf8_iso88591($string) { - if (function_exists('utf8_decode')) { - return utf8_decode($string); - } - // utf8_decode() unavailable, use getID3()'s iconv_fallback() conversions (possibly PHP is compiled without XML support) - $newcharstring = ''; - $offset = 0; - $stringlength = strlen($string); - while ($offset < $stringlength) { - if ((ord($string{$offset}) | 0x07) == 0xF7) { - // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb - $charval = ((ord($string{($offset + 0)}) & 0x07) << 18) & - ((ord($string{($offset + 1)}) & 0x3F) << 12) & - ((ord($string{($offset + 2)}) & 0x3F) << 6) & - (ord($string{($offset + 3)}) & 0x3F); - $offset += 4; - } elseif ((ord($string{$offset}) | 0x0F) == 0xEF) { - // 1110bbbb 10bbbbbb 10bbbbbb - $charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) & - ((ord($string{($offset + 1)}) & 0x3F) << 6) & - (ord($string{($offset + 2)}) & 0x3F); - $offset += 3; - } elseif ((ord($string{$offset}) | 0x1F) == 0xDF) { - // 110bbbbb 10bbbbbb - $charval = ((ord($string{($offset + 0)}) & 0x1F) << 6) & - (ord($string{($offset + 1)}) & 0x3F); - $offset += 2; - } elseif ((ord($string{$offset}) | 0x7F) == 0x7F) { - // 0bbbbbbb - $charval = ord($string{$offset}); - $offset += 1; - } else { - // error? throw some kind of warning here? - $charval = false; - $offset += 1; - } - if ($charval !== false) { - $newcharstring .= (($charval < 256) ? chr($charval) : '?'); - } - } - return $newcharstring; - } - - // UTF-8 => UTF-16BE - function iconv_fallback_utf8_utf16be($string, $bom=false) { - $newcharstring = ''; - if ($bom) { - $newcharstring .= "\xFE\xFF"; - } - $offset = 0; - $stringlength = strlen($string); - while ($offset < $stringlength) { - if ((ord($string{$offset}) | 0x07) == 0xF7) { - // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb - $charval = ((ord($string{($offset + 0)}) & 0x07) << 18) & - ((ord($string{($offset + 1)}) & 0x3F) << 12) & - ((ord($string{($offset + 2)}) & 0x3F) << 6) & - (ord($string{($offset + 3)}) & 0x3F); - $offset += 4; - } elseif ((ord($string{$offset}) | 0x0F) == 0xEF) { - // 1110bbbb 10bbbbbb 10bbbbbb - $charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) & - ((ord($string{($offset + 1)}) & 0x3F) << 6) & - (ord($string{($offset + 2)}) & 0x3F); - $offset += 3; - } elseif ((ord($string{$offset}) | 0x1F) == 0xDF) { - // 110bbbbb 10bbbbbb - $charval = ((ord($string{($offset + 0)}) & 0x1F) << 6) & - (ord($string{($offset + 1)}) & 0x3F); - $offset += 2; - } elseif ((ord($string{$offset}) | 0x7F) == 0x7F) { - // 0bbbbbbb - $charval = ord($string{$offset}); - $offset += 1; - } else { - // error? throw some kind of warning here? - $charval = false; - $offset += 1; - } - if ($charval !== false) { - $newcharstring .= (($charval < 65536) ? getid3_lib::BigEndian2String($charval, 2) : "\x00".'?'); - } - } - return $newcharstring; - } - - // UTF-8 => UTF-16LE - function iconv_fallback_utf8_utf16le($string, $bom=false) { - $newcharstring = ''; - if ($bom) { - $newcharstring .= "\xFF\xFE"; - } - $offset = 0; - $stringlength = strlen($string); - while ($offset < $stringlength) { - if ((ord($string{$offset}) | 0x07) == 0xF7) { - // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb - $charval = ((ord($string{($offset + 0)}) & 0x07) << 18) & - ((ord($string{($offset + 1)}) & 0x3F) << 12) & - ((ord($string{($offset + 2)}) & 0x3F) << 6) & - (ord($string{($offset + 3)}) & 0x3F); - $offset += 4; - } elseif ((ord($string{$offset}) | 0x0F) == 0xEF) { - // 1110bbbb 10bbbbbb 10bbbbbb - $charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) & - ((ord($string{($offset + 1)}) & 0x3F) << 6) & - (ord($string{($offset + 2)}) & 0x3F); - $offset += 3; - } elseif ((ord($string{$offset}) | 0x1F) == 0xDF) { - // 110bbbbb 10bbbbbb - $charval = ((ord($string{($offset + 0)}) & 0x1F) << 6) & - (ord($string{($offset + 1)}) & 0x3F); - $offset += 2; - } elseif ((ord($string{$offset}) | 0x7F) == 0x7F) { - // 0bbbbbbb - $charval = ord($string{$offset}); - $offset += 1; - } else { - // error? maybe throw some warning here? - $charval = false; - $offset += 1; - } - if ($charval !== false) { - $newcharstring .= (($charval < 65536) ? getid3_lib::LittleEndian2String($charval, 2) : '?'."\x00"); - } - } - return $newcharstring; - } - - // UTF-8 => UTF-16LE (BOM) - function iconv_fallback_utf8_utf16($string) { - return getid3_lib::iconv_fallback_utf8_utf16le($string, true); - } - - // UTF-16BE => UTF-8 - function iconv_fallback_utf16be_utf8($string) { - if (substr($string, 0, 2) == "\xFE\xFF") { - // strip BOM - $string = substr($string, 2); - } - $newcharstring = ''; - for ($i = 0; $i < strlen($string); $i += 2) { - $charval = getid3_lib::BigEndian2Int(substr($string, $i, 2)); - $newcharstring .= getid3_lib::iconv_fallback_int_utf8($charval); - } - return $newcharstring; - } - - // UTF-16LE => UTF-8 - function iconv_fallback_utf16le_utf8($string) { - if (substr($string, 0, 2) == "\xFF\xFE") { - // strip BOM - $string = substr($string, 2); - } - $newcharstring = ''; - for ($i = 0; $i < strlen($string); $i += 2) { - $charval = getid3_lib::LittleEndian2Int(substr($string, $i, 2)); - $newcharstring .= getid3_lib::iconv_fallback_int_utf8($charval); - } - return $newcharstring; - } - - // UTF-16BE => ISO-8859-1 - function iconv_fallback_utf16be_iso88591($string) { - if (substr($string, 0, 2) == "\xFE\xFF") { - // strip BOM - $string = substr($string, 2); - } - $newcharstring = ''; - for ($i = 0; $i < strlen($string); $i += 2) { - $charval = getid3_lib::BigEndian2Int(substr($string, $i, 2)); - $newcharstring .= (($charval < 256) ? chr($charval) : '?'); - } - return $newcharstring; - } - - // UTF-16LE => ISO-8859-1 - function iconv_fallback_utf16le_iso88591($string) { - if (substr($string, 0, 2) == "\xFF\xFE") { - // strip BOM - $string = substr($string, 2); - } - $newcharstring = ''; - for ($i = 0; $i < strlen($string); $i += 2) { - $charval = getid3_lib::LittleEndian2Int(substr($string, $i, 2)); - $newcharstring .= (($charval < 256) ? chr($charval) : '?'); - } - return $newcharstring; - } - - // UTF-16 (BOM) => ISO-8859-1 - function iconv_fallback_utf16_iso88591($string) { - $bom = substr($string, 0, 2); - if ($bom == "\xFE\xFF") { - return getid3_lib::iconv_fallback_utf16be_iso88591(substr($string, 2)); - } elseif ($bom == "\xFF\xFE") { - return getid3_lib::iconv_fallback_utf16le_iso88591(substr($string, 2)); - } - return $string; - } - - // UTF-16 (BOM) => UTF-8 - function iconv_fallback_utf16_utf8($string) { - $bom = substr($string, 0, 2); - if ($bom == "\xFE\xFF") { - return getid3_lib::iconv_fallback_utf16be_utf8(substr($string, 2)); - } elseif ($bom == "\xFF\xFE") { - return getid3_lib::iconv_fallback_utf16le_utf8(substr($string, 2)); - } - return $string; - } - - function iconv_fallback($in_charset, $out_charset, $string) { - - if ($in_charset == $out_charset) { - return $string; - } - - static $iconv_broken_or_unavailable = array(); - if (is_null(@$iconv_broken_or_unavailable[$in_charset.'_'.$out_charset])) { - $GETID3_ICONV_TEST_STRING = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ'; - - // Check iconv() - if (function_exists('iconv')) { - if (@iconv($in_charset, 'ISO-8859-1', @iconv('ISO-8859-1', $in_charset, $GETID3_ICONV_TEST_STRING)) == $GETID3_ICONV_TEST_STRING) { - if (@iconv($out_charset, 'ISO-8859-1', @iconv('ISO-8859-1', $out_charset, $GETID3_ICONV_TEST_STRING)) == $GETID3_ICONV_TEST_STRING) { - // everything works, use iconv() - $iconv_broken_or_unavailable[$in_charset.'_'.$out_charset] = false; - } else { - // iconv() available, but broken. Use getID3()'s iconv_fallback() conversions instead - // known issue in PHP v4.1.x - $iconv_broken_or_unavailable[$in_charset.'_'.$out_charset] = true; - } - } else { - // iconv() available, but broken. Use getID3()'s iconv_fallback() conversions instead - // known issue in PHP v4.1.x - $iconv_broken_or_unavailable[$in_charset.'_'.$out_charset] = true; - } - } else { - // iconv() unavailable, use getID3()'s iconv_fallback() conversions - $iconv_broken_or_unavailable[$in_charset.'_'.$out_charset] = true; - } - } - - if ($iconv_broken_or_unavailable[$in_charset.'_'.$out_charset]) { - static $ConversionFunctionList = array(); - if (empty($ConversionFunctionList)) { - $ConversionFunctionList['ISO-8859-1']['UTF-8'] = 'iconv_fallback_iso88591_utf8'; - $ConversionFunctionList['ISO-8859-1']['UTF-16'] = 'iconv_fallback_iso88591_utf16'; - $ConversionFunctionList['ISO-8859-1']['UTF-16BE'] = 'iconv_fallback_iso88591_utf16be'; - $ConversionFunctionList['ISO-8859-1']['UTF-16LE'] = 'iconv_fallback_iso88591_utf16le'; - $ConversionFunctionList['UTF-8']['ISO-8859-1'] = 'iconv_fallback_utf8_iso88591'; - $ConversionFunctionList['UTF-8']['UTF-16'] = 'iconv_fallback_utf8_utf16'; - $ConversionFunctionList['UTF-8']['UTF-16BE'] = 'iconv_fallback_utf8_utf16be'; - $ConversionFunctionList['UTF-8']['UTF-16LE'] = 'iconv_fallback_utf8_utf16le'; - $ConversionFunctionList['UTF-16']['ISO-8859-1'] = 'iconv_fallback_utf16_iso88591'; - $ConversionFunctionList['UTF-16']['UTF-8'] = 'iconv_fallback_utf16_utf8'; - $ConversionFunctionList['UTF-16LE']['ISO-8859-1'] = 'iconv_fallback_utf16le_iso88591'; - $ConversionFunctionList['UTF-16LE']['UTF-8'] = 'iconv_fallback_utf16le_utf8'; - $ConversionFunctionList['UTF-16BE']['ISO-8859-1'] = 'iconv_fallback_utf16be_iso88591'; - $ConversionFunctionList['UTF-16BE']['UTF-8'] = 'iconv_fallback_utf16be_utf8'; - } - if (isset($ConversionFunctionList[strtoupper($in_charset)][strtoupper($out_charset)])) { - $ConversionFunction = $ConversionFunctionList[strtoupper($in_charset)][strtoupper($out_charset)]; - return getid3_lib::$ConversionFunction($string); - } - die('PHP does not have iconv() support - cannot convert from '.$in_charset.' to '.$out_charset); - } - - if ($converted_string = @iconv($in_charset, $out_charset.'//TRANSLIT', $string)) { - switch ($out_charset) { - case 'ISO-8859-1': - $converted_string = rtrim($converted_string, "\x00"); - break; - } - return $converted_string; - } - - // iconv() may sometimes fail with "illegal character in input string" error message - // and return an empty string, but returning the unconverted string is more useful - return $string; - } - - - function MultiByteCharString2HTML($string, $charset='ISO-8859-1') { - $HTMLstring = ''; - - switch ($charset) { - case 'ISO-8859-1': - case 'ISO8859-1': - case 'ISO-8859-15': - case 'ISO8859-15': - case 'cp866': - case 'ibm866': - case '866': - case 'cp1251': - case 'Windows-1251': - case 'win-1251': - case '1251': - case 'cp1252': - case 'Windows-1252': - case '1252': - case 'KOI8-R': - case 'koi8-ru': - case 'koi8r': - case 'BIG5': - case '950': - case 'GB2312': - case '936': - case 'BIG5-HKSCS': - case 'Shift_JIS': - case 'SJIS': - case '932': - case 'EUC-JP': - case 'EUCJP': - $HTMLstring = htmlentities($string, ENT_COMPAT, $charset); - break; - - case 'UTF-8': - $strlen = strlen($string); - for ($i = 0; $i < $strlen; $i++) { - $char_ord_val = ord($string{$i}); - $charval = 0; - if ($char_ord_val < 0x80) { - $charval = $char_ord_val; - } elseif ((($char_ord_val & 0xF0) >> 4) == 0x0F) { - $charval = (($char_ord_val & 0x07) << 18); - $charval += ((ord($string{++$i}) & 0x3F) << 12); - $charval += ((ord($string{++$i}) & 0x3F) << 6); - $charval += (ord($string{++$i}) & 0x3F); - } elseif ((($char_ord_val & 0xE0) >> 5) == 0x07) { - $charval = (($char_ord_val & 0x0F) << 12); - $charval += ((ord($string{++$i}) & 0x3F) << 6); - $charval += (ord($string{++$i}) & 0x3F); - } elseif ((($char_ord_val & 0xC0) >> 6) == 0x03) { - $charval = (($char_ord_val & 0x1F) << 6); - $charval += (ord($string{++$i}) & 0x3F); - } - if (($charval >= 32) && ($charval <= 127)) { - $HTMLstring .= chr($charval); - } else { - $HTMLstring .= '&#'.$charval.';'; - } - } - break; - - case 'UTF-16LE': - for ($i = 0; $i < strlen($string); $i += 2) { - $charval = getid3_lib::LittleEndian2Int(substr($string, $i, 2)); - if (($charval >= 32) && ($charval <= 127)) { - $HTMLstring .= chr($charval); - } else { - $HTMLstring .= '&#'.$charval.';'; - } - } - break; - - case 'UTF-16BE': - for ($i = 0; $i < strlen($string); $i += 2) { - $charval = getid3_lib::BigEndian2Int(substr($string, $i, 2)); - if (($charval >= 32) && ($charval <= 127)) { - $HTMLstring .= chr($charval); - } else { - $HTMLstring .= '&#'.$charval.';'; - } - } - break; - - default: - $HTMLstring = 'ERROR: Character set "'.$charset.'" not supported in MultiByteCharString2HTML()'; - break; - } - return $HTMLstring; - } - - - - function RGADnameLookup($namecode) { - static $RGADname = array(); - if (empty($RGADname)) { - $RGADname[0] = 'not set'; - $RGADname[1] = 'Track Gain Adjustment'; - $RGADname[2] = 'Album Gain Adjustment'; - } - - return (isset($RGADname[$namecode]) ? $RGADname[$namecode] : ''); - } - - - function RGADoriginatorLookup($originatorcode) { - static $RGADoriginator = array(); - if (empty($RGADoriginator)) { - $RGADoriginator[0] = 'unspecified'; - $RGADoriginator[1] = 'pre-set by artist/producer/mastering engineer'; - $RGADoriginator[2] = 'set by user'; - $RGADoriginator[3] = 'determined automatically'; - } - - return (isset($RGADoriginator[$originatorcode]) ? $RGADoriginator[$originatorcode] : ''); - } - - - function RGADadjustmentLookup($rawadjustment, $signbit) { - $adjustment = $rawadjustment / 10; - if ($signbit == 1) { - $adjustment *= -1; - } - return (float) $adjustment; - } - - - function RGADgainString($namecode, $originatorcode, $replaygain) { - if ($replaygain < 0) { - $signbit = '1'; - } else { - $signbit = '0'; - } - $storedreplaygain = intval(round($replaygain * 10)); - $gainstring = str_pad(decbin($namecode), 3, '0', STR_PAD_LEFT); - $gainstring .= str_pad(decbin($originatorcode), 3, '0', STR_PAD_LEFT); - $gainstring .= $signbit; - $gainstring .= str_pad(decbin($storedreplaygain), 9, '0', STR_PAD_LEFT); - - return $gainstring; - } - - function RGADamplitude2dB($amplitude) { - return 20 * log10($amplitude); - } - - - function GetDataImageSize($imgData) { - $GetDataImageSize = false; - if ($tempfilename = tempnam('*', 'getID3')) { - if ($tmp = @fopen($tempfilename, 'wb')) { - fwrite($tmp, $imgData); - fclose($tmp); - $GetDataImageSize = @GetImageSize($tempfilename); - } - unlink($tempfilename); - } - return $GetDataImageSize; - } - - function ImageTypesLookup($imagetypeid) { - static $ImageTypesLookup = array(); - if (empty($ImageTypesLookup)) { - $ImageTypesLookup[1] = 'gif'; - $ImageTypesLookup[2] = 'jpeg'; - $ImageTypesLookup[3] = 'png'; - $ImageTypesLookup[4] = 'swf'; - $ImageTypesLookup[5] = 'psd'; - $ImageTypesLookup[6] = 'bmp'; - $ImageTypesLookup[7] = 'tiff (little-endian)'; - $ImageTypesLookup[8] = 'tiff (big-endian)'; - $ImageTypesLookup[9] = 'jpc'; - $ImageTypesLookup[10] = 'jp2'; - $ImageTypesLookup[11] = 'jpx'; - $ImageTypesLookup[12] = 'jb2'; - $ImageTypesLookup[13] = 'swc'; - $ImageTypesLookup[14] = 'iff'; - } - return (isset($ImageTypesLookup[$imagetypeid]) ? $ImageTypesLookup[$imagetypeid] : ''); - } - - function CopyTagsToComments(&$ThisFileInfo) { - - // Copy all entries from ['tags'] into common ['comments'] - if (!empty($ThisFileInfo['tags'])) { - foreach ($ThisFileInfo['tags'] as $tagtype => $tagarray) { - foreach ($tagarray as $tagname => $tagdata) { - foreach ($tagdata as $key => $value) { - if (!empty($value)) { - if (empty($ThisFileInfo['comments'][$tagname])) { - - // fall through and append value - - } elseif ($tagtype == 'id3v1') { - - $newvaluelength = strlen(trim($value)); - foreach ($ThisFileInfo['comments'][$tagname] as $existingkey => $existingvalue) { - $oldvaluelength = strlen(trim($existingvalue)); - if (($newvaluelength <= $oldvaluelength) && (substr($existingvalue, 0, $newvaluelength) == trim($value))) { - // new value is identical but shorter-than (or equal-length to) one already in comments - skip - break 2; - } - } - - } else { - - $newvaluelength = strlen(trim($value)); - foreach ($ThisFileInfo['comments'][$tagname] as $existingkey => $existingvalue) { - $oldvaluelength = strlen(trim($existingvalue)); - if (($newvaluelength > $oldvaluelength) && (substr(trim($value), 0, strlen($existingvalue)) == $existingvalue)) { - $ThisFileInfo['comments'][$tagname][$existingkey] = trim($value); - break 2; - } - } - - } - if (empty($ThisFileInfo['comments'][$tagname]) || !in_array(trim($value), $ThisFileInfo['comments'][$tagname])) { - $ThisFileInfo['comments'][$tagname][] = trim($value); - } - } - } - } - } - - // Copy to ['comments_html'] - foreach ($ThisFileInfo['comments'] as $field => $values) { - foreach ($values as $index => $value) { - $ThisFileInfo['comments_html'][$field][$index] = str_replace('�', '', getid3_lib::MultiByteCharString2HTML($value, $ThisFileInfo['encoding'])); - } - } - } - } - - - function EmbeddedLookup($key, $begin, $end, $file, $name) { - - // Cached - static $cache; - if (isset($cache[$file][$name])) { - return @$cache[$file][$name][$key]; - } - - // Init - $keylength = strlen($key); - $line_count = $end - $begin - 7; - - // Open php file - $fp = fopen($file, 'r'); - - // Discard $begin lines - for ($i = 0; $i < ($begin + 3); $i++) { - fgets($fp, 1024); - } - - // Loop thru line - while (0 < $line_count--) { - - // Read line - $line = ltrim(fgets($fp, 1024), "\t "); - - // METHOD A: only cache the matching key - less memory but slower on next lookup of not-previously-looked-up key - //$keycheck = substr($line, 0, $keylength); - //if ($key == $keycheck) { - // $cache[$file][$name][$keycheck] = substr($line, $keylength + 1); - // break; - //} - - // METHOD B: cache all keys in this lookup - more memory but faster on next lookup of not-previously-looked-up key - //$cache[$file][$name][substr($line, 0, $keylength)] = trim(substr($line, $keylength + 1)); - @list($ThisKey, $ThisValue) = explode("\t", $line, 2); - $cache[$file][$name][$ThisKey] = trim($ThisValue); - } - - // Close and return - fclose($fp); - return @$cache[$file][$name][$key]; - } - - function IncludeDependency($filename, $sourcefile, $DieOnFailure=false) { - global $GETID3_ERRORARRAY; - - if (file_exists($filename)) { - if (@include_once($filename)) { - return true; - } else { - $diemessage = basename($sourcefile).' depends on '.$filename.', which has errors'; - } - } else { - $diemessage = basename($sourcefile).' depends on '.$filename.', which is missing'; - } - if ($DieOnFailure) { - die($diemessage); - } else { - $GETID3_ERRORARRAY[] = $diemessage; - } - return false; - } - -} - -?> diff --git a/getid3/getid3/getid3.php b/getid3/getid3/getid3.php deleted file mode 100644 index 944868d..0000000 --- a/getid3/getid3/getid3.php +++ /dev/null @@ -1,1305 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// // -// Please see readme.txt for more information // -// /// -///////////////////////////////////////////////////////////////// - -// Defines -define('GETID3_VERSION', '1.7.7'); -define('GETID3_FREAD_BUFFER_SIZE', 16384); // read buffer size in bytes - - - -class getID3 -{ - // public: Settings - var $encoding = 'ISO-8859-1'; // CASE SENSITIVE! - i.e. (must be supported by iconv()) - // Examples: ISO-8859-1 UTF-8 UTF-16 UTF-16BE - - var $encoding_id3v1 = 'ISO-8859-1'; // Should always be 'ISO-8859-1', but some tags may be written in other encodings such as 'EUC-CN' - - var $tempdir = '*'; // default '*' should use system temp dir - - // public: Optional tag checks - disable for speed. - var $option_tag_id3v1 = true; // Read and process ID3v1 tags - var $option_tag_id3v2 = true; // Read and process ID3v2 tags - var $option_tag_lyrics3 = true; // Read and process Lyrics3 tags - var $option_tag_apetag = true; // Read and process APE tags - var $option_tags_process = true; // Copy tags to root key 'tags' and encode to $this->encoding - var $option_tags_html = true; // Copy tags to root key 'tags_html' properly translated from various encodings to HTML entities - - // public: Optional tag/comment calucations - var $option_extra_info = true; // Calculate additional info such as bitrate, channelmode etc - - // public: Optional calculations - var $option_md5_data = false; // Get MD5 sum of data part - slow - var $option_md5_data_source = false; // Use MD5 of source file if availble - only FLAC and OptimFROG - var $option_sha1_data = false; // Get SHA1 sum of data part - slow - var $option_max_2gb_check = true; // Check whether file is larger than 2 Gb and thus not supported by PHP - - // private - var $filename; - - - // public: constructor - function getID3() - { - - $this->startup_error = ''; - $this->startup_warning = ''; - - // Check for PHP version >= 4.1.0 - if (phpversion() < '4.1.0') { - $this->startup_error .= 'getID3() requires PHP v4.1.0 or higher - you are running v'.phpversion(); - } - - // Check memory - $memory_limit = ini_get('memory_limit'); - if (preg_match('/([0-9]+)M/i', $memory_limit, $matches)) { - // could be stored as "16M" rather than 16777216 for example - $memory_limit = $matches[1] * 1048576; - } - if ($memory_limit <= 0) { - // memory limits probably disabled - } elseif ($memory_limit <= 3145728) { - $this->startup_error .= 'PHP has less than 3MB available memory and will very likely run out. Increase memory_limit in php.ini'; - } elseif ($memory_limit <= 12582912) { - $this->startup_warning .= 'PHP has less than 12MB available memory and might run out if all modules are loaded. Increase memory_limit in php.ini'; - } - - // Check safe_mode off - if ((bool) ini_get('safe_mode')) { - $this->warning('WARNING: Safe mode is on, shorten support disabled, md5data/sha1data for ogg vorbis disabled, ogg vorbos/flac tag writing disabled.'); - } - - - // define a constant rather than looking up every time it is needed - if (!defined('GETID3_OS_ISWINDOWS')) { - if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') { - define('GETID3_OS_ISWINDOWS', true); - } else { - define('GETID3_OS_ISWINDOWS', false); - } - } - - // Get base path of getID3() - ONCE - if (!defined('GETID3_INCLUDEPATH')) { - foreach (get_included_files() as $key => $val) { - if (basename($val) == 'getid3.php') { - define('GETID3_INCLUDEPATH', dirname($val).DIRECTORY_SEPARATOR); - break; - } - } - } - - // Load support library - if (!include_once(GETID3_INCLUDEPATH.'getid3.lib.php')) { - $this->startup_error .= 'getid3.lib.php is missing or corrupt'; - } - - - // Needed for Windows only: - // Define locations of helper applications for Shorten, VorbisComment, MetaFLAC - // as well as other helper functions such as head, tail, md5sum, etc - // IMPORTANT: This path cannot have spaces in it. If neccesary, use the 8dot3 equivalent - // ie for "C:/Program Files/Apache/" put "C:/PROGRA~1/APACHE/" - // IMPORTANT: This path must include the trailing slash - if (GETID3_OS_ISWINDOWS && !defined('GETID3_HELPERAPPSDIR')) { - $helperappsdir = GETID3_INCLUDEPATH.'..'.DIRECTORY_SEPARATOR.'helperapps'; // must not have any space in this path - - if (!is_dir($helperappsdir)) { - - $this->startup_error .= '"'.$helperappsdir.'" cannot be defined as GETID3_HELPERAPPSDIR because it does not exist'; - - } elseif (strpos(realpath($helperappsdir), ' ') !== false) { - - $DirPieces = explode(DIRECTORY_SEPARATOR, realpath($helperappsdir)); - $DirPieces8 = $DirPieces; - - $CLIdir = $DirPieces[0].' && cd \\'; - for ($i = 1; $i < count($DirPieces); $i++) { - if (strpos($DirPieces[$i], ' ') === false) { - $CLIdir .= ' && cd '.$DirPieces[$i]; - } else { - ob_start(); - system($CLIdir.' && dir /ad /x'); - $subdirsraw = explode("\n", ob_get_contents()); - ob_end_clean(); - foreach ($subdirsraw as $dummy => $line) { - if (preg_match('#^[0-9]{4}/[0-9]{2}/[0-9]{2} [0-9]{2}:[0-9]{2} [AP]M ([^ ]{8}) '.preg_quote($DirPieces[$i]).'$#i', trim($line), $matches)) { - $CLIdir .= ' && cd '.$matches[1]; - break; - } - } - $DirPieces8[$i] = $matches[1]; - } - } - $helperappsdir = implode(DIRECTORY_SEPARATOR, $DirPieces8); - - } - define('GETID3_HELPERAPPSDIR', realpath($helperappsdir).DIRECTORY_SEPARATOR); - - } - - } - - - // public: setOption - function setOption($optArray) { - if (!is_array($optArray) || empty($optArray)) { - return false; - } - foreach ($optArray as $opt => $val) { - if (isset($this, $opt) === false) { - continue; - } - $this->$opt = $val; - } - return true; - } - - - // public: analyze file - replaces GetAllFileInfo() and GetTagOnly() - function analyze($filename) { - - if (!empty($this->startup_error)) { - return $this->error($this->startup_error); - } - if (!empty($this->startup_warning)) { - $this->warning($this->startup_warning); - } - - // init result array and set parameters - $this->info = array(); - $this->info['GETID3_VERSION'] = GETID3_VERSION; - - // Check encoding/iconv support - if (!function_exists('iconv') && !in_array($this->encoding, array('ISO-8859-1', 'UTF-8', 'UTF-16LE', 'UTF-16BE', 'UTF-16'))) { - $errormessage = 'iconv() support is needed for encodings other than ISO-8859-1, UTF-8, UTF-16LE, UTF16-BE, UTF-16. '; - if (GETID3_OS_ISWINDOWS) { - $errormessage .= 'PHP does not have iconv() support. Please enable php_iconv.dll in php.ini, and copy iconv.dll from c:/php/dlls to c:/windows/system32'; - } else { - $errormessage .= 'PHP is not compiled with iconv() support. Please recompile with the --with-iconv switch'; - } - return $this->error($errormessage); - } - - // Disable magic_quotes_runtime, if neccesary - $old_magic_quotes_runtime = get_magic_quotes_runtime(); // store current setting of magic_quotes_runtime - if ($old_magic_quotes_runtime) { - set_magic_quotes_runtime(0); // turn off magic_quotes_runtime - if (get_magic_quotes_runtime()) { - return $this->error('Could not disable magic_quotes_runtime - getID3() cannot work properly with this setting enabled'); - } - } - - // remote files not supported - if (preg_match('/^(ht|f)tp:\/\//', $filename)) { - return $this->error('Remote files are not supported in this version of getID3() - please copy the file locally first'); - } - - // open local file - if (!$fp = @fopen($filename, 'rb')) { - return $this->error('Could not open file "'.$filename.'"'); - } - - // set parameters - $this->info['filesize'] = filesize($filename); - - // option_max_2gb_check - if ($this->option_max_2gb_check) { - // PHP doesn't support integers larger than 31-bit (~2GB) - // filesize() simply returns (filesize % (pow(2, 32)), no matter the actual filesize - // ftell() returns 0 if seeking to the end is beyond the range of unsigned integer - fseek($fp, 0, SEEK_END); - if ((($this->info['filesize'] != 0) && (ftell($fp) == 0)) || - ($this->info['filesize'] < 0) || - (ftell($fp) < 0)) { - unset($this->info['filesize']); - fclose($fp); - return $this->error('File is most likely larger than 2GB and is not supported by PHP'); - } - } - - // set more parameters - $this->info['avdataoffset'] = 0; - $this->info['avdataend'] = $this->info['filesize']; - $this->info['fileformat'] = ''; // filled in later - $this->info['audio']['dataformat'] = ''; // filled in later, unset if not used - $this->info['video']['dataformat'] = ''; // filled in later, unset if not used - $this->info['tags'] = array(); // filled in later, unset if not used - $this->info['error'] = array(); // filled in later, unset if not used - $this->info['warning'] = array(); // filled in later, unset if not used - $this->info['comments'] = array(); // filled in later, unset if not used - $this->info['encoding'] = $this->encoding; // required by id3v2 and iso modules - can be unset at the end if desired - - // set redundant parameters - might be needed in some include file - $this->info['filename'] = basename($filename); - $this->info['filepath'] = str_replace('\\', '/', realpath(dirname($filename))); - $this->info['filenamepath'] = $this->info['filepath'].'/'.$this->info['filename']; - - - // handle ID3v2 tag - done first - already at beginning of file - // ID3v2 detection (even if not parsing) is always done otherwise fileformat is much harder to detect - if ($this->option_tag_id3v2) { - - $GETID3_ERRORARRAY = &$this->info['warning']; - if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, false)) { - $tag = new getid3_id3v2($fp, $this->info); - } - - } else { - - fseek($fp, 0, SEEK_SET); - $header = fread($fp, 10); - if (substr($header, 0, 3) == 'ID3') { - $this->info['id3v2']['header'] = true; - $this->info['id3v2']['majorversion'] = ord($header{3}); - $this->info['id3v2']['minorversion'] = ord($header{4}); - $this->info['id3v2']['headerlength'] = getid3_lib::BigEndian2Int(substr($header, 6, 4), 1) + 10; // length of ID3v2 tag in 10-byte header doesn't include 10-byte header length - - $this->info['id3v2']['tag_offset_start'] = 0; - $this->info['id3v2']['tag_offset_end'] = $this->info['id3v2']['tag_offset_start'] + $this->info['id3v2']['headerlength']; - $this->info['avdataoffset'] = $this->info['id3v2']['tag_offset_end']; - } - - } - - - // handle ID3v1 tag - if ($this->option_tag_id3v1) { - if (!@include_once(GETID3_INCLUDEPATH.'module.tag.id3v1.php')) { - return $this->error('module.tag.id3v1.php is missing - you may disable option_tag_id3v1.'); - } - $tag = new getid3_id3v1($fp, $this->info); - } - - // handle APE tag - if ($this->option_tag_apetag) { - if (!@include_once(GETID3_INCLUDEPATH.'module.tag.apetag.php')) { - return $this->error('module.tag.apetag.php is missing - you may disable option_tag_apetag.'); - } - $tag = new getid3_apetag($fp, $this->info); - } - - // handle lyrics3 tag - if ($this->option_tag_lyrics3) { - if (!@include_once(GETID3_INCLUDEPATH.'module.tag.lyrics3.php')) { - return $this->error('module.tag.lyrics3.php is missing - you may disable option_tag_lyrics3.'); - } - $tag = new getid3_lyrics3($fp, $this->info); - } - - // read 32 kb file data - fseek($fp, $this->info['avdataoffset'], SEEK_SET); - $formattest = fread($fp, 32774); - - // determine format - $determined_format = $this->GetFileFormat($formattest, $filename); - - // unable to determine file format - if (!$determined_format) { - fclose($fp); - return $this->error('unable to determine file format'); - } - - // check for illegal ID3 tags - if (isset($determined_format['fail_id3']) && (in_array('id3v1', $this->info['tags']) || in_array('id3v2', $this->info['tags']))) { - if ($determined_format['fail_id3'] === 'ERROR') { - fclose($fp); - return $this->error('ID3 tags not allowed on this file type.'); - } elseif ($determined_format['fail_id3'] === 'WARNING') { - $this->info['warning'][] = 'ID3 tags not allowed on this file type.'; - } - } - - // check for illegal APE tags - if (isset($determined_format['fail_ape']) && in_array('ape', $this->info['tags'])) { - if ($determined_format['fail_ape'] === 'ERROR') { - fclose($fp); - return $this->error('APE tags not allowed on this file type.'); - } elseif ($determined_format['fail_ape'] === 'WARNING') { - $this->info['warning'][] = 'APE tags not allowed on this file type.'; - } - } - - // set mime type - $this->info['mime_type'] = $determined_format['mime_type']; - - // supported format signature pattern detected, but module deleted - if (!file_exists(GETID3_INCLUDEPATH.$determined_format['include'])) { - fclose($fp); - return $this->error('Format not supported, module, '.$determined_format['include'].', was removed.'); - } - - // module requires iconv support - if (!function_exists('iconv') && @$determined_format['iconv_req']) { - return $this->error('iconv support is required for this module ('.$determined_format['include'].').'); - } - - // include module - include_once(GETID3_INCLUDEPATH.$determined_format['include']); - - // instantiate module class - $class_name = 'getid3_'.$determined_format['module']; - if (!class_exists($class_name)) { - return $this->error('Format not supported, module, '.$determined_format['include'].', is corrupt.'); - } - if (isset($determined_format['option'])) { - $class = new $class_name($fp, $this->info, $determined_format['option']); - } else { - $class = new $class_name($fp, $this->info); - } - - // close file - fclose($fp); - - // process all tags - copy to 'tags' and convert charsets - if ($this->option_tags_process) { - $this->HandleAllTags(); - } - - // perform more calculations - if ($this->option_extra_info) { - $this->ChannelsBitratePlaytimeCalculations(); - $this->CalculateCompressionRatioVideo(); - $this->CalculateCompressionRatioAudio(); - $this->CalculateReplayGain(); - $this->ProcessAudioStreams(); - } - - // get the MD5 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags - if ($this->option_md5_data) { - // do not cald md5_data if md5_data_source is present - set by flac only - future MPC/SV8 too - if (!$this->option_md5_data_source || empty($this->info['md5_data_source'])) { - $this->getHashdata('md5'); - } - } - - // get the SHA1 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags - if ($this->option_sha1_data) { - $this->getHashdata('sha1'); - } - - // remove undesired keys - $this->CleanUp(); - - // restore magic_quotes_runtime setting - set_magic_quotes_runtime($old_magic_quotes_runtime); - - // return info array - return $this->info; - } - - - // private: error handling - function error($message) { - - $this->CleanUp(); - - $this->info['error'][] = $message; - return $this->info; - } - - - // private: warning handling - function warning($message) { - $this->info['warning'][] = $message; - return true; - } - - - // private: CleanUp - function CleanUp() { - - // remove possible empty keys - $AVpossibleEmptyKeys = array('dataformat', 'bits_per_sample', 'encoder_options', 'streams'); - foreach ($AVpossibleEmptyKeys as $dummy => $key) { - if (empty($this->info['audio'][$key]) && isset($this->info['audio'][$key])) { - unset($this->info['audio'][$key]); - } - if (empty($this->info['video'][$key]) && isset($this->info['video'][$key])) { - unset($this->info['video'][$key]); - } - } - - // remove empty root keys - if (!empty($this->info)) { - foreach ($this->info as $key => $value) { - if (empty($this->info[$key]) && ($this->info[$key] !== 0) && ($this->info[$key] !== '0')) { - unset($this->info[$key]); - } - } - } - - // remove meaningless entries from unknown-format files - if (empty($this->info['fileformat'])) { - if (isset($this->info['avdataoffset'])) { - unset($this->info['avdataoffset']); - } - if (isset($this->info['avdataend'])) { - unset($this->info['avdataend']); - } - } - } - - - // return array containing information about all supported formats - function GetFileFormatArray() { - static $format_info = array(); - if (empty($format_info)) { - $format_info = array( - - // Audio formats - - // AC-3 - audio - Dolby AC-3 / Dolby Digital - 'ac3' => array( - 'pattern' => '^\x0B\x77', - 'group' => 'audio', - 'module' => 'ac3', - 'mime_type' => 'audio/ac3', - ), - - // AAC - audio - Advanced Audio Coding (AAC) - ADIF format - 'adif' => array( - 'pattern' => '^ADIF', - 'group' => 'audio', - 'module' => 'aac', - 'option' => 'adif', - 'mime_type' => 'application/octet-stream', - 'fail_ape' => 'WARNING', - ), - - - // AAC - audio - Advanced Audio Coding (AAC) - ADTS format (very similar to MP3) - 'adts' => array( - 'pattern' => '^\xFF[\xF0-\xF1\xF8-\xF9]', - 'group' => 'audio', - 'module' => 'aac', - 'option' => 'adts', - 'mime_type' => 'application/octet-stream', - 'fail_ape' => 'WARNING', - ), - - - // AU - audio - NeXT/Sun AUdio (AU) - 'au' => array( - 'pattern' => '^\.snd', - 'group' => 'audio', - 'module' => 'au', - 'mime_type' => 'audio/basic', - ), - - // AVR - audio - Audio Visual Research - 'avr' => array( - 'pattern' => '^2BIT', - 'group' => 'audio', - 'module' => 'avr', - 'mime_type' => 'application/octet-stream', - ), - - // BONK - audio - Bonk v0.9+ - 'bonk' => array( - 'pattern' => '^\x00(BONK|INFO|META| ID3)', - 'group' => 'audio', - 'module' => 'bonk', - 'mime_type' => 'audio/xmms-bonk', - ), - - // FLAC - audio - Free Lossless Audio Codec - 'flac' => array( - 'pattern' => '^fLaC', - 'group' => 'audio', - 'module' => 'flac', - 'mime_type' => 'audio/x-flac', - ), - - // LA - audio - Lossless Audio (LA) - 'la' => array( - 'pattern' => '^LA0[2-4]', - 'group' => 'audio', - 'module' => 'la', - 'mime_type' => 'application/octet-stream', - ), - - // LPAC - audio - Lossless Predictive Audio Compression (LPAC) - 'lpac' => array( - 'pattern' => '^LPAC', - 'group' => 'audio', - 'module' => 'lpac', - 'mime_type' => 'application/octet-stream', - ), - - // MIDI - audio - MIDI (Musical Instrument Digital Interface) - 'midi' => array( - 'pattern' => '^MThd', - 'group' => 'audio', - 'module' => 'midi', - 'mime_type' => 'audio/midi', - ), - - // MAC - audio - Monkey's Audio Compressor - 'mac' => array( - 'pattern' => '^MAC ', - 'group' => 'audio', - 'module' => 'monkey', - 'mime_type' => 'application/octet-stream', - ), - - // MOD - audio - MODule (assorted sub-formats) - 'mod' => array( - 'pattern' => '^.{1080}(M.K.|[5-9]CHN|[1-3][0-9]CH)', - 'group' => 'audio', - 'module' => 'mod', - 'option' => 'mod', - 'mime_type' => 'audio/mod', - ), - - // MOD - audio - MODule (Impulse Tracker) - 'it' => array( - 'pattern' => '^IMPM', - 'group' => 'audio', - 'module' => 'mod', - 'option' => 'it', - 'mime_type' => 'audio/it', - ), - - // MOD - audio - MODule (eXtended Module, various sub-formats) - 'xm' => array( - 'pattern' => '^Extended Module', - 'group' => 'audio', - 'module' => 'mod', - 'option' => 'xm', - 'mime_type' => 'audio/xm', - ), - - // MOD - audio - MODule (ScreamTracker) - 's3m' => array( - 'pattern' => '^.{44}SCRM', - 'group' => 'audio', - 'module' => 'mod', - 'option' => 's3m', - 'mime_type' => 'audio/s3m', - ), - - // MPC - audio - Musepack / MPEGplus - 'mpc' => array( - 'pattern' => '^(MP\+|[\x00\x01\x10\x11\x40\x41\x50\x51\x80\x81\x90\x91\xC0\xC1\xD0\xD1][\x20-37][\x00\x20\x40\x60\x80\xA0\xC0\xE0])', - 'group' => 'audio', - 'module' => 'mpc', - 'mime_type' => 'application/octet-stream', - ), - - // MP3 - audio - MPEG-audio Layer 3 (very similar to AAC-ADTS) - 'mp3' => array( - 'pattern' => '^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]', - 'group' => 'audio', - 'module' => 'mp3', - 'mime_type' => 'audio/mpeg', - ), - - // OFR - audio - OptimFROG - 'ofr' => array( - 'pattern' => '^(\*RIFF|OFR)', - 'group' => 'audio', - 'module' => 'optimfrog', - 'mime_type' => 'application/octet-stream', - ), - - // RKAU - audio - RKive AUdio compressor - 'rkau' => array( - 'pattern' => '^RKA', - 'group' => 'audio', - 'module' => 'rkau', - 'mime_type' => 'application/octet-stream', - ), - - // SHN - audio - Shorten - 'shn' => array( - 'pattern' => '^ajkg', - 'group' => 'audio', - 'module' => 'shorten', - 'mime_type' => 'audio/xmms-shn', - 'fail_id3' => 'ERROR', - 'fail_ape' => 'ERROR', - ), - - // TTA - audio - TTA Lossless Audio Compressor (http://tta.corecodec.org) - 'tta' => array( - 'pattern' => '^TTA', // could also be '^TTA(\x01|\x02|\x03|2|1)' - 'group' => 'audio', - 'module' => 'tta', - 'mime_type' => 'application/octet-stream', - ), - - // VOC - audio - Creative Voice (VOC) - 'voc' => array( - 'pattern' => '^Creative Voice File', - 'group' => 'audio', - 'module' => 'voc', - 'mime_type' => 'audio/voc', - ), - - // VQF - audio - transform-domain weighted interleave Vector Quantization Format (VQF) - 'vqf' => array( - 'pattern' => '^TWIN', - 'group' => 'audio', - 'module' => 'vqf', - 'mime_type' => 'application/octet-stream', - ), - - // WV - audio - WavPack (v4.0+) - 'wv' => array( - 'pattern' => '^wvpk', - 'group' => 'audio', - 'module' => 'wavpack', - 'mime_type' => 'application/octet-stream', - ), - - - // Audio-Video formats - - // ASF - audio/video - Advanced Streaming Format, Windows Media Video, Windows Media Audio - 'asf' => array( - 'pattern' => '^\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C', - 'group' => 'audio-video', - 'module' => 'asf', - 'mime_type' => 'video/x-ms-asf', - 'iconv_req' => false, - ), - - // BINK - audio/video - Bink / Smacker - 'bink' => array( - 'pattern' => '^(BIK|SMK)', - 'group' => 'audio-video', - 'module' => 'bink', - 'mime_type' => 'application/octet-stream', - ), - - // FLV - audio/video - FLash Video - 'flv' => array( - 'pattern' => '^FLV\x01', - 'group' => 'audio-video', - 'module' => 'flv', - 'mime_type' => 'video/x-flv', - ), - - // MKAV - audio/video - Mastroka - 'matroska' => array( - 'pattern' => '^\x1A\x45\xDF\xA3', - 'group' => 'audio-video', - 'module' => 'matroska', - 'mime_type' => 'application/octet-stream', - ), - - // MPEG - audio/video - MPEG (Moving Pictures Experts Group) - 'mpeg' => array( - 'pattern' => '^\x00\x00\x01(\xBA|\xB3)', - 'group' => 'audio-video', - 'module' => 'mpeg', - 'mime_type' => 'video/mpeg', - ), - - // NSV - audio/video - Nullsoft Streaming Video (NSV) - 'nsv' => array( - 'pattern' => '^NSV[sf]', - 'group' => 'audio-video', - 'module' => 'nsv', - 'mime_type' => 'application/octet-stream', - ), - - // Ogg - audio/video - Ogg (Ogg-Vorbis, Ogg-FLAC, Speex, Ogg-Theora(*), Ogg-Tarkin(*)) - 'ogg' => array( - 'pattern' => '^OggS', - 'group' => 'audio', - 'module' => 'ogg', - 'mime_type' => 'application/ogg', - 'fail_id3' => 'WARNING', - 'fail_ape' => 'WARNING', - ), - - // QT - audio/video - Quicktime - 'quicktime' => array( - 'pattern' => '^.{4}(cmov|free|ftyp|mdat|moov|pnot|skip|wide)', - 'group' => 'audio-video', - 'module' => 'quicktime', - 'mime_type' => 'video/quicktime', - ), - - // RIFF - audio/video - Resource Interchange File Format (RIFF) / WAV / AVI / CD-audio / SDSS = renamed variant used by SmartSound QuickTracks (www.smartsound.com) / FORM = Audio Interchange File Format (AIFF) - 'riff' => array( - 'pattern' => '^(RIFF|SDSS|FORM)', - 'group' => 'audio-video', - 'module' => 'riff', - 'mime_type' => 'audio/x-wave', - 'fail_ape' => 'WARNING', - ), - - // Real - audio/video - RealAudio, RealVideo - 'real' => array( - 'pattern' => '^(\.RMF|.ra)', - 'group' => 'audio-video', - 'module' => 'real', - 'mime_type' => 'audio/x-realaudio', - ), - - // SWF - audio/video - ShockWave Flash - 'swf' => array( - 'pattern' => '^(F|C)WS', - 'group' => 'audio-video', - 'module' => 'swf', - 'mime_type' => 'application/x-shockwave-flash', - ), - - - // Still-Image formats - - // BMP - still image - Bitmap (Windows, OS/2; uncompressed, RLE8, RLE4) - 'bmp' => array( - 'pattern' => '^BM', - 'group' => 'graphic', - 'module' => 'bmp', - 'mime_type' => 'image/bmp', - 'fail_id3' => 'ERROR', - 'fail_ape' => 'ERROR', - ), - - // GIF - still image - Graphics Interchange Format - 'gif' => array( - 'pattern' => '^GIF', - 'group' => 'graphic', - 'module' => 'gif', - 'mime_type' => 'image/gif', - 'fail_id3' => 'ERROR', - 'fail_ape' => 'ERROR', - ), - - // JPEG - still image - Joint Photographic Experts Group (JPEG) - 'jpg' => array( - 'pattern' => '^\xFF\xD8\xFF', - 'group' => 'graphic', - 'module' => 'jpg', - 'mime_type' => 'image/jpeg', - 'fail_id3' => 'ERROR', - 'fail_ape' => 'ERROR', - ), - - // PCD - still image - Kodak Photo CD - 'pcd' => array( - 'pattern' => '^.{2048}PCD_IPI\x00', - 'group' => 'graphic', - 'module' => 'pcd', - 'mime_type' => 'image/x-photo-cd', - 'fail_id3' => 'ERROR', - 'fail_ape' => 'ERROR', - ), - - - // PNG - still image - Portable Network Graphics (PNG) - 'png' => array( - 'pattern' => '^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A', - 'group' => 'graphic', - 'module' => 'png', - 'mime_type' => 'image/png', - 'fail_id3' => 'ERROR', - 'fail_ape' => 'ERROR', - ), - - - // TIFF - still image - Tagged Information File Format (TIFF) - 'tiff' => array( - 'pattern' => '^(II\x2A\x00|MM\x00\x2A)', - 'group' => 'graphic', - 'module' => 'tiff', - 'mime_type' => 'image/tiff', - 'fail_id3' => 'ERROR', - 'fail_ape' => 'ERROR', - ), - - - // Data formats - - // ISO - data - International Standards Organization (ISO) CD-ROM Image - 'iso' => array( - 'pattern' => '^.{32769}CD001', - 'group' => 'misc', - 'module' => 'iso', - 'mime_type' => 'application/octet-stream', - 'fail_id3' => 'ERROR', - 'fail_ape' => 'ERROR', - 'iconv_req' => false, - ), - - // RAR - data - RAR compressed data - 'rar' => array( - 'pattern' => '^Rar\!', - 'group' => 'archive', - 'module' => 'rar', - 'mime_type' => 'application/octet-stream', - 'fail_id3' => 'ERROR', - 'fail_ape' => 'ERROR', - ), - - // SZIP - audio/data - SZIP compressed data - 'szip' => array( - 'pattern' => '^SZ\x0A\x04', - 'group' => 'archive', - 'module' => 'szip', - 'mime_type' => 'application/octet-stream', - 'fail_id3' => 'ERROR', - 'fail_ape' => 'ERROR', - ), - - // TAR - data - TAR compressed data - 'tar' => array( - 'pattern' => '^.{100}[0-9\x20]{7}\x00[0-9\x20]{7}\x00[0-9\x20]{7}\x00[0-9\x20\x00]{12}[0-9\x20\x00]{12}', - 'group' => 'archive', - 'module' => 'tar', - 'mime_type' => 'application/x-tar', - 'fail_id3' => 'ERROR', - 'fail_ape' => 'ERROR', - ), - - // GZIP - data - GZIP compressed data - 'gz' => array( - 'pattern' => '^\x1F\x8B\x08', - 'group' => 'archive', - 'module' => 'gzip', - 'mime_type' => 'application/x-gzip', - 'fail_id3' => 'ERROR', - 'fail_ape' => 'ERROR', - ), - - // ZIP - data - ZIP compressed data - 'zip' => array( - 'pattern' => '^PK\x03\x04', - 'group' => 'archive', - 'module' => 'zip', - 'mime_type' => 'application/zip', - 'fail_id3' => 'ERROR', - 'fail_ape' => 'ERROR', - ), - - - // Misc other formats - - // PDF - data - ZIP compressed data - 'pdf' => array( - 'pattern' => '^\x25PDF', - 'group' => 'misc', - 'module' => 'pdf', - 'mime_type' => 'application/pdf', - 'fail_id3' => 'ERROR', - 'fail_ape' => 'ERROR', - ), - - // MSOFFICE - data - ZIP compressed data - 'msoffice' => array( - 'pattern' => '^\xD0\xCF\x11\xE0', // D0CF11E == DOCFILE == Microsoft Office Document - 'group' => 'misc', - 'module' => 'msoffice', - 'mime_type' => 'application/octet-stream', - 'fail_id3' => 'ERROR', - 'fail_ape' => 'ERROR', - ), - ); - } - - return $format_info; - } - - - - function GetFileFormat(&$filedata, $filename='') { - // this function will determine the format of a file based on usually - // the first 2-4 bytes of the file (8 bytes for PNG, 16 bytes for JPG, - // and in the case of ISO CD image, 6 bytes offset 32kb from the start - // of the file). - - // Identify file format - loop through $format_info and detect with reg expr - foreach ($this->GetFileFormatArray() as $format_name => $info) { - // Using preg_match() instead of ereg() - much faster - // The /s switch on preg_match() forces preg_match() NOT to treat - // newline (0x0A) characters as special chars but do a binary match - if (preg_match('/'.$info['pattern'].'/s', $filedata)) { - $info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php'; - return $info; - } - } - - - if (preg_match('/\.mp[123a]$/i', $filename)) { - // Too many mp3 encoders on the market put gabage in front of mpeg files - // use assume format on these if format detection failed - $GetFileFormatArray = $this->GetFileFormatArray(); - $info = $GetFileFormatArray['mp3']; - $info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php'; - return $info; - } - - return false; - } - - - // converts array to $encoding charset from $this->encoding - function CharConvert(&$array, $encoding) { - - // identical encoding - end here - if ($encoding == $this->encoding) { - return; - } - - // loop thru array - foreach ($array as $key => $value) { - - // go recursive - if (is_array($value)) { - $this->CharConvert($array[$key], $encoding); - } - - // convert string - elseif (is_string($value)) { - $array[$key] = trim(getid3_lib::iconv_fallback($encoding, $this->encoding, $value)); - } - } - } - - - function HandleAllTags() { - - // key name => array (tag name, character encoding) - static $tags; - if (empty($tags)) { - $tags = array( - 'asf' => array('asf' , 'UTF-16LE'), - 'midi' => array('midi' , 'ISO-8859-1'), - 'nsv' => array('nsv' , 'ISO-8859-1'), - 'ogg' => array('vorbiscomment' , 'UTF-8'), - 'png' => array('png' , 'UTF-8'), - 'tiff' => array('tiff' , 'ISO-8859-1'), - 'quicktime' => array('quicktime' , 'ISO-8859-1'), - 'real' => array('real' , 'ISO-8859-1'), - 'vqf' => array('vqf' , 'ISO-8859-1'), - 'zip' => array('zip' , 'ISO-8859-1'), - 'riff' => array('riff' , 'ISO-8859-1'), - 'lyrics3' => array('lyrics3' , 'ISO-8859-1'), - 'id3v1' => array('id3v1' , $this->encoding_id3v1), - 'id3v2' => array('id3v2' , 'UTF-8'), // not according to the specs (every frame can have a different encoding), but getID3() force-converts all encodings to UTF-8 - 'ape' => array('ape' , 'UTF-8') - ); - } - - // loop thru comments array - foreach ($tags as $comment_name => $tagname_encoding_array) { - list($tag_name, $encoding) = $tagname_encoding_array; - - // fill in default encoding type if not already present - if (isset($this->info[$comment_name]) && !isset($this->info[$comment_name]['encoding'])) { - $this->info[$comment_name]['encoding'] = $encoding; - } - - // copy comments if key name set - if (!empty($this->info[$comment_name]['comments'])) { - - foreach ($this->info[$comment_name]['comments'] as $tag_key => $valuearray) { - foreach ($valuearray as $key => $value) { - if (strlen(trim($value)) > 0) { - $this->info['tags'][trim($tag_name)][trim($tag_key)][] = $value; // do not trim!! Unicode characters will get mangled if trailing nulls are removed! - } - } - } - - if (!isset($this->info['tags'][$tag_name])) { - // comments are set but contain nothing but empty strings, so skip - continue; - } - - if ($this->option_tags_html) { - foreach ($this->info['tags'][$tag_name] as $tag_key => $valuearray) { - foreach ($valuearray as $key => $value) { - if (is_string($value)) { - //$this->info['tags_html'][$tag_name][$tag_key][$key] = getid3_lib::MultiByteCharString2HTML($value, $encoding); - $this->info['tags_html'][$tag_name][$tag_key][$key] = str_replace('�', '', getid3_lib::MultiByteCharString2HTML($value, $encoding)); - } else { - $this->info['tags_html'][$tag_name][$tag_key][$key] = $value; - } - } - } - } - - $this->CharConvert($this->info['tags'][$tag_name], $encoding); // only copy gets converted! - } - - } - return true; - } - - - function getHashdata($algorithm) { - switch ($algorithm) { - case 'md5': - case 'sha1': - break; - - default: - return $this->error('bad algorithm "'.$algorithm.'" in getHashdata()'); - break; - } - - if ((@$this->info['fileformat'] == 'ogg') && (@$this->info['audio']['dataformat'] == 'vorbis')) { - - // We cannot get an identical md5_data value for Ogg files where the comments - // span more than 1 Ogg page (compared to the same audio data with smaller - // comments) using the normal getID3() method of MD5'ing the data between the - // end of the comments and the end of the file (minus any trailing tags), - // because the page sequence numbers of the pages that the audio data is on - // do not match. Under normal circumstances, where comments are smaller than - // the nominal 4-8kB page size, then this is not a problem, but if there are - // very large comments, the only way around it is to strip off the comment - // tags with vorbiscomment and MD5 that file. - // This procedure must be applied to ALL Ogg files, not just the ones with - // comments larger than 1 page, because the below method simply MD5's the - // whole file with the comments stripped, not just the portion after the - // comments block (which is the standard getID3() method. - - // The above-mentioned problem of comments spanning multiple pages and changing - // page sequence numbers likely happens for OggSpeex and OggFLAC as well, but - // currently vorbiscomment only works on OggVorbis files. - - if ((bool) ini_get('safe_mode')) { - - $this->info['warning'][] = 'Failed making system call to vorbiscomment.exe - '.$algorithm.'_data is incorrect - error returned: PHP running in Safe Mode (backtick operator not available)'; - $this->info[$algorithm.'_data'] = false; - - } else { - - // Prevent user from aborting script - $old_abort = ignore_user_abort(true); - - // Create empty file - $empty = tempnam('*', 'getID3'); - touch($empty); - - - // Use vorbiscomment to make temp file without comments - $temp = tempnam('*', 'getID3'); - $file = $this->info['filenamepath']; - - if (GETID3_OS_ISWINDOWS) { - - if (file_exists(GETID3_HELPERAPPSDIR.'vorbiscomment.exe')) { - - $commandline = '"'.GETID3_HELPERAPPSDIR.'vorbiscomment.exe" -w -c "'.$empty.'" "'.$file.'" "'.$temp.'"'; - $VorbisCommentError = `$commandline`; - - } else { - - $VorbisCommentError = 'vorbiscomment.exe not found in '.GETID3_HELPERAPPSDIR; - - } - - } else { - - $commandline = 'vorbiscomment -w -c "'.$empty.'" "'.$file.'" "'.$temp.'" 2>&1'; - $commandline = 'vorbiscomment -w -c '.escapeshellarg($empty).' '.escapeshellarg($file).' '.escapeshellarg($temp).' 2>&1'; - $VorbisCommentError = `$commandline`; - - } - - if (!empty($VorbisCommentError)) { - - $this->info['warning'][] = 'Failed making system call to vorbiscomment(.exe) - '.$algorithm.'_data will be incorrect. If vorbiscomment is unavailable, please download from http://www.vorbis.com/download.psp and put in the getID3() directory. Error returned: '.$VorbisCommentError; - $this->info[$algorithm.'_data'] = false; - - } else { - - // Get hash of newly created file - switch ($algorithm) { - case 'md5': - $this->info[$algorithm.'_data'] = getid3_lib::md5_file($temp); - break; - - case 'sha1': - $this->info[$algorithm.'_data'] = getid3_lib::sha1_file($temp); - break; - } - } - - // Clean up - unlink($empty); - unlink($temp); - - // Reset abort setting - ignore_user_abort($old_abort); - - } - - } else { - - if (!empty($this->info['avdataoffset']) || (isset($this->info['avdataend']) && ($this->info['avdataend'] < $this->info['filesize']))) { - - // get hash from part of file - $this->info[$algorithm.'_data'] = getid3_lib::hash_data($this->info['filenamepath'], $this->info['avdataoffset'], $this->info['avdataend'], $algorithm); - - } else { - - // get hash from whole file - switch ($algorithm) { - case 'md5': - $this->info[$algorithm.'_data'] = getid3_lib::md5_file($this->info['filenamepath']); - break; - - case 'sha1': - $this->info[$algorithm.'_data'] = getid3_lib::sha1_file($this->info['filenamepath']); - break; - } - } - - } - return true; - } - - - function ChannelsBitratePlaytimeCalculations() { - - // set channelmode on audio - if (@$this->info['audio']['channels'] == '1') { - $this->info['audio']['channelmode'] = 'mono'; - } elseif (@$this->info['audio']['channels'] == '2') { - $this->info['audio']['channelmode'] = 'stereo'; - } - - // Calculate combined bitrate - audio + video - $CombinedBitrate = 0; - $CombinedBitrate += (isset($this->info['audio']['bitrate']) ? $this->info['audio']['bitrate'] : 0); - $CombinedBitrate += (isset($this->info['video']['bitrate']) ? $this->info['video']['bitrate'] : 0); - if (($CombinedBitrate > 0) && empty($this->info['bitrate'])) { - $this->info['bitrate'] = $CombinedBitrate; - } - //if ((isset($this->info['video']) && !isset($this->info['video']['bitrate'])) || (isset($this->info['audio']) && !isset($this->info['audio']['bitrate']))) { - // // for example, VBR MPEG video files cannot determine video bitrate: - // // should not set overall bitrate and playtime from audio bitrate only - // unset($this->info['bitrate']); - //} - - if (!isset($this->info['playtime_seconds']) && !empty($this->info['bitrate'])) { - $this->info['playtime_seconds'] = (($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['bitrate']; - } - - // Set playtime string - if (!empty($this->info['playtime_seconds']) && empty($this->info['playtime_string'])) { - $this->info['playtime_string'] = getid3_lib::PlaytimeString($this->info['playtime_seconds']); - } - } - - - function CalculateCompressionRatioVideo() { - if (empty($this->info['video'])) { - return false; - } - if (empty($this->info['video']['resolution_x']) || empty($this->info['video']['resolution_y'])) { - return false; - } - if (empty($this->info['video']['bits_per_sample'])) { - return false; - } - - switch ($this->info['video']['dataformat']) { - case 'bmp': - case 'gif': - case 'jpeg': - case 'jpg': - case 'png': - case 'tiff': - $FrameRate = 1; - $PlaytimeSeconds = 1; - $BitrateCompressed = $this->info['filesize'] * 8; - break; - - default: - if (!empty($this->info['video']['frame_rate'])) { - $FrameRate = $this->info['video']['frame_rate']; - } else { - return false; - } - if (!empty($this->info['playtime_seconds'])) { - $PlaytimeSeconds = $this->info['playtime_seconds']; - } else { - return false; - } - if (!empty($this->info['video']['bitrate'])) { - $BitrateCompressed = $this->info['video']['bitrate']; - } else { - return false; - } - break; - } - $BitrateUncompressed = $this->info['video']['resolution_x'] * $this->info['video']['resolution_y'] * $this->info['video']['bits_per_sample'] * $FrameRate; - - $this->info['video']['compression_ratio'] = $BitrateCompressed / $BitrateUncompressed; - return true; - } - - - function CalculateCompressionRatioAudio() { - if (empty($this->info['audio']['bitrate']) || empty($this->info['audio']['channels']) || empty($this->info['audio']['sample_rate'])) { - return false; - } - $this->info['audio']['compression_ratio'] = $this->info['audio']['bitrate'] / ($this->info['audio']['channels'] * $this->info['audio']['sample_rate'] * (!empty($this->info['audio']['bits_per_sample']) ? $this->info['audio']['bits_per_sample'] : 16)); - - if (!empty($this->info['audio']['streams'])) { - foreach ($this->info['audio']['streams'] as $streamnumber => $streamdata) { - if (!empty($streamdata['bitrate']) && !empty($streamdata['channels']) && !empty($streamdata['sample_rate'])) { - $this->info['audio']['streams'][$streamnumber]['compression_ratio'] = $streamdata['bitrate'] / ($streamdata['channels'] * $streamdata['sample_rate'] * (!empty($streamdata['bits_per_sample']) ? $streamdata['bits_per_sample'] : 16)); - } - } - } - return true; - } - - - function CalculateReplayGain() { - if (isset($this->info['replay_gain'])) { - $this->info['replay_gain']['reference_volume'] = 89; - if (isset($this->info['replay_gain']['track']['adjustment'])) { - $this->info['replay_gain']['track']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['track']['adjustment']; - } - if (isset($this->info['replay_gain']['album']['adjustment'])) { - $this->info['replay_gain']['album']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['album']['adjustment']; - } - - if (isset($this->info['replay_gain']['track']['peak'])) { - $this->info['replay_gain']['track']['max_noclip_gain'] = 0 - getid3_lib::RGADamplitude2dB($this->info['replay_gain']['track']['peak']); - } - if (isset($this->info['replay_gain']['album']['peak'])) { - $this->info['replay_gain']['album']['max_noclip_gain'] = 0 - getid3_lib::RGADamplitude2dB($this->info['replay_gain']['album']['peak']); - } - } - return true; - } - - function ProcessAudioStreams() { - if (!empty($this->info['audio']['bitrate']) || !empty($this->info['audio']['channels']) || !empty($this->info['audio']['sample_rate'])) { - if (!isset($this->info['audio']['streams'])) { - foreach ($this->info['audio'] as $key => $value) { - if ($key != 'streams') { - $this->info['audio']['streams'][0][$key] = $value; - } - } - } - } - return true; - } - - function getid3_tempnam() { - return tempnam($this->tempdir, 'gI3'); - } - -} - -?> \ No newline at end of file diff --git a/getid3/getid3/module.archive.gzip.php b/getid3/getid3/module.archive.gzip.php deleted file mode 100644 index 34a44b1..0000000 --- a/getid3/getid3/module.archive.gzip.php +++ /dev/null @@ -1,249 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.archive.gzip.php // -// written by Mike Mozolin // -// module for analyzing GZIP files // -// dependencies: NONE // -// /// -///////////////////////////////////////////////////////////////// - -class getid3_gzip { - - // public: Optional file list - disable for speed. - var $option_gzip_parse_contents = false; // decode gzipped files, if possible, and parse recursively (.tar.gz for example) - - function getid3_gzip(&$fd, &$ThisFileInfo) { - $ThisFileInfo['fileformat'] = 'gzip'; - - $start_length = 10; - $unpack_header = 'a1id1/a1id2/a1cmethod/a1flags/a4mtime/a1xflags/a1os'; - //+---+---+---+---+---+---+---+---+---+---+ - //|ID1|ID2|CM |FLG| MTIME |XFL|OS | - //+---+---+---+---+---+---+---+---+---+---+ - @fseek($fd, 0); - $buffer = @fread($fd, $ThisFileInfo['filesize']); - - $arr_members = explode("\x1F\x8B\x08", $buffer); - while (true) { - $is_wrong_members = false; - $num_members = intval(count($arr_members)); - for ($i = 0; $i < $num_members; $i++) { - if (strlen($arr_members[$i]) == 0) { - continue; - } - $buf = "\x1F\x8B\x08".$arr_members[$i]; - - $attr = unpack($unpack_header, substr($buf, 0, $start_length)); - if (!$this->get_os_type(ord($attr['os']))) { - // Merge member with previous if wrong OS type - $arr_members[$i - 1] .= $buf; - $arr_members[$i] = ''; - $is_wrong_members = true; - continue; - } - } - if (!$is_wrong_members) { - break; - } - } - - $ThisFileInfo['gzip']['files'] = array(); - - $fpointer = 0; - $idx = 0; - for ($i = 0; $i < $num_members; $i++) { - if (strlen($arr_members[$i]) == 0) { - continue; - } - $thisThisFileInfo = &$ThisFileInfo['gzip']['member_header'][++$idx]; - - $buff = "\x1F\x8B\x08".$arr_members[$i]; - - $attr = unpack($unpack_header, substr($buff, 0, $start_length)); - $thisThisFileInfo['filemtime'] = getid3_lib::LittleEndian2Int($attr['mtime']); - $thisThisFileInfo['raw']['id1'] = ord($attr['cmethod']); - $thisThisFileInfo['raw']['id2'] = ord($attr['cmethod']); - $thisThisFileInfo['raw']['cmethod'] = ord($attr['cmethod']); - $thisThisFileInfo['raw']['os'] = ord($attr['os']); - $thisThisFileInfo['raw']['xflags'] = ord($attr['xflags']); - $thisThisFileInfo['raw']['flags'] = ord($attr['flags']); - - $thisThisFileInfo['flags']['crc16'] = (bool) ($thisThisFileInfo['raw']['flags'] & 0x02); - $thisThisFileInfo['flags']['extra'] = (bool) ($thisThisFileInfo['raw']['flags'] & 0x04); - $thisThisFileInfo['flags']['filename'] = (bool) ($thisThisFileInfo['raw']['flags'] & 0x08); - $thisThisFileInfo['flags']['comment'] = (bool) ($thisThisFileInfo['raw']['flags'] & 0x10); - - $thisThisFileInfo['compression'] = $this->get_xflag_type($thisThisFileInfo['raw']['xflags']); - - $thisThisFileInfo['os'] = $this->get_os_type($thisThisFileInfo['raw']['os']); - if (!$thisThisFileInfo['os']) { - $ThisFileInfo['error'][] = 'Read error on gzip file'; - return false; - } - - $fpointer = 10; - $arr_xsubfield = array(); - // bit 2 - FLG.FEXTRA - //+---+---+=================================+ - //| XLEN |...XLEN bytes of "extra field"...| - //+---+---+=================================+ - if ($thisThisFileInfo['flags']['extra']) { - $w_xlen = substr($buff, $fpointer, 2); - $xlen = getid3_lib::LittleEndian2Int($w_xlen); - $fpointer += 2; - - $thisThisFileInfo['raw']['xfield'] = substr($buff, $fpointer, $xlen); - // Extra SubFields - //+---+---+---+---+==================================+ - //|SI1|SI2| LEN |... LEN bytes of subfield data ...| - //+---+---+---+---+==================================+ - $idx = 0; - while (true) { - if ($idx >= $xlen) { - break; - } - $si1 = ord(substr($buff, $fpointer + $idx++, 1)); - $si2 = ord(substr($buff, $fpointer + $idx++, 1)); - if (($si1 == 0x41) && ($si2 == 0x70)) { - $w_xsublen = substr($buff, $fpointer+$idx, 2); - $xsublen = getid3_lib::LittleEndian2Int($w_xsublen); - $idx += 2; - $arr_xsubfield[] = substr($buff, $fpointer+$idx, $xsublen); - $idx += $xsublen; - } else { - break; - } - } - $fpointer += $xlen; - } - // bit 3 - FLG.FNAME - //+=========================================+ - //|...original file name, zero-terminated...| - //+=========================================+ - // GZIP files may have only one file, with no filename, so assume original filename is current filename without .gz - $thisThisFileInfo['filename'] = eregi_replace('.gz$', '', $ThisFileInfo['filename']); - if ($thisThisFileInfo['flags']['filename']) { - while (true) { - if (ord($buff[$fpointer]) == 0) { - $fpointer++; - break; - } - $thisThisFileInfo['filename'] .= $buff[$fpointer]; - $fpointer++; - } - } - // bit 4 - FLG.FCOMMENT - //+===================================+ - //|...file comment, zero-terminated...| - //+===================================+ - if ($thisThisFileInfo['flags']['comment']) { - while (true) { - if (ord($buff[$fpointer]) == 0) { - $fpointer++; - break; - } - $thisThisFileInfo['comment'] .= $buff[$fpointer]; - $fpointer++; - } - } - // bit 1 - FLG.FHCRC - //+---+---+ - //| CRC16 | - //+---+---+ - if ($thisThisFileInfo['flags']['crc16']) { - $w_crc = substr($buff, $fpointer, 2); - $thisThisFileInfo['crc16'] = getid3_lib::LittleEndian2Int($w_crc); - $fpointer += 2; - } - // bit 0 - FLG.FTEXT - //if ($thisThisFileInfo['raw']['flags'] & 0x01) { - // Ignored... - //} - // bits 5, 6, 7 - reserved - - $thisThisFileInfo['crc32'] = getid3_lib::LittleEndian2Int(substr($buff, strlen($buff) - 8, 4)); - $thisThisFileInfo['filesize'] = getid3_lib::LittleEndian2Int(substr($buff, strlen($buff) - 4)); - - $ThisFileInfo['gzip']['files'] = getid3_lib::array_merge_clobber($ThisFileInfo['gzip']['files'], getid3_lib::CreateDeepArray($thisThisFileInfo['filename'], '/', $thisThisFileInfo['filesize'])); - - if ($this->option_gzip_parse_contents) { - // Try to inflate GZip - $csize = 0; - $inflated = ''; - $chkcrc32 = ''; - if (function_exists('gzinflate')) { - $cdata = substr($buff, $fpointer); - $cdata = substr($cdata, 0, strlen($cdata) - 8); - $csize = strlen($cdata); - $inflated = gzinflate($cdata); - - // Calculate CRC32 for inflated content - $thisThisFileInfo['crc32_valid'] = (bool) (sprintf('%u', crc32($inflated)) == $thisThisFileInfo['crc32']); - - // determine format - $formattest = substr($inflated, 0, 32774); - $newgetID3 = new getID3(); - $determined_format = $newgetID3->GetFileFormat($formattest); - unset($newgetID3); - - // file format is determined - switch (@$determined_format['module']) { - case 'tar': - // view TAR-file info - if (file_exists(GETID3_INCLUDEPATH.$determined_format['include']) && @include_once(GETID3_INCLUDEPATH.$determined_format['include'])) { - getid3_tar::read_tar($inflated, $ThisFileInfo['gzip']['member_header'][$idx]); - } - break; - - case '': - default: - // unknown or unhandled format - break; - } - } - } - } - return true; - } - - // Converts the OS type - function get_os_type($key) { - static $os_type = array( - '0' => 'FAT filesystem (MS-DOS, OS/2, NT/Win32)', - '1' => 'Amiga', - '2' => 'VMS (or OpenVMS)', - '3' => 'Unix', - '4' => 'VM/CMS', - '5' => 'Atari TOS', - '6' => 'HPFS filesystem (OS/2, NT)', - '7' => 'Macintosh', - '8' => 'Z-System', - '9' => 'CP/M', - '10' => 'TOPS-20', - '11' => 'NTFS filesystem (NT)', - '12' => 'QDOS', - '13' => 'Acorn RISCOS', - '255' => 'unknown' - ); - return @$os_type[$key]; - } - - // Converts the eXtra FLags - function get_xflag_type($key) { - static $xflag_type = array( - '0' => 'unknown', - '2' => 'maximum compression', - '4' => 'fastest algorithm' - ); - return @$xflag_type[$key]; - } -} - -?> diff --git a/getid3/getid3/module.archive.rar.php b/getid3/getid3/module.archive.rar.php deleted file mode 100644 index f006ce4..0000000 --- a/getid3/getid3/module.archive.rar.php +++ /dev/null @@ -1,32 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.archive.rar.php // -// module for analyzing RAR files // -// dependencies: NONE // -// /// -///////////////////////////////////////////////////////////////// - - -class getid3_rar -{ - - function getid3_rar(&$fd, &$ThisFileInfo) { - - $ThisFileInfo['fileformat'] = 'rar'; - - $ThisFileInfo['error'][] = 'RAR parsing not enabled in this version of getID3()'; - return false; - - } - -} - - -?> \ No newline at end of file diff --git a/getid3/getid3/module.archive.szip.php b/getid3/getid3/module.archive.szip.php deleted file mode 100644 index 2513c85..0000000 --- a/getid3/getid3/module.archive.szip.php +++ /dev/null @@ -1,97 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.archive.szip.php // -// module for analyzing SZIP compressed files // -// dependencies: NONE // -// /// -///////////////////////////////////////////////////////////////// - - -class getid3_szip -{ - - function getid3_szip(&$fd, &$ThisFileInfo) { - - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $SZIPHeader = fread($fd, 6); - if (substr($SZIPHeader, 0, 4) != 'SZ'."\x0A\x04") { - $ThisFileInfo['error'][] = 'Expecting "SZ[x0A][x04]" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($SZIPHeader, 0, 4).'"'; - return false; - } - - $ThisFileInfo['fileformat'] = 'szip'; - - $ThisFileInfo['szip']['major_version'] = getid3_lib::BigEndian2Int(substr($SZIPHeader, 4, 1)); - $ThisFileInfo['szip']['minor_version'] = getid3_lib::BigEndian2Int(substr($SZIPHeader, 5, 1)); - - while (!feof($fd)) { - $NextBlockID = fread($fd, 2); - switch ($NextBlockID) { - case 'SZ': - // Note that szip files can be concatenated, this has the same effect as - // concatenating the files. this also means that global header blocks - // might be present between directory/data blocks. - fseek($fd, 4, SEEK_CUR); - break; - - case 'BH': - $BHheaderbytes = getid3_lib::BigEndian2Int(fread($fd, 3)); - $BHheaderdata = fread($fd, $BHheaderbytes); - $BHheaderoffset = 0; - while (strpos($BHheaderdata, "\x00", $BHheaderoffset) > 0) { - //filename as \0 terminated string (empty string indicates end) - //owner as \0 terminated string (empty is same as last file) - //group as \0 terminated string (empty is same as last file) - //3 byte filelength in this block - //2 byte access flags - //4 byte creation time (like in unix) - //4 byte modification time (like in unix) - //4 byte access time (like in unix) - - $BHdataArray['filename'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, "\x00")); - $BHheaderoffset += (strlen($BHdataArray['filename']) + 1); - - $BHdataArray['owner'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, "\x00")); - $BHheaderoffset += (strlen($BHdataArray['owner']) + 1); - - $BHdataArray['group'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, "\x00")); - $BHheaderoffset += (strlen($BHdataArray['group']) + 1); - - $BHdataArray['filelength'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 3)); - $BHheaderoffset += 3; - - $BHdataArray['access_flags'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 2)); - $BHheaderoffset += 2; - - $BHdataArray['creation_time'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4)); - $BHheaderoffset += 4; - - $BHdataArray['modification_time'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4)); - $BHheaderoffset += 4; - - $BHdataArray['access_time'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4)); - $BHheaderoffset += 4; - - $ThisFileInfo['szip']['BH'][] = $BHdataArray; - } - break; - - default: - break 2; - } - } - - return true; - - } - -} - -?> \ No newline at end of file diff --git a/getid3/getid3/module.archive.tar.php b/getid3/getid3/module.archive.tar.php deleted file mode 100644 index 61aff40..0000000 --- a/getid3/getid3/module.archive.tar.php +++ /dev/null @@ -1,167 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.archive.tar.php // -// written by Mike Mozolin // -// module for analyzing TAR files // -// dependencies: NONE // -// /// -///////////////////////////////////////////////////////////////// - -class getid3_tar { - - function getid3_tar(&$fd, &$ThisFileInfo) { - $ThisFileInfo['fileformat'] = 'tar'; - $ThisFileInfo['tar']['files'] = array(); - - $unpack_header = 'a100fname/a8mode/a8uid/a8gid/a12size/a12mtime/a8chksum/a1typflag/a100lnkname/a6magic/a2ver/a32uname/a32gname/a8devmaj/a8devmin/a155/prefix'; - $null_512k = str_repeat("\0", 512); // end-of-file marker - - @fseek($fd, 0); - while (!feof($fd)) { - $buffer = fread($fd, 512); - - // check the block - $checksum = 0; - for ($i = 0; $i < 148; $i++) { - $checksum += ord($buffer{$i}); - } - for ($i = 148; $i < 156; $i++) { - $checksum += ord(' '); - } - for ($i = 156; $i < 512; $i++) { - $checksum += ord($buffer{$i}); - } - $attr = unpack($unpack_header, $buffer); - $name = trim(@$attr['fname']); - $mode = octdec(trim(@$attr['mode'])); - $uid = octdec(trim(@$attr['uid'])); - $gid = octdec(trim(@$attr['gid'])); - $size = octdec(trim(@$attr['size'])); - $mtime = octdec(trim(@$attr['mtime'])); - $chksum = octdec(trim(@$attr['chksum'])); - $typflag = trim(@$attr['typflag']); - $lnkname = trim(@$attr['lnkname']); - $magic = trim(@$attr['magic']); - $ver = trim(@$attr['ver']); - $uname = trim(@$attr['uname']); - $gname = trim(@$attr['gname']); - $devmaj = octdec(trim(@$attr['devmaj'])); - $devmin = octdec(trim(@$attr['devmin'])); - $prefix = trim(@$attr['prefix']); - if (($checksum == 256) && ($chksum == 0)) { - // EOF Found - break; - } - if ($prefix) { - $name = $prefix.'/'.$name; - } - if ((preg_match('#/$#', $name)) && !$name) { - $typeflag = 5; - } - if ($buffer == $null_512k) { - // it's the end of the tar-file... - break; - } - - // Read to the next chunk - fseek($fd, $size, SEEK_CUR); - - $diff = $size % 512; - if ($diff != 0) { - // Padding, throw away - fseek($fd, (512 - $diff), SEEK_CUR); - } - // Protect against tar-files with garbage at the end - if ($name == '') { - break; - } - $ThisFileInfo['tar']['file_details'][$name] = array ( - 'name' => $name, - 'mode_raw' => $mode, - 'mode' => getid3_tar::display_perms($mode), - 'uid' => $uid, - 'gid' => $gid, - 'size' => $size, - 'mtime' => $mtime, - 'chksum' => $chksum, - 'typeflag' => getid3_tar::get_flag_type($typflag), - 'linkname' => $lnkname, - 'magic' => $magic, - 'version' => $ver, - 'uname' => $uname, - 'gname' => $gname, - 'devmajor' => $devmaj, - 'devminor' => $devmin - ); - $ThisFileInfo['tar']['files'] = getid3_lib::array_merge_clobber($ThisFileInfo['tar']['files'], getid3_lib::CreateDeepArray($ThisFileInfo['tar']['file_details'][$name]['name'], '/', $size)); - } - return true; - } - - // Parses the file mode to file permissions - function display_perms($mode) { - // Determine Type - if ($mode & 0x1000) $type='p'; // FIFO pipe - elseif ($mode & 0x2000) $type='c'; // Character special - elseif ($mode & 0x4000) $type='d'; // Directory - elseif ($mode & 0x6000) $type='b'; // Block special - elseif ($mode & 0x8000) $type='-'; // Regular - elseif ($mode & 0xA000) $type='l'; // Symbolic Link - elseif ($mode & 0xC000) $type='s'; // Socket - else $type='u'; // UNKNOWN - - // Determine permissions - $owner['read'] = (($mode & 00400) ? 'r' : '-'); - $owner['write'] = (($mode & 00200) ? 'w' : '-'); - $owner['execute'] = (($mode & 00100) ? 'x' : '-'); - $group['read'] = (($mode & 00040) ? 'r' : '-'); - $group['write'] = (($mode & 00020) ? 'w' : '-'); - $group['execute'] = (($mode & 00010) ? 'x' : '-'); - $world['read'] = (($mode & 00004) ? 'r' : '-'); - $world['write'] = (($mode & 00002) ? 'w' : '-'); - $world['execute'] = (($mode & 00001) ? 'x' : '-'); - - // Adjust for SUID, SGID and sticky bit - if ($mode & 0x800) $owner['execute'] = ($owner['execute'] == 'x') ? 's' : 'S'; - if ($mode & 0x400) $group['execute'] = ($group['execute'] == 'x') ? 's' : 'S'; - if ($mode & 0x200) $world['execute'] = ($world['execute'] == 'x') ? 't' : 'T'; - - $s = sprintf('%1s', $type); - $s .= sprintf('%1s%1s%1s', $owner['read'], $owner['write'], $owner['execute']); - $s .= sprintf('%1s%1s%1s', $group['read'], $group['write'], $group['execute']); - $s .= sprintf('%1s%1s%1s'."\n", $world['read'], $world['write'], $world['execute']); - return $s; - } - - // Converts the file type - function get_flag_type($typflag) { - static $flag_types = array( - '0' => 'LF_NORMAL', - '1' => 'LF_LINK', - '2' => 'LF_SYNLINK', - '3' => 'LF_CHR', - '4' => 'LF_BLK', - '5' => 'LF_DIR', - '6' => 'LF_FIFO', - '7' => 'LF_CONFIG', - 'D' => 'LF_DUMPDIR', - 'K' => 'LF_LONGLINK', - 'L' => 'LF_LONGNAME', - 'M' => 'LF_MULTIVOL', - 'N' => 'LF_NAMES', - 'S' => 'LF_SPARSE', - 'V' => 'LF_VOLHDR' - ); - return @$flag_types[$typflag]; - } - -} - -?> \ No newline at end of file diff --git a/getid3/getid3/module.archive.zip.php b/getid3/getid3/module.archive.zip.php deleted file mode 100644 index da6fb72..0000000 --- a/getid3/getid3/module.archive.zip.php +++ /dev/null @@ -1,415 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.archive.zip.php // -// module for analyzing pkZip files // -// dependencies: NONE // -// /// -///////////////////////////////////////////////////////////////// - - -class getid3_zip -{ - - function getid3_zip(&$fd, &$ThisFileInfo) { - - $ThisFileInfo['fileformat'] = 'zip'; - $ThisFileInfo['zip']['encoding'] = 'ISO-8859-1'; - $ThisFileInfo['zip']['files'] = array(); - - $ThisFileInfo['zip']['compressed_size'] = 0; - $ThisFileInfo['zip']['uncompressed_size'] = 0; - $ThisFileInfo['zip']['entries_count'] = 0; - - $EOCDsearchData = ''; - $EOCDsearchCounter = 0; - while ($EOCDsearchCounter++ < 512) { - - fseek($fd, -128 * $EOCDsearchCounter, SEEK_END); - $EOCDsearchData = fread($fd, 128).$EOCDsearchData; - - if (strstr($EOCDsearchData, 'PK'."\x05\x06")) { - - $EOCDposition = strpos($EOCDsearchData, 'PK'."\x05\x06"); - fseek($fd, (-128 * $EOCDsearchCounter) + $EOCDposition, SEEK_END); - $ThisFileInfo['zip']['end_central_directory'] = $this->ZIPparseEndOfCentralDirectory($fd); - - fseek($fd, $ThisFileInfo['zip']['end_central_directory']['directory_offset'], SEEK_SET); - $ThisFileInfo['zip']['entries_count'] = 0; - while ($centraldirectoryentry = $this->ZIPparseCentralDirectory($fd)) { - $ThisFileInfo['zip']['central_directory'][] = $centraldirectoryentry; - $ThisFileInfo['zip']['entries_count']++; - $ThisFileInfo['zip']['compressed_size'] += $centraldirectoryentry['compressed_size']; - $ThisFileInfo['zip']['uncompressed_size'] += $centraldirectoryentry['uncompressed_size']; - - if ($centraldirectoryentry['uncompressed_size'] > 0) { - $ThisFileInfo['zip']['files'] = getid3_lib::array_merge_clobber($ThisFileInfo['zip']['files'], getid3_lib::CreateDeepArray($centraldirectoryentry['filename'], '/', $centraldirectoryentry['uncompressed_size'])); - } - } - - if ($ThisFileInfo['zip']['entries_count'] == 0) { - $ThisFileInfo['error'][] = 'No Central Directory entries found (truncated file?)'; - return false; - } - - if (!empty($ThisFileInfo['zip']['end_central_directory']['comment'])) { - $ThisFileInfo['zip']['comments']['comment'][] = $ThisFileInfo['zip']['end_central_directory']['comment']; - } - - if (isset($ThisFileInfo['zip']['central_directory'][0]['compression_method'])) { - $ThisFileInfo['zip']['compression_method'] = $ThisFileInfo['zip']['central_directory'][0]['compression_method']; - } - if (isset($ThisFileInfo['zip']['central_directory'][0]['flags']['compression_speed'])) { - $ThisFileInfo['zip']['compression_speed'] = $ThisFileInfo['zip']['central_directory'][0]['flags']['compression_speed']; - } - if (isset($ThisFileInfo['zip']['compression_method']) && ($ThisFileInfo['zip']['compression_method'] == 'store') && !isset($ThisFileInfo['zip']['compression_speed'])) { - $ThisFileInfo['zip']['compression_speed'] = 'store'; - } - - return true; - - } - - } - - if ($this->getZIPentriesFilepointer($fd, $ThisFileInfo)) { - - // central directory couldn't be found and/or parsed - // scan through actual file data entries, recover as much as possible from probable trucated file - if ($ThisFileInfo['zip']['compressed_size'] > ($ThisFileInfo['filesize'] - 46 - 22)) { - $ThisFileInfo['error'][] = 'Warning: Truncated file! - Total compressed file sizes ('.$ThisFileInfo['zip']['compressed_size'].' bytes) is greater than filesize minus Central Directory and End Of Central Directory structures ('.($ThisFileInfo['filesize'] - 46 - 22).' bytes)'; - } - $ThisFileInfo['error'][] = 'Cannot find End Of Central Directory - returned list of files in [zip][entries] array may not be complete'; - foreach ($ThisFileInfo['zip']['entries'] as $key => $valuearray) { - $ThisFileInfo['zip']['files'][$valuearray['filename']] = $valuearray['uncompressed_size']; - } - return true; - - } else { - - unset($ThisFileInfo['zip']); - $ThisFileInfo['fileformat'] = ''; - $ThisFileInfo['error'][] = 'Cannot find End Of Central Directory (truncated file?)'; - return false; - - } - } - - - function getZIPHeaderFilepointerTopDown(&$fd, &$ThisFileInfo) { - $ThisFileInfo['fileformat'] = 'zip'; - - $ThisFileInfo['zip']['compressed_size'] = 0; - $ThisFileInfo['zip']['uncompressed_size'] = 0; - $ThisFileInfo['zip']['entries_count'] = 0; - - rewind($fd); - while ($fileentry = $this->ZIPparseLocalFileHeader($fd)) { - $ThisFileInfo['zip']['entries'][] = $fileentry; - $ThisFileInfo['zip']['entries_count']++; - } - if ($ThisFileInfo['zip']['entries_count'] == 0) { - $ThisFileInfo['error'][] = 'No Local File Header entries found'; - return false; - } - - $ThisFileInfo['zip']['entries_count'] = 0; - while ($centraldirectoryentry = $this->ZIPparseCentralDirectory($fd)) { - $ThisFileInfo['zip']['central_directory'][] = $centraldirectoryentry; - $ThisFileInfo['zip']['entries_count']++; - $ThisFileInfo['zip']['compressed_size'] += $centraldirectoryentry['compressed_size']; - $ThisFileInfo['zip']['uncompressed_size'] += $centraldirectoryentry['uncompressed_size']; - } - if ($ThisFileInfo['zip']['entries_count'] == 0) { - $ThisFileInfo['error'][] = 'No Central Directory entries found (truncated file?)'; - return false; - } - - if ($EOCD = $this->ZIPparseEndOfCentralDirectory($fd)) { - $ThisFileInfo['zip']['end_central_directory'] = $EOCD; - } else { - $ThisFileInfo['error'][] = 'No End Of Central Directory entry found (truncated file?)'; - return false; - } - - if (!empty($ThisFileInfo['zip']['end_central_directory']['comment'])) { - $ThisFileInfo['zip']['comments']['comment'][] = $ThisFileInfo['zip']['end_central_directory']['comment']; - } - - return true; - } - - - function getZIPentriesFilepointer(&$fd, &$ThisFileInfo) { - $ThisFileInfo['zip']['compressed_size'] = 0; - $ThisFileInfo['zip']['uncompressed_size'] = 0; - $ThisFileInfo['zip']['entries_count'] = 0; - - rewind($fd); - while ($fileentry = $this->ZIPparseLocalFileHeader($fd)) { - $ThisFileInfo['zip']['entries'][] = $fileentry; - $ThisFileInfo['zip']['entries_count']++; - $ThisFileInfo['zip']['compressed_size'] += $fileentry['compressed_size']; - $ThisFileInfo['zip']['uncompressed_size'] += $fileentry['uncompressed_size']; - } - if ($ThisFileInfo['zip']['entries_count'] == 0) { - $ThisFileInfo['error'][] = 'No Local File Header entries found'; - return false; - } - - return true; - } - - - function ZIPparseLocalFileHeader(&$fd) { - $LocalFileHeader['offset'] = ftell($fd); - - $ZIPlocalFileHeader = fread($fd, 30); - - $LocalFileHeader['raw']['signature'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 0, 4)); - if ($LocalFileHeader['raw']['signature'] != 0x04034B50) { - // invalid Local File Header Signature - fseek($fd, $LocalFileHeader['offset'], SEEK_SET); // seek back to where filepointer originally was so it can be handled properly - return false; - } - $LocalFileHeader['raw']['extract_version'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 4, 2)); - $LocalFileHeader['raw']['general_flags'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 6, 2)); - $LocalFileHeader['raw']['compression_method'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 8, 2)); - $LocalFileHeader['raw']['last_mod_file_time'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 10, 2)); - $LocalFileHeader['raw']['last_mod_file_date'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 12, 2)); - $LocalFileHeader['raw']['crc_32'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 14, 4)); - $LocalFileHeader['raw']['compressed_size'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 18, 4)); - $LocalFileHeader['raw']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 22, 4)); - $LocalFileHeader['raw']['filename_length'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 26, 2)); - $LocalFileHeader['raw']['extra_field_length'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 28, 2)); - - $LocalFileHeader['extract_version'] = sprintf('%1.1f', $LocalFileHeader['raw']['extract_version'] / 10); - $LocalFileHeader['host_os'] = $this->ZIPversionOSLookup(($LocalFileHeader['raw']['extract_version'] & 0xFF00) >> 8); - $LocalFileHeader['compression_method'] = $this->ZIPcompressionMethodLookup($LocalFileHeader['raw']['compression_method']); - $LocalFileHeader['compressed_size'] = $LocalFileHeader['raw']['compressed_size']; - $LocalFileHeader['uncompressed_size'] = $LocalFileHeader['raw']['uncompressed_size']; - $LocalFileHeader['flags'] = $this->ZIPparseGeneralPurposeFlags($LocalFileHeader['raw']['general_flags'], $LocalFileHeader['raw']['compression_method']); - $LocalFileHeader['last_modified_timestamp'] = $this->DOStime2UNIXtime($LocalFileHeader['raw']['last_mod_file_date'], $LocalFileHeader['raw']['last_mod_file_time']); - - $FilenameExtrafieldLength = $LocalFileHeader['raw']['filename_length'] + $LocalFileHeader['raw']['extra_field_length']; - if ($FilenameExtrafieldLength > 0) { - $ZIPlocalFileHeader .= fread($fd, $FilenameExtrafieldLength); - - if ($LocalFileHeader['raw']['filename_length'] > 0) { - $LocalFileHeader['filename'] = substr($ZIPlocalFileHeader, 30, $LocalFileHeader['raw']['filename_length']); - } - if ($LocalFileHeader['raw']['extra_field_length'] > 0) { - $LocalFileHeader['raw']['extra_field_data'] = substr($ZIPlocalFileHeader, 30 + $LocalFileHeader['raw']['filename_length'], $LocalFileHeader['raw']['extra_field_length']); - } - } - - $LocalFileHeader['data_offset'] = ftell($fd); - //$LocalFileHeader['compressed_data'] = fread($fd, $LocalFileHeader['raw']['compressed_size']); - fseek($fd, $LocalFileHeader['raw']['compressed_size'], SEEK_CUR); - - if ($LocalFileHeader['flags']['data_descriptor_used']) { - $DataDescriptor = fread($fd, 12); - $LocalFileHeader['data_descriptor']['crc_32'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 0, 4)); - $LocalFileHeader['data_descriptor']['compressed_size'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 4, 4)); - $LocalFileHeader['data_descriptor']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 8, 4)); - } - - return $LocalFileHeader; - } - - - function ZIPparseCentralDirectory(&$fd) { - $CentralDirectory['offset'] = ftell($fd); - - $ZIPcentralDirectory = fread($fd, 46); - - $CentralDirectory['raw']['signature'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 0, 4)); - if ($CentralDirectory['raw']['signature'] != 0x02014B50) { - // invalid Central Directory Signature - fseek($fd, $CentralDirectory['offset'], SEEK_SET); // seek back to where filepointer originally was so it can be handled properly - return false; - } - $CentralDirectory['raw']['create_version'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 4, 2)); - $CentralDirectory['raw']['extract_version'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 6, 2)); - $CentralDirectory['raw']['general_flags'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 8, 2)); - $CentralDirectory['raw']['compression_method'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 10, 2)); - $CentralDirectory['raw']['last_mod_file_time'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 12, 2)); - $CentralDirectory['raw']['last_mod_file_date'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 14, 2)); - $CentralDirectory['raw']['crc_32'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 16, 4)); - $CentralDirectory['raw']['compressed_size'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 20, 4)); - $CentralDirectory['raw']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 24, 4)); - $CentralDirectory['raw']['filename_length'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 28, 2)); - $CentralDirectory['raw']['extra_field_length'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 30, 2)); - $CentralDirectory['raw']['file_comment_length'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 32, 2)); - $CentralDirectory['raw']['disk_number_start'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 34, 2)); - $CentralDirectory['raw']['internal_file_attrib'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 36, 2)); - $CentralDirectory['raw']['external_file_attrib'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 38, 4)); - $CentralDirectory['raw']['local_header_offset'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 42, 4)); - - $CentralDirectory['entry_offset'] = $CentralDirectory['raw']['local_header_offset']; - $CentralDirectory['create_version'] = sprintf('%1.1f', $CentralDirectory['raw']['create_version'] / 10); - $CentralDirectory['extract_version'] = sprintf('%1.1f', $CentralDirectory['raw']['extract_version'] / 10); - $CentralDirectory['host_os'] = $this->ZIPversionOSLookup(($CentralDirectory['raw']['extract_version'] & 0xFF00) >> 8); - $CentralDirectory['compression_method'] = $this->ZIPcompressionMethodLookup($CentralDirectory['raw']['compression_method']); - $CentralDirectory['compressed_size'] = $CentralDirectory['raw']['compressed_size']; - $CentralDirectory['uncompressed_size'] = $CentralDirectory['raw']['uncompressed_size']; - $CentralDirectory['flags'] = $this->ZIPparseGeneralPurposeFlags($CentralDirectory['raw']['general_flags'], $CentralDirectory['raw']['compression_method']); - $CentralDirectory['last_modified_timestamp'] = $this->DOStime2UNIXtime($CentralDirectory['raw']['last_mod_file_date'], $CentralDirectory['raw']['last_mod_file_time']); - - $FilenameExtrafieldCommentLength = $CentralDirectory['raw']['filename_length'] + $CentralDirectory['raw']['extra_field_length'] + $CentralDirectory['raw']['file_comment_length']; - if ($FilenameExtrafieldCommentLength > 0) { - $FilenameExtrafieldComment = fread($fd, $FilenameExtrafieldCommentLength); - - if ($CentralDirectory['raw']['filename_length'] > 0) { - $CentralDirectory['filename'] = substr($FilenameExtrafieldComment, 0, $CentralDirectory['raw']['filename_length']); - } - if ($CentralDirectory['raw']['extra_field_length'] > 0) { - $CentralDirectory['raw']['extra_field_data'] = substr($FilenameExtrafieldComment, $CentralDirectory['raw']['filename_length'], $CentralDirectory['raw']['extra_field_length']); - } - if ($CentralDirectory['raw']['file_comment_length'] > 0) { - $CentralDirectory['file_comment'] = substr($FilenameExtrafieldComment, $CentralDirectory['raw']['filename_length'] + $CentralDirectory['raw']['extra_field_length'], $CentralDirectory['raw']['file_comment_length']); - } - } - - return $CentralDirectory; - } - - function ZIPparseEndOfCentralDirectory(&$fd) { - $EndOfCentralDirectory['offset'] = ftell($fd); - - $ZIPendOfCentralDirectory = fread($fd, 22); - - $EndOfCentralDirectory['signature'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 0, 4)); - if ($EndOfCentralDirectory['signature'] != 0x06054B50) { - // invalid End Of Central Directory Signature - fseek($fd, $EndOfCentralDirectory['offset'], SEEK_SET); // seek back to where filepointer originally was so it can be handled properly - return false; - } - $EndOfCentralDirectory['disk_number_current'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 4, 2)); - $EndOfCentralDirectory['disk_number_start_directory'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 6, 2)); - $EndOfCentralDirectory['directory_entries_this_disk'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 8, 2)); - $EndOfCentralDirectory['directory_entries_total'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 10, 2)); - $EndOfCentralDirectory['directory_size'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 12, 4)); - $EndOfCentralDirectory['directory_offset'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 16, 4)); - $EndOfCentralDirectory['comment_length'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 20, 2)); - - if ($EndOfCentralDirectory['comment_length'] > 0) { - $EndOfCentralDirectory['comment'] = fread($fd, $EndOfCentralDirectory['comment_length']); - } - - return $EndOfCentralDirectory; - } - - - function ZIPparseGeneralPurposeFlags($flagbytes, $compressionmethod) { - $ParsedFlags['encrypted'] = (bool) ($flagbytes & 0x0001); - - switch ($compressionmethod) { - case 6: - $ParsedFlags['dictionary_size'] = (($flagbytes & 0x0002) ? 8192 : 4096); - $ParsedFlags['shannon_fano_trees'] = (($flagbytes & 0x0004) ? 3 : 2); - break; - - case 8: - case 9: - switch (($flagbytes & 0x0006) >> 1) { - case 0: - $ParsedFlags['compression_speed'] = 'normal'; - break; - case 1: - $ParsedFlags['compression_speed'] = 'maximum'; - break; - case 2: - $ParsedFlags['compression_speed'] = 'fast'; - break; - case 3: - $ParsedFlags['compression_speed'] = 'superfast'; - break; - } - break; - } - $ParsedFlags['data_descriptor_used'] = (bool) ($flagbytes & 0x0008); - - return $ParsedFlags; - } - - - function ZIPversionOSLookup($index) { - static $ZIPversionOSLookup = array( - 0 => 'MS-DOS and OS/2 (FAT / VFAT / FAT32 file systems)', - 1 => 'Amiga', - 2 => 'OpenVMS', - 3 => 'Unix', - 4 => 'VM/CMS', - 5 => 'Atari ST', - 6 => 'OS/2 H.P.F.S.', - 7 => 'Macintosh', - 8 => 'Z-System', - 9 => 'CP/M', - 10 => 'Windows NTFS', - 11 => 'MVS', - 12 => 'VSE', - 13 => 'Acorn Risc', - 14 => 'VFAT', - 15 => 'Alternate MVS', - 16 => 'BeOS', - 17 => 'Tandem' - ); - - return (isset($ZIPversionOSLookup[$index]) ? $ZIPversionOSLookup[$index] : '[unknown]'); - } - - function ZIPcompressionMethodLookup($index) { - static $ZIPcompressionMethodLookup = array( - 0 => 'store', - 1 => 'shrink', - 2 => 'reduce-1', - 3 => 'reduce-2', - 4 => 'reduce-3', - 5 => 'reduce-4', - 6 => 'implode', - 7 => 'tokenize', - 8 => 'deflate', - 9 => 'deflate64', - 10 => 'PKWARE Date Compression Library Imploding' - ); - - return (isset($ZIPcompressionMethodLookup[$index]) ? $ZIPcompressionMethodLookup[$index] : '[unknown]'); - } - - function DOStime2UNIXtime($DOSdate, $DOStime) { - // wFatDate - // Specifies the MS-DOS date. The date is a packed 16-bit value with the following format: - // Bits Contents - // 0-4 Day of the month (1-31) - // 5-8 Month (1 = January, 2 = February, and so on) - // 9-15 Year offset from 1980 (add 1980 to get actual year) - - $UNIXday = ($DOSdate & 0x001F); - $UNIXmonth = (($DOSdate & 0x01E0) >> 5); - $UNIXyear = (($DOSdate & 0xFE00) >> 9) + 1980; - - // wFatTime - // Specifies the MS-DOS time. The time is a packed 16-bit value with the following format: - // Bits Contents - // 0-4 Second divided by 2 - // 5-10 Minute (0-59) - // 11-15 Hour (0-23 on a 24-hour clock) - - $UNIXsecond = ($DOStime & 0x001F) * 2; - $UNIXminute = (($DOStime & 0x07E0) >> 5); - $UNIXhour = (($DOStime & 0xF800) >> 11); - - return gmmktime($UNIXhour, $UNIXminute, $UNIXsecond, $UNIXmonth, $UNIXday, $UNIXyear); - } - -} - - -?> \ No newline at end of file diff --git a/getid3/getid3/module.audio-video.asf.php b/getid3/getid3/module.audio-video.asf.php deleted file mode 100644 index b13db54..0000000 --- a/getid3/getid3/module.audio-video.asf.php +++ /dev/null @@ -1,1635 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.audio-video.asf.php // -// module for analyzing ASF, WMA and WMV files // -// dependencies: module.audio-video.riff.php // -// /// -///////////////////////////////////////////////////////////////// - -getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true); - -$GUIDarray = getid3_asf::KnownGUIDs(); -foreach ($GUIDarray as $GUIDname => $hexstringvalue) { - // initialize all GUID constants - define($GUIDname, getid3_asf::GUIDtoBytestring($hexstringvalue)); -} - - - -class getid3_asf -{ - - function getid3_asf(&$fd, &$ThisFileInfo) { - - // Shortcuts - $thisfile_audio = &$ThisFileInfo['audio']; - $thisfile_video = &$ThisFileInfo['video']; - $ThisFileInfo['asf'] = array(); - $thisfile_asf = &$ThisFileInfo['asf']; - $thisfile_asf['comments'] = array(); - $thisfile_asf_comments = &$thisfile_asf['comments']; - $thisfile_asf['header_object'] = array(); - $thisfile_asf_headerobject = &$thisfile_asf['header_object']; - - - // ASF structure: - // * Header Object [required] - // * File Properties Object [required] (global file attributes) - // * Stream Properties Object [required] (defines media stream & characteristics) - // * Header Extension Object [required] (additional functionality) - // * Content Description Object (bibliographic information) - // * Script Command Object (commands for during playback) - // * Marker Object (named jumped points within the file) - // * Data Object [required] - // * Data Packets - // * Index Object - - // Header Object: (mandatory, one only) - // Field Name Field Type Size (bits) - // Object ID GUID 128 // GUID for header object - GETID3_ASF_Header_Object - // Object Size QWORD 64 // size of header object, including 30 bytes of Header Object header - // Number of Header Objects DWORD 32 // number of objects in header object - // Reserved1 BYTE 8 // hardcoded: 0x01 - // Reserved2 BYTE 8 // hardcoded: 0x02 - - $ThisFileInfo['fileformat'] = 'asf'; - - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $HeaderObjectData = fread($fd, 30); - - $thisfile_asf_headerobject['objectid'] = substr($HeaderObjectData, 0, 16); - $thisfile_asf_headerobject['objectid_guid'] = $this->BytestringToGUID($thisfile_asf_headerobject['objectid']); - if ($thisfile_asf_headerobject['objectid'] != GETID3_ASF_Header_Object) { - $ThisFileInfo['warning'][] = 'ASF header GUID {'.$this->BytestringToGUID($thisfile_asf_headerobject['objectid']).'} does not match expected "GETID3_ASF_Header_Object" GUID {'.$this->BytestringToGUID(GETID3_ASF_Header_Object).'}'; - unset($ThisFileInfo['fileformat']); - unset($ThisFileInfo['asf']); - return false; - break; - } - $thisfile_asf_headerobject['objectsize'] = getid3_lib::LittleEndian2Int(substr($HeaderObjectData, 16, 8)); - $thisfile_asf_headerobject['headerobjects'] = getid3_lib::LittleEndian2Int(substr($HeaderObjectData, 24, 4)); - $thisfile_asf_headerobject['reserved1'] = getid3_lib::LittleEndian2Int(substr($HeaderObjectData, 28, 1)); - $thisfile_asf_headerobject['reserved2'] = getid3_lib::LittleEndian2Int(substr($HeaderObjectData, 29, 1)); - - //$ASFHeaderData = $HeaderObjectData; - $ASFHeaderData = fread($fd, $thisfile_asf_headerobject['objectsize'] - 30); - //$offset = 30; - $offset = 0; - - for ($HeaderObjectsCounter = 0; $HeaderObjectsCounter < $thisfile_asf_headerobject['headerobjects']; $HeaderObjectsCounter++) { - $NextObjectGUID = substr($ASFHeaderData, $offset, 16); - $offset += 16; - $NextObjectGUIDtext = $this->BytestringToGUID($NextObjectGUID); - $NextObjectSize = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8)); - $offset += 8; - switch ($NextObjectGUID) { - - case GETID3_ASF_File_Properties_Object: - // File Properties Object: (mandatory, one only) - // Field Name Field Type Size (bits) - // Object ID GUID 128 // GUID for file properties object - GETID3_ASF_File_Properties_Object - // Object Size QWORD 64 // size of file properties object, including 104 bytes of File Properties Object header - // File ID GUID 128 // unique ID - identical to File ID in Data Object - // File Size QWORD 64 // entire file in bytes. Invalid if Broadcast Flag == 1 - // Creation Date QWORD 64 // date & time of file creation. Maybe invalid if Broadcast Flag == 1 - // Data Packets Count QWORD 64 // number of data packets in Data Object. Invalid if Broadcast Flag == 1 - // Play Duration QWORD 64 // playtime, in 100-nanosecond units. Invalid if Broadcast Flag == 1 - // Send Duration QWORD 64 // time needed to send file, in 100-nanosecond units. Players can ignore this value. Invalid if Broadcast Flag == 1 - // Preroll QWORD 64 // time to buffer data before starting to play file, in 1-millisecond units. If <> 0, PlayDuration and PresentationTime have been offset by this amount - // Flags DWORD 32 // - // * Broadcast Flag bits 1 (0x01) // file is currently being written, some header values are invalid - // * Seekable Flag bits 1 (0x02) // is file seekable - // * Reserved bits 30 (0xFFFFFFFC) // reserved - set to zero - // Minimum Data Packet Size DWORD 32 // in bytes. should be same as Maximum Data Packet Size. Invalid if Broadcast Flag == 1 - // Maximum Data Packet Size DWORD 32 // in bytes. should be same as Minimum Data Packet Size. Invalid if Broadcast Flag == 1 - // Maximum Bitrate DWORD 32 // maximum instantaneous bitrate in bits per second for entire file, including all data streams and ASF overhead - - // shortcut - $thisfile_asf['file_properties_object'] = array(); - $thisfile_asf_filepropertiesobject = &$thisfile_asf['file_properties_object']; - - $thisfile_asf_filepropertiesobject['objectid'] = $NextObjectGUID; - $thisfile_asf_filepropertiesobject['objectid_guid'] = $NextObjectGUIDtext; - $thisfile_asf_filepropertiesobject['objectsize'] = $NextObjectSize; - $thisfile_asf_filepropertiesobject['fileid'] = substr($ASFHeaderData, $offset, 16); - $offset += 16; - $thisfile_asf_filepropertiesobject['fileid_guid'] = $this->BytestringToGUID($thisfile_asf_filepropertiesobject['fileid']); - $thisfile_asf_filepropertiesobject['filesize'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8)); - $offset += 8; - $thisfile_asf_filepropertiesobject['creation_date'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8)); - $thisfile_asf_filepropertiesobject['creation_date_unix'] = $this->FILETIMEtoUNIXtime($thisfile_asf_filepropertiesobject['creation_date']); - $offset += 8; - $thisfile_asf_filepropertiesobject['data_packets'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8)); - $offset += 8; - $thisfile_asf_filepropertiesobject['play_duration'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8)); - $offset += 8; - $thisfile_asf_filepropertiesobject['send_duration'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8)); - $offset += 8; - $thisfile_asf_filepropertiesobject['preroll'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8)); - $offset += 8; - $ThisFileInfo['playtime_seconds'] = ($thisfile_asf_filepropertiesobject['play_duration'] / 10000000) - ($thisfile_asf_filepropertiesobject['preroll'] / 1000); - $thisfile_asf_filepropertiesobject['flags_raw'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); - $offset += 4; - $thisfile_asf_filepropertiesobject['flags']['broadcast'] = (bool) ($thisfile_asf_filepropertiesobject['flags_raw'] & 0x0001); - $thisfile_asf_filepropertiesobject['flags']['seekable'] = (bool) ($thisfile_asf_filepropertiesobject['flags_raw'] & 0x0002); - - $thisfile_asf_filepropertiesobject['min_packet_size'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); - $offset += 4; - $thisfile_asf_filepropertiesobject['max_packet_size'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); - $offset += 4; - $thisfile_asf_filepropertiesobject['max_bitrate'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); - $offset += 4; - //$ThisFileInfo['bitrate'] = $thisfile_asf_filepropertiesobject['max_bitrate']; - $ThisFileInfo['bitrate'] = ($thisfile_asf_filepropertiesobject['filesize'] * 8) / $ThisFileInfo['playtime_seconds']; - break; - - case GETID3_ASF_Stream_Properties_Object: - // Stream Properties Object: (mandatory, one per media stream) - // Field Name Field Type Size (bits) - // Object ID GUID 128 // GUID for stream properties object - GETID3_ASF_Stream_Properties_Object - // Object Size QWORD 64 // size of stream properties object, including 78 bytes of Stream Properties Object header - // Stream Type GUID 128 // GETID3_ASF_Audio_Media, GETID3_ASF_Video_Media or GETID3_ASF_Command_Media - // Error Correction Type GUID 128 // GETID3_ASF_Audio_Spread for audio-only streams, GETID3_ASF_No_Error_Correction for other stream types - // Time Offset QWORD 64 // 100-nanosecond units. typically zero. added to all timestamps of samples in the stream - // Type-Specific Data Length DWORD 32 // number of bytes for Type-Specific Data field - // Error Correction Data Length DWORD 32 // number of bytes for Error Correction Data field - // Flags WORD 16 // - // * Stream Number bits 7 (0x007F) // number of this stream. 1 <= valid <= 127 - // * Reserved bits 8 (0x7F80) // reserved - set to zero - // * Encrypted Content Flag bits 1 (0x8000) // stream contents encrypted if set - // Reserved DWORD 32 // reserved - set to zero - // Type-Specific Data BYTESTREAM variable // type-specific format data, depending on value of Stream Type - // Error Correction Data BYTESTREAM variable // error-correction-specific format data, depending on value of Error Correct Type - - // There is one GETID3_ASF_Stream_Properties_Object for each stream (audio, video) but the - // stream number isn't known until halfway through decoding the structure, hence it - // it is decoded to a temporary variable and then stuck in the appropriate index later - - $StreamPropertiesObjectData['objectid'] = $NextObjectGUID; - $StreamPropertiesObjectData['objectid_guid'] = $NextObjectGUIDtext; - $StreamPropertiesObjectData['objectsize'] = $NextObjectSize; - $StreamPropertiesObjectData['stream_type'] = substr($ASFHeaderData, $offset, 16); - $offset += 16; - $StreamPropertiesObjectData['stream_type_guid'] = $this->BytestringToGUID($StreamPropertiesObjectData['stream_type']); - $StreamPropertiesObjectData['error_correct_type'] = substr($ASFHeaderData, $offset, 16); - $offset += 16; - $StreamPropertiesObjectData['error_correct_guid'] = $this->BytestringToGUID($StreamPropertiesObjectData['error_correct_type']); - $StreamPropertiesObjectData['time_offset'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8)); - $offset += 8; - $StreamPropertiesObjectData['type_data_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); - $offset += 4; - $StreamPropertiesObjectData['error_data_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); - $offset += 4; - $StreamPropertiesObjectData['flags_raw'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); - $offset += 2; - $StreamPropertiesObjectStreamNumber = $StreamPropertiesObjectData['flags_raw'] & 0x007F; - $StreamPropertiesObjectData['flags']['encrypted'] = (bool) ($StreamPropertiesObjectData['flags_raw'] & 0x8000); - - $offset += 4; // reserved - DWORD - $StreamPropertiesObjectData['type_specific_data'] = substr($ASFHeaderData, $offset, $StreamPropertiesObjectData['type_data_length']); - $offset += $StreamPropertiesObjectData['type_data_length']; - $StreamPropertiesObjectData['error_correct_data'] = substr($ASFHeaderData, $offset, $StreamPropertiesObjectData['error_data_length']); - $offset += $StreamPropertiesObjectData['error_data_length']; - - switch ($StreamPropertiesObjectData['stream_type']) { - - case GETID3_ASF_Audio_Media: - $thisfile_audio['dataformat'] = (!empty($thisfile_audio['dataformat']) ? $thisfile_audio['dataformat'] : 'asf'); - $thisfile_audio['bitrate_mode'] = (!empty($thisfile_audio['bitrate_mode']) ? $thisfile_audio['bitrate_mode'] : 'cbr'); - - $audiodata = getid3_riff::RIFFparseWAVEFORMATex(substr($StreamPropertiesObjectData['type_specific_data'], 0, 16)); - unset($audiodata['raw']); - $thisfile_audio = getid3_lib::array_merge_noclobber($audiodata, $thisfile_audio); - break; - - case GETID3_ASF_Video_Media: - $thisfile_video['dataformat'] = (!empty($thisfile_video['dataformat']) ? $thisfile_video['dataformat'] : 'asf'); - $thisfile_video['bitrate_mode'] = (!empty($thisfile_video['bitrate_mode']) ? $thisfile_video['bitrate_mode'] : 'cbr'); - break; - - case GETID3_ASF_Command_Media: - default: - // do nothing - break; - - } - - $thisfile_asf['stream_properties_object'][$StreamPropertiesObjectStreamNumber] = $StreamPropertiesObjectData; - unset($StreamPropertiesObjectData); // clear for next stream, if any - break; - - case GETID3_ASF_Header_Extension_Object: - // Header Extension Object: (mandatory, one only) - // Field Name Field Type Size (bits) - // Object ID GUID 128 // GUID for Header Extension object - GETID3_ASF_Header_Extension_Object - // Object Size QWORD 64 // size of Header Extension object, including 46 bytes of Header Extension Object header - // Reserved Field 1 GUID 128 // hardcoded: GETID3_ASF_Reserved_1 - // Reserved Field 2 WORD 16 // hardcoded: 0x00000006 - // Header Extension Data Size DWORD 32 // in bytes. valid: 0, or > 24. equals object size minus 46 - // Header Extension Data BYTESTREAM variable // array of zero or more extended header objects - - // shortcut - $thisfile_asf['header_extension_object'] = array(); - $thisfile_asf_headerextensionobject = &$thisfile_asf['header_extension_object']; - - $thisfile_asf_headerextensionobject['objectid'] = $NextObjectGUID; - $thisfile_asf_headerextensionobject['objectid_guid'] = $NextObjectGUIDtext; - $thisfile_asf_headerextensionobject['objectsize'] = $NextObjectSize; - $thisfile_asf_headerextensionobject['reserved_1'] = substr($ASFHeaderData, $offset, 16); - $offset += 16; - $thisfile_asf_headerextensionobject['reserved_1_guid'] = $this->BytestringToGUID($thisfile_asf_headerextensionobject['reserved_1']); - if ($thisfile_asf_headerextensionobject['reserved_1'] != GETID3_ASF_Reserved_1) { - $ThisFileInfo['warning'][] = 'header_extension_object.reserved_1 GUID ('.$this->BytestringToGUID($thisfile_asf_headerextensionobject['reserved_1']).') does not match expected "GETID3_ASF_Reserved_1" GUID ('.$this->BytestringToGUID(GETID3_ASF_Reserved_1).')'; - //return false; - break; - } - $thisfile_asf_headerextensionobject['reserved_2'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); - $offset += 2; - if ($thisfile_asf_headerextensionobject['reserved_2'] != 6) { - $ThisFileInfo['warning'][] = 'header_extension_object.reserved_2 ('.getid3_lib::PrintHexBytes($thisfile_asf_headerextensionobject['reserved_2']).') does not match expected value of "6"'; - //return false; - break; - } - $thisfile_asf_headerextensionobject['extension_data_size'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); - $offset += 4; - $thisfile_asf_headerextensionobject['extension_data'] = substr($ASFHeaderData, $offset, $thisfile_asf_headerextensionobject['extension_data_size']); - $offset += $thisfile_asf_headerextensionobject['extension_data_size']; - break; - - case GETID3_ASF_Codec_List_Object: - // Codec List Object: (optional, one only) - // Field Name Field Type Size (bits) - // Object ID GUID 128 // GUID for Codec List object - GETID3_ASF_Codec_List_Object - // Object Size QWORD 64 // size of Codec List object, including 44 bytes of Codec List Object header - // Reserved GUID 128 // hardcoded: 86D15241-311D-11D0-A3A4-00A0C90348F6 - // Codec Entries Count DWORD 32 // number of entries in Codec Entries array - // Codec Entries array of: variable // - // * Type WORD 16 // 0x0001 = Video Codec, 0x0002 = Audio Codec, 0xFFFF = Unknown Codec - // * Codec Name Length WORD 16 // number of Unicode characters stored in the Codec Name field - // * Codec Name WCHAR variable // array of Unicode characters - name of codec used to create the content - // * Codec Description Length WORD 16 // number of Unicode characters stored in the Codec Description field - // * Codec Description WCHAR variable // array of Unicode characters - description of format used to create the content - // * Codec Information Length WORD 16 // number of Unicode characters stored in the Codec Information field - // * Codec Information BYTESTREAM variable // opaque array of information bytes about the codec used to create the content - - // shortcut - $thisfile_asf['codec_list_object'] = array(); - $thisfile_asf_codeclistobject = &$thisfile_asf['codec_list_object']; - - $thisfile_asf_codeclistobject['objectid'] = $NextObjectGUID; - $thisfile_asf_codeclistobject['objectid_guid'] = $NextObjectGUIDtext; - $thisfile_asf_codeclistobject['objectsize'] = $NextObjectSize; - $thisfile_asf_codeclistobject['reserved'] = substr($ASFHeaderData, $offset, 16); - $offset += 16; - $thisfile_asf_codeclistobject['reserved_guid'] = $this->BytestringToGUID($thisfile_asf_codeclistobject['reserved']); - if ($thisfile_asf_codeclistobject['reserved'] != $this->GUIDtoBytestring('86D15241-311D-11D0-A3A4-00A0C90348F6')) { - $ThisFileInfo['warning'][] = 'codec_list_object.reserved GUID {'.$this->BytestringToGUID($thisfile_asf_codeclistobject['reserved']).'} does not match expected "GETID3_ASF_Reserved_1" GUID {86D15241-311D-11D0-A3A4-00A0C90348F6}'; - //return false; - break; - } - $thisfile_asf_codeclistobject['codec_entries_count'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); - $offset += 4; - for ($CodecEntryCounter = 0; $CodecEntryCounter < $thisfile_asf_codeclistobject['codec_entries_count']; $CodecEntryCounter++) { - // shortcut - $thisfile_asf_codeclistobject['codec_entries'][$CodecEntryCounter] = array(); - $thisfile_asf_codeclistobject_codecentries_current = &$thisfile_asf_codeclistobject['codec_entries'][$CodecEntryCounter]; - - $thisfile_asf_codeclistobject_codecentries_current['type_raw'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); - $offset += 2; - $thisfile_asf_codeclistobject_codecentries_current['type'] = $this->ASFCodecListObjectTypeLookup($thisfile_asf_codeclistobject_codecentries_current['type_raw']); - - $CodecNameLength = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)) * 2; // 2 bytes per character - $offset += 2; - $thisfile_asf_codeclistobject_codecentries_current['name'] = substr($ASFHeaderData, $offset, $CodecNameLength); - $offset += $CodecNameLength; - - $CodecDescriptionLength = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)) * 2; // 2 bytes per character - $offset += 2; - $thisfile_asf_codeclistobject_codecentries_current['description'] = substr($ASFHeaderData, $offset, $CodecDescriptionLength); - $offset += $CodecDescriptionLength; - - $CodecInformationLength = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); - $offset += 2; - $thisfile_asf_codeclistobject_codecentries_current['information'] = substr($ASFHeaderData, $offset, $CodecInformationLength); - $offset += $CodecInformationLength; - - if ($thisfile_asf_codeclistobject_codecentries_current['type_raw'] == 2) { - // audio codec - if (strpos($thisfile_asf_codeclistobject_codecentries_current['description'], ',') === false) { - $ThisFileInfo['error'][] = '[asf][codec_list_object][codec_entries]['.$CodecEntryCounter.'][description] expected to contain comma-seperated list of parameters: "'.$thisfile_asf_codeclistobject_codecentries_current['description'].'"'; - return false; - } - list($AudioCodecBitrate, $AudioCodecFrequency, $AudioCodecChannels) = explode(',', $this->TrimConvert($thisfile_asf_codeclistobject_codecentries_current['description'])); - $thisfile_audio['codec'] = $this->TrimConvert($thisfile_asf_codeclistobject_codecentries_current['name']); - - if (!isset($thisfile_audio['bitrate']) && strstr($AudioCodecBitrate, 'kbps')) { - $thisfile_audio['bitrate'] = (int) (trim(str_replace('kbps', '', $AudioCodecBitrate)) * 1000); - } - //if (!isset($thisfile_video['bitrate']) && isset($thisfile_audio['bitrate']) && isset($thisfile_asf['file_properties_object']['max_bitrate']) && ($thisfile_asf_codeclistobject['codec_entries_count'] > 1)) { - if (!@$thisfile_video['bitrate'] && @$thisfile_audio['bitrate'] && @$ThisFileInfo['bitrate']) { - //$thisfile_video['bitrate'] = $thisfile_asf['file_properties_object']['max_bitrate'] - $thisfile_audio['bitrate']; - $thisfile_video['bitrate'] = $ThisFileInfo['bitrate'] - $thisfile_audio['bitrate']; - } - - $AudioCodecFrequency = (int) trim(str_replace('kHz', '', $AudioCodecFrequency)); - switch ($AudioCodecFrequency) { - case 8: - case 8000: - $thisfile_audio['sample_rate'] = 8000; - break; - - case 11: - case 11025: - $thisfile_audio['sample_rate'] = 11025; - break; - - case 12: - case 12000: - $thisfile_audio['sample_rate'] = 12000; - break; - - case 16: - case 16000: - $thisfile_audio['sample_rate'] = 16000; - break; - - case 22: - case 22050: - $thisfile_audio['sample_rate'] = 22050; - break; - - case 24: - case 24000: - $thisfile_audio['sample_rate'] = 24000; - break; - - case 32: - case 32000: - $thisfile_audio['sample_rate'] = 32000; - break; - - case 44: - case 441000: - $thisfile_audio['sample_rate'] = 44100; - break; - - case 48: - case 48000: - $thisfile_audio['sample_rate'] = 48000; - break; - - default: - $ThisFileInfo['warning'][] = 'unknown frequency: "'.$AudioCodecFrequency.'" ('.$this->TrimConvert($thisfile_asf_codeclistobject_codecentries_current['description']).')'; - // return false; - break; - } - - if (!isset($thisfile_audio['channels'])) { - if (strstr($AudioCodecChannels, 'stereo')) { - $thisfile_audio['channels'] = 2; - } elseif (strstr($AudioCodecChannels, 'mono')) { - $thisfile_audio['channels'] = 1; - } - } - } - } - break; - - case GETID3_ASF_Script_Command_Object: - // Script Command Object: (optional, one only) - // Field Name Field Type Size (bits) - // Object ID GUID 128 // GUID for Script Command object - GETID3_ASF_Script_Command_Object - // Object Size QWORD 64 // size of Script Command object, including 44 bytes of Script Command Object header - // Reserved GUID 128 // hardcoded: 4B1ACBE3-100B-11D0-A39B-00A0C90348F6 - // Commands Count WORD 16 // number of Commands structures in the Script Commands Objects - // Command Types Count WORD 16 // number of Command Types structures in the Script Commands Objects - // Command Types array of: variable // - // * Command Type Name Length WORD 16 // number of Unicode characters for Command Type Name - // * Command Type Name WCHAR variable // array of Unicode characters - name of a type of command - // Commands array of: variable // - // * Presentation Time DWORD 32 // presentation time of that command, in milliseconds - // * Type Index WORD 16 // type of this command, as a zero-based index into the array of Command Types of this object - // * Command Name Length WORD 16 // number of Unicode characters for Command Name - // * Command Name WCHAR variable // array of Unicode characters - name of this command - - // shortcut - $thisfile_asf['script_command_object'] = array(); - $thisfile_asf_scriptcommandobject = &$thisfile_asf['script_command_object']; - - $thisfile_asf_scriptcommandobject['objectid'] = $NextObjectGUID; - $thisfile_asf_scriptcommandobject['objectid_guid'] = $NextObjectGUIDtext; - $thisfile_asf_scriptcommandobject['objectsize'] = $NextObjectSize; - $thisfile_asf_scriptcommandobject['reserved'] = substr($ASFHeaderData, $offset, 16); - $offset += 16; - $thisfile_asf_scriptcommandobject['reserved_guid'] = $this->BytestringToGUID($thisfile_asf_scriptcommandobject['reserved']); - if ($thisfile_asf_scriptcommandobject['reserved'] != $this->GUIDtoBytestring('4B1ACBE3-100B-11D0-A39B-00A0C90348F6')) { - $ThisFileInfo['warning'][] = 'script_command_object.reserved GUID {'.$this->BytestringToGUID($thisfile_asf_scriptcommandobject['reserved']).'} does not match expected "GETID3_ASF_Reserved_1" GUID {4B1ACBE3-100B-11D0-A39B-00A0C90348F6}'; - //return false; - break; - } - $thisfile_asf_scriptcommandobject['commands_count'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); - $offset += 2; - $thisfile_asf_scriptcommandobject['command_types_count'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); - $offset += 2; - for ($CommandTypesCounter = 0; $CommandTypesCounter < $thisfile_asf_scriptcommandobject['command_types_count']; $CommandTypesCounter++) { - $CommandTypeNameLength = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)) * 2; // 2 bytes per character - $offset += 2; - $thisfile_asf_scriptcommandobject['command_types'][$CommandTypesCounter]['name'] = substr($ASFHeaderData, $offset, $CommandTypeNameLength); - $offset += $CommandTypeNameLength; - } - for ($CommandsCounter = 0; $CommandsCounter < $thisfile_asf_scriptcommandobject['commands_count']; $CommandsCounter++) { - $thisfile_asf_scriptcommandobject['commands'][$CommandsCounter]['presentation_time'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); - $offset += 4; - $thisfile_asf_scriptcommandobject['commands'][$CommandsCounter]['type_index'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); - $offset += 2; - - $CommandTypeNameLength = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)) * 2; // 2 bytes per character - $offset += 2; - $thisfile_asf_scriptcommandobject['commands'][$CommandsCounter]['name'] = substr($ASFHeaderData, $offset, $CommandTypeNameLength); - $offset += $CommandTypeNameLength; - } - break; - - case GETID3_ASF_Marker_Object: - // Marker Object: (optional, one only) - // Field Name Field Type Size (bits) - // Object ID GUID 128 // GUID for Marker object - GETID3_ASF_Marker_Object - // Object Size QWORD 64 // size of Marker object, including 48 bytes of Marker Object header - // Reserved GUID 128 // hardcoded: 4CFEDB20-75F6-11CF-9C0F-00A0C90349CB - // Markers Count DWORD 32 // number of Marker structures in Marker Object - // Reserved WORD 16 // hardcoded: 0x0000 - // Name Length WORD 16 // number of bytes in the Name field - // Name WCHAR variable // name of the Marker Object - // Markers array of: variable // - // * Offset QWORD 64 // byte offset into Data Object - // * Presentation Time QWORD 64 // in 100-nanosecond units - // * Entry Length WORD 16 // length in bytes of (Send Time + Flags + Marker Description Length + Marker Description + Padding) - // * Send Time DWORD 32 // in milliseconds - // * Flags DWORD 32 // hardcoded: 0x00000000 - // * Marker Description Length DWORD 32 // number of bytes in Marker Description field - // * Marker Description WCHAR variable // array of Unicode characters - description of marker entry - // * Padding BYTESTREAM variable // optional padding bytes - - // shortcut - $thisfile_asf['marker_object'] = array(); - $thisfile_asf_markerobject = &$thisfile_asf['marker_object']; - - $thisfile_asf_markerobject['objectid'] = $NextObjectGUID; - $thisfile_asf_markerobject['objectid_guid'] = $NextObjectGUIDtext; - $thisfile_asf_markerobject['objectsize'] = $NextObjectSize; - $thisfile_asf_markerobject['reserved'] = substr($ASFHeaderData, $offset, 16); - $offset += 16; - $thisfile_asf_markerobject['reserved_guid'] = $this->BytestringToGUID($thisfile_asf_markerobject['reserved']); - if ($thisfile_asf_markerobject['reserved'] != $this->GUIDtoBytestring('4CFEDB20-75F6-11CF-9C0F-00A0C90349CB')) { - $ThisFileInfo['warning'][] = 'marker_object.reserved GUID {'.$this->BytestringToGUID($thisfile_asf_markerobject['reserved_1']).'} does not match expected "GETID3_ASF_Reserved_1" GUID {4CFEDB20-75F6-11CF-9C0F-00A0C90349CB}'; - break; - } - $thisfile_asf_markerobject['markers_count'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); - $offset += 4; - $thisfile_asf_markerobject['reserved_2'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); - $offset += 2; - if ($thisfile_asf_markerobject['reserved_2'] != 0) { - $ThisFileInfo['warning'][] = 'marker_object.reserved_2 ('.getid3_lib::PrintHexBytes($thisfile_asf_markerobject['reserved_2']).') does not match expected value of "0"'; - break; - } - $thisfile_asf_markerobject['name_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); - $offset += 2; - $thisfile_asf_markerobject['name'] = substr($ASFHeaderData, $offset, $thisfile_asf_markerobject['name_length']); - $offset += $thisfile_asf_markerobject['name_length']; - for ($MarkersCounter = 0; $MarkersCounter < $thisfile_asf_markerobject['markers_count']; $MarkersCounter++) { - $thisfile_asf_markerobject['markers'][$MarkersCounter]['offset'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8)); - $offset += 8; - $thisfile_asf_markerobject['markers'][$MarkersCounter]['presentation_time'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8)); - $offset += 8; - $thisfile_asf_markerobject['markers'][$MarkersCounter]['entry_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); - $offset += 2; - $thisfile_asf_markerobject['markers'][$MarkersCounter]['send_time'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); - $offset += 4; - $thisfile_asf_markerobject['markers'][$MarkersCounter]['flags'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); - $offset += 4; - $thisfile_asf_markerobject['markers'][$MarkersCounter]['marker_description_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); - $offset += 4; - $thisfile_asf_markerobject['markers'][$MarkersCounter]['marker_description'] = substr($ASFHeaderData, $offset, $thisfile_asf_markerobject['markers'][$MarkersCounter]['marker_description_length']); - $offset += $thisfile_asf_markerobject['markers'][$MarkersCounter]['marker_description_length']; - $PaddingLength = $thisfile_asf_markerobject['markers'][$MarkersCounter]['entry_length'] - 4 - 4 - 4 - $thisfile_asf_markerobject['markers'][$MarkersCounter]['marker_description_length']; - if ($PaddingLength > 0) { - $thisfile_asf_markerobject['markers'][$MarkersCounter]['padding'] = substr($ASFHeaderData, $offset, $PaddingLength); - $offset += $PaddingLength; - } - } - break; - - case GETID3_ASF_Bitrate_Mutual_Exclusion_Object: - // Bitrate Mutual Exclusion Object: (optional) - // Field Name Field Type Size (bits) - // Object ID GUID 128 // GUID for Bitrate Mutual Exclusion object - GETID3_ASF_Bitrate_Mutual_Exclusion_Object - // Object Size QWORD 64 // size of Bitrate Mutual Exclusion object, including 42 bytes of Bitrate Mutual Exclusion Object header - // Exlusion Type GUID 128 // nature of mutual exclusion relationship. one of: (GETID3_ASF_Mutex_Bitrate, GETID3_ASF_Mutex_Unknown) - // Stream Numbers Count WORD 16 // number of video streams - // Stream Numbers WORD variable // array of mutually exclusive video stream numbers. 1 <= valid <= 127 - - // shortcut - $thisfile_asf['bitrate_mutual_exclusion_object'] = array(); - $thisfile_asf_bitratemutualexclusionobject = &$thisfile_asf['bitrate_mutual_exclusion_object']; - - $thisfile_asf_bitratemutualexclusionobject['objectid'] = $NextObjectGUID; - $thisfile_asf_bitratemutualexclusionobject['objectid_guid'] = $NextObjectGUIDtext; - $thisfile_asf_bitratemutualexclusionobject['objectsize'] = $NextObjectSize; - $thisfile_asf_bitratemutualexclusionobject['reserved'] = substr($ASFHeaderData, $offset, 16); - $thisfile_asf_bitratemutualexclusionobject['reserved_guid'] = $this->BytestringToGUID($thisfile_asf_bitratemutualexclusionobject['reserved']); - $offset += 16; - if (($thisfile_asf_bitratemutualexclusionobject['reserved'] != GETID3_ASF_Mutex_Bitrate) && ($thisfile_asf_bitratemutualexclusionobject['reserved'] != GETID3_ASF_Mutex_Unknown)) { - $ThisFileInfo['warning'][] = 'bitrate_mutual_exclusion_object.reserved GUID {'.$this->BytestringToGUID($thisfile_asf_bitratemutualexclusionobject['reserved']).'} does not match expected "GETID3_ASF_Mutex_Bitrate" GUID {'.$this->BytestringToGUID(GETID3_ASF_Mutex_Bitrate).'} or "GETID3_ASF_Mutex_Unknown" GUID {'.$this->BytestringToGUID(GETID3_ASF_Mutex_Unknown).'}'; - //return false; - break; - } - $thisfile_asf_bitratemutualexclusionobject['stream_numbers_count'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); - $offset += 2; - for ($StreamNumberCounter = 0; $StreamNumberCounter < $thisfile_asf_bitratemutualexclusionobject['stream_numbers_count']; $StreamNumberCounter++) { - $thisfile_asf_bitratemutualexclusionobject['stream_numbers'][$StreamNumberCounter] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); - $offset += 2; - } - break; - - case GETID3_ASF_Error_Correction_Object: - // Error Correction Object: (optional, one only) - // Field Name Field Type Size (bits) - // Object ID GUID 128 // GUID for Error Correction object - GETID3_ASF_Error_Correction_Object - // Object Size QWORD 64 // size of Error Correction object, including 44 bytes of Error Correction Object header - // Error Correction Type GUID 128 // type of error correction. one of: (GETID3_ASF_No_Error_Correction, GETID3_ASF_Audio_Spread) - // Error Correction Data Length DWORD 32 // number of bytes in Error Correction Data field - // Error Correction Data BYTESTREAM variable // structure depends on value of Error Correction Type field - - // shortcut - $thisfile_asf['error_correction_object'] = array(); - $thisfile_asf_errorcorrectionobject = &$thisfile_asf['error_correction_object']; - - $thisfile_asf_errorcorrectionobject['objectid'] = $NextObjectGUID; - $thisfile_asf_errorcorrectionobject['objectid_guid'] = $NextObjectGUIDtext; - $thisfile_asf_errorcorrectionobject['objectsize'] = $NextObjectSize; - $thisfile_asf_errorcorrectionobject['error_correction_type'] = substr($ASFHeaderData, $offset, 16); - $offset += 16; - $thisfile_asf_errorcorrectionobject['error_correction_guid'] = $this->BytestringToGUID($thisfile_asf_errorcorrectionobject['error_correction_type']); - $thisfile_asf_errorcorrectionobject['error_correction_data_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); - $offset += 4; - switch ($thisfile_asf_errorcorrectionobject['error_correction_type']) { - case GETID3_ASF_No_Error_Correction: - // should be no data, but just in case there is, skip to the end of the field - $offset += $thisfile_asf_errorcorrectionobject['error_correction_data_length']; - break; - - case GETID3_ASF_Audio_Spread: - // Field Name Field Type Size (bits) - // Span BYTE 8 // number of packets over which audio will be spread. - // Virtual Packet Length WORD 16 // size of largest audio payload found in audio stream - // Virtual Chunk Length WORD 16 // size of largest audio payload found in audio stream - // Silence Data Length WORD 16 // number of bytes in Silence Data field - // Silence Data BYTESTREAM variable // hardcoded: 0x00 * (Silence Data Length) bytes - - $thisfile_asf_errorcorrectionobject['span'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 1)); - $offset += 1; - $thisfile_asf_errorcorrectionobject['virtual_packet_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); - $offset += 2; - $thisfile_asf_errorcorrectionobject['virtual_chunk_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); - $offset += 2; - $thisfile_asf_errorcorrectionobject['silence_data_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); - $offset += 2; - $thisfile_asf_errorcorrectionobject['silence_data'] = substr($ASFHeaderData, $offset, $thisfile_asf_errorcorrectionobject['silence_data_length']); - $offset += $thisfile_asf_errorcorrectionobject['silence_data_length']; - break; - - default: - $ThisFileInfo['warning'][] = 'error_correction_object.error_correction_type GUID {'.$this->BytestringToGUID($thisfile_asf_errorcorrectionobject['reserved']).'} does not match expected "GETID3_ASF_No_Error_Correction" GUID {'.$this->BytestringToGUID(GETID3_ASF_No_Error_Correction).'} or "GETID3_ASF_Audio_Spread" GUID {'.$this->BytestringToGUID(GETID3_ASF_Audio_Spread).'}'; - //return false; - break; - } - - break; - - case GETID3_ASF_Content_Description_Object: - // Content Description Object: (optional, one only) - // Field Name Field Type Size (bits) - // Object ID GUID 128 // GUID for Content Description object - GETID3_ASF_Content_Description_Object - // Object Size QWORD 64 // size of Content Description object, including 34 bytes of Content Description Object header - // Title Length WORD 16 // number of bytes in Title field - // Author Length WORD 16 // number of bytes in Author field - // Copyright Length WORD 16 // number of bytes in Copyright field - // Description Length WORD 16 // number of bytes in Description field - // Rating Length WORD 16 // number of bytes in Rating field - // Title WCHAR 16 // array of Unicode characters - Title - // Author WCHAR 16 // array of Unicode characters - Author - // Copyright WCHAR 16 // array of Unicode characters - Copyright - // Description WCHAR 16 // array of Unicode characters - Description - // Rating WCHAR 16 // array of Unicode characters - Rating - - // shortcut - $thisfile_asf['content_description_object'] = array(); - $thisfile_asf_contentdescriptionobject = &$thisfile_asf['content_description_object']; - - $thisfile_asf_contentdescriptionobject['objectid'] = $NextObjectGUID; - $thisfile_asf_contentdescriptionobject['objectid_guid'] = $NextObjectGUIDtext; - $thisfile_asf_contentdescriptionobject['objectsize'] = $NextObjectSize; - $thisfile_asf_contentdescriptionobject['title_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); - $offset += 2; - $thisfile_asf_contentdescriptionobject['author_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); - $offset += 2; - $thisfile_asf_contentdescriptionobject['copyright_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); - $offset += 2; - $thisfile_asf_contentdescriptionobject['description_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); - $offset += 2; - $thisfile_asf_contentdescriptionobject['rating_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); - $offset += 2; - $thisfile_asf_contentdescriptionobject['title'] = substr($ASFHeaderData, $offset, $thisfile_asf_contentdescriptionobject['title_length']); - $offset += $thisfile_asf_contentdescriptionobject['title_length']; - $thisfile_asf_contentdescriptionobject['author'] = substr($ASFHeaderData, $offset, $thisfile_asf_contentdescriptionobject['author_length']); - $offset += $thisfile_asf_contentdescriptionobject['author_length']; - $thisfile_asf_contentdescriptionobject['copyright'] = substr($ASFHeaderData, $offset, $thisfile_asf_contentdescriptionobject['copyright_length']); - $offset += $thisfile_asf_contentdescriptionobject['copyright_length']; - $thisfile_asf_contentdescriptionobject['description'] = substr($ASFHeaderData, $offset, $thisfile_asf_contentdescriptionobject['description_length']); - $offset += $thisfile_asf_contentdescriptionobject['description_length']; - $thisfile_asf_contentdescriptionobject['rating'] = substr($ASFHeaderData, $offset, $thisfile_asf_contentdescriptionobject['rating_length']); - $offset += $thisfile_asf_contentdescriptionobject['rating_length']; - - $ASFcommentKeysToCopy = array('title'=>'title', 'author'=>'artist', 'copyright'=>'copyright', 'description'=>'comment', 'rating'=>'rating'); - foreach ($ASFcommentKeysToCopy as $keytocopyfrom => $keytocopyto) { - if (!empty($thisfile_asf_contentdescriptionobject[$keytocopyfrom])) { - $thisfile_asf_comments[$keytocopyto][] = $this->TrimTerm($thisfile_asf_contentdescriptionobject[$keytocopyfrom]); - } - } - break; - - case GETID3_ASF_Extended_Content_Description_Object: - // Extended Content Description Object: (optional, one only) - // Field Name Field Type Size (bits) - // Object ID GUID 128 // GUID for Extended Content Description object - GETID3_ASF_Extended_Content_Description_Object - // Object Size QWORD 64 // size of ExtendedContent Description object, including 26 bytes of Extended Content Description Object header - // Content Descriptors Count WORD 16 // number of entries in Content Descriptors list - // Content Descriptors array of: variable // - // * Descriptor Name Length WORD 16 // size in bytes of Descriptor Name field - // * Descriptor Name WCHAR variable // array of Unicode characters - Descriptor Name - // * Descriptor Value Data Type WORD 16 // Lookup array: - // 0x0000 = Unicode String (variable length) - // 0x0001 = BYTE array (variable length) - // 0x0002 = BOOL (DWORD, 32 bits) - // 0x0003 = DWORD (DWORD, 32 bits) - // 0x0004 = QWORD (QWORD, 64 bits) - // 0x0005 = WORD (WORD, 16 bits) - // * Descriptor Value Length WORD 16 // number of bytes stored in Descriptor Value field - // * Descriptor Value variable variable // value for Content Descriptor - - // shortcut - $thisfile_asf['extended_content_description_object'] = array(); - $thisfile_asf_extendedcontentdescriptionobject = &$thisfile_asf['extended_content_description_object']; - - $thisfile_asf_extendedcontentdescriptionobject['objectid'] = $NextObjectGUID; - $thisfile_asf_extendedcontentdescriptionobject['objectid_guid'] = $NextObjectGUIDtext; - $thisfile_asf_extendedcontentdescriptionobject['objectsize'] = $NextObjectSize; - $thisfile_asf_extendedcontentdescriptionobject['content_descriptors_count'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); - $offset += 2; - for ($ExtendedContentDescriptorsCounter = 0; $ExtendedContentDescriptorsCounter < $thisfile_asf_extendedcontentdescriptionobject['content_descriptors_count']; $ExtendedContentDescriptorsCounter++) { - // shortcut - $thisfile_asf_extendedcontentdescriptionobject['content_descriptors'][$ExtendedContentDescriptorsCounter] = array(); - $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current = &$thisfile_asf_extendedcontentdescriptionobject['content_descriptors'][$ExtendedContentDescriptorsCounter]; - - $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['base_offset'] = $offset + 30; - $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['name_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); - $offset += 2; - $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['name'] = substr($ASFHeaderData, $offset, $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['name_length']); - $offset += $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['name_length']; - $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_type'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); - $offset += 2; - $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); - $offset += 2; - $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'] = substr($ASFHeaderData, $offset, $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_length']); - $offset += $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_length']; - switch ($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_type']) { - case 0x0000: // Unicode string - break; - - case 0x0001: // BYTE array - // do nothing - break; - - case 0x0002: // BOOL - $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'] = (bool) getid3_lib::LittleEndian2Int($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']); - break; - - case 0x0003: // DWORD - case 0x0004: // QWORD - case 0x0005: // WORD - $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'] = getid3_lib::LittleEndian2Int($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']); - break; - - default: - $ThisFileInfo['warning'][] = 'extended_content_description.content_descriptors.'.$ExtendedContentDescriptorsCounter.'.value_type is invalid ('.$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_type'].')'; - //return false; - break; - } - switch ($this->TrimConvert(strtolower($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['name']))) { - - case 'wm/albumartist': - case 'artist': - $thisfile_asf_comments['artist'] = array($this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'])); - break; - - case 'wm/albumtitle': - case 'album': - $thisfile_asf_comments['album'] = array($this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'])); - break; - - case 'wm/genre': - case 'genre': - $thisfile_asf_comments['genre'] = array($this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'])); - break; - - case 'wm/tracknumber': - case 'tracknumber': - $thisfile_asf_comments['track'] = array(intval($this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']))); - break; - - case 'wm/track': - if (empty($thisfile_asf_comments['track'])) { - $thisfile_asf_comments['track'] = array(1 + $this->TrimConvert($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'])); - } - break; - - case 'wm/year': - case 'year': - case 'date': - $thisfile_asf_comments['year'] = array( $this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'])); - break; - - case 'wm/lyrics': - case 'lyrics': - $thisfile_asf_comments['lyrics'] = array($this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'])); - break; - - case 'isvbr': - if ($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']) { - $thisfile_audio['bitrate_mode'] = 'vbr'; - $thisfile_video['bitrate_mode'] = 'vbr'; - } - break; - - case 'id3': - // id3v2 module might not be loaded - if (class_exists('getid3_id3v2')) { - $tempfile = tempnam('*', 'getID3'); - $tempfilehandle = fopen($tempfile, "wb"); - $tempThisfileInfo = array('encoding'=>$ThisFileInfo['encoding']); - fwrite($tempfilehandle, $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']); - fclose($tempfilehandle); - - $tempfilehandle = fopen($tempfile, "rb"); - $id3 = new getid3_id3v2($tempfilehandle, $tempThisfileInfo); - fclose($tempfilehandle); - unlink($tempfile); - - $ThisFileInfo['id3v2'] = $tempThisfileInfo['id3v2']; - } - break; - - case 'wm/encodingtime': - $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['encoding_time_unix'] = $this->FILETIMEtoUNIXtime($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']); - $thisfile_asf_comments['encoding_time_unix'] = array($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['encoding_time_unix']); - break; - - case 'wm/picture': - //typedef struct _WMPicture{ - // LPWSTR pwszMIMEType; - // BYTE bPictureType; - // LPWSTR pwszDescription; - // DWORD dwDataLen; - // BYTE* pbData; - //} WM_PICTURE; - - $wm_picture_offset = 0; - $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_type_id'] = getid3_lib::LittleEndian2Int(substr($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'], $wm_picture_offset, 1)); - $wm_picture_offset += 1; - $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_type'] = $this->WMpictureTypeLookup($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_type_id']); - $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_size'] = getid3_lib::LittleEndian2Int(substr($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'], $wm_picture_offset, 4)); - $wm_picture_offset += 4; - - $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_mime'] = ''; - do { - $next_byte_pair = substr($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'], $wm_picture_offset, 2); - $wm_picture_offset += 2; - $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_mime'] .= $next_byte_pair; - } while ($next_byte_pair !== "\x00\x00"); - - $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_description'] = ''; - do { - $next_byte_pair = substr($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'], $wm_picture_offset, 2); - $wm_picture_offset += 2; - $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_description'] .= $next_byte_pair; - } while ($next_byte_pair !== "\x00\x00"); - - $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['dataoffset'] = $wm_picture_offset; - $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['data'] = substr($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'], $wm_picture_offset); - unset($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']); - - break; - - default: - switch ($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_type']) { - case 0: // Unicode string - if (substr($this->TrimConvert($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['name']), 0, 3) == 'WM/') { - $thisfile_asf_comments[str_replace('wm/', '', strtolower($this->TrimConvert($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['name'])))] = array($this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'])); - } - break; - - case 1: - break; - } - break; - } - - } - break; - - case GETID3_ASF_Stream_Bitrate_Properties_Object: - // Stream Bitrate Properties Object: (optional, one only) - // Field Name Field Type Size (bits) - // Object ID GUID 128 // GUID for Stream Bitrate Properties object - GETID3_ASF_Stream_Bitrate_Properties_Object - // Object Size QWORD 64 // size of Extended Content Description object, including 26 bytes of Stream Bitrate Properties Object header - // Bitrate Records Count WORD 16 // number of records in Bitrate Records - // Bitrate Records array of: variable // - // * Flags WORD 16 // - // * * Stream Number bits 7 (0x007F) // number of this stream - // * * Reserved bits 9 (0xFF80) // hardcoded: 0 - // * Average Bitrate DWORD 32 // in bits per second - - // shortcut - $thisfile_asf['stream_bitrate_properties_object'] = array(); - $thisfile_asf_streambitratepropertiesobject = &$thisfile_asf['stream_bitrate_properties_object']; - - $thisfile_asf_streambitratepropertiesobject['objectid'] = $NextObjectGUID; - $thisfile_asf_streambitratepropertiesobject['objectid_guid'] = $NextObjectGUIDtext; - $thisfile_asf_streambitratepropertiesobject['objectsize'] = $NextObjectSize; - $thisfile_asf_streambitratepropertiesobject['bitrate_records_count'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); - $offset += 2; - for ($BitrateRecordsCounter = 0; $BitrateRecordsCounter < $thisfile_asf_streambitratepropertiesobject['bitrate_records_count']; $BitrateRecordsCounter++) { - $thisfile_asf_streambitratepropertiesobject['bitrate_records'][$BitrateRecordsCounter]['flags_raw'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); - $offset += 2; - $thisfile_asf_streambitratepropertiesobject['bitrate_records'][$BitrateRecordsCounter]['flags']['stream_number'] = $thisfile_asf_streambitratepropertiesobject['bitrate_records'][$BitrateRecordsCounter]['flags_raw'] & 0x007F; - $thisfile_asf_streambitratepropertiesobject['bitrate_records'][$BitrateRecordsCounter]['bitrate'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); - $offset += 4; - } - break; - - case GETID3_ASF_Padding_Object: - // Padding Object: (optional) - // Field Name Field Type Size (bits) - // Object ID GUID 128 // GUID for Padding object - GETID3_ASF_Padding_Object - // Object Size QWORD 64 // size of Padding object, including 24 bytes of ASF Padding Object header - // Padding Data BYTESTREAM variable // ignore - - // shortcut - $thisfile_asf['padding_object'] = array(); - $thisfile_asf_paddingobject = &$thisfile_asf['padding_object']; - - $thisfile_asf_paddingobject['objectid'] = $NextObjectGUID; - $thisfile_asf_paddingobject['objectid_guid'] = $NextObjectGUIDtext; - $thisfile_asf_paddingobject['objectsize'] = $NextObjectSize; - $thisfile_asf_paddingobject['padding_length'] = $thisfile_asf_paddingobject['objectsize'] - 16 - 8; - $thisfile_asf_paddingobject['padding'] = substr($ASFHeaderData, $offset, $thisfile_asf_paddingobject['padding_length']); - break; - - case GETID3_ASF_Extended_Content_Encryption_Object: - case GETID3_ASF_Content_Encryption_Object: - // WMA DRM - just ignore - $offset += ($NextObjectSize - 16 - 8); - break; - - default: - // Implementations shall ignore any standard or non-standard object that they do not know how to handle. - if ($this->GUIDname($NextObjectGUIDtext)) { - $ThisFileInfo['warning'][] = 'unhandled GUID "'.$this->GUIDname($NextObjectGUIDtext).'" {'.$NextObjectGUIDtext.'} in ASF header at offset '.($offset - 16 - 8); - } else { - $ThisFileInfo['warning'][] = 'unknown GUID {'.$NextObjectGUIDtext.'} in ASF header at offset '.($offset - 16 - 8); - } - $offset += ($NextObjectSize - 16 - 8); - break; - } - } - if (isset($thisfile_asf_streambitrateproperties['bitrate_records_count'])) { - $ASFbitrateAudio = 0; - $ASFbitrateVideo = 0; - for ($BitrateRecordsCounter = 0; $BitrateRecordsCounter < $thisfile_asf_streambitrateproperties['bitrate_records_count']; $BitrateRecordsCounter++) { - if (isset($thisfile_asf_codeclistobject['codec_entries'][$BitrateRecordsCounter])) { - switch ($thisfile_asf_codeclistobject['codec_entries'][$BitrateRecordsCounter]['type_raw']) { - case 1: - $ASFbitrateVideo += $thisfile_asf_streambitrateproperties['bitrate_records'][$BitrateRecordsCounter]['bitrate']; - break; - - case 2: - $ASFbitrateAudio += $thisfile_asf_streambitrateproperties['bitrate_records'][$BitrateRecordsCounter]['bitrate']; - break; - - default: - // do nothing - break; - } - } - } - if ($ASFbitrateAudio > 0) { - $thisfile_audio['bitrate'] = $ASFbitrateAudio; - } - if ($ASFbitrateVideo > 0) { - $thisfile_video['bitrate'] = $ASFbitrateVideo; - } - } - if (isset($thisfile_asf['stream_properties_object']) && is_array($thisfile_asf['stream_properties_object'])) { - - $thisfile_audio['bitrate'] = 0; - $thisfile_video['bitrate'] = 0; - - foreach ($thisfile_asf['stream_properties_object'] as $streamnumber => $streamdata) { - - switch ($streamdata['stream_type']) { - case GETID3_ASF_Audio_Media: - // Field Name Field Type Size (bits) - // Codec ID / Format Tag WORD 16 // unique ID of audio codec - defined as wFormatTag field of WAVEFORMATEX structure - // Number of Channels WORD 16 // number of channels of audio - defined as nChannels field of WAVEFORMATEX structure - // Samples Per Second DWORD 32 // in Hertz - defined as nSamplesPerSec field of WAVEFORMATEX structure - // Average number of Bytes/sec DWORD 32 // bytes/sec of audio stream - defined as nAvgBytesPerSec field of WAVEFORMATEX structure - // Block Alignment WORD 16 // block size in bytes of audio codec - defined as nBlockAlign field of WAVEFORMATEX structure - // Bits per sample WORD 16 // bits per sample of mono data. set to zero for variable bitrate codecs. defined as wBitsPerSample field of WAVEFORMATEX structure - // Codec Specific Data Size WORD 16 // size in bytes of Codec Specific Data buffer - defined as cbSize field of WAVEFORMATEX structure - // Codec Specific Data BYTESTREAM variable // array of codec-specific data bytes - - // shortcut - $thisfile_asf['audio_media'][$streamnumber] = array(); - $thisfile_asf_audiomedia_currentstream = &$thisfile_asf['audio_media'][$streamnumber]; - - $audiomediaoffset = 0; - - $thisfile_asf_audiomedia_currentstream = getid3_riff::RIFFparseWAVEFORMATex(substr($streamdata['type_specific_data'], $audiomediaoffset, 16)); - $audiomediaoffset += 16; - - $thisfile_audio['lossless'] = false; - switch ($thisfile_asf_audiomedia_currentstream['raw']['wFormatTag']) { - case 0x0001: // PCM - case 0x0163: // WMA9 Lossless - $thisfile_audio['lossless'] = true; - break; - } - - if (!empty($thisfile_asf['stream_bitrate_properties_object']['bitrate_records'])) { - foreach ($thisfile_asf['stream_bitrate_properties_object']['bitrate_records'] as $dummy => $dataarray) { - if (@$dataarray['flags']['stream_number'] == $streamnumber) { - $thisfile_asf_audiomedia_currentstream['bitrate'] = $dataarray['bitrate']; - $thisfile_audio['bitrate'] += $dataarray['bitrate']; - break; - } - } - } else { - if (@$thisfile_asf_audiomedia_currentstream['bytes_sec']) { - $thisfile_audio['bitrate'] += $thisfile_asf_audiomedia_currentstream['bytes_sec'] * 8; - } elseif (@$thisfile_asf_audiomedia_currentstream['bitrate']) { - $thisfile_audio['bitrate'] += $thisfile_asf_audiomedia_currentstream['bitrate']; - } - } - $thisfile_audio['streams'][$streamnumber] = $thisfile_asf_audiomedia_currentstream; - $thisfile_audio['streams'][$streamnumber]['wformattag'] = $thisfile_asf_audiomedia_currentstream['raw']['wFormatTag']; - $thisfile_audio['streams'][$streamnumber]['lossless'] = $thisfile_audio['lossless']; - $thisfile_audio['streams'][$streamnumber]['bitrate'] = $thisfile_audio['bitrate']; - unset($thisfile_audio['streams'][$streamnumber]['raw']); - - $thisfile_asf_audiomedia_currentstream['codec_data_size'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $audiomediaoffset, 2)); - $audiomediaoffset += 2; - $thisfile_asf_audiomedia_currentstream['codec_data'] = substr($streamdata['type_specific_data'], $audiomediaoffset, $thisfile_asf_audiomedia_currentstream['codec_data_size']); - $audiomediaoffset += $thisfile_asf_audiomedia_currentstream['codec_data_size']; - - break; - - case GETID3_ASF_Video_Media: - // Field Name Field Type Size (bits) - // Encoded Image Width DWORD 32 // width of image in pixels - // Encoded Image Height DWORD 32 // height of image in pixels - // Reserved Flags BYTE 8 // hardcoded: 0x02 - // Format Data Size WORD 16 // size of Format Data field in bytes - // Format Data array of: variable // - // * Format Data Size DWORD 32 // number of bytes in Format Data field, in bytes - defined as biSize field of BITMAPINFOHEADER structure - // * Image Width LONG 32 // width of encoded image in pixels - defined as biWidth field of BITMAPINFOHEADER structure - // * Image Height LONG 32 // height of encoded image in pixels - defined as biHeight field of BITMAPINFOHEADER structure - // * Reserved WORD 16 // hardcoded: 0x0001 - defined as biPlanes field of BITMAPINFOHEADER structure - // * Bits Per Pixel Count WORD 16 // bits per pixel - defined as biBitCount field of BITMAPINFOHEADER structure - // * Compression ID FOURCC 32 // fourcc of video codec - defined as biCompression field of BITMAPINFOHEADER structure - // * Image Size DWORD 32 // image size in bytes - defined as biSizeImage field of BITMAPINFOHEADER structure - // * Horizontal Pixels / Meter DWORD 32 // horizontal resolution of target device in pixels per meter - defined as biXPelsPerMeter field of BITMAPINFOHEADER structure - // * Vertical Pixels / Meter DWORD 32 // vertical resolution of target device in pixels per meter - defined as biYPelsPerMeter field of BITMAPINFOHEADER structure - // * Colors Used Count DWORD 32 // number of color indexes in the color table that are actually used - defined as biClrUsed field of BITMAPINFOHEADER structure - // * Important Colors Count DWORD 32 // number of color index required for displaying bitmap. if zero, all colors are required. defined as biClrImportant field of BITMAPINFOHEADER structure - // * Codec Specific Data BYTESTREAM variable // array of codec-specific data bytes - - // shortcut - $thisfile_asf['video_media'][$streamnumber] = array(); - $thisfile_asf_videomedia_currentstream = &$thisfile_asf['video_media'][$streamnumber]; - - $videomediaoffset = 0; - $thisfile_asf_videomedia_currentstream['image_width'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4)); - $videomediaoffset += 4; - $thisfile_asf_videomedia_currentstream['image_height'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4)); - $videomediaoffset += 4; - $thisfile_asf_videomedia_currentstream['flags'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 1)); - $videomediaoffset += 1; - $thisfile_asf_videomedia_currentstream['format_data_size'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 2)); - $videomediaoffset += 2; - $thisfile_asf_videomedia_currentstream['format_data']['format_data_size'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4)); - $videomediaoffset += 4; - $thisfile_asf_videomedia_currentstream['format_data']['image_width'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4)); - $videomediaoffset += 4; - $thisfile_asf_videomedia_currentstream['format_data']['image_height'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4)); - $videomediaoffset += 4; - $thisfile_asf_videomedia_currentstream['format_data']['reserved'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 2)); - $videomediaoffset += 2; - $thisfile_asf_videomedia_currentstream['format_data']['bits_per_pixel'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 2)); - $videomediaoffset += 2; - $thisfile_asf_videomedia_currentstream['format_data']['codec_fourcc'] = substr($streamdata['type_specific_data'], $videomediaoffset, 4); - $videomediaoffset += 4; - $thisfile_asf_videomedia_currentstream['format_data']['image_size'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4)); - $videomediaoffset += 4; - $thisfile_asf_videomedia_currentstream['format_data']['horizontal_pels'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4)); - $videomediaoffset += 4; - $thisfile_asf_videomedia_currentstream['format_data']['vertical_pels'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4)); - $videomediaoffset += 4; - $thisfile_asf_videomedia_currentstream['format_data']['colors_used'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4)); - $videomediaoffset += 4; - $thisfile_asf_videomedia_currentstream['format_data']['colors_important'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4)); - $videomediaoffset += 4; - $thisfile_asf_videomedia_currentstream['format_data']['codec_data'] = substr($streamdata['type_specific_data'], $videomediaoffset); - - if (!empty($thisfile_asf['stream_bitrate_properties_object']['bitrate_records'])) { - foreach ($thisfile_asf['stream_bitrate_properties_object']['bitrate_records'] as $dummy => $dataarray) { - if (@$dataarray['flags']['stream_number'] == $streamnumber) { - $thisfile_asf_videomedia_currentstream['bitrate'] = $dataarray['bitrate']; - $thisfile_video['streams'][$streamnumber]['bitrate'] = $dataarray['bitrate']; - $thisfile_video['bitrate'] += $dataarray['bitrate']; - break; - } - } - } - - $thisfile_asf_videomedia_currentstream['format_data']['codec'] = getid3_riff::RIFFfourccLookup($thisfile_asf_videomedia_currentstream['format_data']['codec_fourcc']); - - $thisfile_video['streams'][$streamnumber]['fourcc'] = $thisfile_asf_videomedia_currentstream['format_data']['codec_fourcc']; - $thisfile_video['streams'][$streamnumber]['codec'] = $thisfile_asf_videomedia_currentstream['format_data']['codec']; - $thisfile_video['streams'][$streamnumber]['resolution_x'] = $thisfile_asf_videomedia_currentstream['image_width']; - $thisfile_video['streams'][$streamnumber]['resolution_y'] = $thisfile_asf_videomedia_currentstream['image_height']; - $thisfile_video['streams'][$streamnumber]['bits_per_sample'] = $thisfile_asf_videomedia_currentstream['format_data']['bits_per_pixel']; - break; - - default: - break; - } - } - } - - while (ftell($fd) < $ThisFileInfo['avdataend']) { - $NextObjectDataHeader = fread($fd, 24); - $offset = 0; - $NextObjectGUID = substr($NextObjectDataHeader, 0, 16); - $offset += 16; - $NextObjectGUIDtext = $this->BytestringToGUID($NextObjectGUID); - $NextObjectSize = getid3_lib::LittleEndian2Int(substr($NextObjectDataHeader, $offset, 8)); - $offset += 8; - - switch ($NextObjectGUID) { - case GETID3_ASF_Data_Object: - // Data Object: (mandatory, one only) - // Field Name Field Type Size (bits) - // Object ID GUID 128 // GUID for Data object - GETID3_ASF_Data_Object - // Object Size QWORD 64 // size of Data object, including 50 bytes of Data Object header. may be 0 if FilePropertiesObject.BroadcastFlag == 1 - // File ID GUID 128 // unique identifier. identical to File ID field in Header Object - // Total Data Packets QWORD 64 // number of Data Packet entries in Data Object. invalid if FilePropertiesObject.BroadcastFlag == 1 - // Reserved WORD 16 // hardcoded: 0x0101 - - // shortcut - $thisfile_asf['data_object'] = array(); - $thisfile_asf_dataobject = &$thisfile_asf['data_object']; - - $DataObjectData = $NextObjectDataHeader.fread($fd, 50 - 24); - $offset = 24; - - $thisfile_asf_dataobject['objectid'] = $NextObjectGUID; - $thisfile_asf_dataobject['objectid_guid'] = $NextObjectGUIDtext; - $thisfile_asf_dataobject['objectsize'] = $NextObjectSize; - - $thisfile_asf_dataobject['fileid'] = substr($DataObjectData, $offset, 16); - $offset += 16; - $thisfile_asf_dataobject['fileid_guid'] = $this->BytestringToGUID($thisfile_asf_dataobject['fileid']); - $thisfile_asf_dataobject['total_data_packets'] = getid3_lib::LittleEndian2Int(substr($DataObjectData, $offset, 8)); - $offset += 8; - $thisfile_asf_dataobject['reserved'] = getid3_lib::LittleEndian2Int(substr($DataObjectData, $offset, 2)); - $offset += 2; - if ($thisfile_asf_dataobject['reserved'] != 0x0101) { - $ThisFileInfo['warning'][] = 'data_object.reserved ('.getid3_lib::PrintHexBytes($thisfile_asf_dataobject['reserved']).') does not match expected value of "0x0101"'; - //return false; - break; - } - - // Data Packets array of: variable // - // * Error Correction Flags BYTE 8 // - // * * Error Correction Data Length bits 4 // if Error Correction Length Type == 00, size of Error Correction Data in bytes, else hardcoded: 0000 - // * * Opaque Data Present bits 1 // - // * * Error Correction Length Type bits 2 // number of bits for size of the error correction data. hardcoded: 00 - // * * Error Correction Present bits 1 // If set, use Opaque Data Packet structure, else use Payload structure - // * Error Correction Data - - $ThisFileInfo['avdataoffset'] = ftell($fd); - fseek($fd, ($thisfile_asf_dataobject['objectsize'] - 50), SEEK_CUR); // skip actual audio/video data - $ThisFileInfo['avdataend'] = ftell($fd); - break; - - case GETID3_ASF_Simple_Index_Object: - // Simple Index Object: (optional, recommended, one per video stream) - // Field Name Field Type Size (bits) - // Object ID GUID 128 // GUID for Simple Index object - GETID3_ASF_Data_Object - // Object Size QWORD 64 // size of Simple Index object, including 56 bytes of Simple Index Object header - // File ID GUID 128 // unique identifier. may be zero or identical to File ID field in Data Object and Header Object - // Index Entry Time Interval QWORD 64 // interval between index entries in 100-nanosecond units - // Maximum Packet Count DWORD 32 // maximum packet count for all index entries - // Index Entries Count DWORD 32 // number of Index Entries structures - // Index Entries array of: variable // - // * Packet Number DWORD 32 // number of the Data Packet associated with this index entry - // * Packet Count WORD 16 // number of Data Packets to sent at this index entry - - // shortcut - $thisfile_asf['simple_index_object'] = array(); - $thisfile_asf_simpleindexobject = &$thisfile_asf['simple_index_object']; - - $SimpleIndexObjectData = $NextObjectDataHeader.fread($fd, 56 - 24); - $offset = 24; - - $thisfile_asf_simpleindexobject['objectid'] = $NextObjectGUID; - $thisfile_asf_simpleindexobject['objectid_guid'] = $NextObjectGUIDtext; - $thisfile_asf_simpleindexobject['objectsize'] = $NextObjectSize; - - $thisfile_asf_simpleindexobject['fileid'] = substr($SimpleIndexObjectData, $offset, 16); - $offset += 16; - $thisfile_asf_simpleindexobject['fileid_guid'] = $this->BytestringToGUID($thisfile_asf_simpleindexobject['fileid']); - $thisfile_asf_simpleindexobject['index_entry_time_interval'] = getid3_lib::LittleEndian2Int(substr($SimpleIndexObjectData, $offset, 8)); - $offset += 8; - $thisfile_asf_simpleindexobject['maximum_packet_count'] = getid3_lib::LittleEndian2Int(substr($SimpleIndexObjectData, $offset, 4)); - $offset += 4; - $thisfile_asf_simpleindexobject['index_entries_count'] = getid3_lib::LittleEndian2Int(substr($SimpleIndexObjectData, $offset, 4)); - $offset += 4; - - $IndexEntriesData = $SimpleIndexObjectData.fread($fd, 6 * $thisfile_asf_simpleindexobject['index_entries_count']); - for ($IndexEntriesCounter = 0; $IndexEntriesCounter < $thisfile_asf_simpleindexobject['index_entries_count']; $IndexEntriesCounter++) { - $thisfile_asf_simpleindexobject['index_entries'][$IndexEntriesCounter]['packet_number'] = getid3_lib::LittleEndian2Int(substr($IndexEntriesData, $offset, 4)); - $offset += 4; - $thisfile_asf_simpleindexobject['index_entries'][$IndexEntriesCounter]['packet_count'] = getid3_lib::LittleEndian2Int(substr($IndexEntriesData, $offset, 4)); - $offset += 2; - } - - break; - - case GETID3_ASF_Index_Object: - // 6.2 ASF top-level Index Object (optional but recommended when appropriate, 0 or 1) - // Field Name Field Type Size (bits) - // Object ID GUID 128 // GUID for the Index Object - GETID3_ASF_Index_Object - // Object Size QWORD 64 // Specifies the size, in bytes, of the Index Object, including at least 34 bytes of Index Object header - // Index Entry Time Interval DWORD 32 // Specifies the time interval between each index entry in ms. - // Index Specifiers Count WORD 16 // Specifies the number of Index Specifiers structures in this Index Object. - // Index Blocks Count DWORD 32 // Specifies the number of Index Blocks structures in this Index Object. - - // Index Entry Time Interval DWORD 32 // Specifies the time interval between index entries in milliseconds. This value cannot be 0. - // Index Specifiers Count WORD 16 // Specifies the number of entries in the Index Specifiers list. Valid values are 1 and greater. - // Index Specifiers array of: varies // - // * Stream Number WORD 16 // Specifies the stream number that the Index Specifiers refer to. Valid values are between 1 and 127. - // * Index Type WORD 16 // Specifies Index Type values as follows: - // 1 = Nearest Past Data Packet - indexes point to the data packet whose presentation time is closest to the index entry time. - // 2 = Nearest Past Media Object - indexes point to the closest data packet containing an entire object or first fragment of an object. - // 3 = Nearest Past Cleanpoint. - indexes point to the closest data packet containing an entire object (or first fragment of an object) that has the Cleanpoint Flag set. - // Nearest Past Cleanpoint is the most common type of index. - // Index Entry Count DWORD 32 // Specifies the number of Index Entries in the block. - // * Block Positions QWORD varies // Specifies a list of byte offsets of the beginnings of the blocks relative to the beginning of the first Data Packet (i.e., the beginning of the Data Object + 50 bytes). The number of entries in this list is specified by the value of the Index Specifiers Count field. The order of those byte offsets is tied to the order in which Index Specifiers are listed. - // * Index Entries array of: varies // - // * * Offsets DWORD varies // An offset value of 0xffffffff indicates an invalid offset value - - // shortcut - $thisfile_asf['asf_index_object'] = array(); - $thisfile_asf_asfindexobject = &$thisfile_asf['asf_index_object']; - - $ASFIndexObjectData = $NextObjectDataHeader.fread($fd, 34 - 24); - $offset = 24; - - $thisfile_asf_asfindexobject['objectid'] = $NextObjectGUID; - $thisfile_asf_asfindexobject['objectid_guid'] = $NextObjectGUIDtext; - $thisfile_asf_asfindexobject['objectsize'] = $NextObjectSize; - - $thisfile_asf_asfindexobject['entry_time_interval'] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 4)); - $offset += 4; - $thisfile_asf_asfindexobject['index_specifiers_count'] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 2)); - $offset += 2; - $thisfile_asf_asfindexobject['index_blocks_count'] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 4)); - $offset += 4; - - $ASFIndexObjectData .= fread($fd, 4 * $thisfile_asf_asfindexobject['index_specifiers_count']); - for ($IndexSpecifiersCounter = 0; $IndexSpecifiersCounter < $thisfile_asf_asfindexobject['index_specifiers_count']; $IndexSpecifiersCounter++) { - $IndexSpecifierStreamNumber = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 2)); - $offset += 2; - $thisfile_asf_asfindexobject['index_specifiers'][$IndexSpecifiersCounter]['stream_number'] = $IndexSpecifierStreamNumber; - $thisfile_asf_asfindexobject['index_specifiers'][$IndexSpecifiersCounter]['index_type'] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 2)); - $offset += 2; - $thisfile_asf_asfindexobject['index_specifiers'][$IndexSpecifiersCounter]['index_type_text'] = $this->ASFIndexObjectIndexTypeLookup($thisfile_asf_asfindexobject['index_specifiers'][$IndexSpecifiersCounter]['index_type']); - } - - $ASFIndexObjectData .= fread($fd, 4); - $thisfile_asf_asfindexobject['index_entry_count'] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 4)); - $offset += 4; - - $ASFIndexObjectData .= fread($fd, 8 * $thisfile_asf_asfindexobject['index_specifiers_count']); - for ($IndexSpecifiersCounter = 0; $IndexSpecifiersCounter < $thisfile_asf_asfindexobject['index_specifiers_count']; $IndexSpecifiersCounter++) { - $thisfile_asf_asfindexobject['block_positions'][$IndexSpecifiersCounter] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 8)); - $offset += 8; - } - - $ASFIndexObjectData .= fread($fd, 4 * $thisfile_asf_asfindexobject['index_specifiers_count'] * $thisfile_asf_asfindexobject['index_entry_count']); - for ($IndexEntryCounter = 0; $IndexEntryCounter < $thisfile_asf_asfindexobject['index_entry_count']; $IndexEntryCounter++) { - for ($IndexSpecifiersCounter = 0; $IndexSpecifiersCounter < $thisfile_asf_asfindexobject['index_specifiers_count']; $IndexSpecifiersCounter++) { - $thisfile_asf_asfindexobject['offsets'][$IndexSpecifiersCounter][$IndexEntryCounter] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 4)); - $offset += 4; - } - } - break; - - - default: - // Implementations shall ignore any standard or non-standard object that they do not know how to handle. - if ($this->GUIDname($NextObjectGUIDtext)) { - $ThisFileInfo['warning'][] = 'unhandled GUID "'.$this->GUIDname($NextObjectGUIDtext).'" {'.$NextObjectGUIDtext.'} in ASF body at offset '.($offset - 16 - 8); - } else { - $ThisFileInfo['warning'][] = 'unknown GUID {'.$NextObjectGUIDtext.'} in ASF body at offset '.(ftell($fd) - 16 - 8); - } - fseek($fd, ($NextObjectSize - 16 - 8), SEEK_CUR); - break; - } - } - - if (isset($thisfile_asf_codeclistobject['codec_entries']) && is_array($thisfile_asf_codeclistobject['codec_entries'])) { - foreach ($thisfile_asf_codeclistobject['codec_entries'] as $streamnumber => $streamdata) { - switch ($streamdata['information']) { - case 'WMV1': - case 'WMV2': - case 'WMV3': - $thisfile_video['dataformat'] = 'wmv'; - $ThisFileInfo['mime_type'] = 'video/x-ms-wmv'; - break; - - case 'MP42': - case 'MP43': - case 'MP4S': - case 'mp4s': - $thisfile_video['dataformat'] = 'asf'; - $ThisFileInfo['mime_type'] = 'video/x-ms-asf'; - break; - - default: - switch ($streamdata['type_raw']) { - case 1: - if (strstr($this->TrimConvert($streamdata['name']), 'Windows Media')) { - $thisfile_video['dataformat'] = 'wmv'; - if ($ThisFileInfo['mime_type'] == 'video/x-ms-asf') { - $ThisFileInfo['mime_type'] = 'video/x-ms-wmv'; - } - } - break; - - case 2: - if (strstr($this->TrimConvert($streamdata['name']), 'Windows Media')) { - $thisfile_audio['dataformat'] = 'wma'; - if ($ThisFileInfo['mime_type'] == 'video/x-ms-asf') { - $ThisFileInfo['mime_type'] = 'audio/x-ms-wma'; - } - } - break; - - } - break; - } - } - } - - switch (@$thisfile_audio['codec']) { - case 'MPEG Layer-3': - $thisfile_audio['dataformat'] = 'mp3'; - break; - - default: - break; - } - - if (isset($thisfile_asf_codeclistobject['codec_entries'])) { - foreach ($thisfile_asf_codeclistobject['codec_entries'] as $streamnumber => $streamdata) { - switch ($streamdata['type_raw']) { - - case 1: // video - $thisfile_video['encoder'] = $this->TrimConvert($thisfile_asf_codeclistobject['codec_entries'][$streamnumber]['name']); - break; - - case 2: // audio - $thisfile_audio['encoder'] = $this->TrimConvert($thisfile_asf_codeclistobject['codec_entries'][$streamnumber]['name']); - - // AH 2003-10-01 - $thisfile_audio['encoder_options'] = $this->TrimConvert($thisfile_asf_codeclistobject['codec_entries'][0]['description']); - - $thisfile_audio['codec'] = $thisfile_audio['encoder']; - break; - - default: - $ThisFileInfo['warning'][] = 'Unknown streamtype: [codec_list_object][codec_entries]['.$streamnumber.'][type_raw] == '.$streamdata['type_raw']; - break; - - } - } - } - - if (isset($ThisFileInfo['audio'])) { - $thisfile_audio['lossless'] = (isset($thisfile_audio['lossless']) ? $thisfile_audio['lossless'] : false); - $thisfile_audio['dataformat'] = (!empty($thisfile_audio['dataformat']) ? $thisfile_audio['dataformat'] : 'asf'); - } - if (!empty($thisfile_video['dataformat'])) { - $thisfile_video['lossless'] = (isset($thisfile_audio['lossless']) ? $thisfile_audio['lossless'] : false); - $thisfile_video['pixel_aspect_ratio'] = (isset($thisfile_audio['pixel_aspect_ratio']) ? $thisfile_audio['pixel_aspect_ratio'] : (float) 1); - $thisfile_video['dataformat'] = (!empty($thisfile_video['dataformat']) ? $thisfile_video['dataformat'] : 'asf'); - } - $ThisFileInfo['bitrate'] = @$thisfile_audio['bitrate'] + @$thisfile_video['bitrate']; - return true; - } - - function ASFCodecListObjectTypeLookup($CodecListType) { - static $ASFCodecListObjectTypeLookup = array(); - if (empty($ASFCodecListObjectTypeLookup)) { - $ASFCodecListObjectTypeLookup[0x0001] = 'Video Codec'; - $ASFCodecListObjectTypeLookup[0x0002] = 'Audio Codec'; - $ASFCodecListObjectTypeLookup[0xFFFF] = 'Unknown Codec'; - } - - return (isset($ASFCodecListObjectTypeLookup[$CodecListType]) ? $ASFCodecListObjectTypeLookup[$CodecListType] : 'Invalid Codec Type'); - } - - function KnownGUIDs() { - static $GUIDarray = array(); - if (empty($GUIDarray)) { - $GUIDarray['GETID3_ASF_Extended_Stream_Properties_Object'] = '14E6A5CB-C672-4332-8399-A96952065B5A'; - $GUIDarray['GETID3_ASF_Padding_Object'] = '1806D474-CADF-4509-A4BA-9AABCB96AAE8'; - $GUIDarray['GETID3_ASF_Payload_Ext_Syst_Pixel_Aspect_Ratio'] = '1B1EE554-F9EA-4BC8-821A-376B74E4C4B8'; - $GUIDarray['GETID3_ASF_Script_Command_Object'] = '1EFB1A30-0B62-11D0-A39B-00A0C90348F6'; - $GUIDarray['GETID3_ASF_No_Error_Correction'] = '20FB5700-5B55-11CF-A8FD-00805F5C442B'; - $GUIDarray['GETID3_ASF_Content_Branding_Object'] = '2211B3FA-BD23-11D2-B4B7-00A0C955FC6E'; - $GUIDarray['GETID3_ASF_Content_Encryption_Object'] = '2211B3FB-BD23-11D2-B4B7-00A0C955FC6E'; - $GUIDarray['GETID3_ASF_Digital_Signature_Object'] = '2211B3FC-BD23-11D2-B4B7-00A0C955FC6E'; - $GUIDarray['GETID3_ASF_Extended_Content_Encryption_Object'] = '298AE614-2622-4C17-B935-DAE07EE9289C'; - $GUIDarray['GETID3_ASF_Simple_Index_Object'] = '33000890-E5B1-11CF-89F4-00A0C90349CB'; - $GUIDarray['GETID3_ASF_Degradable_JPEG_Media'] = '35907DE0-E415-11CF-A917-00805F5C442B'; - $GUIDarray['GETID3_ASF_Payload_Extension_System_Timecode'] = '399595EC-8667-4E2D-8FDB-98814CE76C1E'; - $GUIDarray['GETID3_ASF_Binary_Media'] = '3AFB65E2-47EF-40F2-AC2C-70A90D71D343'; - $GUIDarray['GETID3_ASF_Timecode_Index_Object'] = '3CB73FD0-0C4A-4803-953D-EDF7B6228F0C'; - $GUIDarray['GETID3_ASF_Metadata_Library_Object'] = '44231C94-9498-49D1-A141-1D134E457054'; - $GUIDarray['GETID3_ASF_Reserved_3'] = '4B1ACBE3-100B-11D0-A39B-00A0C90348F6'; - $GUIDarray['GETID3_ASF_Reserved_4'] = '4CFEDB20-75F6-11CF-9C0F-00A0C90349CB'; - $GUIDarray['GETID3_ASF_Command_Media'] = '59DACFC0-59E6-11D0-A3AC-00A0C90348F6'; - $GUIDarray['GETID3_ASF_Header_Extension_Object'] = '5FBF03B5-A92E-11CF-8EE3-00C00C205365'; - $GUIDarray['GETID3_ASF_Media_Object_Index_Parameters_Obj'] = '6B203BAD-3F11-4E84-ACA8-D7613DE2CFA7'; - $GUIDarray['GETID3_ASF_Header_Object'] = '75B22630-668E-11CF-A6D9-00AA0062CE6C'; - $GUIDarray['GETID3_ASF_Content_Description_Object'] = '75B22633-668E-11CF-A6D9-00AA0062CE6C'; - $GUIDarray['GETID3_ASF_Error_Correction_Object'] = '75B22635-668E-11CF-A6D9-00AA0062CE6C'; - $GUIDarray['GETID3_ASF_Data_Object'] = '75B22636-668E-11CF-A6D9-00AA0062CE6C'; - $GUIDarray['GETID3_ASF_Web_Stream_Media_Subtype'] = '776257D4-C627-41CB-8F81-7AC7FF1C40CC'; - $GUIDarray['GETID3_ASF_Stream_Bitrate_Properties_Object'] = '7BF875CE-468D-11D1-8D82-006097C9A2B2'; - $GUIDarray['GETID3_ASF_Language_List_Object'] = '7C4346A9-EFE0-4BFC-B229-393EDE415C85'; - $GUIDarray['GETID3_ASF_Codec_List_Object'] = '86D15240-311D-11D0-A3A4-00A0C90348F6'; - $GUIDarray['GETID3_ASF_Reserved_2'] = '86D15241-311D-11D0-A3A4-00A0C90348F6'; - $GUIDarray['GETID3_ASF_File_Properties_Object'] = '8CABDCA1-A947-11CF-8EE4-00C00C205365'; - $GUIDarray['GETID3_ASF_File_Transfer_Media'] = '91BD222C-F21C-497A-8B6D-5AA86BFC0185'; - $GUIDarray['GETID3_ASF_Old_RTP_Extension_Data'] = '96800C63-4C94-11D1-837B-0080C7A37F95'; - $GUIDarray['GETID3_ASF_Advanced_Mutual_Exclusion_Object'] = 'A08649CF-4775-4670-8A16-6E35357566CD'; - $GUIDarray['GETID3_ASF_Bandwidth_Sharing_Object'] = 'A69609E6-517B-11D2-B6AF-00C04FD908E9'; - $GUIDarray['GETID3_ASF_Reserved_1'] = 'ABD3D211-A9BA-11cf-8EE6-00C00C205365'; - $GUIDarray['GETID3_ASF_Bandwidth_Sharing_Exclusive'] = 'AF6060AA-5197-11D2-B6AF-00C04FD908E9'; - $GUIDarray['GETID3_ASF_Bandwidth_Sharing_Partial'] = 'AF6060AB-5197-11D2-B6AF-00C04FD908E9'; - $GUIDarray['GETID3_ASF_JFIF_Media'] = 'B61BE100-5B4E-11CF-A8FD-00805F5C442B'; - $GUIDarray['GETID3_ASF_Stream_Properties_Object'] = 'B7DC0791-A9B7-11CF-8EE6-00C00C205365'; - $GUIDarray['GETID3_ASF_Video_Media'] = 'BC19EFC0-5B4D-11CF-A8FD-00805F5C442B'; - $GUIDarray['GETID3_ASF_Audio_Spread'] = 'BFC3CD50-618F-11CF-8BB2-00AA00B4E220'; - $GUIDarray['GETID3_ASF_Metadata_Object'] = 'C5F8CBEA-5BAF-4877-8467-AA8C44FA4CCA'; - $GUIDarray['GETID3_ASF_Payload_Ext_Syst_Sample_Duration'] = 'C6BD9450-867F-4907-83A3-C77921B733AD'; - $GUIDarray['GETID3_ASF_Group_Mutual_Exclusion_Object'] = 'D1465A40-5A79-4338-B71B-E36B8FD6C249'; - $GUIDarray['GETID3_ASF_Extended_Content_Description_Object'] = 'D2D0A440-E307-11D2-97F0-00A0C95EA850'; - $GUIDarray['GETID3_ASF_Stream_Prioritization_Object'] = 'D4FED15B-88D3-454F-81F0-ED5C45999E24'; - $GUIDarray['GETID3_ASF_Payload_Ext_System_Content_Type'] = 'D590DC20-07BC-436C-9CF7-F3BBFBF1A4DC'; - $GUIDarray['GETID3_ASF_Old_File_Properties_Object'] = 'D6E229D0-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_ASF_Header_Object'] = 'D6E229D1-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_ASF_Data_Object'] = 'D6E229D2-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Index_Object'] = 'D6E229D3-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Stream_Properties_Object'] = 'D6E229D4-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Content_Description_Object'] = 'D6E229D5-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Script_Command_Object'] = 'D6E229D6-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Marker_Object'] = 'D6E229D7-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Component_Download_Object'] = 'D6E229D8-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Stream_Group_Object'] = 'D6E229D9-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Scalable_Object'] = 'D6E229DA-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Prioritization_Object'] = 'D6E229DB-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Bitrate_Mutual_Exclusion_Object'] = 'D6E229DC-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Inter_Media_Dependency_Object'] = 'D6E229DD-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Rating_Object'] = 'D6E229DE-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Index_Parameters_Object'] = 'D6E229DF-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Color_Table_Object'] = 'D6E229E0-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Language_List_Object'] = 'D6E229E1-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Audio_Media'] = 'D6E229E2-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Video_Media'] = 'D6E229E3-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Image_Media'] = 'D6E229E4-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Timecode_Media'] = 'D6E229E5-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Text_Media'] = 'D6E229E6-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_MIDI_Media'] = 'D6E229E7-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Command_Media'] = 'D6E229E8-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_No_Error_Concealment'] = 'D6E229EA-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Scrambled_Audio'] = 'D6E229EB-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_No_Color_Table'] = 'D6E229EC-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_SMPTE_Time'] = 'D6E229ED-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_ASCII_Text'] = 'D6E229EE-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Unicode_Text'] = 'D6E229EF-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_HTML_Text'] = 'D6E229F0-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_URL_Command'] = 'D6E229F1-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Filename_Command'] = 'D6E229F2-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_ACM_Codec'] = 'D6E229F3-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_VCM_Codec'] = 'D6E229F4-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_QuickTime_Codec'] = 'D6E229F5-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_DirectShow_Transform_Filter'] = 'D6E229F6-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_DirectShow_Rendering_Filter'] = 'D6E229F7-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_No_Enhancement'] = 'D6E229F8-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Unknown_Enhancement_Type'] = 'D6E229F9-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Temporal_Enhancement'] = 'D6E229FA-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Spatial_Enhancement'] = 'D6E229FB-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Quality_Enhancement'] = 'D6E229FC-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Number_of_Channels_Enhancement'] = 'D6E229FD-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Frequency_Response_Enhancement'] = 'D6E229FE-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Media_Object'] = 'D6E229FF-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Mutex_Language'] = 'D6E22A00-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Mutex_Bitrate'] = 'D6E22A01-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Mutex_Unknown'] = 'D6E22A02-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_ASF_Placeholder_Object'] = 'D6E22A0E-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Old_Data_Unit_Extension_Object'] = 'D6E22A0F-35DA-11D1-9034-00A0C90349BE'; - $GUIDarray['GETID3_ASF_Web_Stream_Format'] = 'DA1E6B13-8359-4050-B398-388E965BF00C'; - $GUIDarray['GETID3_ASF_Payload_Ext_System_File_Name'] = 'E165EC0E-19ED-45D7-B4A7-25CBD1E28E9B'; - $GUIDarray['GETID3_ASF_Marker_Object'] = 'F487CD01-A951-11CF-8EE6-00C00C205365'; - $GUIDarray['GETID3_ASF_Timecode_Index_Parameters_Object'] = 'F55E496D-9797-4B5D-8C8B-604DFE9BFB24'; - $GUIDarray['GETID3_ASF_Audio_Media'] = 'F8699E40-5B4D-11CF-A8FD-00805F5C442B'; - $GUIDarray['GETID3_ASF_Media_Object_Index_Object'] = 'FEB103F8-12AD-4C64-840F-2A1D2F7AD48C'; - $GUIDarray['GETID3_ASF_Alt_Extended_Content_Encryption_Obj'] = 'FF889EF1-ADEE-40DA-9E71-98704BB928CE'; - } - return $GUIDarray; - } - - function GUIDname($GUIDstring) { - static $GUIDarray = array(); - if (empty($GUIDarray)) { - $GUIDarray = $this->KnownGUIDs(); - } - return array_search($GUIDstring, $GUIDarray); - } - - function ASFIndexObjectIndexTypeLookup($id) { - static $ASFIndexObjectIndexTypeLookup = array(); - if (empty($ASFIndexObjectIndexTypeLookup)) { - $ASFIndexObjectIndexTypeLookup[1] = 'Nearest Past Data Packet'; - $ASFIndexObjectIndexTypeLookup[2] = 'Nearest Past Media Object'; - $ASFIndexObjectIndexTypeLookup[3] = 'Nearest Past Cleanpoint'; - } - return (isset($ASFIndexObjectIndexTypeLookup[$id]) ? $ASFIndexObjectIndexTypeLookup[$id] : 'invalid'); - } - - function GUIDtoBytestring($GUIDstring) { - // Microsoft defines these 16-byte (128-bit) GUIDs in the strangest way: - // first 4 bytes are in little-endian order - // next 2 bytes are appended in little-endian order - // next 2 bytes are appended in little-endian order - // next 2 bytes are appended in big-endian order - // next 6 bytes are appended in big-endian order - - // AaBbCcDd-EeFf-GgHh-IiJj-KkLlMmNnOoPp is stored as this 16-byte string: - // $Dd $Cc $Bb $Aa $Ff $Ee $Hh $Gg $Ii $Jj $Kk $Ll $Mm $Nn $Oo $Pp - - $hexbytecharstring = chr(hexdec(substr($GUIDstring, 6, 2))); - $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 4, 2))); - $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 2, 2))); - $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 0, 2))); - - $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 11, 2))); - $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 9, 2))); - - $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 16, 2))); - $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 14, 2))); - - $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 19, 2))); - $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 21, 2))); - - $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 24, 2))); - $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 26, 2))); - $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 28, 2))); - $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 30, 2))); - $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 32, 2))); - $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 34, 2))); - - return $hexbytecharstring; - } - - function BytestringToGUID($Bytestring) { - $GUIDstring = str_pad(dechex(ord($Bytestring{3})), 2, '0', STR_PAD_LEFT); - $GUIDstring .= str_pad(dechex(ord($Bytestring{2})), 2, '0', STR_PAD_LEFT); - $GUIDstring .= str_pad(dechex(ord($Bytestring{1})), 2, '0', STR_PAD_LEFT); - $GUIDstring .= str_pad(dechex(ord($Bytestring{0})), 2, '0', STR_PAD_LEFT); - $GUIDstring .= '-'; - $GUIDstring .= str_pad(dechex(ord($Bytestring{5})), 2, '0', STR_PAD_LEFT); - $GUIDstring .= str_pad(dechex(ord($Bytestring{4})), 2, '0', STR_PAD_LEFT); - $GUIDstring .= '-'; - $GUIDstring .= str_pad(dechex(ord($Bytestring{7})), 2, '0', STR_PAD_LEFT); - $GUIDstring .= str_pad(dechex(ord($Bytestring{6})), 2, '0', STR_PAD_LEFT); - $GUIDstring .= '-'; - $GUIDstring .= str_pad(dechex(ord($Bytestring{8})), 2, '0', STR_PAD_LEFT); - $GUIDstring .= str_pad(dechex(ord($Bytestring{9})), 2, '0', STR_PAD_LEFT); - $GUIDstring .= '-'; - $GUIDstring .= str_pad(dechex(ord($Bytestring{10})), 2, '0', STR_PAD_LEFT); - $GUIDstring .= str_pad(dechex(ord($Bytestring{11})), 2, '0', STR_PAD_LEFT); - $GUIDstring .= str_pad(dechex(ord($Bytestring{12})), 2, '0', STR_PAD_LEFT); - $GUIDstring .= str_pad(dechex(ord($Bytestring{13})), 2, '0', STR_PAD_LEFT); - $GUIDstring .= str_pad(dechex(ord($Bytestring{14})), 2, '0', STR_PAD_LEFT); - $GUIDstring .= str_pad(dechex(ord($Bytestring{15})), 2, '0', STR_PAD_LEFT); - - return strtoupper($GUIDstring); - } - - function FILETIMEtoUNIXtime($FILETIME, $round=true) { - // FILETIME is a 64-bit unsigned integer representing - // the number of 100-nanosecond intervals since January 1, 1601 - // UNIX timestamp is number of seconds since January 1, 1970 - // 116444736000000000 = 10000000 * 60 * 60 * 24 * 365 * 369 + 89 leap days - if ($round) { - return intval(round(($FILETIME - 116444736000000000) / 10000000)); - } - return ($FILETIME - 116444736000000000) / 10000000; - } - - function WMpictureTypeLookup($WMpictureType) { - static $WMpictureTypeLookup = array(); - if (empty($WMpictureTypeLookup)) { - $WMpictureTypeLookup[0x03] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Front Cover'); - $WMpictureTypeLookup[0x04] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Back Cover'); - $WMpictureTypeLookup[0x00] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'User Defined'); - $WMpictureTypeLookup[0x05] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Leaflet Page'); - $WMpictureTypeLookup[0x06] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Media Label'); - $WMpictureTypeLookup[0x07] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Lead Artist'); - $WMpictureTypeLookup[0x08] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Artist'); - $WMpictureTypeLookup[0x09] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Conductor'); - $WMpictureTypeLookup[0x0A] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Band'); - $WMpictureTypeLookup[0x0B] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Composer'); - $WMpictureTypeLookup[0x0C] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Lyricist'); - $WMpictureTypeLookup[0x0D] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Recording Location'); - $WMpictureTypeLookup[0x0E] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'During Recording'); - $WMpictureTypeLookup[0x0F] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'During Performance'); - $WMpictureTypeLookup[0x10] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Video Screen Capture'); - $WMpictureTypeLookup[0x12] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Illustration'); - $WMpictureTypeLookup[0x13] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Band Logotype'); - $WMpictureTypeLookup[0x14] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Publisher Logotype'); - } - return @$WMpictureTypeLookup[$WMpictureType]; - } - - - // Remove terminator 00 00 and convert UNICODE to Latin-1 - function TrimConvert($string) { - - // remove terminator, only if present (it should be, but...) - if (substr($string, strlen($string) - 2, 2) == "\x00\x00") { - $string = substr($string, 0, strlen($string) - 2); - } - - // convert - return trim(getid3_lib::iconv_fallback('UTF-16LE', 'ISO-8859-1', $string), ' '); - } - - - function TrimTerm($string) { - - // remove terminator, only if present (it should be, but...) - if (substr($string, -2) == "\x00\x00") { - $string = substr($string, 0, -2); - } - return $string; - } - - -} - - -?> \ No newline at end of file diff --git a/getid3/getid3/module.audio-video.bink.php b/getid3/getid3/module.audio-video.bink.php deleted file mode 100644 index 2ebc6fe..0000000 --- a/getid3/getid3/module.audio-video.bink.php +++ /dev/null @@ -1,70 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.audio.bink.php // -// module for analyzing Bink or Smacker audio-video files // -// dependencies: NONE // -// /// -///////////////////////////////////////////////////////////////// - - -class getid3_bink -{ - - function getid3_bink(&$fd, &$ThisFileInfo) { - -$ThisFileInfo['error'][] = 'Bink / Smacker files not properly processed by this version of getID3()'; - - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $fileTypeID = fread($fd, 3); - switch ($fileTypeID) { - case 'BIK': - return $this->ParseBink($fd, $ThisFileInfo); - break; - - case 'SMK': - return $this->ParseSmacker($fd, $ThisFileInfo); - break; - - default: - $ThisFileInfo['error'][] = 'Expecting "BIK" or "SMK" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$fileTypeID.'"'; - return false; - break; - } - - return true; - - } - - function ParseBink(&$fd, &$ThisFileInfo) { - $ThisFileInfo['fileformat'] = 'bink'; - $ThisFileInfo['video']['dataformat'] = 'bink'; - - $fileData = 'BIK'.fread($fd, 13); - - $ThisFileInfo['bink']['data_size'] = getid3_lib::LittleEndian2Int(substr($fileData, 4, 4)); - $ThisFileInfo['bink']['frame_count'] = getid3_lib::LittleEndian2Int(substr($fileData, 8, 2)); - - if (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) != ($ThisFileInfo['bink']['data_size'] + 8)) { - $ThisFileInfo['error'][] = 'Probably truncated file: expecting '.$ThisFileInfo['bink']['data_size'].' bytes, found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']); - } - - return true; - } - - function ParseSmacker(&$fd, &$ThisFileInfo) { - $ThisFileInfo['fileformat'] = 'smacker'; - $ThisFileInfo['video']['dataformat'] = 'smacker'; - - return false; - } - -} - -?> \ No newline at end of file diff --git a/getid3/getid3/module.audio-video.flv.php b/getid3/getid3/module.audio-video.flv.php deleted file mode 100644 index d7ca5cf..0000000 --- a/getid3/getid3/module.audio-video.flv.php +++ /dev/null @@ -1,497 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -// // -// FLV module by Seth Kaufman // -// // -// * version 0.1 (26 June 2005) // -// // -// minor modifications by James Heinrich // -// * version 0.1.1 (15 July 2005) // -// // -// Support for On2 VP6 codec and meta information by // -// Steve Webster // -// * version 0.2 (22 February 2006) // -// // -// Modified to not read entire file into memory // -// by James Heinrich // -// * version 0.3 (15 June 2006) // -// // -///////////////////////////////////////////////////////////////// -// // -// module.audio-video.flv.php // -// module for analyzing Shockwave Flash Video files // -// dependencies: NONE // -// /// -///////////////////////////////////////////////////////////////// - -define('GETID3_FLV_TAG_AUDIO', 8); -define('GETID3_FLV_TAG_VIDEO', 9); -define('GETID3_FLV_TAG_META', 18); - -define('GETID3_FLV_VIDEO_H263', 2); -define('GETID3_FLV_VIDEO_SCREEN', 3); -define('GETID3_FLV_VIDEO_VP6', 4); - -class getid3_flv -{ - - function getid3_flv(&$fd, &$ThisFileInfo, $ReturnAllTagData=false) { - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - - $FLVdataLength = $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']; - $FLVheader = fread($fd, 5); - - $ThisFileInfo['fileformat'] = 'flv'; - $ThisFileInfo['flv']['header']['signature'] = substr($FLVheader, 0, 3); - $ThisFileInfo['flv']['header']['version'] = getid3_lib::BigEndian2Int(substr($FLVheader, 3, 1)); - $TypeFlags = getid3_lib::BigEndian2Int(substr($FLVheader, 4, 1)); - - if ($ThisFileInfo['flv']['header']['signature'] != 'FLV') { - $ThisFileInfo['error'][] = 'Expecting "FLV" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$ThisFileInfo['flv']['header']['signature'].'"'; - unset($ThisFileInfo['flv']); - unset($ThisFileInfo['fileformat']); - return false; - } - - $ThisFileInfo['flv']['header']['hasAudio'] = (bool) ($TypeFlags & 0x04); - $ThisFileInfo['flv']['header']['hasVideo'] = (bool) ($TypeFlags & 0x01); - - $FrameSizeDataLength = getid3_lib::BigEndian2Int(fread($fd, 4)); - $FLVheaderFrameLength = 9; - if ($FrameSizeDataLength > $FLVheaderFrameLength) { - fseek($fd, $FrameSizeDataLength - $FLVheaderFrameLength, SEEK_CUR); - } - - $Duration = 0; - while ((ftell($fd) + 1) < $ThisFileInfo['avdataend']) { - //if (!$ThisFileInfo['flv']['header']['hasAudio'] || isset($ThisFileInfo['flv']['audio']['audioFormat'])) { - // if (!$ThisFileInfo['flv']['header']['hasVideo'] || isset($ThisFileInfo['flv']['video']['videoCodec'])) { - // break; - // } - //} - - $ThisTagHeader = fread($fd, 16); - - $PreviousTagLength = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 0, 4)); - $TagType = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 4, 1)); - $DataLength = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 5, 3)); - $Timestamp = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 8, 3)); - $LastHeaderByte = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 15, 1)); - $NextOffset = ftell($fd) - 1 + $DataLength; - - switch ($TagType) { - case GETID3_FLV_TAG_AUDIO: - if (!isset($ThisFileInfo['flv']['audio']['audioFormat'])) { - $ThisFileInfo['flv']['audio']['audioFormat'] = $LastHeaderByte & 0x07; - $ThisFileInfo['flv']['audio']['audioRate'] = ($LastHeaderByte & 0x30) / 0x10; - $ThisFileInfo['flv']['audio']['audioSampleSize'] = ($LastHeaderByte & 0x40) / 0x40; - $ThisFileInfo['flv']['audio']['audioType'] = ($LastHeaderByte & 0x80) / 0x80; - } - break; - - case GETID3_FLV_TAG_VIDEO: - if (!isset($ThisFileInfo['flv']['video']['videoCodec'])) { - $ThisFileInfo['flv']['video']['videoCodec'] = $LastHeaderByte & 0x07; - - $FLVvideoHeader = fread($fd, 11); - - if ($ThisFileInfo['flv']['video']['videoCodec'] != GETID3_FLV_VIDEO_VP6) { - - $PictureSizeType = (getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 3, 2))) >> 7; - $PictureSizeType = $PictureSizeType & 0x0007; - $ThisFileInfo['flv']['header']['videoSizeType'] = $PictureSizeType; - switch ($PictureSizeType) { - case 0: - $PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 2)); - $PictureSizeEnc <<= 1; - $ThisFileInfo['video']['resolution_x'] = ($PictureSizeEnc & 0xFF00) >> 8; - $PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 2)); - $PictureSizeEnc <<= 1; - $ThisFileInfo['video']['resolution_y'] = ($PictureSizeEnc & 0xFF00) >> 8; - break; - - case 1: - $PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 4)); - $PictureSizeEnc <<= 1; - $ThisFileInfo['video']['resolution_x'] = ($PictureSizeEnc & 0xFFFF0000) >> 16; - - $PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 7, 4)); - $PictureSizeEnc <<= 1; - $ThisFileInfo['video']['resolution_y'] = ($PictureSizeEnc & 0xFFFF0000) >> 16; - break; - - case 2: - $ThisFileInfo['video']['resolution_x'] = 352; - $ThisFileInfo['video']['resolution_y'] = 288; - break; - - case 3: - $ThisFileInfo['video']['resolution_x'] = 176; - $ThisFileInfo['video']['resolution_y'] = 144; - break; - - case 4: - $ThisFileInfo['video']['resolution_x'] = 128; - $ThisFileInfo['video']['resolution_y'] = 96; - break; - - case 5: - $ThisFileInfo['video']['resolution_x'] = 320; - $ThisFileInfo['video']['resolution_y'] = 240; - break; - - case 6: - $ThisFileInfo['video']['resolution_x'] = 160; - $ThisFileInfo['video']['resolution_y'] = 120; - break; - - default: - $ThisFileInfo['video']['resolution_x'] = 0; - $ThisFileInfo['video']['resolution_y'] = 0; - break; - - } - } - } - break; - - // Meta tag - case GETID3_FLV_TAG_META: - - fseek($fd, -1, SEEK_CUR); - $reader = new AMFReader(new AMFStream(fread($fd, $DataLength))); - $eventName = $reader->readData(); - $ThisFileInfo['meta'][$eventName] = $reader->readData(); - unset($reader); - - $ThisFileInfo['video']['frame_rate'] = $ThisFileInfo['meta']['onMetaData']['framerate']; - $ThisFileInfo['video']['resolution_x'] = $ThisFileInfo['meta']['onMetaData']['width']; - $ThisFileInfo['video']['resolution_y'] = $ThisFileInfo['meta']['onMetaData']['height']; - break; - - default: - // noop - break; - } - - if ($Timestamp > $Duration) { - $Duration = $Timestamp; - } - - fseek($fd, $NextOffset, SEEK_SET); - } - - $ThisFileInfo['playtime_seconds'] = $Duration / 1000; - $ThisFileInfo['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $ThisFileInfo['playtime_seconds']; - - if ($ThisFileInfo['flv']['header']['hasAudio']) { - $ThisFileInfo['audio']['codec'] = $this->FLVaudioFormat($ThisFileInfo['flv']['audio']['audioFormat']); - $ThisFileInfo['audio']['sample_rate'] = $this->FLVaudioRate($ThisFileInfo['flv']['audio']['audioRate']); - $ThisFileInfo['audio']['bits_per_sample'] = $this->FLVaudioBitDepth($ThisFileInfo['flv']['audio']['audioSampleSize']); - - $ThisFileInfo['audio']['channels'] = $ThisFileInfo['flv']['audio']['audioType'] + 1; // 0=mono,1=stereo - $ThisFileInfo['audio']['lossless'] = ($ThisFileInfo['flv']['audio']['audioFormat'] ? false : true); // 0=uncompressed - $ThisFileInfo['audio']['dataformat'] = 'flv'; - } - if (@$ThisFileInfo['flv']['header']['hasVideo']) { - $ThisFileInfo['video']['codec'] = $this->FLVvideoCodec($ThisFileInfo['flv']['video']['videoCodec']); - $ThisFileInfo['video']['dataformat'] = 'flv'; - $ThisFileInfo['video']['lossless'] = false; - } - - return true; - } - - - function FLVaudioFormat($id) { - $FLVaudioFormat = array( - 0 => 'uncompressed', - 1 => 'ADPCM', - 2 => 'mp3', - 5 => 'Nellymoser 8kHz mono', - 6 => 'Nellymoser', - ); - return (@$FLVaudioFormat[$id] ? @$FLVaudioFormat[$id] : false); - } - - function FLVaudioRate($id) { - $FLVaudioRate = array( - 0 => 5500, - 1 => 11025, - 2 => 22050, - 3 => 44100, - ); - return (@$FLVaudioRate[$id] ? @$FLVaudioRate[$id] : false); - } - - function FLVaudioBitDepth($id) { - $FLVaudioBitDepth = array( - 0 => 8, - 1 => 16, - ); - return (@$FLVaudioBitDepth[$id] ? @$FLVaudioBitDepth[$id] : false); - } - - function FLVvideoCodec($id) { - $FLVvideoCodec = array( - GETID3_FLV_VIDEO_H263 => 'Sorenson H.263', - GETID3_FLV_VIDEO_SCREEN => 'Screen video', - GETID3_FLV_VIDEO_VP6 => 'On2 VP6', - ); - return (@$FLVvideoCodec[$id] ? @$FLVvideoCodec[$id] : false); - } -} - -class AMFStream { - var $bytes; - var $pos; - - function AMFStream(&$bytes) { - $this->bytes =& $bytes; - $this->pos = 0; - } - - function readByte() { - return getid3_lib::BigEndian2Int(substr($this->bytes, $this->pos++, 1)); - } - - function readInt() { - return ($this->readByte() << 8) + $this->readByte(); - } - - function readLong() { - return ($this->readByte() << 24) + ($this->readByte() << 16) + ($this->readByte() << 8) + $this->readByte(); - } - - function readDouble() { - return getid3_lib::BigEndian2Float($this->read(8)); - } - - function readUTF() { - $length = $this->readInt(); - return $this->read($length); - } - - function readLongUTF() { - $length = $this->readLong(); - return $this->read($length); - } - - function read($length) { - $val = substr($this->bytes, $this->pos, $length); - $this->pos += $length; - return $val; - } - - function peekByte() { - $pos = $this->pos; - $val = $this->readByte(); - $this->pos = $pos; - return $val; - } - - function peekInt() { - $pos = $this->pos; - $val = $this->readInt(); - $this->pos = $pos; - return $val; - } - - function peekLong() { - $pos = $this->pos; - $val = $this->readLong(); - $this->pos = $pos; - return $val; - } - - function peekDouble() { - $pos = $this->pos; - $val = $this->readDouble(); - $this->pos = $pos; - return $val; - } - - function peekUTF() { - $pos = $this->pos; - $val = $this->readUTF(); - $this->pos = $pos; - return $val; - } - - function peekLongUTF() { - $pos = $this->pos; - $val = $this->readLongUTF(); - $this->pos = $pos; - return $val; - } -} - -class AMFReader { - var $stream; - - function AMFReader(&$stream) { - $this->stream =& $stream; - } - - function readData() { - $value = null; - - $type = $this->stream->readByte(); - - switch($type) { - // Double - case 0: - $value = $this->readDouble(); - break; - - // Boolean - case 1: - $value = $this->readBoolean(); - break; - - // String - case 2: - $value = $this->readString(); - break; - - // Object - case 3: - $value = $this->readObject(); - break; - - // null - case 6: - return null; - break; - - // Mixed array - case 8: - $value = $this->readMixedArray(); - break; - - // Array - case 10: - $value = $this->readArray(); - break; - - // Date - case 11: - $value = $this->readDate(); - break; - - // Long string - case 13: - $value = $this->readLongString(); - break; - - // XML (handled as string) - case 15: - $value = $this->readXML(); - break; - - // Typed object (handled as object) - case 16: - $value = $this->readTypedObject(); - break; - - // Long string - default: - $value = '(unknown or unsupported data type)'; - break; - } - - return $value; - } - - function readDouble() { - return $this->stream->readDouble(); - } - - function readBoolean() { - return $this->stream->readByte() == 1; - } - - function readString() { - return $this->stream->readUTF(); - } - - function readObject() { - // Get highest numerical index - ignored - $highestIndex = $this->stream->readLong(); - - $data = array(); - - while ($key = $this->stream->readUTF()) { - // Mixed array record ends with empty string (0x00 0x00) and 0x09 - if (($key == '') && ($this->stream->peekByte() == 0x09)) { - // Consume byte - $this->stream->readByte(); - break; - } - - $data[$key] = $this->readData(); - } - - return $data; - } - - function readMixedArray() { - // Get highest numerical index - ignored - $highestIndex = $this->stream->readLong(); - - $data = array(); - - while ($key = $this->stream->readUTF()) { - // Mixed array record ends with empty string (0x00 0x00) and 0x09 - if (($key == '') && ($this->stream->peekByte() == 0x09)) { - // Consume byte - $this->stream->readByte(); - break; - } - - if (is_numeric($key)) { - $key = (float) $key; - } - - $data[$key] = $this->readData(); - } - - return $data; - } - - function readArray() { - $length = $this->stream->readLong(); - - $data = array(); - - for ($i = 0; $i < count($length); $i++) { - $data[] = $this->readData(); - } - - return $data; - } - - function readDate() { - $timestamp = $this->stream->readDouble(); - $timezone = $this->stream->readInt(); - return $timestamp; - } - - function readLongString() { - return $this->stream->readLongUTF(); - } - - function readXML() { - return $this->stream->readLongUTF(); - } - - function readTypedObject() { - $className = $this->stream->readUTF(); - return $this->readObject(); - } -} - -?> \ No newline at end of file diff --git a/getid3/getid3/module.audio-video.matroska.php b/getid3/getid3/module.audio-video.matroska.php deleted file mode 100644 index 11ac929..0000000 --- a/getid3/getid3/module.audio-video.matroska.php +++ /dev/null @@ -1,78 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.audio-video.matriska.php // -// module for analyzing Matroska containers // -// dependencies: NONE // -// /// -///////////////////////////////////////////////////////////////// - - -class getid3_matroska -{ - - function getid3_matroska(&$fd, &$ThisFileInfo) { - - $ThisFileInfo['fileformat'] = 'matroska'; - - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - - //$ThisFileInfo['matroska']['raw']['a'] = $this->EBML2Int(fread($fd, 4)); - - $ThisFileInfo['error'][] = 'Mastroka parsing not enabled in this version of getID3()'; - return false; - - } - - - function EBML2Int($EBMLstring) { - // http://matroska.org/specs/ - - // Element ID coded with an UTF-8 like system: - // 1xxx xxxx - Class A IDs (2^7 -2 possible values) (base 0x8X) - // 01xx xxxx xxxx xxxx - Class B IDs (2^14-2 possible values) (base 0x4X 0xXX) - // 001x xxxx xxxx xxxx xxxx xxxx - Class C IDs (2^21-2 possible values) (base 0x2X 0xXX 0xXX) - // 0001 xxxx xxxx xxxx xxxx xxxx xxxx xxxx - Class D IDs (2^28-2 possible values) (base 0x1X 0xXX 0xXX 0xXX) - // Values with all x at 0 and 1 are reserved (hence the -2). - - // Data size, in octets, is also coded with an UTF-8 like system : - // 1xxx xxxx - value 0 to 2^7-2 - // 01xx xxxx xxxx xxxx - value 0 to 2^14-2 - // 001x xxxx xxxx xxxx xxxx xxxx - value 0 to 2^21-2 - // 0001 xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^28-2 - // 0000 1xxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^35-2 - // 0000 01xx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^42-2 - // 0000 001x xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^49-2 - // 0000 0001 xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^56-2 - - if (0x80 & ord($EBMLstring{0})) { - $EBMLstring{0} = chr(ord($EBMLstring{0}) & 0x7F); - } elseif (0x40 & ord($EBMLstring{0})) { - $EBMLstring{0} = chr(ord($EBMLstring{0}) & 0x3F); - } elseif (0x20 & ord($EBMLstring{0})) { - $EBMLstring{0} = chr(ord($EBMLstring{0}) & 0x1F); - } elseif (0x10 & ord($EBMLstring{0})) { - $EBMLstring{0} = chr(ord($EBMLstring{0}) & 0x0F); - } elseif (0x08 & ord($EBMLstring{0})) { - $EBMLstring{0} = chr(ord($EBMLstring{0}) & 0x07); - } elseif (0x04 & ord($EBMLstring{0})) { - $EBMLstring{0} = chr(ord($EBMLstring{0}) & 0x03); - } elseif (0x02 & ord($EBMLstring{0})) { - $EBMLstring{0} = chr(ord($EBMLstring{0}) & 0x01); - } elseif (0x01 & ord($EBMLstring{0})) { - $EBMLstring{0} = chr(ord($EBMLstring{0}) & 0x00); - } else { - return false; - } - return getid3_lib::BigEndian2Int($EBMLstring); - } - -} - -?> \ No newline at end of file diff --git a/getid3/getid3/module.audio-video.mpeg.php b/getid3/getid3/module.audio-video.mpeg.php deleted file mode 100644 index 8f48784..0000000 --- a/getid3/getid3/module.audio-video.mpeg.php +++ /dev/null @@ -1,292 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.audio-video.mpeg.php // -// module for analyzing MPEG files // -// dependencies: module.audio.mp3.php // -// /// -///////////////////////////////////////////////////////////////// - -getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true); - -define('GETID3_MPEG_VIDEO_PICTURE_START', "\x00\x00\x01\x00"); -define('GETID3_MPEG_VIDEO_USER_DATA_START', "\x00\x00\x01\xB2"); -define('GETID3_MPEG_VIDEO_SEQUENCE_HEADER', "\x00\x00\x01\xB3"); -define('GETID3_MPEG_VIDEO_SEQUENCE_ERROR', "\x00\x00\x01\xB4"); -define('GETID3_MPEG_VIDEO_EXTENSION_START', "\x00\x00\x01\xB5"); -define('GETID3_MPEG_VIDEO_SEQUENCE_END', "\x00\x00\x01\xB7"); -define('GETID3_MPEG_VIDEO_GROUP_START', "\x00\x00\x01\xB8"); -define('GETID3_MPEG_AUDIO_START', "\x00\x00\x01\xC0"); - - -class getid3_mpeg -{ - - function getid3_mpeg(&$fd, &$ThisFileInfo) { - if ($ThisFileInfo['avdataend'] <= $ThisFileInfo['avdataoffset']) { - $ThisFileInfo['error'][] = '"avdataend" ('.$ThisFileInfo['avdataend'].') is unexpectedly less-than-or-equal-to "avdataoffset" ('.$ThisFileInfo['avdataoffset'].')'; - return false; - } - $ThisFileInfo['fileformat'] = 'mpeg'; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $MPEGstreamData = fread($fd, min(100000, $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'])); - $MPEGstreamDataLength = strlen($MPEGstreamData); - - $foundVideo = true; - $VideoChunkOffset = 0; - while (substr($MPEGstreamData, $VideoChunkOffset++, 4) !== GETID3_MPEG_VIDEO_SEQUENCE_HEADER) { - if ($VideoChunkOffset >= $MPEGstreamDataLength) { - $foundVideo = false; - break; - } - } - if ($foundVideo) { - - // Start code 32 bits - // horizontal frame size 12 bits - // vertical frame size 12 bits - // pixel aspect ratio 4 bits - // frame rate 4 bits - // bitrate 18 bits - // marker bit 1 bit - // VBV buffer size 10 bits - // constrained parameter flag 1 bit - // intra quant. matrix flag 1 bit - // intra quant. matrix values 512 bits (present if matrix flag == 1) - // non-intra quant. matrix flag 1 bit - // non-intra quant. matrix values 512 bits (present if matrix flag == 1) - - $ThisFileInfo['video']['dataformat'] = 'mpeg'; - - $VideoChunkOffset += (strlen(GETID3_MPEG_VIDEO_SEQUENCE_HEADER) - 1); - - $FrameSizeDWORD = getid3_lib::BigEndian2Int(substr($MPEGstreamData, $VideoChunkOffset, 3)); - $VideoChunkOffset += 3; - - $AspectRatioFrameRateDWORD = getid3_lib::BigEndian2Int(substr($MPEGstreamData, $VideoChunkOffset, 1)); - $VideoChunkOffset += 1; - - $assortedinformation = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $VideoChunkOffset, 4)); - $VideoChunkOffset += 4; - - $ThisFileInfo['mpeg']['video']['raw']['framesize_horizontal'] = ($FrameSizeDWORD & 0xFFF000) >> 12; // 12 bits for horizontal frame size - $ThisFileInfo['mpeg']['video']['raw']['framesize_vertical'] = ($FrameSizeDWORD & 0x000FFF); // 12 bits for vertical frame size - $ThisFileInfo['mpeg']['video']['raw']['pixel_aspect_ratio'] = ($AspectRatioFrameRateDWORD & 0xF0) >> 4; - $ThisFileInfo['mpeg']['video']['raw']['frame_rate'] = ($AspectRatioFrameRateDWORD & 0x0F); - - $ThisFileInfo['mpeg']['video']['framesize_horizontal'] = $ThisFileInfo['mpeg']['video']['raw']['framesize_horizontal']; - $ThisFileInfo['mpeg']['video']['framesize_vertical'] = $ThisFileInfo['mpeg']['video']['raw']['framesize_vertical']; - - $ThisFileInfo['mpeg']['video']['pixel_aspect_ratio'] = $this->MPEGvideoAspectRatioLookup($ThisFileInfo['mpeg']['video']['raw']['pixel_aspect_ratio']); - $ThisFileInfo['mpeg']['video']['pixel_aspect_ratio_text'] = $this->MPEGvideoAspectRatioTextLookup($ThisFileInfo['mpeg']['video']['raw']['pixel_aspect_ratio']); - $ThisFileInfo['mpeg']['video']['frame_rate'] = $this->MPEGvideoFramerateLookup($ThisFileInfo['mpeg']['video']['raw']['frame_rate']); - - $ThisFileInfo['mpeg']['video']['raw']['bitrate'] = getid3_lib::Bin2Dec(substr($assortedinformation, 0, 18)); - $ThisFileInfo['mpeg']['video']['raw']['marker_bit'] = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 18, 1)); - $ThisFileInfo['mpeg']['video']['raw']['vbv_buffer_size'] = getid3_lib::Bin2Dec(substr($assortedinformation, 19, 10)); - $ThisFileInfo['mpeg']['video']['raw']['constrained_param_flag'] = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 29, 1)); - $ThisFileInfo['mpeg']['video']['raw']['intra_quant_flag'] = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 30, 1)); - if ($ThisFileInfo['mpeg']['video']['raw']['intra_quant_flag']) { - - // read 512 bits - $ThisFileInfo['mpeg']['video']['raw']['intra_quant'] = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $VideoChunkOffset, 64)); - $VideoChunkOffset += 64; - - $ThisFileInfo['mpeg']['video']['raw']['non_intra_quant_flag'] = (bool) getid3_lib::Bin2Dec(substr($ThisFileInfo['mpeg']['video']['raw']['intra_quant'], 511, 1)); - $ThisFileInfo['mpeg']['video']['raw']['intra_quant'] = getid3_lib::Bin2Dec(substr($assortedinformation, 31, 1)).substr(getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $VideoChunkOffset, 64)), 0, 511); - - if ($ThisFileInfo['mpeg']['video']['raw']['non_intra_quant_flag']) { - $ThisFileInfo['mpeg']['video']['raw']['non_intra_quant'] = substr($MPEGstreamData, $VideoChunkOffset, 64); - $VideoChunkOffset += 64; - } - - } else { - - $ThisFileInfo['mpeg']['video']['raw']['non_intra_quant_flag'] = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 31, 1)); - if ($ThisFileInfo['mpeg']['video']['raw']['non_intra_quant_flag']) { - $ThisFileInfo['mpeg']['video']['raw']['non_intra_quant'] = substr($MPEGstreamData, $VideoChunkOffset, 64); - $VideoChunkOffset += 64; - } - - } - - if ($ThisFileInfo['mpeg']['video']['raw']['bitrate'] == 0x3FFFF) { // 18 set bits - - $ThisFileInfo['warning'][] = 'This version of getID3() ['.GETID3_VERSION.'] cannot determine average bitrate of VBR MPEG video files'; - $ThisFileInfo['mpeg']['video']['bitrate_mode'] = 'vbr'; - - } else { - - $ThisFileInfo['mpeg']['video']['bitrate'] = $ThisFileInfo['mpeg']['video']['raw']['bitrate'] * 400; - $ThisFileInfo['mpeg']['video']['bitrate_mode'] = 'cbr'; - $ThisFileInfo['video']['bitrate'] = $ThisFileInfo['mpeg']['video']['bitrate']; - - } - - $ThisFileInfo['video']['resolution_x'] = $ThisFileInfo['mpeg']['video']['framesize_horizontal']; - $ThisFileInfo['video']['resolution_y'] = $ThisFileInfo['mpeg']['video']['framesize_vertical']; - $ThisFileInfo['video']['frame_rate'] = $ThisFileInfo['mpeg']['video']['frame_rate']; - $ThisFileInfo['video']['bitrate_mode'] = $ThisFileInfo['mpeg']['video']['bitrate_mode']; - $ThisFileInfo['video']['pixel_aspect_ratio'] = $ThisFileInfo['mpeg']['video']['pixel_aspect_ratio']; - $ThisFileInfo['video']['lossless'] = false; - $ThisFileInfo['video']['bits_per_sample'] = 24; - - } else { - - $ThisFileInfo['error'][] = 'Could not find start of video block in the first 100,000 bytes (or before end of file) - this might not be an MPEG-video file?'; - - } - - //0x000001B3 begins the sequence_header of every MPEG video stream. - //But in MPEG-2, this header must immediately be followed by an - //extension_start_code (0x000001B5) with a sequence_extension ID (1). - //(This extension contains all the additional MPEG-2 stuff.) - //MPEG-1 doesn't have this extension, so that's a sure way to tell the - //difference between MPEG-1 and MPEG-2 video streams. - - if (substr($MPEGstreamData, $VideoChunkOffset, 4) == GETID3_MPEG_VIDEO_EXTENSION_START) { - $ThisFileInfo['video']['codec'] = 'MPEG-2'; - } else { - $ThisFileInfo['video']['codec'] = 'MPEG-1'; - } - - - $AudioChunkOffset = 0; - while (true) { - while (substr($MPEGstreamData, $AudioChunkOffset++, 4) !== GETID3_MPEG_AUDIO_START) { - if ($AudioChunkOffset >= $MPEGstreamDataLength) { - break 2; - } - } - - for ($i = 0; $i <= 7; $i++) { - // some files have the MPEG-audio header 8 bytes after the end of the $00 $00 $01 $C0 signature, some have it up to 13 bytes (or more?) after - // I have no idea why or what the difference is, so this is a stupid hack. - // If anybody has any better idea of what's going on, please let me know - info@getid3.org - - $dummy = $ThisFileInfo; - if (getid3_mp3::decodeMPEGaudioHeader($fd, ($AudioChunkOffset + 3) + 8 + $i, $dummy, false)) { - $ThisFileInfo = $dummy; - $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; - $ThisFileInfo['audio']['lossless'] = false; - break 2; - - } - } - } - - // Temporary hack to account for interleaving overhead: - if (!empty($ThisFileInfo['video']['bitrate']) && !empty($ThisFileInfo['audio']['bitrate'])) { - $ThisFileInfo['playtime_seconds'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / ($ThisFileInfo['video']['bitrate'] + $ThisFileInfo['audio']['bitrate']); - - // Interleaved MPEG audio/video files have a certain amount of overhead that varies - // by both video and audio bitrates, and not in any sensible, linear/logarithmic patter - // Use interpolated lookup tables to approximately guess how much is overhead, because - // playtime is calculated as filesize / total-bitrate - $ThisFileInfo['playtime_seconds'] *= $this->MPEGsystemNonOverheadPercentage($ThisFileInfo['video']['bitrate'], $ThisFileInfo['audio']['bitrate']); - - //switch ($ThisFileInfo['video']['bitrate']) { - // case('5000000'): - // $multiplier = 0.93292642112380355828048824319889; - // break; - // case('5500000'): - // $multiplier = 0.93582895375200989965359777343219; - // break; - // case('6000000'): - // $multiplier = 0.93796247714820932532911373859139; - // break; - // case('7000000'): - // $multiplier = 0.9413264083635103463010117778776; - // break; - // default: - // $multiplier = 1; - // break; - //} - //$ThisFileInfo['playtime_seconds'] *= $multiplier; - //$ThisFileInfo['warning'][] = 'Interleaved MPEG audio/video playtime may be inaccurate. With current hack should be within a few seconds of accurate. Report to info@getid3.org if off by more than 10 seconds.'; - if ($ThisFileInfo['video']['bitrate'] < 50000) { - $ThisFileInfo['warning'][] = 'Interleaved MPEG audio/video playtime may be slightly inaccurate for video bitrates below 100kbps. Except in extreme low-bitrate situations, error should be less than 1%. Report to info@getid3.org if greater than this.'; - } - } - - return true; - } - - - function MPEGsystemNonOverheadPercentage($VideoBitrate, $AudioBitrate) { - $OverheadPercentage = 0; - - $AudioBitrate = max(min($AudioBitrate / 1000, 384), 32); // limit to range of 32kbps - 384kbps (should be only legal bitrates, but maybe VBR?) - $VideoBitrate = max(min($VideoBitrate / 1000, 10000), 10); // limit to range of 10kbps - 10Mbps (beyond that curves flatten anyways, no big loss) - - - //OMBB[audiobitrate] = array(video-10kbps, video-100kbps, video-1000kbps, video-10000kbps) - $OverheadMultiplierByBitrate[32] = array(0, 0.9676287944368530, 0.9802276264360310, 0.9844916183244460, 0.9852821845179940); - $OverheadMultiplierByBitrate[48] = array(0, 0.9779100089209830, 0.9787770035359320, 0.9846738664076130, 0.9852683013799960); - $OverheadMultiplierByBitrate[56] = array(0, 0.9731249855367600, 0.9776624308938040, 0.9832606361852130, 0.9843922606633340); - $OverheadMultiplierByBitrate[64] = array(0, 0.9755642683275760, 0.9795256705493390, 0.9836573009193170, 0.9851122539404470); - $OverheadMultiplierByBitrate[96] = array(0, 0.9788025247497290, 0.9798553314148700, 0.9822956869792560, 0.9834815119124690); - $OverheadMultiplierByBitrate[128] = array(0, 0.9816940050925480, 0.9821675936072120, 0.9829756927470870, 0.9839763420152050); - $OverheadMultiplierByBitrate[160] = array(0, 0.9825894094561180, 0.9820913399073960, 0.9823907143253970, 0.9832821783651570); - $OverheadMultiplierByBitrate[192] = array(0, 0.9832038474336260, 0.9825731694317960, 0.9821028622712400, 0.9828262076447620); - $OverheadMultiplierByBitrate[224] = array(0, 0.9836516298538770, 0.9824718601823890, 0.9818302180625380, 0.9823735101626480); - $OverheadMultiplierByBitrate[256] = array(0, 0.9845863022094920, 0.9837229411967540, 0.9824521662210830, 0.9828645172100790); - $OverheadMultiplierByBitrate[320] = array(0, 0.9849565280263180, 0.9837683142805110, 0.9822885275960400, 0.9824424382727190); - $OverheadMultiplierByBitrate[384] = array(0, 0.9856094774357600, 0.9844573394432720, 0.9825970399837330, 0.9824673808303890); - - $BitrateToUseMin = 32; - $BitrateToUseMax = 32; - $previousBitrate = 32; - foreach ($OverheadMultiplierByBitrate as $key => $value) { - if ($AudioBitrate >= $previousBitrate) { - $BitrateToUseMin = $previousBitrate; - } - if ($AudioBitrate < $key) { - $BitrateToUseMax = $key; - break; - } - $previousBitrate = $key; - } - $FactorA = ($BitrateToUseMax - $AudioBitrate) / ($BitrateToUseMax - $BitrateToUseMin); - - $VideoBitrateLog10 = log10($VideoBitrate); - $VideoFactorMin1 = $OverheadMultiplierByBitrate[$BitrateToUseMin][floor($VideoBitrateLog10)]; - $VideoFactorMin2 = $OverheadMultiplierByBitrate[$BitrateToUseMax][floor($VideoBitrateLog10)]; - $VideoFactorMax1 = $OverheadMultiplierByBitrate[$BitrateToUseMin][ceil($VideoBitrateLog10)]; - $VideoFactorMax2 = $OverheadMultiplierByBitrate[$BitrateToUseMax][ceil($VideoBitrateLog10)]; - $FactorV = $VideoBitrateLog10 - floor($VideoBitrateLog10); - - $OverheadPercentage = $VideoFactorMin1 * $FactorA * $FactorV; - $OverheadPercentage += $VideoFactorMin2 * (1 - $FactorA) * $FactorV; - $OverheadPercentage += $VideoFactorMax1 * $FactorA * (1 - $FactorV); - $OverheadPercentage += $VideoFactorMax2 * (1 - $FactorA) * (1 - $FactorV); - - return $OverheadPercentage; - } - - - function MPEGvideoFramerateLookup($rawframerate) { - $MPEGvideoFramerateLookup = array(0, 23.976, 24, 25, 29.97, 30, 50, 59.94, 60); - return (isset($MPEGvideoFramerateLookup[$rawframerate]) ? (float) $MPEGvideoFramerateLookup[$rawframerate] : (float) 0); - } - - function MPEGvideoAspectRatioLookup($rawaspectratio) { - $MPEGvideoAspectRatioLookup = array(0, 1, 0.6735, 0.7031, 0.7615, 0.8055, 0.8437, 0.8935, 0.9157, 0.9815, 1.0255, 1.0695, 1.0950, 1.1575, 1.2015, 0); - return (isset($MPEGvideoAspectRatioLookup[$rawaspectratio]) ? (float) $MPEGvideoAspectRatioLookup[$rawaspectratio] : (float) 0); - } - - function MPEGvideoAspectRatioTextLookup($rawaspectratio) { - $MPEGvideoAspectRatioTextLookup = array('forbidden', 'square pixels', '0.6735', '16:9, 625 line, PAL', '0.7615', '0.8055', '16:9, 525 line, NTSC', '0.8935', '4:3, 625 line, PAL, CCIR601', '0.9815', '1.0255', '1.0695', '4:3, 525 line, NTSC, CCIR601', '1.1575', '1.2015', 'reserved'); - return (isset($MPEGvideoAspectRatioTextLookup[$rawaspectratio]) ? $MPEGvideoAspectRatioTextLookup[$rawaspectratio] : ''); - } - -} - - -?> \ No newline at end of file diff --git a/getid3/getid3/module.audio-video.nsv.php b/getid3/getid3/module.audio-video.nsv.php deleted file mode 100644 index dab0338..0000000 --- a/getid3/getid3/module.audio-video.nsv.php +++ /dev/null @@ -1,224 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.audio.nsv.php // -// module for analyzing Nullsoft NSV files // -// dependencies: NONE // -// /// -///////////////////////////////////////////////////////////////// - - -class getid3_nsv -{ - - function getid3_nsv(&$fd, &$ThisFileInfo) { - - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $NSVheader = fread($fd, 4); - - switch ($NSVheader) { - case 'NSVs': - if ($this->getNSVsHeaderFilepointer($fd, $ThisFileInfo, 0)) { - $ThisFileInfo['fileformat'] = 'nsv'; - $ThisFileInfo['audio']['dataformat'] = 'nsv'; - $ThisFileInfo['video']['dataformat'] = 'nsv'; - $ThisFileInfo['audio']['lossless'] = false; - $ThisFileInfo['video']['lossless'] = false; - } - break; - - case 'NSVf': - if ($this->getNSVfHeaderFilepointer($fd, $ThisFileInfo, 0)) { - $ThisFileInfo['fileformat'] = 'nsv'; - $ThisFileInfo['audio']['dataformat'] = 'nsv'; - $ThisFileInfo['video']['dataformat'] = 'nsv'; - $ThisFileInfo['audio']['lossless'] = false; - $ThisFileInfo['video']['lossless'] = false; - $this->getNSVsHeaderFilepointer($fd, $ThisFileInfo, $ThisFileInfo['nsv']['NSVf']['header_length']); - } - break; - - default: - $ThisFileInfo['error'][] = 'Expecting "NSVs" or "NSVf" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$NSVheader.'"'; - return false; - break; - } - - if (!isset($ThisFileInfo['nsv']['NSVf'])) { - $ThisFileInfo['warning'][] = 'NSVf header not present - cannot calculate playtime or bitrate'; - } - - return true; - } - - function getNSVsHeaderFilepointer(&$fd, &$ThisFileInfo, $fileoffset) { - fseek($fd, $fileoffset, SEEK_SET); - $NSVsheader = fread($fd, 28); - $offset = 0; - - $ThisFileInfo['nsv']['NSVs']['identifier'] = substr($NSVsheader, $offset, 4); - $offset += 4; - - if ($ThisFileInfo['nsv']['NSVs']['identifier'] != 'NSVs') { - $ThisFileInfo['error'][] = 'expected "NSVs" at offset ('.$fileoffset.'), found "'.$ThisFileInfo['nsv']['NSVs']['identifier'].'" instead'; - unset($ThisFileInfo['nsv']['NSVs']); - return false; - } - - $ThisFileInfo['nsv']['NSVs']['offset'] = $fileoffset; - - $ThisFileInfo['nsv']['NSVs']['video_codec'] = substr($NSVsheader, $offset, 4); - $offset += 4; - $ThisFileInfo['nsv']['NSVs']['audio_codec'] = substr($NSVsheader, $offset, 4); - $offset += 4; - $ThisFileInfo['nsv']['NSVs']['resolution_x'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 2)); - $offset += 2; - $ThisFileInfo['nsv']['NSVs']['resolution_y'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 2)); - $offset += 2; - - $ThisFileInfo['nsv']['NSVs']['framerate_index'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); - $offset += 1; - //$ThisFileInfo['nsv']['NSVs']['unknown1b'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); - $offset += 1; - //$ThisFileInfo['nsv']['NSVs']['unknown1c'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); - $offset += 1; - //$ThisFileInfo['nsv']['NSVs']['unknown1d'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); - $offset += 1; - //$ThisFileInfo['nsv']['NSVs']['unknown2a'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); - $offset += 1; - //$ThisFileInfo['nsv']['NSVs']['unknown2b'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); - $offset += 1; - //$ThisFileInfo['nsv']['NSVs']['unknown2c'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); - $offset += 1; - //$ThisFileInfo['nsv']['NSVs']['unknown2d'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); - $offset += 1; - - switch ($ThisFileInfo['nsv']['NSVs']['audio_codec']) { - case 'PCM ': - $ThisFileInfo['nsv']['NSVs']['bits_channel'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); - $offset += 1; - $ThisFileInfo['nsv']['NSVs']['channels'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); - $offset += 1; - $ThisFileInfo['nsv']['NSVs']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 2)); - $offset += 2; - - $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['nsv']['NSVs']['sample_rate']; - break; - - case 'MP3 ': - case 'NONE': - default: - //$ThisFileInfo['nsv']['NSVs']['unknown3'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 4)); - $offset += 4; - break; - } - - $ThisFileInfo['video']['resolution_x'] = $ThisFileInfo['nsv']['NSVs']['resolution_x']; - $ThisFileInfo['video']['resolution_y'] = $ThisFileInfo['nsv']['NSVs']['resolution_y']; - $ThisFileInfo['nsv']['NSVs']['frame_rate'] = $this->NSVframerateLookup($ThisFileInfo['nsv']['NSVs']['framerate_index']); - $ThisFileInfo['video']['frame_rate'] = $ThisFileInfo['nsv']['NSVs']['frame_rate']; - $ThisFileInfo['video']['bits_per_sample'] = 24; - $ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1; - - return true; - } - - function getNSVfHeaderFilepointer(&$fd, &$ThisFileInfo, $fileoffset, $getTOCoffsets=false) { - fseek($fd, $fileoffset, SEEK_SET); - $NSVfheader = fread($fd, 28); - $offset = 0; - - $ThisFileInfo['nsv']['NSVf']['identifier'] = substr($NSVfheader, $offset, 4); - $offset += 4; - - if ($ThisFileInfo['nsv']['NSVf']['identifier'] != 'NSVf') { - $ThisFileInfo['error'][] = 'expected "NSVf" at offset ('.$fileoffset.'), found "'.$ThisFileInfo['nsv']['NSVf']['identifier'].'" instead'; - unset($ThisFileInfo['nsv']['NSVf']); - return false; - } - - $ThisFileInfo['nsv']['NSVs']['offset'] = $fileoffset; - - $ThisFileInfo['nsv']['NSVf']['header_length'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4)); - $offset += 4; - $ThisFileInfo['nsv']['NSVf']['file_size'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4)); - $offset += 4; - - if ($ThisFileInfo['nsv']['NSVf']['file_size'] > $ThisFileInfo['avdataend']) { - $ThisFileInfo['warning'][] = 'truncated file - NSVf header indicates '.$ThisFileInfo['nsv']['NSVf']['file_size'].' bytes, file actually '.$ThisFileInfo['avdataend'].' bytes'; - } - - $ThisFileInfo['nsv']['NSVf']['playtime_ms'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4)); - $offset += 4; - $ThisFileInfo['nsv']['NSVf']['meta_size'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4)); - $offset += 4; - $ThisFileInfo['nsv']['NSVf']['TOC_entries_1'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4)); - $offset += 4; - $ThisFileInfo['nsv']['NSVf']['TOC_entries_2'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4)); - $offset += 4; - - if ($ThisFileInfo['nsv']['NSVf']['playtime_ms'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt NSV file: NSVf.playtime_ms == zero'; - return false; - } - - $NSVfheader .= fread($fd, $ThisFileInfo['nsv']['NSVf']['meta_size'] + (4 * $ThisFileInfo['nsv']['NSVf']['TOC_entries_1']) + (4 * $ThisFileInfo['nsv']['NSVf']['TOC_entries_2'])); - $NSVfheaderlength = strlen($NSVfheader); - $ThisFileInfo['nsv']['NSVf']['metadata'] = substr($NSVfheader, $offset, $ThisFileInfo['nsv']['NSVf']['meta_size']); - $offset += $ThisFileInfo['nsv']['NSVf']['meta_size']; - - if ($getTOCoffsets) { - $TOCcounter = 0; - while ($TOCcounter < $ThisFileInfo['nsv']['NSVf']['TOC_entries_1']) { - if ($TOCcounter < $ThisFileInfo['nsv']['NSVf']['TOC_entries_1']) { - $ThisFileInfo['nsv']['NSVf']['TOC_1'][$TOCcounter] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4)); - $offset += 4; - $TOCcounter++; - } - } - } - - if (trim($ThisFileInfo['nsv']['NSVf']['metadata']) != '') { - $ThisFileInfo['nsv']['NSVf']['metadata'] = str_replace('`', "\x01", $ThisFileInfo['nsv']['NSVf']['metadata']); - $CommentPairArray = explode("\x01".' ', $ThisFileInfo['nsv']['NSVf']['metadata']); - foreach ($CommentPairArray as $CommentPair) { - if (strstr($CommentPair, '='."\x01")) { - list($key, $value) = explode('='."\x01", $CommentPair, 2); - $ThisFileInfo['nsv']['comments'][strtolower($key)][] = trim(str_replace("\x01", '', $value)); - } - } - } - - $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['nsv']['NSVf']['playtime_ms'] / 1000; - $ThisFileInfo['bitrate'] = ($ThisFileInfo['nsv']['NSVf']['file_size'] * 8) / $ThisFileInfo['playtime_seconds']; - - return true; - } - - - function NSVframerateLookup($framerateindex) { - if ($framerateindex <= 127) { - return (float) $framerateindex; - } - - static $NSVframerateLookup = array(); - if (empty($NSVframerateLookup)) { - $NSVframerateLookup[129] = (float) 29.970; - $NSVframerateLookup[131] = (float) 23.976; - $NSVframerateLookup[133] = (float) 14.985; - $NSVframerateLookup[197] = (float) 59.940; - $NSVframerateLookup[199] = (float) 47.952; - } - return (isset($NSVframerateLookup[$framerateindex]) ? $NSVframerateLookup[$framerateindex] : false); - } - -} - - -?> \ No newline at end of file diff --git a/getid3/getid3/module.audio-video.quicktime.php b/getid3/getid3/module.audio-video.quicktime.php deleted file mode 100644 index e08b9b2..0000000 --- a/getid3/getid3/module.audio-video.quicktime.php +++ /dev/null @@ -1,1302 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.audio-video.quicktime.php // -// module for analyzing Quicktime and MP3-in-MP4 files // -// dependencies: module.audio.mp3.php // -// /// -///////////////////////////////////////////////////////////////// - -getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true); - -class getid3_quicktime -{ - - function getid3_quicktime(&$fd, &$ThisFileInfo, $ReturnAtomData=true, $ParseAllPossibleAtoms=false) { - - $ThisFileInfo['fileformat'] = 'quicktime'; - $ThisFileInfo['quicktime']['hinting'] = false; - - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - - $offset = 0; - $atomcounter = 0; - - while ($offset < $ThisFileInfo['avdataend']) { - fseek($fd, $offset, SEEK_SET); - $AtomHeader = fread($fd, 8); - - $atomsize = getid3_lib::BigEndian2Int(substr($AtomHeader, 0, 4)); - $atomname = substr($AtomHeader, 4, 4); - $ThisFileInfo['quicktime'][$atomname]['name'] = $atomname; - $ThisFileInfo['quicktime'][$atomname]['size'] = $atomsize; - $ThisFileInfo['quicktime'][$atomname]['offset'] = $offset; - - if (($offset + $atomsize) > $ThisFileInfo['avdataend']) { - $ThisFileInfo['error'][] = 'Atom at offset '.$offset.' claims to go beyond end-of-file (length: '.$atomsize.' bytes)'; - return false; - } - - if ($atomsize == 0) { - // Furthermore, for historical reasons the list of atoms is optionally - // terminated by a 32-bit integer set to 0. If you are writing a program - // to read user data atoms, you should allow for the terminating 0. - break; - } - switch ($atomname) { - case 'mdat': // Media DATa atom - // 'mdat' contains the actual data for the audio/video - if (($atomsize > 8) && (!isset($ThisFileInfo['avdataend_tmp']) || ($ThisFileInfo['quicktime'][$atomname]['size'] > ($ThisFileInfo['avdataend_tmp'] - $ThisFileInfo['avdataoffset'])))) { - - $ThisFileInfo['avdataoffset'] = $ThisFileInfo['quicktime'][$atomname]['offset'] + 8; - $OldAVDataEnd = $ThisFileInfo['avdataend']; - $ThisFileInfo['avdataend'] = $ThisFileInfo['quicktime'][$atomname]['offset'] + $ThisFileInfo['quicktime'][$atomname]['size']; - - if (getid3_mp3::MPEGaudioHeaderValid(getid3_mp3::MPEGaudioHeaderDecode(fread($fd, 4)))) { - getid3_mp3::getOnlyMPEGaudioInfo($fd, $ThisFileInfo, $ThisFileInfo['avdataoffset'], false); - if (isset($ThisFileInfo['mpeg']['audio'])) { - $ThisFileInfo['audio']['dataformat'] = 'mp3'; - $ThisFileInfo['audio']['codec'] = (!empty($ThisFileInfo['mpeg']['audio']['encoder']) ? $ThisFileInfo['mpeg']['audio']['encoder'] : (!empty($ThisFileInfo['mpeg']['audio']['codec']) ? $ThisFileInfo['mpeg']['audio']['codec'] : (!empty($ThisFileInfo['mpeg']['audio']['LAME']) ? 'LAME' :'mp3'))); - $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['mpeg']['audio']['sample_rate']; - $ThisFileInfo['audio']['channels'] = $ThisFileInfo['mpeg']['audio']['channels']; - $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['bitrate']; - $ThisFileInfo['audio']['bitrate_mode'] = strtolower($ThisFileInfo['mpeg']['audio']['bitrate_mode']); - $ThisFileInfo['bitrate'] = $ThisFileInfo['audio']['bitrate']; - } - } - $ThisFileInfo['avdataend'] = $OldAVDataEnd; - unset($OldAVDataEnd); - - } - break; - - case 'free': // FREE space atom - case 'skip': // SKIP atom - case 'wide': // 64-bit expansion placeholder atom - // 'free', 'skip' and 'wide' are just padding, contains no useful data at all - break; - - default: - $atomHierarchy = array(); - $ThisFileInfo['quicktime'][$atomname] = $this->QuicktimeParseAtom($atomname, $atomsize, fread($fd, $atomsize), $ThisFileInfo, $offset, $atomHierarchy, $ParseAllPossibleAtoms); - break; - } - - $offset += $atomsize; - $atomcounter++; - } - - if (!empty($ThisFileInfo['avdataend_tmp'])) { - // this value is assigned to a temp value and then erased because - // otherwise any atoms beyond the 'mdat' atom would not get parsed - $ThisFileInfo['avdataend'] = $ThisFileInfo['avdataend_tmp']; - unset($ThisFileInfo['avdataend_tmp']); - } - - if (!isset($ThisFileInfo['bitrate']) && isset($ThisFileInfo['playtime_seconds'])) { - $ThisFileInfo['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; - } - if (isset($ThisFileInfo['bitrate']) && !isset($ThisFileInfo['audio']['bitrate']) && !isset($ThisFileInfo['quicktime']['video'])) { - $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['bitrate']; - } - - if (($ThisFileInfo['audio']['dataformat'] == 'mp4') && empty($ThisFileInfo['video']['resolution_x'])) { - $ThisFileInfo['fileformat'] = 'mp4'; - $ThisFileInfo['mime_type'] = 'audio/mp4'; - unset($ThisFileInfo['video']['dataformat']); - } - - if (!$ReturnAtomData) { - unset($ThisFileInfo['quicktime']['moov']); - } - - if (empty($ThisFileInfo['audio']['dataformat']) && !empty($ThisFileInfo['quicktime']['audio'])) { - $ThisFileInfo['audio']['dataformat'] = 'quicktime'; - } - if (empty($ThisFileInfo['video']['dataformat']) && !empty($ThisFileInfo['quicktime']['video'])) { - $ThisFileInfo['video']['dataformat'] = 'quicktime'; - } - - return true; - } - - function QuicktimeParseAtom($atomname, $atomsize, $atomdata, &$ThisFileInfo, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) { - // http://developer.apple.com/techpubs/quicktime/qtdevdocs/APIREF/INDEX/atomalphaindex.htm - - array_push($atomHierarchy, $atomname); - $atomstructure['hierarchy'] = implode(' ', $atomHierarchy); - $atomstructure['name'] = $atomname; - $atomstructure['size'] = $atomsize; - $atomstructure['offset'] = $baseoffset; - - switch ($atomname) { - case 'moov': // MOVie container atom - case 'trak': // TRAcK container atom - case 'clip': // CLIPping container atom - case 'matt': // track MATTe container atom - case 'edts': // EDiTS container atom - case 'tref': // Track REFerence container atom - case 'mdia': // MeDIA container atom - case 'minf': // Media INFormation container atom - case 'dinf': // Data INFormation container atom - case 'udta': // User DaTA container atom - case 'stbl': // Sample TaBLe container atom - case 'cmov': // Compressed MOVie container atom - case 'rmra': // Reference Movie Record Atom - case 'rmda': // Reference Movie Descriptor Atom - case 'gmhd': // Generic Media info HeaDer atom (seen on QTVR) - $atomstructure['subatoms'] = $this->QuicktimeParseContainerAtom($atomdata, $ThisFileInfo, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); - break; - - - case '©cpy': - case '©day': - case '©dir': - case '©ed1': - case '©ed2': - case '©ed3': - case '©ed4': - case '©ed5': - case '©ed6': - case '©ed7': - case '©ed8': - case '©ed9': - case '©fmt': - case '©inf': - case '©prd': - case '©prf': - case '©req': - case '©src': - case '©wrt': - case '©nam': - case '©cmt': - case '©wrn': - case '©hst': - case '©mak': - case '©mod': - case '©PRD': - case '©swr': - case '©aut': - case '©ART': - case '©trk': - case '©alb': - case '©com': - case '©gen': - case '©ope': - case '©url': - case '©enc': - $atomstructure['data_length'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 2)); - $atomstructure['language_id'] = getid3_lib::BigEndian2Int(substr($atomdata, 2, 2)); - $atomstructure['data'] = substr($atomdata, 4); - - $atomstructure['language'] = $this->QuicktimeLanguageLookup($atomstructure['language_id']); - if (empty($ThisFileInfo['comments']['language']) || (!in_array($atomstructure['language'], $ThisFileInfo['comments']['language']))) { - $ThisFileInfo['comments']['language'][] = $atomstructure['language']; - } - $this->CopyToAppropriateCommentsSection($atomname, $atomstructure['data'], $ThisFileInfo); - break; - - - case 'play': // auto-PLAY atom - $atomstructure['autoplay'] = (bool) getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); - - $ThisFileInfo['quicktime']['autoplay'] = $atomstructure['autoplay']; - break; - - - case 'WLOC': // Window LOCation atom - $atomstructure['location_x'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 2)); - $atomstructure['location_y'] = getid3_lib::BigEndian2Int(substr($atomdata, 2, 2)); - break; - - - case 'LOOP': // LOOPing atom - case 'SelO': // play SELection Only atom - case 'AllF': // play ALL Frames atom - $atomstructure['data'] = getid3_lib::BigEndian2Int($atomdata); - break; - - - case 'name': // - case 'MCPS': // Media Cleaner PRo - case '@PRM': // adobe PReMiere version - case '@PRQ': // adobe PRemiere Quicktime version - $atomstructure['data'] = $atomdata; - break; - - - case 'cmvd': // Compressed MooV Data atom - // Code by ubergeekØubergeek*tv based on information from - // http://developer.apple.com/quicktime/icefloe/dispatch012.html - $atomstructure['unCompressedSize'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 4)); - - $CompressedFileData = substr($atomdata, 4); - if ($UncompressedHeader = @gzuncompress($CompressedFileData)) { - $atomstructure['subatoms'] = $this->QuicktimeParseContainerAtom($UncompressedHeader, $ThisFileInfo, 0, $atomHierarchy, $ParseAllPossibleAtoms); - } else { - $ThisFileInfo['warning'][] = 'Error decompressing compressed MOV atom at offset '.$atomstructure['offset']; - } - break; - - - case 'dcom': // Data COMpression atom - $atomstructure['compression_id'] = $atomdata; - $atomstructure['compression_text'] = $this->QuicktimeDCOMLookup($atomdata); - break; - - - case 'rdrf': // Reference movie Data ReFerence atom - $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); - $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); - $atomstructure['flags']['internal_data'] = (bool) ($atomstructure['flags_raw'] & 0x000001); - - $atomstructure['reference_type_name'] = substr($atomdata, 4, 4); - $atomstructure['reference_length'] = getid3_lib::BigEndian2Int(substr($atomdata, 8, 4)); - switch ($atomstructure['reference_type_name']) { - case 'url ': - $atomstructure['url'] = $this->NoNullString(substr($atomdata, 12)); - break; - - case 'alis': - $atomstructure['file_alias'] = substr($atomdata, 12); - break; - - case 'rsrc': - $atomstructure['resource_alias'] = substr($atomdata, 12); - break; - - default: - $atomstructure['data'] = substr($atomdata, 12); - break; - } - break; - - - case 'rmqu': // Reference Movie QUality atom - $atomstructure['movie_quality'] = getid3_lib::BigEndian2Int($atomdata); - break; - - - case 'rmcs': // Reference Movie Cpu Speed atom - $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); - $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); // hardcoded: 0x0000 - $atomstructure['cpu_speed_rating'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 2)); - break; - - - case 'rmvc': // Reference Movie Version Check atom - $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); - $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); // hardcoded: 0x0000 - $atomstructure['gestalt_selector'] = substr($atomdata, 4, 4); - $atomstructure['gestalt_value_mask'] = getid3_lib::BigEndian2Int(substr($atomdata, 8, 4)); - $atomstructure['gestalt_value'] = getid3_lib::BigEndian2Int(substr($atomdata, 12, 4)); - $atomstructure['gestalt_check_type'] = getid3_lib::BigEndian2Int(substr($atomdata, 14, 2)); - break; - - - case 'rmcd': // Reference Movie Component check atom - $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); - $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); // hardcoded: 0x0000 - $atomstructure['component_type'] = substr($atomdata, 4, 4); - $atomstructure['component_subtype'] = substr($atomdata, 8, 4); - $atomstructure['component_manufacturer'] = substr($atomdata, 12, 4); - $atomstructure['component_flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 16, 4)); - $atomstructure['component_flags_mask'] = getid3_lib::BigEndian2Int(substr($atomdata, 20, 4)); - $atomstructure['component_min_version'] = getid3_lib::BigEndian2Int(substr($atomdata, 24, 4)); - break; - - - case 'rmdr': // Reference Movie Data Rate atom - $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); - $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); // hardcoded: 0x0000 - $atomstructure['data_rate'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 4)); - - $atomstructure['data_rate_bps'] = $atomstructure['data_rate'] * 10; - break; - - - case 'rmla': // Reference Movie Language Atom - $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); - $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); // hardcoded: 0x0000 - $atomstructure['language_id'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 2)); - - $atomstructure['language'] = $this->QuicktimeLanguageLookup($atomstructure['language_id']); - if (empty($ThisFileInfo['comments']['language']) || (!in_array($atomstructure['language'], $ThisFileInfo['comments']['language']))) { - $ThisFileInfo['comments']['language'][] = $atomstructure['language']; - } - break; - - - case 'rmla': // Reference Movie Language Atom - $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); - $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); // hardcoded: 0x0000 - $atomstructure['track_id'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 2)); - break; - - - case 'ptv ': // Print To Video - defines a movie's full screen mode - // http://developer.apple.com/documentation/QuickTime/APIREF/SOURCESIV/at_ptv-_pg.htm - $atomstructure['display_size_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 2)); - $atomstructure['reserved_1'] = getid3_lib::BigEndian2Int(substr($atomdata, 2, 2)); // hardcoded: 0x0000 - $atomstructure['reserved_2'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 2)); // hardcoded: 0x0000 - $atomstructure['slide_show_flag'] = getid3_lib::BigEndian2Int(substr($atomdata, 6, 1)); - $atomstructure['play_on_open_flag'] = getid3_lib::BigEndian2Int(substr($atomdata, 7, 1)); - - $atomstructure['flags']['play_on_open'] = (bool) $atomstructure['play_on_open_flag']; - $atomstructure['flags']['slide_show'] = (bool) $atomstructure['slide_show_flag']; - - $ptv_lookup[0] = 'normal'; - $ptv_lookup[1] = 'double'; - $ptv_lookup[2] = 'half'; - $ptv_lookup[3] = 'full'; - $ptv_lookup[4] = 'current'; - if (isset($ptv_lookup[$atomstructure['display_size_raw']])) { - $atomstructure['display_size'] = $ptv_lookup[$atomstructure['display_size_raw']]; - } else { - $ThisFileInfo['warning'][] = 'unknown "ptv " display constant ('.$atomstructure['display_size_raw'].')'; - } - break; - - - case 'stsd': // Sample Table Sample Description atom - $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); - $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); // hardcoded: 0x0000 - $atomstructure['number_entries'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 4)); - $stsdEntriesDataOffset = 8; - for ($i = 0; $i < $atomstructure['number_entries']; $i++) { - $atomstructure['sample_description_table'][$i]['size'] = getid3_lib::BigEndian2Int(substr($atomdata, $stsdEntriesDataOffset, 4)); - $stsdEntriesDataOffset += 4; - $atomstructure['sample_description_table'][$i]['data_format'] = substr($atomdata, $stsdEntriesDataOffset, 4); - $stsdEntriesDataOffset += 4; - $atomstructure['sample_description_table'][$i]['reserved'] = getid3_lib::BigEndian2Int(substr($atomdata, $stsdEntriesDataOffset, 6)); - $stsdEntriesDataOffset += 6; - $atomstructure['sample_description_table'][$i]['reference_index'] = getid3_lib::BigEndian2Int(substr($atomdata, $stsdEntriesDataOffset, 2)); - $stsdEntriesDataOffset += 2; - $atomstructure['sample_description_table'][$i]['data'] = substr($atomdata, $stsdEntriesDataOffset, ($atomstructure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2)); - $stsdEntriesDataOffset += ($atomstructure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2); - - $atomstructure['sample_description_table'][$i]['encoder_version'] = getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 0, 2)); - $atomstructure['sample_description_table'][$i]['encoder_revision'] = getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 2, 2)); - $atomstructure['sample_description_table'][$i]['encoder_vendor'] = substr($atomstructure['sample_description_table'][$i]['data'], 4, 4); - - switch ($atomstructure['sample_description_table'][$i]['encoder_vendor']) { - - case "\x00\x00\x00\x00": - // audio atom - $atomstructure['sample_description_table'][$i]['audio_channels'] = getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 8, 2)); - $atomstructure['sample_description_table'][$i]['audio_bit_depth'] = getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 10, 2)); - $atomstructure['sample_description_table'][$i]['audio_compression_id'] = getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 12, 2)); - $atomstructure['sample_description_table'][$i]['audio_packet_size'] = getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 14, 2)); - $atomstructure['sample_description_table'][$i]['audio_sample_rate'] = getid3_lib::FixedPoint16_16(substr($atomstructure['sample_description_table'][$i]['data'], 16, 4)); - - switch ($atomstructure['sample_description_table'][$i]['data_format']) { - case 'mp4v': - $ThisFileInfo['fileformat'] = 'mp4'; - $ThisFileInfo['error'][] = 'This version ('.GETID3_VERSION.') of getID3() does not fully support MPEG-4 audio/video streams'; - break; - - case 'qtvr': - $ThisFileInfo['video']['dataformat'] = 'quicktimevr'; - break; - - case 'mp4a': - default: - $ThisFileInfo['quicktime']['audio']['codec'] = $this->QuicktimeAudioCodecLookup($atomstructure['sample_description_table'][$i]['data_format']); - $ThisFileInfo['quicktime']['audio']['sample_rate'] = $atomstructure['sample_description_table'][$i]['audio_sample_rate']; - $ThisFileInfo['quicktime']['audio']['channels'] = $atomstructure['sample_description_table'][$i]['audio_channels']; - $ThisFileInfo['quicktime']['audio']['bit_depth'] = $atomstructure['sample_description_table'][$i]['audio_bit_depth']; - $ThisFileInfo['audio']['codec'] = $ThisFileInfo['quicktime']['audio']['codec']; - $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['quicktime']['audio']['sample_rate']; - $ThisFileInfo['audio']['channels'] = $ThisFileInfo['quicktime']['audio']['channels']; - $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['quicktime']['audio']['bit_depth']; - switch ($atomstructure['sample_description_table'][$i]['data_format']) { - case 'raw ': // PCM - case 'alac': // Apple Lossless Audio Codec - $ThisFileInfo['audio']['lossless'] = true; - break; - default: - $ThisFileInfo['audio']['lossless'] = false; - break; - } - break; - } - break; - - default: - switch ($atomstructure['sample_description_table'][$i]['data_format']) { - case 'mp4s': - $ThisFileInfo['fileformat'] = 'mp4'; - break; - - default: - // video atom - $atomstructure['sample_description_table'][$i]['video_temporal_quality'] = getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 8, 4)); - $atomstructure['sample_description_table'][$i]['video_spatial_quality'] = getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 12, 4)); - $atomstructure['sample_description_table'][$i]['video_frame_width'] = getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 16, 2)); - $atomstructure['sample_description_table'][$i]['video_frame_height'] = getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 18, 2)); - $atomstructure['sample_description_table'][$i]['video_resolution_x'] = getid3_lib::FixedPoint16_16(substr($atomstructure['sample_description_table'][$i]['data'], 20, 4)); - $atomstructure['sample_description_table'][$i]['video_resolution_y'] = getid3_lib::FixedPoint16_16(substr($atomstructure['sample_description_table'][$i]['data'], 24, 4)); - $atomstructure['sample_description_table'][$i]['video_data_size'] = getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 28, 4)); - $atomstructure['sample_description_table'][$i]['video_frame_count'] = getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 32, 2)); - $atomstructure['sample_description_table'][$i]['video_encoder_name_len'] = getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 34, 1)); - $atomstructure['sample_description_table'][$i]['video_encoder_name'] = substr($atomstructure['sample_description_table'][$i]['data'], 35, $atomstructure['sample_description_table'][$i]['video_encoder_name_len']); - $atomstructure['sample_description_table'][$i]['video_pixel_color_depth'] = getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 66, 2)); - $atomstructure['sample_description_table'][$i]['video_color_table_id'] = getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 68, 2)); - - $atomstructure['sample_description_table'][$i]['video_pixel_color_type'] = (($atomstructure['sample_description_table'][$i]['video_pixel_color_depth'] > 32) ? 'grayscale' : 'color'); - $atomstructure['sample_description_table'][$i]['video_pixel_color_name'] = $this->QuicktimeColorNameLookup($atomstructure['sample_description_table'][$i]['video_pixel_color_depth']); - - if ($atomstructure['sample_description_table'][$i]['video_pixel_color_name'] != 'invalid') { - $ThisFileInfo['quicktime']['video']['codec_fourcc'] = $atomstructure['sample_description_table'][$i]['data_format']; - $ThisFileInfo['quicktime']['video']['codec_fourcc_lookup'] = $this->QuicktimeVideoCodecLookup($atomstructure['sample_description_table'][$i]['data_format']); - $ThisFileInfo['quicktime']['video']['codec'] = $atomstructure['sample_description_table'][$i]['video_encoder_name']; - $ThisFileInfo['quicktime']['video']['color_depth'] = $atomstructure['sample_description_table'][$i]['video_pixel_color_depth']; - $ThisFileInfo['quicktime']['video']['color_depth_name'] = $atomstructure['sample_description_table'][$i]['video_pixel_color_name']; - - $ThisFileInfo['video']['codec'] = $ThisFileInfo['quicktime']['video']['codec']; - $ThisFileInfo['video']['bits_per_sample'] = $ThisFileInfo['quicktime']['video']['color_depth']; - } - $ThisFileInfo['video']['lossless'] = false; - $ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1; - break; - } - break; - } - switch (strtolower($atomstructure['sample_description_table'][$i]['data_format'])) { - case 'mp4a': - $ThisFileInfo['audio']['dataformat'] = 'mp4'; - $ThisFileInfo['quicktime']['audio']['codec'] = 'mp4'; - break; - - case '3ivx': - case '3iv1': - case '3iv2': - $ThisFileInfo['video']['dataformat'] = '3ivx'; - break; - - case 'xvid': - $ThisFileInfo['video']['dataformat'] = 'xvid'; - break; - - case 'mp4v': - $ThisFileInfo['video']['dataformat'] = 'mpeg4'; - break; - - case 'divx': - case 'div1': - case 'div2': - case 'div3': - case 'div4': - case 'div5': - case 'div6': - $TDIVXileInfo['video']['dataformat'] = 'divx'; - break; - - default: - // do nothing - break; - } - unset($atomstructure['sample_description_table'][$i]['data']); - } - break; - - - case 'stts': // Sample Table Time-to-Sample atom - //if ($ParseAllPossibleAtoms) { - $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); - $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); // hardcoded: 0x0000 - $atomstructure['number_entries'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 4)); - $sttsEntriesDataOffset = 8; - $FrameRateCalculatorArray = array(); - for ($i = 0; $i < $atomstructure['number_entries']; $i++) { - $atomstructure['time_to_sample_table'][$i]['sample_count'] = getid3_lib::BigEndian2Int(substr($atomdata, $sttsEntriesDataOffset, 4)); - $sttsEntriesDataOffset += 4; - $atomstructure['time_to_sample_table'][$i]['sample_duration'] = getid3_lib::BigEndian2Int(substr($atomdata, $sttsEntriesDataOffset, 4)); - $sttsEntriesDataOffset += 4; - - if (!empty($ThisFileInfo['quicktime']['time_scale']) && (@$atomstructure['time_to_sample_table'][$i]['sample_duration'] > 0)) { - $stts_new_framerate = $ThisFileInfo['quicktime']['time_scale'] / $atomstructure['time_to_sample_table'][$i]['sample_duration']; - if ($stts_new_framerate <= 60) { - // some atoms have durations of "1" giving a very large framerate, which probably is not right - $ThisFileInfo['video']['frame_rate'] = max(@$ThisFileInfo['video']['frame_rate'], $stts_new_framerate); - } - } - //@$FrameRateCalculatorArray[($ThisFileInfo['quicktime']['time_scale'] / $atomstructure['time_to_sample_table'][$i]['sample_duration'])] += $atomstructure['time_to_sample_table'][$i]['sample_count']; - } - //$sttsFramesTotal = 0; - //$sttsSecondsTotal = 0; - //foreach ($FrameRateCalculatorArray as $frames_per_second => $frame_count) { - // if (($frames_per_second > 60) || ($frames_per_second < 1)) { - // // not video FPS information, probably audio information - // $sttsFramesTotal = 0; - // $sttsSecondsTotal = 0; - // break; - // } - // $sttsFramesTotal += $frame_count; - // $sttsSecondsTotal += $frame_count / $frames_per_second; - //} - //if (($sttsFramesTotal > 0) && ($sttsSecondsTotal > 0)) { - // if (($sttsFramesTotal / $sttsSecondsTotal) > @$ThisFileInfo['video']['frame_rate']) { - // $ThisFileInfo['video']['frame_rate'] = $sttsFramesTotal / $sttsSecondsTotal; - // } - //} - //} - break; - - - case 'stss': // Sample Table Sync Sample (key frames) atom - if ($ParseAllPossibleAtoms) { - $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); - $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); // hardcoded: 0x0000 - $atomstructure['number_entries'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 4)); - $stssEntriesDataOffset = 8; - for ($i = 0; $i < $atomstructure['number_entries']; $i++) { - $atomstructure['time_to_sample_table'][$i] = getid3_lib::BigEndian2Int(substr($atomdata, $stssEntriesDataOffset, 4)); - $stssEntriesDataOffset += 4; - } - } - break; - - - case 'stsc': // Sample Table Sample-to-Chunk atom - if ($ParseAllPossibleAtoms) { - $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); - $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); // hardcoded: 0x0000 - $atomstructure['number_entries'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 4)); - $stscEntriesDataOffset = 8; - for ($i = 0; $i < $atomstructure['number_entries']; $i++) { - $atomstructure['sample_to_chunk_table'][$i]['first_chunk'] = getid3_lib::BigEndian2Int(substr($atomdata, $stscEntriesDataOffset, 4)); - $stscEntriesDataOffset += 4; - $atomstructure['sample_to_chunk_table'][$i]['samples_per_chunk'] = getid3_lib::BigEndian2Int(substr($atomdata, $stscEntriesDataOffset, 4)); - $stscEntriesDataOffset += 4; - $atomstructure['sample_to_chunk_table'][$i]['sample_description'] = getid3_lib::BigEndian2Int(substr($atomdata, $stscEntriesDataOffset, 4)); - $stscEntriesDataOffset += 4; - } - } - break; - - - case 'stsz': // Sample Table SiZe atom - if ($ParseAllPossibleAtoms) { - $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); - $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); // hardcoded: 0x0000 - $atomstructure['sample_size'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 4)); - $atomstructure['number_entries'] = getid3_lib::BigEndian2Int(substr($atomdata, 8, 4)); - $stszEntriesDataOffset = 12; - if ($atomstructure['sample_size'] == 0) { - for ($i = 0; $i < $atomstructure['number_entries']; $i++) { - $atomstructure['sample_size_table'][$i] = getid3_lib::BigEndian2Int(substr($atomdata, $stszEntriesDataOffset, 4)); - $stszEntriesDataOffset += 4; - } - } - } - break; - - - case 'stco': // Sample Table Chunk Offset atom - if ($ParseAllPossibleAtoms) { - $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); - $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); // hardcoded: 0x0000 - $atomstructure['number_entries'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 4)); - $stcoEntriesDataOffset = 8; - for ($i = 0; $i < $atomstructure['number_entries']; $i++) { - $atomstructure['chunk_offset_table'][$i] = getid3_lib::BigEndian2Int(substr($atomdata, $stcoEntriesDataOffset, 4)); - $stcoEntriesDataOffset += 4; - } - } - break; - - - case 'dref': // Data REFerence atom - $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); - $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); // hardcoded: 0x0000 - $atomstructure['number_entries'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 4)); - $drefDataOffset = 8; - for ($i = 0; $i < $atomstructure['number_entries']; $i++) { - $atomstructure['data_references'][$i]['size'] = getid3_lib::BigEndian2Int(substr($atomdata, $drefDataOffset, 4)); - $drefDataOffset += 4; - $atomstructure['data_references'][$i]['type'] = substr($atomdata, $drefDataOffset, 4); - $drefDataOffset += 4; - $atomstructure['data_references'][$i]['version'] = getid3_lib::BigEndian2Int(substr($atomdata, $drefDataOffset, 1)); - $drefDataOffset += 1; - $atomstructure['data_references'][$i]['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, $drefDataOffset, 3)); // hardcoded: 0x0000 - $drefDataOffset += 3; - $atomstructure['data_references'][$i]['data'] = substr($atomdata, $drefDataOffset, ($atomstructure['data_references'][$i]['size'] - 4 - 4 - 1 - 3)); - $drefDataOffset += ($atomstructure['data_references'][$i]['size'] - 4 - 4 - 1 - 3); - - $atomstructure['data_references'][$i]['flags']['self_reference'] = (bool) ($atomstructure['data_references'][$i]['flags_raw'] & 0x001); - } - break; - - - case 'gmin': // base Media INformation atom - $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); - $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); // hardcoded: 0x0000 - $atomstructure['graphics_mode'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 2)); - $atomstructure['opcolor_red'] = getid3_lib::BigEndian2Int(substr($atomdata, 6, 2)); - $atomstructure['opcolor_green'] = getid3_lib::BigEndian2Int(substr($atomdata, 8, 2)); - $atomstructure['opcolor_blue'] = getid3_lib::BigEndian2Int(substr($atomdata, 10, 2)); - $atomstructure['balance'] = getid3_lib::BigEndian2Int(substr($atomdata, 12, 2)); - $atomstructure['reserved'] = getid3_lib::BigEndian2Int(substr($atomdata, 14, 2)); - break; - - - case 'smhd': // Sound Media information HeaDer atom - $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); - $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); // hardcoded: 0x0000 - $atomstructure['balance'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 2)); - $atomstructure['reserved'] = getid3_lib::BigEndian2Int(substr($atomdata, 6, 2)); - break; - - - case 'vmhd': // Video Media information HeaDer atom - $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); - $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); - $atomstructure['graphics_mode'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 2)); - $atomstructure['opcolor_red'] = getid3_lib::BigEndian2Int(substr($atomdata, 6, 2)); - $atomstructure['opcolor_green'] = getid3_lib::BigEndian2Int(substr($atomdata, 8, 2)); - $atomstructure['opcolor_blue'] = getid3_lib::BigEndian2Int(substr($atomdata, 10, 2)); - - $atomstructure['flags']['no_lean_ahead'] = (bool) ($atomstructure['flags_raw'] & 0x001); - break; - - - case 'hdlr': // HanDLeR reference atom - $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); - $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); // hardcoded: 0x0000 - $atomstructure['component_type'] = substr($atomdata, 4, 4); - $atomstructure['component_subtype'] = substr($atomdata, 8, 4); - $atomstructure['component_manufacturer'] = substr($atomdata, 12, 4); - $atomstructure['component_flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 16, 4)); - $atomstructure['component_flags_mask'] = getid3_lib::BigEndian2Int(substr($atomdata, 20, 4)); - $atomstructure['component_name'] = $this->Pascal2String(substr($atomdata, 24)); - - if (($atomstructure['component_subtype'] == 'STpn') && ($atomstructure['component_manufacturer'] == 'zzzz')) { - $ThisFileInfo['video']['dataformat'] = 'quicktimevr'; - } - break; - - - case 'mdhd': // MeDia HeaDer atom - $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); - $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); // hardcoded: 0x0000 - $atomstructure['creation_time'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 4)); - $atomstructure['modify_time'] = getid3_lib::BigEndian2Int(substr($atomdata, 8, 4)); - $atomstructure['time_scale'] = getid3_lib::BigEndian2Int(substr($atomdata, 12, 4)); - $atomstructure['duration'] = getid3_lib::BigEndian2Int(substr($atomdata, 16, 4)); - $atomstructure['language_id'] = getid3_lib::BigEndian2Int(substr($atomdata, 20, 2)); - $atomstructure['quality'] = getid3_lib::BigEndian2Int(substr($atomdata, 22, 2)); - - if ($atomstructure['time_scale'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt Quicktime file: mdhd.time_scale == zero'; - return false; - } - $atomstructure['creation_time_unix'] = getid3_lib::DateMac2Unix($atomstructure['creation_time']); - $atomstructure['modify_time_unix'] = getid3_lib::DateMac2Unix($atomstructure['modify_time']); - $atomstructure['playtime_seconds'] = $atomstructure['duration'] / $atomstructure['time_scale']; - $atomstructure['language'] = $this->QuicktimeLanguageLookup($atomstructure['language_id']); - if (empty($ThisFileInfo['comments']['language']) || (!in_array($atomstructure['language'], $ThisFileInfo['comments']['language']))) { - $ThisFileInfo['comments']['language'][] = $atomstructure['language']; - } - break; - - - case 'pnot': // Preview atom - $atomstructure['modification_date'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 4)); // "standard Macintosh format" - $atomstructure['version_number'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 2)); // hardcoded: 0x00 - $atomstructure['atom_type'] = substr($atomdata, 6, 4); // usually: 'PICT' - $atomstructure['atom_index'] = getid3_lib::BigEndian2Int(substr($atomdata, 10, 2)); // usually: 0x01 - - $atomstructure['modification_date_unix'] = getid3_lib::DateMac2Unix($atomstructure['modification_date']); - break; - - - case 'crgn': // Clipping ReGioN atom - $atomstructure['region_size'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 2)); // The Region size, Region boundary box, - $atomstructure['boundary_box'] = getid3_lib::BigEndian2Int(substr($atomdata, 2, 8)); // and Clipping region data fields - $atomstructure['clipping_data'] = substr($atomdata, 10); // constitute a QuickDraw region. - break; - - - case 'load': // track LOAD settings atom - $atomstructure['preload_start_time'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 4)); - $atomstructure['preload_duration'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 4)); - $atomstructure['preload_flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 8, 4)); - $atomstructure['default_hints_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 12, 4)); - - $atomstructure['default_hints']['double_buffer'] = (bool) ($atomstructure['default_hints_raw'] & 0x0020); - $atomstructure['default_hints']['high_quality'] = (bool) ($atomstructure['default_hints_raw'] & 0x0100); - break; - - - case 'tmcd': // TiMe CoDe atom - case 'chap': // CHAPter list atom - case 'sync': // SYNChronization atom - case 'scpt': // tranSCriPT atom - case 'ssrc': // non-primary SouRCe atom - for ($i = 0; $i < (strlen($atomdata) % 4); $i++) { - $atomstructure['track_id'][$i] = getid3_lib::BigEndian2Int(substr($atomdata, $i * 4, 4)); - } - break; - - - case 'elst': // Edit LiST atom - $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); - $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); // hardcoded: 0x0000 - $atomstructure['number_entries'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 4)); - for ($i = 0; $i < $atomstructure['number_entries']; $i++ ) { - $atomstructure['edit_list'][$i]['track_duration'] = getid3_lib::BigEndian2Int(substr($atomdata, 8 + ($i * 12) + 0, 4)); - $atomstructure['edit_list'][$i]['media_time'] = getid3_lib::BigEndian2Int(substr($atomdata, 8 + ($i * 12) + 4, 4)); - $atomstructure['edit_list'][$i]['media_rate'] = getid3_lib::FixedPoint16_16(substr($atomdata, 8 + ($i * 12) + 8, 4)); - } - break; - - - case 'kmat': // compressed MATte atom - $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); - $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); // hardcoded: 0x0000 - $atomstructure['matte_data_raw'] = substr($atomdata, 4); - break; - - - case 'ctab': // Color TABle atom - $atomstructure['color_table_seed'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 4)); // hardcoded: 0x00000000 - $atomstructure['color_table_flags'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 2)); // hardcoded: 0x8000 - $atomstructure['color_table_size'] = getid3_lib::BigEndian2Int(substr($atomdata, 6, 2)) + 1; - for ($colortableentry = 0; $colortableentry < $atomstructure['color_table_size']; $colortableentry++) { - $atomstructure['color_table'][$colortableentry]['alpha'] = getid3_lib::BigEndian2Int(substr($atomdata, 8 + ($colortableentry * 8) + 0, 2)); - $atomstructure['color_table'][$colortableentry]['red'] = getid3_lib::BigEndian2Int(substr($atomdata, 8 + ($colortableentry * 8) + 2, 2)); - $atomstructure['color_table'][$colortableentry]['green'] = getid3_lib::BigEndian2Int(substr($atomdata, 8 + ($colortableentry * 8) + 4, 2)); - $atomstructure['color_table'][$colortableentry]['blue'] = getid3_lib::BigEndian2Int(substr($atomdata, 8 + ($colortableentry * 8) + 6, 2)); - } - break; - - - case 'mvhd': // MoVie HeaDer atom - $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); - $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); - $atomstructure['creation_time'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 4)); - $atomstructure['modify_time'] = getid3_lib::BigEndian2Int(substr($atomdata, 8, 4)); - $atomstructure['time_scale'] = getid3_lib::BigEndian2Int(substr($atomdata, 12, 4)); - $atomstructure['duration'] = getid3_lib::BigEndian2Int(substr($atomdata, 16, 4)); - $atomstructure['preferred_rate'] = getid3_lib::FixedPoint16_16(substr($atomdata, 20, 4)); - $atomstructure['preferred_volume'] = getid3_lib::FixedPoint8_8(substr($atomdata, 24, 2)); - $atomstructure['reserved'] = substr($atomdata, 26, 10); - $atomstructure['matrix_a'] = getid3_lib::FixedPoint16_16(substr($atomdata, 36, 4)); - $atomstructure['matrix_b'] = getid3_lib::FixedPoint16_16(substr($atomdata, 40, 4)); - $atomstructure['matrix_u'] = getid3_lib::FixedPoint2_30(substr($atomdata, 44, 4)); - $atomstructure['matrix_c'] = getid3_lib::FixedPoint16_16(substr($atomdata, 48, 4)); - $atomstructure['matrix_d'] = getid3_lib::FixedPoint16_16(substr($atomdata, 52, 4)); - $atomstructure['matrix_v'] = getid3_lib::FixedPoint2_30(substr($atomdata, 56, 4)); - $atomstructure['matrix_x'] = getid3_lib::FixedPoint16_16(substr($atomdata, 60, 4)); - $atomstructure['matrix_y'] = getid3_lib::FixedPoint16_16(substr($atomdata, 64, 4)); - $atomstructure['matrix_w'] = getid3_lib::FixedPoint2_30(substr($atomdata, 68, 4)); - $atomstructure['preview_time'] = getid3_lib::BigEndian2Int(substr($atomdata, 72, 4)); - $atomstructure['preview_duration'] = getid3_lib::BigEndian2Int(substr($atomdata, 76, 4)); - $atomstructure['poster_time'] = getid3_lib::BigEndian2Int(substr($atomdata, 80, 4)); - $atomstructure['selection_time'] = getid3_lib::BigEndian2Int(substr($atomdata, 84, 4)); - $atomstructure['selection_duration'] = getid3_lib::BigEndian2Int(substr($atomdata, 88, 4)); - $atomstructure['current_time'] = getid3_lib::BigEndian2Int(substr($atomdata, 92, 4)); - $atomstructure['next_track_id'] = getid3_lib::BigEndian2Int(substr($atomdata, 96, 4)); - - if ($atomstructure['time_scale'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt Quicktime file: mvhd.time_scale == zero'; - return false; - } - $atomstructure['creation_time_unix'] = getid3_lib::DateMac2Unix($atomstructure['creation_time']); - $atomstructure['modify_time_unix'] = getid3_lib::DateMac2Unix($atomstructure['modify_time']); - $ThisFileInfo['quicktime']['time_scale'] = $atomstructure['time_scale']; - $ThisFileInfo['quicktime']['display_scale'] = $atomstructure['matrix_a']; - $ThisFileInfo['playtime_seconds'] = $atomstructure['duration'] / $atomstructure['time_scale']; - break; - - - case 'tkhd': // TracK HeaDer atom - $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); - $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); - $atomstructure['creation_time'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 4)); - $atomstructure['modify_time'] = getid3_lib::BigEndian2Int(substr($atomdata, 8, 4)); - $atomstructure['trackid'] = getid3_lib::BigEndian2Int(substr($atomdata, 12, 4)); - $atomstructure['reserved1'] = getid3_lib::BigEndian2Int(substr($atomdata, 16, 4)); - $atomstructure['duration'] = getid3_lib::BigEndian2Int(substr($atomdata, 20, 4)); - $atomstructure['reserved2'] = getid3_lib::BigEndian2Int(substr($atomdata, 24, 8)); - $atomstructure['layer'] = getid3_lib::BigEndian2Int(substr($atomdata, 32, 2)); - $atomstructure['alternate_group'] = getid3_lib::BigEndian2Int(substr($atomdata, 34, 2)); - $atomstructure['volume'] = getid3_lib::FixedPoint8_8(substr($atomdata, 36, 2)); - $atomstructure['reserved3'] = getid3_lib::BigEndian2Int(substr($atomdata, 38, 2)); - $atomstructure['matrix_a'] = getid3_lib::FixedPoint16_16(substr($atomdata, 40, 4)); - $atomstructure['matrix_b'] = getid3_lib::FixedPoint16_16(substr($atomdata, 44, 4)); - $atomstructure['matrix_u'] = getid3_lib::FixedPoint16_16(substr($atomdata, 48, 4)); - $atomstructure['matrix_c'] = getid3_lib::FixedPoint16_16(substr($atomdata, 52, 4)); - $atomstructure['matrix_v'] = getid3_lib::FixedPoint16_16(substr($atomdata, 56, 4)); - $atomstructure['matrix_d'] = getid3_lib::FixedPoint16_16(substr($atomdata, 60, 4)); - $atomstructure['matrix_x'] = getid3_lib::FixedPoint2_30(substr($atomdata, 64, 4)); - $atomstructure['matrix_y'] = getid3_lib::FixedPoint2_30(substr($atomdata, 68, 4)); - $atomstructure['matrix_w'] = getid3_lib::FixedPoint2_30(substr($atomdata, 72, 4)); - $atomstructure['width'] = getid3_lib::FixedPoint16_16(substr($atomdata, 76, 4)); - $atomstructure['height'] = getid3_lib::FixedPoint16_16(substr($atomdata, 80, 4)); - - $atomstructure['flags']['enabled'] = (bool) ($atomstructure['flags_raw'] & 0x0001); - $atomstructure['flags']['in_movie'] = (bool) ($atomstructure['flags_raw'] & 0x0002); - $atomstructure['flags']['in_preview'] = (bool) ($atomstructure['flags_raw'] & 0x0004); - $atomstructure['flags']['in_poster'] = (bool) ($atomstructure['flags_raw'] & 0x0008); - $atomstructure['creation_time_unix'] = getid3_lib::DateMac2Unix($atomstructure['creation_time']); - $atomstructure['modify_time_unix'] = getid3_lib::DateMac2Unix($atomstructure['modify_time']); - - if (!isset($ThisFileInfo['video']['resolution_x']) || !isset($ThisFileInfo['video']['resolution_y'])) { - $ThisFileInfo['video']['resolution_x'] = $atomstructure['width']; - $ThisFileInfo['video']['resolution_y'] = $atomstructure['height']; - } - if ($atomstructure['flags']['enabled'] == 1) { - $ThisFileInfo['video']['resolution_x'] = max($ThisFileInfo['video']['resolution_x'], $atomstructure['width']); - $ThisFileInfo['video']['resolution_y'] = max($ThisFileInfo['video']['resolution_y'], $atomstructure['height']); - } - if (!empty($ThisFileInfo['video']['resolution_x']) && !empty($ThisFileInfo['video']['resolution_y'])) { - $ThisFileInfo['quicktime']['video']['resolution_x'] = $ThisFileInfo['video']['resolution_x']; - $ThisFileInfo['quicktime']['video']['resolution_y'] = $ThisFileInfo['video']['resolution_y']; - } else { - unset($ThisFileInfo['video']['resolution_x']); - unset($ThisFileInfo['video']['resolution_y']); - unset($ThisFileInfo['quicktime']['video']); - } - break; - - - case 'meta': // METAdata atom - // http://www.geocities.com/xhelmboyx/quicktime/formats/qti-layout.txt - $NextTagPosition = strpos($atomdata, '©'); - while ($NextTagPosition < strlen($atomdata)) { - $metaItemSize = getid3_lib::BigEndian2Int(substr($atomdata, $NextTagPosition - 4, 4)) - 4; - if ($metaItemSize == -4) { - break; - } - $metaItemRaw = substr($atomdata, $NextTagPosition, $metaItemSize); - $metaItemKey = substr($metaItemRaw, 0, 4); - $metaItemData = substr($metaItemRaw, 20); - $NextTagPosition += $metaItemSize + 4; - - $this->CopyToAppropriateCommentsSection($metaItemKey, $metaItemData, $ThisFileInfo); - } - break; - - case 'ftyp': // FileTYPe (?) atom (for MP4 it seems) - $atomstructure['signature'] = substr($atomdata, 0, 4); - $atomstructure['unknown_1'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 4)); - $atomstructure['fourcc'] = substr($atomdata, 8, 4); - break; - - case 'mdat': // Media DATa atom - case 'free': // FREE space atom - case 'skip': // SKIP atom - case 'wide': // 64-bit expansion placeholder atom - // 'mdat' data is too big to deal with, contains no useful metadata - // 'free', 'skip' and 'wide' are just padding, contains no useful data at all - - // When writing QuickTime files, it is sometimes necessary to update an atom's size. - // It is impossible to update a 32-bit atom to a 64-bit atom since the 32-bit atom - // is only 8 bytes in size, and the 64-bit atom requires 16 bytes. Therefore, QuickTime - // puts an 8-byte placeholder atom before any atoms it may have to update the size of. - // In this way, if the atom needs to be converted from a 32-bit to a 64-bit atom, the - // placeholder atom can be overwritten to obtain the necessary 8 extra bytes. - // The placeholder atom has a type of kWideAtomPlaceholderType ( 'wide' ). - break; - - - case 'nsav': // NoSAVe atom - // http://developer.apple.com/technotes/tn/tn2038.html - $atomstructure['data'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 4)); - break; - - case 'ctyp': // Controller TYPe atom (seen on QTVR) - // http://homepages.slingshot.co.nz/~helmboy/quicktime/formats/qtm-layout.txt - // some controller names are: - // 0x00 + 'std' for linear movie - // 'none' for no controls - $atomstructure['ctyp'] = substr($atomdata, 0, 4); - switch ($atomstructure['ctyp']) { - case 'qtvr': - $ThisFileInfo['video']['dataformat'] = 'quicktimevr'; - break; - } - break; - - case 'pano': // PANOrama track (seen on QTVR) - $atomstructure['pano'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 4)); - break; - - case 'hint': // HINT track - case 'hinf': // - case 'hinv': // - case 'hnti': // - $ThisFileInfo['quicktime']['hinting'] = true; - break; - - case 'imgt': // IMaGe Track reference (kQTVRImageTrackRefType) (seen on QTVR) - for ($i = 0; $i < ($atomstructure['size'] - 8); $i += 4) { - $atomstructure['imgt'][] = getid3_lib::BigEndian2Int(substr($atomdata, $i, 4)); - } - break; - - case 'FXTC': // Something to do with Adobe After Effects (?) - case 'PrmA': - case 'code': - case 'FIEL': // this is NOT "fiel" (Field Ordering) as describe here: http://developer.apple.com/documentation/QuickTime/QTFF/QTFFChap3/chapter_4_section_2.html - // Observed-but-not-handled atom types are just listed here - // to prevent warnings being generated - $atomstructure['data'] = $atomdata; - break; - - default: - $ThisFileInfo['warning'][] = 'Unknown QuickTime atom type: "'.$atomname.'" at offset '.$baseoffset; - $atomstructure['data'] = $atomdata; - break; - } - array_pop($atomHierarchy); - return $atomstructure; - } - - function QuicktimeParseContainerAtom($atomdata, &$ThisFileInfo, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) { - $atomstructure = false; - $subatomoffset = 0; - $subatomcounter = 0; - if ((strlen($atomdata) == 4) && (getid3_lib::BigEndian2Int($atomdata) == 0x00000000)) { - return false; - } - while ($subatomoffset < strlen($atomdata)) { - $subatomsize = getid3_lib::BigEndian2Int(substr($atomdata, $subatomoffset + 0, 4)); - $subatomname = substr($atomdata, $subatomoffset + 4, 4); - $subatomdata = substr($atomdata, $subatomoffset + 8, $subatomsize - 8); - if ($subatomsize == 0) { - // Furthermore, for historical reasons the list of atoms is optionally - // terminated by a 32-bit integer set to 0. If you are writing a program - // to read user data atoms, you should allow for the terminating 0. - return $atomstructure; - } - - $atomstructure[$subatomcounter] = $this->QuicktimeParseAtom($subatomname, $subatomsize, $subatomdata, $ThisFileInfo, $baseoffset + $subatomoffset, $atomHierarchy, $ParseAllPossibleAtoms); - - $subatomoffset += $subatomsize; - $subatomcounter++; - } - return $atomstructure; - } - - - function QuicktimeLanguageLookup($languageid) { - static $QuicktimeLanguageLookup = array(); - if (empty($QuicktimeLanguageLookup)) { - $QuicktimeLanguageLookup[0] = 'English'; - $QuicktimeLanguageLookup[1] = 'French'; - $QuicktimeLanguageLookup[2] = 'German'; - $QuicktimeLanguageLookup[3] = 'Italian'; - $QuicktimeLanguageLookup[4] = 'Dutch'; - $QuicktimeLanguageLookup[5] = 'Swedish'; - $QuicktimeLanguageLookup[6] = 'Spanish'; - $QuicktimeLanguageLookup[7] = 'Danish'; - $QuicktimeLanguageLookup[8] = 'Portuguese'; - $QuicktimeLanguageLookup[9] = 'Norwegian'; - $QuicktimeLanguageLookup[10] = 'Hebrew'; - $QuicktimeLanguageLookup[11] = 'Japanese'; - $QuicktimeLanguageLookup[12] = 'Arabic'; - $QuicktimeLanguageLookup[13] = 'Finnish'; - $QuicktimeLanguageLookup[14] = 'Greek'; - $QuicktimeLanguageLookup[15] = 'Icelandic'; - $QuicktimeLanguageLookup[16] = 'Maltese'; - $QuicktimeLanguageLookup[17] = 'Turkish'; - $QuicktimeLanguageLookup[18] = 'Croatian'; - $QuicktimeLanguageLookup[19] = 'Chinese (Traditional)'; - $QuicktimeLanguageLookup[20] = 'Urdu'; - $QuicktimeLanguageLookup[21] = 'Hindi'; - $QuicktimeLanguageLookup[22] = 'Thai'; - $QuicktimeLanguageLookup[23] = 'Korean'; - $QuicktimeLanguageLookup[24] = 'Lithuanian'; - $QuicktimeLanguageLookup[25] = 'Polish'; - $QuicktimeLanguageLookup[26] = 'Hungarian'; - $QuicktimeLanguageLookup[27] = 'Estonian'; - $QuicktimeLanguageLookup[28] = 'Lettish'; - $QuicktimeLanguageLookup[28] = 'Latvian'; - $QuicktimeLanguageLookup[29] = 'Saamisk'; - $QuicktimeLanguageLookup[29] = 'Lappish'; - $QuicktimeLanguageLookup[30] = 'Faeroese'; - $QuicktimeLanguageLookup[31] = 'Farsi'; - $QuicktimeLanguageLookup[31] = 'Persian'; - $QuicktimeLanguageLookup[32] = 'Russian'; - $QuicktimeLanguageLookup[33] = 'Chinese (Simplified)'; - $QuicktimeLanguageLookup[34] = 'Flemish'; - $QuicktimeLanguageLookup[35] = 'Irish'; - $QuicktimeLanguageLookup[36] = 'Albanian'; - $QuicktimeLanguageLookup[37] = 'Romanian'; - $QuicktimeLanguageLookup[38] = 'Czech'; - $QuicktimeLanguageLookup[39] = 'Slovak'; - $QuicktimeLanguageLookup[40] = 'Slovenian'; - $QuicktimeLanguageLookup[41] = 'Yiddish'; - $QuicktimeLanguageLookup[42] = 'Serbian'; - $QuicktimeLanguageLookup[43] = 'Macedonian'; - $QuicktimeLanguageLookup[44] = 'Bulgarian'; - $QuicktimeLanguageLookup[45] = 'Ukrainian'; - $QuicktimeLanguageLookup[46] = 'Byelorussian'; - $QuicktimeLanguageLookup[47] = 'Uzbek'; - $QuicktimeLanguageLookup[48] = 'Kazakh'; - $QuicktimeLanguageLookup[49] = 'Azerbaijani'; - $QuicktimeLanguageLookup[50] = 'AzerbaijanAr'; - $QuicktimeLanguageLookup[51] = 'Armenian'; - $QuicktimeLanguageLookup[52] = 'Georgian'; - $QuicktimeLanguageLookup[53] = 'Moldavian'; - $QuicktimeLanguageLookup[54] = 'Kirghiz'; - $QuicktimeLanguageLookup[55] = 'Tajiki'; - $QuicktimeLanguageLookup[56] = 'Turkmen'; - $QuicktimeLanguageLookup[57] = 'Mongolian'; - $QuicktimeLanguageLookup[58] = 'MongolianCyr'; - $QuicktimeLanguageLookup[59] = 'Pashto'; - $QuicktimeLanguageLookup[60] = 'Kurdish'; - $QuicktimeLanguageLookup[61] = 'Kashmiri'; - $QuicktimeLanguageLookup[62] = 'Sindhi'; - $QuicktimeLanguageLookup[63] = 'Tibetan'; - $QuicktimeLanguageLookup[64] = 'Nepali'; - $QuicktimeLanguageLookup[65] = 'Sanskrit'; - $QuicktimeLanguageLookup[66] = 'Marathi'; - $QuicktimeLanguageLookup[67] = 'Bengali'; - $QuicktimeLanguageLookup[68] = 'Assamese'; - $QuicktimeLanguageLookup[69] = 'Gujarati'; - $QuicktimeLanguageLookup[70] = 'Punjabi'; - $QuicktimeLanguageLookup[71] = 'Oriya'; - $QuicktimeLanguageLookup[72] = 'Malayalam'; - $QuicktimeLanguageLookup[73] = 'Kannada'; - $QuicktimeLanguageLookup[74] = 'Tamil'; - $QuicktimeLanguageLookup[75] = 'Telugu'; - $QuicktimeLanguageLookup[76] = 'Sinhalese'; - $QuicktimeLanguageLookup[77] = 'Burmese'; - $QuicktimeLanguageLookup[78] = 'Khmer'; - $QuicktimeLanguageLookup[79] = 'Lao'; - $QuicktimeLanguageLookup[80] = 'Vietnamese'; - $QuicktimeLanguageLookup[81] = 'Indonesian'; - $QuicktimeLanguageLookup[82] = 'Tagalog'; - $QuicktimeLanguageLookup[83] = 'MalayRoman'; - $QuicktimeLanguageLookup[84] = 'MalayArabic'; - $QuicktimeLanguageLookup[85] = 'Amharic'; - $QuicktimeLanguageLookup[86] = 'Tigrinya'; - $QuicktimeLanguageLookup[87] = 'Galla'; - $QuicktimeLanguageLookup[87] = 'Oromo'; - $QuicktimeLanguageLookup[88] = 'Somali'; - $QuicktimeLanguageLookup[89] = 'Swahili'; - $QuicktimeLanguageLookup[90] = 'Ruanda'; - $QuicktimeLanguageLookup[91] = 'Rundi'; - $QuicktimeLanguageLookup[92] = 'Chewa'; - $QuicktimeLanguageLookup[93] = 'Malagasy'; - $QuicktimeLanguageLookup[94] = 'Esperanto'; - $QuicktimeLanguageLookup[128] = 'Welsh'; - $QuicktimeLanguageLookup[129] = 'Basque'; - $QuicktimeLanguageLookup[130] = 'Catalan'; - $QuicktimeLanguageLookup[131] = 'Latin'; - $QuicktimeLanguageLookup[132] = 'Quechua'; - $QuicktimeLanguageLookup[133] = 'Guarani'; - $QuicktimeLanguageLookup[134] = 'Aymara'; - $QuicktimeLanguageLookup[135] = 'Tatar'; - $QuicktimeLanguageLookup[136] = 'Uighur'; - $QuicktimeLanguageLookup[137] = 'Dzongkha'; - $QuicktimeLanguageLookup[138] = 'JavaneseRom'; - } - return (isset($QuicktimeLanguageLookup[$languageid]) ? $QuicktimeLanguageLookup[$languageid] : 'invalid'); - } - - function QuicktimeVideoCodecLookup($codecid) { - static $QuicktimeVideoCodecLookup = array(); - if (empty($QuicktimeVideoCodecLookup)) { - $QuicktimeVideoCodecLookup['3IVX'] = '3ivx MPEG-4'; - $QuicktimeVideoCodecLookup['3IV1'] = '3ivx MPEG-4 v1'; - $QuicktimeVideoCodecLookup['3IV2'] = '3ivx MPEG-4 v2'; - $QuicktimeVideoCodecLookup['avr '] = 'AVR-JPEG'; - $QuicktimeVideoCodecLookup['base'] = 'Base'; - $QuicktimeVideoCodecLookup['WRLE'] = 'BMP'; - $QuicktimeVideoCodecLookup['cvid'] = 'Cinepak'; - $QuicktimeVideoCodecLookup['clou'] = 'Cloud'; - $QuicktimeVideoCodecLookup['cmyk'] = 'CMYK'; - $QuicktimeVideoCodecLookup['yuv2'] = 'ComponentVideo'; - $QuicktimeVideoCodecLookup['yuvu'] = 'ComponentVideoSigned'; - $QuicktimeVideoCodecLookup['yuvs'] = 'ComponentVideoUnsigned'; - $QuicktimeVideoCodecLookup['dvc '] = 'DVC-NTSC'; - $QuicktimeVideoCodecLookup['dvcp'] = 'DVC-PAL'; - $QuicktimeVideoCodecLookup['dvpn'] = 'DVCPro-NTSC'; - $QuicktimeVideoCodecLookup['dvpp'] = 'DVCPro-PAL'; - $QuicktimeVideoCodecLookup['fire'] = 'Fire'; - $QuicktimeVideoCodecLookup['flic'] = 'FLC'; - $QuicktimeVideoCodecLookup['b48r'] = '48RGB'; - $QuicktimeVideoCodecLookup['gif '] = 'GIF'; - $QuicktimeVideoCodecLookup['smc '] = 'Graphics'; - $QuicktimeVideoCodecLookup['h261'] = 'H261'; - $QuicktimeVideoCodecLookup['h263'] = 'H263'; - $QuicktimeVideoCodecLookup['IV41'] = 'Indeo4'; - $QuicktimeVideoCodecLookup['jpeg'] = 'JPEG'; - $QuicktimeVideoCodecLookup['PNTG'] = 'MacPaint'; - $QuicktimeVideoCodecLookup['msvc'] = 'Microsoft Video1'; - $QuicktimeVideoCodecLookup['mjpa'] = 'Motion JPEG-A'; - $QuicktimeVideoCodecLookup['mjpb'] = 'Motion JPEG-B'; - $QuicktimeVideoCodecLookup['myuv'] = 'MPEG YUV420'; - $QuicktimeVideoCodecLookup['dmb1'] = 'OpenDML JPEG'; - $QuicktimeVideoCodecLookup['kpcd'] = 'PhotoCD'; - $QuicktimeVideoCodecLookup['8BPS'] = 'Planar RGB'; - $QuicktimeVideoCodecLookup['png '] = 'PNG'; - $QuicktimeVideoCodecLookup['qdrw'] = 'QuickDraw'; - $QuicktimeVideoCodecLookup['qdgx'] = 'QuickDrawGX'; - $QuicktimeVideoCodecLookup['raw '] = 'RAW'; - $QuicktimeVideoCodecLookup['.SGI'] = 'SGI'; - $QuicktimeVideoCodecLookup['b16g'] = '16Gray'; - $QuicktimeVideoCodecLookup['b64a'] = '64ARGB'; - $QuicktimeVideoCodecLookup['SVQ1'] = 'Sorenson Video 1'; - $QuicktimeVideoCodecLookup['SVQ1'] = 'Sorenson Video 3'; - $QuicktimeVideoCodecLookup['syv9'] = 'Sorenson YUV9'; - $QuicktimeVideoCodecLookup['tga '] = 'Targa'; - $QuicktimeVideoCodecLookup['b32a'] = '32AlphaGray'; - $QuicktimeVideoCodecLookup['tiff'] = 'TIFF'; - $QuicktimeVideoCodecLookup['path'] = 'Vector'; - $QuicktimeVideoCodecLookup['rpza'] = 'Video'; - $QuicktimeVideoCodecLookup['ripl'] = 'WaterRipple'; - $QuicktimeVideoCodecLookup['WRAW'] = 'Windows RAW'; - $QuicktimeVideoCodecLookup['y420'] = 'YUV420'; - } - return (isset($QuicktimeVideoCodecLookup[$codecid]) ? $QuicktimeVideoCodecLookup[$codecid] : ''); - } - - function QuicktimeAudioCodecLookup($codecid) { - static $QuicktimeAudioCodecLookup = array(); - if (empty($QuicktimeAudioCodecLookup)) { - $QuicktimeAudioCodecLookup['.mp3'] = 'Fraunhofer MPEG Layer-III alias'; - $QuicktimeAudioCodecLookup['aac '] = 'ISO/IEC 14496-3 AAC'; - $QuicktimeAudioCodecLookup['agsm'] = 'Apple GSM 10:1'; - $QuicktimeAudioCodecLookup['alac'] = 'Apple Lossless Audio Codec'; - $QuicktimeAudioCodecLookup['alaw'] = 'A-law 2:1'; - $QuicktimeAudioCodecLookup['conv'] = 'Sample Format'; - $QuicktimeAudioCodecLookup['dvca'] = 'DV'; - $QuicktimeAudioCodecLookup['dvi '] = 'DV 4:1'; - $QuicktimeAudioCodecLookup['eqal'] = 'Frequency Equalizer'; - $QuicktimeAudioCodecLookup['fl32'] = '32-bit Floating Point'; - $QuicktimeAudioCodecLookup['fl64'] = '64-bit Floating Point'; - $QuicktimeAudioCodecLookup['ima4'] = 'Interactive Multimedia Association 4:1'; - $QuicktimeAudioCodecLookup['in24'] = '24-bit Integer'; - $QuicktimeAudioCodecLookup['in32'] = '32-bit Integer'; - $QuicktimeAudioCodecLookup['lpc '] = 'LPC 23:1'; - $QuicktimeAudioCodecLookup['MAC3'] = 'Macintosh Audio Compression/Expansion (MACE) 3:1'; - $QuicktimeAudioCodecLookup['MAC6'] = 'Macintosh Audio Compression/Expansion (MACE) 6:1'; - $QuicktimeAudioCodecLookup['mixb'] = '8-bit Mixer'; - $QuicktimeAudioCodecLookup['mixw'] = '16-bit Mixer'; - $QuicktimeAudioCodecLookup['mp4a'] = 'ISO/IEC 14496-3 AAC'; - $QuicktimeAudioCodecLookup['MS'."\x00\x02"] = 'Microsoft ADPCM'; - $QuicktimeAudioCodecLookup['MS'."\x00\x11"] = 'DV IMA'; - $QuicktimeAudioCodecLookup['MS'."\x00\x55"] = 'Fraunhofer MPEG Layer III'; - $QuicktimeAudioCodecLookup['NONE'] = 'No Encoding'; - $QuicktimeAudioCodecLookup['Qclp'] = 'Qualcomm PureVoice'; - $QuicktimeAudioCodecLookup['QDM2'] = 'QDesign Music 2'; - $QuicktimeAudioCodecLookup['QDMC'] = 'QDesign Music 1'; - $QuicktimeAudioCodecLookup['ratb'] = '8-bit Rate'; - $QuicktimeAudioCodecLookup['ratw'] = '16-bit Rate'; - $QuicktimeAudioCodecLookup['raw '] = 'raw PCM'; - $QuicktimeAudioCodecLookup['sour'] = 'Sound Source'; - $QuicktimeAudioCodecLookup['sowt'] = 'signed/two\'s complement (Little Endian)'; - $QuicktimeAudioCodecLookup['str1'] = 'Iomega MPEG layer II'; - $QuicktimeAudioCodecLookup['str2'] = 'Iomega MPEG *layer II'; - $QuicktimeAudioCodecLookup['str3'] = 'Iomega MPEG **layer II'; - $QuicktimeAudioCodecLookup['str4'] = 'Iomega MPEG ***layer II'; - $QuicktimeAudioCodecLookup['twos'] = 'signed/two\'s complement (Big Endian)'; - $QuicktimeAudioCodecLookup['ulaw'] = 'mu-law 2:1'; - } - return (isset($QuicktimeAudioCodecLookup[$codecid]) ? $QuicktimeAudioCodecLookup[$codecid] : ''); - } - - function QuicktimeDCOMLookup($compressionid) { - static $QuicktimeDCOMLookup = array(); - if (empty($QuicktimeDCOMLookup)) { - $QuicktimeDCOMLookup['zlib'] = 'ZLib Deflate'; - $QuicktimeDCOMLookup['adec'] = 'Apple Compression'; - } - return (isset($QuicktimeDCOMLookup[$compressionid]) ? $QuicktimeDCOMLookup[$compressionid] : ''); - } - - function QuicktimeColorNameLookup($colordepthid) { - static $QuicktimeColorNameLookup = array(); - if (empty($QuicktimeColorNameLookup)) { - $QuicktimeColorNameLookup[1] = '2-color (monochrome)'; - $QuicktimeColorNameLookup[2] = '4-color'; - $QuicktimeColorNameLookup[4] = '16-color'; - $QuicktimeColorNameLookup[8] = '256-color'; - $QuicktimeColorNameLookup[16] = 'thousands (16-bit color)'; - $QuicktimeColorNameLookup[24] = 'millions (24-bit color)'; - $QuicktimeColorNameLookup[32] = 'millions+ (32-bit color)'; - $QuicktimeColorNameLookup[33] = 'black & white'; - $QuicktimeColorNameLookup[34] = '4-gray'; - $QuicktimeColorNameLookup[36] = '16-gray'; - $QuicktimeColorNameLookup[40] = '256-gray'; - } - return (isset($QuicktimeColorNameLookup[$colordepthid]) ? $QuicktimeColorNameLookup[$colordepthid] : 'invalid'); - } - - function CopyToAppropriateCommentsSection($keyname, $data, &$ThisFileInfo) { - static $handyatomtranslatorarray = array(); - if (empty($handyatomtranslatorarray)) { - $handyatomtranslatorarray['©cpy'] = 'copyright'; - $handyatomtranslatorarray['©day'] = 'creation_date'; - $handyatomtranslatorarray['©dir'] = 'director'; - $handyatomtranslatorarray['©ed1'] = 'edit1'; - $handyatomtranslatorarray['©ed2'] = 'edit2'; - $handyatomtranslatorarray['©ed3'] = 'edit3'; - $handyatomtranslatorarray['©ed4'] = 'edit4'; - $handyatomtranslatorarray['©ed5'] = 'edit5'; - $handyatomtranslatorarray['©ed6'] = 'edit6'; - $handyatomtranslatorarray['©ed7'] = 'edit7'; - $handyatomtranslatorarray['©ed8'] = 'edit8'; - $handyatomtranslatorarray['©ed9'] = 'edit9'; - $handyatomtranslatorarray['©fmt'] = 'format'; - $handyatomtranslatorarray['©inf'] = 'information'; - $handyatomtranslatorarray['©prd'] = 'producer'; - $handyatomtranslatorarray['©prf'] = 'performers'; - $handyatomtranslatorarray['©req'] = 'system_requirements'; - $handyatomtranslatorarray['©src'] = 'source_credit'; - $handyatomtranslatorarray['©wrt'] = 'writer'; - - // http://www.geocities.com/xhelmboyx/quicktime/formats/qtm-layout.txt - $handyatomtranslatorarray['©nam'] = 'title'; - $handyatomtranslatorarray['©cmt'] = 'comment'; - $handyatomtranslatorarray['©wrn'] = 'warning'; - $handyatomtranslatorarray['©hst'] = 'host_computer'; - $handyatomtranslatorarray['©mak'] = 'make'; - $handyatomtranslatorarray['©mod'] = 'model'; - $handyatomtranslatorarray['©PRD'] = 'product'; - $handyatomtranslatorarray['©swr'] = 'software'; - $handyatomtranslatorarray['©aut'] = 'author'; - $handyatomtranslatorarray['©ART'] = 'artist'; - $handyatomtranslatorarray['©trk'] = 'track'; - $handyatomtranslatorarray['©alb'] = 'album'; - $handyatomtranslatorarray['©com'] = 'comment'; - $handyatomtranslatorarray['©gen'] = 'genre'; - $handyatomtranslatorarray['©ope'] = 'composer'; - $handyatomtranslatorarray['©url'] = 'url'; - $handyatomtranslatorarray['©enc'] = 'encoder'; - } - if (isset($handyatomtranslatorarray[$keyname])) { - $ThisFileInfo['quicktime']['comments'][$handyatomtranslatorarray[$keyname]][] = $data; - } - - return true; - } - - function NoNullString($nullterminatedstring) { - // remove the single null terminator on null terminated strings - if (substr($nullterminatedstring, strlen($nullterminatedstring) - 1, 1) === "\x00") { - return substr($nullterminatedstring, 0, strlen($nullterminatedstring) - 1); - } - return $nullterminatedstring; - } - - function Pascal2String($pascalstring) { - // Pascal strings have 1 unsigned byte at the beginning saying how many chars (1-255) are in the string - return substr($pascalstring, 1); - } - -} - -?> \ No newline at end of file diff --git a/getid3/getid3/module.audio-video.real.php b/getid3/getid3/module.audio-video.real.php deleted file mode 100644 index 23c8fef..0000000 --- a/getid3/getid3/module.audio-video.real.php +++ /dev/null @@ -1,528 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.audio-video.real.php // -// module for analyzing Real Audio/Video files // -// dependencies: module.audio-video.riff.php // -// /// -///////////////////////////////////////////////////////////////// - -getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true); - -class getid3_real -{ - - function getid3_real(&$fd, &$ThisFileInfo) { - $ThisFileInfo['fileformat'] = 'real'; - $ThisFileInfo['bitrate'] = 0; - $ThisFileInfo['playtime_seconds'] = 0; - - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $ChunkCounter = 0; - while (ftell($fd) < $ThisFileInfo['avdataend']) { - $ChunkData = fread($fd, 8); - $ChunkName = substr($ChunkData, 0, 4); - $ChunkSize = getid3_lib::BigEndian2Int(substr($ChunkData, 4, 4)); - - if ($ChunkName == '.ra'."\xFD") { - $ChunkData .= fread($fd, $ChunkSize - 8); - if ($this->ParseOldRAheader(substr($ChunkData, 0, 128), $ThisFileInfo['real']['old_ra_header'])) { - $ThisFileInfo['audio']['dataformat'] = 'real'; - $ThisFileInfo['audio']['lossless'] = false; - $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['real']['old_ra_header']['sample_rate']; - $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['real']['old_ra_header']['bits_per_sample']; - $ThisFileInfo['audio']['channels'] = $ThisFileInfo['real']['old_ra_header']['channels']; - - $ThisFileInfo['playtime_seconds'] = 60 * ($ThisFileInfo['real']['old_ra_header']['audio_bytes'] / $ThisFileInfo['real']['old_ra_header']['bytes_per_minute']); - $ThisFileInfo['audio']['bitrate'] = 8 * ($ThisFileInfo['real']['old_ra_header']['audio_bytes'] / $ThisFileInfo['playtime_seconds']); - $ThisFileInfo['audio']['codec'] = $this->RealAudioCodecFourCClookup($ThisFileInfo['real']['old_ra_header']['fourcc'], $ThisFileInfo['audio']['bitrate']); - - foreach ($ThisFileInfo['real']['old_ra_header']['comments'] as $key => $valuearray) { - if (strlen(trim($valuearray[0])) > 0) { - $ThisFileInfo['real']['comments'][$key][] = trim($valuearray[0]); - } - } - return true; - } - $ThisFileInfo['error'][] = 'There was a problem parsing this RealAudio file. Please submit it for analysis to http://www.getid3.org/upload/ or info@getid3.org'; - unset($ThisFileInfo['bitrate']); - unset($ThisFileInfo['playtime_seconds']); - return false; - } - - // shortcut - $ThisFileInfo['real']['chunks'][$ChunkCounter] = array(); - $thisfile_real_chunks_currentchunk = &$ThisFileInfo['real']['chunks'][$ChunkCounter]; - - $thisfile_real_chunks_currentchunk['name'] = $ChunkName; - $thisfile_real_chunks_currentchunk['offset'] = ftell($fd) - 8; - $thisfile_real_chunks_currentchunk['length'] = $ChunkSize; - if (($thisfile_real_chunks_currentchunk['offset'] + $thisfile_real_chunks_currentchunk['length']) > $ThisFileInfo['avdataend']) { - $ThisFileInfo['warning'][] = 'Chunk "'.$thisfile_real_chunks_currentchunk['name'].'" at offset '.$thisfile_real_chunks_currentchunk['offset'].' claims to be '.$thisfile_real_chunks_currentchunk['length'].' bytes long, which is beyond end of file'; - return false; - } - - if ($ChunkSize > (GETID3_FREAD_BUFFER_SIZE + 8)) { - - $ChunkData .= fread($fd, GETID3_FREAD_BUFFER_SIZE - 8); - fseek($fd, $thisfile_real_chunks_currentchunk['offset'] + $ChunkSize, SEEK_SET); - - } else { - - $ChunkData .= fread($fd, $ChunkSize - 8); - - } - $offset = 8; - - switch ($ChunkName) { - - case '.RMF': // RealMedia File Header - $thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2)); - $offset += 2; - switch ($thisfile_real_chunks_currentchunk['object_version']) { - - case 0: - $thisfile_real_chunks_currentchunk['file_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); - $offset += 4; - $thisfile_real_chunks_currentchunk['headers_count'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); - $offset += 4; - break; - - default: - //$ThisFileInfo['warning'][] = 'Expected .RMF-object_version to be "0", actual value is "'.$thisfile_real_chunks_currentchunk['object_version'].'" (should not be a problem)'; - break; - - } - break; - - - case 'PROP': // Properties Header - $thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2)); - $offset += 2; - if ($thisfile_real_chunks_currentchunk['object_version'] == 0) { - $thisfile_real_chunks_currentchunk['max_bit_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); - $offset += 4; - $thisfile_real_chunks_currentchunk['avg_bit_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); - $offset += 4; - $thisfile_real_chunks_currentchunk['max_packet_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); - $offset += 4; - $thisfile_real_chunks_currentchunk['avg_packet_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); - $offset += 4; - $thisfile_real_chunks_currentchunk['num_packets'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); - $offset += 4; - $thisfile_real_chunks_currentchunk['duration'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); - $offset += 4; - $thisfile_real_chunks_currentchunk['preroll'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); - $offset += 4; - $thisfile_real_chunks_currentchunk['index_offset'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); - $offset += 4; - $thisfile_real_chunks_currentchunk['data_offset'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); - $offset += 4; - $thisfile_real_chunks_currentchunk['num_streams'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2)); - $offset += 2; - $thisfile_real_chunks_currentchunk['flags_raw'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2)); - $offset += 2; - $ThisFileInfo['playtime_seconds'] = $thisfile_real_chunks_currentchunk['duration'] / 1000; - if ($thisfile_real_chunks_currentchunk['duration'] > 0) { - $ThisFileInfo['bitrate'] += $thisfile_real_chunks_currentchunk['avg_bit_rate']; - } - $thisfile_real_chunks_currentchunk['flags']['save_enabled'] = (bool) ($thisfile_real_chunks_currentchunk['flags_raw'] & 0x0001); - $thisfile_real_chunks_currentchunk['flags']['perfect_play'] = (bool) ($thisfile_real_chunks_currentchunk['flags_raw'] & 0x0002); - $thisfile_real_chunks_currentchunk['flags']['live_broadcast'] = (bool) ($thisfile_real_chunks_currentchunk['flags_raw'] & 0x0004); - } - break; - - case 'MDPR': // Media Properties Header - $thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2)); - $offset += 2; - if ($thisfile_real_chunks_currentchunk['object_version'] == 0) { - $thisfile_real_chunks_currentchunk['stream_number'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2)); - $offset += 2; - $thisfile_real_chunks_currentchunk['max_bit_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); - $offset += 4; - $thisfile_real_chunks_currentchunk['avg_bit_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); - $offset += 4; - $thisfile_real_chunks_currentchunk['max_packet_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); - $offset += 4; - $thisfile_real_chunks_currentchunk['avg_packet_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); - $offset += 4; - $thisfile_real_chunks_currentchunk['start_time'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); - $offset += 4; - $thisfile_real_chunks_currentchunk['preroll'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); - $offset += 4; - $thisfile_real_chunks_currentchunk['duration'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); - $offset += 4; - $thisfile_real_chunks_currentchunk['stream_name_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 1)); - $offset += 1; - $thisfile_real_chunks_currentchunk['stream_name'] = substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['stream_name_size']); - $offset += $thisfile_real_chunks_currentchunk['stream_name_size']; - $thisfile_real_chunks_currentchunk['mime_type_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 1)); - $offset += 1; - $thisfile_real_chunks_currentchunk['mime_type'] = substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['mime_type_size']); - $offset += $thisfile_real_chunks_currentchunk['mime_type_size']; - $thisfile_real_chunks_currentchunk['type_specific_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); - $offset += 4; - $thisfile_real_chunks_currentchunk['type_specific_data'] = substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['type_specific_len']); - $offset += $thisfile_real_chunks_currentchunk['type_specific_len']; - - // shortcut - $thisfile_real_chunks_currentchunk_typespecificdata = &$thisfile_real_chunks_currentchunk['type_specific_data']; - - switch ($thisfile_real_chunks_currentchunk['mime_type']) { - case 'video/x-pn-realvideo': - case 'video/x-pn-multirate-realvideo': - // http://www.freelists.org/archives/matroska-devel/07-2003/msg00010.html - - // shortcut - $thisfile_real_chunks_currentchunk['video_info'] = array(); - $thisfile_real_chunks_currentchunk_videoinfo = &$thisfile_real_chunks_currentchunk['video_info']; - - $thisfile_real_chunks_currentchunk_videoinfo['dwSize'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 0, 4)); - $thisfile_real_chunks_currentchunk_videoinfo['fourcc1'] = substr($thisfile_real_chunks_currentchunk_typespecificdata, 4, 4); - $thisfile_real_chunks_currentchunk_videoinfo['fourcc2'] = substr($thisfile_real_chunks_currentchunk_typespecificdata, 8, 4); - $thisfile_real_chunks_currentchunk_videoinfo['width'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 12, 2)); - $thisfile_real_chunks_currentchunk_videoinfo['height'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 14, 2)); - $thisfile_real_chunks_currentchunk_videoinfo['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 16, 2)); - //$thisfile_real_chunks_currentchunk_videoinfo['unknown1'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 18, 2)); - //$thisfile_real_chunks_currentchunk_videoinfo['unknown2'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 20, 2)); - $thisfile_real_chunks_currentchunk_videoinfo['frames_per_second'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 22, 2)); - //$thisfile_real_chunks_currentchunk_videoinfo['unknown3'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 24, 2)); - //$thisfile_real_chunks_currentchunk_videoinfo['unknown4'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 26, 2)); - //$thisfile_real_chunks_currentchunk_videoinfo['unknown5'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 28, 2)); - //$thisfile_real_chunks_currentchunk_videoinfo['unknown6'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 30, 2)); - //$thisfile_real_chunks_currentchunk_videoinfo['unknown7'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 32, 2)); - //$thisfile_real_chunks_currentchunk_videoinfo['unknown8'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 34, 2)); - //$thisfile_real_chunks_currentchunk_videoinfo['unknown9'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 36, 2)); - - $thisfile_real_chunks_currentchunk_videoinfo['codec'] = getid3_riff::RIFFfourccLookup($thisfile_real_chunks_currentchunk_videoinfo['fourcc2']); - - $ThisFileInfo['video']['resolution_x'] = $thisfile_real_chunks_currentchunk_videoinfo['width']; - $ThisFileInfo['video']['resolution_y'] = $thisfile_real_chunks_currentchunk_videoinfo['height']; - $ThisFileInfo['video']['frame_rate'] = (float) $thisfile_real_chunks_currentchunk_videoinfo['frames_per_second']; - $ThisFileInfo['video']['codec'] = $thisfile_real_chunks_currentchunk_videoinfo['codec']; - $ThisFileInfo['video']['bits_per_sample'] = $thisfile_real_chunks_currentchunk_videoinfo['bits_per_sample']; - break; - - case 'audio/x-pn-realaudio': - case 'audio/x-pn-multirate-realaudio': - $this->ParseOldRAheader($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk['parsed_audio_data']); - - $ThisFileInfo['audio']['sample_rate'] = $thisfile_real_chunks_currentchunk['parsed_audio_data']['sample_rate']; - $ThisFileInfo['audio']['bits_per_sample'] = $thisfile_real_chunks_currentchunk['parsed_audio_data']['bits_per_sample']; - $ThisFileInfo['audio']['channels'] = $thisfile_real_chunks_currentchunk['parsed_audio_data']['channels']; - if (!empty($ThisFileInfo['audio']['dataformat'])) { - foreach ($ThisFileInfo['audio'] as $key => $value) { - if ($key != 'streams') { - $ThisFileInfo['audio']['streams'][$thisfile_real_chunks_currentchunk['stream_number']][$key] = $value; - } - } - } - break; - - case 'logical-fileinfo': - // shortcut - $thisfile_real_chunks_currentchunk['logical_fileinfo'] = array(); - $thisfile_real_chunks_currentchunk_logicalfileinfo = &$thisfile_real_chunks_currentchunk['logical_fileinfo']; - - $thisfile_real_chunks_currentchunk_logicalfileinfo_offset = 0; - $thisfile_real_chunks_currentchunk_logicalfileinfo['logical_fileinfo_length'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4)); - $thisfile_real_chunks_currentchunk_logicalfileinfo_offset += 4; - - //$thisfile_real_chunks_currentchunk_logicalfileinfo['unknown1'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4)); - $thisfile_real_chunks_currentchunk_logicalfileinfo_offset += 4; - - $thisfile_real_chunks_currentchunk_logicalfileinfo['num_tags'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4)); - $thisfile_real_chunks_currentchunk_logicalfileinfo_offset += 4; - - //$thisfile_real_chunks_currentchunk_logicalfileinfo['unknown2'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4)); - $thisfile_real_chunks_currentchunk_logicalfileinfo_offset += 4; - - //$thisfile_real_chunks_currentchunk_logicalfileinfo['d'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 1)); - - //$thisfile_real_chunks_currentchunk_logicalfileinfo['one_type'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4)); - //$thisfile_real_chunks_currentchunk_logicalfileinfo_thislength = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 4 + $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 2)); - //$thisfile_real_chunks_currentchunk_logicalfileinfo['one'] = substr($thisfile_real_chunks_currentchunk_typespecificdata, 6 + $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, $thisfile_real_chunks_currentchunk_logicalfileinfo_thislength); - //$thisfile_real_chunks_currentchunk_logicalfileinfo_offset += (6 + $thisfile_real_chunks_currentchunk_logicalfileinfo_thislength); - - break; - - } - - - if (empty($ThisFileInfo['playtime_seconds'])) { - $ThisFileInfo['playtime_seconds'] = max($ThisFileInfo['playtime_seconds'], ($thisfile_real_chunks_currentchunk['duration'] + $thisfile_real_chunks_currentchunk['start_time']) / 1000); - } - if ($thisfile_real_chunks_currentchunk['duration'] > 0) { - switch ($thisfile_real_chunks_currentchunk['mime_type']) { - case 'audio/x-pn-realaudio': - case 'audio/x-pn-multirate-realaudio': - $ThisFileInfo['audio']['bitrate'] = (isset($ThisFileInfo['audio']['bitrate']) ? $ThisFileInfo['audio']['bitrate'] : 0) + $thisfile_real_chunks_currentchunk['avg_bit_rate']; - $ThisFileInfo['audio']['codec'] = $this->RealAudioCodecFourCClookup($thisfile_real_chunks_currentchunk['parsed_audio_data']['fourcc'], $ThisFileInfo['audio']['bitrate']); - $ThisFileInfo['audio']['dataformat'] = 'real'; - $ThisFileInfo['audio']['lossless'] = false; - break; - - case 'video/x-pn-realvideo': - case 'video/x-pn-multirate-realvideo': - $ThisFileInfo['video']['bitrate'] = (isset($ThisFileInfo['video']['bitrate']) ? $ThisFileInfo['video']['bitrate'] : 0) + $thisfile_real_chunks_currentchunk['avg_bit_rate']; - $ThisFileInfo['video']['bitrate_mode'] = 'cbr'; - $ThisFileInfo['video']['dataformat'] = 'real'; - $ThisFileInfo['video']['lossless'] = false; - $ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1; - break; - - case 'audio/x-ralf-mpeg4-generic': - $ThisFileInfo['audio']['bitrate'] = (isset($ThisFileInfo['audio']['bitrate']) ? $ThisFileInfo['audio']['bitrate'] : 0) + $thisfile_real_chunks_currentchunk['avg_bit_rate']; - $ThisFileInfo['audio']['codec'] = 'RealAudio Lossless'; - $ThisFileInfo['audio']['dataformat'] = 'real'; - $ThisFileInfo['audio']['lossless'] = true; - break; - } - $ThisFileInfo['bitrate'] = (isset($ThisFileInfo['video']['bitrate']) ? $ThisFileInfo['video']['bitrate'] : 0) + (isset($ThisFileInfo['audio']['bitrate']) ? $ThisFileInfo['audio']['bitrate'] : 0); - } - } - break; - - case 'CONT': // Content Description Header (text comments) - $thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2)); - $offset += 2; - if ($thisfile_real_chunks_currentchunk['object_version'] == 0) { - $thisfile_real_chunks_currentchunk['title_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2)); - $offset += 2; - $thisfile_real_chunks_currentchunk['title'] = (string) substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['title_len']); - $offset += $thisfile_real_chunks_currentchunk['title_len']; - - $thisfile_real_chunks_currentchunk['artist_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2)); - $offset += 2; - $thisfile_real_chunks_currentchunk['artist'] = (string) substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['artist_len']); - $offset += $thisfile_real_chunks_currentchunk['artist_len']; - - $thisfile_real_chunks_currentchunk['copyright_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2)); - $offset += 2; - $thisfile_real_chunks_currentchunk['copyright'] = (string) substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['copyright_len']); - $offset += $thisfile_real_chunks_currentchunk['copyright_len']; - - $thisfile_real_chunks_currentchunk['comment_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2)); - $offset += 2; - $thisfile_real_chunks_currentchunk['comment'] = (string) substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['comment_len']); - $offset += $thisfile_real_chunks_currentchunk['comment_len']; - - - $commentkeystocopy = array('title'=>'title', 'artist'=>'artist', 'copyright'=>'copyright', 'comment'=>'comment'); - foreach ($commentkeystocopy as $key => $val) { - if ($thisfile_real_chunks_currentchunk[$key]) { - $ThisFileInfo['real']['comments'][$val][] = trim($thisfile_real_chunks_currentchunk[$key]); - } - } - - } - break; - - - case 'DATA': // Data Chunk Header - // do nothing - break; - - case 'INDX': // Index Section Header - $thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2)); - $offset += 2; - if ($thisfile_real_chunks_currentchunk['object_version'] == 0) { - $thisfile_real_chunks_currentchunk['num_indices'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); - $offset += 4; - $thisfile_real_chunks_currentchunk['stream_number'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2)); - $offset += 2; - $thisfile_real_chunks_currentchunk['next_index_header'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); - $offset += 4; - - if ($thisfile_real_chunks_currentchunk['next_index_header'] == 0) { - // last index chunk found, ignore rest of file - break 2; - } else { - // non-last index chunk, seek to next index chunk (skipping actual index data) - fseek($fd, $thisfile_real_chunks_currentchunk['next_index_header'], SEEK_SET); - } - } - break; - - default: - $ThisFileInfo['warning'][] = 'Unhandled RealMedia chunk "'.$ChunkName.'" at offset '.$thisfile_real_chunks_currentchunk['offset']; - break; - } - $ChunkCounter++; - } - - if (!empty($ThisFileInfo['audio']['streams'])) { - $ThisFileInfo['audio']['bitrate'] = 0; - foreach ($ThisFileInfo['audio']['streams'] as $key => $valuearray) { - $ThisFileInfo['audio']['bitrate'] += $valuearray['bitrate']; - } - } - - return true; - } - - - function ParseOldRAheader($OldRAheaderData, &$ParsedArray) { - // http://www.freelists.org/archives/matroska-devel/07-2003/msg00010.html - - $ParsedArray = array(); - $ParsedArray['magic'] = substr($OldRAheaderData, 0, 4); - if ($ParsedArray['magic'] != '.ra'."\xFD") { - return false; - } - $ParsedArray['version1'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 4, 2)); - - if ($ParsedArray['version1'] < 3) { - - return false; - - } elseif ($ParsedArray['version1'] == 3) { - - $ParsedArray['fourcc1'] = '.ra3'; - $ParsedArray['bits_per_sample'] = 16; // hard-coded for old versions? - $ParsedArray['sample_rate'] = 8000; // hard-coded for old versions? - - $ParsedArray['header_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 6, 2)); - $ParsedArray['channels'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 8, 2)); // always 1 (?) - //$ParsedArray['unknown1'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 10, 2)); - //$ParsedArray['unknown2'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 12, 2)); - //$ParsedArray['unknown3'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 14, 2)); - $ParsedArray['bytes_per_minute'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 16, 2)); - $ParsedArray['audio_bytes'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 18, 4)); - $ParsedArray['comments_raw'] = substr($OldRAheaderData, 22, $ParsedArray['header_size'] - 22 + 1); // not including null terminator - - $commentoffset = 0; - $commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1)); - $ParsedArray['comments']['title'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength); - $commentoffset += $commentlength; - - $commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1)); - $ParsedArray['comments']['artist'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength); - $commentoffset += $commentlength; - - $commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1)); - $ParsedArray['comments']['copyright'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength); - $commentoffset += $commentlength; - - $commentoffset++; // final null terminator (?) - $commentoffset++; // fourcc length (?) should be 4 - $ParsedArray['fourcc'] = substr($OldRAheaderData, 23 + $commentoffset, 4); - - } elseif ($ParsedArray['version1'] <= 5) { - - //$ParsedArray['unknown1'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 6, 2)); - $ParsedArray['fourcc1'] = substr($OldRAheaderData, 8, 4); - $ParsedArray['file_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 12, 4)); - $ParsedArray['version2'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 16, 2)); - $ParsedArray['header_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 18, 4)); - $ParsedArray['codec_flavor_id'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 22, 2)); - $ParsedArray['coded_frame_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 24, 4)); - $ParsedArray['audio_bytes'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 28, 4)); - $ParsedArray['bytes_per_minute'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 32, 4)); - //$ParsedArray['unknown5'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 36, 4)); - $ParsedArray['sub_packet_h'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 40, 2)); - $ParsedArray['frame_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 42, 2)); - $ParsedArray['sub_packet_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 44, 2)); - //$ParsedArray['unknown6'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 46, 2)); - - switch ($ParsedArray['version1']) { - - case 4: - $ParsedArray['sample_rate'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 48, 2)); - //$ParsedArray['unknown8'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 50, 2)); - $ParsedArray['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 52, 2)); - $ParsedArray['channels'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 54, 2)); - $ParsedArray['length_fourcc2'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 56, 1)); - $ParsedArray['fourcc2'] = substr($OldRAheaderData, 57, 4); - $ParsedArray['length_fourcc3'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 61, 1)); - $ParsedArray['fourcc3'] = substr($OldRAheaderData, 62, 4); - //$ParsedArray['unknown9'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 66, 1)); - //$ParsedArray['unknown10'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 67, 2)); - $ParsedArray['comments_raw'] = substr($OldRAheaderData, 69, $ParsedArray['header_size'] - 69 + 16); - - $commentoffset = 0; - $commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1)); - $ParsedArray['comments']['title'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength); - $commentoffset += $commentlength; - - $commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1)); - $ParsedArray['comments']['artist'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength); - $commentoffset += $commentlength; - - $commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1)); - $ParsedArray['comments']['copyright'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength); - $commentoffset += $commentlength; - break; - - case 5: - $ParsedArray['sample_rate'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 48, 4)); - $ParsedArray['sample_rate2'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 52, 4)); - $ParsedArray['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 56, 4)); - $ParsedArray['channels'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 60, 2)); - $ParsedArray['genr'] = substr($OldRAheaderData, 62, 4); - $ParsedArray['fourcc3'] = substr($OldRAheaderData, 66, 4); - $ParsedArray['comments'] = array(); - break; - } - $ParsedArray['fourcc'] = $ParsedArray['fourcc3']; - - } - foreach ($ParsedArray['comments'] as $key => $value) { - if ($ParsedArray['comments'][$key][0] === false) { - $ParsedArray['comments'][$key][0] = ''; - } - } - - return true; - } - - function RealAudioCodecFourCClookup($fourcc, $bitrate) { - static $RealAudioCodecFourCClookup = array(); - if (empty($RealAudioCodecFourCClookup)) { - // http://www.its.msstate.edu/net/real/reports/config/tags.stats - // http://www.freelists.org/archives/matroska-devel/06-2003/fullthread18.html - - $RealAudioCodecFourCClookup['14_4'][8000] = 'RealAudio v2 (14.4kbps)'; - $RealAudioCodecFourCClookup['14.4'][8000] = 'RealAudio v2 (14.4kbps)'; - $RealAudioCodecFourCClookup['lpcJ'][8000] = 'RealAudio v2 (14.4kbps)'; - $RealAudioCodecFourCClookup['28_8'][15200] = 'RealAudio v2 (28.8kbps)'; - $RealAudioCodecFourCClookup['28.8'][15200] = 'RealAudio v2 (28.8kbps)'; - $RealAudioCodecFourCClookup['sipr'][4933] = 'RealAudio v4 (5kbps Voice)'; - $RealAudioCodecFourCClookup['sipr'][6444] = 'RealAudio v4 (6.5kbps Voice)'; - $RealAudioCodecFourCClookup['sipr'][8444] = 'RealAudio v4 (8.5kbps Voice)'; - $RealAudioCodecFourCClookup['sipr'][16000] = 'RealAudio v4 (16kbps Wideband)'; - $RealAudioCodecFourCClookup['dnet'][8000] = 'RealAudio v3 (8kbps Music)'; - $RealAudioCodecFourCClookup['dnet'][16000] = 'RealAudio v3 (16kbps Music Low Response)'; - $RealAudioCodecFourCClookup['dnet'][15963] = 'RealAudio v3 (16kbps Music Mid/High Response)'; - $RealAudioCodecFourCClookup['dnet'][20000] = 'RealAudio v3 (20kbps Music Stereo)'; - $RealAudioCodecFourCClookup['dnet'][32000] = 'RealAudio v3 (32kbps Music Mono)'; - $RealAudioCodecFourCClookup['dnet'][31951] = 'RealAudio v3 (32kbps Music Stereo)'; - $RealAudioCodecFourCClookup['dnet'][39965] = 'RealAudio v3 (40kbps Music Mono)'; - $RealAudioCodecFourCClookup['dnet'][40000] = 'RealAudio v3 (40kbps Music Stereo)'; - $RealAudioCodecFourCClookup['dnet'][79947] = 'RealAudio v3 (80kbps Music Mono)'; - $RealAudioCodecFourCClookup['dnet'][80000] = 'RealAudio v3 (80kbps Music Stereo)'; - - $RealAudioCodecFourCClookup['dnet'][0] = 'RealAudio v3'; - $RealAudioCodecFourCClookup['sipr'][0] = 'RealAudio v4'; - $RealAudioCodecFourCClookup['cook'][0] = 'RealAudio G2'; - $RealAudioCodecFourCClookup['atrc'][0] = 'RealAudio 8'; - } - $roundbitrate = intval(round($bitrate)); - if (isset($RealAudioCodecFourCClookup[$fourcc][$roundbitrate])) { - return $RealAudioCodecFourCClookup[$fourcc][$roundbitrate]; - } elseif (isset($RealAudioCodecFourCClookup[$fourcc][0])) { - return $RealAudioCodecFourCClookup[$fourcc][0]; - } - return $fourcc; - } - -} - - -?> \ No newline at end of file diff --git a/getid3/getid3/module.audio-video.riff.php b/getid3/getid3/module.audio-video.riff.php deleted file mode 100644 index f32dab4..0000000 --- a/getid3/getid3/module.audio-video.riff.php +++ /dev/null @@ -1,1995 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.audio-video.riff.php // -// module for analyzing RIFF files // -// multiple formats supported by this module: // -// Wave, AVI, AIFF/AIFC, (MP3,AC3)/RIFF, Wavpack v3, 8SVX // -// dependencies: module.audio.mp3.php // -// module.audio.ac3.php (optional) // -// /// -///////////////////////////////////////////////////////////////// - -getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true); - -class getid3_riff -{ - - function getid3_riff(&$fd, &$ThisFileInfo) { - - // initialize these values to an empty array, otherwise they default to NULL - // and you can't append array values to a NULL value - $ThisFileInfo['riff'] = array('raw'=>array()); - - // Shortcuts - $thisfile_riff = &$ThisFileInfo['riff']; - $thisfile_riff_raw = &$thisfile_riff['raw']; - $thisfile_audio = &$ThisFileInfo['audio']; - $thisfile_video = &$ThisFileInfo['video']; - $thisfile_avdataoffset = &$ThisFileInfo['avdataoffset']; - $thisfile_avdataend = &$ThisFileInfo['avdataend']; - $thisfile_audio_dataformat = &$thisfile_audio['dataformat']; - $thisfile_riff_audio = &$thisfile_riff['audio']; - $thisfile_riff_video = &$thisfile_riff['video']; - - - $Original['avdataoffset'] = $thisfile_avdataoffset; - $Original['avdataend'] = $thisfile_avdataend; - - fseek($fd, $thisfile_avdataoffset, SEEK_SET); - $RIFFheader = fread($fd, 12); - $RIFFsubtype = substr($RIFFheader, 8, 4); - switch (substr($RIFFheader, 0, 4)) { - case 'FORM': - $ThisFileInfo['fileformat'] = 'aiff'; - $RIFFheaderSize = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($RIFFheader, 4, 4)); - $thisfile_riff[$RIFFsubtype] = getid3_riff::ParseRIFF($fd, $thisfile_avdataoffset + 12, $thisfile_avdataoffset + $RIFFheaderSize, $ThisFileInfo); - $thisfile_riff['header_size'] = $RIFFheaderSize; - break; - - case 'RIFF': - case 'SDSS': // SDSS is identical to RIFF, just renamed. Used by SmartSound QuickTracks (www.smartsound.com) - case 'RMP3': // RMP3 is identical to RIFF, just renamed. Used by [unknown program] when creating RIFF-MP3s - if ($RIFFsubtype == 'RMP3') { - // RMP3 is identical to WAVE, just renamed. Used by [unknown program] when creating RIFF-MP3s - $RIFFsubtype = 'WAVE'; - } - - $ThisFileInfo['fileformat'] = 'riff'; - $RIFFheaderSize = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($RIFFheader, 4, 4)); - $thisfile_riff[$RIFFsubtype] = getid3_riff::ParseRIFF($fd, $thisfile_avdataoffset + 12, $thisfile_avdataoffset + $RIFFheaderSize, $ThisFileInfo); - $thisfile_riff['header_size'] = $RIFFheaderSize; - if ($RIFFsubtype == 'WAVE') { - $thisfile_riff_WAVE = &$thisfile_riff['WAVE']; - } - break; - - default: - $ThisFileInfo['error'][] = 'Cannot parse RIFF (this is maybe not a RIFF / WAV / AVI file?) - expecting "FORM|RIFF|SDSS|RMP3" found "'.$RIFFsubtype.'" instead'; - unset($ThisFileInfo['fileformat']); - return false; - break; - } - - $streamindex = 0; - switch ($RIFFsubtype) { - case 'WAVE': - if (empty($thisfile_audio['bitrate_mode'])) { - $thisfile_audio['bitrate_mode'] = 'cbr'; - } - if (empty($thisfile_audio_dataformat)) { - $thisfile_audio_dataformat = 'wav'; - } - - if (isset($thisfile_riff_WAVE['data'][0]['offset'])) { - $thisfile_avdataoffset = $thisfile_riff_WAVE['data'][0]['offset'] + 8; - $thisfile_avdataend = $thisfile_avdataoffset + $thisfile_riff_WAVE['data'][0]['size']; - } - if (isset($thisfile_riff_WAVE['fmt '][0]['data'])) { - - $thisfile_riff_audio[$streamindex] = getid3_riff::RIFFparseWAVEFORMATex($thisfile_riff_WAVE['fmt '][0]['data']); - $thisfile_audio['wformattag'] = $thisfile_riff_audio[$streamindex]['raw']['wFormatTag']; - if (@$thisfile_riff_audio[$streamindex]['bitrate'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt RIFF file: bitrate_audio == zero'; - return false; - } - $thisfile_riff_raw['fmt '] = $thisfile_riff_audio[$streamindex]['raw']; - unset($thisfile_riff_audio[$streamindex]['raw']); - $thisfile_audio['streams'][$streamindex] = $thisfile_riff_audio[$streamindex]; - - $thisfile_audio = getid3_lib::array_merge_noclobber($thisfile_audio, $thisfile_riff_audio[$streamindex]); - if (substr($thisfile_audio['codec'], 0, strlen('unknown: 0x')) == 'unknown: 0x') { - $ThisFileInfo['warning'][] = 'Audio codec = '.$thisfile_audio['codec']; - } - $thisfile_audio['bitrate'] = $thisfile_riff_audio[$streamindex]['bitrate']; - - $ThisFileInfo['playtime_seconds'] = (float) ((($thisfile_avdataend - $thisfile_avdataoffset) * 8) / $thisfile_audio['bitrate']); - - $thisfile_audio['lossless'] = false; - if (isset($thisfile_riff_WAVE['data'][0]['offset']) && isset($thisfile_riff_raw['fmt ']['wFormatTag'])) { - switch ($thisfile_riff_raw['fmt ']['wFormatTag']) { - - case 0x0001: // PCM - $thisfile_audio['lossless'] = true; - break; - - case 0x2000: // AC-3 - $thisfile_audio_dataformat = 'ac3'; - break; - - default: - // do nothing - break; - - } - } - $thisfile_audio['streams'][$streamindex]['wformattag'] = $thisfile_audio['wformattag']; - $thisfile_audio['streams'][$streamindex]['bitrate_mode'] = $thisfile_audio['bitrate_mode']; - $thisfile_audio['streams'][$streamindex]['lossless'] = $thisfile_audio['lossless']; - $thisfile_audio['streams'][$streamindex]['dataformat'] = $thisfile_audio_dataformat; - } - - if (isset($thisfile_riff_WAVE['rgad'][0]['data'])) { - - // shortcuts - $rgadData = &$thisfile_riff_WAVE['rgad'][0]['data']; - $thisfile_riff_raw['rgad'] = array('track'=>array(), 'album'=>array()); - $thisfile_riff_raw_rgad = &$thisfile_riff_raw['rgad']; - $thisfile_riff_raw_rgad_track = &$thisfile_riff_raw_rgad['track']; - $thisfile_riff_raw_rgad_album = &$thisfile_riff_raw_rgad['album']; - - $thisfile_riff_raw_rgad['fPeakAmplitude'] = getid3_lib::LittleEndian2Float(substr($rgadData, 0, 4)); - $thisfile_riff_raw_rgad['nRadioRgAdjust'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($rgadData, 4, 2)); - $thisfile_riff_raw_rgad['nAudiophileRgAdjust'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($rgadData, 6, 2)); - - $nRadioRgAdjustBitstring = str_pad(getid3_lib::Dec2Bin($thisfile_riff_raw_rgad['nRadioRgAdjust']), 16, '0', STR_PAD_LEFT); - $nAudiophileRgAdjustBitstring = str_pad(getid3_lib::Dec2Bin($thisfile_riff_raw_rgad['nAudiophileRgAdjust']), 16, '0', STR_PAD_LEFT); - $thisfile_riff_raw_rgad_track['name'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 0, 3)); - $thisfile_riff_raw_rgad_track['originator'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 3, 3)); - $thisfile_riff_raw_rgad_track['signbit'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 6, 1)); - $thisfile_riff_raw_rgad_track['adjustment'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 7, 9)); - $thisfile_riff_raw_rgad_album['name'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 0, 3)); - $thisfile_riff_raw_rgad_album['originator'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 3, 3)); - $thisfile_riff_raw_rgad_album['signbit'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 6, 1)); - $thisfile_riff_raw_rgad_album['adjustment'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 7, 9)); - - $thisfile_riff['rgad']['peakamplitude'] = $thisfile_riff_raw_rgad['fPeakAmplitude']; - if (($thisfile_riff_raw_rgad_track['name'] != 0) && ($thisfile_riff_raw_rgad_track['originator'] != 0)) { - $thisfile_riff['rgad']['track']['name'] = getid3_lib::RGADnameLookup($thisfile_riff_raw_rgad_track['name']); - $thisfile_riff['rgad']['track']['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_riff_raw_rgad_track['originator']); - $thisfile_riff['rgad']['track']['adjustment'] = getid3_lib::RGADadjustmentLookup($thisfile_riff_raw_rgad_track['adjustment'], $thisfile_riff_raw_rgad_track['signbit']); - } - if (($thisfile_riff_raw_rgad_album['name'] != 0) && ($thisfile_riff_raw_rgad_album['originator'] != 0)) { - $thisfile_riff['rgad']['album']['name'] = getid3_lib::RGADnameLookup($thisfile_riff_raw_rgad_album['name']); - $thisfile_riff['rgad']['album']['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_riff_raw_rgad_album['originator']); - $thisfile_riff['rgad']['album']['adjustment'] = getid3_lib::RGADadjustmentLookup($thisfile_riff_raw_rgad_album['adjustment'], $thisfile_riff_raw_rgad_album['signbit']); - } - } - - if (isset($thisfile_riff_WAVE['fact'][0]['data'])) { - $thisfile_riff_raw['fact']['NumberOfSamples'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_WAVE['fact'][0]['data'], 0, 4)); - - // This should be a good way of calculating exact playtime, - // but some sample files have had incorrect number of samples, - // so cannot use this method - - // if (!empty($thisfile_riff_raw['fmt ']['nSamplesPerSec'])) { - // $ThisFileInfo['playtime_seconds'] = (float) $thisfile_riff_raw['fact']['NumberOfSamples'] / $thisfile_riff_raw['fmt ']['nSamplesPerSec']; - // } - } - if (!empty($thisfile_riff_raw['fmt ']['nAvgBytesPerSec'])) { - $thisfile_audio['bitrate'] = getid3_lib::CastAsInt($thisfile_riff_raw['fmt ']['nAvgBytesPerSec'] * 8); - } - - if (isset($thisfile_riff_WAVE['bext'][0]['data'])) { - // shortcut - $thisfile_riff_WAVE_bext_0 = &$thisfile_riff_WAVE['bext'][0]; - - $thisfile_riff_WAVE_bext_0['title'] = trim(substr($thisfile_riff_WAVE_bext_0['data'], 0, 256)); - $thisfile_riff_WAVE_bext_0['author'] = trim(substr($thisfile_riff_WAVE_bext_0['data'], 256, 32)); - $thisfile_riff_WAVE_bext_0['reference'] = trim(substr($thisfile_riff_WAVE_bext_0['data'], 288, 32)); - $thisfile_riff_WAVE_bext_0['origin_date'] = substr($thisfile_riff_WAVE_bext_0['data'], 320, 10); - $thisfile_riff_WAVE_bext_0['origin_time'] = substr($thisfile_riff_WAVE_bext_0['data'], 330, 8); - $thisfile_riff_WAVE_bext_0['time_reference'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_bext_0['data'], 338, 8)); - $thisfile_riff_WAVE_bext_0['bwf_version'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_bext_0['data'], 346, 1)); - $thisfile_riff_WAVE_bext_0['reserved'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_bext_0['data'], 347, 254)); - $thisfile_riff_WAVE_bext_0['coding_history'] = explode("\r\n", trim(substr($thisfile_riff_WAVE_bext_0['data'], 601))); - - $thisfile_riff_WAVE_bext_0['origin_date_unix'] = gmmktime( - substr($thisfile_riff_WAVE_bext_0['origin_time'], 0, 2), - substr($thisfile_riff_WAVE_bext_0['origin_time'], 3, 2), - substr($thisfile_riff_WAVE_bext_0['origin_time'], 6, 2), - substr($thisfile_riff_WAVE_bext_0['origin_date'], 5, 2), - substr($thisfile_riff_WAVE_bext_0['origin_date'], 8, 2), - substr($thisfile_riff_WAVE_bext_0['origin_date'], 0, 4)); - - $thisfile_riff['comments']['author'][] = $thisfile_riff_WAVE_bext_0['author']; - $thisfile_riff['comments']['title'][] = $thisfile_riff_WAVE_bext_0['title']; - } - - if (isset($thisfile_riff_WAVE['MEXT'][0]['data'])) { - // shortcut - $thisfile_riff_WAVE_MEXT_0 = &$thisfile_riff_WAVE['MEXT'][0]; - - $thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 0, 2)); - $thisfile_riff_WAVE_MEXT_0['flags']['homogenous'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0001); - if ($thisfile_riff_WAVE_MEXT_0['flags']['homogenous']) { - $thisfile_riff_WAVE_MEXT_0['flags']['padding'] = ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0002) ? false : true; - $thisfile_riff_WAVE_MEXT_0['flags']['22_or_44'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0004); - $thisfile_riff_WAVE_MEXT_0['flags']['free_format'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0008); - - $thisfile_riff_WAVE_MEXT_0['nominal_frame_size'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 2, 2)); - } - $thisfile_riff_WAVE_MEXT_0['anciliary_data_length'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 6, 2)); - $thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 8, 2)); - $thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_left'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x0001); - $thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_free'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x0002); - $thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_right'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x0004); - } - - if (isset($thisfile_riff_WAVE['cart'][0]['data'])) { - // shortcut - $thisfile_riff_WAVE_cart_0 = &$thisfile_riff_WAVE['cart'][0]; - - $thisfile_riff_WAVE_cart_0['version'] = substr($thisfile_riff_WAVE_cart_0['data'], 0, 4); - $thisfile_riff_WAVE_cart_0['title'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 4, 64)); - $thisfile_riff_WAVE_cart_0['artist'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 68, 64)); - $thisfile_riff_WAVE_cart_0['cut_id'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 132, 64)); - $thisfile_riff_WAVE_cart_0['client_id'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 196, 64)); - $thisfile_riff_WAVE_cart_0['category'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 260, 64)); - $thisfile_riff_WAVE_cart_0['classification'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 324, 64)); - $thisfile_riff_WAVE_cart_0['out_cue'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 388, 64)); - $thisfile_riff_WAVE_cart_0['start_date'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 452, 10)); - $thisfile_riff_WAVE_cart_0['start_time'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 462, 8)); - $thisfile_riff_WAVE_cart_0['end_date'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 470, 10)); - $thisfile_riff_WAVE_cart_0['end_time'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 480, 8)); - $thisfile_riff_WAVE_cart_0['producer_app_id'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 488, 64)); - $thisfile_riff_WAVE_cart_0['producer_app_version'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 552, 64)); - $thisfile_riff_WAVE_cart_0['user_defined_text'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 616, 64)); - $thisfile_riff_WAVE_cart_0['zero_db_reference'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_cart_0['data'], 680, 4), true); - for ($i = 0; $i < 8; $i++) { - $thisfile_riff_WAVE_cart_0['post_time'][$i]['usage_fourcc'] = substr($thisfile_riff_WAVE_cart_0['data'], 684 + ($i * 8), 4); - $thisfile_riff_WAVE_cart_0['post_time'][$i]['timer_value'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_cart_0['data'], 684 + ($i * 8) + 4, 4)); - } - $thisfile_riff_WAVE_cart_0['url'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 748, 1024)); - $thisfile_riff_WAVE_cart_0['tag_text'] = explode("\r\n", trim(substr($thisfile_riff_WAVE_cart_0['data'], 1772))); - - $thisfile_riff['comments']['artist'][] = $thisfile_riff_WAVE_cart_0['artist']; - $thisfile_riff['comments']['title'][] = $thisfile_riff_WAVE_cart_0['title']; - } - - if (!isset($thisfile_audio['bitrate']) && isset($thisfile_riff_audio[$streamindex]['bitrate'])) { - $thisfile_audio['bitrate'] = $thisfile_riff_audio[$streamindex]['bitrate']; - $ThisFileInfo['playtime_seconds'] = (float) ((($thisfile_avdataend - $thisfile_avdataoffset) * 8) / $thisfile_audio['bitrate']); - } - - if (!empty($ThisFileInfo['wavpack'])) { - $thisfile_audio_dataformat = 'wavpack'; - $thisfile_audio['bitrate_mode'] = 'vbr'; - $thisfile_audio['encoder'] = 'WavPack v'.$ThisFileInfo['wavpack']['version']; - - // Reset to the way it was - RIFF parsing will have messed this up - $thisfile_avdataend = $Original['avdataend']; - $thisfile_audio['bitrate'] = (($thisfile_avdataend - $thisfile_avdataoffset) * 8) / $ThisFileInfo['playtime_seconds']; - - fseek($fd, $thisfile_avdataoffset - 44, SEEK_SET); - $RIFFdata = fread($fd, 44); - $OrignalRIFFheaderSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 4, 4)) + 8; - $OrignalRIFFdataSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 40, 4)) + 44; - - if ($OrignalRIFFheaderSize > $OrignalRIFFdataSize) { - $thisfile_avdataend -= ($OrignalRIFFheaderSize - $OrignalRIFFdataSize); - fseek($fd, $thisfile_avdataend, SEEK_SET); - $RIFFdata .= fread($fd, $OrignalRIFFheaderSize - $OrignalRIFFdataSize); - } - - // move the data chunk after all other chunks (if any) - // so that the RIFF parser doesn't see EOF when trying - // to skip over the data chunk - $RIFFdata = substr($RIFFdata, 0, 36).substr($RIFFdata, 44).substr($RIFFdata, 36, 8); - getid3_riff::ParseRIFFdata($RIFFdata, $ThisFileInfo); - } - - if (isset($thisfile_riff_raw['fmt ']['wFormatTag'])) { - switch ($thisfile_riff_raw['fmt ']['wFormatTag']) { - case 0x08AE: // ClearJump LiteWave - $thisfile_audio['bitrate_mode'] = 'vbr'; - $thisfile_audio_dataformat = 'litewave'; - - //typedef struct tagSLwFormat { - // WORD m_wCompFormat; // low byte defines compression method, high byte is compression flags - // DWORD m_dwScale; // scale factor for lossy compression - // DWORD m_dwBlockSize; // number of samples in encoded blocks - // WORD m_wQuality; // alias for the scale factor - // WORD m_wMarkDistance; // distance between marks in bytes - // WORD m_wReserved; - // - // //following paramters are ignored if CF_FILESRC is not set - // DWORD m_dwOrgSize; // original file size in bytes - // WORD m_bFactExists; // indicates if 'fact' chunk exists in the original file - // DWORD m_dwRiffChunkSize; // riff chunk size in the original file - // - // PCMWAVEFORMAT m_OrgWf; // original wave format - // }SLwFormat, *PSLwFormat; - - // shortcut - $thisfile_riff['litewave']['raw'] = array(); - $thisfile_riff_litewave = &$thisfile_riff['litewave']; - $thisfile_riff_litewave_raw = &$thisfile_riff_litewave['raw']; - - $thisfile_riff_litewave_raw['compression_method'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 18, 1)); - $thisfile_riff_litewave_raw['compression_flags'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 19, 1)); - $thisfile_riff_litewave_raw['m_dwScale'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 20, 4)); - $thisfile_riff_litewave_raw['m_dwBlockSize'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 24, 4)); - $thisfile_riff_litewave_raw['m_wQuality'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 28, 2)); - $thisfile_riff_litewave_raw['m_wMarkDistance'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 30, 2)); - $thisfile_riff_litewave_raw['m_wReserved'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 32, 2)); - $thisfile_riff_litewave_raw['m_dwOrgSize'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 34, 4)); - $thisfile_riff_litewave_raw['m_bFactExists'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 38, 2)); - $thisfile_riff_litewave_raw['m_dwRiffChunkSize'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 40, 4)); - - //$thisfile_riff_litewave['quality_factor'] = intval(round((2000 - $thisfile_riff_litewave_raw['m_dwScale']) / 20)); - $thisfile_riff_litewave['quality_factor'] = $thisfile_riff_litewave_raw['m_wQuality']; - - $thisfile_riff_litewave['flags']['raw_source'] = ($thisfile_riff_litewave_raw['compression_flags'] & 0x01) ? false : true; - $thisfile_riff_litewave['flags']['vbr_blocksize'] = ($thisfile_riff_litewave_raw['compression_flags'] & 0x02) ? false : true; - $thisfile_riff_litewave['flags']['seekpoints'] = (bool) ($thisfile_riff_litewave_raw['compression_flags'] & 0x04); - - $thisfile_audio['lossless'] = (($thisfile_riff_litewave_raw['m_wQuality'] == 100) ? true : false); - $thisfile_audio['encoder_options'] = '-q'.$thisfile_riff_litewave['quality_factor']; - break; - - default: - break; - } - } - if ($thisfile_avdataend > $ThisFileInfo['filesize']) { - switch (@$thisfile_audio_dataformat) { - case 'wavpack': // WavPack - case 'lpac': // LPAC - case 'ofr': // OptimFROG - case 'ofs': // OptimFROG DualStream - // lossless compressed audio formats that keep original RIFF headers - skip warning - break; - - case 'litewave': - if (($thisfile_avdataend - $ThisFileInfo['filesize']) == 1) { - // LiteWave appears to incorrectly *not* pad actual output file - // to nearest WORD boundary so may appear to be short by one - // byte, in which case - skip warning - } else { - // Short by more than one byte, throw warning - $ThisFileInfo['warning'][] = 'Probably truncated file - expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($ThisFileInfo['filesize'] - $thisfile_avdataoffset).' (short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($ThisFileInfo['filesize'] - $thisfile_avdataoffset)).' bytes)'; - $thisfile_avdataend = $ThisFileInfo['filesize']; - } - break; - - default: - if ((($thisfile_avdataend - $ThisFileInfo['filesize']) == 1) && (($thisfile_riff[$RIFFsubtype]['data'][0]['size'] % 2) == 0) && ((($ThisFileInfo['filesize'] - $thisfile_avdataoffset) % 2) == 1)) { - // output file appears to be incorrectly *not* padded to nearest WORD boundary - // Output less severe warning - $ThisFileInfo['warning'][] = 'File should probably be padded to nearest WORD boundary, but it is not (expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($ThisFileInfo['filesize'] - $thisfile_avdataoffset).' therefore short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($ThisFileInfo['filesize'] - $thisfile_avdataoffset)).' bytes)'; - $thisfile_avdataend = $ThisFileInfo['filesize']; - break; - } - // Short by more than one byte, throw warning - $ThisFileInfo['warning'][] = 'Probably truncated file - expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($ThisFileInfo['filesize'] - $thisfile_avdataoffset).' (short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($ThisFileInfo['filesize'] - $thisfile_avdataoffset)).' bytes)'; - $thisfile_avdataend = $ThisFileInfo['filesize']; - break; - } - } - if (!empty($ThisFileInfo['mpeg']['audio']['LAME']['audio_bytes'])) { - if ((($thisfile_avdataend - $thisfile_avdataoffset) - $ThisFileInfo['mpeg']['audio']['LAME']['audio_bytes']) == 1) { - $thisfile_avdataend--; - $ThisFileInfo['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored'; - } - } - if (@$thisfile_audio_dataformat == 'ac3') { - unset($thisfile_audio['bits_per_sample']); - if (!empty($ThisFileInfo['ac3']['bitrate']) && ($ThisFileInfo['ac3']['bitrate'] != $thisfile_audio['bitrate'])) { - $thisfile_audio['bitrate'] = $ThisFileInfo['ac3']['bitrate']; - } - } - break; - - case 'AVI ': - $thisfile_video['bitrate_mode'] = 'vbr'; // maybe not, but probably - $thisfile_video['dataformat'] = 'avi'; - $ThisFileInfo['mime_type'] = 'video/avi'; - - if (isset($thisfile_riff[$RIFFsubtype]['movi']['offset'])) { - $thisfile_avdataoffset = $thisfile_riff[$RIFFsubtype]['movi']['offset'] + 8; - $thisfile_avdataend = $thisfile_avdataoffset + $thisfile_riff[$RIFFsubtype]['movi']['size']; - if ($thisfile_avdataend > $ThisFileInfo['filesize']) { - $ThisFileInfo['warning'][] = 'Probably truncated file - expecting '.$thisfile_riff[$RIFFsubtype]['movi']['size'].' bytes of data, only found '.($ThisFileInfo['filesize'] - $thisfile_avdataoffset).' (short by '.($thisfile_riff[$RIFFsubtype]['movi']['size'] - ($ThisFileInfo['filesize'] - $thisfile_avdataoffset)).' bytes)'; - $thisfile_avdataend = $ThisFileInfo['filesize']; - } - } - - if (isset($thisfile_riff['AVI ']['hdrl']['avih'][$streamindex]['data'])) { - $avihData = $thisfile_riff['AVI ']['hdrl']['avih'][$streamindex]['data']; - - // shortcut - $thisfile_riff_raw['avih'] = array(); - $thisfile_riff_raw_avih = &$thisfile_riff_raw['avih']; - - $thisfile_riff_raw_avih['dwMicroSecPerFrame'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 0, 4)); // frame display rate (or 0L) - if ($thisfile_riff_raw_avih['dwMicroSecPerFrame'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt RIFF file: avih.dwMicroSecPerFrame == zero'; - return false; - } - $thisfile_riff_raw_avih['dwMaxBytesPerSec'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 4, 4)); // max. transfer rate - $thisfile_riff_raw_avih['dwPaddingGranularity'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 8, 4)); // pad to multiples of this size; normally 2K. - $thisfile_riff_raw_avih['dwFlags'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 12, 4)); // the ever-present flags - $thisfile_riff_raw_avih['dwTotalFrames'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 16, 4)); // # frames in file - $thisfile_riff_raw_avih['dwInitialFrames'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 20, 4)); - $thisfile_riff_raw_avih['dwStreams'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 24, 4)); - $thisfile_riff_raw_avih['dwSuggestedBufferSize'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 28, 4)); - $thisfile_riff_raw_avih['dwWidth'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 32, 4)); - $thisfile_riff_raw_avih['dwHeight'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 36, 4)); - $thisfile_riff_raw_avih['dwScale'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 40, 4)); - $thisfile_riff_raw_avih['dwRate'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 44, 4)); - $thisfile_riff_raw_avih['dwStart'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 48, 4)); - $thisfile_riff_raw_avih['dwLength'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 52, 4)); - - $thisfile_riff_raw_avih['flags']['hasindex'] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & 0x00000010); - $thisfile_riff_raw_avih['flags']['mustuseindex'] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & 0x00000020); - $thisfile_riff_raw_avih['flags']['interleaved'] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & 0x00000100); - $thisfile_riff_raw_avih['flags']['trustcktype'] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & 0x00000800); - $thisfile_riff_raw_avih['flags']['capturedfile'] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & 0x00010000); - $thisfile_riff_raw_avih['flags']['copyrighted'] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & 0x00020010); - - // shortcut - $thisfile_riff_video[$streamindex] = array(); - $thisfile_riff_video_current = &$thisfile_riff_video[$streamindex]; - - if ($thisfile_riff_raw_avih['dwWidth'] > 0) { - $thisfile_riff_video_current['frame_width'] = $thisfile_riff_raw_avih['dwWidth']; - $thisfile_video['resolution_x'] = $thisfile_riff_video_current['frame_width']; - } - if ($thisfile_riff_raw_avih['dwHeight'] > 0) { - $thisfile_riff_video_current['frame_height'] = $thisfile_riff_raw_avih['dwHeight']; - $thisfile_video['resolution_y'] = $thisfile_riff_video_current['frame_height']; - } - if ($thisfile_riff_raw_avih['dwTotalFrames'] > 0) { - $thisfile_riff_video_current['total_frames'] = $thisfile_riff_raw_avih['dwTotalFrames']; - $thisfile_video['total_frames'] = $thisfile_riff_video_current['total_frames']; - } - - $thisfile_riff_video_current['frame_rate'] = round(1000000 / $thisfile_riff_raw_avih['dwMicroSecPerFrame'], 3); - $thisfile_video['frame_rate'] = $thisfile_riff_video_current['frame_rate']; - } - if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strh'][0]['data'])) { - if (is_array($thisfile_riff['AVI ']['hdrl']['strl']['strh'])) { - for ($i = 0; $i < count($thisfile_riff['AVI ']['hdrl']['strl']['strh']); $i++) { - if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strh'][$i]['data'])) { - $strhData = $thisfile_riff['AVI ']['hdrl']['strl']['strh'][$i]['data']; - $strhfccType = substr($strhData, 0, 4); - - if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strf'][$i]['data'])) { - $strfData = $thisfile_riff['AVI ']['hdrl']['strl']['strf'][$i]['data']; - - // shortcut - $thisfile_riff_raw_strf_strhfccType_streamindex = &$thisfile_riff_raw['strf'][$strhfccType][$streamindex]; - - switch ($strhfccType) { - case 'auds': - $thisfile_audio['bitrate_mode'] = 'cbr'; - $thisfile_audio_dataformat = 'wav'; - if (isset($thisfile_riff_audio) && is_array($thisfile_riff_audio)) { - $streamindex = count($thisfile_riff_audio); - } - - $thisfile_riff_audio[$streamindex] = getid3_riff::RIFFparseWAVEFORMATex($strfData); - $thisfile_audio['wformattag'] = $thisfile_riff_audio[$streamindex]['raw']['wFormatTag']; - - // shortcut - $thisfile_audio['streams'][$streamindex] = $thisfile_riff_audio[$streamindex]; - $thisfile_audio_streams_currentstream = &$thisfile_audio['streams'][$streamindex]; - - if ($thisfile_audio_streams_currentstream['bits_per_sample'] == 0) { - unset($thisfile_audio_streams_currentstream['bits_per_sample']); - } - $thisfile_audio_streams_currentstream['wformattag'] = $thisfile_audio_streams_currentstream['raw']['wFormatTag']; - unset($thisfile_audio_streams_currentstream['raw']); - - // shortcut - $thisfile_riff_raw['strf'][$strhfccType][$streamindex] = $thisfile_riff_audio[$streamindex]['raw']; - - unset($thisfile_riff_audio[$streamindex]['raw']); - $thisfile_audio = getid3_lib::array_merge_noclobber($thisfile_audio, $thisfile_riff_audio[$streamindex]); - - $thisfile_audio['lossless'] = false; - switch ($thisfile_riff_raw_strf_strhfccType_streamindex['wFormatTag']) { - case 0x0001: // PCM - $thisfile_audio_dataformat = 'wav'; - $thisfile_audio['lossless'] = true; - break; - - case 0x0050: // MPEG Layer 2 or Layer 1 - $thisfile_audio_dataformat = 'mp2'; // Assume Layer-2 - break; - - case 0x0055: // MPEG Layer 3 - $thisfile_audio_dataformat = 'mp3'; - break; - - case 0x00FF: // AAC - $thisfile_audio_dataformat = 'aac'; - break; - - case 0x0161: // Windows Media v7 / v8 / v9 - case 0x0162: // Windows Media Professional v9 - case 0x0163: // Windows Media Lossess v9 - $thisfile_audio_dataformat = 'wma'; - break; - - case 0x2000: // AC-3 - $thisfile_audio_dataformat = 'ac3'; - break; - - case 0x2001: // DTS - $thisfile_audio_dataformat = 'dts'; - break; - - default: - $thisfile_audio_dataformat = 'wav'; - break; - } - $thisfile_audio_streams_currentstream['dataformat'] = $thisfile_audio_dataformat; - $thisfile_audio_streams_currentstream['lossless'] = $thisfile_audio['lossless']; - $thisfile_audio_streams_currentstream['bitrate_mode'] = $thisfile_audio['bitrate_mode']; - break; - - - case 'iavs': - case 'vids': - // shortcut - $thisfile_riff_raw['strh'][$i] = array(); - $thisfile_riff_raw_strh_current = &$thisfile_riff_raw['strh'][$i]; - - $thisfile_riff_raw_strh_current['fccType'] = substr($strhData, 0, 4); // same as $strhfccType; - $thisfile_riff_raw_strh_current['fccHandler'] = substr($strhData, 4, 4); - $thisfile_riff_raw_strh_current['dwFlags'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 8, 4)); // Contains AVITF_* flags - $thisfile_riff_raw_strh_current['wPriority'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 12, 2)); - $thisfile_riff_raw_strh_current['wLanguage'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 14, 2)); - $thisfile_riff_raw_strh_current['dwInitialFrames'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 16, 4)); - $thisfile_riff_raw_strh_current['dwScale'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 20, 4)); - $thisfile_riff_raw_strh_current['dwRate'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 24, 4)); - $thisfile_riff_raw_strh_current['dwStart'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 28, 4)); - $thisfile_riff_raw_strh_current['dwLength'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 32, 4)); - $thisfile_riff_raw_strh_current['dwSuggestedBufferSize'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 36, 4)); - $thisfile_riff_raw_strh_current['dwQuality'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 40, 4)); - $thisfile_riff_raw_strh_current['dwSampleSize'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 44, 4)); - $thisfile_riff_raw_strh_current['rcFrame'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 48, 4)); - - $thisfile_riff_video_current['codec'] = getid3_riff::RIFFfourccLookup($thisfile_riff_raw_strh_current['fccHandler']); - $thisfile_video['fourcc'] = $thisfile_riff_raw_strh_current['fccHandler']; - if (!$thisfile_riff_video_current['codec'] && isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']) && getid3_riff::RIFFfourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) { - $thisfile_riff_video_current['codec'] = getid3_riff::RIFFfourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']); - $thisfile_video['fourcc'] = $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']; - } - $thisfile_video['codec'] = $thisfile_riff_video_current['codec']; - $thisfile_video['pixel_aspect_ratio'] = (float) 1; - switch ($thisfile_riff_raw_strh_current['fccHandler']) { - case 'HFYU': // Huffman Lossless Codec - case 'IRAW': // Intel YUV Uncompressed - case 'YUY2': // Uncompressed YUV 4:2:2 - $thisfile_video['lossless'] = true; - break; - - default: - $thisfile_video['lossless'] = false; - break; - } - - switch ($strhfccType) { - case 'vids': - $thisfile_riff_raw_strf_strhfccType_streamindex['biSize'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 0, 4)); // number of bytes required by the BITMAPINFOHEADER structure - $thisfile_riff_raw_strf_strhfccType_streamindex['biWidth'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 4, 4)); // width of the bitmap in pixels - $thisfile_riff_raw_strf_strhfccType_streamindex['biHeight'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 8, 4)); // height of the bitmap in pixels. If biHeight is positive, the bitmap is a 'bottom-up' DIB and its origin is the lower left corner. If biHeight is negative, the bitmap is a 'top-down' DIB and its origin is the upper left corner - $thisfile_riff_raw_strf_strhfccType_streamindex['biPlanes'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 12, 2)); // number of color planes on the target device. In most cases this value must be set to 1 - $thisfile_riff_raw_strf_strhfccType_streamindex['biBitCount'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 14, 2)); // Specifies the number of bits per pixels - $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'] = substr($strfData, 16, 4); // - $thisfile_riff_raw_strf_strhfccType_streamindex['biSizeImage'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 20, 4)); // size of the bitmap data section of the image (the actual pixel data, excluding BITMAPINFOHEADER and RGBQUAD structures) - $thisfile_riff_raw_strf_strhfccType_streamindex['biXPelsPerMeter'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 24, 4)); // horizontal resolution, in pixels per metre, of the target device - $thisfile_riff_raw_strf_strhfccType_streamindex['biYPelsPerMeter'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 28, 4)); // vertical resolution, in pixels per metre, of the target device - $thisfile_riff_raw_strf_strhfccType_streamindex['biClrUsed'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 32, 4)); // actual number of color indices in the color table used by the bitmap. If this value is zero, the bitmap uses the maximum number of colors corresponding to the value of the biBitCount member for the compression mode specified by biCompression - $thisfile_riff_raw_strf_strhfccType_streamindex['biClrImportant'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 36, 4)); // number of color indices that are considered important for displaying the bitmap. If this value is zero, all colors are important - - $thisfile_video['bits_per_sample'] = $thisfile_riff_raw_strf_strhfccType_streamindex['biBitCount']; - - if ($thisfile_riff_video_current['codec'] == 'DV') { - $thisfile_riff_video_current['dv_type'] = 2; - } - break; - - case 'iavs': - $thisfile_riff_video_current['dv_type'] = 1; - break; - } - break; - - default: - $ThisFileInfo['warning'][] = 'Unhandled fccType for stream ('.$i.'): "'.$strhfccType.'"'; - break; - - } - } - } - - if (isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']) && getid3_riff::RIFFfourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) { - - $thisfile_riff_video_current['codec'] = getid3_riff::RIFFfourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']); - $thisfile_video['codec'] = $thisfile_riff_video_current['codec']; - $thisfile_video['fourcc'] = $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']; - - switch ($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']) { - case 'HFYU': // Huffman Lossless Codec - case 'IRAW': // Intel YUV Uncompressed - case 'YUY2': // Uncompressed YUV 4:2:2 - $thisfile_video['lossless'] = true; - $thisfile_video['bits_per_sample'] = 24; - break; - - default: - $thisfile_video['lossless'] = false; - $thisfile_video['bits_per_sample'] = 24; - break; - } - - } - } - } - } - break; - - case 'CDDA': - $thisfile_audio['bitrate_mode'] = 'cbr'; - $thisfile_audio_dataformat = 'cda'; - $thisfile_audio['lossless'] = true; - unset($ThisFileInfo['mime_type']); - - $thisfile_avdataoffset = 44; - - if (isset($thisfile_riff['CDDA']['fmt '][0]['data'])) { - // shortcut - $thisfile_riff_CDDA_fmt_0 = &$thisfile_riff['CDDA']['fmt '][0]; - - $thisfile_riff_CDDA_fmt_0['unknown1'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_CDDA_fmt_0['data'], 0, 2)); - $thisfile_riff_CDDA_fmt_0['track_num'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_CDDA_fmt_0['data'], 2, 2)); - $thisfile_riff_CDDA_fmt_0['disc_id'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_CDDA_fmt_0['data'], 4, 4)); - $thisfile_riff_CDDA_fmt_0['start_offset_frame'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_CDDA_fmt_0['data'], 8, 4)); - $thisfile_riff_CDDA_fmt_0['playtime_frames'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_CDDA_fmt_0['data'], 12, 4)); - $thisfile_riff_CDDA_fmt_0['unknown6'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_CDDA_fmt_0['data'], 16, 4)); - $thisfile_riff_CDDA_fmt_0['unknown7'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_CDDA_fmt_0['data'], 20, 4)); - - $thisfile_riff_CDDA_fmt_0['start_offset_seconds'] = (float) $thisfile_riff_CDDA_fmt_0['start_offset_frame'] / 75; - $thisfile_riff_CDDA_fmt_0['playtime_seconds'] = (float) $thisfile_riff_CDDA_fmt_0['playtime_frames'] / 75; - $ThisFileInfo['comments']['track'] = $thisfile_riff_CDDA_fmt_0['track_num']; - $ThisFileInfo['playtime_seconds'] = $thisfile_riff_CDDA_fmt_0['playtime_seconds']; - - // hardcoded data for CD-audio - $thisfile_audio['sample_rate'] = 44100; - $thisfile_audio['channels'] = 2; - $thisfile_audio['bits_per_sample'] = 16; - $thisfile_audio['bitrate'] = $thisfile_audio['sample_rate'] * $thisfile_audio['channels'] * $thisfile_audio['bits_per_sample']; - $thisfile_audio['bitrate_mode'] = 'cbr'; - } - break; - - - case 'AIFF': - case 'AIFC': - $thisfile_audio['bitrate_mode'] = 'cbr'; - $thisfile_audio_dataformat = 'aiff'; - $thisfile_audio['lossless'] = true; - $ThisFileInfo['mime_type'] = 'audio/x-aiff'; - - if (isset($thisfile_riff[$RIFFsubtype]['SSND'][0]['offset'])) { - $thisfile_avdataoffset = $thisfile_riff[$RIFFsubtype]['SSND'][0]['offset'] + 8; - $thisfile_avdataend = $thisfile_avdataoffset + $thisfile_riff[$RIFFsubtype]['SSND'][0]['size']; - if ($thisfile_avdataend > $ThisFileInfo['filesize']) { - if (($thisfile_avdataend == ($ThisFileInfo['filesize'] + 1)) && (($ThisFileInfo['filesize'] % 2) == 1)) { - // structures rounded to 2-byte boundary, but dumb encoders - // forget to pad end of file to make this actually work - } else { - $ThisFileInfo['warning'][] = 'Probable truncated AIFF file: expecting '.$thisfile_riff[$RIFFsubtype]['SSND'][0]['size'].' bytes of audio data, only '.($ThisFileInfo['filesize'] - $thisfile_avdataoffset).' bytes found'; - } - $thisfile_avdataend = $ThisFileInfo['filesize']; - } - } - - if (isset($thisfile_riff[$RIFFsubtype]['COMM'][0]['data'])) { - - // shortcut - $thisfile_riff_RIFFsubtype_COMM_0_data = &$thisfile_riff[$RIFFsubtype]['COMM'][0]['data']; - - $thisfile_riff_audio['channels'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 0, 2), true); - $thisfile_riff_audio['total_samples'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 2, 4), false); - $thisfile_riff_audio['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 6, 2), true); - $thisfile_riff_audio['sample_rate'] = (int) getid3_lib::BigEndian2Float(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 8, 10)); - - if ($thisfile_riff[$RIFFsubtype]['COMM'][0]['size'] > 18) { - $thisfile_riff_audio['codec_fourcc'] = substr($thisfile_riff_RIFFsubtype_COMM_0_data, 18, 4); - $CodecNameSize = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 22, 1), false); - $thisfile_riff_audio['codec_name'] = substr($thisfile_riff_RIFFsubtype_COMM_0_data, 23, $CodecNameSize); - switch ($thisfile_riff_audio['codec_name']) { - case 'NONE': - $thisfile_audio['codec'] = 'Pulse Code Modulation (PCM)'; - $thisfile_audio['lossless'] = true; - break; - - case '': - switch ($thisfile_riff_audio['codec_fourcc']) { - // http://developer.apple.com/qa/snd/snd07.html - case 'sowt': - $thisfile_riff_audio['codec_name'] = 'Two\'s Compliment Little-Endian PCM'; - $thisfile_audio['lossless'] = true; - break; - - case 'twos': - $thisfile_riff_audio['codec_name'] = 'Two\'s Compliment Big-Endian PCM'; - $thisfile_audio['lossless'] = true; - break; - - default: - break; - } - break; - - default: - $thisfile_audio['codec'] = $thisfile_riff_audio['codec_name']; - $thisfile_audio['lossless'] = false; - break; - } - } - - $thisfile_audio['channels'] = $thisfile_riff_audio['channels']; - if ($thisfile_riff_audio['bits_per_sample'] > 0) { - $thisfile_audio['bits_per_sample'] = $thisfile_riff_audio['bits_per_sample']; - } - $thisfile_audio['sample_rate'] = $thisfile_riff_audio['sample_rate']; - if ($thisfile_audio['sample_rate'] == 0) { - $ThisFileInfo['error'][] = 'Corrupted AIFF file: sample_rate == zero'; - return false; - } - $ThisFileInfo['playtime_seconds'] = $thisfile_riff_audio['total_samples'] / $thisfile_audio['sample_rate']; - } - - if (isset($thisfile_riff[$RIFFsubtype]['COMT'])) { - $offset = 0; - $CommentCount = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), false); - $offset += 2; - for ($i = 0; $i < $CommentCount; $i++) { - $ThisFileInfo['comments_raw'][$i]['timestamp'] = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 4), false); - $offset += 4; - $ThisFileInfo['comments_raw'][$i]['marker_id'] = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), true); - $offset += 2; - $CommentLength = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), false); - $offset += 2; - $ThisFileInfo['comments_raw'][$i]['comment'] = substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, $CommentLength); - $offset += $CommentLength; - - $ThisFileInfo['comments_raw'][$i]['timestamp_unix'] = getid3_lib::DateMac2Unix($ThisFileInfo['comments_raw'][$i]['timestamp']); - $thisfile_riff['comments']['comment'][] = $ThisFileInfo['comments_raw'][$i]['comment']; - } - } - - $CommentsChunkNames = array('NAME'=>'title', 'author'=>'artist', '(c) '=>'copyright', 'ANNO'=>'comment'); - foreach ($CommentsChunkNames as $key => $value) { - if (isset($thisfile_riff[$RIFFsubtype][$key][0]['data'])) { - $thisfile_riff['comments'][$value][] = $thisfile_riff[$RIFFsubtype][$key][0]['data']; - } - } - break; - - case '8SVX': - $thisfile_audio['bitrate_mode'] = 'cbr'; - $thisfile_audio_dataformat = '8svx'; - $thisfile_audio['bits_per_sample'] = 8; - $thisfile_audio['channels'] = 1; // overridden below, if need be - $ThisFileInfo['mime_type'] = 'audio/x-aiff'; - - if (isset($thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'])) { - $thisfile_avdataoffset = $thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'] + 8; - $thisfile_avdataend = $thisfile_avdataoffset + $thisfile_riff[$RIFFsubtype]['BODY'][0]['size']; - if ($thisfile_avdataend > $ThisFileInfo['filesize']) { - $ThisFileInfo['warning'][] = 'Probable truncated AIFF file: expecting '.$thisfile_riff[$RIFFsubtype]['BODY'][0]['size'].' bytes of audio data, only '.($ThisFileInfo['filesize'] - $thisfile_avdataoffset).' bytes found'; - } - } - - if (isset($thisfile_riff[$RIFFsubtype]['VHDR'][0]['offset'])) { - // shortcut - $thisfile_riff_RIFFsubtype_VHDR_0 = &$thisfile_riff[$RIFFsubtype]['VHDR'][0]; - - $thisfile_riff_RIFFsubtype_VHDR_0['oneShotHiSamples'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 0, 4)); - $thisfile_riff_RIFFsubtype_VHDR_0['repeatHiSamples'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 4, 4)); - $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerHiCycle'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 8, 4)); - $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerSec'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 12, 2)); - $thisfile_riff_RIFFsubtype_VHDR_0['ctOctave'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 14, 1)); - $thisfile_riff_RIFFsubtype_VHDR_0['sCompression'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 15, 1)); - $thisfile_riff_RIFFsubtype_VHDR_0['Volume'] = getid3_lib::FixedPoint16_16(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 16, 4)); - - $thisfile_audio['sample_rate'] = $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerSec']; - - switch ($thisfile_riff_RIFFsubtype_VHDR_0['sCompression']) { - case 0: - $thisfile_audio['codec'] = 'Pulse Code Modulation (PCM)'; - $thisfile_audio['lossless'] = true; - $ActualBitsPerSample = 8; - break; - - case 1: - $thisfile_audio['codec'] = 'Fibonacci-delta encoding'; - $thisfile_audio['lossless'] = false; - $ActualBitsPerSample = 4; - break; - - default: - $ThisFileInfo['warning'][] = 'Unexpected sCompression value in 8SVX.VHDR chunk - expecting 0 or 1, found "'.sCompression.'"'; - break; - } - } - - if (isset($thisfile_riff[$RIFFsubtype]['CHAN'][0]['data'])) { - $ChannelsIndex = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['CHAN'][0]['data'], 0, 4)); - switch ($ChannelsIndex) { - case 6: // Stereo - $thisfile_audio['channels'] = 2; - break; - - case 2: // Left channel only - case 4: // Right channel only - $thisfile_audio['channels'] = 1; - break; - - default: - $ThisFileInfo['warning'][] = 'Unexpected value in 8SVX.CHAN chunk - expecting 2 or 4 or 6, found "'.$ChannelsIndex.'"'; - break; - } - - } - - $CommentsChunkNames = array('NAME'=>'title', 'author'=>'artist', '(c) '=>'copyright', 'ANNO'=>'comment'); - foreach ($CommentsChunkNames as $key => $value) { - if (isset($thisfile_riff[$RIFFsubtype][$key][0]['data'])) { - $thisfile_riff['comments'][$value][] = $thisfile_riff[$RIFFsubtype][$key][0]['data']; - } - } - - $thisfile_audio['bitrate'] = $thisfile_audio['sample_rate'] * $ActualBitsPerSample * $thisfile_audio['channels']; - if (!empty($thisfile_audio['bitrate'])) { - $ThisFileInfo['playtime_seconds'] = ($thisfile_avdataend - $thisfile_avdataoffset) / ($thisfile_audio['bitrate'] / 8); - } - break; - - - case 'CDXA': - $ThisFileInfo['mime_type'] = 'video/mpeg'; - if (!empty($thisfile_riff['CDXA']['data'][0]['size'])) { - $GETID3_ERRORARRAY = &$ThisFileInfo['warning']; - if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.mpeg.php', __FILE__, false)) { - $dummy = $ThisFileInfo; - $dummy['error'] = array(); - $mpeg_scanner = new getid3_mpeg($fd, $dummy); - if (empty($dummy['error'])) { - $ThisFileInfo['audio'] = $dummy['audio']; - $ThisFileInfo['video'] = $dummy['video']; - $ThisFileInfo['mpeg'] = $dummy['mpeg']; - $ThisFileInfo['warning'] = $dummy['warning']; - } - } - } - break; - - - default: - $ThisFileInfo['error'][] = 'Unknown RIFF type: expecting one of (WAVE|RMP3|AVI |CDDA|AIFF|AIFC|8SVX|CDXA), found "'.$RIFFsubtype.'" instead'; - unset($ThisFileInfo['fileformat']); - break; - } - - if (isset($thisfile_riff_WAVE['DISP']) && is_array($thisfile_riff_WAVE['DISP'])) { - $thisfile_riff['comments']['title'][] = trim(substr($thisfile_riff_WAVE['DISP'][count($thisfile_riff_WAVE['DISP']) - 1]['data'], 4)); - } - if (isset($thisfile_riff_WAVE['INFO']) && is_array($thisfile_riff_WAVE['INFO'])) { - $this->RIFFcommentsParse($thisfile_riff_WAVE['INFO'], $thisfile_riff['comments']); - } - - if (empty($thisfile_audio['encoder']) && !empty($ThisFileInfo['mpeg']['audio']['LAME']['short_version'])) { - $thisfile_audio['encoder'] = $ThisFileInfo['mpeg']['audio']['LAME']['short_version']; - } - - if (!isset($ThisFileInfo['playtime_seconds'])) { - $ThisFileInfo['playtime_seconds'] = 0; - } - if (isset($thisfile_riff_raw['avih']['dwTotalFrames']) && isset($thisfile_riff_raw['avih']['dwMicroSecPerFrame'])) { - $ThisFileInfo['playtime_seconds'] = $thisfile_riff_raw['avih']['dwTotalFrames'] * ($thisfile_riff_raw['avih']['dwMicroSecPerFrame'] / 1000000); - } - - if ($ThisFileInfo['playtime_seconds'] > 0) { - if (isset($thisfile_riff_audio) && isset($thisfile_riff_video)) { - - if (!isset($ThisFileInfo['bitrate'])) { - $ThisFileInfo['bitrate'] = ((($thisfile_avdataend - $thisfile_avdataoffset) / $ThisFileInfo['playtime_seconds']) * 8); - } - - } elseif (isset($thisfile_riff_audio) && !isset($thisfile_riff_video)) { - - if (!isset($thisfile_audio['bitrate'])) { - $thisfile_audio['bitrate'] = ((($thisfile_avdataend - $thisfile_avdataoffset) / $ThisFileInfo['playtime_seconds']) * 8); - } - - } elseif (!isset($thisfile_riff_audio) && isset($thisfile_riff_video)) { - - if (!isset($thisfile_video['bitrate'])) { - $thisfile_video['bitrate'] = ((($thisfile_avdataend - $thisfile_avdataoffset) / $ThisFileInfo['playtime_seconds']) * 8); - } - - } - } - - - if (isset($thisfile_riff_video) && isset($thisfile_audio['bitrate']) && ($thisfile_audio['bitrate'] > 0) && ($ThisFileInfo['playtime_seconds'] > 0)) { - - $ThisFileInfo['bitrate'] = ((($thisfile_avdataend - $thisfile_avdataoffset) / $ThisFileInfo['playtime_seconds']) * 8); - $thisfile_audio['bitrate'] = 0; - $thisfile_video['bitrate'] = $ThisFileInfo['bitrate']; - foreach ($thisfile_riff_audio as $channelnumber => $audioinfoarray) { - $thisfile_video['bitrate'] -= $audioinfoarray['bitrate']; - $thisfile_audio['bitrate'] += $audioinfoarray['bitrate']; - } - if ($thisfile_video['bitrate'] <= 0) { - unset($thisfile_video['bitrate']); - } - if ($thisfile_audio['bitrate'] <= 0) { - unset($thisfile_audio['bitrate']); - } - } - - if (isset($ThisFileInfo['mpeg']['audio'])) { - $thisfile_audio_dataformat = 'mp'.$ThisFileInfo['mpeg']['audio']['layer']; - $thisfile_audio['sample_rate'] = $ThisFileInfo['mpeg']['audio']['sample_rate']; - $thisfile_audio['channels'] = $ThisFileInfo['mpeg']['audio']['channels']; - $thisfile_audio['bitrate'] = $ThisFileInfo['mpeg']['audio']['bitrate']; - $thisfile_audio['bitrate_mode'] = strtolower($ThisFileInfo['mpeg']['audio']['bitrate_mode']); - if (!empty($ThisFileInfo['mpeg']['audio']['codec'])) { - $thisfile_audio['codec'] = $ThisFileInfo['mpeg']['audio']['codec'].' '.$thisfile_audio['codec']; - } - if (!empty($thisfile_audio['streams'])) { - foreach ($thisfile_audio['streams'] as $streamnumber => $streamdata) { - if ($streamdata['dataformat'] == $thisfile_audio_dataformat) { - $thisfile_audio['streams'][$streamnumber]['sample_rate'] = $thisfile_audio['sample_rate']; - $thisfile_audio['streams'][$streamnumber]['channels'] = $thisfile_audio['channels']; - $thisfile_audio['streams'][$streamnumber]['bitrate'] = $thisfile_audio['bitrate']; - $thisfile_audio['streams'][$streamnumber]['bitrate_mode'] = $thisfile_audio['bitrate_mode']; - $thisfile_audio['streams'][$streamnumber]['codec'] = $thisfile_audio['codec']; - } - } - } - $thisfile_audio['encoder_options'] = getid3_mp3::GuessEncoderOptions($ThisFileInfo); - } - - - if (!empty($thisfile_riff_raw['fmt ']['wBitsPerSample']) && ($thisfile_riff_raw['fmt ']['wBitsPerSample'] > 0)) { - switch ($thisfile_audio_dataformat) { - case 'ac3': - // ignore bits_per_sample - break; - - default: - $thisfile_audio['bits_per_sample'] = $thisfile_riff_raw['fmt ']['wBitsPerSample']; - break; - } - } - - - if (empty($thisfile_riff_raw)) { - unset($thisfile_riff['raw']); - } - if (empty($thisfile_riff_audio)) { - unset($thisfile_riff['audio']); - } - if (empty($thisfile_riff_video)) { - unset($thisfile_riff['video']); - } - - return true; - } - - - function RIFFcommentsParse(&$RIFFinfoArray, &$CommentsTargetArray) { - $RIFFinfoKeyLookup = array( - 'IARL'=>'archivallocation', - 'IART'=>'artist', - 'ICDS'=>'costumedesigner', - 'ICMS'=>'commissionedby', - 'ICMT'=>'comment', - 'ICNT'=>'country', - 'ICOP'=>'copyright', - 'ICRD'=>'creationdate', - 'IDIM'=>'dimensions', - 'IDIT'=>'digitizationdate', - 'IDPI'=>'resolution', - 'IDST'=>'distributor', - 'IEDT'=>'editor', - 'IENG'=>'engineers', - 'IFRM'=>'accountofparts', - 'IGNR'=>'genre', - 'IKEY'=>'keywords', - 'ILGT'=>'lightness', - 'ILNG'=>'language', - 'IMED'=>'orignalmedium', - 'IMUS'=>'composer', - 'INAM'=>'title', - 'IPDS'=>'productiondesigner', - 'IPLT'=>'palette', - 'IPRD'=>'product', - 'IPRO'=>'producer', - 'IPRT'=>'part', - 'IRTD'=>'rating', - 'ISBJ'=>'subject', - 'ISFT'=>'software', - 'ISGN'=>'secondarygenre', - 'ISHP'=>'sharpness', - 'ISRC'=>'sourcesupplier', - 'ISRF'=>'digitizationsource', - 'ISTD'=>'productionstudio', - 'ISTR'=>'starring', - 'ITCH'=>'encoded_by', - 'IWEB'=>'url', - 'IWRI'=>'writer' - ); - foreach ($RIFFinfoKeyLookup as $key => $value) { - if (isset($RIFFinfoArray[$key])) { - foreach ($RIFFinfoArray[$key] as $commentid => $commentdata) { - if (trim($commentdata['data']) != '') { - @$CommentsTargetArray[$value][] = trim($commentdata['data']); - } - } - } - } - return true; - } - - function ParseRIFF(&$fd, $startoffset, $maxoffset, &$ThisFileInfo) { - - $maxoffset = min($maxoffset, $ThisFileInfo['avdataend']); - - $RIFFchunk = false; - - fseek($fd, $startoffset, SEEK_SET); - - while (ftell($fd) < $maxoffset) { - $chunkname = fread($fd, 4); - if (strlen($chunkname) < 4) { - $ThisFileInfo['error'][] = 'Expecting chunk name at offset '.(ftell($fd) - 4).' but found nothing. Aborting RIFF parsing.'; - break; - } - - $chunksize = getid3_riff::EitherEndian2Int($ThisFileInfo, fread($fd, 4)); - if ($chunksize == 0) { - $ThisFileInfo['error'][] = 'Chunk size at offset '.(ftell($fd) - 4).' is zero. Aborting RIFF parsing.'; - break; - } - if (($chunksize % 2) != 0) { - // all structures are packed on word boundaries - $chunksize++; - } - - switch ($chunkname) { - case 'LIST': - $listname = fread($fd, 4); - switch ($listname) { - case 'movi': - case 'rec ': - $RIFFchunk[$listname]['offset'] = ftell($fd) - 4; - $RIFFchunk[$listname]['size'] = $chunksize; - - static $ParsedAudioStream = false; - if ($ParsedAudioStream) { - - // skip over - - } else { - - $WhereWeWere = ftell($fd); - $AudioChunkHeader = fread($fd, 12); - $AudioChunkStreamNum = substr($AudioChunkHeader, 0, 2); - $AudioChunkStreamType = substr($AudioChunkHeader, 2, 2); - $AudioChunkSize = getid3_lib::LittleEndian2Int(substr($AudioChunkHeader, 4, 4)); - - if ($AudioChunkStreamType == 'wb') { - $FirstFourBytes = substr($AudioChunkHeader, 8, 4); - if (preg_match('/^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]/s', $FirstFourBytes)) { - - // MP3 - if (getid3_mp3::MPEGaudioHeaderBytesValid($FirstFourBytes)) { - $dummy = $ThisFileInfo; - $dummy['avdataoffset'] = ftell($fd) - 4; - $dummy['avdataend'] = ftell($fd) + $AudioChunkSize; - getid3_mp3::getOnlyMPEGaudioInfo($fd, $dummy, $dummy['avdataoffset'], false); - if (isset($dummy['mpeg']['audio'])) { - $ThisFileInfo = $dummy; - $ThisFileInfo['audio']['dataformat'] = 'mp'.$ThisFileInfo['mpeg']['audio']['layer']; - $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['mpeg']['audio']['sample_rate']; - $ThisFileInfo['audio']['channels'] = $ThisFileInfo['mpeg']['audio']['channels']; - $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['bitrate']; - $ThisFileInfo['bitrate'] = $ThisFileInfo['audio']['bitrate']; - $ThisFileInfo['audio']['bitrate_mode'] = strtolower($ThisFileInfo['mpeg']['audio']['bitrate_mode']); - } - } - - } elseif (preg_match('/^\x0B\x77/s', $FirstFourBytes)) { - - // AC3 - $GETID3_ERRORARRAY = &$ThisFileInfo['warning']; - if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, false)) { - - $dummy = $ThisFileInfo; - $dummy['avdataoffset'] = ftell($fd) - 4; - $dummy['avdataend'] = ftell($fd) + $AudioChunkSize; - $dummy['error'] = array(); - $ac3_tag = new getid3_ac3($fd, $dummy); - if (empty($dummy['error'])) { - $ThisFileInfo['audio'] = $dummy['audio']; - $ThisFileInfo['ac3'] = $dummy['ac3']; - $ThisFileInfo['warning'] = $dummy['warning']; - } - - } - - } - - } - - $ParsedAudioStream = true; - fseek($fd, $WhereWeWere, SEEK_SET); - - } - fseek($fd, $chunksize - 4, SEEK_CUR); - break; - - default: - if (!isset($RIFFchunk[$listname])) { - $RIFFchunk[$listname] = array(); - } - $LISTchunkParent = $listname; - $LISTchunkMaxOffset = ftell($fd) - 4 + $chunksize; - if ($parsedChunk = getid3_riff::ParseRIFF($fd, ftell($fd), ftell($fd) + $chunksize - 4, $ThisFileInfo)) { - $RIFFchunk[$listname] = array_merge_recursive($RIFFchunk[$listname], $parsedChunk); - } - break; - } - break; - - default: - $thisindex = 0; - if (isset($RIFFchunk[$chunkname]) && is_array($RIFFchunk[$chunkname])) { - $thisindex = count($RIFFchunk[$chunkname]); - } - $RIFFchunk[$chunkname][$thisindex]['offset'] = ftell($fd) - 8; - $RIFFchunk[$chunkname][$thisindex]['size'] = $chunksize; - switch ($chunkname) { - case 'data': - $ThisFileInfo['avdataoffset'] = ftell($fd); - $ThisFileInfo['avdataend'] = $ThisFileInfo['avdataoffset'] + $chunksize; - - $RIFFdataChunkContentsTest = fread($fd, 36); - - if ((strlen($RIFFdataChunkContentsTest) > 0) && preg_match('/^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]/s', substr($RIFFdataChunkContentsTest, 0, 4))) { - - // Probably is MP3 data - if (getid3_mp3::MPEGaudioHeaderBytesValid(substr($RIFFdataChunkContentsTest, 0, 4))) { - getid3_mp3::getOnlyMPEGaudioInfo($fd, $ThisFileInfo, $RIFFchunk[$chunkname][$thisindex]['offset'], false); - } - - } elseif ((strlen($RIFFdataChunkContentsTest) > 0) && (substr($RIFFdataChunkContentsTest, 0, 2) == "\x0B\x77")) { - - // This is probably AC-3 data - $GETID3_ERRORARRAY = &$ThisFileInfo['warning']; - if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, false)) { - - $dummy = $ThisFileInfo; - $dummy['avdataoffset'] = $RIFFchunk[$chunkname][$thisindex]['offset']; - $dummy['avdataend'] = $dummy['avdataoffset'] + $RIFFchunk[$chunkname][$thisindex]['size']; - $dummy['error'] = array(); - - $ac3_tag = new getid3_ac3($fd, $dummy); - if (empty($dummy['error'])) { - $ThisFileInfo['audio'] = $dummy['audio']; - $ThisFileInfo['ac3'] = $dummy['ac3']; - $ThisFileInfo['warning'] = $dummy['warning']; - } - - } - - } elseif ((strlen($RIFFdataChunkContentsTest) > 0) && (substr($RIFFdataChunkContentsTest, 8, 2) == "\x77\x0B")) { - - // Dolby Digital WAV - // AC-3 content, but not encoded in same format as normal AC-3 file - // For one thing, byte order is swapped - - $GETID3_ERRORARRAY = &$ThisFileInfo['warning']; - if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, false)) { - - // ok to use tmpfile here - only 56 bytes - if ($fd_temp = tmpfile()) { - - for ($i = 0; $i < 28; $i += 2) { - // swap byte order - fwrite($fd_temp, substr($RIFFdataChunkContentsTest, 8 + $i + 1, 1)); - fwrite($fd_temp, substr($RIFFdataChunkContentsTest, 8 + $i + 0, 1)); - } - - $dummy = $ThisFileInfo; - $dummy['avdataoffset'] = 0; - $dummy['avdataend'] = 20; - $dummy['error'] = array(); - $ac3_tag = new getid3_ac3($fd_temp, $dummy); - fclose($fd_temp); - if (empty($dummy['error'])) { - $ThisFileInfo['audio'] = $dummy['audio']; - $ThisFileInfo['ac3'] = $dummy['ac3']; - $ThisFileInfo['warning'] = $dummy['warning']; - } else { - $ThisFileInfo['error'][] = 'Errors parsing DolbyDigital WAV: '.explode(';', $dummy['error']); - } - - } else { - - $ThisFileInfo['error'][] = 'Could not create temporary file to analyze DolbyDigital WAV'; - - } - - } - - } elseif ((strlen($RIFFdataChunkContentsTest) > 0) && (substr($RIFFdataChunkContentsTest, 0, 4) == 'wvpk')) { - - // This is WavPack data - $ThisFileInfo['wavpack']['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset']; - $ThisFileInfo['wavpack']['size'] = getid3_lib::LittleEndian2Int(substr($RIFFdataChunkContentsTest, 4, 4)); - getid3_riff::RIFFparseWavPackHeader(substr($RIFFdataChunkContentsTest, 8, 28), $ThisFileInfo); - - } else { - - // This is some other kind of data (quite possibly just PCM) - // do nothing special, just skip it - - } - fseek($fd, $RIFFchunk[$chunkname][$thisindex]['offset'] + 8 + $chunksize, SEEK_SET); - break; - - case 'bext': - case 'cart': - case 'fmt ': - case 'MEXT': - case 'DISP': - // always read data in - $RIFFchunk[$chunkname][$thisindex]['data'] = fread($fd, $chunksize); - break; - - default: - if (!empty($LISTchunkParent) && (($RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size']) <= $LISTchunkMaxOffset)) { - $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset']; - $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['size'] = $RIFFchunk[$chunkname][$thisindex]['size']; - unset($RIFFchunk[$chunkname][$thisindex]['offset']); - unset($RIFFchunk[$chunkname][$thisindex]['size']); - if (isset($RIFFchunk[$chunkname][$thisindex]) && empty($RIFFchunk[$chunkname][$thisindex])) { - unset($RIFFchunk[$chunkname][$thisindex]); - } - if (isset($RIFFchunk[$chunkname]) && empty($RIFFchunk[$chunkname])) { - unset($RIFFchunk[$chunkname]); - } - $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['data'] = fread($fd, $chunksize); - } elseif ($chunksize < 2048) { - // only read data in if smaller than 2kB - $RIFFchunk[$chunkname][$thisindex]['data'] = fread($fd, $chunksize); - } else { - fseek($fd, $chunksize, SEEK_CUR); - } - break; - } - break; - - } - - } - - return $RIFFchunk; - } - - - function ParseRIFFdata(&$RIFFdata, &$ThisFileInfo) { - if ($RIFFdata) { - - $tempfile = tempnam('*', 'getID3'); - $fp_temp = fopen($tempfile, "wb"); - $RIFFdataLength = strlen($RIFFdata); - $NewLengthString = getid3_lib::LittleEndian2String($RIFFdataLength, 4); - for ($i = 0; $i < 4; $i++) { - $RIFFdata{$i + 4} = $NewLengthString{$i}; - } - fwrite($fp_temp, $RIFFdata); - fclose($fp_temp); - - $fp_temp = fopen($tempfile, "rb"); - $dummy = array('filesize'=>$RIFFdataLength, 'filenamepath'=>$ThisFileInfo['filenamepath'], 'tags'=>$ThisFileInfo['tags'], 'avdataoffset'=>0, 'avdataend'=>$RIFFdataLength, 'warning'=>$ThisFileInfo['warning'], 'error'=>$ThisFileInfo['error'], 'comments'=>$ThisFileInfo['comments'], 'audio'=>(isset($ThisFileInfo['audio']) ? $ThisFileInfo['audio'] : array()), 'video'=>(isset($ThisFileInfo['video']) ? $ThisFileInfo['video'] : array())); - $riff = new getid3_riff($fp_temp, $dummy); - $ThisFileInfo['riff'] = $dummy['riff']; - $ThisFileInfo['warning'] = $dummy['warning']; - $ThisFileInfo['error'] = $dummy['error']; - $ThisFileInfo['tags'] = $dummy['tags']; - $ThisFileInfo['comments'] = $dummy['comments']; - fclose($fp_temp); - unlink($tempfile); - return true; - } - return false; - } - - - function RIFFparseWAVEFORMATex($WaveFormatExData) { - // shortcut - $WaveFormatEx['raw'] = array(); - $WaveFormatEx_raw = &$WaveFormatEx['raw']; - - $WaveFormatEx_raw['wFormatTag'] = getid3_lib::LittleEndian2Int(substr($WaveFormatExData, 0, 2)); - $WaveFormatEx_raw['nChannels'] = getid3_lib::LittleEndian2Int(substr($WaveFormatExData, 2, 2)); - $WaveFormatEx_raw['nSamplesPerSec'] = getid3_lib::LittleEndian2Int(substr($WaveFormatExData, 4, 4)); - $WaveFormatEx_raw['nAvgBytesPerSec'] = getid3_lib::LittleEndian2Int(substr($WaveFormatExData, 8, 4)); - $WaveFormatEx_raw['nBlockAlign'] = getid3_lib::LittleEndian2Int(substr($WaveFormatExData, 12, 2)); - $WaveFormatEx_raw['wBitsPerSample'] = getid3_lib::LittleEndian2Int(substr($WaveFormatExData, 14, 2)); - if (strlen($WaveFormatExData) > 16) { - $WaveFormatEx_raw['cbSize'] = getid3_lib::LittleEndian2Int(substr($WaveFormatExData, 16, 2)); - } - - $WaveFormatEx['codec'] = getid3_riff::RIFFwFormatTagLookup($WaveFormatEx_raw['wFormatTag']); - $WaveFormatEx['channels'] = $WaveFormatEx_raw['nChannels']; - $WaveFormatEx['sample_rate'] = $WaveFormatEx_raw['nSamplesPerSec']; - $WaveFormatEx['bitrate'] = $WaveFormatEx_raw['nAvgBytesPerSec'] * 8; - $WaveFormatEx['bits_per_sample'] = $WaveFormatEx_raw['wBitsPerSample']; - - return $WaveFormatEx; - } - - - function RIFFparseWavPackHeader($WavPackChunkData, &$ThisFileInfo) { - // typedef struct { - // char ckID [4]; - // long ckSize; - // short version; - // short bits; // added for version 2.00 - // short flags, shift; // added for version 3.00 - // long total_samples, crc, crc2; - // char extension [4], extra_bc, extras [3]; - // } WavpackHeader; - - // shortcut - $ThisFileInfo['wavpack'] = array(); - $thisfile_wavpack = &$ThisFileInfo['wavpack']; - - $thisfile_wavpack['version'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 0, 2)); - if ($thisfile_wavpack['version'] >= 2) { - $thisfile_wavpack['bits'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 2, 2)); - } - if ($thisfile_wavpack['version'] >= 3) { - $thisfile_wavpack['flags_raw'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 4, 2)); - $thisfile_wavpack['shift'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 6, 2)); - $thisfile_wavpack['total_samples'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 8, 4)); - $thisfile_wavpack['crc1'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 12, 4)); - $thisfile_wavpack['crc2'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 16, 4)); - $thisfile_wavpack['extension'] = substr($WavPackChunkData, 20, 4); - $thisfile_wavpack['extra_bc'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 24, 1)); - for ($i = 0; $i <= 2; $i++) { - $thisfile_wavpack['extras'][] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 25 + $i, 1)); - } - - // shortcut - $thisfile_wavpack['flags'] = array(); - $thisfile_wavpack_flags = &$thisfile_wavpack['flags']; - - $thisfile_wavpack_flags['mono'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000001); - $thisfile_wavpack_flags['fast_mode'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000002); - $thisfile_wavpack_flags['raw_mode'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000004); - $thisfile_wavpack_flags['calc_noise'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000008); - $thisfile_wavpack_flags['high_quality'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000010); - $thisfile_wavpack_flags['3_byte_samples'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000020); - $thisfile_wavpack_flags['over_20_bits'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000040); - $thisfile_wavpack_flags['use_wvc'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000080); - $thisfile_wavpack_flags['noiseshaping'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000100); - $thisfile_wavpack_flags['very_fast_mode'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000200); - $thisfile_wavpack_flags['new_high_quality'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000400); - $thisfile_wavpack_flags['cancel_extreme'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000800); - $thisfile_wavpack_flags['cross_decorrelation'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x001000); - $thisfile_wavpack_flags['new_decorrelation'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x002000); - $thisfile_wavpack_flags['joint_stereo'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x004000); - $thisfile_wavpack_flags['extra_decorrelation'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x008000); - $thisfile_wavpack_flags['override_noiseshape'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x010000); - $thisfile_wavpack_flags['override_jointstereo'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x020000); - $thisfile_wavpack_flags['copy_source_filetime'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x040000); - $thisfile_wavpack_flags['create_exe'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x080000); - } - - return true; - } - - function RIFFwFormatTagLookup($wFormatTag) { - - $begin = __LINE__; - - /** This is not a comment! - - 0x0000 Microsoft Unknown Wave Format - 0x0001 Pulse Code Modulation (PCM) - 0x0002 Microsoft ADPCM - 0x0003 IEEE Float - 0x0004 Compaq Computer VSELP - 0x0005 IBM CVSD - 0x0006 Microsoft A-Law - 0x0007 Microsoft mu-Law - 0x0008 Microsoft DTS - 0x0010 OKI ADPCM - 0x0011 Intel DVI/IMA ADPCM - 0x0012 Videologic MediaSpace ADPCM - 0x0013 Sierra Semiconductor ADPCM - 0x0014 Antex Electronics G.723 ADPCM - 0x0015 DSP Solutions DigiSTD - 0x0016 DSP Solutions DigiFIX - 0x0017 Dialogic OKI ADPCM - 0x0018 MediaVision ADPCM - 0x0019 Hewlett-Packard CU - 0x0020 Yamaha ADPCM - 0x0021 Speech Compression Sonarc - 0x0022 DSP Group TrueSpeech - 0x0023 Echo Speech EchoSC1 - 0x0024 Audiofile AF36 - 0x0025 Audio Processing Technology APTX - 0x0026 AudioFile AF10 - 0x0027 Prosody 1612 - 0x0028 LRC - 0x0030 Dolby AC2 - 0x0031 Microsoft GSM 6.10 - 0x0032 MSNAudio - 0x0033 Antex Electronics ADPCME - 0x0034 Control Resources VQLPC - 0x0035 DSP Solutions DigiREAL - 0x0036 DSP Solutions DigiADPCM - 0x0037 Control Resources CR10 - 0x0038 Natural MicroSystems VBXADPCM - 0x0039 Crystal Semiconductor IMA ADPCM - 0x003A EchoSC3 - 0x003B Rockwell ADPCM - 0x003C Rockwell Digit LK - 0x003D Xebec - 0x0040 Antex Electronics G.721 ADPCM - 0x0041 G.728 CELP - 0x0042 MSG723 - 0x0050 MPEG Layer-2 or Layer-1 - 0x0052 RT24 - 0x0053 PAC - 0x0055 MPEG Layer-3 - 0x0059 Lucent G.723 - 0x0060 Cirrus - 0x0061 ESPCM - 0x0062 Voxware - 0x0063 Canopus Atrac - 0x0064 G.726 ADPCM - 0x0065 G.722 ADPCM - 0x0066 DSAT - 0x0067 DSAT Display - 0x0069 Voxware Byte Aligned - 0x0070 Voxware AC8 - 0x0071 Voxware AC10 - 0x0072 Voxware AC16 - 0x0073 Voxware AC20 - 0x0074 Voxware MetaVoice - 0x0075 Voxware MetaSound - 0x0076 Voxware RT29HW - 0x0077 Voxware VR12 - 0x0078 Voxware VR18 - 0x0079 Voxware TQ40 - 0x0080 Softsound - 0x0081 Voxware TQ60 - 0x0082 MSRT24 - 0x0083 G.729A - 0x0084 MVI MV12 - 0x0085 DF G.726 - 0x0086 DF GSM610 - 0x0088 ISIAudio - 0x0089 Onlive - 0x0091 SBC24 - 0x0092 Dolby AC3 SPDIF - 0x0093 MediaSonic G.723 - 0x0094 Aculab PLC Prosody 8kbps - 0x0097 ZyXEL ADPCM - 0x0098 Philips LPCBB - 0x0099 Packed - 0x00FF AAC - 0x0100 Rhetorex ADPCM - 0x0101 IBM mu-law - 0x0102 IBM A-law - 0x0103 IBM AVC Adaptive Differential Pulse Code Modulation (ADPCM) - 0x0111 Vivo G.723 - 0x0112 Vivo Siren - 0x0123 Digital G.723 - 0x0125 Sanyo LD ADPCM - 0x0130 Sipro Lab Telecom ACELP NET - 0x0131 Sipro Lab Telecom ACELP 4800 - 0x0132 Sipro Lab Telecom ACELP 8V3 - 0x0133 Sipro Lab Telecom G.729 - 0x0134 Sipro Lab Telecom G.729A - 0x0135 Sipro Lab Telecom Kelvin - 0x0140 Windows Media Video V8 - 0x0150 Qualcomm PureVoice - 0x0151 Qualcomm HalfRate - 0x0155 Ring Zero Systems TUB GSM - 0x0160 Microsoft Audio 1 - 0x0161 Windows Media Audio V7 / V8 / V9 - 0x0162 Windows Media Audio Professional V9 - 0x0163 Windows Media Audio Lossless V9 - 0x0200 Creative Labs ADPCM - 0x0202 Creative Labs Fastspeech8 - 0x0203 Creative Labs Fastspeech10 - 0x0210 UHER Informatic GmbH ADPCM - 0x0220 Quarterdeck - 0x0230 I-link Worldwide VC - 0x0240 Aureal RAW Sport - 0x0250 Interactive Products HSX - 0x0251 Interactive Products RPELP - 0x0260 Consistent Software CS2 - 0x0270 Sony SCX - 0x0300 Fujitsu FM Towns Snd - 0x0400 BTV Digital - 0x0401 Intel Music Coder - 0x0450 QDesign Music - 0x0680 VME VMPCM - 0x0681 AT&T Labs TPC - 0x08AE ClearJump LiteWave - 0x1000 Olivetti GSM - 0x1001 Olivetti ADPCM - 0x1002 Olivetti CELP - 0x1003 Olivetti SBC - 0x1004 Olivetti OPR - 0x1100 Lernout & Hauspie Codec (0x1100) - 0x1101 Lernout & Hauspie CELP Codec (0x1101) - 0x1102 Lernout & Hauspie SBC Codec (0x1102) - 0x1103 Lernout & Hauspie SBC Codec (0x1103) - 0x1104 Lernout & Hauspie SBC Codec (0x1104) - 0x1400 Norris - 0x1401 AT&T ISIAudio - 0x1500 Soundspace Music Compression - 0x181C VoxWare RT24 Speech - 0x1FC4 NCT Soft ALF2CD (www.nctsoft.com) - 0x2000 Dolby AC3 - 0x2001 Dolby DTS - 0x2002 WAVE_FORMAT_14_4 - 0x2003 WAVE_FORMAT_28_8 - 0x2004 WAVE_FORMAT_COOK - 0x2005 WAVE_FORMAT_DNET - 0x674F Ogg Vorbis 1 - 0x6750 Ogg Vorbis 2 - 0x6751 Ogg Vorbis 3 - 0x676F Ogg Vorbis 1+ - 0x6770 Ogg Vorbis 2+ - 0x6771 Ogg Vorbis 3+ - 0x7A21 GSM-AMR (CBR, no SID) - 0x7A22 GSM-AMR (VBR, including SID) - 0xFFFE WAVE_FORMAT_EXTENSIBLE - 0xFFFF WAVE_FORMAT_DEVELOPMENT - - */ - - return getid3_lib::EmbeddedLookup('0x'.str_pad(strtoupper(dechex($wFormatTag)), 4, '0', STR_PAD_LEFT), $begin, __LINE__, __FILE__, 'riff-wFormatTag'); - - } - - - function RIFFfourccLookup($fourcc) { - - $begin = __LINE__; - - /** This is not a comment! - - swot http://developer.apple.com/qa/snd/snd07.html - ____ No Codec (____) - _BIT BI_BITFIELDS (Raw RGB) - _JPG JPEG compressed - _PNG PNG compressed W3C/ISO/IEC (RFC-2083) - _RAW Full Frames (Uncompressed) - _RGB Raw RGB Bitmap - _RL4 RLE 4bpp RGB - _RL8 RLE 8bpp RGB - 3IV1 3ivx MPEG-4 v1 - 3IV2 3ivx MPEG-4 v2 - 3IVX 3ivx MPEG-4 - AASC Autodesk Animator - ABYR Kensington ?ABYR? - AEMI Array Microsystems VideoONE MPEG1-I Capture - AFLC Autodesk Animator FLC - AFLI Autodesk Animator FLI - AMPG Array Microsystems VideoONE MPEG - ANIM Intel RDX (ANIM) - AP41 AngelPotion Definitive - ASV1 Asus Video v1 - ASV2 Asus Video v2 - ASVX Asus Video 2.0 (audio) - AUR2 AuraVision Aura 2 Codec - YUV 4:2:2 - AURA AuraVision Aura 1 Codec - YUV 4:1:1 - AVDJ Independent JPEG Group\'s codec (AVDJ) - AVRN Independent JPEG Group\'s codec (AVRN) - AYUV 4:4:4 YUV (AYUV) - AZPR Quicktime Apple Video (AZPR) - BGR Raw RGB32 - BLZ0 Blizzard DivX MPEG-4 - BTVC Conexant Composite Video - BINK RAD Game Tools Bink Video - BT20 Conexant Prosumer Video - BTCV Conexant Composite Video Codec - BW10 Data Translation Broadway MPEG Capture - CC12 Intel YUV12 - CDVC Canopus DV - CFCC Digital Processing Systems DPS Perception - CGDI Microsoft Office 97 Camcorder Video - CHAM Winnov Caviara Champagne - CJPG Creative WebCam JPEG - CLJR Cirrus Logic YUV 4:1:1 - CMYK Common Data Format in Printing (Colorgraph) - CPLA Weitek 4:2:0 YUV Planar - CRAM Microsoft Video 1 (CRAM) - cvid Radius Cinepak - CVID Radius Cinepak - CWLT Microsoft Color WLT DIB - CYUV Creative Labs YUV - CYUY ATI YUV - D261 H.261 - D263 H.263 - DIB Device Independent Bitmap - DIV1 FFmpeg OpenDivX - DIV2 Microsoft MPEG-4 v1/v2 - DIV3 DivX ;-) MPEG-4 v3.x Low-Motion - DIV4 DivX ;-) MPEG-4 v3.x Fast-Motion - DIV5 DivX MPEG-4 v5.x - DIV6 DivX ;-) (MS MPEG-4 v3.x) - DIVX DivX MPEG-4 v4 (OpenDivX / Project Mayo) - divx DivX MPEG-4 - DMB1 Matrox Rainbow Runner hardware MJPEG - DMB2 Paradigm MJPEG - DSVD ?DSVD? - DUCK Duck TrueMotion 1.0 - DPS0 DPS/Leitch Reality Motion JPEG - DPSC DPS/Leitch PAR Motion JPEG - DV25 Matrox DVCPRO codec - DV50 Matrox DVCPRO50 codec - DVC IEC 61834 and SMPTE 314M (DVC/DV Video) - DVCP IEC 61834 and SMPTE 314M (DVC/DV Video) - DVHD IEC Standard DV 1125 lines @ 30fps / 1250 lines @ 25fps - DVMA Darim Vision DVMPEG (dummy for MPEG compressor) (www.darvision.com) - DVSL IEC Standard DV compressed in SD (SDL) - DVAN ?DVAN? - DVE2 InSoft DVE-2 Videoconferencing - dvsd IEC 61834 and SMPTE 314M DVC/DV Video - DVSD IEC 61834 and SMPTE 314M DVC/DV Video - DVX1 Lucent DVX1000SP Video Decoder - DVX2 Lucent DVX2000S Video Decoder - DVX3 Lucent DVX3000S Video Decoder - DX50 DivX v5 - DXT1 Microsoft DirectX Compressed Texture (DXT1) - DXT2 Microsoft DirectX Compressed Texture (DXT2) - DXT3 Microsoft DirectX Compressed Texture (DXT3) - DXT4 Microsoft DirectX Compressed Texture (DXT4) - DXT5 Microsoft DirectX Compressed Texture (DXT5) - DXTC Microsoft DirectX Compressed Texture (DXTC) - DXTn Microsoft DirectX Compressed Texture (DXTn) - EM2V Etymonix MPEG-2 I-frame (www.etymonix.com) - EKQ0 Elsa ?EKQ0? - ELK0 Elsa ?ELK0? - ESCP Eidos Escape - ETV1 eTreppid Video ETV1 - ETV2 eTreppid Video ETV2 - ETVC eTreppid Video ETVC - FLIC Autodesk FLI/FLC Animation - FRWT Darim Vision Forward Motion JPEG (www.darvision.com) - FRWU Darim Vision Forward Uncompressed (www.darvision.com) - FLJP D-Vision Field Encoded Motion JPEG - FRWA SoftLab-Nsk Forward Motion JPEG w/ alpha channel - FRWD SoftLab-Nsk Forward Motion JPEG - FVF1 Iterated Systems Fractal Video Frame - GLZW Motion LZW (gabest@freemail.hu) - GPEG Motion JPEG (gabest@freemail.hu) - GWLT Microsoft Greyscale WLT DIB - H260 Intel ITU H.260 Videoconferencing - H261 Intel ITU H.261 Videoconferencing - H262 Intel ITU H.262 Videoconferencing - H263 Intel ITU H.263 Videoconferencing - H264 Intel ITU H.264 Videoconferencing - H265 Intel ITU H.265 Videoconferencing - H266 Intel ITU H.266 Videoconferencing - H267 Intel ITU H.267 Videoconferencing - H268 Intel ITU H.268 Videoconferencing - H269 Intel ITU H.269 Videoconferencing - HFYU Huffman Lossless Codec - HMCR Rendition Motion Compensation Format (HMCR) - HMRR Rendition Motion Compensation Format (HMRR) - I263 FFmpeg I263 decoder - IF09 Indeo YVU9 ("YVU9 with additional delta-frame info after the U plane") - IUYV Interlaced version of UYVY (www.leadtools.com) - IY41 Interlaced version of Y41P (www.leadtools.com) - IYU1 12 bit format used in mode 2 of the IEEE 1394 Digital Camera 1.04 spec IEEE standard - IYU2 24 bit format used in mode 2 of the IEEE 1394 Digital Camera 1.04 spec IEEE standard - IYUV Planar YUV format (8-bpp Y plane, followed by 8-bpp 2×2 U and V planes) - i263 Intel ITU H.263 Videoconferencing (i263) - I420 Intel Indeo 4 - IAN Intel Indeo 4 (RDX) - ICLB InSoft CellB Videoconferencing - IGOR Power DVD - IJPG Intergraph JPEG - ILVC Intel Layered Video - ILVR ITU-T H.263+ - IPDV I-O Data Device Giga AVI DV Codec - IR21 Intel Indeo 2.1 - IRAW Intel YUV Uncompressed - IV30 Intel Indeo 3.0 - IV31 Intel Indeo 3.1 - IV32 Ligos Indeo 3.2 - IV33 Ligos Indeo 3.3 - IV34 Ligos Indeo 3.4 - IV35 Ligos Indeo 3.5 - IV36 Ligos Indeo 3.6 - IV37 Ligos Indeo 3.7 - IV38 Ligos Indeo 3.8 - IV39 Ligos Indeo 3.9 - IV40 Ligos Indeo Interactive 4.0 - IV41 Ligos Indeo Interactive 4.1 - IV42 Ligos Indeo Interactive 4.2 - IV43 Ligos Indeo Interactive 4.3 - IV44 Ligos Indeo Interactive 4.4 - IV45 Ligos Indeo Interactive 4.5 - IV46 Ligos Indeo Interactive 4.6 - IV47 Ligos Indeo Interactive 4.7 - IV48 Ligos Indeo Interactive 4.8 - IV49 Ligos Indeo Interactive 4.9 - IV50 Ligos Indeo Interactive 5.0 - JBYR Kensington ?JBYR? - JPEG Still Image JPEG DIB - JPGL Pegasus Lossless Motion JPEG - KMVC Team17 Software Karl Morton\'s Video Codec - LSVM Vianet Lighting Strike Vmail (Streaming) (www.vianet.com) - LEAD LEAD Video Codec - Ljpg LEAD MJPEG Codec - MDVD Alex MicroDVD Video (hacked MS MPEG-4) (www.tiasoft.de) - MJPA Morgan Motion JPEG (MJPA) (www.morgan-multimedia.com) - MJPB Morgan Motion JPEG (MJPB) (www.morgan-multimedia.com) - MMES Matrox MPEG-2 I-frame - MP2v Microsoft S-Mpeg 4 version 1 (MP2v) - MP42 Microsoft S-Mpeg 4 version 2 (MP42) - MP43 Microsoft S-Mpeg 4 version 3 (MP43) - MP4S Microsoft S-Mpeg 4 version 3 (MP4S) - MP4V FFmpeg MPEG-4 - MPG1 FFmpeg MPEG 1/2 - MPG2 FFmpeg MPEG 1/2 - MPG3 FFmpeg DivX ;-) (MS MPEG-4 v3) - MPG4 Microsoft MPEG-4 - MPGI Sigma Designs MPEG - MPNG PNG images decoder - MSS1 Microsoft Windows Screen Video - MSZH LCL (Lossless Codec Library) (www.geocities.co.jp/Playtown-Denei/2837/LRC.htm) - M261 Microsoft H.261 - M263 Microsoft H.263 - M4S2 Microsoft Fully Compliant MPEG-4 v2 simple profile (M4S2) - m4s2 Microsoft Fully Compliant MPEG-4 v2 simple profile (m4s2) - MC12 ATI Motion Compensation Format (MC12) - MCAM ATI Motion Compensation Format (MCAM) - MJ2C Morgan Multimedia Motion JPEG2000 - mJPG IBM Motion JPEG w/ Huffman Tables - MJPG Microsoft Motion JPEG DIB - MP42 Microsoft MPEG-4 (low-motion) - MP43 Microsoft MPEG-4 (fast-motion) - MP4S Microsoft MPEG-4 (MP4S) - mp4s Microsoft MPEG-4 (mp4s) - MPEG Chromatic Research MPEG-1 Video I-Frame - MPG4 Microsoft MPEG-4 Video High Speed Compressor - MPGI Sigma Designs MPEG - MRCA FAST Multimedia Martin Regen Codec - MRLE Microsoft Run Length Encoding - MSVC Microsoft Video 1 - MTX1 Matrox ?MTX1? - MTX2 Matrox ?MTX2? - MTX3 Matrox ?MTX3? - MTX4 Matrox ?MTX4? - MTX5 Matrox ?MTX5? - MTX6 Matrox ?MTX6? - MTX7 Matrox ?MTX7? - MTX8 Matrox ?MTX8? - MTX9 Matrox ?MTX9? - MV12 Motion Pixels Codec (old) - MWV1 Aware Motion Wavelets - nAVI SMR Codec (hack of Microsoft MPEG-4) (IRC #shadowrealm) - NT00 NewTek LightWave HDTV YUV w/ Alpha (www.newtek.com) - NUV1 NuppelVideo - NTN1 Nogatech Video Compression 1 - NVS0 nVidia GeForce Texture (NVS0) - NVS1 nVidia GeForce Texture (NVS1) - NVS2 nVidia GeForce Texture (NVS2) - NVS3 nVidia GeForce Texture (NVS3) - NVS4 nVidia GeForce Texture (NVS4) - NVS5 nVidia GeForce Texture (NVS5) - NVT0 nVidia GeForce Texture (NVT0) - NVT1 nVidia GeForce Texture (NVT1) - NVT2 nVidia GeForce Texture (NVT2) - NVT3 nVidia GeForce Texture (NVT3) - NVT4 nVidia GeForce Texture (NVT4) - NVT5 nVidia GeForce Texture (NVT5) - PIXL MiroXL, Pinnacle PCTV - PDVC I-O Data Device Digital Video Capture DV codec - PGVV Radius Video Vision - PHMO IBM Photomotion - PIM1 MPEG Realtime (Pinnacle Cards) - PIM2 Pegasus Imaging ?PIM2? - PIMJ Pegasus Imaging Lossless JPEG - PVEZ Horizons Technology PowerEZ - PVMM PacketVideo Corporation MPEG-4 - PVW2 Pegasus Imaging Wavelet Compression - Q1.0 Q-Team\'s QPEG 1.0 (www.q-team.de) - Q1.1 Q-Team\'s QPEG 1.1 (www.q-team.de) - QPEG Q-Team QPEG 1.0 - qpeq Q-Team QPEG 1.1 - RGB Raw BGR32 - RGBA Raw RGB w/ Alpha - RMP4 REALmagic MPEG-4 (unauthorized XVID copy) (www.sigmadesigns.com) - ROQV Id RoQ File Video Decoder - RPZA Quicktime Apple Video (RPZA) - RUD0 Rududu video codec (http://rududu.ifrance.com/rududu/) - RV10 RealVideo 1.0 (aka RealVideo 5.0) - RV13 RealVideo 1.0 (RV13) - RV20 RealVideo G2 - RV30 RealVideo 8 - RV40 RealVideo 9 - RGBT Raw RGB w/ Transparency - RLE Microsoft Run Length Encoder - RLE4 Run Length Encoded (4bpp, 16-color) - RLE8 Run Length Encoded (8bpp, 256-color) - RT21 Intel Indeo RealTime Video 2.1 - rv20 RealVideo G2 - rv30 RealVideo 8 - RVX Intel RDX (RVX ) - SMC Apple Graphics (SMC ) - SP54 Logitech Sunplus Sp54 Codec for Mustek GSmart Mini 2 - SPIG Radius Spigot - SVQ3 Sorenson Video 3 (Apple Quicktime 5) - s422 Tekram VideoCap C210 YUV 4:2:2 - SDCC Sun Communication Digital Camera Codec - SFMC CrystalNet Surface Fitting Method - SMSC Radius SMSC - SMSD Radius SMSD - smsv WorldConnect Wavelet Video - SPIG Radius Spigot - SPLC Splash Studios ACM Audio Codec (www.splashstudios.net) - SQZ2 Microsoft VXTreme Video Codec V2 - STVA ST Microelectronics CMOS Imager Data (Bayer) - STVB ST Microelectronics CMOS Imager Data (Nudged Bayer) - STVC ST Microelectronics CMOS Imager Data (Bunched) - STVX ST Microelectronics CMOS Imager Data (Extended CODEC Data Format) - STVY ST Microelectronics CMOS Imager Data (Extended CODEC Data Format with Correction Data) - SV10 Sorenson Video R1 - SVQ1 Sorenson Video - T420 Toshiba YUV 4:2:0 - TM2A Duck TrueMotion Archiver 2.0 (www.duck.com) - TVJP Pinnacle/Truevision Targa 2000 board (TVJP) - TVMJ Pinnacle/Truevision Targa 2000 board (TVMJ) - TY0N Tecomac Low-Bit Rate Codec (www.tecomac.com) - TY2C Trident Decompression Driver - TLMS TeraLogic Motion Intraframe Codec (TLMS) - TLST TeraLogic Motion Intraframe Codec (TLST) - TM20 Duck TrueMotion 2.0 - TM2X Duck TrueMotion 2X - TMIC TeraLogic Motion Intraframe Codec (TMIC) - TMOT Horizons Technology TrueMotion S - tmot Horizons TrueMotion Video Compression - TR20 Duck TrueMotion RealTime 2.0 - TSCC TechSmith Screen Capture Codec - TV10 Tecomac Low-Bit Rate Codec - TY2N Trident ?TY2N? - U263 UB Video H.263/H.263+/H.263++ Decoder - UMP4 UB Video MPEG 4 (www.ubvideo.com) - UYNV Nvidia UYVY packed 4:2:2 - UYVP Evans & Sutherland YCbCr 4:2:2 extended precision - UCOD eMajix.com ClearVideo - ULTI IBM Ultimotion - UYVY UYVY packed 4:2:2 - V261 Lucent VX2000S - VIFP VFAPI Reader Codec (www.yks.ne.jp/~hori/) - VIV1 FFmpeg H263+ decoder - VIV2 Vivo H.263 - VQC2 Vector-quantised codec 2 (research) http://eprints.ecs.soton.ac.uk/archive/00001310/01/VTC97-js.pdf) - VTLP Alaris VideoGramPiX - VYU9 ATI YUV (VYU9) - VYUY ATI YUV (VYUY) - V261 Lucent VX2000S - V422 Vitec Multimedia 24-bit YUV 4:2:2 Format - V655 Vitec Multimedia 16-bit YUV 4:2:2 Format - VCR1 ATI Video Codec 1 - VCR2 ATI Video Codec 2 - VCR3 ATI VCR 3.0 - VCR4 ATI VCR 4.0 - VCR5 ATI VCR 5.0 - VCR6 ATI VCR 6.0 - VCR7 ATI VCR 7.0 - VCR8 ATI VCR 8.0 - VCR9 ATI VCR 9.0 - VDCT Vitec Multimedia Video Maker Pro DIB - VDOM VDOnet VDOWave - VDOW VDOnet VDOLive (H.263) - VDTZ Darim Vison VideoTizer YUV - VGPX Alaris VideoGramPiX - VIDS Vitec Multimedia YUV 4:2:2 CCIR 601 for V422 - VIVO Vivo H.263 v2.00 - vivo Vivo H.263 - VIXL Miro/Pinnacle Video XL - VLV1 VideoLogic/PURE Digital Videologic Capture - VP30 On2 VP3.0 - VP31 On2 VP3.1 - VX1K Lucent VX1000S Video Codec - VX2K Lucent VX2000S Video Codec - VXSP Lucent VX1000SP Video Codec - WBVC Winbond W9960 - WHAM Microsoft Video 1 (WHAM) - WINX Winnov Software Compression - WJPG AverMedia Winbond JPEG - WMV1 Windows Media Video V7 - WMV2 Windows Media Video V8 - WMV3 Windows Media Video V9 - WNV1 Winnov Hardware Compression - XYZP Extended PAL format XYZ palette (www.riff.org) - x263 Xirlink H.263 - XLV0 NetXL Video Decoder - XMPG Xing MPEG (I-Frame only) - XVID XviD MPEG-4 (www.xvid.org) - XXAN ?XXAN? - YU92 Intel YUV (YU92) - YUNV Nvidia Uncompressed YUV 4:2:2 - YUVP Extended PAL format YUV palette (www.riff.org) - Y211 YUV 2:1:1 Packed - Y411 YUV 4:1:1 Packed - Y41B Weitek YUV 4:1:1 Planar - Y41P Brooktree PC1 YUV 4:1:1 Packed - Y41T Brooktree PC1 YUV 4:1:1 with transparency - Y42B Weitek YUV 4:2:2 Planar - Y42T Brooktree UYUV 4:2:2 with transparency - Y422 ADS Technologies Copy of UYVY used in Pyro WebCam firewire camera - Y800 Simple, single Y plane for monochrome images - Y8 Grayscale video - YC12 Intel YUV 12 codec - YUV8 Winnov Caviar YUV8 - YUV9 Intel YUV9 - YUY2 Uncompressed YUV 4:2:2 - YUYV Canopus YUV - YV12 YVU12 Planar - YVU9 Intel YVU9 Planar (8-bpp Y plane, followed by 8-bpp 4x4 U and V planes) - YVYU YVYU 4:2:2 Packed - ZLIB Lossless Codec Library zlib compression (www.geocities.co.jp/Playtown-Denei/2837/LRC.htm) - ZPEG Metheus Video Zipper - - */ - - return getid3_lib::EmbeddedLookup($fourcc, $begin, __LINE__, __FILE__, 'riff-fourcc'); - } - - - function EitherEndian2Int(&$ThisFileInfo, $byteword, $signed=false) { - if ($ThisFileInfo['fileformat'] == 'riff') { - return getid3_lib::LittleEndian2Int($byteword, $signed); - } - return getid3_lib::BigEndian2Int($byteword, false, $signed); - } - -} - -?> \ No newline at end of file diff --git a/getid3/getid3/module.audio-video.swf.php b/getid3/getid3/module.audio-video.swf.php deleted file mode 100644 index a03806e..0000000 --- a/getid3/getid3/module.audio-video.swf.php +++ /dev/null @@ -1,153 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.audio-video.swf.php // -// module for analyzing Shockwave Flash files // -// dependencies: NONE // -// /// -///////////////////////////////////////////////////////////////// - - -class getid3_swf -{ - - function getid3_swf(&$fd, &$ThisFileInfo, $ReturnAllTagData=false) { - $ThisFileInfo['fileformat'] = 'swf'; - $ThisFileInfo['video']['dataformat'] = 'swf'; - - // http://www.openswf.org/spec/SWFfileformat.html - - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - -//echo 'reading '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']).' bytes
'; - $SWFfileData = fread($fd, $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']); // 8 + 2 + 2 + max(9) bytes NOT including Frame_Size RECT data - - $ThisFileInfo['swf']['header']['signature'] = substr($SWFfileData, 0, 3); - switch ($ThisFileInfo['swf']['header']['signature']) { - case 'FWS': - $ThisFileInfo['swf']['header']['compressed'] = false; - break; - - case 'CWS': - $ThisFileInfo['swf']['header']['compressed'] = true; - break; - - default: - $ThisFileInfo['error'][] = 'Expecting "FWS" or "CWS" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$ThisFileInfo['swf']['header']['signature'].'"'; - unset($ThisFileInfo['swf']); - unset($ThisFileInfo['fileformat']); - return false; - break; - } - $ThisFileInfo['swf']['header']['version'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 3, 1)); - $ThisFileInfo['swf']['header']['length'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 4, 4)); - -//echo '1
'; - if ($ThisFileInfo['swf']['header']['compressed']) { - -//echo '2
'; -// $foo = substr($SWFfileData, 8, 4096); -// echo '['.strlen($foo).']
'; -// $fee = gzuncompress($foo); -// echo '('.strlen($fee).')
'; -//return false; -//echo '
time: '.time().'
'; -//return false; - if ($UncompressedFileData = gzuncompress(substr($SWFfileData, 8))) { - -//echo '3
'; - $SWFfileData = substr($SWFfileData, 0, 8).$UncompressedFileData; - - } else { - -//echo '4
'; - $ThisFileInfo['error'][] = 'Error decompressing compressed SWF data'; - return false; - - } - - } - - $FrameSizeBitsPerValue = (ord(substr($SWFfileData, 8, 1)) & 0xF8) >> 3; - $FrameSizeDataLength = ceil((5 + (4 * $FrameSizeBitsPerValue)) / 8); - $FrameSizeDataString = str_pad(decbin(ord(substr($SWFfileData, 8, 1)) & 0x07), 3, '0', STR_PAD_LEFT); - for ($i = 1; $i < $FrameSizeDataLength; $i++) { - $FrameSizeDataString .= str_pad(decbin(ord(substr($SWFfileData, 8 + $i, 1))), 8, '0', STR_PAD_LEFT); - } - list($X1, $X2, $Y1, $Y2) = explode("\n", wordwrap($FrameSizeDataString, $FrameSizeBitsPerValue, "\n", 1)); - $ThisFileInfo['swf']['header']['frame_width'] = getid3_lib::Bin2Dec($X2); - $ThisFileInfo['swf']['header']['frame_height'] = getid3_lib::Bin2Dec($Y2); - - // http://www-lehre.informatik.uni-osnabrueck.de/~fbstark/diplom/docs/swf/Flash_Uncovered.htm - // Next in the header is the frame rate, which is kind of weird. - // It is supposed to be stored as a 16bit integer, but the first byte - // (or last depending on how you look at it) is completely ignored. - // Example: 0x000C -> 0x0C -> 12 So the frame rate is 12 fps. - - // Byte at (8 + $FrameSizeDataLength) is always zero and ignored - $ThisFileInfo['swf']['header']['frame_rate'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 9 + $FrameSizeDataLength, 1)); - $ThisFileInfo['swf']['header']['frame_count'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 10 + $FrameSizeDataLength, 2)); - - $ThisFileInfo['video']['frame_rate'] = $ThisFileInfo['swf']['header']['frame_rate']; - $ThisFileInfo['video']['resolution_x'] = intval(round($ThisFileInfo['swf']['header']['frame_width'] / 20)); - $ThisFileInfo['video']['resolution_y'] = intval(round($ThisFileInfo['swf']['header']['frame_height'] / 20)); - $ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1; - - if (($ThisFileInfo['swf']['header']['frame_count'] > 0) && ($ThisFileInfo['swf']['header']['frame_rate'] > 0)) { - $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['swf']['header']['frame_count'] / $ThisFileInfo['swf']['header']['frame_rate']; - } - - - // SWF tags - - $CurrentOffset = 12 + $FrameSizeDataLength; - $SWFdataLength = strlen($SWFfileData); - - while ($CurrentOffset < $SWFdataLength) { - - $TagIDTagLength = getid3_lib::LittleEndian2Int(substr($SWFfileData, $CurrentOffset, 2)); - $TagID = ($TagIDTagLength & 0xFFFC) >> 6; - $TagLength = ($TagIDTagLength & 0x003F); - $CurrentOffset += 2; - if ($TagLength == 0x3F) { - $TagLength = getid3_lib::LittleEndian2Int(substr($SWFfileData, $CurrentOffset, 4)); - $CurrentOffset += 4; - } - - unset($TagData); - $TagData['offset'] = $CurrentOffset; - $TagData['size'] = $TagLength; - $TagData['id'] = $TagID; - $TagData['data'] = substr($SWFfileData, $CurrentOffset, $TagLength); - switch ($TagID) { - case 0: // end of movie - break 2; - - case 9: // Set background color - //$ThisFileInfo['swf']['tags'][] = $TagData; - $ThisFileInfo['swf']['bgcolor'] = strtoupper(str_pad(dechex(getid3_lib::BigEndian2Int($TagData['data'])), 6, '0', STR_PAD_LEFT)); - break; - - default: - if ($ReturnAllTagData) { - $ThisFileInfo['swf']['tags'][] = $TagData; - } - break; - } - - $CurrentOffset += $TagLength; - } - - return true; - } - -} - - -?> \ No newline at end of file diff --git a/getid3/getid3/module.audio.aac.php b/getid3/getid3/module.audio.aac.php deleted file mode 100644 index 4d35fb2..0000000 --- a/getid3/getid3/module.audio.aac.php +++ /dev/null @@ -1,538 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.audio.aac.php // -// module for analyzing AAC Audio files // -// dependencies: NONE // -// /// -///////////////////////////////////////////////////////////////// - - -class getid3_aac -{ - - // new combined constructor - function getid3_aac(&$fd, &$ThisFileInfo, $option) { - - if ($option === 'adif') { - $this->getAACADIFheaderFilepointer($fd, $ThisFileInfo); - } - elseif ($option === 'adts') { - $this->getAACADTSheaderFilepointer($fd, $ThisFileInfo); - } - } - - - - function getAACADIFheaderFilepointer(&$fd, &$ThisFileInfo) { - $ThisFileInfo['fileformat'] = 'aac'; - $ThisFileInfo['audio']['dataformat'] = 'aac'; - $ThisFileInfo['audio']['lossless'] = false; - - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $AACheader = fread($fd, 1024); - $offset = 0; - - if (substr($AACheader, 0, 4) == 'ADIF') { - - // http://faac.sourceforge.net/wiki/index.php?page=ADIF - - // http://libmpeg.org/mpeg4/doc/w2203tfs.pdf - // adif_header() { - // adif_id 32 - // copyright_id_present 1 - // if( copyright_id_present ) - // copyright_id 72 - // original_copy 1 - // home 1 - // bitstream_type 1 - // bitrate 23 - // num_program_config_elements 4 - // for (i = 0; i < num_program_config_elements + 1; i++ ) { - // if( bitstream_type == '0' ) - // adif_buffer_fullness 20 - // program_config_element() - // } - // } - - $AACheaderBitstream = getid3_lib::BigEndian2Bin($AACheader); - $bitoffset = 0; - - $ThisFileInfo['aac']['header_type'] = 'ADIF'; - $bitoffset += 32; - $ThisFileInfo['aac']['header']['mpeg_version'] = 4; - - $ThisFileInfo['aac']['header']['copyright'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1'); - $bitoffset += 1; - if ($ThisFileInfo['aac']['header']['copyright']) { - $ThisFileInfo['aac']['header']['copyright_id'] = getid3_lib::Bin2String(substr($AACheaderBitstream, $bitoffset, 72)); - $bitoffset += 72; - } - $ThisFileInfo['aac']['header']['original_copy'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1'); - $bitoffset += 1; - $ThisFileInfo['aac']['header']['home'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1'); - $bitoffset += 1; - $ThisFileInfo['aac']['header']['is_vbr'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1'); - $bitoffset += 1; - if ($ThisFileInfo['aac']['header']['is_vbr']) { - $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; - $ThisFileInfo['aac']['header']['bitrate_max'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 23)); - $bitoffset += 23; - } else { - $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; - $ThisFileInfo['aac']['header']['bitrate'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 23)); - $bitoffset += 23; - $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['aac']['header']['bitrate']; - } - if ($ThisFileInfo['audio']['bitrate'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt AAC file: bitrate_audio == zero'; - return false; - } - $ThisFileInfo['aac']['header']['num_program_configs'] = 1 + getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); - $bitoffset += 4; - - for ($i = 0; $i < $ThisFileInfo['aac']['header']['num_program_configs']; $i++) { - // http://www.audiocoding.com/wiki/index.php?page=program_config_element - - // buffer_fullness 20 - - // element_instance_tag 4 - // object_type 2 - // sampling_frequency_index 4 - // num_front_channel_elements 4 - // num_side_channel_elements 4 - // num_back_channel_elements 4 - // num_lfe_channel_elements 2 - // num_assoc_data_elements 3 - // num_valid_cc_elements 4 - // mono_mixdown_present 1 - // mono_mixdown_element_number 4 if mono_mixdown_present == 1 - // stereo_mixdown_present 1 - // stereo_mixdown_element_number 4 if stereo_mixdown_present == 1 - // matrix_mixdown_idx_present 1 - // matrix_mixdown_idx 2 if matrix_mixdown_idx_present == 1 - // pseudo_surround_enable 1 if matrix_mixdown_idx_present == 1 - // for (i = 0; i < num_front_channel_elements; i++) { - // front_element_is_cpe[i] 1 - // front_element_tag_select[i] 4 - // } - // for (i = 0; i < num_side_channel_elements; i++) { - // side_element_is_cpe[i] 1 - // side_element_tag_select[i] 4 - // } - // for (i = 0; i < num_back_channel_elements; i++) { - // back_element_is_cpe[i] 1 - // back_element_tag_select[i] 4 - // } - // for (i = 0; i < num_lfe_channel_elements; i++) { - // lfe_element_tag_select[i] 4 - // } - // for (i = 0; i < num_assoc_data_elements; i++) { - // assoc_data_element_tag_select[i] 4 - // } - // for (i = 0; i < num_valid_cc_elements; i++) { - // cc_element_is_ind_sw[i] 1 - // valid_cc_element_tag_select[i] 4 - // } - // byte_alignment() VAR - // comment_field_bytes 8 - // for (i = 0; i < comment_field_bytes; i++) { - // comment_field_data[i] 8 - // } - - if (!$ThisFileInfo['aac']['header']['is_vbr']) { - $ThisFileInfo['aac']['program_configs'][$i]['buffer_fullness'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 20)); - $bitoffset += 20; - } - $ThisFileInfo['aac']['program_configs'][$i]['element_instance_tag'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); - $bitoffset += 4; - $ThisFileInfo['aac']['program_configs'][$i]['object_type'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2)); - $bitoffset += 2; - $ThisFileInfo['aac']['program_configs'][$i]['sampling_frequency_index'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); - $bitoffset += 4; - $ThisFileInfo['aac']['program_configs'][$i]['num_front_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); - $bitoffset += 4; - $ThisFileInfo['aac']['program_configs'][$i]['num_side_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); - $bitoffset += 4; - $ThisFileInfo['aac']['program_configs'][$i]['num_back_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); - $bitoffset += 4; - $ThisFileInfo['aac']['program_configs'][$i]['num_lfe_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2)); - $bitoffset += 2; - $ThisFileInfo['aac']['program_configs'][$i]['num_assoc_data_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 3)); - $bitoffset += 3; - $ThisFileInfo['aac']['program_configs'][$i]['num_valid_cc_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); - $bitoffset += 4; - $ThisFileInfo['aac']['program_configs'][$i]['mono_mixdown_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); - $bitoffset += 1; - if ($ThisFileInfo['aac']['program_configs'][$i]['mono_mixdown_present']) { - $ThisFileInfo['aac']['program_configs'][$i]['mono_mixdown_element_number'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); - $bitoffset += 4; - } - $ThisFileInfo['aac']['program_configs'][$i]['stereo_mixdown_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); - $bitoffset += 1; - if ($ThisFileInfo['aac']['program_configs'][$i]['stereo_mixdown_present']) { - $ThisFileInfo['aac']['program_configs'][$i]['stereo_mixdown_element_number'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); - $bitoffset += 4; - } - $ThisFileInfo['aac']['program_configs'][$i]['matrix_mixdown_idx_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); - $bitoffset += 1; - if ($ThisFileInfo['aac']['program_configs'][$i]['matrix_mixdown_idx_present']) { - $ThisFileInfo['aac']['program_configs'][$i]['matrix_mixdown_idx'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2)); - $bitoffset += 2; - $ThisFileInfo['aac']['program_configs'][$i]['pseudo_surround_enable'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); - $bitoffset += 1; - } - for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_front_channel_elements']; $j++) { - $ThisFileInfo['aac']['program_configs'][$i]['front_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); - $bitoffset += 1; - $ThisFileInfo['aac']['program_configs'][$i]['front_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); - $bitoffset += 4; - } - for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_side_channel_elements']; $j++) { - $ThisFileInfo['aac']['program_configs'][$i]['side_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); - $bitoffset += 1; - $ThisFileInfo['aac']['program_configs'][$i]['side_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); - $bitoffset += 4; - } - for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_back_channel_elements']; $j++) { - $ThisFileInfo['aac']['program_configs'][$i]['back_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); - $bitoffset += 1; - $ThisFileInfo['aac']['program_configs'][$i]['back_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); - $bitoffset += 4; - } - for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_lfe_channel_elements']; $j++) { - $ThisFileInfo['aac']['program_configs'][$i]['lfe_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); - $bitoffset += 4; - } - for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_assoc_data_elements']; $j++) { - $ThisFileInfo['aac']['program_configs'][$i]['assoc_data_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); - $bitoffset += 4; - } - for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_valid_cc_elements']; $j++) { - $ThisFileInfo['aac']['program_configs'][$i]['cc_element_is_ind_sw'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); - $bitoffset += 1; - $ThisFileInfo['aac']['program_configs'][$i]['valid_cc_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); - $bitoffset += 4; - } - - $bitoffset = ceil($bitoffset / 8) * 8; - - $ThisFileInfo['aac']['program_configs'][$i]['comment_field_bytes'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 8)); - $bitoffset += 8; - $ThisFileInfo['aac']['program_configs'][$i]['comment_field'] = getid3_lib::Bin2String(substr($AACheaderBitstream, $bitoffset, 8 * $ThisFileInfo['aac']['program_configs'][$i]['comment_field_bytes'])); - $bitoffset += 8 * $ThisFileInfo['aac']['program_configs'][$i]['comment_field_bytes']; - - - $ThisFileInfo['aac']['header']['profile_text'] = $this->AACprofileLookup($ThisFileInfo['aac']['program_configs'][$i]['object_type'], $ThisFileInfo['aac']['header']['mpeg_version']); - $ThisFileInfo['aac']['program_configs'][$i]['sampling_frequency'] = $this->AACsampleRateLookup($ThisFileInfo['aac']['program_configs'][$i]['sampling_frequency_index']); - $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['aac']['program_configs'][$i]['sampling_frequency']; - $ThisFileInfo['audio']['channels'] = $this->AACchannelCountCalculate($ThisFileInfo['aac']['program_configs'][$i]); - if ($ThisFileInfo['aac']['program_configs'][$i]['comment_field']) { - $ThisFileInfo['aac']['comments'][] = $ThisFileInfo['aac']['program_configs'][$i]['comment_field']; - } - } - $ThisFileInfo['playtime_seconds'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['audio']['bitrate']; - - $ThisFileInfo['audio']['encoder_options'] = $ThisFileInfo['aac']['header_type'].' '.$ThisFileInfo['aac']['header']['profile_text']; - - - - return true; - - } else { - - unset($ThisFileInfo['fileformat']); - unset($ThisFileInfo['aac']); - $ThisFileInfo['error'][] = 'AAC-ADIF synch not found at offset '.$ThisFileInfo['avdataoffset'].' (expected "ADIF", found "'.substr($AACheader, 0, 4).'" instead)'; - return false; - - } - - } - - - function getAACADTSheaderFilepointer(&$fd, &$ThisFileInfo, $MaxFramesToScan=1000000, $ReturnExtendedInfo=false) { - // based loosely on code from AACfile by Jurgen Faul - // http://jfaul.de/atl or http://j-faul.virtualave.net/atl/atl.html - - - // http://faac.sourceforge.net/wiki/index.php?page=ADTS - - // * ADTS Fixed Header: these don't change from frame to frame - // syncword 12 always: '111111111111' - // ID 1 0: MPEG-4, 1: MPEG-2 - // layer 2 always: '00' - // protection_absent 1 - // profile 2 - // sampling_frequency_index 4 - // private_bit 1 - // channel_configuration 3 - // original/copy 1 - // home 1 - // emphasis 2 only if ID == 0 (ie MPEG-4) - - // * ADTS Variable Header: these can change from frame to frame - // copyright_identification_bit 1 - // copyright_identification_start 1 - // aac_frame_length 13 length of the frame including header (in bytes) - // adts_buffer_fullness 11 0x7FF indicates VBR - // no_raw_data_blocks_in_frame 2 - - // * ADTS Error check - // crc_check 16 only if protection_absent == 0 - - $byteoffset = 0; - $framenumber = 0; - - // Init bit pattern array - static $decbin = array(); - - // Populate $bindec - for ($i = 0; $i < 256; $i++) { - $decbin[chr($i)] = str_pad(decbin($i), 8, '0', STR_PAD_LEFT); - } - - // used to calculate bitrate below - $BitrateCache = array(); - - - while (true) { - // breaks out when end-of-file encountered, or invalid data found, - // or MaxFramesToScan frames have been scanned - - fseek($fd, $byteoffset, SEEK_SET); - - // First get substring - $substring = fread($fd, 10); - $substringlength = strlen($substring); - if ($substringlength != 10) { - $ThisFileInfo['error'][] = 'Failed to read 10 bytes at offset '.(ftell($fd) - $substringlength).' (only read '.$substringlength.' bytes)'; - return false; - } - - // Initialise $AACheaderBitstream - $AACheaderBitstream = ''; - - // Loop thru substring chars - for ($i = 0; $i < 10; $i++) { - $AACheaderBitstream .= $decbin[$substring{$i}]; - } - - $bitoffset = 0; - - $synctest = bindec(substr($AACheaderBitstream, $bitoffset, 12)); - - $bitoffset += 12; - if ($synctest != 0x0FFF) { - $ThisFileInfo['error'][] = 'Synch pattern (0x0FFF) not found at offset '.(ftell($fd) - 10).' (found 0x0'.strtoupper(dechex($synctest)).' instead)'; - if ($ThisFileInfo['fileformat'] == 'aac') { - return true; - } - return false; - } - - // Gather info for first frame only - this takes time to do 1000 times! - if ($framenumber > 0) { - - if (!$AACheaderBitstream[$bitoffset]) { - - // MPEG-4 - $bitoffset += 20; - - } else { - - // MPEG-2 - $bitoffset += 18; - - } - - } else { - - $ThisFileInfo['aac']['header_type'] = 'ADTS'; - $ThisFileInfo['aac']['header']['synch'] = $synctest; - $ThisFileInfo['fileformat'] = 'aac'; - $ThisFileInfo['audio']['dataformat'] = 'aac'; - - $ThisFileInfo['aac']['header']['mpeg_version'] = ((substr($AACheaderBitstream, $bitoffset, 1) == '0') ? 4 : 2); - $bitoffset += 1; - $ThisFileInfo['aac']['header']['layer'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2)); - $bitoffset += 2; - if ($ThisFileInfo['aac']['header']['layer'] != 0) { - $ThisFileInfo['error'][] = 'Layer error - expected 0x00, found 0x'.dechex($ThisFileInfo['aac']['header']['layer']).' instead'; - return false; - } - $ThisFileInfo['aac']['header']['crc_present'] = ((substr($AACheaderBitstream, $bitoffset, 1) == '0') ? true : false); - $bitoffset += 1; - $ThisFileInfo['aac']['header']['profile_id'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2)); - $bitoffset += 2; - $ThisFileInfo['aac']['header']['profile_text'] = $this->AACprofileLookup($ThisFileInfo['aac']['header']['profile_id'], $ThisFileInfo['aac']['header']['mpeg_version']); - - $ThisFileInfo['aac']['header']['sample_frequency_index'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); - $bitoffset += 4; - $ThisFileInfo['aac']['header']['sample_frequency'] = $this->AACsampleRateLookup($ThisFileInfo['aac']['header']['sample_frequency_index']); - if ($ThisFileInfo['aac']['header']['sample_frequency'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt AAC file: sample_frequency == zero'; - return false; - } - $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['aac']['header']['sample_frequency']; - - $ThisFileInfo['aac']['header']['private'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); - $bitoffset += 1; - $ThisFileInfo['aac']['header']['channel_configuration'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 3)); - $bitoffset += 3; - $ThisFileInfo['audio']['channels'] = $ThisFileInfo['aac']['header']['channel_configuration']; - $ThisFileInfo['aac']['header']['original'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); - $bitoffset += 1; - $ThisFileInfo['aac']['header']['home'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); - $bitoffset += 1; - - if ($ThisFileInfo['aac']['header']['mpeg_version'] == 4) { - $ThisFileInfo['aac']['header']['emphasis'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2)); - $bitoffset += 2; - } - - if ($ReturnExtendedInfo) { - - $ThisFileInfo['aac'][$framenumber]['copyright_id_bit'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); - $bitoffset += 1; - $ThisFileInfo['aac'][$framenumber]['copyright_id_start'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); - $bitoffset += 1; - - } else { - - $bitoffset += 2; - - } - - } - - $FrameLength = bindec(substr($AACheaderBitstream, $bitoffset, 13)); - - if (!isset($BitrateCache[$FrameLength])) { - $BitrateCache[$FrameLength] = ($ThisFileInfo['aac']['header']['sample_frequency'] / 1024) * $FrameLength * 8; - } - @$ThisFileInfo['aac']['bitrate_distribution'][$BitrateCache[$FrameLength]]++; - - $ThisFileInfo['aac'][$framenumber]['aac_frame_length'] = $FrameLength; - $bitoffset += 13; - $ThisFileInfo['aac'][$framenumber]['adts_buffer_fullness'] = bindec(substr($AACheaderBitstream, $bitoffset, 11)); - $bitoffset += 11; - if ($ThisFileInfo['aac'][$framenumber]['adts_buffer_fullness'] == 0x07FF) { - $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; - } else { - $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; - } - $ThisFileInfo['aac'][$framenumber]['num_raw_data_blocks'] = bindec(substr($AACheaderBitstream, $bitoffset, 2)); - $bitoffset += 2; - - if ($ThisFileInfo['aac']['header']['crc_present']) { - //$ThisFileInfo['aac'][$framenumber]['crc'] = bindec(substr($AACheaderBitstream, $bitoffset, 16)); - $bitoffset += 16; - } - - if (!$ReturnExtendedInfo) { - unset($ThisFileInfo['aac'][$framenumber]); - } - - $byteoffset += $FrameLength; - if ((++$framenumber < $MaxFramesToScan) && (($byteoffset + 10) < $ThisFileInfo['avdataend'])) { - - // keep scanning - - } else { - - $ThisFileInfo['aac']['frames'] = $framenumber; - $ThisFileInfo['playtime_seconds'] = ($ThisFileInfo['avdataend'] / $byteoffset) * (($framenumber * 1024) / $ThisFileInfo['aac']['header']['sample_frequency']); // (1 / % of file scanned) * (samples / (samples/sec)) = seconds - if ($ThisFileInfo['playtime_seconds'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt AAC file: playtime_seconds == zero'; - return false; - } - $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; - ksort($ThisFileInfo['aac']['bitrate_distribution']); - - $ThisFileInfo['audio']['encoder_options'] = $ThisFileInfo['aac']['header_type'].' '.$ThisFileInfo['aac']['header']['profile_text']; - - return true; - - } - } - // should never get here. - } - - function AACsampleRateLookup($samplerateid) { - static $AACsampleRateLookup = array(); - if (empty($AACsampleRateLookup)) { - $AACsampleRateLookup[0] = 96000; - $AACsampleRateLookup[1] = 88200; - $AACsampleRateLookup[2] = 64000; - $AACsampleRateLookup[3] = 48000; - $AACsampleRateLookup[4] = 44100; - $AACsampleRateLookup[5] = 32000; - $AACsampleRateLookup[6] = 24000; - $AACsampleRateLookup[7] = 22050; - $AACsampleRateLookup[8] = 16000; - $AACsampleRateLookup[9] = 12000; - $AACsampleRateLookup[10] = 11025; - $AACsampleRateLookup[11] = 8000; - $AACsampleRateLookup[12] = 0; - $AACsampleRateLookup[13] = 0; - $AACsampleRateLookup[14] = 0; - $AACsampleRateLookup[15] = 0; - } - return (isset($AACsampleRateLookup[$samplerateid]) ? $AACsampleRateLookup[$samplerateid] : 'invalid'); - } - - function AACprofileLookup($profileid, $mpegversion) { - static $AACprofileLookup = array(); - if (empty($AACprofileLookup)) { - $AACprofileLookup[2][0] = 'Main profile'; - $AACprofileLookup[2][1] = 'Low Complexity profile (LC)'; - $AACprofileLookup[2][2] = 'Scalable Sample Rate profile (SSR)'; - $AACprofileLookup[2][3] = '(reserved)'; - $AACprofileLookup[4][0] = 'AAC_MAIN'; - $AACprofileLookup[4][1] = 'AAC_LC'; - $AACprofileLookup[4][2] = 'AAC_SSR'; - $AACprofileLookup[4][3] = 'AAC_LTP'; - } - return (isset($AACprofileLookup[$mpegversion][$profileid]) ? $AACprofileLookup[$mpegversion][$profileid] : 'invalid'); - } - - function AACchannelCountCalculate($program_configs) { - $channels = 0; - for ($i = 0; $i < $program_configs['num_front_channel_elements']; $i++) { - $channels++; - if ($program_configs['front_element_is_cpe'][$i]) { - // each front element is channel pair (CPE = Channel Pair Element) - $channels++; - } - } - for ($i = 0; $i < $program_configs['num_side_channel_elements']; $i++) { - $channels++; - if ($program_configs['side_element_is_cpe'][$i]) { - // each side element is channel pair (CPE = Channel Pair Element) - $channels++; - } - } - for ($i = 0; $i < $program_configs['num_back_channel_elements']; $i++) { - $channels++; - if ($program_configs['back_element_is_cpe'][$i]) { - // each back element is channel pair (CPE = Channel Pair Element) - $channels++; - } - } - for ($i = 0; $i < $program_configs['num_lfe_channel_elements']; $i++) { - $channels++; - } - return $channels; - } - -} - - -?> \ No newline at end of file diff --git a/getid3/getid3/module.audio.ac3.php b/getid3/getid3/module.audio.ac3.php deleted file mode 100644 index 9809a47..0000000 --- a/getid3/getid3/module.audio.ac3.php +++ /dev/null @@ -1,497 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.audio.ac3.php // -// module for analyzing AC-3 (aka Dolby Digital) audio files // -// dependencies: NONE // -// /// -///////////////////////////////////////////////////////////////// - - -class getid3_ac3 -{ - - function getid3_ac3(&$fd, &$ThisFileInfo) { - - ///AH - $ThisFileInfo['ac3']['raw']['bsi'] = array(); - $thisfile_ac3 = &$ThisFileInfo['ac3']; - $thisfile_ac3_raw = &$thisfile_ac3['raw']; - $thisfile_ac3_raw_bsi = &$thisfile_ac3_raw['bsi']; - - - // http://www.atsc.org/standards/a_52a.pdf - - $ThisFileInfo['fileformat'] = 'ac3'; - $ThisFileInfo['audio']['dataformat'] = 'ac3'; - $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; - $ThisFileInfo['audio']['lossless'] = false; - - // An AC-3 serial coded audio bit stream is made up of a sequence of synchronization frames - // Each synchronization frame contains 6 coded audio blocks (AB), each of which represent 256 - // new audio samples per channel. A synchronization information (SI) header at the beginning - // of each frame contains information needed to acquire and maintain synchronization. A - // bit stream information (BSI) header follows SI, and contains parameters describing the coded - // audio service. The coded audio blocks may be followed by an auxiliary data (Aux) field. At the - // end of each frame is an error check field that includes a CRC word for error detection. An - // additional CRC word is located in the SI header, the use of which, by a decoder, is optional. - // - // syncinfo() | bsi() | AB0 | AB1 | AB2 | AB3 | AB4 | AB5 | Aux | CRC - - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $AC3header['syncinfo'] = fread($fd, 5); - $thisfile_ac3_raw['synchinfo']['synchword'] = substr($AC3header['syncinfo'], 0, 2); - - if ($thisfile_ac3_raw['synchinfo']['synchword'] != "\x0B\x77") { - - $ThisFileInfo['error'][] = 'Expecting "\x0B\x77" at offset '.$ThisFileInfo['avdataoffset'].', found \x'.strtoupper(dechex($AC3header['syncinfo']{0})).'\x'.strtoupper(dechex($AC3header['syncinfo']{1})).' instead'; - unset($thisfile_ac3); - return false; - - } else { - - // syncinfo() { - // syncword 16 - // crc1 16 - // fscod 2 - // frmsizecod 6 - // } /* end of syncinfo */ - - $thisfile_ac3_raw['synchinfo']['crc1'] = getid3_lib::LittleEndian2Int(substr($AC3header['syncinfo'], 2, 2)); - $ac3_synchinfo_fscod_frmsizecod = getid3_lib::LittleEndian2Int(substr($AC3header['syncinfo'], 4, 1)); - $thisfile_ac3_raw['synchinfo']['fscod'] = ($ac3_synchinfo_fscod_frmsizecod & 0xC0) >> 6; - $thisfile_ac3_raw['synchinfo']['frmsizecod'] = ($ac3_synchinfo_fscod_frmsizecod & 0x3F); - - $thisfile_ac3['sample_rate'] = $this->AC3sampleRateCodeLookup($thisfile_ac3_raw['synchinfo']['fscod']); - if ($thisfile_ac3_raw['synchinfo']['fscod'] <= 3) { - $ThisFileInfo['audio']['sample_rate'] = $thisfile_ac3['sample_rate']; - } - - $thisfile_ac3['frame_length'] = $this->AC3frameSizeLookup($thisfile_ac3_raw['synchinfo']['frmsizecod'], $thisfile_ac3_raw['synchinfo']['fscod']); - $thisfile_ac3['bitrate'] = $this->AC3bitrateLookup($thisfile_ac3_raw['synchinfo']['frmsizecod']); - $ThisFileInfo['audio']['bitrate'] = $thisfile_ac3['bitrate']; - - $AC3header['bsi'] = getid3_lib::BigEndian2Bin(fread($fd, 15)); - $ac3_bsi_offset = 0; - - $thisfile_ac3_raw_bsi['bsid'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 5)); - $ac3_bsi_offset += 5; - if ($thisfile_ac3_raw_bsi['bsid'] > 8) { - // Decoders which can decode version 8 will thus be able to decode version numbers less than 8. - // If this standard is extended by the addition of additional elements or features, a value of bsid greater than 8 will be used. - // Decoders built to this version of the standard will not be able to decode versions with bsid greater than 8. - $ThisFileInfo['error'][] = 'Bit stream identification is version '.$thisfile_ac3_raw_bsi['bsid'].', but getID3() only understands up to version 8'; - unset($thisfile_ac3); - return false; - } - - $thisfile_ac3_raw_bsi['bsmod'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 3)); - $ac3_bsi_offset += 3; - $thisfile_ac3_raw_bsi['acmod'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 3)); - $ac3_bsi_offset += 3; - - $thisfile_ac3['service_type'] = $this->AC3serviceTypeLookup($thisfile_ac3_raw_bsi['bsmod'], $thisfile_ac3_raw_bsi['acmod']); - $ac3_coding_mode = $this->AC3audioCodingModeLookup($thisfile_ac3_raw_bsi['acmod']); - foreach($ac3_coding_mode as $key => $value) { - $thisfile_ac3[$key] = $value; - } - switch ($thisfile_ac3_raw_bsi['acmod']) { - case 0: - case 1: - $ThisFileInfo['audio']['channelmode'] = 'mono'; - break; - case 3: - case 4: - $ThisFileInfo['audio']['channelmode'] = 'stereo'; - break; - default: - $ThisFileInfo['audio']['channelmode'] = 'surround'; - break; - } - $ThisFileInfo['audio']['channels'] = $thisfile_ac3['num_channels']; - - if ($thisfile_ac3_raw_bsi['acmod'] & 0x01) { - // If the lsb of acmod is a 1, center channel is in use and cmixlev follows in the bit stream. - $thisfile_ac3_raw_bsi['cmixlev'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 2)); - $ac3_bsi_offset += 2; - $thisfile_ac3['center_mix_level'] = $this->AC3centerMixLevelLookup($thisfile_ac3_raw_bsi['cmixlev']); - } - - if ($thisfile_ac3_raw_bsi['acmod'] & 0x04) { - // If the msb of acmod is a 1, surround channels are in use and surmixlev follows in the bit stream. - $thisfile_ac3_raw_bsi['surmixlev'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 2)); - $ac3_bsi_offset += 2; - $thisfile_ac3['surround_mix_level'] = $this->AC3surroundMixLevelLookup($thisfile_ac3_raw_bsi['surmixlev']); - } - - if ($thisfile_ac3_raw_bsi['acmod'] == 0x02) { - // When operating in the two channel mode, this 2-bit code indicates whether or not the program has been encoded in Dolby Surround. - $thisfile_ac3_raw_bsi['dsurmod'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 2)); - $ac3_bsi_offset += 2; - $thisfile_ac3['dolby_surround_mode'] = $this->AC3dolbySurroundModeLookup($thisfile_ac3_raw_bsi['dsurmod']); - } - - $thisfile_ac3_raw_bsi['lfeon'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1)); - $ac3_bsi_offset += 1; - $thisfile_ac3['lfe_enabled'] = $thisfile_ac3_raw_bsi['lfeon']; - if ($thisfile_ac3_raw_bsi['lfeon']) { - //$ThisFileInfo['audio']['channels']++; - $ThisFileInfo['audio']['channels'] .= '.1'; - } - - $thisfile_ac3['channels_enabled'] = $this->AC3channelsEnabledLookup($thisfile_ac3_raw_bsi['acmod'], $thisfile_ac3_raw_bsi['lfeon']); - - // This indicates how far the average dialogue level is below digital 100 percent. Valid values are 1–31. - // The value of 0 is reserved. The values of 1 to 31 are interpreted as -1 dB to -31 dB with respect to digital 100 percent. - $thisfile_ac3_raw_bsi['dialnorm'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 5)); - $ac3_bsi_offset += 5; - $thisfile_ac3['dialogue_normalization'] = '-'.$thisfile_ac3_raw_bsi['dialnorm'].'dB'; - - $thisfile_ac3_raw_bsi['compre_flag'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1)); - $ac3_bsi_offset += 1; - if ($thisfile_ac3_raw_bsi['compre_flag']) { - $thisfile_ac3_raw_bsi['compr'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 8)); - $ac3_bsi_offset += 8; - $thisfile_ac3['heavy_compression'] = $this->AC3heavyCompression($thisfile_ac3_raw_bsi['compr']); - } - - $thisfile_ac3_raw_bsi['langcode_flag'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1)); - $ac3_bsi_offset += 1; - if ($thisfile_ac3_raw_bsi['langcode_flag']) { - $thisfile_ac3_raw_bsi['langcod'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 8)); - $ac3_bsi_offset += 8; - } - - $thisfile_ac3_raw_bsi['audprodie'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1)); - $ac3_bsi_offset += 1; - if ($thisfile_ac3_raw_bsi['audprodie']) { - $thisfile_ac3_raw_bsi['mixlevel'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 5)); - $ac3_bsi_offset += 5; - $thisfile_ac3_raw_bsi['roomtyp'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 2)); - $ac3_bsi_offset += 2; - - $thisfile_ac3['mixing_level'] = (80 + $thisfile_ac3_raw_bsi['mixlevel']).'dB'; - $thisfile_ac3['room_type'] = $this->AC3roomTypeLookup($thisfile_ac3_raw_bsi['roomtyp']); - } - - if ($thisfile_ac3_raw_bsi['acmod'] == 0x00) { - // If acmod is 0, then two completely independent program channels (dual mono) - // are encoded into the bit stream, and are referenced as Ch1, Ch2. In this case, - // a number of additional items are present in BSI or audblk to fully describe Ch2. - - - // This indicates how far the average dialogue level is below digital 100 percent. Valid values are 1–31. - // The value of 0 is reserved. The values of 1 to 31 are interpreted as -1 dB to -31 dB with respect to digital 100 percent. - $thisfile_ac3_raw_bsi['dialnorm2'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 5)); - $ac3_bsi_offset += 5; - $thisfile_ac3['dialogue_normalization2'] = '-'.$thisfile_ac3_raw_bsi['dialnorm2'].'dB'; - - $thisfile_ac3_raw_bsi['compre_flag2'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1)); - $ac3_bsi_offset += 1; - if ($thisfile_ac3_raw_bsi['compre_flag2']) { - $thisfile_ac3_raw_bsi['compr2'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 8)); - $ac3_bsi_offset += 8; - $thisfile_ac3['heavy_compression2'] = $this->AC3heavyCompression($thisfile_ac3_raw_bsi['compr2']); - } - - $thisfile_ac3_raw_bsi['langcode_flag2'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1)); - $ac3_bsi_offset += 1; - if ($thisfile_ac3_raw_bsi['langcode_flag2']) { - $thisfile_ac3_raw_bsi['langcod2'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 8)); - $ac3_bsi_offset += 8; - } - - $thisfile_ac3_raw_bsi['audprodie2'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1)); - $ac3_bsi_offset += 1; - if ($thisfile_ac3_raw_bsi['audprodie2']) { - $thisfile_ac3_raw_bsi['mixlevel2'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 5)); - $ac3_bsi_offset += 5; - $thisfile_ac3_raw_bsi['roomtyp2'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 2)); - $ac3_bsi_offset += 2; - - $thisfile_ac3['mixing_level2'] = (80 + $thisfile_ac3_raw_bsi['mixlevel2']).'dB'; - $thisfile_ac3['room_type2'] = $this->AC3roomTypeLookup($thisfile_ac3_raw_bsi['roomtyp2']); - } - - } - - $thisfile_ac3_raw_bsi['copyright'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1)); - $ac3_bsi_offset += 1; - - $thisfile_ac3_raw_bsi['original'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1)); - $ac3_bsi_offset += 1; - - $thisfile_ac3_raw_bsi['timecode1_flag'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1)); - $ac3_bsi_offset += 1; - if ($thisfile_ac3_raw_bsi['timecode1_flag']) { - $thisfile_ac3_raw_bsi['timecode1'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 14)); - $ac3_bsi_offset += 14; - } - - $thisfile_ac3_raw_bsi['timecode2_flag'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1)); - $ac3_bsi_offset += 1; - if ($thisfile_ac3_raw_bsi['timecode2_flag']) { - $thisfile_ac3_raw_bsi['timecode2'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 14)); - $ac3_bsi_offset += 14; - } - - $thisfile_ac3_raw_bsi['addbsi_flag'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1)); - $ac3_bsi_offset += 1; - if ($thisfile_ac3_raw_bsi['addbsi_flag']) { - $thisfile_ac3_raw_bsi['addbsi_length'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 6)); - $ac3_bsi_offset += 6; - - $AC3header['bsi'] .= getid3_lib::BigEndian2Bin(fread($fd, $thisfile_ac3_raw_bsi['addbsi_length'])); - - $thisfile_ac3_raw_bsi['addbsi_data'] = substr($AC3header['bsi'], $ac3_bsi_offset, $thisfile_ac3_raw_bsi['addbsi_length'] * 8); - $ac3_bsi_offset += $thisfile_ac3_raw_bsi['addbsi_length'] * 8; - } - - } - - return true; - } - - - function AC3sampleRateCodeLookup($fscod) { - static $AC3sampleRateCodeLookup = array( - 0 => 48000, - 1 => 44100, - 2 => 32000, - 3 => 'reserved' // If the reserved code is indicated, the decoder should not attempt to decode audio and should mute. - ); - return (isset($AC3sampleRateCodeLookup[$fscod]) ? $AC3sampleRateCodeLookup[$fscod] : false); - } - - function AC3serviceTypeLookup($bsmod, $acmod) { - static $AC3serviceTypeLookup = array(); - if (empty($AC3serviceTypeLookup)) { - for ($i = 0; $i <= 7; $i++) { - $AC3serviceTypeLookup[0][$i] = 'main audio service: complete main (CM)'; - $AC3serviceTypeLookup[1][$i] = 'main audio service: music and effects (ME)'; - $AC3serviceTypeLookup[2][$i] = 'associated service: visually impaired (VI)'; - $AC3serviceTypeLookup[3][$i] = 'associated service: hearing impaired (HI)'; - $AC3serviceTypeLookup[4][$i] = 'associated service: dialogue (D)'; - $AC3serviceTypeLookup[5][$i] = 'associated service: commentary (C)'; - $AC3serviceTypeLookup[6][$i] = 'associated service: emergency (E)'; - } - - $AC3serviceTypeLookup[7][1] = 'associated service: voice over (VO)'; - for ($i = 2; $i <= 7; $i++) { - $AC3serviceTypeLookup[7][$i] = 'main audio service: karaoke'; - } - } - return (isset($AC3serviceTypeLookup[$bsmod][$acmod]) ? $AC3serviceTypeLookup[$bsmod][$acmod] : false); - } - - function AC3audioCodingModeLookup($acmod) { - static $AC3audioCodingModeLookup = array(); - if (empty($AC3audioCodingModeLookup)) { - // array(channel configuration, # channels (not incl LFE), channel order) - $AC3audioCodingModeLookup = array ( - 0 => array('channel_config'=>'1+1', 'num_channels'=>2, 'channel_order'=>'Ch1,Ch2'), - 1 => array('channel_config'=>'1/0', 'num_channels'=>1, 'channel_order'=>'C'), - 2 => array('channel_config'=>'2/0', 'num_channels'=>2, 'channel_order'=>'L,R'), - 3 => array('channel_config'=>'3/0', 'num_channels'=>3, 'channel_order'=>'L,C,R'), - 4 => array('channel_config'=>'2/1', 'num_channels'=>3, 'channel_order'=>'L,R,S'), - 5 => array('channel_config'=>'3/1', 'num_channels'=>4, 'channel_order'=>'L,C,R,S'), - 6 => array('channel_config'=>'2/2', 'num_channels'=>4, 'channel_order'=>'L,R,SL,SR'), - 7 => array('channel_config'=>'3/2', 'num_channels'=>5, 'channel_order'=>'L,C,R,SL,SR') - ); - } - return (isset($AC3audioCodingModeLookup[$acmod]) ? $AC3audioCodingModeLookup[$acmod] : false); - } - - function AC3centerMixLevelLookup($cmixlev) { - static $AC3centerMixLevelLookup; - if (empty($AC3centerMixLevelLookup)) { - $AC3centerMixLevelLookup = array( - 0 => pow(2, -3.0 / 6), // 0.707 (–3.0 dB) - 1 => pow(2, -4.5 / 6), // 0.595 (–4.5 dB) - 2 => pow(2, -6.0 / 6), // 0.500 (–6.0 dB) - 3 => 'reserved' - ); - } - return (isset($AC3centerMixLevelLookup[$cmixlev]) ? $AC3centerMixLevelLookup[$cmixlev] : false); - } - - function AC3surroundMixLevelLookup($surmixlev) { - static $AC3surroundMixLevelLookup; - if (empty($AC3surroundMixLevelLookup)) { - $AC3surroundMixLevelLookup = array( - 0 => pow(2, -3.0 / 6), - 1 => pow(2, -6.0 / 6), - 2 => 0, - 3 => 'reserved' - ); - } - return (isset($AC3surroundMixLevelLookup[$surmixlev]) ? $AC3surroundMixLevelLookup[$surmixlev] : false); - } - - function AC3dolbySurroundModeLookup($dsurmod) { - static $AC3dolbySurroundModeLookup = array( - 0 => 'not indicated', - 1 => 'Not Dolby Surround encoded', - 2 => 'Dolby Surround encoded', - 3 => 'reserved' - ); - return (isset($AC3dolbySurroundModeLookup[$dsurmod]) ? $AC3dolbySurroundModeLookup[$dsurmod] : false); - } - - function AC3channelsEnabledLookup($acmod, $lfeon) { - $AC3channelsEnabledLookup = array( - 'ch1'=>(bool) ($acmod == 0), - 'ch2'=>(bool) ($acmod == 0), - 'left'=>(bool) ($acmod > 1), - 'right'=>(bool) ($acmod > 1), - 'center'=>(bool) ($acmod & 0x01), - 'surround_mono'=>false, - 'surround_left'=>false, - 'surround_right'=>false, - 'lfe'=>$lfeon); - switch ($acmod) { - case 4: - case 5: - $AC3channelsEnabledLookup['surround_mono'] = true; - break; - case 6: - case 7: - $AC3channelsEnabledLookup['surround_left'] = true; - $AC3channelsEnabledLookup['surround_right'] = true; - break; - } - return $AC3channelsEnabledLookup; - } - - function AC3heavyCompression($compre) { - // The first four bits indicate gain changes in 6.02dB increments which can be - // implemented with an arithmetic shift operation. The following four bits - // indicate linear gain changes, and require a 5-bit multiply. - // We will represent the two 4-bit fields of compr as follows: - // X0 X1 X2 X3 . Y4 Y5 Y6 Y7 - // The meaning of the X values is most simply described by considering X to represent a 4-bit - // signed integer with values from –8 to +7. The gain indicated by X is then (X + 1) * 6.02 dB. The - // following table shows this in detail. - - // Meaning of 4 msb of compr - // 7 +48.16 dB - // 6 +42.14 dB - // 5 +36.12 dB - // 4 +30.10 dB - // 3 +24.08 dB - // 2 +18.06 dB - // 1 +12.04 dB - // 0 +6.02 dB - // -1 0 dB - // -2 –6.02 dB - // -3 –12.04 dB - // -4 –18.06 dB - // -5 –24.08 dB - // -6 –30.10 dB - // -7 –36.12 dB - // -8 –42.14 dB - - $fourbit = str_pad(decbin(($compre & 0xF0) >> 4), 4, '0', STR_PAD_LEFT); - if ($fourbit{0} == '1') { - $log_gain = -8 + bindec(substr($fourbit, 1)); - } else { - $log_gain = bindec(substr($fourbit, 1)); - } - $log_gain = ($log_gain + 1) * getid3_lib::RGADamplitude2dB(2); - - // The value of Y is a linear representation of a gain change of up to –6 dB. Y is considered to - // be an unsigned fractional integer, with a leading value of 1, or: 0.1 Y4 Y5 Y6 Y7 (base 2). Y can - // represent values between 0.111112 (or 31/32) and 0.100002 (or 1/2). Thus, Y can represent gain - // changes from –0.28 dB to –6.02 dB. - - $lin_gain = (16 + ($compre & 0x0F)) / 32; - - // The combination of X and Y values allows compr to indicate gain changes from - // 48.16 – 0.28 = +47.89 dB, to - // –42.14 – 6.02 = –48.16 dB. - - return $log_gain - $lin_gain; - } - - function AC3roomTypeLookup($roomtyp) { - static $AC3roomTypeLookup = array( - 0 => 'not indicated', - 1 => 'large room, X curve monitor', - 2 => 'small room, flat monitor', - 3 => 'reserved' - ); - return (isset($AC3roomTypeLookup[$roomtyp]) ? $AC3roomTypeLookup[$roomtyp] : false); - } - - function AC3frameSizeLookup($frmsizecod, $fscod) { - $padding = (bool) ($frmsizecod % 2); - $framesizeid = floor($frmsizecod / 2); - - static $AC3frameSizeLookup = array(); - if (empty($AC3frameSizeLookup)) { - $AC3frameSizeLookup = array ( - 0 => array(128, 138, 192), - 1 => array(40, 160, 174, 240), - 2 => array(48, 192, 208, 288), - 3 => array(56, 224, 242, 336), - 4 => array(64, 256, 278, 384), - 5 => array(80, 320, 348, 480), - 6 => array(96, 384, 416, 576), - 7 => array(112, 448, 486, 672), - 8 => array(128, 512, 556, 768), - 9 => array(160, 640, 696, 960), - 10 => array(192, 768, 834, 1152), - 11 => array(224, 896, 974, 1344), - 12 => array(256, 1024, 1114, 1536), - 13 => array(320, 1280, 1392, 1920), - 14 => array(384, 1536, 1670, 2304), - 15 => array(448, 1792, 1950, 2688), - 16 => array(512, 2048, 2228, 3072), - 17 => array(576, 2304, 2506, 3456), - 18 => array(640, 2560, 2786, 3840) - ); - } - if (($fscod == 1) && $padding) { - // frame lengths are padded by 1 word (16 bits) at 44100 - $AC3frameSizeLookup[$frmsizecod] += 2; - } - return (isset($AC3frameSizeLookup[$framesizeid][$fscod]) ? $AC3frameSizeLookup[$framesizeid][$fscod] : false); - } - - function AC3bitrateLookup($frmsizecod) { - $framesizeid = floor($frmsizecod / 2); - - static $AC3bitrateLookup = array( - 0 => 32000, - 1 => 40000, - 2 => 48000, - 3 => 56000, - 4 => 64000, - 5 => 80000, - 6 => 96000, - 7 => 112000, - 8 => 128000, - 9 => 160000, - 10 => 192000, - 11 => 224000, - 12 => 256000, - 13 => 320000, - 14 => 384000, - 15 => 448000, - 16 => 512000, - 17 => 576000, - 18 => 640000 - ); - return (isset($AC3bitrateLookup[$framesizeid]) ? $AC3bitrateLookup[$framesizeid] : false); - } - - -} - -?> \ No newline at end of file diff --git a/getid3/getid3/module.audio.au.php b/getid3/getid3/module.audio.au.php deleted file mode 100644 index afbc75d..0000000 --- a/getid3/getid3/module.audio.au.php +++ /dev/null @@ -1,163 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.audio.au.php // -// module for analyzing AU files // -// dependencies: NONE // -// /// -///////////////////////////////////////////////////////////////// - - -class getid3_au -{ - - function getid3_au(&$fd, &$ThisFileInfo) { - - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $AUheader = fread($fd, 8); - - if (substr($AUheader, 0, 4) != '.snd') { - $ThisFileInfo['error'][] = 'Expecting ".snd" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($AUheader, 0, 4).'"'; - return false; - } - - // shortcut - $ThisFileInfo['au'] = array(); - $thisfile_au = &$ThisFileInfo['au']; - - $ThisFileInfo['fileformat'] = 'au'; - $ThisFileInfo['audio']['dataformat'] = 'au'; - $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; - $thisfile_au['encoding'] = 'ISO-8859-1'; - - $thisfile_au['header_length'] = getid3_lib::BigEndian2Int(substr($AUheader, 4, 4)); - $AUheader .= fread($fd, $thisfile_au['header_length'] - 8); - $ThisFileInfo['avdataoffset'] += $thisfile_au['header_length']; - - $thisfile_au['data_size'] = getid3_lib::BigEndian2Int(substr($AUheader, 8, 4)); - $thisfile_au['data_format_id'] = getid3_lib::BigEndian2Int(substr($AUheader, 12, 4)); - $thisfile_au['sample_rate'] = getid3_lib::BigEndian2Int(substr($AUheader, 16, 4)); - $thisfile_au['channels'] = getid3_lib::BigEndian2Int(substr($AUheader, 20, 4)); - $thisfile_au['comments']['comment'][] = trim(substr($AUheader, 24)); - - $thisfile_au['data_format'] = $this->AUdataFormatNameLookup($thisfile_au['data_format_id']); - $thisfile_au['used_bits_per_sample'] = $this->AUdataFormatUsedBitsPerSampleLookup($thisfile_au['data_format_id']); - if ($thisfile_au['bits_per_sample'] = $this->AUdataFormatBitsPerSampleLookup($thisfile_au['data_format_id'])) { - $ThisFileInfo['audio']['bits_per_sample'] = $thisfile_au['bits_per_sample']; - } else { - unset($thisfile_au['bits_per_sample']); - } - - $ThisFileInfo['audio']['sample_rate'] = $thisfile_au['sample_rate']; - $ThisFileInfo['audio']['channels'] = $thisfile_au['channels']; - - if (($ThisFileInfo['avdataoffset'] + $thisfile_au['data_size']) > $ThisFileInfo['avdataend']) { - $ThisFileInfo['warning'][] = 'Possible truncated file - expecting "'.$thisfile_au['data_size'].'" bytes of audio data, only found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']).' bytes"'; - } - - $ThisFileInfo['playtime_seconds'] = $thisfile_au['data_size'] / ($thisfile_au['sample_rate'] * $thisfile_au['channels'] * ($thisfile_au['used_bits_per_sample'] / 8)); - $ThisFileInfo['audio']['bitrate'] = ($thisfile_au['data_size'] * 8) / $ThisFileInfo['playtime_seconds']; - - return true; - } - - function AUdataFormatNameLookup($id) { - static $AUdataFormatNameLookup = array( - 0 => 'unspecified format', - 1 => '8-bit mu-law', - 2 => '8-bit linear', - 3 => '16-bit linear', - 4 => '24-bit linear', - 5 => '32-bit linear', - 6 => 'floating-point', - 7 => 'double-precision float', - 8 => 'fragmented sampled data', - 9 => 'SUN_FORMAT_NESTED', - 10 => 'DSP program', - 11 => '8-bit fixed-point', - 12 => '16-bit fixed-point', - 13 => '24-bit fixed-point', - 14 => '32-bit fixed-point', - - 16 => 'non-audio display data', - 17 => 'SND_FORMAT_MULAW_SQUELCH', - 18 => '16-bit linear with emphasis', - 19 => '16-bit linear with compression', - 20 => '16-bit linear with emphasis + compression', - 21 => 'Music Kit DSP commands', - 22 => 'SND_FORMAT_DSP_COMMANDS_SAMPLES', - 23 => 'CCITT g.721 4-bit ADPCM', - 24 => 'CCITT g.722 ADPCM', - 25 => 'CCITT g.723 3-bit ADPCM', - 26 => 'CCITT g.723 5-bit ADPCM', - 27 => 'A-Law 8-bit' - ); - return (isset($AUdataFormatNameLookup[$id]) ? $AUdataFormatNameLookup[$id] : false); - } - - function AUdataFormatBitsPerSampleLookup($id) { - static $AUdataFormatBitsPerSampleLookup = array( - 1 => 8, - 2 => 8, - 3 => 16, - 4 => 24, - 5 => 32, - 6 => 32, - 7 => 64, - - 11 => 8, - 12 => 16, - 13 => 24, - 14 => 32, - - 18 => 16, - 19 => 16, - 20 => 16, - - 23 => 16, - - 25 => 16, - 26 => 16, - 27 => 8 - ); - return (isset($AUdataFormatBitsPerSampleLookup[$id]) ? $AUdataFormatBitsPerSampleLookup[$id] : false); - } - - function AUdataFormatUsedBitsPerSampleLookup($id) { - static $AUdataFormatUsedBitsPerSampleLookup = array( - 1 => 8, - 2 => 8, - 3 => 16, - 4 => 24, - 5 => 32, - 6 => 32, - 7 => 64, - - 11 => 8, - 12 => 16, - 13 => 24, - 14 => 32, - - 18 => 16, - 19 => 16, - 20 => 16, - - 23 => 4, - - 25 => 3, - 26 => 5, - 27 => 8, - ); - return (isset($AUdataFormatUsedBitsPerSampleLookup[$id]) ? $AUdataFormatUsedBitsPerSampleLookup[$id] : false); - } - -} - - -?> \ No newline at end of file diff --git a/getid3/getid3/module.audio.avr.php b/getid3/getid3/module.audio.avr.php deleted file mode 100644 index 6099439..0000000 --- a/getid3/getid3/module.audio.avr.php +++ /dev/null @@ -1,125 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.audio.avr.php // -// module for analyzing AVR Audio files // -// dependencies: NONE // -// /// -///////////////////////////////////////////////////////////////// - - -class getid3_avr -{ - - function getid3_avr(&$fd, &$ThisFileInfo) { - - // http://cui.unige.ch/OSG/info/AudioFormats/ap11.html - // http://www.btinternet.com/~AnthonyJ/Atari/programming/avr_format.html - // offset type length name comments - // --------------------------------------------------------------------- - // 0 char 4 ID format ID == "2BIT" - // 4 char 8 name sample name (unused space filled with 0) - // 12 short 1 mono/stereo 0=mono, -1 (0xFFFF)=stereo - // With stereo, samples are alternated, - // the first voice is the left : - // (LRLRLRLRLRLRLRLRLR...) - // 14 short 1 resolution 8, 12 or 16 (bits) - // 16 short 1 signed or not 0=unsigned, -1 (0xFFFF)=signed - // 18 short 1 loop or not 0=no loop, -1 (0xFFFF)=loop on - // 20 short 1 MIDI note 0xFFnn, where 0 <= nn <= 127 - // 0xFFFF means "no MIDI note defined" - // 22 byte 1 Replay speed Frequence in the Replay software - // 0=5.485 Khz, 1=8.084 Khz, 2=10.971 Khz, - // 3=16.168 Khz, 4=21.942 Khz, 5=32.336 Khz - // 6=43.885 Khz, 7=47.261 Khz - // -1 (0xFF)=no defined Frequence - // 23 byte 3 sample rate in Hertz - // 26 long 1 size in bytes (2 * bytes in stereo) - // 30 long 1 loop begin 0 for no loop - // 34 long 1 loop size equal to 'size' for no loop - // 38 short 2 Reserved, MIDI keyboard split */ - // 40 short 2 Reserved, sample compression */ - // 42 short 2 Reserved */ - // 44 char 20; Additional filename space, used if (name[7] != 0) - // 64 byte 64 user data - // 128 bytes ? sample data (12 bits samples are coded on 16 bits: - // 0000 xxxx xxxx xxxx) - // --------------------------------------------------------------------- - - // Note that all values are in motorola (big-endian) format, and that long is - // assumed to be 4 bytes, and short 2 bytes. - // When reading the samples, you should handle both signed and unsigned data, - // and be prepared to convert 16->8 bit, or mono->stereo if needed. To convert - // 8-bit data between signed/unsigned just add 127 to the sample values. - // Simularly for 16-bit data you should add 32769 - - $ThisFileInfo['fileformat'] = 'avr'; - - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $AVRheader = fread($fd, 128); - - $ThisFileInfo['avr']['raw']['magic'] = substr($AVRheader, 0, 4); - if ($ThisFileInfo['avr']['raw']['magic'] != '2BIT') { - $ThisFileInfo['error'][] = 'Expecting "2BIT" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$ThisFileInfo['avr']['raw']['magic'].'"'; - unset($ThisFileInfo['fileformat']); - unset($ThisFileInfo['avr']); - return false; - } - $ThisFileInfo['avdataoffset'] += 128; - - $ThisFileInfo['avr']['sample_name'] = rtrim(substr($AVRheader, 4, 8)); - $ThisFileInfo['avr']['raw']['mono'] = getid3_lib::BigEndian2Int(substr($AVRheader, 12, 2)); - $ThisFileInfo['avr']['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($AVRheader, 14, 2)); - $ThisFileInfo['avr']['raw']['signed'] = getid3_lib::BigEndian2Int(substr($AVRheader, 16, 2)); - $ThisFileInfo['avr']['raw']['loop'] = getid3_lib::BigEndian2Int(substr($AVRheader, 18, 2)); - $ThisFileInfo['avr']['raw']['midi'] = getid3_lib::BigEndian2Int(substr($AVRheader, 20, 2)); - $ThisFileInfo['avr']['raw']['replay_freq'] = getid3_lib::BigEndian2Int(substr($AVRheader, 22, 1)); - $ThisFileInfo['avr']['sample_rate'] = getid3_lib::BigEndian2Int(substr($AVRheader, 23, 3)); - $ThisFileInfo['avr']['sample_length'] = getid3_lib::BigEndian2Int(substr($AVRheader, 26, 4)); - $ThisFileInfo['avr']['loop_start'] = getid3_lib::BigEndian2Int(substr($AVRheader, 30, 4)); - $ThisFileInfo['avr']['loop_end'] = getid3_lib::BigEndian2Int(substr($AVRheader, 34, 4)); - $ThisFileInfo['avr']['midi_split'] = getid3_lib::BigEndian2Int(substr($AVRheader, 38, 2)); - $ThisFileInfo['avr']['sample_compression'] = getid3_lib::BigEndian2Int(substr($AVRheader, 40, 2)); - $ThisFileInfo['avr']['reserved'] = getid3_lib::BigEndian2Int(substr($AVRheader, 42, 2)); - $ThisFileInfo['avr']['sample_name_extra'] = rtrim(substr($AVRheader, 44, 20)); - $ThisFileInfo['avr']['comment'] = rtrim(substr($AVRheader, 64, 64)); - - $ThisFileInfo['avr']['flags']['stereo'] = (($ThisFileInfo['avr']['raw']['mono'] == 0) ? false : true); - $ThisFileInfo['avr']['flags']['signed'] = (($ThisFileInfo['avr']['raw']['signed'] == 0) ? false : true); - $ThisFileInfo['avr']['flags']['loop'] = (($ThisFileInfo['avr']['raw']['loop'] == 0) ? false : true); - - $ThisFileInfo['avr']['midi_notes'] = array(); - if (($ThisFileInfo['avr']['raw']['midi'] & 0xFF00) != 0xFF00) { - $ThisFileInfo['avr']['midi_notes'][] = ($ThisFileInfo['avr']['raw']['midi'] & 0xFF00) >> 8; - } - if (($ThisFileInfo['avr']['raw']['midi'] & 0x00FF) != 0x00FF) { - $ThisFileInfo['avr']['midi_notes'][] = ($ThisFileInfo['avr']['raw']['midi'] & 0x00FF); - } - - if (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) != ($ThisFileInfo['avr']['sample_length'] * (($ThisFileInfo['avr']['bits_per_sample'] == 8) ? 1 : 2))) { - $ThisFileInfo['warning'][] = 'Probable truncated file: expecting '.($ThisFileInfo['avr']['sample_length'] * (($ThisFileInfo['avr']['bits_per_sample'] == 8) ? 1 : 2)).' bytes of audio data, found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']); - } - - $ThisFileInfo['audio']['dataformat'] = 'avr'; - $ThisFileInfo['audio']['lossless'] = true; - $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; - $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['avr']['bits_per_sample']; - $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['avr']['sample_rate']; - $ThisFileInfo['audio']['channels'] = ($ThisFileInfo['avr']['flags']['stereo'] ? 2 : 1); - $ThisFileInfo['playtime_seconds'] = ($ThisFileInfo['avr']['sample_length'] / $ThisFileInfo['audio']['channels']) / $ThisFileInfo['avr']['sample_rate']; - $ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['avr']['sample_length'] * (($ThisFileInfo['avr']['bits_per_sample'] == 8) ? 8 : 16)) / $ThisFileInfo['playtime_seconds']; - - - return true; - } - -} - - -?> \ No newline at end of file diff --git a/getid3/getid3/module.audio.bonk.php b/getid3/getid3/module.audio.bonk.php deleted file mode 100644 index 218aa32..0000000 --- a/getid3/getid3/module.audio.bonk.php +++ /dev/null @@ -1,213 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.audio.la.php // -// module for analyzing BONK audio files // -// dependencies: module.tag.id3v2.php (optional) // -// /// -///////////////////////////////////////////////////////////////// - - -class getid3_bonk -{ - function getid3_bonk(&$fd, &$ThisFileInfo) { - - // shortcut - $ThisFileInfo['bonk'] = array(); - $thisfile_bonk = &$ThisFileInfo['bonk']; - - $thisfile_bonk['dataoffset'] = $ThisFileInfo['avdataoffset']; - $thisfile_bonk['dataend'] = $ThisFileInfo['avdataend']; - - // scan-from-end method, for v0.6 and higher - fseek($fd, $thisfile_bonk['dataend'] - 8, SEEK_SET); - $PossibleBonkTag = fread($fd, 8); - while ($this->BonkIsValidTagName(substr($PossibleBonkTag, 4, 4), true)) { - $BonkTagSize = getid3_lib::LittleEndian2Int(substr($PossibleBonkTag, 0, 4)); - fseek($fd, 0 - $BonkTagSize, SEEK_CUR); - $BonkTagOffset = ftell($fd); - $TagHeaderTest = fread($fd, 5); - if (($TagHeaderTest{0} != "\x00") || (substr($PossibleBonkTag, 4, 4) != strtolower(substr($PossibleBonkTag, 4, 4)))) { - $ThisFileInfo['error'][] = 'Expecting "Ø'.strtoupper(substr($PossibleBonkTag, 4, 4)).'" at offset '.$BonkTagOffset.', found "'.$TagHeaderTest.'"'; - return false; - } - $BonkTagName = substr($TagHeaderTest, 1, 4); - - $thisfile_bonk[$BonkTagName]['size'] = $BonkTagSize; - $thisfile_bonk[$BonkTagName]['offset'] = $BonkTagOffset; - $this->HandleBonkTags($fd, $BonkTagName, $ThisFileInfo); - $NextTagEndOffset = $BonkTagOffset - 8; - if ($NextTagEndOffset < $thisfile_bonk['dataoffset']) { - if (empty($ThisFileInfo['audio']['encoder'])) { - $ThisFileInfo['audio']['encoder'] = 'Extended BONK v0.9+'; - } - return true; - } - fseek($fd, $NextTagEndOffset, SEEK_SET); - $PossibleBonkTag = fread($fd, 8); - } - - // seek-from-beginning method for v0.4 and v0.5 - if (empty($thisfile_bonk['BONK'])) { - fseek($fd, $thisfile_bonk['dataoffset'], SEEK_SET); - do { - $TagHeaderTest = fread($fd, 5); - switch ($TagHeaderTest) { - case "\x00".'BONK': - if (empty($ThisFileInfo['audio']['encoder'])) { - $ThisFileInfo['audio']['encoder'] = 'BONK v0.4'; - } - break; - - case "\x00".'INFO': - $ThisFileInfo['audio']['encoder'] = 'Extended BONK v0.5'; - break; - - default: - break 2; - } - $BonkTagName = substr($TagHeaderTest, 1, 4); - $thisfile_bonk[$BonkTagName]['size'] = $thisfile_bonk['dataend'] - $thisfile_bonk['dataoffset']; - $thisfile_bonk[$BonkTagName]['offset'] = $thisfile_bonk['dataoffset']; - $this->HandleBonkTags($fd, $BonkTagName, $ThisFileInfo); - - } while (true); - } - - // parse META block for v0.6 - v0.8 - if (empty($thisfile_bonk['INFO']) && isset($thisfile_bonk['META']['tags']['info'])) { - fseek($fd, $thisfile_bonk['META']['tags']['info'], SEEK_SET); - $TagHeaderTest = fread($fd, 5); - if ($TagHeaderTest == "\x00".'INFO') { - $ThisFileInfo['audio']['encoder'] = 'Extended BONK v0.6 - v0.8'; - - $BonkTagName = substr($TagHeaderTest, 1, 4); - $thisfile_bonk[$BonkTagName]['size'] = $thisfile_bonk['dataend'] - $thisfile_bonk['dataoffset']; - $thisfile_bonk[$BonkTagName]['offset'] = $thisfile_bonk['dataoffset']; - $this->HandleBonkTags($fd, $BonkTagName, $ThisFileInfo); - } - } - - if (empty($ThisFileInfo['audio']['encoder'])) { - $ThisFileInfo['audio']['encoder'] = 'Extended BONK v0.9+'; - } - if (empty($thisfile_bonk['BONK'])) { - unset($ThisFileInfo['bonk']); - } - return true; - - } - - function HandleBonkTags(&$fd, &$BonkTagName, &$ThisFileInfo) { - - switch ($BonkTagName) { - case 'BONK': - // shortcut - $thisfile_bonk_BONK = &$ThisFileInfo['bonk']['BONK']; - - $BonkData = "\x00".'BONK'.fread($fd, 17); - $thisfile_bonk_BONK['version'] = getid3_lib::LittleEndian2Int(substr($BonkData, 5, 1)); - $thisfile_bonk_BONK['number_samples'] = getid3_lib::LittleEndian2Int(substr($BonkData, 6, 4)); - $thisfile_bonk_BONK['sample_rate'] = getid3_lib::LittleEndian2Int(substr($BonkData, 10, 4)); - - $thisfile_bonk_BONK['channels'] = getid3_lib::LittleEndian2Int(substr($BonkData, 14, 1)); - $thisfile_bonk_BONK['lossless'] = (bool) getid3_lib::LittleEndian2Int(substr($BonkData, 15, 1)); - $thisfile_bonk_BONK['joint_stereo'] = (bool) getid3_lib::LittleEndian2Int(substr($BonkData, 16, 1)); - $thisfile_bonk_BONK['number_taps'] = getid3_lib::LittleEndian2Int(substr($BonkData, 17, 2)); - $thisfile_bonk_BONK['downsampling_ratio'] = getid3_lib::LittleEndian2Int(substr($BonkData, 19, 1)); - $thisfile_bonk_BONK['samples_per_packet'] = getid3_lib::LittleEndian2Int(substr($BonkData, 20, 2)); - - $ThisFileInfo['avdataoffset'] = $thisfile_bonk_BONK['offset'] + 5 + 17; - $ThisFileInfo['avdataend'] = $thisfile_bonk_BONK['offset'] + $thisfile_bonk_BONK['size']; - - $ThisFileInfo['fileformat'] = 'bonk'; - $ThisFileInfo['audio']['dataformat'] = 'bonk'; - $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; // assumed - $ThisFileInfo['audio']['channels'] = $thisfile_bonk_BONK['channels']; - $ThisFileInfo['audio']['sample_rate'] = $thisfile_bonk_BONK['sample_rate']; - $ThisFileInfo['audio']['channelmode'] = ($thisfile_bonk_BONK['joint_stereo'] ? 'joint stereo' : 'stereo'); - $ThisFileInfo['audio']['lossless'] = $thisfile_bonk_BONK['lossless']; - $ThisFileInfo['audio']['codec'] = 'bonk'; - - $ThisFileInfo['playtime_seconds'] = $thisfile_bonk_BONK['number_samples'] / ($thisfile_bonk_BONK['sample_rate'] * $thisfile_bonk_BONK['channels']); - if ($ThisFileInfo['playtime_seconds'] > 0) { - $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['bonk']['dataend'] - $ThisFileInfo['bonk']['dataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; - } - break; - - case 'INFO': - // shortcut - $thisfile_bonk_INFO = &$ThisFileInfo['bonk']['INFO']; - - $thisfile_bonk_INFO['version'] = getid3_lib::LittleEndian2Int(fread($fd, 1)); - $thisfile_bonk_INFO['entries_count'] = 0; - $NextInfoDataPair = fread($fd, 5); - if (!$this->BonkIsValidTagName(substr($NextInfoDataPair, 1, 4))) { - while (!feof($fd)) { - //$CurrentSeekInfo['offset'] = getid3_lib::LittleEndian2Int(substr($NextInfoDataPair, 0, 4)); - //$CurrentSeekInfo['nextbit'] = getid3_lib::LittleEndian2Int(substr($NextInfoDataPair, 4, 1)); - //$thisfile_bonk_INFO[] = $CurrentSeekInfo; - - $NextInfoDataPair = fread($fd, 5); - if ($this->BonkIsValidTagName(substr($NextInfoDataPair, 1, 4))) { - fseek($fd, -5, SEEK_CUR); - break; - } - $thisfile_bonk_INFO['entries_count']++; - } - } - break; - - case 'META': - $BonkData = "\x00".'META'.fread($fd, $ThisFileInfo['bonk']['META']['size'] - 5); - $ThisFileInfo['bonk']['META']['version'] = getid3_lib::LittleEndian2Int(substr($BonkData, 5, 1)); - - $MetaTagEntries = floor(((strlen($BonkData) - 8) - 6) / 8); // BonkData - xxxxmeta - ØMETA - $offset = 6; - for ($i = 0; $i < $MetaTagEntries; $i++) { - $MetaEntryTagName = substr($BonkData, $offset, 4); - $offset += 4; - $MetaEntryTagOffset = getid3_lib::LittleEndian2Int(substr($BonkData, $offset, 4)); - $offset += 4; - $ThisFileInfo['bonk']['META']['tags'][$MetaEntryTagName] = $MetaEntryTagOffset; - } - break; - - case ' ID3': - $ThisFileInfo['audio']['encoder'] = 'Extended BONK v0.9+'; - - // ID3v2 checking is optional - if (class_exists('getid3_id3v2')) { - $ThisFileInfo['bonk'][' ID3']['valid'] = new getid3_id3v2($fd, $ThisFileInfo, $ThisFileInfo['bonk'][' ID3']['offset'] + 2); - } - break; - - default: - $ThisFileInfo['warning'][] = 'Unexpected Bonk tag "'.$BonkTagName.'" at offset '.$ThisFileInfo['bonk'][$BonkTagName]['offset']; - break; - - } - } - - function BonkIsValidTagName($PossibleBonkTag, $ignorecase=false) { - static $BonkIsValidTagName = array('BONK', 'INFO', ' ID3', 'META'); - foreach ($BonkIsValidTagName as $validtagname) { - if ($validtagname == $PossibleBonkTag) { - return true; - } elseif ($ignorecase && (strtolower($validtagname) == strtolower($PossibleBonkTag))) { - return true; - } - } - return false; - } - -} - - -?> \ No newline at end of file diff --git a/getid3/getid3/module.audio.flac.php b/getid3/getid3/module.audio.flac.php deleted file mode 100644 index 573ed98..0000000 --- a/getid3/getid3/module.audio.flac.php +++ /dev/null @@ -1,309 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.audio.flac.php // -// module for analyzing FLAC and OggFLAC audio files // -// dependencies: module.audio.ogg.php // -// /// -///////////////////////////////////////////////////////////////// - - -getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ogg.php', __FILE__, true); - -class getid3_flac -{ - - function getid3_flac(&$fd, &$ThisFileInfo) { - // http://flac.sourceforge.net/format.html - - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $StreamMarker = fread($fd, 4); - if ($StreamMarker != 'fLaC') { - $ThisFileInfo['error'][] = 'Expecting "fLaC" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$StreamMarker.'"'; - return false; - } - $ThisFileInfo['fileformat'] = 'flac'; - $ThisFileInfo['audio']['dataformat'] = 'flac'; - $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; - $ThisFileInfo['audio']['lossless'] = true; - - return getid3_flac::FLACparseMETAdata($fd, $ThisFileInfo); - } - - - function FLACparseMETAdata(&$fd, &$ThisFileInfo) { - - do { - $METAdataBlockOffset = ftell($fd); - $METAdataBlockHeader = fread($fd, 4); - $METAdataLastBlockFlag = (bool) (getid3_lib::BigEndian2Int(substr($METAdataBlockHeader, 0, 1)) & 0x80); - $METAdataBlockType = getid3_lib::BigEndian2Int(substr($METAdataBlockHeader, 0, 1)) & 0x7F; - $METAdataBlockLength = getid3_lib::BigEndian2Int(substr($METAdataBlockHeader, 1, 3)); - $METAdataBlockTypeText = getid3_flac::FLACmetaBlockTypeLookup($METAdataBlockType); - - if ($METAdataBlockLength < 0) { - $ThisFileInfo['error'][] = 'corrupt or invalid METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$METAdataBlockType.') at offset '.$METAdataBlockOffset; - break; - } - - $ThisFileInfo['flac'][$METAdataBlockTypeText]['raw'] = array(); - $ThisFileInfo_flac_METAdataBlockTypeText_raw = &$ThisFileInfo['flac'][$METAdataBlockTypeText]['raw']; - - $ThisFileInfo_flac_METAdataBlockTypeText_raw['offset'] = $METAdataBlockOffset; - $ThisFileInfo_flac_METAdataBlockTypeText_raw['last_meta_block'] = $METAdataLastBlockFlag; - $ThisFileInfo_flac_METAdataBlockTypeText_raw['block_type'] = $METAdataBlockType; - $ThisFileInfo_flac_METAdataBlockTypeText_raw['block_type_text'] = $METAdataBlockTypeText; - $ThisFileInfo_flac_METAdataBlockTypeText_raw['block_length'] = $METAdataBlockLength; - $ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'] = @fread($fd, $METAdataBlockLength); - $ThisFileInfo['avdataoffset'] = ftell($fd); - - switch ($METAdataBlockTypeText) { - - case 'STREAMINFO': - if (!getid3_flac::FLACparseSTREAMINFO($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) { - return false; - } - break; - - case 'PADDING': - // ignore - break; - - case 'APPLICATION': - if (!getid3_flac::FLACparseAPPLICATION($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) { - return false; - } - break; - - case 'SEEKTABLE': - if (!getid3_flac::FLACparseSEEKTABLE($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) { - return false; - } - break; - - case 'VORBIS_COMMENT': - $OldOffset = ftell($fd); - fseek($fd, 0 - $METAdataBlockLength, SEEK_CUR); - getid3_ogg::ParseVorbisCommentsFilepointer($fd, $ThisFileInfo); - fseek($fd, $OldOffset, SEEK_SET); - break; - - case 'CUESHEET': - if (!getid3_flac::FLACparseCUESHEET($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) { - return false; - } - break; - - default: - $ThisFileInfo['warning'][] = 'Unhandled METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$METAdataBlockType.') at offset '.$METAdataBlockOffset; - break; - } - - } while ($METAdataLastBlockFlag === false); - - - if (isset($ThisFileInfo['flac']['STREAMINFO'])) { - $ThisFileInfo['flac']['compressed_audio_bytes'] = $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']; - $ThisFileInfo['flac']['uncompressed_audio_bytes'] = $ThisFileInfo['flac']['STREAMINFO']['samples_stream'] * $ThisFileInfo['flac']['STREAMINFO']['channels'] * ($ThisFileInfo['flac']['STREAMINFO']['bits_per_sample'] / 8); - if ($ThisFileInfo['flac']['uncompressed_audio_bytes'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt FLAC file: uncompressed_audio_bytes == zero'; - return false; - } - $ThisFileInfo['flac']['compression_ratio'] = $ThisFileInfo['flac']['compressed_audio_bytes'] / $ThisFileInfo['flac']['uncompressed_audio_bytes']; - } - - // set md5_data_source - built into flac 0.5+ - if (isset($ThisFileInfo['flac']['STREAMINFO']['audio_signature'])) { - - if ($ThisFileInfo['flac']['STREAMINFO']['audio_signature'] === str_repeat("\x00", 16)) { - - $ThisFileInfo['warning'][] = 'FLAC STREAMINFO.audio_signature is null (known issue with libOggFLAC)'; - - } else { - - $ThisFileInfo['md5_data_source'] = ''; - $md5 = $ThisFileInfo['flac']['STREAMINFO']['audio_signature']; - for ($i = 0; $i < strlen($md5); $i++) { - $ThisFileInfo['md5_data_source'] .= str_pad(dechex(ord($md5{$i})), 2, '00', STR_PAD_LEFT); - } - if (!preg_match('/^[0-9a-f]{32}$/', $ThisFileInfo['md5_data_source'])) { - unset($ThisFileInfo['md5_data_source']); - } - - } - - } - - $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['flac']['STREAMINFO']['bits_per_sample']; - if ($ThisFileInfo['audio']['bits_per_sample'] == 8) { - // special case - // must invert sign bit on all data bytes before MD5'ing to match FLAC's calculated value - // MD5sum calculates on unsigned bytes, but FLAC calculated MD5 on 8-bit audio data as signed - $ThisFileInfo['warning'][] = 'FLAC calculates MD5 data strangely on 8-bit audio, so the stored md5_data_source value will not match the decoded WAV file'; - } - if (!empty($ThisFileInfo['ogg']['vendor'])) { - $ThisFileInfo['audio']['encoder'] = $ThisFileInfo['ogg']['vendor']; - } - - return true; - } - - function FLACmetaBlockTypeLookup($blocktype) { - static $FLACmetaBlockTypeLookup = array(); - if (empty($FLACmetaBlockTypeLookup)) { - $FLACmetaBlockTypeLookup[0] = 'STREAMINFO'; - $FLACmetaBlockTypeLookup[1] = 'PADDING'; - $FLACmetaBlockTypeLookup[2] = 'APPLICATION'; - $FLACmetaBlockTypeLookup[3] = 'SEEKTABLE'; - $FLACmetaBlockTypeLookup[4] = 'VORBIS_COMMENT'; - $FLACmetaBlockTypeLookup[5] = 'CUESHEET'; - } - return (isset($FLACmetaBlockTypeLookup[$blocktype]) ? $FLACmetaBlockTypeLookup[$blocktype] : 'reserved'); - } - - function FLACapplicationIDLookup($applicationid) { - static $FLACapplicationIDLookup = array(); - if (empty($FLACapplicationIDLookup)) { - // http://flac.sourceforge.net/id.html - $FLACapplicationIDLookup[0x46746F6C] = 'flac-tools'; // 'Ftol' - $FLACapplicationIDLookup[0x46746F6C] = 'Sound Font FLAC'; // 'SFFL' - } - return (isset($FLACapplicationIDLookup[$applicationid]) ? $FLACapplicationIDLookup[$applicationid] : 'reserved'); - } - - function FLACparseSTREAMINFO($METAdataBlockData, &$ThisFileInfo) { - $offset = 0; - $ThisFileInfo['flac']['STREAMINFO']['min_block_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 2)); - $offset += 2; - $ThisFileInfo['flac']['STREAMINFO']['max_block_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 2)); - $offset += 2; - $ThisFileInfo['flac']['STREAMINFO']['min_frame_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 3)); - $offset += 3; - $ThisFileInfo['flac']['STREAMINFO']['max_frame_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 3)); - $offset += 3; - - $SampleRateChannelsSampleBitsStreamSamples = getid3_lib::BigEndian2Bin(substr($METAdataBlockData, $offset, 8)); - $ThisFileInfo['flac']['STREAMINFO']['sample_rate'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 0, 20)); - $ThisFileInfo['flac']['STREAMINFO']['channels'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 20, 3)) + 1; - $ThisFileInfo['flac']['STREAMINFO']['bits_per_sample'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 23, 5)) + 1; - $ThisFileInfo['flac']['STREAMINFO']['samples_stream'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 28, 36)); - $offset += 8; - - $ThisFileInfo['flac']['STREAMINFO']['audio_signature'] = substr($METAdataBlockData, $offset, 16); - $offset += 16; - - if (!empty($ThisFileInfo['flac']['STREAMINFO']['sample_rate'])) { - - $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; - $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['flac']['STREAMINFO']['sample_rate']; - $ThisFileInfo['audio']['channels'] = $ThisFileInfo['flac']['STREAMINFO']['channels']; - $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['flac']['STREAMINFO']['bits_per_sample']; - $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['flac']['STREAMINFO']['samples_stream'] / $ThisFileInfo['flac']['STREAMINFO']['sample_rate']; - $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; - - } else { - - $ThisFileInfo['error'][] = 'Corrupt METAdata block: STREAMINFO'; - return false; - - } - return true; - } - - - function FLACparseAPPLICATION($METAdataBlockData, &$ThisFileInfo) { - $offset = 0; - $ApplicationID = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 4)); - $offset += 4; - $ThisFileInfo['flac']['APPLICATION'][$ApplicationID]['name'] = getid3_flac::FLACapplicationIDLookup($ApplicationID); - $ThisFileInfo['flac']['APPLICATION'][$ApplicationID]['data'] = substr($METAdataBlockData, $offset); - $offset = $METAdataBlockLength; - - return true; - } - - - function FLACparseSEEKTABLE($METAdataBlockData, &$ThisFileInfo) { - $offset = 0; - $METAdataBlockLength = strlen($METAdataBlockData); - $placeholderpattern = str_repeat("\xFF", 8); - while ($offset < $METAdataBlockLength) { - $SampleNumberString = substr($METAdataBlockData, $offset, 8); - $offset += 8; - if ($SampleNumberString == $placeholderpattern) { - - // placeholder point - @$ThisFileInfo['flac']['SEEKTABLE']['placeholders']++; - $offset += 10; - - } else { - - $SampleNumber = getid3_lib::BigEndian2Int($SampleNumberString); - $ThisFileInfo['flac']['SEEKTABLE'][$SampleNumber]['offset'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 8)); - $offset += 8; - $ThisFileInfo['flac']['SEEKTABLE'][$SampleNumber]['samples'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 2)); - $offset += 2; - - } - } - return true; - } - - function FLACparseCUESHEET($METAdataBlockData, &$ThisFileInfo) { - $offset = 0; - $ThisFileInfo['flac']['CUESHEET']['media_catalog_number'] = trim(substr($METAdataBlockData, $offset, 128), "\0"); - $offset += 128; - $ThisFileInfo['flac']['CUESHEET']['lead_in_samples'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 8)); - $offset += 8; - $ThisFileInfo['flac']['CUESHEET']['flags']['is_cd'] = (bool) (getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1)) & 0x80); - $offset += 1; - - $offset += 258; // reserved - - $ThisFileInfo['flac']['CUESHEET']['number_tracks'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1)); - $offset += 1; - - for ($track = 0; $track < $ThisFileInfo['flac']['CUESHEET']['number_tracks']; $track++) { - $TrackSampleOffset = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 8)); - $offset += 8; - $TrackNumber = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1)); - $offset += 1; - - $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['sample_offset'] = $TrackSampleOffset; - - $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['isrc'] = substr($METAdataBlockData, $offset, 12); - $offset += 12; - - $TrackFlagsRaw = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1)); - $offset += 1; - $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['flags']['is_audio'] = (bool) ($TrackFlagsRaw & 0x80); - $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['flags']['pre_emphasis'] = (bool) ($TrackFlagsRaw & 0x40); - - $offset += 13; // reserved - - $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['index_points'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1)); - $offset += 1; - - for ($index = 0; $index < $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['index_points']; $index++) { - $IndexSampleOffset = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 8)); - $offset += 8; - $IndexNumber = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1)); - $offset += 1; - - $offset += 3; // reserved - - $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['indexes'][$IndexNumber] = $IndexSampleOffset; - } - } - return true; - } - -} - -?> \ No newline at end of file diff --git a/getid3/getid3/module.audio.la.php b/getid3/getid3/module.audio.la.php deleted file mode 100644 index 9f82aca..0000000 --- a/getid3/getid3/module.audio.la.php +++ /dev/null @@ -1,227 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.audio.la.php // -// module for analyzing LA audio files // -// dependencies: module.audio.riff.php // -// /// -///////////////////////////////////////////////////////////////// - -getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true); - -class getid3_la -{ - - function getid3_la(&$fd, &$ThisFileInfo) { - $offset = 0; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $rawdata = fread($fd, GETID3_FREAD_BUFFER_SIZE); - - switch (substr($rawdata, $offset, 4)) { - case 'LA02': - case 'LA03': - case 'LA04': - $ThisFileInfo['fileformat'] = 'la'; - $ThisFileInfo['audio']['dataformat'] = 'la'; - $ThisFileInfo['audio']['lossless'] = true; - - $ThisFileInfo['la']['version_major'] = (int) substr($rawdata, $offset + 2, 1); - $ThisFileInfo['la']['version_minor'] = (int) substr($rawdata, $offset + 3, 1); - $ThisFileInfo['la']['version'] = (float) $ThisFileInfo['la']['version_major'] + ($ThisFileInfo['la']['version_minor'] / 10); - $offset += 4; - - $ThisFileInfo['la']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); - $offset += 4; - if ($ThisFileInfo['la']['uncompressed_size'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt LA file: uncompressed_size == zero'; - return false; - } - - $WAVEchunk = substr($rawdata, $offset, 4); - if ($WAVEchunk !== 'WAVE') { - $ThisFileInfo['error'][] = 'Expected "WAVE" ('.getid3_lib::PrintHexBytes('WAVE').') at offset '.$offset.', found "'.$WAVEchunk.'" ('.getid3_lib::PrintHexBytes($WAVEchunk).') instead.'; - return false; - } - $offset += 4; - - $ThisFileInfo['la']['fmt_size'] = 24; - if ($ThisFileInfo['la']['version'] >= 0.3) { - - $ThisFileInfo['la']['fmt_size'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); - $ThisFileInfo['la']['header_size'] = 49 + $ThisFileInfo['la']['fmt_size'] - 24; - $offset += 4; - - } else { - - // version 0.2 didn't support additional data blocks - $ThisFileInfo['la']['header_size'] = 41; - - } - - $fmt_chunk = substr($rawdata, $offset, 4); - if ($fmt_chunk !== 'fmt ') { - $ThisFileInfo['error'][] = 'Expected "fmt " ('.getid3_lib::PrintHexBytes('fmt ').') at offset '.$offset.', found "'.$fmt_chunk.'" ('.getid3_lib::PrintHexBytes($fmt_chunk).') instead.'; - return false; - } - $offset += 4; - $fmt_size = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); - $offset += 4; - - $ThisFileInfo['la']['raw']['format'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2)); - $offset += 2; - - $ThisFileInfo['la']['channels'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2)); - $offset += 2; - if ($ThisFileInfo['la']['channels'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt LA file: channels == zero'; - return false; - } - - $ThisFileInfo['la']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); - $offset += 4; - if ($ThisFileInfo['la']['sample_rate'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt LA file: sample_rate == zero'; - return false; - } - - $ThisFileInfo['la']['bytes_per_second'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); - $offset += 4; - $ThisFileInfo['la']['bytes_per_sample'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2)); - $offset += 2; - $ThisFileInfo['la']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2)); - $offset += 2; - - $ThisFileInfo['la']['samples'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); - $offset += 4; - - $ThisFileInfo['la']['raw']['flags'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 1)); - $offset += 1; - $ThisFileInfo['la']['flags']['seekable'] = (bool) ($ThisFileInfo['la']['raw']['flags'] & 0x01); - if ($ThisFileInfo['la']['version'] >= 0.4) { - $ThisFileInfo['la']['flags']['high_compression'] = (bool) ($ThisFileInfo['la']['raw']['flags'] & 0x02); - } - - $ThisFileInfo['la']['original_crc'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); - $offset += 4; - - // mikeØbevin*de - // Basically, the blocksize/seekevery are 61440/19 in La0.4 and 73728/16 - // in earlier versions. A seekpoint is added every blocksize * seekevery - // samples, so 4 * int(totalSamples / (blockSize * seekEvery)) should - // give the number of bytes used for the seekpoints. Of course, if seeking - // is disabled, there are no seekpoints stored. - if ($ThisFileInfo['la']['version'] >= 0.4) { - $ThisFileInfo['la']['blocksize'] = 61440; - $ThisFileInfo['la']['seekevery'] = 19; - } else { - $ThisFileInfo['la']['blocksize'] = 73728; - $ThisFileInfo['la']['seekevery'] = 16; - } - - $ThisFileInfo['la']['seekpoint_count'] = 0; - if ($ThisFileInfo['la']['flags']['seekable']) { - $ThisFileInfo['la']['seekpoint_count'] = floor($ThisFileInfo['la']['samples'] / ($ThisFileInfo['la']['blocksize'] * $ThisFileInfo['la']['seekevery'])); - - for ($i = 0; $i < $ThisFileInfo['la']['seekpoint_count']; $i++) { - $ThisFileInfo['la']['seekpoints'][] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); - $offset += 4; - } - } - - if ($ThisFileInfo['la']['version'] >= 0.3) { - - // Following the main header information, the program outputs all of the - // seekpoints. Following these is what I called the 'footer start', - // i.e. the position immediately after the La audio data is finished. - $ThisFileInfo['la']['footerstart'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); - $offset += 4; - - if ($ThisFileInfo['la']['footerstart'] > $ThisFileInfo['filesize']) { - $ThisFileInfo['warning'][] = 'FooterStart value points to offset '.$ThisFileInfo['la']['footerstart'].' which is beyond end-of-file ('.$ThisFileInfo['filesize'].')'; - $ThisFileInfo['la']['footerstart'] = $ThisFileInfo['filesize']; - } - - } else { - - // La v0.2 didn't have FooterStart value - $ThisFileInfo['la']['footerstart'] = $ThisFileInfo['avdataend']; - - } - - if ($ThisFileInfo['la']['footerstart'] < $ThisFileInfo['avdataend']) { - if ($RIFFtempfilename = tempnam('*', 'id3')) { - if ($RIFF_fp = fopen($RIFFtempfilename, 'w+b')) { - $RIFFdata = 'WAVE'; - if ($ThisFileInfo['la']['version'] == 0.2) { - $RIFFdata .= substr($rawdata, 12, 24); - } else { - $RIFFdata .= substr($rawdata, 16, 24); - } - if ($ThisFileInfo['la']['footerstart'] < $ThisFileInfo['avdataend']) { - fseek($fd, $ThisFileInfo['la']['footerstart'], SEEK_SET); - $RIFFdata .= fread($fd, $ThisFileInfo['avdataend'] - $ThisFileInfo['la']['footerstart']); - } - $RIFFdata = 'RIFF'.getid3_lib::LittleEndian2String(strlen($RIFFdata), 4, false).$RIFFdata; - fwrite($RIFF_fp, $RIFFdata, strlen($RIFFdata)); - $dummy = $ThisFileInfo; - $dummy['filesize'] = strlen($RIFFdata); - $dummy['avdataoffset'] = 0; - $dummy['avdataend'] = $dummy['filesize']; - - $riff = new getid3_riff($RIFF_fp, $dummy); - if (empty($dummy['error'])) { - $ThisFileInfo['riff'] = $dummy['riff']; - } else { - $ThisFileInfo['warning'][] = 'Error parsing RIFF portion of La file: '.implode($dummy['error']); - } - unset($dummy); - fclose($RIFF_fp); - } - unlink($RIFFtempfilename); - } - } - - // $ThisFileInfo['avdataoffset'] should be zero to begin with, but just in case it's not, include the addition anyway - $ThisFileInfo['avdataend'] = $ThisFileInfo['avdataoffset'] + $ThisFileInfo['la']['footerstart']; - $ThisFileInfo['avdataoffset'] = $ThisFileInfo['avdataoffset'] + $offset; - - //$ThisFileInfo['la']['codec'] = RIFFwFormatTagLookup($ThisFileInfo['la']['raw']['format']); - $ThisFileInfo['la']['compression_ratio'] = (float) (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $ThisFileInfo['la']['uncompressed_size']); - $ThisFileInfo['playtime_seconds'] = (float) ($ThisFileInfo['la']['samples'] / $ThisFileInfo['la']['sample_rate']) / $ThisFileInfo['la']['channels']; - if ($ThisFileInfo['playtime_seconds'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt LA file: playtime_seconds == zero'; - return false; - } - - $ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / $ThisFileInfo['playtime_seconds']; - //$ThisFileInfo['audio']['codec'] = $ThisFileInfo['la']['codec']; - $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['la']['bits_per_sample']; - break; - - default: - if (substr($rawdata, $offset, 2) == 'LA') { - $ThisFileInfo['error'][] = 'This version of getID3() (v'.GETID3_VERSION.') doesn\'t support LA version '.substr($rawdata, $offset + 2, 1).'.'.substr($rawdata, $offset + 3, 1).' which this appears to be - check http://getid3.sourceforge.net for updates.'; - } else { - $ThisFileInfo['error'][] = 'Not a LA (Lossless-Audio) file'; - } - return false; - break; - } - - $ThisFileInfo['audio']['channels'] = $ThisFileInfo['la']['channels']; - $ThisFileInfo['audio']['sample_rate'] = (int) $ThisFileInfo['la']['sample_rate']; - $ThisFileInfo['audio']['encoder'] = 'LA v'.$ThisFileInfo['la']['version']; - - return true; - } - -} - - -?> \ No newline at end of file diff --git a/getid3/getid3/module.audio.lpac.php b/getid3/getid3/module.audio.lpac.php deleted file mode 100644 index 64a87d0..0000000 --- a/getid3/getid3/module.audio.lpac.php +++ /dev/null @@ -1,125 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.audio.lpac.php // -// module for analyzing LPAC Audio files // -// dependencies: module.audio-video.riff.php // -// /// -///////////////////////////////////////////////////////////////// - -getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true); - -class getid3_lpac -{ - - function getid3_lpac(&$fd, &$ThisFileInfo) { - - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $LPACheader = fread($fd, 14); - if (substr($LPACheader, 0, 4) != 'LPAC') { - $ThisFileInfo['error'][] = 'Expected "LPAC" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$StreamMarker.'"'; - return false; - } - $ThisFileInfo['avdataoffset'] += 14; - - $ThisFileInfo['fileformat'] = 'lpac'; - $ThisFileInfo['audio']['dataformat'] = 'lpac'; - $ThisFileInfo['audio']['lossless'] = true; - $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; - - $ThisFileInfo['lpac']['file_version'] = getid3_lib::BigEndian2Int(substr($LPACheader, 4, 1)); - $flags['audio_type'] = getid3_lib::BigEndian2Int(substr($LPACheader, 5, 1)); - $ThisFileInfo['lpac']['total_samples']= getid3_lib::BigEndian2Int(substr($LPACheader, 6, 4)); - $flags['parameters'] = getid3_lib::BigEndian2Int(substr($LPACheader, 10, 4)); - - $ThisFileInfo['lpac']['flags']['is_wave'] = (bool) ($flags['audio_type'] & 0x40); - $ThisFileInfo['lpac']['flags']['stereo'] = (bool) ($flags['audio_type'] & 0x04); - $ThisFileInfo['lpac']['flags']['24_bit'] = (bool) ($flags['audio_type'] & 0x02); - $ThisFileInfo['lpac']['flags']['16_bit'] = (bool) ($flags['audio_type'] & 0x01); - - if ($ThisFileInfo['lpac']['flags']['24_bit'] && $ThisFileInfo['lpac']['flags']['16_bit']) { - $ThisFileInfo['warning'][] = '24-bit and 16-bit flags cannot both be set'; - } - - $ThisFileInfo['lpac']['flags']['fast_compress'] = (bool) ($flags['parameters'] & 0x40000000); - $ThisFileInfo['lpac']['flags']['random_access'] = (bool) ($flags['parameters'] & 0x08000000); - $ThisFileInfo['lpac']['block_length'] = pow(2, (($flags['parameters'] & 0x07000000) >> 24)) * 256; - $ThisFileInfo['lpac']['flags']['adaptive_prediction_order'] = (bool) ($flags['parameters'] & 0x00800000); - $ThisFileInfo['lpac']['flags']['adaptive_quantization'] = (bool) ($flags['parameters'] & 0x00400000); - $ThisFileInfo['lpac']['flags']['joint_stereo'] = (bool) ($flags['parameters'] & 0x00040000); - $ThisFileInfo['lpac']['quantization'] = ($flags['parameters'] & 0x00001F00) >> 8; - $ThisFileInfo['lpac']['max_prediction_order'] = ($flags['parameters'] & 0x0000003F); - - if ($ThisFileInfo['lpac']['flags']['fast_compress'] && ($ThisFileInfo['lpac']['max_prediction_order'] != 3)) { - $ThisFileInfo['warning'][] = 'max_prediction_order expected to be "3" if fast_compress is true, actual value is "'.$ThisFileInfo['lpac']['max_prediction_order'].'"'; - } - switch ($ThisFileInfo['lpac']['file_version']) { - case 6: - if ($ThisFileInfo['lpac']['flags']['adaptive_quantization']) { - $ThisFileInfo['warning'][] = 'adaptive_quantization expected to be false in LPAC file stucture v6, actually true'; - } - if ($ThisFileInfo['lpac']['quantization'] != 20) { - $ThisFileInfo['warning'][] = 'Quantization expected to be 20 in LPAC file stucture v6, actually '.$ThisFileInfo['lpac']['flags']['Q']; - } - break; - - default: - //$ThisFileInfo['warning'][] = 'This version of getID3() only supports LPAC file format version 6, this file is version '.$ThisFileInfo['lpac']['file_version'].' - please report to info@getid3.org'; - break; - } - - $dummy = $ThisFileInfo; - $riff = new getid3_riff($fd, $dummy); - $ThisFileInfo['avdataoffset'] = $dummy['avdataoffset']; - $ThisFileInfo['riff'] = $dummy['riff']; - $ThisFileInfo['error'] = $dummy['error']; - $ThisFileInfo['warning'] = $dummy['warning']; - $ThisFileInfo['lpac']['comments']['comment'] = $dummy['comments']; - $ThisFileInfo['audio']['sample_rate'] = $dummy['audio']['sample_rate']; - - $ThisFileInfo['audio']['channels'] = ($ThisFileInfo['lpac']['flags']['stereo'] ? 2 : 1); - - if ($ThisFileInfo['lpac']['flags']['24_bit']) { - $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['riff']['audio'][0]['bits_per_sample']; - } elseif ($ThisFileInfo['lpac']['flags']['16_bit']) { - $ThisFileInfo['audio']['bits_per_sample'] = 16; - } else { - $ThisFileInfo['audio']['bits_per_sample'] = 8; - } - - if ($ThisFileInfo['lpac']['flags']['fast_compress']) { - // fast - $ThisFileInfo['audio']['encoder_options'] = '-1'; - } else { - switch ($ThisFileInfo['lpac']['max_prediction_order']) { - case 20: // simple - $ThisFileInfo['audio']['encoder_options'] = '-2'; - break; - case 30: // medium - $ThisFileInfo['audio']['encoder_options'] = '-3'; - break; - case 40: // high - $ThisFileInfo['audio']['encoder_options'] = '-4'; - break; - case 60: // extrahigh - $ThisFileInfo['audio']['encoder_options'] = '-5'; - break; - } - } - - $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['lpac']['total_samples'] / $ThisFileInfo['audio']['sample_rate']; - $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; - - return true; - } - -} - - -?> \ No newline at end of file diff --git a/getid3/getid3/module.audio.midi.php b/getid3/getid3/module.audio.midi.php deleted file mode 100644 index f72760d..0000000 --- a/getid3/getid3/module.audio.midi.php +++ /dev/null @@ -1,520 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.audio.midi.php // -// module for Midi Audio files // -// dependencies: NONE // -// /// -///////////////////////////////////////////////////////////////// - - -class getid3_midi -{ - - function getid3_midi(&$fd, &$ThisFileInfo, $scanwholefile=true) { - - // shortcut - $ThisFileInfo['midi']['raw'] = array(); - $thisfile_midi = &$ThisFileInfo['midi']; - $thisfile_midi_raw = &$thisfile_midi['raw']; - - $ThisFileInfo['fileformat'] = 'midi'; - $ThisFileInfo['audio']['dataformat'] = 'midi'; - - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $MIDIdata = fread($fd, GETID3_FREAD_BUFFER_SIZE); - $offset = 0; - $MIDIheaderID = substr($MIDIdata, $offset, 4); // 'MThd' - if ($MIDIheaderID != 'MThd') { - $ThisFileInfo['error'][] = 'Expecting "MThd" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$MIDIheaderID.'"'; - unset($ThisFileInfo['fileformat']); - return false; - } - $offset += 4; - $thisfile_midi_raw['headersize'] = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 4)); - $offset += 4; - $thisfile_midi_raw['fileformat'] = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 2)); - $offset += 2; - $thisfile_midi_raw['tracks'] = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 2)); - $offset += 2; - $thisfile_midi_raw['ticksperqnote'] = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 2)); - $offset += 2; - - for ($i = 0; $i < $thisfile_midi_raw['tracks']; $i++) { - if ((strlen($MIDIdata) - $offset) < 8) { - $MIDIdata .= fread($fd, GETID3_FREAD_BUFFER_SIZE); - } - $trackID = substr($MIDIdata, $offset, 4); - $offset += 4; - if ($trackID == 'MTrk') { - $tracksize = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 4)); - $offset += 4; - // $thisfile_midi['tracks'][$i]['size'] = $tracksize; - $trackdataarray[$i] = substr($MIDIdata, $offset, $tracksize); - $offset += $tracksize; - } else { - $ThisFileInfo['error'][] = 'Expecting "MTrk" at '.$offset.', found '.$trackID.' instead'; - return false; - } - } - - if (!isset($trackdataarray) || !is_array($trackdataarray)) { - $ThisFileInfo['error'][] = 'Cannot find MIDI track information'; - unset($thisfile_midi); - unset($ThisFileInfo['fileformat']); - return false; - } - - if ($scanwholefile) { // this can take quite a long time, so have the option to bypass it if speed is very important - $thisfile_midi['totalticks'] = 0; - $ThisFileInfo['playtime_seconds'] = 0; - $CurrentMicroSecondsPerBeat = 500000; // 120 beats per minute; 60,000,000 microseconds per minute -> 500,000 microseconds per beat - $CurrentBeatsPerMinute = 120; // 120 beats per minute; 60,000,000 microseconds per minute -> 500,000 microseconds per beat - - foreach ($trackdataarray as $tracknumber => $trackdata) { - - $eventsoffset = 0; - $LastIssuedMIDIcommand = 0; - $LastIssuedMIDIchannel = 0; - $CumulativeDeltaTime = 0; - $TicksAtCurrentBPM = 0; - while ($eventsoffset < strlen($trackdata)) { - $eventid = 0; - if (isset($MIDIevents[$tracknumber]) && is_array($MIDIevents[$tracknumber])) { - $eventid = count($MIDIevents[$tracknumber]); - } - $deltatime = 0; - for ($i = 0; $i < 4; $i++) { - $deltatimebyte = ord(substr($trackdata, $eventsoffset++, 1)); - $deltatime = ($deltatime << 7) + ($deltatimebyte & 0x7F); - if ($deltatimebyte & 0x80) { - // another byte follows - } else { - break; - } - } - $CumulativeDeltaTime += $deltatime; - $TicksAtCurrentBPM += $deltatime; - $MIDIevents[$tracknumber][$eventid]['deltatime'] = $deltatime; - $MIDI_event_channel = ord(substr($trackdata, $eventsoffset++, 1)); - if ($MIDI_event_channel & 0x80) { - // OK, normal event - MIDI command has MSB set - $LastIssuedMIDIcommand = $MIDI_event_channel >> 4; - $LastIssuedMIDIchannel = $MIDI_event_channel & 0x0F; - } else { - // running event - assume last command - $eventsoffset--; - } - $MIDIevents[$tracknumber][$eventid]['eventid'] = $LastIssuedMIDIcommand; - $MIDIevents[$tracknumber][$eventid]['channel'] = $LastIssuedMIDIchannel; - if ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x08) { // Note off (key is released) - - $notenumber = ord(substr($trackdata, $eventsoffset++, 1)); - $velocity = ord(substr($trackdata, $eventsoffset++, 1)); - - } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x09) { // Note on (key is pressed) - - $notenumber = ord(substr($trackdata, $eventsoffset++, 1)); - $velocity = ord(substr($trackdata, $eventsoffset++, 1)); - - } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0A) { // Key after-touch - - $notenumber = ord(substr($trackdata, $eventsoffset++, 1)); - $velocity = ord(substr($trackdata, $eventsoffset++, 1)); - - } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0B) { // Control Change - - $controllernum = ord(substr($trackdata, $eventsoffset++, 1)); - $newvalue = ord(substr($trackdata, $eventsoffset++, 1)); - - } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0C) { // Program (patch) change - - $newprogramnum = ord(substr($trackdata, $eventsoffset++, 1)); - - $thisfile_midi_raw['track'][$tracknumber]['instrumentid'] = $newprogramnum; - if ($tracknumber == 10) { - $thisfile_midi_raw['track'][$tracknumber]['instrument'] = $this->GeneralMIDIpercussionLookup($newprogramnum); - } else { - $thisfile_midi_raw['track'][$tracknumber]['instrument'] = $this->GeneralMIDIinstrumentLookup($newprogramnum); - } - - } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0D) { // Channel after-touch - - $channelnumber = ord(substr($trackdata, $eventsoffset++, 1)); - - } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0E) { // Pitch wheel change (2000H is normal or no change) - - $changeLSB = ord(substr($trackdata, $eventsoffset++, 1)); - $changeMSB = ord(substr($trackdata, $eventsoffset++, 1)); - $pitchwheelchange = (($changeMSB & 0x7F) << 7) & ($changeLSB & 0x7F); - - } elseif (($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0F) && ($MIDIevents[$tracknumber][$eventid]['channel'] == 0x0F)) { - - $METAeventCommand = ord(substr($trackdata, $eventsoffset++, 1)); - $METAeventLength = ord(substr($trackdata, $eventsoffset++, 1)); - $METAeventData = substr($trackdata, $eventsoffset, $METAeventLength); - $eventsoffset += $METAeventLength; - switch ($METAeventCommand) { - case 0x00: // Set track sequence number - $track_sequence_number = getid3_lib::BigEndian2Int(substr($METAeventData, 0, $METAeventLength)); - //$thisfile_midi_raw['events'][$tracknumber][$eventid]['seqno'] = $track_sequence_number; - break; - - case 0x01: // Text: generic - $text_generic = substr($METAeventData, 0, $METAeventLength); - //$thisfile_midi_raw['events'][$tracknumber][$eventid]['text'] = $text_generic; - $thisfile_midi['comments']['comment'][] = $text_generic; - break; - - case 0x02: // Text: copyright - $text_copyright = substr($METAeventData, 0, $METAeventLength); - //$thisfile_midi_raw['events'][$tracknumber][$eventid]['copyright'] = $text_copyright; - $thisfile_midi['comments']['copyright'][] = $text_copyright; - break; - - case 0x03: // Text: track name - $text_trackname = substr($METAeventData, 0, $METAeventLength); - $thisfile_midi_raw['track'][$tracknumber]['name'] = $text_trackname; - break; - - case 0x04: // Text: track instrument name - $text_instrument = substr($METAeventData, 0, $METAeventLength); - //$thisfile_midi_raw['events'][$tracknumber][$eventid]['instrument'] = $text_instrument; - break; - - case 0x05: // Text: lyrics - $text_lyrics = substr($METAeventData, 0, $METAeventLength); - //$thisfile_midi_raw['events'][$tracknumber][$eventid]['lyrics'] = $text_lyrics; - if (!isset($thisfile_midi['lyrics'])) { - $thisfile_midi['lyrics'] = ''; - } - $thisfile_midi['lyrics'] .= $text_lyrics."\n"; - break; - - case 0x06: // Text: marker - $text_marker = substr($METAeventData, 0, $METAeventLength); - //$thisfile_midi_raw['events'][$tracknumber][$eventid]['marker'] = $text_marker; - break; - - case 0x07: // Text: cue point - $text_cuepoint = substr($METAeventData, 0, $METAeventLength); - //$thisfile_midi_raw['events'][$tracknumber][$eventid]['cuepoint'] = $text_cuepoint; - break; - - case 0x2F: // End Of Track - //$thisfile_midi_raw['events'][$tracknumber][$eventid]['EOT'] = $CumulativeDeltaTime; - break; - - case 0x51: // Tempo: microseconds / quarter note - $CurrentMicroSecondsPerBeat = getid3_lib::BigEndian2Int(substr($METAeventData, 0, $METAeventLength)); - if ($CurrentMicroSecondsPerBeat == 0) { - $ThisFileInfo['error'][] = 'Corrupt MIDI file: CurrentMicroSecondsPerBeat == zero'; - return false; - } - $thisfile_midi_raw['events'][$tracknumber][$CumulativeDeltaTime]['us_qnote'] = $CurrentMicroSecondsPerBeat; - $CurrentBeatsPerMinute = (1000000 / $CurrentMicroSecondsPerBeat) * 60; - $MicroSecondsPerQuarterNoteAfter[$CumulativeDeltaTime] = $CurrentMicroSecondsPerBeat; - $TicksAtCurrentBPM = 0; - break; - - case 0x58: // Time signature - $timesig_numerator = getid3_lib::BigEndian2Int($METAeventData{0}); - $timesig_denominator = pow(2, getid3_lib::BigEndian2Int($METAeventData{1})); // $02 -> x/4, $03 -> x/8, etc - $timesig_32inqnote = getid3_lib::BigEndian2Int($METAeventData{2}); // number of 32nd notes to the quarter note - //$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_32inqnote'] = $timesig_32inqnote; - //$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_numerator'] = $timesig_numerator; - //$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_denominator'] = $timesig_denominator; - //$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_text'] = $timesig_numerator.'/'.$timesig_denominator; - $thisfile_midi['timesignature'][] = $timesig_numerator.'/'.$timesig_denominator; - break; - - case 0x59: // Keysignature - $keysig_sharpsflats = getid3_lib::BigEndian2Int($METAeventData{0}); - if ($keysig_sharpsflats & 0x80) { - // (-7 -> 7 flats, 0 ->key of C, 7 -> 7 sharps) - $keysig_sharpsflats -= 256; - } - - $keysig_majorminor = getid3_lib::BigEndian2Int($METAeventData{1}); // 0 -> major, 1 -> minor - $keysigs = array(-7=>'Cb', -6=>'Gb', -5=>'Db', -4=>'Ab', -3=>'Eb', -2=>'Bb', -1=>'F', 0=>'C', 1=>'G', 2=>'D', 3=>'A', 4=>'E', 5=>'B', 6=>'F#', 7=>'C#'); - //$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_sharps'] = (($keysig_sharpsflats > 0) ? abs($keysig_sharpsflats) : 0); - //$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_flats'] = (($keysig_sharpsflats < 0) ? abs($keysig_sharpsflats) : 0); - //$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_minor'] = (bool) $keysig_majorminor; - //$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_text'] = $keysigs[$keysig_sharpsflats].' '.($thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_minor'] ? 'minor' : 'major'); - - // $keysigs[$keysig_sharpsflats] gets an int key (correct) - $keysigs["$keysig_sharpsflats"] gets a string key (incorrect) - $thisfile_midi['keysignature'][] = $keysigs[$keysig_sharpsflats].' '.((bool) $keysig_majorminor ? 'minor' : 'major'); - break; - - case 0x7F: // Sequencer specific information - $custom_data = substr($METAeventData, 0, $METAeventLength); - break; - - default: - $ThisFileInfo['warning'][] = 'Unhandled META Event Command: '.$METAeventCommand; - break; - } - - } else { - - $ThisFileInfo['warning'][] = 'Unhandled MIDI Event ID: '.$MIDIevents[$tracknumber][$eventid]['eventid'].' + Channel ID: '.$MIDIevents[$tracknumber][$eventid]['channel']; - - } - } - if (($tracknumber > 0) || (count($trackdataarray) == 1)) { - $thisfile_midi['totalticks'] = max($thisfile_midi['totalticks'], $CumulativeDeltaTime); - } - } - $previoustickoffset = null; - - ksort($MicroSecondsPerQuarterNoteAfter); - foreach ($MicroSecondsPerQuarterNoteAfter as $tickoffset => $microsecondsperbeat) { - if (is_null($previoustickoffset)) { - $prevmicrosecondsperbeat = $microsecondsperbeat; - $previoustickoffset = $tickoffset; - continue; - } - if ($thisfile_midi['totalticks'] > $tickoffset) { - - if ($thisfile_midi_raw['ticksperqnote'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt MIDI file: ticksperqnote == zero'; - return false; - } - - $ThisFileInfo['playtime_seconds'] += (($tickoffset - $previoustickoffset) / $thisfile_midi_raw['ticksperqnote']) * ($prevmicrosecondsperbeat / 1000000); - - $prevmicrosecondsperbeat = $microsecondsperbeat; - $previoustickoffset = $tickoffset; - } - } - if ($thisfile_midi['totalticks'] > $previoustickoffset) { - - if ($thisfile_midi_raw['ticksperqnote'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt MIDI file: ticksperqnote == zero'; - return false; - } - - $ThisFileInfo['playtime_seconds'] += (($thisfile_midi['totalticks'] - $previoustickoffset) / $thisfile_midi_raw['ticksperqnote']) * ($microsecondsperbeat / 1000000); - - } - } - - if ($ThisFileInfo['playtime_seconds'] > 0) { - $ThisFileInfo['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; - } - - if (!empty($thisfile_midi['lyrics'])) { - $thisfile_midi['comments']['lyrics'][] = $thisfile_midi['lyrics']; - } - - return true; - } - - function GeneralMIDIinstrumentLookup($instrumentid) { - - $begin = __LINE__; - - /** This is not a comment! - - 0 Acoustic Grand - 1 Bright Acoustic - 2 Electric Grand - 3 Honky-Tonk - 4 Electric Piano 1 - 5 Electric Piano 2 - 6 Harpsichord - 7 Clavier - 8 Celesta - 9 Glockenspiel - 10 Music Box - 11 Vibraphone - 12 Marimba - 13 Xylophone - 14 Tubular Bells - 15 Dulcimer - 16 Drawbar Organ - 17 Percussive Organ - 18 Rock Organ - 19 Church Organ - 20 Reed Organ - 21 Accordian - 22 Harmonica - 23 Tango Accordian - 24 Acoustic Guitar (nylon) - 25 Acoustic Guitar (steel) - 26 Electric Guitar (jazz) - 27 Electric Guitar (clean) - 28 Electric Guitar (muted) - 29 Overdriven Guitar - 30 Distortion Guitar - 31 Guitar Harmonics - 32 Acoustic Bass - 33 Electric Bass (finger) - 34 Electric Bass (pick) - 35 Fretless Bass - 36 Slap Bass 1 - 37 Slap Bass 2 - 38 Synth Bass 1 - 39 Synth Bass 2 - 40 Violin - 41 Viola - 42 Cello - 43 Contrabass - 44 Tremolo Strings - 45 Pizzicato Strings - 46 Orchestral Strings - 47 Timpani - 48 String Ensemble 1 - 49 String Ensemble 2 - 50 SynthStrings 1 - 51 SynthStrings 2 - 52 Choir Aahs - 53 Voice Oohs - 54 Synth Voice - 55 Orchestra Hit - 56 Trumpet - 57 Trombone - 58 Tuba - 59 Muted Trumpet - 60 French Horn - 61 Brass Section - 62 SynthBrass 1 - 63 SynthBrass 2 - 64 Soprano Sax - 65 Alto Sax - 66 Tenor Sax - 67 Baritone Sax - 68 Oboe - 69 English Horn - 70 Bassoon - 71 Clarinet - 72 Piccolo - 73 Flute - 74 Recorder - 75 Pan Flute - 76 Blown Bottle - 77 Shakuhachi - 78 Whistle - 79 Ocarina - 80 Lead 1 (square) - 81 Lead 2 (sawtooth) - 82 Lead 3 (calliope) - 83 Lead 4 (chiff) - 84 Lead 5 (charang) - 85 Lead 6 (voice) - 86 Lead 7 (fifths) - 87 Lead 8 (bass + lead) - 88 Pad 1 (new age) - 89 Pad 2 (warm) - 90 Pad 3 (polysynth) - 91 Pad 4 (choir) - 92 Pad 5 (bowed) - 93 Pad 6 (metallic) - 94 Pad 7 (halo) - 95 Pad 8 (sweep) - 96 FX 1 (rain) - 97 FX 2 (soundtrack) - 98 FX 3 (crystal) - 99 FX 4 (atmosphere) - 100 FX 5 (brightness) - 101 FX 6 (goblins) - 102 FX 7 (echoes) - 103 FX 8 (sci-fi) - 104 Sitar - 105 Banjo - 106 Shamisen - 107 Koto - 108 Kalimba - 109 Bagpipe - 110 Fiddle - 111 Shanai - 112 Tinkle Bell - 113 Agogo - 114 Steel Drums - 115 Woodblock - 116 Taiko Drum - 117 Melodic Tom - 118 Synth Drum - 119 Reverse Cymbal - 120 Guitar Fret Noise - 121 Breath Noise - 122 Seashore - 123 Bird Tweet - 124 Telephone Ring - 125 Helicopter - 126 Applause - 127 Gunshot - - */ - - return getid3_lib::EmbeddedLookup($instrumentid, $begin, __LINE__, __FILE__, 'GeneralMIDIinstrument'); - } - - function GeneralMIDIpercussionLookup($instrumentid) { - - $begin = __LINE__; - - /** This is not a comment! - - 35 Acoustic Bass Drum - 36 Bass Drum 1 - 37 Side Stick - 38 Acoustic Snare - 39 Hand Clap - 40 Electric Snare - 41 Low Floor Tom - 42 Closed Hi-Hat - 43 High Floor Tom - 44 Pedal Hi-Hat - 45 Low Tom - 46 Open Hi-Hat - 47 Low-Mid Tom - 48 Hi-Mid Tom - 49 Crash Cymbal 1 - 50 High Tom - 51 Ride Cymbal 1 - 52 Chinese Cymbal - 53 Ride Bell - 54 Tambourine - 55 Splash Cymbal - 56 Cowbell - 57 Crash Cymbal 2 - 59 Ride Cymbal 2 - 60 Hi Bongo - 61 Low Bongo - 62 Mute Hi Conga - 63 Open Hi Conga - 64 Low Conga - 65 High Timbale - 66 Low Timbale - 67 High Agogo - 68 Low Agogo - 69 Cabasa - 70 Maracas - 71 Short Whistle - 72 Long Whistle - 73 Short Guiro - 74 Long Guiro - 75 Claves - 76 Hi Wood Block - 77 Low Wood Block - 78 Mute Cuica - 79 Open Cuica - 80 Mute Triangle - 81 Open Triangle - - */ - - return getid3_lib::EmbeddedLookup($instrumentid, $begin, __LINE__, __FILE__, 'GeneralMIDIpercussion'); - } - -} - - -?> \ No newline at end of file diff --git a/getid3/getid3/module.audio.mod.php b/getid3/getid3/module.audio.mod.php deleted file mode 100644 index 7f81ff3..0000000 --- a/getid3/getid3/module.audio.mod.php +++ /dev/null @@ -1,101 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.audio.mod.php // -// module for analyzing MOD Audio files // -// dependencies: NONE // -// /// -///////////////////////////////////////////////////////////////// - - -class getid3_mod -{ - - // new combined constructor - function getid3_mod(&$fd, &$ThisFileInfo, $option) { - - if ($option === 'mod') { - $this->getMODheaderFilepointer($fd, $ThisFileInfo); - } - elseif ($option === 'xm') { - $this->getXMheaderFilepointer($fd, $ThisFileInfo); - } - elseif ($option === 'it') { - $this->getITheaderFilepointer($fd, $ThisFileInfo); - } - elseif ($option === 's3m') { - $this->getS3MheaderFilepointer($fd, $ThisFileInfo); - } - } - - - function getMODheaderFilepointer(&$fd, &$ThisFileInfo) { - - fseek($fd, $ThisFileInfo['avdataoffset'] + 1080); - $FormatID = fread($fd, 4); - if (!ereg('^(M.K.|[5-9]CHN|[1-3][0-9]CH)$', $FormatID)) { - $ThisFileInfo['error'][] = 'This is not a known type of MOD file'; - return false; - } - - $ThisFileInfo['fileformat'] = 'mod'; - - $ThisFileInfo['error'][] = 'MOD parsing not enabled in this version of getID3()'; - return false; - } - - function getXMheaderFilepointer(&$fd, &$ThisFileInfo) { - - fseek($fd, $ThisFileInfo['avdataoffset']); - $FormatID = fread($fd, 15); - if (!ereg('^Extended Module$', $FormatID)) { - $ThisFileInfo['error'][] = 'This is not a known type of XM-MOD file'; - return false; - } - - $ThisFileInfo['fileformat'] = 'xm'; - - $ThisFileInfo['error'][] = 'XM-MOD parsing not enabled in this version of getID3()'; - return false; - } - - function getS3MheaderFilepointer(&$fd, &$ThisFileInfo) { - - fseek($fd, $ThisFileInfo['avdataoffset'] + 44); - $FormatID = fread($fd, 4); - if (!ereg('^SCRM$', $FormatID)) { - $ThisFileInfo['error'][] = 'This is not a ScreamTracker MOD file'; - return false; - } - - $ThisFileInfo['fileformat'] = 's3m'; - - $ThisFileInfo['error'][] = 'ScreamTracker parsing not enabled in this version of getID3()'; - return false; - } - - function getITheaderFilepointer(&$fd, &$ThisFileInfo) { - - fseek($fd, $ThisFileInfo['avdataoffset']); - $FormatID = fread($fd, 4); - if (!ereg('^IMPM$', $FormatID)) { - $ThisFileInfo['error'][] = 'This is not an ImpulseTracker MOD file'; - return false; - } - - $ThisFileInfo['fileformat'] = 'it'; - - $ThisFileInfo['error'][] = 'ImpulseTracker parsing not enabled in this version of getID3()'; - return false; - } - -} - - -?> \ No newline at end of file diff --git a/getid3/getid3/module.audio.monkey.php b/getid3/getid3/module.audio.monkey.php deleted file mode 100644 index 42382ad..0000000 --- a/getid3/getid3/module.audio.monkey.php +++ /dev/null @@ -1,202 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.audio.monkey.php // -// module for analyzing Monkey's Audio files // -// dependencies: NONE // -// /// -///////////////////////////////////////////////////////////////// - - -class getid3_monkey -{ - - function getid3_monkey(&$fd, &$ThisFileInfo) { - // based loosely on code from TMonkey by Jurgen Faul - // http://jfaul.de/atl or http://j-faul.virtualave.net/atl/atl.html - - $ThisFileInfo['fileformat'] = 'mac'; - $ThisFileInfo['audio']['dataformat'] = 'mac'; - $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; - $ThisFileInfo['audio']['lossless'] = true; - - $ThisFileInfo['monkeys_audio']['raw'] = array(); - $thisfile_monkeysaudio = &$ThisFileInfo['monkeys_audio']; - $thisfile_monkeysaudio_raw = &$thisfile_monkeysaudio['raw']; - - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $MACheaderData = fread($fd, 74); - - $thisfile_monkeysaudio_raw['magic'] = substr($MACheaderData, 0, 4); - if ($thisfile_monkeysaudio_raw['magic'] != 'MAC ') { - $ThisFileInfo['error'][] = 'Expecting "MAC" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$thisfile_monkeysaudio_raw['magic'].'"'; - unset($ThisFileInfo['fileformat']); - return false; - } - $thisfile_monkeysaudio_raw['nVersion'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 4, 2)); // appears to be uint32 in 3.98+ - - if ($thisfile_monkeysaudio_raw['nVersion'] < 3980) { - $thisfile_monkeysaudio_raw['nCompressionLevel'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 6, 2)); - $thisfile_monkeysaudio_raw['nFormatFlags'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 8, 2)); - $thisfile_monkeysaudio_raw['nChannels'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 10, 2)); - $thisfile_monkeysaudio_raw['nSampleRate'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 12, 4)); - $thisfile_monkeysaudio_raw['nHeaderDataBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 16, 4)); - $thisfile_monkeysaudio_raw['nWAVTerminatingBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 20, 4)); - $thisfile_monkeysaudio_raw['nTotalFrames'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 24, 4)); - $thisfile_monkeysaudio_raw['nFinalFrameSamples'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 28, 4)); - $thisfile_monkeysaudio_raw['nPeakLevel'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 32, 4)); - $thisfile_monkeysaudio_raw['nSeekElements'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 38, 2)); - $offset = 8; - } else { - $offset = 8; - // APE_DESCRIPTOR - $thisfile_monkeysaudio_raw['nDescriptorBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4)); - $offset += 4; - $thisfile_monkeysaudio_raw['nHeaderBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4)); - $offset += 4; - $thisfile_monkeysaudio_raw['nSeekTableBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4)); - $offset += 4; - $thisfile_monkeysaudio_raw['nHeaderDataBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4)); - $offset += 4; - $thisfile_monkeysaudio_raw['nAPEFrameDataBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4)); - $offset += 4; - $thisfile_monkeysaudio_raw['nAPEFrameDataBytesHigh'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4)); - $offset += 4; - $thisfile_monkeysaudio_raw['nTerminatingDataBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4)); - $offset += 4; - $thisfile_monkeysaudio_raw['cFileMD5'] = substr($MACheaderData, $offset, 16); - $offset += 16; - - // APE_HEADER - $thisfile_monkeysaudio_raw['nCompressionLevel'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 2)); - $offset += 2; - $thisfile_monkeysaudio_raw['nFormatFlags'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 2)); - $offset += 2; - $thisfile_monkeysaudio_raw['nBlocksPerFrame'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4)); - $offset += 4; - $thisfile_monkeysaudio_raw['nFinalFrameBlocks'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4)); - $offset += 4; - $thisfile_monkeysaudio_raw['nTotalFrames'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4)); - $offset += 4; - $thisfile_monkeysaudio_raw['nBitsPerSample'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 2)); - $offset += 2; - $thisfile_monkeysaudio_raw['nChannels'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 2)); - $offset += 2; - $thisfile_monkeysaudio_raw['nSampleRate'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4)); - $offset += 4; - } - - $thisfile_monkeysaudio['flags']['8-bit'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0001); - $thisfile_monkeysaudio['flags']['crc-32'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0002); - $thisfile_monkeysaudio['flags']['peak_level'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0004); - $thisfile_monkeysaudio['flags']['24-bit'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0008); - $thisfile_monkeysaudio['flags']['seek_elements'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0010); - $thisfile_monkeysaudio['flags']['no_wav_header'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0020); - $thisfile_monkeysaudio['version'] = $thisfile_monkeysaudio_raw['nVersion'] / 1000; - $thisfile_monkeysaudio['compression'] = $this->MonkeyCompressionLevelNameLookup($thisfile_monkeysaudio_raw['nCompressionLevel']); - if ($thisfile_monkeysaudio_raw['nVersion'] < 3980) { - $thisfile_monkeysaudio['samples_per_frame'] = $this->MonkeySamplesPerFrame($thisfile_monkeysaudio_raw['nVersion'], $thisfile_monkeysaudio_raw['nCompressionLevel']); - } - $thisfile_monkeysaudio['bits_per_sample'] = ($thisfile_monkeysaudio['flags']['24-bit'] ? 24 : ($thisfile_monkeysaudio['flags']['8-bit'] ? 8 : 16)); - $thisfile_monkeysaudio['channels'] = $thisfile_monkeysaudio_raw['nChannels']; - $ThisFileInfo['audio']['channels'] = $thisfile_monkeysaudio['channels']; - $thisfile_monkeysaudio['sample_rate'] = $thisfile_monkeysaudio_raw['nSampleRate']; - if ($thisfile_monkeysaudio['sample_rate'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt MAC file: frequency == zero'; - return false; - } - $ThisFileInfo['audio']['sample_rate'] = $thisfile_monkeysaudio['sample_rate']; - if ($thisfile_monkeysaudio['flags']['peak_level']) { - $thisfile_monkeysaudio['peak_level'] = $thisfile_monkeysaudio_raw['nPeakLevel']; - $thisfile_monkeysaudio['peak_ratio'] = $thisfile_monkeysaudio['peak_level'] / pow(2, $thisfile_monkeysaudio['bits_per_sample'] - 1); - } - if ($thisfile_monkeysaudio_raw['nVersion'] >= 3980) { - $thisfile_monkeysaudio['samples'] = (($thisfile_monkeysaudio_raw['nTotalFrames'] - 1) * $thisfile_monkeysaudio_raw['nBlocksPerFrame']) + $thisfile_monkeysaudio_raw['nFinalFrameBlocks']; - } else { - $thisfile_monkeysaudio['samples'] = (($thisfile_monkeysaudio_raw['nTotalFrames'] - 1) * $thisfile_monkeysaudio['samples_per_frame']) + $thisfile_monkeysaudio_raw['nFinalFrameSamples']; - } - $thisfile_monkeysaudio['playtime'] = $thisfile_monkeysaudio['samples'] / $thisfile_monkeysaudio['sample_rate']; - if ($thisfile_monkeysaudio['playtime'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt MAC file: playtime == zero'; - return false; - } - $ThisFileInfo['playtime_seconds'] = $thisfile_monkeysaudio['playtime']; - $thisfile_monkeysaudio['compressed_size'] = $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']; - $thisfile_monkeysaudio['uncompressed_size'] = $thisfile_monkeysaudio['samples'] * $thisfile_monkeysaudio['channels'] * ($thisfile_monkeysaudio['bits_per_sample'] / 8); - if ($thisfile_monkeysaudio['uncompressed_size'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt MAC file: uncompressed_size == zero'; - return false; - } - $thisfile_monkeysaudio['compression_ratio'] = $thisfile_monkeysaudio['compressed_size'] / ($thisfile_monkeysaudio['uncompressed_size'] + $thisfile_monkeysaudio_raw['nHeaderDataBytes']); - $thisfile_monkeysaudio['bitrate'] = (($thisfile_monkeysaudio['samples'] * $thisfile_monkeysaudio['channels'] * $thisfile_monkeysaudio['bits_per_sample']) / $thisfile_monkeysaudio['playtime']) * $thisfile_monkeysaudio['compression_ratio']; - $ThisFileInfo['audio']['bitrate'] = $thisfile_monkeysaudio['bitrate']; - - // add size of MAC header to avdataoffset - if ($thisfile_monkeysaudio_raw['nVersion'] >= 3980) { - $ThisFileInfo['avdataoffset'] += $thisfile_monkeysaudio_raw['nDescriptorBytes']; - $ThisFileInfo['avdataoffset'] += $thisfile_monkeysaudio_raw['nHeaderBytes']; - $ThisFileInfo['avdataoffset'] += $thisfile_monkeysaudio_raw['nSeekTableBytes']; - $ThisFileInfo['avdataoffset'] += $thisfile_monkeysaudio_raw['nHeaderDataBytes']; - - $ThisFileInfo['avdataend'] -= $thisfile_monkeysaudio_raw['nTerminatingDataBytes']; - } else { - $ThisFileInfo['avdataoffset'] += $offset; - } - - if ($thisfile_monkeysaudio_raw['nVersion'] >= 3980) { - if ($thisfile_monkeysaudio_raw['cFileMD5'] === str_repeat("\x00", 16)) { - //$ThisFileInfo['warning'][] = 'cFileMD5 is null'; - } else { - $ThisFileInfo['md5_data_source'] = ''; - $md5 = $thisfile_monkeysaudio_raw['cFileMD5']; - for ($i = 0; $i < strlen($md5); $i++) { - $ThisFileInfo['md5_data_source'] .= str_pad(dechex(ord($md5{$i})), 2, '00', STR_PAD_LEFT); - } - if (!preg_match('/^[0-9a-f]{32}$/', $ThisFileInfo['md5_data_source'])) { - unset($ThisFileInfo['md5_data_source']); - } - } - } - - - - $ThisFileInfo['audio']['bits_per_sample'] = $thisfile_monkeysaudio['bits_per_sample']; - $ThisFileInfo['audio']['encoder'] = 'MAC v'.number_format($thisfile_monkeysaudio['version'], 2); - $ThisFileInfo['audio']['encoder_options'] = ucfirst($thisfile_monkeysaudio['compression']).' compression'; - - return true; - } - - function MonkeyCompressionLevelNameLookup($compressionlevel) { - static $MonkeyCompressionLevelNameLookup = array( - 0 => 'unknown', - 1000 => 'fast', - 2000 => 'normal', - 3000 => 'high', - 4000 => 'extra-high', - 5000 => 'insane' - ); - return (isset($MonkeyCompressionLevelNameLookup[$compressionlevel]) ? $MonkeyCompressionLevelNameLookup[$compressionlevel] : 'invalid'); - } - - function MonkeySamplesPerFrame($versionid, $compressionlevel) { - if ($versionid >= 3950) { - return 73728 * 4; - } elseif ($versionid >= 3900) { - return 73728; - } elseif (($versionid >= 3800) && ($compressionlevel == 4000)) { - return 73728; - } else { - return 9216; - } - } - -} - -?> \ No newline at end of file diff --git a/getid3/getid3/module.audio.mp3.php b/getid3/getid3/module.audio.mp3.php deleted file mode 100644 index baff838..0000000 --- a/getid3/getid3/module.audio.mp3.php +++ /dev/null @@ -1,1937 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.audio.mp3.php // -// module for analyzing MP3 files // -// dependencies: NONE // -// /// -///////////////////////////////////////////////////////////////// - - -// number of frames to scan to determine if MPEG-audio sequence is valid -// Lower this number to 5-20 for faster scanning -// Increase this number to 50+ for most accurate detection of valid VBR/CBR -// mpeg-audio streams -define('GETID3_MP3_VALID_CHECK_FRAMES', 35); - - -class getid3_mp3 -{ - - var $allow_bruteforce = false; // forces getID3() to scan the file byte-by-byte and log all the valid audio frame headers - extremely slow, unrecommended, but may provide data from otherwise-unusuable files - - function getid3_mp3(&$fd, &$ThisFileInfo) { - - if (!$this->getOnlyMPEGaudioInfo($fd, $ThisFileInfo, $ThisFileInfo['avdataoffset'])) { - if ($this->allow_bruteforce) { - $ThisFileInfo['error'][] = 'Rescanning file in BruteForce mode'; - $this->getOnlyMPEGaudioInfoBruteForce($fd, $ThisFileInfo); - } - } - - - if (isset($ThisFileInfo['mpeg']['audio']['bitrate_mode'])) { - $ThisFileInfo['audio']['bitrate_mode'] = strtolower($ThisFileInfo['mpeg']['audio']['bitrate_mode']); - } - - if (((isset($ThisFileInfo['id3v2']['headerlength']) && ($ThisFileInfo['avdataoffset'] > $ThisFileInfo['id3v2']['headerlength'])) || (!isset($ThisFileInfo['id3v2']) && ($ThisFileInfo['avdataoffset'] > 0)))) { - - $synchoffsetwarning = 'Unknown data before synch '; - if (isset($ThisFileInfo['id3v2']['headerlength'])) { - $synchoffsetwarning .= '(ID3v2 header ends at '.$ThisFileInfo['id3v2']['headerlength'].', then '.($ThisFileInfo['avdataoffset'] - $ThisFileInfo['id3v2']['headerlength']).' bytes garbage, '; - } else { - $synchoffsetwarning .= '(should be at beginning of file, '; - } - $synchoffsetwarning .= 'synch detected at '.$ThisFileInfo['avdataoffset'].')'; - if ($ThisFileInfo['audio']['bitrate_mode'] == 'cbr') { - - if (!empty($ThisFileInfo['id3v2']['headerlength']) && (($ThisFileInfo['avdataoffset'] - $ThisFileInfo['id3v2']['headerlength']) == $ThisFileInfo['mpeg']['audio']['framelength'])) { - - $synchoffsetwarning .= '. This is a known problem with some versions of LAME (3.90-3.92) DLL in CBR mode.'; - $ThisFileInfo['audio']['codec'] = 'LAME'; - $CurrentDataLAMEversionString = 'LAME3.'; - - } elseif (empty($ThisFileInfo['id3v2']['headerlength']) && ($ThisFileInfo['avdataoffset'] == $ThisFileInfo['mpeg']['audio']['framelength'])) { - - $synchoffsetwarning .= '. This is a known problem with some versions of LAME (3.90 - 3.92) DLL in CBR mode.'; - $ThisFileInfo['audio']['codec'] = 'LAME'; - $CurrentDataLAMEversionString = 'LAME3.'; - - } - - } - $ThisFileInfo['warning'][] = $synchoffsetwarning; - - } - - if (isset($ThisFileInfo['mpeg']['audio']['LAME'])) { - $ThisFileInfo['audio']['codec'] = 'LAME'; - if (!empty($ThisFileInfo['mpeg']['audio']['LAME']['long_version'])) { - $ThisFileInfo['audio']['encoder'] = rtrim($ThisFileInfo['mpeg']['audio']['LAME']['long_version'], "\x00"); - } elseif (!empty($ThisFileInfo['mpeg']['audio']['LAME']['short_version'])) { - $ThisFileInfo['audio']['encoder'] = rtrim($ThisFileInfo['mpeg']['audio']['LAME']['short_version'], "\x00"); - } - } - - $CurrentDataLAMEversionString = (!empty($CurrentDataLAMEversionString) ? $CurrentDataLAMEversionString : @$ThisFileInfo['audio']['encoder']); - if (!empty($CurrentDataLAMEversionString) && (substr($CurrentDataLAMEversionString, 0, 6) == 'LAME3.') && !preg_match('[0-9\)]', substr($CurrentDataLAMEversionString, -1))) { - // a version number of LAME that does not end with a number like "LAME3.92" - // or with a closing parenthesis like "LAME3.88 (alpha)" - // or a version of LAME with the LAMEtag-not-filled-in-DLL-mode bug (3.90-3.92) - - // not sure what the actual last frame length will be, but will be less than or equal to 1441 - $PossiblyLongerLAMEversion_FrameLength = 1441; - - // Not sure what version of LAME this is - look in padding of last frame for longer version string - $PossibleLAMEversionStringOffset = $ThisFileInfo['avdataend'] - $PossiblyLongerLAMEversion_FrameLength; - fseek($fd, $PossibleLAMEversionStringOffset); - $PossiblyLongerLAMEversion_Data = fread($fd, $PossiblyLongerLAMEversion_FrameLength); - switch (substr($CurrentDataLAMEversionString, -1)) { - case 'a': - case 'b': - // "LAME3.94a" will have a longer version string of "LAME3.94 (alpha)" for example - // need to trim off "a" to match longer string - $CurrentDataLAMEversionString = substr($CurrentDataLAMEversionString, 0, -1); - break; - } - if (($PossiblyLongerLAMEversion_String = strstr($PossiblyLongerLAMEversion_Data, $CurrentDataLAMEversionString)) !== false) { - if (substr($PossiblyLongerLAMEversion_String, 0, strlen($CurrentDataLAMEversionString)) == $CurrentDataLAMEversionString) { - $PossiblyLongerLAMEversion_NewString = substr($PossiblyLongerLAMEversion_String, 0, strspn($PossiblyLongerLAMEversion_String, 'LAME0123456789., (abcdefghijklmnopqrstuvwxyzJFSOND)')); //"LAME3.90.3" "LAME3.87 (beta 1, Sep 27 2000)" "LAME3.88 (beta)" - if (strlen($PossiblyLongerLAMEversion_NewString) > strlen(@$ThisFileInfo['audio']['encoder'])) { - $ThisFileInfo['audio']['encoder'] = $PossiblyLongerLAMEversion_NewString; - } - } - } - } - if (!empty($ThisFileInfo['audio']['encoder'])) { - $ThisFileInfo['audio']['encoder'] = rtrim($ThisFileInfo['audio']['encoder'], "\x00 "); - } - - switch (@$ThisFileInfo['mpeg']['audio']['layer']) { - case 1: - case 2: - $ThisFileInfo['audio']['dataformat'] = 'mp'.$ThisFileInfo['mpeg']['audio']['layer']; - break; - } - if ($ThisFileInfo['fileformat'] == 'mp3') { - switch ($ThisFileInfo['audio']['dataformat']) { - case 'mp1': - case 'mp2': - case 'mp3': - $ThisFileInfo['fileformat'] = $ThisFileInfo['audio']['dataformat']; - break; - - default: - $ThisFileInfo['warning'][] = 'Expecting [audio][dataformat] to be mp1/mp2/mp3 when fileformat == mp3, [audio][dataformat] actually "'.$ThisFileInfo['audio']['dataformat'].'"'; - break; - } - } - - if (empty($ThisFileInfo['fileformat'])) { - unset($ThisFileInfo['fileformat']); - unset($ThisFileInfo['audio']['bitrate_mode']); - unset($ThisFileInfo['avdataoffset']); - unset($ThisFileInfo['avdataend']); - return false; - } - - $ThisFileInfo['mime_type'] = 'audio/mpeg'; - $ThisFileInfo['audio']['lossless'] = false; - - // Calculate playtime - if (!isset($ThisFileInfo['playtime_seconds']) && isset($ThisFileInfo['audio']['bitrate']) && ($ThisFileInfo['audio']['bitrate'] > 0)) { - $ThisFileInfo['playtime_seconds'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / $ThisFileInfo['audio']['bitrate']; - } - - $ThisFileInfo['audio']['encoder_options'] = $this->GuessEncoderOptions($ThisFileInfo); - - return true; - } - - - function GuessEncoderOptions(&$ThisFileInfo) { - // shortcuts - if (!empty($ThisFileInfo['mpeg']['audio'])) { - $thisfile_mpeg_audio = &$ThisFileInfo['mpeg']['audio']; - if (!empty($thisfile_mpeg_audio['LAME'])) { - $thisfile_mpeg_audio_lame = &$thisfile_mpeg_audio['LAME']; - } - } - - $encoder_options = ''; - static $NamedPresetBitrates = array(16, 24, 40, 56, 112, 128, 160, 192, 256); - - if ((@$thisfile_mpeg_audio['VBR_method'] == 'Fraunhofer') && !empty($thisfile_mpeg_audio['VBR_quality'])) { - - $encoder_options = 'VBR q'.$thisfile_mpeg_audio['VBR_quality']; - - } elseif (!empty($thisfile_mpeg_audio_lame['preset_used']) && (!in_array($thisfile_mpeg_audio_lame['preset_used_id'], $NamedPresetBitrates))) { - - $encoder_options = $thisfile_mpeg_audio_lame['preset_used']; - - } elseif (!empty($thisfile_mpeg_audio_lame['vbr_quality'])) { - - static $KnownEncoderValues = array(); - if (empty($KnownEncoderValues)) { - - //$KnownEncoderValues[abrbitrate_minbitrate][vbr_quality][raw_vbr_method][raw_noise_shaping][raw_stereo_mode][ath_type][lowpass_frequency] = 'preset name'; - $KnownEncoderValues[0xFF][58][1][1][3][2][20500] = '--alt-preset insane'; // 3.90, 3.90.1, 3.92 - $KnownEncoderValues[0xFF][58][1][1][3][2][20600] = '--alt-preset insane'; // 3.90.2, 3.90.3, 3.91 - $KnownEncoderValues[0xFF][57][1][1][3][4][20500] = '--alt-preset insane'; // 3.94, 3.95 - $KnownEncoderValues['**'][78][3][2][3][2][19500] = '--alt-preset extreme'; // 3.90, 3.90.1, 3.92 - $KnownEncoderValues['**'][78][3][2][3][2][19600] = '--alt-preset extreme'; // 3.90.2, 3.91 - $KnownEncoderValues['**'][78][3][1][3][2][19600] = '--alt-preset extreme'; // 3.90.3 - $KnownEncoderValues['**'][78][4][2][3][2][19500] = '--alt-preset fast extreme'; // 3.90, 3.90.1, 3.92 - $KnownEncoderValues['**'][78][4][2][3][2][19600] = '--alt-preset fast extreme'; // 3.90.2, 3.90.3, 3.91 - $KnownEncoderValues['**'][78][3][2][3][4][19000] = '--alt-preset standard'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 - $KnownEncoderValues['**'][78][3][1][3][4][19000] = '--alt-preset standard'; // 3.90.3 - $KnownEncoderValues['**'][78][4][2][3][4][19000] = '--alt-preset fast standard'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 - $KnownEncoderValues['**'][78][4][1][3][4][19000] = '--alt-preset fast standard'; // 3.90.3 - $KnownEncoderValues['**'][88][4][1][3][3][19500] = '--r3mix'; // 3.90, 3.90.1, 3.92 - $KnownEncoderValues['**'][88][4][1][3][3][19600] = '--r3mix'; // 3.90.2, 3.90.3, 3.91 - $KnownEncoderValues['**'][67][4][1][3][4][18000] = '--r3mix'; // 3.94, 3.95 - $KnownEncoderValues['**'][68][3][2][3][4][18000] = '--alt-preset medium'; // 3.90.3 - $KnownEncoderValues['**'][68][4][2][3][4][18000] = '--alt-preset fast medium'; // 3.90.3 - - $KnownEncoderValues[0xFF][99][1][1][1][2][0] = '--preset studio'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 - $KnownEncoderValues[0xFF][58][2][1][3][2][20600] = '--preset studio'; // 3.90.3, 3.93.1 - $KnownEncoderValues[0xFF][58][2][1][3][2][20500] = '--preset studio'; // 3.93 - $KnownEncoderValues[0xFF][57][2][1][3][4][20500] = '--preset studio'; // 3.94, 3.95 - $KnownEncoderValues[0xC0][88][1][1][1][2][0] = '--preset cd'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 - $KnownEncoderValues[0xC0][58][2][2][3][2][19600] = '--preset cd'; // 3.90.3, 3.93.1 - $KnownEncoderValues[0xC0][58][2][2][3][2][19500] = '--preset cd'; // 3.93 - $KnownEncoderValues[0xC0][57][2][1][3][4][19500] = '--preset cd'; // 3.94, 3.95 - $KnownEncoderValues[0xA0][78][1][1][3][2][18000] = '--preset hifi'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 - $KnownEncoderValues[0xA0][58][2][2][3][2][18000] = '--preset hifi'; // 3.90.3, 3.93, 3.93.1 - $KnownEncoderValues[0xA0][57][2][1][3][4][18000] = '--preset hifi'; // 3.94, 3.95 - $KnownEncoderValues[0x80][67][1][1][3][2][18000] = '--preset tape'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 - $KnownEncoderValues[0x80][67][1][1][3][2][15000] = '--preset radio'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 - $KnownEncoderValues[0x70][67][1][1][3][2][15000] = '--preset fm'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 - $KnownEncoderValues[0x70][58][2][2][3][2][16000] = '--preset tape/radio/fm'; // 3.90.3, 3.93, 3.93.1 - $KnownEncoderValues[0x70][57][2][1][3][4][16000] = '--preset tape/radio/fm'; // 3.94, 3.95 - $KnownEncoderValues[0x38][58][2][2][0][2][10000] = '--preset voice'; // 3.90.3, 3.93, 3.93.1 - $KnownEncoderValues[0x38][57][2][1][0][4][15000] = '--preset voice'; // 3.94, 3.95 - $KnownEncoderValues[0x38][57][2][1][0][4][16000] = '--preset voice'; // 3.94a14 - $KnownEncoderValues[0x28][65][1][1][0][2][7500] = '--preset mw-us'; // 3.90, 3.90.1, 3.92 - $KnownEncoderValues[0x28][65][1][1][0][2][7600] = '--preset mw-us'; // 3.90.2, 3.91 - $KnownEncoderValues[0x28][58][2][2][0][2][7000] = '--preset mw-us'; // 3.90.3, 3.93, 3.93.1 - $KnownEncoderValues[0x28][57][2][1][0][4][10500] = '--preset mw-us'; // 3.94, 3.95 - $KnownEncoderValues[0x28][57][2][1][0][4][11200] = '--preset mw-us'; // 3.94a14 - $KnownEncoderValues[0x28][57][2][1][0][4][8800] = '--preset mw-us'; // 3.94a15 - $KnownEncoderValues[0x18][58][2][2][0][2][4000] = '--preset phon+/lw/mw-eu/sw'; // 3.90.3, 3.93.1 - $KnownEncoderValues[0x18][58][2][2][0][2][3900] = '--preset phon+/lw/mw-eu/sw'; // 3.93 - $KnownEncoderValues[0x18][57][2][1][0][4][5900] = '--preset phon+/lw/mw-eu/sw'; // 3.94, 3.95 - $KnownEncoderValues[0x18][57][2][1][0][4][6200] = '--preset phon+/lw/mw-eu/sw'; // 3.94a14 - $KnownEncoderValues[0x18][57][2][1][0][4][3200] = '--preset phon+/lw/mw-eu/sw'; // 3.94a15 - $KnownEncoderValues[0x10][58][2][2][0][2][3800] = '--preset phone'; // 3.90.3, 3.93.1 - $KnownEncoderValues[0x10][58][2][2][0][2][3700] = '--preset phone'; // 3.93 - $KnownEncoderValues[0x10][57][2][1][0][4][5600] = '--preset phone'; // 3.94, 3.95 - } - - if (isset($KnownEncoderValues[$thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']])) { - - $encoder_options = $KnownEncoderValues[$thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']]; - - } elseif (isset($KnownEncoderValues['**'][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']])) { - - $encoder_options = $KnownEncoderValues['**'][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']]; - - } elseif ($ThisFileInfo['audio']['bitrate_mode'] == 'vbr') { - - // http://gabriel.mp3-tech.org/mp3infotag.html - // int Quality = (100 - 10 * gfp->VBR_q - gfp->quality)h - - - $LAME_V_value = 10 - ceil($thisfile_mpeg_audio_lame['vbr_quality'] / 10); - $LAME_q_value = 100 - $thisfile_mpeg_audio_lame['vbr_quality'] - ($LAME_V_value * 10); - $encoder_options = '-V'.$LAME_V_value.' -q'.$LAME_q_value; - - } elseif ($ThisFileInfo['audio']['bitrate_mode'] == 'cbr') { - - $encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']).ceil($ThisFileInfo['audio']['bitrate'] / 1000); - - } else { - - $encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']); - - } - - } elseif (!empty($thisfile_mpeg_audio_lame['bitrate_abr'])) { - - $encoder_options = 'ABR'.$thisfile_mpeg_audio_lame['bitrate_abr']; - - } elseif (!empty($ThisFileInfo['audio']['bitrate'])) { - - if ($ThisFileInfo['audio']['bitrate_mode'] == 'cbr') { - $encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']).ceil($ThisFileInfo['audio']['bitrate'] / 1000); - } else { - $encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']); - } - - } - if (!empty($thisfile_mpeg_audio_lame['bitrate_min'])) { - $encoder_options .= ' -b'.$thisfile_mpeg_audio_lame['bitrate_min']; - } - - if (@$thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev'] || @$thisfile_mpeg_audio_lame['encoding_flags']['nogap_next']) { - $encoder_options .= ' --nogap'; - } - - if (!empty($thisfile_mpeg_audio_lame['lowpass_frequency'])) { - $ExplodedOptions = explode(' ', $encoder_options, 4); - if ($ExplodedOptions[0] == '--r3mix') { - $ExplodedOptions[1] = 'r3mix'; - } - switch ($ExplodedOptions[0]) { - case '--preset': - case '--alt-preset': - case '--r3mix': - if ($ExplodedOptions[1] == 'fast') { - $ExplodedOptions[1] .= ' '.$ExplodedOptions[2]; - } - switch ($ExplodedOptions[1]) { - case 'portable': - case 'medium': - case 'standard': - case 'extreme': - case 'insane': - case 'fast portable': - case 'fast medium': - case 'fast standard': - case 'fast extreme': - case 'fast insane': - case 'r3mix': - static $ExpectedLowpass = array( - 'insane|20500' => 20500, - 'insane|20600' => 20600, // 3.90.2, 3.90.3, 3.91 - 'medium|18000' => 18000, - 'fast medium|18000' => 18000, - 'extreme|19500' => 19500, // 3.90, 3.90.1, 3.92, 3.95 - 'extreme|19600' => 19600, // 3.90.2, 3.90.3, 3.91, 3.93.1 - 'fast extreme|19500' => 19500, // 3.90, 3.90.1, 3.92, 3.95 - 'fast extreme|19600' => 19600, // 3.90.2, 3.90.3, 3.91, 3.93.1 - 'standard|19000' => 19000, - 'fast standard|19000' => 19000, - 'r3mix|19500' => 19500, // 3.90, 3.90.1, 3.92 - 'r3mix|19600' => 19600, // 3.90.2, 3.90.3, 3.91 - 'r3mix|18000' => 18000, // 3.94, 3.95 - ); - if (!isset($ExpectedLowpass[$ExplodedOptions[1].'|'.$thisfile_mpeg_audio_lame['lowpass_frequency']]) && ($thisfile_mpeg_audio_lame['lowpass_frequency'] < 22050) && (round($thisfile_mpeg_audio_lame['lowpass_frequency'] / 1000) < round($thisfile_mpeg_audio['sample_rate'] / 2000))) { - $encoder_options .= ' --lowpass '.$thisfile_mpeg_audio_lame['lowpass_frequency']; - } - break; - - default: - break; - } - break; - } - } - - if (isset($thisfile_mpeg_audio_lame['raw']['source_sample_freq'])) { - if (($thisfile_mpeg_audio['sample_rate'] == 44100) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 1)) { - $encoder_options .= ' --resample 44100'; - } elseif (($thisfile_mpeg_audio['sample_rate'] == 48000) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 2)) { - $encoder_options .= ' --resample 48000'; - } elseif ($thisfile_mpeg_audio['sample_rate'] < 44100) { - switch ($thisfile_mpeg_audio_lame['raw']['source_sample_freq']) { - case 0: // <= 32000 - // may or may not be same as source frequency - ignore - break; - case 1: // 44100 - case 2: // 48000 - case 3: // 48000+ - $ExplodedOptions = explode(' ', $encoder_options, 4); - switch ($ExplodedOptions[0]) { - case '--preset': - case '--alt-preset': - switch ($ExplodedOptions[1]) { - case 'fast': - case 'portable': - case 'medium': - case 'standard': - case 'extreme': - case 'insane': - $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate']; - break; - - default: - static $ExpectedResampledRate = array( - 'phon+/lw/mw-eu/sw|16000' => 16000, - 'mw-us|24000' => 24000, // 3.95 - 'mw-us|32000' => 32000, // 3.93 - 'mw-us|16000' => 16000, // 3.92 - 'phone|16000' => 16000, - 'phone|11025' => 11025, // 3.94a15 - 'radio|32000' => 32000, // 3.94a15 - 'fm/radio|32000' => 32000, // 3.92 - 'fm|32000' => 32000, // 3.90 - 'voice|32000' => 32000); - if (!isset($ExpectedResampledRate[$ExplodedOptions[1].'|'.$thisfile_mpeg_audio['sample_rate']])) { - $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate']; - } - break; - } - break; - - case '--r3mix': - default: - $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate']; - break; - } - break; - } - } - } - if (empty($encoder_options) && !empty($ThisFileInfo['audio']['bitrate']) && !empty($ThisFileInfo['audio']['bitrate_mode'])) { - //$encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']).ceil($ThisFileInfo['audio']['bitrate'] / 1000); - $encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']); - } - - return $encoder_options; - } - - - function decodeMPEGaudioHeader($fd, $offset, &$ThisFileInfo, $recursivesearch=true, $ScanAsCBR=false, $FastMPEGheaderScan=false) { - - static $MPEGaudioVersionLookup; - static $MPEGaudioLayerLookup; - static $MPEGaudioBitrateLookup; - static $MPEGaudioFrequencyLookup; - static $MPEGaudioChannelModeLookup; - static $MPEGaudioModeExtensionLookup; - static $MPEGaudioEmphasisLookup; - if (empty($MPEGaudioVersionLookup)) { - $MPEGaudioVersionLookup = getid3_mp3::MPEGaudioVersionArray(); - $MPEGaudioLayerLookup = getid3_mp3::MPEGaudioLayerArray(); - $MPEGaudioBitrateLookup = getid3_mp3::MPEGaudioBitrateArray(); - $MPEGaudioFrequencyLookup = getid3_mp3::MPEGaudioFrequencyArray(); - $MPEGaudioChannelModeLookup = getid3_mp3::MPEGaudioChannelModeArray(); - $MPEGaudioModeExtensionLookup = getid3_mp3::MPEGaudioModeExtensionArray(); - $MPEGaudioEmphasisLookup = getid3_mp3::MPEGaudioEmphasisArray(); - } - - if ($offset >= $ThisFileInfo['avdataend']) { - $ThisFileInfo['error'][] = 'end of file encounter looking for MPEG synch'; - return false; - } - fseek($fd, $offset, SEEK_SET); - //$headerstring = fread($fd, 1441); // worst-case max length = 32kHz @ 320kbps layer 3 = 1441 bytes/frame - $headerstring = fread($fd, 226); // LAME header at offset 36 + 190 bytes of Xing/LAME data - - // MP3 audio frame structure: - // $aa $aa $aa $aa [$bb $bb] $cc... - // where $aa..$aa is the four-byte mpeg-audio header (below) - // $bb $bb is the optional 2-byte CRC - // and $cc... is the audio data - - $head4 = substr($headerstring, 0, 4); - - static $MPEGaudioHeaderDecodeCache = array(); - if (isset($MPEGaudioHeaderDecodeCache[$head4])) { - $MPEGheaderRawArray = $MPEGaudioHeaderDecodeCache[$head4]; - } else { - $MPEGheaderRawArray = getid3_mp3::MPEGaudioHeaderDecode($head4); - $MPEGaudioHeaderDecodeCache[$head4] = $MPEGheaderRawArray; - } - - static $MPEGaudioHeaderValidCache = array(); - - // Not in cache - if (!isset($MPEGaudioHeaderValidCache[$head4])) { - //$MPEGaudioHeaderValidCache[$head4] = getid3_mp3::MPEGaudioHeaderValid($MPEGheaderRawArray, false, true); // allow badly-formatted freeformat (from LAME 3.90 - 3.93.1) - $MPEGaudioHeaderValidCache[$head4] = getid3_mp3::MPEGaudioHeaderValid($MPEGheaderRawArray, false, false); - } - - // shortcut - if (!isset($ThisFileInfo['mpeg']['audio'])) { - $ThisFileInfo['mpeg']['audio'] = array(); - } - $thisfile_mpeg_audio = &$ThisFileInfo['mpeg']['audio']; - - - if ($MPEGaudioHeaderValidCache[$head4]) { - $thisfile_mpeg_audio['raw'] = $MPEGheaderRawArray; - } else { - $ThisFileInfo['error'][] = 'Invalid MPEG audio header at offset '.$offset; - return false; - } - - if (!$FastMPEGheaderScan) { - - $thisfile_mpeg_audio['version'] = $MPEGaudioVersionLookup[$thisfile_mpeg_audio['raw']['version']]; - $thisfile_mpeg_audio['layer'] = $MPEGaudioLayerLookup[$thisfile_mpeg_audio['raw']['layer']]; - - $thisfile_mpeg_audio['channelmode'] = $MPEGaudioChannelModeLookup[$thisfile_mpeg_audio['raw']['channelmode']]; - $thisfile_mpeg_audio['channels'] = (($thisfile_mpeg_audio['channelmode'] == 'mono') ? 1 : 2); - $thisfile_mpeg_audio['sample_rate'] = $MPEGaudioFrequencyLookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['raw']['sample_rate']]; - $thisfile_mpeg_audio['protection'] = !$thisfile_mpeg_audio['raw']['protection']; - $thisfile_mpeg_audio['private'] = (bool) $thisfile_mpeg_audio['raw']['private']; - $thisfile_mpeg_audio['modeextension'] = $MPEGaudioModeExtensionLookup[$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['modeextension']]; - $thisfile_mpeg_audio['copyright'] = (bool) $thisfile_mpeg_audio['raw']['copyright']; - $thisfile_mpeg_audio['original'] = (bool) $thisfile_mpeg_audio['raw']['original']; - $thisfile_mpeg_audio['emphasis'] = $MPEGaudioEmphasisLookup[$thisfile_mpeg_audio['raw']['emphasis']]; - - $ThisFileInfo['audio']['channels'] = $thisfile_mpeg_audio['channels']; - $ThisFileInfo['audio']['sample_rate'] = $thisfile_mpeg_audio['sample_rate']; - - if ($thisfile_mpeg_audio['protection']) { - $thisfile_mpeg_audio['crc'] = getid3_lib::BigEndian2Int(substr($headerstring, 4, 2)); - } - - } - - if ($thisfile_mpeg_audio['raw']['bitrate'] == 15) { - // http://www.hydrogenaudio.org/?act=ST&f=16&t=9682&st=0 - $ThisFileInfo['warning'][] = 'Invalid bitrate index (15), this is a known bug in free-format MP3s encoded by LAME v3.90 - 3.93.1'; - $thisfile_mpeg_audio['raw']['bitrate'] = 0; - } - $thisfile_mpeg_audio['padding'] = (bool) $thisfile_mpeg_audio['raw']['padding']; - $thisfile_mpeg_audio['bitrate'] = $MPEGaudioBitrateLookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['bitrate']]; - - if (($thisfile_mpeg_audio['bitrate'] == 'free') && ($offset == $ThisFileInfo['avdataoffset'])) { - // only skip multiple frame check if free-format bitstream found at beginning of file - // otherwise is quite possibly simply corrupted data - $recursivesearch = false; - } - - // For Layer 2 there are some combinations of bitrate and mode which are not allowed. - if (!$FastMPEGheaderScan && ($thisfile_mpeg_audio['layer'] == '2')) { - - $ThisFileInfo['audio']['dataformat'] = 'mp2'; - switch ($thisfile_mpeg_audio['channelmode']) { - - case 'mono': - if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] <= 192000)) { - // these are ok - } else { - $ThisFileInfo['error'][] = $thisfile_mpeg_audio['bitrate'].'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.'; - return false; - } - break; - - case 'stereo': - case 'joint stereo': - case 'dual channel': - if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] == 64000) || ($thisfile_mpeg_audio['bitrate'] >= 96000)) { - // these are ok - } else { - $ThisFileInfo['error'][] = intval(round($thisfile_mpeg_audio['bitrate'] / 1000)).'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.'; - return false; - } - break; - - } - - } - - - if ($ThisFileInfo['audio']['sample_rate'] > 0) { - $thisfile_mpeg_audio['framelength'] = getid3_mp3::MPEGaudioFrameLength($thisfile_mpeg_audio['bitrate'], $thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['layer'], (int) $thisfile_mpeg_audio['padding'], $ThisFileInfo['audio']['sample_rate']); - } - - $nextframetestoffset = $offset + 1; - if ($thisfile_mpeg_audio['bitrate'] != 'free') { - - $ThisFileInfo['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate']; - - if (isset($thisfile_mpeg_audio['framelength'])) { - $nextframetestoffset = $offset + $thisfile_mpeg_audio['framelength']; - } else { - $ThisFileInfo['error'][] = 'Frame at offset('.$offset.') is has an invalid frame length.'; - return false; - } - - } - - $ExpectedNumberOfAudioBytes = 0; - - //////////////////////////////////////////////////////////////////////////////////// - // Variable-bitrate headers - - if (substr($headerstring, 4 + 32, 4) == 'VBRI') { - // Fraunhofer VBR header is hardcoded 'VBRI' at offset 0x24 (36) - // specs taken from http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html - - $thisfile_mpeg_audio['bitrate_mode'] = 'vbr'; - $thisfile_mpeg_audio['VBR_method'] = 'Fraunhofer'; - $ThisFileInfo['audio']['codec'] = 'Fraunhofer'; - - $SideInfoData = substr($headerstring, 4 + 2, 32); - - $FraunhoferVBROffset = 36; - - $thisfile_mpeg_audio['VBR_encoder_version'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 4, 2)); // VbriVersion - $thisfile_mpeg_audio['VBR_encoder_delay'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 6, 2)); // VbriDelay - $thisfile_mpeg_audio['VBR_quality'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 8, 2)); // VbriQuality - $thisfile_mpeg_audio['VBR_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 10, 4)); // VbriStreamBytes - $thisfile_mpeg_audio['VBR_frames'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 14, 4)); // VbriStreamFrames - $thisfile_mpeg_audio['VBR_seek_offsets'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 18, 2)); // VbriTableSize - $thisfile_mpeg_audio['VBR_seek_scale'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 20, 2)); // VbriTableScale - $thisfile_mpeg_audio['VBR_entry_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 22, 2)); // VbriEntryBytes - $thisfile_mpeg_audio['VBR_entry_frames'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 24, 2)); // VbriEntryFrames - - $ExpectedNumberOfAudioBytes = $thisfile_mpeg_audio['VBR_bytes']; - - $previousbyteoffset = $offset; - for ($i = 0; $i < $thisfile_mpeg_audio['VBR_seek_offsets']; $i++) { - $Fraunhofer_OffsetN = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset, $thisfile_mpeg_audio['VBR_entry_bytes'])); - $FraunhoferVBROffset += $thisfile_mpeg_audio['VBR_entry_bytes']; - $thisfile_mpeg_audio['VBR_offsets_relative'][$i] = ($Fraunhofer_OffsetN * $thisfile_mpeg_audio['VBR_seek_scale']); - $thisfile_mpeg_audio['VBR_offsets_absolute'][$i] = ($Fraunhofer_OffsetN * $thisfile_mpeg_audio['VBR_seek_scale']) + $previousbyteoffset; - $previousbyteoffset += $Fraunhofer_OffsetN; - } - - - } else { - - // Xing VBR header is hardcoded 'Xing' at a offset 0x0D (13), 0x15 (21) or 0x24 (36) - // depending on MPEG layer and number of channels - - $VBRidOffset = getid3_mp3::XingVBRidOffset($thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['channelmode']); - $SideInfoData = substr($headerstring, 4 + 2, $VBRidOffset - 4); - - if ((substr($headerstring, $VBRidOffset, strlen('Xing')) == 'Xing') || (substr($headerstring, $VBRidOffset, strlen('Info')) == 'Info')) { - // 'Xing' is traditional Xing VBR frame - // 'Info' is LAME-encoded CBR (This was done to avoid CBR files to be recognized as traditional Xing VBR files by some decoders.) - // 'Info' *can* legally be used to specify a VBR file as well, however. - - // http://www.multiweb.cz/twoinches/MP3inside.htm - //00..03 = "Xing" or "Info" - //04..07 = Flags: - // 0x01 Frames Flag set if value for number of frames in file is stored - // 0x02 Bytes Flag set if value for filesize in bytes is stored - // 0x04 TOC Flag set if values for TOC are stored - // 0x08 VBR Scale Flag set if values for VBR scale is stored - //08..11 Frames: Number of frames in file (including the first Xing/Info one) - //12..15 Bytes: File length in Bytes - //16..115 TOC (Table of Contents): - // Contains of 100 indexes (one Byte length) for easier lookup in file. Approximately solves problem with moving inside file. - // Each Byte has a value according this formula: - // (TOC[i] / 256) * fileLenInBytes - // So if song lasts eg. 240 sec. and you want to jump to 60. sec. (and file is 5 000 000 Bytes length) you can use: - // TOC[(60/240)*100] = TOC[25] - // and corresponding Byte in file is then approximately at: - // (TOC[25]/256) * 5000000 - //116..119 VBR Scale - - - // should be safe to leave this at 'vbr' and let it be overriden to 'cbr' if a CBR preset/mode is used by LAME -// if (substr($headerstring, $VBRidOffset, strlen('Info')) == 'Xing') { - $thisfile_mpeg_audio['bitrate_mode'] = 'vbr'; - $thisfile_mpeg_audio['VBR_method'] = 'Xing'; -// } else { -// $ScanAsCBR = true; -// $thisfile_mpeg_audio['bitrate_mode'] = 'cbr'; -// } - - $thisfile_mpeg_audio['xing_flags_raw'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 4, 4)); - - $thisfile_mpeg_audio['xing_flags']['frames'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000001); - $thisfile_mpeg_audio['xing_flags']['bytes'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000002); - $thisfile_mpeg_audio['xing_flags']['toc'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000004); - $thisfile_mpeg_audio['xing_flags']['vbr_scale'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000008); - - if ($thisfile_mpeg_audio['xing_flags']['frames']) { - $thisfile_mpeg_audio['VBR_frames'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 8, 4)); - //$thisfile_mpeg_audio['VBR_frames']--; // don't count header Xing/Info frame - } - if ($thisfile_mpeg_audio['xing_flags']['bytes']) { - $thisfile_mpeg_audio['VBR_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 12, 4)); - } - - //if (($thisfile_mpeg_audio['bitrate'] == 'free') && !empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) { - if (!empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) { - - $framelengthfloat = $thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']; - - if ($thisfile_mpeg_audio['layer'] == '1') { - // BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12 - //$ThisFileInfo['audio']['bitrate'] = ((($framelengthfloat / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12; - $ThisFileInfo['audio']['bitrate'] = ($framelengthfloat / 4) * $thisfile_mpeg_audio['sample_rate'] * (2 / $ThisFileInfo['audio']['channels']) / 12; - } else { - // Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144 - //$ThisFileInfo['audio']['bitrate'] = (($framelengthfloat - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144; - $ThisFileInfo['audio']['bitrate'] = $framelengthfloat * $thisfile_mpeg_audio['sample_rate'] * (2 / $ThisFileInfo['audio']['channels']) / 144; - } - $thisfile_mpeg_audio['framelength'] = floor($framelengthfloat); - } - - if ($thisfile_mpeg_audio['xing_flags']['toc']) { - $LAMEtocData = substr($headerstring, $VBRidOffset + 16, 100); - for ($i = 0; $i < 100; $i++) { - $thisfile_mpeg_audio['toc'][$i] = ord($LAMEtocData{$i}); - } - } - if ($thisfile_mpeg_audio['xing_flags']['vbr_scale']) { - $thisfile_mpeg_audio['VBR_scale'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 116, 4)); - } - - - // http://gabriel.mp3-tech.org/mp3infotag.html - if (substr($headerstring, $VBRidOffset + 120, 4) == 'LAME') { - - // shortcut - $thisfile_mpeg_audio['LAME'] = array(); - $thisfile_mpeg_audio_lame = &$thisfile_mpeg_audio['LAME']; - - - $thisfile_mpeg_audio_lame['long_version'] = substr($headerstring, $VBRidOffset + 120, 20); - $thisfile_mpeg_audio_lame['short_version'] = substr($thisfile_mpeg_audio_lame['long_version'], 0, 9); - - if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.90') { - - // extra 11 chars are not part of version string when LAMEtag present - unset($thisfile_mpeg_audio_lame['long_version']); - - // It the LAME tag was only introduced in LAME v3.90 - // http://www.hydrogenaudio.org/?act=ST&f=15&t=9933 - - // Offsets of various bytes in http://gabriel.mp3-tech.org/mp3infotag.html - // are assuming a 'Xing' identifier offset of 0x24, which is the case for - // MPEG-1 non-mono, but not for other combinations - $LAMEtagOffsetContant = $VBRidOffset - 0x24; - - // shortcuts - $thisfile_mpeg_audio_lame['RGAD'] = array('track'=>array(), 'album'=>array()); - $thisfile_mpeg_audio_lame_RGAD = &$thisfile_mpeg_audio_lame['RGAD']; - $thisfile_mpeg_audio_lame_RGAD_track = &$thisfile_mpeg_audio_lame_RGAD['track']; - $thisfile_mpeg_audio_lame_RGAD_album = &$thisfile_mpeg_audio_lame_RGAD['album']; - $thisfile_mpeg_audio_lame['raw'] = array(); - $thisfile_mpeg_audio_lame_raw = &$thisfile_mpeg_audio_lame['raw']; - - // byte $9B VBR Quality - // This field is there to indicate a quality level, although the scale was not precised in the original Xing specifications. - // Actually overwrites original Xing bytes - unset($thisfile_mpeg_audio['VBR_scale']); - $thisfile_mpeg_audio_lame['vbr_quality'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0x9B, 1)); - - // bytes $9C-$A4 Encoder short VersionString - $thisfile_mpeg_audio_lame['short_version'] = substr($headerstring, $LAMEtagOffsetContant + 0x9C, 9); - - // byte $A5 Info Tag revision + VBR method - $LAMEtagRevisionVBRmethod = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA5, 1)); - - $thisfile_mpeg_audio_lame['tag_revision'] = ($LAMEtagRevisionVBRmethod & 0xF0) >> 4; - $thisfile_mpeg_audio_lame_raw['vbr_method'] = $LAMEtagRevisionVBRmethod & 0x0F; - $thisfile_mpeg_audio_lame['vbr_method'] = getid3_mp3::LAMEvbrMethodLookup($thisfile_mpeg_audio_lame_raw['vbr_method']); - $thisfile_mpeg_audio['bitrate_mode'] = substr($thisfile_mpeg_audio_lame['vbr_method'], 0, 3); // usually either 'cbr' or 'vbr', but truncates 'vbr-old / vbr-rh' to 'vbr' - - // byte $A6 Lowpass filter value - $thisfile_mpeg_audio_lame['lowpass_frequency'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA6, 1)) * 100; - - // bytes $A7-$AE Replay Gain - // http://privatewww.essex.ac.uk/~djmrob/replaygain/rg_data_format.html - // bytes $A7-$AA : 32 bit floating point "Peak signal amplitude" - if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.94b') { - // LAME 3.94a16 and later - 9.23 fixed point - // ie 0x0059E2EE / (2^23) = 5890798 / 8388608 = 0.7022378444671630859375 - $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] = (float) ((getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA7, 4))) / 8388608); - } else { - // LAME 3.94a15 and earlier - 32-bit floating point - // Actually 3.94a16 will fall in here too and be WRONG, but is hard to detect 3.94a16 vs 3.94a15 - $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] = getid3_lib::LittleEndian2Float(substr($headerstring, $LAMEtagOffsetContant + 0xA7, 4)); - } - if ($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] == 0) { - unset($thisfile_mpeg_audio_lame_RGAD['peak_amplitude']); - } else { - $thisfile_mpeg_audio_lame_RGAD['peak_db'] = getid3_lib::RGADamplitude2dB($thisfile_mpeg_audio_lame_RGAD['peak_amplitude']); - } - - $thisfile_mpeg_audio_lame_raw['RGAD_track'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAB, 2)); - $thisfile_mpeg_audio_lame_raw['RGAD_album'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAD, 2)); - - - if ($thisfile_mpeg_audio_lame_raw['RGAD_track'] != 0) { - - $thisfile_mpeg_audio_lame_RGAD_track['raw']['name'] = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0xE000) >> 13; - $thisfile_mpeg_audio_lame_RGAD_track['raw']['originator'] = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x1C00) >> 10; - $thisfile_mpeg_audio_lame_RGAD_track['raw']['sign_bit'] = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x0200) >> 9; - $thisfile_mpeg_audio_lame_RGAD_track['raw']['gain_adjust'] = $thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x01FF; - $thisfile_mpeg_audio_lame_RGAD_track['name'] = getid3_lib::RGADnameLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['name']); - $thisfile_mpeg_audio_lame_RGAD_track['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['originator']); - $thisfile_mpeg_audio_lame_RGAD_track['gain_db'] = getid3_lib::RGADadjustmentLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['gain_adjust'], $thisfile_mpeg_audio_lame_RGAD_track['raw']['sign_bit']); - - if (!empty($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'])) { - $ThisFileInfo['replay_gain']['track']['peak'] = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude']; - } - $ThisFileInfo['replay_gain']['track']['originator'] = $thisfile_mpeg_audio_lame_RGAD_track['originator']; - $ThisFileInfo['replay_gain']['track']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_track['gain_db']; - } else { - unset($thisfile_mpeg_audio_lame_RGAD['track']); - } - if ($thisfile_mpeg_audio_lame_raw['RGAD_album'] != 0) { - - $thisfile_mpeg_audio_lame_RGAD_album['raw']['name'] = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0xE000) >> 13; - $thisfile_mpeg_audio_lame_RGAD_album['raw']['originator'] = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x1C00) >> 10; - $thisfile_mpeg_audio_lame_RGAD_album['raw']['sign_bit'] = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x0200) >> 9; - $thisfile_mpeg_audio_lame_RGAD_album['raw']['gain_adjust'] = $thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x01FF; - $thisfile_mpeg_audio_lame_RGAD_album['name'] = getid3_lib::RGADnameLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['name']); - $thisfile_mpeg_audio_lame_RGAD_album['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['originator']); - $thisfile_mpeg_audio_lame_RGAD_album['gain_db'] = getid3_lib::RGADadjustmentLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['gain_adjust'], $thisfile_mpeg_audio_lame_RGAD_album['raw']['sign_bit']); - - if (!empty($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'])) { - $ThisFileInfo['replay_gain']['album']['peak'] = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude']; - } - $ThisFileInfo['replay_gain']['album']['originator'] = $thisfile_mpeg_audio_lame_RGAD_album['originator']; - $ThisFileInfo['replay_gain']['album']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_album['gain_db']; - } else { - unset($thisfile_mpeg_audio_lame_RGAD['album']); - } - if (empty($thisfile_mpeg_audio_lame_RGAD)) { - unset($thisfile_mpeg_audio_lame['RGAD']); - } - - - // byte $AF Encoding flags + ATH Type - $EncodingFlagsATHtype = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAF, 1)); - $thisfile_mpeg_audio_lame['encoding_flags']['nspsytune'] = (bool) ($EncodingFlagsATHtype & 0x10); - $thisfile_mpeg_audio_lame['encoding_flags']['nssafejoint'] = (bool) ($EncodingFlagsATHtype & 0x20); - $thisfile_mpeg_audio_lame['encoding_flags']['nogap_next'] = (bool) ($EncodingFlagsATHtype & 0x40); - $thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev'] = (bool) ($EncodingFlagsATHtype & 0x80); - $thisfile_mpeg_audio_lame['ath_type'] = $EncodingFlagsATHtype & 0x0F; - - // byte $B0 if ABR {specified bitrate} else {minimal bitrate} - $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB0, 1)); - if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 2) { // Average BitRate (ABR) - $thisfile_mpeg_audio_lame['bitrate_abr'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']; - } elseif ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) { // Constant BitRate (CBR) - // ignore - } elseif ($thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] > 0) { // Variable BitRate (VBR) - minimum bitrate - $thisfile_mpeg_audio_lame['bitrate_min'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']; - } - - // bytes $B1-$B3 Encoder delays - $EncoderDelays = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB1, 3)); - $thisfile_mpeg_audio_lame['encoder_delay'] = ($EncoderDelays & 0xFFF000) >> 12; - $thisfile_mpeg_audio_lame['end_padding'] = $EncoderDelays & 0x000FFF; - - // byte $B4 Misc - $MiscByte = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB4, 1)); - $thisfile_mpeg_audio_lame_raw['noise_shaping'] = ($MiscByte & 0x03); - $thisfile_mpeg_audio_lame_raw['stereo_mode'] = ($MiscByte & 0x1C) >> 2; - $thisfile_mpeg_audio_lame_raw['not_optimal_quality'] = ($MiscByte & 0x20) >> 5; - $thisfile_mpeg_audio_lame_raw['source_sample_freq'] = ($MiscByte & 0xC0) >> 6; - $thisfile_mpeg_audio_lame['noise_shaping'] = $thisfile_mpeg_audio_lame_raw['noise_shaping']; - $thisfile_mpeg_audio_lame['stereo_mode'] = getid3_mp3::LAMEmiscStereoModeLookup($thisfile_mpeg_audio_lame_raw['stereo_mode']); - $thisfile_mpeg_audio_lame['not_optimal_quality'] = (bool) $thisfile_mpeg_audio_lame_raw['not_optimal_quality']; - $thisfile_mpeg_audio_lame['source_sample_freq'] = getid3_mp3::LAMEmiscSourceSampleFrequencyLookup($thisfile_mpeg_audio_lame_raw['source_sample_freq']); - - // byte $B5 MP3 Gain - $thisfile_mpeg_audio_lame_raw['mp3_gain'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB5, 1), false, true); - $thisfile_mpeg_audio_lame['mp3_gain_db'] = (getid3_lib::RGADamplitude2dB(2) / 4) * $thisfile_mpeg_audio_lame_raw['mp3_gain']; - $thisfile_mpeg_audio_lame['mp3_gain_factor'] = pow(2, ($thisfile_mpeg_audio_lame['mp3_gain_db'] / 6)); - - // bytes $B6-$B7 Preset and surround info - $PresetSurroundBytes = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB6, 2)); - // Reserved = ($PresetSurroundBytes & 0xC000); - $thisfile_mpeg_audio_lame_raw['surround_info'] = ($PresetSurroundBytes & 0x3800); - $thisfile_mpeg_audio_lame['surround_info'] = getid3_mp3::LAMEsurroundInfoLookup($thisfile_mpeg_audio_lame_raw['surround_info']); - $thisfile_mpeg_audio_lame['preset_used_id'] = ($PresetSurroundBytes & 0x07FF); - $thisfile_mpeg_audio_lame['preset_used'] = getid3_mp3::LAMEpresetUsedLookup($thisfile_mpeg_audio_lame); - if (!empty($thisfile_mpeg_audio_lame['preset_used_id']) && empty($thisfile_mpeg_audio_lame['preset_used'])) { - $ThisFileInfo['warning'][] = 'Unknown LAME preset used ('.$thisfile_mpeg_audio_lame['preset_used_id'].') - please report to info@getid3.org'; - } - if (($thisfile_mpeg_audio_lame['short_version'] == 'LAME3.90.') && !empty($thisfile_mpeg_audio_lame['preset_used_id'])) { - // this may change if 3.90.4 ever comes out - $thisfile_mpeg_audio_lame['short_version'] = 'LAME3.90.3'; - } - - // bytes $B8-$BB MusicLength - $thisfile_mpeg_audio_lame['audio_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB8, 4)); - $ExpectedNumberOfAudioBytes = (($thisfile_mpeg_audio_lame['audio_bytes'] > 0) ? $thisfile_mpeg_audio_lame['audio_bytes'] : $thisfile_mpeg_audio['VBR_bytes']); - - // bytes $BC-$BD MusicCRC - $thisfile_mpeg_audio_lame['music_crc'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xBC, 2)); - - // bytes $BE-$BF CRC-16 of Info Tag - $thisfile_mpeg_audio_lame['lame_tag_crc'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xBE, 2)); - - - // LAME CBR - if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) { - - $thisfile_mpeg_audio['bitrate_mode'] = 'cbr'; - $thisfile_mpeg_audio['bitrate'] = getid3_mp3::ClosestStandardMP3Bitrate($thisfile_mpeg_audio['bitrate']); - $ThisFileInfo['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate']; - //if (empty($thisfile_mpeg_audio['bitrate']) || (!empty($thisfile_mpeg_audio_lame['bitrate_min']) && ($thisfile_mpeg_audio_lame['bitrate_min'] != 255))) { - // $thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio_lame['bitrate_min']; - //} - - } - - } - } - - } else { - - // not Fraunhofer or Xing VBR methods, most likely CBR (but could be VBR with no header) - $thisfile_mpeg_audio['bitrate_mode'] = 'cbr'; - if ($recursivesearch) { - $thisfile_mpeg_audio['bitrate_mode'] = 'vbr'; - if (getid3_mp3::RecursiveFrameScanning($fd, $ThisFileInfo, $offset, $nextframetestoffset, true)) { - $recursivesearch = false; - $thisfile_mpeg_audio['bitrate_mode'] = 'cbr'; - } - if ($thisfile_mpeg_audio['bitrate_mode'] == 'vbr') { - $ThisFileInfo['warning'][] = 'VBR file with no VBR header. Bitrate values calculated from actual frame bitrates.'; - } - } - - } - - } - - if (($ExpectedNumberOfAudioBytes > 0) && ($ExpectedNumberOfAudioBytes != ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']))) { - if ($ExpectedNumberOfAudioBytes > ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'])) { - if (($ExpectedNumberOfAudioBytes - ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'])) == 1) { - $ThisFileInfo['warning'][] = 'Last byte of data truncated (this is a known bug in Meracl ID3 Tag Writer before v1.3.5)'; - } else { - $ThisFileInfo['warning'][] = 'Probable truncated file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, only found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']).' (short by '.($ExpectedNumberOfAudioBytes - ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'])).' bytes)'; - } - } else { - if ((($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) - $ExpectedNumberOfAudioBytes) == 1) { - // $prenullbytefileoffset = ftell($fd); - // fseek($fd, $ThisFileInfo['avdataend'], SEEK_SET); - // $PossibleNullByte = fread($fd, 1); - // fseek($fd, $prenullbytefileoffset, SEEK_SET); - // if ($PossibleNullByte === "\x00") { - $ThisFileInfo['avdataend']--; - // $ThisFileInfo['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored'; - // } else { - // $ThisFileInfo['warning'][] = 'Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']).' ('.(($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)'; - // } - } else { - $ThisFileInfo['warning'][] = 'Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']).' ('.(($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)'; - } - } - } - - if (($thisfile_mpeg_audio['bitrate'] == 'free') && empty($ThisFileInfo['audio']['bitrate'])) { - if (($offset == $ThisFileInfo['avdataoffset']) && empty($thisfile_mpeg_audio['VBR_frames'])) { - $framebytelength = getid3_mp3::FreeFormatFrameLength($fd, $offset, $ThisFileInfo, true); - if ($framebytelength > 0) { - $thisfile_mpeg_audio['framelength'] = $framebytelength; - if ($thisfile_mpeg_audio['layer'] == '1') { - // BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12 - $ThisFileInfo['audio']['bitrate'] = ((($framebytelength / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12; - } else { - // Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144 - $ThisFileInfo['audio']['bitrate'] = (($framebytelength - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144; - } - } else { - $ThisFileInfo['error'][] = 'Error calculating frame length of free-format MP3 without Xing/LAME header'; - } - } - } - - if (!empty($thisfile_mpeg_audio['VBR_frames'])) { - switch ($thisfile_mpeg_audio['bitrate_mode']) { - case 'vbr': - case 'abr': - if (($thisfile_mpeg_audio['version'] == '1') && ($thisfile_mpeg_audio['layer'] == 1)) { - $thisfile_mpeg_audio['VBR_bitrate'] = (($thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($ThisFileInfo['audio']['sample_rate'] / 384); - } elseif ((($thisfile_mpeg_audio['version'] == '2') || ($thisfile_mpeg_audio['version'] == '2.5')) && ($thisfile_mpeg_audio['layer'] == 3)) { - $thisfile_mpeg_audio['VBR_bitrate'] = (($thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($ThisFileInfo['audio']['sample_rate'] / 576); - } else { - $thisfile_mpeg_audio['VBR_bitrate'] = (($thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($ThisFileInfo['audio']['sample_rate'] / 1152); - } - if ($thisfile_mpeg_audio['VBR_bitrate'] > 0) { - $ThisFileInfo['audio']['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate']; - $thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate']; // to avoid confusion - } - break; - } - } - - // End variable-bitrate headers - //////////////////////////////////////////////////////////////////////////////////// - - if ($recursivesearch) { - - if (!getid3_mp3::RecursiveFrameScanning($fd, $ThisFileInfo, $offset, $nextframetestoffset, $ScanAsCBR)) { - return false; - } - - } - - - //if (false) { - // // experimental side info parsing section - not returning anything useful yet - // - // $SideInfoBitstream = getid3_lib::BigEndian2Bin($SideInfoData); - // $SideInfoOffset = 0; - // - // if ($thisfile_mpeg_audio['version'] == '1') { - // if ($thisfile_mpeg_audio['channelmode'] == 'mono') { - // // MPEG-1 (mono) - // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9); - // $SideInfoOffset += 9; - // $SideInfoOffset += 5; - // } else { - // // MPEG-1 (stereo, joint-stereo, dual-channel) - // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9); - // $SideInfoOffset += 9; - // $SideInfoOffset += 3; - // } - // } else { // 2 or 2.5 - // if ($thisfile_mpeg_audio['channelmode'] == 'mono') { - // // MPEG-2, MPEG-2.5 (mono) - // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8); - // $SideInfoOffset += 8; - // $SideInfoOffset += 1; - // } else { - // // MPEG-2, MPEG-2.5 (stereo, joint-stereo, dual-channel) - // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8); - // $SideInfoOffset += 8; - // $SideInfoOffset += 2; - // } - // } - // - // if ($thisfile_mpeg_audio['version'] == '1') { - // for ($channel = 0; $channel < $ThisFileInfo['audio']['channels']; $channel++) { - // for ($scfsi_band = 0; $scfsi_band < 4; $scfsi_band++) { - // $thisfile_mpeg_audio['scfsi'][$channel][$scfsi_band] = substr($SideInfoBitstream, $SideInfoOffset, 1); - // $SideInfoOffset += 2; - // } - // } - // } - // for ($granule = 0; $granule < (($thisfile_mpeg_audio['version'] == '1') ? 2 : 1); $granule++) { - // for ($channel = 0; $channel < $ThisFileInfo['audio']['channels']; $channel++) { - // $thisfile_mpeg_audio['part2_3_length'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 12); - // $SideInfoOffset += 12; - // $thisfile_mpeg_audio['big_values'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9); - // $SideInfoOffset += 9; - // $thisfile_mpeg_audio['global_gain'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 8); - // $SideInfoOffset += 8; - // if ($thisfile_mpeg_audio['version'] == '1') { - // $thisfile_mpeg_audio['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4); - // $SideInfoOffset += 4; - // } else { - // $thisfile_mpeg_audio['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9); - // $SideInfoOffset += 9; - // } - // $thisfile_mpeg_audio['window_switching_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); - // $SideInfoOffset += 1; - // - // if ($thisfile_mpeg_audio['window_switching_flag'][$granule][$channel] == '1') { - // - // $thisfile_mpeg_audio['block_type'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 2); - // $SideInfoOffset += 2; - // $thisfile_mpeg_audio['mixed_block_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); - // $SideInfoOffset += 1; - // - // for ($region = 0; $region < 2; $region++) { - // $thisfile_mpeg_audio['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5); - // $SideInfoOffset += 5; - // } - // $thisfile_mpeg_audio['table_select'][$granule][$channel][2] = 0; - // - // for ($window = 0; $window < 3; $window++) { - // $thisfile_mpeg_audio['subblock_gain'][$granule][$channel][$window] = substr($SideInfoBitstream, $SideInfoOffset, 3); - // $SideInfoOffset += 3; - // } - // - // } else { - // - // for ($region = 0; $region < 3; $region++) { - // $thisfile_mpeg_audio['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5); - // $SideInfoOffset += 5; - // } - // - // $thisfile_mpeg_audio['region0_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4); - // $SideInfoOffset += 4; - // $thisfile_mpeg_audio['region1_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 3); - // $SideInfoOffset += 3; - // $thisfile_mpeg_audio['block_type'][$granule][$channel] = 0; - // } - // - // if ($thisfile_mpeg_audio['version'] == '1') { - // $thisfile_mpeg_audio['preflag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); - // $SideInfoOffset += 1; - // } - // $thisfile_mpeg_audio['scalefac_scale'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); - // $SideInfoOffset += 1; - // $thisfile_mpeg_audio['count1table_select'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); - // $SideInfoOffset += 1; - // } - // } - //} - - return true; - } - - function RecursiveFrameScanning(&$fd, &$ThisFileInfo, &$offset, &$nextframetestoffset, $ScanAsCBR) { - for ($i = 0; $i < GETID3_MP3_VALID_CHECK_FRAMES; $i++) { - // check next GETID3_MP3_VALID_CHECK_FRAMES frames for validity, to make sure we haven't run across a false synch - if (($nextframetestoffset + 4) >= $ThisFileInfo['avdataend']) { - // end of file - return true; - } - - $nextframetestarray = array('error'=>'', 'warning'=>'', 'avdataend'=>$ThisFileInfo['avdataend'], 'avdataoffset'=>$ThisFileInfo['avdataoffset']); - if (getid3_mp3::decodeMPEGaudioHeader($fd, $nextframetestoffset, $nextframetestarray, false)) { - if ($ScanAsCBR) { - // force CBR mode, used for trying to pick out invalid audio streams with - // valid(?) VBR headers, or VBR streams with no VBR header - if (!isset($nextframetestarray['mpeg']['audio']['bitrate']) || !isset($ThisFileInfo['mpeg']['audio']['bitrate']) || ($nextframetestarray['mpeg']['audio']['bitrate'] != $ThisFileInfo['mpeg']['audio']['bitrate'])) { - return false; - } - } - - - // next frame is OK, get ready to check the one after that - if (isset($nextframetestarray['mpeg']['audio']['framelength']) && ($nextframetestarray['mpeg']['audio']['framelength'] > 0)) { - $nextframetestoffset += $nextframetestarray['mpeg']['audio']['framelength']; - } else { - $ThisFileInfo['error'][] = 'Frame at offset ('.$offset.') is has an invalid frame length.'; - return false; - } - - } else { - - // next frame is not valid, note the error and fail, so scanning can contiue for a valid frame sequence - $ThisFileInfo['error'][] = 'Frame at offset ('.$offset.') is valid, but the next one at ('.$nextframetestoffset.') is not.'; - - return false; - } - } - return true; - } - - function FreeFormatFrameLength($fd, $offset, &$ThisFileInfo, $deepscan=false) { - fseek($fd, $offset, SEEK_SET); - $MPEGaudioData = fread($fd, 32768); - - $SyncPattern1 = substr($MPEGaudioData, 0, 4); - // may be different pattern due to padding - $SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) | 0x02).$SyncPattern1{3}; - if ($SyncPattern2 === $SyncPattern1) { - $SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) & 0xFD).$SyncPattern1{3}; - } - - $framelength = false; - $framelength1 = strpos($MPEGaudioData, $SyncPattern1, 4); - $framelength2 = strpos($MPEGaudioData, $SyncPattern2, 4); - if ($framelength1 > 4) { - $framelength = $framelength1; - } - if (($framelength2 > 4) && ($framelength2 < $framelength1)) { - $framelength = $framelength2; - } - if (!$framelength) { - - // LAME 3.88 has a different value for modeextension on the first frame vs the rest - $framelength1 = strpos($MPEGaudioData, substr($SyncPattern1, 0, 3), 4); - $framelength2 = strpos($MPEGaudioData, substr($SyncPattern2, 0, 3), 4); - - if ($framelength1 > 4) { - $framelength = $framelength1; - } - if (($framelength2 > 4) && ($framelength2 < $framelength1)) { - $framelength = $framelength2; - } - if (!$framelength) { - $ThisFileInfo['error'][] = 'Cannot find next free-format synch pattern ('.getid3_lib::PrintHexBytes($SyncPattern1).' or '.getid3_lib::PrintHexBytes($SyncPattern2).') after offset '.$offset; - return false; - } else { - $ThisFileInfo['warning'][] = 'ModeExtension varies between first frame and other frames (known free-format issue in LAME 3.88)'; - $ThisFileInfo['audio']['codec'] = 'LAME'; - $ThisFileInfo['audio']['encoder'] = 'LAME3.88'; - $SyncPattern1 = substr($SyncPattern1, 0, 3); - $SyncPattern2 = substr($SyncPattern2, 0, 3); - } - } - - if ($deepscan) { - - $ActualFrameLengthValues = array(); - $nextoffset = $offset + $framelength; - while ($nextoffset < ($ThisFileInfo['avdataend'] - 6)) { - fseek($fd, $nextoffset - 1, SEEK_SET); - $NextSyncPattern = fread($fd, 6); - if ((substr($NextSyncPattern, 1, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 1, strlen($SyncPattern2)) == $SyncPattern2)) { - // good - found where expected - $ActualFrameLengthValues[] = $framelength; - } elseif ((substr($NextSyncPattern, 0, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 0, strlen($SyncPattern2)) == $SyncPattern2)) { - // ok - found one byte earlier than expected (last frame wasn't padded, first frame was) - $ActualFrameLengthValues[] = ($framelength - 1); - $nextoffset--; - } elseif ((substr($NextSyncPattern, 2, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 2, strlen($SyncPattern2)) == $SyncPattern2)) { - // ok - found one byte later than expected (last frame was padded, first frame wasn't) - $ActualFrameLengthValues[] = ($framelength + 1); - $nextoffset++; - } else { - $ThisFileInfo['error'][] = 'Did not find expected free-format sync pattern at offset '.$nextoffset; - return false; - } - $nextoffset += $framelength; - } - if (count($ActualFrameLengthValues) > 0) { - $framelength = intval(round(array_sum($ActualFrameLengthValues) / count($ActualFrameLengthValues))); - } - } - return $framelength; - } - - function getOnlyMPEGaudioInfoBruteForce($fd, &$ThisFileInfo) { - - $MPEGaudioHeaderDecodeCache = array(); - $MPEGaudioHeaderValidCache = array(); - $MPEGaudioHeaderLengthCache = array(); - $MPEGaudioVersionLookup = getid3_mp3::MPEGaudioVersionArray(); - $MPEGaudioLayerLookup = getid3_mp3::MPEGaudioLayerArray(); - $MPEGaudioBitrateLookup = getid3_mp3::MPEGaudioBitrateArray(); - $MPEGaudioFrequencyLookup = getid3_mp3::MPEGaudioFrequencyArray(); - $MPEGaudioChannelModeLookup = getid3_mp3::MPEGaudioChannelModeArray(); - $MPEGaudioModeExtensionLookup = getid3_mp3::MPEGaudioModeExtensionArray(); - $MPEGaudioEmphasisLookup = getid3_mp3::MPEGaudioEmphasisArray(); - $LongMPEGversionLookup = array(); - $LongMPEGlayerLookup = array(); - $LongMPEGbitrateLookup = array(); - $LongMPEGpaddingLookup = array(); - $LongMPEGfrequencyLookup = array(); - - $Distribution['bitrate'] = array(); - $Distribution['frequency'] = array(); - $Distribution['layer'] = array(); - $Distribution['version'] = array(); - $Distribution['padding'] = array(); - - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - - $previousvalidframe = $ThisFileInfo['avdataoffset']; - while (ftell($fd) < $ThisFileInfo['avdataend']) { - set_time_limit(30); - $head4 = fread($fd, 4); - if (strlen($head4) < 4) { - break; - } - if ($head4{0} != "\xFF") { - for ($i = 1; $i < 4; $i++) { - if ($head4{$i} == "\xFF") { - fseek($fd, $i - 4, SEEK_CUR); - continue 2; - } - } - continue; - } - if (!isset($MPEGaudioHeaderDecodeCache[$head4])) { - $MPEGaudioHeaderDecodeCache[$head4] = getid3_mp3::MPEGaudioHeaderDecode($head4); - } - if (!isset($MPEGaudioHeaderValidCache[$head4])) { - $MPEGaudioHeaderValidCache[$head4] = getid3_mp3::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$head4], false, false); - } - if ($MPEGaudioHeaderValidCache[$head4]) { - - if (!isset($MPEGaudioHeaderLengthCache[$head4])) { - $LongMPEGversionLookup[$head4] = $MPEGaudioVersionLookup[$MPEGaudioHeaderDecodeCache[$head4]['version']]; - $LongMPEGlayerLookup[$head4] = $MPEGaudioLayerLookup[$MPEGaudioHeaderDecodeCache[$head4]['layer']]; - $LongMPEGbitrateLookup[$head4] = $MPEGaudioBitrateLookup[$LongMPEGversionLookup[$head4]][$LongMPEGlayerLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['bitrate']]; - $LongMPEGpaddingLookup[$head4] = (bool) $MPEGaudioHeaderDecodeCache[$head4]['padding']; - $LongMPEGfrequencyLookup[$head4] = $MPEGaudioFrequencyLookup[$LongMPEGversionLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['sample_rate']]; - $MPEGaudioHeaderLengthCache[$head4] = getid3_mp3::MPEGaudioFrameLength( - $LongMPEGbitrateLookup[$head4], - $LongMPEGversionLookup[$head4], - $LongMPEGlayerLookup[$head4], - $LongMPEGpaddingLookup[$head4], - $LongMPEGfrequencyLookup[$head4]); - } - if ($MPEGaudioHeaderLengthCache[$head4] > 4) { - $WhereWeWere = ftell($fd); - fseek($fd, $MPEGaudioHeaderLengthCache[$head4] - 4, SEEK_CUR); - $next4 = fread($fd, 4); - if ($next4{0} == "\xFF") { - if (!isset($MPEGaudioHeaderDecodeCache[$next4])) { - $MPEGaudioHeaderDecodeCache[$next4] = getid3_mp3::MPEGaudioHeaderDecode($next4); - } - if (!isset($MPEGaudioHeaderValidCache[$next4])) { - $MPEGaudioHeaderValidCache[$next4] = getid3_mp3::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$next4], false, false); - } - if ($MPEGaudioHeaderValidCache[$next4]) { - fseek($fd, -4, SEEK_CUR); - - @$Distribution['bitrate'][$LongMPEGbitrateLookup[$head4]]++; - @$Distribution['layer'][$LongMPEGlayerLookup[$head4]]++; - @$Distribution['version'][$LongMPEGversionLookup[$head4]]++; - @$Distribution['padding'][intval($LongMPEGpaddingLookup[$head4])]++; - @$Distribution['frequency'][$LongMPEGfrequencyLookup[$head4]]++; - continue; - } - } - unset($next4); - fseek($fd, $WhereWeWere - 3, SEEK_SET); - } - - } - } - foreach ($Distribution as $key => $value) { - ksort($Distribution[$key], SORT_NUMERIC); - } - ksort($Distribution['version'], SORT_STRING); - $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = $Distribution['bitrate']; - $ThisFileInfo['mpeg']['audio']['frequency_distribution'] = $Distribution['frequency']; - $ThisFileInfo['mpeg']['audio']['layer_distribution'] = $Distribution['layer']; - $ThisFileInfo['mpeg']['audio']['version_distribution'] = $Distribution['version']; - $ThisFileInfo['mpeg']['audio']['padding_distribution'] = $Distribution['padding']; - if (count($Distribution['version']) > 1) { - $ThisFileInfo['error'][] = 'Corrupt file - more than one MPEG version detected'; - } - if (count($Distribution['layer']) > 1) { - $ThisFileInfo['error'][] = 'Corrupt file - more than one MPEG layer detected'; - } - if (count($Distribution['frequency']) > 1) { - $ThisFileInfo['error'][] = 'Corrupt file - more than one MPEG sample rate detected'; - } - - - $bittotal = 0; - foreach ($Distribution['bitrate'] as $bitratevalue => $bitratecount) { - if ($bitratevalue != 'free') { - $bittotal += ($bitratevalue * $bitratecount); - } - } - $ThisFileInfo['mpeg']['audio']['frame_count'] = array_sum($Distribution['bitrate']); - if ($ThisFileInfo['mpeg']['audio']['frame_count'] == 0) { - $ThisFileInfo['error'][] = 'no MPEG audio frames found'; - return false; - } - $ThisFileInfo['mpeg']['audio']['bitrate'] = ($bittotal / $ThisFileInfo['mpeg']['audio']['frame_count']); - $ThisFileInfo['mpeg']['audio']['bitrate_mode'] = ((count($Distribution['bitrate']) > 0) ? 'vbr' : 'cbr'); - $ThisFileInfo['mpeg']['audio']['sample_rate'] = getid3_lib::array_max($Distribution['frequency'], true); - - $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['bitrate']; - $ThisFileInfo['audio']['bitrate_mode'] = $ThisFileInfo['mpeg']['audio']['bitrate_mode']; - $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['mpeg']['audio']['sample_rate']; - $ThisFileInfo['audio']['dataformat'] = 'mp'.getid3_lib::array_max($Distribution['layer'], true); - $ThisFileInfo['fileformat'] = $ThisFileInfo['audio']['dataformat']; - - return true; - } - - - function getOnlyMPEGaudioInfo($fd, &$ThisFileInfo, $avdataoffset, $BitrateHistogram=false) { - // looks for synch, decodes MPEG audio header - - fseek($fd, $avdataoffset, SEEK_SET); - $header = ''; - $SynchSeekOffset = 0; - - static $MPEGaudioVersionLookup; - static $MPEGaudioLayerLookup; - static $MPEGaudioBitrateLookup; - if (empty($MPEGaudioVersionLookup)) { - $MPEGaudioVersionLookup = getid3_mp3::MPEGaudioVersionArray(); - $MPEGaudioLayerLookup = getid3_mp3::MPEGaudioLayerArray(); - $MPEGaudioBitrateLookup = getid3_mp3::MPEGaudioBitrateArray(); - - } - - $header_len = strlen($header) - intval(round(GETID3_FREAD_BUFFER_SIZE / 2)); - while (true) { - - if (($SynchSeekOffset > $header_len) && (($avdataoffset + $SynchSeekOffset) < $ThisFileInfo['avdataend']) && !feof($fd)) { - - if ($SynchSeekOffset > 131072) { - // if a synch's not found within the first 128k bytes, then give up - $ThisFileInfo['error'][] = 'could not find valid MPEG audio synch within the first 128k bytes'; - if (isset($ThisFileInfo['audio']['bitrate'])) { - unset($ThisFileInfo['audio']['bitrate']); - } - if (isset($ThisFileInfo['mpeg']['audio'])) { - unset($ThisFileInfo['mpeg']['audio']); - } - if (empty($ThisFileInfo['mpeg'])) { - unset($ThisFileInfo['mpeg']); - } - return false; - - } elseif ($header .= fread($fd, GETID3_FREAD_BUFFER_SIZE)) { - - // great - $header_len = strlen($header) - intval(round(GETID3_FREAD_BUFFER_SIZE / 2)); - - } else { - - $ThisFileInfo['error'][] = 'could not find valid MPEG audio synch before end of file'; - if (isset($ThisFileInfo['audio']['bitrate'])) { - unset($ThisFileInfo['audio']['bitrate']); - } - if (isset($ThisFileInfo['mpeg']['audio'])) { - unset($ThisFileInfo['mpeg']['audio']); - } - if (isset($ThisFileInfo['mpeg']) && (!is_array($ThisFileInfo['mpeg']) || (count($ThisFileInfo['mpeg']) == 0))) { - unset($ThisFileInfo['mpeg']); - } - return false; - - } - } - - if (($SynchSeekOffset + 1) >= strlen($header)) { - $ThisFileInfo['error'][] = 'could not find valid MPEG synch before end of file'; - return false; - } - - if (($header{$SynchSeekOffset} == "\xFF") && ($header{($SynchSeekOffset + 1)} > "\xE0")) { // synch detected - - if (!isset($FirstFrameThisfileInfo) && !isset($ThisFileInfo['mpeg']['audio'])) { - $FirstFrameThisfileInfo = $ThisFileInfo; - $FirstFrameAVDataOffset = $avdataoffset + $SynchSeekOffset; - if (!getid3_mp3::decodeMPEGaudioHeader($fd, $avdataoffset + $SynchSeekOffset, $FirstFrameThisfileInfo, false)) { - // if this is the first valid MPEG-audio frame, save it in case it's a VBR header frame and there's - // garbage between this frame and a valid sequence of MPEG-audio frames, to be restored below - unset($FirstFrameThisfileInfo); - } - } - - $dummy = $ThisFileInfo; // only overwrite real data if valid header found - if (getid3_mp3::decodeMPEGaudioHeader($fd, $avdataoffset + $SynchSeekOffset, $dummy, true)) { - $ThisFileInfo = $dummy; - $ThisFileInfo['avdataoffset'] = $avdataoffset + $SynchSeekOffset; - switch ($ThisFileInfo['fileformat']) { - case '': - case 'id3': - case 'ape': - case 'mp3': - $ThisFileInfo['fileformat'] = 'mp3'; - $ThisFileInfo['audio']['dataformat'] = 'mp3'; - break; - } - if (isset($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode']) && ($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr')) { - if (!(abs($ThisFileInfo['audio']['bitrate'] - $FirstFrameThisfileInfo['audio']['bitrate']) <= 1)) { - // If there is garbage data between a valid VBR header frame and a sequence - // of valid MPEG-audio frames the VBR data is no longer discarded. - $ThisFileInfo = $FirstFrameThisfileInfo; - $ThisFileInfo['avdataoffset'] = $FirstFrameAVDataOffset; - $ThisFileInfo['fileformat'] = 'mp3'; - $ThisFileInfo['audio']['dataformat'] = 'mp3'; - $dummy = $ThisFileInfo; - unset($dummy['mpeg']['audio']); - $GarbageOffsetStart = $FirstFrameAVDataOffset + $FirstFrameThisfileInfo['mpeg']['audio']['framelength']; - $GarbageOffsetEnd = $avdataoffset + $SynchSeekOffset; - if (getid3_mp3::decodeMPEGaudioHeader($fd, $GarbageOffsetEnd, $dummy, true, true)) { - - $ThisFileInfo = $dummy; - $ThisFileInfo['avdataoffset'] = $GarbageOffsetEnd; - $ThisFileInfo['warning'][] = 'apparently-valid VBR header not used because could not find '.GETID3_MP3_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.'), but did find valid CBR stream starting at '.$GarbageOffsetEnd; - - } else { - - $ThisFileInfo['warning'][] = 'using data from VBR header even though could not find '.GETID3_MP3_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.')'; - - } - } - } - if (isset($ThisFileInfo['mpeg']['audio']['bitrate_mode']) && ($ThisFileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr') && !isset($ThisFileInfo['mpeg']['audio']['VBR_method'])) { - // VBR file with no VBR header - $BitrateHistogram = true; - } - - if ($BitrateHistogram) { - - $ThisFileInfo['mpeg']['audio']['stereo_distribution'] = array('stereo'=>0, 'joint stereo'=>0, 'dual channel'=>0, 'mono'=>0); - $ThisFileInfo['mpeg']['audio']['version_distribution'] = array('1'=>0, '2'=>0, '2.5'=>0); - - if ($ThisFileInfo['mpeg']['audio']['version'] == '1') { - if ($ThisFileInfo['mpeg']['audio']['layer'] == 3) { - $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 40000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 320000=>0); - } elseif ($ThisFileInfo['mpeg']['audio']['layer'] == 2) { - $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 320000=>0, 384000=>0); - } elseif ($ThisFileInfo['mpeg']['audio']['layer'] == 1) { - $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 64000=>0, 96000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 288000=>0, 320000=>0, 352000=>0, 384000=>0, 416000=>0, 448000=>0); - } - } elseif ($ThisFileInfo['mpeg']['audio']['layer'] == 1) { - $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 144000=>0, 160000=>0, 176000=>0, 192000=>0, 224000=>0, 256000=>0); - } else { - $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 8000=>0, 16000=>0, 24000=>0, 32000=>0, 40000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 144000=>0, 160000=>0); - } - - $dummy = array('error'=>$ThisFileInfo['error'], 'warning'=>$ThisFileInfo['warning'], 'avdataend'=>$ThisFileInfo['avdataend'], 'avdataoffset'=>$ThisFileInfo['avdataoffset']); - $synchstartoffset = $ThisFileInfo['avdataoffset']; - - $FastMode = false; - $SynchErrorsFound = 0; - while (getid3_mp3::decodeMPEGaudioHeader($fd, $synchstartoffset, $dummy, false, false, $FastMode)) { - $FastMode = true; - $thisframebitrate = $MPEGaudioBitrateLookup[$MPEGaudioVersionLookup[$dummy['mpeg']['audio']['raw']['version']]][$MPEGaudioLayerLookup[$dummy['mpeg']['audio']['raw']['layer']]][$dummy['mpeg']['audio']['raw']['bitrate']]; - - if (empty($dummy['mpeg']['audio']['framelength'])) { - $SynchErrorsFound++; - } else { - $ThisFileInfo['mpeg']['audio']['bitrate_distribution'][$thisframebitrate]++; - $ThisFileInfo['mpeg']['audio']['stereo_distribution'][$dummy['mpeg']['audio']['channelmode']]++; - $ThisFileInfo['mpeg']['audio']['version_distribution'][$dummy['mpeg']['audio']['version']]++; - - $synchstartoffset += $dummy['mpeg']['audio']['framelength']; - } - } - if ($SynchErrorsFound > 0) { - $ThisFileInfo['warning'][] = 'Found '.$SynchErrorsFound.' synch errors in histogram analysis'; - //return false; - } - - $bittotal = 0; - $framecounter = 0; - foreach ($ThisFileInfo['mpeg']['audio']['bitrate_distribution'] as $bitratevalue => $bitratecount) { - $framecounter += $bitratecount; - if ($bitratevalue != 'free') { - $bittotal += ($bitratevalue * $bitratecount); - } - } - if ($framecounter == 0) { - $ThisFileInfo['error'][] = 'Corrupt MP3 file: framecounter == zero'; - return false; - } - $ThisFileInfo['mpeg']['audio']['frame_count'] = $framecounter; - $ThisFileInfo['mpeg']['audio']['bitrate'] = ($bittotal / $framecounter); - - $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['bitrate']; - - - // Definitively set VBR vs CBR, even if the Xing/LAME/VBRI header says differently - $distinct_bitrates = 0; - foreach ($ThisFileInfo['mpeg']['audio']['bitrate_distribution'] as $bitrate_value => $bitrate_count) { - if ($bitrate_count > 0) { - $distinct_bitrates++; - } - } - if ($distinct_bitrates > 1) { - $ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'vbr'; - } else { - $ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'cbr'; - } - $ThisFileInfo['audio']['bitrate_mode'] = $ThisFileInfo['mpeg']['audio']['bitrate_mode']; - - } - - break; // exit while() - } - } - - $SynchSeekOffset++; - if (($avdataoffset + $SynchSeekOffset) >= $ThisFileInfo['avdataend']) { - // end of file/data - - if (empty($ThisFileInfo['mpeg']['audio'])) { - - $ThisFileInfo['error'][] = 'could not find valid MPEG synch before end of file'; - if (isset($ThisFileInfo['audio']['bitrate'])) { - unset($ThisFileInfo['audio']['bitrate']); - } - if (isset($ThisFileInfo['mpeg']['audio'])) { - unset($ThisFileInfo['mpeg']['audio']); - } - if (isset($ThisFileInfo['mpeg']) && (!is_array($ThisFileInfo['mpeg']) || empty($ThisFileInfo['mpeg']))) { - unset($ThisFileInfo['mpeg']); - } - return false; - - } - break; - } - - } - $ThisFileInfo['audio']['channels'] = $ThisFileInfo['mpeg']['audio']['channels']; - $ThisFileInfo['audio']['channelmode'] = $ThisFileInfo['mpeg']['audio']['channelmode']; - $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['mpeg']['audio']['sample_rate']; - return true; - } - - - function MPEGaudioVersionArray() { - static $MPEGaudioVersion = array('2.5', false, '2', '1'); - return $MPEGaudioVersion; - } - - function MPEGaudioLayerArray() { - static $MPEGaudioLayer = array(false, 3, 2, 1); - return $MPEGaudioLayer; - } - - function MPEGaudioBitrateArray() { - static $MPEGaudioBitrate; - if (empty($MPEGaudioBitrate)) { - $MPEGaudioBitrate = array ( - '1' => array (1 => array('free', 32000, 64000, 96000, 128000, 160000, 192000, 224000, 256000, 288000, 320000, 352000, 384000, 416000, 448000), - 2 => array('free', 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 384000), - 3 => array('free', 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000) - ), - - '2' => array (1 => array('free', 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, 176000, 192000, 224000, 256000), - 2 => array('free', 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000), - ) - ); - $MPEGaudioBitrate['2'][3] = $MPEGaudioBitrate['2'][2]; - $MPEGaudioBitrate['2.5'] = $MPEGaudioBitrate['2']; - } - return $MPEGaudioBitrate; - } - - function MPEGaudioFrequencyArray() { - static $MPEGaudioFrequency; - if (empty($MPEGaudioFrequency)) { - $MPEGaudioFrequency = array ( - '1' => array(44100, 48000, 32000), - '2' => array(22050, 24000, 16000), - '2.5' => array(11025, 12000, 8000) - ); - } - return $MPEGaudioFrequency; - } - - function MPEGaudioChannelModeArray() { - static $MPEGaudioChannelMode = array('stereo', 'joint stereo', 'dual channel', 'mono'); - return $MPEGaudioChannelMode; - } - - function MPEGaudioModeExtensionArray() { - static $MPEGaudioModeExtension; - if (empty($MPEGaudioModeExtension)) { - $MPEGaudioModeExtension = array ( - 1 => array('4-31', '8-31', '12-31', '16-31'), - 2 => array('4-31', '8-31', '12-31', '16-31'), - 3 => array('', 'IS', 'MS', 'IS+MS') - ); - } - return $MPEGaudioModeExtension; - } - - function MPEGaudioEmphasisArray() { - static $MPEGaudioEmphasis = array('none', '50/15ms', false, 'CCIT J.17'); - return $MPEGaudioEmphasis; - } - - function MPEGaudioHeaderBytesValid($head4, $allowBitrate15=false) { - return getid3_mp3::MPEGaudioHeaderValid(getid3_mp3::MPEGaudioHeaderDecode($head4), false, $allowBitrate15); - } - - function MPEGaudioHeaderValid($rawarray, $echoerrors=false, $allowBitrate15=false) { - if (($rawarray['synch'] & 0x0FFE) != 0x0FFE) { - return false; - } - - static $MPEGaudioVersionLookup; - static $MPEGaudioLayerLookup; - static $MPEGaudioBitrateLookup; - static $MPEGaudioFrequencyLookup; - static $MPEGaudioChannelModeLookup; - static $MPEGaudioModeExtensionLookup; - static $MPEGaudioEmphasisLookup; - if (empty($MPEGaudioVersionLookup)) { - $MPEGaudioVersionLookup = getid3_mp3::MPEGaudioVersionArray(); - $MPEGaudioLayerLookup = getid3_mp3::MPEGaudioLayerArray(); - $MPEGaudioBitrateLookup = getid3_mp3::MPEGaudioBitrateArray(); - $MPEGaudioFrequencyLookup = getid3_mp3::MPEGaudioFrequencyArray(); - $MPEGaudioChannelModeLookup = getid3_mp3::MPEGaudioChannelModeArray(); - $MPEGaudioModeExtensionLookup = getid3_mp3::MPEGaudioModeExtensionArray(); - $MPEGaudioEmphasisLookup = getid3_mp3::MPEGaudioEmphasisArray(); - } - - if (isset($MPEGaudioVersionLookup[$rawarray['version']])) { - $decodedVersion = $MPEGaudioVersionLookup[$rawarray['version']]; - } else { - echo ($echoerrors ? "\n".'invalid Version ('.$rawarray['version'].')' : ''); - return false; - } - if (isset($MPEGaudioLayerLookup[$rawarray['layer']])) { - $decodedLayer = $MPEGaudioLayerLookup[$rawarray['layer']]; - } else { - echo ($echoerrors ? "\n".'invalid Layer ('.$rawarray['layer'].')' : ''); - return false; - } - if (!isset($MPEGaudioBitrateLookup[$decodedVersion][$decodedLayer][$rawarray['bitrate']])) { - echo ($echoerrors ? "\n".'invalid Bitrate ('.$rawarray['bitrate'].')' : ''); - if ($rawarray['bitrate'] == 15) { - // known issue in LAME 3.90 - 3.93.1 where free-format has bitrate ID of 15 instead of 0 - // let it go through here otherwise file will not be identified - if (!$allowBitrate15) { - return false; - } - } else { - return false; - } - } - if (!isset($MPEGaudioFrequencyLookup[$decodedVersion][$rawarray['sample_rate']])) { - echo ($echoerrors ? "\n".'invalid Frequency ('.$rawarray['sample_rate'].')' : ''); - return false; - } - if (!isset($MPEGaudioChannelModeLookup[$rawarray['channelmode']])) { - echo ($echoerrors ? "\n".'invalid ChannelMode ('.$rawarray['channelmode'].')' : ''); - return false; - } - if (!isset($MPEGaudioModeExtensionLookup[$decodedLayer][$rawarray['modeextension']])) { - echo ($echoerrors ? "\n".'invalid Mode Extension ('.$rawarray['modeextension'].')' : ''); - return false; - } - if (!isset($MPEGaudioEmphasisLookup[$rawarray['emphasis']])) { - echo ($echoerrors ? "\n".'invalid Emphasis ('.$rawarray['emphasis'].')' : ''); - return false; - } - // These are just either set or not set, you can't mess that up :) - // $rawarray['protection']; - // $rawarray['padding']; - // $rawarray['private']; - // $rawarray['copyright']; - // $rawarray['original']; - - return true; - } - - function MPEGaudioHeaderDecode($Header4Bytes) { - // AAAA AAAA AAAB BCCD EEEE FFGH IIJJ KLMM - // A - Frame sync (all bits set) - // B - MPEG Audio version ID - // C - Layer description - // D - Protection bit - // E - Bitrate index - // F - Sampling rate frequency index - // G - Padding bit - // H - Private bit - // I - Channel Mode - // J - Mode extension (Only if Joint stereo) - // K - Copyright - // L - Original - // M - Emphasis - - if (strlen($Header4Bytes) != 4) { - return false; - } - - $MPEGrawHeader['synch'] = (getid3_lib::BigEndian2Int(substr($Header4Bytes, 0, 2)) & 0xFFE0) >> 4; - $MPEGrawHeader['version'] = (ord($Header4Bytes{1}) & 0x18) >> 3; // BB - $MPEGrawHeader['layer'] = (ord($Header4Bytes{1}) & 0x06) >> 1; // CC - $MPEGrawHeader['protection'] = (ord($Header4Bytes{1}) & 0x01); // D - $MPEGrawHeader['bitrate'] = (ord($Header4Bytes{2}) & 0xF0) >> 4; // EEEE - $MPEGrawHeader['sample_rate'] = (ord($Header4Bytes{2}) & 0x0C) >> 2; // FF - $MPEGrawHeader['padding'] = (ord($Header4Bytes{2}) & 0x02) >> 1; // G - $MPEGrawHeader['private'] = (ord($Header4Bytes{2}) & 0x01); // H - $MPEGrawHeader['channelmode'] = (ord($Header4Bytes{3}) & 0xC0) >> 6; // II - $MPEGrawHeader['modeextension'] = (ord($Header4Bytes{3}) & 0x30) >> 4; // JJ - $MPEGrawHeader['copyright'] = (ord($Header4Bytes{3}) & 0x08) >> 3; // K - $MPEGrawHeader['original'] = (ord($Header4Bytes{3}) & 0x04) >> 2; // L - $MPEGrawHeader['emphasis'] = (ord($Header4Bytes{3}) & 0x03); // MM - - return $MPEGrawHeader; - } - - function MPEGaudioFrameLength(&$bitrate, &$version, &$layer, $padding, &$samplerate) { - static $AudioFrameLengthCache = array(); - - if (!isset($AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate])) { - $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate] = false; - if ($bitrate != 'free') { - - if ($version == '1') { - - if ($layer == '1') { - - // For Layer I slot is 32 bits long - $FrameLengthCoefficient = 48; - $SlotLength = 4; - - } else { // Layer 2 / 3 - - // for Layer 2 and Layer 3 slot is 8 bits long. - $FrameLengthCoefficient = 144; - $SlotLength = 1; - - } - - } else { // MPEG-2 / MPEG-2.5 - - if ($layer == '1') { - - // For Layer I slot is 32 bits long - $FrameLengthCoefficient = 24; - $SlotLength = 4; - - } elseif ($layer == '2') { - - // for Layer 2 and Layer 3 slot is 8 bits long. - $FrameLengthCoefficient = 144; - $SlotLength = 1; - - } else { // layer 3 - - // for Layer 2 and Layer 3 slot is 8 bits long. - $FrameLengthCoefficient = 72; - $SlotLength = 1; - - } - - } - - // FrameLengthInBytes = ((Coefficient * BitRate) / SampleRate) + Padding - if ($samplerate > 0) { - $NewFramelength = ($FrameLengthCoefficient * $bitrate) / $samplerate; - $NewFramelength = floor($NewFramelength / $SlotLength) * $SlotLength; // round to next-lower multiple of SlotLength (1 byte for Layer 2/3, 4 bytes for Layer I) - if ($padding) { - $NewFramelength += $SlotLength; - } - $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate] = (int) $NewFramelength; - } - } - } - return $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate]; - } - - function ClosestStandardMP3Bitrate($bitrate) { - static $StandardBitrates = array(320000, 256000, 224000, 192000, 160000, 128000, 112000, 96000, 80000, 64000, 56000, 48000, 40000, 32000, 24000, 16000, 8000); - static $BitrateTable = array(0=>'-'); - $roundbitrate = intval(round($bitrate, -3)); - if (!isset($BitrateTable[$roundbitrate])) { - if ($roundbitrate > 320000) { - $BitrateTable[$roundbitrate] = round($bitrate, -4); - } else { - $LastBitrate = 320000; - foreach ($StandardBitrates as $StandardBitrate) { - $BitrateTable[$roundbitrate] = $StandardBitrate; - if ($roundbitrate >= $StandardBitrate - (($LastBitrate - $StandardBitrate) / 2)) { - break; - } - $LastBitrate = $StandardBitrate; - } - } - } - return $BitrateTable[$roundbitrate]; - } - - function XingVBRidOffset($version, $channelmode) { - static $XingVBRidOffsetCache = array(); - if (empty($XingVBRidOffset)) { - $XingVBRidOffset = array ( - '1' => array ('mono' => 0x15, // 4 + 17 = 21 - 'stereo' => 0x24, // 4 + 32 = 36 - 'joint stereo' => 0x24, - 'dual channel' => 0x24 - ), - - '2' => array ('mono' => 0x0D, // 4 + 9 = 13 - 'stereo' => 0x15, // 4 + 17 = 21 - 'joint stereo' => 0x15, - 'dual channel' => 0x15 - ), - - '2.5' => array ('mono' => 0x15, - 'stereo' => 0x15, - 'joint stereo' => 0x15, - 'dual channel' => 0x15 - ) - ); - } - return $XingVBRidOffset[$version][$channelmode]; - } - - function LAMEvbrMethodLookup($VBRmethodID) { - static $LAMEvbrMethodLookup = array( - 0x00 => 'unknown', - 0x01 => 'cbr', - 0x02 => 'abr', - 0x03 => 'vbr-old / vbr-rh', - 0x04 => 'vbr-new / vbr-mtrh', - 0x05 => 'vbr-mt', - 0x06 => 'Full VBR Method 4', - 0x08 => 'constant bitrate 2 pass', - 0x09 => 'abr 2 pass', - 0x0F => 'reserved' - ); - return (isset($LAMEvbrMethodLookup[$VBRmethodID]) ? $LAMEvbrMethodLookup[$VBRmethodID] : ''); - } - - function LAMEmiscStereoModeLookup($StereoModeID) { - static $LAMEmiscStereoModeLookup = array( - 0 => 'mono', - 1 => 'stereo', - 2 => 'dual mono', - 3 => 'joint stereo', - 4 => 'forced stereo', - 5 => 'auto', - 6 => 'intensity stereo', - 7 => 'other' - ); - return (isset($LAMEmiscStereoModeLookup[$StereoModeID]) ? $LAMEmiscStereoModeLookup[$StereoModeID] : ''); - } - - function LAMEmiscSourceSampleFrequencyLookup($SourceSampleFrequencyID) { - static $LAMEmiscSourceSampleFrequencyLookup = array( - 0 => '<= 32 kHz', - 1 => '44.1 kHz', - 2 => '48 kHz', - 3 => '> 48kHz' - ); - return (isset($LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID]) ? $LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID] : ''); - } - - function LAMEsurroundInfoLookup($SurroundInfoID) { - static $LAMEsurroundInfoLookup = array( - 0 => 'no surround info', - 1 => 'DPL encoding', - 2 => 'DPL2 encoding', - 3 => 'Ambisonic encoding' - ); - return (isset($LAMEsurroundInfoLookup[$SurroundInfoID]) ? $LAMEsurroundInfoLookup[$SurroundInfoID] : 'reserved'); - } - - function LAMEpresetUsedLookup($LAMEtag) { - - if ($LAMEtag['preset_used_id'] == 0) { - // no preset used (LAME >=3.93) - // no preset recorded (LAME <3.93) - return ''; - } - $LAMEpresetUsedLookup = array(); - - ///// THIS PART CANNOT BE STATIC . - for ($i = 8; $i <= 320; $i++) { - switch ($LAMEtag['vbr_method']) { - case 'cbr': - $LAMEpresetUsedLookup[$i] = '--alt-preset '.$LAMEtag['vbr_method'].' '.$i; - break; - case 'abr': - default: // other VBR modes shouldn't be here(?) - $LAMEpresetUsedLookup[$i] = '--alt-preset '.$i; - break; - } - } - - // named old-style presets (studio, phone, voice, etc) are handled in GuessEncoderOptions() - - // named alt-presets - $LAMEpresetUsedLookup[1000] = '--r3mix'; - $LAMEpresetUsedLookup[1001] = '--alt-preset standard'; - $LAMEpresetUsedLookup[1002] = '--alt-preset extreme'; - $LAMEpresetUsedLookup[1003] = '--alt-preset insane'; - $LAMEpresetUsedLookup[1004] = '--alt-preset fast standard'; - $LAMEpresetUsedLookup[1005] = '--alt-preset fast extreme'; - $LAMEpresetUsedLookup[1006] = '--alt-preset medium'; - $LAMEpresetUsedLookup[1007] = '--alt-preset fast medium'; - - // LAME 3.94 additions/changes - $LAMEpresetUsedLookup[1010] = '--preset portable'; // 3.94a15 Oct 21 2003 - $LAMEpresetUsedLookup[1015] = '--preset radio'; // 3.94a15 Oct 21 2003 - - $LAMEpresetUsedLookup[320] = '--preset insane'; // 3.94a15 Nov 12 2003 - $LAMEpresetUsedLookup[410] = '-V9'; - $LAMEpresetUsedLookup[420] = '-V8'; - $LAMEpresetUsedLookup[440] = '-V6'; - $LAMEpresetUsedLookup[430] = '--preset radio'; // 3.94a15 Nov 12 2003 - $LAMEpresetUsedLookup[450] = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'portable'; // 3.94a15 Nov 12 2003 - $LAMEpresetUsedLookup[460] = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'medium'; // 3.94a15 Nov 12 2003 - $LAMEpresetUsedLookup[470] = '--r3mix'; // 3.94b1 Dec 18 2003 - $LAMEpresetUsedLookup[480] = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'standard'; // 3.94a15 Nov 12 2003 - $LAMEpresetUsedLookup[490] = '-V1'; - $LAMEpresetUsedLookup[500] = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'extreme'; // 3.94a15 Nov 12 2003 - - return (isset($LAMEpresetUsedLookup[$LAMEtag['preset_used_id']]) ? $LAMEpresetUsedLookup[$LAMEtag['preset_used_id']] : 'new/unknown preset: '.$LAMEtag['preset_used_id'].' - report to info@getid3.org'); - } - -} - -?> \ No newline at end of file diff --git a/getid3/getid3/module.audio.mpc.php b/getid3/getid3/module.audio.mpc.php deleted file mode 100644 index d0a7202..0000000 --- a/getid3/getid3/module.audio.mpc.php +++ /dev/null @@ -1,296 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.audio.mpc.php // -// module for analyzing Musepack/MPEG+ Audio files // -// dependencies: NONE // -// /// -///////////////////////////////////////////////////////////////// - - -class getid3_mpc -{ - - function getid3_mpc(&$fd, &$ThisFileInfo) { - // http://www.uni-jena.de/~pfk/mpp/sv8/header.html - - $ThisFileInfo['mpc']['header'] = array(); - $thisfile_mpc_header = &$ThisFileInfo['mpc']['header']; - - $ThisFileInfo['fileformat'] = 'mpc'; - $ThisFileInfo['audio']['dataformat'] = 'mpc'; - $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; - $ThisFileInfo['audio']['channels'] = 2; // the format appears to be hardcoded for stereo only - $ThisFileInfo['audio']['lossless'] = false; - - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - - $thisfile_mpc_header['size'] = 28; - $MPCheaderData = fread($fd, $thisfile_mpc_header['size']); - $offset = 0; - - if (substr($MPCheaderData, $offset, 3) == 'MP+') { - - // great, this is SV7+ - $thisfile_mpc_header['raw']['preamble'] = substr($MPCheaderData, $offset, 3); // should be 'MP+' - $offset += 3; - - } elseif (preg_match('/^[\x00\x01\x10\x11\x40\x41\x50\x51\x80\x81\x90\x91\xC0\xC1\xD0\xD1][\x20-37][\x00\x20\x40\x60\x80\xA0\xC0\xE0]/s', substr($MPCheaderData, 0, 4))) { - - // this is SV4 - SV6, handle seperately - $thisfile_mpc_header['size'] = 8; - - // add size of file header to avdataoffset - calc bitrate correctly + MD5 data - $ThisFileInfo['avdataoffset'] += $thisfile_mpc_header['size']; - - // Most of this code adapted from Jurgen Faul's MPEGplus source code - thanks Jurgen! :) - $HeaderDWORD[0] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, 0, 4)); - $HeaderDWORD[1] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, 4, 4)); - - - // DDDD DDDD CCCC CCCC BBBB BBBB AAAA AAAA - // aaaa aaaa abcd dddd dddd deee eeff ffff - // - // a = bitrate = anything - // b = IS = anything - // c = MS = anything - // d = streamversion = 0000000004 or 0000000005 or 0000000006 - // e = maxband = anything - // f = blocksize = 000001 for SV5+, anything(?) for SV4 - - $thisfile_mpc_header['target_bitrate'] = (($HeaderDWORD[0] & 0xFF800000) >> 23); - $thisfile_mpc_header['intensity_stereo'] = (bool) (($HeaderDWORD[0] & 0x00400000) >> 22); - $thisfile_mpc_header['mid-side_stereo'] = (bool) (($HeaderDWORD[0] & 0x00200000) >> 21); - $thisfile_mpc_header['stream_major_version'] = ($HeaderDWORD[0] & 0x001FF800) >> 11; - $thisfile_mpc_header['stream_minor_version'] = 0; // no sub-version numbers before SV7 - $thisfile_mpc_header['max_band'] = ($HeaderDWORD[0] & 0x000007C0) >> 6; // related to lowpass frequency, not sure how it translates exactly - $thisfile_mpc_header['block_size'] = ($HeaderDWORD[0] & 0x0000003F); - - switch ($thisfile_mpc_header['stream_major_version']) { - case 4: - $thisfile_mpc_header['frame_count'] = ($HeaderDWORD[1] >> 16); - break; - - case 5: - case 6: - $thisfile_mpc_header['frame_count'] = $HeaderDWORD[1]; - break; - - default: - $ThisFileInfo['error'] = 'Expecting 4, 5 or 6 in version field, found '.$thisfile_mpc_header['stream_major_version'].' instead'; - unset($ThisFileInfo['mpc']); - return false; - break; - } - - if (($thisfile_mpc_header['stream_major_version'] > 4) && ($thisfile_mpc_header['block_size'] != 1)) { - $ThisFileInfo['warning'][] = 'Block size expected to be 1, actual value found: '.$thisfile_mpc_header['block_size']; - } - - $thisfile_mpc_header['sample_rate'] = 44100; // AB: used by all files up to SV7 - $ThisFileInfo['audio']['sample_rate'] = $thisfile_mpc_header['sample_rate']; - $thisfile_mpc_header['samples'] = $thisfile_mpc_header['frame_count'] * 1152 * $ThisFileInfo['audio']['channels']; - - if ($thisfile_mpc_header['target_bitrate'] == 0) { - $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; - } else { - $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; - } - - $ThisFileInfo['mpc']['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 * 44100 / $thisfile_mpc_header['frame_count'] / 1152; - $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['mpc']['bitrate']; - $ThisFileInfo['audio']['encoder'] = 'SV'.$thisfile_mpc_header['stream_major_version']; - - return true; - - } else { - - $ThisFileInfo['error'][] = 'Expecting "MP+" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($MPCheaderData, $offset, 3).'"'; - unset($ThisFileInfo['fileformat']); - unset($ThisFileInfo['mpc']); - return false; - - } - - // Continue with SV7+ handling - $StreamVersionByte = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 1)); - $offset += 1; - $thisfile_mpc_header['stream_major_version'] = ($StreamVersionByte & 0x0F); - $thisfile_mpc_header['stream_minor_version'] = ($StreamVersionByte & 0xF0) >> 4; - $thisfile_mpc_header['frame_count'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 4)); - $offset += 4; - - switch ($thisfile_mpc_header['stream_major_version']) { - case 7: - //$ThisFileInfo['fileformat'] = 'SV7'; - break; - - default: - $ThisFileInfo['error'][] = 'Only Musepack SV7 supported'; - return false; - } - - $FlagsDWORD1 = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 4)); - $offset += 4; - $thisfile_mpc_header['intensity_stereo'] = (bool) (($FlagsDWORD1 & 0x80000000) >> 31); - $thisfile_mpc_header['mid_side_stereo'] = (bool) (($FlagsDWORD1 & 0x40000000) >> 30); - $thisfile_mpc_header['max_subband'] = ($FlagsDWORD1 & 0x3F000000) >> 24; - $thisfile_mpc_header['raw']['profile'] = ($FlagsDWORD1 & 0x00F00000) >> 20; - $thisfile_mpc_header['begin_loud'] = (bool) (($FlagsDWORD1 & 0x00080000) >> 19); - $thisfile_mpc_header['end_loud'] = (bool) (($FlagsDWORD1 & 0x00040000) >> 18); - $thisfile_mpc_header['raw']['sample_rate'] = ($FlagsDWORD1 & 0x00030000) >> 16; - $thisfile_mpc_header['max_level'] = ($FlagsDWORD1 & 0x0000FFFF); - - $thisfile_mpc_header['raw']['title_peak'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 2)); - $offset += 2; - $thisfile_mpc_header['raw']['title_gain'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 2), true); - $offset += 2; - - $thisfile_mpc_header['raw']['album_peak'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 2)); - $offset += 2; - $thisfile_mpc_header['raw']['album_gain'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 2), true); - $offset += 2; - - $FlagsDWORD2 = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 4)); - $offset += 4; - $thisfile_mpc_header['true_gapless'] = (bool) (($FlagsDWORD2 & 0x80000000) >> 31); - $thisfile_mpc_header['last_frame_length'] = ($FlagsDWORD2 & 0x7FF00000) >> 20; - - - $thisfile_mpc_header['raw']['not_sure_what'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 3)); - $offset += 3; - $thisfile_mpc_header['raw']['encoder_version'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 1)); - $offset += 1; - - $thisfile_mpc_header['profile'] = $this->MPCprofileNameLookup($thisfile_mpc_header['raw']['profile']); - $thisfile_mpc_header['sample_rate'] = $this->MPCfrequencyLookup($thisfile_mpc_header['raw']['sample_rate']); - if ($thisfile_mpc_header['sample_rate'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt MPC file: frequency == zero'; - return false; - } - $ThisFileInfo['audio']['sample_rate'] = $thisfile_mpc_header['sample_rate']; - $thisfile_mpc_header['samples'] = ((($thisfile_mpc_header['frame_count'] - 1) * 1152) + $thisfile_mpc_header['last_frame_length']) * $ThisFileInfo['audio']['channels']; - - $ThisFileInfo['playtime_seconds'] = ($thisfile_mpc_header['samples'] / $ThisFileInfo['audio']['channels']) / $ThisFileInfo['audio']['sample_rate']; - if ($ThisFileInfo['playtime_seconds'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt MPC file: playtime_seconds == zero'; - return false; - } - - // add size of file header to avdataoffset - calc bitrate correctly + MD5 data - $ThisFileInfo['avdataoffset'] += $thisfile_mpc_header['size']; - - $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; - - $thisfile_mpc_header['title_peak'] = $thisfile_mpc_header['raw']['title_peak']; - $thisfile_mpc_header['title_peak_db'] = $this->MPCpeakDBLookup($thisfile_mpc_header['title_peak']); - if ($thisfile_mpc_header['raw']['title_gain'] < 0) { - $thisfile_mpc_header['title_gain_db'] = (float) (32768 + $thisfile_mpc_header['raw']['title_gain']) / -100; - } else { - $thisfile_mpc_header['title_gain_db'] = (float) $thisfile_mpc_header['raw']['title_gain'] / 100; - } - - $thisfile_mpc_header['album_peak'] = $thisfile_mpc_header['raw']['album_peak']; - $thisfile_mpc_header['album_peak_db'] = $this->MPCpeakDBLookup($thisfile_mpc_header['album_peak']); - if ($thisfile_mpc_header['raw']['album_gain'] < 0) { - $thisfile_mpc_header['album_gain_db'] = (float) (32768 + $thisfile_mpc_header['raw']['album_gain']) / -100; - } else { - $thisfile_mpc_header['album_gain_db'] = (float) $thisfile_mpc_header['raw']['album_gain'] / 100;; - } - $thisfile_mpc_header['encoder_version'] = $this->MPCencoderVersionLookup($thisfile_mpc_header['raw']['encoder_version']); - - $ThisFileInfo['replay_gain']['track']['adjustment'] = $thisfile_mpc_header['title_gain_db']; - $ThisFileInfo['replay_gain']['album']['adjustment'] = $thisfile_mpc_header['album_gain_db']; - - if ($thisfile_mpc_header['title_peak'] > 0) { - $ThisFileInfo['replay_gain']['track']['peak'] = $thisfile_mpc_header['title_peak']; - } elseif (round($thisfile_mpc_header['max_level'] * 1.18) > 0) { - $ThisFileInfo['replay_gain']['track']['peak'] = getid3_lib::CastAsInt(round($thisfile_mpc_header['max_level'] * 1.18)); // why? I don't know - see mppdec.c - } - if ($thisfile_mpc_header['album_peak'] > 0) { - $ThisFileInfo['replay_gain']['album']['peak'] = $thisfile_mpc_header['album_peak']; - } - - //$ThisFileInfo['audio']['encoder'] = 'SV'.$thisfile_mpc_header['stream_major_version'].'.'.$thisfile_mpc_header['stream_minor_version'].', '.$thisfile_mpc_header['encoder_version']; - $ThisFileInfo['audio']['encoder'] = $thisfile_mpc_header['encoder_version']; - $ThisFileInfo['audio']['encoder_options'] = $thisfile_mpc_header['profile']; - - return true; - } - - function MPCprofileNameLookup($profileid) { - static $MPCprofileNameLookup = array( - 0 => 'no profile', - 1 => 'Experimental', - 2 => 'unused', - 3 => 'unused', - 4 => 'unused', - 5 => 'below Telephone (q = 0.0)', - 6 => 'below Telephone (q = 1.0)', - 7 => 'Telephone (q = 2.0)', - 8 => 'Thumb (q = 3.0)', - 9 => 'Radio (q = 4.0)', - 10 => 'Standard (q = 5.0)', - 11 => 'Extreme (q = 6.0)', - 12 => 'Insane (q = 7.0)', - 13 => 'BrainDead (q = 8.0)', - 14 => 'above BrainDead (q = 9.0)', - 15 => 'above BrainDead (q = 10.0)' - ); - return (isset($MPCprofileNameLookup[$profileid]) ? $MPCprofileNameLookup[$profileid] : 'invalid'); - } - - function MPCfrequencyLookup($frequencyid) { - static $MPCfrequencyLookup = array( - 0 => 44100, - 1 => 48000, - 2 => 37800, - 3 => 32000 - ); - return (isset($MPCfrequencyLookup[$frequencyid]) ? $MPCfrequencyLookup[$frequencyid] : 'invalid'); - } - - function MPCpeakDBLookup($intvalue) { - if ($intvalue > 0) { - return ((log10($intvalue) / log10(2)) - 15) * 6; - } - return false; - } - - function MPCencoderVersionLookup($encoderversion) { - //Encoder version * 100 (106 = 1.06) - //EncoderVersion % 10 == 0 Release (1.0) - //EncoderVersion % 2 == 0 Beta (1.06) - //EncoderVersion % 2 == 1 Alpha (1.05a...z) - - if ($encoderversion == 0) { - // very old version, not known exactly which - return 'Buschmann v1.7.0-v1.7.9 or Klemm v0.90-v1.05'; - } - - if (($encoderversion % 10) == 0) { - - // release version - return number_format($encoderversion / 100, 2); - - } elseif (($encoderversion % 2) == 0) { - - // beta version - return number_format($encoderversion / 100, 2).' beta'; - - } - - // alpha version - return number_format($encoderversion / 100, 2).' alpha'; - } - -} - - -?> \ No newline at end of file diff --git a/getid3/getid3/module.audio.ogg.php b/getid3/getid3/module.audio.ogg.php deleted file mode 100644 index f722b33..0000000 --- a/getid3/getid3/module.audio.ogg.php +++ /dev/null @@ -1,543 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.audio.ogg.php // -// module for analyzing Ogg Vorbis, OggFLAC and Speex files // -// dependencies: module.audio.flac.php // -// /// -///////////////////////////////////////////////////////////////// - -getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.flac.php', __FILE__, true); - -class getid3_ogg -{ - - function getid3_ogg(&$fd, &$ThisFileInfo) { - - $ThisFileInfo['fileformat'] = 'ogg'; - - // Warn about illegal tags - only vorbiscomments are allowed - if (isset($ThisFileInfo['id3v2'])) { - $ThisFileInfo['warning'][] = 'Illegal ID3v2 tag present.'; - } - if (isset($ThisFileInfo['id3v1'])) { - $ThisFileInfo['warning'][] = 'Illegal ID3v1 tag present.'; - } - if (isset($ThisFileInfo['ape'])) { - $ThisFileInfo['warning'][] = 'Illegal APE tag present.'; - } - - - // Page 1 - Stream Header - - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - - $oggpageinfo = getid3_ogg::ParseOggPageHeader($fd); - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo; - - if (ftell($fd) >= GETID3_FREAD_BUFFER_SIZE) { - $ThisFileInfo['error'][] = 'Could not find start of Ogg page in the first '.GETID3_FREAD_BUFFER_SIZE.' bytes (this might not be an Ogg-Vorbis file?)'; - unset($ThisFileInfo['fileformat']); - unset($ThisFileInfo['ogg']); - return false; - } - - $filedata = fread($fd, $oggpageinfo['page_length']); - $filedataoffset = 0; - - if (substr($filedata, 0, 4) == 'fLaC') { - - $ThisFileInfo['audio']['dataformat'] = 'flac'; - $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; - $ThisFileInfo['audio']['lossless'] = true; - - } elseif (substr($filedata, 1, 6) == 'vorbis') { - - $ThisFileInfo['audio']['dataformat'] = 'vorbis'; - $ThisFileInfo['audio']['lossless'] = false; - - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); - $filedataoffset += 1; - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, $filedataoffset, 6); // hard-coded to 'vorbis' - $filedataoffset += 6; - $ThisFileInfo['ogg']['bitstreamversion'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); - $filedataoffset += 4; - $ThisFileInfo['ogg']['numberofchannels'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); - $filedataoffset += 1; - $ThisFileInfo['audio']['channels'] = $ThisFileInfo['ogg']['numberofchannels']; - $ThisFileInfo['ogg']['samplerate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); - $filedataoffset += 4; - if ($ThisFileInfo['ogg']['samplerate'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt Ogg file: sample rate == zero'; - return false; - } - $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['ogg']['samplerate']; - $ThisFileInfo['ogg']['samples'] = 0; // filled in later - $ThisFileInfo['ogg']['bitrate_average'] = 0; // filled in later - $ThisFileInfo['ogg']['bitrate_max'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); - $filedataoffset += 4; - $ThisFileInfo['ogg']['bitrate_nominal'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); - $filedataoffset += 4; - $ThisFileInfo['ogg']['bitrate_min'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); - $filedataoffset += 4; - $ThisFileInfo['ogg']['blocksize_small'] = pow(2, getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)) & 0x0F); - $ThisFileInfo['ogg']['blocksize_large'] = pow(2, (getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)) & 0xF0) >> 4); - $ThisFileInfo['ogg']['stop_bit'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); // must be 1, marks end of packet - - $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; // overridden if actually abr - if ($ThisFileInfo['ogg']['bitrate_max'] == 0xFFFFFFFF) { - unset($ThisFileInfo['ogg']['bitrate_max']); - $ThisFileInfo['audio']['bitrate_mode'] = 'abr'; - } - if ($ThisFileInfo['ogg']['bitrate_nominal'] == 0xFFFFFFFF) { - unset($ThisFileInfo['ogg']['bitrate_nominal']); - } - if ($ThisFileInfo['ogg']['bitrate_min'] == 0xFFFFFFFF) { - unset($ThisFileInfo['ogg']['bitrate_min']); - $ThisFileInfo['audio']['bitrate_mode'] = 'abr'; - } - - } elseif (substr($filedata, 0, 8) == 'Speex ') { - - // http://www.speex.org/manual/node10.html - - $ThisFileInfo['audio']['dataformat'] = 'speex'; - $ThisFileInfo['mime_type'] = 'audio/speex'; - $ThisFileInfo['audio']['bitrate_mode'] = 'abr'; - $ThisFileInfo['audio']['lossless'] = false; - - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_string'] = substr($filedata, $filedataoffset, 8); // hard-coded to 'Speex ' - $filedataoffset += 8; - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version'] = substr($filedata, $filedataoffset, 20); - $filedataoffset += 20; - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version_id'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); - $filedataoffset += 4; - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['header_size'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); - $filedataoffset += 4; - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['rate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); - $filedataoffset += 4; - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); - $filedataoffset += 4; - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode_bitstream_version'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); - $filedataoffset += 4; - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['nb_channels'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); - $filedataoffset += 4; - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['bitrate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); - $filedataoffset += 4; - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['framesize'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); - $filedataoffset += 4; - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['vbr'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); - $filedataoffset += 4; - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['frames_per_packet'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); - $filedataoffset += 4; - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['extra_headers'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); - $filedataoffset += 4; - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved1'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); - $filedataoffset += 4; - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved2'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); - $filedataoffset += 4; - - $ThisFileInfo['speex']['speex_version'] = trim($ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version']); - $ThisFileInfo['speex']['sample_rate'] = $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['rate']; - $ThisFileInfo['speex']['channels'] = $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['nb_channels']; - $ThisFileInfo['speex']['vbr'] = (bool) $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['vbr']; - $ThisFileInfo['speex']['band_type'] = getid3_ogg::SpeexBandModeLookup($ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode']); - - $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['speex']['sample_rate']; - $ThisFileInfo['audio']['channels'] = $ThisFileInfo['speex']['channels']; - if ($ThisFileInfo['speex']['vbr']) { - $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; - } - - } else { - - $ThisFileInfo['error'][] = 'Expecting either "Speex " or "vorbis" identifier strings, found neither'; - unset($ThisFileInfo['ogg']); - unset($ThisFileInfo['mime_type']); - return false; - - } - - - // Page 2 - Comment Header - - $oggpageinfo = getid3_ogg::ParseOggPageHeader($fd); - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo; - - switch ($ThisFileInfo['audio']['dataformat']) { - - case 'vorbis': - $filedata = fread($fd, $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']); - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($filedata, 0, 1)); - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, 1, 6); // hard-coded to 'vorbis' - - getid3_ogg::ParseVorbisCommentsFilepointer($fd, $ThisFileInfo); - break; - - case 'flac': - if (!getid3_flac::FLACparseMETAdata($fd, $ThisFileInfo)) { - $ThisFileInfo['error'][] = 'Failed to parse FLAC headers'; - return false; - } - break; - - case 'speex': - fseek($fd, $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length'], SEEK_CUR); - getid3_ogg::ParseVorbisCommentsFilepointer($fd, $ThisFileInfo); - break; - - } - - - - // Last Page - Number of Samples - - fseek($fd, max($ThisFileInfo['avdataend'] - GETID3_FREAD_BUFFER_SIZE, 0), SEEK_SET); - $LastChunkOfOgg = strrev(fread($fd, GETID3_FREAD_BUFFER_SIZE)); - if ($LastOggSpostion = strpos($LastChunkOfOgg, 'SggO')) { - fseek($fd, $ThisFileInfo['avdataend'] - ($LastOggSpostion + strlen('SggO')), SEEK_SET); - $ThisFileInfo['avdataend'] = ftell($fd); - $ThisFileInfo['ogg']['pageheader']['eos'] = getid3_ogg::ParseOggPageHeader($fd); - $ThisFileInfo['ogg']['samples'] = $ThisFileInfo['ogg']['pageheader']['eos']['pcm_abs_position']; - if ($ThisFileInfo['ogg']['samples'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt Ogg file: eos.number of samples == zero'; - return false; - } - $ThisFileInfo['ogg']['bitrate_average'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / ($ThisFileInfo['ogg']['samples'] / $ThisFileInfo['audio']['sample_rate']); - } - - if (!empty($ThisFileInfo['ogg']['bitrate_average'])) { - $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['ogg']['bitrate_average']; - } elseif (!empty($ThisFileInfo['ogg']['bitrate_nominal'])) { - $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['ogg']['bitrate_nominal']; - } elseif (!empty($ThisFileInfo['ogg']['bitrate_min']) && !empty($ThisFileInfo['ogg']['bitrate_max'])) { - $ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['ogg']['bitrate_min'] + $ThisFileInfo['ogg']['bitrate_max']) / 2; - } - if (isset($ThisFileInfo['audio']['bitrate']) && !isset($ThisFileInfo['playtime_seconds'])) { - if ($ThisFileInfo['audio']['bitrate'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt Ogg file: bitrate_audio == zero'; - return false; - } - $ThisFileInfo['playtime_seconds'] = (float) ((($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['audio']['bitrate']); - } - - if (isset($ThisFileInfo['ogg']['vendor'])) { - $ThisFileInfo['audio']['encoder'] = preg_replace('/^Encoded with /', '', $ThisFileInfo['ogg']['vendor']); - - // Vorbis only - if ($ThisFileInfo['audio']['dataformat'] == 'vorbis') { - - // Vorbis 1.0 starts with Xiph.Org - if (preg_match('/^Xiph.Org/', $ThisFileInfo['audio']['encoder'])) { - - if ($ThisFileInfo['audio']['bitrate_mode'] == 'abr') { - - // Set -b 128 on abr files - $ThisFileInfo['audio']['encoder_options'] = '-b '.round($ThisFileInfo['ogg']['bitrate_nominal'] / 1000); - - } elseif (($ThisFileInfo['audio']['bitrate_mode'] == 'vbr') && ($ThisFileInfo['audio']['channels'] == 2) && ($ThisFileInfo['audio']['sample_rate'] >= 44100) && ($ThisFileInfo['audio']['sample_rate'] <= 48000)) { - // Set -q N on vbr files - $ThisFileInfo['audio']['encoder_options'] = '-q '.$this->get_quality_from_nominal_bitrate($ThisFileInfo['ogg']['bitrate_nominal']); - - } - } - - if (empty($ThisFileInfo['audio']['encoder_options']) && !empty($ThisFileInfo['ogg']['bitrate_nominal'])) { - $ThisFileInfo['audio']['encoder_options'] = 'Nominal bitrate: '.intval(round($ThisFileInfo['ogg']['bitrate_nominal'] / 1000)).'kbps'; - } - } - } - - return true; - } - - - function ParseOggPageHeader(&$fd) { - // http://xiph.org/ogg/vorbis/doc/framing.html - $oggheader['page_start_offset'] = ftell($fd); // where we started from in the file - - $filedata = fread($fd, GETID3_FREAD_BUFFER_SIZE); - $filedataoffset = 0; - while ((substr($filedata, $filedataoffset++, 4) != 'OggS')) { - if ((ftell($fd) - $oggheader['page_start_offset']) >= GETID3_FREAD_BUFFER_SIZE) { - // should be found before here - return false; - } - if ((($filedataoffset + 28) > strlen($filedata)) || (strlen($filedata) < 28)) { - if (feof($fd) || (($filedata .= fread($fd, GETID3_FREAD_BUFFER_SIZE)) === false)) { - // get some more data, unless eof, in which case fail - return false; - } - } - } - $filedataoffset += strlen('OggS') - 1; // page, delimited by 'OggS' - - $oggheader['stream_structver'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); - $filedataoffset += 1; - $oggheader['flags_raw'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); - $filedataoffset += 1; - $oggheader['flags']['fresh'] = (bool) ($oggheader['flags_raw'] & 0x01); // fresh packet - $oggheader['flags']['bos'] = (bool) ($oggheader['flags_raw'] & 0x02); // first page of logical bitstream (bos) - $oggheader['flags']['eos'] = (bool) ($oggheader['flags_raw'] & 0x04); // last page of logical bitstream (eos) - - $oggheader['pcm_abs_position'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8)); - $filedataoffset += 8; - $oggheader['stream_serialno'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); - $filedataoffset += 4; - $oggheader['page_seqno'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); - $filedataoffset += 4; - $oggheader['page_checksum'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); - $filedataoffset += 4; - $oggheader['page_segments'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); - $filedataoffset += 1; - $oggheader['page_length'] = 0; - for ($i = 0; $i < $oggheader['page_segments']; $i++) { - $oggheader['segment_table'][$i] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); - $filedataoffset += 1; - $oggheader['page_length'] += $oggheader['segment_table'][$i]; - } - $oggheader['header_end_offset'] = $oggheader['page_start_offset'] + $filedataoffset; - $oggheader['page_end_offset'] = $oggheader['header_end_offset'] + $oggheader['page_length']; - fseek($fd, $oggheader['header_end_offset'], SEEK_SET); - - return $oggheader; - } - - - function ParseVorbisCommentsFilepointer(&$fd, &$ThisFileInfo) { - - $OriginalOffset = ftell($fd); - $CommentStartOffset = $OriginalOffset; - $commentdataoffset = 0; - $VorbisCommentPage = 1; - - switch ($ThisFileInfo['audio']['dataformat']) { - case 'vorbis': - $CommentStartOffset = $ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage]['page_start_offset']; // Second Ogg page, after header block - fseek($fd, $CommentStartOffset, SEEK_SET); - $commentdataoffset = 27 + $ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage]['page_segments']; - $commentdata = fread($fd, getid3_ogg::OggPageSegmentLength($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage], 1) + $commentdataoffset); - - $commentdataoffset += (strlen('vorbis') + 1); - break; - - case 'flac': - fseek($fd, $ThisFileInfo['flac']['VORBIS_COMMENT']['raw']['offset'] + 4, SEEK_SET); - $commentdata = fread($fd, $ThisFileInfo['flac']['VORBIS_COMMENT']['raw']['block_length']); - break; - - case 'speex': - $CommentStartOffset = $ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage]['page_start_offset']; // Second Ogg page, after header block - fseek($fd, $CommentStartOffset, SEEK_SET); - $commentdataoffset = 27 + $ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage]['page_segments']; - $commentdata = fread($fd, getid3_ogg::OggPageSegmentLength($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage], 1) + $commentdataoffset); - break; - - default: - return false; - break; - } - - $VendorSize = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4)); - $commentdataoffset += 4; - - $ThisFileInfo['ogg']['vendor'] = substr($commentdata, $commentdataoffset, $VendorSize); - $commentdataoffset += $VendorSize; - - $CommentsCount = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4)); - $commentdataoffset += 4; - $ThisFileInfo['avdataoffset'] = $CommentStartOffset + $commentdataoffset; - - $basicfields = array('TITLE', 'ARTIST', 'ALBUM', 'TRACKNUMBER', 'GENRE', 'DATE', 'DESCRIPTION', 'COMMENT'); - for ($i = 0; $i < $CommentsCount; $i++) { - - $ThisFileInfo['ogg']['comments_raw'][$i]['dataoffset'] = $CommentStartOffset + $commentdataoffset; - - if (ftell($fd) < ($ThisFileInfo['ogg']['comments_raw'][$i]['dataoffset'] + 4)) { - $VorbisCommentPage++; - - $oggpageinfo = getid3_ogg::ParseOggPageHeader($fd); - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo; - - // First, save what we haven't read yet - $AsYetUnusedData = substr($commentdata, $commentdataoffset); - - // Then take that data off the end - $commentdata = substr($commentdata, 0, $commentdataoffset); - - // Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct - $commentdata .= str_repeat("\x00", 27 + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']); - $commentdataoffset += (27 + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']); - - // Finally, stick the unused data back on the end - $commentdata .= $AsYetUnusedData; - - //$commentdata .= fread($fd, $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']); - $commentdata .= fread($fd, getid3_ogg::OggPageSegmentLength($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage], 1)); - - } - $ThisFileInfo['ogg']['comments_raw'][$i]['size'] = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4)); - - // replace avdataoffset with position just after the last vorbiscomment - $ThisFileInfo['avdataoffset'] = $ThisFileInfo['ogg']['comments_raw'][$i]['dataoffset'] + $ThisFileInfo['ogg']['comments_raw'][$i]['size'] + 4; - - $commentdataoffset += 4; - while ((strlen($commentdata) - $commentdataoffset) < $ThisFileInfo['ogg']['comments_raw'][$i]['size']) { - if (($ThisFileInfo['ogg']['comments_raw'][$i]['size'] > $ThisFileInfo['avdataend']) || ($ThisFileInfo['ogg']['comments_raw'][$i]['size'] < 0)) { - $ThisFileInfo['error'][] = 'Invalid Ogg comment size (comment #'.$i.', claims to be '.number_format($ThisFileInfo['ogg']['comments_raw'][$i]['size']).' bytes) - aborting reading comments'; - break 2; - } - - $VorbisCommentPage++; - - $oggpageinfo = getid3_ogg::ParseOggPageHeader($fd); - $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo; - - // First, save what we haven't read yet - $AsYetUnusedData = substr($commentdata, $commentdataoffset); - - // Then take that data off the end - $commentdata = substr($commentdata, 0, $commentdataoffset); - - // Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct - $commentdata .= str_repeat("\x00", 27 + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']); - $commentdataoffset += (27 + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']); - - // Finally, stick the unused data back on the end - $commentdata .= $AsYetUnusedData; - - //$commentdata .= fread($fd, $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']); - $commentdata .= fread($fd, getid3_ogg::OggPageSegmentLength($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage], 1)); - - //$filebaseoffset += $oggpageinfo['header_end_offset'] - $oggpageinfo['page_start_offset']; - } - $commentstring = substr($commentdata, $commentdataoffset, $ThisFileInfo['ogg']['comments_raw'][$i]['size']); - $commentdataoffset += $ThisFileInfo['ogg']['comments_raw'][$i]['size']; - - if (!$commentstring) { - - // no comment? - $ThisFileInfo['warning'][] = 'Blank Ogg comment ['.$i.']'; - - } elseif (strstr($commentstring, '=')) { - - $commentexploded = explode('=', $commentstring, 2); - $ThisFileInfo['ogg']['comments_raw'][$i]['key'] = strtoupper($commentexploded[0]); - $ThisFileInfo['ogg']['comments_raw'][$i]['value'] = @$commentexploded[1]; - $ThisFileInfo['ogg']['comments_raw'][$i]['data'] = base64_decode($ThisFileInfo['ogg']['comments_raw'][$i]['value']); - - $ThisFileInfo['ogg']['comments'][strtolower($ThisFileInfo['ogg']['comments_raw'][$i]['key'])][] = $ThisFileInfo['ogg']['comments_raw'][$i]['value']; - - $imagechunkcheck = getid3_lib::GetDataImageSize($ThisFileInfo['ogg']['comments_raw'][$i]['data']); - $ThisFileInfo['ogg']['comments_raw'][$i]['image_mime'] = getid3_lib::image_type_to_mime_type($imagechunkcheck[2]); - if (!$ThisFileInfo['ogg']['comments_raw'][$i]['image_mime'] || ($ThisFileInfo['ogg']['comments_raw'][$i]['image_mime'] == 'application/octet-stream')) { - unset($ThisFileInfo['ogg']['comments_raw'][$i]['image_mime']); - unset($ThisFileInfo['ogg']['comments_raw'][$i]['data']); - } - - } else { - - $ThisFileInfo['warning'][] = '[known problem with CDex >= v1.40, < v1.50b7] Invalid Ogg comment name/value pair ['.$i.']: '.$commentstring; - - } - } - - - // Replay Gain Adjustment - // http://privatewww.essex.ac.uk/~djmrob/replaygain/ - if (isset($ThisFileInfo['ogg']['comments']) && is_array($ThisFileInfo['ogg']['comments'])) { - foreach ($ThisFileInfo['ogg']['comments'] as $index => $commentvalue) { - switch ($index) { - case 'rg_audiophile': - case 'replaygain_album_gain': - $ThisFileInfo['replay_gain']['album']['adjustment'] = (double) $commentvalue[0]; - unset($ThisFileInfo['ogg']['comments'][$index]); - break; - - case 'rg_radio': - case 'replaygain_track_gain': - $ThisFileInfo['replay_gain']['track']['adjustment'] = (double) $commentvalue[0]; - unset($ThisFileInfo['ogg']['comments'][$index]); - break; - - case 'replaygain_album_peak': - $ThisFileInfo['replay_gain']['album']['peak'] = (double) $commentvalue[0]; - unset($ThisFileInfo['ogg']['comments'][$index]); - break; - - case 'rg_peak': - case 'replaygain_track_peak': - $ThisFileInfo['replay_gain']['track']['peak'] = (double) $commentvalue[0]; - unset($ThisFileInfo['ogg']['comments'][$index]); - break; - - - default: - // do nothing - break; - } - } - } - - fseek($fd, $OriginalOffset, SEEK_SET); - - return true; - } - - function SpeexBandModeLookup($mode) { - static $SpeexBandModeLookup = array(); - if (empty($SpeexBandModeLookup)) { - $SpeexBandModeLookup[0] = 'narrow'; - $SpeexBandModeLookup[1] = 'wide'; - $SpeexBandModeLookup[2] = 'ultra-wide'; - } - return (isset($SpeexBandModeLookup[$mode]) ? $SpeexBandModeLookup[$mode] : null); - } - - - function OggPageSegmentLength($OggInfoArray, $SegmentNumber=1) { - for ($i = 0; $i < $SegmentNumber; $i++) { - $segmentlength = 0; - foreach ($OggInfoArray['segment_table'] as $key => $value) { - $segmentlength += $value; - if ($value < 255) { - break; - } - } - } - return $segmentlength; - } - - - function get_quality_from_nominal_bitrate($nominal_bitrate) { - - // decrease precision - $nominal_bitrate = $nominal_bitrate / 1000; - - if ($nominal_bitrate < 128) { - // q-1 to q4 - $qval = ($nominal_bitrate - 64) / 16; - } elseif ($nominal_bitrate < 256) { - // q4 to q8 - $qval = $nominal_bitrate / 32; - } elseif ($nominal_bitrate < 320) { - // q8 to q9 - $qval = ($nominal_bitrate + 256) / 64; - } else { - // q9 to q10 - $qval = ($nominal_bitrate + 1300) / 180; - } - //return $qval; // 5.031324 - //return intval($qval); // 5 - return round($qval, 1); // 5 or 4.9 - } - -} - -?> \ No newline at end of file diff --git a/getid3/getid3/module.audio.optimfrog.php b/getid3/getid3/module.audio.optimfrog.php deleted file mode 100644 index 3c2dfb0..0000000 --- a/getid3/getid3/module.audio.optimfrog.php +++ /dev/null @@ -1,408 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.audio.optimfrog.php // -// module for analyzing OptimFROG audio files // -// dependencies: module.audio.riff.php // -// /// -///////////////////////////////////////////////////////////////// - -getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true); - -class getid3_optimfrog -{ - - function getid3_optimfrog(&$fd, &$ThisFileInfo) { - $ThisFileInfo['fileformat'] = 'ofr'; - $ThisFileInfo['audio']['dataformat'] = 'ofr'; - $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; - $ThisFileInfo['audio']['lossless'] = true; - - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $OFRheader = fread($fd, 8); - if (substr($OFRheader, 0, 5) == '*RIFF') { - - return $this->ParseOptimFROGheader42($fd, $ThisFileInfo); - - } elseif (substr($OFRheader, 0, 3) == 'OFR') { - - return $this->ParseOptimFROGheader45($fd, $ThisFileInfo); - - } - - $ThisFileInfo['error'][] = 'Expecting "*RIFF" or "OFR " at offset '.$ThisFileInfo['avdataoffset'].', found "'.$OFRheader.'"'; - unset($ThisFileInfo['fileformat']); - return false; - } - - - function ParseOptimFROGheader42(&$fd, &$ThisFileInfo) { - // for fileformat of v4.21 and older - - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $OptimFROGheaderData = fread($fd, 45); - $ThisFileInfo['avdataoffset'] = 45; - - $OptimFROGencoderVersion_raw = getid3_lib::LittleEndian2Int(substr($OptimFROGheaderData, 0, 1)); - $OptimFROGencoderVersion_major = floor($OptimFROGencoderVersion_raw / 10); - $OptimFROGencoderVersion_minor = $OptimFROGencoderVersion_raw - ($OptimFROGencoderVersion_major * 10); - $RIFFdata = substr($OptimFROGheaderData, 1, 44); - $OrignalRIFFheaderSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 4, 4)) + 8; - $OrignalRIFFdataSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 40, 4)) + 44; - - if ($OrignalRIFFheaderSize > $OrignalRIFFdataSize) { - $ThisFileInfo['avdataend'] -= ($OrignalRIFFheaderSize - $OrignalRIFFdataSize); - fseek($fd, $ThisFileInfo['avdataend'], SEEK_SET); - $RIFFdata .= fread($fd, $OrignalRIFFheaderSize - $OrignalRIFFdataSize); - } - - // move the data chunk after all other chunks (if any) - // so that the RIFF parser doesn't see EOF when trying - // to skip over the data chunk - $RIFFdata = substr($RIFFdata, 0, 36).substr($RIFFdata, 44).substr($RIFFdata, 36, 8); - getid3_riff::ParseRIFFdata($RIFFdata, $ThisFileInfo); - - $ThisFileInfo['audio']['encoder'] = 'OptimFROG '.$OptimFROGencoderVersion_major.'.'.$OptimFROGencoderVersion_minor; - $ThisFileInfo['audio']['channels'] = $ThisFileInfo['riff']['audio'][0]['channels']; - $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['riff']['audio'][0]['sample_rate']; - $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['riff']['audio'][0]['bits_per_sample']; - $ThisFileInfo['playtime_seconds'] = $OrignalRIFFdataSize / ($ThisFileInfo['audio']['channels'] * $ThisFileInfo['audio']['sample_rate'] * ($ThisFileInfo['audio']['bits_per_sample'] / 8)); - $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; - - return true; - } - - - function ParseOptimFROGheader45(&$fd, &$ThisFileInfo) { - // for fileformat of v4.50a and higher - - $RIFFdata = ''; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - while (!feof($fd) && (ftell($fd) < $ThisFileInfo['avdataend'])) { - $BlockOffset = ftell($fd); - $BlockData = fread($fd, 8); - $offset = 8; - $BlockName = substr($BlockData, 0, 4); - $BlockSize = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 4)); - - if ($BlockName == 'OFRX') { - $BlockName = 'OFR '; - } - if (!isset($ThisFileInfo['ofr'][$BlockName])) { - $ThisFileInfo['ofr'][$BlockName] = array(); - } - $thisfile_ofr_thisblock = &$ThisFileInfo['ofr'][$BlockName]; - - switch ($BlockName) { - case 'OFR ': - - // shortcut - $thisfile_ofr_thisblock['offset'] = $BlockOffset; - $thisfile_ofr_thisblock['size'] = $BlockSize; - - $ThisFileInfo['audio']['encoder'] = 'OptimFROG 4.50 alpha'; - switch ($BlockSize) { - case 12: - case 15: - // good - break; - - default: - $ThisFileInfo['warning'][] = '"'.$BlockName.'" contains more data than expected (expected 12 or 15 bytes, found '.$BlockSize.' bytes)'; - break; - } - $BlockData .= fread($fd, $BlockSize); - - $thisfile_ofr_thisblock['total_samples'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 6)); - $offset += 6; - $thisfile_ofr_thisblock['raw']['sample_type'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1)); - $thisfile_ofr_thisblock['sample_type'] = $this->OptimFROGsampleTypeLookup($thisfile_ofr_thisblock['raw']['sample_type']); - $offset += 1; - $thisfile_ofr_thisblock['channel_config'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1)); - $thisfile_ofr_thisblock['channels'] = $thisfile_ofr_thisblock['channel_config']; - $offset += 1; - $thisfile_ofr_thisblock['sample_rate'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 4)); - $offset += 4; - - if ($BlockSize > 12) { - - // OFR 4.504b or higher - $thisfile_ofr_thisblock['channels'] = $this->OptimFROGchannelConfigNumChannelsLookup($thisfile_ofr_thisblock['channel_config']); - $thisfile_ofr_thisblock['raw']['encoder_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 2)); - $thisfile_ofr_thisblock['encoder'] = $this->OptimFROGencoderNameLookup($thisfile_ofr_thisblock['raw']['encoder_id']); - $offset += 2; - $thisfile_ofr_thisblock['raw']['compression'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1)); - $thisfile_ofr_thisblock['compression'] = $this->OptimFROGcompressionLookup($thisfile_ofr_thisblock['raw']['compression']); - $thisfile_ofr_thisblock['speedup'] = $this->OptimFROGspeedupLookup($thisfile_ofr_thisblock['raw']['compression']); - $offset += 1; - - $ThisFileInfo['audio']['encoder'] = 'OptimFROG '.$thisfile_ofr_thisblock['encoder']; - $ThisFileInfo['audio']['encoder_options'] = '--mode '.$thisfile_ofr_thisblock['compression']; - - if ((($thisfile_ofr_thisblock['raw']['encoder_id'] & 0xF0) >> 4) == 7) { // v4.507 - if (strtolower(getid3_lib::fileextension($ThisFileInfo['filename'])) == 'ofs') { - // OptimFROG DualStream format is lossy, but as of v4.507 there is no way to tell the difference - // between lossless and lossy other than the file extension. - $ThisFileInfo['audio']['dataformat'] = 'ofs'; - $ThisFileInfo['audio']['lossless'] = true; - } - } - - } - - $ThisFileInfo['audio']['channels'] = $thisfile_ofr_thisblock['channels']; - $ThisFileInfo['audio']['sample_rate'] = $thisfile_ofr_thisblock['sample_rate']; - $ThisFileInfo['audio']['bits_per_sample'] = $this->OptimFROGbitsPerSampleTypeLookup($thisfile_ofr_thisblock['raw']['sample_type']); - break; - - - case 'COMP': - // unlike other block types, there CAN be multiple COMP blocks - - $COMPdata['offset'] = $BlockOffset; - $COMPdata['size'] = $BlockSize; - - if ($ThisFileInfo['avdataoffset'] == 0) { - $ThisFileInfo['avdataoffset'] = $BlockOffset; - } - - // Only interested in first 14 bytes (only first 12 needed for v4.50 alpha), not actual audio data - $BlockData .= fread($fd, 14); - fseek($fd, $BlockSize - 14, SEEK_CUR); - - $COMPdata['crc_32'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 4)); - $offset += 4; - $COMPdata['sample_count'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 4)); - $offset += 4; - $COMPdata['raw']['sample_type'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1)); - $COMPdata['sample_type'] = $this->OptimFROGsampleTypeLookup($COMPdata['raw']['sample_type']); - $offset += 1; - $COMPdata['raw']['channel_configuration'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1)); - $COMPdata['channel_configuration'] = $this->OptimFROGchannelConfigurationLookup($COMPdata['raw']['channel_configuration']); - $offset += 1; - $COMPdata['raw']['algorithm_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 2)); - //$COMPdata['algorithm'] = OptimFROGalgorithmNameLookup($COMPdata['raw']['algorithm_id']); - $offset += 2; - - if ($ThisFileInfo['ofr']['OFR ']['size'] > 12) { - - // OFR 4.504b or higher - $COMPdata['raw']['encoder_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 2)); - $COMPdata['encoder'] = $this->OptimFROGencoderNameLookup($COMPdata['raw']['encoder_id']); - $offset += 2; - - } - - if ($COMPdata['crc_32'] == 0x454E4F4E) { - // ASCII value of 'NONE' - placeholder value in v4.50a - $COMPdata['crc_32'] = false; - } - - $thisfile_ofr_thisblock[] = $COMPdata; - break; - - case 'HEAD': - $thisfile_ofr_thisblock['offset'] = $BlockOffset; - $thisfile_ofr_thisblock['size'] = $BlockSize; - - $RIFFdata .= fread($fd, $BlockSize); - break; - - case 'TAIL': - $thisfile_ofr_thisblock['offset'] = $BlockOffset; - $thisfile_ofr_thisblock['size'] = $BlockSize; - - if ($BlockSize > 0) { - $RIFFdata .= fread($fd, $BlockSize); - } - break; - - case 'RECV': - // block contains no useful meta data - simply note and skip - - $thisfile_ofr_thisblock['offset'] = $BlockOffset; - $thisfile_ofr_thisblock['size'] = $BlockSize; - - fseek($fd, $BlockSize, SEEK_CUR); - break; - - - case 'APET': - // APEtag v2 - - $thisfile_ofr_thisblock['offset'] = $BlockOffset; - $thisfile_ofr_thisblock['size'] = $BlockSize; - $ThisFileInfo['warning'][] = 'APEtag processing inside OptimFROG not supported in this version ('.GETID3_VERSION.') of getID3()'; - - fseek($fd, $BlockSize, SEEK_CUR); - break; - - - case 'MD5 ': - // APEtag v2 - - $thisfile_ofr_thisblock['offset'] = $BlockOffset; - $thisfile_ofr_thisblock['size'] = $BlockSize; - - if ($BlockSize == 16) { - - $thisfile_ofr_thisblock['md5_binary'] = fread($fd, $BlockSize); - $thisfile_ofr_thisblock['md5_string'] = getid3_lib::PrintHexBytes($thisfile_ofr_thisblock['md5_binary'], true, false, false); - $ThisFileInfo['md5_data_source'] = $thisfile_ofr_thisblock['md5_string']; - - } else { - - $ThisFileInfo['warning'][] = 'Expecting block size of 16 in "MD5 " chunk, found '.$BlockSize.' instead'; - fseek($fd, $BlockSize, SEEK_CUR); - - } - break; - - - default: - $thisfile_ofr_thisblock['offset'] = $BlockOffset; - $thisfile_ofr_thisblock['size'] = $BlockSize; - - $ThisFileInfo['warning'][] = 'Unhandled OptimFROG block type "'.$BlockName.'" at offset '.$thisfile_ofr_thisblock['offset']; - fseek($fd, $BlockSize, SEEK_CUR); - break; - } - } - if (isset($ThisFileInfo['ofr']['TAIL']['offset'])) { - $ThisFileInfo['avdataend'] = $ThisFileInfo['ofr']['TAIL']['offset']; - } - - $ThisFileInfo['playtime_seconds'] = (float) $ThisFileInfo['ofr']['OFR ']['total_samples'] / ($ThisFileInfo['audio']['channels'] * $ThisFileInfo['audio']['sample_rate']); - $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; - - // move the data chunk after all other chunks (if any) - // so that the RIFF parser doesn't see EOF when trying - // to skip over the data chunk - $RIFFdata = substr($RIFFdata, 0, 36).substr($RIFFdata, 44).substr($RIFFdata, 36, 8); - getid3_riff::ParseRIFFdata($RIFFdata, $ThisFileInfo); - - return true; - } - - - function OptimFROGsampleTypeLookup($SampleType) { - static $OptimFROGsampleTypeLookup = array( - 0 => 'unsigned int (8-bit)', - 1 => 'signed int (8-bit)', - 2 => 'unsigned int (16-bit)', - 3 => 'signed int (16-bit)', - 4 => 'unsigned int (24-bit)', - 5 => 'signed int (24-bit)', - 6 => 'unsigned int (32-bit)', - 7 => 'signed int (32-bit)', - 8 => 'float 0.24 (32-bit)', - 9 => 'float 16.8 (32-bit)', - 10 => 'float 24.0 (32-bit)' - ); - return (isset($OptimFROGsampleTypeLookup[$SampleType]) ? $OptimFROGsampleTypeLookup[$SampleType] : false); - } - - function OptimFROGbitsPerSampleTypeLookup($SampleType) { - static $OptimFROGbitsPerSampleTypeLookup = array( - 0 => 8, - 1 => 8, - 2 => 16, - 3 => 16, - 4 => 24, - 5 => 24, - 6 => 32, - 7 => 32, - 8 => 32, - 9 => 32, - 10 => 32 - ); - return (isset($OptimFROGbitsPerSampleTypeLookup[$SampleType]) ? $OptimFROGbitsPerSampleTypeLookup[$SampleType] : false); - } - - function OptimFROGchannelConfigurationLookup($ChannelConfiguration) { - static $OptimFROGchannelConfigurationLookup = array( - 0 => 'mono', - 1 => 'stereo' - ); - return (isset($OptimFROGchannelConfigurationLookup[$ChannelConfiguration]) ? $OptimFROGchannelConfigurationLookup[$ChannelConfiguration] : false); - } - - function OptimFROGchannelConfigNumChannelsLookup($ChannelConfiguration) { - static $OptimFROGchannelConfigNumChannelsLookup = array( - 0 => 1, - 1 => 2 - ); - return (isset($OptimFROGchannelConfigNumChannelsLookup[$ChannelConfiguration]) ? $OptimFROGchannelConfigNumChannelsLookup[$ChannelConfiguration] : false); - } - - - - // function OptimFROGalgorithmNameLookup($AlgorithID) { - // static $OptimFROGalgorithmNameLookup = array(); - // return (isset($OptimFROGalgorithmNameLookup[$AlgorithID]) ? $OptimFROGalgorithmNameLookup[$AlgorithID] : false); - // } - - - function OptimFROGencoderNameLookup($EncoderID) { - // version = (encoderID >> 4) + 4500 - // system = encoderID & 0xF - - $EncoderVersion = number_format(((($EncoderID & 0xF0) >> 4) + 4500) / 1000, 3); - $EncoderSystemID = ($EncoderID & 0x0F); - - static $OptimFROGencoderSystemLookup = array( - 0x00 => 'Windows console', - 0x01 => 'Linux console', - 0x0F => 'unknown' - ); - return $EncoderVersion.' ('.(isset($OptimFROGencoderSystemLookup[$EncoderSystemID]) ? $OptimFROGencoderSystemLookup[$EncoderSystemID] : 'undefined encoder type (0x'.dechex($EncoderSystemID).')').')'; - } - - function OptimFROGcompressionLookup($CompressionID) { - // mode = compression >> 3 - // speedup = compression & 0x07 - - $CompressionModeID = ($CompressionID & 0xF8) >> 3; - //$CompressionSpeedupID = ($CompressionID & 0x07); - - static $OptimFROGencoderModeLookup = array( - 0x00 => 'fast', - 0x01 => 'normal', - 0x02 => 'high', - 0x03 => 'extra', // extranew (some versions) - 0x04 => 'best', // bestnew (some versions) - 0x05 => 'ultra', - 0x06 => 'insane', - 0x07 => 'highnew', - 0x08 => 'extranew', - 0x09 => 'bestnew' - ); - return (isset($OptimFROGencoderModeLookup[$CompressionModeID]) ? $OptimFROGencoderModeLookup[$CompressionModeID] : 'undefined mode (0x'.str_pad(dechex($CompressionModeID), 2, '0', STR_PAD_LEFT).')'); - } - - function OptimFROGspeedupLookup($CompressionID) { - // mode = compression >> 3 - // speedup = compression & 0x07 - - //$CompressionModeID = ($CompressionID & 0xF8) >> 3; - $CompressionSpeedupID = ($CompressionID & 0x07); - - static $OptimFROGencoderSpeedupLookup = array( - 0x00 => '1x', - 0x01 => '2x', - 0x02 => '4x' - ); - - return (isset($OptimFROGencoderSpeedupLookup[$CompressionSpeedupID]) ? $OptimFROGencoderSpeedupLookup[$CompressionSpeedupID] : 'undefined mode (0x'.dechex($CompressionSpeedupID)); - } - -} - - -?> \ No newline at end of file diff --git a/getid3/getid3/module.audio.rkau.php b/getid3/getid3/module.audio.rkau.php deleted file mode 100644 index 0e344d4..0000000 --- a/getid3/getid3/module.audio.rkau.php +++ /dev/null @@ -1,92 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.audio.shorten.php // -// module for analyzing Shorten Audio files // -// dependencies: NONE // -// /// -///////////////////////////////////////////////////////////////// - - -class getid3_rkau -{ - - function getid3_rkau(&$fd, &$ThisFileInfo) { - - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $RKAUHeader = fread($fd, 20); - if (substr($RKAUHeader, 0, 3) != 'RKA') { - $ThisFileInfo['error'][] = 'Expecting "RKA" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($RKAUHeader, 0, 3).'"'; - return false; - } - - $ThisFileInfo['fileformat'] = 'rkau'; - $ThisFileInfo['audio']['dataformat'] = 'rkau'; - $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; - - $ThisFileInfo['rkau']['raw']['version'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 3, 1)); - $ThisFileInfo['rkau']['version'] = '1.'.str_pad($ThisFileInfo['rkau']['raw']['version'] & 0x0F, 2, '0', STR_PAD_LEFT); - if (($ThisFileInfo['rkau']['version'] > 1.07) || ($ThisFileInfo['rkau']['version'] < 1.06)) { - $ThisFileInfo['error'][] = 'This version of getID3() can only parse RKAU files v1.06 and 1.07 (this file is v'.$ThisFileInfo['rkau']['version'].')'; - unset($ThisFileInfo['rkau']); - return false; - } - - $ThisFileInfo['rkau']['source_bytes'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 4, 4)); - $ThisFileInfo['rkau']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 8, 4)); - $ThisFileInfo['rkau']['channels'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 12, 1)); - $ThisFileInfo['rkau']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 13, 1)); - - $ThisFileInfo['rkau']['raw']['quality'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 14, 1)); - $this->RKAUqualityLookup($ThisFileInfo['rkau']); - - $ThisFileInfo['rkau']['raw']['flags'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 15, 1)); - $ThisFileInfo['rkau']['flags']['joint_stereo'] = (bool) (!($ThisFileInfo['rkau']['raw']['flags'] & 0x01)); - $ThisFileInfo['rkau']['flags']['streaming'] = (bool) ($ThisFileInfo['rkau']['raw']['flags'] & 0x02); - $ThisFileInfo['rkau']['flags']['vrq_lossy_mode'] = (bool) ($ThisFileInfo['rkau']['raw']['flags'] & 0x04); - - if ($ThisFileInfo['rkau']['flags']['streaming']) { - $ThisFileInfo['avdataoffset'] += 20; - $ThisFileInfo['rkau']['compressed_bytes'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 16, 4)); - } else { - $ThisFileInfo['avdataoffset'] += 16; - $ThisFileInfo['rkau']['compressed_bytes'] = $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'] - 1; - } - // Note: compressed_bytes does not always equal what appears to be the actual number of compressed bytes, - // sometimes it's more, sometimes less. No idea why(?) - - $ThisFileInfo['audio']['lossless'] = $ThisFileInfo['rkau']['lossless']; - $ThisFileInfo['audio']['channels'] = $ThisFileInfo['rkau']['channels']; - $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['rkau']['bits_per_sample']; - $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['rkau']['sample_rate']; - - $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['rkau']['source_bytes'] / ($ThisFileInfo['rkau']['sample_rate'] * $ThisFileInfo['rkau']['channels'] * ($ThisFileInfo['rkau']['bits_per_sample'] / 8)); - $ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['rkau']['compressed_bytes'] * 8) / $ThisFileInfo['playtime_seconds']; - - return true; - - } - - - function RKAUqualityLookup(&$RKAUdata) { - $level = ($RKAUdata['raw']['quality'] & 0xF0) >> 4; - $quality = $RKAUdata['raw']['quality'] & 0x0F; - - $RKAUdata['lossless'] = (($quality == 0) ? true : false); - $RKAUdata['compression_level'] = $level + 1; - if (!$RKAUdata['lossless']) { - $RKAUdata['quality_setting'] = $quality; - } - - return true; - } - -} - -?> \ No newline at end of file diff --git a/getid3/getid3/module.audio.shorten.php b/getid3/getid3/module.audio.shorten.php deleted file mode 100644 index 3a09bdd..0000000 --- a/getid3/getid3/module.audio.shorten.php +++ /dev/null @@ -1,179 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.audio.shorten.php // -// module for analyzing Shorten Audio files // -// dependencies: NONE // -// /// -///////////////////////////////////////////////////////////////// - - -class getid3_shorten -{ - - function getid3_shorten(&$fd, &$ThisFileInfo) { - - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - - $ShortenHeader = fread($fd, 8); - if (substr($ShortenHeader, 0, 4) != 'ajkg') { - $ThisFileInfo['error'][] = 'Expecting "ajkg" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($ShortenHeader, 0, 4).'"'; - return false; - } - $ThisFileInfo['fileformat'] = 'shn'; - $ThisFileInfo['audio']['dataformat'] = 'shn'; - $ThisFileInfo['audio']['lossless'] = true; - $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; - - $ThisFileInfo['shn']['version'] = getid3_lib::LittleEndian2Int(substr($ShortenHeader, 4, 1)); - - fseek($fd, $ThisFileInfo['avdataend'] - 12, SEEK_SET); - $SeekTableSignatureTest = fread($fd, 12); - $ThisFileInfo['shn']['seektable']['present'] = (bool) (substr($SeekTableSignatureTest, 4, 8) == 'SHNAMPSK'); - if ($ThisFileInfo['shn']['seektable']['present']) { - $ThisFileInfo['shn']['seektable']['length'] = getid3_lib::LittleEndian2Int(substr($SeekTableSignatureTest, 0, 4)); - $ThisFileInfo['shn']['seektable']['offset'] = $ThisFileInfo['avdataend'] - $ThisFileInfo['shn']['seektable']['length']; - fseek($fd, $ThisFileInfo['shn']['seektable']['offset'], SEEK_SET); - $SeekTableMagic = fread($fd, 4); - if ($SeekTableMagic != 'SEEK') { - - $ThisFileInfo['error'][] = 'Expecting "SEEK" at offset '.$ThisFileInfo['shn']['seektable']['offset'].', found "'.$SeekTableMagic.'"'; - return false; - - } else { - - // typedef struct tag_TSeekEntry - // { - // unsigned long SampleNumber; - // unsigned long SHNFileByteOffset; - // unsigned long SHNLastBufferReadPosition; - // unsigned short SHNByteGet; - // unsigned short SHNBufferOffset; - // unsigned short SHNFileBitOffset; - // unsigned long SHNGBuffer; - // unsigned short SHNBitShift; - // long CBuf0[3]; - // long CBuf1[3]; - // long Offset0[4]; - // long Offset1[4]; - // }TSeekEntry; - - $SeekTableData = fread($fd, $ThisFileInfo['shn']['seektable']['length'] - 16); - $ThisFileInfo['shn']['seektable']['entry_count'] = floor(strlen($SeekTableData) / 80); - //$ThisFileInfo['shn']['seektable']['entries'] = array(); - //$SeekTableOffset = 0; - //for ($i = 0; $i < $ThisFileInfo['shn']['seektable']['entry_count']; $i++) { - // $SeekTableEntry['sample_number'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); - // $SeekTableOffset += 4; - // $SeekTableEntry['shn_file_byte_offset'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); - // $SeekTableOffset += 4; - // $SeekTableEntry['shn_last_buffer_read_position'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); - // $SeekTableOffset += 4; - // $SeekTableEntry['shn_byte_get'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2)); - // $SeekTableOffset += 2; - // $SeekTableEntry['shn_buffer_offset'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2)); - // $SeekTableOffset += 2; - // $SeekTableEntry['shn_file_bit_offset'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2)); - // $SeekTableOffset += 2; - // $SeekTableEntry['shn_gbuffer'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); - // $SeekTableOffset += 4; - // $SeekTableEntry['shn_bit_shift'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2)); - // $SeekTableOffset += 2; - // for ($j = 0; $j < 3; $j++) { - // $SeekTableEntry['cbuf0'][$j] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); - // $SeekTableOffset += 4; - // } - // for ($j = 0; $j < 3; $j++) { - // $SeekTableEntry['cbuf1'][$j] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); - // $SeekTableOffset += 4; - // } - // for ($j = 0; $j < 4; $j++) { - // $SeekTableEntry['offset0'][$j] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); - // $SeekTableOffset += 4; - // } - // for ($j = 0; $j < 4; $j++) { - // $SeekTableEntry['offset1'][$j] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); - // $SeekTableOffset += 4; - // } - // - // $ThisFileInfo['shn']['seektable']['entries'][] = $SeekTableEntry; - //} - - } - - } - - if ((bool) ini_get('safe_mode')) { - $ThisFileInfo['error'][] = 'PHP running in Safe Mode - backtick operator not available, cannot run shntool to analyze Shorten files'; - return false; - } - - if (GETID3_OS_ISWINDOWS) { - - $RequiredFiles = array('shorten.exe', 'cygwin1.dll', 'head.exe'); - foreach ($RequiredFiles as $required_file) { - if (!is_readable(GETID3_HELPERAPPSDIR.$required_file)) { - $ThisFileInfo['error'][] = GETID3_HELPERAPPSDIR.$required_file.' does not exist'; - return false; - } - } - $commandline = GETID3_HELPERAPPSDIR.'shorten.exe -x "'.$ThisFileInfo['filenamepath'].'" - | '.GETID3_HELPERAPPSDIR.'head.exe -c 44'; - $commandline = str_replace('/', '\\', $commandline); - - } else { - - static $shorten_present; - if (!isset($shorten_present)) { - $shorten_present = file_exists('/usr/local/bin/shorten') || `which shorten`; - } - if (!$shorten_present) { - $ThisFileInfo['error'][] = 'shorten binary was not found in path or /usr/local/bin'; - return false; - } - $commandline = (file_exists('/usr/local/bin/shorten') ? '/usr/local/bin/' : '' ) . 'shorten -x '.escapeshellarg($ThisFileInfo['filenamepath']).' - | head -c 44'; - - } - - $output = `$commandline`; - - if (!empty($output) && (substr($output, 12, 4) == 'fmt ')) { - - getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true); - - $DecodedWAVFORMATEX = getid3_riff::RIFFparseWAVEFORMATex(substr($output, 20, 16)); - $ThisFileInfo['audio']['channels'] = $DecodedWAVFORMATEX['channels']; - $ThisFileInfo['audio']['bits_per_sample'] = $DecodedWAVFORMATEX['bits_per_sample']; - $ThisFileInfo['audio']['sample_rate'] = $DecodedWAVFORMATEX['sample_rate']; - - if (substr($output, 36, 4) == 'data') { - - $ThisFileInfo['playtime_seconds'] = getid3_lib::LittleEndian2Int(substr($output, 40, 4)) / $DecodedWAVFORMATEX['raw']['nAvgBytesPerSec']; - - } else { - - $ThisFileInfo['error'][] = 'shorten failed to decode DATA chunk to expected location, cannot determine playtime'; - return false; - - } - - $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $ThisFileInfo['playtime_seconds']) * 8; - - } else { - - $ThisFileInfo['error'][] = 'shorten failed to decode file to WAV for parsing'; - return false; - - } - - return true; - } - -} - -?> \ No newline at end of file diff --git a/getid3/getid3/module.audio.tta.php b/getid3/getid3/module.audio.tta.php deleted file mode 100644 index 903de6b..0000000 --- a/getid3/getid3/module.audio.tta.php +++ /dev/null @@ -1,107 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.audio.tta.php // -// module for analyzing TTA Audio files // -// dependencies: NONE // -// /// -///////////////////////////////////////////////////////////////// - - -class getid3_tta -{ - - function getid3_tta(&$fd, &$ThisFileInfo) { - - $ThisFileInfo['fileformat'] = 'tta'; - $ThisFileInfo['audio']['dataformat'] = 'tta'; - $ThisFileInfo['audio']['lossless'] = true; - $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; - - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $ttaheader = fread($fd, 26); - - $ThisFileInfo['tta']['magic'] = substr($ttaheader, 0, 3); - if ($ThisFileInfo['tta']['magic'] != 'TTA') { - $ThisFileInfo['error'][] = 'Expecting "TTA" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$ThisFileInfo['tta']['magic'].'"'; - unset($ThisFileInfo['fileformat']); - unset($ThisFileInfo['audio']); - unset($ThisFileInfo['tta']); - return false; - } - - switch ($ttaheader{3}) { - case "\x01": // TTA v1.x - case "\x02": // TTA v1.x - case "\x03": // TTA v1.x - // "It was the demo-version of the TTA encoder. There is no released format with such header. TTA encoder v1 is not supported about a year." - $ThisFileInfo['tta']['major_version'] = 1; - $ThisFileInfo['avdataoffset'] += 16; - - $ThisFileInfo['tta']['compression_level'] = ord($ttaheader{3}); - $ThisFileInfo['tta']['channels'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 4, 2)); - $ThisFileInfo['tta']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 6, 2)); - $ThisFileInfo['tta']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 8, 4)); - $ThisFileInfo['tta']['samples_per_channel'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 12, 4)); - - $ThisFileInfo['audio']['encoder_options'] = '-e'.$ThisFileInfo['tta']['compression_level']; - $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['tta']['samples_per_channel'] / $ThisFileInfo['tta']['sample_rate']; - break; - - case '2': // TTA v2.x - // "I have hurried to release the TTA 2.0 encoder. Format documentation is removed from our site. This format still in development. Please wait the TTA2 format, encoder v4." - $ThisFileInfo['tta']['major_version'] = 2; - $ThisFileInfo['avdataoffset'] += 20; - - $ThisFileInfo['tta']['compression_level'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 4, 2)); - $ThisFileInfo['tta']['audio_format'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 6, 2)); - $ThisFileInfo['tta']['channels'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 8, 2)); - $ThisFileInfo['tta']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 10, 2)); - $ThisFileInfo['tta']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 12, 4)); - $ThisFileInfo['tta']['data_length'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 16, 4)); - - $ThisFileInfo['audio']['encoder_options'] = '-e'.$ThisFileInfo['tta']['compression_level']; - $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['tta']['data_length'] / $ThisFileInfo['tta']['sample_rate']; - break; - - case '1': // TTA v3.x - // "This is a first stable release of the TTA format. It will be supported by the encoders v3 or higher." - $ThisFileInfo['tta']['major_version'] = 3; - $ThisFileInfo['avdataoffset'] += 26; - - $ThisFileInfo['tta']['audio_format'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 4, 2)); // getid3_riff::RIFFwFormatTagLookup() - $ThisFileInfo['tta']['channels'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 6, 2)); - $ThisFileInfo['tta']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 8, 2)); - $ThisFileInfo['tta']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 10, 4)); - $ThisFileInfo['tta']['data_length'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 14, 4)); - $ThisFileInfo['tta']['crc32_footer'] = substr($ttaheader, 18, 4); - $ThisFileInfo['tta']['seek_point'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 22, 4)); - - $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['tta']['data_length'] / $ThisFileInfo['tta']['sample_rate']; - break; - - default: - $ThisFileInfo['error'][] = 'This version of getID3() only knows how to handle TTA v1 and v2 - it may not work correctly with this file which appears to be TTA v'.$ttaheader{3}; - return false; - break; - } - - $ThisFileInfo['audio']['encoder'] = 'TTA v'.$ThisFileInfo['tta']['major_version']; - $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['tta']['bits_per_sample']; - $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['tta']['sample_rate']; - $ThisFileInfo['audio']['channels'] = $ThisFileInfo['tta']['channels']; - $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; - - return true; - } - -} - - -?> \ No newline at end of file diff --git a/getid3/getid3/module.audio.voc.php b/getid3/getid3/module.audio.voc.php deleted file mode 100644 index e93b44f..0000000 --- a/getid3/getid3/module.audio.voc.php +++ /dev/null @@ -1,205 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.audio.voc.php // -// module for analyzing Creative VOC Audio files // -// dependencies: NONE // -// /// -///////////////////////////////////////////////////////////////// - - -class getid3_voc -{ - - function getid3_voc(&$fd, &$ThisFileInfo) { - - $OriginalAVdataOffset = $ThisFileInfo['avdataoffset']; - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $VOCheader = fread($fd, 26); - - if (substr($VOCheader, 0, 19) != 'Creative Voice File') { - $ThisFileInfo['error'][] = 'Expecting "Creative Voice File" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($VOCheader, 0, 19).'"'; - return false; - } - - // shortcuts - $thisfile_audio = &$ThisFileInfo['audio']; - $ThisFileInfo['voc'] = array(); - $thisfile_voc = &$ThisFileInfo['voc']; - - $ThisFileInfo['fileformat'] = 'voc'; - $thisfile_audio['dataformat'] = 'voc'; - $thisfile_audio['bitrate_mode'] = 'cbr'; - $thisfile_audio['lossless'] = true; - $thisfile_audio['channels'] = 1; // might be overriden below - $thisfile_audio['bits_per_sample'] = 8; // might be overriden below - - // byte # Description - // ------ ------------------------------------------ - // 00-12 'Creative Voice File' - // 13 1A (eof to abort printing of file) - // 14-15 Offset of first datablock in .voc file (std 1A 00 in Intel Notation) - // 16-17 Version number (minor,major) (VOC-HDR puts 0A 01) - // 18-19 2's Comp of Ver. # + 1234h (VOC-HDR puts 29 11) - - $thisfile_voc['header']['datablock_offset'] = getid3_lib::LittleEndian2Int(substr($VOCheader, 20, 2)); - $thisfile_voc['header']['minor_version'] = getid3_lib::LittleEndian2Int(substr($VOCheader, 22, 1)); - $thisfile_voc['header']['major_version'] = getid3_lib::LittleEndian2Int(substr($VOCheader, 23, 1)); - - do { - - $BlockOffset = ftell($fd); - $BlockData = fread($fd, 4); - $BlockType = ord($BlockData{0}); - $BlockSize = getid3_lib::LittleEndian2Int(substr($BlockData, 1, 3)); - $ThisBlock = array(); - - @$thisfile_voc['blocktypes'][$BlockType]++; - switch ($BlockType) { - case 0: // Terminator - // do nothing, we'll break out of the loop down below - break; - - case 1: // Sound data - $BlockData .= fread($fd, 2); - if ($ThisFileInfo['avdataoffset'] <= $OriginalAVdataOffset) { - $ThisFileInfo['avdataoffset'] = ftell($fd); - } - fseek($fd, $BlockSize - 2, SEEK_CUR); - - $ThisBlock['sample_rate_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 1)); - $ThisBlock['compression_type'] = getid3_lib::LittleEndian2Int(substr($BlockData, 5, 1)); - - $ThisBlock['compression_name'] = $this->VOCcompressionTypeLookup($ThisBlock['compression_type']); - if ($ThisBlock['compression_type'] <= 3) { - $thisfile_voc['compressed_bits_per_sample'] = getid3_lib::CastAsInt(str_replace('-bit', '', $ThisBlock['compression_name'])); - } - - // Less accurate sample_rate calculation than the Extended block (#8) data (but better than nothing if Extended Block is not available) - if (empty($thisfile_audio['sample_rate'])) { - // SR byte = 256 - (1000000 / sample_rate) - $thisfile_audio['sample_rate'] = getid3_lib::trunc((1000000 / (256 - $ThisBlock['sample_rate_id'])) / $thisfile_audio['channels']); - } - break; - - case 2: // Sound continue - case 3: // Silence - case 4: // Marker - case 6: // Repeat - case 7: // End repeat - // nothing useful, just skip - fseek($fd, $BlockSize, SEEK_CUR); - break; - - case 8: // Extended - $BlockData .= fread($fd, 4); - - //00-01 Time Constant: - // Mono: 65536 - (256000000 / sample_rate) - // Stereo: 65536 - (256000000 / (sample_rate * 2)) - $ThisBlock['time_constant'] = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 2)); - $ThisBlock['pack_method'] = getid3_lib::LittleEndian2Int(substr($BlockData, 6, 1)); - $ThisBlock['stereo'] = (bool) getid3_lib::LittleEndian2Int(substr($BlockData, 7, 1)); - - $thisfile_audio['channels'] = ($ThisBlock['stereo'] ? 2 : 1); - $thisfile_audio['sample_rate'] = getid3_lib::trunc((256000000 / (65536 - $ThisBlock['time_constant'])) / $thisfile_audio['channels']); - break; - - case 9: // data block that supersedes blocks 1 and 8. Used for stereo, 16 bit - $BlockData .= fread($fd, 12); - if ($ThisFileInfo['avdataoffset'] <= $OriginalAVdataOffset) { - $ThisFileInfo['avdataoffset'] = ftell($fd); - } - fseek($fd, $BlockSize - 12, SEEK_CUR); - - $ThisBlock['sample_rate'] = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 4)); - $ThisBlock['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($BlockData, 8, 1)); - $ThisBlock['channels'] = getid3_lib::LittleEndian2Int(substr($BlockData, 9, 1)); - $ThisBlock['wFormat'] = getid3_lib::LittleEndian2Int(substr($BlockData, 10, 2)); - - $ThisBlock['compression_name'] = $this->VOCwFormatLookup($ThisBlock['wFormat']); - if ($this->VOCwFormatActualBitsPerSampleLookup($ThisBlock['wFormat'])) { - $thisfile_voc['compressed_bits_per_sample'] = $this->VOCwFormatActualBitsPerSampleLookup($ThisBlock['wFormat']); - } - - $thisfile_audio['sample_rate'] = $ThisBlock['sample_rate']; - $thisfile_audio['bits_per_sample'] = $ThisBlock['bits_per_sample']; - $thisfile_audio['channels'] = $ThisBlock['channels']; - break; - - default: - $ThisFileInfo['warning'][] = 'Unhandled block type "'.$BlockType.'" at offset '.$BlockOffset; - fseek($fd, $BlockSize, SEEK_CUR); - break; - } - - if (!empty($ThisBlock)) { - $ThisBlock['block_offset'] = $BlockOffset; - $ThisBlock['block_size'] = $BlockSize; - $ThisBlock['block_type_id'] = $BlockType; - $thisfile_voc['blocks'][] = $ThisBlock; - } - - } while (!feof($fd) && ($BlockType != 0)); - - // Terminator block doesn't have size field, so seek back 3 spaces - fseek($fd, -3, SEEK_CUR); - - ksort($thisfile_voc['blocktypes']); - - if (!empty($thisfile_voc['compressed_bits_per_sample'])) { - $ThisFileInfo['playtime_seconds'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / ($thisfile_voc['compressed_bits_per_sample'] * $thisfile_audio['channels'] * $thisfile_audio['sample_rate']); - $thisfile_audio['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; - } - - return true; - } - - function VOCcompressionTypeLookup($index) { - static $VOCcompressionTypeLookup = array( - 0 => '8-bit', - 1 => '4-bit', - 2 => '2.6-bit', - 3 => '2-bit' - ); - return (isset($VOCcompressionTypeLookup[$index]) ? $VOCcompressionTypeLookup[$index] : 'Multi DAC ('.($index - 3).') channels'); - } - - function VOCwFormatLookup($index) { - static $VOCwFormatLookup = array( - 0x0000 => '8-bit unsigned PCM', - 0x0001 => 'Creative 8-bit to 4-bit ADPCM', - 0x0002 => 'Creative 8-bit to 3-bit ADPCM', - 0x0003 => 'Creative 8-bit to 2-bit ADPCM', - 0x0004 => '16-bit signed PCM', - 0x0006 => 'CCITT a-Law', - 0x0007 => 'CCITT u-Law', - 0x2000 => 'Creative 16-bit to 4-bit ADPCM' - ); - return (isset($VOCwFormatLookup[$index]) ? $VOCwFormatLookup[$index] : false); - } - - function VOCwFormatActualBitsPerSampleLookup($index) { - static $VOCwFormatLookup = array( - 0x0000 => 8, - 0x0001 => 4, - 0x0002 => 3, - 0x0003 => 2, - 0x0004 => 16, - 0x0006 => 8, - 0x0007 => 8, - 0x2000 => 4 - ); - return (isset($VOCwFormatLookup[$index]) ? $VOCwFormatLookup[$index] : false); - } - -} - - -?> \ No newline at end of file diff --git a/getid3/getid3/module.audio.vqf.php b/getid3/getid3/module.audio.vqf.php deleted file mode 100644 index 49d4e85..0000000 --- a/getid3/getid3/module.audio.vqf.php +++ /dev/null @@ -1,159 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.audio.vqf.php // -// module for analyzing VQF audio files // -// dependencies: NONE // -// /// -///////////////////////////////////////////////////////////////// - - -class getid3_vqf -{ - function getid3_vqf(&$fd, &$ThisFileInfo) { - // based loosely on code from TTwinVQ by Jurgen Faul - // http://jfaul.de/atl or http://j-faul.virtualave.net/atl/atl.html - - $ThisFileInfo['fileformat'] = 'vqf'; - $ThisFileInfo['audio']['dataformat'] = 'vqf'; - $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; - $ThisFileInfo['audio']['lossless'] = false; - - // shortcut - $ThisFileInfo['vqf']['raw'] = array(); - $thisfile_vqf = &$ThisFileInfo['vqf']; - $thisfile_vqf_raw = &$thisfile_vqf['raw']; - - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $VQFheaderData = fread($fd, 16); - - $offset = 0; - $thisfile_vqf_raw['header_tag'] = substr($VQFheaderData, $offset, 4); - if ($thisfile_vqf_raw['header_tag'] != 'TWIN') { - $ThisFileInfo['error'][] = 'Expecting "TWIN" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$thisfile_vqf_raw['header_tag'].'"'; - unset($ThisFileInfo['vqf']); - unset($ThisFileInfo['fileformat']); - return false; - } - $offset += 4; - $thisfile_vqf_raw['version'] = substr($VQFheaderData, $offset, 8); - $offset += 8; - $thisfile_vqf_raw['size'] = getid3_lib::BigEndian2Int(substr($VQFheaderData, $offset, 4)); - $offset += 4; - - while (ftell($fd) < $ThisFileInfo['avdataend']) { - - $ChunkBaseOffset = ftell($fd); - $chunkoffset = 0; - $ChunkData = fread($fd, 8); - $ChunkName = substr($ChunkData, $chunkoffset, 4); - if ($ChunkName == 'DATA') { - $ThisFileInfo['avdataoffset'] = $ChunkBaseOffset; - break; - } - $chunkoffset += 4; - $ChunkSize = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4)); - $chunkoffset += 4; - if ($ChunkSize > ($ThisFileInfo['avdataend'] - ftell($fd))) { - $ThisFileInfo['error'][] = 'Invalid chunk size ('.$ChunkSize.') for chunk "'.$ChunkName.'" at offset '.$ChunkBaseOffset; - break; - } - if ($ChunkSize > 0) { - $ChunkData .= fread($fd, $ChunkSize); - } - - switch ($ChunkName) { - case 'COMM': - // shortcut - $thisfile_vqf['COMM'] = array(); - $thisfile_vqf_COMM = &$thisfile_vqf['COMM']; - - $thisfile_vqf_COMM['channel_mode'] = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4)); - $chunkoffset += 4; - $thisfile_vqf_COMM['bitrate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4)); - $chunkoffset += 4; - $thisfile_vqf_COMM['sample_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4)); - $chunkoffset += 4; - $thisfile_vqf_COMM['security_level'] = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4)); - $chunkoffset += 4; - - $ThisFileInfo['audio']['channels'] = $thisfile_vqf_COMM['channel_mode'] + 1; - $ThisFileInfo['audio']['sample_rate'] = $this->VQFchannelFrequencyLookup($thisfile_vqf_COMM['sample_rate']); - $ThisFileInfo['audio']['bitrate'] = $thisfile_vqf_COMM['bitrate'] * 1000; - $ThisFileInfo['audio']['encoder_options'] = 'CBR' . ceil($ThisFileInfo['audio']['bitrate']/1000); - - if ($ThisFileInfo['audio']['bitrate'] == 0) { - $ThisFileInfo['error'][] = 'Corrupt VQF file: bitrate_audio == zero'; - return false; - } - break; - - case 'NAME': - case 'AUTH': - case '(c) ': - case 'FILE': - case 'COMT': - case 'ALBM': - $thisfile_vqf['comments'][$this->VQFcommentNiceNameLookup($ChunkName)][] = trim(substr($ChunkData, 8)); - break; - - case 'DSIZ': - $thisfile_vqf['DSIZ'] = getid3_lib::BigEndian2Int(substr($ChunkData, 8, 4)); - break; - - default: - $ThisFileInfo['warning'][] = 'Unhandled chunk type "'.$ChunkName.'" at offset '.$ChunkBaseOffset; - break; - } - } - - $ThisFileInfo['playtime_seconds'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['audio']['bitrate']; - - if (isset($thisfile_vqf['DSIZ']) && (($thisfile_vqf['DSIZ'] != ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'] - strlen('DATA'))))) { - switch ($thisfile_vqf['DSIZ']) { - case 0: - case 1: - $ThisFileInfo['warning'][] = 'Invalid DSIZ value "'.$thisfile_vqf['DSIZ'].'". This is known to happen with VQF files encoded by Ahead Nero, and seems to be its way of saying this is TwinVQF v'.($thisfile_vqf['DSIZ'] + 1).'.0'; - $ThisFileInfo['audio']['encoder'] = 'Ahead Nero'; - break; - - default: - $ThisFileInfo['warning'][] = 'Probable corrupted file - should be '.$thisfile_vqf['DSIZ'].' bytes, actually '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'] - strlen('DATA')); - break; - } - } - - return true; - } - - function VQFchannelFrequencyLookup($frequencyid) { - static $VQFchannelFrequencyLookup = array( - 11 => 11025, - 22 => 22050, - 44 => 44100 - ); - return (isset($VQFchannelFrequencyLookup[$frequencyid]) ? $VQFchannelFrequencyLookup[$frequencyid] : $frequencyid * 1000); - } - - function VQFcommentNiceNameLookup($shortname) { - static $VQFcommentNiceNameLookup = array( - 'NAME' => 'title', - 'AUTH' => 'artist', - '(c) ' => 'copyright', - 'FILE' => 'filename', - 'COMT' => 'comment', - 'ALBM' => 'album' - ); - return (isset($VQFcommentNiceNameLookup[$shortname]) ? $VQFcommentNiceNameLookup[$shortname] : $shortname); - } - -} - - -?> \ No newline at end of file diff --git a/getid3/getid3/module.audio.wavpack.php b/getid3/getid3/module.audio.wavpack.php deleted file mode 100644 index 0435f08..0000000 --- a/getid3/getid3/module.audio.wavpack.php +++ /dev/null @@ -1,372 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.audio.wavpack.php // -// module for analyzing WavPack v4.0+ Audio files // -// dependencies: NONE // -// /// -///////////////////////////////////////////////////////////////// - - -class getid3_wavpack -{ - - function getid3_wavpack(&$fd, &$ThisFileInfo) { - - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - - while (true) { - - $wavpackheader = fread($fd, 32); - - if (ftell($fd) >= $ThisFileInfo['avdataend']) { - break; - } elseif (feof($fd)) { - break; - } elseif ( - (@$ThisFileInfo['wavpack']['blockheader']['total_samples'] > 0) && - (@$ThisFileInfo['wavpack']['blockheader']['block_samples'] > 0) && - (!isset($ThisFileInfo['wavpack']['riff_trailer_size']) || ($ThisFileInfo['wavpack']['riff_trailer_size'] <= 0)) && - ((@$ThisFileInfo['wavpack']['config_flags']['md5_checksum'] === false) || !empty($ThisFileInfo['md5_data_source']))) { - break; - } - - $blockheader_offset = ftell($fd) - 32; - $blockheader_magic = substr($wavpackheader, 0, 4); - $blockheader_size = getid3_lib::LittleEndian2Int(substr($wavpackheader, 4, 4)); - - if ($blockheader_magic != 'wvpk') { - $ThisFileInfo['error'][] = 'Expecting "wvpk" at offset '.$blockheader_offset.', found "'.$blockheader_magic.'"'; - if ((@$ThisFileInfo['audio']['dataformat'] != 'wavpack') && (@$ThisFileInfo['audio']['dataformat'] != 'wvc')) { - unset($ThisFileInfo['fileformat']); - unset($ThisFileInfo['audio']); - unset($ThisFileInfo['wavpack']); - } - return false; - } - - - if ((@$ThisFileInfo['wavpack']['blockheader']['block_samples'] <= 0) || - (@$ThisFileInfo['wavpack']['blockheader']['total_samples'] <= 0)) { - // Also, it is possible that the first block might not have - // any samples (block_samples == 0) and in this case you should skip blocks - // until you find one with samples because the other information (like - // total_samples) are not guaranteed to be correct until (block_samples > 0) - - // Finally, I have defined a format for files in which the length is not known - // (for example when raw files are created using pipes). In these cases - // total_samples will be -1 and you must seek to the final block to determine - // the total number of samples. - - - $ThisFileInfo['audio']['dataformat'] = 'wavpack'; - $ThisFileInfo['fileformat'] = 'wavpack'; - $ThisFileInfo['audio']['lossless'] = true; - $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; - - $ThisFileInfo['wavpack']['blockheader']['offset'] = $blockheader_offset; - $ThisFileInfo['wavpack']['blockheader']['magic'] = $blockheader_magic; - $ThisFileInfo['wavpack']['blockheader']['size'] = $blockheader_size; - - if ($ThisFileInfo['wavpack']['blockheader']['size'] >= 0x100000) { - $ThisFileInfo['error'][] = 'Expecting WavPack block size less than "0x100000", found "'.$ThisFileInfo['wavpack']['blockheader']['size'].'" at offset '.$ThisFileInfo['wavpack']['blockheader']['offset']; - if ((@$ThisFileInfo['audio']['dataformat'] != 'wavpack') && (@$ThisFileInfo['audio']['dataformat'] != 'wvc')) { - unset($ThisFileInfo['fileformat']); - unset($ThisFileInfo['audio']); - unset($ThisFileInfo['wavpack']); - } - return false; - } - - $ThisFileInfo['wavpack']['blockheader']['minor_version'] = ord($wavpackheader{8}); - $ThisFileInfo['wavpack']['blockheader']['major_version'] = ord($wavpackheader{9}); - - if (($ThisFileInfo['wavpack']['blockheader']['major_version'] != 4) || - (($ThisFileInfo['wavpack']['blockheader']['minor_version'] < 4) && - ($ThisFileInfo['wavpack']['blockheader']['minor_version'] > 16))) { - $ThisFileInfo['error'][] = 'Expecting WavPack version between "4.2" and "4.16", found version "'.$ThisFileInfo['wavpack']['blockheader']['major_version'].'.'.$ThisFileInfo['wavpack']['blockheader']['minor_version'].'" at offset '.$ThisFileInfo['wavpack']['blockheader']['offset']; - if ((@$ThisFileInfo['audio']['dataformat'] != 'wavpack') && (@$ThisFileInfo['audio']['dataformat'] != 'wvc')) { - unset($ThisFileInfo['fileformat']); - unset($ThisFileInfo['audio']); - unset($ThisFileInfo['wavpack']); - } - return false; - } - - $ThisFileInfo['wavpack']['blockheader']['track_number'] = ord($wavpackheader{10}); // unused - $ThisFileInfo['wavpack']['blockheader']['index_number'] = ord($wavpackheader{11}); // unused - $ThisFileInfo['wavpack']['blockheader']['total_samples'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 12, 4)); - $ThisFileInfo['wavpack']['blockheader']['block_index'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 16, 4)); - $ThisFileInfo['wavpack']['blockheader']['block_samples'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 20, 4)); - $ThisFileInfo['wavpack']['blockheader']['flags_raw'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 24, 4)); - $ThisFileInfo['wavpack']['blockheader']['crc'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 28, 4)); - - $ThisFileInfo['wavpack']['blockheader']['flags']['bytes_per_sample'] = 1 + ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000003); - $ThisFileInfo['wavpack']['blockheader']['flags']['mono'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000004); - $ThisFileInfo['wavpack']['blockheader']['flags']['hybrid'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000008); - $ThisFileInfo['wavpack']['blockheader']['flags']['joint_stereo'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000010); - $ThisFileInfo['wavpack']['blockheader']['flags']['cross_decorrelation'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000020); - $ThisFileInfo['wavpack']['blockheader']['flags']['hybrid_noiseshape'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000040); - $ThisFileInfo['wavpack']['blockheader']['flags']['ieee_32bit_float'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000080); - $ThisFileInfo['wavpack']['blockheader']['flags']['int_32bit'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000100); - $ThisFileInfo['wavpack']['blockheader']['flags']['hybrid_bitrate_noise'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000200); - $ThisFileInfo['wavpack']['blockheader']['flags']['hybrid_balance_noise'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000400); - $ThisFileInfo['wavpack']['blockheader']['flags']['multichannel_initial'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000800); - $ThisFileInfo['wavpack']['blockheader']['flags']['multichannel_final'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00001000); - - $ThisFileInfo['audio']['lossless'] = !$ThisFileInfo['wavpack']['blockheader']['flags']['hybrid']; - } - - while (!feof($fd) && (ftell($fd) < ($blockheader_offset + $blockheader_size + 8))) { - - $metablock = array('offset'=>ftell($fd)); - $metablockheader = fread($fd, 2); - if (feof($fd)) { - break; - } - $metablock['id'] = ord($metablockheader{0}); - $metablock['function_id'] = ($metablock['id'] & 0x3F); - $metablock['function_name'] = $this->WavPackMetablockNameLookup($metablock['function_id']); - - // The 0x20 bit in the id of the meta subblocks (which is defined as - // ID_OPTIONAL_DATA) is a permanent part of the id. The idea is that - // if a decoder encounters an id that it does not know about, it uses - // that "ID_OPTIONAL_DATA" flag to determine what to do. If it is set - // then the decoder simply ignores the metadata, but if it is zero - // then the decoder should quit because it means that an understanding - // of the metadata is required to correctly decode the audio. - $metablock['non_decoder'] = (bool) ($metablock['id'] & 0x20); - - $metablock['padded_data'] = (bool) ($metablock['id'] & 0x40); - $metablock['large_block'] = (bool) ($metablock['id'] & 0x80); - if ($metablock['large_block']) { - $metablockheader .= fread($fd, 2); - } - $metablock['size'] = getid3_lib::LittleEndian2Int(substr($metablockheader, 1)) * 2; // size is stored in words - $metablock['data'] = null; - - if ($metablock['size'] > 0) { - - switch ($metablock['function_id']) { - case 0x21: // ID_RIFF_HEADER - case 0x22: // ID_RIFF_TRAILER - case 0x23: // ID_REPLAY_GAIN - case 0x24: // ID_CUESHEET - case 0x25: // ID_CONFIG_BLOCK - case 0x26: // ID_MD5_CHECKSUM - $metablock['data'] = fread($fd, $metablock['size']); - - if ($metablock['padded_data']) { - // padded to the nearest even byte - $metablock['size']--; - $metablock['data'] = substr($metablock['data'], 0, -1); - } - break; - - case 0x00: // ID_DUMMY - case 0x01: // ID_ENCODER_INFO - case 0x02: // ID_DECORR_TERMS - case 0x03: // ID_DECORR_WEIGHTS - case 0x04: // ID_DECORR_SAMPLES - case 0x05: // ID_ENTROPY_VARS - case 0x06: // ID_HYBRID_PROFILE - case 0x07: // ID_SHAPING_WEIGHTS - case 0x08: // ID_FLOAT_INFO - case 0x09: // ID_INT32_INFO - case 0x0A: // ID_WV_BITSTREAM - case 0x0B: // ID_WVC_BITSTREAM - case 0x0C: // ID_WVX_BITSTREAM - case 0x0D: // ID_CHANNEL_INFO - fseek($fd, $metablock['offset'] + ($metablock['large_block'] ? 4 : 2) + $metablock['size'], SEEK_SET); - break; - - default: - $ThisFileInfo['warning'][] = 'Unexpected metablock type "0x'.str_pad(dechex($metablock['function_id']), 2, '0', STR_PAD_LEFT).'" at offset '.$metablock['offset']; - fseek($fd, $metablock['offset'] + ($metablock['large_block'] ? 4 : 2) + $metablock['size'], SEEK_SET); - break; - } - - switch ($metablock['function_id']) { - case 0x21: // ID_RIFF_HEADER - getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true); - $original_wav_filesize = getid3_lib::LittleEndian2Int(substr($metablock['data'], 4, 4)); - getid3_riff::ParseRIFFdata($metablock['data'], $ParsedRIFFheader); - $metablock['riff'] = $ParsedRIFFheader['riff']; - $metablock['riff']['original_filesize'] = $original_wav_filesize; - $ThisFileInfo['wavpack']['riff_trailer_size'] = $original_wav_filesize - $metablock['riff']['WAVE']['data'][0]['size'] - $metablock['riff']['header_size']; - - $ThisFileInfo['audio']['sample_rate'] = $ParsedRIFFheader['riff']['raw']['fmt ']['nSamplesPerSec']; - $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['wavpack']['blockheader']['total_samples'] / $ThisFileInfo['audio']['sample_rate']; - - // Safe RIFF header in case there's a RIFF footer later - $metablockRIFFheader = $metablock['data']; - break; - - - case 0x22: // ID_RIFF_TRAILER - $metablockRIFFfooter = $metablockRIFFheader.$metablock['data']; - getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true); - - $ftell_old = ftell($fd); - $startoffset = $metablock['offset'] + ($metablock['large_block'] ? 4 : 2); - $ParsedRIFFfooter = array('avdataend'=>$ThisFileInfo['avdataend'], 'fileformat'=>'riff', 'error'=>array(), 'warning'=>array()); - $metablock['riff'] = getid3_riff::ParseRIFF($fd, $startoffset, $startoffset + $metablock['size'], $ParsedRIFFfooter); - fseek($fd, $ftell_old, SEEK_SET); - - if (!empty($metablock['riff']['INFO'])) { - getid3_riff::RIFFcommentsParse($metablock['riff']['INFO'], $metablock['comments']); - $ThisFileInfo['tags']['riff'] = $metablock['comments']; - } - break; - - - case 0x23: // ID_REPLAY_GAIN - $ThisFileInfo['warning'][] = 'WavPack "Replay Gain" contents not yet handled by getID3() in metablock at offset '.$metablock['offset']; - break; - - - case 0x24: // ID_CUESHEET - $ThisFileInfo['warning'][] = 'WavPack "Cuesheet" contents not yet handled by getID3() in metablock at offset '.$metablock['offset']; - break; - - - case 0x25: // ID_CONFIG_BLOCK - $metablock['flags_raw'] = getid3_lib::LittleEndian2Int(substr($metablock['data'], 0, 3)); - - $metablock['flags']['adobe_mode'] = (bool) ($metablock['flags_raw'] & 0x000001); // "adobe" mode for 32-bit floats - $metablock['flags']['fast_flag'] = (bool) ($metablock['flags_raw'] & 0x000002); // fast mode - $metablock['flags']['very_fast_flag'] = (bool) ($metablock['flags_raw'] & 0x000004); // double fast - $metablock['flags']['high_flag'] = (bool) ($metablock['flags_raw'] & 0x000008); // high quality mode - $metablock['flags']['very_high_flag'] = (bool) ($metablock['flags_raw'] & 0x000010); // double high (not used yet) - $metablock['flags']['bitrate_kbps'] = (bool) ($metablock['flags_raw'] & 0x000020); // bitrate is kbps, not bits / sample - $metablock['flags']['auto_shaping'] = (bool) ($metablock['flags_raw'] & 0x000040); // automatic noise shaping - $metablock['flags']['shape_override'] = (bool) ($metablock['flags_raw'] & 0x000080); // shaping mode specified - $metablock['flags']['joint_override'] = (bool) ($metablock['flags_raw'] & 0x000100); // joint-stereo mode specified - $metablock['flags']['copy_time'] = (bool) ($metablock['flags_raw'] & 0x000200); // copy file-time from source - $metablock['flags']['create_exe'] = (bool) ($metablock['flags_raw'] & 0x000400); // create executable - $metablock['flags']['create_wvc'] = (bool) ($metablock['flags_raw'] & 0x000800); // create correction file - $metablock['flags']['optimize_wvc'] = (bool) ($metablock['flags_raw'] & 0x001000); // maximize bybrid compression - $metablock['flags']['quality_mode'] = (bool) ($metablock['flags_raw'] & 0x002000); // psychoacoustic quality mode - $metablock['flags']['raw_flag'] = (bool) ($metablock['flags_raw'] & 0x004000); // raw mode (not implemented yet) - $metablock['flags']['calc_noise'] = (bool) ($metablock['flags_raw'] & 0x008000); // calc noise in hybrid mode - $metablock['flags']['lossy_mode'] = (bool) ($metablock['flags_raw'] & 0x010000); // obsolete (for information) - $metablock['flags']['extra_mode'] = (bool) ($metablock['flags_raw'] & 0x020000); // extra processing mode - $metablock['flags']['skip_wvx'] = (bool) ($metablock['flags_raw'] & 0x040000); // no wvx stream w/ floats & big ints - $metablock['flags']['md5_checksum'] = (bool) ($metablock['flags_raw'] & 0x080000); // compute & store MD5 signature - $metablock['flags']['quiet_mode'] = (bool) ($metablock['flags_raw'] & 0x100000); // don't report progress % - - $ThisFileInfo['wavpack']['config_flags'] = $metablock['flags']; - - - if ($ThisFileInfo['wavpack']['blockheader']['flags']['hybrid']) { - @$ThisFileInfo['audio']['encoder_options'] .= ' -b???'; - } - @$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['adobe_mode'] ? ' -a' : ''); - @$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['optimize_wvc'] ? ' -cc' : ''); - @$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['create_exe'] ? ' -e' : ''); - @$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['fast_flag'] ? ' -f' : ''); - @$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['joint_override'] ? ' -j?' : ''); - @$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['high_flag'] ? ' -h' : ''); - @$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['md5_checksum'] ? ' -m' : ''); - @$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['calc_noise'] ? ' -n' : ''); - @$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['shape_override'] ? ' -s?' : ''); - @$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['extra_mode'] ? ' -x?' : ''); - if (@$ThisFileInfo['audio']['encoder_options']) { - $ThisFileInfo['audio']['encoder_options'] = trim(@$ThisFileInfo['audio']['encoder_options']); - } - elseif (isset($ThisFileInfo['audio']['encoder_options'])) { - unset($ThisFileInfo['audio']['encoder_options']); - } - break; - - - case 0x26: // ID_MD5_CHECKSUM - if (strlen($metablock['data']) == 16) { - $ThisFileInfo['md5_data_source'] = strtolower(getid3_lib::PrintHexBytes($metablock['data'], true, false, false)); - } else { - $ThisFileInfo['warning'][] = 'Expecting 16 bytes of WavPack "MD5 Checksum" in metablock at offset '.$metablock['offset'].', but found '.strlen($metablock['data']).' bytes'; - } - break; - - - case 0x00: // ID_DUMMY - case 0x01: // ID_ENCODER_INFO - case 0x02: // ID_DECORR_TERMS - case 0x03: // ID_DECORR_WEIGHTS - case 0x04: // ID_DECORR_SAMPLES - case 0x05: // ID_ENTROPY_VARS - case 0x06: // ID_HYBRID_PROFILE - case 0x07: // ID_SHAPING_WEIGHTS - case 0x08: // ID_FLOAT_INFO - case 0x09: // ID_INT32_INFO - case 0x0A: // ID_WV_BITSTREAM - case 0x0B: // ID_WVC_BITSTREAM - case 0x0C: // ID_WVX_BITSTREAM - case 0x0D: // ID_CHANNEL_INFO - unset($metablock); - break; - } - - } - if (!empty($metablock)) { - $ThisFileInfo['wavpack']['metablocks'][] = $metablock; - } - - } - - } - - $ThisFileInfo['audio']['encoder'] = 'WavPack v'.$ThisFileInfo['wavpack']['blockheader']['major_version'].'.'.str_pad($ThisFileInfo['wavpack']['blockheader']['minor_version'], 2, '0', STR_PAD_LEFT); - $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['wavpack']['blockheader']['flags']['bytes_per_sample'] * 8; - $ThisFileInfo['audio']['channels'] = ($ThisFileInfo['wavpack']['blockheader']['flags']['mono'] ? 1 : 2); - - if (@$ThisFileInfo['playtime_seconds']) { - - $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; - - } else { - - $ThisFileInfo['audio']['dataformat'] = 'wvc'; - - } - - return true; - } - - - function WavPackMetablockNameLookup(&$id) { - static $WavPackMetablockNameLookup = array( - 0x00 => 'Dummy', - 0x01 => 'Encoder Info', - 0x02 => 'Decorrelation Terms', - 0x03 => 'Decorrelation Weights', - 0x04 => 'Decorrelation Samples', - 0x05 => 'Entropy Variables', - 0x06 => 'Hybrid Profile', - 0x07 => 'Shaping Weights', - 0x08 => 'Float Info', - 0x09 => 'Int32 Info', - 0x0A => 'WV Bitstream', - 0x0B => 'WVC Bitstream', - 0x0C => 'WVX Bitstream', - 0x0D => 'Channel Info', - 0x21 => 'RIFF header', - 0x22 => 'RIFF trailer', - 0x23 => 'Replay Gain', - 0x24 => 'Cuesheet', - 0x25 => 'Config Block', - 0x26 => 'MD5 Checksum', - ); - return (@$WavPackMetablockNameLookup[$id]); - } - -} - - -?> \ No newline at end of file diff --git a/getid3/getid3/module.graphic.bmp.php b/getid3/getid3/module.graphic.bmp.php deleted file mode 100644 index dc6d733..0000000 --- a/getid3/getid3/module.graphic.bmp.php +++ /dev/null @@ -1,683 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.graphic.bmp.php // -// module for analyzing BMP Image files // -// dependencies: NONE // -// /// -///////////////////////////////////////////////////////////////// - - -class getid3_bmp -{ - - function getid3_bmp(&$fd, &$ThisFileInfo, $ExtractPalette=false, $ExtractData=false) { - - // shortcuts - $ThisFileInfo['bmp']['header']['raw'] = array(); - $thisfile_bmp = &$ThisFileInfo['bmp']; - $thisfile_bmp_header = &$thisfile_bmp['header']; - $thisfile_bmp_header_raw = &$thisfile_bmp_header['raw']; - - // BITMAPFILEHEADER [14 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_62uq.asp - // all versions - // WORD bfType; - // DWORD bfSize; - // WORD bfReserved1; - // WORD bfReserved2; - // DWORD bfOffBits; - - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $offset = 0; - $BMPheader = fread($fd, 14 + 40); - - $thisfile_bmp_header_raw['identifier'] = substr($BMPheader, $offset, 2); - $offset += 2; - - if ($thisfile_bmp_header_raw['identifier'] != 'BM') { - $ThisFileInfo['error'][] = 'Expecting "BM" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$thisfile_bmp_header_raw['identifier'].'"'; - unset($ThisFileInfo['fileformat']); - unset($ThisFileInfo['bmp']); - return false; - } - - $thisfile_bmp_header_raw['filesize'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); - $offset += 4; - $thisfile_bmp_header_raw['reserved1'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2)); - $offset += 2; - $thisfile_bmp_header_raw['reserved2'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2)); - $offset += 2; - $thisfile_bmp_header_raw['data_offset'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); - $offset += 4; - $thisfile_bmp_header_raw['header_size'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); - $offset += 4; - - - // check if the hardcoded-to-1 "planes" is at offset 22 or 26 - $planes22 = getid3_lib::LittleEndian2Int(substr($BMPheader, 22, 2)); - $planes26 = getid3_lib::LittleEndian2Int(substr($BMPheader, 26, 2)); - if (($planes22 == 1) && ($planes26 != 1)) { - $thisfile_bmp['type_os'] = 'OS/2'; - $thisfile_bmp['type_version'] = 1; - } elseif (($planes26 == 1) && ($planes22 != 1)) { - $thisfile_bmp['type_os'] = 'Windows'; - $thisfile_bmp['type_version'] = 1; - } elseif ($thisfile_bmp_header_raw['header_size'] == 12) { - $thisfile_bmp['type_os'] = 'OS/2'; - $thisfile_bmp['type_version'] = 1; - } elseif ($thisfile_bmp_header_raw['header_size'] == 40) { - $thisfile_bmp['type_os'] = 'Windows'; - $thisfile_bmp['type_version'] = 1; - } elseif ($thisfile_bmp_header_raw['header_size'] == 84) { - $thisfile_bmp['type_os'] = 'Windows'; - $thisfile_bmp['type_version'] = 4; - } elseif ($thisfile_bmp_header_raw['header_size'] == 100) { - $thisfile_bmp['type_os'] = 'Windows'; - $thisfile_bmp['type_version'] = 5; - } else { - $ThisFileInfo['error'][] = 'Unknown BMP subtype (or not a BMP file)'; - unset($ThisFileInfo['fileformat']); - unset($ThisFileInfo['bmp']); - return false; - } - - $ThisFileInfo['fileformat'] = 'bmp'; - $ThisFileInfo['video']['dataformat'] = 'bmp'; - $ThisFileInfo['video']['lossless'] = true; - $ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1; - - if ($thisfile_bmp['type_os'] == 'OS/2') { - - // OS/2-format BMP - // http://netghost.narod.ru/gff/graphics/summary/os2bmp.htm - - // DWORD Size; /* Size of this structure in bytes */ - // DWORD Width; /* Bitmap width in pixels */ - // DWORD Height; /* Bitmap height in pixel */ - // WORD NumPlanes; /* Number of bit planes (color depth) */ - // WORD BitsPerPixel; /* Number of bits per pixel per plane */ - - $thisfile_bmp_header_raw['width'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2)); - $offset += 2; - $thisfile_bmp_header_raw['height'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2)); - $offset += 2; - $thisfile_bmp_header_raw['planes'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2)); - $offset += 2; - $thisfile_bmp_header_raw['bits_per_pixel'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2)); - $offset += 2; - - $ThisFileInfo['video']['resolution_x'] = $thisfile_bmp_header_raw['width']; - $ThisFileInfo['video']['resolution_y'] = $thisfile_bmp_header_raw['height']; - $ThisFileInfo['video']['codec'] = 'BI_RGB '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit'; - $ThisFileInfo['video']['bits_per_sample'] = $thisfile_bmp_header_raw['bits_per_pixel']; - - if ($thisfile_bmp['type_version'] >= 2) { - // DWORD Compression; /* Bitmap compression scheme */ - // DWORD ImageDataSize; /* Size of bitmap data in bytes */ - // DWORD XResolution; /* X resolution of display device */ - // DWORD YResolution; /* Y resolution of display device */ - // DWORD ColorsUsed; /* Number of color table indices used */ - // DWORD ColorsImportant; /* Number of important color indices */ - // WORD Units; /* Type of units used to measure resolution */ - // WORD Reserved; /* Pad structure to 4-byte boundary */ - // WORD Recording; /* Recording algorithm */ - // WORD Rendering; /* Halftoning algorithm used */ - // DWORD Size1; /* Reserved for halftoning algorithm use */ - // DWORD Size2; /* Reserved for halftoning algorithm use */ - // DWORD ColorEncoding; /* Color model used in bitmap */ - // DWORD Identifier; /* Reserved for application use */ - - $thisfile_bmp_header_raw['compression'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); - $offset += 4; - $thisfile_bmp_header_raw['bmp_data_size'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); - $offset += 4; - $thisfile_bmp_header_raw['resolution_h'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); - $offset += 4; - $thisfile_bmp_header_raw['resolution_v'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); - $offset += 4; - $thisfile_bmp_header_raw['colors_used'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); - $offset += 4; - $thisfile_bmp_header_raw['colors_important'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); - $offset += 4; - $thisfile_bmp_header_raw['resolution_units'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2)); - $offset += 2; - $thisfile_bmp_header_raw['reserved1'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2)); - $offset += 2; - $thisfile_bmp_header_raw['recording'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2)); - $offset += 2; - $thisfile_bmp_header_raw['rendering'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2)); - $offset += 2; - $thisfile_bmp_header_raw['size1'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); - $offset += 4; - $thisfile_bmp_header_raw['size2'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); - $offset += 4; - $thisfile_bmp_header_raw['color_encoding'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); - $offset += 4; - $thisfile_bmp_header_raw['identifier'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); - $offset += 4; - - $thisfile_bmp_header['compression'] = $this->BMPcompressionOS2Lookup($thisfile_bmp_header_raw['compression']); - - $ThisFileInfo['video']['codec'] = $thisfile_bmp_header['compression'].' '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit'; - } - - } elseif ($thisfile_bmp['type_os'] == 'Windows') { - - // Windows-format BMP - - // BITMAPINFOHEADER - [40 bytes] http://msdn.microsoft.com/library/en-us/gdi/bitmaps_1rw2.asp - // all versions - // DWORD biSize; - // LONG biWidth; - // LONG biHeight; - // WORD biPlanes; - // WORD biBitCount; - // DWORD biCompression; - // DWORD biSizeImage; - // LONG biXPelsPerMeter; - // LONG biYPelsPerMeter; - // DWORD biClrUsed; - // DWORD biClrImportant; - - $thisfile_bmp_header_raw['width'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4), true); - $offset += 4; - $thisfile_bmp_header_raw['height'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4), true); - $offset += 4; - $thisfile_bmp_header_raw['planes'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2)); - $offset += 2; - $thisfile_bmp_header_raw['bits_per_pixel'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2)); - $offset += 2; - $thisfile_bmp_header_raw['compression'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); - $offset += 4; - $thisfile_bmp_header_raw['bmp_data_size'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); - $offset += 4; - $thisfile_bmp_header_raw['resolution_h'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4), true); - $offset += 4; - $thisfile_bmp_header_raw['resolution_v'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4), true); - $offset += 4; - $thisfile_bmp_header_raw['colors_used'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); - $offset += 4; - $thisfile_bmp_header_raw['colors_important'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); - $offset += 4; - - $thisfile_bmp_header['compression'] = $this->BMPcompressionWindowsLookup($thisfile_bmp_header_raw['compression']); - $ThisFileInfo['video']['resolution_x'] = $thisfile_bmp_header_raw['width']; - $ThisFileInfo['video']['resolution_y'] = $thisfile_bmp_header_raw['height']; - $ThisFileInfo['video']['codec'] = $thisfile_bmp_header['compression'].' '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit'; - $ThisFileInfo['video']['bits_per_sample'] = $thisfile_bmp_header_raw['bits_per_pixel']; - - if (($thisfile_bmp['type_version'] >= 4) || ($thisfile_bmp_header_raw['compression'] == 3)) { - // should only be v4+, but BMPs with type_version==1 and BI_BITFIELDS compression have been seen - $BMPheader .= fread($fd, 44); - - // BITMAPV4HEADER - [44 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_2k1e.asp - // Win95+, WinNT4.0+ - // DWORD bV4RedMask; - // DWORD bV4GreenMask; - // DWORD bV4BlueMask; - // DWORD bV4AlphaMask; - // DWORD bV4CSType; - // CIEXYZTRIPLE bV4Endpoints; - // DWORD bV4GammaRed; - // DWORD bV4GammaGreen; - // DWORD bV4GammaBlue; - $thisfile_bmp_header_raw['red_mask'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); - $offset += 4; - $thisfile_bmp_header_raw['green_mask'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); - $offset += 4; - $thisfile_bmp_header_raw['blue_mask'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); - $offset += 4; - $thisfile_bmp_header_raw['alpha_mask'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); - $offset += 4; - $thisfile_bmp_header_raw['cs_type'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); - $offset += 4; - $thisfile_bmp_header_raw['ciexyz_red'] = substr($BMPheader, $offset, 4); - $offset += 4; - $thisfile_bmp_header_raw['ciexyz_green'] = substr($BMPheader, $offset, 4); - $offset += 4; - $thisfile_bmp_header_raw['ciexyz_blue'] = substr($BMPheader, $offset, 4); - $offset += 4; - $thisfile_bmp_header_raw['gamma_red'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); - $offset += 4; - $thisfile_bmp_header_raw['gamma_green'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); - $offset += 4; - $thisfile_bmp_header_raw['gamma_blue'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); - $offset += 4; - - $thisfile_bmp_header['ciexyz_red'] = getid3_lib::FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_red'])); - $thisfile_bmp_header['ciexyz_green'] = getid3_lib::FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_green'])); - $thisfile_bmp_header['ciexyz_blue'] = getid3_lib::FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_blue'])); - } - - if ($thisfile_bmp['type_version'] >= 5) { - $BMPheader .= fread($fd, 16); - - // BITMAPV5HEADER - [16 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_7c36.asp - // Win98+, Win2000+ - // DWORD bV5Intent; - // DWORD bV5ProfileData; - // DWORD bV5ProfileSize; - // DWORD bV5Reserved; - $thisfile_bmp_header_raw['intent'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); - $offset += 4; - $thisfile_bmp_header_raw['profile_data_offset'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); - $offset += 4; - $thisfile_bmp_header_raw['profile_data_size'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); - $offset += 4; - $thisfile_bmp_header_raw['reserved3'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); - $offset += 4; - } - - } else { - - $ThisFileInfo['error'][] = 'Unknown BMP format in header.'; - return false; - - } - - - if ($ExtractPalette || $ExtractData) { - $PaletteEntries = 0; - if ($thisfile_bmp_header_raw['bits_per_pixel'] < 16) { - $PaletteEntries = pow(2, $thisfile_bmp_header_raw['bits_per_pixel']); - } elseif (isset($thisfile_bmp_header_raw['colors_used']) && ($thisfile_bmp_header_raw['colors_used'] > 0) && ($thisfile_bmp_header_raw['colors_used'] <= 256)) { - $PaletteEntries = $thisfile_bmp_header_raw['colors_used']; - } - if ($PaletteEntries > 0) { - $BMPpalette = fread($fd, 4 * $PaletteEntries); - $paletteoffset = 0; - for ($i = 0; $i < $PaletteEntries; $i++) { - // RGBQUAD - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_5f8y.asp - // BYTE rgbBlue; - // BYTE rgbGreen; - // BYTE rgbRed; - // BYTE rgbReserved; - $blue = getid3_lib::LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1)); - $green = getid3_lib::LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1)); - $red = getid3_lib::LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1)); - if (($thisfile_bmp['type_os'] == 'OS/2') && ($thisfile_bmp['type_version'] == 1)) { - // no padding byte - } else { - $paletteoffset++; // padding byte - } - $thisfile_bmp['palette'][$i] = (($red << 16) | ($green << 8) | $blue); - } - } - } - - if ($ExtractData) { - fseek($fd, $thisfile_bmp_header_raw['data_offset'], SEEK_SET); - $RowByteLength = ceil(($thisfile_bmp_header_raw['width'] * ($thisfile_bmp_header_raw['bits_per_pixel'] / 8)) / 4) * 4; // round up to nearest DWORD boundry - $BMPpixelData = fread($fd, $thisfile_bmp_header_raw['height'] * $RowByteLength); - $pixeldataoffset = 0; - switch (@$thisfile_bmp_header_raw['compression']) { - - case 0: // BI_RGB - switch ($thisfile_bmp_header_raw['bits_per_pixel']) { - case 1: - for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) { - for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col = $col) { - $paletteindexbyte = ord($BMPpixelData{$pixeldataoffset++}); - for ($i = 7; $i >= 0; $i--) { - $paletteindex = ($paletteindexbyte & (0x01 << $i)) >> $i; - $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex]; - $col++; - } - } - while (($pixeldataoffset % 4) != 0) { - // lines are padded to nearest DWORD - $pixeldataoffset++; - } - } - break; - - case 4: - for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) { - for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col = $col) { - $paletteindexbyte = ord($BMPpixelData{$pixeldataoffset++}); - for ($i = 1; $i >= 0; $i--) { - $paletteindex = ($paletteindexbyte & (0x0F << (4 * $i))) >> (4 * $i); - $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex]; - $col++; - } - } - while (($pixeldataoffset % 4) != 0) { - // lines are padded to nearest DWORD - $pixeldataoffset++; - } - } - break; - - case 8: - for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) { - for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) { - $paletteindex = ord($BMPpixelData{$pixeldataoffset++}); - $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex]; - } - while (($pixeldataoffset % 4) != 0) { - // lines are padded to nearest DWORD - $pixeldataoffset++; - } - } - break; - - case 24: - for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) { - for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) { - $thisfile_bmp['data'][$row][$col] = (ord($BMPpixelData{$pixeldataoffset+2}) << 16) | (ord($BMPpixelData{$pixeldataoffset+1}) << 8) | ord($BMPpixelData{$pixeldataoffset}); - $pixeldataoffset += 3; - } - while (($pixeldataoffset % 4) != 0) { - // lines are padded to nearest DWORD - $pixeldataoffset++; - } - } - break; - - case 32: - for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) { - for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) { - $thisfile_bmp['data'][$row][$col] = (ord($BMPpixelData{$pixeldataoffset+3}) << 24) | (ord($BMPpixelData{$pixeldataoffset+2}) << 16) | (ord($BMPpixelData{$pixeldataoffset+1}) << 8) | ord($BMPpixelData{$pixeldataoffset}); - $pixeldataoffset += 4; - } - while (($pixeldataoffset % 4) != 0) { - // lines are padded to nearest DWORD - $pixeldataoffset++; - } - } - break; - - case 16: - // ? - break; - - default: - $ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data'; - break; - } - break; - - - case 1: // BI_RLE8 - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_6x0u.asp - switch ($thisfile_bmp_header_raw['bits_per_pixel']) { - case 8: - $pixelcounter = 0; - while ($pixeldataoffset < strlen($BMPpixelData)) { - $firstbyte = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1)); - $secondbyte = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1)); - if ($firstbyte == 0) { - - // escaped/absolute mode - the first byte of the pair can be set to zero to - // indicate an escape character that denotes the end of a line, the end of - // a bitmap, or a delta, depending on the value of the second byte. - switch ($secondbyte) { - case 0: - // end of line - // no need for special processing, just ignore - break; - - case 1: - // end of bitmap - $pixeldataoffset = strlen($BMPpixelData); // force to exit loop just in case - break; - - case 2: - // delta - The 2 bytes following the escape contain unsigned values - // indicating the horizontal and vertical offsets of the next pixel - // from the current position. - $colincrement = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1)); - $rowincrement = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1)); - $col = ($pixelcounter % $thisfile_bmp_header_raw['width']) + $colincrement; - $row = ($thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width'])) - $rowincrement; - $pixelcounter = ($row * $thisfile_bmp_header_raw['width']) + $col; - break; - - default: - // In absolute mode, the first byte is zero and the second byte is a - // value in the range 03H through FFH. The second byte represents the - // number of bytes that follow, each of which contains the color index - // of a single pixel. Each run must be aligned on a word boundary. - for ($i = 0; $i < $secondbyte; $i++) { - $paletteindex = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1)); - $col = $pixelcounter % $thisfile_bmp_header_raw['width']; - $row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']); - $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex]; - $pixelcounter++; - } - while (($pixeldataoffset % 2) != 0) { - // Each run must be aligned on a word boundary. - $pixeldataoffset++; - } - break; - } - - } else { - - // encoded mode - the first byte specifies the number of consecutive pixels - // to be drawn using the color index contained in the second byte. - for ($i = 0; $i < $firstbyte; $i++) { - $col = $pixelcounter % $thisfile_bmp_header_raw['width']; - $row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']); - $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$secondbyte]; - $pixelcounter++; - } - - } - } - break; - - default: - $ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data'; - break; - } - break; - - - - case 2: // BI_RLE4 - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_6x0u.asp - switch ($thisfile_bmp_header_raw['bits_per_pixel']) { - case 4: - $pixelcounter = 0; - while ($pixeldataoffset < strlen($BMPpixelData)) { - $firstbyte = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1)); - $secondbyte = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1)); - if ($firstbyte == 0) { - - // escaped/absolute mode - the first byte of the pair can be set to zero to - // indicate an escape character that denotes the end of a line, the end of - // a bitmap, or a delta, depending on the value of the second byte. - switch ($secondbyte) { - case 0: - // end of line - // no need for special processing, just ignore - break; - - case 1: - // end of bitmap - $pixeldataoffset = strlen($BMPpixelData); // force to exit loop just in case - break; - - case 2: - // delta - The 2 bytes following the escape contain unsigned values - // indicating the horizontal and vertical offsets of the next pixel - // from the current position. - $colincrement = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1)); - $rowincrement = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1)); - $col = ($pixelcounter % $thisfile_bmp_header_raw['width']) + $colincrement; - $row = ($thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width'])) - $rowincrement; - $pixelcounter = ($row * $thisfile_bmp_header_raw['width']) + $col; - break; - - default: - // In absolute mode, the first byte is zero. The second byte contains the number - // of color indexes that follow. Subsequent bytes contain color indexes in their - // high- and low-order 4 bits, one color index for each pixel. In absolute mode, - // each run must be aligned on a word boundary. - unset($paletteindexes); - for ($i = 0; $i < ceil($secondbyte / 2); $i++) { - $paletteindexbyte = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1)); - $paletteindexes[] = ($paletteindexbyte & 0xF0) >> 4; - $paletteindexes[] = ($paletteindexbyte & 0x0F); - } - while (($pixeldataoffset % 2) != 0) { - // Each run must be aligned on a word boundary. - $pixeldataoffset++; - } - - foreach ($paletteindexes as $paletteindex) { - $col = $pixelcounter % $thisfile_bmp_header_raw['width']; - $row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']); - $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex]; - $pixelcounter++; - } - break; - } - - } else { - - // encoded mode - the first byte of the pair contains the number of pixels to be - // drawn using the color indexes in the second byte. The second byte contains two - // color indexes, one in its high-order 4 bits and one in its low-order 4 bits. - // The first of the pixels is drawn using the color specified by the high-order - // 4 bits, the second is drawn using the color in the low-order 4 bits, the third - // is drawn using the color in the high-order 4 bits, and so on, until all the - // pixels specified by the first byte have been drawn. - $paletteindexes[0] = ($secondbyte & 0xF0) >> 4; - $paletteindexes[1] = ($secondbyte & 0x0F); - for ($i = 0; $i < $firstbyte; $i++) { - $col = $pixelcounter % $thisfile_bmp_header_raw['width']; - $row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']); - $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindexes[($i % 2)]]; - $pixelcounter++; - } - - } - } - break; - - default: - $ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data'; - break; - } - break; - - - case 3: // BI_BITFIELDS - switch ($thisfile_bmp_header_raw['bits_per_pixel']) { - case 16: - case 32: - $redshift = 0; - $greenshift = 0; - $blueshift = 0; - while ((($thisfile_bmp_header_raw['red_mask'] >> $redshift) & 0x01) == 0) { - $redshift++; - } - while ((($thisfile_bmp_header_raw['green_mask'] >> $greenshift) & 0x01) == 0) { - $greenshift++; - } - while ((($thisfile_bmp_header_raw['blue_mask'] >> $blueshift) & 0x01) == 0) { - $blueshift++; - } - for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) { - for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) { - $pixelvalue = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset, $thisfile_bmp_header_raw['bits_per_pixel'] / 8)); - $pixeldataoffset += $thisfile_bmp_header_raw['bits_per_pixel'] / 8; - - $red = intval(round(((($pixelvalue & $thisfile_bmp_header_raw['red_mask']) >> $redshift) / ($thisfile_bmp_header_raw['red_mask'] >> $redshift)) * 255)); - $green = intval(round(((($pixelvalue & $thisfile_bmp_header_raw['green_mask']) >> $greenshift) / ($thisfile_bmp_header_raw['green_mask'] >> $greenshift)) * 255)); - $blue = intval(round(((($pixelvalue & $thisfile_bmp_header_raw['blue_mask']) >> $blueshift) / ($thisfile_bmp_header_raw['blue_mask'] >> $blueshift)) * 255)); - $thisfile_bmp['data'][$row][$col] = (($red << 16) | ($green << 8) | ($blue)); - } - while (($pixeldataoffset % 4) != 0) { - // lines are padded to nearest DWORD - $pixeldataoffset++; - } - } - break; - - default: - $ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data'; - break; - } - break; - - - default: // unhandled compression type - $ThisFileInfo['error'][] = 'Unknown/unhandled compression type value ('.$thisfile_bmp_header_raw['compression'].') - cannot decompress pixel data'; - break; - } - } - - return true; - } - - - function PlotBMP(&$BMPinfo) { - $starttime = time(); - if (!isset($BMPinfo['bmp']['data']) || !is_array($BMPinfo['bmp']['data'])) { - echo 'ERROR: no pixel data
'; - return false; - } - set_time_limit(intval(round($BMPinfo['resolution_x'] * $BMPinfo['resolution_y'] / 10000))); - if ($im = ImageCreateTrueColor($BMPinfo['resolution_x'], $BMPinfo['resolution_y'])) { - for ($row = 0; $row < $BMPinfo['resolution_y']; $row++) { - for ($col = 0; $col < $BMPinfo['resolution_x']; $col++) { - if (isset($BMPinfo['bmp']['data'][$row][$col])) { - $red = ($BMPinfo['bmp']['data'][$row][$col] & 0x00FF0000) >> 16; - $green = ($BMPinfo['bmp']['data'][$row][$col] & 0x0000FF00) >> 8; - $blue = ($BMPinfo['bmp']['data'][$row][$col] & 0x000000FF); - $pixelcolor = ImageColorAllocate($im, $red, $green, $blue); - ImageSetPixel($im, $col, $row, $pixelcolor); - } else { - //echo 'ERROR: no data for pixel '.$row.' x '.$col.'
'; - //return false; - } - } - } - if (headers_sent()) { - echo 'plotted '.($BMPinfo['resolution_x'] * $BMPinfo['resolution_y']).' pixels in '.(time() - $starttime).' seconds
'; - ImageDestroy($im); - exit; - } else { - header('Content-type: image/png'); - ImagePNG($im); - ImageDestroy($im); - return true; - } - } - return false; - } - - function BMPcompressionWindowsLookup($compressionid) { - static $BMPcompressionWindowsLookup = array( - 0 => 'BI_RGB', - 1 => 'BI_RLE8', - 2 => 'BI_RLE4', - 3 => 'BI_BITFIELDS', - 4 => 'BI_JPEG', - 5 => 'BI_PNG' - ); - return (isset($BMPcompressionWindowsLookup[$compressionid]) ? $BMPcompressionWindowsLookup[$compressionid] : 'invalid'); - } - - function BMPcompressionOS2Lookup($compressionid) { - static $BMPcompressionOS2Lookup = array( - 0 => 'BI_RGB', - 1 => 'BI_RLE8', - 2 => 'BI_RLE4', - 3 => 'Huffman 1D', - 4 => 'BI_RLE24', - ); - return (isset($BMPcompressionOS2Lookup[$compressionid]) ? $BMPcompressionOS2Lookup[$compressionid] : 'invalid'); - } - -} - - -?> \ No newline at end of file diff --git a/getid3/getid3/module.graphic.gif.php b/getid3/getid3/module.graphic.gif.php deleted file mode 100644 index 986ada3..0000000 --- a/getid3/getid3/module.graphic.gif.php +++ /dev/null @@ -1,183 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.graphic.gif.php // -// module for analyzing GIF Image files // -// dependencies: NONE // -// /// -///////////////////////////////////////////////////////////////// - - -class getid3_gif -{ - - function getid3_gif(&$fd, &$ThisFileInfo) { - $ThisFileInfo['fileformat'] = 'gif'; - $ThisFileInfo['video']['dataformat'] = 'gif'; - $ThisFileInfo['video']['lossless'] = true; - $ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1; - - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $GIFheader = fread($fd, 13); - $offset = 0; - - $ThisFileInfo['gif']['header']['raw']['identifier'] = substr($GIFheader, $offset, 3); - $offset += 3; - - if ($ThisFileInfo['gif']['header']['raw']['identifier'] != 'GIF') { - $ThisFileInfo['error'][] = 'Expecting "GIF" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$ThisFileInfo['gif']['header']['raw']['identifier'].'"'; - unset($ThisFileInfo['fileformat']); - unset($ThisFileInfo['gif']); - return false; - } - - $ThisFileInfo['gif']['header']['raw']['version'] = substr($GIFheader, $offset, 3); - $offset += 3; - $ThisFileInfo['gif']['header']['raw']['width'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 2)); - $offset += 2; - $ThisFileInfo['gif']['header']['raw']['height'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 2)); - $offset += 2; - $ThisFileInfo['gif']['header']['raw']['flags'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 1)); - $offset += 1; - $ThisFileInfo['gif']['header']['raw']['bg_color_index'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 1)); - $offset += 1; - $ThisFileInfo['gif']['header']['raw']['aspect_ratio'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 1)); - $offset += 1; - - $ThisFileInfo['video']['resolution_x'] = $ThisFileInfo['gif']['header']['raw']['width']; - $ThisFileInfo['video']['resolution_y'] = $ThisFileInfo['gif']['header']['raw']['height']; - $ThisFileInfo['gif']['version'] = $ThisFileInfo['gif']['header']['raw']['version']; - $ThisFileInfo['gif']['header']['flags']['global_color_table'] = (bool) ($ThisFileInfo['gif']['header']['raw']['flags'] & 0x80); - if ($ThisFileInfo['gif']['header']['raw']['flags'] & 0x80) { - // Number of bits per primary color available to the original image, minus 1 - $ThisFileInfo['gif']['header']['bits_per_pixel'] = 3 * ((($ThisFileInfo['gif']['header']['raw']['flags'] & 0x70) >> 4) + 1); - } else { - $ThisFileInfo['gif']['header']['bits_per_pixel'] = 0; - } - $ThisFileInfo['gif']['header']['flags']['global_color_sorted'] = (bool) ($ThisFileInfo['gif']['header']['raw']['flags'] & 0x40); - if ($ThisFileInfo['gif']['header']['flags']['global_color_table']) { - // the number of bytes contained in the Global Color Table. To determine that - // actual size of the color table, raise 2 to [the value of the field + 1] - $ThisFileInfo['gif']['header']['global_color_size'] = pow(2, ($ThisFileInfo['gif']['header']['raw']['flags'] & 0x07) + 1); - $ThisFileInfo['video']['bits_per_sample'] = ($ThisFileInfo['gif']['header']['raw']['flags'] & 0x07) + 1; - } else { - $ThisFileInfo['gif']['header']['global_color_size'] = 0; - } - if ($ThisFileInfo['gif']['header']['raw']['aspect_ratio'] != 0) { - // Aspect Ratio = (Pixel Aspect Ratio + 15) / 64 - $ThisFileInfo['gif']['header']['aspect_ratio'] = ($ThisFileInfo['gif']['header']['raw']['aspect_ratio'] + 15) / 64; - } - -// if ($ThisFileInfo['gif']['header']['flags']['global_color_table']) { -// $GIFcolorTable = fread($fd, 3 * $ThisFileInfo['gif']['header']['global_color_size']); -// $offset = 0; -// for ($i = 0; $i < $ThisFileInfo['gif']['header']['global_color_size']; $i++) { -// $red = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1)); -// $green = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1)); -// $blue = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1)); -// $ThisFileInfo['gif']['global_color_table'][$i] = (($red << 16) | ($green << 8) | ($blue)); -// } -// } -// -// // Image Descriptor -// while (!feof($fd)) { -// $NextBlockTest = fread($fd, 1); -// switch ($NextBlockTest) { -// -// case ',': // ',' - Image separator character -// -// $ImageDescriptorData = $NextBlockTest.fread($fd, 9); -// $ImageDescriptor = array(); -// $ImageDescriptor['image_left'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 1, 2)); -// $ImageDescriptor['image_top'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 3, 2)); -// $ImageDescriptor['image_width'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 5, 2)); -// $ImageDescriptor['image_height'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 7, 2)); -// $ImageDescriptor['flags_raw'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 9, 1)); -// $ImageDescriptor['flags']['use_local_color_map'] = (bool) ($ImageDescriptor['flags_raw'] & 0x80); -// $ImageDescriptor['flags']['image_interlaced'] = (bool) ($ImageDescriptor['flags_raw'] & 0x40); -// $ThisFileInfo['gif']['image_descriptor'][] = $ImageDescriptor; -// -// if ($ImageDescriptor['flags']['use_local_color_map']) { -// -// $ThisFileInfo['warning'][] = 'This version of getID3() cannot parse local color maps for GIFs'; -// return true; -// -// } -//echo 'Start of raster data: '.ftell($fd).'
'; -// $RasterData = array(); -// $RasterData['code_size'] = getid3_lib::LittleEndian2Int(fread($fd, 1)); -// $RasterData['block_byte_count'] = getid3_lib::LittleEndian2Int(fread($fd, 1)); -// $ThisFileInfo['gif']['raster_data'][count($ThisFileInfo['gif']['image_descriptor']) - 1] = $RasterData; -// -// $CurrentCodeSize = $RasterData['code_size'] + 1; -// for ($i = 0; $i < pow(2, $RasterData['code_size']); $i++) { -// $DefaultDataLookupTable[$i] = chr($i); -// } -// $DefaultDataLookupTable[pow(2, $RasterData['code_size']) + 0] = ''; // Clear Code -// $DefaultDataLookupTable[pow(2, $RasterData['code_size']) + 1] = ''; // End Of Image Code -// -// -// $NextValue = $this->GetLSBits($fd, $CurrentCodeSize); -// echo 'Clear Code: '.$NextValue.'
'; -// -// $NextValue = $this->GetLSBits($fd, $CurrentCodeSize); -// echo 'First Color: '.$NextValue.'
'; -// -// $Prefix = $NextValue; -//$i = 0; -// while ($i++ < 20) { -// $NextValue = $this->GetLSBits($fd, $CurrentCodeSize); -// echo $NextValue.'
'; -// } -//return true; -// break; -// -// case '!': -// // GIF Extension Block -// $ExtensionBlockData = $NextBlockTest.fread($fd, 2); -// $ExtensionBlock = array(); -// $ExtensionBlock['function_code'] = getid3_lib::LittleEndian2Int(substr($ExtensionBlockData, 1, 1)); -// $ExtensionBlock['byte_length'] = getid3_lib::LittleEndian2Int(substr($ExtensionBlockData, 2, 1)); -// $ExtensionBlock['data'] = fread($fd, $ExtensionBlock['byte_length']); -// $ThisFileInfo['gif']['extension_blocks'][] = $ExtensionBlock; -// break; -// -// case ';': -// $ThisFileInfo['gif']['terminator_offset'] = ftell($fd) - 1; -// // GIF Terminator -// break; -// -// default: -// break; -// -// -// } -// } - - return true; - } - - - function GetLSBits($fd, $bits) { - static $bitbuffer = ''; - while (strlen($bitbuffer) < $bits) { -//echo 'Read another byte: '.ftell($fd).'
'; - $bitbuffer = str_pad(decbin(ord(fread($fd, 1))), 8, '0', STR_PAD_LEFT).$bitbuffer; - } - - $value = bindec(substr($bitbuffer, 0 - $bits)); - $bitbuffer = substr($bitbuffer, 0, 0 - $bits); - - return $value; - } - -} - - -?> \ No newline at end of file diff --git a/getid3/getid3/module.graphic.jpg.php b/getid3/getid3/module.graphic.jpg.php deleted file mode 100644 index 0cd305c..0000000 --- a/getid3/getid3/module.graphic.jpg.php +++ /dev/null @@ -1,72 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.graphic.jpg.php // -// module for analyzing JPEG Image files // -// dependencies: NONE // -// /// -///////////////////////////////////////////////////////////////// - - -class getid3_jpg -{ - - - function getid3_jpg(&$fd, &$ThisFileInfo) { - $ThisFileInfo['fileformat'] = 'jpg'; - $ThisFileInfo['video']['dataformat'] = 'jpg'; - $ThisFileInfo['video']['lossless'] = false; - $ThisFileInfo['video']['bits_per_sample'] = 24; - $ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1; - - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - - list($width, $height, $type) = getid3_lib::GetDataImageSize(fread($fd, $ThisFileInfo['filesize'])); - if ($type == 2) { - - $ThisFileInfo['video']['resolution_x'] = $width; - $ThisFileInfo['video']['resolution_y'] = $height; - - if (version_compare(phpversion(), '4.2.0', '>=')) { - - if (function_exists('exif_read_data')) { - - ob_start(); - $ThisFileInfo['jpg']['exif'] = exif_read_data($ThisFileInfo['filenamepath'], '', true, false); - $errors = ob_get_contents(); - if ($errors) { - $ThisFileInfo['error'][] = strip_tags($errors); - unset($ThisFileInfo['jpg']['exif']); - } - ob_end_clean(); - - } else { - - $ThisFileInfo['warning'][] = 'EXIF parsing only available when '.(GETID3_OS_ISWINDOWS ? 'php_exif.dll enabled' : 'compiled with --enable-exif'); - - } - - } else { - - $ThisFileInfo['warning'][] = 'EXIF parsing only available in PHP v4.2.0 and higher compiled with --enable-exif (or php_exif.dll enabled for Windows). You are using PHP v'.phpversion(); - - } - - return true; - - } - - unset($ThisFileInfo['fileformat']); - return false; - } - -} - - -?> \ No newline at end of file diff --git a/getid3/getid3/module.graphic.pcd.php b/getid3/getid3/module.graphic.pcd.php deleted file mode 100644 index 60efabd..0000000 --- a/getid3/getid3/module.graphic.pcd.php +++ /dev/null @@ -1,130 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.graphic.pcd.php // -// module for analyzing PhotoCD (PCD) Image files // -// dependencies: NONE // -// /// -///////////////////////////////////////////////////////////////// - - -class getid3_pcd -{ - function getid3_pcd(&$fd, &$ThisFileInfo, $ExtractData=0) { - $ThisFileInfo['fileformat'] = 'pcd'; - $ThisFileInfo['video']['dataformat'] = 'pcd'; - $ThisFileInfo['video']['lossless'] = false; - - - fseek($fd, $ThisFileInfo['avdataoffset'] + 72, SEEK_SET); - - $PCDflags = fread($fd, 1); - $PCDisVertical = ((ord($PCDflags) & 0x01) ? true : false); - - - if ($PCDisVertical) { - $ThisFileInfo['video']['resolution_x'] = 3072; - $ThisFileInfo['video']['resolution_y'] = 2048; - } else { - $ThisFileInfo['video']['resolution_x'] = 2048; - $ThisFileInfo['video']['resolution_y'] = 3072; - } - - - if ($ExtractData > 3) { - - $ThisFileInfo['error'][] = 'Cannot extract PSD image data for detail levels above BASE (3)'; - - } elseif ($ExtractData > 0) { - - $PCD_levels[1] = array( 192, 128, 0x02000); // BASE/16 - $PCD_levels[2] = array( 384, 256, 0x0B800); // BASE/4 - $PCD_levels[3] = array( 768, 512, 0x30000); // BASE - //$PCD_levels[4] = array(1536, 1024, ??); // BASE*4 - encrypted with Kodak-proprietary compression/encryption - //$PCD_levels[5] = array(3072, 2048, ??); // BASE*16 - encrypted with Kodak-proprietary compression/encryption - //$PCD_levels[6] = array(6144, 4096, ??); // BASE*64 - encrypted with Kodak-proprietary compression/encryption; PhotoCD-Pro only - - list($PCD_width, $PCD_height, $PCD_dataOffset) = $PCD_levels[3]; - - fseek($fd, $ThisFileInfo['avdataoffset'] + $PCD_dataOffset, SEEK_SET); - - for ($y = 0; $y < $PCD_height; $y += 2) { - // The image-data of these subtypes start at the respective offsets of 02000h, 0b800h and 30000h. - // To decode the YcbYr to the more usual RGB-code, three lines of data have to be read, each - // consisting of ‘w’ bytes, where ‘w’ is the width of the image-subtype. The first ‘w’ bytes and - // the first half of the third ‘w’ bytes contain data for the first RGB-line, the second ‘w’ bytes - // and the second half of the third ‘w’ bytes contain data for a second RGB-line. - - $PCD_data_Y1 = fread($fd, $PCD_width); - $PCD_data_Y2 = fread($fd, $PCD_width); - $PCD_data_Cb = fread($fd, intval(round($PCD_width / 2))); - $PCD_data_Cr = fread($fd, intval(round($PCD_width / 2))); - - for ($x = 0; $x < $PCD_width; $x++) { - if ($PCDisVertical) { - $ThisFileInfo['pcd']['data'][$PCD_width - $x][$y] = $this->YCbCr2RGB(ord($PCD_data_Y1{$x}), ord($PCD_data_Cb{floor($x / 2)}), ord($PCD_data_Cr{floor($x / 2)})); - $ThisFileInfo['pcd']['data'][$PCD_width - $x][$y + 1] = $this->YCbCr2RGB(ord($PCD_data_Y2{$x}), ord($PCD_data_Cb{floor($x / 2)}), ord($PCD_data_Cr{floor($x / 2)})); - } else { - $ThisFileInfo['pcd']['data'][$y][$x] = $this->YCbCr2RGB(ord($PCD_data_Y1{$x}), ord($PCD_data_Cb{floor($x / 2)}), ord($PCD_data_Cr{floor($x / 2)})); - $ThisFileInfo['pcd']['data'][$y + 1][$x] = $this->YCbCr2RGB(ord($PCD_data_Y2{$x}), ord($PCD_data_Cb{floor($x / 2)}), ord($PCD_data_Cr{floor($x / 2)})); - } - } - } - - // Example for plotting extracted data - //getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, true); - //if ($PCDisVertical) { - // $BMPinfo['resolution_x'] = $PCD_height; - // $BMPinfo['resolution_y'] = $PCD_width; - //} else { - // $BMPinfo['resolution_x'] = $PCD_width; - // $BMPinfo['resolution_y'] = $PCD_height; - //} - //$BMPinfo['bmp']['data'] = $ThisFileInfo['pcd']['data']; - //getid3_bmp::PlotBMP($BMPinfo); - //exit; - - } - - } - - function YCbCr2RGB($Y, $Cb, $Cr) { - static $YCbCr_constants = array(); - if (empty($YCbCr_constants)) { - $YCbCr_constants['red']['Y'] = 0.0054980 * 256; - $YCbCr_constants['red']['Cb'] = 0.0000000 * 256; - $YCbCr_constants['red']['Cr'] = 0.0051681 * 256; - $YCbCr_constants['green']['Y'] = 0.0054980 * 256; - $YCbCr_constants['green']['Cb'] = -0.0015446 * 256; - $YCbCr_constants['green']['Cr'] = -0.0026325 * 256; - $YCbCr_constants['blue']['Y'] = 0.0054980 * 256; - $YCbCr_constants['blue']['Cb'] = 0.0079533 * 256; - $YCbCr_constants['blue']['Cr'] = 0.0000000 * 256; - } - - $RGBcolor = array('red'=>0, 'green'=>0, 'blue'=>0); - foreach ($RGBcolor as $rgbname => $dummy) { - $RGBcolor[$rgbname] = max(0, - min(255, - intval( - round( - ($YCbCr_constants[$rgbname]['Y'] * $Y) + - ($YCbCr_constants[$rgbname]['Cb'] * ($Cb - 156)) + - ($YCbCr_constants[$rgbname]['Cr'] * ($Cr - 137)) - ) - ) - ) - ); - } - return (($RGBcolor['red'] * 65536) + ($RGBcolor['green'] * 256) + $RGBcolor['blue']); - } - -} - -?> \ No newline at end of file diff --git a/getid3/getid3/module.graphic.png.php b/getid3/getid3/module.graphic.png.php deleted file mode 100644 index 7d82b84..0000000 --- a/getid3/getid3/module.graphic.png.php +++ /dev/null @@ -1,519 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.graphic.png.php // -// module for analyzing PNG Image files // -// dependencies: NONE // -// /// -///////////////////////////////////////////////////////////////// - - -class getid3_png -{ - - function getid3_png(&$fd, &$ThisFileInfo) { - - // shortcut - $ThisFileInfo['png'] = array(); - $thisfile_png = &$ThisFileInfo['png']; - - $ThisFileInfo['fileformat'] = 'png'; - $ThisFileInfo['video']['dataformat'] = 'png'; - $ThisFileInfo['video']['lossless'] = false; - - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $PNGfiledata = fread($fd, GETID3_FREAD_BUFFER_SIZE); - $offset = 0; - - $PNGidentifier = substr($PNGfiledata, $offset, 8); // $89 $50 $4E $47 $0D $0A $1A $0A - $offset += 8; - - if ($PNGidentifier != "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A") { - $ThisFileInfo['error'][] = 'First 8 bytes of file ('.getid3_lib::PrintHexBytes($PNGidentifier).') did not match expected PNG identifier'; - unset($ThisFileInfo['fileformat']); - return false; - } - - while (((ftell($fd) - (strlen($PNGfiledata) - $offset)) < $ThisFileInfo['filesize'])) { - $chunk['data_length'] = getid3_lib::BigEndian2Int(substr($PNGfiledata, $offset, 4)); - $offset += 4; - while (((strlen($PNGfiledata) - $offset) < ($chunk['data_length'] + 4)) && (ftell($fd) < $ThisFileInfo['filesize'])) { - $PNGfiledata .= fread($fd, GETID3_FREAD_BUFFER_SIZE); - } - $chunk['type_text'] = substr($PNGfiledata, $offset, 4); - $offset += 4; - $chunk['type_raw'] = getid3_lib::BigEndian2Int($chunk['type_text']); - $chunk['data'] = substr($PNGfiledata, $offset, $chunk['data_length']); - $offset += $chunk['data_length']; - $chunk['crc'] = getid3_lib::BigEndian2Int(substr($PNGfiledata, $offset, 4)); - $offset += 4; - - $chunk['flags']['ancilliary'] = (bool) ($chunk['type_raw'] & 0x20000000); - $chunk['flags']['private'] = (bool) ($chunk['type_raw'] & 0x00200000); - $chunk['flags']['reserved'] = (bool) ($chunk['type_raw'] & 0x00002000); - $chunk['flags']['safe_to_copy'] = (bool) ($chunk['type_raw'] & 0x00000020); - - // shortcut - $thisfile_png[$chunk['type_text']] = array(); - $thisfile_png_chunk_type_text = &$thisfile_png[$chunk['type_text']]; - - switch ($chunk['type_text']) { - - case 'IHDR': // Image Header - $thisfile_png_chunk_type_text['header'] = $chunk; - $thisfile_png_chunk_type_text['width'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 0, 4)); - $thisfile_png_chunk_type_text['height'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 4, 4)); - $thisfile_png_chunk_type_text['raw']['bit_depth'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 8, 1)); - $thisfile_png_chunk_type_text['raw']['color_type'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 9, 1)); - $thisfile_png_chunk_type_text['raw']['compression_method'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 10, 1)); - $thisfile_png_chunk_type_text['raw']['filter_method'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 11, 1)); - $thisfile_png_chunk_type_text['raw']['interlace_method'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 12, 1)); - - $thisfile_png_chunk_type_text['compression_method_text'] = $this->PNGcompressionMethodLookup($thisfile_png_chunk_type_text['raw']['compression_method']); - $thisfile_png_chunk_type_text['color_type']['palette'] = (bool) ($thisfile_png_chunk_type_text['raw']['color_type'] & 0x01); - $thisfile_png_chunk_type_text['color_type']['true_color'] = (bool) ($thisfile_png_chunk_type_text['raw']['color_type'] & 0x02); - $thisfile_png_chunk_type_text['color_type']['alpha'] = (bool) ($thisfile_png_chunk_type_text['raw']['color_type'] & 0x04); - - $ThisFileInfo['video']['resolution_x'] = $thisfile_png_chunk_type_text['width']; - $ThisFileInfo['video']['resolution_y'] = $thisfile_png_chunk_type_text['height']; - - $ThisFileInfo['video']['bits_per_sample'] = $this->IHDRcalculateBitsPerSample($thisfile_png_chunk_type_text['raw']['color_type'], $thisfile_png_chunk_type_text['raw']['bit_depth']); - break; - - - case 'PLTE': // Palette - $thisfile_png_chunk_type_text['header'] = $chunk; - $paletteoffset = 0; - for ($i = 0; $i <= 255; $i++) { - //$thisfile_png_chunk_type_text['red'][$i] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], $paletteoffset++, 1)); - //$thisfile_png_chunk_type_text['green'][$i] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], $paletteoffset++, 1)); - //$thisfile_png_chunk_type_text['blue'][$i] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], $paletteoffset++, 1)); - $red = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], $paletteoffset++, 1)); - $green = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], $paletteoffset++, 1)); - $blue = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], $paletteoffset++, 1)); - $thisfile_png_chunk_type_text[$i] = (($red << 16) | ($green << 8) | ($blue)); - } - break; - - - case 'tRNS': // Transparency - $thisfile_png_chunk_type_text['header'] = $chunk; - switch ($thisfile_png['IHDR']['raw']['color_type']) { - case 0: - $thisfile_png_chunk_type_text['transparent_color_gray'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 0, 2)); - break; - - case 2: - $thisfile_png_chunk_type_text['transparent_color_red'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 0, 2)); - $thisfile_png_chunk_type_text['transparent_color_green'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 2, 2)); - $thisfile_png_chunk_type_text['transparent_color_blue'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 4, 2)); - break; - - case 3: - for ($i = 0; $i < strlen($thisfile_png_chunk_type_text['header']['data']); $i++) { - $thisfile_png_chunk_type_text['palette_opacity'][$i] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], $i, 1)); - } - break; - - case 4: - case 6: - $ThisFileInfo['error'][] = 'Invalid color_type in tRNS chunk: '.$thisfile_png['IHDR']['raw']['color_type']; - - default: - $ThisFileInfo['warning'][] = 'Unhandled color_type in tRNS chunk: '.$thisfile_png['IHDR']['raw']['color_type']; - break; - } - break; - - - case 'gAMA': // Image Gamma - $thisfile_png_chunk_type_text['header'] = $chunk; - $thisfile_png_chunk_type_text['gamma'] = getid3_lib::BigEndian2Int($thisfile_png_chunk_type_text['header']['data']) / 100000; - break; - - - case 'cHRM': // Primary Chromaticities - $thisfile_png_chunk_type_text['header'] = $chunk; - $thisfile_png_chunk_type_text['white_x'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 0, 4)) / 100000; - $thisfile_png_chunk_type_text['white_y'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 4, 4)) / 100000; - $thisfile_png_chunk_type_text['red_y'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 8, 4)) / 100000; - $thisfile_png_chunk_type_text['red_y'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 12, 4)) / 100000; - $thisfile_png_chunk_type_text['green_y'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 16, 4)) / 100000; - $thisfile_png_chunk_type_text['green_y'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 20, 4)) / 100000; - $thisfile_png_chunk_type_text['blue_y'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 24, 4)) / 100000; - $thisfile_png_chunk_type_text['blue_y'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 28, 4)) / 100000; - break; - - - case 'sRGB': // Standard RGB Color Space - $thisfile_png_chunk_type_text['header'] = $chunk; - $thisfile_png_chunk_type_text['reindering_intent'] = getid3_lib::BigEndian2Int($thisfile_png_chunk_type_text['header']['data']); - $thisfile_png_chunk_type_text['reindering_intent_text'] = $this->PNGsRGBintentLookup($thisfile_png_chunk_type_text['reindering_intent']); - break; - - - case 'iCCP': // Embedded ICC Profile - $thisfile_png_chunk_type_text['header'] = $chunk; - list($profilename, $compressiondata) = explode("\x00", $thisfile_png_chunk_type_text['header']['data'], 2); - $thisfile_png_chunk_type_text['profile_name'] = $profilename; - $thisfile_png_chunk_type_text['compression_method'] = getid3_lib::BigEndian2Int(substr($compressiondata, 0, 1)); - $thisfile_png_chunk_type_text['compression_profile'] = substr($compressiondata, 1); - - $thisfile_png_chunk_type_text['compression_method_text'] = $this->PNGcompressionMethodLookup($thisfile_png_chunk_type_text['compression_method']); - break; - - - case 'tEXt': // Textual Data - $thisfile_png_chunk_type_text['header'] = $chunk; - list($keyword, $text) = explode("\x00", $thisfile_png_chunk_type_text['header']['data'], 2); - $thisfile_png_chunk_type_text['keyword'] = $keyword; - $thisfile_png_chunk_type_text['text'] = $text; - - $thisfile_png['comments'][$thisfile_png_chunk_type_text['keyword']][] = $thisfile_png_chunk_type_text['text']; - break; - - - case 'zTXt': // Compressed Textual Data - $thisfile_png_chunk_type_text['header'] = $chunk; - list($keyword, $otherdata) = explode("\x00", $thisfile_png_chunk_type_text['header']['data'], 2); - $thisfile_png_chunk_type_text['keyword'] = $keyword; - $thisfile_png_chunk_type_text['compression_method'] = getid3_lib::BigEndian2Int(substr($otherdata, 0, 1)); - $thisfile_png_chunk_type_text['compressed_text'] = substr($otherdata, 1); - $thisfile_png_chunk_type_text['compression_method_text'] = $this->PNGcompressionMethodLookup($thisfile_png_chunk_type_text['compression_method']); - switch ($thisfile_png_chunk_type_text['compression_method']) { - case 0: - $thisfile_png_chunk_type_text['text'] = gzuncompress($thisfile_png_chunk_type_text['compressed_text']); - break; - - default: - // unknown compression method - break; - } - - if (isset($thisfile_png_chunk_type_text['text'])) { - $thisfile_png['comments'][$thisfile_png_chunk_type_text['keyword']][] = $thisfile_png_chunk_type_text['text']; - } - break; - - - case 'iTXt': // International Textual Data - $thisfile_png_chunk_type_text['header'] = $chunk; - list($keyword, $otherdata) = explode("\x00", $thisfile_png_chunk_type_text['header']['data'], 2); - $thisfile_png_chunk_type_text['keyword'] = $keyword; - $thisfile_png_chunk_type_text['compression'] = (bool) getid3_lib::BigEndian2Int(substr($otherdata, 0, 1)); - $thisfile_png_chunk_type_text['compression_method'] = getid3_lib::BigEndian2Int(substr($otherdata, 1, 1)); - $thisfile_png_chunk_type_text['compression_method_text'] = $this->PNGcompressionMethodLookup($thisfile_png_chunk_type_text['compression_method']); - list($languagetag, $translatedkeyword, $text) = explode("\x00", substr($otherdata, 2), 3); - $thisfile_png_chunk_type_text['language_tag'] = $languagetag; - $thisfile_png_chunk_type_text['translated_keyword'] = $translatedkeyword; - - if ($thisfile_png_chunk_type_text['compression']) { - - switch ($thisfile_png_chunk_type_text['compression_method']) { - case 0: - $thisfile_png_chunk_type_text['text'] = gzuncompress($text); - break; - - default: - // unknown compression method - break; - } - - } else { - - $thisfile_png_chunk_type_text['text'] = $text; - - } - - if (isset($thisfile_png_chunk_type_text['text'])) { - $thisfile_png['comments'][$thisfile_png_chunk_type_text['keyword']][] = $thisfile_png_chunk_type_text['text']; - } - break; - - - case 'bKGD': // Background Color - $thisfile_png_chunk_type_text['header'] = $chunk; - switch ($thisfile_png['IHDR']['raw']['color_type']) { - case 0: - case 4: - $thisfile_png_chunk_type_text['background_gray'] = getid3_lib::BigEndian2Int($thisfile_png_chunk_type_text['header']['data']); - break; - - case 2: - case 6: - $thisfile_png_chunk_type_text['background_red'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 0 * $thisfile_png['IHDR']['raw']['bit_depth'], $thisfile_png['IHDR']['raw']['bit_depth'])); - $thisfile_png_chunk_type_text['background_green'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 1 * $thisfile_png['IHDR']['raw']['bit_depth'], $thisfile_png['IHDR']['raw']['bit_depth'])); - $thisfile_png_chunk_type_text['background_blue'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 2 * $thisfile_png['IHDR']['raw']['bit_depth'], $thisfile_png['IHDR']['raw']['bit_depth'])); - break; - - case 3: - $thisfile_png_chunk_type_text['background_index'] = getid3_lib::BigEndian2Int($thisfile_png_chunk_type_text['header']['data']); - break; - - default: - break; - } - break; - - - case 'pHYs': // Physical Pixel Dimensions - $thisfile_png_chunk_type_text['header'] = $chunk; - $thisfile_png_chunk_type_text['pixels_per_unit_x'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 0, 4)); - $thisfile_png_chunk_type_text['pixels_per_unit_y'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 4, 4)); - $thisfile_png_chunk_type_text['unit_specifier'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 8, 1)); - $thisfile_png_chunk_type_text['unit'] = $this->PNGpHYsUnitLookup($thisfile_png_chunk_type_text['unit_specifier']); - break; - - - case 'sBIT': // Significant Bits - $thisfile_png_chunk_type_text['header'] = $chunk; - switch ($thisfile_png['IHDR']['raw']['color_type']) { - case 0: - $thisfile_png_chunk_type_text['significant_bits_gray'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 0, 1)); - break; - - case 2: - case 3: - $thisfile_png_chunk_type_text['significant_bits_red'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 0, 1)); - $thisfile_png_chunk_type_text['significant_bits_green'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 1, 1)); - $thisfile_png_chunk_type_text['significant_bits_blue'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 2, 1)); - break; - - case 4: - $thisfile_png_chunk_type_text['significant_bits_gray'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 0, 1)); - $thisfile_png_chunk_type_text['significant_bits_alpha'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 1, 1)); - break; - - case 6: - $thisfile_png_chunk_type_text['significant_bits_red'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 0, 1)); - $thisfile_png_chunk_type_text['significant_bits_green'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 1, 1)); - $thisfile_png_chunk_type_text['significant_bits_blue'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 2, 1)); - $thisfile_png_chunk_type_text['significant_bits_alpha'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 3, 1)); - break; - - default: - break; - } - break; - - - case 'sPLT': // Suggested Palette - $thisfile_png_chunk_type_text['header'] = $chunk; - list($palettename, $otherdata) = explode("\x00", $thisfile_png_chunk_type_text['header']['data'], 2); - $thisfile_png_chunk_type_text['palette_name'] = $palettename; - $sPLToffset = 0; - $thisfile_png_chunk_type_text['sample_depth_bits'] = getid3_lib::BigEndian2Int(substr($otherdata, $sPLToffset, 1)); - $sPLToffset += 1; - $thisfile_png_chunk_type_text['sample_depth_bytes'] = $thisfile_png_chunk_type_text['sample_depth_bits'] / 8; - $paletteCounter = 0; - while ($sPLToffset < strlen($otherdata)) { - $thisfile_png_chunk_type_text['red'][$paletteCounter] = getid3_lib::BigEndian2Int(substr($otherdata, $sPLToffset, $thisfile_png_chunk_type_text['sample_depth_bytes'])); - $sPLToffset += $thisfile_png_chunk_type_text['sample_depth_bytes']; - $thisfile_png_chunk_type_text['green'][$paletteCounter] = getid3_lib::BigEndian2Int(substr($otherdata, $sPLToffset, $thisfile_png_chunk_type_text['sample_depth_bytes'])); - $sPLToffset += $thisfile_png_chunk_type_text['sample_depth_bytes']; - $thisfile_png_chunk_type_text['blue'][$paletteCounter] = getid3_lib::BigEndian2Int(substr($otherdata, $sPLToffset, $thisfile_png_chunk_type_text['sample_depth_bytes'])); - $sPLToffset += $thisfile_png_chunk_type_text['sample_depth_bytes']; - $thisfile_png_chunk_type_text['alpha'][$paletteCounter] = getid3_lib::BigEndian2Int(substr($otherdata, $sPLToffset, $thisfile_png_chunk_type_text['sample_depth_bytes'])); - $sPLToffset += $thisfile_png_chunk_type_text['sample_depth_bytes']; - $thisfile_png_chunk_type_text['frequency'][$paletteCounter] = getid3_lib::BigEndian2Int(substr($otherdata, $sPLToffset, 2)); - $sPLToffset += 2; - $paletteCounter++; - } - break; - - - case 'hIST': // Palette Histogram - $thisfile_png_chunk_type_text['header'] = $chunk; - $hISTcounter = 0; - while ($hISTcounter < strlen($thisfile_png_chunk_type_text['header']['data'])) { - $thisfile_png_chunk_type_text[$hISTcounter] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], $hISTcounter / 2, 2)); - $hISTcounter += 2; - } - break; - - - case 'tIME': // Image Last-Modification Time - $thisfile_png_chunk_type_text['header'] = $chunk; - $thisfile_png_chunk_type_text['year'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 0, 2)); - $thisfile_png_chunk_type_text['month'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 2, 1)); - $thisfile_png_chunk_type_text['day'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 3, 1)); - $thisfile_png_chunk_type_text['hour'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 4, 1)); - $thisfile_png_chunk_type_text['minute'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 5, 1)); - $thisfile_png_chunk_type_text['second'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 6, 1)); - $thisfile_png_chunk_type_text['unix'] = gmmktime($thisfile_png_chunk_type_text['hour'], $thisfile_png_chunk_type_text['minute'], $thisfile_png_chunk_type_text['second'], $thisfile_png_chunk_type_text['month'], $thisfile_png_chunk_type_text['day'], $thisfile_png_chunk_type_text['year']); - break; - - - case 'oFFs': // Image Offset - $thisfile_png_chunk_type_text['header'] = $chunk; - $thisfile_png_chunk_type_text['position_x'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 0, 4), false, true); - $thisfile_png_chunk_type_text['position_y'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 4, 4), false, true); - $thisfile_png_chunk_type_text['unit_specifier'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 8, 1)); - $thisfile_png_chunk_type_text['unit'] = $this->PNGoFFsUnitLookup($thisfile_png_chunk_type_text['unit_specifier']); - break; - - - case 'pCAL': // Calibration Of Pixel Values - $thisfile_png_chunk_type_text['header'] = $chunk; - list($calibrationname, $otherdata) = explode("\x00", $thisfile_png_chunk_type_text['header']['data'], 2); - $thisfile_png_chunk_type_text['calibration_name'] = $calibrationname; - $pCALoffset = 0; - $thisfile_png_chunk_type_text['original_zero'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], $pCALoffset, 4), false, true); - $pCALoffset += 4; - $thisfile_png_chunk_type_text['original_max'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], $pCALoffset, 4), false, true); - $pCALoffset += 4; - $thisfile_png_chunk_type_text['equation_type'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], $pCALoffset, 1)); - $pCALoffset += 1; - $thisfile_png_chunk_type_text['equation_type_text'] = $this->PNGpCALequationTypeLookup($thisfile_png_chunk_type_text['equation_type']); - $thisfile_png_chunk_type_text['parameter_count'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], $pCALoffset, 1)); - $pCALoffset += 1; - $thisfile_png_chunk_type_text['parameters'] = explode("\x00", substr($thisfile_png_chunk_type_text['header']['data'], $pCALoffset)); - break; - - - case 'sCAL': // Physical Scale Of Image Subject - $thisfile_png_chunk_type_text['header'] = $chunk; - $thisfile_png_chunk_type_text['unit_specifier'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 0, 1)); - $thisfile_png_chunk_type_text['unit'] = $this->PNGsCALUnitLookup($thisfile_png_chunk_type_text['unit_specifier']); - list($pixelwidth, $pixelheight) = explode("\x00", substr($thisfile_png_chunk_type_text['header']['data'], 1)); - $thisfile_png_chunk_type_text['pixel_width'] = $pixelwidth; - $thisfile_png_chunk_type_text['pixel_height'] = $pixelheight; - break; - - - case 'gIFg': // GIF Graphic Control Extension - $gIFgCounter = 0; - if (isset($thisfile_png_chunk_type_text) && is_array($thisfile_png_chunk_type_text)) { - $gIFgCounter = count($thisfile_png_chunk_type_text); - } - $thisfile_png_chunk_type_text[$gIFgCounter]['header'] = $chunk; - $thisfile_png_chunk_type_text[$gIFgCounter]['disposal_method'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 0, 1)); - $thisfile_png_chunk_type_text[$gIFgCounter]['user_input_flag'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 1, 1)); - $thisfile_png_chunk_type_text[$gIFgCounter]['delay_time'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 2, 2)); - break; - - - case 'gIFx': // GIF Application Extension - $gIFxCounter = 0; - if (isset($thisfile_png_chunk_type_text) && is_array($thisfile_png_chunk_type_text)) { - $gIFxCounter = count($thisfile_png_chunk_type_text); - } - $thisfile_png_chunk_type_text[$gIFxCounter]['header'] = $chunk; - $thisfile_png_chunk_type_text[$gIFxCounter]['application_identifier'] = substr($thisfile_png_chunk_type_text['header']['data'], 0, 8); - $thisfile_png_chunk_type_text[$gIFxCounter]['authentication_code'] = substr($thisfile_png_chunk_type_text['header']['data'], 8, 3); - $thisfile_png_chunk_type_text[$gIFxCounter]['application_data'] = substr($thisfile_png_chunk_type_text['header']['data'], 11); - break; - - - case 'IDAT': // Image Data - $idatinformationfieldindex = 0; - if (isset($thisfile_png['IDAT']) && is_array($thisfile_png['IDAT'])) { - $idatinformationfieldindex = count($thisfile_png['IDAT']); - } - unset($chunk['data']); - $thisfile_png_chunk_type_text[$idatinformationfieldindex]['header'] = $chunk; - break; - - - case 'IEND': // Image Trailer - $thisfile_png_chunk_type_text['header'] = $chunk; - break; - - - default: - //unset($chunk['data']); - $thisfile_png_chunk_type_text['header'] = $chunk; - $ThisFileInfo['warning'][] = 'Unhandled chunk type: '.$chunk['type_text']; - break; - } - } - - return true; - } - - function PNGsRGBintentLookup($sRGB) { - static $PNGsRGBintentLookup = array( - 0 => 'Perceptual', - 1 => 'Relative colorimetric', - 2 => 'Saturation', - 3 => 'Absolute colorimetric' - ); - return (isset($PNGsRGBintentLookup[$sRGB]) ? $PNGsRGBintentLookup[$sRGB] : 'invalid'); - } - - function PNGcompressionMethodLookup($compressionmethod) { - static $PNGcompressionMethodLookup = array( - 0 => 'deflate/inflate' - ); - return (isset($PNGcompressionMethodLookup[$compressionmethod]) ? $PNGcompressionMethodLookup[$compressionmethod] : 'invalid'); - } - - function PNGpHYsUnitLookup($unitid) { - static $PNGpHYsUnitLookup = array( - 0 => 'unknown', - 1 => 'meter' - ); - return (isset($PNGpHYsUnitLookup[$unitid]) ? $PNGpHYsUnitLookup[$unitid] : 'invalid'); - } - - function PNGoFFsUnitLookup($unitid) { - static $PNGoFFsUnitLookup = array( - 0 => 'pixel', - 1 => 'micrometer' - ); - return (isset($PNGoFFsUnitLookup[$unitid]) ? $PNGoFFsUnitLookup[$unitid] : 'invalid'); - } - - function PNGpCALequationTypeLookup($equationtype) { - static $PNGpCALequationTypeLookup = array( - 0 => 'Linear mapping', - 1 => 'Base-e exponential mapping', - 2 => 'Arbitrary-base exponential mapping', - 3 => 'Hyperbolic mapping' - ); - return (isset($PNGpCALequationTypeLookup[$equationtype]) ? $PNGpCALequationTypeLookup[$equationtype] : 'invalid'); - } - - function PNGsCALUnitLookup($unitid) { - static $PNGsCALUnitLookup = array( - 0 => 'meter', - 1 => 'radian' - ); - return (isset($PNGsCALUnitLookup[$unitid]) ? $PNGsCALUnitLookup[$unitid] : 'invalid'); - } - - function IHDRcalculateBitsPerSample($color_type, $bit_depth) { - switch ($color_type) { - case 0: // Each pixel is a grayscale sample. - return $bit_depth; - break; - - case 2: // Each pixel is an R,G,B triple - return 3 * $bit_depth; - break; - - case 3: // Each pixel is a palette index; a PLTE chunk must appear. - return $bit_depth; - break; - - case 4: // Each pixel is a grayscale sample, followed by an alpha sample. - return 2 * $bit_depth; - break; - - case 6: // Each pixel is an R,G,B triple, followed by an alpha sample. - return 4 * $bit_depth; - break; - } - return false; - } - -} - - -?> \ No newline at end of file diff --git a/getid3/getid3/module.graphic.svg.php b/getid3/getid3/module.graphic.svg.php deleted file mode 100644 index e447145..0000000 --- a/getid3/getid3/module.graphic.svg.php +++ /dev/null @@ -1,52 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.graphic.svg.php // -// module for analyzing SVG Image files // -// dependencies: NONE // -// author: Bryce Harrington // -// /// -///////////////////////////////////////////////////////////////// - - -class getid3_svg -{ - - - function getid3_svg(&$fd, &$ThisFileInfo) { - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - - // I'm making this up, please modify as appropriate - $SVGheader = fread($fd, 32); - $ThisFileInfo['svg']['magic'] = substr($SVGheader, 0, 4); - if ($ThisFileInfo['svg']['magic'] == 'aBcD') { - - $ThisFileInfo['fileformat'] = 'svg'; - $ThisFileInfo['video']['dataformat'] = 'svg'; - $ThisFileInfo['video']['lossless'] = true; - $ThisFileInfo['video']['bits_per_sample'] = 24; - $ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1; - - $ThisFileInfo['svg']['width'] = getid3_lib::LittleEndian2Int(substr($fileData, 4, 4)); - $ThisFileInfo['svg']['height'] = getid3_lib::LittleEndian2Int(substr($fileData, 8, 4)); - - $ThisFileInfo['video']['resolution_x'] = $ThisFileInfo['svg']['width']; - $ThisFileInfo['video']['resolution_y'] = $ThisFileInfo['svg']['height']; - - return true; - } - $ThisFileInfo['error'][] = 'Did not find SVG magic bytes "aBcD" at '.$ThisFileInfo['avdataoffset']; - unset($ThisFileInfo['fileformat']); - return false; - } - -} - - -?> \ No newline at end of file diff --git a/getid3/getid3/module.graphic.tiff.php b/getid3/getid3/module.graphic.tiff.php deleted file mode 100644 index ae57cd6..0000000 --- a/getid3/getid3/module.graphic.tiff.php +++ /dev/null @@ -1,221 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.archive.tiff.php // -// module for analyzing TIFF files // -// dependencies: NONE // -// /// -///////////////////////////////////////////////////////////////// - - -class getid3_tiff -{ - - function getid3_tiff(&$fd, &$ThisFileInfo) { - - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $TIFFheader = fread($fd, 4); - - switch (substr($TIFFheader, 0, 2)) { - case 'II': - $ThisFileInfo['tiff']['byte_order'] = 'Intel'; - break; - case 'MM': - $ThisFileInfo['tiff']['byte_order'] = 'Motorola'; - break; - default: - $ThisFileInfo['error'][] = 'Invalid TIFF byte order identifier ('.substr($TIFFheader, 0, 2).') at offset '.$ThisFileInfo['avdataoffset']; - return false; - break; - } - - $ThisFileInfo['fileformat'] = 'tiff'; - $ThisFileInfo['video']['dataformat'] = 'tiff'; - $ThisFileInfo['video']['lossless'] = true; - $ThisFileInfo['tiff']['ifd'] = array(); - $CurrentIFD = array(); - - $FieldTypeByteLength = array(1=>1, 2=>1, 3=>2, 4=>4, 5=>8); - - $nextIFDoffset = $this->TIFFendian2Int(fread($fd, 4), $ThisFileInfo['tiff']['byte_order']); - - while ($nextIFDoffset > 0) { - - $CurrentIFD['offset'] = $nextIFDoffset; - - fseek($fd, $ThisFileInfo['avdataoffset'] + $nextIFDoffset, SEEK_SET); - $CurrentIFD['fieldcount'] = $this->TIFFendian2Int(fread($fd, 2), $ThisFileInfo['tiff']['byte_order']); - - for ($i = 0; $i < $CurrentIFD['fieldcount']; $i++) { - $CurrentIFD['fields'][$i]['raw']['tag'] = $this->TIFFendian2Int(fread($fd, 2), $ThisFileInfo['tiff']['byte_order']); - $CurrentIFD['fields'][$i]['raw']['type'] = $this->TIFFendian2Int(fread($fd, 2), $ThisFileInfo['tiff']['byte_order']); - $CurrentIFD['fields'][$i]['raw']['length'] = $this->TIFFendian2Int(fread($fd, 4), $ThisFileInfo['tiff']['byte_order']); - $CurrentIFD['fields'][$i]['raw']['offset'] = fread($fd, 4); - - switch ($CurrentIFD['fields'][$i]['raw']['type']) { - case 1: // BYTE An 8-bit unsigned integer. - if ($CurrentIFD['fields'][$i]['raw']['length'] <= 4) { - $CurrentIFD['fields'][$i]['value'] = $this->TIFFendian2Int(substr($CurrentIFD['fields'][$i]['raw']['offset'], 0, 1), $ThisFileInfo['tiff']['byte_order']); - } else { - $CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $ThisFileInfo['tiff']['byte_order']); - } - break; - - case 2: // ASCII 8-bit bytes that store ASCII codes; the last byte must be null. - if ($CurrentIFD['fields'][$i]['raw']['length'] <= 4) { - $CurrentIFD['fields'][$i]['value'] = substr($CurrentIFD['fields'][$i]['raw']['offset'], 3); - } else { - $CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $ThisFileInfo['tiff']['byte_order']); - } - break; - - case 3: // SHORT A 16-bit (2-byte) unsigned integer. - if ($CurrentIFD['fields'][$i]['raw']['length'] <= 2) { - $CurrentIFD['fields'][$i]['value'] = $this->TIFFendian2Int(substr($CurrentIFD['fields'][$i]['raw']['offset'], 0, 2), $ThisFileInfo['tiff']['byte_order']); - } else { - $CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $ThisFileInfo['tiff']['byte_order']); - } - break; - - case 4: // LONG A 32-bit (4-byte) unsigned integer. - if ($CurrentIFD['fields'][$i]['raw']['length'] <= 1) { - $CurrentIFD['fields'][$i]['value'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $ThisFileInfo['tiff']['byte_order']); - } else { - $CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $ThisFileInfo['tiff']['byte_order']); - } - break; - - case 5: // RATIONAL Two LONG_s: the first represents the numerator of a fraction, the second the denominator. - break; - } - } - - $ThisFileInfo['tiff']['ifd'][] = $CurrentIFD; - $CurrentIFD = array(); - $nextIFDoffset = $this->TIFFendian2Int(fread($fd, 4), $ThisFileInfo['tiff']['byte_order']); - - } - - foreach ($ThisFileInfo['tiff']['ifd'] as $IFDid => $IFDarray) { - foreach ($IFDarray['fields'] as $key => $fieldarray) { - switch ($fieldarray['raw']['tag']) { - case 256: // ImageWidth - case 257: // ImageLength - case 258: // BitsPerSample - case 259: // Compression - if (!isset($fieldarray['value'])) { - fseek($fd, $fieldarray['offset'], SEEK_SET); - $ThisFileInfo['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = fread($fd, $fieldarray['raw']['length'] * $FieldTypeByteLength[$fieldarray['raw']['type']]); - - } - break; - - case 270: // ImageDescription - case 271: // Make - case 272: // Model - case 305: // Software - case 306: // DateTime - case 315: // Artist - case 316: // HostComputer - if (isset($fieldarray['value'])) { - $ThisFileInfo['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = $fieldarray['value']; - } else { - fseek($fd, $fieldarray['offset'], SEEK_SET); - $ThisFileInfo['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = fread($fd, $fieldarray['raw']['length'] * $FieldTypeByteLength[$fieldarray['raw']['type']]); - - } - break; - } - switch ($fieldarray['raw']['tag']) { - case 256: // ImageWidth - $ThisFileInfo['video']['resolution_x'] = $fieldarray['value']; - break; - - case 257: // ImageLength - $ThisFileInfo['video']['resolution_y'] = $fieldarray['value']; - break; - - case 258: // BitsPerSample - if (isset($fieldarray['value'])) { - $ThisFileInfo['video']['bits_per_sample'] = $fieldarray['value']; - } else { - $ThisFileInfo['video']['bits_per_sample'] = 0; - for ($i = 0; $i < $fieldarray['raw']['length']; $i++) { - $ThisFileInfo['video']['bits_per_sample'] += $this->TIFFendian2Int(substr($ThisFileInfo['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'], $i * $FieldTypeByteLength[$fieldarray['raw']['type']], $FieldTypeByteLength[$fieldarray['raw']['type']]), $ThisFileInfo['tiff']['byte_order']); - } - } - break; - - case 259: // Compression - $ThisFileInfo['video']['codec'] = $this->TIFFcompressionMethod($fieldarray['value']); - break; - - case 270: // ImageDescription - case 271: // Make - case 272: // Model - case 305: // Software - case 306: // DateTime - case 315: // Artist - case 316: // HostComputer - @$ThisFileInfo['tiff']['comments'][$this->TIFFcommentName($fieldarray['raw']['tag'])][] = $ThisFileInfo['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data']; - break; - - default: - break; - } - } - } - - return true; - } - - - function TIFFendian2Int($bytestring, $byteorder) { - if ($byteorder == 'Intel') { - return getid3_lib::LittleEndian2Int($bytestring); - } elseif ($byteorder == 'Motorola') { - return getid3_lib::BigEndian2Int($bytestring); - } - return false; - } - - function TIFFcompressionMethod($id) { - static $TIFFcompressionMethod = array(); - if (empty($TIFFcompressionMethod)) { - $TIFFcompressionMethod = array( - 1 => 'Uncompressed', - 2 => 'Huffman', - 3 => 'Fax - CCITT 3', - 5 => 'LZW', - 32773 => 'PackBits', - ); - } - return (isset($TIFFcompressionMethod[$id]) ? $TIFFcompressionMethod[$id] : 'unknown/invalid ('.$id.')'); - } - - function TIFFcommentName($id) { - static $TIFFcommentName = array(); - if (empty($TIFFcommentName)) { - $TIFFcommentName = array( - 270 => 'imagedescription', - 271 => 'make', - 272 => 'model', - 305 => 'software', - 306 => 'datetime', - 315 => 'artist', - 316 => 'hostcomputer', - ); - } - return (isset($TIFFcommentName[$id]) ? $TIFFcommentName[$id] : 'unknown/invalid ('.$id.')'); - } - -} - - -?> \ No newline at end of file diff --git a/getid3/getid3/module.misc.exe.php b/getid3/getid3/module.misc.exe.php deleted file mode 100644 index 8c6bfcf..0000000 --- a/getid3/getid3/module.misc.exe.php +++ /dev/null @@ -1,59 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.misc.exe.php // -// module for analyzing EXE files // -// dependencies: NONE // -// /// -///////////////////////////////////////////////////////////////// - - -class getid3_exe -{ - - function getid3_exe(&$fd, &$ThisFileInfo) { - - fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); - $EXEheader = fread($fd, 28); - - if (substr($EXEheader, 0, 2) != 'MZ') { - $ThisFileInfo['error'][] = 'Expecting "MZ" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($EXEheader, 0, 2).'" instead.'; - return false; - } - - $ThisFileInfo['fileformat'] = 'exe'; - $ThisFileInfo['exe']['mz']['magic'] = 'MZ'; - - $ThisFileInfo['exe']['mz']['raw']['last_page_size'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 2, 2)); - $ThisFileInfo['exe']['mz']['raw']['page_count'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 4, 2)); - $ThisFileInfo['exe']['mz']['raw']['relocation_count'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 6, 2)); - $ThisFileInfo['exe']['mz']['raw']['header_paragraphs'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 8, 2)); - $ThisFileInfo['exe']['mz']['raw']['min_memory_paragraphs'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 10, 2)); - $ThisFileInfo['exe']['mz']['raw']['max_memory_paragraphs'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 12, 2)); - $ThisFileInfo['exe']['mz']['raw']['initial_ss'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 14, 2)); - $ThisFileInfo['exe']['mz']['raw']['initial_sp'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 16, 2)); - $ThisFileInfo['exe']['mz']['raw']['checksum'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 18, 2)); - $ThisFileInfo['exe']['mz']['raw']['cs_ip'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 20, 4)); - $ThisFileInfo['exe']['mz']['raw']['relocation_table_offset'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 24, 2)); - $ThisFileInfo['exe']['mz']['raw']['overlay_number'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 26, 2)); - - $ThisFileInfo['exe']['mz']['byte_size'] = (($ThisFileInfo['exe']['mz']['raw']['page_count'] - 1)) * 512 + $ThisFileInfo['exe']['mz']['raw']['last_page_size']; - $ThisFileInfo['exe']['mz']['header_size'] = $ThisFileInfo['exe']['mz']['raw']['header_paragraphs'] * 16; - $ThisFileInfo['exe']['mz']['memory_minimum'] = $ThisFileInfo['exe']['mz']['raw']['min_memory_paragraphs'] * 16; - $ThisFileInfo['exe']['mz']['memory_recommended'] = $ThisFileInfo['exe']['mz']['raw']['max_memory_paragraphs'] * 16; - -$ThisFileInfo['error'][] = 'EXE parsing not enabled in this version of getID3()'; -return false; - - } - -} - - -?> \ No newline at end of file diff --git a/getid3/getid3/module.misc.iso.php b/getid3/getid3/module.misc.iso.php deleted file mode 100644 index 94df929..0000000 --- a/getid3/getid3/module.misc.iso.php +++ /dev/null @@ -1,386 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.misc.iso.php // -// module for analyzing ISO files // -// dependencies: NONE // -// /// -///////////////////////////////////////////////////////////////// - - -class getid3_iso -{ - - function getid3_iso($fd, &$ThisFileInfo) { - $ThisFileInfo['fileformat'] = 'iso'; - - for ($i = 16; $i <= 19; $i++) { - fseek($fd, 2048 * $i, SEEK_SET); - $ISOheader = fread($fd, 2048); - if (substr($ISOheader, 1, 5) == 'CD001') { - switch (ord($ISOheader{0})) { - case 1: - $ThisFileInfo['iso']['primary_volume_descriptor']['offset'] = 2048 * $i; - $this->ParsePrimaryVolumeDescriptor($ISOheader, $ThisFileInfo); - break; - - case 2: - $ThisFileInfo['iso']['supplementary_volume_descriptor']['offset'] = 2048 * $i; - $this->ParseSupplementaryVolumeDescriptor($ISOheader, $ThisFileInfo); - break; - - default: - // skip - break; - } - } - } - - $this->ParsePathTable($fd, $ThisFileInfo); - - $ThisFileInfo['iso']['files'] = array(); - foreach ($ThisFileInfo['iso']['path_table']['directories'] as $directorynum => $directorydata) { - - $ThisFileInfo['iso']['directories'][$directorynum] = $this->ParseDirectoryRecord($fd, $directorydata, $ThisFileInfo); - - } - - return true; - - } - - - function ParsePrimaryVolumeDescriptor(&$ISOheader, &$ThisFileInfo) { - // ISO integer values are stored *BOTH* Little-Endian AND Big-Endian format!! - // ie 12345 == 0x3039 is stored as $39 $30 $30 $39 in a 4-byte field - - // shortcuts - $ThisFileInfo['iso']['primary_volume_descriptor']['raw'] = array(); - $thisfile_iso_primaryVD = &$ThisFileInfo['iso']['primary_volume_descriptor']; - $thisfile_iso_primaryVD_raw = &$thisfile_iso_primaryVD['raw']; - - $thisfile_iso_primaryVD_raw['volume_descriptor_type'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 0, 1)); - $thisfile_iso_primaryVD_raw['standard_identifier'] = substr($ISOheader, 1, 5); - if ($thisfile_iso_primaryVD_raw['standard_identifier'] != 'CD001') { - $ThisFileInfo['error'][] = 'Expected "CD001" at offset ('.($thisfile_iso_primaryVD['offset'] + 1).'), found "'.$thisfile_iso_primaryVD_raw['standard_identifier'].'" instead'; - unset($ThisFileInfo['fileformat']); - unset($ThisFileInfo['iso']); - return false; - } - - - $thisfile_iso_primaryVD_raw['volume_descriptor_version'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 6, 1)); - //$thisfile_iso_primaryVD_raw['unused_1'] = substr($ISOheader, 7, 1); - $thisfile_iso_primaryVD_raw['system_identifier'] = substr($ISOheader, 8, 32); - $thisfile_iso_primaryVD_raw['volume_identifier'] = substr($ISOheader, 40, 32); - //$thisfile_iso_primaryVD_raw['unused_2'] = substr($ISOheader, 72, 8); - $thisfile_iso_primaryVD_raw['volume_space_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 80, 4)); - //$thisfile_iso_primaryVD_raw['unused_3'] = substr($ISOheader, 88, 32); - $thisfile_iso_primaryVD_raw['volume_set_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 120, 2)); - $thisfile_iso_primaryVD_raw['volume_sequence_number'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 124, 2)); - $thisfile_iso_primaryVD_raw['logical_block_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 128, 2)); - $thisfile_iso_primaryVD_raw['path_table_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 132, 4)); - $thisfile_iso_primaryVD_raw['path_table_l_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 140, 2)); - $thisfile_iso_primaryVD_raw['path_table_l_opt_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 144, 2)); - $thisfile_iso_primaryVD_raw['path_table_m_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 148, 2)); - $thisfile_iso_primaryVD_raw['path_table_m_opt_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 152, 2)); - $thisfile_iso_primaryVD_raw['root_directory_record'] = substr($ISOheader, 156, 34); - $thisfile_iso_primaryVD_raw['volume_set_identifier'] = substr($ISOheader, 190, 128); - $thisfile_iso_primaryVD_raw['publisher_identifier'] = substr($ISOheader, 318, 128); - $thisfile_iso_primaryVD_raw['data_preparer_identifier'] = substr($ISOheader, 446, 128); - $thisfile_iso_primaryVD_raw['application_identifier'] = substr($ISOheader, 574, 128); - $thisfile_iso_primaryVD_raw['copyright_file_identifier'] = substr($ISOheader, 702, 37); - $thisfile_iso_primaryVD_raw['abstract_file_identifier'] = substr($ISOheader, 739, 37); - $thisfile_iso_primaryVD_raw['bibliographic_file_identifier'] = substr($ISOheader, 776, 37); - $thisfile_iso_primaryVD_raw['volume_creation_date_time'] = substr($ISOheader, 813, 17); - $thisfile_iso_primaryVD_raw['volume_modification_date_time'] = substr($ISOheader, 830, 17); - $thisfile_iso_primaryVD_raw['volume_expiration_date_time'] = substr($ISOheader, 847, 17); - $thisfile_iso_primaryVD_raw['volume_effective_date_time'] = substr($ISOheader, 864, 17); - $thisfile_iso_primaryVD_raw['file_structure_version'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 881, 1)); - //$thisfile_iso_primaryVD_raw['unused_4'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 882, 1)); - $thisfile_iso_primaryVD_raw['application_data'] = substr($ISOheader, 883, 512); - //$thisfile_iso_primaryVD_raw['unused_5'] = substr($ISOheader, 1395, 653); - - $thisfile_iso_primaryVD['system_identifier'] = trim($thisfile_iso_primaryVD_raw['system_identifier']); - $thisfile_iso_primaryVD['volume_identifier'] = trim($thisfile_iso_primaryVD_raw['volume_identifier']); - $thisfile_iso_primaryVD['volume_set_identifier'] = trim($thisfile_iso_primaryVD_raw['volume_set_identifier']); - $thisfile_iso_primaryVD['publisher_identifier'] = trim($thisfile_iso_primaryVD_raw['publisher_identifier']); - $thisfile_iso_primaryVD['data_preparer_identifier'] = trim($thisfile_iso_primaryVD_raw['data_preparer_identifier']); - $thisfile_iso_primaryVD['application_identifier'] = trim($thisfile_iso_primaryVD_raw['application_identifier']); - $thisfile_iso_primaryVD['copyright_file_identifier'] = trim($thisfile_iso_primaryVD_raw['copyright_file_identifier']); - $thisfile_iso_primaryVD['abstract_file_identifier'] = trim($thisfile_iso_primaryVD_raw['abstract_file_identifier']); - $thisfile_iso_primaryVD['bibliographic_file_identifier'] = trim($thisfile_iso_primaryVD_raw['bibliographic_file_identifier']); - $thisfile_iso_primaryVD['volume_creation_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_primaryVD_raw['volume_creation_date_time']); - $thisfile_iso_primaryVD['volume_modification_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_primaryVD_raw['volume_modification_date_time']); - $thisfile_iso_primaryVD['volume_expiration_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_primaryVD_raw['volume_expiration_date_time']); - $thisfile_iso_primaryVD['volume_effective_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_primaryVD_raw['volume_effective_date_time']); - - if (($thisfile_iso_primaryVD_raw['volume_space_size'] * 2048) > $ThisFileInfo['filesize']) { - $ThisFileInfo['error'][] = 'Volume Space Size ('.($thisfile_iso_primaryVD_raw['volume_space_size'] * 2048).' bytes) is larger than the file size ('.$ThisFileInfo['filesize'].' bytes) (truncated file?)'; - } - - return true; - } - - - function ParseSupplementaryVolumeDescriptor(&$ISOheader, &$ThisFileInfo) { - // ISO integer values are stored Both-Endian format!! - // ie 12345 == 0x3039 is stored as $39 $30 $30 $39 in a 4-byte field - - // shortcuts - $ThisFileInfo['iso']['supplementary_volume_descriptor']['raw'] = array(); - $thisfile_iso_supplementaryVD = &$ThisFileInfo['iso']['supplementary_volume_descriptor']; - $thisfile_iso_supplementaryVD_raw = &$thisfile_iso_supplementaryVD['raw']; - - $thisfile_iso_supplementaryVD_raw['volume_descriptor_type'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 0, 1)); - $thisfile_iso_supplementaryVD_raw['standard_identifier'] = substr($ISOheader, 1, 5); - if ($thisfile_iso_supplementaryVD_raw['standard_identifier'] != 'CD001') { - $ThisFileInfo['error'][] = 'Expected "CD001" at offset ('.($thisfile_iso_supplementaryVD['offset'] + 1).'), found "'.$thisfile_iso_supplementaryVD_raw['standard_identifier'].'" instead'; - unset($ThisFileInfo['fileformat']); - unset($ThisFileInfo['iso']); - return false; - } - - $thisfile_iso_supplementaryVD_raw['volume_descriptor_version'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 6, 1)); - //$thisfile_iso_supplementaryVD_raw['unused_1'] = substr($ISOheader, 7, 1); - $thisfile_iso_supplementaryVD_raw['system_identifier'] = substr($ISOheader, 8, 32); - $thisfile_iso_supplementaryVD_raw['volume_identifier'] = substr($ISOheader, 40, 32); - //$thisfile_iso_supplementaryVD_raw['unused_2'] = substr($ISOheader, 72, 8); - $thisfile_iso_supplementaryVD_raw['volume_space_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 80, 4)); - if ($thisfile_iso_supplementaryVD_raw['volume_space_size'] == 0) { - // Supplementary Volume Descriptor not used - //unset($thisfile_iso_supplementaryVD); - //return false; - } - - //$thisfile_iso_supplementaryVD_raw['unused_3'] = substr($ISOheader, 88, 32); - $thisfile_iso_supplementaryVD_raw['volume_set_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 120, 2)); - $thisfile_iso_supplementaryVD_raw['volume_sequence_number'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 124, 2)); - $thisfile_iso_supplementaryVD_raw['logical_block_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 128, 2)); - $thisfile_iso_supplementaryVD_raw['path_table_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 132, 4)); - $thisfile_iso_supplementaryVD_raw['path_table_l_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 140, 2)); - $thisfile_iso_supplementaryVD_raw['path_table_l_opt_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 144, 2)); - $thisfile_iso_supplementaryVD_raw['path_table_m_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 148, 2)); - $thisfile_iso_supplementaryVD_raw['path_table_m_opt_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 152, 2)); - $thisfile_iso_supplementaryVD_raw['root_directory_record'] = substr($ISOheader, 156, 34); - $thisfile_iso_supplementaryVD_raw['volume_set_identifier'] = substr($ISOheader, 190, 128); - $thisfile_iso_supplementaryVD_raw['publisher_identifier'] = substr($ISOheader, 318, 128); - $thisfile_iso_supplementaryVD_raw['data_preparer_identifier'] = substr($ISOheader, 446, 128); - $thisfile_iso_supplementaryVD_raw['application_identifier'] = substr($ISOheader, 574, 128); - $thisfile_iso_supplementaryVD_raw['copyright_file_identifier'] = substr($ISOheader, 702, 37); - $thisfile_iso_supplementaryVD_raw['abstract_file_identifier'] = substr($ISOheader, 739, 37); - $thisfile_iso_supplementaryVD_raw['bibliographic_file_identifier'] = substr($ISOheader, 776, 37); - $thisfile_iso_supplementaryVD_raw['volume_creation_date_time'] = substr($ISOheader, 813, 17); - $thisfile_iso_supplementaryVD_raw['volume_modification_date_time'] = substr($ISOheader, 830, 17); - $thisfile_iso_supplementaryVD_raw['volume_expiration_date_time'] = substr($ISOheader, 847, 17); - $thisfile_iso_supplementaryVD_raw['volume_effective_date_time'] = substr($ISOheader, 864, 17); - $thisfile_iso_supplementaryVD_raw['file_structure_version'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 881, 1)); - //$thisfile_iso_supplementaryVD_raw['unused_4'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 882, 1)); - $thisfile_iso_supplementaryVD_raw['application_data'] = substr($ISOheader, 883, 512); - //$thisfile_iso_supplementaryVD_raw['unused_5'] = substr($ISOheader, 1395, 653); - - $thisfile_iso_supplementaryVD['system_identifier'] = trim($thisfile_iso_supplementaryVD_raw['system_identifier']); - $thisfile_iso_supplementaryVD['volume_identifier'] = trim($thisfile_iso_supplementaryVD_raw['volume_identifier']); - $thisfile_iso_supplementaryVD['volume_set_identifier'] = trim($thisfile_iso_supplementaryVD_raw['volume_set_identifier']); - $thisfile_iso_supplementaryVD['publisher_identifier'] = trim($thisfile_iso_supplementaryVD_raw['publisher_identifier']); - $thisfile_iso_supplementaryVD['data_preparer_identifier'] = trim($thisfile_iso_supplementaryVD_raw['data_preparer_identifier']); - $thisfile_iso_supplementaryVD['application_identifier'] = trim($thisfile_iso_supplementaryVD_raw['application_identifier']); - $thisfile_iso_supplementaryVD['copyright_file_identifier'] = trim($thisfile_iso_supplementaryVD_raw['copyright_file_identifier']); - $thisfile_iso_supplementaryVD['abstract_file_identifier'] = trim($thisfile_iso_supplementaryVD_raw['abstract_file_identifier']); - $thisfile_iso_supplementaryVD['bibliographic_file_identifier'] = trim($thisfile_iso_supplementaryVD_raw['bibliographic_file_identifier']); - $thisfile_iso_supplementaryVD['volume_creation_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_supplementaryVD_raw['volume_creation_date_time']); - $thisfile_iso_supplementaryVD['volume_modification_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_supplementaryVD_raw['volume_modification_date_time']); - $thisfile_iso_supplementaryVD['volume_expiration_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_supplementaryVD_raw['volume_expiration_date_time']); - $thisfile_iso_supplementaryVD['volume_effective_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_supplementaryVD_raw['volume_effective_date_time']); - - if (($thisfile_iso_supplementaryVD_raw['volume_space_size'] * $thisfile_iso_supplementaryVD_raw['logical_block_size']) > $ThisFileInfo['filesize']) { - $ThisFileInfo['error'][] = 'Volume Space Size ('.($thisfile_iso_supplementaryVD_raw['volume_space_size'] * $thisfile_iso_supplementaryVD_raw['logical_block_size']).' bytes) is larger than the file size ('.$ThisFileInfo['filesize'].' bytes) (truncated file?)'; - } - - return true; - } - - - function ParsePathTable($fd, &$ThisFileInfo) { - if (!isset($ThisFileInfo['iso']['supplementary_volume_descriptor']['raw']['path_table_l_location']) && !isset($ThisFileInfo['iso']['primary_volume_descriptor']['raw']['path_table_l_location'])) { - return false; - } - if (isset($ThisFileInfo['iso']['supplementary_volume_descriptor']['raw']['path_table_l_location'])) { - $PathTableLocation = $ThisFileInfo['iso']['supplementary_volume_descriptor']['raw']['path_table_l_location']; - $PathTableSize = $ThisFileInfo['iso']['supplementary_volume_descriptor']['raw']['path_table_size']; - $TextEncoding = 'UTF-16BE'; // Big-Endian Unicode - } else { - $PathTableLocation = $ThisFileInfo['iso']['primary_volume_descriptor']['raw']['path_table_l_location']; - $PathTableSize = $ThisFileInfo['iso']['primary_volume_descriptor']['raw']['path_table_size']; - $TextEncoding = 'ISO-8859-1'; // Latin-1 - } - - if (($PathTableLocation * 2048) > $ThisFileInfo['filesize']) { - $ThisFileInfo['error'][] = 'Path Table Location specifies an offset ('.($PathTableLocation * 2048).') beyond the end-of-file ('.$ThisFileInfo['filesize'].')'; - return false; - } - - $ThisFileInfo['iso']['path_table']['offset'] = $PathTableLocation * 2048; - fseek($fd, $ThisFileInfo['iso']['path_table']['offset'], SEEK_SET); - $ThisFileInfo['iso']['path_table']['raw'] = fread($fd, $PathTableSize); - - $offset = 0; - $pathcounter = 1; - while ($offset < $PathTableSize) { - // shortcut - $ThisFileInfo['iso']['path_table']['directories'][$pathcounter] = array(); - $thisfile_iso_pathtable_directories_current = &$ThisFileInfo['iso']['path_table']['directories'][$pathcounter]; - - $thisfile_iso_pathtable_directories_current['length'] = getid3_lib::LittleEndian2Int(substr($ThisFileInfo['iso']['path_table']['raw'], $offset, 1)); - $offset += 1; - $thisfile_iso_pathtable_directories_current['extended_length'] = getid3_lib::LittleEndian2Int(substr($ThisFileInfo['iso']['path_table']['raw'], $offset, 1)); - $offset += 1; - $thisfile_iso_pathtable_directories_current['location_logical'] = getid3_lib::LittleEndian2Int(substr($ThisFileInfo['iso']['path_table']['raw'], $offset, 4)); - $offset += 4; - $thisfile_iso_pathtable_directories_current['parent_directory'] = getid3_lib::LittleEndian2Int(substr($ThisFileInfo['iso']['path_table']['raw'], $offset, 2)); - $offset += 2; - $thisfile_iso_pathtable_directories_current['name'] = substr($ThisFileInfo['iso']['path_table']['raw'], $offset, $thisfile_iso_pathtable_directories_current['length']); - $offset += $thisfile_iso_pathtable_directories_current['length'] + ($thisfile_iso_pathtable_directories_current['length'] % 2); - - $thisfile_iso_pathtable_directories_current['name_ascii'] = getid3_lib::iconv_fallback($TextEncoding, $ThisFileInfo['encoding'], $thisfile_iso_pathtable_directories_current['name']); - - $thisfile_iso_pathtable_directories_current['location_bytes'] = $thisfile_iso_pathtable_directories_current['location_logical'] * 2048; - if ($pathcounter == 1) { - $thisfile_iso_pathtable_directories_current['full_path'] = '/'; - } else { - $thisfile_iso_pathtable_directories_current['full_path'] = $ThisFileInfo['iso']['path_table']['directories'][$thisfile_iso_pathtable_directories_current['parent_directory']]['full_path'].$thisfile_iso_pathtable_directories_current['name_ascii'].'/'; - } - $FullPathArray[] = $thisfile_iso_pathtable_directories_current['full_path']; - - $pathcounter++; - } - - return true; - } - - - function ParseDirectoryRecord(&$fd, $directorydata, &$ThisFileInfo) { - if (isset($ThisFileInfo['iso']['supplementary_volume_descriptor'])) { - $TextEncoding = 'UTF-16BE'; // Big-Endian Unicode - } else { - $TextEncoding = 'ISO-8859-1'; // Latin-1 - } - - fseek($fd, $directorydata['location_bytes'], SEEK_SET); - $DirectoryRecordData = fread($fd, 1); - - while (ord($DirectoryRecordData{0}) > 33) { - - $DirectoryRecordData .= fread($fd, ord($DirectoryRecordData{0}) - 1); - - $ThisDirectoryRecord['raw']['length'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 0, 1)); - $ThisDirectoryRecord['raw']['extended_attribute_length'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 1, 1)); - $ThisDirectoryRecord['raw']['offset_logical'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 2, 4)); - $ThisDirectoryRecord['raw']['filesize'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 10, 4)); - $ThisDirectoryRecord['raw']['recording_date_time'] = substr($DirectoryRecordData, 18, 7); - $ThisDirectoryRecord['raw']['file_flags'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 25, 1)); - $ThisDirectoryRecord['raw']['file_unit_size'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 26, 1)); - $ThisDirectoryRecord['raw']['interleave_gap_size'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 27, 1)); - $ThisDirectoryRecord['raw']['volume_sequence_number'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 28, 2)); - $ThisDirectoryRecord['raw']['file_identifier_length'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 32, 1)); - $ThisDirectoryRecord['raw']['file_identifier'] = substr($DirectoryRecordData, 33, $ThisDirectoryRecord['raw']['file_identifier_length']); - - $ThisDirectoryRecord['file_identifier_ascii'] = getid3_lib::iconv_fallback($TextEncoding, $ThisFileInfo['encoding'], $ThisDirectoryRecord['raw']['file_identifier']); - - $ThisDirectoryRecord['filesize'] = $ThisDirectoryRecord['raw']['filesize']; - $ThisDirectoryRecord['offset_bytes'] = $ThisDirectoryRecord['raw']['offset_logical'] * 2048; - $ThisDirectoryRecord['file_flags']['hidden'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x01); - $ThisDirectoryRecord['file_flags']['directory'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x02); - $ThisDirectoryRecord['file_flags']['associated'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x04); - $ThisDirectoryRecord['file_flags']['extended'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x08); - $ThisDirectoryRecord['file_flags']['permissions'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x10); - $ThisDirectoryRecord['file_flags']['multiple'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x80); - $ThisDirectoryRecord['recording_timestamp'] = $this->ISOtime2UNIXtime($ThisDirectoryRecord['raw']['recording_date_time']); - - if ($ThisDirectoryRecord['file_flags']['directory']) { - $ThisDirectoryRecord['filename'] = $directorydata['full_path']; - } else { - $ThisDirectoryRecord['filename'] = $directorydata['full_path'].$this->ISOstripFilenameVersion($ThisDirectoryRecord['file_identifier_ascii']); - $ThisFileInfo['iso']['files'] = getid3_lib::array_merge_clobber($ThisFileInfo['iso']['files'], getid3_lib::CreateDeepArray($ThisDirectoryRecord['filename'], '/', $ThisDirectoryRecord['filesize'])); - } - - $DirectoryRecord[] = $ThisDirectoryRecord; - $DirectoryRecordData = fread($fd, 1); - } - - return $DirectoryRecord; - } - - function ISOstripFilenameVersion($ISOfilename) { - // convert 'filename.ext;1' to 'filename.ext' - if (!strstr($ISOfilename, ';')) { - return $ISOfilename; - } else { - return substr($ISOfilename, 0, strpos($ISOfilename, ';')); - } - } - - function ISOtimeText2UNIXtime($ISOtime) { - - $UNIXyear = (int) substr($ISOtime, 0, 4); - $UNIXmonth = (int) substr($ISOtime, 4, 2); - $UNIXday = (int) substr($ISOtime, 6, 2); - $UNIXhour = (int) substr($ISOtime, 8, 2); - $UNIXminute = (int) substr($ISOtime, 10, 2); - $UNIXsecond = (int) substr($ISOtime, 12, 2); - - if (!$UNIXyear) { - return false; - } - return gmmktime($UNIXhour, $UNIXminute, $UNIXsecond, $UNIXmonth, $UNIXday, $UNIXyear); - } - - function ISOtime2UNIXtime($ISOtime) { - // Represented by seven bytes: - // 1: Number of years since 1900 - // 2: Month of the year from 1 to 12 - // 3: Day of the Month from 1 to 31 - // 4: Hour of the day from 0 to 23 - // 5: Minute of the hour from 0 to 59 - // 6: second of the minute from 0 to 59 - // 7: Offset from Greenwich Mean Time in number of 15 minute intervals from -48 (West) to +52 (East) - - $UNIXyear = ord($ISOtime{0}) + 1900; - $UNIXmonth = ord($ISOtime{1}); - $UNIXday = ord($ISOtime{2}); - $UNIXhour = ord($ISOtime{3}); - $UNIXminute = ord($ISOtime{4}); - $UNIXsecond = ord($ISOtime{5}); - $GMToffset = $this->TwosCompliment2Decimal(ord($ISOtime{5})); - - return gmmktime($UNIXhour, $UNIXminute, $UNIXsecond, $UNIXmonth, $UNIXday, $UNIXyear); - } - - function TwosCompliment2Decimal($BinaryValue) { - // http://sandbox.mc.edu/~bennet/cs110/tc/tctod.html - // First check if the number is negative or positive by looking at the sign bit. - // If it is positive, simply convert it to decimal. - // If it is negative, make it positive by inverting the bits and adding one. - // Then, convert the result to decimal. - // The negative of this number is the value of the original binary. - - if ($BinaryValue & 0x80) { - - // negative number - return (0 - ((~$BinaryValue & 0xFF) + 1)); - } else { - // positive number - return $BinaryValue; - } - } - - -} - -?> \ No newline at end of file diff --git a/getid3/getid3/module.tag.apetag.php b/getid3/getid3/module.tag.apetag.php deleted file mode 100644 index 6ea8010..0000000 --- a/getid3/getid3/module.tag.apetag.php +++ /dev/null @@ -1,284 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.tag.apetag.php // -// module for analyzing APE tags // -// dependencies: NONE // -// /// -///////////////////////////////////////////////////////////////// - -class getid3_apetag -{ - - function getid3_apetag(&$fd, &$ThisFileInfo, $overrideendoffset=0) { - $id3v1tagsize = 128; - $apetagheadersize = 32; - $lyrics3tagsize = 10; - - if ($overrideendoffset == 0) { - - fseek($fd, 0 - $id3v1tagsize - $apetagheadersize - $lyrics3tagsize, SEEK_END); - $APEfooterID3v1 = fread($fd, $id3v1tagsize + $apetagheadersize + $lyrics3tagsize); - - //if (preg_match('/APETAGEX.{24}TAG.{125}$/i', $APEfooterID3v1)) { - if (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $id3v1tagsize - $apetagheadersize, 8) == 'APETAGEX') { - - // APE tag found before ID3v1 - $ThisFileInfo['ape']['tag_offset_end'] = $ThisFileInfo['filesize'] - $id3v1tagsize; - - //} elseif (preg_match('/APETAGEX.{24}$/i', $APEfooterID3v1)) { - } elseif (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $apetagheadersize, 8) == 'APETAGEX') { - - // APE tag found, no ID3v1 - $ThisFileInfo['ape']['tag_offset_end'] = $ThisFileInfo['filesize']; - - } - - } else { - - fseek($fd, $overrideendoffset - $apetagheadersize, SEEK_SET); - if (fread($fd, 8) == 'APETAGEX') { - $ThisFileInfo['ape']['tag_offset_end'] = $overrideendoffset; - } - - } - if (!isset($ThisFileInfo['ape']['tag_offset_end'])) { - - // APE tag not found - unset($ThisFileInfo['ape']); - return false; - - } - - // shortcut - $thisfile_ape = &$ThisFileInfo['ape']; - - fseek($fd, $thisfile_ape['tag_offset_end'] - $apetagheadersize, SEEK_SET); - $APEfooterData = fread($fd, 32); - if (!($thisfile_ape['footer'] = $this->parseAPEheaderFooter($APEfooterData))) { - $ThisFileInfo['error'][] = 'Error parsing APE footer at offset '.$thisfile_ape['tag_offset_end']; - return false; - } - - if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) { - fseek($fd, $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'] - $apetagheadersize, SEEK_SET); - $thisfile_ape['tag_offset_start'] = ftell($fd); - $APEtagData = fread($fd, $thisfile_ape['footer']['raw']['tagsize'] + $apetagheadersize); - } else { - $thisfile_ape['tag_offset_start'] = $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize']; - fseek($fd, $thisfile_ape['tag_offset_start'], SEEK_SET); - $APEtagData = fread($fd, $thisfile_ape['footer']['raw']['tagsize']); - } - $ThisFileInfo['avdataend'] = $thisfile_ape['tag_offset_start']; - - if (isset($ThisFileInfo['id3v1']['tag_offset_start']) && ($ThisFileInfo['id3v1']['tag_offset_start'] < $thisfile_ape['tag_offset_end'])) { - $ThisFileInfo['warning'][] = 'ID3v1 tag information ignored since it appears to be a false synch in APEtag data'; - unset($ThisFileInfo['id3v1']); - foreach ($ThisFileInfo['warning'] as $key => $value) { - if ($value == 'Some ID3v1 fields do not use NULL characters for padding') { - unset($ThisFileInfo['warning'][$key]); - sort($ThisFileInfo['warning']); - break; - } - } - } - - $offset = 0; - if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) { - if ($thisfile_ape['header'] = $this->parseAPEheaderFooter(substr($APEtagData, 0, $apetagheadersize))) { - $offset += $apetagheadersize; - } else { - $ThisFileInfo['error'][] = 'Error parsing APE header at offset '.$thisfile_ape['tag_offset_start']; - return false; - } - } - - // shortcut - $ThisFileInfo['replay_gain'] = array(); - $thisfile_replaygain = &$ThisFileInfo['replay_gain']; - - for ($i = 0; $i < $thisfile_ape['footer']['raw']['tag_items']; $i++) { - $value_size = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4)); - $offset += 4; - $item_flags = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4)); - $offset += 4; - if (strstr(substr($APEtagData, $offset), "\x00") === false) { - $ThisFileInfo['error'][] = 'Cannot find null-byte (0x00) seperator between ItemKey #'.$i.' and value. ItemKey starts '.$offset.' bytes into the APE tag, at file offset '.($thisfile_ape['tag_offset_start'] + $offset); - return false; - } - $ItemKeyLength = strpos($APEtagData, "\x00", $offset) - $offset; - $item_key = strtolower(substr($APEtagData, $offset, $ItemKeyLength)); - - // shortcut - $thisfile_ape['items'][$item_key] = array(); - $thisfile_ape_items_current = &$thisfile_ape['items'][$item_key]; - - $offset += ($ItemKeyLength + 1); // skip 0x00 terminator - $thisfile_ape_items_current['data'] = substr($APEtagData, $offset, $value_size); - $offset += $value_size; - - $thisfile_ape_items_current['flags'] = $this->parseAPEtagFlags($item_flags); - switch ($thisfile_ape_items_current['flags']['item_contents_raw']) { - case 0: // UTF-8 - case 3: // Locator (URL, filename, etc), UTF-8 encoded - $thisfile_ape_items_current['data'] = explode("\x00", trim($thisfile_ape_items_current['data'])); - break; - - default: // binary data - break; - } - - switch (strtolower($item_key)) { - case 'replaygain_track_gain': - $thisfile_replaygain['track']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero! - $thisfile_replaygain['track']['originator'] = 'unspecified'; - break; - - case 'replaygain_track_peak': - $thisfile_replaygain['track']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero! - $thisfile_replaygain['track']['originator'] = 'unspecified'; - if ($thisfile_replaygain['track']['peak'] <= 0) { - $ThisFileInfo['warning'][] = 'ReplayGain Track peak from APEtag appears invalid: '.$thisfile_replaygain['track']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")'; - } - break; - - case 'replaygain_album_gain': - $thisfile_replaygain['album']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero! - $thisfile_replaygain['album']['originator'] = 'unspecified'; - break; - - case 'replaygain_album_peak': - $thisfile_replaygain['album']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero! - $thisfile_replaygain['album']['originator'] = 'unspecified'; - if ($thisfile_replaygain['album']['peak'] <= 0) { - $ThisFileInfo['warning'][] = 'ReplayGain Album peak from APEtag appears invalid: '.$thisfile_replaygain['album']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")'; - } - break; - - case 'mp3gain_undo': - list($mp3gain_undo_left, $mp3gain_undo_right, $mp3gain_undo_wrap) = explode(',', $thisfile_ape_items_current['data'][0]); - $thisfile_replaygain['mp3gain']['undo_left'] = intval($mp3gain_undo_left); - $thisfile_replaygain['mp3gain']['undo_right'] = intval($mp3gain_undo_right); - $thisfile_replaygain['mp3gain']['undo_wrap'] = (($mp3gain_undo_wrap == 'Y') ? true : false); - break; - - case 'mp3gain_minmax': - list($mp3gain_globalgain_min, $mp3gain_globalgain_max) = explode(',', $thisfile_ape_items_current['data'][0]); - $thisfile_replaygain['mp3gain']['globalgain_track_min'] = intval($mp3gain_globalgain_min); - $thisfile_replaygain['mp3gain']['globalgain_track_max'] = intval($mp3gain_globalgain_max); - break; - - case 'mp3gain_album_minmax': - list($mp3gain_globalgain_album_min, $mp3gain_globalgain_album_max) = explode(',', $thisfile_ape_items_current['data'][0]); - $thisfile_replaygain['mp3gain']['globalgain_album_min'] = intval($mp3gain_globalgain_album_min); - $thisfile_replaygain['mp3gain']['globalgain_album_max'] = intval($mp3gain_globalgain_album_max); - break; - - case 'tracknumber': - foreach ($thisfile_ape_items_current['data'] as $comment) { - $thisfile_ape['comments']['track'][] = $comment; - } - break; - - default: - foreach ($thisfile_ape_items_current['data'] as $comment) { - $thisfile_ape['comments'][strtolower($item_key)][] = $comment; - } - break; - } - - } - if (empty($thisfile_replaygain)) { - unset($ThisFileInfo['replay_gain']); - } - - return true; - } - - function parseAPEheaderFooter($APEheaderFooterData) { - // http://www.uni-jena.de/~pfk/mpp/sv8/apeheader.html - - // shortcut - $headerfooterinfo['raw'] = array(); - $headerfooterinfo_raw = &$headerfooterinfo['raw']; - - $headerfooterinfo_raw['footer_tag'] = substr($APEheaderFooterData, 0, 8); - if ($headerfooterinfo_raw['footer_tag'] != 'APETAGEX') { - return false; - } - $headerfooterinfo_raw['version'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 8, 4)); - $headerfooterinfo_raw['tagsize'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 12, 4)); - $headerfooterinfo_raw['tag_items'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 16, 4)); - $headerfooterinfo_raw['global_flags'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 20, 4)); - $headerfooterinfo_raw['reserved'] = substr($APEheaderFooterData, 24, 8); - - $headerfooterinfo['tag_version'] = $headerfooterinfo_raw['version'] / 1000; - if ($headerfooterinfo['tag_version'] >= 2) { - $headerfooterinfo['flags'] = $this->parseAPEtagFlags($headerfooterinfo_raw['global_flags']); - } - return $headerfooterinfo; - } - - function parseAPEtagFlags($rawflagint) { - // "Note: APE Tags 1.0 do not use any of the APE Tag flags. - // All are set to zero on creation and ignored on reading." - // http://www.uni-jena.de/~pfk/mpp/sv8/apetagflags.html - $flags['header'] = (bool) ($rawflagint & 0x80000000); - $flags['footer'] = (bool) ($rawflagint & 0x40000000); - $flags['this_is_header'] = (bool) ($rawflagint & 0x20000000); - $flags['item_contents_raw'] = ($rawflagint & 0x00000006) >> 1; - $flags['read_only'] = (bool) ($rawflagint & 0x00000001); - - $flags['item_contents'] = $this->APEcontentTypeFlagLookup($flags['item_contents_raw']); - - return $flags; - } - - function APEcontentTypeFlagLookup($contenttypeid) { - static $APEcontentTypeFlagLookup = array( - 0 => 'utf-8', - 1 => 'binary', - 2 => 'external', - 3 => 'reserved' - ); - return (isset($APEcontentTypeFlagLookup[$contenttypeid]) ? $APEcontentTypeFlagLookup[$contenttypeid] : 'invalid'); - } - - function APEtagItemIsUTF8Lookup($itemkey) { - static $APEtagItemIsUTF8Lookup = array( - 'title', - 'subtitle', - 'artist', - 'album', - 'debut album', - 'publisher', - 'conductor', - 'track', - 'composer', - 'comment', - 'copyright', - 'publicationright', - 'file', - 'year', - 'record date', - 'record location', - 'genre', - 'media', - 'related', - 'isrc', - 'abstract', - 'language', - 'bibliography' - ); - return in_array(strtolower($itemkey), $APEtagItemIsUTF8Lookup); - } - -} - -?> \ No newline at end of file diff --git a/getid3/getid3/module.tag.id3v1.php b/getid3/getid3/module.tag.id3v1.php deleted file mode 100644 index dd9b47b..0000000 --- a/getid3/getid3/module.tag.id3v1.php +++ /dev/null @@ -1,356 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// module.tag.id3v1.php // -// module for analyzing ID3v1 tags // -// dependencies: NONE // -// /// -///////////////////////////////////////////////////////////////// - - -class getid3_id3v1 -{ - - function getid3_id3v1(&$fd, &$ThisFileInfo) { - - fseek($fd, -256, SEEK_END); - $preid3v1 = fread($fd, 128); - $id3v1tag = fread($fd, 128); - - if (substr($id3v1tag, 0, 3) == 'TAG') { - - $ThisFileInfo['avdataend'] = $ThisFileInfo['filesize'] - 128; - - $ParsedID3v1['title'] = $this->cutfield(substr($id3v1tag, 3, 30)); - $ParsedID3v1['artist'] = $this->cutfield(substr($id3v1tag, 33, 30)); - $ParsedID3v1['album'] = $this->cutfield(substr($id3v1tag, 63, 30)); - $ParsedID3v1['year'] = $this->cutfield(substr($id3v1tag, 93, 4)); - $ParsedID3v1['comment'] = substr($id3v1tag, 97, 30); // can't remove nulls yet, track detection depends on them - $ParsedID3v1['genreid'] = ord(substr($id3v1tag, 127, 1)); - - // If second-last byte of comment field is null and last byte of comment field is non-null - // then this is ID3v1.1 and the comment field is 28 bytes long and the 30th byte is the track number - if (($id3v1tag{125} === "\x00") && ($id3v1tag{126} !== "\x00")) { - $ParsedID3v1['track'] = ord(substr($ParsedID3v1['comment'], 29, 1)); - $ParsedID3v1['comment'] = substr($ParsedID3v1['comment'], 0, 28); - } - $ParsedID3v1['comment'] = $this->cutfield($ParsedID3v1['comment']); - - $ParsedID3v1['genre'] = $this->LookupGenreName($ParsedID3v1['genreid']); - if (!empty($ParsedID3v1['genre'])) { - unset($ParsedID3v1['genreid']); - } - if (empty($ParsedID3v1['genre']) || (@$ParsedID3v1['genre'] == 'Unknown')) { - unset($ParsedID3v1['genre']); - } - - foreach ($ParsedID3v1 as $key => $value) { - $ParsedID3v1['comments'][$key][0] = $value; - } - - // ID3v1 data is supposed to be padded with NULL characters, but some taggers pad with spaces - $GoodFormatID3v1tag = $this->GenerateID3v1Tag( - $ParsedID3v1['title'], - $ParsedID3v1['artist'], - $ParsedID3v1['album'], - $ParsedID3v1['year'], - $this->LookupGenreID(@$ParsedID3v1['genre']), - $ParsedID3v1['comment'], - @$ParsedID3v1['track']); - $ParsedID3v1['padding_valid'] = true; - if ($id3v1tag !== $GoodFormatID3v1tag) { - $ParsedID3v1['padding_valid'] = false; - $ThisFileInfo['warning'][] = 'Some ID3v1 fields do not use NULL characters for padding'; - } - - $ParsedID3v1['tag_offset_end'] = $ThisFileInfo['filesize']; - $ParsedID3v1['tag_offset_start'] = $ParsedID3v1['tag_offset_end'] - 128; - - $ThisFileInfo['id3v1'] = $ParsedID3v1; - } - - if (substr($preid3v1, 0, 3) == 'TAG') { - // The way iTunes handles tags is, well, brain-damaged. - // It completely ignores v1 if ID3v2 is present. - // This goes as far as adding a new v1 tag *even if there already is one* - - // A suspected double-ID3v1 tag has been detected, but it could be that - // the "TAG" identifier is a legitimate part of an APE or Lyrics3 tag - if (substr($preid3v1, 96, 8) == 'APETAGEX') { - // an APE tag footer was found before the last ID3v1, assume false "TAG" synch - } elseif (substr($preid3v1, 119, 6) == 'LYRICS') { - // a Lyrics3 tag footer was found before the last ID3v1, assume false "TAG" synch - } else { - // APE and Lyrics3 footers not found - assume double ID3v1 - $ThisFileInfo['warning'][] = 'Duplicate ID3v1 tag detected - this has been known to happen with iTunes'; - $ThisFileInfo['avdataend'] -= 128; - } - } - - return true; - } - - function cutfield($str) { - return trim(substr($str, 0, strcspn($str, "\x00"))); - } - - function ArrayOfGenres($allowSCMPXextended=false) { - static $GenreLookup = array( - 0 => 'Blues', - 1 => 'Classic Rock', - 2 => 'Country', - 3 => 'Dance', - 4 => 'Disco', - 5 => 'Funk', - 6 => 'Grunge', - 7 => 'Hip-Hop', - 8 => 'Jazz', - 9 => 'Metal', - 10 => 'New Age', - 11 => 'Oldies', - 12 => 'Other', - 13 => 'Pop', - 14 => 'R&B', - 15 => 'Rap', - 16 => 'Reggae', - 17 => 'Rock', - 18 => 'Techno', - 19 => 'Industrial', - 20 => 'Alternative', - 21 => 'Ska', - 22 => 'Death Metal', - 23 => 'Pranks', - 24 => 'Soundtrack', - 25 => 'Euro-Techno', - 26 => 'Ambient', - 27 => 'Trip-Hop', - 28 => 'Vocal', - 29 => 'Jazz+Funk', - 30 => 'Fusion', - 31 => 'Trance', - 32 => 'Classical', - 33 => 'Instrumental', - 34 => 'Acid', - 35 => 'House', - 36 => 'Game', - 37 => 'Sound Clip', - 38 => 'Gospel', - 39 => 'Noise', - 40 => 'Alt. Rock', - 41 => 'Bass', - 42 => 'Soul', - 43 => 'Punk', - 44 => 'Space', - 45 => 'Meditative', - 46 => 'Instrumental Pop', - 47 => 'Instrumental Rock', - 48 => 'Ethnic', - 49 => 'Gothic', - 50 => 'Darkwave', - 51 => 'Techno-Industrial', - 52 => 'Electronic', - 53 => 'Pop-Folk', - 54 => 'Eurodance', - 55 => 'Dream', - 56 => 'Southern Rock', - 57 => 'Comedy', - 58 => 'Cult', - 59 => 'Gangsta Rap', - 60 => 'Top 40', - 61 => 'Christian Rap', - 62 => 'Pop/Funk', - 63 => 'Jungle', - 64 => 'Native American', - 65 => 'Cabaret', - 66 => 'New Wave', - 67 => 'Psychedelic', - 68 => 'Rave', - 69 => 'Showtunes', - 70 => 'Trailer', - 71 => 'Lo-Fi', - 72 => 'Tribal', - 73 => 'Acid Punk', - 74 => 'Acid Jazz', - 75 => 'Polka', - 76 => 'Retro', - 77 => 'Musical', - 78 => 'Rock & Roll', - 79 => 'Hard Rock', - 80 => 'Folk', - 81 => 'Folk/Rock', - 82 => 'National Folk', - 83 => 'Swing', - 84 => 'Fast-Fusion', - 85 => 'Bebob', - 86 => 'Latin', - 87 => 'Revival', - 88 => 'Celtic', - 89 => 'Bluegrass', - 90 => 'Avantgarde', - 91 => 'Gothic Rock', - 92 => 'Progressive Rock', - 93 => 'Psychedelic Rock', - 94 => 'Symphonic Rock', - 95 => 'Slow Rock', - 96 => 'Big Band', - 97 => 'Chorus', - 98 => 'Easy Listening', - 99 => 'Acoustic', - 100 => 'Humour', - 101 => 'Speech', - 102 => 'Chanson', - 103 => 'Opera', - 104 => 'Chamber Music', - 105 => 'Sonata', - 106 => 'Symphony', - 107 => 'Booty Bass', - 108 => 'Primus', - 109 => 'Porn Groove', - 110 => 'Satire', - 111 => 'Slow Jam', - 112 => 'Club', - 113 => 'Tango', - 114 => 'Samba', - 115 => 'Folklore', - 116 => 'Ballad', - 117 => 'Power Ballad', - 118 => 'Rhythmic Soul', - 119 => 'Freestyle', - 120 => 'Duet', - 121 => 'Punk Rock', - 122 => 'Drum Solo', - 123 => 'A Cappella', - 124 => 'Euro-House', - 125 => 'Dance Hall', - 126 => 'Goa', - 127 => 'Drum & Bass', - 128 => 'Club-House', - 129 => 'Hardcore', - 130 => 'Terror', - 131 => 'Indie', - 132 => 'BritPop', - 133 => 'Negerpunk', - 134 => 'Polsk Punk', - 135 => 'Beat', - 136 => 'Christian Gangsta Rap', - 137 => 'Heavy Metal', - 138 => 'Black Metal', - 139 => 'Crossover', - 140 => 'Contemporary Christian', - 141 => 'Christian Rock', - 142 => 'Merengue', - 143 => 'Salsa', - 144 => 'Trash Metal', - 145 => 'Anime', - 146 => 'JPop', - 147 => 'Synthpop', - - 255 => 'Unknown', - - 'CR' => 'Cover', - 'RX' => 'Remix' - ); - - static $GenreLookupSCMPX = array(); - if ($allowSCMPXextended && empty($GenreLookupSCMPX)) { - $GenreLookupSCMPX = $GenreLookup; - // http://www.geocities.co.jp/SiliconValley-Oakland/3664/alittle.html#GenreExtended - // Extended ID3v1 genres invented by SCMPX - // Note that 255 "Japanese Anime" conflicts with standard "Unknown" - $GenreLookupSCMPX[240] = 'Sacred'; - $GenreLookupSCMPX[241] = 'Northern Europe'; - $GenreLookupSCMPX[242] = 'Irish & Scottish'; - $GenreLookupSCMPX[243] = 'Scotland'; - $GenreLookupSCMPX[244] = 'Ethnic Europe'; - $GenreLookupSCMPX[245] = 'Enka'; - $GenreLookupSCMPX[246] = 'Children\'s Song'; - $GenreLookupSCMPX[247] = 'Japanese Sky'; - $GenreLookupSCMPX[248] = 'Japanese Heavy Rock'; - $GenreLookupSCMPX[249] = 'Japanese Doom Rock'; - $GenreLookupSCMPX[250] = 'Japanese J-POP'; - $GenreLookupSCMPX[251] = 'Japanese Seiyu'; - $GenreLookupSCMPX[252] = 'Japanese Ambient Techno'; - $GenreLookupSCMPX[253] = 'Japanese Moemoe'; - $GenreLookupSCMPX[254] = 'Japanese Tokusatsu'; - //$GenreLookupSCMPX[255] = 'Japanese Anime'; - } - - return ($allowSCMPXextended ? $GenreLookupSCMPX : $GenreLookup); - } - - function LookupGenreName($genreid, $allowSCMPXextended=true) { - switch ($genreid) { - case 'RX': - case 'CR': - break; - default: - $genreid = intval($genreid); // to handle 3 or '3' or '03' - break; - } - $GenreLookup = getid3_id3v1::ArrayOfGenres($allowSCMPXextended); - return (isset($GenreLookup[$genreid]) ? $GenreLookup[$genreid] : false); - } - - function LookupGenreID($genre, $allowSCMPXextended=false) { - $GenreLookup = getid3_id3v1::ArrayOfGenres($allowSCMPXextended); - $LowerCaseNoSpaceSearchTerm = strtolower(str_replace(' ', '', $genre)); - foreach ($GenreLookup as $key => $value) { - foreach ($GenreLookup as $key => $value) { - if (strtolower(str_replace(' ', '', $value)) == $LowerCaseNoSpaceSearchTerm) { - return $key; - } - } - return false; - } - return (isset($GenreLookup[$genreid]) ? $GenreLookup[$genreid] : false); - } - - function StandardiseID3v1GenreName($OriginalGenre) { - if (($GenreID = getid3_id3v1::LookupGenreID($OriginalGenre)) !== false) { - return getid3_id3v1::LookupGenreName($GenreID); - } - return $OriginalGenre; - } - - function GenerateID3v1Tag($title, $artist, $album, $year, $genreid, $comment, $track='') { - $ID3v1Tag = 'TAG'; - $ID3v1Tag .= str_pad(trim(substr($title, 0, 30)), 30, "\x00", STR_PAD_RIGHT); - $ID3v1Tag .= str_pad(trim(substr($artist, 0, 30)), 30, "\x00", STR_PAD_RIGHT); - $ID3v1Tag .= str_pad(trim(substr($album, 0, 30)), 30, "\x00", STR_PAD_RIGHT); - $ID3v1Tag .= str_pad(trim(substr($year, 0, 4)), 4, "\x00", STR_PAD_LEFT); - if (!empty($track) && ($track > 0) && ($track <= 255)) { - $ID3v1Tag .= str_pad(trim(substr($comment, 0, 28)), 28, "\x00", STR_PAD_RIGHT); - $ID3v1Tag .= "\x00"; - if (gettype($track) == 'string') { - $track = (int) $track; - } - $ID3v1Tag .= chr($track); - } else { - $ID3v1Tag .= str_pad(trim(substr($comment, 0, 30)), 30, "\x00", STR_PAD_RIGHT); - } - if (($genreid < 0) || ($genreid > 147)) { - $genreid = 255; // 'unknown' genre - } - switch (gettype($genreid)) { - case 'string': - case 'integer': - $ID3v1Tag .= chr(intval($genreid)); - break; - default: - $ID3v1Tag .= chr(255); // 'unknown' genre - break; - } - - return $ID3v1Tag; - } - -} - - -?> \ No newline at end of file diff --git a/getid3/getid3/module.tag.id3v2.php b/getid3/getid3/module.tag.id3v2.php deleted file mode 100644 index 16f517e..0000000 --- a/getid3/getid3/module.tag.id3v2.php +++ /dev/null @@ -1,3130 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -/// // -// module.tag.id3v2.php // -// module for analyzing ID3v2 tags // -// dependencies: module.tag.id3v1.php // -// /// -///////////////////////////////////////////////////////////////// - -getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v1.php', __FILE__, true); - -class getid3_id3v2 -{ - - function getid3_id3v2(&$fd, &$ThisFileInfo, $StartingOffset=0) { - // Overall tag structure: - // +-----------------------------+ - // | Header (10 bytes) | - // +-----------------------------+ - // | Extended Header | - // | (variable length, OPTIONAL) | - // +-----------------------------+ - // | Frames (variable length) | - // +-----------------------------+ - // | Padding | - // | (variable length, OPTIONAL) | - // +-----------------------------+ - // | Footer (10 bytes, OPTIONAL) | - // +-----------------------------+ - - // Header - // ID3v2/file identifier "ID3" - // ID3v2 version $04 00 - // ID3v2 flags (%ab000000 in v2.2, %abc00000 in v2.3, %abcd0000 in v2.4.x) - // ID3v2 size 4 * %0xxxxxxx - - - // shortcuts - $ThisFileInfo['id3v2']['header'] = true; - $thisfile_id3v2 = &$ThisFileInfo['id3v2']; - $thisfile_id3v2['flags'] = array(); - $thisfile_id3v2_flags = &$thisfile_id3v2['flags']; - - - fseek($fd, $StartingOffset, SEEK_SET); - $header = fread($fd, 10); - if (substr($header, 0, 3) == 'ID3') { - - $thisfile_id3v2['majorversion'] = ord($header{3}); - $thisfile_id3v2['minorversion'] = ord($header{4}); - - // shortcut - $id3v2_majorversion = &$thisfile_id3v2['majorversion']; - - } else { - - unset($ThisFileInfo['id3v2']); - return false; - - } - - if ($id3v2_majorversion > 4) { // this script probably won't correctly parse ID3v2.5.x and above (if it ever exists) - - $ThisFileInfo['error'][] = 'this script only parses up to ID3v2.4.x - this tag is ID3v2.'.$id3v2_majorversion.'.'.$thisfile_id3v2['minorversion']; - return false; - - } - - $id3_flags = ord($header{5}); - switch ($id3v2_majorversion) { - case 2: - // %ab000000 in v2.2 - $thisfile_id3v2_flags['unsynch'] = (bool) ($id3_flags & 0x80); // a - Unsynchronisation - $thisfile_id3v2_flags['compression'] = (bool) ($id3_flags & 0x40); // b - Compression - break; - - case 3: - // %abc00000 in v2.3 - $thisfile_id3v2_flags['unsynch'] = (bool) ($id3_flags & 0x80); // a - Unsynchronisation - $thisfile_id3v2_flags['exthead'] = (bool) ($id3_flags & 0x40); // b - Extended header - $thisfile_id3v2_flags['experim'] = (bool) ($id3_flags & 0x20); // c - Experimental indicator - break; - - case 4: - // %abcd0000 in v2.4 - $thisfile_id3v2_flags['unsynch'] = (bool) ($id3_flags & 0x80); // a - Unsynchronisation - $thisfile_id3v2_flags['exthead'] = (bool) ($id3_flags & 0x40); // b - Extended header - $thisfile_id3v2_flags['experim'] = (bool) ($id3_flags & 0x20); // c - Experimental indicator - $thisfile_id3v2_flags['isfooter'] = (bool) ($id3_flags & 0x10); // d - Footer present - break; - } - - $thisfile_id3v2['headerlength'] = getid3_lib::BigEndian2Int(substr($header, 6, 4), 1) + 10; // length of ID3v2 tag in 10-byte header doesn't include 10-byte header length - - $thisfile_id3v2['tag_offset_start'] = $StartingOffset; - $thisfile_id3v2['tag_offset_end'] = $thisfile_id3v2['tag_offset_start'] + $thisfile_id3v2['headerlength']; - - // Extended Header - if (isset($thisfile_id3v2_flags['exthead']) && $thisfile_id3v2_flags['exthead']) { - // Extended header size 4 * %0xxxxxxx - // Number of flag bytes $01 - // Extended Flags $xx - // Where the 'Extended header size' is the size of the whole extended header, stored as a 32 bit synchsafe integer. - $thisfile_id3v2['exthead_length'] = getid3_lib::BigEndian2Int(fread($fd, 4), 1); - - $thisfile_id3v2['exthead_flag_bytes'] = ord(fread($fd, 1)); - if ($thisfile_id3v2['exthead_flag_bytes'] == 1) { - // The extended flags field, with its size described by 'number of flag bytes', is defined as: - // %0bcd0000 - // b - Tag is an update - // Flag data length $00 - // c - CRC data present - // Flag data length $05 - // Total frame CRC 5 * %0xxxxxxx - // d - Tag restrictions - // Flag data length $01 - $extheaderflags = fread($fd, $thisfile_id3v2['exthead_flag_bytes']); - $id3_exthead_flags = getid3_lib::BigEndian2Bin(substr($header, 5, 1)); - $thisfile_id3v2['exthead_flags']['update'] = substr($id3_exthead_flags, 1, 1); - $thisfile_id3v2['exthead_flags']['CRC'] = substr($id3_exthead_flags, 2, 1); - if ($thisfile_id3v2['exthead_flags']['CRC']) { - $extheaderrawCRC = fread($fd, 5); - $thisfile_id3v2['exthead_flags']['CRC'] = getid3_lib::BigEndian2Int($extheaderrawCRC, 1); - } - $thisfile_id3v2['exthead_flags']['restrictions'] = substr($id3_exthead_flags, 3, 1); - if ($thisfile_id3v2['exthead_flags']['restrictions']) { - // Restrictions %ppqrrstt - $extheaderrawrestrictions = fread($fd, 1); - $thisfile_id3v2['exthead_flags']['restrictions_tagsize'] = (bindec('11000000') & ord($extheaderrawrestrictions)) >> 6; // p - Tag size restrictions - $thisfile_id3v2['exthead_flags']['restrictions_textenc'] = (bindec('00100000') & ord($extheaderrawrestrictions)) >> 5; // q - Text encoding restrictions - $thisfile_id3v2['exthead_flags']['restrictions_textsize'] = (bindec('00011000') & ord($extheaderrawrestrictions)) >> 3; // r - Text fields size restrictions - $thisfile_id3v2['exthead_flags']['restrictions_imgenc'] = (bindec('00000100') & ord($extheaderrawrestrictions)) >> 2; // s - Image encoding restrictions - $thisfile_id3v2['exthead_flags']['restrictions_imgsize'] = (bindec('00000011') & ord($extheaderrawrestrictions)) >> 0; // t - Image size restrictions - } - } else { - $ThisFileInfo['warning'][] = '$thisfile_id3v2[exthead_flag_bytes] = "'.$thisfile_id3v2['exthead_flag_bytes'].'" (expecting "1")'; - fseek($fd, $thisfile_id3v2['exthead_length'] - 1, SEEK_CUR); - //return false; - } - } // end extended header - - - // create 'encoding' key - used by getid3::HandleAllTags() - // in ID3v2 every field can have it's own encoding type - // so force everything to UTF-8 so it can be handled consistantly - $thisfile_id3v2['encoding'] = 'UTF-8'; - - - // Frames - - // All ID3v2 frames consists of one frame header followed by one or more - // fields containing the actual information. The header is always 10 - // bytes and laid out as follows: - // - // Frame ID $xx xx xx xx (four characters) - // Size 4 * %0xxxxxxx - // Flags $xx xx - - $sizeofframes = $thisfile_id3v2['headerlength'] - 10; // not including 10-byte initial header - if (@$thisfile_id3v2['exthead_length']) { - $sizeofframes -= ($thisfile_id3v2['exthead_length'] + 4); - } - if (@$thisfile_id3v2_flags['isfooter']) { - $sizeofframes -= 10; // footer takes last 10 bytes of ID3v2 header, after frame data, before audio - } - if ($sizeofframes > 0) { - - $framedata = fread($fd, $sizeofframes); // read all frames from file into $framedata variable - - // if entire frame data is unsynched, de-unsynch it now (ID3v2.3.x) - if (@$thisfile_id3v2_flags['unsynch'] && ($id3v2_majorversion <= 3)) { - $framedata = $this->DeUnsynchronise($framedata); - } - // [in ID3v2.4.0] Unsynchronisation [S:6.1] is done on frame level, instead - // of on tag level, making it easier to skip frames, increasing the streamability - // of the tag. The unsynchronisation flag in the header [S:3.1] indicates that - // there exists an unsynchronised frame, while the new unsynchronisation flag in - // the frame header [S:4.1.2] indicates unsynchronisation. - - $framedataoffset = 10 + (@$thisfile_id3v2['exthead_length'] ? $thisfile_id3v2['exthead_length'] + 4 : 0); // how many bytes into the stream - start from after the 10-byte header (and extended header length+4, if present) - while (isset($framedata) && (strlen($framedata) > 0)) { // cycle through until no more frame data is left to parse - if (strlen($framedata) <= $this->ID3v2HeaderLength($id3v2_majorversion)) { - // insufficient room left in ID3v2 header for actual data - must be padding - $thisfile_id3v2['padding']['start'] = $framedataoffset; - $thisfile_id3v2['padding']['length'] = strlen($framedata); - $thisfile_id3v2['padding']['valid'] = true; - for ($i = 0; $i < $thisfile_id3v2['padding']['length']; $i++) { - if ($framedata{$i} != "\x00") { - $thisfile_id3v2['padding']['valid'] = false; - $thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i; - $ThisFileInfo['warning'][] = 'Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)'; - break; - } - } - break; // skip rest of ID3v2 header - } - if ($id3v2_majorversion == 2) { - // Frame ID $xx xx xx (three characters) - // Size $xx xx xx (24-bit integer) - // Flags $xx xx - - $frame_header = substr($framedata, 0, 6); // take next 6 bytes for header - $framedata = substr($framedata, 6); // and leave the rest in $framedata - $frame_name = substr($frame_header, 0, 3); - $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 3, 3), 0); - $frame_flags = 0; // not used for anything in ID3v2.2, just set to avoid E_NOTICEs - - } elseif ($id3v2_majorversion > 2) { - - // Frame ID $xx xx xx xx (four characters) - // Size $xx xx xx xx (32-bit integer in v2.3, 28-bit synchsafe in v2.4+) - // Flags $xx xx - - $frame_header = substr($framedata, 0, 10); // take next 10 bytes for header - $framedata = substr($framedata, 10); // and leave the rest in $framedata - - $frame_name = substr($frame_header, 0, 4); - if ($id3v2_majorversion == 3) { - $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0); // 32-bit integer - } else { // ID3v2.4+ - $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 1); // 32-bit synchsafe integer (28-bit value) - } - - if ($frame_size < (strlen($framedata) + 4)) { - $nextFrameID = substr($framedata, $frame_size, 4); - if ($this->IsValidID3v2FrameName($nextFrameID, $id3v2_majorversion)) { - // next frame is OK - } elseif (($frame_name == "\x00".'MP3') || ($frame_name == "\x00\x00".'MP') || ($frame_name == ' MP3') || ($frame_name == 'MP3e')) { - // MP3ext known broken frames - "ok" for the purposes of this test - } elseif (($id3v2_majorversion == 4) && ($this->IsValidID3v2FrameName(substr($framedata, getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0), 4), 3))) { - $ThisFileInfo['warning'][] = 'ID3v2 tag written as ID3v2.4, but with non-synchsafe integers (ID3v2.3 style). Older versions of (Helium2; iTunes) are known culprits of this. Tag has been parsed as ID3v2.3'; - $id3v2_majorversion = 3; - $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0); // 32-bit integer - } - } - - - $frame_flags = getid3_lib::BigEndian2Int(substr($frame_header, 8, 2)); - } - - if ((($id3v2_majorversion == 2) && ($frame_name == "\x00\x00\x00")) || ($frame_name == "\x00\x00\x00\x00")) { - // padding encountered - - $thisfile_id3v2['padding']['start'] = $framedataoffset; - $thisfile_id3v2['padding']['length'] = strlen($framedata); - $thisfile_id3v2['padding']['valid'] = true; - for ($i = 0; $i < $thisfile_id3v2['padding']['length']; $i++) { - if ($framedata{$i} != "\x00") { - $thisfile_id3v2['padding']['valid'] = false; - $thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i; - $ThisFileInfo['warning'][] = 'Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)'; - break; - } - } - break; // skip rest of ID3v2 header - } - - if ($frame_name == 'COM ') { - $ThisFileInfo['warning'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))). [Note: this particular error has been known to happen with tags edited by iTunes (versions "X v2.0.3", "v3.0.1" are known-guilty, probably others too)]'; - $frame_name = 'COMM'; - } - if (($frame_size <= strlen($framedata)) && ($this->IsValidID3v2FrameName($frame_name, $id3v2_majorversion))) { - - unset($parsedFrame); - $parsedFrame['frame_name'] = $frame_name; - $parsedFrame['frame_flags_raw'] = $frame_flags; - $parsedFrame['data'] = substr($framedata, 0, $frame_size); - $parsedFrame['datalength'] = getid3_lib::CastAsInt($frame_size); - $parsedFrame['dataoffset'] = $framedataoffset; - - $this->ParseID3v2Frame($parsedFrame, $ThisFileInfo); - $thisfile_id3v2[$frame_name][] = $parsedFrame; - - $framedata = substr($framedata, $frame_size); - - } else { // invalid frame length or FrameID - - if ($frame_size <= strlen($framedata)) { - - if ($this->IsValidID3v2FrameName(substr($framedata, $frame_size, 4), $id3v2_majorversion)) { - - // next frame is valid, just skip the current frame - $framedata = substr($framedata, $frame_size); - $ThisFileInfo['warning'][] = 'Next ID3v2 frame is valid, skipping current frame.'; - - } else { - - // next frame is invalid too, abort processing - //unset($framedata); - $framedata = null; - $ThisFileInfo['error'][] = 'Next ID3v2 frame is also invalid, aborting processing.'; - - } - - } elseif ($frame_size == strlen($framedata)) { - - // this is the last frame, just skip - $ThisFileInfo['warning'][] = 'This was the last ID3v2 frame.'; - - } else { - - // next frame is invalid too, abort processing - //unset($framedata); - $framedata = null; - $ThisFileInfo['warning'][] = 'Invalid ID3v2 frame size, aborting.'; - - } - if (!$this->IsValidID3v2FrameName($frame_name, $id3v2_majorversion)) { - - switch ($frame_name) { - case "\x00\x00".'MP': - case "\x00".'MP3': - case ' MP3': - case 'MP3e': - case "\x00".'MP': - case ' MP': - case 'MP3': - $ThisFileInfo['warning'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: !IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))). [Note: this particular error has been known to happen with tags edited by "MP3ext (www.mutschler.de/mp3ext/)"]'; - break; - - default: - $ThisFileInfo['warning'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: !IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))).'; - break; - } - - } elseif ($frame_size > strlen($framedata)){ - - $ThisFileInfo['error'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: $frame_size ('.$frame_size.') > strlen($framedata) ('.strlen($framedata).')).'; - - } else { - - $ThisFileInfo['error'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag).'; - - } - - } - $framedataoffset += ($frame_size + $this->ID3v2HeaderLength($id3v2_majorversion)); - - } - - } - - - // Footer - - // The footer is a copy of the header, but with a different identifier. - // ID3v2 identifier "3DI" - // ID3v2 version $04 00 - // ID3v2 flags %abcd0000 - // ID3v2 size 4 * %0xxxxxxx - - if (isset($thisfile_id3v2_flags['isfooter']) && $thisfile_id3v2_flags['isfooter']) { - $footer = fread($fd, 10); - if (substr($footer, 0, 3) == '3DI') { - $thisfile_id3v2['footer'] = true; - $thisfile_id3v2['majorversion_footer'] = ord($footer{3}); - $thisfile_id3v2['minorversion_footer'] = ord($footer{4}); - } - if ($thisfile_id3v2['majorversion_footer'] <= 4) { - $id3_flags = ord(substr($footer{5})); - $thisfile_id3v2_flags['unsynch_footer'] = (bool) ($id3_flags & 0x80); - $thisfile_id3v2_flags['extfoot_footer'] = (bool) ($id3_flags & 0x40); - $thisfile_id3v2_flags['experim_footer'] = (bool) ($id3_flags & 0x20); - $thisfile_id3v2_flags['isfooter_footer'] = (bool) ($id3_flags & 0x10); - - $thisfile_id3v2['footerlength'] = getid3_lib::BigEndian2Int(substr($footer, 6, 4), 1); - } - } // end footer - - if (isset($thisfile_id3v2['comments']['genre'])) { - foreach ($thisfile_id3v2['comments']['genre'] as $key => $value) { - unset($thisfile_id3v2['comments']['genre'][$key]); - $thisfile_id3v2['comments'] = getid3_lib::array_merge_noclobber($thisfile_id3v2['comments'], $this->ParseID3v2GenreString($value)); - } - } - - if (isset($thisfile_id3v2['comments']['track'])) { - foreach ($thisfile_id3v2['comments']['track'] as $key => $value) { - if (strstr($value, '/')) { - list($thisfile_id3v2['comments']['tracknum'][$key], $thisfile_id3v2['comments']['totaltracks'][$key]) = explode('/', $thisfile_id3v2['comments']['track'][$key]); - } - } - } - - if (!isset($thisfile_id3v2['comments']['year']) && ereg('^([0-9]{4})', trim(@$thisfile_id3v2['comments']['recording_time'][0]), $matches)) { - $thisfile_id3v2['comments']['year'] = array($matches[1]); - } - - - // Set avdataoffset - $ThisFileInfo['avdataoffset'] = $thisfile_id3v2['headerlength']; - if (isset($thisfile_id3v2['footer'])) { - $ThisFileInfo['avdataoffset'] += 10; - } - - return true; - } - - - function ParseID3v2GenreString($genrestring) { - // Parse genres into arrays of genreName and genreID - // ID3v2.2.x, ID3v2.3.x: '(21)' or '(4)Eurodisco' or '(51)(39)' or '(55)((I think...)' - // ID3v2.4.x: '21' $00 'Eurodisco' $00 - - $genrestring = trim($genrestring); - $returnarray = array(); - if (strpos($genrestring, "\x00") !== false) { - $unprocessed = trim($genrestring); // trailing nulls will cause an infinite loop. - $genrestring = ''; - while (strpos($unprocessed, "\x00") !== false) { - // convert null-seperated v2.4-format into v2.3 ()-seperated format - $endpos = strpos($unprocessed, "\x00"); - $genrestring .= '('.substr($unprocessed, 0, $endpos).')'; - $unprocessed = substr($unprocessed, $endpos + 1); - } - unset($unprocessed); - } - if (getid3_id3v1::LookupGenreID($genrestring)) { - - $returnarray['genre'][] = $genrestring; - - } else { - - while (strpos($genrestring, '(') !== false) { - - $startpos = strpos($genrestring, '('); - $endpos = strpos($genrestring, ')'); - if (substr($genrestring, $startpos + 1, 1) == '(') { - $genrestring = substr($genrestring, 0, $startpos).substr($genrestring, $startpos + 1); - $endpos--; - } - $element = substr($genrestring, $startpos + 1, $endpos - ($startpos + 1)); - $genrestring = substr($genrestring, 0, $startpos).substr($genrestring, $endpos + 1); - if (getid3_id3v1::LookupGenreName($element)) { // $element is a valid genre id/abbreviation - - if (empty($returnarray['genre']) || !in_array(getid3_id3v1::LookupGenreName($element), $returnarray['genre'])) { // avoid duplicate entires - $returnarray['genre'][] = getid3_id3v1::LookupGenreName($element); - } - - } else { - - if (empty($returnarray['genre']) || !in_array($element, $returnarray['genre'])) { // avoid duplicate entires - $returnarray['genre'][] = $element; - } - - } - } - } - if ($genrestring) { - if (empty($returnarray['genre']) || !in_array($genrestring, $returnarray['genre'])) { // avoid duplicate entires - $returnarray['genre'][] = $genrestring; - } - } - - return $returnarray; - } - - - function ParseID3v2Frame(&$parsedFrame, &$ThisFileInfo) { - - // shortcuts - $id3v2_majorversion = $ThisFileInfo['id3v2']['majorversion']; - - $parsedFrame['framenamelong'] = $this->FrameNameLongLookup($parsedFrame['frame_name']); - if (empty($parsedFrame['framenamelong'])) { - unset($parsedFrame['framenamelong']); - } - $parsedFrame['framenameshort'] = $this->FrameNameShortLookup($parsedFrame['frame_name']); - if (empty($parsedFrame['framenameshort'])) { - unset($parsedFrame['framenameshort']); - } - - if ($id3v2_majorversion >= 3) { // frame flags are not part of the ID3v2.2 standard - if ($id3v2_majorversion == 3) { - // Frame Header Flags - // %abc00000 %ijk00000 - $parsedFrame['flags']['TagAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x8000); // a - Tag alter preservation - $parsedFrame['flags']['FileAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4000); // b - File alter preservation - $parsedFrame['flags']['ReadOnly'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2000); // c - Read only - $parsedFrame['flags']['compression'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0080); // i - Compression - $parsedFrame['flags']['Encryption'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0040); // j - Encryption - $parsedFrame['flags']['GroupingIdentity'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0020); // k - Grouping identity - - } elseif ($id3v2_majorversion == 4) { - // Frame Header Flags - // %0abc0000 %0h00kmnp - $parsedFrame['flags']['TagAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4000); // a - Tag alter preservation - $parsedFrame['flags']['FileAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2000); // b - File alter preservation - $parsedFrame['flags']['ReadOnly'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x1000); // c - Read only - $parsedFrame['flags']['GroupingIdentity'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0040); // h - Grouping identity - $parsedFrame['flags']['compression'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0008); // k - Compression - $parsedFrame['flags']['Encryption'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0004); // m - Encryption - $parsedFrame['flags']['Unsynchronisation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0002); // n - Unsynchronisation - $parsedFrame['flags']['DataLengthIndicator'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0001); // p - Data length indicator - - // Frame-level de-unsynchronisation - ID3v2.4 - if ($parsedFrame['flags']['Unsynchronisation']) { - $parsedFrame['data'] = $this->DeUnsynchronise($parsedFrame['data']); - } - } - - // Frame-level de-compression - if ($parsedFrame['flags']['compression']) { - $parsedFrame['decompressed_size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 4)); - if (!function_exists('gzuncompress')) { - $ThisFileInfo['warning'][] = 'gzuncompress() support required to decompress ID3v2 frame "'.$parsedFrame['frame_name'].'"'; - } elseif ($decompresseddata = @gzuncompress(substr($parsedFrame['data'], 4))) { - $parsedFrame['data'] = $decompresseddata; - } else { - $ThisFileInfo['warning'][] = 'gzuncompress() failed on compressed contents of ID3v2 frame "'.$parsedFrame['frame_name'].'"'; - } - } - } - - if (isset($parsedFrame['datalength']) && ($parsedFrame['datalength'] == 0)) { - - $warning = 'Frame "'.$parsedFrame['frame_name'].'" at offset '.$parsedFrame['dataoffset'].' has no data portion'; - switch ($parsedFrame['frame_name']) { - case 'WCOM': - $warning .= ' (this is known to happen with files tagged by RioPort)'; - break; - - default: - break; - } - $ThisFileInfo['warning'][] = $warning; - - } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'UFID')) || // 4.1 UFID Unique file identifier - (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'UFI'))) { // 4.1 UFI Unique file identifier - // There may be more than one 'UFID' frame in a tag, - // but only one with the same 'Owner identifier'. - //
- // Owner identifier $00 - // Identifier - - $frame_terminatorpos = strpos($parsedFrame['data'], "\x00"); - $frame_idstring = substr($parsedFrame['data'], 0, $frame_terminatorpos); - $parsedFrame['ownerid'] = $frame_idstring; - $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen("\x00")); - unset($parsedFrame['data']); - - - } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'TXXX')) || // 4.2.2 TXXX User defined text information frame - (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'TXX'))) { // 4.2.2 TXX User defined text information frame - // There may be more than one 'TXXX' frame in each tag, - // but only one with the same description. - //
- // Text encoding $xx - // Description $00 (00) - // Value - - $frame_offset = 0; - $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); - - if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { - $ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; - } - $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); - if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { - $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 - } - $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_description) === 0) { - $frame_description = ''; - } - $parsedFrame['encodingid'] = $frame_textencoding; - $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); - - $parsedFrame['description'] = $frame_description; - $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); - if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { - $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data'])); - } - unset($parsedFrame['data']); - - - } elseif ($parsedFrame['frame_name']{0} == 'T') { // 4.2. T??[?] Text information frame - // There may only be one text information frame of its kind in an tag. - //
- // Text encoding $xx - // Information - - $frame_offset = 0; - $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); - if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { - $ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; - } - - $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); - - $parsedFrame['encodingid'] = $frame_textencoding; - $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); - - if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { - $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data']); - } - - } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'WXXX')) || // 4.3.2 WXXX User defined URL link frame - (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'WXX'))) { // 4.3.2 WXX User defined URL link frame - // There may be more than one 'WXXX' frame in each tag, - // but only one with the same description - //
- // Text encoding $xx - // Description $00 (00) - // URL - - $frame_offset = 0; - $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); - if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { - $ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; - } - $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); - if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { - $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 - } - $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - - if (ord($frame_description) === 0) { - $frame_description = ''; - } - $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); - - $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding)); - if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { - $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 - } - if ($frame_terminatorpos) { - // there are null bytes after the data - this is not according to spec - // only use data up to first null byte - $frame_urldata = (string) substr($parsedFrame['data'], 0, $frame_terminatorpos); - } else { - // no null bytes following data, just use all data - $frame_urldata = (string) $parsedFrame['data']; - } - - $parsedFrame['encodingid'] = $frame_textencoding; - $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); - - $parsedFrame['url'] = $frame_urldata; - $parsedFrame['description'] = $frame_description; - if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) { - $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['url']); - } - unset($parsedFrame['data']); - - - } elseif ($parsedFrame['frame_name']{0} == 'W') { // 4.3. W??? URL link frames - // There may only be one URL link frame of its kind in a tag, - // except when stated otherwise in the frame description - //
- // URL - - $parsedFrame['url'] = trim($parsedFrame['data']); - if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) { - $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['url']; - } - unset($parsedFrame['data']); - - - } elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'IPLS')) || // 4.4 IPLS Involved people list (ID3v2.3 only) - (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'IPL'))) { // 4.4 IPL Involved people list (ID3v2.2 only) - // There may only be one 'IPL' frame in each tag - //
- // Text encoding $xx - // People list strings - - $frame_offset = 0; - $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); - if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { - $ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; - } - $parsedFrame['encodingid'] = $frame_textencoding; - $parsedFrame['encoding'] = $this->TextEncodingNameLookup($parsedFrame['encodingid']); - - $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); - if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { - $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data']); - } - - - } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'MCDI')) || // 4.4 MCDI Music CD identifier - (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'MCI'))) { // 4.5 MCI Music CD identifier - // There may only be one 'MCDI' frame in each tag - //
- // CD TOC - - if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { - $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['data']; - } - - - } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'ETCO')) || // 4.5 ETCO Event timing codes - (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'ETC'))) { // 4.6 ETC Event timing codes - // There may only be one 'ETCO' frame in each tag - //
- // Time stamp format $xx - // Where time stamp format is: - // $01 (32-bit value) MPEG frames from beginning of file - // $02 (32-bit value) milliseconds from beginning of file - // Followed by a list of key events in the following format: - // Type of event $xx - // Time stamp $xx (xx ...) - // The 'Time stamp' is set to zero if directly at the beginning of the sound - // or after the previous event. All events MUST be sorted in chronological order. - - $frame_offset = 0; - $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); - - while ($frame_offset < strlen($parsedFrame['data'])) { - $parsedFrame['typeid'] = substr($parsedFrame['data'], $frame_offset++, 1); - $parsedFrame['type'] = $this->ETCOEventLookup($parsedFrame['typeid']); - $parsedFrame['timestamp'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); - $frame_offset += 4; - } - unset($parsedFrame['data']); - - - } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'MLLT')) || // 4.6 MLLT MPEG location lookup table - (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'MLL'))) { // 4.7 MLL MPEG location lookup table - // There may only be one 'MLLT' frame in each tag - //
- // MPEG frames between reference $xx xx - // Bytes between reference $xx xx xx - // Milliseconds between reference $xx xx xx - // Bits for bytes deviation $xx - // Bits for milliseconds dev. $xx - // Then for every reference the following data is included; - // Deviation in bytes %xxx.... - // Deviation in milliseconds %xxx.... - - $frame_offset = 0; - $parsedFrame['framesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 2)); - $parsedFrame['bytesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 2, 3)); - $parsedFrame['msbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 5, 3)); - $parsedFrame['bitsforbytesdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 8, 1)); - $parsedFrame['bitsformsdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 9, 1)); - $parsedFrame['data'] = substr($parsedFrame['data'], 10); - while ($frame_offset < strlen($parsedFrame['data'])) { - $deviationbitstream .= getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1)); - } - $reference_counter = 0; - while (strlen($deviationbitstream) > 0) { - $parsedFrame[$reference_counter]['bytedeviation'] = bindec(substr($deviationbitstream, 0, $parsedFrame['bitsforbytesdeviation'])); - $parsedFrame[$reference_counter]['msdeviation'] = bindec(substr($deviationbitstream, $parsedFrame['bitsforbytesdeviation'], $parsedFrame['bitsformsdeviation'])); - $deviationbitstream = substr($deviationbitstream, $parsedFrame['bitsforbytesdeviation'] + $parsedFrame['bitsformsdeviation']); - $reference_counter++; - } - unset($parsedFrame['data']); - - - } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'SYTC')) || // 4.7 SYTC Synchronised tempo codes - (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'STC'))) { // 4.8 STC Synchronised tempo codes - // There may only be one 'SYTC' frame in each tag - //
- // Time stamp format $xx - // Tempo data - // Where time stamp format is: - // $01 (32-bit value) MPEG frames from beginning of file - // $02 (32-bit value) milliseconds from beginning of file - - $frame_offset = 0; - $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); - $timestamp_counter = 0; - while ($frame_offset < strlen($parsedFrame['data'])) { - $parsedFrame[$timestamp_counter]['tempo'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); - if ($parsedFrame[$timestamp_counter]['tempo'] == 255) { - $parsedFrame[$timestamp_counter]['tempo'] += ord(substr($parsedFrame['data'], $frame_offset++, 1)); - } - $parsedFrame[$timestamp_counter]['timestamp'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); - $frame_offset += 4; - $timestamp_counter++; - } - unset($parsedFrame['data']); - - - } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'USLT')) || // 4.8 USLT Unsynchronised lyric/text transcription - (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'ULT'))) { // 4.9 ULT Unsynchronised lyric/text transcription - // There may be more than one 'Unsynchronised lyrics/text transcription' frame - // in each tag, but only one with the same language and content descriptor. - //
- // Text encoding $xx - // Language $xx xx xx - // Content descriptor $00 (00) - // Lyrics/text - - $frame_offset = 0; - $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); - if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { - $ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; - } - $frame_language = substr($parsedFrame['data'], $frame_offset, 3); - $frame_offset += 3; - $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); - if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { - $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 - } - $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_description) === 0) { - $frame_description = ''; - } - $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); - - $parsedFrame['encodingid'] = $frame_textencoding; - $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); - - $parsedFrame['data'] = $parsedFrame['data']; - $parsedFrame['language'] = $frame_language; - $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); - $parsedFrame['description'] = $frame_description; - if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { - $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data']); - } - unset($parsedFrame['data']); - - - } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'SYLT')) || // 4.9 SYLT Synchronised lyric/text - (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'SLT'))) { // 4.10 SLT Synchronised lyric/text - // There may be more than one 'SYLT' frame in each tag, - // but only one with the same language and content descriptor. - //
- // Text encoding $xx - // Language $xx xx xx - // Time stamp format $xx - // $01 (32-bit value) MPEG frames from beginning of file - // $02 (32-bit value) milliseconds from beginning of file - // Content type $xx - // Content descriptor $00 (00) - // Terminated text to be synced (typically a syllable) - // Sync identifier (terminator to above string) $00 (00) - // Time stamp $xx (xx ...) - - $frame_offset = 0; - $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); - if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { - $ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; - } - $frame_language = substr($parsedFrame['data'], $frame_offset, 3); - $frame_offset += 3; - $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); - $parsedFrame['contenttypeid'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); - $parsedFrame['contenttype'] = $this->SYTLContentTypeLookup($parsedFrame['contenttypeid']); - $parsedFrame['encodingid'] = $frame_textencoding; - $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); - - $parsedFrame['language'] = $frame_language; - $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); - - $timestampindex = 0; - $frame_remainingdata = substr($parsedFrame['data'], $frame_offset); - while (strlen($frame_remainingdata)) { - $frame_offset = 0; - $frame_terminatorpos = strpos($frame_remainingdata, $this->TextEncodingTerminatorLookup($frame_textencoding)); - if ($frame_terminatorpos === false) { - $frame_remainingdata = ''; - } else { - if (ord(substr($frame_remainingdata, $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { - $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 - } - $parsedFrame['lyrics'][$timestampindex]['data'] = substr($frame_remainingdata, $frame_offset, $frame_terminatorpos - $frame_offset); - - $frame_remainingdata = substr($frame_remainingdata, $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); - if (($timestampindex == 0) && (ord($frame_remainingdata{0}) != 0)) { - // timestamp probably omitted for first data item - } else { - $parsedFrame['lyrics'][$timestampindex]['timestamp'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 4)); - $frame_remainingdata = substr($frame_remainingdata, 4); - } - $timestampindex++; - } - } - unset($parsedFrame['data']); - - - } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'COMM')) || // 4.10 COMM Comments - (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'COM'))) { // 4.11 COM Comments - // There may be more than one comment frame in each tag, - // but only one with the same language and content descriptor. - //
- // Text encoding $xx - // Language $xx xx xx - // Short content descrip. $00 (00) - // The actual text - - if (strlen($parsedFrame['data']) < 5) { - - $ThisFileInfo['warning'][] = 'Invalid data (too short) for "'.$parsedFrame['frame_name'].'" frame at offset '.$parsedFrame['dataoffset']; - - } else { - - $frame_offset = 0; - $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); - if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { - $ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; - } - $frame_language = substr($parsedFrame['data'], $frame_offset, 3); - $frame_offset += 3; - $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); - if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { - $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 - } - $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_description) === 0) { - $frame_description = ''; - } - $frame_text = (string) substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); - - $parsedFrame['encodingid'] = $frame_textencoding; - $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); - - $parsedFrame['language'] = $frame_language; - $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); - $parsedFrame['description'] = $frame_description; - $parsedFrame['data'] = $frame_text; - if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { - $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data']); - } - - } - - } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'RVA2')) { // 4.11 RVA2 Relative volume adjustment (2) (ID3v2.4+ only) - // There may be more than one 'RVA2' frame in each tag, - // but only one with the same identification string - //
- // Identification $00 - // The 'identification' string is used to identify the situation and/or - // device where this adjustment should apply. The following is then - // repeated for every channel: - // Type of channel $xx - // Volume adjustment $xx xx - // Bits representing peak $xx - // Peak volume $xx (xx ...) - - $frame_terminatorpos = strpos($parsedFrame['data'], "\x00"); - $frame_idstring = substr($parsedFrame['data'], 0, $frame_terminatorpos); - if (ord($frame_idstring) === 0) { - $frame_idstring = ''; - } - $frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen("\x00")); - $parsedFrame['description'] = $frame_idstring; - while (strlen($frame_remainingdata)) { - $frame_offset = 0; - $frame_channeltypeid = ord(substr($frame_remainingdata, $frame_offset++, 1)); - $parsedFrame[$frame_channeltypeid]['channeltypeid'] = $frame_channeltypeid; - $parsedFrame[$frame_channeltypeid]['channeltype'] = $this->RVA2ChannelTypeLookup($frame_channeltypeid); - $parsedFrame[$frame_channeltypeid]['volumeadjust'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, $frame_offset, 2), false, true); // 16-bit signed - $frame_offset += 2; - $parsedFrame[$frame_channeltypeid]['bitspeakvolume'] = ord(substr($frame_remainingdata, $frame_offset++, 1)); - $frame_bytespeakvolume = ceil($parsedFrame[$frame_channeltypeid]['bitspeakvolume'] / 8); - $parsedFrame[$frame_channeltypeid]['peakvolume'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, $frame_offset, $frame_bytespeakvolume)); - $frame_remainingdata = substr($frame_remainingdata, $frame_offset + $frame_bytespeakvolume); - } - unset($parsedFrame['data']); - - - } elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'RVAD')) || // 4.12 RVAD Relative volume adjustment (ID3v2.3 only) - (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'RVA'))) { // 4.12 RVA Relative volume adjustment (ID3v2.2 only) - // There may only be one 'RVA' frame in each tag - //
- // ID3v2.2 => Increment/decrement %000000ba - // ID3v2.3 => Increment/decrement %00fedcba - // Bits used for volume descr. $xx - // Relative volume change, right $xx xx (xx ...) // a - // Relative volume change, left $xx xx (xx ...) // b - // Peak volume right $xx xx (xx ...) - // Peak volume left $xx xx (xx ...) - // ID3v2.3 only, optional (not present in ID3v2.2): - // Relative volume change, right back $xx xx (xx ...) // c - // Relative volume change, left back $xx xx (xx ...) // d - // Peak volume right back $xx xx (xx ...) - // Peak volume left back $xx xx (xx ...) - // ID3v2.3 only, optional (not present in ID3v2.2): - // Relative volume change, center $xx xx (xx ...) // e - // Peak volume center $xx xx (xx ...) - // ID3v2.3 only, optional (not present in ID3v2.2): - // Relative volume change, bass $xx xx (xx ...) // f - // Peak volume bass $xx xx (xx ...) - - $frame_offset = 0; - $frame_incrdecrflags = getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1)); - $parsedFrame['incdec']['right'] = (bool) substr($frame_incrdecrflags, 6, 1); - $parsedFrame['incdec']['left'] = (bool) substr($frame_incrdecrflags, 7, 1); - $parsedFrame['bitsvolume'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); - $frame_bytesvolume = ceil($parsedFrame['bitsvolume'] / 8); - $parsedFrame['volumechange']['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); - if ($parsedFrame['incdec']['right'] === false) { - $parsedFrame['volumechange']['right'] *= -1; - } - $frame_offset += $frame_bytesvolume; - $parsedFrame['volumechange']['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); - if ($parsedFrame['incdec']['left'] === false) { - $parsedFrame['volumechange']['left'] *= -1; - } - $frame_offset += $frame_bytesvolume; - $parsedFrame['peakvolume']['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); - $frame_offset += $frame_bytesvolume; - $parsedFrame['peakvolume']['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); - $frame_offset += $frame_bytesvolume; - if ($id3v2_majorversion == 3) { - $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset); - if (strlen($parsedFrame['data']) > 0) { - $parsedFrame['incdec']['rightrear'] = (bool) substr($frame_incrdecrflags, 4, 1); - $parsedFrame['incdec']['leftrear'] = (bool) substr($frame_incrdecrflags, 5, 1); - $parsedFrame['volumechange']['rightrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); - if ($parsedFrame['incdec']['rightrear'] === false) { - $parsedFrame['volumechange']['rightrear'] *= -1; - } - $frame_offset += $frame_bytesvolume; - $parsedFrame['volumechange']['leftrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); - if ($parsedFrame['incdec']['leftrear'] === false) { - $parsedFrame['volumechange']['leftrear'] *= -1; - } - $frame_offset += $frame_bytesvolume; - $parsedFrame['peakvolume']['rightrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); - $frame_offset += $frame_bytesvolume; - $parsedFrame['peakvolume']['leftrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); - $frame_offset += $frame_bytesvolume; - } - $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset); - if (strlen($parsedFrame['data']) > 0) { - $parsedFrame['incdec']['center'] = (bool) substr($frame_incrdecrflags, 3, 1); - $parsedFrame['volumechange']['center'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); - if ($parsedFrame['incdec']['center'] === false) { - $parsedFrame['volumechange']['center'] *= -1; - } - $frame_offset += $frame_bytesvolume; - $parsedFrame['peakvolume']['center'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); - $frame_offset += $frame_bytesvolume; - } - $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset); - if (strlen($parsedFrame['data']) > 0) { - $parsedFrame['incdec']['bass'] = (bool) substr($frame_incrdecrflags, 2, 1); - $parsedFrame['volumechange']['bass'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); - if ($parsedFrame['incdec']['bass'] === false) { - $parsedFrame['volumechange']['bass'] *= -1; - } - $frame_offset += $frame_bytesvolume; - $parsedFrame['peakvolume']['bass'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); - $frame_offset += $frame_bytesvolume; - } - } - unset($parsedFrame['data']); - - - } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'EQU2')) { // 4.12 EQU2 Equalisation (2) (ID3v2.4+ only) - // There may be more than one 'EQU2' frame in each tag, - // but only one with the same identification string - //
- // Interpolation method $xx - // $00 Band - // $01 Linear - // Identification $00 - // The following is then repeated for every adjustment point - // Frequency $xx xx - // Volume adjustment $xx xx - - $frame_offset = 0; - $frame_interpolationmethod = ord(substr($parsedFrame['data'], $frame_offset++, 1)); - $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); - $frame_idstring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_idstring) === 0) { - $frame_idstring = ''; - } - $parsedFrame['description'] = $frame_idstring; - $frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen("\x00")); - while (strlen($frame_remainingdata)) { - $frame_frequency = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 2)) / 2; - $parsedFrame['data'][$frame_frequency] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 2, 2), false, true); - $frame_remainingdata = substr($frame_remainingdata, 4); - } - $parsedFrame['interpolationmethod'] = $frame_interpolationmethod; - unset($parsedFrame['data']); - - - } elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'EQUA')) || // 4.12 EQUA Equalisation (ID3v2.3 only) - (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'EQU'))) { // 4.13 EQU Equalisation (ID3v2.2 only) - // There may only be one 'EQUA' frame in each tag - //
- // Adjustment bits $xx - // This is followed by 2 bytes + ('adjustment bits' rounded up to the - // nearest byte) for every equalisation band in the following format, - // giving a frequency range of 0 - 32767Hz: - // Increment/decrement %x (MSB of the Frequency) - // Frequency (lower 15 bits) - // Adjustment $xx (xx ...) - - $frame_offset = 0; - $parsedFrame['adjustmentbits'] = substr($parsedFrame['data'], $frame_offset++, 1); - $frame_adjustmentbytes = ceil($parsedFrame['adjustmentbits'] / 8); - - $frame_remainingdata = (string) substr($parsedFrame['data'], $frame_offset); - while (strlen($frame_remainingdata) > 0) { - $frame_frequencystr = getid3_lib::BigEndian2Bin(substr($frame_remainingdata, 0, 2)); - $frame_incdec = (bool) substr($frame_frequencystr, 0, 1); - $frame_frequency = bindec(substr($frame_frequencystr, 1, 15)); - $parsedFrame[$frame_frequency]['incdec'] = $frame_incdec; - $parsedFrame[$frame_frequency]['adjustment'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 2, $frame_adjustmentbytes)); - if ($parsedFrame[$frame_frequency]['incdec'] === false) { - $parsedFrame[$frame_frequency]['adjustment'] *= -1; - } - $frame_remainingdata = substr($frame_remainingdata, 2 + $frame_adjustmentbytes); - } - unset($parsedFrame['data']); - - - } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RVRB')) || // 4.13 RVRB Reverb - (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'REV'))) { // 4.14 REV Reverb - // There may only be one 'RVRB' frame in each tag. - //
- // Reverb left (ms) $xx xx - // Reverb right (ms) $xx xx - // Reverb bounces, left $xx - // Reverb bounces, right $xx - // Reverb feedback, left to left $xx - // Reverb feedback, left to right $xx - // Reverb feedback, right to right $xx - // Reverb feedback, right to left $xx - // Premix left to right $xx - // Premix right to left $xx - - $frame_offset = 0; - $parsedFrame['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); - $frame_offset += 2; - $parsedFrame['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); - $frame_offset += 2; - $parsedFrame['bouncesL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); - $parsedFrame['bouncesR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); - $parsedFrame['feedbackLL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); - $parsedFrame['feedbackLR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); - $parsedFrame['feedbackRR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); - $parsedFrame['feedbackRL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); - $parsedFrame['premixLR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); - $parsedFrame['premixRL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); - unset($parsedFrame['data']); - - - } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'APIC')) || // 4.14 APIC Attached picture - (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'PIC'))) { // 4.15 PIC Attached picture - // There may be several pictures attached to one file, - // each in their individual 'APIC' frame, but only one - // with the same content descriptor - //
- // Text encoding $xx - // ID3v2.3+ => MIME type $00 - // ID3v2.2 => Image format $xx xx xx - // Picture type $xx - // Description $00 (00) - // Picture data - - $frame_offset = 0; - $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); - if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { - $ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; - } - - if ($id3v2_majorversion == 2) { - $frame_imagetype = substr($parsedFrame['data'], $frame_offset, 3); - if (strtolower($frame_imagetype) == 'ima') { - // complete hack for mp3Rage (www.chaoticsoftware.com) that puts ID3v2.3-formatted - // MIME type instead of 3-char ID3v2.2-format image type (thanks xbhoffØpacbell*net) - $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); - $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_mimetype) === 0) { - $frame_mimetype = ''; - } - $frame_imagetype = strtoupper(str_replace('image/', '', strtolower($frame_mimetype))); - if ($frame_imagetype == 'JPEG') { - $frame_imagetype = 'JPG'; - } - $frame_offset = $frame_terminatorpos + strlen("\x00"); - } else { - $frame_offset += 3; - } - } - if ($id3v2_majorversion > 2) { - $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); - $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_mimetype) === 0) { - $frame_mimetype = ''; - } - $frame_offset = $frame_terminatorpos + strlen("\x00"); - } - - $frame_picturetype = ord(substr($parsedFrame['data'], $frame_offset++, 1)); - - $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); - if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { - $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 - } - $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_description) === 0) { - $frame_description = ''; - } - $parsedFrame['encodingid'] = $frame_textencoding; - $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); - - if ($id3v2_majorversion == 2) { - $parsedFrame['imagetype'] = $frame_imagetype; - } else { - $parsedFrame['mime'] = $frame_mimetype; - } - $parsedFrame['picturetypeid'] = $frame_picturetype; - $parsedFrame['picturetype'] = $this->APICPictureTypeLookup($frame_picturetype); - $parsedFrame['description'] = $frame_description; - $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); - - $imagechunkcheck = getid3_lib::GetDataImageSize($parsedFrame['data']); - if (($imagechunkcheck[2] >= 1) && ($imagechunkcheck[2] <= 3)) { - $parsedFrame['image_mime'] = 'image/'.getid3_lib::ImageTypesLookup($imagechunkcheck[2]); - if ($imagechunkcheck[0]) { - $parsedFrame['image_width'] = $imagechunkcheck[0]; - } - if ($imagechunkcheck[1]) { - $parsedFrame['image_height'] = $imagechunkcheck[1]; - } - $parsedFrame['image_bytes'] = strlen($parsedFrame['data']); - } - - - } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'GEOB')) || // 4.15 GEOB General encapsulated object - (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'GEO'))) { // 4.16 GEO General encapsulated object - // There may be more than one 'GEOB' frame in each tag, - // but only one with the same content descriptor - //
- // Text encoding $xx - // MIME type $00 - // Filename $00 (00) - // Content description $00 (00) - // Encapsulated object - - $frame_offset = 0; - $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); - if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { - $ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; - } - $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); - $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_mimetype) === 0) { - $frame_mimetype = ''; - } - $frame_offset = $frame_terminatorpos + strlen("\x00"); - - $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); - if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { - $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 - } - $frame_filename = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_filename) === 0) { - $frame_filename = ''; - } - $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)); - - $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); - if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { - $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 - } - $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_description) === 0) { - $frame_description = ''; - } - $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)); - - $parsedFrame['objectdata'] = (string) substr($parsedFrame['data'], $frame_offset); - $parsedFrame['encodingid'] = $frame_textencoding; - $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); - - $parsedFrame['mime'] = $frame_mimetype; - $parsedFrame['filename'] = $frame_filename; - $parsedFrame['description'] = $frame_description; - unset($parsedFrame['data']); - - - } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'PCNT')) || // 4.16 PCNT Play counter - (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CNT'))) { // 4.17 CNT Play counter - // There may only be one 'PCNT' frame in each tag. - // When the counter reaches all one's, one byte is inserted in - // front of the counter thus making the counter eight bits bigger - //
- // Counter $xx xx xx xx (xx ...) - - $parsedFrame['data'] = getid3_lib::BigEndian2Int($parsedFrame['data']); - - - } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'POPM')) || // 4.17 POPM Popularimeter - (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'POP'))) { // 4.18 POP Popularimeter - // There may be more than one 'POPM' frame in each tag, - // but only one with the same email address - //
- // Email to user $00 - // Rating $xx - // Counter $xx xx xx xx (xx ...) - - $frame_offset = 0; - $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); - $frame_emailaddress = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_emailaddress) === 0) { - $frame_emailaddress = ''; - } - $frame_offset = $frame_terminatorpos + strlen("\x00"); - $frame_rating = ord(substr($parsedFrame['data'], $frame_offset++, 1)); - $parsedFrame['data'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset)); - $parsedFrame['email'] = $frame_emailaddress; - $parsedFrame['rating'] = $frame_rating; - unset($parsedFrame['data']); - - - } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RBUF')) || // 4.18 RBUF Recommended buffer size - (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'BUF'))) { // 4.19 BUF Recommended buffer size - // There may only be one 'RBUF' frame in each tag - //
- // Buffer size $xx xx xx - // Embedded info flag %0000000x - // Offset to next tag $xx xx xx xx - - $frame_offset = 0; - $parsedFrame['buffersize'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 3)); - $frame_offset += 3; - - $frame_embeddedinfoflags = getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1)); - $parsedFrame['flags']['embededinfo'] = (bool) substr($frame_embeddedinfoflags, 7, 1); - $parsedFrame['nexttagoffset'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); - unset($parsedFrame['data']); - - - } elseif (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CRM')) { // 4.20 Encrypted meta frame (ID3v2.2 only) - // There may be more than one 'CRM' frame in a tag, - // but only one with the same 'owner identifier' - //
- // Owner identifier $00 (00) - // Content/explanation $00 (00) - // Encrypted datablock - - $frame_offset = 0; - $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); - $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - $frame_offset = $frame_terminatorpos + strlen("\x00"); - - $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); - $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_description) === 0) { - $frame_description = ''; - } - $frame_offset = $frame_terminatorpos + strlen("\x00"); - - $parsedFrame['ownerid'] = $frame_ownerid; - $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); - $parsedFrame['description'] = $frame_description; - unset($parsedFrame['data']); - - - } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'AENC')) || // 4.19 AENC Audio encryption - (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CRA'))) { // 4.21 CRA Audio encryption - // There may be more than one 'AENC' frames in a tag, - // but only one with the same 'Owner identifier' - //
- // Owner identifier $00 - // Preview start $xx xx - // Preview length $xx xx - // Encryption info - - $frame_offset = 0; - $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); - $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_ownerid) === 0) { - $frame_ownerid == ''; - } - $frame_offset = $frame_terminatorpos + strlen("\x00"); - $parsedFrame['ownerid'] = $frame_ownerid; - $parsedFrame['previewstart'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); - $frame_offset += 2; - $parsedFrame['previewlength'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); - $frame_offset += 2; - $parsedFrame['encryptioninfo'] = (string) substr($parsedFrame['data'], $frame_offset); - unset($parsedFrame['data']); - - - } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'LINK')) || // 4.20 LINK Linked information - (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'LNK'))) { // 4.22 LNK Linked information - // There may be more than one 'LINK' frame in a tag, - // but only one with the same contents - //
- // ID3v2.3+ => Frame identifier $xx xx xx xx - // ID3v2.2 => Frame identifier $xx xx xx - // URL $00 - // ID and additional data - - $frame_offset = 0; - if ($id3v2_majorversion == 2) { - $parsedFrame['frameid'] = substr($parsedFrame['data'], $frame_offset, 3); - $frame_offset += 3; - } else { - $parsedFrame['frameid'] = substr($parsedFrame['data'], $frame_offset, 4); - $frame_offset += 4; - } - - $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); - $frame_url = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_url) === 0) { - $frame_url = ''; - } - $frame_offset = $frame_terminatorpos + strlen("\x00"); - $parsedFrame['url'] = $frame_url; - - $parsedFrame['additionaldata'] = (string) substr($parsedFrame['data'], $frame_offset); - if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) { - $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['url']); - } - unset($parsedFrame['data']); - - - } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'POSS')) { // 4.21 POSS Position synchronisation frame (ID3v2.3+ only) - // There may only be one 'POSS' frame in each tag - // - // Time stamp format $xx - // Position $xx (xx ...) - - $frame_offset = 0; - $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); - $parsedFrame['position'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset)); - unset($parsedFrame['data']); - - - } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'USER')) { // 4.22 USER Terms of use (ID3v2.3+ only) - // There may be more than one 'Terms of use' frame in a tag, - // but only one with the same 'Language' - //
- // Text encoding $xx - // Language $xx xx xx - // The actual text - - $frame_offset = 0; - $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); - if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { - $ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; - } - $frame_language = substr($parsedFrame['data'], $frame_offset, 3); - $frame_offset += 3; - $parsedFrame['language'] = $frame_language; - $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); - $parsedFrame['encodingid'] = $frame_textencoding; - $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); - - $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); - if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { - $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data']); - } - unset($parsedFrame['data']); - - - } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'OWNE')) { // 4.23 OWNE Ownership frame (ID3v2.3+ only) - // There may only be one 'OWNE' frame in a tag - //
- // Text encoding $xx - // Price paid $00 - // Date of purch. - // Seller - - $frame_offset = 0; - $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); - if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { - $ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; - } - $parsedFrame['encodingid'] = $frame_textencoding; - $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); - - $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); - $frame_pricepaid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - $frame_offset = $frame_terminatorpos + strlen("\x00"); - - $parsedFrame['pricepaid']['currencyid'] = substr($frame_pricepaid, 0, 3); - $parsedFrame['pricepaid']['currency'] = $this->LookupCurrencyUnits($parsedFrame['pricepaid']['currencyid']); - $parsedFrame['pricepaid']['value'] = substr($frame_pricepaid, 3); - - $parsedFrame['purchasedate'] = substr($parsedFrame['data'], $frame_offset, 8); - if (!$this->IsValidDateStampString($parsedFrame['purchasedate'])) { - $parsedFrame['purchasedateunix'] = mktime (0, 0, 0, substr($parsedFrame['purchasedate'], 4, 2), substr($parsedFrame['purchasedate'], 6, 2), substr($parsedFrame['purchasedate'], 0, 4)); - } - $frame_offset += 8; - - $parsedFrame['seller'] = (string) substr($parsedFrame['data'], $frame_offset); - unset($parsedFrame['data']); - - - } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'COMR')) { // 4.24 COMR Commercial frame (ID3v2.3+ only) - // There may be more than one 'commercial frame' in a tag, - // but no two may be identical - //
- // Text encoding $xx - // Price string $00 - // Valid until - // Contact URL $00 - // Received as $xx - // Name of seller $00 (00) - // Description $00 (00) - // Picture MIME type $00 - // Seller logo - - $frame_offset = 0; - $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); - if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { - $ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; - } - - $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); - $frame_pricestring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - $frame_offset = $frame_terminatorpos + strlen("\x00"); - $frame_rawpricearray = explode('/', $frame_pricestring); - foreach ($frame_rawpricearray as $key => $val) { - $frame_currencyid = substr($val, 0, 3); - $parsedFrame['price'][$frame_currencyid]['currency'] = $this->LookupCurrencyUnits($frame_currencyid); - $parsedFrame['price'][$frame_currencyid]['value'] = substr($val, 3); - } - - $frame_datestring = substr($parsedFrame['data'], $frame_offset, 8); - $frame_offset += 8; - - $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); - $frame_contacturl = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - $frame_offset = $frame_terminatorpos + strlen("\x00"); - - $frame_receivedasid = ord(substr($parsedFrame['data'], $frame_offset++, 1)); - - $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); - if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { - $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 - } - $frame_sellername = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_sellername) === 0) { - $frame_sellername = ''; - } - $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)); - - $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); - if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { - $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 - } - $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_description) === 0) { - $frame_description = ''; - } - $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)); - - $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); - $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - $frame_offset = $frame_terminatorpos + strlen("\x00"); - - $frame_sellerlogo = substr($parsedFrame['data'], $frame_offset); - - $parsedFrame['encodingid'] = $frame_textencoding; - $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); - - $parsedFrame['pricevaliduntil'] = $frame_datestring; - $parsedFrame['contacturl'] = $frame_contacturl; - $parsedFrame['receivedasid'] = $frame_receivedasid; - $parsedFrame['receivedas'] = $this->COMRReceivedAsLookup($frame_receivedasid); - $parsedFrame['sellername'] = $frame_sellername; - $parsedFrame['description'] = $frame_description; - $parsedFrame['mime'] = $frame_mimetype; - $parsedFrame['logo'] = $frame_sellerlogo; - unset($parsedFrame['data']); - - - } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'ENCR')) { // 4.25 ENCR Encryption method registration (ID3v2.3+ only) - // There may be several 'ENCR' frames in a tag, - // but only one containing the same symbol - // and only one containing the same owner identifier - //
- // Owner identifier $00 - // Method symbol $xx - // Encryption data - - $frame_offset = 0; - $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); - $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_ownerid) === 0) { - $frame_ownerid = ''; - } - $frame_offset = $frame_terminatorpos + strlen("\x00"); - - $parsedFrame['ownerid'] = $frame_ownerid; - $parsedFrame['methodsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); - $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); - - - } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'GRID')) { // 4.26 GRID Group identification registration (ID3v2.3+ only) - - // There may be several 'GRID' frames in a tag, - // but only one containing the same symbol - // and only one containing the same owner identifier - //
- // Owner identifier $00 - // Group symbol $xx - // Group dependent data - - $frame_offset = 0; - $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); - $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_ownerid) === 0) { - $frame_ownerid = ''; - } - $frame_offset = $frame_terminatorpos + strlen("\x00"); - - $parsedFrame['ownerid'] = $frame_ownerid; - $parsedFrame['groupsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); - $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); - - - } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'PRIV')) { // 4.27 PRIV Private frame (ID3v2.3+ only) - // The tag may contain more than one 'PRIV' frame - // but only with different contents - //
- // Owner identifier $00 - // The private data - - $frame_offset = 0; - $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); - $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); - if (ord($frame_ownerid) === 0) { - $frame_ownerid = ''; - } - $frame_offset = $frame_terminatorpos + strlen("\x00"); - - $parsedFrame['ownerid'] = $frame_ownerid; - $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); - - - } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'SIGN')) { // 4.28 SIGN Signature frame (ID3v2.4+ only) - // There may be more than one 'signature frame' in a tag, - // but no two may be identical - //
- // Group symbol $xx - // Signature - - $frame_offset = 0; - $parsedFrame['groupsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); - $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); - - - } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'SEEK')) { // 4.29 SEEK Seek frame (ID3v2.4+ only) - // There may only be one 'seek frame' in a tag - //
- // Minimum offset to next tag $xx xx xx xx - - $frame_offset = 0; - $parsedFrame['data'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); - - - } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'ASPI')) { // 4.30 ASPI Audio seek point index (ID3v2.4+ only) - // There may only be one 'audio seek point index' frame in a tag - //
- // Indexed data start (S) $xx xx xx xx - // Indexed data length (L) $xx xx xx xx - // Number of index points (N) $xx xx - // Bits per index point (b) $xx - // Then for every index point the following data is included: - // Fraction at index (Fi) $xx (xx) - - $frame_offset = 0; - $parsedFrame['datastart'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); - $frame_offset += 4; - $parsedFrame['indexeddatalength'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); - $frame_offset += 4; - $parsedFrame['indexpoints'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); - $frame_offset += 2; - $parsedFrame['bitsperpoint'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); - $frame_bytesperpoint = ceil($parsedFrame['bitsperpoint'] / 8); - for ($i = 0; $i < $frame_indexpoints; $i++) { - $parsedFrame['indexes'][$i] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesperpoint)); - $frame_offset += $frame_bytesperpoint; - } - unset($parsedFrame['data']); - - } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RGAD')) { // Replay Gain Adjustment - // http://privatewww.essex.ac.uk/~djmrob/replaygain/file_format_id3v2.html - // There may only be one 'RGAD' frame in a tag - //
- // Peak Amplitude $xx $xx $xx $xx - // Radio Replay Gain Adjustment %aaabbbcd %dddddddd - // Audiophile Replay Gain Adjustment %aaabbbcd %dddddddd - // a - name code - // b - originator code - // c - sign bit - // d - replay gain adjustment - - $frame_offset = 0; - $parsedFrame['peakamplitude'] = getid3_lib::BigEndian2Float(substr($parsedFrame['data'], $frame_offset, 4)); - $frame_offset += 4; - $rg_track_adjustment = getid3_lib::Dec2Bin(substr($parsedFrame['data'], $frame_offset, 2)); - $frame_offset += 2; - $rg_album_adjustment = getid3_lib::Dec2Bin(substr($parsedFrame['data'], $frame_offset, 2)); - $frame_offset += 2; - $parsedFrame['raw']['track']['name'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 0, 3)); - $parsedFrame['raw']['track']['originator'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 3, 3)); - $parsedFrame['raw']['track']['signbit'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 6, 1)); - $parsedFrame['raw']['track']['adjustment'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 7, 9)); - $parsedFrame['raw']['album']['name'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 0, 3)); - $parsedFrame['raw']['album']['originator'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 3, 3)); - $parsedFrame['raw']['album']['signbit'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 6, 1)); - $parsedFrame['raw']['album']['adjustment'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 7, 9)); - $parsedFrame['track']['name'] = getid3_lib::RGADnameLookup($parsedFrame['raw']['track']['name']); - $parsedFrame['track']['originator'] = getid3_lib::RGADoriginatorLookup($parsedFrame['raw']['track']['originator']); - $parsedFrame['track']['adjustment'] = getid3_lib::RGADadjustmentLookup($parsedFrame['raw']['track']['adjustment'], $parsedFrame['raw']['track']['signbit']); - $parsedFrame['album']['name'] = getid3_lib::RGADnameLookup($parsedFrame['raw']['album']['name']); - $parsedFrame['album']['originator'] = getid3_lib::RGADoriginatorLookup($parsedFrame['raw']['album']['originator']); - $parsedFrame['album']['adjustment'] = getid3_lib::RGADadjustmentLookup($parsedFrame['raw']['album']['adjustment'], $parsedFrame['raw']['album']['signbit']); - - $ThisFileInfo['replay_gain']['track']['peak'] = $parsedFrame['peakamplitude']; - $ThisFileInfo['replay_gain']['track']['originator'] = $parsedFrame['track']['originator']; - $ThisFileInfo['replay_gain']['track']['adjustment'] = $parsedFrame['track']['adjustment']; - $ThisFileInfo['replay_gain']['album']['originator'] = $parsedFrame['album']['originator']; - $ThisFileInfo['replay_gain']['album']['adjustment'] = $parsedFrame['album']['adjustment']; - - unset($parsedFrame['data']); - - } - - return true; - } - - - function DeUnsynchronise($data) { - return str_replace("\xFF\x00", "\xFF", $data); - } - - function LookupCurrencyUnits($currencyid) { - - $begin = __LINE__; - - /** This is not a comment! - - - AED Dirhams - AFA Afghanis - ALL Leke - AMD Drams - ANG Guilders - AOA Kwanza - ARS Pesos - ATS Schillings - AUD Dollars - AWG Guilders - AZM Manats - BAM Convertible Marka - BBD Dollars - BDT Taka - BEF Francs - BGL Leva - BHD Dinars - BIF Francs - BMD Dollars - BND Dollars - BOB Bolivianos - BRL Brazil Real - BSD Dollars - BTN Ngultrum - BWP Pulas - BYR Rubles - BZD Dollars - CAD Dollars - CDF Congolese Francs - CHF Francs - CLP Pesos - CNY Yuan Renminbi - COP Pesos - CRC Colones - CUP Pesos - CVE Escudos - CYP Pounds - CZK Koruny - DEM Deutsche Marks - DJF Francs - DKK Kroner - DOP Pesos - DZD Algeria Dinars - EEK Krooni - EGP Pounds - ERN Nakfa - ESP Pesetas - ETB Birr - EUR Euro - FIM Markkaa - FJD Dollars - FKP Pounds - FRF Francs - GBP Pounds - GEL Lari - GGP Pounds - GHC Cedis - GIP Pounds - GMD Dalasi - GNF Francs - GRD Drachmae - GTQ Quetzales - GYD Dollars - HKD Dollars - HNL Lempiras - HRK Kuna - HTG Gourdes - HUF Forints - IDR Rupiahs - IEP Pounds - ILS New Shekels - IMP Pounds - INR Rupees - IQD Dinars - IRR Rials - ISK Kronur - ITL Lire - JEP Pounds - JMD Dollars - JOD Dinars - JPY Yen - KES Shillings - KGS Soms - KHR Riels - KMF Francs - KPW Won - KWD Dinars - KYD Dollars - KZT Tenge - LAK Kips - LBP Pounds - LKR Rupees - LRD Dollars - LSL Maloti - LTL Litai - LUF Francs - LVL Lati - LYD Dinars - MAD Dirhams - MDL Lei - MGF Malagasy Francs - MKD Denars - MMK Kyats - MNT Tugriks - MOP Patacas - MRO Ouguiyas - MTL Liri - MUR Rupees - MVR Rufiyaa - MWK Kwachas - MXN Pesos - MYR Ringgits - MZM Meticais - NAD Dollars - NGN Nairas - NIO Gold Cordobas - NLG Guilders - NOK Krone - NPR Nepal Rupees - NZD Dollars - OMR Rials - PAB Balboa - PEN Nuevos Soles - PGK Kina - PHP Pesos - PKR Rupees - PLN Zlotych - PTE Escudos - PYG Guarani - QAR Rials - ROL Lei - RUR Rubles - RWF Rwanda Francs - SAR Riyals - SBD Dollars - SCR Rupees - SDD Dinars - SEK Kronor - SGD Dollars - SHP Pounds - SIT Tolars - SKK Koruny - SLL Leones - SOS Shillings - SPL Luigini - SRG Guilders - STD Dobras - SVC Colones - SYP Pounds - SZL Emalangeni - THB Baht - TJR Rubles - TMM Manats - TND Dinars - TOP Pa'anga - TRL Liras - TTD Dollars - TVD Tuvalu Dollars - TWD New Dollars - TZS Shillings - UAH Hryvnia - UGX Shillings - USD Dollars - UYU Pesos - UZS Sums - VAL Lire - VEB Bolivares - VND Dong - VUV Vatu - WST Tala - XAF Francs - XAG Ounces - XAU Ounces - XCD Dollars - XDR Special Drawing Rights - XPD Ounces - XPF Francs - XPT Ounces - YER Rials - YUM New Dinars - ZAR Rand - ZMK Kwacha - ZWD Zimbabwe Dollars - - */ - - return getid3_lib::EmbeddedLookup($currencyid, $begin, __LINE__, __FILE__, 'id3v2-currency-units'); - } - - - function LookupCurrencyCountry($currencyid) { - - $begin = __LINE__; - - /** This is not a comment! - - AED United Arab Emirates - AFA Afghanistan - ALL Albania - AMD Armenia - ANG Netherlands Antilles - AOA Angola - ARS Argentina - ATS Austria - AUD Australia - AWG Aruba - AZM Azerbaijan - BAM Bosnia and Herzegovina - BBD Barbados - BDT Bangladesh - BEF Belgium - BGL Bulgaria - BHD Bahrain - BIF Burundi - BMD Bermuda - BND Brunei Darussalam - BOB Bolivia - BRL Brazil - BSD Bahamas - BTN Bhutan - BWP Botswana - BYR Belarus - BZD Belize - CAD Canada - CDF Congo/Kinshasa - CHF Switzerland - CLP Chile - CNY China - COP Colombia - CRC Costa Rica - CUP Cuba - CVE Cape Verde - CYP Cyprus - CZK Czech Republic - DEM Germany - DJF Djibouti - DKK Denmark - DOP Dominican Republic - DZD Algeria - EEK Estonia - EGP Egypt - ERN Eritrea - ESP Spain - ETB Ethiopia - EUR Euro Member Countries - FIM Finland - FJD Fiji - FKP Falkland Islands (Malvinas) - FRF France - GBP United Kingdom - GEL Georgia - GGP Guernsey - GHC Ghana - GIP Gibraltar - GMD Gambia - GNF Guinea - GRD Greece - GTQ Guatemala - GYD Guyana - HKD Hong Kong - HNL Honduras - HRK Croatia - HTG Haiti - HUF Hungary - IDR Indonesia - IEP Ireland (Eire) - ILS Israel - IMP Isle of Man - INR India - IQD Iraq - IRR Iran - ISK Iceland - ITL Italy - JEP Jersey - JMD Jamaica - JOD Jordan - JPY Japan - KES Kenya - KGS Kyrgyzstan - KHR Cambodia - KMF Comoros - KPW Korea - KWD Kuwait - KYD Cayman Islands - KZT Kazakstan - LAK Laos - LBP Lebanon - LKR Sri Lanka - LRD Liberia - LSL Lesotho - LTL Lithuania - LUF Luxembourg - LVL Latvia - LYD Libya - MAD Morocco - MDL Moldova - MGF Madagascar - MKD Macedonia - MMK Myanmar (Burma) - MNT Mongolia - MOP Macau - MRO Mauritania - MTL Malta - MUR Mauritius - MVR Maldives (Maldive Islands) - MWK Malawi - MXN Mexico - MYR Malaysia - MZM Mozambique - NAD Namibia - NGN Nigeria - NIO Nicaragua - NLG Netherlands (Holland) - NOK Norway - NPR Nepal - NZD New Zealand - OMR Oman - PAB Panama - PEN Peru - PGK Papua New Guinea - PHP Philippines - PKR Pakistan - PLN Poland - PTE Portugal - PYG Paraguay - QAR Qatar - ROL Romania - RUR Russia - RWF Rwanda - SAR Saudi Arabia - SBD Solomon Islands - SCR Seychelles - SDD Sudan - SEK Sweden - SGD Singapore - SHP Saint Helena - SIT Slovenia - SKK Slovakia - SLL Sierra Leone - SOS Somalia - SPL Seborga - SRG Suriname - STD São Tome and Principe - SVC El Salvador - SYP Syria - SZL Swaziland - THB Thailand - TJR Tajikistan - TMM Turkmenistan - TND Tunisia - TOP Tonga - TRL Turkey - TTD Trinidad and Tobago - TVD Tuvalu - TWD Taiwan - TZS Tanzania - UAH Ukraine - UGX Uganda - USD United States of America - UYU Uruguay - UZS Uzbekistan - VAL Vatican City - VEB Venezuela - VND Viet Nam - VUV Vanuatu - WST Samoa - XAF Communauté Financière Africaine - XAG Silver - XAU Gold - XCD East Caribbean - XDR International Monetary Fund - XPD Palladium - XPF Comptoirs Français du Pacifique - XPT Platinum - YER Yemen - YUM Yugoslavia - ZAR South Africa - ZMK Zambia - ZWD Zimbabwe - - */ - - return getid3_lib::EmbeddedLookup($currencyid, $begin, __LINE__, __FILE__, 'id3v2-currency-country'); - } - - - - function LanguageLookup($languagecode, $casesensitive=false) { - - if (!$casesensitive) { - $languagecode = strtolower($languagecode); - } - - // http://www.id3.org/id3v2.4.0-structure.txt - // [4. ID3v2 frame overview] - // The three byte language field, present in several frames, is used to - // describe the language of the frame's content, according to ISO-639-2 - // [ISO-639-2]. The language should be represented in lower case. If the - // language is not known the string "XXX" should be used. - - - // ISO 639-2 - http://www.id3.org/iso639-2.html - - $begin = __LINE__; - - /** This is not a comment! - - XXX unknown - xxx unknown - aar Afar - abk Abkhazian - ace Achinese - ach Acoli - ada Adangme - afa Afro-Asiatic (Other) - afh Afrihili - afr Afrikaans - aka Akan - akk Akkadian - alb Albanian - ale Aleut - alg Algonquian Languages - amh Amharic - ang English, Old (ca. 450-1100) - apa Apache Languages - ara Arabic - arc Aramaic - arm Armenian - arn Araucanian - arp Arapaho - art Artificial (Other) - arw Arawak - asm Assamese - ath Athapascan Languages - ava Avaric - ave Avestan - awa Awadhi - aym Aymara - aze Azerbaijani - bad Banda - bai Bamileke Languages - bak Bashkir - bal Baluchi - bam Bambara - ban Balinese - baq Basque - bas Basa - bat Baltic (Other) - bej Beja - bel Byelorussian - bem Bemba - ben Bengali - ber Berber (Other) - bho Bhojpuri - bih Bihari - bik Bikol - bin Bini - bis Bislama - bla Siksika - bnt Bantu (Other) - bod Tibetan - bra Braj - bre Breton - bua Buriat - bug Buginese - bul Bulgarian - bur Burmese - cad Caddo - cai Central American Indian (Other) - car Carib - cat Catalan - cau Caucasian (Other) - ceb Cebuano - cel Celtic (Other) - ces Czech - cha Chamorro - chb Chibcha - che Chechen - chg Chagatai - chi Chinese - chm Mari - chn Chinook jargon - cho Choctaw - chr Cherokee - chu Church Slavic - chv Chuvash - chy Cheyenne - cop Coptic - cor Cornish - cos Corsican - cpe Creoles and Pidgins, English-based (Other) - cpf Creoles and Pidgins, French-based (Other) - cpp Creoles and Pidgins, Portuguese-based (Other) - cre Cree - crp Creoles and Pidgins (Other) - cus Cushitic (Other) - cym Welsh - cze Czech - dak Dakota - dan Danish - del Delaware - deu German - din Dinka - div Divehi - doi Dogri - dra Dravidian (Other) - dua Duala - dum Dutch, Middle (ca. 1050-1350) - dut Dutch - dyu Dyula - dzo Dzongkha - efi Efik - egy Egyptian (Ancient) - eka Ekajuk - ell Greek, Modern (1453-) - elx Elamite - eng English - enm English, Middle (ca. 1100-1500) - epo Esperanto - esk Eskimo (Other) - esl Spanish - est Estonian - eus Basque - ewe Ewe - ewo Ewondo - fan Fang - fao Faroese - fas Persian - fat Fanti - fij Fijian - fin Finnish - fiu Finno-Ugrian (Other) - fon Fon - fra French - fre French - frm French, Middle (ca. 1400-1600) - fro French, Old (842- ca. 1400) - fry Frisian - ful Fulah - gaa Ga - gae Gaelic (Scots) - gai Irish - gay Gayo - gdh Gaelic (Scots) - gem Germanic (Other) - geo Georgian - ger German - gez Geez - gil Gilbertese - glg Gallegan - gmh German, Middle High (ca. 1050-1500) - goh German, Old High (ca. 750-1050) - gon Gondi - got Gothic - grb Grebo - grc Greek, Ancient (to 1453) - gre Greek, Modern (1453-) - grn Guarani - guj Gujarati - hai Haida - hau Hausa - haw Hawaiian - heb Hebrew - her Herero - hil Hiligaynon - him Himachali - hin Hindi - hmo Hiri Motu - hun Hungarian - hup Hupa - hye Armenian - iba Iban - ibo Igbo - ice Icelandic - ijo Ijo - iku Inuktitut - ilo Iloko - ina Interlingua (International Auxiliary language Association) - inc Indic (Other) - ind Indonesian - ine Indo-European (Other) - ine Interlingue - ipk Inupiak - ira Iranian (Other) - iri Irish - iro Iroquoian uages - isl Icelandic - ita Italian - jav Javanese - jaw Javanese - jpn Japanese - jpr Judeo-Persian - jrb Judeo-Arabic - kaa Kara-Kalpak - kab Kabyle - kac Kachin - kal Greenlandic - kam Kamba - kan Kannada - kar Karen - kas Kashmiri - kat Georgian - kau Kanuri - kaw Kawi - kaz Kazakh - kha Khasi - khi Khoisan (Other) - khm Khmer - kho Khotanese - kik Kikuyu - kin Kinyarwanda - kir Kirghiz - kok Konkani - kom Komi - kon Kongo - kor Korean - kpe Kpelle - kro Kru - kru Kurukh - kua Kuanyama - kum Kumyk - kur Kurdish - kus Kusaie - kut Kutenai - lad Ladino - lah Lahnda - lam Lamba - lao Lao - lat Latin - lav Latvian - lez Lezghian - lin Lingala - lit Lithuanian - lol Mongo - loz Lozi - ltz Letzeburgesch - lub Luba-Katanga - lug Ganda - lui Luiseno - lun Lunda - luo Luo (Kenya and Tanzania) - mac Macedonian - mad Madurese - mag Magahi - mah Marshall - mai Maithili - mak Macedonian - mak Makasar - mal Malayalam - man Mandingo - mao Maori - map Austronesian (Other) - mar Marathi - mas Masai - max Manx - may Malay - men Mende - mga Irish, Middle (900 - 1200) - mic Micmac - min Minangkabau - mis Miscellaneous (Other) - mkh Mon-Kmer (Other) - mlg Malagasy - mlt Maltese - mni Manipuri - mno Manobo Languages - moh Mohawk - mol Moldavian - mon Mongolian - mos Mossi - mri Maori - msa Malay - mul Multiple Languages - mun Munda Languages - mus Creek - mwr Marwari - mya Burmese - myn Mayan Languages - nah Aztec - nai North American Indian (Other) - nau Nauru - nav Navajo - nbl Ndebele, South - nde Ndebele, North - ndo Ndongo - nep Nepali - new Newari - nic Niger-Kordofanian (Other) - niu Niuean - nla Dutch - nno Norwegian (Nynorsk) - non Norse, Old - nor Norwegian - nso Sotho, Northern - nub Nubian Languages - nya Nyanja - nym Nyamwezi - nyn Nyankole - nyo Nyoro - nzi Nzima - oci Langue d'Oc (post 1500) - oji Ojibwa - ori Oriya - orm Oromo - osa Osage - oss Ossetic - ota Turkish, Ottoman (1500 - 1928) - oto Otomian Languages - paa Papuan-Australian (Other) - pag Pangasinan - pal Pahlavi - pam Pampanga - pan Panjabi - pap Papiamento - pau Palauan - peo Persian, Old (ca 600 - 400 B.C.) - per Persian - phn Phoenician - pli Pali - pol Polish - pon Ponape - por Portuguese - pra Prakrit uages - pro Provencal, Old (to 1500) - pus Pushto - que Quechua - raj Rajasthani - rar Rarotongan - roa Romance (Other) - roh Rhaeto-Romance - rom Romany - ron Romanian - rum Romanian - run Rundi - rus Russian - sad Sandawe - sag Sango - sah Yakut - sai South American Indian (Other) - sal Salishan Languages - sam Samaritan Aramaic - san Sanskrit - sco Scots - scr Serbo-Croatian - sel Selkup - sem Semitic (Other) - sga Irish, Old (to 900) - shn Shan - sid Sidamo - sin Singhalese - sio Siouan Languages - sit Sino-Tibetan (Other) - sla Slavic (Other) - slk Slovak - slo Slovak - slv Slovenian - smi Sami Languages - smo Samoan - sna Shona - snd Sindhi - sog Sogdian - som Somali - son Songhai - sot Sotho, Southern - spa Spanish - sqi Albanian - srd Sardinian - srr Serer - ssa Nilo-Saharan (Other) - ssw Siswant - ssw Swazi - suk Sukuma - sun Sudanese - sus Susu - sux Sumerian - sve Swedish - swa Swahili - swe Swedish - syr Syriac - tah Tahitian - tam Tamil - tat Tatar - tel Telugu - tem Timne - ter Tereno - tgk Tajik - tgl Tagalog - tha Thai - tib Tibetan - tig Tigre - tir Tigrinya - tiv Tivi - tli Tlingit - tmh Tamashek - tog Tonga (Nyasa) - ton Tonga (Tonga Islands) - tru Truk - tsi Tsimshian - tsn Tswana - tso Tsonga - tuk Turkmen - tum Tumbuka - tur Turkish - tut Altaic (Other) - twi Twi - tyv Tuvinian - uga Ugaritic - uig Uighur - ukr Ukrainian - umb Umbundu - und Undetermined - urd Urdu - uzb Uzbek - vai Vai - ven Venda - vie Vietnamese - vol Volapük - vot Votic - wak Wakashan Languages - wal Walamo - war Waray - was Washo - wel Welsh - wen Sorbian Languages - wol Wolof - xho Xhosa - yao Yao - yap Yap - yid Yiddish - yor Yoruba - zap Zapotec - zen Zenaga - zha Zhuang - zho Chinese - zul Zulu - zun Zuni - - */ - - return getid3_lib::EmbeddedLookup($languagecode, $begin, __LINE__, __FILE__, 'id3v2-languagecode'); - } - - - function ETCOEventLookup($index) { - if (($index >= 0x17) && ($index <= 0xDF)) { - return 'reserved for future use'; - } - if (($index >= 0xE0) && ($index <= 0xEF)) { - return 'not predefined synch 0-F'; - } - if (($index >= 0xF0) && ($index <= 0xFC)) { - return 'reserved for future use'; - } - - static $EventLookup = array( - 0x00 => 'padding (has no meaning)', - 0x01 => 'end of initial silence', - 0x02 => 'intro start', - 0x03 => 'main part start', - 0x04 => 'outro start', - 0x05 => 'outro end', - 0x06 => 'verse start', - 0x07 => 'refrain start', - 0x08 => 'interlude start', - 0x09 => 'theme start', - 0x0A => 'variation start', - 0x0B => 'key change', - 0x0C => 'time change', - 0x0D => 'momentary unwanted noise (Snap, Crackle & Pop)', - 0x0E => 'sustained noise', - 0x0F => 'sustained noise end', - 0x10 => 'intro end', - 0x11 => 'main part end', - 0x12 => 'verse end', - 0x13 => 'refrain end', - 0x14 => 'theme end', - 0x15 => 'profanity', - 0x16 => 'profanity end', - 0xFD => 'audio end (start of silence)', - 0xFE => 'audio file ends', - 0xFF => 'one more byte of events follows' - ); - - return (isset($EventLookup[$index]) ? $EventLookup[$index] : ''); - } - - function SYTLContentTypeLookup($index) { - static $SYTLContentTypeLookup = array( - 0x00 => 'other', - 0x01 => 'lyrics', - 0x02 => 'text transcription', - 0x03 => 'movement/part name', // (e.g. 'Adagio') - 0x04 => 'events', // (e.g. 'Don Quijote enters the stage') - 0x05 => 'chord', // (e.g. 'Bb F Fsus') - 0x06 => 'trivia/\'pop up\' information', - 0x07 => 'URLs to webpages', - 0x08 => 'URLs to images' - ); - - return (isset($SYTLContentTypeLookup[$index]) ? $SYTLContentTypeLookup[$index] : ''); - } - - function APICPictureTypeLookup($index, $returnarray=false) { - static $APICPictureTypeLookup = array( - 0x00 => 'Other', - 0x01 => '32x32 pixels \'file icon\' (PNG only)', - 0x02 => 'Other file icon', - 0x03 => 'Cover (front)', - 0x04 => 'Cover (back)', - 0x05 => 'Leaflet page', - 0x06 => 'Media (e.g. label side of CD)', - 0x07 => 'Lead artist/lead performer/soloist', - 0x08 => 'Artist/performer', - 0x09 => 'Conductor', - 0x0A => 'Band/Orchestra', - 0x0B => 'Composer', - 0x0C => 'Lyricist/text writer', - 0x0D => 'Recording Location', - 0x0E => 'During recording', - 0x0F => 'During performance', - 0x10 => 'Movie/video screen capture', - 0x11 => 'A bright coloured fish', - 0x12 => 'Illustration', - 0x13 => 'Band/artist logotype', - 0x14 => 'Publisher/Studio logotype' - ); - if ($returnarray) { - return $APICPictureTypeLookup; - } - return (isset($APICPictureTypeLookup[$index]) ? $APICPictureTypeLookup[$index] : ''); - } - - function COMRReceivedAsLookup($index) { - static $COMRReceivedAsLookup = array( - 0x00 => 'Other', - 0x01 => 'Standard CD album with other songs', - 0x02 => 'Compressed audio on CD', - 0x03 => 'File over the Internet', - 0x04 => 'Stream over the Internet', - 0x05 => 'As note sheets', - 0x06 => 'As note sheets in a book with other sheets', - 0x07 => 'Music on other media', - 0x08 => 'Non-musical merchandise' - ); - - return (isset($COMRReceivedAsLookup[$index]) ? $COMRReceivedAsLookup[$index] : ''); - } - - function RVA2ChannelTypeLookup($index) { - static $RVA2ChannelTypeLookup = array( - 0x00 => 'Other', - 0x01 => 'Master volume', - 0x02 => 'Front right', - 0x03 => 'Front left', - 0x04 => 'Back right', - 0x05 => 'Back left', - 0x06 => 'Front centre', - 0x07 => 'Back centre', - 0x08 => 'Subwoofer' - ); - - return (isset($RVA2ChannelTypeLookup[$index]) ? $RVA2ChannelTypeLookup[$index] : ''); - } - - function FrameNameLongLookup($framename) { - - $begin = __LINE__; - - /** This is not a comment! - - AENC Audio encryption - APIC Attached picture - ASPI Audio seek point index - BUF Recommended buffer size - CNT Play counter - COM Comments - COMM Comments - COMR Commercial frame - CRA Audio encryption - CRM Encrypted meta frame - ENCR Encryption method registration - EQU Equalisation - EQU2 Equalisation (2) - EQUA Equalisation - ETC Event timing codes - ETCO Event timing codes - GEO General encapsulated object - GEOB General encapsulated object - GRID Group identification registration - IPL Involved people list - IPLS Involved people list - LINK Linked information - LNK Linked information - MCDI Music CD identifier - MCI Music CD Identifier - MLL MPEG location lookup table - MLLT MPEG location lookup table - OWNE Ownership frame - PCNT Play counter - PIC Attached picture - POP Popularimeter - POPM Popularimeter - POSS Position synchronisation frame - PRIV Private frame - RBUF Recommended buffer size - REV Reverb - RVA Relative volume adjustment - RVA2 Relative volume adjustment (2) - RVAD Relative volume adjustment - RVRB Reverb - SEEK Seek frame - SIGN Signature frame - SLT Synchronised lyric/text - STC Synced tempo codes - SYLT Synchronised lyric/text - SYTC Synchronised tempo codes - TAL Album/Movie/Show title - TALB Album/Movie/Show title - TBP BPM (Beats Per Minute) - TBPM BPM (beats per minute) - TCM Composer - TCO Content type - TCOM Composer - TCON Content type - TCOP Copyright message - TCR Copyright message - TDA Date - TDAT Date - TDEN Encoding time - TDLY Playlist delay - TDOR Original release time - TDRC Recording time - TDRL Release time - TDTG Tagging time - TDY Playlist delay - TEN Encoded by - TENC Encoded by - TEXT Lyricist/Text writer - TFLT File type - TFT File type - TIM Time - TIME Time - TIPL Involved people list - TIT1 Content group description - TIT2 Title/songname/content description - TIT3 Subtitle/Description refinement - TKE Initial key - TKEY Initial key - TLA Language(s) - TLAN Language(s) - TLE Length - TLEN Length - TMCL Musician credits list - TMED Media type - TMOO Mood - TMT Media type - TOA Original artist(s)/performer(s) - TOAL Original album/movie/show title - TOF Original filename - TOFN Original filename - TOL Original Lyricist(s)/text writer(s) - TOLY Original lyricist(s)/text writer(s) - TOPE Original artist(s)/performer(s) - TOR Original release year - TORY Original release year - TOT Original album/Movie/Show title - TOWN File owner/licensee - TP1 Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group - TP2 Band/Orchestra/Accompaniment - TP3 Conductor/Performer refinement - TP4 Interpreted, remixed, or otherwise modified by - TPA Part of a set - TPB Publisher - TPE1 Lead performer(s)/Soloist(s) - TPE2 Band/orchestra/accompaniment - TPE3 Conductor/performer refinement - TPE4 Interpreted, remixed, or otherwise modified by - TPOS Part of a set - TPRO Produced notice - TPUB Publisher - TRC ISRC (International Standard Recording Code) - TRCK Track number/Position in set - TRD Recording dates - TRDA Recording dates - TRK Track number/Position in set - TRSN Internet radio station name - TRSO Internet radio station owner - TSI Size - TSIZ Size - TSOA Album sort order - TSOP Performer sort order - TSOT Title sort order - TSRC ISRC (international standard recording code) - TSS Software/hardware and settings used for encoding - TSSE Software/Hardware and settings used for encoding - TSST Set subtitle - TT1 Content group description - TT2 Title/Songname/Content description - TT3 Subtitle/Description refinement - TXT Lyricist/text writer - TXX User defined text information frame - TXXX User defined text information frame - TYE Year - TYER Year - UFI Unique file identifier - UFID Unique file identifier - ULT Unsychronised lyric/text transcription - USER Terms of use - USLT Unsynchronised lyric/text transcription - WAF Official audio file webpage - WAR Official artist/performer webpage - WAS Official audio source webpage - WCM Commercial information - WCOM Commercial information - WCOP Copyright/Legal information - WCP Copyright/Legal information - WOAF Official audio file webpage - WOAR Official artist/performer webpage - WOAS Official audio source webpage - WORS Official Internet radio station homepage - WPAY Payment - WPB Publishers official webpage - WPUB Publishers official webpage - WXX User defined URL link frame - WXXX User defined URL link frame - TFEA Featured Artist - TSTU Recording Studio - rgad Replay Gain Adjustment - - */ - - return getid3_lib::EmbeddedLookup($framename, $begin, __LINE__, __FILE__, 'id3v2-framename_long'); - - // Last three: - // from Helium2 [www.helium2.com] - // from http://privatewww.essex.ac.uk/~djmrob/replaygain/file_format_id3v2.html - } - - - function FrameNameShortLookup($framename) { - - $begin = __LINE__; - - /** This is not a comment! - - AENC audio_encryption - APIC attached_picture - ASPI audio_seek_point_index - BUF recommended_buffer_size - CNT play_counter - COM comments - COMM comments - COMR commercial_frame - CRA audio_encryption - CRM encrypted_meta_frame - ENCR encryption_method_registration - EQU equalisation - EQU2 equalisation - EQUA equalisation - ETC event_timing_codes - ETCO event_timing_codes - GEO general_encapsulated_object - GEOB general_encapsulated_object - GRID group_identification_registration - IPL involved_people_list - IPLS involved_people_list - LINK linked_information - LNK linked_information - MCDI music_cd_identifier - MCI music_cd_identifier - MLL mpeg_location_lookup_table - MLLT mpeg_location_lookup_table - OWNE ownership_frame - PCNT play_counter - PIC attached_picture - POP popularimeter - POPM popularimeter - POSS position_synchronisation_frame - PRIV private_frame - RBUF recommended_buffer_size - REV reverb - RVA relative_volume_adjustment - RVA2 relative_volume_adjustment - RVAD relative_volume_adjustment - RVRB reverb - SEEK seek_frame - SIGN signature_frame - SLT synchronised_lyric - STC synced_tempo_codes - SYLT synchronised_lyric - SYTC synchronised_tempo_codes - TAL album - TALB album - TBP bpm - TBPM bpm - TCM composer - TCO content_type - TCOM composer - TCON content_type - TCOP copyright_message - TCR copyright_message - TDA date - TDAT date - TDEN encoding_time - TDLY playlist_delay - TDOR original_release_time - TDRC recording_time - TDRL release_time - TDTG tagging_time - TDY playlist_delay - TEN encoded_by - TENC encoded_by - TEXT lyricist - TFLT file_type - TFT file_type - TIM time - TIME time - TIPL involved_people_list - TIT1 content_group_description - TIT2 title - TIT3 subtitle - TKE initial_key - TKEY initial_key - TLA language - TLAN language - TLE length - TLEN length - TMCL musician_credits_list - TMED media_type - TMOO mood - TMT media_type - TOA original_artist - TOAL original_album - TOF original_filename - TOFN original_filename - TOL original_lyricist - TOLY original_lyricist - TOPE original_artist - TOR original_year - TORY original_year - TOT original_album - TOWN file_owner - TP1 artist - TP2 band - TP3 conductor - TP4 remixer - TPA part_of_a_set - TPB publisher - TPE1 artist - TPE2 band - TPE3 conductor - TPE4 remixer - TPOS part_of_a_set - TPRO produced_notice - TPUB publisher - TRC isrc - TRCK track_number - TRD recording_dates - TRDA recording_dates - TRK track_number - TRSN internet_radio_station_name - TRSO internet_radio_station_owner - TSI size - TSIZ size - TSOA album_sort_order - TSOP performer_sort_order - TSOT title_sort_order - TSRC isrc - TSS encoder_settings - TSSE encoder_settings - TSST set_subtitle - TT1 description - TT2 title - TT3 subtitle - TXT lyricist - TXX text - TXXX text - TYE year - TYER year - UFI unique_file_identifier - UFID unique_file_identifier - ULT unsychronised_lyric - USER terms_of_use - USLT unsynchronised_lyric - WAF url_file - WAR url_artist - WAS url_source - WCM commercial_information - WCOM commercial_information - WCOP copyright - WCP copyright - WOAF url_file - WOAR url_artist - WOAS url_source - WORS url_station - WPAY url_payment - WPB url_publisher - WPUB url_publisher - WXX url_user - WXXX url_user - TFEA featured_artist - TSTU recording_studio - rgad replay_gain_adjustment - - */ - - return getid3_lib::EmbeddedLookup($framename, $begin, __LINE__, __FILE__, 'id3v2-framename_short'); - } - - function TextEncodingTerminatorLookup($encoding) { - // http://www.id3.org/id3v2.4.0-structure.txt - // Frames that allow different types of text encoding contains a text encoding description byte. Possible encodings: - // $00 ISO-8859-1. Terminated with $00. - // $01 UTF-16 encoded Unicode with BOM. All strings in the same frame SHALL have the same byteorder. Terminated with $00 00. - // $02 UTF-16BE encoded Unicode without BOM. Terminated with $00 00. - // $03 UTF-8 encoded Unicode. Terminated with $00. - - static $TextEncodingTerminatorLookup = array(0=>"\x00", 1=>"\x00\x00", 2=>"\x00\x00", 3=>"\x00", 255=>"\x00\x00"); - - return @$TextEncodingTerminatorLookup[$encoding]; - } - - function TextEncodingNameLookup($encoding) { - // http://www.id3.org/id3v2.4.0-structure.txt - static $TextEncodingNameLookup = array(0=>'ISO-8859-1', 1=>'UTF-16', 2=>'UTF-16BE', 3=>'UTF-8', 255=>'UTF-16BE'); - return (isset($TextEncodingNameLookup[$encoding]) ? $TextEncodingNameLookup[$encoding] : 'ISO-8859-1'); - } - - function IsValidID3v2FrameName($framename, $id3v2majorversion) { - switch ($id3v2majorversion) { - case 2: - return ereg('[A-Z][A-Z0-9]{2}', $framename); - break; - - case 3: - case 4: - return ereg('[A-Z][A-Z0-9]{3}', $framename); - break; - } - return false; - } - - function IsANumber($numberstring, $allowdecimal=false, $allownegative=false) { - for ($i = 0; $i < strlen($numberstring); $i++) { - if ((chr($numberstring{$i}) < chr('0')) || (chr($numberstring{$i}) > chr('9'))) { - if (($numberstring{$i} == '.') && $allowdecimal) { - // allowed - } elseif (($numberstring{$i} == '-') && $allownegative && ($i == 0)) { - // allowed - } else { - return false; - } - } - } - return true; - } - - function IsValidDateStampString($datestamp) { - if (strlen($datestamp) != 8) { - return false; - } - if (!$this->IsANumber($datestamp, false)) { - return false; - } - $year = substr($datestamp, 0, 4); - $month = substr($datestamp, 4, 2); - $day = substr($datestamp, 6, 2); - if (($year == 0) || ($month == 0) || ($day == 0)) { - return false; - } - if ($month > 12) { - return false; - } - if ($day > 31) { - return false; - } - if (($day > 30) && (($month == 4) || ($month == 6) || ($month == 9) || ($month == 11))) { - return false; - } - if (($day > 29) && ($month == 2)) { - return false; - } - return true; - } - - function ID3v2HeaderLength($majorversion) { - return (($majorversion == 2) ? 6 : 10); - } - -} - -?> diff --git a/getid3/getid3/module.tag.lyrics3.php b/getid3/getid3/module.tag.lyrics3.php deleted file mode 100644 index e735d6d..0000000 --- a/getid3/getid3/module.tag.lyrics3.php +++ /dev/null @@ -1,271 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -/// // -// module.tag.lyrics3.php // -// module for analyzing Lyrics3 tags // -// dependencies: module.tag.apetag.php (optional) // -// /// -///////////////////////////////////////////////////////////////// - - -class getid3_lyrics3 -{ - - function getid3_lyrics3(&$fd, &$ThisFileInfo) { - // http://www.volweb.cz/str/tags.htm - - fseek($fd, (0 - 128 - 9 - 6), SEEK_END); // end - ID3v1 - LYRICSEND - [Lyrics3size] - $lyrics3_id3v1 = fread($fd, 128 + 9 + 6); - $lyrics3lsz = substr($lyrics3_id3v1, 0, 6); // Lyrics3size - $lyrics3end = substr($lyrics3_id3v1, 6, 9); // LYRICSEND or LYRICS200 - $id3v1tag = substr($lyrics3_id3v1, 15, 128); // ID3v1 - - if ($lyrics3end == 'LYRICSEND') { - // Lyrics3v1, ID3v1, no APE - - $lyrics3size = 5100; - $lyrics3offset = $ThisFileInfo['filesize'] - 128 - $lyrics3size; - $lyrics3version = 1; - - } elseif ($lyrics3end == 'LYRICS200') { - // Lyrics3v2, ID3v1, no APE - - // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200' - $lyrics3size = $lyrics3lsz + 6 + strlen('LYRICS200'); - $lyrics3offset = $ThisFileInfo['filesize'] - 128 - $lyrics3size; - $lyrics3version = 2; - - } elseif (substr(strrev($lyrics3_id3v1), 0, 9) == strrev('LYRICSEND')) { - // Lyrics3v1, no ID3v1, no APE - - $lyrics3size = 5100; - $lyrics3offset = $ThisFileInfo['filesize'] - $lyrics3size; - $lyrics3version = 1; - $lyrics3offset = $ThisFileInfo['filesize'] - $lyrics3size; - - } elseif (substr(strrev($lyrics3_id3v1), 0, 9) == strrev('LYRICS200')) { - - // Lyrics3v2, no ID3v1, no APE - - $lyrics3size = strrev(substr(strrev($lyrics3_id3v1), 9, 6)) + 6 + strlen('LYRICS200'); // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200' - $lyrics3offset = $ThisFileInfo['filesize'] - $lyrics3size; - $lyrics3version = 2; - - } else { - - if (isset($ThisFileInfo['ape']['tag_offset_start']) && ($ThisFileInfo['ape']['tag_offset_start'] > 15)) { - - fseek($fd, $ThisFileInfo['ape']['tag_offset_start'] - 15, SEEK_SET); - $lyrics3lsz = fread($fd, 6); - $lyrics3end = fread($fd, 9); - - if ($lyrics3end == 'LYRICSEND') { - // Lyrics3v1, APE, maybe ID3v1 - - $lyrics3size = 5100; - $lyrics3offset = $ThisFileInfo['ape']['tag_offset_start'] - $lyrics3size; - $ThisFileInfo['avdataend'] = $lyrics3offset; - $lyrics3version = 1; - $ThisFileInfo['warning'][] = 'APE tag located after Lyrics3, will probably break Lyrics3 compatability'; - - } elseif ($lyrics3end == 'LYRICS200') { - // Lyrics3v2, APE, maybe ID3v1 - - $lyrics3size = $lyrics3lsz + 6 + strlen('LYRICS200'); // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200' - $lyrics3offset = $ThisFileInfo['ape']['tag_offset_start'] - $lyrics3size; - $lyrics3version = 2; - $ThisFileInfo['warning'][] = 'APE tag located after Lyrics3, will probably break Lyrics3 compatability'; - - } - - } - - } - - if (isset($lyrics3offset)) { - $ThisFileInfo['avdataend'] = $lyrics3offset; - $this->getLyrics3Data($ThisFileInfo, $fd, $lyrics3offset, $lyrics3version, $lyrics3size); - - if (!isset($ThisFileInfo['ape'])) { - $GETID3_ERRORARRAY = &$ThisFileInfo['warning']; - if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE__, false)) { - $tag = new getid3_apetag($fd, $ThisFileInfo, $ThisFileInfo['lyrics3']['tag_offset_start']); - } - } - - } - - return true; - } - - function getLyrics3Data(&$ThisFileInfo, &$fd, $endoffset, $version, $length) { - // http://www.volweb.cz/str/tags.htm - - fseek($fd, $endoffset, SEEK_SET); - if ($length <= 0) { - return false; - } - $rawdata = fread($fd, $length); - - if (substr($rawdata, 0, 11) != 'LYRICSBEGIN') { - if (strpos($rawdata, 'LYRICSBEGIN') !== false) { - - $ThisFileInfo['warning'][] = '"LYRICSBEGIN" expected at '.$endoffset.' but actually found at '.($endoffset + strpos($rawdata, 'LYRICSBEGIN')).' - this is invalid for Lyrics3 v'.$version; - $ThisFileInfo['avdataend'] = $endoffset + strpos($rawdata, 'LYRICSBEGIN'); - $ParsedLyrics3['tag_offset_start'] = $ThisFileInfo['avdataend']; - $rawdata = substr($rawdata, strpos($rawdata, 'LYRICSBEGIN')); - $length = strlen($rawdata); - - } else { - - $ThisFileInfo['error'][] = '"LYRICSBEGIN" expected at '.$endoffset.' but found "'.substr($rawdata, 0, 11).'" instead'; - return false; - - } - - } - - $ParsedLyrics3['raw']['lyrics3version'] = $version; - $ParsedLyrics3['raw']['lyrics3tagsize'] = $length; - $ParsedLyrics3['tag_offset_start'] = $endoffset; - $ParsedLyrics3['tag_offset_end'] = $endoffset + $length; - - switch ($version) { - - case 1: - if (substr($rawdata, strlen($rawdata) - 9, 9) == 'LYRICSEND') { - $ParsedLyrics3['raw']['LYR'] = trim(substr($rawdata, 11, strlen($rawdata) - 11 - 9)); - $this->Lyrics3LyricsTimestampParse($ParsedLyrics3); - } else { - $ThisFileInfo['error'][] = '"LYRICSEND" expected at '.(ftell($fd) - 11 + $length - 9).' but found "'.substr($rawdata, strlen($rawdata) - 9, 9).'" instead'; - return false; - } - break; - - case 2: - if (substr($rawdata, strlen($rawdata) - 9, 9) == 'LYRICS200') { - $ParsedLyrics3['raw']['unparsed'] = substr($rawdata, 11, strlen($rawdata) - 11 - 9 - 6); // LYRICSBEGIN + LYRICS200 + LSZ - $rawdata = $ParsedLyrics3['raw']['unparsed']; - while (strlen($rawdata) > 0) { - $fieldname = substr($rawdata, 0, 3); - $fieldsize = (int) substr($rawdata, 3, 5); - $ParsedLyrics3['raw'][$fieldname] = substr($rawdata, 8, $fieldsize); - $rawdata = substr($rawdata, 3 + 5 + $fieldsize); - } - - if (isset($ParsedLyrics3['raw']['IND'])) { - $i = 0; - $flagnames = array('lyrics', 'timestamps', 'inhibitrandom'); - foreach ($flagnames as $flagname) { - if (strlen($ParsedLyrics3['raw']['IND']) > ++$i) { - $ParsedLyrics3['flags'][$flagname] = $this->IntString2Bool(substr($ParsedLyrics3['raw']['IND'], $i, 1)); - } - } - } - - $fieldnametranslation = array('ETT'=>'title', 'EAR'=>'artist', 'EAL'=>'album', 'INF'=>'comment', 'AUT'=>'author'); - foreach ($fieldnametranslation as $key => $value) { - if (isset($ParsedLyrics3['raw'][$key])) { - $ParsedLyrics3['comments'][$value][] = trim($ParsedLyrics3['raw'][$key]); - } - } - - if (isset($ParsedLyrics3['raw']['IMG'])) { - $imagestrings = explode("\r\n", $ParsedLyrics3['raw']['IMG']); - foreach ($imagestrings as $key => $imagestring) { - if (strpos($imagestring, '||') !== false) { - $imagearray = explode('||', $imagestring); - $ParsedLyrics3['images'][$key]['filename'] = $imagearray[0]; - $ParsedLyrics3['images'][$key]['description'] = $imagearray[1]; - $ParsedLyrics3['images'][$key]['timestamp'] = $this->Lyrics3Timestamp2Seconds($imagearray[2]); - } - } - } - if (isset($ParsedLyrics3['raw']['LYR'])) { - $this->Lyrics3LyricsTimestampParse($ParsedLyrics3); - } - } else { - $ThisFileInfo['error'][] = '"LYRICS200" expected at '.(ftell($fd) - 11 + $length - 9).' but found "'.substr($rawdata, strlen($rawdata) - 9, 9).'" instead'; - return false; - } - break; - - default: - $ThisFileInfo['error'][] = 'Cannot process Lyrics3 version '.$version.' (only v1 and v2)'; - return false; - break; - } - - - if (isset($ThisFileInfo['id3v1']['tag_offset_start']) && ($ThisFileInfo['id3v1']['tag_offset_start'] < $ParsedLyrics3['tag_offset_end'])) { - $ThisFileInfo['warning'][] = 'ID3v1 tag information ignored since it appears to be a false synch in Lyrics3 tag data'; - unset($ThisFileInfo['id3v1']); - foreach ($ThisFileInfo['warning'] as $key => $value) { - if ($value == 'Some ID3v1 fields do not use NULL characters for padding') { - unset($ThisFileInfo['warning'][$key]); - sort($ThisFileInfo['warning']); - break; - } - } - } - - $ThisFileInfo['lyrics3'] = $ParsedLyrics3; - - return true; - } - - function Lyrics3Timestamp2Seconds($rawtimestamp) { - if (ereg('^\\[([0-9]{2}):([0-9]{2})\\]$', $rawtimestamp, $regs)) { - return (int) (($regs[1] * 60) + $regs[2]); - } - return false; - } - - function Lyrics3LyricsTimestampParse(&$Lyrics3data) { - $lyricsarray = explode("\r\n", $Lyrics3data['raw']['LYR']); - foreach ($lyricsarray as $key => $lyricline) { - $regs = array(); - unset($thislinetimestamps); - while (ereg('^(\\[[0-9]{2}:[0-9]{2}\\])', $lyricline, $regs)) { - $thislinetimestamps[] = $this->Lyrics3Timestamp2Seconds($regs[0]); - $lyricline = str_replace($regs[0], '', $lyricline); - } - $notimestamplyricsarray[$key] = $lyricline; - if (isset($thislinetimestamps) && is_array($thislinetimestamps)) { - sort($thislinetimestamps); - foreach ($thislinetimestamps as $timestampkey => $timestamp) { - if (isset($Lyrics3data['synchedlyrics'][$timestamp])) { - // timestamps only have a 1-second resolution, it's possible that multiple lines - // could have the same timestamp, if so, append - $Lyrics3data['synchedlyrics'][$timestamp] .= "\r\n".$lyricline; - } else { - $Lyrics3data['synchedlyrics'][$timestamp] = $lyricline; - } - } - } - } - $Lyrics3data['unsynchedlyrics'] = implode("\r\n", $notimestamplyricsarray); - if (isset($Lyrics3data['synchedlyrics']) && is_array($Lyrics3data['synchedlyrics'])) { - ksort($Lyrics3data['synchedlyrics']); - } - return true; - } - - function IntString2Bool($char) { - if ($char == '1') { - return true; - } elseif ($char == '0') { - return false; - } - return null; - } -} - - -?> \ No newline at end of file diff --git a/getid3/getid3/write.apetag.php b/getid3/getid3/write.apetag.php deleted file mode 100644 index 189160a..0000000 --- a/getid3/getid3/write.apetag.php +++ /dev/null @@ -1,228 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// write.apetag.php // -// module for writing APE tags // -// dependencies: module.tag.apetag.php // -// /// -///////////////////////////////////////////////////////////////// - - -getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE__, true); - -class getid3_write_apetag -{ - - var $filename; - var $tag_data; - var $always_preserve_replaygain = true; // ReplayGain / MP3gain tags will be copied from old tag even if not passed in data - var $warnings = array(); // any non-critical errors will be stored here - var $errors = array(); // any critical errors will be stored here - - function getid3_write_apetag() { - return true; - } - - function WriteAPEtag() { - // NOTE: All data passed to this function must be UTF-8 format - - $getID3 = new getID3; - $ThisFileInfo = $getID3->analyze($this->filename); - - if (isset($ThisFileInfo['ape']['tag_offset_start']) && isset($ThisFileInfo['lyrics3']['tag_offset_end'])) { - if ($ThisFileInfo['ape']['tag_offset_start'] >= $ThisFileInfo['lyrics3']['tag_offset_end']) { - // Current APE tag between Lyrics3 and ID3v1/EOF - // This break Lyrics3 functionality - if (!$this->DeleteAPEtag()) { - return false; - } - $ThisFileInfo = $getID3->analyze($this->filename); - } - } - - if ($this->always_preserve_replaygain) { - $ReplayGainTagsToPreserve = array('mp3gain_minmax', 'mp3gain_album_minmax', 'mp3gain_undo', 'replaygain_track_peak', 'replaygain_track_gain', 'replaygain_album_peak', 'replaygain_album_gain'); - foreach ($ReplayGainTagsToPreserve as $rg_key) { - if (isset($ThisFileInfo['ape']['items'][strtolower($rg_key)]['data'][0]) && !isset($this->tag_data[strtoupper($rg_key)][0])) { - $this->tag_data[strtoupper($rg_key)][0] = $ThisFileInfo['ape']['items'][strtolower($rg_key)]['data'][0]; - } - } - } - - if ($APEtag = $this->GenerateAPEtag()) { - if ($fp = @fopen($this->filename, 'a+b')) { - $oldignoreuserabort = ignore_user_abort(true); - flock($fp, LOCK_EX); - - $PostAPEdataOffset = $ThisFileInfo['avdataend']; - if (isset($ThisFileInfo['ape']['tag_offset_end'])) { - $PostAPEdataOffset = max($PostAPEdataOffset, $ThisFileInfo['ape']['tag_offset_end']); - } - if (isset($ThisFileInfo['lyrics3']['tag_offset_start'])) { - $PostAPEdataOffset = max($PostAPEdataOffset, $ThisFileInfo['lyrics3']['tag_offset_start']); - } - fseek($fp, $PostAPEdataOffset, SEEK_SET); - $PostAPEdata = ''; - if ($ThisFileInfo['filesize'] > $PostAPEdataOffset) { - $PostAPEdata = fread($fp, $ThisFileInfo['filesize'] - $PostAPEdataOffset); - } - - fseek($fp, $PostAPEdataOffset, SEEK_SET); - if (isset($ThisFileInfo['ape']['tag_offset_start'])) { - fseek($fp, $ThisFileInfo['ape']['tag_offset_start'], SEEK_SET); - } - ftruncate($fp, ftell($fp)); - fwrite($fp, $APEtag, strlen($APEtag)); - if (!empty($PostAPEdata)) { - fwrite($fp, $PostAPEdata, strlen($PostAPEdata)); - } - flock($fp, LOCK_UN); - fclose($fp); - ignore_user_abort($oldignoreuserabort); - return true; - - } - return false; - } - return false; - } - - function DeleteAPEtag() { - $getID3 = new getID3; - $ThisFileInfo = $getID3->analyze($this->filename); - if (isset($ThisFileInfo['ape']['tag_offset_start']) && isset($ThisFileInfo['ape']['tag_offset_end'])) { - if ($fp = @fopen($this->filename, 'a+b')) { - - flock($fp, LOCK_EX); - $oldignoreuserabort = ignore_user_abort(true); - - fseek($fp, $ThisFileInfo['ape']['tag_offset_end'], SEEK_SET); - $DataAfterAPE = ''; - if ($ThisFileInfo['filesize'] > $ThisFileInfo['ape']['tag_offset_end']) { - $DataAfterAPE = fread($fp, $ThisFileInfo['filesize'] - $ThisFileInfo['ape']['tag_offset_end']); - } - - ftruncate($fp, $ThisFileInfo['ape']['tag_offset_start']); - fseek($fp, $ThisFileInfo['ape']['tag_offset_start'], SEEK_SET); - - if (!empty($DataAfterAPE)) { - fwrite($fp, $DataAfterAPE, strlen($DataAfterAPE)); - } - - flock($fp, LOCK_UN); - fclose($fp); - ignore_user_abort($oldignoreuserabort); - - return true; - - } - return false; - } - return true; - } - - - function GenerateAPEtag() { - // NOTE: All data passed to this function must be UTF-8 format - - $items = array(); - if (!is_array($this->tag_data)) { - return false; - } - foreach ($this->tag_data as $key => $arrayofvalues) { - if (!is_array($arrayofvalues)) { - return false; - } - - $valuestring = ''; - foreach ($arrayofvalues as $value) { - $valuestring .= str_replace("\x00", '', $value)."\x00"; - } - $valuestring = rtrim($valuestring, "\x00"); - - // Length of the assigned value in bytes - $tagitem = getid3_lib::LittleEndian2String(strlen($valuestring), 4); - - //$tagitem .= $this->GenerateAPEtagFlags(true, true, false, 0, false); - $tagitem .= "\x00\x00\x00\x00"; - - $tagitem .= $this->CleanAPEtagItemKey($key)."\x00"; - $tagitem .= $valuestring; - - $items[] = $tagitem; - - } - - return $this->GenerateAPEtagHeaderFooter($items, true).implode('', $items).$this->GenerateAPEtagHeaderFooter($items, false); - } - - function GenerateAPEtagHeaderFooter(&$items, $isheader=false) { - $tagdatalength = 0; - foreach ($items as $itemdata) { - $tagdatalength += strlen($itemdata); - } - - $APEheader = 'APETAGEX'; - $APEheader .= getid3_lib::LittleEndian2String(2000, 4); - $APEheader .= getid3_lib::LittleEndian2String(32 + $tagdatalength, 4); - $APEheader .= getid3_lib::LittleEndian2String(count($items), 4); - $APEheader .= $this->GenerateAPEtagFlags(true, true, $isheader, 0, false); - $APEheader .= str_repeat("\x00", 8); - - return $APEheader; - } - - function GenerateAPEtagFlags($header=true, $footer=true, $isheader=false, $encodingid=0, $readonly=false) { - $APEtagFlags = array_fill(0, 4, 0); - if ($header) { - $APEtagFlags[0] |= 0x80; // Tag contains a header - } - if (!$footer) { - $APEtagFlags[0] |= 0x40; // Tag contains no footer - } - if ($isheader) { - $APEtagFlags[0] |= 0x20; // This is the header, not the footer - } - - // 0: Item contains text information coded in UTF-8 - // 1: Item contains binary information °) - // 2: Item is a locator of external stored information °°) - // 3: reserved - $APEtagFlags[3] |= ($encodingid << 1); - - if ($readonly) { - $APEtagFlags[3] |= 0x01; // Tag or Item is Read Only - } - - return chr($APEtagFlags[3]).chr($APEtagFlags[2]).chr($APEtagFlags[1]).chr($APEtagFlags[0]); - } - - function CleanAPEtagItemKey($itemkey) { - $itemkey = eregi_replace("[^\x20-\x7E]", '', $itemkey); - - // http://www.personal.uni-jena.de/~pfk/mpp/sv8/apekey.html - switch (strtoupper($itemkey)) { - case 'EAN/UPC': - case 'ISBN': - case 'LC': - case 'ISRC': - $itemkey = strtoupper($itemkey); - break; - - default: - $itemkey = ucwords($itemkey); - break; - } - return $itemkey; - - } - -} - -?> \ No newline at end of file diff --git a/getid3/getid3/write.id3v1.php b/getid3/getid3/write.id3v1.php deleted file mode 100644 index 2125214..0000000 --- a/getid3/getid3/write.id3v1.php +++ /dev/null @@ -1,104 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// write.id3v1.php // -// module for writing ID3v1 tags // -// dependencies: module.tag.id3v1.php // -// /// -///////////////////////////////////////////////////////////////// - -getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v1.php', __FILE__, true); - -class getid3_write_id3v1 -{ - var $filename; - var $tag_data; - var $warnings = array(); // any non-critical errors will be stored here - var $errors = array(); // any critical errors will be stored here - - function getid3_write_id3v1() { - return true; - } - - function WriteID3v1() { - // File MUST be writeable - CHMOD(646) at least - if (is_writeable($this->filename)) { - if ($fp_source = @fopen($this->filename, 'r+b')) { - - fseek($fp_source, -128, SEEK_END); - if (fread($fp_source, 3) == 'TAG') { - fseek($fp_source, -128, SEEK_END); // overwrite existing ID3v1 tag - } else { - fseek($fp_source, 0, SEEK_END); // append new ID3v1 tag - } - - $new_id3v1_tag_data = getid3_id3v1::GenerateID3v1Tag( - @$this->tag_data['title'], - @$this->tag_data['artist'], - @$this->tag_data['album'], - @$this->tag_data['year'], - @$this->tag_data['genreid'], - @$this->tag_data['comment'], - @$this->tag_data['track']); - fwrite($fp_source, $new_id3v1_tag_data, 128); - fclose($fp_source); - return true; - - } else { - $this->errors[] = 'Could not open '.$this->filename.' mode "r+b"'; - return false; - } - } - $this->errors[] = 'File is not writeable: '.$this->filename; - return false; - } - - function FixID3v1Padding() { - // ID3v1 data is supposed to be padded with NULL characters, but some taggers incorrectly use spaces - // This function rewrites the ID3v1 tag with correct padding - - // Initialize getID3 engine - $getID3 = new getID3; - $ThisFileInfo = $getID3->analyze($this->filename); - if (isset($ThisFileInfo['tags']['id3v1'])) { - foreach ($ThisFileInfo['tags']['id3v1'] as $key => $value) { - $id3v1data[$key] = implode(',', $value); - } - $this->tag_data = $id3v1data; - return $this->WriteID3v1(); - } - return false; - } - - function RemoveID3v1() { - // File MUST be writeable - CHMOD(646) at least - if (is_writeable($this->filename)) { - if ($fp_source = @fopen($this->filename, 'r+b')) { - - fseek($fp_source, -128, SEEK_END); - if (fread($fp_source, 3) == 'TAG') { - ftruncate($fp_source, filesize($this->filename) - 128); - } else { - // no ID3v1 tag to begin with - do nothing - } - fclose($fp_source); - return true; - - } else { - $this->errors[] = 'Could not open '.$this->filename.' mode "r+b"'; - } - } else { - $this->errors[] = $this->filename.' is not writeable'; - } - return false; - } - -} - -?> \ No newline at end of file diff --git a/getid3/getid3/write.id3v2.php b/getid3/getid3/write.id3v2.php deleted file mode 100644 index 045bbab..0000000 --- a/getid3/getid3/write.id3v2.php +++ /dev/null @@ -1,2038 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -/// // -// write.id3v2.php // -// module for writing ID3v2 tags // -// dependencies: module.tag.id3v2.php // -// /// -///////////////////////////////////////////////////////////////// - -getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true); - -class getid3_write_id3v2 -{ - var $filename; - var $tag_data; - var $paddedlength = 4096; // minimum length of ID3v2 tag in bytes - var $majorversion = 3; // ID3v2 major version (2, 3 (recommended), 4) - var $minorversion = 0; // ID3v2 minor version - always 0 - var $merge_existing_data = false; // if true, merge new data with existing tags; if false, delete old tag data and only write new tags - var $id3v2_default_encodingid = 0; // default text encoding (ISO-8859-1) if not explicitly passed - var $id3v2_use_unsynchronisation = false; // the specs say it should be TRUE, but most other ID3v2-aware programs are broken if unsynchronization is used, so by default don't use it. - var $warnings = array(); // any non-critical errors will be stored here - var $errors = array(); // any critical errors will be stored here - - function getid3_write_id3v2() { - return true; - } - - function WriteID3v2() { - // File MUST be writeable - CHMOD(646) at least. It's best if the - // directory is also writeable, because that method is both faster and less susceptible to errors. - - if (is_writeable($this->filename) || (!file_exists($this->filename) && is_writeable(dirname($this->filename)))) { - // Initialize getID3 engine - $getID3 = new getID3; - $OldThisFileInfo = $getID3->analyze($this->filename); - if ($this->merge_existing_data) { - // merge with existing data - if (!empty($OldThisFileInfo['id3v2'])) { - $this->tag_data = $this->array_join_merge($OldThisFileInfo['id3v2'], $this->tag_data); - } - } - $this->paddedlength = max(@$OldThisFileInfo['id3v2']['headerlength'], $this->paddedlength); - - if ($NewID3v2Tag = $this->GenerateID3v2Tag()) { - - if (file_exists($this->filename) && is_writeable($this->filename) && isset($OldThisFileInfo['id3v2']['headerlength']) && ($OldThisFileInfo['id3v2']['headerlength'] == strlen($NewID3v2Tag))) { - - // best and fastest method - insert-overwrite existing tag (padded to length of old tag if neccesary) - if (file_exists($this->filename)) { - - ob_start(); - if ($fp = fopen($this->filename, 'r+b')) { - rewind($fp); - fwrite($fp, $NewID3v2Tag, strlen($NewID3v2Tag)); - fclose($fp); - } else { - $this->errors[] = 'Could not open '.$this->filename.' mode "r+b" - '.strip_tags(ob_get_contents()); - } - ob_end_clean(); - - } else { - - ob_start(); - if ($fp = fopen($this->filename, 'wb')) { - rewind($fp); - fwrite($fp, $NewID3v2Tag, strlen($NewID3v2Tag)); - fclose($fp); - } else { - $this->errors[] = 'Could not open '.$this->filename.' mode "wb" - '.strip_tags(ob_get_contents()); - } - ob_end_clean(); - - } - - } else { - - if ($tempfilename = tempnam('*', 'getID3')) { - ob_start(); - if ($fp_source = fopen($this->filename, 'rb')) { - if ($fp_temp = fopen($tempfilename, 'wb')) { - - fwrite($fp_temp, $NewID3v2Tag, strlen($NewID3v2Tag)); - - rewind($fp_source); - if (!empty($OldThisFileInfo['avdataoffset'])) { - fseek($fp_source, $OldThisFileInfo['avdataoffset'], SEEK_SET); - } - - while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) { - fwrite($fp_temp, $buffer, strlen($buffer)); - } - - fclose($fp_temp); - fclose($fp_source); - copy($tempfilename, $this->filename); - unlink($tempfilename); - ob_end_clean(); - return true; - - } else { - - $this->errors[] = 'Could not open '.$tempfilename.' mode "wb" - '.strip_tags(ob_get_contents()); - - } - fclose($fp_source); - - } else { - - $this->errors[] = 'Could not open '.$this->filename.' mode "rb" - '.strip_tags(ob_get_contents()); - - } - ob_end_clean(); - } - return false; - - } - - } else { - - $this->errors[] = '$this->GenerateID3v2Tag() failed'; - - } - - if (!empty($this->errors)) { - return false; - } - return true; - } else { - $this->errors[] = '!is_writeable('.$this->filename.')'; - } - return false; - } - - function RemoveID3v2() { - - // File MUST be writeable - CHMOD(646) at least. It's best if the - // directory is also writeable, because that method is both faster and less susceptible to errors. - if (is_writeable(dirname($this->filename))) { - - // preferred method - only one copying operation, minimal chance of corrupting - // original file if script is interrupted, but required directory to be writeable - if ($fp_source = @fopen($this->filename, 'rb')) { - // Initialize getID3 engine - $getID3 = new getID3; - $OldThisFileInfo = $getID3->analyze($this->filename); - rewind($fp_source); - if ($OldThisFileInfo['avdataoffset'] !== false) { - fseek($fp_source, $OldThisFileInfo['avdataoffset'], SEEK_SET); - } - if ($fp_temp = @fopen($this->filename.'getid3tmp', 'w+b')) { - while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) { - fwrite($fp_temp, $buffer, strlen($buffer)); - } - fclose($fp_temp); - } else { - $this->errors[] = 'Could not open '.$this->filename.'getid3tmp mode "w+b"'; - } - fclose($fp_source); - } else { - $this->errors[] = 'Could not open '.$this->filename.' mode "rb"'; - } - if (file_exists($this->filename)) { - unlink($this->filename); - } - rename($this->filename.'getid3tmp', $this->filename); - - } elseif (is_writable($this->filename)) { - - // less desirable alternate method - double-copies the file, overwrites original file - // and could corrupt source file if the script is interrupted or an error occurs. - if ($fp_source = @fopen($this->filename, 'rb')) { - // Initialize getID3 engine - $getID3 = new getID3; - $OldThisFileInfo = $getID3->analyze($this->filename); - rewind($fp_source); - if ($OldThisFileInfo['avdataoffset'] !== false) { - fseek($fp_source, $OldThisFileInfo['avdataoffset'], SEEK_SET); - } - if ($fp_temp = tmpfile()) { - while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) { - fwrite($fp_temp, $buffer, strlen($buffer)); - } - fclose($fp_source); - if ($fp_source = @fopen($this->filename, 'wb')) { - rewind($fp_temp); - while ($buffer = fread($fp_temp, GETID3_FREAD_BUFFER_SIZE)) { - fwrite($fp_source, $buffer, strlen($buffer)); - } - fseek($fp_temp, -128, SEEK_END); - fclose($fp_source); - } else { - $this->errors[] = 'Could not open '.$this->filename.' mode "wb"'; - } - fclose($fp_temp); - } else { - $this->errors[] = 'Could not create tmpfile()'; - } - } else { - $this->errors[] = 'Could not open '.$this->filename.' mode "rb"'; - } - - } else { - - $this->errors[] = 'Directory and file both not writeable'; - - } - - if (!empty($this->errors)) { - return false; - } - return true; - } - - - function GenerateID3v2TagFlags($flags) { - switch ($this->majorversion) { - case 4: - // %abcd0000 - $flag = (@$flags['unsynchronisation'] ? '1' : '0'); // a - Unsynchronisation - $flag .= (@$flags['extendedheader'] ? '1' : '0'); // b - Extended header - $flag .= (@$flags['experimental'] ? '1' : '0'); // c - Experimental indicator - $flag .= (@$flags['footer'] ? '1' : '0'); // d - Footer present - $flag .= '0000'; - break; - - case 3: - // %abc00000 - $flag = (@$flags['unsynchronisation'] ? '1' : '0'); // a - Unsynchronisation - $flag .= (@$flags['extendedheader'] ? '1' : '0'); // b - Extended header - $flag .= (@$flags['experimental'] ? '1' : '0'); // c - Experimental indicator - $flag .= '00000'; - break; - - case 2: - // %ab000000 - $flag = (@$flags['unsynchronisation'] ? '1' : '0'); // a - Unsynchronisation - $flag .= (@$flags['compression'] ? '1' : '0'); // b - Compression - $flag .= '000000'; - break; - - default: - return false; - break; - } - return chr(bindec($flag)); - } - - - function GenerateID3v2FrameFlags($TagAlter=false, $FileAlter=false, $ReadOnly=false, $Compression=false, $Encryption=false, $GroupingIdentity=false, $Unsynchronisation=false, $DataLengthIndicator=false) { - switch ($this->majorversion) { - case 4: - // %0abc0000 %0h00kmnp - $flag1 = '0'; - $flag1 .= $TagAlter ? '1' : '0'; // a - Tag alter preservation (true == discard) - $flag1 .= $FileAlter ? '1' : '0'; // b - File alter preservation (true == discard) - $flag1 .= $ReadOnly ? '1' : '0'; // c - Read only (true == read only) - $flag1 .= '0000'; - - $flag2 = '0'; - $flag2 .= $GroupingIdentity ? '1' : '0'; // h - Grouping identity (true == contains group information) - $flag2 .= '00'; - $flag2 .= $Compression ? '1' : '0'; // k - Compression (true == compressed) - $flag2 .= $Encryption ? '1' : '0'; // m - Encryption (true == encrypted) - $flag2 .= $Unsynchronisation ? '1' : '0'; // n - Unsynchronisation (true == unsynchronised) - $flag2 .= $DataLengthIndicator ? '1' : '0'; // p - Data length indicator (true == data length indicator added) - break; - - case 3: - // %abc00000 %ijk00000 - $flag1 = $TagAlter ? '1' : '0'; // a - Tag alter preservation (true == discard) - $flag1 .= $FileAlter ? '1' : '0'; // b - File alter preservation (true == discard) - $flag1 .= $ReadOnly ? '1' : '0'; // c - Read only (true == read only) - $flag1 .= '00000'; - - $flag2 = $Compression ? '1' : '0'; // i - Compression (true == compressed) - $flag2 .= $Encryption ? '1' : '0'; // j - Encryption (true == encrypted) - $flag2 .= $GroupingIdentity ? '1' : '0'; // k - Grouping identity (true == contains group information) - $flag2 .= '00000'; - break; - - default: - return false; - break; - - } - return chr(bindec($flag1)).chr(bindec($flag2)); - } - - function GenerateID3v2FrameData($frame_name, $source_data_array) { - if (!getid3_id3v2::IsValidID3v2FrameName($frame_name, $this->majorversion)) { - return false; - } - $framedata = ''; - - if (($this->majorversion < 3) || ($this->majorversion > 4)) { - - $this->errors[] = 'Only ID3v2.3 and ID3v2.4 are supported in GenerateID3v2FrameData()'; - - } else { // $this->majorversion 3 or 4 - - switch ($frame_name) { - case 'UFID': - // 4.1 UFID Unique file identifier - // Owner identifier $00 - // Identifier - if (strlen($source_data_array['data']) > 64) { - $this->errors[] = 'Identifier not allowed to be longer than 64 bytes in '.$frame_name.' (supplied data was '.strlen($source_data_array['data']).' bytes long)'; - } else { - $framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00"; - $framedata .= substr($source_data_array['data'], 0, 64); // max 64 bytes - truncate anything longer - } - break; - - case 'TXXX': - // 4.2.2 TXXX User defined text information frame - // Text encoding $xx - // Description $00 (00) - // Value - $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid); - if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'], $this->majorversion)) { - $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion; - } else { - $framedata .= chr($source_data_array['encodingid']); - $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']); - $framedata .= $source_data_array['data']; - } - break; - - case 'WXXX': - // 4.3.2 WXXX User defined URL link frame - // Text encoding $xx - // Description $00 (00) - // URL - $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid); - if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'], $this->majorversion)) { - $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion; - } elseif (!isset($source_data_array['data']) || !$this->IsValidURL($source_data_array['data'], false, false)) { - //$this->errors[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')'; - // probably should be an error, need to rewrite IsValidURL() to handle other encodings - $this->warnings[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')'; - } else { - $framedata .= chr($source_data_array['encodingid']); - $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']); - $framedata .= $source_data_array['data']; - } - break; - - case 'IPLS': - // 4.4 IPLS Involved people list (ID3v2.3 only) - // Text encoding $xx - // People list strings - $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid); - if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'], $this->majorversion)) { - $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion; - } else { - $framedata .= chr($source_data_array['encodingid']); - $framedata .= $source_data_array['data']; - } - break; - - case 'MCDI': - // 4.4 MCDI Music CD identifier - // CD TOC - $framedata .= $source_data_array['data']; - break; - - case 'ETCO': - // 4.5 ETCO Event timing codes - // Time stamp format $xx - // Where time stamp format is: - // $01 (32-bit value) MPEG frames from beginning of file - // $02 (32-bit value) milliseconds from beginning of file - // Followed by a list of key events in the following format: - // Type of event $xx - // Time stamp $xx (xx ...) - // The 'Time stamp' is set to zero if directly at the beginning of the sound - // or after the previous event. All events MUST be sorted in chronological order. - if (($source_data_array['timestampformat'] > 2) || ($source_data_array['timestampformat'] < 1)) { - $this->errors[] = 'Invalid Time Stamp Format byte in '.$frame_name.' ('.$source_data_array['timestampformat'].')'; - } else { - $framedata .= chr($source_data_array['timestampformat']); - foreach ($source_data_array as $key => $val) { - if (!$this->ID3v2IsValidETCOevent($val['typeid'])) { - $this->errors[] = 'Invalid Event Type byte in '.$frame_name.' ('.$val['typeid'].')'; - } elseif (($key != 'timestampformat') && ($key != 'flags')) { - if (($val['timestamp'] > 0) && ($previousETCOtimestamp >= $val['timestamp'])) { - // The 'Time stamp' is set to zero if directly at the beginning of the sound - // or after the previous event. All events MUST be sorted in chronological order. - $this->errors[] = 'Out-of-order timestamp in '.$frame_name.' ('.$val['timestamp'].') for Event Type ('.$val['typeid'].')'; - } else { - $framedata .= chr($val['typeid']); - $framedata .= getid3_lib::BigEndian2String($val['timestamp'], 4, false); - } - } - } - } - break; - - case 'MLLT': - // 4.6 MLLT MPEG location lookup table - // MPEG frames between reference $xx xx - // Bytes between reference $xx xx xx - // Milliseconds between reference $xx xx xx - // Bits for bytes deviation $xx - // Bits for milliseconds dev. $xx - // Then for every reference the following data is included; - // Deviation in bytes %xxx.... - // Deviation in milliseconds %xxx.... - if (($source_data_array['framesbetweenreferences'] > 0) && ($source_data_array['framesbetweenreferences'] <= 65535)) { - $framedata .= getid3_lib::BigEndian2String($source_data_array['framesbetweenreferences'], 2, false); - } else { - $this->errors[] = 'Invalid MPEG Frames Between References in '.$frame_name.' ('.$source_data_array['framesbetweenreferences'].')'; - } - if (($source_data_array['bytesbetweenreferences'] > 0) && ($source_data_array['bytesbetweenreferences'] <= 16777215)) { - $framedata .= getid3_lib::BigEndian2String($source_data_array['bytesbetweenreferences'], 3, false); - } else { - $this->errors[] = 'Invalid bytes Between References in '.$frame_name.' ('.$source_data_array['bytesbetweenreferences'].')'; - } - if (($source_data_array['msbetweenreferences'] > 0) && ($source_data_array['msbetweenreferences'] <= 16777215)) { - $framedata .= getid3_lib::BigEndian2String($source_data_array['msbetweenreferences'], 3, false); - } else { - $this->errors[] = 'Invalid Milliseconds Between References in '.$frame_name.' ('.$source_data_array['msbetweenreferences'].')'; - } - if (!$this->IsWithinBitRange($source_data_array['bitsforbytesdeviation'], 8, false)) { - if (($source_data_array['bitsforbytesdeviation'] % 4) == 0) { - $framedata .= chr($source_data_array['bitsforbytesdeviation']); - } else { - $this->errors[] = 'Bits For Bytes Deviation in '.$frame_name.' ('.$source_data_array['bitsforbytesdeviation'].') must be a multiple of 4.'; - } - } else { - $this->errors[] = 'Invalid Bits For Bytes Deviation in '.$frame_name.' ('.$source_data_array['bitsforbytesdeviation'].')'; - } - if (!$this->IsWithinBitRange($source_data_array['bitsformsdeviation'], 8, false)) { - if (($source_data_array['bitsformsdeviation'] % 4) == 0) { - $framedata .= chr($source_data_array['bitsformsdeviation']); - } else { - $this->errors[] = 'Bits For Milliseconds Deviation in '.$frame_name.' ('.$source_data_array['bitsforbytesdeviation'].') must be a multiple of 4.'; - } - } else { - $this->errors[] = 'Invalid Bits For Milliseconds Deviation in '.$frame_name.' ('.$source_data_array['bitsformsdeviation'].')'; - } - foreach ($source_data_array as $key => $val) { - if (($key != 'framesbetweenreferences') && ($key != 'bytesbetweenreferences') && ($key != 'msbetweenreferences') && ($key != 'bitsforbytesdeviation') && ($key != 'bitsformsdeviation') && ($key != 'flags')) { - $unwrittenbitstream .= str_pad(getid3_lib::Dec2Bin($val['bytedeviation']), $source_data_array['bitsforbytesdeviation'], '0', STR_PAD_LEFT); - $unwrittenbitstream .= str_pad(getid3_lib::Dec2Bin($val['msdeviation']), $source_data_array['bitsformsdeviation'], '0', STR_PAD_LEFT); - } - } - for ($i = 0; $i < strlen($unwrittenbitstream); $i += 8) { - $highnibble = bindec(substr($unwrittenbitstream, $i, 4)) << 4; - $lownibble = bindec(substr($unwrittenbitstream, $i + 4, 4)); - $framedata .= chr($highnibble & $lownibble); - } - break; - - case 'SYTC': - // 4.7 SYTC Synchronised tempo codes - // Time stamp format $xx - // Tempo data - // Where time stamp format is: - // $01 (32-bit value) MPEG frames from beginning of file - // $02 (32-bit value) milliseconds from beginning of file - if (($source_data_array['timestampformat'] > 2) || ($source_data_array['timestampformat'] < 1)) { - $this->errors[] = 'Invalid Time Stamp Format byte in '.$frame_name.' ('.$source_data_array['timestampformat'].')'; - } else { - $framedata .= chr($source_data_array['timestampformat']); - foreach ($source_data_array as $key => $val) { - if (!$this->ID3v2IsValidETCOevent($val['typeid'])) { - $this->errors[] = 'Invalid Event Type byte in '.$frame_name.' ('.$val['typeid'].')'; - } elseif (($key != 'timestampformat') && ($key != 'flags')) { - if (($val['tempo'] < 0) || ($val['tempo'] > 510)) { - $this->errors[] = 'Invalid Tempo (max = 510) in '.$frame_name.' ('.$val['tempo'].') at timestamp ('.$val['timestamp'].')'; - } else { - if ($val['tempo'] > 255) { - $framedata .= chr(255); - $val['tempo'] -= 255; - } - $framedata .= chr($val['tempo']); - $framedata .= getid3_lib::BigEndian2String($val['timestamp'], 4, false); - } - } - } - } - break; - - case 'USLT': - // 4.8 USLT Unsynchronised lyric/text transcription - // Text encoding $xx - // Language $xx xx xx - // Content descriptor $00 (00) - // Lyrics/text - $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid); - if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) { - $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion; - } elseif (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') { - $this->errors[] = 'Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')'; - } else { - $framedata .= chr($source_data_array['encodingid']); - $framedata .= strtolower($source_data_array['language']); - $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']); - $framedata .= $source_data_array['data']; - } - break; - - case 'SYLT': - // 4.9 SYLT Synchronised lyric/text - // Text encoding $xx - // Language $xx xx xx - // Time stamp format $xx - // $01 (32-bit value) MPEG frames from beginning of file - // $02 (32-bit value) milliseconds from beginning of file - // Content type $xx - // Content descriptor $00 (00) - // Terminated text to be synced (typically a syllable) - // Sync identifier (terminator to above string) $00 (00) - // Time stamp $xx (xx ...) - $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid); - if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) { - $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion; - } elseif (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') { - $this->errors[] = 'Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')'; - } elseif (($source_data_array['timestampformat'] > 2) || ($source_data_array['timestampformat'] < 1)) { - $this->errors[] = 'Invalid Time Stamp Format byte in '.$frame_name.' ('.$source_data_array['timestampformat'].')'; - } elseif (!$this->ID3v2IsValidSYLTtype($source_data_array['contenttypeid'])) { - $this->errors[] = 'Invalid Content Type byte in '.$frame_name.' ('.$source_data_array['contenttypeid'].')'; - } elseif (!is_array($source_data_array['data'])) { - $this->errors[] = 'Invalid Lyric/Timestamp data in '.$frame_name.' (must be an array)'; - } else { - $framedata .= chr($source_data_array['encodingid']); - $framedata .= strtolower($source_data_array['language']); - $framedata .= chr($source_data_array['timestampformat']); - $framedata .= chr($source_data_array['contenttypeid']); - $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']); - ksort($source_data_array['data']); - foreach ($source_data_array['data'] as $key => $val) { - $framedata .= $val['data'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']); - $framedata .= getid3_lib::BigEndian2String($val['timestamp'], 4, false); - } - } - break; - - case 'COMM': - // 4.10 COMM Comments - // Text encoding $xx - // Language $xx xx xx - // Short content descrip. $00 (00) - // The actual text - $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid); - if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) { - $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion; - } elseif (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') { - $this->errors[] = 'Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')'; - } else { - $framedata .= chr($source_data_array['encodingid']); - $framedata .= strtolower($source_data_array['language']); - $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']); - $framedata .= $source_data_array['data']; - } - break; - - case 'RVA2': - // 4.11 RVA2 Relative volume adjustment (2) (ID3v2.4+ only) - // Identification $00 - // The 'identification' string is used to identify the situation and/or - // device where this adjustment should apply. The following is then - // repeated for every channel: - // Type of channel $xx - // Volume adjustment $xx xx - // Bits representing peak $xx - // Peak volume $xx (xx ...) - $framedata .= str_replace("\x00", '', $source_data_array['description'])."\x00"; - foreach ($source_data_array as $key => $val) { - if ($key != 'description') { - $framedata .= chr($val['channeltypeid']); - $framedata .= getid3_lib::BigEndian2String($val['volumeadjust'], 2, false, true); // signed 16-bit - if (!$this->IsWithinBitRange($source_data_array['bitspeakvolume'], 8, false)) { - $framedata .= chr($val['bitspeakvolume']); - if ($val['bitspeakvolume'] > 0) { - $framedata .= getid3_lib::BigEndian2String($val['peakvolume'], ceil($val['bitspeakvolume'] / 8), false, false); - } - } else { - $this->errors[] = 'Invalid Bits Representing Peak Volume in '.$frame_name.' ('.$val['bitspeakvolume'].') (range = 0 to 255)'; - } - } - } - break; - - case 'RVAD': - // 4.12 RVAD Relative volume adjustment (ID3v2.3 only) - // Increment/decrement %00fedcba - // Bits used for volume descr. $xx - // Relative volume change, right $xx xx (xx ...) // a - // Relative volume change, left $xx xx (xx ...) // b - // Peak volume right $xx xx (xx ...) - // Peak volume left $xx xx (xx ...) - // Relative volume change, right back $xx xx (xx ...) // c - // Relative volume change, left back $xx xx (xx ...) // d - // Peak volume right back $xx xx (xx ...) - // Peak volume left back $xx xx (xx ...) - // Relative volume change, center $xx xx (xx ...) // e - // Peak volume center $xx xx (xx ...) - // Relative volume change, bass $xx xx (xx ...) // f - // Peak volume bass $xx xx (xx ...) - if (!$this->IsWithinBitRange($source_data_array['bitsvolume'], 8, false)) { - $this->errors[] = 'Invalid Bits For Volume Description byte in '.$frame_name.' ('.$source_data_array['bitsvolume'].') (range = 1 to 255)'; - } else { - $incdecflag .= '00'; - $incdecflag .= $source_data_array['incdec']['right'] ? '1' : '0'; // a - Relative volume change, right - $incdecflag .= $source_data_array['incdec']['left'] ? '1' : '0'; // b - Relative volume change, left - $incdecflag .= $source_data_array['incdec']['rightrear'] ? '1' : '0'; // c - Relative volume change, right back - $incdecflag .= $source_data_array['incdec']['leftrear'] ? '1' : '0'; // d - Relative volume change, left back - $incdecflag .= $source_data_array['incdec']['center'] ? '1' : '0'; // e - Relative volume change, center - $incdecflag .= $source_data_array['incdec']['bass'] ? '1' : '0'; // f - Relative volume change, bass - $framedata .= chr(bindec($incdecflag)); - $framedata .= chr($source_data_array['bitsvolume']); - $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['right'], ceil($source_data_array['bitsvolume'] / 8), false); - $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['left'], ceil($source_data_array['bitsvolume'] / 8), false); - $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['right'], ceil($source_data_array['bitsvolume'] / 8), false); - $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['left'], ceil($source_data_array['bitsvolume'] / 8), false); - if ($source_data_array['volumechange']['rightrear'] || $source_data_array['volumechange']['leftrear'] || - $source_data_array['peakvolume']['rightrear'] || $source_data_array['peakvolume']['leftrear'] || - $source_data_array['volumechange']['center'] || $source_data_array['peakvolume']['center'] || - $source_data_array['volumechange']['bass'] || $source_data_array['peakvolume']['bass']) { - $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['rightrear'], ceil($source_data_array['bitsvolume']/8), false); - $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['leftrear'], ceil($source_data_array['bitsvolume']/8), false); - $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['rightrear'], ceil($source_data_array['bitsvolume']/8), false); - $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['leftrear'], ceil($source_data_array['bitsvolume']/8), false); - } - if ($source_data_array['volumechange']['center'] || $source_data_array['peakvolume']['center'] || - $source_data_array['volumechange']['bass'] || $source_data_array['peakvolume']['bass']) { - $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['center'], ceil($source_data_array['bitsvolume']/8), false); - $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['center'], ceil($source_data_array['bitsvolume']/8), false); - } - if ($source_data_array['volumechange']['bass'] || $source_data_array['peakvolume']['bass']) { - $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['bass'], ceil($source_data_array['bitsvolume']/8), false); - $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['bass'], ceil($source_data_array['bitsvolume']/8), false); - } - } - break; - - case 'EQU2': - // 4.12 EQU2 Equalisation (2) (ID3v2.4+ only) - // Interpolation method $xx - // $00 Band - // $01 Linear - // Identification $00 - // The following is then repeated for every adjustment point - // Frequency $xx xx - // Volume adjustment $xx xx - if (($source_data_array['interpolationmethod'] < 0) || ($source_data_array['interpolationmethod'] > 1)) { - $this->errors[] = 'Invalid Interpolation Method byte in '.$frame_name.' ('.$source_data_array['interpolationmethod'].') (valid = 0 or 1)'; - } else { - $framedata .= chr($source_data_array['interpolationmethod']); - $framedata .= str_replace("\x00", '', $source_data_array['description'])."\x00"; - foreach ($source_data_array['data'] as $key => $val) { - $framedata .= getid3_lib::BigEndian2String(intval(round($key * 2)), 2, false); - $framedata .= getid3_lib::BigEndian2String($val, 2, false, true); // signed 16-bit - } - } - break; - - case 'EQUA': - // 4.12 EQUA Equalisation (ID3v2.3 only) - // Adjustment bits $xx - // This is followed by 2 bytes + ('adjustment bits' rounded up to the - // nearest byte) for every equalisation band in the following format, - // giving a frequency range of 0 - 32767Hz: - // Increment/decrement %x (MSB of the Frequency) - // Frequency (lower 15 bits) - // Adjustment $xx (xx ...) - if (!$this->IsWithinBitRange($source_data_array['bitsvolume'], 8, false)) { - $this->errors[] = 'Invalid Adjustment Bits byte in '.$frame_name.' ('.$source_data_array['bitsvolume'].') (range = 1 to 255)'; - } else { - $framedata .= chr($source_data_array['adjustmentbits']); - foreach ($source_data_array as $key => $val) { - if ($key != 'bitsvolume') { - if (($key > 32767) || ($key < 0)) { - $this->errors[] = 'Invalid Frequency in '.$frame_name.' ('.$key.') (range = 0 to 32767)'; - } else { - if ($val >= 0) { - // put MSB of frequency to 1 if increment, 0 if decrement - $key |= 0x8000; - } - $framedata .= getid3_lib::BigEndian2String($key, 2, false); - $framedata .= getid3_lib::BigEndian2String($val, ceil($source_data_array['adjustmentbits'] / 8), false); - } - } - } - } - break; - - case 'RVRB': - // 4.13 RVRB Reverb - // Reverb left (ms) $xx xx - // Reverb right (ms) $xx xx - // Reverb bounces, left $xx - // Reverb bounces, right $xx - // Reverb feedback, left to left $xx - // Reverb feedback, left to right $xx - // Reverb feedback, right to right $xx - // Reverb feedback, right to left $xx - // Premix left to right $xx - // Premix right to left $xx - if (!$this->IsWithinBitRange($source_data_array['left'], 16, false)) { - $this->errors[] = 'Invalid Reverb Left in '.$frame_name.' ('.$source_data_array['left'].') (range = 0 to 65535)'; - } elseif (!$this->IsWithinBitRange($source_data_array['right'], 16, false)) { - $this->errors[] = 'Invalid Reverb Left in '.$frame_name.' ('.$source_data_array['right'].') (range = 0 to 65535)'; - } elseif (!$this->IsWithinBitRange($source_data_array['bouncesL'], 8, false)) { - $this->errors[] = 'Invalid Reverb Bounces, Left in '.$frame_name.' ('.$source_data_array['bouncesL'].') (range = 0 to 255)'; - } elseif (!$this->IsWithinBitRange($source_data_array['bouncesR'], 8, false)) { - $this->errors[] = 'Invalid Reverb Bounces, Right in '.$frame_name.' ('.$source_data_array['bouncesR'].') (range = 0 to 255)'; - } elseif (!$this->IsWithinBitRange($source_data_array['feedbackLL'], 8, false)) { - $this->errors[] = 'Invalid Reverb Feedback, Left-To-Left in '.$frame_name.' ('.$source_data_array['feedbackLL'].') (range = 0 to 255)'; - } elseif (!$this->IsWithinBitRange($source_data_array['feedbackLR'], 8, false)) { - $this->errors[] = 'Invalid Reverb Feedback, Left-To-Right in '.$frame_name.' ('.$source_data_array['feedbackLR'].') (range = 0 to 255)'; - } elseif (!$this->IsWithinBitRange($source_data_array['feedbackRR'], 8, false)) { - $this->errors[] = 'Invalid Reverb Feedback, Right-To-Right in '.$frame_name.' ('.$source_data_array['feedbackRR'].') (range = 0 to 255)'; - } elseif (!$this->IsWithinBitRange($source_data_array['feedbackRL'], 8, false)) { - $this->errors[] = 'Invalid Reverb Feedback, Right-To-Left in '.$frame_name.' ('.$source_data_array['feedbackRL'].') (range = 0 to 255)'; - } elseif (!$this->IsWithinBitRange($source_data_array['premixLR'], 8, false)) { - $this->errors[] = 'Invalid Premix, Left-To-Right in '.$frame_name.' ('.$source_data_array['premixLR'].') (range = 0 to 255)'; - } elseif (!$this->IsWithinBitRange($source_data_array['premixRL'], 8, false)) { - $this->errors[] = 'Invalid Premix, Right-To-Left in '.$frame_name.' ('.$source_data_array['premixRL'].') (range = 0 to 255)'; - } else { - $framedata .= getid3_lib::BigEndian2String($source_data_array['left'], 2, false); - $framedata .= getid3_lib::BigEndian2String($source_data_array['right'], 2, false); - $framedata .= chr($source_data_array['bouncesL']); - $framedata .= chr($source_data_array['bouncesR']); - $framedata .= chr($source_data_array['feedbackLL']); - $framedata .= chr($source_data_array['feedbackLR']); - $framedata .= chr($source_data_array['feedbackRR']); - $framedata .= chr($source_data_array['feedbackRL']); - $framedata .= chr($source_data_array['premixLR']); - $framedata .= chr($source_data_array['premixRL']); - } - break; - - case 'APIC': - // 4.14 APIC Attached picture - // Text encoding $xx - // MIME type $00 - // Picture type $xx - // Description $00 (00) - // Picture data - $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid); - if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) { - $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion; - } elseif (!$this->ID3v2IsValidAPICpicturetype($source_data_array['picturetypeid'])) { - $this->errors[] = 'Invalid Picture Type byte in '.$frame_name.' ('.$source_data_array['picturetypeid'].') for ID3v2.'.$this->majorversion; - } elseif (($this->majorversion >= 3) && (!$this->ID3v2IsValidAPICimageformat($source_data_array['mime']))) { - $this->errors[] = 'Invalid MIME Type in '.$frame_name.' ('.$source_data_array['mime'].') for ID3v2.'.$this->majorversion; - } elseif (($source_data_array['mime'] == '-->') && (!$this->IsValidURL($source_data_array['data'], false, false))) { - //$this->errors[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')'; - // probably should be an error, need to rewrite IsValidURL() to handle other encodings - $this->warnings[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')'; - } else { - $framedata .= chr($source_data_array['encodingid']); - $framedata .= str_replace("\x00", '', $source_data_array['mime'])."\x00"; - $framedata .= chr($source_data_array['picturetypeid']); - $framedata .= @$source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']); - $framedata .= $source_data_array['data']; - } - break; - - case 'GEOB': - // 4.15 GEOB General encapsulated object - // Text encoding $xx - // MIME type $00 - // Filename $00 (00) - // Content description $00 (00) - // Encapsulated object - $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid); - if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) { - $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion; - } elseif (!$this->IsValidMIMEstring($source_data_array['mime'])) { - $this->errors[] = 'Invalid MIME Type in '.$frame_name.' ('.$source_data_array['mime'].')'; - } elseif (!$source_data_array['description']) { - $this->errors[] = 'Missing Description in '.$frame_name; - } else { - $framedata .= chr($source_data_array['encodingid']); - $framedata .= str_replace("\x00", '', $source_data_array['mime'])."\x00"; - $framedata .= $source_data_array['filename'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']); - $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']); - $framedata .= $source_data_array['data']; - } - break; - - case 'PCNT': - // 4.16 PCNT Play counter - // When the counter reaches all one's, one byte is inserted in - // front of the counter thus making the counter eight bits bigger - // Counter $xx xx xx xx (xx ...) - $framedata .= getid3_lib::BigEndian2String($source_data_array['data'], 4, false); - break; - - case 'POPM': - // 4.17 POPM Popularimeter - // When the counter reaches all one's, one byte is inserted in - // front of the counter thus making the counter eight bits bigger - // Email to user $00 - // Rating $xx - // Counter $xx xx xx xx (xx ...) - if (!$this->IsWithinBitRange($source_data_array['rating'], 8, false)) { - $this->errors[] = 'Invalid Rating byte in '.$frame_name.' ('.$source_data_array['rating'].') (range = 0 to 255)'; - } elseif (!IsValidEmail($source_data_array['email'])) { - $this->errors[] = 'Invalid Email in '.$frame_name.' ('.$source_data_array['email'].')'; - } else { - $framedata .= str_replace("\x00", '', $source_data_array['email'])."\x00"; - $framedata .= chr($source_data_array['rating']); - $framedata .= getid3_lib::BigEndian2String($source_data_array['data'], 4, false); - } - break; - - case 'RBUF': - // 4.18 RBUF Recommended buffer size - // Buffer size $xx xx xx - // Embedded info flag %0000000x - // Offset to next tag $xx xx xx xx - if (!$this->IsWithinBitRange($source_data_array['buffersize'], 24, false)) { - $this->errors[] = 'Invalid Buffer Size in '.$frame_name; - } elseif (!$this->IsWithinBitRange($source_data_array['nexttagoffset'], 32, false)) { - $this->errors[] = 'Invalid Offset To Next Tag in '.$frame_name; - } else { - $framedata .= getid3_lib::BigEndian2String($source_data_array['buffersize'], 3, false); - $flag .= '0000000'; - $flag .= $source_data_array['flags']['embededinfo'] ? '1' : '0'; - $framedata .= chr(bindec($flag)); - $framedata .= getid3_lib::BigEndian2String($source_data_array['nexttagoffset'], 4, false); - } - break; - - case 'AENC': - // 4.19 AENC Audio encryption - // Owner identifier $00 - // Preview start $xx xx - // Preview length $xx xx - // Encryption info - if (!$this->IsWithinBitRange($source_data_array['previewstart'], 16, false)) { - $this->errors[] = 'Invalid Preview Start in '.$frame_name.' ('.$source_data_array['previewstart'].')'; - } elseif (!$this->IsWithinBitRange($source_data_array['previewlength'], 16, false)) { - $this->errors[] = 'Invalid Preview Length in '.$frame_name.' ('.$source_data_array['previewlength'].')'; - } else { - $framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00"; - $framedata .= getid3_lib::BigEndian2String($source_data_array['previewstart'], 2, false); - $framedata .= getid3_lib::BigEndian2String($source_data_array['previewlength'], 2, false); - $framedata .= $source_data_array['encryptioninfo']; - } - break; - - case 'LINK': - // 4.20 LINK Linked information - // Frame identifier $xx xx xx xx - // URL $00 - // ID and additional data - if (!getid3_id3v2::IsValidID3v2FrameName($source_data_array['frameid'], $this->majorversion)) { - $this->errors[] = 'Invalid Frame Identifier in '.$frame_name.' ('.$source_data_array['frameid'].')'; - } elseif (!$this->IsValidURL($source_data_array['data'], true, false)) { - //$this->errors[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')'; - // probably should be an error, need to rewrite IsValidURL() to handle other encodings - $this->warnings[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')'; - } elseif ((($source_data_array['frameid'] == 'AENC') || ($source_data_array['frameid'] == 'APIC') || ($source_data_array['frameid'] == 'GEOB') || ($source_data_array['frameid'] == 'TXXX')) && ($source_data_array['additionaldata'] == '')) { - $this->errors[] = 'Content Descriptor must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name; - } elseif (($source_data_array['frameid'] == 'USER') && (getid3_id3v2::LanguageLookup($source_data_array['additionaldata'], true) == '')) { - $this->errors[] = 'Language must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name; - } elseif (($source_data_array['frameid'] == 'PRIV') && ($source_data_array['additionaldata'] == '')) { - $this->errors[] = 'Owner Identifier must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name; - } elseif ((($source_data_array['frameid'] == 'COMM') || ($source_data_array['frameid'] == 'SYLT') || ($source_data_array['frameid'] == 'USLT')) && ((getid3_id3v2::LanguageLookup(substr($source_data_array['additionaldata'], 0, 3), true) == '') || (substr($source_data_array['additionaldata'], 3) == ''))) { - $this->errors[] = 'Language followed by Content Descriptor must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name; - } else { - $framedata .= $source_data_array['frameid']; - $framedata .= str_replace("\x00", '', $source_data_array['data'])."\x00"; - switch ($source_data_array['frameid']) { - case 'COMM': - case 'SYLT': - case 'USLT': - case 'PRIV': - case 'USER': - case 'AENC': - case 'APIC': - case 'GEOB': - case 'TXXX': - $framedata .= $source_data_array['additionaldata']; - break; - case 'ASPI': - case 'ETCO': - case 'EQU2': - case 'MCID': - case 'MLLT': - case 'OWNE': - case 'RVA2': - case 'RVRB': - case 'SYTC': - case 'IPLS': - case 'RVAD': - case 'EQUA': - // no additional data required - break; - case 'RBUF': - if ($this->majorversion == 3) { - // no additional data required - } else { - $this->errors[] = $source_data_array['frameid'].' is not a valid Frame Identifier in '.$frame_name.' (in ID3v2.'.$this->majorversion.')'; - } - - default: - if ((substr($source_data_array['frameid'], 0, 1) == 'T') || (substr($source_data_array['frameid'], 0, 1) == 'W')) { - // no additional data required - } else { - $this->errors[] = $source_data_array['frameid'].' is not a valid Frame Identifier in '.$frame_name.' (in ID3v2.'.$this->majorversion.')'; - } - break; - } - } - break; - - case 'POSS': - // 4.21 POSS Position synchronisation frame (ID3v2.3+ only) - // Time stamp format $xx - // Position $xx (xx ...) - if (($source_data_array['timestampformat'] < 1) || ($source_data_array['timestampformat'] > 2)) { - $this->errors[] = 'Invalid Time Stamp Format in '.$frame_name.' ('.$source_data_array['timestampformat'].') (valid = 1 or 2)'; - } elseif (!$this->IsWithinBitRange($source_data_array['position'], 32, false)) { - $this->errors[] = 'Invalid Position in '.$frame_name.' ('.$source_data_array['position'].') (range = 0 to 4294967295)'; - } else { - $framedata .= chr($source_data_array['timestampformat']); - $framedata .= getid3_lib::BigEndian2String($source_data_array['position'], 4, false); - } - break; - - case 'USER': - // 4.22 USER Terms of use (ID3v2.3+ only) - // Text encoding $xx - // Language $xx xx xx - // The actual text - $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid); - if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) { - $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].')'; - } elseif (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') { - $this->errors[] = 'Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')'; - } else { - $framedata .= chr($source_data_array['encodingid']); - $framedata .= strtolower($source_data_array['language']); - $framedata .= $source_data_array['data']; - } - break; - - case 'OWNE': - // 4.23 OWNE Ownership frame (ID3v2.3+ only) - // Text encoding $xx - // Price paid $00 - // Date of purch. - // Seller - $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid); - if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) { - $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].')'; - } elseif (!$this->IsANumber($source_data_array['pricepaid']['value'], false)) { - $this->errors[] = 'Invalid Price Paid in '.$frame_name.' ('.$source_data_array['pricepaid']['value'].')'; - } elseif (!$this->IsValidDateStampString($source_data_array['purchasedate'])) { - $this->errors[] = 'Invalid Date Of Purchase in '.$frame_name.' ('.$source_data_array['purchasedate'].') (format = YYYYMMDD)'; - } else { - $framedata .= chr($source_data_array['encodingid']); - $framedata .= str_replace("\x00", '', $source_data_array['pricepaid']['value'])."\x00"; - $framedata .= $source_data_array['purchasedate']; - $framedata .= $source_data_array['seller']; - } - break; - - case 'COMR': - // 4.24 COMR Commercial frame (ID3v2.3+ only) - // Text encoding $xx - // Price string $00 - // Valid until - // Contact URL $00 - // Received as $xx - // Name of seller $00 (00) - // Description $00 (00) - // Picture MIME type $00 - // Seller logo - $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid); - if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) { - $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].')'; - } elseif (!$this->IsValidDateStampString($source_data_array['pricevaliduntil'])) { - $this->errors[] = 'Invalid Valid Until date in '.$frame_name.' ('.$source_data_array['pricevaliduntil'].') (format = YYYYMMDD)'; - } elseif (!$this->IsValidURL($source_data_array['contacturl'], false, true)) { - $this->errors[] = 'Invalid Contact URL in '.$frame_name.' ('.$source_data_array['contacturl'].') (allowed schemes: http, https, ftp, mailto)'; - } elseif (!$this->ID3v2IsValidCOMRreceivedAs($source_data_array['receivedasid'])) { - $this->errors[] = 'Invalid Received As byte in '.$frame_name.' ('.$source_data_array['contacturl'].') (range = 0 to 8)'; - } elseif (!$this->IsValidMIMEstring($source_data_array['mime'])) { - $this->errors[] = 'Invalid MIME Type in '.$frame_name.' ('.$source_data_array['mime'].')'; - } else { - $framedata .= chr($source_data_array['encodingid']); - unset($pricestring); - foreach ($source_data_array['price'] as $key => $val) { - if ($this->ID3v2IsValidPriceString($key.$val['value'])) { - $pricestrings[] = $key.$val['value']; - } else { - $this->errors[] = 'Invalid Price String in '.$frame_name.' ('.$key.$val['value'].')'; - } - } - $framedata .= implode('/', $pricestrings); - $framedata .= $source_data_array['pricevaliduntil']; - $framedata .= str_replace("\x00", '', $source_data_array['contacturl'])."\x00"; - $framedata .= chr($source_data_array['receivedasid']); - $framedata .= $source_data_array['sellername'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']); - $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']); - $framedata .= $source_data_array['mime']."\x00"; - $framedata .= $source_data_array['logo']; - } - break; - - case 'ENCR': - // 4.25 ENCR Encryption method registration (ID3v2.3+ only) - // Owner identifier $00 - // Method symbol $xx - // Encryption data - if (!$this->IsWithinBitRange($source_data_array['methodsymbol'], 8, false)) { - $this->errors[] = 'Invalid Group Symbol in '.$frame_name.' ('.$source_data_array['methodsymbol'].') (range = 0 to 255)'; - } else { - $framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00"; - $framedata .= ord($source_data_array['methodsymbol']); - $framedata .= $source_data_array['data']; - } - break; - - case 'GRID': - // 4.26 GRID Group identification registration (ID3v2.3+ only) - // Owner identifier $00 - // Group symbol $xx - // Group dependent data - if (!$this->IsWithinBitRange($source_data_array['groupsymbol'], 8, false)) { - $this->errors[] = 'Invalid Group Symbol in '.$frame_name.' ('.$source_data_array['groupsymbol'].') (range = 0 to 255)'; - } else { - $framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00"; - $framedata .= ord($source_data_array['groupsymbol']); - $framedata .= $source_data_array['data']; - } - break; - - case 'PRIV': - // 4.27 PRIV Private frame (ID3v2.3+ only) - // Owner identifier $00 - // The private data - $framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00"; - $framedata .= $source_data_array['data']; - break; - - case 'SIGN': - // 4.28 SIGN Signature frame (ID3v2.4+ only) - // Group symbol $xx - // Signature - if (!$this->IsWithinBitRange($source_data_array['groupsymbol'], 8, false)) { - $this->errors[] = 'Invalid Group Symbol in '.$frame_name.' ('.$source_data_array['groupsymbol'].') (range = 0 to 255)'; - } else { - $framedata .= ord($source_data_array['groupsymbol']); - $framedata .= $source_data_array['data']; - } - break; - - case 'SEEK': - // 4.29 SEEK Seek frame (ID3v2.4+ only) - // Minimum offset to next tag $xx xx xx xx - if (!$this->IsWithinBitRange($source_data_array['data'], 32, false)) { - $this->errors[] = 'Invalid Minimum Offset in '.$frame_name.' ('.$source_data_array['data'].') (range = 0 to 4294967295)'; - } else { - $framedata .= getid3_lib::BigEndian2String($source_data_array['data'], 4, false); - } - break; - - case 'ASPI': - // 4.30 ASPI Audio seek point index (ID3v2.4+ only) - // Indexed data start (S) $xx xx xx xx - // Indexed data length (L) $xx xx xx xx - // Number of index points (N) $xx xx - // Bits per index point (b) $xx - // Then for every index point the following data is included: - // Fraction at index (Fi) $xx (xx) - if (!$this->IsWithinBitRange($source_data_array['datastart'], 32, false)) { - $this->errors[] = 'Invalid Indexed Data Start in '.$frame_name.' ('.$source_data_array['datastart'].') (range = 0 to 4294967295)'; - } elseif (!$this->IsWithinBitRange($source_data_array['datalength'], 32, false)) { - $this->errors[] = 'Invalid Indexed Data Length in '.$frame_name.' ('.$source_data_array['datalength'].') (range = 0 to 4294967295)'; - } elseif (!$this->IsWithinBitRange($source_data_array['indexpoints'], 16, false)) { - $this->errors[] = 'Invalid Number Of Index Points in '.$frame_name.' ('.$source_data_array['indexpoints'].') (range = 0 to 65535)'; - } elseif (!$this->IsWithinBitRange($source_data_array['bitsperpoint'], 8, false)) { - $this->errors[] = 'Invalid Bits Per Index Point in '.$frame_name.' ('.$source_data_array['bitsperpoint'].') (range = 0 to 255)'; - } elseif ($source_data_array['indexpoints'] != count($source_data_array['indexes'])) { - $this->errors[] = 'Number Of Index Points does not match actual supplied data in '.$frame_name; - } else { - $framedata .= getid3_lib::BigEndian2String($source_data_array['datastart'], 4, false); - $framedata .= getid3_lib::BigEndian2String($source_data_array['datalength'], 4, false); - $framedata .= getid3_lib::BigEndian2String($source_data_array['indexpoints'], 2, false); - $framedata .= getid3_lib::BigEndian2String($source_data_array['bitsperpoint'], 1, false); - foreach ($source_data_array['indexes'] as $key => $val) { - $framedata .= getid3_lib::BigEndian2String($val, ceil($source_data_array['bitsperpoint'] / 8), false); - } - } - break; - - case 'RGAD': - // RGAD Replay Gain Adjustment - // http://privatewww.essex.ac.uk/~djmrob/replaygain/ - // Peak Amplitude $xx $xx $xx $xx - // Radio Replay Gain Adjustment %aaabbbcd %dddddddd - // Audiophile Replay Gain Adjustment %aaabbbcd %dddddddd - // a - name code - // b - originator code - // c - sign bit - // d - replay gain adjustment - - if (($source_data_array['track_adjustment'] > 51) || ($source_data_array['track_adjustment'] < -51)) { - $this->errors[] = 'Invalid Track Adjustment in '.$frame_name.' ('.$source_data_array['track_adjustment'].') (range = -51.0 to +51.0)'; - } elseif (($source_data_array['album_adjustment'] > 51) || ($source_data_array['album_adjustment'] < -51)) { - $this->errors[] = 'Invalid Album Adjustment in '.$frame_name.' ('.$source_data_array['album_adjustment'].') (range = -51.0 to +51.0)'; - } elseif (!$this->ID3v2IsValidRGADname($source_data_array['raw']['track_name'])) { - $this->errors[] = 'Invalid Track Name Code in '.$frame_name.' ('.$source_data_array['raw']['track_name'].') (range = 0 to 2)'; - } elseif (!$this->ID3v2IsValidRGADname($source_data_array['raw']['album_name'])) { - $this->errors[] = 'Invalid Album Name Code in '.$frame_name.' ('.$source_data_array['raw']['album_name'].') (range = 0 to 2)'; - } elseif (!$this->ID3v2IsValidRGADoriginator($source_data_array['raw']['track_originator'])) { - $this->errors[] = 'Invalid Track Originator Code in '.$frame_name.' ('.$source_data_array['raw']['track_originator'].') (range = 0 to 3)'; - } elseif (!$this->ID3v2IsValidRGADoriginator($source_data_array['raw']['album_originator'])) { - $this->errors[] = 'Invalid Album Originator Code in '.$frame_name.' ('.$source_data_array['raw']['album_originator'].') (range = 0 to 3)'; - } else { - $framedata .= getid3_lib::Float2String($source_data_array['peakamplitude'], 32); - $framedata .= getid3_lib::RGADgainString($source_data_array['raw']['track_name'], $source_data_array['raw']['track_originator'], $source_data_array['track_adjustment']); - $framedata .= getid3_lib::RGADgainString($source_data_array['raw']['album_name'], $source_data_array['raw']['album_originator'], $source_data_array['album_adjustment']); - } - break; - - default: - if ($frame_name{0} == 'T') { - // 4.2. T??? Text information frames - // Text encoding $xx - // Information - $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid); - if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) { - $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion; - } else { - $framedata .= chr($source_data_array['encodingid']); - $framedata .= $source_data_array['data']; - } - } elseif ($frame_name{0} == 'W') { - // 4.3. W??? URL link frames - // URL - if (!$this->IsValidURL($source_data_array['data'], false, false)) { - //$this->errors[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')'; - // probably should be an error, need to rewrite IsValidURL() to handle other encodings - $this->warnings[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')'; - } else { - $framedata .= $source_data_array['data']; - } - } else { - $this->errors[] = $frame_name.' not yet supported in $this->GenerateID3v2FrameData()'; - } - break; - } - } - if (!empty($this->errors)) { - return false; - } - return $framedata; - } - - function ID3v2FrameIsAllowed($frame_name, $source_data_array) { - static $PreviousFrames = array(); - - if ($frame_name === null) { - // if the writing functions are called multiple times, the static array needs to be - // cleared - this can be done by calling $this->ID3v2FrameIsAllowed(null, '') - $PreviousFrames = array(); - return true; - } - - if ($this->majorversion == 4) { - switch ($frame_name) { - case 'UFID': - case 'AENC': - case 'ENCR': - case 'GRID': - if (!isset($source_data_array['ownerid'])) { - $this->errors[] = '[ownerid] not specified for '.$frame_name; - } elseif (in_array($frame_name.$source_data_array['ownerid'], $PreviousFrames)) { - $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same OwnerID ('.$source_data_array['ownerid'].')'; - } else { - $PreviousFrames[] = $frame_name.$source_data_array['ownerid']; - } - break; - - case 'TXXX': - case 'WXXX': - case 'RVA2': - case 'EQU2': - case 'APIC': - case 'GEOB': - if (!isset($source_data_array['description'])) { - $this->errors[] = '[description] not specified for '.$frame_name; - } elseif (in_array($frame_name.$source_data_array['description'], $PreviousFrames)) { - $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Description ('.$source_data_array['description'].')'; - } else { - $PreviousFrames[] = $frame_name.$source_data_array['description']; - } - break; - - case 'USER': - if (!isset($source_data_array['language'])) { - $this->errors[] = '[language] not specified for '.$frame_name; - } elseif (in_array($frame_name.$source_data_array['language'], $PreviousFrames)) { - $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Language ('.$source_data_array['language'].')'; - } else { - $PreviousFrames[] = $frame_name.$source_data_array['language']; - } - break; - - case 'USLT': - case 'SYLT': - case 'COMM': - if (!isset($source_data_array['language'])) { - $this->errors[] = '[language] not specified for '.$frame_name; - } elseif (!isset($source_data_array['description'])) { - $this->errors[] = '[description] not specified for '.$frame_name; - } elseif (in_array($frame_name.$source_data_array['language'].$source_data_array['description'], $PreviousFrames)) { - $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Language + Description ('.$source_data_array['language'].' + '.$source_data_array['description'].')'; - } else { - $PreviousFrames[] = $frame_name.$source_data_array['language'].$source_data_array['description']; - } - break; - - case 'POPM': - if (!isset($source_data_array['email'])) { - $this->errors[] = '[email] not specified for '.$frame_name; - } elseif (in_array($frame_name.$source_data_array['email'], $PreviousFrames)) { - $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Email ('.$source_data_array['email'].')'; - } else { - $PreviousFrames[] = $frame_name.$source_data_array['email']; - } - break; - - case 'IPLS': - case 'MCDI': - case 'ETCO': - case 'MLLT': - case 'SYTC': - case 'RVRB': - case 'PCNT': - case 'RBUF': - case 'POSS': - case 'OWNE': - case 'SEEK': - case 'ASPI': - case 'RGAD': - if (in_array($frame_name, $PreviousFrames)) { - $this->errors[] = 'Only one '.$frame_name.' tag allowed'; - } else { - $PreviousFrames[] = $frame_name; - } - break; - - case 'LINK': - // this isn't implemented quite right (yet) - it should check the target frame data for compliance - // but right now it just allows one linked frame of each type, to be safe. - if (!isset($source_data_array['frameid'])) { - $this->errors[] = '[frameid] not specified for '.$frame_name; - } elseif (in_array($frame_name.$source_data_array['frameid'], $PreviousFrames)) { - $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same FrameID ('.$source_data_array['frameid'].')'; - } elseif (in_array($source_data_array['frameid'], $PreviousFrames)) { - // no links to singleton tags - $this->errors[] = 'Cannot specify a '.$frame_name.' tag to a singleton tag that already exists ('.$source_data_array['frameid'].')'; - } else { - $PreviousFrames[] = $frame_name.$source_data_array['frameid']; // only one linked tag of this type - $PreviousFrames[] = $source_data_array['frameid']; // no non-linked singleton tags of this type - } - break; - - case 'COMR': - // There may be more than one 'commercial frame' in a tag, but no two may be identical - // Checking isn't implemented at all (yet) - just assumes that it's OK. - break; - - case 'PRIV': - case 'SIGN': - if (!isset($source_data_array['ownerid'])) { - $this->errors[] = '[ownerid] not specified for '.$frame_name; - } elseif (!isset($source_data_array['data'])) { - $this->errors[] = '[data] not specified for '.$frame_name; - } elseif (in_array($frame_name.$source_data_array['ownerid'].$source_data_array['data'], $PreviousFrames)) { - $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same OwnerID + Data ('.$source_data_array['ownerid'].' + '.$source_data_array['data'].')'; - } else { - $PreviousFrames[] = $frame_name.$source_data_array['ownerid'].$source_data_array['data']; - } - break; - - default: - if (($frame_name{0} != 'T') && ($frame_name{0} != 'W')) { - $this->errors[] = 'Frame not allowed in ID3v2.'.$this->majorversion.': '.$frame_name; - } - break; - } - - } elseif ($this->majorversion == 3) { - - switch ($frame_name) { - case 'UFID': - case 'AENC': - case 'ENCR': - case 'GRID': - if (!isset($source_data_array['ownerid'])) { - $this->errors[] = '[ownerid] not specified for '.$frame_name; - } elseif (in_array($frame_name.$source_data_array['ownerid'], $PreviousFrames)) { - $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same OwnerID ('.$source_data_array['ownerid'].')'; - } else { - $PreviousFrames[] = $frame_name.$source_data_array['ownerid']; - } - break; - - case 'TXXX': - case 'WXXX': - case 'APIC': - case 'GEOB': - if (!isset($source_data_array['description'])) { - $this->errors[] = '[description] not specified for '.$frame_name; - } elseif (in_array($frame_name.$source_data_array['description'], $PreviousFrames)) { - $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Description ('.$source_data_array['description'].')'; - } else { - $PreviousFrames[] = $frame_name.$source_data_array['description']; - } - break; - - case 'USER': - if (!isset($source_data_array['language'])) { - $this->errors[] = '[language] not specified for '.$frame_name; - } elseif (in_array($frame_name.$source_data_array['language'], $PreviousFrames)) { - $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Language ('.$source_data_array['language'].')'; - } else { - $PreviousFrames[] = $frame_name.$source_data_array['language']; - } - break; - - case 'USLT': - case 'SYLT': - case 'COMM': - if (!isset($source_data_array['language'])) { - $this->errors[] = '[language] not specified for '.$frame_name; - } elseif (!isset($source_data_array['description'])) { - $this->errors[] = '[description] not specified for '.$frame_name; - } elseif (in_array($frame_name.$source_data_array['language'].$source_data_array['description'], $PreviousFrames)) { - $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Language + Description ('.$source_data_array['language'].' + '.$source_data_array['description'].')'; - } else { - $PreviousFrames[] = $frame_name.$source_data_array['language'].$source_data_array['description']; - } - break; - - case 'POPM': - if (!isset($source_data_array['email'])) { - $this->errors[] = '[email] not specified for '.$frame_name; - } elseif (in_array($frame_name.$source_data_array['email'], $PreviousFrames)) { - $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Email ('.$source_data_array['email'].')'; - } else { - $PreviousFrames[] = $frame_name.$source_data_array['email']; - } - break; - - case 'IPLS': - case 'MCDI': - case 'ETCO': - case 'MLLT': - case 'SYTC': - case 'RVAD': - case 'EQUA': - case 'RVRB': - case 'PCNT': - case 'RBUF': - case 'POSS': - case 'OWNE': - case 'RGAD': - if (in_array($frame_name, $PreviousFrames)) { - $this->errors[] = 'Only one '.$frame_name.' tag allowed'; - } else { - $PreviousFrames[] = $frame_name; - } - break; - - case 'LINK': - // this isn't implemented quite right (yet) - it should check the target frame data for compliance - // but right now it just allows one linked frame of each type, to be safe. - if (!isset($source_data_array['frameid'])) { - $this->errors[] = '[frameid] not specified for '.$frame_name; - } elseif (in_array($frame_name.$source_data_array['frameid'], $PreviousFrames)) { - $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same FrameID ('.$source_data_array['frameid'].')'; - } elseif (in_array($source_data_array['frameid'], $PreviousFrames)) { - // no links to singleton tags - $this->errors[] = 'Cannot specify a '.$frame_name.' tag to a singleton tag that already exists ('.$source_data_array['frameid'].')'; - } else { - $PreviousFrames[] = $frame_name.$source_data_array['frameid']; // only one linked tag of this type - $PreviousFrames[] = $source_data_array['frameid']; // no non-linked singleton tags of this type - } - break; - - case 'COMR': - // There may be more than one 'commercial frame' in a tag, but no two may be identical - // Checking isn't implemented at all (yet) - just assumes that it's OK. - break; - - case 'PRIV': - if (!isset($source_data_array['ownerid'])) { - $this->errors[] = '[ownerid] not specified for '.$frame_name; - } elseif (!isset($source_data_array['data'])) { - $this->errors[] = '[data] not specified for '.$frame_name; - } elseif (in_array($frame_name.$source_data_array['ownerid'].$source_data_array['data'], $PreviousFrames)) { - $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same OwnerID + Data ('.$source_data_array['ownerid'].' + '.$source_data_array['data'].')'; - } else { - $PreviousFrames[] = $frame_name.$source_data_array['ownerid'].$source_data_array['data']; - } - break; - - default: - if (($frame_name{0} != 'T') && ($frame_name{0} != 'W')) { - $this->errors[] = 'Frame not allowed in ID3v2.'.$this->majorversion.': '.$frame_name; - } - break; - } - - } elseif ($this->majorversion == 2) { - - switch ($frame_name) { - case 'UFI': - case 'CRM': - case 'CRA': - if (!isset($source_data_array['ownerid'])) { - $this->errors[] = '[ownerid] not specified for '.$frame_name; - } elseif (in_array($frame_name.$source_data_array['ownerid'], $PreviousFrames)) { - $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same OwnerID ('.$source_data_array['ownerid'].')'; - } else { - $PreviousFrames[] = $frame_name.$source_data_array['ownerid']; - } - break; - - case 'TXX': - case 'WXX': - case 'PIC': - case 'GEO': - if (!isset($source_data_array['description'])) { - $this->errors[] = '[description] not specified for '.$frame_name; - } elseif (in_array($frame_name.$source_data_array['description'], $PreviousFrames)) { - $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Description ('.$source_data_array['description'].')'; - } else { - $PreviousFrames[] = $frame_name.$source_data_array['description']; - } - break; - - case 'ULT': - case 'SLT': - case 'COM': - if (!isset($source_data_array['language'])) { - $this->errors[] = '[language] not specified for '.$frame_name; - } elseif (!isset($source_data_array['description'])) { - $this->errors[] = '[description] not specified for '.$frame_name; - } elseif (in_array($frame_name.$source_data_array['language'].$source_data_array['description'], $PreviousFrames)) { - $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Language + Description ('.$source_data_array['language'].' + '.$source_data_array['description'].')'; - } else { - $PreviousFrames[] = $frame_name.$source_data_array['language'].$source_data_array['description']; - } - break; - - case 'POP': - if (!isset($source_data_array['email'])) { - $this->errors[] = '[email] not specified for '.$frame_name; - } elseif (in_array($frame_name.$source_data_array['email'], $PreviousFrames)) { - $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Email ('.$source_data_array['email'].')'; - } else { - $PreviousFrames[] = $frame_name.$source_data_array['email']; - } - break; - - case 'IPL': - case 'MCI': - case 'ETC': - case 'MLL': - case 'STC': - case 'RVA': - case 'EQU': - case 'REV': - case 'CNT': - case 'BUF': - if (in_array($frame_name, $PreviousFrames)) { - $this->errors[] = 'Only one '.$frame_name.' tag allowed'; - } else { - $PreviousFrames[] = $frame_name; - } - break; - - case 'LNK': - // this isn't implemented quite right (yet) - it should check the target frame data for compliance - // but right now it just allows one linked frame of each type, to be safe. - if (!isset($source_data_array['frameid'])) { - $this->errors[] = '[frameid] not specified for '.$frame_name; - } elseif (in_array($frame_name.$source_data_array['frameid'], $PreviousFrames)) { - $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same FrameID ('.$source_data_array['frameid'].')'; - } elseif (in_array($source_data_array['frameid'], $PreviousFrames)) { - // no links to singleton tags - $this->errors[] = 'Cannot specify a '.$frame_name.' tag to a singleton tag that already exists ('.$source_data_array['frameid'].')'; - } else { - $PreviousFrames[] = $frame_name.$source_data_array['frameid']; // only one linked tag of this type - $PreviousFrames[] = $source_data_array['frameid']; // no non-linked singleton tags of this type - } - break; - - default: - if (($frame_name{0} != 'T') && ($frame_name{0} != 'W')) { - $this->errors[] = 'Frame not allowed in ID3v2.'.$this->majorversion.': '.$frame_name; - } - break; - } - } - - if (!empty($this->errors)) { - return false; - } - return true; - } - - function GenerateID3v2Tag($noerrorsonly=true) { - $this->ID3v2FrameIsAllowed(null, ''); // clear static array in case this isn't the first call to $this->GenerateID3v2Tag() - - $tagstring = ''; - if (is_array($this->tag_data)) { - foreach ($this->tag_data as $frame_name => $frame_rawinputdata) { - foreach ($frame_rawinputdata as $irrelevantindex => $source_data_array) { - if (getid3_id3v2::IsValidID3v2FrameName($frame_name, $this->majorversion)) { - unset($frame_length); - unset($frame_flags); - $frame_data = false; - if ($this->ID3v2FrameIsAllowed($frame_name, $source_data_array)) { - if ($frame_data = $this->GenerateID3v2FrameData($frame_name, $source_data_array)) { - $FrameUnsynchronisation = false; - if ($this->majorversion >= 4) { - // frame-level unsynchronisation - $unsynchdata = $frame_data; - if ($this->id3v2_use_unsynchronisation) { - $unsynchdata = $this->Unsynchronise($frame_data); - } - if (strlen($unsynchdata) != strlen($frame_data)) { - // unsynchronisation needed - $FrameUnsynchronisation = true; - $frame_data = $unsynchdata; - if (isset($TagUnsynchronisation) && $TagUnsynchronisation === false) { - // only set to true if ALL frames are unsynchronised - } else { - $TagUnsynchronisation = true; - } - } else { - if (isset($TagUnsynchronisation)) { - $TagUnsynchronisation = false; - } - } - unset($unsynchdata); - - $frame_length = getid3_lib::BigEndian2String(strlen($frame_data), 4, true); - } else { - $frame_length = getid3_lib::BigEndian2String(strlen($frame_data), 4, false); - } - $frame_flags = $this->GenerateID3v2FrameFlags($this->ID3v2FrameFlagsLookupTagAlter($frame_name), $this->ID3v2FrameFlagsLookupFileAlter($frame_name), false, false, false, false, $FrameUnsynchronisation, false); - } - } else { - $this->errors[] = 'Frame "'.$frame_name.'" is NOT allowed'; - } - if ($frame_data === false) { - $this->errors[] = '$this->GenerateID3v2FrameData() failed for "'.$frame_name.'"'; - if ($noerrorsonly) { - return false; - } else { - unset($frame_name); - } - } - } else { - // ignore any invalid frame names, including 'title', 'header', etc - $this->warnings[] = 'Ignoring invalid ID3v2 frame type: "'.$frame_name.'"'; - unset($frame_name); - unset($frame_length); - unset($frame_flags); - unset($frame_data); - } - if (isset($frame_name) && isset($frame_length) && isset($frame_flags) && isset($frame_data)) { - $tagstring .= $frame_name.$frame_length.$frame_flags.$frame_data; - } - } - } - - if (!isset($TagUnsynchronisation)) { - $TagUnsynchronisation = false; - } - if (($this->majorversion <= 3) && $this->id3v2_use_unsynchronisation) { - // tag-level unsynchronisation - $unsynchdata = $this->Unsynchronise($tagstring); - if (strlen($unsynchdata) != strlen($tagstring)) { - // unsynchronisation needed - $TagUnsynchronisation = true; - $tagstring = $unsynchdata; - } - } - - while ($this->paddedlength < (strlen($tagstring) + getid3_id3v2::ID3v2HeaderLength($this->majorversion))) { - $this->paddedlength += 1024; - } - - $footer = false; // ID3v2 footers not yet supported in getID3() - if (!$footer && ($this->paddedlength > (strlen($tagstring) + getid3_id3v2::ID3v2HeaderLength($this->majorversion)))) { - // pad up to $paddedlength bytes if unpadded tag is shorter than $paddedlength - // "Furthermore it MUST NOT have any padding when a tag footer is added to the tag." - $tagstring .= @str_repeat("\x00", $this->paddedlength - strlen($tagstring) - getid3_id3v2::ID3v2HeaderLength($this->majorversion)); - } - if ($this->id3v2_use_unsynchronisation && (substr($tagstring, strlen($tagstring) - 1, 1) == "\xFF")) { - // special unsynchronisation case: - // if last byte == $FF then appended a $00 - $TagUnsynchronisation = true; - $tagstring .= "\x00"; - } - - $tagheader = 'ID3'; - $tagheader .= chr($this->majorversion); - $tagheader .= chr($this->minorversion); - $tagheader .= $this->GenerateID3v2TagFlags(array('unsynchronisation'=>$TagUnsynchronisation)); - $tagheader .= getid3_lib::BigEndian2String(strlen($tagstring), 4, true); - - return $tagheader.$tagstring; - } - $this->errors[] = 'tag_data is not an array in GenerateID3v2Tag()'; - return false; - } - - function ID3v2IsValidPriceString($pricestring) { - if (getid3_id3v2::LanguageLookup(substr($pricestring, 0, 3), true) == '') { - return false; - } elseif (!$this->IsANumber(substr($pricestring, 3), true)) { - return false; - } - return true; - } - - function ID3v2FrameFlagsLookupTagAlter($framename) { - // unfinished - switch ($framename) { - case 'RGAD': - $allow = true; - default: - $allow = false; - break; - } - return $allow; - } - - function ID3v2FrameFlagsLookupFileAlter($framename) { - // unfinished - switch ($framename) { - case 'RGAD': - return false; - break; - - default: - return false; - break; - } - } - - function ID3v2IsValidETCOevent($eventid) { - if (($eventid < 0) || ($eventid > 0xFF)) { - // outside range of 1 byte - return false; - } elseif (($eventid >= 0xF0) && ($eventid <= 0xFC)) { - // reserved for future use - return false; - } elseif (($eventid >= 0x17) && ($eventid <= 0xDF)) { - // reserved for future use - return false; - } elseif (($eventid >= 0x0E) && ($eventid <= 0x16) && ($this->majorversion == 2)) { - // not defined in ID3v2.2 - return false; - } elseif (($eventid >= 0x15) && ($eventid <= 0x16) && ($this->majorversion == 3)) { - // not defined in ID3v2.3 - return false; - } - return true; - } - - function ID3v2IsValidSYLTtype($contenttype) { - if (($contenttype >= 0) && ($contenttype <= 8) && ($this->majorversion == 4)) { - return true; - } elseif (($contenttype >= 0) && ($contenttype <= 6) && ($this->majorversion == 3)) { - return true; - } - return false; - } - - function ID3v2IsValidRVA2channeltype($channeltype) { - if (($channeltype >= 0) && ($channeltype <= 8) && ($this->majorversion == 4)) { - return true; - } - return false; - } - - function ID3v2IsValidAPICpicturetype($picturetype) { - if (($picturetype >= 0) && ($picturetype <= 0x14) && ($this->majorversion >= 2) && ($this->majorversion <= 4)) { - return true; - } - return false; - } - - function ID3v2IsValidAPICimageformat($imageformat) { - if ($imageformat == '-->') { - return true; - } elseif ($this->majorversion == 2) { - if ((strlen($imageformat) == 3) && ($imageformat == strtoupper($imageformat))) { - return true; - } - } elseif (($this->majorversion == 3) || ($this->majorversion == 4)) { - if ($this->IsValidMIMEstring($imageformat)) { - return true; - } - } - return false; - } - - function ID3v2IsValidCOMRreceivedAs($receivedas) { - if (($this->majorversion >= 3) && ($receivedas >= 0) && ($receivedas <= 8)) { - return true; - } - return false; - } - - function ID3v2IsValidRGADname($RGADname) { - if (($RGADname >= 0) && ($RGADname <= 2)) { - return true; - } - return false; - } - - function ID3v2IsValidRGADoriginator($RGADoriginator) { - if (($RGADoriginator >= 0) && ($RGADoriginator <= 3)) { - return true; - } - return false; - } - - function ID3v2IsValidTextEncoding($textencodingbyte) { - static $ID3v2IsValidTextEncoding_cache = array( - 2 => array(true, true), - 3 => array(true, true), - 4 => array(true, true, true, true)); - return isset($ID3v2IsValidTextEncoding_cache[$this->majorversion][$textencodingbyte]); - } - - function Unsynchronise($data) { - // Whenever a false synchronisation is found within the tag, one zeroed - // byte is inserted after the first false synchronisation byte. The - // format of a correct sync that should be altered by ID3 encoders is as - // follows: - // %11111111 111xxxxx - // And should be replaced with: - // %11111111 00000000 111xxxxx - // This has the side effect that all $FF 00 combinations have to be - // altered, so they won't be affected by the decoding process. Therefore - // all the $FF 00 combinations have to be replaced with the $FF 00 00 - // combination during the unsynchronisation. - - $data = str_replace("\xFF\x00", "\xFF\x00\x00", $data); - $unsyncheddata = ''; - $datalength = strlen($data); - for ($i = 0; $i < $datalength; $i++) { - $thischar = $data{$i}; - $unsyncheddata .= $thischar; - if ($thischar == "\xFF") { - $nextchar = ord($data{$i + 1}); - if (($nextchar & 0xE0) == 0xE0) { - // previous byte = 11111111, this byte = 111????? - $unsyncheddata .= "\x00"; - } - } - } - return $unsyncheddata; - } - - function is_hash($var) { - // written by dev-null�christophe*vg - // taken from http://www.php.net/manual/en/function.array-merge-recursive.php - if (is_array($var)) { - $keys = array_keys($var); - $all_num = true; - for ($i = 0; $i < count($keys); $i++) { - if (is_string($keys[$i])) { - return true; - } - } - } - return false; - } - - function array_join_merge($arr1, $arr2) { - // written by dev-null�christophe*vg - // taken from http://www.php.net/manual/en/function.array-merge-recursive.php - if (is_array($arr1) && is_array($arr2)) { - // the same -> merge - $new_array = array(); - - if ($this->is_hash($arr1) && $this->is_hash($arr2)) { - // hashes -> merge based on keys - $keys = array_merge(array_keys($arr1), array_keys($arr2)); - foreach ($keys as $key) { - $new_array[$key] = $this->array_join_merge(@$arr1[$key], @$arr2[$key]); - } - } else { - // two real arrays -> merge - $new_array = array_reverse(array_unique(array_reverse(array_merge($arr1, $arr2)))); - } - return $new_array; - } else { - // not the same ... take new one if defined, else the old one stays - return $arr2 ? $arr2 : $arr1; - } - } - - function IsValidMIMEstring($mimestring) { - if ((strlen($mimestring) >= 3) && (strpos($mimestring, '/') > 0) && (strpos($mimestring, '/') < (strlen($mimestring) - 1))) { - return true; - } - return false; - } - - function IsWithinBitRange($number, $maxbits, $signed=false) { - if ($signed) { - if (($number > (0 - pow(2, $maxbits - 1))) && ($number <= pow(2, $maxbits - 1))) { - return true; - } - } else { - if (($number >= 0) && ($number <= pow(2, $maxbits))) { - return true; - } - } - return false; - } - - function safe_parse_url($url) { - $parts = @parse_url($url); - $parts['scheme'] = (isset($parts['scheme']) ? $parts['scheme'] : ''); - $parts['host'] = (isset($parts['host']) ? $parts['host'] : ''); - $parts['user'] = (isset($parts['user']) ? $parts['user'] : ''); - $parts['pass'] = (isset($parts['pass']) ? $parts['pass'] : ''); - $parts['path'] = (isset($parts['path']) ? $parts['path'] : ''); - $parts['query'] = (isset($parts['query']) ? $parts['query'] : ''); - return $parts; - } - - function IsValidURL($url, $allowUserPass=false) { - if ($url == '') { - return false; - } - if ($allowUserPass !== true) { - if (strstr($url, '@')) { - // in the format http://user:pass@example.com or http://user@example.com - // but could easily be somebody incorrectly entering an email address in place of a URL - return false; - } - } - if ($parts = $this->safe_parse_url($url)) { - if (($parts['scheme'] != 'http') && ($parts['scheme'] != 'https') && ($parts['scheme'] != 'ftp') && ($parts['scheme'] != 'gopher')) { - return false; - } elseif (!preg_match("/^[[:alnum:]]([-.]?[0-9a-z])*\.[a-z]{2,3}$/i", $parts['host'], $regs) && !IsValidDottedIP($parts['host'])) { - return false; - } elseif (!preg_match("/^([[:alnum:]-]|[\_])*$/i", $parts['user'], $regs)) { - return false; - } elseif (!preg_match("/^([[:alnum:]-]|[\_])*$/i", $parts['pass'], $regs)) { - return false; - } elseif (!preg_match("/^[[:alnum:]/_\.@~-]*$/i", $parts['path'], $regs)) { - return false; - } elseif (!preg_match("/^[[:alnum:]?&=+:;_()%#/,\.-]*$/i", $parts['query'], $regs)) { - return false; - } else { - return true; - } - } - return false; - } - - function ID3v2ShortFrameNameLookup($majorversion, $long_description) { - $long_description = str_replace(' ', '_', strtolower(trim($long_description))); - static $ID3v2ShortFrameNameLookup = array(); - if (empty($ID3v2ShortFrameNameLookup)) { - - // The following are unique to ID3v2.2 - $ID3v2ShortFrameNameLookup[2]['comment'] = 'COM'; - $ID3v2ShortFrameNameLookup[2]['album'] = 'TAL'; - $ID3v2ShortFrameNameLookup[2]['beats_per_minute'] = 'TBP'; - $ID3v2ShortFrameNameLookup[2]['composer'] = 'TCM'; - $ID3v2ShortFrameNameLookup[2]['genre'] = 'TCO'; - $ID3v2ShortFrameNameLookup[2]['copyright'] = 'TCR'; - $ID3v2ShortFrameNameLookup[2]['encoded_by'] = 'TEN'; - $ID3v2ShortFrameNameLookup[2]['language'] = 'TLA'; - $ID3v2ShortFrameNameLookup[2]['length'] = 'TLE'; - $ID3v2ShortFrameNameLookup[2]['original_artist'] = 'TOA'; - $ID3v2ShortFrameNameLookup[2]['original_filename'] = 'TOF'; - $ID3v2ShortFrameNameLookup[2]['original_lyricist'] = 'TOL'; - $ID3v2ShortFrameNameLookup[2]['original_album_title'] = 'TOT'; - $ID3v2ShortFrameNameLookup[2]['artist'] = 'TP1'; - $ID3v2ShortFrameNameLookup[2]['band'] = 'TP2'; - $ID3v2ShortFrameNameLookup[2]['conductor'] = 'TP3'; - $ID3v2ShortFrameNameLookup[2]['remixer'] = 'TP4'; - $ID3v2ShortFrameNameLookup[2]['publisher'] = 'TPB'; - $ID3v2ShortFrameNameLookup[2]['isrc'] = 'TRC'; - $ID3v2ShortFrameNameLookup[2]['tracknumber'] = 'TRK'; - $ID3v2ShortFrameNameLookup[2]['size'] = 'TSI'; - $ID3v2ShortFrameNameLookup[2]['encoder_settings'] = 'TSS'; - $ID3v2ShortFrameNameLookup[2]['description'] = 'TT1'; - $ID3v2ShortFrameNameLookup[2]['title'] = 'TT2'; - $ID3v2ShortFrameNameLookup[2]['subtitle'] = 'TT3'; - $ID3v2ShortFrameNameLookup[2]['lyricist'] = 'TXT'; - $ID3v2ShortFrameNameLookup[2]['user_text'] = 'TXX'; - $ID3v2ShortFrameNameLookup[2]['year'] = 'TYE'; - $ID3v2ShortFrameNameLookup[2]['unique_file_identifier'] = 'UFI'; - $ID3v2ShortFrameNameLookup[2]['unsynchronised_lyrics'] = 'ULT'; - $ID3v2ShortFrameNameLookup[2]['url_file'] = 'WAF'; - $ID3v2ShortFrameNameLookup[2]['url_artist'] = 'WAR'; - $ID3v2ShortFrameNameLookup[2]['url_source'] = 'WAS'; - $ID3v2ShortFrameNameLookup[2]['copyright_information'] = 'WCP'; - $ID3v2ShortFrameNameLookup[2]['url_publisher'] = 'WPB'; - $ID3v2ShortFrameNameLookup[2]['url_user'] = 'WXX'; - - // The following are common to ID3v2.3 and ID3v2.4 - $ID3v2ShortFrameNameLookup[3]['audio_encryption'] = 'AENC'; - $ID3v2ShortFrameNameLookup[3]['attached_picture'] = 'APIC'; - $ID3v2ShortFrameNameLookup[3]['comment'] = 'COMM'; - $ID3v2ShortFrameNameLookup[3]['commercial'] = 'COMR'; - $ID3v2ShortFrameNameLookup[3]['encryption_method_registration'] = 'ENCR'; - $ID3v2ShortFrameNameLookup[3]['event_timing_codes'] = 'ETCO'; - $ID3v2ShortFrameNameLookup[3]['general_encapsulated_object'] = 'GEOB'; - $ID3v2ShortFrameNameLookup[3]['group_identification_registration'] = 'GRID'; - $ID3v2ShortFrameNameLookup[3]['linked_information'] = 'LINK'; - $ID3v2ShortFrameNameLookup[3]['music_cd_identifier'] = 'MCDI'; - $ID3v2ShortFrameNameLookup[3]['mpeg_location_lookup_table'] = 'MLLT'; - $ID3v2ShortFrameNameLookup[3]['ownership'] = 'OWNE'; - $ID3v2ShortFrameNameLookup[3]['play_counter'] = 'PCNT'; - $ID3v2ShortFrameNameLookup[3]['popularimeter'] = 'POPM'; - $ID3v2ShortFrameNameLookup[3]['position_synchronisation'] = 'POSS'; - $ID3v2ShortFrameNameLookup[3]['private'] = 'PRIV'; - $ID3v2ShortFrameNameLookup[3]['recommended_buffer_size'] = 'RBUF'; - $ID3v2ShortFrameNameLookup[3]['reverb'] = 'RVRB'; - $ID3v2ShortFrameNameLookup[3]['synchronised_lyrics'] = 'SYLT'; - $ID3v2ShortFrameNameLookup[3]['synchronised_tempo_codes'] = 'SYTC'; - $ID3v2ShortFrameNameLookup[3]['album'] = 'TALB'; - $ID3v2ShortFrameNameLookup[3]['beats_per_minute'] = 'TBPM'; - $ID3v2ShortFrameNameLookup[3]['composer'] = 'TCOM'; - $ID3v2ShortFrameNameLookup[3]['genre'] = 'TCON'; - $ID3v2ShortFrameNameLookup[3]['copyright'] = 'TCOP'; - $ID3v2ShortFrameNameLookup[3]['playlist_delay'] = 'TDLY'; - $ID3v2ShortFrameNameLookup[3]['encoded_by'] = 'TENC'; - $ID3v2ShortFrameNameLookup[3]['lyricist'] = 'TEXT'; - $ID3v2ShortFrameNameLookup[3]['file_type'] = 'TFLT'; - $ID3v2ShortFrameNameLookup[3]['content_group_description'] = 'TIT1'; - $ID3v2ShortFrameNameLookup[3]['title'] = 'TIT2'; - $ID3v2ShortFrameNameLookup[3]['subtitle'] = 'TIT3'; - $ID3v2ShortFrameNameLookup[3]['initial_key'] = 'TKEY'; - $ID3v2ShortFrameNameLookup[3]['language'] = 'TLAN'; - $ID3v2ShortFrameNameLookup[3]['length'] = 'TLEN'; - $ID3v2ShortFrameNameLookup[3]['media_type'] = 'TMED'; - $ID3v2ShortFrameNameLookup[3]['original_album_title'] = 'TOAL'; - $ID3v2ShortFrameNameLookup[3]['original_filename'] = 'TOFN'; - $ID3v2ShortFrameNameLookup[3]['original_lyricist'] = 'TOLY'; - $ID3v2ShortFrameNameLookup[3]['original_artist'] = 'TOPE'; - $ID3v2ShortFrameNameLookup[3]['file_owner'] = 'TOWN'; - $ID3v2ShortFrameNameLookup[3]['artist'] = 'TPE1'; - $ID3v2ShortFrameNameLookup[3]['band'] = 'TPE2'; - $ID3v2ShortFrameNameLookup[3]['conductor'] = 'TPE3'; - $ID3v2ShortFrameNameLookup[3]['remixer'] = 'TPE4'; - $ID3v2ShortFrameNameLookup[3]['part_of_set'] = 'TPOS'; - $ID3v2ShortFrameNameLookup[3]['publisher'] = 'TPUB'; - $ID3v2ShortFrameNameLookup[3]['tracknumber'] = 'TRCK'; - $ID3v2ShortFrameNameLookup[3]['internet_radio_station_name'] = 'TRSN'; - $ID3v2ShortFrameNameLookup[3]['internet_radio_station_owner'] = 'TRSO'; - $ID3v2ShortFrameNameLookup[3]['isrc'] = 'TSRC'; - $ID3v2ShortFrameNameLookup[3]['encoder_settings'] = 'TSSE'; - $ID3v2ShortFrameNameLookup[3]['user_text'] = 'TXXX'; - $ID3v2ShortFrameNameLookup[3]['unique_file_identifier'] = 'UFID'; - $ID3v2ShortFrameNameLookup[3]['terms_of_use'] = 'USER'; - $ID3v2ShortFrameNameLookup[3]['unsynchronised_lyrics'] = 'USLT'; - $ID3v2ShortFrameNameLookup[3]['commercial'] = 'WCOM'; - $ID3v2ShortFrameNameLookup[3]['copyright_information'] = 'WCOP'; - $ID3v2ShortFrameNameLookup[3]['url_file'] = 'WOAF'; - $ID3v2ShortFrameNameLookup[3]['url_artist'] = 'WOAR'; - $ID3v2ShortFrameNameLookup[3]['url_source'] = 'WOAS'; - $ID3v2ShortFrameNameLookup[3]['url_station'] = 'WORS'; - $ID3v2ShortFrameNameLookup[3]['payment'] = 'WPAY'; - $ID3v2ShortFrameNameLookup[3]['url_publisher'] = 'WPUB'; - $ID3v2ShortFrameNameLookup[3]['url_user'] = 'WXXX'; - - // The above are common to ID3v2.3 and ID3v2.4 - // so copy them to ID3v2.4 before adding specifics for 2.3 and 2.4 - $ID3v2ShortFrameNameLookup[4] = $ID3v2ShortFrameNameLookup[3]; - - // The following are unique to ID3v2.3 - $ID3v2ShortFrameNameLookup[3]['equalisation'] = 'EQUA'; - $ID3v2ShortFrameNameLookup[3]['involved_people_list'] = 'IPLS'; - $ID3v2ShortFrameNameLookup[3]['relative_volume_adjustment'] = 'RVAD'; - $ID3v2ShortFrameNameLookup[3]['date'] = 'TDAT'; - $ID3v2ShortFrameNameLookup[3]['time'] = 'TIME'; - $ID3v2ShortFrameNameLookup[3]['original_release_year'] = 'TORY'; - $ID3v2ShortFrameNameLookup[3]['recording_dates'] = 'TRDA'; - $ID3v2ShortFrameNameLookup[3]['size'] = 'TSIZ'; - $ID3v2ShortFrameNameLookup[3]['year'] = 'TYER'; - - - // The following are unique to ID3v2.4 - $ID3v2ShortFrameNameLookup[4]['audio_seek_point_index'] = 'ASPI'; - $ID3v2ShortFrameNameLookup[4]['equalisation'] = 'EQU2'; - $ID3v2ShortFrameNameLookup[4]['relative_volume_adjustment'] = 'RVA2'; - $ID3v2ShortFrameNameLookup[4]['seek'] = 'SEEK'; - $ID3v2ShortFrameNameLookup[4]['signature'] = 'SIGN'; - $ID3v2ShortFrameNameLookup[4]['encoding_time'] = 'TDEN'; - $ID3v2ShortFrameNameLookup[4]['original_release_time'] = 'TDOR'; - $ID3v2ShortFrameNameLookup[4]['recording_time'] = 'TDRC'; - $ID3v2ShortFrameNameLookup[4]['release_time'] = 'TDRL'; - $ID3v2ShortFrameNameLookup[4]['tagging_time'] = 'TDTG'; - $ID3v2ShortFrameNameLookup[4]['involved_people_list'] = 'TIPL'; - $ID3v2ShortFrameNameLookup[4]['musician_credits_list'] = 'TMCL'; - $ID3v2ShortFrameNameLookup[4]['mood'] = 'TMOO'; - $ID3v2ShortFrameNameLookup[4]['produced_notice'] = 'TPRO'; - $ID3v2ShortFrameNameLookup[4]['album_sort_order'] = 'TSOA'; - $ID3v2ShortFrameNameLookup[4]['performer_sort_order'] = 'TSOP'; - $ID3v2ShortFrameNameLookup[4]['title_sort_order'] = 'TSOT'; - $ID3v2ShortFrameNameLookup[4]['set_subtitle'] = 'TSST'; - } - return @$ID3v2ShortFrameNameLookup[$majorversion][strtolower($long_description)]; - - } - -} - -?> diff --git a/getid3/getid3/write.lyrics3.php b/getid3/getid3/write.lyrics3.php deleted file mode 100644 index 6b8a47d..0000000 --- a/getid3/getid3/write.lyrics3.php +++ /dev/null @@ -1,78 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// write.lyrics3.php // -// module for writing Lyrics3 tags // -// dependencies: module.tag.lyrics3.php // -// /// -///////////////////////////////////////////////////////////////// - - -class getid3_write_lyrics3 -{ - var $filename; - var $tag_data; - //var $lyrics3_version = 2; // 1 or 2 - var $warnings = array(); // any non-critical errors will be stored here - var $errors = array(); // any critical errors will be stored here - - function getid3_write_lyrics3() { - return true; - } - - function WriteLyrics3() { - $this->errors[] = 'WriteLyrics3() not yet functional - cannot write Lyrics3'; - return false; - } - - function DeleteLyrics3() { - // Initialize getID3 engine - $getID3 = new getID3; - $ThisFileInfo = $getID3->analyze($this->filename); - if (isset($ThisFileInfo['lyrics3']['tag_offset_start']) && isset($ThisFileInfo['lyrics3']['tag_offset_end'])) { - if ($fp = @fopen($this->filename, 'a+b')) { - - flock($fp, LOCK_EX); - $oldignoreuserabort = ignore_user_abort(true); - - fseek($fp, $ThisFileInfo['lyrics3']['tag_offset_end'], SEEK_SET); - $DataAfterLyrics3 = ''; - if ($ThisFileInfo['filesize'] > $ThisFileInfo['lyrics3']['tag_offset_end']) { - $DataAfterLyrics3 = fread($fp, $ThisFileInfo['filesize'] - $ThisFileInfo['lyrics3']['tag_offset_end']); - } - - ftruncate($fp, $ThisFileInfo['lyrics3']['tag_offset_start']); - - if (!empty($DataAfterLyrics3)) { - fseek($fp, $ThisFileInfo['lyrics3']['tag_offset_start'], SEEK_SET); - fwrite($fp, $DataAfterLyrics3, strlen($DataAfterLyrics3)); - } - - flock($fp, LOCK_UN); - fclose($fp); - ignore_user_abort($oldignoreuserabort); - - return true; - - } else { - - $this->errors[] = 'Cannot open "'.$this->filename.'" in "a+b" mode'; - return false; - - } - } - // no Lyrics3 present - return true; - } - - - -} - -?> \ No newline at end of file diff --git a/getid3/getid3/write.metaflac.php b/getid3/getid3/write.metaflac.php deleted file mode 100644 index c5acc8c..0000000 --- a/getid3/getid3/write.metaflac.php +++ /dev/null @@ -1,167 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// write.metaflac.php // -// module for writing metaflac tags // -// dependencies: /helperapps/metaflac.exe // -// /// -///////////////////////////////////////////////////////////////// - - -class getid3_write_metaflac -{ - - var $filename; - var $tag_data; - var $warnings = array(); // any non-critical errors will be stored here - var $errors = array(); // any critical errors will be stored here - - function getid3_write_metaflac() { - return true; - } - - function WriteMetaFLAC() { - - if (!ini_get('safe_mode')) { - - // Create file with new comments - $tempcommentsfilename = tempnam('*', 'getID3'); - if ($fpcomments = @fopen($tempcommentsfilename, 'wb')) { - - foreach ($this->tag_data as $key => $value) { - foreach ($value as $commentdata) { - fwrite($fpcomments, $this->CleanmetaflacName($key).'='.$commentdata."\n"); - } - } - fclose($fpcomments); - - } else { - - $this->errors[] = 'failed to open temporary tags file "'.$tempcommentsfilename.'", tags not written'; - return false; - - } - - $oldignoreuserabort = ignore_user_abort(true); - if (GETID3_OS_ISWINDOWS) { - - if (file_exists(GETID3_HELPERAPPSDIR.'metaflac.exe')) { - //$commandline = '"'.GETID3_HELPERAPPSDIR.'metaflac.exe" --no-utf8-convert --remove-vc-all --import-vc-from="'.$tempcommentsfilename.'" "'.str_replace('/', '\\', $this->filename).'"'; - // metaflac works fine if you copy-paste the above commandline into a command prompt, - // but refuses to work with `backtick` if there are "doublequotes" present around BOTH - // the metaflac pathname and the target filename. For whatever reason...?? - // The solution is simply ensure that the metaflac pathname has no spaces, - // and therefore does not need to be quoted - - // On top of that, if error messages are not always captured properly under Windows - // To at least see if there was a problem, compare file modification timestamps before and after writing - clearstatcache(); - $timestampbeforewriting = filemtime($this->filename); - - $commandline = GETID3_HELPERAPPSDIR.'metaflac.exe --no-utf8-convert --remove-vc-all --import-vc-from="'.$tempcommentsfilename.'" "'.$this->filename.'" 2>&1'; - $metaflacError = `$commandline`; - - if (empty($metaflacError)) { - clearstatcache(); - if ($timestampbeforewriting == filemtime($this->filename)) { - $metaflacError = 'File modification timestamp has not changed - it looks like the tags were not written'; - } - } - } else { - $metaflacError = 'metaflac.exe not found in '.GETID3_HELPERAPPSDIR; - } - - } else { - - // It's simpler on *nix - $commandline = 'metaflac --no-utf8-convert --remove-vc-all --import-vc-from='.$tempcommentsfilename.' "'.$this->filename.'" 2>&1'; - $metaflacError = `$commandline`; - - } - - // Remove temporary comments file - unlink($tempcommentsfilename); - ignore_user_abort($oldignoreuserabort); - - if (!empty($metaflacError)) { - - $this->errors[] = 'System call to metaflac failed with this message returned: '."\n\n".$metaflacError; - return false; - - } - - return true; - } - - $this->errors[] = 'PHP running in Safe Mode (backtick operator not available) - cannot call metaflac, tags not written'; - return false; - } - - - function DeleteMetaFLAC() { - - if (!ini_get('safe_mode')) { - - $oldignoreuserabort = ignore_user_abort(true); - if (GETID3_OS_ISWINDOWS) { - - if (file_exists(GETID3_HELPERAPPSDIR.'metaflac.exe')) { - // To at least see if there was a problem, compare file modification timestamps before and after writing - clearstatcache(); - $timestampbeforewriting = filemtime($this->filename); - - $commandline = GETID3_HELPERAPPSDIR.'metaflac.exe --remove-vc-all "'.$this->filename.'" 2>&1'; - $metaflacError = `$commandline`; - - if (empty($metaflacError)) { - clearstatcache(); - if ($timestampbeforewriting == filemtime($this->filename)) { - $metaflacError = 'File modification timestamp has not changed - it looks like the tags were not deleted'; - } - } - } else { - $metaflacError = 'metaflac.exe not found in '.GETID3_HELPERAPPSDIR; - } - - } else { - - // It's simpler on *nix - $commandline = 'metaflac --remove-vc-all "'.$this->filename.'" 2>&1'; - $metaflacError = `$commandline`; - - } - - ignore_user_abort($oldignoreuserabort); - - if (!empty($metaflacError)) { - $this->errors[] = 'System call to metaflac failed with this message returned: '."\n\n".$metaflacError; - return false; - } - return true; - } - $this->errors[] = 'PHP running in Safe Mode (backtick operator not available) - cannot call metaflac, tags not deleted'; - return false; - } - - - function CleanmetaflacName($originalcommentname) { - // A case-insensitive field name that may consist of ASCII 0x20 through 0x7D, 0x3D ('=') excluded. - // ASCII 0x41 through 0x5A inclusive (A-Z) is to be considered equivalent to ASCII 0x61 through - // 0x7A inclusive (a-z). - - // replace invalid chars with a space, return uppercase text - // Thanks Chris Bolt for improving this function - // note: ereg_replace() replaces nulls with empty string (not space) - return strtoupper(ereg_replace('[^ -<>-}]', ' ', str_replace("\x00", ' ', $originalcommentname))); - - } - -} - -?> \ No newline at end of file diff --git a/getid3/getid3/write.php b/getid3/getid3/write.php deleted file mode 100644 index 73e2610..0000000 --- a/getid3/getid3/write.php +++ /dev/null @@ -1,592 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -/// // -// write.php // -// module for writing tags (APEv2, ID3v1, ID3v2) // -// dependencies: getid3.lib.php // -// write.apetag.php (optional) // -// write.id3v1.php (optional) // -// write.id3v2.php (optional) // -// write.vorbiscomment.php (optional) // -// write.metaflac.php (optional) // -// write.lyrics3.php (optional) // -// /// -///////////////////////////////////////////////////////////////// - -if (!defined('GETID3_INCLUDEPATH')) { - die('getid3.php MUST be included before calling getid3_writetags'); -} -if (!include_once(GETID3_INCLUDEPATH.'getid3.lib.php')) { - die('write.php depends on getid3.lib.php, which is missing.'); -} - - -// NOTES: -// -// You should pass data here with standard field names as follows: -// * TITLE -// * ARTIST -// * ALBUM -// * TRACKNUMBER -// * COMMENT -// * GENRE -// * YEAR -// * ATTACHED_PICTURE (ID3v2 only) -// -// http://www.personal.uni-jena.de/~pfk/mpp/sv8/apekey.html -// The APEv2 Tag Items Keys definition says "TRACK" is correct but foobar2000 uses "TRACKNUMBER" instead -// Pass data here as "TRACKNUMBER" for compatability with all formats - - -class getid3_writetags -{ - // public - var $filename; // absolute filename of file to write tags to - var $tagformats = array(); // array of tag formats to write ('id3v1', 'id3v2.2', 'id2v2.3', 'id3v2.4', 'ape', 'vorbiscomment', 'metaflac', 'real') - var $tag_data = array(array()); // 2-dimensional array of tag data (ex: $data['ARTIST'][0] = 'Elvis') - var $tag_encoding = 'ISO-8859-1'; // text encoding used for tag data ('ISO-8859-1', 'UTF-8', 'UTF-16', 'UTF-16LE', 'UTF-16BE', ) - var $overwrite_tags = true; // if true will erase existing tag data and write only passed data; if false will merge passed data with existing tag data - var $remove_other_tags = false; // if true will erase remove all existing tags and only write those passed in $tagformats; if false will ignore any tags not mentioned in $tagformats - - var $id3v2_tag_language = 'eng'; // ISO-639-2 3-character language code needed for some ID3v2 frames (http://www.id3.org/iso639-2.html) - var $id3v2_paddedlength = 4096; // minimum length of ID3v2 tags (will be padded to this length if tag data is shorter) - - var $warnings = array(); // any non-critical errors will be stored here - var $errors = array(); // any critical errors will be stored here - - // private - var $ThisFileInfo; // analysis of file before writing - - function getid3_writetags() { - return true; - } - - - function WriteTags() { - - if (empty($this->filename)) { - $this->errors[] = 'filename is undefined in getid3_writetags'; - return false; - } elseif (!file_exists($this->filename)) { - $this->errors[] = 'filename set to non-existant file "'.$this->filename.'" in getid3_writetags'; - return false; - } - - if (!is_array($this->tagformats)) { - $this->errors[] = 'tagformats must be an array in getid3_writetags'; - return false; - } - - $TagFormatsToRemove = array(); - if (filesize($this->filename) == 0) { - - // empty file special case - allow any tag format, don't check existing format - // could be useful if you want to generate tag data for a non-existant file - $this->ThisFileInfo = array('fileformat'=>''); - $AllowedTagFormats = array('id3v1', 'id3v2.2', 'id3v2.3', 'id3v2.4', 'ape', 'lyrics3'); - - } else { - - $getID3 = new getID3; - $getID3->encoding = $this->tag_encoding; - $this->ThisFileInfo = $getID3->analyze($this->filename); - - // check for what file types are allowed on this fileformat - switch (@$this->ThisFileInfo['fileformat']) { - case 'mp3': - case 'mp2': - case 'mp1': - case 'riff': // maybe not officially, but people do it anyway - $AllowedTagFormats = array('id3v1', 'id3v2.2', 'id3v2.3', 'id3v2.4', 'ape', 'lyrics3'); - break; - - case 'mpc': - $AllowedTagFormats = array('ape'); - break; - - case 'flac': - $AllowedTagFormats = array('metaflac'); - break; - - case 'real': - $AllowedTagFormats = array('real'); - break; - - case 'ogg': - switch (@$this->ThisFileInfo['audio']['dataformat']) { - case 'flac': - //$AllowedTagFormats = array('metaflac'); - $this->errors[] = 'metaflac is not (yet) compatible with OggFLAC files'; - return false; - break; - case 'vorbis': - $AllowedTagFormats = array('vorbiscomment'); - break; - default: - $this->errors[] = 'metaflac is not (yet) compatible with Ogg files other than OggVorbis'; - return false; - break; - } - break; - - default: - $AllowedTagFormats = array(); - break; - } - foreach ($this->tagformats as $requested_tag_format) { - if (!in_array($requested_tag_format, $AllowedTagFormats)) { - $errormessage = 'Tag format "'.$requested_tag_format.'" is not allowed on "'.@$this->ThisFileInfo['fileformat']; - if (@$this->ThisFileInfo['fileformat'] != @$this->ThisFileInfo['audio']['dataformat']) { - $errormessage .= '.'.@$this->ThisFileInfo['audio']['dataformat']; - } - $errormessage .= '" files'; - $this->errors[] = $errormessage; - return false; - } - } - - // List of other tag formats, removed if requested - if ($this->remove_other_tags) { - foreach ($AllowedTagFormats as $AllowedTagFormat) { - switch ($AllowedTagFormat) { - case 'id3v2.2': - case 'id3v2.3': - case 'id3v2.4': - if (!in_array('id3v2', $TagFormatsToRemove) && !in_array('id3v2.2', $this->tagformats) && !in_array('id3v2.3', $this->tagformats) && !in_array('id3v2.4', $this->tagformats)) { - $TagFormatsToRemove[] = 'id3v2'; - } - break; - - default: - if (!in_array($AllowedTagFormat, $this->tagformats)) { - $TagFormatsToRemove[] = $AllowedTagFormat; - } - break; - } - } - } - } - - $WritingFilesToInclude = array_merge($this->tagformats, $TagFormatsToRemove); - - // Check for required include files and include them - foreach ($WritingFilesToInclude as $tagformat) { - switch ($tagformat) { - case 'ape': - $GETID3_ERRORARRAY = &$this->errors; - if (!getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.apetag.php', __FILE__, false)) { - return false; - } - break; - - case 'id3v1': - case 'lyrics3': - case 'vorbiscomment': - case 'metaflac': - case 'real': - $GETID3_ERRORARRAY = &$this->errors; - if (!getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.'.$tagformat.'.php', __FILE__, false)) { - return false; - } - break; - - case 'id3v2.2': - case 'id3v2.3': - case 'id3v2.4': - case 'id3v2': - $GETID3_ERRORARRAY = &$this->errors; - if (!getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.id3v2.php', __FILE__, false)) { - return false; - } - break; - - default: - $this->errors[] = 'unknown tag format "'.$tagformat.'" in $tagformats in WriteTags()'; - return false; - break; - } - - } - - // Validation of supplied data - if (!is_array($this->tag_data)) { - $this->errors[] = '$tag_data is not an array in WriteTags()'; - return false; - } - // convert supplied data array keys to upper case, if they're not already - foreach ($this->tag_data as $tag_key => $tag_array) { - if (strtoupper($tag_key) !== $tag_key) { - $this->tag_data[strtoupper($tag_key)] = $this->tag_data[$tag_key]; - unset($this->tag_data[$tag_key]); - } - } - // convert source data array keys to upper case, if they're not already - if (!empty($this->ThisFileInfo['tags'])) { - foreach ($this->ThisFileInfo['tags'] as $tag_format => $tag_data_array) { - foreach ($tag_data_array as $tag_key => $tag_array) { - if (strtoupper($tag_key) !== $tag_key) { - $this->ThisFileInfo['tags'][$tag_format][strtoupper($tag_key)] = $this->ThisFileInfo['tags'][$tag_format][$tag_key]; - unset($this->ThisFileInfo['tags'][$tag_format][$tag_key]); - } - } - } - } - - // Convert "TRACK" to "TRACKNUMBER" (if needed) for compatability with all formats - if (isset($this->tag_data['TRACK']) && !isset($this->tag_data['TRACKNUMBER'])) { - $this->tag_data['TRACKNUMBER'] = $this->tag_data['TRACK']; - unset($this->tag_data['TRACK']); - } - - // Remove all other tag formats, if requested - if ($this->remove_other_tags) { - $this->DeleteTags($TagFormatsToRemove); - } - - // Write data for each tag format - foreach ($this->tagformats as $tagformat) { - $success = false; // overridden if tag writing is successful - switch ($tagformat) { - case 'ape': - $ape_writer = new getid3_write_apetag; - if (($ape_writer->tag_data = $this->FormatDataForAPE()) !== false) { - $ape_writer->filename = $this->filename; - if (($success = $ape_writer->WriteAPEtag()) === false) { - $this->errors[] = 'WriteAPEtag() failed with message(s):
  • '.trim(implode('
  • ', $ape_writer->errors)).'
'; - } - } else { - $this->errors[] = 'FormatDataForAPE() failed'; - } - break; - - case 'id3v1': - $id3v1_writer = new getid3_write_id3v1; - if (($id3v1_writer->tag_data = $this->FormatDataForID3v1()) !== false) { - $id3v1_writer->filename = $this->filename; - if (($success = $id3v1_writer->WriteID3v1()) === false) { - $this->errors[] = 'WriteID3v1() failed with message(s):
  • '.trim(implode('
  • ', $id3v1_writer->errors)).'
'; - } - } else { - $this->errors[] = 'FormatDataForID3v1() failed'; - } - break; - - case 'id3v2.2': - case 'id3v2.3': - case 'id3v2.4': - $id3v2_writer = new getid3_write_id3v2; - $id3v2_writer->majorversion = intval(substr($tagformat, -1)); - $id3v2_writer->paddedlength = $this->id3v2_paddedlength; - if (($id3v2_writer->tag_data = $this->FormatDataForID3v2($id3v2_writer->majorversion)) !== false) { - $id3v2_writer->filename = $this->filename; - if (($success = $id3v2_writer->WriteID3v2()) === false) { - $this->errors[] = 'WriteID3v2() failed with message(s):
  • '.trim(implode('
  • ', $id3v2_writer->errors)).'
'; - } - } else { - $this->errors[] = 'FormatDataForID3v2() failed'; - } - break; - - case 'vorbiscomment': - $vorbiscomment_writer = new getid3_write_vorbiscomment; - if (($vorbiscomment_writer->tag_data = $this->FormatDataForVorbisComment()) !== false) { - $vorbiscomment_writer->filename = $this->filename; - if (($success = $vorbiscomment_writer->WriteVorbisComment()) === false) { - $this->errors[] = 'WriteVorbisComment() failed with message(s):
  • '.trim(implode('
  • ', $vorbiscomment_writer->errors)).'
'; - } - } else { - $this->errors[] = 'FormatDataForVorbisComment() failed'; - } - break; - - case 'metaflac': - $metaflac_writer = new getid3_write_metaflac; - if (($metaflac_writer->tag_data = $this->FormatDataForMetaFLAC()) !== false) { - $metaflac_writer->filename = $this->filename; - if (($success = $metaflac_writer->WriteMetaFLAC()) === false) { - $this->errors[] = 'WriteMetaFLAC() failed with message(s):
  • '.trim(implode('
  • ', $metaflac_writer->errors)).'
'; - } - } else { - $this->errors[] = 'FormatDataForMetaFLAC() failed'; - } - break; - - case 'real': - $real_writer = new getid3_write_real; - if (($real_writer->tag_data = $this->FormatDataForReal()) !== false) { - $real_writer->filename = $this->filename; - if (($success = $real_writer->WriteReal()) === false) { - $this->errors[] = 'WriteReal() failed with message(s):
  • '.trim(implode('
  • ', $real_writer->errors)).'
'; - } - } else { - $this->errors[] = 'FormatDataForReal() failed'; - } - break; - - default: - $this->errors[] = 'Invalid tag format to write: "'.$tagformat.'"'; - return false; - break; - } - if (!$success) { - return false; - } - } - return true; - - } - - - function DeleteTags($TagFormatsToDelete) { - foreach ($TagFormatsToDelete as $DeleteTagFormat) { - $success = false; // overridden if tag deletion is successful - switch ($DeleteTagFormat) { - case 'id3v1': - $id3v1_writer = new getid3_write_id3v1; - $id3v1_writer->filename = $this->filename; - if (($success = $id3v1_writer->RemoveID3v1()) === false) { - $this->errors[] = 'RemoveID3v1() failed with message(s):
  • '.trim(implode('
  • ', $id3v1_writer->errors)).'
'; - } - break; - - case 'id3v2': - $id3v2_writer = new getid3_write_id3v2; - $id3v2_writer->filename = $this->filename; - if (($success = $id3v2_writer->RemoveID3v2()) === false) { - $this->errors[] = 'RemoveID3v2() failed with message(s):
  • '.trim(implode('
  • ', $id3v2_writer->errors)).'
'; - } - break; - - case 'ape': - $ape_writer = new getid3_write_apetag; - $ape_writer->filename = $this->filename; - if (($success = $ape_writer->DeleteAPEtag()) === false) { - $this->errors[] = 'DeleteAPEtag() failed with message(s):
  • '.trim(implode('
  • ', $ape_writer->errors)).'
'; - } - break; - - case 'vorbiscomment': - $vorbiscomment_writer = new getid3_write_vorbiscomment; - $vorbiscomment_writer->filename = $this->filename; - if (($success = $vorbiscomment_writer->DeleteVorbisComment()) === false) { - $this->errors[] = 'DeleteVorbisComment() failed with message(s):
  • '.trim(implode('
  • ', $vorbiscomment_writer->errors)).'
'; - } - break; - - case 'metaflac': - $metaflac_writer = new getid3_write_metaflac; - $metaflac_writer->filename = $this->filename; - if (($success = $metaflac_writer->DeleteMetaFLAC()) === false) { - $this->errors[] = 'DeleteMetaFLAC() failed with message(s):
  • '.trim(implode('
  • ', $metaflac_writer->errors)).'
'; - } - break; - - case 'lyrics3': - $lyrics3_writer = new getid3_write_lyrics3; - $lyrics3_writer->filename = $this->filename; - if (($success = $lyrics3_writer->DeleteLyrics3()) === false) { - $this->errors[] = 'DeleteLyrics3() failed with message(s):
  • '.trim(implode('
  • ', $lyrics3_writer->errors)).'
'; - } - break; - - case 'real': - $real_writer = new getid3_write_real; - $real_writer->filename = $this->filename; - if (($success = $real_writer->RemoveReal()) === false) { - $this->errors[] = 'RemoveReal() failed with message(s):
  • '.trim(implode('
  • ', $real_writer->errors)).'
'; - } - break; - - default: - $this->errors[] = 'Invalid tag format to delete: "'.$tagformat.'"'; - return false; - break; - } - if (!$success) { - return false; - } - } - return true; - } - - - function MergeExistingTagData($TagFormat, &$tag_data) { - // Merge supplied data with existing data, if requested - if ($this->overwrite_tags) { - // do nothing - ignore previous data - } else { - if (!isset($this->ThisFileInfo['tags'][$TagFormat])) { - return false; - } - $tag_data = array_merge_recursive($tag_data, $this->ThisFileInfo['tags'][$TagFormat]); - } - return true; - } - - function FormatDataForAPE() { - $ape_tag_data = array(); - foreach ($this->tag_data as $tag_key => $valuearray) { - switch ($tag_key) { - case 'ATTACHED_PICTURE': - // ATTACHED_PICTURE is ID3v2 only - ignore - $this->warnings[] = '$data['.$tag_key.'] is assumed to be ID3v2 APIC data - NOT written to APE tag'; - break; - - default: - foreach ($valuearray as $key => $value) { - if (is_string($value) || is_numeric($value)) { - $ape_tag_data[$tag_key][$key] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-8', $value); - } else { - $this->warnings[] = '$data['.$tag_key.']['.$key.'] is not a string value - all of $data['.$tag_key.'] NOT written to APE tag'; - unset($ape_tag_data[$tag_key]); - break; - } - } - break; - } - } - $this->MergeExistingTagData('ape', $ape_tag_data); - return $ape_tag_data; - } - - - function FormatDataForID3v1() { - $tag_data_id3v1['genreid'] = 255; - if (!empty($this->tag_data['GENRE'])) { - foreach ($this->tag_data['GENRE'] as $key => $value) { - if (getid3_id3v1::LookupGenreID($value) !== false) { - $tag_data_id3v1['genreid'] = getid3_id3v1::LookupGenreID($value); - break; - } - } - } - - $tag_data_id3v1['title'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['TITLE'])); - $tag_data_id3v1['artist'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['ARTIST'])); - $tag_data_id3v1['album'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['ALBUM'])); - $tag_data_id3v1['year'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['YEAR'])); - $tag_data_id3v1['comment'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['COMMENT'])); - - $tag_data_id3v1['track'] = intval(getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['TRACKNUMBER']))); - if ($tag_data_id3v1['track'] <= 0) { - $tag_data_id3v1['track'] = ''; - } - - $this->MergeExistingTagData('id3v1', $tag_data_id3v1); - return $tag_data_id3v1; - } - - function FormatDataForID3v2($id3v2_majorversion) { - $tag_data_id3v2 = array(); - - $ID3v2_text_encoding_lookup[2] = array('ISO-8859-1'=>0, 'UTF-16'=>1); - $ID3v2_text_encoding_lookup[3] = array('ISO-8859-1'=>0, 'UTF-16'=>1); - $ID3v2_text_encoding_lookup[4] = array('ISO-8859-1'=>0, 'UTF-16'=>1, 'UTF-16BE'=>2, 'UTF-8'=>3); - foreach ($this->tag_data as $tag_key => $valuearray) { - $ID3v2_framename = getid3_write_id3v2::ID3v2ShortFrameNameLookup($id3v2_majorversion, $tag_key); - switch ($ID3v2_framename) { - case 'APIC': - foreach ($valuearray as $key => $apic_data_array) { - if (isset($apic_data_array['data']) && - isset($apic_data_array['picturetypeid']) && - isset($apic_data_array['description']) && - isset($apic_data_array['mime'])) { - $tag_data_id3v2['APIC'][] = $apic_data_array; - } else { - $this->errors[] = 'ID3v2 APIC data is not properly structured'; - return false; - } - } - break; - - case '': - $this->errors[] = 'ID3v2: Skipping "'.$tag_key.'" because cannot match it to a known ID3v2 frame type'; - // some other data type, don't know how to handle it, ignore it - break; - - default: - // most other (text) frames can be copied over as-is - foreach ($valuearray as $key => $value) { - if (isset($ID3v2_text_encoding_lookup[$id3v2_majorversion][$this->tag_encoding])) { - // source encoding is valid in ID3v2 - use it with no conversion - $tag_data_id3v2[$ID3v2_framename][$key]['encodingid'] = $ID3v2_text_encoding_lookup[$id3v2_majorversion][$this->tag_encoding]; - $tag_data_id3v2[$ID3v2_framename][$key]['data'] = $value; - } else { - // source encoding is NOT valid in ID3v2 - convert it to an ID3v2-valid encoding first - if ($id3v2_majorversion < 4) { - // convert data from other encoding to UTF-16 - $tag_data_id3v2[$ID3v2_framename][$key]['encodingid'] = 1; - $tag_data_id3v2[$ID3v2_framename][$key]['data'] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-16', $value); - - } else { - // convert data from other encoding to UTF-8 - $tag_data_id3v2[$ID3v2_framename][$key]['encodingid'] = 3; - $tag_data_id3v2[$ID3v2_framename][$key]['data'] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-8', $value); - } - } - - // These values are not needed for all frame types, but if they're not used no matter - $tag_data_id3v2[$ID3v2_framename][$key]['description'] = ''; - $tag_data_id3v2[$ID3v2_framename][$key]['language'] = $this->id3v2_tag_language; - } - break; - } - } - $this->MergeExistingTagData('id3v2', $tag_data_id3v2); - return $tag_data_id3v2; - } - - function FormatDataForVorbisComment() { - $tag_data_vorbiscomment = $this->tag_data; - - // check for multi-line comment values - split out to multiple comments if neccesary - // and convert data to UTF-8 strings - foreach ($tag_data_vorbiscomment as $tag_key => $valuearray) { - foreach ($valuearray as $key => $value) { - str_replace("\r", "\n", $value); - if (strstr($value, "\n")) { - unset($tag_data_vorbiscomment[$tag_key][$key]); - $multilineexploded = explode("\n", $value); - foreach ($multilineexploded as $newcomment) { - if (strlen(trim($newcomment)) > 0) { - $tag_data_vorbiscomment[$tag_key][] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-8', $newcomment); - } - } - } elseif (is_string($value) || is_numeric($value)) { - $tag_data_vorbiscomment[$tag_key][$key] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-8', $value); - } else { - $this->warnings[] = '$data['.$tag_key.']['.$key.'] is not a string value - all of $data['.$tag_key.'] NOT written to VorbisComment tag'; - unset($tag_data_vorbiscomment[$tag_key]); - break; - } - } - } - $this->MergeExistingTagData('vorbiscomment', $tag_data_vorbiscomment); - return $tag_data_vorbiscomment; - } - - function FormatDataForMetaFLAC() { - // FLAC & OggFLAC use VorbisComments same as OggVorbis - // but require metaflac to do the writing rather than vorbiscomment - return $this->FormatDataForVorbisComment(); - } - - function FormatDataForReal() { - $tag_data_real['title'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['TITLE'])); - $tag_data_real['artist'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['ARTIST'])); - $tag_data_real['copyright'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['COPYRIGHT'])); - $tag_data_real['comment'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['COMMENT'])); - - $this->MergeExistingTagData('real', $tag_data_real); - return $tag_data_real; - } - -} - -?> \ No newline at end of file diff --git a/getid3/getid3/write.real.php b/getid3/getid3/write.real.php deleted file mode 100644 index 1e0240c..0000000 --- a/getid3/getid3/write.real.php +++ /dev/null @@ -1,295 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// write.real.php // -// module for writing RealAudio/RealVideo tags // -// dependencies: module.tag.real.php // -// /// -///////////////////////////////////////////////////////////////// - -class getid3_write_real -{ - var $filename; - var $tag_data = array(); - var $warnings = array(); // any non-critical errors will be stored here - var $errors = array(); // any critical errors will be stored here - var $paddedlength = 512; // minimum length of CONT tag in bytes - - function getid3_write_real() { - return true; - } - - function WriteReal() { - // File MUST be writeable - CHMOD(646) at least - if (is_writeable($this->filename)) { - if ($fp_source = @fopen($this->filename, 'r+b')) { - - // Initialize getID3 engine - $getID3 = new getID3; - $OldThisFileInfo = $getID3->analyze($this->filename); - if (empty($OldThisFileInfo['real']['chunks']) && !empty($OldThisFileInfo['real']['old_ra_header'])) { - $this->errors[] = 'Cannot write Real tags on old-style file format'; - fclose($fp_source); - return false; - } - - if (empty($OldThisFileInfo['real']['chunks'])) { - $this->errors[] = 'Cannot write Real tags because cannot find DATA chunk in file'; - fclose($fp_source); - return false; - } - foreach ($OldThisFileInfo['real']['chunks'] as $chunknumber => $chunkarray) { - $oldChunkInfo[$chunkarray['name']] = $chunkarray; - } - if (!empty($oldChunkInfo['CONT']['length'])) { - $this->paddedlength = max($oldChunkInfo['CONT']['length'], $this->paddedlength); - } - - $new_CONT_tag_data = $this->GenerateCONTchunk(); - $new_PROP_tag_data = $this->GeneratePROPchunk($OldThisFileInfo['real']['chunks'], $new_CONT_tag_data); - $new__RMF_tag_data = $this->GenerateRMFchunk($OldThisFileInfo['real']['chunks']); - - if (@$oldChunkInfo['.RMF']['length'] == strlen($new__RMF_tag_data)) { - fseek($fp_source, $oldChunkInfo['.RMF']['offset'], SEEK_SET); - fwrite($fp_source, $new__RMF_tag_data); - } else { - $this->errors[] = 'new .RMF tag ('.strlen($new__RMF_tag_data).' bytes) different length than old .RMF tag ('.$oldChunkInfo['.RMF']['length'].' bytes)'; - fclose($fp_source); - return false; - } - - if (@$oldChunkInfo['PROP']['length'] == strlen($new_PROP_tag_data)) { - fseek($fp_source, $oldChunkInfo['PROP']['offset'], SEEK_SET); - fwrite($fp_source, $new_PROP_tag_data); - } else { - $this->errors[] = 'new PROP tag ('.strlen($new_PROP_tag_data).' bytes) different length than old PROP tag ('.$oldChunkInfo['PROP']['length'].' bytes)'; - fclose($fp_source); - return false; - } - - if (@$oldChunkInfo['CONT']['length'] == strlen($new_CONT_tag_data)) { - - // new data length is same as old data length - just overwrite - fseek($fp_source, $oldChunkInfo['CONT']['offset'], SEEK_SET); - fwrite($fp_source, $new_CONT_tag_data); - fclose($fp_source); - return true; - - } else { - - if (empty($oldChunkInfo['CONT'])) { - // no existing CONT chunk - $BeforeOffset = $oldChunkInfo['DATA']['offset']; - $AfterOffset = $oldChunkInfo['DATA']['offset']; - } else { - // new data is longer than old data - $BeforeOffset = $oldChunkInfo['CONT']['offset']; - $AfterOffset = $oldChunkInfo['CONT']['offset'] + $oldChunkInfo['CONT']['length']; - } - if ($tempfilename = tempnam('*', 'getID3')) { - ob_start(); - if ($fp_temp = fopen($tempfilename, 'wb')) { - - rewind($fp_source); - fwrite($fp_temp, fread($fp_source, $BeforeOffset)); - fwrite($fp_temp, $new_CONT_tag_data); - fseek($fp_source, $AfterOffset, SEEK_SET); - while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) { - fwrite($fp_temp, $buffer, strlen($buffer)); - } - fclose($fp_temp); - - if (copy($tempfilename, $this->filename)) { - unlink($tempfilename); - fclose($fp_source); - return true; - } - unlink($tempfilename); - $this->errors[] = 'FAILED: copy('.$tempfilename.', '.$this->filename.') - '.strip_tags(ob_get_contents()); - - } else { - - $this->errors[] = 'Could not open '.$tempfilename.' mode "wb" - '.strip_tags(ob_get_contents()); - - } - ob_end_clean(); - } - fclose($fp_source); - return false; - - } - - - } else { - $this->errors[] = 'Could not open '.$this->filename.' mode "r+b"'; - return false; - } - } - $this->errors[] = 'File is not writeable: '.$this->filename; - return false; - } - - function GenerateRMFchunk(&$chunks) { - $oldCONTexists = false; - foreach ($chunks as $key => $chunk) { - $chunkNameKeys[$chunk['name']] = $key; - if ($chunk['name'] == 'CONT') { - $oldCONTexists = true; - } - } - $newHeadersCount = $chunks[$chunkNameKeys['.RMF']]['headers_count'] + ($oldCONTexists ? 0 : 1); - - $RMFchunk = "\x00\x00"; // object version - $RMFchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['.RMF']]['file_version'], 4); - $RMFchunk .= getid3_lib::BigEndian2String($newHeadersCount, 4); - - $RMFchunk = '.RMF'.getid3_lib::BigEndian2String(strlen($RMFchunk) + 8, 4).$RMFchunk; // .RMF chunk identifier + chunk length - return $RMFchunk; - } - - function GeneratePROPchunk(&$chunks, &$new_CONT_tag_data) { - $old_CONT_length = 0; - $old_DATA_offset = 0; - $old_INDX_offset = 0; - foreach ($chunks as $key => $chunk) { - $chunkNameKeys[$chunk['name']] = $key; - if ($chunk['name'] == 'CONT') { - $old_CONT_length = $chunk['length']; - } elseif ($chunk['name'] == 'DATA') { - if (!$old_DATA_offset) { - $old_DATA_offset = $chunk['offset']; - } - } elseif ($chunk['name'] == 'INDX') { - if (!$old_INDX_offset) { - $old_INDX_offset = $chunk['offset']; - } - } - } - $CONTdelta = strlen($new_CONT_tag_data) - $old_CONT_length; - - $PROPchunk = "\x00\x00"; // object version - $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['max_bit_rate'], 4); - $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['avg_bit_rate'], 4); - $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['max_packet_size'], 4); - $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['avg_packet_size'], 4); - $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['num_packets'], 4); - $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['duration'], 4); - $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['preroll'], 4); - $PROPchunk .= getid3_lib::BigEndian2String(max(0, $old_INDX_offset + $CONTdelta), 4); - $PROPchunk .= getid3_lib::BigEndian2String(max(0, $old_DATA_offset + $CONTdelta), 4); - $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['num_streams'], 2); - $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['flags_raw'], 2); - - $PROPchunk = 'PROP'.getid3_lib::BigEndian2String(strlen($PROPchunk) + 8, 4).$PROPchunk; // PROP chunk identifier + chunk length - return $PROPchunk; - } - - function GenerateCONTchunk() { - foreach ($this->tag_data as $key => $value) { - // limit each value to 0xFFFF bytes - $this->tag_data[$key] = substr($value, 0, 65535); - } - - $CONTchunk = "\x00\x00"; // object version - - $CONTchunk .= getid3_lib::BigEndian2String(strlen(@$this->tag_data['title']), 2); - $CONTchunk .= @$this->tag_data['title']; - - $CONTchunk .= getid3_lib::BigEndian2String(strlen(@$this->tag_data['artist']), 2); - $CONTchunk .= @$this->tag_data['artist']; - - $CONTchunk .= getid3_lib::BigEndian2String(strlen(@$this->tag_data['copyright']), 2); - $CONTchunk .= @$this->tag_data['copyright']; - - $CONTchunk .= getid3_lib::BigEndian2String(strlen(@$this->tag_data['comment']), 2); - $CONTchunk .= @$this->tag_data['comment']; - - if ($this->paddedlength > (strlen($CONTchunk) + 8)) { - $CONTchunk .= str_repeat("\x00", $this->paddedlength - strlen($CONTchunk) - 8); - } - - $CONTchunk = 'CONT'.getid3_lib::BigEndian2String(strlen($CONTchunk) + 8, 4).$CONTchunk; // CONT chunk identifier + chunk length - - return $CONTchunk; - } - - function RemoveReal() { - // File MUST be writeable - CHMOD(646) at least - if (is_writeable($this->filename)) { - if ($fp_source = @fopen($this->filename, 'r+b')) { - - // Initialize getID3 engine - $getID3 = new getID3; - $OldThisFileInfo = $getID3->analyze($this->filename); - if (empty($OldThisFileInfo['real']['chunks']) && !empty($OldThisFileInfo['real']['old_ra_header'])) { - $this->errors[] = 'Cannot remove Real tags from old-style file format'; - fclose($fp_source); - return false; - } - - if (empty($OldThisFileInfo['real']['chunks'])) { - $this->errors[] = 'Cannot remove Real tags because cannot find DATA chunk in file'; - fclose($fp_source); - return false; - } - foreach ($OldThisFileInfo['real']['chunks'] as $chunknumber => $chunkarray) { - $oldChunkInfo[$chunkarray['name']] = $chunkarray; - } - - if (empty($oldChunkInfo['CONT'])) { - // no existing CONT chunk - fclose($fp_source); - return true; - } - - $BeforeOffset = $oldChunkInfo['CONT']['offset']; - $AfterOffset = $oldChunkInfo['CONT']['offset'] + $oldChunkInfo['CONT']['length']; - if ($tempfilename = tempnam('*', 'getID3')) { - ob_start(); - if ($fp_temp = fopen($tempfilename, 'wb')) { - - rewind($fp_source); - fwrite($fp_temp, fread($fp_source, $BeforeOffset)); - fseek($fp_source, $AfterOffset, SEEK_SET); - while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) { - fwrite($fp_temp, $buffer, strlen($buffer)); - } - fclose($fp_temp); - - if (copy($tempfilename, $this->filename)) { - unlink($tempfilename); - fclose($fp_source); - return true; - } - unlink($tempfilename); - $this->errors[] = 'FAILED: copy('.$tempfilename.', '.$this->filename.') - '.strip_tags(ob_get_contents()); - - } else { - - $this->errors[] = 'Could not open '.$tempfilename.' mode "wb" - '.strip_tags(ob_get_contents()); - - } - ob_end_clean(); - } - fclose($fp_source); - return false; - - - } else { - $this->errors[] = 'Could not open '.$this->filename.' mode "r+b"'; - return false; - } - } - $this->errors[] = 'File is not writeable: '.$this->filename; - return false; - } - -} - -?> \ No newline at end of file diff --git a/getid3/getid3/write.vorbiscomment.php b/getid3/getid3/write.vorbiscomment.php deleted file mode 100644 index f93b1a1..0000000 --- a/getid3/getid3/write.vorbiscomment.php +++ /dev/null @@ -1,124 +0,0 @@ - // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// See readme.txt for more details // -///////////////////////////////////////////////////////////////// -// // -// write.vorbiscomment.php // -// module for writing VorbisComment tags // -// dependencies: /helperapps/vorbiscomment.exe // -// /// -///////////////////////////////////////////////////////////////// - - -class getid3_write_vorbiscomment -{ - - var $filename; - var $tag_data; - var $warnings = array(); // any non-critical errors will be stored here - var $errors = array(); // any critical errors will be stored here - - function getid3_write_vorbiscomment() { - return true; - } - - function WriteVorbisComment() { - - if (!ini_get('safe_mode')) { - - // Create file with new comments - $tempcommentsfilename = tempnam('*', 'getID3'); - if ($fpcomments = @fopen($tempcommentsfilename, 'wb')) { - - foreach ($this->tag_data as $key => $value) { - foreach ($value as $commentdata) { - fwrite($fpcomments, $this->CleanVorbisCommentName($key).'='.$commentdata."\n"); - } - } - fclose($fpcomments); - - } else { - - $this->errors[] = 'failed to open temporary tags file "'.$tempcommentsfilename.'", tags not written'; - return false; - - } - - $oldignoreuserabort = ignore_user_abort(true); - if (GETID3_OS_ISWINDOWS) { - - if (file_exists(GETID3_HELPERAPPSDIR.'vorbiscomment.exe')) { - //$commandline = '"'.GETID3_HELPERAPPSDIR.'vorbiscomment.exe" -w --raw -c "'.$tempcommentsfilename.'" "'.str_replace('/', '\\', $this->filename).'"'; - // vorbiscomment works fine if you copy-paste the above commandline into a command prompt, - // but refuses to work with `backtick` if there are "doublequotes" present around BOTH - // the metaflac pathname and the target filename. For whatever reason...?? - // The solution is simply ensure that the metaflac pathname has no spaces, - // and therefore does not need to be quoted - - // On top of that, if error messages are not always captured properly under Windows - // To at least see if there was a problem, compare file modification timestamps before and after writing - clearstatcache(); - $timestampbeforewriting = filemtime($this->filename); - - $commandline = GETID3_HELPERAPPSDIR.'vorbiscomment.exe -w --raw -c "'.$tempcommentsfilename.'" "'.$this->filename.'" 2>&1'; - $VorbiscommentError = `$commandline`; - - if (empty($VorbiscommentError)) { - clearstatcache(); - if ($timestampbeforewriting == filemtime($this->filename)) { - $VorbiscommentError = 'File modification timestamp has not changed - it looks like the tags were not written'; - } - } - } else { - $VorbiscommentError = 'vorbiscomment.exe not found in '.GETID3_HELPERAPPSDIR; - } - - } else { - - $commandline = 'vorbiscomment -w --raw -c "'.$tempcommentsfilename.'" "'.$this->filename.'" 2>&1'; - $VorbiscommentError = `$commandline`; - - } - - // Remove temporary comments file - unlink($tempcommentsfilename); - ignore_user_abort($oldignoreuserabort); - - if (!empty($VorbiscommentError)) { - - $this->errors[] = 'system call to vorbiscomment failed with message: '."\n\n".$VorbiscommentError; - return false; - - } - - return true; - } - - $this->errors[] = 'PHP running in Safe Mode (backtick operator not available) - cannot call vorbiscomment, tags not written'; - return false; - } - - function DeleteVorbisComment() { - $this->tag_data = array(array()); - return $this->WriteVorbisComment(); - } - - function CleanVorbisCommentName($originalcommentname) { - // A case-insensitive field name that may consist of ASCII 0x20 through 0x7D, 0x3D ('=') excluded. - // ASCII 0x41 through 0x5A inclusive (A-Z) is to be considered equivalent to ASCII 0x61 through - // 0x7A inclusive (a-z). - - // replace invalid chars with a space, return uppercase text - // Thanks Chris Bolt for improving this function - // note: ereg_replace() replaces nulls with empty string (not space) - return strtoupper(ereg_replace('[^ -<>-}]', ' ', str_replace("\x00", ' ', $originalcommentname))); - - } - -} - -?> \ No newline at end of file diff --git a/getid3/license.commercial.txt b/getid3/license.commercial.txt deleted file mode 100644 index 416e5a1..0000000 --- a/getid3/license.commercial.txt +++ /dev/null @@ -1,27 +0,0 @@ - getID3() Commercial License - =========================== - -getID3() is licensed under the "GNU Public License" (GPL) and/or the -"getID3() Commercial License" (gCL). This document describes the gCL. - ---------------------------------------------------------------------- - -The license is non-exclusively granted to a single person or company, -per payment of the license fee, for the lifetime of that person or -company. The license is non-transferrable. - -The gCL grants the licensee the right to use getID3() in commercial -closed-source projects. Modifications may be made to getID3() with no -obligation to release the modified source code. getID3() (or pieces -thereof) may be included in any number of projects authored (in whole -or in part) by the licensee. - -The licensee may use any version of getID3(), past, present or future, -as is most convenient. This license does not entitle the licensee to -receive any technical support, updates or bugfixes, except as such are -made publicly available to all getID3() users. - -The licensee may not sub-license getID3() itself, meaning that any -commercially released product containing all or parts of getID3() must -have added functionality beyond what is available in getID3(); -getID3() itself may not be re-licensed by the licensee. diff --git a/getid3/license.txt b/getid3/license.txt deleted file mode 100644 index 9fec808..0000000 --- a/getid3/license.txt +++ /dev/null @@ -1,340 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General -Public License instead of this License. diff --git a/getid3/readme.txt b/getid3/readme.txt deleted file mode 100644 index 32ec06e..0000000 --- a/getid3/readme.txt +++ /dev/null @@ -1,355 +0,0 @@ -///////////////////////////////////////////////////////////////// -/// getID3() by James Heinrich // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org /// -///////////////////////////////////////////////////////////////// -/// getID3() v2.x optimized for PHP5 by: // -// Allan Hansen /// -///////////////////////////////////////////////////////////////// - - This code is released under the GNU GPL: - http://www.gnu.org/copyleft/gpl.html - - +---------------------------------------------+ - | If you do use this code somewhere, send me | - | an email and tell me how/where you used it. | - | | - | If you want to donate, there is a link on | - | http://www.getid3.org for PayPal donations. | - +---------------------------------------------+ - -Quick Start -=========== - -Q: How can I check that getID3() works on my server/files? -A: Unzip getID3() to a directory, then access /demos/demo.browse.php - - -Sourceforge Notification -======================== - -It's highly recommended that you sign up for notification from -Sourceforge for when new versions are released. Please visit: -http://sourceforge.net/project/showfiles.php?group_id=55859 -and click the little "monitor package" icon/link. If you're -previously signed up for the mailing list, be aware that it has -been discontinued, only the automated Sourceforge notification -will be used from now on. - - -What does getID3() do? -====================== - -Reads & parses (to varying degrees): - ¤ tags: - * APE (v1 and v2) - * ID3v1 (& ID3v1.1) - * ID3v2 (v2.4, v2.3, v2.2) - * Lyrics3 (v1 & v2) - - ¤ audio-lossy: - * MP3/MP2/MP1 - * MPC / Musepack - * Ogg (Vorbis, OggFLAC, Speex) - * RealAudio - * Speex - * VQF - - ¤ audio-lossless: - * AIFF - * AU - * Bonk - * CD-audio (*.cda) - * FLAC - * LA (Lossless Audio) - * LPAC - * MIDI - * Monkey's Audio - * OptimFROG - * RKAU - * VOC - * WAV (RIFF) - * WavPack - - ¤ audio-video: - * ASF: ASF, Windows Media Audio (WMA), Windows Media Video (WMV) - * AVI (RIFF) - * Flash - * MPEG-1 / MPEG-2 - * NSV (Nullsoft Streaming Video) - * Quicktime - * RealVideo - - ¤ still image: - * BMP - * GIF - * JPEG - * PNG - - ¤ data: - * ISO-9660 CD-ROM image (directory structure) - * SZIP (limited support) - * ZIP (directory structure) - - -Writes: - * ID3v1 (& ID3v1.1) - * ID3v2 (v2.3 & v2.4) - * VorbisComment on OggVorbis - * VorbisComment on FLAC (not OggFLAC) - * APE v2 - * Lyrics3 (delete only) - - -Requirements -============ - -* PHP 4.1.0 (or higher) -* at least 4MB memory for PHP. 8MB is highly recommended. - 12MB is required with all modules loaded. - - -Usage -===== - -require_once('/path/getid3.php'); -$getID3 = new getID3; -$fileinfo = $getID3->analyze($filename); - -See structure.txt for the returned data structure. - - -*> For an example of a complete directory-browsing, <* -*> file-scanning implementation of getID3(), please run <* -*> /demos/demo.browse.php <* - - -See /demos/demo.basic.php for a very basic use of getID3() with no -fancy output, just scanning one file. - -See /demos/demo.mysql.php for a sample recursive scanning code that -scans every file in a given directory, and all sub-directories, stores -the results in a database and allows various analysis / maintenance -operations - -See /demos/demo.simple.php for a simple example script that scans all -files in one directory and output artist, title, bitrate and playtime - -See /demos/demo.mimeonly.php for a simple example script that scans a -single file and returns only the MIME information - -To analyze remote files over HTTP or FTP you need to copy the file -locally first before running getID3(). Your code would look something -like this: - -// Copy remote file locally to scan with getID3() -$remotefilename = 'http://www.example.com/filename.mp3'; -if ($fp_remote = fopen($remotefilename, 'rb')) { - $localtempfilename = tempnam('/tmp', 'getID3'); - if ($fp_local = fopen($localtempfilename, 'wb')) { - while ($buffer = fread($fp_remote, 8192)) { - fwrite($fp_local, $buffer); - } - fclose($fp_local); - - // Initialize getID3 engine - $getID3 = new getID3; - - $ThisFileInfo = $getID3->analyze($filename); - - // Delete temporary file - unlink($localtempfilename); - } - fclose($fp_remote); -} - - -// Writing tags: -require_once('getid3.php'); -$getID3 = new getID3; -getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.php', __FILE__); -$tagwriter = new getid3_writetags; -$tagwriter->filename = $Filename; -$tagwriter->tagformats = array('id3v2.3', 'ape'); -$TagData['title'][] = 'Song Title'; -$TagData['artist'][] = 'Artist Name'; -$tagwriter->tag_data = array(; -if ($tagwriter->WriteTags()) { - echo 'success'; -} else { - echo 'failure'; -} - - - -What does the returned data structure look like? -================================================ - -See structure.txt - -It is recommended that you look at the output of -/demos/demo.browse.php scanning the file(s) you're interested in to -confirm what data is actually returned for any particular filetype in -general, and your files in particular, as the actual data returned -may vary considerably depending on what information is available in -the file itself. - - -Notes -===== - -If the format parser encounters a critical problem, it will return -something in $fileinfo['error'], describing the encountered error. If -a less critical error or notice is generated it will appear in -$fileinfo['warning']. Both keys may contain more than one warning or -error. If something is returned in ['error'] then the file was not -correctly parsed and returned data may or may not be correct and/or -complete. If something is returned in ['warning'] (and not ['error']) -then the data that is returned is OK - usually getID3() is reporting -errors in the file that have been worked around due to known bugs in -other programs. Some warnings may indicate that the data that is -returned is OK but that some data could not be extracted due to -errors in the file. - - -Known Bugs/Issues -================= - -See the end of changelog.txt for notes on known issues with -getID3(), encoders, players, etc. - - -Disclaimer -========== - -getID3() has been tested on many systems, on many types of files, -under many operating systems, and is generally believe to be stable -and safe. That being said, there is still the chance there is an -undiscovered and/or unfixed bug that may potentially corrupt your -file, especially within the writing functions. By using getID3() you -agree that it's not my fault if any of your files are corrupted. -In fact, I'm not liable for anything :) - - -///////////////////////////////////////////////////////////////////// - -GNU General Public License - see license.txt - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to: -Free Software Foundation, Inc. -59 Temple Place - Suite 330 -Boston, MA 02111-1307, USA. - - -FAQ: -Q: Can I use getID3() in my program? Do I need a commercial license? -A: You're generally free to use getID3 however you see fit. The only - case in which you would require a commercial license is if you're - selling your closed-source program that integrates getID3. If you - sell your program including a copy of getID3, that's fine as long - as you include a copy of the sourcecode when you sell it. Or you - can distribute your code without getID3 and say "download it from - getid3.sourceforge.net" - - -///////////////////////////////////////////////////////////////////// - -Reference material: -[www.id3.org material now mirrored at http://id3lib.sourceforge.net/id3/] -* http://www.id3.org/id3v2.4.0-structure.txt -* http://www.id3.org/id3v2.4.0-frames.txt -* http://www.id3.org/id3v2.4.0-changes.txt -* http://www.id3.org/id3v2.3.0.txt -* http://www.id3.org/id3v2-00.txt -* http://www.id3.org/mp3frame.html -* http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html -* http://www.dv.co.yu/mpgscript/mpeghdr.htm -* http://www.mp3-tech.org/programmer/frame_header.html -* http://users.belgacom.net/gc247244/extra/tag.html -* http://gabriel.mp3-tech.org/mp3infotag.html -* http://www.id3.org/iso4217.html -* http://www.unicode.org/Public/MAPPINGS/ISO8859/8859-1.TXT -* http://www.xiph.org/ogg/vorbis/doc/framing.html -* http://www.xiph.org/ogg/vorbis/doc/v-comment.html -* http://leknor.com/code/php/class.ogg.php.txt -* http://www.id3.org/iso639-2.html -* http://www.id3.org/lyrics3.html -* http://www.id3.org/lyrics3200.html -* http://www.psc.edu/general/software/packages/ieee/ieee.html -* http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee-expl.html -* http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html -* http://www.jmcgowan.com/avi.html -* http://www.wotsit.org/ -* http://www.herdsoft.com/ti/davincie/davp3xo2.htm -* http://www.mathdogs.com/vorbis-illuminated/bitstream-appendix.html -* "Standard MIDI File Format" by Dustin Caldwell (from www.wotsit.org) -* http://midistudio.com/Help/GMSpecs_Patches.htm -* http://www.xiph.org/archives/vorbis/200109/0459.html -* http://www.replaygain.org/ -* http://www.lossless-audio.com/ -* http://download.microsoft.com/download/winmediatech40/Doc/1.0/WIN98MeXP/EN-US/ASF_Specification_v.1.0.exe -* http://mediaxw.sourceforge.net/files/doc/Active%20Streaming%20Format%20(ASF)%201.0%20Specification.pdf -* http://www.uni-jena.de/~pfk/mpp/sv8/ -* http://jfaul.de/atl/ -* http://www.uni-jena.de/~pfk/mpp/ -* http://www.libpng.org/pub/png/spec/png-1.2-pdg.html -* http://www.real.com/devzone/library/creating/rmsdk/doc/rmff.htm -* http://www.fastgraph.com/help/bmp_os2_header_format.html -* http://netghost.narod.ru/gff/graphics/summary/os2bmp.htm -* http://flac.sourceforge.net/format.html -* http://www.research.att.com/projects/mpegaudio/mpeg2.html -* http://www.audiocoding.com/wiki/index.php?page=AAC -* http://libmpeg.org/mpeg4/doc/w2203tfs.pdf -* http://www.geocities.com/xhelmboyx/quicktime/formats/qtm-layout.txt -* http://developer.apple.com/techpubs/quicktime/qtdevdocs/RM/frameset.htm -* http://www.nullsoft.com/nsv/ -* http://www.wotsit.org/download.asp?f=iso9660 -* http://sandbox.mc.edu/~bennet/cs110/tc/tctod.html -* http://www.cdroller.com/htm/readdata.html -* http://www.speex.org/manual/node10.html -* http://www.harmony-central.com/Computer/Programming/aiff-file-format.doc -* http://www.faqs.org/rfcs/rfc2361.html -* http://ghido.shelter.ro/ -* http://www.ebu.ch/tech_t3285.pdf -* http://www.sr.se/utveckling/tu/bwf -* http://ftp.aessc.org/pub/aes46-2002.pdf -* http://cartchunk.org:8080/ -* http://www.broadcastpapers.com/radio/cartchunk01.htm -* http://www.hr/josip/DSP/AudioFile2.html -* http://home.attbi.com/~chris.bagwell/AudioFormats-11.html -* http://www.pure-mac.com/extkey.html -* http://cesnet.dl.sourceforge.net/sourceforge/bonkenc/bonk-binary-format-0.9.txt -* http://www.headbands.com/gspot/ -* http://www.openswf.org/spec/SWFfileformat.html -* http://j-faul.virtualave.net/ -* http://www.btinternet.com/~AnthonyJ/Atari/programming/avr_format.html -* http://cui.unige.ch/OSG/info/AudioFormats/ap11.html -* http://sswf.sourceforge.net/SWFalexref.html -* http://www.geocities.com/xhelmboyx/quicktime/formats/qti-layout.txt -* http://www-lehre.informatik.uni-osnabrueck.de/~fbstark/diplom/docs/swf/Flash_Uncovered.htm -* http://developer.apple.com/quicktime/icefloe/dispatch012.html -* http://www.csdn.net/Dev/Format/graphics/PCD.htm -* http://tta.iszf.irk.ru/ -* http://www.atsc.org/standards/a_52a.pdf -* http://www.alanwood.net/unicode/ -* http://www.freelists.org/archives/matroska-devel/07-2003/msg00010.html -* http://www.its.msstate.edu/net/real/reports/config/tags.stats -* http://homepages.slingshot.co.nz/~helmboy/quicktime/formats/qtm-layout.txt -* http://brennan.young.net/Comp/LiveStage/things.html -* http://www.multiweb.cz/twoinches/MP3inside.htm -* http://www.geocities.co.jp/SiliconValley-Oakland/3664/alittle.html#GenreExtended -* http://www.mactech.com/articles/mactech/Vol.06/06.01/SANENormalized/ -* http://www.unicode.org/unicode/faq/utf_bom.html -* http://tta.corecodec.org/?menu=format -* http://www.scvi.net/nsvformat.htm \ No newline at end of file diff --git a/getid3/structure.txt b/getid3/structure.txt deleted file mode 100644 index a0651c6..0000000 --- a/getid3/structure.txt +++ /dev/null @@ -1,2251 +0,0 @@ -///////////////////////////////////////////////////////////////// -/// getID3() by James Heinrich // -// available at http://getid3.sourceforge.net // -// or http://www.getid3.org // -///////////////////////////////////////////////////////////////// -// // -// changelog.txt - part of getID3() // -// See readme.txt for more details // -// /// -///////////////////////////////////////////////////////////////// - -What does the returned data structure look like? -================================================ - -Hint: If you take a look at the nicely-formatted output of -/demos/demo.browse.php you can generally see where the data you want -is returned. - -Note that what is described below is only a rough guide to what data -is actually returned by getID3(), since the actual data returned -depends entirely on what data is in your file, what type of file it -is, what kind of data is in the tags, etc. In addition, some formats -(Quicktime for example) use a freeform recursive structure that is -impossible to document completely. - -In the vast majority of cases, all the data you'll need is located -in the root of the array or the special arrays described below in -Section 1 (['audio'], ['video'], ['tags_html'], ['replay_gain']). - -It is suggested that for most applications you should use tag data -from the root ['tags_html'] array, as this is the only location -where data is stored in a consistant format: HTML-compatible -character entities (ie Ӓ) for characters outside the 0x20-0x7F -range (printable ISO-8859-1 characters). This data can be used as-is -for output in HTML, and can be converted to whatever character set -you wish to use if the output is not HTML. - -If you want to merge all available tags (for example, ID3v2 + ID3v1) -into one array, you can call -getid3_lib::CopyTagsToComments($ThisFileInfo) -and you'll then have ['comments'] and ['comments_html'] which are -identical to ['tags'] and ['tags_html'] except the array is one -dimension shorter (no tag type array keys). For example, artist is: -['tags_html']['id3v1']['artist'][0] or ['comments_html']['artist'][0] - - -Some commonly-used information is found in these locations: - -File type: ['fileformat'] // ex 'mp3' -Song length: ['playtime_string'] // ex '3:45' (minutes:seconds) - ['playtime_seconds'] // ex 225.13 (seconds) -Overall bitrate: ['bitrate'] // ex 113485.71 (bits-per-second - divide by 1000 for kbps) -Audio frequency: ['audio']['sample_rate'] // ex 44100 (Hertz) -Artist name: ['comments_html']['artist'][0] // ex 'Elvis' (if CopyTagsToComments() is used - see above) - // more than one artist may be present, you may want to use implode: - // implode(' & ', ['comments_html']['artist']) - - -///////////////////////////////////////////////////////////////// - -array() { - // SECTION 1: Values that are present for most or all file types - - ['getID3version']=>string() // version of getID3() that scanned this file (ex: '1.6.2') - ['error']=>array() // if present, contains one or more fatal error messages - ['warning']=>array() // if present, contains one or more non-fatal warning messages - ['exist']=>boolean() // does this file actually exist? - ['fileformat']=>string() // one of the standard filetype abbreviations ('mp3', 'riff', 'quicktime', etc) - ['filename']=>string() // filename only, no path - ['filenamepath']=>string() // full filename with path - ['filepath']=>string() // path to file, not including filename - ['filesize']=>integer() // filesize in bytes - ['md5_file']=>string() // md5 hash of entire file - ['md5_data']=>string() // md5 hash of portion of file excluding prepended and appeneded metainformation tags (ID3, APE, etc) - may be identical to ['md5_file'] - ['md5_data_source']=>string() // md5 hash of original source file before compression (currently used by FLAC, OptimFROG, WavPack v4+) - ['sha1_file']=>string() // sha1 hash of entire file - ['sha1_data']=>string() // sha1 hash of portion of file excluding prepended and appeneded metainformation tags (ID3, APE, etc) - may be identical to ['md5_file'] - ['avdataoffset']=>integer() // offset in bytes where audio/video data starts and prepended tags end - ['avdataend']=>integer() // offset in bytes where audio/video data ends and appended tags start - ['bitrate']=>double() // average bitrate for entire file (all audio/video streams), in bits per second - ['mime_type']=>string() // if present, MIME type of scanned file - ['playtime_seconds']=>double() // playing time of file, in seconds - ['playtime_string']=>string() // playing time of file, formatted as : - ['tags']=>array() // array of all metainformation tags present in file ('id3v1', 'id3v2', 'ape', 'riff', 'asf', etc) - ['audio']=>array() { - ['bitrate']=>double() // average bitrate for audio portion of file (all audio streams), in bits per second - ['bitrate_mode']=>string() // 'cbr' (Constant Bit Rate) or 'vbr' (Variable Bit Rate) - ['bits_per_sample']=>integer() // - ['channelmode']=>string() // 'mono' or 'stereo' - ['channels']=>integer() // number of audio channels - ['codec']=>string() // name of audio compression codec - ['compression_ratio']=>double() // ratio of compressed byte size of audio to uncompressed size - ['dataformat']=>string() // one of the standard filetype abbreviations ('mp3', 'wma', etc) - ['encoder']=>string() // name and version of encoder used to create file, if known - ['lossless']=>boolean() // true = lossless compression; false = lossy compression - ['sample_rate']=>integer() - } - ['video']=>array() { - ['bitrate']=>integer() // average bitrate for video portion of file (all video streams), in bits per second - ['bitrate_mode']=>string() // 'cbr' (Constant Bit Rate) or 'vbr' (Variable Bit Rate) - ['bits_per_sample']=>integer() // - ['codec']=>string() // name of video compression codec - ['compression_ratio']=>double() // ratio of compressed byte size of video to uncompressed size - ['dataformat']=>string() // one of the standard filetype abbreviations ('avi', 'mpeg', etc) - ['encoder']=>string() // name and version of encoder used to create file, if known - ['frame_rate']=>double() // frames per second - ['lossless']=>boolean() // true = lossless compression; false = lossy compression - ['resolution_x']=>integer() // horizontal dimension of video/image in pixels - ['resolution_y']=>integer() // vertical dimension of video/image in pixels - ['pixel_aspect_ratio']=>double() // pixel display aspect ratio - } - ['tags']=>array() { // array of array of strings containing best data from any available metainformation tag (APE, ID3v2, ID3v1, Lyrics3, Vorbis, ASF, RIFF, Real, etc.) - []=>array() // can be anything, usually 'artist', 'title', etc. Contains array of one or more values (eg: multiple artists are possible) - } - ['tags_html']=>array() { // identical to ['tags'], but with all entries converted to HTML entities as appropriate from various source encodings - []=>array() // - } - ['replay_gain']=>array() { // replay gain information combined from any source that contains this information (LAME, ID3v2, Vorbis, APE, etc) - ['audiophile']=>array() { - ['adjustment']=>double() - ['originator']=>string() - ['peak']=>double() - } - ['radio']=>array() { - ['adjustment']=>double() - ['originator']=>string() - ['peak']=>double() - } - } - - - // SECTION 2: Values that are present for specific file types only - - ['aac']=>array() { // AAC - Advanced Audio Coding / MPEG-4 - ['bitrate_distribution']=>array() // - ['header']=>array() { // - ['channel_configuration']=>integer() // - ['crc_present']=>boolean() // - ['home']=>boolean() // - ['layer']=>integer() // - ['mpeg_version']=>integer() // - ['original']=>boolean() // - ['private']=>boolean() // - ['profile_id']=>integer() // - ['profile_text']=>string() // - ['sample_frequency']=>integer() // - ['sample_frequency_index']=>integer() // - ['synch']=>integer() // - } // - ['header_type']=>string() // - } // - // - ['ape']=>array() // - { // - ['comments']=>array() { // array of array of strings containing best data from any available metainformation tag (APE, ID3v2, ID3v1, Lyrics3, Vorbis, ASF, RIFF, Real, etc.) - []=>array() // can be anything, usually 'artist', 'title', etc. Contains array of one or more values (eg: multiple artists are possible) - } // - ['footer']=>array() // - { // - ['flags']=>array() // - ['raw']=>array() // - ['tag_version']=>integer() // - } // - ['header']=>array() // - { // - ['flags']=>array() // - ['raw']=>array() // - ['tag_version']=>integer() // - } // - ['items']=>array() { // array of array of strings containing metainformation - []=>array() { // can be anything, usually 'artist', 'title', etc. Contains array of one or more values (eg: multiple artists are possible) - ['data']=>array() { // array of one or more Unicode values - ['data_ascii']=>array() { // array of values converted approximately from Unicode to ASCII - ['flags']=>array() // - } // - } // - ['tag_offset_end']=>integer() // - ['tag_offset_start']=>integer() // - } // - - - ['asf']=>array() { // ASF - Advanced Streaming Format (ASF, Windows Media Audio (WMA), Windows Media Video (WMV)) - ['audio_media']=>array() { // - []=>array() { // - ['bitrate']=>integer() // - ['bits_per_sample']=>integer() // - ['channels']=>integer() // - ['codec']=>string() // - ['codec_data']=>string() // - ['codec_data_size']=>integer() // - ['raw']=>array() { // - ['nAvgBytesPerSec']=>integer() // - ['wBitsPerSample']=>integer() // - ['nBlockAlign']=>integer() // - ['nChannels']=>integer() // - ['nSamplesPerSec']=>integer() // - ['wFormatTag']=>integer() // - } // - ['sample_rate']=>integer() // - } // - } // - ['codec_list']=>array() { // - ['codec_entries']=>array() { // - []=>array() { // - ['description']=>string() // - ['description_ascii']=>string() // - ['information']=>string() // - ['name']=>string() // - ['name_ascii']=>string() // - ['type']=>string() // - ['type_raw']=>integer() // - } // - } // - ['codec_entries_count']=>integer() // - ['objectid']=>string() // - ['objectid_guid']=>string() // - ['objectsize']=>integer() // - ['reserved']=>string() // - ['reserved_guid']=>string() // - } // - ['comments']=>array() { // array of comment values, derived from ['content_description'] - ['album']=>string() // - ['artist']=>string() // - ['comment']=>string() // - ['copyright']=>string() // - ['genre']=>string() // - ['title']=>string() // - ['track']=>string() // - ['year']=>string() // - } // - ['content_description']=>array() { // raw values - should use values from ['comments'] instead - ['author']=>string() // - ['author_ascii']=>string() // - ['author_length']=>integer() // - ['copyright']=>string() // - ['copyright_ascii']=>string() // - ['copyright_length']=>integer() // - ['description']=>string() // - ['description_ascii']=>string() // - ['description_length']=>integer() // - ['objectid']=>string() // - ['objectid_guid']=>string() // - ['objectsize']=>integer() // - ['rating']=>string() // - ['rating_ascii']=>string() // - ['rating_length']=>integer() // - ['title']=>string() // - ['title_ascii']=>string() // - ['title_length']=>integer() // - } // - ['data_object']=>array() { // - ['fileid']=>string() // - ['fileid_guid']=>string() // - ['objectid']=>string() // - ['objectid_guid']=>string() // - ['objectsize']=>integer() // - ['reserved']=>integer() // - ['total_data_packets']=>integer() // - } // - ['extended_content_description']=>array() { // - ['content_descriptors']=>array() { // - []=>array() { // - ['name']=>string() // - ['name_ascii']=>string() // - ['name_length']=>integer() // - ['value']=>string() // - ['value_ascii']=>string() // - ['value_length']=>integer() // - ['value_type']=>integer() // - } // - } // - ['content_descriptors_count']=>integer() // - ['objectid']=>string() // - ['objectid_guid']=>string() // - ['objectsize']=>integer() // - } // - ['file_properties_object']=>array() { // - ['creation_date']=>double() // - ['creation_date_unix']=>double() // - ['data_packets']=>integer() // - ['fileid']=>string() // - ['fileid_guid']=>string() // - ['filesize']=>integer() // - ['flags']=>array() { // - ['broadcast']=>boolean() // - ['seekable']=>boolean() // - } // - ['flags_raw']=>integer() // - ['max_bitrate']=>integer() // - ['max_packet_size']=>integer() // - ['min_packet_size']=>integer() // - ['objectid']=>string() // - ['objectid_guid']=>string() // - ['objectsize']=>integer() // - ['play_duration']=>double() // - ['preroll']=>integer() // - ['send_duration']=>double() // - } // - ['header_extension_object']=>array() { // - ['extension_data']=>integer() // - ['extension_data_size']=>integer() // - ['objectid']=>string() // - ['objectid_guid']=>string() // - ['objectsize']=>integer() // - ['reserved_1']=>string() // - ['reserved_1_guid']=>string() // - ['reserved_2']=>integer() // - } // - ['header_object']=>array() { // - ['headerobjects']=>integer() // - ['objectid']=>string() // - ['objectid_guid']=>string() // - ['objectsize']=>integer() // - ['reserved1']=>integer() // - ['reserved2']=>integer() // - } // - ['marker_object']=>array() { // - ['markers_count']=>integer() // - ['objectid']=>string() // - ['objectid_guid']=>string() // - ['objectsize']=>integer() // - ['reserved']=>string() // - ['reserved_2']=>integer() // - ['reserved_guid']=>string() // - } // - ['stream_bitrate_properties']=>array() { // - ['bitrate_records']=>array() { // - []=>array() { // - ['bitrate']=>integer() // - ['flags_raw']=>integer() // - ['flags']=>array() { // - ['stream_number']=>integer() // - } // - } // - } // - ['bitrate_records_count']=>integer() // - ['objectid']=>string() // - ['objectid_guid']=>string() // - ['objectsize']=>integer() // - } // - ['stream_properties_object']=>array() { // - []=>array() { // - ['error_correct_data']=>string() // - ['error_correct_guid']=>string() // - ['error_correct_type']=>string() // - ['error_data_length']=>integer() // - ['flags_raw']=>integer() // - ['flags']=>array() { // - ['encrypted']=>boolean() // - } // - ['objectid']=>string() // - ['objectid_guid']=>string() // - ['objectsize']=>integer() // - ['stream_type']=>string() // - ['stream_type_guid']=>string() // - ['time_offset']=>integer() // - ['type_data_length']=>integer() // - ['type_specific_data']=>string() // - } // - } // - ['video_media']=>array() { // - []=>array() { // - ['flags']=>integer() // - ['format_data']=>array() { // - ['bits_per_pixel']=>integer() // - ['codec']=>string() // - ['codec_data']=>boolean() // - ['codec_fourcc']=>string() // - ['colors_important']=>integer() // - ['colors_used']=>integer() // - ['format_data_size']=>integer() // - ['horizontal_pels']=>integer() // - ['image_height']=>integer() // - ['image_size']=>integer() // - ['image_width']=>integer() // - ['reserved']=>integer() // - ['vertical_pels']=>integer() // - } // - ['format_data_size']=>integer() // - ['image_height']=>integer() // - ['image_width']=>integer() // - } // - } // - } // - - - ['au']=>array() { // AU - Next/Sun AUdio format - ['bits_per_sample']=>integer() // - ['channels']=>integer() // - ['comment']=>string() // - ['data_format']=>string() // - ['data_format_id']=>integer() // - ['data_size']=>integer() // - ['header_length']=>integer() // - ['sample_rate']=>integer() // - ['used_bits_per_sample']=>integer() // - } // - - - ['bmp']=>array() { // BMP - OS/2 or Windows BitMaP - ['header']=>array() { // - ['compression']=>string() // - ['raw']=>array() { // - ['bits_per_pixel']=>integer() // - ['bmp_data_size']=>integer() // - ['colors_important']=>integer() // - ['colors_used']=>integer() // - ['compression']=>integer() // - ['data_offset']=>integer() // - ['filesize']=>integer() // - ['header_size']=>integer() // - ['height']=>integer() // - ['identifier']=>string() // - ['planes']=>integer() // - ['resolution_h']=>integer() // - ['resolution_v']=>integer() // - ['width']=>integer() // - } // - } // - ['type_os']=>string() // - ['type_version']=>integer() // - } // - - - ['bonk']=>array() { // BONK - lossy/lossless audio compression (www.bonkenc.org) - ['BONK']=>array() { // - ['channels']=>integer() // - ['downsampling_ratio']=>integer() // - ['joint_stereo']=>boolean() // - ['lossless']=>boolean() // - ['number_samples']=>integer() // - ['number_taps']=>integer() // - ['offset']=>integer() // - ['sample_rate']=>integer() // - ['samples_per_packet']=>integer() // - ['size']=>integer() // - ['version']=>integer() // - } // - ['INFO']=>array() { // - ['size']=>integer() // - ['offset']=>integer() // - ['version']=>integer() // - []=>array() { // - ['nextbit']=>integer() // - ['offset']=>integer() // - } // - } // - ['dataend']=>integer() // - ['dataoffset']=>integer() // - } // - - - ['flac']=>array() { // FLAC - Free Lossless Audio Compressor - ['SEEKTABLE']=>array() { // - []=>array() { // - ['offset']=>integer() // - ['samples']=>integer() // - } // - ['placeholders']=>integer() // - ['raw']=>array() { // - ['block_data']=>string() // - ['block_length']=>integer() // - ['block_type']=>integer() // - ['block_type_text']=>string() // - ['last_meta_block']=>boolean() // - ['offset']=>integer() // - } // - } // - ['STREAMINFO']=>array() { // - ['audio_signature']=>string() // - ['bits_per_sample']=>integer() // - ['channels']=>integer() // - ['max_block_size']=>integer() // - ['max_frame_size']=>integer() // - ['min_block_size']=>integer() // - ['min_frame_size']=>integer() // - ['raw']=>array() { // - ['block_data']=>string() // - ['block_length']=>integer() // - ['block_type']=>integer() // - ['block_type_text']=>string() // - ['last_meta_block']=>boolean() // - ['offset']=>integer() // - } // - ['sample_rate']=>integer() // - ['samples_stream']=>integer() // - } // - ['VORBIS_COMMENT']=>array() { // - ['raw']=>array() { // - ['block_data']=>string() // - ['block_length']=>integer() // - ['block_type']=>integer() // - ['block_type_text']=>string() // - ['last_meta_block']=>boolean() // - ['offset']=>integer() // - } // - } // - ['compressed_audio_bytes']=>integer() // - ['compression_ratio']=>double() // - ['uncompressed_audio_bytes']=>integer() // - } // - - - ['gif']=>array() { // GIF - Graphics Interchange Format - ['global_color_table']=>array() { // - []=>integer() // - } // - ['header']=>array() { // - ['bits_per_pixel']=>integer() // - ['flags']=>array() { // - ['global_color_sorted']=>boolean() // - ['global_color_table']=>boolean() // - } // - ['global_color_size']=>integer() // - ['raw']=>array() { // - ['aspect_ratio']=>integer() // - ['bg_color_index']=>integer() // - ['flags']=>integer() // - ['height']=>integer() // - ['identifier']=>string() // - ['version']=>string() // - ['width']=>integer() // - } // - } // - ['version']=>string() // - } // - - - ['id3v1']=>array() { // ID3v1 - ['album']=>string() // - ['artist']=>string() // - ['comment']=>string() // - ['genre']=>string() // - ['genreid']=>integer() // - ['title']=>string() // - ['track']=>integer() // - ['year']=>string() // - ['padding_valid']=>boolean() // - ['comments']=>array() // - ['tag_offset_start']=>integer() // - ['tag_offset_end']=>integer() // - } // - - - ['id3v2']=>array() { // ID3v2 - www.id3.org - []=>array() { // can be any of the 4-character (3-character in ID3v2.2) frame names allowed in the ID3v2 spec. Exact contents of returned array data varies with frame type. - []=>array() { // some frames types allow multiple values ('COMM' for example), others do not and do not have this array level - ['asciidata']=>boolean() // - ['asciidescription']=>string() // - ['data']=>boolean() // - ['datalength']=>integer() // - ['dataoffset']=>integer() // - ['description']=>string() // - ['encoding']=>string() // - ['encodingid']=>integer() // - ['flags']=>array() { // - ['Encryption']=>boolean() // - ['FileAlterPreservation']=>boolean() // - ['GroupingIdentity']=>boolean() // - ['ReadOnly']=>boolean() // - ['TagAlterPreservation']=>boolean() // - ['compression']=>boolean() // - } // - ['framenamelong']=>string() // - ['language']=>string() // - ['languagename']=>string() // - } // - } // - ['comments']=>array() { // array of array of strings containing best data from any available metainformation tag (APE, ID3v2, ID3v1, Lyrics3, Vorbis, ASF, RIFF, Real, etc.) - []=>array() // can be anything, usually 'artist', 'title', etc. Contains array of one or more values (eg: multiple artists are possible) - } // - ['flags']=>array() { // - ['experim']=>string() // - ['exthead']=>string() // - ['unsynch']=>string() // - } // - ['header']=>boolean() // - ['headerlength']=>integer() // - ['majorversion']=>integer() // - ['minorversion']=>integer() // - ['padding']=>array() { // - ['length']=>integer() // - ['start']=>integer() // - ['valid']=>boolean() // - } // - ['tag_offset_end']=>integer() // - ['tag_offset_start']=>integer() // - } // - - - ['iso']=>array() { // ISO-9660 - CD-ROM Image - ['directories']=>array() { // - []=>array() { // - []=>array() { // - ['file_flags']=>array() { // - ['associated']=>boolean() // - ['directory']=>boolean() // - ['extended']=>boolean() // - ['hidden']=>boolean() // - ['multiple']=>boolean() // - ['permissions']=>boolean() // - } // - ['file_identifier_ascii']=>string() // - ['filename']=>string() // - ['filesize']=>integer() // - ['offset_bytes']=>integer() // - ['raw']=>array() { // - ['extended_attribute_length']=>integer() // - ['file_flags']=>integer() // - ['file_identifier']=>string() // - ['file_identifier_length']=>integer() // - ['file_unit_size']=>integer() // - ['filesize']=>integer() // - ['interleave_gap_size']=>integer() // - ['length']=>integer() // - ['offset_logical']=>integer() // - ['recording_date_time']=>string() // - ['volume_sequence_number']=>integer() // - } // - ['recording_timestamp']=>integer() // - } // - } // - } // - ['files']=>array() { // multidimensional tree-structure array listing of all files and directories in image - []=>array() // entries of type array are directories (key is directory name), may contain files and/or other subdirectories - []=>integer() // entries of type integer are files (key is file name, value is file size in bytes) - } // - ['path_table']=>array() { // - ['directories']=>array() { // - []=>array() { // - ['extended_length']=>integer() // - ['full_path']=>string() // - ['length']=>integer() // - ['location_bytes']=>integer() // - ['location_logical']=>integer() // - ['name']=>string() // - ['name_ascii']=>string() // - ['parent_directory']=>integer() // - } // - } // - ['offset']=>integer() // - ['raw']=>string() // - } // - ['primary_volume_descriptor']=>array() { // - ['abstract_file_identifier']=>string() // - ['application_identifier']=>string() // - ['bibliographic_file_identifier']=>string() // - ['copyright_file_identifier']=>string() // - ['data_preparer_identifier']=>string() // - ['offset']=>integer() // - ['publisher_identifier']=>string() // - ['raw']=>array() { // - ['abstract_file_identifier']=>string() // - ['application_data']=>string() // - ['application_identifier']=>string() // - ['bibliographic_file_identifier']=>string() // - ['copyright_file_identifier']=>string() // - ['data_preparer_identifier']=>string() // - ['file_structure_version']=>integer() // - ['logical_block_size']=>integer() // - ['path_table_l_location']=>integer() // - ['path_table_l_opt_location']=>integer() // - ['path_table_m_location']=>integer() // - ['path_table_m_opt_location']=>integer() // - ['path_table_size']=>integer() // - ['publisher_identifier']=>string() // - ['root_directory_record']=>string() // - ['standard_identifier']=>string() // - ['system_identifier']=>string() // - ['unused_1']=>string() // - ['unused_2']=>string() // - ['unused_3']=>string() // - ['unused_4']=>integer() // - ['volume_creation_date_time']=>string() // - ['volume_descriptor_type']=>integer() // - ['volume_descriptor_version']=>integer() // - ['volume_effective_date_time']=>string() // - ['volume_expiration_date_time']=>string() // - ['volume_identifier']=>string() // - ['volume_modification_date_time']=>string() // - ['volume_sequence_number']=>integer() // - ['volume_set_identifier']=>string() // - ['volume_set_size']=>integer() // - ['volume_space_size']=>integer() // - } // - ['system_identifier']=>string() // - ['volume_creation_date_time']=>integer() // - ['volume_effective_date_time']=>boolean() // - ['volume_expiration_date_time']=>boolean() // - ['volume_identifier']=>string() // - ['volume_modification_date_time']=>integer() // - ['volume_set_identifier']=>string() // - } // - ['supplementary_volume_descriptor']=>array() { // - ['abstract_file_identifier']=>string() // - ['application_identifier']=>string() // - ['bibliographic_file_identifier']=>string() // - ['copyright_file_identifier']=>string() // - ['data_preparer_identifier']=>string() // - ['offset']=>integer() // - ['publisher_identifier']=>string() // - ['raw']=>array() { // - ['abstract_file_identifier']=>string() // - ['application_data']=>string() // - ['application_identifier']=>string() // - ['bibliographic_file_identifier']=>string() // - ['copyright_file_identifier']=>string() // - ['data_preparer_identifier']=>string() // - ['file_structure_version']=>integer() // - ['logical_block_size']=>integer() // - ['path_table_l_location']=>integer() // - ['path_table_l_opt_location']=>integer() // - ['path_table_m_location']=>integer() // - ['path_table_m_opt_location']=>integer() // - ['path_table_size']=>integer() // - ['publisher_identifier']=>string() // - ['root_directory_record']=>string() // - ['standard_identifier']=>string() // - ['system_identifier']=>string() // - ['unused_1']=>string() // - ['unused_2']=>string() // - ['unused_3']=>string() // - ['unused_4']=>integer() // - ['volume_creation_date_time']=>string() // - ['volume_descriptor_type']=>integer() // - ['volume_descriptor_version']=>integer() // - ['volume_effective_date_time']=>string() // - ['volume_expiration_date_time']=>string() // - ['volume_identifier']=>string() // - ['volume_modification_date_time']=>string() // - ['volume_sequence_number']=>integer() // - ['volume_set_identifier']=>string() // - ['volume_set_size']=>integer() // - ['volume_space_size']=>integer() // - } // - ['system_identifier']=>string() // - ['volume_creation_date_time']=>integer() // - ['volume_effective_date_time']=>boolean() // - ['volume_expiration_date_time']=>boolean() // - ['volume_identifier']=>string() // - ['volume_modification_date_time']=>integer() // - ['volume_set_identifier']=>string() // - } // - } // - - - ['jpg']=>array() { // JPEG - still image - ['exif']=>array() // data returned from PHP's exif_read_data() function - } // - - - ['la']=>array() { // LA - Lossless Audio (www.lossless-audio.com) - ['raw']=>array() { - ['format']=>integer() // - ['flags']=>integer() // - } // - ['flags']=>array() { // - ['seekable']=>boolean() // - ['high_compression']=>boolean() // - } // - ['bits_per_sample']=>integer() // - ['bytes_per_sample']=>integer() // - ['bytes_per_second']=>integer() // - ['channels']=>integer() // - ['compression_ratio']=>double() // - ['format_size']=>integer() // - ['header_size']=>integer() // - ['original_crc']=>double() // - ['sample_rate']=>integer() // - ['samples']=>integer() // - ['uncompressed_size']=>integer() // - ['version']=>double() // - ['version_major']=>integer() // - ['version_minor']=>integer() // - ['footerstart']=>double() // - } - - - ['lpac']=>array() { // LPAC - Lossless Predictive Audio Compressor - ['block_length']=>integer() // - ['file_version']=>integer() // - ['flags']=>array() { // - ['16_bit']=>boolean() // - ['24_bit']=>boolean() // - ['adaptive_prediction_order']=>boolean() // - ['adaptive_quantization']=>boolean() // - ['fast_compress']=>boolean() // - ['is_wave']=>boolean() // - ['joint_stereo']=>boolean() // - ['max_prediction_order']=>integer() // - ['quantization']=>integer() // - ['random_access']=>boolean() // - ['stereo']=>boolean() // - } // - ['raw']=>array() { // - ['audio_type']=>integer() // - ['parameters']=>double() // - } // - ['total_samples']=>integer() // - } // - - - ['lyrics3']=>array() { // Lyrics3 - metainformation tags - ['comments']=>array() { // - ['album']=>string() // - ['artist']=>string() // - ['author']=>string() // - ['comment']=>string() // - ['title']=>string() // - } // - ['flags']=>array() { // - ['lyrics']=>boolean() // - ['timestamps']=>boolean() // - } // - ['images']=>array() { // - []=>array() { // - ['description']=>string() // - ['filename']=>string() // - ['timestamp']=>integer() // - } // - } // - ['raw']=>array() { // - ['offset_start']=>integer() // - ['offset_end']=>integer() // - ['AUT']=>string() // - ['EAL']=>string() // - ['EAR']=>string() // - ['ETT']=>string() // - ['IMG']=>string() // - ['IND']=>string() // - ['INF']=>string() // - ['LYR']=>string() // - ['lyrics3tagsize']=>integer() // - ['lyrics3version']=>integer() // - ['unparsed']=>string() // - } // - ['synchedlyrics']=>array() { // - []=>string() // - } // - ['unsynchedlyrics']=>string() // - } // - - - ['midi']=>array() { // MIDI (Musical Instrument Digital Interface) - sequenced music - ['comments']=>array() { // - ['comment']=>string() // - ['copyright']=>string() // - } // - ['keysignature']=>array() { // - []=>string() // - } // - ['raw']=>array() { // - ['events']=>array() { // - []=>array() { // - []=>array() { // - ['us_qnote']=>integer() // - } // - } // - } // - ['fileformat']=>integer() // - ['headersize']=>integer() // - ['ticksperqnote']=>integer() // - ['track']=>array() { // - []=>array() { // - ['instrument']=>string() // - ['instrumentid']=>integer() // - ['name']=>string() // - } // - } // - ['tracks']=>integer() // - } // - ['timesignature']=>array() { // - []=>string() // - } // - ['totalticks']=>integer() // - } // - - - ['monkeys_audio']=>array() { // Monkey's Audio - lossless audio compression - ['bitrate']=>double() // - ['bits_per_sample']=>integer() // - ['channels']=>integer() // - ['compressed_size']=>integer() // - ['compression']=>string() // - ['compression_ratio']=>double() // - ['flags']=>array() { // - ['24-bit']=>boolean() // - ['8-bit']=>boolean() // - ['crc-32']=>boolean() // - ['no_wav_header']=>boolean() // - ['peak_level']=>boolean() // - ['seek_elements']=>boolean() // - } // - ['frames']=>integer() // - ['peak_level']=>integer() // - ['peak_ratio']=>double() // - ['playtime']=>double() // - ['raw']=>array() { // - ['header_tag']=>string() // - ['nChannels']=>integer() // - ['nCompressionLevel']=>integer() // - ['nFinalFrameSamples']=>integer() // - ['nFormatFlags']=>integer() // - ['nPeakLevel']=>integer() // - ['nSampleRate']=>integer() // - ['nSeekElements']=>integer() // - ['nTotalFrames']=>integer() // - ['nVersion']=>integer() // - ['nWAVHeaderBytes']=>integer() // - ['nWAVTerminatingBytes']=>integer() // - } // - ['sample_rate']=>integer() // - ['samples']=>integer() // - ['samples_per_frame']=>integer() // - ['uncompressed_size']=>integer() // - ['version']=>double() // - } // - - - ['mpc']=>array() { // MPC (Musepack) - lossy audio compression - ['header']=>array() { // - ['album_gain_db']=>integer() // - ['album_peak']=>integer() // - ['album_peak_db']=>boolean() // - ['title_gain_db']=>integer() // - ['title_peak']=>integer() // - ['title_peak_db']=>boolean() // - ['begin_loud']=>boolean() // - ['end_loud']=>boolean() // - ['encoder_version']=>string() // - ['frame_count']=>integer() // - ['intensity_stereo']=>boolean() // - ['last_frame_length']=>integer() // - ['max_level']=>integer() // - ['max_subband']=>integer() // - ['mid_side_stereo']=>boolean() // - ['profile']=>string() // - ['sample_rate']=>integer() // - ['samples']=>integer() // - ['size']=>integer() // - ['stream_major_version']=>integer() // - ['stream_minor_version']=>integer() // - ['true_gapless']=>boolean() // - ['raw']=>array() { // - ['album_gain']=>integer() // - ['album_peak']=>integer() // - ['encoder_version']=>integer() // - ['preamble']=>string() // - ['profile']=>integer() // - ['sample_rate']=>integer() // - ['title_gain']=>integer() // - ['title_peak']=>integer() // - } // - } // - } // - - - ['mpeg']=>array() { // MPEG (Motion Picture Experts Group) - MPEG video and/or MPEG audio (MP3/MP2/MP1) - ['audio']=>array() { // - ['LAME']=>array() { // - ['RGAD']=>array() { // - ['peak_amplitude']=>double() // - } // - ['ath_type']=>integer() // - ['audio_bytes']=>integer() // - ['bitrate_min']=>integer() // - ['encoder_delay']=>integer() // - ['encoding_flags']=>array() { // - ['nogap_next']=>boolean() // - ['nogap_prev']=>boolean() // - ['nspsytune']=>boolean() // - ['nssafejoint']=>boolean() // - } // - ['end_padding']=>integer() // - ['lame_tag_crc']=>integer() // - ['lowpass_frequency']=>integer() // - ['mp3_gain_db']=>double() // - ['mp3_gain_factor']=>double() // - ['mp3_gain_raw']=>integer() // - ['music_crc']=>integer() // - ['noise_shaping']=>integer() // - ['noise_shaping_raw']=>integer() // - ['not_optimal_quality']=>boolean() // - ['not_optimal_quality_raw']=>integer() // - ['preset_used_id']=>integer() // - ['short_version']=>string() // ex: "LAME 3.93" - ['long_version']=>string() // (pre-v3.90 only) ex: "LAME 3.88 (alpha)" - ['source_sample_freq']=>string() // - ['source_sample_freq_raw']=>integer() // - ['stereo_mode']=>string() // - ['stereo_mode_raw']=>integer() // - ['surround_info']=>string() // - ['surround_info_id']=>integer() // - ['tag_revision']=>integer() // - ['vbr_method']=>string() // - ['vbr_method_raw']=>integer() // - } // - ['VBR_bitrate']=>double() // - ['VBR_bytes']=>integer() // - ['VBR_frames']=>integer() // - ['VBR_method']=>string() // - ['VBR_scale']=>integer() // - ['bitrate']=>integer() // - ['bitrate_distribution']=>array() { // - ['free']=>integer() // - ['8']=>integer() // - ['16']=>integer() // - ['24']=>integer() // - ['32']=>integer() // - ['40']=>integer() // - ['48']=>integer() // - ['56']=>integer() // - ['64']=>integer() // - ['80']=>integer() // - ['96']=>integer() // - ['112']=>integer() // - ['128']=>integer() // - ['144']=>integer() // - ['160']=>integer() // - } // - ['bitrate_mode']=>string() // - ['channelmode']=>string() // - ['channels']=>integer() // - ['copyright']=>boolean() // - ['crc']=>integer() // - ['emphasis']=>string() // - ['frame_count']=>integer() // - ['framelength']=>integer() // - ['layer']=>integer() // - ['modeextension']=>string() // - ['original']=>boolean() // - ['padding']=>boolean() // - ['private']=>boolean() // - ['protection']=>boolean() // - ['raw']=>array() { // - ['bitrate']=>integer() // - ['channelmode']=>integer() // - ['copyright']=>integer() // - ['emphasis']=>integer() // - ['layer']=>integer() // - ['modeextension']=>integer() // - ['original']=>integer() // - ['padding']=>integer() // - ['private']=>integer() // - ['protection']=>integer() // - ['sample_rate']=>integer() // - ['synch']=>integer() // - ['version']=>integer() // - } // - ['sample_rate']=>integer() // - ['stereo_distribution']=>array() { // - ['dual channel']=>integer() // - ['joint stereo']=>integer() // - ['mono']=>integer() // - ['stereo']=>integer() // - } // - ['toc']=>array() { // - []=>integer() // - } // - ['version']=>string() // - ['version_distribution']=>array() { // - []=>integer() // - []=>integer() // - ['2.5']=>integer() // - } // - ['xing_flags']=>array() { // - ['bytes']=>boolean() // - ['frames']=>boolean() // - ['toc']=>boolean() // - ['vbr_scale']=>boolean() // - } // - ['xing_flags_raw']=>string() // - } // - ['video']=>array() { // - ['bitrate']=>integer() // - ['bitrate_mode']=>string() // - ['frame_rate']=>double() // - ['framesize_horizontal']=>integer() // - ['framesize_vertical']=>integer() // - ['pixel_aspect_ratio']=>double() // - ['pixel_aspect_ratio_text']=>string() // - ['raw']=>array() { // - ['bitrate']=>integer() // - ['constrained_param_flag']=>integer() // - ['frame_rate']=>integer() // - ['framesize_horizontal']=>integer() // - ['framesize_vertical']=>integer() // - ['intra_quant_flag']=>integer() // - ['marker_bit']=>integer() // - ['pixel_aspect_ratio']=>integer() // - ['vbv_buffer_size']=>integer() // - } // - } // - } // - - - ['nsv']=>array() { // NSV - Nullsoft Streaming Video - ['NSVf']=>array() { // - ['TOC_entries_1']=>integer() // - ['TOC_entries_2']=>integer() // - ['file_size']=>integer() // - ['header_length']=>integer() // - ['identifier']=>string() // - ['meta_size']=>integer() // - ['metadata']=>string() // - ['playtime_ms']=>integer() // - } // - ['NSVs']=>array() { // - ['audio_codec']=>string() // - ['frame_rate']=>double() // - ['framerate_index']=>integer() // - ['identifier']=>string() // - ['offset']=>integer() // - ['resolution_x']=>integer() // - ['resolution_y']=>integer() // - ['unknown1b']=>integer() // - ['unknown1c']=>integer() // - ['unknown1d']=>integer() // - ['unknown2a']=>integer() // - ['unknown2b']=>integer() // - ['unknown2c']=>integer() // - ['unknown2d']=>integer() // - ['unknown3a']=>integer() // - ['unknown3b']=>integer() // - ['unknown3c']=>integer() // - ['unknown3d']=>integer() // - ['video_codec']=>string() // - } // - ['comments']=>array() { // - ['aspect']=>string() // - ['title']=>string() // - } // - } // - - - ['ofr']=>array() { // OFR (OptimFROG) - lossless audio compression - ['COMP']=>array() { // - []=>array() { // - ['channel_configuration']=>string() // - ['crc_32']=>boolean() // - ['encoder']=>string() // - ['offset']=>integer() // - ['raw']=>array() { // - ['algorithm_id']=>integer() // - ['channel_configuration']=>integer() // - ['encoder_id']=>integer() // - ['sample_type']=>integer() // - } // - ['sample_count']=>integer() // - ['sample_type']=>string() // - ['size']=>integer() // - } // - } // - ['HEAD']=>array() { // - ['offset']=>integer() // - ['size']=>integer() // - } // - ['OFR ']=>array() { // - ['channel_config']=>integer() // - ['channels']=>integer() // - ['compression']=>string() // - ['encoder']=>string() // - ['offset']=>integer() // - ['raw']=>array() { // - ['compression']=>integer() // - ['encoder_id']=>integer() // - ['sample_type']=>integer() // - } // - ['sample_rate']=>integer() // - ['sample_type']=>string() // - ['size']=>integer() // - ['total_samples']=>integer() // - } // - ['TAIL']=>array() { // - ['offset']=>integer() // - ['size']=>integer() // - } // - } // - - - ['ogg']=>array() { // OGG - container format for Ogg Vorbis, OggFLAC, Speex, etc - ['bitrate_average']=>double() // - ['bitrate_max']=>integer() // - ['bitrate_min']=>integer() // - ['bitrate_nominal']=>integer() // - ['bitstreamversion']=>integer() // - ['blocksize_large']=>integer() // - ['blocksize_small']=>integer() // - ['comments']=>array() { // array of array of strings containing best data from any available metainformation tag (APE, ID3v2, ID3v1, Lyrics3, Vorbis, ASF, RIFF, Real, etc.) - []=>array() // can be anything, usually 'artist', 'title', etc. Contains array of one or more values (eg: multiple artists are possible) - } // - ['comments_raw']=>array() { // - []=>array() { // - ['dataoffset']=>integer() // - ['key']=>string() // - ['size']=>integer() // - ['value']=>string() // - } // - } // - ['numberofchannels']=>integer() // - ['pageheader']=>array() { // - []=>array() { // - ['flags']=>array() { // - ['bos']=>boolean() // - ['eos']=>boolean() // - ['fresh']=>boolean() // - } // - ['flags_raw']=>integer() // - ['header_end_offset']=>integer() // - ['packet_type']=>integer() // - ['page_checksum']=>double() // - ['page_end_offset']=>integer() // - ['page_length']=>integer() // - ['page_segments']=>integer() // - ['page_seqno']=>integer() // - ['page_start_offset']=>integer() // - ['pcm_abs_position']=>integer() // - ['segment_table']=>array() { // - []=>integer() // - } // - ['stream_serialno']=>integer() // - ['stream_structver']=>integer() // - ['stream_type']=>string() // - } // - ['eos']=>array() { // - ['flags']=>array() { // - ['bos']=>boolean() // - ['eos']=>boolean() // - ['fresh']=>boolean() // - } // - ['flags_raw']=>integer() // - ['header_end_offset']=>integer() // - ['page_checksum']=>double() // - ['page_end_offset']=>integer() // - ['page_length']=>integer() // - ['page_segments']=>integer() // - ['page_seqno']=>integer() // - ['page_start_offset']=>integer() // - ['pcm_abs_position']=>integer() // - ['segment_table']=>array() { // - []=>integer() // - } // - ['stream_serialno']=>integer() // - ['stream_structver']=>integer() // - } // - } // - ['samplerate']=>integer() // - ['samples']=>integer() // - ['stop_bit']=>integer() // - ['vendor']=>string() // - } // - - - ['png']=>array() { // PNG (Portable Network Graphics) - still image - ['IDAT']=>array() { // - []=>array() { // - ['header']=>array() { // - ['crc']=>integer() // - ['data_length']=>integer() // - ['flags']=>array() { // - ['ancilliary']=>boolean() // - ['private']=>boolean() // - ['reserved']=>boolean() // - ['safe_to_copy']=>boolean() // - } // - ['type_raw']=>double() // - ['type_text']=>string() // - } // - } // - } // - ['IEND']=>array() { // - ['header']=>array() { // - ['crc']=>double() // - ['data']=>string() // - ['data_length']=>integer() // - ['flags']=>array() { // - ['ancilliary']=>boolean() // - ['private']=>boolean() // - ['reserved']=>boolean() // - ['safe_to_copy']=>boolean() // - } // - ['type_raw']=>double() // - ['type_text']=>string() // - } // - } // - ['IHDR']=>array() { // - ['color_type']=>array() { // - ['alpha']=>boolean() // - ['palette']=>boolean() // - ['true_color']=>boolean() // - } // - ['compression_method_text']=>string() // - ['header']=>array() { // - ['crc']=>double() // - ['data']=>string() // - ['data_length']=>integer() // - ['flags']=>array() { // - ['ancilliary']=>boolean() // - ['private']=>boolean() // - ['reserved']=>boolean() // - ['safe_to_copy']=>boolean() // - } // - ['type_raw']=>double() // - ['type_text']=>string() // - } // - ['height']=>integer() // - ['raw']=>array() { // - ['bit_depth']=>integer() // - ['color_type']=>integer() // - ['compression_method']=>integer() // - ['filter_method']=>integer() // - ['interlace_method']=>integer() // - } // - ['width']=>integer() // - } // - ['PLTE']=>array() { // - ['header']=>array() { // - ['crc']=>double() // - ['data']=>string() // - ['data_length']=>integer() // - ['flags']=>array() { // - ['ancilliary']=>boolean() // - ['private']=>boolean() // - ['reserved']=>boolean() // - ['safe_to_copy']=>boolean() // - } // - ['type_raw']=>double() // - ['type_text']=>string() // - } // - []=>integer() // - } // - ['comments']=>array() { // array of array of strings containing best data from any available metainformation tag (APE, ID3v2, ID3v1, Lyrics3, Vorbis, ASF, RIFF, Real, etc.) - []=>array() // can be anything, usually 'artist', 'title', etc. Contains array of one or more values (eg: multiple artists are possible) - } // - ['gAMA']=>array() { // - ['gamma']=>double() // - ['header']=>array() { // - ['crc']=>integer() // - ['data']=>string() // - ['data_length']=>integer() // - ['flags']=>array() { // - ['ancilliary']=>boolean() // - ['private']=>boolean() // - ['reserved']=>boolean() // - ['safe_to_copy']=>boolean() // - } // - ['type_raw']=>double() // - ['type_text']=>string() // - } // - } // - ['oFFs']=>array() { // - ['header']=>array() { // - ['crc']=>double() // - ['data']=>string() // - ['data_length']=>integer() // - ['flags']=>array() { // - ['ancilliary']=>boolean() // - ['private']=>boolean() // - ['reserved']=>boolean() // - ['safe_to_copy']=>boolean() // - } // - ['type_raw']=>double() // - ['type_text']=>string() // - } // - ['position_x']=>integer() // - ['position_y']=>integer() // - ['unit']=>string() // - ['unit_specifier']=>integer() // - } // - ['pHYs']=>array() { // - ['header']=>array() { // - ['crc']=>integer() // - ['data']=>string() // - ['data_length']=>integer() // - ['flags']=>array() { // - ['ancilliary']=>boolean() // - ['private']=>boolean() // - ['reserved']=>boolean() // - ['safe_to_copy']=>boolean() // - } // - ['type_raw']=>double() // - ['type_text']=>string() // - } // - ['pixels_per_unit_x']=>integer() // - ['pixels_per_unit_y']=>integer() // - ['unit']=>string() // - ['unit_specifier']=>integer() // - } // - ['pcLb']=>array() { // - ['header']=>array() { // - ['crc']=>double() // - ['data']=>string() // - ['data_length']=>integer() // - ['flags']=>array() { // - ['ancilliary']=>boolean() // - ['private']=>boolean() // - ['reserved']=>boolean() // - ['safe_to_copy']=>boolean() // - } // - ['type_raw']=>double() // - ['type_text']=>string() // - } // - } // - ['tEXt']=>array() { // - ['header']=>array() { // - ['crc']=>integer() // - ['data']=>string() // - ['data_length']=>integer() // - ['flags']=>array() { // - ['ancilliary']=>boolean() // - ['private']=>boolean() // - ['reserved']=>boolean() // - ['safe_to_copy']=>boolean() // - } // - ['type_raw']=>double() // - ['type_text']=>string() // - } // - ['keyword']=>string() // - ['text']=>string() // - } // - ['tIME']=>array() { // - ['day']=>integer() // - ['header']=>array() { // - ['crc']=>integer() // - ['data']=>string() // - ['data_length']=>integer() // - ['flags']=>array() { // - ['ancilliary']=>boolean() // - ['private']=>boolean() // - ['reserved']=>boolean() // - ['safe_to_copy']=>boolean() // - } // - ['type_raw']=>double() // - ['type_text']=>string() // - } // - ['hour']=>integer() // - ['minute']=>integer() // - ['month']=>integer() // - ['second']=>integer() // - ['unix']=>integer() // - ['year']=>integer() // - } // - ['tRNS']=>array() { // - ['header']=>array() { // - ['crc']=>double() // - ['data']=>string() // - ['data_length']=>integer() // - ['flags']=>array() { // - ['ancilliary']=>boolean() // - ['private']=>boolean() // - ['reserved']=>boolean() // - ['safe_to_copy']=>boolean() // - } // - ['type_raw']=>double() // - ['type_text']=>string() // - } // - ['transparent_color_blue']=>integer() // - ['transparent_color_green']=>integer() // - ['transparent_color_red']=>integer() // - } // - ['zTXt']=>array() { // - ['compressed_text']=>string() // - ['compression_method']=>integer() // - ['compression_method_text']=>string() // - ['header']=>array() { // - ['crc']=>double() // - ['data']=>string() // - ['data_length']=>integer() // - ['flags']=>array() { // - ['ancilliary']=>boolean() // - ['private']=>boolean() // - ['reserved']=>boolean() // - ['safe_to_copy']=>boolean() // - } // - ['type_raw']=>double() // - ['type_text']=>string() // - } // - ['keyword']=>string() // - ['text']=>string() // - } // - } // - - - ['quicktime']=>array() { // Quicktime - video/audio - ['']=>array() { // - ['name']=>boolean() // - ['offset']=>integer() // - ['size']=>integer() // - } // - ['audio']=>array() { // - ['bit_depth']=>integer() // - ['channels']=>integer() // - ['codec']=>string() // - ['sample_rate']=>double() // - } // - ['free']=>array() { // - ['name']=>string() // - ['offset']=>integer() // - ['size']=>integer() // - } // - ['mdat']=>array() { // - ['name']=>string() // - ['offset']=>integer() // - ['size']=>integer() // - } // - ['moov']=>array() { // - ['hierarchy']=>string() // - ['name']=>string() // - ['offset']=>integer() // - ['size']=>integer() // - ['subatoms']=>array() // This is an undocumentably-complex recursive array, typically containing a huge amount of seemingly disorganized data. Avoid this like the plague. - } // - ['time_scale']=>integer() // - ['display_scale']=>integer() // 1 = normal; 0.5 = half; 2 = double - ['video']=>array() { // - ['codec']=>string() // - ['color_depth']=>integer() // - ['color_depth_name']=>string() // - ['resolution_x']=>double() // - ['resolution_y']=>double() // - } // - ['wide']=>array() { // - ['name']=>string() // - ['offset']=>integer() // - ['size']=>integer() // - } // - } // - - - ['real']=>array() { // Real (RealAudio / RealVideo) - audio/video - ['chunks']=>array() { // - []=>array() { // - ['file_version']=>integer() // - ['headers_count']=>integer() // - ['length']=>integer() // - ['name']=>string() // - ['object_version']=>integer() // - ['offset']=>integer() // - } // - []=>array() { // - ['avg_bit_rate']=>integer() // - ['avg_packet_size']=>integer() // - ['data_offset']=>integer() // - ['duration']=>integer() // - ['flags']=>array() { // - ['live_broadcast']=>boolean() // - ['perfect_play']=>boolean() // - ['save_enabled']=>boolean() // - } // - ['flags_raw']=>integer() // - ['index_offset']=>integer() // - ['length']=>integer() // - ['max_bit_rate']=>integer() // - ['max_packet_size']=>integer() // - ['name']=>string() // - ['num_packets']=>integer() // - ['num_streams']=>integer() // - ['object_version']=>integer() // - ['offset']=>integer() // - ['preroll']=>integer() // - } // - } // - ['comments']=>array() { // - ['artist']=>string() // - ['comment']=>string() // - ['title']=>string() // - } // - } // - - - ['riff']=>array() { // RIFF (Resource Interchange File Format) - audio/video container format (AVI, WAV, CDDA, etc) - ['AIFC']=>array() { // - ['COMM']=>array() { // - []=>array() { // - ['data']=>string() // - ['offset']=>integer() // - ['size']=>integer() // - } // - } // - ['FVER']=>array() { // - []=>array() { // - ['data']=>string() // - ['offset']=>integer() // - ['size']=>integer() // - } // - } // - ['INST']=>array() { // - []=>array() { // - ['data']=>string() // - ['offset']=>integer() // - ['size']=>integer() // - } // - } // - ['MARK']=>array() { // - []=>array() { // - ['data']=>string() // - ['offset']=>integer() // - ['size']=>integer() // - } // - } // - ['SSND']=>array() { // - []=>array() { // - ['offset']=>integer() // - ['size']=>integer() // - } // - } // - } // - ['AIFF']=>array() { // - ['(c) ']=>array() { // - []=>array() { // - ['data']=>string() // - ['offset']=>integer() // - ['size']=>integer() // - } // - } // - ['COMM']=>array() { // - []=>array() { // - ['data']=>string() // - ['offset']=>integer() // - ['size']=>integer() // - } // - } // - ['SSND']=>array() { // - []=>array() { // - ['offset']=>integer() // - ['size']=>integer() // - } // - } // - } // - ['AVI ']=>array() { // - ['JUNK']=>array() { // - []=>array() { // - ['data']=>string() // - ['offset']=>integer() // - ['size']=>integer() // - } // - } // - ['hdrl']=>array() { // - ['avih']=>array() { // - []=>array() { // - ['data']=>string() // - ['offset']=>integer() // - ['size']=>integer() // - } // - } // - ['odml']=>array() { // - ['dmlh']=>array() { // - []=>array() { // - ['data']=>string() // - ['offset']=>integer() // - ['size']=>integer() // - } // - } // - } // - ['strl']=>array() { // - ['JUNK']=>array() { // - []=>array() { // - ['offset']=>integer() // - ['size']=>integer() // - } // - } // - ['strf']=>array() { // - []=>array() { // - ['data']=>string() // - ['offset']=>integer() // - ['size']=>integer() // - } // - } // - ['strh']=>array() { // - []=>array() { // - ['data']=>string() // - ['offset']=>integer() // - ['size']=>integer() // - } // - } // - ['strn']=>array() { // - []=>array() { // - ['data']=>string() // - ['offset']=>integer() // - ['size']=>integer() // - } // - } // - } // - } // - ['idx1']=>array() { // - []=>array() { // - ['data']=>string() // - ['offset']=>integer() // - ['size']=>integer() // - } // - } // - ['movi']=>array() { // - ['offset']=>integer() // - ['size']=>integer() // - } // - } // - ['CDDA']=>array() { // - ['fmt ']=>array() { // - []=>array() { // - ['data']=>string() // - ['disc_id']=>integer() // - ['offset']=>integer() // - ['playtime_frames']=>integer() // - ['playtime_seconds']=>double() // - ['size']=>integer() // - ['start_offset_frame']=>integer() // - ['start_offset_seconds']=>double() // - ['track_num']=>integer() // - ['unknown1']=>integer() // - ['unknown6']=>integer() // - ['unknown7']=>integer() // - } // - } // - } // - ['WAVE']=>array() { // - ['DISP']=>array() { // - []=>array() { // - ['data']=>string() // - ['offset']=>integer() // - ['size']=>integer() // - } // - } // - ['INFO']=>array() { // - ['IART']=>array() { // - []=>array() { // - ['data']=>string() // - ['offset']=>integer() // - ['size']=>integer() // - } // - } // - ['ICMT']=>array() { // - []=>array() { // - ['data']=>string() // - ['offset']=>integer() // - ['size']=>integer() // - } // - } // - ['ICOP']=>array() { // - []=>array() { // - ['data']=>string() // - ['offset']=>integer() // - ['size']=>integer() // - } // - } // - ['IENG']=>array() { // - []=>array() { // - ['data']=>string() // - ['offset']=>integer() // - ['size']=>integer() // - } // - } // - ['IGNR']=>array() { // - []=>array() { // - ['data']=>string() // - ['offset']=>integer() // - ['size']=>integer() // - } // - } // - ['IKEY']=>array() { // - []=>array() { // - ['data']=>string() // - ['offset']=>integer() // - ['size']=>integer() // - } // - } // - ['IMED']=>array() { // - []=>array() { // - ['data']=>string() // - ['offset']=>integer() // - ['size']=>integer() // - } // - } // - ['INAM']=>array() { // - []=>array() { // - ['data']=>string() // - ['offset']=>integer() // - ['size']=>integer() // - } // - } // - ['ISBJ']=>array() { // - []=>array() { // - ['data']=>string() // - ['offset']=>integer() // - ['size']=>integer() // - } // - } // - ['ISFT']=>array() { // - []=>array() { // - ['data']=>string() // - ['offset']=>integer() // - ['size']=>integer() // - } // - } // - ['ISRC']=>array() { // - []=>array() { // - ['data']=>string() // - ['offset']=>integer() // - ['size']=>integer() // - } // - } // - ['ISRF']=>array() { // - []=>array() { // - ['data']=>string() // - ['offset']=>integer() // - ['size']=>integer() // - } // - } // - ['ITCH']=>array() { // - []=>array() { // - ['data']=>string() // - ['offset']=>integer() // - ['size']=>integer() // - } // - } // - } // - ['MEXT']=>array() { // - []=>array() { // - ['anciliary_data_length']=>integer() // - ['data']=>string() // - ['flags']=>array() { // - ['anciliary_data_free']=>boolean() // - ['anciliary_data_left']=>boolean() // - ['anciliary_data_right']=>boolean() // - ['homogenous']=>boolean() // - } // - ['offset']=>integer() // - ['raw']=>array() { // - ['anciliary_data_def']=>integer() // - ['sound_information']=>integer() // - } // - ['size']=>integer() // - } // - } // - ['bext']=>array() { // - []=>array() { // - ['author']=>string() // - ['bwf_version']=>integer() // - ['coding_history']=>array() { // - []=>string() // - } // - ['data']=>string() // - ['offset']=>integer() // - ['origin_date']=>string() // - ['origin_date_unix']=>integer() // - ['origin_time']=>string() // - ['reference']=>string() // - ['reserved']=>integer() // - ['size']=>integer() // - ['time_reference']=>integer() // - ['title']=>string() // - } // - } // - ['cart']=>array() { // - []=>array() { // - ['artist']=>string() // - ['category']=>string() // - ['classification']=>string() // - ['client_id']=>string() // - ['cut_id']=>string() // - ['data']=>string() // - ['end_date']=>string() // - ['end_time']=>string() // - ['offset']=>integer() // - ['out_cue']=>string() // - ['post_time']=>array() { // - []=>array() { // - ['timer_value']=>integer() // - ['usage_fourcc']=>string() // - } // - } // - ['producer_app_id']=>string() // - ['producer_app_version']=>string() // - ['size']=>integer() // - ['start_date']=>string() // - ['start_time']=>string() // - ['tag_text']=>array() { // - []=>string() // - } // - ['title']=>string() // - ['url']=>string() // - ['user_defined_text']=>string() // - ['version']=>string() // - ['zero_db_reference']=>integer() // - } // - } // - ['data']=>array() { // - []=>array() { // - ['offset']=>integer() // - ['size']=>integer() // - } // - } // - ['fact']=>array() { // - []=>array() { // - ['data']=>string() // - ['offset']=>integer() // - ['size']=>integer() // - } // - } // - ['fmt ']=>array() { // - []=>array() { // - ['data']=>string() // - ['offset']=>integer() // - ['size']=>integer() // - } // - } // - ['rgad']=>array() { // - []=>array() { // - ['data']=>string() // - ['offset']=>integer() // - ['size']=>integer() // - } // - } // - } // - ['audio']=>array() { // - []=>array() { // - ['bitrate']=>integer() // - ['bits_per_sample']=>integer() // - ['channels']=>integer() // - ['codec']=>string() // - ['sample_rate']=>integer() // - } // - ['bits_per_sample']=>integer() // - ['channels']=>integer() // - ['codec_fourcc']=>string() // - ['codec_name']=>string() // - ['sample_rate']=>integer() // - ['total_samples']=>integer() // - } // - ['comments']=>array() { // array of array of strings containing best data from any available metainformation tag (APE, ID3v2, ID3v1, Lyrics3, Vorbis, ASF, RIFF, Real, etc.) - []=>array() // can be anything, usually 'artist', 'title', etc. Contains array of one or more values (eg: multiple artists are possible) - } // - ['header_size']=>integer() // - ['raw']=>array() { // - ['avih']=>array() { // - ['dwFlags']=>integer() // - ['dwHeight']=>integer() // - ['dwInitialFrames']=>integer() // - ['dwLength']=>integer() // - ['dwMaxBytesPerSec']=>integer() // - ['dwMicroSecPerFrame']=>integer() // - ['dwPaddingGranularity']=>integer() // - ['dwRate']=>integer() // - ['dwScale']=>integer() // - ['dwStart']=>integer() // - ['dwStreams']=>integer() // - ['dwSuggestedBufferSize']=>integer() // - ['dwTotalFrames']=>integer() // - ['dwWidth']=>integer() // - ['flags']=>array() { // - ['capturedfile']=>boolean() // - ['copyrighted']=>boolean() // - ['hasindex']=>boolean() // - ['interleaved']=>boolean() // - ['mustuseindex']=>boolean() // - ['trustcktype']=>boolean() // - } // - } // - ['fact']=>array() { // - ['NumberOfSamples']=>integer() // - } // - ['fmt ']=>array() { // - ['nAvgBytesPerSec']=>integer() // - ['wBitsPerSample']=>integer() // - ['nBlockAlign']=>integer() // - ['nChannels']=>integer() // - ['nSamplesPerSec']=>integer() // - ['wFormatTag']=>integer() // - } // - ['rgad']=>array() { // - ['audiophile']=>array() { // - ['adjustment']=>integer() // - ['name']=>integer() // - ['originator']=>integer() // - ['signbit']=>integer() // - } // - ['fPeakAmplitude']=>double() // - ['nAudiophileRgAdjust']=>integer() // - ['nRadioRgAdjust']=>integer() // - ['radio']=>array() { // - ['adjustment']=>integer() // - ['name']=>integer() // - ['originator']=>integer() // - ['signbit']=>integer() // - } // - } // - ['strf']=>array() { // - ['auds']=>array() { // - []=>array() { // - ['nAvgBytesPerSec']=>integer() // - ['wBitsPerSample']=>integer() // - ['nBlockAlign']=>integer() // - ['nChannels']=>integer() // - ['nSamplesPerSec']=>integer() // - ['wFormatTag']=>integer() // - } // - } // - ['vids']=>array() { // - []=>array() { // - ['biBitCount']=>integer() // - ['biClrImportant']=>integer() // - ['biClrUsed']=>integer() // - ['biHeight']=>integer() // - ['biPlanes']=>integer() // - ['biSize']=>integer() // - ['biSizeImage']=>integer() // - ['biWidth']=>integer() // - ['biXPelsPerMeter']=>integer() // - ['biYPelsPerMeter']=>integer() // - ['fourcc']=>string() // - } // - } // - } // - ['strh']=>array() { // - []=>array() { // - ['dwFlags']=>integer() // - ['dwInitialFrames']=>integer() // - ['dwLength']=>integer() // - ['dwQuality']=>integer() // - ['dwRate']=>integer() // - ['dwSampleSize']=>integer() // - ['dwScale']=>integer() // - ['dwStart']=>integer() // - ['dwSuggestedBufferSize']=>integer() // - ['fccHandler']=>string() // - ['fccType']=>string() // - ['rcFrame']=>integer() // - ['wLanguage']=>integer() // - ['wPriority']=>integer() // - } // - } // - } // - ['rgad']=>array() { // - ['audiophile']=>array() { // - ['adjustment']=>double() // - ['name']=>string() // - ['originator']=>string() // - } // - ['peakamplitude']=>double() // - ['radio']=>array() { // - ['adjustment']=>double() // - ['name']=>string() // - ['originator']=>string() // - } // - } // - ['video']=>array() { // - []=>array() { // - ['codec']=>string() // - ['frame_height']=>integer() // - ['frame_rate']=>double() // - ['frame_width']=>integer() // - } // - } // - ['litewave']=>array() { // http://www.clearjump.com - ['raw']=>array() { // - ['compression_method']=>integer() // 1=lossy; 2=lossless - ['compression_flags']=>integer() // - ['m_dwScale']=>integer() // scalefactor for lossy compression - related to m_wQuality as: $m_wQuality = round((2000 - $m_dwScale) / 20) - ['m_dwBlockSize']=>integer() // number of samples in encoded blocks - ['m_wQuality']=>integer() // quality factor (0=most compressed lossy; 99=best quality lossy; 100=lossless) - ['m_wMarkDistance']=>integer() // distance between marks in bytes - ['m_wReserved']=>integer() // - ['m_dwOrgSize']=>integer() // original file size in bytes - ['m_bFactExists']=>integer() // indicates if 'fact' chunk exists in the original file - ['m_dwRiffChunkSize']=>integer() // riff chunk size in the original file - } // - ['quality_factor']=>integer() // alias of ['raw']['m_wQuality'] - } // - } // - - - ['shn']=>array() { // Shorten - lossless audio compression - ['seektable']=>array() { // - ['length']=>integer() // - ['offset']=>integer() // - ['present']=>boolean() // - } // - ['version']=>integer() // - } // - - - ['swf']=>array() { // SWF - ShockWave Flash (www.openswf.org) - ['header']=>array() { // - ['frame_count']=>integer() // - ['frame_height']=>integer() // - ['frame_width']=>integer() // - ['length']=>integer() // - ['signature']=>string() // - ['version']=>integer() // - } // - ['bgcolor']=>string() // - ['tags']=>array() // - } // - - - ['voc']=>array() { // VOC - SoundBlaster VOC audio format - ['blocks']=>array() { // - []=>array() { // - ['bits_per_sample']=>integer() // - ['block_offset']=>integer() // - ['block_size']=>integer() // - ['block_type_id']=>integer() // - ['channels']=>integer() // - ['compression_name']=>string() // - ['compression_type']=>integer() // - ['pack_method']=>integer() // - ['sample_rate']=>integer() // - ['sample_rate_id']=>integer() // - ['stereo']=>boolean() // - ['time_constant']=>integer() // - ['wFormat']=>integer() // - } // - } // - ['compressed_bits_per_sample']=>integer() // - ['header']=>array() { // - ['datablock_offset']=>integer() // - ['major_version']=>integer() // - ['minor_version']=>integer() // - } // - } // - - - ['vqf']=>array() { // VQF - transform-domain weighted interleave Vector Quantization Format (lossy audio) - ['COMM']=>array() { // - ['bitrate']=>integer() // - ['channel_mode']=>integer() // - ['sample_rate']=>integer() // - ['security_level']=>integer() // - } // - ['DSIZ']=>integer() // - ['comments']=>array() { // array of array of strings containing best data from any available metainformation tag (APE, ID3v2, ID3v1, Lyrics3, Vorbis, ASF, RIFF, Real, etc.) - []=>array() // can be anything, usually 'artist', 'title', etc. Contains array of one or more values (eg: multiple artists are possible) - } // - ['raw']=>array() { // - ['header_tag']=>string() // - ['size']=>integer() // - ['version']=>string() // - } // - } // - - - ['wavpack']=>array() { // WavPack - lossless audio compression - ['bits']=>integer() // - ['crc1']=>double() // - ['crc2']=>integer() // - ['extension']=>string() // - ['extra_bc']=>string() // - ['extras']=>string() // - ['flags_raw']=>integer() // - ['offset']=>integer() // - ['shift']=>integer() // - ['size']=>integer() // - ['total_samples']=>integer() // - ['version']=>integer() // - } // - - - ['zip']=>array() { // ZIP - lossless data compression - ['central_directory']=>array() { // - []=>array() { // - ['compressed_size']=>integer() // - ['compression_method']=>string() // - ['create_version']=>string() // - ['entry_offset']=>integer() // - ['extract_version']=>string() // - ['filename']=>string() // - ['flags']=>array() { // - ['compression_speed']=>string() // - ['data_descriptor_used']=>boolean() // - ['encrypted']=>boolean() // - } // - ['host_os']=>string() // - ['last_modified_timestamp']=>integer() // - ['offset']=>integer() // - ['raw']=>array() { // - ['compressed_size']=>integer() // - ['compression_method']=>integer() // - ['crc_32']=>double() // - ['create_version']=>integer() // - ['disk_number_start']=>integer() // - ['external_file_attrib']=>double() // - ['extra_field_length']=>integer() // - ['extract_version']=>integer() // - ['file_comment_length']=>integer() // - ['filename_length']=>integer() // - ['general_flags']=>integer() // - ['internal_file_attrib']=>integer() // - ['last_mod_file_date']=>integer() // - ['last_mod_file_time']=>integer() // - ['local_header_offset']=>integer() // - ['signature']=>integer() // - ['uncompressed_size']=>integer() // - } // - ['uncompressed_size']=>integer() // - } // - } // - ['comments']=>array() { // - ['comment']=>string() // - } // - ['compressed_size']=>integer() // - ['compression_method']=>string() // - ['compression_speed']=>string() // - ['end_central_directory']=>array() { // - ['comment']=>string() // - ['comment_length']=>integer() // - ['directory_entries_this_disk']=>integer() // - ['directory_entries_total']=>integer() // - ['directory_offset']=>integer() // - ['directory_size']=>integer() // - ['disk_number_current']=>integer() // - ['disk_number_start_directory']=>integer() // - ['offset']=>integer() // - ['signature']=>integer() // - } // - ['entries']=>array() { // - []=>array() { // - ['compressed_size']=>integer() // - ['compression_method']=>string() // - ['extract_version']=>string() // - ['filename']=>string() // - ['flags']=>array() { // - ['compression_speed']=>string() // - ['data_descriptor_used']=>boolean() // - ['encrypted']=>boolean() // - } // - ['host_os']=>string() // - ['last_modified_timestamp']=>integer() // - ['offset']=>integer() // - ['raw']=>array() { // - ['compressed_size']=>integer() // - ['compression_method']=>integer() // - ['crc_32']=>integer() // - ['extra_field_length']=>integer() // - ['extract_version']=>integer() // - ['filename_length']=>integer() // - ['general_flags']=>integer() // - ['last_mod_file_date']=>integer() // - ['last_mod_file_time']=>integer() // - ['signature']=>integer() // - ['uncompressed_size']=>integer() // - } // - ['uncompressed_size']=>integer() // - } // - } // - ['entries_count']=>integer() // - ['files']=>array() { // multidimensional tree-structure array listing of all files and directories in image - []=>array() // entries of type array are directories (key is directory name), may contain files and/or other subdirectories - []=>integer() // entries of type integer are files (key is file name, value is file size in bytes) - } // - ['uncompressed_size']=>integer() // - } // -} // diff --git a/htmlparser/html_parser_inc.php b/htmlparser/html_parser_inc.php deleted file mode 100644 index 83b0f66..0000000 --- a/htmlparser/html_parser_inc.php +++ /dev/null @@ -1,492 +0,0 @@ -dc=array(" ","\t","\r","\n","<",">","\"","'","=","/"); - $this->nc=array("<",">","=","/"); - $this->qc=array("\"","'"); - $this->sc=array("\r","\n"," ","\t"); - $this->prevstate=array("state"=>0,"word"=>""); - $this->pg=&$grammar; - $this->pos=0; - $this->stacktag=array(); - $this->stacktagpos=-1; - $this->content=array(); - $this->content["contentpos"]=-1; - $this->c=&$this->content; - $this->cp=-1; - $this->quotstate=-1; - $this->allreadyparsed=0; - $this->text=""; - $this->processtag=0; - $this->processpar=0; - $this->processparvalue=0; - $this->slevel=array(0); - $this->slevelpos=0; - $this->quottype=""; - $this->skipto=""; - $this->incomment=0; - $this->tagreg=array(); - $this->wasquot=0; - - if(isset($this->data) && is_array($this->data)) { - $this->content=&$data; - $this->allreadyparsed=1; - return; - } - clearstatcache(); - $this->name=$data; - if (!$datatype) { - $this->name=$name; - $this->data=$data; - $this->length=strlen($this->data); - return; - } - if (!$fp=fopen($this->name,"rb")) { - $this->SetError(1,"Can't open file $this->name.",0,0,"Error"); - return; - } - flock($fp,1); - $this->data=fread($fp,filesize($this->name)); - flock($fp,3); - fclose($fp); - $this->length=strlen($this->data); - } - -/******************************************************************************************** - * Get word from data - ********************************************************************************************/ - function GetWord(&$word) { - $word=""; - $this->wasquot=0; - if ($this->pos>$this->length) return false; - while (1) { - if ($this->pos>$this->length) return false; - if ($this->pos==$this->length) { - $this->pos++; - return true; - } - if ($this->data[$this->pos]=="<") { - if ($this->data[$this->pos+1]=="!") - if ($this->length>6 && $this->length-$this->pos+1>6) { - if (substr($this->data,$this->pos,4)=="") { - $word.="-->"; - $this->pos+=3; - break; - } else - $word.=$this->data[$this->pos++]; - } - if ($this->incomment) break; - } - } - } - if (!$this->processtag) { - if ($this->data[$this->pos]=="<") { - $this->processtag=1; - $this->tagpos=strlen($this->text); - } else { - $this->text.=$this->data[$this->pos++]; - continue; - } - } - if (in_array($this->data[$this->pos],$this->dc)) { - if (($this->data[$this->pos]=="<" || $this->data[$this->pos]==">") && $this->quotstate==-1 && $this->processparvalue) { - $this->processparvalue=0; - return true; - } - if (in_array($this->data[$this->pos],$this->sc) && $this->quotstate==-1) { - $this->text.=$this->data[$this->pos++]; - if (strlen($word)) { - if ($this->processparvalue) $this->processparvalue=0; - return true; - } else - continue; - } - if (!strlen($word)) { - if (in_array($this->data[$this->pos],$this->qc) && $this->processpar) { - if ($this->quotstate==-1) { - $this->wasquot=1; - $this->quotstate*=-1; - $this->quottype=$this->data[$this->pos]; - $this->text.=$this->data[$this->pos++]; - continue; - } elseif ($this->quottype==$this->data[$this->pos]) { - $this->quotstate*=-1; - $this->quottype=$this->data[$this->pos]; - $this->processpar=$this->processparvalue=0; - $this->text.=$this->data[$this->pos++]; - return true; - } - } elseif (in_array($this->data[$this->pos],$this->nc)) { - $word.=$this->data[$this->pos]; - $this->text.=$this->data[$this->pos++]; - if ($this->processparvalue) - continue; - else - return true; - } - } else { - if (in_array($this->data[$this->pos],$this->qc) && $this->processpar) { - if ($this->quotstate==1) { - if ($this->data[$this->pos]==$this->quottype && $this->processparvalue) { - $this->quotstate*=-1; - $this->quottype=$this->data[$this->pos]; - $this->processpar=$this->processparvalue=0; - $this->text.=$this->data[$this->pos++]; -// continue; - } else { - if ($this->data[$this->pos]==$this->quottype) { - $this->quotstate*=-1; - $this->quottype=""; - } - $word.=$this->data[$this->pos]; - $this->text.=$this->data[$this->pos++]; - continue; - } - } - return true; - } else { - if (in_array($this->data[$this->pos],$this->nc)) { - if ($this->quotstate==-1) { - if ($this->processparvalue) { - if($this->data[$this->pos]!="/" && $this->data[$this->pos]!="=") return true; - $word.=$this->data[$this->pos]; - $this->text.=$this->data[$this->pos++]; - continue; - } - } else { - $word.=$this->data[$this->pos]; - $this->text.=$this->data[$this->pos++]; - continue; - } - return true; - } elseif ($this->quotstate==-1 && $this->processparvalue && strlen($word)) { - if ($this->data[$this->pos]==" ") { - $this->text.=$this->data[$this->pos++]; - $this->processparvalue=0; - return true; - } - } - } - } - } - $word.=$this->data[$this->pos]; - $this->text.=$this->data[$this->pos++]; - } - return true; - } - -/******************************************************************************************** - * Parse HTML code - ******************************************************************************************** - | -<[/]tagname> - -in/state 0 1 2 3 4 5 6 7 8 -< 1 -1 -1 -1 -1 -1 -1 -1 -1 -/ -1 7 6 6 6 6 -1 -1 -1 -= -1 -1 -1 4 -1 -1 -1 -1 -1 -> -1 -1 -2 -2 -2 -2 -2 -1 -3 -anyword -1 2 3 3 5 3 -1 8 -1 - --3 end parse close tag --2 end parse open tag --1 error - 0 begin parse - 1 got '<', waiting '/' or any word as tag name - 2 got any word as tagname, waiting '/' or '>' or any word as parameter name - 3 got any word as parameter name, waiting '/' or '>' or '=' or any word as parameter name - 4 got '=' waiting '/' or '>' or any word as parameter value - 5 got any word as parameter value, waiting '/' or '>' or any word as parameter name - 6 got '/' waiting '>' - 7 got '/', waiting any word as close tagname - 8 got any word as close tag name, waiting '>' - ********************************************************************************************/ - function Parse() { - $automat=array( -// states 0 1 2 3 4 5 6 7 8 - "0"=>array( 1, -1, -1, -1, -1, -1, -1, -1, -1),// < - "1"=>array(-1, 7, 6, 6, 6, 6, -1, -1, -1),// / - "2"=>array(-1, -1, -1, 4, -1, -1, -1, -1, -1),// = - "3"=>array(-1, -1, -2, -2, -2, -2, -2, -1, -3),// > - "4"=>array(-1, 2, 3, 3, 5, 3, -1, 8, -1) // any word - ); - if (!strlen($this->data)) return; - $instates=array("<"=>0,"/"=>1,"="=>2,">"=>3); - $parcount=0; - $state=0; - $this->c=&$this->content; - $this->cp=&$this->content["contentpos"]; - $this->stacktag[0]["tag"]=&$this->c; - $this->stacktag[0]["level"]=&$this->slevel; - $this->stacktag[0]["levelpos"]=0; - $this->stacktagpos=0; - while(1) { - if (!$isword=$this->GetWord($word)) break; - $w=strtolower($word); - if (!isset($instates[$w])) - $instate=4; - else - $instate=$instates[$w]; -//print htmlspecialchars($word).",$state,$instate,$this->quottype
"; - $state=$automat[$instate][$state]; - if ($this->wasquot && $state==6) $state=5; -//print htmlspecialchars($word).",$state
"; - switch($state) { - case -3:// end parse close tag - if (strlen($this->skipto) && $this->tagname!=$this->skipto) { - $parcount=$state=$this->processpar=$this->processparvalue=$this->processtag=0; - $this->pars=array(); - break; - } else - $this->skipto=""; - $script=($this->tagname=="script") ? 1:0; - $this->AddNewText(substr($this->text,0,$this->tagpos),$script); - $this->AddNewTag(0); - $parcount=$state=$this->processpar=$this->processparvalue=$this->processtag=0; - $this->quottype=""; - $this->quotstate=-1; - $this->text=""; - $this->pars=array(); - $this->tagpos=0; - break; - case -2:// end parse open tag - if (strlen($this->skipto)) { - $parcount=$state=$this->processpar=$this->processparvalue=$this->processtag=0; - $this->pars=array(); - break; - } - $this->AddNewText(substr($this->text,0,$this->tagpos)); - $this->AddNewTag(1,$xmlclose); - $parcount=$state=$this->processpar=$this->processparvalue=$this->processtag=0; - $this->quottype=""; - $this->quotstate=-1; - $this->text=""; - $this->pars=array(); - $this->tagpos=0; - if (isset($this->pg[$this->tagname]["nohavetags"]) && !strlen($this->skipto)) $this->skipto=$this->tagname; - break; - case -1:// Error found - $parcount=$state=$this->processpar=$this->processparvalue=$this->processtag=0; - $this->pars=array(); - if ($this->incomment) { - if (strlen($this->text)) { - $this->AddNewText($this->text); - $this->text=""; - $this->tagpos=0; - } - $this->AddNewText($word,0,1); - $this->incomment=0; - break; - } - if ($word=="<") { - $state=1; - $this->processtag=1; - $this->processparvalue=0; - $this->tagpos=strlen($this->text)-1; - $this->quottype=""; - $this->quotstate=-1; - } - break; - case 2:// got any word as tagname, waiting '/' or '>' or any word as parameter name - $this->tagname=$w; - $xmlclose=0; - if (!ereg("^[a-zA-Z0-9!_-]+$",$this->tagname) || strlen($this->skipto)) { - $parcount=$state=$this->processpar=$this->processparvalue=$this->processtag=0; - $this->quottype=""; - $this->quotstate=-1; - $this->pars=array(); - break; - } - break; - case 3:// got any word as parameter name, waiting '/' or '>' or '=' or any word as parameter name - $this->parname=$w; - if (!ereg("^[a-zA-Z0-9!_-]+$",$this->parname) || strlen($this->skipto)) { - $parcount=$state=$this->processpar=$this->processparvalue=$this->processtag=0; - $this->quottype=""; - $this->quotstate=-1; - $this->pars=array(); - break; - } - $this->processpar=1; - if ($w!="/") { - $parcount++; - $this->pars[$this->parname]["single"]=1; - } else - $xmlclose=1; - break; - case 4:// got '=' waiting '/' or '>' or any word as parameter value - $this->processparvalue=1; - break; - case 5:// got any word as parameter value, waiting '/' or '>' or any word as parameter name - if ($this->parname!="/") { - unset($this->pars[$this->parname]["single"]); - $this->pars[$this->parname]["value"]=$word; - $this->pars[$this->parname]["quot"]=$this->quottype; - } - $this->quottype=""; - $this->processpar=$this->processparvalue=0; - break; - case 6:// got '/' waiting '>' - $xmlclose=1; - break; - case 8:// got any word as close tag name, waiting '>' - $this->tagname=$w; - break; - } - $this->prevstate["states"]=$state; - $this->prevstate["word"]=$word; - } - if (strlen($this->text)) $this->AddNewText($this->text); - } -/******************************************************************************************** - * Add new tag - ********************************************************************************************/ - function AddNewTag($open,$xmlclose=0) { - $actionclose=0; - if (!$open && in_array( $this->tagname, $this->pg ) && $this->pg[$this->tagname]["endtag"]!="absent") $actionclose=1; - - if ($open) - for ($i=$this->stacktagpos;$i>0;$i--) { - $ct=&$this->stacktag[$i]["tag"]; - $t=&$ct[$ct["contentpos"]]; - $tagname=$t["data"]["name"]; - if (isset($this->pg[$tagname]["closeon"])) { - if (isset($this->pg[$tagname]["closeon"]["in"]) && sizeof($this->pg[$tagname]["closeon"]["in"]) && in_array($this->tagname,$this->pg[$tagname]["closeon"]["in"]) - || isset($this->pg[$tagname]["closeon"]["notin"]) && sizeof($this->pg[$tagname]["closeon"]["notin"]) && !in_array($this->tagname,$this->pg[$tagname]["closeon"]["notin"])) { - $actionclose=2; - break; - } - } - if ($actionclose!=2) $i=-1; - } - - if ($actionclose) { - if ($actionclose==1) { - $i=$this->FindTag($this->tagname); - if ($i>-1) - if ($this->tagreg[$this->tagname]!=$this->stacktag[$i]["num"]) - $i=-1; - } - if ($i>-1) { - $this->c=&$this->stacktag[$i]["tag"]; - $this->cp=&$this->c["contentpos"]; - $this->stacktagpos=$i; - if ($actionclose==1) { - $c=&$this->c[$this->c["contentpos"]]["content"]; - $cp=&$this->c[$this->c["contentpos"]]["content"]["contentpos"]; - $cp++; - $c[$cp]["type"]="tag"; - $c[$cp]["data"]["name"]=$this->tagname; - $c[$cp]["data"]["type"]="close"; - if (isset($this->tagreg[$this->tagname])) - if ($this->tagreg[$this->tagname]) - $this->tagreg[$this->tagname]--; - $this->stacktag[$this->stacktagpos]["num"]=$this->tagreg[$this->tagname]; - $this->stacktagpos--; - } - if ($this->stacktagposstacktag)) - for ($i=$this->stacktagpos+1;$istacktag);$i++) - unset($this->stacktag[$i]); - if ($actionclose==1) return; - } - } - $this->cp++; - $this->c[$this->cp]["type"]="tag"; - $this->c[$this->cp]["data"]["name"]=$this->tagname; - $this->c[$this->cp]["data"]["type"]=($open) ? "open" : "close"; - if (!$open) - if (isset($this->tagreg[$this->tagname])) - if ($this->tagreg[$this->tagname]) - $this->tagreg[$this->tagname]--; - if ($xmlclose) $this->c[$this->cp]["xmlclose"]=1; - if (sizeof($this->pars)) $this->c[$this->cp]["pars"]=$this->pars; - if ($open && !$xmlclose && in_array( $this->tagname, $this->pg ) && $this->pg[$this->tagname]["endtag"]!="absent") { - if (!isset($this->tagreg[$this->tagname])) $this->tagreg[$this->tagname]=0; - $this->tagreg[$this->tagname]++; - $this->stacktagpos++; - $this->stacktag[$this->stacktagpos]["tag"]=&$this->c; - $this->stacktag[$this->stacktagpos]["num"]=$this->tagreg[$this->tagname]; - $this->c[$this->cp]["content"]=array(); - $this->c[$this->cp]["content"]["contentpos"]=-1; - $this->c=&$this->c[$this->cp]["content"]; - $this->cp=&$this->c["contentpos"]; - } - } - -/******************************************************************************************** - * Add new text - ********************************************************************************************/ - function AddNewText($text,$script=0,$comment=0) { - if (!strlen($text)) return; - $this->cp++; - if (!$comment) - $this->c[$this->cp]["type"]="text"; - else - $this->c[$this->cp]["type"]="comment"; - if ($script) { - $inputarray=array("/_top/","/top.location.href/","/([ \n]+)?window\.name/","/parent.location/"); - $replarray=array("_echoserver_file_space","parent.frames('_echoserver_file_space').src","//window.name","parent.frames('_echoserver_file_space').src"); -/* - $text=str_replace("_top","_echoserver_file_space",$text); - $text=str_replace("top.location.href","parent.frames('_echoserver_file_space').src",$text); - $text=preg_replace("/([ \n]+)?window\.name/","//window.name",$text); -*/ - $text=preg_replace($inputarray,$replarray,$text); - - } - $this->c[$this->cp]["data"]=$text; - $this->text=""; - } - -/******************************************************************************************** - * Find first tag in stack - ********************************************************************************************/ - function FindTag($tagname) { - for($i=$this->stacktagpos;$i>=0;$i--) - if ($this->stacktag[$i]["tag"][$this->stacktag[$i]["tag"]["contentpos"]]["data"]["name"]==$tagname) - return $i; - return -1; - } -} - -} //_ECHOSERVER_HTML_PARSER -?> diff --git a/htmlparser/htmlgrammar.cmp b/htmlparser/htmlgrammar.cmp deleted file mode 100644 index f4fcf0e..0000000 --- a/htmlparser/htmlgrammar.cmp +++ /dev/null @@ -1 +0,0 @@ -a:57:{s:3:"pre";a:4:{s:3:"tag";s:3:"pre";s:6:"endtag";s:7:"present";s:10:"nohavetags";s:0:"";s:4:"pars";a:0:{}}s:2:"hr";a:3:{s:3:"tag";s:2:"hr";s:6:"endtag";s:6:"absent";s:4:"pars";a:3:{s:5:"color";a:1:{s:3:"par";s:5:"color";}s:5:"width";a:1:{s:3:"par";s:5:"width";}s:7:"noshade";a:3:{s:3:"par";s:7:"noshade";s:6:"single";s:0:"";s:10:"editmethod";s:40:"_Translator_edit_standart_single_control";}}}s:8:"noframes";a:3:{s:3:"tag";s:8:"noframes";s:6:"endtag";s:7:"present";s:4:"pars";a:0:{}}s:8:"frameset";a:4:{s:3:"tag";s:8:"frameset";s:6:"endtag";s:9:"canabsent";s:14:"pictureforedit";s:12:"tags/tag.gif";s:4:"pars";a:8:{s:4:"rows";a:1:{s:3:"par";s:4:"rows";}s:4:"cols";a:1:{s:3:"par";s:4:"cols";}s:11:"frameborder";a:1:{s:3:"par";s:11:"frameborder";}s:6:"border";a:1:{s:3:"par";s:6:"border";}s:12:"framespacing";a:1:{s:3:"par";s:12:"framespacing";}s:12:"marginheight";a:1:{s:3:"par";s:12:"marginheight";}s:11:"marginwidth";a:1:{s:3:"par";s:11:"marginwidth";}s:8:"noresize";a:1:{s:3:"par";s:8:"noresize";}}}s:5:"frame";a:4:{s:3:"tag";s:5:"frame";s:6:"endtag";s:6:"absent";s:14:"pictureforedit";s:19:"tags/tag_frame2.gif";s:4:"pars";a:10:{s:4:"name";a:1:{s:3:"par";s:4:"name";}s:6:"target";a:1:{s:3:"par";s:6:"target";}s:9:"scrolling";a:1:{s:3:"par";s:9:"scrolling";}s:6:"border";a:1:{s:3:"par";s:6:"border";}s:11:"frameborder";a:1:{s:3:"par";s:11:"frameborder";}s:12:"framespacing";a:1:{s:3:"par";s:12:"framespacing";}s:12:"marginheight";a:1:{s:3:"par";s:12:"marginheight";}s:11:"marginwidth";a:1:{s:3:"par";s:11:"marginwidth";}s:8:"noresize";a:1:{s:3:"par";s:8:"noresize";}s:3:"src";a:3:{s:3:"par";s:3:"src";s:5:"width";s:2:"40";s:6:"height";s:1:"3";}}}s:6:"iframe";a:3:{s:3:"tag";s:6:"iframe";s:14:"pictureforedit";s:20:"tags/tag_iframe2.gif";s:4:"pars";a:0:{}}s:4:"html";a:6:{s:3:"tag";s:4:"html";s:6:"endtag";s:7:"present";s:13:"edittagsafter";s:4:"body";s:7:"comment";s:4:"Html";s:14:"pictureforedit";s:12:"tags/tag.gif";s:4:"pars";a:0:{}}s:4:"meta";a:4:{s:3:"tag";s:4:"meta";s:6:"endtag";s:6:"absent";s:13:"edittagsafter";s:4:"body";s:4:"pars";a:0:{}}s:6:"script";a:6:{s:3:"tag";s:6:"script";s:10:"nohavetags";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:19:"tags/tag_script.gif";s:14:"edittagsbefore";s:6:"script";s:4:"pars";a:2:{s:8:"language";a:1:{s:3:"par";s:8:"language";}s:3:"src";a:3:{s:3:"par";s:3:"src";s:5:"width";s:2:"40";s:6:"height";s:1:"3";}}}s:4:"nobr";a:4:{s:3:"tag";s:4:"nobr";s:7:"closeon";a:2:{s:5:"notin";a:14:{i:0;s:6:"strong";i:1;s:3:"sup";i:2;s:6:"center";i:3;s:4:"font";i:4;s:1:"i";i:5;s:1:"b";i:6;s:1:"u";i:7;s:2:"tt";i:8;s:1:"s";i:9;s:3:"big";i:10;s:5:"small";i:11;s:6:"strike";i:12;s:2:"br";i:13;s:6:"script";}s:2:"in";a:0:{}}s:6:"endtag";s:7:"present";s:4:"pars";a:0:{}}s:1:"p";a:5:{s:3:"tag";s:1:"p";s:7:"closeon";a:2:{s:5:"notin";a:23:{i:0;s:1:"a";i:1;s:3:"map";i:2;s:4:"area";i:3;s:6:"strong";i:4;s:3:"sup";i:5;s:4:"font";i:6;s:1:"i";i:7;s:1:"b";i:8;s:1:"u";i:9;s:2:"tt";i:10;s:3:"img";i:11;s:1:"s";i:12;s:3:"big";i:13;s:5:"small";i:14;s:6:"strike";i:15;s:4:"nobr";i:16;s:2:"br";i:17;s:6:"script";i:18;s:5:"input";i:19;s:6:"select";i:20;s:8:"textarea";i:21;s:6:"option";i:22;s:6:"button";}s:2:"in";a:0:{}}s:6:"endtag";s:9:"canabsent";s:14:"pictureforedit";s:15:"tags/tag_p2.gif";s:4:"pars";a:1:{s:5:"align";a:2:{s:3:"par";s:5:"align";s:10:"editmethod";s:24:"_Translator_edit_p_align";}}}s:2:"th";a:3:{s:3:"tag";s:2:"th";s:6:"endtag";s:6:"absent";s:4:"pars";a:1:{s:5:"align";a:2:{s:3:"par";s:5:"align";s:10:"editmethod";s:25:"_Translator_edit_th_align";}}}s:8:"noscript";a:3:{s:3:"tag";s:8:"noscript";s:6:"endtag";s:7:"present";s:4:"pars";a:0:{}}s:5:"style";a:4:{s:3:"tag";s:5:"style";s:10:"nohavetags";s:0:"";s:6:"endtag";s:7:"present";s:4:"pars";a:0:{}}s:4:"head";a:4:{s:3:"tag";s:4:"head";s:6:"endtag";s:7:"present";s:13:"edittagsafter";s:4:"body";s:4:"pars";a:0:{}}s:6:"center";a:5:{s:3:"tag";s:6:"center";s:6:"endtag";s:7:"present";s:7:"comment";s:6:"Center";s:14:"pictureforedit";s:12:"tags/tag.gif";s:4:"pars";a:0:{}}s:3:"img";a:5:{s:3:"tag";s:3:"img";s:6:"endtag";s:6:"absent";s:7:"comment";s:7:"Picture";s:14:"pictureforedit";s:16:"tags/tag_img.gif";s:4:"pars";a:16:{s:3:"src";a:3:{s:3:"par";s:3:"src";s:5:"width";s:2:"40";s:6:"height";s:1:"3";}s:3:"alt";a:1:{s:3:"par";s:3:"alt";}s:5:"width";a:1:{s:3:"par";s:5:"width";}s:6:"height";a:1:{s:3:"par";s:6:"height";}s:6:"border";a:1:{s:3:"par";s:6:"border";}s:4:"name";a:1:{s:3:"par";s:4:"name";}s:5:"align";a:2:{s:3:"par";s:5:"align";s:10:"editmethod";s:26:"_Translator_edit_img_align";}s:6:"vspace";a:1:{s:3:"par";s:6:"vspace";}s:6:"hspace";a:1:{s:3:"par";s:6:"hspace";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:6:"usemap";a:2:{s:3:"par";s:6:"usemap";s:10:"editmethod";s:32:"_Translator_edit_standart_usemap";}}}s:1:"a";a:8:{s:3:"tag";s:1:"a";s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"edittagsbefore";s:1:"a";s:7:"comment";s:3:"Url";s:14:"pictureforedit";s:14:"tags/tag_a.gif";s:7:"closeon";a:2:{s:5:"notin";a:19:{i:0;s:6:"strong";i:1;s:3:"sup";i:2;s:6:"center";i:3;s:4:"font";i:4;s:1:"i";i:5;s:1:"b";i:6;s:1:"u";i:7;s:2:"tt";i:8;s:3:"img";i:9;s:1:"s";i:10;s:3:"big";i:11;s:5:"small";i:12;s:6:"strike";i:13;s:4:"nobr";i:14;s:2:"br";i:15;s:6:"script";i:16;s:2:"li";i:17;s:2:"ol";i:18;s:2:"ul";}s:2:"in";a:1:{i:0;s:1:"a";}}s:4:"pars";a:24:{s:4:"href";a:3:{s:3:"par";s:4:"href";s:5:"width";s:2:"40";s:6:"height";s:1:"3";}s:4:"name";a:1:{s:3:"par";s:4:"name";}s:8:"hreflang";a:1:{s:3:"par";s:8:"hreflang";}s:3:"rel";a:1:{s:3:"par";s:3:"rel";}s:3:"rev";a:1:{s:3:"par";s:3:"rev";}s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:9:"accesskey";a:1:{s:3:"par";s:9:"accesskey";}s:5:"shape";a:1:{s:3:"par";s:5:"shape";}s:7:"onfocus";a:1:{s:3:"par";s:7:"onfocus";}s:6:"onblur";a:1:{s:3:"par";s:6:"onblur";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}s:8:"tabindex";a:1:{s:3:"par";s:8:"tabindex";}s:6:"target";a:1:{s:3:"par";s:6:"target";}}}s:2:"ul";a:6:{s:3:"tag";s:2:"ul";s:6:"endtag";s:7:"present";s:7:"comment";s:15:"Unsequence list";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"closeon";a:2:{s:5:"notin";a:17:{i:0;s:6:"strong";i:1;s:3:"sup";i:2;s:4:"font";i:3;s:1:"i";i:4;s:1:"b";i:5;s:1:"u";i:6;s:2:"tt";i:7;s:1:"a";i:8;s:1:"s";i:9;s:3:"big";i:10;s:5:"small";i:11;s:6:"strike";i:12;s:2:"br";i:13;s:4:"nobr";i:14;s:6:"script";i:15;s:2:"li";i:16;s:2:"ol";}s:2:"in";a:3:{i:0;s:5:"table";i:1;s:2:"tr";i:2;s:2:"td";}}s:4:"pars";a:17:{s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:3:"dir";a:1:{s:3:"par";s:3:"dir";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:12:"onkeypressed";a:1:{s:3:"par";s:12:"onkeypressed";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}s:4:"type";a:1:{s:3:"par";s:4:"type";}}}s:2:"li";a:7:{s:3:"tag";s:2:"li";s:6:"endtag";s:7:"present";s:7:"comment";s:12:"List element";s:14:"pictureforedit";s:12:"tags/tag.gif";s:13:"nohavesametag";s:0:"";s:7:"closeon";a:2:{s:5:"notin";a:17:{i:0;s:6:"strong";i:1;s:3:"sup";i:2;s:4:"font";i:3;s:1:"i";i:4;s:1:"b";i:5;s:1:"u";i:6;s:2:"tt";i:7;s:1:"a";i:8;s:1:"s";i:9;s:3:"big";i:10;s:5:"small";i:11;s:6:"strike";i:12;s:2:"br";i:13;s:4:"nobr";i:14;s:6:"script";i:15;s:2:"ol";i:16;s:2:"ul";}s:2:"in";a:1:{i:0;s:2:"li";}}s:4:"pars";a:18:{s:5:"value";a:1:{s:3:"par";s:5:"value";}s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:3:"dir";a:1:{s:3:"par";s:3:"dir";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:12:"onkeypressed";a:1:{s:3:"par";s:12:"onkeypressed";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}s:4:"type";a:1:{s:3:"par";s:4:"type";}}}s:2:"ol";a:7:{s:3:"tag";s:2:"ol";s:6:"endtag";s:7:"present";s:7:"comment";s:13:"Sequence list";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"closeon";a:2:{s:5:"notin";a:17:{i:0;s:6:"strong";i:1;s:3:"sup";i:2;s:4:"font";i:3;s:1:"i";i:4;s:1:"b";i:5;s:1:"u";i:6;s:2:"tt";i:7;s:1:"a";i:8;s:1:"s";i:9;s:3:"big";i:10;s:5:"small";i:11;s:6:"strike";i:12;s:2:"br";i:13;s:4:"nobr";i:14;s:6:"script";i:15;s:2:"ol";i:16;s:2:"ul";}s:2:"in";a:1:{i:0;s:2:"ol";}}s:13:"nohavesametag";s:0:"";s:4:"pars";a:18:{s:5:"start";a:1:{s:3:"par";s:5:"start";}s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:3:"dir";a:1:{s:3:"par";s:3:"dir";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:12:"onkeypressed";a:1:{s:3:"par";s:12:"onkeypressed";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}s:4:"type";a:1:{s:3:"par";s:4:"type";}}}s:5:"title";a:5:{s:3:"tag";s:5:"title";s:6:"endtag";s:7:"present";s:7:"comment";s:11:"Page header";s:14:"pictureforedit";s:12:"tags/tag.gif";s:4:"pars";a:0:{}}s:8:"textarea";a:6:{s:3:"tag";s:8:"textarea";s:10:"nohavetags";s:0:"";s:6:"endtag";s:7:"present";s:7:"comment";s:8:"Textarea";s:14:"pictureforedit";s:21:"tags/tag_textarea.gif";s:4:"pars";a:26:{s:8:"disabled";a:3:{s:3:"par";s:8:"disabled";s:6:"single";s:0:"";s:10:"editmethod";s:40:"_Translator_edit_standart_single_control";}s:4:"name";a:1:{s:3:"par";s:4:"name";}s:4:"rows";a:1:{s:3:"par";s:4:"rows";}s:4:"cols";a:1:{s:3:"par";s:4:"cols";}s:9:"accesskey";a:1:{s:3:"par";s:9:"accesskey";}s:4:"wrap";a:1:{s:3:"par";s:4:"wrap";}s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:3:"dir";a:1:{s:3:"par";s:3:"dir";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:7:"onfocus";a:1:{s:3:"par";s:7:"onfocus";}s:6:"onblur";a:1:{s:3:"par";s:6:"onblur";}s:8:"onselect";a:1:{s:3:"par";s:8:"onselect";}s:8:"onchange";a:1:{s:3:"par";s:8:"onchange";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:12:"onkeypressed";a:1:{s:3:"par";s:12:"onkeypressed";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:2:"tr";a:6:{s:3:"tag";s:2:"tr";s:6:"endtag";s:7:"present";s:7:"comment";s:9:"Table row";s:14:"pictureforedit";s:15:"tags/tag_tr.gif";s:13:"edittagsafter";s:2:"td";s:4:"pars";a:20:{s:5:"align";a:2:{s:3:"par";s:5:"align";s:10:"editmethod";s:25:"_Translator_edit_td_align";}s:6:"valign";a:2:{s:3:"par";s:6:"valign";s:10:"editmethod";s:26:"_Translator_edit_td_valign";}s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:3:"dir";a:1:{s:3:"par";s:3:"dir";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:7:"bgcolor";a:1:{s:3:"par";s:7:"bgcolor";}s:10:"background";a:1:{s:3:"par";s:10:"background";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:12:"onkeypressed";a:1:{s:3:"par";s:12:"onkeypressed";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:2:"td";a:6:{s:3:"tag";s:2:"td";s:6:"endtag";s:7:"present";s:7:"comment";s:10:"Table cell";s:14:"pictureforedit";s:15:"tags/tag_td.gif";s:13:"edittagsafter";s:2:"td";s:4:"pars";a:25:{s:7:"colspan";a:1:{s:3:"par";s:7:"colspan";}s:7:"rowspan";a:1:{s:3:"par";s:7:"rowspan";}s:5:"width";a:1:{s:3:"par";s:5:"width";}s:6:"height";a:1:{s:3:"par";s:6:"height";}s:7:"bgcolor";a:1:{s:3:"par";s:7:"bgcolor";}s:10:"background";a:1:{s:3:"par";s:10:"background";}s:5:"align";a:2:{s:3:"par";s:5:"align";s:10:"editmethod";s:25:"_Translator_edit_td_align";}s:6:"valign";a:2:{s:3:"par";s:6:"valign";s:10:"editmethod";s:26:"_Translator_edit_td_valign";}s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:3:"dir";a:1:{s:3:"par";s:3:"dir";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:12:"onkeypressed";a:1:{s:3:"par";s:12:"onkeypressed";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}s:6:"nowrap";a:3:{s:3:"par";s:6:"nowrap";s:6:"single";s:0:"";s:10:"editmethod";s:40:"_Translator_edit_standart_single_control";}}}s:5:"table";a:5:{s:3:"tag";s:5:"table";s:6:"endtag";s:7:"present";s:7:"comment";s:5:"Table";s:14:"pictureforedit";s:18:"tags/tag_table.gif";s:4:"pars";a:26:{s:5:"width";a:1:{s:3:"par";s:5:"width";}s:6:"height";a:1:{s:3:"par";s:6:"height";}s:7:"bgcolor";a:1:{s:3:"par";s:7:"bgcolor";}s:10:"background";a:1:{s:3:"par";s:10:"background";}s:11:"cellspacing";a:1:{s:3:"par";s:11:"cellspacing";}s:11:"cellpadding";a:1:{s:3:"par";s:11:"cellpadding";}s:6:"border";a:1:{s:3:"par";s:6:"border";}s:11:"bordercolor";a:1:{s:3:"par";s:11:"bordercolor";}s:7:"summary";a:1:{s:3:"par";s:7:"summary";}s:5:"align";a:2:{s:3:"par";s:5:"align";s:10:"editmethod";s:28:"_Translator_edit_table_align";}s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:3:"dir";a:1:{s:3:"par";s:3:"dir";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:12:"onkeypressed";a:1:{s:3:"par";s:12:"onkeypressed";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:4:"area";a:5:{s:3:"tag";s:4:"area";s:6:"endtag";s:6:"absent";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"comment";s:10:"Map region";s:4:"pars";a:22:{s:4:"href";a:3:{s:3:"par";s:4:"href";s:5:"width";s:2:"40";s:6:"height";s:1:"3";}s:5:"shape";a:2:{s:3:"par";s:5:"shape";s:10:"editmethod";s:31:"_Translator_edit_standart_shape";}s:6:"coords";a:1:{s:3:"par";s:6:"coords";}s:6:"usemap";a:1:{s:3:"par";s:6:"usemap";}s:3:"alt";a:1:{s:3:"par";s:3:"alt";}s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:9:"accesskey";a:1:{s:3:"par";s:9:"accesskey";}s:7:"onfocus";a:1:{s:3:"par";s:7:"onfocus";}s:6:"onblur";a:1:{s:3:"par";s:6:"onblur";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}s:8:"tabindex";a:1:{s:3:"par";s:8:"tabindex";}}}s:4:"span";a:5:{s:3:"tag";s:4:"span";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"comment";s:4:"span";s:4:"pars";a:13:{s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:1:"b";a:7:{s:3:"tag";s:1:"b";s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:15:"tags/tag_b2.gif";s:7:"comment";s:1:"B";s:7:"closeon";a:1:{s:2:"in";a:1:{i:0;s:1:"b";}}s:4:"pars";a:13:{s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:2:"tt";a:7:{s:3:"tag";s:2:"tt";s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"comment";s:2:"TT";s:7:"closeon";a:1:{s:2:"in";a:1:{i:0;s:2:"tt";}}s:4:"pars";a:13:{s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:1:"i";a:7:{s:3:"tag";s:1:"i";s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:15:"tags/tag_i2.gif";s:7:"comment";s:1:"I";s:7:"closeon";a:1:{s:2:"in";a:1:{i:0;s:1:"i";}}s:4:"pars";a:13:{s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:3:"big";a:7:{s:3:"tag";s:3:"big";s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"comment";s:3:"Big";s:7:"closeon";a:1:{s:2:"in";a:1:{i:0;s:3:"big";}}s:4:"pars";a:13:{s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:5:"small";a:7:{s:3:"tag";s:5:"small";s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"comment";s:5:"Small";s:7:"closeon";a:1:{s:2:"in";a:1:{i:0;s:5:"small";}}s:4:"pars";a:13:{s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:6:"strike";a:7:{s:3:"tag";s:6:"strike";s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"comment";s:6:"Strike";s:7:"closeon";a:1:{s:2:"in";a:1:{i:0;s:6:"strike";}}s:4:"pars";a:13:{s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:1:"s";a:7:{s:3:"tag";s:1:"s";s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"comment";s:1:"S";s:7:"closeon";a:1:{s:2:"in";a:1:{i:0;s:1:"s";}}s:4:"pars";a:13:{s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:1:"u";a:7:{s:3:"tag";s:1:"u";s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:15:"tags/tag_u2.gif";s:7:"comment";s:1:"U";s:7:"closeon";a:1:{s:2:"in";a:1:{i:0;s:1:"u";}}s:4:"pars";a:13:{s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:3:"map";a:7:{s:3:"tag";s:3:"map";s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"comment";s:3:"Map";s:7:"closeon";a:2:{s:5:"notin";a:1:{i:0;s:4:"area";}s:2:"in";a:1:{i:0;s:3:"map";}}s:4:"pars";a:1:{s:4:"name";a:1:{s:3:"par";s:4:"name";}}}s:2:"br";a:4:{s:3:"tag";s:2:"br";s:4:"edit";s:1:"0";s:6:"endtag";s:6:"absent";s:4:"pars";a:5:{s:5:"clear";a:1:{s:3:"par";s:5:"clear";}s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:5:"style";a:1:{s:3:"par";s:5:"style";}}}s:4:"base";a:5:{s:3:"tag";s:4:"base";s:6:"endtag";s:6:"absent";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"comment";s:4:"Base";s:4:"pars";a:2:{s:4:"href";a:3:{s:3:"par";s:4:"href";s:5:"width";s:2:"40";s:6:"height";s:1:"3";}s:6:"target";a:1:{s:3:"par";s:6:"target";}}}s:8:"basefont";a:5:{s:3:"tag";s:8:"basefont";s:6:"endtag";s:6:"absent";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"comment";s:8:"Basefont";s:4:"pars";a:7:{s:2:"id";a:1:{s:3:"par";s:2:"id";}s:4:"size";a:1:{s:3:"par";s:4:"size";}s:5:"color";a:1:{s:3:"par";s:5:"color";}s:4:"face";a:1:{s:3:"par";s:4:"face";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"style";a:1:{s:3:"par";s:5:"style";}}}s:4:"body";a:8:{s:3:"tag";s:4:"body";s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:17:"tags/tag_body.gif";s:13:"edittagsafter";s:4:"body";s:7:"comment";s:9:"Page body";s:7:"closeon";a:1:{s:2:"in";a:1:{i:0;s:4:"body";}}s:4:"pars";a:27:{s:10:"background";a:1:{s:3:"par";s:10:"background";}s:4:"text";a:1:{s:3:"par";s:4:"text";}s:4:"link";a:1:{s:3:"par";s:4:"link";}s:5:"vlink";a:1:{s:3:"par";s:5:"vlink";}s:5:"alink";a:1:{s:3:"par";s:5:"alink";}s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:7:"bgcolor";a:1:{s:3:"par";s:7:"bgcolor";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:9:"topmargin";a:1:{s:3:"par";s:9:"topmargin";}s:10:"leftmargin";a:1:{s:3:"par";s:10:"leftmargin";}s:11:"marginwidth";a:1:{s:3:"par";s:11:"marginwidth";}s:12:"marginheight";a:1:{s:3:"par";s:12:"marginheight";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}s:6:"onload";a:1:{s:3:"par";s:6:"onload";}s:8:"onunload";a:1:{s:3:"par";s:8:"onunload";}}}s:6:"button";a:7:{s:3:"tag";s:6:"button";s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:19:"tags/tag_button.gif";s:7:"comment";s:6:"Button";s:7:"closeon";a:2:{s:5:"notin";a:15:{i:0;s:6:"center";i:1;s:4:"font";i:2;s:1:"i";i:3;s:1:"b";i:4;s:1:"u";i:5;s:2:"tt";i:6;s:1:"a";i:7;s:3:"img";i:8;s:1:"s";i:9;s:3:"big";i:10;s:5:"small";i:11;s:6:"strike";i:12;s:4:"nobr";i:13;s:2:"br";i:14;s:6:"script";}s:2:"in";a:1:{i:0;s:6:"button";}}s:4:"pars";a:18:{s:4:"name";a:1:{s:3:"par";s:4:"name";}s:5:"value";a:1:{s:3:"par";s:5:"value";}s:4:"type";a:1:{s:3:"par";s:4:"type";}s:8:"disabled";a:3:{s:3:"par";s:8:"disabled";s:6:"single";s:0:"";s:10:"editmethod";s:40:"_Translator_edit_standart_single_control";}s:9:"accesskey";a:1:{s:3:"par";s:9:"accesskey";}s:6:"usemap";a:1:{s:3:"par";s:6:"usemap";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}s:7:"onfocus";a:1:{s:3:"par";s:7:"onfocus";}s:6:"onblur";a:1:{s:3:"par";s:6:"onblur";}}}s:3:"div";a:5:{s:3:"tag";s:3:"div";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:16:"tags/tag_div.gif";s:7:"comment";s:8:"Division";s:4:"pars";a:15:{s:2:"id";a:1:{s:3:"par";s:2:"id";}s:4:"name";a:1:{s:3:"par";s:4:"name";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:5:"align";a:2:{s:3:"par";s:5:"align";s:10:"editmethod";s:26:"_Translator_edit_div_align";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:4:"font";a:6:{s:3:"tag";s:4:"font";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:18:"tags/tag_font2.gif";s:7:"comment";s:4:"Font";s:7:"closeon";a:2:{s:5:"notin";a:13:{i:0;s:1:"i";i:1;s:1:"b";i:2;s:1:"u";i:3;s:2:"tt";i:4;s:1:"a";i:5;s:3:"img";i:6;s:1:"s";i:7;s:3:"big";i:8;s:5:"small";i:9;s:6:"strike";i:10;s:4:"nobr";i:11;s:2:"br";i:12;s:6:"script";}s:2:"in";a:0:{}}s:4:"pars";a:9:{s:2:"id";a:1:{s:3:"par";s:2:"id";}s:4:"size";a:1:{s:3:"par";s:4:"size";}s:5:"color";a:1:{s:3:"par";s:5:"color";}s:4:"face";a:1:{s:3:"par";s:4:"face";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:3:"dir";a:1:{s:3:"par";s:3:"dir";}}}s:4:"form";a:5:{s:3:"tag";s:4:"form";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:17:"tags/tag_form.gif";s:7:"comment";s:4:"Form";s:4:"pars";a:23:{s:6:"action";a:3:{s:3:"par";s:6:"action";s:5:"width";s:2:"40";s:6:"height";s:1:"3";}s:6:"method";a:1:{s:3:"par";s:6:"method";}s:7:"enctype";a:1:{s:3:"par";s:7:"enctype";}s:14:"accept-charset";a:1:{s:3:"par";s:14:"accept-charset";}s:6:"accept";a:1:{s:3:"par";s:6:"accept";}s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:6:"target";a:1:{s:3:"par";s:6:"target";}s:8:"onsubmit";a:1:{s:3:"par";s:8:"onsubmit";}s:7:"onreset";a:1:{s:3:"par";s:7:"onreset";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:2:"h1";a:7:{s:3:"tag";s:2:"h1";s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"comment";s:2:"hl";s:7:"closeon";a:2:{s:5:"notin";a:15:{i:0;s:6:"strong";i:1;s:3:"sup";i:2;s:4:"font";i:3;s:1:"i";i:4;s:1:"b";i:5;s:1:"u";i:6;s:2:"tt";i:7;s:1:"a";i:8;s:1:"s";i:9;s:3:"big";i:10;s:5:"small";i:11;s:6:"strike";i:12;s:4:"nobr";i:13;s:2:"br";i:14;s:6:"script";}s:2:"in";a:1:{i:0;s:2:"h1";}}s:4:"pars";a:17:{s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:3:"dir";a:1:{s:3:"par";s:3:"dir";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"align";a:1:{s:3:"par";s:5:"align";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:2:"h2";a:7:{s:3:"tag";s:2:"h2";s:7:"closeon";a:2:{s:5:"notin";a:15:{i:0;s:6:"strong";i:1;s:3:"sup";i:2;s:4:"font";i:3;s:1:"i";i:4;s:1:"b";i:5;s:1:"u";i:6;s:2:"tt";i:7;s:1:"a";i:8;s:1:"s";i:9;s:3:"big";i:10;s:5:"small";i:11;s:6:"strike";i:12;s:4:"nobr";i:13;s:2:"br";i:14;s:6:"script";}s:2:"in";a:1:{i:0;s:2:"h2";}}s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"comment";s:2:"h2";s:4:"pars";a:17:{s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:3:"dir";a:1:{s:3:"par";s:3:"dir";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"align";a:1:{s:3:"par";s:5:"align";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:2:"h3";a:7:{s:3:"tag";s:2:"h3";s:7:"closeon";a:2:{s:5:"notin";a:15:{i:0;s:6:"strong";i:1;s:3:"sup";i:2;s:4:"font";i:3;s:1:"i";i:4;s:1:"b";i:5;s:1:"u";i:6;s:2:"tt";i:7;s:1:"a";i:8;s:1:"s";i:9;s:3:"big";i:10;s:5:"small";i:11;s:6:"strike";i:12;s:4:"nobr";i:13;s:2:"br";i:14;s:6:"script";}s:2:"in";a:1:{i:0;s:2:"h3";}}s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"comment";s:2:"h3";s:4:"pars";a:17:{s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:3:"dir";a:1:{s:3:"par";s:3:"dir";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"align";a:1:{s:3:"par";s:5:"align";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:2:"h4";a:7:{s:3:"tag";s:2:"h4";s:7:"closeon";a:2:{s:5:"notin";a:15:{i:0;s:6:"strong";i:1;s:3:"sup";i:2;s:4:"font";i:3;s:1:"i";i:4;s:1:"b";i:5;s:1:"u";i:6;s:2:"tt";i:7;s:1:"a";i:8;s:1:"s";i:9;s:3:"big";i:10;s:5:"small";i:11;s:6:"strike";i:12;s:4:"nobr";i:13;s:2:"br";i:14;s:6:"script";}s:2:"in";a:1:{i:0;s:2:"h4";}}s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"comment";s:2:"h4";s:4:"pars";a:17:{s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:3:"dir";a:1:{s:3:"par";s:3:"dir";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"align";a:1:{s:3:"par";s:5:"align";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:2:"h5";a:7:{s:3:"tag";s:2:"h5";s:7:"closeon";a:2:{s:5:"notin";a:15:{i:0;s:6:"strong";i:1;s:3:"sup";i:2;s:4:"font";i:3;s:1:"i";i:4;s:1:"b";i:5;s:1:"u";i:6;s:2:"tt";i:7;s:1:"a";i:8;s:1:"s";i:9;s:3:"big";i:10;s:5:"small";i:11;s:6:"strike";i:12;s:4:"nobr";i:13;s:2:"br";i:14;s:6:"script";}s:2:"in";a:1:{i:0;s:2:"h5";}}s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"comment";s:2:"h5";s:4:"pars";a:17:{s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:3:"dir";a:1:{s:3:"par";s:3:"dir";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"align";a:1:{s:3:"par";s:5:"align";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:2:"h6";a:7:{s:3:"tag";s:2:"h6";s:7:"closeon";a:2:{s:5:"notin";a:15:{i:0;s:6:"strong";i:1;s:3:"sup";i:2;s:4:"font";i:3;s:1:"i";i:4;s:1:"b";i:5;s:1:"u";i:6;s:2:"tt";i:7;s:1:"a";i:8;s:1:"s";i:9;s:3:"big";i:10;s:5:"small";i:11;s:6:"strike";i:12;s:4:"nobr";i:13;s:2:"br";i:14;s:6:"script";}s:2:"in";a:1:{i:0;s:2:"h6";}}s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"comment";s:2:"h6";s:4:"pars";a:17:{s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:3:"dir";a:1:{s:3:"par";s:3:"dir";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"align";a:1:{s:3:"par";s:5:"align";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:5:"input";a:5:{s:3:"tag";s:5:"input";s:6:"endtag";s:6:"absent";s:14:"pictureforedit";s:18:"tags/tag_input.gif";s:7:"comment";s:18:"Form input element";s:4:"pars";a:38:{s:4:"type";a:2:{s:3:"par";s:4:"type";s:10:"editmethod";s:27:"_Translator_edit_input_type";}s:4:"name";a:1:{s:3:"par";s:4:"name";}s:5:"value";a:1:{s:3:"par";s:5:"value";}s:7:"checked";a:3:{s:3:"par";s:7:"checked";s:6:"single";s:0:"";s:10:"editmethod";s:40:"_Translator_edit_standart_single_control";}s:8:"disabled";a:3:{s:3:"par";s:8:"disabled";s:6:"single";s:0:"";s:10:"editmethod";s:40:"_Translator_edit_standart_single_control";}s:8:"readonly";a:3:{s:3:"par";s:8:"readonly";s:6:"single";s:0:"";s:10:"editmethod";s:40:"_Translator_edit_standart_single_control";}s:4:"size";a:1:{s:3:"par";s:4:"size";}s:9:"maxlength";a:1:{s:3:"par";s:9:"maxlength";}s:3:"src";a:3:{s:3:"par";s:3:"src";s:5:"width";s:2:"40";s:6:"height";s:1:"3";}s:5:"width";a:1:{s:3:"par";s:5:"width";}s:6:"height";a:1:{s:3:"par";s:6:"height";}s:3:"alt";a:1:{s:3:"par";s:3:"alt";}s:6:"border";a:1:{s:3:"par";s:6:"border";}s:8:"tabindex";a:1:{s:3:"par";s:8:"tabindex";}s:9:"accesskey";a:1:{s:3:"par";s:9:"accesskey";}s:7:"onfocus";a:1:{s:3:"par";s:7:"onfocus";}s:6:"onblur";a:1:{s:3:"par";s:6:"onblur";}s:8:"onselect";a:1:{s:3:"par";s:8:"onselect";}s:8:"onchange";a:1:{s:3:"par";s:8:"onchange";}s:6:"accept";a:1:{s:3:"par";s:6:"accept";}s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:3:"dir";a:1:{s:3:"par";s:3:"dir";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:5:"align";a:1:{s:3:"par";s:5:"align";}s:5:"shape";a:1:{s:3:"par";s:5:"shape";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:5:"label";a:7:{s:3:"tag";s:5:"label";s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"comment";s:5:"Label";s:7:"closeon";a:1:{s:2:"in";a:1:{i:0;s:5:"label";}}s:4:"pars";a:20:{s:3:"for";a:1:{s:3:"par";s:3:"for";}s:9:"accesskey";a:1:{s:3:"par";s:9:"accesskey";}s:7:"onfocus";a:1:{s:3:"par";s:7:"onfocus";}s:6:"onblur";a:1:{s:3:"par";s:6:"onblur";}s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:3:"dir";a:1:{s:3:"par";s:3:"dir";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:6:"select";a:8:{s:3:"tag";s:6:"select";s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"edittagsbefore";s:6:"select";s:14:"pictureforedit";s:19:"tags/tag_select.gif";s:7:"comment";s:6:"Select";s:7:"closeon";a:2:{s:5:"notin";a:1:{i:0;s:6:"option";}s:2:"in";a:1:{i:0;s:6:"select";}}s:4:"pars";a:24:{s:8:"disabled";a:3:{s:3:"par";s:8:"disabled";s:6:"single";s:0:"";s:10:"editmethod";s:40:"_Translator_edit_standart_single_control";}s:8:"multiple";a:3:{s:3:"par";s:8:"multiple";s:6:"single";s:0:"";s:10:"editmethod";s:40:"_Translator_edit_standart_single_control";}s:4:"name";a:1:{s:3:"par";s:4:"name";}s:4:"size";a:1:{s:3:"par";s:4:"size";}s:5:"width";a:1:{s:3:"par";s:5:"width";}s:9:"accesskey";a:1:{s:3:"par";s:9:"accesskey";}s:7:"onfocus";a:1:{s:3:"par";s:7:"onfocus";}s:6:"onblur";a:1:{s:3:"par";s:6:"onblur";}s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:3:"dir";a:1:{s:3:"par";s:3:"dir";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:6:"option";a:7:{s:3:"tag";s:6:"option";s:7:"closeon";a:2:{s:5:"notin";a:15:{i:0;s:6:"strong";i:1;s:3:"sup";i:2;s:4:"font";i:3;s:1:"i";i:4;s:1:"b";i:5;s:1:"u";i:6;s:2:"tt";i:7;s:1:"a";i:8;s:1:"s";i:9;s:3:"big";i:10;s:5:"small";i:11;s:6:"strike";i:12;s:2:"br";i:13;s:4:"nobr";i:14;s:6:"script";}s:2:"in";a:2:{i:0;s:6:"option";i:1;s:6:"option";}}s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"comment";s:6:"Option";s:4:"pars";a:20:{s:4:"name";a:1:{s:3:"par";s:4:"name";}s:5:"value";a:1:{s:3:"par";s:5:"value";}s:9:"accesskey";a:1:{s:3:"par";s:9:"accesskey";}s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:3:"dir";a:1:{s:3:"par";s:3:"dir";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}s:8:"selected";a:3:{s:3:"par";s:8:"selected";s:6:"single";s:0:"";s:10:"editmethod";s:40:"_Translator_edit_standart_single_control";}}}s:4:"link";a:4:{s:3:"tag";s:4:"link";s:6:"endtag";s:6:"absent";s:14:"pictureforedit";s:17:"tags/tag_link.gif";s:4:"pars";a:13:{s:3:"rel";a:1:{s:3:"par";s:3:"rel";}s:3:"rev";a:1:{s:3:"par";s:3:"rev";}s:4:"href";a:1:{s:3:"par";s:4:"href";}s:6:"target";a:1:{s:3:"par";s:6:"target";}s:5:"media";a:1:{s:3:"par";s:5:"media";}s:8:"hreflang";a:1:{s:3:"par";s:8:"hreflang";}s:7:"charset";a:1:{s:3:"par";s:7:"charset";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:3:"dir";a:1:{s:3:"par";s:3:"dir";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}}}s:21:"EDIT_TAGS_AFTER_TABLE";a:2:{i:0;s:4:"body";i:1;s:2:"td";}} \ No newline at end of file diff --git a/htmlparser/htmlgrammar.dat b/htmlparser/htmlgrammar.dat deleted file mode 100644 index 8329ad8..0000000 --- a/htmlparser/htmlgrammar.dat +++ /dev/null @@ -1,1176 +0,0 @@ -/* -Îïèñàíèå ïàðàìåòðîâ ãðàììàòèêè -The grammar parameters description. - -endtag= "present | canabsent | absent" - present - çàêðûòèå òåãà îáÿçàòåëüíî äîëæíî ïðèñóòñòâîâàòü - - close tag must present - - canabsent - çàêðûòèå òåãà ìîæåò îòñóòñòâîâàòü, íî ìîæåò è ïðèñóòñòâîâàòü - - close tag can absent - - absent - çàêðûòèå òåãà âñåãäà îòñóòñòâóåò - - close tag always absent - -nohavesametag - òýã íå ìîæåò ñîäåðæàòü â ñåáå ñåáÿ ñàìîãî, åñëè âíóòðè òýãà âñòðå÷àåòñÿ - îí ñàì, òî òåã çàêðûâàåòñÿ - - tag can't contains itself inside. If it has itself inside then it must - be closed before itself - -nohavetags - òåã ñîäåðæèò òîëüêî òåêñò. Äàæå åñëè âíóòðè íåãî âñòðå÷àþòñÿ òýãè, òî - îíè òðàêòóþòñÿ êàê òåêñò. - - tag can has text only. If it has tags inside then these tags will be to interpret - as text. - -closeon = - "tagname|...|!tagname|..." - ïðèíóäèòåëüíîå çàêðûòèå òýãà ïðè âñòðå÷å îòêðûòèÿ - äðóãîãî òåãà. ìîæíî äåëàòü ïåðå÷èñëåíèÿ ìíîæåñòâà òåãîâ èëè îòðèöàíèå ìíîæåñòâà - òåãîâ, íàïðèìåð closeon="a|b|i|!form" òî åñòü çàêðûâàòü òåã ïðè âñòðå÷å îòêðûòèÿ - òåãîâ A,B,I è åñëè ýòî ê òîìó æå íå òåã FORM - - "tagname|...|!tagname|..." - rules for closing tag. |tagname|... list says - that tag must to be closed before first tagname if it exist inside it. - |!tagname|!.... list says then tag must to be closed if it has not a tagname - tag inside. You must combine these lists into one closeon part. For example: - closeon="a|b|i|!form" or closeon="a" or closeon="!form". - -Âû òàêæå ìîæåòå äîáàâëÿòü ñâîè ïàðàìåòðû â ãðàììàòèêó åñëè Âàì íàäî èñïîëüçîâàòü èõ â äàëüíåéøåì. -Íàïðèìåð, Âàì íàäî óêàçàòü êàêîé ôóíêöèåé âû áóäåòå îáðàáàòûâàòü òåã óæå ïîñëå ïàðñèíãà -òîãäà ïèøèòå, íàïðèìåð, function="MyFunction". -À ïîñëå ïðîöåññà ïàðñèíãà êîäà, ïðîáåãàÿ ïî äåðåâó òýãîâ çàãëÿäûâàåòå â ãðàììàòèêó ýòîãî òåãà -è, íàïðèìåð, âûçûâàåòå äëÿ íåãî ôóíêöèþ ñ ýòèì èìåíåì. -Òî÷íî òàê æå ìîæíî äîáàâëÿòü ïàðàìåòðû â îïèñàíèå ïàðàìåòðîâ òåãîâ. - -You can add your own parameters into grammar if it needs. For example, you need walk -through parsed grammar tree and if you meet someone tag you need process it by someone function. -In this case you can add special parameter for this tag, for example: function="MyFunction" and -after HTML parse process you will be walk through HTML tree and see into -grammar when you meet tag, and if it has function field you will call function by name in this field for this tag. -Using the same method you can add own parameter fields on tag parameters into grammar. - -I used this grammar in my HTML tag visualizer and you can see my additional parameter -"editmethod" for this with function names which I called in my visualizer. -you can see my additional parameter "pictureforedit" too. It needs for the pictogram for each tag. -I used so "width" and "height" additional parameters for visualize fields of tag with certain -width and height. - -BUT THESE ADDITIONAL PARAMETERS NOT NEEDS FOR HTML PARSER! IT WILL NEED AFTER ONLY! FOR -MY SPECIAL NEEDS. - -Ïðåêîìïèëåíàÿ ãðàììàòèêà èìååò ñëåäóþùóþ ñòðóêòóðó: -structure of precompiled grammar is: - -array( - "tagname"=>array( - "internal parameter name"=>value, - ..... - "pars"=>array( - "parname"=>array( - "parparameter name"=>value, - ..... - ), - ..... - ) - ) - .... -) -íàïðèìåð, ó âàñ â îòïàðñåíîì äåðåâå åñòü òåã . Åãî ïîëíûé íàáîð ïàðàìåòðîâ -â ïðåêîìïèëåíîé ãðàììàòèêå $grammar ìîæíî ïîñìîòðåòü ñëåäóþùèì îáðàçîì - -For example, if you have tag A into parsed tree, then you can see the -grammar for A tag as - -PrintArray($grammar["a"]) - -à òîëüêî íàáîð ïàðàìåòðîâ òåãà, íàïðèìåð òàê - -and parameters only for tag A as - -PrintArray($grammar["a"]["pars"]) -*/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/htmlparser/htmlgrammarparser.inc b/htmlparser/htmlgrammarparser.inc deleted file mode 100644 index 259fcce..0000000 --- a/htmlparser/htmlgrammarparser.inc +++ /dev/null @@ -1,478 +0,0 @@ -firstprev=array("state"=>0,"word"=>""); - $this->secondprev=array("state"=>0,"word"=>""); - $this->line=0; - $this->pos=0; - $this->errors=array(); - $this->errpos=-1; - $this->incomment=-1; - $this->allreadyparsed=0; - $this->pg=array(); - $this->pgpos=-1; - $this->quotstate=-1; - $this->iseof=false; - $this->firststate=0; - $this->secondstate=0; - $this->mode=1; - - if(gettype($this->data)=="array") { - $this->pg=&$data; - $this->allreadyparsed=1; - return; - } - clearstatcache(); - $this->name=$data; - if (!file_exists($this->name)) { - $this->SetError(1,"File $this->name not exists.",0,0,"Error"); - return; - } - if (!$fp=fopen($this->name,"r")) { - $this->SetError(1,"Can't open file $this->name.",0,0,"Error"); - return; - } - flock($fp,1); - $this->data=fread($fp,filesize($this->name)); - flock($fp,3); - fclose($fp); - $this->length=strlen($this->data); - } - -/******************************************************************************************** - * Store parser's errors and warnings - ********************************************************************************************/ - function SetError($e,$str,$line=0,$column=0,$errtype="Warning") { - $this->errors[++$this->errpos]["type"]=$errtype; - $this->errors[$this->errpos]["code"]=$e; - $this->err=$e; - $this->errstr="$errtype: $e, $str"; - if ($line) { - if (strlen($this->name)) - $this->errstr.="object $this->name"; - $this->errstr.=" Line $line, Column $column"; - } - $this->errors[$this->errpos]["str"]=$this->errstr."
\r\n"; - } - -/******************************************************************************************** - * Print parser's errors and warnings - ********************************************************************************************/ - function PrintErrors() { - for ($i=0;$i<=$this->errpos;$i++) - print $this->errors[$i]["str"]; - } - -/******************************************************************************************** - * Get word from data - ********************************************************************************************/ - function GetWord($word) { - $word=""; - $found=0; - $iter=0; - if ($this->pos>$this->length) - return false; - while (!$found) { - if ($this->pos>$this->length) - return false; - if ($this->pos==$this->length) { - $this->pos++; - return $word; - } - switch($this->data[$this->pos]) { - case "*": - if ($this->quotstate==1) { - $word.=$this->data[$this->pos++]; - $this->column++; - break; - } - $this->column++; - $this->pos++; - if ($word[0]=="/") - $found=1; - $word.=$this->data[$this->pos-1]; - break; - case "/": - if ($this->quotstate==1) { - $word.=$this->data[$this->pos++]; - $this->column++; - break; - } - $this->column++; - $this->pos++; - if ($word[0]=="*") - $found=1; - $word.=$this->data[$this->pos-1]; - break; - case " ": - case "\r": - case "\t": - if ($this->quotstate==1) { - $word.=$this->data[$this->pos++]; - $this->column++; - break; - } - $this->column++; - $this->pos++; - if (strlen($word)) - $found=1; - break; - case "\n": - if ($this->quotstate==1) { - $word.=$this->data[$this->pos++]; - $this->column++; - break; - } - $this->column=0; - $this->line++; - $this->pos++; - if (strlen($word)) - $found=1; - break; - case ">": - case "<": - case "=": - if ($this->quotstate==1) { - $word.=$this->data[$this->pos++]; - $this->column++; - } else { - if (!strlen($word)) { - $word=$this->data[$this->pos++]; - $this->column++; - } - $found=1; - } - break; - case "\"": - if ($this->pos) { - if ($this->data[$this->pos-1]=="\\") { - $word.=$this->data[$this->pos++]; - $this->column++; - } else { - if (!strlen($word)) { - $this->quotstate*=-1; - $word=$this->data[$this->pos++]; - $this->column++; - } - $found=1; - } - } else { - $word=$this->data[$this->pos++]; - $this->column++; - $found=1; - } - break; - default: - $this->column++; - $word.=$this->data[$this->pos++]; - } - } - return true; - } - -/******************************************************************************************** - * Parse grammar first step - ******************************************************************************************** -Parse -< [] [] > - -in/state 0 1 2 3 -< 1 -1 -1 1 -[ -1 2 -1 -1 -] -1 -1 1 -1 -> -1 3 -1 -1 -word -1 1 2 -1 -EOF -1 -1 -1 -2 - --2 end parse - 0 begin parse, waiting '<' - 1 got '<' need to parse parameters, or wait '>' or wait '[' - 2 got '[' or ']' need to parse parameters - 3 got '>', waiting eof or '<' - - ********************************************************************************************/ - function ParseFirst($word) { - if ($this->iseof) { - $this->firstprev["state"]=0; - $this->firstprev["word"]=""; - return true; - } - $automat=array( - "0"=>array( 1, -1, -1, 1), - "1"=>array(-1, 2, -1, -1), - "2"=>array(-1, -1, 1, -1), - "3"=>array(-1, 3, -1, -1), - "4"=>array(-1, 1, 2, -1), - "5"=>array(-1, -1, -1, -2) - ); - switch($word) { - case "<": - $instate=0; - $this->pgpos++; - $this->parpos=-1; - break; - case "[": - $this->parpos++; - $instate=1; - break; - case "]": - $instate=2; - break; - case ">": - $instate=3; - break; - default: - $instate=4; - break; - } - $this->firststate=$automat[$instate][$this->firststate]; - if ($this->firststate==-1) return false; - switch ($this->firststate) { - case 1: - $this->mode=1; - if ($this->firstprev["state"]==1) - if (!$this->ParseSecond($word)) return false; - break; - case 2: - switch($this->firstprev["state"]) { - case 1: - $this->mode=2; - break; - case 3: - case 2: - if ($this->firstprev["state"]==2) - $this->mode=2; - else - $this->mode=1; - break; - } - if ($this->firstprev["state"]==2) - if (!$this->ParseSecond($word)) return false; - break; - case 3: - if (isset($this->pg[$this->pgpos]["tag"]["nohavesametag"])) - $this->pg[$this->pgpos]["tag"]["closeon"]["in"][]=$this->tagname; - break; - } - $this->firstprev["state"]=$this->firststate; - $this->firstprev["word"]=$word; - return true; - } - -/******************************************************************************************** - * Parse grammar second step - ******************************************************************************************** - -Parse -par1="value" par2=value - -in/state 0 1 2 3 4 -= -1 2 -1 3 -1 -" -1 -1 3 4 -1 -word 1 -1 4 3 1 -EOF -1 -1 -1 -1 -1 - --3 end parse by '>' --2 end parse by ']' - 0 begin parse waiting parname - 1 got parname, waiting '=' or new parname - 2 got '=' waiting any word as value or first '"' - 3 collect words to next '"' - 4 got parvalue, waiting new parname - ********************************************************************************************/ - function ParseSecond($word) { - if ($this->iseof) return false; - $automat=array( - "0"=>array(-1, 2, -1, 3, -1), - "1"=>array(-1, -1, 3, 4, -1), - "2"=>array( 1, 1, 4, 3, 1), - "3"=>array(-1, -1, -1, -1, -1) - ); - switch($word) { - case "=": - $instate=0; - break; - case "\"": - $instate=1; - break; - default: - $instate=2; - break; - } - $this->secondstate=$automat[$instate][$this->secondstate]; - if ($this->secondstate==-1) return false; - switch ($this->secondstate) { - case 1: - $this->parname=$word; - if (!ereg("[a-zA-Z_-]+([0-9]+)?",$word)) { - $this->SetError(1,"Fatal error.",$this->line,$this->column,"Error"); - return false; - } - switch($this->mode) { - case 1: - $this->pg[$this->pgpos]["tag"][$this->parname]=""; - break; - case 2: - $this->pg[$this->pgpos]["pars"][$this->parpos][$this->parname]=""; - break; - } - break; - case 4: - switch($this->mode) { - case 1: - if ($this->secondprev["state"]==3) - $this->pg[$this->pgpos]["tag"][$this->parname]=$this->secondprev["word"]; - else - $this->pg[$this->pgpos]["tag"][$this->parname]=$word; - if ($this->parname=="closeon") { - $notexists=array(); - $exists=array(); - $this->ParseCloseOn($this->pg[$this->pgpos]["tag"][$this->parname],&$notexists,&$exists); - $this->pg[$this->pgpos]["tag"][$this->parname]=array(); - $this->pg[$this->pgpos]["tag"][$this->parname]["notin"]=$notexists; - $this->pg[$this->pgpos]["tag"][$this->parname]["in"]=$exists; - } elseif ($this->parname=="tag") - $this->tagname=$this->pg[$this->pgpos]["tag"]["tag"]; - break; - case 2: - if ($this->secondprev["state"]==3) - $this->pg[$this->pgpos]["pars"][$this->parpos][$this->parname]=$this->secondprev["word"]; - else - $this->pg[$this->pgpos]["pars"][$this->parpos][$this->parname]=$word; - break; - } - break; - } - $this->secondprev["state"]=$this->secondstate; - $this->secondprev["word"]=$word; - return true; - } - -/******************************************************************************************** - * Parse closeon structure - ********************************************************************************************/ - function ParseCloseOn($str,$notexists,$exists) { - $arr=explode("|",$str); - if (!is_array($arr)) { - if (!strlen($str)) - return; - else - $arr[]=$str; - } - for ($i=0;$iallreadyparsed) return true; - $this->line=1; - while(1) { - $isword=$this->GetWord(&$word); - if (!$isword) $this->iseof=true; - switch (strtolower($word)) { - case "/*"; - $this->incomment*=-1; - break; - case "*/"; - if ($this->incomment!=1) { - $this->SetError(1,"Not found begin of comment operator.",$this->line,$this->column,"Error"); - return; - } - $this->incomment*=-1; - break; - default: - if ($this->incomment==1) break; - if (!$this->ParseFirst($word)) { - $this->SetError(1,"Fatal error",$this->line,$this->column,"Error"); - return false; - } - break; - } - if ($this->iseof) break; - } - if ($this->incomment==1) { - $this->SetError(1,"Not found end of comment operator.",$this->line,$this->column,"Error"); - return false; - } - $this->PrepareGrammar(); - return true; - } -/******************************************************************************************** - * Prepare grammar for future using - ********************************************************************************************/ - function PrepareGrammar() { - $edittagsaftertable=$this->ScanGrammar(); - $l=sizeof($this->pg); - for ($i=0;$i<$l;$i++) { - $this->pg[$this->pg[$i]["tag"]["tag"]]=$this->pg[$i]["tag"]; - if (isset($this->pg[$i]["pars"])) { - $n=sizeof($this->pg[$i]["pars"]); - for ($j=0;$j<$n;$j++) - $this->pg[$this->pg[$i]["tag"]["tag"]]["pars"][$this->pg[$i]["pars"][$j]["par"]]=$this->pg[$i]["pars"][$j]; - } else - $this->pg[$this->pg[$i]["tag"]["tag"]]["pars"]=array(); - unset($this->pg["$i"]); - } - $this->pg["EDIT_TAGS_AFTER_TABLE"]=$edittagsaftertable; - } -/******************************************************************************************** - * Scan grammar for creating edittagsafter table - ********************************************************************************************/ - function ScanGrammar() { - $edittagsaftertable=array(); - for ($i=0;$ipg);$i++) - if (isset($this->pg[$i]["tag"]["edittagsafter"])) - if (!in_array($this->pg[$i]["tag"]["edittagsafter"],$edittagsaftertable)) $edittagsaftertable[]=$this->pg[$i]["tag"]["edittagsafter"]; - return $edittagsaftertable; - } -/******************************************************************************************** - * Save precompiled grammar in file - ********************************************************************************************/ - function SaveGrammar($name) { - $str=serialize($this->pg); - if (!$fp=fopen($name,"w")) - print "
Error: Can't create file $name. Unable to save grammar.
"; - flock($fp,2); - fwrite($fp,$str,strlen($str)); - flock($fp,3); - fclose($fp); - } -} - -} //_ECHOSERVER_HTML_GRAMMARPARSER -?> diff --git a/htmlparser/readme.eng.txt b/htmlparser/readme.eng.txt deleted file mode 100644 index 96a9da1..0000000 --- a/htmlparser/readme.eng.txt +++ /dev/null @@ -1,19 +0,0 @@ -testhtmlparser.php - example of using -rebuildgrammar.php - grammar compiler -htmlgrammar.dat - grammar -htmlgrammar.cmp - compiled grammar -test.html - html file for test the html parser -common.inc - additional functions -imgsrcchange.inc - this example shows how to change src parameter for all img tags. - -Setup is very simple. You need copy all files into someone catalog on -your web server and after this call http://..../testhtmlparser.php -As result you will have start page of Russian PHPClub site which placed into the test.html file. -This page was formed by tag's tree after parse process. Bellow you can see -printed tag's tree. - -All questions you can send to -anton@concord.ru - -or by ICQ -3180528 diff --git a/htmlparser/rebuildgrammar.php b/htmlparser/rebuildgrammar.php deleted file mode 100644 index 9b5bdbb..0000000 --- a/htmlparser/rebuildgrammar.php +++ /dev/null @@ -1,13 +0,0 @@ -Parse(); -$p->PrintErrors(); -$p->SaveGrammar("htmlgrammar.cmp"); -print "Done."; -//PrintArray($p->pg); - -?> diff --git a/htmlpure/Filter/CNBC.php b/htmlpure/Filter/CNBC.php deleted file mode 100644 index f8bfdde..0000000 --- a/htmlpure/Filter/CNBC.php +++ /dev/null @@ -1,56 +0,0 @@ -#s'; - $pre_replace = '\6'; - $ret = preg_replace($pre_regex, $pre_replace, $html); - return $ret; - } - - public function postFilter($html, $config, &$context) { - $width = '\1'; - $height = '\2'; - - /* the CNBC embed size may be fixed by the swf app and so it may never be possible to override - // @config->def->info['bitweaver']['CNBC'] params width and height will force the size of the video - if( !empty( $config->def->info['bitweaver']['CNBC'] ) ){ - $moviesize = $config->def->info['bitweaver']['CNBC']; - $width = $moviesize['width']; - $height = $moviesize['height']; - } - */ - - $src_url='http://plus.cnbc.com/rssvideosearch/action/player/id/\3/code/cnbcplayershare'; - - $post_regex = '#([A-Za-z0-9\-_]+)#'; - $post_replace = '
- - - - - - - - - - -
'; - $ret = preg_replace($post_regex, $post_replace, $html); - return $ret; - } - -} - diff --git a/htmlpure/Filter/SafeIframe.php b/htmlpure/Filter/SafeIframe.php deleted file mode 100644 index 2b9a415..0000000 --- a/htmlpure/Filter/SafeIframe.php +++ /dev/null @@ -1,22 +0,0 @@ -#'; - $pre_replace = '\1'; - return preg_replace($pre_regex, $pre_replace, preg_replace("/<\/iframe>/", "", $html)); - } - - public function postFilter($html, $config, $context) { - $post_regex = '#(.+?)#'; - return preg_replace_callback($post_regex, array($this, 'postFilterCallback'), $html); - } - - protected function postFilterCallback($matches) { - return ''; - } -} - diff --git a/htmlpure/Filter/YouTube.php b/htmlpure/Filter/YouTube.php deleted file mode 100644 index 53615d4..0000000 --- a/htmlpure/Filter/YouTube.php +++ /dev/null @@ -1,46 +0,0 @@ -#s'; - $pre_replace = '\6'; - $ret = preg_replace($pre_regex, $pre_replace, $html); - return $ret; - } - - // @config->def->info['bitweaver']['YouTube'] params width and height will force the size of the video - public function postFilter($html, $config, &$context) { - $width = '\1'; - $height = '\2'; - if( !empty( $config->def->info['bitweaver']['YouTube'] ) ){ - $moviesize = $config->def->info['bitweaver']['YouTube']; - $width = $moviesize['width']; - $height = $moviesize['height']; - } - - $post_regex = '#([A-Za-z0-9\-_]+)#'; - $post_replace = '
'. - ''. - ''. - ''. - ''. - ''. - '
'; - $ret = preg_replace($post_regex, $post_replace, $html); - return $ret; - } - -} - diff --git a/includes/.htaccess b/includes/.htaccess new file mode 100644 index 0000000..3a42882 --- /dev/null +++ b/includes/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/includes/Date/Calc.php b/includes/Date/Calc.php new file mode 100644 index 0000000..349f3a8 --- /dev/null +++ b/includes/Date/Calc.php @@ -0,0 +1,1531 @@ + + */ +class Date_Calc +{ + /** + * Returns the current local date. NOTE: This function + * retrieves the local date using strftime(), which may + * or may not be 32-bit safe on your system. + * + * @param string the strftime() format to return the date + * + * @access public + * + * @return string the current date in specified format + */ + function dateNow($format="%Y%m%d") + { + return(strftime($format,time())); + } // end func dateNow + /** + * Returns true for valid date, false for invalid date. + * + * @param string year in format CCYY + * @param string month in format MM + * @param string day in format DD + * + * @access public + * + * @return boolean true/false + */ + function is_validDate($day, $month, $year) + { + if($year < 0 || $year > 9999) { + return false; + } + if(!checkdate($month,$day,$year)) { + return false; + } + return true; + } // end func is_validDate + /** + * Returns true for a leap year, else false + * + * @param string year in format CCYY + * + * @access public + * + * @return boolean true/false + */ + function isLeapYear($year="") + { + if(empty($year)) { + $year = Date_Calc::dateNow("%Y"); + } + if(strlen($year) != 4) { + return false; + } + if(preg_match("/\D/",$year)) { + return false; + } + return (($year % 4 == 0 && $year % 100 != 0) || $year % 400 == 0); + } // end func isLeapYear + /** + * Determines if given date is a future date from now. + * + * @param string year in format CCYY + * @param string month in format MM + * @param string day in format DD + * + * @access public + * + * @return boolean true/false + */ + function isFutureDate($day,$month,$year) + { + $this_year = Date_Calc::dateNow("%Y"); + $this_month = Date_Calc::dateNow("%m"); + $this_day = Date_Calc::dateNow("%d"); + if($year > $this_year) { + return true; + } elseif($year == $this_year) { + if($month > $this_month) { + return true; + } elseif($month == $this_month) { + if($day > $this_day) { + return true; + } + } + } + return false; + } // end func isFutureDate + /** + * Determines if given date is a past date from now. + * + * @param string year in format CCYY + * @param string month in format MM + * @param string day in format DD + * + * @access public + * + * @return boolean true/false + */ + function isPastDate($day,$month,$year) + { + $this_year = Date_Calc::dateNow("%Y"); + $this_month = Date_Calc::dateNow("%m"); + $this_day = Date_Calc::dateNow("%d"); + if($year < $this_year) { + return true; + } elseif($year == $this_year) { + if($month < $this_month) { + return true; + } elseif($month == $this_month) { + if($day < $this_day) { + return true; + } + } + } + return false; + } // end func isPastDate + /** + * Returns day of week for given date, 0=Sunday + * + * @param string year in format CCYY, default is current local year + * @param string month in format MM, default is current local month + * @param string day in format DD, default is current local day + * + * @access public + * + * @return int $weekday_number + */ + function dayOfWeek($day="",$month="",$year="") + { + if(empty($year)) { + $year = Date_Calc::dateNow("%Y"); + } + if(empty($month)) { + $month = Date_Calc::dateNow("%m"); + } + if(empty($day)) { + $day = Date_Calc::dateNow("%d"); + } + if($month > 2) { + $month -= 2; + } else { + $month += 10; + $year--; + } + $day = ( floor((13 * $month - 1) / 5) + + $day + ($year % 100) + + floor(($year % 100) / 4) + + floor(($year / 100) / 4) - 2 * + floor($year / 100) + 77); + $weekday_number = (($day - 7 * floor($day / 7))); + return $weekday_number; + } // end func dayOfWeek + /** + * Returns week of the year, first Sunday is first day of first week + * + * @param string day in format DD + * @param string month in format MM + * @param string year in format CCYY + * + * @access public + * + * @return integer $week_number + */ + function weekOfYear($day,$month,$year) + { + if(empty($year)) { + $year = Date_Calc::dateNow("%Y"); + } + if(empty($month)) { + $month = Date_Calc::dateNow("%m"); + } + if(empty($day)) { + $day = Date_Calc::dateNow("%d"); + } + $iso = Date_Calc::gregorianToISO($day, $month, $year); + $parts = explode('-',$iso); + $week_number = intval($parts[1]); + return $week_number; + } // end func weekOfYear + /** + * Returns number of days since 31 December of year before given date. + * + * @param string year in format CCYY, default is current local year + * @param string month in format MM, default is current local month + * @param string day in format DD, default is current local day + * + * @access public + * + * @return int $julian + */ + function julianDate($day="",$month="",$year="") + { + if(empty($year)) { + $year = Date_Calc::dateNow("%Y"); + } + if(empty($month)) { + $month = Date_Calc::dateNow("%m"); + } + if(empty($day)) { + $day = Date_Calc::dateNow("%d"); + } + $days = array(0,31,59,90,120,151,181,212,243,273,304,334); + $julian = ($days[$month - 1] + $day); + if($month > 2 && Date_Calc::isLeapYear($year)) { + $julian++; + } + return($julian); + } // end func julianDate + /** + * Returns quarter of the year for given date + * + * @param string year in format CCYY, default current local year + * @param string month in format MM, default current local month + * @param string day in format DD, default current local day + * + * @access public + * + * @return int $year_quarter + */ + function quarterOfYear($day="",$month="",$year="") + { + if(empty($year)) { + $year = Date_Calc::dateNow("%Y"); + } + if(empty($month)) { + $month = Date_Calc::dateNow("%m"); + } + if(empty($day)) { + $day = Date_Calc::dateNow("%d"); + } + $year_quarter = (intval(($month - 1) / 3 + 1)); + return $year_quarter; + } // end func quarterOfYear + /** + * Returns date of begin of next month of given date. + * + * @param string year in format CCYY, default current local year + * @param string month in format MM, default current local month + * @param string day in format DD, default current local day + * @param string format for returned date + * + * @access public + * + * @return string date in given format + */ + function beginOfNextMonth($day="",$month="",$year="",$format="%Y%m%d") + { + if(empty($year)) { + $year = Date_Calc::dateNow("%Y"); + } + if(empty($month)) { + $month = Date_Calc::dateNow("%m"); + } + if(empty($day)) { + $day = Date_Calc::dateNow("%d"); + } + if($month < 12) { + $month++; + $day=1; + } else { + $year++; + $month=1; + $day=1; + } + return Date_Calc::dateFormat($day,$month,$year,$format); + } // end func beginOfNextMonth + /** + * Returns date of the last day of next month of given date. + * + * @param string year in format CCYY, default current local year + * @param string month in format MM, default current local month + * @param string day in format DD, default current local day + * @param string format for returned date + * + * @access public + * + * @return string date in given format + */ + function endOfNextMonth($day="",$month="",$year="",$format="%Y%m%d") + { + if(empty($year)) { + $year = Date_Calc::dateNow("%Y"); + } + if(empty($month)) { + $month = Date_Calc::dateNow("%m"); + } + if(empty($day)) { + $day = Date_Calc::dateNow("%d"); + } + if($month < 12) { + $month++; + } else { + $year++; + $month=1; + } + $day = Date_Calc::daysInMonth($month,$year); + return Date_Calc::dateFormat($day,$month,$year,$format); + } // end func endOfNextMonth + /** + * Returns date of the first day of previous month of given date. + * + * @param string year in format CCYY, default current local year + * @param string month in format MM, default current local month + * @param string day in format DD, default current local day + * @param string format for returned date + * + * @access public + * + * @return string date in given format + */ + function beginOfPrevMonth($day="",$month="",$year="",$format="%Y%m%d") + { + if(empty($year)) { + $year = Date_Calc::dateNow("%Y"); + } + if(empty($month)) { + $month = Date_Calc::dateNow("%m"); + } + if(empty($day)) { + $day = Date_Calc::dateNow("%d"); + } + if($month > 1) { + $month--; + $day=1; + } else { + $year--; + $month=12; + $day=1; + } + return Date_Calc::dateFormat($day,$month,$year,$format); + } // end func beginOfPrevMonth + /** + * Returns date of the last day of previous month for given date. + * + * @param string year in format CCYY, default current local year + * @param string month in format MM, default current local month + * @param string day in format DD, default current local day + * @param string format for returned date + * + * @access public + * + * @return string date in given format + */ + function endOfPrevMonth($day="",$month="",$year="",$format="%Y%m%d") + { + if(empty($year)) { + $year = Date_Calc::dateNow("%Y"); + } + if(empty($month)) { + $month = Date_Calc::dateNow("%m"); + } + if(empty($day)) { + $day = Date_Calc::dateNow("%d"); + } + if($month > 1) { + $month--; + } else { + $year--; + $month=12; + } + $day = Date_Calc::daysInMonth($month,$year); + return Date_Calc::dateFormat($day,$month,$year,$format); + } // end func endOfPrevMonth + /** + * Returns date of the next weekday of given date, + * skipping from Friday to Monday. + * + * @param string year in format CCYY, default current local year + * @param string month in format MM, default current local month + * @param string day in format DD, default current local day + * @param string format for returned date + * + * @access public + * + * @return string date in given format + */ + function nextWeekday($day="",$month="",$year="",$format="%Y%m%d") + { + if(empty($year)) { + $year = Date_Calc::dateNow("%Y"); + } + if(empty($month)) { + $month = Date_Calc::dateNow("%m"); + } + if(empty($day)) { + $day = Date_Calc::dateNow("%d"); + } + $days = Date_Calc::dateToDays($day,$month,$year); + if(Date_Calc::dayOfWeek($day,$month,$year) == 5) { + $days += 3; + } elseif(Date_Calc::dayOfWeek($day,$month,$year) == 6) { + $days += 2; + } else { + $days += 1; + } + return(Date_Calc::daysToDate($days,$format)); + } // end func nextWeekday + /** + * Returns date of the previous weekday, + * skipping from Monday to Friday. + * + * @param string year in format CCYY, default current local year + * @param string month in format MM, default current local month + * @param string day in format DD, default current local day + * @param string format for returned date + * + * @access public + * + * @return string date in given format + */ + function prevWeekday($day="",$month="",$year="",$format="%Y%m%d") + { + if(empty($year)) { + $year = Date_Calc::dateNow("%Y"); + } + if(empty($month)) { + $month = Date_Calc::dateNow("%m"); + } + if(empty($day)) { + $day = Date_Calc::dateNow("%d"); + } + $days = Date_Calc::dateToDays($day,$month,$year); + if(Date_Calc::dayOfWeek($day,$month,$year) == 1) { + $days -= 3; + } elseif(Date_Calc::dayOfWeek($day,$month,$year) == 0) { + $days -= 2; + } else { + $days -= 1; + } + return(Date_Calc::daysToDate($days,$format)); + } // end func prevWeekday + /** + * Returns date of the next specific day of the week + * from the given date. + * + * @param int day of week, 0=Sunday + * @param string year in format CCYY, default current local year + * @param string month in format MM, default current local month + * @param string day in format DD, default current local day + * @param boolean onOrAfter if true and days are same, returns current day + * @param string format for returned date + * + * @access public + * + * @return string date in given format + */ + function nextDayOfWeek($dow,$day='',$month="",$year="",$format="%Y%m%d",$onOrAfter=false) + { + if(empty($year)) { + $year = Date_Calc::dateNow("%Y"); + } + if(empty($month)) { + $month = Date_Calc::dateNow("%m"); + } + if(empty($day)) { + $day = Date_Calc::dateNow("%d"); + } + $days = Date_Calc::dateToDays($day,$month,$year); + $curr_weekday = Date_Calc::dayOfWeek($day,$month,$year); + if($curr_weekday == $dow) { + if(!$onOrAfter) { + $days += 7; + } + } + elseif($curr_weekday > $dow) { + $days += 7 - ( $curr_weekday - $dow ); + } else { + $days += $dow - $curr_weekday; + } + return(Date_Calc::daysToDate($days,$format)); + } // end func nextDayOfWeek + /** + * Returns date of the previous specific day of the week + * from the given date. + * + * @param int day of week, 0=Sunday + * @param string year in format CCYY, default current local year + * @param string month in format MM, default current local month + * @param string day in format DD, default current local day + * @param boolean onOrBefore if true and days are same, returns current day + * @param string format for returned date + * + * @access public + * + * @return string date in given format + */ + function prevDayOfWeek($dow,$day="",$month="",$year="",$format="%Y%m%d",$onOrBefore=false) + { + if(empty($year)) { + $year = Date_Calc::dateNow("%Y"); + } + if(empty($month)) { + $month = Date_Calc::dateNow("%m"); + } + if(empty($day)) { + $day = Date_Calc::dateNow("%d"); + } + $days = Date_Calc::dateToDays($day,$month,$year); + $curr_weekday = Date_Calc::dayOfWeek($day,$month,$year); + if($curr_weekday == $dow) { + if(!$onOrBefore) { + $days -= 7; + } + } + elseif($curr_weekday < $dow) { + $days -= 7 - ( $dow - $curr_weekday ); + } else { + $days -= $curr_weekday - $dow; + } + return(Date_Calc::daysToDate($days,$format)); + } // end func prevDayOfWeek + /** + * Returns date of the next specific day of the week + * on or before the given date. + * + * @param int day of week, 0=Sunday + * @param string year in format CCYY, default current local year + * @param string month in format MM, default current local month + * @param string day in format DD, default current local day + * @param string format for returned date + * + * @access public + * + * @return string date in given format + */ + function nextDayOfWeekOnOrAfter($dow,$day="",$month="",$year="",$format="%Y%m%d") + { + return(Date_Calc::nextDayOfWeek($dow,$day="",$month="",$year="",$format="%Y%m%d",true)); + } // end func nextDayOfWeekOnOrAfter + /** + * Returns date of the previous specific day of the week + * on or before the given date. + * + * @param int day of week, 0=Sunday + * @param string year in format CCYY, default current local year + * @param string month in format MM, default current local month + * @param string day in format DD, default current local day + * @param string format for returned date + * + * @access public + * + * @return string date in given format + */ + function prevDayOfWeekOnOrBefore($dow,$day="",$month="",$year="",$format="%Y%m%d") + { + return(Date_Calc::prevDayOfWeek($dow,$day="",$month="",$year="",$format="%Y%m%d",true)); + } // end func prevDayOfWeekOnOrAfter + /** + * Returns date of day after given date. + * + * @param string year in format CCYY, default current local year + * @param string month in format MM, default current local month + * @param string day in format DD, default current local day + * @param string format for returned date + * + * @access public + * + * @return string date in given format + */ + function nextDay($day="",$month="",$year="",$format="%Y%m%d") + { + if(empty($year)) { + $year = Date_Calc::dateNow("%Y"); + } + if(empty($month)) { + $month = Date_Calc::dateNow("%m"); + } + if(empty($day)) { + $day = Date_Calc::dateNow("%d"); + } + $days = Date_Calc::dateToDays($day,$month,$year); + return(Date_Calc::daysToDate($days + 1,$format)); + } // end func nextDay + /** + * Returns date of day before given date. + * + * @param string year in format CCYY, default current local year + * @param string month in format MM, default current local month + * @param string day in format DD, default current local day + * @param string format for returned date + * + * @access public + * + * @return string date in given format + */ + function prevDay($day="",$month="",$year="",$format="%Y%m%d") + { + if(empty($year)) { + $year = Date_Calc::dateNow("%Y"); + } + if(empty($month)) { + $month = Date_Calc::dateNow("%m"); + } + if(empty($day)) { + $day = Date_Calc::dateNow("%d"); + } + $days = Date_Calc::dateToDays($day,$month,$year); + return(Date_Calc::daysToDate($days - 1,$format)); + } // end func prevDay + /** + * Sets century for 2 digit year. + * 51-99 is 19, else 20 + * + * @param string 2 digit year + * + * @access public + * + * @return string 4 digit year + */ + function defaultCentury($year) + { + if(strlen($year) == 1) { + $year = "0$year"; + } + if($year > 50) { + return( "19$year" ); + } else { + return( "20$year" ); + } + } // end func defaultCentury + /** + * Returns number of days between two given dates. + * + * @param string year in format CCYY + * @param string month in format MM + * @param string day in format DD + * @param string year in format CCYY + * @param string month in format MM + * @param string day in format DD + * + * @access public + * + * @return int absolute number of days between dates, + * -1 if there is an error. + */ + function dateDiff($day1,$month1,$year1,$day2,$month2,$year2) + { + if(!Date_Calc::is_validDate($day1,$month1,$year1)) { + return -1; + } + if(!Date_Calc::is_validDate($day2,$month2,$year2)) { + return -1; + } + return(abs((Date_Calc::dateToDays($day1,$month1,$year1)) + - (Date_Calc::dateToDays($day2,$month2,$year2)))); + } // end func dateDiff + /** + * Compares two dates + * + * @param string $day1 day in format DD + * @param string $month1 month in format MM + * @param string $year1 year in format CCYY + * @param string $day2 day in format DD + * @param string $month2 month in format MM + * @param string $year2 year in format CCYY + * + * @access public + * @return int 0 on equality, 1 if date 1 is greater, -1 if smaller + */ + function compareDates($day1,$month1,$year1,$day2,$month2,$year2) + { + $ndays1 = Date_Calc::dateToDays($day1, $month1, $year1); + $ndays2 = Date_Calc::dateToDays($day2, $month2, $year2); + if ($ndays1 == $ndays2) { + return 0; + } + return ($ndays1 > $ndays2) ? 1 : -1; + } // end func compareDates + /** + * Find the number of days in the given month. + * + * @param string month in format MM, default current local month + * + * @access public + * + * @return int number of days + */ + function daysInMonth($month="",$year="") + { + if(empty($year)) { + $year = Date_Calc::dateNow("%Y"); + } + if(empty($month)) { + $month = Date_Calc::dateNow("%m"); + } + if($month == 2) { + if(Date_Calc::isLeapYear($year)) { + return 29; + } else { + return 28; + } + } elseif($month == 4 or $month == 6 or $month == 9 or $month == 11) { + return 30; + } else { + return 31; + } + } // end func daysInMonth + /** + * Returns the number of rows on a calendar month. Useful for + * determining the number of rows when displaying a typical + * month calendar. + * + * @param string month in format MM, default current local month + * @param string year in format YYCC, default current local year + * + * @access public + * + * @return int number of weeks + */ + function weeksInMonth($month="",$year="") + { + if(empty($year)) { + $year = Date_Calc::dateNow("%Y"); + } + if(empty($month)) { + $month = Date_Calc::dateNow("%m"); + } + if(DATE_CALC_BEGIN_WEEKDAY == 1) { + if(Date_Calc::firstOfMonthWeekday($month,$year) == 0) { + $first_week_days = 1; + } else { + $first_week_days = + 7 - (Date_Calc::firstOfMonthWeekday($month,$year) - 1); + } + } else { + $first_week_days = 7 - Date_Calc::firstOfMonthWeekday($month,$year); + } + return ceil(((Date_Calc::daysInMonth($month,$year) - $first_week_days) / 7) + 1); + } // end func weeksInMonth + /** + * Find the day of the week for the first of the month of given date. + * + * @param string year in format CCYY, default to current local year + * @param string month in format MM, default to current local month + * + * @access public + * + * @return int number of weekday for the first day, 0=Sunday + */ + function firstOfMonthWeekday($month="",$year="") + { + if(empty($year)) { + $year = Date_Calc::dateNow("%Y"); + } + if(empty($month)) { + $month = Date_Calc::dateNow("%m"); + } + return(Date_Calc::dayOfWeek("01",$month,$year)); + } // end func firstOfMonthWeekday + /** + * Return date of first day of month of given date. + * + * @param string year in format CCYY, default current local year + * @param string month in format MM, default current local month + * @param string format for returned date + * + * @access public + * + * @return string date in given format + */ + function beginOfMonth($month="",$year="",$format="%Y%m%d") + { + if(empty($year)) { + $year = Date_Calc::dateNow("%Y"); + } + if(empty($month)) { + $month = Date_Calc::dateNow("%m"); + } + return(Date_Calc::dateFormat("01",$month,$year,$format)); + } // end of func beginOfMonth + /** + * Find the month day of the beginning of week for given date, + * using DATE_CALC_BEGIN_WEEKDAY. (can return weekday of prev month.) + * + * @param string year in format CCYY, default current local year + * @param string month in format MM, default current local month + * @param string day in format DD, default current local day + * @param string format for returned date + * + * @access public + * + * @return string date in given format + */ + function beginOfWeek($day="",$month="",$year="",$format="%Y%m%d") + { + if(empty($year)) { + $year = Date_Calc::dateNow("%Y"); + } + if(empty($month)) { + $month = Date_Calc::dateNow("%m"); + } + if(empty($day)) { + $day = Date_Calc::dateNow("%d"); + } + $this_weekday = Date_Calc::dayOfWeek($day,$month,$year); + if(DATE_CALC_BEGIN_WEEKDAY == 1) { + if($this_weekday == 0) { + $beginOfWeek = Date_Calc::dateToDays($day,$month,$year) - 6; + } else { + $beginOfWeek = Date_Calc::dateToDays($day,$month,$year) + - $this_weekday + 1; + } + } else { + $beginOfWeek = (Date_Calc::dateToDays($day,$month,$year) + - $this_weekday); + } + /* $beginOfWeek = (Date_Calc::dateToDays($day,$month,$year) + - ($this_weekday - DATE_CALC_BEGIN_WEEKDAY)); */ + return(Date_Calc::daysToDate($beginOfWeek,$format)); + } // end of func beginOfWeek + /** + * Find the month day of the end of week for given date, + * using DATE_CALC_BEGIN_WEEKDAY. (can return weekday + * of following month.) + * + * @param string year in format CCYY, default current local year + * @param string month in format MM, default current local month + * @param string day in format DD, default current local day + * @param string format for returned date + * + * @access public + * + * @return string date in given format + */ + function endOfWeek($day="",$month="",$year="",$format="%Y%m%d") + { + if(empty($year)) { + $year = Date_Calc::dateNow("%Y"); + } + if(empty($month)) { + $month = Date_Calc::dateNow("%m"); + } + if(empty($day)) { + $day = Date_Calc::dateNow("%d"); + } + $this_weekday = Date_Calc::dayOfWeek($day,$month,$year); + $last_dayOfWeek = (Date_Calc::dateToDays($day,$month,$year) + + (6 - $this_weekday + DATE_CALC_BEGIN_WEEKDAY)); + return(Date_Calc::daysToDate($last_dayOfWeek,$format)); + } // end func endOfWeek + /** + * Find the month day of the beginning of week after given date, + * using DATE_CALC_BEGIN_WEEKDAY. (can return weekday of prev month.) + * + * @param string year in format CCYY, default current local year + * @param string month in format MM, default current local month + * @param string day in format DD, default current local day + * @param string format for returned date + * + * @access public + * + * @return string date in given format + */ + function beginOfNextWeek($day="",$month="",$year="",$format="%Y%m%d") + { + if(empty($year)) { + $year = Date_Calc::dateNow("%Y"); + } + if(empty($month)) { + $month = Date_Calc::dateNow("%m"); + } + if(empty($day)) { + $day = Date_Calc::dateNow("%d"); + } + $date = Date_Calc::daysToDate(Date_Calc::dateToDays($day+7,$month,$year),"%Y%m%d"); + $next_week_year = substr($date,0,4); + $next_week_month = substr($date,4,2); + $next_week_day = substr($date,6,2); + $this_weekday = + Date_Calc::dayOfWeek($next_week_day,$next_week_month,$next_week_year); + $beginOfWeek = (Date_Calc::dateToDays($next_week_day,$next_week_month,$next_week_year) + - ($this_weekday - DATE_CALC_BEGIN_WEEKDAY)); + return(Date_Calc::daysToDate($beginOfWeek,$format)); + } // end func beginOfNextWeek + /** + * Find the month day of the beginning of week before given date, + * using DATE_CALC_BEGIN_WEEKDAY. (can return weekday of prev month.) + * + * @param string year in format CCYY, default current local year + * @param string month in format MM, default current local month + * @param string day in format DD, default current local day + * @param string format for returned date + * + * @access public + * + * @return string date in given format + */ + function beginOfPrevWeek($day="",$month="",$year="",$format="%Y%m%d") + { + if(empty($year)) { + $year = Date_Calc::dateNow("%Y"); + } + if(empty($month)) { + $month = Date_Calc::dateNow("%m"); + } + if(empty($day)) { + $day = Date_Calc::dateNow("%d"); + } + $date = Date_Calc::daysToDate(Date_Calc::dateToDays($day-7,$month,$year),"%Y%m%d"); + $next_week_year = substr($date,0,4); + $next_week_month = substr($date,4,2); + $next_week_day = substr($date,6,2); + $this_weekday = + Date_Calc::dayOfWeek($next_week_day,$next_week_month,$next_week_year); + $beginOfWeek = (Date_Calc::dateToDays($next_week_day,$next_week_month,$next_week_year) + - ($this_weekday - DATE_CALC_BEGIN_WEEKDAY)); + return(Date_Calc::daysToDate($beginOfWeek,$format)); + } // end func beginOfPrevWeek + /** + * Return an array with days in week + * + * @param string year in format CCYY, default current local year + * @param string month in format MM, default current local month + * @param string day in format DD, default current local day + * @param string format for returned date + * + * @access public + * + * @return array $week[$weekday] + */ + function getCalendarWeek($day="",$month="",$year="",$format="%Y%m%d") + { + if(empty($year)) { + $year = Date_Calc::dateNow("%Y"); + } + if(empty($month)) { + $month = Date_Calc::dateNow("%m"); + } + if(empty($day)) { + $day = Date_Calc::dateNow("%d"); + } + $week_array = array(); + // date for the column of week + $curr_day = Date_Calc::beginOfWeek($day,$month,$year,"%E"); + for($counter=0; $counter <= 6; $counter++) { + $week_array[$counter] = Date_Calc::daysToDate($curr_day,$format); + $curr_day++; + } + return $week_array; + } // end func getCalendarWeek + /** + * Return a set of arrays to construct a calendar month for + * the given date. + * + * @param string year in format CCYY, default current local year + * @param string month in format MM, default current local month + * @param string format for returned date + * + * @access public + * + * @return array $month[$row][$col] + */ + function getCalendarMonth($month="",$year="",$format="%Y%m%d") + { + if(empty($year)) { + $year = Date_Calc::dateNow("%Y"); + } + if(empty($month)) { + $month = Date_Calc::dateNow("%m"); + } + $month_array = array(); + // date for the first row, first column of calendar month + if(DATE_CALC_BEGIN_WEEKDAY == 1) { + if(Date_Calc::firstOfMonthWeekday($month,$year) == 0) { + $curr_day = Date_Calc::dateToDays("01",$month,$year) - 6; + } else { + $curr_day = Date_Calc::dateToDays("01",$month,$year) + - Date_Calc::firstOfMonthWeekday($month,$year) + 1; + } + } else { + $curr_day = (Date_Calc::dateToDays("01",$month,$year) + - Date_Calc::firstOfMonthWeekday($month,$year)); + } + // number of days in this month + $daysInMonth = Date_Calc::daysInMonth($month,$year); + $weeksInMonth = Date_Calc::weeksInMonth($month,$year); + for($row_counter=0; $row_counter < $weeksInMonth; $row_counter++) { + for($column_counter=0; $column_counter <= 6; $column_counter++) { + $month_array[$row_counter][$column_counter] = + Date_Calc::daysToDate($curr_day,$format); + $curr_day++; + } + } + return $month_array; + } // end func getCalendarMonth + /** + * Return a set of arrays to construct a calendar year for + * the given date. + * + * @param string year in format CCYY, default current local year + * @param string format for returned date + * + * @access public + * + * @return array $year[$month][$row][$col] + */ + function getCalendarYear($year="",$format="%Y%m%d") + { + if(empty($year)) { + $year = Date_Calc::dateNow("%Y"); + } + $year_array = array(); + for($curr_month=0; $curr_month <=11; $curr_month++) { + $year_array[$curr_month] = + Date_Calc::getCalendarMonth(sprintf("%02d",$curr_month+1),$year,$format); + } + return $year_array; + } // end func getCalendarYear + /** + * Converts a date to number of days since a + * distant unspecified epoch. + * + * @param string year in format CCYY + * @param string month in format MM + * @param string day in format DD + * + * @access public + * + * @return integer number of days + */ + function dateToDays($day,$month,$year) + { + $century = (int) substr($year,0,2); + $year = (int) substr($year,2,2); + if($month > 2) { + $month -= 3; + } else { + $month += 9; + if($year) { + $year--; + } else { + $year = 99; + $century --; + } + } + return (floor(( 146097 * $century) / 4 ) + + floor(( 1461 * $year) / 4 ) + + floor(( 153 * $month + 2) / 5 ) + + $day + 1721119); + } // end func dateToDays + /** + * Converts number of days to a distant unspecified epoch. + * + * @param int number of days + * @param string format for returned date + * + * @access public + * + * @return string date in specified format + */ + function daysToDate($days,$format="%Y%m%d") + { + $days -= 1721119; + $century = floor(( 4 * $days - 1) / 146097); + $days = floor(4 * $days - 1 - 146097 * $century); + $day = floor($days / 4); + $year = floor(( 4 * $day + 3) / 1461); + $day = floor(4 * $day + 3 - 1461 * $year); + $day = floor(($day + 4) / 4); + $month = floor(( 5 * $day - 3) / 153); + $day = floor(5 * $day - 3 - 153 * $month); + $day = floor(($day + 5) / 5); + if($month < 10) { + $month +=3; + } else { + $month -=9; + if($year++ == 99) { + $year = 0; + $century++; + } + } + $century = sprintf("%02d",$century); + $year = sprintf("%02d",$year); + return(Date_Calc::dateFormat($day,$month,$century.$year,$format)); + } // end func daysToDate + /** + * Calculates the date of the Nth weekday of the month, + * such as the second Saturday of January 2000. + * + * @param string occurance: 1=first, 2=second, 3=third, etc. + * @param string dayOfWeek: 0=Sunday, 1=Monday, etc. + * @param string year in format CCYY + * @param string month in format MM + * @param string format for returned date + * + * @access public + * + * @return string date in given format + */ + function NWeekdayOfMonth($occurance,$dayOfWeek,$month,$year,$format="%Y%m%d") + { + $year = sprintf("%04d",$year); + $month = sprintf("%02d",$month); + $DOW1day = sprintf("%02d",(($occurance - 1) * 7 + 1)); + $DOW1 = Date_Calc::dayOfWeek($DOW1day,$month,$year); + $wdate = ($occurance - 1) * 7 + 1 + + (7 + $dayOfWeek - $DOW1) % 7; + if( $wdate > Date_Calc::daysInMonth($month,$year)) { + return -1; + } else { + return(Date_Calc::dateFormat($wdate,$month,$year,$format)); + } + } // end func NWeekdayOfMonth + /** + * Formats the date in the given format, much like + * strfmt(). This function is used to alleviate the + * problem with 32-bit numbers for dates pre 1970 + * or post 2038, as strfmt() has on most systems. + * Most of the formatting options are compatible. + * + * formatting options: + * + * %a abbreviated weekday name (Sun, Mon, Tue) + * %A full weekday name (Sunday, Monday, Tuesday) + * %b abbreviated month name (Jan, Feb, Mar) + * %B full month name (January, February, March) + * %d day of month (range 00 to 31) + * %e day of month, single digit (range 0 to 31) + * %E number of days since unspecified epoch (integer) + * (%E is useful for passing a date in a URL as + * an integer value. Then simply use + * daysToDate() to convert back to a date.) + * %j day of year (range 001 to 366) + * %m month as decimal number (range 1 to 12) + * %n newline character (\n) + * %t tab character (\t) + * %w weekday as decimal (0 = Sunday) + * %U week number of current year, first sunday as first week + * %y year as decimal (range 00 to 99) + * %Y year as decimal including century (range 0000 to 9999) + * %% literal '%' + * + * @param string year in format CCYY + * @param string month in format MM + * @param string day in format DD + * @param string format for returned date + * + * @access public + * + * @return string date in given format + */ + function dateFormat($day,$month,$year,$format) + { + if(!Date_Calc::is_validDate($day,$month,$year)) { + $year = Date_Calc::dateNow("%Y"); + $month = Date_Calc::dateNow("%m"); + $day = Date_Calc::dateNow("%d"); + } + $output = ""; + for($strpos = 0; $strpos < strlen($format); $strpos++) { + $char = substr($format,$strpos,1); + if($char == "%") { + $nextchar = substr($format,$strpos + 1,1); + switch($nextchar) { + case "a": + $output .= Date_Calc::getWeekdayAbbrname($day,$month,$year); + break; + case "A": + $output .= Date_Calc::getWeekdayFullname($day,$month,$year); + break; + case "b": + $output .= Date_Calc::getMonthAbbrname($month); + break; + case "B": + $output .= Date_Calc::getMonthFullname($month); + break; + case "d": + $output .= sprintf("%02d",$day); + break; + case "e": + $output .= $day; + break; + case "E": + $output .= Date_Calc::dateToDays($day,$month,$year); + break; + case "j": + $output .= Date_Calc::julianDate($day,$month,$year); + break; + case "m": + $output .= sprintf("%02d",$month); + break; + case "n": + $output .= "\n"; + break; + case "t": + $output .= "\t"; + break; + case "w": + $output .= Date_Calc::dayOfWeek($day,$month,$year); + break; + case "U": + $output .= Date_Calc::weekOfYear($day,$month,$year); + break; + case "y": + $output .= substr($year,2,2); + break; + case "Y": + $output .= $year; + break; + case "%": + $output .= "%"; + break; + default: + $output .= $char.$nextchar; + } + $strpos++; + } else { + $output .= $char; + } + } + return $output; + } // end func dateFormat + /** + * Returns the current local year in format CCYY + * + * @access public + * + * @return string year in format CCYY + */ + function getYear() + { + return Date_Calc::dateNow("%Y"); + } // end func getYear + /** + * Returns the current local month in format MM + * + * @access public + * + * @return string month in format MM + */ + function getMonth() + { + return Date_Calc::dateNow("%m"); + } // end func getMonth + /** + * Returns the current local day in format DD + * + * @access public + * + * @return string day in format DD + */ + function getDay() + { + return Date_Calc::dateNow("%d"); + } // end func getDay + /** + * Returns the full month name for the given month + * + * @param string month in format MM + * + * @access public + * + * @return string full month name + */ + function getMonthFullname($month) + { + $month = (int)$month; + if(empty($month)) { + $month = (int) Date_Calc::dateNow("%m"); + } + $month_names = Date_Calc::getMonthNames(); + return $month_names[$month]; + // getMonthNames returns months with correct indexes + //return $month_names[($month - 1)]; + } // end func getMonthFullname + /** + * Returns the abbreviated month name for the given month + * + * @param string month in format MM + * @param int optional length of abbreviation, default is 3 + * + * @access public + * + * @return string abbreviated month name + * @see Date_Calc::getMonthFullname + */ + function getMonthAbbrname($month,$length=3) + { + $month = (int)$month; + if(empty($month)) { + $month = Date_Calc::dateNow("%m"); + } + return mb_substr(Date_Calc::getMonthFullname($month), 0, $length); + } // end func getMonthAbbrname + /** + * Returns the full weekday name for the given date + * + * @param string year in format CCYY, default current local year + * @param string month in format MM, default current local month + * @param string day in format DD, default current local day + * + * @access public + * + * @return string full month name + */ + function getWeekdayFullname($day="",$month="",$year="") + { + if(empty($year)) { + $year = Date_Calc::dateNow("%Y"); + } + if(empty($month)) { + $month = Date_Calc::dateNow("%m"); + } + if(empty($day)) { + $day = Date_Calc::dateNow("%d"); + } + $weekday_names = Date_Calc::getWeekDays(); + $weekday = Date_Calc::dayOfWeek($day,$month,$year); + return $weekday_names[$weekday]; + } // end func getWeekdayFullname + /** + * Returns the abbreviated weekday name for the given date + * + * @param string year in format CCYY, default current local year + * @param string month in format MM, default current local month + * @param string day in format DD, default current local day + * @param int optional length of abbreviation, default is 3 + * + * @access public + * + * @return string full month name + * @see Date_Calc::getWeekdayFullname + */ + function getWeekdayAbbrname($day="",$month="",$year="",$length=3) + { + if(empty($year)) { + $year = Date_Calc::dateNow("%Y"); + } + if(empty($month)) { + $month = Date_Calc::dateNow("%m"); + } + if(empty($day)) { + $day = Date_Calc::dateNow("%d"); + } + return mb_substr(Date_Calc::getWeekdayFullname($day,$month,$year),0,$length); + } // end func getWeekdayFullname + /** + * Returns the numeric month from the month name or an abreviation + * + * Both August and Aug would return 8. + * Month name is case insensitive. + * + * @param string month name + * @return integer month number + */ + function getMonthFromFullName($month) + { + $month = strtolower($month); + $months = Date_Calc::getMonthNames(); + while(list($id, $name) = each($months)) { + if(ereg($month, strtolower($name))) { + return($id); + } + } + return(0); + } // end func getMonthFromFullName + /** + * Returns an array of month names + * + * Used to take advantage of the setlocale function to return + * language specific month names. + * XXX cache values to some global array to avoid preformace + * hits when called more than once. + * + * @returns array An array of month names + */ + function getMonthNames() + { + for($i=1;$i<13;$i++) { + $months[$i] = strftime('%B', mktime(0, 0, 0, $i, 1, 2001)); + } + return($months); + } // end func getMonthNames + /** + * Returns an array of week days + * + * Used to take advantage of the setlocale function to + * return language specific week days + * XXX cache values to some global array to avoid preformace + * hits when called more than once. + * + * @returns array An array of week day names + */ + function getWeekDays() + { + for($i=0;$i<7;$i++) { + $weekdays[$i] = strftime('%A', mktime(0, 0, 0, 1, $i, 2001)); + } + return($weekdays); + } // end func getWeekDays + /** + * Converts from Gregorian Year-Month-Day to ISO YearNumber-WeekNumber-WeekDay + * + * Uses ISO 8601 definitions. + * Algorithm from Rick McCarty, 1999 at http://personal.ecu.edu/mccartyr/ISOwdALG.txt + * + * @param int $year + * @param int $month + * @param int $day + * @return string + * @access public + */ + // Transcribed to PHP by Jesus M. Castagnetto (blame him if it is fubared ;-) + function gregorianToISO($day, $month, $year) { + $mnth = array (0,31,59,90,120,151,181,212,243,273,304,334); + $y_isleap = Date_Calc::isLeapYear($year); + $y_1_isleap = Date_Calc::isLeapYear($year - 1); + $day_of_year_number = $day + $mnth[$month - 1]; + if ($y_isleap && $month > 2) { + $day_of_year_number++; + } + // find Jan 1 weekday (monday = 1, sunday = 7) + $yy = ($year - 1) % 100; + $c = ($year - 1) - $yy; + $g = $yy + intval($yy/4); + $jan1_weekday = 1 + intval((((($c / 100) % 4) * 5) + $g) % 7); + // weekday for year-month-day + $h = $day_of_year_number + ($jan1_weekday - 1); + $weekday = 1 + intval(($h - 1) % 7); + // find if Y M D falls in YearNumber Y-1, WeekNumber 52 or + if ($day_of_year_number <= (8 - $jan1_weekday) && $jan1_weekday > 4){ + $yearnumber = $year - 1; + if ($jan1_weekday == 5 || ($jan1_weekday == 6 && $y_1_isleap)) { + $weeknumber = 53; + } else { + $weeknumber = 52; + } + } else { + $yearnumber = $year; + } + // find if Y M D falls in YearNumber Y+1, WeekNumber 1 + if ($yearnumber == $year) { + if ($y_isleap) { + $i = 366; + } else { + $i = 365; + } + if (($i - $day_of_year_number) < (4 - $weekday)) { + $yearnumber++; + $weeknumber = 1; + } + } + // find if Y M D falls in YearNumber Y, WeekNumber 1 through 53 + if ($yearnumber == $year) { + $j = $day_of_year_number + (7 - $weekday) + ($jan1_weekday - 1); + //$weeknumber = intval($j / 7) + 1; // kludge!!! - JMC + $weeknumber = intval($j / 7); // kludge!!! - JMC + if ($jan1_weekday > 4) { + $weeknumber--; + } + } + // put it all together + if ($weeknumber < 10) + $weeknumber = '0'.$weeknumber; + return "{$yearnumber}-{$weeknumber}-{$weekday}"; + } + /** + * Determines julian date of the given season + * Adapted from previous work in Java by James Mark Hamilton, mhamilton@qwest.net + * + * @author Robert Butler + * + * @param string is VERNALEQUINOX, SUMMERSOLSTICE, AUTUMNALEQUINOX, or WINTERSOLSTICE. + * @param string year in format CCYY, must be a calendar year between -1000BC and 3000AD. + * + * @access public + * + * @return float $juliandate + */ + function dateSeason ($season, $year = '') { + if ($year == '') { + $year = Date_Calc::dateNow('%Y'); + } + if (($year >= -1000) && ($year <= 1000)) { + $y = $year / 1000.0; + if ($season == "VERNALEQUINOX") { + $juliandate = (((((((-0.00071 * $y) - 0.00111) * $y) + 0.06134) * $y) + 365242.1374) * $y) + 1721139.29189; + } else if ($season == "SUMMERSOLSTICE") { + $juliandate = ((((((( 0.00025 * $y) + 0.00907) * $y) - 0.05323) * $y) + 365241.72562) * $y) + 1721233.25401; + } else if ($season == "AUTUMNALEQUINOX") { + $juliandate = ((((((( 0.00074 * $y) - 0.00297) * $y) - 0.11677) * $y) + 365242.49558) * $y) + 1721325.70455; + } else if ($season == "WINTERSOLSTICE") { + $juliandate = (((((((-0.00006 * $y) - 0.00933) * $y) - 0.00769) * $y) + 365242.88257) * $y) + 1721414.39987; + } + } elseif (($year > 1000) && ($year <= 3000)) { + $y = ($year - 2000) / 1000; + if ($season == "VERNALEQUINOX") { + $juliandate = (((((((-0.00057 * $y) - 0.00411) * $y) + 0.05169) * $y) + 365242.37404) * $y) + 2451623.80984; + } else if ($season == "SUMMERSOLSTICE") { + $juliandate = (((((((-0.0003 * $y) + 0.00888) * $y) + 0.00325) * $y) + 365241.62603) * $y) + 2451716.56767; + } else if ($season == "AUTUMNALEQUINOX") { + $juliandate = ((((((( 0.00078 * $y) + 0.00337) * $y) - 0.11575) * $y) + 365242.01767) * $y) + 2451810.21715; + } else if ($season == "WINTERSOLSTICE") { + $juliandate = ((((((( 0.00032 * $y) - 0.00823) * $y) - 0.06223) * $y) + 365242.74049) * $y) + 2451900.05952; + } + } + return ($juliandate); + } // end func dateSeason +} // end class Date_Calc +?> \ No newline at end of file diff --git a/includes/Date/Human.php b/includes/Date/Human.php new file mode 100644 index 0000000..a09caa9 --- /dev/null +++ b/includes/Date/Human.php @@ -0,0 +1,184 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id$ +// +/** + * Class to convert date strings between Gregorian and Human calendar formats. + * The Human Calendar format has been proposed by Scott Flansburg and can be + * explained as follows: + * The year is made up of 13 months + * Each month has 28 days + * Counting of months starts from 0 (zero) so the months will run from 0 to 12 + * New Years day (00) is a monthless day + * Note: Leap Years are not yet accounted for in the Human Calendar system + * + * @since PHP 4.0.4 + * @author Allan Kent + */ +class Date_Human +{ + /** + * Returns an associative array containing the converted date information + * in 'Human Calendar' format. + * + * @param int day in DD format, default current local day + * @param int month in MM format, default current local month + * @param int year in CCYY format, default to current local year + * + * @access public + * + * @return associative array( + * hdom, // Human Day Of Month, starting at 1 + * hdow, // Human Day Of Week, starting at 1 + * hwom, // Human Week of Month, starting at 1 + * hwoy, // Human Week of Year, starting at 1 + * hmoy, // Human Month of Year, starting at 0 + * ) + * + * If the day is New Years Day, the function will return + * "hdom" => 0 + * "hdow" => 0 + * "hwom" => 0 + * "hwoy" => 0 + * "hmoy" => -1 + * Since 0 is a valid month number under the Human Calendar, I have left + * the month as -1 for New Years Day. + */ + function gregorianToHuman($day=0, $month=0, $year=0) + { + /** + * Check to see if any of the arguments are empty + * If they are then populate the $dateinfo array + * Then check to see which arguments are empty and fill + * those with the current date info + */ + if ((empty($day) || (empty($month)) || empty($year))) { + $dateinfo = getdate(time()); + } + if (empty($day)) { + $day = $dateinfo["mday"]; + } + if (empty($month)) { + $month = $dateinfo["mon"]; + } + if (empty($year)) { + $year = $dateinfo["year"]; + } + /** + * We need to know how many days into the year we are + */ + $dateinfo = getdate(mktime(0, 0, 0, $month, $day, $year)); + $dayofyear = $dateinfo["yday"]; + /** + * Human Calendar starts at 0 for months and the first day of the year + * is designated 00, so we need to start our day of the year at 0 for + * these calculations. + * Also, the day of the month is calculated with a modulus of 28. + * Because a day is 28 days, the last day of the month would have a + * remainder of 0 and not 28 as it should be. Decrementing $dayofyear + * gets around this. + */ + $dayofyear--; + /** + * 28 days in a month... + */ + $humanMonthOfYear = floor($dayofyear / 28); + /** + * If we are in the first month then the day of the month is $dayofyear + * else we need to find the modulus of 28. + */ + if ($humanMonthOfYear == 0) { + $humanDayOfMonth = $dayofyear; + } else { + $humanDayOfMonth = ($dayofyear) % 28; + } + /** + * Day of the week is modulus 7 + */ + $humanDayOfWeek = $dayofyear % 7; + /** + * We can now increment $dayofyear back to it's correct value for + * the remainder of the calculations + */ + $dayofyear++; + /** + * $humanDayOfMonth needs to be incremented now - recall that we fudged + * it a bit by decrementing $dayofyear earlier + * Same goes for $humanDayOfWeek + */ + $humanDayOfMonth++; + $humanDayOfWeek++; + /** + * Week of the month is day of the month divided by 7, rounded up + * Same for week of the year, but use $dayofyear instead $humanDayOfMonth + */ + $humanWeekOfMonth = ceil($humanDayOfMonth / 7); + $humanWeekOfYear = ceil($dayofyear / 7); + /** + * Return an associative array of the values + */ + return array( + "hdom" => $humanDayOfMonth, + "hdow" => $humanDayOfWeek, + "hwom" => $humanWeekOfMonth, + "hwoy" => $humanWeekOfYear, + "hmoy" => $humanMonthOfYear ); + } + /** + * Returns unix timestamp for a given Human Calendar date + * + * @param int day in DD format + * @param int month in MM format + * @param int year in CCYY format, default to current local year + * + * @access public + * + * @return int unix timestamp of date + */ + function HumanToGregorian($day, $month, $year=0) + { + /** + * Check to see if the year has been passed through. + * If not get current year + */ + if (empty($year)) { + $dateinfo = getdate(time()); + $year = $dateinfo["year"]; + } + /** + * We need to get the day of the year that we are currently at so that + * we can work out the Gregorian Month and day + */ + $DayOfYear = $month * 28; + $DayOfYear += $day; + /** + * Human Calendar starts at 0, so we need to increment $DayOfYear + * to take into account the day 00 + */ + $DayOfYear++; + /** + * the mktime() function will correctly calculate the date for out of + * range values, so putting $DayOfYear instead of the day of the month + * will work fine. + */ + $GregorianTimeStamp = mktime(0, 0, 0, 1, $DayOfYear, $year); + return $GregorianTimeStamp; + } +} +?> \ No newline at end of file diff --git a/includes/Date/TimeZone.php b/includes/Date/TimeZone.php new file mode 100644 index 0000000..57e203b --- /dev/null +++ b/includes/Date/TimeZone.php @@ -0,0 +1,3632 @@ + | +// | | +// +----------------------------------------------------------------------+ +// +// $Id$ +// +// Date_TimeZone Class +// +/** + * TimeZone representation class, along with time zone information data. + * + * TimeZone representation class, along with time zone information data. + * The default timezone is set from the first valid timezone id found + * in one of the following places, in this order:
+ * 1) global $_DATE_TIMEZONE_DEFAULT
+ * 2) system environment variable PHP_TZ
+ * 3) system environment variable TZ
+ * 4) the result of date('T')
+ * If no valid timezone id is found, the default timezone is set to 'UTC'. + * You may also manually set the default timezone by passing a valid id to + * Date_TimeZone::setDefault().
+ * + * This class includes time zone data (from zoneinfo) in the form of a global array, $_DATE_TIMEZONE_DATA. + * + * + * @author Baba Buehler + * @package Date + * @access public + * @version 1.0 + */ +class Date_TimeZone +{ + /** + * Time Zone ID of this time zone + * @var string + */ + var $id; + /** + * Long Name of this time zone (ie Central Standard Time) + * @var string + */ + var $longname; + /** + * Short Name of this time zone (ie CST) + * @var string + */ + var $shortname; + /** + * true if this time zone observes daylight savings time + * @var boolean + */ + var $hasdst; + /** + * DST Long Name of this time zone + * @var string + */ + var $dstlongname; + /** + * DST Short Name of this timezone + * @var string + */ + var $dstshortname; + /** + * offset, in milliseconds, of this timezone + * @var int + */ + var $offset; + /** + * System Default Time Zone + * @var object Date_TimeZone + */ + var $default; + /** + * Constructor + * + * Creates a new Date::TimeZone object, representing the time zone + * specified in $id. If the supplied ID is invalid, the created + * time zone is UTC. + * + * @access public + * @param string $id the time zone id + * @return object Date_TimeZone the new Date_TimeZone object + */ + function Date_TimeZone($id) + { + global $_DATE_TIMEZONE_DATA; + if(Date_TimeZone::is_validID($id)) { + $this->id = $id; + $this->longname = $_DATE_TIMEZONE_DATA[$id]['longname']; + $this->shortname = $_DATE_TIMEZONE_DATA[$id]['shortname']; + $this->offset = $_DATE_TIMEZONE_DATA[$id]['offset']; + if($_DATE_TIMEZONE_DATA[$id]['hasdst']) { + $this->hasdst = true; + $this->dstlongname = $_DATE_TIMEZONE_DATA[$id]['dstlongname']; + $this->dstshortname = $_DATE_TIMEZONE_DATA[$id]['dstshortname']; + } else { + $this->hasdst = false; + $this->dstlongname = $this->longname; + $this->dstshortname = $this->shortname; + } + } else { + $this->id = 'UTC'; + $this->longname = $_DATE_TIMEZONE_DATA[$this->id]['longname']; + $this->shortname = $_DATE_TIMEZONE_DATA[$this->id]['shortname']; + $this->hasdst = $_DATE_TIMEZONE_DATA[$this->id]['hasdst']; + $this->offset = $_DATE_TIMEZONE_DATA[$this->id]['offset']; + } + } + /** + * Return a TimeZone object representing the system default time zone + * + * Return a TimeZone object representing the system default time zone, + * which is initialized during the loading of TimeZone.php. + * + * @access public + * @return object Date_TimeZone the default time zone + */ + function getDefault() + { + global $default; + return new Date_TimeZone($default); + } + /** + * Sets the system default time zone to the time zone in $id + * + * Sets the system default time zone to the time zone in $id + * + * @access public + * @param string $id the time zone id to use + */ + function setDefault($id) + { + global $default; + if(Date_TimeZone::is_validID($id)) { + $default = $id; + } + } + /** + * Tests if given id is represented in the $_DATE_TIMEZONE_DATA time zone data + * + * Tests if given id is represented in the $_DATE_TIMEZONE_DATA time zone data + * + * @access public + * @param string $id the id to test + * @return boolean true if the supplied ID is valid + */ + function is_validID($id) + { + global $_DATE_TIMEZONE_DATA; + if(isset($_DATE_TIMEZONE_DATA[$id])) { + return true; + } else { + return false; + } + } + /** + * Is this time zone equal to another + * + * Tests to see if this time zone is equal (ids match) + * to a given Date_TimeZone object. + * + * @access public + * @param object Date_TimeZone $tz the timezone to test + * @return boolean true if this time zone is equal to the supplied time zone + */ + function isEqual($tz) + { + if(strcasecmp($this->id, $tz->id) == 0) { + return true; + } else { + return false; + } + } + /** + * Is this time zone equivalent to another + * + * Tests to see if this time zone is equivalent to + * a given time zone object. Equivalence in this context + * is defined by the two time zones having an equal raw + * offset and an equal setting of "hasdst". This is not true + * equivalence, as the two time zones may have different rules + * for the observance of DST, but this implementation does not + * know DST rules. + * + * @access public + * @param object Date_TimeZone $tz the timezone object to test + * @return boolean true if this time zone is equivalent to the supplied time zone + */ + function isEquivalent($tz) + { + if($this->offset == $tz->offset && $this->hasdst == $tz->hasdst) { + return true; + } else { + return false; + } + } + /** + * Returns true if this zone observes daylight savings time + * + * Returns true if this zone observes daylight savings time + * + * @access public + * @return boolean true if this time zone has DST + */ + function hasDaylightTime() + { + return $this->hasdst; + } + /** + * Is the given date/time in DST for this time zone + * + * Attempts to determine if a given Date object represents a date/time + * that is in DST for this time zone. WARNINGS: this basically attempts to + * "trick" the system into telling us if we're in DST for a given time zone. + * This uses putenv() which may not work in safe mode, and relies on unix time + * which is only valid for dates from 1970 to ~2038. This relies on the + * underlying OS calls, so it may not work on Windows or on a system where + * zoneinfo is not installed or configured properly. + * + * @access public + * @param object Date $date the date/time to test + * @return boolean true if this date is in DST for this time zone + */ + function inDaylightTime($date) + { + $env_tz = ""; + if(getenv("TZ")) { + $env_tz = getenv("TZ"); + } + putenv("TZ=".$this->id); + $ltime = localtime($date->getTime(), true); + putenv("TZ=".$env_tz); + return $ltime['tm_isdst']; + } + /** + * Get the DST offset for this time zone + * + * Returns the DST offset of this time zone, in milliseconds, + * if the zone observes DST, zero otherwise. Currently the + * DST offset is hard-coded to one hour. + * + * @access public + * @return int the DST offset, in milliseconds or zero if the zone does not observe DST + */ + function getDSTSavings() + { + if($this->hasdst) { + return 3600000; + } else { + return 0; + } + } + /** + * Get the DST-corrected offset to UTC for the given date + * + * Attempts to get the offset to UTC for a given date/time, taking into + * account daylight savings time, if the time zone observes it and if + * it is in effect. Please see the WARNINGS on Date::TimeZone::inDaylightTime(). + * + * + * @access public + * @param object Date $date the Date to test + * @return int the corrected offset to UTC in milliseconds + */ + function getOffset($date) + { + if($this->inDaylightTime($date)) { + return $this->offset + $this->getDSTSavings(); + } else { + return $this->offset; + } + } + /** + * Returns the list of valid time zone id strings + * + * Returns the list of valid time zone id strings + * + * @access public + * @return mixed an array of strings with the valid time zone IDs + */ + function getAvailableIDs() + { + global $_DATE_TIMEZONE_DATA; + return array_keys($_DATE_TIMEZONE_DATA); + } + /** + * Returns the id for this time zone + * + * Returns the time zone id for this time zone, i.e. "America/Chicago" + * + * @access public + * @return string the id + */ + function getID() + { + return $this->id; + } + /** + * Returns the long name for this time zone + * + * Returns the long name for this time zone, + * i.e. "Central Standard Time" + * + * @access public + * @return string the long name + */ + function getLongName() + { + return $this->longname; + } + /** + * Returns the short name for this time zone + * + * Returns the short name for this time zone, i.e. "CST" + * + * @access public + * @return string the short name + */ + function getShortName() + { + return $this->shortname; + } + /** + * Returns the DST long name for this time zone + * + * Returns the DST long name for this time zone, i.e. "Central Daylight Time" + * + * @access public + * @return string the daylight savings time long name + */ + function getDSTLongName() + { + return $this->dstlongname; + } + /** + * Returns the DST short name for this time zone + * + * Returns the DST short name for this time zone, i.e. "CDT" + * + * @access public + * @return string the daylight savings time short name + */ + function getDSTShortName() + { + return $this->dstshortname; + } + /** + * Returns the raw (non-DST-corrected) offset from UTC/GMT for this time zone + * + * Returns the raw (non-DST-corrected) offset from UTC/GMT for this time zone + * + * @access public + * @return int the offset, in milliseconds + */ + function getRawOffset() + { + return $this->offset; + } +} // Date_TimeZone +// +// Time Zone Data +// offset is in miliseconds +// +$GLOBALS['_DATE_TIMEZONE_DATA'] = array( + 'Etc/GMT+12' => array( + 'offset' => -43200000, + 'longname' => "GMT-12:00", + 'shortname' => 'GMT-12:00', + 'hasdst' => false ), + 'Etc/GMT+11' => array( + 'offset' => -39600000, + 'longname' => "GMT-11:00", + 'shortname' => 'GMT-11:00', + 'hasdst' => false ), + 'MIT' => array( + 'offset' => -39600000, + 'longname' => "West Samoa Time", + 'shortname' => 'WST', + 'hasdst' => false ), + 'Pacific/Apia' => array( + 'offset' => -39600000, + 'longname' => "West Samoa Time", + 'shortname' => 'WST', + 'hasdst' => false ), + 'Pacific/Midway' => array( + 'offset' => -39600000, + 'longname' => "Samoa Standard Time", + 'shortname' => 'SST', + 'hasdst' => false ), + 'Pacific/Niue' => array( + 'offset' => -39600000, + 'longname' => "Niue Time", + 'shortname' => 'NUT', + 'hasdst' => false ), + 'Pacific/Pago_Pago' => array( + 'offset' => -39600000, + 'longname' => "Samoa Standard Time", + 'shortname' => 'SST', + 'hasdst' => false ), + 'Pacific/Samoa' => array( + 'offset' => -39600000, + 'longname' => "Samoa Standard Time", + 'shortname' => 'SST', + 'hasdst' => false ), + 'US/Samoa' => array( + 'offset' => -39600000, + 'longname' => "Samoa Standard Time", + 'shortname' => 'SST', + 'hasdst' => false ), + 'America/Adak' => array( + 'offset' => -36000000, + 'longname' => "Hawaii-Aleutian Standard Time", + 'shortname' => 'HAST', + 'hasdst' => true, + 'dstlongname' => "Hawaii-Aleutian Daylight Time", + 'dstshortname' => 'HADT' ), + 'America/Atka' => array( + 'offset' => -36000000, + 'longname' => "Hawaii-Aleutian Standard Time", + 'shortname' => 'HAST', + 'hasdst' => true, + 'dstlongname' => "Hawaii-Aleutian Daylight Time", + 'dstshortname' => 'HADT' ), + 'Etc/GMT+10' => array( + 'offset' => -36000000, + 'longname' => "GMT-10:00", + 'shortname' => 'GMT-10:00', + 'hasdst' => false ), + 'HST' => array( + 'offset' => -36000000, + 'longname' => "Hawaii Standard Time", + 'shortname' => 'HST', + 'hasdst' => false ), + 'Pacific/Fakaofo' => array( + 'offset' => -36000000, + 'longname' => "Tokelau Time", + 'shortname' => 'TKT', + 'hasdst' => false ), + 'Pacific/Honolulu' => array( + 'offset' => -36000000, + 'longname' => "Hawaii Standard Time", + 'shortname' => 'HST', + 'hasdst' => false ), + 'Pacific/Johnston' => array( + 'offset' => -36000000, + 'longname' => "Hawaii Standard Time", + 'shortname' => 'HST', + 'hasdst' => false ), + 'Pacific/Rarotonga' => array( + 'offset' => -36000000, + 'longname' => "Cook Is. Time", + 'shortname' => 'CKT', + 'hasdst' => false ), + 'Pacific/Tahiti' => array( + 'offset' => -36000000, + 'longname' => "Tahiti Time", + 'shortname' => 'TAHT', + 'hasdst' => false ), + 'SystemV/HST10' => array( + 'offset' => -36000000, + 'longname' => "Hawaii Standard Time", + 'shortname' => 'HST', + 'hasdst' => false ), + 'US/Aleutian' => array( + 'offset' => -36000000, + 'longname' => "Hawaii-Aleutian Standard Time", + 'shortname' => 'HAST', + 'hasdst' => true, + 'dstlongname' => "Hawaii-Aleutian Daylight Time", + 'dstshortname' => 'HADT' ), + 'US/Hawaii' => array( + 'offset' => -36000000, + 'longname' => "Hawaii Standard Time", + 'shortname' => 'HST', + 'hasdst' => false ), + 'Pacific/Marquesas' => array( + 'offset' => -34200000, + 'longname' => "Marquesas Time", + 'shortname' => 'MART', + 'hasdst' => false ), + 'AST' => array( + 'offset' => -32400000, + 'longname' => "Alaska Standard Time", + 'shortname' => 'AKST', + 'hasdst' => true, + 'dstlongname' => "Alaska Daylight Time", + 'dstshortname' => 'AKDT' ), + 'America/Anchorage' => array( + 'offset' => -32400000, + 'longname' => "Alaska Standard Time", + 'shortname' => 'AKST', + 'hasdst' => true, + 'dstlongname' => "Alaska Daylight Time", + 'dstshortname' => 'AKDT' ), + 'America/Juneau' => array( + 'offset' => -32400000, + 'longname' => "Alaska Standard Time", + 'shortname' => 'AKST', + 'hasdst' => true, + 'dstlongname' => "Alaska Daylight Time", + 'dstshortname' => 'AKDT' ), + 'America/Nome' => array( + 'offset' => -32400000, + 'longname' => "Alaska Standard Time", + 'shortname' => 'AKST', + 'hasdst' => true, + 'dstlongname' => "Alaska Daylight Time", + 'dstshortname' => 'AKDT' ), + 'America/Yakutat' => array( + 'offset' => -32400000, + 'longname' => "Alaska Standard Time", + 'shortname' => 'AKST', + 'hasdst' => true, + 'dstlongname' => "Alaska Daylight Time", + 'dstshortname' => 'AKDT' ), + 'Etc/GMT+9' => array( + 'offset' => -32400000, + 'longname' => "GMT-09:00", + 'shortname' => 'GMT-09:00', + 'hasdst' => false ), + 'Pacific/Gambier' => array( + 'offset' => -32400000, + 'longname' => "Gambier Time", + 'shortname' => 'GAMT', + 'hasdst' => false ), + 'SystemV/YST9' => array( + 'offset' => -32400000, + 'longname' => "Gambier Time", + 'shortname' => 'GAMT', + 'hasdst' => false ), + 'SystemV/YST9YDT' => array( + 'offset' => -32400000, + 'longname' => "Alaska Standard Time", + 'shortname' => 'AKST', + 'hasdst' => true, + 'dstlongname' => "Alaska Daylight Time", + 'dstshortname' => 'AKDT' ), + 'US/Alaska' => array( + 'offset' => -32400000, + 'longname' => "Alaska Standard Time", + 'shortname' => 'AKST', + 'hasdst' => true, + 'dstlongname' => "Alaska Daylight Time", + 'dstshortname' => 'AKDT' ), + 'America/Dawson' => array( + 'offset' => -28800000, + 'longname' => "Pacific Standard Time", + 'shortname' => 'PST', + 'hasdst' => true, + 'dstlongname' => "Pacific Daylight Time", + 'dstshortname' => 'PDT' ), + 'America/Ensenada' => array( + 'offset' => -28800000, + 'longname' => "Pacific Standard Time", + 'shortname' => 'PST', + 'hasdst' => true, + 'dstlongname' => "Pacific Daylight Time", + 'dstshortname' => 'PDT' ), + 'America/Los_Angeles' => array( + 'offset' => -28800000, + 'longname' => "Pacific Standard Time", + 'shortname' => 'PST', + 'hasdst' => true, + 'dstlongname' => "Pacific Daylight Time", + 'dstshortname' => 'PDT' ), + 'America/Tijuana' => array( + 'offset' => -28800000, + 'longname' => "Pacific Standard Time", + 'shortname' => 'PST', + 'hasdst' => true, + 'dstlongname' => "Pacific Daylight Time", + 'dstshortname' => 'PDT' ), + 'America/Vancouver' => array( + 'offset' => -28800000, + 'longname' => "Pacific Standard Time", + 'shortname' => 'PST', + 'hasdst' => true, + 'dstlongname' => "Pacific Daylight Time", + 'dstshortname' => 'PDT' ), + 'America/Whitehorse' => array( + 'offset' => -28800000, + 'longname' => "Pacific Standard Time", + 'shortname' => 'PST', + 'hasdst' => true, + 'dstlongname' => "Pacific Daylight Time", + 'dstshortname' => 'PDT' ), + 'Canada/Pacific' => array( + 'offset' => -28800000, + 'longname' => "Pacific Standard Time", + 'shortname' => 'PST', + 'hasdst' => true, + 'dstlongname' => "Pacific Daylight Time", + 'dstshortname' => 'PDT' ), + 'Canada/Yukon' => array( + 'offset' => -28800000, + 'longname' => "Pacific Standard Time", + 'shortname' => 'PST', + 'hasdst' => true, + 'dstlongname' => "Pacific Daylight Time", + 'dstshortname' => 'PDT' ), + 'Etc/GMT+8' => array( + 'offset' => -28800000, + 'longname' => "GMT-08:00", + 'shortname' => 'GMT-08:00', + 'hasdst' => false ), + 'Mexico/BajaNorte' => array( + 'offset' => -28800000, + 'longname' => "Pacific Standard Time", + 'shortname' => 'PST', + 'hasdst' => true, + 'dstlongname' => "Pacific Daylight Time", + 'dstshortname' => 'PDT' ), + 'PST' => array( + 'offset' => -28800000, + 'longname' => "Pacific Standard Time", + 'shortname' => 'PST', + 'hasdst' => true, + 'dstlongname' => "Pacific Daylight Time", + 'dstshortname' => 'PDT' ), + 'PST8PDT' => array( + 'offset' => -28800000, + 'longname' => "Pacific Standard Time", + 'shortname' => 'PST', + 'hasdst' => true, + 'dstlongname' => "Pacific Daylight Time", + 'dstshortname' => 'PDT' ), + 'Pacific/Pitcairn' => array( + 'offset' => -28800000, + 'longname' => "Pitcairn Standard Time", + 'shortname' => 'PST', + 'hasdst' => false ), + 'SystemV/PST8' => array( + 'offset' => -28800000, + 'longname' => "Pitcairn Standard Time", + 'shortname' => 'PST', + 'hasdst' => false ), + 'SystemV/PST8PDT' => array( + 'offset' => -28800000, + 'longname' => "Pacific Standard Time", + 'shortname' => 'PST', + 'hasdst' => true, + 'dstlongname' => "Pacific Daylight Time", + 'dstshortname' => 'PDT' ), + 'US/Pacific' => array( + 'offset' => -28800000, + 'longname' => "Pacific Standard Time", + 'shortname' => 'PST', + 'hasdst' => true, + 'dstlongname' => "Pacific Daylight Time", + 'dstshortname' => 'PDT' ), + 'US/Pacific-New' => array( + 'offset' => -28800000, + 'longname' => "Pacific Standard Time", + 'shortname' => 'PST', + 'hasdst' => true, + 'dstlongname' => "Pacific Daylight Time", + 'dstshortname' => 'PDT' ), + 'America/Boise' => array( + 'offset' => -25200000, + 'longname' => "Mountain Standard Time", + 'shortname' => 'MST', + 'hasdst' => true, + 'dstlongname' => "Mountain Daylight Time", + 'dstshortname' => 'MDT' ), + 'America/Cambridge_Bay' => array( + 'offset' => -25200000, + 'longname' => "Mountain Standard Time", + 'shortname' => 'MST', + 'hasdst' => true, + 'dstlongname' => "Mountain Daylight Time", + 'dstshortname' => 'MDT' ), + 'America/Chihuahua' => array( + 'offset' => -25200000, + 'longname' => "Mountain Standard Time", + 'shortname' => 'MST', + 'hasdst' => true, + 'dstlongname' => "Mountain Daylight Time", + 'dstshortname' => 'MDT' ), + 'America/Dawson_Creek' => array( + 'offset' => -25200000, + 'longname' => "Mountain Standard Time", + 'shortname' => 'MST', + 'hasdst' => false ), + 'America/Denver' => array( + 'offset' => -25200000, + 'longname' => "Mountain Standard Time", + 'shortname' => 'MST', + 'hasdst' => true, + 'dstlongname' => "Mountain Daylight Time", + 'dstshortname' => 'MDT' ), + 'America/Edmonton' => array( + 'offset' => -25200000, + 'longname' => "Mountain Standard Time", + 'shortname' => 'MST', + 'hasdst' => true, + 'dstlongname' => "Mountain Daylight Time", + 'dstshortname' => 'MDT' ), + 'America/Hermosillo' => array( + 'offset' => -25200000, + 'longname' => "Mountain Standard Time", + 'shortname' => 'MST', + 'hasdst' => false ), + 'America/Inuvik' => array( + 'offset' => -25200000, + 'longname' => "Mountain Standard Time", + 'shortname' => 'MST', + 'hasdst' => true, + 'dstlongname' => "Mountain Daylight Time", + 'dstshortname' => 'MDT' ), + 'America/Mazatlan' => array( + 'offset' => -25200000, + 'longname' => "Mountain Standard Time", + 'shortname' => 'MST', + 'hasdst' => true, + 'dstlongname' => "Mountain Daylight Time", + 'dstshortname' => 'MDT' ), + 'America/Phoenix' => array( + 'offset' => -25200000, + 'longname' => "Mountain Standard Time", + 'shortname' => 'MST', + 'hasdst' => false ), + 'America/Shiprock' => array( + 'offset' => -25200000, + 'longname' => "Mountain Standard Time", + 'shortname' => 'MST', + 'hasdst' => true, + 'dstlongname' => "Mountain Daylight Time", + 'dstshortname' => 'MDT' ), + 'America/Yellowknife' => array( + 'offset' => -25200000, + 'longname' => "Mountain Standard Time", + 'shortname' => 'MST', + 'hasdst' => true, + 'dstlongname' => "Mountain Daylight Time", + 'dstshortname' => 'MDT' ), + 'Canada/Mountain' => array( + 'offset' => -25200000, + 'longname' => "Mountain Standard Time", + 'shortname' => 'MST', + 'hasdst' => true, + 'dstlongname' => "Mountain Daylight Time", + 'dstshortname' => 'MDT' ), + 'Etc/GMT+7' => array( + 'offset' => -25200000, + 'longname' => "GMT-07:00", + 'shortname' => 'GMT-07:00', + 'hasdst' => false ), + 'MST' => array( + 'offset' => -25200000, + 'longname' => "Mountain Standard Time", + 'shortname' => 'MST', + 'hasdst' => true, + 'dstlongname' => "Mountain Daylight Time", + 'dstshortname' => 'MDT' ), + 'MST7MDT' => array( + 'offset' => -25200000, + 'longname' => "Mountain Standard Time", + 'shortname' => 'MST', + 'hasdst' => true, + 'dstlongname' => "Mountain Daylight Time", + 'dstshortname' => 'MDT' ), + 'Mexico/BajaSur' => array( + 'offset' => -25200000, + 'longname' => "Mountain Standard Time", + 'shortname' => 'MST', + 'hasdst' => true, + 'dstlongname' => "Mountain Daylight Time", + 'dstshortname' => 'MDT' ), + 'Navajo' => array( + 'offset' => -25200000, + 'longname' => "Mountain Standard Time", + 'shortname' => 'MST', + 'hasdst' => true, + 'dstlongname' => "Mountain Daylight Time", + 'dstshortname' => 'MDT' ), + 'PNT' => array( + 'offset' => -25200000, + 'longname' => "Mountain Standard Time", + 'shortname' => 'MST', + 'hasdst' => false ), + 'SystemV/MST7' => array( + 'offset' => -25200000, + 'longname' => "Mountain Standard Time", + 'shortname' => 'MST', + 'hasdst' => false ), + 'SystemV/MST7MDT' => array( + 'offset' => -25200000, + 'longname' => "Mountain Standard Time", + 'shortname' => 'MST', + 'hasdst' => true, + 'dstlongname' => "Mountain Daylight Time", + 'dstshortname' => 'MDT' ), + 'US/Arizona' => array( + 'offset' => -25200000, + 'longname' => "Mountain Standard Time", + 'shortname' => 'MST', + 'hasdst' => false ), + 'US/Mountain' => array( + 'offset' => -25200000, + 'longname' => "Mountain Standard Time", + 'shortname' => 'MST', + 'hasdst' => true, + 'dstlongname' => "Mountain Daylight Time", + 'dstshortname' => 'MDT' ), + 'America/Belize' => array( + 'offset' => -21600000, + 'longname' => "Central Standard Time", + 'shortname' => 'CST', + 'hasdst' => false ), + 'America/Cancun' => array( + 'offset' => -21600000, + 'longname' => "Central Standard Time", + 'shortname' => 'CST', + 'hasdst' => true, + 'dstlongname' => "Central Daylight Time", + 'dstshortname' => 'CDT' ), + 'America/Chicago' => array( + 'offset' => -21600000, + 'longname' => "Central Standard Time", + 'shortname' => 'CST', + 'hasdst' => true, + 'dstlongname' => "Central Daylight Time", + 'dstshortname' => 'CDT' ), + 'America/Costa_Rica' => array( + 'offset' => -21600000, + 'longname' => "Central Standard Time", + 'shortname' => 'CST', + 'hasdst' => false ), + 'America/El_Salvador' => array( + 'offset' => -21600000, + 'longname' => "Central Standard Time", + 'shortname' => 'CST', + 'hasdst' => false ), + 'America/Guatemala' => array( + 'offset' => -21600000, + 'longname' => "Central Standard Time", + 'shortname' => 'CST', + 'hasdst' => false ), + 'America/Managua' => array( + 'offset' => -21600000, + 'longname' => "Central Standard Time", + 'shortname' => 'CST', + 'hasdst' => false ), + 'America/Menominee' => array( + 'offset' => -21600000, + 'longname' => "Central Standard Time", + 'shortname' => 'CST', + 'hasdst' => true, + 'dstlongname' => "Central Daylight Time", + 'dstshortname' => 'CDT' ), + 'America/Merida' => array( + 'offset' => -21600000, + 'longname' => "Central Standard Time", + 'shortname' => 'CST', + 'hasdst' => true, + 'dstlongname' => "Central Daylight Time", + 'dstshortname' => 'CDT' ), + 'America/Mexico_City' => array( + 'offset' => -21600000, + 'longname' => "Central Standard Time", + 'shortname' => 'CST', + 'hasdst' => false ), + 'America/Monterrey' => array( + 'offset' => -21600000, + 'longname' => "Central Standard Time", + 'shortname' => 'CST', + 'hasdst' => true, + 'dstlongname' => "Central Daylight Time", + 'dstshortname' => 'CDT' ), + 'America/North_Dakota/Center' => array( + 'offset' => -21600000, + 'longname' => "Central Standard Time", + 'shortname' => 'CST', + 'hasdst' => true, + 'dstlongname' => "Central Daylight Time", + 'dstshortname' => 'CDT' ), + 'America/Rainy_River' => array( + 'offset' => -21600000, + 'longname' => "Central Standard Time", + 'shortname' => 'CST', + 'hasdst' => true, + 'dstlongname' => "Central Daylight Time", + 'dstshortname' => 'CDT' ), + 'America/Rankin_Inlet' => array( + 'offset' => -21600000, + 'longname' => "Eastern Standard Time", + 'shortname' => 'EST', + 'hasdst' => true, + 'dstlongname' => "Eastern Daylight Time", + 'dstshortname' => 'EDT' ), + 'America/Regina' => array( + 'offset' => -21600000, + 'longname' => "Central Standard Time", + 'shortname' => 'CST', + 'hasdst' => false ), + 'America/Swift_Current' => array( + 'offset' => -21600000, + 'longname' => "Central Standard Time", + 'shortname' => 'CST', + 'hasdst' => false ), + 'America/Tegucigalpa' => array( + 'offset' => -21600000, + 'longname' => "Central Standard Time", + 'shortname' => 'CST', + 'hasdst' => false ), + 'America/Winnipeg' => array( + 'offset' => -21600000, + 'longname' => "Central Standard Time", + 'shortname' => 'CST', + 'hasdst' => true, + 'dstlongname' => "Central Daylight Time", + 'dstshortname' => 'CDT' ), + 'CST' => array( + 'offset' => -21600000, + 'longname' => "Central Standard Time", + 'shortname' => 'CST', + 'hasdst' => true, + 'dstlongname' => "Central Daylight Time", + 'dstshortname' => 'CDT' ), + 'CST6CDT' => array( + 'offset' => -21600000, + 'longname' => "Central Standard Time", + 'shortname' => 'CST', + 'hasdst' => true, + 'dstlongname' => "Central Daylight Time", + 'dstshortname' => 'CDT' ), + 'Canada/Central' => array( + 'offset' => -21600000, + 'longname' => "Central Standard Time", + 'shortname' => 'CST', + 'hasdst' => true, + 'dstlongname' => "Central Daylight Time", + 'dstshortname' => 'CDT' ), + 'Canada/East-Saskatchewan' => array( + 'offset' => -21600000, + 'longname' => "Central Standard Time", + 'shortname' => 'CST', + 'hasdst' => false ), + 'Canada/Saskatchewan' => array( + 'offset' => -21600000, + 'longname' => "Central Standard Time", + 'shortname' => 'CST', + 'hasdst' => false ), + 'Chile/EasterIsland' => array( + 'offset' => -21600000, + 'longname' => "Easter Is. Time", + 'shortname' => 'EAST', + 'hasdst' => true, + 'dstlongname' => "Easter Is. Summer Time", + 'dstshortname' => 'EASST' ), + 'Etc/GMT+6' => array( + 'offset' => -21600000, + 'longname' => "GMT-06:00", + 'shortname' => 'GMT-06:00', + 'hasdst' => false ), + 'Mexico/General' => array( + 'offset' => -21600000, + 'longname' => "Central Standard Time", + 'shortname' => 'CST', + 'hasdst' => false ), + 'Pacific/Easter' => array( + 'offset' => -21600000, + 'longname' => "Easter Is. Time", + 'shortname' => 'EAST', + 'hasdst' => true, + 'dstlongname' => "Easter Is. Summer Time", + 'dstshortname' => 'EASST' ), + 'Pacific/Galapagos' => array( + 'offset' => -21600000, + 'longname' => "Galapagos Time", + 'shortname' => 'GALT', + 'hasdst' => false ), + 'SystemV/CST6' => array( + 'offset' => -21600000, + 'longname' => "Central Standard Time", + 'shortname' => 'CST', + 'hasdst' => false ), + 'SystemV/CST6CDT' => array( + 'offset' => -21600000, + 'longname' => "Central Standard Time", + 'shortname' => 'CST', + 'hasdst' => true, + 'dstlongname' => "Central Daylight Time", + 'dstshortname' => 'CDT' ), + 'US/Central' => array( + 'offset' => -21600000, + 'longname' => "Central Standard Time", + 'shortname' => 'CST', + 'hasdst' => true, + 'dstlongname' => "Central Daylight Time", + 'dstshortname' => 'CDT' ), + 'America/Bogota' => array( + 'offset' => -18000000, + 'longname' => "Colombia Time", + 'shortname' => 'COT', + 'hasdst' => false ), + 'America/Cayman' => array( + 'offset' => -18000000, + 'longname' => "Eastern Standard Time", + 'shortname' => 'EST', + 'hasdst' => false ), + 'America/Detroit' => array( + 'offset' => -18000000, + 'longname' => "Eastern Standard Time", + 'shortname' => 'EST', + 'hasdst' => true, + 'dstlongname' => "Eastern Daylight Time", + 'dstshortname' => 'EDT' ), + 'America/Eirunepe' => array( + 'offset' => -18000000, + 'longname' => "Acre Time", + 'shortname' => 'ACT', + 'hasdst' => false ), + 'America/Fort_Wayne' => array( + 'offset' => -18000000, + 'longname' => "Eastern Standard Time", + 'shortname' => 'EST', + 'hasdst' => false ), + 'America/Grand_Turk' => array( + 'offset' => -18000000, + 'longname' => "Eastern Standard Time", + 'shortname' => 'EST', + 'hasdst' => true, + 'dstlongname' => "Eastern Daylight Time", + 'dstshortname' => 'EDT' ), + 'America/Guayaquil' => array( + 'offset' => -18000000, + 'longname' => "Ecuador Time", + 'shortname' => 'ECT', + 'hasdst' => false ), + 'America/Havana' => array( + 'offset' => -18000000, + 'longname' => "Central Standard Time", + 'shortname' => 'CST', + 'hasdst' => true, + 'dstlongname' => "Central Daylight Time", + 'dstshortname' => 'CDT' ), + 'America/Indiana/Indianapolis' => array( + 'offset' => -18000000, + 'longname' => "Eastern Standard Time", + 'shortname' => 'EST', + 'hasdst' => false ), + 'America/Indiana/Knox' => array( + 'offset' => -18000000, + 'longname' => "Eastern Standard Time", + 'shortname' => 'EST', + 'hasdst' => false ), + 'America/Indiana/Marengo' => array( + 'offset' => -18000000, + 'longname' => "Eastern Standard Time", + 'shortname' => 'EST', + 'hasdst' => false ), + 'America/Indiana/Vevay' => array( + 'offset' => -18000000, + 'longname' => "Eastern Standard Time", + 'shortname' => 'EST', + 'hasdst' => false ), + 'America/Indianapolis' => array( + 'offset' => -18000000, + 'longname' => "Eastern Standard Time", + 'shortname' => 'EST', + 'hasdst' => false ), + 'America/Iqaluit' => array( + 'offset' => -18000000, + 'longname' => "Eastern Standard Time", + 'shortname' => 'EST', + 'hasdst' => true, + 'dstlongname' => "Eastern Daylight Time", + 'dstshortname' => 'EDT' ), + 'America/Jamaica' => array( + 'offset' => -18000000, + 'longname' => "Eastern Standard Time", + 'shortname' => 'EST', + 'hasdst' => false ), + 'America/Kentucky/Louisville' => array( + 'offset' => -18000000, + 'longname' => "Eastern Standard Time", + 'shortname' => 'EST', + 'hasdst' => true, + 'dstlongname' => "Eastern Daylight Time", + 'dstshortname' => 'EDT' ), + 'America/Kentucky/Monticello' => array( + 'offset' => -18000000, + 'longname' => "Eastern Standard Time", + 'shortname' => 'EST', + 'hasdst' => true, + 'dstlongname' => "Eastern Daylight Time", + 'dstshortname' => 'EDT' ), + 'America/Knox_IN' => array( + 'offset' => -18000000, + 'longname' => "Eastern Standard Time", + 'shortname' => 'EST', + 'hasdst' => false ), + 'America/Lima' => array( + 'offset' => -18000000, + 'longname' => "Peru Time", + 'shortname' => 'PET', + 'hasdst' => false ), + 'America/Louisville' => array( + 'offset' => -18000000, + 'longname' => "Eastern Standard Time", + 'shortname' => 'EST', + 'hasdst' => true, + 'dstlongname' => "Eastern Daylight Time", + 'dstshortname' => 'EDT' ), + 'America/Montreal' => array( + 'offset' => -18000000, + 'longname' => "Eastern Standard Time", + 'shortname' => 'EST', + 'hasdst' => true, + 'dstlongname' => "Eastern Daylight Time", + 'dstshortname' => 'EDT' ), + 'America/Nassau' => array( + 'offset' => -18000000, + 'longname' => "Eastern Standard Time", + 'shortname' => 'EST', + 'hasdst' => true, + 'dstlongname' => "Eastern Daylight Time", + 'dstshortname' => 'EDT' ), + 'America/New_York' => array( + 'offset' => -18000000, + 'longname' => "Eastern Standard Time", + 'shortname' => 'EST', + 'hasdst' => true, + 'dstlongname' => "Eastern Daylight Time", + 'dstshortname' => 'EDT' ), + 'America/Nipigon' => array( + 'offset' => -18000000, + 'longname' => "Eastern Standard Time", + 'shortname' => 'EST', + 'hasdst' => true, + 'dstlongname' => "Eastern Daylight Time", + 'dstshortname' => 'EDT' ), + 'America/Panama' => array( + 'offset' => -18000000, + 'longname' => "Eastern Standard Time", + 'shortname' => 'EST', + 'hasdst' => false ), + 'America/Pangnirtung' => array( + 'offset' => -18000000, + 'longname' => "Eastern Standard Time", + 'shortname' => 'EST', + 'hasdst' => true, + 'dstlongname' => "Eastern Daylight Time", + 'dstshortname' => 'EDT' ), + 'America/Port-au-Prince' => array( + 'offset' => -18000000, + 'longname' => "Eastern Standard Time", + 'shortname' => 'EST', + 'hasdst' => false ), + 'America/Porto_Acre' => array( + 'offset' => -18000000, + 'longname' => "Acre Time", + 'shortname' => 'ACT', + 'hasdst' => false ), + 'America/Rio_Branco' => array( + 'offset' => -18000000, + 'longname' => "Acre Time", + 'shortname' => 'ACT', + 'hasdst' => false ), + 'America/Thunder_Bay' => array( + 'offset' => -18000000, + 'longname' => "Eastern Standard Time", + 'shortname' => 'EST', + 'hasdst' => true, + 'dstlongname' => "Eastern Daylight Time", + 'dstshortname' => 'EDT' ), + 'Brazil/Acre' => array( + 'offset' => -18000000, + 'longname' => "Acre Time", + 'shortname' => 'ACT', + 'hasdst' => false ), + 'Canada/Eastern' => array( + 'offset' => -18000000, + 'longname' => "Eastern Standard Time", + 'shortname' => 'EST', + 'hasdst' => true, + 'dstlongname' => "Eastern Daylight Time", + 'dstshortname' => 'EDT' ), + 'Cuba' => array( + 'offset' => -18000000, + 'longname' => "Central Standard Time", + 'shortname' => 'CST', + 'hasdst' => true, + 'dstlongname' => "Central Daylight Time", + 'dstshortname' => 'CDT' ), + 'EST' => array( + 'offset' => -18000000, + 'longname' => "Eastern Standard Time", + 'shortname' => 'EST', + 'hasdst' => true, + 'dstlongname' => "Eastern Daylight Time", + 'dstshortname' => 'EDT' ), + 'EST5EDT' => array( + 'offset' => -18000000, + 'longname' => "Eastern Standard Time", + 'shortname' => 'EST', + 'hasdst' => true, + 'dstlongname' => "Eastern Daylight Time", + 'dstshortname' => 'EDT' ), + 'EDT' => array( // Added by AWC + 'offset' => -14400000, // Added by AWC + 'longname' => "Eastern Daylight Time", // Added by AWC + 'shortname' => 'EDT', // Added by AWC + 'hasdst' => false ), // Added by AWC + 'Etc/GMT+5' => array( + 'offset' => -18000000, + 'longname' => "GMT-05:00", + 'shortname' => 'GMT-05:00', + 'hasdst' => false ), + 'IET' => array( + 'offset' => -18000000, + 'longname' => "Eastern Standard Time", + 'shortname' => 'EST', + 'hasdst' => false ), + 'Jamaica' => array( + 'offset' => -18000000, + 'longname' => "Eastern Standard Time", + 'shortname' => 'EST', + 'hasdst' => false ), + 'SystemV/EST5' => array( + 'offset' => -18000000, + 'longname' => "Eastern Standard Time", + 'shortname' => 'EST', + 'hasdst' => false ), + 'SystemV/EST5EDT' => array( + 'offset' => -18000000, + 'longname' => "Eastern Standard Time", + 'shortname' => 'EST', + 'hasdst' => true, + 'dstlongname' => "Eastern Daylight Time", + 'dstshortname' => 'EDT' ), + 'US/East-Indiana' => array( + 'offset' => -18000000, + 'longname' => "Eastern Standard Time", + 'shortname' => 'EST', + 'hasdst' => false ), + 'US/Eastern' => array( + 'offset' => -18000000, + 'longname' => "Eastern Standard Time", + 'shortname' => 'EST', + 'hasdst' => true, + 'dstlongname' => "Eastern Daylight Time", + 'dstshortname' => 'EDT' ), + 'US/Indiana-Starke' => array( + 'offset' => -18000000, + 'longname' => "Eastern Standard Time", + 'shortname' => 'EST', + 'hasdst' => false ), + 'US/Michigan' => array( + 'offset' => -18000000, + 'longname' => "Eastern Standard Time", + 'shortname' => 'EST', + 'hasdst' => true, + 'dstlongname' => "Eastern Daylight Time", + 'dstshortname' => 'EDT' ), + 'America/Anguilla' => array( + 'offset' => -14400000, + 'longname' => "Atlantic Standard Time", + 'shortname' => 'AST', + 'hasdst' => false ), + 'America/Antigua' => array( + 'offset' => -14400000, + 'longname' => "Atlantic Standard Time", + 'shortname' => 'AST', + 'hasdst' => false ), + 'America/Aruba' => array( + 'offset' => -14400000, + 'longname' => "Atlantic Standard Time", + 'shortname' => 'AST', + 'hasdst' => false ), + 'America/Asuncion' => array( + 'offset' => -14400000, + 'longname' => "Paraguay Time", + 'shortname' => 'PYT', + 'hasdst' => true, + 'dstlongname' => "Paraguay Summer Time", + 'dstshortname' => 'PYST' ), + 'America/Barbados' => array( + 'offset' => -14400000, + 'longname' => "Atlantic Standard Time", + 'shortname' => 'AST', + 'hasdst' => false ), + 'America/Boa_Vista' => array( + 'offset' => -14400000, + 'longname' => "Amazon Standard Time", + 'shortname' => 'AMT', + 'hasdst' => false ), + 'America/Caracas' => array( + 'offset' => -14400000, + 'longname' => "Venezuela Time", + 'shortname' => 'VET', + 'hasdst' => false ), + 'America/Cuiaba' => array( + 'offset' => -14400000, + 'longname' => "Amazon Standard Time", + 'shortname' => 'AMT', + 'hasdst' => true, + 'dstlongname' => "Amazon Summer Time", + 'dstshortname' => 'AMST' ), + 'America/Curacao' => array( + 'offset' => -14400000, + 'longname' => "Atlantic Standard Time", + 'shortname' => 'AST', + 'hasdst' => false ), + 'America/Dominica' => array( + 'offset' => -14400000, + 'longname' => "Atlantic Standard Time", + 'shortname' => 'AST', + 'hasdst' => false ), + 'America/Glace_Bay' => array( + 'offset' => -14400000, + 'longname' => "Atlantic Standard Time", + 'shortname' => 'AST', + 'hasdst' => true, + 'dstlongname' => "Atlantic Daylight Time", + 'dstshortname' => 'ADT' ), + 'America/Goose_Bay' => array( + 'offset' => -14400000, + 'longname' => "Atlantic Standard Time", + 'shortname' => 'AST', + 'hasdst' => true, + 'dstlongname' => "Atlantic Daylight Time", + 'dstshortname' => 'ADT' ), + 'America/Grenada' => array( + 'offset' => -14400000, + 'longname' => "Atlantic Standard Time", + 'shortname' => 'AST', + 'hasdst' => false ), + 'America/Guadeloupe' => array( + 'offset' => -14400000, + 'longname' => "Atlantic Standard Time", + 'shortname' => 'AST', + 'hasdst' => false ), + 'America/Guyana' => array( + 'offset' => -14400000, + 'longname' => "Guyana Time", + 'shortname' => 'GYT', + 'hasdst' => false ), + 'America/Halifax' => array( + 'offset' => -14400000, + 'longname' => "Atlantic Standard Time", + 'shortname' => 'AST', + 'hasdst' => true, + 'dstlongname' => "Atlantic Daylight Time", + 'dstshortname' => 'ADT' ), + 'America/La_Paz' => array( + 'offset' => -14400000, + 'longname' => "Bolivia Time", + 'shortname' => 'BOT', + 'hasdst' => false ), + 'America/Manaus' => array( + 'offset' => -14400000, + 'longname' => "Amazon Standard Time", + 'shortname' => 'AMT', + 'hasdst' => false ), + 'America/Martinique' => array( + 'offset' => -14400000, + 'longname' => "Atlantic Standard Time", + 'shortname' => 'AST', + 'hasdst' => false ), + 'America/Montserrat' => array( + 'offset' => -14400000, + 'longname' => "Atlantic Standard Time", + 'shortname' => 'AST', + 'hasdst' => false ), + 'America/Port_of_Spain' => array( + 'offset' => -14400000, + 'longname' => "Atlantic Standard Time", + 'shortname' => 'AST', + 'hasdst' => false ), + 'America/Porto_Velho' => array( + 'offset' => -14400000, + 'longname' => "Amazon Standard Time", + 'shortname' => 'AMT', + 'hasdst' => false ), + 'America/Puerto_Rico' => array( + 'offset' => -14400000, + 'longname' => "Atlantic Standard Time", + 'shortname' => 'AST', + 'hasdst' => false ), + 'America/Santiago' => array( + 'offset' => -14400000, + 'longname' => "Chile Time", + 'shortname' => 'CLT', + 'hasdst' => true, + 'dstlongname' => "Chile Summer Time", + 'dstshortname' => 'CLST' ), + 'America/Santo_Domingo' => array( + 'offset' => -14400000, + 'longname' => "Atlantic Standard Time", + 'shortname' => 'AST', + 'hasdst' => false ), + 'America/St_Kitts' => array( + 'offset' => -14400000, + 'longname' => "Atlantic Standard Time", + 'shortname' => 'AST', + 'hasdst' => false ), + 'America/St_Lucia' => array( + 'offset' => -14400000, + 'longname' => "Atlantic Standard Time", + 'shortname' => 'AST', + 'hasdst' => false ), + 'America/St_Thomas' => array( + 'offset' => -14400000, + 'longname' => "Atlantic Standard Time", + 'shortname' => 'AST', + 'hasdst' => false ), + 'America/St_Vincent' => array( + 'offset' => -14400000, + 'longname' => "Atlantic Standard Time", + 'shortname' => 'AST', + 'hasdst' => false ), + 'America/Thule' => array( + 'offset' => -14400000, + 'longname' => "Atlantic Standard Time", + 'shortname' => 'AST', + 'hasdst' => false ), + 'America/Tortola' => array( + 'offset' => -14400000, + 'longname' => "Atlantic Standard Time", + 'shortname' => 'AST', + 'hasdst' => false ), + 'America/Virgin' => array( + 'offset' => -14400000, + 'longname' => "Atlantic Standard Time", + 'shortname' => 'AST', + 'hasdst' => false ), + 'Antarctica/Palmer' => array( + 'offset' => -14400000, + 'longname' => "Chile Time", + 'shortname' => 'CLT', + 'hasdst' => true, + 'dstlongname' => "Chile Summer Time", + 'dstshortname' => 'CLST' ), + 'Atlantic/Bermuda' => array( + 'offset' => -14400000, + 'longname' => "Atlantic Standard Time", + 'shortname' => 'AST', + 'hasdst' => true, + 'dstlongname' => "Atlantic Daylight Time", + 'dstshortname' => 'ADT' ), + 'Atlantic/Stanley' => array( + 'offset' => -14400000, + 'longname' => "Falkland Is. Time", + 'shortname' => 'FKT', + 'hasdst' => true, + 'dstlongname' => "Falkland Is. Summer Time", + 'dstshortname' => 'FKST' ), + 'Brazil/West' => array( + 'offset' => -14400000, + 'longname' => "Amazon Standard Time", + 'shortname' => 'AMT', + 'hasdst' => false ), + 'Canada/Atlantic' => array( + 'offset' => -14400000, + 'longname' => "Atlantic Standard Time", + 'shortname' => 'AST', + 'hasdst' => true, + 'dstlongname' => "Atlantic Daylight Time", + 'dstshortname' => 'ADT' ), + 'Chile/Continental' => array( + 'offset' => -14400000, + 'longname' => "Chile Time", + 'shortname' => 'CLT', + 'hasdst' => true, + 'dstlongname' => "Chile Summer Time", + 'dstshortname' => 'CLST' ), + 'Etc/GMT+4' => array( + 'offset' => -14400000, + 'longname' => "GMT-04:00", + 'shortname' => 'GMT-04:00', + 'hasdst' => false ), + 'PRT' => array( + 'offset' => -14400000, + 'longname' => "Atlantic Standard Time", + 'shortname' => 'AST', + 'hasdst' => false ), + 'SystemV/AST4' => array( + 'offset' => -14400000, + 'longname' => "Atlantic Standard Time", + 'shortname' => 'AST', + 'hasdst' => false ), + 'SystemV/AST4ADT' => array( + 'offset' => -14400000, + 'longname' => "Atlantic Standard Time", + 'shortname' => 'AST', + 'hasdst' => true, + 'dstlongname' => "Atlantic Daylight Time", + 'dstshortname' => 'ADT' ), + 'America/St_Johns' => array( + 'offset' => -12600000, + 'longname' => "Newfoundland Standard Time", + 'shortname' => 'NST', + 'hasdst' => true, + 'dstlongname' => "Newfoundland Daylight Time", + 'dstshortname' => 'NDT' ), + 'CNT' => array( + 'offset' => -12600000, + 'longname' => "Newfoundland Standard Time", + 'shortname' => 'NST', + 'hasdst' => true, + 'dstlongname' => "Newfoundland Daylight Time", + 'dstshortname' => 'NDT' ), + 'Canada/Newfoundland' => array( + 'offset' => -12600000, + 'longname' => "Newfoundland Standard Time", + 'shortname' => 'NST', + 'hasdst' => true, + 'dstlongname' => "Newfoundland Daylight Time", + 'dstshortname' => 'NDT' ), + 'AGT' => array( + 'offset' => -10800000, + 'longname' => "Argentine Time", + 'shortname' => 'ART', + 'hasdst' => false ), + 'America/Araguaina' => array( + 'offset' => -10800000, + 'longname' => "Brazil Time", + 'shortname' => 'BRT', + 'hasdst' => true, + 'dstlongname' => "Brazil Summer Time", + 'dstshortname' => 'BRST' ), + 'America/Belem' => array( + 'offset' => -10800000, + 'longname' => "Brazil Time", + 'shortname' => 'BRT', + 'hasdst' => false ), + 'America/Buenos_Aires' => array( + 'offset' => -10800000, + 'longname' => "Argentine Time", + 'shortname' => 'ART', + 'hasdst' => false ), + 'America/Catamarca' => array( + 'offset' => -10800000, + 'longname' => "Argentine Time", + 'shortname' => 'ART', + 'hasdst' => false ), + 'America/Cayenne' => array( + 'offset' => -10800000, + 'longname' => "French Guiana Time", + 'shortname' => 'GFT', + 'hasdst' => false ), + 'America/Cordoba' => array( + 'offset' => -10800000, + 'longname' => "Argentine Time", + 'shortname' => 'ART', + 'hasdst' => false ), + 'America/Fortaleza' => array( + 'offset' => -10800000, + 'longname' => "Brazil Time", + 'shortname' => 'BRT', + 'hasdst' => true, + 'dstlongname' => "Brazil Summer Time", + 'dstshortname' => 'BRST' ), + 'America/Godthab' => array( + 'offset' => -10800000, + 'longname' => "Western Greenland Time", + 'shortname' => 'WGT', + 'hasdst' => true, + 'dstlongname' => "Western Greenland Summer Time", + 'dstshortname' => 'WGST' ), + 'America/Jujuy' => array( + 'offset' => -10800000, + 'longname' => "Argentine Time", + 'shortname' => 'ART', + 'hasdst' => false ), + 'America/Maceio' => array( + 'offset' => -10800000, + 'longname' => "Brazil Time", + 'shortname' => 'BRT', + 'hasdst' => true, + 'dstlongname' => "Brazil Summer Time", + 'dstshortname' => 'BRST' ), + 'America/Mendoza' => array( + 'offset' => -10800000, + 'longname' => "Argentine Time", + 'shortname' => 'ART', + 'hasdst' => false ), + 'America/Miquelon' => array( + 'offset' => -10800000, + 'longname' => "Pierre & Miquelon Standard Time", + 'shortname' => 'PMST', + 'hasdst' => true, + 'dstlongname' => "Pierre & Miquelon Daylight Time", + 'dstshortname' => 'PMDT' ), + 'America/Montevideo' => array( + 'offset' => -10800000, + 'longname' => "Uruguay Time", + 'shortname' => 'UYT', + 'hasdst' => false ), + 'America/Paramaribo' => array( + 'offset' => -10800000, + 'longname' => "Suriname Time", + 'shortname' => 'SRT', + 'hasdst' => false ), + 'America/Recife' => array( + 'offset' => -10800000, + 'longname' => "Brazil Time", + 'shortname' => 'BRT', + 'hasdst' => true, + 'dstlongname' => "Brazil Summer Time", + 'dstshortname' => 'BRST' ), + 'America/Rosario' => array( + 'offset' => -10800000, + 'longname' => "Argentine Time", + 'shortname' => 'ART', + 'hasdst' => false ), + 'America/Sao_Paulo' => array( + 'offset' => -10800000, + 'longname' => "Brazil Time", + 'shortname' => 'BRT', + 'hasdst' => true, + 'dstlongname' => "Brazil Summer Time", + 'dstshortname' => 'BRST' ), + 'BET' => array( + 'offset' => -10800000, + 'longname' => "Brazil Time", + 'shortname' => 'BRT', + 'hasdst' => true, + 'dstlongname' => "Brazil Summer Time", + 'dstshortname' => 'BRST' ), + 'Brazil/East' => array( + 'offset' => -10800000, + 'longname' => "Brazil Time", + 'shortname' => 'BRT', + 'hasdst' => true, + 'dstlongname' => "Brazil Summer Time", + 'dstshortname' => 'BRST' ), + 'Etc/GMT+3' => array( + 'offset' => -10800000, + 'longname' => "GMT-03:00", + 'shortname' => 'GMT-03:00', + 'hasdst' => false ), + 'America/Noronha' => array( + 'offset' => -7200000, + 'longname' => "Fernando de Noronha Time", + 'shortname' => 'FNT', + 'hasdst' => false ), + 'Atlantic/South_Georgia' => array( + 'offset' => -7200000, + 'longname' => "South Georgia Standard Time", + 'shortname' => 'GST', + 'hasdst' => false ), + 'Brazil/DeNoronha' => array( + 'offset' => -7200000, + 'longname' => "Fernando de Noronha Time", + 'shortname' => 'FNT', + 'hasdst' => false ), + 'Etc/GMT+2' => array( + 'offset' => -7200000, + 'longname' => "GMT-02:00", + 'shortname' => 'GMT-02:00', + 'hasdst' => false ), + 'America/Scoresbysund' => array( + 'offset' => -3600000, + 'longname' => "Eastern Greenland Time", + 'shortname' => 'EGT', + 'hasdst' => true, + 'dstlongname' => "Eastern Greenland Summer Time", + 'dstshortname' => 'EGST' ), + 'Atlantic/Azores' => array( + 'offset' => -3600000, + 'longname' => "Azores Time", + 'shortname' => 'AZOT', + 'hasdst' => true, + 'dstlongname' => "Azores Summer Time", + 'dstshortname' => 'AZOST' ), + 'Atlantic/Cape_Verde' => array( + 'offset' => -3600000, + 'longname' => "Cape Verde Time", + 'shortname' => 'CVT', + 'hasdst' => false ), + 'Etc/GMT+1' => array( + 'offset' => -3600000, + 'longname' => "GMT-01:00", + 'shortname' => 'GMT-01:00', + 'hasdst' => false ), + 'Africa/Abidjan' => array( + 'offset' => 0, + 'longname' => "Greenwich Mean Time", + 'shortname' => 'GMT', + 'hasdst' => false ), + 'Africa/Accra' => array( + 'offset' => 0, + 'longname' => "Greenwich Mean Time", + 'shortname' => 'GMT', + 'hasdst' => false ), + 'Africa/Bamako' => array( + 'offset' => 0, + 'longname' => "Greenwich Mean Time", + 'shortname' => 'GMT', + 'hasdst' => false ), + 'Africa/Banjul' => array( + 'offset' => 0, + 'longname' => "Greenwich Mean Time", + 'shortname' => 'GMT', + 'hasdst' => false ), + 'Africa/Bissau' => array( + 'offset' => 0, + 'longname' => "Greenwich Mean Time", + 'shortname' => 'GMT', + 'hasdst' => false ), + 'Africa/Casablanca' => array( + 'offset' => 0, + 'longname' => "Western European Time", + 'shortname' => 'WET', + 'hasdst' => false ), + 'Africa/Conakry' => array( + 'offset' => 0, + 'longname' => "Greenwich Mean Time", + 'shortname' => 'GMT', + 'hasdst' => false ), + 'Africa/Dakar' => array( + 'offset' => 0, + 'longname' => "Greenwich Mean Time", + 'shortname' => 'GMT', + 'hasdst' => false ), + 'Africa/El_Aaiun' => array( + 'offset' => 0, + 'longname' => "Western European Time", + 'shortname' => 'WET', + 'hasdst' => false ), + 'Africa/Freetown' => array( + 'offset' => 0, + 'longname' => "Greenwich Mean Time", + 'shortname' => 'GMT', + 'hasdst' => false ), + 'Africa/Lome' => array( + 'offset' => 0, + 'longname' => "Greenwich Mean Time", + 'shortname' => 'GMT', + 'hasdst' => false ), + 'Africa/Monrovia' => array( + 'offset' => 0, + 'longname' => "Greenwich Mean Time", + 'shortname' => 'GMT', + 'hasdst' => false ), + 'Africa/Nouakchott' => array( + 'offset' => 0, + 'longname' => "Greenwich Mean Time", + 'shortname' => 'GMT', + 'hasdst' => false ), + 'Africa/Ouagadougou' => array( + 'offset' => 0, + 'longname' => "Greenwich Mean Time", + 'shortname' => 'GMT', + 'hasdst' => false ), + 'Africa/Sao_Tome' => array( + 'offset' => 0, + 'longname' => "Greenwich Mean Time", + 'shortname' => 'GMT', + 'hasdst' => false ), + 'Africa/Timbuktu' => array( + 'offset' => 0, + 'longname' => "Greenwich Mean Time", + 'shortname' => 'GMT', + 'hasdst' => false ), + 'America/Danmarkshavn' => array( + 'offset' => 0, + 'longname' => "Greenwich Mean Time", + 'shortname' => 'GMT', + 'hasdst' => false ), + 'Atlantic/Canary' => array( + 'offset' => 0, + 'longname' => "Western European Time", + 'shortname' => 'WET', + 'hasdst' => true, + 'dstlongname' => "Western European Summer Time", + 'dstshortname' => 'WEST' ), + 'Atlantic/Faeroe' => array( + 'offset' => 0, + 'longname' => "Western European Time", + 'shortname' => 'WET', + 'hasdst' => true, + 'dstlongname' => "Western European Summer Time", + 'dstshortname' => 'WEST' ), + 'Atlantic/Madeira' => array( + 'offset' => 0, + 'longname' => "Western European Time", + 'shortname' => 'WET', + 'hasdst' => true, + 'dstlongname' => "Western European Summer Time", + 'dstshortname' => 'WEST' ), + 'Atlantic/Reykjavik' => array( + 'offset' => 0, + 'longname' => "Greenwich Mean Time", + 'shortname' => 'GMT', + 'hasdst' => false ), + 'Atlantic/St_Helena' => array( + 'offset' => 0, + 'longname' => "Greenwich Mean Time", + 'shortname' => 'GMT', + 'hasdst' => false ), + 'Eire' => array( + 'offset' => 0, + 'longname' => "Greenwich Mean Time", + 'shortname' => 'GMT', + 'hasdst' => true, + 'dstlongname' => "Irish Summer Time", + 'dstshortname' => 'IST' ), + 'Etc/GMT' => array( + 'offset' => 0, + 'longname' => "GMT+00:00", + 'shortname' => 'GMT+00:00', + 'hasdst' => false ), + 'Etc/GMT+0' => array( + 'offset' => 0, + 'longname' => "GMT+00:00", + 'shortname' => 'GMT+00:00', + 'hasdst' => false ), + 'Etc/GMT-0' => array( + 'offset' => 0, + 'longname' => "GMT+00:00", + 'shortname' => 'GMT+00:00', + 'hasdst' => false ), + 'Etc/GMT0' => array( + 'offset' => 0, + 'longname' => "GMT+00:00", + 'shortname' => 'GMT+00:00', + 'hasdst' => false ), + 'Etc/Greenwich' => array( + 'offset' => 0, + 'longname' => "Greenwich Mean Time", + 'shortname' => 'GMT', + 'hasdst' => false ), + 'Etc/UCT' => array( + 'offset' => 0, + 'longname' => "Coordinated Universal Time", + 'shortname' => 'UTC', + 'hasdst' => false ), + 'Etc/UTC' => array( + 'offset' => 0, + 'longname' => "Coordinated Universal Time", + 'shortname' => 'UTC', + 'hasdst' => false ), + 'Etc/Universal' => array( + 'offset' => 0, + 'longname' => "Coordinated Universal Time", + 'shortname' => 'UTC', + 'hasdst' => false ), + 'Etc/Zulu' => array( + 'offset' => 0, + 'longname' => "Coordinated Universal Time", + 'shortname' => 'UTC', + 'hasdst' => false ), + 'Europe/Belfast' => array( + 'offset' => 0, + 'longname' => "Greenwich Mean Time", + 'shortname' => 'GMT', + 'hasdst' => true, + 'dstlongname' => "British Summer Time", + 'dstshortname' => 'BST' ), + 'Europe/Dublin' => array( + 'offset' => 0, + 'longname' => "Greenwich Mean Time", + 'shortname' => 'GMT', + 'hasdst' => true, + 'dstlongname' => "Irish Summer Time", + 'dstshortname' => 'IST' ), + 'Europe/Lisbon' => array( + 'offset' => 0, + 'longname' => "Western European Time", + 'shortname' => 'WET', + 'hasdst' => true, + 'dstlongname' => "Western European Summer Time", + 'dstshortname' => 'WEST' ), + 'Europe/London' => array( + 'offset' => 0, + 'longname' => "Greenwich Mean Time", + 'shortname' => 'GMT', + 'hasdst' => true, + 'dstlongname' => "British Summer Time", + 'dstshortname' => 'BST' ), + 'GB' => array( + 'offset' => 0, + 'longname' => "Greenwich Mean Time", + 'shortname' => 'GMT', + 'hasdst' => true, + 'dstlongname' => "British Summer Time", + 'dstshortname' => 'BST' ), + 'GB-Eire' => array( + 'offset' => 0, + 'longname' => "Greenwich Mean Time", + 'shortname' => 'GMT', + 'hasdst' => true, + 'dstlongname' => "British Summer Time", + 'dstshortname' => 'BST' ), + 'GMT' => array( + 'offset' => 0, + 'longname' => "Greenwich Mean Time", + 'shortname' => 'GMT', + 'hasdst' => false ), + 'GMT0' => array( + 'offset' => 0, + 'longname' => "GMT+00:00", + 'shortname' => 'GMT+00:00', + 'hasdst' => false ), + 'Greenwich' => array( + 'offset' => 0, + 'longname' => "Greenwich Mean Time", + 'shortname' => 'GMT', + 'hasdst' => false ), + 'Iceland' => array( + 'offset' => 0, + 'longname' => "Greenwich Mean Time", + 'shortname' => 'GMT', + 'hasdst' => false ), + 'Portugal' => array( + 'offset' => 0, + 'longname' => "Western European Time", + 'shortname' => 'WET', + 'hasdst' => true, + 'dstlongname' => "Western European Summer Time", + 'dstshortname' => 'WEST' ), + 'UCT' => array( + 'offset' => 0, + 'longname' => "Coordinated Universal Time", + 'shortname' => 'UTC', + 'hasdst' => false ), + 'UTC' => array( + 'offset' => 0, + 'longname' => "Coordinated Universal Time", + 'shortname' => 'UTC', + 'hasdst' => false ), + 'Universal' => array( + 'offset' => 0, + 'longname' => "Coordinated Universal Time", + 'shortname' => 'UTC', + 'hasdst' => false ), + 'WET' => array( + 'offset' => 0, + 'longname' => "Western European Time", + 'shortname' => 'WET', + 'hasdst' => true, + 'dstlongname' => "Western European Summer Time", + 'dstshortname' => 'WEST' ), + 'Zulu' => array( + 'offset' => 0, + 'longname' => "Coordinated Universal Time", + 'shortname' => 'UTC', + 'hasdst' => false ), + 'Africa/Algiers' => array( + 'offset' => 3600000, + 'longname' => "Central European Time", + 'shortname' => 'CET', + 'hasdst' => false ), + 'Africa/Bangui' => array( + 'offset' => 3600000, + 'longname' => "Western African Time", + 'shortname' => 'WAT', + 'hasdst' => false ), + 'Africa/Brazzaville' => array( + 'offset' => 3600000, + 'longname' => "Western African Time", + 'shortname' => 'WAT', + 'hasdst' => false ), + 'Africa/Ceuta' => array( + 'offset' => 3600000, + 'longname' => "Central European Time", + 'shortname' => 'CET', + 'hasdst' => true, + 'dstlongname' => "Central European Summer Time", + 'dstshortname' => 'CEST' ), + 'Africa/Douala' => array( + 'offset' => 3600000, + 'longname' => "Western African Time", + 'shortname' => 'WAT', + 'hasdst' => false ), + 'Africa/Kinshasa' => array( + 'offset' => 3600000, + 'longname' => "Western African Time", + 'shortname' => 'WAT', + 'hasdst' => false ), + 'Africa/Lagos' => array( + 'offset' => 3600000, + 'longname' => "Western African Time", + 'shortname' => 'WAT', + 'hasdst' => false ), + 'Africa/Libreville' => array( + 'offset' => 3600000, + 'longname' => "Western African Time", + 'shortname' => 'WAT', + 'hasdst' => false ), + 'Africa/Luanda' => array( + 'offset' => 3600000, + 'longname' => "Western African Time", + 'shortname' => 'WAT', + 'hasdst' => false ), + 'Africa/Malabo' => array( + 'offset' => 3600000, + 'longname' => "Western African Time", + 'shortname' => 'WAT', + 'hasdst' => false ), + 'Africa/Ndjamena' => array( + 'offset' => 3600000, + 'longname' => "Western African Time", + 'shortname' => 'WAT', + 'hasdst' => false ), + 'Africa/Niamey' => array( + 'offset' => 3600000, + 'longname' => "Western African Time", + 'shortname' => 'WAT', + 'hasdst' => false ), + 'Africa/Porto-Novo' => array( + 'offset' => 3600000, + 'longname' => "Western African Time", + 'shortname' => 'WAT', + 'hasdst' => false ), + 'Africa/Tunis' => array( + 'offset' => 3600000, + 'longname' => "Central European Time", + 'shortname' => 'CET', + 'hasdst' => false ), + 'Africa/Windhoek' => array( + 'offset' => 3600000, + 'longname' => "Western African Time", + 'shortname' => 'WAT', + 'hasdst' => true, + 'dstlongname' => "Western African Summer Time", + 'dstshortname' => 'WAST' ), + 'Arctic/Longyearbyen' => array( + 'offset' => 3600000, + 'longname' => "Central European Time", + 'shortname' => 'CET', + 'hasdst' => true, + 'dstlongname' => "Central European Summer Time", + 'dstshortname' => 'CEST' ), + 'Atlantic/Jan_Mayen' => array( + 'offset' => 3600000, + 'longname' => "Eastern Greenland Time", + 'shortname' => 'EGT', + 'hasdst' => true, + 'dstlongname' => "Eastern Greenland Summer Time", + 'dstshortname' => 'EGST' ), + 'CET' => array( + 'offset' => 3600000, + 'longname' => "Central European Time", + 'shortname' => 'CET', + 'hasdst' => true, + 'dstlongname' => "Central European Summer Time", + 'dstshortname' => 'CEST' ), + 'ECT' => array( + 'offset' => 3600000, + 'longname' => "Central European Time", + 'shortname' => 'CET', + 'hasdst' => true, + 'dstlongname' => "Central European Summer Time", + 'dstshortname' => 'CEST' ), + 'Etc/GMT-1' => array( + 'offset' => 3600000, + 'longname' => "GMT+01:00", + 'shortname' => 'GMT+01:00', + 'hasdst' => false ), + 'Europe/Amsterdam' => array( + 'offset' => 3600000, + 'longname' => "Central European Time", + 'shortname' => 'CET', + 'hasdst' => true, + 'dstlongname' => "Central European Summer Time", + 'dstshortname' => 'CEST' ), + 'Europe/Andorra' => array( + 'offset' => 3600000, + 'longname' => "Central European Time", + 'shortname' => 'CET', + 'hasdst' => true, + 'dstlongname' => "Central European Summer Time", + 'dstshortname' => 'CEST' ), + 'Europe/Belgrade' => array( + 'offset' => 3600000, + 'longname' => "Central European Time", + 'shortname' => 'CET', + 'hasdst' => true, + 'dstlongname' => "Central European Summer Time", + 'dstshortname' => 'CEST' ), + 'Europe/Berlin' => array( + 'offset' => 3600000, + 'longname' => "Central European Time", + 'shortname' => 'CET', + 'hasdst' => true, + 'dstlongname' => "Central European Summer Time", + 'dstshortname' => 'CEST' ), + 'Europe/Bratislava' => array( + 'offset' => 3600000, + 'longname' => "Central European Time", + 'shortname' => 'CET', + 'hasdst' => true, + 'dstlongname' => "Central European Summer Time", + 'dstshortname' => 'CEST' ), + 'Europe/Brussels' => array( + 'offset' => 3600000, + 'longname' => "Central European Time", + 'shortname' => 'CET', + 'hasdst' => true, + 'dstlongname' => "Central European Summer Time", + 'dstshortname' => 'CEST' ), + 'Europe/Budapest' => array( + 'offset' => 3600000, + 'longname' => "Central European Time", + 'shortname' => 'CET', + 'hasdst' => true, + 'dstlongname' => "Central European Summer Time", + 'dstshortname' => 'CEST' ), + 'Europe/Copenhagen' => array( + 'offset' => 3600000, + 'longname' => "Central European Time", + 'shortname' => 'CET', + 'hasdst' => true, + 'dstlongname' => "Central European Summer Time", + 'dstshortname' => 'CEST' ), + 'Europe/Gibraltar' => array( + 'offset' => 3600000, + 'longname' => "Central European Time", + 'shortname' => 'CET', + 'hasdst' => true, + 'dstlongname' => "Central European Summer Time", + 'dstshortname' => 'CEST' ), + 'Europe/Ljubljana' => array( + 'offset' => 3600000, + 'longname' => "Central European Time", + 'shortname' => 'CET', + 'hasdst' => true, + 'dstlongname' => "Central European Summer Time", + 'dstshortname' => 'CEST' ), + 'Europe/Luxembourg' => array( + 'offset' => 3600000, + 'longname' => "Central European Time", + 'shortname' => 'CET', + 'hasdst' => true, + 'dstlongname' => "Central European Summer Time", + 'dstshortname' => 'CEST' ), + 'Europe/Madrid' => array( + 'offset' => 3600000, + 'longname' => "Central European Time", + 'shortname' => 'CET', + 'hasdst' => true, + 'dstlongname' => "Central European Summer Time", + 'dstshortname' => 'CEST' ), + 'Europe/Malta' => array( + 'offset' => 3600000, + 'longname' => "Central European Time", + 'shortname' => 'CET', + 'hasdst' => true, + 'dstlongname' => "Central European Summer Time", + 'dstshortname' => 'CEST' ), + 'Europe/Monaco' => array( + 'offset' => 3600000, + 'longname' => "Central European Time", + 'shortname' => 'CET', + 'hasdst' => true, + 'dstlongname' => "Central European Summer Time", + 'dstshortname' => 'CEST' ), + 'Europe/Oslo' => array( + 'offset' => 3600000, + 'longname' => "Central European Time", + 'shortname' => 'CET', + 'hasdst' => true, + 'dstlongname' => "Central European Summer Time", + 'dstshortname' => 'CEST' ), + 'Europe/Paris' => array( + 'offset' => 3600000, + 'longname' => "Central European Time", + 'shortname' => 'CET', + 'hasdst' => true, + 'dstlongname' => "Central European Summer Time", + 'dstshortname' => 'CEST' ), + 'Europe/Prague' => array( + 'offset' => 3600000, + 'longname' => "Central European Time", + 'shortname' => 'CET', + 'hasdst' => true, + 'dstlongname' => "Central European Summer Time", + 'dstshortname' => 'CEST' ), + 'Europe/Rome' => array( + 'offset' => 3600000, + 'longname' => "Central European Time", + 'shortname' => 'CET', + 'hasdst' => true, + 'dstlongname' => "Central European Summer Time", + 'dstshortname' => 'CEST' ), + 'Europe/San_Marino' => array( + 'offset' => 3600000, + 'longname' => "Central European Time", + 'shortname' => 'CET', + 'hasdst' => true, + 'dstlongname' => "Central European Summer Time", + 'dstshortname' => 'CEST' ), + 'Europe/Sarajevo' => array( + 'offset' => 3600000, + 'longname' => "Central European Time", + 'shortname' => 'CET', + 'hasdst' => true, + 'dstlongname' => "Central European Summer Time", + 'dstshortname' => 'CEST' ), + 'Europe/Skopje' => array( + 'offset' => 3600000, + 'longname' => "Central European Time", + 'shortname' => 'CET', + 'hasdst' => true, + 'dstlongname' => "Central European Summer Time", + 'dstshortname' => 'CEST' ), + 'Europe/Stockholm' => array( + 'offset' => 3600000, + 'longname' => "Central European Time", + 'shortname' => 'CET', + 'hasdst' => true, + 'dstlongname' => "Central European Summer Time", + 'dstshortname' => 'CEST' ), + 'Europe/Tirane' => array( + 'offset' => 3600000, + 'longname' => "Central European Time", + 'shortname' => 'CET', + 'hasdst' => true, + 'dstlongname' => "Central European Summer Time", + 'dstshortname' => 'CEST' ), + 'Europe/Vaduz' => array( + 'offset' => 3600000, + 'longname' => "Central European Time", + 'shortname' => 'CET', + 'hasdst' => true, + 'dstlongname' => "Central European Summer Time", + 'dstshortname' => 'CEST' ), + 'Europe/Vatican' => array( + 'offset' => 3600000, + 'longname' => "Central European Time", + 'shortname' => 'CET', + 'hasdst' => true, + 'dstlongname' => "Central European Summer Time", + 'dstshortname' => 'CEST' ), + 'Europe/Vienna' => array( + 'offset' => 3600000, + 'longname' => "Central European Time", + 'shortname' => 'CET', + 'hasdst' => true, + 'dstlongname' => "Central European Summer Time", + 'dstshortname' => 'CEST' ), + 'Europe/Warsaw' => array( + 'offset' => 3600000, + 'longname' => "Central European Time", + 'shortname' => 'CET', + 'hasdst' => true, + 'dstlongname' => "Central European Summer Time", + 'dstshortname' => 'CEST' ), + 'Europe/Zagreb' => array( + 'offset' => 3600000, + 'longname' => "Central European Time", + 'shortname' => 'CET', + 'hasdst' => true, + 'dstlongname' => "Central European Summer Time", + 'dstshortname' => 'CEST' ), + 'Europe/Zurich' => array( + 'offset' => 3600000, + 'longname' => "Central European Time", + 'shortname' => 'CET', + 'hasdst' => true, + 'dstlongname' => "Central European Summer Time", + 'dstshortname' => 'CEST' ), + 'MET' => array( + 'offset' => 3600000, + 'longname' => "Middle Europe Time", + 'shortname' => 'MET', + 'hasdst' => true, + 'dstlongname' => "Middle Europe Summer Time", + 'dstshortname' => 'MEST' ), + 'Poland' => array( + 'offset' => 3600000, + 'longname' => "Central European Time", + 'shortname' => 'CET', + 'hasdst' => true, + 'dstlongname' => "Central European Summer Time", + 'dstshortname' => 'CEST' ), + 'ART' => array( + 'offset' => 7200000, + 'longname' => "Eastern European Time", + 'shortname' => 'EET', + 'hasdst' => true, + 'dstlongname' => "Eastern European Summer Time", + 'dstshortname' => 'EEST' ), + 'Africa/Blantyre' => array( + 'offset' => 7200000, + 'longname' => "Central African Time", + 'shortname' => 'CAT', + 'hasdst' => false ), + 'Africa/Bujumbura' => array( + 'offset' => 7200000, + 'longname' => "Central African Time", + 'shortname' => 'CAT', + 'hasdst' => false ), + 'Africa/Cairo' => array( + 'offset' => 7200000, + 'longname' => "Eastern European Time", + 'shortname' => 'EET', + 'hasdst' => true, + 'dstlongname' => "Eastern European Summer Time", + 'dstshortname' => 'EEST' ), + 'Africa/Gaborone' => array( + 'offset' => 7200000, + 'longname' => "Central African Time", + 'shortname' => 'CAT', + 'hasdst' => false ), + 'Africa/Harare' => array( + 'offset' => 7200000, + 'longname' => "Central African Time", + 'shortname' => 'CAT', + 'hasdst' => false ), + 'Africa/Johannesburg' => array( + 'offset' => 7200000, + 'longname' => "South Africa Standard Time", + 'shortname' => 'SAST', + 'hasdst' => false ), + 'Africa/Kigali' => array( + 'offset' => 7200000, + 'longname' => "Central African Time", + 'shortname' => 'CAT', + 'hasdst' => false ), + 'Africa/Lubumbashi' => array( + 'offset' => 7200000, + 'longname' => "Central African Time", + 'shortname' => 'CAT', + 'hasdst' => false ), + 'Africa/Lusaka' => array( + 'offset' => 7200000, + 'longname' => "Central African Time", + 'shortname' => 'CAT', + 'hasdst' => false ), + 'Africa/Maputo' => array( + 'offset' => 7200000, + 'longname' => "Central African Time", + 'shortname' => 'CAT', + 'hasdst' => false ), + 'Africa/Maseru' => array( + 'offset' => 7200000, + 'longname' => "South Africa Standard Time", + 'shortname' => 'SAST', + 'hasdst' => false ), + 'Africa/Mbabane' => array( + 'offset' => 7200000, + 'longname' => "South Africa Standard Time", + 'shortname' => 'SAST', + 'hasdst' => false ), + 'Africa/Tripoli' => array( + 'offset' => 7200000, + 'longname' => "Eastern European Time", + 'shortname' => 'EET', + 'hasdst' => false ), + 'Asia/Amman' => array( + 'offset' => 7200000, + 'longname' => "Eastern European Time", + 'shortname' => 'EET', + 'hasdst' => true, + 'dstlongname' => "Eastern European Summer Time", + 'dstshortname' => 'EEST' ), + 'Asia/Beirut' => array( + 'offset' => 7200000, + 'longname' => "Eastern European Time", + 'shortname' => 'EET', + 'hasdst' => true, + 'dstlongname' => "Eastern European Summer Time", + 'dstshortname' => 'EEST' ), + 'Asia/Damascus' => array( + 'offset' => 7200000, + 'longname' => "Eastern European Time", + 'shortname' => 'EET', + 'hasdst' => true, + 'dstlongname' => "Eastern European Summer Time", + 'dstshortname' => 'EEST' ), + 'Asia/Gaza' => array( + 'offset' => 7200000, + 'longname' => "Eastern European Time", + 'shortname' => 'EET', + 'hasdst' => true, + 'dstlongname' => "Eastern European Summer Time", + 'dstshortname' => 'EEST' ), + 'Asia/Istanbul' => array( + 'offset' => 7200000, + 'longname' => "Eastern European Time", + 'shortname' => 'EET', + 'hasdst' => true, + 'dstlongname' => "Eastern European Summer Time", + 'dstshortname' => 'EEST' ), + 'Asia/Jerusalem' => array( + 'offset' => 7200000, + 'longname' => "Israel Standard Time", + 'shortname' => 'IST', + 'hasdst' => true, + 'dstlongname' => "Israel Daylight Time", + 'dstshortname' => 'IDT' ), + 'Asia/Nicosia' => array( + 'offset' => 7200000, + 'longname' => "Eastern European Time", + 'shortname' => 'EET', + 'hasdst' => true, + 'dstlongname' => "Eastern European Summer Time", + 'dstshortname' => 'EEST' ), + 'Asia/Tel_Aviv' => array( + 'offset' => 7200000, + 'longname' => "Israel Standard Time", + 'shortname' => 'IST', + 'hasdst' => true, + 'dstlongname' => "Israel Daylight Time", + 'dstshortname' => 'IDT' ), + 'CAT' => array( + 'offset' => 7200000, + 'longname' => "Central African Time", + 'shortname' => 'CAT', + 'hasdst' => false ), + 'EET' => array( + 'offset' => 7200000, + 'longname' => "Eastern European Time", + 'shortname' => 'EET', + 'hasdst' => true, + 'dstlongname' => "Eastern European Summer Time", + 'dstshortname' => 'EEST' ), + 'Egypt' => array( + 'offset' => 7200000, + 'longname' => "Eastern European Time", + 'shortname' => 'EET', + 'hasdst' => true, + 'dstlongname' => "Eastern European Summer Time", + 'dstshortname' => 'EEST' ), + 'Etc/GMT-2' => array( + 'offset' => 7200000, + 'longname' => "GMT+02:00", + 'shortname' => 'GMT+02:00', + 'hasdst' => false ), + 'Europe/Athens' => array( + 'offset' => 7200000, + 'longname' => "Eastern European Time", + 'shortname' => 'EET', + 'hasdst' => true, + 'dstlongname' => "Eastern European Summer Time", + 'dstshortname' => 'EEST' ), + 'Europe/Bucharest' => array( + 'offset' => 7200000, + 'longname' => "Eastern European Time", + 'shortname' => 'EET', + 'hasdst' => true, + 'dstlongname' => "Eastern European Summer Time", + 'dstshortname' => 'EEST' ), + 'Europe/Chisinau' => array( + 'offset' => 7200000, + 'longname' => "Eastern European Time", + 'shortname' => 'EET', + 'hasdst' => true, + 'dstlongname' => "Eastern European Summer Time", + 'dstshortname' => 'EEST' ), + 'Europe/Helsinki' => array( + 'offset' => 7200000, + 'longname' => "Eastern European Time", + 'shortname' => 'EET', + 'hasdst' => true, + 'dstlongname' => "Eastern European Summer Time", + 'dstshortname' => 'EEST' ), + 'Europe/Istanbul' => array( + 'offset' => 7200000, + 'longname' => "Eastern European Time", + 'shortname' => 'EET', + 'hasdst' => true, + 'dstlongname' => "Eastern European Summer Time", + 'dstshortname' => 'EEST' ), + 'Europe/Kaliningrad' => array( + 'offset' => 7200000, + 'longname' => "Eastern European Time", + 'shortname' => 'EET', + 'hasdst' => true, + 'dstlongname' => "Eastern European Summer Time", + 'dstshortname' => 'EEST' ), + 'Europe/Kiev' => array( + 'offset' => 7200000, + 'longname' => "Eastern European Time", + 'shortname' => 'EET', + 'hasdst' => true, + 'dstlongname' => "Eastern European Summer Time", + 'dstshortname' => 'EEST' ), + 'Europe/Minsk' => array( + 'offset' => 7200000, + 'longname' => "Eastern European Time", + 'shortname' => 'EET', + 'hasdst' => true, + 'dstlongname' => "Eastern European Summer Time", + 'dstshortname' => 'EEST' ), + 'Europe/Nicosia' => array( + 'offset' => 7200000, + 'longname' => "Eastern European Time", + 'shortname' => 'EET', + 'hasdst' => true, + 'dstlongname' => "Eastern European Summer Time", + 'dstshortname' => 'EEST' ), + 'Europe/Riga' => array( + 'offset' => 7200000, + 'longname' => "Eastern European Time", + 'shortname' => 'EET', + 'hasdst' => true, + 'dstlongname' => "Eastern European Summer Time", + 'dstshortname' => 'EEST' ), + 'Europe/Simferopol' => array( + 'offset' => 7200000, + 'longname' => "Eastern European Time", + 'shortname' => 'EET', + 'hasdst' => true, + 'dstlongname' => "Eastern European Summer Time", + 'dstshortname' => 'EEST' ), + 'Europe/Sofia' => array( + 'offset' => 7200000, + 'longname' => "Eastern European Time", + 'shortname' => 'EET', + 'hasdst' => true, + 'dstlongname' => "Eastern European Summer Time", + 'dstshortname' => 'EEST' ), + 'Europe/Tallinn' => array( + 'offset' => 7200000, + 'longname' => "Eastern European Time", + 'shortname' => 'EET', + 'hasdst' => false ), + 'Europe/Tiraspol' => array( + 'offset' => 7200000, + 'longname' => "Eastern European Time", + 'shortname' => 'EET', + 'hasdst' => true, + 'dstlongname' => "Eastern European Summer Time", + 'dstshortname' => 'EEST' ), + 'Europe/Uzhgorod' => array( + 'offset' => 7200000, + 'longname' => "Eastern European Time", + 'shortname' => 'EET', + 'hasdst' => true, + 'dstlongname' => "Eastern European Summer Time", + 'dstshortname' => 'EEST' ), + 'Europe/Vilnius' => array( + 'offset' => 7200000, + 'longname' => "Eastern European Time", + 'shortname' => 'EET', + 'hasdst' => false ), + 'Europe/Zaporozhye' => array( + 'offset' => 7200000, + 'longname' => "Eastern European Time", + 'shortname' => 'EET', + 'hasdst' => true, + 'dstlongname' => "Eastern European Summer Time", + 'dstshortname' => 'EEST' ), + 'Israel' => array( + 'offset' => 7200000, + 'longname' => "Israel Standard Time", + 'shortname' => 'IST', + 'hasdst' => true, + 'dstlongname' => "Israel Daylight Time", + 'dstshortname' => 'IDT' ), + 'Libya' => array( + 'offset' => 7200000, + 'longname' => "Eastern European Time", + 'shortname' => 'EET', + 'hasdst' => false ), + 'Turkey' => array( + 'offset' => 7200000, + 'longname' => "Eastern European Time", + 'shortname' => 'EET', + 'hasdst' => true, + 'dstlongname' => "Eastern European Summer Time", + 'dstshortname' => 'EEST' ), + 'Africa/Addis_Ababa' => array( + 'offset' => 10800000, + 'longname' => "Eastern African Time", + 'shortname' => 'EAT', + 'hasdst' => false ), + 'Africa/Asmera' => array( + 'offset' => 10800000, + 'longname' => "Eastern African Time", + 'shortname' => 'EAT', + 'hasdst' => false ), + 'Africa/Dar_es_Salaam' => array( + 'offset' => 10800000, + 'longname' => "Eastern African Time", + 'shortname' => 'EAT', + 'hasdst' => false ), + 'Africa/Djibouti' => array( + 'offset' => 10800000, + 'longname' => "Eastern African Time", + 'shortname' => 'EAT', + 'hasdst' => false ), + 'Africa/Kampala' => array( + 'offset' => 10800000, + 'longname' => "Eastern African Time", + 'shortname' => 'EAT', + 'hasdst' => false ), + 'Africa/Khartoum' => array( + 'offset' => 10800000, + 'longname' => "Eastern African Time", + 'shortname' => 'EAT', + 'hasdst' => false ), + 'Africa/Mogadishu' => array( + 'offset' => 10800000, + 'longname' => "Eastern African Time", + 'shortname' => 'EAT', + 'hasdst' => false ), + 'Africa/Nairobi' => array( + 'offset' => 10800000, + 'longname' => "Eastern African Time", + 'shortname' => 'EAT', + 'hasdst' => false ), + 'Antarctica/Syowa' => array( + 'offset' => 10800000, + 'longname' => "Syowa Time", + 'shortname' => 'SYOT', + 'hasdst' => false ), + 'Asia/Aden' => array( + 'offset' => 10800000, + 'longname' => "Arabia Standard Time", + 'shortname' => 'AST', + 'hasdst' => false ), + 'Asia/Baghdad' => array( + 'offset' => 10800000, + 'longname' => "Arabia Standard Time", + 'shortname' => 'AST', + 'hasdst' => true, + 'dstlongname' => "Arabia Daylight Time", + 'dstshortname' => 'ADT' ), + 'Asia/Bahrain' => array( + 'offset' => 10800000, + 'longname' => "Arabia Standard Time", + 'shortname' => 'AST', + 'hasdst' => false ), + 'Asia/Kuwait' => array( + 'offset' => 10800000, + 'longname' => "Arabia Standard Time", + 'shortname' => 'AST', + 'hasdst' => false ), + 'Asia/Qatar' => array( + 'offset' => 10800000, + 'longname' => "Arabia Standard Time", + 'shortname' => 'AST', + 'hasdst' => false ), + 'Asia/Riyadh' => array( + 'offset' => 10800000, + 'longname' => "Arabia Standard Time", + 'shortname' => 'AST', + 'hasdst' => false ), + 'EAT' => array( + 'offset' => 10800000, + 'longname' => "Eastern African Time", + 'shortname' => 'EAT', + 'hasdst' => false ), + 'Etc/GMT-3' => array( + 'offset' => 10800000, + 'longname' => "GMT+03:00", + 'shortname' => 'GMT+03:00', + 'hasdst' => false ), + 'Europe/Moscow' => array( + 'offset' => 10800000, + 'longname' => "Moscow Standard Time", + 'shortname' => 'MSK', + 'hasdst' => true, + 'dstlongname' => "Moscow Daylight Time", + 'dstshortname' => 'MSD' ), + 'Indian/Antananarivo' => array( + 'offset' => 10800000, + 'longname' => "Eastern African Time", + 'shortname' => 'EAT', + 'hasdst' => false ), + 'Indian/Comoro' => array( + 'offset' => 10800000, + 'longname' => "Eastern African Time", + 'shortname' => 'EAT', + 'hasdst' => false ), + 'Indian/Mayotte' => array( + 'offset' => 10800000, + 'longname' => "Eastern African Time", + 'shortname' => 'EAT', + 'hasdst' => false ), + 'W-SU' => array( + 'offset' => 10800000, + 'longname' => "Moscow Standard Time", + 'shortname' => 'MSK', + 'hasdst' => true, + 'dstlongname' => "Moscow Daylight Time", + 'dstshortname' => 'MSD' ), + 'Asia/Riyadh87' => array( + 'offset' => 11224000, + 'longname' => "GMT+03:07", + 'shortname' => 'GMT+03:07', + 'hasdst' => false ), + 'Asia/Riyadh88' => array( + 'offset' => 11224000, + 'longname' => "GMT+03:07", + 'shortname' => 'GMT+03:07', + 'hasdst' => false ), + 'Asia/Riyadh89' => array( + 'offset' => 11224000, + 'longname' => "GMT+03:07", + 'shortname' => 'GMT+03:07', + 'hasdst' => false ), + 'Mideast/Riyadh87' => array( + 'offset' => 11224000, + 'longname' => "GMT+03:07", + 'shortname' => 'GMT+03:07', + 'hasdst' => false ), + 'Mideast/Riyadh88' => array( + 'offset' => 11224000, + 'longname' => "GMT+03:07", + 'shortname' => 'GMT+03:07', + 'hasdst' => false ), + 'Mideast/Riyadh89' => array( + 'offset' => 11224000, + 'longname' => "GMT+03:07", + 'shortname' => 'GMT+03:07', + 'hasdst' => false ), + 'Asia/Tehran' => array( + 'offset' => 12600000, + 'longname' => "Iran Time", + 'shortname' => 'IRT', + 'hasdst' => true, + 'dstlongname' => "Iran Sumer Time", + 'dstshortname' => 'IRST' ), + 'Iran' => array( + 'offset' => 12600000, + 'longname' => "Iran Time", + 'shortname' => 'IRT', + 'hasdst' => true, + 'dstlongname' => "Iran Sumer Time", + 'dstshortname' => 'IRST' ), + 'Asia/Aqtau' => array( + 'offset' => 14400000, + 'longname' => "Aqtau Time", + 'shortname' => 'AQTT', + 'hasdst' => true, + 'dstlongname' => "Aqtau Summer Time", + 'dstshortname' => 'AQTST' ), + 'Asia/Baku' => array( + 'offset' => 14400000, + 'longname' => "Azerbaijan Time", + 'shortname' => 'AZT', + 'hasdst' => true, + 'dstlongname' => "Azerbaijan Summer Time", + 'dstshortname' => 'AZST' ), + 'Asia/Dubai' => array( + 'offset' => 14400000, + 'longname' => "Gulf Standard Time", + 'shortname' => 'GST', + 'hasdst' => false ), + 'Asia/Muscat' => array( + 'offset' => 14400000, + 'longname' => "Gulf Standard Time", + 'shortname' => 'GST', + 'hasdst' => false ), + 'Asia/Tbilisi' => array( + 'offset' => 14400000, + 'longname' => "Georgia Time", + 'shortname' => 'GET', + 'hasdst' => true, + 'dstlongname' => "Georgia Summer Time", + 'dstshortname' => 'GEST' ), + 'Asia/Yerevan' => array( + 'offset' => 14400000, + 'longname' => "Armenia Time", + 'shortname' => 'AMT', + 'hasdst' => true, + 'dstlongname' => "Armenia Summer Time", + 'dstshortname' => 'AMST' ), + 'Etc/GMT-4' => array( + 'offset' => 14400000, + 'longname' => "GMT+04:00", + 'shortname' => 'GMT+04:00', + 'hasdst' => false ), + 'Europe/Samara' => array( + 'offset' => 14400000, + 'longname' => "Samara Time", + 'shortname' => 'SAMT', + 'hasdst' => true, + 'dstlongname' => "Samara Summer Time", + 'dstshortname' => 'SAMST' ), + 'Indian/Mahe' => array( + 'offset' => 14400000, + 'longname' => "Seychelles Time", + 'shortname' => 'SCT', + 'hasdst' => false ), + 'Indian/Mauritius' => array( + 'offset' => 14400000, + 'longname' => "Mauritius Time", + 'shortname' => 'MUT', + 'hasdst' => false ), + 'Indian/Reunion' => array( + 'offset' => 14400000, + 'longname' => "Reunion Time", + 'shortname' => 'RET', + 'hasdst' => false ), + 'NET' => array( + 'offset' => 14400000, + 'longname' => "Armenia Time", + 'shortname' => 'AMT', + 'hasdst' => true, + 'dstlongname' => "Armenia Summer Time", + 'dstshortname' => 'AMST' ), + 'Asia/Kabul' => array( + 'offset' => 16200000, + 'longname' => "Afghanistan Time", + 'shortname' => 'AFT', + 'hasdst' => false ), + 'Asia/Aqtobe' => array( + 'offset' => 18000000, + 'longname' => "Aqtobe Time", + 'shortname' => 'AQTT', + 'hasdst' => true, + 'dstlongname' => "Aqtobe Summer Time", + 'dstshortname' => 'AQTST' ), + 'Asia/Ashgabat' => array( + 'offset' => 18000000, + 'longname' => "Turkmenistan Time", + 'shortname' => 'TMT', + 'hasdst' => false ), + 'Asia/Ashkhabad' => array( + 'offset' => 18000000, + 'longname' => "Turkmenistan Time", + 'shortname' => 'TMT', + 'hasdst' => false ), + 'Asia/Bishkek' => array( + 'offset' => 18000000, + 'longname' => "Kirgizstan Time", + 'shortname' => 'KGT', + 'hasdst' => true, + 'dstlongname' => "Kirgizstan Summer Time", + 'dstshortname' => 'KGST' ), + 'Asia/Dushanbe' => array( + 'offset' => 18000000, + 'longname' => "Tajikistan Time", + 'shortname' => 'TJT', + 'hasdst' => false ), + 'Asia/Karachi' => array( + 'offset' => 18000000, + 'longname' => "Pakistan Time", + 'shortname' => 'PKT', + 'hasdst' => false ), + 'Asia/Samarkand' => array( + 'offset' => 18000000, + 'longname' => "Turkmenistan Time", + 'shortname' => 'TMT', + 'hasdst' => false ), + 'Asia/Tashkent' => array( + 'offset' => 18000000, + 'longname' => "Uzbekistan Time", + 'shortname' => 'UZT', + 'hasdst' => false ), + 'Asia/Yekaterinburg' => array( + 'offset' => 18000000, + 'longname' => "Yekaterinburg Time", + 'shortname' => 'YEKT', + 'hasdst' => true, + 'dstlongname' => "Yekaterinburg Summer Time", + 'dstshortname' => 'YEKST' ), + 'Etc/GMT-5' => array( + 'offset' => 18000000, + 'longname' => "GMT+05:00", + 'shortname' => 'GMT+05:00', + 'hasdst' => false ), + 'Indian/Kerguelen' => array( + 'offset' => 18000000, + 'longname' => "French Southern & Antarctic Lands Time", + 'shortname' => 'TFT', + 'hasdst' => false ), + 'Indian/Maldives' => array( + 'offset' => 18000000, + 'longname' => "Maldives Time", + 'shortname' => 'MVT', + 'hasdst' => false ), + 'PLT' => array( + 'offset' => 18000000, + 'longname' => "Pakistan Time", + 'shortname' => 'PKT', + 'hasdst' => false ), + 'Asia/Calcutta' => array( + 'offset' => 19800000, + 'longname' => "India Standard Time", + 'shortname' => 'IST', + 'hasdst' => false ), + 'IST' => array( + 'offset' => 19800000, + 'longname' => "India Standard Time", + 'shortname' => 'IST', + 'hasdst' => false ), + 'Asia/Katmandu' => array( + 'offset' => 20700000, + 'longname' => "Nepal Time", + 'shortname' => 'NPT', + 'hasdst' => false ), + 'Antarctica/Mawson' => array( + 'offset' => 21600000, + 'longname' => "Mawson Time", + 'shortname' => 'MAWT', + 'hasdst' => false ), + 'Antarctica/Vostok' => array( + 'offset' => 21600000, + 'longname' => "Vostok time", + 'shortname' => 'VOST', + 'hasdst' => false ), + 'Asia/Almaty' => array( + 'offset' => 21600000, + 'longname' => "Alma-Ata Time", + 'shortname' => 'ALMT', + 'hasdst' => true, + 'dstlongname' => "Alma-Ata Summer Time", + 'dstshortname' => 'ALMST' ), + 'Asia/Colombo' => array( + 'offset' => 21600000, + 'longname' => "Sri Lanka Time", + 'shortname' => 'LKT', + 'hasdst' => false ), + 'Asia/Dacca' => array( + 'offset' => 21600000, + 'longname' => "Bangladesh Time", + 'shortname' => 'BDT', + 'hasdst' => false ), + 'Asia/Dhaka' => array( + 'offset' => 21600000, + 'longname' => "Bangladesh Time", + 'shortname' => 'BDT', + 'hasdst' => false ), + 'Asia/Novosibirsk' => array( + 'offset' => 21600000, + 'longname' => "Novosibirsk Time", + 'shortname' => 'NOVT', + 'hasdst' => true, + 'dstlongname' => "Novosibirsk Summer Time", + 'dstshortname' => 'NOVST' ), + 'Asia/Omsk' => array( + 'offset' => 21600000, + 'longname' => "Omsk Time", + 'shortname' => 'OMST', + 'hasdst' => true, + 'dstlongname' => "Omsk Summer Time", + 'dstshortname' => 'OMSST' ), + 'Asia/Thimbu' => array( + 'offset' => 21600000, + 'longname' => "Bhutan Time", + 'shortname' => 'BTT', + 'hasdst' => false ), + 'Asia/Thimphu' => array( + 'offset' => 21600000, + 'longname' => "Bhutan Time", + 'shortname' => 'BTT', + 'hasdst' => false ), + 'BST' => array( + 'offset' => 21600000, + 'longname' => "Bangladesh Time", + 'shortname' => 'BDT', + 'hasdst' => false ), + 'Etc/GMT-6' => array( + 'offset' => 21600000, + 'longname' => "GMT+06:00", + 'shortname' => 'GMT+06:00', + 'hasdst' => false ), + 'Indian/Chagos' => array( + 'offset' => 21600000, + 'longname' => "Indian Ocean Territory Time", + 'shortname' => 'IOT', + 'hasdst' => false ), + 'Asia/Rangoon' => array( + 'offset' => 23400000, + 'longname' => "Myanmar Time", + 'shortname' => 'MMT', + 'hasdst' => false ), + 'Indian/Cocos' => array( + 'offset' => 23400000, + 'longname' => "Cocos Islands Time", + 'shortname' => 'CCT', + 'hasdst' => false ), + 'Antarctica/Davis' => array( + 'offset' => 25200000, + 'longname' => "Davis Time", + 'shortname' => 'DAVT', + 'hasdst' => false ), + 'Asia/Bangkok' => array( + 'offset' => 25200000, + 'longname' => "Indochina Time", + 'shortname' => 'ICT', + 'hasdst' => false ), + 'Asia/Hovd' => array( + 'offset' => 25200000, + 'longname' => "Hovd Time", + 'shortname' => 'HOVT', + 'hasdst' => false ), + 'Asia/Jakarta' => array( + 'offset' => 25200000, + 'longname' => "West Indonesia Time", + 'shortname' => 'WIT', + 'hasdst' => false ), + 'Asia/Krasnoyarsk' => array( + 'offset' => 25200000, + 'longname' => "Krasnoyarsk Time", + 'shortname' => 'KRAT', + 'hasdst' => true, + 'dstlongname' => "Krasnoyarsk Summer Time", + 'dstshortname' => 'KRAST' ), + 'Asia/Phnom_Penh' => array( + 'offset' => 25200000, + 'longname' => "Indochina Time", + 'shortname' => 'ICT', + 'hasdst' => false ), + 'Asia/Pontianak' => array( + 'offset' => 25200000, + 'longname' => "West Indonesia Time", + 'shortname' => 'WIT', + 'hasdst' => false ), + 'Asia/Saigon' => array( + 'offset' => 25200000, + 'longname' => "Indochina Time", + 'shortname' => 'ICT', + 'hasdst' => false ), + 'Asia/Vientiane' => array( + 'offset' => 25200000, + 'longname' => "Indochina Time", + 'shortname' => 'ICT', + 'hasdst' => false ), + 'Etc/GMT-7' => array( + 'offset' => 25200000, + 'longname' => "GMT+07:00", + 'shortname' => 'GMT+07:00', + 'hasdst' => false ), + 'Indian/Christmas' => array( + 'offset' => 25200000, + 'longname' => "Christmas Island Time", + 'shortname' => 'CXT', + 'hasdst' => false ), + 'VST' => array( + 'offset' => 25200000, + 'longname' => "Indochina Time", + 'shortname' => 'ICT', + 'hasdst' => false ), + 'Antarctica/Casey' => array( + 'offset' => 28800000, + 'longname' => "Western Standard Time (Australia)", + 'shortname' => 'WST', + 'hasdst' => false ), + 'Asia/Brunei' => array( + 'offset' => 28800000, + 'longname' => "Brunei Time", + 'shortname' => 'BNT', + 'hasdst' => false ), + 'Asia/Chongqing' => array( + 'offset' => 28800000, + 'longname' => "China Standard Time", + 'shortname' => 'CST', + 'hasdst' => false ), + 'Asia/Chungking' => array( + 'offset' => 28800000, + 'longname' => "China Standard Time", + 'shortname' => 'CST', + 'hasdst' => false ), + 'Asia/Harbin' => array( + 'offset' => 28800000, + 'longname' => "China Standard Time", + 'shortname' => 'CST', + 'hasdst' => false ), + 'Asia/Hong_Kong' => array( + 'offset' => 28800000, + 'longname' => "Hong Kong Time", + 'shortname' => 'HKT', + 'hasdst' => false ), + 'Asia/Irkutsk' => array( + 'offset' => 28800000, + 'longname' => "Irkutsk Time", + 'shortname' => 'IRKT', + 'hasdst' => true, + 'dstlongname' => "Irkutsk Summer Time", + 'dstshortname' => 'IRKST' ), + 'Asia/Kashgar' => array( + 'offset' => 28800000, + 'longname' => "China Standard Time", + 'shortname' => 'CST', + 'hasdst' => false ), + 'Asia/Kuala_Lumpur' => array( + 'offset' => 28800000, + 'longname' => "Malaysia Time", + 'shortname' => 'MYT', + 'hasdst' => false ), + 'Asia/Kuching' => array( + 'offset' => 28800000, + 'longname' => "Malaysia Time", + 'shortname' => 'MYT', + 'hasdst' => false ), + 'Asia/Macao' => array( + 'offset' => 28800000, + 'longname' => "China Standard Time", + 'shortname' => 'CST', + 'hasdst' => false ), + 'Asia/Manila' => array( + 'offset' => 28800000, + 'longname' => "Philippines Time", + 'shortname' => 'PHT', + 'hasdst' => false ), + 'Asia/Shanghai' => array( + 'offset' => 28800000, + 'longname' => "China Standard Time", + 'shortname' => 'CST', + 'hasdst' => false ), + 'Asia/Singapore' => array( + 'offset' => 28800000, + 'longname' => "Singapore Time", + 'shortname' => 'SGT', + 'hasdst' => false ), + 'Asia/Taipei' => array( + 'offset' => 28800000, + 'longname' => "China Standard Time", + 'shortname' => 'CST', + 'hasdst' => false ), + 'Asia/Ujung_Pandang' => array( + 'offset' => 28800000, + 'longname' => "Central Indonesia Time", + 'shortname' => 'CIT', + 'hasdst' => false ), + 'Asia/Ulaanbaatar' => array( + 'offset' => 28800000, + 'longname' => "Ulaanbaatar Time", + 'shortname' => 'ULAT', + 'hasdst' => false ), + 'Asia/Ulan_Bator' => array( + 'offset' => 28800000, + 'longname' => "Ulaanbaatar Time", + 'shortname' => 'ULAT', + 'hasdst' => false ), + 'Asia/Urumqi' => array( + 'offset' => 28800000, + 'longname' => "China Standard Time", + 'shortname' => 'CST', + 'hasdst' => false ), + 'Australia/Perth' => array( + 'offset' => 28800000, + 'longname' => "Western Standard Time (Australia)", + 'shortname' => 'WST', + 'hasdst' => false ), + 'Australia/West' => array( + 'offset' => 28800000, + 'longname' => "Western Standard Time (Australia)", + 'shortname' => 'WST', + 'hasdst' => false ), + 'CTT' => array( + 'offset' => 28800000, + 'longname' => "China Standard Time", + 'shortname' => 'CST', + 'hasdst' => false ), + 'Etc/GMT-8' => array( + 'offset' => 28800000, + 'longname' => "GMT+08:00", + 'shortname' => 'GMT+08:00', + 'hasdst' => false ), + 'Hongkong' => array( + 'offset' => 28800000, + 'longname' => "Hong Kong Time", + 'shortname' => 'HKT', + 'hasdst' => false ), + 'PRC' => array( + 'offset' => 28800000, + 'longname' => "China Standard Time", + 'shortname' => 'CST', + 'hasdst' => false ), + 'Singapore' => array( + 'offset' => 28800000, + 'longname' => "Singapore Time", + 'shortname' => 'SGT', + 'hasdst' => false ), + 'Asia/Choibalsan' => array( + 'offset' => 32400000, + 'longname' => "Choibalsan Time", + 'shortname' => 'CHOT', + 'hasdst' => false ), + 'Asia/Dili' => array( + 'offset' => 32400000, + 'longname' => "East Timor Time", + 'shortname' => 'TPT', + 'hasdst' => false ), + 'Asia/Jayapura' => array( + 'offset' => 32400000, + 'longname' => "East Indonesia Time", + 'shortname' => 'EIT', + 'hasdst' => false ), + 'Asia/Pyongyang' => array( + 'offset' => 32400000, + 'longname' => "Korea Standard Time", + 'shortname' => 'KST', + 'hasdst' => false ), + 'Asia/Seoul' => array( + 'offset' => 32400000, + 'longname' => "Korea Standard Time", + 'shortname' => 'KST', + 'hasdst' => false ), + 'Asia/Tokyo' => array( + 'offset' => 32400000, + 'longname' => "Japan Standard Time", + 'shortname' => 'JST', + 'hasdst' => false ), + 'Asia/Yakutsk' => array( + 'offset' => 32400000, + 'longname' => "Yakutsk Time", + 'shortname' => 'YAKT', + 'hasdst' => true, + 'dstlongname' => "Yaktsk Summer Time", + 'dstshortname' => 'YAKST' ), + 'Etc/GMT-9' => array( + 'offset' => 32400000, + 'longname' => "GMT+09:00", + 'shortname' => 'GMT+09:00', + 'hasdst' => false ), + 'JST' => array( + 'offset' => 32400000, + 'longname' => "Japan Standard Time", + 'shortname' => 'JST', + 'hasdst' => false ), + 'Japan' => array( + 'offset' => 32400000, + 'longname' => "Japan Standard Time", + 'shortname' => 'JST', + 'hasdst' => false ), + 'Pacific/Palau' => array( + 'offset' => 32400000, + 'longname' => "Palau Time", + 'shortname' => 'PWT', + 'hasdst' => false ), + 'ROK' => array( + 'offset' => 32400000, + 'longname' => "Korea Standard Time", + 'shortname' => 'KST', + 'hasdst' => false ), + 'ACT' => array( + 'offset' => 34200000, + 'longname' => "Central Standard Time (Northern Territory)", + 'shortname' => 'CST', + 'hasdst' => false ), + 'Australia/Adelaide' => array( + 'offset' => 34200000, + 'longname' => "Central Standard Time (South Australia)", + 'shortname' => 'CST', + 'hasdst' => true, + 'dstlongname' => "Central Summer Time (South Australia)", + 'dstshortname' => 'CST' ), + 'Australia/Broken_Hill' => array( + 'offset' => 34200000, + 'longname' => "Central Standard Time (S Australia/NSW)", + 'shortname' => 'CST', + 'hasdst' => true, + 'dstlongname' => "Central Summer Time (S Australia/NSW)", + 'dstshortname' => 'CST' ), + 'Australia/Darwin' => array( + 'offset' => 34200000, + 'longname' => "Central Standard Time (Northern Territory)", + 'shortname' => 'CST', + 'hasdst' => false ), + 'Australia/North' => array( + 'offset' => 34200000, + 'longname' => "Central Standard Time (Northern Territory)", + 'shortname' => 'CST', + 'hasdst' => false ), + 'Australia/South' => array( + 'offset' => 34200000, + 'longname' => "Central Standard Time (South Australia)", + 'shortname' => 'CST', + 'hasdst' => true, + 'dstlongname' => "Central Summer Time (South Australia)", + 'dstshortname' => 'CST' ), + 'Australia/Yancowinna' => array( + 'offset' => 34200000, + 'longname' => "Central Standard Time (S Australia/NSW)", + 'shortname' => 'CST', + 'hasdst' => true, + 'dstlongname' => "Central Summer Time (S Australia/NSW)", + 'dstshortname' => 'CST' ), + 'AET' => array( + 'offset' => 36000000, + 'longname' => "Eastern Standard Time (New South Wales)", + 'shortname' => 'EST', + 'hasdst' => true, + 'dstlongname' => "Eastern Summer Time (New South Wales)", + 'dstshortname' => 'EST' ), + 'Antarctica/DumontDUrville' => array( + 'offset' => 36000000, + 'longname' => "Dumont-d'Urville Time", + 'shortname' => 'DDUT', + 'hasdst' => false ), + 'Asia/Sakhalin' => array( + 'offset' => 36000000, + 'longname' => "Sakhalin Time", + 'shortname' => 'SAKT', + 'hasdst' => true, + 'dstlongname' => "Sakhalin Summer Time", + 'dstshortname' => 'SAKST' ), + 'Asia/Vladivostok' => array( + 'offset' => 36000000, + 'longname' => "Vladivostok Time", + 'shortname' => 'VLAT', + 'hasdst' => true, + 'dstlongname' => "Vladivostok Summer Time", + 'dstshortname' => 'VLAST' ), + 'Australia/ACT' => array( + 'offset' => 36000000, + 'longname' => "Eastern Standard Time (New South Wales)", + 'shortname' => 'EST', + 'hasdst' => true, + 'dstlongname' => "Eastern Summer Time (New South Wales)", + 'dstshortname' => 'EST' ), + 'Australia/Brisbane' => array( + 'offset' => 36000000, + 'longname' => "Eastern Standard Time (Queensland)", + 'shortname' => 'EST', + 'hasdst' => false ), + 'Australia/Canberra' => array( + 'offset' => 36000000, + 'longname' => "Eastern Standard Time (New South Wales)", + 'shortname' => 'EST', + 'hasdst' => true, + 'dstlongname' => "Eastern Summer Time (New South Wales)", + 'dstshortname' => 'EST' ), + 'Australia/Hobart' => array( + 'offset' => 36000000, + 'longname' => "Eastern Standard Time (Tasmania)", + 'shortname' => 'EST', + 'hasdst' => true, + 'dstlongname' => "Eastern Summer Time (Tasmania)", + 'dstshortname' => 'EST' ), + 'Australia/Lindeman' => array( + 'offset' => 36000000, + 'longname' => "Eastern Standard Time (Queensland)", + 'shortname' => 'EST', + 'hasdst' => false ), + 'Australia/Melbourne' => array( + 'offset' => 36000000, + 'longname' => "Eastern Standard Time (Victoria)", + 'shortname' => 'EST', + 'hasdst' => true, + 'dstlongname' => "Eastern Summer Time (Victoria)", + 'dstshortname' => 'EST' ), + 'Australia/NSW' => array( + 'offset' => 36000000, + 'longname' => "Eastern Standard Time (New South Wales)", + 'shortname' => 'EST', + 'hasdst' => true, + 'dstlongname' => "Eastern Summer Time (New South Wales)", + 'dstshortname' => 'EST' ), + 'Australia/Queensland' => array( + 'offset' => 36000000, + 'longname' => "Eastern Standard Time (Queensland)", + 'shortname' => 'EST', + 'hasdst' => false ), + 'Australia/Sydney' => array( + 'offset' => 36000000, + 'longname' => "Eastern Standard Time (New South Wales)", + 'shortname' => 'EST', + 'hasdst' => true, + 'dstlongname' => "Eastern Summer Time (New South Wales)", + 'dstshortname' => 'EST' ), + 'Australia/Tasmania' => array( + 'offset' => 36000000, + 'longname' => "Eastern Standard Time (Tasmania)", + 'shortname' => 'EST', + 'hasdst' => true, + 'dstlongname' => "Eastern Summer Time (Tasmania)", + 'dstshortname' => 'EST' ), + 'Australia/Victoria' => array( + 'offset' => 36000000, + 'longname' => "Eastern Standard Time (Victoria)", + 'shortname' => 'EST', + 'hasdst' => true, + 'dstlongname' => "Eastern Summer Time (Victoria)", + 'dstshortname' => 'EST' ), + 'Etc/GMT-10' => array( + 'offset' => 36000000, + 'longname' => "GMT+10:00", + 'shortname' => 'GMT+10:00', + 'hasdst' => false ), + 'Pacific/Guam' => array( + 'offset' => 36000000, + 'longname' => "Chamorro Standard Time", + 'shortname' => 'ChST', + 'hasdst' => false ), + 'Pacific/Port_Moresby' => array( + 'offset' => 36000000, + 'longname' => "Papua New Guinea Time", + 'shortname' => 'PGT', + 'hasdst' => false ), + 'Pacific/Saipan' => array( + 'offset' => 36000000, + 'longname' => "Chamorro Standard Time", + 'shortname' => 'ChST', + 'hasdst' => false ), + 'Pacific/Truk' => array( + 'offset' => 36000000, + 'longname' => "Truk Time", + 'shortname' => 'TRUT', + 'hasdst' => false ), + 'Pacific/Yap' => array( + 'offset' => 36000000, + 'longname' => "Yap Time", + 'shortname' => 'YAPT', + 'hasdst' => false ), + 'Australia/LHI' => array( + 'offset' => 37800000, + 'longname' => "Load Howe Standard Time", + 'shortname' => 'LHST', + 'hasdst' => true, + 'dstlongname' => "Load Howe Summer Time", + 'dstshortname' => 'LHST' ), + 'Australia/Lord_Howe' => array( + 'offset' => 37800000, + 'longname' => "Load Howe Standard Time", + 'shortname' => 'LHST', + 'hasdst' => true, + 'dstlongname' => "Load Howe Summer Time", + 'dstshortname' => 'LHST' ), + 'Asia/Magadan' => array( + 'offset' => 39600000, + 'longname' => "Magadan Time", + 'shortname' => 'MAGT', + 'hasdst' => true, + 'dstlongname' => "Magadan Summer Time", + 'dstshortname' => 'MAGST' ), + 'Etc/GMT-11' => array( + 'offset' => 39600000, + 'longname' => "GMT+11:00", + 'shortname' => 'GMT+11:00', + 'hasdst' => false ), + 'Pacific/Efate' => array( + 'offset' => 39600000, + 'longname' => "Vanuatu Time", + 'shortname' => 'VUT', + 'hasdst' => false ), + 'Pacific/Guadalcanal' => array( + 'offset' => 39600000, + 'longname' => "Solomon Is. Time", + 'shortname' => 'SBT', + 'hasdst' => false ), + 'Pacific/Kosrae' => array( + 'offset' => 39600000, + 'longname' => "Kosrae Time", + 'shortname' => 'KOST', + 'hasdst' => false ), + 'Pacific/Noumea' => array( + 'offset' => 39600000, + 'longname' => "New Caledonia Time", + 'shortname' => 'NCT', + 'hasdst' => false ), + 'Pacific/Ponape' => array( + 'offset' => 39600000, + 'longname' => "Ponape Time", + 'shortname' => 'PONT', + 'hasdst' => false ), + 'SST' => array( + 'offset' => 39600000, + 'longname' => "Solomon Is. Time", + 'shortname' => 'SBT', + 'hasdst' => false ), + 'Pacific/Norfolk' => array( + 'offset' => 41400000, + 'longname' => "Norfolk Time", + 'shortname' => 'NFT', + 'hasdst' => false ), + 'Antarctica/McMurdo' => array( + 'offset' => 43200000, + 'longname' => "New Zealand Standard Time", + 'shortname' => 'NZST', + 'hasdst' => true, + 'dstlongname' => "New Zealand Daylight Time", + 'dstshortname' => 'NZDT' ), + 'Antarctica/South_Pole' => array( + 'offset' => 43200000, + 'longname' => "New Zealand Standard Time", + 'shortname' => 'NZST', + 'hasdst' => true, + 'dstlongname' => "New Zealand Daylight Time", + 'dstshortname' => 'NZDT' ), + 'Asia/Anadyr' => array( + 'offset' => 43200000, + 'longname' => "Anadyr Time", + 'shortname' => 'ANAT', + 'hasdst' => true, + 'dstlongname' => "Anadyr Summer Time", + 'dstshortname' => 'ANAST' ), + 'Asia/Kamchatka' => array( + 'offset' => 43200000, + 'longname' => "Petropavlovsk-Kamchatski Time", + 'shortname' => 'PETT', + 'hasdst' => true, + 'dstlongname' => "Petropavlovsk-Kamchatski Summer Time", + 'dstshortname' => 'PETST' ), + 'Etc/GMT-12' => array( + 'offset' => 43200000, + 'longname' => "GMT+12:00", + 'shortname' => 'GMT+12:00', + 'hasdst' => false ), + 'Kwajalein' => array( + 'offset' => 43200000, + 'longname' => "Marshall Islands Time", + 'shortname' => 'MHT', + 'hasdst' => false ), + 'NST' => array( + 'offset' => 43200000, + 'longname' => "New Zealand Standard Time", + 'shortname' => 'NZST', + 'hasdst' => true, + 'dstlongname' => "New Zealand Daylight Time", + 'dstshortname' => 'NZDT' ), + 'NZ' => array( + 'offset' => 43200000, + 'longname' => "New Zealand Standard Time", + 'shortname' => 'NZST', + 'hasdst' => true, + 'dstlongname' => "New Zealand Daylight Time", + 'dstshortname' => 'NZDT' ), + 'Pacific/Auckland' => array( + 'offset' => 43200000, + 'longname' => "New Zealand Standard Time", + 'shortname' => 'NZST', + 'hasdst' => true, + 'dstlongname' => "New Zealand Daylight Time", + 'dstshortname' => 'NZDT' ), + 'Pacific/Fiji' => array( + 'offset' => 43200000, + 'longname' => "Fiji Time", + 'shortname' => 'FJT', + 'hasdst' => false ), + 'Pacific/Funafuti' => array( + 'offset' => 43200000, + 'longname' => "Tuvalu Time", + 'shortname' => 'TVT', + 'hasdst' => false ), + 'Pacific/Kwajalein' => array( + 'offset' => 43200000, + 'longname' => "Marshall Islands Time", + 'shortname' => 'MHT', + 'hasdst' => false ), + 'Pacific/Majuro' => array( + 'offset' => 43200000, + 'longname' => "Marshall Islands Time", + 'shortname' => 'MHT', + 'hasdst' => false ), + 'Pacific/Nauru' => array( + 'offset' => 43200000, + 'longname' => "Nauru Time", + 'shortname' => 'NRT', + 'hasdst' => false ), + 'Pacific/Tarawa' => array( + 'offset' => 43200000, + 'longname' => "Gilbert Is. Time", + 'shortname' => 'GILT', + 'hasdst' => false ), + 'Pacific/Wake' => array( + 'offset' => 43200000, + 'longname' => "Wake Time", + 'shortname' => 'WAKT', + 'hasdst' => false ), + 'Pacific/Wallis' => array( + 'offset' => 43200000, + 'longname' => "Wallis & Futuna Time", + 'shortname' => 'WFT', + 'hasdst' => false ), + 'NZ-CHAT' => array( + 'offset' => 45900000, + 'longname' => "Chatham Standard Time", + 'shortname' => 'CHAST', + 'hasdst' => true, + 'dstlongname' => "Chatham Daylight Time", + 'dstshortname' => 'CHADT' ), + 'Pacific/Chatham' => array( + 'offset' => 45900000, + 'longname' => "Chatham Standard Time", + 'shortname' => 'CHAST', + 'hasdst' => true, + 'dstlongname' => "Chatham Daylight Time", + 'dstshortname' => 'CHADT' ), + 'Etc/GMT-13' => array( + 'offset' => 46800000, + 'longname' => "GMT+13:00", + 'shortname' => 'GMT+13:00', + 'hasdst' => false ), + 'Pacific/Enderbury' => array( + 'offset' => 46800000, + 'longname' => "Phoenix Is. Time", + 'shortname' => 'PHOT', + 'hasdst' => false ), + 'Pacific/Tongatapu' => array( + 'offset' => 46800000, + 'longname' => "Tonga Time", + 'shortname' => 'TOT', + 'hasdst' => false ), + 'Etc/GMT-14' => array( + 'offset' => 50400000, + 'longname' => "GMT+14:00", + 'shortname' => 'GMT+14:00', + 'hasdst' => false ), + 'Pacific/Kiritimati' => array( + 'offset' => 50400000, + 'longname' => "Line Is. Time", + 'shortname' => 'LINT', + 'hasdst' => false ) +); +// +// Initialize default timezone +// First try _DATE_TIMEZONE_DEFAULT global, +// then PHP_TZ environment var, then TZ environment var +// +if(isset($_DATE_TIMEZONE_DEFAULT) + && Date_TimeZone::is_validID($_DATE_TIMEZONE_DEFAULT) +) { + Date_TimeZone::setDefault($_DATE_TIMEZONE_DEFAULT); +} elseif (getenv('PHP_TZ') && Date_TimeZone::is_validID(getenv('PHP_TZ'))) { + Date_TimeZone::setDefault(getenv('PHP_TZ')); +} elseif (getenv('TZ') && Date_TimeZone::is_validID(getenv('TZ'))) { + Date_TimeZone::setDefault(getenv('TZ')); +} elseif (Date_TimeZone::is_validID(date('T'))) { + Date_TimeZone::setDefault(date('T')); +} elseif (substr(php_uname(), 0, 7) == "Windows") { + include_once('TimeZoneWindows.php'); + if (isset($_DATE_TIMEZONE_DATA_WINDOWS[date('T')])) { + Date_TimeZone::setDefault($_DATE_TIMEZONE_DATA_WINDOWS[date('T')]); + } else { + Date_TimeZone::setDefault('UTC'); + } +} else { + Date_TimeZone::setDefault('UTC'); +} +// +// END +?> \ No newline at end of file diff --git a/includes/Date/TimeZoneWindows.php b/includes/Date/TimeZoneWindows.php new file mode 100644 index 0000000..d5fdc67 --- /dev/null +++ b/includes/Date/TimeZoneWindows.php @@ -0,0 +1,109 @@ + | +// | | +// +----------------------------------------------------------------------+ +// +// $Id$ +// +// Date_TimeZone Class Windows Support File +// +/** + * This class includes Windows time zone data (from zoneinfo) in the form of a global array, + * $_DATE_TIMEZONE_DATA_WINDOWS. + * + * @author Ross Smith + * @package Date + * @access public + * @version 1.0 + */ +$GLOBALS['_DATE_TIMEZONE_DATA_WINDOWS'] = array( + 'Dateline Standard Time' => 'Etc/GMT+12', # (GMT-12:00) Eniwetok, Kwajalein Dateline Daylight Time + 'Samoa Standard Time' => 'Pacific/Samoa', # (GMT-11:00) Midway Island, Samoa Samoa Daylight Time + 'Hawaiian Standard Time' => 'HST', # (GMT-10:00) Hawaii Hawaiian Daylight Time + 'Alaskan Standard Time' => 'AST', # (GMT-09:00) Alaska Alaskan Daylight Time + 'Pacific Standard Time' => 'PST', # (GMT-08:00) Pacific Time (US & Canada); Tijuana Pacific Daylight Time + 'Mountain Standard Time' => 'MST', # (GMT-07:00) Mountain Time (US & Canada) Mountain Daylight Time + 'US Mountain Standard Time' => 'US/Mountain', # (GMT-07:00) Arizona US Mountain Daylight Time + 'Canada Central Standard Time' => 'Canada/Central', # (GMT-06:00) Saskatchewan Canada Central Daylight Time + 'Mexico Standard Time' => 'Mexico/General', # (GMT-06:00) Mexico City Mexico Daylight Time + 'Central Standard Time' => 'CST', # (GMT-06:00) Central Time (US & Canada) Central Daylight Time + 'Central America Standard Time' => 'CST', # (GMT-06:00) Central America Central America Daylight Time + 'US Eastern Standard Time' => 'EST', # (GMT-05:00) Indiana (East) US Eastern Daylight Time + 'Eastern Standard Time' => 'EST', # (GMT-05:00) Eastern Time (US & Canada) Eastern Daylight Time + 'SA Pacific Standard Time' => 'EST', # (GMT-05:00) Bogota, Lima, Quito SA Pacific Daylight Time + 'Pacific SA Standard Time' => 'America/Anguilla', # (GMT-04:00) Santiago Pacific SA Daylight Time + 'SA Western Standard Time' => 'America/Anguilla', # (GMT-04:00) Caracas, La Paz SA Western Daylight Time + 'Atlantic Standard Time' => 'America/Anguilla', # (GMT-04:00) Atlantic Time (Canada) Atlantic Daylight Time + 'Newfoundland Standard Time' => 'America/St_Johns', # (GMT-03:30) Newfoundland Newfoundland Daylight Time + 'Greenland Standard Time' => 'America/Godthab', # (GMT-03:00) Greenland Greenland Daylight Time + 'SA Eastern Standard Time' => 'America/Araguaina', # (GMT-03:00) Buenos Aires, Georgetown SA Eastern Daylight Time + 'E. South America Standard Time' => 'America/Araguaina', # (GMT-03:00) Brasilia E. South America Daylight Time + 'Mid-Atlantic Standard Time' => 'Atlantic/South_Georgia', # (GMT-02:00) Mid-Atlantic Mid-Atlantic Daylight Time + 'Cape Verde Standard Time' => 'Atlantic/Cape_Verde', # (GMT-01:00) Cape Verde Is. Cape Verde Daylight Time + 'Azores Standard Time' => 'Atlantic/Azores', # (GMT-01:00) Azores Azores Daylight Time + 'Greenwich Standard Time' => 'GMT', # (GMT+00:00) Casablanca, Monrovia Greenwich Daylight Time + 'GMT Standard Time' => 'GMT', # (GMT+00:00) Greenwich Mean Time : Dublin, Edinburgh, Lisbon, London GMT Daylight Time + 'W. Europe Standard Time' => 'ECT', # (GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna W. Europe Daylight Time + 'Central Europe Standard Time' => 'ECT', # (GMT+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague Central Europe Daylight Time + 'Romance Standard Time' => 'ECT', # (GMT+01:00) Brussels, Copenhagen, Madrid, Paris Romance Daylight Time + 'Central European Standard Time' => 'ECT', # (GMT+01:00) Sarajevo, Skopje, Sofija, Vilnius, Warsaw, Zagreb Central European Daylight Time + 'W. Central Africa Standard Time' => 'ECT', # (GMT+01:00) West Central Africa W. Central Africa Daylight Time + 'GTB Standard Time' => 'ART', # (GMT+02:00) Athens, Istanbul, Minsk GTB Daylight Time + 'E. Europe Standard Time' => 'EET', # (GMT+02:00) Bucharest E. Europe Daylight Time + 'Egypt Standard Time' => 'Egypt', # (GMT+02:00) Cairo Egypt Daylight Time + 'South Africa Standard Time' => 'Africa/Johannesburg', # (GMT+02:00) Harare, Pretoria South Africa Daylight Time + 'FLE Standard Time' => 'ART', # (GMT+02:00) Helsinki, Riga, Tallinn FLE Daylight Time + 'Jerusalem Standard Time' => 'Israel', # (GMT+02:00) Jerusalem Jerusalem Daylight Time + 'Arabic Standard Time' => 'Asia/Aden', # (GMT+03:00) Baghdad Arabic Daylight Time + 'Arab Standard Time' => 'Asia/Riyadh', # (GMT+03:00) Kuwait, Riyadh Arab Daylight Time + 'Russian Standard Time' => 'Europe/Moscow', # (GMT+03:00) Moscow, St. Petersburg, Volgograd Russian Daylight Time + 'E. Africa Standard Time' => 'EAT', # (GMT+03:00) Nairobi E. Africa Daylight Time + 'Iran Standard Time' => 'Asia/Tehran', # (GMT+03:30) Tehran Iran Daylight Time + 'Arabian Standard Time' => 'Asia/Dubai', # (GMT+04:00) Abu Dhabi, Muscat Arabian Daylight Time + 'Caucasus Standard Time' => 'Asia/Baku', # (GMT+04:00) Baku, Tbilisi, Yerevan Caucasus Daylight Time + 'Afghanistan Standard Time' => 'Asia/Kabul', # (GMT+04:30) Kabul Afghanistan Daylight Time + 'Ekaterinburg Standard Time' => 'Asia/Yekaterinburg', # (GMT+05:00) Ekaterinburg Ekaterinburg Daylight Time + 'West Asia Standard Time' => 'PLT', # (GMT+05:00) Islamabad, Karachi, Tashkent West Asia Daylight Time + 'India Standard Time' => 'IST', # (GMT+05:30) Calcutta, Chennai, Mumbai, New Delhi India Daylight Time + 'Nepal Standard Time' => 'Asia/Katmandu', # (GMT+05:45) Kathmandu Nepal Daylight Time + 'N. Central Asia Standard Time' => 'Asia/Novosibirsk', # (GMT+06:00) Almaty, Novosibirsk N. Central Asia Daylight Time + 'Central Asia Standard Time' => 'Asia/Dacca', # (GMT+06:00) Astana, Dhaka Central Asia Daylight Time + 'Sri Lanka Standard Time' => 'Asia/Colombo', # (GMT+06:00) Sri Jayawardenepura Sri Lanka Daylight Time + 'Myanmar Standard Time' => 'Asia/Rangoon', # (GMT+06:30) Rangoon Myanmar Daylight Time + 'SE Asia Standard Time' => 'Asia/Bangkok', # (GMT+07:00) Bangkok, Hanoi, Jakarta SE Asia Daylight Time + 'North Asia Standard Time' => 'Asia/Krasnoyarsk', # (GMT+07:00) Krasnoyarsk North Asia Daylight Time + 'China Standard Time' => 'Asia/Chongqing', # (GMT+08:00) Beijing, Chongqing, Hong Kong, Urumqi China Daylight Time + 'North Asia East Standard Time' => 'Asia/Irkutsk', # (GMT+08:00) Irkutsk, Ulaan Bataar North Asia East Daylight Time + 'Malay Peninsula Standard Time' => 'Asia/Kuala_Lumpur', # (GMT+08:00) Kuala Lumpur, Singapore Malay Peninsula Daylight Time + 'W. Australia Standard Time' => 'Australia/Perth', # (GMT+08:00) Perth W. Australia Daylight Time + 'Taipei Standard Time' => 'Asia/Taipei', # (GMT+08:00) Taipei Taipei Daylight Time + 'Tokyo Standard Time' => 'Asia/Tokyo', # (GMT+09:00) Osaka, Sapporo, Tokyo Tokyo Daylight Time + 'Korea Standard Time' => 'Asia/Seoul', # (GMT+09:00) Seoul Korea Daylight Time + 'Yakutsk Standard Time' => 'Asia/Yakutsk', # (GMT+09:00) Yakutsk Yakutsk Daylight Time + 'Cen. Australia Standard Time' => 'Australia/Adelaide', # (GMT+09:30) Adelaide Cen. Australia Daylight Time + 'AUS Central Standard Time' => 'Australia/Darwin', # (GMT+09:30) Darwin AUS Central Daylight Time + 'E. Australia Standard Time' => 'Australia/Brisbane', # (GMT+10:00) Brisbane E. Australia Daylight Time + 'AUS Eastern Standard Time' => 'Australia/Sydney', # (GMT+10:00) Canberra, Melbourne, Sydney AUS Eastern Daylight Time + 'West Pacific Standard Time' => 'Pacific/Guam', # (GMT+10:00) Guam, Port Moresby West Pacific Daylight Time + 'Tasmania Standard Time' => 'Australia/Hobart', # (GMT+10:00) Hobart Tasmania Daylight Time + 'Vladivostok Standard Time' => 'Asia/Vladivostok', # (GMT+10:00) Vladivostok Vladivostok Daylight Time + 'Central Pacific Standard Time' => 'Pacific/Noumea', # (GMT+11:00) Magadan, Solomon Is., New Caledonia Central Pacific Daylight Time + 'New Zealand Standard Time' => 'NST', # (GMT+12:00) Auckland, Wellington New Zealand Daylight Time + 'Fiji Standard Time' => 'Pacific/Fiji', # (GMT+12:00) Fiji, Kamchatka, Marshall Is. Fiji Daylight Time + 'Tonga Standard Time' => 'Pacific/Tongatapu', # (GMT+13:00) Nuku'alofa Tonga Daylight Time +); +?> \ No newline at end of file diff --git a/includes/Date/index.php b/includes/Date/index.php new file mode 100644 index 0000000..80f6d40 --- /dev/null +++ b/includes/Date/index.php @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/includes/PHPAsync.php b/includes/PHPAsync.php new file mode 100644 index 0000000..9249bb3 --- /dev/null +++ b/includes/PHPAsync.php @@ -0,0 +1,236 @@ +asyncProcess = new PHPAsync(); + $async->runProcess( + $yourObject, + 'doSomeWork', + 'getBufferContent', + 'getLogHtml', + ); + + in YourClass::doSomeWork send notifications to the async log like so + + $this->asyncProcess->updateStatus( ); a numeric value, do not include "%" + * + * @package kernel + */ + +/** + * Setup + */ +require_once( UTIL_PKG_PATH.'phpcontrib_lib.php' ); + +// written for bitweaver environment, but you can override +if( !defined( 'PHPASYNC_TEMP_DIR' ) ){ + define( 'PHPASYNC_TEMP_DIR', TEMP_PKG_PATH.'phpasync' ); +} + +/** + * @package kernel + */ +class PHPAsync extends BitBase{ + private $mPid; + + private $mLogFile; + + private $mUpdateLogHandler; + + private $mFileHandle; + + private $mStatus; // pct complete + + // default config + public $mConfig = array(); + + // constructor + public function __construct( $pPidId = NULL, $pConfig = array() ){ + if( !empty( $pPidId ) ){ + // set id + $this->mPid = $pPidId; + + // log file path + $this->mLogFile = PHPASYNC_TEMP_DIR.'/'.$this->mPid; + } + + // set default config values + $this->mConfig = array( + 'append_log' => FALSE, + 'max_execution_time' => "1200", + 'memory_limit' => '128M', + 'no-gzip' => 1, + 'zlib.output_compression' => 0, + 'ignore_user_abort' => FALSE, + ); + + // override default config + if( !empty( $pConfig ) ){ + extract_to( $pConfig, $this->mConfig, EXTR_IF_EXISTS ); + } + + parent::__construct(); + } + + // runProcess + /** + * wraps a class instance method as a async process + * @param $pObject - The object instance whose process is being wrapped + * @param $pProcessHandler - The process being wrapped to run in the background + * @param $pOutputHandler - The output handler is invoked to get whatever page content should be returned to the user before the background process begins + * @param $pUpdateLogHandler - The update log handler is invoked when updateStatus is called by the wrapped process. the object can push customize content into the log by defining this callback + */ + public function runProcess( $pObject, $pProcessHandler, $pProcessHash = NULL, $pOutputHandler, $pUpdateLogHandler = NULL ){ + global $gBitSystem, $gBitSmarty; + + // create file tracking id + $this->mLogFile = $this->genLogFile(); + + $this->mPid = substr( $this->mLogFile, strlen(PHPASYNC_TEMP_DIR.'/') ); + + $this->mProcessObject = $pObject; + + // register log callback + if( !empty( $pUpdateLogHandler ) ){ + $this->mUpdateLogHandler = $pUpdateLogHandler; + } + + // override some apache settings that might screw up flushing the buffer + @apache_setenv('no-gzip', $this->getConfig('no-gzip') ); + @ini_set('zlib.output_compression', $this->getConfig('zlib.output_compression') ); + + // set time and memory + ini_set('max_execution_time', $this->getConfig('max_execution_time') ); + ini_set('memory_limit', $this->getConfig('memory_limit') ); + + // allow disallow process abort + ignore_user_abort( $this->getConfig('ignore_user_abort') ); + + // create the file for writing + $this->mFileHandle = fopen($this->mLogFile, 'wrx+'); + if (! $this->mFileHandle){ + $gBitSystem->fatalError( "Error in PHPAsync: could not create log file - process aborted. Please notify your website developer." ); + } + + // convenience make the pid available to smarty as most output handlers will want access to it + $gBitSmarty->assign( 'pid', $this->mPid ); + + // output Buffered content + $this->outputBuffer( $pObject->$pOutputHandler() ); + + // run the background process + $pObject->$pProcessHandler( $pProcessHash ); + + // clean up + sleep(20); // number of seconds the temp file should remain available for status check before it is deleted + + fclose($this->mFileHandle); + + // delete the progress file + unlink($this->mLogFile); + + //return true if completed + return true; + } + + /** + * this is our output buffer + * send it anything and it will + * send it to the browser + */ + public function outputBuffer( $pContent ){ + ob_start(); + + echo( $pContent ); + + $size = ob_get_length(); + header("Content-Length: $size"); + header('Connection: close'); + + ob_end_flush(); + // ob_flush(); -- worked fine in preliminary test and was suggested by web sample, but causes warning here + flush(); + session_write_close(); + } + + /** + * notifies our running process + * of the percentage of a task completed + */ + public function updateStatus( $pPct, $pLogText = NULL ){ + global $gBitSystem; + + // crap - hack to store pct and msg - no other way to know without storing this info in table or session or something + $logText = !empty( $pLogText )?$pPct.":".$pLogText:$pPct; + + if( (int)$pPct >= 100 ){ + $this->mStatus = 100; + }else{ + $this->mStatus = $pPct; + } + + // if a custom log message handler is registered get the log message from it + if( $func = $this->mUpdateLogHandler ){ + $this->mProcessObject->$func( $pPct ); + } + // rewind to the beginning of the log file if append is false + if( $this->getConfig( 'append_log' ) == FALSE ){ + rewind( $this->mFileHandle ); + ftruncate( $this->mFileHandle, 0 ); //filesize($this->mLogFile)); + } + // update the log file + if( fwrite( $this->mFileHandle, $logText ) == false ){ + $gBitSystem->fatalError( "Error in PHPAsync: Write to log file failed - process aborted. " . error_get_last() ); + exit; + } + } + + /** + * Retrieves the content of the log file. To be used by request checking on the status of the process + */ + public function getStatus(){ + if ( file_exists( $this->mLogFile ) ){ + if(! $progress = file_get_contents($this->mLogFile)){ + header('HTTP/1.1 500 Internal Server Error'); + exit; + } + // crap = hack to get pct and msg - see related hack in updateStatus + $delimpos = strpos( $progress, ':' ); + $pct = substr( $progress, 0, $delimpos ); + $log = substr( $progress, $delimpos+1 ); + return array( 'pct_complete' => $pct, 'log' => $log ); + }else{ + $this->setError( 'get_status', 'log file not found' ); + return FALSE; + } + } + + public function getPidId(){ + if( !empty( $this->mPid ) ){ + return $this->mPid; + } + return NULL; + } + + private function genLogFile(){ + if( !is_dir( PHPASYNC_TEMP_DIR )) { + mkdir_p( PHPASYNC_TEMP_DIR ); + } + return tempnam( PHPASYNC_TEMP_DIR, ''); + } + + private function setLogFile(){ + if( !empty( $this->mPid ) ){ + $this->mLogFile = PHPASYNC_TEMP_DIR.'/'.$this->mPid; + } + } + + private function getConfig( $pKey ){ + if( isset( $this->mConfig[$pKey] ) ){ + return $this->mConfig[$pKey]; + } + return NULL; + } +} diff --git a/includes/bitexcel/BitExcel.php b/includes/bitexcel/BitExcel.php new file mode 100644 index 0000000..19aa2f8 --- /dev/null +++ b/includes/bitexcel/BitExcel.php @@ -0,0 +1,365 @@ +mConfig = array( + 'cache_method' => 'cache_in_memory', + 'auto_name_doc' => TRUE, + 'auto_sanitize_doc_name' => TRUE, + ); + + // override default config + if( !empty( $pConfig ) ){ + extract_to( $pConfig, $this->mConfig, EXTR_IF_EXISTS ); + } + + } + + private function configExcel( $pParamHash ){ + // config PHPExcel + + // cache method + switch( $this->getConfig( 'cache_method' ) ){ + case 'cache_to_discISAM': + $cacheMethod = PHPExcel_CachedObjectStorageFactory::cache_to_discISAM; + break; + case 'cache_in_memory_serialized': + $cacheMethod = PHPExcel_CachedObjectStorageFactory::cache_in_memory_serialized; + break; + case 'cache_in_memory': + default: + $cacheMethod = PHPExcel_CachedObjectStorageFactory::cache_in_memory; + break; + } + PHPExcel_Settings::setCacheStorageMethod($cacheMethod); + + // create excel object + if( !isset( $this->mPHPExcel ) ){ + $this->mPHPExcel = new PHPExcel(); + } + + // set doc properties + $this->mPHPExcel->getProperties()->setTitle( $pParamHash['workbook']['title'] ); + // $this->mPHPExcel->getProperties()->setCreator("Maarten Balliauw"); + // $this->mPHPExcel->getProperties()->setLastModifiedBy("Maarten Balliauw"); + // $this->mPHPExcel->getProperties()->setSubject("Office 2007 XLSX Test Document"); + // $this->mPHPExcel->getProperties()->setDescription("Test document for Office 2007 XLSX, generated using PHP classes."); + } + + /** + * write Excel 2007 file + */ + public function writeWorkbook( $pParamHash ){ + if( $this->verifyWorkbook( $pParamHash ) ){ + // init the excel object + $this->configExcel( $pParamHash ); + + if( !is_dir( EXCEL_TEMP_PATH ) ){ + mkdir_p( EXCEL_TEMP_PATH ); + } + + /* + $pParamHash['workbook'] = array( + 'title' => // document title used inside the document + 'doc_name' => // document doc name + 'worksheets' => array( + array( // worksheet + 'title' => 'foo', // worksheet title + 'rows' => array( + array( // row + => , + => array( , , ), + => array( , array() ), + ), + array( ... ), + array( ... ), + ) + ) + ), + array( ... ) // worksheet + ) + ) + + */ + + // prep doc name + // auto gen doc name + if( $this->getConfig( 'auto_name_doc' ) ){ + $docName = "somedocName"; + // defined doc name + }else{ + // get doc name from + $docName = $pParamHash['workbook']['doc_name']; + // sanitize name + if( $this->getConfig( 'auto_sanitize_doc_name' ) ){ + // replace white spaces + $docName = str_replace( ' ', '-', $docName ); + // strip nasty chars + $docName = preg_replace( '/[&$\?\*\%:\/\\\]/', '', $docName ); + } + } + + // validate filename - we validate in all cases to be certain + // a little hack so we can use validator + $dataHash = array(); + $array1 = array( 'filename' => array( 'excel_file_name' => array(), ), ); + $array2 = array( 'excel_file_name' => $docName ); + LibertyValidator::validate( + $array1, + $array2, + $this, + $dataHash + ); + + // construct the workbook + if( count( $this->getErrors() ) == 0 ){ + foreach( $pParamHash['workbook']['worksheets'] as $index=>$worksheetData ){ + // first sheet is automatically generated, for all others create it + if( $index > 0 ){ + // create sheet + $index = $this->mPHPExcel->getIndex( $this->mPHPExcel->createSheet() ); + } + $this->initWorksheet( $index, $worksheetData ); + $this->updateWorksheet( $index, $worksheetData ); + } + + } + + // no errors then write the doc + if( count( $this->getErrors() ) == 0 ){ + $fullDocName = $docName.'.xlsx'; + $xlsxFile = EXCEL_TEMP_PATH.$fullDocName; + // echo date('H:i:s') . " Write to Excel2007 format\n"; + $objWriter = new PHPExcel_Writer_Excel2007( $this->mPHPExcel ); + $objWriter->save( $xlsxFile ); + // return the doc reference + return $xlsxFile; + } + + // default fail + return FALSE; + } + } + + /** + * updateWorkbook + * adds data to an existing workbook + * @param pFileName - name of the workbook (without the extension) + * @param pParamHash['title'] - name of the worksheet to insert data into + * @param pParamHash['rows'] - data to insert + * @return bool + */ + public function updateWorkbook( $pFileName, $pParamHash ){ + $xlsxFile = EXCEL_TEMP_PATH.$pFileName.".xlsx"; + if( !empty( $pParamHash['title'] ) && !empty( $pParamHash['rows'] ) ){ + if( file_exists( $xlsxFile ) ){ + require_once( EXTERNALS_PKG_PATH.'phpexcel/Classes/PHPExcel/IOFactory.php' ); + if( $this->mPHPExcel = PHPExcel_IOFactory::load( $xlsxFile ) ){ + // @TODO support adding any new sheet - for now we assume top level data has an existing sheet + $index = $this->mPHPExcel->getIndex( $this->mPHPExcel->getSheetByName( $pParmaHash['title'] ) ); + $this->updateWorksheet( $index, $pParamHash ); + // return file path + return $xlsxFile; + }else{ + $this->setError( 'workbook', 'The workbook document failed to load' ); + } + }else{ + $this->setError( 'workbook', 'The workbook document could not be found' ); + } + }else{ + $this->setError( 'worksheet', 'No title or rows data provided' ); + } + return FALSE; + } + + public function saveWorkbook(){ + $xlsxFile = EXCEL_TEMP_PATH.$docName.'.xlsx'; + // echo date('H:i:s') . " Write to Excel2007 format\n"; + $objWriter = new PHPExcel_Writer_Excel2007( $this->mPHPExcel ); + $objWriter->save( $xlsxFile ); + // return the doc reference + return $xlsxFile; + } + + private function verifyWorkbook( $pParamHash ){ + if( empty( $pParamHash['workbook'] ) ){ + $this->setError( 'workbook', 'No workbook data provided' ); + } + if( empty( $pParamHash['workbook']['title'] ) ){ + $this->setError( 'workbook', 'No workbook title provided' ); + } + if( !$this->getConfig('auto_name_doc') && empty( $pParamHash['workbook']['doc_name'] ) ){ + $this->setError( 'workbook', 'No document name provided' ); + } + + // @TODO verify each worksheet has a title and rows + + return ( count($this->getErrors() ) == 0); + } + + private function initWorksheet( $pIndex, $pParamHash ){ + $this->mPHPExcel->setActiveSheetIndex($pIndex); + + // create title + // sanitize sheet titles + $sheetTitle = preg_replace( '/[\[\]\?\*:\/\\\]/', '', $pParamHash['title'] ); + $this->mPHPExcel->getActiveSheet()->setTitle( $sheetTitle ); + + // create column headers from first row of data + $headings = array_keys( ( is_array( current( $pParamHash['rows'] ) )?current($pParamHash['rows']):$pParamHash['rows'] ) ); + + // naturally in excel rows start at 1 + $row = 1; + $this->insertWorksheetRow( $pIndex, $headings, $row ); + } + + private function updateWorksheet( $pIndex, $pParamHash ){ + $this->mPHPExcel->setActiveSheetIndex($pIndex); + + // get next available row + $row = $this->mPHPExcel->getActiveSheet()->getHighestRow();; + $row++; + + // fill in rows + foreach( $pParamHash['rows'] as $rowData ){ + // reset the col and val + $col = 0; + $val = NULL; + // write row + $this->insertWorksheetRow( $pIndex, $rowData, $row ); + $row++; + } + } + + /** + * inserts a row into the active worksheet + * be sure you have set the active worksheet to the one you + * want to insert into + */ + private function insertWorksheetRow( $pIndex, $pParamHash, $pRow=NULL ){ + $this->mPHPExcel->setActiveSheetIndex($pIndex); + + $row = $pRow; + // undefined row get row autoamtically + if( is_null( $pRow ) ){ + if( $row = $this->mPHPExcel->getActiveSheet()->getHighestRow() ){ + $row++; + }else{ + // default first row + $row = 1; + } + } + // insert the data hash + // key of hash can not always be trusted to be numeric so manage col value + $col = 0; + foreach( $pParamHash as $key=>$val ){ + // string insert into cel + if( is_numeric( $val ) || is_string( $val ) || $val == '' ){ + $this->mPHPExcel->getActiveSheet()->setCellValueByColumnAndRow($col, $row, $val); + // array add to worksheet + }elseif( is_array( $val ) ){ + + $hasArrays = array_map( 'is_array', $val ); + $isNumeric = array_map( 'is_numeric', array_keys( $val ) ); + + // array values contain arrays or the keys are strings + if( in_array( TRUE, $hasArrays ) || in_array( FALSE, $isNumeric ) ){ + + $worksheetData = array( 'title'=>$key,'rows'=>$val ); + + // create a worksheet for these values if it doesnt already exist + $sheets = $this->mPHPExcel->getSheetNames(); + // if keys are numeric proceed with filling out sheet + if( !in_array( $key, $sheets ) ){ + $index = $this->mPHPExcel->getIndex( $this->mPHPExcel->createSheet() ); + $this->initWorksheet( $index, $worksheetData ); + // get the existing worksheet; + }else{ + $index = $this->mPHPExcel->getIndex( $this->mPHPExcel->getSheetByName($key) ); + } + + // entried nested in an array we update + if( in_array( TRUE, $hasArrays ) ) { + $this->mPHPExcel->setActiveSheetIndex($index); + $remoteRow1 = $this->mPHPExcel->getActiveSheet()->getHighestRow() + 1; + $this->updateWorksheet( $index, $worksheetData ); + $remoteRow = $this->mPHPExcel->getActiveSheet()->getHighestRow(); // last row inserted + $remoteRowLink = "A".$remoteRow; + if( $remoteRow1 != $remoteRow ){ + $remoteRowLink = "A".$remoteRow1.':A'.$remoteRow; + $remoteRow = $remoteRow1.':'.$remoteRow; + } + // single entry we insert + }else{ + $this->insertWorksheetRow( $index, $val ); + $remoteRow = $this->mPHPExcel->getActiveSheet()->getHighestRow(); // last row inserted + $remoteRowLink = "A".$remoteRow; + } + + // the active sheet was switched so switch back + $this->mPHPExcel->setActiveSheetIndex($pIndex); + $this->mPHPExcel->getActiveSheet()->setCellValueByColumnAndRow($col, $row, tra('See sheet:').$key.' '.tra('row').$remoteRow ); + // create a link to the related sheet + $sheetLink = "sheet://'".$key."'!".$remoteRowLink; + $this->mPHPExcel->getActiveSheet()->getCellByColumnAndRow($col, $row)->getHyperlink()->setUrl($sheetLink); + $this->mPHPExcel->getActiveSheet()->getCellByColumnAndRow($col, $row)->getHyperlink()->setTooltip(tra('Click for details')); + }else{ + // array does not contain arrays and keys are numeric + $val2 = implode( ',', $val ); + $this->mPHPExcel->getActiveSheet()->setCellValueByColumnAndRow($col, $row, $val2); + } + } + $col++; + } + } + + private function getConfig( $pKey ){ + if( isset( $this->mConfig[$pKey] ) ){ + return $this->mConfig[$pKey]; + } + return NULL; + } + +} + diff --git a/includes/bitexcel/BitExcelAsync.php b/includes/bitexcel/BitExcelAsync.php new file mode 100644 index 0000000..ddff6a0 --- /dev/null +++ b/includes/bitexcel/BitExcelAsync.php @@ -0,0 +1,59 @@ + TRUE, + 'memory_limit' => '256M', + ); + $this->mAsync = new PHPAsync( NULL, $config ); + $this->mAsync->runProcess( $this, 'writeWorkbook', $pParamHash, 'getUpdateInitOutput' ); + } + + public function writeWorkbook( $pParamHash ){ + if( $rslt = parent::writeWorkbook( $pParamHash ) ){ + // notify async + $this->mAsync->updateStatus( $rslt ); + }else{ + $this->mAsync->updateStatus( 'There was a problem' ); + } + } + + public function getUpdateInitOutput(){ + return tra( "Generating Export File... please be patient" ); + } + + public function getUpdateStatus( $pPidId ){ + $this->mAsync = new PHPAsync( $pPidId ); + if( $status = $this->mAsync->getStatus() ){ + return $status; + } + return 'Error: '.$this->mAsync->getErrorValue('get_status'); + } +} diff --git a/includes/cufon/cufon-yui.js b/includes/cufon/cufon-yui.js new file mode 100644 index 0000000..6443cb8 --- /dev/null +++ b/includes/cufon/cufon-yui.js @@ -0,0 +1,1379 @@ +/*! + * Copyright (c) 2010 Simo Kinnunen. + * Licensed under the MIT license. + * + * @version 1.10 + */ + +var Cufon = (function() { + + var api = function() { + return api.replace.apply(null, arguments); + }; + + var DOM = api.DOM = { + + ready: (function() { + + var complete = false, readyStatus = { loaded: 1, complete: 1 }; + + var queue = [], perform = function() { + if (complete) return; + complete = true; + for (var fn; fn = queue.shift(); fn()); + }; + + // Gecko, Opera, WebKit r26101+ + + if (document.addEventListener) { + document.addEventListener('DOMContentLoaded', perform, false); + window.addEventListener('pageshow', perform, false); // For cached Gecko pages + } + + // Old WebKit, Internet Explorer + + if (!window.opera && document.readyState) (function() { + readyStatus[document.readyState] ? perform() : setTimeout(arguments.callee, 10); + })(); + + // Internet Explorer + + if (document.readyState && document.createStyleSheet) (function() { + try { + document.body.doScroll('left'); + perform(); + } + catch (e) { + setTimeout(arguments.callee, 1); + } + })(); + + addEvent(window, 'load', perform); // Fallback + + return function(listener) { + if (!arguments.length) perform(); + else complete ? listener() : queue.push(listener); + }; + + })(), + + root: function() { + return document.documentElement || document.body; + } + + }; + + var CSS = api.CSS = { + + Size: function(value, base) { + + this.value = parseFloat(value); + this.unit = String(value).match(/[a-z%]*$/)[0] || 'px'; + + this.convert = function(value) { + return value / base * this.value; + }; + + this.convertFrom = function(value) { + return value / this.value * base; + }; + + this.toString = function() { + return this.value + this.unit; + }; + + }, + + addClass: function(el, className) { + var current = el.className; + el.className = current + (current && ' ') + className; + return el; + }, + + color: cached(function(value) { + var parsed = {}; + parsed.color = value.replace(/^rgba\((.*?),\s*([\d.]+)\)/, function($0, $1, $2) { + parsed.opacity = parseFloat($2); + return 'rgb(' + $1 + ')'; + }); + return parsed; + }), + + // has no direct CSS equivalent. + // @see http://msdn.microsoft.com/en-us/library/system.windows.fontstretches.aspx + fontStretch: cached(function(value) { + if (typeof value == 'number') return value; + if (/%$/.test(value)) return parseFloat(value) / 100; + return { + 'ultra-condensed': 0.5, + 'extra-condensed': 0.625, + condensed: 0.75, + 'semi-condensed': 0.875, + 'semi-expanded': 1.125, + expanded: 1.25, + 'extra-expanded': 1.5, + 'ultra-expanded': 2 + }[value] || 1; + }), + + getStyle: function(el) { + var view = document.defaultView; + if (view && view.getComputedStyle) return new Style(view.getComputedStyle(el, null)); + if (el.currentStyle) return new Style(el.currentStyle); + return new Style(el.style); + }, + + gradient: cached(function(value) { + var gradient = { + id: value, + type: value.match(/^-([a-z]+)-gradient\(/)[1], + stops: [] + }, colors = value.substr(value.indexOf('(')).match(/([\d.]+=)?(#[a-f0-9]+|[a-z]+\(.*?\)|[a-z]+)/ig); + for (var i = 0, l = colors.length, stop; i < l; ++i) { + stop = colors[i].split('=', 2).reverse(); + gradient.stops.push([ stop[1] || i / (l - 1), stop[0] ]); + } + return gradient; + }), + + quotedList: cached(function(value) { + // doesn't work properly with empty quoted strings (""), but + // it's not worth the extra code. + var list = [], re = /\s*((["'])([\s\S]*?[^\\])\2|[^,]+)\s*/g, match; + while (match = re.exec(value)) list.push(match[3] || match[1]); + return list; + }), + + recognizesMedia: cached(function(media) { + var el = document.createElement('style'), sheet, container, supported; + el.type = 'text/css'; + el.media = media; + try { // this is cached anyway + el.appendChild(document.createTextNode('/**/')); + } catch (e) {} + container = elementsByTagName('head')[0]; + container.insertBefore(el, container.firstChild); + sheet = (el.sheet || el.styleSheet); + supported = sheet && !sheet.disabled; + container.removeChild(el); + return supported; + }), + + removeClass: function(el, className) { + var re = RegExp('(?:^|\\s+)' + className + '(?=\\s|$)', 'g'); + el.className = el.className.replace(re, ''); + return el; + }, + + supports: function(property, value) { + var checker = document.createElement('span').style; + if (checker[property] === undefined) return false; + checker[property] = value; + return checker[property] === value; + }, + + textAlign: function(word, style, position, wordCount) { + if (style.get('textAlign') == 'right') { + if (position > 0) word = ' ' + word; + } + else if (position < wordCount - 1) word += ' '; + return word; + }, + + textShadow: cached(function(value) { + if (value == 'none') return null; + var shadows = [], currentShadow = {}, result, offCount = 0; + var re = /(#[a-f0-9]+|[a-z]+\(.*?\)|[a-z]+)|(-?[\d.]+[a-z%]*)|,/ig; + while (result = re.exec(value)) { + if (result[0] == ',') { + shadows.push(currentShadow); + currentShadow = {}; + offCount = 0; + } + else if (result[1]) { + currentShadow.color = result[1]; + } + else { + currentShadow[[ 'offX', 'offY', 'blur' ][offCount++]] = result[2]; + } + } + shadows.push(currentShadow); + return shadows; + }), + + textTransform: (function() { + var map = { + uppercase: function(s) { + return s.toUpperCase(); + }, + lowercase: function(s) { + return s.toLowerCase(); + }, + capitalize: function(s) { + return s.replace(/(?:^|\s)./g, function($0) { + return $0.toUpperCase(); + }); + } + }; + return function(text, style) { + var transform = map[style.get('textTransform')]; + return transform ? transform(text) : text; + }; + })(), + + whiteSpace: (function() { + var ignore = { + inline: 1, + 'inline-block': 1, + 'run-in': 1 + }; + var wsStart = /^\s+/, wsEnd = /\s+$/; + return function(text, style, node, previousElement, simple) { + if (simple) return text.replace(wsStart, '').replace(wsEnd, ''); // @fixme too simple + if (previousElement) { + if (previousElement.nodeName.toLowerCase() == 'br') { + text = text.replace(wsStart, ''); + } + } + if (ignore[style.get('display')]) return text; + if (!node.previousSibling) text = text.replace(wsStart, ''); + if (!node.nextSibling) text = text.replace(wsEnd, ''); + return text; + }; + })() + + }; + + CSS.ready = (function() { + + // don't do anything in Safari 2 (it doesn't recognize any media type) + var complete = !CSS.recognizesMedia('all'), hasLayout = false; + + var queue = [], perform = function() { + complete = true; + for (var fn; fn = queue.shift(); fn()); + }; + + var links = elementsByTagName('link'), styles = elementsByTagName('style'); + + function isContainerReady(el) { + return el.disabled || isSheetReady(el.sheet, el.media || 'screen'); + } + + function isSheetReady(sheet, media) { + // in Opera sheet.disabled is true when it's still loading, + // even though link.disabled is false. they stay in sync if + // set manually. + if (!CSS.recognizesMedia(media || 'all')) return true; + if (!sheet || sheet.disabled) return false; + try { + var rules = sheet.cssRules, rule; + if (rules) { + // needed for Safari 3 and Chrome 1.0. + // in standards-conforming browsers cssRules contains @-rules. + // Chrome 1.0 weirdness: rules[] + // returns the last rule, so a for loop is the only option. + search: for (var i = 0, l = rules.length; rule = rules[i], i < l; ++i) { + switch (rule.type) { + case 2: // @charset + break; + case 3: // @import + if (!isSheetReady(rule.styleSheet, rule.media.mediaText)) return false; + break; + default: + // only @charset can precede @import + break search; + } + } + } + } + catch (e) {} // probably a style sheet from another domain + return true; + } + + function allStylesLoaded() { + // Internet Explorer's style sheet model, there's no need to do anything + if (document.createStyleSheet) return true; + // standards-compliant browsers + var el, i; + for (i = 0; el = links[i]; ++i) { + if (el.rel.toLowerCase() == 'stylesheet' && !isContainerReady(el)) return false; + } + for (i = 0; el = styles[i]; ++i) { + if (!isContainerReady(el)) return false; + } + return true; + } + + DOM.ready(function() { + // getComputedStyle returns null in Gecko if used in an iframe with display: none + if (!hasLayout) hasLayout = CSS.getStyle(document.body).isUsable(); + if (complete || (hasLayout && allStylesLoaded())) perform(); + else setTimeout(arguments.callee, 10); + }); + + return function(listener) { + if (complete) listener(); + else queue.push(listener); + }; + + })(); + + function Font(data) { + + var face = this.face = data.face, wordSeparators = { + '\u0020': 1, + '\u00a0': 1, + '\u3000': 1 + }; + + this.glyphs = (function(glyphs) { + var key, fallbacks = { + '\u2011': '\u002d', + '\u00ad': '\u2011' + }; + for (key in fallbacks) { + if (!hasOwnProperty(fallbacks, key)) continue; + if (!glyphs[key]) glyphs[key] = glyphs[fallbacks[key]]; + } + return glyphs; + })(data.glyphs); + + this.w = data.w; + this.baseSize = parseInt(face['units-per-em'], 10); + + this.family = face['font-family'].toLowerCase(); + this.weight = face['font-weight']; + this.style = face['font-style'] || 'normal'; + + this.viewBox = (function () { + var parts = face.bbox.split(/\s+/); + var box = { + minX: parseInt(parts[0], 10), + minY: parseInt(parts[1], 10), + maxX: parseInt(parts[2], 10), + maxY: parseInt(parts[3], 10) + }; + box.width = box.maxX - box.minX; + box.height = box.maxY - box.minY; + box.toString = function() { + return [ this.minX, this.minY, this.width, this.height ].join(' '); + }; + return box; + })(); + + this.ascent = -parseInt(face.ascent, 10); + this.descent = -parseInt(face.descent, 10); + + this.height = -this.ascent + this.descent; + + this.spacing = function(chars, letterSpacing, wordSpacing) { + var glyphs = this.glyphs, glyph, + kerning, k, + jumps = [], + width = 0, w, + i = -1, j = -1, chr; + while (chr = chars[++i]) { + glyph = glyphs[chr] || this.missingGlyph; + if (!glyph) continue; + if (kerning) { + width -= k = kerning[chr] || 0; + jumps[j] -= k; + } + w = glyph.w; + if (isNaN(w)) w = +this.w; // may have been a String in old fonts + if (w > 0) { + w += letterSpacing; + if (wordSeparators[chr]) w += wordSpacing; + } + width += jumps[++j] = ~~w; // get rid of decimals + kerning = glyph.k; + } + jumps.total = width; + return jumps; + }; + + } + + function FontFamily() { + + var styles = {}, mapping = { + oblique: 'italic', + italic: 'oblique' + }; + + this.add = function(font) { + (styles[font.style] || (styles[font.style] = {}))[font.weight] = font; + }; + + this.get = function(style, weight) { + var weights = styles[style] || styles[mapping[style]] + || styles.normal || styles.italic || styles.oblique; + if (!weights) return null; + // we don't have to worry about "bolder" and "lighter" + // because IE's currentStyle returns a numeric value for it, + // and other browsers use the computed value anyway + weight = { + normal: 400, + bold: 700 + }[weight] || parseInt(weight, 10); + if (weights[weight]) return weights[weight]; + // http://www.w3.org/TR/CSS21/fonts.html#propdef-font-weight + // Gecko uses x99/x01 for lighter/bolder + var up = { + 1: 1, + 99: 0 + }[weight % 100], alts = [], min, max; + if (up === undefined) up = weight > 400; + if (weight == 500) weight = 400; + for (var alt in weights) { + if (!hasOwnProperty(weights, alt)) continue; + alt = parseInt(alt, 10); + if (!min || alt < min) min = alt; + if (!max || alt > max) max = alt; + alts.push(alt); + } + if (weight < min) weight = min; + if (weight > max) weight = max; + alts.sort(function(a, b) { + return (up + ? (a >= weight && b >= weight) ? a < b : a > b + : (a <= weight && b <= weight) ? a > b : a < b) ? -1 : 1; + }); + return weights[alts[0]]; + }; + + } + + function HoverHandler() { + + function contains(node, anotherNode) { + try { + if (node.contains) return node.contains(anotherNode); + return node.compareDocumentPosition(anotherNode) & 16; + } + catch(e) {} // probably a XUL element such as a scrollbar + return false; + } + + function onOverOut(e) { + var related = e.relatedTarget; + // there might be no relatedTarget if the element is right next + // to the window frame + if (related && contains(this, related)) return; + trigger(this, e.type == 'mouseover'); + } + + function onEnterLeave(e) { + trigger(this, e.type == 'mouseenter'); + } + + function trigger(el, hoverState) { + // A timeout is needed so that the event can actually "happen" + // before replace is triggered. This ensures that styles are up + // to date. + setTimeout(function() { + var options = sharedStorage.get(el).options; + api.replace(el, hoverState ? merge(options, options.hover) : options, true); + }, 10); + } + + this.attach = function(el) { + if (el.onmouseenter === undefined) { + addEvent(el, 'mouseover', onOverOut); + addEvent(el, 'mouseout', onOverOut); + } + else { + addEvent(el, 'mouseenter', onEnterLeave); + addEvent(el, 'mouseleave', onEnterLeave); + } + }; + + } + + function ReplaceHistory() { + + var list = [], map = {}; + + function filter(keys) { + var values = [], key; + for (var i = 0; key = keys[i]; ++i) values[i] = list[map[key]]; + return values; + } + + this.add = function(key, args) { + map[key] = list.push(args) - 1; + }; + + this.repeat = function() { + var snapshot = arguments.length ? filter(arguments) : list, args; + for (var i = 0; args = snapshot[i++];) api.replace(args[0], args[1], true); + }; + + } + + function Storage() { + + var map = {}, at = 0; + + function identify(el) { + return el.cufid || (el.cufid = ++at); + } + + this.get = function(el) { + var id = identify(el); + return map[id] || (map[id] = {}); + }; + + } + + function Style(style) { + + var custom = {}, sizes = {}; + + this.extend = function(styles) { + for (var property in styles) { + if (hasOwnProperty(styles, property)) custom[property] = styles[property]; + } + return this; + }; + + this.get = function(property) { + return custom[property] != undefined ? custom[property] : style[property]; + }; + + this.getSize = function(property, base) { + return sizes[property] || (sizes[property] = new CSS.Size(this.get(property), base)); + }; + + this.isUsable = function() { + return !!style; + }; + + } + + function addEvent(el, type, listener) { + if (el.addEventListener) { + el.addEventListener(type, listener, false); + } + else if (el.attachEvent) { + el.attachEvent('on' + type, function() { + return listener.call(el, window.event); + }); + } + } + + function attach(el, options) { + var storage = sharedStorage.get(el); + if (storage.options) return el; + if (options.hover && options.hoverables[el.nodeName.toLowerCase()]) { + hoverHandler.attach(el); + } + storage.options = options; + return el; + } + + function cached(fun) { + var cache = {}; + return function(key) { + if (!hasOwnProperty(cache, key)) cache[key] = fun.apply(null, arguments); + return cache[key]; + }; + } + + function getFont(el, style) { + var families = CSS.quotedList(style.get('fontFamily').toLowerCase()), family; + for (var i = 0; family = families[i]; ++i) { + if (fonts[family]) return fonts[family].get(style.get('fontStyle'), style.get('fontWeight')); + } + return null; + } + + function elementsByTagName(query) { + return document.getElementsByTagName(query); + } + + function hasOwnProperty(obj, property) { + return obj.hasOwnProperty(property); + } + + function merge() { + var merged = {}, arg, key; + for (var i = 0, l = arguments.length; arg = arguments[i], i < l; ++i) { + for (key in arg) { + if (hasOwnProperty(arg, key)) merged[key] = arg[key]; + } + } + return merged; + } + + function process(font, text, style, options, node, el) { + var fragment = document.createDocumentFragment(), processed; + if (text === '') return fragment; + var separate = options.separate; + var parts = text.split(separators[separate]), needsAligning = (separate == 'words'); + if (needsAligning && HAS_BROKEN_REGEXP) { + // @todo figure out a better way to do this + if (/^\s/.test(text)) parts.unshift(''); + if (/\s$/.test(text)) parts.push(''); + } + for (var i = 0, l = parts.length; i < l; ++i) { + processed = engines[options.engine](font, + needsAligning ? CSS.textAlign(parts[i], style, i, l) : parts[i], + style, options, node, el, i < l - 1); + if (processed) fragment.appendChild(processed); + } + return fragment; + } + + function replaceElement(el, options) { + var name = el.nodeName.toLowerCase(); + if (options.ignore[name]) return; + if (options.onBeforeReplace) options.onBeforeReplace(el, options); + if (el.className.indexOf('sudo') > -1 ) el = el.parentNode; + var replace = !options.textless[name], simple = (options.trim === 'simple'); + var style = CSS.getStyle(attach(el, options)).extend(options); + // may cause issues if the element contains other elements + // with larger fontSize, however such cases are rare and can + // be fixed by using a more specific selector + if (parseFloat(style.get('fontSize')) === 0) return; + var font = getFont(el, style), node, type, next, anchor, text, lastElement; + var isShy = options.softHyphens, anyShy = false, pos, shy, reShy = /\u00ad/g; + var modifyText = options.modifyText; + if (!font) return; + for (node = el.firstChild; node; node = next) { + type = node.nodeType; + next = node.nextSibling; + if (replace && type == 3) { + if (isShy && el.nodeName.toLowerCase() != TAG_SHY) { + pos = node.data.indexOf('\u00ad'); + if (pos >= 0) { + node.splitText(pos); + next = node.nextSibling; + next.deleteData(0, 1); + shy = document.createElement(TAG_SHY); + shy.appendChild(document.createTextNode('\u00ad')); + el.insertBefore(shy, next); + next = shy; + anyShy = true; + } + } + // Node.normalize() is broken in IE 6, 7, 8 + if (anchor) { + anchor.appendData(node.data); + el.removeChild(node); + } + else anchor = node; + if (next) continue; + } + if (anchor) { + text = anchor.data; + if (!isShy) text = text.replace(reShy, ''); + text = CSS.whiteSpace(text, style, anchor, lastElement, simple); + // modify text only on the first replace + if (modifyText) text = modifyText(text, anchor, el, options); + el.replaceChild(process(font, text, style, options, node, el), anchor); + anchor = null; + } + if (type == 1) { + if (node.firstChild) { + if (node.nodeName.toLowerCase() == 'cufon') { + engines[options.engine](font, null, style, options, node, el); + } + else arguments.callee(node, options); + } + lastElement = node; + } + } + if (isShy && anyShy) { + updateShy(el); + if (!trackingShy) addEvent(window, 'resize', updateShyOnResize); + trackingShy = true; + } + if (options.onAfterReplace) options.onAfterReplace(el, options); + } + + function updateShy(context) { + var shys, shy, parent, glue, newGlue, next, prev, i; + shys = context.getElementsByTagName(TAG_SHY); + // unfortunately there doesn't seem to be any easy + // way to avoid having to loop through the shys twice. + for (i = 0; shy = shys[i]; ++i) { + shy.className = C_SHY_DISABLED; + glue = parent = shy.parentNode; + if (glue.nodeName.toLowerCase() != TAG_GLUE) { + newGlue = document.createElement(TAG_GLUE); + newGlue.appendChild(shy.previousSibling); + parent.insertBefore(newGlue, shy); + newGlue.appendChild(shy); + } + else { + // get rid of double glue (edge case fix) + glue = glue.parentNode; + if (glue.nodeName.toLowerCase() == TAG_GLUE) { + parent = glue.parentNode; + while (glue.firstChild) { + parent.insertBefore(glue.firstChild, glue); + } + parent.removeChild(glue); + } + } + } + for (i = 0; shy = shys[i]; ++i) { + shy.className = ''; + glue = shy.parentNode; + parent = glue.parentNode; + next = glue.nextSibling || parent.nextSibling; + // make sure we're comparing same types + prev = (next.nodeName.toLowerCase() == TAG_GLUE) ? glue : shy.previousSibling; + if (prev.offsetTop >= next.offsetTop) { + shy.className = C_SHY_DISABLED; + if (prev.offsetTop < next.offsetTop) { + // we have an annoying edge case, double the glue + newGlue = document.createElement(TAG_GLUE); + parent.insertBefore(newGlue, glue); + newGlue.appendChild(glue); + newGlue.appendChild(next); + } + } + } + } + + function updateShyOnResize() { + if (ignoreResize) return; // needed for IE + CSS.addClass(DOM.root(), C_VIEWPORT_RESIZING); + clearTimeout(shyTimer); + shyTimer = setTimeout(function() { + ignoreResize = true; + CSS.removeClass(DOM.root(), C_VIEWPORT_RESIZING); + updateShy(document); + ignoreResize = false; + }, 100); + } + + var HAS_BROKEN_REGEXP = ' '.split(/\s+/).length == 0; + var TAG_GLUE = 'cufonglue'; + var TAG_SHY = 'cufonshy'; + var C_SHY_DISABLED = 'cufon-shy-disabled'; + var C_VIEWPORT_RESIZING = 'cufon-viewport-resizing'; + + var sharedStorage = new Storage(); + var hoverHandler = new HoverHandler(); + var replaceHistory = new ReplaceHistory(); + var initialized = false; + var trackingShy = false; + var shyTimer; + var ignoreResize = false; + + var engines = {}, fonts = {}, defaultOptions = { + autoDetect: false, + engine: null, + //fontScale: 1, + //fontScaling: false, + forceHitArea: false, + hover: false, + hoverables: { + a: true + }, + ignore: { + applet: 1, + canvas: 1, + col: 1, + colgroup: 1, + head: 1, + iframe: 1, + map: 1, + noscript: 1, + optgroup: 1, + option: 1, + script: 1, + select: 1, + style: 1, + textarea: 1, + title: 1, + pre: 1 + }, + modifyText: null, + onAfterReplace: null, + onBeforeReplace: null, + printable: true, + //rotation: 0, + //selectable: false, + selector: ( + window.Sizzle + || (window.jQuery && function(query) { return jQuery(query); }) // avoid noConflict issues + || (window.dojo && dojo.query) + || (window.glow && glow.dom && glow.dom.get) + || (window.Ext && Ext.query) + || (window.YAHOO && YAHOO.util && YAHOO.util.Selector && YAHOO.util.Selector.query) + || (window.$$ && function(query) { return $$(query); }) + || (window.$ && function(query) { return $(query); }) + || (document.querySelectorAll && function(query) { return document.querySelectorAll(query); }) + || elementsByTagName + ), + separate: 'words', // 'none' and 'characters' are also accepted + softHyphens: true, + textless: { + dl: 1, + html: 1, + ol: 1, + table: 1, + tbody: 1, + thead: 1, + tfoot: 1, + tr: 1, + ul: 1 + }, + textShadow: 'none', + trim: 'advanced' + }; + + var separators = { + // The first pattern may cause unicode characters above + // code point 255 to be removed in Safari 3.0. Luckily enough + // Safari 3.0 does not include non-breaking spaces in \s, so + // we can just use a simple alternative pattern. + words: /\s/.test('\u00a0') ? /[^\S\u00a0]+/ : /\s+/, + characters: '', + none: /^/ + }; + + api.now = function() { + DOM.ready(); + return api; + }; + + api.refresh = function() { + replaceHistory.repeat.apply(replaceHistory, arguments); + return api; + }; + + api.registerEngine = function(id, engine) { + if (!engine) return api; + engines[id] = engine; + return api.set('engine', id); + }; + + api.registerFont = function(data) { + if (!data) return api; + var font = new Font(data), family = font.family; + if (!fonts[family]) fonts[family] = new FontFamily(); + fonts[family].add(font); + return api.set('fontFamily', '"' + family + '"'); + }; + + api.replace = function(elements, options, ignoreHistory) { + options = merge(defaultOptions, options); + if (!options.engine) return api; // there's no browser support so we'll just stop here + if (!initialized) { + CSS.addClass(DOM.root(), 'cufon-active cufon-loading'); + CSS.ready(function() { + // fires before any replace() calls, but it doesn't really matter + CSS.addClass(CSS.removeClass(DOM.root(), 'cufon-loading'), 'cufon-ready'); + }); + initialized = true; + } + if (options.hover) options.forceHitArea = true; + if (options.autoDetect) delete options.fontFamily; + if (typeof options.textShadow == 'string') { + options.textShadow = CSS.textShadow(options.textShadow); + } + if (typeof options.color == 'string' && /^-/.test(options.color)) { + options.textGradient = CSS.gradient(options.color); + } + else delete options.textGradient; + if (!ignoreHistory) replaceHistory.add(elements, arguments); + if (elements.nodeType || typeof elements == 'string') elements = [ elements ]; + CSS.ready(function() { + for (var i = 0, l = elements.length; i < l; ++i) { + var el = elements[i]; + if (typeof el == 'string') api.replace(options.selector(el), options, true); + else replaceElement(el, options); + } + }); + return api; + }; + + api.set = function(option, value) { + defaultOptions[option] = value; + return api; + }; + + return api; + +})(); + +Cufon.registerEngine('vml', (function() { + + var ns = document.namespaces; + if (!ns) return; + ns.add('cvml', 'urn:schemas-microsoft-com:vml'); + ns = null; + + var check = document.createElement('cvml:shape'); + check.style.behavior = 'url(#default#VML)'; + if (!check.coordsize) return; // VML isn't supported + check = null; + + var HAS_BROKEN_LINEHEIGHT = (document.documentMode || 0) < 8; + + document.write(('').replace(/;/g, '!important;')); + + function getFontSizeInPixels(el, value) { + return getSizeInPixels(el, /(?:em|ex|%)$|^[a-z-]+$/i.test(value) ? '1em' : value); + } + + // Original by Dead Edwards. + // Combined with getFontSizeInPixels it also works with relative units. + function getSizeInPixels(el, value) { + if (!isNaN(value) || /px$/i.test(value)) return parseFloat(value); + var style = el.style.left, runtimeStyle = el.runtimeStyle.left; + el.runtimeStyle.left = el.currentStyle.left; + el.style.left = value.replace('%', 'em'); + var result = el.style.pixelLeft; + el.style.left = style; + el.runtimeStyle.left = runtimeStyle; + return result; + } + + function getSpacingValue(el, style, size, property) { + var key = 'computed' + property, value = style[key]; + if (isNaN(value)) { + value = style.get(property); + style[key] = value = (value == 'normal') ? 0 : ~~size.convertFrom(getSizeInPixels(el, value)); + } + return value; + } + + var fills = {}; + + function gradientFill(gradient) { + var id = gradient.id; + if (!fills[id]) { + var stops = gradient.stops, fill = document.createElement('cvml:fill'), colors = []; + fill.type = 'gradient'; + fill.angle = 180; + fill.focus = '0'; + fill.method = 'none'; + fill.color = stops[0][1]; + for (var j = 1, k = stops.length - 1; j < k; ++j) { + colors.push(stops[j][0] * 100 + '% ' + stops[j][1]); + } + fill.colors = colors.join(','); + fill.color2 = stops[k][1]; + fills[id] = fill; + } + return fills[id]; + } + + return function(font, text, style, options, node, el, hasNext) { + + var redraw = (text === null); + + if (redraw) text = node.alt; + + var viewBox = font.viewBox; + + var size = style.computedFontSize || (style.computedFontSize = new Cufon.CSS.Size(getFontSizeInPixels(el, style.get('fontSize')) + 'px', font.baseSize)); + + var wrapper, canvas; + + if (redraw) { + wrapper = node; + canvas = node.firstChild; + } + else { + wrapper = document.createElement('cufon'); + wrapper.className = 'cufon cufon-vml'; + wrapper.alt = text; + + canvas = document.createElement('cufoncanvas'); + wrapper.appendChild(canvas); + + if (options.printable) { + var print = document.createElement('cufontext'); + print.appendChild(document.createTextNode(text)); + wrapper.appendChild(print); + } + + // ie6, for some reason, has trouble rendering the last VML element in the document. + // we can work around this by injecting a dummy element where needed. + // @todo find a better solution + if (!hasNext) wrapper.appendChild(document.createElement('cvml:shape')); + } + + var wStyle = wrapper.style; + var cStyle = canvas.style; + + var height = size.convert(viewBox.height), roundedHeight = Math.ceil(height); + var roundingFactor = roundedHeight / height; + var stretchFactor = roundingFactor * Cufon.CSS.fontStretch(style.get('fontStretch')); + var minX = viewBox.minX, minY = viewBox.minY; + + cStyle.height = roundedHeight; + cStyle.top = Math.round(size.convert(minY - font.ascent)); + cStyle.left = Math.round(size.convert(minX)); + + wStyle.height = size.convert(font.height) + 'px'; + + var color = style.get('color'); + var chars = Cufon.CSS.textTransform(text, style).split(''); + + var jumps = font.spacing(chars, + getSpacingValue(el, style, size, 'letterSpacing'), + getSpacingValue(el, style, size, 'wordSpacing') + ); + + if (!jumps.length) return null; + + var width = jumps.total; + var fullWidth = -minX + width + (viewBox.width - jumps[jumps.length - 1]); + + var shapeWidth = size.convert(fullWidth * stretchFactor), roundedShapeWidth = Math.round(shapeWidth); + + var coordSize = fullWidth + ',' + viewBox.height, coordOrigin; + var stretch = 'r' + coordSize + 'ns'; + + var fill = options.textGradient && gradientFill(options.textGradient); + + var glyphs = font.glyphs, offsetX = 0; + var shadows = options.textShadow; + var i = -1, j = 0, chr; + + while (chr = chars[++i]) { + + var glyph = glyphs[chars[i]] || font.missingGlyph, shape; + if (!glyph) continue; + + if (redraw) { + // some glyphs may be missing so we can't use i + shape = canvas.childNodes[j]; + while (shape.firstChild) shape.removeChild(shape.firstChild); // shadow, fill + } + else { + shape = document.createElement('cvml:shape'); + canvas.appendChild(shape); + } + + shape.stroked = 'f'; + shape.coordsize = coordSize; + shape.coordorigin = coordOrigin = (minX - offsetX) + ',' + minY; + shape.path = (glyph.d ? 'm' + glyph.d + 'xe' : '') + 'm' + coordOrigin + stretch; + shape.fillcolor = color; + + if (fill) shape.appendChild(fill.cloneNode(false)); + + // it's important to not set top/left or IE8 will grind to a halt + var sStyle = shape.style; + sStyle.width = roundedShapeWidth; + sStyle.height = roundedHeight; + + if (shadows) { + // due to the limitations of the VML shadow element there + // can only be two visible shadows. opacity is shared + // for all shadows. + var shadow1 = shadows[0], shadow2 = shadows[1]; + var color1 = Cufon.CSS.color(shadow1.color), color2; + var shadow = document.createElement('cvml:shadow'); + shadow.on = 't'; + shadow.color = color1.color; + shadow.offset = shadow1.offX + ',' + shadow1.offY; + if (shadow2) { + color2 = Cufon.CSS.color(shadow2.color); + shadow.type = 'double'; + shadow.color2 = color2.color; + shadow.offset2 = shadow2.offX + ',' + shadow2.offY; + } + shadow.opacity = color1.opacity || (color2 && color2.opacity) || 1; + shape.appendChild(shadow); + } + + offsetX += jumps[j++]; + } + + // addresses flickering issues on :hover + + var cover = shape.nextSibling, coverFill, vStyle; + + if (options.forceHitArea) { + + if (!cover) { + cover = document.createElement('cvml:rect'); + cover.stroked = 'f'; + cover.className = 'cufon-vml-cover'; + coverFill = document.createElement('cvml:fill'); + coverFill.opacity = 0; + cover.appendChild(coverFill); + canvas.appendChild(cover); + } + + vStyle = cover.style; + + vStyle.width = roundedShapeWidth; + vStyle.height = roundedHeight; + + } + else if (cover) canvas.removeChild(cover); + + wStyle.width = Math.max(Math.ceil(size.convert(width * stretchFactor)), 0); + + if (HAS_BROKEN_LINEHEIGHT) { + + var yAdjust = style.computedYAdjust; + + if (yAdjust === undefined) { + var lineHeight = style.get('lineHeight'); + if (lineHeight == 'normal') lineHeight = '1em'; + else if (!isNaN(lineHeight)) lineHeight += 'em'; // no unit + style.computedYAdjust = yAdjust = 0.5 * (getSizeInPixels(el, lineHeight) - parseFloat(wStyle.height)); + } + + if (yAdjust) { + wStyle.marginTop = Math.ceil(yAdjust) + 'px'; + wStyle.marginBottom = yAdjust + 'px'; + } + + } + + return wrapper; + + }; + +})()); + +Cufon.registerEngine('canvas', (function() { + + // Safari 2 doesn't support .apply() on native methods + + var check = document.createElement('canvas'); + if (!check || !check.getContext || !check.getContext.apply) return; + check = null; + + var HAS_INLINE_BLOCK = Cufon.CSS.supports('display', 'inline-block'); + + // Firefox 2 w/ non-strict doctype (almost standards mode) + var HAS_BROKEN_LINEHEIGHT = !HAS_INLINE_BLOCK && (document.compatMode == 'BackCompat' || /frameset|transitional/i.test(document.doctype.publicId)); + + var styleSheet = document.createElement('style'); + styleSheet.type = 'text/css'; + styleSheet.appendChild(document.createTextNode(( + 'cufon{text-indent:0;}' + + '@media screen,projection{' + + 'cufon{display:inline;display:inline-block;position:relative;vertical-align:middle;' + + (HAS_BROKEN_LINEHEIGHT + ? '' + : 'font-size:1px;line-height:1px;') + + '}cufon cufontext{display:-moz-inline-box;display:inline-block;width:0;height:0;text-align:left;text-indent:-10000in;}' + + (HAS_INLINE_BLOCK + ? 'cufon canvas{position:relative;}' + : 'cufon canvas{position:absolute;}') + + 'cufonshy.cufon-shy-disabled,.cufon-viewport-resizing cufonshy{display:none;}' + + 'cufonglue{white-space:nowrap;display:inline-block;}' + + '.cufon-viewport-resizing cufonglue{white-space:normal;}' + + '}' + + '@media print{' + + 'cufon{padding:0;}' + // Firefox 2 + 'cufon canvas{display:none;}' + + '}' + ).replace(/;/g, '!important;'))); + document.getElementsByTagName('head')[0].appendChild(styleSheet); + + function generateFromVML(path, context) { + var atX = 0, atY = 0; + var code = [], re = /([mrvxe])([^a-z]*)/g, match; + generate: for (var i = 0; match = re.exec(path); ++i) { + var c = match[2].split(','); + switch (match[1]) { + case 'v': + code[i] = { m: 'bezierCurveTo', a: [ atX + ~~c[0], atY + ~~c[1], atX + ~~c[2], atY + ~~c[3], atX += ~~c[4], atY += ~~c[5] ] }; + break; + case 'r': + code[i] = { m: 'lineTo', a: [ atX += ~~c[0], atY += ~~c[1] ] }; + break; + case 'm': + code[i] = { m: 'moveTo', a: [ atX = ~~c[0], atY = ~~c[1] ] }; + break; + case 'x': + code[i] = { m: 'closePath' }; + break; + case 'e': + break generate; + } + context[code[i].m].apply(context, code[i].a); + } + return code; + } + + function interpret(code, context) { + for (var i = 0, l = code.length; i < l; ++i) { + var line = code[i]; + context[line.m].apply(context, line.a); + } + } + + return function(font, text, style, options, node, el) { + + var redraw = (text === null); + + if (redraw) text = node.getAttribute('alt'); + + var viewBox = font.viewBox; + + var size = style.getSize('fontSize', font.baseSize); + + var expandTop = 0, expandRight = 0, expandBottom = 0, expandLeft = 0; + var shadows = options.textShadow, shadowOffsets = []; + if (shadows) { + for (var i = shadows.length; i--;) { + var shadow = shadows[i]; + var x = size.convertFrom(parseFloat(shadow.offX)); + var y = size.convertFrom(parseFloat(shadow.offY)); + shadowOffsets[i] = [ x, y ]; + if (y < expandTop) expandTop = y; + if (x > expandRight) expandRight = x; + if (y > expandBottom) expandBottom = y; + if (x < expandLeft) expandLeft = x; + } + } + + var chars = Cufon.CSS.textTransform(text, style).split(''); + + var jumps = font.spacing(chars, + ~~size.convertFrom(parseFloat(style.get('letterSpacing')) || 0), + ~~size.convertFrom(parseFloat(style.get('wordSpacing')) || 0) + ); + + if (!jumps.length) return null; // there's nothing to render + + var width = jumps.total; + + expandRight += viewBox.width - jumps[jumps.length - 1]; + expandLeft += viewBox.minX; + + var wrapper, canvas; + + if (redraw) { + wrapper = node; + canvas = node.firstChild; + } + else { + wrapper = document.createElement('cufon'); + wrapper.className = 'cufon cufon-canvas'; + wrapper.setAttribute('alt', text); + + canvas = document.createElement('canvas'); + wrapper.appendChild(canvas); + + if (options.printable) { + var print = document.createElement('cufontext'); + print.appendChild(document.createTextNode(text)); + wrapper.appendChild(print); + } + } + + var wStyle = wrapper.style; + var cStyle = canvas.style; + + var height = size.convert(viewBox.height); + var roundedHeight = Math.ceil(height); + var roundingFactor = roundedHeight / height; + var stretchFactor = roundingFactor * Cufon.CSS.fontStretch(style.get('fontStretch')); + var stretchedWidth = width * stretchFactor; + + var canvasWidth = Math.ceil(size.convert(stretchedWidth + expandRight - expandLeft)); + var canvasHeight = Math.ceil(size.convert(viewBox.height - expandTop + expandBottom)); + + canvas.width = canvasWidth; + canvas.height = canvasHeight; + + // needed for WebKit and full page zoom + cStyle.width = canvasWidth + 'px'; + cStyle.height = canvasHeight + 'px'; + + // minY has no part in canvas.height + expandTop += viewBox.minY; + + cStyle.top = Math.round(size.convert(expandTop - font.ascent)) + 'px'; + cStyle.left = Math.round(size.convert(expandLeft)) + 'px'; + + var wrapperWidth = Math.max(Math.ceil(size.convert(stretchedWidth)), 0) + 'px'; + + if (HAS_INLINE_BLOCK) { + wStyle.width = wrapperWidth; + wStyle.height = size.convert(font.height) + 'px'; + } + else { + wStyle.paddingLeft = wrapperWidth; + wStyle.paddingBottom = (size.convert(font.height) - 1) + 'px'; + } + + var g = canvas.getContext('2d'), scale = height / viewBox.height; + + // proper horizontal scaling is performed later + g.scale(scale, scale * roundingFactor); + g.translate(-expandLeft, -expandTop); + g.save(); + + function renderText() { + var glyphs = font.glyphs, glyph, i = -1, j = -1, chr; + g.scale(stretchFactor, 1); + while (chr = chars[++i]) { + var glyph = glyphs[chars[i]] || font.missingGlyph; + if (!glyph) continue; + if (glyph.d) { + g.beginPath(); + if (glyph.code) interpret(glyph.code, g); + else glyph.code = generateFromVML('m' + glyph.d, g); + g.fill(); + } + g.translate(jumps[++j], 0); + } + g.restore(); + } + + if (shadows) { + for (var i = shadows.length; i--;) { + var shadow = shadows[i]; + g.save(); + g.fillStyle = shadow.color; + g.translate.apply(g, shadowOffsets[i]); + renderText(); + } + } + + var gradient = options.textGradient; + if (gradient) { + var stops = gradient.stops, fill = g.createLinearGradient(0, viewBox.minY, 0, viewBox.maxY); + for (var i = 0, l = stops.length; i < l; ++i) { + fill.addColorStop.apply(fill, stops[i]); + } + g.fillStyle = fill; + } + else g.fillStyle = style.get('color'); + + renderText(); + + return wrapper; + + }; + +})()); \ No newline at end of file diff --git a/includes/dBug/dBug.php b/includes/dBug/dBug.php new file mode 100644 index 0000000..ed0ffa2 --- /dev/null +++ b/includes/dBug/dBug.php @@ -0,0 +1,531 @@ +initJSandCSS(); + } + $arrAccept=array("array","object","xml"); //array of variable types that can be "forced" + $this->bCollapsed = $bCollapsed; + if(in_array($forceType,$arrAccept)) + $this->{"varIs".ucfirst($forceType)}($var); + else + $this->checkType($var); + } + + //get variable name + function getVariableName() { + $arrBacktrace = debug_backtrace(); + + //possible 'included' functions + $arrInclude = array("include","include_once","require","require_once"); + + //check for any included/required files. if found, get array of the last included file (they contain the right line numbers) + for($i=count($arrBacktrace)-1; $i>=0; $i--) { + $arrCurrent = $arrBacktrace[$i]; + if(array_key_exists("function", $arrCurrent) && + (in_array($arrCurrent["function"], $arrInclude) || (0 != strcasecmp($arrCurrent["function"], "dbug")))) + continue; + + $arrFile = $arrCurrent; + + break; + } + + if(isset($arrFile)) { + $arrLines = file($arrFile["file"]); + $code = $arrLines[($arrFile["line"]-1)]; + + //find call to dBug class + preg_match('/\bnew dBug\s*\(\s*(.+)\s*\);/i', $code, $arrMatches); + + return $arrMatches[1]; + } + return ""; + } + + //create the main table header + function makeTableHeader($type,$header,$colspan=2) { + if(!$this->bInitialized) { + $header = $this->getVariableName() . " (" . $header . ")"; + $this->bInitialized = true; + } + $str_i = ($this->bCollapsed) ? "style=\"font-style:italic\" " : ""; + + echo " + + + "; + } + + //create the table row header + function makeTDHeader($type,$header) { + $str_d = ($this->bCollapsed) ? " style=\"display:none\"" : ""; + echo " + + \n"; + } + + //error + function error($type) { + $error="Error: Variable cannot be a"; + // this just checks if the type starts with a vowel or "x" and displays either "a" or "an" + if(in_array(substr($type,0,1),array("a","e","i","o","u","x"))) + $error.="n"; + return ($error." ".$type." type"); + } + + //check variable type + function checkType($var) { + switch(gettype($var)) { + case "resource": + $this->varIsResource($var); + break; + case "object": + $this->varIsObject($var); + break; + case "array": + $this->varIsArray($var); + break; + case "NULL": + $this->varIsNULL(); + break; + case "boolean": + $this->varIsBoolean($var); + break; + default: + $var=($var=="") ? "[empty string]" : $var; + echo "
".$header."
".$header.""; + } + + //close table row + function closeTDRow() { + return "
\n\n\n
".htmlentities($var)."
\n"; + break; + } + } + + //if variable is a NULL type + function varIsNULL() { + echo "NULL"; + } + + //if variable is a boolean type + function varIsBoolean($var) { + $var=($var==1) ? "TRUE" : "FALSE"; + echo $var; + } + + //if variable is an array type + function varIsArray($var) { + $var_ser = serialize($var); + array_push($this->arrHistory, $var_ser); + + $this->makeTableHeader("array", (empty($var)?"empty array":"array")); + if(is_array($var)) { + foreach($var as $key=>$value) { + $this->makeTDHeader("array",$key); + + //check for recursion + if(is_array($value)) { + $var_ser = serialize($value); + if(in_array($var_ser, $this->arrHistory, TRUE)) + $value = "*RECURSION*"; + } + + if(in_array(gettype($value),$this->arrType)) + $this->checkType($value); + else { + $value=(trim($value)=="") ? "[empty string]" : $value; + echo htmlentities($value); + } + echo $this->closeTDRow(); + } + } + else echo "".$this->error("array").$this->closeTDRow(); + array_pop($this->arrHistory); + echo ""; + } + + //if variable is an object type + function varIsObject($var) { + $var_ser = serialize($var); + array_push($this->arrHistory, $var_ser); + $this->makeTableHeader("object","object"); + + if(is_object($var)) { + $arrObjVars=get_object_vars($var); + foreach($arrObjVars as $key=>$value) { + + $value=(!is_object($value) && !is_array($value) && trim($value)=="") ? "[empty string]" : $value; + $this->makeTDHeader("object",$key); + + //check for recursion + if(is_object($value)||is_array($value)) { + $var_ser = serialize($value); + if(in_array($var_ser, $this->arrHistory, TRUE)) { + $value = (is_object($value)) ? "*RECURSION* -> $".get_class($value) : "*RECURSION*"; + + } + } + if(in_array(gettype($value),$this->arrType)) + $this->checkType($value); + else echo $value; + echo $this->closeTDRow(); + } + $arrObjMethods=get_class_methods(get_class($var)); + foreach($arrObjMethods as $key=>$value) { + $this->makeTDHeader("object",$value); + echo "[function]".$this->closeTDRow(); + } + } + else echo "".$this->error("object").$this->closeTDRow(); + array_pop($this->arrHistory); + echo ""; + } + + //if variable is a resource type + function varIsResource($var) { + $this->makeTableHeader("resourceC","resource",1); + echo "\n\n"; + switch(get_resource_type($var)) { + case "fbsql result": + case "mssql result": + case "msql query": + case "pgsql result": + case "sybase-db result": + case "sybase-ct result": + case "mysql result": + $db=current(explode(" ",get_resource_type($var))); + $this->varIsDBResource($var,$db); + break; + case "gd": + $this->varIsGDResource($var); + break; + case "xml": + $this->varIsXmlResource($var); + break; + default: + echo get_resource_type($var).$this->closeTDRow(); + break; + } + echo $this->closeTDRow()."\n"; + } + + //if variable is a database resource type + function varIsDBResource($var,$db="mysql") { + if($db == "pgsql") + $db = "pg"; + if($db == "sybase-db" || $db == "sybase-ct") + $db = "sybase"; + $arrFields = array("name","type","flags"); + $numrows=call_user_func($db."_num_rows",$var); + $numfields=call_user_func($db."_num_fields",$var); + $this->makeTableHeader("resource",$db." result",$numfields+1); + echo " "; + for($i=0;$i<$numfields;$i++) { + $field_header = ""; + for($j=0; $j".$field_name.""; + } + echo ""; + for($i=0;$i<$numrows;$i++) { + $row=call_user_func($db."_fetch_array",$var,constant(strtoupper($db)."_ASSOC")); + echo "\n"; + echo "".($i+1).""; + for($k=0;$k<$numfields;$k++) { + $tempField=$field[$k]->name; + $fieldrow=$row[($field[$k]->name)]; + $fieldrow=($fieldrow=="") ? "[empty string]" : $fieldrow; + echo "".$fieldrow."\n"; + } + echo "\n"; + } + echo ""; + if($numrows>0) + call_user_func($db."_data_seek",$var,0); + } + + //if variable is an image/gd resource type + function varIsGDResource($var) { + $this->makeTableHeader("resource","gd",2); + $this->makeTDHeader("resource","Width"); + echo imagesx($var).$this->closeTDRow(); + $this->makeTDHeader("resource","Height"); + echo imagesy($var).$this->closeTDRow(); + $this->makeTDHeader("resource","Colors"); + echo imagecolorstotal($var).$this->closeTDRow(); + echo ""; + } + + //if variable is an xml type + function varIsXml($var) { + $this->varIsXmlResource($var); + } + + //if variable is an xml resource type + function varIsXmlResource($var) { + $xml_parser=xml_parser_create(); + xml_parser_set_option($xml_parser,XML_OPTION_CASE_FOLDING,0); + xml_set_element_handler($xml_parser,array(&$this,"xmlStartElement"),array(&$this,"xmlEndElement")); + xml_set_character_data_handler($xml_parser,array(&$this,"xmlCharacterData")); + xml_set_default_handler($xml_parser,array(&$this,"xmlDefaultHandler")); + + $this->makeTableHeader("xml","xml document",2); + $this->makeTDHeader("xml","xmlRoot"); + + //attempt to open xml file + $bFile=(!($fp=@fopen($var,"r"))) ? false : true; + + //read xml file + if($bFile) { + while($data=str_replace("\n","",fread($fp,4096))) + $this->xmlParse($xml_parser,$data,feof($fp)); + } + //if xml is not a file, attempt to read it as a string + else { + if(!is_string($var)) { + echo $this->error("xml").$this->closeTDRow()."\n"; + return; + } + $data=$var; + $this->xmlParse($xml_parser,$data,1); + } + + echo $this->closeTDRow()."\n"; + + } + + //parse xml + function xmlParse($xml_parser,$data,$bFinal) { + if (!xml_parse($xml_parser,$data,$bFinal)) { + die(sprintf("XML error: %s at line %d\n", + xml_error_string(xml_get_error_code($xml_parser)), + xml_get_current_line_number($xml_parser))); + } + } + + //xml: inititiated when a start tag is encountered + function xmlStartElement($parser,$name,$attribs) { + $this->xmlAttrib[$this->xmlCount]=$attribs; + $this->xmlName[$this->xmlCount]=$name; + $this->xmlSData[$this->xmlCount]='$this->makeTableHeader("xml","xml element",2);'; + $this->xmlSData[$this->xmlCount].='$this->makeTDHeader("xml","xmlName");'; + $this->xmlSData[$this->xmlCount].='echo "'.$this->xmlName[$this->xmlCount].'".$this->closeTDRow();'; + $this->xmlSData[$this->xmlCount].='$this->makeTDHeader("xml","xmlAttributes");'; + if(count($attribs)>0) + $this->xmlSData[$this->xmlCount].='$this->varIsArray($this->xmlAttrib['.$this->xmlCount.']);'; + else + $this->xmlSData[$this->xmlCount].='echo " ";'; + $this->xmlSData[$this->xmlCount].='echo $this->closeTDRow();'; + $this->xmlCount++; + } + + //xml: initiated when an end tag is encountered + function xmlEndElement($parser,$name) { + for($i=0;$i<$this->xmlCount;$i++) { + eval($this->xmlSData[$i]); + $this->makeTDHeader("xml","xmlText"); + echo (!empty($this->xmlCData[$i])) ? $this->xmlCData[$i] : " "; + echo $this->closeTDRow(); + $this->makeTDHeader("xml","xmlComment"); + echo (!empty($this->xmlDData[$i])) ? $this->xmlDData[$i] : " "; + echo $this->closeTDRow(); + $this->makeTDHeader("xml","xmlChildren"); + unset($this->xmlCData[$i],$this->xmlDData[$i]); + } + echo $this->closeTDRow(); + echo ""; + $this->xmlCount=0; + } + + //xml: initiated when text between tags is encountered + function xmlCharacterData($parser,$data) { + $count=$this->xmlCount-1; + if(!empty($this->xmlCData[$count])) + $this->xmlCData[$count].=$data; + else + $this->xmlCData[$count]=$data; + } + + //xml: initiated when a comment or other miscellaneous texts is encountered + function xmlDefaultHandler($parser,$data) { + //strip '' off comments + $data=str_replace(array("<!--","-->"),"",htmlspecialchars($data)); + $count=$this->xmlCount-1; + if(!empty($this->xmlDData[$count])) + $this->xmlDData[$count].=$data; + else + $this->xmlDData[$count]=$data; + } + + function initJSandCSS() { + echo << + /* code modified from ColdFusion's cfdump code */ + function dBug_toggleRow(source) { + var target = (document.all) ? source.parentElement.cells[1] : source.parentNode.lastChild; + dBug_toggleTarget(target,dBug_toggleSource(source)); + } + + function dBug_toggleSource(source) { + if (source.style.fontStyle=='italic') { + source.style.fontStyle='normal'; + source.title='click to collapse'; + return 'open'; + } else { + source.style.fontStyle='italic'; + source.title='click to expand'; + return 'closed'; + } + } + + function dBug_toggleTarget(target,switchToState) { + target.style.display = (switchToState=='open') ? '' : 'none'; + } + + function dBug_toggleTable(source) { + var switchToState=dBug_toggleSource(source); + if(document.all) { + var table=source.parentElement.parentElement; + for(var i=1;i + + +SCRIPTS; + } + +} +?> diff --git a/includes/daemonize/.htaccess b/includes/daemonize/.htaccess new file mode 100644 index 0000000..3a42882 --- /dev/null +++ b/includes/daemonize/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/includes/daemonize/daemonize.php b/includes/daemonize/daemonize.php new file mode 100644 index 0000000..1f51708 --- /dev/null +++ b/includes/daemonize/daemonize.php @@ -0,0 +1,205 @@ +&1 >> /path/to/logfile +Could be trivially modified to take the script and other globals +as arguments. But that seems a bit pointless to me. +*/ + +function usage_die( $pError ) { + die( "ERROR: $pError\nUsage: /path/to/bin/php /path/to/script/daemonize.php /path/to/script/daemon.php [--debug] [--nohup] [--pidfile=/path/to/lock/daemon.pid]\n\n" ); +} + +$PHP = $_SERVER['_']; # Remember to change the #! line above, too. +if( empty( $argv[1] ) || !file_exists( $argv[1] ) ) { + usage_die( 'daemon script not found' ); +} +$daemonScript = $argv[1]; + +$debug = FALSE; +if( !empty( $argv[2] ) ) { + // convert any remaining arguments into local variables + for( $i=2; $i < count( $argv ); $i++ ) { + $arg = preg_replace( '/^[-]*/', '', $argv[$i] ); + @list( $name, $val ) = @split( '=', $arg ); + ${$name} = (!empty( $val ) ? $val : TRUE); + } +} + + +if( empty( $pidfile ) ) { + $pidfile = '/var/lock/php-' . basename( $daemonScript,'.php' ).'.pid'; +} + +# Must contain any of nohup, perl, daemonise.pl, is_up.php and this script +# That you intend to use. +# Get the following values from "kill -l" or /usr/include/linux/signal.h +# Should be correct for most unixes. +$SIGTERM = 15; +$SIGKILL = 9; + +# If we've self-bootstrapped, then load the bot and run with it. +if ( !empty( $nohup ) ) { + to_log("arg detected, bootstrapping daemon through require."); + require( $daemonScript ); + to_log("killing daemon child."); + exit(0); +} + +# If we're already started, then don't bother running. +if( empty( $nohup ) ) { + $test = 0; + $lines = array(0, 0); + $pid_data = "unread"; + + #to_log("Testing for pre-existing... $pidfile"); # Can get spammy. + if (file_exists($pidfile)) { + $pid_data = file_get_contents($pidfile); + $lines = explode("\n", $pid_data); + if (!empty($lines[0]) && is_numeric($lines[0]) && !empty($lines[1]) && is_numeric($lines[1])) { + # Kill hung processes. + if ($lines[1] + 300 < time()) { // If it's an OLD pidfile... + to_log("$pidfile OLD pidfile found from $lines[1] - ".date('d/M/Y:H:i:s O',$lines[1]).": $pid_data"); + zero_file($pidfile); + + # If found, kill. If it won't die, kill it harder. Then give up. + # In practice, it can take a bit longer than 20 seconds to die, but it + # tries again in a minute's time, and works. + if (is_alive($lines[0])) { + to_log("killing and waiting a few secs..."); + posix_kill($lines[0], $SIGTERM); + # Loop for 10 seconds, or until it dies. + for ($i=0; $i<20 && is_alive($lines[0]); $i++) { + sleep(1); + if ($i == 10) { # After ten seconds, kill it harder. + to_log("Failed to kill it. Killing it harder!"); + posix_kill($lines[0], $SIGKILL); + } + } + if (is_alive($lines[0])) { + to_log("Failed to kill it. Giving up."); + exit(0); + } + } + } else { + #to_log("Young pidfile found."); # Can get spammy. + } + if (is_alive($lines[0])) { +// to_log( "DAEMON $lines[0] : $daemonScript running. Last seen at ".date('d/M/Y:H:i:s O', $lines[1] ) ); + exit(0); + } else { + to_log("$pidfile pidfile $lines[0] found, but process is dead."); + } + } + else { + to_log("$pidfile Bad format pidfile found, zeroing, restarting..."); + zero_file($pidfile); + } + } + else { + to_log("pidfile not found: $pidfile"); + } + to_log("NOT found running."); +} + +# If pcntl_fork() doesn't exist, we need to load ourselves in the background, then die. +if (!function_exists("pcntl_fork")) { + to_log("no pcntl, calling self with nohup and param"); + system("nohup $PHP $argv[1] -nohup &", $return); + if ($return == 0) { exit(0); } + # If there was a problem, we have one more ace in the hole - perl! + to_log("nohup failed with return $return, calling self with perl"); + system("perl ".dirname( $argv[0] )."daemonize.pl &", $return); + to_log("killing daemon parent with return $return"); + exit(0); +} + +# If we've got the right functions to play with, all the above becomes moot. +to_log("we have pcntl! Doing it all ourselves!"); + +# Replace file handles +$fh_unused = array(STDIN, STDOUT); + +ob_implicit_flush(); + +# Daemon Rule 1) Fork and exit the parent. +$pid = pcntl_fork(); +if ($pid == -1) { + die("could not fork"); +} +else if ($pid) { + exit(); # Kill the parent +} + +# Daemon Rule 2) become session leader, pg leader, no term +$session_id = posix_setsid(); +if (!$session_id) { + die("Could not detach from terminal."); +} + +# Daemon Rule 3) cd to / +if (!chdir('/')) { die("Could not cd to rootfs"); } + +# Daemon Rule 4) set file creation mask to 0 +$oldmask = umask(00); + +# Daemon Rule 5) Close unneeded file handles +foreach ($fh_unused as $fh) { + if (!fclose($fh)) { die("Unable to close $fh"); } +} + +# Daemon Rule 6) Set up signal handlers where necessary. +pcntl_signal(SIGTERM, "sig_handler"); +pcntl_signal(SIGHUP, "sig_handler"); + + +function sig_handler($signo) { + switch ($signo) { + case SIGTERM: + # handle shutdown tasks + die("Caught thud SIGTERM"); + break; + case SIGHUP: + # handle restart tasks + die("Caught thud SIGHUP"); + break; + default: + # handle all other signals + } +} + +# Meat of the daemon goes here. +to_log("requiring $daemonScript"); +chdir( dirname( $daemonScript ) ); +require_once( $daemonScript ); + +exit(0); + + +# Access functions. +function is_alive($pid) { + $output = array(); + exec( dirname( __FILE__ )."/is_up.sh $pid", $output ); + $result = $output[0]; + return (0 < $result); +} + +function to_log($string) { + error_log( date( 'd/M/Y:H:i:s O' )." - $string"); +} + +function zero_file($pidfile) { + $fp = fopen($pidfile, "w"); + fwrite($fp, ""); # Zero the pidfile. + fclose($fp); +} +?> diff --git a/includes/daemonize/daemonize_lib.php b/includes/daemonize/daemonize_lib.php new file mode 100644 index 0000000..6880c43 --- /dev/null +++ b/includes/daemonize/daemonize_lib.php @@ -0,0 +1,34 @@ +loadStates(); + } + + public static function getDataset(){ + if( !isset( self::$uniqueInstance ) ){ + self::$uniqueInstance = new USStates(); + } + return self::$uniqueInstance->mStates; + } + + public function loadStates(){ + if ( empty( $this->mStates ) ) { + $this->mStates = array( + "AL" => tra("Alabama"), + "AK" => tra("Alaska"), + "AZ" => tra("Arizona"), + "AR" => tra("Arkansas"), + "CA" => tra("California"), + "CO" => tra("Colorado"), + "CT" => tra("Connecticut"), + "DE" => tra("Delaware"), + "DC" => tra("District of Columbia"), + "FL" => tra("Florida"), + "GA" => tra("Georgia"), + "HI" => tra("Hawaii"), + "ID" => tra("Idaho"), + "IL" => tra("Illinois"), + "IN" => tra("Indiana"), + "IA" => tra("Iowa"), + "KS" => tra("Kansas"), + "KY" => tra("Kentucky"), + "LA" => tra("Louisiana"), + "ME" => tra("Maine"), + "MD" => tra("Maryland"), + "MA" => tra("Massachusetts"), + "MI" => tra("Michigan"), + "MN" => tra("Minnesota"), + "MS" => tra("Mississippi"), + "MO" => tra("Missouri"), + "MT" => tra("Montana"), + "NE" => tra("Nebraska"), + "NV" => tra("Nevada"), + "NH" => tra("New Hampshire"), + "NJ" => tra("New Jersey"), + "NM" => tra("New Mexico"), + "NY" => tra("New York"), + "NC" => tra("North Carolina"), + "ND" => tra("North Dakota"), + "OH" => tra("Ohio"), + "OK" => tra("Oklahoma"), + "OR" => tra("Oregon"), + "PA" => tra("Pennsylvania"), + "PR" => tra("Puerto Rico"), + "RI" => tra("Rhode Island"), + "SC" => tra("South Carolina"), + "SD" => tra("South Dakota"), + "TN" => tra("Tennessee"), + "TX" => tra("Texas"), + "UT" => tra("Utah"), + "VT" => tra("Vermont"), + "VA" => tra("Virginia"), + "WA" => tra("Washington"), + "WV" => tra("West Virginia"), + "WI" => tra("Wisconsin"), + "WY" => tra("Wyoming"), + ); + } + return count( $this->mStates ); + } +} diff --git a/includes/diff.php b/includes/diff.php new file mode 100644 index 0000000..8b57d32 --- /dev/null +++ b/includes/diff.php @@ -0,0 +1,1027 @@ + + * All Rights Reserved. See below for details and a complete list of authors. + * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See http://www.gnu.org/copyleft/lesser.html for details + * + * $Id$ + * @package util + */ + +/** + * required setup + */ +define('USE_ASSERTS', function_exists('assert')); +/** + * Class used internally by WikiDiff to actually compute the diffs. + * + * The algorithm used here is mostly lifted from the perl module + * Algorithm::Diff (version 1.06) by Ned Konz, which is available at: + * http://www.perl.com/CPAN/authors/id/N/NE/NEDKONZ/Algorithm-Diff-1.06.zip + * + * More ideas are taken from: + * http://www.ics.uci.edu/~eppstein/161/960229.html + * + * Some ideas are (and a bit of code) are from from analyze.c, from GNU + * diffutils-2.7, which can be found at: + * ftp://gnudist.gnu.org/pub/gnu/diffutils/diffutils-2.7.tar.gz + * + * Finally, some ideas (subdivision by NCHUNKS > 2, and some optimizations) + * are my own. + * @package util + * @subpackage WikiDiffEngine + */ +class _WikiDiffEngine +{ + public $edits; // List of editing operation to convert XV to YV. + public $xv = array(), $yv = array(); + public $xchanged, $ychanged; + function _WikiDiffEngine ($from_lines, $to_lines) + { + $n_from = sizeof($from_lines); + $n_to = sizeof($to_lines); + $endskip = 0; + // Ignore differences in line endings + for ($i = 0; $i < $n_from; $i++) + { + $from_lines[$i] = rtrim($from_lines[$i]); + } + for ($i = 0; $i < $n_to; $i++) + { + $to_lines[$i] = rtrim($to_lines[$i]); + } + // Ignore trailing and leading matching lines. + while ($n_from > 0 && $n_to > 0) + { + if ($from_lines[$n_from - 1] != $to_lines[$n_to - 1]) + break; + $n_from--; + $n_to--; + $endskip++; + } + for ( $skip = 0; $skip < min($n_from, $n_to); $skip++) + if ($from_lines[$skip] != $to_lines[$skip]) + break; + $n_from -= $skip; + $n_to -= $skip; + $xlines = array(); + $ylines = array(); + // Ignore lines which do not exist in both files. + for ($x = 0; $x < $n_from; $x++) + $xhash[$from_lines[$x + $skip]] = 1; + for ($y = 0; $y < $n_to; $y++) + { + $line = $to_lines[$y + $skip]; + $ylines[] = $line; + if ( ($this->ychanged[$y] = empty($xhash[$line])) ) + continue; + $yhash[$line] = 1; + $this->yv[] = $line; + $this->yind[] = $y; + } + for ($x = 0; $x < $n_from; $x++) + { + $line = $from_lines[$x + $skip]; + $xlines[] = $line; + if ( ($this->xchanged[$x] = empty($yhash[$line])) ) + continue; + $this->xv[] = $line; + $this->xind[] = $x; + } + // Find the LCS. + $this->_compareseq(0, sizeof($this->xv), 0, sizeof($this->yv)); + // Merge edits when possible + $this->_shift_boundaries($xlines, $this->xchanged, $this->ychanged); + $this->_shift_boundaries($ylines, $this->ychanged, $this->xchanged); + // Compute the edit operations. + $this->edits = array(); + if ($skip) + $this->edits[] = $skip; + $x = 0; + $y = 0; + while ($x < $n_from || $y < $n_to) + { + USE_ASSERTS && assert($y < $n_to || $this->xchanged[$x]); + USE_ASSERTS && assert($x < $n_from || $this->ychanged[$y]); + // Skip matching "snake". + $x0 = $x; + $ncopy = 0; + while ( $x < $n_from && $y < $n_to + && !$this->xchanged[$x] && !$this->ychanged[$y]) + { + ++$x; + ++$y; + ++$ncopy; + } + if ($x > $x0) + $this->edits[] = $x - $x0; + // Find deletes. + $x0 = $x; + $ndelete = 0; + while ($x < $n_from && $this->xchanged[$x]) + { + ++$x; + ++$ndelete; + } + if ($x > $x0) + $this->edits[] = -($x - $x0); + // Find adds. + if (isset($this->ychanged[$y]) && $this->ychanged[$y]) + { + $adds = array(); + while ($y < $n_to && $this->ychanged[$y]) + $adds[] = "" . $ylines[$y++]; + $this->edits[] = $adds; + } + } + if ($endskip != 0) + $this->edits[] = $endskip; + } + /* Divide the Largest Common Subsequence (LCS) of the sequences + * [XOFF, XLIM) and [YOFF, YLIM) into NCHUNKS approximately equally + * sized segments. + * + * Returns (LCS, PTS). LCS is the length of the LCS. PTS is an + * array of NCHUNKS+1 (X, Y) indexes giving the diving points between + * sub sequences. The first sub-sequence is contained in [X0, X1), + * [Y0, Y1), the second in [X1, X2), [Y1, Y2) and so on. Note + * that (X0, Y0) == (XOFF, YOFF) and + * (X[NCHUNKS], Y[NCHUNKS]) == (XLIM, YLIM). + * + * This function assumes that the first lines of the specified portions + * of the two files do not match, and likewise that the last lines do not + * match. The caller must trim matching lines from the beginning and end + * of the portions it is going to specify. + */ + function _diag ($xoff, $xlim, $yoff, $ylim, $nchunks) + { + $flip = false; + if ($xlim - $xoff > $ylim - $yoff) + { + // Things seems faster (I'm not sure I understand why) + // when the shortest sequence in X. + $flip = true; + list ($xoff, $xlim, $yoff, $ylim) + = array( $yoff, $ylim, $xoff, $xlim); + } + if ($flip) + for ($i = $ylim - 1; $i >= $yoff; $i--) + $ymatches[$this->xv[$i]][] = $i; + else + for ($i = $ylim - 1; $i >= $yoff; $i--) + $ymatches[$this->yv[$i]][] = $i; + $this->lcs = 0; + $this->seq[0]= $yoff - 1; + $this->in_seq = array(); + $ymids[0] = array(); + $numer = $xlim - $xoff + $nchunks - 1; + $x = $xoff; + for ($chunk = 0; $chunk < $nchunks; $chunk++) + { + if ($chunk > 0) + for ($i = 0; $i <= $this->lcs; $i++) + $ymids[$i][$chunk-1] = $this->seq[$i]; + $x1 = $xoff + (int)(($numer + ($xlim-$xoff)*$chunk) / $nchunks); + for ( ; $x < $x1; $x++) + { + $matches = $ymatches[$flip ? $this->yv[$x] : $this->xv[$x]]; + if (!$matches) + continue; + reset($matches); + while (list ($junk, $y) = each($matches)) + if (empty($this->in_seq[$y])) + { + $k = $this->_lcs_pos($y); + USE_ASSERTS && assert($k > 0); + $ymids[$k] = $ymids[$k-1]; + break; + } + while (list ($junk, $y) = each($matches)) + { + if ($y > $this->seq[$k-1]) + { + USE_ASSERTS && assert($y < $this->seq[$k]); + // Optimization: this is a common case: + // next match is just replacing previous match. + $this->in_seq[$this->seq[$k]] = false; + $this->seq[$k] = $y; + $this->in_seq[$y] = 1; + } + else if (empty($this->in_seq[$y])) + { + $k = $this->_lcs_pos($y); + USE_ASSERTS && assert($k > 0); + $ymids[$k] = $ymids[$k-1]; + } + } + } + } + $seps[] = $flip ? array($yoff, $xoff) : array($xoff, $yoff); + $ymid = $ymids[$this->lcs]; + for ($n = 0; $n < $nchunks - 1; $n++) + { + $x1 = $xoff + (int)(($numer + ($xlim - $xoff) * $n) / $nchunks); + $y1 = $ymid[$n] + 1; + $seps[] = $flip ? array($y1, $x1) : array($x1, $y1); + } + $seps[] = $flip ? array($ylim, $xlim) : array($xlim, $ylim); + return array($this->lcs, $seps); + } + function _lcs_pos ($ypos) + { + $end = $this->lcs; + if ($end == 0 || $ypos > $this->seq[$end]) + { + $this->seq[++$this->lcs] = $ypos; + $this->in_seq[$ypos] = 1; + return $this->lcs; + } + $beg = 1; + while ($beg < $end) + { + $mid = (int)(($beg + $end) / 2); + if ( $ypos > $this->seq[$mid] ) + $beg = $mid + 1; + else + $end = $mid; + } + USE_ASSERTS && assert($ypos != $this->seq[$end]); + $this->in_seq[$this->seq[$end]] = false; + $this->seq[$end] = $ypos; + $this->in_seq[$ypos] = 1; + return $end; + } + /* Find LCS of two sequences. + * + * The results are recorded in the vectors $this->{x,y}changed[], by + * storing a 1 in the element for each line that is an insertion + * or deletion (ie. is not in the LCS). + * + * The subsequence of file 0 is [XOFF, XLIM) and likewise for file 1. + * + * Note that XLIM, YLIM are exclusive bounds. + * All line numbers are origin-0 and discarded lines are not counted. + */ + function _compareseq ($xoff, $xlim, $yoff, $ylim) + { + // Slide down the bottom initial diagonal. + while ($xoff < $xlim && $yoff < $ylim + && $this->xv[$xoff] == $this->yv[$yoff]) + { + ++$xoff; + ++$yoff; + } + // Slide up the top initial diagonal. + while ($xlim > $xoff && $ylim > $yoff + && $this->xv[$xlim - 1] == $this->yv[$ylim - 1]) + { + --$xlim; + --$ylim; + } + if ($xoff == $xlim || $yoff == $ylim) + $lcs = 0; + else + { + // This is ad hoc but seems to work well. + //$nchunks = sqrt(min($xlim - $xoff, $ylim - $yoff) / 2.5); + //$nchunks = max(2,min(8,(int)$nchunks)); + $nchunks = min(7, $xlim - $xoff, $ylim - $yoff) + 1; + list ($lcs, $seps) + = $this->_diag($xoff,$xlim,$yoff, $ylim,$nchunks); + } + if ($lcs == 0) + { + // X and Y sequences have no common subsequence: + // mark all changed. + while ($yoff < $ylim) + $this->ychanged[$this->yind[$yoff++]] = 1; + while ($xoff < $xlim) + $this->xchanged[$this->xind[$xoff++]] = 1; + } + else + { + // Use the partitions to split this problem into subproblems. + reset($seps); + $pt1 = $seps[0]; + while ($pt2 = next($seps)) + { + $this->_compareseq ($pt1[0], $pt2[0], $pt1[1], $pt2[1]); + $pt1 = $pt2; + } + } + } + /* Adjust inserts/deletes of identical lines to join changes + * as much as possible. + * + * We do something when a run of changed lines include a + * line at one end and has an excluded, identical line at the other. + * We are free to choose which identical line is included. + * `compareseq' usually chooses the one at the beginning, + * but usually it is cleaner to consider the following identical line + * to be the "change". + * + * This is extracted verbatim from analyze.c (GNU diffutils-2.7). + */ + function _shift_boundaries ($lines, &$changed, $other_changed) + { + $i = 0; + $j = 0; + USE_ASSERTS && assert('sizeof($lines) == sizeof($changed)'); + $len = sizeof($lines); + $other_len = sizeof($other_changed); + while (1) + { + /* + * Scan forwards to find beginning of another run of changes. + * Also keep track of the corresponding point in the other file. + * + * Throughout this code, $i and $j are adjusted together so that + * the first $i elements of $changed and the first $j elements + * of $other_changed both contain the same number of zeros + * (unchanged lines). + * Furthermore, $j is always kept so that $j == $other_len or + * $other_changed[$j] == false. + */ + while ($j < $other_len && $other_changed[$j]) + $j++; + while ($i < $len && ! $changed[$i]) + { + USE_ASSERTS && assert('$j < $other_len && ! $other_changed[$j]'); + $i++; $j++; + while ($j < $other_len && $other_changed[$j]) + $j++; + } + if ($i == $len) + break; + $start = $i; + // Find the end of this run of changes. + while (++$i < $len && $changed[$i]) + continue; + do + { + /* + * Record the length of this run of changes, so that + * we can later determine whether the run has grown. + */ + $runlength = $i - $start; + /* + * Move the changed region back, so long as the + * previous unchanged line matches the last changed one. + * This merges with previous changed regions. + */ + while ($start > 0 && $lines[$start - 1] == $lines[$i - 1]) + { + $changed[--$start] = 1; + $changed[--$i] = false; + while ($start > 0 && $changed[$start - 1]) + $start--; + USE_ASSERTS && assert('$j > 0'); + while ($other_changed[--$j]) + continue; + USE_ASSERTS && assert('$j >= 0 && !$other_changed[$j]'); + } + /* + * Set CORRESPONDING to the end of the changed run, at the last + * point where it corresponds to a changed run in the other file. + * CORRESPONDING == LEN means no such point has been found. + */ + $corresponding = $j < $other_len ? $i : $len; + /* + * Move the changed region forward, so long as the + * first changed line matches the following unchanged one. + * This merges with following changed regions. + * Do this second, so that if there are no merges, + * the changed region is moved forward as far as possible. + */ + while ($i < $len && $lines[$start] == $lines[$i]) + { + $changed[$start++] = false; + $changed[$i++] = 1; + while ($i < $len && $changed[$i]) + $i++; + USE_ASSERTS && assert('$j < $other_len && ! $other_changed[$j]'); + $j++; + if ($j < $other_len && $other_changed[$j]) + { + $corresponding = $i; + while ($j < $other_len && $other_changed[$j]) + $j++; + } + } + } + while ($runlength != $i - $start); + /* + * If possible, move the fully-merged run of changes + * back to a corresponding run in the other file. + */ + while ($corresponding < $i) + { + $changed[--$start] = 1; + $changed[--$i] = 0; + USE_ASSERTS && assert('$j > 0'); + while ($other_changed[--$j]) + continue; + USE_ASSERTS && assert('$j >= 0 && !$other_changed[$j]'); + } + } + } +} +/** + * Class representing a diff between two files. + * + * @package wiki + * @subpackage WikiDiff + */ +class WikiDiff +{ + public $edits; + /** + * Compute diff between files (or deserialize serialized WikiDiff.) + */ + function WikiDiff($from_lines = false, $to_lines = false) + { + if ($from_lines && $to_lines) + { + $compute = new _WikiDiffEngine($from_lines, $to_lines); + $this->edits = $compute->edits; + } + else if ($from_lines) + { + // $from_lines is not really from_lines, but rather + // a serialized WikiDiff. + $this->edits = unserialize($from_lines); + } + else + { + $this->edits = array(); + } + } + /** + * Compute reversed WikiDiff. + * + * SYNOPSIS: + * + * $diff = new WikiDiff($lines1, $lines2); + * $rev = $diff->reverse($lines1); + * + * // reconstruct $lines1 from $lines2: + * $out = $rev->apply($lines2); + */ + function reverse ($from_lines) + { + $x = 0; + $rev = new WikiDiff; + for ( reset($this->edits); + $edit = current($this->edits); + next($this->edits) ) + { + if (is_array($edit)) + { // Was an add, turn it into a delete. + $nadd = sizeof($edit); + USE_ASSERTS && assert ($nadd > 0); + $edit = -$nadd; + } + else if ($edit > 0) + { + // Was a copy --- just pass it through. } + $x += $edit; + } + else if ($edit < 0) + { // Was a delete, turn it into an add. + $ndelete = -$edit; + $edit = array(); + while ($ndelete-- > 0) + $edit[] = "" . $from_lines[$x++]; + } + else die("assertion error"); + $rev->edits[] = $edit; + } + return $rev; + } + /** + * Compose (concatenate) WikiDiffs. + * + * SYNOPSIS: + * + * $diff1 = new WikiDiff($lines1, $lines2); + * $diff2 = new WikiDiff($lines2, $lines3); + * $comp = $diff1->compose($diff2); + * + * // reconstruct $lines3 from $lines1: + * $out = $comp->apply($lines1); + */ + function compose ($that) + { + reset($this->edits); + reset($that->edits); + $comp = new WikiDiff; + $left = current($this->edits); + $right = current($that->edits); + while ($left || $right) + { + if (!is_array($left) && $left < 0) + { // Left op is a delete. + $newop = $left; + $left = next($this->edits); + } + else if (is_array($right)) + { // Right op is an add. + $newop = $right; + $right = next($that->edits); + } + else if (!$left || !$right) + die ("assertion error"); + else if (!is_array($left) && $left > 0) + { // Left op is a copy. + if ($left <= abs($right)) + { + $newop = $right > 0 ? $left : -$left; + $right -= $newop; + if ($right == 0) + $right = next($that->edits); + $left = next($this->edits); + } + else + { + $newop = $right; + $left -= abs($right); + $right = next($that->edits); + } + } + else + { // Left op is an add. + if (!is_array($left)) die('assertion error'); + $nleft = sizeof($left); + if ($nleft <= abs($right)) + { + if ($right > 0) + { // Right op is copy + $newop = $left; + $right -= $nleft; + } + else // Right op is delete + { + $newop = false; + $right += $nleft; + } + if ($right == 0) + $right = next($that->edits); + $left = next($this->edits); + } + else + { + unset($newop); + if ($right > 0) + for ($i = 0; $i < $right; $i++) + $newop[] = $left[$i]; + $tmp = array(); + for ($i = abs($right); $i < $nleft; $i++) + $tmp[] = $left[$i]; + $left = $tmp; + $right = next($that->edits); + } + } + if (!$op) + { + $op = $newop; + continue; + } + if (! $newop) + continue; + if (is_array($op) && is_array($newop)) + { + // Both $op and $newop are adds. + for ($i = 0; $i < sizeof($newop); $i++) + $op[] = $newop[$i]; + } + else if (($op > 0 && $newop > 0) || ($op < 0 && $newop < 0)) + { // $op and $newop are both either deletes or copies. + $op += $newop; + } + else + { + $comp->edits[] = $op; + $op = $newop; + } + } + if ($op) + $comp->edits[] = $op; + return $comp; + } + /* Debugging only: + function _dump () + { + echo "
    "; + for (reset($this->edits); + $edit = current($this->edits); + next($this->edits)) + { + echo "
  1. "; + if ($edit > 0) + echo "Copy $edit"; + else if ($edit < 0) + echo "Delete " . -$edit; + else if (is_array($edit)) + { + echo "Add " . sizeof($edit) . "
      "; + for ($i = 0; $i < sizeof($edit); $i++) + echo "
    • " . htmlspecialchars($edit[$i]); + echo "
    "; + } + else + die("assertion error"); + } + echo "
"; + } + */ + /** + * Apply a WikiDiff to a set of lines. + * + * SYNOPSIS: + * + * $diff = new WikiDiff($lines1, $lines2); + * + * // reconstruct $lines2 from $lines1: + * $out = $diff->apply($lines1); + */ + function apply ($from_lines) + { + $x = 0; + $xlim = sizeof($from_lines); + for ( reset($this->edits); + $edit = current($this->edits); + next($this->edits) ) + { + if (is_array($edit)) + { + reset($edit); + while (list ($junk, $line) = each($edit)) + $output[] = $line; + } + else if ($edit > 0) + while ($edit--) + $output[] = $from_lines[$x++]; + else + $x += -$edit; + } + if ($x != $xlim) + ExitWiki(sprintf(tra ("WikiDiff::apply: line count mismatch: %s != %s"), $x, $xlim)); + return $output; + } + /** + * Serialize a WikiDiff. + * + * SYNOPSIS: + * + * $diff = new WikiDiff($lines1, $lines2); + * $string = $diff->serialize; + * + * // recover WikiDiff from serialized version: + * $diff2 = new WikiDiff($string); + */ + function serialize () + { + return serialize($this->edits); + } + /** + * Return true if two files were equal. + */ + function isEmpty () + { + if (sizeof($this->edits) > 1) + return false; + if (sizeof($this->edits) == 0) + return true; + // Test for: only edit is a copy. + return !is_array($this->edits[0]) && $this->edits[0] > 0; + } + /** + * Compute the length of the Longest Common Subsequence (LCS). + * + * This is mostly for diagnostic purposed. + */ + function lcs () + { + $lcs = 0; + for (reset($this->edits); + $edit = current($this->edits); + next($this->edits)) + { + if (!is_array($edit) && $edit > 0) + $lcs += $edit; + } + return $lcs; + } + /** + * Check a WikiDiff for validity. + * + * This is here only for debugging purposes. + */ + function _check ($from_lines, $to_lines) + { + $test = $this->apply($from_lines); + if (serialize($test) != serialize($to_lines)) + ExitWiki(tra ("WikiDiff::_check: failed")); + reset($this->edits); + $prev = current($this->edits); + $prevtype = is_array($prev) ? 'a' : ($prev > 0 ? 'c' : 'd'); + while ($edit = next($this->edits)) + { + $type = is_array($edit) ? 'a' : ($edit > 0 ? 'c' : 'd'); + if ( $prevtype == $type ) + ExitWiki(tra ("WikiDiff::_check: edit sequence is non-optimal")); + $prevtype = $type; + } + $lcs = $this->lcs(); + printf ("" . tra ("WikiDiff Okay: LCS = %s") . "\n", $lcs); + } +} +/** + * A class to format a WikiDiff as HTML. + * + * Usage: + * + * $diff = new WikiDiff($lines1, $lines2); // compute diff. + * + * $fmt = new WikiDiffFormatter; + * echo $fmt->format($diff, $lines1); // Output HTMLified standard diff. + * + * or to output reverse diff (diff's that would take $lines2 to $lines1): + * + * $fmt = new WikiDiffFormatter('reversed'); + * echo $fmt->format($diff, $lines1); + * + * @package wiki + * @subpackage WikiDiffFormatter + */ +class WikiDiffFormatter +{ + public $context_lines; + public $do_reverse_diff; + public $context_prefix, $deletes_prefix, $adds_prefix; + function WikiDiffFormatter ($reverse = false) + { + $this->do_reverse_diff = $reverse; + $this->context_lines = 0; + $this->context_prefix = '  '; + $this->deletes_prefix = '< '; + $this->adds_prefix = '> '; + } + function format ($diff, $from_lines) + { + $html = '
'; + $html .= $this->_format($diff->edits, $from_lines); + $html .= "
\n"; + return $html; + } + function _format ($edits, $from_lines) + { + $html = ''; + $x = 0; $y = 0; + $xlim = sizeof($from_lines); + reset($edits); + while ($edit = current($edits)) + { + if (!is_array($edit) && $edit >= 0) + { // Edit op is a copy. + $ncopy = $edit; + } + else + { + $ncopy = 0; + if (empty($hunk)) + { + // Start of an output hunk. + $xoff = max(0, $x - $this->context_lines); + $yoff = $xoff + $y - $x; + if ($xoff < $x) + { + // Get leading context. + $context = array(); + for ($i = $xoff; $i < $x; $i++) + $context[] = $from_lines[$i]; + $hunk['c'] = $context; + } + } + if (is_array($edit)) + { // Edit op is an add. + $y += sizeof($edit); + $hunk[$this->do_reverse_diff ? 'd' : 'a'] = $edit; + } + else + { // Edit op is a delete + $deletes = array(); + while ($edit++ < 0) + $deletes[] = $from_lines[$x++]; + $hunk[$this->do_reverse_diff ? 'a' : 'd'] = $deletes; + } + } + $next = next($edits); + if (!empty($hunk)) + { + if ( !$next || $ncopy > 2 * $this->context_lines) + { + // End of an output hunk. + $hunks[] = $hunk; + unset($hunk); + $xend = min($x + $this->context_lines, $xlim); + if ($x < $xend) + { + // Get trailing context. + $context = array(); + for ($i = $x; $i < $xend; $i++) + $context[] = $from_lines[$i]; + $hunks[] = array('c' => $context); + } + $xlen = $xend - $xoff; + $ylen = $xend + $y - $x - $yoff; + $xbeg = $xlen ? $xoff + 1 : $xoff; + $ybeg = $ylen ? $yoff + 1 : $yoff; + if ($this->do_reverse_diff) + list ($xbeg, $xlen, $ybeg, $ylen) + = array($ybeg, $ylen, $xbeg, $xlen); + $html .= $this->_emit_diff($xbeg,$xlen,$ybeg,$ylen, + $hunks); + unset($hunks); + } + else if ($ncopy) + { + $hunks[] = $hunk; + // Copy context. + $context = array(); + for ($i = $x; $i < $x + $ncopy; $i++) + $context[] = $from_lines[$i]; + $hunk = array('c' => $context); + } + } + $x += $ncopy; + $y += $ncopy; + } + return $html; + } + function _emit_lines($lines, $prefix, $color) + { + $html = ''; + reset($lines); + while (list ($junk, $line) = each($lines)) + { + $html .= "$prefix"; + $html .= "" . htmlspecialchars($line) . "\n"; + } + return $html; + } + function _emit_diff ($xbeg,$xlen,$ybeg,$ylen,$hunks) + { + $html = '
' + . '\n
' + . $this->_diff_header($xbeg, $xlen, $ybeg, $ylen) + . "
\n" + . '
'; + $prefix = array('c' => $this->context_prefix, + 'a' => $this->adds_prefix, + 'd' => $this->deletes_prefix); + $color = array('c' => '#ffffff', + 'a' => '#ccffcc', + 'd' => '#ffcccc'); + for (reset($hunks); $hunk = current($hunks); next($hunks)) + { + if (!empty($hunk['c'])) + $html .= $this->_emit_lines($hunk['c'], + $this->context_prefix, '#ffffff'); + if (!empty($hunk['d'])) + $html .= $this->_emit_lines($hunk['d'], + $this->deletes_prefix, '#ffcccc'); + if (!empty($hunk['a'])) + $html .= $this->_emit_lines($hunk['a'], + $this->adds_prefix, '#ccffcc'); + } + $html .= "
\n"; + return $html; + } + function _diff_header ($xbeg,$xlen,$ybeg,$ylen) + { + $what = $xlen ? ($ylen ? 'c' : 'd') : 'a'; + $xlen = $xlen > 1 ? "," . ($xbeg + $xlen - 1) : ''; + $ylen = $ylen > 1 ? "," . ($ybeg + $ylen - 1) : ''; + return "$xbeg$xlen$what$ybeg$ylen"; + } +} +/** + * A class to format a WikiDiff as a pretty HTML unified diff. + * + * Usage: + * + * $diff = new WikiDiff($lines1, $lines2); // compute diff. + * + * $fmt = new WikiUnifiedDiffFormatter; + * echo $fmt->format($diff, $lines1); // Output HTMLified unified diff. + * + * @package wiki + * @subpackage WikiUnifiedDiffFormatter + */ + +class WikiUnifiedDiffFormatter extends WikiDiffFormatter +{ + function WikiUnifiedDiffFormatter ($reverse = false, $context_lines = 3) + { + $this->do_reverse_diff = $reverse; + $this->context_lines = $context_lines; + $this->context_prefix = ' '; + $this->deletes_prefix = '-'; + $this->adds_prefix = '+'; + } + function _diff_header ($xbeg,$xlen,$ybeg,$ylen) + { + $xlen = $xlen == 1 ? '' : ",$xlen"; + $ylen = $ylen == 1 ? '' : ",$ylen"; + return "@@ -$xbeg$xlen +$ybeg$ylen @@"; + } +} +///////////////////////////////////////////////////////////////// +/* +if ($diff) +{ + if (get_magic_quotes_gpc()) { + $diff = stripslashes($diff); + } + $pagename = $diff; + $wiki = RetrievePage($dbi, $pagename, $WikiPageStore); +// $dba = OpenDataBase($ArchivePageStore); + $archive= RetrievePage($dbi, $pagename, $ArchivePageStore); + $html = ''; + if (is_array($wiki)) { + $html .= ""; + } else { + $html .= ""; + } + $html .= "\n"; + $html .= ''; + if (is_array($archive)) { + $html .= ""; + } else { + $html .= ""; + } + $html .= "
'; + $html .= tra ("Current page:"); + $html .= '"; + $html .= sprintf(tra ("version %s"), $wiki['version']); + $html .= ""; + $html .= sprintf(tra ("last modified on %s"), + date($datetimeformat, $wiki['lastmodified'])); + $html .= ""; + $html .= sprintf (tra ("by %s"), $wiki['author']); + $html .= ""; + $html .= tra ("None"); + $html .= "
'; + $html .= tra ("Archived page:"); + $html .= '"; + $html .= sprintf(tra ("version %s"), $archive['version']); + $html .= ""; + $html .= sprintf(tra ("last modified on %s"), + date($datetimeformat, $archive['lastmodified'])); + $html .= ""; + $html .= sprintf(tra ("by %s"), $archive['author']); + $html .= ""; + $html .= tra ("None"); + $html .= "

\n"; + if (is_array($wiki) && is_array($archive)) + { + $diff = new WikiDiff($archive['content'], $wiki['content']); + if ($diff->isEmpty()) { + $html .= '


[' . tra ("Versions are identical") . ']'; + } else { + //$fmt = new WikiDiffFormatter; + $fmt = new WikiUnifiedDiffFormatter; + $html .= $fmt->format($diff, $archive['content']); + } + } + GeneratePage('MESSAGE', $html, sprintf(tra ("Diff of %s."), + htmlspecialchars($pagename)), 0); +} +*/ + + + // \todo remove html hardcoded in diff2 + function diff2($page1, $page2) { + $page1 = split("\n", $page1); + $page2 = split("\n", $page2); + $z = new WikiDiff($page1, $page2); + if ($z->isEmpty()) { + $html = '

[' . tra("Versions are identical"). ']

'; + } else { + //$fmt = new WikiDiffFormatter; + $fmt = new WikiUnifiedDiffFormatter; + $html = $fmt->format($z, $page1); + } + return $html; + } + +?> diff --git a/includes/geocalc/GeoCalc.class.php b/includes/geocalc/GeoCalc.class.php new file mode 100644 index 0000000..2015f0b --- /dev/null +++ b/includes/geocalc/GeoCalc.class.php @@ -0,0 +1,238 @@ +GCDistance(38.9333,-94.3253,38.9314,-94.4876) . "
\n"; + * print "GCAzimuth: " . $oGC->GCAzimuth(38.9333,-94.3253,38.9314,-94.4876) . "
\n"; + * print "ApproxDistance: " . $oGC->ApproxDistance(38.9333,-94.3253,38.9314,-94.4876) . "
\n"; + * print "EllipsoidDistance: " . $oGC->EllipsoidDistance(38.80126649,-94.44590241,43.3368,-96.8755) . "

\n"; + * print "EllipsoidDistance In Miles: " . ConvKilometersToMiles($oGC->EllipsoidDistance(38.80126649,-94.44590241,43.3368,-96.8755)); + * + * @package geocalc + */ + +/** + * @package geocalc + */ +class GeoCalc { + + var $PI = 3.14159265359; + var $TWOPI = 6.28318530718; + var $DE2RA = 0.01745329252; + var $RA2DE = 57.2957795129; + var $ERAD = 6378.135; + var $ERADM = 6378135.0; + var $AVG_ERAD = 6371.0; + var $EPS = 0.000000000005; + var $KM2MI = 0.621371; + var $FLATTENING = 0; + + function GeoCalc() { + $this->FLATTENING = 1.0/298.26; // Earth flattening + // (WGS 1972) + return; + } + + function GCDistance($lat1, $lon1, $lat2, $lon2) { + $lat1 *= $this->DE2RA; + $lon1 *= $this->DE2RA; + $lat2 *= $this->DE2RA; + $lon2 *= $this->DE2RA; + $d = sin($lat1)*sin($lat2) + cos($lat1)*cos($lat2)*cos($lon1 - $lon2); + return ($this->AVG_ERAD * acos($d)); + } + + + function GCAzimuth($lat1, $lon1, $lat2, $lon2) { + $result = 0.0; + + $ilat1 = intval(0.50 + $lat1 * 360000.0); + $ilat2 = intval(0.50 + $lat2 * 360000.0); + $ilon1 = intval(0.50 + $lon1 * 360000.0); + $ilon2 = intval(0.50 + $lon2 * 360000.0); + + $lat1 *= $this->DE2RA; + $lon1 *= $this->DE2RA; + $lat2 *= $this->DE2RA; + $lon2 *= $this->DE2RA; + + if (($ilat1 == $ilat2) && ($ilon1 == $ilon2)) { + return result; + } + else if ($ilat1 == $ilat2) { + if ($ilon1 > $ilon2) + $result = 90.0; + else + $result = 270.0; + } + else if ($ilon1 == $ilon2) { + if ($ilat1 > $ilat2) + $result = 180.0; + } + else { + $c = acos(sin($lat2)*sin($lat1) + cos($lat2)*cos($lat1)*cos(($lon2-$lon1))); + $A = asin(cos($lat2)*sin(($lon2-$lon1))/sin($c)); + $result = ($A * $this->RA2DE); + + + if (($ilat2 > $ilat1) && ($ilon2 > $ilon1)) { + $result = $result; + } + else if (($ilat2 < $ilat1) && ($ilon2 < $ilon1)) { + $result = 180.0 - $result; + } + else if (($ilat2 < $ilat1) && ($ilon2 > $ilon1)) { + $result = 180.0 - $result; + } + else if (($ilat2 > $ilat1) && ($ilon2 < $ilon1)) { + $result += 360.0; + } + } + + return $result; + } + + function ApproxDistance($lat1, $lon1, $lat2, $lon2) { + $lat1 = $this->DE2RA * $lat1; + $lon1 = -$this->DE2RA * $lon1; + $lat2 = $this->DE2RA * $lat2; + $lon2 = -$this->DE2RA * $lon2; + + $F = ($lat1 + $lat2) / 2.0; + $G = ($lat1 - $lat2) / 2.0; + $L = ($lon1 - $lon2) / 2.0; + + $sing = sin($G); + $cosl = cos($L); + $cosf = cos($F); + $sinl = sin($L); + $sinf = sin($F); + $cosg = cos($G); + + $S = $sing*$sing*$cosl*$cosl + $cosf*$cosf*$sinl*$sinl; + $C = $cosg*$cosg*$cosl*$cosl + $sinf*$sinf*$sinl*$sinl; + $W = atan2(sqrt($S),sqrt($C)); + $R = sqrt(($S*$C))/$W; + $H1 = (3 * $R - 1.0) / (2.0 * $C); + $H2 = (3 * $R + 1.0) / (2.0 * $S); + $D = 2 * $W * $this->ERAD; + $return = ($D * (1 + $this->FLATTENING * $H1 * $sinf*$sinf*$cosg*$cosg - $this->FLATTENING*$H2*$cosf*$cosf*$sing*$sing)); + return $return; + } + + function EllipsoidDistance($lat1, $lon1, $lat2, $lon2) { + $distance = 0.0; + $faz = 0.0; + $baz = 0.0; + $r = 1.0 - $this->FLATTENING; + $tu1 = 0.0; + $tu2 = 0.0; + $cu1 = 0.0; + $su1 = 0.0; + $cu2 = 0.0; + $x = 0.0; + $sx = 0.0; + $cx = 0.0; + $sy = 0.0; + $cy = 0.0; + $y = 0.0; + $sa = 0.0; + $c2a = 0.0; + $cz = 0.0; + $e = 0.0; + $c = 0.0; + $d = 0.0; + + $cosy1 = 0.0; + $cosy2 = 0.0; + + if(($lon1 == $lon2) && ($lat1 == $lat2)) + return $distance; + $lon1 *= $this->DE2RA; + $lon2 *= $this->DE2RA; + $lat1 *= $this->DE2RA; + $lat2 *= $this->DE2RA; + + $cosy1 = cos($lat1); + $cosy2 = cos($lat2); + + if($cosy1 == 0.0) $cosy1 = 0.0000000001; + if($cosy2 == 0.0) $cosy2 = 0.0000000001; + + $tu1 = $r * sin($lat1) / $cosy1; + $tu2 = $r * sin($lat2) / $cosy2; + $cu1 = 1.0 / sqrt($tu1 * $tu1 + 1.0); + $su1 = $cu1 * $tu1; + $cu2 = 1.0 / sqrt($tu2 * $tu2 + 1.0); + $x = $lon2 - $lon1; + + $distance = $cu1 * $cu2; + $baz = $distance * $tu2; + $faz = $baz * $tu1; + + while(abs($d - $x) > $this->EPS) { + $sx = sin($x); + $cx = cos($x); + $tu1 = $cu2 * $sx; + $tu2 = $baz - $su1 * $cu2 * $cx; + $sy = sqrt($tu1 * $tu1 + $tu2 * $tu2); + $cy = $distance * $cx + $faz; + $y = atan2($sy, $cy); + $sa = $distance * $sx / $sy; + $c2a = - $sa * $sa + 1.0; + $cz = $faz + $faz; + if($c2a > 0.0) $cz = - $cz / $c2a + $cy; + $e = $cz * $cz * 2.0 - 1.0; + $c = ((-3.0 * $c2a + 4.0) * $this->FLATTENING + 4.0) * $c2a * $this->FLATTENING / 16.0; + $d = $x; + $x = (($e * $cy * $c + $cz) * $sy * $c + $y) * $sa; + $x = (1.0 - $c) * $x * $this->FLATTENING + $lon2 - $lon1; + } + + $x = sqrt((1.0 / $r / $r - 1.0) * $c2a + 1.0) + 1.0; + $x = ($x - 2.0) / $x; + $c = 1.0 - $x; + $c = ($x * $x / 4.0 + 1.0) / $c; + $d = (0.375 * $x * $x - 1.0) * $x; + $x = $e * $cy; + $distance = 1.0 - $e - $e; + $distance = (((($sy * $sy * 4.0 - 3.0) * $distance * $cz * $d / 6.0 - $x) * $d / 4.0 + $cz) * $sy * $d + $y) * $c * $this->ERAD * $r; + + return $distance; + } + + function getKmPerLonAtLat($dLatitude) { + // Thanks to Eric Iverson for this correction! Must convert degrees to radians... + $dLatitude *= $this->DE2RA; + return 111.321 * cos($dLatitude); + } + + function getLonPerKmAtLat($dLatitude) { + return 1 / $this->getKmPerLonAtLat($dLatitude); + } + + function getKmPerLat() { + return 111.000; + } + + function getLatPerKm() { + return 1 / $this->getKmPerLat(); + } + +} + +function ConvKilometersToMiles($dValue) { + return $dValue / 1.609344; +} + + +?> \ No newline at end of file diff --git a/includes/geocalc/README b/includes/geocalc/README new file mode 100644 index 0000000..36391e8 --- /dev/null +++ b/includes/geocalc/README @@ -0,0 +1,148 @@ +GEOCALC-PHP +Geographic Distance and Azimuth Calculations in PHP +Version 1.2 + + +ABOUT THIS LIBRARY +This code was "ported" to PHP from Visual C++ by Steven Brendtro +of www.imaginerc.com. The original article and source code, written +by andyf4i can be found on CodeGuru.com at the following address: + +http://www.codeguru.com/Cpp/Cpp/algorithms/article.php/c5115/ + + +VERSION HISTORY +Version 1.0 - Initial release +Version 1.1 - Bug in getKMPerLonAtLat() fixed. + Thanks to Eric Iverson for finding it. +Version 1.2 - Bug in getLonPerKMAtLat() fixed. + + +FILES INCLUDED IN THIS RELEASE +GeoCalc.class.php - The actual PHP class +README - This file + + +ADDITIONAL PACKAGES +Also available on SourceForge.net under the GEOCALC-PHP project, +you can find a ZIP Code database (for FREE!) that you can use +for distance calculations between ZIP Codes. + +For increased speed when performing distance calculations using +MySQL, be sure to check out GEOCALC-UDF at SourceForge.net. +This UDF (User Defined Function) is designed for MySQL and has +been known to reduce query time up to 1000%! + + +METHODS + +# Basic Methods: +GeoCalc(); # Default Constructor + +GCDistance($lat1, $lon1, $lat2, $lon2); + # Using the Great Circle formula, calculate + # the distance in kilometers between + # Latitude/Longitude 1 and Latitude/Longitude 2 + +GCAzimuth($lat1, $lon1, $lat2, $lon2); + # Using the Great Circle formula, calculate the + # azimuth between Latitude/Longitude 1 and + # Latitude/Longitude 2 + +ApproxDistance($lat1, $lon1, $lat2, $lon2); + # Using the Ellipsoidal Approximation formula, + # calculate the distance in kilometers between + # Latitude/Longitude 1 and Latitude/Longitude 2 + +EllipsoidDistance($lat1, $lon1, $lat2, $lon2); + # Using the Ellipsoidal Distance formula, calculate + # the distance in kilometers between Latitude/Longitude 1 + # and Latitude/Longitude 2. This formula is the most + # accurate of the formulas. + +# Helper Methods + +getKmPerLonAtLat($dLatitude); + # Get the number of Kilometers per degree longitude + # at the given latitude. + +getLonPerKmAtLat($dLatitude); + # Get the number of degrees longitude per kilometer at the + # given latitude. + +getKmPerLat(); + # Get the number of kilometers per degree latitude (average + # of 111.0 kilometers per degree) + +getLatPerKm(); + # Get the number of degrees latitude per kilometer (average + # of 1/111 degrees per kilometer) + + +# Helper Function (not a method in the class, but a function) + +ConvKilometersToMiles($dValue); + # Convert a distance from kilometers to miles + # (km / 1.609344 = miles). + + +EXAMPLES + +Here are some example uses for this class: + + include_once("GeoCalc.class.php"); + + $oGC = new GeoCalc(); + + // Great Circle Distance + $dDist = $oGC->GCDistance(38.9333,-94.3253,38.9314,-94.4876); + + // Great Circle Azimuth + $dDist = $oGC->GCAzimuth(38.9333,-94.3253,38.9314,-94.4876); + + // Approximate Ellipsoid Distance + $dDist = $oGC->ApproxDistance(38.9333,-94.3253,38.9314,-94.4876); + + // Accurate Ellipsoid Distance + $dDist = $oGC->EllipsoidDistance(38.9333,-94.3253,38.9314,-94.4876); + + // Convert distance from kilometers to miles + $dDistMiles = ConvKilometersToMiles($dDist); + + // Advanced Calculation: + // The following will search for ZIP codes + // within a radius (roughly calculated) + + // Define the center of the search bounds... + $dLongitude = -94.44590241; + $dLatitude = 38.7996; + + // Define the maximum search distance + $dRadius = 100.00; // in kilometers + + // Calculate the boundary distance in degrees longitude / latitude + $dAddLat = $oGC->getLatPerKm() * $dRadius; + $dAddLon = $oGC->getLonPerKmAtLat($dLatitude) * $dRadius; + + // Calculate the boundaries + $dNorthBounds = $dLatitude + $dAddLat; + $dSouthBounds = $dLatitude - $dAddLat; + $dWestBounds = $dLongitude - $dAddLon; + $dEastBounds = $dLongitude + $dAddLon; + + print "Center Longitude: $dLongitude\n"; + print "Center Latitude: $dLatitude\n"; + print "Radius: $dRadius kilometers\n"; + + print "North Bounds: $dNorthBounds\n"; + print "South Bounds: $dSouthBounds\n"; + print "East Bounds: $dEastBounds\n"; + print "West Bounds: $dWestBounds\n"; + + // Sample SQL query stament based on above boundaries: + $strQuery = "SELECT * FROM PostalCodes " . + "WHERE Latitude > $dSouthBounds " . + "AND Latitude < $dNorthBounds " . + "AND Longitude > $dWestBounds " . + "AND Longitude < $dEastBounds"; + diff --git a/includes/getid3/changelog.txt b/includes/getid3/changelog.txt new file mode 100644 index 0000000..a0be611 --- /dev/null +++ b/includes/getid3/changelog.txt @@ -0,0 +1,2488 @@ +///////////////////////////////////////////////////////////////// +/// getID3() by James Heinrich // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// // +// changelog.txt - part of getID3() // +// See readme.txt for more details // +// /// +///////////////////////////////////////////////////////////////// + + » denotes a major feature addition/change + ¤ denotes a change in the returned structure +* Bugfix: denotes a fixed bug + +Version History +=============== + +1.7.7: [2006-06-25] Allan Hansen + * Bugfix: AAC static bitrate cache wrong result when parsing + several files. + * Bugfix: Do not return NULL video bitrate for ASF v3. + * Bugfix: getid3_lib::GetImageSize() broken => JPG module broken. + * Bugfix: Encoder options should now be returned with correct + "--alt-preset n" / "--alt-preset cbr n" when scanning more files. + * Bugfix: Shorten module not escapeshellarg() filenames (UNIX only). + * Bugfix: Filenames not escapeshellarg() for md5_data and + sha1_data (UNIX only). + * Bugfix: UNIX: head and tail called with -cNNN instead of "-c NNN". + » Added detection support for PDF and MS Office documents + (*.doc, *.xls, *.pps, etc) (thanks zeromassmediaØgmail*com) + ¤ Bugfix: ID3v2 "TDRC" frame now used as "year" in comments if TYER + unavailable (TYER is deprecated in ID3v2.4) + (thanks matthiasØpanczyk*org) + ¤ Removed GETID3_OS_DIRSLASH, replaced with DIRECTORY_SEPARATOR + * Bugfix: added LAME preset guessing for presets 410,420,440,490 + (thanks adminØlogbud*com) + * Bugfix: Added escapeshellarg() call in getid3_lib::hash_data + (thanks towbØgmx*net) + » TAR module no longer reads entire file into memory + » FLV module no longer reads entire file into memory + * Bugfix: added LAME preset guessing for presets 410,420,440,490 + (thanks adminØlogbud*com) + * Bugfix: Added escapeshellarg() call in getid3_lib::hash_data + (thanks towbØgmx*net) + * Bugfix: Error message when padding in FLAC files were used up. + * Bugfix: Shorten module not working under windows. + ¤ Bugfix: gmmktime() instead of mktime(). + ¤ Using gmmktime() instead of mktime() in ISO, ZIP, PNG and RIFF + modules to avoid E_STRICT notices with PHP5.1+. + * Bugfix: ['comments_html'] and ['comments'] contains different + value when having multiple tags (one of them ID3v1) and the + long field names. + +1.7.6: [2006-03-12] James Heinrich + * Rewrote getid3_lib::GetDataImageSize() to use GetImageSize() + instead of using code by filØrezox*com + * Bugfix: incorrect dimensions from disabled Quicktime tracks + (thanks m-1Øgmx*net) + * Bugfix: ['codec'] key warning in module.audio-video.asf.php + (thanks niel*archerØblueyonder*co*uk) + * Bugfix: undefined array in write.php + (thanks drewishØkatherinehouse*com) + * Bugfix: DeleteAPEtag() incorrectly failing when no tag present + (thanks drewishØkatherinehouse*com) + * Bugfix: ID3v2 writing frames with URL fields failing when URL + is not in ISO-8859-1 (thanks drewishØkatherinehouse*com) + * Bugfix: PHP notices on bad ID3v2 frames + (thanks cw264701Øohiou*edu) + * Bugfix: audio & video bitrates sometimes wrong in ASF files + (thanks kris_kauperØexcite*com) + +1.7.5: [2005-12-29] James Heinrich + » Added FLV (FLash Video) support -- new file: + module.audio-video.flv.php + (thanks Seth Kaufman for code) + » Real tags can now be written (previous Real tag writing + code was not supposed to be in public releases, as it + was not complete) + » GETID3_HELPERAPPSDIR now autodetected under Windows + ¤ ASF lyrics now returned under [comments][lyrics] + * Bugfix: removed "--lowpass xxxxx" info from guessed + LAME presets when source frequency <= 32kHz + * Bugfix: ID3v2 extended header errors + * Bugfix: missing ob_end_clean() in write.id3v2.php + (thanks rasherØgmail*com) + +1.7.4: [2005-05-04] James Heinrich + ¤ Added ['quicktime']['hinting'] key (boolean) + (thanks jonØwebignition*net) + * Bugfix: major UTF-8 to UTF-16/ISO-8859-1 conversion + bug (empty string returned) when using iconv_fallback + (thanks chrisØfmgp*com) + * Bugfix: Missing 'lossless' key in RIFF-WAV + (thanks bobbfwedØcomcast*net) + +1.7.3: [2005-04-22] James Heinrich + » Added TAR support -- new file: module.archive.tar.php + (thanks Mike Mozolin for code!) + » Added GZIP support -- new file: module.archive.gzip.php + (thanks Mike Mozolin for code!) + * Bugfix: demo.browse.php now displays embedded images + internally instead of passing local filename as IMG + SRC (should allow demo.browse.php to correctly show + embedded images over a network) + (thanks patpowermanØhotmail*com) + * Bugfix: minor UTF-8 display issues in demo.browse.php + * Bugfix: demo.browse.php now works even if the evil + setting magic_quotes_gpc is turned on + (thanks patpowermanØhotmail*com) + * Bugfix: incorrect MIDI playtime for some files + (thanks joelØoneporpoise*com) + * Bugfix: 'url_source' typo in module.tag.id3v2.php + (thanks richardlynchØusers*sourceforge*net) + * Bugfix: Quicktime 'mvhd' matrix values were wrong + (thanks webØbobbymac*net) + ¤ ID3v2 now returns xx/yy for ['track'] (if + available), with xx in ['tracknum'] and yy in + ['totaltracks']. Previously ['tracknum'] was not + available and ['track'] had only xx. + Bugfixes and improvements to /demo/demo.mysql.php: + - remix/version parsed from tags and stored in + database, can be used when renaming files + - track number can be used for renaming files + + +1.7.2: [2004-10-18] Allan Hansen + » Added support for WavPack v4.0+ + (thanks ahØartemis*dk) + » Removed code for parsing EXE files + (thanks ahØartemis*dk) + Removed file: module.misc.exe.php + * Bugfix: Large ID3v2 tags inside ASF not parsed + properly under PHP5. + * Bugfix: Certain Wavpack3 files failed under PHP5 due + to new undocumented tmpfile() limit (same problem as + above). + * Bugfix: New iTunes crashes PHP - temp fix - no tags + on those files. + * Bugfix: ['nsv']['NSVs']['framerate_index'] might be + wrong (thanks ahØartemis*dk) + * Bugfix: transparent color was wrong from truecolor + PNG (thanks ahØartemis*dk) + * Bugfix: Changed MPC SV7 header size from 30 to 28, + this will change hash values for MPC files + (thanks ahØartemis*dk) + * Bugfix: Changed MPC SV4-6 header size from 28 to 8, + this will change hash values for MPC files + (thanks ahØartemis*dk) + ¤ Trim/unset wavpack encoder_options to match 2.0.0b2 + output. + ¤ Commented-out unknown/unused values in NSV and ISO + modules (thanks ahØartemis*dk) + + +1.7.1b1: [July-26-2004] James Heinrich + » Added support for Apple Lossless Audio Codec + » Added support for RealAudio Lossless + » Added support for TTA v3 + » Added support for TIFF + New file: /getid3/module.graphic.tiff.php + » Modified iconv_fallback to work with UTF-8, UTF-16, UTF-16LE, + UTF-16BE and ISO-8859-1 even if iconv() and/or XML support is + not available. This means that iconv() is no longer required + for most users of getID3() + (thanks Jeremia, khleeØbitpass*com) + » Added support for Monkey's Audio v3.98+ (thanks ahØartemis*dk) + » Included new demo showing most-basic getID3() usage + New file: /demos/demo.basic.php + * Bugfix: LAME3.94+ presets cached incorrectly if multiple files + are scanned in one batch and first file is LAME3.93 or earlier + (thanks enoyandØyahoo*com) + * Bugfix: Added warning if compressed ID3v2 frame decompression + fails. (thanks Mike Billings) + * Bugfix: Assorted small fixes to ensure compatability with PHP5 + * Bugfix: ID3v1 genre "Blues" could not be written + (thanks Jeremia) + * Bugfix: ['bitrate_mode'] typo in module.audio-video.real.php + (thanks asukakenjiØusers*sourceforge*net) + * Bugfix: ['zip']['files'] is now populated with filenames even + if End Of Central Directory couldn't be parsed + * Bugfix: ['audio']['lossless'] was incorrect for FLAC + (thanks WaldoMonster) + * Bugfix: MD5 File was incorrect in directory browse mode for + /demo/getid3.browse.php + * Bugfix: PHP v5 compatability changes (float array keys, fread() + calls with zero data length) + (thanks getid3Øjsc*pp*ru) + * Bugfix: was dying if on compressed ID3v2 frames if + gzuncompress() function was unavailable + * Bugfix: ['vqf']['COMM'] was always empty + * Bugfix: MIDI playtime was missing for single-track MIDI files + * Bugfix: removed � characters from ['comments_html'] + (thanks p*quaedackersØplanet*nl) + * Bugfix: improved MIDI playtime accuracy + (thanks joelØoneporpoise*com) + * Bugfix: BMP subtypes 4 and 5 were not being identified + * Bugfix: frame_rate in AVI was incorrectly truncated to integer + * Bugfix: FLAC cuesheet track index was incorrect + (thanks tetsuo*yokozukaØoperamail*com) + ¤ ['quicktime']['display_scale'] now contains the playback scale + multiplier for QuickTime movies - a movie set to playback at + double-size will have "2" here. Other values are "1" and "0.5" + ¤ Added LAME preset guessing for --preset medium with v3.90.3 + (thanks phwipØfish*co*uk) + ¤ Added $encoding_id3v1 to allow for ID3v1 encodings other than + the standard ISO-8859-1 + ¤ Default AVI video bitrate_mode is now 'vbr' + (thanks eltoderØpisem*net) + Force getID3() to abort if Shorten files have ID3 or APE tags + (thanks ahØartemis*dk) + Editable textbox for parent directory in demo.browse.php + (thanks eltoderØpisem*net) + + +1.7.0-hotfix [2004-03-17] Allan Hansen + (hotfix version released by Allan Hansen) + * Bugfix: PHP 4.1.x compatiblity - fgets($fp) => fgets($fp, 1024) + * Bugfix: Added default charset to TextEncodingNameLookup() in + module.tag.id3v2.php + Ø Removed option_no_iconv + iconv() support is only a requirement for WMA/WMW/ASF, and for + destination encodings other than ISO-8859-1 and UTF-8, iconv is + not needed otherwise. New 'iconv_req' in GetFileFormatArray() + only set for WMA/WMV/ASF. analyze() now refuses to analyse + WMA/ASF file if iconv is not present. + iconv_fallback() only dies on internal errors not missing iconv() + + +1.7.0: [January-19-2004] James Heinrich + » Added support for RIFF/CDXA files (MPEG video in RIFF container + format (thanks chrisØdigitekdesign*com) + » Added support for TTA v2 (thanks ahØartemis*dk) + ¤ ID3v2 unsynchronisation scheme disabled by default because most + tag-reading programs cannot read unsynchronised tags. Can be + overridden by setting id3v2_use_unsynchronisation to true. + (thanks mikeØdelusion*org) + ¤ extention.*.php renamed to extension.*.php + (thanks tp62Øcornell*edu) + ¤ /demo/demo.check.php renamed to /demo/demo.browse.php + ¤ Added id3v2_paddedlength configuration parameter to WriteTags() + and renamed tag_language to id3v2_tag_language + ¤ MPEG audio layers are now represented as 1, 2 or 3 instead of + 'I', 'II', or 'III' + ¤ Added [audio][wformattag] and [video][fourcc] for WAV and AVI + ¤ Added [audio][streams] which contains one entry for each audio + stream present in the file (usually only one). The data is a + copy of what is usually found in [audio]. If there are multiple + audio streams then [audio] will contain a sum of the bitrates + of all audio streams, and the data format of the first stream + (if streams are of different data types) + ¤ Added BruteForce mode to mp3 scanning. Disabled by default as + it is extremely slow and only files that are broken enough to + not really play will gain any benefit from this. + ¤ Suppress '--resample xxxxx' appended to encoder options for mp3 + with low-quality presets for default sampling frequencies + ¤ Enhanced LAME preset guessing for pre-3.93 with a better lookup + table, --resample/--lowpass guessing (thanks phwipØfish*co*uk) + ¤ RIFF files with non-MP3 contents no longer have + [audio][encoder_options] set + ¤ Added [audio][encoder_options] to audio formats where possible + (including LiteWave, LPAC, OptimFROG, TTA) + ¤ Moved [quantization] and [max_prediction_order] from + [lpac][flags] to just [lpac] + ¤ WavPack flags are now parsed into [wavpack][flags] + * Bugfix: APEtags with ReplayGain information stored with comma- + seperated decimal values (ie "0,95" instead of "0.95") were + giving wrong peak and gain values + * Bugfix: Filesize > 2GB not always detected correctly + * Bugfix: Some ID3v2 frames had data key unset incorrectly + (thanks chrisØdigitekdesign*com) + * Bugfix: Warnings on empty-strings-only comments + * Bugfix: ID3v2 tag writing may have had incorrect padding length + if padded length less than current ID3v2 tag length and + merge_existing_data is false (thanks mikeØdelusion*org) + * Bugfix: hash_data() for SHA1 was broken under Windows + * Bugfix: BigEndian2Float()/LittleEndian2Float() were broken + * Bugfix: LAME header calculated track peaks were incorrect for + LAME3.94a15 and earlier + * Bugfix: AVIs with VBR MP3 audio data reported incorrect bitrate + and bitrate_mode + * Bugfix: AVIs sometimes had incorrect or missing video and total + bitrates + * Bugifx: AVIs sometimes had incorrect ['avdataend'] and + therefore also incorrect data hashes (md5_data, sha1_data) + * Bugfix: ID3v1 genreid no longer returned for Unknown genre + * Bugfix: ID3v1 SCMPX genres were broken + Modified LAME header parsing to correctly process peak track + value for LAME3.94a16+ (thanks Gabriel) + md5_file() and sha1_file() now work under Windows in PHP < 4.2.0 + and 4.3.0 respectively with helper apps + Default md5_data() tempfile location is now system temp directory + instead of same directory as file (thanks towbØtiscali*de) + Improved list of RIFF ['INFO'] comment key translations + More helpful error message when GETID3_HELPERAPPSDIR has spaces + /demo/demo.browse.php now autogets both MD5 and SHA1 hashes for + files < 50MB + Replaced PHP_OS comparisons with GETID3_OS_ISWINDOWS define + (thanks necroticØusers*sourceforge*net) + + +1.7.0b5: [December-29-2003] James Heinrich + » Windows only: Various binary files are now required for some + file formats, especially for tag writing, as well as md5sum + (and other) calculations. These binaries are now stored in the + directory defined as GETID3_HELPERAPPSDIR in getid3.php + (default is /helperapps/ parallel to /getid3/). + Note: This directory must not have any spaces in the pathname. + All neccesary files are available as a seperate download. + See /helperapps/readme.txt for more information + New file: /helperapps/readme.txt + » Unified tag-writing interface for all tag formats + New file: /getid3/write.php + /getid3/write.apetag.php + /getid3/write.id3v1.php + /getid3/write.id3v2.php + /getid3/write.lyrics3.php + /getid3/write.metaflac.php + /getid3/write.vorbiscomment.php + » Added support for Shorten - requires shorten binary (head.exe + is also required under Windows). + New file: /getid3/module.audio.shorten.php + » Added support for RKAU + New file: /getid3/module.audio.rkau.php + » Added (minimal) support for SZIP + New file: /getid3/module.archive.szip.php + » Added MySQL caching extention (thanks ahØartemis*dk) + New file: /getid3/extention.cache.mysql.php + » Added DBM caching extention (thanks ahØartemis*dk) + New file: /getid3/extention.cache.dbm.php + » Added sha1_data hash option (thanks ahØartemis*dk) + » Added option to allow getID3() to skip ID3v2 without parsing it + for faster scanning when ID3v2 data is not required. If you + want to enable this feature delete /getid3/module.tag.id3v2.php + (thanks ahØartemis*dk) + ¤ 8-bit WAV data now calculates MD5 checksums as normal, not + converting to signed data as before, so stored md5_data_source + in FLAC files will no longer match md5_data for the equivalent + decoded 8-bit WAV. A warning will be generated for 8-bit FLAC + files + ¤ Added option_no_iconv option to allow getID3() to work + partially without iconv() support enabled in PHP + (thanks ahØartemis*dk) + ¤ All '*_ascii' keys removed for ASF/WMA/WMV files + ¤ All 'ascii*' keys removed for ID3v2 tags + ¤ Ogg filetypes now return MIME of "application/ogg" instead of + the previous "application/x-ogg" + (thanks blakewattersØusers*sourceforge*net) + ¤ Force contents of ['id3v2']['comments'] to UTF-8 format from + whatever encoding each frame may have (text encoding can vary + from frame to frame in ID3v2) + ¤ MP3Gain information from APE tags suppressed from ['tags'] and + parsed into ['replay_gain'] + ¤ ReplayGain information (all formats) changed from "Radio" and + "Audiophile" to "Track" and "Album" respectively + ¤ ['volume'] and ['max_noclip_gain'] are now available in both + ['replay_gain']['track'] and ['replay_gain']['album'] for all + formats that calculate ReplayGain. + ¤ ['video']['total_frames'] is available for AVIs + ¤ All parsed ID3v2 frame data is now in ['id3v2'][XXXX][#] + (previously some frame types would have numeric array keys if + multiple instances of that frame type were allowed and other + frame types would not) + ¤ ASF/WMA "WM/Picture" images are now parsed in the same manner + as ID3v2 with the image (ex JPEG) data returned in [data] + rather than [value] + * Bugfix: Optional tag processing options were being ignored (ie + ID3v1 still processed even if option_tag_id3v1 == false) + (thanks ahØartemis*dk) + * Bugfix: fixed MultiByteCharString2HTML() for UTF-8 + * Bugfix: Quicktime files not always reporting video frame_rate + * Bugfix: False ID3v1 synch patterns in APE or Lyrics3 tags are + now detected and incorrect ID3v1 data not returned + (thanks sebastian_maresØusers*sourceforge*net for the idea) + * Bugfix: WMA9 Lossless now reported as lossless + * Bugfix: two typos in ID3v1 genre list + * Bugfix: MPEG-2/2.5 ABR/VBR MP3 files had doubled playtime + * Bugfix: MPEG-2/2.5 LayerII (ie MP2: 24/22.05/16kHz) files were + not detected due to incorrect frame length calculation + * Bugfix: MPEG LayerI files were not detected due to incorrect + frame length calculation (must be multiple of slot length) + Added alternative md5_data via system call - twice as fast. Needs + "getID3()-WindowsSupport" to work under Windows. + (thanks ahØartemis*dk) + ID3v2.4 compressed frames are now supported + php_uname() calls changed to use PHP_OS constant + Added SCMPX extensions to ID3v1 genres (0xF0-0xFE) + Obfuscated contributor email address in changelog and sourcecode + Added memory-saving EmbeddedLookup() function for lookup tables + in RIFF and ID3v2 modules (thanks ahØartemis*dk) + Major memory patches to many modules by using + $var = &$INFO_ARRAY_AT_SOME_INDEX + in place of large multi-dimensional array declarations. + Memory saved: RIFF: ~200kB; ID3v2: ~475kB; ASF: ~50kB etc. + (thanks ahØartemis*dk) + + +1.7.0b4: [November-19-2003] James Heinrich + » Support added for MPC files with old SV4-SV6 structure + » RealVideo now properly supported with resolution, framerate, etc + (thanks jcsston) + » RealAudio files with old-style file format (v2-v4) are now + fully supported + » Support added for DolbyDigital WAV files (thanks ahØartemis*dk) + ¤ ['RIFF'] is now ['riff'] to conform to make all root key names + lowercase + ¤ ['OFR'] is now ['ofr'] to conform to make all root key names + lowercase + ¤ ['tags_html'] is now available as a copy of ['tags'] but + with all text replaced with an HTML version of all characters + above chr(127), translated according to whatever the encoding + of the source tag is, in the HTML form Ӓ + ¤ CopyTagsToComments() is now available in getid3_lib + ¤ QuicktimeVR files now return a ['video']['dataformat'] of + 'quicktimevr' instead of 'quicktime' (thanks gtsØtsu*biz) + ¤ Quicktime video files with DivX, Xvid, 3ivx or MPEG4 video + streams now return those names as ['video']['dataformat'] + ¤ MPEG video files are now identified with ['video']['codec'] set + to either 'MPEG-1' or 'MPEG-2' (rather than just 'MPEG'). If you + see a file wrongly identified, please report it! + (thanks fccHandler) + ¤ All bitrate values in ['mpeg']['audio'] is now reported in bps + rather than kbps (ie 128000 instead of 128) for consistancy + ¤ AVIs with MP2 audio now report ['audio']['dataformat'] as 'mp2' + rather than 'wav' (thanks metalbrainØnetian*com) + ¤ Added ['md5_data_source'] for OptimFROG + ¤ AC3 in RIFF-WAV now identified with ['audio']['dataformat'] + returning 'ac3' + ¤ WavPack ['extra_bc'] now returned as integer + ¤ WavPack ['extras'] now returned as 3-element array of integers + ¤ MP3 ['audio']['encoder options'] now returns 'VBR' or 'CBR' only + (no bitrate) if no LAME preset is used, or 'VBR q??' where ?? is + a number 0-100 for Fraunhofer-encoded VBR MP3s + * Bugfix: VBR MP3s could have incorrect bitrate reported + * Bugfix: Quicktime files with MP4 audio were not returning + ['video']['dataformat'] (thanks robØmassive-interactive*nl) + * Bugfix: strpad vs str_pad typo in module.riff.php + (thanks nicojunØusers*sourceforge*net) + * Bugfix: ReplayGain information was often wrong for MPC files + * Bugfix: MD5 and other post-TAIL chunks were not being processed + in module.audio.optimfrog.php + * Bugfix: Undefined variable in table_var_dump() in demo/check.php + * Bugfix: QuickTime files now only return information in [audio] + or [video] if those exist in the file + * Bugfix: WavPack no longer tries to read entire compressed data + chunk + * Bugfix: Properly handle VBR MP3s with "Info" (rather than + "Xing") header frame. foobar2000 adds this to MP3 files when + "Fix MP3 Header" function is used (thanks ahØartemis*dk) + * Bugfix: Fraunhofer VBRI headers for MP3s were assuming 2-byte + entries for TOC rather than using stride, and were ignoring the + scaling value. (thanks sebastianØmaresweb*net) + Several QuickTime atoms have been added to an exclusion list + because they have been observed, but I have no idea what they + are supposed to do so I can't add real support for them, but + they should not generate warnings (robØmassive-interactive*nl) + Old MPC encoder (before v1.06) was return as v0.00, now returned + as 'Buschmann v1.7.0-v1.7.9 or Klemm v0.90-v1.05' + (thanks ahØartemis*dk) + Added check for magic_quotes_runtime and code to disable it if + neccesary (thanks stefan*kischkelØt-online*de) + Added 3ivx fourCCs to module.audio-video.quicktime.php + MP3 and AC3 streams are now parsed when contained inside RIFF-WAV + or RIFF-AVI container formats + Better detection of named presets in LAME 3.93/3.94 + + +1.7.0b3: [October-17-2003] James Heinrich + » AC-3 (aka Dolby Digital) is now supported. + New file: /getid3/module.audio.ac3.php + * Bugfix: ID3v2-writing function Unsynchronise() was broken, which + made ID3v2 tag containing binary data (typically pictures) get + corrupted. (thanks t*coombesØbendigo*vic*gov*au, + i*kuehlbornØndh*net, mikeØdelusion*org, mikeØftl*com) + * Bugfix: Zip comments now returned as array instead of string, + as they're supposed to be. + * Bugfix: Quicktime/MP4 files may have reported extremely low + bitrates (thanks spunkØdasspunk*com) + Improved double-ID3v1 check to prevent false detection when string + "TAG" is present in APE or Lyrics3 + Fixed /demo/simple.php + Fixed /demo/joinmp3.php + Fixed /demo/mimeonly.php + Fixed /demo/write.php + + +1.7.0b2: [October-15-2003] James Heinrich + » TTA Lossless Audio Compressor format now supported. + (http://tta.iszf.irk.ru) + New file: /getid3/module.graphic.tta.php + » PhotoCD (PCD) format now supported. Image data for the three + lowest resolutions (192x128, 384x256, 768x512) can be optionally + extracted. + New file: /getid3/module.graphic.pcd.php + ¤ RIFF-MP3 files now should return the same ['md5_data'] as the + identical MP3 file outside the RIFF container + ¤ Name of LAME preset used (if available, needs LAME v3.90+) + returned in ['mpeg']['audio']['LAME']['preset_used'] and also as + part of ['audio']['encoder_options'] + ¤ VQF module now sets ['audio']['encoder_options'] to i.e. CBR96 + ¤ MP3 module now sets ['audio']['encoder_options'] on CBR files + and all LAME-encoded files + ¤ MPC module now sets ['audio']['encoder_options'] + ¤ Monkey module now sets ['audio']['encoder_options'] + ¤ AAC module now sets ['audio']['encoder_options'] to profile name + ¤ ASF module now sets ['audio']['encoder_options'] + ¤ Ogg module adds ['audio']['encoder_options'] -b 128 on + Ogg Vorbis 1.0+ ABR files + ¤ Ogg module adds ['audio']['encoder_options'] -q N on + Ogg Vorbis 1.0+ VBR files 44k/48k sample rate/stereo files only. + ¤ Ogg module ['audio']['encoder_options'] "Nominal birate: 80k" to + other Ogg Vorbis files. + ¤ ID3v2 track number now returned as string (with leading zeros, + if present in data) rather than integer (thanks Plamen) + ¤ ASF module returns ['asf']['comments']['encoding_time_unix'] if + available (from WM/EncodingTime) + ¤ Fixed /demo/mysql.php and added some new features: + - encoder options + - ID3v2 "Encoded By" + - non-empty comments + - total entries in database summary (totals & averages) + - database version update + * Bugfix: 'UNICODE' iconv() charset changed to 'UTF-16LE' or + 'UTF-16BE' as appropriate + * Bugfix: iconv_fallback() function created in case iconv() fails + * Bugfix: fixed MD5 calls in demo/check.php + * Bugfix: reenabled detection of APE + Lyrics3 tags in same file + * Bugfix: ASF module now returns ID3v1 genre as string instead of + number - patch from Eugene Toder. + * Bugfix: ASF module now reads non-standard field names, + i.e. "date" as well as WM/Year - patch from Eugene Toder. + * Bugfix: ASF module now returns genre as-is if it is not a + standard ID3v1 genre (thanks wonderboy) + * Bugfix: Eliminated false-synch problem in MP3 module + * Bugfix: Fixed missing root ['bitrate'] for most formats + * Bugfix: ['audio']['compression_ration'] missing for MPC + (thanks WaldoMonster) + * Bugfix: NSV module died in 1.7.0b1 + * Bugfix: ASF module died in 1.7.0b1 when WM/Picture preset + * Bugfix: ASF tracknumber incorrect when specified by WM/Track + rather than WM/TrackNumber (thanks jgriffiniiiØhotmail*com) + * Bugfix: MPEG audio+video playtime should now be pretty accurate + (ie within 0.1% variation at most) + (thanks mgrimmØhealthtvchannel*org) + * Bugfix: ID3v2 not being copied to ['tags'] in some cases + * Bugfix: LAME CBR files with Info tag were being incorrectly + flagged as VBR (thanks Jojo) + * Bugfix: LAME tag not being detected for LAME 3.90 (original) + Changed regex pattern match for MP3 to include 3rd byte for more + reliable/accurate pattern matching + Added duplicate-ID3v1 tag checking (two ID3v1 tags, one after the + other) that has been known to occur with iTunes + (thanks towbØtiscali*de) + Added instructions for enabling iconv() support under Windows + Removed some unneccesary debugging code + Suppressed duplicate PHP warnings for missing include files + Included some missing dependencies in various files + /demo/audioinfo.class.php now copies ['audio']['encoder_options'] + + +1.7.0b1: [2003-09-28] Allan Hansen + This beta version was not made by James Heinrich. It was made by + Allan Hansen - please send bug reports on this + beta directly to me. + + James Heinrich will release 1.7.0 final, but it may take some time + to work out the bugs from the major rewrite. + + This version could be called getID3lite. It makes a lot of checks + optional and makes it easy to remove support for undesired formats + + It also is more library-like. Older versions of getID3() declared + an incredible amount of global scope functions and defined several + constants. 1.7.0beta1 still declares constants, but they are all + prepended by GETID3_. It declares no global scope functions - they + are all wrapped into classes. + + » Made getID3() depend on iconv library: compile PHP --with-iconv + » Created new directory structure + Moved all demos to demos/ + Moved all getID3() files to getid3/ + Renamed most files to module.something + Changed header in all module.something to explain what they do + Simply remove all modules you don't need + Wrapped all modules into classes + * Bugfix: Implemented misc patches from Eugene Toder + * Bugfix: Implemented misc patches from "six" + ¤ Added root key 'encoding' + ¤ Added prefix GETID3_ to all defined constants. + ¤ Wrapped getid3.php into getid3 class + ¤ Wrapped getid3.functions.php into getid3_lib class + Removed unused functions + Moved several functions away from getid3.functions.php and + into the files where they are actually used. + Renamed getid3.functions.php to getid3.lib.php + Moved getid3.rgad.php functions into getid3_lib + Moved getid3.getimagesize.php funcitons ingo getid3_lib + ¤ Moved getid3.ogginfo.php into ogg module + ¤ Combined GetTagOnly() and GetAllFileInfo() in method analyze + ¤ Removed redundant and unuseful root keys + 'file_modified_time' == filemtime($filename) + 'md5_file' == md5_file($filename) + 'exist' == file_exists($filename) + ¤ Changed root key ['tags'] from array of string to array of array + of comments. + Simplified code for detecting base path. + Removed ob_ from InitializeFilepointerArray(). That was really a + ugly HACK to get output from fopen. If user want the reason, + he should open the file himself! + Checking for APE tags before lyrics3 - makes Lyrics3 not depend + on APE tag. It seems to work on my test file. + Changed ['error'] and ['warning'] in multiple files to append to + array instead of appending to string. That simplified code in + getid3.php too. + Simplified clean-up procedure: simply remove all empty root keys + Setting tags in individual modules instead of main getid3.php + Made Bonk and ASF modules non-dependent on id3 modules - id3 + optional. + Rewrote HandleAllTags() - simplified and convert comments to + desired encoding. + Replaced all calls to RoughTranslateUnicodeToASCII() in ASF module + with a TrimConvert() method. This uses iconv() for conversion. + It also converts from UNICODE instead of UTF-16BE, as the spec + says it should. + Replaced all calls to RoughTranslateUnicodeToASCII() in id3v2 + module with iconv(). id3v2 module also reads + $ThisFileInfo['encoding'] and converts all comments to this + format. All other formats just add their comments in their + native charset, but every comment field in id3v2 can have a + different encoding, so this is needed. + Did same thing as above with ISO module. However - it does not + work. I need to find out how to specify big-endian unicode != + UNICODING encoding name given to iconv(). + Built-in assume mp3 format in getid3.php + Temporarily nuked root key ['comments'] and CopyCommentsToRoot() + Updated demo/audioinfo.class.php + Updated demo/check.php - some thing don't work! + Other demos are out of order! + + +1.6.5: [October-06-2003] James Heinrich + » Added support for LiteWave (thanks supportØclearjump*com) + Ø Split out speedup info from ['OFR']['OFR']['compression'] into + ['OFR']['OFR']['speedup'] + Ø If EXIF functions for JPEG not available, now warning not error + Ø ID3v2 track number now returned as string (with leading zeros, + if present in data) rather than integer (thanks Plamen) + * Bugfix: now correctly parses cbSize element of WAVEFORMATEX + structure (thanks supportØclearjump*com) + * Bugfix: ASF module now reads non-standard field names, + i.e. "date" as well as WM/Year - patch from Eugene Toder. + * Bugfix: ASF module now returns genre as-is if it is not a + standard ID3v1 genre (thanks wonderboy) + * Bugfix: ['audio']['compression_ration'] missing for MPC + (thanks WaldoMonster) + Prevent infinite loop in MP3 histogram if framelength == 0 + Added wFormatTag values 0x00FF and 0x2001 - 0x2005 + (thanks steveØheadbands*com) + Added "twos" and "sowt" FourCCs for Mac AIFC + + +1.6.4: [June-30-2003] James Heinrich + » Added support for free-format MP3s + (thanks Sebastian Mares for the idea) + » Compressed (Flash 6+) SWF files are now handled properly + (thanks alan*cheungØalumni*ust*hk) + » Added DeleteLyrics3() to getid3.lyrics3.php + » Added FixID3v1Padding() to getid3.putid3.php + » Added new simple MP3-splicing sample file + (thanks tommybobØmailandnews*com for the idea) + New file: getid3.demo.joinmp3.php + » Moved all contents of getid3.putid3.php into either + getid3.id3v1.php or getid3.id3v2.php or getid3.functions.php as + appropriate + Removed file: getid3.putid3.php + ¤ ['error'] and ['warning'] keys now return as arrays, not strings + ¤ New root key for all files: ['file_modified_time'] (UNIX time) + ¤ getid3.demo.scandir.php renamed to getid3.demo.mysql.php + ¤ New demo file returns the MIME type only for a single file + (thanks adminØe-tones*co*uk for the idea) + New file: getid3.demo.mimeonly.php + ¤ Added check for valid ID3v1 padding (strings should be padded + with null characters but some taggers incorrectly use spaces). + A warning will be generated if padding is invalid. New boolean + key ['id3v1']['padding_valid'] indicates padding validity. + ¤ CleanUpGetAllMP3info() removes more useless root keys for + unknown-format files + ¤ Extended LAME information in ['mpeg']['audio']['LAME'] is now + only returned for LAME v3.90+ + ¤ LAME-encoded MP3s now return + ['mpeg']['audio']['LAME']['long_version'] as well as + ['mpeg']['audio']['LAME']['short_version'] - these are identical + in LAME v3.90+ but older versions will report longer more + detailed version information if available + ¤ New Lyrics3 values: ['lyrics3']['raw']['offset_start'] and + ['lyrics3']['raw']['offset_end'] + ¤ New optional parameter on getAPEtagFilepointer() to scan from a + defined offset rather than end-of-file to allow scanning of APE + tags before Lyrics3 tags + ¤ ['tag_offset_start'] and ['tag_offset_end'] are now present in + ['ape'], ['lyrics3'], ['id3v1'] and ['id3v2'] + ¤ Numerous changes to the returned structure and content for La + files, including parsing the seektable (if applicable) and + parsing RIFF data occuring after the end of the compressed audio + data (notably RIFF comments) + (thanks mikeØbevin*de) + ¤ getSWFHeaderFilepointer() now has optional 3rd parameter + $ReturnAllTagData (default == false) which if set to true will + return data on all tags in ['swf']['tags'] + ¤ ['swf']['bgcolor'] now returns the 6-character string + representing the background color in HTML hex color format + (thanks ubergeekØubergeek*tv) + ¤ ['swf']['header']['frame_delay'] is no longer returned + ¤ getQuicktimeHeaderFilepointer() now has two additional optional + parameters: $ReturnAtomData (default == true) and + $ParseAllPossibleAtoms (default == false). Setting + $ReturnAtomData to false will reduce the size of the returned + data array by unsetting ['quicktime']['moov'] before returning. + Leaving $ParseAllPossibleAtoms as false now suppresses parsing + of several atom types that contain very large tables of data + that are not typically useful. Atom type suppressed are: + stts, stss, stsc, stsz, and stco + (thanks ubergeekØubergeek*tv) + ¤ ['fileformat'] no longer set to 'id3' if ID3v1 or ID3v2 tag + detected but no other data format recognized + * Bugfix: La files now return the correct values for + ['avdataoffset'] and ['avdataend'] and therefore the correct + values for ['md5_data'] - note that ['md5_data'] values will not + match values from previous versions of getID3() - the previous + versions were incorrect + (thanks mikeØbevin*de) + * Bugfix: A temporary file was being created in the web server's + root directory (not DocumentRoot) each time ['md5_data'] was + calculated, and not removed due to lack of permissions. Temp + file is now created (as it was supposed to be) in the directory + of the file being examined, or the system temp directory, and + properly removed when done. + * Bugfix: Several incorrect values were being returned inside + ['mpeg']['audio']['LAME'] (thanks bouvigneØmp3-tech*org) + * Bugfix: SWF frame rates values were usually incorrect. + (thanks alan.cheungØalumni*ust*hk and ubergeekØubergeek*tv) + * Bugfix: ID3v2.2 files always flagged 4 bytes of invalid padding + (thanks marcaØmac*com) + * Bugfix: Lyrics3 without ID3v1 was not working properly + * Bugfix: Lyrics3, APE & ID3v1 can all now exist in the same file. + A warning is issued if APE comes after Lyrics3 (because Lyrics3- + aware taggers probably are not APE-aware and therefore won't be + able to find the Lyrics3 tag) (thanks mp3gainØhotmail*com) + * Bugfix: WriteAPEtag() now writes the APE tag before any Lyrics3 + tags (if present) and removes any incorrect ones that are after + existing Lyrics3 tags (thanks mp3gainØhotmail*com) + * Bugfix: RIFF-WAVE file with incorrect NumberOfSamples values in + the 'fact' chunk no longer cause incorrect playtime calculation + (thanks stprasadØindusnetworks*com) + * Bugfix: getid3.demo.simple.php had undefined variables if the + file needed to be deep-scanned with assumeFormat + * Bugfix: fixed previously-incorrect ['avdataend'] values for APE + and Lyrics3 tags in some cases, which in some cases means that + ['md5_data'] is different than previously (now correct) + Much-improved detection of AAC-ADTS, which also means MP3 + format detection should now be nearly twice as fast + Truncated AVIs and WAVs are now reported + Number of new features and bugfixes in getid3.demo.mysql.php + Quicktime 'meta' atoms now parsed, so Quicktime MP4 files can now + return artist, title, album, etc (thanks spunkØdasspunk*com) + Consolidated all comments processing functions (processing the + ['comments'] and ['tags'] keys) into HandleAllTags() which now + also checks to ensure that APE tags are really better than ID3v2 + before using them in ['comments'] + Known issue with Meracl ID3 Tag Writer v1.3.4 truncating last byte + of MP3 file when appending new ID3v1 tag now specifically noted + (rather than generic Probably Truncated File message) + getid3.demo.mysql.php now stores last-modified time for each file + getid3.demo.mysql.php is now case-sensitive for filenames + getid3.demo.mysql.php can generate M3U playlists of any of the + groups of files it can select (duplicate filenames, tag types, + etc.) + getid3.demo.mysql.php can now find mismatched tag contents and + filenames + getid3.demo.check.php now shows total number of errors & warnings + GetFileFormatArray() now matches actual patterns for MP3 files + based on the first two bytes of the file, rather than just the + first one + Simplified DeleteAPEtag() and made it work properly with Lyrics3 + + +1.6.3: [May-17-2003] James Heinrich + » Added support for Bonk (thanks ahØartemis*dk) + New file: getid3.bonk.php + » Added support for AVR (thanks ahØartemis*dk) + New file: getid3.avr.php + ¤ Contents of getid3.id3.php moved to getid3.id3v1.php + Removed file: getid3.id3.php + ¤ Contents of getid3.frames.php moved to getid3.id3v2.php + Removed file: getid3.frames.php + ¤ Returned data structure documentation improved and updated and + now stored in getid3.structure.txt rather than getid3.readme.txt + New file: getid3.structure.txt + ¤ Now including the GNU General Public License in the distribution + as getid3.license.txt + New file: getid3.license.txt + ¤ Added new, optional, parameter to WriteAPEtag() (and also + GenerateAPEtag()) which must be set to TRUE if the values you + are passing are already UTF8-encoded, otherwise all data is + encoded to UTF8 by default. For all ASCII/ANSI data this value + should be left at the defaul value of FALSE. + ¤ Added third, optional, parameter to getID3v2Filepointer() - + $StartingOffset (default == 0) which can parse an ID3v2 tag + in a file at a position other than the start-of-file. + ¤ ['video']['pixel_aspect_ratio'] now returned when known + ¤ AVI files with WMA audio now return ['audio']['dataformat'] + of 'wma' rather than 'wav' + ¤ ASF-WMA files now return the artist value from WM/AlbumArtist + in ['comments']['artist'] (thanks msibbaldØsaebauld*com) + ¤ ASF-WMA files now return the 'author' value from + ['asf']['content_description'] in ['comments']['artist'] + instead of ['comments']['author'] + ¤ ASF-WMA files now return the 'description' value from + ['asf']['content_description'] in ['comments']['comment'] + instead of ['comments']['description'] + * Bugfix: APE tag writing with multiple values for a tag (more + than one ARTIST for example) was not being correctly written + (thanks ahØartemis*dk) + * Bugfix: CreateDeepArray() was returning an empty-string key as + the top-level returned value - ['iso']['files'] now directly + contains the file listing without an empty array in between. + * Bugfix: ID3v2 genreid was not being returned in some cases. + * Bugfix: APEv1 tags would generate error messages + * Bugfix: APE tags would sometimes show phantom second entry for + each item (title, artist, etc) with no data + * Bugfix: APE tag writing was not UTF8-encoding the data - + non-ASCII characters (above chr(127)) were being incorrectly + stored (thanks ahØartemis*dk) + * Bugfix: getid3.demo.scandir.php had undefined function error + * Bugfix: getid3.demo.scandir.php would not display list of files + with no tags + Added link to getid3.demo.check.php from list of specific-tags + files in getid3.demo.scandir.php + + +1.6.2: [May-04-2003] James Heinrich + » New official mirror site for getID3() - http://www.getid3.org + » Added basic support for SWF (Flash) (thanks n8n8Øyahoo*com) + New file: getid3.swf.php + » Added experimental support for parsing the audio portion of + MPEG-video files. I don't have any actual documentation for + this, so this part is experimental and not guaranteed accurate, + but it seems to be working OK as far as I have been able to test + it. Bug reports (or even better - documentation!) are welcome at + info@getid3.org + » Added new simple directory-scanning sample file + New file: getid3.demo.simple.php + » getid3.demo.write.php now writes APE tags as well. + ¤ Renamed getid3.write.php to getid3.demo.write.php + ¤ Renamed audioinfo.class.php to getid3.demo.audioinfo.class.php + ¤ getid3.php now automatically includes the getid3.functions.php + function library file, no need to include it seperately. + ¤ getLyrics3Filepointer() has been changed to be consistant with + all the other similar function structures - the parameters have + changed. The old function has been renamed to getLyrics3Data() + ¤ Added DeleteAPEtag() function to getid3.ape.php + ¤ HandleID3v1Tag() now only handles ID3v1. Lyrics3 processing is + now done by HandleLyrics3Tag() + ¤ If BitrateHistogram is enabled in getOnlyMPEGaudioInfo() it now + also returns ['mpeg']['audio']['version_distribution'] showing + the number of frames of each MPEG version (1, 2 or 2.5) - all + frames *should* be of the same MPEG version + ¤ getID3v1Filepointer() always returns TRUE now, even if it didn't + find a valid ID3v1 tag + ¤ getOnlyMPEGaudioInfo() now looks for MPEG sync in the first 128k + bytes rather than the first 64k bytes + ¤ Added dummy function GetAllMP3info() to generate warning not to + use that deprecated function. + ¤ ['video']['codec'] is now 'MPEG' for all MPEG video files (this + will change to 'MPEG-1' or 'MPEG-2' as soon as I figure out how + to determine that) (thanks jigalØspill*nl) + ¤ ['mpeg']['audio']['LAME']['mp3_gain'] renamed to + ['mpeg']['audio']['LAME']['mp3_gain_db'] (gain in dB) + ¤ Added ['mpeg']['audio']['LAME']['mp3_gain_factor'] (gain as a + multiplication factor) + ¤ Added support for Preset and Surround Info bytes from LAME VBR + tag (http://gabriel.mp3-tech.org/mp3infotag.html) + * Bugfix: APE tag writing would put the string 'Array' for all + values rather than the actual data (thanks ahØartemis*dk) + * Bugfix: Warning now generated for VBR MPEG-video files because + getID3() cannot determine average bitrate. If you know of + documentation that would tell me how to do this, please email + info@getid3.org + * Bugfix: Replay Gain values from Vorbis comments are now + returned in ['replay_gain'] (and not in ['comments']) + (thanks ahØartemis*dk) + * Bugfix: Replay Gain values from APE comments are now correctly + returned in ['replay_gain'] (thanks ahØartemis*dk) + * Bugfix: getid3.demo.check.php is now case-insensitive when + assuming a format for a corrupted file if standard detection + does not identify the file type. + * Bugfix: RIFF comments were overwriting/suppressing ID3 comments + for RIFF-MP3 files (thanks wmØwofuer*com) + * Bugfix: RIFF-MP3 files with 'RMP3' chunks instead of 'WAVE' were + not being correctly identified. + * Bugfix: ID3v2 padding shorter than the length of an ID3v2 frame + header was not correctly detected + * Bugfix: getid3.demo.check.php now does in-depth scanning for MP2 + and MP1 files the same as for MP3 files based on file extension + if a MPEG-audio structure isn't found immediately at the start + of the file + * Bugfix: removed condition where RIFF-WAV was being scanned for + MPEG-audio signature when it shouldn't be present (non-MP3 WAV) + * Bugfix: ASF files were not always showing correct audio datatype + * Bugfix: array_merge_clobber() and array_merge_noclobber() were + not being conditionally defined in getid3.functions.php + (thanks rich.martinØreden-anders*com) + * Bugfix: stream_numbers was not being correctly returned in + bitrate_mutual_exclusion_object chunks of ASF files + * Bugfix: Added support for 24kHz and 12kHz audio in ASF files + * Bugfix: Removed possible undefined offset error in MP3s where + cannot find synch before end of file + * Bugfix: Removed potential out-of-memory crash situation when + parsing Real files with chunks larger than the available memory + (thanks jigalØspill*nl) + * Bugfix: ID3v1 was incorrectly taking precedence over ID3v2 in + the ['comments'] array (thanks lionelflØwanadoo*fr) + * Bugfix: No longer calculates overall bitrate and playtime for + VBR MPEG video files based on the audio bitrate. + * Bugfix: AssumeFormat was not working properly + Added summary footer line to getid3.demo.check.php + Added '.mpeg' to the list of assume-format-from-filenames list in + getid3.demo.check.php + MPEG-video files now more reliably detected + A number of additional features have been added to + getid3.demo.scandir.php + Added many RIFF-AVI audio types and fourcc video types to the + lookup functions in getid3.riff.php + Now identifes files with Lyrics3 v1 tags that are of incorrect + length (v1 Lyrics3 is supposed to be 5100 bytes long, but + [unknown program] writes variable-length tags (which is illegal + for Lyrics3 v1)). getID3() now correctly parses these tags and + issues a warning. + Split GetFileFormat() to GetFileFormat() and GetFileFormatArray() + HTML colors in getid3.demo.check.php are now defined as constant + variables at the top of the file (if you want to change them) + Added support for OptimFROG v4.50x (non-alpha) (new header fields) + (thanks floringhidoØyahoo*com) + Added support for Lossless Audio v0.4 (thanks mikeØbevin*de) + + +1.6.1: [March-03-2003] James Heinrich + » Added support for writing APE v2. + WriteAPEtag() in getid3.ape.php + NOTE: APE v1 writing support will *not* be added to future + versions of getID3() + (thanks ahØartemis*dk and adamØphysco*com for the idea) + » Added support for AIFF (Audio Interchange File Format) including + AIFF, AIFC and 8SVX (thanks ahØartemis*dk for the idea) + Removed file: getid3.aiff.php + » Added support for OptimFROG (v4.50a and v4.2x) + (thanks ahØartemis*dk for the idea) + New file: getid3.optimfrog.php + » Added support for WavPack (thanks ahØartemis*dk for the idea) + » Added support for LPAC (thanks ahØartemis*dk for the idea) + » Added support for NeXT/Sun .au format + New file: getid3.au.php + » Added support for Creative SoundBlaster VOC format + New file: getid3.voc.php + » Added support for the BWF (Broadcast Wave File) RIFF chunks + "bext" and "MEXT" (thanks Ryan and njhØsurgeradio*co*uk) + » Added support for the CART (Broadcast Wave File) RIFF chunks + (thanks Ryan) + » Added getid3.demo.scandir.php - a sample recursive scanning demo + that scans every file in a given directory, and all sub- + directories, and stores the resulting data in MySQL database, + and then displays a list of duplicate files based on md5_data + ¤ ['md5_data_source'] now contains the MD5 value for the original + uncompressed data for formats that store that information + (currently only FLAC v0.5+). ['md5_data'] (if chosen to be + calculated) will contain the calculated MD5 value for the + compressed file. To check if 2 files are identical in every way, + including all comments: compare ['md5_file']. To check if two + files were compressed from the same source file: compare + ['md5_data_source']. To check if the compressed audio/video data + of two files is identical, even if comments or even the + container file format is different (MP3 in RIFF container, + FLAC in Ogg container, etc): compare ['md5_data']. + ¤ ['md5_data'] for 8-bit WAV files is now calculated based on a + converted version of the data from unsigned to signed (MSB + inverted) to match the MD5 value calculated by FLAC + ¤ New optional parameter added to GetAllFileInfo() - + $MD5dataIfMD5SourceKnown (default: false). If false the md5_data + value will NOT be calculated for files (such as FLAC) that have + ['md5_data_source'] set, even if $MD5data == true. + (thanks ahØartemis*dk) + ¤ getid3.check.php renamed to getid3.demo.check.php + ¤ Added GetTagOnly() function to getid3.php - similar to + GetAllFileInfo() except only takes a filename as a parameter and + only returns ID3v2, APE, Lyrics3 and ID3v1 tag information - no + attempt is made to parse the data contents of the file at all. + (thanks Phil for the idea) + ¤ Added ['audio']['lossless'] and ['video']['lossless'] for all + formats (when known). Both are boolean values - true means the + data is lossless-compressed, false means the data is lossy- + compressed. + ¤ Added ['audio']['compression_ratio'] and/or + ['video']['compression_ratio'] for all formats. Returns a number + (usually) less than 1, where 1 represents no compression and 0.5 + represents a compressed file half the size of the original file + ¤ Added ['video']['bits_per_sample'] to all video formats (when + known) + ¤ Added ['video']['frame_rate'] to all video formats (when known) + ¤ ['fileformat'] set to 'mp1' or 'mp2' instead of 'mp3' when + ['audio']['dataformat'] is one of those (thanks ahØartemis*dk) + ¤ Added 4th parameter to md5_data(), $invertsign, which will invert + the MSB of each byte before MD5'ing. This is needed for 8-bit + WAV files because FLAC calculates the stored MD5 value on + signed data rather than the original byte values. ['md5_data'] + of an 8-bit WAV will now match the ['md5_data_source'] value + (thanks lichvarmØphoenix*inf*upol*cz) + ¤ ['ape']['items']['data'] and ['ape']['items']['data_ascii'] now + contains an array of values, if the tag contains UTF-8 text (as + opposed to binary data) + ¤ ['mpeg']['audio']['bitratemode'] renamed to + ['mpeg']['audio']['bitrate_mode'] + * Bugfix: Removed potential bug that could replace all MP3 file + contents with only the new ID3v2 tag in getid3.putid3.php + * Bugfix: md5_data values calculated for RIFF (WAV, AVI) files + were incorrect (thanks ahØartemis*dk) + * Bugfix: MP3 data in an MP4 wrapper fileformat could not identify + bitrate (thanks ahØartemis*dk) + * Bugfix: ['audio'] and/or ['video'] keys would sometimes get + removed even if not empty + * Bugfix: Prevented creation of null entries in + ['RIFF']['WAVE']['INFO'] if a comment entry was not present + * Bugfix: Potential infinite-loop condition in getid3.ogg.php + (thanks afshin.behniaØsbcglobal*net) + * Bugfix: Ogg files with illegal ID3v1 (and/or APE or Lyrics3) + tags were not finding the last Ogg page + (thanks afshin.behniaØsbcglobal*net) + * Bugfix: replay-gain values not properly set from LAME tag + * Bugfix: RIFF-MP3 had incorrect md5_data + * Bugfix: the LAME DLL CBR problem of not re-writing the LAME + frame at the beginning of the data is now detected for MP3s + with ID3v2 tags as well + * Bugfix: APE tags with multiple values (ie multiple entries in + the "artist" tag) are now shown properly in ['ape']['items'] + * Bugfix: fixed condition where APE tag with no ID3v1 tag could be + mistaken for APE tag with ID3v1 (and incorrectly parsed) + * Bugfix: added warning if ID3v2 frame has zero-length data + (thanks cmassetØclubinternet*fr) + * Bugfix: getid3.frames.php looking for non-existant key in USER + frames + Improved detection of RIFF-MP3 data. [unknown program] encodes + RIFF-WAV data with a chunk name of 'RMP3' instead of the + standard 'RIFF' + Encoder now returned in both ['comments'] and ['audio']['encoder'] + for RIFF-WAV files with an INFO.ISFT chunk + Generate a warning for FLAC files encoded with v0.3 or v0.4 + because audio_signature is not calculated during encoding + (thanks ahØartemis*dk) + Modified getid3.check.php to display md5_data_source as well as + md5_file and md5_data if display-MD5 mode is selected + Modified getid3.check.php to assume-format based on file extension + in browse mode if fileformat is found to be 'id3' (formerly only + if the fileformat was null) + Changed scaling of BitrateColor() from representing 1-256kbps to + representing 1-768kbps for better display of high-bitrate files, + specifically lossless-compressed CD-audio (FLAC, LA, etc) + + +1.6.0: [January-30-2003] James Heinrich + » Added support for OggFLAC (FLAC data stored in an Ogg container) + (thanks ahØartemis*dk for the idea) + » Added support for Speex (the data stored in an Ogg container) + » Comments are now available in the root 2-dimensional array + ['comments'] - each entry in this array will contain one or more + strings. For example, if there are two artists then + ['comments']['artist'][0] will contain the first one and + ['comments']['artist'][1] the other. All keys are forced + lowercase. Comments will be stored in the ['comments'] array in + this order of precedence: + 1) Native format tags (ASF, VQF, NSV, RIFF, Quicktime, Vorbis) + 2) APE tags + 3) ID3v2 + 4) Lyrics3 + 5) ID3v1 + Lower-priority tags will not overwrite or append existing values + of higher-priority tags (for example, 'artist' in ID3v1 will be + ignored if already specified in APE), but missing values will be + filled in (for example, if 'album' is specified in ID3v2 but not + in APE, it will be included in the ['comments'] array). + Note: Root keys (['title'], ['artist'], etc) are NOT available + in this or future versions of getID3(). + (thanks ahØartemis*dk) + » MD5 hashes are now available for all formats for both the entire + file (['md5_file']) and the portion of the file containing only + the audio/video data, stripped of all prepended/appended tags + like ID3v2, ID3v1, APE, etc - ['md5_data'] + (thanks ahØartemis*dk for alternate md5_file() function that + runs on UNIX system running PHP < 4.2.0) + NOTE: Ogg files require the use of vorbiscomment to obtain the + md5_data value. vorbiscomment must be downloaded from + http://www.vorbis.com/download.psp and placed in the getID3() + directory. All Ogg formats (Vorbis, OggFLAC, Speex) are affected + by this problem, but only OggVorbis files can be processed with + vorbiscomment. OggFLAC and Speex files will be processed by + getID3(), but this may result in an incorrect value for md5_data + in the event that VorbisComments are larger than 1 page (4-8kB). + NOTE: md5_data for Ogg will not work if PHP is running in Safe + Mode + » There is now a wrapper class available, written by Allan Hansen, + which should simplify extracting most common basic information + (such as format, bitrate, comments). + New file: audioinfo.class.php + » OggWrite() in getid3.ogginfo.php has been replaced with a new + version that uses vorbiscomment to write the comments, because + of a reported bug that can corrupt OggVorbis files such they + cannot be played. + NOTE: Ogg comment writing now requires the use of vorbiscomment + which must be downloaded from http://www.vorbis.com/download.psp + and placed in the getID3() directory. + NOTE: Ogg comment writing will not work if PHP is running in + Safe Mode + ¤ New root key ['tags'] is now always returned for all formats. + It is an array that may contain any of: + * Native format tags: 'vqf', 'riff', 'vorbiscomment', 'asf', + 'nsv', 'real', 'midi', 'zip', 'quicktime' + * Appended data tags: 'ape', 'lyrics3', 'id3v2', 'id3v1' + ¤ New root key ['audio'] is an array containing any or all of: + codec, channels, channelmode, bitrate, bits_per_sample, + dataformat, bitrate_mode, sample_rate, encoder + Note: This replaces several root keys, including: + bitrate_audio, bits_per_sample, frequency, channels + ¤ New root key ['video'] is an array containing any or all of: + bitrate_mode, bitrate, codec, resolution_x, resolution_y, + resolution_y, frame_rate, encoder + Note: This replaces several root keys, including: + bitrate_video, resolution_x, resolution_y, frame_rate + ¤ ['id3']['id3v1'] has moved to ['id3v1'] + ¤ ['id3']['id3v2'] has moved to ['id3v2'] + ¤ ['audiodataoffset'] and ['audiodataend'] have been renamed to + ['avdataoffset'] and ['avdataend'] respectively + ¤ GetAllMP3info() has been changed to GetAllFileInfo() with a + different parameter list ($allowedFormats is no longer a + parameter). Check your code where you're calling + GetAllMP3Info() - you will need to change both the function + name and the parameter list if you pass more than 2 parameters + ¤ All formats now return ['audio']['dataformat'] and/or + ['video']['dataformat'] where appropriate - this goes along with + ['fileformat'] - ['fileformat'] will return the actual structure + of the file, whereas ['dataformat'] will return the format of + the data inside that structure. For example, an Ogg file can + contain Vobis data (normal), or it can contain FLAC data in the + Ogg container format. In that case, ['fileformat'] would be + 'ogg', but ['dataformat'] would be 'flac'. + Note: this means that WAV and AVI files now return a + ['fileformat'] of 'riff' rather than 'wav' or 'avi'. + ¤ ['filesize'] is no longer returned for files larger than 2GB + because PHP does not support large file access. Attempting to + parse a file larger than 2GB will result in a message stored in + ['error'] and ['filesize'] not set. + ¤ APEtag, ID3v1, and ID3v2 are now supported on ALL multimedia + files - even if illegal by format. Ogg will return warning if + ID3/APE tags are present. (thanks ahØartemis*dk) + ¤ All files: non-critical errors are now returned in the root key + ['warning'] rather than ['error'] (only critical errors that + prevent getID3() from correctly parsing the file are returned in + ['error'] (thanks ahØartemis*dk) + ¤ Renamed all references to $MP3fileInfo to $ThisFileInfo + ¤ Joliet now supported for ISO-9660. + ['iso']['supplementary_volume_descriptor'] is now returned, if + available, and ['iso']['files'] will contain ASCII equivalents + of the Unicode directory structure & filenames stored. + ¤ Moved Monkey's Audio code from getid3.ape.php to seperate file. + New file: getid3.monkey.php + ¤ Added new keys for ISO-9660: ['name_ascii'] for directories, + ['file_identifier_ascii'] for files + ¤ Added root key ['track'] for CD-audio files + ¤ Ogg/Vorbis-comment files now have comments returned inside + ['ogg']['comments_common'] as an array of strings, rather than + simple strings in ['ogg'] + ¤ Quicktime files now have comments returned inside + ['quicktime']['comments'] as an array of strings, rather than + simple strings in ['quicktime'] + ¤ ['mime_type'] is a new root key returned for all supported + formats (thanks ahØartemis*dk) + ¤ ['fileformat'] now returns 'mp1' instead of 'mp3' for MPEG-1 + layer-I audio files (thanks ahØartemis*dk) + ¤ ['mpeg']['audio']['bitratemode'] now returns lowercase + ¤ MPEG-4 audio files which consist of MP3 data wrapped in a + Quicktime fileformat will now return the usual data in + ['mpeg']['audio'] + ¤ Type-1 DV AVIs are now supported + ¤ DV AVIs will return 1 or 2 in ['RIFF']['video'][x]['dv_type'] + ¤ Changed ['fileformat'] from 'mpg' to 'mpeg' for MPEG video files + ¤ ASF comments are now stored in ['asf']['comments'] instead of + ['asf'] + ¤ RealMedia chunk data is now returned inside ['real']['chunks'] + instead of ['real'] + ¤ ['replay_gain'] now properly populated from APE tags + ¤ Added support for ASF_Old_ASF_Index_Object in ASF files + (thanks ahØartemis*dk) + ¤ AAC-ADTS files now return ['aac']['bitrate_distribution'] + ¤ ParseVorbisComments() has been replaced with + ParseVorbisCommentsFilepointer() (with different parameters) + ¤ All references to any key ['frequency'] are now ['sample_rate'] + ¤ Moved ID3v2 comments from ['id3v2'] into common root + ['comments'] structure, and now returns more values than before + * Bugfix: ['iso']['files'] and ['zip']['files'] could potentially + contain duplicate entries (in a numeric-indexed array) for files + if the directory structure specifies files multiple times. + Entries are now guaranteed unique, with the last entry for the + file overwriting any former ones. + * Bugfix: RIFF parsing had numerous issues, including: + - large AVIs would take a very very long time to parse + - chunks with odd (not even) sizes would cause the parser fail + - video and/or audio codecs not always identified + The ParseRIFF() function has been completely rewritten and fixes + all known issues with RIFF parsing. Users are, however, + encouraged to double-check output of any parsed (AVI/WAV/CDDA) + files. + * Bugfix: Modified getid3.riff.php to return correct total + bitrates for AVIs with multiple audio streams + * Bugfix: GetFileFormat() was not creating array structure + correctly (thanks ahØartemis*dk) + * Bugfix: LAME tag for MP3s can only specify up to 255kbps, so any + files with actual CBR bitrate of >=256 were reported incorrectly + * Bugfix: Lyrics3 synched lyrics were not being correctly returned + * Bugfix: CreateDeepArray() was broken for non-nested cases, which + meant ZIP and ISO ['files'] structures were broken + * Bugfix: Incorrect pattern matching for ZIP files meant no zip + files were being detected as such + * Bugfix: AAC-ADIF was returning an incorrect number of channels + (too few) in some cases (thanks ahØartemis*dk) + * Bugfix: Vorbis comments were returning an incorrect value for + ['dataoffset'] in some cases + * Bugfix: MPEG video ['marker_bit'] and ['vbv_buffer_size'] were + incorrect + * Bugfix: ['playtime_string'] could potentially have a value of + x minutes and 60 seconds (ie 3:60 instead of 4:00) + Added support for FLAC cuesheets (FLAC 1.1.0+) + (thanks ahØartemis*dk) + Improved parsing speed in MP3, MP2 and AAC (thanks ahØartemis*dk) + Extra error-checking added to try and identify corrupt files for + most audio formats (thanks ahØartemis*dk) + More accurate playtime calculation for RealMedia + (thanks ahØartemis*dk) + Changed all relevant files to use ['audiodataoffset'] and + ['audiodataend'] rather than ['filesize'] where appropriate + (thanks ahØartemis*dk) + Added text encoding type 255 as a duplicate of UTF-16BE but with + Big-Endian rather than Little-Endian byte order + Added many RIFF-AVI audio types and fourcc video types to the + lookup functions in getid3.riff.php + Added numerous new known GUIDs to getid3.asf.php + Added PoweredBygetID3() function to easily get a "powered by" + string with the current getID3() version. + Added "Morgan Multimedia Motion JPEG2000" (MJ2C), "DivX v5" (DX50) + and "XviD" (XVID) codecs to list of known codecs in + getid3.riff.php + Changed GETID3_INCLUDEPATH path seperators to forced / + (from \ for Windows) + Modified getid3.check.php to only change \ directory seperators to + / on Windows operating systems + Modified getid3.check.php to handle larger-than-2GB files (which + now do not return a filesize) + Modified getid3.check.php to handle ['dataformat_audio'] and + ['dataformat_video'] + Modified getid3.check.php to show a list of present tags in one + column rather than one column for each of ID3v1, ID3v2, etc + Modified getid3.check.php to show MD5 values. Initially disabled + but can be enabled for a directory with a click. md5_file is + always calculated when displaying detailed info about a single + file; md5_data is calculated if the file is < 50MB + Modified getid3.check.php to show errors and warnings. Details are + visible with a mouseover or a click. + Changed getid3.check.php to use SafeStripSlashes instead of a + manual conditional directory name replacement for special + characters + Added sample recursive scanning sample code to getid3.readme.txt + (thanks lipisinØmail*ru for the idea) + + +1.5.7: [January-10-2003] James Heinrich + » Added support for ISO 9660 (CD-ROM image) format. Most-useful + data is directory structure returned under ['iso']['files'] + Note: Only ISO-9660 supported, not (yet) Joliet extension + (thanks nebula_djØsofthome*net for the idea) + New file: getid3.iso.php + ¤ ZIP files are now parsed by getID3() itself without relying on + built-in PHP functions and/or ZZipLib support. + (thanks Vince for the idea) + ¤ ZIP files now return a simple directory listing with filename + and filesize info only under ['zip']['files']. + Note: empty subdirectories will note appear in here, only files + and non-empty subdirectories. Information for all entries, + including empty subdirectories, is available under + ['zip']['central_directory'] (or under ['zip']['entries'] if the + Central Directory cannot be located (usually due to a trucated + file). + ¤ RIFF-WAV files with MP3 data (or MP3s with RIFF headers, if you + want to think of it that way) now have the MPEG audio portion + scanned and the usual data returned in ['mpeg']['audio'] if the + RIFF audio codec has wFormatTag of "85" (identified by getID3() + as "MPEG Layer 3") + (thanks ahØartemis*dk for the idea) + ¤ EXIF data (if present) is returned for JPEG files under + ['jpg']['exif'] (thanks nebula_djØsofthome*net) + ¤ ['filepath'] now returned for all files with the directory part + of the full filename. + ¤ ['filenamepath'] is now returned for all files (equivalent to + ['filepath'].'/'.['filename']) + * Bugfix: ['id3']['id3v2'][]['dataoffset'] was wrong + * Bugfix: MP3s tagged with iTunes have an invalid comment field + frame name ('COM ' - should be 'COMM') but the data is valid + otherwise; the frame is now renamed to 'COMM' and parsed + normally (with the error noted in ['error']) + (thanks kheller2Ømac*com for the sample file) + * Bugfix: Some ASF/WMA audio files were not being identified as + any format (thanks ahØartemis*dk) + * Bugfix: Warning now generated and ASCII format assumed for + invalid text encoding values in ID3v2 + * Bugfix: Changed ZIP detection pattern from 'PK' to 'PK\x04\x03' + * Bugfix: Ogg/FLAC files with large Vorbis comments were dying in + an infinite loop with lots of error messages due to missing $fd + parameter on ParseVorbisComments() (thanks ahØartemis*dk) + * Bugfix: ['data'] and ['image_mime'] were being returned for all + Ogg comments even if they were not images for versions of PHP + that have image_type_to_mime_type() built in (ie PHP 4.3.0+) + + +1.5.6: [December-31-2002] James Heinrich + » Added support for NSV (Nullsoft Streaming Video) + (www.nullsoft.com/nsv/) + (thanks demonØsoundplanet*com for the idea) + New file: getid3.nsv.php + » Added support for CD-audio track files (track01.cda etc) + ¤ Added standard ['frame_rate'] root value when known (AVI, NSV, + MPEG-video) + ¤ ASF files now report ['fileformat'] of: + 'wmv' when Windows Media Video codec v7/v8/v9 is used; + 'wma' when any 'Windows Media Audio' named audio codec is used + and no video stream is present; + 'asf' in all other cases (audio-only, video-only, or both) + ¤ Removed support for ZIP functions (will be rewritten to not + require ZZIPlib support in future versions) + ¤ Added function SafeStripSlashes() as a drop-in replacement for + stripslashes(), but that only strips slashes if magic_quotes_gpc + is set + ¤ Removed support for remote file scanning (HTTP / FTP) + ¤ Added ['aac']['frames'] (number of AAC frames in file) + ¤ Added ['mpeg']['audio']['frame_count'] when a bitrate histogram + is created + ¤ Average bitrate for VBR MP3/MP2 is calculated from actual counts + of frames of various bitrates (rather than relying on the header + values or filesize) when a bitrate histogram is created + ¤ RecursiveFrameScanning() split out into seperate function + (getid3.mp3.php) + ¤ Removed old function getMP3header() from getid3.mp3.php + ¤ Changed default MPEG_VALID_CHECK_FRAMES (number of mp3 frames + scanned to ensure a valid audio sequence has been located) from + 10 to 25. This means scanning will be slightly slower, but more + reliable/accurate + * Bugfix: ID3v2.2 - valid frame names not correctly detected + (thanks maeckerØweb*de for the sample file) + * Bugfix: ID3v2.2 - valid padding not correctly detected + (thanks maeckerØweb*de for the sample file) + * Bugfix: MIDI files with flat key signatures were not being + correctly reported (thanks alexleeisØshaw*ca for sample file) + * Bugfix: now returns message in ['error'] if file does not exist + * Bugfix: ['RIFF']['video'][x]['codec'] wasn't always being + correctly populated + * Bugfix: ['bitrate'] was incorrect for multi-stream RealMedia + * Bugfix: ['playtime_seconds'] was sometimes null or incorrect + for multi-stream RealMedia + * Bugfix: ChannelTypeID was incorrect in RVA2 ID3v2.4 frames + * Bugfix: Fixed potential divide-by-zero error for corrupt FLAC + files (thanks ahØartemis*dk) + * Bugfix: AAC-ADTS was not returning ['bitrate_mode'] unless + $ReturnExtendedInfo was TRUE (thanks ahØartemis*dk) + * Bugfix: LAME-encoded CBR MP3s now properly identified as CBR + with correct bitrate (thanks ahØartemis*dk) + * Bugfix: VBR MP2 (or headerless MP3) is now identified as VBR + rather than CBR. Note: to obtain VBR bitrate for headerless + files, the entire file is scanned and a histogram distribution + of bitrates is created, and the average bitrate calculated from + that. (thanks ahØartemis*dk for sample file) + Added support for DSIZ chunks in VQF, and checks to make sure size + of audio data matches DSIZ value, if present + (thanks ahØartemis*dk for sample file) + Rewrote GetAllMP3info() - removed some unneccesary code, changed + format-detection routine from ParseAsThisFormat() to + GetFileFormat() to allow for more flexible format parsing + (needed for ISO CD-ROM images, helpful for Quicktime and others) + Changed references in all files from string-cast indexes: ["$i"] + to non-cast indexes: [$i] where appropriate + Put a sans-serif 9pt style on all text in getid3.check.php + getAACADTSheaderFilepointer() now return TRUE if synch is lost + after the first frame has been successfully parsed (previously + it would return FALSE if synch was lost at any time, meaning the + file is most likely MP3, which was incorrect) + (thanks ahØartemis*dk for sample file) + Speed improvement code changes to getid3.mp3.php (up to 24% faster + in some cases) (thanks ahØartemis*dk for the code) + Changed all include_once() to require_once() + + +1.5.5: [November-25-2002] James Heinrich + » Added support for La (Lossless Audio - www.lossless-audio.com) + (thanks ahØartemis*dk for the idea) + New file: getid3.la.php + ¤ Moved lookup functions from getid3.lookup.php to the files where + they are used. + New file: getid3.id3.php + New file: getid3.rgad.php + Removed file: getid3.lookup.php + ¤ getID3v1Filepointer() returns FALSE if ID3v1 tag not found + ¤ Added new paramter "ReturnExtendedInfo" to the function + getAACADTSheaderFilepointer() in getid3.aac.php which now + defaults to FALSE - if TRUE then the data for every frame is + returned (containing aac_frame_length, adts_buffer_fullness and + num_raw_data_blocks, which aren't usually very useful). Speed + improvement with FALSE is about 35%. + ¤ Now returns fopen() errors in ['error'], for example if a remote + file is not accessible. + ¤ Changed default number of MP3 audio frames to scan to determine + if a valid stream has been found from 5 to 10, now also defined + as a constant at the top of getid3.mp3.php This will result in + slightly slower MP3 parsing, but greater reliability in + detecting false/invalid/corrupted VBR headers. + ¤ fopen() errors now displayed in getid3.putid3.php + (thanks miguel.dieckmannØhamburg*de) + ¤ Added 4th parameter to decodeMPEGaudioHeader() $ScanAsCBR which + will force an MP3 audio frame sequence to be force-scanned in + CBR mode. You should never need to call this directly, it's only + used internally to scan for MP3 files that have an illegal VBR + header with CBR data. (thanks fletchØpobox*com) + * Bugfix: ASF_Marker_Object in getid3.asf.php was always returning + an error in non-existant "reserved_1" and failing + * Bugfix: VBR bitrate calculations in getid3.mp3.php only occur if + ['mpeg']['audio']['VBR_frames'] is defined. + (thanks fletchØpobox*com) + * Bugfix: getid3.putid3.php no longer deletes original MP3 if + ID3v2 tag writing fails (thanks miguel*dieckmannØhamburg*de) + * Bugfix: incorrect order of if-statement error messages in + getid3.putid3.php (thanks miguel*dieckmannØhamburg*de) + getid3.asf.php now notes the error and continues parsing rather + than failing when it encounters an error parsing a chunk + Now actually scan 1000 frames for AAC ADTS as reported in the + v1.5.4 changelog, rather than 100. (thanks ahØartemis*dk) + Improved scanning speed in getAACADTSheaderFilepointer() by ~30% + (thanks ahØartemis*dk for the fix) + Added FileSizeNiceDisplay() function to getid3.functions.php for + formatting filesize output in kB, MB, GB, etc. + + +1.5.4: [October-07-2002] James Heinrich + » Added support for Quicktime. + New file: getid3.quicktime.php + » Added support for AAC files, both ADTS and ADIF header formats. + ADIF format is a pain because it's very similar to standard MP3 + header format, and it's hard to distinguish between the two. I + have tried to make the detection accurate, but I have a limited + number of AAC test files to play with so if you have an AAC file + that gets detected as MP3/MP2 (or vice-versa), please send me + the details via email at getid3Øsilisoftware*com + ADTS format is very slow to parse because to get the bitrate of + VBR files the whole file has to be stepped through frame by + frame (getID3() scans up to the first 1000 frames and assumes + that to be close enough). + Note: I would suggest commenting out support for AAC (see top of + GetAllMP3info() function in getid3.php) unless you need it. + (thanks jfaulØgmx*de for the idea and sample Delphi source code) + New file: getid3.aac.php + » Added bitrate distribution analysis option for MP3 VBR files. A + new boolean parameter for getOnlyMPEGaudioInfo() enabled this + feature which steps through the MP3 file frame by frame and + counts how many frames of each bitrate exist. This information + is returned in ['mpeg']['audio']['bitrate_distribution'] + Caution: this feature is very inefficient for large files and + takes a very long time and does lots of disk I/O. Use with care. + ¤ Changed layout of allowedFormats in GetAllMP3info() function in + getid3.php to allow easy removal of support for any of the + supported format. As stated above, I recommend commenting out + AAC unless needed. + ¤ Added ['flac']['compressed_audio_bytes'], + ['flac']['uncompressed_audio_bytes'], and + ['flac']['compression_ratio'] + ¤ Replaced FXPT2DOT30toFloat() function with FixedPoint2_30() + * Bugfix: getid3.mpc.php was slightly miscalculating the number of + samples, therefore also bitrate and playtime + (thanks ahØartemis*dk for the fix) + * Bugfix: MonkeyCompressionLevelNameLookup() didn't know about + 'insane' compression (thanks ahØartemis*dk for the fix) + * Bugfix: MonkeySamplesPerFrame() was incorrect for MAC v3.95+ + (thanks ahØartemis*dk for the fix) + * Bugfix: getid3.check.php wasn't processing the assumeFormat + directive when (register_globals == off) + * Bugfix: detecting of synch pattern for MP3 files with invalid + data at the beginning wasn't always correct, also meant possible + incorrect bitrate/duration/etc info for such corrupt files. + getid3.functions.php now includes a replacement utf8_decode() + function for those PHP installations that are not configured + with the --with-xml option. (thanks stephaneØtekartists*com) + + +1.5.3: [September-30-2002] James Heinrich + » Added support for VQF. (thanks mtØmansonthomas*com for the idea) + New file: getid3.vqf.php + » Added support for FLAC. Comments, if present, are returned under + ['ogg'] because they follow the Ogg Vorbis structure standard. + New file: getid3.flac.php + ¤ OS/2-format bitmaps are now correctly interpreted. The format of + the bitmap is now returned in ['bmp']['type_os'] and + ['bmp']['type_version']. OS/2 bitmaps can be v1 or v2, Windows + can be v1, v4 or v5 + + +1.5.2: [September-25-2002] James Heinrich + » Support for RealMedia (audio & video) added + Note: only tested on G2 and v5 audio and video files - if anyone + has older and/or newer sample files, please test it and/or send + me the sample files. + (thanks stephaneØtekartists*com for idea) + New file: getid3.real.php + » Support for BMP added. Palette and pixel data can optionally be + extracted as well - this is slow and generally unneccesary, but + the option is there if you need it. Also includes PlotBMP() + which will take the extracted pixel data and output it as a true + color PNG. This function requires GD v2.0+ + Note: Untested on 16-bit and 32-bit BMPs because I couldn't find + any sample files - if you know of a program that can create such + files, please email getid3Øsilisoftware*com + Note: Support for RGB (uncompressed), RLE8 and RLE4 is included + and tested. BITFIELDS support is also included for 16- & 32-bit + formats, but it's untested, so if anybody has any test files + please send them to getid3Øsilisoftware*com + Note: Support currently only for Windows-format BMPs, and trying + to parse an OS/2-format bitmap leads to unpredictable/invalid + results. + New file: getid3.bmp.php + » PNG now fully parsed, including all information chunks + New file: getid3.png.php + ¤ Support for GIF/JPG/PNG moved to seperate files and expanded, + including standard ['resolution_x'] and ['resolution_y'] as well + as more thorough parsing of header information + New file: getid3.gif.php + New file: getid3.jpg.php + table_var_dump() simplified and now outputs {-style character + entities for characters outside the normal alphanumeric range + CleanOggCommentName() changed to a regular expression + (thanks chris-getid3Øbolt*cx for rewriting the function) + + +1.5.1: [September-20-2002] James Heinrich + » Added support for MPEGplus/Musepack SV7. ['fileformat'] is 'SV7' + for version 7 files (versions 4, 5 ,6 and 8 are not supported + yet, but will be of ['fileformat'] SV4, SV5, SV6 and SV8) when + they are supported (thanks Christian Fritz for the idea) + New file: getid3.mpc.php + ¤ ['bitrate_audio'], ['bitrate_video'], ['bitrate_mode'], + ['channels'], ['resolution_x'], and ['resolution_y'] keys added + for all appropriate formats + ¤ Ogg files with a COVERART comment now save and display the + attached image the same way as is done with ID3v2 APICs + ¤ ['ogg']['comments'][n]['data'] and + ['ogg']['comments'][n]['dataoffset'] is now returned for all + comments. ['ogg']['comments'][n]['data'] is only useful if + the field is supposed to contain binary data. It is a + base64_decode()'d version of ['value']. + ['ogg']['comments'][n]['dataoffset'] is the byte offset in the + file at which the 'COMMENTNAME=value string' starts, not the + start of just 'value' + ¤ ['ogg']['comments'][n]['image_mime'] is now returned if + ['ogg']['comments'][n]['data'] contains valid image data. + ¤ More than 3 Ogg pages may now be read in, if the comment data + is longer than 1 page (usually about 4kB) + ¤ ['fileformat'] is now 'mp2' rather than 'mp3' if it's MPEG-1, + Layer-II audio + ¤ ASF bitrates now calculated even if stream_bitrate_properties + object not present + ¤ ['asf']['stream_properties_object'] is now a numeric-key array + with one entry for each stream - the key being the stream number + ¤ ['replay_gain'] is returned for all audio formats that support + it (MP3-LAME, ID3v2, Ogg) (thanks Christian Fritz for the idea) + ¤ ['mpeg']['audio']['LAME']['RGAD']['radio_replay_gain'] is now + ['mpeg']['audio']['LAME']['RGAD']['radio'] (same for audiophile) + ¤ ASF/WMA files now use WM/Track to get track number from if + WM/TrackNumber is not available (thanks stephaneØtekartists*com) + ¤ ASF/WMV files now returns ['year'] and ['asf']['year'] + ¤ ASV/WMV files now use ['content_description']['description'] for + the ['comment'] field (thanks stephaneØtekartists*com) + ¤ ['track'] is now always returned as an integer + * Bugfix: Ogg comments that are larger than one data page (usually + about 4kB) are now correctly parsed (thanks Christian Fritz) + * Bugfix: Ogg comment data is now UTF8-decoded + * Bugfix: Ogg comment writing now UTF8-encodes the data + * Bugfix: playtime for ASF files was off by (usually + between 3 and 12 seconds) + * Bugfix: ['asf']['stream_properties_objects']['flags'] data was + possibly incorrect + * Bugfix: ASF Padding Object was overwriting + Stream Bitrate Properties Object data (now returned correctly in + ['asf']['padding_object'] + * Bugfix: ASF Marker Object Reserved_2 field was incorrect + * Bugfix: ASF Bitrate Mutual Exclusion Object had incorrect stream + numbers + Warning displayed if incorrectly-formatted Ogg comment is present + (known to be an issue with CDex v1.40, but fixed by v1.50b7) + (thanks Christian Fritz) + Ogg comment writing now checks for valid comment names + Added bitrate column in getid3.check.php, and added some formatting + (font, colour) + Performance tweaks using bitwise math instead of binary string + operations + + +1.5.0: [September-18-2002] James Heinrich + » Ogg comment writing support added. getid3.write.php has been + updated to allow for writing comment tags to both MP3 and Ogg. + Big thanks to Chris Bolt for writing the + OggWrite() function and offering it for inclusion in getID3() + New file: getid3.ogginfo.php + » Support for Monkey's Audio and APE tag added. + (thanks Christian Fritz for the idea) + New file: getid3.ape.php + ['fileformat'] now returns 'mac' for Monkey's Audio files, or + 'ape' for files with an APE tag (Monkey's Audio or other format) + » getid3.thumbnail.php has been removed from the distribution and + the table_var_dump() function now outputs APICs as seperate + files in the same directory as the analyzed file. This should + make the image-displaying more reliable as well as reduce + complexity. The naming convention for the images is + filename.ext.[byte offset of APIC data].[jpg|gif|png] + If anybody still has any problems with corrupted images please + let me know at getid3Øsilisoftware*com + » Support for extended Xing/LAME tag + (see http://users.belgacom.net/gc247244/extra/tag.html) + Data is returned in ['mpeg']['audio']['LAME'] + ¤ ['ogg']['tracknumber'] has been renamed to ['ogg']['track'] and + ['track'] is now returned in the root of the array + ¤ ['ogg']['pageheader'][n]['flag'] has been renamed to + ['ogg']['pageheader'][n]['flags'] and the unprocessed flag byte + is available in ['ogg']['pageheader'][n]['flags_raw'] + ¤ ['frequency'] is now returned for WAVE files in the root of the + array (thanks danielØelectroteque*org) + ¤ ASF files now return codec, bitrate, resolution, etc information + under ['asf']['video_media'] or ['asf']['audio_media'] + * Bugfix: RVA2 and EQU2 writing in getid3.putid3.php were + incorrectly writing Volume Adjustment field + * Bugfix: EQU2 in getid3.frames.php was reading Volume Adjustment + as unsigned integer instead of signed integer + * Bugfix: handling of remote files over HTTP & FTP was broken + (thanks Vince) + * Bugfix: incorrect handling of some ASF packets + ASF/Windows Media format now more fully parsed, including Index + Objects + Added several new fourCC video codecs + + +1.4.3: [September-15-2002] James Heinrich + » Now parses ASF / WMV / WMA files + ¤ New file: getid3.asf.php + * Bugfix: RoughTranslateUnicodeToASCII() would return nothing + if didn't find a terminator it was expecting + Added FILETIMEtoUNIXtime() function (for converting 64-bit + Microsoft FILETIME timestamps, used in ASF files and elsewhere, + to UNIX Epoch timestamps) + Added GUIDtoBytestring() and BytestringToGUID() functions + + +1.4.2: [September-12-2002] James Heinrich + » getID3() now requires PHP v4.1.0 or higher because it now is + designed to work with register_globals = off and the new auto- + globals ($_GET, $_SERVER, etc). + * Bugfix: VBR MP3 files with Fraunhofer-style VBR header were not + being correctly detected in most cases + (thanks dkushnerØoddcast*com and mikeØftl*com for sample files) + * Bugfix: IsValidTextEncoding() was broken + * Bugfix: Add stripslashes($EditorFilename) to getid3.write.php + (writing was broken for files with ' or " in the filename) + (thanks mikeØftl*com and kthejoker) + * Bugfix: If there is garbage data between a valid VBR header + frame and a sequence of valid MPEG-audio frames the VBR data is + no longer discarded. (thanks to mikeØftl*com for sample + Fraunhofer-style VBR file produced with MusicMatch v7.2) + ¤ Changed variable system to work with (register_globals = off) + ¤ Moved relevant code into seperate PlaytimeString() function + ¤ Added nl2br() to table_var_dump() for cleaner output + ¤ Now returns the following keys from Fraunhofer-VBR files: + ['VBR_seek_offsets'], ['VBR_seek_offsets_stride'], + ['VBR_offsets_relative'] and ['VBR_offsets_absolute'] + ¤ Added ID3v1matchesID3v2() function and implemented in + getid3.check.php (thanks to "Guest" in the forums for the idea) + Changed amount of data read in getid3.getimagesize.php from 10kB + to entire file. (thanks mikeØftl*com) + Wrapped function_exists() checks around function definitions in + getid3.functions.php + Fixed a lot of E_WARNING and E_NOTICE situations, especially in + ID3-writing code (getid3.putid3.php, etc) + Added checks to make sure all needed data is available for writing + ID3v2 tags + + +1.4.1b5: [May-30-2002] James Heinrich + * Bugfix: Unsynchronise() was broken, now fixed + (thanks mikeØftl*com) + * Bugfix: GenerateID3v2Tag() now correctly uses non-synchsafe + integers for frame size descriptors in ID3v2.3 and ID3v2.2 + (thanks mikeØftl*com) + ¤ Added ['artist'], ['title'], etc keys to root of returned + array to provide a common place to access any returned info + from any file type. Currently gets info from ID3v1, ID3v2, + Ogg, and RIFF/WAVE. Possible returned keys are: + title, artist, album, year, genre, comment, track + ¤ Modified LookupGenre() function to search for either genre based + on numeric ID, or now reverse lookup as well + ¤ Added ['artist'], ['title'], etc keys to ['RIFF'] information + if info tags are present + Added functionality to attach a picture to the ID3v2 tag in + getid3.write.php + Sorted genres into alphabetical order (special 3 at end of list) + in getid3.write.php + Changed the comment-edit field in getid3.write.php to a multi-line + '; + + echo 'Picture
(ID3v2 only)
'; + echo ''; + echo ' '; + echo ''; + + } else { + + echo 'Error'.FixTextFields($Filename).' does not exist'; + + } + echo ''; + +} + +?> + + \ No newline at end of file diff --git a/includes/getid3/demos/index.php b/includes/getid3/demos/index.php new file mode 100644 index 0000000..13783f0 --- /dev/null +++ b/includes/getid3/demos/index.php @@ -0,0 +1 @@ +In this directory are a number of examples of how to use
getID3() - if you don't know what to run, take a look at demo.browse.php \ No newline at end of file diff --git a/includes/getid3/dependencies.txt b/includes/getid3/dependencies.txt new file mode 100644 index 0000000..34a72ba --- /dev/null +++ b/includes/getid3/dependencies.txt @@ -0,0 +1,24 @@ +///////////////////////////////////////////////////////////////// +/// getID3() by James Heinrich // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// // +// dependencies.txt - part of getID3() // +// See readme.txt for more details // +// /// +///////////////////////////////////////////////////////////////// + +lyrics3 depends on apetag (optional) +ogg depends on flac +id3v2 depends on id3v1 +apetag depends on id3v1 (optional, writing only) +bonk depends on id3v2 (optional) +riff depends on mp3 +mpeg depends on mp3 +quicktime depends on mp3 +flac depends on ogg +optimfrog depends on riff +la depends on riff +lpac depends on riff +asf depends on riff, id3v1 (optional) diff --git a/includes/getid3/getid3/extension.cache.dbm.php b/includes/getid3/getid3/extension.cache.dbm.php new file mode 100644 index 0000000..051bb1f --- /dev/null +++ b/includes/getid3/getid3/extension.cache.dbm.php @@ -0,0 +1,222 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// // +// extension.cache.dbm.php - part of getID3() // +// Please see readme.txt for more information // +// /// +///////////////////////////////////////////////////////////////// +// // +// This extension written by Allan Hansen // +// /// +///////////////////////////////////////////////////////////////// + + +/** +* This is a caching extension for getID3(). It works the exact same +* way as the getID3 class, but return cached information very fast +* +* Example: +* +* Normal getID3 usage (example): +* +* require_once 'getid3/getid3.php'; +* $getID3 = new getID3; +* $getID3->encoding = 'UTF-8'; +* $info1 = $getID3->analyze('file1.flac'); +* $info2 = $getID3->analyze('file2.wv'); +* +* getID3_cached usage: +* +* require_once 'getid3/getid3.php'; +* require_once 'getid3/getid3/extension.cache.dbm.php'; +* $getID3 = new getID3_cached('db3', '/tmp/getid3_cache.dbm', +* '/tmp/getid3_cache.lock'); +* $getID3->encoding = 'UTF-8'; +* $info1 = $getID3->analyze('file1.flac'); +* $info2 = $getID3->analyze('file2.wv'); +* +* +* Supported Cache Types +* +* SQL Databases: (use extension.cache.mysql) +* +* cache_type cache_options +* ------------------------------------------------------------------- +* mysql host, database, username, password +* +* +* DBM-Style Databases: (this extension) +* +* cache_type cache_options +* ------------------------------------------------------------------- +* gdbm dbm_filename, lock_filename +* ndbm dbm_filename, lock_filename +* db2 dbm_filename, lock_filename +* db3 dbm_filename, lock_filename +* db4 dbm_filename, lock_filename (PHP5 required) +* +* PHP must have write access to both dbm_filename and lock_filename. +* +* +* Recommended Cache Types +* +* Infrequent updates, many reads any DBM +* Frequent updates mysql +*/ + + +class getID3_cached_dbm extends getID3 +{ + + // public: constructor - see top of this file for cache type and cache_options + function getID3_cached_dbm($cache_type, $dbm_filename, $lock_filename) { + + // Check for dba extension + if (!extension_loaded('dba')) { + die('PHP is not compiled with dba support, required to use DBM style cache.'); + } + + // Check for specific dba driver + if (function_exists('dba_handlers')) { // PHP 4.3.0+ + if (!in_array('db3', dba_handlers())) { + die('PHP is not compiled --with '.$cache_type.' support, required to use DBM style cache.'); + } + } + else { // PHP <= 4.2.3 + ob_start(); // nasty, buy the only way to check... + phpinfo(); + $contents = ob_get_contents(); + ob_end_clean(); + if (!strstr($contents, $cache_type)) { + die('PHP is not compiled --with '.$cache_type.' support, required to use DBM style cache.'); + } + } + + // Create lock file if needed + if (!file_exists($lock_filename)) { + if (!touch($lock_filename)) { + die('failed to create lock file: ' . $lock_filename); + } + } + + // Open lock file for writing + if (!is_writeable($lock_filename)) { + die('lock file: ' . $lock_filename . ' is not writable'); + } + $this->lock = fopen($lock_filename, 'w'); + + // Acquire exclusive write lock to lock file + flock($this->lock, LOCK_EX); + + // Create dbm-file if needed + if (!file_exists($dbm_filename)) { + if (!touch($dbm_filename)) { + die('failed to create dbm file: ' . $dbm_filename); + } + } + + // Try to open dbm file for writing + $this->dba = @dba_open($dbm_filename, 'w', $cache_type); + if (!$this->dba) { + + // Failed - create new dbm file + $this->dba = dba_open($dbm_filename, 'n', $cache_type); + + if (!$this->dba) { + die('failed to create dbm file: ' . $dbm_filename); + } + + // Insert getID3 version number + dba_insert(GETID3_VERSION, GETID3_VERSION, $this->dba); + } + + // Init misc values + $this->cache_type = $cache_type; + $this->dbm_filename = $dbm_filename; + + // Register destructor + register_shutdown_function(array($this, '__destruct')); + + // Check version number and clear cache if changed + if (dba_fetch(GETID3_VERSION, $this->dba) != GETID3_VERSION) { + $this->clear_cache(); + } + + parent::getID3(); + } + + + + // public: destuctor + function __destruct() { + + // Close dbm file + @dba_close($this->dba); + + // Release exclusive lock + @flock($this->lock, LOCK_UN); + + // Close lock file + @fclose($this->lock); + } + + + + // public: clear cache + function clear_cache() { + + // Close dbm file + dba_close($this->dba); + + // Create new dbm file + $this->dba = dba_open($this->dbm_filename, 'n', $this->cache_type); + + if (!$this->dba) { + die('failed to clear cache/recreate dbm file: ' . $this->dbm_filename); + } + + // Insert getID3 version number + dba_insert(GETID3_VERSION, GETID3_VERSION, $this->dba); + + // Reregister shutdown function + register_shutdown_function(array($this, '__destruct')); + } + + + + // public: analyze file + function analyze($filename) { + + if (file_exists($filename)) { + + // Calc key filename::mod_time::size - should be unique + $key = $filename . '::' . filemtime($filename) . '::' . filesize($filename); + + // Loopup key + $result = dba_fetch($key, $this->dba); + + // Hit + if ($result !== false) { + return unserialize($result); + } + } + + // Miss + $result = parent::analyze($filename); + + // Save result + if (file_exists($filename)) { + dba_insert($key, serialize($result), $this->dba); + } + + return $result; + } + +} + + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/extension.cache.mysql.php b/includes/getid3/getid3/extension.cache.mysql.php new file mode 100644 index 0000000..40ea688 --- /dev/null +++ b/includes/getid3/getid3/extension.cache.mysql.php @@ -0,0 +1,171 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// // +// extension.cache.mysql.php - part of getID3() // +// Please see readme.txt for more information // +// /// +///////////////////////////////////////////////////////////////// +// // +// This extension written by Allan Hansen // +// /// +///////////////////////////////////////////////////////////////// + + +/** +* This is a caching extension for getID3(). It works the exact same +* way as the getID3 class, but return cached information very fast +* +* Example: (see also demo.cache.mysql.php in /demo/) +* +* Normal getID3 usage (example): +* +* require_once 'getid3/getid3.php'; +* $getID3 = new getID3; +* $getID3->encoding = 'UTF-8'; +* $info1 = $getID3->analyze('file1.flac'); +* $info2 = $getID3->analyze('file2.wv'); +* +* getID3_cached usage: +* +* require_once 'getid3/getid3.php'; +* require_once 'getid3/getid3/extension.cache.mysql.php'; +* $getID3 = new getID3_cached_mysql('localhost', 'database', +* 'username', 'password'); +* $getID3->encoding = 'UTF-8'; +* $info1 = $getID3->analyze('file1.flac'); +* $info2 = $getID3->analyze('file2.wv'); +* +* +* Supported Cache Types (this extension) +* +* SQL Databases: +* +* cache_type cache_options +* ------------------------------------------------------------------- +* mysql host, database, username, password +* +* +* DBM-Style Databases: (use extension.cache.dbm) +* +* cache_type cache_options +* ------------------------------------------------------------------- +* gdbm dbm_filename, lock_filename +* ndbm dbm_filename, lock_filename +* db2 dbm_filename, lock_filename +* db3 dbm_filename, lock_filename +* db4 dbm_filename, lock_filename (PHP5 required) +* +* PHP must have write access to both dbm_filename and lock_filename. +* +* +* Recommended Cache Types +* +* Infrequent updates, many reads any DBM +* Frequent updates mysql +*/ + + +class getID3_cached_mysql extends getID3 +{ + + // private vars + var $cursor; + var $connection; + + + // public: constructor - see top of this file for cache type and cache_options + function getID3_cached_mysql($host, $database, $username, $password) { + + // Check for mysql support + if (!function_exists('mysql_pconnect')) { + die('PHP not compiled with mysql support.'); + } + + // Connect to database + $this->connection = mysql_pconnect($host, $username, $password); + if (!$this->connection) { + die('mysql_pconnect() failed - check permissions and spelling.'); + } + + // Select database + if (!mysql_select_db($database, $this->connection)) { + die('Cannot use database '.$database); + } + + // Create cache table if not exists + $this->create_table(); + + // Check version number and clear cache if changed + $this->cursor = mysql_query("SELECT `value` FROM `getid3_cache` WHERE (`filename` = '".GETID3_VERSION."') AND (`filesize` = '-1') AND (`filetime` = '-1') AND (`analyzetime` = '-1')", $this->connection); + list($version) = @mysql_fetch_array($this->cursor); + if ($version != GETID3_VERSION) { + $this->clear_cache(); + } + + parent::getID3(); + } + + + + // public: clear cache + function clear_cache() { + + $this->cursor = mysql_query("DELETE FROM `getid3_cache`", $this->connection); + $this->cursor = mysql_query("INSERT INTO `getid3_cache` VALUES ('".GETID3_VERSION."', -1, -1, -1, '".GETID3_VERSION."')", $this->connection); + } + + + + // public: analyze file + function analyze($filename) { + + if (file_exists($filename)) { + + // Short-hands + $filetime = filemtime($filename); + $filesize = filesize($filename); + $filenam2 = mysql_escape_string($filename); + + // Loopup file + $this->cursor = mysql_query("SELECT `value` FROM `getid3_cache` WHERE (`filename`='".$filenam2."') AND (`filesize`='".$filesize."') AND (`filetime`='".$filetime."')", $this->connection); + list($result) = @mysql_fetch_array($this->cursor); + + // Hit + if ($result) { + return unserialize($result); + } + } + + // Miss + $result = parent::analyze($filename); + + // Save result + if (file_exists($filename)) { + $res2 = mysql_escape_string(serialize($result)); + $this->cursor = mysql_query("INSERT INTO `getid3_cache` (`filename`, `filesize`, `filetime`, `analyzetime`, `value`) VALUES ('".$filenam2."', '".$filesize."', '".$filetime."', '".time()."', '".$res2."')", $this->connection); + } + return $result; + } + + + + // private: (re)create sql table + function create_table($drop = false) { + + $this->cursor = mysql_query("CREATE TABLE IF NOT EXISTS `getid3_cache` ( + `filename` VARCHAR(255) NOT NULL DEFAULT '', + `filesize` INT(11) NOT NULL DEFAULT '0', + `filetime` INT(11) NOT NULL DEFAULT '0', + `analyzetime` INT(11) NOT NULL DEFAULT '0', + `value` TEXT NOT NULL, + PRIMARY KEY (`filename`,`filesize`,`filetime`)) TYPE=MyISAM", $this->connection); + echo mysql_error($this->connection); + } +} + + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/getid3.lib.php b/includes/getid3/getid3/getid3.lib.php new file mode 100644 index 0000000..40728d1 --- /dev/null +++ b/includes/getid3/getid3/getid3.lib.php @@ -0,0 +1,1323 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// // +// getid3.lib.php - part of getID3() // +// See readme.txt for more details // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_lib +{ + + function PrintHexBytes($string, $hex=true, $spaces=true, $htmlsafe=true) { + $returnstring = ''; + for ($i = 0; $i < strlen($string); $i++) { + if ($hex) { + $returnstring .= str_pad(dechex(ord($string{$i})), 2, '0', STR_PAD_LEFT); + } else { + $returnstring .= ' '.(ereg("[\x20-\x7E]", $string{$i}) ? $string{$i} : '¤'); + } + if ($spaces) { + $returnstring .= ' '; + } + } + if ($htmlsafe) { + $returnstring = htmlentities($returnstring); + } + return $returnstring; + } + + function SafeStripSlashes($text) { + if (get_magic_quotes_gpc()) { + return stripslashes($text); + } + return $text; + } + + + function trunc($floatnumber) { + // truncates a floating-point number at the decimal point + // returns int (if possible, otherwise float) + if ($floatnumber >= 1) { + $truncatednumber = floor($floatnumber); + } elseif ($floatnumber <= -1) { + $truncatednumber = ceil($floatnumber); + } else { + $truncatednumber = 0; + } + if ($truncatednumber <= 1073741824) { // 2^30 + $truncatednumber = (int) $truncatednumber; + } + return $truncatednumber; + } + + + function CastAsInt($floatnum) { + // convert to float if not already + $floatnum = (float) $floatnum; + + // convert a float to type int, only if possible + if (getid3_lib::trunc($floatnum) == $floatnum) { + // it's not floating point + if ($floatnum <= 1073741824) { // 2^30 + // it's within int range + $floatnum = (int) $floatnum; + } + } + return $floatnum; + } + + + function DecimalBinary2Float($binarynumerator) { + $numerator = getid3_lib::Bin2Dec($binarynumerator); + $denominator = getid3_lib::Bin2Dec('1'.str_repeat('0', strlen($binarynumerator))); + return ($numerator / $denominator); + } + + + function NormalizeBinaryPoint($binarypointnumber, $maxbits=52) { + // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html + if (strpos($binarypointnumber, '.') === false) { + $binarypointnumber = '0.'.$binarypointnumber; + } elseif ($binarypointnumber{0} == '.') { + $binarypointnumber = '0'.$binarypointnumber; + } + $exponent = 0; + while (($binarypointnumber{0} != '1') || (substr($binarypointnumber, 1, 1) != '.')) { + if (substr($binarypointnumber, 1, 1) == '.') { + $exponent--; + $binarypointnumber = substr($binarypointnumber, 2, 1).'.'.substr($binarypointnumber, 3); + } else { + $pointpos = strpos($binarypointnumber, '.'); + $exponent += ($pointpos - 1); + $binarypointnumber = str_replace('.', '', $binarypointnumber); + $binarypointnumber = $binarypointnumber{0}.'.'.substr($binarypointnumber, 1); + } + } + $binarypointnumber = str_pad(substr($binarypointnumber, 0, $maxbits + 2), $maxbits + 2, '0', STR_PAD_RIGHT); + return array('normalized'=>$binarypointnumber, 'exponent'=>(int) $exponent); + } + + + function Float2BinaryDecimal($floatvalue) { + // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html + $maxbits = 128; // to how many bits of precision should the calculations be taken? + $intpart = getid3_lib::trunc($floatvalue); + $floatpart = abs($floatvalue - $intpart); + $pointbitstring = ''; + while (($floatpart != 0) && (strlen($pointbitstring) < $maxbits)) { + $floatpart *= 2; + $pointbitstring .= (string) getid3_lib::trunc($floatpart); + $floatpart -= getid3_lib::trunc($floatpart); + } + $binarypointnumber = decbin($intpart).'.'.$pointbitstring; + return $binarypointnumber; + } + + + function Float2String($floatvalue, $bits) { + // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee-expl.html + switch ($bits) { + case 32: + $exponentbits = 8; + $fractionbits = 23; + break; + + case 64: + $exponentbits = 11; + $fractionbits = 52; + break; + + default: + return false; + break; + } + if ($floatvalue >= 0) { + $signbit = '0'; + } else { + $signbit = '1'; + } + $normalizedbinary = getid3_lib::NormalizeBinaryPoint(getid3_lib::Float2BinaryDecimal($floatvalue), $fractionbits); + $biasedexponent = pow(2, $exponentbits - 1) - 1 + $normalizedbinary['exponent']; // (127 or 1023) +/- exponent + $exponentbitstring = str_pad(decbin($biasedexponent), $exponentbits, '0', STR_PAD_LEFT); + $fractionbitstring = str_pad(substr($normalizedbinary['normalized'], 2), $fractionbits, '0', STR_PAD_RIGHT); + + return getid3_lib::BigEndian2String(getid3_lib::Bin2Dec($signbit.$exponentbitstring.$fractionbitstring), $bits % 8, false); + } + + + function LittleEndian2Float($byteword) { + return getid3_lib::BigEndian2Float(strrev($byteword)); + } + + + function BigEndian2Float($byteword) { + // ANSI/IEEE Standard 754-1985, Standard for Binary Floating Point Arithmetic + // http://www.psc.edu/general/software/packages/ieee/ieee.html + // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee.html + + $bitword = getid3_lib::BigEndian2Bin($byteword); + $signbit = $bitword{0}; + + switch (strlen($byteword) * 8) { + case 32: + $exponentbits = 8; + $fractionbits = 23; + break; + + case 64: + $exponentbits = 11; + $fractionbits = 52; + break; + + case 80: + // 80-bit Apple SANE format + // http://www.mactech.com/articles/mactech/Vol.06/06.01/SANENormalized/ + $exponentstring = substr($bitword, 1, 15); + $isnormalized = intval($bitword{16}); + $fractionstring = substr($bitword, 17, 63); + $exponent = pow(2, getid3_lib::Bin2Dec($exponentstring) - 16383); + $fraction = $isnormalized + getid3_lib::DecimalBinary2Float($fractionstring); + $floatvalue = $exponent * $fraction; + if ($signbit == '1') { + $floatvalue *= -1; + } + return $floatvalue; + break; + + default: + return false; + break; + } + $exponentstring = substr($bitword, 1, $exponentbits); + $fractionstring = substr($bitword, $exponentbits + 1, $fractionbits); + $exponent = getid3_lib::Bin2Dec($exponentstring); + $fraction = getid3_lib::Bin2Dec($fractionstring); + + if (($exponent == (pow(2, $exponentbits) - 1)) && ($fraction != 0)) { + // Not a Number + $floatvalue = false; + } elseif (($exponent == (pow(2, $exponentbits) - 1)) && ($fraction == 0)) { + if ($signbit == '1') { + $floatvalue = '-infinity'; + } else { + $floatvalue = '+infinity'; + } + } elseif (($exponent == 0) && ($fraction == 0)) { + if ($signbit == '1') { + $floatvalue = -0; + } else { + $floatvalue = 0; + } + $floatvalue = ($signbit ? 0 : -0); + } elseif (($exponent == 0) && ($fraction != 0)) { + // These are 'unnormalized' values + $floatvalue = pow(2, (-1 * (pow(2, $exponentbits - 1) - 2))) * getid3_lib::DecimalBinary2Float($fractionstring); + if ($signbit == '1') { + $floatvalue *= -1; + } + } elseif ($exponent != 0) { + $floatvalue = pow(2, ($exponent - (pow(2, $exponentbits - 1) - 1))) * (1 + getid3_lib::DecimalBinary2Float($fractionstring)); + if ($signbit == '1') { + $floatvalue *= -1; + } + } + return (float) $floatvalue; + } + + + function BigEndian2Int($byteword, $synchsafe=false, $signed=false) { + $intvalue = 0; + $bytewordlen = strlen($byteword); + for ($i = 0; $i < $bytewordlen; $i++) { + if ($synchsafe) { // disregard MSB, effectively 7-bit bytes + $intvalue = $intvalue | (ord($byteword{$i}) & 0x7F) << (($bytewordlen - 1 - $i) * 7); + } else { + $intvalue += ord($byteword{$i}) * pow(256, ($bytewordlen - 1 - $i)); + } + } + if ($signed && !$synchsafe) { + // synchsafe ints are not allowed to be signed + switch ($bytewordlen) { + case 1: + case 2: + case 3: + case 4: + $signmaskbit = 0x80 << (8 * ($bytewordlen - 1)); + if ($intvalue & $signmaskbit) { + $intvalue = 0 - ($intvalue & ($signmaskbit - 1)); + } + break; + + default: + die('ERROR: Cannot have signed integers larger than 32-bits in getid3_lib::BigEndian2Int()'); + break; + } + } + return getid3_lib::CastAsInt($intvalue); + } + + + function LittleEndian2Int($byteword, $signed=false) { + return getid3_lib::BigEndian2Int(strrev($byteword), false, $signed); + } + + + function BigEndian2Bin($byteword) { + $binvalue = ''; + $bytewordlen = strlen($byteword); + for ($i = 0; $i < $bytewordlen; $i++) { + $binvalue .= str_pad(decbin(ord($byteword{$i})), 8, '0', STR_PAD_LEFT); + } + return $binvalue; + } + + + function BigEndian2String($number, $minbytes=1, $synchsafe=false, $signed=false) { + if ($number < 0) { + return false; + } + $maskbyte = (($synchsafe || $signed) ? 0x7F : 0xFF); + $intstring = ''; + if ($signed) { + if ($minbytes > 4) { + die('ERROR: Cannot have signed integers larger than 32-bits in getid3_lib::BigEndian2String()'); + } + $number = $number & (0x80 << (8 * ($minbytes - 1))); + } + while ($number != 0) { + $quotient = ($number / ($maskbyte + 1)); + $intstring = chr(ceil(($quotient - floor($quotient)) * $maskbyte)).$intstring; + $number = floor($quotient); + } + return str_pad($intstring, $minbytes, "\x00", STR_PAD_LEFT); + } + + + function Dec2Bin($number) { + while ($number >= 256) { + $bytes[] = (($number / 256) - (floor($number / 256))) * 256; + $number = floor($number / 256); + } + $bytes[] = $number; + $binstring = ''; + for ($i = 0; $i < count($bytes); $i++) { + $binstring = (($i == count($bytes) - 1) ? decbin($bytes[$i]) : str_pad(decbin($bytes[$i]), 8, '0', STR_PAD_LEFT)).$binstring; + } + return $binstring; + } + + + function Bin2Dec($binstring, $signed=false) { + $signmult = 1; + if ($signed) { + if ($binstring{0} == '1') { + $signmult = -1; + } + $binstring = substr($binstring, 1); + } + $decvalue = 0; + for ($i = 0; $i < strlen($binstring); $i++) { + $decvalue += ((int) substr($binstring, strlen($binstring) - $i - 1, 1)) * pow(2, $i); + } + return getid3_lib::CastAsInt($decvalue * $signmult); + } + + + function Bin2String($binstring) { + // return 'hi' for input of '0110100001101001' + $string = ''; + $binstringreversed = strrev($binstring); + for ($i = 0; $i < strlen($binstringreversed); $i += 8) { + $string = chr(getid3_lib::Bin2Dec(strrev(substr($binstringreversed, $i, 8)))).$string; + } + return $string; + } + + + function LittleEndian2String($number, $minbytes=1, $synchsafe=false) { + $intstring = ''; + while ($number > 0) { + if ($synchsafe) { + $intstring = $intstring.chr($number & 127); + $number >>= 7; + } else { + $intstring = $intstring.chr($number & 255); + $number >>= 8; + } + } + return str_pad($intstring, $minbytes, "\x00", STR_PAD_RIGHT); + } + + + function array_merge_clobber($array1, $array2) { + // written by kcØhireability*com + // taken from http://www.php.net/manual/en/function.array-merge-recursive.php + if (!is_array($array1) || !is_array($array2)) { + return false; + } + $newarray = $array1; + foreach ($array2 as $key => $val) { + if (is_array($val) && isset($newarray[$key]) && is_array($newarray[$key])) { + $newarray[$key] = getid3_lib::array_merge_clobber($newarray[$key], $val); + } else { + $newarray[$key] = $val; + } + } + return $newarray; + } + + + function array_merge_noclobber($array1, $array2) { + if (!is_array($array1) || !is_array($array2)) { + return false; + } + $newarray = $array1; + foreach ($array2 as $key => $val) { + if (is_array($val) && isset($newarray[$key]) && is_array($newarray[$key])) { + $newarray[$key] = getid3_lib::array_merge_noclobber($newarray[$key], $val); + } elseif (!isset($newarray[$key])) { + $newarray[$key] = $val; + } + } + return $newarray; + } + + + function fileextension($filename, $numextensions=1) { + if (strstr($filename, '.')) { + $reversedfilename = strrev($filename); + $offset = 0; + for ($i = 0; $i < $numextensions; $i++) { + $offset = strpos($reversedfilename, '.', $offset + 1); + if ($offset === false) { + return ''; + } + } + return strrev(substr($reversedfilename, 0, $offset)); + } + return ''; + } + + + function PlaytimeString($playtimeseconds) { + $contentseconds = round((($playtimeseconds / 60) - floor($playtimeseconds / 60)) * 60); + $contentminutes = floor($playtimeseconds / 60); + if ($contentseconds >= 60) { + $contentseconds -= 60; + $contentminutes++; + } + return intval($contentminutes).':'.str_pad($contentseconds, 2, 0, STR_PAD_LEFT); + } + + + function image_type_to_mime_type($imagetypeid) { + // only available in PHP v4.3.0+ + static $image_type_to_mime_type = array(); + if (empty($image_type_to_mime_type)) { + $image_type_to_mime_type[1] = 'image/gif'; // GIF + $image_type_to_mime_type[2] = 'image/jpeg'; // JPEG + $image_type_to_mime_type[3] = 'image/png'; // PNG + $image_type_to_mime_type[4] = 'application/x-shockwave-flash'; // Flash + $image_type_to_mime_type[5] = 'image/psd'; // PSD + $image_type_to_mime_type[6] = 'image/bmp'; // BMP + $image_type_to_mime_type[7] = 'image/tiff'; // TIFF: little-endian (Intel) + $image_type_to_mime_type[8] = 'image/tiff'; // TIFF: big-endian (Motorola) + //$image_type_to_mime_type[9] = 'image/jpc'; // JPC + //$image_type_to_mime_type[10] = 'image/jp2'; // JPC + //$image_type_to_mime_type[11] = 'image/jpx'; // JPC + //$image_type_to_mime_type[12] = 'image/jb2'; // JPC + $image_type_to_mime_type[13] = 'application/x-shockwave-flash'; // Shockwave + $image_type_to_mime_type[14] = 'image/iff'; // IFF + } + return (isset($image_type_to_mime_type[$imagetypeid]) ? $image_type_to_mime_type[$imagetypeid] : 'application/octet-stream'); + } + + + function DateMac2Unix($macdate) { + // Macintosh timestamp: seconds since 00:00h January 1, 1904 + // UNIX timestamp: seconds since 00:00h January 1, 1970 + return getid3_lib::CastAsInt($macdate - 2082844800); + } + + + function FixedPoint8_8($rawdata) { + return getid3_lib::BigEndian2Int(substr($rawdata, 0, 1)) + (float) (getid3_lib::BigEndian2Int(substr($rawdata, 1, 1)) / pow(2, 8)); + } + + + function FixedPoint16_16($rawdata) { + return getid3_lib::BigEndian2Int(substr($rawdata, 0, 2)) + (float) (getid3_lib::BigEndian2Int(substr($rawdata, 2, 2)) / pow(2, 16)); + } + + + function FixedPoint2_30($rawdata) { + $binarystring = getid3_lib::BigEndian2Bin($rawdata); + return getid3_lib::Bin2Dec(substr($binarystring, 0, 2)) + (float) (getid3_lib::Bin2Dec(substr($binarystring, 2, 30)) / 1073741824); + } + + + function CreateDeepArray($ArrayPath, $Separator, $Value) { + // assigns $Value to a nested array path: + // $foo = getid3_lib::CreateDeepArray('/path/to/my', '/', 'file.txt') + // is the same as: + // $foo = array('path'=>array('to'=>'array('my'=>array('file.txt')))); + // or + // $foo['path']['to']['my'] = 'file.txt'; + while ($ArrayPath && ($ArrayPath{0} == $Separator)) { + $ArrayPath = substr($ArrayPath, 1); + } + if (($pos = strpos($ArrayPath, $Separator)) !== false) { + $ReturnedArray[substr($ArrayPath, 0, $pos)] = getid3_lib::CreateDeepArray(substr($ArrayPath, $pos + 1), $Separator, $Value); + } else { + $ReturnedArray[$ArrayPath] = $Value; + } + return $ReturnedArray; + } + + function array_max($arraydata, $returnkey=false) { + $maxvalue = false; + $maxkey = false; + foreach ($arraydata as $key => $value) { + if (!is_array($value)) { + if ($value > $maxvalue) { + $maxvalue = $value; + $maxkey = $key; + } + } + } + return ($returnkey ? $maxkey : $maxvalue); + } + + function array_min($arraydata, $returnkey=false) { + $minvalue = false; + $minkey = false; + foreach ($arraydata as $key => $value) { + if (!is_array($value)) { + if ($value > $minvalue) { + $minvalue = $value; + $minkey = $key; + } + } + } + return ($returnkey ? $minkey : $minvalue); + } + + + function md5_file($file) { + + // md5_file() exists in PHP 4.2.0+. + if (function_exists('md5_file')) { + return md5_file($file); + } + + if (GETID3_OS_ISWINDOWS) { + + $RequiredFiles = array('cygwin1.dll', 'md5sum.exe'); + foreach ($RequiredFiles as $required_file) { + if (!is_readable(GETID3_HELPERAPPSDIR.$required_file)) { + die(implode(' and ', $RequiredFiles).' are required in '.GETID3_HELPERAPPSDIR.' for getid3_lib::md5_file() to function under Windows in PHP < v4.2.0'); + } + } + $commandline = GETID3_HELPERAPPSDIR.'md5sum.exe "'.str_replace('/', DIRECTORY_SEPARATOR, $file).'"'; + if (ereg("^[\\]?([0-9a-f]{32})", strtolower(`$commandline`), $r)) { + return $r[1]; + } + + } else { + + // The following works under UNIX only + $file = str_replace('`', '\\`', $file); + if (ereg("^([0-9a-f]{32})[ \t\n\r]", `md5sum "$file"`, $r)) { + return $r[1]; + } + + } + return false; + } + + + function sha1_file($file) { + + // sha1_file() exists in PHP 4.3.0+. + if (function_exists('sha1_file')) { + return sha1_file($file); + } + + $file = str_replace('`', '\\`', $file); + + if (GETID3_OS_ISWINDOWS) { + + $RequiredFiles = array('cygwin1.dll', 'sha1sum.exe'); + foreach ($RequiredFiles as $required_file) { + if (!is_readable(GETID3_HELPERAPPSDIR.$required_file)) { + die(implode(' and ', $RequiredFiles).' are required in '.GETID3_HELPERAPPSDIR.' for getid3_lib::sha1_file() to function under Windows in PHP < v4.3.0'); + } + } + $commandline = GETID3_HELPERAPPSDIR.'sha1sum.exe "'.str_replace('/', DIRECTORY_SEPARATOR, $file).'"'; + if (ereg("^sha1=([0-9a-f]{40})", strtolower(`$commandline`), $r)) { + return $r[1]; + } + + } else { + + $commandline = 'sha1sum '.escapeshellarg($file).''; + if (ereg("^([0-9a-f]{40})[ \t\n\r]", strtolower(`$commandline`), $r)) { + return $r[1]; + } + + } + + return false; + } + + + // Allan Hansen + // getid3_lib::md5_data() - returns md5sum for a file from startuing position to absolute end position + function hash_data($file, $offset, $end, $algorithm) { + + switch ($algorithm) { + case 'md5': + $hash_function = 'md5_file'; + $unix_call = 'md5sum'; + $windows_call = 'md5sum.exe'; + $hash_length = 32; + break; + + case 'sha1': + $hash_function = 'sha1_file'; + $unix_call = 'sha1sum'; + $windows_call = 'sha1sum.exe'; + $hash_length = 40; + break; + + default: + die('Invalid algorithm ('.$algorithm.') in getid3_lib::hash_data()'); + break; + } + $size = $end - $offset; + while (true) { + if (GETID3_OS_ISWINDOWS) { + + // It seems that sha1sum.exe for Windows only works on physical files, does not accept piped data + // Fall back to create-temp-file method: + if ($algorithm == 'sha1') { + break; + } + + $RequiredFiles = array('cygwin1.dll', 'head.exe', 'tail.exe', $windows_call); + foreach ($RequiredFiles as $required_file) { + if (!is_readable(GETID3_HELPERAPPSDIR.$required_file)) { + // helper apps not available - fall back to old method + break; + } + } + $commandline = GETID3_HELPERAPPSDIR.'head.exe -c '.$end.' "'.escapeshellarg(str_replace('/', DIRECTORY_SEPARATOR, $file)).'" | '; + $commandline .= GETID3_HELPERAPPSDIR.'tail.exe -c '.$size.' | '; + $commandline .= GETID3_HELPERAPPSDIR.$windows_call; + + } else { + + $commandline = 'head -c'.$end.' '.escapeshellarg($file).' | '; + $commandline .= 'tail -c'.$size.' | '; + $commandline .= $unix_call; + + } + if ((bool) ini_get('safe_mode')) { + $ThisFileInfo['warning'][] = 'PHP running in Safe Mode - backtick operator not available, using slower non-system-call '.$algorithm.' algorithm'; + break; + } + return substr(`$commandline`, 0, $hash_length); + } + + // try to create a temporary file in the system temp directory - invalid dirname should force to system temp dir + if (($data_filename = tempnam('*', 'getID3')) === false) { + // can't find anywhere to create a temp file, just die + return false; + } + + // Init + $result = false; + + // copy parts of file + if ($fp = @fopen($file, 'rb')) { + + if ($fp_data = @fopen($data_filename, 'wb')) { + + fseek($fp, $offset, SEEK_SET); + $byteslefttowrite = $end - $offset; + while (($byteslefttowrite > 0) && ($buffer = fread($fp, GETID3_FREAD_BUFFER_SIZE))) { + $byteswritten = fwrite($fp_data, $buffer, $byteslefttowrite); + $byteslefttowrite -= $byteswritten; + } + fclose($fp_data); + $result = getid3_lib::$hash_function($data_filename); + + } + fclose($fp); + } + unlink($data_filename); + return $result; + } + + + function iconv_fallback_int_utf8($charval) { + if ($charval < 128) { + // 0bbbbbbb + $newcharstring = chr($charval); + } elseif ($charval < 2048) { + // 110bbbbb 10bbbbbb + $newcharstring = chr(($charval >> 6) | 0xC0); + $newcharstring .= chr(($charval & 0x3F) | 0x80); + } elseif ($charval < 65536) { + // 1110bbbb 10bbbbbb 10bbbbbb + $newcharstring = chr(($charval >> 12) | 0xE0); + $newcharstring .= chr(($charval >> 6) | 0xC0); + $newcharstring .= chr(($charval & 0x3F) | 0x80); + } else { + // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb + $newcharstring = chr(($charval >> 18) | 0xF0); + $newcharstring .= chr(($charval >> 12) | 0xC0); + $newcharstring .= chr(($charval >> 6) | 0xC0); + $newcharstring .= chr(($charval & 0x3F) | 0x80); + } + return $newcharstring; + } + + // ISO-8859-1 => UTF-8 + function iconv_fallback_iso88591_utf8($string, $bom=false) { + if (function_exists('utf8_encode')) { + return utf8_encode($string); + } + // utf8_encode() unavailable, use getID3()'s iconv_fallback() conversions (possibly PHP is compiled without XML support) + $newcharstring = ''; + if ($bom) { + $newcharstring .= "\xEF\xBB\xBF"; + } + for ($i = 0; $i < strlen($string); $i++) { + $charval = ord($string{$i}); + $newcharstring .= getid3_lib::iconv_fallback_int_utf8($charval); + } + return $newcharstring; + } + + // ISO-8859-1 => UTF-16BE + function iconv_fallback_iso88591_utf16be($string, $bom=false) { + $newcharstring = ''; + if ($bom) { + $newcharstring .= "\xFE\xFF"; + } + for ($i = 0; $i < strlen($string); $i++) { + $newcharstring .= "\x00".$string{$i}; + } + return $newcharstring; + } + + // ISO-8859-1 => UTF-16LE + function iconv_fallback_iso88591_utf16le($string, $bom=false) { + $newcharstring = ''; + if ($bom) { + $newcharstring .= "\xFF\xFE"; + } + for ($i = 0; $i < strlen($string); $i++) { + $newcharstring .= $string{$i}."\x00"; + } + return $newcharstring; + } + + // ISO-8859-1 => UTF-16LE (BOM) + function iconv_fallback_iso88591_utf16($string) { + return getid3_lib::iconv_fallback_iso88591_utf16le($string, true); + } + + // UTF-8 => ISO-8859-1 + function iconv_fallback_utf8_iso88591($string) { + if (function_exists('utf8_decode')) { + return utf8_decode($string); + } + // utf8_decode() unavailable, use getID3()'s iconv_fallback() conversions (possibly PHP is compiled without XML support) + $newcharstring = ''; + $offset = 0; + $stringlength = strlen($string); + while ($offset < $stringlength) { + if ((ord($string{$offset}) | 0x07) == 0xF7) { + // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb + $charval = ((ord($string{($offset + 0)}) & 0x07) << 18) & + ((ord($string{($offset + 1)}) & 0x3F) << 12) & + ((ord($string{($offset + 2)}) & 0x3F) << 6) & + (ord($string{($offset + 3)}) & 0x3F); + $offset += 4; + } elseif ((ord($string{$offset}) | 0x0F) == 0xEF) { + // 1110bbbb 10bbbbbb 10bbbbbb + $charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) & + ((ord($string{($offset + 1)}) & 0x3F) << 6) & + (ord($string{($offset + 2)}) & 0x3F); + $offset += 3; + } elseif ((ord($string{$offset}) | 0x1F) == 0xDF) { + // 110bbbbb 10bbbbbb + $charval = ((ord($string{($offset + 0)}) & 0x1F) << 6) & + (ord($string{($offset + 1)}) & 0x3F); + $offset += 2; + } elseif ((ord($string{$offset}) | 0x7F) == 0x7F) { + // 0bbbbbbb + $charval = ord($string{$offset}); + $offset += 1; + } else { + // error? throw some kind of warning here? + $charval = false; + $offset += 1; + } + if ($charval !== false) { + $newcharstring .= (($charval < 256) ? chr($charval) : '?'); + } + } + return $newcharstring; + } + + // UTF-8 => UTF-16BE + function iconv_fallback_utf8_utf16be($string, $bom=false) { + $newcharstring = ''; + if ($bom) { + $newcharstring .= "\xFE\xFF"; + } + $offset = 0; + $stringlength = strlen($string); + while ($offset < $stringlength) { + if ((ord($string{$offset}) | 0x07) == 0xF7) { + // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb + $charval = ((ord($string{($offset + 0)}) & 0x07) << 18) & + ((ord($string{($offset + 1)}) & 0x3F) << 12) & + ((ord($string{($offset + 2)}) & 0x3F) << 6) & + (ord($string{($offset + 3)}) & 0x3F); + $offset += 4; + } elseif ((ord($string{$offset}) | 0x0F) == 0xEF) { + // 1110bbbb 10bbbbbb 10bbbbbb + $charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) & + ((ord($string{($offset + 1)}) & 0x3F) << 6) & + (ord($string{($offset + 2)}) & 0x3F); + $offset += 3; + } elseif ((ord($string{$offset}) | 0x1F) == 0xDF) { + // 110bbbbb 10bbbbbb + $charval = ((ord($string{($offset + 0)}) & 0x1F) << 6) & + (ord($string{($offset + 1)}) & 0x3F); + $offset += 2; + } elseif ((ord($string{$offset}) | 0x7F) == 0x7F) { + // 0bbbbbbb + $charval = ord($string{$offset}); + $offset += 1; + } else { + // error? throw some kind of warning here? + $charval = false; + $offset += 1; + } + if ($charval !== false) { + $newcharstring .= (($charval < 65536) ? getid3_lib::BigEndian2String($charval, 2) : "\x00".'?'); + } + } + return $newcharstring; + } + + // UTF-8 => UTF-16LE + function iconv_fallback_utf8_utf16le($string, $bom=false) { + $newcharstring = ''; + if ($bom) { + $newcharstring .= "\xFF\xFE"; + } + $offset = 0; + $stringlength = strlen($string); + while ($offset < $stringlength) { + if ((ord($string{$offset}) | 0x07) == 0xF7) { + // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb + $charval = ((ord($string{($offset + 0)}) & 0x07) << 18) & + ((ord($string{($offset + 1)}) & 0x3F) << 12) & + ((ord($string{($offset + 2)}) & 0x3F) << 6) & + (ord($string{($offset + 3)}) & 0x3F); + $offset += 4; + } elseif ((ord($string{$offset}) | 0x0F) == 0xEF) { + // 1110bbbb 10bbbbbb 10bbbbbb + $charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) & + ((ord($string{($offset + 1)}) & 0x3F) << 6) & + (ord($string{($offset + 2)}) & 0x3F); + $offset += 3; + } elseif ((ord($string{$offset}) | 0x1F) == 0xDF) { + // 110bbbbb 10bbbbbb + $charval = ((ord($string{($offset + 0)}) & 0x1F) << 6) & + (ord($string{($offset + 1)}) & 0x3F); + $offset += 2; + } elseif ((ord($string{$offset}) | 0x7F) == 0x7F) { + // 0bbbbbbb + $charval = ord($string{$offset}); + $offset += 1; + } else { + // error? maybe throw some warning here? + $charval = false; + $offset += 1; + } + if ($charval !== false) { + $newcharstring .= (($charval < 65536) ? getid3_lib::LittleEndian2String($charval, 2) : '?'."\x00"); + } + } + return $newcharstring; + } + + // UTF-8 => UTF-16LE (BOM) + function iconv_fallback_utf8_utf16($string) { + return getid3_lib::iconv_fallback_utf8_utf16le($string, true); + } + + // UTF-16BE => UTF-8 + function iconv_fallback_utf16be_utf8($string) { + if (substr($string, 0, 2) == "\xFE\xFF") { + // strip BOM + $string = substr($string, 2); + } + $newcharstring = ''; + for ($i = 0; $i < strlen($string); $i += 2) { + $charval = getid3_lib::BigEndian2Int(substr($string, $i, 2)); + $newcharstring .= getid3_lib::iconv_fallback_int_utf8($charval); + } + return $newcharstring; + } + + // UTF-16LE => UTF-8 + function iconv_fallback_utf16le_utf8($string) { + if (substr($string, 0, 2) == "\xFF\xFE") { + // strip BOM + $string = substr($string, 2); + } + $newcharstring = ''; + for ($i = 0; $i < strlen($string); $i += 2) { + $charval = getid3_lib::LittleEndian2Int(substr($string, $i, 2)); + $newcharstring .= getid3_lib::iconv_fallback_int_utf8($charval); + } + return $newcharstring; + } + + // UTF-16BE => ISO-8859-1 + function iconv_fallback_utf16be_iso88591($string) { + if (substr($string, 0, 2) == "\xFE\xFF") { + // strip BOM + $string = substr($string, 2); + } + $newcharstring = ''; + for ($i = 0; $i < strlen($string); $i += 2) { + $charval = getid3_lib::BigEndian2Int(substr($string, $i, 2)); + $newcharstring .= (($charval < 256) ? chr($charval) : '?'); + } + return $newcharstring; + } + + // UTF-16LE => ISO-8859-1 + function iconv_fallback_utf16le_iso88591($string) { + if (substr($string, 0, 2) == "\xFF\xFE") { + // strip BOM + $string = substr($string, 2); + } + $newcharstring = ''; + for ($i = 0; $i < strlen($string); $i += 2) { + $charval = getid3_lib::LittleEndian2Int(substr($string, $i, 2)); + $newcharstring .= (($charval < 256) ? chr($charval) : '?'); + } + return $newcharstring; + } + + // UTF-16 (BOM) => ISO-8859-1 + function iconv_fallback_utf16_iso88591($string) { + $bom = substr($string, 0, 2); + if ($bom == "\xFE\xFF") { + return getid3_lib::iconv_fallback_utf16be_iso88591(substr($string, 2)); + } elseif ($bom == "\xFF\xFE") { + return getid3_lib::iconv_fallback_utf16le_iso88591(substr($string, 2)); + } + return $string; + } + + // UTF-16 (BOM) => UTF-8 + function iconv_fallback_utf16_utf8($string) { + $bom = substr($string, 0, 2); + if ($bom == "\xFE\xFF") { + return getid3_lib::iconv_fallback_utf16be_utf8(substr($string, 2)); + } elseif ($bom == "\xFF\xFE") { + return getid3_lib::iconv_fallback_utf16le_utf8(substr($string, 2)); + } + return $string; + } + + function iconv_fallback($in_charset, $out_charset, $string) { + + if ($in_charset == $out_charset) { + return $string; + } + + static $iconv_broken_or_unavailable = array(); + if (is_null(@$iconv_broken_or_unavailable[$in_charset.'_'.$out_charset])) { + $GETID3_ICONV_TEST_STRING = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ'; + + // Check iconv() + if (function_exists('iconv')) { + if (@iconv($in_charset, 'ISO-8859-1', @iconv('ISO-8859-1', $in_charset, $GETID3_ICONV_TEST_STRING)) == $GETID3_ICONV_TEST_STRING) { + if (@iconv($out_charset, 'ISO-8859-1', @iconv('ISO-8859-1', $out_charset, $GETID3_ICONV_TEST_STRING)) == $GETID3_ICONV_TEST_STRING) { + // everything works, use iconv() + $iconv_broken_or_unavailable[$in_charset.'_'.$out_charset] = false; + } else { + // iconv() available, but broken. Use getID3()'s iconv_fallback() conversions instead + // known issue in PHP v4.1.x + $iconv_broken_or_unavailable[$in_charset.'_'.$out_charset] = true; + } + } else { + // iconv() available, but broken. Use getID3()'s iconv_fallback() conversions instead + // known issue in PHP v4.1.x + $iconv_broken_or_unavailable[$in_charset.'_'.$out_charset] = true; + } + } else { + // iconv() unavailable, use getID3()'s iconv_fallback() conversions + $iconv_broken_or_unavailable[$in_charset.'_'.$out_charset] = true; + } + } + + if ($iconv_broken_or_unavailable[$in_charset.'_'.$out_charset]) { + static $ConversionFunctionList = array(); + if (empty($ConversionFunctionList)) { + $ConversionFunctionList['ISO-8859-1']['UTF-8'] = 'iconv_fallback_iso88591_utf8'; + $ConversionFunctionList['ISO-8859-1']['UTF-16'] = 'iconv_fallback_iso88591_utf16'; + $ConversionFunctionList['ISO-8859-1']['UTF-16BE'] = 'iconv_fallback_iso88591_utf16be'; + $ConversionFunctionList['ISO-8859-1']['UTF-16LE'] = 'iconv_fallback_iso88591_utf16le'; + $ConversionFunctionList['UTF-8']['ISO-8859-1'] = 'iconv_fallback_utf8_iso88591'; + $ConversionFunctionList['UTF-8']['UTF-16'] = 'iconv_fallback_utf8_utf16'; + $ConversionFunctionList['UTF-8']['UTF-16BE'] = 'iconv_fallback_utf8_utf16be'; + $ConversionFunctionList['UTF-8']['UTF-16LE'] = 'iconv_fallback_utf8_utf16le'; + $ConversionFunctionList['UTF-16']['ISO-8859-1'] = 'iconv_fallback_utf16_iso88591'; + $ConversionFunctionList['UTF-16']['UTF-8'] = 'iconv_fallback_utf16_utf8'; + $ConversionFunctionList['UTF-16LE']['ISO-8859-1'] = 'iconv_fallback_utf16le_iso88591'; + $ConversionFunctionList['UTF-16LE']['UTF-8'] = 'iconv_fallback_utf16le_utf8'; + $ConversionFunctionList['UTF-16BE']['ISO-8859-1'] = 'iconv_fallback_utf16be_iso88591'; + $ConversionFunctionList['UTF-16BE']['UTF-8'] = 'iconv_fallback_utf16be_utf8'; + } + if (isset($ConversionFunctionList[strtoupper($in_charset)][strtoupper($out_charset)])) { + $ConversionFunction = $ConversionFunctionList[strtoupper($in_charset)][strtoupper($out_charset)]; + return getid3_lib::$ConversionFunction($string); + } + die('PHP does not have iconv() support - cannot convert from '.$in_charset.' to '.$out_charset); + } + + if ($converted_string = @iconv($in_charset, $out_charset.'//TRANSLIT', $string)) { + switch ($out_charset) { + case 'ISO-8859-1': + $converted_string = rtrim($converted_string, "\x00"); + break; + } + return $converted_string; + } + + // iconv() may sometimes fail with "illegal character in input string" error message + // and return an empty string, but returning the unconverted string is more useful + return $string; + } + + + function MultiByteCharString2HTML($string, $charset='ISO-8859-1') { + $HTMLstring = ''; + + switch ($charset) { + case 'ISO-8859-1': + case 'ISO8859-1': + case 'ISO-8859-15': + case 'ISO8859-15': + case 'cp866': + case 'ibm866': + case '866': + case 'cp1251': + case 'Windows-1251': + case 'win-1251': + case '1251': + case 'cp1252': + case 'Windows-1252': + case '1252': + case 'KOI8-R': + case 'koi8-ru': + case 'koi8r': + case 'BIG5': + case '950': + case 'GB2312': + case '936': + case 'BIG5-HKSCS': + case 'Shift_JIS': + case 'SJIS': + case '932': + case 'EUC-JP': + case 'EUCJP': + $HTMLstring = htmlentities($string, ENT_COMPAT, $charset); + break; + + case 'UTF-8': + $strlen = strlen($string); + for ($i = 0; $i < $strlen; $i++) { + $char_ord_val = ord($string{$i}); + $charval = 0; + if ($char_ord_val < 0x80) { + $charval = $char_ord_val; + } elseif ((($char_ord_val & 0xF0) >> 4) == 0x0F) { + $charval = (($char_ord_val & 0x07) << 18); + $charval += ((ord($string{++$i}) & 0x3F) << 12); + $charval += ((ord($string{++$i}) & 0x3F) << 6); + $charval += (ord($string{++$i}) & 0x3F); + } elseif ((($char_ord_val & 0xE0) >> 5) == 0x07) { + $charval = (($char_ord_val & 0x0F) << 12); + $charval += ((ord($string{++$i}) & 0x3F) << 6); + $charval += (ord($string{++$i}) & 0x3F); + } elseif ((($char_ord_val & 0xC0) >> 6) == 0x03) { + $charval = (($char_ord_val & 0x1F) << 6); + $charval += (ord($string{++$i}) & 0x3F); + } + if (($charval >= 32) && ($charval <= 127)) { + $HTMLstring .= chr($charval); + } else { + $HTMLstring .= '&#'.$charval.';'; + } + } + break; + + case 'UTF-16LE': + for ($i = 0; $i < strlen($string); $i += 2) { + $charval = getid3_lib::LittleEndian2Int(substr($string, $i, 2)); + if (($charval >= 32) && ($charval <= 127)) { + $HTMLstring .= chr($charval); + } else { + $HTMLstring .= '&#'.$charval.';'; + } + } + break; + + case 'UTF-16BE': + for ($i = 0; $i < strlen($string); $i += 2) { + $charval = getid3_lib::BigEndian2Int(substr($string, $i, 2)); + if (($charval >= 32) && ($charval <= 127)) { + $HTMLstring .= chr($charval); + } else { + $HTMLstring .= '&#'.$charval.';'; + } + } + break; + + default: + $HTMLstring = 'ERROR: Character set "'.$charset.'" not supported in MultiByteCharString2HTML()'; + break; + } + return $HTMLstring; + } + + + + function RGADnameLookup($namecode) { + static $RGADname = array(); + if (empty($RGADname)) { + $RGADname[0] = 'not set'; + $RGADname[1] = 'Track Gain Adjustment'; + $RGADname[2] = 'Album Gain Adjustment'; + } + + return (isset($RGADname[$namecode]) ? $RGADname[$namecode] : ''); + } + + + function RGADoriginatorLookup($originatorcode) { + static $RGADoriginator = array(); + if (empty($RGADoriginator)) { + $RGADoriginator[0] = 'unspecified'; + $RGADoriginator[1] = 'pre-set by artist/producer/mastering engineer'; + $RGADoriginator[2] = 'set by user'; + $RGADoriginator[3] = 'determined automatically'; + } + + return (isset($RGADoriginator[$originatorcode]) ? $RGADoriginator[$originatorcode] : ''); + } + + + function RGADadjustmentLookup($rawadjustment, $signbit) { + $adjustment = $rawadjustment / 10; + if ($signbit == 1) { + $adjustment *= -1; + } + return (float) $adjustment; + } + + + function RGADgainString($namecode, $originatorcode, $replaygain) { + if ($replaygain < 0) { + $signbit = '1'; + } else { + $signbit = '0'; + } + $storedreplaygain = intval(round($replaygain * 10)); + $gainstring = str_pad(decbin($namecode), 3, '0', STR_PAD_LEFT); + $gainstring .= str_pad(decbin($originatorcode), 3, '0', STR_PAD_LEFT); + $gainstring .= $signbit; + $gainstring .= str_pad(decbin($storedreplaygain), 9, '0', STR_PAD_LEFT); + + return $gainstring; + } + + function RGADamplitude2dB($amplitude) { + return 20 * log10($amplitude); + } + + + function GetDataImageSize($imgData) { + $GetDataImageSize = false; + if ($tempfilename = tempnam('*', 'getID3')) { + if ($tmp = @fopen($tempfilename, 'wb')) { + fwrite($tmp, $imgData); + fclose($tmp); + $GetDataImageSize = @GetImageSize($tempfilename); + } + unlink($tempfilename); + } + return $GetDataImageSize; + } + + function ImageTypesLookup($imagetypeid) { + static $ImageTypesLookup = array(); + if (empty($ImageTypesLookup)) { + $ImageTypesLookup[1] = 'gif'; + $ImageTypesLookup[2] = 'jpeg'; + $ImageTypesLookup[3] = 'png'; + $ImageTypesLookup[4] = 'swf'; + $ImageTypesLookup[5] = 'psd'; + $ImageTypesLookup[6] = 'bmp'; + $ImageTypesLookup[7] = 'tiff (little-endian)'; + $ImageTypesLookup[8] = 'tiff (big-endian)'; + $ImageTypesLookup[9] = 'jpc'; + $ImageTypesLookup[10] = 'jp2'; + $ImageTypesLookup[11] = 'jpx'; + $ImageTypesLookup[12] = 'jb2'; + $ImageTypesLookup[13] = 'swc'; + $ImageTypesLookup[14] = 'iff'; + } + return (isset($ImageTypesLookup[$imagetypeid]) ? $ImageTypesLookup[$imagetypeid] : ''); + } + + function CopyTagsToComments(&$ThisFileInfo) { + + // Copy all entries from ['tags'] into common ['comments'] + if (!empty($ThisFileInfo['tags'])) { + foreach ($ThisFileInfo['tags'] as $tagtype => $tagarray) { + foreach ($tagarray as $tagname => $tagdata) { + foreach ($tagdata as $key => $value) { + if (!empty($value)) { + if (empty($ThisFileInfo['comments'][$tagname])) { + + // fall through and append value + + } elseif ($tagtype == 'id3v1') { + + $newvaluelength = strlen(trim($value)); + foreach ($ThisFileInfo['comments'][$tagname] as $existingkey => $existingvalue) { + $oldvaluelength = strlen(trim($existingvalue)); + if (($newvaluelength <= $oldvaluelength) && (substr($existingvalue, 0, $newvaluelength) == trim($value))) { + // new value is identical but shorter-than (or equal-length to) one already in comments - skip + break 2; + } + } + + } else { + + $newvaluelength = strlen(trim($value)); + foreach ($ThisFileInfo['comments'][$tagname] as $existingkey => $existingvalue) { + $oldvaluelength = strlen(trim($existingvalue)); + if (($newvaluelength > $oldvaluelength) && (substr(trim($value), 0, strlen($existingvalue)) == $existingvalue)) { + $ThisFileInfo['comments'][$tagname][$existingkey] = trim($value); + break 2; + } + } + + } + if (empty($ThisFileInfo['comments'][$tagname]) || !in_array(trim($value), $ThisFileInfo['comments'][$tagname])) { + $ThisFileInfo['comments'][$tagname][] = trim($value); + } + } + } + } + } + + // Copy to ['comments_html'] + foreach ($ThisFileInfo['comments'] as $field => $values) { + foreach ($values as $index => $value) { + $ThisFileInfo['comments_html'][$field][$index] = str_replace('�', '', getid3_lib::MultiByteCharString2HTML($value, $ThisFileInfo['encoding'])); + } + } + } + } + + + function EmbeddedLookup($key, $begin, $end, $file, $name) { + + // Cached + static $cache; + if (isset($cache[$file][$name])) { + return @$cache[$file][$name][$key]; + } + + // Init + $keylength = strlen($key); + $line_count = $end - $begin - 7; + + // Open php file + $fp = fopen($file, 'r'); + + // Discard $begin lines + for ($i = 0; $i < ($begin + 3); $i++) { + fgets($fp, 1024); + } + + // Loop thru line + while (0 < $line_count--) { + + // Read line + $line = ltrim(fgets($fp, 1024), "\t "); + + // METHOD A: only cache the matching key - less memory but slower on next lookup of not-previously-looked-up key + //$keycheck = substr($line, 0, $keylength); + //if ($key == $keycheck) { + // $cache[$file][$name][$keycheck] = substr($line, $keylength + 1); + // break; + //} + + // METHOD B: cache all keys in this lookup - more memory but faster on next lookup of not-previously-looked-up key + //$cache[$file][$name][substr($line, 0, $keylength)] = trim(substr($line, $keylength + 1)); + @list($ThisKey, $ThisValue) = explode("\t", $line, 2); + $cache[$file][$name][$ThisKey] = trim($ThisValue); + } + + // Close and return + fclose($fp); + return @$cache[$file][$name][$key]; + } + + function IncludeDependency($filename, $sourcefile, $DieOnFailure=false) { + global $GETID3_ERRORARRAY; + + if (file_exists($filename)) { + if (@include_once($filename)) { + return true; + } else { + $diemessage = basename($sourcefile).' depends on '.$filename.', which has errors'; + } + } else { + $diemessage = basename($sourcefile).' depends on '.$filename.', which is missing'; + } + if ($DieOnFailure) { + die($diemessage); + } else { + $GETID3_ERRORARRAY[] = $diemessage; + } + return false; + } + +} + +?> diff --git a/includes/getid3/getid3/getid3.php b/includes/getid3/getid3/getid3.php new file mode 100644 index 0000000..944868d --- /dev/null +++ b/includes/getid3/getid3/getid3.php @@ -0,0 +1,1305 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// // +// Please see readme.txt for more information // +// /// +///////////////////////////////////////////////////////////////// + +// Defines +define('GETID3_VERSION', '1.7.7'); +define('GETID3_FREAD_BUFFER_SIZE', 16384); // read buffer size in bytes + + + +class getID3 +{ + // public: Settings + var $encoding = 'ISO-8859-1'; // CASE SENSITIVE! - i.e. (must be supported by iconv()) + // Examples: ISO-8859-1 UTF-8 UTF-16 UTF-16BE + + var $encoding_id3v1 = 'ISO-8859-1'; // Should always be 'ISO-8859-1', but some tags may be written in other encodings such as 'EUC-CN' + + var $tempdir = '*'; // default '*' should use system temp dir + + // public: Optional tag checks - disable for speed. + var $option_tag_id3v1 = true; // Read and process ID3v1 tags + var $option_tag_id3v2 = true; // Read and process ID3v2 tags + var $option_tag_lyrics3 = true; // Read and process Lyrics3 tags + var $option_tag_apetag = true; // Read and process APE tags + var $option_tags_process = true; // Copy tags to root key 'tags' and encode to $this->encoding + var $option_tags_html = true; // Copy tags to root key 'tags_html' properly translated from various encodings to HTML entities + + // public: Optional tag/comment calucations + var $option_extra_info = true; // Calculate additional info such as bitrate, channelmode etc + + // public: Optional calculations + var $option_md5_data = false; // Get MD5 sum of data part - slow + var $option_md5_data_source = false; // Use MD5 of source file if availble - only FLAC and OptimFROG + var $option_sha1_data = false; // Get SHA1 sum of data part - slow + var $option_max_2gb_check = true; // Check whether file is larger than 2 Gb and thus not supported by PHP + + // private + var $filename; + + + // public: constructor + function getID3() + { + + $this->startup_error = ''; + $this->startup_warning = ''; + + // Check for PHP version >= 4.1.0 + if (phpversion() < '4.1.0') { + $this->startup_error .= 'getID3() requires PHP v4.1.0 or higher - you are running v'.phpversion(); + } + + // Check memory + $memory_limit = ini_get('memory_limit'); + if (preg_match('/([0-9]+)M/i', $memory_limit, $matches)) { + // could be stored as "16M" rather than 16777216 for example + $memory_limit = $matches[1] * 1048576; + } + if ($memory_limit <= 0) { + // memory limits probably disabled + } elseif ($memory_limit <= 3145728) { + $this->startup_error .= 'PHP has less than 3MB available memory and will very likely run out. Increase memory_limit in php.ini'; + } elseif ($memory_limit <= 12582912) { + $this->startup_warning .= 'PHP has less than 12MB available memory and might run out if all modules are loaded. Increase memory_limit in php.ini'; + } + + // Check safe_mode off + if ((bool) ini_get('safe_mode')) { + $this->warning('WARNING: Safe mode is on, shorten support disabled, md5data/sha1data for ogg vorbis disabled, ogg vorbos/flac tag writing disabled.'); + } + + + // define a constant rather than looking up every time it is needed + if (!defined('GETID3_OS_ISWINDOWS')) { + if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') { + define('GETID3_OS_ISWINDOWS', true); + } else { + define('GETID3_OS_ISWINDOWS', false); + } + } + + // Get base path of getID3() - ONCE + if (!defined('GETID3_INCLUDEPATH')) { + foreach (get_included_files() as $key => $val) { + if (basename($val) == 'getid3.php') { + define('GETID3_INCLUDEPATH', dirname($val).DIRECTORY_SEPARATOR); + break; + } + } + } + + // Load support library + if (!include_once(GETID3_INCLUDEPATH.'getid3.lib.php')) { + $this->startup_error .= 'getid3.lib.php is missing or corrupt'; + } + + + // Needed for Windows only: + // Define locations of helper applications for Shorten, VorbisComment, MetaFLAC + // as well as other helper functions such as head, tail, md5sum, etc + // IMPORTANT: This path cannot have spaces in it. If neccesary, use the 8dot3 equivalent + // ie for "C:/Program Files/Apache/" put "C:/PROGRA~1/APACHE/" + // IMPORTANT: This path must include the trailing slash + if (GETID3_OS_ISWINDOWS && !defined('GETID3_HELPERAPPSDIR')) { + $helperappsdir = GETID3_INCLUDEPATH.'..'.DIRECTORY_SEPARATOR.'helperapps'; // must not have any space in this path + + if (!is_dir($helperappsdir)) { + + $this->startup_error .= '"'.$helperappsdir.'" cannot be defined as GETID3_HELPERAPPSDIR because it does not exist'; + + } elseif (strpos(realpath($helperappsdir), ' ') !== false) { + + $DirPieces = explode(DIRECTORY_SEPARATOR, realpath($helperappsdir)); + $DirPieces8 = $DirPieces; + + $CLIdir = $DirPieces[0].' && cd \\'; + for ($i = 1; $i < count($DirPieces); $i++) { + if (strpos($DirPieces[$i], ' ') === false) { + $CLIdir .= ' && cd '.$DirPieces[$i]; + } else { + ob_start(); + system($CLIdir.' && dir /ad /x'); + $subdirsraw = explode("\n", ob_get_contents()); + ob_end_clean(); + foreach ($subdirsraw as $dummy => $line) { + if (preg_match('#^[0-9]{4}/[0-9]{2}/[0-9]{2} [0-9]{2}:[0-9]{2} [AP]M ([^ ]{8}) '.preg_quote($DirPieces[$i]).'$#i', trim($line), $matches)) { + $CLIdir .= ' && cd '.$matches[1]; + break; + } + } + $DirPieces8[$i] = $matches[1]; + } + } + $helperappsdir = implode(DIRECTORY_SEPARATOR, $DirPieces8); + + } + define('GETID3_HELPERAPPSDIR', realpath($helperappsdir).DIRECTORY_SEPARATOR); + + } + + } + + + // public: setOption + function setOption($optArray) { + if (!is_array($optArray) || empty($optArray)) { + return false; + } + foreach ($optArray as $opt => $val) { + if (isset($this, $opt) === false) { + continue; + } + $this->$opt = $val; + } + return true; + } + + + // public: analyze file - replaces GetAllFileInfo() and GetTagOnly() + function analyze($filename) { + + if (!empty($this->startup_error)) { + return $this->error($this->startup_error); + } + if (!empty($this->startup_warning)) { + $this->warning($this->startup_warning); + } + + // init result array and set parameters + $this->info = array(); + $this->info['GETID3_VERSION'] = GETID3_VERSION; + + // Check encoding/iconv support + if (!function_exists('iconv') && !in_array($this->encoding, array('ISO-8859-1', 'UTF-8', 'UTF-16LE', 'UTF-16BE', 'UTF-16'))) { + $errormessage = 'iconv() support is needed for encodings other than ISO-8859-1, UTF-8, UTF-16LE, UTF16-BE, UTF-16. '; + if (GETID3_OS_ISWINDOWS) { + $errormessage .= 'PHP does not have iconv() support. Please enable php_iconv.dll in php.ini, and copy iconv.dll from c:/php/dlls to c:/windows/system32'; + } else { + $errormessage .= 'PHP is not compiled with iconv() support. Please recompile with the --with-iconv switch'; + } + return $this->error($errormessage); + } + + // Disable magic_quotes_runtime, if neccesary + $old_magic_quotes_runtime = get_magic_quotes_runtime(); // store current setting of magic_quotes_runtime + if ($old_magic_quotes_runtime) { + set_magic_quotes_runtime(0); // turn off magic_quotes_runtime + if (get_magic_quotes_runtime()) { + return $this->error('Could not disable magic_quotes_runtime - getID3() cannot work properly with this setting enabled'); + } + } + + // remote files not supported + if (preg_match('/^(ht|f)tp:\/\//', $filename)) { + return $this->error('Remote files are not supported in this version of getID3() - please copy the file locally first'); + } + + // open local file + if (!$fp = @fopen($filename, 'rb')) { + return $this->error('Could not open file "'.$filename.'"'); + } + + // set parameters + $this->info['filesize'] = filesize($filename); + + // option_max_2gb_check + if ($this->option_max_2gb_check) { + // PHP doesn't support integers larger than 31-bit (~2GB) + // filesize() simply returns (filesize % (pow(2, 32)), no matter the actual filesize + // ftell() returns 0 if seeking to the end is beyond the range of unsigned integer + fseek($fp, 0, SEEK_END); + if ((($this->info['filesize'] != 0) && (ftell($fp) == 0)) || + ($this->info['filesize'] < 0) || + (ftell($fp) < 0)) { + unset($this->info['filesize']); + fclose($fp); + return $this->error('File is most likely larger than 2GB and is not supported by PHP'); + } + } + + // set more parameters + $this->info['avdataoffset'] = 0; + $this->info['avdataend'] = $this->info['filesize']; + $this->info['fileformat'] = ''; // filled in later + $this->info['audio']['dataformat'] = ''; // filled in later, unset if not used + $this->info['video']['dataformat'] = ''; // filled in later, unset if not used + $this->info['tags'] = array(); // filled in later, unset if not used + $this->info['error'] = array(); // filled in later, unset if not used + $this->info['warning'] = array(); // filled in later, unset if not used + $this->info['comments'] = array(); // filled in later, unset if not used + $this->info['encoding'] = $this->encoding; // required by id3v2 and iso modules - can be unset at the end if desired + + // set redundant parameters - might be needed in some include file + $this->info['filename'] = basename($filename); + $this->info['filepath'] = str_replace('\\', '/', realpath(dirname($filename))); + $this->info['filenamepath'] = $this->info['filepath'].'/'.$this->info['filename']; + + + // handle ID3v2 tag - done first - already at beginning of file + // ID3v2 detection (even if not parsing) is always done otherwise fileformat is much harder to detect + if ($this->option_tag_id3v2) { + + $GETID3_ERRORARRAY = &$this->info['warning']; + if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, false)) { + $tag = new getid3_id3v2($fp, $this->info); + } + + } else { + + fseek($fp, 0, SEEK_SET); + $header = fread($fp, 10); + if (substr($header, 0, 3) == 'ID3') { + $this->info['id3v2']['header'] = true; + $this->info['id3v2']['majorversion'] = ord($header{3}); + $this->info['id3v2']['minorversion'] = ord($header{4}); + $this->info['id3v2']['headerlength'] = getid3_lib::BigEndian2Int(substr($header, 6, 4), 1) + 10; // length of ID3v2 tag in 10-byte header doesn't include 10-byte header length + + $this->info['id3v2']['tag_offset_start'] = 0; + $this->info['id3v2']['tag_offset_end'] = $this->info['id3v2']['tag_offset_start'] + $this->info['id3v2']['headerlength']; + $this->info['avdataoffset'] = $this->info['id3v2']['tag_offset_end']; + } + + } + + + // handle ID3v1 tag + if ($this->option_tag_id3v1) { + if (!@include_once(GETID3_INCLUDEPATH.'module.tag.id3v1.php')) { + return $this->error('module.tag.id3v1.php is missing - you may disable option_tag_id3v1.'); + } + $tag = new getid3_id3v1($fp, $this->info); + } + + // handle APE tag + if ($this->option_tag_apetag) { + if (!@include_once(GETID3_INCLUDEPATH.'module.tag.apetag.php')) { + return $this->error('module.tag.apetag.php is missing - you may disable option_tag_apetag.'); + } + $tag = new getid3_apetag($fp, $this->info); + } + + // handle lyrics3 tag + if ($this->option_tag_lyrics3) { + if (!@include_once(GETID3_INCLUDEPATH.'module.tag.lyrics3.php')) { + return $this->error('module.tag.lyrics3.php is missing - you may disable option_tag_lyrics3.'); + } + $tag = new getid3_lyrics3($fp, $this->info); + } + + // read 32 kb file data + fseek($fp, $this->info['avdataoffset'], SEEK_SET); + $formattest = fread($fp, 32774); + + // determine format + $determined_format = $this->GetFileFormat($formattest, $filename); + + // unable to determine file format + if (!$determined_format) { + fclose($fp); + return $this->error('unable to determine file format'); + } + + // check for illegal ID3 tags + if (isset($determined_format['fail_id3']) && (in_array('id3v1', $this->info['tags']) || in_array('id3v2', $this->info['tags']))) { + if ($determined_format['fail_id3'] === 'ERROR') { + fclose($fp); + return $this->error('ID3 tags not allowed on this file type.'); + } elseif ($determined_format['fail_id3'] === 'WARNING') { + $this->info['warning'][] = 'ID3 tags not allowed on this file type.'; + } + } + + // check for illegal APE tags + if (isset($determined_format['fail_ape']) && in_array('ape', $this->info['tags'])) { + if ($determined_format['fail_ape'] === 'ERROR') { + fclose($fp); + return $this->error('APE tags not allowed on this file type.'); + } elseif ($determined_format['fail_ape'] === 'WARNING') { + $this->info['warning'][] = 'APE tags not allowed on this file type.'; + } + } + + // set mime type + $this->info['mime_type'] = $determined_format['mime_type']; + + // supported format signature pattern detected, but module deleted + if (!file_exists(GETID3_INCLUDEPATH.$determined_format['include'])) { + fclose($fp); + return $this->error('Format not supported, module, '.$determined_format['include'].', was removed.'); + } + + // module requires iconv support + if (!function_exists('iconv') && @$determined_format['iconv_req']) { + return $this->error('iconv support is required for this module ('.$determined_format['include'].').'); + } + + // include module + include_once(GETID3_INCLUDEPATH.$determined_format['include']); + + // instantiate module class + $class_name = 'getid3_'.$determined_format['module']; + if (!class_exists($class_name)) { + return $this->error('Format not supported, module, '.$determined_format['include'].', is corrupt.'); + } + if (isset($determined_format['option'])) { + $class = new $class_name($fp, $this->info, $determined_format['option']); + } else { + $class = new $class_name($fp, $this->info); + } + + // close file + fclose($fp); + + // process all tags - copy to 'tags' and convert charsets + if ($this->option_tags_process) { + $this->HandleAllTags(); + } + + // perform more calculations + if ($this->option_extra_info) { + $this->ChannelsBitratePlaytimeCalculations(); + $this->CalculateCompressionRatioVideo(); + $this->CalculateCompressionRatioAudio(); + $this->CalculateReplayGain(); + $this->ProcessAudioStreams(); + } + + // get the MD5 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags + if ($this->option_md5_data) { + // do not cald md5_data if md5_data_source is present - set by flac only - future MPC/SV8 too + if (!$this->option_md5_data_source || empty($this->info['md5_data_source'])) { + $this->getHashdata('md5'); + } + } + + // get the SHA1 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags + if ($this->option_sha1_data) { + $this->getHashdata('sha1'); + } + + // remove undesired keys + $this->CleanUp(); + + // restore magic_quotes_runtime setting + set_magic_quotes_runtime($old_magic_quotes_runtime); + + // return info array + return $this->info; + } + + + // private: error handling + function error($message) { + + $this->CleanUp(); + + $this->info['error'][] = $message; + return $this->info; + } + + + // private: warning handling + function warning($message) { + $this->info['warning'][] = $message; + return true; + } + + + // private: CleanUp + function CleanUp() { + + // remove possible empty keys + $AVpossibleEmptyKeys = array('dataformat', 'bits_per_sample', 'encoder_options', 'streams'); + foreach ($AVpossibleEmptyKeys as $dummy => $key) { + if (empty($this->info['audio'][$key]) && isset($this->info['audio'][$key])) { + unset($this->info['audio'][$key]); + } + if (empty($this->info['video'][$key]) && isset($this->info['video'][$key])) { + unset($this->info['video'][$key]); + } + } + + // remove empty root keys + if (!empty($this->info)) { + foreach ($this->info as $key => $value) { + if (empty($this->info[$key]) && ($this->info[$key] !== 0) && ($this->info[$key] !== '0')) { + unset($this->info[$key]); + } + } + } + + // remove meaningless entries from unknown-format files + if (empty($this->info['fileformat'])) { + if (isset($this->info['avdataoffset'])) { + unset($this->info['avdataoffset']); + } + if (isset($this->info['avdataend'])) { + unset($this->info['avdataend']); + } + } + } + + + // return array containing information about all supported formats + function GetFileFormatArray() { + static $format_info = array(); + if (empty($format_info)) { + $format_info = array( + + // Audio formats + + // AC-3 - audio - Dolby AC-3 / Dolby Digital + 'ac3' => array( + 'pattern' => '^\x0B\x77', + 'group' => 'audio', + 'module' => 'ac3', + 'mime_type' => 'audio/ac3', + ), + + // AAC - audio - Advanced Audio Coding (AAC) - ADIF format + 'adif' => array( + 'pattern' => '^ADIF', + 'group' => 'audio', + 'module' => 'aac', + 'option' => 'adif', + 'mime_type' => 'application/octet-stream', + 'fail_ape' => 'WARNING', + ), + + + // AAC - audio - Advanced Audio Coding (AAC) - ADTS format (very similar to MP3) + 'adts' => array( + 'pattern' => '^\xFF[\xF0-\xF1\xF8-\xF9]', + 'group' => 'audio', + 'module' => 'aac', + 'option' => 'adts', + 'mime_type' => 'application/octet-stream', + 'fail_ape' => 'WARNING', + ), + + + // AU - audio - NeXT/Sun AUdio (AU) + 'au' => array( + 'pattern' => '^\.snd', + 'group' => 'audio', + 'module' => 'au', + 'mime_type' => 'audio/basic', + ), + + // AVR - audio - Audio Visual Research + 'avr' => array( + 'pattern' => '^2BIT', + 'group' => 'audio', + 'module' => 'avr', + 'mime_type' => 'application/octet-stream', + ), + + // BONK - audio - Bonk v0.9+ + 'bonk' => array( + 'pattern' => '^\x00(BONK|INFO|META| ID3)', + 'group' => 'audio', + 'module' => 'bonk', + 'mime_type' => 'audio/xmms-bonk', + ), + + // FLAC - audio - Free Lossless Audio Codec + 'flac' => array( + 'pattern' => '^fLaC', + 'group' => 'audio', + 'module' => 'flac', + 'mime_type' => 'audio/x-flac', + ), + + // LA - audio - Lossless Audio (LA) + 'la' => array( + 'pattern' => '^LA0[2-4]', + 'group' => 'audio', + 'module' => 'la', + 'mime_type' => 'application/octet-stream', + ), + + // LPAC - audio - Lossless Predictive Audio Compression (LPAC) + 'lpac' => array( + 'pattern' => '^LPAC', + 'group' => 'audio', + 'module' => 'lpac', + 'mime_type' => 'application/octet-stream', + ), + + // MIDI - audio - MIDI (Musical Instrument Digital Interface) + 'midi' => array( + 'pattern' => '^MThd', + 'group' => 'audio', + 'module' => 'midi', + 'mime_type' => 'audio/midi', + ), + + // MAC - audio - Monkey's Audio Compressor + 'mac' => array( + 'pattern' => '^MAC ', + 'group' => 'audio', + 'module' => 'monkey', + 'mime_type' => 'application/octet-stream', + ), + + // MOD - audio - MODule (assorted sub-formats) + 'mod' => array( + 'pattern' => '^.{1080}(M.K.|[5-9]CHN|[1-3][0-9]CH)', + 'group' => 'audio', + 'module' => 'mod', + 'option' => 'mod', + 'mime_type' => 'audio/mod', + ), + + // MOD - audio - MODule (Impulse Tracker) + 'it' => array( + 'pattern' => '^IMPM', + 'group' => 'audio', + 'module' => 'mod', + 'option' => 'it', + 'mime_type' => 'audio/it', + ), + + // MOD - audio - MODule (eXtended Module, various sub-formats) + 'xm' => array( + 'pattern' => '^Extended Module', + 'group' => 'audio', + 'module' => 'mod', + 'option' => 'xm', + 'mime_type' => 'audio/xm', + ), + + // MOD - audio - MODule (ScreamTracker) + 's3m' => array( + 'pattern' => '^.{44}SCRM', + 'group' => 'audio', + 'module' => 'mod', + 'option' => 's3m', + 'mime_type' => 'audio/s3m', + ), + + // MPC - audio - Musepack / MPEGplus + 'mpc' => array( + 'pattern' => '^(MP\+|[\x00\x01\x10\x11\x40\x41\x50\x51\x80\x81\x90\x91\xC0\xC1\xD0\xD1][\x20-37][\x00\x20\x40\x60\x80\xA0\xC0\xE0])', + 'group' => 'audio', + 'module' => 'mpc', + 'mime_type' => 'application/octet-stream', + ), + + // MP3 - audio - MPEG-audio Layer 3 (very similar to AAC-ADTS) + 'mp3' => array( + 'pattern' => '^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]', + 'group' => 'audio', + 'module' => 'mp3', + 'mime_type' => 'audio/mpeg', + ), + + // OFR - audio - OptimFROG + 'ofr' => array( + 'pattern' => '^(\*RIFF|OFR)', + 'group' => 'audio', + 'module' => 'optimfrog', + 'mime_type' => 'application/octet-stream', + ), + + // RKAU - audio - RKive AUdio compressor + 'rkau' => array( + 'pattern' => '^RKA', + 'group' => 'audio', + 'module' => 'rkau', + 'mime_type' => 'application/octet-stream', + ), + + // SHN - audio - Shorten + 'shn' => array( + 'pattern' => '^ajkg', + 'group' => 'audio', + 'module' => 'shorten', + 'mime_type' => 'audio/xmms-shn', + 'fail_id3' => 'ERROR', + 'fail_ape' => 'ERROR', + ), + + // TTA - audio - TTA Lossless Audio Compressor (http://tta.corecodec.org) + 'tta' => array( + 'pattern' => '^TTA', // could also be '^TTA(\x01|\x02|\x03|2|1)' + 'group' => 'audio', + 'module' => 'tta', + 'mime_type' => 'application/octet-stream', + ), + + // VOC - audio - Creative Voice (VOC) + 'voc' => array( + 'pattern' => '^Creative Voice File', + 'group' => 'audio', + 'module' => 'voc', + 'mime_type' => 'audio/voc', + ), + + // VQF - audio - transform-domain weighted interleave Vector Quantization Format (VQF) + 'vqf' => array( + 'pattern' => '^TWIN', + 'group' => 'audio', + 'module' => 'vqf', + 'mime_type' => 'application/octet-stream', + ), + + // WV - audio - WavPack (v4.0+) + 'wv' => array( + 'pattern' => '^wvpk', + 'group' => 'audio', + 'module' => 'wavpack', + 'mime_type' => 'application/octet-stream', + ), + + + // Audio-Video formats + + // ASF - audio/video - Advanced Streaming Format, Windows Media Video, Windows Media Audio + 'asf' => array( + 'pattern' => '^\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C', + 'group' => 'audio-video', + 'module' => 'asf', + 'mime_type' => 'video/x-ms-asf', + 'iconv_req' => false, + ), + + // BINK - audio/video - Bink / Smacker + 'bink' => array( + 'pattern' => '^(BIK|SMK)', + 'group' => 'audio-video', + 'module' => 'bink', + 'mime_type' => 'application/octet-stream', + ), + + // FLV - audio/video - FLash Video + 'flv' => array( + 'pattern' => '^FLV\x01', + 'group' => 'audio-video', + 'module' => 'flv', + 'mime_type' => 'video/x-flv', + ), + + // MKAV - audio/video - Mastroka + 'matroska' => array( + 'pattern' => '^\x1A\x45\xDF\xA3', + 'group' => 'audio-video', + 'module' => 'matroska', + 'mime_type' => 'application/octet-stream', + ), + + // MPEG - audio/video - MPEG (Moving Pictures Experts Group) + 'mpeg' => array( + 'pattern' => '^\x00\x00\x01(\xBA|\xB3)', + 'group' => 'audio-video', + 'module' => 'mpeg', + 'mime_type' => 'video/mpeg', + ), + + // NSV - audio/video - Nullsoft Streaming Video (NSV) + 'nsv' => array( + 'pattern' => '^NSV[sf]', + 'group' => 'audio-video', + 'module' => 'nsv', + 'mime_type' => 'application/octet-stream', + ), + + // Ogg - audio/video - Ogg (Ogg-Vorbis, Ogg-FLAC, Speex, Ogg-Theora(*), Ogg-Tarkin(*)) + 'ogg' => array( + 'pattern' => '^OggS', + 'group' => 'audio', + 'module' => 'ogg', + 'mime_type' => 'application/ogg', + 'fail_id3' => 'WARNING', + 'fail_ape' => 'WARNING', + ), + + // QT - audio/video - Quicktime + 'quicktime' => array( + 'pattern' => '^.{4}(cmov|free|ftyp|mdat|moov|pnot|skip|wide)', + 'group' => 'audio-video', + 'module' => 'quicktime', + 'mime_type' => 'video/quicktime', + ), + + // RIFF - audio/video - Resource Interchange File Format (RIFF) / WAV / AVI / CD-audio / SDSS = renamed variant used by SmartSound QuickTracks (www.smartsound.com) / FORM = Audio Interchange File Format (AIFF) + 'riff' => array( + 'pattern' => '^(RIFF|SDSS|FORM)', + 'group' => 'audio-video', + 'module' => 'riff', + 'mime_type' => 'audio/x-wave', + 'fail_ape' => 'WARNING', + ), + + // Real - audio/video - RealAudio, RealVideo + 'real' => array( + 'pattern' => '^(\.RMF|.ra)', + 'group' => 'audio-video', + 'module' => 'real', + 'mime_type' => 'audio/x-realaudio', + ), + + // SWF - audio/video - ShockWave Flash + 'swf' => array( + 'pattern' => '^(F|C)WS', + 'group' => 'audio-video', + 'module' => 'swf', + 'mime_type' => 'application/x-shockwave-flash', + ), + + + // Still-Image formats + + // BMP - still image - Bitmap (Windows, OS/2; uncompressed, RLE8, RLE4) + 'bmp' => array( + 'pattern' => '^BM', + 'group' => 'graphic', + 'module' => 'bmp', + 'mime_type' => 'image/bmp', + 'fail_id3' => 'ERROR', + 'fail_ape' => 'ERROR', + ), + + // GIF - still image - Graphics Interchange Format + 'gif' => array( + 'pattern' => '^GIF', + 'group' => 'graphic', + 'module' => 'gif', + 'mime_type' => 'image/gif', + 'fail_id3' => 'ERROR', + 'fail_ape' => 'ERROR', + ), + + // JPEG - still image - Joint Photographic Experts Group (JPEG) + 'jpg' => array( + 'pattern' => '^\xFF\xD8\xFF', + 'group' => 'graphic', + 'module' => 'jpg', + 'mime_type' => 'image/jpeg', + 'fail_id3' => 'ERROR', + 'fail_ape' => 'ERROR', + ), + + // PCD - still image - Kodak Photo CD + 'pcd' => array( + 'pattern' => '^.{2048}PCD_IPI\x00', + 'group' => 'graphic', + 'module' => 'pcd', + 'mime_type' => 'image/x-photo-cd', + 'fail_id3' => 'ERROR', + 'fail_ape' => 'ERROR', + ), + + + // PNG - still image - Portable Network Graphics (PNG) + 'png' => array( + 'pattern' => '^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A', + 'group' => 'graphic', + 'module' => 'png', + 'mime_type' => 'image/png', + 'fail_id3' => 'ERROR', + 'fail_ape' => 'ERROR', + ), + + + // TIFF - still image - Tagged Information File Format (TIFF) + 'tiff' => array( + 'pattern' => '^(II\x2A\x00|MM\x00\x2A)', + 'group' => 'graphic', + 'module' => 'tiff', + 'mime_type' => 'image/tiff', + 'fail_id3' => 'ERROR', + 'fail_ape' => 'ERROR', + ), + + + // Data formats + + // ISO - data - International Standards Organization (ISO) CD-ROM Image + 'iso' => array( + 'pattern' => '^.{32769}CD001', + 'group' => 'misc', + 'module' => 'iso', + 'mime_type' => 'application/octet-stream', + 'fail_id3' => 'ERROR', + 'fail_ape' => 'ERROR', + 'iconv_req' => false, + ), + + // RAR - data - RAR compressed data + 'rar' => array( + 'pattern' => '^Rar\!', + 'group' => 'archive', + 'module' => 'rar', + 'mime_type' => 'application/octet-stream', + 'fail_id3' => 'ERROR', + 'fail_ape' => 'ERROR', + ), + + // SZIP - audio/data - SZIP compressed data + 'szip' => array( + 'pattern' => '^SZ\x0A\x04', + 'group' => 'archive', + 'module' => 'szip', + 'mime_type' => 'application/octet-stream', + 'fail_id3' => 'ERROR', + 'fail_ape' => 'ERROR', + ), + + // TAR - data - TAR compressed data + 'tar' => array( + 'pattern' => '^.{100}[0-9\x20]{7}\x00[0-9\x20]{7}\x00[0-9\x20]{7}\x00[0-9\x20\x00]{12}[0-9\x20\x00]{12}', + 'group' => 'archive', + 'module' => 'tar', + 'mime_type' => 'application/x-tar', + 'fail_id3' => 'ERROR', + 'fail_ape' => 'ERROR', + ), + + // GZIP - data - GZIP compressed data + 'gz' => array( + 'pattern' => '^\x1F\x8B\x08', + 'group' => 'archive', + 'module' => 'gzip', + 'mime_type' => 'application/x-gzip', + 'fail_id3' => 'ERROR', + 'fail_ape' => 'ERROR', + ), + + // ZIP - data - ZIP compressed data + 'zip' => array( + 'pattern' => '^PK\x03\x04', + 'group' => 'archive', + 'module' => 'zip', + 'mime_type' => 'application/zip', + 'fail_id3' => 'ERROR', + 'fail_ape' => 'ERROR', + ), + + + // Misc other formats + + // PDF - data - ZIP compressed data + 'pdf' => array( + 'pattern' => '^\x25PDF', + 'group' => 'misc', + 'module' => 'pdf', + 'mime_type' => 'application/pdf', + 'fail_id3' => 'ERROR', + 'fail_ape' => 'ERROR', + ), + + // MSOFFICE - data - ZIP compressed data + 'msoffice' => array( + 'pattern' => '^\xD0\xCF\x11\xE0', // D0CF11E == DOCFILE == Microsoft Office Document + 'group' => 'misc', + 'module' => 'msoffice', + 'mime_type' => 'application/octet-stream', + 'fail_id3' => 'ERROR', + 'fail_ape' => 'ERROR', + ), + ); + } + + return $format_info; + } + + + + function GetFileFormat(&$filedata, $filename='') { + // this function will determine the format of a file based on usually + // the first 2-4 bytes of the file (8 bytes for PNG, 16 bytes for JPG, + // and in the case of ISO CD image, 6 bytes offset 32kb from the start + // of the file). + + // Identify file format - loop through $format_info and detect with reg expr + foreach ($this->GetFileFormatArray() as $format_name => $info) { + // Using preg_match() instead of ereg() - much faster + // The /s switch on preg_match() forces preg_match() NOT to treat + // newline (0x0A) characters as special chars but do a binary match + if (preg_match('/'.$info['pattern'].'/s', $filedata)) { + $info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php'; + return $info; + } + } + + + if (preg_match('/\.mp[123a]$/i', $filename)) { + // Too many mp3 encoders on the market put gabage in front of mpeg files + // use assume format on these if format detection failed + $GetFileFormatArray = $this->GetFileFormatArray(); + $info = $GetFileFormatArray['mp3']; + $info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php'; + return $info; + } + + return false; + } + + + // converts array to $encoding charset from $this->encoding + function CharConvert(&$array, $encoding) { + + // identical encoding - end here + if ($encoding == $this->encoding) { + return; + } + + // loop thru array + foreach ($array as $key => $value) { + + // go recursive + if (is_array($value)) { + $this->CharConvert($array[$key], $encoding); + } + + // convert string + elseif (is_string($value)) { + $array[$key] = trim(getid3_lib::iconv_fallback($encoding, $this->encoding, $value)); + } + } + } + + + function HandleAllTags() { + + // key name => array (tag name, character encoding) + static $tags; + if (empty($tags)) { + $tags = array( + 'asf' => array('asf' , 'UTF-16LE'), + 'midi' => array('midi' , 'ISO-8859-1'), + 'nsv' => array('nsv' , 'ISO-8859-1'), + 'ogg' => array('vorbiscomment' , 'UTF-8'), + 'png' => array('png' , 'UTF-8'), + 'tiff' => array('tiff' , 'ISO-8859-1'), + 'quicktime' => array('quicktime' , 'ISO-8859-1'), + 'real' => array('real' , 'ISO-8859-1'), + 'vqf' => array('vqf' , 'ISO-8859-1'), + 'zip' => array('zip' , 'ISO-8859-1'), + 'riff' => array('riff' , 'ISO-8859-1'), + 'lyrics3' => array('lyrics3' , 'ISO-8859-1'), + 'id3v1' => array('id3v1' , $this->encoding_id3v1), + 'id3v2' => array('id3v2' , 'UTF-8'), // not according to the specs (every frame can have a different encoding), but getID3() force-converts all encodings to UTF-8 + 'ape' => array('ape' , 'UTF-8') + ); + } + + // loop thru comments array + foreach ($tags as $comment_name => $tagname_encoding_array) { + list($tag_name, $encoding) = $tagname_encoding_array; + + // fill in default encoding type if not already present + if (isset($this->info[$comment_name]) && !isset($this->info[$comment_name]['encoding'])) { + $this->info[$comment_name]['encoding'] = $encoding; + } + + // copy comments if key name set + if (!empty($this->info[$comment_name]['comments'])) { + + foreach ($this->info[$comment_name]['comments'] as $tag_key => $valuearray) { + foreach ($valuearray as $key => $value) { + if (strlen(trim($value)) > 0) { + $this->info['tags'][trim($tag_name)][trim($tag_key)][] = $value; // do not trim!! Unicode characters will get mangled if trailing nulls are removed! + } + } + } + + if (!isset($this->info['tags'][$tag_name])) { + // comments are set but contain nothing but empty strings, so skip + continue; + } + + if ($this->option_tags_html) { + foreach ($this->info['tags'][$tag_name] as $tag_key => $valuearray) { + foreach ($valuearray as $key => $value) { + if (is_string($value)) { + //$this->info['tags_html'][$tag_name][$tag_key][$key] = getid3_lib::MultiByteCharString2HTML($value, $encoding); + $this->info['tags_html'][$tag_name][$tag_key][$key] = str_replace('�', '', getid3_lib::MultiByteCharString2HTML($value, $encoding)); + } else { + $this->info['tags_html'][$tag_name][$tag_key][$key] = $value; + } + } + } + } + + $this->CharConvert($this->info['tags'][$tag_name], $encoding); // only copy gets converted! + } + + } + return true; + } + + + function getHashdata($algorithm) { + switch ($algorithm) { + case 'md5': + case 'sha1': + break; + + default: + return $this->error('bad algorithm "'.$algorithm.'" in getHashdata()'); + break; + } + + if ((@$this->info['fileformat'] == 'ogg') && (@$this->info['audio']['dataformat'] == 'vorbis')) { + + // We cannot get an identical md5_data value for Ogg files where the comments + // span more than 1 Ogg page (compared to the same audio data with smaller + // comments) using the normal getID3() method of MD5'ing the data between the + // end of the comments and the end of the file (minus any trailing tags), + // because the page sequence numbers of the pages that the audio data is on + // do not match. Under normal circumstances, where comments are smaller than + // the nominal 4-8kB page size, then this is not a problem, but if there are + // very large comments, the only way around it is to strip off the comment + // tags with vorbiscomment and MD5 that file. + // This procedure must be applied to ALL Ogg files, not just the ones with + // comments larger than 1 page, because the below method simply MD5's the + // whole file with the comments stripped, not just the portion after the + // comments block (which is the standard getID3() method. + + // The above-mentioned problem of comments spanning multiple pages and changing + // page sequence numbers likely happens for OggSpeex and OggFLAC as well, but + // currently vorbiscomment only works on OggVorbis files. + + if ((bool) ini_get('safe_mode')) { + + $this->info['warning'][] = 'Failed making system call to vorbiscomment.exe - '.$algorithm.'_data is incorrect - error returned: PHP running in Safe Mode (backtick operator not available)'; + $this->info[$algorithm.'_data'] = false; + + } else { + + // Prevent user from aborting script + $old_abort = ignore_user_abort(true); + + // Create empty file + $empty = tempnam('*', 'getID3'); + touch($empty); + + + // Use vorbiscomment to make temp file without comments + $temp = tempnam('*', 'getID3'); + $file = $this->info['filenamepath']; + + if (GETID3_OS_ISWINDOWS) { + + if (file_exists(GETID3_HELPERAPPSDIR.'vorbiscomment.exe')) { + + $commandline = '"'.GETID3_HELPERAPPSDIR.'vorbiscomment.exe" -w -c "'.$empty.'" "'.$file.'" "'.$temp.'"'; + $VorbisCommentError = `$commandline`; + + } else { + + $VorbisCommentError = 'vorbiscomment.exe not found in '.GETID3_HELPERAPPSDIR; + + } + + } else { + + $commandline = 'vorbiscomment -w -c "'.$empty.'" "'.$file.'" "'.$temp.'" 2>&1'; + $commandline = 'vorbiscomment -w -c '.escapeshellarg($empty).' '.escapeshellarg($file).' '.escapeshellarg($temp).' 2>&1'; + $VorbisCommentError = `$commandline`; + + } + + if (!empty($VorbisCommentError)) { + + $this->info['warning'][] = 'Failed making system call to vorbiscomment(.exe) - '.$algorithm.'_data will be incorrect. If vorbiscomment is unavailable, please download from http://www.vorbis.com/download.psp and put in the getID3() directory. Error returned: '.$VorbisCommentError; + $this->info[$algorithm.'_data'] = false; + + } else { + + // Get hash of newly created file + switch ($algorithm) { + case 'md5': + $this->info[$algorithm.'_data'] = getid3_lib::md5_file($temp); + break; + + case 'sha1': + $this->info[$algorithm.'_data'] = getid3_lib::sha1_file($temp); + break; + } + } + + // Clean up + unlink($empty); + unlink($temp); + + // Reset abort setting + ignore_user_abort($old_abort); + + } + + } else { + + if (!empty($this->info['avdataoffset']) || (isset($this->info['avdataend']) && ($this->info['avdataend'] < $this->info['filesize']))) { + + // get hash from part of file + $this->info[$algorithm.'_data'] = getid3_lib::hash_data($this->info['filenamepath'], $this->info['avdataoffset'], $this->info['avdataend'], $algorithm); + + } else { + + // get hash from whole file + switch ($algorithm) { + case 'md5': + $this->info[$algorithm.'_data'] = getid3_lib::md5_file($this->info['filenamepath']); + break; + + case 'sha1': + $this->info[$algorithm.'_data'] = getid3_lib::sha1_file($this->info['filenamepath']); + break; + } + } + + } + return true; + } + + + function ChannelsBitratePlaytimeCalculations() { + + // set channelmode on audio + if (@$this->info['audio']['channels'] == '1') { + $this->info['audio']['channelmode'] = 'mono'; + } elseif (@$this->info['audio']['channels'] == '2') { + $this->info['audio']['channelmode'] = 'stereo'; + } + + // Calculate combined bitrate - audio + video + $CombinedBitrate = 0; + $CombinedBitrate += (isset($this->info['audio']['bitrate']) ? $this->info['audio']['bitrate'] : 0); + $CombinedBitrate += (isset($this->info['video']['bitrate']) ? $this->info['video']['bitrate'] : 0); + if (($CombinedBitrate > 0) && empty($this->info['bitrate'])) { + $this->info['bitrate'] = $CombinedBitrate; + } + //if ((isset($this->info['video']) && !isset($this->info['video']['bitrate'])) || (isset($this->info['audio']) && !isset($this->info['audio']['bitrate']))) { + // // for example, VBR MPEG video files cannot determine video bitrate: + // // should not set overall bitrate and playtime from audio bitrate only + // unset($this->info['bitrate']); + //} + + if (!isset($this->info['playtime_seconds']) && !empty($this->info['bitrate'])) { + $this->info['playtime_seconds'] = (($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['bitrate']; + } + + // Set playtime string + if (!empty($this->info['playtime_seconds']) && empty($this->info['playtime_string'])) { + $this->info['playtime_string'] = getid3_lib::PlaytimeString($this->info['playtime_seconds']); + } + } + + + function CalculateCompressionRatioVideo() { + if (empty($this->info['video'])) { + return false; + } + if (empty($this->info['video']['resolution_x']) || empty($this->info['video']['resolution_y'])) { + return false; + } + if (empty($this->info['video']['bits_per_sample'])) { + return false; + } + + switch ($this->info['video']['dataformat']) { + case 'bmp': + case 'gif': + case 'jpeg': + case 'jpg': + case 'png': + case 'tiff': + $FrameRate = 1; + $PlaytimeSeconds = 1; + $BitrateCompressed = $this->info['filesize'] * 8; + break; + + default: + if (!empty($this->info['video']['frame_rate'])) { + $FrameRate = $this->info['video']['frame_rate']; + } else { + return false; + } + if (!empty($this->info['playtime_seconds'])) { + $PlaytimeSeconds = $this->info['playtime_seconds']; + } else { + return false; + } + if (!empty($this->info['video']['bitrate'])) { + $BitrateCompressed = $this->info['video']['bitrate']; + } else { + return false; + } + break; + } + $BitrateUncompressed = $this->info['video']['resolution_x'] * $this->info['video']['resolution_y'] * $this->info['video']['bits_per_sample'] * $FrameRate; + + $this->info['video']['compression_ratio'] = $BitrateCompressed / $BitrateUncompressed; + return true; + } + + + function CalculateCompressionRatioAudio() { + if (empty($this->info['audio']['bitrate']) || empty($this->info['audio']['channels']) || empty($this->info['audio']['sample_rate'])) { + return false; + } + $this->info['audio']['compression_ratio'] = $this->info['audio']['bitrate'] / ($this->info['audio']['channels'] * $this->info['audio']['sample_rate'] * (!empty($this->info['audio']['bits_per_sample']) ? $this->info['audio']['bits_per_sample'] : 16)); + + if (!empty($this->info['audio']['streams'])) { + foreach ($this->info['audio']['streams'] as $streamnumber => $streamdata) { + if (!empty($streamdata['bitrate']) && !empty($streamdata['channels']) && !empty($streamdata['sample_rate'])) { + $this->info['audio']['streams'][$streamnumber]['compression_ratio'] = $streamdata['bitrate'] / ($streamdata['channels'] * $streamdata['sample_rate'] * (!empty($streamdata['bits_per_sample']) ? $streamdata['bits_per_sample'] : 16)); + } + } + } + return true; + } + + + function CalculateReplayGain() { + if (isset($this->info['replay_gain'])) { + $this->info['replay_gain']['reference_volume'] = 89; + if (isset($this->info['replay_gain']['track']['adjustment'])) { + $this->info['replay_gain']['track']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['track']['adjustment']; + } + if (isset($this->info['replay_gain']['album']['adjustment'])) { + $this->info['replay_gain']['album']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['album']['adjustment']; + } + + if (isset($this->info['replay_gain']['track']['peak'])) { + $this->info['replay_gain']['track']['max_noclip_gain'] = 0 - getid3_lib::RGADamplitude2dB($this->info['replay_gain']['track']['peak']); + } + if (isset($this->info['replay_gain']['album']['peak'])) { + $this->info['replay_gain']['album']['max_noclip_gain'] = 0 - getid3_lib::RGADamplitude2dB($this->info['replay_gain']['album']['peak']); + } + } + return true; + } + + function ProcessAudioStreams() { + if (!empty($this->info['audio']['bitrate']) || !empty($this->info['audio']['channels']) || !empty($this->info['audio']['sample_rate'])) { + if (!isset($this->info['audio']['streams'])) { + foreach ($this->info['audio'] as $key => $value) { + if ($key != 'streams') { + $this->info['audio']['streams'][0][$key] = $value; + } + } + } + } + return true; + } + + function getid3_tempnam() { + return tempnam($this->tempdir, 'gI3'); + } + +} + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.archive.gzip.php b/includes/getid3/getid3/module.archive.gzip.php new file mode 100644 index 0000000..34a44b1 --- /dev/null +++ b/includes/getid3/getid3/module.archive.gzip.php @@ -0,0 +1,249 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.archive.gzip.php // +// written by Mike Mozolin // +// module for analyzing GZIP files // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + +class getid3_gzip { + + // public: Optional file list - disable for speed. + var $option_gzip_parse_contents = false; // decode gzipped files, if possible, and parse recursively (.tar.gz for example) + + function getid3_gzip(&$fd, &$ThisFileInfo) { + $ThisFileInfo['fileformat'] = 'gzip'; + + $start_length = 10; + $unpack_header = 'a1id1/a1id2/a1cmethod/a1flags/a4mtime/a1xflags/a1os'; + //+---+---+---+---+---+---+---+---+---+---+ + //|ID1|ID2|CM |FLG| MTIME |XFL|OS | + //+---+---+---+---+---+---+---+---+---+---+ + @fseek($fd, 0); + $buffer = @fread($fd, $ThisFileInfo['filesize']); + + $arr_members = explode("\x1F\x8B\x08", $buffer); + while (true) { + $is_wrong_members = false; + $num_members = intval(count($arr_members)); + for ($i = 0; $i < $num_members; $i++) { + if (strlen($arr_members[$i]) == 0) { + continue; + } + $buf = "\x1F\x8B\x08".$arr_members[$i]; + + $attr = unpack($unpack_header, substr($buf, 0, $start_length)); + if (!$this->get_os_type(ord($attr['os']))) { + // Merge member with previous if wrong OS type + $arr_members[$i - 1] .= $buf; + $arr_members[$i] = ''; + $is_wrong_members = true; + continue; + } + } + if (!$is_wrong_members) { + break; + } + } + + $ThisFileInfo['gzip']['files'] = array(); + + $fpointer = 0; + $idx = 0; + for ($i = 0; $i < $num_members; $i++) { + if (strlen($arr_members[$i]) == 0) { + continue; + } + $thisThisFileInfo = &$ThisFileInfo['gzip']['member_header'][++$idx]; + + $buff = "\x1F\x8B\x08".$arr_members[$i]; + + $attr = unpack($unpack_header, substr($buff, 0, $start_length)); + $thisThisFileInfo['filemtime'] = getid3_lib::LittleEndian2Int($attr['mtime']); + $thisThisFileInfo['raw']['id1'] = ord($attr['cmethod']); + $thisThisFileInfo['raw']['id2'] = ord($attr['cmethod']); + $thisThisFileInfo['raw']['cmethod'] = ord($attr['cmethod']); + $thisThisFileInfo['raw']['os'] = ord($attr['os']); + $thisThisFileInfo['raw']['xflags'] = ord($attr['xflags']); + $thisThisFileInfo['raw']['flags'] = ord($attr['flags']); + + $thisThisFileInfo['flags']['crc16'] = (bool) ($thisThisFileInfo['raw']['flags'] & 0x02); + $thisThisFileInfo['flags']['extra'] = (bool) ($thisThisFileInfo['raw']['flags'] & 0x04); + $thisThisFileInfo['flags']['filename'] = (bool) ($thisThisFileInfo['raw']['flags'] & 0x08); + $thisThisFileInfo['flags']['comment'] = (bool) ($thisThisFileInfo['raw']['flags'] & 0x10); + + $thisThisFileInfo['compression'] = $this->get_xflag_type($thisThisFileInfo['raw']['xflags']); + + $thisThisFileInfo['os'] = $this->get_os_type($thisThisFileInfo['raw']['os']); + if (!$thisThisFileInfo['os']) { + $ThisFileInfo['error'][] = 'Read error on gzip file'; + return false; + } + + $fpointer = 10; + $arr_xsubfield = array(); + // bit 2 - FLG.FEXTRA + //+---+---+=================================+ + //| XLEN |...XLEN bytes of "extra field"...| + //+---+---+=================================+ + if ($thisThisFileInfo['flags']['extra']) { + $w_xlen = substr($buff, $fpointer, 2); + $xlen = getid3_lib::LittleEndian2Int($w_xlen); + $fpointer += 2; + + $thisThisFileInfo['raw']['xfield'] = substr($buff, $fpointer, $xlen); + // Extra SubFields + //+---+---+---+---+==================================+ + //|SI1|SI2| LEN |... LEN bytes of subfield data ...| + //+---+---+---+---+==================================+ + $idx = 0; + while (true) { + if ($idx >= $xlen) { + break; + } + $si1 = ord(substr($buff, $fpointer + $idx++, 1)); + $si2 = ord(substr($buff, $fpointer + $idx++, 1)); + if (($si1 == 0x41) && ($si2 == 0x70)) { + $w_xsublen = substr($buff, $fpointer+$idx, 2); + $xsublen = getid3_lib::LittleEndian2Int($w_xsublen); + $idx += 2; + $arr_xsubfield[] = substr($buff, $fpointer+$idx, $xsublen); + $idx += $xsublen; + } else { + break; + } + } + $fpointer += $xlen; + } + // bit 3 - FLG.FNAME + //+=========================================+ + //|...original file name, zero-terminated...| + //+=========================================+ + // GZIP files may have only one file, with no filename, so assume original filename is current filename without .gz + $thisThisFileInfo['filename'] = eregi_replace('.gz$', '', $ThisFileInfo['filename']); + if ($thisThisFileInfo['flags']['filename']) { + while (true) { + if (ord($buff[$fpointer]) == 0) { + $fpointer++; + break; + } + $thisThisFileInfo['filename'] .= $buff[$fpointer]; + $fpointer++; + } + } + // bit 4 - FLG.FCOMMENT + //+===================================+ + //|...file comment, zero-terminated...| + //+===================================+ + if ($thisThisFileInfo['flags']['comment']) { + while (true) { + if (ord($buff[$fpointer]) == 0) { + $fpointer++; + break; + } + $thisThisFileInfo['comment'] .= $buff[$fpointer]; + $fpointer++; + } + } + // bit 1 - FLG.FHCRC + //+---+---+ + //| CRC16 | + //+---+---+ + if ($thisThisFileInfo['flags']['crc16']) { + $w_crc = substr($buff, $fpointer, 2); + $thisThisFileInfo['crc16'] = getid3_lib::LittleEndian2Int($w_crc); + $fpointer += 2; + } + // bit 0 - FLG.FTEXT + //if ($thisThisFileInfo['raw']['flags'] & 0x01) { + // Ignored... + //} + // bits 5, 6, 7 - reserved + + $thisThisFileInfo['crc32'] = getid3_lib::LittleEndian2Int(substr($buff, strlen($buff) - 8, 4)); + $thisThisFileInfo['filesize'] = getid3_lib::LittleEndian2Int(substr($buff, strlen($buff) - 4)); + + $ThisFileInfo['gzip']['files'] = getid3_lib::array_merge_clobber($ThisFileInfo['gzip']['files'], getid3_lib::CreateDeepArray($thisThisFileInfo['filename'], '/', $thisThisFileInfo['filesize'])); + + if ($this->option_gzip_parse_contents) { + // Try to inflate GZip + $csize = 0; + $inflated = ''; + $chkcrc32 = ''; + if (function_exists('gzinflate')) { + $cdata = substr($buff, $fpointer); + $cdata = substr($cdata, 0, strlen($cdata) - 8); + $csize = strlen($cdata); + $inflated = gzinflate($cdata); + + // Calculate CRC32 for inflated content + $thisThisFileInfo['crc32_valid'] = (bool) (sprintf('%u', crc32($inflated)) == $thisThisFileInfo['crc32']); + + // determine format + $formattest = substr($inflated, 0, 32774); + $newgetID3 = new getID3(); + $determined_format = $newgetID3->GetFileFormat($formattest); + unset($newgetID3); + + // file format is determined + switch (@$determined_format['module']) { + case 'tar': + // view TAR-file info + if (file_exists(GETID3_INCLUDEPATH.$determined_format['include']) && @include_once(GETID3_INCLUDEPATH.$determined_format['include'])) { + getid3_tar::read_tar($inflated, $ThisFileInfo['gzip']['member_header'][$idx]); + } + break; + + case '': + default: + // unknown or unhandled format + break; + } + } + } + } + return true; + } + + // Converts the OS type + function get_os_type($key) { + static $os_type = array( + '0' => 'FAT filesystem (MS-DOS, OS/2, NT/Win32)', + '1' => 'Amiga', + '2' => 'VMS (or OpenVMS)', + '3' => 'Unix', + '4' => 'VM/CMS', + '5' => 'Atari TOS', + '6' => 'HPFS filesystem (OS/2, NT)', + '7' => 'Macintosh', + '8' => 'Z-System', + '9' => 'CP/M', + '10' => 'TOPS-20', + '11' => 'NTFS filesystem (NT)', + '12' => 'QDOS', + '13' => 'Acorn RISCOS', + '255' => 'unknown' + ); + return @$os_type[$key]; + } + + // Converts the eXtra FLags + function get_xflag_type($key) { + static $xflag_type = array( + '0' => 'unknown', + '2' => 'maximum compression', + '4' => 'fastest algorithm' + ); + return @$xflag_type[$key]; + } +} + +?> diff --git a/includes/getid3/getid3/module.archive.rar.php b/includes/getid3/getid3/module.archive.rar.php new file mode 100644 index 0000000..f006ce4 --- /dev/null +++ b/includes/getid3/getid3/module.archive.rar.php @@ -0,0 +1,32 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.archive.rar.php // +// module for analyzing RAR files // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_rar +{ + + function getid3_rar(&$fd, &$ThisFileInfo) { + + $ThisFileInfo['fileformat'] = 'rar'; + + $ThisFileInfo['error'][] = 'RAR parsing not enabled in this version of getID3()'; + return false; + + } + +} + + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.archive.szip.php b/includes/getid3/getid3/module.archive.szip.php new file mode 100644 index 0000000..2513c85 --- /dev/null +++ b/includes/getid3/getid3/module.archive.szip.php @@ -0,0 +1,97 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.archive.szip.php // +// module for analyzing SZIP compressed files // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_szip +{ + + function getid3_szip(&$fd, &$ThisFileInfo) { + + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + $SZIPHeader = fread($fd, 6); + if (substr($SZIPHeader, 0, 4) != 'SZ'."\x0A\x04") { + $ThisFileInfo['error'][] = 'Expecting "SZ[x0A][x04]" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($SZIPHeader, 0, 4).'"'; + return false; + } + + $ThisFileInfo['fileformat'] = 'szip'; + + $ThisFileInfo['szip']['major_version'] = getid3_lib::BigEndian2Int(substr($SZIPHeader, 4, 1)); + $ThisFileInfo['szip']['minor_version'] = getid3_lib::BigEndian2Int(substr($SZIPHeader, 5, 1)); + + while (!feof($fd)) { + $NextBlockID = fread($fd, 2); + switch ($NextBlockID) { + case 'SZ': + // Note that szip files can be concatenated, this has the same effect as + // concatenating the files. this also means that global header blocks + // might be present between directory/data blocks. + fseek($fd, 4, SEEK_CUR); + break; + + case 'BH': + $BHheaderbytes = getid3_lib::BigEndian2Int(fread($fd, 3)); + $BHheaderdata = fread($fd, $BHheaderbytes); + $BHheaderoffset = 0; + while (strpos($BHheaderdata, "\x00", $BHheaderoffset) > 0) { + //filename as \0 terminated string (empty string indicates end) + //owner as \0 terminated string (empty is same as last file) + //group as \0 terminated string (empty is same as last file) + //3 byte filelength in this block + //2 byte access flags + //4 byte creation time (like in unix) + //4 byte modification time (like in unix) + //4 byte access time (like in unix) + + $BHdataArray['filename'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, "\x00")); + $BHheaderoffset += (strlen($BHdataArray['filename']) + 1); + + $BHdataArray['owner'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, "\x00")); + $BHheaderoffset += (strlen($BHdataArray['owner']) + 1); + + $BHdataArray['group'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, "\x00")); + $BHheaderoffset += (strlen($BHdataArray['group']) + 1); + + $BHdataArray['filelength'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 3)); + $BHheaderoffset += 3; + + $BHdataArray['access_flags'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 2)); + $BHheaderoffset += 2; + + $BHdataArray['creation_time'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4)); + $BHheaderoffset += 4; + + $BHdataArray['modification_time'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4)); + $BHheaderoffset += 4; + + $BHdataArray['access_time'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4)); + $BHheaderoffset += 4; + + $ThisFileInfo['szip']['BH'][] = $BHdataArray; + } + break; + + default: + break 2; + } + } + + return true; + + } + +} + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.archive.tar.php b/includes/getid3/getid3/module.archive.tar.php new file mode 100644 index 0000000..61aff40 --- /dev/null +++ b/includes/getid3/getid3/module.archive.tar.php @@ -0,0 +1,167 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.archive.tar.php // +// written by Mike Mozolin // +// module for analyzing TAR files // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + +class getid3_tar { + + function getid3_tar(&$fd, &$ThisFileInfo) { + $ThisFileInfo['fileformat'] = 'tar'; + $ThisFileInfo['tar']['files'] = array(); + + $unpack_header = 'a100fname/a8mode/a8uid/a8gid/a12size/a12mtime/a8chksum/a1typflag/a100lnkname/a6magic/a2ver/a32uname/a32gname/a8devmaj/a8devmin/a155/prefix'; + $null_512k = str_repeat("\0", 512); // end-of-file marker + + @fseek($fd, 0); + while (!feof($fd)) { + $buffer = fread($fd, 512); + + // check the block + $checksum = 0; + for ($i = 0; $i < 148; $i++) { + $checksum += ord($buffer{$i}); + } + for ($i = 148; $i < 156; $i++) { + $checksum += ord(' '); + } + for ($i = 156; $i < 512; $i++) { + $checksum += ord($buffer{$i}); + } + $attr = unpack($unpack_header, $buffer); + $name = trim(@$attr['fname']); + $mode = octdec(trim(@$attr['mode'])); + $uid = octdec(trim(@$attr['uid'])); + $gid = octdec(trim(@$attr['gid'])); + $size = octdec(trim(@$attr['size'])); + $mtime = octdec(trim(@$attr['mtime'])); + $chksum = octdec(trim(@$attr['chksum'])); + $typflag = trim(@$attr['typflag']); + $lnkname = trim(@$attr['lnkname']); + $magic = trim(@$attr['magic']); + $ver = trim(@$attr['ver']); + $uname = trim(@$attr['uname']); + $gname = trim(@$attr['gname']); + $devmaj = octdec(trim(@$attr['devmaj'])); + $devmin = octdec(trim(@$attr['devmin'])); + $prefix = trim(@$attr['prefix']); + if (($checksum == 256) && ($chksum == 0)) { + // EOF Found + break; + } + if ($prefix) { + $name = $prefix.'/'.$name; + } + if ((preg_match('#/$#', $name)) && !$name) { + $typeflag = 5; + } + if ($buffer == $null_512k) { + // it's the end of the tar-file... + break; + } + + // Read to the next chunk + fseek($fd, $size, SEEK_CUR); + + $diff = $size % 512; + if ($diff != 0) { + // Padding, throw away + fseek($fd, (512 - $diff), SEEK_CUR); + } + // Protect against tar-files with garbage at the end + if ($name == '') { + break; + } + $ThisFileInfo['tar']['file_details'][$name] = array ( + 'name' => $name, + 'mode_raw' => $mode, + 'mode' => getid3_tar::display_perms($mode), + 'uid' => $uid, + 'gid' => $gid, + 'size' => $size, + 'mtime' => $mtime, + 'chksum' => $chksum, + 'typeflag' => getid3_tar::get_flag_type($typflag), + 'linkname' => $lnkname, + 'magic' => $magic, + 'version' => $ver, + 'uname' => $uname, + 'gname' => $gname, + 'devmajor' => $devmaj, + 'devminor' => $devmin + ); + $ThisFileInfo['tar']['files'] = getid3_lib::array_merge_clobber($ThisFileInfo['tar']['files'], getid3_lib::CreateDeepArray($ThisFileInfo['tar']['file_details'][$name]['name'], '/', $size)); + } + return true; + } + + // Parses the file mode to file permissions + function display_perms($mode) { + // Determine Type + if ($mode & 0x1000) $type='p'; // FIFO pipe + elseif ($mode & 0x2000) $type='c'; // Character special + elseif ($mode & 0x4000) $type='d'; // Directory + elseif ($mode & 0x6000) $type='b'; // Block special + elseif ($mode & 0x8000) $type='-'; // Regular + elseif ($mode & 0xA000) $type='l'; // Symbolic Link + elseif ($mode & 0xC000) $type='s'; // Socket + else $type='u'; // UNKNOWN + + // Determine permissions + $owner['read'] = (($mode & 00400) ? 'r' : '-'); + $owner['write'] = (($mode & 00200) ? 'w' : '-'); + $owner['execute'] = (($mode & 00100) ? 'x' : '-'); + $group['read'] = (($mode & 00040) ? 'r' : '-'); + $group['write'] = (($mode & 00020) ? 'w' : '-'); + $group['execute'] = (($mode & 00010) ? 'x' : '-'); + $world['read'] = (($mode & 00004) ? 'r' : '-'); + $world['write'] = (($mode & 00002) ? 'w' : '-'); + $world['execute'] = (($mode & 00001) ? 'x' : '-'); + + // Adjust for SUID, SGID and sticky bit + if ($mode & 0x800) $owner['execute'] = ($owner['execute'] == 'x') ? 's' : 'S'; + if ($mode & 0x400) $group['execute'] = ($group['execute'] == 'x') ? 's' : 'S'; + if ($mode & 0x200) $world['execute'] = ($world['execute'] == 'x') ? 't' : 'T'; + + $s = sprintf('%1s', $type); + $s .= sprintf('%1s%1s%1s', $owner['read'], $owner['write'], $owner['execute']); + $s .= sprintf('%1s%1s%1s', $group['read'], $group['write'], $group['execute']); + $s .= sprintf('%1s%1s%1s'."\n", $world['read'], $world['write'], $world['execute']); + return $s; + } + + // Converts the file type + function get_flag_type($typflag) { + static $flag_types = array( + '0' => 'LF_NORMAL', + '1' => 'LF_LINK', + '2' => 'LF_SYNLINK', + '3' => 'LF_CHR', + '4' => 'LF_BLK', + '5' => 'LF_DIR', + '6' => 'LF_FIFO', + '7' => 'LF_CONFIG', + 'D' => 'LF_DUMPDIR', + 'K' => 'LF_LONGLINK', + 'L' => 'LF_LONGNAME', + 'M' => 'LF_MULTIVOL', + 'N' => 'LF_NAMES', + 'S' => 'LF_SPARSE', + 'V' => 'LF_VOLHDR' + ); + return @$flag_types[$typflag]; + } + +} + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.archive.zip.php b/includes/getid3/getid3/module.archive.zip.php new file mode 100644 index 0000000..da6fb72 --- /dev/null +++ b/includes/getid3/getid3/module.archive.zip.php @@ -0,0 +1,415 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.archive.zip.php // +// module for analyzing pkZip files // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_zip +{ + + function getid3_zip(&$fd, &$ThisFileInfo) { + + $ThisFileInfo['fileformat'] = 'zip'; + $ThisFileInfo['zip']['encoding'] = 'ISO-8859-1'; + $ThisFileInfo['zip']['files'] = array(); + + $ThisFileInfo['zip']['compressed_size'] = 0; + $ThisFileInfo['zip']['uncompressed_size'] = 0; + $ThisFileInfo['zip']['entries_count'] = 0; + + $EOCDsearchData = ''; + $EOCDsearchCounter = 0; + while ($EOCDsearchCounter++ < 512) { + + fseek($fd, -128 * $EOCDsearchCounter, SEEK_END); + $EOCDsearchData = fread($fd, 128).$EOCDsearchData; + + if (strstr($EOCDsearchData, 'PK'."\x05\x06")) { + + $EOCDposition = strpos($EOCDsearchData, 'PK'."\x05\x06"); + fseek($fd, (-128 * $EOCDsearchCounter) + $EOCDposition, SEEK_END); + $ThisFileInfo['zip']['end_central_directory'] = $this->ZIPparseEndOfCentralDirectory($fd); + + fseek($fd, $ThisFileInfo['zip']['end_central_directory']['directory_offset'], SEEK_SET); + $ThisFileInfo['zip']['entries_count'] = 0; + while ($centraldirectoryentry = $this->ZIPparseCentralDirectory($fd)) { + $ThisFileInfo['zip']['central_directory'][] = $centraldirectoryentry; + $ThisFileInfo['zip']['entries_count']++; + $ThisFileInfo['zip']['compressed_size'] += $centraldirectoryentry['compressed_size']; + $ThisFileInfo['zip']['uncompressed_size'] += $centraldirectoryentry['uncompressed_size']; + + if ($centraldirectoryentry['uncompressed_size'] > 0) { + $ThisFileInfo['zip']['files'] = getid3_lib::array_merge_clobber($ThisFileInfo['zip']['files'], getid3_lib::CreateDeepArray($centraldirectoryentry['filename'], '/', $centraldirectoryentry['uncompressed_size'])); + } + } + + if ($ThisFileInfo['zip']['entries_count'] == 0) { + $ThisFileInfo['error'][] = 'No Central Directory entries found (truncated file?)'; + return false; + } + + if (!empty($ThisFileInfo['zip']['end_central_directory']['comment'])) { + $ThisFileInfo['zip']['comments']['comment'][] = $ThisFileInfo['zip']['end_central_directory']['comment']; + } + + if (isset($ThisFileInfo['zip']['central_directory'][0]['compression_method'])) { + $ThisFileInfo['zip']['compression_method'] = $ThisFileInfo['zip']['central_directory'][0]['compression_method']; + } + if (isset($ThisFileInfo['zip']['central_directory'][0]['flags']['compression_speed'])) { + $ThisFileInfo['zip']['compression_speed'] = $ThisFileInfo['zip']['central_directory'][0]['flags']['compression_speed']; + } + if (isset($ThisFileInfo['zip']['compression_method']) && ($ThisFileInfo['zip']['compression_method'] == 'store') && !isset($ThisFileInfo['zip']['compression_speed'])) { + $ThisFileInfo['zip']['compression_speed'] = 'store'; + } + + return true; + + } + + } + + if ($this->getZIPentriesFilepointer($fd, $ThisFileInfo)) { + + // central directory couldn't be found and/or parsed + // scan through actual file data entries, recover as much as possible from probable trucated file + if ($ThisFileInfo['zip']['compressed_size'] > ($ThisFileInfo['filesize'] - 46 - 22)) { + $ThisFileInfo['error'][] = 'Warning: Truncated file! - Total compressed file sizes ('.$ThisFileInfo['zip']['compressed_size'].' bytes) is greater than filesize minus Central Directory and End Of Central Directory structures ('.($ThisFileInfo['filesize'] - 46 - 22).' bytes)'; + } + $ThisFileInfo['error'][] = 'Cannot find End Of Central Directory - returned list of files in [zip][entries] array may not be complete'; + foreach ($ThisFileInfo['zip']['entries'] as $key => $valuearray) { + $ThisFileInfo['zip']['files'][$valuearray['filename']] = $valuearray['uncompressed_size']; + } + return true; + + } else { + + unset($ThisFileInfo['zip']); + $ThisFileInfo['fileformat'] = ''; + $ThisFileInfo['error'][] = 'Cannot find End Of Central Directory (truncated file?)'; + return false; + + } + } + + + function getZIPHeaderFilepointerTopDown(&$fd, &$ThisFileInfo) { + $ThisFileInfo['fileformat'] = 'zip'; + + $ThisFileInfo['zip']['compressed_size'] = 0; + $ThisFileInfo['zip']['uncompressed_size'] = 0; + $ThisFileInfo['zip']['entries_count'] = 0; + + rewind($fd); + while ($fileentry = $this->ZIPparseLocalFileHeader($fd)) { + $ThisFileInfo['zip']['entries'][] = $fileentry; + $ThisFileInfo['zip']['entries_count']++; + } + if ($ThisFileInfo['zip']['entries_count'] == 0) { + $ThisFileInfo['error'][] = 'No Local File Header entries found'; + return false; + } + + $ThisFileInfo['zip']['entries_count'] = 0; + while ($centraldirectoryentry = $this->ZIPparseCentralDirectory($fd)) { + $ThisFileInfo['zip']['central_directory'][] = $centraldirectoryentry; + $ThisFileInfo['zip']['entries_count']++; + $ThisFileInfo['zip']['compressed_size'] += $centraldirectoryentry['compressed_size']; + $ThisFileInfo['zip']['uncompressed_size'] += $centraldirectoryentry['uncompressed_size']; + } + if ($ThisFileInfo['zip']['entries_count'] == 0) { + $ThisFileInfo['error'][] = 'No Central Directory entries found (truncated file?)'; + return false; + } + + if ($EOCD = $this->ZIPparseEndOfCentralDirectory($fd)) { + $ThisFileInfo['zip']['end_central_directory'] = $EOCD; + } else { + $ThisFileInfo['error'][] = 'No End Of Central Directory entry found (truncated file?)'; + return false; + } + + if (!empty($ThisFileInfo['zip']['end_central_directory']['comment'])) { + $ThisFileInfo['zip']['comments']['comment'][] = $ThisFileInfo['zip']['end_central_directory']['comment']; + } + + return true; + } + + + function getZIPentriesFilepointer(&$fd, &$ThisFileInfo) { + $ThisFileInfo['zip']['compressed_size'] = 0; + $ThisFileInfo['zip']['uncompressed_size'] = 0; + $ThisFileInfo['zip']['entries_count'] = 0; + + rewind($fd); + while ($fileentry = $this->ZIPparseLocalFileHeader($fd)) { + $ThisFileInfo['zip']['entries'][] = $fileentry; + $ThisFileInfo['zip']['entries_count']++; + $ThisFileInfo['zip']['compressed_size'] += $fileentry['compressed_size']; + $ThisFileInfo['zip']['uncompressed_size'] += $fileentry['uncompressed_size']; + } + if ($ThisFileInfo['zip']['entries_count'] == 0) { + $ThisFileInfo['error'][] = 'No Local File Header entries found'; + return false; + } + + return true; + } + + + function ZIPparseLocalFileHeader(&$fd) { + $LocalFileHeader['offset'] = ftell($fd); + + $ZIPlocalFileHeader = fread($fd, 30); + + $LocalFileHeader['raw']['signature'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 0, 4)); + if ($LocalFileHeader['raw']['signature'] != 0x04034B50) { + // invalid Local File Header Signature + fseek($fd, $LocalFileHeader['offset'], SEEK_SET); // seek back to where filepointer originally was so it can be handled properly + return false; + } + $LocalFileHeader['raw']['extract_version'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 4, 2)); + $LocalFileHeader['raw']['general_flags'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 6, 2)); + $LocalFileHeader['raw']['compression_method'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 8, 2)); + $LocalFileHeader['raw']['last_mod_file_time'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 10, 2)); + $LocalFileHeader['raw']['last_mod_file_date'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 12, 2)); + $LocalFileHeader['raw']['crc_32'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 14, 4)); + $LocalFileHeader['raw']['compressed_size'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 18, 4)); + $LocalFileHeader['raw']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 22, 4)); + $LocalFileHeader['raw']['filename_length'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 26, 2)); + $LocalFileHeader['raw']['extra_field_length'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 28, 2)); + + $LocalFileHeader['extract_version'] = sprintf('%1.1f', $LocalFileHeader['raw']['extract_version'] / 10); + $LocalFileHeader['host_os'] = $this->ZIPversionOSLookup(($LocalFileHeader['raw']['extract_version'] & 0xFF00) >> 8); + $LocalFileHeader['compression_method'] = $this->ZIPcompressionMethodLookup($LocalFileHeader['raw']['compression_method']); + $LocalFileHeader['compressed_size'] = $LocalFileHeader['raw']['compressed_size']; + $LocalFileHeader['uncompressed_size'] = $LocalFileHeader['raw']['uncompressed_size']; + $LocalFileHeader['flags'] = $this->ZIPparseGeneralPurposeFlags($LocalFileHeader['raw']['general_flags'], $LocalFileHeader['raw']['compression_method']); + $LocalFileHeader['last_modified_timestamp'] = $this->DOStime2UNIXtime($LocalFileHeader['raw']['last_mod_file_date'], $LocalFileHeader['raw']['last_mod_file_time']); + + $FilenameExtrafieldLength = $LocalFileHeader['raw']['filename_length'] + $LocalFileHeader['raw']['extra_field_length']; + if ($FilenameExtrafieldLength > 0) { + $ZIPlocalFileHeader .= fread($fd, $FilenameExtrafieldLength); + + if ($LocalFileHeader['raw']['filename_length'] > 0) { + $LocalFileHeader['filename'] = substr($ZIPlocalFileHeader, 30, $LocalFileHeader['raw']['filename_length']); + } + if ($LocalFileHeader['raw']['extra_field_length'] > 0) { + $LocalFileHeader['raw']['extra_field_data'] = substr($ZIPlocalFileHeader, 30 + $LocalFileHeader['raw']['filename_length'], $LocalFileHeader['raw']['extra_field_length']); + } + } + + $LocalFileHeader['data_offset'] = ftell($fd); + //$LocalFileHeader['compressed_data'] = fread($fd, $LocalFileHeader['raw']['compressed_size']); + fseek($fd, $LocalFileHeader['raw']['compressed_size'], SEEK_CUR); + + if ($LocalFileHeader['flags']['data_descriptor_used']) { + $DataDescriptor = fread($fd, 12); + $LocalFileHeader['data_descriptor']['crc_32'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 0, 4)); + $LocalFileHeader['data_descriptor']['compressed_size'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 4, 4)); + $LocalFileHeader['data_descriptor']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 8, 4)); + } + + return $LocalFileHeader; + } + + + function ZIPparseCentralDirectory(&$fd) { + $CentralDirectory['offset'] = ftell($fd); + + $ZIPcentralDirectory = fread($fd, 46); + + $CentralDirectory['raw']['signature'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 0, 4)); + if ($CentralDirectory['raw']['signature'] != 0x02014B50) { + // invalid Central Directory Signature + fseek($fd, $CentralDirectory['offset'], SEEK_SET); // seek back to where filepointer originally was so it can be handled properly + return false; + } + $CentralDirectory['raw']['create_version'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 4, 2)); + $CentralDirectory['raw']['extract_version'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 6, 2)); + $CentralDirectory['raw']['general_flags'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 8, 2)); + $CentralDirectory['raw']['compression_method'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 10, 2)); + $CentralDirectory['raw']['last_mod_file_time'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 12, 2)); + $CentralDirectory['raw']['last_mod_file_date'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 14, 2)); + $CentralDirectory['raw']['crc_32'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 16, 4)); + $CentralDirectory['raw']['compressed_size'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 20, 4)); + $CentralDirectory['raw']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 24, 4)); + $CentralDirectory['raw']['filename_length'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 28, 2)); + $CentralDirectory['raw']['extra_field_length'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 30, 2)); + $CentralDirectory['raw']['file_comment_length'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 32, 2)); + $CentralDirectory['raw']['disk_number_start'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 34, 2)); + $CentralDirectory['raw']['internal_file_attrib'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 36, 2)); + $CentralDirectory['raw']['external_file_attrib'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 38, 4)); + $CentralDirectory['raw']['local_header_offset'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 42, 4)); + + $CentralDirectory['entry_offset'] = $CentralDirectory['raw']['local_header_offset']; + $CentralDirectory['create_version'] = sprintf('%1.1f', $CentralDirectory['raw']['create_version'] / 10); + $CentralDirectory['extract_version'] = sprintf('%1.1f', $CentralDirectory['raw']['extract_version'] / 10); + $CentralDirectory['host_os'] = $this->ZIPversionOSLookup(($CentralDirectory['raw']['extract_version'] & 0xFF00) >> 8); + $CentralDirectory['compression_method'] = $this->ZIPcompressionMethodLookup($CentralDirectory['raw']['compression_method']); + $CentralDirectory['compressed_size'] = $CentralDirectory['raw']['compressed_size']; + $CentralDirectory['uncompressed_size'] = $CentralDirectory['raw']['uncompressed_size']; + $CentralDirectory['flags'] = $this->ZIPparseGeneralPurposeFlags($CentralDirectory['raw']['general_flags'], $CentralDirectory['raw']['compression_method']); + $CentralDirectory['last_modified_timestamp'] = $this->DOStime2UNIXtime($CentralDirectory['raw']['last_mod_file_date'], $CentralDirectory['raw']['last_mod_file_time']); + + $FilenameExtrafieldCommentLength = $CentralDirectory['raw']['filename_length'] + $CentralDirectory['raw']['extra_field_length'] + $CentralDirectory['raw']['file_comment_length']; + if ($FilenameExtrafieldCommentLength > 0) { + $FilenameExtrafieldComment = fread($fd, $FilenameExtrafieldCommentLength); + + if ($CentralDirectory['raw']['filename_length'] > 0) { + $CentralDirectory['filename'] = substr($FilenameExtrafieldComment, 0, $CentralDirectory['raw']['filename_length']); + } + if ($CentralDirectory['raw']['extra_field_length'] > 0) { + $CentralDirectory['raw']['extra_field_data'] = substr($FilenameExtrafieldComment, $CentralDirectory['raw']['filename_length'], $CentralDirectory['raw']['extra_field_length']); + } + if ($CentralDirectory['raw']['file_comment_length'] > 0) { + $CentralDirectory['file_comment'] = substr($FilenameExtrafieldComment, $CentralDirectory['raw']['filename_length'] + $CentralDirectory['raw']['extra_field_length'], $CentralDirectory['raw']['file_comment_length']); + } + } + + return $CentralDirectory; + } + + function ZIPparseEndOfCentralDirectory(&$fd) { + $EndOfCentralDirectory['offset'] = ftell($fd); + + $ZIPendOfCentralDirectory = fread($fd, 22); + + $EndOfCentralDirectory['signature'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 0, 4)); + if ($EndOfCentralDirectory['signature'] != 0x06054B50) { + // invalid End Of Central Directory Signature + fseek($fd, $EndOfCentralDirectory['offset'], SEEK_SET); // seek back to where filepointer originally was so it can be handled properly + return false; + } + $EndOfCentralDirectory['disk_number_current'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 4, 2)); + $EndOfCentralDirectory['disk_number_start_directory'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 6, 2)); + $EndOfCentralDirectory['directory_entries_this_disk'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 8, 2)); + $EndOfCentralDirectory['directory_entries_total'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 10, 2)); + $EndOfCentralDirectory['directory_size'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 12, 4)); + $EndOfCentralDirectory['directory_offset'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 16, 4)); + $EndOfCentralDirectory['comment_length'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 20, 2)); + + if ($EndOfCentralDirectory['comment_length'] > 0) { + $EndOfCentralDirectory['comment'] = fread($fd, $EndOfCentralDirectory['comment_length']); + } + + return $EndOfCentralDirectory; + } + + + function ZIPparseGeneralPurposeFlags($flagbytes, $compressionmethod) { + $ParsedFlags['encrypted'] = (bool) ($flagbytes & 0x0001); + + switch ($compressionmethod) { + case 6: + $ParsedFlags['dictionary_size'] = (($flagbytes & 0x0002) ? 8192 : 4096); + $ParsedFlags['shannon_fano_trees'] = (($flagbytes & 0x0004) ? 3 : 2); + break; + + case 8: + case 9: + switch (($flagbytes & 0x0006) >> 1) { + case 0: + $ParsedFlags['compression_speed'] = 'normal'; + break; + case 1: + $ParsedFlags['compression_speed'] = 'maximum'; + break; + case 2: + $ParsedFlags['compression_speed'] = 'fast'; + break; + case 3: + $ParsedFlags['compression_speed'] = 'superfast'; + break; + } + break; + } + $ParsedFlags['data_descriptor_used'] = (bool) ($flagbytes & 0x0008); + + return $ParsedFlags; + } + + + function ZIPversionOSLookup($index) { + static $ZIPversionOSLookup = array( + 0 => 'MS-DOS and OS/2 (FAT / VFAT / FAT32 file systems)', + 1 => 'Amiga', + 2 => 'OpenVMS', + 3 => 'Unix', + 4 => 'VM/CMS', + 5 => 'Atari ST', + 6 => 'OS/2 H.P.F.S.', + 7 => 'Macintosh', + 8 => 'Z-System', + 9 => 'CP/M', + 10 => 'Windows NTFS', + 11 => 'MVS', + 12 => 'VSE', + 13 => 'Acorn Risc', + 14 => 'VFAT', + 15 => 'Alternate MVS', + 16 => 'BeOS', + 17 => 'Tandem' + ); + + return (isset($ZIPversionOSLookup[$index]) ? $ZIPversionOSLookup[$index] : '[unknown]'); + } + + function ZIPcompressionMethodLookup($index) { + static $ZIPcompressionMethodLookup = array( + 0 => 'store', + 1 => 'shrink', + 2 => 'reduce-1', + 3 => 'reduce-2', + 4 => 'reduce-3', + 5 => 'reduce-4', + 6 => 'implode', + 7 => 'tokenize', + 8 => 'deflate', + 9 => 'deflate64', + 10 => 'PKWARE Date Compression Library Imploding' + ); + + return (isset($ZIPcompressionMethodLookup[$index]) ? $ZIPcompressionMethodLookup[$index] : '[unknown]'); + } + + function DOStime2UNIXtime($DOSdate, $DOStime) { + // wFatDate + // Specifies the MS-DOS date. The date is a packed 16-bit value with the following format: + // Bits Contents + // 0-4 Day of the month (1-31) + // 5-8 Month (1 = January, 2 = February, and so on) + // 9-15 Year offset from 1980 (add 1980 to get actual year) + + $UNIXday = ($DOSdate & 0x001F); + $UNIXmonth = (($DOSdate & 0x01E0) >> 5); + $UNIXyear = (($DOSdate & 0xFE00) >> 9) + 1980; + + // wFatTime + // Specifies the MS-DOS time. The time is a packed 16-bit value with the following format: + // Bits Contents + // 0-4 Second divided by 2 + // 5-10 Minute (0-59) + // 11-15 Hour (0-23 on a 24-hour clock) + + $UNIXsecond = ($DOStime & 0x001F) * 2; + $UNIXminute = (($DOStime & 0x07E0) >> 5); + $UNIXhour = (($DOStime & 0xF800) >> 11); + + return gmmktime($UNIXhour, $UNIXminute, $UNIXsecond, $UNIXmonth, $UNIXday, $UNIXyear); + } + +} + + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.audio-video.asf.php b/includes/getid3/getid3/module.audio-video.asf.php new file mode 100644 index 0000000..b13db54 --- /dev/null +++ b/includes/getid3/getid3/module.audio-video.asf.php @@ -0,0 +1,1635 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio-video.asf.php // +// module for analyzing ASF, WMA and WMV files // +// dependencies: module.audio-video.riff.php // +// /// +///////////////////////////////////////////////////////////////// + +getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true); + +$GUIDarray = getid3_asf::KnownGUIDs(); +foreach ($GUIDarray as $GUIDname => $hexstringvalue) { + // initialize all GUID constants + define($GUIDname, getid3_asf::GUIDtoBytestring($hexstringvalue)); +} + + + +class getid3_asf +{ + + function getid3_asf(&$fd, &$ThisFileInfo) { + + // Shortcuts + $thisfile_audio = &$ThisFileInfo['audio']; + $thisfile_video = &$ThisFileInfo['video']; + $ThisFileInfo['asf'] = array(); + $thisfile_asf = &$ThisFileInfo['asf']; + $thisfile_asf['comments'] = array(); + $thisfile_asf_comments = &$thisfile_asf['comments']; + $thisfile_asf['header_object'] = array(); + $thisfile_asf_headerobject = &$thisfile_asf['header_object']; + + + // ASF structure: + // * Header Object [required] + // * File Properties Object [required] (global file attributes) + // * Stream Properties Object [required] (defines media stream & characteristics) + // * Header Extension Object [required] (additional functionality) + // * Content Description Object (bibliographic information) + // * Script Command Object (commands for during playback) + // * Marker Object (named jumped points within the file) + // * Data Object [required] + // * Data Packets + // * Index Object + + // Header Object: (mandatory, one only) + // Field Name Field Type Size (bits) + // Object ID GUID 128 // GUID for header object - GETID3_ASF_Header_Object + // Object Size QWORD 64 // size of header object, including 30 bytes of Header Object header + // Number of Header Objects DWORD 32 // number of objects in header object + // Reserved1 BYTE 8 // hardcoded: 0x01 + // Reserved2 BYTE 8 // hardcoded: 0x02 + + $ThisFileInfo['fileformat'] = 'asf'; + + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + $HeaderObjectData = fread($fd, 30); + + $thisfile_asf_headerobject['objectid'] = substr($HeaderObjectData, 0, 16); + $thisfile_asf_headerobject['objectid_guid'] = $this->BytestringToGUID($thisfile_asf_headerobject['objectid']); + if ($thisfile_asf_headerobject['objectid'] != GETID3_ASF_Header_Object) { + $ThisFileInfo['warning'][] = 'ASF header GUID {'.$this->BytestringToGUID($thisfile_asf_headerobject['objectid']).'} does not match expected "GETID3_ASF_Header_Object" GUID {'.$this->BytestringToGUID(GETID3_ASF_Header_Object).'}'; + unset($ThisFileInfo['fileformat']); + unset($ThisFileInfo['asf']); + return false; + break; + } + $thisfile_asf_headerobject['objectsize'] = getid3_lib::LittleEndian2Int(substr($HeaderObjectData, 16, 8)); + $thisfile_asf_headerobject['headerobjects'] = getid3_lib::LittleEndian2Int(substr($HeaderObjectData, 24, 4)); + $thisfile_asf_headerobject['reserved1'] = getid3_lib::LittleEndian2Int(substr($HeaderObjectData, 28, 1)); + $thisfile_asf_headerobject['reserved2'] = getid3_lib::LittleEndian2Int(substr($HeaderObjectData, 29, 1)); + + //$ASFHeaderData = $HeaderObjectData; + $ASFHeaderData = fread($fd, $thisfile_asf_headerobject['objectsize'] - 30); + //$offset = 30; + $offset = 0; + + for ($HeaderObjectsCounter = 0; $HeaderObjectsCounter < $thisfile_asf_headerobject['headerobjects']; $HeaderObjectsCounter++) { + $NextObjectGUID = substr($ASFHeaderData, $offset, 16); + $offset += 16; + $NextObjectGUIDtext = $this->BytestringToGUID($NextObjectGUID); + $NextObjectSize = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8)); + $offset += 8; + switch ($NextObjectGUID) { + + case GETID3_ASF_File_Properties_Object: + // File Properties Object: (mandatory, one only) + // Field Name Field Type Size (bits) + // Object ID GUID 128 // GUID for file properties object - GETID3_ASF_File_Properties_Object + // Object Size QWORD 64 // size of file properties object, including 104 bytes of File Properties Object header + // File ID GUID 128 // unique ID - identical to File ID in Data Object + // File Size QWORD 64 // entire file in bytes. Invalid if Broadcast Flag == 1 + // Creation Date QWORD 64 // date & time of file creation. Maybe invalid if Broadcast Flag == 1 + // Data Packets Count QWORD 64 // number of data packets in Data Object. Invalid if Broadcast Flag == 1 + // Play Duration QWORD 64 // playtime, in 100-nanosecond units. Invalid if Broadcast Flag == 1 + // Send Duration QWORD 64 // time needed to send file, in 100-nanosecond units. Players can ignore this value. Invalid if Broadcast Flag == 1 + // Preroll QWORD 64 // time to buffer data before starting to play file, in 1-millisecond units. If <> 0, PlayDuration and PresentationTime have been offset by this amount + // Flags DWORD 32 // + // * Broadcast Flag bits 1 (0x01) // file is currently being written, some header values are invalid + // * Seekable Flag bits 1 (0x02) // is file seekable + // * Reserved bits 30 (0xFFFFFFFC) // reserved - set to zero + // Minimum Data Packet Size DWORD 32 // in bytes. should be same as Maximum Data Packet Size. Invalid if Broadcast Flag == 1 + // Maximum Data Packet Size DWORD 32 // in bytes. should be same as Minimum Data Packet Size. Invalid if Broadcast Flag == 1 + // Maximum Bitrate DWORD 32 // maximum instantaneous bitrate in bits per second for entire file, including all data streams and ASF overhead + + // shortcut + $thisfile_asf['file_properties_object'] = array(); + $thisfile_asf_filepropertiesobject = &$thisfile_asf['file_properties_object']; + + $thisfile_asf_filepropertiesobject['objectid'] = $NextObjectGUID; + $thisfile_asf_filepropertiesobject['objectid_guid'] = $NextObjectGUIDtext; + $thisfile_asf_filepropertiesobject['objectsize'] = $NextObjectSize; + $thisfile_asf_filepropertiesobject['fileid'] = substr($ASFHeaderData, $offset, 16); + $offset += 16; + $thisfile_asf_filepropertiesobject['fileid_guid'] = $this->BytestringToGUID($thisfile_asf_filepropertiesobject['fileid']); + $thisfile_asf_filepropertiesobject['filesize'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8)); + $offset += 8; + $thisfile_asf_filepropertiesobject['creation_date'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8)); + $thisfile_asf_filepropertiesobject['creation_date_unix'] = $this->FILETIMEtoUNIXtime($thisfile_asf_filepropertiesobject['creation_date']); + $offset += 8; + $thisfile_asf_filepropertiesobject['data_packets'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8)); + $offset += 8; + $thisfile_asf_filepropertiesobject['play_duration'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8)); + $offset += 8; + $thisfile_asf_filepropertiesobject['send_duration'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8)); + $offset += 8; + $thisfile_asf_filepropertiesobject['preroll'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8)); + $offset += 8; + $ThisFileInfo['playtime_seconds'] = ($thisfile_asf_filepropertiesobject['play_duration'] / 10000000) - ($thisfile_asf_filepropertiesobject['preroll'] / 1000); + $thisfile_asf_filepropertiesobject['flags_raw'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); + $offset += 4; + $thisfile_asf_filepropertiesobject['flags']['broadcast'] = (bool) ($thisfile_asf_filepropertiesobject['flags_raw'] & 0x0001); + $thisfile_asf_filepropertiesobject['flags']['seekable'] = (bool) ($thisfile_asf_filepropertiesobject['flags_raw'] & 0x0002); + + $thisfile_asf_filepropertiesobject['min_packet_size'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); + $offset += 4; + $thisfile_asf_filepropertiesobject['max_packet_size'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); + $offset += 4; + $thisfile_asf_filepropertiesobject['max_bitrate'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); + $offset += 4; + //$ThisFileInfo['bitrate'] = $thisfile_asf_filepropertiesobject['max_bitrate']; + $ThisFileInfo['bitrate'] = ($thisfile_asf_filepropertiesobject['filesize'] * 8) / $ThisFileInfo['playtime_seconds']; + break; + + case GETID3_ASF_Stream_Properties_Object: + // Stream Properties Object: (mandatory, one per media stream) + // Field Name Field Type Size (bits) + // Object ID GUID 128 // GUID for stream properties object - GETID3_ASF_Stream_Properties_Object + // Object Size QWORD 64 // size of stream properties object, including 78 bytes of Stream Properties Object header + // Stream Type GUID 128 // GETID3_ASF_Audio_Media, GETID3_ASF_Video_Media or GETID3_ASF_Command_Media + // Error Correction Type GUID 128 // GETID3_ASF_Audio_Spread for audio-only streams, GETID3_ASF_No_Error_Correction for other stream types + // Time Offset QWORD 64 // 100-nanosecond units. typically zero. added to all timestamps of samples in the stream + // Type-Specific Data Length DWORD 32 // number of bytes for Type-Specific Data field + // Error Correction Data Length DWORD 32 // number of bytes for Error Correction Data field + // Flags WORD 16 // + // * Stream Number bits 7 (0x007F) // number of this stream. 1 <= valid <= 127 + // * Reserved bits 8 (0x7F80) // reserved - set to zero + // * Encrypted Content Flag bits 1 (0x8000) // stream contents encrypted if set + // Reserved DWORD 32 // reserved - set to zero + // Type-Specific Data BYTESTREAM variable // type-specific format data, depending on value of Stream Type + // Error Correction Data BYTESTREAM variable // error-correction-specific format data, depending on value of Error Correct Type + + // There is one GETID3_ASF_Stream_Properties_Object for each stream (audio, video) but the + // stream number isn't known until halfway through decoding the structure, hence it + // it is decoded to a temporary variable and then stuck in the appropriate index later + + $StreamPropertiesObjectData['objectid'] = $NextObjectGUID; + $StreamPropertiesObjectData['objectid_guid'] = $NextObjectGUIDtext; + $StreamPropertiesObjectData['objectsize'] = $NextObjectSize; + $StreamPropertiesObjectData['stream_type'] = substr($ASFHeaderData, $offset, 16); + $offset += 16; + $StreamPropertiesObjectData['stream_type_guid'] = $this->BytestringToGUID($StreamPropertiesObjectData['stream_type']); + $StreamPropertiesObjectData['error_correct_type'] = substr($ASFHeaderData, $offset, 16); + $offset += 16; + $StreamPropertiesObjectData['error_correct_guid'] = $this->BytestringToGUID($StreamPropertiesObjectData['error_correct_type']); + $StreamPropertiesObjectData['time_offset'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8)); + $offset += 8; + $StreamPropertiesObjectData['type_data_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); + $offset += 4; + $StreamPropertiesObjectData['error_data_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); + $offset += 4; + $StreamPropertiesObjectData['flags_raw'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + $StreamPropertiesObjectStreamNumber = $StreamPropertiesObjectData['flags_raw'] & 0x007F; + $StreamPropertiesObjectData['flags']['encrypted'] = (bool) ($StreamPropertiesObjectData['flags_raw'] & 0x8000); + + $offset += 4; // reserved - DWORD + $StreamPropertiesObjectData['type_specific_data'] = substr($ASFHeaderData, $offset, $StreamPropertiesObjectData['type_data_length']); + $offset += $StreamPropertiesObjectData['type_data_length']; + $StreamPropertiesObjectData['error_correct_data'] = substr($ASFHeaderData, $offset, $StreamPropertiesObjectData['error_data_length']); + $offset += $StreamPropertiesObjectData['error_data_length']; + + switch ($StreamPropertiesObjectData['stream_type']) { + + case GETID3_ASF_Audio_Media: + $thisfile_audio['dataformat'] = (!empty($thisfile_audio['dataformat']) ? $thisfile_audio['dataformat'] : 'asf'); + $thisfile_audio['bitrate_mode'] = (!empty($thisfile_audio['bitrate_mode']) ? $thisfile_audio['bitrate_mode'] : 'cbr'); + + $audiodata = getid3_riff::RIFFparseWAVEFORMATex(substr($StreamPropertiesObjectData['type_specific_data'], 0, 16)); + unset($audiodata['raw']); + $thisfile_audio = getid3_lib::array_merge_noclobber($audiodata, $thisfile_audio); + break; + + case GETID3_ASF_Video_Media: + $thisfile_video['dataformat'] = (!empty($thisfile_video['dataformat']) ? $thisfile_video['dataformat'] : 'asf'); + $thisfile_video['bitrate_mode'] = (!empty($thisfile_video['bitrate_mode']) ? $thisfile_video['bitrate_mode'] : 'cbr'); + break; + + case GETID3_ASF_Command_Media: + default: + // do nothing + break; + + } + + $thisfile_asf['stream_properties_object'][$StreamPropertiesObjectStreamNumber] = $StreamPropertiesObjectData; + unset($StreamPropertiesObjectData); // clear for next stream, if any + break; + + case GETID3_ASF_Header_Extension_Object: + // Header Extension Object: (mandatory, one only) + // Field Name Field Type Size (bits) + // Object ID GUID 128 // GUID for Header Extension object - GETID3_ASF_Header_Extension_Object + // Object Size QWORD 64 // size of Header Extension object, including 46 bytes of Header Extension Object header + // Reserved Field 1 GUID 128 // hardcoded: GETID3_ASF_Reserved_1 + // Reserved Field 2 WORD 16 // hardcoded: 0x00000006 + // Header Extension Data Size DWORD 32 // in bytes. valid: 0, or > 24. equals object size minus 46 + // Header Extension Data BYTESTREAM variable // array of zero or more extended header objects + + // shortcut + $thisfile_asf['header_extension_object'] = array(); + $thisfile_asf_headerextensionobject = &$thisfile_asf['header_extension_object']; + + $thisfile_asf_headerextensionobject['objectid'] = $NextObjectGUID; + $thisfile_asf_headerextensionobject['objectid_guid'] = $NextObjectGUIDtext; + $thisfile_asf_headerextensionobject['objectsize'] = $NextObjectSize; + $thisfile_asf_headerextensionobject['reserved_1'] = substr($ASFHeaderData, $offset, 16); + $offset += 16; + $thisfile_asf_headerextensionobject['reserved_1_guid'] = $this->BytestringToGUID($thisfile_asf_headerextensionobject['reserved_1']); + if ($thisfile_asf_headerextensionobject['reserved_1'] != GETID3_ASF_Reserved_1) { + $ThisFileInfo['warning'][] = 'header_extension_object.reserved_1 GUID ('.$this->BytestringToGUID($thisfile_asf_headerextensionobject['reserved_1']).') does not match expected "GETID3_ASF_Reserved_1" GUID ('.$this->BytestringToGUID(GETID3_ASF_Reserved_1).')'; + //return false; + break; + } + $thisfile_asf_headerextensionobject['reserved_2'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + if ($thisfile_asf_headerextensionobject['reserved_2'] != 6) { + $ThisFileInfo['warning'][] = 'header_extension_object.reserved_2 ('.getid3_lib::PrintHexBytes($thisfile_asf_headerextensionobject['reserved_2']).') does not match expected value of "6"'; + //return false; + break; + } + $thisfile_asf_headerextensionobject['extension_data_size'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); + $offset += 4; + $thisfile_asf_headerextensionobject['extension_data'] = substr($ASFHeaderData, $offset, $thisfile_asf_headerextensionobject['extension_data_size']); + $offset += $thisfile_asf_headerextensionobject['extension_data_size']; + break; + + case GETID3_ASF_Codec_List_Object: + // Codec List Object: (optional, one only) + // Field Name Field Type Size (bits) + // Object ID GUID 128 // GUID for Codec List object - GETID3_ASF_Codec_List_Object + // Object Size QWORD 64 // size of Codec List object, including 44 bytes of Codec List Object header + // Reserved GUID 128 // hardcoded: 86D15241-311D-11D0-A3A4-00A0C90348F6 + // Codec Entries Count DWORD 32 // number of entries in Codec Entries array + // Codec Entries array of: variable // + // * Type WORD 16 // 0x0001 = Video Codec, 0x0002 = Audio Codec, 0xFFFF = Unknown Codec + // * Codec Name Length WORD 16 // number of Unicode characters stored in the Codec Name field + // * Codec Name WCHAR variable // array of Unicode characters - name of codec used to create the content + // * Codec Description Length WORD 16 // number of Unicode characters stored in the Codec Description field + // * Codec Description WCHAR variable // array of Unicode characters - description of format used to create the content + // * Codec Information Length WORD 16 // number of Unicode characters stored in the Codec Information field + // * Codec Information BYTESTREAM variable // opaque array of information bytes about the codec used to create the content + + // shortcut + $thisfile_asf['codec_list_object'] = array(); + $thisfile_asf_codeclistobject = &$thisfile_asf['codec_list_object']; + + $thisfile_asf_codeclistobject['objectid'] = $NextObjectGUID; + $thisfile_asf_codeclistobject['objectid_guid'] = $NextObjectGUIDtext; + $thisfile_asf_codeclistobject['objectsize'] = $NextObjectSize; + $thisfile_asf_codeclistobject['reserved'] = substr($ASFHeaderData, $offset, 16); + $offset += 16; + $thisfile_asf_codeclistobject['reserved_guid'] = $this->BytestringToGUID($thisfile_asf_codeclistobject['reserved']); + if ($thisfile_asf_codeclistobject['reserved'] != $this->GUIDtoBytestring('86D15241-311D-11D0-A3A4-00A0C90348F6')) { + $ThisFileInfo['warning'][] = 'codec_list_object.reserved GUID {'.$this->BytestringToGUID($thisfile_asf_codeclistobject['reserved']).'} does not match expected "GETID3_ASF_Reserved_1" GUID {86D15241-311D-11D0-A3A4-00A0C90348F6}'; + //return false; + break; + } + $thisfile_asf_codeclistobject['codec_entries_count'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); + $offset += 4; + for ($CodecEntryCounter = 0; $CodecEntryCounter < $thisfile_asf_codeclistobject['codec_entries_count']; $CodecEntryCounter++) { + // shortcut + $thisfile_asf_codeclistobject['codec_entries'][$CodecEntryCounter] = array(); + $thisfile_asf_codeclistobject_codecentries_current = &$thisfile_asf_codeclistobject['codec_entries'][$CodecEntryCounter]; + + $thisfile_asf_codeclistobject_codecentries_current['type_raw'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + $thisfile_asf_codeclistobject_codecentries_current['type'] = $this->ASFCodecListObjectTypeLookup($thisfile_asf_codeclistobject_codecentries_current['type_raw']); + + $CodecNameLength = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)) * 2; // 2 bytes per character + $offset += 2; + $thisfile_asf_codeclistobject_codecentries_current['name'] = substr($ASFHeaderData, $offset, $CodecNameLength); + $offset += $CodecNameLength; + + $CodecDescriptionLength = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)) * 2; // 2 bytes per character + $offset += 2; + $thisfile_asf_codeclistobject_codecentries_current['description'] = substr($ASFHeaderData, $offset, $CodecDescriptionLength); + $offset += $CodecDescriptionLength; + + $CodecInformationLength = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + $thisfile_asf_codeclistobject_codecentries_current['information'] = substr($ASFHeaderData, $offset, $CodecInformationLength); + $offset += $CodecInformationLength; + + if ($thisfile_asf_codeclistobject_codecentries_current['type_raw'] == 2) { + // audio codec + if (strpos($thisfile_asf_codeclistobject_codecentries_current['description'], ',') === false) { + $ThisFileInfo['error'][] = '[asf][codec_list_object][codec_entries]['.$CodecEntryCounter.'][description] expected to contain comma-seperated list of parameters: "'.$thisfile_asf_codeclistobject_codecentries_current['description'].'"'; + return false; + } + list($AudioCodecBitrate, $AudioCodecFrequency, $AudioCodecChannels) = explode(',', $this->TrimConvert($thisfile_asf_codeclistobject_codecentries_current['description'])); + $thisfile_audio['codec'] = $this->TrimConvert($thisfile_asf_codeclistobject_codecentries_current['name']); + + if (!isset($thisfile_audio['bitrate']) && strstr($AudioCodecBitrate, 'kbps')) { + $thisfile_audio['bitrate'] = (int) (trim(str_replace('kbps', '', $AudioCodecBitrate)) * 1000); + } + //if (!isset($thisfile_video['bitrate']) && isset($thisfile_audio['bitrate']) && isset($thisfile_asf['file_properties_object']['max_bitrate']) && ($thisfile_asf_codeclistobject['codec_entries_count'] > 1)) { + if (!@$thisfile_video['bitrate'] && @$thisfile_audio['bitrate'] && @$ThisFileInfo['bitrate']) { + //$thisfile_video['bitrate'] = $thisfile_asf['file_properties_object']['max_bitrate'] - $thisfile_audio['bitrate']; + $thisfile_video['bitrate'] = $ThisFileInfo['bitrate'] - $thisfile_audio['bitrate']; + } + + $AudioCodecFrequency = (int) trim(str_replace('kHz', '', $AudioCodecFrequency)); + switch ($AudioCodecFrequency) { + case 8: + case 8000: + $thisfile_audio['sample_rate'] = 8000; + break; + + case 11: + case 11025: + $thisfile_audio['sample_rate'] = 11025; + break; + + case 12: + case 12000: + $thisfile_audio['sample_rate'] = 12000; + break; + + case 16: + case 16000: + $thisfile_audio['sample_rate'] = 16000; + break; + + case 22: + case 22050: + $thisfile_audio['sample_rate'] = 22050; + break; + + case 24: + case 24000: + $thisfile_audio['sample_rate'] = 24000; + break; + + case 32: + case 32000: + $thisfile_audio['sample_rate'] = 32000; + break; + + case 44: + case 441000: + $thisfile_audio['sample_rate'] = 44100; + break; + + case 48: + case 48000: + $thisfile_audio['sample_rate'] = 48000; + break; + + default: + $ThisFileInfo['warning'][] = 'unknown frequency: "'.$AudioCodecFrequency.'" ('.$this->TrimConvert($thisfile_asf_codeclistobject_codecentries_current['description']).')'; + // return false; + break; + } + + if (!isset($thisfile_audio['channels'])) { + if (strstr($AudioCodecChannels, 'stereo')) { + $thisfile_audio['channels'] = 2; + } elseif (strstr($AudioCodecChannels, 'mono')) { + $thisfile_audio['channels'] = 1; + } + } + } + } + break; + + case GETID3_ASF_Script_Command_Object: + // Script Command Object: (optional, one only) + // Field Name Field Type Size (bits) + // Object ID GUID 128 // GUID for Script Command object - GETID3_ASF_Script_Command_Object + // Object Size QWORD 64 // size of Script Command object, including 44 bytes of Script Command Object header + // Reserved GUID 128 // hardcoded: 4B1ACBE3-100B-11D0-A39B-00A0C90348F6 + // Commands Count WORD 16 // number of Commands structures in the Script Commands Objects + // Command Types Count WORD 16 // number of Command Types structures in the Script Commands Objects + // Command Types array of: variable // + // * Command Type Name Length WORD 16 // number of Unicode characters for Command Type Name + // * Command Type Name WCHAR variable // array of Unicode characters - name of a type of command + // Commands array of: variable // + // * Presentation Time DWORD 32 // presentation time of that command, in milliseconds + // * Type Index WORD 16 // type of this command, as a zero-based index into the array of Command Types of this object + // * Command Name Length WORD 16 // number of Unicode characters for Command Name + // * Command Name WCHAR variable // array of Unicode characters - name of this command + + // shortcut + $thisfile_asf['script_command_object'] = array(); + $thisfile_asf_scriptcommandobject = &$thisfile_asf['script_command_object']; + + $thisfile_asf_scriptcommandobject['objectid'] = $NextObjectGUID; + $thisfile_asf_scriptcommandobject['objectid_guid'] = $NextObjectGUIDtext; + $thisfile_asf_scriptcommandobject['objectsize'] = $NextObjectSize; + $thisfile_asf_scriptcommandobject['reserved'] = substr($ASFHeaderData, $offset, 16); + $offset += 16; + $thisfile_asf_scriptcommandobject['reserved_guid'] = $this->BytestringToGUID($thisfile_asf_scriptcommandobject['reserved']); + if ($thisfile_asf_scriptcommandobject['reserved'] != $this->GUIDtoBytestring('4B1ACBE3-100B-11D0-A39B-00A0C90348F6')) { + $ThisFileInfo['warning'][] = 'script_command_object.reserved GUID {'.$this->BytestringToGUID($thisfile_asf_scriptcommandobject['reserved']).'} does not match expected "GETID3_ASF_Reserved_1" GUID {4B1ACBE3-100B-11D0-A39B-00A0C90348F6}'; + //return false; + break; + } + $thisfile_asf_scriptcommandobject['commands_count'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + $thisfile_asf_scriptcommandobject['command_types_count'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + for ($CommandTypesCounter = 0; $CommandTypesCounter < $thisfile_asf_scriptcommandobject['command_types_count']; $CommandTypesCounter++) { + $CommandTypeNameLength = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)) * 2; // 2 bytes per character + $offset += 2; + $thisfile_asf_scriptcommandobject['command_types'][$CommandTypesCounter]['name'] = substr($ASFHeaderData, $offset, $CommandTypeNameLength); + $offset += $CommandTypeNameLength; + } + for ($CommandsCounter = 0; $CommandsCounter < $thisfile_asf_scriptcommandobject['commands_count']; $CommandsCounter++) { + $thisfile_asf_scriptcommandobject['commands'][$CommandsCounter]['presentation_time'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); + $offset += 4; + $thisfile_asf_scriptcommandobject['commands'][$CommandsCounter]['type_index'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + + $CommandTypeNameLength = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)) * 2; // 2 bytes per character + $offset += 2; + $thisfile_asf_scriptcommandobject['commands'][$CommandsCounter]['name'] = substr($ASFHeaderData, $offset, $CommandTypeNameLength); + $offset += $CommandTypeNameLength; + } + break; + + case GETID3_ASF_Marker_Object: + // Marker Object: (optional, one only) + // Field Name Field Type Size (bits) + // Object ID GUID 128 // GUID for Marker object - GETID3_ASF_Marker_Object + // Object Size QWORD 64 // size of Marker object, including 48 bytes of Marker Object header + // Reserved GUID 128 // hardcoded: 4CFEDB20-75F6-11CF-9C0F-00A0C90349CB + // Markers Count DWORD 32 // number of Marker structures in Marker Object + // Reserved WORD 16 // hardcoded: 0x0000 + // Name Length WORD 16 // number of bytes in the Name field + // Name WCHAR variable // name of the Marker Object + // Markers array of: variable // + // * Offset QWORD 64 // byte offset into Data Object + // * Presentation Time QWORD 64 // in 100-nanosecond units + // * Entry Length WORD 16 // length in bytes of (Send Time + Flags + Marker Description Length + Marker Description + Padding) + // * Send Time DWORD 32 // in milliseconds + // * Flags DWORD 32 // hardcoded: 0x00000000 + // * Marker Description Length DWORD 32 // number of bytes in Marker Description field + // * Marker Description WCHAR variable // array of Unicode characters - description of marker entry + // * Padding BYTESTREAM variable // optional padding bytes + + // shortcut + $thisfile_asf['marker_object'] = array(); + $thisfile_asf_markerobject = &$thisfile_asf['marker_object']; + + $thisfile_asf_markerobject['objectid'] = $NextObjectGUID; + $thisfile_asf_markerobject['objectid_guid'] = $NextObjectGUIDtext; + $thisfile_asf_markerobject['objectsize'] = $NextObjectSize; + $thisfile_asf_markerobject['reserved'] = substr($ASFHeaderData, $offset, 16); + $offset += 16; + $thisfile_asf_markerobject['reserved_guid'] = $this->BytestringToGUID($thisfile_asf_markerobject['reserved']); + if ($thisfile_asf_markerobject['reserved'] != $this->GUIDtoBytestring('4CFEDB20-75F6-11CF-9C0F-00A0C90349CB')) { + $ThisFileInfo['warning'][] = 'marker_object.reserved GUID {'.$this->BytestringToGUID($thisfile_asf_markerobject['reserved_1']).'} does not match expected "GETID3_ASF_Reserved_1" GUID {4CFEDB20-75F6-11CF-9C0F-00A0C90349CB}'; + break; + } + $thisfile_asf_markerobject['markers_count'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); + $offset += 4; + $thisfile_asf_markerobject['reserved_2'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + if ($thisfile_asf_markerobject['reserved_2'] != 0) { + $ThisFileInfo['warning'][] = 'marker_object.reserved_2 ('.getid3_lib::PrintHexBytes($thisfile_asf_markerobject['reserved_2']).') does not match expected value of "0"'; + break; + } + $thisfile_asf_markerobject['name_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + $thisfile_asf_markerobject['name'] = substr($ASFHeaderData, $offset, $thisfile_asf_markerobject['name_length']); + $offset += $thisfile_asf_markerobject['name_length']; + for ($MarkersCounter = 0; $MarkersCounter < $thisfile_asf_markerobject['markers_count']; $MarkersCounter++) { + $thisfile_asf_markerobject['markers'][$MarkersCounter]['offset'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8)); + $offset += 8; + $thisfile_asf_markerobject['markers'][$MarkersCounter]['presentation_time'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8)); + $offset += 8; + $thisfile_asf_markerobject['markers'][$MarkersCounter]['entry_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + $thisfile_asf_markerobject['markers'][$MarkersCounter]['send_time'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); + $offset += 4; + $thisfile_asf_markerobject['markers'][$MarkersCounter]['flags'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); + $offset += 4; + $thisfile_asf_markerobject['markers'][$MarkersCounter]['marker_description_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); + $offset += 4; + $thisfile_asf_markerobject['markers'][$MarkersCounter]['marker_description'] = substr($ASFHeaderData, $offset, $thisfile_asf_markerobject['markers'][$MarkersCounter]['marker_description_length']); + $offset += $thisfile_asf_markerobject['markers'][$MarkersCounter]['marker_description_length']; + $PaddingLength = $thisfile_asf_markerobject['markers'][$MarkersCounter]['entry_length'] - 4 - 4 - 4 - $thisfile_asf_markerobject['markers'][$MarkersCounter]['marker_description_length']; + if ($PaddingLength > 0) { + $thisfile_asf_markerobject['markers'][$MarkersCounter]['padding'] = substr($ASFHeaderData, $offset, $PaddingLength); + $offset += $PaddingLength; + } + } + break; + + case GETID3_ASF_Bitrate_Mutual_Exclusion_Object: + // Bitrate Mutual Exclusion Object: (optional) + // Field Name Field Type Size (bits) + // Object ID GUID 128 // GUID for Bitrate Mutual Exclusion object - GETID3_ASF_Bitrate_Mutual_Exclusion_Object + // Object Size QWORD 64 // size of Bitrate Mutual Exclusion object, including 42 bytes of Bitrate Mutual Exclusion Object header + // Exlusion Type GUID 128 // nature of mutual exclusion relationship. one of: (GETID3_ASF_Mutex_Bitrate, GETID3_ASF_Mutex_Unknown) + // Stream Numbers Count WORD 16 // number of video streams + // Stream Numbers WORD variable // array of mutually exclusive video stream numbers. 1 <= valid <= 127 + + // shortcut + $thisfile_asf['bitrate_mutual_exclusion_object'] = array(); + $thisfile_asf_bitratemutualexclusionobject = &$thisfile_asf['bitrate_mutual_exclusion_object']; + + $thisfile_asf_bitratemutualexclusionobject['objectid'] = $NextObjectGUID; + $thisfile_asf_bitratemutualexclusionobject['objectid_guid'] = $NextObjectGUIDtext; + $thisfile_asf_bitratemutualexclusionobject['objectsize'] = $NextObjectSize; + $thisfile_asf_bitratemutualexclusionobject['reserved'] = substr($ASFHeaderData, $offset, 16); + $thisfile_asf_bitratemutualexclusionobject['reserved_guid'] = $this->BytestringToGUID($thisfile_asf_bitratemutualexclusionobject['reserved']); + $offset += 16; + if (($thisfile_asf_bitratemutualexclusionobject['reserved'] != GETID3_ASF_Mutex_Bitrate) && ($thisfile_asf_bitratemutualexclusionobject['reserved'] != GETID3_ASF_Mutex_Unknown)) { + $ThisFileInfo['warning'][] = 'bitrate_mutual_exclusion_object.reserved GUID {'.$this->BytestringToGUID($thisfile_asf_bitratemutualexclusionobject['reserved']).'} does not match expected "GETID3_ASF_Mutex_Bitrate" GUID {'.$this->BytestringToGUID(GETID3_ASF_Mutex_Bitrate).'} or "GETID3_ASF_Mutex_Unknown" GUID {'.$this->BytestringToGUID(GETID3_ASF_Mutex_Unknown).'}'; + //return false; + break; + } + $thisfile_asf_bitratemutualexclusionobject['stream_numbers_count'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + for ($StreamNumberCounter = 0; $StreamNumberCounter < $thisfile_asf_bitratemutualexclusionobject['stream_numbers_count']; $StreamNumberCounter++) { + $thisfile_asf_bitratemutualexclusionobject['stream_numbers'][$StreamNumberCounter] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + } + break; + + case GETID3_ASF_Error_Correction_Object: + // Error Correction Object: (optional, one only) + // Field Name Field Type Size (bits) + // Object ID GUID 128 // GUID for Error Correction object - GETID3_ASF_Error_Correction_Object + // Object Size QWORD 64 // size of Error Correction object, including 44 bytes of Error Correction Object header + // Error Correction Type GUID 128 // type of error correction. one of: (GETID3_ASF_No_Error_Correction, GETID3_ASF_Audio_Spread) + // Error Correction Data Length DWORD 32 // number of bytes in Error Correction Data field + // Error Correction Data BYTESTREAM variable // structure depends on value of Error Correction Type field + + // shortcut + $thisfile_asf['error_correction_object'] = array(); + $thisfile_asf_errorcorrectionobject = &$thisfile_asf['error_correction_object']; + + $thisfile_asf_errorcorrectionobject['objectid'] = $NextObjectGUID; + $thisfile_asf_errorcorrectionobject['objectid_guid'] = $NextObjectGUIDtext; + $thisfile_asf_errorcorrectionobject['objectsize'] = $NextObjectSize; + $thisfile_asf_errorcorrectionobject['error_correction_type'] = substr($ASFHeaderData, $offset, 16); + $offset += 16; + $thisfile_asf_errorcorrectionobject['error_correction_guid'] = $this->BytestringToGUID($thisfile_asf_errorcorrectionobject['error_correction_type']); + $thisfile_asf_errorcorrectionobject['error_correction_data_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); + $offset += 4; + switch ($thisfile_asf_errorcorrectionobject['error_correction_type']) { + case GETID3_ASF_No_Error_Correction: + // should be no data, but just in case there is, skip to the end of the field + $offset += $thisfile_asf_errorcorrectionobject['error_correction_data_length']; + break; + + case GETID3_ASF_Audio_Spread: + // Field Name Field Type Size (bits) + // Span BYTE 8 // number of packets over which audio will be spread. + // Virtual Packet Length WORD 16 // size of largest audio payload found in audio stream + // Virtual Chunk Length WORD 16 // size of largest audio payload found in audio stream + // Silence Data Length WORD 16 // number of bytes in Silence Data field + // Silence Data BYTESTREAM variable // hardcoded: 0x00 * (Silence Data Length) bytes + + $thisfile_asf_errorcorrectionobject['span'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 1)); + $offset += 1; + $thisfile_asf_errorcorrectionobject['virtual_packet_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + $thisfile_asf_errorcorrectionobject['virtual_chunk_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + $thisfile_asf_errorcorrectionobject['silence_data_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + $thisfile_asf_errorcorrectionobject['silence_data'] = substr($ASFHeaderData, $offset, $thisfile_asf_errorcorrectionobject['silence_data_length']); + $offset += $thisfile_asf_errorcorrectionobject['silence_data_length']; + break; + + default: + $ThisFileInfo['warning'][] = 'error_correction_object.error_correction_type GUID {'.$this->BytestringToGUID($thisfile_asf_errorcorrectionobject['reserved']).'} does not match expected "GETID3_ASF_No_Error_Correction" GUID {'.$this->BytestringToGUID(GETID3_ASF_No_Error_Correction).'} or "GETID3_ASF_Audio_Spread" GUID {'.$this->BytestringToGUID(GETID3_ASF_Audio_Spread).'}'; + //return false; + break; + } + + break; + + case GETID3_ASF_Content_Description_Object: + // Content Description Object: (optional, one only) + // Field Name Field Type Size (bits) + // Object ID GUID 128 // GUID for Content Description object - GETID3_ASF_Content_Description_Object + // Object Size QWORD 64 // size of Content Description object, including 34 bytes of Content Description Object header + // Title Length WORD 16 // number of bytes in Title field + // Author Length WORD 16 // number of bytes in Author field + // Copyright Length WORD 16 // number of bytes in Copyright field + // Description Length WORD 16 // number of bytes in Description field + // Rating Length WORD 16 // number of bytes in Rating field + // Title WCHAR 16 // array of Unicode characters - Title + // Author WCHAR 16 // array of Unicode characters - Author + // Copyright WCHAR 16 // array of Unicode characters - Copyright + // Description WCHAR 16 // array of Unicode characters - Description + // Rating WCHAR 16 // array of Unicode characters - Rating + + // shortcut + $thisfile_asf['content_description_object'] = array(); + $thisfile_asf_contentdescriptionobject = &$thisfile_asf['content_description_object']; + + $thisfile_asf_contentdescriptionobject['objectid'] = $NextObjectGUID; + $thisfile_asf_contentdescriptionobject['objectid_guid'] = $NextObjectGUIDtext; + $thisfile_asf_contentdescriptionobject['objectsize'] = $NextObjectSize; + $thisfile_asf_contentdescriptionobject['title_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + $thisfile_asf_contentdescriptionobject['author_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + $thisfile_asf_contentdescriptionobject['copyright_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + $thisfile_asf_contentdescriptionobject['description_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + $thisfile_asf_contentdescriptionobject['rating_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + $thisfile_asf_contentdescriptionobject['title'] = substr($ASFHeaderData, $offset, $thisfile_asf_contentdescriptionobject['title_length']); + $offset += $thisfile_asf_contentdescriptionobject['title_length']; + $thisfile_asf_contentdescriptionobject['author'] = substr($ASFHeaderData, $offset, $thisfile_asf_contentdescriptionobject['author_length']); + $offset += $thisfile_asf_contentdescriptionobject['author_length']; + $thisfile_asf_contentdescriptionobject['copyright'] = substr($ASFHeaderData, $offset, $thisfile_asf_contentdescriptionobject['copyright_length']); + $offset += $thisfile_asf_contentdescriptionobject['copyright_length']; + $thisfile_asf_contentdescriptionobject['description'] = substr($ASFHeaderData, $offset, $thisfile_asf_contentdescriptionobject['description_length']); + $offset += $thisfile_asf_contentdescriptionobject['description_length']; + $thisfile_asf_contentdescriptionobject['rating'] = substr($ASFHeaderData, $offset, $thisfile_asf_contentdescriptionobject['rating_length']); + $offset += $thisfile_asf_contentdescriptionobject['rating_length']; + + $ASFcommentKeysToCopy = array('title'=>'title', 'author'=>'artist', 'copyright'=>'copyright', 'description'=>'comment', 'rating'=>'rating'); + foreach ($ASFcommentKeysToCopy as $keytocopyfrom => $keytocopyto) { + if (!empty($thisfile_asf_contentdescriptionobject[$keytocopyfrom])) { + $thisfile_asf_comments[$keytocopyto][] = $this->TrimTerm($thisfile_asf_contentdescriptionobject[$keytocopyfrom]); + } + } + break; + + case GETID3_ASF_Extended_Content_Description_Object: + // Extended Content Description Object: (optional, one only) + // Field Name Field Type Size (bits) + // Object ID GUID 128 // GUID for Extended Content Description object - GETID3_ASF_Extended_Content_Description_Object + // Object Size QWORD 64 // size of ExtendedContent Description object, including 26 bytes of Extended Content Description Object header + // Content Descriptors Count WORD 16 // number of entries in Content Descriptors list + // Content Descriptors array of: variable // + // * Descriptor Name Length WORD 16 // size in bytes of Descriptor Name field + // * Descriptor Name WCHAR variable // array of Unicode characters - Descriptor Name + // * Descriptor Value Data Type WORD 16 // Lookup array: + // 0x0000 = Unicode String (variable length) + // 0x0001 = BYTE array (variable length) + // 0x0002 = BOOL (DWORD, 32 bits) + // 0x0003 = DWORD (DWORD, 32 bits) + // 0x0004 = QWORD (QWORD, 64 bits) + // 0x0005 = WORD (WORD, 16 bits) + // * Descriptor Value Length WORD 16 // number of bytes stored in Descriptor Value field + // * Descriptor Value variable variable // value for Content Descriptor + + // shortcut + $thisfile_asf['extended_content_description_object'] = array(); + $thisfile_asf_extendedcontentdescriptionobject = &$thisfile_asf['extended_content_description_object']; + + $thisfile_asf_extendedcontentdescriptionobject['objectid'] = $NextObjectGUID; + $thisfile_asf_extendedcontentdescriptionobject['objectid_guid'] = $NextObjectGUIDtext; + $thisfile_asf_extendedcontentdescriptionobject['objectsize'] = $NextObjectSize; + $thisfile_asf_extendedcontentdescriptionobject['content_descriptors_count'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + for ($ExtendedContentDescriptorsCounter = 0; $ExtendedContentDescriptorsCounter < $thisfile_asf_extendedcontentdescriptionobject['content_descriptors_count']; $ExtendedContentDescriptorsCounter++) { + // shortcut + $thisfile_asf_extendedcontentdescriptionobject['content_descriptors'][$ExtendedContentDescriptorsCounter] = array(); + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current = &$thisfile_asf_extendedcontentdescriptionobject['content_descriptors'][$ExtendedContentDescriptorsCounter]; + + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['base_offset'] = $offset + 30; + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['name_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['name'] = substr($ASFHeaderData, $offset, $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['name_length']); + $offset += $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['name_length']; + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_type'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'] = substr($ASFHeaderData, $offset, $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_length']); + $offset += $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_length']; + switch ($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_type']) { + case 0x0000: // Unicode string + break; + + case 0x0001: // BYTE array + // do nothing + break; + + case 0x0002: // BOOL + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'] = (bool) getid3_lib::LittleEndian2Int($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']); + break; + + case 0x0003: // DWORD + case 0x0004: // QWORD + case 0x0005: // WORD + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'] = getid3_lib::LittleEndian2Int($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']); + break; + + default: + $ThisFileInfo['warning'][] = 'extended_content_description.content_descriptors.'.$ExtendedContentDescriptorsCounter.'.value_type is invalid ('.$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_type'].')'; + //return false; + break; + } + switch ($this->TrimConvert(strtolower($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['name']))) { + + case 'wm/albumartist': + case 'artist': + $thisfile_asf_comments['artist'] = array($this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'])); + break; + + case 'wm/albumtitle': + case 'album': + $thisfile_asf_comments['album'] = array($this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'])); + break; + + case 'wm/genre': + case 'genre': + $thisfile_asf_comments['genre'] = array($this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'])); + break; + + case 'wm/tracknumber': + case 'tracknumber': + $thisfile_asf_comments['track'] = array(intval($this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']))); + break; + + case 'wm/track': + if (empty($thisfile_asf_comments['track'])) { + $thisfile_asf_comments['track'] = array(1 + $this->TrimConvert($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'])); + } + break; + + case 'wm/year': + case 'year': + case 'date': + $thisfile_asf_comments['year'] = array( $this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'])); + break; + + case 'wm/lyrics': + case 'lyrics': + $thisfile_asf_comments['lyrics'] = array($this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'])); + break; + + case 'isvbr': + if ($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']) { + $thisfile_audio['bitrate_mode'] = 'vbr'; + $thisfile_video['bitrate_mode'] = 'vbr'; + } + break; + + case 'id3': + // id3v2 module might not be loaded + if (class_exists('getid3_id3v2')) { + $tempfile = tempnam('*', 'getID3'); + $tempfilehandle = fopen($tempfile, "wb"); + $tempThisfileInfo = array('encoding'=>$ThisFileInfo['encoding']); + fwrite($tempfilehandle, $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']); + fclose($tempfilehandle); + + $tempfilehandle = fopen($tempfile, "rb"); + $id3 = new getid3_id3v2($tempfilehandle, $tempThisfileInfo); + fclose($tempfilehandle); + unlink($tempfile); + + $ThisFileInfo['id3v2'] = $tempThisfileInfo['id3v2']; + } + break; + + case 'wm/encodingtime': + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['encoding_time_unix'] = $this->FILETIMEtoUNIXtime($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']); + $thisfile_asf_comments['encoding_time_unix'] = array($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['encoding_time_unix']); + break; + + case 'wm/picture': + //typedef struct _WMPicture{ + // LPWSTR pwszMIMEType; + // BYTE bPictureType; + // LPWSTR pwszDescription; + // DWORD dwDataLen; + // BYTE* pbData; + //} WM_PICTURE; + + $wm_picture_offset = 0; + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_type_id'] = getid3_lib::LittleEndian2Int(substr($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'], $wm_picture_offset, 1)); + $wm_picture_offset += 1; + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_type'] = $this->WMpictureTypeLookup($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_type_id']); + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_size'] = getid3_lib::LittleEndian2Int(substr($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'], $wm_picture_offset, 4)); + $wm_picture_offset += 4; + + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_mime'] = ''; + do { + $next_byte_pair = substr($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'], $wm_picture_offset, 2); + $wm_picture_offset += 2; + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_mime'] .= $next_byte_pair; + } while ($next_byte_pair !== "\x00\x00"); + + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_description'] = ''; + do { + $next_byte_pair = substr($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'], $wm_picture_offset, 2); + $wm_picture_offset += 2; + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_description'] .= $next_byte_pair; + } while ($next_byte_pair !== "\x00\x00"); + + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['dataoffset'] = $wm_picture_offset; + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['data'] = substr($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'], $wm_picture_offset); + unset($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']); + + break; + + default: + switch ($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_type']) { + case 0: // Unicode string + if (substr($this->TrimConvert($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['name']), 0, 3) == 'WM/') { + $thisfile_asf_comments[str_replace('wm/', '', strtolower($this->TrimConvert($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['name'])))] = array($this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'])); + } + break; + + case 1: + break; + } + break; + } + + } + break; + + case GETID3_ASF_Stream_Bitrate_Properties_Object: + // Stream Bitrate Properties Object: (optional, one only) + // Field Name Field Type Size (bits) + // Object ID GUID 128 // GUID for Stream Bitrate Properties object - GETID3_ASF_Stream_Bitrate_Properties_Object + // Object Size QWORD 64 // size of Extended Content Description object, including 26 bytes of Stream Bitrate Properties Object header + // Bitrate Records Count WORD 16 // number of records in Bitrate Records + // Bitrate Records array of: variable // + // * Flags WORD 16 // + // * * Stream Number bits 7 (0x007F) // number of this stream + // * * Reserved bits 9 (0xFF80) // hardcoded: 0 + // * Average Bitrate DWORD 32 // in bits per second + + // shortcut + $thisfile_asf['stream_bitrate_properties_object'] = array(); + $thisfile_asf_streambitratepropertiesobject = &$thisfile_asf['stream_bitrate_properties_object']; + + $thisfile_asf_streambitratepropertiesobject['objectid'] = $NextObjectGUID; + $thisfile_asf_streambitratepropertiesobject['objectid_guid'] = $NextObjectGUIDtext; + $thisfile_asf_streambitratepropertiesobject['objectsize'] = $NextObjectSize; + $thisfile_asf_streambitratepropertiesobject['bitrate_records_count'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + for ($BitrateRecordsCounter = 0; $BitrateRecordsCounter < $thisfile_asf_streambitratepropertiesobject['bitrate_records_count']; $BitrateRecordsCounter++) { + $thisfile_asf_streambitratepropertiesobject['bitrate_records'][$BitrateRecordsCounter]['flags_raw'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + $thisfile_asf_streambitratepropertiesobject['bitrate_records'][$BitrateRecordsCounter]['flags']['stream_number'] = $thisfile_asf_streambitratepropertiesobject['bitrate_records'][$BitrateRecordsCounter]['flags_raw'] & 0x007F; + $thisfile_asf_streambitratepropertiesobject['bitrate_records'][$BitrateRecordsCounter]['bitrate'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); + $offset += 4; + } + break; + + case GETID3_ASF_Padding_Object: + // Padding Object: (optional) + // Field Name Field Type Size (bits) + // Object ID GUID 128 // GUID for Padding object - GETID3_ASF_Padding_Object + // Object Size QWORD 64 // size of Padding object, including 24 bytes of ASF Padding Object header + // Padding Data BYTESTREAM variable // ignore + + // shortcut + $thisfile_asf['padding_object'] = array(); + $thisfile_asf_paddingobject = &$thisfile_asf['padding_object']; + + $thisfile_asf_paddingobject['objectid'] = $NextObjectGUID; + $thisfile_asf_paddingobject['objectid_guid'] = $NextObjectGUIDtext; + $thisfile_asf_paddingobject['objectsize'] = $NextObjectSize; + $thisfile_asf_paddingobject['padding_length'] = $thisfile_asf_paddingobject['objectsize'] - 16 - 8; + $thisfile_asf_paddingobject['padding'] = substr($ASFHeaderData, $offset, $thisfile_asf_paddingobject['padding_length']); + break; + + case GETID3_ASF_Extended_Content_Encryption_Object: + case GETID3_ASF_Content_Encryption_Object: + // WMA DRM - just ignore + $offset += ($NextObjectSize - 16 - 8); + break; + + default: + // Implementations shall ignore any standard or non-standard object that they do not know how to handle. + if ($this->GUIDname($NextObjectGUIDtext)) { + $ThisFileInfo['warning'][] = 'unhandled GUID "'.$this->GUIDname($NextObjectGUIDtext).'" {'.$NextObjectGUIDtext.'} in ASF header at offset '.($offset - 16 - 8); + } else { + $ThisFileInfo['warning'][] = 'unknown GUID {'.$NextObjectGUIDtext.'} in ASF header at offset '.($offset - 16 - 8); + } + $offset += ($NextObjectSize - 16 - 8); + break; + } + } + if (isset($thisfile_asf_streambitrateproperties['bitrate_records_count'])) { + $ASFbitrateAudio = 0; + $ASFbitrateVideo = 0; + for ($BitrateRecordsCounter = 0; $BitrateRecordsCounter < $thisfile_asf_streambitrateproperties['bitrate_records_count']; $BitrateRecordsCounter++) { + if (isset($thisfile_asf_codeclistobject['codec_entries'][$BitrateRecordsCounter])) { + switch ($thisfile_asf_codeclistobject['codec_entries'][$BitrateRecordsCounter]['type_raw']) { + case 1: + $ASFbitrateVideo += $thisfile_asf_streambitrateproperties['bitrate_records'][$BitrateRecordsCounter]['bitrate']; + break; + + case 2: + $ASFbitrateAudio += $thisfile_asf_streambitrateproperties['bitrate_records'][$BitrateRecordsCounter]['bitrate']; + break; + + default: + // do nothing + break; + } + } + } + if ($ASFbitrateAudio > 0) { + $thisfile_audio['bitrate'] = $ASFbitrateAudio; + } + if ($ASFbitrateVideo > 0) { + $thisfile_video['bitrate'] = $ASFbitrateVideo; + } + } + if (isset($thisfile_asf['stream_properties_object']) && is_array($thisfile_asf['stream_properties_object'])) { + + $thisfile_audio['bitrate'] = 0; + $thisfile_video['bitrate'] = 0; + + foreach ($thisfile_asf['stream_properties_object'] as $streamnumber => $streamdata) { + + switch ($streamdata['stream_type']) { + case GETID3_ASF_Audio_Media: + // Field Name Field Type Size (bits) + // Codec ID / Format Tag WORD 16 // unique ID of audio codec - defined as wFormatTag field of WAVEFORMATEX structure + // Number of Channels WORD 16 // number of channels of audio - defined as nChannels field of WAVEFORMATEX structure + // Samples Per Second DWORD 32 // in Hertz - defined as nSamplesPerSec field of WAVEFORMATEX structure + // Average number of Bytes/sec DWORD 32 // bytes/sec of audio stream - defined as nAvgBytesPerSec field of WAVEFORMATEX structure + // Block Alignment WORD 16 // block size in bytes of audio codec - defined as nBlockAlign field of WAVEFORMATEX structure + // Bits per sample WORD 16 // bits per sample of mono data. set to zero for variable bitrate codecs. defined as wBitsPerSample field of WAVEFORMATEX structure + // Codec Specific Data Size WORD 16 // size in bytes of Codec Specific Data buffer - defined as cbSize field of WAVEFORMATEX structure + // Codec Specific Data BYTESTREAM variable // array of codec-specific data bytes + + // shortcut + $thisfile_asf['audio_media'][$streamnumber] = array(); + $thisfile_asf_audiomedia_currentstream = &$thisfile_asf['audio_media'][$streamnumber]; + + $audiomediaoffset = 0; + + $thisfile_asf_audiomedia_currentstream = getid3_riff::RIFFparseWAVEFORMATex(substr($streamdata['type_specific_data'], $audiomediaoffset, 16)); + $audiomediaoffset += 16; + + $thisfile_audio['lossless'] = false; + switch ($thisfile_asf_audiomedia_currentstream['raw']['wFormatTag']) { + case 0x0001: // PCM + case 0x0163: // WMA9 Lossless + $thisfile_audio['lossless'] = true; + break; + } + + if (!empty($thisfile_asf['stream_bitrate_properties_object']['bitrate_records'])) { + foreach ($thisfile_asf['stream_bitrate_properties_object']['bitrate_records'] as $dummy => $dataarray) { + if (@$dataarray['flags']['stream_number'] == $streamnumber) { + $thisfile_asf_audiomedia_currentstream['bitrate'] = $dataarray['bitrate']; + $thisfile_audio['bitrate'] += $dataarray['bitrate']; + break; + } + } + } else { + if (@$thisfile_asf_audiomedia_currentstream['bytes_sec']) { + $thisfile_audio['bitrate'] += $thisfile_asf_audiomedia_currentstream['bytes_sec'] * 8; + } elseif (@$thisfile_asf_audiomedia_currentstream['bitrate']) { + $thisfile_audio['bitrate'] += $thisfile_asf_audiomedia_currentstream['bitrate']; + } + } + $thisfile_audio['streams'][$streamnumber] = $thisfile_asf_audiomedia_currentstream; + $thisfile_audio['streams'][$streamnumber]['wformattag'] = $thisfile_asf_audiomedia_currentstream['raw']['wFormatTag']; + $thisfile_audio['streams'][$streamnumber]['lossless'] = $thisfile_audio['lossless']; + $thisfile_audio['streams'][$streamnumber]['bitrate'] = $thisfile_audio['bitrate']; + unset($thisfile_audio['streams'][$streamnumber]['raw']); + + $thisfile_asf_audiomedia_currentstream['codec_data_size'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $audiomediaoffset, 2)); + $audiomediaoffset += 2; + $thisfile_asf_audiomedia_currentstream['codec_data'] = substr($streamdata['type_specific_data'], $audiomediaoffset, $thisfile_asf_audiomedia_currentstream['codec_data_size']); + $audiomediaoffset += $thisfile_asf_audiomedia_currentstream['codec_data_size']; + + break; + + case GETID3_ASF_Video_Media: + // Field Name Field Type Size (bits) + // Encoded Image Width DWORD 32 // width of image in pixels + // Encoded Image Height DWORD 32 // height of image in pixels + // Reserved Flags BYTE 8 // hardcoded: 0x02 + // Format Data Size WORD 16 // size of Format Data field in bytes + // Format Data array of: variable // + // * Format Data Size DWORD 32 // number of bytes in Format Data field, in bytes - defined as biSize field of BITMAPINFOHEADER structure + // * Image Width LONG 32 // width of encoded image in pixels - defined as biWidth field of BITMAPINFOHEADER structure + // * Image Height LONG 32 // height of encoded image in pixels - defined as biHeight field of BITMAPINFOHEADER structure + // * Reserved WORD 16 // hardcoded: 0x0001 - defined as biPlanes field of BITMAPINFOHEADER structure + // * Bits Per Pixel Count WORD 16 // bits per pixel - defined as biBitCount field of BITMAPINFOHEADER structure + // * Compression ID FOURCC 32 // fourcc of video codec - defined as biCompression field of BITMAPINFOHEADER structure + // * Image Size DWORD 32 // image size in bytes - defined as biSizeImage field of BITMAPINFOHEADER structure + // * Horizontal Pixels / Meter DWORD 32 // horizontal resolution of target device in pixels per meter - defined as biXPelsPerMeter field of BITMAPINFOHEADER structure + // * Vertical Pixels / Meter DWORD 32 // vertical resolution of target device in pixels per meter - defined as biYPelsPerMeter field of BITMAPINFOHEADER structure + // * Colors Used Count DWORD 32 // number of color indexes in the color table that are actually used - defined as biClrUsed field of BITMAPINFOHEADER structure + // * Important Colors Count DWORD 32 // number of color index required for displaying bitmap. if zero, all colors are required. defined as biClrImportant field of BITMAPINFOHEADER structure + // * Codec Specific Data BYTESTREAM variable // array of codec-specific data bytes + + // shortcut + $thisfile_asf['video_media'][$streamnumber] = array(); + $thisfile_asf_videomedia_currentstream = &$thisfile_asf['video_media'][$streamnumber]; + + $videomediaoffset = 0; + $thisfile_asf_videomedia_currentstream['image_width'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4)); + $videomediaoffset += 4; + $thisfile_asf_videomedia_currentstream['image_height'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4)); + $videomediaoffset += 4; + $thisfile_asf_videomedia_currentstream['flags'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 1)); + $videomediaoffset += 1; + $thisfile_asf_videomedia_currentstream['format_data_size'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 2)); + $videomediaoffset += 2; + $thisfile_asf_videomedia_currentstream['format_data']['format_data_size'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4)); + $videomediaoffset += 4; + $thisfile_asf_videomedia_currentstream['format_data']['image_width'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4)); + $videomediaoffset += 4; + $thisfile_asf_videomedia_currentstream['format_data']['image_height'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4)); + $videomediaoffset += 4; + $thisfile_asf_videomedia_currentstream['format_data']['reserved'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 2)); + $videomediaoffset += 2; + $thisfile_asf_videomedia_currentstream['format_data']['bits_per_pixel'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 2)); + $videomediaoffset += 2; + $thisfile_asf_videomedia_currentstream['format_data']['codec_fourcc'] = substr($streamdata['type_specific_data'], $videomediaoffset, 4); + $videomediaoffset += 4; + $thisfile_asf_videomedia_currentstream['format_data']['image_size'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4)); + $videomediaoffset += 4; + $thisfile_asf_videomedia_currentstream['format_data']['horizontal_pels'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4)); + $videomediaoffset += 4; + $thisfile_asf_videomedia_currentstream['format_data']['vertical_pels'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4)); + $videomediaoffset += 4; + $thisfile_asf_videomedia_currentstream['format_data']['colors_used'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4)); + $videomediaoffset += 4; + $thisfile_asf_videomedia_currentstream['format_data']['colors_important'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4)); + $videomediaoffset += 4; + $thisfile_asf_videomedia_currentstream['format_data']['codec_data'] = substr($streamdata['type_specific_data'], $videomediaoffset); + + if (!empty($thisfile_asf['stream_bitrate_properties_object']['bitrate_records'])) { + foreach ($thisfile_asf['stream_bitrate_properties_object']['bitrate_records'] as $dummy => $dataarray) { + if (@$dataarray['flags']['stream_number'] == $streamnumber) { + $thisfile_asf_videomedia_currentstream['bitrate'] = $dataarray['bitrate']; + $thisfile_video['streams'][$streamnumber]['bitrate'] = $dataarray['bitrate']; + $thisfile_video['bitrate'] += $dataarray['bitrate']; + break; + } + } + } + + $thisfile_asf_videomedia_currentstream['format_data']['codec'] = getid3_riff::RIFFfourccLookup($thisfile_asf_videomedia_currentstream['format_data']['codec_fourcc']); + + $thisfile_video['streams'][$streamnumber]['fourcc'] = $thisfile_asf_videomedia_currentstream['format_data']['codec_fourcc']; + $thisfile_video['streams'][$streamnumber]['codec'] = $thisfile_asf_videomedia_currentstream['format_data']['codec']; + $thisfile_video['streams'][$streamnumber]['resolution_x'] = $thisfile_asf_videomedia_currentstream['image_width']; + $thisfile_video['streams'][$streamnumber]['resolution_y'] = $thisfile_asf_videomedia_currentstream['image_height']; + $thisfile_video['streams'][$streamnumber]['bits_per_sample'] = $thisfile_asf_videomedia_currentstream['format_data']['bits_per_pixel']; + break; + + default: + break; + } + } + } + + while (ftell($fd) < $ThisFileInfo['avdataend']) { + $NextObjectDataHeader = fread($fd, 24); + $offset = 0; + $NextObjectGUID = substr($NextObjectDataHeader, 0, 16); + $offset += 16; + $NextObjectGUIDtext = $this->BytestringToGUID($NextObjectGUID); + $NextObjectSize = getid3_lib::LittleEndian2Int(substr($NextObjectDataHeader, $offset, 8)); + $offset += 8; + + switch ($NextObjectGUID) { + case GETID3_ASF_Data_Object: + // Data Object: (mandatory, one only) + // Field Name Field Type Size (bits) + // Object ID GUID 128 // GUID for Data object - GETID3_ASF_Data_Object + // Object Size QWORD 64 // size of Data object, including 50 bytes of Data Object header. may be 0 if FilePropertiesObject.BroadcastFlag == 1 + // File ID GUID 128 // unique identifier. identical to File ID field in Header Object + // Total Data Packets QWORD 64 // number of Data Packet entries in Data Object. invalid if FilePropertiesObject.BroadcastFlag == 1 + // Reserved WORD 16 // hardcoded: 0x0101 + + // shortcut + $thisfile_asf['data_object'] = array(); + $thisfile_asf_dataobject = &$thisfile_asf['data_object']; + + $DataObjectData = $NextObjectDataHeader.fread($fd, 50 - 24); + $offset = 24; + + $thisfile_asf_dataobject['objectid'] = $NextObjectGUID; + $thisfile_asf_dataobject['objectid_guid'] = $NextObjectGUIDtext; + $thisfile_asf_dataobject['objectsize'] = $NextObjectSize; + + $thisfile_asf_dataobject['fileid'] = substr($DataObjectData, $offset, 16); + $offset += 16; + $thisfile_asf_dataobject['fileid_guid'] = $this->BytestringToGUID($thisfile_asf_dataobject['fileid']); + $thisfile_asf_dataobject['total_data_packets'] = getid3_lib::LittleEndian2Int(substr($DataObjectData, $offset, 8)); + $offset += 8; + $thisfile_asf_dataobject['reserved'] = getid3_lib::LittleEndian2Int(substr($DataObjectData, $offset, 2)); + $offset += 2; + if ($thisfile_asf_dataobject['reserved'] != 0x0101) { + $ThisFileInfo['warning'][] = 'data_object.reserved ('.getid3_lib::PrintHexBytes($thisfile_asf_dataobject['reserved']).') does not match expected value of "0x0101"'; + //return false; + break; + } + + // Data Packets array of: variable // + // * Error Correction Flags BYTE 8 // + // * * Error Correction Data Length bits 4 // if Error Correction Length Type == 00, size of Error Correction Data in bytes, else hardcoded: 0000 + // * * Opaque Data Present bits 1 // + // * * Error Correction Length Type bits 2 // number of bits for size of the error correction data. hardcoded: 00 + // * * Error Correction Present bits 1 // If set, use Opaque Data Packet structure, else use Payload structure + // * Error Correction Data + + $ThisFileInfo['avdataoffset'] = ftell($fd); + fseek($fd, ($thisfile_asf_dataobject['objectsize'] - 50), SEEK_CUR); // skip actual audio/video data + $ThisFileInfo['avdataend'] = ftell($fd); + break; + + case GETID3_ASF_Simple_Index_Object: + // Simple Index Object: (optional, recommended, one per video stream) + // Field Name Field Type Size (bits) + // Object ID GUID 128 // GUID for Simple Index object - GETID3_ASF_Data_Object + // Object Size QWORD 64 // size of Simple Index object, including 56 bytes of Simple Index Object header + // File ID GUID 128 // unique identifier. may be zero or identical to File ID field in Data Object and Header Object + // Index Entry Time Interval QWORD 64 // interval between index entries in 100-nanosecond units + // Maximum Packet Count DWORD 32 // maximum packet count for all index entries + // Index Entries Count DWORD 32 // number of Index Entries structures + // Index Entries array of: variable // + // * Packet Number DWORD 32 // number of the Data Packet associated with this index entry + // * Packet Count WORD 16 // number of Data Packets to sent at this index entry + + // shortcut + $thisfile_asf['simple_index_object'] = array(); + $thisfile_asf_simpleindexobject = &$thisfile_asf['simple_index_object']; + + $SimpleIndexObjectData = $NextObjectDataHeader.fread($fd, 56 - 24); + $offset = 24; + + $thisfile_asf_simpleindexobject['objectid'] = $NextObjectGUID; + $thisfile_asf_simpleindexobject['objectid_guid'] = $NextObjectGUIDtext; + $thisfile_asf_simpleindexobject['objectsize'] = $NextObjectSize; + + $thisfile_asf_simpleindexobject['fileid'] = substr($SimpleIndexObjectData, $offset, 16); + $offset += 16; + $thisfile_asf_simpleindexobject['fileid_guid'] = $this->BytestringToGUID($thisfile_asf_simpleindexobject['fileid']); + $thisfile_asf_simpleindexobject['index_entry_time_interval'] = getid3_lib::LittleEndian2Int(substr($SimpleIndexObjectData, $offset, 8)); + $offset += 8; + $thisfile_asf_simpleindexobject['maximum_packet_count'] = getid3_lib::LittleEndian2Int(substr($SimpleIndexObjectData, $offset, 4)); + $offset += 4; + $thisfile_asf_simpleindexobject['index_entries_count'] = getid3_lib::LittleEndian2Int(substr($SimpleIndexObjectData, $offset, 4)); + $offset += 4; + + $IndexEntriesData = $SimpleIndexObjectData.fread($fd, 6 * $thisfile_asf_simpleindexobject['index_entries_count']); + for ($IndexEntriesCounter = 0; $IndexEntriesCounter < $thisfile_asf_simpleindexobject['index_entries_count']; $IndexEntriesCounter++) { + $thisfile_asf_simpleindexobject['index_entries'][$IndexEntriesCounter]['packet_number'] = getid3_lib::LittleEndian2Int(substr($IndexEntriesData, $offset, 4)); + $offset += 4; + $thisfile_asf_simpleindexobject['index_entries'][$IndexEntriesCounter]['packet_count'] = getid3_lib::LittleEndian2Int(substr($IndexEntriesData, $offset, 4)); + $offset += 2; + } + + break; + + case GETID3_ASF_Index_Object: + // 6.2 ASF top-level Index Object (optional but recommended when appropriate, 0 or 1) + // Field Name Field Type Size (bits) + // Object ID GUID 128 // GUID for the Index Object - GETID3_ASF_Index_Object + // Object Size QWORD 64 // Specifies the size, in bytes, of the Index Object, including at least 34 bytes of Index Object header + // Index Entry Time Interval DWORD 32 // Specifies the time interval between each index entry in ms. + // Index Specifiers Count WORD 16 // Specifies the number of Index Specifiers structures in this Index Object. + // Index Blocks Count DWORD 32 // Specifies the number of Index Blocks structures in this Index Object. + + // Index Entry Time Interval DWORD 32 // Specifies the time interval between index entries in milliseconds. This value cannot be 0. + // Index Specifiers Count WORD 16 // Specifies the number of entries in the Index Specifiers list. Valid values are 1 and greater. + // Index Specifiers array of: varies // + // * Stream Number WORD 16 // Specifies the stream number that the Index Specifiers refer to. Valid values are between 1 and 127. + // * Index Type WORD 16 // Specifies Index Type values as follows: + // 1 = Nearest Past Data Packet - indexes point to the data packet whose presentation time is closest to the index entry time. + // 2 = Nearest Past Media Object - indexes point to the closest data packet containing an entire object or first fragment of an object. + // 3 = Nearest Past Cleanpoint. - indexes point to the closest data packet containing an entire object (or first fragment of an object) that has the Cleanpoint Flag set. + // Nearest Past Cleanpoint is the most common type of index. + // Index Entry Count DWORD 32 // Specifies the number of Index Entries in the block. + // * Block Positions QWORD varies // Specifies a list of byte offsets of the beginnings of the blocks relative to the beginning of the first Data Packet (i.e., the beginning of the Data Object + 50 bytes). The number of entries in this list is specified by the value of the Index Specifiers Count field. The order of those byte offsets is tied to the order in which Index Specifiers are listed. + // * Index Entries array of: varies // + // * * Offsets DWORD varies // An offset value of 0xffffffff indicates an invalid offset value + + // shortcut + $thisfile_asf['asf_index_object'] = array(); + $thisfile_asf_asfindexobject = &$thisfile_asf['asf_index_object']; + + $ASFIndexObjectData = $NextObjectDataHeader.fread($fd, 34 - 24); + $offset = 24; + + $thisfile_asf_asfindexobject['objectid'] = $NextObjectGUID; + $thisfile_asf_asfindexobject['objectid_guid'] = $NextObjectGUIDtext; + $thisfile_asf_asfindexobject['objectsize'] = $NextObjectSize; + + $thisfile_asf_asfindexobject['entry_time_interval'] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 4)); + $offset += 4; + $thisfile_asf_asfindexobject['index_specifiers_count'] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 2)); + $offset += 2; + $thisfile_asf_asfindexobject['index_blocks_count'] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 4)); + $offset += 4; + + $ASFIndexObjectData .= fread($fd, 4 * $thisfile_asf_asfindexobject['index_specifiers_count']); + for ($IndexSpecifiersCounter = 0; $IndexSpecifiersCounter < $thisfile_asf_asfindexobject['index_specifiers_count']; $IndexSpecifiersCounter++) { + $IndexSpecifierStreamNumber = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 2)); + $offset += 2; + $thisfile_asf_asfindexobject['index_specifiers'][$IndexSpecifiersCounter]['stream_number'] = $IndexSpecifierStreamNumber; + $thisfile_asf_asfindexobject['index_specifiers'][$IndexSpecifiersCounter]['index_type'] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 2)); + $offset += 2; + $thisfile_asf_asfindexobject['index_specifiers'][$IndexSpecifiersCounter]['index_type_text'] = $this->ASFIndexObjectIndexTypeLookup($thisfile_asf_asfindexobject['index_specifiers'][$IndexSpecifiersCounter]['index_type']); + } + + $ASFIndexObjectData .= fread($fd, 4); + $thisfile_asf_asfindexobject['index_entry_count'] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 4)); + $offset += 4; + + $ASFIndexObjectData .= fread($fd, 8 * $thisfile_asf_asfindexobject['index_specifiers_count']); + for ($IndexSpecifiersCounter = 0; $IndexSpecifiersCounter < $thisfile_asf_asfindexobject['index_specifiers_count']; $IndexSpecifiersCounter++) { + $thisfile_asf_asfindexobject['block_positions'][$IndexSpecifiersCounter] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 8)); + $offset += 8; + } + + $ASFIndexObjectData .= fread($fd, 4 * $thisfile_asf_asfindexobject['index_specifiers_count'] * $thisfile_asf_asfindexobject['index_entry_count']); + for ($IndexEntryCounter = 0; $IndexEntryCounter < $thisfile_asf_asfindexobject['index_entry_count']; $IndexEntryCounter++) { + for ($IndexSpecifiersCounter = 0; $IndexSpecifiersCounter < $thisfile_asf_asfindexobject['index_specifiers_count']; $IndexSpecifiersCounter++) { + $thisfile_asf_asfindexobject['offsets'][$IndexSpecifiersCounter][$IndexEntryCounter] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 4)); + $offset += 4; + } + } + break; + + + default: + // Implementations shall ignore any standard or non-standard object that they do not know how to handle. + if ($this->GUIDname($NextObjectGUIDtext)) { + $ThisFileInfo['warning'][] = 'unhandled GUID "'.$this->GUIDname($NextObjectGUIDtext).'" {'.$NextObjectGUIDtext.'} in ASF body at offset '.($offset - 16 - 8); + } else { + $ThisFileInfo['warning'][] = 'unknown GUID {'.$NextObjectGUIDtext.'} in ASF body at offset '.(ftell($fd) - 16 - 8); + } + fseek($fd, ($NextObjectSize - 16 - 8), SEEK_CUR); + break; + } + } + + if (isset($thisfile_asf_codeclistobject['codec_entries']) && is_array($thisfile_asf_codeclistobject['codec_entries'])) { + foreach ($thisfile_asf_codeclistobject['codec_entries'] as $streamnumber => $streamdata) { + switch ($streamdata['information']) { + case 'WMV1': + case 'WMV2': + case 'WMV3': + $thisfile_video['dataformat'] = 'wmv'; + $ThisFileInfo['mime_type'] = 'video/x-ms-wmv'; + break; + + case 'MP42': + case 'MP43': + case 'MP4S': + case 'mp4s': + $thisfile_video['dataformat'] = 'asf'; + $ThisFileInfo['mime_type'] = 'video/x-ms-asf'; + break; + + default: + switch ($streamdata['type_raw']) { + case 1: + if (strstr($this->TrimConvert($streamdata['name']), 'Windows Media')) { + $thisfile_video['dataformat'] = 'wmv'; + if ($ThisFileInfo['mime_type'] == 'video/x-ms-asf') { + $ThisFileInfo['mime_type'] = 'video/x-ms-wmv'; + } + } + break; + + case 2: + if (strstr($this->TrimConvert($streamdata['name']), 'Windows Media')) { + $thisfile_audio['dataformat'] = 'wma'; + if ($ThisFileInfo['mime_type'] == 'video/x-ms-asf') { + $ThisFileInfo['mime_type'] = 'audio/x-ms-wma'; + } + } + break; + + } + break; + } + } + } + + switch (@$thisfile_audio['codec']) { + case 'MPEG Layer-3': + $thisfile_audio['dataformat'] = 'mp3'; + break; + + default: + break; + } + + if (isset($thisfile_asf_codeclistobject['codec_entries'])) { + foreach ($thisfile_asf_codeclistobject['codec_entries'] as $streamnumber => $streamdata) { + switch ($streamdata['type_raw']) { + + case 1: // video + $thisfile_video['encoder'] = $this->TrimConvert($thisfile_asf_codeclistobject['codec_entries'][$streamnumber]['name']); + break; + + case 2: // audio + $thisfile_audio['encoder'] = $this->TrimConvert($thisfile_asf_codeclistobject['codec_entries'][$streamnumber]['name']); + + // AH 2003-10-01 + $thisfile_audio['encoder_options'] = $this->TrimConvert($thisfile_asf_codeclistobject['codec_entries'][0]['description']); + + $thisfile_audio['codec'] = $thisfile_audio['encoder']; + break; + + default: + $ThisFileInfo['warning'][] = 'Unknown streamtype: [codec_list_object][codec_entries]['.$streamnumber.'][type_raw] == '.$streamdata['type_raw']; + break; + + } + } + } + + if (isset($ThisFileInfo['audio'])) { + $thisfile_audio['lossless'] = (isset($thisfile_audio['lossless']) ? $thisfile_audio['lossless'] : false); + $thisfile_audio['dataformat'] = (!empty($thisfile_audio['dataformat']) ? $thisfile_audio['dataformat'] : 'asf'); + } + if (!empty($thisfile_video['dataformat'])) { + $thisfile_video['lossless'] = (isset($thisfile_audio['lossless']) ? $thisfile_audio['lossless'] : false); + $thisfile_video['pixel_aspect_ratio'] = (isset($thisfile_audio['pixel_aspect_ratio']) ? $thisfile_audio['pixel_aspect_ratio'] : (float) 1); + $thisfile_video['dataformat'] = (!empty($thisfile_video['dataformat']) ? $thisfile_video['dataformat'] : 'asf'); + } + $ThisFileInfo['bitrate'] = @$thisfile_audio['bitrate'] + @$thisfile_video['bitrate']; + return true; + } + + function ASFCodecListObjectTypeLookup($CodecListType) { + static $ASFCodecListObjectTypeLookup = array(); + if (empty($ASFCodecListObjectTypeLookup)) { + $ASFCodecListObjectTypeLookup[0x0001] = 'Video Codec'; + $ASFCodecListObjectTypeLookup[0x0002] = 'Audio Codec'; + $ASFCodecListObjectTypeLookup[0xFFFF] = 'Unknown Codec'; + } + + return (isset($ASFCodecListObjectTypeLookup[$CodecListType]) ? $ASFCodecListObjectTypeLookup[$CodecListType] : 'Invalid Codec Type'); + } + + function KnownGUIDs() { + static $GUIDarray = array(); + if (empty($GUIDarray)) { + $GUIDarray['GETID3_ASF_Extended_Stream_Properties_Object'] = '14E6A5CB-C672-4332-8399-A96952065B5A'; + $GUIDarray['GETID3_ASF_Padding_Object'] = '1806D474-CADF-4509-A4BA-9AABCB96AAE8'; + $GUIDarray['GETID3_ASF_Payload_Ext_Syst_Pixel_Aspect_Ratio'] = '1B1EE554-F9EA-4BC8-821A-376B74E4C4B8'; + $GUIDarray['GETID3_ASF_Script_Command_Object'] = '1EFB1A30-0B62-11D0-A39B-00A0C90348F6'; + $GUIDarray['GETID3_ASF_No_Error_Correction'] = '20FB5700-5B55-11CF-A8FD-00805F5C442B'; + $GUIDarray['GETID3_ASF_Content_Branding_Object'] = '2211B3FA-BD23-11D2-B4B7-00A0C955FC6E'; + $GUIDarray['GETID3_ASF_Content_Encryption_Object'] = '2211B3FB-BD23-11D2-B4B7-00A0C955FC6E'; + $GUIDarray['GETID3_ASF_Digital_Signature_Object'] = '2211B3FC-BD23-11D2-B4B7-00A0C955FC6E'; + $GUIDarray['GETID3_ASF_Extended_Content_Encryption_Object'] = '298AE614-2622-4C17-B935-DAE07EE9289C'; + $GUIDarray['GETID3_ASF_Simple_Index_Object'] = '33000890-E5B1-11CF-89F4-00A0C90349CB'; + $GUIDarray['GETID3_ASF_Degradable_JPEG_Media'] = '35907DE0-E415-11CF-A917-00805F5C442B'; + $GUIDarray['GETID3_ASF_Payload_Extension_System_Timecode'] = '399595EC-8667-4E2D-8FDB-98814CE76C1E'; + $GUIDarray['GETID3_ASF_Binary_Media'] = '3AFB65E2-47EF-40F2-AC2C-70A90D71D343'; + $GUIDarray['GETID3_ASF_Timecode_Index_Object'] = '3CB73FD0-0C4A-4803-953D-EDF7B6228F0C'; + $GUIDarray['GETID3_ASF_Metadata_Library_Object'] = '44231C94-9498-49D1-A141-1D134E457054'; + $GUIDarray['GETID3_ASF_Reserved_3'] = '4B1ACBE3-100B-11D0-A39B-00A0C90348F6'; + $GUIDarray['GETID3_ASF_Reserved_4'] = '4CFEDB20-75F6-11CF-9C0F-00A0C90349CB'; + $GUIDarray['GETID3_ASF_Command_Media'] = '59DACFC0-59E6-11D0-A3AC-00A0C90348F6'; + $GUIDarray['GETID3_ASF_Header_Extension_Object'] = '5FBF03B5-A92E-11CF-8EE3-00C00C205365'; + $GUIDarray['GETID3_ASF_Media_Object_Index_Parameters_Obj'] = '6B203BAD-3F11-4E84-ACA8-D7613DE2CFA7'; + $GUIDarray['GETID3_ASF_Header_Object'] = '75B22630-668E-11CF-A6D9-00AA0062CE6C'; + $GUIDarray['GETID3_ASF_Content_Description_Object'] = '75B22633-668E-11CF-A6D9-00AA0062CE6C'; + $GUIDarray['GETID3_ASF_Error_Correction_Object'] = '75B22635-668E-11CF-A6D9-00AA0062CE6C'; + $GUIDarray['GETID3_ASF_Data_Object'] = '75B22636-668E-11CF-A6D9-00AA0062CE6C'; + $GUIDarray['GETID3_ASF_Web_Stream_Media_Subtype'] = '776257D4-C627-41CB-8F81-7AC7FF1C40CC'; + $GUIDarray['GETID3_ASF_Stream_Bitrate_Properties_Object'] = '7BF875CE-468D-11D1-8D82-006097C9A2B2'; + $GUIDarray['GETID3_ASF_Language_List_Object'] = '7C4346A9-EFE0-4BFC-B229-393EDE415C85'; + $GUIDarray['GETID3_ASF_Codec_List_Object'] = '86D15240-311D-11D0-A3A4-00A0C90348F6'; + $GUIDarray['GETID3_ASF_Reserved_2'] = '86D15241-311D-11D0-A3A4-00A0C90348F6'; + $GUIDarray['GETID3_ASF_File_Properties_Object'] = '8CABDCA1-A947-11CF-8EE4-00C00C205365'; + $GUIDarray['GETID3_ASF_File_Transfer_Media'] = '91BD222C-F21C-497A-8B6D-5AA86BFC0185'; + $GUIDarray['GETID3_ASF_Old_RTP_Extension_Data'] = '96800C63-4C94-11D1-837B-0080C7A37F95'; + $GUIDarray['GETID3_ASF_Advanced_Mutual_Exclusion_Object'] = 'A08649CF-4775-4670-8A16-6E35357566CD'; + $GUIDarray['GETID3_ASF_Bandwidth_Sharing_Object'] = 'A69609E6-517B-11D2-B6AF-00C04FD908E9'; + $GUIDarray['GETID3_ASF_Reserved_1'] = 'ABD3D211-A9BA-11cf-8EE6-00C00C205365'; + $GUIDarray['GETID3_ASF_Bandwidth_Sharing_Exclusive'] = 'AF6060AA-5197-11D2-B6AF-00C04FD908E9'; + $GUIDarray['GETID3_ASF_Bandwidth_Sharing_Partial'] = 'AF6060AB-5197-11D2-B6AF-00C04FD908E9'; + $GUIDarray['GETID3_ASF_JFIF_Media'] = 'B61BE100-5B4E-11CF-A8FD-00805F5C442B'; + $GUIDarray['GETID3_ASF_Stream_Properties_Object'] = 'B7DC0791-A9B7-11CF-8EE6-00C00C205365'; + $GUIDarray['GETID3_ASF_Video_Media'] = 'BC19EFC0-5B4D-11CF-A8FD-00805F5C442B'; + $GUIDarray['GETID3_ASF_Audio_Spread'] = 'BFC3CD50-618F-11CF-8BB2-00AA00B4E220'; + $GUIDarray['GETID3_ASF_Metadata_Object'] = 'C5F8CBEA-5BAF-4877-8467-AA8C44FA4CCA'; + $GUIDarray['GETID3_ASF_Payload_Ext_Syst_Sample_Duration'] = 'C6BD9450-867F-4907-83A3-C77921B733AD'; + $GUIDarray['GETID3_ASF_Group_Mutual_Exclusion_Object'] = 'D1465A40-5A79-4338-B71B-E36B8FD6C249'; + $GUIDarray['GETID3_ASF_Extended_Content_Description_Object'] = 'D2D0A440-E307-11D2-97F0-00A0C95EA850'; + $GUIDarray['GETID3_ASF_Stream_Prioritization_Object'] = 'D4FED15B-88D3-454F-81F0-ED5C45999E24'; + $GUIDarray['GETID3_ASF_Payload_Ext_System_Content_Type'] = 'D590DC20-07BC-436C-9CF7-F3BBFBF1A4DC'; + $GUIDarray['GETID3_ASF_Old_File_Properties_Object'] = 'D6E229D0-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_ASF_Header_Object'] = 'D6E229D1-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_ASF_Data_Object'] = 'D6E229D2-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Index_Object'] = 'D6E229D3-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_Stream_Properties_Object'] = 'D6E229D4-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_Content_Description_Object'] = 'D6E229D5-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_Script_Command_Object'] = 'D6E229D6-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_Marker_Object'] = 'D6E229D7-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_Component_Download_Object'] = 'D6E229D8-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_Stream_Group_Object'] = 'D6E229D9-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_Scalable_Object'] = 'D6E229DA-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_Prioritization_Object'] = 'D6E229DB-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Bitrate_Mutual_Exclusion_Object'] = 'D6E229DC-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_Inter_Media_Dependency_Object'] = 'D6E229DD-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_Rating_Object'] = 'D6E229DE-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Index_Parameters_Object'] = 'D6E229DF-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_Color_Table_Object'] = 'D6E229E0-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_Language_List_Object'] = 'D6E229E1-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_Audio_Media'] = 'D6E229E2-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_Video_Media'] = 'D6E229E3-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_Image_Media'] = 'D6E229E4-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_Timecode_Media'] = 'D6E229E5-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_Text_Media'] = 'D6E229E6-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_MIDI_Media'] = 'D6E229E7-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_Command_Media'] = 'D6E229E8-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_No_Error_Concealment'] = 'D6E229EA-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_Scrambled_Audio'] = 'D6E229EB-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_No_Color_Table'] = 'D6E229EC-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_SMPTE_Time'] = 'D6E229ED-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_ASCII_Text'] = 'D6E229EE-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_Unicode_Text'] = 'D6E229EF-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_HTML_Text'] = 'D6E229F0-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_URL_Command'] = 'D6E229F1-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_Filename_Command'] = 'D6E229F2-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_ACM_Codec'] = 'D6E229F3-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_VCM_Codec'] = 'D6E229F4-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_QuickTime_Codec'] = 'D6E229F5-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_DirectShow_Transform_Filter'] = 'D6E229F6-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_DirectShow_Rendering_Filter'] = 'D6E229F7-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_No_Enhancement'] = 'D6E229F8-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_Unknown_Enhancement_Type'] = 'D6E229F9-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_Temporal_Enhancement'] = 'D6E229FA-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_Spatial_Enhancement'] = 'D6E229FB-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_Quality_Enhancement'] = 'D6E229FC-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_Number_of_Channels_Enhancement'] = 'D6E229FD-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_Frequency_Response_Enhancement'] = 'D6E229FE-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_Media_Object'] = 'D6E229FF-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Mutex_Language'] = 'D6E22A00-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Mutex_Bitrate'] = 'D6E22A01-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Mutex_Unknown'] = 'D6E22A02-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_ASF_Placeholder_Object'] = 'D6E22A0E-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Old_Data_Unit_Extension_Object'] = 'D6E22A0F-35DA-11D1-9034-00A0C90349BE'; + $GUIDarray['GETID3_ASF_Web_Stream_Format'] = 'DA1E6B13-8359-4050-B398-388E965BF00C'; + $GUIDarray['GETID3_ASF_Payload_Ext_System_File_Name'] = 'E165EC0E-19ED-45D7-B4A7-25CBD1E28E9B'; + $GUIDarray['GETID3_ASF_Marker_Object'] = 'F487CD01-A951-11CF-8EE6-00C00C205365'; + $GUIDarray['GETID3_ASF_Timecode_Index_Parameters_Object'] = 'F55E496D-9797-4B5D-8C8B-604DFE9BFB24'; + $GUIDarray['GETID3_ASF_Audio_Media'] = 'F8699E40-5B4D-11CF-A8FD-00805F5C442B'; + $GUIDarray['GETID3_ASF_Media_Object_Index_Object'] = 'FEB103F8-12AD-4C64-840F-2A1D2F7AD48C'; + $GUIDarray['GETID3_ASF_Alt_Extended_Content_Encryption_Obj'] = 'FF889EF1-ADEE-40DA-9E71-98704BB928CE'; + } + return $GUIDarray; + } + + function GUIDname($GUIDstring) { + static $GUIDarray = array(); + if (empty($GUIDarray)) { + $GUIDarray = $this->KnownGUIDs(); + } + return array_search($GUIDstring, $GUIDarray); + } + + function ASFIndexObjectIndexTypeLookup($id) { + static $ASFIndexObjectIndexTypeLookup = array(); + if (empty($ASFIndexObjectIndexTypeLookup)) { + $ASFIndexObjectIndexTypeLookup[1] = 'Nearest Past Data Packet'; + $ASFIndexObjectIndexTypeLookup[2] = 'Nearest Past Media Object'; + $ASFIndexObjectIndexTypeLookup[3] = 'Nearest Past Cleanpoint'; + } + return (isset($ASFIndexObjectIndexTypeLookup[$id]) ? $ASFIndexObjectIndexTypeLookup[$id] : 'invalid'); + } + + function GUIDtoBytestring($GUIDstring) { + // Microsoft defines these 16-byte (128-bit) GUIDs in the strangest way: + // first 4 bytes are in little-endian order + // next 2 bytes are appended in little-endian order + // next 2 bytes are appended in little-endian order + // next 2 bytes are appended in big-endian order + // next 6 bytes are appended in big-endian order + + // AaBbCcDd-EeFf-GgHh-IiJj-KkLlMmNnOoPp is stored as this 16-byte string: + // $Dd $Cc $Bb $Aa $Ff $Ee $Hh $Gg $Ii $Jj $Kk $Ll $Mm $Nn $Oo $Pp + + $hexbytecharstring = chr(hexdec(substr($GUIDstring, 6, 2))); + $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 4, 2))); + $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 2, 2))); + $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 0, 2))); + + $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 11, 2))); + $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 9, 2))); + + $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 16, 2))); + $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 14, 2))); + + $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 19, 2))); + $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 21, 2))); + + $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 24, 2))); + $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 26, 2))); + $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 28, 2))); + $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 30, 2))); + $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 32, 2))); + $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 34, 2))); + + return $hexbytecharstring; + } + + function BytestringToGUID($Bytestring) { + $GUIDstring = str_pad(dechex(ord($Bytestring{3})), 2, '0', STR_PAD_LEFT); + $GUIDstring .= str_pad(dechex(ord($Bytestring{2})), 2, '0', STR_PAD_LEFT); + $GUIDstring .= str_pad(dechex(ord($Bytestring{1})), 2, '0', STR_PAD_LEFT); + $GUIDstring .= str_pad(dechex(ord($Bytestring{0})), 2, '0', STR_PAD_LEFT); + $GUIDstring .= '-'; + $GUIDstring .= str_pad(dechex(ord($Bytestring{5})), 2, '0', STR_PAD_LEFT); + $GUIDstring .= str_pad(dechex(ord($Bytestring{4})), 2, '0', STR_PAD_LEFT); + $GUIDstring .= '-'; + $GUIDstring .= str_pad(dechex(ord($Bytestring{7})), 2, '0', STR_PAD_LEFT); + $GUIDstring .= str_pad(dechex(ord($Bytestring{6})), 2, '0', STR_PAD_LEFT); + $GUIDstring .= '-'; + $GUIDstring .= str_pad(dechex(ord($Bytestring{8})), 2, '0', STR_PAD_LEFT); + $GUIDstring .= str_pad(dechex(ord($Bytestring{9})), 2, '0', STR_PAD_LEFT); + $GUIDstring .= '-'; + $GUIDstring .= str_pad(dechex(ord($Bytestring{10})), 2, '0', STR_PAD_LEFT); + $GUIDstring .= str_pad(dechex(ord($Bytestring{11})), 2, '0', STR_PAD_LEFT); + $GUIDstring .= str_pad(dechex(ord($Bytestring{12})), 2, '0', STR_PAD_LEFT); + $GUIDstring .= str_pad(dechex(ord($Bytestring{13})), 2, '0', STR_PAD_LEFT); + $GUIDstring .= str_pad(dechex(ord($Bytestring{14})), 2, '0', STR_PAD_LEFT); + $GUIDstring .= str_pad(dechex(ord($Bytestring{15})), 2, '0', STR_PAD_LEFT); + + return strtoupper($GUIDstring); + } + + function FILETIMEtoUNIXtime($FILETIME, $round=true) { + // FILETIME is a 64-bit unsigned integer representing + // the number of 100-nanosecond intervals since January 1, 1601 + // UNIX timestamp is number of seconds since January 1, 1970 + // 116444736000000000 = 10000000 * 60 * 60 * 24 * 365 * 369 + 89 leap days + if ($round) { + return intval(round(($FILETIME - 116444736000000000) / 10000000)); + } + return ($FILETIME - 116444736000000000) / 10000000; + } + + function WMpictureTypeLookup($WMpictureType) { + static $WMpictureTypeLookup = array(); + if (empty($WMpictureTypeLookup)) { + $WMpictureTypeLookup[0x03] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Front Cover'); + $WMpictureTypeLookup[0x04] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Back Cover'); + $WMpictureTypeLookup[0x00] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'User Defined'); + $WMpictureTypeLookup[0x05] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Leaflet Page'); + $WMpictureTypeLookup[0x06] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Media Label'); + $WMpictureTypeLookup[0x07] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Lead Artist'); + $WMpictureTypeLookup[0x08] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Artist'); + $WMpictureTypeLookup[0x09] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Conductor'); + $WMpictureTypeLookup[0x0A] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Band'); + $WMpictureTypeLookup[0x0B] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Composer'); + $WMpictureTypeLookup[0x0C] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Lyricist'); + $WMpictureTypeLookup[0x0D] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Recording Location'); + $WMpictureTypeLookup[0x0E] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'During Recording'); + $WMpictureTypeLookup[0x0F] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'During Performance'); + $WMpictureTypeLookup[0x10] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Video Screen Capture'); + $WMpictureTypeLookup[0x12] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Illustration'); + $WMpictureTypeLookup[0x13] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Band Logotype'); + $WMpictureTypeLookup[0x14] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Publisher Logotype'); + } + return @$WMpictureTypeLookup[$WMpictureType]; + } + + + // Remove terminator 00 00 and convert UNICODE to Latin-1 + function TrimConvert($string) { + + // remove terminator, only if present (it should be, but...) + if (substr($string, strlen($string) - 2, 2) == "\x00\x00") { + $string = substr($string, 0, strlen($string) - 2); + } + + // convert + return trim(getid3_lib::iconv_fallback('UTF-16LE', 'ISO-8859-1', $string), ' '); + } + + + function TrimTerm($string) { + + // remove terminator, only if present (it should be, but...) + if (substr($string, -2) == "\x00\x00") { + $string = substr($string, 0, -2); + } + return $string; + } + + +} + + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.audio-video.bink.php b/includes/getid3/getid3/module.audio-video.bink.php new file mode 100644 index 0000000..2ebc6fe --- /dev/null +++ b/includes/getid3/getid3/module.audio-video.bink.php @@ -0,0 +1,70 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio.bink.php // +// module for analyzing Bink or Smacker audio-video files // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_bink +{ + + function getid3_bink(&$fd, &$ThisFileInfo) { + +$ThisFileInfo['error'][] = 'Bink / Smacker files not properly processed by this version of getID3()'; + + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + $fileTypeID = fread($fd, 3); + switch ($fileTypeID) { + case 'BIK': + return $this->ParseBink($fd, $ThisFileInfo); + break; + + case 'SMK': + return $this->ParseSmacker($fd, $ThisFileInfo); + break; + + default: + $ThisFileInfo['error'][] = 'Expecting "BIK" or "SMK" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$fileTypeID.'"'; + return false; + break; + } + + return true; + + } + + function ParseBink(&$fd, &$ThisFileInfo) { + $ThisFileInfo['fileformat'] = 'bink'; + $ThisFileInfo['video']['dataformat'] = 'bink'; + + $fileData = 'BIK'.fread($fd, 13); + + $ThisFileInfo['bink']['data_size'] = getid3_lib::LittleEndian2Int(substr($fileData, 4, 4)); + $ThisFileInfo['bink']['frame_count'] = getid3_lib::LittleEndian2Int(substr($fileData, 8, 2)); + + if (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) != ($ThisFileInfo['bink']['data_size'] + 8)) { + $ThisFileInfo['error'][] = 'Probably truncated file: expecting '.$ThisFileInfo['bink']['data_size'].' bytes, found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']); + } + + return true; + } + + function ParseSmacker(&$fd, &$ThisFileInfo) { + $ThisFileInfo['fileformat'] = 'smacker'; + $ThisFileInfo['video']['dataformat'] = 'smacker'; + + return false; + } + +} + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.audio-video.flv.php b/includes/getid3/getid3/module.audio-video.flv.php new file mode 100644 index 0000000..d7ca5cf --- /dev/null +++ b/includes/getid3/getid3/module.audio-video.flv.php @@ -0,0 +1,497 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +// // +// FLV module by Seth Kaufman // +// // +// * version 0.1 (26 June 2005) // +// // +// minor modifications by James Heinrich // +// * version 0.1.1 (15 July 2005) // +// // +// Support for On2 VP6 codec and meta information by // +// Steve Webster // +// * version 0.2 (22 February 2006) // +// // +// Modified to not read entire file into memory // +// by James Heinrich // +// * version 0.3 (15 June 2006) // +// // +///////////////////////////////////////////////////////////////// +// // +// module.audio-video.flv.php // +// module for analyzing Shockwave Flash Video files // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + +define('GETID3_FLV_TAG_AUDIO', 8); +define('GETID3_FLV_TAG_VIDEO', 9); +define('GETID3_FLV_TAG_META', 18); + +define('GETID3_FLV_VIDEO_H263', 2); +define('GETID3_FLV_VIDEO_SCREEN', 3); +define('GETID3_FLV_VIDEO_VP6', 4); + +class getid3_flv +{ + + function getid3_flv(&$fd, &$ThisFileInfo, $ReturnAllTagData=false) { + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + + $FLVdataLength = $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']; + $FLVheader = fread($fd, 5); + + $ThisFileInfo['fileformat'] = 'flv'; + $ThisFileInfo['flv']['header']['signature'] = substr($FLVheader, 0, 3); + $ThisFileInfo['flv']['header']['version'] = getid3_lib::BigEndian2Int(substr($FLVheader, 3, 1)); + $TypeFlags = getid3_lib::BigEndian2Int(substr($FLVheader, 4, 1)); + + if ($ThisFileInfo['flv']['header']['signature'] != 'FLV') { + $ThisFileInfo['error'][] = 'Expecting "FLV" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$ThisFileInfo['flv']['header']['signature'].'"'; + unset($ThisFileInfo['flv']); + unset($ThisFileInfo['fileformat']); + return false; + } + + $ThisFileInfo['flv']['header']['hasAudio'] = (bool) ($TypeFlags & 0x04); + $ThisFileInfo['flv']['header']['hasVideo'] = (bool) ($TypeFlags & 0x01); + + $FrameSizeDataLength = getid3_lib::BigEndian2Int(fread($fd, 4)); + $FLVheaderFrameLength = 9; + if ($FrameSizeDataLength > $FLVheaderFrameLength) { + fseek($fd, $FrameSizeDataLength - $FLVheaderFrameLength, SEEK_CUR); + } + + $Duration = 0; + while ((ftell($fd) + 1) < $ThisFileInfo['avdataend']) { + //if (!$ThisFileInfo['flv']['header']['hasAudio'] || isset($ThisFileInfo['flv']['audio']['audioFormat'])) { + // if (!$ThisFileInfo['flv']['header']['hasVideo'] || isset($ThisFileInfo['flv']['video']['videoCodec'])) { + // break; + // } + //} + + $ThisTagHeader = fread($fd, 16); + + $PreviousTagLength = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 0, 4)); + $TagType = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 4, 1)); + $DataLength = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 5, 3)); + $Timestamp = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 8, 3)); + $LastHeaderByte = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 15, 1)); + $NextOffset = ftell($fd) - 1 + $DataLength; + + switch ($TagType) { + case GETID3_FLV_TAG_AUDIO: + if (!isset($ThisFileInfo['flv']['audio']['audioFormat'])) { + $ThisFileInfo['flv']['audio']['audioFormat'] = $LastHeaderByte & 0x07; + $ThisFileInfo['flv']['audio']['audioRate'] = ($LastHeaderByte & 0x30) / 0x10; + $ThisFileInfo['flv']['audio']['audioSampleSize'] = ($LastHeaderByte & 0x40) / 0x40; + $ThisFileInfo['flv']['audio']['audioType'] = ($LastHeaderByte & 0x80) / 0x80; + } + break; + + case GETID3_FLV_TAG_VIDEO: + if (!isset($ThisFileInfo['flv']['video']['videoCodec'])) { + $ThisFileInfo['flv']['video']['videoCodec'] = $LastHeaderByte & 0x07; + + $FLVvideoHeader = fread($fd, 11); + + if ($ThisFileInfo['flv']['video']['videoCodec'] != GETID3_FLV_VIDEO_VP6) { + + $PictureSizeType = (getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 3, 2))) >> 7; + $PictureSizeType = $PictureSizeType & 0x0007; + $ThisFileInfo['flv']['header']['videoSizeType'] = $PictureSizeType; + switch ($PictureSizeType) { + case 0: + $PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 2)); + $PictureSizeEnc <<= 1; + $ThisFileInfo['video']['resolution_x'] = ($PictureSizeEnc & 0xFF00) >> 8; + $PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 2)); + $PictureSizeEnc <<= 1; + $ThisFileInfo['video']['resolution_y'] = ($PictureSizeEnc & 0xFF00) >> 8; + break; + + case 1: + $PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 4)); + $PictureSizeEnc <<= 1; + $ThisFileInfo['video']['resolution_x'] = ($PictureSizeEnc & 0xFFFF0000) >> 16; + + $PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 7, 4)); + $PictureSizeEnc <<= 1; + $ThisFileInfo['video']['resolution_y'] = ($PictureSizeEnc & 0xFFFF0000) >> 16; + break; + + case 2: + $ThisFileInfo['video']['resolution_x'] = 352; + $ThisFileInfo['video']['resolution_y'] = 288; + break; + + case 3: + $ThisFileInfo['video']['resolution_x'] = 176; + $ThisFileInfo['video']['resolution_y'] = 144; + break; + + case 4: + $ThisFileInfo['video']['resolution_x'] = 128; + $ThisFileInfo['video']['resolution_y'] = 96; + break; + + case 5: + $ThisFileInfo['video']['resolution_x'] = 320; + $ThisFileInfo['video']['resolution_y'] = 240; + break; + + case 6: + $ThisFileInfo['video']['resolution_x'] = 160; + $ThisFileInfo['video']['resolution_y'] = 120; + break; + + default: + $ThisFileInfo['video']['resolution_x'] = 0; + $ThisFileInfo['video']['resolution_y'] = 0; + break; + + } + } + } + break; + + // Meta tag + case GETID3_FLV_TAG_META: + + fseek($fd, -1, SEEK_CUR); + $reader = new AMFReader(new AMFStream(fread($fd, $DataLength))); + $eventName = $reader->readData(); + $ThisFileInfo['meta'][$eventName] = $reader->readData(); + unset($reader); + + $ThisFileInfo['video']['frame_rate'] = $ThisFileInfo['meta']['onMetaData']['framerate']; + $ThisFileInfo['video']['resolution_x'] = $ThisFileInfo['meta']['onMetaData']['width']; + $ThisFileInfo['video']['resolution_y'] = $ThisFileInfo['meta']['onMetaData']['height']; + break; + + default: + // noop + break; + } + + if ($Timestamp > $Duration) { + $Duration = $Timestamp; + } + + fseek($fd, $NextOffset, SEEK_SET); + } + + $ThisFileInfo['playtime_seconds'] = $Duration / 1000; + $ThisFileInfo['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $ThisFileInfo['playtime_seconds']; + + if ($ThisFileInfo['flv']['header']['hasAudio']) { + $ThisFileInfo['audio']['codec'] = $this->FLVaudioFormat($ThisFileInfo['flv']['audio']['audioFormat']); + $ThisFileInfo['audio']['sample_rate'] = $this->FLVaudioRate($ThisFileInfo['flv']['audio']['audioRate']); + $ThisFileInfo['audio']['bits_per_sample'] = $this->FLVaudioBitDepth($ThisFileInfo['flv']['audio']['audioSampleSize']); + + $ThisFileInfo['audio']['channels'] = $ThisFileInfo['flv']['audio']['audioType'] + 1; // 0=mono,1=stereo + $ThisFileInfo['audio']['lossless'] = ($ThisFileInfo['flv']['audio']['audioFormat'] ? false : true); // 0=uncompressed + $ThisFileInfo['audio']['dataformat'] = 'flv'; + } + if (@$ThisFileInfo['flv']['header']['hasVideo']) { + $ThisFileInfo['video']['codec'] = $this->FLVvideoCodec($ThisFileInfo['flv']['video']['videoCodec']); + $ThisFileInfo['video']['dataformat'] = 'flv'; + $ThisFileInfo['video']['lossless'] = false; + } + + return true; + } + + + function FLVaudioFormat($id) { + $FLVaudioFormat = array( + 0 => 'uncompressed', + 1 => 'ADPCM', + 2 => 'mp3', + 5 => 'Nellymoser 8kHz mono', + 6 => 'Nellymoser', + ); + return (@$FLVaudioFormat[$id] ? @$FLVaudioFormat[$id] : false); + } + + function FLVaudioRate($id) { + $FLVaudioRate = array( + 0 => 5500, + 1 => 11025, + 2 => 22050, + 3 => 44100, + ); + return (@$FLVaudioRate[$id] ? @$FLVaudioRate[$id] : false); + } + + function FLVaudioBitDepth($id) { + $FLVaudioBitDepth = array( + 0 => 8, + 1 => 16, + ); + return (@$FLVaudioBitDepth[$id] ? @$FLVaudioBitDepth[$id] : false); + } + + function FLVvideoCodec($id) { + $FLVvideoCodec = array( + GETID3_FLV_VIDEO_H263 => 'Sorenson H.263', + GETID3_FLV_VIDEO_SCREEN => 'Screen video', + GETID3_FLV_VIDEO_VP6 => 'On2 VP6', + ); + return (@$FLVvideoCodec[$id] ? @$FLVvideoCodec[$id] : false); + } +} + +class AMFStream { + var $bytes; + var $pos; + + function AMFStream(&$bytes) { + $this->bytes =& $bytes; + $this->pos = 0; + } + + function readByte() { + return getid3_lib::BigEndian2Int(substr($this->bytes, $this->pos++, 1)); + } + + function readInt() { + return ($this->readByte() << 8) + $this->readByte(); + } + + function readLong() { + return ($this->readByte() << 24) + ($this->readByte() << 16) + ($this->readByte() << 8) + $this->readByte(); + } + + function readDouble() { + return getid3_lib::BigEndian2Float($this->read(8)); + } + + function readUTF() { + $length = $this->readInt(); + return $this->read($length); + } + + function readLongUTF() { + $length = $this->readLong(); + return $this->read($length); + } + + function read($length) { + $val = substr($this->bytes, $this->pos, $length); + $this->pos += $length; + return $val; + } + + function peekByte() { + $pos = $this->pos; + $val = $this->readByte(); + $this->pos = $pos; + return $val; + } + + function peekInt() { + $pos = $this->pos; + $val = $this->readInt(); + $this->pos = $pos; + return $val; + } + + function peekLong() { + $pos = $this->pos; + $val = $this->readLong(); + $this->pos = $pos; + return $val; + } + + function peekDouble() { + $pos = $this->pos; + $val = $this->readDouble(); + $this->pos = $pos; + return $val; + } + + function peekUTF() { + $pos = $this->pos; + $val = $this->readUTF(); + $this->pos = $pos; + return $val; + } + + function peekLongUTF() { + $pos = $this->pos; + $val = $this->readLongUTF(); + $this->pos = $pos; + return $val; + } +} + +class AMFReader { + var $stream; + + function AMFReader(&$stream) { + $this->stream =& $stream; + } + + function readData() { + $value = null; + + $type = $this->stream->readByte(); + + switch($type) { + // Double + case 0: + $value = $this->readDouble(); + break; + + // Boolean + case 1: + $value = $this->readBoolean(); + break; + + // String + case 2: + $value = $this->readString(); + break; + + // Object + case 3: + $value = $this->readObject(); + break; + + // null + case 6: + return null; + break; + + // Mixed array + case 8: + $value = $this->readMixedArray(); + break; + + // Array + case 10: + $value = $this->readArray(); + break; + + // Date + case 11: + $value = $this->readDate(); + break; + + // Long string + case 13: + $value = $this->readLongString(); + break; + + // XML (handled as string) + case 15: + $value = $this->readXML(); + break; + + // Typed object (handled as object) + case 16: + $value = $this->readTypedObject(); + break; + + // Long string + default: + $value = '(unknown or unsupported data type)'; + break; + } + + return $value; + } + + function readDouble() { + return $this->stream->readDouble(); + } + + function readBoolean() { + return $this->stream->readByte() == 1; + } + + function readString() { + return $this->stream->readUTF(); + } + + function readObject() { + // Get highest numerical index - ignored + $highestIndex = $this->stream->readLong(); + + $data = array(); + + while ($key = $this->stream->readUTF()) { + // Mixed array record ends with empty string (0x00 0x00) and 0x09 + if (($key == '') && ($this->stream->peekByte() == 0x09)) { + // Consume byte + $this->stream->readByte(); + break; + } + + $data[$key] = $this->readData(); + } + + return $data; + } + + function readMixedArray() { + // Get highest numerical index - ignored + $highestIndex = $this->stream->readLong(); + + $data = array(); + + while ($key = $this->stream->readUTF()) { + // Mixed array record ends with empty string (0x00 0x00) and 0x09 + if (($key == '') && ($this->stream->peekByte() == 0x09)) { + // Consume byte + $this->stream->readByte(); + break; + } + + if (is_numeric($key)) { + $key = (float) $key; + } + + $data[$key] = $this->readData(); + } + + return $data; + } + + function readArray() { + $length = $this->stream->readLong(); + + $data = array(); + + for ($i = 0; $i < count($length); $i++) { + $data[] = $this->readData(); + } + + return $data; + } + + function readDate() { + $timestamp = $this->stream->readDouble(); + $timezone = $this->stream->readInt(); + return $timestamp; + } + + function readLongString() { + return $this->stream->readLongUTF(); + } + + function readXML() { + return $this->stream->readLongUTF(); + } + + function readTypedObject() { + $className = $this->stream->readUTF(); + return $this->readObject(); + } +} + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.audio-video.matroska.php b/includes/getid3/getid3/module.audio-video.matroska.php new file mode 100644 index 0000000..11ac929 --- /dev/null +++ b/includes/getid3/getid3/module.audio-video.matroska.php @@ -0,0 +1,78 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio-video.matriska.php // +// module for analyzing Matroska containers // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_matroska +{ + + function getid3_matroska(&$fd, &$ThisFileInfo) { + + $ThisFileInfo['fileformat'] = 'matroska'; + + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + + //$ThisFileInfo['matroska']['raw']['a'] = $this->EBML2Int(fread($fd, 4)); + + $ThisFileInfo['error'][] = 'Mastroka parsing not enabled in this version of getID3()'; + return false; + + } + + + function EBML2Int($EBMLstring) { + // http://matroska.org/specs/ + + // Element ID coded with an UTF-8 like system: + // 1xxx xxxx - Class A IDs (2^7 -2 possible values) (base 0x8X) + // 01xx xxxx xxxx xxxx - Class B IDs (2^14-2 possible values) (base 0x4X 0xXX) + // 001x xxxx xxxx xxxx xxxx xxxx - Class C IDs (2^21-2 possible values) (base 0x2X 0xXX 0xXX) + // 0001 xxxx xxxx xxxx xxxx xxxx xxxx xxxx - Class D IDs (2^28-2 possible values) (base 0x1X 0xXX 0xXX 0xXX) + // Values with all x at 0 and 1 are reserved (hence the -2). + + // Data size, in octets, is also coded with an UTF-8 like system : + // 1xxx xxxx - value 0 to 2^7-2 + // 01xx xxxx xxxx xxxx - value 0 to 2^14-2 + // 001x xxxx xxxx xxxx xxxx xxxx - value 0 to 2^21-2 + // 0001 xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^28-2 + // 0000 1xxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^35-2 + // 0000 01xx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^42-2 + // 0000 001x xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^49-2 + // 0000 0001 xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^56-2 + + if (0x80 & ord($EBMLstring{0})) { + $EBMLstring{0} = chr(ord($EBMLstring{0}) & 0x7F); + } elseif (0x40 & ord($EBMLstring{0})) { + $EBMLstring{0} = chr(ord($EBMLstring{0}) & 0x3F); + } elseif (0x20 & ord($EBMLstring{0})) { + $EBMLstring{0} = chr(ord($EBMLstring{0}) & 0x1F); + } elseif (0x10 & ord($EBMLstring{0})) { + $EBMLstring{0} = chr(ord($EBMLstring{0}) & 0x0F); + } elseif (0x08 & ord($EBMLstring{0})) { + $EBMLstring{0} = chr(ord($EBMLstring{0}) & 0x07); + } elseif (0x04 & ord($EBMLstring{0})) { + $EBMLstring{0} = chr(ord($EBMLstring{0}) & 0x03); + } elseif (0x02 & ord($EBMLstring{0})) { + $EBMLstring{0} = chr(ord($EBMLstring{0}) & 0x01); + } elseif (0x01 & ord($EBMLstring{0})) { + $EBMLstring{0} = chr(ord($EBMLstring{0}) & 0x00); + } else { + return false; + } + return getid3_lib::BigEndian2Int($EBMLstring); + } + +} + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.audio-video.mpeg.php b/includes/getid3/getid3/module.audio-video.mpeg.php new file mode 100644 index 0000000..8f48784 --- /dev/null +++ b/includes/getid3/getid3/module.audio-video.mpeg.php @@ -0,0 +1,292 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio-video.mpeg.php // +// module for analyzing MPEG files // +// dependencies: module.audio.mp3.php // +// /// +///////////////////////////////////////////////////////////////// + +getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true); + +define('GETID3_MPEG_VIDEO_PICTURE_START', "\x00\x00\x01\x00"); +define('GETID3_MPEG_VIDEO_USER_DATA_START', "\x00\x00\x01\xB2"); +define('GETID3_MPEG_VIDEO_SEQUENCE_HEADER', "\x00\x00\x01\xB3"); +define('GETID3_MPEG_VIDEO_SEQUENCE_ERROR', "\x00\x00\x01\xB4"); +define('GETID3_MPEG_VIDEO_EXTENSION_START', "\x00\x00\x01\xB5"); +define('GETID3_MPEG_VIDEO_SEQUENCE_END', "\x00\x00\x01\xB7"); +define('GETID3_MPEG_VIDEO_GROUP_START', "\x00\x00\x01\xB8"); +define('GETID3_MPEG_AUDIO_START', "\x00\x00\x01\xC0"); + + +class getid3_mpeg +{ + + function getid3_mpeg(&$fd, &$ThisFileInfo) { + if ($ThisFileInfo['avdataend'] <= $ThisFileInfo['avdataoffset']) { + $ThisFileInfo['error'][] = '"avdataend" ('.$ThisFileInfo['avdataend'].') is unexpectedly less-than-or-equal-to "avdataoffset" ('.$ThisFileInfo['avdataoffset'].')'; + return false; + } + $ThisFileInfo['fileformat'] = 'mpeg'; + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + $MPEGstreamData = fread($fd, min(100000, $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'])); + $MPEGstreamDataLength = strlen($MPEGstreamData); + + $foundVideo = true; + $VideoChunkOffset = 0; + while (substr($MPEGstreamData, $VideoChunkOffset++, 4) !== GETID3_MPEG_VIDEO_SEQUENCE_HEADER) { + if ($VideoChunkOffset >= $MPEGstreamDataLength) { + $foundVideo = false; + break; + } + } + if ($foundVideo) { + + // Start code 32 bits + // horizontal frame size 12 bits + // vertical frame size 12 bits + // pixel aspect ratio 4 bits + // frame rate 4 bits + // bitrate 18 bits + // marker bit 1 bit + // VBV buffer size 10 bits + // constrained parameter flag 1 bit + // intra quant. matrix flag 1 bit + // intra quant. matrix values 512 bits (present if matrix flag == 1) + // non-intra quant. matrix flag 1 bit + // non-intra quant. matrix values 512 bits (present if matrix flag == 1) + + $ThisFileInfo['video']['dataformat'] = 'mpeg'; + + $VideoChunkOffset += (strlen(GETID3_MPEG_VIDEO_SEQUENCE_HEADER) - 1); + + $FrameSizeDWORD = getid3_lib::BigEndian2Int(substr($MPEGstreamData, $VideoChunkOffset, 3)); + $VideoChunkOffset += 3; + + $AspectRatioFrameRateDWORD = getid3_lib::BigEndian2Int(substr($MPEGstreamData, $VideoChunkOffset, 1)); + $VideoChunkOffset += 1; + + $assortedinformation = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $VideoChunkOffset, 4)); + $VideoChunkOffset += 4; + + $ThisFileInfo['mpeg']['video']['raw']['framesize_horizontal'] = ($FrameSizeDWORD & 0xFFF000) >> 12; // 12 bits for horizontal frame size + $ThisFileInfo['mpeg']['video']['raw']['framesize_vertical'] = ($FrameSizeDWORD & 0x000FFF); // 12 bits for vertical frame size + $ThisFileInfo['mpeg']['video']['raw']['pixel_aspect_ratio'] = ($AspectRatioFrameRateDWORD & 0xF0) >> 4; + $ThisFileInfo['mpeg']['video']['raw']['frame_rate'] = ($AspectRatioFrameRateDWORD & 0x0F); + + $ThisFileInfo['mpeg']['video']['framesize_horizontal'] = $ThisFileInfo['mpeg']['video']['raw']['framesize_horizontal']; + $ThisFileInfo['mpeg']['video']['framesize_vertical'] = $ThisFileInfo['mpeg']['video']['raw']['framesize_vertical']; + + $ThisFileInfo['mpeg']['video']['pixel_aspect_ratio'] = $this->MPEGvideoAspectRatioLookup($ThisFileInfo['mpeg']['video']['raw']['pixel_aspect_ratio']); + $ThisFileInfo['mpeg']['video']['pixel_aspect_ratio_text'] = $this->MPEGvideoAspectRatioTextLookup($ThisFileInfo['mpeg']['video']['raw']['pixel_aspect_ratio']); + $ThisFileInfo['mpeg']['video']['frame_rate'] = $this->MPEGvideoFramerateLookup($ThisFileInfo['mpeg']['video']['raw']['frame_rate']); + + $ThisFileInfo['mpeg']['video']['raw']['bitrate'] = getid3_lib::Bin2Dec(substr($assortedinformation, 0, 18)); + $ThisFileInfo['mpeg']['video']['raw']['marker_bit'] = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 18, 1)); + $ThisFileInfo['mpeg']['video']['raw']['vbv_buffer_size'] = getid3_lib::Bin2Dec(substr($assortedinformation, 19, 10)); + $ThisFileInfo['mpeg']['video']['raw']['constrained_param_flag'] = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 29, 1)); + $ThisFileInfo['mpeg']['video']['raw']['intra_quant_flag'] = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 30, 1)); + if ($ThisFileInfo['mpeg']['video']['raw']['intra_quant_flag']) { + + // read 512 bits + $ThisFileInfo['mpeg']['video']['raw']['intra_quant'] = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $VideoChunkOffset, 64)); + $VideoChunkOffset += 64; + + $ThisFileInfo['mpeg']['video']['raw']['non_intra_quant_flag'] = (bool) getid3_lib::Bin2Dec(substr($ThisFileInfo['mpeg']['video']['raw']['intra_quant'], 511, 1)); + $ThisFileInfo['mpeg']['video']['raw']['intra_quant'] = getid3_lib::Bin2Dec(substr($assortedinformation, 31, 1)).substr(getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $VideoChunkOffset, 64)), 0, 511); + + if ($ThisFileInfo['mpeg']['video']['raw']['non_intra_quant_flag']) { + $ThisFileInfo['mpeg']['video']['raw']['non_intra_quant'] = substr($MPEGstreamData, $VideoChunkOffset, 64); + $VideoChunkOffset += 64; + } + + } else { + + $ThisFileInfo['mpeg']['video']['raw']['non_intra_quant_flag'] = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 31, 1)); + if ($ThisFileInfo['mpeg']['video']['raw']['non_intra_quant_flag']) { + $ThisFileInfo['mpeg']['video']['raw']['non_intra_quant'] = substr($MPEGstreamData, $VideoChunkOffset, 64); + $VideoChunkOffset += 64; + } + + } + + if ($ThisFileInfo['mpeg']['video']['raw']['bitrate'] == 0x3FFFF) { // 18 set bits + + $ThisFileInfo['warning'][] = 'This version of getID3() ['.GETID3_VERSION.'] cannot determine average bitrate of VBR MPEG video files'; + $ThisFileInfo['mpeg']['video']['bitrate_mode'] = 'vbr'; + + } else { + + $ThisFileInfo['mpeg']['video']['bitrate'] = $ThisFileInfo['mpeg']['video']['raw']['bitrate'] * 400; + $ThisFileInfo['mpeg']['video']['bitrate_mode'] = 'cbr'; + $ThisFileInfo['video']['bitrate'] = $ThisFileInfo['mpeg']['video']['bitrate']; + + } + + $ThisFileInfo['video']['resolution_x'] = $ThisFileInfo['mpeg']['video']['framesize_horizontal']; + $ThisFileInfo['video']['resolution_y'] = $ThisFileInfo['mpeg']['video']['framesize_vertical']; + $ThisFileInfo['video']['frame_rate'] = $ThisFileInfo['mpeg']['video']['frame_rate']; + $ThisFileInfo['video']['bitrate_mode'] = $ThisFileInfo['mpeg']['video']['bitrate_mode']; + $ThisFileInfo['video']['pixel_aspect_ratio'] = $ThisFileInfo['mpeg']['video']['pixel_aspect_ratio']; + $ThisFileInfo['video']['lossless'] = false; + $ThisFileInfo['video']['bits_per_sample'] = 24; + + } else { + + $ThisFileInfo['error'][] = 'Could not find start of video block in the first 100,000 bytes (or before end of file) - this might not be an MPEG-video file?'; + + } + + //0x000001B3 begins the sequence_header of every MPEG video stream. + //But in MPEG-2, this header must immediately be followed by an + //extension_start_code (0x000001B5) with a sequence_extension ID (1). + //(This extension contains all the additional MPEG-2 stuff.) + //MPEG-1 doesn't have this extension, so that's a sure way to tell the + //difference between MPEG-1 and MPEG-2 video streams. + + if (substr($MPEGstreamData, $VideoChunkOffset, 4) == GETID3_MPEG_VIDEO_EXTENSION_START) { + $ThisFileInfo['video']['codec'] = 'MPEG-2'; + } else { + $ThisFileInfo['video']['codec'] = 'MPEG-1'; + } + + + $AudioChunkOffset = 0; + while (true) { + while (substr($MPEGstreamData, $AudioChunkOffset++, 4) !== GETID3_MPEG_AUDIO_START) { + if ($AudioChunkOffset >= $MPEGstreamDataLength) { + break 2; + } + } + + for ($i = 0; $i <= 7; $i++) { + // some files have the MPEG-audio header 8 bytes after the end of the $00 $00 $01 $C0 signature, some have it up to 13 bytes (or more?) after + // I have no idea why or what the difference is, so this is a stupid hack. + // If anybody has any better idea of what's going on, please let me know - info@getid3.org + + $dummy = $ThisFileInfo; + if (getid3_mp3::decodeMPEGaudioHeader($fd, ($AudioChunkOffset + 3) + 8 + $i, $dummy, false)) { + $ThisFileInfo = $dummy; + $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; + $ThisFileInfo['audio']['lossless'] = false; + break 2; + + } + } + } + + // Temporary hack to account for interleaving overhead: + if (!empty($ThisFileInfo['video']['bitrate']) && !empty($ThisFileInfo['audio']['bitrate'])) { + $ThisFileInfo['playtime_seconds'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / ($ThisFileInfo['video']['bitrate'] + $ThisFileInfo['audio']['bitrate']); + + // Interleaved MPEG audio/video files have a certain amount of overhead that varies + // by both video and audio bitrates, and not in any sensible, linear/logarithmic patter + // Use interpolated lookup tables to approximately guess how much is overhead, because + // playtime is calculated as filesize / total-bitrate + $ThisFileInfo['playtime_seconds'] *= $this->MPEGsystemNonOverheadPercentage($ThisFileInfo['video']['bitrate'], $ThisFileInfo['audio']['bitrate']); + + //switch ($ThisFileInfo['video']['bitrate']) { + // case('5000000'): + // $multiplier = 0.93292642112380355828048824319889; + // break; + // case('5500000'): + // $multiplier = 0.93582895375200989965359777343219; + // break; + // case('6000000'): + // $multiplier = 0.93796247714820932532911373859139; + // break; + // case('7000000'): + // $multiplier = 0.9413264083635103463010117778776; + // break; + // default: + // $multiplier = 1; + // break; + //} + //$ThisFileInfo['playtime_seconds'] *= $multiplier; + //$ThisFileInfo['warning'][] = 'Interleaved MPEG audio/video playtime may be inaccurate. With current hack should be within a few seconds of accurate. Report to info@getid3.org if off by more than 10 seconds.'; + if ($ThisFileInfo['video']['bitrate'] < 50000) { + $ThisFileInfo['warning'][] = 'Interleaved MPEG audio/video playtime may be slightly inaccurate for video bitrates below 100kbps. Except in extreme low-bitrate situations, error should be less than 1%. Report to info@getid3.org if greater than this.'; + } + } + + return true; + } + + + function MPEGsystemNonOverheadPercentage($VideoBitrate, $AudioBitrate) { + $OverheadPercentage = 0; + + $AudioBitrate = max(min($AudioBitrate / 1000, 384), 32); // limit to range of 32kbps - 384kbps (should be only legal bitrates, but maybe VBR?) + $VideoBitrate = max(min($VideoBitrate / 1000, 10000), 10); // limit to range of 10kbps - 10Mbps (beyond that curves flatten anyways, no big loss) + + + //OMBB[audiobitrate] = array(video-10kbps, video-100kbps, video-1000kbps, video-10000kbps) + $OverheadMultiplierByBitrate[32] = array(0, 0.9676287944368530, 0.9802276264360310, 0.9844916183244460, 0.9852821845179940); + $OverheadMultiplierByBitrate[48] = array(0, 0.9779100089209830, 0.9787770035359320, 0.9846738664076130, 0.9852683013799960); + $OverheadMultiplierByBitrate[56] = array(0, 0.9731249855367600, 0.9776624308938040, 0.9832606361852130, 0.9843922606633340); + $OverheadMultiplierByBitrate[64] = array(0, 0.9755642683275760, 0.9795256705493390, 0.9836573009193170, 0.9851122539404470); + $OverheadMultiplierByBitrate[96] = array(0, 0.9788025247497290, 0.9798553314148700, 0.9822956869792560, 0.9834815119124690); + $OverheadMultiplierByBitrate[128] = array(0, 0.9816940050925480, 0.9821675936072120, 0.9829756927470870, 0.9839763420152050); + $OverheadMultiplierByBitrate[160] = array(0, 0.9825894094561180, 0.9820913399073960, 0.9823907143253970, 0.9832821783651570); + $OverheadMultiplierByBitrate[192] = array(0, 0.9832038474336260, 0.9825731694317960, 0.9821028622712400, 0.9828262076447620); + $OverheadMultiplierByBitrate[224] = array(0, 0.9836516298538770, 0.9824718601823890, 0.9818302180625380, 0.9823735101626480); + $OverheadMultiplierByBitrate[256] = array(0, 0.9845863022094920, 0.9837229411967540, 0.9824521662210830, 0.9828645172100790); + $OverheadMultiplierByBitrate[320] = array(0, 0.9849565280263180, 0.9837683142805110, 0.9822885275960400, 0.9824424382727190); + $OverheadMultiplierByBitrate[384] = array(0, 0.9856094774357600, 0.9844573394432720, 0.9825970399837330, 0.9824673808303890); + + $BitrateToUseMin = 32; + $BitrateToUseMax = 32; + $previousBitrate = 32; + foreach ($OverheadMultiplierByBitrate as $key => $value) { + if ($AudioBitrate >= $previousBitrate) { + $BitrateToUseMin = $previousBitrate; + } + if ($AudioBitrate < $key) { + $BitrateToUseMax = $key; + break; + } + $previousBitrate = $key; + } + $FactorA = ($BitrateToUseMax - $AudioBitrate) / ($BitrateToUseMax - $BitrateToUseMin); + + $VideoBitrateLog10 = log10($VideoBitrate); + $VideoFactorMin1 = $OverheadMultiplierByBitrate[$BitrateToUseMin][floor($VideoBitrateLog10)]; + $VideoFactorMin2 = $OverheadMultiplierByBitrate[$BitrateToUseMax][floor($VideoBitrateLog10)]; + $VideoFactorMax1 = $OverheadMultiplierByBitrate[$BitrateToUseMin][ceil($VideoBitrateLog10)]; + $VideoFactorMax2 = $OverheadMultiplierByBitrate[$BitrateToUseMax][ceil($VideoBitrateLog10)]; + $FactorV = $VideoBitrateLog10 - floor($VideoBitrateLog10); + + $OverheadPercentage = $VideoFactorMin1 * $FactorA * $FactorV; + $OverheadPercentage += $VideoFactorMin2 * (1 - $FactorA) * $FactorV; + $OverheadPercentage += $VideoFactorMax1 * $FactorA * (1 - $FactorV); + $OverheadPercentage += $VideoFactorMax2 * (1 - $FactorA) * (1 - $FactorV); + + return $OverheadPercentage; + } + + + function MPEGvideoFramerateLookup($rawframerate) { + $MPEGvideoFramerateLookup = array(0, 23.976, 24, 25, 29.97, 30, 50, 59.94, 60); + return (isset($MPEGvideoFramerateLookup[$rawframerate]) ? (float) $MPEGvideoFramerateLookup[$rawframerate] : (float) 0); + } + + function MPEGvideoAspectRatioLookup($rawaspectratio) { + $MPEGvideoAspectRatioLookup = array(0, 1, 0.6735, 0.7031, 0.7615, 0.8055, 0.8437, 0.8935, 0.9157, 0.9815, 1.0255, 1.0695, 1.0950, 1.1575, 1.2015, 0); + return (isset($MPEGvideoAspectRatioLookup[$rawaspectratio]) ? (float) $MPEGvideoAspectRatioLookup[$rawaspectratio] : (float) 0); + } + + function MPEGvideoAspectRatioTextLookup($rawaspectratio) { + $MPEGvideoAspectRatioTextLookup = array('forbidden', 'square pixels', '0.6735', '16:9, 625 line, PAL', '0.7615', '0.8055', '16:9, 525 line, NTSC', '0.8935', '4:3, 625 line, PAL, CCIR601', '0.9815', '1.0255', '1.0695', '4:3, 525 line, NTSC, CCIR601', '1.1575', '1.2015', 'reserved'); + return (isset($MPEGvideoAspectRatioTextLookup[$rawaspectratio]) ? $MPEGvideoAspectRatioTextLookup[$rawaspectratio] : ''); + } + +} + + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.audio-video.nsv.php b/includes/getid3/getid3/module.audio-video.nsv.php new file mode 100644 index 0000000..dab0338 --- /dev/null +++ b/includes/getid3/getid3/module.audio-video.nsv.php @@ -0,0 +1,224 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio.nsv.php // +// module for analyzing Nullsoft NSV files // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_nsv +{ + + function getid3_nsv(&$fd, &$ThisFileInfo) { + + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + $NSVheader = fread($fd, 4); + + switch ($NSVheader) { + case 'NSVs': + if ($this->getNSVsHeaderFilepointer($fd, $ThisFileInfo, 0)) { + $ThisFileInfo['fileformat'] = 'nsv'; + $ThisFileInfo['audio']['dataformat'] = 'nsv'; + $ThisFileInfo['video']['dataformat'] = 'nsv'; + $ThisFileInfo['audio']['lossless'] = false; + $ThisFileInfo['video']['lossless'] = false; + } + break; + + case 'NSVf': + if ($this->getNSVfHeaderFilepointer($fd, $ThisFileInfo, 0)) { + $ThisFileInfo['fileformat'] = 'nsv'; + $ThisFileInfo['audio']['dataformat'] = 'nsv'; + $ThisFileInfo['video']['dataformat'] = 'nsv'; + $ThisFileInfo['audio']['lossless'] = false; + $ThisFileInfo['video']['lossless'] = false; + $this->getNSVsHeaderFilepointer($fd, $ThisFileInfo, $ThisFileInfo['nsv']['NSVf']['header_length']); + } + break; + + default: + $ThisFileInfo['error'][] = 'Expecting "NSVs" or "NSVf" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$NSVheader.'"'; + return false; + break; + } + + if (!isset($ThisFileInfo['nsv']['NSVf'])) { + $ThisFileInfo['warning'][] = 'NSVf header not present - cannot calculate playtime or bitrate'; + } + + return true; + } + + function getNSVsHeaderFilepointer(&$fd, &$ThisFileInfo, $fileoffset) { + fseek($fd, $fileoffset, SEEK_SET); + $NSVsheader = fread($fd, 28); + $offset = 0; + + $ThisFileInfo['nsv']['NSVs']['identifier'] = substr($NSVsheader, $offset, 4); + $offset += 4; + + if ($ThisFileInfo['nsv']['NSVs']['identifier'] != 'NSVs') { + $ThisFileInfo['error'][] = 'expected "NSVs" at offset ('.$fileoffset.'), found "'.$ThisFileInfo['nsv']['NSVs']['identifier'].'" instead'; + unset($ThisFileInfo['nsv']['NSVs']); + return false; + } + + $ThisFileInfo['nsv']['NSVs']['offset'] = $fileoffset; + + $ThisFileInfo['nsv']['NSVs']['video_codec'] = substr($NSVsheader, $offset, 4); + $offset += 4; + $ThisFileInfo['nsv']['NSVs']['audio_codec'] = substr($NSVsheader, $offset, 4); + $offset += 4; + $ThisFileInfo['nsv']['NSVs']['resolution_x'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 2)); + $offset += 2; + $ThisFileInfo['nsv']['NSVs']['resolution_y'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 2)); + $offset += 2; + + $ThisFileInfo['nsv']['NSVs']['framerate_index'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); + $offset += 1; + //$ThisFileInfo['nsv']['NSVs']['unknown1b'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); + $offset += 1; + //$ThisFileInfo['nsv']['NSVs']['unknown1c'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); + $offset += 1; + //$ThisFileInfo['nsv']['NSVs']['unknown1d'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); + $offset += 1; + //$ThisFileInfo['nsv']['NSVs']['unknown2a'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); + $offset += 1; + //$ThisFileInfo['nsv']['NSVs']['unknown2b'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); + $offset += 1; + //$ThisFileInfo['nsv']['NSVs']['unknown2c'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); + $offset += 1; + //$ThisFileInfo['nsv']['NSVs']['unknown2d'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); + $offset += 1; + + switch ($ThisFileInfo['nsv']['NSVs']['audio_codec']) { + case 'PCM ': + $ThisFileInfo['nsv']['NSVs']['bits_channel'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); + $offset += 1; + $ThisFileInfo['nsv']['NSVs']['channels'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1)); + $offset += 1; + $ThisFileInfo['nsv']['NSVs']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 2)); + $offset += 2; + + $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['nsv']['NSVs']['sample_rate']; + break; + + case 'MP3 ': + case 'NONE': + default: + //$ThisFileInfo['nsv']['NSVs']['unknown3'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 4)); + $offset += 4; + break; + } + + $ThisFileInfo['video']['resolution_x'] = $ThisFileInfo['nsv']['NSVs']['resolution_x']; + $ThisFileInfo['video']['resolution_y'] = $ThisFileInfo['nsv']['NSVs']['resolution_y']; + $ThisFileInfo['nsv']['NSVs']['frame_rate'] = $this->NSVframerateLookup($ThisFileInfo['nsv']['NSVs']['framerate_index']); + $ThisFileInfo['video']['frame_rate'] = $ThisFileInfo['nsv']['NSVs']['frame_rate']; + $ThisFileInfo['video']['bits_per_sample'] = 24; + $ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1; + + return true; + } + + function getNSVfHeaderFilepointer(&$fd, &$ThisFileInfo, $fileoffset, $getTOCoffsets=false) { + fseek($fd, $fileoffset, SEEK_SET); + $NSVfheader = fread($fd, 28); + $offset = 0; + + $ThisFileInfo['nsv']['NSVf']['identifier'] = substr($NSVfheader, $offset, 4); + $offset += 4; + + if ($ThisFileInfo['nsv']['NSVf']['identifier'] != 'NSVf') { + $ThisFileInfo['error'][] = 'expected "NSVf" at offset ('.$fileoffset.'), found "'.$ThisFileInfo['nsv']['NSVf']['identifier'].'" instead'; + unset($ThisFileInfo['nsv']['NSVf']); + return false; + } + + $ThisFileInfo['nsv']['NSVs']['offset'] = $fileoffset; + + $ThisFileInfo['nsv']['NSVf']['header_length'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4)); + $offset += 4; + $ThisFileInfo['nsv']['NSVf']['file_size'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4)); + $offset += 4; + + if ($ThisFileInfo['nsv']['NSVf']['file_size'] > $ThisFileInfo['avdataend']) { + $ThisFileInfo['warning'][] = 'truncated file - NSVf header indicates '.$ThisFileInfo['nsv']['NSVf']['file_size'].' bytes, file actually '.$ThisFileInfo['avdataend'].' bytes'; + } + + $ThisFileInfo['nsv']['NSVf']['playtime_ms'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4)); + $offset += 4; + $ThisFileInfo['nsv']['NSVf']['meta_size'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4)); + $offset += 4; + $ThisFileInfo['nsv']['NSVf']['TOC_entries_1'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4)); + $offset += 4; + $ThisFileInfo['nsv']['NSVf']['TOC_entries_2'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4)); + $offset += 4; + + if ($ThisFileInfo['nsv']['NSVf']['playtime_ms'] == 0) { + $ThisFileInfo['error'][] = 'Corrupt NSV file: NSVf.playtime_ms == zero'; + return false; + } + + $NSVfheader .= fread($fd, $ThisFileInfo['nsv']['NSVf']['meta_size'] + (4 * $ThisFileInfo['nsv']['NSVf']['TOC_entries_1']) + (4 * $ThisFileInfo['nsv']['NSVf']['TOC_entries_2'])); + $NSVfheaderlength = strlen($NSVfheader); + $ThisFileInfo['nsv']['NSVf']['metadata'] = substr($NSVfheader, $offset, $ThisFileInfo['nsv']['NSVf']['meta_size']); + $offset += $ThisFileInfo['nsv']['NSVf']['meta_size']; + + if ($getTOCoffsets) { + $TOCcounter = 0; + while ($TOCcounter < $ThisFileInfo['nsv']['NSVf']['TOC_entries_1']) { + if ($TOCcounter < $ThisFileInfo['nsv']['NSVf']['TOC_entries_1']) { + $ThisFileInfo['nsv']['NSVf']['TOC_1'][$TOCcounter] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4)); + $offset += 4; + $TOCcounter++; + } + } + } + + if (trim($ThisFileInfo['nsv']['NSVf']['metadata']) != '') { + $ThisFileInfo['nsv']['NSVf']['metadata'] = str_replace('`', "\x01", $ThisFileInfo['nsv']['NSVf']['metadata']); + $CommentPairArray = explode("\x01".' ', $ThisFileInfo['nsv']['NSVf']['metadata']); + foreach ($CommentPairArray as $CommentPair) { + if (strstr($CommentPair, '='."\x01")) { + list($key, $value) = explode('='."\x01", $CommentPair, 2); + $ThisFileInfo['nsv']['comments'][strtolower($key)][] = trim(str_replace("\x01", '', $value)); + } + } + } + + $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['nsv']['NSVf']['playtime_ms'] / 1000; + $ThisFileInfo['bitrate'] = ($ThisFileInfo['nsv']['NSVf']['file_size'] * 8) / $ThisFileInfo['playtime_seconds']; + + return true; + } + + + function NSVframerateLookup($framerateindex) { + if ($framerateindex <= 127) { + return (float) $framerateindex; + } + + static $NSVframerateLookup = array(); + if (empty($NSVframerateLookup)) { + $NSVframerateLookup[129] = (float) 29.970; + $NSVframerateLookup[131] = (float) 23.976; + $NSVframerateLookup[133] = (float) 14.985; + $NSVframerateLookup[197] = (float) 59.940; + $NSVframerateLookup[199] = (float) 47.952; + } + return (isset($NSVframerateLookup[$framerateindex]) ? $NSVframerateLookup[$framerateindex] : false); + } + +} + + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.audio-video.quicktime.php b/includes/getid3/getid3/module.audio-video.quicktime.php new file mode 100644 index 0000000..e08b9b2 --- /dev/null +++ b/includes/getid3/getid3/module.audio-video.quicktime.php @@ -0,0 +1,1302 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio-video.quicktime.php // +// module for analyzing Quicktime and MP3-in-MP4 files // +// dependencies: module.audio.mp3.php // +// /// +///////////////////////////////////////////////////////////////// + +getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true); + +class getid3_quicktime +{ + + function getid3_quicktime(&$fd, &$ThisFileInfo, $ReturnAtomData=true, $ParseAllPossibleAtoms=false) { + + $ThisFileInfo['fileformat'] = 'quicktime'; + $ThisFileInfo['quicktime']['hinting'] = false; + + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + + $offset = 0; + $atomcounter = 0; + + while ($offset < $ThisFileInfo['avdataend']) { + fseek($fd, $offset, SEEK_SET); + $AtomHeader = fread($fd, 8); + + $atomsize = getid3_lib::BigEndian2Int(substr($AtomHeader, 0, 4)); + $atomname = substr($AtomHeader, 4, 4); + $ThisFileInfo['quicktime'][$atomname]['name'] = $atomname; + $ThisFileInfo['quicktime'][$atomname]['size'] = $atomsize; + $ThisFileInfo['quicktime'][$atomname]['offset'] = $offset; + + if (($offset + $atomsize) > $ThisFileInfo['avdataend']) { + $ThisFileInfo['error'][] = 'Atom at offset '.$offset.' claims to go beyond end-of-file (length: '.$atomsize.' bytes)'; + return false; + } + + if ($atomsize == 0) { + // Furthermore, for historical reasons the list of atoms is optionally + // terminated by a 32-bit integer set to 0. If you are writing a program + // to read user data atoms, you should allow for the terminating 0. + break; + } + switch ($atomname) { + case 'mdat': // Media DATa atom + // 'mdat' contains the actual data for the audio/video + if (($atomsize > 8) && (!isset($ThisFileInfo['avdataend_tmp']) || ($ThisFileInfo['quicktime'][$atomname]['size'] > ($ThisFileInfo['avdataend_tmp'] - $ThisFileInfo['avdataoffset'])))) { + + $ThisFileInfo['avdataoffset'] = $ThisFileInfo['quicktime'][$atomname]['offset'] + 8; + $OldAVDataEnd = $ThisFileInfo['avdataend']; + $ThisFileInfo['avdataend'] = $ThisFileInfo['quicktime'][$atomname]['offset'] + $ThisFileInfo['quicktime'][$atomname]['size']; + + if (getid3_mp3::MPEGaudioHeaderValid(getid3_mp3::MPEGaudioHeaderDecode(fread($fd, 4)))) { + getid3_mp3::getOnlyMPEGaudioInfo($fd, $ThisFileInfo, $ThisFileInfo['avdataoffset'], false); + if (isset($ThisFileInfo['mpeg']['audio'])) { + $ThisFileInfo['audio']['dataformat'] = 'mp3'; + $ThisFileInfo['audio']['codec'] = (!empty($ThisFileInfo['mpeg']['audio']['encoder']) ? $ThisFileInfo['mpeg']['audio']['encoder'] : (!empty($ThisFileInfo['mpeg']['audio']['codec']) ? $ThisFileInfo['mpeg']['audio']['codec'] : (!empty($ThisFileInfo['mpeg']['audio']['LAME']) ? 'LAME' :'mp3'))); + $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['mpeg']['audio']['sample_rate']; + $ThisFileInfo['audio']['channels'] = $ThisFileInfo['mpeg']['audio']['channels']; + $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['bitrate']; + $ThisFileInfo['audio']['bitrate_mode'] = strtolower($ThisFileInfo['mpeg']['audio']['bitrate_mode']); + $ThisFileInfo['bitrate'] = $ThisFileInfo['audio']['bitrate']; + } + } + $ThisFileInfo['avdataend'] = $OldAVDataEnd; + unset($OldAVDataEnd); + + } + break; + + case 'free': // FREE space atom + case 'skip': // SKIP atom + case 'wide': // 64-bit expansion placeholder atom + // 'free', 'skip' and 'wide' are just padding, contains no useful data at all + break; + + default: + $atomHierarchy = array(); + $ThisFileInfo['quicktime'][$atomname] = $this->QuicktimeParseAtom($atomname, $atomsize, fread($fd, $atomsize), $ThisFileInfo, $offset, $atomHierarchy, $ParseAllPossibleAtoms); + break; + } + + $offset += $atomsize; + $atomcounter++; + } + + if (!empty($ThisFileInfo['avdataend_tmp'])) { + // this value is assigned to a temp value and then erased because + // otherwise any atoms beyond the 'mdat' atom would not get parsed + $ThisFileInfo['avdataend'] = $ThisFileInfo['avdataend_tmp']; + unset($ThisFileInfo['avdataend_tmp']); + } + + if (!isset($ThisFileInfo['bitrate']) && isset($ThisFileInfo['playtime_seconds'])) { + $ThisFileInfo['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; + } + if (isset($ThisFileInfo['bitrate']) && !isset($ThisFileInfo['audio']['bitrate']) && !isset($ThisFileInfo['quicktime']['video'])) { + $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['bitrate']; + } + + if (($ThisFileInfo['audio']['dataformat'] == 'mp4') && empty($ThisFileInfo['video']['resolution_x'])) { + $ThisFileInfo['fileformat'] = 'mp4'; + $ThisFileInfo['mime_type'] = 'audio/mp4'; + unset($ThisFileInfo['video']['dataformat']); + } + + if (!$ReturnAtomData) { + unset($ThisFileInfo['quicktime']['moov']); + } + + if (empty($ThisFileInfo['audio']['dataformat']) && !empty($ThisFileInfo['quicktime']['audio'])) { + $ThisFileInfo['audio']['dataformat'] = 'quicktime'; + } + if (empty($ThisFileInfo['video']['dataformat']) && !empty($ThisFileInfo['quicktime']['video'])) { + $ThisFileInfo['video']['dataformat'] = 'quicktime'; + } + + return true; + } + + function QuicktimeParseAtom($atomname, $atomsize, $atomdata, &$ThisFileInfo, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) { + // http://developer.apple.com/techpubs/quicktime/qtdevdocs/APIREF/INDEX/atomalphaindex.htm + + array_push($atomHierarchy, $atomname); + $atomstructure['hierarchy'] = implode(' ', $atomHierarchy); + $atomstructure['name'] = $atomname; + $atomstructure['size'] = $atomsize; + $atomstructure['offset'] = $baseoffset; + + switch ($atomname) { + case 'moov': // MOVie container atom + case 'trak': // TRAcK container atom + case 'clip': // CLIPping container atom + case 'matt': // track MATTe container atom + case 'edts': // EDiTS container atom + case 'tref': // Track REFerence container atom + case 'mdia': // MeDIA container atom + case 'minf': // Media INFormation container atom + case 'dinf': // Data INFormation container atom + case 'udta': // User DaTA container atom + case 'stbl': // Sample TaBLe container atom + case 'cmov': // Compressed MOVie container atom + case 'rmra': // Reference Movie Record Atom + case 'rmda': // Reference Movie Descriptor Atom + case 'gmhd': // Generic Media info HeaDer atom (seen on QTVR) + $atomstructure['subatoms'] = $this->QuicktimeParseContainerAtom($atomdata, $ThisFileInfo, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); + break; + + + case '©cpy': + case '©day': + case '©dir': + case '©ed1': + case '©ed2': + case '©ed3': + case '©ed4': + case '©ed5': + case '©ed6': + case '©ed7': + case '©ed8': + case '©ed9': + case '©fmt': + case '©inf': + case '©prd': + case '©prf': + case '©req': + case '©src': + case '©wrt': + case '©nam': + case '©cmt': + case '©wrn': + case '©hst': + case '©mak': + case '©mod': + case '©PRD': + case '©swr': + case '©aut': + case '©ART': + case '©trk': + case '©alb': + case '©com': + case '©gen': + case '©ope': + case '©url': + case '©enc': + $atomstructure['data_length'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 2)); + $atomstructure['language_id'] = getid3_lib::BigEndian2Int(substr($atomdata, 2, 2)); + $atomstructure['data'] = substr($atomdata, 4); + + $atomstructure['language'] = $this->QuicktimeLanguageLookup($atomstructure['language_id']); + if (empty($ThisFileInfo['comments']['language']) || (!in_array($atomstructure['language'], $ThisFileInfo['comments']['language']))) { + $ThisFileInfo['comments']['language'][] = $atomstructure['language']; + } + $this->CopyToAppropriateCommentsSection($atomname, $atomstructure['data'], $ThisFileInfo); + break; + + + case 'play': // auto-PLAY atom + $atomstructure['autoplay'] = (bool) getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); + + $ThisFileInfo['quicktime']['autoplay'] = $atomstructure['autoplay']; + break; + + + case 'WLOC': // Window LOCation atom + $atomstructure['location_x'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 2)); + $atomstructure['location_y'] = getid3_lib::BigEndian2Int(substr($atomdata, 2, 2)); + break; + + + case 'LOOP': // LOOPing atom + case 'SelO': // play SELection Only atom + case 'AllF': // play ALL Frames atom + $atomstructure['data'] = getid3_lib::BigEndian2Int($atomdata); + break; + + + case 'name': // + case 'MCPS': // Media Cleaner PRo + case '@PRM': // adobe PReMiere version + case '@PRQ': // adobe PRemiere Quicktime version + $atomstructure['data'] = $atomdata; + break; + + + case 'cmvd': // Compressed MooV Data atom + // Code by ubergeekØubergeek*tv based on information from + // http://developer.apple.com/quicktime/icefloe/dispatch012.html + $atomstructure['unCompressedSize'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 4)); + + $CompressedFileData = substr($atomdata, 4); + if ($UncompressedHeader = @gzuncompress($CompressedFileData)) { + $atomstructure['subatoms'] = $this->QuicktimeParseContainerAtom($UncompressedHeader, $ThisFileInfo, 0, $atomHierarchy, $ParseAllPossibleAtoms); + } else { + $ThisFileInfo['warning'][] = 'Error decompressing compressed MOV atom at offset '.$atomstructure['offset']; + } + break; + + + case 'dcom': // Data COMpression atom + $atomstructure['compression_id'] = $atomdata; + $atomstructure['compression_text'] = $this->QuicktimeDCOMLookup($atomdata); + break; + + + case 'rdrf': // Reference movie Data ReFerence atom + $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); + $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); + $atomstructure['flags']['internal_data'] = (bool) ($atomstructure['flags_raw'] & 0x000001); + + $atomstructure['reference_type_name'] = substr($atomdata, 4, 4); + $atomstructure['reference_length'] = getid3_lib::BigEndian2Int(substr($atomdata, 8, 4)); + switch ($atomstructure['reference_type_name']) { + case 'url ': + $atomstructure['url'] = $this->NoNullString(substr($atomdata, 12)); + break; + + case 'alis': + $atomstructure['file_alias'] = substr($atomdata, 12); + break; + + case 'rsrc': + $atomstructure['resource_alias'] = substr($atomdata, 12); + break; + + default: + $atomstructure['data'] = substr($atomdata, 12); + break; + } + break; + + + case 'rmqu': // Reference Movie QUality atom + $atomstructure['movie_quality'] = getid3_lib::BigEndian2Int($atomdata); + break; + + + case 'rmcs': // Reference Movie Cpu Speed atom + $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); + $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); // hardcoded: 0x0000 + $atomstructure['cpu_speed_rating'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 2)); + break; + + + case 'rmvc': // Reference Movie Version Check atom + $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); + $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); // hardcoded: 0x0000 + $atomstructure['gestalt_selector'] = substr($atomdata, 4, 4); + $atomstructure['gestalt_value_mask'] = getid3_lib::BigEndian2Int(substr($atomdata, 8, 4)); + $atomstructure['gestalt_value'] = getid3_lib::BigEndian2Int(substr($atomdata, 12, 4)); + $atomstructure['gestalt_check_type'] = getid3_lib::BigEndian2Int(substr($atomdata, 14, 2)); + break; + + + case 'rmcd': // Reference Movie Component check atom + $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); + $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); // hardcoded: 0x0000 + $atomstructure['component_type'] = substr($atomdata, 4, 4); + $atomstructure['component_subtype'] = substr($atomdata, 8, 4); + $atomstructure['component_manufacturer'] = substr($atomdata, 12, 4); + $atomstructure['component_flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 16, 4)); + $atomstructure['component_flags_mask'] = getid3_lib::BigEndian2Int(substr($atomdata, 20, 4)); + $atomstructure['component_min_version'] = getid3_lib::BigEndian2Int(substr($atomdata, 24, 4)); + break; + + + case 'rmdr': // Reference Movie Data Rate atom + $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); + $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); // hardcoded: 0x0000 + $atomstructure['data_rate'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 4)); + + $atomstructure['data_rate_bps'] = $atomstructure['data_rate'] * 10; + break; + + + case 'rmla': // Reference Movie Language Atom + $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); + $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); // hardcoded: 0x0000 + $atomstructure['language_id'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 2)); + + $atomstructure['language'] = $this->QuicktimeLanguageLookup($atomstructure['language_id']); + if (empty($ThisFileInfo['comments']['language']) || (!in_array($atomstructure['language'], $ThisFileInfo['comments']['language']))) { + $ThisFileInfo['comments']['language'][] = $atomstructure['language']; + } + break; + + + case 'rmla': // Reference Movie Language Atom + $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); + $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); // hardcoded: 0x0000 + $atomstructure['track_id'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 2)); + break; + + + case 'ptv ': // Print To Video - defines a movie's full screen mode + // http://developer.apple.com/documentation/QuickTime/APIREF/SOURCESIV/at_ptv-_pg.htm + $atomstructure['display_size_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 2)); + $atomstructure['reserved_1'] = getid3_lib::BigEndian2Int(substr($atomdata, 2, 2)); // hardcoded: 0x0000 + $atomstructure['reserved_2'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 2)); // hardcoded: 0x0000 + $atomstructure['slide_show_flag'] = getid3_lib::BigEndian2Int(substr($atomdata, 6, 1)); + $atomstructure['play_on_open_flag'] = getid3_lib::BigEndian2Int(substr($atomdata, 7, 1)); + + $atomstructure['flags']['play_on_open'] = (bool) $atomstructure['play_on_open_flag']; + $atomstructure['flags']['slide_show'] = (bool) $atomstructure['slide_show_flag']; + + $ptv_lookup[0] = 'normal'; + $ptv_lookup[1] = 'double'; + $ptv_lookup[2] = 'half'; + $ptv_lookup[3] = 'full'; + $ptv_lookup[4] = 'current'; + if (isset($ptv_lookup[$atomstructure['display_size_raw']])) { + $atomstructure['display_size'] = $ptv_lookup[$atomstructure['display_size_raw']]; + } else { + $ThisFileInfo['warning'][] = 'unknown "ptv " display constant ('.$atomstructure['display_size_raw'].')'; + } + break; + + + case 'stsd': // Sample Table Sample Description atom + $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); + $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); // hardcoded: 0x0000 + $atomstructure['number_entries'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 4)); + $stsdEntriesDataOffset = 8; + for ($i = 0; $i < $atomstructure['number_entries']; $i++) { + $atomstructure['sample_description_table'][$i]['size'] = getid3_lib::BigEndian2Int(substr($atomdata, $stsdEntriesDataOffset, 4)); + $stsdEntriesDataOffset += 4; + $atomstructure['sample_description_table'][$i]['data_format'] = substr($atomdata, $stsdEntriesDataOffset, 4); + $stsdEntriesDataOffset += 4; + $atomstructure['sample_description_table'][$i]['reserved'] = getid3_lib::BigEndian2Int(substr($atomdata, $stsdEntriesDataOffset, 6)); + $stsdEntriesDataOffset += 6; + $atomstructure['sample_description_table'][$i]['reference_index'] = getid3_lib::BigEndian2Int(substr($atomdata, $stsdEntriesDataOffset, 2)); + $stsdEntriesDataOffset += 2; + $atomstructure['sample_description_table'][$i]['data'] = substr($atomdata, $stsdEntriesDataOffset, ($atomstructure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2)); + $stsdEntriesDataOffset += ($atomstructure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2); + + $atomstructure['sample_description_table'][$i]['encoder_version'] = getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 0, 2)); + $atomstructure['sample_description_table'][$i]['encoder_revision'] = getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 2, 2)); + $atomstructure['sample_description_table'][$i]['encoder_vendor'] = substr($atomstructure['sample_description_table'][$i]['data'], 4, 4); + + switch ($atomstructure['sample_description_table'][$i]['encoder_vendor']) { + + case "\x00\x00\x00\x00": + // audio atom + $atomstructure['sample_description_table'][$i]['audio_channels'] = getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 8, 2)); + $atomstructure['sample_description_table'][$i]['audio_bit_depth'] = getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 10, 2)); + $atomstructure['sample_description_table'][$i]['audio_compression_id'] = getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 12, 2)); + $atomstructure['sample_description_table'][$i]['audio_packet_size'] = getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 14, 2)); + $atomstructure['sample_description_table'][$i]['audio_sample_rate'] = getid3_lib::FixedPoint16_16(substr($atomstructure['sample_description_table'][$i]['data'], 16, 4)); + + switch ($atomstructure['sample_description_table'][$i]['data_format']) { + case 'mp4v': + $ThisFileInfo['fileformat'] = 'mp4'; + $ThisFileInfo['error'][] = 'This version ('.GETID3_VERSION.') of getID3() does not fully support MPEG-4 audio/video streams'; + break; + + case 'qtvr': + $ThisFileInfo['video']['dataformat'] = 'quicktimevr'; + break; + + case 'mp4a': + default: + $ThisFileInfo['quicktime']['audio']['codec'] = $this->QuicktimeAudioCodecLookup($atomstructure['sample_description_table'][$i]['data_format']); + $ThisFileInfo['quicktime']['audio']['sample_rate'] = $atomstructure['sample_description_table'][$i]['audio_sample_rate']; + $ThisFileInfo['quicktime']['audio']['channels'] = $atomstructure['sample_description_table'][$i]['audio_channels']; + $ThisFileInfo['quicktime']['audio']['bit_depth'] = $atomstructure['sample_description_table'][$i]['audio_bit_depth']; + $ThisFileInfo['audio']['codec'] = $ThisFileInfo['quicktime']['audio']['codec']; + $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['quicktime']['audio']['sample_rate']; + $ThisFileInfo['audio']['channels'] = $ThisFileInfo['quicktime']['audio']['channels']; + $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['quicktime']['audio']['bit_depth']; + switch ($atomstructure['sample_description_table'][$i]['data_format']) { + case 'raw ': // PCM + case 'alac': // Apple Lossless Audio Codec + $ThisFileInfo['audio']['lossless'] = true; + break; + default: + $ThisFileInfo['audio']['lossless'] = false; + break; + } + break; + } + break; + + default: + switch ($atomstructure['sample_description_table'][$i]['data_format']) { + case 'mp4s': + $ThisFileInfo['fileformat'] = 'mp4'; + break; + + default: + // video atom + $atomstructure['sample_description_table'][$i]['video_temporal_quality'] = getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 8, 4)); + $atomstructure['sample_description_table'][$i]['video_spatial_quality'] = getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 12, 4)); + $atomstructure['sample_description_table'][$i]['video_frame_width'] = getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 16, 2)); + $atomstructure['sample_description_table'][$i]['video_frame_height'] = getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 18, 2)); + $atomstructure['sample_description_table'][$i]['video_resolution_x'] = getid3_lib::FixedPoint16_16(substr($atomstructure['sample_description_table'][$i]['data'], 20, 4)); + $atomstructure['sample_description_table'][$i]['video_resolution_y'] = getid3_lib::FixedPoint16_16(substr($atomstructure['sample_description_table'][$i]['data'], 24, 4)); + $atomstructure['sample_description_table'][$i]['video_data_size'] = getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 28, 4)); + $atomstructure['sample_description_table'][$i]['video_frame_count'] = getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 32, 2)); + $atomstructure['sample_description_table'][$i]['video_encoder_name_len'] = getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 34, 1)); + $atomstructure['sample_description_table'][$i]['video_encoder_name'] = substr($atomstructure['sample_description_table'][$i]['data'], 35, $atomstructure['sample_description_table'][$i]['video_encoder_name_len']); + $atomstructure['sample_description_table'][$i]['video_pixel_color_depth'] = getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 66, 2)); + $atomstructure['sample_description_table'][$i]['video_color_table_id'] = getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 68, 2)); + + $atomstructure['sample_description_table'][$i]['video_pixel_color_type'] = (($atomstructure['sample_description_table'][$i]['video_pixel_color_depth'] > 32) ? 'grayscale' : 'color'); + $atomstructure['sample_description_table'][$i]['video_pixel_color_name'] = $this->QuicktimeColorNameLookup($atomstructure['sample_description_table'][$i]['video_pixel_color_depth']); + + if ($atomstructure['sample_description_table'][$i]['video_pixel_color_name'] != 'invalid') { + $ThisFileInfo['quicktime']['video']['codec_fourcc'] = $atomstructure['sample_description_table'][$i]['data_format']; + $ThisFileInfo['quicktime']['video']['codec_fourcc_lookup'] = $this->QuicktimeVideoCodecLookup($atomstructure['sample_description_table'][$i]['data_format']); + $ThisFileInfo['quicktime']['video']['codec'] = $atomstructure['sample_description_table'][$i]['video_encoder_name']; + $ThisFileInfo['quicktime']['video']['color_depth'] = $atomstructure['sample_description_table'][$i]['video_pixel_color_depth']; + $ThisFileInfo['quicktime']['video']['color_depth_name'] = $atomstructure['sample_description_table'][$i]['video_pixel_color_name']; + + $ThisFileInfo['video']['codec'] = $ThisFileInfo['quicktime']['video']['codec']; + $ThisFileInfo['video']['bits_per_sample'] = $ThisFileInfo['quicktime']['video']['color_depth']; + } + $ThisFileInfo['video']['lossless'] = false; + $ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1; + break; + } + break; + } + switch (strtolower($atomstructure['sample_description_table'][$i]['data_format'])) { + case 'mp4a': + $ThisFileInfo['audio']['dataformat'] = 'mp4'; + $ThisFileInfo['quicktime']['audio']['codec'] = 'mp4'; + break; + + case '3ivx': + case '3iv1': + case '3iv2': + $ThisFileInfo['video']['dataformat'] = '3ivx'; + break; + + case 'xvid': + $ThisFileInfo['video']['dataformat'] = 'xvid'; + break; + + case 'mp4v': + $ThisFileInfo['video']['dataformat'] = 'mpeg4'; + break; + + case 'divx': + case 'div1': + case 'div2': + case 'div3': + case 'div4': + case 'div5': + case 'div6': + $TDIVXileInfo['video']['dataformat'] = 'divx'; + break; + + default: + // do nothing + break; + } + unset($atomstructure['sample_description_table'][$i]['data']); + } + break; + + + case 'stts': // Sample Table Time-to-Sample atom + //if ($ParseAllPossibleAtoms) { + $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); + $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); // hardcoded: 0x0000 + $atomstructure['number_entries'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 4)); + $sttsEntriesDataOffset = 8; + $FrameRateCalculatorArray = array(); + for ($i = 0; $i < $atomstructure['number_entries']; $i++) { + $atomstructure['time_to_sample_table'][$i]['sample_count'] = getid3_lib::BigEndian2Int(substr($atomdata, $sttsEntriesDataOffset, 4)); + $sttsEntriesDataOffset += 4; + $atomstructure['time_to_sample_table'][$i]['sample_duration'] = getid3_lib::BigEndian2Int(substr($atomdata, $sttsEntriesDataOffset, 4)); + $sttsEntriesDataOffset += 4; + + if (!empty($ThisFileInfo['quicktime']['time_scale']) && (@$atomstructure['time_to_sample_table'][$i]['sample_duration'] > 0)) { + $stts_new_framerate = $ThisFileInfo['quicktime']['time_scale'] / $atomstructure['time_to_sample_table'][$i]['sample_duration']; + if ($stts_new_framerate <= 60) { + // some atoms have durations of "1" giving a very large framerate, which probably is not right + $ThisFileInfo['video']['frame_rate'] = max(@$ThisFileInfo['video']['frame_rate'], $stts_new_framerate); + } + } + //@$FrameRateCalculatorArray[($ThisFileInfo['quicktime']['time_scale'] / $atomstructure['time_to_sample_table'][$i]['sample_duration'])] += $atomstructure['time_to_sample_table'][$i]['sample_count']; + } + //$sttsFramesTotal = 0; + //$sttsSecondsTotal = 0; + //foreach ($FrameRateCalculatorArray as $frames_per_second => $frame_count) { + // if (($frames_per_second > 60) || ($frames_per_second < 1)) { + // // not video FPS information, probably audio information + // $sttsFramesTotal = 0; + // $sttsSecondsTotal = 0; + // break; + // } + // $sttsFramesTotal += $frame_count; + // $sttsSecondsTotal += $frame_count / $frames_per_second; + //} + //if (($sttsFramesTotal > 0) && ($sttsSecondsTotal > 0)) { + // if (($sttsFramesTotal / $sttsSecondsTotal) > @$ThisFileInfo['video']['frame_rate']) { + // $ThisFileInfo['video']['frame_rate'] = $sttsFramesTotal / $sttsSecondsTotal; + // } + //} + //} + break; + + + case 'stss': // Sample Table Sync Sample (key frames) atom + if ($ParseAllPossibleAtoms) { + $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); + $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); // hardcoded: 0x0000 + $atomstructure['number_entries'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 4)); + $stssEntriesDataOffset = 8; + for ($i = 0; $i < $atomstructure['number_entries']; $i++) { + $atomstructure['time_to_sample_table'][$i] = getid3_lib::BigEndian2Int(substr($atomdata, $stssEntriesDataOffset, 4)); + $stssEntriesDataOffset += 4; + } + } + break; + + + case 'stsc': // Sample Table Sample-to-Chunk atom + if ($ParseAllPossibleAtoms) { + $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); + $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); // hardcoded: 0x0000 + $atomstructure['number_entries'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 4)); + $stscEntriesDataOffset = 8; + for ($i = 0; $i < $atomstructure['number_entries']; $i++) { + $atomstructure['sample_to_chunk_table'][$i]['first_chunk'] = getid3_lib::BigEndian2Int(substr($atomdata, $stscEntriesDataOffset, 4)); + $stscEntriesDataOffset += 4; + $atomstructure['sample_to_chunk_table'][$i]['samples_per_chunk'] = getid3_lib::BigEndian2Int(substr($atomdata, $stscEntriesDataOffset, 4)); + $stscEntriesDataOffset += 4; + $atomstructure['sample_to_chunk_table'][$i]['sample_description'] = getid3_lib::BigEndian2Int(substr($atomdata, $stscEntriesDataOffset, 4)); + $stscEntriesDataOffset += 4; + } + } + break; + + + case 'stsz': // Sample Table SiZe atom + if ($ParseAllPossibleAtoms) { + $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); + $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); // hardcoded: 0x0000 + $atomstructure['sample_size'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 4)); + $atomstructure['number_entries'] = getid3_lib::BigEndian2Int(substr($atomdata, 8, 4)); + $stszEntriesDataOffset = 12; + if ($atomstructure['sample_size'] == 0) { + for ($i = 0; $i < $atomstructure['number_entries']; $i++) { + $atomstructure['sample_size_table'][$i] = getid3_lib::BigEndian2Int(substr($atomdata, $stszEntriesDataOffset, 4)); + $stszEntriesDataOffset += 4; + } + } + } + break; + + + case 'stco': // Sample Table Chunk Offset atom + if ($ParseAllPossibleAtoms) { + $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); + $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); // hardcoded: 0x0000 + $atomstructure['number_entries'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 4)); + $stcoEntriesDataOffset = 8; + for ($i = 0; $i < $atomstructure['number_entries']; $i++) { + $atomstructure['chunk_offset_table'][$i] = getid3_lib::BigEndian2Int(substr($atomdata, $stcoEntriesDataOffset, 4)); + $stcoEntriesDataOffset += 4; + } + } + break; + + + case 'dref': // Data REFerence atom + $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); + $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); // hardcoded: 0x0000 + $atomstructure['number_entries'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 4)); + $drefDataOffset = 8; + for ($i = 0; $i < $atomstructure['number_entries']; $i++) { + $atomstructure['data_references'][$i]['size'] = getid3_lib::BigEndian2Int(substr($atomdata, $drefDataOffset, 4)); + $drefDataOffset += 4; + $atomstructure['data_references'][$i]['type'] = substr($atomdata, $drefDataOffset, 4); + $drefDataOffset += 4; + $atomstructure['data_references'][$i]['version'] = getid3_lib::BigEndian2Int(substr($atomdata, $drefDataOffset, 1)); + $drefDataOffset += 1; + $atomstructure['data_references'][$i]['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, $drefDataOffset, 3)); // hardcoded: 0x0000 + $drefDataOffset += 3; + $atomstructure['data_references'][$i]['data'] = substr($atomdata, $drefDataOffset, ($atomstructure['data_references'][$i]['size'] - 4 - 4 - 1 - 3)); + $drefDataOffset += ($atomstructure['data_references'][$i]['size'] - 4 - 4 - 1 - 3); + + $atomstructure['data_references'][$i]['flags']['self_reference'] = (bool) ($atomstructure['data_references'][$i]['flags_raw'] & 0x001); + } + break; + + + case 'gmin': // base Media INformation atom + $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); + $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); // hardcoded: 0x0000 + $atomstructure['graphics_mode'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 2)); + $atomstructure['opcolor_red'] = getid3_lib::BigEndian2Int(substr($atomdata, 6, 2)); + $atomstructure['opcolor_green'] = getid3_lib::BigEndian2Int(substr($atomdata, 8, 2)); + $atomstructure['opcolor_blue'] = getid3_lib::BigEndian2Int(substr($atomdata, 10, 2)); + $atomstructure['balance'] = getid3_lib::BigEndian2Int(substr($atomdata, 12, 2)); + $atomstructure['reserved'] = getid3_lib::BigEndian2Int(substr($atomdata, 14, 2)); + break; + + + case 'smhd': // Sound Media information HeaDer atom + $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); + $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); // hardcoded: 0x0000 + $atomstructure['balance'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 2)); + $atomstructure['reserved'] = getid3_lib::BigEndian2Int(substr($atomdata, 6, 2)); + break; + + + case 'vmhd': // Video Media information HeaDer atom + $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); + $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); + $atomstructure['graphics_mode'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 2)); + $atomstructure['opcolor_red'] = getid3_lib::BigEndian2Int(substr($atomdata, 6, 2)); + $atomstructure['opcolor_green'] = getid3_lib::BigEndian2Int(substr($atomdata, 8, 2)); + $atomstructure['opcolor_blue'] = getid3_lib::BigEndian2Int(substr($atomdata, 10, 2)); + + $atomstructure['flags']['no_lean_ahead'] = (bool) ($atomstructure['flags_raw'] & 0x001); + break; + + + case 'hdlr': // HanDLeR reference atom + $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); + $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); // hardcoded: 0x0000 + $atomstructure['component_type'] = substr($atomdata, 4, 4); + $atomstructure['component_subtype'] = substr($atomdata, 8, 4); + $atomstructure['component_manufacturer'] = substr($atomdata, 12, 4); + $atomstructure['component_flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 16, 4)); + $atomstructure['component_flags_mask'] = getid3_lib::BigEndian2Int(substr($atomdata, 20, 4)); + $atomstructure['component_name'] = $this->Pascal2String(substr($atomdata, 24)); + + if (($atomstructure['component_subtype'] == 'STpn') && ($atomstructure['component_manufacturer'] == 'zzzz')) { + $ThisFileInfo['video']['dataformat'] = 'quicktimevr'; + } + break; + + + case 'mdhd': // MeDia HeaDer atom + $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); + $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); // hardcoded: 0x0000 + $atomstructure['creation_time'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 4)); + $atomstructure['modify_time'] = getid3_lib::BigEndian2Int(substr($atomdata, 8, 4)); + $atomstructure['time_scale'] = getid3_lib::BigEndian2Int(substr($atomdata, 12, 4)); + $atomstructure['duration'] = getid3_lib::BigEndian2Int(substr($atomdata, 16, 4)); + $atomstructure['language_id'] = getid3_lib::BigEndian2Int(substr($atomdata, 20, 2)); + $atomstructure['quality'] = getid3_lib::BigEndian2Int(substr($atomdata, 22, 2)); + + if ($atomstructure['time_scale'] == 0) { + $ThisFileInfo['error'][] = 'Corrupt Quicktime file: mdhd.time_scale == zero'; + return false; + } + $atomstructure['creation_time_unix'] = getid3_lib::DateMac2Unix($atomstructure['creation_time']); + $atomstructure['modify_time_unix'] = getid3_lib::DateMac2Unix($atomstructure['modify_time']); + $atomstructure['playtime_seconds'] = $atomstructure['duration'] / $atomstructure['time_scale']; + $atomstructure['language'] = $this->QuicktimeLanguageLookup($atomstructure['language_id']); + if (empty($ThisFileInfo['comments']['language']) || (!in_array($atomstructure['language'], $ThisFileInfo['comments']['language']))) { + $ThisFileInfo['comments']['language'][] = $atomstructure['language']; + } + break; + + + case 'pnot': // Preview atom + $atomstructure['modification_date'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 4)); // "standard Macintosh format" + $atomstructure['version_number'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 2)); // hardcoded: 0x00 + $atomstructure['atom_type'] = substr($atomdata, 6, 4); // usually: 'PICT' + $atomstructure['atom_index'] = getid3_lib::BigEndian2Int(substr($atomdata, 10, 2)); // usually: 0x01 + + $atomstructure['modification_date_unix'] = getid3_lib::DateMac2Unix($atomstructure['modification_date']); + break; + + + case 'crgn': // Clipping ReGioN atom + $atomstructure['region_size'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 2)); // The Region size, Region boundary box, + $atomstructure['boundary_box'] = getid3_lib::BigEndian2Int(substr($atomdata, 2, 8)); // and Clipping region data fields + $atomstructure['clipping_data'] = substr($atomdata, 10); // constitute a QuickDraw region. + break; + + + case 'load': // track LOAD settings atom + $atomstructure['preload_start_time'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 4)); + $atomstructure['preload_duration'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 4)); + $atomstructure['preload_flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 8, 4)); + $atomstructure['default_hints_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 12, 4)); + + $atomstructure['default_hints']['double_buffer'] = (bool) ($atomstructure['default_hints_raw'] & 0x0020); + $atomstructure['default_hints']['high_quality'] = (bool) ($atomstructure['default_hints_raw'] & 0x0100); + break; + + + case 'tmcd': // TiMe CoDe atom + case 'chap': // CHAPter list atom + case 'sync': // SYNChronization atom + case 'scpt': // tranSCriPT atom + case 'ssrc': // non-primary SouRCe atom + for ($i = 0; $i < (strlen($atomdata) % 4); $i++) { + $atomstructure['track_id'][$i] = getid3_lib::BigEndian2Int(substr($atomdata, $i * 4, 4)); + } + break; + + + case 'elst': // Edit LiST atom + $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); + $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); // hardcoded: 0x0000 + $atomstructure['number_entries'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 4)); + for ($i = 0; $i < $atomstructure['number_entries']; $i++ ) { + $atomstructure['edit_list'][$i]['track_duration'] = getid3_lib::BigEndian2Int(substr($atomdata, 8 + ($i * 12) + 0, 4)); + $atomstructure['edit_list'][$i]['media_time'] = getid3_lib::BigEndian2Int(substr($atomdata, 8 + ($i * 12) + 4, 4)); + $atomstructure['edit_list'][$i]['media_rate'] = getid3_lib::FixedPoint16_16(substr($atomdata, 8 + ($i * 12) + 8, 4)); + } + break; + + + case 'kmat': // compressed MATte atom + $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); + $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); // hardcoded: 0x0000 + $atomstructure['matte_data_raw'] = substr($atomdata, 4); + break; + + + case 'ctab': // Color TABle atom + $atomstructure['color_table_seed'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 4)); // hardcoded: 0x00000000 + $atomstructure['color_table_flags'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 2)); // hardcoded: 0x8000 + $atomstructure['color_table_size'] = getid3_lib::BigEndian2Int(substr($atomdata, 6, 2)) + 1; + for ($colortableentry = 0; $colortableentry < $atomstructure['color_table_size']; $colortableentry++) { + $atomstructure['color_table'][$colortableentry]['alpha'] = getid3_lib::BigEndian2Int(substr($atomdata, 8 + ($colortableentry * 8) + 0, 2)); + $atomstructure['color_table'][$colortableentry]['red'] = getid3_lib::BigEndian2Int(substr($atomdata, 8 + ($colortableentry * 8) + 2, 2)); + $atomstructure['color_table'][$colortableentry]['green'] = getid3_lib::BigEndian2Int(substr($atomdata, 8 + ($colortableentry * 8) + 4, 2)); + $atomstructure['color_table'][$colortableentry]['blue'] = getid3_lib::BigEndian2Int(substr($atomdata, 8 + ($colortableentry * 8) + 6, 2)); + } + break; + + + case 'mvhd': // MoVie HeaDer atom + $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); + $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); + $atomstructure['creation_time'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 4)); + $atomstructure['modify_time'] = getid3_lib::BigEndian2Int(substr($atomdata, 8, 4)); + $atomstructure['time_scale'] = getid3_lib::BigEndian2Int(substr($atomdata, 12, 4)); + $atomstructure['duration'] = getid3_lib::BigEndian2Int(substr($atomdata, 16, 4)); + $atomstructure['preferred_rate'] = getid3_lib::FixedPoint16_16(substr($atomdata, 20, 4)); + $atomstructure['preferred_volume'] = getid3_lib::FixedPoint8_8(substr($atomdata, 24, 2)); + $atomstructure['reserved'] = substr($atomdata, 26, 10); + $atomstructure['matrix_a'] = getid3_lib::FixedPoint16_16(substr($atomdata, 36, 4)); + $atomstructure['matrix_b'] = getid3_lib::FixedPoint16_16(substr($atomdata, 40, 4)); + $atomstructure['matrix_u'] = getid3_lib::FixedPoint2_30(substr($atomdata, 44, 4)); + $atomstructure['matrix_c'] = getid3_lib::FixedPoint16_16(substr($atomdata, 48, 4)); + $atomstructure['matrix_d'] = getid3_lib::FixedPoint16_16(substr($atomdata, 52, 4)); + $atomstructure['matrix_v'] = getid3_lib::FixedPoint2_30(substr($atomdata, 56, 4)); + $atomstructure['matrix_x'] = getid3_lib::FixedPoint16_16(substr($atomdata, 60, 4)); + $atomstructure['matrix_y'] = getid3_lib::FixedPoint16_16(substr($atomdata, 64, 4)); + $atomstructure['matrix_w'] = getid3_lib::FixedPoint2_30(substr($atomdata, 68, 4)); + $atomstructure['preview_time'] = getid3_lib::BigEndian2Int(substr($atomdata, 72, 4)); + $atomstructure['preview_duration'] = getid3_lib::BigEndian2Int(substr($atomdata, 76, 4)); + $atomstructure['poster_time'] = getid3_lib::BigEndian2Int(substr($atomdata, 80, 4)); + $atomstructure['selection_time'] = getid3_lib::BigEndian2Int(substr($atomdata, 84, 4)); + $atomstructure['selection_duration'] = getid3_lib::BigEndian2Int(substr($atomdata, 88, 4)); + $atomstructure['current_time'] = getid3_lib::BigEndian2Int(substr($atomdata, 92, 4)); + $atomstructure['next_track_id'] = getid3_lib::BigEndian2Int(substr($atomdata, 96, 4)); + + if ($atomstructure['time_scale'] == 0) { + $ThisFileInfo['error'][] = 'Corrupt Quicktime file: mvhd.time_scale == zero'; + return false; + } + $atomstructure['creation_time_unix'] = getid3_lib::DateMac2Unix($atomstructure['creation_time']); + $atomstructure['modify_time_unix'] = getid3_lib::DateMac2Unix($atomstructure['modify_time']); + $ThisFileInfo['quicktime']['time_scale'] = $atomstructure['time_scale']; + $ThisFileInfo['quicktime']['display_scale'] = $atomstructure['matrix_a']; + $ThisFileInfo['playtime_seconds'] = $atomstructure['duration'] / $atomstructure['time_scale']; + break; + + + case 'tkhd': // TracK HeaDer atom + $atomstructure['version'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 1)); + $atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata, 1, 3)); + $atomstructure['creation_time'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 4)); + $atomstructure['modify_time'] = getid3_lib::BigEndian2Int(substr($atomdata, 8, 4)); + $atomstructure['trackid'] = getid3_lib::BigEndian2Int(substr($atomdata, 12, 4)); + $atomstructure['reserved1'] = getid3_lib::BigEndian2Int(substr($atomdata, 16, 4)); + $atomstructure['duration'] = getid3_lib::BigEndian2Int(substr($atomdata, 20, 4)); + $atomstructure['reserved2'] = getid3_lib::BigEndian2Int(substr($atomdata, 24, 8)); + $atomstructure['layer'] = getid3_lib::BigEndian2Int(substr($atomdata, 32, 2)); + $atomstructure['alternate_group'] = getid3_lib::BigEndian2Int(substr($atomdata, 34, 2)); + $atomstructure['volume'] = getid3_lib::FixedPoint8_8(substr($atomdata, 36, 2)); + $atomstructure['reserved3'] = getid3_lib::BigEndian2Int(substr($atomdata, 38, 2)); + $atomstructure['matrix_a'] = getid3_lib::FixedPoint16_16(substr($atomdata, 40, 4)); + $atomstructure['matrix_b'] = getid3_lib::FixedPoint16_16(substr($atomdata, 44, 4)); + $atomstructure['matrix_u'] = getid3_lib::FixedPoint16_16(substr($atomdata, 48, 4)); + $atomstructure['matrix_c'] = getid3_lib::FixedPoint16_16(substr($atomdata, 52, 4)); + $atomstructure['matrix_v'] = getid3_lib::FixedPoint16_16(substr($atomdata, 56, 4)); + $atomstructure['matrix_d'] = getid3_lib::FixedPoint16_16(substr($atomdata, 60, 4)); + $atomstructure['matrix_x'] = getid3_lib::FixedPoint2_30(substr($atomdata, 64, 4)); + $atomstructure['matrix_y'] = getid3_lib::FixedPoint2_30(substr($atomdata, 68, 4)); + $atomstructure['matrix_w'] = getid3_lib::FixedPoint2_30(substr($atomdata, 72, 4)); + $atomstructure['width'] = getid3_lib::FixedPoint16_16(substr($atomdata, 76, 4)); + $atomstructure['height'] = getid3_lib::FixedPoint16_16(substr($atomdata, 80, 4)); + + $atomstructure['flags']['enabled'] = (bool) ($atomstructure['flags_raw'] & 0x0001); + $atomstructure['flags']['in_movie'] = (bool) ($atomstructure['flags_raw'] & 0x0002); + $atomstructure['flags']['in_preview'] = (bool) ($atomstructure['flags_raw'] & 0x0004); + $atomstructure['flags']['in_poster'] = (bool) ($atomstructure['flags_raw'] & 0x0008); + $atomstructure['creation_time_unix'] = getid3_lib::DateMac2Unix($atomstructure['creation_time']); + $atomstructure['modify_time_unix'] = getid3_lib::DateMac2Unix($atomstructure['modify_time']); + + if (!isset($ThisFileInfo['video']['resolution_x']) || !isset($ThisFileInfo['video']['resolution_y'])) { + $ThisFileInfo['video']['resolution_x'] = $atomstructure['width']; + $ThisFileInfo['video']['resolution_y'] = $atomstructure['height']; + } + if ($atomstructure['flags']['enabled'] == 1) { + $ThisFileInfo['video']['resolution_x'] = max($ThisFileInfo['video']['resolution_x'], $atomstructure['width']); + $ThisFileInfo['video']['resolution_y'] = max($ThisFileInfo['video']['resolution_y'], $atomstructure['height']); + } + if (!empty($ThisFileInfo['video']['resolution_x']) && !empty($ThisFileInfo['video']['resolution_y'])) { + $ThisFileInfo['quicktime']['video']['resolution_x'] = $ThisFileInfo['video']['resolution_x']; + $ThisFileInfo['quicktime']['video']['resolution_y'] = $ThisFileInfo['video']['resolution_y']; + } else { + unset($ThisFileInfo['video']['resolution_x']); + unset($ThisFileInfo['video']['resolution_y']); + unset($ThisFileInfo['quicktime']['video']); + } + break; + + + case 'meta': // METAdata atom + // http://www.geocities.com/xhelmboyx/quicktime/formats/qti-layout.txt + $NextTagPosition = strpos($atomdata, '©'); + while ($NextTagPosition < strlen($atomdata)) { + $metaItemSize = getid3_lib::BigEndian2Int(substr($atomdata, $NextTagPosition - 4, 4)) - 4; + if ($metaItemSize == -4) { + break; + } + $metaItemRaw = substr($atomdata, $NextTagPosition, $metaItemSize); + $metaItemKey = substr($metaItemRaw, 0, 4); + $metaItemData = substr($metaItemRaw, 20); + $NextTagPosition += $metaItemSize + 4; + + $this->CopyToAppropriateCommentsSection($metaItemKey, $metaItemData, $ThisFileInfo); + } + break; + + case 'ftyp': // FileTYPe (?) atom (for MP4 it seems) + $atomstructure['signature'] = substr($atomdata, 0, 4); + $atomstructure['unknown_1'] = getid3_lib::BigEndian2Int(substr($atomdata, 4, 4)); + $atomstructure['fourcc'] = substr($atomdata, 8, 4); + break; + + case 'mdat': // Media DATa atom + case 'free': // FREE space atom + case 'skip': // SKIP atom + case 'wide': // 64-bit expansion placeholder atom + // 'mdat' data is too big to deal with, contains no useful metadata + // 'free', 'skip' and 'wide' are just padding, contains no useful data at all + + // When writing QuickTime files, it is sometimes necessary to update an atom's size. + // It is impossible to update a 32-bit atom to a 64-bit atom since the 32-bit atom + // is only 8 bytes in size, and the 64-bit atom requires 16 bytes. Therefore, QuickTime + // puts an 8-byte placeholder atom before any atoms it may have to update the size of. + // In this way, if the atom needs to be converted from a 32-bit to a 64-bit atom, the + // placeholder atom can be overwritten to obtain the necessary 8 extra bytes. + // The placeholder atom has a type of kWideAtomPlaceholderType ( 'wide' ). + break; + + + case 'nsav': // NoSAVe atom + // http://developer.apple.com/technotes/tn/tn2038.html + $atomstructure['data'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 4)); + break; + + case 'ctyp': // Controller TYPe atom (seen on QTVR) + // http://homepages.slingshot.co.nz/~helmboy/quicktime/formats/qtm-layout.txt + // some controller names are: + // 0x00 + 'std' for linear movie + // 'none' for no controls + $atomstructure['ctyp'] = substr($atomdata, 0, 4); + switch ($atomstructure['ctyp']) { + case 'qtvr': + $ThisFileInfo['video']['dataformat'] = 'quicktimevr'; + break; + } + break; + + case 'pano': // PANOrama track (seen on QTVR) + $atomstructure['pano'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 4)); + break; + + case 'hint': // HINT track + case 'hinf': // + case 'hinv': // + case 'hnti': // + $ThisFileInfo['quicktime']['hinting'] = true; + break; + + case 'imgt': // IMaGe Track reference (kQTVRImageTrackRefType) (seen on QTVR) + for ($i = 0; $i < ($atomstructure['size'] - 8); $i += 4) { + $atomstructure['imgt'][] = getid3_lib::BigEndian2Int(substr($atomdata, $i, 4)); + } + break; + + case 'FXTC': // Something to do with Adobe After Effects (?) + case 'PrmA': + case 'code': + case 'FIEL': // this is NOT "fiel" (Field Ordering) as describe here: http://developer.apple.com/documentation/QuickTime/QTFF/QTFFChap3/chapter_4_section_2.html + // Observed-but-not-handled atom types are just listed here + // to prevent warnings being generated + $atomstructure['data'] = $atomdata; + break; + + default: + $ThisFileInfo['warning'][] = 'Unknown QuickTime atom type: "'.$atomname.'" at offset '.$baseoffset; + $atomstructure['data'] = $atomdata; + break; + } + array_pop($atomHierarchy); + return $atomstructure; + } + + function QuicktimeParseContainerAtom($atomdata, &$ThisFileInfo, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) { + $atomstructure = false; + $subatomoffset = 0; + $subatomcounter = 0; + if ((strlen($atomdata) == 4) && (getid3_lib::BigEndian2Int($atomdata) == 0x00000000)) { + return false; + } + while ($subatomoffset < strlen($atomdata)) { + $subatomsize = getid3_lib::BigEndian2Int(substr($atomdata, $subatomoffset + 0, 4)); + $subatomname = substr($atomdata, $subatomoffset + 4, 4); + $subatomdata = substr($atomdata, $subatomoffset + 8, $subatomsize - 8); + if ($subatomsize == 0) { + // Furthermore, for historical reasons the list of atoms is optionally + // terminated by a 32-bit integer set to 0. If you are writing a program + // to read user data atoms, you should allow for the terminating 0. + return $atomstructure; + } + + $atomstructure[$subatomcounter] = $this->QuicktimeParseAtom($subatomname, $subatomsize, $subatomdata, $ThisFileInfo, $baseoffset + $subatomoffset, $atomHierarchy, $ParseAllPossibleAtoms); + + $subatomoffset += $subatomsize; + $subatomcounter++; + } + return $atomstructure; + } + + + function QuicktimeLanguageLookup($languageid) { + static $QuicktimeLanguageLookup = array(); + if (empty($QuicktimeLanguageLookup)) { + $QuicktimeLanguageLookup[0] = 'English'; + $QuicktimeLanguageLookup[1] = 'French'; + $QuicktimeLanguageLookup[2] = 'German'; + $QuicktimeLanguageLookup[3] = 'Italian'; + $QuicktimeLanguageLookup[4] = 'Dutch'; + $QuicktimeLanguageLookup[5] = 'Swedish'; + $QuicktimeLanguageLookup[6] = 'Spanish'; + $QuicktimeLanguageLookup[7] = 'Danish'; + $QuicktimeLanguageLookup[8] = 'Portuguese'; + $QuicktimeLanguageLookup[9] = 'Norwegian'; + $QuicktimeLanguageLookup[10] = 'Hebrew'; + $QuicktimeLanguageLookup[11] = 'Japanese'; + $QuicktimeLanguageLookup[12] = 'Arabic'; + $QuicktimeLanguageLookup[13] = 'Finnish'; + $QuicktimeLanguageLookup[14] = 'Greek'; + $QuicktimeLanguageLookup[15] = 'Icelandic'; + $QuicktimeLanguageLookup[16] = 'Maltese'; + $QuicktimeLanguageLookup[17] = 'Turkish'; + $QuicktimeLanguageLookup[18] = 'Croatian'; + $QuicktimeLanguageLookup[19] = 'Chinese (Traditional)'; + $QuicktimeLanguageLookup[20] = 'Urdu'; + $QuicktimeLanguageLookup[21] = 'Hindi'; + $QuicktimeLanguageLookup[22] = 'Thai'; + $QuicktimeLanguageLookup[23] = 'Korean'; + $QuicktimeLanguageLookup[24] = 'Lithuanian'; + $QuicktimeLanguageLookup[25] = 'Polish'; + $QuicktimeLanguageLookup[26] = 'Hungarian'; + $QuicktimeLanguageLookup[27] = 'Estonian'; + $QuicktimeLanguageLookup[28] = 'Lettish'; + $QuicktimeLanguageLookup[28] = 'Latvian'; + $QuicktimeLanguageLookup[29] = 'Saamisk'; + $QuicktimeLanguageLookup[29] = 'Lappish'; + $QuicktimeLanguageLookup[30] = 'Faeroese'; + $QuicktimeLanguageLookup[31] = 'Farsi'; + $QuicktimeLanguageLookup[31] = 'Persian'; + $QuicktimeLanguageLookup[32] = 'Russian'; + $QuicktimeLanguageLookup[33] = 'Chinese (Simplified)'; + $QuicktimeLanguageLookup[34] = 'Flemish'; + $QuicktimeLanguageLookup[35] = 'Irish'; + $QuicktimeLanguageLookup[36] = 'Albanian'; + $QuicktimeLanguageLookup[37] = 'Romanian'; + $QuicktimeLanguageLookup[38] = 'Czech'; + $QuicktimeLanguageLookup[39] = 'Slovak'; + $QuicktimeLanguageLookup[40] = 'Slovenian'; + $QuicktimeLanguageLookup[41] = 'Yiddish'; + $QuicktimeLanguageLookup[42] = 'Serbian'; + $QuicktimeLanguageLookup[43] = 'Macedonian'; + $QuicktimeLanguageLookup[44] = 'Bulgarian'; + $QuicktimeLanguageLookup[45] = 'Ukrainian'; + $QuicktimeLanguageLookup[46] = 'Byelorussian'; + $QuicktimeLanguageLookup[47] = 'Uzbek'; + $QuicktimeLanguageLookup[48] = 'Kazakh'; + $QuicktimeLanguageLookup[49] = 'Azerbaijani'; + $QuicktimeLanguageLookup[50] = 'AzerbaijanAr'; + $QuicktimeLanguageLookup[51] = 'Armenian'; + $QuicktimeLanguageLookup[52] = 'Georgian'; + $QuicktimeLanguageLookup[53] = 'Moldavian'; + $QuicktimeLanguageLookup[54] = 'Kirghiz'; + $QuicktimeLanguageLookup[55] = 'Tajiki'; + $QuicktimeLanguageLookup[56] = 'Turkmen'; + $QuicktimeLanguageLookup[57] = 'Mongolian'; + $QuicktimeLanguageLookup[58] = 'MongolianCyr'; + $QuicktimeLanguageLookup[59] = 'Pashto'; + $QuicktimeLanguageLookup[60] = 'Kurdish'; + $QuicktimeLanguageLookup[61] = 'Kashmiri'; + $QuicktimeLanguageLookup[62] = 'Sindhi'; + $QuicktimeLanguageLookup[63] = 'Tibetan'; + $QuicktimeLanguageLookup[64] = 'Nepali'; + $QuicktimeLanguageLookup[65] = 'Sanskrit'; + $QuicktimeLanguageLookup[66] = 'Marathi'; + $QuicktimeLanguageLookup[67] = 'Bengali'; + $QuicktimeLanguageLookup[68] = 'Assamese'; + $QuicktimeLanguageLookup[69] = 'Gujarati'; + $QuicktimeLanguageLookup[70] = 'Punjabi'; + $QuicktimeLanguageLookup[71] = 'Oriya'; + $QuicktimeLanguageLookup[72] = 'Malayalam'; + $QuicktimeLanguageLookup[73] = 'Kannada'; + $QuicktimeLanguageLookup[74] = 'Tamil'; + $QuicktimeLanguageLookup[75] = 'Telugu'; + $QuicktimeLanguageLookup[76] = 'Sinhalese'; + $QuicktimeLanguageLookup[77] = 'Burmese'; + $QuicktimeLanguageLookup[78] = 'Khmer'; + $QuicktimeLanguageLookup[79] = 'Lao'; + $QuicktimeLanguageLookup[80] = 'Vietnamese'; + $QuicktimeLanguageLookup[81] = 'Indonesian'; + $QuicktimeLanguageLookup[82] = 'Tagalog'; + $QuicktimeLanguageLookup[83] = 'MalayRoman'; + $QuicktimeLanguageLookup[84] = 'MalayArabic'; + $QuicktimeLanguageLookup[85] = 'Amharic'; + $QuicktimeLanguageLookup[86] = 'Tigrinya'; + $QuicktimeLanguageLookup[87] = 'Galla'; + $QuicktimeLanguageLookup[87] = 'Oromo'; + $QuicktimeLanguageLookup[88] = 'Somali'; + $QuicktimeLanguageLookup[89] = 'Swahili'; + $QuicktimeLanguageLookup[90] = 'Ruanda'; + $QuicktimeLanguageLookup[91] = 'Rundi'; + $QuicktimeLanguageLookup[92] = 'Chewa'; + $QuicktimeLanguageLookup[93] = 'Malagasy'; + $QuicktimeLanguageLookup[94] = 'Esperanto'; + $QuicktimeLanguageLookup[128] = 'Welsh'; + $QuicktimeLanguageLookup[129] = 'Basque'; + $QuicktimeLanguageLookup[130] = 'Catalan'; + $QuicktimeLanguageLookup[131] = 'Latin'; + $QuicktimeLanguageLookup[132] = 'Quechua'; + $QuicktimeLanguageLookup[133] = 'Guarani'; + $QuicktimeLanguageLookup[134] = 'Aymara'; + $QuicktimeLanguageLookup[135] = 'Tatar'; + $QuicktimeLanguageLookup[136] = 'Uighur'; + $QuicktimeLanguageLookup[137] = 'Dzongkha'; + $QuicktimeLanguageLookup[138] = 'JavaneseRom'; + } + return (isset($QuicktimeLanguageLookup[$languageid]) ? $QuicktimeLanguageLookup[$languageid] : 'invalid'); + } + + function QuicktimeVideoCodecLookup($codecid) { + static $QuicktimeVideoCodecLookup = array(); + if (empty($QuicktimeVideoCodecLookup)) { + $QuicktimeVideoCodecLookup['3IVX'] = '3ivx MPEG-4'; + $QuicktimeVideoCodecLookup['3IV1'] = '3ivx MPEG-4 v1'; + $QuicktimeVideoCodecLookup['3IV2'] = '3ivx MPEG-4 v2'; + $QuicktimeVideoCodecLookup['avr '] = 'AVR-JPEG'; + $QuicktimeVideoCodecLookup['base'] = 'Base'; + $QuicktimeVideoCodecLookup['WRLE'] = 'BMP'; + $QuicktimeVideoCodecLookup['cvid'] = 'Cinepak'; + $QuicktimeVideoCodecLookup['clou'] = 'Cloud'; + $QuicktimeVideoCodecLookup['cmyk'] = 'CMYK'; + $QuicktimeVideoCodecLookup['yuv2'] = 'ComponentVideo'; + $QuicktimeVideoCodecLookup['yuvu'] = 'ComponentVideoSigned'; + $QuicktimeVideoCodecLookup['yuvs'] = 'ComponentVideoUnsigned'; + $QuicktimeVideoCodecLookup['dvc '] = 'DVC-NTSC'; + $QuicktimeVideoCodecLookup['dvcp'] = 'DVC-PAL'; + $QuicktimeVideoCodecLookup['dvpn'] = 'DVCPro-NTSC'; + $QuicktimeVideoCodecLookup['dvpp'] = 'DVCPro-PAL'; + $QuicktimeVideoCodecLookup['fire'] = 'Fire'; + $QuicktimeVideoCodecLookup['flic'] = 'FLC'; + $QuicktimeVideoCodecLookup['b48r'] = '48RGB'; + $QuicktimeVideoCodecLookup['gif '] = 'GIF'; + $QuicktimeVideoCodecLookup['smc '] = 'Graphics'; + $QuicktimeVideoCodecLookup['h261'] = 'H261'; + $QuicktimeVideoCodecLookup['h263'] = 'H263'; + $QuicktimeVideoCodecLookup['IV41'] = 'Indeo4'; + $QuicktimeVideoCodecLookup['jpeg'] = 'JPEG'; + $QuicktimeVideoCodecLookup['PNTG'] = 'MacPaint'; + $QuicktimeVideoCodecLookup['msvc'] = 'Microsoft Video1'; + $QuicktimeVideoCodecLookup['mjpa'] = 'Motion JPEG-A'; + $QuicktimeVideoCodecLookup['mjpb'] = 'Motion JPEG-B'; + $QuicktimeVideoCodecLookup['myuv'] = 'MPEG YUV420'; + $QuicktimeVideoCodecLookup['dmb1'] = 'OpenDML JPEG'; + $QuicktimeVideoCodecLookup['kpcd'] = 'PhotoCD'; + $QuicktimeVideoCodecLookup['8BPS'] = 'Planar RGB'; + $QuicktimeVideoCodecLookup['png '] = 'PNG'; + $QuicktimeVideoCodecLookup['qdrw'] = 'QuickDraw'; + $QuicktimeVideoCodecLookup['qdgx'] = 'QuickDrawGX'; + $QuicktimeVideoCodecLookup['raw '] = 'RAW'; + $QuicktimeVideoCodecLookup['.SGI'] = 'SGI'; + $QuicktimeVideoCodecLookup['b16g'] = '16Gray'; + $QuicktimeVideoCodecLookup['b64a'] = '64ARGB'; + $QuicktimeVideoCodecLookup['SVQ1'] = 'Sorenson Video 1'; + $QuicktimeVideoCodecLookup['SVQ1'] = 'Sorenson Video 3'; + $QuicktimeVideoCodecLookup['syv9'] = 'Sorenson YUV9'; + $QuicktimeVideoCodecLookup['tga '] = 'Targa'; + $QuicktimeVideoCodecLookup['b32a'] = '32AlphaGray'; + $QuicktimeVideoCodecLookup['tiff'] = 'TIFF'; + $QuicktimeVideoCodecLookup['path'] = 'Vector'; + $QuicktimeVideoCodecLookup['rpza'] = 'Video'; + $QuicktimeVideoCodecLookup['ripl'] = 'WaterRipple'; + $QuicktimeVideoCodecLookup['WRAW'] = 'Windows RAW'; + $QuicktimeVideoCodecLookup['y420'] = 'YUV420'; + } + return (isset($QuicktimeVideoCodecLookup[$codecid]) ? $QuicktimeVideoCodecLookup[$codecid] : ''); + } + + function QuicktimeAudioCodecLookup($codecid) { + static $QuicktimeAudioCodecLookup = array(); + if (empty($QuicktimeAudioCodecLookup)) { + $QuicktimeAudioCodecLookup['.mp3'] = 'Fraunhofer MPEG Layer-III alias'; + $QuicktimeAudioCodecLookup['aac '] = 'ISO/IEC 14496-3 AAC'; + $QuicktimeAudioCodecLookup['agsm'] = 'Apple GSM 10:1'; + $QuicktimeAudioCodecLookup['alac'] = 'Apple Lossless Audio Codec'; + $QuicktimeAudioCodecLookup['alaw'] = 'A-law 2:1'; + $QuicktimeAudioCodecLookup['conv'] = 'Sample Format'; + $QuicktimeAudioCodecLookup['dvca'] = 'DV'; + $QuicktimeAudioCodecLookup['dvi '] = 'DV 4:1'; + $QuicktimeAudioCodecLookup['eqal'] = 'Frequency Equalizer'; + $QuicktimeAudioCodecLookup['fl32'] = '32-bit Floating Point'; + $QuicktimeAudioCodecLookup['fl64'] = '64-bit Floating Point'; + $QuicktimeAudioCodecLookup['ima4'] = 'Interactive Multimedia Association 4:1'; + $QuicktimeAudioCodecLookup['in24'] = '24-bit Integer'; + $QuicktimeAudioCodecLookup['in32'] = '32-bit Integer'; + $QuicktimeAudioCodecLookup['lpc '] = 'LPC 23:1'; + $QuicktimeAudioCodecLookup['MAC3'] = 'Macintosh Audio Compression/Expansion (MACE) 3:1'; + $QuicktimeAudioCodecLookup['MAC6'] = 'Macintosh Audio Compression/Expansion (MACE) 6:1'; + $QuicktimeAudioCodecLookup['mixb'] = '8-bit Mixer'; + $QuicktimeAudioCodecLookup['mixw'] = '16-bit Mixer'; + $QuicktimeAudioCodecLookup['mp4a'] = 'ISO/IEC 14496-3 AAC'; + $QuicktimeAudioCodecLookup['MS'."\x00\x02"] = 'Microsoft ADPCM'; + $QuicktimeAudioCodecLookup['MS'."\x00\x11"] = 'DV IMA'; + $QuicktimeAudioCodecLookup['MS'."\x00\x55"] = 'Fraunhofer MPEG Layer III'; + $QuicktimeAudioCodecLookup['NONE'] = 'No Encoding'; + $QuicktimeAudioCodecLookup['Qclp'] = 'Qualcomm PureVoice'; + $QuicktimeAudioCodecLookup['QDM2'] = 'QDesign Music 2'; + $QuicktimeAudioCodecLookup['QDMC'] = 'QDesign Music 1'; + $QuicktimeAudioCodecLookup['ratb'] = '8-bit Rate'; + $QuicktimeAudioCodecLookup['ratw'] = '16-bit Rate'; + $QuicktimeAudioCodecLookup['raw '] = 'raw PCM'; + $QuicktimeAudioCodecLookup['sour'] = 'Sound Source'; + $QuicktimeAudioCodecLookup['sowt'] = 'signed/two\'s complement (Little Endian)'; + $QuicktimeAudioCodecLookup['str1'] = 'Iomega MPEG layer II'; + $QuicktimeAudioCodecLookup['str2'] = 'Iomega MPEG *layer II'; + $QuicktimeAudioCodecLookup['str3'] = 'Iomega MPEG **layer II'; + $QuicktimeAudioCodecLookup['str4'] = 'Iomega MPEG ***layer II'; + $QuicktimeAudioCodecLookup['twos'] = 'signed/two\'s complement (Big Endian)'; + $QuicktimeAudioCodecLookup['ulaw'] = 'mu-law 2:1'; + } + return (isset($QuicktimeAudioCodecLookup[$codecid]) ? $QuicktimeAudioCodecLookup[$codecid] : ''); + } + + function QuicktimeDCOMLookup($compressionid) { + static $QuicktimeDCOMLookup = array(); + if (empty($QuicktimeDCOMLookup)) { + $QuicktimeDCOMLookup['zlib'] = 'ZLib Deflate'; + $QuicktimeDCOMLookup['adec'] = 'Apple Compression'; + } + return (isset($QuicktimeDCOMLookup[$compressionid]) ? $QuicktimeDCOMLookup[$compressionid] : ''); + } + + function QuicktimeColorNameLookup($colordepthid) { + static $QuicktimeColorNameLookup = array(); + if (empty($QuicktimeColorNameLookup)) { + $QuicktimeColorNameLookup[1] = '2-color (monochrome)'; + $QuicktimeColorNameLookup[2] = '4-color'; + $QuicktimeColorNameLookup[4] = '16-color'; + $QuicktimeColorNameLookup[8] = '256-color'; + $QuicktimeColorNameLookup[16] = 'thousands (16-bit color)'; + $QuicktimeColorNameLookup[24] = 'millions (24-bit color)'; + $QuicktimeColorNameLookup[32] = 'millions+ (32-bit color)'; + $QuicktimeColorNameLookup[33] = 'black & white'; + $QuicktimeColorNameLookup[34] = '4-gray'; + $QuicktimeColorNameLookup[36] = '16-gray'; + $QuicktimeColorNameLookup[40] = '256-gray'; + } + return (isset($QuicktimeColorNameLookup[$colordepthid]) ? $QuicktimeColorNameLookup[$colordepthid] : 'invalid'); + } + + function CopyToAppropriateCommentsSection($keyname, $data, &$ThisFileInfo) { + static $handyatomtranslatorarray = array(); + if (empty($handyatomtranslatorarray)) { + $handyatomtranslatorarray['©cpy'] = 'copyright'; + $handyatomtranslatorarray['©day'] = 'creation_date'; + $handyatomtranslatorarray['©dir'] = 'director'; + $handyatomtranslatorarray['©ed1'] = 'edit1'; + $handyatomtranslatorarray['©ed2'] = 'edit2'; + $handyatomtranslatorarray['©ed3'] = 'edit3'; + $handyatomtranslatorarray['©ed4'] = 'edit4'; + $handyatomtranslatorarray['©ed5'] = 'edit5'; + $handyatomtranslatorarray['©ed6'] = 'edit6'; + $handyatomtranslatorarray['©ed7'] = 'edit7'; + $handyatomtranslatorarray['©ed8'] = 'edit8'; + $handyatomtranslatorarray['©ed9'] = 'edit9'; + $handyatomtranslatorarray['©fmt'] = 'format'; + $handyatomtranslatorarray['©inf'] = 'information'; + $handyatomtranslatorarray['©prd'] = 'producer'; + $handyatomtranslatorarray['©prf'] = 'performers'; + $handyatomtranslatorarray['©req'] = 'system_requirements'; + $handyatomtranslatorarray['©src'] = 'source_credit'; + $handyatomtranslatorarray['©wrt'] = 'writer'; + + // http://www.geocities.com/xhelmboyx/quicktime/formats/qtm-layout.txt + $handyatomtranslatorarray['©nam'] = 'title'; + $handyatomtranslatorarray['©cmt'] = 'comment'; + $handyatomtranslatorarray['©wrn'] = 'warning'; + $handyatomtranslatorarray['©hst'] = 'host_computer'; + $handyatomtranslatorarray['©mak'] = 'make'; + $handyatomtranslatorarray['©mod'] = 'model'; + $handyatomtranslatorarray['©PRD'] = 'product'; + $handyatomtranslatorarray['©swr'] = 'software'; + $handyatomtranslatorarray['©aut'] = 'author'; + $handyatomtranslatorarray['©ART'] = 'artist'; + $handyatomtranslatorarray['©trk'] = 'track'; + $handyatomtranslatorarray['©alb'] = 'album'; + $handyatomtranslatorarray['©com'] = 'comment'; + $handyatomtranslatorarray['©gen'] = 'genre'; + $handyatomtranslatorarray['©ope'] = 'composer'; + $handyatomtranslatorarray['©url'] = 'url'; + $handyatomtranslatorarray['©enc'] = 'encoder'; + } + if (isset($handyatomtranslatorarray[$keyname])) { + $ThisFileInfo['quicktime']['comments'][$handyatomtranslatorarray[$keyname]][] = $data; + } + + return true; + } + + function NoNullString($nullterminatedstring) { + // remove the single null terminator on null terminated strings + if (substr($nullterminatedstring, strlen($nullterminatedstring) - 1, 1) === "\x00") { + return substr($nullterminatedstring, 0, strlen($nullterminatedstring) - 1); + } + return $nullterminatedstring; + } + + function Pascal2String($pascalstring) { + // Pascal strings have 1 unsigned byte at the beginning saying how many chars (1-255) are in the string + return substr($pascalstring, 1); + } + +} + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.audio-video.real.php b/includes/getid3/getid3/module.audio-video.real.php new file mode 100644 index 0000000..23c8fef --- /dev/null +++ b/includes/getid3/getid3/module.audio-video.real.php @@ -0,0 +1,528 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio-video.real.php // +// module for analyzing Real Audio/Video files // +// dependencies: module.audio-video.riff.php // +// /// +///////////////////////////////////////////////////////////////// + +getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true); + +class getid3_real +{ + + function getid3_real(&$fd, &$ThisFileInfo) { + $ThisFileInfo['fileformat'] = 'real'; + $ThisFileInfo['bitrate'] = 0; + $ThisFileInfo['playtime_seconds'] = 0; + + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + $ChunkCounter = 0; + while (ftell($fd) < $ThisFileInfo['avdataend']) { + $ChunkData = fread($fd, 8); + $ChunkName = substr($ChunkData, 0, 4); + $ChunkSize = getid3_lib::BigEndian2Int(substr($ChunkData, 4, 4)); + + if ($ChunkName == '.ra'."\xFD") { + $ChunkData .= fread($fd, $ChunkSize - 8); + if ($this->ParseOldRAheader(substr($ChunkData, 0, 128), $ThisFileInfo['real']['old_ra_header'])) { + $ThisFileInfo['audio']['dataformat'] = 'real'; + $ThisFileInfo['audio']['lossless'] = false; + $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['real']['old_ra_header']['sample_rate']; + $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['real']['old_ra_header']['bits_per_sample']; + $ThisFileInfo['audio']['channels'] = $ThisFileInfo['real']['old_ra_header']['channels']; + + $ThisFileInfo['playtime_seconds'] = 60 * ($ThisFileInfo['real']['old_ra_header']['audio_bytes'] / $ThisFileInfo['real']['old_ra_header']['bytes_per_minute']); + $ThisFileInfo['audio']['bitrate'] = 8 * ($ThisFileInfo['real']['old_ra_header']['audio_bytes'] / $ThisFileInfo['playtime_seconds']); + $ThisFileInfo['audio']['codec'] = $this->RealAudioCodecFourCClookup($ThisFileInfo['real']['old_ra_header']['fourcc'], $ThisFileInfo['audio']['bitrate']); + + foreach ($ThisFileInfo['real']['old_ra_header']['comments'] as $key => $valuearray) { + if (strlen(trim($valuearray[0])) > 0) { + $ThisFileInfo['real']['comments'][$key][] = trim($valuearray[0]); + } + } + return true; + } + $ThisFileInfo['error'][] = 'There was a problem parsing this RealAudio file. Please submit it for analysis to http://www.getid3.org/upload/ or info@getid3.org'; + unset($ThisFileInfo['bitrate']); + unset($ThisFileInfo['playtime_seconds']); + return false; + } + + // shortcut + $ThisFileInfo['real']['chunks'][$ChunkCounter] = array(); + $thisfile_real_chunks_currentchunk = &$ThisFileInfo['real']['chunks'][$ChunkCounter]; + + $thisfile_real_chunks_currentchunk['name'] = $ChunkName; + $thisfile_real_chunks_currentchunk['offset'] = ftell($fd) - 8; + $thisfile_real_chunks_currentchunk['length'] = $ChunkSize; + if (($thisfile_real_chunks_currentchunk['offset'] + $thisfile_real_chunks_currentchunk['length']) > $ThisFileInfo['avdataend']) { + $ThisFileInfo['warning'][] = 'Chunk "'.$thisfile_real_chunks_currentchunk['name'].'" at offset '.$thisfile_real_chunks_currentchunk['offset'].' claims to be '.$thisfile_real_chunks_currentchunk['length'].' bytes long, which is beyond end of file'; + return false; + } + + if ($ChunkSize > (GETID3_FREAD_BUFFER_SIZE + 8)) { + + $ChunkData .= fread($fd, GETID3_FREAD_BUFFER_SIZE - 8); + fseek($fd, $thisfile_real_chunks_currentchunk['offset'] + $ChunkSize, SEEK_SET); + + } else { + + $ChunkData .= fread($fd, $ChunkSize - 8); + + } + $offset = 8; + + switch ($ChunkName) { + + case '.RMF': // RealMedia File Header + $thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2)); + $offset += 2; + switch ($thisfile_real_chunks_currentchunk['object_version']) { + + case 0: + $thisfile_real_chunks_currentchunk['file_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); + $offset += 4; + $thisfile_real_chunks_currentchunk['headers_count'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); + $offset += 4; + break; + + default: + //$ThisFileInfo['warning'][] = 'Expected .RMF-object_version to be "0", actual value is "'.$thisfile_real_chunks_currentchunk['object_version'].'" (should not be a problem)'; + break; + + } + break; + + + case 'PROP': // Properties Header + $thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2)); + $offset += 2; + if ($thisfile_real_chunks_currentchunk['object_version'] == 0) { + $thisfile_real_chunks_currentchunk['max_bit_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); + $offset += 4; + $thisfile_real_chunks_currentchunk['avg_bit_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); + $offset += 4; + $thisfile_real_chunks_currentchunk['max_packet_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); + $offset += 4; + $thisfile_real_chunks_currentchunk['avg_packet_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); + $offset += 4; + $thisfile_real_chunks_currentchunk['num_packets'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); + $offset += 4; + $thisfile_real_chunks_currentchunk['duration'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); + $offset += 4; + $thisfile_real_chunks_currentchunk['preroll'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); + $offset += 4; + $thisfile_real_chunks_currentchunk['index_offset'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); + $offset += 4; + $thisfile_real_chunks_currentchunk['data_offset'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); + $offset += 4; + $thisfile_real_chunks_currentchunk['num_streams'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2)); + $offset += 2; + $thisfile_real_chunks_currentchunk['flags_raw'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2)); + $offset += 2; + $ThisFileInfo['playtime_seconds'] = $thisfile_real_chunks_currentchunk['duration'] / 1000; + if ($thisfile_real_chunks_currentchunk['duration'] > 0) { + $ThisFileInfo['bitrate'] += $thisfile_real_chunks_currentchunk['avg_bit_rate']; + } + $thisfile_real_chunks_currentchunk['flags']['save_enabled'] = (bool) ($thisfile_real_chunks_currentchunk['flags_raw'] & 0x0001); + $thisfile_real_chunks_currentchunk['flags']['perfect_play'] = (bool) ($thisfile_real_chunks_currentchunk['flags_raw'] & 0x0002); + $thisfile_real_chunks_currentchunk['flags']['live_broadcast'] = (bool) ($thisfile_real_chunks_currentchunk['flags_raw'] & 0x0004); + } + break; + + case 'MDPR': // Media Properties Header + $thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2)); + $offset += 2; + if ($thisfile_real_chunks_currentchunk['object_version'] == 0) { + $thisfile_real_chunks_currentchunk['stream_number'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2)); + $offset += 2; + $thisfile_real_chunks_currentchunk['max_bit_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); + $offset += 4; + $thisfile_real_chunks_currentchunk['avg_bit_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); + $offset += 4; + $thisfile_real_chunks_currentchunk['max_packet_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); + $offset += 4; + $thisfile_real_chunks_currentchunk['avg_packet_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); + $offset += 4; + $thisfile_real_chunks_currentchunk['start_time'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); + $offset += 4; + $thisfile_real_chunks_currentchunk['preroll'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); + $offset += 4; + $thisfile_real_chunks_currentchunk['duration'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); + $offset += 4; + $thisfile_real_chunks_currentchunk['stream_name_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 1)); + $offset += 1; + $thisfile_real_chunks_currentchunk['stream_name'] = substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['stream_name_size']); + $offset += $thisfile_real_chunks_currentchunk['stream_name_size']; + $thisfile_real_chunks_currentchunk['mime_type_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 1)); + $offset += 1; + $thisfile_real_chunks_currentchunk['mime_type'] = substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['mime_type_size']); + $offset += $thisfile_real_chunks_currentchunk['mime_type_size']; + $thisfile_real_chunks_currentchunk['type_specific_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); + $offset += 4; + $thisfile_real_chunks_currentchunk['type_specific_data'] = substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['type_specific_len']); + $offset += $thisfile_real_chunks_currentchunk['type_specific_len']; + + // shortcut + $thisfile_real_chunks_currentchunk_typespecificdata = &$thisfile_real_chunks_currentchunk['type_specific_data']; + + switch ($thisfile_real_chunks_currentchunk['mime_type']) { + case 'video/x-pn-realvideo': + case 'video/x-pn-multirate-realvideo': + // http://www.freelists.org/archives/matroska-devel/07-2003/msg00010.html + + // shortcut + $thisfile_real_chunks_currentchunk['video_info'] = array(); + $thisfile_real_chunks_currentchunk_videoinfo = &$thisfile_real_chunks_currentchunk['video_info']; + + $thisfile_real_chunks_currentchunk_videoinfo['dwSize'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 0, 4)); + $thisfile_real_chunks_currentchunk_videoinfo['fourcc1'] = substr($thisfile_real_chunks_currentchunk_typespecificdata, 4, 4); + $thisfile_real_chunks_currentchunk_videoinfo['fourcc2'] = substr($thisfile_real_chunks_currentchunk_typespecificdata, 8, 4); + $thisfile_real_chunks_currentchunk_videoinfo['width'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 12, 2)); + $thisfile_real_chunks_currentchunk_videoinfo['height'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 14, 2)); + $thisfile_real_chunks_currentchunk_videoinfo['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 16, 2)); + //$thisfile_real_chunks_currentchunk_videoinfo['unknown1'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 18, 2)); + //$thisfile_real_chunks_currentchunk_videoinfo['unknown2'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 20, 2)); + $thisfile_real_chunks_currentchunk_videoinfo['frames_per_second'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 22, 2)); + //$thisfile_real_chunks_currentchunk_videoinfo['unknown3'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 24, 2)); + //$thisfile_real_chunks_currentchunk_videoinfo['unknown4'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 26, 2)); + //$thisfile_real_chunks_currentchunk_videoinfo['unknown5'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 28, 2)); + //$thisfile_real_chunks_currentchunk_videoinfo['unknown6'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 30, 2)); + //$thisfile_real_chunks_currentchunk_videoinfo['unknown7'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 32, 2)); + //$thisfile_real_chunks_currentchunk_videoinfo['unknown8'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 34, 2)); + //$thisfile_real_chunks_currentchunk_videoinfo['unknown9'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 36, 2)); + + $thisfile_real_chunks_currentchunk_videoinfo['codec'] = getid3_riff::RIFFfourccLookup($thisfile_real_chunks_currentchunk_videoinfo['fourcc2']); + + $ThisFileInfo['video']['resolution_x'] = $thisfile_real_chunks_currentchunk_videoinfo['width']; + $ThisFileInfo['video']['resolution_y'] = $thisfile_real_chunks_currentchunk_videoinfo['height']; + $ThisFileInfo['video']['frame_rate'] = (float) $thisfile_real_chunks_currentchunk_videoinfo['frames_per_second']; + $ThisFileInfo['video']['codec'] = $thisfile_real_chunks_currentchunk_videoinfo['codec']; + $ThisFileInfo['video']['bits_per_sample'] = $thisfile_real_chunks_currentchunk_videoinfo['bits_per_sample']; + break; + + case 'audio/x-pn-realaudio': + case 'audio/x-pn-multirate-realaudio': + $this->ParseOldRAheader($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk['parsed_audio_data']); + + $ThisFileInfo['audio']['sample_rate'] = $thisfile_real_chunks_currentchunk['parsed_audio_data']['sample_rate']; + $ThisFileInfo['audio']['bits_per_sample'] = $thisfile_real_chunks_currentchunk['parsed_audio_data']['bits_per_sample']; + $ThisFileInfo['audio']['channels'] = $thisfile_real_chunks_currentchunk['parsed_audio_data']['channels']; + if (!empty($ThisFileInfo['audio']['dataformat'])) { + foreach ($ThisFileInfo['audio'] as $key => $value) { + if ($key != 'streams') { + $ThisFileInfo['audio']['streams'][$thisfile_real_chunks_currentchunk['stream_number']][$key] = $value; + } + } + } + break; + + case 'logical-fileinfo': + // shortcut + $thisfile_real_chunks_currentchunk['logical_fileinfo'] = array(); + $thisfile_real_chunks_currentchunk_logicalfileinfo = &$thisfile_real_chunks_currentchunk['logical_fileinfo']; + + $thisfile_real_chunks_currentchunk_logicalfileinfo_offset = 0; + $thisfile_real_chunks_currentchunk_logicalfileinfo['logical_fileinfo_length'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4)); + $thisfile_real_chunks_currentchunk_logicalfileinfo_offset += 4; + + //$thisfile_real_chunks_currentchunk_logicalfileinfo['unknown1'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4)); + $thisfile_real_chunks_currentchunk_logicalfileinfo_offset += 4; + + $thisfile_real_chunks_currentchunk_logicalfileinfo['num_tags'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4)); + $thisfile_real_chunks_currentchunk_logicalfileinfo_offset += 4; + + //$thisfile_real_chunks_currentchunk_logicalfileinfo['unknown2'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4)); + $thisfile_real_chunks_currentchunk_logicalfileinfo_offset += 4; + + //$thisfile_real_chunks_currentchunk_logicalfileinfo['d'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 1)); + + //$thisfile_real_chunks_currentchunk_logicalfileinfo['one_type'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4)); + //$thisfile_real_chunks_currentchunk_logicalfileinfo_thislength = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 4 + $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 2)); + //$thisfile_real_chunks_currentchunk_logicalfileinfo['one'] = substr($thisfile_real_chunks_currentchunk_typespecificdata, 6 + $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, $thisfile_real_chunks_currentchunk_logicalfileinfo_thislength); + //$thisfile_real_chunks_currentchunk_logicalfileinfo_offset += (6 + $thisfile_real_chunks_currentchunk_logicalfileinfo_thislength); + + break; + + } + + + if (empty($ThisFileInfo['playtime_seconds'])) { + $ThisFileInfo['playtime_seconds'] = max($ThisFileInfo['playtime_seconds'], ($thisfile_real_chunks_currentchunk['duration'] + $thisfile_real_chunks_currentchunk['start_time']) / 1000); + } + if ($thisfile_real_chunks_currentchunk['duration'] > 0) { + switch ($thisfile_real_chunks_currentchunk['mime_type']) { + case 'audio/x-pn-realaudio': + case 'audio/x-pn-multirate-realaudio': + $ThisFileInfo['audio']['bitrate'] = (isset($ThisFileInfo['audio']['bitrate']) ? $ThisFileInfo['audio']['bitrate'] : 0) + $thisfile_real_chunks_currentchunk['avg_bit_rate']; + $ThisFileInfo['audio']['codec'] = $this->RealAudioCodecFourCClookup($thisfile_real_chunks_currentchunk['parsed_audio_data']['fourcc'], $ThisFileInfo['audio']['bitrate']); + $ThisFileInfo['audio']['dataformat'] = 'real'; + $ThisFileInfo['audio']['lossless'] = false; + break; + + case 'video/x-pn-realvideo': + case 'video/x-pn-multirate-realvideo': + $ThisFileInfo['video']['bitrate'] = (isset($ThisFileInfo['video']['bitrate']) ? $ThisFileInfo['video']['bitrate'] : 0) + $thisfile_real_chunks_currentchunk['avg_bit_rate']; + $ThisFileInfo['video']['bitrate_mode'] = 'cbr'; + $ThisFileInfo['video']['dataformat'] = 'real'; + $ThisFileInfo['video']['lossless'] = false; + $ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1; + break; + + case 'audio/x-ralf-mpeg4-generic': + $ThisFileInfo['audio']['bitrate'] = (isset($ThisFileInfo['audio']['bitrate']) ? $ThisFileInfo['audio']['bitrate'] : 0) + $thisfile_real_chunks_currentchunk['avg_bit_rate']; + $ThisFileInfo['audio']['codec'] = 'RealAudio Lossless'; + $ThisFileInfo['audio']['dataformat'] = 'real'; + $ThisFileInfo['audio']['lossless'] = true; + break; + } + $ThisFileInfo['bitrate'] = (isset($ThisFileInfo['video']['bitrate']) ? $ThisFileInfo['video']['bitrate'] : 0) + (isset($ThisFileInfo['audio']['bitrate']) ? $ThisFileInfo['audio']['bitrate'] : 0); + } + } + break; + + case 'CONT': // Content Description Header (text comments) + $thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2)); + $offset += 2; + if ($thisfile_real_chunks_currentchunk['object_version'] == 0) { + $thisfile_real_chunks_currentchunk['title_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2)); + $offset += 2; + $thisfile_real_chunks_currentchunk['title'] = (string) substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['title_len']); + $offset += $thisfile_real_chunks_currentchunk['title_len']; + + $thisfile_real_chunks_currentchunk['artist_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2)); + $offset += 2; + $thisfile_real_chunks_currentchunk['artist'] = (string) substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['artist_len']); + $offset += $thisfile_real_chunks_currentchunk['artist_len']; + + $thisfile_real_chunks_currentchunk['copyright_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2)); + $offset += 2; + $thisfile_real_chunks_currentchunk['copyright'] = (string) substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['copyright_len']); + $offset += $thisfile_real_chunks_currentchunk['copyright_len']; + + $thisfile_real_chunks_currentchunk['comment_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2)); + $offset += 2; + $thisfile_real_chunks_currentchunk['comment'] = (string) substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['comment_len']); + $offset += $thisfile_real_chunks_currentchunk['comment_len']; + + + $commentkeystocopy = array('title'=>'title', 'artist'=>'artist', 'copyright'=>'copyright', 'comment'=>'comment'); + foreach ($commentkeystocopy as $key => $val) { + if ($thisfile_real_chunks_currentchunk[$key]) { + $ThisFileInfo['real']['comments'][$val][] = trim($thisfile_real_chunks_currentchunk[$key]); + } + } + + } + break; + + + case 'DATA': // Data Chunk Header + // do nothing + break; + + case 'INDX': // Index Section Header + $thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2)); + $offset += 2; + if ($thisfile_real_chunks_currentchunk['object_version'] == 0) { + $thisfile_real_chunks_currentchunk['num_indices'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); + $offset += 4; + $thisfile_real_chunks_currentchunk['stream_number'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2)); + $offset += 2; + $thisfile_real_chunks_currentchunk['next_index_header'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4)); + $offset += 4; + + if ($thisfile_real_chunks_currentchunk['next_index_header'] == 0) { + // last index chunk found, ignore rest of file + break 2; + } else { + // non-last index chunk, seek to next index chunk (skipping actual index data) + fseek($fd, $thisfile_real_chunks_currentchunk['next_index_header'], SEEK_SET); + } + } + break; + + default: + $ThisFileInfo['warning'][] = 'Unhandled RealMedia chunk "'.$ChunkName.'" at offset '.$thisfile_real_chunks_currentchunk['offset']; + break; + } + $ChunkCounter++; + } + + if (!empty($ThisFileInfo['audio']['streams'])) { + $ThisFileInfo['audio']['bitrate'] = 0; + foreach ($ThisFileInfo['audio']['streams'] as $key => $valuearray) { + $ThisFileInfo['audio']['bitrate'] += $valuearray['bitrate']; + } + } + + return true; + } + + + function ParseOldRAheader($OldRAheaderData, &$ParsedArray) { + // http://www.freelists.org/archives/matroska-devel/07-2003/msg00010.html + + $ParsedArray = array(); + $ParsedArray['magic'] = substr($OldRAheaderData, 0, 4); + if ($ParsedArray['magic'] != '.ra'."\xFD") { + return false; + } + $ParsedArray['version1'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 4, 2)); + + if ($ParsedArray['version1'] < 3) { + + return false; + + } elseif ($ParsedArray['version1'] == 3) { + + $ParsedArray['fourcc1'] = '.ra3'; + $ParsedArray['bits_per_sample'] = 16; // hard-coded for old versions? + $ParsedArray['sample_rate'] = 8000; // hard-coded for old versions? + + $ParsedArray['header_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 6, 2)); + $ParsedArray['channels'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 8, 2)); // always 1 (?) + //$ParsedArray['unknown1'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 10, 2)); + //$ParsedArray['unknown2'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 12, 2)); + //$ParsedArray['unknown3'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 14, 2)); + $ParsedArray['bytes_per_minute'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 16, 2)); + $ParsedArray['audio_bytes'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 18, 4)); + $ParsedArray['comments_raw'] = substr($OldRAheaderData, 22, $ParsedArray['header_size'] - 22 + 1); // not including null terminator + + $commentoffset = 0; + $commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1)); + $ParsedArray['comments']['title'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength); + $commentoffset += $commentlength; + + $commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1)); + $ParsedArray['comments']['artist'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength); + $commentoffset += $commentlength; + + $commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1)); + $ParsedArray['comments']['copyright'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength); + $commentoffset += $commentlength; + + $commentoffset++; // final null terminator (?) + $commentoffset++; // fourcc length (?) should be 4 + $ParsedArray['fourcc'] = substr($OldRAheaderData, 23 + $commentoffset, 4); + + } elseif ($ParsedArray['version1'] <= 5) { + + //$ParsedArray['unknown1'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 6, 2)); + $ParsedArray['fourcc1'] = substr($OldRAheaderData, 8, 4); + $ParsedArray['file_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 12, 4)); + $ParsedArray['version2'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 16, 2)); + $ParsedArray['header_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 18, 4)); + $ParsedArray['codec_flavor_id'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 22, 2)); + $ParsedArray['coded_frame_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 24, 4)); + $ParsedArray['audio_bytes'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 28, 4)); + $ParsedArray['bytes_per_minute'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 32, 4)); + //$ParsedArray['unknown5'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 36, 4)); + $ParsedArray['sub_packet_h'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 40, 2)); + $ParsedArray['frame_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 42, 2)); + $ParsedArray['sub_packet_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 44, 2)); + //$ParsedArray['unknown6'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 46, 2)); + + switch ($ParsedArray['version1']) { + + case 4: + $ParsedArray['sample_rate'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 48, 2)); + //$ParsedArray['unknown8'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 50, 2)); + $ParsedArray['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 52, 2)); + $ParsedArray['channels'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 54, 2)); + $ParsedArray['length_fourcc2'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 56, 1)); + $ParsedArray['fourcc2'] = substr($OldRAheaderData, 57, 4); + $ParsedArray['length_fourcc3'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 61, 1)); + $ParsedArray['fourcc3'] = substr($OldRAheaderData, 62, 4); + //$ParsedArray['unknown9'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 66, 1)); + //$ParsedArray['unknown10'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 67, 2)); + $ParsedArray['comments_raw'] = substr($OldRAheaderData, 69, $ParsedArray['header_size'] - 69 + 16); + + $commentoffset = 0; + $commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1)); + $ParsedArray['comments']['title'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength); + $commentoffset += $commentlength; + + $commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1)); + $ParsedArray['comments']['artist'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength); + $commentoffset += $commentlength; + + $commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1)); + $ParsedArray['comments']['copyright'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength); + $commentoffset += $commentlength; + break; + + case 5: + $ParsedArray['sample_rate'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 48, 4)); + $ParsedArray['sample_rate2'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 52, 4)); + $ParsedArray['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 56, 4)); + $ParsedArray['channels'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 60, 2)); + $ParsedArray['genr'] = substr($OldRAheaderData, 62, 4); + $ParsedArray['fourcc3'] = substr($OldRAheaderData, 66, 4); + $ParsedArray['comments'] = array(); + break; + } + $ParsedArray['fourcc'] = $ParsedArray['fourcc3']; + + } + foreach ($ParsedArray['comments'] as $key => $value) { + if ($ParsedArray['comments'][$key][0] === false) { + $ParsedArray['comments'][$key][0] = ''; + } + } + + return true; + } + + function RealAudioCodecFourCClookup($fourcc, $bitrate) { + static $RealAudioCodecFourCClookup = array(); + if (empty($RealAudioCodecFourCClookup)) { + // http://www.its.msstate.edu/net/real/reports/config/tags.stats + // http://www.freelists.org/archives/matroska-devel/06-2003/fullthread18.html + + $RealAudioCodecFourCClookup['14_4'][8000] = 'RealAudio v2 (14.4kbps)'; + $RealAudioCodecFourCClookup['14.4'][8000] = 'RealAudio v2 (14.4kbps)'; + $RealAudioCodecFourCClookup['lpcJ'][8000] = 'RealAudio v2 (14.4kbps)'; + $RealAudioCodecFourCClookup['28_8'][15200] = 'RealAudio v2 (28.8kbps)'; + $RealAudioCodecFourCClookup['28.8'][15200] = 'RealAudio v2 (28.8kbps)'; + $RealAudioCodecFourCClookup['sipr'][4933] = 'RealAudio v4 (5kbps Voice)'; + $RealAudioCodecFourCClookup['sipr'][6444] = 'RealAudio v4 (6.5kbps Voice)'; + $RealAudioCodecFourCClookup['sipr'][8444] = 'RealAudio v4 (8.5kbps Voice)'; + $RealAudioCodecFourCClookup['sipr'][16000] = 'RealAudio v4 (16kbps Wideband)'; + $RealAudioCodecFourCClookup['dnet'][8000] = 'RealAudio v3 (8kbps Music)'; + $RealAudioCodecFourCClookup['dnet'][16000] = 'RealAudio v3 (16kbps Music Low Response)'; + $RealAudioCodecFourCClookup['dnet'][15963] = 'RealAudio v3 (16kbps Music Mid/High Response)'; + $RealAudioCodecFourCClookup['dnet'][20000] = 'RealAudio v3 (20kbps Music Stereo)'; + $RealAudioCodecFourCClookup['dnet'][32000] = 'RealAudio v3 (32kbps Music Mono)'; + $RealAudioCodecFourCClookup['dnet'][31951] = 'RealAudio v3 (32kbps Music Stereo)'; + $RealAudioCodecFourCClookup['dnet'][39965] = 'RealAudio v3 (40kbps Music Mono)'; + $RealAudioCodecFourCClookup['dnet'][40000] = 'RealAudio v3 (40kbps Music Stereo)'; + $RealAudioCodecFourCClookup['dnet'][79947] = 'RealAudio v3 (80kbps Music Mono)'; + $RealAudioCodecFourCClookup['dnet'][80000] = 'RealAudio v3 (80kbps Music Stereo)'; + + $RealAudioCodecFourCClookup['dnet'][0] = 'RealAudio v3'; + $RealAudioCodecFourCClookup['sipr'][0] = 'RealAudio v4'; + $RealAudioCodecFourCClookup['cook'][0] = 'RealAudio G2'; + $RealAudioCodecFourCClookup['atrc'][0] = 'RealAudio 8'; + } + $roundbitrate = intval(round($bitrate)); + if (isset($RealAudioCodecFourCClookup[$fourcc][$roundbitrate])) { + return $RealAudioCodecFourCClookup[$fourcc][$roundbitrate]; + } elseif (isset($RealAudioCodecFourCClookup[$fourcc][0])) { + return $RealAudioCodecFourCClookup[$fourcc][0]; + } + return $fourcc; + } + +} + + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.audio-video.riff.php b/includes/getid3/getid3/module.audio-video.riff.php new file mode 100644 index 0000000..f32dab4 --- /dev/null +++ b/includes/getid3/getid3/module.audio-video.riff.php @@ -0,0 +1,1995 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio-video.riff.php // +// module for analyzing RIFF files // +// multiple formats supported by this module: // +// Wave, AVI, AIFF/AIFC, (MP3,AC3)/RIFF, Wavpack v3, 8SVX // +// dependencies: module.audio.mp3.php // +// module.audio.ac3.php (optional) // +// /// +///////////////////////////////////////////////////////////////// + +getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true); + +class getid3_riff +{ + + function getid3_riff(&$fd, &$ThisFileInfo) { + + // initialize these values to an empty array, otherwise they default to NULL + // and you can't append array values to a NULL value + $ThisFileInfo['riff'] = array('raw'=>array()); + + // Shortcuts + $thisfile_riff = &$ThisFileInfo['riff']; + $thisfile_riff_raw = &$thisfile_riff['raw']; + $thisfile_audio = &$ThisFileInfo['audio']; + $thisfile_video = &$ThisFileInfo['video']; + $thisfile_avdataoffset = &$ThisFileInfo['avdataoffset']; + $thisfile_avdataend = &$ThisFileInfo['avdataend']; + $thisfile_audio_dataformat = &$thisfile_audio['dataformat']; + $thisfile_riff_audio = &$thisfile_riff['audio']; + $thisfile_riff_video = &$thisfile_riff['video']; + + + $Original['avdataoffset'] = $thisfile_avdataoffset; + $Original['avdataend'] = $thisfile_avdataend; + + fseek($fd, $thisfile_avdataoffset, SEEK_SET); + $RIFFheader = fread($fd, 12); + $RIFFsubtype = substr($RIFFheader, 8, 4); + switch (substr($RIFFheader, 0, 4)) { + case 'FORM': + $ThisFileInfo['fileformat'] = 'aiff'; + $RIFFheaderSize = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($RIFFheader, 4, 4)); + $thisfile_riff[$RIFFsubtype] = getid3_riff::ParseRIFF($fd, $thisfile_avdataoffset + 12, $thisfile_avdataoffset + $RIFFheaderSize, $ThisFileInfo); + $thisfile_riff['header_size'] = $RIFFheaderSize; + break; + + case 'RIFF': + case 'SDSS': // SDSS is identical to RIFF, just renamed. Used by SmartSound QuickTracks (www.smartsound.com) + case 'RMP3': // RMP3 is identical to RIFF, just renamed. Used by [unknown program] when creating RIFF-MP3s + if ($RIFFsubtype == 'RMP3') { + // RMP3 is identical to WAVE, just renamed. Used by [unknown program] when creating RIFF-MP3s + $RIFFsubtype = 'WAVE'; + } + + $ThisFileInfo['fileformat'] = 'riff'; + $RIFFheaderSize = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($RIFFheader, 4, 4)); + $thisfile_riff[$RIFFsubtype] = getid3_riff::ParseRIFF($fd, $thisfile_avdataoffset + 12, $thisfile_avdataoffset + $RIFFheaderSize, $ThisFileInfo); + $thisfile_riff['header_size'] = $RIFFheaderSize; + if ($RIFFsubtype == 'WAVE') { + $thisfile_riff_WAVE = &$thisfile_riff['WAVE']; + } + break; + + default: + $ThisFileInfo['error'][] = 'Cannot parse RIFF (this is maybe not a RIFF / WAV / AVI file?) - expecting "FORM|RIFF|SDSS|RMP3" found "'.$RIFFsubtype.'" instead'; + unset($ThisFileInfo['fileformat']); + return false; + break; + } + + $streamindex = 0; + switch ($RIFFsubtype) { + case 'WAVE': + if (empty($thisfile_audio['bitrate_mode'])) { + $thisfile_audio['bitrate_mode'] = 'cbr'; + } + if (empty($thisfile_audio_dataformat)) { + $thisfile_audio_dataformat = 'wav'; + } + + if (isset($thisfile_riff_WAVE['data'][0]['offset'])) { + $thisfile_avdataoffset = $thisfile_riff_WAVE['data'][0]['offset'] + 8; + $thisfile_avdataend = $thisfile_avdataoffset + $thisfile_riff_WAVE['data'][0]['size']; + } + if (isset($thisfile_riff_WAVE['fmt '][0]['data'])) { + + $thisfile_riff_audio[$streamindex] = getid3_riff::RIFFparseWAVEFORMATex($thisfile_riff_WAVE['fmt '][0]['data']); + $thisfile_audio['wformattag'] = $thisfile_riff_audio[$streamindex]['raw']['wFormatTag']; + if (@$thisfile_riff_audio[$streamindex]['bitrate'] == 0) { + $ThisFileInfo['error'][] = 'Corrupt RIFF file: bitrate_audio == zero'; + return false; + } + $thisfile_riff_raw['fmt '] = $thisfile_riff_audio[$streamindex]['raw']; + unset($thisfile_riff_audio[$streamindex]['raw']); + $thisfile_audio['streams'][$streamindex] = $thisfile_riff_audio[$streamindex]; + + $thisfile_audio = getid3_lib::array_merge_noclobber($thisfile_audio, $thisfile_riff_audio[$streamindex]); + if (substr($thisfile_audio['codec'], 0, strlen('unknown: 0x')) == 'unknown: 0x') { + $ThisFileInfo['warning'][] = 'Audio codec = '.$thisfile_audio['codec']; + } + $thisfile_audio['bitrate'] = $thisfile_riff_audio[$streamindex]['bitrate']; + + $ThisFileInfo['playtime_seconds'] = (float) ((($thisfile_avdataend - $thisfile_avdataoffset) * 8) / $thisfile_audio['bitrate']); + + $thisfile_audio['lossless'] = false; + if (isset($thisfile_riff_WAVE['data'][0]['offset']) && isset($thisfile_riff_raw['fmt ']['wFormatTag'])) { + switch ($thisfile_riff_raw['fmt ']['wFormatTag']) { + + case 0x0001: // PCM + $thisfile_audio['lossless'] = true; + break; + + case 0x2000: // AC-3 + $thisfile_audio_dataformat = 'ac3'; + break; + + default: + // do nothing + break; + + } + } + $thisfile_audio['streams'][$streamindex]['wformattag'] = $thisfile_audio['wformattag']; + $thisfile_audio['streams'][$streamindex]['bitrate_mode'] = $thisfile_audio['bitrate_mode']; + $thisfile_audio['streams'][$streamindex]['lossless'] = $thisfile_audio['lossless']; + $thisfile_audio['streams'][$streamindex]['dataformat'] = $thisfile_audio_dataformat; + } + + if (isset($thisfile_riff_WAVE['rgad'][0]['data'])) { + + // shortcuts + $rgadData = &$thisfile_riff_WAVE['rgad'][0]['data']; + $thisfile_riff_raw['rgad'] = array('track'=>array(), 'album'=>array()); + $thisfile_riff_raw_rgad = &$thisfile_riff_raw['rgad']; + $thisfile_riff_raw_rgad_track = &$thisfile_riff_raw_rgad['track']; + $thisfile_riff_raw_rgad_album = &$thisfile_riff_raw_rgad['album']; + + $thisfile_riff_raw_rgad['fPeakAmplitude'] = getid3_lib::LittleEndian2Float(substr($rgadData, 0, 4)); + $thisfile_riff_raw_rgad['nRadioRgAdjust'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($rgadData, 4, 2)); + $thisfile_riff_raw_rgad['nAudiophileRgAdjust'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($rgadData, 6, 2)); + + $nRadioRgAdjustBitstring = str_pad(getid3_lib::Dec2Bin($thisfile_riff_raw_rgad['nRadioRgAdjust']), 16, '0', STR_PAD_LEFT); + $nAudiophileRgAdjustBitstring = str_pad(getid3_lib::Dec2Bin($thisfile_riff_raw_rgad['nAudiophileRgAdjust']), 16, '0', STR_PAD_LEFT); + $thisfile_riff_raw_rgad_track['name'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 0, 3)); + $thisfile_riff_raw_rgad_track['originator'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 3, 3)); + $thisfile_riff_raw_rgad_track['signbit'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 6, 1)); + $thisfile_riff_raw_rgad_track['adjustment'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 7, 9)); + $thisfile_riff_raw_rgad_album['name'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 0, 3)); + $thisfile_riff_raw_rgad_album['originator'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 3, 3)); + $thisfile_riff_raw_rgad_album['signbit'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 6, 1)); + $thisfile_riff_raw_rgad_album['adjustment'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 7, 9)); + + $thisfile_riff['rgad']['peakamplitude'] = $thisfile_riff_raw_rgad['fPeakAmplitude']; + if (($thisfile_riff_raw_rgad_track['name'] != 0) && ($thisfile_riff_raw_rgad_track['originator'] != 0)) { + $thisfile_riff['rgad']['track']['name'] = getid3_lib::RGADnameLookup($thisfile_riff_raw_rgad_track['name']); + $thisfile_riff['rgad']['track']['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_riff_raw_rgad_track['originator']); + $thisfile_riff['rgad']['track']['adjustment'] = getid3_lib::RGADadjustmentLookup($thisfile_riff_raw_rgad_track['adjustment'], $thisfile_riff_raw_rgad_track['signbit']); + } + if (($thisfile_riff_raw_rgad_album['name'] != 0) && ($thisfile_riff_raw_rgad_album['originator'] != 0)) { + $thisfile_riff['rgad']['album']['name'] = getid3_lib::RGADnameLookup($thisfile_riff_raw_rgad_album['name']); + $thisfile_riff['rgad']['album']['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_riff_raw_rgad_album['originator']); + $thisfile_riff['rgad']['album']['adjustment'] = getid3_lib::RGADadjustmentLookup($thisfile_riff_raw_rgad_album['adjustment'], $thisfile_riff_raw_rgad_album['signbit']); + } + } + + if (isset($thisfile_riff_WAVE['fact'][0]['data'])) { + $thisfile_riff_raw['fact']['NumberOfSamples'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_WAVE['fact'][0]['data'], 0, 4)); + + // This should be a good way of calculating exact playtime, + // but some sample files have had incorrect number of samples, + // so cannot use this method + + // if (!empty($thisfile_riff_raw['fmt ']['nSamplesPerSec'])) { + // $ThisFileInfo['playtime_seconds'] = (float) $thisfile_riff_raw['fact']['NumberOfSamples'] / $thisfile_riff_raw['fmt ']['nSamplesPerSec']; + // } + } + if (!empty($thisfile_riff_raw['fmt ']['nAvgBytesPerSec'])) { + $thisfile_audio['bitrate'] = getid3_lib::CastAsInt($thisfile_riff_raw['fmt ']['nAvgBytesPerSec'] * 8); + } + + if (isset($thisfile_riff_WAVE['bext'][0]['data'])) { + // shortcut + $thisfile_riff_WAVE_bext_0 = &$thisfile_riff_WAVE['bext'][0]; + + $thisfile_riff_WAVE_bext_0['title'] = trim(substr($thisfile_riff_WAVE_bext_0['data'], 0, 256)); + $thisfile_riff_WAVE_bext_0['author'] = trim(substr($thisfile_riff_WAVE_bext_0['data'], 256, 32)); + $thisfile_riff_WAVE_bext_0['reference'] = trim(substr($thisfile_riff_WAVE_bext_0['data'], 288, 32)); + $thisfile_riff_WAVE_bext_0['origin_date'] = substr($thisfile_riff_WAVE_bext_0['data'], 320, 10); + $thisfile_riff_WAVE_bext_0['origin_time'] = substr($thisfile_riff_WAVE_bext_0['data'], 330, 8); + $thisfile_riff_WAVE_bext_0['time_reference'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_bext_0['data'], 338, 8)); + $thisfile_riff_WAVE_bext_0['bwf_version'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_bext_0['data'], 346, 1)); + $thisfile_riff_WAVE_bext_0['reserved'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_bext_0['data'], 347, 254)); + $thisfile_riff_WAVE_bext_0['coding_history'] = explode("\r\n", trim(substr($thisfile_riff_WAVE_bext_0['data'], 601))); + + $thisfile_riff_WAVE_bext_0['origin_date_unix'] = gmmktime( + substr($thisfile_riff_WAVE_bext_0['origin_time'], 0, 2), + substr($thisfile_riff_WAVE_bext_0['origin_time'], 3, 2), + substr($thisfile_riff_WAVE_bext_0['origin_time'], 6, 2), + substr($thisfile_riff_WAVE_bext_0['origin_date'], 5, 2), + substr($thisfile_riff_WAVE_bext_0['origin_date'], 8, 2), + substr($thisfile_riff_WAVE_bext_0['origin_date'], 0, 4)); + + $thisfile_riff['comments']['author'][] = $thisfile_riff_WAVE_bext_0['author']; + $thisfile_riff['comments']['title'][] = $thisfile_riff_WAVE_bext_0['title']; + } + + if (isset($thisfile_riff_WAVE['MEXT'][0]['data'])) { + // shortcut + $thisfile_riff_WAVE_MEXT_0 = &$thisfile_riff_WAVE['MEXT'][0]; + + $thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 0, 2)); + $thisfile_riff_WAVE_MEXT_0['flags']['homogenous'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0001); + if ($thisfile_riff_WAVE_MEXT_0['flags']['homogenous']) { + $thisfile_riff_WAVE_MEXT_0['flags']['padding'] = ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0002) ? false : true; + $thisfile_riff_WAVE_MEXT_0['flags']['22_or_44'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0004); + $thisfile_riff_WAVE_MEXT_0['flags']['free_format'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0008); + + $thisfile_riff_WAVE_MEXT_0['nominal_frame_size'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 2, 2)); + } + $thisfile_riff_WAVE_MEXT_0['anciliary_data_length'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 6, 2)); + $thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 8, 2)); + $thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_left'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x0001); + $thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_free'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x0002); + $thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_right'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x0004); + } + + if (isset($thisfile_riff_WAVE['cart'][0]['data'])) { + // shortcut + $thisfile_riff_WAVE_cart_0 = &$thisfile_riff_WAVE['cart'][0]; + + $thisfile_riff_WAVE_cart_0['version'] = substr($thisfile_riff_WAVE_cart_0['data'], 0, 4); + $thisfile_riff_WAVE_cart_0['title'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 4, 64)); + $thisfile_riff_WAVE_cart_0['artist'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 68, 64)); + $thisfile_riff_WAVE_cart_0['cut_id'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 132, 64)); + $thisfile_riff_WAVE_cart_0['client_id'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 196, 64)); + $thisfile_riff_WAVE_cart_0['category'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 260, 64)); + $thisfile_riff_WAVE_cart_0['classification'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 324, 64)); + $thisfile_riff_WAVE_cart_0['out_cue'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 388, 64)); + $thisfile_riff_WAVE_cart_0['start_date'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 452, 10)); + $thisfile_riff_WAVE_cart_0['start_time'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 462, 8)); + $thisfile_riff_WAVE_cart_0['end_date'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 470, 10)); + $thisfile_riff_WAVE_cart_0['end_time'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 480, 8)); + $thisfile_riff_WAVE_cart_0['producer_app_id'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 488, 64)); + $thisfile_riff_WAVE_cart_0['producer_app_version'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 552, 64)); + $thisfile_riff_WAVE_cart_0['user_defined_text'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 616, 64)); + $thisfile_riff_WAVE_cart_0['zero_db_reference'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_cart_0['data'], 680, 4), true); + for ($i = 0; $i < 8; $i++) { + $thisfile_riff_WAVE_cart_0['post_time'][$i]['usage_fourcc'] = substr($thisfile_riff_WAVE_cart_0['data'], 684 + ($i * 8), 4); + $thisfile_riff_WAVE_cart_0['post_time'][$i]['timer_value'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_cart_0['data'], 684 + ($i * 8) + 4, 4)); + } + $thisfile_riff_WAVE_cart_0['url'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 748, 1024)); + $thisfile_riff_WAVE_cart_0['tag_text'] = explode("\r\n", trim(substr($thisfile_riff_WAVE_cart_0['data'], 1772))); + + $thisfile_riff['comments']['artist'][] = $thisfile_riff_WAVE_cart_0['artist']; + $thisfile_riff['comments']['title'][] = $thisfile_riff_WAVE_cart_0['title']; + } + + if (!isset($thisfile_audio['bitrate']) && isset($thisfile_riff_audio[$streamindex]['bitrate'])) { + $thisfile_audio['bitrate'] = $thisfile_riff_audio[$streamindex]['bitrate']; + $ThisFileInfo['playtime_seconds'] = (float) ((($thisfile_avdataend - $thisfile_avdataoffset) * 8) / $thisfile_audio['bitrate']); + } + + if (!empty($ThisFileInfo['wavpack'])) { + $thisfile_audio_dataformat = 'wavpack'; + $thisfile_audio['bitrate_mode'] = 'vbr'; + $thisfile_audio['encoder'] = 'WavPack v'.$ThisFileInfo['wavpack']['version']; + + // Reset to the way it was - RIFF parsing will have messed this up + $thisfile_avdataend = $Original['avdataend']; + $thisfile_audio['bitrate'] = (($thisfile_avdataend - $thisfile_avdataoffset) * 8) / $ThisFileInfo['playtime_seconds']; + + fseek($fd, $thisfile_avdataoffset - 44, SEEK_SET); + $RIFFdata = fread($fd, 44); + $OrignalRIFFheaderSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 4, 4)) + 8; + $OrignalRIFFdataSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 40, 4)) + 44; + + if ($OrignalRIFFheaderSize > $OrignalRIFFdataSize) { + $thisfile_avdataend -= ($OrignalRIFFheaderSize - $OrignalRIFFdataSize); + fseek($fd, $thisfile_avdataend, SEEK_SET); + $RIFFdata .= fread($fd, $OrignalRIFFheaderSize - $OrignalRIFFdataSize); + } + + // move the data chunk after all other chunks (if any) + // so that the RIFF parser doesn't see EOF when trying + // to skip over the data chunk + $RIFFdata = substr($RIFFdata, 0, 36).substr($RIFFdata, 44).substr($RIFFdata, 36, 8); + getid3_riff::ParseRIFFdata($RIFFdata, $ThisFileInfo); + } + + if (isset($thisfile_riff_raw['fmt ']['wFormatTag'])) { + switch ($thisfile_riff_raw['fmt ']['wFormatTag']) { + case 0x08AE: // ClearJump LiteWave + $thisfile_audio['bitrate_mode'] = 'vbr'; + $thisfile_audio_dataformat = 'litewave'; + + //typedef struct tagSLwFormat { + // WORD m_wCompFormat; // low byte defines compression method, high byte is compression flags + // DWORD m_dwScale; // scale factor for lossy compression + // DWORD m_dwBlockSize; // number of samples in encoded blocks + // WORD m_wQuality; // alias for the scale factor + // WORD m_wMarkDistance; // distance between marks in bytes + // WORD m_wReserved; + // + // //following paramters are ignored if CF_FILESRC is not set + // DWORD m_dwOrgSize; // original file size in bytes + // WORD m_bFactExists; // indicates if 'fact' chunk exists in the original file + // DWORD m_dwRiffChunkSize; // riff chunk size in the original file + // + // PCMWAVEFORMAT m_OrgWf; // original wave format + // }SLwFormat, *PSLwFormat; + + // shortcut + $thisfile_riff['litewave']['raw'] = array(); + $thisfile_riff_litewave = &$thisfile_riff['litewave']; + $thisfile_riff_litewave_raw = &$thisfile_riff_litewave['raw']; + + $thisfile_riff_litewave_raw['compression_method'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 18, 1)); + $thisfile_riff_litewave_raw['compression_flags'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 19, 1)); + $thisfile_riff_litewave_raw['m_dwScale'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 20, 4)); + $thisfile_riff_litewave_raw['m_dwBlockSize'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 24, 4)); + $thisfile_riff_litewave_raw['m_wQuality'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 28, 2)); + $thisfile_riff_litewave_raw['m_wMarkDistance'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 30, 2)); + $thisfile_riff_litewave_raw['m_wReserved'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 32, 2)); + $thisfile_riff_litewave_raw['m_dwOrgSize'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 34, 4)); + $thisfile_riff_litewave_raw['m_bFactExists'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 38, 2)); + $thisfile_riff_litewave_raw['m_dwRiffChunkSize'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 40, 4)); + + //$thisfile_riff_litewave['quality_factor'] = intval(round((2000 - $thisfile_riff_litewave_raw['m_dwScale']) / 20)); + $thisfile_riff_litewave['quality_factor'] = $thisfile_riff_litewave_raw['m_wQuality']; + + $thisfile_riff_litewave['flags']['raw_source'] = ($thisfile_riff_litewave_raw['compression_flags'] & 0x01) ? false : true; + $thisfile_riff_litewave['flags']['vbr_blocksize'] = ($thisfile_riff_litewave_raw['compression_flags'] & 0x02) ? false : true; + $thisfile_riff_litewave['flags']['seekpoints'] = (bool) ($thisfile_riff_litewave_raw['compression_flags'] & 0x04); + + $thisfile_audio['lossless'] = (($thisfile_riff_litewave_raw['m_wQuality'] == 100) ? true : false); + $thisfile_audio['encoder_options'] = '-q'.$thisfile_riff_litewave['quality_factor']; + break; + + default: + break; + } + } + if ($thisfile_avdataend > $ThisFileInfo['filesize']) { + switch (@$thisfile_audio_dataformat) { + case 'wavpack': // WavPack + case 'lpac': // LPAC + case 'ofr': // OptimFROG + case 'ofs': // OptimFROG DualStream + // lossless compressed audio formats that keep original RIFF headers - skip warning + break; + + case 'litewave': + if (($thisfile_avdataend - $ThisFileInfo['filesize']) == 1) { + // LiteWave appears to incorrectly *not* pad actual output file + // to nearest WORD boundary so may appear to be short by one + // byte, in which case - skip warning + } else { + // Short by more than one byte, throw warning + $ThisFileInfo['warning'][] = 'Probably truncated file - expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($ThisFileInfo['filesize'] - $thisfile_avdataoffset).' (short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($ThisFileInfo['filesize'] - $thisfile_avdataoffset)).' bytes)'; + $thisfile_avdataend = $ThisFileInfo['filesize']; + } + break; + + default: + if ((($thisfile_avdataend - $ThisFileInfo['filesize']) == 1) && (($thisfile_riff[$RIFFsubtype]['data'][0]['size'] % 2) == 0) && ((($ThisFileInfo['filesize'] - $thisfile_avdataoffset) % 2) == 1)) { + // output file appears to be incorrectly *not* padded to nearest WORD boundary + // Output less severe warning + $ThisFileInfo['warning'][] = 'File should probably be padded to nearest WORD boundary, but it is not (expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($ThisFileInfo['filesize'] - $thisfile_avdataoffset).' therefore short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($ThisFileInfo['filesize'] - $thisfile_avdataoffset)).' bytes)'; + $thisfile_avdataend = $ThisFileInfo['filesize']; + break; + } + // Short by more than one byte, throw warning + $ThisFileInfo['warning'][] = 'Probably truncated file - expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($ThisFileInfo['filesize'] - $thisfile_avdataoffset).' (short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($ThisFileInfo['filesize'] - $thisfile_avdataoffset)).' bytes)'; + $thisfile_avdataend = $ThisFileInfo['filesize']; + break; + } + } + if (!empty($ThisFileInfo['mpeg']['audio']['LAME']['audio_bytes'])) { + if ((($thisfile_avdataend - $thisfile_avdataoffset) - $ThisFileInfo['mpeg']['audio']['LAME']['audio_bytes']) == 1) { + $thisfile_avdataend--; + $ThisFileInfo['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored'; + } + } + if (@$thisfile_audio_dataformat == 'ac3') { + unset($thisfile_audio['bits_per_sample']); + if (!empty($ThisFileInfo['ac3']['bitrate']) && ($ThisFileInfo['ac3']['bitrate'] != $thisfile_audio['bitrate'])) { + $thisfile_audio['bitrate'] = $ThisFileInfo['ac3']['bitrate']; + } + } + break; + + case 'AVI ': + $thisfile_video['bitrate_mode'] = 'vbr'; // maybe not, but probably + $thisfile_video['dataformat'] = 'avi'; + $ThisFileInfo['mime_type'] = 'video/avi'; + + if (isset($thisfile_riff[$RIFFsubtype]['movi']['offset'])) { + $thisfile_avdataoffset = $thisfile_riff[$RIFFsubtype]['movi']['offset'] + 8; + $thisfile_avdataend = $thisfile_avdataoffset + $thisfile_riff[$RIFFsubtype]['movi']['size']; + if ($thisfile_avdataend > $ThisFileInfo['filesize']) { + $ThisFileInfo['warning'][] = 'Probably truncated file - expecting '.$thisfile_riff[$RIFFsubtype]['movi']['size'].' bytes of data, only found '.($ThisFileInfo['filesize'] - $thisfile_avdataoffset).' (short by '.($thisfile_riff[$RIFFsubtype]['movi']['size'] - ($ThisFileInfo['filesize'] - $thisfile_avdataoffset)).' bytes)'; + $thisfile_avdataend = $ThisFileInfo['filesize']; + } + } + + if (isset($thisfile_riff['AVI ']['hdrl']['avih'][$streamindex]['data'])) { + $avihData = $thisfile_riff['AVI ']['hdrl']['avih'][$streamindex]['data']; + + // shortcut + $thisfile_riff_raw['avih'] = array(); + $thisfile_riff_raw_avih = &$thisfile_riff_raw['avih']; + + $thisfile_riff_raw_avih['dwMicroSecPerFrame'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 0, 4)); // frame display rate (or 0L) + if ($thisfile_riff_raw_avih['dwMicroSecPerFrame'] == 0) { + $ThisFileInfo['error'][] = 'Corrupt RIFF file: avih.dwMicroSecPerFrame == zero'; + return false; + } + $thisfile_riff_raw_avih['dwMaxBytesPerSec'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 4, 4)); // max. transfer rate + $thisfile_riff_raw_avih['dwPaddingGranularity'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 8, 4)); // pad to multiples of this size; normally 2K. + $thisfile_riff_raw_avih['dwFlags'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 12, 4)); // the ever-present flags + $thisfile_riff_raw_avih['dwTotalFrames'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 16, 4)); // # frames in file + $thisfile_riff_raw_avih['dwInitialFrames'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 20, 4)); + $thisfile_riff_raw_avih['dwStreams'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 24, 4)); + $thisfile_riff_raw_avih['dwSuggestedBufferSize'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 28, 4)); + $thisfile_riff_raw_avih['dwWidth'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 32, 4)); + $thisfile_riff_raw_avih['dwHeight'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 36, 4)); + $thisfile_riff_raw_avih['dwScale'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 40, 4)); + $thisfile_riff_raw_avih['dwRate'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 44, 4)); + $thisfile_riff_raw_avih['dwStart'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 48, 4)); + $thisfile_riff_raw_avih['dwLength'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 52, 4)); + + $thisfile_riff_raw_avih['flags']['hasindex'] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & 0x00000010); + $thisfile_riff_raw_avih['flags']['mustuseindex'] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & 0x00000020); + $thisfile_riff_raw_avih['flags']['interleaved'] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & 0x00000100); + $thisfile_riff_raw_avih['flags']['trustcktype'] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & 0x00000800); + $thisfile_riff_raw_avih['flags']['capturedfile'] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & 0x00010000); + $thisfile_riff_raw_avih['flags']['copyrighted'] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & 0x00020010); + + // shortcut + $thisfile_riff_video[$streamindex] = array(); + $thisfile_riff_video_current = &$thisfile_riff_video[$streamindex]; + + if ($thisfile_riff_raw_avih['dwWidth'] > 0) { + $thisfile_riff_video_current['frame_width'] = $thisfile_riff_raw_avih['dwWidth']; + $thisfile_video['resolution_x'] = $thisfile_riff_video_current['frame_width']; + } + if ($thisfile_riff_raw_avih['dwHeight'] > 0) { + $thisfile_riff_video_current['frame_height'] = $thisfile_riff_raw_avih['dwHeight']; + $thisfile_video['resolution_y'] = $thisfile_riff_video_current['frame_height']; + } + if ($thisfile_riff_raw_avih['dwTotalFrames'] > 0) { + $thisfile_riff_video_current['total_frames'] = $thisfile_riff_raw_avih['dwTotalFrames']; + $thisfile_video['total_frames'] = $thisfile_riff_video_current['total_frames']; + } + + $thisfile_riff_video_current['frame_rate'] = round(1000000 / $thisfile_riff_raw_avih['dwMicroSecPerFrame'], 3); + $thisfile_video['frame_rate'] = $thisfile_riff_video_current['frame_rate']; + } + if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strh'][0]['data'])) { + if (is_array($thisfile_riff['AVI ']['hdrl']['strl']['strh'])) { + for ($i = 0; $i < count($thisfile_riff['AVI ']['hdrl']['strl']['strh']); $i++) { + if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strh'][$i]['data'])) { + $strhData = $thisfile_riff['AVI ']['hdrl']['strl']['strh'][$i]['data']; + $strhfccType = substr($strhData, 0, 4); + + if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strf'][$i]['data'])) { + $strfData = $thisfile_riff['AVI ']['hdrl']['strl']['strf'][$i]['data']; + + // shortcut + $thisfile_riff_raw_strf_strhfccType_streamindex = &$thisfile_riff_raw['strf'][$strhfccType][$streamindex]; + + switch ($strhfccType) { + case 'auds': + $thisfile_audio['bitrate_mode'] = 'cbr'; + $thisfile_audio_dataformat = 'wav'; + if (isset($thisfile_riff_audio) && is_array($thisfile_riff_audio)) { + $streamindex = count($thisfile_riff_audio); + } + + $thisfile_riff_audio[$streamindex] = getid3_riff::RIFFparseWAVEFORMATex($strfData); + $thisfile_audio['wformattag'] = $thisfile_riff_audio[$streamindex]['raw']['wFormatTag']; + + // shortcut + $thisfile_audio['streams'][$streamindex] = $thisfile_riff_audio[$streamindex]; + $thisfile_audio_streams_currentstream = &$thisfile_audio['streams'][$streamindex]; + + if ($thisfile_audio_streams_currentstream['bits_per_sample'] == 0) { + unset($thisfile_audio_streams_currentstream['bits_per_sample']); + } + $thisfile_audio_streams_currentstream['wformattag'] = $thisfile_audio_streams_currentstream['raw']['wFormatTag']; + unset($thisfile_audio_streams_currentstream['raw']); + + // shortcut + $thisfile_riff_raw['strf'][$strhfccType][$streamindex] = $thisfile_riff_audio[$streamindex]['raw']; + + unset($thisfile_riff_audio[$streamindex]['raw']); + $thisfile_audio = getid3_lib::array_merge_noclobber($thisfile_audio, $thisfile_riff_audio[$streamindex]); + + $thisfile_audio['lossless'] = false; + switch ($thisfile_riff_raw_strf_strhfccType_streamindex['wFormatTag']) { + case 0x0001: // PCM + $thisfile_audio_dataformat = 'wav'; + $thisfile_audio['lossless'] = true; + break; + + case 0x0050: // MPEG Layer 2 or Layer 1 + $thisfile_audio_dataformat = 'mp2'; // Assume Layer-2 + break; + + case 0x0055: // MPEG Layer 3 + $thisfile_audio_dataformat = 'mp3'; + break; + + case 0x00FF: // AAC + $thisfile_audio_dataformat = 'aac'; + break; + + case 0x0161: // Windows Media v7 / v8 / v9 + case 0x0162: // Windows Media Professional v9 + case 0x0163: // Windows Media Lossess v9 + $thisfile_audio_dataformat = 'wma'; + break; + + case 0x2000: // AC-3 + $thisfile_audio_dataformat = 'ac3'; + break; + + case 0x2001: // DTS + $thisfile_audio_dataformat = 'dts'; + break; + + default: + $thisfile_audio_dataformat = 'wav'; + break; + } + $thisfile_audio_streams_currentstream['dataformat'] = $thisfile_audio_dataformat; + $thisfile_audio_streams_currentstream['lossless'] = $thisfile_audio['lossless']; + $thisfile_audio_streams_currentstream['bitrate_mode'] = $thisfile_audio['bitrate_mode']; + break; + + + case 'iavs': + case 'vids': + // shortcut + $thisfile_riff_raw['strh'][$i] = array(); + $thisfile_riff_raw_strh_current = &$thisfile_riff_raw['strh'][$i]; + + $thisfile_riff_raw_strh_current['fccType'] = substr($strhData, 0, 4); // same as $strhfccType; + $thisfile_riff_raw_strh_current['fccHandler'] = substr($strhData, 4, 4); + $thisfile_riff_raw_strh_current['dwFlags'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 8, 4)); // Contains AVITF_* flags + $thisfile_riff_raw_strh_current['wPriority'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 12, 2)); + $thisfile_riff_raw_strh_current['wLanguage'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 14, 2)); + $thisfile_riff_raw_strh_current['dwInitialFrames'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 16, 4)); + $thisfile_riff_raw_strh_current['dwScale'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 20, 4)); + $thisfile_riff_raw_strh_current['dwRate'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 24, 4)); + $thisfile_riff_raw_strh_current['dwStart'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 28, 4)); + $thisfile_riff_raw_strh_current['dwLength'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 32, 4)); + $thisfile_riff_raw_strh_current['dwSuggestedBufferSize'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 36, 4)); + $thisfile_riff_raw_strh_current['dwQuality'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 40, 4)); + $thisfile_riff_raw_strh_current['dwSampleSize'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 44, 4)); + $thisfile_riff_raw_strh_current['rcFrame'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 48, 4)); + + $thisfile_riff_video_current['codec'] = getid3_riff::RIFFfourccLookup($thisfile_riff_raw_strh_current['fccHandler']); + $thisfile_video['fourcc'] = $thisfile_riff_raw_strh_current['fccHandler']; + if (!$thisfile_riff_video_current['codec'] && isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']) && getid3_riff::RIFFfourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) { + $thisfile_riff_video_current['codec'] = getid3_riff::RIFFfourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']); + $thisfile_video['fourcc'] = $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']; + } + $thisfile_video['codec'] = $thisfile_riff_video_current['codec']; + $thisfile_video['pixel_aspect_ratio'] = (float) 1; + switch ($thisfile_riff_raw_strh_current['fccHandler']) { + case 'HFYU': // Huffman Lossless Codec + case 'IRAW': // Intel YUV Uncompressed + case 'YUY2': // Uncompressed YUV 4:2:2 + $thisfile_video['lossless'] = true; + break; + + default: + $thisfile_video['lossless'] = false; + break; + } + + switch ($strhfccType) { + case 'vids': + $thisfile_riff_raw_strf_strhfccType_streamindex['biSize'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 0, 4)); // number of bytes required by the BITMAPINFOHEADER structure + $thisfile_riff_raw_strf_strhfccType_streamindex['biWidth'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 4, 4)); // width of the bitmap in pixels + $thisfile_riff_raw_strf_strhfccType_streamindex['biHeight'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 8, 4)); // height of the bitmap in pixels. If biHeight is positive, the bitmap is a 'bottom-up' DIB and its origin is the lower left corner. If biHeight is negative, the bitmap is a 'top-down' DIB and its origin is the upper left corner + $thisfile_riff_raw_strf_strhfccType_streamindex['biPlanes'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 12, 2)); // number of color planes on the target device. In most cases this value must be set to 1 + $thisfile_riff_raw_strf_strhfccType_streamindex['biBitCount'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 14, 2)); // Specifies the number of bits per pixels + $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'] = substr($strfData, 16, 4); // + $thisfile_riff_raw_strf_strhfccType_streamindex['biSizeImage'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 20, 4)); // size of the bitmap data section of the image (the actual pixel data, excluding BITMAPINFOHEADER and RGBQUAD structures) + $thisfile_riff_raw_strf_strhfccType_streamindex['biXPelsPerMeter'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 24, 4)); // horizontal resolution, in pixels per metre, of the target device + $thisfile_riff_raw_strf_strhfccType_streamindex['biYPelsPerMeter'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 28, 4)); // vertical resolution, in pixels per metre, of the target device + $thisfile_riff_raw_strf_strhfccType_streamindex['biClrUsed'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 32, 4)); // actual number of color indices in the color table used by the bitmap. If this value is zero, the bitmap uses the maximum number of colors corresponding to the value of the biBitCount member for the compression mode specified by biCompression + $thisfile_riff_raw_strf_strhfccType_streamindex['biClrImportant'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 36, 4)); // number of color indices that are considered important for displaying the bitmap. If this value is zero, all colors are important + + $thisfile_video['bits_per_sample'] = $thisfile_riff_raw_strf_strhfccType_streamindex['biBitCount']; + + if ($thisfile_riff_video_current['codec'] == 'DV') { + $thisfile_riff_video_current['dv_type'] = 2; + } + break; + + case 'iavs': + $thisfile_riff_video_current['dv_type'] = 1; + break; + } + break; + + default: + $ThisFileInfo['warning'][] = 'Unhandled fccType for stream ('.$i.'): "'.$strhfccType.'"'; + break; + + } + } + } + + if (isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']) && getid3_riff::RIFFfourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) { + + $thisfile_riff_video_current['codec'] = getid3_riff::RIFFfourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']); + $thisfile_video['codec'] = $thisfile_riff_video_current['codec']; + $thisfile_video['fourcc'] = $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']; + + switch ($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']) { + case 'HFYU': // Huffman Lossless Codec + case 'IRAW': // Intel YUV Uncompressed + case 'YUY2': // Uncompressed YUV 4:2:2 + $thisfile_video['lossless'] = true; + $thisfile_video['bits_per_sample'] = 24; + break; + + default: + $thisfile_video['lossless'] = false; + $thisfile_video['bits_per_sample'] = 24; + break; + } + + } + } + } + } + break; + + case 'CDDA': + $thisfile_audio['bitrate_mode'] = 'cbr'; + $thisfile_audio_dataformat = 'cda'; + $thisfile_audio['lossless'] = true; + unset($ThisFileInfo['mime_type']); + + $thisfile_avdataoffset = 44; + + if (isset($thisfile_riff['CDDA']['fmt '][0]['data'])) { + // shortcut + $thisfile_riff_CDDA_fmt_0 = &$thisfile_riff['CDDA']['fmt '][0]; + + $thisfile_riff_CDDA_fmt_0['unknown1'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_CDDA_fmt_0['data'], 0, 2)); + $thisfile_riff_CDDA_fmt_0['track_num'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_CDDA_fmt_0['data'], 2, 2)); + $thisfile_riff_CDDA_fmt_0['disc_id'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_CDDA_fmt_0['data'], 4, 4)); + $thisfile_riff_CDDA_fmt_0['start_offset_frame'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_CDDA_fmt_0['data'], 8, 4)); + $thisfile_riff_CDDA_fmt_0['playtime_frames'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_CDDA_fmt_0['data'], 12, 4)); + $thisfile_riff_CDDA_fmt_0['unknown6'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_CDDA_fmt_0['data'], 16, 4)); + $thisfile_riff_CDDA_fmt_0['unknown7'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_CDDA_fmt_0['data'], 20, 4)); + + $thisfile_riff_CDDA_fmt_0['start_offset_seconds'] = (float) $thisfile_riff_CDDA_fmt_0['start_offset_frame'] / 75; + $thisfile_riff_CDDA_fmt_0['playtime_seconds'] = (float) $thisfile_riff_CDDA_fmt_0['playtime_frames'] / 75; + $ThisFileInfo['comments']['track'] = $thisfile_riff_CDDA_fmt_0['track_num']; + $ThisFileInfo['playtime_seconds'] = $thisfile_riff_CDDA_fmt_0['playtime_seconds']; + + // hardcoded data for CD-audio + $thisfile_audio['sample_rate'] = 44100; + $thisfile_audio['channels'] = 2; + $thisfile_audio['bits_per_sample'] = 16; + $thisfile_audio['bitrate'] = $thisfile_audio['sample_rate'] * $thisfile_audio['channels'] * $thisfile_audio['bits_per_sample']; + $thisfile_audio['bitrate_mode'] = 'cbr'; + } + break; + + + case 'AIFF': + case 'AIFC': + $thisfile_audio['bitrate_mode'] = 'cbr'; + $thisfile_audio_dataformat = 'aiff'; + $thisfile_audio['lossless'] = true; + $ThisFileInfo['mime_type'] = 'audio/x-aiff'; + + if (isset($thisfile_riff[$RIFFsubtype]['SSND'][0]['offset'])) { + $thisfile_avdataoffset = $thisfile_riff[$RIFFsubtype]['SSND'][0]['offset'] + 8; + $thisfile_avdataend = $thisfile_avdataoffset + $thisfile_riff[$RIFFsubtype]['SSND'][0]['size']; + if ($thisfile_avdataend > $ThisFileInfo['filesize']) { + if (($thisfile_avdataend == ($ThisFileInfo['filesize'] + 1)) && (($ThisFileInfo['filesize'] % 2) == 1)) { + // structures rounded to 2-byte boundary, but dumb encoders + // forget to pad end of file to make this actually work + } else { + $ThisFileInfo['warning'][] = 'Probable truncated AIFF file: expecting '.$thisfile_riff[$RIFFsubtype]['SSND'][0]['size'].' bytes of audio data, only '.($ThisFileInfo['filesize'] - $thisfile_avdataoffset).' bytes found'; + } + $thisfile_avdataend = $ThisFileInfo['filesize']; + } + } + + if (isset($thisfile_riff[$RIFFsubtype]['COMM'][0]['data'])) { + + // shortcut + $thisfile_riff_RIFFsubtype_COMM_0_data = &$thisfile_riff[$RIFFsubtype]['COMM'][0]['data']; + + $thisfile_riff_audio['channels'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 0, 2), true); + $thisfile_riff_audio['total_samples'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 2, 4), false); + $thisfile_riff_audio['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 6, 2), true); + $thisfile_riff_audio['sample_rate'] = (int) getid3_lib::BigEndian2Float(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 8, 10)); + + if ($thisfile_riff[$RIFFsubtype]['COMM'][0]['size'] > 18) { + $thisfile_riff_audio['codec_fourcc'] = substr($thisfile_riff_RIFFsubtype_COMM_0_data, 18, 4); + $CodecNameSize = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 22, 1), false); + $thisfile_riff_audio['codec_name'] = substr($thisfile_riff_RIFFsubtype_COMM_0_data, 23, $CodecNameSize); + switch ($thisfile_riff_audio['codec_name']) { + case 'NONE': + $thisfile_audio['codec'] = 'Pulse Code Modulation (PCM)'; + $thisfile_audio['lossless'] = true; + break; + + case '': + switch ($thisfile_riff_audio['codec_fourcc']) { + // http://developer.apple.com/qa/snd/snd07.html + case 'sowt': + $thisfile_riff_audio['codec_name'] = 'Two\'s Compliment Little-Endian PCM'; + $thisfile_audio['lossless'] = true; + break; + + case 'twos': + $thisfile_riff_audio['codec_name'] = 'Two\'s Compliment Big-Endian PCM'; + $thisfile_audio['lossless'] = true; + break; + + default: + break; + } + break; + + default: + $thisfile_audio['codec'] = $thisfile_riff_audio['codec_name']; + $thisfile_audio['lossless'] = false; + break; + } + } + + $thisfile_audio['channels'] = $thisfile_riff_audio['channels']; + if ($thisfile_riff_audio['bits_per_sample'] > 0) { + $thisfile_audio['bits_per_sample'] = $thisfile_riff_audio['bits_per_sample']; + } + $thisfile_audio['sample_rate'] = $thisfile_riff_audio['sample_rate']; + if ($thisfile_audio['sample_rate'] == 0) { + $ThisFileInfo['error'][] = 'Corrupted AIFF file: sample_rate == zero'; + return false; + } + $ThisFileInfo['playtime_seconds'] = $thisfile_riff_audio['total_samples'] / $thisfile_audio['sample_rate']; + } + + if (isset($thisfile_riff[$RIFFsubtype]['COMT'])) { + $offset = 0; + $CommentCount = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), false); + $offset += 2; + for ($i = 0; $i < $CommentCount; $i++) { + $ThisFileInfo['comments_raw'][$i]['timestamp'] = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 4), false); + $offset += 4; + $ThisFileInfo['comments_raw'][$i]['marker_id'] = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), true); + $offset += 2; + $CommentLength = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), false); + $offset += 2; + $ThisFileInfo['comments_raw'][$i]['comment'] = substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, $CommentLength); + $offset += $CommentLength; + + $ThisFileInfo['comments_raw'][$i]['timestamp_unix'] = getid3_lib::DateMac2Unix($ThisFileInfo['comments_raw'][$i]['timestamp']); + $thisfile_riff['comments']['comment'][] = $ThisFileInfo['comments_raw'][$i]['comment']; + } + } + + $CommentsChunkNames = array('NAME'=>'title', 'author'=>'artist', '(c) '=>'copyright', 'ANNO'=>'comment'); + foreach ($CommentsChunkNames as $key => $value) { + if (isset($thisfile_riff[$RIFFsubtype][$key][0]['data'])) { + $thisfile_riff['comments'][$value][] = $thisfile_riff[$RIFFsubtype][$key][0]['data']; + } + } + break; + + case '8SVX': + $thisfile_audio['bitrate_mode'] = 'cbr'; + $thisfile_audio_dataformat = '8svx'; + $thisfile_audio['bits_per_sample'] = 8; + $thisfile_audio['channels'] = 1; // overridden below, if need be + $ThisFileInfo['mime_type'] = 'audio/x-aiff'; + + if (isset($thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'])) { + $thisfile_avdataoffset = $thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'] + 8; + $thisfile_avdataend = $thisfile_avdataoffset + $thisfile_riff[$RIFFsubtype]['BODY'][0]['size']; + if ($thisfile_avdataend > $ThisFileInfo['filesize']) { + $ThisFileInfo['warning'][] = 'Probable truncated AIFF file: expecting '.$thisfile_riff[$RIFFsubtype]['BODY'][0]['size'].' bytes of audio data, only '.($ThisFileInfo['filesize'] - $thisfile_avdataoffset).' bytes found'; + } + } + + if (isset($thisfile_riff[$RIFFsubtype]['VHDR'][0]['offset'])) { + // shortcut + $thisfile_riff_RIFFsubtype_VHDR_0 = &$thisfile_riff[$RIFFsubtype]['VHDR'][0]; + + $thisfile_riff_RIFFsubtype_VHDR_0['oneShotHiSamples'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 0, 4)); + $thisfile_riff_RIFFsubtype_VHDR_0['repeatHiSamples'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 4, 4)); + $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerHiCycle'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 8, 4)); + $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerSec'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 12, 2)); + $thisfile_riff_RIFFsubtype_VHDR_0['ctOctave'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 14, 1)); + $thisfile_riff_RIFFsubtype_VHDR_0['sCompression'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 15, 1)); + $thisfile_riff_RIFFsubtype_VHDR_0['Volume'] = getid3_lib::FixedPoint16_16(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 16, 4)); + + $thisfile_audio['sample_rate'] = $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerSec']; + + switch ($thisfile_riff_RIFFsubtype_VHDR_0['sCompression']) { + case 0: + $thisfile_audio['codec'] = 'Pulse Code Modulation (PCM)'; + $thisfile_audio['lossless'] = true; + $ActualBitsPerSample = 8; + break; + + case 1: + $thisfile_audio['codec'] = 'Fibonacci-delta encoding'; + $thisfile_audio['lossless'] = false; + $ActualBitsPerSample = 4; + break; + + default: + $ThisFileInfo['warning'][] = 'Unexpected sCompression value in 8SVX.VHDR chunk - expecting 0 or 1, found "'.sCompression.'"'; + break; + } + } + + if (isset($thisfile_riff[$RIFFsubtype]['CHAN'][0]['data'])) { + $ChannelsIndex = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['CHAN'][0]['data'], 0, 4)); + switch ($ChannelsIndex) { + case 6: // Stereo + $thisfile_audio['channels'] = 2; + break; + + case 2: // Left channel only + case 4: // Right channel only + $thisfile_audio['channels'] = 1; + break; + + default: + $ThisFileInfo['warning'][] = 'Unexpected value in 8SVX.CHAN chunk - expecting 2 or 4 or 6, found "'.$ChannelsIndex.'"'; + break; + } + + } + + $CommentsChunkNames = array('NAME'=>'title', 'author'=>'artist', '(c) '=>'copyright', 'ANNO'=>'comment'); + foreach ($CommentsChunkNames as $key => $value) { + if (isset($thisfile_riff[$RIFFsubtype][$key][0]['data'])) { + $thisfile_riff['comments'][$value][] = $thisfile_riff[$RIFFsubtype][$key][0]['data']; + } + } + + $thisfile_audio['bitrate'] = $thisfile_audio['sample_rate'] * $ActualBitsPerSample * $thisfile_audio['channels']; + if (!empty($thisfile_audio['bitrate'])) { + $ThisFileInfo['playtime_seconds'] = ($thisfile_avdataend - $thisfile_avdataoffset) / ($thisfile_audio['bitrate'] / 8); + } + break; + + + case 'CDXA': + $ThisFileInfo['mime_type'] = 'video/mpeg'; + if (!empty($thisfile_riff['CDXA']['data'][0]['size'])) { + $GETID3_ERRORARRAY = &$ThisFileInfo['warning']; + if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.mpeg.php', __FILE__, false)) { + $dummy = $ThisFileInfo; + $dummy['error'] = array(); + $mpeg_scanner = new getid3_mpeg($fd, $dummy); + if (empty($dummy['error'])) { + $ThisFileInfo['audio'] = $dummy['audio']; + $ThisFileInfo['video'] = $dummy['video']; + $ThisFileInfo['mpeg'] = $dummy['mpeg']; + $ThisFileInfo['warning'] = $dummy['warning']; + } + } + } + break; + + + default: + $ThisFileInfo['error'][] = 'Unknown RIFF type: expecting one of (WAVE|RMP3|AVI |CDDA|AIFF|AIFC|8SVX|CDXA), found "'.$RIFFsubtype.'" instead'; + unset($ThisFileInfo['fileformat']); + break; + } + + if (isset($thisfile_riff_WAVE['DISP']) && is_array($thisfile_riff_WAVE['DISP'])) { + $thisfile_riff['comments']['title'][] = trim(substr($thisfile_riff_WAVE['DISP'][count($thisfile_riff_WAVE['DISP']) - 1]['data'], 4)); + } + if (isset($thisfile_riff_WAVE['INFO']) && is_array($thisfile_riff_WAVE['INFO'])) { + $this->RIFFcommentsParse($thisfile_riff_WAVE['INFO'], $thisfile_riff['comments']); + } + + if (empty($thisfile_audio['encoder']) && !empty($ThisFileInfo['mpeg']['audio']['LAME']['short_version'])) { + $thisfile_audio['encoder'] = $ThisFileInfo['mpeg']['audio']['LAME']['short_version']; + } + + if (!isset($ThisFileInfo['playtime_seconds'])) { + $ThisFileInfo['playtime_seconds'] = 0; + } + if (isset($thisfile_riff_raw['avih']['dwTotalFrames']) && isset($thisfile_riff_raw['avih']['dwMicroSecPerFrame'])) { + $ThisFileInfo['playtime_seconds'] = $thisfile_riff_raw['avih']['dwTotalFrames'] * ($thisfile_riff_raw['avih']['dwMicroSecPerFrame'] / 1000000); + } + + if ($ThisFileInfo['playtime_seconds'] > 0) { + if (isset($thisfile_riff_audio) && isset($thisfile_riff_video)) { + + if (!isset($ThisFileInfo['bitrate'])) { + $ThisFileInfo['bitrate'] = ((($thisfile_avdataend - $thisfile_avdataoffset) / $ThisFileInfo['playtime_seconds']) * 8); + } + + } elseif (isset($thisfile_riff_audio) && !isset($thisfile_riff_video)) { + + if (!isset($thisfile_audio['bitrate'])) { + $thisfile_audio['bitrate'] = ((($thisfile_avdataend - $thisfile_avdataoffset) / $ThisFileInfo['playtime_seconds']) * 8); + } + + } elseif (!isset($thisfile_riff_audio) && isset($thisfile_riff_video)) { + + if (!isset($thisfile_video['bitrate'])) { + $thisfile_video['bitrate'] = ((($thisfile_avdataend - $thisfile_avdataoffset) / $ThisFileInfo['playtime_seconds']) * 8); + } + + } + } + + + if (isset($thisfile_riff_video) && isset($thisfile_audio['bitrate']) && ($thisfile_audio['bitrate'] > 0) && ($ThisFileInfo['playtime_seconds'] > 0)) { + + $ThisFileInfo['bitrate'] = ((($thisfile_avdataend - $thisfile_avdataoffset) / $ThisFileInfo['playtime_seconds']) * 8); + $thisfile_audio['bitrate'] = 0; + $thisfile_video['bitrate'] = $ThisFileInfo['bitrate']; + foreach ($thisfile_riff_audio as $channelnumber => $audioinfoarray) { + $thisfile_video['bitrate'] -= $audioinfoarray['bitrate']; + $thisfile_audio['bitrate'] += $audioinfoarray['bitrate']; + } + if ($thisfile_video['bitrate'] <= 0) { + unset($thisfile_video['bitrate']); + } + if ($thisfile_audio['bitrate'] <= 0) { + unset($thisfile_audio['bitrate']); + } + } + + if (isset($ThisFileInfo['mpeg']['audio'])) { + $thisfile_audio_dataformat = 'mp'.$ThisFileInfo['mpeg']['audio']['layer']; + $thisfile_audio['sample_rate'] = $ThisFileInfo['mpeg']['audio']['sample_rate']; + $thisfile_audio['channels'] = $ThisFileInfo['mpeg']['audio']['channels']; + $thisfile_audio['bitrate'] = $ThisFileInfo['mpeg']['audio']['bitrate']; + $thisfile_audio['bitrate_mode'] = strtolower($ThisFileInfo['mpeg']['audio']['bitrate_mode']); + if (!empty($ThisFileInfo['mpeg']['audio']['codec'])) { + $thisfile_audio['codec'] = $ThisFileInfo['mpeg']['audio']['codec'].' '.$thisfile_audio['codec']; + } + if (!empty($thisfile_audio['streams'])) { + foreach ($thisfile_audio['streams'] as $streamnumber => $streamdata) { + if ($streamdata['dataformat'] == $thisfile_audio_dataformat) { + $thisfile_audio['streams'][$streamnumber]['sample_rate'] = $thisfile_audio['sample_rate']; + $thisfile_audio['streams'][$streamnumber]['channels'] = $thisfile_audio['channels']; + $thisfile_audio['streams'][$streamnumber]['bitrate'] = $thisfile_audio['bitrate']; + $thisfile_audio['streams'][$streamnumber]['bitrate_mode'] = $thisfile_audio['bitrate_mode']; + $thisfile_audio['streams'][$streamnumber]['codec'] = $thisfile_audio['codec']; + } + } + } + $thisfile_audio['encoder_options'] = getid3_mp3::GuessEncoderOptions($ThisFileInfo); + } + + + if (!empty($thisfile_riff_raw['fmt ']['wBitsPerSample']) && ($thisfile_riff_raw['fmt ']['wBitsPerSample'] > 0)) { + switch ($thisfile_audio_dataformat) { + case 'ac3': + // ignore bits_per_sample + break; + + default: + $thisfile_audio['bits_per_sample'] = $thisfile_riff_raw['fmt ']['wBitsPerSample']; + break; + } + } + + + if (empty($thisfile_riff_raw)) { + unset($thisfile_riff['raw']); + } + if (empty($thisfile_riff_audio)) { + unset($thisfile_riff['audio']); + } + if (empty($thisfile_riff_video)) { + unset($thisfile_riff['video']); + } + + return true; + } + + + function RIFFcommentsParse(&$RIFFinfoArray, &$CommentsTargetArray) { + $RIFFinfoKeyLookup = array( + 'IARL'=>'archivallocation', + 'IART'=>'artist', + 'ICDS'=>'costumedesigner', + 'ICMS'=>'commissionedby', + 'ICMT'=>'comment', + 'ICNT'=>'country', + 'ICOP'=>'copyright', + 'ICRD'=>'creationdate', + 'IDIM'=>'dimensions', + 'IDIT'=>'digitizationdate', + 'IDPI'=>'resolution', + 'IDST'=>'distributor', + 'IEDT'=>'editor', + 'IENG'=>'engineers', + 'IFRM'=>'accountofparts', + 'IGNR'=>'genre', + 'IKEY'=>'keywords', + 'ILGT'=>'lightness', + 'ILNG'=>'language', + 'IMED'=>'orignalmedium', + 'IMUS'=>'composer', + 'INAM'=>'title', + 'IPDS'=>'productiondesigner', + 'IPLT'=>'palette', + 'IPRD'=>'product', + 'IPRO'=>'producer', + 'IPRT'=>'part', + 'IRTD'=>'rating', + 'ISBJ'=>'subject', + 'ISFT'=>'software', + 'ISGN'=>'secondarygenre', + 'ISHP'=>'sharpness', + 'ISRC'=>'sourcesupplier', + 'ISRF'=>'digitizationsource', + 'ISTD'=>'productionstudio', + 'ISTR'=>'starring', + 'ITCH'=>'encoded_by', + 'IWEB'=>'url', + 'IWRI'=>'writer' + ); + foreach ($RIFFinfoKeyLookup as $key => $value) { + if (isset($RIFFinfoArray[$key])) { + foreach ($RIFFinfoArray[$key] as $commentid => $commentdata) { + if (trim($commentdata['data']) != '') { + @$CommentsTargetArray[$value][] = trim($commentdata['data']); + } + } + } + } + return true; + } + + function ParseRIFF(&$fd, $startoffset, $maxoffset, &$ThisFileInfo) { + + $maxoffset = min($maxoffset, $ThisFileInfo['avdataend']); + + $RIFFchunk = false; + + fseek($fd, $startoffset, SEEK_SET); + + while (ftell($fd) < $maxoffset) { + $chunkname = fread($fd, 4); + if (strlen($chunkname) < 4) { + $ThisFileInfo['error'][] = 'Expecting chunk name at offset '.(ftell($fd) - 4).' but found nothing. Aborting RIFF parsing.'; + break; + } + + $chunksize = getid3_riff::EitherEndian2Int($ThisFileInfo, fread($fd, 4)); + if ($chunksize == 0) { + $ThisFileInfo['error'][] = 'Chunk size at offset '.(ftell($fd) - 4).' is zero. Aborting RIFF parsing.'; + break; + } + if (($chunksize % 2) != 0) { + // all structures are packed on word boundaries + $chunksize++; + } + + switch ($chunkname) { + case 'LIST': + $listname = fread($fd, 4); + switch ($listname) { + case 'movi': + case 'rec ': + $RIFFchunk[$listname]['offset'] = ftell($fd) - 4; + $RIFFchunk[$listname]['size'] = $chunksize; + + static $ParsedAudioStream = false; + if ($ParsedAudioStream) { + + // skip over + + } else { + + $WhereWeWere = ftell($fd); + $AudioChunkHeader = fread($fd, 12); + $AudioChunkStreamNum = substr($AudioChunkHeader, 0, 2); + $AudioChunkStreamType = substr($AudioChunkHeader, 2, 2); + $AudioChunkSize = getid3_lib::LittleEndian2Int(substr($AudioChunkHeader, 4, 4)); + + if ($AudioChunkStreamType == 'wb') { + $FirstFourBytes = substr($AudioChunkHeader, 8, 4); + if (preg_match('/^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]/s', $FirstFourBytes)) { + + // MP3 + if (getid3_mp3::MPEGaudioHeaderBytesValid($FirstFourBytes)) { + $dummy = $ThisFileInfo; + $dummy['avdataoffset'] = ftell($fd) - 4; + $dummy['avdataend'] = ftell($fd) + $AudioChunkSize; + getid3_mp3::getOnlyMPEGaudioInfo($fd, $dummy, $dummy['avdataoffset'], false); + if (isset($dummy['mpeg']['audio'])) { + $ThisFileInfo = $dummy; + $ThisFileInfo['audio']['dataformat'] = 'mp'.$ThisFileInfo['mpeg']['audio']['layer']; + $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['mpeg']['audio']['sample_rate']; + $ThisFileInfo['audio']['channels'] = $ThisFileInfo['mpeg']['audio']['channels']; + $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['bitrate']; + $ThisFileInfo['bitrate'] = $ThisFileInfo['audio']['bitrate']; + $ThisFileInfo['audio']['bitrate_mode'] = strtolower($ThisFileInfo['mpeg']['audio']['bitrate_mode']); + } + } + + } elseif (preg_match('/^\x0B\x77/s', $FirstFourBytes)) { + + // AC3 + $GETID3_ERRORARRAY = &$ThisFileInfo['warning']; + if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, false)) { + + $dummy = $ThisFileInfo; + $dummy['avdataoffset'] = ftell($fd) - 4; + $dummy['avdataend'] = ftell($fd) + $AudioChunkSize; + $dummy['error'] = array(); + $ac3_tag = new getid3_ac3($fd, $dummy); + if (empty($dummy['error'])) { + $ThisFileInfo['audio'] = $dummy['audio']; + $ThisFileInfo['ac3'] = $dummy['ac3']; + $ThisFileInfo['warning'] = $dummy['warning']; + } + + } + + } + + } + + $ParsedAudioStream = true; + fseek($fd, $WhereWeWere, SEEK_SET); + + } + fseek($fd, $chunksize - 4, SEEK_CUR); + break; + + default: + if (!isset($RIFFchunk[$listname])) { + $RIFFchunk[$listname] = array(); + } + $LISTchunkParent = $listname; + $LISTchunkMaxOffset = ftell($fd) - 4 + $chunksize; + if ($parsedChunk = getid3_riff::ParseRIFF($fd, ftell($fd), ftell($fd) + $chunksize - 4, $ThisFileInfo)) { + $RIFFchunk[$listname] = array_merge_recursive($RIFFchunk[$listname], $parsedChunk); + } + break; + } + break; + + default: + $thisindex = 0; + if (isset($RIFFchunk[$chunkname]) && is_array($RIFFchunk[$chunkname])) { + $thisindex = count($RIFFchunk[$chunkname]); + } + $RIFFchunk[$chunkname][$thisindex]['offset'] = ftell($fd) - 8; + $RIFFchunk[$chunkname][$thisindex]['size'] = $chunksize; + switch ($chunkname) { + case 'data': + $ThisFileInfo['avdataoffset'] = ftell($fd); + $ThisFileInfo['avdataend'] = $ThisFileInfo['avdataoffset'] + $chunksize; + + $RIFFdataChunkContentsTest = fread($fd, 36); + + if ((strlen($RIFFdataChunkContentsTest) > 0) && preg_match('/^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]/s', substr($RIFFdataChunkContentsTest, 0, 4))) { + + // Probably is MP3 data + if (getid3_mp3::MPEGaudioHeaderBytesValid(substr($RIFFdataChunkContentsTest, 0, 4))) { + getid3_mp3::getOnlyMPEGaudioInfo($fd, $ThisFileInfo, $RIFFchunk[$chunkname][$thisindex]['offset'], false); + } + + } elseif ((strlen($RIFFdataChunkContentsTest) > 0) && (substr($RIFFdataChunkContentsTest, 0, 2) == "\x0B\x77")) { + + // This is probably AC-3 data + $GETID3_ERRORARRAY = &$ThisFileInfo['warning']; + if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, false)) { + + $dummy = $ThisFileInfo; + $dummy['avdataoffset'] = $RIFFchunk[$chunkname][$thisindex]['offset']; + $dummy['avdataend'] = $dummy['avdataoffset'] + $RIFFchunk[$chunkname][$thisindex]['size']; + $dummy['error'] = array(); + + $ac3_tag = new getid3_ac3($fd, $dummy); + if (empty($dummy['error'])) { + $ThisFileInfo['audio'] = $dummy['audio']; + $ThisFileInfo['ac3'] = $dummy['ac3']; + $ThisFileInfo['warning'] = $dummy['warning']; + } + + } + + } elseif ((strlen($RIFFdataChunkContentsTest) > 0) && (substr($RIFFdataChunkContentsTest, 8, 2) == "\x77\x0B")) { + + // Dolby Digital WAV + // AC-3 content, but not encoded in same format as normal AC-3 file + // For one thing, byte order is swapped + + $GETID3_ERRORARRAY = &$ThisFileInfo['warning']; + if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, false)) { + + // ok to use tmpfile here - only 56 bytes + if ($fd_temp = tmpfile()) { + + for ($i = 0; $i < 28; $i += 2) { + // swap byte order + fwrite($fd_temp, substr($RIFFdataChunkContentsTest, 8 + $i + 1, 1)); + fwrite($fd_temp, substr($RIFFdataChunkContentsTest, 8 + $i + 0, 1)); + } + + $dummy = $ThisFileInfo; + $dummy['avdataoffset'] = 0; + $dummy['avdataend'] = 20; + $dummy['error'] = array(); + $ac3_tag = new getid3_ac3($fd_temp, $dummy); + fclose($fd_temp); + if (empty($dummy['error'])) { + $ThisFileInfo['audio'] = $dummy['audio']; + $ThisFileInfo['ac3'] = $dummy['ac3']; + $ThisFileInfo['warning'] = $dummy['warning']; + } else { + $ThisFileInfo['error'][] = 'Errors parsing DolbyDigital WAV: '.explode(';', $dummy['error']); + } + + } else { + + $ThisFileInfo['error'][] = 'Could not create temporary file to analyze DolbyDigital WAV'; + + } + + } + + } elseif ((strlen($RIFFdataChunkContentsTest) > 0) && (substr($RIFFdataChunkContentsTest, 0, 4) == 'wvpk')) { + + // This is WavPack data + $ThisFileInfo['wavpack']['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset']; + $ThisFileInfo['wavpack']['size'] = getid3_lib::LittleEndian2Int(substr($RIFFdataChunkContentsTest, 4, 4)); + getid3_riff::RIFFparseWavPackHeader(substr($RIFFdataChunkContentsTest, 8, 28), $ThisFileInfo); + + } else { + + // This is some other kind of data (quite possibly just PCM) + // do nothing special, just skip it + + } + fseek($fd, $RIFFchunk[$chunkname][$thisindex]['offset'] + 8 + $chunksize, SEEK_SET); + break; + + case 'bext': + case 'cart': + case 'fmt ': + case 'MEXT': + case 'DISP': + // always read data in + $RIFFchunk[$chunkname][$thisindex]['data'] = fread($fd, $chunksize); + break; + + default: + if (!empty($LISTchunkParent) && (($RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size']) <= $LISTchunkMaxOffset)) { + $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset']; + $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['size'] = $RIFFchunk[$chunkname][$thisindex]['size']; + unset($RIFFchunk[$chunkname][$thisindex]['offset']); + unset($RIFFchunk[$chunkname][$thisindex]['size']); + if (isset($RIFFchunk[$chunkname][$thisindex]) && empty($RIFFchunk[$chunkname][$thisindex])) { + unset($RIFFchunk[$chunkname][$thisindex]); + } + if (isset($RIFFchunk[$chunkname]) && empty($RIFFchunk[$chunkname])) { + unset($RIFFchunk[$chunkname]); + } + $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['data'] = fread($fd, $chunksize); + } elseif ($chunksize < 2048) { + // only read data in if smaller than 2kB + $RIFFchunk[$chunkname][$thisindex]['data'] = fread($fd, $chunksize); + } else { + fseek($fd, $chunksize, SEEK_CUR); + } + break; + } + break; + + } + + } + + return $RIFFchunk; + } + + + function ParseRIFFdata(&$RIFFdata, &$ThisFileInfo) { + if ($RIFFdata) { + + $tempfile = tempnam('*', 'getID3'); + $fp_temp = fopen($tempfile, "wb"); + $RIFFdataLength = strlen($RIFFdata); + $NewLengthString = getid3_lib::LittleEndian2String($RIFFdataLength, 4); + for ($i = 0; $i < 4; $i++) { + $RIFFdata{$i + 4} = $NewLengthString{$i}; + } + fwrite($fp_temp, $RIFFdata); + fclose($fp_temp); + + $fp_temp = fopen($tempfile, "rb"); + $dummy = array('filesize'=>$RIFFdataLength, 'filenamepath'=>$ThisFileInfo['filenamepath'], 'tags'=>$ThisFileInfo['tags'], 'avdataoffset'=>0, 'avdataend'=>$RIFFdataLength, 'warning'=>$ThisFileInfo['warning'], 'error'=>$ThisFileInfo['error'], 'comments'=>$ThisFileInfo['comments'], 'audio'=>(isset($ThisFileInfo['audio']) ? $ThisFileInfo['audio'] : array()), 'video'=>(isset($ThisFileInfo['video']) ? $ThisFileInfo['video'] : array())); + $riff = new getid3_riff($fp_temp, $dummy); + $ThisFileInfo['riff'] = $dummy['riff']; + $ThisFileInfo['warning'] = $dummy['warning']; + $ThisFileInfo['error'] = $dummy['error']; + $ThisFileInfo['tags'] = $dummy['tags']; + $ThisFileInfo['comments'] = $dummy['comments']; + fclose($fp_temp); + unlink($tempfile); + return true; + } + return false; + } + + + function RIFFparseWAVEFORMATex($WaveFormatExData) { + // shortcut + $WaveFormatEx['raw'] = array(); + $WaveFormatEx_raw = &$WaveFormatEx['raw']; + + $WaveFormatEx_raw['wFormatTag'] = getid3_lib::LittleEndian2Int(substr($WaveFormatExData, 0, 2)); + $WaveFormatEx_raw['nChannels'] = getid3_lib::LittleEndian2Int(substr($WaveFormatExData, 2, 2)); + $WaveFormatEx_raw['nSamplesPerSec'] = getid3_lib::LittleEndian2Int(substr($WaveFormatExData, 4, 4)); + $WaveFormatEx_raw['nAvgBytesPerSec'] = getid3_lib::LittleEndian2Int(substr($WaveFormatExData, 8, 4)); + $WaveFormatEx_raw['nBlockAlign'] = getid3_lib::LittleEndian2Int(substr($WaveFormatExData, 12, 2)); + $WaveFormatEx_raw['wBitsPerSample'] = getid3_lib::LittleEndian2Int(substr($WaveFormatExData, 14, 2)); + if (strlen($WaveFormatExData) > 16) { + $WaveFormatEx_raw['cbSize'] = getid3_lib::LittleEndian2Int(substr($WaveFormatExData, 16, 2)); + } + + $WaveFormatEx['codec'] = getid3_riff::RIFFwFormatTagLookup($WaveFormatEx_raw['wFormatTag']); + $WaveFormatEx['channels'] = $WaveFormatEx_raw['nChannels']; + $WaveFormatEx['sample_rate'] = $WaveFormatEx_raw['nSamplesPerSec']; + $WaveFormatEx['bitrate'] = $WaveFormatEx_raw['nAvgBytesPerSec'] * 8; + $WaveFormatEx['bits_per_sample'] = $WaveFormatEx_raw['wBitsPerSample']; + + return $WaveFormatEx; + } + + + function RIFFparseWavPackHeader($WavPackChunkData, &$ThisFileInfo) { + // typedef struct { + // char ckID [4]; + // long ckSize; + // short version; + // short bits; // added for version 2.00 + // short flags, shift; // added for version 3.00 + // long total_samples, crc, crc2; + // char extension [4], extra_bc, extras [3]; + // } WavpackHeader; + + // shortcut + $ThisFileInfo['wavpack'] = array(); + $thisfile_wavpack = &$ThisFileInfo['wavpack']; + + $thisfile_wavpack['version'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 0, 2)); + if ($thisfile_wavpack['version'] >= 2) { + $thisfile_wavpack['bits'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 2, 2)); + } + if ($thisfile_wavpack['version'] >= 3) { + $thisfile_wavpack['flags_raw'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 4, 2)); + $thisfile_wavpack['shift'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 6, 2)); + $thisfile_wavpack['total_samples'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 8, 4)); + $thisfile_wavpack['crc1'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 12, 4)); + $thisfile_wavpack['crc2'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 16, 4)); + $thisfile_wavpack['extension'] = substr($WavPackChunkData, 20, 4); + $thisfile_wavpack['extra_bc'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 24, 1)); + for ($i = 0; $i <= 2; $i++) { + $thisfile_wavpack['extras'][] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 25 + $i, 1)); + } + + // shortcut + $thisfile_wavpack['flags'] = array(); + $thisfile_wavpack_flags = &$thisfile_wavpack['flags']; + + $thisfile_wavpack_flags['mono'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000001); + $thisfile_wavpack_flags['fast_mode'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000002); + $thisfile_wavpack_flags['raw_mode'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000004); + $thisfile_wavpack_flags['calc_noise'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000008); + $thisfile_wavpack_flags['high_quality'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000010); + $thisfile_wavpack_flags['3_byte_samples'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000020); + $thisfile_wavpack_flags['over_20_bits'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000040); + $thisfile_wavpack_flags['use_wvc'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000080); + $thisfile_wavpack_flags['noiseshaping'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000100); + $thisfile_wavpack_flags['very_fast_mode'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000200); + $thisfile_wavpack_flags['new_high_quality'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000400); + $thisfile_wavpack_flags['cancel_extreme'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000800); + $thisfile_wavpack_flags['cross_decorrelation'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x001000); + $thisfile_wavpack_flags['new_decorrelation'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x002000); + $thisfile_wavpack_flags['joint_stereo'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x004000); + $thisfile_wavpack_flags['extra_decorrelation'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x008000); + $thisfile_wavpack_flags['override_noiseshape'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x010000); + $thisfile_wavpack_flags['override_jointstereo'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x020000); + $thisfile_wavpack_flags['copy_source_filetime'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x040000); + $thisfile_wavpack_flags['create_exe'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x080000); + } + + return true; + } + + function RIFFwFormatTagLookup($wFormatTag) { + + $begin = __LINE__; + + /** This is not a comment! + + 0x0000 Microsoft Unknown Wave Format + 0x0001 Pulse Code Modulation (PCM) + 0x0002 Microsoft ADPCM + 0x0003 IEEE Float + 0x0004 Compaq Computer VSELP + 0x0005 IBM CVSD + 0x0006 Microsoft A-Law + 0x0007 Microsoft mu-Law + 0x0008 Microsoft DTS + 0x0010 OKI ADPCM + 0x0011 Intel DVI/IMA ADPCM + 0x0012 Videologic MediaSpace ADPCM + 0x0013 Sierra Semiconductor ADPCM + 0x0014 Antex Electronics G.723 ADPCM + 0x0015 DSP Solutions DigiSTD + 0x0016 DSP Solutions DigiFIX + 0x0017 Dialogic OKI ADPCM + 0x0018 MediaVision ADPCM + 0x0019 Hewlett-Packard CU + 0x0020 Yamaha ADPCM + 0x0021 Speech Compression Sonarc + 0x0022 DSP Group TrueSpeech + 0x0023 Echo Speech EchoSC1 + 0x0024 Audiofile AF36 + 0x0025 Audio Processing Technology APTX + 0x0026 AudioFile AF10 + 0x0027 Prosody 1612 + 0x0028 LRC + 0x0030 Dolby AC2 + 0x0031 Microsoft GSM 6.10 + 0x0032 MSNAudio + 0x0033 Antex Electronics ADPCME + 0x0034 Control Resources VQLPC + 0x0035 DSP Solutions DigiREAL + 0x0036 DSP Solutions DigiADPCM + 0x0037 Control Resources CR10 + 0x0038 Natural MicroSystems VBXADPCM + 0x0039 Crystal Semiconductor IMA ADPCM + 0x003A EchoSC3 + 0x003B Rockwell ADPCM + 0x003C Rockwell Digit LK + 0x003D Xebec + 0x0040 Antex Electronics G.721 ADPCM + 0x0041 G.728 CELP + 0x0042 MSG723 + 0x0050 MPEG Layer-2 or Layer-1 + 0x0052 RT24 + 0x0053 PAC + 0x0055 MPEG Layer-3 + 0x0059 Lucent G.723 + 0x0060 Cirrus + 0x0061 ESPCM + 0x0062 Voxware + 0x0063 Canopus Atrac + 0x0064 G.726 ADPCM + 0x0065 G.722 ADPCM + 0x0066 DSAT + 0x0067 DSAT Display + 0x0069 Voxware Byte Aligned + 0x0070 Voxware AC8 + 0x0071 Voxware AC10 + 0x0072 Voxware AC16 + 0x0073 Voxware AC20 + 0x0074 Voxware MetaVoice + 0x0075 Voxware MetaSound + 0x0076 Voxware RT29HW + 0x0077 Voxware VR12 + 0x0078 Voxware VR18 + 0x0079 Voxware TQ40 + 0x0080 Softsound + 0x0081 Voxware TQ60 + 0x0082 MSRT24 + 0x0083 G.729A + 0x0084 MVI MV12 + 0x0085 DF G.726 + 0x0086 DF GSM610 + 0x0088 ISIAudio + 0x0089 Onlive + 0x0091 SBC24 + 0x0092 Dolby AC3 SPDIF + 0x0093 MediaSonic G.723 + 0x0094 Aculab PLC Prosody 8kbps + 0x0097 ZyXEL ADPCM + 0x0098 Philips LPCBB + 0x0099 Packed + 0x00FF AAC + 0x0100 Rhetorex ADPCM + 0x0101 IBM mu-law + 0x0102 IBM A-law + 0x0103 IBM AVC Adaptive Differential Pulse Code Modulation (ADPCM) + 0x0111 Vivo G.723 + 0x0112 Vivo Siren + 0x0123 Digital G.723 + 0x0125 Sanyo LD ADPCM + 0x0130 Sipro Lab Telecom ACELP NET + 0x0131 Sipro Lab Telecom ACELP 4800 + 0x0132 Sipro Lab Telecom ACELP 8V3 + 0x0133 Sipro Lab Telecom G.729 + 0x0134 Sipro Lab Telecom G.729A + 0x0135 Sipro Lab Telecom Kelvin + 0x0140 Windows Media Video V8 + 0x0150 Qualcomm PureVoice + 0x0151 Qualcomm HalfRate + 0x0155 Ring Zero Systems TUB GSM + 0x0160 Microsoft Audio 1 + 0x0161 Windows Media Audio V7 / V8 / V9 + 0x0162 Windows Media Audio Professional V9 + 0x0163 Windows Media Audio Lossless V9 + 0x0200 Creative Labs ADPCM + 0x0202 Creative Labs Fastspeech8 + 0x0203 Creative Labs Fastspeech10 + 0x0210 UHER Informatic GmbH ADPCM + 0x0220 Quarterdeck + 0x0230 I-link Worldwide VC + 0x0240 Aureal RAW Sport + 0x0250 Interactive Products HSX + 0x0251 Interactive Products RPELP + 0x0260 Consistent Software CS2 + 0x0270 Sony SCX + 0x0300 Fujitsu FM Towns Snd + 0x0400 BTV Digital + 0x0401 Intel Music Coder + 0x0450 QDesign Music + 0x0680 VME VMPCM + 0x0681 AT&T Labs TPC + 0x08AE ClearJump LiteWave + 0x1000 Olivetti GSM + 0x1001 Olivetti ADPCM + 0x1002 Olivetti CELP + 0x1003 Olivetti SBC + 0x1004 Olivetti OPR + 0x1100 Lernout & Hauspie Codec (0x1100) + 0x1101 Lernout & Hauspie CELP Codec (0x1101) + 0x1102 Lernout & Hauspie SBC Codec (0x1102) + 0x1103 Lernout & Hauspie SBC Codec (0x1103) + 0x1104 Lernout & Hauspie SBC Codec (0x1104) + 0x1400 Norris + 0x1401 AT&T ISIAudio + 0x1500 Soundspace Music Compression + 0x181C VoxWare RT24 Speech + 0x1FC4 NCT Soft ALF2CD (www.nctsoft.com) + 0x2000 Dolby AC3 + 0x2001 Dolby DTS + 0x2002 WAVE_FORMAT_14_4 + 0x2003 WAVE_FORMAT_28_8 + 0x2004 WAVE_FORMAT_COOK + 0x2005 WAVE_FORMAT_DNET + 0x674F Ogg Vorbis 1 + 0x6750 Ogg Vorbis 2 + 0x6751 Ogg Vorbis 3 + 0x676F Ogg Vorbis 1+ + 0x6770 Ogg Vorbis 2+ + 0x6771 Ogg Vorbis 3+ + 0x7A21 GSM-AMR (CBR, no SID) + 0x7A22 GSM-AMR (VBR, including SID) + 0xFFFE WAVE_FORMAT_EXTENSIBLE + 0xFFFF WAVE_FORMAT_DEVELOPMENT + + */ + + return getid3_lib::EmbeddedLookup('0x'.str_pad(strtoupper(dechex($wFormatTag)), 4, '0', STR_PAD_LEFT), $begin, __LINE__, __FILE__, 'riff-wFormatTag'); + + } + + + function RIFFfourccLookup($fourcc) { + + $begin = __LINE__; + + /** This is not a comment! + + swot http://developer.apple.com/qa/snd/snd07.html + ____ No Codec (____) + _BIT BI_BITFIELDS (Raw RGB) + _JPG JPEG compressed + _PNG PNG compressed W3C/ISO/IEC (RFC-2083) + _RAW Full Frames (Uncompressed) + _RGB Raw RGB Bitmap + _RL4 RLE 4bpp RGB + _RL8 RLE 8bpp RGB + 3IV1 3ivx MPEG-4 v1 + 3IV2 3ivx MPEG-4 v2 + 3IVX 3ivx MPEG-4 + AASC Autodesk Animator + ABYR Kensington ?ABYR? + AEMI Array Microsystems VideoONE MPEG1-I Capture + AFLC Autodesk Animator FLC + AFLI Autodesk Animator FLI + AMPG Array Microsystems VideoONE MPEG + ANIM Intel RDX (ANIM) + AP41 AngelPotion Definitive + ASV1 Asus Video v1 + ASV2 Asus Video v2 + ASVX Asus Video 2.0 (audio) + AUR2 AuraVision Aura 2 Codec - YUV 4:2:2 + AURA AuraVision Aura 1 Codec - YUV 4:1:1 + AVDJ Independent JPEG Group\'s codec (AVDJ) + AVRN Independent JPEG Group\'s codec (AVRN) + AYUV 4:4:4 YUV (AYUV) + AZPR Quicktime Apple Video (AZPR) + BGR Raw RGB32 + BLZ0 Blizzard DivX MPEG-4 + BTVC Conexant Composite Video + BINK RAD Game Tools Bink Video + BT20 Conexant Prosumer Video + BTCV Conexant Composite Video Codec + BW10 Data Translation Broadway MPEG Capture + CC12 Intel YUV12 + CDVC Canopus DV + CFCC Digital Processing Systems DPS Perception + CGDI Microsoft Office 97 Camcorder Video + CHAM Winnov Caviara Champagne + CJPG Creative WebCam JPEG + CLJR Cirrus Logic YUV 4:1:1 + CMYK Common Data Format in Printing (Colorgraph) + CPLA Weitek 4:2:0 YUV Planar + CRAM Microsoft Video 1 (CRAM) + cvid Radius Cinepak + CVID Radius Cinepak + CWLT Microsoft Color WLT DIB + CYUV Creative Labs YUV + CYUY ATI YUV + D261 H.261 + D263 H.263 + DIB Device Independent Bitmap + DIV1 FFmpeg OpenDivX + DIV2 Microsoft MPEG-4 v1/v2 + DIV3 DivX ;-) MPEG-4 v3.x Low-Motion + DIV4 DivX ;-) MPEG-4 v3.x Fast-Motion + DIV5 DivX MPEG-4 v5.x + DIV6 DivX ;-) (MS MPEG-4 v3.x) + DIVX DivX MPEG-4 v4 (OpenDivX / Project Mayo) + divx DivX MPEG-4 + DMB1 Matrox Rainbow Runner hardware MJPEG + DMB2 Paradigm MJPEG + DSVD ?DSVD? + DUCK Duck TrueMotion 1.0 + DPS0 DPS/Leitch Reality Motion JPEG + DPSC DPS/Leitch PAR Motion JPEG + DV25 Matrox DVCPRO codec + DV50 Matrox DVCPRO50 codec + DVC IEC 61834 and SMPTE 314M (DVC/DV Video) + DVCP IEC 61834 and SMPTE 314M (DVC/DV Video) + DVHD IEC Standard DV 1125 lines @ 30fps / 1250 lines @ 25fps + DVMA Darim Vision DVMPEG (dummy for MPEG compressor) (www.darvision.com) + DVSL IEC Standard DV compressed in SD (SDL) + DVAN ?DVAN? + DVE2 InSoft DVE-2 Videoconferencing + dvsd IEC 61834 and SMPTE 314M DVC/DV Video + DVSD IEC 61834 and SMPTE 314M DVC/DV Video + DVX1 Lucent DVX1000SP Video Decoder + DVX2 Lucent DVX2000S Video Decoder + DVX3 Lucent DVX3000S Video Decoder + DX50 DivX v5 + DXT1 Microsoft DirectX Compressed Texture (DXT1) + DXT2 Microsoft DirectX Compressed Texture (DXT2) + DXT3 Microsoft DirectX Compressed Texture (DXT3) + DXT4 Microsoft DirectX Compressed Texture (DXT4) + DXT5 Microsoft DirectX Compressed Texture (DXT5) + DXTC Microsoft DirectX Compressed Texture (DXTC) + DXTn Microsoft DirectX Compressed Texture (DXTn) + EM2V Etymonix MPEG-2 I-frame (www.etymonix.com) + EKQ0 Elsa ?EKQ0? + ELK0 Elsa ?ELK0? + ESCP Eidos Escape + ETV1 eTreppid Video ETV1 + ETV2 eTreppid Video ETV2 + ETVC eTreppid Video ETVC + FLIC Autodesk FLI/FLC Animation + FRWT Darim Vision Forward Motion JPEG (www.darvision.com) + FRWU Darim Vision Forward Uncompressed (www.darvision.com) + FLJP D-Vision Field Encoded Motion JPEG + FRWA SoftLab-Nsk Forward Motion JPEG w/ alpha channel + FRWD SoftLab-Nsk Forward Motion JPEG + FVF1 Iterated Systems Fractal Video Frame + GLZW Motion LZW (gabest@freemail.hu) + GPEG Motion JPEG (gabest@freemail.hu) + GWLT Microsoft Greyscale WLT DIB + H260 Intel ITU H.260 Videoconferencing + H261 Intel ITU H.261 Videoconferencing + H262 Intel ITU H.262 Videoconferencing + H263 Intel ITU H.263 Videoconferencing + H264 Intel ITU H.264 Videoconferencing + H265 Intel ITU H.265 Videoconferencing + H266 Intel ITU H.266 Videoconferencing + H267 Intel ITU H.267 Videoconferencing + H268 Intel ITU H.268 Videoconferencing + H269 Intel ITU H.269 Videoconferencing + HFYU Huffman Lossless Codec + HMCR Rendition Motion Compensation Format (HMCR) + HMRR Rendition Motion Compensation Format (HMRR) + I263 FFmpeg I263 decoder + IF09 Indeo YVU9 ("YVU9 with additional delta-frame info after the U plane") + IUYV Interlaced version of UYVY (www.leadtools.com) + IY41 Interlaced version of Y41P (www.leadtools.com) + IYU1 12 bit format used in mode 2 of the IEEE 1394 Digital Camera 1.04 spec IEEE standard + IYU2 24 bit format used in mode 2 of the IEEE 1394 Digital Camera 1.04 spec IEEE standard + IYUV Planar YUV format (8-bpp Y plane, followed by 8-bpp 2×2 U and V planes) + i263 Intel ITU H.263 Videoconferencing (i263) + I420 Intel Indeo 4 + IAN Intel Indeo 4 (RDX) + ICLB InSoft CellB Videoconferencing + IGOR Power DVD + IJPG Intergraph JPEG + ILVC Intel Layered Video + ILVR ITU-T H.263+ + IPDV I-O Data Device Giga AVI DV Codec + IR21 Intel Indeo 2.1 + IRAW Intel YUV Uncompressed + IV30 Intel Indeo 3.0 + IV31 Intel Indeo 3.1 + IV32 Ligos Indeo 3.2 + IV33 Ligos Indeo 3.3 + IV34 Ligos Indeo 3.4 + IV35 Ligos Indeo 3.5 + IV36 Ligos Indeo 3.6 + IV37 Ligos Indeo 3.7 + IV38 Ligos Indeo 3.8 + IV39 Ligos Indeo 3.9 + IV40 Ligos Indeo Interactive 4.0 + IV41 Ligos Indeo Interactive 4.1 + IV42 Ligos Indeo Interactive 4.2 + IV43 Ligos Indeo Interactive 4.3 + IV44 Ligos Indeo Interactive 4.4 + IV45 Ligos Indeo Interactive 4.5 + IV46 Ligos Indeo Interactive 4.6 + IV47 Ligos Indeo Interactive 4.7 + IV48 Ligos Indeo Interactive 4.8 + IV49 Ligos Indeo Interactive 4.9 + IV50 Ligos Indeo Interactive 5.0 + JBYR Kensington ?JBYR? + JPEG Still Image JPEG DIB + JPGL Pegasus Lossless Motion JPEG + KMVC Team17 Software Karl Morton\'s Video Codec + LSVM Vianet Lighting Strike Vmail (Streaming) (www.vianet.com) + LEAD LEAD Video Codec + Ljpg LEAD MJPEG Codec + MDVD Alex MicroDVD Video (hacked MS MPEG-4) (www.tiasoft.de) + MJPA Morgan Motion JPEG (MJPA) (www.morgan-multimedia.com) + MJPB Morgan Motion JPEG (MJPB) (www.morgan-multimedia.com) + MMES Matrox MPEG-2 I-frame + MP2v Microsoft S-Mpeg 4 version 1 (MP2v) + MP42 Microsoft S-Mpeg 4 version 2 (MP42) + MP43 Microsoft S-Mpeg 4 version 3 (MP43) + MP4S Microsoft S-Mpeg 4 version 3 (MP4S) + MP4V FFmpeg MPEG-4 + MPG1 FFmpeg MPEG 1/2 + MPG2 FFmpeg MPEG 1/2 + MPG3 FFmpeg DivX ;-) (MS MPEG-4 v3) + MPG4 Microsoft MPEG-4 + MPGI Sigma Designs MPEG + MPNG PNG images decoder + MSS1 Microsoft Windows Screen Video + MSZH LCL (Lossless Codec Library) (www.geocities.co.jp/Playtown-Denei/2837/LRC.htm) + M261 Microsoft H.261 + M263 Microsoft H.263 + M4S2 Microsoft Fully Compliant MPEG-4 v2 simple profile (M4S2) + m4s2 Microsoft Fully Compliant MPEG-4 v2 simple profile (m4s2) + MC12 ATI Motion Compensation Format (MC12) + MCAM ATI Motion Compensation Format (MCAM) + MJ2C Morgan Multimedia Motion JPEG2000 + mJPG IBM Motion JPEG w/ Huffman Tables + MJPG Microsoft Motion JPEG DIB + MP42 Microsoft MPEG-4 (low-motion) + MP43 Microsoft MPEG-4 (fast-motion) + MP4S Microsoft MPEG-4 (MP4S) + mp4s Microsoft MPEG-4 (mp4s) + MPEG Chromatic Research MPEG-1 Video I-Frame + MPG4 Microsoft MPEG-4 Video High Speed Compressor + MPGI Sigma Designs MPEG + MRCA FAST Multimedia Martin Regen Codec + MRLE Microsoft Run Length Encoding + MSVC Microsoft Video 1 + MTX1 Matrox ?MTX1? + MTX2 Matrox ?MTX2? + MTX3 Matrox ?MTX3? + MTX4 Matrox ?MTX4? + MTX5 Matrox ?MTX5? + MTX6 Matrox ?MTX6? + MTX7 Matrox ?MTX7? + MTX8 Matrox ?MTX8? + MTX9 Matrox ?MTX9? + MV12 Motion Pixels Codec (old) + MWV1 Aware Motion Wavelets + nAVI SMR Codec (hack of Microsoft MPEG-4) (IRC #shadowrealm) + NT00 NewTek LightWave HDTV YUV w/ Alpha (www.newtek.com) + NUV1 NuppelVideo + NTN1 Nogatech Video Compression 1 + NVS0 nVidia GeForce Texture (NVS0) + NVS1 nVidia GeForce Texture (NVS1) + NVS2 nVidia GeForce Texture (NVS2) + NVS3 nVidia GeForce Texture (NVS3) + NVS4 nVidia GeForce Texture (NVS4) + NVS5 nVidia GeForce Texture (NVS5) + NVT0 nVidia GeForce Texture (NVT0) + NVT1 nVidia GeForce Texture (NVT1) + NVT2 nVidia GeForce Texture (NVT2) + NVT3 nVidia GeForce Texture (NVT3) + NVT4 nVidia GeForce Texture (NVT4) + NVT5 nVidia GeForce Texture (NVT5) + PIXL MiroXL, Pinnacle PCTV + PDVC I-O Data Device Digital Video Capture DV codec + PGVV Radius Video Vision + PHMO IBM Photomotion + PIM1 MPEG Realtime (Pinnacle Cards) + PIM2 Pegasus Imaging ?PIM2? + PIMJ Pegasus Imaging Lossless JPEG + PVEZ Horizons Technology PowerEZ + PVMM PacketVideo Corporation MPEG-4 + PVW2 Pegasus Imaging Wavelet Compression + Q1.0 Q-Team\'s QPEG 1.0 (www.q-team.de) + Q1.1 Q-Team\'s QPEG 1.1 (www.q-team.de) + QPEG Q-Team QPEG 1.0 + qpeq Q-Team QPEG 1.1 + RGB Raw BGR32 + RGBA Raw RGB w/ Alpha + RMP4 REALmagic MPEG-4 (unauthorized XVID copy) (www.sigmadesigns.com) + ROQV Id RoQ File Video Decoder + RPZA Quicktime Apple Video (RPZA) + RUD0 Rududu video codec (http://rududu.ifrance.com/rududu/) + RV10 RealVideo 1.0 (aka RealVideo 5.0) + RV13 RealVideo 1.0 (RV13) + RV20 RealVideo G2 + RV30 RealVideo 8 + RV40 RealVideo 9 + RGBT Raw RGB w/ Transparency + RLE Microsoft Run Length Encoder + RLE4 Run Length Encoded (4bpp, 16-color) + RLE8 Run Length Encoded (8bpp, 256-color) + RT21 Intel Indeo RealTime Video 2.1 + rv20 RealVideo G2 + rv30 RealVideo 8 + RVX Intel RDX (RVX ) + SMC Apple Graphics (SMC ) + SP54 Logitech Sunplus Sp54 Codec for Mustek GSmart Mini 2 + SPIG Radius Spigot + SVQ3 Sorenson Video 3 (Apple Quicktime 5) + s422 Tekram VideoCap C210 YUV 4:2:2 + SDCC Sun Communication Digital Camera Codec + SFMC CrystalNet Surface Fitting Method + SMSC Radius SMSC + SMSD Radius SMSD + smsv WorldConnect Wavelet Video + SPIG Radius Spigot + SPLC Splash Studios ACM Audio Codec (www.splashstudios.net) + SQZ2 Microsoft VXTreme Video Codec V2 + STVA ST Microelectronics CMOS Imager Data (Bayer) + STVB ST Microelectronics CMOS Imager Data (Nudged Bayer) + STVC ST Microelectronics CMOS Imager Data (Bunched) + STVX ST Microelectronics CMOS Imager Data (Extended CODEC Data Format) + STVY ST Microelectronics CMOS Imager Data (Extended CODEC Data Format with Correction Data) + SV10 Sorenson Video R1 + SVQ1 Sorenson Video + T420 Toshiba YUV 4:2:0 + TM2A Duck TrueMotion Archiver 2.0 (www.duck.com) + TVJP Pinnacle/Truevision Targa 2000 board (TVJP) + TVMJ Pinnacle/Truevision Targa 2000 board (TVMJ) + TY0N Tecomac Low-Bit Rate Codec (www.tecomac.com) + TY2C Trident Decompression Driver + TLMS TeraLogic Motion Intraframe Codec (TLMS) + TLST TeraLogic Motion Intraframe Codec (TLST) + TM20 Duck TrueMotion 2.0 + TM2X Duck TrueMotion 2X + TMIC TeraLogic Motion Intraframe Codec (TMIC) + TMOT Horizons Technology TrueMotion S + tmot Horizons TrueMotion Video Compression + TR20 Duck TrueMotion RealTime 2.0 + TSCC TechSmith Screen Capture Codec + TV10 Tecomac Low-Bit Rate Codec + TY2N Trident ?TY2N? + U263 UB Video H.263/H.263+/H.263++ Decoder + UMP4 UB Video MPEG 4 (www.ubvideo.com) + UYNV Nvidia UYVY packed 4:2:2 + UYVP Evans & Sutherland YCbCr 4:2:2 extended precision + UCOD eMajix.com ClearVideo + ULTI IBM Ultimotion + UYVY UYVY packed 4:2:2 + V261 Lucent VX2000S + VIFP VFAPI Reader Codec (www.yks.ne.jp/~hori/) + VIV1 FFmpeg H263+ decoder + VIV2 Vivo H.263 + VQC2 Vector-quantised codec 2 (research) http://eprints.ecs.soton.ac.uk/archive/00001310/01/VTC97-js.pdf) + VTLP Alaris VideoGramPiX + VYU9 ATI YUV (VYU9) + VYUY ATI YUV (VYUY) + V261 Lucent VX2000S + V422 Vitec Multimedia 24-bit YUV 4:2:2 Format + V655 Vitec Multimedia 16-bit YUV 4:2:2 Format + VCR1 ATI Video Codec 1 + VCR2 ATI Video Codec 2 + VCR3 ATI VCR 3.0 + VCR4 ATI VCR 4.0 + VCR5 ATI VCR 5.0 + VCR6 ATI VCR 6.0 + VCR7 ATI VCR 7.0 + VCR8 ATI VCR 8.0 + VCR9 ATI VCR 9.0 + VDCT Vitec Multimedia Video Maker Pro DIB + VDOM VDOnet VDOWave + VDOW VDOnet VDOLive (H.263) + VDTZ Darim Vison VideoTizer YUV + VGPX Alaris VideoGramPiX + VIDS Vitec Multimedia YUV 4:2:2 CCIR 601 for V422 + VIVO Vivo H.263 v2.00 + vivo Vivo H.263 + VIXL Miro/Pinnacle Video XL + VLV1 VideoLogic/PURE Digital Videologic Capture + VP30 On2 VP3.0 + VP31 On2 VP3.1 + VX1K Lucent VX1000S Video Codec + VX2K Lucent VX2000S Video Codec + VXSP Lucent VX1000SP Video Codec + WBVC Winbond W9960 + WHAM Microsoft Video 1 (WHAM) + WINX Winnov Software Compression + WJPG AverMedia Winbond JPEG + WMV1 Windows Media Video V7 + WMV2 Windows Media Video V8 + WMV3 Windows Media Video V9 + WNV1 Winnov Hardware Compression + XYZP Extended PAL format XYZ palette (www.riff.org) + x263 Xirlink H.263 + XLV0 NetXL Video Decoder + XMPG Xing MPEG (I-Frame only) + XVID XviD MPEG-4 (www.xvid.org) + XXAN ?XXAN? + YU92 Intel YUV (YU92) + YUNV Nvidia Uncompressed YUV 4:2:2 + YUVP Extended PAL format YUV palette (www.riff.org) + Y211 YUV 2:1:1 Packed + Y411 YUV 4:1:1 Packed + Y41B Weitek YUV 4:1:1 Planar + Y41P Brooktree PC1 YUV 4:1:1 Packed + Y41T Brooktree PC1 YUV 4:1:1 with transparency + Y42B Weitek YUV 4:2:2 Planar + Y42T Brooktree UYUV 4:2:2 with transparency + Y422 ADS Technologies Copy of UYVY used in Pyro WebCam firewire camera + Y800 Simple, single Y plane for monochrome images + Y8 Grayscale video + YC12 Intel YUV 12 codec + YUV8 Winnov Caviar YUV8 + YUV9 Intel YUV9 + YUY2 Uncompressed YUV 4:2:2 + YUYV Canopus YUV + YV12 YVU12 Planar + YVU9 Intel YVU9 Planar (8-bpp Y plane, followed by 8-bpp 4x4 U and V planes) + YVYU YVYU 4:2:2 Packed + ZLIB Lossless Codec Library zlib compression (www.geocities.co.jp/Playtown-Denei/2837/LRC.htm) + ZPEG Metheus Video Zipper + + */ + + return getid3_lib::EmbeddedLookup($fourcc, $begin, __LINE__, __FILE__, 'riff-fourcc'); + } + + + function EitherEndian2Int(&$ThisFileInfo, $byteword, $signed=false) { + if ($ThisFileInfo['fileformat'] == 'riff') { + return getid3_lib::LittleEndian2Int($byteword, $signed); + } + return getid3_lib::BigEndian2Int($byteword, false, $signed); + } + +} + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.audio-video.swf.php b/includes/getid3/getid3/module.audio-video.swf.php new file mode 100644 index 0000000..a03806e --- /dev/null +++ b/includes/getid3/getid3/module.audio-video.swf.php @@ -0,0 +1,153 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio-video.swf.php // +// module for analyzing Shockwave Flash files // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_swf +{ + + function getid3_swf(&$fd, &$ThisFileInfo, $ReturnAllTagData=false) { + $ThisFileInfo['fileformat'] = 'swf'; + $ThisFileInfo['video']['dataformat'] = 'swf'; + + // http://www.openswf.org/spec/SWFfileformat.html + + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + +//echo 'reading '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']).' bytes
'; + $SWFfileData = fread($fd, $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']); // 8 + 2 + 2 + max(9) bytes NOT including Frame_Size RECT data + + $ThisFileInfo['swf']['header']['signature'] = substr($SWFfileData, 0, 3); + switch ($ThisFileInfo['swf']['header']['signature']) { + case 'FWS': + $ThisFileInfo['swf']['header']['compressed'] = false; + break; + + case 'CWS': + $ThisFileInfo['swf']['header']['compressed'] = true; + break; + + default: + $ThisFileInfo['error'][] = 'Expecting "FWS" or "CWS" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$ThisFileInfo['swf']['header']['signature'].'"'; + unset($ThisFileInfo['swf']); + unset($ThisFileInfo['fileformat']); + return false; + break; + } + $ThisFileInfo['swf']['header']['version'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 3, 1)); + $ThisFileInfo['swf']['header']['length'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 4, 4)); + +//echo '1
'; + if ($ThisFileInfo['swf']['header']['compressed']) { + +//echo '2
'; +// $foo = substr($SWFfileData, 8, 4096); +// echo '['.strlen($foo).']
'; +// $fee = gzuncompress($foo); +// echo '('.strlen($fee).')
'; +//return false; +//echo '
time: '.time().'
'; +//return false; + if ($UncompressedFileData = gzuncompress(substr($SWFfileData, 8))) { + +//echo '3
'; + $SWFfileData = substr($SWFfileData, 0, 8).$UncompressedFileData; + + } else { + +//echo '4
'; + $ThisFileInfo['error'][] = 'Error decompressing compressed SWF data'; + return false; + + } + + } + + $FrameSizeBitsPerValue = (ord(substr($SWFfileData, 8, 1)) & 0xF8) >> 3; + $FrameSizeDataLength = ceil((5 + (4 * $FrameSizeBitsPerValue)) / 8); + $FrameSizeDataString = str_pad(decbin(ord(substr($SWFfileData, 8, 1)) & 0x07), 3, '0', STR_PAD_LEFT); + for ($i = 1; $i < $FrameSizeDataLength; $i++) { + $FrameSizeDataString .= str_pad(decbin(ord(substr($SWFfileData, 8 + $i, 1))), 8, '0', STR_PAD_LEFT); + } + list($X1, $X2, $Y1, $Y2) = explode("\n", wordwrap($FrameSizeDataString, $FrameSizeBitsPerValue, "\n", 1)); + $ThisFileInfo['swf']['header']['frame_width'] = getid3_lib::Bin2Dec($X2); + $ThisFileInfo['swf']['header']['frame_height'] = getid3_lib::Bin2Dec($Y2); + + // http://www-lehre.informatik.uni-osnabrueck.de/~fbstark/diplom/docs/swf/Flash_Uncovered.htm + // Next in the header is the frame rate, which is kind of weird. + // It is supposed to be stored as a 16bit integer, but the first byte + // (or last depending on how you look at it) is completely ignored. + // Example: 0x000C -> 0x0C -> 12 So the frame rate is 12 fps. + + // Byte at (8 + $FrameSizeDataLength) is always zero and ignored + $ThisFileInfo['swf']['header']['frame_rate'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 9 + $FrameSizeDataLength, 1)); + $ThisFileInfo['swf']['header']['frame_count'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 10 + $FrameSizeDataLength, 2)); + + $ThisFileInfo['video']['frame_rate'] = $ThisFileInfo['swf']['header']['frame_rate']; + $ThisFileInfo['video']['resolution_x'] = intval(round($ThisFileInfo['swf']['header']['frame_width'] / 20)); + $ThisFileInfo['video']['resolution_y'] = intval(round($ThisFileInfo['swf']['header']['frame_height'] / 20)); + $ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1; + + if (($ThisFileInfo['swf']['header']['frame_count'] > 0) && ($ThisFileInfo['swf']['header']['frame_rate'] > 0)) { + $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['swf']['header']['frame_count'] / $ThisFileInfo['swf']['header']['frame_rate']; + } + + + // SWF tags + + $CurrentOffset = 12 + $FrameSizeDataLength; + $SWFdataLength = strlen($SWFfileData); + + while ($CurrentOffset < $SWFdataLength) { + + $TagIDTagLength = getid3_lib::LittleEndian2Int(substr($SWFfileData, $CurrentOffset, 2)); + $TagID = ($TagIDTagLength & 0xFFFC) >> 6; + $TagLength = ($TagIDTagLength & 0x003F); + $CurrentOffset += 2; + if ($TagLength == 0x3F) { + $TagLength = getid3_lib::LittleEndian2Int(substr($SWFfileData, $CurrentOffset, 4)); + $CurrentOffset += 4; + } + + unset($TagData); + $TagData['offset'] = $CurrentOffset; + $TagData['size'] = $TagLength; + $TagData['id'] = $TagID; + $TagData['data'] = substr($SWFfileData, $CurrentOffset, $TagLength); + switch ($TagID) { + case 0: // end of movie + break 2; + + case 9: // Set background color + //$ThisFileInfo['swf']['tags'][] = $TagData; + $ThisFileInfo['swf']['bgcolor'] = strtoupper(str_pad(dechex(getid3_lib::BigEndian2Int($TagData['data'])), 6, '0', STR_PAD_LEFT)); + break; + + default: + if ($ReturnAllTagData) { + $ThisFileInfo['swf']['tags'][] = $TagData; + } + break; + } + + $CurrentOffset += $TagLength; + } + + return true; + } + +} + + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.audio.aac.php b/includes/getid3/getid3/module.audio.aac.php new file mode 100644 index 0000000..4d35fb2 --- /dev/null +++ b/includes/getid3/getid3/module.audio.aac.php @@ -0,0 +1,538 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio.aac.php // +// module for analyzing AAC Audio files // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_aac +{ + + // new combined constructor + function getid3_aac(&$fd, &$ThisFileInfo, $option) { + + if ($option === 'adif') { + $this->getAACADIFheaderFilepointer($fd, $ThisFileInfo); + } + elseif ($option === 'adts') { + $this->getAACADTSheaderFilepointer($fd, $ThisFileInfo); + } + } + + + + function getAACADIFheaderFilepointer(&$fd, &$ThisFileInfo) { + $ThisFileInfo['fileformat'] = 'aac'; + $ThisFileInfo['audio']['dataformat'] = 'aac'; + $ThisFileInfo['audio']['lossless'] = false; + + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + $AACheader = fread($fd, 1024); + $offset = 0; + + if (substr($AACheader, 0, 4) == 'ADIF') { + + // http://faac.sourceforge.net/wiki/index.php?page=ADIF + + // http://libmpeg.org/mpeg4/doc/w2203tfs.pdf + // adif_header() { + // adif_id 32 + // copyright_id_present 1 + // if( copyright_id_present ) + // copyright_id 72 + // original_copy 1 + // home 1 + // bitstream_type 1 + // bitrate 23 + // num_program_config_elements 4 + // for (i = 0; i < num_program_config_elements + 1; i++ ) { + // if( bitstream_type == '0' ) + // adif_buffer_fullness 20 + // program_config_element() + // } + // } + + $AACheaderBitstream = getid3_lib::BigEndian2Bin($AACheader); + $bitoffset = 0; + + $ThisFileInfo['aac']['header_type'] = 'ADIF'; + $bitoffset += 32; + $ThisFileInfo['aac']['header']['mpeg_version'] = 4; + + $ThisFileInfo['aac']['header']['copyright'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1'); + $bitoffset += 1; + if ($ThisFileInfo['aac']['header']['copyright']) { + $ThisFileInfo['aac']['header']['copyright_id'] = getid3_lib::Bin2String(substr($AACheaderBitstream, $bitoffset, 72)); + $bitoffset += 72; + } + $ThisFileInfo['aac']['header']['original_copy'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1'); + $bitoffset += 1; + $ThisFileInfo['aac']['header']['home'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1'); + $bitoffset += 1; + $ThisFileInfo['aac']['header']['is_vbr'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1'); + $bitoffset += 1; + if ($ThisFileInfo['aac']['header']['is_vbr']) { + $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; + $ThisFileInfo['aac']['header']['bitrate_max'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 23)); + $bitoffset += 23; + } else { + $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; + $ThisFileInfo['aac']['header']['bitrate'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 23)); + $bitoffset += 23; + $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['aac']['header']['bitrate']; + } + if ($ThisFileInfo['audio']['bitrate'] == 0) { + $ThisFileInfo['error'][] = 'Corrupt AAC file: bitrate_audio == zero'; + return false; + } + $ThisFileInfo['aac']['header']['num_program_configs'] = 1 + getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); + $bitoffset += 4; + + for ($i = 0; $i < $ThisFileInfo['aac']['header']['num_program_configs']; $i++) { + // http://www.audiocoding.com/wiki/index.php?page=program_config_element + + // buffer_fullness 20 + + // element_instance_tag 4 + // object_type 2 + // sampling_frequency_index 4 + // num_front_channel_elements 4 + // num_side_channel_elements 4 + // num_back_channel_elements 4 + // num_lfe_channel_elements 2 + // num_assoc_data_elements 3 + // num_valid_cc_elements 4 + // mono_mixdown_present 1 + // mono_mixdown_element_number 4 if mono_mixdown_present == 1 + // stereo_mixdown_present 1 + // stereo_mixdown_element_number 4 if stereo_mixdown_present == 1 + // matrix_mixdown_idx_present 1 + // matrix_mixdown_idx 2 if matrix_mixdown_idx_present == 1 + // pseudo_surround_enable 1 if matrix_mixdown_idx_present == 1 + // for (i = 0; i < num_front_channel_elements; i++) { + // front_element_is_cpe[i] 1 + // front_element_tag_select[i] 4 + // } + // for (i = 0; i < num_side_channel_elements; i++) { + // side_element_is_cpe[i] 1 + // side_element_tag_select[i] 4 + // } + // for (i = 0; i < num_back_channel_elements; i++) { + // back_element_is_cpe[i] 1 + // back_element_tag_select[i] 4 + // } + // for (i = 0; i < num_lfe_channel_elements; i++) { + // lfe_element_tag_select[i] 4 + // } + // for (i = 0; i < num_assoc_data_elements; i++) { + // assoc_data_element_tag_select[i] 4 + // } + // for (i = 0; i < num_valid_cc_elements; i++) { + // cc_element_is_ind_sw[i] 1 + // valid_cc_element_tag_select[i] 4 + // } + // byte_alignment() VAR + // comment_field_bytes 8 + // for (i = 0; i < comment_field_bytes; i++) { + // comment_field_data[i] 8 + // } + + if (!$ThisFileInfo['aac']['header']['is_vbr']) { + $ThisFileInfo['aac']['program_configs'][$i]['buffer_fullness'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 20)); + $bitoffset += 20; + } + $ThisFileInfo['aac']['program_configs'][$i]['element_instance_tag'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); + $bitoffset += 4; + $ThisFileInfo['aac']['program_configs'][$i]['object_type'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2)); + $bitoffset += 2; + $ThisFileInfo['aac']['program_configs'][$i]['sampling_frequency_index'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); + $bitoffset += 4; + $ThisFileInfo['aac']['program_configs'][$i]['num_front_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); + $bitoffset += 4; + $ThisFileInfo['aac']['program_configs'][$i]['num_side_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); + $bitoffset += 4; + $ThisFileInfo['aac']['program_configs'][$i]['num_back_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); + $bitoffset += 4; + $ThisFileInfo['aac']['program_configs'][$i]['num_lfe_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2)); + $bitoffset += 2; + $ThisFileInfo['aac']['program_configs'][$i]['num_assoc_data_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 3)); + $bitoffset += 3; + $ThisFileInfo['aac']['program_configs'][$i]['num_valid_cc_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); + $bitoffset += 4; + $ThisFileInfo['aac']['program_configs'][$i]['mono_mixdown_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); + $bitoffset += 1; + if ($ThisFileInfo['aac']['program_configs'][$i]['mono_mixdown_present']) { + $ThisFileInfo['aac']['program_configs'][$i]['mono_mixdown_element_number'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); + $bitoffset += 4; + } + $ThisFileInfo['aac']['program_configs'][$i]['stereo_mixdown_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); + $bitoffset += 1; + if ($ThisFileInfo['aac']['program_configs'][$i]['stereo_mixdown_present']) { + $ThisFileInfo['aac']['program_configs'][$i]['stereo_mixdown_element_number'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); + $bitoffset += 4; + } + $ThisFileInfo['aac']['program_configs'][$i]['matrix_mixdown_idx_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); + $bitoffset += 1; + if ($ThisFileInfo['aac']['program_configs'][$i]['matrix_mixdown_idx_present']) { + $ThisFileInfo['aac']['program_configs'][$i]['matrix_mixdown_idx'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2)); + $bitoffset += 2; + $ThisFileInfo['aac']['program_configs'][$i]['pseudo_surround_enable'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); + $bitoffset += 1; + } + for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_front_channel_elements']; $j++) { + $ThisFileInfo['aac']['program_configs'][$i]['front_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); + $bitoffset += 1; + $ThisFileInfo['aac']['program_configs'][$i]['front_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); + $bitoffset += 4; + } + for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_side_channel_elements']; $j++) { + $ThisFileInfo['aac']['program_configs'][$i]['side_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); + $bitoffset += 1; + $ThisFileInfo['aac']['program_configs'][$i]['side_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); + $bitoffset += 4; + } + for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_back_channel_elements']; $j++) { + $ThisFileInfo['aac']['program_configs'][$i]['back_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); + $bitoffset += 1; + $ThisFileInfo['aac']['program_configs'][$i]['back_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); + $bitoffset += 4; + } + for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_lfe_channel_elements']; $j++) { + $ThisFileInfo['aac']['program_configs'][$i]['lfe_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); + $bitoffset += 4; + } + for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_assoc_data_elements']; $j++) { + $ThisFileInfo['aac']['program_configs'][$i]['assoc_data_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); + $bitoffset += 4; + } + for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_valid_cc_elements']; $j++) { + $ThisFileInfo['aac']['program_configs'][$i]['cc_element_is_ind_sw'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); + $bitoffset += 1; + $ThisFileInfo['aac']['program_configs'][$i]['valid_cc_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); + $bitoffset += 4; + } + + $bitoffset = ceil($bitoffset / 8) * 8; + + $ThisFileInfo['aac']['program_configs'][$i]['comment_field_bytes'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 8)); + $bitoffset += 8; + $ThisFileInfo['aac']['program_configs'][$i]['comment_field'] = getid3_lib::Bin2String(substr($AACheaderBitstream, $bitoffset, 8 * $ThisFileInfo['aac']['program_configs'][$i]['comment_field_bytes'])); + $bitoffset += 8 * $ThisFileInfo['aac']['program_configs'][$i]['comment_field_bytes']; + + + $ThisFileInfo['aac']['header']['profile_text'] = $this->AACprofileLookup($ThisFileInfo['aac']['program_configs'][$i]['object_type'], $ThisFileInfo['aac']['header']['mpeg_version']); + $ThisFileInfo['aac']['program_configs'][$i]['sampling_frequency'] = $this->AACsampleRateLookup($ThisFileInfo['aac']['program_configs'][$i]['sampling_frequency_index']); + $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['aac']['program_configs'][$i]['sampling_frequency']; + $ThisFileInfo['audio']['channels'] = $this->AACchannelCountCalculate($ThisFileInfo['aac']['program_configs'][$i]); + if ($ThisFileInfo['aac']['program_configs'][$i]['comment_field']) { + $ThisFileInfo['aac']['comments'][] = $ThisFileInfo['aac']['program_configs'][$i]['comment_field']; + } + } + $ThisFileInfo['playtime_seconds'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['audio']['bitrate']; + + $ThisFileInfo['audio']['encoder_options'] = $ThisFileInfo['aac']['header_type'].' '.$ThisFileInfo['aac']['header']['profile_text']; + + + + return true; + + } else { + + unset($ThisFileInfo['fileformat']); + unset($ThisFileInfo['aac']); + $ThisFileInfo['error'][] = 'AAC-ADIF synch not found at offset '.$ThisFileInfo['avdataoffset'].' (expected "ADIF", found "'.substr($AACheader, 0, 4).'" instead)'; + return false; + + } + + } + + + function getAACADTSheaderFilepointer(&$fd, &$ThisFileInfo, $MaxFramesToScan=1000000, $ReturnExtendedInfo=false) { + // based loosely on code from AACfile by Jurgen Faul + // http://jfaul.de/atl or http://j-faul.virtualave.net/atl/atl.html + + + // http://faac.sourceforge.net/wiki/index.php?page=ADTS + + // * ADTS Fixed Header: these don't change from frame to frame + // syncword 12 always: '111111111111' + // ID 1 0: MPEG-4, 1: MPEG-2 + // layer 2 always: '00' + // protection_absent 1 + // profile 2 + // sampling_frequency_index 4 + // private_bit 1 + // channel_configuration 3 + // original/copy 1 + // home 1 + // emphasis 2 only if ID == 0 (ie MPEG-4) + + // * ADTS Variable Header: these can change from frame to frame + // copyright_identification_bit 1 + // copyright_identification_start 1 + // aac_frame_length 13 length of the frame including header (in bytes) + // adts_buffer_fullness 11 0x7FF indicates VBR + // no_raw_data_blocks_in_frame 2 + + // * ADTS Error check + // crc_check 16 only if protection_absent == 0 + + $byteoffset = 0; + $framenumber = 0; + + // Init bit pattern array + static $decbin = array(); + + // Populate $bindec + for ($i = 0; $i < 256; $i++) { + $decbin[chr($i)] = str_pad(decbin($i), 8, '0', STR_PAD_LEFT); + } + + // used to calculate bitrate below + $BitrateCache = array(); + + + while (true) { + // breaks out when end-of-file encountered, or invalid data found, + // or MaxFramesToScan frames have been scanned + + fseek($fd, $byteoffset, SEEK_SET); + + // First get substring + $substring = fread($fd, 10); + $substringlength = strlen($substring); + if ($substringlength != 10) { + $ThisFileInfo['error'][] = 'Failed to read 10 bytes at offset '.(ftell($fd) - $substringlength).' (only read '.$substringlength.' bytes)'; + return false; + } + + // Initialise $AACheaderBitstream + $AACheaderBitstream = ''; + + // Loop thru substring chars + for ($i = 0; $i < 10; $i++) { + $AACheaderBitstream .= $decbin[$substring{$i}]; + } + + $bitoffset = 0; + + $synctest = bindec(substr($AACheaderBitstream, $bitoffset, 12)); + + $bitoffset += 12; + if ($synctest != 0x0FFF) { + $ThisFileInfo['error'][] = 'Synch pattern (0x0FFF) not found at offset '.(ftell($fd) - 10).' (found 0x0'.strtoupper(dechex($synctest)).' instead)'; + if ($ThisFileInfo['fileformat'] == 'aac') { + return true; + } + return false; + } + + // Gather info for first frame only - this takes time to do 1000 times! + if ($framenumber > 0) { + + if (!$AACheaderBitstream[$bitoffset]) { + + // MPEG-4 + $bitoffset += 20; + + } else { + + // MPEG-2 + $bitoffset += 18; + + } + + } else { + + $ThisFileInfo['aac']['header_type'] = 'ADTS'; + $ThisFileInfo['aac']['header']['synch'] = $synctest; + $ThisFileInfo['fileformat'] = 'aac'; + $ThisFileInfo['audio']['dataformat'] = 'aac'; + + $ThisFileInfo['aac']['header']['mpeg_version'] = ((substr($AACheaderBitstream, $bitoffset, 1) == '0') ? 4 : 2); + $bitoffset += 1; + $ThisFileInfo['aac']['header']['layer'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2)); + $bitoffset += 2; + if ($ThisFileInfo['aac']['header']['layer'] != 0) { + $ThisFileInfo['error'][] = 'Layer error - expected 0x00, found 0x'.dechex($ThisFileInfo['aac']['header']['layer']).' instead'; + return false; + } + $ThisFileInfo['aac']['header']['crc_present'] = ((substr($AACheaderBitstream, $bitoffset, 1) == '0') ? true : false); + $bitoffset += 1; + $ThisFileInfo['aac']['header']['profile_id'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2)); + $bitoffset += 2; + $ThisFileInfo['aac']['header']['profile_text'] = $this->AACprofileLookup($ThisFileInfo['aac']['header']['profile_id'], $ThisFileInfo['aac']['header']['mpeg_version']); + + $ThisFileInfo['aac']['header']['sample_frequency_index'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4)); + $bitoffset += 4; + $ThisFileInfo['aac']['header']['sample_frequency'] = $this->AACsampleRateLookup($ThisFileInfo['aac']['header']['sample_frequency_index']); + if ($ThisFileInfo['aac']['header']['sample_frequency'] == 0) { + $ThisFileInfo['error'][] = 'Corrupt AAC file: sample_frequency == zero'; + return false; + } + $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['aac']['header']['sample_frequency']; + + $ThisFileInfo['aac']['header']['private'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); + $bitoffset += 1; + $ThisFileInfo['aac']['header']['channel_configuration'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 3)); + $bitoffset += 3; + $ThisFileInfo['audio']['channels'] = $ThisFileInfo['aac']['header']['channel_configuration']; + $ThisFileInfo['aac']['header']['original'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); + $bitoffset += 1; + $ThisFileInfo['aac']['header']['home'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); + $bitoffset += 1; + + if ($ThisFileInfo['aac']['header']['mpeg_version'] == 4) { + $ThisFileInfo['aac']['header']['emphasis'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2)); + $bitoffset += 2; + } + + if ($ReturnExtendedInfo) { + + $ThisFileInfo['aac'][$framenumber]['copyright_id_bit'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); + $bitoffset += 1; + $ThisFileInfo['aac'][$framenumber]['copyright_id_start'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1)); + $bitoffset += 1; + + } else { + + $bitoffset += 2; + + } + + } + + $FrameLength = bindec(substr($AACheaderBitstream, $bitoffset, 13)); + + if (!isset($BitrateCache[$FrameLength])) { + $BitrateCache[$FrameLength] = ($ThisFileInfo['aac']['header']['sample_frequency'] / 1024) * $FrameLength * 8; + } + @$ThisFileInfo['aac']['bitrate_distribution'][$BitrateCache[$FrameLength]]++; + + $ThisFileInfo['aac'][$framenumber]['aac_frame_length'] = $FrameLength; + $bitoffset += 13; + $ThisFileInfo['aac'][$framenumber]['adts_buffer_fullness'] = bindec(substr($AACheaderBitstream, $bitoffset, 11)); + $bitoffset += 11; + if ($ThisFileInfo['aac'][$framenumber]['adts_buffer_fullness'] == 0x07FF) { + $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; + } else { + $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; + } + $ThisFileInfo['aac'][$framenumber]['num_raw_data_blocks'] = bindec(substr($AACheaderBitstream, $bitoffset, 2)); + $bitoffset += 2; + + if ($ThisFileInfo['aac']['header']['crc_present']) { + //$ThisFileInfo['aac'][$framenumber]['crc'] = bindec(substr($AACheaderBitstream, $bitoffset, 16)); + $bitoffset += 16; + } + + if (!$ReturnExtendedInfo) { + unset($ThisFileInfo['aac'][$framenumber]); + } + + $byteoffset += $FrameLength; + if ((++$framenumber < $MaxFramesToScan) && (($byteoffset + 10) < $ThisFileInfo['avdataend'])) { + + // keep scanning + + } else { + + $ThisFileInfo['aac']['frames'] = $framenumber; + $ThisFileInfo['playtime_seconds'] = ($ThisFileInfo['avdataend'] / $byteoffset) * (($framenumber * 1024) / $ThisFileInfo['aac']['header']['sample_frequency']); // (1 / % of file scanned) * (samples / (samples/sec)) = seconds + if ($ThisFileInfo['playtime_seconds'] == 0) { + $ThisFileInfo['error'][] = 'Corrupt AAC file: playtime_seconds == zero'; + return false; + } + $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; + ksort($ThisFileInfo['aac']['bitrate_distribution']); + + $ThisFileInfo['audio']['encoder_options'] = $ThisFileInfo['aac']['header_type'].' '.$ThisFileInfo['aac']['header']['profile_text']; + + return true; + + } + } + // should never get here. + } + + function AACsampleRateLookup($samplerateid) { + static $AACsampleRateLookup = array(); + if (empty($AACsampleRateLookup)) { + $AACsampleRateLookup[0] = 96000; + $AACsampleRateLookup[1] = 88200; + $AACsampleRateLookup[2] = 64000; + $AACsampleRateLookup[3] = 48000; + $AACsampleRateLookup[4] = 44100; + $AACsampleRateLookup[5] = 32000; + $AACsampleRateLookup[6] = 24000; + $AACsampleRateLookup[7] = 22050; + $AACsampleRateLookup[8] = 16000; + $AACsampleRateLookup[9] = 12000; + $AACsampleRateLookup[10] = 11025; + $AACsampleRateLookup[11] = 8000; + $AACsampleRateLookup[12] = 0; + $AACsampleRateLookup[13] = 0; + $AACsampleRateLookup[14] = 0; + $AACsampleRateLookup[15] = 0; + } + return (isset($AACsampleRateLookup[$samplerateid]) ? $AACsampleRateLookup[$samplerateid] : 'invalid'); + } + + function AACprofileLookup($profileid, $mpegversion) { + static $AACprofileLookup = array(); + if (empty($AACprofileLookup)) { + $AACprofileLookup[2][0] = 'Main profile'; + $AACprofileLookup[2][1] = 'Low Complexity profile (LC)'; + $AACprofileLookup[2][2] = 'Scalable Sample Rate profile (SSR)'; + $AACprofileLookup[2][3] = '(reserved)'; + $AACprofileLookup[4][0] = 'AAC_MAIN'; + $AACprofileLookup[4][1] = 'AAC_LC'; + $AACprofileLookup[4][2] = 'AAC_SSR'; + $AACprofileLookup[4][3] = 'AAC_LTP'; + } + return (isset($AACprofileLookup[$mpegversion][$profileid]) ? $AACprofileLookup[$mpegversion][$profileid] : 'invalid'); + } + + function AACchannelCountCalculate($program_configs) { + $channels = 0; + for ($i = 0; $i < $program_configs['num_front_channel_elements']; $i++) { + $channels++; + if ($program_configs['front_element_is_cpe'][$i]) { + // each front element is channel pair (CPE = Channel Pair Element) + $channels++; + } + } + for ($i = 0; $i < $program_configs['num_side_channel_elements']; $i++) { + $channels++; + if ($program_configs['side_element_is_cpe'][$i]) { + // each side element is channel pair (CPE = Channel Pair Element) + $channels++; + } + } + for ($i = 0; $i < $program_configs['num_back_channel_elements']; $i++) { + $channels++; + if ($program_configs['back_element_is_cpe'][$i]) { + // each back element is channel pair (CPE = Channel Pair Element) + $channels++; + } + } + for ($i = 0; $i < $program_configs['num_lfe_channel_elements']; $i++) { + $channels++; + } + return $channels; + } + +} + + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.audio.ac3.php b/includes/getid3/getid3/module.audio.ac3.php new file mode 100644 index 0000000..9809a47 --- /dev/null +++ b/includes/getid3/getid3/module.audio.ac3.php @@ -0,0 +1,497 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio.ac3.php // +// module for analyzing AC-3 (aka Dolby Digital) audio files // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_ac3 +{ + + function getid3_ac3(&$fd, &$ThisFileInfo) { + + ///AH + $ThisFileInfo['ac3']['raw']['bsi'] = array(); + $thisfile_ac3 = &$ThisFileInfo['ac3']; + $thisfile_ac3_raw = &$thisfile_ac3['raw']; + $thisfile_ac3_raw_bsi = &$thisfile_ac3_raw['bsi']; + + + // http://www.atsc.org/standards/a_52a.pdf + + $ThisFileInfo['fileformat'] = 'ac3'; + $ThisFileInfo['audio']['dataformat'] = 'ac3'; + $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; + $ThisFileInfo['audio']['lossless'] = false; + + // An AC-3 serial coded audio bit stream is made up of a sequence of synchronization frames + // Each synchronization frame contains 6 coded audio blocks (AB), each of which represent 256 + // new audio samples per channel. A synchronization information (SI) header at the beginning + // of each frame contains information needed to acquire and maintain synchronization. A + // bit stream information (BSI) header follows SI, and contains parameters describing the coded + // audio service. The coded audio blocks may be followed by an auxiliary data (Aux) field. At the + // end of each frame is an error check field that includes a CRC word for error detection. An + // additional CRC word is located in the SI header, the use of which, by a decoder, is optional. + // + // syncinfo() | bsi() | AB0 | AB1 | AB2 | AB3 | AB4 | AB5 | Aux | CRC + + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + $AC3header['syncinfo'] = fread($fd, 5); + $thisfile_ac3_raw['synchinfo']['synchword'] = substr($AC3header['syncinfo'], 0, 2); + + if ($thisfile_ac3_raw['synchinfo']['synchword'] != "\x0B\x77") { + + $ThisFileInfo['error'][] = 'Expecting "\x0B\x77" at offset '.$ThisFileInfo['avdataoffset'].', found \x'.strtoupper(dechex($AC3header['syncinfo']{0})).'\x'.strtoupper(dechex($AC3header['syncinfo']{1})).' instead'; + unset($thisfile_ac3); + return false; + + } else { + + // syncinfo() { + // syncword 16 + // crc1 16 + // fscod 2 + // frmsizecod 6 + // } /* end of syncinfo */ + + $thisfile_ac3_raw['synchinfo']['crc1'] = getid3_lib::LittleEndian2Int(substr($AC3header['syncinfo'], 2, 2)); + $ac3_synchinfo_fscod_frmsizecod = getid3_lib::LittleEndian2Int(substr($AC3header['syncinfo'], 4, 1)); + $thisfile_ac3_raw['synchinfo']['fscod'] = ($ac3_synchinfo_fscod_frmsizecod & 0xC0) >> 6; + $thisfile_ac3_raw['synchinfo']['frmsizecod'] = ($ac3_synchinfo_fscod_frmsizecod & 0x3F); + + $thisfile_ac3['sample_rate'] = $this->AC3sampleRateCodeLookup($thisfile_ac3_raw['synchinfo']['fscod']); + if ($thisfile_ac3_raw['synchinfo']['fscod'] <= 3) { + $ThisFileInfo['audio']['sample_rate'] = $thisfile_ac3['sample_rate']; + } + + $thisfile_ac3['frame_length'] = $this->AC3frameSizeLookup($thisfile_ac3_raw['synchinfo']['frmsizecod'], $thisfile_ac3_raw['synchinfo']['fscod']); + $thisfile_ac3['bitrate'] = $this->AC3bitrateLookup($thisfile_ac3_raw['synchinfo']['frmsizecod']); + $ThisFileInfo['audio']['bitrate'] = $thisfile_ac3['bitrate']; + + $AC3header['bsi'] = getid3_lib::BigEndian2Bin(fread($fd, 15)); + $ac3_bsi_offset = 0; + + $thisfile_ac3_raw_bsi['bsid'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 5)); + $ac3_bsi_offset += 5; + if ($thisfile_ac3_raw_bsi['bsid'] > 8) { + // Decoders which can decode version 8 will thus be able to decode version numbers less than 8. + // If this standard is extended by the addition of additional elements or features, a value of bsid greater than 8 will be used. + // Decoders built to this version of the standard will not be able to decode versions with bsid greater than 8. + $ThisFileInfo['error'][] = 'Bit stream identification is version '.$thisfile_ac3_raw_bsi['bsid'].', but getID3() only understands up to version 8'; + unset($thisfile_ac3); + return false; + } + + $thisfile_ac3_raw_bsi['bsmod'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 3)); + $ac3_bsi_offset += 3; + $thisfile_ac3_raw_bsi['acmod'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 3)); + $ac3_bsi_offset += 3; + + $thisfile_ac3['service_type'] = $this->AC3serviceTypeLookup($thisfile_ac3_raw_bsi['bsmod'], $thisfile_ac3_raw_bsi['acmod']); + $ac3_coding_mode = $this->AC3audioCodingModeLookup($thisfile_ac3_raw_bsi['acmod']); + foreach($ac3_coding_mode as $key => $value) { + $thisfile_ac3[$key] = $value; + } + switch ($thisfile_ac3_raw_bsi['acmod']) { + case 0: + case 1: + $ThisFileInfo['audio']['channelmode'] = 'mono'; + break; + case 3: + case 4: + $ThisFileInfo['audio']['channelmode'] = 'stereo'; + break; + default: + $ThisFileInfo['audio']['channelmode'] = 'surround'; + break; + } + $ThisFileInfo['audio']['channels'] = $thisfile_ac3['num_channels']; + + if ($thisfile_ac3_raw_bsi['acmod'] & 0x01) { + // If the lsb of acmod is a 1, center channel is in use and cmixlev follows in the bit stream. + $thisfile_ac3_raw_bsi['cmixlev'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 2)); + $ac3_bsi_offset += 2; + $thisfile_ac3['center_mix_level'] = $this->AC3centerMixLevelLookup($thisfile_ac3_raw_bsi['cmixlev']); + } + + if ($thisfile_ac3_raw_bsi['acmod'] & 0x04) { + // If the msb of acmod is a 1, surround channels are in use and surmixlev follows in the bit stream. + $thisfile_ac3_raw_bsi['surmixlev'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 2)); + $ac3_bsi_offset += 2; + $thisfile_ac3['surround_mix_level'] = $this->AC3surroundMixLevelLookup($thisfile_ac3_raw_bsi['surmixlev']); + } + + if ($thisfile_ac3_raw_bsi['acmod'] == 0x02) { + // When operating in the two channel mode, this 2-bit code indicates whether or not the program has been encoded in Dolby Surround. + $thisfile_ac3_raw_bsi['dsurmod'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 2)); + $ac3_bsi_offset += 2; + $thisfile_ac3['dolby_surround_mode'] = $this->AC3dolbySurroundModeLookup($thisfile_ac3_raw_bsi['dsurmod']); + } + + $thisfile_ac3_raw_bsi['lfeon'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1)); + $ac3_bsi_offset += 1; + $thisfile_ac3['lfe_enabled'] = $thisfile_ac3_raw_bsi['lfeon']; + if ($thisfile_ac3_raw_bsi['lfeon']) { + //$ThisFileInfo['audio']['channels']++; + $ThisFileInfo['audio']['channels'] .= '.1'; + } + + $thisfile_ac3['channels_enabled'] = $this->AC3channelsEnabledLookup($thisfile_ac3_raw_bsi['acmod'], $thisfile_ac3_raw_bsi['lfeon']); + + // This indicates how far the average dialogue level is below digital 100 percent. Valid values are 1–31. + // The value of 0 is reserved. The values of 1 to 31 are interpreted as -1 dB to -31 dB with respect to digital 100 percent. + $thisfile_ac3_raw_bsi['dialnorm'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 5)); + $ac3_bsi_offset += 5; + $thisfile_ac3['dialogue_normalization'] = '-'.$thisfile_ac3_raw_bsi['dialnorm'].'dB'; + + $thisfile_ac3_raw_bsi['compre_flag'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1)); + $ac3_bsi_offset += 1; + if ($thisfile_ac3_raw_bsi['compre_flag']) { + $thisfile_ac3_raw_bsi['compr'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 8)); + $ac3_bsi_offset += 8; + $thisfile_ac3['heavy_compression'] = $this->AC3heavyCompression($thisfile_ac3_raw_bsi['compr']); + } + + $thisfile_ac3_raw_bsi['langcode_flag'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1)); + $ac3_bsi_offset += 1; + if ($thisfile_ac3_raw_bsi['langcode_flag']) { + $thisfile_ac3_raw_bsi['langcod'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 8)); + $ac3_bsi_offset += 8; + } + + $thisfile_ac3_raw_bsi['audprodie'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1)); + $ac3_bsi_offset += 1; + if ($thisfile_ac3_raw_bsi['audprodie']) { + $thisfile_ac3_raw_bsi['mixlevel'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 5)); + $ac3_bsi_offset += 5; + $thisfile_ac3_raw_bsi['roomtyp'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 2)); + $ac3_bsi_offset += 2; + + $thisfile_ac3['mixing_level'] = (80 + $thisfile_ac3_raw_bsi['mixlevel']).'dB'; + $thisfile_ac3['room_type'] = $this->AC3roomTypeLookup($thisfile_ac3_raw_bsi['roomtyp']); + } + + if ($thisfile_ac3_raw_bsi['acmod'] == 0x00) { + // If acmod is 0, then two completely independent program channels (dual mono) + // are encoded into the bit stream, and are referenced as Ch1, Ch2. In this case, + // a number of additional items are present in BSI or audblk to fully describe Ch2. + + + // This indicates how far the average dialogue level is below digital 100 percent. Valid values are 1–31. + // The value of 0 is reserved. The values of 1 to 31 are interpreted as -1 dB to -31 dB with respect to digital 100 percent. + $thisfile_ac3_raw_bsi['dialnorm2'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 5)); + $ac3_bsi_offset += 5; + $thisfile_ac3['dialogue_normalization2'] = '-'.$thisfile_ac3_raw_bsi['dialnorm2'].'dB'; + + $thisfile_ac3_raw_bsi['compre_flag2'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1)); + $ac3_bsi_offset += 1; + if ($thisfile_ac3_raw_bsi['compre_flag2']) { + $thisfile_ac3_raw_bsi['compr2'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 8)); + $ac3_bsi_offset += 8; + $thisfile_ac3['heavy_compression2'] = $this->AC3heavyCompression($thisfile_ac3_raw_bsi['compr2']); + } + + $thisfile_ac3_raw_bsi['langcode_flag2'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1)); + $ac3_bsi_offset += 1; + if ($thisfile_ac3_raw_bsi['langcode_flag2']) { + $thisfile_ac3_raw_bsi['langcod2'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 8)); + $ac3_bsi_offset += 8; + } + + $thisfile_ac3_raw_bsi['audprodie2'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1)); + $ac3_bsi_offset += 1; + if ($thisfile_ac3_raw_bsi['audprodie2']) { + $thisfile_ac3_raw_bsi['mixlevel2'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 5)); + $ac3_bsi_offset += 5; + $thisfile_ac3_raw_bsi['roomtyp2'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 2)); + $ac3_bsi_offset += 2; + + $thisfile_ac3['mixing_level2'] = (80 + $thisfile_ac3_raw_bsi['mixlevel2']).'dB'; + $thisfile_ac3['room_type2'] = $this->AC3roomTypeLookup($thisfile_ac3_raw_bsi['roomtyp2']); + } + + } + + $thisfile_ac3_raw_bsi['copyright'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1)); + $ac3_bsi_offset += 1; + + $thisfile_ac3_raw_bsi['original'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1)); + $ac3_bsi_offset += 1; + + $thisfile_ac3_raw_bsi['timecode1_flag'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1)); + $ac3_bsi_offset += 1; + if ($thisfile_ac3_raw_bsi['timecode1_flag']) { + $thisfile_ac3_raw_bsi['timecode1'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 14)); + $ac3_bsi_offset += 14; + } + + $thisfile_ac3_raw_bsi['timecode2_flag'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1)); + $ac3_bsi_offset += 1; + if ($thisfile_ac3_raw_bsi['timecode2_flag']) { + $thisfile_ac3_raw_bsi['timecode2'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 14)); + $ac3_bsi_offset += 14; + } + + $thisfile_ac3_raw_bsi['addbsi_flag'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1)); + $ac3_bsi_offset += 1; + if ($thisfile_ac3_raw_bsi['addbsi_flag']) { + $thisfile_ac3_raw_bsi['addbsi_length'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 6)); + $ac3_bsi_offset += 6; + + $AC3header['bsi'] .= getid3_lib::BigEndian2Bin(fread($fd, $thisfile_ac3_raw_bsi['addbsi_length'])); + + $thisfile_ac3_raw_bsi['addbsi_data'] = substr($AC3header['bsi'], $ac3_bsi_offset, $thisfile_ac3_raw_bsi['addbsi_length'] * 8); + $ac3_bsi_offset += $thisfile_ac3_raw_bsi['addbsi_length'] * 8; + } + + } + + return true; + } + + + function AC3sampleRateCodeLookup($fscod) { + static $AC3sampleRateCodeLookup = array( + 0 => 48000, + 1 => 44100, + 2 => 32000, + 3 => 'reserved' // If the reserved code is indicated, the decoder should not attempt to decode audio and should mute. + ); + return (isset($AC3sampleRateCodeLookup[$fscod]) ? $AC3sampleRateCodeLookup[$fscod] : false); + } + + function AC3serviceTypeLookup($bsmod, $acmod) { + static $AC3serviceTypeLookup = array(); + if (empty($AC3serviceTypeLookup)) { + for ($i = 0; $i <= 7; $i++) { + $AC3serviceTypeLookup[0][$i] = 'main audio service: complete main (CM)'; + $AC3serviceTypeLookup[1][$i] = 'main audio service: music and effects (ME)'; + $AC3serviceTypeLookup[2][$i] = 'associated service: visually impaired (VI)'; + $AC3serviceTypeLookup[3][$i] = 'associated service: hearing impaired (HI)'; + $AC3serviceTypeLookup[4][$i] = 'associated service: dialogue (D)'; + $AC3serviceTypeLookup[5][$i] = 'associated service: commentary (C)'; + $AC3serviceTypeLookup[6][$i] = 'associated service: emergency (E)'; + } + + $AC3serviceTypeLookup[7][1] = 'associated service: voice over (VO)'; + for ($i = 2; $i <= 7; $i++) { + $AC3serviceTypeLookup[7][$i] = 'main audio service: karaoke'; + } + } + return (isset($AC3serviceTypeLookup[$bsmod][$acmod]) ? $AC3serviceTypeLookup[$bsmod][$acmod] : false); + } + + function AC3audioCodingModeLookup($acmod) { + static $AC3audioCodingModeLookup = array(); + if (empty($AC3audioCodingModeLookup)) { + // array(channel configuration, # channels (not incl LFE), channel order) + $AC3audioCodingModeLookup = array ( + 0 => array('channel_config'=>'1+1', 'num_channels'=>2, 'channel_order'=>'Ch1,Ch2'), + 1 => array('channel_config'=>'1/0', 'num_channels'=>1, 'channel_order'=>'C'), + 2 => array('channel_config'=>'2/0', 'num_channels'=>2, 'channel_order'=>'L,R'), + 3 => array('channel_config'=>'3/0', 'num_channels'=>3, 'channel_order'=>'L,C,R'), + 4 => array('channel_config'=>'2/1', 'num_channels'=>3, 'channel_order'=>'L,R,S'), + 5 => array('channel_config'=>'3/1', 'num_channels'=>4, 'channel_order'=>'L,C,R,S'), + 6 => array('channel_config'=>'2/2', 'num_channels'=>4, 'channel_order'=>'L,R,SL,SR'), + 7 => array('channel_config'=>'3/2', 'num_channels'=>5, 'channel_order'=>'L,C,R,SL,SR') + ); + } + return (isset($AC3audioCodingModeLookup[$acmod]) ? $AC3audioCodingModeLookup[$acmod] : false); + } + + function AC3centerMixLevelLookup($cmixlev) { + static $AC3centerMixLevelLookup; + if (empty($AC3centerMixLevelLookup)) { + $AC3centerMixLevelLookup = array( + 0 => pow(2, -3.0 / 6), // 0.707 (–3.0 dB) + 1 => pow(2, -4.5 / 6), // 0.595 (–4.5 dB) + 2 => pow(2, -6.0 / 6), // 0.500 (–6.0 dB) + 3 => 'reserved' + ); + } + return (isset($AC3centerMixLevelLookup[$cmixlev]) ? $AC3centerMixLevelLookup[$cmixlev] : false); + } + + function AC3surroundMixLevelLookup($surmixlev) { + static $AC3surroundMixLevelLookup; + if (empty($AC3surroundMixLevelLookup)) { + $AC3surroundMixLevelLookup = array( + 0 => pow(2, -3.0 / 6), + 1 => pow(2, -6.0 / 6), + 2 => 0, + 3 => 'reserved' + ); + } + return (isset($AC3surroundMixLevelLookup[$surmixlev]) ? $AC3surroundMixLevelLookup[$surmixlev] : false); + } + + function AC3dolbySurroundModeLookup($dsurmod) { + static $AC3dolbySurroundModeLookup = array( + 0 => 'not indicated', + 1 => 'Not Dolby Surround encoded', + 2 => 'Dolby Surround encoded', + 3 => 'reserved' + ); + return (isset($AC3dolbySurroundModeLookup[$dsurmod]) ? $AC3dolbySurroundModeLookup[$dsurmod] : false); + } + + function AC3channelsEnabledLookup($acmod, $lfeon) { + $AC3channelsEnabledLookup = array( + 'ch1'=>(bool) ($acmod == 0), + 'ch2'=>(bool) ($acmod == 0), + 'left'=>(bool) ($acmod > 1), + 'right'=>(bool) ($acmod > 1), + 'center'=>(bool) ($acmod & 0x01), + 'surround_mono'=>false, + 'surround_left'=>false, + 'surround_right'=>false, + 'lfe'=>$lfeon); + switch ($acmod) { + case 4: + case 5: + $AC3channelsEnabledLookup['surround_mono'] = true; + break; + case 6: + case 7: + $AC3channelsEnabledLookup['surround_left'] = true; + $AC3channelsEnabledLookup['surround_right'] = true; + break; + } + return $AC3channelsEnabledLookup; + } + + function AC3heavyCompression($compre) { + // The first four bits indicate gain changes in 6.02dB increments which can be + // implemented with an arithmetic shift operation. The following four bits + // indicate linear gain changes, and require a 5-bit multiply. + // We will represent the two 4-bit fields of compr as follows: + // X0 X1 X2 X3 . Y4 Y5 Y6 Y7 + // The meaning of the X values is most simply described by considering X to represent a 4-bit + // signed integer with values from –8 to +7. The gain indicated by X is then (X + 1) * 6.02 dB. The + // following table shows this in detail. + + // Meaning of 4 msb of compr + // 7 +48.16 dB + // 6 +42.14 dB + // 5 +36.12 dB + // 4 +30.10 dB + // 3 +24.08 dB + // 2 +18.06 dB + // 1 +12.04 dB + // 0 +6.02 dB + // -1 0 dB + // -2 –6.02 dB + // -3 –12.04 dB + // -4 –18.06 dB + // -5 –24.08 dB + // -6 –30.10 dB + // -7 –36.12 dB + // -8 –42.14 dB + + $fourbit = str_pad(decbin(($compre & 0xF0) >> 4), 4, '0', STR_PAD_LEFT); + if ($fourbit{0} == '1') { + $log_gain = -8 + bindec(substr($fourbit, 1)); + } else { + $log_gain = bindec(substr($fourbit, 1)); + } + $log_gain = ($log_gain + 1) * getid3_lib::RGADamplitude2dB(2); + + // The value of Y is a linear representation of a gain change of up to –6 dB. Y is considered to + // be an unsigned fractional integer, with a leading value of 1, or: 0.1 Y4 Y5 Y6 Y7 (base 2). Y can + // represent values between 0.111112 (or 31/32) and 0.100002 (or 1/2). Thus, Y can represent gain + // changes from –0.28 dB to –6.02 dB. + + $lin_gain = (16 + ($compre & 0x0F)) / 32; + + // The combination of X and Y values allows compr to indicate gain changes from + // 48.16 – 0.28 = +47.89 dB, to + // –42.14 – 6.02 = –48.16 dB. + + return $log_gain - $lin_gain; + } + + function AC3roomTypeLookup($roomtyp) { + static $AC3roomTypeLookup = array( + 0 => 'not indicated', + 1 => 'large room, X curve monitor', + 2 => 'small room, flat monitor', + 3 => 'reserved' + ); + return (isset($AC3roomTypeLookup[$roomtyp]) ? $AC3roomTypeLookup[$roomtyp] : false); + } + + function AC3frameSizeLookup($frmsizecod, $fscod) { + $padding = (bool) ($frmsizecod % 2); + $framesizeid = floor($frmsizecod / 2); + + static $AC3frameSizeLookup = array(); + if (empty($AC3frameSizeLookup)) { + $AC3frameSizeLookup = array ( + 0 => array(128, 138, 192), + 1 => array(40, 160, 174, 240), + 2 => array(48, 192, 208, 288), + 3 => array(56, 224, 242, 336), + 4 => array(64, 256, 278, 384), + 5 => array(80, 320, 348, 480), + 6 => array(96, 384, 416, 576), + 7 => array(112, 448, 486, 672), + 8 => array(128, 512, 556, 768), + 9 => array(160, 640, 696, 960), + 10 => array(192, 768, 834, 1152), + 11 => array(224, 896, 974, 1344), + 12 => array(256, 1024, 1114, 1536), + 13 => array(320, 1280, 1392, 1920), + 14 => array(384, 1536, 1670, 2304), + 15 => array(448, 1792, 1950, 2688), + 16 => array(512, 2048, 2228, 3072), + 17 => array(576, 2304, 2506, 3456), + 18 => array(640, 2560, 2786, 3840) + ); + } + if (($fscod == 1) && $padding) { + // frame lengths are padded by 1 word (16 bits) at 44100 + $AC3frameSizeLookup[$frmsizecod] += 2; + } + return (isset($AC3frameSizeLookup[$framesizeid][$fscod]) ? $AC3frameSizeLookup[$framesizeid][$fscod] : false); + } + + function AC3bitrateLookup($frmsizecod) { + $framesizeid = floor($frmsizecod / 2); + + static $AC3bitrateLookup = array( + 0 => 32000, + 1 => 40000, + 2 => 48000, + 3 => 56000, + 4 => 64000, + 5 => 80000, + 6 => 96000, + 7 => 112000, + 8 => 128000, + 9 => 160000, + 10 => 192000, + 11 => 224000, + 12 => 256000, + 13 => 320000, + 14 => 384000, + 15 => 448000, + 16 => 512000, + 17 => 576000, + 18 => 640000 + ); + return (isset($AC3bitrateLookup[$framesizeid]) ? $AC3bitrateLookup[$framesizeid] : false); + } + + +} + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.audio.au.php b/includes/getid3/getid3/module.audio.au.php new file mode 100644 index 0000000..afbc75d --- /dev/null +++ b/includes/getid3/getid3/module.audio.au.php @@ -0,0 +1,163 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio.au.php // +// module for analyzing AU files // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_au +{ + + function getid3_au(&$fd, &$ThisFileInfo) { + + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + $AUheader = fread($fd, 8); + + if (substr($AUheader, 0, 4) != '.snd') { + $ThisFileInfo['error'][] = 'Expecting ".snd" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($AUheader, 0, 4).'"'; + return false; + } + + // shortcut + $ThisFileInfo['au'] = array(); + $thisfile_au = &$ThisFileInfo['au']; + + $ThisFileInfo['fileformat'] = 'au'; + $ThisFileInfo['audio']['dataformat'] = 'au'; + $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; + $thisfile_au['encoding'] = 'ISO-8859-1'; + + $thisfile_au['header_length'] = getid3_lib::BigEndian2Int(substr($AUheader, 4, 4)); + $AUheader .= fread($fd, $thisfile_au['header_length'] - 8); + $ThisFileInfo['avdataoffset'] += $thisfile_au['header_length']; + + $thisfile_au['data_size'] = getid3_lib::BigEndian2Int(substr($AUheader, 8, 4)); + $thisfile_au['data_format_id'] = getid3_lib::BigEndian2Int(substr($AUheader, 12, 4)); + $thisfile_au['sample_rate'] = getid3_lib::BigEndian2Int(substr($AUheader, 16, 4)); + $thisfile_au['channels'] = getid3_lib::BigEndian2Int(substr($AUheader, 20, 4)); + $thisfile_au['comments']['comment'][] = trim(substr($AUheader, 24)); + + $thisfile_au['data_format'] = $this->AUdataFormatNameLookup($thisfile_au['data_format_id']); + $thisfile_au['used_bits_per_sample'] = $this->AUdataFormatUsedBitsPerSampleLookup($thisfile_au['data_format_id']); + if ($thisfile_au['bits_per_sample'] = $this->AUdataFormatBitsPerSampleLookup($thisfile_au['data_format_id'])) { + $ThisFileInfo['audio']['bits_per_sample'] = $thisfile_au['bits_per_sample']; + } else { + unset($thisfile_au['bits_per_sample']); + } + + $ThisFileInfo['audio']['sample_rate'] = $thisfile_au['sample_rate']; + $ThisFileInfo['audio']['channels'] = $thisfile_au['channels']; + + if (($ThisFileInfo['avdataoffset'] + $thisfile_au['data_size']) > $ThisFileInfo['avdataend']) { + $ThisFileInfo['warning'][] = 'Possible truncated file - expecting "'.$thisfile_au['data_size'].'" bytes of audio data, only found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']).' bytes"'; + } + + $ThisFileInfo['playtime_seconds'] = $thisfile_au['data_size'] / ($thisfile_au['sample_rate'] * $thisfile_au['channels'] * ($thisfile_au['used_bits_per_sample'] / 8)); + $ThisFileInfo['audio']['bitrate'] = ($thisfile_au['data_size'] * 8) / $ThisFileInfo['playtime_seconds']; + + return true; + } + + function AUdataFormatNameLookup($id) { + static $AUdataFormatNameLookup = array( + 0 => 'unspecified format', + 1 => '8-bit mu-law', + 2 => '8-bit linear', + 3 => '16-bit linear', + 4 => '24-bit linear', + 5 => '32-bit linear', + 6 => 'floating-point', + 7 => 'double-precision float', + 8 => 'fragmented sampled data', + 9 => 'SUN_FORMAT_NESTED', + 10 => 'DSP program', + 11 => '8-bit fixed-point', + 12 => '16-bit fixed-point', + 13 => '24-bit fixed-point', + 14 => '32-bit fixed-point', + + 16 => 'non-audio display data', + 17 => 'SND_FORMAT_MULAW_SQUELCH', + 18 => '16-bit linear with emphasis', + 19 => '16-bit linear with compression', + 20 => '16-bit linear with emphasis + compression', + 21 => 'Music Kit DSP commands', + 22 => 'SND_FORMAT_DSP_COMMANDS_SAMPLES', + 23 => 'CCITT g.721 4-bit ADPCM', + 24 => 'CCITT g.722 ADPCM', + 25 => 'CCITT g.723 3-bit ADPCM', + 26 => 'CCITT g.723 5-bit ADPCM', + 27 => 'A-Law 8-bit' + ); + return (isset($AUdataFormatNameLookup[$id]) ? $AUdataFormatNameLookup[$id] : false); + } + + function AUdataFormatBitsPerSampleLookup($id) { + static $AUdataFormatBitsPerSampleLookup = array( + 1 => 8, + 2 => 8, + 3 => 16, + 4 => 24, + 5 => 32, + 6 => 32, + 7 => 64, + + 11 => 8, + 12 => 16, + 13 => 24, + 14 => 32, + + 18 => 16, + 19 => 16, + 20 => 16, + + 23 => 16, + + 25 => 16, + 26 => 16, + 27 => 8 + ); + return (isset($AUdataFormatBitsPerSampleLookup[$id]) ? $AUdataFormatBitsPerSampleLookup[$id] : false); + } + + function AUdataFormatUsedBitsPerSampleLookup($id) { + static $AUdataFormatUsedBitsPerSampleLookup = array( + 1 => 8, + 2 => 8, + 3 => 16, + 4 => 24, + 5 => 32, + 6 => 32, + 7 => 64, + + 11 => 8, + 12 => 16, + 13 => 24, + 14 => 32, + + 18 => 16, + 19 => 16, + 20 => 16, + + 23 => 4, + + 25 => 3, + 26 => 5, + 27 => 8, + ); + return (isset($AUdataFormatUsedBitsPerSampleLookup[$id]) ? $AUdataFormatUsedBitsPerSampleLookup[$id] : false); + } + +} + + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.audio.avr.php b/includes/getid3/getid3/module.audio.avr.php new file mode 100644 index 0000000..6099439 --- /dev/null +++ b/includes/getid3/getid3/module.audio.avr.php @@ -0,0 +1,125 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio.avr.php // +// module for analyzing AVR Audio files // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_avr +{ + + function getid3_avr(&$fd, &$ThisFileInfo) { + + // http://cui.unige.ch/OSG/info/AudioFormats/ap11.html + // http://www.btinternet.com/~AnthonyJ/Atari/programming/avr_format.html + // offset type length name comments + // --------------------------------------------------------------------- + // 0 char 4 ID format ID == "2BIT" + // 4 char 8 name sample name (unused space filled with 0) + // 12 short 1 mono/stereo 0=mono, -1 (0xFFFF)=stereo + // With stereo, samples are alternated, + // the first voice is the left : + // (LRLRLRLRLRLRLRLRLR...) + // 14 short 1 resolution 8, 12 or 16 (bits) + // 16 short 1 signed or not 0=unsigned, -1 (0xFFFF)=signed + // 18 short 1 loop or not 0=no loop, -1 (0xFFFF)=loop on + // 20 short 1 MIDI note 0xFFnn, where 0 <= nn <= 127 + // 0xFFFF means "no MIDI note defined" + // 22 byte 1 Replay speed Frequence in the Replay software + // 0=5.485 Khz, 1=8.084 Khz, 2=10.971 Khz, + // 3=16.168 Khz, 4=21.942 Khz, 5=32.336 Khz + // 6=43.885 Khz, 7=47.261 Khz + // -1 (0xFF)=no defined Frequence + // 23 byte 3 sample rate in Hertz + // 26 long 1 size in bytes (2 * bytes in stereo) + // 30 long 1 loop begin 0 for no loop + // 34 long 1 loop size equal to 'size' for no loop + // 38 short 2 Reserved, MIDI keyboard split */ + // 40 short 2 Reserved, sample compression */ + // 42 short 2 Reserved */ + // 44 char 20; Additional filename space, used if (name[7] != 0) + // 64 byte 64 user data + // 128 bytes ? sample data (12 bits samples are coded on 16 bits: + // 0000 xxxx xxxx xxxx) + // --------------------------------------------------------------------- + + // Note that all values are in motorola (big-endian) format, and that long is + // assumed to be 4 bytes, and short 2 bytes. + // When reading the samples, you should handle both signed and unsigned data, + // and be prepared to convert 16->8 bit, or mono->stereo if needed. To convert + // 8-bit data between signed/unsigned just add 127 to the sample values. + // Simularly for 16-bit data you should add 32769 + + $ThisFileInfo['fileformat'] = 'avr'; + + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + $AVRheader = fread($fd, 128); + + $ThisFileInfo['avr']['raw']['magic'] = substr($AVRheader, 0, 4); + if ($ThisFileInfo['avr']['raw']['magic'] != '2BIT') { + $ThisFileInfo['error'][] = 'Expecting "2BIT" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$ThisFileInfo['avr']['raw']['magic'].'"'; + unset($ThisFileInfo['fileformat']); + unset($ThisFileInfo['avr']); + return false; + } + $ThisFileInfo['avdataoffset'] += 128; + + $ThisFileInfo['avr']['sample_name'] = rtrim(substr($AVRheader, 4, 8)); + $ThisFileInfo['avr']['raw']['mono'] = getid3_lib::BigEndian2Int(substr($AVRheader, 12, 2)); + $ThisFileInfo['avr']['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($AVRheader, 14, 2)); + $ThisFileInfo['avr']['raw']['signed'] = getid3_lib::BigEndian2Int(substr($AVRheader, 16, 2)); + $ThisFileInfo['avr']['raw']['loop'] = getid3_lib::BigEndian2Int(substr($AVRheader, 18, 2)); + $ThisFileInfo['avr']['raw']['midi'] = getid3_lib::BigEndian2Int(substr($AVRheader, 20, 2)); + $ThisFileInfo['avr']['raw']['replay_freq'] = getid3_lib::BigEndian2Int(substr($AVRheader, 22, 1)); + $ThisFileInfo['avr']['sample_rate'] = getid3_lib::BigEndian2Int(substr($AVRheader, 23, 3)); + $ThisFileInfo['avr']['sample_length'] = getid3_lib::BigEndian2Int(substr($AVRheader, 26, 4)); + $ThisFileInfo['avr']['loop_start'] = getid3_lib::BigEndian2Int(substr($AVRheader, 30, 4)); + $ThisFileInfo['avr']['loop_end'] = getid3_lib::BigEndian2Int(substr($AVRheader, 34, 4)); + $ThisFileInfo['avr']['midi_split'] = getid3_lib::BigEndian2Int(substr($AVRheader, 38, 2)); + $ThisFileInfo['avr']['sample_compression'] = getid3_lib::BigEndian2Int(substr($AVRheader, 40, 2)); + $ThisFileInfo['avr']['reserved'] = getid3_lib::BigEndian2Int(substr($AVRheader, 42, 2)); + $ThisFileInfo['avr']['sample_name_extra'] = rtrim(substr($AVRheader, 44, 20)); + $ThisFileInfo['avr']['comment'] = rtrim(substr($AVRheader, 64, 64)); + + $ThisFileInfo['avr']['flags']['stereo'] = (($ThisFileInfo['avr']['raw']['mono'] == 0) ? false : true); + $ThisFileInfo['avr']['flags']['signed'] = (($ThisFileInfo['avr']['raw']['signed'] == 0) ? false : true); + $ThisFileInfo['avr']['flags']['loop'] = (($ThisFileInfo['avr']['raw']['loop'] == 0) ? false : true); + + $ThisFileInfo['avr']['midi_notes'] = array(); + if (($ThisFileInfo['avr']['raw']['midi'] & 0xFF00) != 0xFF00) { + $ThisFileInfo['avr']['midi_notes'][] = ($ThisFileInfo['avr']['raw']['midi'] & 0xFF00) >> 8; + } + if (($ThisFileInfo['avr']['raw']['midi'] & 0x00FF) != 0x00FF) { + $ThisFileInfo['avr']['midi_notes'][] = ($ThisFileInfo['avr']['raw']['midi'] & 0x00FF); + } + + if (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) != ($ThisFileInfo['avr']['sample_length'] * (($ThisFileInfo['avr']['bits_per_sample'] == 8) ? 1 : 2))) { + $ThisFileInfo['warning'][] = 'Probable truncated file: expecting '.($ThisFileInfo['avr']['sample_length'] * (($ThisFileInfo['avr']['bits_per_sample'] == 8) ? 1 : 2)).' bytes of audio data, found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']); + } + + $ThisFileInfo['audio']['dataformat'] = 'avr'; + $ThisFileInfo['audio']['lossless'] = true; + $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; + $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['avr']['bits_per_sample']; + $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['avr']['sample_rate']; + $ThisFileInfo['audio']['channels'] = ($ThisFileInfo['avr']['flags']['stereo'] ? 2 : 1); + $ThisFileInfo['playtime_seconds'] = ($ThisFileInfo['avr']['sample_length'] / $ThisFileInfo['audio']['channels']) / $ThisFileInfo['avr']['sample_rate']; + $ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['avr']['sample_length'] * (($ThisFileInfo['avr']['bits_per_sample'] == 8) ? 8 : 16)) / $ThisFileInfo['playtime_seconds']; + + + return true; + } + +} + + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.audio.bonk.php b/includes/getid3/getid3/module.audio.bonk.php new file mode 100644 index 0000000..218aa32 --- /dev/null +++ b/includes/getid3/getid3/module.audio.bonk.php @@ -0,0 +1,213 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio.la.php // +// module for analyzing BONK audio files // +// dependencies: module.tag.id3v2.php (optional) // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_bonk +{ + function getid3_bonk(&$fd, &$ThisFileInfo) { + + // shortcut + $ThisFileInfo['bonk'] = array(); + $thisfile_bonk = &$ThisFileInfo['bonk']; + + $thisfile_bonk['dataoffset'] = $ThisFileInfo['avdataoffset']; + $thisfile_bonk['dataend'] = $ThisFileInfo['avdataend']; + + // scan-from-end method, for v0.6 and higher + fseek($fd, $thisfile_bonk['dataend'] - 8, SEEK_SET); + $PossibleBonkTag = fread($fd, 8); + while ($this->BonkIsValidTagName(substr($PossibleBonkTag, 4, 4), true)) { + $BonkTagSize = getid3_lib::LittleEndian2Int(substr($PossibleBonkTag, 0, 4)); + fseek($fd, 0 - $BonkTagSize, SEEK_CUR); + $BonkTagOffset = ftell($fd); + $TagHeaderTest = fread($fd, 5); + if (($TagHeaderTest{0} != "\x00") || (substr($PossibleBonkTag, 4, 4) != strtolower(substr($PossibleBonkTag, 4, 4)))) { + $ThisFileInfo['error'][] = 'Expecting "Ø'.strtoupper(substr($PossibleBonkTag, 4, 4)).'" at offset '.$BonkTagOffset.', found "'.$TagHeaderTest.'"'; + return false; + } + $BonkTagName = substr($TagHeaderTest, 1, 4); + + $thisfile_bonk[$BonkTagName]['size'] = $BonkTagSize; + $thisfile_bonk[$BonkTagName]['offset'] = $BonkTagOffset; + $this->HandleBonkTags($fd, $BonkTagName, $ThisFileInfo); + $NextTagEndOffset = $BonkTagOffset - 8; + if ($NextTagEndOffset < $thisfile_bonk['dataoffset']) { + if (empty($ThisFileInfo['audio']['encoder'])) { + $ThisFileInfo['audio']['encoder'] = 'Extended BONK v0.9+'; + } + return true; + } + fseek($fd, $NextTagEndOffset, SEEK_SET); + $PossibleBonkTag = fread($fd, 8); + } + + // seek-from-beginning method for v0.4 and v0.5 + if (empty($thisfile_bonk['BONK'])) { + fseek($fd, $thisfile_bonk['dataoffset'], SEEK_SET); + do { + $TagHeaderTest = fread($fd, 5); + switch ($TagHeaderTest) { + case "\x00".'BONK': + if (empty($ThisFileInfo['audio']['encoder'])) { + $ThisFileInfo['audio']['encoder'] = 'BONK v0.4'; + } + break; + + case "\x00".'INFO': + $ThisFileInfo['audio']['encoder'] = 'Extended BONK v0.5'; + break; + + default: + break 2; + } + $BonkTagName = substr($TagHeaderTest, 1, 4); + $thisfile_bonk[$BonkTagName]['size'] = $thisfile_bonk['dataend'] - $thisfile_bonk['dataoffset']; + $thisfile_bonk[$BonkTagName]['offset'] = $thisfile_bonk['dataoffset']; + $this->HandleBonkTags($fd, $BonkTagName, $ThisFileInfo); + + } while (true); + } + + // parse META block for v0.6 - v0.8 + if (empty($thisfile_bonk['INFO']) && isset($thisfile_bonk['META']['tags']['info'])) { + fseek($fd, $thisfile_bonk['META']['tags']['info'], SEEK_SET); + $TagHeaderTest = fread($fd, 5); + if ($TagHeaderTest == "\x00".'INFO') { + $ThisFileInfo['audio']['encoder'] = 'Extended BONK v0.6 - v0.8'; + + $BonkTagName = substr($TagHeaderTest, 1, 4); + $thisfile_bonk[$BonkTagName]['size'] = $thisfile_bonk['dataend'] - $thisfile_bonk['dataoffset']; + $thisfile_bonk[$BonkTagName]['offset'] = $thisfile_bonk['dataoffset']; + $this->HandleBonkTags($fd, $BonkTagName, $ThisFileInfo); + } + } + + if (empty($ThisFileInfo['audio']['encoder'])) { + $ThisFileInfo['audio']['encoder'] = 'Extended BONK v0.9+'; + } + if (empty($thisfile_bonk['BONK'])) { + unset($ThisFileInfo['bonk']); + } + return true; + + } + + function HandleBonkTags(&$fd, &$BonkTagName, &$ThisFileInfo) { + + switch ($BonkTagName) { + case 'BONK': + // shortcut + $thisfile_bonk_BONK = &$ThisFileInfo['bonk']['BONK']; + + $BonkData = "\x00".'BONK'.fread($fd, 17); + $thisfile_bonk_BONK['version'] = getid3_lib::LittleEndian2Int(substr($BonkData, 5, 1)); + $thisfile_bonk_BONK['number_samples'] = getid3_lib::LittleEndian2Int(substr($BonkData, 6, 4)); + $thisfile_bonk_BONK['sample_rate'] = getid3_lib::LittleEndian2Int(substr($BonkData, 10, 4)); + + $thisfile_bonk_BONK['channels'] = getid3_lib::LittleEndian2Int(substr($BonkData, 14, 1)); + $thisfile_bonk_BONK['lossless'] = (bool) getid3_lib::LittleEndian2Int(substr($BonkData, 15, 1)); + $thisfile_bonk_BONK['joint_stereo'] = (bool) getid3_lib::LittleEndian2Int(substr($BonkData, 16, 1)); + $thisfile_bonk_BONK['number_taps'] = getid3_lib::LittleEndian2Int(substr($BonkData, 17, 2)); + $thisfile_bonk_BONK['downsampling_ratio'] = getid3_lib::LittleEndian2Int(substr($BonkData, 19, 1)); + $thisfile_bonk_BONK['samples_per_packet'] = getid3_lib::LittleEndian2Int(substr($BonkData, 20, 2)); + + $ThisFileInfo['avdataoffset'] = $thisfile_bonk_BONK['offset'] + 5 + 17; + $ThisFileInfo['avdataend'] = $thisfile_bonk_BONK['offset'] + $thisfile_bonk_BONK['size']; + + $ThisFileInfo['fileformat'] = 'bonk'; + $ThisFileInfo['audio']['dataformat'] = 'bonk'; + $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; // assumed + $ThisFileInfo['audio']['channels'] = $thisfile_bonk_BONK['channels']; + $ThisFileInfo['audio']['sample_rate'] = $thisfile_bonk_BONK['sample_rate']; + $ThisFileInfo['audio']['channelmode'] = ($thisfile_bonk_BONK['joint_stereo'] ? 'joint stereo' : 'stereo'); + $ThisFileInfo['audio']['lossless'] = $thisfile_bonk_BONK['lossless']; + $ThisFileInfo['audio']['codec'] = 'bonk'; + + $ThisFileInfo['playtime_seconds'] = $thisfile_bonk_BONK['number_samples'] / ($thisfile_bonk_BONK['sample_rate'] * $thisfile_bonk_BONK['channels']); + if ($ThisFileInfo['playtime_seconds'] > 0) { + $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['bonk']['dataend'] - $ThisFileInfo['bonk']['dataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; + } + break; + + case 'INFO': + // shortcut + $thisfile_bonk_INFO = &$ThisFileInfo['bonk']['INFO']; + + $thisfile_bonk_INFO['version'] = getid3_lib::LittleEndian2Int(fread($fd, 1)); + $thisfile_bonk_INFO['entries_count'] = 0; + $NextInfoDataPair = fread($fd, 5); + if (!$this->BonkIsValidTagName(substr($NextInfoDataPair, 1, 4))) { + while (!feof($fd)) { + //$CurrentSeekInfo['offset'] = getid3_lib::LittleEndian2Int(substr($NextInfoDataPair, 0, 4)); + //$CurrentSeekInfo['nextbit'] = getid3_lib::LittleEndian2Int(substr($NextInfoDataPair, 4, 1)); + //$thisfile_bonk_INFO[] = $CurrentSeekInfo; + + $NextInfoDataPair = fread($fd, 5); + if ($this->BonkIsValidTagName(substr($NextInfoDataPair, 1, 4))) { + fseek($fd, -5, SEEK_CUR); + break; + } + $thisfile_bonk_INFO['entries_count']++; + } + } + break; + + case 'META': + $BonkData = "\x00".'META'.fread($fd, $ThisFileInfo['bonk']['META']['size'] - 5); + $ThisFileInfo['bonk']['META']['version'] = getid3_lib::LittleEndian2Int(substr($BonkData, 5, 1)); + + $MetaTagEntries = floor(((strlen($BonkData) - 8) - 6) / 8); // BonkData - xxxxmeta - ØMETA + $offset = 6; + for ($i = 0; $i < $MetaTagEntries; $i++) { + $MetaEntryTagName = substr($BonkData, $offset, 4); + $offset += 4; + $MetaEntryTagOffset = getid3_lib::LittleEndian2Int(substr($BonkData, $offset, 4)); + $offset += 4; + $ThisFileInfo['bonk']['META']['tags'][$MetaEntryTagName] = $MetaEntryTagOffset; + } + break; + + case ' ID3': + $ThisFileInfo['audio']['encoder'] = 'Extended BONK v0.9+'; + + // ID3v2 checking is optional + if (class_exists('getid3_id3v2')) { + $ThisFileInfo['bonk'][' ID3']['valid'] = new getid3_id3v2($fd, $ThisFileInfo, $ThisFileInfo['bonk'][' ID3']['offset'] + 2); + } + break; + + default: + $ThisFileInfo['warning'][] = 'Unexpected Bonk tag "'.$BonkTagName.'" at offset '.$ThisFileInfo['bonk'][$BonkTagName]['offset']; + break; + + } + } + + function BonkIsValidTagName($PossibleBonkTag, $ignorecase=false) { + static $BonkIsValidTagName = array('BONK', 'INFO', ' ID3', 'META'); + foreach ($BonkIsValidTagName as $validtagname) { + if ($validtagname == $PossibleBonkTag) { + return true; + } elseif ($ignorecase && (strtolower($validtagname) == strtolower($PossibleBonkTag))) { + return true; + } + } + return false; + } + +} + + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.audio.flac.php b/includes/getid3/getid3/module.audio.flac.php new file mode 100644 index 0000000..573ed98 --- /dev/null +++ b/includes/getid3/getid3/module.audio.flac.php @@ -0,0 +1,309 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio.flac.php // +// module for analyzing FLAC and OggFLAC audio files // +// dependencies: module.audio.ogg.php // +// /// +///////////////////////////////////////////////////////////////// + + +getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ogg.php', __FILE__, true); + +class getid3_flac +{ + + function getid3_flac(&$fd, &$ThisFileInfo) { + // http://flac.sourceforge.net/format.html + + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + $StreamMarker = fread($fd, 4); + if ($StreamMarker != 'fLaC') { + $ThisFileInfo['error'][] = 'Expecting "fLaC" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$StreamMarker.'"'; + return false; + } + $ThisFileInfo['fileformat'] = 'flac'; + $ThisFileInfo['audio']['dataformat'] = 'flac'; + $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; + $ThisFileInfo['audio']['lossless'] = true; + + return getid3_flac::FLACparseMETAdata($fd, $ThisFileInfo); + } + + + function FLACparseMETAdata(&$fd, &$ThisFileInfo) { + + do { + $METAdataBlockOffset = ftell($fd); + $METAdataBlockHeader = fread($fd, 4); + $METAdataLastBlockFlag = (bool) (getid3_lib::BigEndian2Int(substr($METAdataBlockHeader, 0, 1)) & 0x80); + $METAdataBlockType = getid3_lib::BigEndian2Int(substr($METAdataBlockHeader, 0, 1)) & 0x7F; + $METAdataBlockLength = getid3_lib::BigEndian2Int(substr($METAdataBlockHeader, 1, 3)); + $METAdataBlockTypeText = getid3_flac::FLACmetaBlockTypeLookup($METAdataBlockType); + + if ($METAdataBlockLength < 0) { + $ThisFileInfo['error'][] = 'corrupt or invalid METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$METAdataBlockType.') at offset '.$METAdataBlockOffset; + break; + } + + $ThisFileInfo['flac'][$METAdataBlockTypeText]['raw'] = array(); + $ThisFileInfo_flac_METAdataBlockTypeText_raw = &$ThisFileInfo['flac'][$METAdataBlockTypeText]['raw']; + + $ThisFileInfo_flac_METAdataBlockTypeText_raw['offset'] = $METAdataBlockOffset; + $ThisFileInfo_flac_METAdataBlockTypeText_raw['last_meta_block'] = $METAdataLastBlockFlag; + $ThisFileInfo_flac_METAdataBlockTypeText_raw['block_type'] = $METAdataBlockType; + $ThisFileInfo_flac_METAdataBlockTypeText_raw['block_type_text'] = $METAdataBlockTypeText; + $ThisFileInfo_flac_METAdataBlockTypeText_raw['block_length'] = $METAdataBlockLength; + $ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'] = @fread($fd, $METAdataBlockLength); + $ThisFileInfo['avdataoffset'] = ftell($fd); + + switch ($METAdataBlockTypeText) { + + case 'STREAMINFO': + if (!getid3_flac::FLACparseSTREAMINFO($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) { + return false; + } + break; + + case 'PADDING': + // ignore + break; + + case 'APPLICATION': + if (!getid3_flac::FLACparseAPPLICATION($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) { + return false; + } + break; + + case 'SEEKTABLE': + if (!getid3_flac::FLACparseSEEKTABLE($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) { + return false; + } + break; + + case 'VORBIS_COMMENT': + $OldOffset = ftell($fd); + fseek($fd, 0 - $METAdataBlockLength, SEEK_CUR); + getid3_ogg::ParseVorbisCommentsFilepointer($fd, $ThisFileInfo); + fseek($fd, $OldOffset, SEEK_SET); + break; + + case 'CUESHEET': + if (!getid3_flac::FLACparseCUESHEET($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) { + return false; + } + break; + + default: + $ThisFileInfo['warning'][] = 'Unhandled METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$METAdataBlockType.') at offset '.$METAdataBlockOffset; + break; + } + + } while ($METAdataLastBlockFlag === false); + + + if (isset($ThisFileInfo['flac']['STREAMINFO'])) { + $ThisFileInfo['flac']['compressed_audio_bytes'] = $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']; + $ThisFileInfo['flac']['uncompressed_audio_bytes'] = $ThisFileInfo['flac']['STREAMINFO']['samples_stream'] * $ThisFileInfo['flac']['STREAMINFO']['channels'] * ($ThisFileInfo['flac']['STREAMINFO']['bits_per_sample'] / 8); + if ($ThisFileInfo['flac']['uncompressed_audio_bytes'] == 0) { + $ThisFileInfo['error'][] = 'Corrupt FLAC file: uncompressed_audio_bytes == zero'; + return false; + } + $ThisFileInfo['flac']['compression_ratio'] = $ThisFileInfo['flac']['compressed_audio_bytes'] / $ThisFileInfo['flac']['uncompressed_audio_bytes']; + } + + // set md5_data_source - built into flac 0.5+ + if (isset($ThisFileInfo['flac']['STREAMINFO']['audio_signature'])) { + + if ($ThisFileInfo['flac']['STREAMINFO']['audio_signature'] === str_repeat("\x00", 16)) { + + $ThisFileInfo['warning'][] = 'FLAC STREAMINFO.audio_signature is null (known issue with libOggFLAC)'; + + } else { + + $ThisFileInfo['md5_data_source'] = ''; + $md5 = $ThisFileInfo['flac']['STREAMINFO']['audio_signature']; + for ($i = 0; $i < strlen($md5); $i++) { + $ThisFileInfo['md5_data_source'] .= str_pad(dechex(ord($md5{$i})), 2, '00', STR_PAD_LEFT); + } + if (!preg_match('/^[0-9a-f]{32}$/', $ThisFileInfo['md5_data_source'])) { + unset($ThisFileInfo['md5_data_source']); + } + + } + + } + + $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['flac']['STREAMINFO']['bits_per_sample']; + if ($ThisFileInfo['audio']['bits_per_sample'] == 8) { + // special case + // must invert sign bit on all data bytes before MD5'ing to match FLAC's calculated value + // MD5sum calculates on unsigned bytes, but FLAC calculated MD5 on 8-bit audio data as signed + $ThisFileInfo['warning'][] = 'FLAC calculates MD5 data strangely on 8-bit audio, so the stored md5_data_source value will not match the decoded WAV file'; + } + if (!empty($ThisFileInfo['ogg']['vendor'])) { + $ThisFileInfo['audio']['encoder'] = $ThisFileInfo['ogg']['vendor']; + } + + return true; + } + + function FLACmetaBlockTypeLookup($blocktype) { + static $FLACmetaBlockTypeLookup = array(); + if (empty($FLACmetaBlockTypeLookup)) { + $FLACmetaBlockTypeLookup[0] = 'STREAMINFO'; + $FLACmetaBlockTypeLookup[1] = 'PADDING'; + $FLACmetaBlockTypeLookup[2] = 'APPLICATION'; + $FLACmetaBlockTypeLookup[3] = 'SEEKTABLE'; + $FLACmetaBlockTypeLookup[4] = 'VORBIS_COMMENT'; + $FLACmetaBlockTypeLookup[5] = 'CUESHEET'; + } + return (isset($FLACmetaBlockTypeLookup[$blocktype]) ? $FLACmetaBlockTypeLookup[$blocktype] : 'reserved'); + } + + function FLACapplicationIDLookup($applicationid) { + static $FLACapplicationIDLookup = array(); + if (empty($FLACapplicationIDLookup)) { + // http://flac.sourceforge.net/id.html + $FLACapplicationIDLookup[0x46746F6C] = 'flac-tools'; // 'Ftol' + $FLACapplicationIDLookup[0x46746F6C] = 'Sound Font FLAC'; // 'SFFL' + } + return (isset($FLACapplicationIDLookup[$applicationid]) ? $FLACapplicationIDLookup[$applicationid] : 'reserved'); + } + + function FLACparseSTREAMINFO($METAdataBlockData, &$ThisFileInfo) { + $offset = 0; + $ThisFileInfo['flac']['STREAMINFO']['min_block_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 2)); + $offset += 2; + $ThisFileInfo['flac']['STREAMINFO']['max_block_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 2)); + $offset += 2; + $ThisFileInfo['flac']['STREAMINFO']['min_frame_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 3)); + $offset += 3; + $ThisFileInfo['flac']['STREAMINFO']['max_frame_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 3)); + $offset += 3; + + $SampleRateChannelsSampleBitsStreamSamples = getid3_lib::BigEndian2Bin(substr($METAdataBlockData, $offset, 8)); + $ThisFileInfo['flac']['STREAMINFO']['sample_rate'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 0, 20)); + $ThisFileInfo['flac']['STREAMINFO']['channels'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 20, 3)) + 1; + $ThisFileInfo['flac']['STREAMINFO']['bits_per_sample'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 23, 5)) + 1; + $ThisFileInfo['flac']['STREAMINFO']['samples_stream'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 28, 36)); + $offset += 8; + + $ThisFileInfo['flac']['STREAMINFO']['audio_signature'] = substr($METAdataBlockData, $offset, 16); + $offset += 16; + + if (!empty($ThisFileInfo['flac']['STREAMINFO']['sample_rate'])) { + + $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; + $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['flac']['STREAMINFO']['sample_rate']; + $ThisFileInfo['audio']['channels'] = $ThisFileInfo['flac']['STREAMINFO']['channels']; + $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['flac']['STREAMINFO']['bits_per_sample']; + $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['flac']['STREAMINFO']['samples_stream'] / $ThisFileInfo['flac']['STREAMINFO']['sample_rate']; + $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; + + } else { + + $ThisFileInfo['error'][] = 'Corrupt METAdata block: STREAMINFO'; + return false; + + } + return true; + } + + + function FLACparseAPPLICATION($METAdataBlockData, &$ThisFileInfo) { + $offset = 0; + $ApplicationID = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 4)); + $offset += 4; + $ThisFileInfo['flac']['APPLICATION'][$ApplicationID]['name'] = getid3_flac::FLACapplicationIDLookup($ApplicationID); + $ThisFileInfo['flac']['APPLICATION'][$ApplicationID]['data'] = substr($METAdataBlockData, $offset); + $offset = $METAdataBlockLength; + + return true; + } + + + function FLACparseSEEKTABLE($METAdataBlockData, &$ThisFileInfo) { + $offset = 0; + $METAdataBlockLength = strlen($METAdataBlockData); + $placeholderpattern = str_repeat("\xFF", 8); + while ($offset < $METAdataBlockLength) { + $SampleNumberString = substr($METAdataBlockData, $offset, 8); + $offset += 8; + if ($SampleNumberString == $placeholderpattern) { + + // placeholder point + @$ThisFileInfo['flac']['SEEKTABLE']['placeholders']++; + $offset += 10; + + } else { + + $SampleNumber = getid3_lib::BigEndian2Int($SampleNumberString); + $ThisFileInfo['flac']['SEEKTABLE'][$SampleNumber]['offset'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 8)); + $offset += 8; + $ThisFileInfo['flac']['SEEKTABLE'][$SampleNumber]['samples'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 2)); + $offset += 2; + + } + } + return true; + } + + function FLACparseCUESHEET($METAdataBlockData, &$ThisFileInfo) { + $offset = 0; + $ThisFileInfo['flac']['CUESHEET']['media_catalog_number'] = trim(substr($METAdataBlockData, $offset, 128), "\0"); + $offset += 128; + $ThisFileInfo['flac']['CUESHEET']['lead_in_samples'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 8)); + $offset += 8; + $ThisFileInfo['flac']['CUESHEET']['flags']['is_cd'] = (bool) (getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1)) & 0x80); + $offset += 1; + + $offset += 258; // reserved + + $ThisFileInfo['flac']['CUESHEET']['number_tracks'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1)); + $offset += 1; + + for ($track = 0; $track < $ThisFileInfo['flac']['CUESHEET']['number_tracks']; $track++) { + $TrackSampleOffset = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 8)); + $offset += 8; + $TrackNumber = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1)); + $offset += 1; + + $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['sample_offset'] = $TrackSampleOffset; + + $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['isrc'] = substr($METAdataBlockData, $offset, 12); + $offset += 12; + + $TrackFlagsRaw = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1)); + $offset += 1; + $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['flags']['is_audio'] = (bool) ($TrackFlagsRaw & 0x80); + $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['flags']['pre_emphasis'] = (bool) ($TrackFlagsRaw & 0x40); + + $offset += 13; // reserved + + $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['index_points'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1)); + $offset += 1; + + for ($index = 0; $index < $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['index_points']; $index++) { + $IndexSampleOffset = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 8)); + $offset += 8; + $IndexNumber = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1)); + $offset += 1; + + $offset += 3; // reserved + + $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['indexes'][$IndexNumber] = $IndexSampleOffset; + } + } + return true; + } + +} + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.audio.la.php b/includes/getid3/getid3/module.audio.la.php new file mode 100644 index 0000000..9f82aca --- /dev/null +++ b/includes/getid3/getid3/module.audio.la.php @@ -0,0 +1,227 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio.la.php // +// module for analyzing LA audio files // +// dependencies: module.audio.riff.php // +// /// +///////////////////////////////////////////////////////////////// + +getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true); + +class getid3_la +{ + + function getid3_la(&$fd, &$ThisFileInfo) { + $offset = 0; + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + $rawdata = fread($fd, GETID3_FREAD_BUFFER_SIZE); + + switch (substr($rawdata, $offset, 4)) { + case 'LA02': + case 'LA03': + case 'LA04': + $ThisFileInfo['fileformat'] = 'la'; + $ThisFileInfo['audio']['dataformat'] = 'la'; + $ThisFileInfo['audio']['lossless'] = true; + + $ThisFileInfo['la']['version_major'] = (int) substr($rawdata, $offset + 2, 1); + $ThisFileInfo['la']['version_minor'] = (int) substr($rawdata, $offset + 3, 1); + $ThisFileInfo['la']['version'] = (float) $ThisFileInfo['la']['version_major'] + ($ThisFileInfo['la']['version_minor'] / 10); + $offset += 4; + + $ThisFileInfo['la']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); + $offset += 4; + if ($ThisFileInfo['la']['uncompressed_size'] == 0) { + $ThisFileInfo['error'][] = 'Corrupt LA file: uncompressed_size == zero'; + return false; + } + + $WAVEchunk = substr($rawdata, $offset, 4); + if ($WAVEchunk !== 'WAVE') { + $ThisFileInfo['error'][] = 'Expected "WAVE" ('.getid3_lib::PrintHexBytes('WAVE').') at offset '.$offset.', found "'.$WAVEchunk.'" ('.getid3_lib::PrintHexBytes($WAVEchunk).') instead.'; + return false; + } + $offset += 4; + + $ThisFileInfo['la']['fmt_size'] = 24; + if ($ThisFileInfo['la']['version'] >= 0.3) { + + $ThisFileInfo['la']['fmt_size'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); + $ThisFileInfo['la']['header_size'] = 49 + $ThisFileInfo['la']['fmt_size'] - 24; + $offset += 4; + + } else { + + // version 0.2 didn't support additional data blocks + $ThisFileInfo['la']['header_size'] = 41; + + } + + $fmt_chunk = substr($rawdata, $offset, 4); + if ($fmt_chunk !== 'fmt ') { + $ThisFileInfo['error'][] = 'Expected "fmt " ('.getid3_lib::PrintHexBytes('fmt ').') at offset '.$offset.', found "'.$fmt_chunk.'" ('.getid3_lib::PrintHexBytes($fmt_chunk).') instead.'; + return false; + } + $offset += 4; + $fmt_size = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); + $offset += 4; + + $ThisFileInfo['la']['raw']['format'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2)); + $offset += 2; + + $ThisFileInfo['la']['channels'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2)); + $offset += 2; + if ($ThisFileInfo['la']['channels'] == 0) { + $ThisFileInfo['error'][] = 'Corrupt LA file: channels == zero'; + return false; + } + + $ThisFileInfo['la']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); + $offset += 4; + if ($ThisFileInfo['la']['sample_rate'] == 0) { + $ThisFileInfo['error'][] = 'Corrupt LA file: sample_rate == zero'; + return false; + } + + $ThisFileInfo['la']['bytes_per_second'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); + $offset += 4; + $ThisFileInfo['la']['bytes_per_sample'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2)); + $offset += 2; + $ThisFileInfo['la']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2)); + $offset += 2; + + $ThisFileInfo['la']['samples'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); + $offset += 4; + + $ThisFileInfo['la']['raw']['flags'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 1)); + $offset += 1; + $ThisFileInfo['la']['flags']['seekable'] = (bool) ($ThisFileInfo['la']['raw']['flags'] & 0x01); + if ($ThisFileInfo['la']['version'] >= 0.4) { + $ThisFileInfo['la']['flags']['high_compression'] = (bool) ($ThisFileInfo['la']['raw']['flags'] & 0x02); + } + + $ThisFileInfo['la']['original_crc'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); + $offset += 4; + + // mikeØbevin*de + // Basically, the blocksize/seekevery are 61440/19 in La0.4 and 73728/16 + // in earlier versions. A seekpoint is added every blocksize * seekevery + // samples, so 4 * int(totalSamples / (blockSize * seekEvery)) should + // give the number of bytes used for the seekpoints. Of course, if seeking + // is disabled, there are no seekpoints stored. + if ($ThisFileInfo['la']['version'] >= 0.4) { + $ThisFileInfo['la']['blocksize'] = 61440; + $ThisFileInfo['la']['seekevery'] = 19; + } else { + $ThisFileInfo['la']['blocksize'] = 73728; + $ThisFileInfo['la']['seekevery'] = 16; + } + + $ThisFileInfo['la']['seekpoint_count'] = 0; + if ($ThisFileInfo['la']['flags']['seekable']) { + $ThisFileInfo['la']['seekpoint_count'] = floor($ThisFileInfo['la']['samples'] / ($ThisFileInfo['la']['blocksize'] * $ThisFileInfo['la']['seekevery'])); + + for ($i = 0; $i < $ThisFileInfo['la']['seekpoint_count']; $i++) { + $ThisFileInfo['la']['seekpoints'][] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); + $offset += 4; + } + } + + if ($ThisFileInfo['la']['version'] >= 0.3) { + + // Following the main header information, the program outputs all of the + // seekpoints. Following these is what I called the 'footer start', + // i.e. the position immediately after the La audio data is finished. + $ThisFileInfo['la']['footerstart'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4)); + $offset += 4; + + if ($ThisFileInfo['la']['footerstart'] > $ThisFileInfo['filesize']) { + $ThisFileInfo['warning'][] = 'FooterStart value points to offset '.$ThisFileInfo['la']['footerstart'].' which is beyond end-of-file ('.$ThisFileInfo['filesize'].')'; + $ThisFileInfo['la']['footerstart'] = $ThisFileInfo['filesize']; + } + + } else { + + // La v0.2 didn't have FooterStart value + $ThisFileInfo['la']['footerstart'] = $ThisFileInfo['avdataend']; + + } + + if ($ThisFileInfo['la']['footerstart'] < $ThisFileInfo['avdataend']) { + if ($RIFFtempfilename = tempnam('*', 'id3')) { + if ($RIFF_fp = fopen($RIFFtempfilename, 'w+b')) { + $RIFFdata = 'WAVE'; + if ($ThisFileInfo['la']['version'] == 0.2) { + $RIFFdata .= substr($rawdata, 12, 24); + } else { + $RIFFdata .= substr($rawdata, 16, 24); + } + if ($ThisFileInfo['la']['footerstart'] < $ThisFileInfo['avdataend']) { + fseek($fd, $ThisFileInfo['la']['footerstart'], SEEK_SET); + $RIFFdata .= fread($fd, $ThisFileInfo['avdataend'] - $ThisFileInfo['la']['footerstart']); + } + $RIFFdata = 'RIFF'.getid3_lib::LittleEndian2String(strlen($RIFFdata), 4, false).$RIFFdata; + fwrite($RIFF_fp, $RIFFdata, strlen($RIFFdata)); + $dummy = $ThisFileInfo; + $dummy['filesize'] = strlen($RIFFdata); + $dummy['avdataoffset'] = 0; + $dummy['avdataend'] = $dummy['filesize']; + + $riff = new getid3_riff($RIFF_fp, $dummy); + if (empty($dummy['error'])) { + $ThisFileInfo['riff'] = $dummy['riff']; + } else { + $ThisFileInfo['warning'][] = 'Error parsing RIFF portion of La file: '.implode($dummy['error']); + } + unset($dummy); + fclose($RIFF_fp); + } + unlink($RIFFtempfilename); + } + } + + // $ThisFileInfo['avdataoffset'] should be zero to begin with, but just in case it's not, include the addition anyway + $ThisFileInfo['avdataend'] = $ThisFileInfo['avdataoffset'] + $ThisFileInfo['la']['footerstart']; + $ThisFileInfo['avdataoffset'] = $ThisFileInfo['avdataoffset'] + $offset; + + //$ThisFileInfo['la']['codec'] = RIFFwFormatTagLookup($ThisFileInfo['la']['raw']['format']); + $ThisFileInfo['la']['compression_ratio'] = (float) (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $ThisFileInfo['la']['uncompressed_size']); + $ThisFileInfo['playtime_seconds'] = (float) ($ThisFileInfo['la']['samples'] / $ThisFileInfo['la']['sample_rate']) / $ThisFileInfo['la']['channels']; + if ($ThisFileInfo['playtime_seconds'] == 0) { + $ThisFileInfo['error'][] = 'Corrupt LA file: playtime_seconds == zero'; + return false; + } + + $ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / $ThisFileInfo['playtime_seconds']; + //$ThisFileInfo['audio']['codec'] = $ThisFileInfo['la']['codec']; + $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['la']['bits_per_sample']; + break; + + default: + if (substr($rawdata, $offset, 2) == 'LA') { + $ThisFileInfo['error'][] = 'This version of getID3() (v'.GETID3_VERSION.') doesn\'t support LA version '.substr($rawdata, $offset + 2, 1).'.'.substr($rawdata, $offset + 3, 1).' which this appears to be - check http://getid3.sourceforge.net for updates.'; + } else { + $ThisFileInfo['error'][] = 'Not a LA (Lossless-Audio) file'; + } + return false; + break; + } + + $ThisFileInfo['audio']['channels'] = $ThisFileInfo['la']['channels']; + $ThisFileInfo['audio']['sample_rate'] = (int) $ThisFileInfo['la']['sample_rate']; + $ThisFileInfo['audio']['encoder'] = 'LA v'.$ThisFileInfo['la']['version']; + + return true; + } + +} + + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.audio.lpac.php b/includes/getid3/getid3/module.audio.lpac.php new file mode 100644 index 0000000..64a87d0 --- /dev/null +++ b/includes/getid3/getid3/module.audio.lpac.php @@ -0,0 +1,125 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio.lpac.php // +// module for analyzing LPAC Audio files // +// dependencies: module.audio-video.riff.php // +// /// +///////////////////////////////////////////////////////////////// + +getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true); + +class getid3_lpac +{ + + function getid3_lpac(&$fd, &$ThisFileInfo) { + + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + $LPACheader = fread($fd, 14); + if (substr($LPACheader, 0, 4) != 'LPAC') { + $ThisFileInfo['error'][] = 'Expected "LPAC" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$StreamMarker.'"'; + return false; + } + $ThisFileInfo['avdataoffset'] += 14; + + $ThisFileInfo['fileformat'] = 'lpac'; + $ThisFileInfo['audio']['dataformat'] = 'lpac'; + $ThisFileInfo['audio']['lossless'] = true; + $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; + + $ThisFileInfo['lpac']['file_version'] = getid3_lib::BigEndian2Int(substr($LPACheader, 4, 1)); + $flags['audio_type'] = getid3_lib::BigEndian2Int(substr($LPACheader, 5, 1)); + $ThisFileInfo['lpac']['total_samples']= getid3_lib::BigEndian2Int(substr($LPACheader, 6, 4)); + $flags['parameters'] = getid3_lib::BigEndian2Int(substr($LPACheader, 10, 4)); + + $ThisFileInfo['lpac']['flags']['is_wave'] = (bool) ($flags['audio_type'] & 0x40); + $ThisFileInfo['lpac']['flags']['stereo'] = (bool) ($flags['audio_type'] & 0x04); + $ThisFileInfo['lpac']['flags']['24_bit'] = (bool) ($flags['audio_type'] & 0x02); + $ThisFileInfo['lpac']['flags']['16_bit'] = (bool) ($flags['audio_type'] & 0x01); + + if ($ThisFileInfo['lpac']['flags']['24_bit'] && $ThisFileInfo['lpac']['flags']['16_bit']) { + $ThisFileInfo['warning'][] = '24-bit and 16-bit flags cannot both be set'; + } + + $ThisFileInfo['lpac']['flags']['fast_compress'] = (bool) ($flags['parameters'] & 0x40000000); + $ThisFileInfo['lpac']['flags']['random_access'] = (bool) ($flags['parameters'] & 0x08000000); + $ThisFileInfo['lpac']['block_length'] = pow(2, (($flags['parameters'] & 0x07000000) >> 24)) * 256; + $ThisFileInfo['lpac']['flags']['adaptive_prediction_order'] = (bool) ($flags['parameters'] & 0x00800000); + $ThisFileInfo['lpac']['flags']['adaptive_quantization'] = (bool) ($flags['parameters'] & 0x00400000); + $ThisFileInfo['lpac']['flags']['joint_stereo'] = (bool) ($flags['parameters'] & 0x00040000); + $ThisFileInfo['lpac']['quantization'] = ($flags['parameters'] & 0x00001F00) >> 8; + $ThisFileInfo['lpac']['max_prediction_order'] = ($flags['parameters'] & 0x0000003F); + + if ($ThisFileInfo['lpac']['flags']['fast_compress'] && ($ThisFileInfo['lpac']['max_prediction_order'] != 3)) { + $ThisFileInfo['warning'][] = 'max_prediction_order expected to be "3" if fast_compress is true, actual value is "'.$ThisFileInfo['lpac']['max_prediction_order'].'"'; + } + switch ($ThisFileInfo['lpac']['file_version']) { + case 6: + if ($ThisFileInfo['lpac']['flags']['adaptive_quantization']) { + $ThisFileInfo['warning'][] = 'adaptive_quantization expected to be false in LPAC file stucture v6, actually true'; + } + if ($ThisFileInfo['lpac']['quantization'] != 20) { + $ThisFileInfo['warning'][] = 'Quantization expected to be 20 in LPAC file stucture v6, actually '.$ThisFileInfo['lpac']['flags']['Q']; + } + break; + + default: + //$ThisFileInfo['warning'][] = 'This version of getID3() only supports LPAC file format version 6, this file is version '.$ThisFileInfo['lpac']['file_version'].' - please report to info@getid3.org'; + break; + } + + $dummy = $ThisFileInfo; + $riff = new getid3_riff($fd, $dummy); + $ThisFileInfo['avdataoffset'] = $dummy['avdataoffset']; + $ThisFileInfo['riff'] = $dummy['riff']; + $ThisFileInfo['error'] = $dummy['error']; + $ThisFileInfo['warning'] = $dummy['warning']; + $ThisFileInfo['lpac']['comments']['comment'] = $dummy['comments']; + $ThisFileInfo['audio']['sample_rate'] = $dummy['audio']['sample_rate']; + + $ThisFileInfo['audio']['channels'] = ($ThisFileInfo['lpac']['flags']['stereo'] ? 2 : 1); + + if ($ThisFileInfo['lpac']['flags']['24_bit']) { + $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['riff']['audio'][0]['bits_per_sample']; + } elseif ($ThisFileInfo['lpac']['flags']['16_bit']) { + $ThisFileInfo['audio']['bits_per_sample'] = 16; + } else { + $ThisFileInfo['audio']['bits_per_sample'] = 8; + } + + if ($ThisFileInfo['lpac']['flags']['fast_compress']) { + // fast + $ThisFileInfo['audio']['encoder_options'] = '-1'; + } else { + switch ($ThisFileInfo['lpac']['max_prediction_order']) { + case 20: // simple + $ThisFileInfo['audio']['encoder_options'] = '-2'; + break; + case 30: // medium + $ThisFileInfo['audio']['encoder_options'] = '-3'; + break; + case 40: // high + $ThisFileInfo['audio']['encoder_options'] = '-4'; + break; + case 60: // extrahigh + $ThisFileInfo['audio']['encoder_options'] = '-5'; + break; + } + } + + $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['lpac']['total_samples'] / $ThisFileInfo['audio']['sample_rate']; + $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; + + return true; + } + +} + + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.audio.midi.php b/includes/getid3/getid3/module.audio.midi.php new file mode 100644 index 0000000..f72760d --- /dev/null +++ b/includes/getid3/getid3/module.audio.midi.php @@ -0,0 +1,520 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio.midi.php // +// module for Midi Audio files // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_midi +{ + + function getid3_midi(&$fd, &$ThisFileInfo, $scanwholefile=true) { + + // shortcut + $ThisFileInfo['midi']['raw'] = array(); + $thisfile_midi = &$ThisFileInfo['midi']; + $thisfile_midi_raw = &$thisfile_midi['raw']; + + $ThisFileInfo['fileformat'] = 'midi'; + $ThisFileInfo['audio']['dataformat'] = 'midi'; + + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + $MIDIdata = fread($fd, GETID3_FREAD_BUFFER_SIZE); + $offset = 0; + $MIDIheaderID = substr($MIDIdata, $offset, 4); // 'MThd' + if ($MIDIheaderID != 'MThd') { + $ThisFileInfo['error'][] = 'Expecting "MThd" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$MIDIheaderID.'"'; + unset($ThisFileInfo['fileformat']); + return false; + } + $offset += 4; + $thisfile_midi_raw['headersize'] = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 4)); + $offset += 4; + $thisfile_midi_raw['fileformat'] = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 2)); + $offset += 2; + $thisfile_midi_raw['tracks'] = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 2)); + $offset += 2; + $thisfile_midi_raw['ticksperqnote'] = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 2)); + $offset += 2; + + for ($i = 0; $i < $thisfile_midi_raw['tracks']; $i++) { + if ((strlen($MIDIdata) - $offset) < 8) { + $MIDIdata .= fread($fd, GETID3_FREAD_BUFFER_SIZE); + } + $trackID = substr($MIDIdata, $offset, 4); + $offset += 4; + if ($trackID == 'MTrk') { + $tracksize = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 4)); + $offset += 4; + // $thisfile_midi['tracks'][$i]['size'] = $tracksize; + $trackdataarray[$i] = substr($MIDIdata, $offset, $tracksize); + $offset += $tracksize; + } else { + $ThisFileInfo['error'][] = 'Expecting "MTrk" at '.$offset.', found '.$trackID.' instead'; + return false; + } + } + + if (!isset($trackdataarray) || !is_array($trackdataarray)) { + $ThisFileInfo['error'][] = 'Cannot find MIDI track information'; + unset($thisfile_midi); + unset($ThisFileInfo['fileformat']); + return false; + } + + if ($scanwholefile) { // this can take quite a long time, so have the option to bypass it if speed is very important + $thisfile_midi['totalticks'] = 0; + $ThisFileInfo['playtime_seconds'] = 0; + $CurrentMicroSecondsPerBeat = 500000; // 120 beats per minute; 60,000,000 microseconds per minute -> 500,000 microseconds per beat + $CurrentBeatsPerMinute = 120; // 120 beats per minute; 60,000,000 microseconds per minute -> 500,000 microseconds per beat + + foreach ($trackdataarray as $tracknumber => $trackdata) { + + $eventsoffset = 0; + $LastIssuedMIDIcommand = 0; + $LastIssuedMIDIchannel = 0; + $CumulativeDeltaTime = 0; + $TicksAtCurrentBPM = 0; + while ($eventsoffset < strlen($trackdata)) { + $eventid = 0; + if (isset($MIDIevents[$tracknumber]) && is_array($MIDIevents[$tracknumber])) { + $eventid = count($MIDIevents[$tracknumber]); + } + $deltatime = 0; + for ($i = 0; $i < 4; $i++) { + $deltatimebyte = ord(substr($trackdata, $eventsoffset++, 1)); + $deltatime = ($deltatime << 7) + ($deltatimebyte & 0x7F); + if ($deltatimebyte & 0x80) { + // another byte follows + } else { + break; + } + } + $CumulativeDeltaTime += $deltatime; + $TicksAtCurrentBPM += $deltatime; + $MIDIevents[$tracknumber][$eventid]['deltatime'] = $deltatime; + $MIDI_event_channel = ord(substr($trackdata, $eventsoffset++, 1)); + if ($MIDI_event_channel & 0x80) { + // OK, normal event - MIDI command has MSB set + $LastIssuedMIDIcommand = $MIDI_event_channel >> 4; + $LastIssuedMIDIchannel = $MIDI_event_channel & 0x0F; + } else { + // running event - assume last command + $eventsoffset--; + } + $MIDIevents[$tracknumber][$eventid]['eventid'] = $LastIssuedMIDIcommand; + $MIDIevents[$tracknumber][$eventid]['channel'] = $LastIssuedMIDIchannel; + if ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x08) { // Note off (key is released) + + $notenumber = ord(substr($trackdata, $eventsoffset++, 1)); + $velocity = ord(substr($trackdata, $eventsoffset++, 1)); + + } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x09) { // Note on (key is pressed) + + $notenumber = ord(substr($trackdata, $eventsoffset++, 1)); + $velocity = ord(substr($trackdata, $eventsoffset++, 1)); + + } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0A) { // Key after-touch + + $notenumber = ord(substr($trackdata, $eventsoffset++, 1)); + $velocity = ord(substr($trackdata, $eventsoffset++, 1)); + + } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0B) { // Control Change + + $controllernum = ord(substr($trackdata, $eventsoffset++, 1)); + $newvalue = ord(substr($trackdata, $eventsoffset++, 1)); + + } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0C) { // Program (patch) change + + $newprogramnum = ord(substr($trackdata, $eventsoffset++, 1)); + + $thisfile_midi_raw['track'][$tracknumber]['instrumentid'] = $newprogramnum; + if ($tracknumber == 10) { + $thisfile_midi_raw['track'][$tracknumber]['instrument'] = $this->GeneralMIDIpercussionLookup($newprogramnum); + } else { + $thisfile_midi_raw['track'][$tracknumber]['instrument'] = $this->GeneralMIDIinstrumentLookup($newprogramnum); + } + + } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0D) { // Channel after-touch + + $channelnumber = ord(substr($trackdata, $eventsoffset++, 1)); + + } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0E) { // Pitch wheel change (2000H is normal or no change) + + $changeLSB = ord(substr($trackdata, $eventsoffset++, 1)); + $changeMSB = ord(substr($trackdata, $eventsoffset++, 1)); + $pitchwheelchange = (($changeMSB & 0x7F) << 7) & ($changeLSB & 0x7F); + + } elseif (($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0F) && ($MIDIevents[$tracknumber][$eventid]['channel'] == 0x0F)) { + + $METAeventCommand = ord(substr($trackdata, $eventsoffset++, 1)); + $METAeventLength = ord(substr($trackdata, $eventsoffset++, 1)); + $METAeventData = substr($trackdata, $eventsoffset, $METAeventLength); + $eventsoffset += $METAeventLength; + switch ($METAeventCommand) { + case 0x00: // Set track sequence number + $track_sequence_number = getid3_lib::BigEndian2Int(substr($METAeventData, 0, $METAeventLength)); + //$thisfile_midi_raw['events'][$tracknumber][$eventid]['seqno'] = $track_sequence_number; + break; + + case 0x01: // Text: generic + $text_generic = substr($METAeventData, 0, $METAeventLength); + //$thisfile_midi_raw['events'][$tracknumber][$eventid]['text'] = $text_generic; + $thisfile_midi['comments']['comment'][] = $text_generic; + break; + + case 0x02: // Text: copyright + $text_copyright = substr($METAeventData, 0, $METAeventLength); + //$thisfile_midi_raw['events'][$tracknumber][$eventid]['copyright'] = $text_copyright; + $thisfile_midi['comments']['copyright'][] = $text_copyright; + break; + + case 0x03: // Text: track name + $text_trackname = substr($METAeventData, 0, $METAeventLength); + $thisfile_midi_raw['track'][$tracknumber]['name'] = $text_trackname; + break; + + case 0x04: // Text: track instrument name + $text_instrument = substr($METAeventData, 0, $METAeventLength); + //$thisfile_midi_raw['events'][$tracknumber][$eventid]['instrument'] = $text_instrument; + break; + + case 0x05: // Text: lyrics + $text_lyrics = substr($METAeventData, 0, $METAeventLength); + //$thisfile_midi_raw['events'][$tracknumber][$eventid]['lyrics'] = $text_lyrics; + if (!isset($thisfile_midi['lyrics'])) { + $thisfile_midi['lyrics'] = ''; + } + $thisfile_midi['lyrics'] .= $text_lyrics."\n"; + break; + + case 0x06: // Text: marker + $text_marker = substr($METAeventData, 0, $METAeventLength); + //$thisfile_midi_raw['events'][$tracknumber][$eventid]['marker'] = $text_marker; + break; + + case 0x07: // Text: cue point + $text_cuepoint = substr($METAeventData, 0, $METAeventLength); + //$thisfile_midi_raw['events'][$tracknumber][$eventid]['cuepoint'] = $text_cuepoint; + break; + + case 0x2F: // End Of Track + //$thisfile_midi_raw['events'][$tracknumber][$eventid]['EOT'] = $CumulativeDeltaTime; + break; + + case 0x51: // Tempo: microseconds / quarter note + $CurrentMicroSecondsPerBeat = getid3_lib::BigEndian2Int(substr($METAeventData, 0, $METAeventLength)); + if ($CurrentMicroSecondsPerBeat == 0) { + $ThisFileInfo['error'][] = 'Corrupt MIDI file: CurrentMicroSecondsPerBeat == zero'; + return false; + } + $thisfile_midi_raw['events'][$tracknumber][$CumulativeDeltaTime]['us_qnote'] = $CurrentMicroSecondsPerBeat; + $CurrentBeatsPerMinute = (1000000 / $CurrentMicroSecondsPerBeat) * 60; + $MicroSecondsPerQuarterNoteAfter[$CumulativeDeltaTime] = $CurrentMicroSecondsPerBeat; + $TicksAtCurrentBPM = 0; + break; + + case 0x58: // Time signature + $timesig_numerator = getid3_lib::BigEndian2Int($METAeventData{0}); + $timesig_denominator = pow(2, getid3_lib::BigEndian2Int($METAeventData{1})); // $02 -> x/4, $03 -> x/8, etc + $timesig_32inqnote = getid3_lib::BigEndian2Int($METAeventData{2}); // number of 32nd notes to the quarter note + //$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_32inqnote'] = $timesig_32inqnote; + //$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_numerator'] = $timesig_numerator; + //$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_denominator'] = $timesig_denominator; + //$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_text'] = $timesig_numerator.'/'.$timesig_denominator; + $thisfile_midi['timesignature'][] = $timesig_numerator.'/'.$timesig_denominator; + break; + + case 0x59: // Keysignature + $keysig_sharpsflats = getid3_lib::BigEndian2Int($METAeventData{0}); + if ($keysig_sharpsflats & 0x80) { + // (-7 -> 7 flats, 0 ->key of C, 7 -> 7 sharps) + $keysig_sharpsflats -= 256; + } + + $keysig_majorminor = getid3_lib::BigEndian2Int($METAeventData{1}); // 0 -> major, 1 -> minor + $keysigs = array(-7=>'Cb', -6=>'Gb', -5=>'Db', -4=>'Ab', -3=>'Eb', -2=>'Bb', -1=>'F', 0=>'C', 1=>'G', 2=>'D', 3=>'A', 4=>'E', 5=>'B', 6=>'F#', 7=>'C#'); + //$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_sharps'] = (($keysig_sharpsflats > 0) ? abs($keysig_sharpsflats) : 0); + //$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_flats'] = (($keysig_sharpsflats < 0) ? abs($keysig_sharpsflats) : 0); + //$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_minor'] = (bool) $keysig_majorminor; + //$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_text'] = $keysigs[$keysig_sharpsflats].' '.($thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_minor'] ? 'minor' : 'major'); + + // $keysigs[$keysig_sharpsflats] gets an int key (correct) - $keysigs["$keysig_sharpsflats"] gets a string key (incorrect) + $thisfile_midi['keysignature'][] = $keysigs[$keysig_sharpsflats].' '.((bool) $keysig_majorminor ? 'minor' : 'major'); + break; + + case 0x7F: // Sequencer specific information + $custom_data = substr($METAeventData, 0, $METAeventLength); + break; + + default: + $ThisFileInfo['warning'][] = 'Unhandled META Event Command: '.$METAeventCommand; + break; + } + + } else { + + $ThisFileInfo['warning'][] = 'Unhandled MIDI Event ID: '.$MIDIevents[$tracknumber][$eventid]['eventid'].' + Channel ID: '.$MIDIevents[$tracknumber][$eventid]['channel']; + + } + } + if (($tracknumber > 0) || (count($trackdataarray) == 1)) { + $thisfile_midi['totalticks'] = max($thisfile_midi['totalticks'], $CumulativeDeltaTime); + } + } + $previoustickoffset = null; + + ksort($MicroSecondsPerQuarterNoteAfter); + foreach ($MicroSecondsPerQuarterNoteAfter as $tickoffset => $microsecondsperbeat) { + if (is_null($previoustickoffset)) { + $prevmicrosecondsperbeat = $microsecondsperbeat; + $previoustickoffset = $tickoffset; + continue; + } + if ($thisfile_midi['totalticks'] > $tickoffset) { + + if ($thisfile_midi_raw['ticksperqnote'] == 0) { + $ThisFileInfo['error'][] = 'Corrupt MIDI file: ticksperqnote == zero'; + return false; + } + + $ThisFileInfo['playtime_seconds'] += (($tickoffset - $previoustickoffset) / $thisfile_midi_raw['ticksperqnote']) * ($prevmicrosecondsperbeat / 1000000); + + $prevmicrosecondsperbeat = $microsecondsperbeat; + $previoustickoffset = $tickoffset; + } + } + if ($thisfile_midi['totalticks'] > $previoustickoffset) { + + if ($thisfile_midi_raw['ticksperqnote'] == 0) { + $ThisFileInfo['error'][] = 'Corrupt MIDI file: ticksperqnote == zero'; + return false; + } + + $ThisFileInfo['playtime_seconds'] += (($thisfile_midi['totalticks'] - $previoustickoffset) / $thisfile_midi_raw['ticksperqnote']) * ($microsecondsperbeat / 1000000); + + } + } + + if ($ThisFileInfo['playtime_seconds'] > 0) { + $ThisFileInfo['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; + } + + if (!empty($thisfile_midi['lyrics'])) { + $thisfile_midi['comments']['lyrics'][] = $thisfile_midi['lyrics']; + } + + return true; + } + + function GeneralMIDIinstrumentLookup($instrumentid) { + + $begin = __LINE__; + + /** This is not a comment! + + 0 Acoustic Grand + 1 Bright Acoustic + 2 Electric Grand + 3 Honky-Tonk + 4 Electric Piano 1 + 5 Electric Piano 2 + 6 Harpsichord + 7 Clavier + 8 Celesta + 9 Glockenspiel + 10 Music Box + 11 Vibraphone + 12 Marimba + 13 Xylophone + 14 Tubular Bells + 15 Dulcimer + 16 Drawbar Organ + 17 Percussive Organ + 18 Rock Organ + 19 Church Organ + 20 Reed Organ + 21 Accordian + 22 Harmonica + 23 Tango Accordian + 24 Acoustic Guitar (nylon) + 25 Acoustic Guitar (steel) + 26 Electric Guitar (jazz) + 27 Electric Guitar (clean) + 28 Electric Guitar (muted) + 29 Overdriven Guitar + 30 Distortion Guitar + 31 Guitar Harmonics + 32 Acoustic Bass + 33 Electric Bass (finger) + 34 Electric Bass (pick) + 35 Fretless Bass + 36 Slap Bass 1 + 37 Slap Bass 2 + 38 Synth Bass 1 + 39 Synth Bass 2 + 40 Violin + 41 Viola + 42 Cello + 43 Contrabass + 44 Tremolo Strings + 45 Pizzicato Strings + 46 Orchestral Strings + 47 Timpani + 48 String Ensemble 1 + 49 String Ensemble 2 + 50 SynthStrings 1 + 51 SynthStrings 2 + 52 Choir Aahs + 53 Voice Oohs + 54 Synth Voice + 55 Orchestra Hit + 56 Trumpet + 57 Trombone + 58 Tuba + 59 Muted Trumpet + 60 French Horn + 61 Brass Section + 62 SynthBrass 1 + 63 SynthBrass 2 + 64 Soprano Sax + 65 Alto Sax + 66 Tenor Sax + 67 Baritone Sax + 68 Oboe + 69 English Horn + 70 Bassoon + 71 Clarinet + 72 Piccolo + 73 Flute + 74 Recorder + 75 Pan Flute + 76 Blown Bottle + 77 Shakuhachi + 78 Whistle + 79 Ocarina + 80 Lead 1 (square) + 81 Lead 2 (sawtooth) + 82 Lead 3 (calliope) + 83 Lead 4 (chiff) + 84 Lead 5 (charang) + 85 Lead 6 (voice) + 86 Lead 7 (fifths) + 87 Lead 8 (bass + lead) + 88 Pad 1 (new age) + 89 Pad 2 (warm) + 90 Pad 3 (polysynth) + 91 Pad 4 (choir) + 92 Pad 5 (bowed) + 93 Pad 6 (metallic) + 94 Pad 7 (halo) + 95 Pad 8 (sweep) + 96 FX 1 (rain) + 97 FX 2 (soundtrack) + 98 FX 3 (crystal) + 99 FX 4 (atmosphere) + 100 FX 5 (brightness) + 101 FX 6 (goblins) + 102 FX 7 (echoes) + 103 FX 8 (sci-fi) + 104 Sitar + 105 Banjo + 106 Shamisen + 107 Koto + 108 Kalimba + 109 Bagpipe + 110 Fiddle + 111 Shanai + 112 Tinkle Bell + 113 Agogo + 114 Steel Drums + 115 Woodblock + 116 Taiko Drum + 117 Melodic Tom + 118 Synth Drum + 119 Reverse Cymbal + 120 Guitar Fret Noise + 121 Breath Noise + 122 Seashore + 123 Bird Tweet + 124 Telephone Ring + 125 Helicopter + 126 Applause + 127 Gunshot + + */ + + return getid3_lib::EmbeddedLookup($instrumentid, $begin, __LINE__, __FILE__, 'GeneralMIDIinstrument'); + } + + function GeneralMIDIpercussionLookup($instrumentid) { + + $begin = __LINE__; + + /** This is not a comment! + + 35 Acoustic Bass Drum + 36 Bass Drum 1 + 37 Side Stick + 38 Acoustic Snare + 39 Hand Clap + 40 Electric Snare + 41 Low Floor Tom + 42 Closed Hi-Hat + 43 High Floor Tom + 44 Pedal Hi-Hat + 45 Low Tom + 46 Open Hi-Hat + 47 Low-Mid Tom + 48 Hi-Mid Tom + 49 Crash Cymbal 1 + 50 High Tom + 51 Ride Cymbal 1 + 52 Chinese Cymbal + 53 Ride Bell + 54 Tambourine + 55 Splash Cymbal + 56 Cowbell + 57 Crash Cymbal 2 + 59 Ride Cymbal 2 + 60 Hi Bongo + 61 Low Bongo + 62 Mute Hi Conga + 63 Open Hi Conga + 64 Low Conga + 65 High Timbale + 66 Low Timbale + 67 High Agogo + 68 Low Agogo + 69 Cabasa + 70 Maracas + 71 Short Whistle + 72 Long Whistle + 73 Short Guiro + 74 Long Guiro + 75 Claves + 76 Hi Wood Block + 77 Low Wood Block + 78 Mute Cuica + 79 Open Cuica + 80 Mute Triangle + 81 Open Triangle + + */ + + return getid3_lib::EmbeddedLookup($instrumentid, $begin, __LINE__, __FILE__, 'GeneralMIDIpercussion'); + } + +} + + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.audio.mod.php b/includes/getid3/getid3/module.audio.mod.php new file mode 100644 index 0000000..7f81ff3 --- /dev/null +++ b/includes/getid3/getid3/module.audio.mod.php @@ -0,0 +1,101 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio.mod.php // +// module for analyzing MOD Audio files // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_mod +{ + + // new combined constructor + function getid3_mod(&$fd, &$ThisFileInfo, $option) { + + if ($option === 'mod') { + $this->getMODheaderFilepointer($fd, $ThisFileInfo); + } + elseif ($option === 'xm') { + $this->getXMheaderFilepointer($fd, $ThisFileInfo); + } + elseif ($option === 'it') { + $this->getITheaderFilepointer($fd, $ThisFileInfo); + } + elseif ($option === 's3m') { + $this->getS3MheaderFilepointer($fd, $ThisFileInfo); + } + } + + + function getMODheaderFilepointer(&$fd, &$ThisFileInfo) { + + fseek($fd, $ThisFileInfo['avdataoffset'] + 1080); + $FormatID = fread($fd, 4); + if (!ereg('^(M.K.|[5-9]CHN|[1-3][0-9]CH)$', $FormatID)) { + $ThisFileInfo['error'][] = 'This is not a known type of MOD file'; + return false; + } + + $ThisFileInfo['fileformat'] = 'mod'; + + $ThisFileInfo['error'][] = 'MOD parsing not enabled in this version of getID3()'; + return false; + } + + function getXMheaderFilepointer(&$fd, &$ThisFileInfo) { + + fseek($fd, $ThisFileInfo['avdataoffset']); + $FormatID = fread($fd, 15); + if (!ereg('^Extended Module$', $FormatID)) { + $ThisFileInfo['error'][] = 'This is not a known type of XM-MOD file'; + return false; + } + + $ThisFileInfo['fileformat'] = 'xm'; + + $ThisFileInfo['error'][] = 'XM-MOD parsing not enabled in this version of getID3()'; + return false; + } + + function getS3MheaderFilepointer(&$fd, &$ThisFileInfo) { + + fseek($fd, $ThisFileInfo['avdataoffset'] + 44); + $FormatID = fread($fd, 4); + if (!ereg('^SCRM$', $FormatID)) { + $ThisFileInfo['error'][] = 'This is not a ScreamTracker MOD file'; + return false; + } + + $ThisFileInfo['fileformat'] = 's3m'; + + $ThisFileInfo['error'][] = 'ScreamTracker parsing not enabled in this version of getID3()'; + return false; + } + + function getITheaderFilepointer(&$fd, &$ThisFileInfo) { + + fseek($fd, $ThisFileInfo['avdataoffset']); + $FormatID = fread($fd, 4); + if (!ereg('^IMPM$', $FormatID)) { + $ThisFileInfo['error'][] = 'This is not an ImpulseTracker MOD file'; + return false; + } + + $ThisFileInfo['fileformat'] = 'it'; + + $ThisFileInfo['error'][] = 'ImpulseTracker parsing not enabled in this version of getID3()'; + return false; + } + +} + + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.audio.monkey.php b/includes/getid3/getid3/module.audio.monkey.php new file mode 100644 index 0000000..42382ad --- /dev/null +++ b/includes/getid3/getid3/module.audio.monkey.php @@ -0,0 +1,202 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio.monkey.php // +// module for analyzing Monkey's Audio files // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_monkey +{ + + function getid3_monkey(&$fd, &$ThisFileInfo) { + // based loosely on code from TMonkey by Jurgen Faul + // http://jfaul.de/atl or http://j-faul.virtualave.net/atl/atl.html + + $ThisFileInfo['fileformat'] = 'mac'; + $ThisFileInfo['audio']['dataformat'] = 'mac'; + $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; + $ThisFileInfo['audio']['lossless'] = true; + + $ThisFileInfo['monkeys_audio']['raw'] = array(); + $thisfile_monkeysaudio = &$ThisFileInfo['monkeys_audio']; + $thisfile_monkeysaudio_raw = &$thisfile_monkeysaudio['raw']; + + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + $MACheaderData = fread($fd, 74); + + $thisfile_monkeysaudio_raw['magic'] = substr($MACheaderData, 0, 4); + if ($thisfile_monkeysaudio_raw['magic'] != 'MAC ') { + $ThisFileInfo['error'][] = 'Expecting "MAC" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$thisfile_monkeysaudio_raw['magic'].'"'; + unset($ThisFileInfo['fileformat']); + return false; + } + $thisfile_monkeysaudio_raw['nVersion'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 4, 2)); // appears to be uint32 in 3.98+ + + if ($thisfile_monkeysaudio_raw['nVersion'] < 3980) { + $thisfile_monkeysaudio_raw['nCompressionLevel'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 6, 2)); + $thisfile_monkeysaudio_raw['nFormatFlags'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 8, 2)); + $thisfile_monkeysaudio_raw['nChannels'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 10, 2)); + $thisfile_monkeysaudio_raw['nSampleRate'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 12, 4)); + $thisfile_monkeysaudio_raw['nHeaderDataBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 16, 4)); + $thisfile_monkeysaudio_raw['nWAVTerminatingBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 20, 4)); + $thisfile_monkeysaudio_raw['nTotalFrames'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 24, 4)); + $thisfile_monkeysaudio_raw['nFinalFrameSamples'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 28, 4)); + $thisfile_monkeysaudio_raw['nPeakLevel'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 32, 4)); + $thisfile_monkeysaudio_raw['nSeekElements'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 38, 2)); + $offset = 8; + } else { + $offset = 8; + // APE_DESCRIPTOR + $thisfile_monkeysaudio_raw['nDescriptorBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4)); + $offset += 4; + $thisfile_monkeysaudio_raw['nHeaderBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4)); + $offset += 4; + $thisfile_monkeysaudio_raw['nSeekTableBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4)); + $offset += 4; + $thisfile_monkeysaudio_raw['nHeaderDataBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4)); + $offset += 4; + $thisfile_monkeysaudio_raw['nAPEFrameDataBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4)); + $offset += 4; + $thisfile_monkeysaudio_raw['nAPEFrameDataBytesHigh'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4)); + $offset += 4; + $thisfile_monkeysaudio_raw['nTerminatingDataBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4)); + $offset += 4; + $thisfile_monkeysaudio_raw['cFileMD5'] = substr($MACheaderData, $offset, 16); + $offset += 16; + + // APE_HEADER + $thisfile_monkeysaudio_raw['nCompressionLevel'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 2)); + $offset += 2; + $thisfile_monkeysaudio_raw['nFormatFlags'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 2)); + $offset += 2; + $thisfile_monkeysaudio_raw['nBlocksPerFrame'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4)); + $offset += 4; + $thisfile_monkeysaudio_raw['nFinalFrameBlocks'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4)); + $offset += 4; + $thisfile_monkeysaudio_raw['nTotalFrames'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4)); + $offset += 4; + $thisfile_monkeysaudio_raw['nBitsPerSample'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 2)); + $offset += 2; + $thisfile_monkeysaudio_raw['nChannels'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 2)); + $offset += 2; + $thisfile_monkeysaudio_raw['nSampleRate'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4)); + $offset += 4; + } + + $thisfile_monkeysaudio['flags']['8-bit'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0001); + $thisfile_monkeysaudio['flags']['crc-32'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0002); + $thisfile_monkeysaudio['flags']['peak_level'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0004); + $thisfile_monkeysaudio['flags']['24-bit'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0008); + $thisfile_monkeysaudio['flags']['seek_elements'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0010); + $thisfile_monkeysaudio['flags']['no_wav_header'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0020); + $thisfile_monkeysaudio['version'] = $thisfile_monkeysaudio_raw['nVersion'] / 1000; + $thisfile_monkeysaudio['compression'] = $this->MonkeyCompressionLevelNameLookup($thisfile_monkeysaudio_raw['nCompressionLevel']); + if ($thisfile_monkeysaudio_raw['nVersion'] < 3980) { + $thisfile_monkeysaudio['samples_per_frame'] = $this->MonkeySamplesPerFrame($thisfile_monkeysaudio_raw['nVersion'], $thisfile_monkeysaudio_raw['nCompressionLevel']); + } + $thisfile_monkeysaudio['bits_per_sample'] = ($thisfile_monkeysaudio['flags']['24-bit'] ? 24 : ($thisfile_monkeysaudio['flags']['8-bit'] ? 8 : 16)); + $thisfile_monkeysaudio['channels'] = $thisfile_monkeysaudio_raw['nChannels']; + $ThisFileInfo['audio']['channels'] = $thisfile_monkeysaudio['channels']; + $thisfile_monkeysaudio['sample_rate'] = $thisfile_monkeysaudio_raw['nSampleRate']; + if ($thisfile_monkeysaudio['sample_rate'] == 0) { + $ThisFileInfo['error'][] = 'Corrupt MAC file: frequency == zero'; + return false; + } + $ThisFileInfo['audio']['sample_rate'] = $thisfile_monkeysaudio['sample_rate']; + if ($thisfile_monkeysaudio['flags']['peak_level']) { + $thisfile_monkeysaudio['peak_level'] = $thisfile_monkeysaudio_raw['nPeakLevel']; + $thisfile_monkeysaudio['peak_ratio'] = $thisfile_monkeysaudio['peak_level'] / pow(2, $thisfile_monkeysaudio['bits_per_sample'] - 1); + } + if ($thisfile_monkeysaudio_raw['nVersion'] >= 3980) { + $thisfile_monkeysaudio['samples'] = (($thisfile_monkeysaudio_raw['nTotalFrames'] - 1) * $thisfile_monkeysaudio_raw['nBlocksPerFrame']) + $thisfile_monkeysaudio_raw['nFinalFrameBlocks']; + } else { + $thisfile_monkeysaudio['samples'] = (($thisfile_monkeysaudio_raw['nTotalFrames'] - 1) * $thisfile_monkeysaudio['samples_per_frame']) + $thisfile_monkeysaudio_raw['nFinalFrameSamples']; + } + $thisfile_monkeysaudio['playtime'] = $thisfile_monkeysaudio['samples'] / $thisfile_monkeysaudio['sample_rate']; + if ($thisfile_monkeysaudio['playtime'] == 0) { + $ThisFileInfo['error'][] = 'Corrupt MAC file: playtime == zero'; + return false; + } + $ThisFileInfo['playtime_seconds'] = $thisfile_monkeysaudio['playtime']; + $thisfile_monkeysaudio['compressed_size'] = $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']; + $thisfile_monkeysaudio['uncompressed_size'] = $thisfile_monkeysaudio['samples'] * $thisfile_monkeysaudio['channels'] * ($thisfile_monkeysaudio['bits_per_sample'] / 8); + if ($thisfile_monkeysaudio['uncompressed_size'] == 0) { + $ThisFileInfo['error'][] = 'Corrupt MAC file: uncompressed_size == zero'; + return false; + } + $thisfile_monkeysaudio['compression_ratio'] = $thisfile_monkeysaudio['compressed_size'] / ($thisfile_monkeysaudio['uncompressed_size'] + $thisfile_monkeysaudio_raw['nHeaderDataBytes']); + $thisfile_monkeysaudio['bitrate'] = (($thisfile_monkeysaudio['samples'] * $thisfile_monkeysaudio['channels'] * $thisfile_monkeysaudio['bits_per_sample']) / $thisfile_monkeysaudio['playtime']) * $thisfile_monkeysaudio['compression_ratio']; + $ThisFileInfo['audio']['bitrate'] = $thisfile_monkeysaudio['bitrate']; + + // add size of MAC header to avdataoffset + if ($thisfile_monkeysaudio_raw['nVersion'] >= 3980) { + $ThisFileInfo['avdataoffset'] += $thisfile_monkeysaudio_raw['nDescriptorBytes']; + $ThisFileInfo['avdataoffset'] += $thisfile_monkeysaudio_raw['nHeaderBytes']; + $ThisFileInfo['avdataoffset'] += $thisfile_monkeysaudio_raw['nSeekTableBytes']; + $ThisFileInfo['avdataoffset'] += $thisfile_monkeysaudio_raw['nHeaderDataBytes']; + + $ThisFileInfo['avdataend'] -= $thisfile_monkeysaudio_raw['nTerminatingDataBytes']; + } else { + $ThisFileInfo['avdataoffset'] += $offset; + } + + if ($thisfile_monkeysaudio_raw['nVersion'] >= 3980) { + if ($thisfile_monkeysaudio_raw['cFileMD5'] === str_repeat("\x00", 16)) { + //$ThisFileInfo['warning'][] = 'cFileMD5 is null'; + } else { + $ThisFileInfo['md5_data_source'] = ''; + $md5 = $thisfile_monkeysaudio_raw['cFileMD5']; + for ($i = 0; $i < strlen($md5); $i++) { + $ThisFileInfo['md5_data_source'] .= str_pad(dechex(ord($md5{$i})), 2, '00', STR_PAD_LEFT); + } + if (!preg_match('/^[0-9a-f]{32}$/', $ThisFileInfo['md5_data_source'])) { + unset($ThisFileInfo['md5_data_source']); + } + } + } + + + + $ThisFileInfo['audio']['bits_per_sample'] = $thisfile_monkeysaudio['bits_per_sample']; + $ThisFileInfo['audio']['encoder'] = 'MAC v'.number_format($thisfile_monkeysaudio['version'], 2); + $ThisFileInfo['audio']['encoder_options'] = ucfirst($thisfile_monkeysaudio['compression']).' compression'; + + return true; + } + + function MonkeyCompressionLevelNameLookup($compressionlevel) { + static $MonkeyCompressionLevelNameLookup = array( + 0 => 'unknown', + 1000 => 'fast', + 2000 => 'normal', + 3000 => 'high', + 4000 => 'extra-high', + 5000 => 'insane' + ); + return (isset($MonkeyCompressionLevelNameLookup[$compressionlevel]) ? $MonkeyCompressionLevelNameLookup[$compressionlevel] : 'invalid'); + } + + function MonkeySamplesPerFrame($versionid, $compressionlevel) { + if ($versionid >= 3950) { + return 73728 * 4; + } elseif ($versionid >= 3900) { + return 73728; + } elseif (($versionid >= 3800) && ($compressionlevel == 4000)) { + return 73728; + } else { + return 9216; + } + } + +} + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.audio.mp3.php b/includes/getid3/getid3/module.audio.mp3.php new file mode 100644 index 0000000..baff838 --- /dev/null +++ b/includes/getid3/getid3/module.audio.mp3.php @@ -0,0 +1,1937 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio.mp3.php // +// module for analyzing MP3 files // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + + +// number of frames to scan to determine if MPEG-audio sequence is valid +// Lower this number to 5-20 for faster scanning +// Increase this number to 50+ for most accurate detection of valid VBR/CBR +// mpeg-audio streams +define('GETID3_MP3_VALID_CHECK_FRAMES', 35); + + +class getid3_mp3 +{ + + var $allow_bruteforce = false; // forces getID3() to scan the file byte-by-byte and log all the valid audio frame headers - extremely slow, unrecommended, but may provide data from otherwise-unusuable files + + function getid3_mp3(&$fd, &$ThisFileInfo) { + + if (!$this->getOnlyMPEGaudioInfo($fd, $ThisFileInfo, $ThisFileInfo['avdataoffset'])) { + if ($this->allow_bruteforce) { + $ThisFileInfo['error'][] = 'Rescanning file in BruteForce mode'; + $this->getOnlyMPEGaudioInfoBruteForce($fd, $ThisFileInfo); + } + } + + + if (isset($ThisFileInfo['mpeg']['audio']['bitrate_mode'])) { + $ThisFileInfo['audio']['bitrate_mode'] = strtolower($ThisFileInfo['mpeg']['audio']['bitrate_mode']); + } + + if (((isset($ThisFileInfo['id3v2']['headerlength']) && ($ThisFileInfo['avdataoffset'] > $ThisFileInfo['id3v2']['headerlength'])) || (!isset($ThisFileInfo['id3v2']) && ($ThisFileInfo['avdataoffset'] > 0)))) { + + $synchoffsetwarning = 'Unknown data before synch '; + if (isset($ThisFileInfo['id3v2']['headerlength'])) { + $synchoffsetwarning .= '(ID3v2 header ends at '.$ThisFileInfo['id3v2']['headerlength'].', then '.($ThisFileInfo['avdataoffset'] - $ThisFileInfo['id3v2']['headerlength']).' bytes garbage, '; + } else { + $synchoffsetwarning .= '(should be at beginning of file, '; + } + $synchoffsetwarning .= 'synch detected at '.$ThisFileInfo['avdataoffset'].')'; + if ($ThisFileInfo['audio']['bitrate_mode'] == 'cbr') { + + if (!empty($ThisFileInfo['id3v2']['headerlength']) && (($ThisFileInfo['avdataoffset'] - $ThisFileInfo['id3v2']['headerlength']) == $ThisFileInfo['mpeg']['audio']['framelength'])) { + + $synchoffsetwarning .= '. This is a known problem with some versions of LAME (3.90-3.92) DLL in CBR mode.'; + $ThisFileInfo['audio']['codec'] = 'LAME'; + $CurrentDataLAMEversionString = 'LAME3.'; + + } elseif (empty($ThisFileInfo['id3v2']['headerlength']) && ($ThisFileInfo['avdataoffset'] == $ThisFileInfo['mpeg']['audio']['framelength'])) { + + $synchoffsetwarning .= '. This is a known problem with some versions of LAME (3.90 - 3.92) DLL in CBR mode.'; + $ThisFileInfo['audio']['codec'] = 'LAME'; + $CurrentDataLAMEversionString = 'LAME3.'; + + } + + } + $ThisFileInfo['warning'][] = $synchoffsetwarning; + + } + + if (isset($ThisFileInfo['mpeg']['audio']['LAME'])) { + $ThisFileInfo['audio']['codec'] = 'LAME'; + if (!empty($ThisFileInfo['mpeg']['audio']['LAME']['long_version'])) { + $ThisFileInfo['audio']['encoder'] = rtrim($ThisFileInfo['mpeg']['audio']['LAME']['long_version'], "\x00"); + } elseif (!empty($ThisFileInfo['mpeg']['audio']['LAME']['short_version'])) { + $ThisFileInfo['audio']['encoder'] = rtrim($ThisFileInfo['mpeg']['audio']['LAME']['short_version'], "\x00"); + } + } + + $CurrentDataLAMEversionString = (!empty($CurrentDataLAMEversionString) ? $CurrentDataLAMEversionString : @$ThisFileInfo['audio']['encoder']); + if (!empty($CurrentDataLAMEversionString) && (substr($CurrentDataLAMEversionString, 0, 6) == 'LAME3.') && !preg_match('[0-9\)]', substr($CurrentDataLAMEversionString, -1))) { + // a version number of LAME that does not end with a number like "LAME3.92" + // or with a closing parenthesis like "LAME3.88 (alpha)" + // or a version of LAME with the LAMEtag-not-filled-in-DLL-mode bug (3.90-3.92) + + // not sure what the actual last frame length will be, but will be less than or equal to 1441 + $PossiblyLongerLAMEversion_FrameLength = 1441; + + // Not sure what version of LAME this is - look in padding of last frame for longer version string + $PossibleLAMEversionStringOffset = $ThisFileInfo['avdataend'] - $PossiblyLongerLAMEversion_FrameLength; + fseek($fd, $PossibleLAMEversionStringOffset); + $PossiblyLongerLAMEversion_Data = fread($fd, $PossiblyLongerLAMEversion_FrameLength); + switch (substr($CurrentDataLAMEversionString, -1)) { + case 'a': + case 'b': + // "LAME3.94a" will have a longer version string of "LAME3.94 (alpha)" for example + // need to trim off "a" to match longer string + $CurrentDataLAMEversionString = substr($CurrentDataLAMEversionString, 0, -1); + break; + } + if (($PossiblyLongerLAMEversion_String = strstr($PossiblyLongerLAMEversion_Data, $CurrentDataLAMEversionString)) !== false) { + if (substr($PossiblyLongerLAMEversion_String, 0, strlen($CurrentDataLAMEversionString)) == $CurrentDataLAMEversionString) { + $PossiblyLongerLAMEversion_NewString = substr($PossiblyLongerLAMEversion_String, 0, strspn($PossiblyLongerLAMEversion_String, 'LAME0123456789., (abcdefghijklmnopqrstuvwxyzJFSOND)')); //"LAME3.90.3" "LAME3.87 (beta 1, Sep 27 2000)" "LAME3.88 (beta)" + if (strlen($PossiblyLongerLAMEversion_NewString) > strlen(@$ThisFileInfo['audio']['encoder'])) { + $ThisFileInfo['audio']['encoder'] = $PossiblyLongerLAMEversion_NewString; + } + } + } + } + if (!empty($ThisFileInfo['audio']['encoder'])) { + $ThisFileInfo['audio']['encoder'] = rtrim($ThisFileInfo['audio']['encoder'], "\x00 "); + } + + switch (@$ThisFileInfo['mpeg']['audio']['layer']) { + case 1: + case 2: + $ThisFileInfo['audio']['dataformat'] = 'mp'.$ThisFileInfo['mpeg']['audio']['layer']; + break; + } + if ($ThisFileInfo['fileformat'] == 'mp3') { + switch ($ThisFileInfo['audio']['dataformat']) { + case 'mp1': + case 'mp2': + case 'mp3': + $ThisFileInfo['fileformat'] = $ThisFileInfo['audio']['dataformat']; + break; + + default: + $ThisFileInfo['warning'][] = 'Expecting [audio][dataformat] to be mp1/mp2/mp3 when fileformat == mp3, [audio][dataformat] actually "'.$ThisFileInfo['audio']['dataformat'].'"'; + break; + } + } + + if (empty($ThisFileInfo['fileformat'])) { + unset($ThisFileInfo['fileformat']); + unset($ThisFileInfo['audio']['bitrate_mode']); + unset($ThisFileInfo['avdataoffset']); + unset($ThisFileInfo['avdataend']); + return false; + } + + $ThisFileInfo['mime_type'] = 'audio/mpeg'; + $ThisFileInfo['audio']['lossless'] = false; + + // Calculate playtime + if (!isset($ThisFileInfo['playtime_seconds']) && isset($ThisFileInfo['audio']['bitrate']) && ($ThisFileInfo['audio']['bitrate'] > 0)) { + $ThisFileInfo['playtime_seconds'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / $ThisFileInfo['audio']['bitrate']; + } + + $ThisFileInfo['audio']['encoder_options'] = $this->GuessEncoderOptions($ThisFileInfo); + + return true; + } + + + function GuessEncoderOptions(&$ThisFileInfo) { + // shortcuts + if (!empty($ThisFileInfo['mpeg']['audio'])) { + $thisfile_mpeg_audio = &$ThisFileInfo['mpeg']['audio']; + if (!empty($thisfile_mpeg_audio['LAME'])) { + $thisfile_mpeg_audio_lame = &$thisfile_mpeg_audio['LAME']; + } + } + + $encoder_options = ''; + static $NamedPresetBitrates = array(16, 24, 40, 56, 112, 128, 160, 192, 256); + + if ((@$thisfile_mpeg_audio['VBR_method'] == 'Fraunhofer') && !empty($thisfile_mpeg_audio['VBR_quality'])) { + + $encoder_options = 'VBR q'.$thisfile_mpeg_audio['VBR_quality']; + + } elseif (!empty($thisfile_mpeg_audio_lame['preset_used']) && (!in_array($thisfile_mpeg_audio_lame['preset_used_id'], $NamedPresetBitrates))) { + + $encoder_options = $thisfile_mpeg_audio_lame['preset_used']; + + } elseif (!empty($thisfile_mpeg_audio_lame['vbr_quality'])) { + + static $KnownEncoderValues = array(); + if (empty($KnownEncoderValues)) { + + //$KnownEncoderValues[abrbitrate_minbitrate][vbr_quality][raw_vbr_method][raw_noise_shaping][raw_stereo_mode][ath_type][lowpass_frequency] = 'preset name'; + $KnownEncoderValues[0xFF][58][1][1][3][2][20500] = '--alt-preset insane'; // 3.90, 3.90.1, 3.92 + $KnownEncoderValues[0xFF][58][1][1][3][2][20600] = '--alt-preset insane'; // 3.90.2, 3.90.3, 3.91 + $KnownEncoderValues[0xFF][57][1][1][3][4][20500] = '--alt-preset insane'; // 3.94, 3.95 + $KnownEncoderValues['**'][78][3][2][3][2][19500] = '--alt-preset extreme'; // 3.90, 3.90.1, 3.92 + $KnownEncoderValues['**'][78][3][2][3][2][19600] = '--alt-preset extreme'; // 3.90.2, 3.91 + $KnownEncoderValues['**'][78][3][1][3][2][19600] = '--alt-preset extreme'; // 3.90.3 + $KnownEncoderValues['**'][78][4][2][3][2][19500] = '--alt-preset fast extreme'; // 3.90, 3.90.1, 3.92 + $KnownEncoderValues['**'][78][4][2][3][2][19600] = '--alt-preset fast extreme'; // 3.90.2, 3.90.3, 3.91 + $KnownEncoderValues['**'][78][3][2][3][4][19000] = '--alt-preset standard'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 + $KnownEncoderValues['**'][78][3][1][3][4][19000] = '--alt-preset standard'; // 3.90.3 + $KnownEncoderValues['**'][78][4][2][3][4][19000] = '--alt-preset fast standard'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 + $KnownEncoderValues['**'][78][4][1][3][4][19000] = '--alt-preset fast standard'; // 3.90.3 + $KnownEncoderValues['**'][88][4][1][3][3][19500] = '--r3mix'; // 3.90, 3.90.1, 3.92 + $KnownEncoderValues['**'][88][4][1][3][3][19600] = '--r3mix'; // 3.90.2, 3.90.3, 3.91 + $KnownEncoderValues['**'][67][4][1][3][4][18000] = '--r3mix'; // 3.94, 3.95 + $KnownEncoderValues['**'][68][3][2][3][4][18000] = '--alt-preset medium'; // 3.90.3 + $KnownEncoderValues['**'][68][4][2][3][4][18000] = '--alt-preset fast medium'; // 3.90.3 + + $KnownEncoderValues[0xFF][99][1][1][1][2][0] = '--preset studio'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 + $KnownEncoderValues[0xFF][58][2][1][3][2][20600] = '--preset studio'; // 3.90.3, 3.93.1 + $KnownEncoderValues[0xFF][58][2][1][3][2][20500] = '--preset studio'; // 3.93 + $KnownEncoderValues[0xFF][57][2][1][3][4][20500] = '--preset studio'; // 3.94, 3.95 + $KnownEncoderValues[0xC0][88][1][1][1][2][0] = '--preset cd'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 + $KnownEncoderValues[0xC0][58][2][2][3][2][19600] = '--preset cd'; // 3.90.3, 3.93.1 + $KnownEncoderValues[0xC0][58][2][2][3][2][19500] = '--preset cd'; // 3.93 + $KnownEncoderValues[0xC0][57][2][1][3][4][19500] = '--preset cd'; // 3.94, 3.95 + $KnownEncoderValues[0xA0][78][1][1][3][2][18000] = '--preset hifi'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 + $KnownEncoderValues[0xA0][58][2][2][3][2][18000] = '--preset hifi'; // 3.90.3, 3.93, 3.93.1 + $KnownEncoderValues[0xA0][57][2][1][3][4][18000] = '--preset hifi'; // 3.94, 3.95 + $KnownEncoderValues[0x80][67][1][1][3][2][18000] = '--preset tape'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 + $KnownEncoderValues[0x80][67][1][1][3][2][15000] = '--preset radio'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 + $KnownEncoderValues[0x70][67][1][1][3][2][15000] = '--preset fm'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 + $KnownEncoderValues[0x70][58][2][2][3][2][16000] = '--preset tape/radio/fm'; // 3.90.3, 3.93, 3.93.1 + $KnownEncoderValues[0x70][57][2][1][3][4][16000] = '--preset tape/radio/fm'; // 3.94, 3.95 + $KnownEncoderValues[0x38][58][2][2][0][2][10000] = '--preset voice'; // 3.90.3, 3.93, 3.93.1 + $KnownEncoderValues[0x38][57][2][1][0][4][15000] = '--preset voice'; // 3.94, 3.95 + $KnownEncoderValues[0x38][57][2][1][0][4][16000] = '--preset voice'; // 3.94a14 + $KnownEncoderValues[0x28][65][1][1][0][2][7500] = '--preset mw-us'; // 3.90, 3.90.1, 3.92 + $KnownEncoderValues[0x28][65][1][1][0][2][7600] = '--preset mw-us'; // 3.90.2, 3.91 + $KnownEncoderValues[0x28][58][2][2][0][2][7000] = '--preset mw-us'; // 3.90.3, 3.93, 3.93.1 + $KnownEncoderValues[0x28][57][2][1][0][4][10500] = '--preset mw-us'; // 3.94, 3.95 + $KnownEncoderValues[0x28][57][2][1][0][4][11200] = '--preset mw-us'; // 3.94a14 + $KnownEncoderValues[0x28][57][2][1][0][4][8800] = '--preset mw-us'; // 3.94a15 + $KnownEncoderValues[0x18][58][2][2][0][2][4000] = '--preset phon+/lw/mw-eu/sw'; // 3.90.3, 3.93.1 + $KnownEncoderValues[0x18][58][2][2][0][2][3900] = '--preset phon+/lw/mw-eu/sw'; // 3.93 + $KnownEncoderValues[0x18][57][2][1][0][4][5900] = '--preset phon+/lw/mw-eu/sw'; // 3.94, 3.95 + $KnownEncoderValues[0x18][57][2][1][0][4][6200] = '--preset phon+/lw/mw-eu/sw'; // 3.94a14 + $KnownEncoderValues[0x18][57][2][1][0][4][3200] = '--preset phon+/lw/mw-eu/sw'; // 3.94a15 + $KnownEncoderValues[0x10][58][2][2][0][2][3800] = '--preset phone'; // 3.90.3, 3.93.1 + $KnownEncoderValues[0x10][58][2][2][0][2][3700] = '--preset phone'; // 3.93 + $KnownEncoderValues[0x10][57][2][1][0][4][5600] = '--preset phone'; // 3.94, 3.95 + } + + if (isset($KnownEncoderValues[$thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']])) { + + $encoder_options = $KnownEncoderValues[$thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']]; + + } elseif (isset($KnownEncoderValues['**'][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']])) { + + $encoder_options = $KnownEncoderValues['**'][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']]; + + } elseif ($ThisFileInfo['audio']['bitrate_mode'] == 'vbr') { + + // http://gabriel.mp3-tech.org/mp3infotag.html + // int Quality = (100 - 10 * gfp->VBR_q - gfp->quality)h + + + $LAME_V_value = 10 - ceil($thisfile_mpeg_audio_lame['vbr_quality'] / 10); + $LAME_q_value = 100 - $thisfile_mpeg_audio_lame['vbr_quality'] - ($LAME_V_value * 10); + $encoder_options = '-V'.$LAME_V_value.' -q'.$LAME_q_value; + + } elseif ($ThisFileInfo['audio']['bitrate_mode'] == 'cbr') { + + $encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']).ceil($ThisFileInfo['audio']['bitrate'] / 1000); + + } else { + + $encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']); + + } + + } elseif (!empty($thisfile_mpeg_audio_lame['bitrate_abr'])) { + + $encoder_options = 'ABR'.$thisfile_mpeg_audio_lame['bitrate_abr']; + + } elseif (!empty($ThisFileInfo['audio']['bitrate'])) { + + if ($ThisFileInfo['audio']['bitrate_mode'] == 'cbr') { + $encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']).ceil($ThisFileInfo['audio']['bitrate'] / 1000); + } else { + $encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']); + } + + } + if (!empty($thisfile_mpeg_audio_lame['bitrate_min'])) { + $encoder_options .= ' -b'.$thisfile_mpeg_audio_lame['bitrate_min']; + } + + if (@$thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev'] || @$thisfile_mpeg_audio_lame['encoding_flags']['nogap_next']) { + $encoder_options .= ' --nogap'; + } + + if (!empty($thisfile_mpeg_audio_lame['lowpass_frequency'])) { + $ExplodedOptions = explode(' ', $encoder_options, 4); + if ($ExplodedOptions[0] == '--r3mix') { + $ExplodedOptions[1] = 'r3mix'; + } + switch ($ExplodedOptions[0]) { + case '--preset': + case '--alt-preset': + case '--r3mix': + if ($ExplodedOptions[1] == 'fast') { + $ExplodedOptions[1] .= ' '.$ExplodedOptions[2]; + } + switch ($ExplodedOptions[1]) { + case 'portable': + case 'medium': + case 'standard': + case 'extreme': + case 'insane': + case 'fast portable': + case 'fast medium': + case 'fast standard': + case 'fast extreme': + case 'fast insane': + case 'r3mix': + static $ExpectedLowpass = array( + 'insane|20500' => 20500, + 'insane|20600' => 20600, // 3.90.2, 3.90.3, 3.91 + 'medium|18000' => 18000, + 'fast medium|18000' => 18000, + 'extreme|19500' => 19500, // 3.90, 3.90.1, 3.92, 3.95 + 'extreme|19600' => 19600, // 3.90.2, 3.90.3, 3.91, 3.93.1 + 'fast extreme|19500' => 19500, // 3.90, 3.90.1, 3.92, 3.95 + 'fast extreme|19600' => 19600, // 3.90.2, 3.90.3, 3.91, 3.93.1 + 'standard|19000' => 19000, + 'fast standard|19000' => 19000, + 'r3mix|19500' => 19500, // 3.90, 3.90.1, 3.92 + 'r3mix|19600' => 19600, // 3.90.2, 3.90.3, 3.91 + 'r3mix|18000' => 18000, // 3.94, 3.95 + ); + if (!isset($ExpectedLowpass[$ExplodedOptions[1].'|'.$thisfile_mpeg_audio_lame['lowpass_frequency']]) && ($thisfile_mpeg_audio_lame['lowpass_frequency'] < 22050) && (round($thisfile_mpeg_audio_lame['lowpass_frequency'] / 1000) < round($thisfile_mpeg_audio['sample_rate'] / 2000))) { + $encoder_options .= ' --lowpass '.$thisfile_mpeg_audio_lame['lowpass_frequency']; + } + break; + + default: + break; + } + break; + } + } + + if (isset($thisfile_mpeg_audio_lame['raw']['source_sample_freq'])) { + if (($thisfile_mpeg_audio['sample_rate'] == 44100) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 1)) { + $encoder_options .= ' --resample 44100'; + } elseif (($thisfile_mpeg_audio['sample_rate'] == 48000) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 2)) { + $encoder_options .= ' --resample 48000'; + } elseif ($thisfile_mpeg_audio['sample_rate'] < 44100) { + switch ($thisfile_mpeg_audio_lame['raw']['source_sample_freq']) { + case 0: // <= 32000 + // may or may not be same as source frequency - ignore + break; + case 1: // 44100 + case 2: // 48000 + case 3: // 48000+ + $ExplodedOptions = explode(' ', $encoder_options, 4); + switch ($ExplodedOptions[0]) { + case '--preset': + case '--alt-preset': + switch ($ExplodedOptions[1]) { + case 'fast': + case 'portable': + case 'medium': + case 'standard': + case 'extreme': + case 'insane': + $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate']; + break; + + default: + static $ExpectedResampledRate = array( + 'phon+/lw/mw-eu/sw|16000' => 16000, + 'mw-us|24000' => 24000, // 3.95 + 'mw-us|32000' => 32000, // 3.93 + 'mw-us|16000' => 16000, // 3.92 + 'phone|16000' => 16000, + 'phone|11025' => 11025, // 3.94a15 + 'radio|32000' => 32000, // 3.94a15 + 'fm/radio|32000' => 32000, // 3.92 + 'fm|32000' => 32000, // 3.90 + 'voice|32000' => 32000); + if (!isset($ExpectedResampledRate[$ExplodedOptions[1].'|'.$thisfile_mpeg_audio['sample_rate']])) { + $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate']; + } + break; + } + break; + + case '--r3mix': + default: + $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate']; + break; + } + break; + } + } + } + if (empty($encoder_options) && !empty($ThisFileInfo['audio']['bitrate']) && !empty($ThisFileInfo['audio']['bitrate_mode'])) { + //$encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']).ceil($ThisFileInfo['audio']['bitrate'] / 1000); + $encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']); + } + + return $encoder_options; + } + + + function decodeMPEGaudioHeader($fd, $offset, &$ThisFileInfo, $recursivesearch=true, $ScanAsCBR=false, $FastMPEGheaderScan=false) { + + static $MPEGaudioVersionLookup; + static $MPEGaudioLayerLookup; + static $MPEGaudioBitrateLookup; + static $MPEGaudioFrequencyLookup; + static $MPEGaudioChannelModeLookup; + static $MPEGaudioModeExtensionLookup; + static $MPEGaudioEmphasisLookup; + if (empty($MPEGaudioVersionLookup)) { + $MPEGaudioVersionLookup = getid3_mp3::MPEGaudioVersionArray(); + $MPEGaudioLayerLookup = getid3_mp3::MPEGaudioLayerArray(); + $MPEGaudioBitrateLookup = getid3_mp3::MPEGaudioBitrateArray(); + $MPEGaudioFrequencyLookup = getid3_mp3::MPEGaudioFrequencyArray(); + $MPEGaudioChannelModeLookup = getid3_mp3::MPEGaudioChannelModeArray(); + $MPEGaudioModeExtensionLookup = getid3_mp3::MPEGaudioModeExtensionArray(); + $MPEGaudioEmphasisLookup = getid3_mp3::MPEGaudioEmphasisArray(); + } + + if ($offset >= $ThisFileInfo['avdataend']) { + $ThisFileInfo['error'][] = 'end of file encounter looking for MPEG synch'; + return false; + } + fseek($fd, $offset, SEEK_SET); + //$headerstring = fread($fd, 1441); // worst-case max length = 32kHz @ 320kbps layer 3 = 1441 bytes/frame + $headerstring = fread($fd, 226); // LAME header at offset 36 + 190 bytes of Xing/LAME data + + // MP3 audio frame structure: + // $aa $aa $aa $aa [$bb $bb] $cc... + // where $aa..$aa is the four-byte mpeg-audio header (below) + // $bb $bb is the optional 2-byte CRC + // and $cc... is the audio data + + $head4 = substr($headerstring, 0, 4); + + static $MPEGaudioHeaderDecodeCache = array(); + if (isset($MPEGaudioHeaderDecodeCache[$head4])) { + $MPEGheaderRawArray = $MPEGaudioHeaderDecodeCache[$head4]; + } else { + $MPEGheaderRawArray = getid3_mp3::MPEGaudioHeaderDecode($head4); + $MPEGaudioHeaderDecodeCache[$head4] = $MPEGheaderRawArray; + } + + static $MPEGaudioHeaderValidCache = array(); + + // Not in cache + if (!isset($MPEGaudioHeaderValidCache[$head4])) { + //$MPEGaudioHeaderValidCache[$head4] = getid3_mp3::MPEGaudioHeaderValid($MPEGheaderRawArray, false, true); // allow badly-formatted freeformat (from LAME 3.90 - 3.93.1) + $MPEGaudioHeaderValidCache[$head4] = getid3_mp3::MPEGaudioHeaderValid($MPEGheaderRawArray, false, false); + } + + // shortcut + if (!isset($ThisFileInfo['mpeg']['audio'])) { + $ThisFileInfo['mpeg']['audio'] = array(); + } + $thisfile_mpeg_audio = &$ThisFileInfo['mpeg']['audio']; + + + if ($MPEGaudioHeaderValidCache[$head4]) { + $thisfile_mpeg_audio['raw'] = $MPEGheaderRawArray; + } else { + $ThisFileInfo['error'][] = 'Invalid MPEG audio header at offset '.$offset; + return false; + } + + if (!$FastMPEGheaderScan) { + + $thisfile_mpeg_audio['version'] = $MPEGaudioVersionLookup[$thisfile_mpeg_audio['raw']['version']]; + $thisfile_mpeg_audio['layer'] = $MPEGaudioLayerLookup[$thisfile_mpeg_audio['raw']['layer']]; + + $thisfile_mpeg_audio['channelmode'] = $MPEGaudioChannelModeLookup[$thisfile_mpeg_audio['raw']['channelmode']]; + $thisfile_mpeg_audio['channels'] = (($thisfile_mpeg_audio['channelmode'] == 'mono') ? 1 : 2); + $thisfile_mpeg_audio['sample_rate'] = $MPEGaudioFrequencyLookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['raw']['sample_rate']]; + $thisfile_mpeg_audio['protection'] = !$thisfile_mpeg_audio['raw']['protection']; + $thisfile_mpeg_audio['private'] = (bool) $thisfile_mpeg_audio['raw']['private']; + $thisfile_mpeg_audio['modeextension'] = $MPEGaudioModeExtensionLookup[$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['modeextension']]; + $thisfile_mpeg_audio['copyright'] = (bool) $thisfile_mpeg_audio['raw']['copyright']; + $thisfile_mpeg_audio['original'] = (bool) $thisfile_mpeg_audio['raw']['original']; + $thisfile_mpeg_audio['emphasis'] = $MPEGaudioEmphasisLookup[$thisfile_mpeg_audio['raw']['emphasis']]; + + $ThisFileInfo['audio']['channels'] = $thisfile_mpeg_audio['channels']; + $ThisFileInfo['audio']['sample_rate'] = $thisfile_mpeg_audio['sample_rate']; + + if ($thisfile_mpeg_audio['protection']) { + $thisfile_mpeg_audio['crc'] = getid3_lib::BigEndian2Int(substr($headerstring, 4, 2)); + } + + } + + if ($thisfile_mpeg_audio['raw']['bitrate'] == 15) { + // http://www.hydrogenaudio.org/?act=ST&f=16&t=9682&st=0 + $ThisFileInfo['warning'][] = 'Invalid bitrate index (15), this is a known bug in free-format MP3s encoded by LAME v3.90 - 3.93.1'; + $thisfile_mpeg_audio['raw']['bitrate'] = 0; + } + $thisfile_mpeg_audio['padding'] = (bool) $thisfile_mpeg_audio['raw']['padding']; + $thisfile_mpeg_audio['bitrate'] = $MPEGaudioBitrateLookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['bitrate']]; + + if (($thisfile_mpeg_audio['bitrate'] == 'free') && ($offset == $ThisFileInfo['avdataoffset'])) { + // only skip multiple frame check if free-format bitstream found at beginning of file + // otherwise is quite possibly simply corrupted data + $recursivesearch = false; + } + + // For Layer 2 there are some combinations of bitrate and mode which are not allowed. + if (!$FastMPEGheaderScan && ($thisfile_mpeg_audio['layer'] == '2')) { + + $ThisFileInfo['audio']['dataformat'] = 'mp2'; + switch ($thisfile_mpeg_audio['channelmode']) { + + case 'mono': + if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] <= 192000)) { + // these are ok + } else { + $ThisFileInfo['error'][] = $thisfile_mpeg_audio['bitrate'].'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.'; + return false; + } + break; + + case 'stereo': + case 'joint stereo': + case 'dual channel': + if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] == 64000) || ($thisfile_mpeg_audio['bitrate'] >= 96000)) { + // these are ok + } else { + $ThisFileInfo['error'][] = intval(round($thisfile_mpeg_audio['bitrate'] / 1000)).'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.'; + return false; + } + break; + + } + + } + + + if ($ThisFileInfo['audio']['sample_rate'] > 0) { + $thisfile_mpeg_audio['framelength'] = getid3_mp3::MPEGaudioFrameLength($thisfile_mpeg_audio['bitrate'], $thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['layer'], (int) $thisfile_mpeg_audio['padding'], $ThisFileInfo['audio']['sample_rate']); + } + + $nextframetestoffset = $offset + 1; + if ($thisfile_mpeg_audio['bitrate'] != 'free') { + + $ThisFileInfo['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate']; + + if (isset($thisfile_mpeg_audio['framelength'])) { + $nextframetestoffset = $offset + $thisfile_mpeg_audio['framelength']; + } else { + $ThisFileInfo['error'][] = 'Frame at offset('.$offset.') is has an invalid frame length.'; + return false; + } + + } + + $ExpectedNumberOfAudioBytes = 0; + + //////////////////////////////////////////////////////////////////////////////////// + // Variable-bitrate headers + + if (substr($headerstring, 4 + 32, 4) == 'VBRI') { + // Fraunhofer VBR header is hardcoded 'VBRI' at offset 0x24 (36) + // specs taken from http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html + + $thisfile_mpeg_audio['bitrate_mode'] = 'vbr'; + $thisfile_mpeg_audio['VBR_method'] = 'Fraunhofer'; + $ThisFileInfo['audio']['codec'] = 'Fraunhofer'; + + $SideInfoData = substr($headerstring, 4 + 2, 32); + + $FraunhoferVBROffset = 36; + + $thisfile_mpeg_audio['VBR_encoder_version'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 4, 2)); // VbriVersion + $thisfile_mpeg_audio['VBR_encoder_delay'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 6, 2)); // VbriDelay + $thisfile_mpeg_audio['VBR_quality'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 8, 2)); // VbriQuality + $thisfile_mpeg_audio['VBR_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 10, 4)); // VbriStreamBytes + $thisfile_mpeg_audio['VBR_frames'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 14, 4)); // VbriStreamFrames + $thisfile_mpeg_audio['VBR_seek_offsets'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 18, 2)); // VbriTableSize + $thisfile_mpeg_audio['VBR_seek_scale'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 20, 2)); // VbriTableScale + $thisfile_mpeg_audio['VBR_entry_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 22, 2)); // VbriEntryBytes + $thisfile_mpeg_audio['VBR_entry_frames'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 24, 2)); // VbriEntryFrames + + $ExpectedNumberOfAudioBytes = $thisfile_mpeg_audio['VBR_bytes']; + + $previousbyteoffset = $offset; + for ($i = 0; $i < $thisfile_mpeg_audio['VBR_seek_offsets']; $i++) { + $Fraunhofer_OffsetN = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset, $thisfile_mpeg_audio['VBR_entry_bytes'])); + $FraunhoferVBROffset += $thisfile_mpeg_audio['VBR_entry_bytes']; + $thisfile_mpeg_audio['VBR_offsets_relative'][$i] = ($Fraunhofer_OffsetN * $thisfile_mpeg_audio['VBR_seek_scale']); + $thisfile_mpeg_audio['VBR_offsets_absolute'][$i] = ($Fraunhofer_OffsetN * $thisfile_mpeg_audio['VBR_seek_scale']) + $previousbyteoffset; + $previousbyteoffset += $Fraunhofer_OffsetN; + } + + + } else { + + // Xing VBR header is hardcoded 'Xing' at a offset 0x0D (13), 0x15 (21) or 0x24 (36) + // depending on MPEG layer and number of channels + + $VBRidOffset = getid3_mp3::XingVBRidOffset($thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['channelmode']); + $SideInfoData = substr($headerstring, 4 + 2, $VBRidOffset - 4); + + if ((substr($headerstring, $VBRidOffset, strlen('Xing')) == 'Xing') || (substr($headerstring, $VBRidOffset, strlen('Info')) == 'Info')) { + // 'Xing' is traditional Xing VBR frame + // 'Info' is LAME-encoded CBR (This was done to avoid CBR files to be recognized as traditional Xing VBR files by some decoders.) + // 'Info' *can* legally be used to specify a VBR file as well, however. + + // http://www.multiweb.cz/twoinches/MP3inside.htm + //00..03 = "Xing" or "Info" + //04..07 = Flags: + // 0x01 Frames Flag set if value for number of frames in file is stored + // 0x02 Bytes Flag set if value for filesize in bytes is stored + // 0x04 TOC Flag set if values for TOC are stored + // 0x08 VBR Scale Flag set if values for VBR scale is stored + //08..11 Frames: Number of frames in file (including the first Xing/Info one) + //12..15 Bytes: File length in Bytes + //16..115 TOC (Table of Contents): + // Contains of 100 indexes (one Byte length) for easier lookup in file. Approximately solves problem with moving inside file. + // Each Byte has a value according this formula: + // (TOC[i] / 256) * fileLenInBytes + // So if song lasts eg. 240 sec. and you want to jump to 60. sec. (and file is 5 000 000 Bytes length) you can use: + // TOC[(60/240)*100] = TOC[25] + // and corresponding Byte in file is then approximately at: + // (TOC[25]/256) * 5000000 + //116..119 VBR Scale + + + // should be safe to leave this at 'vbr' and let it be overriden to 'cbr' if a CBR preset/mode is used by LAME +// if (substr($headerstring, $VBRidOffset, strlen('Info')) == 'Xing') { + $thisfile_mpeg_audio['bitrate_mode'] = 'vbr'; + $thisfile_mpeg_audio['VBR_method'] = 'Xing'; +// } else { +// $ScanAsCBR = true; +// $thisfile_mpeg_audio['bitrate_mode'] = 'cbr'; +// } + + $thisfile_mpeg_audio['xing_flags_raw'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 4, 4)); + + $thisfile_mpeg_audio['xing_flags']['frames'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000001); + $thisfile_mpeg_audio['xing_flags']['bytes'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000002); + $thisfile_mpeg_audio['xing_flags']['toc'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000004); + $thisfile_mpeg_audio['xing_flags']['vbr_scale'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000008); + + if ($thisfile_mpeg_audio['xing_flags']['frames']) { + $thisfile_mpeg_audio['VBR_frames'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 8, 4)); + //$thisfile_mpeg_audio['VBR_frames']--; // don't count header Xing/Info frame + } + if ($thisfile_mpeg_audio['xing_flags']['bytes']) { + $thisfile_mpeg_audio['VBR_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 12, 4)); + } + + //if (($thisfile_mpeg_audio['bitrate'] == 'free') && !empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) { + if (!empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) { + + $framelengthfloat = $thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']; + + if ($thisfile_mpeg_audio['layer'] == '1') { + // BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12 + //$ThisFileInfo['audio']['bitrate'] = ((($framelengthfloat / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12; + $ThisFileInfo['audio']['bitrate'] = ($framelengthfloat / 4) * $thisfile_mpeg_audio['sample_rate'] * (2 / $ThisFileInfo['audio']['channels']) / 12; + } else { + // Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144 + //$ThisFileInfo['audio']['bitrate'] = (($framelengthfloat - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144; + $ThisFileInfo['audio']['bitrate'] = $framelengthfloat * $thisfile_mpeg_audio['sample_rate'] * (2 / $ThisFileInfo['audio']['channels']) / 144; + } + $thisfile_mpeg_audio['framelength'] = floor($framelengthfloat); + } + + if ($thisfile_mpeg_audio['xing_flags']['toc']) { + $LAMEtocData = substr($headerstring, $VBRidOffset + 16, 100); + for ($i = 0; $i < 100; $i++) { + $thisfile_mpeg_audio['toc'][$i] = ord($LAMEtocData{$i}); + } + } + if ($thisfile_mpeg_audio['xing_flags']['vbr_scale']) { + $thisfile_mpeg_audio['VBR_scale'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 116, 4)); + } + + + // http://gabriel.mp3-tech.org/mp3infotag.html + if (substr($headerstring, $VBRidOffset + 120, 4) == 'LAME') { + + // shortcut + $thisfile_mpeg_audio['LAME'] = array(); + $thisfile_mpeg_audio_lame = &$thisfile_mpeg_audio['LAME']; + + + $thisfile_mpeg_audio_lame['long_version'] = substr($headerstring, $VBRidOffset + 120, 20); + $thisfile_mpeg_audio_lame['short_version'] = substr($thisfile_mpeg_audio_lame['long_version'], 0, 9); + + if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.90') { + + // extra 11 chars are not part of version string when LAMEtag present + unset($thisfile_mpeg_audio_lame['long_version']); + + // It the LAME tag was only introduced in LAME v3.90 + // http://www.hydrogenaudio.org/?act=ST&f=15&t=9933 + + // Offsets of various bytes in http://gabriel.mp3-tech.org/mp3infotag.html + // are assuming a 'Xing' identifier offset of 0x24, which is the case for + // MPEG-1 non-mono, but not for other combinations + $LAMEtagOffsetContant = $VBRidOffset - 0x24; + + // shortcuts + $thisfile_mpeg_audio_lame['RGAD'] = array('track'=>array(), 'album'=>array()); + $thisfile_mpeg_audio_lame_RGAD = &$thisfile_mpeg_audio_lame['RGAD']; + $thisfile_mpeg_audio_lame_RGAD_track = &$thisfile_mpeg_audio_lame_RGAD['track']; + $thisfile_mpeg_audio_lame_RGAD_album = &$thisfile_mpeg_audio_lame_RGAD['album']; + $thisfile_mpeg_audio_lame['raw'] = array(); + $thisfile_mpeg_audio_lame_raw = &$thisfile_mpeg_audio_lame['raw']; + + // byte $9B VBR Quality + // This field is there to indicate a quality level, although the scale was not precised in the original Xing specifications. + // Actually overwrites original Xing bytes + unset($thisfile_mpeg_audio['VBR_scale']); + $thisfile_mpeg_audio_lame['vbr_quality'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0x9B, 1)); + + // bytes $9C-$A4 Encoder short VersionString + $thisfile_mpeg_audio_lame['short_version'] = substr($headerstring, $LAMEtagOffsetContant + 0x9C, 9); + + // byte $A5 Info Tag revision + VBR method + $LAMEtagRevisionVBRmethod = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA5, 1)); + + $thisfile_mpeg_audio_lame['tag_revision'] = ($LAMEtagRevisionVBRmethod & 0xF0) >> 4; + $thisfile_mpeg_audio_lame_raw['vbr_method'] = $LAMEtagRevisionVBRmethod & 0x0F; + $thisfile_mpeg_audio_lame['vbr_method'] = getid3_mp3::LAMEvbrMethodLookup($thisfile_mpeg_audio_lame_raw['vbr_method']); + $thisfile_mpeg_audio['bitrate_mode'] = substr($thisfile_mpeg_audio_lame['vbr_method'], 0, 3); // usually either 'cbr' or 'vbr', but truncates 'vbr-old / vbr-rh' to 'vbr' + + // byte $A6 Lowpass filter value + $thisfile_mpeg_audio_lame['lowpass_frequency'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA6, 1)) * 100; + + // bytes $A7-$AE Replay Gain + // http://privatewww.essex.ac.uk/~djmrob/replaygain/rg_data_format.html + // bytes $A7-$AA : 32 bit floating point "Peak signal amplitude" + if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.94b') { + // LAME 3.94a16 and later - 9.23 fixed point + // ie 0x0059E2EE / (2^23) = 5890798 / 8388608 = 0.7022378444671630859375 + $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] = (float) ((getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA7, 4))) / 8388608); + } else { + // LAME 3.94a15 and earlier - 32-bit floating point + // Actually 3.94a16 will fall in here too and be WRONG, but is hard to detect 3.94a16 vs 3.94a15 + $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] = getid3_lib::LittleEndian2Float(substr($headerstring, $LAMEtagOffsetContant + 0xA7, 4)); + } + if ($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] == 0) { + unset($thisfile_mpeg_audio_lame_RGAD['peak_amplitude']); + } else { + $thisfile_mpeg_audio_lame_RGAD['peak_db'] = getid3_lib::RGADamplitude2dB($thisfile_mpeg_audio_lame_RGAD['peak_amplitude']); + } + + $thisfile_mpeg_audio_lame_raw['RGAD_track'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAB, 2)); + $thisfile_mpeg_audio_lame_raw['RGAD_album'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAD, 2)); + + + if ($thisfile_mpeg_audio_lame_raw['RGAD_track'] != 0) { + + $thisfile_mpeg_audio_lame_RGAD_track['raw']['name'] = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0xE000) >> 13; + $thisfile_mpeg_audio_lame_RGAD_track['raw']['originator'] = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x1C00) >> 10; + $thisfile_mpeg_audio_lame_RGAD_track['raw']['sign_bit'] = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x0200) >> 9; + $thisfile_mpeg_audio_lame_RGAD_track['raw']['gain_adjust'] = $thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x01FF; + $thisfile_mpeg_audio_lame_RGAD_track['name'] = getid3_lib::RGADnameLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['name']); + $thisfile_mpeg_audio_lame_RGAD_track['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['originator']); + $thisfile_mpeg_audio_lame_RGAD_track['gain_db'] = getid3_lib::RGADadjustmentLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['gain_adjust'], $thisfile_mpeg_audio_lame_RGAD_track['raw']['sign_bit']); + + if (!empty($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'])) { + $ThisFileInfo['replay_gain']['track']['peak'] = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude']; + } + $ThisFileInfo['replay_gain']['track']['originator'] = $thisfile_mpeg_audio_lame_RGAD_track['originator']; + $ThisFileInfo['replay_gain']['track']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_track['gain_db']; + } else { + unset($thisfile_mpeg_audio_lame_RGAD['track']); + } + if ($thisfile_mpeg_audio_lame_raw['RGAD_album'] != 0) { + + $thisfile_mpeg_audio_lame_RGAD_album['raw']['name'] = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0xE000) >> 13; + $thisfile_mpeg_audio_lame_RGAD_album['raw']['originator'] = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x1C00) >> 10; + $thisfile_mpeg_audio_lame_RGAD_album['raw']['sign_bit'] = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x0200) >> 9; + $thisfile_mpeg_audio_lame_RGAD_album['raw']['gain_adjust'] = $thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x01FF; + $thisfile_mpeg_audio_lame_RGAD_album['name'] = getid3_lib::RGADnameLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['name']); + $thisfile_mpeg_audio_lame_RGAD_album['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['originator']); + $thisfile_mpeg_audio_lame_RGAD_album['gain_db'] = getid3_lib::RGADadjustmentLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['gain_adjust'], $thisfile_mpeg_audio_lame_RGAD_album['raw']['sign_bit']); + + if (!empty($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'])) { + $ThisFileInfo['replay_gain']['album']['peak'] = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude']; + } + $ThisFileInfo['replay_gain']['album']['originator'] = $thisfile_mpeg_audio_lame_RGAD_album['originator']; + $ThisFileInfo['replay_gain']['album']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_album['gain_db']; + } else { + unset($thisfile_mpeg_audio_lame_RGAD['album']); + } + if (empty($thisfile_mpeg_audio_lame_RGAD)) { + unset($thisfile_mpeg_audio_lame['RGAD']); + } + + + // byte $AF Encoding flags + ATH Type + $EncodingFlagsATHtype = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAF, 1)); + $thisfile_mpeg_audio_lame['encoding_flags']['nspsytune'] = (bool) ($EncodingFlagsATHtype & 0x10); + $thisfile_mpeg_audio_lame['encoding_flags']['nssafejoint'] = (bool) ($EncodingFlagsATHtype & 0x20); + $thisfile_mpeg_audio_lame['encoding_flags']['nogap_next'] = (bool) ($EncodingFlagsATHtype & 0x40); + $thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev'] = (bool) ($EncodingFlagsATHtype & 0x80); + $thisfile_mpeg_audio_lame['ath_type'] = $EncodingFlagsATHtype & 0x0F; + + // byte $B0 if ABR {specified bitrate} else {minimal bitrate} + $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB0, 1)); + if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 2) { // Average BitRate (ABR) + $thisfile_mpeg_audio_lame['bitrate_abr'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']; + } elseif ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) { // Constant BitRate (CBR) + // ignore + } elseif ($thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] > 0) { // Variable BitRate (VBR) - minimum bitrate + $thisfile_mpeg_audio_lame['bitrate_min'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']; + } + + // bytes $B1-$B3 Encoder delays + $EncoderDelays = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB1, 3)); + $thisfile_mpeg_audio_lame['encoder_delay'] = ($EncoderDelays & 0xFFF000) >> 12; + $thisfile_mpeg_audio_lame['end_padding'] = $EncoderDelays & 0x000FFF; + + // byte $B4 Misc + $MiscByte = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB4, 1)); + $thisfile_mpeg_audio_lame_raw['noise_shaping'] = ($MiscByte & 0x03); + $thisfile_mpeg_audio_lame_raw['stereo_mode'] = ($MiscByte & 0x1C) >> 2; + $thisfile_mpeg_audio_lame_raw['not_optimal_quality'] = ($MiscByte & 0x20) >> 5; + $thisfile_mpeg_audio_lame_raw['source_sample_freq'] = ($MiscByte & 0xC0) >> 6; + $thisfile_mpeg_audio_lame['noise_shaping'] = $thisfile_mpeg_audio_lame_raw['noise_shaping']; + $thisfile_mpeg_audio_lame['stereo_mode'] = getid3_mp3::LAMEmiscStereoModeLookup($thisfile_mpeg_audio_lame_raw['stereo_mode']); + $thisfile_mpeg_audio_lame['not_optimal_quality'] = (bool) $thisfile_mpeg_audio_lame_raw['not_optimal_quality']; + $thisfile_mpeg_audio_lame['source_sample_freq'] = getid3_mp3::LAMEmiscSourceSampleFrequencyLookup($thisfile_mpeg_audio_lame_raw['source_sample_freq']); + + // byte $B5 MP3 Gain + $thisfile_mpeg_audio_lame_raw['mp3_gain'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB5, 1), false, true); + $thisfile_mpeg_audio_lame['mp3_gain_db'] = (getid3_lib::RGADamplitude2dB(2) / 4) * $thisfile_mpeg_audio_lame_raw['mp3_gain']; + $thisfile_mpeg_audio_lame['mp3_gain_factor'] = pow(2, ($thisfile_mpeg_audio_lame['mp3_gain_db'] / 6)); + + // bytes $B6-$B7 Preset and surround info + $PresetSurroundBytes = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB6, 2)); + // Reserved = ($PresetSurroundBytes & 0xC000); + $thisfile_mpeg_audio_lame_raw['surround_info'] = ($PresetSurroundBytes & 0x3800); + $thisfile_mpeg_audio_lame['surround_info'] = getid3_mp3::LAMEsurroundInfoLookup($thisfile_mpeg_audio_lame_raw['surround_info']); + $thisfile_mpeg_audio_lame['preset_used_id'] = ($PresetSurroundBytes & 0x07FF); + $thisfile_mpeg_audio_lame['preset_used'] = getid3_mp3::LAMEpresetUsedLookup($thisfile_mpeg_audio_lame); + if (!empty($thisfile_mpeg_audio_lame['preset_used_id']) && empty($thisfile_mpeg_audio_lame['preset_used'])) { + $ThisFileInfo['warning'][] = 'Unknown LAME preset used ('.$thisfile_mpeg_audio_lame['preset_used_id'].') - please report to info@getid3.org'; + } + if (($thisfile_mpeg_audio_lame['short_version'] == 'LAME3.90.') && !empty($thisfile_mpeg_audio_lame['preset_used_id'])) { + // this may change if 3.90.4 ever comes out + $thisfile_mpeg_audio_lame['short_version'] = 'LAME3.90.3'; + } + + // bytes $B8-$BB MusicLength + $thisfile_mpeg_audio_lame['audio_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB8, 4)); + $ExpectedNumberOfAudioBytes = (($thisfile_mpeg_audio_lame['audio_bytes'] > 0) ? $thisfile_mpeg_audio_lame['audio_bytes'] : $thisfile_mpeg_audio['VBR_bytes']); + + // bytes $BC-$BD MusicCRC + $thisfile_mpeg_audio_lame['music_crc'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xBC, 2)); + + // bytes $BE-$BF CRC-16 of Info Tag + $thisfile_mpeg_audio_lame['lame_tag_crc'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xBE, 2)); + + + // LAME CBR + if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) { + + $thisfile_mpeg_audio['bitrate_mode'] = 'cbr'; + $thisfile_mpeg_audio['bitrate'] = getid3_mp3::ClosestStandardMP3Bitrate($thisfile_mpeg_audio['bitrate']); + $ThisFileInfo['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate']; + //if (empty($thisfile_mpeg_audio['bitrate']) || (!empty($thisfile_mpeg_audio_lame['bitrate_min']) && ($thisfile_mpeg_audio_lame['bitrate_min'] != 255))) { + // $thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio_lame['bitrate_min']; + //} + + } + + } + } + + } else { + + // not Fraunhofer or Xing VBR methods, most likely CBR (but could be VBR with no header) + $thisfile_mpeg_audio['bitrate_mode'] = 'cbr'; + if ($recursivesearch) { + $thisfile_mpeg_audio['bitrate_mode'] = 'vbr'; + if (getid3_mp3::RecursiveFrameScanning($fd, $ThisFileInfo, $offset, $nextframetestoffset, true)) { + $recursivesearch = false; + $thisfile_mpeg_audio['bitrate_mode'] = 'cbr'; + } + if ($thisfile_mpeg_audio['bitrate_mode'] == 'vbr') { + $ThisFileInfo['warning'][] = 'VBR file with no VBR header. Bitrate values calculated from actual frame bitrates.'; + } + } + + } + + } + + if (($ExpectedNumberOfAudioBytes > 0) && ($ExpectedNumberOfAudioBytes != ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']))) { + if ($ExpectedNumberOfAudioBytes > ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'])) { + if (($ExpectedNumberOfAudioBytes - ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'])) == 1) { + $ThisFileInfo['warning'][] = 'Last byte of data truncated (this is a known bug in Meracl ID3 Tag Writer before v1.3.5)'; + } else { + $ThisFileInfo['warning'][] = 'Probable truncated file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, only found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']).' (short by '.($ExpectedNumberOfAudioBytes - ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'])).' bytes)'; + } + } else { + if ((($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) - $ExpectedNumberOfAudioBytes) == 1) { + // $prenullbytefileoffset = ftell($fd); + // fseek($fd, $ThisFileInfo['avdataend'], SEEK_SET); + // $PossibleNullByte = fread($fd, 1); + // fseek($fd, $prenullbytefileoffset, SEEK_SET); + // if ($PossibleNullByte === "\x00") { + $ThisFileInfo['avdataend']--; + // $ThisFileInfo['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored'; + // } else { + // $ThisFileInfo['warning'][] = 'Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']).' ('.(($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)'; + // } + } else { + $ThisFileInfo['warning'][] = 'Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']).' ('.(($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)'; + } + } + } + + if (($thisfile_mpeg_audio['bitrate'] == 'free') && empty($ThisFileInfo['audio']['bitrate'])) { + if (($offset == $ThisFileInfo['avdataoffset']) && empty($thisfile_mpeg_audio['VBR_frames'])) { + $framebytelength = getid3_mp3::FreeFormatFrameLength($fd, $offset, $ThisFileInfo, true); + if ($framebytelength > 0) { + $thisfile_mpeg_audio['framelength'] = $framebytelength; + if ($thisfile_mpeg_audio['layer'] == '1') { + // BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12 + $ThisFileInfo['audio']['bitrate'] = ((($framebytelength / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12; + } else { + // Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144 + $ThisFileInfo['audio']['bitrate'] = (($framebytelength - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144; + } + } else { + $ThisFileInfo['error'][] = 'Error calculating frame length of free-format MP3 without Xing/LAME header'; + } + } + } + + if (!empty($thisfile_mpeg_audio['VBR_frames'])) { + switch ($thisfile_mpeg_audio['bitrate_mode']) { + case 'vbr': + case 'abr': + if (($thisfile_mpeg_audio['version'] == '1') && ($thisfile_mpeg_audio['layer'] == 1)) { + $thisfile_mpeg_audio['VBR_bitrate'] = (($thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($ThisFileInfo['audio']['sample_rate'] / 384); + } elseif ((($thisfile_mpeg_audio['version'] == '2') || ($thisfile_mpeg_audio['version'] == '2.5')) && ($thisfile_mpeg_audio['layer'] == 3)) { + $thisfile_mpeg_audio['VBR_bitrate'] = (($thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($ThisFileInfo['audio']['sample_rate'] / 576); + } else { + $thisfile_mpeg_audio['VBR_bitrate'] = (($thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($ThisFileInfo['audio']['sample_rate'] / 1152); + } + if ($thisfile_mpeg_audio['VBR_bitrate'] > 0) { + $ThisFileInfo['audio']['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate']; + $thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate']; // to avoid confusion + } + break; + } + } + + // End variable-bitrate headers + //////////////////////////////////////////////////////////////////////////////////// + + if ($recursivesearch) { + + if (!getid3_mp3::RecursiveFrameScanning($fd, $ThisFileInfo, $offset, $nextframetestoffset, $ScanAsCBR)) { + return false; + } + + } + + + //if (false) { + // // experimental side info parsing section - not returning anything useful yet + // + // $SideInfoBitstream = getid3_lib::BigEndian2Bin($SideInfoData); + // $SideInfoOffset = 0; + // + // if ($thisfile_mpeg_audio['version'] == '1') { + // if ($thisfile_mpeg_audio['channelmode'] == 'mono') { + // // MPEG-1 (mono) + // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9); + // $SideInfoOffset += 9; + // $SideInfoOffset += 5; + // } else { + // // MPEG-1 (stereo, joint-stereo, dual-channel) + // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9); + // $SideInfoOffset += 9; + // $SideInfoOffset += 3; + // } + // } else { // 2 or 2.5 + // if ($thisfile_mpeg_audio['channelmode'] == 'mono') { + // // MPEG-2, MPEG-2.5 (mono) + // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8); + // $SideInfoOffset += 8; + // $SideInfoOffset += 1; + // } else { + // // MPEG-2, MPEG-2.5 (stereo, joint-stereo, dual-channel) + // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8); + // $SideInfoOffset += 8; + // $SideInfoOffset += 2; + // } + // } + // + // if ($thisfile_mpeg_audio['version'] == '1') { + // for ($channel = 0; $channel < $ThisFileInfo['audio']['channels']; $channel++) { + // for ($scfsi_band = 0; $scfsi_band < 4; $scfsi_band++) { + // $thisfile_mpeg_audio['scfsi'][$channel][$scfsi_band] = substr($SideInfoBitstream, $SideInfoOffset, 1); + // $SideInfoOffset += 2; + // } + // } + // } + // for ($granule = 0; $granule < (($thisfile_mpeg_audio['version'] == '1') ? 2 : 1); $granule++) { + // for ($channel = 0; $channel < $ThisFileInfo['audio']['channels']; $channel++) { + // $thisfile_mpeg_audio['part2_3_length'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 12); + // $SideInfoOffset += 12; + // $thisfile_mpeg_audio['big_values'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9); + // $SideInfoOffset += 9; + // $thisfile_mpeg_audio['global_gain'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 8); + // $SideInfoOffset += 8; + // if ($thisfile_mpeg_audio['version'] == '1') { + // $thisfile_mpeg_audio['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4); + // $SideInfoOffset += 4; + // } else { + // $thisfile_mpeg_audio['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9); + // $SideInfoOffset += 9; + // } + // $thisfile_mpeg_audio['window_switching_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); + // $SideInfoOffset += 1; + // + // if ($thisfile_mpeg_audio['window_switching_flag'][$granule][$channel] == '1') { + // + // $thisfile_mpeg_audio['block_type'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 2); + // $SideInfoOffset += 2; + // $thisfile_mpeg_audio['mixed_block_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); + // $SideInfoOffset += 1; + // + // for ($region = 0; $region < 2; $region++) { + // $thisfile_mpeg_audio['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5); + // $SideInfoOffset += 5; + // } + // $thisfile_mpeg_audio['table_select'][$granule][$channel][2] = 0; + // + // for ($window = 0; $window < 3; $window++) { + // $thisfile_mpeg_audio['subblock_gain'][$granule][$channel][$window] = substr($SideInfoBitstream, $SideInfoOffset, 3); + // $SideInfoOffset += 3; + // } + // + // } else { + // + // for ($region = 0; $region < 3; $region++) { + // $thisfile_mpeg_audio['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5); + // $SideInfoOffset += 5; + // } + // + // $thisfile_mpeg_audio['region0_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4); + // $SideInfoOffset += 4; + // $thisfile_mpeg_audio['region1_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 3); + // $SideInfoOffset += 3; + // $thisfile_mpeg_audio['block_type'][$granule][$channel] = 0; + // } + // + // if ($thisfile_mpeg_audio['version'] == '1') { + // $thisfile_mpeg_audio['preflag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); + // $SideInfoOffset += 1; + // } + // $thisfile_mpeg_audio['scalefac_scale'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); + // $SideInfoOffset += 1; + // $thisfile_mpeg_audio['count1table_select'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); + // $SideInfoOffset += 1; + // } + // } + //} + + return true; + } + + function RecursiveFrameScanning(&$fd, &$ThisFileInfo, &$offset, &$nextframetestoffset, $ScanAsCBR) { + for ($i = 0; $i < GETID3_MP3_VALID_CHECK_FRAMES; $i++) { + // check next GETID3_MP3_VALID_CHECK_FRAMES frames for validity, to make sure we haven't run across a false synch + if (($nextframetestoffset + 4) >= $ThisFileInfo['avdataend']) { + // end of file + return true; + } + + $nextframetestarray = array('error'=>'', 'warning'=>'', 'avdataend'=>$ThisFileInfo['avdataend'], 'avdataoffset'=>$ThisFileInfo['avdataoffset']); + if (getid3_mp3::decodeMPEGaudioHeader($fd, $nextframetestoffset, $nextframetestarray, false)) { + if ($ScanAsCBR) { + // force CBR mode, used for trying to pick out invalid audio streams with + // valid(?) VBR headers, or VBR streams with no VBR header + if (!isset($nextframetestarray['mpeg']['audio']['bitrate']) || !isset($ThisFileInfo['mpeg']['audio']['bitrate']) || ($nextframetestarray['mpeg']['audio']['bitrate'] != $ThisFileInfo['mpeg']['audio']['bitrate'])) { + return false; + } + } + + + // next frame is OK, get ready to check the one after that + if (isset($nextframetestarray['mpeg']['audio']['framelength']) && ($nextframetestarray['mpeg']['audio']['framelength'] > 0)) { + $nextframetestoffset += $nextframetestarray['mpeg']['audio']['framelength']; + } else { + $ThisFileInfo['error'][] = 'Frame at offset ('.$offset.') is has an invalid frame length.'; + return false; + } + + } else { + + // next frame is not valid, note the error and fail, so scanning can contiue for a valid frame sequence + $ThisFileInfo['error'][] = 'Frame at offset ('.$offset.') is valid, but the next one at ('.$nextframetestoffset.') is not.'; + + return false; + } + } + return true; + } + + function FreeFormatFrameLength($fd, $offset, &$ThisFileInfo, $deepscan=false) { + fseek($fd, $offset, SEEK_SET); + $MPEGaudioData = fread($fd, 32768); + + $SyncPattern1 = substr($MPEGaudioData, 0, 4); + // may be different pattern due to padding + $SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) | 0x02).$SyncPattern1{3}; + if ($SyncPattern2 === $SyncPattern1) { + $SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) & 0xFD).$SyncPattern1{3}; + } + + $framelength = false; + $framelength1 = strpos($MPEGaudioData, $SyncPattern1, 4); + $framelength2 = strpos($MPEGaudioData, $SyncPattern2, 4); + if ($framelength1 > 4) { + $framelength = $framelength1; + } + if (($framelength2 > 4) && ($framelength2 < $framelength1)) { + $framelength = $framelength2; + } + if (!$framelength) { + + // LAME 3.88 has a different value for modeextension on the first frame vs the rest + $framelength1 = strpos($MPEGaudioData, substr($SyncPattern1, 0, 3), 4); + $framelength2 = strpos($MPEGaudioData, substr($SyncPattern2, 0, 3), 4); + + if ($framelength1 > 4) { + $framelength = $framelength1; + } + if (($framelength2 > 4) && ($framelength2 < $framelength1)) { + $framelength = $framelength2; + } + if (!$framelength) { + $ThisFileInfo['error'][] = 'Cannot find next free-format synch pattern ('.getid3_lib::PrintHexBytes($SyncPattern1).' or '.getid3_lib::PrintHexBytes($SyncPattern2).') after offset '.$offset; + return false; + } else { + $ThisFileInfo['warning'][] = 'ModeExtension varies between first frame and other frames (known free-format issue in LAME 3.88)'; + $ThisFileInfo['audio']['codec'] = 'LAME'; + $ThisFileInfo['audio']['encoder'] = 'LAME3.88'; + $SyncPattern1 = substr($SyncPattern1, 0, 3); + $SyncPattern2 = substr($SyncPattern2, 0, 3); + } + } + + if ($deepscan) { + + $ActualFrameLengthValues = array(); + $nextoffset = $offset + $framelength; + while ($nextoffset < ($ThisFileInfo['avdataend'] - 6)) { + fseek($fd, $nextoffset - 1, SEEK_SET); + $NextSyncPattern = fread($fd, 6); + if ((substr($NextSyncPattern, 1, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 1, strlen($SyncPattern2)) == $SyncPattern2)) { + // good - found where expected + $ActualFrameLengthValues[] = $framelength; + } elseif ((substr($NextSyncPattern, 0, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 0, strlen($SyncPattern2)) == $SyncPattern2)) { + // ok - found one byte earlier than expected (last frame wasn't padded, first frame was) + $ActualFrameLengthValues[] = ($framelength - 1); + $nextoffset--; + } elseif ((substr($NextSyncPattern, 2, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 2, strlen($SyncPattern2)) == $SyncPattern2)) { + // ok - found one byte later than expected (last frame was padded, first frame wasn't) + $ActualFrameLengthValues[] = ($framelength + 1); + $nextoffset++; + } else { + $ThisFileInfo['error'][] = 'Did not find expected free-format sync pattern at offset '.$nextoffset; + return false; + } + $nextoffset += $framelength; + } + if (count($ActualFrameLengthValues) > 0) { + $framelength = intval(round(array_sum($ActualFrameLengthValues) / count($ActualFrameLengthValues))); + } + } + return $framelength; + } + + function getOnlyMPEGaudioInfoBruteForce($fd, &$ThisFileInfo) { + + $MPEGaudioHeaderDecodeCache = array(); + $MPEGaudioHeaderValidCache = array(); + $MPEGaudioHeaderLengthCache = array(); + $MPEGaudioVersionLookup = getid3_mp3::MPEGaudioVersionArray(); + $MPEGaudioLayerLookup = getid3_mp3::MPEGaudioLayerArray(); + $MPEGaudioBitrateLookup = getid3_mp3::MPEGaudioBitrateArray(); + $MPEGaudioFrequencyLookup = getid3_mp3::MPEGaudioFrequencyArray(); + $MPEGaudioChannelModeLookup = getid3_mp3::MPEGaudioChannelModeArray(); + $MPEGaudioModeExtensionLookup = getid3_mp3::MPEGaudioModeExtensionArray(); + $MPEGaudioEmphasisLookup = getid3_mp3::MPEGaudioEmphasisArray(); + $LongMPEGversionLookup = array(); + $LongMPEGlayerLookup = array(); + $LongMPEGbitrateLookup = array(); + $LongMPEGpaddingLookup = array(); + $LongMPEGfrequencyLookup = array(); + + $Distribution['bitrate'] = array(); + $Distribution['frequency'] = array(); + $Distribution['layer'] = array(); + $Distribution['version'] = array(); + $Distribution['padding'] = array(); + + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + + $previousvalidframe = $ThisFileInfo['avdataoffset']; + while (ftell($fd) < $ThisFileInfo['avdataend']) { + set_time_limit(30); + $head4 = fread($fd, 4); + if (strlen($head4) < 4) { + break; + } + if ($head4{0} != "\xFF") { + for ($i = 1; $i < 4; $i++) { + if ($head4{$i} == "\xFF") { + fseek($fd, $i - 4, SEEK_CUR); + continue 2; + } + } + continue; + } + if (!isset($MPEGaudioHeaderDecodeCache[$head4])) { + $MPEGaudioHeaderDecodeCache[$head4] = getid3_mp3::MPEGaudioHeaderDecode($head4); + } + if (!isset($MPEGaudioHeaderValidCache[$head4])) { + $MPEGaudioHeaderValidCache[$head4] = getid3_mp3::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$head4], false, false); + } + if ($MPEGaudioHeaderValidCache[$head4]) { + + if (!isset($MPEGaudioHeaderLengthCache[$head4])) { + $LongMPEGversionLookup[$head4] = $MPEGaudioVersionLookup[$MPEGaudioHeaderDecodeCache[$head4]['version']]; + $LongMPEGlayerLookup[$head4] = $MPEGaudioLayerLookup[$MPEGaudioHeaderDecodeCache[$head4]['layer']]; + $LongMPEGbitrateLookup[$head4] = $MPEGaudioBitrateLookup[$LongMPEGversionLookup[$head4]][$LongMPEGlayerLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['bitrate']]; + $LongMPEGpaddingLookup[$head4] = (bool) $MPEGaudioHeaderDecodeCache[$head4]['padding']; + $LongMPEGfrequencyLookup[$head4] = $MPEGaudioFrequencyLookup[$LongMPEGversionLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['sample_rate']]; + $MPEGaudioHeaderLengthCache[$head4] = getid3_mp3::MPEGaudioFrameLength( + $LongMPEGbitrateLookup[$head4], + $LongMPEGversionLookup[$head4], + $LongMPEGlayerLookup[$head4], + $LongMPEGpaddingLookup[$head4], + $LongMPEGfrequencyLookup[$head4]); + } + if ($MPEGaudioHeaderLengthCache[$head4] > 4) { + $WhereWeWere = ftell($fd); + fseek($fd, $MPEGaudioHeaderLengthCache[$head4] - 4, SEEK_CUR); + $next4 = fread($fd, 4); + if ($next4{0} == "\xFF") { + if (!isset($MPEGaudioHeaderDecodeCache[$next4])) { + $MPEGaudioHeaderDecodeCache[$next4] = getid3_mp3::MPEGaudioHeaderDecode($next4); + } + if (!isset($MPEGaudioHeaderValidCache[$next4])) { + $MPEGaudioHeaderValidCache[$next4] = getid3_mp3::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$next4], false, false); + } + if ($MPEGaudioHeaderValidCache[$next4]) { + fseek($fd, -4, SEEK_CUR); + + @$Distribution['bitrate'][$LongMPEGbitrateLookup[$head4]]++; + @$Distribution['layer'][$LongMPEGlayerLookup[$head4]]++; + @$Distribution['version'][$LongMPEGversionLookup[$head4]]++; + @$Distribution['padding'][intval($LongMPEGpaddingLookup[$head4])]++; + @$Distribution['frequency'][$LongMPEGfrequencyLookup[$head4]]++; + continue; + } + } + unset($next4); + fseek($fd, $WhereWeWere - 3, SEEK_SET); + } + + } + } + foreach ($Distribution as $key => $value) { + ksort($Distribution[$key], SORT_NUMERIC); + } + ksort($Distribution['version'], SORT_STRING); + $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = $Distribution['bitrate']; + $ThisFileInfo['mpeg']['audio']['frequency_distribution'] = $Distribution['frequency']; + $ThisFileInfo['mpeg']['audio']['layer_distribution'] = $Distribution['layer']; + $ThisFileInfo['mpeg']['audio']['version_distribution'] = $Distribution['version']; + $ThisFileInfo['mpeg']['audio']['padding_distribution'] = $Distribution['padding']; + if (count($Distribution['version']) > 1) { + $ThisFileInfo['error'][] = 'Corrupt file - more than one MPEG version detected'; + } + if (count($Distribution['layer']) > 1) { + $ThisFileInfo['error'][] = 'Corrupt file - more than one MPEG layer detected'; + } + if (count($Distribution['frequency']) > 1) { + $ThisFileInfo['error'][] = 'Corrupt file - more than one MPEG sample rate detected'; + } + + + $bittotal = 0; + foreach ($Distribution['bitrate'] as $bitratevalue => $bitratecount) { + if ($bitratevalue != 'free') { + $bittotal += ($bitratevalue * $bitratecount); + } + } + $ThisFileInfo['mpeg']['audio']['frame_count'] = array_sum($Distribution['bitrate']); + if ($ThisFileInfo['mpeg']['audio']['frame_count'] == 0) { + $ThisFileInfo['error'][] = 'no MPEG audio frames found'; + return false; + } + $ThisFileInfo['mpeg']['audio']['bitrate'] = ($bittotal / $ThisFileInfo['mpeg']['audio']['frame_count']); + $ThisFileInfo['mpeg']['audio']['bitrate_mode'] = ((count($Distribution['bitrate']) > 0) ? 'vbr' : 'cbr'); + $ThisFileInfo['mpeg']['audio']['sample_rate'] = getid3_lib::array_max($Distribution['frequency'], true); + + $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['bitrate']; + $ThisFileInfo['audio']['bitrate_mode'] = $ThisFileInfo['mpeg']['audio']['bitrate_mode']; + $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['mpeg']['audio']['sample_rate']; + $ThisFileInfo['audio']['dataformat'] = 'mp'.getid3_lib::array_max($Distribution['layer'], true); + $ThisFileInfo['fileformat'] = $ThisFileInfo['audio']['dataformat']; + + return true; + } + + + function getOnlyMPEGaudioInfo($fd, &$ThisFileInfo, $avdataoffset, $BitrateHistogram=false) { + // looks for synch, decodes MPEG audio header + + fseek($fd, $avdataoffset, SEEK_SET); + $header = ''; + $SynchSeekOffset = 0; + + static $MPEGaudioVersionLookup; + static $MPEGaudioLayerLookup; + static $MPEGaudioBitrateLookup; + if (empty($MPEGaudioVersionLookup)) { + $MPEGaudioVersionLookup = getid3_mp3::MPEGaudioVersionArray(); + $MPEGaudioLayerLookup = getid3_mp3::MPEGaudioLayerArray(); + $MPEGaudioBitrateLookup = getid3_mp3::MPEGaudioBitrateArray(); + + } + + $header_len = strlen($header) - intval(round(GETID3_FREAD_BUFFER_SIZE / 2)); + while (true) { + + if (($SynchSeekOffset > $header_len) && (($avdataoffset + $SynchSeekOffset) < $ThisFileInfo['avdataend']) && !feof($fd)) { + + if ($SynchSeekOffset > 131072) { + // if a synch's not found within the first 128k bytes, then give up + $ThisFileInfo['error'][] = 'could not find valid MPEG audio synch within the first 128k bytes'; + if (isset($ThisFileInfo['audio']['bitrate'])) { + unset($ThisFileInfo['audio']['bitrate']); + } + if (isset($ThisFileInfo['mpeg']['audio'])) { + unset($ThisFileInfo['mpeg']['audio']); + } + if (empty($ThisFileInfo['mpeg'])) { + unset($ThisFileInfo['mpeg']); + } + return false; + + } elseif ($header .= fread($fd, GETID3_FREAD_BUFFER_SIZE)) { + + // great + $header_len = strlen($header) - intval(round(GETID3_FREAD_BUFFER_SIZE / 2)); + + } else { + + $ThisFileInfo['error'][] = 'could not find valid MPEG audio synch before end of file'; + if (isset($ThisFileInfo['audio']['bitrate'])) { + unset($ThisFileInfo['audio']['bitrate']); + } + if (isset($ThisFileInfo['mpeg']['audio'])) { + unset($ThisFileInfo['mpeg']['audio']); + } + if (isset($ThisFileInfo['mpeg']) && (!is_array($ThisFileInfo['mpeg']) || (count($ThisFileInfo['mpeg']) == 0))) { + unset($ThisFileInfo['mpeg']); + } + return false; + + } + } + + if (($SynchSeekOffset + 1) >= strlen($header)) { + $ThisFileInfo['error'][] = 'could not find valid MPEG synch before end of file'; + return false; + } + + if (($header{$SynchSeekOffset} == "\xFF") && ($header{($SynchSeekOffset + 1)} > "\xE0")) { // synch detected + + if (!isset($FirstFrameThisfileInfo) && !isset($ThisFileInfo['mpeg']['audio'])) { + $FirstFrameThisfileInfo = $ThisFileInfo; + $FirstFrameAVDataOffset = $avdataoffset + $SynchSeekOffset; + if (!getid3_mp3::decodeMPEGaudioHeader($fd, $avdataoffset + $SynchSeekOffset, $FirstFrameThisfileInfo, false)) { + // if this is the first valid MPEG-audio frame, save it in case it's a VBR header frame and there's + // garbage between this frame and a valid sequence of MPEG-audio frames, to be restored below + unset($FirstFrameThisfileInfo); + } + } + + $dummy = $ThisFileInfo; // only overwrite real data if valid header found + if (getid3_mp3::decodeMPEGaudioHeader($fd, $avdataoffset + $SynchSeekOffset, $dummy, true)) { + $ThisFileInfo = $dummy; + $ThisFileInfo['avdataoffset'] = $avdataoffset + $SynchSeekOffset; + switch ($ThisFileInfo['fileformat']) { + case '': + case 'id3': + case 'ape': + case 'mp3': + $ThisFileInfo['fileformat'] = 'mp3'; + $ThisFileInfo['audio']['dataformat'] = 'mp3'; + break; + } + if (isset($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode']) && ($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr')) { + if (!(abs($ThisFileInfo['audio']['bitrate'] - $FirstFrameThisfileInfo['audio']['bitrate']) <= 1)) { + // If there is garbage data between a valid VBR header frame and a sequence + // of valid MPEG-audio frames the VBR data is no longer discarded. + $ThisFileInfo = $FirstFrameThisfileInfo; + $ThisFileInfo['avdataoffset'] = $FirstFrameAVDataOffset; + $ThisFileInfo['fileformat'] = 'mp3'; + $ThisFileInfo['audio']['dataformat'] = 'mp3'; + $dummy = $ThisFileInfo; + unset($dummy['mpeg']['audio']); + $GarbageOffsetStart = $FirstFrameAVDataOffset + $FirstFrameThisfileInfo['mpeg']['audio']['framelength']; + $GarbageOffsetEnd = $avdataoffset + $SynchSeekOffset; + if (getid3_mp3::decodeMPEGaudioHeader($fd, $GarbageOffsetEnd, $dummy, true, true)) { + + $ThisFileInfo = $dummy; + $ThisFileInfo['avdataoffset'] = $GarbageOffsetEnd; + $ThisFileInfo['warning'][] = 'apparently-valid VBR header not used because could not find '.GETID3_MP3_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.'), but did find valid CBR stream starting at '.$GarbageOffsetEnd; + + } else { + + $ThisFileInfo['warning'][] = 'using data from VBR header even though could not find '.GETID3_MP3_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.')'; + + } + } + } + if (isset($ThisFileInfo['mpeg']['audio']['bitrate_mode']) && ($ThisFileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr') && !isset($ThisFileInfo['mpeg']['audio']['VBR_method'])) { + // VBR file with no VBR header + $BitrateHistogram = true; + } + + if ($BitrateHistogram) { + + $ThisFileInfo['mpeg']['audio']['stereo_distribution'] = array('stereo'=>0, 'joint stereo'=>0, 'dual channel'=>0, 'mono'=>0); + $ThisFileInfo['mpeg']['audio']['version_distribution'] = array('1'=>0, '2'=>0, '2.5'=>0); + + if ($ThisFileInfo['mpeg']['audio']['version'] == '1') { + if ($ThisFileInfo['mpeg']['audio']['layer'] == 3) { + $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 40000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 320000=>0); + } elseif ($ThisFileInfo['mpeg']['audio']['layer'] == 2) { + $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 320000=>0, 384000=>0); + } elseif ($ThisFileInfo['mpeg']['audio']['layer'] == 1) { + $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 64000=>0, 96000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 288000=>0, 320000=>0, 352000=>0, 384000=>0, 416000=>0, 448000=>0); + } + } elseif ($ThisFileInfo['mpeg']['audio']['layer'] == 1) { + $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 144000=>0, 160000=>0, 176000=>0, 192000=>0, 224000=>0, 256000=>0); + } else { + $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 8000=>0, 16000=>0, 24000=>0, 32000=>0, 40000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 144000=>0, 160000=>0); + } + + $dummy = array('error'=>$ThisFileInfo['error'], 'warning'=>$ThisFileInfo['warning'], 'avdataend'=>$ThisFileInfo['avdataend'], 'avdataoffset'=>$ThisFileInfo['avdataoffset']); + $synchstartoffset = $ThisFileInfo['avdataoffset']; + + $FastMode = false; + $SynchErrorsFound = 0; + while (getid3_mp3::decodeMPEGaudioHeader($fd, $synchstartoffset, $dummy, false, false, $FastMode)) { + $FastMode = true; + $thisframebitrate = $MPEGaudioBitrateLookup[$MPEGaudioVersionLookup[$dummy['mpeg']['audio']['raw']['version']]][$MPEGaudioLayerLookup[$dummy['mpeg']['audio']['raw']['layer']]][$dummy['mpeg']['audio']['raw']['bitrate']]; + + if (empty($dummy['mpeg']['audio']['framelength'])) { + $SynchErrorsFound++; + } else { + $ThisFileInfo['mpeg']['audio']['bitrate_distribution'][$thisframebitrate]++; + $ThisFileInfo['mpeg']['audio']['stereo_distribution'][$dummy['mpeg']['audio']['channelmode']]++; + $ThisFileInfo['mpeg']['audio']['version_distribution'][$dummy['mpeg']['audio']['version']]++; + + $synchstartoffset += $dummy['mpeg']['audio']['framelength']; + } + } + if ($SynchErrorsFound > 0) { + $ThisFileInfo['warning'][] = 'Found '.$SynchErrorsFound.' synch errors in histogram analysis'; + //return false; + } + + $bittotal = 0; + $framecounter = 0; + foreach ($ThisFileInfo['mpeg']['audio']['bitrate_distribution'] as $bitratevalue => $bitratecount) { + $framecounter += $bitratecount; + if ($bitratevalue != 'free') { + $bittotal += ($bitratevalue * $bitratecount); + } + } + if ($framecounter == 0) { + $ThisFileInfo['error'][] = 'Corrupt MP3 file: framecounter == zero'; + return false; + } + $ThisFileInfo['mpeg']['audio']['frame_count'] = $framecounter; + $ThisFileInfo['mpeg']['audio']['bitrate'] = ($bittotal / $framecounter); + + $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['bitrate']; + + + // Definitively set VBR vs CBR, even if the Xing/LAME/VBRI header says differently + $distinct_bitrates = 0; + foreach ($ThisFileInfo['mpeg']['audio']['bitrate_distribution'] as $bitrate_value => $bitrate_count) { + if ($bitrate_count > 0) { + $distinct_bitrates++; + } + } + if ($distinct_bitrates > 1) { + $ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'vbr'; + } else { + $ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'cbr'; + } + $ThisFileInfo['audio']['bitrate_mode'] = $ThisFileInfo['mpeg']['audio']['bitrate_mode']; + + } + + break; // exit while() + } + } + + $SynchSeekOffset++; + if (($avdataoffset + $SynchSeekOffset) >= $ThisFileInfo['avdataend']) { + // end of file/data + + if (empty($ThisFileInfo['mpeg']['audio'])) { + + $ThisFileInfo['error'][] = 'could not find valid MPEG synch before end of file'; + if (isset($ThisFileInfo['audio']['bitrate'])) { + unset($ThisFileInfo['audio']['bitrate']); + } + if (isset($ThisFileInfo['mpeg']['audio'])) { + unset($ThisFileInfo['mpeg']['audio']); + } + if (isset($ThisFileInfo['mpeg']) && (!is_array($ThisFileInfo['mpeg']) || empty($ThisFileInfo['mpeg']))) { + unset($ThisFileInfo['mpeg']); + } + return false; + + } + break; + } + + } + $ThisFileInfo['audio']['channels'] = $ThisFileInfo['mpeg']['audio']['channels']; + $ThisFileInfo['audio']['channelmode'] = $ThisFileInfo['mpeg']['audio']['channelmode']; + $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['mpeg']['audio']['sample_rate']; + return true; + } + + + function MPEGaudioVersionArray() { + static $MPEGaudioVersion = array('2.5', false, '2', '1'); + return $MPEGaudioVersion; + } + + function MPEGaudioLayerArray() { + static $MPEGaudioLayer = array(false, 3, 2, 1); + return $MPEGaudioLayer; + } + + function MPEGaudioBitrateArray() { + static $MPEGaudioBitrate; + if (empty($MPEGaudioBitrate)) { + $MPEGaudioBitrate = array ( + '1' => array (1 => array('free', 32000, 64000, 96000, 128000, 160000, 192000, 224000, 256000, 288000, 320000, 352000, 384000, 416000, 448000), + 2 => array('free', 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 384000), + 3 => array('free', 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000) + ), + + '2' => array (1 => array('free', 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, 176000, 192000, 224000, 256000), + 2 => array('free', 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000), + ) + ); + $MPEGaudioBitrate['2'][3] = $MPEGaudioBitrate['2'][2]; + $MPEGaudioBitrate['2.5'] = $MPEGaudioBitrate['2']; + } + return $MPEGaudioBitrate; + } + + function MPEGaudioFrequencyArray() { + static $MPEGaudioFrequency; + if (empty($MPEGaudioFrequency)) { + $MPEGaudioFrequency = array ( + '1' => array(44100, 48000, 32000), + '2' => array(22050, 24000, 16000), + '2.5' => array(11025, 12000, 8000) + ); + } + return $MPEGaudioFrequency; + } + + function MPEGaudioChannelModeArray() { + static $MPEGaudioChannelMode = array('stereo', 'joint stereo', 'dual channel', 'mono'); + return $MPEGaudioChannelMode; + } + + function MPEGaudioModeExtensionArray() { + static $MPEGaudioModeExtension; + if (empty($MPEGaudioModeExtension)) { + $MPEGaudioModeExtension = array ( + 1 => array('4-31', '8-31', '12-31', '16-31'), + 2 => array('4-31', '8-31', '12-31', '16-31'), + 3 => array('', 'IS', 'MS', 'IS+MS') + ); + } + return $MPEGaudioModeExtension; + } + + function MPEGaudioEmphasisArray() { + static $MPEGaudioEmphasis = array('none', '50/15ms', false, 'CCIT J.17'); + return $MPEGaudioEmphasis; + } + + function MPEGaudioHeaderBytesValid($head4, $allowBitrate15=false) { + return getid3_mp3::MPEGaudioHeaderValid(getid3_mp3::MPEGaudioHeaderDecode($head4), false, $allowBitrate15); + } + + function MPEGaudioHeaderValid($rawarray, $echoerrors=false, $allowBitrate15=false) { + if (($rawarray['synch'] & 0x0FFE) != 0x0FFE) { + return false; + } + + static $MPEGaudioVersionLookup; + static $MPEGaudioLayerLookup; + static $MPEGaudioBitrateLookup; + static $MPEGaudioFrequencyLookup; + static $MPEGaudioChannelModeLookup; + static $MPEGaudioModeExtensionLookup; + static $MPEGaudioEmphasisLookup; + if (empty($MPEGaudioVersionLookup)) { + $MPEGaudioVersionLookup = getid3_mp3::MPEGaudioVersionArray(); + $MPEGaudioLayerLookup = getid3_mp3::MPEGaudioLayerArray(); + $MPEGaudioBitrateLookup = getid3_mp3::MPEGaudioBitrateArray(); + $MPEGaudioFrequencyLookup = getid3_mp3::MPEGaudioFrequencyArray(); + $MPEGaudioChannelModeLookup = getid3_mp3::MPEGaudioChannelModeArray(); + $MPEGaudioModeExtensionLookup = getid3_mp3::MPEGaudioModeExtensionArray(); + $MPEGaudioEmphasisLookup = getid3_mp3::MPEGaudioEmphasisArray(); + } + + if (isset($MPEGaudioVersionLookup[$rawarray['version']])) { + $decodedVersion = $MPEGaudioVersionLookup[$rawarray['version']]; + } else { + echo ($echoerrors ? "\n".'invalid Version ('.$rawarray['version'].')' : ''); + return false; + } + if (isset($MPEGaudioLayerLookup[$rawarray['layer']])) { + $decodedLayer = $MPEGaudioLayerLookup[$rawarray['layer']]; + } else { + echo ($echoerrors ? "\n".'invalid Layer ('.$rawarray['layer'].')' : ''); + return false; + } + if (!isset($MPEGaudioBitrateLookup[$decodedVersion][$decodedLayer][$rawarray['bitrate']])) { + echo ($echoerrors ? "\n".'invalid Bitrate ('.$rawarray['bitrate'].')' : ''); + if ($rawarray['bitrate'] == 15) { + // known issue in LAME 3.90 - 3.93.1 where free-format has bitrate ID of 15 instead of 0 + // let it go through here otherwise file will not be identified + if (!$allowBitrate15) { + return false; + } + } else { + return false; + } + } + if (!isset($MPEGaudioFrequencyLookup[$decodedVersion][$rawarray['sample_rate']])) { + echo ($echoerrors ? "\n".'invalid Frequency ('.$rawarray['sample_rate'].')' : ''); + return false; + } + if (!isset($MPEGaudioChannelModeLookup[$rawarray['channelmode']])) { + echo ($echoerrors ? "\n".'invalid ChannelMode ('.$rawarray['channelmode'].')' : ''); + return false; + } + if (!isset($MPEGaudioModeExtensionLookup[$decodedLayer][$rawarray['modeextension']])) { + echo ($echoerrors ? "\n".'invalid Mode Extension ('.$rawarray['modeextension'].')' : ''); + return false; + } + if (!isset($MPEGaudioEmphasisLookup[$rawarray['emphasis']])) { + echo ($echoerrors ? "\n".'invalid Emphasis ('.$rawarray['emphasis'].')' : ''); + return false; + } + // These are just either set or not set, you can't mess that up :) + // $rawarray['protection']; + // $rawarray['padding']; + // $rawarray['private']; + // $rawarray['copyright']; + // $rawarray['original']; + + return true; + } + + function MPEGaudioHeaderDecode($Header4Bytes) { + // AAAA AAAA AAAB BCCD EEEE FFGH IIJJ KLMM + // A - Frame sync (all bits set) + // B - MPEG Audio version ID + // C - Layer description + // D - Protection bit + // E - Bitrate index + // F - Sampling rate frequency index + // G - Padding bit + // H - Private bit + // I - Channel Mode + // J - Mode extension (Only if Joint stereo) + // K - Copyright + // L - Original + // M - Emphasis + + if (strlen($Header4Bytes) != 4) { + return false; + } + + $MPEGrawHeader['synch'] = (getid3_lib::BigEndian2Int(substr($Header4Bytes, 0, 2)) & 0xFFE0) >> 4; + $MPEGrawHeader['version'] = (ord($Header4Bytes{1}) & 0x18) >> 3; // BB + $MPEGrawHeader['layer'] = (ord($Header4Bytes{1}) & 0x06) >> 1; // CC + $MPEGrawHeader['protection'] = (ord($Header4Bytes{1}) & 0x01); // D + $MPEGrawHeader['bitrate'] = (ord($Header4Bytes{2}) & 0xF0) >> 4; // EEEE + $MPEGrawHeader['sample_rate'] = (ord($Header4Bytes{2}) & 0x0C) >> 2; // FF + $MPEGrawHeader['padding'] = (ord($Header4Bytes{2}) & 0x02) >> 1; // G + $MPEGrawHeader['private'] = (ord($Header4Bytes{2}) & 0x01); // H + $MPEGrawHeader['channelmode'] = (ord($Header4Bytes{3}) & 0xC0) >> 6; // II + $MPEGrawHeader['modeextension'] = (ord($Header4Bytes{3}) & 0x30) >> 4; // JJ + $MPEGrawHeader['copyright'] = (ord($Header4Bytes{3}) & 0x08) >> 3; // K + $MPEGrawHeader['original'] = (ord($Header4Bytes{3}) & 0x04) >> 2; // L + $MPEGrawHeader['emphasis'] = (ord($Header4Bytes{3}) & 0x03); // MM + + return $MPEGrawHeader; + } + + function MPEGaudioFrameLength(&$bitrate, &$version, &$layer, $padding, &$samplerate) { + static $AudioFrameLengthCache = array(); + + if (!isset($AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate])) { + $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate] = false; + if ($bitrate != 'free') { + + if ($version == '1') { + + if ($layer == '1') { + + // For Layer I slot is 32 bits long + $FrameLengthCoefficient = 48; + $SlotLength = 4; + + } else { // Layer 2 / 3 + + // for Layer 2 and Layer 3 slot is 8 bits long. + $FrameLengthCoefficient = 144; + $SlotLength = 1; + + } + + } else { // MPEG-2 / MPEG-2.5 + + if ($layer == '1') { + + // For Layer I slot is 32 bits long + $FrameLengthCoefficient = 24; + $SlotLength = 4; + + } elseif ($layer == '2') { + + // for Layer 2 and Layer 3 slot is 8 bits long. + $FrameLengthCoefficient = 144; + $SlotLength = 1; + + } else { // layer 3 + + // for Layer 2 and Layer 3 slot is 8 bits long. + $FrameLengthCoefficient = 72; + $SlotLength = 1; + + } + + } + + // FrameLengthInBytes = ((Coefficient * BitRate) / SampleRate) + Padding + if ($samplerate > 0) { + $NewFramelength = ($FrameLengthCoefficient * $bitrate) / $samplerate; + $NewFramelength = floor($NewFramelength / $SlotLength) * $SlotLength; // round to next-lower multiple of SlotLength (1 byte for Layer 2/3, 4 bytes for Layer I) + if ($padding) { + $NewFramelength += $SlotLength; + } + $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate] = (int) $NewFramelength; + } + } + } + return $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate]; + } + + function ClosestStandardMP3Bitrate($bitrate) { + static $StandardBitrates = array(320000, 256000, 224000, 192000, 160000, 128000, 112000, 96000, 80000, 64000, 56000, 48000, 40000, 32000, 24000, 16000, 8000); + static $BitrateTable = array(0=>'-'); + $roundbitrate = intval(round($bitrate, -3)); + if (!isset($BitrateTable[$roundbitrate])) { + if ($roundbitrate > 320000) { + $BitrateTable[$roundbitrate] = round($bitrate, -4); + } else { + $LastBitrate = 320000; + foreach ($StandardBitrates as $StandardBitrate) { + $BitrateTable[$roundbitrate] = $StandardBitrate; + if ($roundbitrate >= $StandardBitrate - (($LastBitrate - $StandardBitrate) / 2)) { + break; + } + $LastBitrate = $StandardBitrate; + } + } + } + return $BitrateTable[$roundbitrate]; + } + + function XingVBRidOffset($version, $channelmode) { + static $XingVBRidOffsetCache = array(); + if (empty($XingVBRidOffset)) { + $XingVBRidOffset = array ( + '1' => array ('mono' => 0x15, // 4 + 17 = 21 + 'stereo' => 0x24, // 4 + 32 = 36 + 'joint stereo' => 0x24, + 'dual channel' => 0x24 + ), + + '2' => array ('mono' => 0x0D, // 4 + 9 = 13 + 'stereo' => 0x15, // 4 + 17 = 21 + 'joint stereo' => 0x15, + 'dual channel' => 0x15 + ), + + '2.5' => array ('mono' => 0x15, + 'stereo' => 0x15, + 'joint stereo' => 0x15, + 'dual channel' => 0x15 + ) + ); + } + return $XingVBRidOffset[$version][$channelmode]; + } + + function LAMEvbrMethodLookup($VBRmethodID) { + static $LAMEvbrMethodLookup = array( + 0x00 => 'unknown', + 0x01 => 'cbr', + 0x02 => 'abr', + 0x03 => 'vbr-old / vbr-rh', + 0x04 => 'vbr-new / vbr-mtrh', + 0x05 => 'vbr-mt', + 0x06 => 'Full VBR Method 4', + 0x08 => 'constant bitrate 2 pass', + 0x09 => 'abr 2 pass', + 0x0F => 'reserved' + ); + return (isset($LAMEvbrMethodLookup[$VBRmethodID]) ? $LAMEvbrMethodLookup[$VBRmethodID] : ''); + } + + function LAMEmiscStereoModeLookup($StereoModeID) { + static $LAMEmiscStereoModeLookup = array( + 0 => 'mono', + 1 => 'stereo', + 2 => 'dual mono', + 3 => 'joint stereo', + 4 => 'forced stereo', + 5 => 'auto', + 6 => 'intensity stereo', + 7 => 'other' + ); + return (isset($LAMEmiscStereoModeLookup[$StereoModeID]) ? $LAMEmiscStereoModeLookup[$StereoModeID] : ''); + } + + function LAMEmiscSourceSampleFrequencyLookup($SourceSampleFrequencyID) { + static $LAMEmiscSourceSampleFrequencyLookup = array( + 0 => '<= 32 kHz', + 1 => '44.1 kHz', + 2 => '48 kHz', + 3 => '> 48kHz' + ); + return (isset($LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID]) ? $LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID] : ''); + } + + function LAMEsurroundInfoLookup($SurroundInfoID) { + static $LAMEsurroundInfoLookup = array( + 0 => 'no surround info', + 1 => 'DPL encoding', + 2 => 'DPL2 encoding', + 3 => 'Ambisonic encoding' + ); + return (isset($LAMEsurroundInfoLookup[$SurroundInfoID]) ? $LAMEsurroundInfoLookup[$SurroundInfoID] : 'reserved'); + } + + function LAMEpresetUsedLookup($LAMEtag) { + + if ($LAMEtag['preset_used_id'] == 0) { + // no preset used (LAME >=3.93) + // no preset recorded (LAME <3.93) + return ''; + } + $LAMEpresetUsedLookup = array(); + + ///// THIS PART CANNOT BE STATIC . + for ($i = 8; $i <= 320; $i++) { + switch ($LAMEtag['vbr_method']) { + case 'cbr': + $LAMEpresetUsedLookup[$i] = '--alt-preset '.$LAMEtag['vbr_method'].' '.$i; + break; + case 'abr': + default: // other VBR modes shouldn't be here(?) + $LAMEpresetUsedLookup[$i] = '--alt-preset '.$i; + break; + } + } + + // named old-style presets (studio, phone, voice, etc) are handled in GuessEncoderOptions() + + // named alt-presets + $LAMEpresetUsedLookup[1000] = '--r3mix'; + $LAMEpresetUsedLookup[1001] = '--alt-preset standard'; + $LAMEpresetUsedLookup[1002] = '--alt-preset extreme'; + $LAMEpresetUsedLookup[1003] = '--alt-preset insane'; + $LAMEpresetUsedLookup[1004] = '--alt-preset fast standard'; + $LAMEpresetUsedLookup[1005] = '--alt-preset fast extreme'; + $LAMEpresetUsedLookup[1006] = '--alt-preset medium'; + $LAMEpresetUsedLookup[1007] = '--alt-preset fast medium'; + + // LAME 3.94 additions/changes + $LAMEpresetUsedLookup[1010] = '--preset portable'; // 3.94a15 Oct 21 2003 + $LAMEpresetUsedLookup[1015] = '--preset radio'; // 3.94a15 Oct 21 2003 + + $LAMEpresetUsedLookup[320] = '--preset insane'; // 3.94a15 Nov 12 2003 + $LAMEpresetUsedLookup[410] = '-V9'; + $LAMEpresetUsedLookup[420] = '-V8'; + $LAMEpresetUsedLookup[440] = '-V6'; + $LAMEpresetUsedLookup[430] = '--preset radio'; // 3.94a15 Nov 12 2003 + $LAMEpresetUsedLookup[450] = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'portable'; // 3.94a15 Nov 12 2003 + $LAMEpresetUsedLookup[460] = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'medium'; // 3.94a15 Nov 12 2003 + $LAMEpresetUsedLookup[470] = '--r3mix'; // 3.94b1 Dec 18 2003 + $LAMEpresetUsedLookup[480] = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'standard'; // 3.94a15 Nov 12 2003 + $LAMEpresetUsedLookup[490] = '-V1'; + $LAMEpresetUsedLookup[500] = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'extreme'; // 3.94a15 Nov 12 2003 + + return (isset($LAMEpresetUsedLookup[$LAMEtag['preset_used_id']]) ? $LAMEpresetUsedLookup[$LAMEtag['preset_used_id']] : 'new/unknown preset: '.$LAMEtag['preset_used_id'].' - report to info@getid3.org'); + } + +} + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.audio.mpc.php b/includes/getid3/getid3/module.audio.mpc.php new file mode 100644 index 0000000..d0a7202 --- /dev/null +++ b/includes/getid3/getid3/module.audio.mpc.php @@ -0,0 +1,296 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio.mpc.php // +// module for analyzing Musepack/MPEG+ Audio files // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_mpc +{ + + function getid3_mpc(&$fd, &$ThisFileInfo) { + // http://www.uni-jena.de/~pfk/mpp/sv8/header.html + + $ThisFileInfo['mpc']['header'] = array(); + $thisfile_mpc_header = &$ThisFileInfo['mpc']['header']; + + $ThisFileInfo['fileformat'] = 'mpc'; + $ThisFileInfo['audio']['dataformat'] = 'mpc'; + $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; + $ThisFileInfo['audio']['channels'] = 2; // the format appears to be hardcoded for stereo only + $ThisFileInfo['audio']['lossless'] = false; + + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + + $thisfile_mpc_header['size'] = 28; + $MPCheaderData = fread($fd, $thisfile_mpc_header['size']); + $offset = 0; + + if (substr($MPCheaderData, $offset, 3) == 'MP+') { + + // great, this is SV7+ + $thisfile_mpc_header['raw']['preamble'] = substr($MPCheaderData, $offset, 3); // should be 'MP+' + $offset += 3; + + } elseif (preg_match('/^[\x00\x01\x10\x11\x40\x41\x50\x51\x80\x81\x90\x91\xC0\xC1\xD0\xD1][\x20-37][\x00\x20\x40\x60\x80\xA0\xC0\xE0]/s', substr($MPCheaderData, 0, 4))) { + + // this is SV4 - SV6, handle seperately + $thisfile_mpc_header['size'] = 8; + + // add size of file header to avdataoffset - calc bitrate correctly + MD5 data + $ThisFileInfo['avdataoffset'] += $thisfile_mpc_header['size']; + + // Most of this code adapted from Jurgen Faul's MPEGplus source code - thanks Jurgen! :) + $HeaderDWORD[0] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, 0, 4)); + $HeaderDWORD[1] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, 4, 4)); + + + // DDDD DDDD CCCC CCCC BBBB BBBB AAAA AAAA + // aaaa aaaa abcd dddd dddd deee eeff ffff + // + // a = bitrate = anything + // b = IS = anything + // c = MS = anything + // d = streamversion = 0000000004 or 0000000005 or 0000000006 + // e = maxband = anything + // f = blocksize = 000001 for SV5+, anything(?) for SV4 + + $thisfile_mpc_header['target_bitrate'] = (($HeaderDWORD[0] & 0xFF800000) >> 23); + $thisfile_mpc_header['intensity_stereo'] = (bool) (($HeaderDWORD[0] & 0x00400000) >> 22); + $thisfile_mpc_header['mid-side_stereo'] = (bool) (($HeaderDWORD[0] & 0x00200000) >> 21); + $thisfile_mpc_header['stream_major_version'] = ($HeaderDWORD[0] & 0x001FF800) >> 11; + $thisfile_mpc_header['stream_minor_version'] = 0; // no sub-version numbers before SV7 + $thisfile_mpc_header['max_band'] = ($HeaderDWORD[0] & 0x000007C0) >> 6; // related to lowpass frequency, not sure how it translates exactly + $thisfile_mpc_header['block_size'] = ($HeaderDWORD[0] & 0x0000003F); + + switch ($thisfile_mpc_header['stream_major_version']) { + case 4: + $thisfile_mpc_header['frame_count'] = ($HeaderDWORD[1] >> 16); + break; + + case 5: + case 6: + $thisfile_mpc_header['frame_count'] = $HeaderDWORD[1]; + break; + + default: + $ThisFileInfo['error'] = 'Expecting 4, 5 or 6 in version field, found '.$thisfile_mpc_header['stream_major_version'].' instead'; + unset($ThisFileInfo['mpc']); + return false; + break; + } + + if (($thisfile_mpc_header['stream_major_version'] > 4) && ($thisfile_mpc_header['block_size'] != 1)) { + $ThisFileInfo['warning'][] = 'Block size expected to be 1, actual value found: '.$thisfile_mpc_header['block_size']; + } + + $thisfile_mpc_header['sample_rate'] = 44100; // AB: used by all files up to SV7 + $ThisFileInfo['audio']['sample_rate'] = $thisfile_mpc_header['sample_rate']; + $thisfile_mpc_header['samples'] = $thisfile_mpc_header['frame_count'] * 1152 * $ThisFileInfo['audio']['channels']; + + if ($thisfile_mpc_header['target_bitrate'] == 0) { + $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; + } else { + $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; + } + + $ThisFileInfo['mpc']['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 * 44100 / $thisfile_mpc_header['frame_count'] / 1152; + $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['mpc']['bitrate']; + $ThisFileInfo['audio']['encoder'] = 'SV'.$thisfile_mpc_header['stream_major_version']; + + return true; + + } else { + + $ThisFileInfo['error'][] = 'Expecting "MP+" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($MPCheaderData, $offset, 3).'"'; + unset($ThisFileInfo['fileformat']); + unset($ThisFileInfo['mpc']); + return false; + + } + + // Continue with SV7+ handling + $StreamVersionByte = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 1)); + $offset += 1; + $thisfile_mpc_header['stream_major_version'] = ($StreamVersionByte & 0x0F); + $thisfile_mpc_header['stream_minor_version'] = ($StreamVersionByte & 0xF0) >> 4; + $thisfile_mpc_header['frame_count'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 4)); + $offset += 4; + + switch ($thisfile_mpc_header['stream_major_version']) { + case 7: + //$ThisFileInfo['fileformat'] = 'SV7'; + break; + + default: + $ThisFileInfo['error'][] = 'Only Musepack SV7 supported'; + return false; + } + + $FlagsDWORD1 = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 4)); + $offset += 4; + $thisfile_mpc_header['intensity_stereo'] = (bool) (($FlagsDWORD1 & 0x80000000) >> 31); + $thisfile_mpc_header['mid_side_stereo'] = (bool) (($FlagsDWORD1 & 0x40000000) >> 30); + $thisfile_mpc_header['max_subband'] = ($FlagsDWORD1 & 0x3F000000) >> 24; + $thisfile_mpc_header['raw']['profile'] = ($FlagsDWORD1 & 0x00F00000) >> 20; + $thisfile_mpc_header['begin_loud'] = (bool) (($FlagsDWORD1 & 0x00080000) >> 19); + $thisfile_mpc_header['end_loud'] = (bool) (($FlagsDWORD1 & 0x00040000) >> 18); + $thisfile_mpc_header['raw']['sample_rate'] = ($FlagsDWORD1 & 0x00030000) >> 16; + $thisfile_mpc_header['max_level'] = ($FlagsDWORD1 & 0x0000FFFF); + + $thisfile_mpc_header['raw']['title_peak'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 2)); + $offset += 2; + $thisfile_mpc_header['raw']['title_gain'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 2), true); + $offset += 2; + + $thisfile_mpc_header['raw']['album_peak'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 2)); + $offset += 2; + $thisfile_mpc_header['raw']['album_gain'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 2), true); + $offset += 2; + + $FlagsDWORD2 = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 4)); + $offset += 4; + $thisfile_mpc_header['true_gapless'] = (bool) (($FlagsDWORD2 & 0x80000000) >> 31); + $thisfile_mpc_header['last_frame_length'] = ($FlagsDWORD2 & 0x7FF00000) >> 20; + + + $thisfile_mpc_header['raw']['not_sure_what'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 3)); + $offset += 3; + $thisfile_mpc_header['raw']['encoder_version'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 1)); + $offset += 1; + + $thisfile_mpc_header['profile'] = $this->MPCprofileNameLookup($thisfile_mpc_header['raw']['profile']); + $thisfile_mpc_header['sample_rate'] = $this->MPCfrequencyLookup($thisfile_mpc_header['raw']['sample_rate']); + if ($thisfile_mpc_header['sample_rate'] == 0) { + $ThisFileInfo['error'][] = 'Corrupt MPC file: frequency == zero'; + return false; + } + $ThisFileInfo['audio']['sample_rate'] = $thisfile_mpc_header['sample_rate']; + $thisfile_mpc_header['samples'] = ((($thisfile_mpc_header['frame_count'] - 1) * 1152) + $thisfile_mpc_header['last_frame_length']) * $ThisFileInfo['audio']['channels']; + + $ThisFileInfo['playtime_seconds'] = ($thisfile_mpc_header['samples'] / $ThisFileInfo['audio']['channels']) / $ThisFileInfo['audio']['sample_rate']; + if ($ThisFileInfo['playtime_seconds'] == 0) { + $ThisFileInfo['error'][] = 'Corrupt MPC file: playtime_seconds == zero'; + return false; + } + + // add size of file header to avdataoffset - calc bitrate correctly + MD5 data + $ThisFileInfo['avdataoffset'] += $thisfile_mpc_header['size']; + + $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; + + $thisfile_mpc_header['title_peak'] = $thisfile_mpc_header['raw']['title_peak']; + $thisfile_mpc_header['title_peak_db'] = $this->MPCpeakDBLookup($thisfile_mpc_header['title_peak']); + if ($thisfile_mpc_header['raw']['title_gain'] < 0) { + $thisfile_mpc_header['title_gain_db'] = (float) (32768 + $thisfile_mpc_header['raw']['title_gain']) / -100; + } else { + $thisfile_mpc_header['title_gain_db'] = (float) $thisfile_mpc_header['raw']['title_gain'] / 100; + } + + $thisfile_mpc_header['album_peak'] = $thisfile_mpc_header['raw']['album_peak']; + $thisfile_mpc_header['album_peak_db'] = $this->MPCpeakDBLookup($thisfile_mpc_header['album_peak']); + if ($thisfile_mpc_header['raw']['album_gain'] < 0) { + $thisfile_mpc_header['album_gain_db'] = (float) (32768 + $thisfile_mpc_header['raw']['album_gain']) / -100; + } else { + $thisfile_mpc_header['album_gain_db'] = (float) $thisfile_mpc_header['raw']['album_gain'] / 100;; + } + $thisfile_mpc_header['encoder_version'] = $this->MPCencoderVersionLookup($thisfile_mpc_header['raw']['encoder_version']); + + $ThisFileInfo['replay_gain']['track']['adjustment'] = $thisfile_mpc_header['title_gain_db']; + $ThisFileInfo['replay_gain']['album']['adjustment'] = $thisfile_mpc_header['album_gain_db']; + + if ($thisfile_mpc_header['title_peak'] > 0) { + $ThisFileInfo['replay_gain']['track']['peak'] = $thisfile_mpc_header['title_peak']; + } elseif (round($thisfile_mpc_header['max_level'] * 1.18) > 0) { + $ThisFileInfo['replay_gain']['track']['peak'] = getid3_lib::CastAsInt(round($thisfile_mpc_header['max_level'] * 1.18)); // why? I don't know - see mppdec.c + } + if ($thisfile_mpc_header['album_peak'] > 0) { + $ThisFileInfo['replay_gain']['album']['peak'] = $thisfile_mpc_header['album_peak']; + } + + //$ThisFileInfo['audio']['encoder'] = 'SV'.$thisfile_mpc_header['stream_major_version'].'.'.$thisfile_mpc_header['stream_minor_version'].', '.$thisfile_mpc_header['encoder_version']; + $ThisFileInfo['audio']['encoder'] = $thisfile_mpc_header['encoder_version']; + $ThisFileInfo['audio']['encoder_options'] = $thisfile_mpc_header['profile']; + + return true; + } + + function MPCprofileNameLookup($profileid) { + static $MPCprofileNameLookup = array( + 0 => 'no profile', + 1 => 'Experimental', + 2 => 'unused', + 3 => 'unused', + 4 => 'unused', + 5 => 'below Telephone (q = 0.0)', + 6 => 'below Telephone (q = 1.0)', + 7 => 'Telephone (q = 2.0)', + 8 => 'Thumb (q = 3.0)', + 9 => 'Radio (q = 4.0)', + 10 => 'Standard (q = 5.0)', + 11 => 'Extreme (q = 6.0)', + 12 => 'Insane (q = 7.0)', + 13 => 'BrainDead (q = 8.0)', + 14 => 'above BrainDead (q = 9.0)', + 15 => 'above BrainDead (q = 10.0)' + ); + return (isset($MPCprofileNameLookup[$profileid]) ? $MPCprofileNameLookup[$profileid] : 'invalid'); + } + + function MPCfrequencyLookup($frequencyid) { + static $MPCfrequencyLookup = array( + 0 => 44100, + 1 => 48000, + 2 => 37800, + 3 => 32000 + ); + return (isset($MPCfrequencyLookup[$frequencyid]) ? $MPCfrequencyLookup[$frequencyid] : 'invalid'); + } + + function MPCpeakDBLookup($intvalue) { + if ($intvalue > 0) { + return ((log10($intvalue) / log10(2)) - 15) * 6; + } + return false; + } + + function MPCencoderVersionLookup($encoderversion) { + //Encoder version * 100 (106 = 1.06) + //EncoderVersion % 10 == 0 Release (1.0) + //EncoderVersion % 2 == 0 Beta (1.06) + //EncoderVersion % 2 == 1 Alpha (1.05a...z) + + if ($encoderversion == 0) { + // very old version, not known exactly which + return 'Buschmann v1.7.0-v1.7.9 or Klemm v0.90-v1.05'; + } + + if (($encoderversion % 10) == 0) { + + // release version + return number_format($encoderversion / 100, 2); + + } elseif (($encoderversion % 2) == 0) { + + // beta version + return number_format($encoderversion / 100, 2).' beta'; + + } + + // alpha version + return number_format($encoderversion / 100, 2).' alpha'; + } + +} + + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.audio.ogg.php b/includes/getid3/getid3/module.audio.ogg.php new file mode 100644 index 0000000..f722b33 --- /dev/null +++ b/includes/getid3/getid3/module.audio.ogg.php @@ -0,0 +1,543 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio.ogg.php // +// module for analyzing Ogg Vorbis, OggFLAC and Speex files // +// dependencies: module.audio.flac.php // +// /// +///////////////////////////////////////////////////////////////// + +getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.flac.php', __FILE__, true); + +class getid3_ogg +{ + + function getid3_ogg(&$fd, &$ThisFileInfo) { + + $ThisFileInfo['fileformat'] = 'ogg'; + + // Warn about illegal tags - only vorbiscomments are allowed + if (isset($ThisFileInfo['id3v2'])) { + $ThisFileInfo['warning'][] = 'Illegal ID3v2 tag present.'; + } + if (isset($ThisFileInfo['id3v1'])) { + $ThisFileInfo['warning'][] = 'Illegal ID3v1 tag present.'; + } + if (isset($ThisFileInfo['ape'])) { + $ThisFileInfo['warning'][] = 'Illegal APE tag present.'; + } + + + // Page 1 - Stream Header + + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + + $oggpageinfo = getid3_ogg::ParseOggPageHeader($fd); + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo; + + if (ftell($fd) >= GETID3_FREAD_BUFFER_SIZE) { + $ThisFileInfo['error'][] = 'Could not find start of Ogg page in the first '.GETID3_FREAD_BUFFER_SIZE.' bytes (this might not be an Ogg-Vorbis file?)'; + unset($ThisFileInfo['fileformat']); + unset($ThisFileInfo['ogg']); + return false; + } + + $filedata = fread($fd, $oggpageinfo['page_length']); + $filedataoffset = 0; + + if (substr($filedata, 0, 4) == 'fLaC') { + + $ThisFileInfo['audio']['dataformat'] = 'flac'; + $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; + $ThisFileInfo['audio']['lossless'] = true; + + } elseif (substr($filedata, 1, 6) == 'vorbis') { + + $ThisFileInfo['audio']['dataformat'] = 'vorbis'; + $ThisFileInfo['audio']['lossless'] = false; + + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); + $filedataoffset += 1; + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, $filedataoffset, 6); // hard-coded to 'vorbis' + $filedataoffset += 6; + $ThisFileInfo['ogg']['bitstreamversion'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $ThisFileInfo['ogg']['numberofchannels'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); + $filedataoffset += 1; + $ThisFileInfo['audio']['channels'] = $ThisFileInfo['ogg']['numberofchannels']; + $ThisFileInfo['ogg']['samplerate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + if ($ThisFileInfo['ogg']['samplerate'] == 0) { + $ThisFileInfo['error'][] = 'Corrupt Ogg file: sample rate == zero'; + return false; + } + $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['ogg']['samplerate']; + $ThisFileInfo['ogg']['samples'] = 0; // filled in later + $ThisFileInfo['ogg']['bitrate_average'] = 0; // filled in later + $ThisFileInfo['ogg']['bitrate_max'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $ThisFileInfo['ogg']['bitrate_nominal'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $ThisFileInfo['ogg']['bitrate_min'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $ThisFileInfo['ogg']['blocksize_small'] = pow(2, getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)) & 0x0F); + $ThisFileInfo['ogg']['blocksize_large'] = pow(2, (getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)) & 0xF0) >> 4); + $ThisFileInfo['ogg']['stop_bit'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); // must be 1, marks end of packet + + $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; // overridden if actually abr + if ($ThisFileInfo['ogg']['bitrate_max'] == 0xFFFFFFFF) { + unset($ThisFileInfo['ogg']['bitrate_max']); + $ThisFileInfo['audio']['bitrate_mode'] = 'abr'; + } + if ($ThisFileInfo['ogg']['bitrate_nominal'] == 0xFFFFFFFF) { + unset($ThisFileInfo['ogg']['bitrate_nominal']); + } + if ($ThisFileInfo['ogg']['bitrate_min'] == 0xFFFFFFFF) { + unset($ThisFileInfo['ogg']['bitrate_min']); + $ThisFileInfo['audio']['bitrate_mode'] = 'abr'; + } + + } elseif (substr($filedata, 0, 8) == 'Speex ') { + + // http://www.speex.org/manual/node10.html + + $ThisFileInfo['audio']['dataformat'] = 'speex'; + $ThisFileInfo['mime_type'] = 'audio/speex'; + $ThisFileInfo['audio']['bitrate_mode'] = 'abr'; + $ThisFileInfo['audio']['lossless'] = false; + + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_string'] = substr($filedata, $filedataoffset, 8); // hard-coded to 'Speex ' + $filedataoffset += 8; + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version'] = substr($filedata, $filedataoffset, 20); + $filedataoffset += 20; + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version_id'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['header_size'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['rate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode_bitstream_version'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['nb_channels'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['bitrate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['framesize'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['vbr'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['frames_per_packet'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['extra_headers'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved1'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved2'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + + $ThisFileInfo['speex']['speex_version'] = trim($ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version']); + $ThisFileInfo['speex']['sample_rate'] = $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['rate']; + $ThisFileInfo['speex']['channels'] = $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['nb_channels']; + $ThisFileInfo['speex']['vbr'] = (bool) $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['vbr']; + $ThisFileInfo['speex']['band_type'] = getid3_ogg::SpeexBandModeLookup($ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode']); + + $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['speex']['sample_rate']; + $ThisFileInfo['audio']['channels'] = $ThisFileInfo['speex']['channels']; + if ($ThisFileInfo['speex']['vbr']) { + $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; + } + + } else { + + $ThisFileInfo['error'][] = 'Expecting either "Speex " or "vorbis" identifier strings, found neither'; + unset($ThisFileInfo['ogg']); + unset($ThisFileInfo['mime_type']); + return false; + + } + + + // Page 2 - Comment Header + + $oggpageinfo = getid3_ogg::ParseOggPageHeader($fd); + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo; + + switch ($ThisFileInfo['audio']['dataformat']) { + + case 'vorbis': + $filedata = fread($fd, $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']); + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($filedata, 0, 1)); + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, 1, 6); // hard-coded to 'vorbis' + + getid3_ogg::ParseVorbisCommentsFilepointer($fd, $ThisFileInfo); + break; + + case 'flac': + if (!getid3_flac::FLACparseMETAdata($fd, $ThisFileInfo)) { + $ThisFileInfo['error'][] = 'Failed to parse FLAC headers'; + return false; + } + break; + + case 'speex': + fseek($fd, $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length'], SEEK_CUR); + getid3_ogg::ParseVorbisCommentsFilepointer($fd, $ThisFileInfo); + break; + + } + + + + // Last Page - Number of Samples + + fseek($fd, max($ThisFileInfo['avdataend'] - GETID3_FREAD_BUFFER_SIZE, 0), SEEK_SET); + $LastChunkOfOgg = strrev(fread($fd, GETID3_FREAD_BUFFER_SIZE)); + if ($LastOggSpostion = strpos($LastChunkOfOgg, 'SggO')) { + fseek($fd, $ThisFileInfo['avdataend'] - ($LastOggSpostion + strlen('SggO')), SEEK_SET); + $ThisFileInfo['avdataend'] = ftell($fd); + $ThisFileInfo['ogg']['pageheader']['eos'] = getid3_ogg::ParseOggPageHeader($fd); + $ThisFileInfo['ogg']['samples'] = $ThisFileInfo['ogg']['pageheader']['eos']['pcm_abs_position']; + if ($ThisFileInfo['ogg']['samples'] == 0) { + $ThisFileInfo['error'][] = 'Corrupt Ogg file: eos.number of samples == zero'; + return false; + } + $ThisFileInfo['ogg']['bitrate_average'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / ($ThisFileInfo['ogg']['samples'] / $ThisFileInfo['audio']['sample_rate']); + } + + if (!empty($ThisFileInfo['ogg']['bitrate_average'])) { + $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['ogg']['bitrate_average']; + } elseif (!empty($ThisFileInfo['ogg']['bitrate_nominal'])) { + $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['ogg']['bitrate_nominal']; + } elseif (!empty($ThisFileInfo['ogg']['bitrate_min']) && !empty($ThisFileInfo['ogg']['bitrate_max'])) { + $ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['ogg']['bitrate_min'] + $ThisFileInfo['ogg']['bitrate_max']) / 2; + } + if (isset($ThisFileInfo['audio']['bitrate']) && !isset($ThisFileInfo['playtime_seconds'])) { + if ($ThisFileInfo['audio']['bitrate'] == 0) { + $ThisFileInfo['error'][] = 'Corrupt Ogg file: bitrate_audio == zero'; + return false; + } + $ThisFileInfo['playtime_seconds'] = (float) ((($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['audio']['bitrate']); + } + + if (isset($ThisFileInfo['ogg']['vendor'])) { + $ThisFileInfo['audio']['encoder'] = preg_replace('/^Encoded with /', '', $ThisFileInfo['ogg']['vendor']); + + // Vorbis only + if ($ThisFileInfo['audio']['dataformat'] == 'vorbis') { + + // Vorbis 1.0 starts with Xiph.Org + if (preg_match('/^Xiph.Org/', $ThisFileInfo['audio']['encoder'])) { + + if ($ThisFileInfo['audio']['bitrate_mode'] == 'abr') { + + // Set -b 128 on abr files + $ThisFileInfo['audio']['encoder_options'] = '-b '.round($ThisFileInfo['ogg']['bitrate_nominal'] / 1000); + + } elseif (($ThisFileInfo['audio']['bitrate_mode'] == 'vbr') && ($ThisFileInfo['audio']['channels'] == 2) && ($ThisFileInfo['audio']['sample_rate'] >= 44100) && ($ThisFileInfo['audio']['sample_rate'] <= 48000)) { + // Set -q N on vbr files + $ThisFileInfo['audio']['encoder_options'] = '-q '.$this->get_quality_from_nominal_bitrate($ThisFileInfo['ogg']['bitrate_nominal']); + + } + } + + if (empty($ThisFileInfo['audio']['encoder_options']) && !empty($ThisFileInfo['ogg']['bitrate_nominal'])) { + $ThisFileInfo['audio']['encoder_options'] = 'Nominal bitrate: '.intval(round($ThisFileInfo['ogg']['bitrate_nominal'] / 1000)).'kbps'; + } + } + } + + return true; + } + + + function ParseOggPageHeader(&$fd) { + // http://xiph.org/ogg/vorbis/doc/framing.html + $oggheader['page_start_offset'] = ftell($fd); // where we started from in the file + + $filedata = fread($fd, GETID3_FREAD_BUFFER_SIZE); + $filedataoffset = 0; + while ((substr($filedata, $filedataoffset++, 4) != 'OggS')) { + if ((ftell($fd) - $oggheader['page_start_offset']) >= GETID3_FREAD_BUFFER_SIZE) { + // should be found before here + return false; + } + if ((($filedataoffset + 28) > strlen($filedata)) || (strlen($filedata) < 28)) { + if (feof($fd) || (($filedata .= fread($fd, GETID3_FREAD_BUFFER_SIZE)) === false)) { + // get some more data, unless eof, in which case fail + return false; + } + } + } + $filedataoffset += strlen('OggS') - 1; // page, delimited by 'OggS' + + $oggheader['stream_structver'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); + $filedataoffset += 1; + $oggheader['flags_raw'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); + $filedataoffset += 1; + $oggheader['flags']['fresh'] = (bool) ($oggheader['flags_raw'] & 0x01); // fresh packet + $oggheader['flags']['bos'] = (bool) ($oggheader['flags_raw'] & 0x02); // first page of logical bitstream (bos) + $oggheader['flags']['eos'] = (bool) ($oggheader['flags_raw'] & 0x04); // last page of logical bitstream (eos) + + $oggheader['pcm_abs_position'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8)); + $filedataoffset += 8; + $oggheader['stream_serialno'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $oggheader['page_seqno'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $oggheader['page_checksum'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $oggheader['page_segments'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); + $filedataoffset += 1; + $oggheader['page_length'] = 0; + for ($i = 0; $i < $oggheader['page_segments']; $i++) { + $oggheader['segment_table'][$i] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); + $filedataoffset += 1; + $oggheader['page_length'] += $oggheader['segment_table'][$i]; + } + $oggheader['header_end_offset'] = $oggheader['page_start_offset'] + $filedataoffset; + $oggheader['page_end_offset'] = $oggheader['header_end_offset'] + $oggheader['page_length']; + fseek($fd, $oggheader['header_end_offset'], SEEK_SET); + + return $oggheader; + } + + + function ParseVorbisCommentsFilepointer(&$fd, &$ThisFileInfo) { + + $OriginalOffset = ftell($fd); + $CommentStartOffset = $OriginalOffset; + $commentdataoffset = 0; + $VorbisCommentPage = 1; + + switch ($ThisFileInfo['audio']['dataformat']) { + case 'vorbis': + $CommentStartOffset = $ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage]['page_start_offset']; // Second Ogg page, after header block + fseek($fd, $CommentStartOffset, SEEK_SET); + $commentdataoffset = 27 + $ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage]['page_segments']; + $commentdata = fread($fd, getid3_ogg::OggPageSegmentLength($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage], 1) + $commentdataoffset); + + $commentdataoffset += (strlen('vorbis') + 1); + break; + + case 'flac': + fseek($fd, $ThisFileInfo['flac']['VORBIS_COMMENT']['raw']['offset'] + 4, SEEK_SET); + $commentdata = fread($fd, $ThisFileInfo['flac']['VORBIS_COMMENT']['raw']['block_length']); + break; + + case 'speex': + $CommentStartOffset = $ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage]['page_start_offset']; // Second Ogg page, after header block + fseek($fd, $CommentStartOffset, SEEK_SET); + $commentdataoffset = 27 + $ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage]['page_segments']; + $commentdata = fread($fd, getid3_ogg::OggPageSegmentLength($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage], 1) + $commentdataoffset); + break; + + default: + return false; + break; + } + + $VendorSize = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4)); + $commentdataoffset += 4; + + $ThisFileInfo['ogg']['vendor'] = substr($commentdata, $commentdataoffset, $VendorSize); + $commentdataoffset += $VendorSize; + + $CommentsCount = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4)); + $commentdataoffset += 4; + $ThisFileInfo['avdataoffset'] = $CommentStartOffset + $commentdataoffset; + + $basicfields = array('TITLE', 'ARTIST', 'ALBUM', 'TRACKNUMBER', 'GENRE', 'DATE', 'DESCRIPTION', 'COMMENT'); + for ($i = 0; $i < $CommentsCount; $i++) { + + $ThisFileInfo['ogg']['comments_raw'][$i]['dataoffset'] = $CommentStartOffset + $commentdataoffset; + + if (ftell($fd) < ($ThisFileInfo['ogg']['comments_raw'][$i]['dataoffset'] + 4)) { + $VorbisCommentPage++; + + $oggpageinfo = getid3_ogg::ParseOggPageHeader($fd); + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo; + + // First, save what we haven't read yet + $AsYetUnusedData = substr($commentdata, $commentdataoffset); + + // Then take that data off the end + $commentdata = substr($commentdata, 0, $commentdataoffset); + + // Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct + $commentdata .= str_repeat("\x00", 27 + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']); + $commentdataoffset += (27 + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']); + + // Finally, stick the unused data back on the end + $commentdata .= $AsYetUnusedData; + + //$commentdata .= fread($fd, $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']); + $commentdata .= fread($fd, getid3_ogg::OggPageSegmentLength($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage], 1)); + + } + $ThisFileInfo['ogg']['comments_raw'][$i]['size'] = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4)); + + // replace avdataoffset with position just after the last vorbiscomment + $ThisFileInfo['avdataoffset'] = $ThisFileInfo['ogg']['comments_raw'][$i]['dataoffset'] + $ThisFileInfo['ogg']['comments_raw'][$i]['size'] + 4; + + $commentdataoffset += 4; + while ((strlen($commentdata) - $commentdataoffset) < $ThisFileInfo['ogg']['comments_raw'][$i]['size']) { + if (($ThisFileInfo['ogg']['comments_raw'][$i]['size'] > $ThisFileInfo['avdataend']) || ($ThisFileInfo['ogg']['comments_raw'][$i]['size'] < 0)) { + $ThisFileInfo['error'][] = 'Invalid Ogg comment size (comment #'.$i.', claims to be '.number_format($ThisFileInfo['ogg']['comments_raw'][$i]['size']).' bytes) - aborting reading comments'; + break 2; + } + + $VorbisCommentPage++; + + $oggpageinfo = getid3_ogg::ParseOggPageHeader($fd); + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo; + + // First, save what we haven't read yet + $AsYetUnusedData = substr($commentdata, $commentdataoffset); + + // Then take that data off the end + $commentdata = substr($commentdata, 0, $commentdataoffset); + + // Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct + $commentdata .= str_repeat("\x00", 27 + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']); + $commentdataoffset += (27 + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']); + + // Finally, stick the unused data back on the end + $commentdata .= $AsYetUnusedData; + + //$commentdata .= fread($fd, $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']); + $commentdata .= fread($fd, getid3_ogg::OggPageSegmentLength($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage], 1)); + + //$filebaseoffset += $oggpageinfo['header_end_offset'] - $oggpageinfo['page_start_offset']; + } + $commentstring = substr($commentdata, $commentdataoffset, $ThisFileInfo['ogg']['comments_raw'][$i]['size']); + $commentdataoffset += $ThisFileInfo['ogg']['comments_raw'][$i]['size']; + + if (!$commentstring) { + + // no comment? + $ThisFileInfo['warning'][] = 'Blank Ogg comment ['.$i.']'; + + } elseif (strstr($commentstring, '=')) { + + $commentexploded = explode('=', $commentstring, 2); + $ThisFileInfo['ogg']['comments_raw'][$i]['key'] = strtoupper($commentexploded[0]); + $ThisFileInfo['ogg']['comments_raw'][$i]['value'] = @$commentexploded[1]; + $ThisFileInfo['ogg']['comments_raw'][$i]['data'] = base64_decode($ThisFileInfo['ogg']['comments_raw'][$i]['value']); + + $ThisFileInfo['ogg']['comments'][strtolower($ThisFileInfo['ogg']['comments_raw'][$i]['key'])][] = $ThisFileInfo['ogg']['comments_raw'][$i]['value']; + + $imagechunkcheck = getid3_lib::GetDataImageSize($ThisFileInfo['ogg']['comments_raw'][$i]['data']); + $ThisFileInfo['ogg']['comments_raw'][$i]['image_mime'] = getid3_lib::image_type_to_mime_type($imagechunkcheck[2]); + if (!$ThisFileInfo['ogg']['comments_raw'][$i]['image_mime'] || ($ThisFileInfo['ogg']['comments_raw'][$i]['image_mime'] == 'application/octet-stream')) { + unset($ThisFileInfo['ogg']['comments_raw'][$i]['image_mime']); + unset($ThisFileInfo['ogg']['comments_raw'][$i]['data']); + } + + } else { + + $ThisFileInfo['warning'][] = '[known problem with CDex >= v1.40, < v1.50b7] Invalid Ogg comment name/value pair ['.$i.']: '.$commentstring; + + } + } + + + // Replay Gain Adjustment + // http://privatewww.essex.ac.uk/~djmrob/replaygain/ + if (isset($ThisFileInfo['ogg']['comments']) && is_array($ThisFileInfo['ogg']['comments'])) { + foreach ($ThisFileInfo['ogg']['comments'] as $index => $commentvalue) { + switch ($index) { + case 'rg_audiophile': + case 'replaygain_album_gain': + $ThisFileInfo['replay_gain']['album']['adjustment'] = (double) $commentvalue[0]; + unset($ThisFileInfo['ogg']['comments'][$index]); + break; + + case 'rg_radio': + case 'replaygain_track_gain': + $ThisFileInfo['replay_gain']['track']['adjustment'] = (double) $commentvalue[0]; + unset($ThisFileInfo['ogg']['comments'][$index]); + break; + + case 'replaygain_album_peak': + $ThisFileInfo['replay_gain']['album']['peak'] = (double) $commentvalue[0]; + unset($ThisFileInfo['ogg']['comments'][$index]); + break; + + case 'rg_peak': + case 'replaygain_track_peak': + $ThisFileInfo['replay_gain']['track']['peak'] = (double) $commentvalue[0]; + unset($ThisFileInfo['ogg']['comments'][$index]); + break; + + + default: + // do nothing + break; + } + } + } + + fseek($fd, $OriginalOffset, SEEK_SET); + + return true; + } + + function SpeexBandModeLookup($mode) { + static $SpeexBandModeLookup = array(); + if (empty($SpeexBandModeLookup)) { + $SpeexBandModeLookup[0] = 'narrow'; + $SpeexBandModeLookup[1] = 'wide'; + $SpeexBandModeLookup[2] = 'ultra-wide'; + } + return (isset($SpeexBandModeLookup[$mode]) ? $SpeexBandModeLookup[$mode] : null); + } + + + function OggPageSegmentLength($OggInfoArray, $SegmentNumber=1) { + for ($i = 0; $i < $SegmentNumber; $i++) { + $segmentlength = 0; + foreach ($OggInfoArray['segment_table'] as $key => $value) { + $segmentlength += $value; + if ($value < 255) { + break; + } + } + } + return $segmentlength; + } + + + function get_quality_from_nominal_bitrate($nominal_bitrate) { + + // decrease precision + $nominal_bitrate = $nominal_bitrate / 1000; + + if ($nominal_bitrate < 128) { + // q-1 to q4 + $qval = ($nominal_bitrate - 64) / 16; + } elseif ($nominal_bitrate < 256) { + // q4 to q8 + $qval = $nominal_bitrate / 32; + } elseif ($nominal_bitrate < 320) { + // q8 to q9 + $qval = ($nominal_bitrate + 256) / 64; + } else { + // q9 to q10 + $qval = ($nominal_bitrate + 1300) / 180; + } + //return $qval; // 5.031324 + //return intval($qval); // 5 + return round($qval, 1); // 5 or 4.9 + } + +} + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.audio.optimfrog.php b/includes/getid3/getid3/module.audio.optimfrog.php new file mode 100644 index 0000000..3c2dfb0 --- /dev/null +++ b/includes/getid3/getid3/module.audio.optimfrog.php @@ -0,0 +1,408 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio.optimfrog.php // +// module for analyzing OptimFROG audio files // +// dependencies: module.audio.riff.php // +// /// +///////////////////////////////////////////////////////////////// + +getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true); + +class getid3_optimfrog +{ + + function getid3_optimfrog(&$fd, &$ThisFileInfo) { + $ThisFileInfo['fileformat'] = 'ofr'; + $ThisFileInfo['audio']['dataformat'] = 'ofr'; + $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; + $ThisFileInfo['audio']['lossless'] = true; + + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + $OFRheader = fread($fd, 8); + if (substr($OFRheader, 0, 5) == '*RIFF') { + + return $this->ParseOptimFROGheader42($fd, $ThisFileInfo); + + } elseif (substr($OFRheader, 0, 3) == 'OFR') { + + return $this->ParseOptimFROGheader45($fd, $ThisFileInfo); + + } + + $ThisFileInfo['error'][] = 'Expecting "*RIFF" or "OFR " at offset '.$ThisFileInfo['avdataoffset'].', found "'.$OFRheader.'"'; + unset($ThisFileInfo['fileformat']); + return false; + } + + + function ParseOptimFROGheader42(&$fd, &$ThisFileInfo) { + // for fileformat of v4.21 and older + + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + $OptimFROGheaderData = fread($fd, 45); + $ThisFileInfo['avdataoffset'] = 45; + + $OptimFROGencoderVersion_raw = getid3_lib::LittleEndian2Int(substr($OptimFROGheaderData, 0, 1)); + $OptimFROGencoderVersion_major = floor($OptimFROGencoderVersion_raw / 10); + $OptimFROGencoderVersion_minor = $OptimFROGencoderVersion_raw - ($OptimFROGencoderVersion_major * 10); + $RIFFdata = substr($OptimFROGheaderData, 1, 44); + $OrignalRIFFheaderSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 4, 4)) + 8; + $OrignalRIFFdataSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 40, 4)) + 44; + + if ($OrignalRIFFheaderSize > $OrignalRIFFdataSize) { + $ThisFileInfo['avdataend'] -= ($OrignalRIFFheaderSize - $OrignalRIFFdataSize); + fseek($fd, $ThisFileInfo['avdataend'], SEEK_SET); + $RIFFdata .= fread($fd, $OrignalRIFFheaderSize - $OrignalRIFFdataSize); + } + + // move the data chunk after all other chunks (if any) + // so that the RIFF parser doesn't see EOF when trying + // to skip over the data chunk + $RIFFdata = substr($RIFFdata, 0, 36).substr($RIFFdata, 44).substr($RIFFdata, 36, 8); + getid3_riff::ParseRIFFdata($RIFFdata, $ThisFileInfo); + + $ThisFileInfo['audio']['encoder'] = 'OptimFROG '.$OptimFROGencoderVersion_major.'.'.$OptimFROGencoderVersion_minor; + $ThisFileInfo['audio']['channels'] = $ThisFileInfo['riff']['audio'][0]['channels']; + $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['riff']['audio'][0]['sample_rate']; + $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['riff']['audio'][0]['bits_per_sample']; + $ThisFileInfo['playtime_seconds'] = $OrignalRIFFdataSize / ($ThisFileInfo['audio']['channels'] * $ThisFileInfo['audio']['sample_rate'] * ($ThisFileInfo['audio']['bits_per_sample'] / 8)); + $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; + + return true; + } + + + function ParseOptimFROGheader45(&$fd, &$ThisFileInfo) { + // for fileformat of v4.50a and higher + + $RIFFdata = ''; + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + while (!feof($fd) && (ftell($fd) < $ThisFileInfo['avdataend'])) { + $BlockOffset = ftell($fd); + $BlockData = fread($fd, 8); + $offset = 8; + $BlockName = substr($BlockData, 0, 4); + $BlockSize = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 4)); + + if ($BlockName == 'OFRX') { + $BlockName = 'OFR '; + } + if (!isset($ThisFileInfo['ofr'][$BlockName])) { + $ThisFileInfo['ofr'][$BlockName] = array(); + } + $thisfile_ofr_thisblock = &$ThisFileInfo['ofr'][$BlockName]; + + switch ($BlockName) { + case 'OFR ': + + // shortcut + $thisfile_ofr_thisblock['offset'] = $BlockOffset; + $thisfile_ofr_thisblock['size'] = $BlockSize; + + $ThisFileInfo['audio']['encoder'] = 'OptimFROG 4.50 alpha'; + switch ($BlockSize) { + case 12: + case 15: + // good + break; + + default: + $ThisFileInfo['warning'][] = '"'.$BlockName.'" contains more data than expected (expected 12 or 15 bytes, found '.$BlockSize.' bytes)'; + break; + } + $BlockData .= fread($fd, $BlockSize); + + $thisfile_ofr_thisblock['total_samples'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 6)); + $offset += 6; + $thisfile_ofr_thisblock['raw']['sample_type'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1)); + $thisfile_ofr_thisblock['sample_type'] = $this->OptimFROGsampleTypeLookup($thisfile_ofr_thisblock['raw']['sample_type']); + $offset += 1; + $thisfile_ofr_thisblock['channel_config'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1)); + $thisfile_ofr_thisblock['channels'] = $thisfile_ofr_thisblock['channel_config']; + $offset += 1; + $thisfile_ofr_thisblock['sample_rate'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 4)); + $offset += 4; + + if ($BlockSize > 12) { + + // OFR 4.504b or higher + $thisfile_ofr_thisblock['channels'] = $this->OptimFROGchannelConfigNumChannelsLookup($thisfile_ofr_thisblock['channel_config']); + $thisfile_ofr_thisblock['raw']['encoder_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 2)); + $thisfile_ofr_thisblock['encoder'] = $this->OptimFROGencoderNameLookup($thisfile_ofr_thisblock['raw']['encoder_id']); + $offset += 2; + $thisfile_ofr_thisblock['raw']['compression'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1)); + $thisfile_ofr_thisblock['compression'] = $this->OptimFROGcompressionLookup($thisfile_ofr_thisblock['raw']['compression']); + $thisfile_ofr_thisblock['speedup'] = $this->OptimFROGspeedupLookup($thisfile_ofr_thisblock['raw']['compression']); + $offset += 1; + + $ThisFileInfo['audio']['encoder'] = 'OptimFROG '.$thisfile_ofr_thisblock['encoder']; + $ThisFileInfo['audio']['encoder_options'] = '--mode '.$thisfile_ofr_thisblock['compression']; + + if ((($thisfile_ofr_thisblock['raw']['encoder_id'] & 0xF0) >> 4) == 7) { // v4.507 + if (strtolower(getid3_lib::fileextension($ThisFileInfo['filename'])) == 'ofs') { + // OptimFROG DualStream format is lossy, but as of v4.507 there is no way to tell the difference + // between lossless and lossy other than the file extension. + $ThisFileInfo['audio']['dataformat'] = 'ofs'; + $ThisFileInfo['audio']['lossless'] = true; + } + } + + } + + $ThisFileInfo['audio']['channels'] = $thisfile_ofr_thisblock['channels']; + $ThisFileInfo['audio']['sample_rate'] = $thisfile_ofr_thisblock['sample_rate']; + $ThisFileInfo['audio']['bits_per_sample'] = $this->OptimFROGbitsPerSampleTypeLookup($thisfile_ofr_thisblock['raw']['sample_type']); + break; + + + case 'COMP': + // unlike other block types, there CAN be multiple COMP blocks + + $COMPdata['offset'] = $BlockOffset; + $COMPdata['size'] = $BlockSize; + + if ($ThisFileInfo['avdataoffset'] == 0) { + $ThisFileInfo['avdataoffset'] = $BlockOffset; + } + + // Only interested in first 14 bytes (only first 12 needed for v4.50 alpha), not actual audio data + $BlockData .= fread($fd, 14); + fseek($fd, $BlockSize - 14, SEEK_CUR); + + $COMPdata['crc_32'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 4)); + $offset += 4; + $COMPdata['sample_count'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 4)); + $offset += 4; + $COMPdata['raw']['sample_type'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1)); + $COMPdata['sample_type'] = $this->OptimFROGsampleTypeLookup($COMPdata['raw']['sample_type']); + $offset += 1; + $COMPdata['raw']['channel_configuration'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1)); + $COMPdata['channel_configuration'] = $this->OptimFROGchannelConfigurationLookup($COMPdata['raw']['channel_configuration']); + $offset += 1; + $COMPdata['raw']['algorithm_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 2)); + //$COMPdata['algorithm'] = OptimFROGalgorithmNameLookup($COMPdata['raw']['algorithm_id']); + $offset += 2; + + if ($ThisFileInfo['ofr']['OFR ']['size'] > 12) { + + // OFR 4.504b or higher + $COMPdata['raw']['encoder_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 2)); + $COMPdata['encoder'] = $this->OptimFROGencoderNameLookup($COMPdata['raw']['encoder_id']); + $offset += 2; + + } + + if ($COMPdata['crc_32'] == 0x454E4F4E) { + // ASCII value of 'NONE' - placeholder value in v4.50a + $COMPdata['crc_32'] = false; + } + + $thisfile_ofr_thisblock[] = $COMPdata; + break; + + case 'HEAD': + $thisfile_ofr_thisblock['offset'] = $BlockOffset; + $thisfile_ofr_thisblock['size'] = $BlockSize; + + $RIFFdata .= fread($fd, $BlockSize); + break; + + case 'TAIL': + $thisfile_ofr_thisblock['offset'] = $BlockOffset; + $thisfile_ofr_thisblock['size'] = $BlockSize; + + if ($BlockSize > 0) { + $RIFFdata .= fread($fd, $BlockSize); + } + break; + + case 'RECV': + // block contains no useful meta data - simply note and skip + + $thisfile_ofr_thisblock['offset'] = $BlockOffset; + $thisfile_ofr_thisblock['size'] = $BlockSize; + + fseek($fd, $BlockSize, SEEK_CUR); + break; + + + case 'APET': + // APEtag v2 + + $thisfile_ofr_thisblock['offset'] = $BlockOffset; + $thisfile_ofr_thisblock['size'] = $BlockSize; + $ThisFileInfo['warning'][] = 'APEtag processing inside OptimFROG not supported in this version ('.GETID3_VERSION.') of getID3()'; + + fseek($fd, $BlockSize, SEEK_CUR); + break; + + + case 'MD5 ': + // APEtag v2 + + $thisfile_ofr_thisblock['offset'] = $BlockOffset; + $thisfile_ofr_thisblock['size'] = $BlockSize; + + if ($BlockSize == 16) { + + $thisfile_ofr_thisblock['md5_binary'] = fread($fd, $BlockSize); + $thisfile_ofr_thisblock['md5_string'] = getid3_lib::PrintHexBytes($thisfile_ofr_thisblock['md5_binary'], true, false, false); + $ThisFileInfo['md5_data_source'] = $thisfile_ofr_thisblock['md5_string']; + + } else { + + $ThisFileInfo['warning'][] = 'Expecting block size of 16 in "MD5 " chunk, found '.$BlockSize.' instead'; + fseek($fd, $BlockSize, SEEK_CUR); + + } + break; + + + default: + $thisfile_ofr_thisblock['offset'] = $BlockOffset; + $thisfile_ofr_thisblock['size'] = $BlockSize; + + $ThisFileInfo['warning'][] = 'Unhandled OptimFROG block type "'.$BlockName.'" at offset '.$thisfile_ofr_thisblock['offset']; + fseek($fd, $BlockSize, SEEK_CUR); + break; + } + } + if (isset($ThisFileInfo['ofr']['TAIL']['offset'])) { + $ThisFileInfo['avdataend'] = $ThisFileInfo['ofr']['TAIL']['offset']; + } + + $ThisFileInfo['playtime_seconds'] = (float) $ThisFileInfo['ofr']['OFR ']['total_samples'] / ($ThisFileInfo['audio']['channels'] * $ThisFileInfo['audio']['sample_rate']); + $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; + + // move the data chunk after all other chunks (if any) + // so that the RIFF parser doesn't see EOF when trying + // to skip over the data chunk + $RIFFdata = substr($RIFFdata, 0, 36).substr($RIFFdata, 44).substr($RIFFdata, 36, 8); + getid3_riff::ParseRIFFdata($RIFFdata, $ThisFileInfo); + + return true; + } + + + function OptimFROGsampleTypeLookup($SampleType) { + static $OptimFROGsampleTypeLookup = array( + 0 => 'unsigned int (8-bit)', + 1 => 'signed int (8-bit)', + 2 => 'unsigned int (16-bit)', + 3 => 'signed int (16-bit)', + 4 => 'unsigned int (24-bit)', + 5 => 'signed int (24-bit)', + 6 => 'unsigned int (32-bit)', + 7 => 'signed int (32-bit)', + 8 => 'float 0.24 (32-bit)', + 9 => 'float 16.8 (32-bit)', + 10 => 'float 24.0 (32-bit)' + ); + return (isset($OptimFROGsampleTypeLookup[$SampleType]) ? $OptimFROGsampleTypeLookup[$SampleType] : false); + } + + function OptimFROGbitsPerSampleTypeLookup($SampleType) { + static $OptimFROGbitsPerSampleTypeLookup = array( + 0 => 8, + 1 => 8, + 2 => 16, + 3 => 16, + 4 => 24, + 5 => 24, + 6 => 32, + 7 => 32, + 8 => 32, + 9 => 32, + 10 => 32 + ); + return (isset($OptimFROGbitsPerSampleTypeLookup[$SampleType]) ? $OptimFROGbitsPerSampleTypeLookup[$SampleType] : false); + } + + function OptimFROGchannelConfigurationLookup($ChannelConfiguration) { + static $OptimFROGchannelConfigurationLookup = array( + 0 => 'mono', + 1 => 'stereo' + ); + return (isset($OptimFROGchannelConfigurationLookup[$ChannelConfiguration]) ? $OptimFROGchannelConfigurationLookup[$ChannelConfiguration] : false); + } + + function OptimFROGchannelConfigNumChannelsLookup($ChannelConfiguration) { + static $OptimFROGchannelConfigNumChannelsLookup = array( + 0 => 1, + 1 => 2 + ); + return (isset($OptimFROGchannelConfigNumChannelsLookup[$ChannelConfiguration]) ? $OptimFROGchannelConfigNumChannelsLookup[$ChannelConfiguration] : false); + } + + + + // function OptimFROGalgorithmNameLookup($AlgorithID) { + // static $OptimFROGalgorithmNameLookup = array(); + // return (isset($OptimFROGalgorithmNameLookup[$AlgorithID]) ? $OptimFROGalgorithmNameLookup[$AlgorithID] : false); + // } + + + function OptimFROGencoderNameLookup($EncoderID) { + // version = (encoderID >> 4) + 4500 + // system = encoderID & 0xF + + $EncoderVersion = number_format(((($EncoderID & 0xF0) >> 4) + 4500) / 1000, 3); + $EncoderSystemID = ($EncoderID & 0x0F); + + static $OptimFROGencoderSystemLookup = array( + 0x00 => 'Windows console', + 0x01 => 'Linux console', + 0x0F => 'unknown' + ); + return $EncoderVersion.' ('.(isset($OptimFROGencoderSystemLookup[$EncoderSystemID]) ? $OptimFROGencoderSystemLookup[$EncoderSystemID] : 'undefined encoder type (0x'.dechex($EncoderSystemID).')').')'; + } + + function OptimFROGcompressionLookup($CompressionID) { + // mode = compression >> 3 + // speedup = compression & 0x07 + + $CompressionModeID = ($CompressionID & 0xF8) >> 3; + //$CompressionSpeedupID = ($CompressionID & 0x07); + + static $OptimFROGencoderModeLookup = array( + 0x00 => 'fast', + 0x01 => 'normal', + 0x02 => 'high', + 0x03 => 'extra', // extranew (some versions) + 0x04 => 'best', // bestnew (some versions) + 0x05 => 'ultra', + 0x06 => 'insane', + 0x07 => 'highnew', + 0x08 => 'extranew', + 0x09 => 'bestnew' + ); + return (isset($OptimFROGencoderModeLookup[$CompressionModeID]) ? $OptimFROGencoderModeLookup[$CompressionModeID] : 'undefined mode (0x'.str_pad(dechex($CompressionModeID), 2, '0', STR_PAD_LEFT).')'); + } + + function OptimFROGspeedupLookup($CompressionID) { + // mode = compression >> 3 + // speedup = compression & 0x07 + + //$CompressionModeID = ($CompressionID & 0xF8) >> 3; + $CompressionSpeedupID = ($CompressionID & 0x07); + + static $OptimFROGencoderSpeedupLookup = array( + 0x00 => '1x', + 0x01 => '2x', + 0x02 => '4x' + ); + + return (isset($OptimFROGencoderSpeedupLookup[$CompressionSpeedupID]) ? $OptimFROGencoderSpeedupLookup[$CompressionSpeedupID] : 'undefined mode (0x'.dechex($CompressionSpeedupID)); + } + +} + + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.audio.rkau.php b/includes/getid3/getid3/module.audio.rkau.php new file mode 100644 index 0000000..0e344d4 --- /dev/null +++ b/includes/getid3/getid3/module.audio.rkau.php @@ -0,0 +1,92 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio.shorten.php // +// module for analyzing Shorten Audio files // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_rkau +{ + + function getid3_rkau(&$fd, &$ThisFileInfo) { + + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + $RKAUHeader = fread($fd, 20); + if (substr($RKAUHeader, 0, 3) != 'RKA') { + $ThisFileInfo['error'][] = 'Expecting "RKA" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($RKAUHeader, 0, 3).'"'; + return false; + } + + $ThisFileInfo['fileformat'] = 'rkau'; + $ThisFileInfo['audio']['dataformat'] = 'rkau'; + $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; + + $ThisFileInfo['rkau']['raw']['version'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 3, 1)); + $ThisFileInfo['rkau']['version'] = '1.'.str_pad($ThisFileInfo['rkau']['raw']['version'] & 0x0F, 2, '0', STR_PAD_LEFT); + if (($ThisFileInfo['rkau']['version'] > 1.07) || ($ThisFileInfo['rkau']['version'] < 1.06)) { + $ThisFileInfo['error'][] = 'This version of getID3() can only parse RKAU files v1.06 and 1.07 (this file is v'.$ThisFileInfo['rkau']['version'].')'; + unset($ThisFileInfo['rkau']); + return false; + } + + $ThisFileInfo['rkau']['source_bytes'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 4, 4)); + $ThisFileInfo['rkau']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 8, 4)); + $ThisFileInfo['rkau']['channels'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 12, 1)); + $ThisFileInfo['rkau']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 13, 1)); + + $ThisFileInfo['rkau']['raw']['quality'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 14, 1)); + $this->RKAUqualityLookup($ThisFileInfo['rkau']); + + $ThisFileInfo['rkau']['raw']['flags'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 15, 1)); + $ThisFileInfo['rkau']['flags']['joint_stereo'] = (bool) (!($ThisFileInfo['rkau']['raw']['flags'] & 0x01)); + $ThisFileInfo['rkau']['flags']['streaming'] = (bool) ($ThisFileInfo['rkau']['raw']['flags'] & 0x02); + $ThisFileInfo['rkau']['flags']['vrq_lossy_mode'] = (bool) ($ThisFileInfo['rkau']['raw']['flags'] & 0x04); + + if ($ThisFileInfo['rkau']['flags']['streaming']) { + $ThisFileInfo['avdataoffset'] += 20; + $ThisFileInfo['rkau']['compressed_bytes'] = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 16, 4)); + } else { + $ThisFileInfo['avdataoffset'] += 16; + $ThisFileInfo['rkau']['compressed_bytes'] = $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'] - 1; + } + // Note: compressed_bytes does not always equal what appears to be the actual number of compressed bytes, + // sometimes it's more, sometimes less. No idea why(?) + + $ThisFileInfo['audio']['lossless'] = $ThisFileInfo['rkau']['lossless']; + $ThisFileInfo['audio']['channels'] = $ThisFileInfo['rkau']['channels']; + $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['rkau']['bits_per_sample']; + $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['rkau']['sample_rate']; + + $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['rkau']['source_bytes'] / ($ThisFileInfo['rkau']['sample_rate'] * $ThisFileInfo['rkau']['channels'] * ($ThisFileInfo['rkau']['bits_per_sample'] / 8)); + $ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['rkau']['compressed_bytes'] * 8) / $ThisFileInfo['playtime_seconds']; + + return true; + + } + + + function RKAUqualityLookup(&$RKAUdata) { + $level = ($RKAUdata['raw']['quality'] & 0xF0) >> 4; + $quality = $RKAUdata['raw']['quality'] & 0x0F; + + $RKAUdata['lossless'] = (($quality == 0) ? true : false); + $RKAUdata['compression_level'] = $level + 1; + if (!$RKAUdata['lossless']) { + $RKAUdata['quality_setting'] = $quality; + } + + return true; + } + +} + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.audio.shorten.php b/includes/getid3/getid3/module.audio.shorten.php new file mode 100644 index 0000000..3a09bdd --- /dev/null +++ b/includes/getid3/getid3/module.audio.shorten.php @@ -0,0 +1,179 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio.shorten.php // +// module for analyzing Shorten Audio files // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_shorten +{ + + function getid3_shorten(&$fd, &$ThisFileInfo) { + + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + + $ShortenHeader = fread($fd, 8); + if (substr($ShortenHeader, 0, 4) != 'ajkg') { + $ThisFileInfo['error'][] = 'Expecting "ajkg" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($ShortenHeader, 0, 4).'"'; + return false; + } + $ThisFileInfo['fileformat'] = 'shn'; + $ThisFileInfo['audio']['dataformat'] = 'shn'; + $ThisFileInfo['audio']['lossless'] = true; + $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; + + $ThisFileInfo['shn']['version'] = getid3_lib::LittleEndian2Int(substr($ShortenHeader, 4, 1)); + + fseek($fd, $ThisFileInfo['avdataend'] - 12, SEEK_SET); + $SeekTableSignatureTest = fread($fd, 12); + $ThisFileInfo['shn']['seektable']['present'] = (bool) (substr($SeekTableSignatureTest, 4, 8) == 'SHNAMPSK'); + if ($ThisFileInfo['shn']['seektable']['present']) { + $ThisFileInfo['shn']['seektable']['length'] = getid3_lib::LittleEndian2Int(substr($SeekTableSignatureTest, 0, 4)); + $ThisFileInfo['shn']['seektable']['offset'] = $ThisFileInfo['avdataend'] - $ThisFileInfo['shn']['seektable']['length']; + fseek($fd, $ThisFileInfo['shn']['seektable']['offset'], SEEK_SET); + $SeekTableMagic = fread($fd, 4); + if ($SeekTableMagic != 'SEEK') { + + $ThisFileInfo['error'][] = 'Expecting "SEEK" at offset '.$ThisFileInfo['shn']['seektable']['offset'].', found "'.$SeekTableMagic.'"'; + return false; + + } else { + + // typedef struct tag_TSeekEntry + // { + // unsigned long SampleNumber; + // unsigned long SHNFileByteOffset; + // unsigned long SHNLastBufferReadPosition; + // unsigned short SHNByteGet; + // unsigned short SHNBufferOffset; + // unsigned short SHNFileBitOffset; + // unsigned long SHNGBuffer; + // unsigned short SHNBitShift; + // long CBuf0[3]; + // long CBuf1[3]; + // long Offset0[4]; + // long Offset1[4]; + // }TSeekEntry; + + $SeekTableData = fread($fd, $ThisFileInfo['shn']['seektable']['length'] - 16); + $ThisFileInfo['shn']['seektable']['entry_count'] = floor(strlen($SeekTableData) / 80); + //$ThisFileInfo['shn']['seektable']['entries'] = array(); + //$SeekTableOffset = 0; + //for ($i = 0; $i < $ThisFileInfo['shn']['seektable']['entry_count']; $i++) { + // $SeekTableEntry['sample_number'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); + // $SeekTableOffset += 4; + // $SeekTableEntry['shn_file_byte_offset'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); + // $SeekTableOffset += 4; + // $SeekTableEntry['shn_last_buffer_read_position'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); + // $SeekTableOffset += 4; + // $SeekTableEntry['shn_byte_get'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2)); + // $SeekTableOffset += 2; + // $SeekTableEntry['shn_buffer_offset'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2)); + // $SeekTableOffset += 2; + // $SeekTableEntry['shn_file_bit_offset'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2)); + // $SeekTableOffset += 2; + // $SeekTableEntry['shn_gbuffer'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); + // $SeekTableOffset += 4; + // $SeekTableEntry['shn_bit_shift'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2)); + // $SeekTableOffset += 2; + // for ($j = 0; $j < 3; $j++) { + // $SeekTableEntry['cbuf0'][$j] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); + // $SeekTableOffset += 4; + // } + // for ($j = 0; $j < 3; $j++) { + // $SeekTableEntry['cbuf1'][$j] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); + // $SeekTableOffset += 4; + // } + // for ($j = 0; $j < 4; $j++) { + // $SeekTableEntry['offset0'][$j] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); + // $SeekTableOffset += 4; + // } + // for ($j = 0; $j < 4; $j++) { + // $SeekTableEntry['offset1'][$j] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); + // $SeekTableOffset += 4; + // } + // + // $ThisFileInfo['shn']['seektable']['entries'][] = $SeekTableEntry; + //} + + } + + } + + if ((bool) ini_get('safe_mode')) { + $ThisFileInfo['error'][] = 'PHP running in Safe Mode - backtick operator not available, cannot run shntool to analyze Shorten files'; + return false; + } + + if (GETID3_OS_ISWINDOWS) { + + $RequiredFiles = array('shorten.exe', 'cygwin1.dll', 'head.exe'); + foreach ($RequiredFiles as $required_file) { + if (!is_readable(GETID3_HELPERAPPSDIR.$required_file)) { + $ThisFileInfo['error'][] = GETID3_HELPERAPPSDIR.$required_file.' does not exist'; + return false; + } + } + $commandline = GETID3_HELPERAPPSDIR.'shorten.exe -x "'.$ThisFileInfo['filenamepath'].'" - | '.GETID3_HELPERAPPSDIR.'head.exe -c 44'; + $commandline = str_replace('/', '\\', $commandline); + + } else { + + static $shorten_present; + if (!isset($shorten_present)) { + $shorten_present = file_exists('/usr/local/bin/shorten') || `which shorten`; + } + if (!$shorten_present) { + $ThisFileInfo['error'][] = 'shorten binary was not found in path or /usr/local/bin'; + return false; + } + $commandline = (file_exists('/usr/local/bin/shorten') ? '/usr/local/bin/' : '' ) . 'shorten -x '.escapeshellarg($ThisFileInfo['filenamepath']).' - | head -c 44'; + + } + + $output = `$commandline`; + + if (!empty($output) && (substr($output, 12, 4) == 'fmt ')) { + + getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true); + + $DecodedWAVFORMATEX = getid3_riff::RIFFparseWAVEFORMATex(substr($output, 20, 16)); + $ThisFileInfo['audio']['channels'] = $DecodedWAVFORMATEX['channels']; + $ThisFileInfo['audio']['bits_per_sample'] = $DecodedWAVFORMATEX['bits_per_sample']; + $ThisFileInfo['audio']['sample_rate'] = $DecodedWAVFORMATEX['sample_rate']; + + if (substr($output, 36, 4) == 'data') { + + $ThisFileInfo['playtime_seconds'] = getid3_lib::LittleEndian2Int(substr($output, 40, 4)) / $DecodedWAVFORMATEX['raw']['nAvgBytesPerSec']; + + } else { + + $ThisFileInfo['error'][] = 'shorten failed to decode DATA chunk to expected location, cannot determine playtime'; + return false; + + } + + $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $ThisFileInfo['playtime_seconds']) * 8; + + } else { + + $ThisFileInfo['error'][] = 'shorten failed to decode file to WAV for parsing'; + return false; + + } + + return true; + } + +} + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.audio.tta.php b/includes/getid3/getid3/module.audio.tta.php new file mode 100644 index 0000000..903de6b --- /dev/null +++ b/includes/getid3/getid3/module.audio.tta.php @@ -0,0 +1,107 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio.tta.php // +// module for analyzing TTA Audio files // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_tta +{ + + function getid3_tta(&$fd, &$ThisFileInfo) { + + $ThisFileInfo['fileformat'] = 'tta'; + $ThisFileInfo['audio']['dataformat'] = 'tta'; + $ThisFileInfo['audio']['lossless'] = true; + $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; + + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + $ttaheader = fread($fd, 26); + + $ThisFileInfo['tta']['magic'] = substr($ttaheader, 0, 3); + if ($ThisFileInfo['tta']['magic'] != 'TTA') { + $ThisFileInfo['error'][] = 'Expecting "TTA" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$ThisFileInfo['tta']['magic'].'"'; + unset($ThisFileInfo['fileformat']); + unset($ThisFileInfo['audio']); + unset($ThisFileInfo['tta']); + return false; + } + + switch ($ttaheader{3}) { + case "\x01": // TTA v1.x + case "\x02": // TTA v1.x + case "\x03": // TTA v1.x + // "It was the demo-version of the TTA encoder. There is no released format with such header. TTA encoder v1 is not supported about a year." + $ThisFileInfo['tta']['major_version'] = 1; + $ThisFileInfo['avdataoffset'] += 16; + + $ThisFileInfo['tta']['compression_level'] = ord($ttaheader{3}); + $ThisFileInfo['tta']['channels'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 4, 2)); + $ThisFileInfo['tta']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 6, 2)); + $ThisFileInfo['tta']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 8, 4)); + $ThisFileInfo['tta']['samples_per_channel'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 12, 4)); + + $ThisFileInfo['audio']['encoder_options'] = '-e'.$ThisFileInfo['tta']['compression_level']; + $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['tta']['samples_per_channel'] / $ThisFileInfo['tta']['sample_rate']; + break; + + case '2': // TTA v2.x + // "I have hurried to release the TTA 2.0 encoder. Format documentation is removed from our site. This format still in development. Please wait the TTA2 format, encoder v4." + $ThisFileInfo['tta']['major_version'] = 2; + $ThisFileInfo['avdataoffset'] += 20; + + $ThisFileInfo['tta']['compression_level'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 4, 2)); + $ThisFileInfo['tta']['audio_format'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 6, 2)); + $ThisFileInfo['tta']['channels'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 8, 2)); + $ThisFileInfo['tta']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 10, 2)); + $ThisFileInfo['tta']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 12, 4)); + $ThisFileInfo['tta']['data_length'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 16, 4)); + + $ThisFileInfo['audio']['encoder_options'] = '-e'.$ThisFileInfo['tta']['compression_level']; + $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['tta']['data_length'] / $ThisFileInfo['tta']['sample_rate']; + break; + + case '1': // TTA v3.x + // "This is a first stable release of the TTA format. It will be supported by the encoders v3 or higher." + $ThisFileInfo['tta']['major_version'] = 3; + $ThisFileInfo['avdataoffset'] += 26; + + $ThisFileInfo['tta']['audio_format'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 4, 2)); // getid3_riff::RIFFwFormatTagLookup() + $ThisFileInfo['tta']['channels'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 6, 2)); + $ThisFileInfo['tta']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 8, 2)); + $ThisFileInfo['tta']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 10, 4)); + $ThisFileInfo['tta']['data_length'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 14, 4)); + $ThisFileInfo['tta']['crc32_footer'] = substr($ttaheader, 18, 4); + $ThisFileInfo['tta']['seek_point'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 22, 4)); + + $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['tta']['data_length'] / $ThisFileInfo['tta']['sample_rate']; + break; + + default: + $ThisFileInfo['error'][] = 'This version of getID3() only knows how to handle TTA v1 and v2 - it may not work correctly with this file which appears to be TTA v'.$ttaheader{3}; + return false; + break; + } + + $ThisFileInfo['audio']['encoder'] = 'TTA v'.$ThisFileInfo['tta']['major_version']; + $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['tta']['bits_per_sample']; + $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['tta']['sample_rate']; + $ThisFileInfo['audio']['channels'] = $ThisFileInfo['tta']['channels']; + $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; + + return true; + } + +} + + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.audio.voc.php b/includes/getid3/getid3/module.audio.voc.php new file mode 100644 index 0000000..e93b44f --- /dev/null +++ b/includes/getid3/getid3/module.audio.voc.php @@ -0,0 +1,205 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio.voc.php // +// module for analyzing Creative VOC Audio files // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_voc +{ + + function getid3_voc(&$fd, &$ThisFileInfo) { + + $OriginalAVdataOffset = $ThisFileInfo['avdataoffset']; + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + $VOCheader = fread($fd, 26); + + if (substr($VOCheader, 0, 19) != 'Creative Voice File') { + $ThisFileInfo['error'][] = 'Expecting "Creative Voice File" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($VOCheader, 0, 19).'"'; + return false; + } + + // shortcuts + $thisfile_audio = &$ThisFileInfo['audio']; + $ThisFileInfo['voc'] = array(); + $thisfile_voc = &$ThisFileInfo['voc']; + + $ThisFileInfo['fileformat'] = 'voc'; + $thisfile_audio['dataformat'] = 'voc'; + $thisfile_audio['bitrate_mode'] = 'cbr'; + $thisfile_audio['lossless'] = true; + $thisfile_audio['channels'] = 1; // might be overriden below + $thisfile_audio['bits_per_sample'] = 8; // might be overriden below + + // byte # Description + // ------ ------------------------------------------ + // 00-12 'Creative Voice File' + // 13 1A (eof to abort printing of file) + // 14-15 Offset of first datablock in .voc file (std 1A 00 in Intel Notation) + // 16-17 Version number (minor,major) (VOC-HDR puts 0A 01) + // 18-19 2's Comp of Ver. # + 1234h (VOC-HDR puts 29 11) + + $thisfile_voc['header']['datablock_offset'] = getid3_lib::LittleEndian2Int(substr($VOCheader, 20, 2)); + $thisfile_voc['header']['minor_version'] = getid3_lib::LittleEndian2Int(substr($VOCheader, 22, 1)); + $thisfile_voc['header']['major_version'] = getid3_lib::LittleEndian2Int(substr($VOCheader, 23, 1)); + + do { + + $BlockOffset = ftell($fd); + $BlockData = fread($fd, 4); + $BlockType = ord($BlockData{0}); + $BlockSize = getid3_lib::LittleEndian2Int(substr($BlockData, 1, 3)); + $ThisBlock = array(); + + @$thisfile_voc['blocktypes'][$BlockType]++; + switch ($BlockType) { + case 0: // Terminator + // do nothing, we'll break out of the loop down below + break; + + case 1: // Sound data + $BlockData .= fread($fd, 2); + if ($ThisFileInfo['avdataoffset'] <= $OriginalAVdataOffset) { + $ThisFileInfo['avdataoffset'] = ftell($fd); + } + fseek($fd, $BlockSize - 2, SEEK_CUR); + + $ThisBlock['sample_rate_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 1)); + $ThisBlock['compression_type'] = getid3_lib::LittleEndian2Int(substr($BlockData, 5, 1)); + + $ThisBlock['compression_name'] = $this->VOCcompressionTypeLookup($ThisBlock['compression_type']); + if ($ThisBlock['compression_type'] <= 3) { + $thisfile_voc['compressed_bits_per_sample'] = getid3_lib::CastAsInt(str_replace('-bit', '', $ThisBlock['compression_name'])); + } + + // Less accurate sample_rate calculation than the Extended block (#8) data (but better than nothing if Extended Block is not available) + if (empty($thisfile_audio['sample_rate'])) { + // SR byte = 256 - (1000000 / sample_rate) + $thisfile_audio['sample_rate'] = getid3_lib::trunc((1000000 / (256 - $ThisBlock['sample_rate_id'])) / $thisfile_audio['channels']); + } + break; + + case 2: // Sound continue + case 3: // Silence + case 4: // Marker + case 6: // Repeat + case 7: // End repeat + // nothing useful, just skip + fseek($fd, $BlockSize, SEEK_CUR); + break; + + case 8: // Extended + $BlockData .= fread($fd, 4); + + //00-01 Time Constant: + // Mono: 65536 - (256000000 / sample_rate) + // Stereo: 65536 - (256000000 / (sample_rate * 2)) + $ThisBlock['time_constant'] = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 2)); + $ThisBlock['pack_method'] = getid3_lib::LittleEndian2Int(substr($BlockData, 6, 1)); + $ThisBlock['stereo'] = (bool) getid3_lib::LittleEndian2Int(substr($BlockData, 7, 1)); + + $thisfile_audio['channels'] = ($ThisBlock['stereo'] ? 2 : 1); + $thisfile_audio['sample_rate'] = getid3_lib::trunc((256000000 / (65536 - $ThisBlock['time_constant'])) / $thisfile_audio['channels']); + break; + + case 9: // data block that supersedes blocks 1 and 8. Used for stereo, 16 bit + $BlockData .= fread($fd, 12); + if ($ThisFileInfo['avdataoffset'] <= $OriginalAVdataOffset) { + $ThisFileInfo['avdataoffset'] = ftell($fd); + } + fseek($fd, $BlockSize - 12, SEEK_CUR); + + $ThisBlock['sample_rate'] = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 4)); + $ThisBlock['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($BlockData, 8, 1)); + $ThisBlock['channels'] = getid3_lib::LittleEndian2Int(substr($BlockData, 9, 1)); + $ThisBlock['wFormat'] = getid3_lib::LittleEndian2Int(substr($BlockData, 10, 2)); + + $ThisBlock['compression_name'] = $this->VOCwFormatLookup($ThisBlock['wFormat']); + if ($this->VOCwFormatActualBitsPerSampleLookup($ThisBlock['wFormat'])) { + $thisfile_voc['compressed_bits_per_sample'] = $this->VOCwFormatActualBitsPerSampleLookup($ThisBlock['wFormat']); + } + + $thisfile_audio['sample_rate'] = $ThisBlock['sample_rate']; + $thisfile_audio['bits_per_sample'] = $ThisBlock['bits_per_sample']; + $thisfile_audio['channels'] = $ThisBlock['channels']; + break; + + default: + $ThisFileInfo['warning'][] = 'Unhandled block type "'.$BlockType.'" at offset '.$BlockOffset; + fseek($fd, $BlockSize, SEEK_CUR); + break; + } + + if (!empty($ThisBlock)) { + $ThisBlock['block_offset'] = $BlockOffset; + $ThisBlock['block_size'] = $BlockSize; + $ThisBlock['block_type_id'] = $BlockType; + $thisfile_voc['blocks'][] = $ThisBlock; + } + + } while (!feof($fd) && ($BlockType != 0)); + + // Terminator block doesn't have size field, so seek back 3 spaces + fseek($fd, -3, SEEK_CUR); + + ksort($thisfile_voc['blocktypes']); + + if (!empty($thisfile_voc['compressed_bits_per_sample'])) { + $ThisFileInfo['playtime_seconds'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / ($thisfile_voc['compressed_bits_per_sample'] * $thisfile_audio['channels'] * $thisfile_audio['sample_rate']); + $thisfile_audio['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; + } + + return true; + } + + function VOCcompressionTypeLookup($index) { + static $VOCcompressionTypeLookup = array( + 0 => '8-bit', + 1 => '4-bit', + 2 => '2.6-bit', + 3 => '2-bit' + ); + return (isset($VOCcompressionTypeLookup[$index]) ? $VOCcompressionTypeLookup[$index] : 'Multi DAC ('.($index - 3).') channels'); + } + + function VOCwFormatLookup($index) { + static $VOCwFormatLookup = array( + 0x0000 => '8-bit unsigned PCM', + 0x0001 => 'Creative 8-bit to 4-bit ADPCM', + 0x0002 => 'Creative 8-bit to 3-bit ADPCM', + 0x0003 => 'Creative 8-bit to 2-bit ADPCM', + 0x0004 => '16-bit signed PCM', + 0x0006 => 'CCITT a-Law', + 0x0007 => 'CCITT u-Law', + 0x2000 => 'Creative 16-bit to 4-bit ADPCM' + ); + return (isset($VOCwFormatLookup[$index]) ? $VOCwFormatLookup[$index] : false); + } + + function VOCwFormatActualBitsPerSampleLookup($index) { + static $VOCwFormatLookup = array( + 0x0000 => 8, + 0x0001 => 4, + 0x0002 => 3, + 0x0003 => 2, + 0x0004 => 16, + 0x0006 => 8, + 0x0007 => 8, + 0x2000 => 4 + ); + return (isset($VOCwFormatLookup[$index]) ? $VOCwFormatLookup[$index] : false); + } + +} + + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.audio.vqf.php b/includes/getid3/getid3/module.audio.vqf.php new file mode 100644 index 0000000..49d4e85 --- /dev/null +++ b/includes/getid3/getid3/module.audio.vqf.php @@ -0,0 +1,159 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio.vqf.php // +// module for analyzing VQF audio files // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_vqf +{ + function getid3_vqf(&$fd, &$ThisFileInfo) { + // based loosely on code from TTwinVQ by Jurgen Faul + // http://jfaul.de/atl or http://j-faul.virtualave.net/atl/atl.html + + $ThisFileInfo['fileformat'] = 'vqf'; + $ThisFileInfo['audio']['dataformat'] = 'vqf'; + $ThisFileInfo['audio']['bitrate_mode'] = 'cbr'; + $ThisFileInfo['audio']['lossless'] = false; + + // shortcut + $ThisFileInfo['vqf']['raw'] = array(); + $thisfile_vqf = &$ThisFileInfo['vqf']; + $thisfile_vqf_raw = &$thisfile_vqf['raw']; + + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + $VQFheaderData = fread($fd, 16); + + $offset = 0; + $thisfile_vqf_raw['header_tag'] = substr($VQFheaderData, $offset, 4); + if ($thisfile_vqf_raw['header_tag'] != 'TWIN') { + $ThisFileInfo['error'][] = 'Expecting "TWIN" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$thisfile_vqf_raw['header_tag'].'"'; + unset($ThisFileInfo['vqf']); + unset($ThisFileInfo['fileformat']); + return false; + } + $offset += 4; + $thisfile_vqf_raw['version'] = substr($VQFheaderData, $offset, 8); + $offset += 8; + $thisfile_vqf_raw['size'] = getid3_lib::BigEndian2Int(substr($VQFheaderData, $offset, 4)); + $offset += 4; + + while (ftell($fd) < $ThisFileInfo['avdataend']) { + + $ChunkBaseOffset = ftell($fd); + $chunkoffset = 0; + $ChunkData = fread($fd, 8); + $ChunkName = substr($ChunkData, $chunkoffset, 4); + if ($ChunkName == 'DATA') { + $ThisFileInfo['avdataoffset'] = $ChunkBaseOffset; + break; + } + $chunkoffset += 4; + $ChunkSize = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4)); + $chunkoffset += 4; + if ($ChunkSize > ($ThisFileInfo['avdataend'] - ftell($fd))) { + $ThisFileInfo['error'][] = 'Invalid chunk size ('.$ChunkSize.') for chunk "'.$ChunkName.'" at offset '.$ChunkBaseOffset; + break; + } + if ($ChunkSize > 0) { + $ChunkData .= fread($fd, $ChunkSize); + } + + switch ($ChunkName) { + case 'COMM': + // shortcut + $thisfile_vqf['COMM'] = array(); + $thisfile_vqf_COMM = &$thisfile_vqf['COMM']; + + $thisfile_vqf_COMM['channel_mode'] = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4)); + $chunkoffset += 4; + $thisfile_vqf_COMM['bitrate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4)); + $chunkoffset += 4; + $thisfile_vqf_COMM['sample_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4)); + $chunkoffset += 4; + $thisfile_vqf_COMM['security_level'] = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4)); + $chunkoffset += 4; + + $ThisFileInfo['audio']['channels'] = $thisfile_vqf_COMM['channel_mode'] + 1; + $ThisFileInfo['audio']['sample_rate'] = $this->VQFchannelFrequencyLookup($thisfile_vqf_COMM['sample_rate']); + $ThisFileInfo['audio']['bitrate'] = $thisfile_vqf_COMM['bitrate'] * 1000; + $ThisFileInfo['audio']['encoder_options'] = 'CBR' . ceil($ThisFileInfo['audio']['bitrate']/1000); + + if ($ThisFileInfo['audio']['bitrate'] == 0) { + $ThisFileInfo['error'][] = 'Corrupt VQF file: bitrate_audio == zero'; + return false; + } + break; + + case 'NAME': + case 'AUTH': + case '(c) ': + case 'FILE': + case 'COMT': + case 'ALBM': + $thisfile_vqf['comments'][$this->VQFcommentNiceNameLookup($ChunkName)][] = trim(substr($ChunkData, 8)); + break; + + case 'DSIZ': + $thisfile_vqf['DSIZ'] = getid3_lib::BigEndian2Int(substr($ChunkData, 8, 4)); + break; + + default: + $ThisFileInfo['warning'][] = 'Unhandled chunk type "'.$ChunkName.'" at offset '.$ChunkBaseOffset; + break; + } + } + + $ThisFileInfo['playtime_seconds'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['audio']['bitrate']; + + if (isset($thisfile_vqf['DSIZ']) && (($thisfile_vqf['DSIZ'] != ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'] - strlen('DATA'))))) { + switch ($thisfile_vqf['DSIZ']) { + case 0: + case 1: + $ThisFileInfo['warning'][] = 'Invalid DSIZ value "'.$thisfile_vqf['DSIZ'].'". This is known to happen with VQF files encoded by Ahead Nero, and seems to be its way of saying this is TwinVQF v'.($thisfile_vqf['DSIZ'] + 1).'.0'; + $ThisFileInfo['audio']['encoder'] = 'Ahead Nero'; + break; + + default: + $ThisFileInfo['warning'][] = 'Probable corrupted file - should be '.$thisfile_vqf['DSIZ'].' bytes, actually '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'] - strlen('DATA')); + break; + } + } + + return true; + } + + function VQFchannelFrequencyLookup($frequencyid) { + static $VQFchannelFrequencyLookup = array( + 11 => 11025, + 22 => 22050, + 44 => 44100 + ); + return (isset($VQFchannelFrequencyLookup[$frequencyid]) ? $VQFchannelFrequencyLookup[$frequencyid] : $frequencyid * 1000); + } + + function VQFcommentNiceNameLookup($shortname) { + static $VQFcommentNiceNameLookup = array( + 'NAME' => 'title', + 'AUTH' => 'artist', + '(c) ' => 'copyright', + 'FILE' => 'filename', + 'COMT' => 'comment', + 'ALBM' => 'album' + ); + return (isset($VQFcommentNiceNameLookup[$shortname]) ? $VQFcommentNiceNameLookup[$shortname] : $shortname); + } + +} + + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.audio.wavpack.php b/includes/getid3/getid3/module.audio.wavpack.php new file mode 100644 index 0000000..0435f08 --- /dev/null +++ b/includes/getid3/getid3/module.audio.wavpack.php @@ -0,0 +1,372 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio.wavpack.php // +// module for analyzing WavPack v4.0+ Audio files // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_wavpack +{ + + function getid3_wavpack(&$fd, &$ThisFileInfo) { + + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + + while (true) { + + $wavpackheader = fread($fd, 32); + + if (ftell($fd) >= $ThisFileInfo['avdataend']) { + break; + } elseif (feof($fd)) { + break; + } elseif ( + (@$ThisFileInfo['wavpack']['blockheader']['total_samples'] > 0) && + (@$ThisFileInfo['wavpack']['blockheader']['block_samples'] > 0) && + (!isset($ThisFileInfo['wavpack']['riff_trailer_size']) || ($ThisFileInfo['wavpack']['riff_trailer_size'] <= 0)) && + ((@$ThisFileInfo['wavpack']['config_flags']['md5_checksum'] === false) || !empty($ThisFileInfo['md5_data_source']))) { + break; + } + + $blockheader_offset = ftell($fd) - 32; + $blockheader_magic = substr($wavpackheader, 0, 4); + $blockheader_size = getid3_lib::LittleEndian2Int(substr($wavpackheader, 4, 4)); + + if ($blockheader_magic != 'wvpk') { + $ThisFileInfo['error'][] = 'Expecting "wvpk" at offset '.$blockheader_offset.', found "'.$blockheader_magic.'"'; + if ((@$ThisFileInfo['audio']['dataformat'] != 'wavpack') && (@$ThisFileInfo['audio']['dataformat'] != 'wvc')) { + unset($ThisFileInfo['fileformat']); + unset($ThisFileInfo['audio']); + unset($ThisFileInfo['wavpack']); + } + return false; + } + + + if ((@$ThisFileInfo['wavpack']['blockheader']['block_samples'] <= 0) || + (@$ThisFileInfo['wavpack']['blockheader']['total_samples'] <= 0)) { + // Also, it is possible that the first block might not have + // any samples (block_samples == 0) and in this case you should skip blocks + // until you find one with samples because the other information (like + // total_samples) are not guaranteed to be correct until (block_samples > 0) + + // Finally, I have defined a format for files in which the length is not known + // (for example when raw files are created using pipes). In these cases + // total_samples will be -1 and you must seek to the final block to determine + // the total number of samples. + + + $ThisFileInfo['audio']['dataformat'] = 'wavpack'; + $ThisFileInfo['fileformat'] = 'wavpack'; + $ThisFileInfo['audio']['lossless'] = true; + $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; + + $ThisFileInfo['wavpack']['blockheader']['offset'] = $blockheader_offset; + $ThisFileInfo['wavpack']['blockheader']['magic'] = $blockheader_magic; + $ThisFileInfo['wavpack']['blockheader']['size'] = $blockheader_size; + + if ($ThisFileInfo['wavpack']['blockheader']['size'] >= 0x100000) { + $ThisFileInfo['error'][] = 'Expecting WavPack block size less than "0x100000", found "'.$ThisFileInfo['wavpack']['blockheader']['size'].'" at offset '.$ThisFileInfo['wavpack']['blockheader']['offset']; + if ((@$ThisFileInfo['audio']['dataformat'] != 'wavpack') && (@$ThisFileInfo['audio']['dataformat'] != 'wvc')) { + unset($ThisFileInfo['fileformat']); + unset($ThisFileInfo['audio']); + unset($ThisFileInfo['wavpack']); + } + return false; + } + + $ThisFileInfo['wavpack']['blockheader']['minor_version'] = ord($wavpackheader{8}); + $ThisFileInfo['wavpack']['blockheader']['major_version'] = ord($wavpackheader{9}); + + if (($ThisFileInfo['wavpack']['blockheader']['major_version'] != 4) || + (($ThisFileInfo['wavpack']['blockheader']['minor_version'] < 4) && + ($ThisFileInfo['wavpack']['blockheader']['minor_version'] > 16))) { + $ThisFileInfo['error'][] = 'Expecting WavPack version between "4.2" and "4.16", found version "'.$ThisFileInfo['wavpack']['blockheader']['major_version'].'.'.$ThisFileInfo['wavpack']['blockheader']['minor_version'].'" at offset '.$ThisFileInfo['wavpack']['blockheader']['offset']; + if ((@$ThisFileInfo['audio']['dataformat'] != 'wavpack') && (@$ThisFileInfo['audio']['dataformat'] != 'wvc')) { + unset($ThisFileInfo['fileformat']); + unset($ThisFileInfo['audio']); + unset($ThisFileInfo['wavpack']); + } + return false; + } + + $ThisFileInfo['wavpack']['blockheader']['track_number'] = ord($wavpackheader{10}); // unused + $ThisFileInfo['wavpack']['blockheader']['index_number'] = ord($wavpackheader{11}); // unused + $ThisFileInfo['wavpack']['blockheader']['total_samples'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 12, 4)); + $ThisFileInfo['wavpack']['blockheader']['block_index'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 16, 4)); + $ThisFileInfo['wavpack']['blockheader']['block_samples'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 20, 4)); + $ThisFileInfo['wavpack']['blockheader']['flags_raw'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 24, 4)); + $ThisFileInfo['wavpack']['blockheader']['crc'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 28, 4)); + + $ThisFileInfo['wavpack']['blockheader']['flags']['bytes_per_sample'] = 1 + ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000003); + $ThisFileInfo['wavpack']['blockheader']['flags']['mono'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000004); + $ThisFileInfo['wavpack']['blockheader']['flags']['hybrid'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000008); + $ThisFileInfo['wavpack']['blockheader']['flags']['joint_stereo'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000010); + $ThisFileInfo['wavpack']['blockheader']['flags']['cross_decorrelation'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000020); + $ThisFileInfo['wavpack']['blockheader']['flags']['hybrid_noiseshape'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000040); + $ThisFileInfo['wavpack']['blockheader']['flags']['ieee_32bit_float'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000080); + $ThisFileInfo['wavpack']['blockheader']['flags']['int_32bit'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000100); + $ThisFileInfo['wavpack']['blockheader']['flags']['hybrid_bitrate_noise'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000200); + $ThisFileInfo['wavpack']['blockheader']['flags']['hybrid_balance_noise'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000400); + $ThisFileInfo['wavpack']['blockheader']['flags']['multichannel_initial'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000800); + $ThisFileInfo['wavpack']['blockheader']['flags']['multichannel_final'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00001000); + + $ThisFileInfo['audio']['lossless'] = !$ThisFileInfo['wavpack']['blockheader']['flags']['hybrid']; + } + + while (!feof($fd) && (ftell($fd) < ($blockheader_offset + $blockheader_size + 8))) { + + $metablock = array('offset'=>ftell($fd)); + $metablockheader = fread($fd, 2); + if (feof($fd)) { + break; + } + $metablock['id'] = ord($metablockheader{0}); + $metablock['function_id'] = ($metablock['id'] & 0x3F); + $metablock['function_name'] = $this->WavPackMetablockNameLookup($metablock['function_id']); + + // The 0x20 bit in the id of the meta subblocks (which is defined as + // ID_OPTIONAL_DATA) is a permanent part of the id. The idea is that + // if a decoder encounters an id that it does not know about, it uses + // that "ID_OPTIONAL_DATA" flag to determine what to do. If it is set + // then the decoder simply ignores the metadata, but if it is zero + // then the decoder should quit because it means that an understanding + // of the metadata is required to correctly decode the audio. + $metablock['non_decoder'] = (bool) ($metablock['id'] & 0x20); + + $metablock['padded_data'] = (bool) ($metablock['id'] & 0x40); + $metablock['large_block'] = (bool) ($metablock['id'] & 0x80); + if ($metablock['large_block']) { + $metablockheader .= fread($fd, 2); + } + $metablock['size'] = getid3_lib::LittleEndian2Int(substr($metablockheader, 1)) * 2; // size is stored in words + $metablock['data'] = null; + + if ($metablock['size'] > 0) { + + switch ($metablock['function_id']) { + case 0x21: // ID_RIFF_HEADER + case 0x22: // ID_RIFF_TRAILER + case 0x23: // ID_REPLAY_GAIN + case 0x24: // ID_CUESHEET + case 0x25: // ID_CONFIG_BLOCK + case 0x26: // ID_MD5_CHECKSUM + $metablock['data'] = fread($fd, $metablock['size']); + + if ($metablock['padded_data']) { + // padded to the nearest even byte + $metablock['size']--; + $metablock['data'] = substr($metablock['data'], 0, -1); + } + break; + + case 0x00: // ID_DUMMY + case 0x01: // ID_ENCODER_INFO + case 0x02: // ID_DECORR_TERMS + case 0x03: // ID_DECORR_WEIGHTS + case 0x04: // ID_DECORR_SAMPLES + case 0x05: // ID_ENTROPY_VARS + case 0x06: // ID_HYBRID_PROFILE + case 0x07: // ID_SHAPING_WEIGHTS + case 0x08: // ID_FLOAT_INFO + case 0x09: // ID_INT32_INFO + case 0x0A: // ID_WV_BITSTREAM + case 0x0B: // ID_WVC_BITSTREAM + case 0x0C: // ID_WVX_BITSTREAM + case 0x0D: // ID_CHANNEL_INFO + fseek($fd, $metablock['offset'] + ($metablock['large_block'] ? 4 : 2) + $metablock['size'], SEEK_SET); + break; + + default: + $ThisFileInfo['warning'][] = 'Unexpected metablock type "0x'.str_pad(dechex($metablock['function_id']), 2, '0', STR_PAD_LEFT).'" at offset '.$metablock['offset']; + fseek($fd, $metablock['offset'] + ($metablock['large_block'] ? 4 : 2) + $metablock['size'], SEEK_SET); + break; + } + + switch ($metablock['function_id']) { + case 0x21: // ID_RIFF_HEADER + getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true); + $original_wav_filesize = getid3_lib::LittleEndian2Int(substr($metablock['data'], 4, 4)); + getid3_riff::ParseRIFFdata($metablock['data'], $ParsedRIFFheader); + $metablock['riff'] = $ParsedRIFFheader['riff']; + $metablock['riff']['original_filesize'] = $original_wav_filesize; + $ThisFileInfo['wavpack']['riff_trailer_size'] = $original_wav_filesize - $metablock['riff']['WAVE']['data'][0]['size'] - $metablock['riff']['header_size']; + + $ThisFileInfo['audio']['sample_rate'] = $ParsedRIFFheader['riff']['raw']['fmt ']['nSamplesPerSec']; + $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['wavpack']['blockheader']['total_samples'] / $ThisFileInfo['audio']['sample_rate']; + + // Safe RIFF header in case there's a RIFF footer later + $metablockRIFFheader = $metablock['data']; + break; + + + case 0x22: // ID_RIFF_TRAILER + $metablockRIFFfooter = $metablockRIFFheader.$metablock['data']; + getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true); + + $ftell_old = ftell($fd); + $startoffset = $metablock['offset'] + ($metablock['large_block'] ? 4 : 2); + $ParsedRIFFfooter = array('avdataend'=>$ThisFileInfo['avdataend'], 'fileformat'=>'riff', 'error'=>array(), 'warning'=>array()); + $metablock['riff'] = getid3_riff::ParseRIFF($fd, $startoffset, $startoffset + $metablock['size'], $ParsedRIFFfooter); + fseek($fd, $ftell_old, SEEK_SET); + + if (!empty($metablock['riff']['INFO'])) { + getid3_riff::RIFFcommentsParse($metablock['riff']['INFO'], $metablock['comments']); + $ThisFileInfo['tags']['riff'] = $metablock['comments']; + } + break; + + + case 0x23: // ID_REPLAY_GAIN + $ThisFileInfo['warning'][] = 'WavPack "Replay Gain" contents not yet handled by getID3() in metablock at offset '.$metablock['offset']; + break; + + + case 0x24: // ID_CUESHEET + $ThisFileInfo['warning'][] = 'WavPack "Cuesheet" contents not yet handled by getID3() in metablock at offset '.$metablock['offset']; + break; + + + case 0x25: // ID_CONFIG_BLOCK + $metablock['flags_raw'] = getid3_lib::LittleEndian2Int(substr($metablock['data'], 0, 3)); + + $metablock['flags']['adobe_mode'] = (bool) ($metablock['flags_raw'] & 0x000001); // "adobe" mode for 32-bit floats + $metablock['flags']['fast_flag'] = (bool) ($metablock['flags_raw'] & 0x000002); // fast mode + $metablock['flags']['very_fast_flag'] = (bool) ($metablock['flags_raw'] & 0x000004); // double fast + $metablock['flags']['high_flag'] = (bool) ($metablock['flags_raw'] & 0x000008); // high quality mode + $metablock['flags']['very_high_flag'] = (bool) ($metablock['flags_raw'] & 0x000010); // double high (not used yet) + $metablock['flags']['bitrate_kbps'] = (bool) ($metablock['flags_raw'] & 0x000020); // bitrate is kbps, not bits / sample + $metablock['flags']['auto_shaping'] = (bool) ($metablock['flags_raw'] & 0x000040); // automatic noise shaping + $metablock['flags']['shape_override'] = (bool) ($metablock['flags_raw'] & 0x000080); // shaping mode specified + $metablock['flags']['joint_override'] = (bool) ($metablock['flags_raw'] & 0x000100); // joint-stereo mode specified + $metablock['flags']['copy_time'] = (bool) ($metablock['flags_raw'] & 0x000200); // copy file-time from source + $metablock['flags']['create_exe'] = (bool) ($metablock['flags_raw'] & 0x000400); // create executable + $metablock['flags']['create_wvc'] = (bool) ($metablock['flags_raw'] & 0x000800); // create correction file + $metablock['flags']['optimize_wvc'] = (bool) ($metablock['flags_raw'] & 0x001000); // maximize bybrid compression + $metablock['flags']['quality_mode'] = (bool) ($metablock['flags_raw'] & 0x002000); // psychoacoustic quality mode + $metablock['flags']['raw_flag'] = (bool) ($metablock['flags_raw'] & 0x004000); // raw mode (not implemented yet) + $metablock['flags']['calc_noise'] = (bool) ($metablock['flags_raw'] & 0x008000); // calc noise in hybrid mode + $metablock['flags']['lossy_mode'] = (bool) ($metablock['flags_raw'] & 0x010000); // obsolete (for information) + $metablock['flags']['extra_mode'] = (bool) ($metablock['flags_raw'] & 0x020000); // extra processing mode + $metablock['flags']['skip_wvx'] = (bool) ($metablock['flags_raw'] & 0x040000); // no wvx stream w/ floats & big ints + $metablock['flags']['md5_checksum'] = (bool) ($metablock['flags_raw'] & 0x080000); // compute & store MD5 signature + $metablock['flags']['quiet_mode'] = (bool) ($metablock['flags_raw'] & 0x100000); // don't report progress % + + $ThisFileInfo['wavpack']['config_flags'] = $metablock['flags']; + + + if ($ThisFileInfo['wavpack']['blockheader']['flags']['hybrid']) { + @$ThisFileInfo['audio']['encoder_options'] .= ' -b???'; + } + @$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['adobe_mode'] ? ' -a' : ''); + @$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['optimize_wvc'] ? ' -cc' : ''); + @$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['create_exe'] ? ' -e' : ''); + @$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['fast_flag'] ? ' -f' : ''); + @$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['joint_override'] ? ' -j?' : ''); + @$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['high_flag'] ? ' -h' : ''); + @$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['md5_checksum'] ? ' -m' : ''); + @$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['calc_noise'] ? ' -n' : ''); + @$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['shape_override'] ? ' -s?' : ''); + @$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['extra_mode'] ? ' -x?' : ''); + if (@$ThisFileInfo['audio']['encoder_options']) { + $ThisFileInfo['audio']['encoder_options'] = trim(@$ThisFileInfo['audio']['encoder_options']); + } + elseif (isset($ThisFileInfo['audio']['encoder_options'])) { + unset($ThisFileInfo['audio']['encoder_options']); + } + break; + + + case 0x26: // ID_MD5_CHECKSUM + if (strlen($metablock['data']) == 16) { + $ThisFileInfo['md5_data_source'] = strtolower(getid3_lib::PrintHexBytes($metablock['data'], true, false, false)); + } else { + $ThisFileInfo['warning'][] = 'Expecting 16 bytes of WavPack "MD5 Checksum" in metablock at offset '.$metablock['offset'].', but found '.strlen($metablock['data']).' bytes'; + } + break; + + + case 0x00: // ID_DUMMY + case 0x01: // ID_ENCODER_INFO + case 0x02: // ID_DECORR_TERMS + case 0x03: // ID_DECORR_WEIGHTS + case 0x04: // ID_DECORR_SAMPLES + case 0x05: // ID_ENTROPY_VARS + case 0x06: // ID_HYBRID_PROFILE + case 0x07: // ID_SHAPING_WEIGHTS + case 0x08: // ID_FLOAT_INFO + case 0x09: // ID_INT32_INFO + case 0x0A: // ID_WV_BITSTREAM + case 0x0B: // ID_WVC_BITSTREAM + case 0x0C: // ID_WVX_BITSTREAM + case 0x0D: // ID_CHANNEL_INFO + unset($metablock); + break; + } + + } + if (!empty($metablock)) { + $ThisFileInfo['wavpack']['metablocks'][] = $metablock; + } + + } + + } + + $ThisFileInfo['audio']['encoder'] = 'WavPack v'.$ThisFileInfo['wavpack']['blockheader']['major_version'].'.'.str_pad($ThisFileInfo['wavpack']['blockheader']['minor_version'], 2, '0', STR_PAD_LEFT); + $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['wavpack']['blockheader']['flags']['bytes_per_sample'] * 8; + $ThisFileInfo['audio']['channels'] = ($ThisFileInfo['wavpack']['blockheader']['flags']['mono'] ? 1 : 2); + + if (@$ThisFileInfo['playtime_seconds']) { + + $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds']; + + } else { + + $ThisFileInfo['audio']['dataformat'] = 'wvc'; + + } + + return true; + } + + + function WavPackMetablockNameLookup(&$id) { + static $WavPackMetablockNameLookup = array( + 0x00 => 'Dummy', + 0x01 => 'Encoder Info', + 0x02 => 'Decorrelation Terms', + 0x03 => 'Decorrelation Weights', + 0x04 => 'Decorrelation Samples', + 0x05 => 'Entropy Variables', + 0x06 => 'Hybrid Profile', + 0x07 => 'Shaping Weights', + 0x08 => 'Float Info', + 0x09 => 'Int32 Info', + 0x0A => 'WV Bitstream', + 0x0B => 'WVC Bitstream', + 0x0C => 'WVX Bitstream', + 0x0D => 'Channel Info', + 0x21 => 'RIFF header', + 0x22 => 'RIFF trailer', + 0x23 => 'Replay Gain', + 0x24 => 'Cuesheet', + 0x25 => 'Config Block', + 0x26 => 'MD5 Checksum', + ); + return (@$WavPackMetablockNameLookup[$id]); + } + +} + + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.graphic.bmp.php b/includes/getid3/getid3/module.graphic.bmp.php new file mode 100644 index 0000000..dc6d733 --- /dev/null +++ b/includes/getid3/getid3/module.graphic.bmp.php @@ -0,0 +1,683 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.graphic.bmp.php // +// module for analyzing BMP Image files // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_bmp +{ + + function getid3_bmp(&$fd, &$ThisFileInfo, $ExtractPalette=false, $ExtractData=false) { + + // shortcuts + $ThisFileInfo['bmp']['header']['raw'] = array(); + $thisfile_bmp = &$ThisFileInfo['bmp']; + $thisfile_bmp_header = &$thisfile_bmp['header']; + $thisfile_bmp_header_raw = &$thisfile_bmp_header['raw']; + + // BITMAPFILEHEADER [14 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_62uq.asp + // all versions + // WORD bfType; + // DWORD bfSize; + // WORD bfReserved1; + // WORD bfReserved2; + // DWORD bfOffBits; + + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + $offset = 0; + $BMPheader = fread($fd, 14 + 40); + + $thisfile_bmp_header_raw['identifier'] = substr($BMPheader, $offset, 2); + $offset += 2; + + if ($thisfile_bmp_header_raw['identifier'] != 'BM') { + $ThisFileInfo['error'][] = 'Expecting "BM" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$thisfile_bmp_header_raw['identifier'].'"'; + unset($ThisFileInfo['fileformat']); + unset($ThisFileInfo['bmp']); + return false; + } + + $thisfile_bmp_header_raw['filesize'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['reserved1'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2)); + $offset += 2; + $thisfile_bmp_header_raw['reserved2'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2)); + $offset += 2; + $thisfile_bmp_header_raw['data_offset'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['header_size'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + + + // check if the hardcoded-to-1 "planes" is at offset 22 or 26 + $planes22 = getid3_lib::LittleEndian2Int(substr($BMPheader, 22, 2)); + $planes26 = getid3_lib::LittleEndian2Int(substr($BMPheader, 26, 2)); + if (($planes22 == 1) && ($planes26 != 1)) { + $thisfile_bmp['type_os'] = 'OS/2'; + $thisfile_bmp['type_version'] = 1; + } elseif (($planes26 == 1) && ($planes22 != 1)) { + $thisfile_bmp['type_os'] = 'Windows'; + $thisfile_bmp['type_version'] = 1; + } elseif ($thisfile_bmp_header_raw['header_size'] == 12) { + $thisfile_bmp['type_os'] = 'OS/2'; + $thisfile_bmp['type_version'] = 1; + } elseif ($thisfile_bmp_header_raw['header_size'] == 40) { + $thisfile_bmp['type_os'] = 'Windows'; + $thisfile_bmp['type_version'] = 1; + } elseif ($thisfile_bmp_header_raw['header_size'] == 84) { + $thisfile_bmp['type_os'] = 'Windows'; + $thisfile_bmp['type_version'] = 4; + } elseif ($thisfile_bmp_header_raw['header_size'] == 100) { + $thisfile_bmp['type_os'] = 'Windows'; + $thisfile_bmp['type_version'] = 5; + } else { + $ThisFileInfo['error'][] = 'Unknown BMP subtype (or not a BMP file)'; + unset($ThisFileInfo['fileformat']); + unset($ThisFileInfo['bmp']); + return false; + } + + $ThisFileInfo['fileformat'] = 'bmp'; + $ThisFileInfo['video']['dataformat'] = 'bmp'; + $ThisFileInfo['video']['lossless'] = true; + $ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1; + + if ($thisfile_bmp['type_os'] == 'OS/2') { + + // OS/2-format BMP + // http://netghost.narod.ru/gff/graphics/summary/os2bmp.htm + + // DWORD Size; /* Size of this structure in bytes */ + // DWORD Width; /* Bitmap width in pixels */ + // DWORD Height; /* Bitmap height in pixel */ + // WORD NumPlanes; /* Number of bit planes (color depth) */ + // WORD BitsPerPixel; /* Number of bits per pixel per plane */ + + $thisfile_bmp_header_raw['width'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2)); + $offset += 2; + $thisfile_bmp_header_raw['height'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2)); + $offset += 2; + $thisfile_bmp_header_raw['planes'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2)); + $offset += 2; + $thisfile_bmp_header_raw['bits_per_pixel'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2)); + $offset += 2; + + $ThisFileInfo['video']['resolution_x'] = $thisfile_bmp_header_raw['width']; + $ThisFileInfo['video']['resolution_y'] = $thisfile_bmp_header_raw['height']; + $ThisFileInfo['video']['codec'] = 'BI_RGB '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit'; + $ThisFileInfo['video']['bits_per_sample'] = $thisfile_bmp_header_raw['bits_per_pixel']; + + if ($thisfile_bmp['type_version'] >= 2) { + // DWORD Compression; /* Bitmap compression scheme */ + // DWORD ImageDataSize; /* Size of bitmap data in bytes */ + // DWORD XResolution; /* X resolution of display device */ + // DWORD YResolution; /* Y resolution of display device */ + // DWORD ColorsUsed; /* Number of color table indices used */ + // DWORD ColorsImportant; /* Number of important color indices */ + // WORD Units; /* Type of units used to measure resolution */ + // WORD Reserved; /* Pad structure to 4-byte boundary */ + // WORD Recording; /* Recording algorithm */ + // WORD Rendering; /* Halftoning algorithm used */ + // DWORD Size1; /* Reserved for halftoning algorithm use */ + // DWORD Size2; /* Reserved for halftoning algorithm use */ + // DWORD ColorEncoding; /* Color model used in bitmap */ + // DWORD Identifier; /* Reserved for application use */ + + $thisfile_bmp_header_raw['compression'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['bmp_data_size'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['resolution_h'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['resolution_v'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['colors_used'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['colors_important'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['resolution_units'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2)); + $offset += 2; + $thisfile_bmp_header_raw['reserved1'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2)); + $offset += 2; + $thisfile_bmp_header_raw['recording'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2)); + $offset += 2; + $thisfile_bmp_header_raw['rendering'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2)); + $offset += 2; + $thisfile_bmp_header_raw['size1'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['size2'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['color_encoding'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['identifier'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + + $thisfile_bmp_header['compression'] = $this->BMPcompressionOS2Lookup($thisfile_bmp_header_raw['compression']); + + $ThisFileInfo['video']['codec'] = $thisfile_bmp_header['compression'].' '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit'; + } + + } elseif ($thisfile_bmp['type_os'] == 'Windows') { + + // Windows-format BMP + + // BITMAPINFOHEADER - [40 bytes] http://msdn.microsoft.com/library/en-us/gdi/bitmaps_1rw2.asp + // all versions + // DWORD biSize; + // LONG biWidth; + // LONG biHeight; + // WORD biPlanes; + // WORD biBitCount; + // DWORD biCompression; + // DWORD biSizeImage; + // LONG biXPelsPerMeter; + // LONG biYPelsPerMeter; + // DWORD biClrUsed; + // DWORD biClrImportant; + + $thisfile_bmp_header_raw['width'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4), true); + $offset += 4; + $thisfile_bmp_header_raw['height'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4), true); + $offset += 4; + $thisfile_bmp_header_raw['planes'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2)); + $offset += 2; + $thisfile_bmp_header_raw['bits_per_pixel'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2)); + $offset += 2; + $thisfile_bmp_header_raw['compression'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['bmp_data_size'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['resolution_h'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4), true); + $offset += 4; + $thisfile_bmp_header_raw['resolution_v'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4), true); + $offset += 4; + $thisfile_bmp_header_raw['colors_used'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['colors_important'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + + $thisfile_bmp_header['compression'] = $this->BMPcompressionWindowsLookup($thisfile_bmp_header_raw['compression']); + $ThisFileInfo['video']['resolution_x'] = $thisfile_bmp_header_raw['width']; + $ThisFileInfo['video']['resolution_y'] = $thisfile_bmp_header_raw['height']; + $ThisFileInfo['video']['codec'] = $thisfile_bmp_header['compression'].' '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit'; + $ThisFileInfo['video']['bits_per_sample'] = $thisfile_bmp_header_raw['bits_per_pixel']; + + if (($thisfile_bmp['type_version'] >= 4) || ($thisfile_bmp_header_raw['compression'] == 3)) { + // should only be v4+, but BMPs with type_version==1 and BI_BITFIELDS compression have been seen + $BMPheader .= fread($fd, 44); + + // BITMAPV4HEADER - [44 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_2k1e.asp + // Win95+, WinNT4.0+ + // DWORD bV4RedMask; + // DWORD bV4GreenMask; + // DWORD bV4BlueMask; + // DWORD bV4AlphaMask; + // DWORD bV4CSType; + // CIEXYZTRIPLE bV4Endpoints; + // DWORD bV4GammaRed; + // DWORD bV4GammaGreen; + // DWORD bV4GammaBlue; + $thisfile_bmp_header_raw['red_mask'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['green_mask'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['blue_mask'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['alpha_mask'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['cs_type'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['ciexyz_red'] = substr($BMPheader, $offset, 4); + $offset += 4; + $thisfile_bmp_header_raw['ciexyz_green'] = substr($BMPheader, $offset, 4); + $offset += 4; + $thisfile_bmp_header_raw['ciexyz_blue'] = substr($BMPheader, $offset, 4); + $offset += 4; + $thisfile_bmp_header_raw['gamma_red'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['gamma_green'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['gamma_blue'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + + $thisfile_bmp_header['ciexyz_red'] = getid3_lib::FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_red'])); + $thisfile_bmp_header['ciexyz_green'] = getid3_lib::FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_green'])); + $thisfile_bmp_header['ciexyz_blue'] = getid3_lib::FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_blue'])); + } + + if ($thisfile_bmp['type_version'] >= 5) { + $BMPheader .= fread($fd, 16); + + // BITMAPV5HEADER - [16 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_7c36.asp + // Win98+, Win2000+ + // DWORD bV5Intent; + // DWORD bV5ProfileData; + // DWORD bV5ProfileSize; + // DWORD bV5Reserved; + $thisfile_bmp_header_raw['intent'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['profile_data_offset'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['profile_data_size'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['reserved3'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + } + + } else { + + $ThisFileInfo['error'][] = 'Unknown BMP format in header.'; + return false; + + } + + + if ($ExtractPalette || $ExtractData) { + $PaletteEntries = 0; + if ($thisfile_bmp_header_raw['bits_per_pixel'] < 16) { + $PaletteEntries = pow(2, $thisfile_bmp_header_raw['bits_per_pixel']); + } elseif (isset($thisfile_bmp_header_raw['colors_used']) && ($thisfile_bmp_header_raw['colors_used'] > 0) && ($thisfile_bmp_header_raw['colors_used'] <= 256)) { + $PaletteEntries = $thisfile_bmp_header_raw['colors_used']; + } + if ($PaletteEntries > 0) { + $BMPpalette = fread($fd, 4 * $PaletteEntries); + $paletteoffset = 0; + for ($i = 0; $i < $PaletteEntries; $i++) { + // RGBQUAD - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_5f8y.asp + // BYTE rgbBlue; + // BYTE rgbGreen; + // BYTE rgbRed; + // BYTE rgbReserved; + $blue = getid3_lib::LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1)); + $green = getid3_lib::LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1)); + $red = getid3_lib::LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1)); + if (($thisfile_bmp['type_os'] == 'OS/2') && ($thisfile_bmp['type_version'] == 1)) { + // no padding byte + } else { + $paletteoffset++; // padding byte + } + $thisfile_bmp['palette'][$i] = (($red << 16) | ($green << 8) | $blue); + } + } + } + + if ($ExtractData) { + fseek($fd, $thisfile_bmp_header_raw['data_offset'], SEEK_SET); + $RowByteLength = ceil(($thisfile_bmp_header_raw['width'] * ($thisfile_bmp_header_raw['bits_per_pixel'] / 8)) / 4) * 4; // round up to nearest DWORD boundry + $BMPpixelData = fread($fd, $thisfile_bmp_header_raw['height'] * $RowByteLength); + $pixeldataoffset = 0; + switch (@$thisfile_bmp_header_raw['compression']) { + + case 0: // BI_RGB + switch ($thisfile_bmp_header_raw['bits_per_pixel']) { + case 1: + for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) { + for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col = $col) { + $paletteindexbyte = ord($BMPpixelData{$pixeldataoffset++}); + for ($i = 7; $i >= 0; $i--) { + $paletteindex = ($paletteindexbyte & (0x01 << $i)) >> $i; + $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex]; + $col++; + } + } + while (($pixeldataoffset % 4) != 0) { + // lines are padded to nearest DWORD + $pixeldataoffset++; + } + } + break; + + case 4: + for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) { + for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col = $col) { + $paletteindexbyte = ord($BMPpixelData{$pixeldataoffset++}); + for ($i = 1; $i >= 0; $i--) { + $paletteindex = ($paletteindexbyte & (0x0F << (4 * $i))) >> (4 * $i); + $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex]; + $col++; + } + } + while (($pixeldataoffset % 4) != 0) { + // lines are padded to nearest DWORD + $pixeldataoffset++; + } + } + break; + + case 8: + for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) { + for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) { + $paletteindex = ord($BMPpixelData{$pixeldataoffset++}); + $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex]; + } + while (($pixeldataoffset % 4) != 0) { + // lines are padded to nearest DWORD + $pixeldataoffset++; + } + } + break; + + case 24: + for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) { + for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) { + $thisfile_bmp['data'][$row][$col] = (ord($BMPpixelData{$pixeldataoffset+2}) << 16) | (ord($BMPpixelData{$pixeldataoffset+1}) << 8) | ord($BMPpixelData{$pixeldataoffset}); + $pixeldataoffset += 3; + } + while (($pixeldataoffset % 4) != 0) { + // lines are padded to nearest DWORD + $pixeldataoffset++; + } + } + break; + + case 32: + for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) { + for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) { + $thisfile_bmp['data'][$row][$col] = (ord($BMPpixelData{$pixeldataoffset+3}) << 24) | (ord($BMPpixelData{$pixeldataoffset+2}) << 16) | (ord($BMPpixelData{$pixeldataoffset+1}) << 8) | ord($BMPpixelData{$pixeldataoffset}); + $pixeldataoffset += 4; + } + while (($pixeldataoffset % 4) != 0) { + // lines are padded to nearest DWORD + $pixeldataoffset++; + } + } + break; + + case 16: + // ? + break; + + default: + $ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data'; + break; + } + break; + + + case 1: // BI_RLE8 - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_6x0u.asp + switch ($thisfile_bmp_header_raw['bits_per_pixel']) { + case 8: + $pixelcounter = 0; + while ($pixeldataoffset < strlen($BMPpixelData)) { + $firstbyte = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1)); + $secondbyte = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1)); + if ($firstbyte == 0) { + + // escaped/absolute mode - the first byte of the pair can be set to zero to + // indicate an escape character that denotes the end of a line, the end of + // a bitmap, or a delta, depending on the value of the second byte. + switch ($secondbyte) { + case 0: + // end of line + // no need for special processing, just ignore + break; + + case 1: + // end of bitmap + $pixeldataoffset = strlen($BMPpixelData); // force to exit loop just in case + break; + + case 2: + // delta - The 2 bytes following the escape contain unsigned values + // indicating the horizontal and vertical offsets of the next pixel + // from the current position. + $colincrement = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1)); + $rowincrement = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1)); + $col = ($pixelcounter % $thisfile_bmp_header_raw['width']) + $colincrement; + $row = ($thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width'])) - $rowincrement; + $pixelcounter = ($row * $thisfile_bmp_header_raw['width']) + $col; + break; + + default: + // In absolute mode, the first byte is zero and the second byte is a + // value in the range 03H through FFH. The second byte represents the + // number of bytes that follow, each of which contains the color index + // of a single pixel. Each run must be aligned on a word boundary. + for ($i = 0; $i < $secondbyte; $i++) { + $paletteindex = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1)); + $col = $pixelcounter % $thisfile_bmp_header_raw['width']; + $row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']); + $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex]; + $pixelcounter++; + } + while (($pixeldataoffset % 2) != 0) { + // Each run must be aligned on a word boundary. + $pixeldataoffset++; + } + break; + } + + } else { + + // encoded mode - the first byte specifies the number of consecutive pixels + // to be drawn using the color index contained in the second byte. + for ($i = 0; $i < $firstbyte; $i++) { + $col = $pixelcounter % $thisfile_bmp_header_raw['width']; + $row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']); + $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$secondbyte]; + $pixelcounter++; + } + + } + } + break; + + default: + $ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data'; + break; + } + break; + + + + case 2: // BI_RLE4 - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_6x0u.asp + switch ($thisfile_bmp_header_raw['bits_per_pixel']) { + case 4: + $pixelcounter = 0; + while ($pixeldataoffset < strlen($BMPpixelData)) { + $firstbyte = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1)); + $secondbyte = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1)); + if ($firstbyte == 0) { + + // escaped/absolute mode - the first byte of the pair can be set to zero to + // indicate an escape character that denotes the end of a line, the end of + // a bitmap, or a delta, depending on the value of the second byte. + switch ($secondbyte) { + case 0: + // end of line + // no need for special processing, just ignore + break; + + case 1: + // end of bitmap + $pixeldataoffset = strlen($BMPpixelData); // force to exit loop just in case + break; + + case 2: + // delta - The 2 bytes following the escape contain unsigned values + // indicating the horizontal and vertical offsets of the next pixel + // from the current position. + $colincrement = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1)); + $rowincrement = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1)); + $col = ($pixelcounter % $thisfile_bmp_header_raw['width']) + $colincrement; + $row = ($thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width'])) - $rowincrement; + $pixelcounter = ($row * $thisfile_bmp_header_raw['width']) + $col; + break; + + default: + // In absolute mode, the first byte is zero. The second byte contains the number + // of color indexes that follow. Subsequent bytes contain color indexes in their + // high- and low-order 4 bits, one color index for each pixel. In absolute mode, + // each run must be aligned on a word boundary. + unset($paletteindexes); + for ($i = 0; $i < ceil($secondbyte / 2); $i++) { + $paletteindexbyte = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1)); + $paletteindexes[] = ($paletteindexbyte & 0xF0) >> 4; + $paletteindexes[] = ($paletteindexbyte & 0x0F); + } + while (($pixeldataoffset % 2) != 0) { + // Each run must be aligned on a word boundary. + $pixeldataoffset++; + } + + foreach ($paletteindexes as $paletteindex) { + $col = $pixelcounter % $thisfile_bmp_header_raw['width']; + $row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']); + $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex]; + $pixelcounter++; + } + break; + } + + } else { + + // encoded mode - the first byte of the pair contains the number of pixels to be + // drawn using the color indexes in the second byte. The second byte contains two + // color indexes, one in its high-order 4 bits and one in its low-order 4 bits. + // The first of the pixels is drawn using the color specified by the high-order + // 4 bits, the second is drawn using the color in the low-order 4 bits, the third + // is drawn using the color in the high-order 4 bits, and so on, until all the + // pixels specified by the first byte have been drawn. + $paletteindexes[0] = ($secondbyte & 0xF0) >> 4; + $paletteindexes[1] = ($secondbyte & 0x0F); + for ($i = 0; $i < $firstbyte; $i++) { + $col = $pixelcounter % $thisfile_bmp_header_raw['width']; + $row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']); + $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindexes[($i % 2)]]; + $pixelcounter++; + } + + } + } + break; + + default: + $ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data'; + break; + } + break; + + + case 3: // BI_BITFIELDS + switch ($thisfile_bmp_header_raw['bits_per_pixel']) { + case 16: + case 32: + $redshift = 0; + $greenshift = 0; + $blueshift = 0; + while ((($thisfile_bmp_header_raw['red_mask'] >> $redshift) & 0x01) == 0) { + $redshift++; + } + while ((($thisfile_bmp_header_raw['green_mask'] >> $greenshift) & 0x01) == 0) { + $greenshift++; + } + while ((($thisfile_bmp_header_raw['blue_mask'] >> $blueshift) & 0x01) == 0) { + $blueshift++; + } + for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) { + for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) { + $pixelvalue = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset, $thisfile_bmp_header_raw['bits_per_pixel'] / 8)); + $pixeldataoffset += $thisfile_bmp_header_raw['bits_per_pixel'] / 8; + + $red = intval(round(((($pixelvalue & $thisfile_bmp_header_raw['red_mask']) >> $redshift) / ($thisfile_bmp_header_raw['red_mask'] >> $redshift)) * 255)); + $green = intval(round(((($pixelvalue & $thisfile_bmp_header_raw['green_mask']) >> $greenshift) / ($thisfile_bmp_header_raw['green_mask'] >> $greenshift)) * 255)); + $blue = intval(round(((($pixelvalue & $thisfile_bmp_header_raw['blue_mask']) >> $blueshift) / ($thisfile_bmp_header_raw['blue_mask'] >> $blueshift)) * 255)); + $thisfile_bmp['data'][$row][$col] = (($red << 16) | ($green << 8) | ($blue)); + } + while (($pixeldataoffset % 4) != 0) { + // lines are padded to nearest DWORD + $pixeldataoffset++; + } + } + break; + + default: + $ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data'; + break; + } + break; + + + default: // unhandled compression type + $ThisFileInfo['error'][] = 'Unknown/unhandled compression type value ('.$thisfile_bmp_header_raw['compression'].') - cannot decompress pixel data'; + break; + } + } + + return true; + } + + + function PlotBMP(&$BMPinfo) { + $starttime = time(); + if (!isset($BMPinfo['bmp']['data']) || !is_array($BMPinfo['bmp']['data'])) { + echo 'ERROR: no pixel data
'; + return false; + } + set_time_limit(intval(round($BMPinfo['resolution_x'] * $BMPinfo['resolution_y'] / 10000))); + if ($im = ImageCreateTrueColor($BMPinfo['resolution_x'], $BMPinfo['resolution_y'])) { + for ($row = 0; $row < $BMPinfo['resolution_y']; $row++) { + for ($col = 0; $col < $BMPinfo['resolution_x']; $col++) { + if (isset($BMPinfo['bmp']['data'][$row][$col])) { + $red = ($BMPinfo['bmp']['data'][$row][$col] & 0x00FF0000) >> 16; + $green = ($BMPinfo['bmp']['data'][$row][$col] & 0x0000FF00) >> 8; + $blue = ($BMPinfo['bmp']['data'][$row][$col] & 0x000000FF); + $pixelcolor = ImageColorAllocate($im, $red, $green, $blue); + ImageSetPixel($im, $col, $row, $pixelcolor); + } else { + //echo 'ERROR: no data for pixel '.$row.' x '.$col.'
'; + //return false; + } + } + } + if (headers_sent()) { + echo 'plotted '.($BMPinfo['resolution_x'] * $BMPinfo['resolution_y']).' pixels in '.(time() - $starttime).' seconds
'; + ImageDestroy($im); + exit; + } else { + header('Content-type: image/png'); + ImagePNG($im); + ImageDestroy($im); + return true; + } + } + return false; + } + + function BMPcompressionWindowsLookup($compressionid) { + static $BMPcompressionWindowsLookup = array( + 0 => 'BI_RGB', + 1 => 'BI_RLE8', + 2 => 'BI_RLE4', + 3 => 'BI_BITFIELDS', + 4 => 'BI_JPEG', + 5 => 'BI_PNG' + ); + return (isset($BMPcompressionWindowsLookup[$compressionid]) ? $BMPcompressionWindowsLookup[$compressionid] : 'invalid'); + } + + function BMPcompressionOS2Lookup($compressionid) { + static $BMPcompressionOS2Lookup = array( + 0 => 'BI_RGB', + 1 => 'BI_RLE8', + 2 => 'BI_RLE4', + 3 => 'Huffman 1D', + 4 => 'BI_RLE24', + ); + return (isset($BMPcompressionOS2Lookup[$compressionid]) ? $BMPcompressionOS2Lookup[$compressionid] : 'invalid'); + } + +} + + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.graphic.gif.php b/includes/getid3/getid3/module.graphic.gif.php new file mode 100644 index 0000000..986ada3 --- /dev/null +++ b/includes/getid3/getid3/module.graphic.gif.php @@ -0,0 +1,183 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.graphic.gif.php // +// module for analyzing GIF Image files // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_gif +{ + + function getid3_gif(&$fd, &$ThisFileInfo) { + $ThisFileInfo['fileformat'] = 'gif'; + $ThisFileInfo['video']['dataformat'] = 'gif'; + $ThisFileInfo['video']['lossless'] = true; + $ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1; + + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + $GIFheader = fread($fd, 13); + $offset = 0; + + $ThisFileInfo['gif']['header']['raw']['identifier'] = substr($GIFheader, $offset, 3); + $offset += 3; + + if ($ThisFileInfo['gif']['header']['raw']['identifier'] != 'GIF') { + $ThisFileInfo['error'][] = 'Expecting "GIF" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$ThisFileInfo['gif']['header']['raw']['identifier'].'"'; + unset($ThisFileInfo['fileformat']); + unset($ThisFileInfo['gif']); + return false; + } + + $ThisFileInfo['gif']['header']['raw']['version'] = substr($GIFheader, $offset, 3); + $offset += 3; + $ThisFileInfo['gif']['header']['raw']['width'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 2)); + $offset += 2; + $ThisFileInfo['gif']['header']['raw']['height'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 2)); + $offset += 2; + $ThisFileInfo['gif']['header']['raw']['flags'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 1)); + $offset += 1; + $ThisFileInfo['gif']['header']['raw']['bg_color_index'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 1)); + $offset += 1; + $ThisFileInfo['gif']['header']['raw']['aspect_ratio'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 1)); + $offset += 1; + + $ThisFileInfo['video']['resolution_x'] = $ThisFileInfo['gif']['header']['raw']['width']; + $ThisFileInfo['video']['resolution_y'] = $ThisFileInfo['gif']['header']['raw']['height']; + $ThisFileInfo['gif']['version'] = $ThisFileInfo['gif']['header']['raw']['version']; + $ThisFileInfo['gif']['header']['flags']['global_color_table'] = (bool) ($ThisFileInfo['gif']['header']['raw']['flags'] & 0x80); + if ($ThisFileInfo['gif']['header']['raw']['flags'] & 0x80) { + // Number of bits per primary color available to the original image, minus 1 + $ThisFileInfo['gif']['header']['bits_per_pixel'] = 3 * ((($ThisFileInfo['gif']['header']['raw']['flags'] & 0x70) >> 4) + 1); + } else { + $ThisFileInfo['gif']['header']['bits_per_pixel'] = 0; + } + $ThisFileInfo['gif']['header']['flags']['global_color_sorted'] = (bool) ($ThisFileInfo['gif']['header']['raw']['flags'] & 0x40); + if ($ThisFileInfo['gif']['header']['flags']['global_color_table']) { + // the number of bytes contained in the Global Color Table. To determine that + // actual size of the color table, raise 2 to [the value of the field + 1] + $ThisFileInfo['gif']['header']['global_color_size'] = pow(2, ($ThisFileInfo['gif']['header']['raw']['flags'] & 0x07) + 1); + $ThisFileInfo['video']['bits_per_sample'] = ($ThisFileInfo['gif']['header']['raw']['flags'] & 0x07) + 1; + } else { + $ThisFileInfo['gif']['header']['global_color_size'] = 0; + } + if ($ThisFileInfo['gif']['header']['raw']['aspect_ratio'] != 0) { + // Aspect Ratio = (Pixel Aspect Ratio + 15) / 64 + $ThisFileInfo['gif']['header']['aspect_ratio'] = ($ThisFileInfo['gif']['header']['raw']['aspect_ratio'] + 15) / 64; + } + +// if ($ThisFileInfo['gif']['header']['flags']['global_color_table']) { +// $GIFcolorTable = fread($fd, 3 * $ThisFileInfo['gif']['header']['global_color_size']); +// $offset = 0; +// for ($i = 0; $i < $ThisFileInfo['gif']['header']['global_color_size']; $i++) { +// $red = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1)); +// $green = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1)); +// $blue = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1)); +// $ThisFileInfo['gif']['global_color_table'][$i] = (($red << 16) | ($green << 8) | ($blue)); +// } +// } +// +// // Image Descriptor +// while (!feof($fd)) { +// $NextBlockTest = fread($fd, 1); +// switch ($NextBlockTest) { +// +// case ',': // ',' - Image separator character +// +// $ImageDescriptorData = $NextBlockTest.fread($fd, 9); +// $ImageDescriptor = array(); +// $ImageDescriptor['image_left'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 1, 2)); +// $ImageDescriptor['image_top'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 3, 2)); +// $ImageDescriptor['image_width'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 5, 2)); +// $ImageDescriptor['image_height'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 7, 2)); +// $ImageDescriptor['flags_raw'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 9, 1)); +// $ImageDescriptor['flags']['use_local_color_map'] = (bool) ($ImageDescriptor['flags_raw'] & 0x80); +// $ImageDescriptor['flags']['image_interlaced'] = (bool) ($ImageDescriptor['flags_raw'] & 0x40); +// $ThisFileInfo['gif']['image_descriptor'][] = $ImageDescriptor; +// +// if ($ImageDescriptor['flags']['use_local_color_map']) { +// +// $ThisFileInfo['warning'][] = 'This version of getID3() cannot parse local color maps for GIFs'; +// return true; +// +// } +//echo 'Start of raster data: '.ftell($fd).'
'; +// $RasterData = array(); +// $RasterData['code_size'] = getid3_lib::LittleEndian2Int(fread($fd, 1)); +// $RasterData['block_byte_count'] = getid3_lib::LittleEndian2Int(fread($fd, 1)); +// $ThisFileInfo['gif']['raster_data'][count($ThisFileInfo['gif']['image_descriptor']) - 1] = $RasterData; +// +// $CurrentCodeSize = $RasterData['code_size'] + 1; +// for ($i = 0; $i < pow(2, $RasterData['code_size']); $i++) { +// $DefaultDataLookupTable[$i] = chr($i); +// } +// $DefaultDataLookupTable[pow(2, $RasterData['code_size']) + 0] = ''; // Clear Code +// $DefaultDataLookupTable[pow(2, $RasterData['code_size']) + 1] = ''; // End Of Image Code +// +// +// $NextValue = $this->GetLSBits($fd, $CurrentCodeSize); +// echo 'Clear Code: '.$NextValue.'
'; +// +// $NextValue = $this->GetLSBits($fd, $CurrentCodeSize); +// echo 'First Color: '.$NextValue.'
'; +// +// $Prefix = $NextValue; +//$i = 0; +// while ($i++ < 20) { +// $NextValue = $this->GetLSBits($fd, $CurrentCodeSize); +// echo $NextValue.'
'; +// } +//return true; +// break; +// +// case '!': +// // GIF Extension Block +// $ExtensionBlockData = $NextBlockTest.fread($fd, 2); +// $ExtensionBlock = array(); +// $ExtensionBlock['function_code'] = getid3_lib::LittleEndian2Int(substr($ExtensionBlockData, 1, 1)); +// $ExtensionBlock['byte_length'] = getid3_lib::LittleEndian2Int(substr($ExtensionBlockData, 2, 1)); +// $ExtensionBlock['data'] = fread($fd, $ExtensionBlock['byte_length']); +// $ThisFileInfo['gif']['extension_blocks'][] = $ExtensionBlock; +// break; +// +// case ';': +// $ThisFileInfo['gif']['terminator_offset'] = ftell($fd) - 1; +// // GIF Terminator +// break; +// +// default: +// break; +// +// +// } +// } + + return true; + } + + + function GetLSBits($fd, $bits) { + static $bitbuffer = ''; + while (strlen($bitbuffer) < $bits) { +//echo 'Read another byte: '.ftell($fd).'
'; + $bitbuffer = str_pad(decbin(ord(fread($fd, 1))), 8, '0', STR_PAD_LEFT).$bitbuffer; + } + + $value = bindec(substr($bitbuffer, 0 - $bits)); + $bitbuffer = substr($bitbuffer, 0, 0 - $bits); + + return $value; + } + +} + + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.graphic.jpg.php b/includes/getid3/getid3/module.graphic.jpg.php new file mode 100644 index 0000000..0cd305c --- /dev/null +++ b/includes/getid3/getid3/module.graphic.jpg.php @@ -0,0 +1,72 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.graphic.jpg.php // +// module for analyzing JPEG Image files // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_jpg +{ + + + function getid3_jpg(&$fd, &$ThisFileInfo) { + $ThisFileInfo['fileformat'] = 'jpg'; + $ThisFileInfo['video']['dataformat'] = 'jpg'; + $ThisFileInfo['video']['lossless'] = false; + $ThisFileInfo['video']['bits_per_sample'] = 24; + $ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1; + + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + + list($width, $height, $type) = getid3_lib::GetDataImageSize(fread($fd, $ThisFileInfo['filesize'])); + if ($type == 2) { + + $ThisFileInfo['video']['resolution_x'] = $width; + $ThisFileInfo['video']['resolution_y'] = $height; + + if (version_compare(phpversion(), '4.2.0', '>=')) { + + if (function_exists('exif_read_data')) { + + ob_start(); + $ThisFileInfo['jpg']['exif'] = exif_read_data($ThisFileInfo['filenamepath'], '', true, false); + $errors = ob_get_contents(); + if ($errors) { + $ThisFileInfo['error'][] = strip_tags($errors); + unset($ThisFileInfo['jpg']['exif']); + } + ob_end_clean(); + + } else { + + $ThisFileInfo['warning'][] = 'EXIF parsing only available when '.(GETID3_OS_ISWINDOWS ? 'php_exif.dll enabled' : 'compiled with --enable-exif'); + + } + + } else { + + $ThisFileInfo['warning'][] = 'EXIF parsing only available in PHP v4.2.0 and higher compiled with --enable-exif (or php_exif.dll enabled for Windows). You are using PHP v'.phpversion(); + + } + + return true; + + } + + unset($ThisFileInfo['fileformat']); + return false; + } + +} + + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.graphic.pcd.php b/includes/getid3/getid3/module.graphic.pcd.php new file mode 100644 index 0000000..60efabd --- /dev/null +++ b/includes/getid3/getid3/module.graphic.pcd.php @@ -0,0 +1,130 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.graphic.pcd.php // +// module for analyzing PhotoCD (PCD) Image files // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_pcd +{ + function getid3_pcd(&$fd, &$ThisFileInfo, $ExtractData=0) { + $ThisFileInfo['fileformat'] = 'pcd'; + $ThisFileInfo['video']['dataformat'] = 'pcd'; + $ThisFileInfo['video']['lossless'] = false; + + + fseek($fd, $ThisFileInfo['avdataoffset'] + 72, SEEK_SET); + + $PCDflags = fread($fd, 1); + $PCDisVertical = ((ord($PCDflags) & 0x01) ? true : false); + + + if ($PCDisVertical) { + $ThisFileInfo['video']['resolution_x'] = 3072; + $ThisFileInfo['video']['resolution_y'] = 2048; + } else { + $ThisFileInfo['video']['resolution_x'] = 2048; + $ThisFileInfo['video']['resolution_y'] = 3072; + } + + + if ($ExtractData > 3) { + + $ThisFileInfo['error'][] = 'Cannot extract PSD image data for detail levels above BASE (3)'; + + } elseif ($ExtractData > 0) { + + $PCD_levels[1] = array( 192, 128, 0x02000); // BASE/16 + $PCD_levels[2] = array( 384, 256, 0x0B800); // BASE/4 + $PCD_levels[3] = array( 768, 512, 0x30000); // BASE + //$PCD_levels[4] = array(1536, 1024, ??); // BASE*4 - encrypted with Kodak-proprietary compression/encryption + //$PCD_levels[5] = array(3072, 2048, ??); // BASE*16 - encrypted with Kodak-proprietary compression/encryption + //$PCD_levels[6] = array(6144, 4096, ??); // BASE*64 - encrypted with Kodak-proprietary compression/encryption; PhotoCD-Pro only + + list($PCD_width, $PCD_height, $PCD_dataOffset) = $PCD_levels[3]; + + fseek($fd, $ThisFileInfo['avdataoffset'] + $PCD_dataOffset, SEEK_SET); + + for ($y = 0; $y < $PCD_height; $y += 2) { + // The image-data of these subtypes start at the respective offsets of 02000h, 0b800h and 30000h. + // To decode the YcbYr to the more usual RGB-code, three lines of data have to be read, each + // consisting of ‘w’ bytes, where ‘w’ is the width of the image-subtype. The first ‘w’ bytes and + // the first half of the third ‘w’ bytes contain data for the first RGB-line, the second ‘w’ bytes + // and the second half of the third ‘w’ bytes contain data for a second RGB-line. + + $PCD_data_Y1 = fread($fd, $PCD_width); + $PCD_data_Y2 = fread($fd, $PCD_width); + $PCD_data_Cb = fread($fd, intval(round($PCD_width / 2))); + $PCD_data_Cr = fread($fd, intval(round($PCD_width / 2))); + + for ($x = 0; $x < $PCD_width; $x++) { + if ($PCDisVertical) { + $ThisFileInfo['pcd']['data'][$PCD_width - $x][$y] = $this->YCbCr2RGB(ord($PCD_data_Y1{$x}), ord($PCD_data_Cb{floor($x / 2)}), ord($PCD_data_Cr{floor($x / 2)})); + $ThisFileInfo['pcd']['data'][$PCD_width - $x][$y + 1] = $this->YCbCr2RGB(ord($PCD_data_Y2{$x}), ord($PCD_data_Cb{floor($x / 2)}), ord($PCD_data_Cr{floor($x / 2)})); + } else { + $ThisFileInfo['pcd']['data'][$y][$x] = $this->YCbCr2RGB(ord($PCD_data_Y1{$x}), ord($PCD_data_Cb{floor($x / 2)}), ord($PCD_data_Cr{floor($x / 2)})); + $ThisFileInfo['pcd']['data'][$y + 1][$x] = $this->YCbCr2RGB(ord($PCD_data_Y2{$x}), ord($PCD_data_Cb{floor($x / 2)}), ord($PCD_data_Cr{floor($x / 2)})); + } + } + } + + // Example for plotting extracted data + //getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, true); + //if ($PCDisVertical) { + // $BMPinfo['resolution_x'] = $PCD_height; + // $BMPinfo['resolution_y'] = $PCD_width; + //} else { + // $BMPinfo['resolution_x'] = $PCD_width; + // $BMPinfo['resolution_y'] = $PCD_height; + //} + //$BMPinfo['bmp']['data'] = $ThisFileInfo['pcd']['data']; + //getid3_bmp::PlotBMP($BMPinfo); + //exit; + + } + + } + + function YCbCr2RGB($Y, $Cb, $Cr) { + static $YCbCr_constants = array(); + if (empty($YCbCr_constants)) { + $YCbCr_constants['red']['Y'] = 0.0054980 * 256; + $YCbCr_constants['red']['Cb'] = 0.0000000 * 256; + $YCbCr_constants['red']['Cr'] = 0.0051681 * 256; + $YCbCr_constants['green']['Y'] = 0.0054980 * 256; + $YCbCr_constants['green']['Cb'] = -0.0015446 * 256; + $YCbCr_constants['green']['Cr'] = -0.0026325 * 256; + $YCbCr_constants['blue']['Y'] = 0.0054980 * 256; + $YCbCr_constants['blue']['Cb'] = 0.0079533 * 256; + $YCbCr_constants['blue']['Cr'] = 0.0000000 * 256; + } + + $RGBcolor = array('red'=>0, 'green'=>0, 'blue'=>0); + foreach ($RGBcolor as $rgbname => $dummy) { + $RGBcolor[$rgbname] = max(0, + min(255, + intval( + round( + ($YCbCr_constants[$rgbname]['Y'] * $Y) + + ($YCbCr_constants[$rgbname]['Cb'] * ($Cb - 156)) + + ($YCbCr_constants[$rgbname]['Cr'] * ($Cr - 137)) + ) + ) + ) + ); + } + return (($RGBcolor['red'] * 65536) + ($RGBcolor['green'] * 256) + $RGBcolor['blue']); + } + +} + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.graphic.png.php b/includes/getid3/getid3/module.graphic.png.php new file mode 100644 index 0000000..7d82b84 --- /dev/null +++ b/includes/getid3/getid3/module.graphic.png.php @@ -0,0 +1,519 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.graphic.png.php // +// module for analyzing PNG Image files // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_png +{ + + function getid3_png(&$fd, &$ThisFileInfo) { + + // shortcut + $ThisFileInfo['png'] = array(); + $thisfile_png = &$ThisFileInfo['png']; + + $ThisFileInfo['fileformat'] = 'png'; + $ThisFileInfo['video']['dataformat'] = 'png'; + $ThisFileInfo['video']['lossless'] = false; + + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + $PNGfiledata = fread($fd, GETID3_FREAD_BUFFER_SIZE); + $offset = 0; + + $PNGidentifier = substr($PNGfiledata, $offset, 8); // $89 $50 $4E $47 $0D $0A $1A $0A + $offset += 8; + + if ($PNGidentifier != "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A") { + $ThisFileInfo['error'][] = 'First 8 bytes of file ('.getid3_lib::PrintHexBytes($PNGidentifier).') did not match expected PNG identifier'; + unset($ThisFileInfo['fileformat']); + return false; + } + + while (((ftell($fd) - (strlen($PNGfiledata) - $offset)) < $ThisFileInfo['filesize'])) { + $chunk['data_length'] = getid3_lib::BigEndian2Int(substr($PNGfiledata, $offset, 4)); + $offset += 4; + while (((strlen($PNGfiledata) - $offset) < ($chunk['data_length'] + 4)) && (ftell($fd) < $ThisFileInfo['filesize'])) { + $PNGfiledata .= fread($fd, GETID3_FREAD_BUFFER_SIZE); + } + $chunk['type_text'] = substr($PNGfiledata, $offset, 4); + $offset += 4; + $chunk['type_raw'] = getid3_lib::BigEndian2Int($chunk['type_text']); + $chunk['data'] = substr($PNGfiledata, $offset, $chunk['data_length']); + $offset += $chunk['data_length']; + $chunk['crc'] = getid3_lib::BigEndian2Int(substr($PNGfiledata, $offset, 4)); + $offset += 4; + + $chunk['flags']['ancilliary'] = (bool) ($chunk['type_raw'] & 0x20000000); + $chunk['flags']['private'] = (bool) ($chunk['type_raw'] & 0x00200000); + $chunk['flags']['reserved'] = (bool) ($chunk['type_raw'] & 0x00002000); + $chunk['flags']['safe_to_copy'] = (bool) ($chunk['type_raw'] & 0x00000020); + + // shortcut + $thisfile_png[$chunk['type_text']] = array(); + $thisfile_png_chunk_type_text = &$thisfile_png[$chunk['type_text']]; + + switch ($chunk['type_text']) { + + case 'IHDR': // Image Header + $thisfile_png_chunk_type_text['header'] = $chunk; + $thisfile_png_chunk_type_text['width'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 0, 4)); + $thisfile_png_chunk_type_text['height'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 4, 4)); + $thisfile_png_chunk_type_text['raw']['bit_depth'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 8, 1)); + $thisfile_png_chunk_type_text['raw']['color_type'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 9, 1)); + $thisfile_png_chunk_type_text['raw']['compression_method'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 10, 1)); + $thisfile_png_chunk_type_text['raw']['filter_method'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 11, 1)); + $thisfile_png_chunk_type_text['raw']['interlace_method'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 12, 1)); + + $thisfile_png_chunk_type_text['compression_method_text'] = $this->PNGcompressionMethodLookup($thisfile_png_chunk_type_text['raw']['compression_method']); + $thisfile_png_chunk_type_text['color_type']['palette'] = (bool) ($thisfile_png_chunk_type_text['raw']['color_type'] & 0x01); + $thisfile_png_chunk_type_text['color_type']['true_color'] = (bool) ($thisfile_png_chunk_type_text['raw']['color_type'] & 0x02); + $thisfile_png_chunk_type_text['color_type']['alpha'] = (bool) ($thisfile_png_chunk_type_text['raw']['color_type'] & 0x04); + + $ThisFileInfo['video']['resolution_x'] = $thisfile_png_chunk_type_text['width']; + $ThisFileInfo['video']['resolution_y'] = $thisfile_png_chunk_type_text['height']; + + $ThisFileInfo['video']['bits_per_sample'] = $this->IHDRcalculateBitsPerSample($thisfile_png_chunk_type_text['raw']['color_type'], $thisfile_png_chunk_type_text['raw']['bit_depth']); + break; + + + case 'PLTE': // Palette + $thisfile_png_chunk_type_text['header'] = $chunk; + $paletteoffset = 0; + for ($i = 0; $i <= 255; $i++) { + //$thisfile_png_chunk_type_text['red'][$i] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], $paletteoffset++, 1)); + //$thisfile_png_chunk_type_text['green'][$i] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], $paletteoffset++, 1)); + //$thisfile_png_chunk_type_text['blue'][$i] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], $paletteoffset++, 1)); + $red = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], $paletteoffset++, 1)); + $green = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], $paletteoffset++, 1)); + $blue = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], $paletteoffset++, 1)); + $thisfile_png_chunk_type_text[$i] = (($red << 16) | ($green << 8) | ($blue)); + } + break; + + + case 'tRNS': // Transparency + $thisfile_png_chunk_type_text['header'] = $chunk; + switch ($thisfile_png['IHDR']['raw']['color_type']) { + case 0: + $thisfile_png_chunk_type_text['transparent_color_gray'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 0, 2)); + break; + + case 2: + $thisfile_png_chunk_type_text['transparent_color_red'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 0, 2)); + $thisfile_png_chunk_type_text['transparent_color_green'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 2, 2)); + $thisfile_png_chunk_type_text['transparent_color_blue'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 4, 2)); + break; + + case 3: + for ($i = 0; $i < strlen($thisfile_png_chunk_type_text['header']['data']); $i++) { + $thisfile_png_chunk_type_text['palette_opacity'][$i] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], $i, 1)); + } + break; + + case 4: + case 6: + $ThisFileInfo['error'][] = 'Invalid color_type in tRNS chunk: '.$thisfile_png['IHDR']['raw']['color_type']; + + default: + $ThisFileInfo['warning'][] = 'Unhandled color_type in tRNS chunk: '.$thisfile_png['IHDR']['raw']['color_type']; + break; + } + break; + + + case 'gAMA': // Image Gamma + $thisfile_png_chunk_type_text['header'] = $chunk; + $thisfile_png_chunk_type_text['gamma'] = getid3_lib::BigEndian2Int($thisfile_png_chunk_type_text['header']['data']) / 100000; + break; + + + case 'cHRM': // Primary Chromaticities + $thisfile_png_chunk_type_text['header'] = $chunk; + $thisfile_png_chunk_type_text['white_x'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 0, 4)) / 100000; + $thisfile_png_chunk_type_text['white_y'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 4, 4)) / 100000; + $thisfile_png_chunk_type_text['red_y'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 8, 4)) / 100000; + $thisfile_png_chunk_type_text['red_y'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 12, 4)) / 100000; + $thisfile_png_chunk_type_text['green_y'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 16, 4)) / 100000; + $thisfile_png_chunk_type_text['green_y'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 20, 4)) / 100000; + $thisfile_png_chunk_type_text['blue_y'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 24, 4)) / 100000; + $thisfile_png_chunk_type_text['blue_y'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 28, 4)) / 100000; + break; + + + case 'sRGB': // Standard RGB Color Space + $thisfile_png_chunk_type_text['header'] = $chunk; + $thisfile_png_chunk_type_text['reindering_intent'] = getid3_lib::BigEndian2Int($thisfile_png_chunk_type_text['header']['data']); + $thisfile_png_chunk_type_text['reindering_intent_text'] = $this->PNGsRGBintentLookup($thisfile_png_chunk_type_text['reindering_intent']); + break; + + + case 'iCCP': // Embedded ICC Profile + $thisfile_png_chunk_type_text['header'] = $chunk; + list($profilename, $compressiondata) = explode("\x00", $thisfile_png_chunk_type_text['header']['data'], 2); + $thisfile_png_chunk_type_text['profile_name'] = $profilename; + $thisfile_png_chunk_type_text['compression_method'] = getid3_lib::BigEndian2Int(substr($compressiondata, 0, 1)); + $thisfile_png_chunk_type_text['compression_profile'] = substr($compressiondata, 1); + + $thisfile_png_chunk_type_text['compression_method_text'] = $this->PNGcompressionMethodLookup($thisfile_png_chunk_type_text['compression_method']); + break; + + + case 'tEXt': // Textual Data + $thisfile_png_chunk_type_text['header'] = $chunk; + list($keyword, $text) = explode("\x00", $thisfile_png_chunk_type_text['header']['data'], 2); + $thisfile_png_chunk_type_text['keyword'] = $keyword; + $thisfile_png_chunk_type_text['text'] = $text; + + $thisfile_png['comments'][$thisfile_png_chunk_type_text['keyword']][] = $thisfile_png_chunk_type_text['text']; + break; + + + case 'zTXt': // Compressed Textual Data + $thisfile_png_chunk_type_text['header'] = $chunk; + list($keyword, $otherdata) = explode("\x00", $thisfile_png_chunk_type_text['header']['data'], 2); + $thisfile_png_chunk_type_text['keyword'] = $keyword; + $thisfile_png_chunk_type_text['compression_method'] = getid3_lib::BigEndian2Int(substr($otherdata, 0, 1)); + $thisfile_png_chunk_type_text['compressed_text'] = substr($otherdata, 1); + $thisfile_png_chunk_type_text['compression_method_text'] = $this->PNGcompressionMethodLookup($thisfile_png_chunk_type_text['compression_method']); + switch ($thisfile_png_chunk_type_text['compression_method']) { + case 0: + $thisfile_png_chunk_type_text['text'] = gzuncompress($thisfile_png_chunk_type_text['compressed_text']); + break; + + default: + // unknown compression method + break; + } + + if (isset($thisfile_png_chunk_type_text['text'])) { + $thisfile_png['comments'][$thisfile_png_chunk_type_text['keyword']][] = $thisfile_png_chunk_type_text['text']; + } + break; + + + case 'iTXt': // International Textual Data + $thisfile_png_chunk_type_text['header'] = $chunk; + list($keyword, $otherdata) = explode("\x00", $thisfile_png_chunk_type_text['header']['data'], 2); + $thisfile_png_chunk_type_text['keyword'] = $keyword; + $thisfile_png_chunk_type_text['compression'] = (bool) getid3_lib::BigEndian2Int(substr($otherdata, 0, 1)); + $thisfile_png_chunk_type_text['compression_method'] = getid3_lib::BigEndian2Int(substr($otherdata, 1, 1)); + $thisfile_png_chunk_type_text['compression_method_text'] = $this->PNGcompressionMethodLookup($thisfile_png_chunk_type_text['compression_method']); + list($languagetag, $translatedkeyword, $text) = explode("\x00", substr($otherdata, 2), 3); + $thisfile_png_chunk_type_text['language_tag'] = $languagetag; + $thisfile_png_chunk_type_text['translated_keyword'] = $translatedkeyword; + + if ($thisfile_png_chunk_type_text['compression']) { + + switch ($thisfile_png_chunk_type_text['compression_method']) { + case 0: + $thisfile_png_chunk_type_text['text'] = gzuncompress($text); + break; + + default: + // unknown compression method + break; + } + + } else { + + $thisfile_png_chunk_type_text['text'] = $text; + + } + + if (isset($thisfile_png_chunk_type_text['text'])) { + $thisfile_png['comments'][$thisfile_png_chunk_type_text['keyword']][] = $thisfile_png_chunk_type_text['text']; + } + break; + + + case 'bKGD': // Background Color + $thisfile_png_chunk_type_text['header'] = $chunk; + switch ($thisfile_png['IHDR']['raw']['color_type']) { + case 0: + case 4: + $thisfile_png_chunk_type_text['background_gray'] = getid3_lib::BigEndian2Int($thisfile_png_chunk_type_text['header']['data']); + break; + + case 2: + case 6: + $thisfile_png_chunk_type_text['background_red'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 0 * $thisfile_png['IHDR']['raw']['bit_depth'], $thisfile_png['IHDR']['raw']['bit_depth'])); + $thisfile_png_chunk_type_text['background_green'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 1 * $thisfile_png['IHDR']['raw']['bit_depth'], $thisfile_png['IHDR']['raw']['bit_depth'])); + $thisfile_png_chunk_type_text['background_blue'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 2 * $thisfile_png['IHDR']['raw']['bit_depth'], $thisfile_png['IHDR']['raw']['bit_depth'])); + break; + + case 3: + $thisfile_png_chunk_type_text['background_index'] = getid3_lib::BigEndian2Int($thisfile_png_chunk_type_text['header']['data']); + break; + + default: + break; + } + break; + + + case 'pHYs': // Physical Pixel Dimensions + $thisfile_png_chunk_type_text['header'] = $chunk; + $thisfile_png_chunk_type_text['pixels_per_unit_x'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 0, 4)); + $thisfile_png_chunk_type_text['pixels_per_unit_y'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 4, 4)); + $thisfile_png_chunk_type_text['unit_specifier'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 8, 1)); + $thisfile_png_chunk_type_text['unit'] = $this->PNGpHYsUnitLookup($thisfile_png_chunk_type_text['unit_specifier']); + break; + + + case 'sBIT': // Significant Bits + $thisfile_png_chunk_type_text['header'] = $chunk; + switch ($thisfile_png['IHDR']['raw']['color_type']) { + case 0: + $thisfile_png_chunk_type_text['significant_bits_gray'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 0, 1)); + break; + + case 2: + case 3: + $thisfile_png_chunk_type_text['significant_bits_red'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 0, 1)); + $thisfile_png_chunk_type_text['significant_bits_green'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 1, 1)); + $thisfile_png_chunk_type_text['significant_bits_blue'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 2, 1)); + break; + + case 4: + $thisfile_png_chunk_type_text['significant_bits_gray'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 0, 1)); + $thisfile_png_chunk_type_text['significant_bits_alpha'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 1, 1)); + break; + + case 6: + $thisfile_png_chunk_type_text['significant_bits_red'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 0, 1)); + $thisfile_png_chunk_type_text['significant_bits_green'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 1, 1)); + $thisfile_png_chunk_type_text['significant_bits_blue'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 2, 1)); + $thisfile_png_chunk_type_text['significant_bits_alpha'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 3, 1)); + break; + + default: + break; + } + break; + + + case 'sPLT': // Suggested Palette + $thisfile_png_chunk_type_text['header'] = $chunk; + list($palettename, $otherdata) = explode("\x00", $thisfile_png_chunk_type_text['header']['data'], 2); + $thisfile_png_chunk_type_text['palette_name'] = $palettename; + $sPLToffset = 0; + $thisfile_png_chunk_type_text['sample_depth_bits'] = getid3_lib::BigEndian2Int(substr($otherdata, $sPLToffset, 1)); + $sPLToffset += 1; + $thisfile_png_chunk_type_text['sample_depth_bytes'] = $thisfile_png_chunk_type_text['sample_depth_bits'] / 8; + $paletteCounter = 0; + while ($sPLToffset < strlen($otherdata)) { + $thisfile_png_chunk_type_text['red'][$paletteCounter] = getid3_lib::BigEndian2Int(substr($otherdata, $sPLToffset, $thisfile_png_chunk_type_text['sample_depth_bytes'])); + $sPLToffset += $thisfile_png_chunk_type_text['sample_depth_bytes']; + $thisfile_png_chunk_type_text['green'][$paletteCounter] = getid3_lib::BigEndian2Int(substr($otherdata, $sPLToffset, $thisfile_png_chunk_type_text['sample_depth_bytes'])); + $sPLToffset += $thisfile_png_chunk_type_text['sample_depth_bytes']; + $thisfile_png_chunk_type_text['blue'][$paletteCounter] = getid3_lib::BigEndian2Int(substr($otherdata, $sPLToffset, $thisfile_png_chunk_type_text['sample_depth_bytes'])); + $sPLToffset += $thisfile_png_chunk_type_text['sample_depth_bytes']; + $thisfile_png_chunk_type_text['alpha'][$paletteCounter] = getid3_lib::BigEndian2Int(substr($otherdata, $sPLToffset, $thisfile_png_chunk_type_text['sample_depth_bytes'])); + $sPLToffset += $thisfile_png_chunk_type_text['sample_depth_bytes']; + $thisfile_png_chunk_type_text['frequency'][$paletteCounter] = getid3_lib::BigEndian2Int(substr($otherdata, $sPLToffset, 2)); + $sPLToffset += 2; + $paletteCounter++; + } + break; + + + case 'hIST': // Palette Histogram + $thisfile_png_chunk_type_text['header'] = $chunk; + $hISTcounter = 0; + while ($hISTcounter < strlen($thisfile_png_chunk_type_text['header']['data'])) { + $thisfile_png_chunk_type_text[$hISTcounter] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], $hISTcounter / 2, 2)); + $hISTcounter += 2; + } + break; + + + case 'tIME': // Image Last-Modification Time + $thisfile_png_chunk_type_text['header'] = $chunk; + $thisfile_png_chunk_type_text['year'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 0, 2)); + $thisfile_png_chunk_type_text['month'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 2, 1)); + $thisfile_png_chunk_type_text['day'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 3, 1)); + $thisfile_png_chunk_type_text['hour'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 4, 1)); + $thisfile_png_chunk_type_text['minute'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 5, 1)); + $thisfile_png_chunk_type_text['second'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 6, 1)); + $thisfile_png_chunk_type_text['unix'] = gmmktime($thisfile_png_chunk_type_text['hour'], $thisfile_png_chunk_type_text['minute'], $thisfile_png_chunk_type_text['second'], $thisfile_png_chunk_type_text['month'], $thisfile_png_chunk_type_text['day'], $thisfile_png_chunk_type_text['year']); + break; + + + case 'oFFs': // Image Offset + $thisfile_png_chunk_type_text['header'] = $chunk; + $thisfile_png_chunk_type_text['position_x'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 0, 4), false, true); + $thisfile_png_chunk_type_text['position_y'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 4, 4), false, true); + $thisfile_png_chunk_type_text['unit_specifier'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 8, 1)); + $thisfile_png_chunk_type_text['unit'] = $this->PNGoFFsUnitLookup($thisfile_png_chunk_type_text['unit_specifier']); + break; + + + case 'pCAL': // Calibration Of Pixel Values + $thisfile_png_chunk_type_text['header'] = $chunk; + list($calibrationname, $otherdata) = explode("\x00", $thisfile_png_chunk_type_text['header']['data'], 2); + $thisfile_png_chunk_type_text['calibration_name'] = $calibrationname; + $pCALoffset = 0; + $thisfile_png_chunk_type_text['original_zero'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], $pCALoffset, 4), false, true); + $pCALoffset += 4; + $thisfile_png_chunk_type_text['original_max'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], $pCALoffset, 4), false, true); + $pCALoffset += 4; + $thisfile_png_chunk_type_text['equation_type'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], $pCALoffset, 1)); + $pCALoffset += 1; + $thisfile_png_chunk_type_text['equation_type_text'] = $this->PNGpCALequationTypeLookup($thisfile_png_chunk_type_text['equation_type']); + $thisfile_png_chunk_type_text['parameter_count'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], $pCALoffset, 1)); + $pCALoffset += 1; + $thisfile_png_chunk_type_text['parameters'] = explode("\x00", substr($thisfile_png_chunk_type_text['header']['data'], $pCALoffset)); + break; + + + case 'sCAL': // Physical Scale Of Image Subject + $thisfile_png_chunk_type_text['header'] = $chunk; + $thisfile_png_chunk_type_text['unit_specifier'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 0, 1)); + $thisfile_png_chunk_type_text['unit'] = $this->PNGsCALUnitLookup($thisfile_png_chunk_type_text['unit_specifier']); + list($pixelwidth, $pixelheight) = explode("\x00", substr($thisfile_png_chunk_type_text['header']['data'], 1)); + $thisfile_png_chunk_type_text['pixel_width'] = $pixelwidth; + $thisfile_png_chunk_type_text['pixel_height'] = $pixelheight; + break; + + + case 'gIFg': // GIF Graphic Control Extension + $gIFgCounter = 0; + if (isset($thisfile_png_chunk_type_text) && is_array($thisfile_png_chunk_type_text)) { + $gIFgCounter = count($thisfile_png_chunk_type_text); + } + $thisfile_png_chunk_type_text[$gIFgCounter]['header'] = $chunk; + $thisfile_png_chunk_type_text[$gIFgCounter]['disposal_method'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 0, 1)); + $thisfile_png_chunk_type_text[$gIFgCounter]['user_input_flag'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 1, 1)); + $thisfile_png_chunk_type_text[$gIFgCounter]['delay_time'] = getid3_lib::BigEndian2Int(substr($thisfile_png_chunk_type_text['header']['data'], 2, 2)); + break; + + + case 'gIFx': // GIF Application Extension + $gIFxCounter = 0; + if (isset($thisfile_png_chunk_type_text) && is_array($thisfile_png_chunk_type_text)) { + $gIFxCounter = count($thisfile_png_chunk_type_text); + } + $thisfile_png_chunk_type_text[$gIFxCounter]['header'] = $chunk; + $thisfile_png_chunk_type_text[$gIFxCounter]['application_identifier'] = substr($thisfile_png_chunk_type_text['header']['data'], 0, 8); + $thisfile_png_chunk_type_text[$gIFxCounter]['authentication_code'] = substr($thisfile_png_chunk_type_text['header']['data'], 8, 3); + $thisfile_png_chunk_type_text[$gIFxCounter]['application_data'] = substr($thisfile_png_chunk_type_text['header']['data'], 11); + break; + + + case 'IDAT': // Image Data + $idatinformationfieldindex = 0; + if (isset($thisfile_png['IDAT']) && is_array($thisfile_png['IDAT'])) { + $idatinformationfieldindex = count($thisfile_png['IDAT']); + } + unset($chunk['data']); + $thisfile_png_chunk_type_text[$idatinformationfieldindex]['header'] = $chunk; + break; + + + case 'IEND': // Image Trailer + $thisfile_png_chunk_type_text['header'] = $chunk; + break; + + + default: + //unset($chunk['data']); + $thisfile_png_chunk_type_text['header'] = $chunk; + $ThisFileInfo['warning'][] = 'Unhandled chunk type: '.$chunk['type_text']; + break; + } + } + + return true; + } + + function PNGsRGBintentLookup($sRGB) { + static $PNGsRGBintentLookup = array( + 0 => 'Perceptual', + 1 => 'Relative colorimetric', + 2 => 'Saturation', + 3 => 'Absolute colorimetric' + ); + return (isset($PNGsRGBintentLookup[$sRGB]) ? $PNGsRGBintentLookup[$sRGB] : 'invalid'); + } + + function PNGcompressionMethodLookup($compressionmethod) { + static $PNGcompressionMethodLookup = array( + 0 => 'deflate/inflate' + ); + return (isset($PNGcompressionMethodLookup[$compressionmethod]) ? $PNGcompressionMethodLookup[$compressionmethod] : 'invalid'); + } + + function PNGpHYsUnitLookup($unitid) { + static $PNGpHYsUnitLookup = array( + 0 => 'unknown', + 1 => 'meter' + ); + return (isset($PNGpHYsUnitLookup[$unitid]) ? $PNGpHYsUnitLookup[$unitid] : 'invalid'); + } + + function PNGoFFsUnitLookup($unitid) { + static $PNGoFFsUnitLookup = array( + 0 => 'pixel', + 1 => 'micrometer' + ); + return (isset($PNGoFFsUnitLookup[$unitid]) ? $PNGoFFsUnitLookup[$unitid] : 'invalid'); + } + + function PNGpCALequationTypeLookup($equationtype) { + static $PNGpCALequationTypeLookup = array( + 0 => 'Linear mapping', + 1 => 'Base-e exponential mapping', + 2 => 'Arbitrary-base exponential mapping', + 3 => 'Hyperbolic mapping' + ); + return (isset($PNGpCALequationTypeLookup[$equationtype]) ? $PNGpCALequationTypeLookup[$equationtype] : 'invalid'); + } + + function PNGsCALUnitLookup($unitid) { + static $PNGsCALUnitLookup = array( + 0 => 'meter', + 1 => 'radian' + ); + return (isset($PNGsCALUnitLookup[$unitid]) ? $PNGsCALUnitLookup[$unitid] : 'invalid'); + } + + function IHDRcalculateBitsPerSample($color_type, $bit_depth) { + switch ($color_type) { + case 0: // Each pixel is a grayscale sample. + return $bit_depth; + break; + + case 2: // Each pixel is an R,G,B triple + return 3 * $bit_depth; + break; + + case 3: // Each pixel is a palette index; a PLTE chunk must appear. + return $bit_depth; + break; + + case 4: // Each pixel is a grayscale sample, followed by an alpha sample. + return 2 * $bit_depth; + break; + + case 6: // Each pixel is an R,G,B triple, followed by an alpha sample. + return 4 * $bit_depth; + break; + } + return false; + } + +} + + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.graphic.svg.php b/includes/getid3/getid3/module.graphic.svg.php new file mode 100644 index 0000000..e447145 --- /dev/null +++ b/includes/getid3/getid3/module.graphic.svg.php @@ -0,0 +1,52 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.graphic.svg.php // +// module for analyzing SVG Image files // +// dependencies: NONE // +// author: Bryce Harrington // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_svg +{ + + + function getid3_svg(&$fd, &$ThisFileInfo) { + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + + // I'm making this up, please modify as appropriate + $SVGheader = fread($fd, 32); + $ThisFileInfo['svg']['magic'] = substr($SVGheader, 0, 4); + if ($ThisFileInfo['svg']['magic'] == 'aBcD') { + + $ThisFileInfo['fileformat'] = 'svg'; + $ThisFileInfo['video']['dataformat'] = 'svg'; + $ThisFileInfo['video']['lossless'] = true; + $ThisFileInfo['video']['bits_per_sample'] = 24; + $ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1; + + $ThisFileInfo['svg']['width'] = getid3_lib::LittleEndian2Int(substr($fileData, 4, 4)); + $ThisFileInfo['svg']['height'] = getid3_lib::LittleEndian2Int(substr($fileData, 8, 4)); + + $ThisFileInfo['video']['resolution_x'] = $ThisFileInfo['svg']['width']; + $ThisFileInfo['video']['resolution_y'] = $ThisFileInfo['svg']['height']; + + return true; + } + $ThisFileInfo['error'][] = 'Did not find SVG magic bytes "aBcD" at '.$ThisFileInfo['avdataoffset']; + unset($ThisFileInfo['fileformat']); + return false; + } + +} + + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.graphic.tiff.php b/includes/getid3/getid3/module.graphic.tiff.php new file mode 100644 index 0000000..ae57cd6 --- /dev/null +++ b/includes/getid3/getid3/module.graphic.tiff.php @@ -0,0 +1,221 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.archive.tiff.php // +// module for analyzing TIFF files // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_tiff +{ + + function getid3_tiff(&$fd, &$ThisFileInfo) { + + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + $TIFFheader = fread($fd, 4); + + switch (substr($TIFFheader, 0, 2)) { + case 'II': + $ThisFileInfo['tiff']['byte_order'] = 'Intel'; + break; + case 'MM': + $ThisFileInfo['tiff']['byte_order'] = 'Motorola'; + break; + default: + $ThisFileInfo['error'][] = 'Invalid TIFF byte order identifier ('.substr($TIFFheader, 0, 2).') at offset '.$ThisFileInfo['avdataoffset']; + return false; + break; + } + + $ThisFileInfo['fileformat'] = 'tiff'; + $ThisFileInfo['video']['dataformat'] = 'tiff'; + $ThisFileInfo['video']['lossless'] = true; + $ThisFileInfo['tiff']['ifd'] = array(); + $CurrentIFD = array(); + + $FieldTypeByteLength = array(1=>1, 2=>1, 3=>2, 4=>4, 5=>8); + + $nextIFDoffset = $this->TIFFendian2Int(fread($fd, 4), $ThisFileInfo['tiff']['byte_order']); + + while ($nextIFDoffset > 0) { + + $CurrentIFD['offset'] = $nextIFDoffset; + + fseek($fd, $ThisFileInfo['avdataoffset'] + $nextIFDoffset, SEEK_SET); + $CurrentIFD['fieldcount'] = $this->TIFFendian2Int(fread($fd, 2), $ThisFileInfo['tiff']['byte_order']); + + for ($i = 0; $i < $CurrentIFD['fieldcount']; $i++) { + $CurrentIFD['fields'][$i]['raw']['tag'] = $this->TIFFendian2Int(fread($fd, 2), $ThisFileInfo['tiff']['byte_order']); + $CurrentIFD['fields'][$i]['raw']['type'] = $this->TIFFendian2Int(fread($fd, 2), $ThisFileInfo['tiff']['byte_order']); + $CurrentIFD['fields'][$i]['raw']['length'] = $this->TIFFendian2Int(fread($fd, 4), $ThisFileInfo['tiff']['byte_order']); + $CurrentIFD['fields'][$i]['raw']['offset'] = fread($fd, 4); + + switch ($CurrentIFD['fields'][$i]['raw']['type']) { + case 1: // BYTE An 8-bit unsigned integer. + if ($CurrentIFD['fields'][$i]['raw']['length'] <= 4) { + $CurrentIFD['fields'][$i]['value'] = $this->TIFFendian2Int(substr($CurrentIFD['fields'][$i]['raw']['offset'], 0, 1), $ThisFileInfo['tiff']['byte_order']); + } else { + $CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $ThisFileInfo['tiff']['byte_order']); + } + break; + + case 2: // ASCII 8-bit bytes that store ASCII codes; the last byte must be null. + if ($CurrentIFD['fields'][$i]['raw']['length'] <= 4) { + $CurrentIFD['fields'][$i]['value'] = substr($CurrentIFD['fields'][$i]['raw']['offset'], 3); + } else { + $CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $ThisFileInfo['tiff']['byte_order']); + } + break; + + case 3: // SHORT A 16-bit (2-byte) unsigned integer. + if ($CurrentIFD['fields'][$i]['raw']['length'] <= 2) { + $CurrentIFD['fields'][$i]['value'] = $this->TIFFendian2Int(substr($CurrentIFD['fields'][$i]['raw']['offset'], 0, 2), $ThisFileInfo['tiff']['byte_order']); + } else { + $CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $ThisFileInfo['tiff']['byte_order']); + } + break; + + case 4: // LONG A 32-bit (4-byte) unsigned integer. + if ($CurrentIFD['fields'][$i]['raw']['length'] <= 1) { + $CurrentIFD['fields'][$i]['value'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $ThisFileInfo['tiff']['byte_order']); + } else { + $CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $ThisFileInfo['tiff']['byte_order']); + } + break; + + case 5: // RATIONAL Two LONG_s: the first represents the numerator of a fraction, the second the denominator. + break; + } + } + + $ThisFileInfo['tiff']['ifd'][] = $CurrentIFD; + $CurrentIFD = array(); + $nextIFDoffset = $this->TIFFendian2Int(fread($fd, 4), $ThisFileInfo['tiff']['byte_order']); + + } + + foreach ($ThisFileInfo['tiff']['ifd'] as $IFDid => $IFDarray) { + foreach ($IFDarray['fields'] as $key => $fieldarray) { + switch ($fieldarray['raw']['tag']) { + case 256: // ImageWidth + case 257: // ImageLength + case 258: // BitsPerSample + case 259: // Compression + if (!isset($fieldarray['value'])) { + fseek($fd, $fieldarray['offset'], SEEK_SET); + $ThisFileInfo['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = fread($fd, $fieldarray['raw']['length'] * $FieldTypeByteLength[$fieldarray['raw']['type']]); + + } + break; + + case 270: // ImageDescription + case 271: // Make + case 272: // Model + case 305: // Software + case 306: // DateTime + case 315: // Artist + case 316: // HostComputer + if (isset($fieldarray['value'])) { + $ThisFileInfo['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = $fieldarray['value']; + } else { + fseek($fd, $fieldarray['offset'], SEEK_SET); + $ThisFileInfo['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = fread($fd, $fieldarray['raw']['length'] * $FieldTypeByteLength[$fieldarray['raw']['type']]); + + } + break; + } + switch ($fieldarray['raw']['tag']) { + case 256: // ImageWidth + $ThisFileInfo['video']['resolution_x'] = $fieldarray['value']; + break; + + case 257: // ImageLength + $ThisFileInfo['video']['resolution_y'] = $fieldarray['value']; + break; + + case 258: // BitsPerSample + if (isset($fieldarray['value'])) { + $ThisFileInfo['video']['bits_per_sample'] = $fieldarray['value']; + } else { + $ThisFileInfo['video']['bits_per_sample'] = 0; + for ($i = 0; $i < $fieldarray['raw']['length']; $i++) { + $ThisFileInfo['video']['bits_per_sample'] += $this->TIFFendian2Int(substr($ThisFileInfo['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'], $i * $FieldTypeByteLength[$fieldarray['raw']['type']], $FieldTypeByteLength[$fieldarray['raw']['type']]), $ThisFileInfo['tiff']['byte_order']); + } + } + break; + + case 259: // Compression + $ThisFileInfo['video']['codec'] = $this->TIFFcompressionMethod($fieldarray['value']); + break; + + case 270: // ImageDescription + case 271: // Make + case 272: // Model + case 305: // Software + case 306: // DateTime + case 315: // Artist + case 316: // HostComputer + @$ThisFileInfo['tiff']['comments'][$this->TIFFcommentName($fieldarray['raw']['tag'])][] = $ThisFileInfo['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data']; + break; + + default: + break; + } + } + } + + return true; + } + + + function TIFFendian2Int($bytestring, $byteorder) { + if ($byteorder == 'Intel') { + return getid3_lib::LittleEndian2Int($bytestring); + } elseif ($byteorder == 'Motorola') { + return getid3_lib::BigEndian2Int($bytestring); + } + return false; + } + + function TIFFcompressionMethod($id) { + static $TIFFcompressionMethod = array(); + if (empty($TIFFcompressionMethod)) { + $TIFFcompressionMethod = array( + 1 => 'Uncompressed', + 2 => 'Huffman', + 3 => 'Fax - CCITT 3', + 5 => 'LZW', + 32773 => 'PackBits', + ); + } + return (isset($TIFFcompressionMethod[$id]) ? $TIFFcompressionMethod[$id] : 'unknown/invalid ('.$id.')'); + } + + function TIFFcommentName($id) { + static $TIFFcommentName = array(); + if (empty($TIFFcommentName)) { + $TIFFcommentName = array( + 270 => 'imagedescription', + 271 => 'make', + 272 => 'model', + 305 => 'software', + 306 => 'datetime', + 315 => 'artist', + 316 => 'hostcomputer', + ); + } + return (isset($TIFFcommentName[$id]) ? $TIFFcommentName[$id] : 'unknown/invalid ('.$id.')'); + } + +} + + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.misc.exe.php b/includes/getid3/getid3/module.misc.exe.php new file mode 100644 index 0000000..8c6bfcf --- /dev/null +++ b/includes/getid3/getid3/module.misc.exe.php @@ -0,0 +1,59 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.misc.exe.php // +// module for analyzing EXE files // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_exe +{ + + function getid3_exe(&$fd, &$ThisFileInfo) { + + fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); + $EXEheader = fread($fd, 28); + + if (substr($EXEheader, 0, 2) != 'MZ') { + $ThisFileInfo['error'][] = 'Expecting "MZ" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($EXEheader, 0, 2).'" instead.'; + return false; + } + + $ThisFileInfo['fileformat'] = 'exe'; + $ThisFileInfo['exe']['mz']['magic'] = 'MZ'; + + $ThisFileInfo['exe']['mz']['raw']['last_page_size'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 2, 2)); + $ThisFileInfo['exe']['mz']['raw']['page_count'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 4, 2)); + $ThisFileInfo['exe']['mz']['raw']['relocation_count'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 6, 2)); + $ThisFileInfo['exe']['mz']['raw']['header_paragraphs'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 8, 2)); + $ThisFileInfo['exe']['mz']['raw']['min_memory_paragraphs'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 10, 2)); + $ThisFileInfo['exe']['mz']['raw']['max_memory_paragraphs'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 12, 2)); + $ThisFileInfo['exe']['mz']['raw']['initial_ss'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 14, 2)); + $ThisFileInfo['exe']['mz']['raw']['initial_sp'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 16, 2)); + $ThisFileInfo['exe']['mz']['raw']['checksum'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 18, 2)); + $ThisFileInfo['exe']['mz']['raw']['cs_ip'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 20, 4)); + $ThisFileInfo['exe']['mz']['raw']['relocation_table_offset'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 24, 2)); + $ThisFileInfo['exe']['mz']['raw']['overlay_number'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 26, 2)); + + $ThisFileInfo['exe']['mz']['byte_size'] = (($ThisFileInfo['exe']['mz']['raw']['page_count'] - 1)) * 512 + $ThisFileInfo['exe']['mz']['raw']['last_page_size']; + $ThisFileInfo['exe']['mz']['header_size'] = $ThisFileInfo['exe']['mz']['raw']['header_paragraphs'] * 16; + $ThisFileInfo['exe']['mz']['memory_minimum'] = $ThisFileInfo['exe']['mz']['raw']['min_memory_paragraphs'] * 16; + $ThisFileInfo['exe']['mz']['memory_recommended'] = $ThisFileInfo['exe']['mz']['raw']['max_memory_paragraphs'] * 16; + +$ThisFileInfo['error'][] = 'EXE parsing not enabled in this version of getID3()'; +return false; + + } + +} + + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.misc.iso.php b/includes/getid3/getid3/module.misc.iso.php new file mode 100644 index 0000000..94df929 --- /dev/null +++ b/includes/getid3/getid3/module.misc.iso.php @@ -0,0 +1,386 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.misc.iso.php // +// module for analyzing ISO files // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_iso +{ + + function getid3_iso($fd, &$ThisFileInfo) { + $ThisFileInfo['fileformat'] = 'iso'; + + for ($i = 16; $i <= 19; $i++) { + fseek($fd, 2048 * $i, SEEK_SET); + $ISOheader = fread($fd, 2048); + if (substr($ISOheader, 1, 5) == 'CD001') { + switch (ord($ISOheader{0})) { + case 1: + $ThisFileInfo['iso']['primary_volume_descriptor']['offset'] = 2048 * $i; + $this->ParsePrimaryVolumeDescriptor($ISOheader, $ThisFileInfo); + break; + + case 2: + $ThisFileInfo['iso']['supplementary_volume_descriptor']['offset'] = 2048 * $i; + $this->ParseSupplementaryVolumeDescriptor($ISOheader, $ThisFileInfo); + break; + + default: + // skip + break; + } + } + } + + $this->ParsePathTable($fd, $ThisFileInfo); + + $ThisFileInfo['iso']['files'] = array(); + foreach ($ThisFileInfo['iso']['path_table']['directories'] as $directorynum => $directorydata) { + + $ThisFileInfo['iso']['directories'][$directorynum] = $this->ParseDirectoryRecord($fd, $directorydata, $ThisFileInfo); + + } + + return true; + + } + + + function ParsePrimaryVolumeDescriptor(&$ISOheader, &$ThisFileInfo) { + // ISO integer values are stored *BOTH* Little-Endian AND Big-Endian format!! + // ie 12345 == 0x3039 is stored as $39 $30 $30 $39 in a 4-byte field + + // shortcuts + $ThisFileInfo['iso']['primary_volume_descriptor']['raw'] = array(); + $thisfile_iso_primaryVD = &$ThisFileInfo['iso']['primary_volume_descriptor']; + $thisfile_iso_primaryVD_raw = &$thisfile_iso_primaryVD['raw']; + + $thisfile_iso_primaryVD_raw['volume_descriptor_type'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 0, 1)); + $thisfile_iso_primaryVD_raw['standard_identifier'] = substr($ISOheader, 1, 5); + if ($thisfile_iso_primaryVD_raw['standard_identifier'] != 'CD001') { + $ThisFileInfo['error'][] = 'Expected "CD001" at offset ('.($thisfile_iso_primaryVD['offset'] + 1).'), found "'.$thisfile_iso_primaryVD_raw['standard_identifier'].'" instead'; + unset($ThisFileInfo['fileformat']); + unset($ThisFileInfo['iso']); + return false; + } + + + $thisfile_iso_primaryVD_raw['volume_descriptor_version'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 6, 1)); + //$thisfile_iso_primaryVD_raw['unused_1'] = substr($ISOheader, 7, 1); + $thisfile_iso_primaryVD_raw['system_identifier'] = substr($ISOheader, 8, 32); + $thisfile_iso_primaryVD_raw['volume_identifier'] = substr($ISOheader, 40, 32); + //$thisfile_iso_primaryVD_raw['unused_2'] = substr($ISOheader, 72, 8); + $thisfile_iso_primaryVD_raw['volume_space_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 80, 4)); + //$thisfile_iso_primaryVD_raw['unused_3'] = substr($ISOheader, 88, 32); + $thisfile_iso_primaryVD_raw['volume_set_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 120, 2)); + $thisfile_iso_primaryVD_raw['volume_sequence_number'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 124, 2)); + $thisfile_iso_primaryVD_raw['logical_block_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 128, 2)); + $thisfile_iso_primaryVD_raw['path_table_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 132, 4)); + $thisfile_iso_primaryVD_raw['path_table_l_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 140, 2)); + $thisfile_iso_primaryVD_raw['path_table_l_opt_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 144, 2)); + $thisfile_iso_primaryVD_raw['path_table_m_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 148, 2)); + $thisfile_iso_primaryVD_raw['path_table_m_opt_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 152, 2)); + $thisfile_iso_primaryVD_raw['root_directory_record'] = substr($ISOheader, 156, 34); + $thisfile_iso_primaryVD_raw['volume_set_identifier'] = substr($ISOheader, 190, 128); + $thisfile_iso_primaryVD_raw['publisher_identifier'] = substr($ISOheader, 318, 128); + $thisfile_iso_primaryVD_raw['data_preparer_identifier'] = substr($ISOheader, 446, 128); + $thisfile_iso_primaryVD_raw['application_identifier'] = substr($ISOheader, 574, 128); + $thisfile_iso_primaryVD_raw['copyright_file_identifier'] = substr($ISOheader, 702, 37); + $thisfile_iso_primaryVD_raw['abstract_file_identifier'] = substr($ISOheader, 739, 37); + $thisfile_iso_primaryVD_raw['bibliographic_file_identifier'] = substr($ISOheader, 776, 37); + $thisfile_iso_primaryVD_raw['volume_creation_date_time'] = substr($ISOheader, 813, 17); + $thisfile_iso_primaryVD_raw['volume_modification_date_time'] = substr($ISOheader, 830, 17); + $thisfile_iso_primaryVD_raw['volume_expiration_date_time'] = substr($ISOheader, 847, 17); + $thisfile_iso_primaryVD_raw['volume_effective_date_time'] = substr($ISOheader, 864, 17); + $thisfile_iso_primaryVD_raw['file_structure_version'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 881, 1)); + //$thisfile_iso_primaryVD_raw['unused_4'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 882, 1)); + $thisfile_iso_primaryVD_raw['application_data'] = substr($ISOheader, 883, 512); + //$thisfile_iso_primaryVD_raw['unused_5'] = substr($ISOheader, 1395, 653); + + $thisfile_iso_primaryVD['system_identifier'] = trim($thisfile_iso_primaryVD_raw['system_identifier']); + $thisfile_iso_primaryVD['volume_identifier'] = trim($thisfile_iso_primaryVD_raw['volume_identifier']); + $thisfile_iso_primaryVD['volume_set_identifier'] = trim($thisfile_iso_primaryVD_raw['volume_set_identifier']); + $thisfile_iso_primaryVD['publisher_identifier'] = trim($thisfile_iso_primaryVD_raw['publisher_identifier']); + $thisfile_iso_primaryVD['data_preparer_identifier'] = trim($thisfile_iso_primaryVD_raw['data_preparer_identifier']); + $thisfile_iso_primaryVD['application_identifier'] = trim($thisfile_iso_primaryVD_raw['application_identifier']); + $thisfile_iso_primaryVD['copyright_file_identifier'] = trim($thisfile_iso_primaryVD_raw['copyright_file_identifier']); + $thisfile_iso_primaryVD['abstract_file_identifier'] = trim($thisfile_iso_primaryVD_raw['abstract_file_identifier']); + $thisfile_iso_primaryVD['bibliographic_file_identifier'] = trim($thisfile_iso_primaryVD_raw['bibliographic_file_identifier']); + $thisfile_iso_primaryVD['volume_creation_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_primaryVD_raw['volume_creation_date_time']); + $thisfile_iso_primaryVD['volume_modification_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_primaryVD_raw['volume_modification_date_time']); + $thisfile_iso_primaryVD['volume_expiration_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_primaryVD_raw['volume_expiration_date_time']); + $thisfile_iso_primaryVD['volume_effective_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_primaryVD_raw['volume_effective_date_time']); + + if (($thisfile_iso_primaryVD_raw['volume_space_size'] * 2048) > $ThisFileInfo['filesize']) { + $ThisFileInfo['error'][] = 'Volume Space Size ('.($thisfile_iso_primaryVD_raw['volume_space_size'] * 2048).' bytes) is larger than the file size ('.$ThisFileInfo['filesize'].' bytes) (truncated file?)'; + } + + return true; + } + + + function ParseSupplementaryVolumeDescriptor(&$ISOheader, &$ThisFileInfo) { + // ISO integer values are stored Both-Endian format!! + // ie 12345 == 0x3039 is stored as $39 $30 $30 $39 in a 4-byte field + + // shortcuts + $ThisFileInfo['iso']['supplementary_volume_descriptor']['raw'] = array(); + $thisfile_iso_supplementaryVD = &$ThisFileInfo['iso']['supplementary_volume_descriptor']; + $thisfile_iso_supplementaryVD_raw = &$thisfile_iso_supplementaryVD['raw']; + + $thisfile_iso_supplementaryVD_raw['volume_descriptor_type'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 0, 1)); + $thisfile_iso_supplementaryVD_raw['standard_identifier'] = substr($ISOheader, 1, 5); + if ($thisfile_iso_supplementaryVD_raw['standard_identifier'] != 'CD001') { + $ThisFileInfo['error'][] = 'Expected "CD001" at offset ('.($thisfile_iso_supplementaryVD['offset'] + 1).'), found "'.$thisfile_iso_supplementaryVD_raw['standard_identifier'].'" instead'; + unset($ThisFileInfo['fileformat']); + unset($ThisFileInfo['iso']); + return false; + } + + $thisfile_iso_supplementaryVD_raw['volume_descriptor_version'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 6, 1)); + //$thisfile_iso_supplementaryVD_raw['unused_1'] = substr($ISOheader, 7, 1); + $thisfile_iso_supplementaryVD_raw['system_identifier'] = substr($ISOheader, 8, 32); + $thisfile_iso_supplementaryVD_raw['volume_identifier'] = substr($ISOheader, 40, 32); + //$thisfile_iso_supplementaryVD_raw['unused_2'] = substr($ISOheader, 72, 8); + $thisfile_iso_supplementaryVD_raw['volume_space_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 80, 4)); + if ($thisfile_iso_supplementaryVD_raw['volume_space_size'] == 0) { + // Supplementary Volume Descriptor not used + //unset($thisfile_iso_supplementaryVD); + //return false; + } + + //$thisfile_iso_supplementaryVD_raw['unused_3'] = substr($ISOheader, 88, 32); + $thisfile_iso_supplementaryVD_raw['volume_set_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 120, 2)); + $thisfile_iso_supplementaryVD_raw['volume_sequence_number'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 124, 2)); + $thisfile_iso_supplementaryVD_raw['logical_block_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 128, 2)); + $thisfile_iso_supplementaryVD_raw['path_table_size'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 132, 4)); + $thisfile_iso_supplementaryVD_raw['path_table_l_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 140, 2)); + $thisfile_iso_supplementaryVD_raw['path_table_l_opt_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 144, 2)); + $thisfile_iso_supplementaryVD_raw['path_table_m_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 148, 2)); + $thisfile_iso_supplementaryVD_raw['path_table_m_opt_location'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 152, 2)); + $thisfile_iso_supplementaryVD_raw['root_directory_record'] = substr($ISOheader, 156, 34); + $thisfile_iso_supplementaryVD_raw['volume_set_identifier'] = substr($ISOheader, 190, 128); + $thisfile_iso_supplementaryVD_raw['publisher_identifier'] = substr($ISOheader, 318, 128); + $thisfile_iso_supplementaryVD_raw['data_preparer_identifier'] = substr($ISOheader, 446, 128); + $thisfile_iso_supplementaryVD_raw['application_identifier'] = substr($ISOheader, 574, 128); + $thisfile_iso_supplementaryVD_raw['copyright_file_identifier'] = substr($ISOheader, 702, 37); + $thisfile_iso_supplementaryVD_raw['abstract_file_identifier'] = substr($ISOheader, 739, 37); + $thisfile_iso_supplementaryVD_raw['bibliographic_file_identifier'] = substr($ISOheader, 776, 37); + $thisfile_iso_supplementaryVD_raw['volume_creation_date_time'] = substr($ISOheader, 813, 17); + $thisfile_iso_supplementaryVD_raw['volume_modification_date_time'] = substr($ISOheader, 830, 17); + $thisfile_iso_supplementaryVD_raw['volume_expiration_date_time'] = substr($ISOheader, 847, 17); + $thisfile_iso_supplementaryVD_raw['volume_effective_date_time'] = substr($ISOheader, 864, 17); + $thisfile_iso_supplementaryVD_raw['file_structure_version'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 881, 1)); + //$thisfile_iso_supplementaryVD_raw['unused_4'] = getid3_lib::LittleEndian2Int(substr($ISOheader, 882, 1)); + $thisfile_iso_supplementaryVD_raw['application_data'] = substr($ISOheader, 883, 512); + //$thisfile_iso_supplementaryVD_raw['unused_5'] = substr($ISOheader, 1395, 653); + + $thisfile_iso_supplementaryVD['system_identifier'] = trim($thisfile_iso_supplementaryVD_raw['system_identifier']); + $thisfile_iso_supplementaryVD['volume_identifier'] = trim($thisfile_iso_supplementaryVD_raw['volume_identifier']); + $thisfile_iso_supplementaryVD['volume_set_identifier'] = trim($thisfile_iso_supplementaryVD_raw['volume_set_identifier']); + $thisfile_iso_supplementaryVD['publisher_identifier'] = trim($thisfile_iso_supplementaryVD_raw['publisher_identifier']); + $thisfile_iso_supplementaryVD['data_preparer_identifier'] = trim($thisfile_iso_supplementaryVD_raw['data_preparer_identifier']); + $thisfile_iso_supplementaryVD['application_identifier'] = trim($thisfile_iso_supplementaryVD_raw['application_identifier']); + $thisfile_iso_supplementaryVD['copyright_file_identifier'] = trim($thisfile_iso_supplementaryVD_raw['copyright_file_identifier']); + $thisfile_iso_supplementaryVD['abstract_file_identifier'] = trim($thisfile_iso_supplementaryVD_raw['abstract_file_identifier']); + $thisfile_iso_supplementaryVD['bibliographic_file_identifier'] = trim($thisfile_iso_supplementaryVD_raw['bibliographic_file_identifier']); + $thisfile_iso_supplementaryVD['volume_creation_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_supplementaryVD_raw['volume_creation_date_time']); + $thisfile_iso_supplementaryVD['volume_modification_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_supplementaryVD_raw['volume_modification_date_time']); + $thisfile_iso_supplementaryVD['volume_expiration_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_supplementaryVD_raw['volume_expiration_date_time']); + $thisfile_iso_supplementaryVD['volume_effective_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_supplementaryVD_raw['volume_effective_date_time']); + + if (($thisfile_iso_supplementaryVD_raw['volume_space_size'] * $thisfile_iso_supplementaryVD_raw['logical_block_size']) > $ThisFileInfo['filesize']) { + $ThisFileInfo['error'][] = 'Volume Space Size ('.($thisfile_iso_supplementaryVD_raw['volume_space_size'] * $thisfile_iso_supplementaryVD_raw['logical_block_size']).' bytes) is larger than the file size ('.$ThisFileInfo['filesize'].' bytes) (truncated file?)'; + } + + return true; + } + + + function ParsePathTable($fd, &$ThisFileInfo) { + if (!isset($ThisFileInfo['iso']['supplementary_volume_descriptor']['raw']['path_table_l_location']) && !isset($ThisFileInfo['iso']['primary_volume_descriptor']['raw']['path_table_l_location'])) { + return false; + } + if (isset($ThisFileInfo['iso']['supplementary_volume_descriptor']['raw']['path_table_l_location'])) { + $PathTableLocation = $ThisFileInfo['iso']['supplementary_volume_descriptor']['raw']['path_table_l_location']; + $PathTableSize = $ThisFileInfo['iso']['supplementary_volume_descriptor']['raw']['path_table_size']; + $TextEncoding = 'UTF-16BE'; // Big-Endian Unicode + } else { + $PathTableLocation = $ThisFileInfo['iso']['primary_volume_descriptor']['raw']['path_table_l_location']; + $PathTableSize = $ThisFileInfo['iso']['primary_volume_descriptor']['raw']['path_table_size']; + $TextEncoding = 'ISO-8859-1'; // Latin-1 + } + + if (($PathTableLocation * 2048) > $ThisFileInfo['filesize']) { + $ThisFileInfo['error'][] = 'Path Table Location specifies an offset ('.($PathTableLocation * 2048).') beyond the end-of-file ('.$ThisFileInfo['filesize'].')'; + return false; + } + + $ThisFileInfo['iso']['path_table']['offset'] = $PathTableLocation * 2048; + fseek($fd, $ThisFileInfo['iso']['path_table']['offset'], SEEK_SET); + $ThisFileInfo['iso']['path_table']['raw'] = fread($fd, $PathTableSize); + + $offset = 0; + $pathcounter = 1; + while ($offset < $PathTableSize) { + // shortcut + $ThisFileInfo['iso']['path_table']['directories'][$pathcounter] = array(); + $thisfile_iso_pathtable_directories_current = &$ThisFileInfo['iso']['path_table']['directories'][$pathcounter]; + + $thisfile_iso_pathtable_directories_current['length'] = getid3_lib::LittleEndian2Int(substr($ThisFileInfo['iso']['path_table']['raw'], $offset, 1)); + $offset += 1; + $thisfile_iso_pathtable_directories_current['extended_length'] = getid3_lib::LittleEndian2Int(substr($ThisFileInfo['iso']['path_table']['raw'], $offset, 1)); + $offset += 1; + $thisfile_iso_pathtable_directories_current['location_logical'] = getid3_lib::LittleEndian2Int(substr($ThisFileInfo['iso']['path_table']['raw'], $offset, 4)); + $offset += 4; + $thisfile_iso_pathtable_directories_current['parent_directory'] = getid3_lib::LittleEndian2Int(substr($ThisFileInfo['iso']['path_table']['raw'], $offset, 2)); + $offset += 2; + $thisfile_iso_pathtable_directories_current['name'] = substr($ThisFileInfo['iso']['path_table']['raw'], $offset, $thisfile_iso_pathtable_directories_current['length']); + $offset += $thisfile_iso_pathtable_directories_current['length'] + ($thisfile_iso_pathtable_directories_current['length'] % 2); + + $thisfile_iso_pathtable_directories_current['name_ascii'] = getid3_lib::iconv_fallback($TextEncoding, $ThisFileInfo['encoding'], $thisfile_iso_pathtable_directories_current['name']); + + $thisfile_iso_pathtable_directories_current['location_bytes'] = $thisfile_iso_pathtable_directories_current['location_logical'] * 2048; + if ($pathcounter == 1) { + $thisfile_iso_pathtable_directories_current['full_path'] = '/'; + } else { + $thisfile_iso_pathtable_directories_current['full_path'] = $ThisFileInfo['iso']['path_table']['directories'][$thisfile_iso_pathtable_directories_current['parent_directory']]['full_path'].$thisfile_iso_pathtable_directories_current['name_ascii'].'/'; + } + $FullPathArray[] = $thisfile_iso_pathtable_directories_current['full_path']; + + $pathcounter++; + } + + return true; + } + + + function ParseDirectoryRecord(&$fd, $directorydata, &$ThisFileInfo) { + if (isset($ThisFileInfo['iso']['supplementary_volume_descriptor'])) { + $TextEncoding = 'UTF-16BE'; // Big-Endian Unicode + } else { + $TextEncoding = 'ISO-8859-1'; // Latin-1 + } + + fseek($fd, $directorydata['location_bytes'], SEEK_SET); + $DirectoryRecordData = fread($fd, 1); + + while (ord($DirectoryRecordData{0}) > 33) { + + $DirectoryRecordData .= fread($fd, ord($DirectoryRecordData{0}) - 1); + + $ThisDirectoryRecord['raw']['length'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 0, 1)); + $ThisDirectoryRecord['raw']['extended_attribute_length'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 1, 1)); + $ThisDirectoryRecord['raw']['offset_logical'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 2, 4)); + $ThisDirectoryRecord['raw']['filesize'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 10, 4)); + $ThisDirectoryRecord['raw']['recording_date_time'] = substr($DirectoryRecordData, 18, 7); + $ThisDirectoryRecord['raw']['file_flags'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 25, 1)); + $ThisDirectoryRecord['raw']['file_unit_size'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 26, 1)); + $ThisDirectoryRecord['raw']['interleave_gap_size'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 27, 1)); + $ThisDirectoryRecord['raw']['volume_sequence_number'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 28, 2)); + $ThisDirectoryRecord['raw']['file_identifier_length'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 32, 1)); + $ThisDirectoryRecord['raw']['file_identifier'] = substr($DirectoryRecordData, 33, $ThisDirectoryRecord['raw']['file_identifier_length']); + + $ThisDirectoryRecord['file_identifier_ascii'] = getid3_lib::iconv_fallback($TextEncoding, $ThisFileInfo['encoding'], $ThisDirectoryRecord['raw']['file_identifier']); + + $ThisDirectoryRecord['filesize'] = $ThisDirectoryRecord['raw']['filesize']; + $ThisDirectoryRecord['offset_bytes'] = $ThisDirectoryRecord['raw']['offset_logical'] * 2048; + $ThisDirectoryRecord['file_flags']['hidden'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x01); + $ThisDirectoryRecord['file_flags']['directory'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x02); + $ThisDirectoryRecord['file_flags']['associated'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x04); + $ThisDirectoryRecord['file_flags']['extended'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x08); + $ThisDirectoryRecord['file_flags']['permissions'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x10); + $ThisDirectoryRecord['file_flags']['multiple'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x80); + $ThisDirectoryRecord['recording_timestamp'] = $this->ISOtime2UNIXtime($ThisDirectoryRecord['raw']['recording_date_time']); + + if ($ThisDirectoryRecord['file_flags']['directory']) { + $ThisDirectoryRecord['filename'] = $directorydata['full_path']; + } else { + $ThisDirectoryRecord['filename'] = $directorydata['full_path'].$this->ISOstripFilenameVersion($ThisDirectoryRecord['file_identifier_ascii']); + $ThisFileInfo['iso']['files'] = getid3_lib::array_merge_clobber($ThisFileInfo['iso']['files'], getid3_lib::CreateDeepArray($ThisDirectoryRecord['filename'], '/', $ThisDirectoryRecord['filesize'])); + } + + $DirectoryRecord[] = $ThisDirectoryRecord; + $DirectoryRecordData = fread($fd, 1); + } + + return $DirectoryRecord; + } + + function ISOstripFilenameVersion($ISOfilename) { + // convert 'filename.ext;1' to 'filename.ext' + if (!strstr($ISOfilename, ';')) { + return $ISOfilename; + } else { + return substr($ISOfilename, 0, strpos($ISOfilename, ';')); + } + } + + function ISOtimeText2UNIXtime($ISOtime) { + + $UNIXyear = (int) substr($ISOtime, 0, 4); + $UNIXmonth = (int) substr($ISOtime, 4, 2); + $UNIXday = (int) substr($ISOtime, 6, 2); + $UNIXhour = (int) substr($ISOtime, 8, 2); + $UNIXminute = (int) substr($ISOtime, 10, 2); + $UNIXsecond = (int) substr($ISOtime, 12, 2); + + if (!$UNIXyear) { + return false; + } + return gmmktime($UNIXhour, $UNIXminute, $UNIXsecond, $UNIXmonth, $UNIXday, $UNIXyear); + } + + function ISOtime2UNIXtime($ISOtime) { + // Represented by seven bytes: + // 1: Number of years since 1900 + // 2: Month of the year from 1 to 12 + // 3: Day of the Month from 1 to 31 + // 4: Hour of the day from 0 to 23 + // 5: Minute of the hour from 0 to 59 + // 6: second of the minute from 0 to 59 + // 7: Offset from Greenwich Mean Time in number of 15 minute intervals from -48 (West) to +52 (East) + + $UNIXyear = ord($ISOtime{0}) + 1900; + $UNIXmonth = ord($ISOtime{1}); + $UNIXday = ord($ISOtime{2}); + $UNIXhour = ord($ISOtime{3}); + $UNIXminute = ord($ISOtime{4}); + $UNIXsecond = ord($ISOtime{5}); + $GMToffset = $this->TwosCompliment2Decimal(ord($ISOtime{5})); + + return gmmktime($UNIXhour, $UNIXminute, $UNIXsecond, $UNIXmonth, $UNIXday, $UNIXyear); + } + + function TwosCompliment2Decimal($BinaryValue) { + // http://sandbox.mc.edu/~bennet/cs110/tc/tctod.html + // First check if the number is negative or positive by looking at the sign bit. + // If it is positive, simply convert it to decimal. + // If it is negative, make it positive by inverting the bits and adding one. + // Then, convert the result to decimal. + // The negative of this number is the value of the original binary. + + if ($BinaryValue & 0x80) { + + // negative number + return (0 - ((~$BinaryValue & 0xFF) + 1)); + } else { + // positive number + return $BinaryValue; + } + } + + +} + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.tag.apetag.php b/includes/getid3/getid3/module.tag.apetag.php new file mode 100644 index 0000000..6ea8010 --- /dev/null +++ b/includes/getid3/getid3/module.tag.apetag.php @@ -0,0 +1,284 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.tag.apetag.php // +// module for analyzing APE tags // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + +class getid3_apetag +{ + + function getid3_apetag(&$fd, &$ThisFileInfo, $overrideendoffset=0) { + $id3v1tagsize = 128; + $apetagheadersize = 32; + $lyrics3tagsize = 10; + + if ($overrideendoffset == 0) { + + fseek($fd, 0 - $id3v1tagsize - $apetagheadersize - $lyrics3tagsize, SEEK_END); + $APEfooterID3v1 = fread($fd, $id3v1tagsize + $apetagheadersize + $lyrics3tagsize); + + //if (preg_match('/APETAGEX.{24}TAG.{125}$/i', $APEfooterID3v1)) { + if (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $id3v1tagsize - $apetagheadersize, 8) == 'APETAGEX') { + + // APE tag found before ID3v1 + $ThisFileInfo['ape']['tag_offset_end'] = $ThisFileInfo['filesize'] - $id3v1tagsize; + + //} elseif (preg_match('/APETAGEX.{24}$/i', $APEfooterID3v1)) { + } elseif (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $apetagheadersize, 8) == 'APETAGEX') { + + // APE tag found, no ID3v1 + $ThisFileInfo['ape']['tag_offset_end'] = $ThisFileInfo['filesize']; + + } + + } else { + + fseek($fd, $overrideendoffset - $apetagheadersize, SEEK_SET); + if (fread($fd, 8) == 'APETAGEX') { + $ThisFileInfo['ape']['tag_offset_end'] = $overrideendoffset; + } + + } + if (!isset($ThisFileInfo['ape']['tag_offset_end'])) { + + // APE tag not found + unset($ThisFileInfo['ape']); + return false; + + } + + // shortcut + $thisfile_ape = &$ThisFileInfo['ape']; + + fseek($fd, $thisfile_ape['tag_offset_end'] - $apetagheadersize, SEEK_SET); + $APEfooterData = fread($fd, 32); + if (!($thisfile_ape['footer'] = $this->parseAPEheaderFooter($APEfooterData))) { + $ThisFileInfo['error'][] = 'Error parsing APE footer at offset '.$thisfile_ape['tag_offset_end']; + return false; + } + + if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) { + fseek($fd, $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'] - $apetagheadersize, SEEK_SET); + $thisfile_ape['tag_offset_start'] = ftell($fd); + $APEtagData = fread($fd, $thisfile_ape['footer']['raw']['tagsize'] + $apetagheadersize); + } else { + $thisfile_ape['tag_offset_start'] = $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize']; + fseek($fd, $thisfile_ape['tag_offset_start'], SEEK_SET); + $APEtagData = fread($fd, $thisfile_ape['footer']['raw']['tagsize']); + } + $ThisFileInfo['avdataend'] = $thisfile_ape['tag_offset_start']; + + if (isset($ThisFileInfo['id3v1']['tag_offset_start']) && ($ThisFileInfo['id3v1']['tag_offset_start'] < $thisfile_ape['tag_offset_end'])) { + $ThisFileInfo['warning'][] = 'ID3v1 tag information ignored since it appears to be a false synch in APEtag data'; + unset($ThisFileInfo['id3v1']); + foreach ($ThisFileInfo['warning'] as $key => $value) { + if ($value == 'Some ID3v1 fields do not use NULL characters for padding') { + unset($ThisFileInfo['warning'][$key]); + sort($ThisFileInfo['warning']); + break; + } + } + } + + $offset = 0; + if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) { + if ($thisfile_ape['header'] = $this->parseAPEheaderFooter(substr($APEtagData, 0, $apetagheadersize))) { + $offset += $apetagheadersize; + } else { + $ThisFileInfo['error'][] = 'Error parsing APE header at offset '.$thisfile_ape['tag_offset_start']; + return false; + } + } + + // shortcut + $ThisFileInfo['replay_gain'] = array(); + $thisfile_replaygain = &$ThisFileInfo['replay_gain']; + + for ($i = 0; $i < $thisfile_ape['footer']['raw']['tag_items']; $i++) { + $value_size = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4)); + $offset += 4; + $item_flags = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4)); + $offset += 4; + if (strstr(substr($APEtagData, $offset), "\x00") === false) { + $ThisFileInfo['error'][] = 'Cannot find null-byte (0x00) seperator between ItemKey #'.$i.' and value. ItemKey starts '.$offset.' bytes into the APE tag, at file offset '.($thisfile_ape['tag_offset_start'] + $offset); + return false; + } + $ItemKeyLength = strpos($APEtagData, "\x00", $offset) - $offset; + $item_key = strtolower(substr($APEtagData, $offset, $ItemKeyLength)); + + // shortcut + $thisfile_ape['items'][$item_key] = array(); + $thisfile_ape_items_current = &$thisfile_ape['items'][$item_key]; + + $offset += ($ItemKeyLength + 1); // skip 0x00 terminator + $thisfile_ape_items_current['data'] = substr($APEtagData, $offset, $value_size); + $offset += $value_size; + + $thisfile_ape_items_current['flags'] = $this->parseAPEtagFlags($item_flags); + switch ($thisfile_ape_items_current['flags']['item_contents_raw']) { + case 0: // UTF-8 + case 3: // Locator (URL, filename, etc), UTF-8 encoded + $thisfile_ape_items_current['data'] = explode("\x00", trim($thisfile_ape_items_current['data'])); + break; + + default: // binary data + break; + } + + switch (strtolower($item_key)) { + case 'replaygain_track_gain': + $thisfile_replaygain['track']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero! + $thisfile_replaygain['track']['originator'] = 'unspecified'; + break; + + case 'replaygain_track_peak': + $thisfile_replaygain['track']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero! + $thisfile_replaygain['track']['originator'] = 'unspecified'; + if ($thisfile_replaygain['track']['peak'] <= 0) { + $ThisFileInfo['warning'][] = 'ReplayGain Track peak from APEtag appears invalid: '.$thisfile_replaygain['track']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")'; + } + break; + + case 'replaygain_album_gain': + $thisfile_replaygain['album']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero! + $thisfile_replaygain['album']['originator'] = 'unspecified'; + break; + + case 'replaygain_album_peak': + $thisfile_replaygain['album']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero! + $thisfile_replaygain['album']['originator'] = 'unspecified'; + if ($thisfile_replaygain['album']['peak'] <= 0) { + $ThisFileInfo['warning'][] = 'ReplayGain Album peak from APEtag appears invalid: '.$thisfile_replaygain['album']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")'; + } + break; + + case 'mp3gain_undo': + list($mp3gain_undo_left, $mp3gain_undo_right, $mp3gain_undo_wrap) = explode(',', $thisfile_ape_items_current['data'][0]); + $thisfile_replaygain['mp3gain']['undo_left'] = intval($mp3gain_undo_left); + $thisfile_replaygain['mp3gain']['undo_right'] = intval($mp3gain_undo_right); + $thisfile_replaygain['mp3gain']['undo_wrap'] = (($mp3gain_undo_wrap == 'Y') ? true : false); + break; + + case 'mp3gain_minmax': + list($mp3gain_globalgain_min, $mp3gain_globalgain_max) = explode(',', $thisfile_ape_items_current['data'][0]); + $thisfile_replaygain['mp3gain']['globalgain_track_min'] = intval($mp3gain_globalgain_min); + $thisfile_replaygain['mp3gain']['globalgain_track_max'] = intval($mp3gain_globalgain_max); + break; + + case 'mp3gain_album_minmax': + list($mp3gain_globalgain_album_min, $mp3gain_globalgain_album_max) = explode(',', $thisfile_ape_items_current['data'][0]); + $thisfile_replaygain['mp3gain']['globalgain_album_min'] = intval($mp3gain_globalgain_album_min); + $thisfile_replaygain['mp3gain']['globalgain_album_max'] = intval($mp3gain_globalgain_album_max); + break; + + case 'tracknumber': + foreach ($thisfile_ape_items_current['data'] as $comment) { + $thisfile_ape['comments']['track'][] = $comment; + } + break; + + default: + foreach ($thisfile_ape_items_current['data'] as $comment) { + $thisfile_ape['comments'][strtolower($item_key)][] = $comment; + } + break; + } + + } + if (empty($thisfile_replaygain)) { + unset($ThisFileInfo['replay_gain']); + } + + return true; + } + + function parseAPEheaderFooter($APEheaderFooterData) { + // http://www.uni-jena.de/~pfk/mpp/sv8/apeheader.html + + // shortcut + $headerfooterinfo['raw'] = array(); + $headerfooterinfo_raw = &$headerfooterinfo['raw']; + + $headerfooterinfo_raw['footer_tag'] = substr($APEheaderFooterData, 0, 8); + if ($headerfooterinfo_raw['footer_tag'] != 'APETAGEX') { + return false; + } + $headerfooterinfo_raw['version'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 8, 4)); + $headerfooterinfo_raw['tagsize'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 12, 4)); + $headerfooterinfo_raw['tag_items'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 16, 4)); + $headerfooterinfo_raw['global_flags'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 20, 4)); + $headerfooterinfo_raw['reserved'] = substr($APEheaderFooterData, 24, 8); + + $headerfooterinfo['tag_version'] = $headerfooterinfo_raw['version'] / 1000; + if ($headerfooterinfo['tag_version'] >= 2) { + $headerfooterinfo['flags'] = $this->parseAPEtagFlags($headerfooterinfo_raw['global_flags']); + } + return $headerfooterinfo; + } + + function parseAPEtagFlags($rawflagint) { + // "Note: APE Tags 1.0 do not use any of the APE Tag flags. + // All are set to zero on creation and ignored on reading." + // http://www.uni-jena.de/~pfk/mpp/sv8/apetagflags.html + $flags['header'] = (bool) ($rawflagint & 0x80000000); + $flags['footer'] = (bool) ($rawflagint & 0x40000000); + $flags['this_is_header'] = (bool) ($rawflagint & 0x20000000); + $flags['item_contents_raw'] = ($rawflagint & 0x00000006) >> 1; + $flags['read_only'] = (bool) ($rawflagint & 0x00000001); + + $flags['item_contents'] = $this->APEcontentTypeFlagLookup($flags['item_contents_raw']); + + return $flags; + } + + function APEcontentTypeFlagLookup($contenttypeid) { + static $APEcontentTypeFlagLookup = array( + 0 => 'utf-8', + 1 => 'binary', + 2 => 'external', + 3 => 'reserved' + ); + return (isset($APEcontentTypeFlagLookup[$contenttypeid]) ? $APEcontentTypeFlagLookup[$contenttypeid] : 'invalid'); + } + + function APEtagItemIsUTF8Lookup($itemkey) { + static $APEtagItemIsUTF8Lookup = array( + 'title', + 'subtitle', + 'artist', + 'album', + 'debut album', + 'publisher', + 'conductor', + 'track', + 'composer', + 'comment', + 'copyright', + 'publicationright', + 'file', + 'year', + 'record date', + 'record location', + 'genre', + 'media', + 'related', + 'isrc', + 'abstract', + 'language', + 'bibliography' + ); + return in_array(strtolower($itemkey), $APEtagItemIsUTF8Lookup); + } + +} + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.tag.id3v1.php b/includes/getid3/getid3/module.tag.id3v1.php new file mode 100644 index 0000000..dd9b47b --- /dev/null +++ b/includes/getid3/getid3/module.tag.id3v1.php @@ -0,0 +1,356 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.tag.id3v1.php // +// module for analyzing ID3v1 tags // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_id3v1 +{ + + function getid3_id3v1(&$fd, &$ThisFileInfo) { + + fseek($fd, -256, SEEK_END); + $preid3v1 = fread($fd, 128); + $id3v1tag = fread($fd, 128); + + if (substr($id3v1tag, 0, 3) == 'TAG') { + + $ThisFileInfo['avdataend'] = $ThisFileInfo['filesize'] - 128; + + $ParsedID3v1['title'] = $this->cutfield(substr($id3v1tag, 3, 30)); + $ParsedID3v1['artist'] = $this->cutfield(substr($id3v1tag, 33, 30)); + $ParsedID3v1['album'] = $this->cutfield(substr($id3v1tag, 63, 30)); + $ParsedID3v1['year'] = $this->cutfield(substr($id3v1tag, 93, 4)); + $ParsedID3v1['comment'] = substr($id3v1tag, 97, 30); // can't remove nulls yet, track detection depends on them + $ParsedID3v1['genreid'] = ord(substr($id3v1tag, 127, 1)); + + // If second-last byte of comment field is null and last byte of comment field is non-null + // then this is ID3v1.1 and the comment field is 28 bytes long and the 30th byte is the track number + if (($id3v1tag{125} === "\x00") && ($id3v1tag{126} !== "\x00")) { + $ParsedID3v1['track'] = ord(substr($ParsedID3v1['comment'], 29, 1)); + $ParsedID3v1['comment'] = substr($ParsedID3v1['comment'], 0, 28); + } + $ParsedID3v1['comment'] = $this->cutfield($ParsedID3v1['comment']); + + $ParsedID3v1['genre'] = $this->LookupGenreName($ParsedID3v1['genreid']); + if (!empty($ParsedID3v1['genre'])) { + unset($ParsedID3v1['genreid']); + } + if (empty($ParsedID3v1['genre']) || (@$ParsedID3v1['genre'] == 'Unknown')) { + unset($ParsedID3v1['genre']); + } + + foreach ($ParsedID3v1 as $key => $value) { + $ParsedID3v1['comments'][$key][0] = $value; + } + + // ID3v1 data is supposed to be padded with NULL characters, but some taggers pad with spaces + $GoodFormatID3v1tag = $this->GenerateID3v1Tag( + $ParsedID3v1['title'], + $ParsedID3v1['artist'], + $ParsedID3v1['album'], + $ParsedID3v1['year'], + $this->LookupGenreID(@$ParsedID3v1['genre']), + $ParsedID3v1['comment'], + @$ParsedID3v1['track']); + $ParsedID3v1['padding_valid'] = true; + if ($id3v1tag !== $GoodFormatID3v1tag) { + $ParsedID3v1['padding_valid'] = false; + $ThisFileInfo['warning'][] = 'Some ID3v1 fields do not use NULL characters for padding'; + } + + $ParsedID3v1['tag_offset_end'] = $ThisFileInfo['filesize']; + $ParsedID3v1['tag_offset_start'] = $ParsedID3v1['tag_offset_end'] - 128; + + $ThisFileInfo['id3v1'] = $ParsedID3v1; + } + + if (substr($preid3v1, 0, 3) == 'TAG') { + // The way iTunes handles tags is, well, brain-damaged. + // It completely ignores v1 if ID3v2 is present. + // This goes as far as adding a new v1 tag *even if there already is one* + + // A suspected double-ID3v1 tag has been detected, but it could be that + // the "TAG" identifier is a legitimate part of an APE or Lyrics3 tag + if (substr($preid3v1, 96, 8) == 'APETAGEX') { + // an APE tag footer was found before the last ID3v1, assume false "TAG" synch + } elseif (substr($preid3v1, 119, 6) == 'LYRICS') { + // a Lyrics3 tag footer was found before the last ID3v1, assume false "TAG" synch + } else { + // APE and Lyrics3 footers not found - assume double ID3v1 + $ThisFileInfo['warning'][] = 'Duplicate ID3v1 tag detected - this has been known to happen with iTunes'; + $ThisFileInfo['avdataend'] -= 128; + } + } + + return true; + } + + function cutfield($str) { + return trim(substr($str, 0, strcspn($str, "\x00"))); + } + + function ArrayOfGenres($allowSCMPXextended=false) { + static $GenreLookup = array( + 0 => 'Blues', + 1 => 'Classic Rock', + 2 => 'Country', + 3 => 'Dance', + 4 => 'Disco', + 5 => 'Funk', + 6 => 'Grunge', + 7 => 'Hip-Hop', + 8 => 'Jazz', + 9 => 'Metal', + 10 => 'New Age', + 11 => 'Oldies', + 12 => 'Other', + 13 => 'Pop', + 14 => 'R&B', + 15 => 'Rap', + 16 => 'Reggae', + 17 => 'Rock', + 18 => 'Techno', + 19 => 'Industrial', + 20 => 'Alternative', + 21 => 'Ska', + 22 => 'Death Metal', + 23 => 'Pranks', + 24 => 'Soundtrack', + 25 => 'Euro-Techno', + 26 => 'Ambient', + 27 => 'Trip-Hop', + 28 => 'Vocal', + 29 => 'Jazz+Funk', + 30 => 'Fusion', + 31 => 'Trance', + 32 => 'Classical', + 33 => 'Instrumental', + 34 => 'Acid', + 35 => 'House', + 36 => 'Game', + 37 => 'Sound Clip', + 38 => 'Gospel', + 39 => 'Noise', + 40 => 'Alt. Rock', + 41 => 'Bass', + 42 => 'Soul', + 43 => 'Punk', + 44 => 'Space', + 45 => 'Meditative', + 46 => 'Instrumental Pop', + 47 => 'Instrumental Rock', + 48 => 'Ethnic', + 49 => 'Gothic', + 50 => 'Darkwave', + 51 => 'Techno-Industrial', + 52 => 'Electronic', + 53 => 'Pop-Folk', + 54 => 'Eurodance', + 55 => 'Dream', + 56 => 'Southern Rock', + 57 => 'Comedy', + 58 => 'Cult', + 59 => 'Gangsta Rap', + 60 => 'Top 40', + 61 => 'Christian Rap', + 62 => 'Pop/Funk', + 63 => 'Jungle', + 64 => 'Native American', + 65 => 'Cabaret', + 66 => 'New Wave', + 67 => 'Psychedelic', + 68 => 'Rave', + 69 => 'Showtunes', + 70 => 'Trailer', + 71 => 'Lo-Fi', + 72 => 'Tribal', + 73 => 'Acid Punk', + 74 => 'Acid Jazz', + 75 => 'Polka', + 76 => 'Retro', + 77 => 'Musical', + 78 => 'Rock & Roll', + 79 => 'Hard Rock', + 80 => 'Folk', + 81 => 'Folk/Rock', + 82 => 'National Folk', + 83 => 'Swing', + 84 => 'Fast-Fusion', + 85 => 'Bebob', + 86 => 'Latin', + 87 => 'Revival', + 88 => 'Celtic', + 89 => 'Bluegrass', + 90 => 'Avantgarde', + 91 => 'Gothic Rock', + 92 => 'Progressive Rock', + 93 => 'Psychedelic Rock', + 94 => 'Symphonic Rock', + 95 => 'Slow Rock', + 96 => 'Big Band', + 97 => 'Chorus', + 98 => 'Easy Listening', + 99 => 'Acoustic', + 100 => 'Humour', + 101 => 'Speech', + 102 => 'Chanson', + 103 => 'Opera', + 104 => 'Chamber Music', + 105 => 'Sonata', + 106 => 'Symphony', + 107 => 'Booty Bass', + 108 => 'Primus', + 109 => 'Porn Groove', + 110 => 'Satire', + 111 => 'Slow Jam', + 112 => 'Club', + 113 => 'Tango', + 114 => 'Samba', + 115 => 'Folklore', + 116 => 'Ballad', + 117 => 'Power Ballad', + 118 => 'Rhythmic Soul', + 119 => 'Freestyle', + 120 => 'Duet', + 121 => 'Punk Rock', + 122 => 'Drum Solo', + 123 => 'A Cappella', + 124 => 'Euro-House', + 125 => 'Dance Hall', + 126 => 'Goa', + 127 => 'Drum & Bass', + 128 => 'Club-House', + 129 => 'Hardcore', + 130 => 'Terror', + 131 => 'Indie', + 132 => 'BritPop', + 133 => 'Negerpunk', + 134 => 'Polsk Punk', + 135 => 'Beat', + 136 => 'Christian Gangsta Rap', + 137 => 'Heavy Metal', + 138 => 'Black Metal', + 139 => 'Crossover', + 140 => 'Contemporary Christian', + 141 => 'Christian Rock', + 142 => 'Merengue', + 143 => 'Salsa', + 144 => 'Trash Metal', + 145 => 'Anime', + 146 => 'JPop', + 147 => 'Synthpop', + + 255 => 'Unknown', + + 'CR' => 'Cover', + 'RX' => 'Remix' + ); + + static $GenreLookupSCMPX = array(); + if ($allowSCMPXextended && empty($GenreLookupSCMPX)) { + $GenreLookupSCMPX = $GenreLookup; + // http://www.geocities.co.jp/SiliconValley-Oakland/3664/alittle.html#GenreExtended + // Extended ID3v1 genres invented by SCMPX + // Note that 255 "Japanese Anime" conflicts with standard "Unknown" + $GenreLookupSCMPX[240] = 'Sacred'; + $GenreLookupSCMPX[241] = 'Northern Europe'; + $GenreLookupSCMPX[242] = 'Irish & Scottish'; + $GenreLookupSCMPX[243] = 'Scotland'; + $GenreLookupSCMPX[244] = 'Ethnic Europe'; + $GenreLookupSCMPX[245] = 'Enka'; + $GenreLookupSCMPX[246] = 'Children\'s Song'; + $GenreLookupSCMPX[247] = 'Japanese Sky'; + $GenreLookupSCMPX[248] = 'Japanese Heavy Rock'; + $GenreLookupSCMPX[249] = 'Japanese Doom Rock'; + $GenreLookupSCMPX[250] = 'Japanese J-POP'; + $GenreLookupSCMPX[251] = 'Japanese Seiyu'; + $GenreLookupSCMPX[252] = 'Japanese Ambient Techno'; + $GenreLookupSCMPX[253] = 'Japanese Moemoe'; + $GenreLookupSCMPX[254] = 'Japanese Tokusatsu'; + //$GenreLookupSCMPX[255] = 'Japanese Anime'; + } + + return ($allowSCMPXextended ? $GenreLookupSCMPX : $GenreLookup); + } + + function LookupGenreName($genreid, $allowSCMPXextended=true) { + switch ($genreid) { + case 'RX': + case 'CR': + break; + default: + $genreid = intval($genreid); // to handle 3 or '3' or '03' + break; + } + $GenreLookup = getid3_id3v1::ArrayOfGenres($allowSCMPXextended); + return (isset($GenreLookup[$genreid]) ? $GenreLookup[$genreid] : false); + } + + function LookupGenreID($genre, $allowSCMPXextended=false) { + $GenreLookup = getid3_id3v1::ArrayOfGenres($allowSCMPXextended); + $LowerCaseNoSpaceSearchTerm = strtolower(str_replace(' ', '', $genre)); + foreach ($GenreLookup as $key => $value) { + foreach ($GenreLookup as $key => $value) { + if (strtolower(str_replace(' ', '', $value)) == $LowerCaseNoSpaceSearchTerm) { + return $key; + } + } + return false; + } + return (isset($GenreLookup[$genreid]) ? $GenreLookup[$genreid] : false); + } + + function StandardiseID3v1GenreName($OriginalGenre) { + if (($GenreID = getid3_id3v1::LookupGenreID($OriginalGenre)) !== false) { + return getid3_id3v1::LookupGenreName($GenreID); + } + return $OriginalGenre; + } + + function GenerateID3v1Tag($title, $artist, $album, $year, $genreid, $comment, $track='') { + $ID3v1Tag = 'TAG'; + $ID3v1Tag .= str_pad(trim(substr($title, 0, 30)), 30, "\x00", STR_PAD_RIGHT); + $ID3v1Tag .= str_pad(trim(substr($artist, 0, 30)), 30, "\x00", STR_PAD_RIGHT); + $ID3v1Tag .= str_pad(trim(substr($album, 0, 30)), 30, "\x00", STR_PAD_RIGHT); + $ID3v1Tag .= str_pad(trim(substr($year, 0, 4)), 4, "\x00", STR_PAD_LEFT); + if (!empty($track) && ($track > 0) && ($track <= 255)) { + $ID3v1Tag .= str_pad(trim(substr($comment, 0, 28)), 28, "\x00", STR_PAD_RIGHT); + $ID3v1Tag .= "\x00"; + if (gettype($track) == 'string') { + $track = (int) $track; + } + $ID3v1Tag .= chr($track); + } else { + $ID3v1Tag .= str_pad(trim(substr($comment, 0, 30)), 30, "\x00", STR_PAD_RIGHT); + } + if (($genreid < 0) || ($genreid > 147)) { + $genreid = 255; // 'unknown' genre + } + switch (gettype($genreid)) { + case 'string': + case 'integer': + $ID3v1Tag .= chr(intval($genreid)); + break; + default: + $ID3v1Tag .= chr(255); // 'unknown' genre + break; + } + + return $ID3v1Tag; + } + +} + + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/module.tag.id3v2.php b/includes/getid3/getid3/module.tag.id3v2.php new file mode 100644 index 0000000..16f517e --- /dev/null +++ b/includes/getid3/getid3/module.tag.id3v2.php @@ -0,0 +1,3130 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +/// // +// module.tag.id3v2.php // +// module for analyzing ID3v2 tags // +// dependencies: module.tag.id3v1.php // +// /// +///////////////////////////////////////////////////////////////// + +getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v1.php', __FILE__, true); + +class getid3_id3v2 +{ + + function getid3_id3v2(&$fd, &$ThisFileInfo, $StartingOffset=0) { + // Overall tag structure: + // +-----------------------------+ + // | Header (10 bytes) | + // +-----------------------------+ + // | Extended Header | + // | (variable length, OPTIONAL) | + // +-----------------------------+ + // | Frames (variable length) | + // +-----------------------------+ + // | Padding | + // | (variable length, OPTIONAL) | + // +-----------------------------+ + // | Footer (10 bytes, OPTIONAL) | + // +-----------------------------+ + + // Header + // ID3v2/file identifier "ID3" + // ID3v2 version $04 00 + // ID3v2 flags (%ab000000 in v2.2, %abc00000 in v2.3, %abcd0000 in v2.4.x) + // ID3v2 size 4 * %0xxxxxxx + + + // shortcuts + $ThisFileInfo['id3v2']['header'] = true; + $thisfile_id3v2 = &$ThisFileInfo['id3v2']; + $thisfile_id3v2['flags'] = array(); + $thisfile_id3v2_flags = &$thisfile_id3v2['flags']; + + + fseek($fd, $StartingOffset, SEEK_SET); + $header = fread($fd, 10); + if (substr($header, 0, 3) == 'ID3') { + + $thisfile_id3v2['majorversion'] = ord($header{3}); + $thisfile_id3v2['minorversion'] = ord($header{4}); + + // shortcut + $id3v2_majorversion = &$thisfile_id3v2['majorversion']; + + } else { + + unset($ThisFileInfo['id3v2']); + return false; + + } + + if ($id3v2_majorversion > 4) { // this script probably won't correctly parse ID3v2.5.x and above (if it ever exists) + + $ThisFileInfo['error'][] = 'this script only parses up to ID3v2.4.x - this tag is ID3v2.'.$id3v2_majorversion.'.'.$thisfile_id3v2['minorversion']; + return false; + + } + + $id3_flags = ord($header{5}); + switch ($id3v2_majorversion) { + case 2: + // %ab000000 in v2.2 + $thisfile_id3v2_flags['unsynch'] = (bool) ($id3_flags & 0x80); // a - Unsynchronisation + $thisfile_id3v2_flags['compression'] = (bool) ($id3_flags & 0x40); // b - Compression + break; + + case 3: + // %abc00000 in v2.3 + $thisfile_id3v2_flags['unsynch'] = (bool) ($id3_flags & 0x80); // a - Unsynchronisation + $thisfile_id3v2_flags['exthead'] = (bool) ($id3_flags & 0x40); // b - Extended header + $thisfile_id3v2_flags['experim'] = (bool) ($id3_flags & 0x20); // c - Experimental indicator + break; + + case 4: + // %abcd0000 in v2.4 + $thisfile_id3v2_flags['unsynch'] = (bool) ($id3_flags & 0x80); // a - Unsynchronisation + $thisfile_id3v2_flags['exthead'] = (bool) ($id3_flags & 0x40); // b - Extended header + $thisfile_id3v2_flags['experim'] = (bool) ($id3_flags & 0x20); // c - Experimental indicator + $thisfile_id3v2_flags['isfooter'] = (bool) ($id3_flags & 0x10); // d - Footer present + break; + } + + $thisfile_id3v2['headerlength'] = getid3_lib::BigEndian2Int(substr($header, 6, 4), 1) + 10; // length of ID3v2 tag in 10-byte header doesn't include 10-byte header length + + $thisfile_id3v2['tag_offset_start'] = $StartingOffset; + $thisfile_id3v2['tag_offset_end'] = $thisfile_id3v2['tag_offset_start'] + $thisfile_id3v2['headerlength']; + + // Extended Header + if (isset($thisfile_id3v2_flags['exthead']) && $thisfile_id3v2_flags['exthead']) { + // Extended header size 4 * %0xxxxxxx + // Number of flag bytes $01 + // Extended Flags $xx + // Where the 'Extended header size' is the size of the whole extended header, stored as a 32 bit synchsafe integer. + $thisfile_id3v2['exthead_length'] = getid3_lib::BigEndian2Int(fread($fd, 4), 1); + + $thisfile_id3v2['exthead_flag_bytes'] = ord(fread($fd, 1)); + if ($thisfile_id3v2['exthead_flag_bytes'] == 1) { + // The extended flags field, with its size described by 'number of flag bytes', is defined as: + // %0bcd0000 + // b - Tag is an update + // Flag data length $00 + // c - CRC data present + // Flag data length $05 + // Total frame CRC 5 * %0xxxxxxx + // d - Tag restrictions + // Flag data length $01 + $extheaderflags = fread($fd, $thisfile_id3v2['exthead_flag_bytes']); + $id3_exthead_flags = getid3_lib::BigEndian2Bin(substr($header, 5, 1)); + $thisfile_id3v2['exthead_flags']['update'] = substr($id3_exthead_flags, 1, 1); + $thisfile_id3v2['exthead_flags']['CRC'] = substr($id3_exthead_flags, 2, 1); + if ($thisfile_id3v2['exthead_flags']['CRC']) { + $extheaderrawCRC = fread($fd, 5); + $thisfile_id3v2['exthead_flags']['CRC'] = getid3_lib::BigEndian2Int($extheaderrawCRC, 1); + } + $thisfile_id3v2['exthead_flags']['restrictions'] = substr($id3_exthead_flags, 3, 1); + if ($thisfile_id3v2['exthead_flags']['restrictions']) { + // Restrictions %ppqrrstt + $extheaderrawrestrictions = fread($fd, 1); + $thisfile_id3v2['exthead_flags']['restrictions_tagsize'] = (bindec('11000000') & ord($extheaderrawrestrictions)) >> 6; // p - Tag size restrictions + $thisfile_id3v2['exthead_flags']['restrictions_textenc'] = (bindec('00100000') & ord($extheaderrawrestrictions)) >> 5; // q - Text encoding restrictions + $thisfile_id3v2['exthead_flags']['restrictions_textsize'] = (bindec('00011000') & ord($extheaderrawrestrictions)) >> 3; // r - Text fields size restrictions + $thisfile_id3v2['exthead_flags']['restrictions_imgenc'] = (bindec('00000100') & ord($extheaderrawrestrictions)) >> 2; // s - Image encoding restrictions + $thisfile_id3v2['exthead_flags']['restrictions_imgsize'] = (bindec('00000011') & ord($extheaderrawrestrictions)) >> 0; // t - Image size restrictions + } + } else { + $ThisFileInfo['warning'][] = '$thisfile_id3v2[exthead_flag_bytes] = "'.$thisfile_id3v2['exthead_flag_bytes'].'" (expecting "1")'; + fseek($fd, $thisfile_id3v2['exthead_length'] - 1, SEEK_CUR); + //return false; + } + } // end extended header + + + // create 'encoding' key - used by getid3::HandleAllTags() + // in ID3v2 every field can have it's own encoding type + // so force everything to UTF-8 so it can be handled consistantly + $thisfile_id3v2['encoding'] = 'UTF-8'; + + + // Frames + + // All ID3v2 frames consists of one frame header followed by one or more + // fields containing the actual information. The header is always 10 + // bytes and laid out as follows: + // + // Frame ID $xx xx xx xx (four characters) + // Size 4 * %0xxxxxxx + // Flags $xx xx + + $sizeofframes = $thisfile_id3v2['headerlength'] - 10; // not including 10-byte initial header + if (@$thisfile_id3v2['exthead_length']) { + $sizeofframes -= ($thisfile_id3v2['exthead_length'] + 4); + } + if (@$thisfile_id3v2_flags['isfooter']) { + $sizeofframes -= 10; // footer takes last 10 bytes of ID3v2 header, after frame data, before audio + } + if ($sizeofframes > 0) { + + $framedata = fread($fd, $sizeofframes); // read all frames from file into $framedata variable + + // if entire frame data is unsynched, de-unsynch it now (ID3v2.3.x) + if (@$thisfile_id3v2_flags['unsynch'] && ($id3v2_majorversion <= 3)) { + $framedata = $this->DeUnsynchronise($framedata); + } + // [in ID3v2.4.0] Unsynchronisation [S:6.1] is done on frame level, instead + // of on tag level, making it easier to skip frames, increasing the streamability + // of the tag. The unsynchronisation flag in the header [S:3.1] indicates that + // there exists an unsynchronised frame, while the new unsynchronisation flag in + // the frame header [S:4.1.2] indicates unsynchronisation. + + $framedataoffset = 10 + (@$thisfile_id3v2['exthead_length'] ? $thisfile_id3v2['exthead_length'] + 4 : 0); // how many bytes into the stream - start from after the 10-byte header (and extended header length+4, if present) + while (isset($framedata) && (strlen($framedata) > 0)) { // cycle through until no more frame data is left to parse + if (strlen($framedata) <= $this->ID3v2HeaderLength($id3v2_majorversion)) { + // insufficient room left in ID3v2 header for actual data - must be padding + $thisfile_id3v2['padding']['start'] = $framedataoffset; + $thisfile_id3v2['padding']['length'] = strlen($framedata); + $thisfile_id3v2['padding']['valid'] = true; + for ($i = 0; $i < $thisfile_id3v2['padding']['length']; $i++) { + if ($framedata{$i} != "\x00") { + $thisfile_id3v2['padding']['valid'] = false; + $thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i; + $ThisFileInfo['warning'][] = 'Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)'; + break; + } + } + break; // skip rest of ID3v2 header + } + if ($id3v2_majorversion == 2) { + // Frame ID $xx xx xx (three characters) + // Size $xx xx xx (24-bit integer) + // Flags $xx xx + + $frame_header = substr($framedata, 0, 6); // take next 6 bytes for header + $framedata = substr($framedata, 6); // and leave the rest in $framedata + $frame_name = substr($frame_header, 0, 3); + $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 3, 3), 0); + $frame_flags = 0; // not used for anything in ID3v2.2, just set to avoid E_NOTICEs + + } elseif ($id3v2_majorversion > 2) { + + // Frame ID $xx xx xx xx (four characters) + // Size $xx xx xx xx (32-bit integer in v2.3, 28-bit synchsafe in v2.4+) + // Flags $xx xx + + $frame_header = substr($framedata, 0, 10); // take next 10 bytes for header + $framedata = substr($framedata, 10); // and leave the rest in $framedata + + $frame_name = substr($frame_header, 0, 4); + if ($id3v2_majorversion == 3) { + $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0); // 32-bit integer + } else { // ID3v2.4+ + $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 1); // 32-bit synchsafe integer (28-bit value) + } + + if ($frame_size < (strlen($framedata) + 4)) { + $nextFrameID = substr($framedata, $frame_size, 4); + if ($this->IsValidID3v2FrameName($nextFrameID, $id3v2_majorversion)) { + // next frame is OK + } elseif (($frame_name == "\x00".'MP3') || ($frame_name == "\x00\x00".'MP') || ($frame_name == ' MP3') || ($frame_name == 'MP3e')) { + // MP3ext known broken frames - "ok" for the purposes of this test + } elseif (($id3v2_majorversion == 4) && ($this->IsValidID3v2FrameName(substr($framedata, getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0), 4), 3))) { + $ThisFileInfo['warning'][] = 'ID3v2 tag written as ID3v2.4, but with non-synchsafe integers (ID3v2.3 style). Older versions of (Helium2; iTunes) are known culprits of this. Tag has been parsed as ID3v2.3'; + $id3v2_majorversion = 3; + $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0); // 32-bit integer + } + } + + + $frame_flags = getid3_lib::BigEndian2Int(substr($frame_header, 8, 2)); + } + + if ((($id3v2_majorversion == 2) && ($frame_name == "\x00\x00\x00")) || ($frame_name == "\x00\x00\x00\x00")) { + // padding encountered + + $thisfile_id3v2['padding']['start'] = $framedataoffset; + $thisfile_id3v2['padding']['length'] = strlen($framedata); + $thisfile_id3v2['padding']['valid'] = true; + for ($i = 0; $i < $thisfile_id3v2['padding']['length']; $i++) { + if ($framedata{$i} != "\x00") { + $thisfile_id3v2['padding']['valid'] = false; + $thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i; + $ThisFileInfo['warning'][] = 'Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)'; + break; + } + } + break; // skip rest of ID3v2 header + } + + if ($frame_name == 'COM ') { + $ThisFileInfo['warning'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))). [Note: this particular error has been known to happen with tags edited by iTunes (versions "X v2.0.3", "v3.0.1" are known-guilty, probably others too)]'; + $frame_name = 'COMM'; + } + if (($frame_size <= strlen($framedata)) && ($this->IsValidID3v2FrameName($frame_name, $id3v2_majorversion))) { + + unset($parsedFrame); + $parsedFrame['frame_name'] = $frame_name; + $parsedFrame['frame_flags_raw'] = $frame_flags; + $parsedFrame['data'] = substr($framedata, 0, $frame_size); + $parsedFrame['datalength'] = getid3_lib::CastAsInt($frame_size); + $parsedFrame['dataoffset'] = $framedataoffset; + + $this->ParseID3v2Frame($parsedFrame, $ThisFileInfo); + $thisfile_id3v2[$frame_name][] = $parsedFrame; + + $framedata = substr($framedata, $frame_size); + + } else { // invalid frame length or FrameID + + if ($frame_size <= strlen($framedata)) { + + if ($this->IsValidID3v2FrameName(substr($framedata, $frame_size, 4), $id3v2_majorversion)) { + + // next frame is valid, just skip the current frame + $framedata = substr($framedata, $frame_size); + $ThisFileInfo['warning'][] = 'Next ID3v2 frame is valid, skipping current frame.'; + + } else { + + // next frame is invalid too, abort processing + //unset($framedata); + $framedata = null; + $ThisFileInfo['error'][] = 'Next ID3v2 frame is also invalid, aborting processing.'; + + } + + } elseif ($frame_size == strlen($framedata)) { + + // this is the last frame, just skip + $ThisFileInfo['warning'][] = 'This was the last ID3v2 frame.'; + + } else { + + // next frame is invalid too, abort processing + //unset($framedata); + $framedata = null; + $ThisFileInfo['warning'][] = 'Invalid ID3v2 frame size, aborting.'; + + } + if (!$this->IsValidID3v2FrameName($frame_name, $id3v2_majorversion)) { + + switch ($frame_name) { + case "\x00\x00".'MP': + case "\x00".'MP3': + case ' MP3': + case 'MP3e': + case "\x00".'MP': + case ' MP': + case 'MP3': + $ThisFileInfo['warning'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: !IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))). [Note: this particular error has been known to happen with tags edited by "MP3ext (www.mutschler.de/mp3ext/)"]'; + break; + + default: + $ThisFileInfo['warning'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: !IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))).'; + break; + } + + } elseif ($frame_size > strlen($framedata)){ + + $ThisFileInfo['error'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: $frame_size ('.$frame_size.') > strlen($framedata) ('.strlen($framedata).')).'; + + } else { + + $ThisFileInfo['error'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag).'; + + } + + } + $framedataoffset += ($frame_size + $this->ID3v2HeaderLength($id3v2_majorversion)); + + } + + } + + + // Footer + + // The footer is a copy of the header, but with a different identifier. + // ID3v2 identifier "3DI" + // ID3v2 version $04 00 + // ID3v2 flags %abcd0000 + // ID3v2 size 4 * %0xxxxxxx + + if (isset($thisfile_id3v2_flags['isfooter']) && $thisfile_id3v2_flags['isfooter']) { + $footer = fread($fd, 10); + if (substr($footer, 0, 3) == '3DI') { + $thisfile_id3v2['footer'] = true; + $thisfile_id3v2['majorversion_footer'] = ord($footer{3}); + $thisfile_id3v2['minorversion_footer'] = ord($footer{4}); + } + if ($thisfile_id3v2['majorversion_footer'] <= 4) { + $id3_flags = ord(substr($footer{5})); + $thisfile_id3v2_flags['unsynch_footer'] = (bool) ($id3_flags & 0x80); + $thisfile_id3v2_flags['extfoot_footer'] = (bool) ($id3_flags & 0x40); + $thisfile_id3v2_flags['experim_footer'] = (bool) ($id3_flags & 0x20); + $thisfile_id3v2_flags['isfooter_footer'] = (bool) ($id3_flags & 0x10); + + $thisfile_id3v2['footerlength'] = getid3_lib::BigEndian2Int(substr($footer, 6, 4), 1); + } + } // end footer + + if (isset($thisfile_id3v2['comments']['genre'])) { + foreach ($thisfile_id3v2['comments']['genre'] as $key => $value) { + unset($thisfile_id3v2['comments']['genre'][$key]); + $thisfile_id3v2['comments'] = getid3_lib::array_merge_noclobber($thisfile_id3v2['comments'], $this->ParseID3v2GenreString($value)); + } + } + + if (isset($thisfile_id3v2['comments']['track'])) { + foreach ($thisfile_id3v2['comments']['track'] as $key => $value) { + if (strstr($value, '/')) { + list($thisfile_id3v2['comments']['tracknum'][$key], $thisfile_id3v2['comments']['totaltracks'][$key]) = explode('/', $thisfile_id3v2['comments']['track'][$key]); + } + } + } + + if (!isset($thisfile_id3v2['comments']['year']) && ereg('^([0-9]{4})', trim(@$thisfile_id3v2['comments']['recording_time'][0]), $matches)) { + $thisfile_id3v2['comments']['year'] = array($matches[1]); + } + + + // Set avdataoffset + $ThisFileInfo['avdataoffset'] = $thisfile_id3v2['headerlength']; + if (isset($thisfile_id3v2['footer'])) { + $ThisFileInfo['avdataoffset'] += 10; + } + + return true; + } + + + function ParseID3v2GenreString($genrestring) { + // Parse genres into arrays of genreName and genreID + // ID3v2.2.x, ID3v2.3.x: '(21)' or '(4)Eurodisco' or '(51)(39)' or '(55)((I think...)' + // ID3v2.4.x: '21' $00 'Eurodisco' $00 + + $genrestring = trim($genrestring); + $returnarray = array(); + if (strpos($genrestring, "\x00") !== false) { + $unprocessed = trim($genrestring); // trailing nulls will cause an infinite loop. + $genrestring = ''; + while (strpos($unprocessed, "\x00") !== false) { + // convert null-seperated v2.4-format into v2.3 ()-seperated format + $endpos = strpos($unprocessed, "\x00"); + $genrestring .= '('.substr($unprocessed, 0, $endpos).')'; + $unprocessed = substr($unprocessed, $endpos + 1); + } + unset($unprocessed); + } + if (getid3_id3v1::LookupGenreID($genrestring)) { + + $returnarray['genre'][] = $genrestring; + + } else { + + while (strpos($genrestring, '(') !== false) { + + $startpos = strpos($genrestring, '('); + $endpos = strpos($genrestring, ')'); + if (substr($genrestring, $startpos + 1, 1) == '(') { + $genrestring = substr($genrestring, 0, $startpos).substr($genrestring, $startpos + 1); + $endpos--; + } + $element = substr($genrestring, $startpos + 1, $endpos - ($startpos + 1)); + $genrestring = substr($genrestring, 0, $startpos).substr($genrestring, $endpos + 1); + if (getid3_id3v1::LookupGenreName($element)) { // $element is a valid genre id/abbreviation + + if (empty($returnarray['genre']) || !in_array(getid3_id3v1::LookupGenreName($element), $returnarray['genre'])) { // avoid duplicate entires + $returnarray['genre'][] = getid3_id3v1::LookupGenreName($element); + } + + } else { + + if (empty($returnarray['genre']) || !in_array($element, $returnarray['genre'])) { // avoid duplicate entires + $returnarray['genre'][] = $element; + } + + } + } + } + if ($genrestring) { + if (empty($returnarray['genre']) || !in_array($genrestring, $returnarray['genre'])) { // avoid duplicate entires + $returnarray['genre'][] = $genrestring; + } + } + + return $returnarray; + } + + + function ParseID3v2Frame(&$parsedFrame, &$ThisFileInfo) { + + // shortcuts + $id3v2_majorversion = $ThisFileInfo['id3v2']['majorversion']; + + $parsedFrame['framenamelong'] = $this->FrameNameLongLookup($parsedFrame['frame_name']); + if (empty($parsedFrame['framenamelong'])) { + unset($parsedFrame['framenamelong']); + } + $parsedFrame['framenameshort'] = $this->FrameNameShortLookup($parsedFrame['frame_name']); + if (empty($parsedFrame['framenameshort'])) { + unset($parsedFrame['framenameshort']); + } + + if ($id3v2_majorversion >= 3) { // frame flags are not part of the ID3v2.2 standard + if ($id3v2_majorversion == 3) { + // Frame Header Flags + // %abc00000 %ijk00000 + $parsedFrame['flags']['TagAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x8000); // a - Tag alter preservation + $parsedFrame['flags']['FileAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4000); // b - File alter preservation + $parsedFrame['flags']['ReadOnly'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2000); // c - Read only + $parsedFrame['flags']['compression'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0080); // i - Compression + $parsedFrame['flags']['Encryption'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0040); // j - Encryption + $parsedFrame['flags']['GroupingIdentity'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0020); // k - Grouping identity + + } elseif ($id3v2_majorversion == 4) { + // Frame Header Flags + // %0abc0000 %0h00kmnp + $parsedFrame['flags']['TagAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4000); // a - Tag alter preservation + $parsedFrame['flags']['FileAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2000); // b - File alter preservation + $parsedFrame['flags']['ReadOnly'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x1000); // c - Read only + $parsedFrame['flags']['GroupingIdentity'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0040); // h - Grouping identity + $parsedFrame['flags']['compression'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0008); // k - Compression + $parsedFrame['flags']['Encryption'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0004); // m - Encryption + $parsedFrame['flags']['Unsynchronisation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0002); // n - Unsynchronisation + $parsedFrame['flags']['DataLengthIndicator'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0001); // p - Data length indicator + + // Frame-level de-unsynchronisation - ID3v2.4 + if ($parsedFrame['flags']['Unsynchronisation']) { + $parsedFrame['data'] = $this->DeUnsynchronise($parsedFrame['data']); + } + } + + // Frame-level de-compression + if ($parsedFrame['flags']['compression']) { + $parsedFrame['decompressed_size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 4)); + if (!function_exists('gzuncompress')) { + $ThisFileInfo['warning'][] = 'gzuncompress() support required to decompress ID3v2 frame "'.$parsedFrame['frame_name'].'"'; + } elseif ($decompresseddata = @gzuncompress(substr($parsedFrame['data'], 4))) { + $parsedFrame['data'] = $decompresseddata; + } else { + $ThisFileInfo['warning'][] = 'gzuncompress() failed on compressed contents of ID3v2 frame "'.$parsedFrame['frame_name'].'"'; + } + } + } + + if (isset($parsedFrame['datalength']) && ($parsedFrame['datalength'] == 0)) { + + $warning = 'Frame "'.$parsedFrame['frame_name'].'" at offset '.$parsedFrame['dataoffset'].' has no data portion'; + switch ($parsedFrame['frame_name']) { + case 'WCOM': + $warning .= ' (this is known to happen with files tagged by RioPort)'; + break; + + default: + break; + } + $ThisFileInfo['warning'][] = $warning; + + } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'UFID')) || // 4.1 UFID Unique file identifier + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'UFI'))) { // 4.1 UFI Unique file identifier + // There may be more than one 'UFID' frame in a tag, + // but only one with the same 'Owner identifier'. + //
+ // Owner identifier $00 + // Identifier + + $frame_terminatorpos = strpos($parsedFrame['data'], "\x00"); + $frame_idstring = substr($parsedFrame['data'], 0, $frame_terminatorpos); + $parsedFrame['ownerid'] = $frame_idstring; + $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen("\x00")); + unset($parsedFrame['data']); + + + } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'TXXX')) || // 4.2.2 TXXX User defined text information frame + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'TXX'))) { // 4.2.2 TXX User defined text information frame + // There may be more than one 'TXXX' frame in each tag, + // but only one with the same description. + //
+ // Text encoding $xx + // Description $00 (00) + // Value + + $frame_offset = 0; + $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + + if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { + $ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; + } + $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); + if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { + $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 + } + $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_description) === 0) { + $frame_description = ''; + } + $parsedFrame['encodingid'] = $frame_textencoding; + $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); + + $parsedFrame['description'] = $frame_description; + $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); + if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { + $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data'])); + } + unset($parsedFrame['data']); + + + } elseif ($parsedFrame['frame_name']{0} == 'T') { // 4.2. T??[?] Text information frame + // There may only be one text information frame of its kind in an tag. + //
+ // Text encoding $xx + // Information + + $frame_offset = 0; + $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { + $ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; + } + + $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); + + $parsedFrame['encodingid'] = $frame_textencoding; + $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); + + if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { + $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data']); + } + + } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'WXXX')) || // 4.3.2 WXXX User defined URL link frame + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'WXX'))) { // 4.3.2 WXX User defined URL link frame + // There may be more than one 'WXXX' frame in each tag, + // but only one with the same description + //
+ // Text encoding $xx + // Description $00 (00) + // URL + + $frame_offset = 0; + $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { + $ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; + } + $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); + if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { + $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 + } + $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + + if (ord($frame_description) === 0) { + $frame_description = ''; + } + $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); + + $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding)); + if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { + $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 + } + if ($frame_terminatorpos) { + // there are null bytes after the data - this is not according to spec + // only use data up to first null byte + $frame_urldata = (string) substr($parsedFrame['data'], 0, $frame_terminatorpos); + } else { + // no null bytes following data, just use all data + $frame_urldata = (string) $parsedFrame['data']; + } + + $parsedFrame['encodingid'] = $frame_textencoding; + $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); + + $parsedFrame['url'] = $frame_urldata; + $parsedFrame['description'] = $frame_description; + if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) { + $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['url']); + } + unset($parsedFrame['data']); + + + } elseif ($parsedFrame['frame_name']{0} == 'W') { // 4.3. W??? URL link frames + // There may only be one URL link frame of its kind in a tag, + // except when stated otherwise in the frame description + //
+ // URL + + $parsedFrame['url'] = trim($parsedFrame['data']); + if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) { + $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['url']; + } + unset($parsedFrame['data']); + + + } elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'IPLS')) || // 4.4 IPLS Involved people list (ID3v2.3 only) + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'IPL'))) { // 4.4 IPL Involved people list (ID3v2.2 only) + // There may only be one 'IPL' frame in each tag + //
+ // Text encoding $xx + // People list strings + + $frame_offset = 0; + $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { + $ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; + } + $parsedFrame['encodingid'] = $frame_textencoding; + $parsedFrame['encoding'] = $this->TextEncodingNameLookup($parsedFrame['encodingid']); + + $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); + if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { + $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data']); + } + + + } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'MCDI')) || // 4.4 MCDI Music CD identifier + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'MCI'))) { // 4.5 MCI Music CD identifier + // There may only be one 'MCDI' frame in each tag + //
+ // CD TOC + + if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { + $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['data']; + } + + + } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'ETCO')) || // 4.5 ETCO Event timing codes + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'ETC'))) { // 4.6 ETC Event timing codes + // There may only be one 'ETCO' frame in each tag + //
+ // Time stamp format $xx + // Where time stamp format is: + // $01 (32-bit value) MPEG frames from beginning of file + // $02 (32-bit value) milliseconds from beginning of file + // Followed by a list of key events in the following format: + // Type of event $xx + // Time stamp $xx (xx ...) + // The 'Time stamp' is set to zero if directly at the beginning of the sound + // or after the previous event. All events MUST be sorted in chronological order. + + $frame_offset = 0; + $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + + while ($frame_offset < strlen($parsedFrame['data'])) { + $parsedFrame['typeid'] = substr($parsedFrame['data'], $frame_offset++, 1); + $parsedFrame['type'] = $this->ETCOEventLookup($parsedFrame['typeid']); + $parsedFrame['timestamp'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); + $frame_offset += 4; + } + unset($parsedFrame['data']); + + + } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'MLLT')) || // 4.6 MLLT MPEG location lookup table + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'MLL'))) { // 4.7 MLL MPEG location lookup table + // There may only be one 'MLLT' frame in each tag + //
+ // MPEG frames between reference $xx xx + // Bytes between reference $xx xx xx + // Milliseconds between reference $xx xx xx + // Bits for bytes deviation $xx + // Bits for milliseconds dev. $xx + // Then for every reference the following data is included; + // Deviation in bytes %xxx.... + // Deviation in milliseconds %xxx.... + + $frame_offset = 0; + $parsedFrame['framesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 2)); + $parsedFrame['bytesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 2, 3)); + $parsedFrame['msbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 5, 3)); + $parsedFrame['bitsforbytesdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 8, 1)); + $parsedFrame['bitsformsdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 9, 1)); + $parsedFrame['data'] = substr($parsedFrame['data'], 10); + while ($frame_offset < strlen($parsedFrame['data'])) { + $deviationbitstream .= getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1)); + } + $reference_counter = 0; + while (strlen($deviationbitstream) > 0) { + $parsedFrame[$reference_counter]['bytedeviation'] = bindec(substr($deviationbitstream, 0, $parsedFrame['bitsforbytesdeviation'])); + $parsedFrame[$reference_counter]['msdeviation'] = bindec(substr($deviationbitstream, $parsedFrame['bitsforbytesdeviation'], $parsedFrame['bitsformsdeviation'])); + $deviationbitstream = substr($deviationbitstream, $parsedFrame['bitsforbytesdeviation'] + $parsedFrame['bitsformsdeviation']); + $reference_counter++; + } + unset($parsedFrame['data']); + + + } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'SYTC')) || // 4.7 SYTC Synchronised tempo codes + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'STC'))) { // 4.8 STC Synchronised tempo codes + // There may only be one 'SYTC' frame in each tag + //
+ // Time stamp format $xx + // Tempo data + // Where time stamp format is: + // $01 (32-bit value) MPEG frames from beginning of file + // $02 (32-bit value) milliseconds from beginning of file + + $frame_offset = 0; + $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + $timestamp_counter = 0; + while ($frame_offset < strlen($parsedFrame['data'])) { + $parsedFrame[$timestamp_counter]['tempo'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + if ($parsedFrame[$timestamp_counter]['tempo'] == 255) { + $parsedFrame[$timestamp_counter]['tempo'] += ord(substr($parsedFrame['data'], $frame_offset++, 1)); + } + $parsedFrame[$timestamp_counter]['timestamp'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); + $frame_offset += 4; + $timestamp_counter++; + } + unset($parsedFrame['data']); + + + } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'USLT')) || // 4.8 USLT Unsynchronised lyric/text transcription + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'ULT'))) { // 4.9 ULT Unsynchronised lyric/text transcription + // There may be more than one 'Unsynchronised lyrics/text transcription' frame + // in each tag, but only one with the same language and content descriptor. + //
+ // Text encoding $xx + // Language $xx xx xx + // Content descriptor $00 (00) + // Lyrics/text + + $frame_offset = 0; + $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { + $ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; + } + $frame_language = substr($parsedFrame['data'], $frame_offset, 3); + $frame_offset += 3; + $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); + if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { + $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 + } + $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_description) === 0) { + $frame_description = ''; + } + $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); + + $parsedFrame['encodingid'] = $frame_textencoding; + $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); + + $parsedFrame['data'] = $parsedFrame['data']; + $parsedFrame['language'] = $frame_language; + $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); + $parsedFrame['description'] = $frame_description; + if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { + $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data']); + } + unset($parsedFrame['data']); + + + } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'SYLT')) || // 4.9 SYLT Synchronised lyric/text + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'SLT'))) { // 4.10 SLT Synchronised lyric/text + // There may be more than one 'SYLT' frame in each tag, + // but only one with the same language and content descriptor. + //
+ // Text encoding $xx + // Language $xx xx xx + // Time stamp format $xx + // $01 (32-bit value) MPEG frames from beginning of file + // $02 (32-bit value) milliseconds from beginning of file + // Content type $xx + // Content descriptor $00 (00) + // Terminated text to be synced (typically a syllable) + // Sync identifier (terminator to above string) $00 (00) + // Time stamp $xx (xx ...) + + $frame_offset = 0; + $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { + $ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; + } + $frame_language = substr($parsedFrame['data'], $frame_offset, 3); + $frame_offset += 3; + $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + $parsedFrame['contenttypeid'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + $parsedFrame['contenttype'] = $this->SYTLContentTypeLookup($parsedFrame['contenttypeid']); + $parsedFrame['encodingid'] = $frame_textencoding; + $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); + + $parsedFrame['language'] = $frame_language; + $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); + + $timestampindex = 0; + $frame_remainingdata = substr($parsedFrame['data'], $frame_offset); + while (strlen($frame_remainingdata)) { + $frame_offset = 0; + $frame_terminatorpos = strpos($frame_remainingdata, $this->TextEncodingTerminatorLookup($frame_textencoding)); + if ($frame_terminatorpos === false) { + $frame_remainingdata = ''; + } else { + if (ord(substr($frame_remainingdata, $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { + $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 + } + $parsedFrame['lyrics'][$timestampindex]['data'] = substr($frame_remainingdata, $frame_offset, $frame_terminatorpos - $frame_offset); + + $frame_remainingdata = substr($frame_remainingdata, $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); + if (($timestampindex == 0) && (ord($frame_remainingdata{0}) != 0)) { + // timestamp probably omitted for first data item + } else { + $parsedFrame['lyrics'][$timestampindex]['timestamp'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 4)); + $frame_remainingdata = substr($frame_remainingdata, 4); + } + $timestampindex++; + } + } + unset($parsedFrame['data']); + + + } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'COMM')) || // 4.10 COMM Comments + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'COM'))) { // 4.11 COM Comments + // There may be more than one comment frame in each tag, + // but only one with the same language and content descriptor. + //
+ // Text encoding $xx + // Language $xx xx xx + // Short content descrip. $00 (00) + // The actual text + + if (strlen($parsedFrame['data']) < 5) { + + $ThisFileInfo['warning'][] = 'Invalid data (too short) for "'.$parsedFrame['frame_name'].'" frame at offset '.$parsedFrame['dataoffset']; + + } else { + + $frame_offset = 0; + $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { + $ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; + } + $frame_language = substr($parsedFrame['data'], $frame_offset, 3); + $frame_offset += 3; + $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); + if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { + $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 + } + $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_description) === 0) { + $frame_description = ''; + } + $frame_text = (string) substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); + + $parsedFrame['encodingid'] = $frame_textencoding; + $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); + + $parsedFrame['language'] = $frame_language; + $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); + $parsedFrame['description'] = $frame_description; + $parsedFrame['data'] = $frame_text; + if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { + $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data']); + } + + } + + } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'RVA2')) { // 4.11 RVA2 Relative volume adjustment (2) (ID3v2.4+ only) + // There may be more than one 'RVA2' frame in each tag, + // but only one with the same identification string + //
+ // Identification $00 + // The 'identification' string is used to identify the situation and/or + // device where this adjustment should apply. The following is then + // repeated for every channel: + // Type of channel $xx + // Volume adjustment $xx xx + // Bits representing peak $xx + // Peak volume $xx (xx ...) + + $frame_terminatorpos = strpos($parsedFrame['data'], "\x00"); + $frame_idstring = substr($parsedFrame['data'], 0, $frame_terminatorpos); + if (ord($frame_idstring) === 0) { + $frame_idstring = ''; + } + $frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen("\x00")); + $parsedFrame['description'] = $frame_idstring; + while (strlen($frame_remainingdata)) { + $frame_offset = 0; + $frame_channeltypeid = ord(substr($frame_remainingdata, $frame_offset++, 1)); + $parsedFrame[$frame_channeltypeid]['channeltypeid'] = $frame_channeltypeid; + $parsedFrame[$frame_channeltypeid]['channeltype'] = $this->RVA2ChannelTypeLookup($frame_channeltypeid); + $parsedFrame[$frame_channeltypeid]['volumeadjust'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, $frame_offset, 2), false, true); // 16-bit signed + $frame_offset += 2; + $parsedFrame[$frame_channeltypeid]['bitspeakvolume'] = ord(substr($frame_remainingdata, $frame_offset++, 1)); + $frame_bytespeakvolume = ceil($parsedFrame[$frame_channeltypeid]['bitspeakvolume'] / 8); + $parsedFrame[$frame_channeltypeid]['peakvolume'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, $frame_offset, $frame_bytespeakvolume)); + $frame_remainingdata = substr($frame_remainingdata, $frame_offset + $frame_bytespeakvolume); + } + unset($parsedFrame['data']); + + + } elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'RVAD')) || // 4.12 RVAD Relative volume adjustment (ID3v2.3 only) + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'RVA'))) { // 4.12 RVA Relative volume adjustment (ID3v2.2 only) + // There may only be one 'RVA' frame in each tag + //
+ // ID3v2.2 => Increment/decrement %000000ba + // ID3v2.3 => Increment/decrement %00fedcba + // Bits used for volume descr. $xx + // Relative volume change, right $xx xx (xx ...) // a + // Relative volume change, left $xx xx (xx ...) // b + // Peak volume right $xx xx (xx ...) + // Peak volume left $xx xx (xx ...) + // ID3v2.3 only, optional (not present in ID3v2.2): + // Relative volume change, right back $xx xx (xx ...) // c + // Relative volume change, left back $xx xx (xx ...) // d + // Peak volume right back $xx xx (xx ...) + // Peak volume left back $xx xx (xx ...) + // ID3v2.3 only, optional (not present in ID3v2.2): + // Relative volume change, center $xx xx (xx ...) // e + // Peak volume center $xx xx (xx ...) + // ID3v2.3 only, optional (not present in ID3v2.2): + // Relative volume change, bass $xx xx (xx ...) // f + // Peak volume bass $xx xx (xx ...) + + $frame_offset = 0; + $frame_incrdecrflags = getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1)); + $parsedFrame['incdec']['right'] = (bool) substr($frame_incrdecrflags, 6, 1); + $parsedFrame['incdec']['left'] = (bool) substr($frame_incrdecrflags, 7, 1); + $parsedFrame['bitsvolume'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + $frame_bytesvolume = ceil($parsedFrame['bitsvolume'] / 8); + $parsedFrame['volumechange']['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); + if ($parsedFrame['incdec']['right'] === false) { + $parsedFrame['volumechange']['right'] *= -1; + } + $frame_offset += $frame_bytesvolume; + $parsedFrame['volumechange']['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); + if ($parsedFrame['incdec']['left'] === false) { + $parsedFrame['volumechange']['left'] *= -1; + } + $frame_offset += $frame_bytesvolume; + $parsedFrame['peakvolume']['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); + $frame_offset += $frame_bytesvolume; + $parsedFrame['peakvolume']['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); + $frame_offset += $frame_bytesvolume; + if ($id3v2_majorversion == 3) { + $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset); + if (strlen($parsedFrame['data']) > 0) { + $parsedFrame['incdec']['rightrear'] = (bool) substr($frame_incrdecrflags, 4, 1); + $parsedFrame['incdec']['leftrear'] = (bool) substr($frame_incrdecrflags, 5, 1); + $parsedFrame['volumechange']['rightrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); + if ($parsedFrame['incdec']['rightrear'] === false) { + $parsedFrame['volumechange']['rightrear'] *= -1; + } + $frame_offset += $frame_bytesvolume; + $parsedFrame['volumechange']['leftrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); + if ($parsedFrame['incdec']['leftrear'] === false) { + $parsedFrame['volumechange']['leftrear'] *= -1; + } + $frame_offset += $frame_bytesvolume; + $parsedFrame['peakvolume']['rightrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); + $frame_offset += $frame_bytesvolume; + $parsedFrame['peakvolume']['leftrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); + $frame_offset += $frame_bytesvolume; + } + $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset); + if (strlen($parsedFrame['data']) > 0) { + $parsedFrame['incdec']['center'] = (bool) substr($frame_incrdecrflags, 3, 1); + $parsedFrame['volumechange']['center'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); + if ($parsedFrame['incdec']['center'] === false) { + $parsedFrame['volumechange']['center'] *= -1; + } + $frame_offset += $frame_bytesvolume; + $parsedFrame['peakvolume']['center'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); + $frame_offset += $frame_bytesvolume; + } + $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset); + if (strlen($parsedFrame['data']) > 0) { + $parsedFrame['incdec']['bass'] = (bool) substr($frame_incrdecrflags, 2, 1); + $parsedFrame['volumechange']['bass'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); + if ($parsedFrame['incdec']['bass'] === false) { + $parsedFrame['volumechange']['bass'] *= -1; + } + $frame_offset += $frame_bytesvolume; + $parsedFrame['peakvolume']['bass'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); + $frame_offset += $frame_bytesvolume; + } + } + unset($parsedFrame['data']); + + + } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'EQU2')) { // 4.12 EQU2 Equalisation (2) (ID3v2.4+ only) + // There may be more than one 'EQU2' frame in each tag, + // but only one with the same identification string + //
+ // Interpolation method $xx + // $00 Band + // $01 Linear + // Identification $00 + // The following is then repeated for every adjustment point + // Frequency $xx xx + // Volume adjustment $xx xx + + $frame_offset = 0; + $frame_interpolationmethod = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); + $frame_idstring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_idstring) === 0) { + $frame_idstring = ''; + } + $parsedFrame['description'] = $frame_idstring; + $frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen("\x00")); + while (strlen($frame_remainingdata)) { + $frame_frequency = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 2)) / 2; + $parsedFrame['data'][$frame_frequency] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 2, 2), false, true); + $frame_remainingdata = substr($frame_remainingdata, 4); + } + $parsedFrame['interpolationmethod'] = $frame_interpolationmethod; + unset($parsedFrame['data']); + + + } elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'EQUA')) || // 4.12 EQUA Equalisation (ID3v2.3 only) + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'EQU'))) { // 4.13 EQU Equalisation (ID3v2.2 only) + // There may only be one 'EQUA' frame in each tag + //
+ // Adjustment bits $xx + // This is followed by 2 bytes + ('adjustment bits' rounded up to the + // nearest byte) for every equalisation band in the following format, + // giving a frequency range of 0 - 32767Hz: + // Increment/decrement %x (MSB of the Frequency) + // Frequency (lower 15 bits) + // Adjustment $xx (xx ...) + + $frame_offset = 0; + $parsedFrame['adjustmentbits'] = substr($parsedFrame['data'], $frame_offset++, 1); + $frame_adjustmentbytes = ceil($parsedFrame['adjustmentbits'] / 8); + + $frame_remainingdata = (string) substr($parsedFrame['data'], $frame_offset); + while (strlen($frame_remainingdata) > 0) { + $frame_frequencystr = getid3_lib::BigEndian2Bin(substr($frame_remainingdata, 0, 2)); + $frame_incdec = (bool) substr($frame_frequencystr, 0, 1); + $frame_frequency = bindec(substr($frame_frequencystr, 1, 15)); + $parsedFrame[$frame_frequency]['incdec'] = $frame_incdec; + $parsedFrame[$frame_frequency]['adjustment'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 2, $frame_adjustmentbytes)); + if ($parsedFrame[$frame_frequency]['incdec'] === false) { + $parsedFrame[$frame_frequency]['adjustment'] *= -1; + } + $frame_remainingdata = substr($frame_remainingdata, 2 + $frame_adjustmentbytes); + } + unset($parsedFrame['data']); + + + } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RVRB')) || // 4.13 RVRB Reverb + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'REV'))) { // 4.14 REV Reverb + // There may only be one 'RVRB' frame in each tag. + //
+ // Reverb left (ms) $xx xx + // Reverb right (ms) $xx xx + // Reverb bounces, left $xx + // Reverb bounces, right $xx + // Reverb feedback, left to left $xx + // Reverb feedback, left to right $xx + // Reverb feedback, right to right $xx + // Reverb feedback, right to left $xx + // Premix left to right $xx + // Premix right to left $xx + + $frame_offset = 0; + $parsedFrame['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); + $frame_offset += 2; + $parsedFrame['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); + $frame_offset += 2; + $parsedFrame['bouncesL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + $parsedFrame['bouncesR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + $parsedFrame['feedbackLL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + $parsedFrame['feedbackLR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + $parsedFrame['feedbackRR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + $parsedFrame['feedbackRL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + $parsedFrame['premixLR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + $parsedFrame['premixRL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + unset($parsedFrame['data']); + + + } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'APIC')) || // 4.14 APIC Attached picture + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'PIC'))) { // 4.15 PIC Attached picture + // There may be several pictures attached to one file, + // each in their individual 'APIC' frame, but only one + // with the same content descriptor + //
+ // Text encoding $xx + // ID3v2.3+ => MIME type $00 + // ID3v2.2 => Image format $xx xx xx + // Picture type $xx + // Description $00 (00) + // Picture data + + $frame_offset = 0; + $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { + $ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; + } + + if ($id3v2_majorversion == 2) { + $frame_imagetype = substr($parsedFrame['data'], $frame_offset, 3); + if (strtolower($frame_imagetype) == 'ima') { + // complete hack for mp3Rage (www.chaoticsoftware.com) that puts ID3v2.3-formatted + // MIME type instead of 3-char ID3v2.2-format image type (thanks xbhoffØpacbell*net) + $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); + $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_mimetype) === 0) { + $frame_mimetype = ''; + } + $frame_imagetype = strtoupper(str_replace('image/', '', strtolower($frame_mimetype))); + if ($frame_imagetype == 'JPEG') { + $frame_imagetype = 'JPG'; + } + $frame_offset = $frame_terminatorpos + strlen("\x00"); + } else { + $frame_offset += 3; + } + } + if ($id3v2_majorversion > 2) { + $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); + $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_mimetype) === 0) { + $frame_mimetype = ''; + } + $frame_offset = $frame_terminatorpos + strlen("\x00"); + } + + $frame_picturetype = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + + $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); + if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { + $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 + } + $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_description) === 0) { + $frame_description = ''; + } + $parsedFrame['encodingid'] = $frame_textencoding; + $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); + + if ($id3v2_majorversion == 2) { + $parsedFrame['imagetype'] = $frame_imagetype; + } else { + $parsedFrame['mime'] = $frame_mimetype; + } + $parsedFrame['picturetypeid'] = $frame_picturetype; + $parsedFrame['picturetype'] = $this->APICPictureTypeLookup($frame_picturetype); + $parsedFrame['description'] = $frame_description; + $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); + + $imagechunkcheck = getid3_lib::GetDataImageSize($parsedFrame['data']); + if (($imagechunkcheck[2] >= 1) && ($imagechunkcheck[2] <= 3)) { + $parsedFrame['image_mime'] = 'image/'.getid3_lib::ImageTypesLookup($imagechunkcheck[2]); + if ($imagechunkcheck[0]) { + $parsedFrame['image_width'] = $imagechunkcheck[0]; + } + if ($imagechunkcheck[1]) { + $parsedFrame['image_height'] = $imagechunkcheck[1]; + } + $parsedFrame['image_bytes'] = strlen($parsedFrame['data']); + } + + + } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'GEOB')) || // 4.15 GEOB General encapsulated object + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'GEO'))) { // 4.16 GEO General encapsulated object + // There may be more than one 'GEOB' frame in each tag, + // but only one with the same content descriptor + //
+ // Text encoding $xx + // MIME type $00 + // Filename $00 (00) + // Content description $00 (00) + // Encapsulated object + + $frame_offset = 0; + $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { + $ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; + } + $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); + $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_mimetype) === 0) { + $frame_mimetype = ''; + } + $frame_offset = $frame_terminatorpos + strlen("\x00"); + + $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); + if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { + $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 + } + $frame_filename = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_filename) === 0) { + $frame_filename = ''; + } + $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)); + + $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); + if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { + $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 + } + $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_description) === 0) { + $frame_description = ''; + } + $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)); + + $parsedFrame['objectdata'] = (string) substr($parsedFrame['data'], $frame_offset); + $parsedFrame['encodingid'] = $frame_textencoding; + $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); + + $parsedFrame['mime'] = $frame_mimetype; + $parsedFrame['filename'] = $frame_filename; + $parsedFrame['description'] = $frame_description; + unset($parsedFrame['data']); + + + } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'PCNT')) || // 4.16 PCNT Play counter + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CNT'))) { // 4.17 CNT Play counter + // There may only be one 'PCNT' frame in each tag. + // When the counter reaches all one's, one byte is inserted in + // front of the counter thus making the counter eight bits bigger + //
+ // Counter $xx xx xx xx (xx ...) + + $parsedFrame['data'] = getid3_lib::BigEndian2Int($parsedFrame['data']); + + + } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'POPM')) || // 4.17 POPM Popularimeter + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'POP'))) { // 4.18 POP Popularimeter + // There may be more than one 'POPM' frame in each tag, + // but only one with the same email address + //
+ // Email to user $00 + // Rating $xx + // Counter $xx xx xx xx (xx ...) + + $frame_offset = 0; + $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); + $frame_emailaddress = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_emailaddress) === 0) { + $frame_emailaddress = ''; + } + $frame_offset = $frame_terminatorpos + strlen("\x00"); + $frame_rating = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + $parsedFrame['data'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset)); + $parsedFrame['email'] = $frame_emailaddress; + $parsedFrame['rating'] = $frame_rating; + unset($parsedFrame['data']); + + + } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RBUF')) || // 4.18 RBUF Recommended buffer size + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'BUF'))) { // 4.19 BUF Recommended buffer size + // There may only be one 'RBUF' frame in each tag + //
+ // Buffer size $xx xx xx + // Embedded info flag %0000000x + // Offset to next tag $xx xx xx xx + + $frame_offset = 0; + $parsedFrame['buffersize'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 3)); + $frame_offset += 3; + + $frame_embeddedinfoflags = getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1)); + $parsedFrame['flags']['embededinfo'] = (bool) substr($frame_embeddedinfoflags, 7, 1); + $parsedFrame['nexttagoffset'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); + unset($parsedFrame['data']); + + + } elseif (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CRM')) { // 4.20 Encrypted meta frame (ID3v2.2 only) + // There may be more than one 'CRM' frame in a tag, + // but only one with the same 'owner identifier' + //
+ // Owner identifier $00 (00) + // Content/explanation $00 (00) + // Encrypted datablock + + $frame_offset = 0; + $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); + $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + $frame_offset = $frame_terminatorpos + strlen("\x00"); + + $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); + $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_description) === 0) { + $frame_description = ''; + } + $frame_offset = $frame_terminatorpos + strlen("\x00"); + + $parsedFrame['ownerid'] = $frame_ownerid; + $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); + $parsedFrame['description'] = $frame_description; + unset($parsedFrame['data']); + + + } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'AENC')) || // 4.19 AENC Audio encryption + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CRA'))) { // 4.21 CRA Audio encryption + // There may be more than one 'AENC' frames in a tag, + // but only one with the same 'Owner identifier' + //
+ // Owner identifier $00 + // Preview start $xx xx + // Preview length $xx xx + // Encryption info + + $frame_offset = 0; + $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); + $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_ownerid) === 0) { + $frame_ownerid == ''; + } + $frame_offset = $frame_terminatorpos + strlen("\x00"); + $parsedFrame['ownerid'] = $frame_ownerid; + $parsedFrame['previewstart'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); + $frame_offset += 2; + $parsedFrame['previewlength'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); + $frame_offset += 2; + $parsedFrame['encryptioninfo'] = (string) substr($parsedFrame['data'], $frame_offset); + unset($parsedFrame['data']); + + + } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'LINK')) || // 4.20 LINK Linked information + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'LNK'))) { // 4.22 LNK Linked information + // There may be more than one 'LINK' frame in a tag, + // but only one with the same contents + //
+ // ID3v2.3+ => Frame identifier $xx xx xx xx + // ID3v2.2 => Frame identifier $xx xx xx + // URL $00 + // ID and additional data + + $frame_offset = 0; + if ($id3v2_majorversion == 2) { + $parsedFrame['frameid'] = substr($parsedFrame['data'], $frame_offset, 3); + $frame_offset += 3; + } else { + $parsedFrame['frameid'] = substr($parsedFrame['data'], $frame_offset, 4); + $frame_offset += 4; + } + + $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); + $frame_url = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_url) === 0) { + $frame_url = ''; + } + $frame_offset = $frame_terminatorpos + strlen("\x00"); + $parsedFrame['url'] = $frame_url; + + $parsedFrame['additionaldata'] = (string) substr($parsedFrame['data'], $frame_offset); + if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) { + $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['url']); + } + unset($parsedFrame['data']); + + + } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'POSS')) { // 4.21 POSS Position synchronisation frame (ID3v2.3+ only) + // There may only be one 'POSS' frame in each tag + // + // Time stamp format $xx + // Position $xx (xx ...) + + $frame_offset = 0; + $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + $parsedFrame['position'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset)); + unset($parsedFrame['data']); + + + } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'USER')) { // 4.22 USER Terms of use (ID3v2.3+ only) + // There may be more than one 'Terms of use' frame in a tag, + // but only one with the same 'Language' + //
+ // Text encoding $xx + // Language $xx xx xx + // The actual text + + $frame_offset = 0; + $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { + $ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; + } + $frame_language = substr($parsedFrame['data'], $frame_offset, 3); + $frame_offset += 3; + $parsedFrame['language'] = $frame_language; + $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); + $parsedFrame['encodingid'] = $frame_textencoding; + $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); + + $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); + if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { + $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data']); + } + unset($parsedFrame['data']); + + + } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'OWNE')) { // 4.23 OWNE Ownership frame (ID3v2.3+ only) + // There may only be one 'OWNE' frame in a tag + //
+ // Text encoding $xx + // Price paid $00 + // Date of purch. + // Seller + + $frame_offset = 0; + $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { + $ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; + } + $parsedFrame['encodingid'] = $frame_textencoding; + $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); + + $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); + $frame_pricepaid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + $frame_offset = $frame_terminatorpos + strlen("\x00"); + + $parsedFrame['pricepaid']['currencyid'] = substr($frame_pricepaid, 0, 3); + $parsedFrame['pricepaid']['currency'] = $this->LookupCurrencyUnits($parsedFrame['pricepaid']['currencyid']); + $parsedFrame['pricepaid']['value'] = substr($frame_pricepaid, 3); + + $parsedFrame['purchasedate'] = substr($parsedFrame['data'], $frame_offset, 8); + if (!$this->IsValidDateStampString($parsedFrame['purchasedate'])) { + $parsedFrame['purchasedateunix'] = mktime (0, 0, 0, substr($parsedFrame['purchasedate'], 4, 2), substr($parsedFrame['purchasedate'], 6, 2), substr($parsedFrame['purchasedate'], 0, 4)); + } + $frame_offset += 8; + + $parsedFrame['seller'] = (string) substr($parsedFrame['data'], $frame_offset); + unset($parsedFrame['data']); + + + } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'COMR')) { // 4.24 COMR Commercial frame (ID3v2.3+ only) + // There may be more than one 'commercial frame' in a tag, + // but no two may be identical + //
+ // Text encoding $xx + // Price string $00 + // Valid until + // Contact URL $00 + // Received as $xx + // Name of seller $00 (00) + // Description $00 (00) + // Picture MIME type $00 + // Seller logo + + $frame_offset = 0; + $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { + $ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; + } + + $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); + $frame_pricestring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + $frame_offset = $frame_terminatorpos + strlen("\x00"); + $frame_rawpricearray = explode('/', $frame_pricestring); + foreach ($frame_rawpricearray as $key => $val) { + $frame_currencyid = substr($val, 0, 3); + $parsedFrame['price'][$frame_currencyid]['currency'] = $this->LookupCurrencyUnits($frame_currencyid); + $parsedFrame['price'][$frame_currencyid]['value'] = substr($val, 3); + } + + $frame_datestring = substr($parsedFrame['data'], $frame_offset, 8); + $frame_offset += 8; + + $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); + $frame_contacturl = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + $frame_offset = $frame_terminatorpos + strlen("\x00"); + + $frame_receivedasid = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + + $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); + if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { + $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 + } + $frame_sellername = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_sellername) === 0) { + $frame_sellername = ''; + } + $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)); + + $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); + if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { + $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 + } + $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_description) === 0) { + $frame_description = ''; + } + $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)); + + $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); + $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + $frame_offset = $frame_terminatorpos + strlen("\x00"); + + $frame_sellerlogo = substr($parsedFrame['data'], $frame_offset); + + $parsedFrame['encodingid'] = $frame_textencoding; + $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); + + $parsedFrame['pricevaliduntil'] = $frame_datestring; + $parsedFrame['contacturl'] = $frame_contacturl; + $parsedFrame['receivedasid'] = $frame_receivedasid; + $parsedFrame['receivedas'] = $this->COMRReceivedAsLookup($frame_receivedasid); + $parsedFrame['sellername'] = $frame_sellername; + $parsedFrame['description'] = $frame_description; + $parsedFrame['mime'] = $frame_mimetype; + $parsedFrame['logo'] = $frame_sellerlogo; + unset($parsedFrame['data']); + + + } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'ENCR')) { // 4.25 ENCR Encryption method registration (ID3v2.3+ only) + // There may be several 'ENCR' frames in a tag, + // but only one containing the same symbol + // and only one containing the same owner identifier + //
+ // Owner identifier $00 + // Method symbol $xx + // Encryption data + + $frame_offset = 0; + $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); + $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_ownerid) === 0) { + $frame_ownerid = ''; + } + $frame_offset = $frame_terminatorpos + strlen("\x00"); + + $parsedFrame['ownerid'] = $frame_ownerid; + $parsedFrame['methodsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); + + + } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'GRID')) { // 4.26 GRID Group identification registration (ID3v2.3+ only) + + // There may be several 'GRID' frames in a tag, + // but only one containing the same symbol + // and only one containing the same owner identifier + //
+ // Owner identifier $00 + // Group symbol $xx + // Group dependent data + + $frame_offset = 0; + $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); + $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_ownerid) === 0) { + $frame_ownerid = ''; + } + $frame_offset = $frame_terminatorpos + strlen("\x00"); + + $parsedFrame['ownerid'] = $frame_ownerid; + $parsedFrame['groupsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); + + + } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'PRIV')) { // 4.27 PRIV Private frame (ID3v2.3+ only) + // The tag may contain more than one 'PRIV' frame + // but only with different contents + //
+ // Owner identifier $00 + // The private data + + $frame_offset = 0; + $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); + $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_ownerid) === 0) { + $frame_ownerid = ''; + } + $frame_offset = $frame_terminatorpos + strlen("\x00"); + + $parsedFrame['ownerid'] = $frame_ownerid; + $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); + + + } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'SIGN')) { // 4.28 SIGN Signature frame (ID3v2.4+ only) + // There may be more than one 'signature frame' in a tag, + // but no two may be identical + //
+ // Group symbol $xx + // Signature + + $frame_offset = 0; + $parsedFrame['groupsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); + + + } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'SEEK')) { // 4.29 SEEK Seek frame (ID3v2.4+ only) + // There may only be one 'seek frame' in a tag + //
+ // Minimum offset to next tag $xx xx xx xx + + $frame_offset = 0; + $parsedFrame['data'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); + + + } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'ASPI')) { // 4.30 ASPI Audio seek point index (ID3v2.4+ only) + // There may only be one 'audio seek point index' frame in a tag + //
+ // Indexed data start (S) $xx xx xx xx + // Indexed data length (L) $xx xx xx xx + // Number of index points (N) $xx xx + // Bits per index point (b) $xx + // Then for every index point the following data is included: + // Fraction at index (Fi) $xx (xx) + + $frame_offset = 0; + $parsedFrame['datastart'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); + $frame_offset += 4; + $parsedFrame['indexeddatalength'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); + $frame_offset += 4; + $parsedFrame['indexpoints'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); + $frame_offset += 2; + $parsedFrame['bitsperpoint'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + $frame_bytesperpoint = ceil($parsedFrame['bitsperpoint'] / 8); + for ($i = 0; $i < $frame_indexpoints; $i++) { + $parsedFrame['indexes'][$i] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesperpoint)); + $frame_offset += $frame_bytesperpoint; + } + unset($parsedFrame['data']); + + } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RGAD')) { // Replay Gain Adjustment + // http://privatewww.essex.ac.uk/~djmrob/replaygain/file_format_id3v2.html + // There may only be one 'RGAD' frame in a tag + //
+ // Peak Amplitude $xx $xx $xx $xx + // Radio Replay Gain Adjustment %aaabbbcd %dddddddd + // Audiophile Replay Gain Adjustment %aaabbbcd %dddddddd + // a - name code + // b - originator code + // c - sign bit + // d - replay gain adjustment + + $frame_offset = 0; + $parsedFrame['peakamplitude'] = getid3_lib::BigEndian2Float(substr($parsedFrame['data'], $frame_offset, 4)); + $frame_offset += 4; + $rg_track_adjustment = getid3_lib::Dec2Bin(substr($parsedFrame['data'], $frame_offset, 2)); + $frame_offset += 2; + $rg_album_adjustment = getid3_lib::Dec2Bin(substr($parsedFrame['data'], $frame_offset, 2)); + $frame_offset += 2; + $parsedFrame['raw']['track']['name'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 0, 3)); + $parsedFrame['raw']['track']['originator'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 3, 3)); + $parsedFrame['raw']['track']['signbit'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 6, 1)); + $parsedFrame['raw']['track']['adjustment'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 7, 9)); + $parsedFrame['raw']['album']['name'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 0, 3)); + $parsedFrame['raw']['album']['originator'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 3, 3)); + $parsedFrame['raw']['album']['signbit'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 6, 1)); + $parsedFrame['raw']['album']['adjustment'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 7, 9)); + $parsedFrame['track']['name'] = getid3_lib::RGADnameLookup($parsedFrame['raw']['track']['name']); + $parsedFrame['track']['originator'] = getid3_lib::RGADoriginatorLookup($parsedFrame['raw']['track']['originator']); + $parsedFrame['track']['adjustment'] = getid3_lib::RGADadjustmentLookup($parsedFrame['raw']['track']['adjustment'], $parsedFrame['raw']['track']['signbit']); + $parsedFrame['album']['name'] = getid3_lib::RGADnameLookup($parsedFrame['raw']['album']['name']); + $parsedFrame['album']['originator'] = getid3_lib::RGADoriginatorLookup($parsedFrame['raw']['album']['originator']); + $parsedFrame['album']['adjustment'] = getid3_lib::RGADadjustmentLookup($parsedFrame['raw']['album']['adjustment'], $parsedFrame['raw']['album']['signbit']); + + $ThisFileInfo['replay_gain']['track']['peak'] = $parsedFrame['peakamplitude']; + $ThisFileInfo['replay_gain']['track']['originator'] = $parsedFrame['track']['originator']; + $ThisFileInfo['replay_gain']['track']['adjustment'] = $parsedFrame['track']['adjustment']; + $ThisFileInfo['replay_gain']['album']['originator'] = $parsedFrame['album']['originator']; + $ThisFileInfo['replay_gain']['album']['adjustment'] = $parsedFrame['album']['adjustment']; + + unset($parsedFrame['data']); + + } + + return true; + } + + + function DeUnsynchronise($data) { + return str_replace("\xFF\x00", "\xFF", $data); + } + + function LookupCurrencyUnits($currencyid) { + + $begin = __LINE__; + + /** This is not a comment! + + + AED Dirhams + AFA Afghanis + ALL Leke + AMD Drams + ANG Guilders + AOA Kwanza + ARS Pesos + ATS Schillings + AUD Dollars + AWG Guilders + AZM Manats + BAM Convertible Marka + BBD Dollars + BDT Taka + BEF Francs + BGL Leva + BHD Dinars + BIF Francs + BMD Dollars + BND Dollars + BOB Bolivianos + BRL Brazil Real + BSD Dollars + BTN Ngultrum + BWP Pulas + BYR Rubles + BZD Dollars + CAD Dollars + CDF Congolese Francs + CHF Francs + CLP Pesos + CNY Yuan Renminbi + COP Pesos + CRC Colones + CUP Pesos + CVE Escudos + CYP Pounds + CZK Koruny + DEM Deutsche Marks + DJF Francs + DKK Kroner + DOP Pesos + DZD Algeria Dinars + EEK Krooni + EGP Pounds + ERN Nakfa + ESP Pesetas + ETB Birr + EUR Euro + FIM Markkaa + FJD Dollars + FKP Pounds + FRF Francs + GBP Pounds + GEL Lari + GGP Pounds + GHC Cedis + GIP Pounds + GMD Dalasi + GNF Francs + GRD Drachmae + GTQ Quetzales + GYD Dollars + HKD Dollars + HNL Lempiras + HRK Kuna + HTG Gourdes + HUF Forints + IDR Rupiahs + IEP Pounds + ILS New Shekels + IMP Pounds + INR Rupees + IQD Dinars + IRR Rials + ISK Kronur + ITL Lire + JEP Pounds + JMD Dollars + JOD Dinars + JPY Yen + KES Shillings + KGS Soms + KHR Riels + KMF Francs + KPW Won + KWD Dinars + KYD Dollars + KZT Tenge + LAK Kips + LBP Pounds + LKR Rupees + LRD Dollars + LSL Maloti + LTL Litai + LUF Francs + LVL Lati + LYD Dinars + MAD Dirhams + MDL Lei + MGF Malagasy Francs + MKD Denars + MMK Kyats + MNT Tugriks + MOP Patacas + MRO Ouguiyas + MTL Liri + MUR Rupees + MVR Rufiyaa + MWK Kwachas + MXN Pesos + MYR Ringgits + MZM Meticais + NAD Dollars + NGN Nairas + NIO Gold Cordobas + NLG Guilders + NOK Krone + NPR Nepal Rupees + NZD Dollars + OMR Rials + PAB Balboa + PEN Nuevos Soles + PGK Kina + PHP Pesos + PKR Rupees + PLN Zlotych + PTE Escudos + PYG Guarani + QAR Rials + ROL Lei + RUR Rubles + RWF Rwanda Francs + SAR Riyals + SBD Dollars + SCR Rupees + SDD Dinars + SEK Kronor + SGD Dollars + SHP Pounds + SIT Tolars + SKK Koruny + SLL Leones + SOS Shillings + SPL Luigini + SRG Guilders + STD Dobras + SVC Colones + SYP Pounds + SZL Emalangeni + THB Baht + TJR Rubles + TMM Manats + TND Dinars + TOP Pa'anga + TRL Liras + TTD Dollars + TVD Tuvalu Dollars + TWD New Dollars + TZS Shillings + UAH Hryvnia + UGX Shillings + USD Dollars + UYU Pesos + UZS Sums + VAL Lire + VEB Bolivares + VND Dong + VUV Vatu + WST Tala + XAF Francs + XAG Ounces + XAU Ounces + XCD Dollars + XDR Special Drawing Rights + XPD Ounces + XPF Francs + XPT Ounces + YER Rials + YUM New Dinars + ZAR Rand + ZMK Kwacha + ZWD Zimbabwe Dollars + + */ + + return getid3_lib::EmbeddedLookup($currencyid, $begin, __LINE__, __FILE__, 'id3v2-currency-units'); + } + + + function LookupCurrencyCountry($currencyid) { + + $begin = __LINE__; + + /** This is not a comment! + + AED United Arab Emirates + AFA Afghanistan + ALL Albania + AMD Armenia + ANG Netherlands Antilles + AOA Angola + ARS Argentina + ATS Austria + AUD Australia + AWG Aruba + AZM Azerbaijan + BAM Bosnia and Herzegovina + BBD Barbados + BDT Bangladesh + BEF Belgium + BGL Bulgaria + BHD Bahrain + BIF Burundi + BMD Bermuda + BND Brunei Darussalam + BOB Bolivia + BRL Brazil + BSD Bahamas + BTN Bhutan + BWP Botswana + BYR Belarus + BZD Belize + CAD Canada + CDF Congo/Kinshasa + CHF Switzerland + CLP Chile + CNY China + COP Colombia + CRC Costa Rica + CUP Cuba + CVE Cape Verde + CYP Cyprus + CZK Czech Republic + DEM Germany + DJF Djibouti + DKK Denmark + DOP Dominican Republic + DZD Algeria + EEK Estonia + EGP Egypt + ERN Eritrea + ESP Spain + ETB Ethiopia + EUR Euro Member Countries + FIM Finland + FJD Fiji + FKP Falkland Islands (Malvinas) + FRF France + GBP United Kingdom + GEL Georgia + GGP Guernsey + GHC Ghana + GIP Gibraltar + GMD Gambia + GNF Guinea + GRD Greece + GTQ Guatemala + GYD Guyana + HKD Hong Kong + HNL Honduras + HRK Croatia + HTG Haiti + HUF Hungary + IDR Indonesia + IEP Ireland (Eire) + ILS Israel + IMP Isle of Man + INR India + IQD Iraq + IRR Iran + ISK Iceland + ITL Italy + JEP Jersey + JMD Jamaica + JOD Jordan + JPY Japan + KES Kenya + KGS Kyrgyzstan + KHR Cambodia + KMF Comoros + KPW Korea + KWD Kuwait + KYD Cayman Islands + KZT Kazakstan + LAK Laos + LBP Lebanon + LKR Sri Lanka + LRD Liberia + LSL Lesotho + LTL Lithuania + LUF Luxembourg + LVL Latvia + LYD Libya + MAD Morocco + MDL Moldova + MGF Madagascar + MKD Macedonia + MMK Myanmar (Burma) + MNT Mongolia + MOP Macau + MRO Mauritania + MTL Malta + MUR Mauritius + MVR Maldives (Maldive Islands) + MWK Malawi + MXN Mexico + MYR Malaysia + MZM Mozambique + NAD Namibia + NGN Nigeria + NIO Nicaragua + NLG Netherlands (Holland) + NOK Norway + NPR Nepal + NZD New Zealand + OMR Oman + PAB Panama + PEN Peru + PGK Papua New Guinea + PHP Philippines + PKR Pakistan + PLN Poland + PTE Portugal + PYG Paraguay + QAR Qatar + ROL Romania + RUR Russia + RWF Rwanda + SAR Saudi Arabia + SBD Solomon Islands + SCR Seychelles + SDD Sudan + SEK Sweden + SGD Singapore + SHP Saint Helena + SIT Slovenia + SKK Slovakia + SLL Sierra Leone + SOS Somalia + SPL Seborga + SRG Suriname + STD São Tome and Principe + SVC El Salvador + SYP Syria + SZL Swaziland + THB Thailand + TJR Tajikistan + TMM Turkmenistan + TND Tunisia + TOP Tonga + TRL Turkey + TTD Trinidad and Tobago + TVD Tuvalu + TWD Taiwan + TZS Tanzania + UAH Ukraine + UGX Uganda + USD United States of America + UYU Uruguay + UZS Uzbekistan + VAL Vatican City + VEB Venezuela + VND Viet Nam + VUV Vanuatu + WST Samoa + XAF Communauté Financière Africaine + XAG Silver + XAU Gold + XCD East Caribbean + XDR International Monetary Fund + XPD Palladium + XPF Comptoirs Français du Pacifique + XPT Platinum + YER Yemen + YUM Yugoslavia + ZAR South Africa + ZMK Zambia + ZWD Zimbabwe + + */ + + return getid3_lib::EmbeddedLookup($currencyid, $begin, __LINE__, __FILE__, 'id3v2-currency-country'); + } + + + + function LanguageLookup($languagecode, $casesensitive=false) { + + if (!$casesensitive) { + $languagecode = strtolower($languagecode); + } + + // http://www.id3.org/id3v2.4.0-structure.txt + // [4. ID3v2 frame overview] + // The three byte language field, present in several frames, is used to + // describe the language of the frame's content, according to ISO-639-2 + // [ISO-639-2]. The language should be represented in lower case. If the + // language is not known the string "XXX" should be used. + + + // ISO 639-2 - http://www.id3.org/iso639-2.html + + $begin = __LINE__; + + /** This is not a comment! + + XXX unknown + xxx unknown + aar Afar + abk Abkhazian + ace Achinese + ach Acoli + ada Adangme + afa Afro-Asiatic (Other) + afh Afrihili + afr Afrikaans + aka Akan + akk Akkadian + alb Albanian + ale Aleut + alg Algonquian Languages + amh Amharic + ang English, Old (ca. 450-1100) + apa Apache Languages + ara Arabic + arc Aramaic + arm Armenian + arn Araucanian + arp Arapaho + art Artificial (Other) + arw Arawak + asm Assamese + ath Athapascan Languages + ava Avaric + ave Avestan + awa Awadhi + aym Aymara + aze Azerbaijani + bad Banda + bai Bamileke Languages + bak Bashkir + bal Baluchi + bam Bambara + ban Balinese + baq Basque + bas Basa + bat Baltic (Other) + bej Beja + bel Byelorussian + bem Bemba + ben Bengali + ber Berber (Other) + bho Bhojpuri + bih Bihari + bik Bikol + bin Bini + bis Bislama + bla Siksika + bnt Bantu (Other) + bod Tibetan + bra Braj + bre Breton + bua Buriat + bug Buginese + bul Bulgarian + bur Burmese + cad Caddo + cai Central American Indian (Other) + car Carib + cat Catalan + cau Caucasian (Other) + ceb Cebuano + cel Celtic (Other) + ces Czech + cha Chamorro + chb Chibcha + che Chechen + chg Chagatai + chi Chinese + chm Mari + chn Chinook jargon + cho Choctaw + chr Cherokee + chu Church Slavic + chv Chuvash + chy Cheyenne + cop Coptic + cor Cornish + cos Corsican + cpe Creoles and Pidgins, English-based (Other) + cpf Creoles and Pidgins, French-based (Other) + cpp Creoles and Pidgins, Portuguese-based (Other) + cre Cree + crp Creoles and Pidgins (Other) + cus Cushitic (Other) + cym Welsh + cze Czech + dak Dakota + dan Danish + del Delaware + deu German + din Dinka + div Divehi + doi Dogri + dra Dravidian (Other) + dua Duala + dum Dutch, Middle (ca. 1050-1350) + dut Dutch + dyu Dyula + dzo Dzongkha + efi Efik + egy Egyptian (Ancient) + eka Ekajuk + ell Greek, Modern (1453-) + elx Elamite + eng English + enm English, Middle (ca. 1100-1500) + epo Esperanto + esk Eskimo (Other) + esl Spanish + est Estonian + eus Basque + ewe Ewe + ewo Ewondo + fan Fang + fao Faroese + fas Persian + fat Fanti + fij Fijian + fin Finnish + fiu Finno-Ugrian (Other) + fon Fon + fra French + fre French + frm French, Middle (ca. 1400-1600) + fro French, Old (842- ca. 1400) + fry Frisian + ful Fulah + gaa Ga + gae Gaelic (Scots) + gai Irish + gay Gayo + gdh Gaelic (Scots) + gem Germanic (Other) + geo Georgian + ger German + gez Geez + gil Gilbertese + glg Gallegan + gmh German, Middle High (ca. 1050-1500) + goh German, Old High (ca. 750-1050) + gon Gondi + got Gothic + grb Grebo + grc Greek, Ancient (to 1453) + gre Greek, Modern (1453-) + grn Guarani + guj Gujarati + hai Haida + hau Hausa + haw Hawaiian + heb Hebrew + her Herero + hil Hiligaynon + him Himachali + hin Hindi + hmo Hiri Motu + hun Hungarian + hup Hupa + hye Armenian + iba Iban + ibo Igbo + ice Icelandic + ijo Ijo + iku Inuktitut + ilo Iloko + ina Interlingua (International Auxiliary language Association) + inc Indic (Other) + ind Indonesian + ine Indo-European (Other) + ine Interlingue + ipk Inupiak + ira Iranian (Other) + iri Irish + iro Iroquoian uages + isl Icelandic + ita Italian + jav Javanese + jaw Javanese + jpn Japanese + jpr Judeo-Persian + jrb Judeo-Arabic + kaa Kara-Kalpak + kab Kabyle + kac Kachin + kal Greenlandic + kam Kamba + kan Kannada + kar Karen + kas Kashmiri + kat Georgian + kau Kanuri + kaw Kawi + kaz Kazakh + kha Khasi + khi Khoisan (Other) + khm Khmer + kho Khotanese + kik Kikuyu + kin Kinyarwanda + kir Kirghiz + kok Konkani + kom Komi + kon Kongo + kor Korean + kpe Kpelle + kro Kru + kru Kurukh + kua Kuanyama + kum Kumyk + kur Kurdish + kus Kusaie + kut Kutenai + lad Ladino + lah Lahnda + lam Lamba + lao Lao + lat Latin + lav Latvian + lez Lezghian + lin Lingala + lit Lithuanian + lol Mongo + loz Lozi + ltz Letzeburgesch + lub Luba-Katanga + lug Ganda + lui Luiseno + lun Lunda + luo Luo (Kenya and Tanzania) + mac Macedonian + mad Madurese + mag Magahi + mah Marshall + mai Maithili + mak Macedonian + mak Makasar + mal Malayalam + man Mandingo + mao Maori + map Austronesian (Other) + mar Marathi + mas Masai + max Manx + may Malay + men Mende + mga Irish, Middle (900 - 1200) + mic Micmac + min Minangkabau + mis Miscellaneous (Other) + mkh Mon-Kmer (Other) + mlg Malagasy + mlt Maltese + mni Manipuri + mno Manobo Languages + moh Mohawk + mol Moldavian + mon Mongolian + mos Mossi + mri Maori + msa Malay + mul Multiple Languages + mun Munda Languages + mus Creek + mwr Marwari + mya Burmese + myn Mayan Languages + nah Aztec + nai North American Indian (Other) + nau Nauru + nav Navajo + nbl Ndebele, South + nde Ndebele, North + ndo Ndongo + nep Nepali + new Newari + nic Niger-Kordofanian (Other) + niu Niuean + nla Dutch + nno Norwegian (Nynorsk) + non Norse, Old + nor Norwegian + nso Sotho, Northern + nub Nubian Languages + nya Nyanja + nym Nyamwezi + nyn Nyankole + nyo Nyoro + nzi Nzima + oci Langue d'Oc (post 1500) + oji Ojibwa + ori Oriya + orm Oromo + osa Osage + oss Ossetic + ota Turkish, Ottoman (1500 - 1928) + oto Otomian Languages + paa Papuan-Australian (Other) + pag Pangasinan + pal Pahlavi + pam Pampanga + pan Panjabi + pap Papiamento + pau Palauan + peo Persian, Old (ca 600 - 400 B.C.) + per Persian + phn Phoenician + pli Pali + pol Polish + pon Ponape + por Portuguese + pra Prakrit uages + pro Provencal, Old (to 1500) + pus Pushto + que Quechua + raj Rajasthani + rar Rarotongan + roa Romance (Other) + roh Rhaeto-Romance + rom Romany + ron Romanian + rum Romanian + run Rundi + rus Russian + sad Sandawe + sag Sango + sah Yakut + sai South American Indian (Other) + sal Salishan Languages + sam Samaritan Aramaic + san Sanskrit + sco Scots + scr Serbo-Croatian + sel Selkup + sem Semitic (Other) + sga Irish, Old (to 900) + shn Shan + sid Sidamo + sin Singhalese + sio Siouan Languages + sit Sino-Tibetan (Other) + sla Slavic (Other) + slk Slovak + slo Slovak + slv Slovenian + smi Sami Languages + smo Samoan + sna Shona + snd Sindhi + sog Sogdian + som Somali + son Songhai + sot Sotho, Southern + spa Spanish + sqi Albanian + srd Sardinian + srr Serer + ssa Nilo-Saharan (Other) + ssw Siswant + ssw Swazi + suk Sukuma + sun Sudanese + sus Susu + sux Sumerian + sve Swedish + swa Swahili + swe Swedish + syr Syriac + tah Tahitian + tam Tamil + tat Tatar + tel Telugu + tem Timne + ter Tereno + tgk Tajik + tgl Tagalog + tha Thai + tib Tibetan + tig Tigre + tir Tigrinya + tiv Tivi + tli Tlingit + tmh Tamashek + tog Tonga (Nyasa) + ton Tonga (Tonga Islands) + tru Truk + tsi Tsimshian + tsn Tswana + tso Tsonga + tuk Turkmen + tum Tumbuka + tur Turkish + tut Altaic (Other) + twi Twi + tyv Tuvinian + uga Ugaritic + uig Uighur + ukr Ukrainian + umb Umbundu + und Undetermined + urd Urdu + uzb Uzbek + vai Vai + ven Venda + vie Vietnamese + vol Volapük + vot Votic + wak Wakashan Languages + wal Walamo + war Waray + was Washo + wel Welsh + wen Sorbian Languages + wol Wolof + xho Xhosa + yao Yao + yap Yap + yid Yiddish + yor Yoruba + zap Zapotec + zen Zenaga + zha Zhuang + zho Chinese + zul Zulu + zun Zuni + + */ + + return getid3_lib::EmbeddedLookup($languagecode, $begin, __LINE__, __FILE__, 'id3v2-languagecode'); + } + + + function ETCOEventLookup($index) { + if (($index >= 0x17) && ($index <= 0xDF)) { + return 'reserved for future use'; + } + if (($index >= 0xE0) && ($index <= 0xEF)) { + return 'not predefined synch 0-F'; + } + if (($index >= 0xF0) && ($index <= 0xFC)) { + return 'reserved for future use'; + } + + static $EventLookup = array( + 0x00 => 'padding (has no meaning)', + 0x01 => 'end of initial silence', + 0x02 => 'intro start', + 0x03 => 'main part start', + 0x04 => 'outro start', + 0x05 => 'outro end', + 0x06 => 'verse start', + 0x07 => 'refrain start', + 0x08 => 'interlude start', + 0x09 => 'theme start', + 0x0A => 'variation start', + 0x0B => 'key change', + 0x0C => 'time change', + 0x0D => 'momentary unwanted noise (Snap, Crackle & Pop)', + 0x0E => 'sustained noise', + 0x0F => 'sustained noise end', + 0x10 => 'intro end', + 0x11 => 'main part end', + 0x12 => 'verse end', + 0x13 => 'refrain end', + 0x14 => 'theme end', + 0x15 => 'profanity', + 0x16 => 'profanity end', + 0xFD => 'audio end (start of silence)', + 0xFE => 'audio file ends', + 0xFF => 'one more byte of events follows' + ); + + return (isset($EventLookup[$index]) ? $EventLookup[$index] : ''); + } + + function SYTLContentTypeLookup($index) { + static $SYTLContentTypeLookup = array( + 0x00 => 'other', + 0x01 => 'lyrics', + 0x02 => 'text transcription', + 0x03 => 'movement/part name', // (e.g. 'Adagio') + 0x04 => 'events', // (e.g. 'Don Quijote enters the stage') + 0x05 => 'chord', // (e.g. 'Bb F Fsus') + 0x06 => 'trivia/\'pop up\' information', + 0x07 => 'URLs to webpages', + 0x08 => 'URLs to images' + ); + + return (isset($SYTLContentTypeLookup[$index]) ? $SYTLContentTypeLookup[$index] : ''); + } + + function APICPictureTypeLookup($index, $returnarray=false) { + static $APICPictureTypeLookup = array( + 0x00 => 'Other', + 0x01 => '32x32 pixels \'file icon\' (PNG only)', + 0x02 => 'Other file icon', + 0x03 => 'Cover (front)', + 0x04 => 'Cover (back)', + 0x05 => 'Leaflet page', + 0x06 => 'Media (e.g. label side of CD)', + 0x07 => 'Lead artist/lead performer/soloist', + 0x08 => 'Artist/performer', + 0x09 => 'Conductor', + 0x0A => 'Band/Orchestra', + 0x0B => 'Composer', + 0x0C => 'Lyricist/text writer', + 0x0D => 'Recording Location', + 0x0E => 'During recording', + 0x0F => 'During performance', + 0x10 => 'Movie/video screen capture', + 0x11 => 'A bright coloured fish', + 0x12 => 'Illustration', + 0x13 => 'Band/artist logotype', + 0x14 => 'Publisher/Studio logotype' + ); + if ($returnarray) { + return $APICPictureTypeLookup; + } + return (isset($APICPictureTypeLookup[$index]) ? $APICPictureTypeLookup[$index] : ''); + } + + function COMRReceivedAsLookup($index) { + static $COMRReceivedAsLookup = array( + 0x00 => 'Other', + 0x01 => 'Standard CD album with other songs', + 0x02 => 'Compressed audio on CD', + 0x03 => 'File over the Internet', + 0x04 => 'Stream over the Internet', + 0x05 => 'As note sheets', + 0x06 => 'As note sheets in a book with other sheets', + 0x07 => 'Music on other media', + 0x08 => 'Non-musical merchandise' + ); + + return (isset($COMRReceivedAsLookup[$index]) ? $COMRReceivedAsLookup[$index] : ''); + } + + function RVA2ChannelTypeLookup($index) { + static $RVA2ChannelTypeLookup = array( + 0x00 => 'Other', + 0x01 => 'Master volume', + 0x02 => 'Front right', + 0x03 => 'Front left', + 0x04 => 'Back right', + 0x05 => 'Back left', + 0x06 => 'Front centre', + 0x07 => 'Back centre', + 0x08 => 'Subwoofer' + ); + + return (isset($RVA2ChannelTypeLookup[$index]) ? $RVA2ChannelTypeLookup[$index] : ''); + } + + function FrameNameLongLookup($framename) { + + $begin = __LINE__; + + /** This is not a comment! + + AENC Audio encryption + APIC Attached picture + ASPI Audio seek point index + BUF Recommended buffer size + CNT Play counter + COM Comments + COMM Comments + COMR Commercial frame + CRA Audio encryption + CRM Encrypted meta frame + ENCR Encryption method registration + EQU Equalisation + EQU2 Equalisation (2) + EQUA Equalisation + ETC Event timing codes + ETCO Event timing codes + GEO General encapsulated object + GEOB General encapsulated object + GRID Group identification registration + IPL Involved people list + IPLS Involved people list + LINK Linked information + LNK Linked information + MCDI Music CD identifier + MCI Music CD Identifier + MLL MPEG location lookup table + MLLT MPEG location lookup table + OWNE Ownership frame + PCNT Play counter + PIC Attached picture + POP Popularimeter + POPM Popularimeter + POSS Position synchronisation frame + PRIV Private frame + RBUF Recommended buffer size + REV Reverb + RVA Relative volume adjustment + RVA2 Relative volume adjustment (2) + RVAD Relative volume adjustment + RVRB Reverb + SEEK Seek frame + SIGN Signature frame + SLT Synchronised lyric/text + STC Synced tempo codes + SYLT Synchronised lyric/text + SYTC Synchronised tempo codes + TAL Album/Movie/Show title + TALB Album/Movie/Show title + TBP BPM (Beats Per Minute) + TBPM BPM (beats per minute) + TCM Composer + TCO Content type + TCOM Composer + TCON Content type + TCOP Copyright message + TCR Copyright message + TDA Date + TDAT Date + TDEN Encoding time + TDLY Playlist delay + TDOR Original release time + TDRC Recording time + TDRL Release time + TDTG Tagging time + TDY Playlist delay + TEN Encoded by + TENC Encoded by + TEXT Lyricist/Text writer + TFLT File type + TFT File type + TIM Time + TIME Time + TIPL Involved people list + TIT1 Content group description + TIT2 Title/songname/content description + TIT3 Subtitle/Description refinement + TKE Initial key + TKEY Initial key + TLA Language(s) + TLAN Language(s) + TLE Length + TLEN Length + TMCL Musician credits list + TMED Media type + TMOO Mood + TMT Media type + TOA Original artist(s)/performer(s) + TOAL Original album/movie/show title + TOF Original filename + TOFN Original filename + TOL Original Lyricist(s)/text writer(s) + TOLY Original lyricist(s)/text writer(s) + TOPE Original artist(s)/performer(s) + TOR Original release year + TORY Original release year + TOT Original album/Movie/Show title + TOWN File owner/licensee + TP1 Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group + TP2 Band/Orchestra/Accompaniment + TP3 Conductor/Performer refinement + TP4 Interpreted, remixed, or otherwise modified by + TPA Part of a set + TPB Publisher + TPE1 Lead performer(s)/Soloist(s) + TPE2 Band/orchestra/accompaniment + TPE3 Conductor/performer refinement + TPE4 Interpreted, remixed, or otherwise modified by + TPOS Part of a set + TPRO Produced notice + TPUB Publisher + TRC ISRC (International Standard Recording Code) + TRCK Track number/Position in set + TRD Recording dates + TRDA Recording dates + TRK Track number/Position in set + TRSN Internet radio station name + TRSO Internet radio station owner + TSI Size + TSIZ Size + TSOA Album sort order + TSOP Performer sort order + TSOT Title sort order + TSRC ISRC (international standard recording code) + TSS Software/hardware and settings used for encoding + TSSE Software/Hardware and settings used for encoding + TSST Set subtitle + TT1 Content group description + TT2 Title/Songname/Content description + TT3 Subtitle/Description refinement + TXT Lyricist/text writer + TXX User defined text information frame + TXXX User defined text information frame + TYE Year + TYER Year + UFI Unique file identifier + UFID Unique file identifier + ULT Unsychronised lyric/text transcription + USER Terms of use + USLT Unsynchronised lyric/text transcription + WAF Official audio file webpage + WAR Official artist/performer webpage + WAS Official audio source webpage + WCM Commercial information + WCOM Commercial information + WCOP Copyright/Legal information + WCP Copyright/Legal information + WOAF Official audio file webpage + WOAR Official artist/performer webpage + WOAS Official audio source webpage + WORS Official Internet radio station homepage + WPAY Payment + WPB Publishers official webpage + WPUB Publishers official webpage + WXX User defined URL link frame + WXXX User defined URL link frame + TFEA Featured Artist + TSTU Recording Studio + rgad Replay Gain Adjustment + + */ + + return getid3_lib::EmbeddedLookup($framename, $begin, __LINE__, __FILE__, 'id3v2-framename_long'); + + // Last three: + // from Helium2 [www.helium2.com] + // from http://privatewww.essex.ac.uk/~djmrob/replaygain/file_format_id3v2.html + } + + + function FrameNameShortLookup($framename) { + + $begin = __LINE__; + + /** This is not a comment! + + AENC audio_encryption + APIC attached_picture + ASPI audio_seek_point_index + BUF recommended_buffer_size + CNT play_counter + COM comments + COMM comments + COMR commercial_frame + CRA audio_encryption + CRM encrypted_meta_frame + ENCR encryption_method_registration + EQU equalisation + EQU2 equalisation + EQUA equalisation + ETC event_timing_codes + ETCO event_timing_codes + GEO general_encapsulated_object + GEOB general_encapsulated_object + GRID group_identification_registration + IPL involved_people_list + IPLS involved_people_list + LINK linked_information + LNK linked_information + MCDI music_cd_identifier + MCI music_cd_identifier + MLL mpeg_location_lookup_table + MLLT mpeg_location_lookup_table + OWNE ownership_frame + PCNT play_counter + PIC attached_picture + POP popularimeter + POPM popularimeter + POSS position_synchronisation_frame + PRIV private_frame + RBUF recommended_buffer_size + REV reverb + RVA relative_volume_adjustment + RVA2 relative_volume_adjustment + RVAD relative_volume_adjustment + RVRB reverb + SEEK seek_frame + SIGN signature_frame + SLT synchronised_lyric + STC synced_tempo_codes + SYLT synchronised_lyric + SYTC synchronised_tempo_codes + TAL album + TALB album + TBP bpm + TBPM bpm + TCM composer + TCO content_type + TCOM composer + TCON content_type + TCOP copyright_message + TCR copyright_message + TDA date + TDAT date + TDEN encoding_time + TDLY playlist_delay + TDOR original_release_time + TDRC recording_time + TDRL release_time + TDTG tagging_time + TDY playlist_delay + TEN encoded_by + TENC encoded_by + TEXT lyricist + TFLT file_type + TFT file_type + TIM time + TIME time + TIPL involved_people_list + TIT1 content_group_description + TIT2 title + TIT3 subtitle + TKE initial_key + TKEY initial_key + TLA language + TLAN language + TLE length + TLEN length + TMCL musician_credits_list + TMED media_type + TMOO mood + TMT media_type + TOA original_artist + TOAL original_album + TOF original_filename + TOFN original_filename + TOL original_lyricist + TOLY original_lyricist + TOPE original_artist + TOR original_year + TORY original_year + TOT original_album + TOWN file_owner + TP1 artist + TP2 band + TP3 conductor + TP4 remixer + TPA part_of_a_set + TPB publisher + TPE1 artist + TPE2 band + TPE3 conductor + TPE4 remixer + TPOS part_of_a_set + TPRO produced_notice + TPUB publisher + TRC isrc + TRCK track_number + TRD recording_dates + TRDA recording_dates + TRK track_number + TRSN internet_radio_station_name + TRSO internet_radio_station_owner + TSI size + TSIZ size + TSOA album_sort_order + TSOP performer_sort_order + TSOT title_sort_order + TSRC isrc + TSS encoder_settings + TSSE encoder_settings + TSST set_subtitle + TT1 description + TT2 title + TT3 subtitle + TXT lyricist + TXX text + TXXX text + TYE year + TYER year + UFI unique_file_identifier + UFID unique_file_identifier + ULT unsychronised_lyric + USER terms_of_use + USLT unsynchronised_lyric + WAF url_file + WAR url_artist + WAS url_source + WCM commercial_information + WCOM commercial_information + WCOP copyright + WCP copyright + WOAF url_file + WOAR url_artist + WOAS url_source + WORS url_station + WPAY url_payment + WPB url_publisher + WPUB url_publisher + WXX url_user + WXXX url_user + TFEA featured_artist + TSTU recording_studio + rgad replay_gain_adjustment + + */ + + return getid3_lib::EmbeddedLookup($framename, $begin, __LINE__, __FILE__, 'id3v2-framename_short'); + } + + function TextEncodingTerminatorLookup($encoding) { + // http://www.id3.org/id3v2.4.0-structure.txt + // Frames that allow different types of text encoding contains a text encoding description byte. Possible encodings: + // $00 ISO-8859-1. Terminated with $00. + // $01 UTF-16 encoded Unicode with BOM. All strings in the same frame SHALL have the same byteorder. Terminated with $00 00. + // $02 UTF-16BE encoded Unicode without BOM. Terminated with $00 00. + // $03 UTF-8 encoded Unicode. Terminated with $00. + + static $TextEncodingTerminatorLookup = array(0=>"\x00", 1=>"\x00\x00", 2=>"\x00\x00", 3=>"\x00", 255=>"\x00\x00"); + + return @$TextEncodingTerminatorLookup[$encoding]; + } + + function TextEncodingNameLookup($encoding) { + // http://www.id3.org/id3v2.4.0-structure.txt + static $TextEncodingNameLookup = array(0=>'ISO-8859-1', 1=>'UTF-16', 2=>'UTF-16BE', 3=>'UTF-8', 255=>'UTF-16BE'); + return (isset($TextEncodingNameLookup[$encoding]) ? $TextEncodingNameLookup[$encoding] : 'ISO-8859-1'); + } + + function IsValidID3v2FrameName($framename, $id3v2majorversion) { + switch ($id3v2majorversion) { + case 2: + return ereg('[A-Z][A-Z0-9]{2}', $framename); + break; + + case 3: + case 4: + return ereg('[A-Z][A-Z0-9]{3}', $framename); + break; + } + return false; + } + + function IsANumber($numberstring, $allowdecimal=false, $allownegative=false) { + for ($i = 0; $i < strlen($numberstring); $i++) { + if ((chr($numberstring{$i}) < chr('0')) || (chr($numberstring{$i}) > chr('9'))) { + if (($numberstring{$i} == '.') && $allowdecimal) { + // allowed + } elseif (($numberstring{$i} == '-') && $allownegative && ($i == 0)) { + // allowed + } else { + return false; + } + } + } + return true; + } + + function IsValidDateStampString($datestamp) { + if (strlen($datestamp) != 8) { + return false; + } + if (!$this->IsANumber($datestamp, false)) { + return false; + } + $year = substr($datestamp, 0, 4); + $month = substr($datestamp, 4, 2); + $day = substr($datestamp, 6, 2); + if (($year == 0) || ($month == 0) || ($day == 0)) { + return false; + } + if ($month > 12) { + return false; + } + if ($day > 31) { + return false; + } + if (($day > 30) && (($month == 4) || ($month == 6) || ($month == 9) || ($month == 11))) { + return false; + } + if (($day > 29) && ($month == 2)) { + return false; + } + return true; + } + + function ID3v2HeaderLength($majorversion) { + return (($majorversion == 2) ? 6 : 10); + } + +} + +?> diff --git a/includes/getid3/getid3/module.tag.lyrics3.php b/includes/getid3/getid3/module.tag.lyrics3.php new file mode 100644 index 0000000..e735d6d --- /dev/null +++ b/includes/getid3/getid3/module.tag.lyrics3.php @@ -0,0 +1,271 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +/// // +// module.tag.lyrics3.php // +// module for analyzing Lyrics3 tags // +// dependencies: module.tag.apetag.php (optional) // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_lyrics3 +{ + + function getid3_lyrics3(&$fd, &$ThisFileInfo) { + // http://www.volweb.cz/str/tags.htm + + fseek($fd, (0 - 128 - 9 - 6), SEEK_END); // end - ID3v1 - LYRICSEND - [Lyrics3size] + $lyrics3_id3v1 = fread($fd, 128 + 9 + 6); + $lyrics3lsz = substr($lyrics3_id3v1, 0, 6); // Lyrics3size + $lyrics3end = substr($lyrics3_id3v1, 6, 9); // LYRICSEND or LYRICS200 + $id3v1tag = substr($lyrics3_id3v1, 15, 128); // ID3v1 + + if ($lyrics3end == 'LYRICSEND') { + // Lyrics3v1, ID3v1, no APE + + $lyrics3size = 5100; + $lyrics3offset = $ThisFileInfo['filesize'] - 128 - $lyrics3size; + $lyrics3version = 1; + + } elseif ($lyrics3end == 'LYRICS200') { + // Lyrics3v2, ID3v1, no APE + + // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200' + $lyrics3size = $lyrics3lsz + 6 + strlen('LYRICS200'); + $lyrics3offset = $ThisFileInfo['filesize'] - 128 - $lyrics3size; + $lyrics3version = 2; + + } elseif (substr(strrev($lyrics3_id3v1), 0, 9) == strrev('LYRICSEND')) { + // Lyrics3v1, no ID3v1, no APE + + $lyrics3size = 5100; + $lyrics3offset = $ThisFileInfo['filesize'] - $lyrics3size; + $lyrics3version = 1; + $lyrics3offset = $ThisFileInfo['filesize'] - $lyrics3size; + + } elseif (substr(strrev($lyrics3_id3v1), 0, 9) == strrev('LYRICS200')) { + + // Lyrics3v2, no ID3v1, no APE + + $lyrics3size = strrev(substr(strrev($lyrics3_id3v1), 9, 6)) + 6 + strlen('LYRICS200'); // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200' + $lyrics3offset = $ThisFileInfo['filesize'] - $lyrics3size; + $lyrics3version = 2; + + } else { + + if (isset($ThisFileInfo['ape']['tag_offset_start']) && ($ThisFileInfo['ape']['tag_offset_start'] > 15)) { + + fseek($fd, $ThisFileInfo['ape']['tag_offset_start'] - 15, SEEK_SET); + $lyrics3lsz = fread($fd, 6); + $lyrics3end = fread($fd, 9); + + if ($lyrics3end == 'LYRICSEND') { + // Lyrics3v1, APE, maybe ID3v1 + + $lyrics3size = 5100; + $lyrics3offset = $ThisFileInfo['ape']['tag_offset_start'] - $lyrics3size; + $ThisFileInfo['avdataend'] = $lyrics3offset; + $lyrics3version = 1; + $ThisFileInfo['warning'][] = 'APE tag located after Lyrics3, will probably break Lyrics3 compatability'; + + } elseif ($lyrics3end == 'LYRICS200') { + // Lyrics3v2, APE, maybe ID3v1 + + $lyrics3size = $lyrics3lsz + 6 + strlen('LYRICS200'); // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200' + $lyrics3offset = $ThisFileInfo['ape']['tag_offset_start'] - $lyrics3size; + $lyrics3version = 2; + $ThisFileInfo['warning'][] = 'APE tag located after Lyrics3, will probably break Lyrics3 compatability'; + + } + + } + + } + + if (isset($lyrics3offset)) { + $ThisFileInfo['avdataend'] = $lyrics3offset; + $this->getLyrics3Data($ThisFileInfo, $fd, $lyrics3offset, $lyrics3version, $lyrics3size); + + if (!isset($ThisFileInfo['ape'])) { + $GETID3_ERRORARRAY = &$ThisFileInfo['warning']; + if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE__, false)) { + $tag = new getid3_apetag($fd, $ThisFileInfo, $ThisFileInfo['lyrics3']['tag_offset_start']); + } + } + + } + + return true; + } + + function getLyrics3Data(&$ThisFileInfo, &$fd, $endoffset, $version, $length) { + // http://www.volweb.cz/str/tags.htm + + fseek($fd, $endoffset, SEEK_SET); + if ($length <= 0) { + return false; + } + $rawdata = fread($fd, $length); + + if (substr($rawdata, 0, 11) != 'LYRICSBEGIN') { + if (strpos($rawdata, 'LYRICSBEGIN') !== false) { + + $ThisFileInfo['warning'][] = '"LYRICSBEGIN" expected at '.$endoffset.' but actually found at '.($endoffset + strpos($rawdata, 'LYRICSBEGIN')).' - this is invalid for Lyrics3 v'.$version; + $ThisFileInfo['avdataend'] = $endoffset + strpos($rawdata, 'LYRICSBEGIN'); + $ParsedLyrics3['tag_offset_start'] = $ThisFileInfo['avdataend']; + $rawdata = substr($rawdata, strpos($rawdata, 'LYRICSBEGIN')); + $length = strlen($rawdata); + + } else { + + $ThisFileInfo['error'][] = '"LYRICSBEGIN" expected at '.$endoffset.' but found "'.substr($rawdata, 0, 11).'" instead'; + return false; + + } + + } + + $ParsedLyrics3['raw']['lyrics3version'] = $version; + $ParsedLyrics3['raw']['lyrics3tagsize'] = $length; + $ParsedLyrics3['tag_offset_start'] = $endoffset; + $ParsedLyrics3['tag_offset_end'] = $endoffset + $length; + + switch ($version) { + + case 1: + if (substr($rawdata, strlen($rawdata) - 9, 9) == 'LYRICSEND') { + $ParsedLyrics3['raw']['LYR'] = trim(substr($rawdata, 11, strlen($rawdata) - 11 - 9)); + $this->Lyrics3LyricsTimestampParse($ParsedLyrics3); + } else { + $ThisFileInfo['error'][] = '"LYRICSEND" expected at '.(ftell($fd) - 11 + $length - 9).' but found "'.substr($rawdata, strlen($rawdata) - 9, 9).'" instead'; + return false; + } + break; + + case 2: + if (substr($rawdata, strlen($rawdata) - 9, 9) == 'LYRICS200') { + $ParsedLyrics3['raw']['unparsed'] = substr($rawdata, 11, strlen($rawdata) - 11 - 9 - 6); // LYRICSBEGIN + LYRICS200 + LSZ + $rawdata = $ParsedLyrics3['raw']['unparsed']; + while (strlen($rawdata) > 0) { + $fieldname = substr($rawdata, 0, 3); + $fieldsize = (int) substr($rawdata, 3, 5); + $ParsedLyrics3['raw'][$fieldname] = substr($rawdata, 8, $fieldsize); + $rawdata = substr($rawdata, 3 + 5 + $fieldsize); + } + + if (isset($ParsedLyrics3['raw']['IND'])) { + $i = 0; + $flagnames = array('lyrics', 'timestamps', 'inhibitrandom'); + foreach ($flagnames as $flagname) { + if (strlen($ParsedLyrics3['raw']['IND']) > ++$i) { + $ParsedLyrics3['flags'][$flagname] = $this->IntString2Bool(substr($ParsedLyrics3['raw']['IND'], $i, 1)); + } + } + } + + $fieldnametranslation = array('ETT'=>'title', 'EAR'=>'artist', 'EAL'=>'album', 'INF'=>'comment', 'AUT'=>'author'); + foreach ($fieldnametranslation as $key => $value) { + if (isset($ParsedLyrics3['raw'][$key])) { + $ParsedLyrics3['comments'][$value][] = trim($ParsedLyrics3['raw'][$key]); + } + } + + if (isset($ParsedLyrics3['raw']['IMG'])) { + $imagestrings = explode("\r\n", $ParsedLyrics3['raw']['IMG']); + foreach ($imagestrings as $key => $imagestring) { + if (strpos($imagestring, '||') !== false) { + $imagearray = explode('||', $imagestring); + $ParsedLyrics3['images'][$key]['filename'] = $imagearray[0]; + $ParsedLyrics3['images'][$key]['description'] = $imagearray[1]; + $ParsedLyrics3['images'][$key]['timestamp'] = $this->Lyrics3Timestamp2Seconds($imagearray[2]); + } + } + } + if (isset($ParsedLyrics3['raw']['LYR'])) { + $this->Lyrics3LyricsTimestampParse($ParsedLyrics3); + } + } else { + $ThisFileInfo['error'][] = '"LYRICS200" expected at '.(ftell($fd) - 11 + $length - 9).' but found "'.substr($rawdata, strlen($rawdata) - 9, 9).'" instead'; + return false; + } + break; + + default: + $ThisFileInfo['error'][] = 'Cannot process Lyrics3 version '.$version.' (only v1 and v2)'; + return false; + break; + } + + + if (isset($ThisFileInfo['id3v1']['tag_offset_start']) && ($ThisFileInfo['id3v1']['tag_offset_start'] < $ParsedLyrics3['tag_offset_end'])) { + $ThisFileInfo['warning'][] = 'ID3v1 tag information ignored since it appears to be a false synch in Lyrics3 tag data'; + unset($ThisFileInfo['id3v1']); + foreach ($ThisFileInfo['warning'] as $key => $value) { + if ($value == 'Some ID3v1 fields do not use NULL characters for padding') { + unset($ThisFileInfo['warning'][$key]); + sort($ThisFileInfo['warning']); + break; + } + } + } + + $ThisFileInfo['lyrics3'] = $ParsedLyrics3; + + return true; + } + + function Lyrics3Timestamp2Seconds($rawtimestamp) { + if (ereg('^\\[([0-9]{2}):([0-9]{2})\\]$', $rawtimestamp, $regs)) { + return (int) (($regs[1] * 60) + $regs[2]); + } + return false; + } + + function Lyrics3LyricsTimestampParse(&$Lyrics3data) { + $lyricsarray = explode("\r\n", $Lyrics3data['raw']['LYR']); + foreach ($lyricsarray as $key => $lyricline) { + $regs = array(); + unset($thislinetimestamps); + while (ereg('^(\\[[0-9]{2}:[0-9]{2}\\])', $lyricline, $regs)) { + $thislinetimestamps[] = $this->Lyrics3Timestamp2Seconds($regs[0]); + $lyricline = str_replace($regs[0], '', $lyricline); + } + $notimestamplyricsarray[$key] = $lyricline; + if (isset($thislinetimestamps) && is_array($thislinetimestamps)) { + sort($thislinetimestamps); + foreach ($thislinetimestamps as $timestampkey => $timestamp) { + if (isset($Lyrics3data['synchedlyrics'][$timestamp])) { + // timestamps only have a 1-second resolution, it's possible that multiple lines + // could have the same timestamp, if so, append + $Lyrics3data['synchedlyrics'][$timestamp] .= "\r\n".$lyricline; + } else { + $Lyrics3data['synchedlyrics'][$timestamp] = $lyricline; + } + } + } + } + $Lyrics3data['unsynchedlyrics'] = implode("\r\n", $notimestamplyricsarray); + if (isset($Lyrics3data['synchedlyrics']) && is_array($Lyrics3data['synchedlyrics'])) { + ksort($Lyrics3data['synchedlyrics']); + } + return true; + } + + function IntString2Bool($char) { + if ($char == '1') { + return true; + } elseif ($char == '0') { + return false; + } + return null; + } +} + + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/write.apetag.php b/includes/getid3/getid3/write.apetag.php new file mode 100644 index 0000000..189160a --- /dev/null +++ b/includes/getid3/getid3/write.apetag.php @@ -0,0 +1,228 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// write.apetag.php // +// module for writing APE tags // +// dependencies: module.tag.apetag.php // +// /// +///////////////////////////////////////////////////////////////// + + +getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE__, true); + +class getid3_write_apetag +{ + + var $filename; + var $tag_data; + var $always_preserve_replaygain = true; // ReplayGain / MP3gain tags will be copied from old tag even if not passed in data + var $warnings = array(); // any non-critical errors will be stored here + var $errors = array(); // any critical errors will be stored here + + function getid3_write_apetag() { + return true; + } + + function WriteAPEtag() { + // NOTE: All data passed to this function must be UTF-8 format + + $getID3 = new getID3; + $ThisFileInfo = $getID3->analyze($this->filename); + + if (isset($ThisFileInfo['ape']['tag_offset_start']) && isset($ThisFileInfo['lyrics3']['tag_offset_end'])) { + if ($ThisFileInfo['ape']['tag_offset_start'] >= $ThisFileInfo['lyrics3']['tag_offset_end']) { + // Current APE tag between Lyrics3 and ID3v1/EOF + // This break Lyrics3 functionality + if (!$this->DeleteAPEtag()) { + return false; + } + $ThisFileInfo = $getID3->analyze($this->filename); + } + } + + if ($this->always_preserve_replaygain) { + $ReplayGainTagsToPreserve = array('mp3gain_minmax', 'mp3gain_album_minmax', 'mp3gain_undo', 'replaygain_track_peak', 'replaygain_track_gain', 'replaygain_album_peak', 'replaygain_album_gain'); + foreach ($ReplayGainTagsToPreserve as $rg_key) { + if (isset($ThisFileInfo['ape']['items'][strtolower($rg_key)]['data'][0]) && !isset($this->tag_data[strtoupper($rg_key)][0])) { + $this->tag_data[strtoupper($rg_key)][0] = $ThisFileInfo['ape']['items'][strtolower($rg_key)]['data'][0]; + } + } + } + + if ($APEtag = $this->GenerateAPEtag()) { + if ($fp = @fopen($this->filename, 'a+b')) { + $oldignoreuserabort = ignore_user_abort(true); + flock($fp, LOCK_EX); + + $PostAPEdataOffset = $ThisFileInfo['avdataend']; + if (isset($ThisFileInfo['ape']['tag_offset_end'])) { + $PostAPEdataOffset = max($PostAPEdataOffset, $ThisFileInfo['ape']['tag_offset_end']); + } + if (isset($ThisFileInfo['lyrics3']['tag_offset_start'])) { + $PostAPEdataOffset = max($PostAPEdataOffset, $ThisFileInfo['lyrics3']['tag_offset_start']); + } + fseek($fp, $PostAPEdataOffset, SEEK_SET); + $PostAPEdata = ''; + if ($ThisFileInfo['filesize'] > $PostAPEdataOffset) { + $PostAPEdata = fread($fp, $ThisFileInfo['filesize'] - $PostAPEdataOffset); + } + + fseek($fp, $PostAPEdataOffset, SEEK_SET); + if (isset($ThisFileInfo['ape']['tag_offset_start'])) { + fseek($fp, $ThisFileInfo['ape']['tag_offset_start'], SEEK_SET); + } + ftruncate($fp, ftell($fp)); + fwrite($fp, $APEtag, strlen($APEtag)); + if (!empty($PostAPEdata)) { + fwrite($fp, $PostAPEdata, strlen($PostAPEdata)); + } + flock($fp, LOCK_UN); + fclose($fp); + ignore_user_abort($oldignoreuserabort); + return true; + + } + return false; + } + return false; + } + + function DeleteAPEtag() { + $getID3 = new getID3; + $ThisFileInfo = $getID3->analyze($this->filename); + if (isset($ThisFileInfo['ape']['tag_offset_start']) && isset($ThisFileInfo['ape']['tag_offset_end'])) { + if ($fp = @fopen($this->filename, 'a+b')) { + + flock($fp, LOCK_EX); + $oldignoreuserabort = ignore_user_abort(true); + + fseek($fp, $ThisFileInfo['ape']['tag_offset_end'], SEEK_SET); + $DataAfterAPE = ''; + if ($ThisFileInfo['filesize'] > $ThisFileInfo['ape']['tag_offset_end']) { + $DataAfterAPE = fread($fp, $ThisFileInfo['filesize'] - $ThisFileInfo['ape']['tag_offset_end']); + } + + ftruncate($fp, $ThisFileInfo['ape']['tag_offset_start']); + fseek($fp, $ThisFileInfo['ape']['tag_offset_start'], SEEK_SET); + + if (!empty($DataAfterAPE)) { + fwrite($fp, $DataAfterAPE, strlen($DataAfterAPE)); + } + + flock($fp, LOCK_UN); + fclose($fp); + ignore_user_abort($oldignoreuserabort); + + return true; + + } + return false; + } + return true; + } + + + function GenerateAPEtag() { + // NOTE: All data passed to this function must be UTF-8 format + + $items = array(); + if (!is_array($this->tag_data)) { + return false; + } + foreach ($this->tag_data as $key => $arrayofvalues) { + if (!is_array($arrayofvalues)) { + return false; + } + + $valuestring = ''; + foreach ($arrayofvalues as $value) { + $valuestring .= str_replace("\x00", '', $value)."\x00"; + } + $valuestring = rtrim($valuestring, "\x00"); + + // Length of the assigned value in bytes + $tagitem = getid3_lib::LittleEndian2String(strlen($valuestring), 4); + + //$tagitem .= $this->GenerateAPEtagFlags(true, true, false, 0, false); + $tagitem .= "\x00\x00\x00\x00"; + + $tagitem .= $this->CleanAPEtagItemKey($key)."\x00"; + $tagitem .= $valuestring; + + $items[] = $tagitem; + + } + + return $this->GenerateAPEtagHeaderFooter($items, true).implode('', $items).$this->GenerateAPEtagHeaderFooter($items, false); + } + + function GenerateAPEtagHeaderFooter(&$items, $isheader=false) { + $tagdatalength = 0; + foreach ($items as $itemdata) { + $tagdatalength += strlen($itemdata); + } + + $APEheader = 'APETAGEX'; + $APEheader .= getid3_lib::LittleEndian2String(2000, 4); + $APEheader .= getid3_lib::LittleEndian2String(32 + $tagdatalength, 4); + $APEheader .= getid3_lib::LittleEndian2String(count($items), 4); + $APEheader .= $this->GenerateAPEtagFlags(true, true, $isheader, 0, false); + $APEheader .= str_repeat("\x00", 8); + + return $APEheader; + } + + function GenerateAPEtagFlags($header=true, $footer=true, $isheader=false, $encodingid=0, $readonly=false) { + $APEtagFlags = array_fill(0, 4, 0); + if ($header) { + $APEtagFlags[0] |= 0x80; // Tag contains a header + } + if (!$footer) { + $APEtagFlags[0] |= 0x40; // Tag contains no footer + } + if ($isheader) { + $APEtagFlags[0] |= 0x20; // This is the header, not the footer + } + + // 0: Item contains text information coded in UTF-8 + // 1: Item contains binary information °) + // 2: Item is a locator of external stored information °°) + // 3: reserved + $APEtagFlags[3] |= ($encodingid << 1); + + if ($readonly) { + $APEtagFlags[3] |= 0x01; // Tag or Item is Read Only + } + + return chr($APEtagFlags[3]).chr($APEtagFlags[2]).chr($APEtagFlags[1]).chr($APEtagFlags[0]); + } + + function CleanAPEtagItemKey($itemkey) { + $itemkey = eregi_replace("[^\x20-\x7E]", '', $itemkey); + + // http://www.personal.uni-jena.de/~pfk/mpp/sv8/apekey.html + switch (strtoupper($itemkey)) { + case 'EAN/UPC': + case 'ISBN': + case 'LC': + case 'ISRC': + $itemkey = strtoupper($itemkey); + break; + + default: + $itemkey = ucwords($itemkey); + break; + } + return $itemkey; + + } + +} + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/write.id3v1.php b/includes/getid3/getid3/write.id3v1.php new file mode 100644 index 0000000..2125214 --- /dev/null +++ b/includes/getid3/getid3/write.id3v1.php @@ -0,0 +1,104 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// write.id3v1.php // +// module for writing ID3v1 tags // +// dependencies: module.tag.id3v1.php // +// /// +///////////////////////////////////////////////////////////////// + +getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v1.php', __FILE__, true); + +class getid3_write_id3v1 +{ + var $filename; + var $tag_data; + var $warnings = array(); // any non-critical errors will be stored here + var $errors = array(); // any critical errors will be stored here + + function getid3_write_id3v1() { + return true; + } + + function WriteID3v1() { + // File MUST be writeable - CHMOD(646) at least + if (is_writeable($this->filename)) { + if ($fp_source = @fopen($this->filename, 'r+b')) { + + fseek($fp_source, -128, SEEK_END); + if (fread($fp_source, 3) == 'TAG') { + fseek($fp_source, -128, SEEK_END); // overwrite existing ID3v1 tag + } else { + fseek($fp_source, 0, SEEK_END); // append new ID3v1 tag + } + + $new_id3v1_tag_data = getid3_id3v1::GenerateID3v1Tag( + @$this->tag_data['title'], + @$this->tag_data['artist'], + @$this->tag_data['album'], + @$this->tag_data['year'], + @$this->tag_data['genreid'], + @$this->tag_data['comment'], + @$this->tag_data['track']); + fwrite($fp_source, $new_id3v1_tag_data, 128); + fclose($fp_source); + return true; + + } else { + $this->errors[] = 'Could not open '.$this->filename.' mode "r+b"'; + return false; + } + } + $this->errors[] = 'File is not writeable: '.$this->filename; + return false; + } + + function FixID3v1Padding() { + // ID3v1 data is supposed to be padded with NULL characters, but some taggers incorrectly use spaces + // This function rewrites the ID3v1 tag with correct padding + + // Initialize getID3 engine + $getID3 = new getID3; + $ThisFileInfo = $getID3->analyze($this->filename); + if (isset($ThisFileInfo['tags']['id3v1'])) { + foreach ($ThisFileInfo['tags']['id3v1'] as $key => $value) { + $id3v1data[$key] = implode(',', $value); + } + $this->tag_data = $id3v1data; + return $this->WriteID3v1(); + } + return false; + } + + function RemoveID3v1() { + // File MUST be writeable - CHMOD(646) at least + if (is_writeable($this->filename)) { + if ($fp_source = @fopen($this->filename, 'r+b')) { + + fseek($fp_source, -128, SEEK_END); + if (fread($fp_source, 3) == 'TAG') { + ftruncate($fp_source, filesize($this->filename) - 128); + } else { + // no ID3v1 tag to begin with - do nothing + } + fclose($fp_source); + return true; + + } else { + $this->errors[] = 'Could not open '.$this->filename.' mode "r+b"'; + } + } else { + $this->errors[] = $this->filename.' is not writeable'; + } + return false; + } + +} + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/write.id3v2.php b/includes/getid3/getid3/write.id3v2.php new file mode 100644 index 0000000..045bbab --- /dev/null +++ b/includes/getid3/getid3/write.id3v2.php @@ -0,0 +1,2038 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +/// // +// write.id3v2.php // +// module for writing ID3v2 tags // +// dependencies: module.tag.id3v2.php // +// /// +///////////////////////////////////////////////////////////////// + +getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true); + +class getid3_write_id3v2 +{ + var $filename; + var $tag_data; + var $paddedlength = 4096; // minimum length of ID3v2 tag in bytes + var $majorversion = 3; // ID3v2 major version (2, 3 (recommended), 4) + var $minorversion = 0; // ID3v2 minor version - always 0 + var $merge_existing_data = false; // if true, merge new data with existing tags; if false, delete old tag data and only write new tags + var $id3v2_default_encodingid = 0; // default text encoding (ISO-8859-1) if not explicitly passed + var $id3v2_use_unsynchronisation = false; // the specs say it should be TRUE, but most other ID3v2-aware programs are broken if unsynchronization is used, so by default don't use it. + var $warnings = array(); // any non-critical errors will be stored here + var $errors = array(); // any critical errors will be stored here + + function getid3_write_id3v2() { + return true; + } + + function WriteID3v2() { + // File MUST be writeable - CHMOD(646) at least. It's best if the + // directory is also writeable, because that method is both faster and less susceptible to errors. + + if (is_writeable($this->filename) || (!file_exists($this->filename) && is_writeable(dirname($this->filename)))) { + // Initialize getID3 engine + $getID3 = new getID3; + $OldThisFileInfo = $getID3->analyze($this->filename); + if ($this->merge_existing_data) { + // merge with existing data + if (!empty($OldThisFileInfo['id3v2'])) { + $this->tag_data = $this->array_join_merge($OldThisFileInfo['id3v2'], $this->tag_data); + } + } + $this->paddedlength = max(@$OldThisFileInfo['id3v2']['headerlength'], $this->paddedlength); + + if ($NewID3v2Tag = $this->GenerateID3v2Tag()) { + + if (file_exists($this->filename) && is_writeable($this->filename) && isset($OldThisFileInfo['id3v2']['headerlength']) && ($OldThisFileInfo['id3v2']['headerlength'] == strlen($NewID3v2Tag))) { + + // best and fastest method - insert-overwrite existing tag (padded to length of old tag if neccesary) + if (file_exists($this->filename)) { + + ob_start(); + if ($fp = fopen($this->filename, 'r+b')) { + rewind($fp); + fwrite($fp, $NewID3v2Tag, strlen($NewID3v2Tag)); + fclose($fp); + } else { + $this->errors[] = 'Could not open '.$this->filename.' mode "r+b" - '.strip_tags(ob_get_contents()); + } + ob_end_clean(); + + } else { + + ob_start(); + if ($fp = fopen($this->filename, 'wb')) { + rewind($fp); + fwrite($fp, $NewID3v2Tag, strlen($NewID3v2Tag)); + fclose($fp); + } else { + $this->errors[] = 'Could not open '.$this->filename.' mode "wb" - '.strip_tags(ob_get_contents()); + } + ob_end_clean(); + + } + + } else { + + if ($tempfilename = tempnam('*', 'getID3')) { + ob_start(); + if ($fp_source = fopen($this->filename, 'rb')) { + if ($fp_temp = fopen($tempfilename, 'wb')) { + + fwrite($fp_temp, $NewID3v2Tag, strlen($NewID3v2Tag)); + + rewind($fp_source); + if (!empty($OldThisFileInfo['avdataoffset'])) { + fseek($fp_source, $OldThisFileInfo['avdataoffset'], SEEK_SET); + } + + while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) { + fwrite($fp_temp, $buffer, strlen($buffer)); + } + + fclose($fp_temp); + fclose($fp_source); + copy($tempfilename, $this->filename); + unlink($tempfilename); + ob_end_clean(); + return true; + + } else { + + $this->errors[] = 'Could not open '.$tempfilename.' mode "wb" - '.strip_tags(ob_get_contents()); + + } + fclose($fp_source); + + } else { + + $this->errors[] = 'Could not open '.$this->filename.' mode "rb" - '.strip_tags(ob_get_contents()); + + } + ob_end_clean(); + } + return false; + + } + + } else { + + $this->errors[] = '$this->GenerateID3v2Tag() failed'; + + } + + if (!empty($this->errors)) { + return false; + } + return true; + } else { + $this->errors[] = '!is_writeable('.$this->filename.')'; + } + return false; + } + + function RemoveID3v2() { + + // File MUST be writeable - CHMOD(646) at least. It's best if the + // directory is also writeable, because that method is both faster and less susceptible to errors. + if (is_writeable(dirname($this->filename))) { + + // preferred method - only one copying operation, minimal chance of corrupting + // original file if script is interrupted, but required directory to be writeable + if ($fp_source = @fopen($this->filename, 'rb')) { + // Initialize getID3 engine + $getID3 = new getID3; + $OldThisFileInfo = $getID3->analyze($this->filename); + rewind($fp_source); + if ($OldThisFileInfo['avdataoffset'] !== false) { + fseek($fp_source, $OldThisFileInfo['avdataoffset'], SEEK_SET); + } + if ($fp_temp = @fopen($this->filename.'getid3tmp', 'w+b')) { + while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) { + fwrite($fp_temp, $buffer, strlen($buffer)); + } + fclose($fp_temp); + } else { + $this->errors[] = 'Could not open '.$this->filename.'getid3tmp mode "w+b"'; + } + fclose($fp_source); + } else { + $this->errors[] = 'Could not open '.$this->filename.' mode "rb"'; + } + if (file_exists($this->filename)) { + unlink($this->filename); + } + rename($this->filename.'getid3tmp', $this->filename); + + } elseif (is_writable($this->filename)) { + + // less desirable alternate method - double-copies the file, overwrites original file + // and could corrupt source file if the script is interrupted or an error occurs. + if ($fp_source = @fopen($this->filename, 'rb')) { + // Initialize getID3 engine + $getID3 = new getID3; + $OldThisFileInfo = $getID3->analyze($this->filename); + rewind($fp_source); + if ($OldThisFileInfo['avdataoffset'] !== false) { + fseek($fp_source, $OldThisFileInfo['avdataoffset'], SEEK_SET); + } + if ($fp_temp = tmpfile()) { + while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) { + fwrite($fp_temp, $buffer, strlen($buffer)); + } + fclose($fp_source); + if ($fp_source = @fopen($this->filename, 'wb')) { + rewind($fp_temp); + while ($buffer = fread($fp_temp, GETID3_FREAD_BUFFER_SIZE)) { + fwrite($fp_source, $buffer, strlen($buffer)); + } + fseek($fp_temp, -128, SEEK_END); + fclose($fp_source); + } else { + $this->errors[] = 'Could not open '.$this->filename.' mode "wb"'; + } + fclose($fp_temp); + } else { + $this->errors[] = 'Could not create tmpfile()'; + } + } else { + $this->errors[] = 'Could not open '.$this->filename.' mode "rb"'; + } + + } else { + + $this->errors[] = 'Directory and file both not writeable'; + + } + + if (!empty($this->errors)) { + return false; + } + return true; + } + + + function GenerateID3v2TagFlags($flags) { + switch ($this->majorversion) { + case 4: + // %abcd0000 + $flag = (@$flags['unsynchronisation'] ? '1' : '0'); // a - Unsynchronisation + $flag .= (@$flags['extendedheader'] ? '1' : '0'); // b - Extended header + $flag .= (@$flags['experimental'] ? '1' : '0'); // c - Experimental indicator + $flag .= (@$flags['footer'] ? '1' : '0'); // d - Footer present + $flag .= '0000'; + break; + + case 3: + // %abc00000 + $flag = (@$flags['unsynchronisation'] ? '1' : '0'); // a - Unsynchronisation + $flag .= (@$flags['extendedheader'] ? '1' : '0'); // b - Extended header + $flag .= (@$flags['experimental'] ? '1' : '0'); // c - Experimental indicator + $flag .= '00000'; + break; + + case 2: + // %ab000000 + $flag = (@$flags['unsynchronisation'] ? '1' : '0'); // a - Unsynchronisation + $flag .= (@$flags['compression'] ? '1' : '0'); // b - Compression + $flag .= '000000'; + break; + + default: + return false; + break; + } + return chr(bindec($flag)); + } + + + function GenerateID3v2FrameFlags($TagAlter=false, $FileAlter=false, $ReadOnly=false, $Compression=false, $Encryption=false, $GroupingIdentity=false, $Unsynchronisation=false, $DataLengthIndicator=false) { + switch ($this->majorversion) { + case 4: + // %0abc0000 %0h00kmnp + $flag1 = '0'; + $flag1 .= $TagAlter ? '1' : '0'; // a - Tag alter preservation (true == discard) + $flag1 .= $FileAlter ? '1' : '0'; // b - File alter preservation (true == discard) + $flag1 .= $ReadOnly ? '1' : '0'; // c - Read only (true == read only) + $flag1 .= '0000'; + + $flag2 = '0'; + $flag2 .= $GroupingIdentity ? '1' : '0'; // h - Grouping identity (true == contains group information) + $flag2 .= '00'; + $flag2 .= $Compression ? '1' : '0'; // k - Compression (true == compressed) + $flag2 .= $Encryption ? '1' : '0'; // m - Encryption (true == encrypted) + $flag2 .= $Unsynchronisation ? '1' : '0'; // n - Unsynchronisation (true == unsynchronised) + $flag2 .= $DataLengthIndicator ? '1' : '0'; // p - Data length indicator (true == data length indicator added) + break; + + case 3: + // %abc00000 %ijk00000 + $flag1 = $TagAlter ? '1' : '0'; // a - Tag alter preservation (true == discard) + $flag1 .= $FileAlter ? '1' : '0'; // b - File alter preservation (true == discard) + $flag1 .= $ReadOnly ? '1' : '0'; // c - Read only (true == read only) + $flag1 .= '00000'; + + $flag2 = $Compression ? '1' : '0'; // i - Compression (true == compressed) + $flag2 .= $Encryption ? '1' : '0'; // j - Encryption (true == encrypted) + $flag2 .= $GroupingIdentity ? '1' : '0'; // k - Grouping identity (true == contains group information) + $flag2 .= '00000'; + break; + + default: + return false; + break; + + } + return chr(bindec($flag1)).chr(bindec($flag2)); + } + + function GenerateID3v2FrameData($frame_name, $source_data_array) { + if (!getid3_id3v2::IsValidID3v2FrameName($frame_name, $this->majorversion)) { + return false; + } + $framedata = ''; + + if (($this->majorversion < 3) || ($this->majorversion > 4)) { + + $this->errors[] = 'Only ID3v2.3 and ID3v2.4 are supported in GenerateID3v2FrameData()'; + + } else { // $this->majorversion 3 or 4 + + switch ($frame_name) { + case 'UFID': + // 4.1 UFID Unique file identifier + // Owner identifier $00 + // Identifier + if (strlen($source_data_array['data']) > 64) { + $this->errors[] = 'Identifier not allowed to be longer than 64 bytes in '.$frame_name.' (supplied data was '.strlen($source_data_array['data']).' bytes long)'; + } else { + $framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00"; + $framedata .= substr($source_data_array['data'], 0, 64); // max 64 bytes - truncate anything longer + } + break; + + case 'TXXX': + // 4.2.2 TXXX User defined text information frame + // Text encoding $xx + // Description $00 (00) + // Value + $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid); + if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'], $this->majorversion)) { + $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion; + } else { + $framedata .= chr($source_data_array['encodingid']); + $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']); + $framedata .= $source_data_array['data']; + } + break; + + case 'WXXX': + // 4.3.2 WXXX User defined URL link frame + // Text encoding $xx + // Description $00 (00) + // URL + $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid); + if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'], $this->majorversion)) { + $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion; + } elseif (!isset($source_data_array['data']) || !$this->IsValidURL($source_data_array['data'], false, false)) { + //$this->errors[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')'; + // probably should be an error, need to rewrite IsValidURL() to handle other encodings + $this->warnings[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')'; + } else { + $framedata .= chr($source_data_array['encodingid']); + $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']); + $framedata .= $source_data_array['data']; + } + break; + + case 'IPLS': + // 4.4 IPLS Involved people list (ID3v2.3 only) + // Text encoding $xx + // People list strings + $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid); + if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'], $this->majorversion)) { + $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion; + } else { + $framedata .= chr($source_data_array['encodingid']); + $framedata .= $source_data_array['data']; + } + break; + + case 'MCDI': + // 4.4 MCDI Music CD identifier + // CD TOC + $framedata .= $source_data_array['data']; + break; + + case 'ETCO': + // 4.5 ETCO Event timing codes + // Time stamp format $xx + // Where time stamp format is: + // $01 (32-bit value) MPEG frames from beginning of file + // $02 (32-bit value) milliseconds from beginning of file + // Followed by a list of key events in the following format: + // Type of event $xx + // Time stamp $xx (xx ...) + // The 'Time stamp' is set to zero if directly at the beginning of the sound + // or after the previous event. All events MUST be sorted in chronological order. + if (($source_data_array['timestampformat'] > 2) || ($source_data_array['timestampformat'] < 1)) { + $this->errors[] = 'Invalid Time Stamp Format byte in '.$frame_name.' ('.$source_data_array['timestampformat'].')'; + } else { + $framedata .= chr($source_data_array['timestampformat']); + foreach ($source_data_array as $key => $val) { + if (!$this->ID3v2IsValidETCOevent($val['typeid'])) { + $this->errors[] = 'Invalid Event Type byte in '.$frame_name.' ('.$val['typeid'].')'; + } elseif (($key != 'timestampformat') && ($key != 'flags')) { + if (($val['timestamp'] > 0) && ($previousETCOtimestamp >= $val['timestamp'])) { + // The 'Time stamp' is set to zero if directly at the beginning of the sound + // or after the previous event. All events MUST be sorted in chronological order. + $this->errors[] = 'Out-of-order timestamp in '.$frame_name.' ('.$val['timestamp'].') for Event Type ('.$val['typeid'].')'; + } else { + $framedata .= chr($val['typeid']); + $framedata .= getid3_lib::BigEndian2String($val['timestamp'], 4, false); + } + } + } + } + break; + + case 'MLLT': + // 4.6 MLLT MPEG location lookup table + // MPEG frames between reference $xx xx + // Bytes between reference $xx xx xx + // Milliseconds between reference $xx xx xx + // Bits for bytes deviation $xx + // Bits for milliseconds dev. $xx + // Then for every reference the following data is included; + // Deviation in bytes %xxx.... + // Deviation in milliseconds %xxx.... + if (($source_data_array['framesbetweenreferences'] > 0) && ($source_data_array['framesbetweenreferences'] <= 65535)) { + $framedata .= getid3_lib::BigEndian2String($source_data_array['framesbetweenreferences'], 2, false); + } else { + $this->errors[] = 'Invalid MPEG Frames Between References in '.$frame_name.' ('.$source_data_array['framesbetweenreferences'].')'; + } + if (($source_data_array['bytesbetweenreferences'] > 0) && ($source_data_array['bytesbetweenreferences'] <= 16777215)) { + $framedata .= getid3_lib::BigEndian2String($source_data_array['bytesbetweenreferences'], 3, false); + } else { + $this->errors[] = 'Invalid bytes Between References in '.$frame_name.' ('.$source_data_array['bytesbetweenreferences'].')'; + } + if (($source_data_array['msbetweenreferences'] > 0) && ($source_data_array['msbetweenreferences'] <= 16777215)) { + $framedata .= getid3_lib::BigEndian2String($source_data_array['msbetweenreferences'], 3, false); + } else { + $this->errors[] = 'Invalid Milliseconds Between References in '.$frame_name.' ('.$source_data_array['msbetweenreferences'].')'; + } + if (!$this->IsWithinBitRange($source_data_array['bitsforbytesdeviation'], 8, false)) { + if (($source_data_array['bitsforbytesdeviation'] % 4) == 0) { + $framedata .= chr($source_data_array['bitsforbytesdeviation']); + } else { + $this->errors[] = 'Bits For Bytes Deviation in '.$frame_name.' ('.$source_data_array['bitsforbytesdeviation'].') must be a multiple of 4.'; + } + } else { + $this->errors[] = 'Invalid Bits For Bytes Deviation in '.$frame_name.' ('.$source_data_array['bitsforbytesdeviation'].')'; + } + if (!$this->IsWithinBitRange($source_data_array['bitsformsdeviation'], 8, false)) { + if (($source_data_array['bitsformsdeviation'] % 4) == 0) { + $framedata .= chr($source_data_array['bitsformsdeviation']); + } else { + $this->errors[] = 'Bits For Milliseconds Deviation in '.$frame_name.' ('.$source_data_array['bitsforbytesdeviation'].') must be a multiple of 4.'; + } + } else { + $this->errors[] = 'Invalid Bits For Milliseconds Deviation in '.$frame_name.' ('.$source_data_array['bitsformsdeviation'].')'; + } + foreach ($source_data_array as $key => $val) { + if (($key != 'framesbetweenreferences') && ($key != 'bytesbetweenreferences') && ($key != 'msbetweenreferences') && ($key != 'bitsforbytesdeviation') && ($key != 'bitsformsdeviation') && ($key != 'flags')) { + $unwrittenbitstream .= str_pad(getid3_lib::Dec2Bin($val['bytedeviation']), $source_data_array['bitsforbytesdeviation'], '0', STR_PAD_LEFT); + $unwrittenbitstream .= str_pad(getid3_lib::Dec2Bin($val['msdeviation']), $source_data_array['bitsformsdeviation'], '0', STR_PAD_LEFT); + } + } + for ($i = 0; $i < strlen($unwrittenbitstream); $i += 8) { + $highnibble = bindec(substr($unwrittenbitstream, $i, 4)) << 4; + $lownibble = bindec(substr($unwrittenbitstream, $i + 4, 4)); + $framedata .= chr($highnibble & $lownibble); + } + break; + + case 'SYTC': + // 4.7 SYTC Synchronised tempo codes + // Time stamp format $xx + // Tempo data + // Where time stamp format is: + // $01 (32-bit value) MPEG frames from beginning of file + // $02 (32-bit value) milliseconds from beginning of file + if (($source_data_array['timestampformat'] > 2) || ($source_data_array['timestampformat'] < 1)) { + $this->errors[] = 'Invalid Time Stamp Format byte in '.$frame_name.' ('.$source_data_array['timestampformat'].')'; + } else { + $framedata .= chr($source_data_array['timestampformat']); + foreach ($source_data_array as $key => $val) { + if (!$this->ID3v2IsValidETCOevent($val['typeid'])) { + $this->errors[] = 'Invalid Event Type byte in '.$frame_name.' ('.$val['typeid'].')'; + } elseif (($key != 'timestampformat') && ($key != 'flags')) { + if (($val['tempo'] < 0) || ($val['tempo'] > 510)) { + $this->errors[] = 'Invalid Tempo (max = 510) in '.$frame_name.' ('.$val['tempo'].') at timestamp ('.$val['timestamp'].')'; + } else { + if ($val['tempo'] > 255) { + $framedata .= chr(255); + $val['tempo'] -= 255; + } + $framedata .= chr($val['tempo']); + $framedata .= getid3_lib::BigEndian2String($val['timestamp'], 4, false); + } + } + } + } + break; + + case 'USLT': + // 4.8 USLT Unsynchronised lyric/text transcription + // Text encoding $xx + // Language $xx xx xx + // Content descriptor $00 (00) + // Lyrics/text + $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid); + if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) { + $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion; + } elseif (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') { + $this->errors[] = 'Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')'; + } else { + $framedata .= chr($source_data_array['encodingid']); + $framedata .= strtolower($source_data_array['language']); + $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']); + $framedata .= $source_data_array['data']; + } + break; + + case 'SYLT': + // 4.9 SYLT Synchronised lyric/text + // Text encoding $xx + // Language $xx xx xx + // Time stamp format $xx + // $01 (32-bit value) MPEG frames from beginning of file + // $02 (32-bit value) milliseconds from beginning of file + // Content type $xx + // Content descriptor $00 (00) + // Terminated text to be synced (typically a syllable) + // Sync identifier (terminator to above string) $00 (00) + // Time stamp $xx (xx ...) + $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid); + if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) { + $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion; + } elseif (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') { + $this->errors[] = 'Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')'; + } elseif (($source_data_array['timestampformat'] > 2) || ($source_data_array['timestampformat'] < 1)) { + $this->errors[] = 'Invalid Time Stamp Format byte in '.$frame_name.' ('.$source_data_array['timestampformat'].')'; + } elseif (!$this->ID3v2IsValidSYLTtype($source_data_array['contenttypeid'])) { + $this->errors[] = 'Invalid Content Type byte in '.$frame_name.' ('.$source_data_array['contenttypeid'].')'; + } elseif (!is_array($source_data_array['data'])) { + $this->errors[] = 'Invalid Lyric/Timestamp data in '.$frame_name.' (must be an array)'; + } else { + $framedata .= chr($source_data_array['encodingid']); + $framedata .= strtolower($source_data_array['language']); + $framedata .= chr($source_data_array['timestampformat']); + $framedata .= chr($source_data_array['contenttypeid']); + $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']); + ksort($source_data_array['data']); + foreach ($source_data_array['data'] as $key => $val) { + $framedata .= $val['data'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']); + $framedata .= getid3_lib::BigEndian2String($val['timestamp'], 4, false); + } + } + break; + + case 'COMM': + // 4.10 COMM Comments + // Text encoding $xx + // Language $xx xx xx + // Short content descrip. $00 (00) + // The actual text + $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid); + if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) { + $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion; + } elseif (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') { + $this->errors[] = 'Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')'; + } else { + $framedata .= chr($source_data_array['encodingid']); + $framedata .= strtolower($source_data_array['language']); + $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']); + $framedata .= $source_data_array['data']; + } + break; + + case 'RVA2': + // 4.11 RVA2 Relative volume adjustment (2) (ID3v2.4+ only) + // Identification $00 + // The 'identification' string is used to identify the situation and/or + // device where this adjustment should apply. The following is then + // repeated for every channel: + // Type of channel $xx + // Volume adjustment $xx xx + // Bits representing peak $xx + // Peak volume $xx (xx ...) + $framedata .= str_replace("\x00", '', $source_data_array['description'])."\x00"; + foreach ($source_data_array as $key => $val) { + if ($key != 'description') { + $framedata .= chr($val['channeltypeid']); + $framedata .= getid3_lib::BigEndian2String($val['volumeadjust'], 2, false, true); // signed 16-bit + if (!$this->IsWithinBitRange($source_data_array['bitspeakvolume'], 8, false)) { + $framedata .= chr($val['bitspeakvolume']); + if ($val['bitspeakvolume'] > 0) { + $framedata .= getid3_lib::BigEndian2String($val['peakvolume'], ceil($val['bitspeakvolume'] / 8), false, false); + } + } else { + $this->errors[] = 'Invalid Bits Representing Peak Volume in '.$frame_name.' ('.$val['bitspeakvolume'].') (range = 0 to 255)'; + } + } + } + break; + + case 'RVAD': + // 4.12 RVAD Relative volume adjustment (ID3v2.3 only) + // Increment/decrement %00fedcba + // Bits used for volume descr. $xx + // Relative volume change, right $xx xx (xx ...) // a + // Relative volume change, left $xx xx (xx ...) // b + // Peak volume right $xx xx (xx ...) + // Peak volume left $xx xx (xx ...) + // Relative volume change, right back $xx xx (xx ...) // c + // Relative volume change, left back $xx xx (xx ...) // d + // Peak volume right back $xx xx (xx ...) + // Peak volume left back $xx xx (xx ...) + // Relative volume change, center $xx xx (xx ...) // e + // Peak volume center $xx xx (xx ...) + // Relative volume change, bass $xx xx (xx ...) // f + // Peak volume bass $xx xx (xx ...) + if (!$this->IsWithinBitRange($source_data_array['bitsvolume'], 8, false)) { + $this->errors[] = 'Invalid Bits For Volume Description byte in '.$frame_name.' ('.$source_data_array['bitsvolume'].') (range = 1 to 255)'; + } else { + $incdecflag .= '00'; + $incdecflag .= $source_data_array['incdec']['right'] ? '1' : '0'; // a - Relative volume change, right + $incdecflag .= $source_data_array['incdec']['left'] ? '1' : '0'; // b - Relative volume change, left + $incdecflag .= $source_data_array['incdec']['rightrear'] ? '1' : '0'; // c - Relative volume change, right back + $incdecflag .= $source_data_array['incdec']['leftrear'] ? '1' : '0'; // d - Relative volume change, left back + $incdecflag .= $source_data_array['incdec']['center'] ? '1' : '0'; // e - Relative volume change, center + $incdecflag .= $source_data_array['incdec']['bass'] ? '1' : '0'; // f - Relative volume change, bass + $framedata .= chr(bindec($incdecflag)); + $framedata .= chr($source_data_array['bitsvolume']); + $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['right'], ceil($source_data_array['bitsvolume'] / 8), false); + $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['left'], ceil($source_data_array['bitsvolume'] / 8), false); + $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['right'], ceil($source_data_array['bitsvolume'] / 8), false); + $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['left'], ceil($source_data_array['bitsvolume'] / 8), false); + if ($source_data_array['volumechange']['rightrear'] || $source_data_array['volumechange']['leftrear'] || + $source_data_array['peakvolume']['rightrear'] || $source_data_array['peakvolume']['leftrear'] || + $source_data_array['volumechange']['center'] || $source_data_array['peakvolume']['center'] || + $source_data_array['volumechange']['bass'] || $source_data_array['peakvolume']['bass']) { + $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['rightrear'], ceil($source_data_array['bitsvolume']/8), false); + $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['leftrear'], ceil($source_data_array['bitsvolume']/8), false); + $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['rightrear'], ceil($source_data_array['bitsvolume']/8), false); + $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['leftrear'], ceil($source_data_array['bitsvolume']/8), false); + } + if ($source_data_array['volumechange']['center'] || $source_data_array['peakvolume']['center'] || + $source_data_array['volumechange']['bass'] || $source_data_array['peakvolume']['bass']) { + $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['center'], ceil($source_data_array['bitsvolume']/8), false); + $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['center'], ceil($source_data_array['bitsvolume']/8), false); + } + if ($source_data_array['volumechange']['bass'] || $source_data_array['peakvolume']['bass']) { + $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['bass'], ceil($source_data_array['bitsvolume']/8), false); + $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['bass'], ceil($source_data_array['bitsvolume']/8), false); + } + } + break; + + case 'EQU2': + // 4.12 EQU2 Equalisation (2) (ID3v2.4+ only) + // Interpolation method $xx + // $00 Band + // $01 Linear + // Identification $00 + // The following is then repeated for every adjustment point + // Frequency $xx xx + // Volume adjustment $xx xx + if (($source_data_array['interpolationmethod'] < 0) || ($source_data_array['interpolationmethod'] > 1)) { + $this->errors[] = 'Invalid Interpolation Method byte in '.$frame_name.' ('.$source_data_array['interpolationmethod'].') (valid = 0 or 1)'; + } else { + $framedata .= chr($source_data_array['interpolationmethod']); + $framedata .= str_replace("\x00", '', $source_data_array['description'])."\x00"; + foreach ($source_data_array['data'] as $key => $val) { + $framedata .= getid3_lib::BigEndian2String(intval(round($key * 2)), 2, false); + $framedata .= getid3_lib::BigEndian2String($val, 2, false, true); // signed 16-bit + } + } + break; + + case 'EQUA': + // 4.12 EQUA Equalisation (ID3v2.3 only) + // Adjustment bits $xx + // This is followed by 2 bytes + ('adjustment bits' rounded up to the + // nearest byte) for every equalisation band in the following format, + // giving a frequency range of 0 - 32767Hz: + // Increment/decrement %x (MSB of the Frequency) + // Frequency (lower 15 bits) + // Adjustment $xx (xx ...) + if (!$this->IsWithinBitRange($source_data_array['bitsvolume'], 8, false)) { + $this->errors[] = 'Invalid Adjustment Bits byte in '.$frame_name.' ('.$source_data_array['bitsvolume'].') (range = 1 to 255)'; + } else { + $framedata .= chr($source_data_array['adjustmentbits']); + foreach ($source_data_array as $key => $val) { + if ($key != 'bitsvolume') { + if (($key > 32767) || ($key < 0)) { + $this->errors[] = 'Invalid Frequency in '.$frame_name.' ('.$key.') (range = 0 to 32767)'; + } else { + if ($val >= 0) { + // put MSB of frequency to 1 if increment, 0 if decrement + $key |= 0x8000; + } + $framedata .= getid3_lib::BigEndian2String($key, 2, false); + $framedata .= getid3_lib::BigEndian2String($val, ceil($source_data_array['adjustmentbits'] / 8), false); + } + } + } + } + break; + + case 'RVRB': + // 4.13 RVRB Reverb + // Reverb left (ms) $xx xx + // Reverb right (ms) $xx xx + // Reverb bounces, left $xx + // Reverb bounces, right $xx + // Reverb feedback, left to left $xx + // Reverb feedback, left to right $xx + // Reverb feedback, right to right $xx + // Reverb feedback, right to left $xx + // Premix left to right $xx + // Premix right to left $xx + if (!$this->IsWithinBitRange($source_data_array['left'], 16, false)) { + $this->errors[] = 'Invalid Reverb Left in '.$frame_name.' ('.$source_data_array['left'].') (range = 0 to 65535)'; + } elseif (!$this->IsWithinBitRange($source_data_array['right'], 16, false)) { + $this->errors[] = 'Invalid Reverb Left in '.$frame_name.' ('.$source_data_array['right'].') (range = 0 to 65535)'; + } elseif (!$this->IsWithinBitRange($source_data_array['bouncesL'], 8, false)) { + $this->errors[] = 'Invalid Reverb Bounces, Left in '.$frame_name.' ('.$source_data_array['bouncesL'].') (range = 0 to 255)'; + } elseif (!$this->IsWithinBitRange($source_data_array['bouncesR'], 8, false)) { + $this->errors[] = 'Invalid Reverb Bounces, Right in '.$frame_name.' ('.$source_data_array['bouncesR'].') (range = 0 to 255)'; + } elseif (!$this->IsWithinBitRange($source_data_array['feedbackLL'], 8, false)) { + $this->errors[] = 'Invalid Reverb Feedback, Left-To-Left in '.$frame_name.' ('.$source_data_array['feedbackLL'].') (range = 0 to 255)'; + } elseif (!$this->IsWithinBitRange($source_data_array['feedbackLR'], 8, false)) { + $this->errors[] = 'Invalid Reverb Feedback, Left-To-Right in '.$frame_name.' ('.$source_data_array['feedbackLR'].') (range = 0 to 255)'; + } elseif (!$this->IsWithinBitRange($source_data_array['feedbackRR'], 8, false)) { + $this->errors[] = 'Invalid Reverb Feedback, Right-To-Right in '.$frame_name.' ('.$source_data_array['feedbackRR'].') (range = 0 to 255)'; + } elseif (!$this->IsWithinBitRange($source_data_array['feedbackRL'], 8, false)) { + $this->errors[] = 'Invalid Reverb Feedback, Right-To-Left in '.$frame_name.' ('.$source_data_array['feedbackRL'].') (range = 0 to 255)'; + } elseif (!$this->IsWithinBitRange($source_data_array['premixLR'], 8, false)) { + $this->errors[] = 'Invalid Premix, Left-To-Right in '.$frame_name.' ('.$source_data_array['premixLR'].') (range = 0 to 255)'; + } elseif (!$this->IsWithinBitRange($source_data_array['premixRL'], 8, false)) { + $this->errors[] = 'Invalid Premix, Right-To-Left in '.$frame_name.' ('.$source_data_array['premixRL'].') (range = 0 to 255)'; + } else { + $framedata .= getid3_lib::BigEndian2String($source_data_array['left'], 2, false); + $framedata .= getid3_lib::BigEndian2String($source_data_array['right'], 2, false); + $framedata .= chr($source_data_array['bouncesL']); + $framedata .= chr($source_data_array['bouncesR']); + $framedata .= chr($source_data_array['feedbackLL']); + $framedata .= chr($source_data_array['feedbackLR']); + $framedata .= chr($source_data_array['feedbackRR']); + $framedata .= chr($source_data_array['feedbackRL']); + $framedata .= chr($source_data_array['premixLR']); + $framedata .= chr($source_data_array['premixRL']); + } + break; + + case 'APIC': + // 4.14 APIC Attached picture + // Text encoding $xx + // MIME type $00 + // Picture type $xx + // Description $00 (00) + // Picture data + $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid); + if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) { + $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion; + } elseif (!$this->ID3v2IsValidAPICpicturetype($source_data_array['picturetypeid'])) { + $this->errors[] = 'Invalid Picture Type byte in '.$frame_name.' ('.$source_data_array['picturetypeid'].') for ID3v2.'.$this->majorversion; + } elseif (($this->majorversion >= 3) && (!$this->ID3v2IsValidAPICimageformat($source_data_array['mime']))) { + $this->errors[] = 'Invalid MIME Type in '.$frame_name.' ('.$source_data_array['mime'].') for ID3v2.'.$this->majorversion; + } elseif (($source_data_array['mime'] == '-->') && (!$this->IsValidURL($source_data_array['data'], false, false))) { + //$this->errors[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')'; + // probably should be an error, need to rewrite IsValidURL() to handle other encodings + $this->warnings[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')'; + } else { + $framedata .= chr($source_data_array['encodingid']); + $framedata .= str_replace("\x00", '', $source_data_array['mime'])."\x00"; + $framedata .= chr($source_data_array['picturetypeid']); + $framedata .= @$source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']); + $framedata .= $source_data_array['data']; + } + break; + + case 'GEOB': + // 4.15 GEOB General encapsulated object + // Text encoding $xx + // MIME type $00 + // Filename $00 (00) + // Content description $00 (00) + // Encapsulated object + $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid); + if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) { + $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion; + } elseif (!$this->IsValidMIMEstring($source_data_array['mime'])) { + $this->errors[] = 'Invalid MIME Type in '.$frame_name.' ('.$source_data_array['mime'].')'; + } elseif (!$source_data_array['description']) { + $this->errors[] = 'Missing Description in '.$frame_name; + } else { + $framedata .= chr($source_data_array['encodingid']); + $framedata .= str_replace("\x00", '', $source_data_array['mime'])."\x00"; + $framedata .= $source_data_array['filename'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']); + $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']); + $framedata .= $source_data_array['data']; + } + break; + + case 'PCNT': + // 4.16 PCNT Play counter + // When the counter reaches all one's, one byte is inserted in + // front of the counter thus making the counter eight bits bigger + // Counter $xx xx xx xx (xx ...) + $framedata .= getid3_lib::BigEndian2String($source_data_array['data'], 4, false); + break; + + case 'POPM': + // 4.17 POPM Popularimeter + // When the counter reaches all one's, one byte is inserted in + // front of the counter thus making the counter eight bits bigger + // Email to user $00 + // Rating $xx + // Counter $xx xx xx xx (xx ...) + if (!$this->IsWithinBitRange($source_data_array['rating'], 8, false)) { + $this->errors[] = 'Invalid Rating byte in '.$frame_name.' ('.$source_data_array['rating'].') (range = 0 to 255)'; + } elseif (!IsValidEmail($source_data_array['email'])) { + $this->errors[] = 'Invalid Email in '.$frame_name.' ('.$source_data_array['email'].')'; + } else { + $framedata .= str_replace("\x00", '', $source_data_array['email'])."\x00"; + $framedata .= chr($source_data_array['rating']); + $framedata .= getid3_lib::BigEndian2String($source_data_array['data'], 4, false); + } + break; + + case 'RBUF': + // 4.18 RBUF Recommended buffer size + // Buffer size $xx xx xx + // Embedded info flag %0000000x + // Offset to next tag $xx xx xx xx + if (!$this->IsWithinBitRange($source_data_array['buffersize'], 24, false)) { + $this->errors[] = 'Invalid Buffer Size in '.$frame_name; + } elseif (!$this->IsWithinBitRange($source_data_array['nexttagoffset'], 32, false)) { + $this->errors[] = 'Invalid Offset To Next Tag in '.$frame_name; + } else { + $framedata .= getid3_lib::BigEndian2String($source_data_array['buffersize'], 3, false); + $flag .= '0000000'; + $flag .= $source_data_array['flags']['embededinfo'] ? '1' : '0'; + $framedata .= chr(bindec($flag)); + $framedata .= getid3_lib::BigEndian2String($source_data_array['nexttagoffset'], 4, false); + } + break; + + case 'AENC': + // 4.19 AENC Audio encryption + // Owner identifier $00 + // Preview start $xx xx + // Preview length $xx xx + // Encryption info + if (!$this->IsWithinBitRange($source_data_array['previewstart'], 16, false)) { + $this->errors[] = 'Invalid Preview Start in '.$frame_name.' ('.$source_data_array['previewstart'].')'; + } elseif (!$this->IsWithinBitRange($source_data_array['previewlength'], 16, false)) { + $this->errors[] = 'Invalid Preview Length in '.$frame_name.' ('.$source_data_array['previewlength'].')'; + } else { + $framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00"; + $framedata .= getid3_lib::BigEndian2String($source_data_array['previewstart'], 2, false); + $framedata .= getid3_lib::BigEndian2String($source_data_array['previewlength'], 2, false); + $framedata .= $source_data_array['encryptioninfo']; + } + break; + + case 'LINK': + // 4.20 LINK Linked information + // Frame identifier $xx xx xx xx + // URL $00 + // ID and additional data + if (!getid3_id3v2::IsValidID3v2FrameName($source_data_array['frameid'], $this->majorversion)) { + $this->errors[] = 'Invalid Frame Identifier in '.$frame_name.' ('.$source_data_array['frameid'].')'; + } elseif (!$this->IsValidURL($source_data_array['data'], true, false)) { + //$this->errors[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')'; + // probably should be an error, need to rewrite IsValidURL() to handle other encodings + $this->warnings[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')'; + } elseif ((($source_data_array['frameid'] == 'AENC') || ($source_data_array['frameid'] == 'APIC') || ($source_data_array['frameid'] == 'GEOB') || ($source_data_array['frameid'] == 'TXXX')) && ($source_data_array['additionaldata'] == '')) { + $this->errors[] = 'Content Descriptor must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name; + } elseif (($source_data_array['frameid'] == 'USER') && (getid3_id3v2::LanguageLookup($source_data_array['additionaldata'], true) == '')) { + $this->errors[] = 'Language must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name; + } elseif (($source_data_array['frameid'] == 'PRIV') && ($source_data_array['additionaldata'] == '')) { + $this->errors[] = 'Owner Identifier must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name; + } elseif ((($source_data_array['frameid'] == 'COMM') || ($source_data_array['frameid'] == 'SYLT') || ($source_data_array['frameid'] == 'USLT')) && ((getid3_id3v2::LanguageLookup(substr($source_data_array['additionaldata'], 0, 3), true) == '') || (substr($source_data_array['additionaldata'], 3) == ''))) { + $this->errors[] = 'Language followed by Content Descriptor must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name; + } else { + $framedata .= $source_data_array['frameid']; + $framedata .= str_replace("\x00", '', $source_data_array['data'])."\x00"; + switch ($source_data_array['frameid']) { + case 'COMM': + case 'SYLT': + case 'USLT': + case 'PRIV': + case 'USER': + case 'AENC': + case 'APIC': + case 'GEOB': + case 'TXXX': + $framedata .= $source_data_array['additionaldata']; + break; + case 'ASPI': + case 'ETCO': + case 'EQU2': + case 'MCID': + case 'MLLT': + case 'OWNE': + case 'RVA2': + case 'RVRB': + case 'SYTC': + case 'IPLS': + case 'RVAD': + case 'EQUA': + // no additional data required + break; + case 'RBUF': + if ($this->majorversion == 3) { + // no additional data required + } else { + $this->errors[] = $source_data_array['frameid'].' is not a valid Frame Identifier in '.$frame_name.' (in ID3v2.'.$this->majorversion.')'; + } + + default: + if ((substr($source_data_array['frameid'], 0, 1) == 'T') || (substr($source_data_array['frameid'], 0, 1) == 'W')) { + // no additional data required + } else { + $this->errors[] = $source_data_array['frameid'].' is not a valid Frame Identifier in '.$frame_name.' (in ID3v2.'.$this->majorversion.')'; + } + break; + } + } + break; + + case 'POSS': + // 4.21 POSS Position synchronisation frame (ID3v2.3+ only) + // Time stamp format $xx + // Position $xx (xx ...) + if (($source_data_array['timestampformat'] < 1) || ($source_data_array['timestampformat'] > 2)) { + $this->errors[] = 'Invalid Time Stamp Format in '.$frame_name.' ('.$source_data_array['timestampformat'].') (valid = 1 or 2)'; + } elseif (!$this->IsWithinBitRange($source_data_array['position'], 32, false)) { + $this->errors[] = 'Invalid Position in '.$frame_name.' ('.$source_data_array['position'].') (range = 0 to 4294967295)'; + } else { + $framedata .= chr($source_data_array['timestampformat']); + $framedata .= getid3_lib::BigEndian2String($source_data_array['position'], 4, false); + } + break; + + case 'USER': + // 4.22 USER Terms of use (ID3v2.3+ only) + // Text encoding $xx + // Language $xx xx xx + // The actual text + $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid); + if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) { + $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].')'; + } elseif (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') { + $this->errors[] = 'Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')'; + } else { + $framedata .= chr($source_data_array['encodingid']); + $framedata .= strtolower($source_data_array['language']); + $framedata .= $source_data_array['data']; + } + break; + + case 'OWNE': + // 4.23 OWNE Ownership frame (ID3v2.3+ only) + // Text encoding $xx + // Price paid $00 + // Date of purch. + // Seller + $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid); + if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) { + $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].')'; + } elseif (!$this->IsANumber($source_data_array['pricepaid']['value'], false)) { + $this->errors[] = 'Invalid Price Paid in '.$frame_name.' ('.$source_data_array['pricepaid']['value'].')'; + } elseif (!$this->IsValidDateStampString($source_data_array['purchasedate'])) { + $this->errors[] = 'Invalid Date Of Purchase in '.$frame_name.' ('.$source_data_array['purchasedate'].') (format = YYYYMMDD)'; + } else { + $framedata .= chr($source_data_array['encodingid']); + $framedata .= str_replace("\x00", '', $source_data_array['pricepaid']['value'])."\x00"; + $framedata .= $source_data_array['purchasedate']; + $framedata .= $source_data_array['seller']; + } + break; + + case 'COMR': + // 4.24 COMR Commercial frame (ID3v2.3+ only) + // Text encoding $xx + // Price string $00 + // Valid until + // Contact URL $00 + // Received as $xx + // Name of seller $00 (00) + // Description $00 (00) + // Picture MIME type $00 + // Seller logo + $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid); + if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) { + $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].')'; + } elseif (!$this->IsValidDateStampString($source_data_array['pricevaliduntil'])) { + $this->errors[] = 'Invalid Valid Until date in '.$frame_name.' ('.$source_data_array['pricevaliduntil'].') (format = YYYYMMDD)'; + } elseif (!$this->IsValidURL($source_data_array['contacturl'], false, true)) { + $this->errors[] = 'Invalid Contact URL in '.$frame_name.' ('.$source_data_array['contacturl'].') (allowed schemes: http, https, ftp, mailto)'; + } elseif (!$this->ID3v2IsValidCOMRreceivedAs($source_data_array['receivedasid'])) { + $this->errors[] = 'Invalid Received As byte in '.$frame_name.' ('.$source_data_array['contacturl'].') (range = 0 to 8)'; + } elseif (!$this->IsValidMIMEstring($source_data_array['mime'])) { + $this->errors[] = 'Invalid MIME Type in '.$frame_name.' ('.$source_data_array['mime'].')'; + } else { + $framedata .= chr($source_data_array['encodingid']); + unset($pricestring); + foreach ($source_data_array['price'] as $key => $val) { + if ($this->ID3v2IsValidPriceString($key.$val['value'])) { + $pricestrings[] = $key.$val['value']; + } else { + $this->errors[] = 'Invalid Price String in '.$frame_name.' ('.$key.$val['value'].')'; + } + } + $framedata .= implode('/', $pricestrings); + $framedata .= $source_data_array['pricevaliduntil']; + $framedata .= str_replace("\x00", '', $source_data_array['contacturl'])."\x00"; + $framedata .= chr($source_data_array['receivedasid']); + $framedata .= $source_data_array['sellername'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']); + $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']); + $framedata .= $source_data_array['mime']."\x00"; + $framedata .= $source_data_array['logo']; + } + break; + + case 'ENCR': + // 4.25 ENCR Encryption method registration (ID3v2.3+ only) + // Owner identifier $00 + // Method symbol $xx + // Encryption data + if (!$this->IsWithinBitRange($source_data_array['methodsymbol'], 8, false)) { + $this->errors[] = 'Invalid Group Symbol in '.$frame_name.' ('.$source_data_array['methodsymbol'].') (range = 0 to 255)'; + } else { + $framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00"; + $framedata .= ord($source_data_array['methodsymbol']); + $framedata .= $source_data_array['data']; + } + break; + + case 'GRID': + // 4.26 GRID Group identification registration (ID3v2.3+ only) + // Owner identifier $00 + // Group symbol $xx + // Group dependent data + if (!$this->IsWithinBitRange($source_data_array['groupsymbol'], 8, false)) { + $this->errors[] = 'Invalid Group Symbol in '.$frame_name.' ('.$source_data_array['groupsymbol'].') (range = 0 to 255)'; + } else { + $framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00"; + $framedata .= ord($source_data_array['groupsymbol']); + $framedata .= $source_data_array['data']; + } + break; + + case 'PRIV': + // 4.27 PRIV Private frame (ID3v2.3+ only) + // Owner identifier $00 + // The private data + $framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00"; + $framedata .= $source_data_array['data']; + break; + + case 'SIGN': + // 4.28 SIGN Signature frame (ID3v2.4+ only) + // Group symbol $xx + // Signature + if (!$this->IsWithinBitRange($source_data_array['groupsymbol'], 8, false)) { + $this->errors[] = 'Invalid Group Symbol in '.$frame_name.' ('.$source_data_array['groupsymbol'].') (range = 0 to 255)'; + } else { + $framedata .= ord($source_data_array['groupsymbol']); + $framedata .= $source_data_array['data']; + } + break; + + case 'SEEK': + // 4.29 SEEK Seek frame (ID3v2.4+ only) + // Minimum offset to next tag $xx xx xx xx + if (!$this->IsWithinBitRange($source_data_array['data'], 32, false)) { + $this->errors[] = 'Invalid Minimum Offset in '.$frame_name.' ('.$source_data_array['data'].') (range = 0 to 4294967295)'; + } else { + $framedata .= getid3_lib::BigEndian2String($source_data_array['data'], 4, false); + } + break; + + case 'ASPI': + // 4.30 ASPI Audio seek point index (ID3v2.4+ only) + // Indexed data start (S) $xx xx xx xx + // Indexed data length (L) $xx xx xx xx + // Number of index points (N) $xx xx + // Bits per index point (b) $xx + // Then for every index point the following data is included: + // Fraction at index (Fi) $xx (xx) + if (!$this->IsWithinBitRange($source_data_array['datastart'], 32, false)) { + $this->errors[] = 'Invalid Indexed Data Start in '.$frame_name.' ('.$source_data_array['datastart'].') (range = 0 to 4294967295)'; + } elseif (!$this->IsWithinBitRange($source_data_array['datalength'], 32, false)) { + $this->errors[] = 'Invalid Indexed Data Length in '.$frame_name.' ('.$source_data_array['datalength'].') (range = 0 to 4294967295)'; + } elseif (!$this->IsWithinBitRange($source_data_array['indexpoints'], 16, false)) { + $this->errors[] = 'Invalid Number Of Index Points in '.$frame_name.' ('.$source_data_array['indexpoints'].') (range = 0 to 65535)'; + } elseif (!$this->IsWithinBitRange($source_data_array['bitsperpoint'], 8, false)) { + $this->errors[] = 'Invalid Bits Per Index Point in '.$frame_name.' ('.$source_data_array['bitsperpoint'].') (range = 0 to 255)'; + } elseif ($source_data_array['indexpoints'] != count($source_data_array['indexes'])) { + $this->errors[] = 'Number Of Index Points does not match actual supplied data in '.$frame_name; + } else { + $framedata .= getid3_lib::BigEndian2String($source_data_array['datastart'], 4, false); + $framedata .= getid3_lib::BigEndian2String($source_data_array['datalength'], 4, false); + $framedata .= getid3_lib::BigEndian2String($source_data_array['indexpoints'], 2, false); + $framedata .= getid3_lib::BigEndian2String($source_data_array['bitsperpoint'], 1, false); + foreach ($source_data_array['indexes'] as $key => $val) { + $framedata .= getid3_lib::BigEndian2String($val, ceil($source_data_array['bitsperpoint'] / 8), false); + } + } + break; + + case 'RGAD': + // RGAD Replay Gain Adjustment + // http://privatewww.essex.ac.uk/~djmrob/replaygain/ + // Peak Amplitude $xx $xx $xx $xx + // Radio Replay Gain Adjustment %aaabbbcd %dddddddd + // Audiophile Replay Gain Adjustment %aaabbbcd %dddddddd + // a - name code + // b - originator code + // c - sign bit + // d - replay gain adjustment + + if (($source_data_array['track_adjustment'] > 51) || ($source_data_array['track_adjustment'] < -51)) { + $this->errors[] = 'Invalid Track Adjustment in '.$frame_name.' ('.$source_data_array['track_adjustment'].') (range = -51.0 to +51.0)'; + } elseif (($source_data_array['album_adjustment'] > 51) || ($source_data_array['album_adjustment'] < -51)) { + $this->errors[] = 'Invalid Album Adjustment in '.$frame_name.' ('.$source_data_array['album_adjustment'].') (range = -51.0 to +51.0)'; + } elseif (!$this->ID3v2IsValidRGADname($source_data_array['raw']['track_name'])) { + $this->errors[] = 'Invalid Track Name Code in '.$frame_name.' ('.$source_data_array['raw']['track_name'].') (range = 0 to 2)'; + } elseif (!$this->ID3v2IsValidRGADname($source_data_array['raw']['album_name'])) { + $this->errors[] = 'Invalid Album Name Code in '.$frame_name.' ('.$source_data_array['raw']['album_name'].') (range = 0 to 2)'; + } elseif (!$this->ID3v2IsValidRGADoriginator($source_data_array['raw']['track_originator'])) { + $this->errors[] = 'Invalid Track Originator Code in '.$frame_name.' ('.$source_data_array['raw']['track_originator'].') (range = 0 to 3)'; + } elseif (!$this->ID3v2IsValidRGADoriginator($source_data_array['raw']['album_originator'])) { + $this->errors[] = 'Invalid Album Originator Code in '.$frame_name.' ('.$source_data_array['raw']['album_originator'].') (range = 0 to 3)'; + } else { + $framedata .= getid3_lib::Float2String($source_data_array['peakamplitude'], 32); + $framedata .= getid3_lib::RGADgainString($source_data_array['raw']['track_name'], $source_data_array['raw']['track_originator'], $source_data_array['track_adjustment']); + $framedata .= getid3_lib::RGADgainString($source_data_array['raw']['album_name'], $source_data_array['raw']['album_originator'], $source_data_array['album_adjustment']); + } + break; + + default: + if ($frame_name{0} == 'T') { + // 4.2. T??? Text information frames + // Text encoding $xx + // Information + $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid); + if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) { + $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion; + } else { + $framedata .= chr($source_data_array['encodingid']); + $framedata .= $source_data_array['data']; + } + } elseif ($frame_name{0} == 'W') { + // 4.3. W??? URL link frames + // URL + if (!$this->IsValidURL($source_data_array['data'], false, false)) { + //$this->errors[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')'; + // probably should be an error, need to rewrite IsValidURL() to handle other encodings + $this->warnings[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')'; + } else { + $framedata .= $source_data_array['data']; + } + } else { + $this->errors[] = $frame_name.' not yet supported in $this->GenerateID3v2FrameData()'; + } + break; + } + } + if (!empty($this->errors)) { + return false; + } + return $framedata; + } + + function ID3v2FrameIsAllowed($frame_name, $source_data_array) { + static $PreviousFrames = array(); + + if ($frame_name === null) { + // if the writing functions are called multiple times, the static array needs to be + // cleared - this can be done by calling $this->ID3v2FrameIsAllowed(null, '') + $PreviousFrames = array(); + return true; + } + + if ($this->majorversion == 4) { + switch ($frame_name) { + case 'UFID': + case 'AENC': + case 'ENCR': + case 'GRID': + if (!isset($source_data_array['ownerid'])) { + $this->errors[] = '[ownerid] not specified for '.$frame_name; + } elseif (in_array($frame_name.$source_data_array['ownerid'], $PreviousFrames)) { + $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same OwnerID ('.$source_data_array['ownerid'].')'; + } else { + $PreviousFrames[] = $frame_name.$source_data_array['ownerid']; + } + break; + + case 'TXXX': + case 'WXXX': + case 'RVA2': + case 'EQU2': + case 'APIC': + case 'GEOB': + if (!isset($source_data_array['description'])) { + $this->errors[] = '[description] not specified for '.$frame_name; + } elseif (in_array($frame_name.$source_data_array['description'], $PreviousFrames)) { + $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Description ('.$source_data_array['description'].')'; + } else { + $PreviousFrames[] = $frame_name.$source_data_array['description']; + } + break; + + case 'USER': + if (!isset($source_data_array['language'])) { + $this->errors[] = '[language] not specified for '.$frame_name; + } elseif (in_array($frame_name.$source_data_array['language'], $PreviousFrames)) { + $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Language ('.$source_data_array['language'].')'; + } else { + $PreviousFrames[] = $frame_name.$source_data_array['language']; + } + break; + + case 'USLT': + case 'SYLT': + case 'COMM': + if (!isset($source_data_array['language'])) { + $this->errors[] = '[language] not specified for '.$frame_name; + } elseif (!isset($source_data_array['description'])) { + $this->errors[] = '[description] not specified for '.$frame_name; + } elseif (in_array($frame_name.$source_data_array['language'].$source_data_array['description'], $PreviousFrames)) { + $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Language + Description ('.$source_data_array['language'].' + '.$source_data_array['description'].')'; + } else { + $PreviousFrames[] = $frame_name.$source_data_array['language'].$source_data_array['description']; + } + break; + + case 'POPM': + if (!isset($source_data_array['email'])) { + $this->errors[] = '[email] not specified for '.$frame_name; + } elseif (in_array($frame_name.$source_data_array['email'], $PreviousFrames)) { + $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Email ('.$source_data_array['email'].')'; + } else { + $PreviousFrames[] = $frame_name.$source_data_array['email']; + } + break; + + case 'IPLS': + case 'MCDI': + case 'ETCO': + case 'MLLT': + case 'SYTC': + case 'RVRB': + case 'PCNT': + case 'RBUF': + case 'POSS': + case 'OWNE': + case 'SEEK': + case 'ASPI': + case 'RGAD': + if (in_array($frame_name, $PreviousFrames)) { + $this->errors[] = 'Only one '.$frame_name.' tag allowed'; + } else { + $PreviousFrames[] = $frame_name; + } + break; + + case 'LINK': + // this isn't implemented quite right (yet) - it should check the target frame data for compliance + // but right now it just allows one linked frame of each type, to be safe. + if (!isset($source_data_array['frameid'])) { + $this->errors[] = '[frameid] not specified for '.$frame_name; + } elseif (in_array($frame_name.$source_data_array['frameid'], $PreviousFrames)) { + $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same FrameID ('.$source_data_array['frameid'].')'; + } elseif (in_array($source_data_array['frameid'], $PreviousFrames)) { + // no links to singleton tags + $this->errors[] = 'Cannot specify a '.$frame_name.' tag to a singleton tag that already exists ('.$source_data_array['frameid'].')'; + } else { + $PreviousFrames[] = $frame_name.$source_data_array['frameid']; // only one linked tag of this type + $PreviousFrames[] = $source_data_array['frameid']; // no non-linked singleton tags of this type + } + break; + + case 'COMR': + // There may be more than one 'commercial frame' in a tag, but no two may be identical + // Checking isn't implemented at all (yet) - just assumes that it's OK. + break; + + case 'PRIV': + case 'SIGN': + if (!isset($source_data_array['ownerid'])) { + $this->errors[] = '[ownerid] not specified for '.$frame_name; + } elseif (!isset($source_data_array['data'])) { + $this->errors[] = '[data] not specified for '.$frame_name; + } elseif (in_array($frame_name.$source_data_array['ownerid'].$source_data_array['data'], $PreviousFrames)) { + $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same OwnerID + Data ('.$source_data_array['ownerid'].' + '.$source_data_array['data'].')'; + } else { + $PreviousFrames[] = $frame_name.$source_data_array['ownerid'].$source_data_array['data']; + } + break; + + default: + if (($frame_name{0} != 'T') && ($frame_name{0} != 'W')) { + $this->errors[] = 'Frame not allowed in ID3v2.'.$this->majorversion.': '.$frame_name; + } + break; + } + + } elseif ($this->majorversion == 3) { + + switch ($frame_name) { + case 'UFID': + case 'AENC': + case 'ENCR': + case 'GRID': + if (!isset($source_data_array['ownerid'])) { + $this->errors[] = '[ownerid] not specified for '.$frame_name; + } elseif (in_array($frame_name.$source_data_array['ownerid'], $PreviousFrames)) { + $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same OwnerID ('.$source_data_array['ownerid'].')'; + } else { + $PreviousFrames[] = $frame_name.$source_data_array['ownerid']; + } + break; + + case 'TXXX': + case 'WXXX': + case 'APIC': + case 'GEOB': + if (!isset($source_data_array['description'])) { + $this->errors[] = '[description] not specified for '.$frame_name; + } elseif (in_array($frame_name.$source_data_array['description'], $PreviousFrames)) { + $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Description ('.$source_data_array['description'].')'; + } else { + $PreviousFrames[] = $frame_name.$source_data_array['description']; + } + break; + + case 'USER': + if (!isset($source_data_array['language'])) { + $this->errors[] = '[language] not specified for '.$frame_name; + } elseif (in_array($frame_name.$source_data_array['language'], $PreviousFrames)) { + $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Language ('.$source_data_array['language'].')'; + } else { + $PreviousFrames[] = $frame_name.$source_data_array['language']; + } + break; + + case 'USLT': + case 'SYLT': + case 'COMM': + if (!isset($source_data_array['language'])) { + $this->errors[] = '[language] not specified for '.$frame_name; + } elseif (!isset($source_data_array['description'])) { + $this->errors[] = '[description] not specified for '.$frame_name; + } elseif (in_array($frame_name.$source_data_array['language'].$source_data_array['description'], $PreviousFrames)) { + $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Language + Description ('.$source_data_array['language'].' + '.$source_data_array['description'].')'; + } else { + $PreviousFrames[] = $frame_name.$source_data_array['language'].$source_data_array['description']; + } + break; + + case 'POPM': + if (!isset($source_data_array['email'])) { + $this->errors[] = '[email] not specified for '.$frame_name; + } elseif (in_array($frame_name.$source_data_array['email'], $PreviousFrames)) { + $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Email ('.$source_data_array['email'].')'; + } else { + $PreviousFrames[] = $frame_name.$source_data_array['email']; + } + break; + + case 'IPLS': + case 'MCDI': + case 'ETCO': + case 'MLLT': + case 'SYTC': + case 'RVAD': + case 'EQUA': + case 'RVRB': + case 'PCNT': + case 'RBUF': + case 'POSS': + case 'OWNE': + case 'RGAD': + if (in_array($frame_name, $PreviousFrames)) { + $this->errors[] = 'Only one '.$frame_name.' tag allowed'; + } else { + $PreviousFrames[] = $frame_name; + } + break; + + case 'LINK': + // this isn't implemented quite right (yet) - it should check the target frame data for compliance + // but right now it just allows one linked frame of each type, to be safe. + if (!isset($source_data_array['frameid'])) { + $this->errors[] = '[frameid] not specified for '.$frame_name; + } elseif (in_array($frame_name.$source_data_array['frameid'], $PreviousFrames)) { + $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same FrameID ('.$source_data_array['frameid'].')'; + } elseif (in_array($source_data_array['frameid'], $PreviousFrames)) { + // no links to singleton tags + $this->errors[] = 'Cannot specify a '.$frame_name.' tag to a singleton tag that already exists ('.$source_data_array['frameid'].')'; + } else { + $PreviousFrames[] = $frame_name.$source_data_array['frameid']; // only one linked tag of this type + $PreviousFrames[] = $source_data_array['frameid']; // no non-linked singleton tags of this type + } + break; + + case 'COMR': + // There may be more than one 'commercial frame' in a tag, but no two may be identical + // Checking isn't implemented at all (yet) - just assumes that it's OK. + break; + + case 'PRIV': + if (!isset($source_data_array['ownerid'])) { + $this->errors[] = '[ownerid] not specified for '.$frame_name; + } elseif (!isset($source_data_array['data'])) { + $this->errors[] = '[data] not specified for '.$frame_name; + } elseif (in_array($frame_name.$source_data_array['ownerid'].$source_data_array['data'], $PreviousFrames)) { + $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same OwnerID + Data ('.$source_data_array['ownerid'].' + '.$source_data_array['data'].')'; + } else { + $PreviousFrames[] = $frame_name.$source_data_array['ownerid'].$source_data_array['data']; + } + break; + + default: + if (($frame_name{0} != 'T') && ($frame_name{0} != 'W')) { + $this->errors[] = 'Frame not allowed in ID3v2.'.$this->majorversion.': '.$frame_name; + } + break; + } + + } elseif ($this->majorversion == 2) { + + switch ($frame_name) { + case 'UFI': + case 'CRM': + case 'CRA': + if (!isset($source_data_array['ownerid'])) { + $this->errors[] = '[ownerid] not specified for '.$frame_name; + } elseif (in_array($frame_name.$source_data_array['ownerid'], $PreviousFrames)) { + $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same OwnerID ('.$source_data_array['ownerid'].')'; + } else { + $PreviousFrames[] = $frame_name.$source_data_array['ownerid']; + } + break; + + case 'TXX': + case 'WXX': + case 'PIC': + case 'GEO': + if (!isset($source_data_array['description'])) { + $this->errors[] = '[description] not specified for '.$frame_name; + } elseif (in_array($frame_name.$source_data_array['description'], $PreviousFrames)) { + $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Description ('.$source_data_array['description'].')'; + } else { + $PreviousFrames[] = $frame_name.$source_data_array['description']; + } + break; + + case 'ULT': + case 'SLT': + case 'COM': + if (!isset($source_data_array['language'])) { + $this->errors[] = '[language] not specified for '.$frame_name; + } elseif (!isset($source_data_array['description'])) { + $this->errors[] = '[description] not specified for '.$frame_name; + } elseif (in_array($frame_name.$source_data_array['language'].$source_data_array['description'], $PreviousFrames)) { + $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Language + Description ('.$source_data_array['language'].' + '.$source_data_array['description'].')'; + } else { + $PreviousFrames[] = $frame_name.$source_data_array['language'].$source_data_array['description']; + } + break; + + case 'POP': + if (!isset($source_data_array['email'])) { + $this->errors[] = '[email] not specified for '.$frame_name; + } elseif (in_array($frame_name.$source_data_array['email'], $PreviousFrames)) { + $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Email ('.$source_data_array['email'].')'; + } else { + $PreviousFrames[] = $frame_name.$source_data_array['email']; + } + break; + + case 'IPL': + case 'MCI': + case 'ETC': + case 'MLL': + case 'STC': + case 'RVA': + case 'EQU': + case 'REV': + case 'CNT': + case 'BUF': + if (in_array($frame_name, $PreviousFrames)) { + $this->errors[] = 'Only one '.$frame_name.' tag allowed'; + } else { + $PreviousFrames[] = $frame_name; + } + break; + + case 'LNK': + // this isn't implemented quite right (yet) - it should check the target frame data for compliance + // but right now it just allows one linked frame of each type, to be safe. + if (!isset($source_data_array['frameid'])) { + $this->errors[] = '[frameid] not specified for '.$frame_name; + } elseif (in_array($frame_name.$source_data_array['frameid'], $PreviousFrames)) { + $this->errors[] = 'Only one '.$frame_name.' tag allowed with the same FrameID ('.$source_data_array['frameid'].')'; + } elseif (in_array($source_data_array['frameid'], $PreviousFrames)) { + // no links to singleton tags + $this->errors[] = 'Cannot specify a '.$frame_name.' tag to a singleton tag that already exists ('.$source_data_array['frameid'].')'; + } else { + $PreviousFrames[] = $frame_name.$source_data_array['frameid']; // only one linked tag of this type + $PreviousFrames[] = $source_data_array['frameid']; // no non-linked singleton tags of this type + } + break; + + default: + if (($frame_name{0} != 'T') && ($frame_name{0} != 'W')) { + $this->errors[] = 'Frame not allowed in ID3v2.'.$this->majorversion.': '.$frame_name; + } + break; + } + } + + if (!empty($this->errors)) { + return false; + } + return true; + } + + function GenerateID3v2Tag($noerrorsonly=true) { + $this->ID3v2FrameIsAllowed(null, ''); // clear static array in case this isn't the first call to $this->GenerateID3v2Tag() + + $tagstring = ''; + if (is_array($this->tag_data)) { + foreach ($this->tag_data as $frame_name => $frame_rawinputdata) { + foreach ($frame_rawinputdata as $irrelevantindex => $source_data_array) { + if (getid3_id3v2::IsValidID3v2FrameName($frame_name, $this->majorversion)) { + unset($frame_length); + unset($frame_flags); + $frame_data = false; + if ($this->ID3v2FrameIsAllowed($frame_name, $source_data_array)) { + if ($frame_data = $this->GenerateID3v2FrameData($frame_name, $source_data_array)) { + $FrameUnsynchronisation = false; + if ($this->majorversion >= 4) { + // frame-level unsynchronisation + $unsynchdata = $frame_data; + if ($this->id3v2_use_unsynchronisation) { + $unsynchdata = $this->Unsynchronise($frame_data); + } + if (strlen($unsynchdata) != strlen($frame_data)) { + // unsynchronisation needed + $FrameUnsynchronisation = true; + $frame_data = $unsynchdata; + if (isset($TagUnsynchronisation) && $TagUnsynchronisation === false) { + // only set to true if ALL frames are unsynchronised + } else { + $TagUnsynchronisation = true; + } + } else { + if (isset($TagUnsynchronisation)) { + $TagUnsynchronisation = false; + } + } + unset($unsynchdata); + + $frame_length = getid3_lib::BigEndian2String(strlen($frame_data), 4, true); + } else { + $frame_length = getid3_lib::BigEndian2String(strlen($frame_data), 4, false); + } + $frame_flags = $this->GenerateID3v2FrameFlags($this->ID3v2FrameFlagsLookupTagAlter($frame_name), $this->ID3v2FrameFlagsLookupFileAlter($frame_name), false, false, false, false, $FrameUnsynchronisation, false); + } + } else { + $this->errors[] = 'Frame "'.$frame_name.'" is NOT allowed'; + } + if ($frame_data === false) { + $this->errors[] = '$this->GenerateID3v2FrameData() failed for "'.$frame_name.'"'; + if ($noerrorsonly) { + return false; + } else { + unset($frame_name); + } + } + } else { + // ignore any invalid frame names, including 'title', 'header', etc + $this->warnings[] = 'Ignoring invalid ID3v2 frame type: "'.$frame_name.'"'; + unset($frame_name); + unset($frame_length); + unset($frame_flags); + unset($frame_data); + } + if (isset($frame_name) && isset($frame_length) && isset($frame_flags) && isset($frame_data)) { + $tagstring .= $frame_name.$frame_length.$frame_flags.$frame_data; + } + } + } + + if (!isset($TagUnsynchronisation)) { + $TagUnsynchronisation = false; + } + if (($this->majorversion <= 3) && $this->id3v2_use_unsynchronisation) { + // tag-level unsynchronisation + $unsynchdata = $this->Unsynchronise($tagstring); + if (strlen($unsynchdata) != strlen($tagstring)) { + // unsynchronisation needed + $TagUnsynchronisation = true; + $tagstring = $unsynchdata; + } + } + + while ($this->paddedlength < (strlen($tagstring) + getid3_id3v2::ID3v2HeaderLength($this->majorversion))) { + $this->paddedlength += 1024; + } + + $footer = false; // ID3v2 footers not yet supported in getID3() + if (!$footer && ($this->paddedlength > (strlen($tagstring) + getid3_id3v2::ID3v2HeaderLength($this->majorversion)))) { + // pad up to $paddedlength bytes if unpadded tag is shorter than $paddedlength + // "Furthermore it MUST NOT have any padding when a tag footer is added to the tag." + $tagstring .= @str_repeat("\x00", $this->paddedlength - strlen($tagstring) - getid3_id3v2::ID3v2HeaderLength($this->majorversion)); + } + if ($this->id3v2_use_unsynchronisation && (substr($tagstring, strlen($tagstring) - 1, 1) == "\xFF")) { + // special unsynchronisation case: + // if last byte == $FF then appended a $00 + $TagUnsynchronisation = true; + $tagstring .= "\x00"; + } + + $tagheader = 'ID3'; + $tagheader .= chr($this->majorversion); + $tagheader .= chr($this->minorversion); + $tagheader .= $this->GenerateID3v2TagFlags(array('unsynchronisation'=>$TagUnsynchronisation)); + $tagheader .= getid3_lib::BigEndian2String(strlen($tagstring), 4, true); + + return $tagheader.$tagstring; + } + $this->errors[] = 'tag_data is not an array in GenerateID3v2Tag()'; + return false; + } + + function ID3v2IsValidPriceString($pricestring) { + if (getid3_id3v2::LanguageLookup(substr($pricestring, 0, 3), true) == '') { + return false; + } elseif (!$this->IsANumber(substr($pricestring, 3), true)) { + return false; + } + return true; + } + + function ID3v2FrameFlagsLookupTagAlter($framename) { + // unfinished + switch ($framename) { + case 'RGAD': + $allow = true; + default: + $allow = false; + break; + } + return $allow; + } + + function ID3v2FrameFlagsLookupFileAlter($framename) { + // unfinished + switch ($framename) { + case 'RGAD': + return false; + break; + + default: + return false; + break; + } + } + + function ID3v2IsValidETCOevent($eventid) { + if (($eventid < 0) || ($eventid > 0xFF)) { + // outside range of 1 byte + return false; + } elseif (($eventid >= 0xF0) && ($eventid <= 0xFC)) { + // reserved for future use + return false; + } elseif (($eventid >= 0x17) && ($eventid <= 0xDF)) { + // reserved for future use + return false; + } elseif (($eventid >= 0x0E) && ($eventid <= 0x16) && ($this->majorversion == 2)) { + // not defined in ID3v2.2 + return false; + } elseif (($eventid >= 0x15) && ($eventid <= 0x16) && ($this->majorversion == 3)) { + // not defined in ID3v2.3 + return false; + } + return true; + } + + function ID3v2IsValidSYLTtype($contenttype) { + if (($contenttype >= 0) && ($contenttype <= 8) && ($this->majorversion == 4)) { + return true; + } elseif (($contenttype >= 0) && ($contenttype <= 6) && ($this->majorversion == 3)) { + return true; + } + return false; + } + + function ID3v2IsValidRVA2channeltype($channeltype) { + if (($channeltype >= 0) && ($channeltype <= 8) && ($this->majorversion == 4)) { + return true; + } + return false; + } + + function ID3v2IsValidAPICpicturetype($picturetype) { + if (($picturetype >= 0) && ($picturetype <= 0x14) && ($this->majorversion >= 2) && ($this->majorversion <= 4)) { + return true; + } + return false; + } + + function ID3v2IsValidAPICimageformat($imageformat) { + if ($imageformat == '-->') { + return true; + } elseif ($this->majorversion == 2) { + if ((strlen($imageformat) == 3) && ($imageformat == strtoupper($imageformat))) { + return true; + } + } elseif (($this->majorversion == 3) || ($this->majorversion == 4)) { + if ($this->IsValidMIMEstring($imageformat)) { + return true; + } + } + return false; + } + + function ID3v2IsValidCOMRreceivedAs($receivedas) { + if (($this->majorversion >= 3) && ($receivedas >= 0) && ($receivedas <= 8)) { + return true; + } + return false; + } + + function ID3v2IsValidRGADname($RGADname) { + if (($RGADname >= 0) && ($RGADname <= 2)) { + return true; + } + return false; + } + + function ID3v2IsValidRGADoriginator($RGADoriginator) { + if (($RGADoriginator >= 0) && ($RGADoriginator <= 3)) { + return true; + } + return false; + } + + function ID3v2IsValidTextEncoding($textencodingbyte) { + static $ID3v2IsValidTextEncoding_cache = array( + 2 => array(true, true), + 3 => array(true, true), + 4 => array(true, true, true, true)); + return isset($ID3v2IsValidTextEncoding_cache[$this->majorversion][$textencodingbyte]); + } + + function Unsynchronise($data) { + // Whenever a false synchronisation is found within the tag, one zeroed + // byte is inserted after the first false synchronisation byte. The + // format of a correct sync that should be altered by ID3 encoders is as + // follows: + // %11111111 111xxxxx + // And should be replaced with: + // %11111111 00000000 111xxxxx + // This has the side effect that all $FF 00 combinations have to be + // altered, so they won't be affected by the decoding process. Therefore + // all the $FF 00 combinations have to be replaced with the $FF 00 00 + // combination during the unsynchronisation. + + $data = str_replace("\xFF\x00", "\xFF\x00\x00", $data); + $unsyncheddata = ''; + $datalength = strlen($data); + for ($i = 0; $i < $datalength; $i++) { + $thischar = $data{$i}; + $unsyncheddata .= $thischar; + if ($thischar == "\xFF") { + $nextchar = ord($data{$i + 1}); + if (($nextchar & 0xE0) == 0xE0) { + // previous byte = 11111111, this byte = 111????? + $unsyncheddata .= "\x00"; + } + } + } + return $unsyncheddata; + } + + function is_hash($var) { + // written by dev-null�christophe*vg + // taken from http://www.php.net/manual/en/function.array-merge-recursive.php + if (is_array($var)) { + $keys = array_keys($var); + $all_num = true; + for ($i = 0; $i < count($keys); $i++) { + if (is_string($keys[$i])) { + return true; + } + } + } + return false; + } + + function array_join_merge($arr1, $arr2) { + // written by dev-null�christophe*vg + // taken from http://www.php.net/manual/en/function.array-merge-recursive.php + if (is_array($arr1) && is_array($arr2)) { + // the same -> merge + $new_array = array(); + + if ($this->is_hash($arr1) && $this->is_hash($arr2)) { + // hashes -> merge based on keys + $keys = array_merge(array_keys($arr1), array_keys($arr2)); + foreach ($keys as $key) { + $new_array[$key] = $this->array_join_merge(@$arr1[$key], @$arr2[$key]); + } + } else { + // two real arrays -> merge + $new_array = array_reverse(array_unique(array_reverse(array_merge($arr1, $arr2)))); + } + return $new_array; + } else { + // not the same ... take new one if defined, else the old one stays + return $arr2 ? $arr2 : $arr1; + } + } + + function IsValidMIMEstring($mimestring) { + if ((strlen($mimestring) >= 3) && (strpos($mimestring, '/') > 0) && (strpos($mimestring, '/') < (strlen($mimestring) - 1))) { + return true; + } + return false; + } + + function IsWithinBitRange($number, $maxbits, $signed=false) { + if ($signed) { + if (($number > (0 - pow(2, $maxbits - 1))) && ($number <= pow(2, $maxbits - 1))) { + return true; + } + } else { + if (($number >= 0) && ($number <= pow(2, $maxbits))) { + return true; + } + } + return false; + } + + function safe_parse_url($url) { + $parts = @parse_url($url); + $parts['scheme'] = (isset($parts['scheme']) ? $parts['scheme'] : ''); + $parts['host'] = (isset($parts['host']) ? $parts['host'] : ''); + $parts['user'] = (isset($parts['user']) ? $parts['user'] : ''); + $parts['pass'] = (isset($parts['pass']) ? $parts['pass'] : ''); + $parts['path'] = (isset($parts['path']) ? $parts['path'] : ''); + $parts['query'] = (isset($parts['query']) ? $parts['query'] : ''); + return $parts; + } + + function IsValidURL($url, $allowUserPass=false) { + if ($url == '') { + return false; + } + if ($allowUserPass !== true) { + if (strstr($url, '@')) { + // in the format http://user:pass@example.com or http://user@example.com + // but could easily be somebody incorrectly entering an email address in place of a URL + return false; + } + } + if ($parts = $this->safe_parse_url($url)) { + if (($parts['scheme'] != 'http') && ($parts['scheme'] != 'https') && ($parts['scheme'] != 'ftp') && ($parts['scheme'] != 'gopher')) { + return false; + } elseif (!preg_match("/^[[:alnum:]]([-.]?[0-9a-z])*\.[a-z]{2,3}$/i", $parts['host'], $regs) && !IsValidDottedIP($parts['host'])) { + return false; + } elseif (!preg_match("/^([[:alnum:]-]|[\_])*$/i", $parts['user'], $regs)) { + return false; + } elseif (!preg_match("/^([[:alnum:]-]|[\_])*$/i", $parts['pass'], $regs)) { + return false; + } elseif (!preg_match("/^[[:alnum:]/_\.@~-]*$/i", $parts['path'], $regs)) { + return false; + } elseif (!preg_match("/^[[:alnum:]?&=+:;_()%#/,\.-]*$/i", $parts['query'], $regs)) { + return false; + } else { + return true; + } + } + return false; + } + + function ID3v2ShortFrameNameLookup($majorversion, $long_description) { + $long_description = str_replace(' ', '_', strtolower(trim($long_description))); + static $ID3v2ShortFrameNameLookup = array(); + if (empty($ID3v2ShortFrameNameLookup)) { + + // The following are unique to ID3v2.2 + $ID3v2ShortFrameNameLookup[2]['comment'] = 'COM'; + $ID3v2ShortFrameNameLookup[2]['album'] = 'TAL'; + $ID3v2ShortFrameNameLookup[2]['beats_per_minute'] = 'TBP'; + $ID3v2ShortFrameNameLookup[2]['composer'] = 'TCM'; + $ID3v2ShortFrameNameLookup[2]['genre'] = 'TCO'; + $ID3v2ShortFrameNameLookup[2]['copyright'] = 'TCR'; + $ID3v2ShortFrameNameLookup[2]['encoded_by'] = 'TEN'; + $ID3v2ShortFrameNameLookup[2]['language'] = 'TLA'; + $ID3v2ShortFrameNameLookup[2]['length'] = 'TLE'; + $ID3v2ShortFrameNameLookup[2]['original_artist'] = 'TOA'; + $ID3v2ShortFrameNameLookup[2]['original_filename'] = 'TOF'; + $ID3v2ShortFrameNameLookup[2]['original_lyricist'] = 'TOL'; + $ID3v2ShortFrameNameLookup[2]['original_album_title'] = 'TOT'; + $ID3v2ShortFrameNameLookup[2]['artist'] = 'TP1'; + $ID3v2ShortFrameNameLookup[2]['band'] = 'TP2'; + $ID3v2ShortFrameNameLookup[2]['conductor'] = 'TP3'; + $ID3v2ShortFrameNameLookup[2]['remixer'] = 'TP4'; + $ID3v2ShortFrameNameLookup[2]['publisher'] = 'TPB'; + $ID3v2ShortFrameNameLookup[2]['isrc'] = 'TRC'; + $ID3v2ShortFrameNameLookup[2]['tracknumber'] = 'TRK'; + $ID3v2ShortFrameNameLookup[2]['size'] = 'TSI'; + $ID3v2ShortFrameNameLookup[2]['encoder_settings'] = 'TSS'; + $ID3v2ShortFrameNameLookup[2]['description'] = 'TT1'; + $ID3v2ShortFrameNameLookup[2]['title'] = 'TT2'; + $ID3v2ShortFrameNameLookup[2]['subtitle'] = 'TT3'; + $ID3v2ShortFrameNameLookup[2]['lyricist'] = 'TXT'; + $ID3v2ShortFrameNameLookup[2]['user_text'] = 'TXX'; + $ID3v2ShortFrameNameLookup[2]['year'] = 'TYE'; + $ID3v2ShortFrameNameLookup[2]['unique_file_identifier'] = 'UFI'; + $ID3v2ShortFrameNameLookup[2]['unsynchronised_lyrics'] = 'ULT'; + $ID3v2ShortFrameNameLookup[2]['url_file'] = 'WAF'; + $ID3v2ShortFrameNameLookup[2]['url_artist'] = 'WAR'; + $ID3v2ShortFrameNameLookup[2]['url_source'] = 'WAS'; + $ID3v2ShortFrameNameLookup[2]['copyright_information'] = 'WCP'; + $ID3v2ShortFrameNameLookup[2]['url_publisher'] = 'WPB'; + $ID3v2ShortFrameNameLookup[2]['url_user'] = 'WXX'; + + // The following are common to ID3v2.3 and ID3v2.4 + $ID3v2ShortFrameNameLookup[3]['audio_encryption'] = 'AENC'; + $ID3v2ShortFrameNameLookup[3]['attached_picture'] = 'APIC'; + $ID3v2ShortFrameNameLookup[3]['comment'] = 'COMM'; + $ID3v2ShortFrameNameLookup[3]['commercial'] = 'COMR'; + $ID3v2ShortFrameNameLookup[3]['encryption_method_registration'] = 'ENCR'; + $ID3v2ShortFrameNameLookup[3]['event_timing_codes'] = 'ETCO'; + $ID3v2ShortFrameNameLookup[3]['general_encapsulated_object'] = 'GEOB'; + $ID3v2ShortFrameNameLookup[3]['group_identification_registration'] = 'GRID'; + $ID3v2ShortFrameNameLookup[3]['linked_information'] = 'LINK'; + $ID3v2ShortFrameNameLookup[3]['music_cd_identifier'] = 'MCDI'; + $ID3v2ShortFrameNameLookup[3]['mpeg_location_lookup_table'] = 'MLLT'; + $ID3v2ShortFrameNameLookup[3]['ownership'] = 'OWNE'; + $ID3v2ShortFrameNameLookup[3]['play_counter'] = 'PCNT'; + $ID3v2ShortFrameNameLookup[3]['popularimeter'] = 'POPM'; + $ID3v2ShortFrameNameLookup[3]['position_synchronisation'] = 'POSS'; + $ID3v2ShortFrameNameLookup[3]['private'] = 'PRIV'; + $ID3v2ShortFrameNameLookup[3]['recommended_buffer_size'] = 'RBUF'; + $ID3v2ShortFrameNameLookup[3]['reverb'] = 'RVRB'; + $ID3v2ShortFrameNameLookup[3]['synchronised_lyrics'] = 'SYLT'; + $ID3v2ShortFrameNameLookup[3]['synchronised_tempo_codes'] = 'SYTC'; + $ID3v2ShortFrameNameLookup[3]['album'] = 'TALB'; + $ID3v2ShortFrameNameLookup[3]['beats_per_minute'] = 'TBPM'; + $ID3v2ShortFrameNameLookup[3]['composer'] = 'TCOM'; + $ID3v2ShortFrameNameLookup[3]['genre'] = 'TCON'; + $ID3v2ShortFrameNameLookup[3]['copyright'] = 'TCOP'; + $ID3v2ShortFrameNameLookup[3]['playlist_delay'] = 'TDLY'; + $ID3v2ShortFrameNameLookup[3]['encoded_by'] = 'TENC'; + $ID3v2ShortFrameNameLookup[3]['lyricist'] = 'TEXT'; + $ID3v2ShortFrameNameLookup[3]['file_type'] = 'TFLT'; + $ID3v2ShortFrameNameLookup[3]['content_group_description'] = 'TIT1'; + $ID3v2ShortFrameNameLookup[3]['title'] = 'TIT2'; + $ID3v2ShortFrameNameLookup[3]['subtitle'] = 'TIT3'; + $ID3v2ShortFrameNameLookup[3]['initial_key'] = 'TKEY'; + $ID3v2ShortFrameNameLookup[3]['language'] = 'TLAN'; + $ID3v2ShortFrameNameLookup[3]['length'] = 'TLEN'; + $ID3v2ShortFrameNameLookup[3]['media_type'] = 'TMED'; + $ID3v2ShortFrameNameLookup[3]['original_album_title'] = 'TOAL'; + $ID3v2ShortFrameNameLookup[3]['original_filename'] = 'TOFN'; + $ID3v2ShortFrameNameLookup[3]['original_lyricist'] = 'TOLY'; + $ID3v2ShortFrameNameLookup[3]['original_artist'] = 'TOPE'; + $ID3v2ShortFrameNameLookup[3]['file_owner'] = 'TOWN'; + $ID3v2ShortFrameNameLookup[3]['artist'] = 'TPE1'; + $ID3v2ShortFrameNameLookup[3]['band'] = 'TPE2'; + $ID3v2ShortFrameNameLookup[3]['conductor'] = 'TPE3'; + $ID3v2ShortFrameNameLookup[3]['remixer'] = 'TPE4'; + $ID3v2ShortFrameNameLookup[3]['part_of_set'] = 'TPOS'; + $ID3v2ShortFrameNameLookup[3]['publisher'] = 'TPUB'; + $ID3v2ShortFrameNameLookup[3]['tracknumber'] = 'TRCK'; + $ID3v2ShortFrameNameLookup[3]['internet_radio_station_name'] = 'TRSN'; + $ID3v2ShortFrameNameLookup[3]['internet_radio_station_owner'] = 'TRSO'; + $ID3v2ShortFrameNameLookup[3]['isrc'] = 'TSRC'; + $ID3v2ShortFrameNameLookup[3]['encoder_settings'] = 'TSSE'; + $ID3v2ShortFrameNameLookup[3]['user_text'] = 'TXXX'; + $ID3v2ShortFrameNameLookup[3]['unique_file_identifier'] = 'UFID'; + $ID3v2ShortFrameNameLookup[3]['terms_of_use'] = 'USER'; + $ID3v2ShortFrameNameLookup[3]['unsynchronised_lyrics'] = 'USLT'; + $ID3v2ShortFrameNameLookup[3]['commercial'] = 'WCOM'; + $ID3v2ShortFrameNameLookup[3]['copyright_information'] = 'WCOP'; + $ID3v2ShortFrameNameLookup[3]['url_file'] = 'WOAF'; + $ID3v2ShortFrameNameLookup[3]['url_artist'] = 'WOAR'; + $ID3v2ShortFrameNameLookup[3]['url_source'] = 'WOAS'; + $ID3v2ShortFrameNameLookup[3]['url_station'] = 'WORS'; + $ID3v2ShortFrameNameLookup[3]['payment'] = 'WPAY'; + $ID3v2ShortFrameNameLookup[3]['url_publisher'] = 'WPUB'; + $ID3v2ShortFrameNameLookup[3]['url_user'] = 'WXXX'; + + // The above are common to ID3v2.3 and ID3v2.4 + // so copy them to ID3v2.4 before adding specifics for 2.3 and 2.4 + $ID3v2ShortFrameNameLookup[4] = $ID3v2ShortFrameNameLookup[3]; + + // The following are unique to ID3v2.3 + $ID3v2ShortFrameNameLookup[3]['equalisation'] = 'EQUA'; + $ID3v2ShortFrameNameLookup[3]['involved_people_list'] = 'IPLS'; + $ID3v2ShortFrameNameLookup[3]['relative_volume_adjustment'] = 'RVAD'; + $ID3v2ShortFrameNameLookup[3]['date'] = 'TDAT'; + $ID3v2ShortFrameNameLookup[3]['time'] = 'TIME'; + $ID3v2ShortFrameNameLookup[3]['original_release_year'] = 'TORY'; + $ID3v2ShortFrameNameLookup[3]['recording_dates'] = 'TRDA'; + $ID3v2ShortFrameNameLookup[3]['size'] = 'TSIZ'; + $ID3v2ShortFrameNameLookup[3]['year'] = 'TYER'; + + + // The following are unique to ID3v2.4 + $ID3v2ShortFrameNameLookup[4]['audio_seek_point_index'] = 'ASPI'; + $ID3v2ShortFrameNameLookup[4]['equalisation'] = 'EQU2'; + $ID3v2ShortFrameNameLookup[4]['relative_volume_adjustment'] = 'RVA2'; + $ID3v2ShortFrameNameLookup[4]['seek'] = 'SEEK'; + $ID3v2ShortFrameNameLookup[4]['signature'] = 'SIGN'; + $ID3v2ShortFrameNameLookup[4]['encoding_time'] = 'TDEN'; + $ID3v2ShortFrameNameLookup[4]['original_release_time'] = 'TDOR'; + $ID3v2ShortFrameNameLookup[4]['recording_time'] = 'TDRC'; + $ID3v2ShortFrameNameLookup[4]['release_time'] = 'TDRL'; + $ID3v2ShortFrameNameLookup[4]['tagging_time'] = 'TDTG'; + $ID3v2ShortFrameNameLookup[4]['involved_people_list'] = 'TIPL'; + $ID3v2ShortFrameNameLookup[4]['musician_credits_list'] = 'TMCL'; + $ID3v2ShortFrameNameLookup[4]['mood'] = 'TMOO'; + $ID3v2ShortFrameNameLookup[4]['produced_notice'] = 'TPRO'; + $ID3v2ShortFrameNameLookup[4]['album_sort_order'] = 'TSOA'; + $ID3v2ShortFrameNameLookup[4]['performer_sort_order'] = 'TSOP'; + $ID3v2ShortFrameNameLookup[4]['title_sort_order'] = 'TSOT'; + $ID3v2ShortFrameNameLookup[4]['set_subtitle'] = 'TSST'; + } + return @$ID3v2ShortFrameNameLookup[$majorversion][strtolower($long_description)]; + + } + +} + +?> diff --git a/includes/getid3/getid3/write.lyrics3.php b/includes/getid3/getid3/write.lyrics3.php new file mode 100644 index 0000000..6b8a47d --- /dev/null +++ b/includes/getid3/getid3/write.lyrics3.php @@ -0,0 +1,78 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// write.lyrics3.php // +// module for writing Lyrics3 tags // +// dependencies: module.tag.lyrics3.php // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_write_lyrics3 +{ + var $filename; + var $tag_data; + //var $lyrics3_version = 2; // 1 or 2 + var $warnings = array(); // any non-critical errors will be stored here + var $errors = array(); // any critical errors will be stored here + + function getid3_write_lyrics3() { + return true; + } + + function WriteLyrics3() { + $this->errors[] = 'WriteLyrics3() not yet functional - cannot write Lyrics3'; + return false; + } + + function DeleteLyrics3() { + // Initialize getID3 engine + $getID3 = new getID3; + $ThisFileInfo = $getID3->analyze($this->filename); + if (isset($ThisFileInfo['lyrics3']['tag_offset_start']) && isset($ThisFileInfo['lyrics3']['tag_offset_end'])) { + if ($fp = @fopen($this->filename, 'a+b')) { + + flock($fp, LOCK_EX); + $oldignoreuserabort = ignore_user_abort(true); + + fseek($fp, $ThisFileInfo['lyrics3']['tag_offset_end'], SEEK_SET); + $DataAfterLyrics3 = ''; + if ($ThisFileInfo['filesize'] > $ThisFileInfo['lyrics3']['tag_offset_end']) { + $DataAfterLyrics3 = fread($fp, $ThisFileInfo['filesize'] - $ThisFileInfo['lyrics3']['tag_offset_end']); + } + + ftruncate($fp, $ThisFileInfo['lyrics3']['tag_offset_start']); + + if (!empty($DataAfterLyrics3)) { + fseek($fp, $ThisFileInfo['lyrics3']['tag_offset_start'], SEEK_SET); + fwrite($fp, $DataAfterLyrics3, strlen($DataAfterLyrics3)); + } + + flock($fp, LOCK_UN); + fclose($fp); + ignore_user_abort($oldignoreuserabort); + + return true; + + } else { + + $this->errors[] = 'Cannot open "'.$this->filename.'" in "a+b" mode'; + return false; + + } + } + // no Lyrics3 present + return true; + } + + + +} + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/write.metaflac.php b/includes/getid3/getid3/write.metaflac.php new file mode 100644 index 0000000..c5acc8c --- /dev/null +++ b/includes/getid3/getid3/write.metaflac.php @@ -0,0 +1,167 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// write.metaflac.php // +// module for writing metaflac tags // +// dependencies: /helperapps/metaflac.exe // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_write_metaflac +{ + + var $filename; + var $tag_data; + var $warnings = array(); // any non-critical errors will be stored here + var $errors = array(); // any critical errors will be stored here + + function getid3_write_metaflac() { + return true; + } + + function WriteMetaFLAC() { + + if (!ini_get('safe_mode')) { + + // Create file with new comments + $tempcommentsfilename = tempnam('*', 'getID3'); + if ($fpcomments = @fopen($tempcommentsfilename, 'wb')) { + + foreach ($this->tag_data as $key => $value) { + foreach ($value as $commentdata) { + fwrite($fpcomments, $this->CleanmetaflacName($key).'='.$commentdata."\n"); + } + } + fclose($fpcomments); + + } else { + + $this->errors[] = 'failed to open temporary tags file "'.$tempcommentsfilename.'", tags not written'; + return false; + + } + + $oldignoreuserabort = ignore_user_abort(true); + if (GETID3_OS_ISWINDOWS) { + + if (file_exists(GETID3_HELPERAPPSDIR.'metaflac.exe')) { + //$commandline = '"'.GETID3_HELPERAPPSDIR.'metaflac.exe" --no-utf8-convert --remove-vc-all --import-vc-from="'.$tempcommentsfilename.'" "'.str_replace('/', '\\', $this->filename).'"'; + // metaflac works fine if you copy-paste the above commandline into a command prompt, + // but refuses to work with `backtick` if there are "doublequotes" present around BOTH + // the metaflac pathname and the target filename. For whatever reason...?? + // The solution is simply ensure that the metaflac pathname has no spaces, + // and therefore does not need to be quoted + + // On top of that, if error messages are not always captured properly under Windows + // To at least see if there was a problem, compare file modification timestamps before and after writing + clearstatcache(); + $timestampbeforewriting = filemtime($this->filename); + + $commandline = GETID3_HELPERAPPSDIR.'metaflac.exe --no-utf8-convert --remove-vc-all --import-vc-from="'.$tempcommentsfilename.'" "'.$this->filename.'" 2>&1'; + $metaflacError = `$commandline`; + + if (empty($metaflacError)) { + clearstatcache(); + if ($timestampbeforewriting == filemtime($this->filename)) { + $metaflacError = 'File modification timestamp has not changed - it looks like the tags were not written'; + } + } + } else { + $metaflacError = 'metaflac.exe not found in '.GETID3_HELPERAPPSDIR; + } + + } else { + + // It's simpler on *nix + $commandline = 'metaflac --no-utf8-convert --remove-vc-all --import-vc-from='.$tempcommentsfilename.' "'.$this->filename.'" 2>&1'; + $metaflacError = `$commandline`; + + } + + // Remove temporary comments file + unlink($tempcommentsfilename); + ignore_user_abort($oldignoreuserabort); + + if (!empty($metaflacError)) { + + $this->errors[] = 'System call to metaflac failed with this message returned: '."\n\n".$metaflacError; + return false; + + } + + return true; + } + + $this->errors[] = 'PHP running in Safe Mode (backtick operator not available) - cannot call metaflac, tags not written'; + return false; + } + + + function DeleteMetaFLAC() { + + if (!ini_get('safe_mode')) { + + $oldignoreuserabort = ignore_user_abort(true); + if (GETID3_OS_ISWINDOWS) { + + if (file_exists(GETID3_HELPERAPPSDIR.'metaflac.exe')) { + // To at least see if there was a problem, compare file modification timestamps before and after writing + clearstatcache(); + $timestampbeforewriting = filemtime($this->filename); + + $commandline = GETID3_HELPERAPPSDIR.'metaflac.exe --remove-vc-all "'.$this->filename.'" 2>&1'; + $metaflacError = `$commandline`; + + if (empty($metaflacError)) { + clearstatcache(); + if ($timestampbeforewriting == filemtime($this->filename)) { + $metaflacError = 'File modification timestamp has not changed - it looks like the tags were not deleted'; + } + } + } else { + $metaflacError = 'metaflac.exe not found in '.GETID3_HELPERAPPSDIR; + } + + } else { + + // It's simpler on *nix + $commandline = 'metaflac --remove-vc-all "'.$this->filename.'" 2>&1'; + $metaflacError = `$commandline`; + + } + + ignore_user_abort($oldignoreuserabort); + + if (!empty($metaflacError)) { + $this->errors[] = 'System call to metaflac failed with this message returned: '."\n\n".$metaflacError; + return false; + } + return true; + } + $this->errors[] = 'PHP running in Safe Mode (backtick operator not available) - cannot call metaflac, tags not deleted'; + return false; + } + + + function CleanmetaflacName($originalcommentname) { + // A case-insensitive field name that may consist of ASCII 0x20 through 0x7D, 0x3D ('=') excluded. + // ASCII 0x41 through 0x5A inclusive (A-Z) is to be considered equivalent to ASCII 0x61 through + // 0x7A inclusive (a-z). + + // replace invalid chars with a space, return uppercase text + // Thanks Chris Bolt for improving this function + // note: ereg_replace() replaces nulls with empty string (not space) + return strtoupper(ereg_replace('[^ -<>-}]', ' ', str_replace("\x00", ' ', $originalcommentname))); + + } + +} + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/write.php b/includes/getid3/getid3/write.php new file mode 100644 index 0000000..73e2610 --- /dev/null +++ b/includes/getid3/getid3/write.php @@ -0,0 +1,592 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +/// // +// write.php // +// module for writing tags (APEv2, ID3v1, ID3v2) // +// dependencies: getid3.lib.php // +// write.apetag.php (optional) // +// write.id3v1.php (optional) // +// write.id3v2.php (optional) // +// write.vorbiscomment.php (optional) // +// write.metaflac.php (optional) // +// write.lyrics3.php (optional) // +// /// +///////////////////////////////////////////////////////////////// + +if (!defined('GETID3_INCLUDEPATH')) { + die('getid3.php MUST be included before calling getid3_writetags'); +} +if (!include_once(GETID3_INCLUDEPATH.'getid3.lib.php')) { + die('write.php depends on getid3.lib.php, which is missing.'); +} + + +// NOTES: +// +// You should pass data here with standard field names as follows: +// * TITLE +// * ARTIST +// * ALBUM +// * TRACKNUMBER +// * COMMENT +// * GENRE +// * YEAR +// * ATTACHED_PICTURE (ID3v2 only) +// +// http://www.personal.uni-jena.de/~pfk/mpp/sv8/apekey.html +// The APEv2 Tag Items Keys definition says "TRACK" is correct but foobar2000 uses "TRACKNUMBER" instead +// Pass data here as "TRACKNUMBER" for compatability with all formats + + +class getid3_writetags +{ + // public + var $filename; // absolute filename of file to write tags to + var $tagformats = array(); // array of tag formats to write ('id3v1', 'id3v2.2', 'id2v2.3', 'id3v2.4', 'ape', 'vorbiscomment', 'metaflac', 'real') + var $tag_data = array(array()); // 2-dimensional array of tag data (ex: $data['ARTIST'][0] = 'Elvis') + var $tag_encoding = 'ISO-8859-1'; // text encoding used for tag data ('ISO-8859-1', 'UTF-8', 'UTF-16', 'UTF-16LE', 'UTF-16BE', ) + var $overwrite_tags = true; // if true will erase existing tag data and write only passed data; if false will merge passed data with existing tag data + var $remove_other_tags = false; // if true will erase remove all existing tags and only write those passed in $tagformats; if false will ignore any tags not mentioned in $tagformats + + var $id3v2_tag_language = 'eng'; // ISO-639-2 3-character language code needed for some ID3v2 frames (http://www.id3.org/iso639-2.html) + var $id3v2_paddedlength = 4096; // minimum length of ID3v2 tags (will be padded to this length if tag data is shorter) + + var $warnings = array(); // any non-critical errors will be stored here + var $errors = array(); // any critical errors will be stored here + + // private + var $ThisFileInfo; // analysis of file before writing + + function getid3_writetags() { + return true; + } + + + function WriteTags() { + + if (empty($this->filename)) { + $this->errors[] = 'filename is undefined in getid3_writetags'; + return false; + } elseif (!file_exists($this->filename)) { + $this->errors[] = 'filename set to non-existant file "'.$this->filename.'" in getid3_writetags'; + return false; + } + + if (!is_array($this->tagformats)) { + $this->errors[] = 'tagformats must be an array in getid3_writetags'; + return false; + } + + $TagFormatsToRemove = array(); + if (filesize($this->filename) == 0) { + + // empty file special case - allow any tag format, don't check existing format + // could be useful if you want to generate tag data for a non-existant file + $this->ThisFileInfo = array('fileformat'=>''); + $AllowedTagFormats = array('id3v1', 'id3v2.2', 'id3v2.3', 'id3v2.4', 'ape', 'lyrics3'); + + } else { + + $getID3 = new getID3; + $getID3->encoding = $this->tag_encoding; + $this->ThisFileInfo = $getID3->analyze($this->filename); + + // check for what file types are allowed on this fileformat + switch (@$this->ThisFileInfo['fileformat']) { + case 'mp3': + case 'mp2': + case 'mp1': + case 'riff': // maybe not officially, but people do it anyway + $AllowedTagFormats = array('id3v1', 'id3v2.2', 'id3v2.3', 'id3v2.4', 'ape', 'lyrics3'); + break; + + case 'mpc': + $AllowedTagFormats = array('ape'); + break; + + case 'flac': + $AllowedTagFormats = array('metaflac'); + break; + + case 'real': + $AllowedTagFormats = array('real'); + break; + + case 'ogg': + switch (@$this->ThisFileInfo['audio']['dataformat']) { + case 'flac': + //$AllowedTagFormats = array('metaflac'); + $this->errors[] = 'metaflac is not (yet) compatible with OggFLAC files'; + return false; + break; + case 'vorbis': + $AllowedTagFormats = array('vorbiscomment'); + break; + default: + $this->errors[] = 'metaflac is not (yet) compatible with Ogg files other than OggVorbis'; + return false; + break; + } + break; + + default: + $AllowedTagFormats = array(); + break; + } + foreach ($this->tagformats as $requested_tag_format) { + if (!in_array($requested_tag_format, $AllowedTagFormats)) { + $errormessage = 'Tag format "'.$requested_tag_format.'" is not allowed on "'.@$this->ThisFileInfo['fileformat']; + if (@$this->ThisFileInfo['fileformat'] != @$this->ThisFileInfo['audio']['dataformat']) { + $errormessage .= '.'.@$this->ThisFileInfo['audio']['dataformat']; + } + $errormessage .= '" files'; + $this->errors[] = $errormessage; + return false; + } + } + + // List of other tag formats, removed if requested + if ($this->remove_other_tags) { + foreach ($AllowedTagFormats as $AllowedTagFormat) { + switch ($AllowedTagFormat) { + case 'id3v2.2': + case 'id3v2.3': + case 'id3v2.4': + if (!in_array('id3v2', $TagFormatsToRemove) && !in_array('id3v2.2', $this->tagformats) && !in_array('id3v2.3', $this->tagformats) && !in_array('id3v2.4', $this->tagformats)) { + $TagFormatsToRemove[] = 'id3v2'; + } + break; + + default: + if (!in_array($AllowedTagFormat, $this->tagformats)) { + $TagFormatsToRemove[] = $AllowedTagFormat; + } + break; + } + } + } + } + + $WritingFilesToInclude = array_merge($this->tagformats, $TagFormatsToRemove); + + // Check for required include files and include them + foreach ($WritingFilesToInclude as $tagformat) { + switch ($tagformat) { + case 'ape': + $GETID3_ERRORARRAY = &$this->errors; + if (!getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.apetag.php', __FILE__, false)) { + return false; + } + break; + + case 'id3v1': + case 'lyrics3': + case 'vorbiscomment': + case 'metaflac': + case 'real': + $GETID3_ERRORARRAY = &$this->errors; + if (!getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.'.$tagformat.'.php', __FILE__, false)) { + return false; + } + break; + + case 'id3v2.2': + case 'id3v2.3': + case 'id3v2.4': + case 'id3v2': + $GETID3_ERRORARRAY = &$this->errors; + if (!getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.id3v2.php', __FILE__, false)) { + return false; + } + break; + + default: + $this->errors[] = 'unknown tag format "'.$tagformat.'" in $tagformats in WriteTags()'; + return false; + break; + } + + } + + // Validation of supplied data + if (!is_array($this->tag_data)) { + $this->errors[] = '$tag_data is not an array in WriteTags()'; + return false; + } + // convert supplied data array keys to upper case, if they're not already + foreach ($this->tag_data as $tag_key => $tag_array) { + if (strtoupper($tag_key) !== $tag_key) { + $this->tag_data[strtoupper($tag_key)] = $this->tag_data[$tag_key]; + unset($this->tag_data[$tag_key]); + } + } + // convert source data array keys to upper case, if they're not already + if (!empty($this->ThisFileInfo['tags'])) { + foreach ($this->ThisFileInfo['tags'] as $tag_format => $tag_data_array) { + foreach ($tag_data_array as $tag_key => $tag_array) { + if (strtoupper($tag_key) !== $tag_key) { + $this->ThisFileInfo['tags'][$tag_format][strtoupper($tag_key)] = $this->ThisFileInfo['tags'][$tag_format][$tag_key]; + unset($this->ThisFileInfo['tags'][$tag_format][$tag_key]); + } + } + } + } + + // Convert "TRACK" to "TRACKNUMBER" (if needed) for compatability with all formats + if (isset($this->tag_data['TRACK']) && !isset($this->tag_data['TRACKNUMBER'])) { + $this->tag_data['TRACKNUMBER'] = $this->tag_data['TRACK']; + unset($this->tag_data['TRACK']); + } + + // Remove all other tag formats, if requested + if ($this->remove_other_tags) { + $this->DeleteTags($TagFormatsToRemove); + } + + // Write data for each tag format + foreach ($this->tagformats as $tagformat) { + $success = false; // overridden if tag writing is successful + switch ($tagformat) { + case 'ape': + $ape_writer = new getid3_write_apetag; + if (($ape_writer->tag_data = $this->FormatDataForAPE()) !== false) { + $ape_writer->filename = $this->filename; + if (($success = $ape_writer->WriteAPEtag()) === false) { + $this->errors[] = 'WriteAPEtag() failed with message(s):
  • '.trim(implode('
  • ', $ape_writer->errors)).'
'; + } + } else { + $this->errors[] = 'FormatDataForAPE() failed'; + } + break; + + case 'id3v1': + $id3v1_writer = new getid3_write_id3v1; + if (($id3v1_writer->tag_data = $this->FormatDataForID3v1()) !== false) { + $id3v1_writer->filename = $this->filename; + if (($success = $id3v1_writer->WriteID3v1()) === false) { + $this->errors[] = 'WriteID3v1() failed with message(s):
  • '.trim(implode('
  • ', $id3v1_writer->errors)).'
'; + } + } else { + $this->errors[] = 'FormatDataForID3v1() failed'; + } + break; + + case 'id3v2.2': + case 'id3v2.3': + case 'id3v2.4': + $id3v2_writer = new getid3_write_id3v2; + $id3v2_writer->majorversion = intval(substr($tagformat, -1)); + $id3v2_writer->paddedlength = $this->id3v2_paddedlength; + if (($id3v2_writer->tag_data = $this->FormatDataForID3v2($id3v2_writer->majorversion)) !== false) { + $id3v2_writer->filename = $this->filename; + if (($success = $id3v2_writer->WriteID3v2()) === false) { + $this->errors[] = 'WriteID3v2() failed with message(s):
  • '.trim(implode('
  • ', $id3v2_writer->errors)).'
'; + } + } else { + $this->errors[] = 'FormatDataForID3v2() failed'; + } + break; + + case 'vorbiscomment': + $vorbiscomment_writer = new getid3_write_vorbiscomment; + if (($vorbiscomment_writer->tag_data = $this->FormatDataForVorbisComment()) !== false) { + $vorbiscomment_writer->filename = $this->filename; + if (($success = $vorbiscomment_writer->WriteVorbisComment()) === false) { + $this->errors[] = 'WriteVorbisComment() failed with message(s):
  • '.trim(implode('
  • ', $vorbiscomment_writer->errors)).'
'; + } + } else { + $this->errors[] = 'FormatDataForVorbisComment() failed'; + } + break; + + case 'metaflac': + $metaflac_writer = new getid3_write_metaflac; + if (($metaflac_writer->tag_data = $this->FormatDataForMetaFLAC()) !== false) { + $metaflac_writer->filename = $this->filename; + if (($success = $metaflac_writer->WriteMetaFLAC()) === false) { + $this->errors[] = 'WriteMetaFLAC() failed with message(s):
  • '.trim(implode('
  • ', $metaflac_writer->errors)).'
'; + } + } else { + $this->errors[] = 'FormatDataForMetaFLAC() failed'; + } + break; + + case 'real': + $real_writer = new getid3_write_real; + if (($real_writer->tag_data = $this->FormatDataForReal()) !== false) { + $real_writer->filename = $this->filename; + if (($success = $real_writer->WriteReal()) === false) { + $this->errors[] = 'WriteReal() failed with message(s):
  • '.trim(implode('
  • ', $real_writer->errors)).'
'; + } + } else { + $this->errors[] = 'FormatDataForReal() failed'; + } + break; + + default: + $this->errors[] = 'Invalid tag format to write: "'.$tagformat.'"'; + return false; + break; + } + if (!$success) { + return false; + } + } + return true; + + } + + + function DeleteTags($TagFormatsToDelete) { + foreach ($TagFormatsToDelete as $DeleteTagFormat) { + $success = false; // overridden if tag deletion is successful + switch ($DeleteTagFormat) { + case 'id3v1': + $id3v1_writer = new getid3_write_id3v1; + $id3v1_writer->filename = $this->filename; + if (($success = $id3v1_writer->RemoveID3v1()) === false) { + $this->errors[] = 'RemoveID3v1() failed with message(s):
  • '.trim(implode('
  • ', $id3v1_writer->errors)).'
'; + } + break; + + case 'id3v2': + $id3v2_writer = new getid3_write_id3v2; + $id3v2_writer->filename = $this->filename; + if (($success = $id3v2_writer->RemoveID3v2()) === false) { + $this->errors[] = 'RemoveID3v2() failed with message(s):
  • '.trim(implode('
  • ', $id3v2_writer->errors)).'
'; + } + break; + + case 'ape': + $ape_writer = new getid3_write_apetag; + $ape_writer->filename = $this->filename; + if (($success = $ape_writer->DeleteAPEtag()) === false) { + $this->errors[] = 'DeleteAPEtag() failed with message(s):
  • '.trim(implode('
  • ', $ape_writer->errors)).'
'; + } + break; + + case 'vorbiscomment': + $vorbiscomment_writer = new getid3_write_vorbiscomment; + $vorbiscomment_writer->filename = $this->filename; + if (($success = $vorbiscomment_writer->DeleteVorbisComment()) === false) { + $this->errors[] = 'DeleteVorbisComment() failed with message(s):
  • '.trim(implode('
  • ', $vorbiscomment_writer->errors)).'
'; + } + break; + + case 'metaflac': + $metaflac_writer = new getid3_write_metaflac; + $metaflac_writer->filename = $this->filename; + if (($success = $metaflac_writer->DeleteMetaFLAC()) === false) { + $this->errors[] = 'DeleteMetaFLAC() failed with message(s):
  • '.trim(implode('
  • ', $metaflac_writer->errors)).'
'; + } + break; + + case 'lyrics3': + $lyrics3_writer = new getid3_write_lyrics3; + $lyrics3_writer->filename = $this->filename; + if (($success = $lyrics3_writer->DeleteLyrics3()) === false) { + $this->errors[] = 'DeleteLyrics3() failed with message(s):
  • '.trim(implode('
  • ', $lyrics3_writer->errors)).'
'; + } + break; + + case 'real': + $real_writer = new getid3_write_real; + $real_writer->filename = $this->filename; + if (($success = $real_writer->RemoveReal()) === false) { + $this->errors[] = 'RemoveReal() failed with message(s):
  • '.trim(implode('
  • ', $real_writer->errors)).'
'; + } + break; + + default: + $this->errors[] = 'Invalid tag format to delete: "'.$tagformat.'"'; + return false; + break; + } + if (!$success) { + return false; + } + } + return true; + } + + + function MergeExistingTagData($TagFormat, &$tag_data) { + // Merge supplied data with existing data, if requested + if ($this->overwrite_tags) { + // do nothing - ignore previous data + } else { + if (!isset($this->ThisFileInfo['tags'][$TagFormat])) { + return false; + } + $tag_data = array_merge_recursive($tag_data, $this->ThisFileInfo['tags'][$TagFormat]); + } + return true; + } + + function FormatDataForAPE() { + $ape_tag_data = array(); + foreach ($this->tag_data as $tag_key => $valuearray) { + switch ($tag_key) { + case 'ATTACHED_PICTURE': + // ATTACHED_PICTURE is ID3v2 only - ignore + $this->warnings[] = '$data['.$tag_key.'] is assumed to be ID3v2 APIC data - NOT written to APE tag'; + break; + + default: + foreach ($valuearray as $key => $value) { + if (is_string($value) || is_numeric($value)) { + $ape_tag_data[$tag_key][$key] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-8', $value); + } else { + $this->warnings[] = '$data['.$tag_key.']['.$key.'] is not a string value - all of $data['.$tag_key.'] NOT written to APE tag'; + unset($ape_tag_data[$tag_key]); + break; + } + } + break; + } + } + $this->MergeExistingTagData('ape', $ape_tag_data); + return $ape_tag_data; + } + + + function FormatDataForID3v1() { + $tag_data_id3v1['genreid'] = 255; + if (!empty($this->tag_data['GENRE'])) { + foreach ($this->tag_data['GENRE'] as $key => $value) { + if (getid3_id3v1::LookupGenreID($value) !== false) { + $tag_data_id3v1['genreid'] = getid3_id3v1::LookupGenreID($value); + break; + } + } + } + + $tag_data_id3v1['title'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['TITLE'])); + $tag_data_id3v1['artist'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['ARTIST'])); + $tag_data_id3v1['album'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['ALBUM'])); + $tag_data_id3v1['year'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['YEAR'])); + $tag_data_id3v1['comment'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['COMMENT'])); + + $tag_data_id3v1['track'] = intval(getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['TRACKNUMBER']))); + if ($tag_data_id3v1['track'] <= 0) { + $tag_data_id3v1['track'] = ''; + } + + $this->MergeExistingTagData('id3v1', $tag_data_id3v1); + return $tag_data_id3v1; + } + + function FormatDataForID3v2($id3v2_majorversion) { + $tag_data_id3v2 = array(); + + $ID3v2_text_encoding_lookup[2] = array('ISO-8859-1'=>0, 'UTF-16'=>1); + $ID3v2_text_encoding_lookup[3] = array('ISO-8859-1'=>0, 'UTF-16'=>1); + $ID3v2_text_encoding_lookup[4] = array('ISO-8859-1'=>0, 'UTF-16'=>1, 'UTF-16BE'=>2, 'UTF-8'=>3); + foreach ($this->tag_data as $tag_key => $valuearray) { + $ID3v2_framename = getid3_write_id3v2::ID3v2ShortFrameNameLookup($id3v2_majorversion, $tag_key); + switch ($ID3v2_framename) { + case 'APIC': + foreach ($valuearray as $key => $apic_data_array) { + if (isset($apic_data_array['data']) && + isset($apic_data_array['picturetypeid']) && + isset($apic_data_array['description']) && + isset($apic_data_array['mime'])) { + $tag_data_id3v2['APIC'][] = $apic_data_array; + } else { + $this->errors[] = 'ID3v2 APIC data is not properly structured'; + return false; + } + } + break; + + case '': + $this->errors[] = 'ID3v2: Skipping "'.$tag_key.'" because cannot match it to a known ID3v2 frame type'; + // some other data type, don't know how to handle it, ignore it + break; + + default: + // most other (text) frames can be copied over as-is + foreach ($valuearray as $key => $value) { + if (isset($ID3v2_text_encoding_lookup[$id3v2_majorversion][$this->tag_encoding])) { + // source encoding is valid in ID3v2 - use it with no conversion + $tag_data_id3v2[$ID3v2_framename][$key]['encodingid'] = $ID3v2_text_encoding_lookup[$id3v2_majorversion][$this->tag_encoding]; + $tag_data_id3v2[$ID3v2_framename][$key]['data'] = $value; + } else { + // source encoding is NOT valid in ID3v2 - convert it to an ID3v2-valid encoding first + if ($id3v2_majorversion < 4) { + // convert data from other encoding to UTF-16 + $tag_data_id3v2[$ID3v2_framename][$key]['encodingid'] = 1; + $tag_data_id3v2[$ID3v2_framename][$key]['data'] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-16', $value); + + } else { + // convert data from other encoding to UTF-8 + $tag_data_id3v2[$ID3v2_framename][$key]['encodingid'] = 3; + $tag_data_id3v2[$ID3v2_framename][$key]['data'] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-8', $value); + } + } + + // These values are not needed for all frame types, but if they're not used no matter + $tag_data_id3v2[$ID3v2_framename][$key]['description'] = ''; + $tag_data_id3v2[$ID3v2_framename][$key]['language'] = $this->id3v2_tag_language; + } + break; + } + } + $this->MergeExistingTagData('id3v2', $tag_data_id3v2); + return $tag_data_id3v2; + } + + function FormatDataForVorbisComment() { + $tag_data_vorbiscomment = $this->tag_data; + + // check for multi-line comment values - split out to multiple comments if neccesary + // and convert data to UTF-8 strings + foreach ($tag_data_vorbiscomment as $tag_key => $valuearray) { + foreach ($valuearray as $key => $value) { + str_replace("\r", "\n", $value); + if (strstr($value, "\n")) { + unset($tag_data_vorbiscomment[$tag_key][$key]); + $multilineexploded = explode("\n", $value); + foreach ($multilineexploded as $newcomment) { + if (strlen(trim($newcomment)) > 0) { + $tag_data_vorbiscomment[$tag_key][] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-8', $newcomment); + } + } + } elseif (is_string($value) || is_numeric($value)) { + $tag_data_vorbiscomment[$tag_key][$key] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-8', $value); + } else { + $this->warnings[] = '$data['.$tag_key.']['.$key.'] is not a string value - all of $data['.$tag_key.'] NOT written to VorbisComment tag'; + unset($tag_data_vorbiscomment[$tag_key]); + break; + } + } + } + $this->MergeExistingTagData('vorbiscomment', $tag_data_vorbiscomment); + return $tag_data_vorbiscomment; + } + + function FormatDataForMetaFLAC() { + // FLAC & OggFLAC use VorbisComments same as OggVorbis + // but require metaflac to do the writing rather than vorbiscomment + return $this->FormatDataForVorbisComment(); + } + + function FormatDataForReal() { + $tag_data_real['title'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['TITLE'])); + $tag_data_real['artist'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['ARTIST'])); + $tag_data_real['copyright'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['COPYRIGHT'])); + $tag_data_real['comment'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['COMMENT'])); + + $this->MergeExistingTagData('real', $tag_data_real); + return $tag_data_real; + } + +} + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/write.real.php b/includes/getid3/getid3/write.real.php new file mode 100644 index 0000000..1e0240c --- /dev/null +++ b/includes/getid3/getid3/write.real.php @@ -0,0 +1,295 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// write.real.php // +// module for writing RealAudio/RealVideo tags // +// dependencies: module.tag.real.php // +// /// +///////////////////////////////////////////////////////////////// + +class getid3_write_real +{ + var $filename; + var $tag_data = array(); + var $warnings = array(); // any non-critical errors will be stored here + var $errors = array(); // any critical errors will be stored here + var $paddedlength = 512; // minimum length of CONT tag in bytes + + function getid3_write_real() { + return true; + } + + function WriteReal() { + // File MUST be writeable - CHMOD(646) at least + if (is_writeable($this->filename)) { + if ($fp_source = @fopen($this->filename, 'r+b')) { + + // Initialize getID3 engine + $getID3 = new getID3; + $OldThisFileInfo = $getID3->analyze($this->filename); + if (empty($OldThisFileInfo['real']['chunks']) && !empty($OldThisFileInfo['real']['old_ra_header'])) { + $this->errors[] = 'Cannot write Real tags on old-style file format'; + fclose($fp_source); + return false; + } + + if (empty($OldThisFileInfo['real']['chunks'])) { + $this->errors[] = 'Cannot write Real tags because cannot find DATA chunk in file'; + fclose($fp_source); + return false; + } + foreach ($OldThisFileInfo['real']['chunks'] as $chunknumber => $chunkarray) { + $oldChunkInfo[$chunkarray['name']] = $chunkarray; + } + if (!empty($oldChunkInfo['CONT']['length'])) { + $this->paddedlength = max($oldChunkInfo['CONT']['length'], $this->paddedlength); + } + + $new_CONT_tag_data = $this->GenerateCONTchunk(); + $new_PROP_tag_data = $this->GeneratePROPchunk($OldThisFileInfo['real']['chunks'], $new_CONT_tag_data); + $new__RMF_tag_data = $this->GenerateRMFchunk($OldThisFileInfo['real']['chunks']); + + if (@$oldChunkInfo['.RMF']['length'] == strlen($new__RMF_tag_data)) { + fseek($fp_source, $oldChunkInfo['.RMF']['offset'], SEEK_SET); + fwrite($fp_source, $new__RMF_tag_data); + } else { + $this->errors[] = 'new .RMF tag ('.strlen($new__RMF_tag_data).' bytes) different length than old .RMF tag ('.$oldChunkInfo['.RMF']['length'].' bytes)'; + fclose($fp_source); + return false; + } + + if (@$oldChunkInfo['PROP']['length'] == strlen($new_PROP_tag_data)) { + fseek($fp_source, $oldChunkInfo['PROP']['offset'], SEEK_SET); + fwrite($fp_source, $new_PROP_tag_data); + } else { + $this->errors[] = 'new PROP tag ('.strlen($new_PROP_tag_data).' bytes) different length than old PROP tag ('.$oldChunkInfo['PROP']['length'].' bytes)'; + fclose($fp_source); + return false; + } + + if (@$oldChunkInfo['CONT']['length'] == strlen($new_CONT_tag_data)) { + + // new data length is same as old data length - just overwrite + fseek($fp_source, $oldChunkInfo['CONT']['offset'], SEEK_SET); + fwrite($fp_source, $new_CONT_tag_data); + fclose($fp_source); + return true; + + } else { + + if (empty($oldChunkInfo['CONT'])) { + // no existing CONT chunk + $BeforeOffset = $oldChunkInfo['DATA']['offset']; + $AfterOffset = $oldChunkInfo['DATA']['offset']; + } else { + // new data is longer than old data + $BeforeOffset = $oldChunkInfo['CONT']['offset']; + $AfterOffset = $oldChunkInfo['CONT']['offset'] + $oldChunkInfo['CONT']['length']; + } + if ($tempfilename = tempnam('*', 'getID3')) { + ob_start(); + if ($fp_temp = fopen($tempfilename, 'wb')) { + + rewind($fp_source); + fwrite($fp_temp, fread($fp_source, $BeforeOffset)); + fwrite($fp_temp, $new_CONT_tag_data); + fseek($fp_source, $AfterOffset, SEEK_SET); + while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) { + fwrite($fp_temp, $buffer, strlen($buffer)); + } + fclose($fp_temp); + + if (copy($tempfilename, $this->filename)) { + unlink($tempfilename); + fclose($fp_source); + return true; + } + unlink($tempfilename); + $this->errors[] = 'FAILED: copy('.$tempfilename.', '.$this->filename.') - '.strip_tags(ob_get_contents()); + + } else { + + $this->errors[] = 'Could not open '.$tempfilename.' mode "wb" - '.strip_tags(ob_get_contents()); + + } + ob_end_clean(); + } + fclose($fp_source); + return false; + + } + + + } else { + $this->errors[] = 'Could not open '.$this->filename.' mode "r+b"'; + return false; + } + } + $this->errors[] = 'File is not writeable: '.$this->filename; + return false; + } + + function GenerateRMFchunk(&$chunks) { + $oldCONTexists = false; + foreach ($chunks as $key => $chunk) { + $chunkNameKeys[$chunk['name']] = $key; + if ($chunk['name'] == 'CONT') { + $oldCONTexists = true; + } + } + $newHeadersCount = $chunks[$chunkNameKeys['.RMF']]['headers_count'] + ($oldCONTexists ? 0 : 1); + + $RMFchunk = "\x00\x00"; // object version + $RMFchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['.RMF']]['file_version'], 4); + $RMFchunk .= getid3_lib::BigEndian2String($newHeadersCount, 4); + + $RMFchunk = '.RMF'.getid3_lib::BigEndian2String(strlen($RMFchunk) + 8, 4).$RMFchunk; // .RMF chunk identifier + chunk length + return $RMFchunk; + } + + function GeneratePROPchunk(&$chunks, &$new_CONT_tag_data) { + $old_CONT_length = 0; + $old_DATA_offset = 0; + $old_INDX_offset = 0; + foreach ($chunks as $key => $chunk) { + $chunkNameKeys[$chunk['name']] = $key; + if ($chunk['name'] == 'CONT') { + $old_CONT_length = $chunk['length']; + } elseif ($chunk['name'] == 'DATA') { + if (!$old_DATA_offset) { + $old_DATA_offset = $chunk['offset']; + } + } elseif ($chunk['name'] == 'INDX') { + if (!$old_INDX_offset) { + $old_INDX_offset = $chunk['offset']; + } + } + } + $CONTdelta = strlen($new_CONT_tag_data) - $old_CONT_length; + + $PROPchunk = "\x00\x00"; // object version + $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['max_bit_rate'], 4); + $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['avg_bit_rate'], 4); + $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['max_packet_size'], 4); + $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['avg_packet_size'], 4); + $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['num_packets'], 4); + $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['duration'], 4); + $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['preroll'], 4); + $PROPchunk .= getid3_lib::BigEndian2String(max(0, $old_INDX_offset + $CONTdelta), 4); + $PROPchunk .= getid3_lib::BigEndian2String(max(0, $old_DATA_offset + $CONTdelta), 4); + $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['num_streams'], 2); + $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['flags_raw'], 2); + + $PROPchunk = 'PROP'.getid3_lib::BigEndian2String(strlen($PROPchunk) + 8, 4).$PROPchunk; // PROP chunk identifier + chunk length + return $PROPchunk; + } + + function GenerateCONTchunk() { + foreach ($this->tag_data as $key => $value) { + // limit each value to 0xFFFF bytes + $this->tag_data[$key] = substr($value, 0, 65535); + } + + $CONTchunk = "\x00\x00"; // object version + + $CONTchunk .= getid3_lib::BigEndian2String(strlen(@$this->tag_data['title']), 2); + $CONTchunk .= @$this->tag_data['title']; + + $CONTchunk .= getid3_lib::BigEndian2String(strlen(@$this->tag_data['artist']), 2); + $CONTchunk .= @$this->tag_data['artist']; + + $CONTchunk .= getid3_lib::BigEndian2String(strlen(@$this->tag_data['copyright']), 2); + $CONTchunk .= @$this->tag_data['copyright']; + + $CONTchunk .= getid3_lib::BigEndian2String(strlen(@$this->tag_data['comment']), 2); + $CONTchunk .= @$this->tag_data['comment']; + + if ($this->paddedlength > (strlen($CONTchunk) + 8)) { + $CONTchunk .= str_repeat("\x00", $this->paddedlength - strlen($CONTchunk) - 8); + } + + $CONTchunk = 'CONT'.getid3_lib::BigEndian2String(strlen($CONTchunk) + 8, 4).$CONTchunk; // CONT chunk identifier + chunk length + + return $CONTchunk; + } + + function RemoveReal() { + // File MUST be writeable - CHMOD(646) at least + if (is_writeable($this->filename)) { + if ($fp_source = @fopen($this->filename, 'r+b')) { + + // Initialize getID3 engine + $getID3 = new getID3; + $OldThisFileInfo = $getID3->analyze($this->filename); + if (empty($OldThisFileInfo['real']['chunks']) && !empty($OldThisFileInfo['real']['old_ra_header'])) { + $this->errors[] = 'Cannot remove Real tags from old-style file format'; + fclose($fp_source); + return false; + } + + if (empty($OldThisFileInfo['real']['chunks'])) { + $this->errors[] = 'Cannot remove Real tags because cannot find DATA chunk in file'; + fclose($fp_source); + return false; + } + foreach ($OldThisFileInfo['real']['chunks'] as $chunknumber => $chunkarray) { + $oldChunkInfo[$chunkarray['name']] = $chunkarray; + } + + if (empty($oldChunkInfo['CONT'])) { + // no existing CONT chunk + fclose($fp_source); + return true; + } + + $BeforeOffset = $oldChunkInfo['CONT']['offset']; + $AfterOffset = $oldChunkInfo['CONT']['offset'] + $oldChunkInfo['CONT']['length']; + if ($tempfilename = tempnam('*', 'getID3')) { + ob_start(); + if ($fp_temp = fopen($tempfilename, 'wb')) { + + rewind($fp_source); + fwrite($fp_temp, fread($fp_source, $BeforeOffset)); + fseek($fp_source, $AfterOffset, SEEK_SET); + while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) { + fwrite($fp_temp, $buffer, strlen($buffer)); + } + fclose($fp_temp); + + if (copy($tempfilename, $this->filename)) { + unlink($tempfilename); + fclose($fp_source); + return true; + } + unlink($tempfilename); + $this->errors[] = 'FAILED: copy('.$tempfilename.', '.$this->filename.') - '.strip_tags(ob_get_contents()); + + } else { + + $this->errors[] = 'Could not open '.$tempfilename.' mode "wb" - '.strip_tags(ob_get_contents()); + + } + ob_end_clean(); + } + fclose($fp_source); + return false; + + + } else { + $this->errors[] = 'Could not open '.$this->filename.' mode "r+b"'; + return false; + } + } + $this->errors[] = 'File is not writeable: '.$this->filename; + return false; + } + +} + +?> \ No newline at end of file diff --git a/includes/getid3/getid3/write.vorbiscomment.php b/includes/getid3/getid3/write.vorbiscomment.php new file mode 100644 index 0000000..f93b1a1 --- /dev/null +++ b/includes/getid3/getid3/write.vorbiscomment.php @@ -0,0 +1,124 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// write.vorbiscomment.php // +// module for writing VorbisComment tags // +// dependencies: /helperapps/vorbiscomment.exe // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_write_vorbiscomment +{ + + var $filename; + var $tag_data; + var $warnings = array(); // any non-critical errors will be stored here + var $errors = array(); // any critical errors will be stored here + + function getid3_write_vorbiscomment() { + return true; + } + + function WriteVorbisComment() { + + if (!ini_get('safe_mode')) { + + // Create file with new comments + $tempcommentsfilename = tempnam('*', 'getID3'); + if ($fpcomments = @fopen($tempcommentsfilename, 'wb')) { + + foreach ($this->tag_data as $key => $value) { + foreach ($value as $commentdata) { + fwrite($fpcomments, $this->CleanVorbisCommentName($key).'='.$commentdata."\n"); + } + } + fclose($fpcomments); + + } else { + + $this->errors[] = 'failed to open temporary tags file "'.$tempcommentsfilename.'", tags not written'; + return false; + + } + + $oldignoreuserabort = ignore_user_abort(true); + if (GETID3_OS_ISWINDOWS) { + + if (file_exists(GETID3_HELPERAPPSDIR.'vorbiscomment.exe')) { + //$commandline = '"'.GETID3_HELPERAPPSDIR.'vorbiscomment.exe" -w --raw -c "'.$tempcommentsfilename.'" "'.str_replace('/', '\\', $this->filename).'"'; + // vorbiscomment works fine if you copy-paste the above commandline into a command prompt, + // but refuses to work with `backtick` if there are "doublequotes" present around BOTH + // the metaflac pathname and the target filename. For whatever reason...?? + // The solution is simply ensure that the metaflac pathname has no spaces, + // and therefore does not need to be quoted + + // On top of that, if error messages are not always captured properly under Windows + // To at least see if there was a problem, compare file modification timestamps before and after writing + clearstatcache(); + $timestampbeforewriting = filemtime($this->filename); + + $commandline = GETID3_HELPERAPPSDIR.'vorbiscomment.exe -w --raw -c "'.$tempcommentsfilename.'" "'.$this->filename.'" 2>&1'; + $VorbiscommentError = `$commandline`; + + if (empty($VorbiscommentError)) { + clearstatcache(); + if ($timestampbeforewriting == filemtime($this->filename)) { + $VorbiscommentError = 'File modification timestamp has not changed - it looks like the tags were not written'; + } + } + } else { + $VorbiscommentError = 'vorbiscomment.exe not found in '.GETID3_HELPERAPPSDIR; + } + + } else { + + $commandline = 'vorbiscomment -w --raw -c "'.$tempcommentsfilename.'" "'.$this->filename.'" 2>&1'; + $VorbiscommentError = `$commandline`; + + } + + // Remove temporary comments file + unlink($tempcommentsfilename); + ignore_user_abort($oldignoreuserabort); + + if (!empty($VorbiscommentError)) { + + $this->errors[] = 'system call to vorbiscomment failed with message: '."\n\n".$VorbiscommentError; + return false; + + } + + return true; + } + + $this->errors[] = 'PHP running in Safe Mode (backtick operator not available) - cannot call vorbiscomment, tags not written'; + return false; + } + + function DeleteVorbisComment() { + $this->tag_data = array(array()); + return $this->WriteVorbisComment(); + } + + function CleanVorbisCommentName($originalcommentname) { + // A case-insensitive field name that may consist of ASCII 0x20 through 0x7D, 0x3D ('=') excluded. + // ASCII 0x41 through 0x5A inclusive (A-Z) is to be considered equivalent to ASCII 0x61 through + // 0x7A inclusive (a-z). + + // replace invalid chars with a space, return uppercase text + // Thanks Chris Bolt for improving this function + // note: ereg_replace() replaces nulls with empty string (not space) + return strtoupper(ereg_replace('[^ -<>-}]', ' ', str_replace("\x00", ' ', $originalcommentname))); + + } + +} + +?> \ No newline at end of file diff --git a/includes/getid3/license.commercial.txt b/includes/getid3/license.commercial.txt new file mode 100644 index 0000000..416e5a1 --- /dev/null +++ b/includes/getid3/license.commercial.txt @@ -0,0 +1,27 @@ + getID3() Commercial License + =========================== + +getID3() is licensed under the "GNU Public License" (GPL) and/or the +"getID3() Commercial License" (gCL). This document describes the gCL. + +--------------------------------------------------------------------- + +The license is non-exclusively granted to a single person or company, +per payment of the license fee, for the lifetime of that person or +company. The license is non-transferrable. + +The gCL grants the licensee the right to use getID3() in commercial +closed-source projects. Modifications may be made to getID3() with no +obligation to release the modified source code. getID3() (or pieces +thereof) may be included in any number of projects authored (in whole +or in part) by the licensee. + +The licensee may use any version of getID3(), past, present or future, +as is most convenient. This license does not entitle the licensee to +receive any technical support, updates or bugfixes, except as such are +made publicly available to all getID3() users. + +The licensee may not sub-license getID3() itself, meaning that any +commercially released product containing all or parts of getID3() must +have added functionality beyond what is available in getID3(); +getID3() itself may not be re-licensed by the licensee. diff --git a/includes/getid3/license.txt b/includes/getid3/license.txt new file mode 100644 index 0000000..9fec808 --- /dev/null +++ b/includes/getid3/license.txt @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/includes/getid3/readme.txt b/includes/getid3/readme.txt new file mode 100644 index 0000000..32ec06e --- /dev/null +++ b/includes/getid3/readme.txt @@ -0,0 +1,355 @@ +///////////////////////////////////////////////////////////////// +/// getID3() by James Heinrich // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org /// +///////////////////////////////////////////////////////////////// +/// getID3() v2.x optimized for PHP5 by: // +// Allan Hansen /// +///////////////////////////////////////////////////////////////// + + This code is released under the GNU GPL: + http://www.gnu.org/copyleft/gpl.html + + +---------------------------------------------+ + | If you do use this code somewhere, send me | + | an email and tell me how/where you used it. | + | | + | If you want to donate, there is a link on | + | http://www.getid3.org for PayPal donations. | + +---------------------------------------------+ + +Quick Start +=========== + +Q: How can I check that getID3() works on my server/files? +A: Unzip getID3() to a directory, then access /demos/demo.browse.php + + +Sourceforge Notification +======================== + +It's highly recommended that you sign up for notification from +Sourceforge for when new versions are released. Please visit: +http://sourceforge.net/project/showfiles.php?group_id=55859 +and click the little "monitor package" icon/link. If you're +previously signed up for the mailing list, be aware that it has +been discontinued, only the automated Sourceforge notification +will be used from now on. + + +What does getID3() do? +====================== + +Reads & parses (to varying degrees): + ¤ tags: + * APE (v1 and v2) + * ID3v1 (& ID3v1.1) + * ID3v2 (v2.4, v2.3, v2.2) + * Lyrics3 (v1 & v2) + + ¤ audio-lossy: + * MP3/MP2/MP1 + * MPC / Musepack + * Ogg (Vorbis, OggFLAC, Speex) + * RealAudio + * Speex + * VQF + + ¤ audio-lossless: + * AIFF + * AU + * Bonk + * CD-audio (*.cda) + * FLAC + * LA (Lossless Audio) + * LPAC + * MIDI + * Monkey's Audio + * OptimFROG + * RKAU + * VOC + * WAV (RIFF) + * WavPack + + ¤ audio-video: + * ASF: ASF, Windows Media Audio (WMA), Windows Media Video (WMV) + * AVI (RIFF) + * Flash + * MPEG-1 / MPEG-2 + * NSV (Nullsoft Streaming Video) + * Quicktime + * RealVideo + + ¤ still image: + * BMP + * GIF + * JPEG + * PNG + + ¤ data: + * ISO-9660 CD-ROM image (directory structure) + * SZIP (limited support) + * ZIP (directory structure) + + +Writes: + * ID3v1 (& ID3v1.1) + * ID3v2 (v2.3 & v2.4) + * VorbisComment on OggVorbis + * VorbisComment on FLAC (not OggFLAC) + * APE v2 + * Lyrics3 (delete only) + + +Requirements +============ + +* PHP 4.1.0 (or higher) +* at least 4MB memory for PHP. 8MB is highly recommended. + 12MB is required with all modules loaded. + + +Usage +===== + +require_once('/path/getid3.php'); +$getID3 = new getID3; +$fileinfo = $getID3->analyze($filename); + +See structure.txt for the returned data structure. + + +*> For an example of a complete directory-browsing, <* +*> file-scanning implementation of getID3(), please run <* +*> /demos/demo.browse.php <* + + +See /demos/demo.basic.php for a very basic use of getID3() with no +fancy output, just scanning one file. + +See /demos/demo.mysql.php for a sample recursive scanning code that +scans every file in a given directory, and all sub-directories, stores +the results in a database and allows various analysis / maintenance +operations + +See /demos/demo.simple.php for a simple example script that scans all +files in one directory and output artist, title, bitrate and playtime + +See /demos/demo.mimeonly.php for a simple example script that scans a +single file and returns only the MIME information + +To analyze remote files over HTTP or FTP you need to copy the file +locally first before running getID3(). Your code would look something +like this: + +// Copy remote file locally to scan with getID3() +$remotefilename = 'http://www.example.com/filename.mp3'; +if ($fp_remote = fopen($remotefilename, 'rb')) { + $localtempfilename = tempnam('/tmp', 'getID3'); + if ($fp_local = fopen($localtempfilename, 'wb')) { + while ($buffer = fread($fp_remote, 8192)) { + fwrite($fp_local, $buffer); + } + fclose($fp_local); + + // Initialize getID3 engine + $getID3 = new getID3; + + $ThisFileInfo = $getID3->analyze($filename); + + // Delete temporary file + unlink($localtempfilename); + } + fclose($fp_remote); +} + + +// Writing tags: +require_once('getid3.php'); +$getID3 = new getID3; +getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.php', __FILE__); +$tagwriter = new getid3_writetags; +$tagwriter->filename = $Filename; +$tagwriter->tagformats = array('id3v2.3', 'ape'); +$TagData['title'][] = 'Song Title'; +$TagData['artist'][] = 'Artist Name'; +$tagwriter->tag_data = array(; +if ($tagwriter->WriteTags()) { + echo 'success'; +} else { + echo 'failure'; +} + + + +What does the returned data structure look like? +================================================ + +See structure.txt + +It is recommended that you look at the output of +/demos/demo.browse.php scanning the file(s) you're interested in to +confirm what data is actually returned for any particular filetype in +general, and your files in particular, as the actual data returned +may vary considerably depending on what information is available in +the file itself. + + +Notes +===== + +If the format parser encounters a critical problem, it will return +something in $fileinfo['error'], describing the encountered error. If +a less critical error or notice is generated it will appear in +$fileinfo['warning']. Both keys may contain more than one warning or +error. If something is returned in ['error'] then the file was not +correctly parsed and returned data may or may not be correct and/or +complete. If something is returned in ['warning'] (and not ['error']) +then the data that is returned is OK - usually getID3() is reporting +errors in the file that have been worked around due to known bugs in +other programs. Some warnings may indicate that the data that is +returned is OK but that some data could not be extracted due to +errors in the file. + + +Known Bugs/Issues +================= + +See the end of changelog.txt for notes on known issues with +getID3(), encoders, players, etc. + + +Disclaimer +========== + +getID3() has been tested on many systems, on many types of files, +under many operating systems, and is generally believe to be stable +and safe. That being said, there is still the chance there is an +undiscovered and/or unfixed bug that may potentially corrupt your +file, especially within the writing functions. By using getID3() you +agree that it's not my fault if any of your files are corrupted. +In fact, I'm not liable for anything :) + + +///////////////////////////////////////////////////////////////////// + +GNU General Public License - see license.txt + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to: +Free Software Foundation, Inc. +59 Temple Place - Suite 330 +Boston, MA 02111-1307, USA. + + +FAQ: +Q: Can I use getID3() in my program? Do I need a commercial license? +A: You're generally free to use getID3 however you see fit. The only + case in which you would require a commercial license is if you're + selling your closed-source program that integrates getID3. If you + sell your program including a copy of getID3, that's fine as long + as you include a copy of the sourcecode when you sell it. Or you + can distribute your code without getID3 and say "download it from + getid3.sourceforge.net" + + +///////////////////////////////////////////////////////////////////// + +Reference material: +[www.id3.org material now mirrored at http://id3lib.sourceforge.net/id3/] +* http://www.id3.org/id3v2.4.0-structure.txt +* http://www.id3.org/id3v2.4.0-frames.txt +* http://www.id3.org/id3v2.4.0-changes.txt +* http://www.id3.org/id3v2.3.0.txt +* http://www.id3.org/id3v2-00.txt +* http://www.id3.org/mp3frame.html +* http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html +* http://www.dv.co.yu/mpgscript/mpeghdr.htm +* http://www.mp3-tech.org/programmer/frame_header.html +* http://users.belgacom.net/gc247244/extra/tag.html +* http://gabriel.mp3-tech.org/mp3infotag.html +* http://www.id3.org/iso4217.html +* http://www.unicode.org/Public/MAPPINGS/ISO8859/8859-1.TXT +* http://www.xiph.org/ogg/vorbis/doc/framing.html +* http://www.xiph.org/ogg/vorbis/doc/v-comment.html +* http://leknor.com/code/php/class.ogg.php.txt +* http://www.id3.org/iso639-2.html +* http://www.id3.org/lyrics3.html +* http://www.id3.org/lyrics3200.html +* http://www.psc.edu/general/software/packages/ieee/ieee.html +* http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee-expl.html +* http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html +* http://www.jmcgowan.com/avi.html +* http://www.wotsit.org/ +* http://www.herdsoft.com/ti/davincie/davp3xo2.htm +* http://www.mathdogs.com/vorbis-illuminated/bitstream-appendix.html +* "Standard MIDI File Format" by Dustin Caldwell (from www.wotsit.org) +* http://midistudio.com/Help/GMSpecs_Patches.htm +* http://www.xiph.org/archives/vorbis/200109/0459.html +* http://www.replaygain.org/ +* http://www.lossless-audio.com/ +* http://download.microsoft.com/download/winmediatech40/Doc/1.0/WIN98MeXP/EN-US/ASF_Specification_v.1.0.exe +* http://mediaxw.sourceforge.net/files/doc/Active%20Streaming%20Format%20(ASF)%201.0%20Specification.pdf +* http://www.uni-jena.de/~pfk/mpp/sv8/ +* http://jfaul.de/atl/ +* http://www.uni-jena.de/~pfk/mpp/ +* http://www.libpng.org/pub/png/spec/png-1.2-pdg.html +* http://www.real.com/devzone/library/creating/rmsdk/doc/rmff.htm +* http://www.fastgraph.com/help/bmp_os2_header_format.html +* http://netghost.narod.ru/gff/graphics/summary/os2bmp.htm +* http://flac.sourceforge.net/format.html +* http://www.research.att.com/projects/mpegaudio/mpeg2.html +* http://www.audiocoding.com/wiki/index.php?page=AAC +* http://libmpeg.org/mpeg4/doc/w2203tfs.pdf +* http://www.geocities.com/xhelmboyx/quicktime/formats/qtm-layout.txt +* http://developer.apple.com/techpubs/quicktime/qtdevdocs/RM/frameset.htm +* http://www.nullsoft.com/nsv/ +* http://www.wotsit.org/download.asp?f=iso9660 +* http://sandbox.mc.edu/~bennet/cs110/tc/tctod.html +* http://www.cdroller.com/htm/readdata.html +* http://www.speex.org/manual/node10.html +* http://www.harmony-central.com/Computer/Programming/aiff-file-format.doc +* http://www.faqs.org/rfcs/rfc2361.html +* http://ghido.shelter.ro/ +* http://www.ebu.ch/tech_t3285.pdf +* http://www.sr.se/utveckling/tu/bwf +* http://ftp.aessc.org/pub/aes46-2002.pdf +* http://cartchunk.org:8080/ +* http://www.broadcastpapers.com/radio/cartchunk01.htm +* http://www.hr/josip/DSP/AudioFile2.html +* http://home.attbi.com/~chris.bagwell/AudioFormats-11.html +* http://www.pure-mac.com/extkey.html +* http://cesnet.dl.sourceforge.net/sourceforge/bonkenc/bonk-binary-format-0.9.txt +* http://www.headbands.com/gspot/ +* http://www.openswf.org/spec/SWFfileformat.html +* http://j-faul.virtualave.net/ +* http://www.btinternet.com/~AnthonyJ/Atari/programming/avr_format.html +* http://cui.unige.ch/OSG/info/AudioFormats/ap11.html +* http://sswf.sourceforge.net/SWFalexref.html +* http://www.geocities.com/xhelmboyx/quicktime/formats/qti-layout.txt +* http://www-lehre.informatik.uni-osnabrueck.de/~fbstark/diplom/docs/swf/Flash_Uncovered.htm +* http://developer.apple.com/quicktime/icefloe/dispatch012.html +* http://www.csdn.net/Dev/Format/graphics/PCD.htm +* http://tta.iszf.irk.ru/ +* http://www.atsc.org/standards/a_52a.pdf +* http://www.alanwood.net/unicode/ +* http://www.freelists.org/archives/matroska-devel/07-2003/msg00010.html +* http://www.its.msstate.edu/net/real/reports/config/tags.stats +* http://homepages.slingshot.co.nz/~helmboy/quicktime/formats/qtm-layout.txt +* http://brennan.young.net/Comp/LiveStage/things.html +* http://www.multiweb.cz/twoinches/MP3inside.htm +* http://www.geocities.co.jp/SiliconValley-Oakland/3664/alittle.html#GenreExtended +* http://www.mactech.com/articles/mactech/Vol.06/06.01/SANENormalized/ +* http://www.unicode.org/unicode/faq/utf_bom.html +* http://tta.corecodec.org/?menu=format +* http://www.scvi.net/nsvformat.htm \ No newline at end of file diff --git a/includes/getid3/structure.txt b/includes/getid3/structure.txt new file mode 100644 index 0000000..a0651c6 --- /dev/null +++ b/includes/getid3/structure.txt @@ -0,0 +1,2251 @@ +///////////////////////////////////////////////////////////////// +/// getID3() by James Heinrich // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// // +// changelog.txt - part of getID3() // +// See readme.txt for more details // +// /// +///////////////////////////////////////////////////////////////// + +What does the returned data structure look like? +================================================ + +Hint: If you take a look at the nicely-formatted output of +/demos/demo.browse.php you can generally see where the data you want +is returned. + +Note that what is described below is only a rough guide to what data +is actually returned by getID3(), since the actual data returned +depends entirely on what data is in your file, what type of file it +is, what kind of data is in the tags, etc. In addition, some formats +(Quicktime for example) use a freeform recursive structure that is +impossible to document completely. + +In the vast majority of cases, all the data you'll need is located +in the root of the array or the special arrays described below in +Section 1 (['audio'], ['video'], ['tags_html'], ['replay_gain']). + +It is suggested that for most applications you should use tag data +from the root ['tags_html'] array, as this is the only location +where data is stored in a consistant format: HTML-compatible +character entities (ie Ӓ) for characters outside the 0x20-0x7F +range (printable ISO-8859-1 characters). This data can be used as-is +for output in HTML, and can be converted to whatever character set +you wish to use if the output is not HTML. + +If you want to merge all available tags (for example, ID3v2 + ID3v1) +into one array, you can call +getid3_lib::CopyTagsToComments($ThisFileInfo) +and you'll then have ['comments'] and ['comments_html'] which are +identical to ['tags'] and ['tags_html'] except the array is one +dimension shorter (no tag type array keys). For example, artist is: +['tags_html']['id3v1']['artist'][0] or ['comments_html']['artist'][0] + + +Some commonly-used information is found in these locations: + +File type: ['fileformat'] // ex 'mp3' +Song length: ['playtime_string'] // ex '3:45' (minutes:seconds) + ['playtime_seconds'] // ex 225.13 (seconds) +Overall bitrate: ['bitrate'] // ex 113485.71 (bits-per-second - divide by 1000 for kbps) +Audio frequency: ['audio']['sample_rate'] // ex 44100 (Hertz) +Artist name: ['comments_html']['artist'][0] // ex 'Elvis' (if CopyTagsToComments() is used - see above) + // more than one artist may be present, you may want to use implode: + // implode(' & ', ['comments_html']['artist']) + + +///////////////////////////////////////////////////////////////// + +array() { + // SECTION 1: Values that are present for most or all file types + + ['getID3version']=>string() // version of getID3() that scanned this file (ex: '1.6.2') + ['error']=>array() // if present, contains one or more fatal error messages + ['warning']=>array() // if present, contains one or more non-fatal warning messages + ['exist']=>boolean() // does this file actually exist? + ['fileformat']=>string() // one of the standard filetype abbreviations ('mp3', 'riff', 'quicktime', etc) + ['filename']=>string() // filename only, no path + ['filenamepath']=>string() // full filename with path + ['filepath']=>string() // path to file, not including filename + ['filesize']=>integer() // filesize in bytes + ['md5_file']=>string() // md5 hash of entire file + ['md5_data']=>string() // md5 hash of portion of file excluding prepended and appeneded metainformation tags (ID3, APE, etc) - may be identical to ['md5_file'] + ['md5_data_source']=>string() // md5 hash of original source file before compression (currently used by FLAC, OptimFROG, WavPack v4+) + ['sha1_file']=>string() // sha1 hash of entire file + ['sha1_data']=>string() // sha1 hash of portion of file excluding prepended and appeneded metainformation tags (ID3, APE, etc) - may be identical to ['md5_file'] + ['avdataoffset']=>integer() // offset in bytes where audio/video data starts and prepended tags end + ['avdataend']=>integer() // offset in bytes where audio/video data ends and appended tags start + ['bitrate']=>double() // average bitrate for entire file (all audio/video streams), in bits per second + ['mime_type']=>string() // if present, MIME type of scanned file + ['playtime_seconds']=>double() // playing time of file, in seconds + ['playtime_string']=>string() // playing time of file, formatted as : + ['tags']=>array() // array of all metainformation tags present in file ('id3v1', 'id3v2', 'ape', 'riff', 'asf', etc) + ['audio']=>array() { + ['bitrate']=>double() // average bitrate for audio portion of file (all audio streams), in bits per second + ['bitrate_mode']=>string() // 'cbr' (Constant Bit Rate) or 'vbr' (Variable Bit Rate) + ['bits_per_sample']=>integer() // + ['channelmode']=>string() // 'mono' or 'stereo' + ['channels']=>integer() // number of audio channels + ['codec']=>string() // name of audio compression codec + ['compression_ratio']=>double() // ratio of compressed byte size of audio to uncompressed size + ['dataformat']=>string() // one of the standard filetype abbreviations ('mp3', 'wma', etc) + ['encoder']=>string() // name and version of encoder used to create file, if known + ['lossless']=>boolean() // true = lossless compression; false = lossy compression + ['sample_rate']=>integer() + } + ['video']=>array() { + ['bitrate']=>integer() // average bitrate for video portion of file (all video streams), in bits per second + ['bitrate_mode']=>string() // 'cbr' (Constant Bit Rate) or 'vbr' (Variable Bit Rate) + ['bits_per_sample']=>integer() // + ['codec']=>string() // name of video compression codec + ['compression_ratio']=>double() // ratio of compressed byte size of video to uncompressed size + ['dataformat']=>string() // one of the standard filetype abbreviations ('avi', 'mpeg', etc) + ['encoder']=>string() // name and version of encoder used to create file, if known + ['frame_rate']=>double() // frames per second + ['lossless']=>boolean() // true = lossless compression; false = lossy compression + ['resolution_x']=>integer() // horizontal dimension of video/image in pixels + ['resolution_y']=>integer() // vertical dimension of video/image in pixels + ['pixel_aspect_ratio']=>double() // pixel display aspect ratio + } + ['tags']=>array() { // array of array of strings containing best data from any available metainformation tag (APE, ID3v2, ID3v1, Lyrics3, Vorbis, ASF, RIFF, Real, etc.) + []=>array() // can be anything, usually 'artist', 'title', etc. Contains array of one or more values (eg: multiple artists are possible) + } + ['tags_html']=>array() { // identical to ['tags'], but with all entries converted to HTML entities as appropriate from various source encodings + []=>array() // + } + ['replay_gain']=>array() { // replay gain information combined from any source that contains this information (LAME, ID3v2, Vorbis, APE, etc) + ['audiophile']=>array() { + ['adjustment']=>double() + ['originator']=>string() + ['peak']=>double() + } + ['radio']=>array() { + ['adjustment']=>double() + ['originator']=>string() + ['peak']=>double() + } + } + + + // SECTION 2: Values that are present for specific file types only + + ['aac']=>array() { // AAC - Advanced Audio Coding / MPEG-4 + ['bitrate_distribution']=>array() // + ['header']=>array() { // + ['channel_configuration']=>integer() // + ['crc_present']=>boolean() // + ['home']=>boolean() // + ['layer']=>integer() // + ['mpeg_version']=>integer() // + ['original']=>boolean() // + ['private']=>boolean() // + ['profile_id']=>integer() // + ['profile_text']=>string() // + ['sample_frequency']=>integer() // + ['sample_frequency_index']=>integer() // + ['synch']=>integer() // + } // + ['header_type']=>string() // + } // + // + ['ape']=>array() // + { // + ['comments']=>array() { // array of array of strings containing best data from any available metainformation tag (APE, ID3v2, ID3v1, Lyrics3, Vorbis, ASF, RIFF, Real, etc.) + []=>array() // can be anything, usually 'artist', 'title', etc. Contains array of one or more values (eg: multiple artists are possible) + } // + ['footer']=>array() // + { // + ['flags']=>array() // + ['raw']=>array() // + ['tag_version']=>integer() // + } // + ['header']=>array() // + { // + ['flags']=>array() // + ['raw']=>array() // + ['tag_version']=>integer() // + } // + ['items']=>array() { // array of array of strings containing metainformation + []=>array() { // can be anything, usually 'artist', 'title', etc. Contains array of one or more values (eg: multiple artists are possible) + ['data']=>array() { // array of one or more Unicode values + ['data_ascii']=>array() { // array of values converted approximately from Unicode to ASCII + ['flags']=>array() // + } // + } // + ['tag_offset_end']=>integer() // + ['tag_offset_start']=>integer() // + } // + + + ['asf']=>array() { // ASF - Advanced Streaming Format (ASF, Windows Media Audio (WMA), Windows Media Video (WMV)) + ['audio_media']=>array() { // + []=>array() { // + ['bitrate']=>integer() // + ['bits_per_sample']=>integer() // + ['channels']=>integer() // + ['codec']=>string() // + ['codec_data']=>string() // + ['codec_data_size']=>integer() // + ['raw']=>array() { // + ['nAvgBytesPerSec']=>integer() // + ['wBitsPerSample']=>integer() // + ['nBlockAlign']=>integer() // + ['nChannels']=>integer() // + ['nSamplesPerSec']=>integer() // + ['wFormatTag']=>integer() // + } // + ['sample_rate']=>integer() // + } // + } // + ['codec_list']=>array() { // + ['codec_entries']=>array() { // + []=>array() { // + ['description']=>string() // + ['description_ascii']=>string() // + ['information']=>string() // + ['name']=>string() // + ['name_ascii']=>string() // + ['type']=>string() // + ['type_raw']=>integer() // + } // + } // + ['codec_entries_count']=>integer() // + ['objectid']=>string() // + ['objectid_guid']=>string() // + ['objectsize']=>integer() // + ['reserved']=>string() // + ['reserved_guid']=>string() // + } // + ['comments']=>array() { // array of comment values, derived from ['content_description'] + ['album']=>string() // + ['artist']=>string() // + ['comment']=>string() // + ['copyright']=>string() // + ['genre']=>string() // + ['title']=>string() // + ['track']=>string() // + ['year']=>string() // + } // + ['content_description']=>array() { // raw values - should use values from ['comments'] instead + ['author']=>string() // + ['author_ascii']=>string() // + ['author_length']=>integer() // + ['copyright']=>string() // + ['copyright_ascii']=>string() // + ['copyright_length']=>integer() // + ['description']=>string() // + ['description_ascii']=>string() // + ['description_length']=>integer() // + ['objectid']=>string() // + ['objectid_guid']=>string() // + ['objectsize']=>integer() // + ['rating']=>string() // + ['rating_ascii']=>string() // + ['rating_length']=>integer() // + ['title']=>string() // + ['title_ascii']=>string() // + ['title_length']=>integer() // + } // + ['data_object']=>array() { // + ['fileid']=>string() // + ['fileid_guid']=>string() // + ['objectid']=>string() // + ['objectid_guid']=>string() // + ['objectsize']=>integer() // + ['reserved']=>integer() // + ['total_data_packets']=>integer() // + } // + ['extended_content_description']=>array() { // + ['content_descriptors']=>array() { // + []=>array() { // + ['name']=>string() // + ['name_ascii']=>string() // + ['name_length']=>integer() // + ['value']=>string() // + ['value_ascii']=>string() // + ['value_length']=>integer() // + ['value_type']=>integer() // + } // + } // + ['content_descriptors_count']=>integer() // + ['objectid']=>string() // + ['objectid_guid']=>string() // + ['objectsize']=>integer() // + } // + ['file_properties_object']=>array() { // + ['creation_date']=>double() // + ['creation_date_unix']=>double() // + ['data_packets']=>integer() // + ['fileid']=>string() // + ['fileid_guid']=>string() // + ['filesize']=>integer() // + ['flags']=>array() { // + ['broadcast']=>boolean() // + ['seekable']=>boolean() // + } // + ['flags_raw']=>integer() // + ['max_bitrate']=>integer() // + ['max_packet_size']=>integer() // + ['min_packet_size']=>integer() // + ['objectid']=>string() // + ['objectid_guid']=>string() // + ['objectsize']=>integer() // + ['play_duration']=>double() // + ['preroll']=>integer() // + ['send_duration']=>double() // + } // + ['header_extension_object']=>array() { // + ['extension_data']=>integer() // + ['extension_data_size']=>integer() // + ['objectid']=>string() // + ['objectid_guid']=>string() // + ['objectsize']=>integer() // + ['reserved_1']=>string() // + ['reserved_1_guid']=>string() // + ['reserved_2']=>integer() // + } // + ['header_object']=>array() { // + ['headerobjects']=>integer() // + ['objectid']=>string() // + ['objectid_guid']=>string() // + ['objectsize']=>integer() // + ['reserved1']=>integer() // + ['reserved2']=>integer() // + } // + ['marker_object']=>array() { // + ['markers_count']=>integer() // + ['objectid']=>string() // + ['objectid_guid']=>string() // + ['objectsize']=>integer() // + ['reserved']=>string() // + ['reserved_2']=>integer() // + ['reserved_guid']=>string() // + } // + ['stream_bitrate_properties']=>array() { // + ['bitrate_records']=>array() { // + []=>array() { // + ['bitrate']=>integer() // + ['flags_raw']=>integer() // + ['flags']=>array() { // + ['stream_number']=>integer() // + } // + } // + } // + ['bitrate_records_count']=>integer() // + ['objectid']=>string() // + ['objectid_guid']=>string() // + ['objectsize']=>integer() // + } // + ['stream_properties_object']=>array() { // + []=>array() { // + ['error_correct_data']=>string() // + ['error_correct_guid']=>string() // + ['error_correct_type']=>string() // + ['error_data_length']=>integer() // + ['flags_raw']=>integer() // + ['flags']=>array() { // + ['encrypted']=>boolean() // + } // + ['objectid']=>string() // + ['objectid_guid']=>string() // + ['objectsize']=>integer() // + ['stream_type']=>string() // + ['stream_type_guid']=>string() // + ['time_offset']=>integer() // + ['type_data_length']=>integer() // + ['type_specific_data']=>string() // + } // + } // + ['video_media']=>array() { // + []=>array() { // + ['flags']=>integer() // + ['format_data']=>array() { // + ['bits_per_pixel']=>integer() // + ['codec']=>string() // + ['codec_data']=>boolean() // + ['codec_fourcc']=>string() // + ['colors_important']=>integer() // + ['colors_used']=>integer() // + ['format_data_size']=>integer() // + ['horizontal_pels']=>integer() // + ['image_height']=>integer() // + ['image_size']=>integer() // + ['image_width']=>integer() // + ['reserved']=>integer() // + ['vertical_pels']=>integer() // + } // + ['format_data_size']=>integer() // + ['image_height']=>integer() // + ['image_width']=>integer() // + } // + } // + } // + + + ['au']=>array() { // AU - Next/Sun AUdio format + ['bits_per_sample']=>integer() // + ['channels']=>integer() // + ['comment']=>string() // + ['data_format']=>string() // + ['data_format_id']=>integer() // + ['data_size']=>integer() // + ['header_length']=>integer() // + ['sample_rate']=>integer() // + ['used_bits_per_sample']=>integer() // + } // + + + ['bmp']=>array() { // BMP - OS/2 or Windows BitMaP + ['header']=>array() { // + ['compression']=>string() // + ['raw']=>array() { // + ['bits_per_pixel']=>integer() // + ['bmp_data_size']=>integer() // + ['colors_important']=>integer() // + ['colors_used']=>integer() // + ['compression']=>integer() // + ['data_offset']=>integer() // + ['filesize']=>integer() // + ['header_size']=>integer() // + ['height']=>integer() // + ['identifier']=>string() // + ['planes']=>integer() // + ['resolution_h']=>integer() // + ['resolution_v']=>integer() // + ['width']=>integer() // + } // + } // + ['type_os']=>string() // + ['type_version']=>integer() // + } // + + + ['bonk']=>array() { // BONK - lossy/lossless audio compression (www.bonkenc.org) + ['BONK']=>array() { // + ['channels']=>integer() // + ['downsampling_ratio']=>integer() // + ['joint_stereo']=>boolean() // + ['lossless']=>boolean() // + ['number_samples']=>integer() // + ['number_taps']=>integer() // + ['offset']=>integer() // + ['sample_rate']=>integer() // + ['samples_per_packet']=>integer() // + ['size']=>integer() // + ['version']=>integer() // + } // + ['INFO']=>array() { // + ['size']=>integer() // + ['offset']=>integer() // + ['version']=>integer() // + []=>array() { // + ['nextbit']=>integer() // + ['offset']=>integer() // + } // + } // + ['dataend']=>integer() // + ['dataoffset']=>integer() // + } // + + + ['flac']=>array() { // FLAC - Free Lossless Audio Compressor + ['SEEKTABLE']=>array() { // + []=>array() { // + ['offset']=>integer() // + ['samples']=>integer() // + } // + ['placeholders']=>integer() // + ['raw']=>array() { // + ['block_data']=>string() // + ['block_length']=>integer() // + ['block_type']=>integer() // + ['block_type_text']=>string() // + ['last_meta_block']=>boolean() // + ['offset']=>integer() // + } // + } // + ['STREAMINFO']=>array() { // + ['audio_signature']=>string() // + ['bits_per_sample']=>integer() // + ['channels']=>integer() // + ['max_block_size']=>integer() // + ['max_frame_size']=>integer() // + ['min_block_size']=>integer() // + ['min_frame_size']=>integer() // + ['raw']=>array() { // + ['block_data']=>string() // + ['block_length']=>integer() // + ['block_type']=>integer() // + ['block_type_text']=>string() // + ['last_meta_block']=>boolean() // + ['offset']=>integer() // + } // + ['sample_rate']=>integer() // + ['samples_stream']=>integer() // + } // + ['VORBIS_COMMENT']=>array() { // + ['raw']=>array() { // + ['block_data']=>string() // + ['block_length']=>integer() // + ['block_type']=>integer() // + ['block_type_text']=>string() // + ['last_meta_block']=>boolean() // + ['offset']=>integer() // + } // + } // + ['compressed_audio_bytes']=>integer() // + ['compression_ratio']=>double() // + ['uncompressed_audio_bytes']=>integer() // + } // + + + ['gif']=>array() { // GIF - Graphics Interchange Format + ['global_color_table']=>array() { // + []=>integer() // + } // + ['header']=>array() { // + ['bits_per_pixel']=>integer() // + ['flags']=>array() { // + ['global_color_sorted']=>boolean() // + ['global_color_table']=>boolean() // + } // + ['global_color_size']=>integer() // + ['raw']=>array() { // + ['aspect_ratio']=>integer() // + ['bg_color_index']=>integer() // + ['flags']=>integer() // + ['height']=>integer() // + ['identifier']=>string() // + ['version']=>string() // + ['width']=>integer() // + } // + } // + ['version']=>string() // + } // + + + ['id3v1']=>array() { // ID3v1 + ['album']=>string() // + ['artist']=>string() // + ['comment']=>string() // + ['genre']=>string() // + ['genreid']=>integer() // + ['title']=>string() // + ['track']=>integer() // + ['year']=>string() // + ['padding_valid']=>boolean() // + ['comments']=>array() // + ['tag_offset_start']=>integer() // + ['tag_offset_end']=>integer() // + } // + + + ['id3v2']=>array() { // ID3v2 - www.id3.org + []=>array() { // can be any of the 4-character (3-character in ID3v2.2) frame names allowed in the ID3v2 spec. Exact contents of returned array data varies with frame type. + []=>array() { // some frames types allow multiple values ('COMM' for example), others do not and do not have this array level + ['asciidata']=>boolean() // + ['asciidescription']=>string() // + ['data']=>boolean() // + ['datalength']=>integer() // + ['dataoffset']=>integer() // + ['description']=>string() // + ['encoding']=>string() // + ['encodingid']=>integer() // + ['flags']=>array() { // + ['Encryption']=>boolean() // + ['FileAlterPreservation']=>boolean() // + ['GroupingIdentity']=>boolean() // + ['ReadOnly']=>boolean() // + ['TagAlterPreservation']=>boolean() // + ['compression']=>boolean() // + } // + ['framenamelong']=>string() // + ['language']=>string() // + ['languagename']=>string() // + } // + } // + ['comments']=>array() { // array of array of strings containing best data from any available metainformation tag (APE, ID3v2, ID3v1, Lyrics3, Vorbis, ASF, RIFF, Real, etc.) + []=>array() // can be anything, usually 'artist', 'title', etc. Contains array of one or more values (eg: multiple artists are possible) + } // + ['flags']=>array() { // + ['experim']=>string() // + ['exthead']=>string() // + ['unsynch']=>string() // + } // + ['header']=>boolean() // + ['headerlength']=>integer() // + ['majorversion']=>integer() // + ['minorversion']=>integer() // + ['padding']=>array() { // + ['length']=>integer() // + ['start']=>integer() // + ['valid']=>boolean() // + } // + ['tag_offset_end']=>integer() // + ['tag_offset_start']=>integer() // + } // + + + ['iso']=>array() { // ISO-9660 - CD-ROM Image + ['directories']=>array() { // + []=>array() { // + []=>array() { // + ['file_flags']=>array() { // + ['associated']=>boolean() // + ['directory']=>boolean() // + ['extended']=>boolean() // + ['hidden']=>boolean() // + ['multiple']=>boolean() // + ['permissions']=>boolean() // + } // + ['file_identifier_ascii']=>string() // + ['filename']=>string() // + ['filesize']=>integer() // + ['offset_bytes']=>integer() // + ['raw']=>array() { // + ['extended_attribute_length']=>integer() // + ['file_flags']=>integer() // + ['file_identifier']=>string() // + ['file_identifier_length']=>integer() // + ['file_unit_size']=>integer() // + ['filesize']=>integer() // + ['interleave_gap_size']=>integer() // + ['length']=>integer() // + ['offset_logical']=>integer() // + ['recording_date_time']=>string() // + ['volume_sequence_number']=>integer() // + } // + ['recording_timestamp']=>integer() // + } // + } // + } // + ['files']=>array() { // multidimensional tree-structure array listing of all files and directories in image + []=>array() // entries of type array are directories (key is directory name), may contain files and/or other subdirectories + []=>integer() // entries of type integer are files (key is file name, value is file size in bytes) + } // + ['path_table']=>array() { // + ['directories']=>array() { // + []=>array() { // + ['extended_length']=>integer() // + ['full_path']=>string() // + ['length']=>integer() // + ['location_bytes']=>integer() // + ['location_logical']=>integer() // + ['name']=>string() // + ['name_ascii']=>string() // + ['parent_directory']=>integer() // + } // + } // + ['offset']=>integer() // + ['raw']=>string() // + } // + ['primary_volume_descriptor']=>array() { // + ['abstract_file_identifier']=>string() // + ['application_identifier']=>string() // + ['bibliographic_file_identifier']=>string() // + ['copyright_file_identifier']=>string() // + ['data_preparer_identifier']=>string() // + ['offset']=>integer() // + ['publisher_identifier']=>string() // + ['raw']=>array() { // + ['abstract_file_identifier']=>string() // + ['application_data']=>string() // + ['application_identifier']=>string() // + ['bibliographic_file_identifier']=>string() // + ['copyright_file_identifier']=>string() // + ['data_preparer_identifier']=>string() // + ['file_structure_version']=>integer() // + ['logical_block_size']=>integer() // + ['path_table_l_location']=>integer() // + ['path_table_l_opt_location']=>integer() // + ['path_table_m_location']=>integer() // + ['path_table_m_opt_location']=>integer() // + ['path_table_size']=>integer() // + ['publisher_identifier']=>string() // + ['root_directory_record']=>string() // + ['standard_identifier']=>string() // + ['system_identifier']=>string() // + ['unused_1']=>string() // + ['unused_2']=>string() // + ['unused_3']=>string() // + ['unused_4']=>integer() // + ['volume_creation_date_time']=>string() // + ['volume_descriptor_type']=>integer() // + ['volume_descriptor_version']=>integer() // + ['volume_effective_date_time']=>string() // + ['volume_expiration_date_time']=>string() // + ['volume_identifier']=>string() // + ['volume_modification_date_time']=>string() // + ['volume_sequence_number']=>integer() // + ['volume_set_identifier']=>string() // + ['volume_set_size']=>integer() // + ['volume_space_size']=>integer() // + } // + ['system_identifier']=>string() // + ['volume_creation_date_time']=>integer() // + ['volume_effective_date_time']=>boolean() // + ['volume_expiration_date_time']=>boolean() // + ['volume_identifier']=>string() // + ['volume_modification_date_time']=>integer() // + ['volume_set_identifier']=>string() // + } // + ['supplementary_volume_descriptor']=>array() { // + ['abstract_file_identifier']=>string() // + ['application_identifier']=>string() // + ['bibliographic_file_identifier']=>string() // + ['copyright_file_identifier']=>string() // + ['data_preparer_identifier']=>string() // + ['offset']=>integer() // + ['publisher_identifier']=>string() // + ['raw']=>array() { // + ['abstract_file_identifier']=>string() // + ['application_data']=>string() // + ['application_identifier']=>string() // + ['bibliographic_file_identifier']=>string() // + ['copyright_file_identifier']=>string() // + ['data_preparer_identifier']=>string() // + ['file_structure_version']=>integer() // + ['logical_block_size']=>integer() // + ['path_table_l_location']=>integer() // + ['path_table_l_opt_location']=>integer() // + ['path_table_m_location']=>integer() // + ['path_table_m_opt_location']=>integer() // + ['path_table_size']=>integer() // + ['publisher_identifier']=>string() // + ['root_directory_record']=>string() // + ['standard_identifier']=>string() // + ['system_identifier']=>string() // + ['unused_1']=>string() // + ['unused_2']=>string() // + ['unused_3']=>string() // + ['unused_4']=>integer() // + ['volume_creation_date_time']=>string() // + ['volume_descriptor_type']=>integer() // + ['volume_descriptor_version']=>integer() // + ['volume_effective_date_time']=>string() // + ['volume_expiration_date_time']=>string() // + ['volume_identifier']=>string() // + ['volume_modification_date_time']=>string() // + ['volume_sequence_number']=>integer() // + ['volume_set_identifier']=>string() // + ['volume_set_size']=>integer() // + ['volume_space_size']=>integer() // + } // + ['system_identifier']=>string() // + ['volume_creation_date_time']=>integer() // + ['volume_effective_date_time']=>boolean() // + ['volume_expiration_date_time']=>boolean() // + ['volume_identifier']=>string() // + ['volume_modification_date_time']=>integer() // + ['volume_set_identifier']=>string() // + } // + } // + + + ['jpg']=>array() { // JPEG - still image + ['exif']=>array() // data returned from PHP's exif_read_data() function + } // + + + ['la']=>array() { // LA - Lossless Audio (www.lossless-audio.com) + ['raw']=>array() { + ['format']=>integer() // + ['flags']=>integer() // + } // + ['flags']=>array() { // + ['seekable']=>boolean() // + ['high_compression']=>boolean() // + } // + ['bits_per_sample']=>integer() // + ['bytes_per_sample']=>integer() // + ['bytes_per_second']=>integer() // + ['channels']=>integer() // + ['compression_ratio']=>double() // + ['format_size']=>integer() // + ['header_size']=>integer() // + ['original_crc']=>double() // + ['sample_rate']=>integer() // + ['samples']=>integer() // + ['uncompressed_size']=>integer() // + ['version']=>double() // + ['version_major']=>integer() // + ['version_minor']=>integer() // + ['footerstart']=>double() // + } + + + ['lpac']=>array() { // LPAC - Lossless Predictive Audio Compressor + ['block_length']=>integer() // + ['file_version']=>integer() // + ['flags']=>array() { // + ['16_bit']=>boolean() // + ['24_bit']=>boolean() // + ['adaptive_prediction_order']=>boolean() // + ['adaptive_quantization']=>boolean() // + ['fast_compress']=>boolean() // + ['is_wave']=>boolean() // + ['joint_stereo']=>boolean() // + ['max_prediction_order']=>integer() // + ['quantization']=>integer() // + ['random_access']=>boolean() // + ['stereo']=>boolean() // + } // + ['raw']=>array() { // + ['audio_type']=>integer() // + ['parameters']=>double() // + } // + ['total_samples']=>integer() // + } // + + + ['lyrics3']=>array() { // Lyrics3 - metainformation tags + ['comments']=>array() { // + ['album']=>string() // + ['artist']=>string() // + ['author']=>string() // + ['comment']=>string() // + ['title']=>string() // + } // + ['flags']=>array() { // + ['lyrics']=>boolean() // + ['timestamps']=>boolean() // + } // + ['images']=>array() { // + []=>array() { // + ['description']=>string() // + ['filename']=>string() // + ['timestamp']=>integer() // + } // + } // + ['raw']=>array() { // + ['offset_start']=>integer() // + ['offset_end']=>integer() // + ['AUT']=>string() // + ['EAL']=>string() // + ['EAR']=>string() // + ['ETT']=>string() // + ['IMG']=>string() // + ['IND']=>string() // + ['INF']=>string() // + ['LYR']=>string() // + ['lyrics3tagsize']=>integer() // + ['lyrics3version']=>integer() // + ['unparsed']=>string() // + } // + ['synchedlyrics']=>array() { // + []=>string() // + } // + ['unsynchedlyrics']=>string() // + } // + + + ['midi']=>array() { // MIDI (Musical Instrument Digital Interface) - sequenced music + ['comments']=>array() { // + ['comment']=>string() // + ['copyright']=>string() // + } // + ['keysignature']=>array() { // + []=>string() // + } // + ['raw']=>array() { // + ['events']=>array() { // + []=>array() { // + []=>array() { // + ['us_qnote']=>integer() // + } // + } // + } // + ['fileformat']=>integer() // + ['headersize']=>integer() // + ['ticksperqnote']=>integer() // + ['track']=>array() { // + []=>array() { // + ['instrument']=>string() // + ['instrumentid']=>integer() // + ['name']=>string() // + } // + } // + ['tracks']=>integer() // + } // + ['timesignature']=>array() { // + []=>string() // + } // + ['totalticks']=>integer() // + } // + + + ['monkeys_audio']=>array() { // Monkey's Audio - lossless audio compression + ['bitrate']=>double() // + ['bits_per_sample']=>integer() // + ['channels']=>integer() // + ['compressed_size']=>integer() // + ['compression']=>string() // + ['compression_ratio']=>double() // + ['flags']=>array() { // + ['24-bit']=>boolean() // + ['8-bit']=>boolean() // + ['crc-32']=>boolean() // + ['no_wav_header']=>boolean() // + ['peak_level']=>boolean() // + ['seek_elements']=>boolean() // + } // + ['frames']=>integer() // + ['peak_level']=>integer() // + ['peak_ratio']=>double() // + ['playtime']=>double() // + ['raw']=>array() { // + ['header_tag']=>string() // + ['nChannels']=>integer() // + ['nCompressionLevel']=>integer() // + ['nFinalFrameSamples']=>integer() // + ['nFormatFlags']=>integer() // + ['nPeakLevel']=>integer() // + ['nSampleRate']=>integer() // + ['nSeekElements']=>integer() // + ['nTotalFrames']=>integer() // + ['nVersion']=>integer() // + ['nWAVHeaderBytes']=>integer() // + ['nWAVTerminatingBytes']=>integer() // + } // + ['sample_rate']=>integer() // + ['samples']=>integer() // + ['samples_per_frame']=>integer() // + ['uncompressed_size']=>integer() // + ['version']=>double() // + } // + + + ['mpc']=>array() { // MPC (Musepack) - lossy audio compression + ['header']=>array() { // + ['album_gain_db']=>integer() // + ['album_peak']=>integer() // + ['album_peak_db']=>boolean() // + ['title_gain_db']=>integer() // + ['title_peak']=>integer() // + ['title_peak_db']=>boolean() // + ['begin_loud']=>boolean() // + ['end_loud']=>boolean() // + ['encoder_version']=>string() // + ['frame_count']=>integer() // + ['intensity_stereo']=>boolean() // + ['last_frame_length']=>integer() // + ['max_level']=>integer() // + ['max_subband']=>integer() // + ['mid_side_stereo']=>boolean() // + ['profile']=>string() // + ['sample_rate']=>integer() // + ['samples']=>integer() // + ['size']=>integer() // + ['stream_major_version']=>integer() // + ['stream_minor_version']=>integer() // + ['true_gapless']=>boolean() // + ['raw']=>array() { // + ['album_gain']=>integer() // + ['album_peak']=>integer() // + ['encoder_version']=>integer() // + ['preamble']=>string() // + ['profile']=>integer() // + ['sample_rate']=>integer() // + ['title_gain']=>integer() // + ['title_peak']=>integer() // + } // + } // + } // + + + ['mpeg']=>array() { // MPEG (Motion Picture Experts Group) - MPEG video and/or MPEG audio (MP3/MP2/MP1) + ['audio']=>array() { // + ['LAME']=>array() { // + ['RGAD']=>array() { // + ['peak_amplitude']=>double() // + } // + ['ath_type']=>integer() // + ['audio_bytes']=>integer() // + ['bitrate_min']=>integer() // + ['encoder_delay']=>integer() // + ['encoding_flags']=>array() { // + ['nogap_next']=>boolean() // + ['nogap_prev']=>boolean() // + ['nspsytune']=>boolean() // + ['nssafejoint']=>boolean() // + } // + ['end_padding']=>integer() // + ['lame_tag_crc']=>integer() // + ['lowpass_frequency']=>integer() // + ['mp3_gain_db']=>double() // + ['mp3_gain_factor']=>double() // + ['mp3_gain_raw']=>integer() // + ['music_crc']=>integer() // + ['noise_shaping']=>integer() // + ['noise_shaping_raw']=>integer() // + ['not_optimal_quality']=>boolean() // + ['not_optimal_quality_raw']=>integer() // + ['preset_used_id']=>integer() // + ['short_version']=>string() // ex: "LAME 3.93" + ['long_version']=>string() // (pre-v3.90 only) ex: "LAME 3.88 (alpha)" + ['source_sample_freq']=>string() // + ['source_sample_freq_raw']=>integer() // + ['stereo_mode']=>string() // + ['stereo_mode_raw']=>integer() // + ['surround_info']=>string() // + ['surround_info_id']=>integer() // + ['tag_revision']=>integer() // + ['vbr_method']=>string() // + ['vbr_method_raw']=>integer() // + } // + ['VBR_bitrate']=>double() // + ['VBR_bytes']=>integer() // + ['VBR_frames']=>integer() // + ['VBR_method']=>string() // + ['VBR_scale']=>integer() // + ['bitrate']=>integer() // + ['bitrate_distribution']=>array() { // + ['free']=>integer() // + ['8']=>integer() // + ['16']=>integer() // + ['24']=>integer() // + ['32']=>integer() // + ['40']=>integer() // + ['48']=>integer() // + ['56']=>integer() // + ['64']=>integer() // + ['80']=>integer() // + ['96']=>integer() // + ['112']=>integer() // + ['128']=>integer() // + ['144']=>integer() // + ['160']=>integer() // + } // + ['bitrate_mode']=>string() // + ['channelmode']=>string() // + ['channels']=>integer() // + ['copyright']=>boolean() // + ['crc']=>integer() // + ['emphasis']=>string() // + ['frame_count']=>integer() // + ['framelength']=>integer() // + ['layer']=>integer() // + ['modeextension']=>string() // + ['original']=>boolean() // + ['padding']=>boolean() // + ['private']=>boolean() // + ['protection']=>boolean() // + ['raw']=>array() { // + ['bitrate']=>integer() // + ['channelmode']=>integer() // + ['copyright']=>integer() // + ['emphasis']=>integer() // + ['layer']=>integer() // + ['modeextension']=>integer() // + ['original']=>integer() // + ['padding']=>integer() // + ['private']=>integer() // + ['protection']=>integer() // + ['sample_rate']=>integer() // + ['synch']=>integer() // + ['version']=>integer() // + } // + ['sample_rate']=>integer() // + ['stereo_distribution']=>array() { // + ['dual channel']=>integer() // + ['joint stereo']=>integer() // + ['mono']=>integer() // + ['stereo']=>integer() // + } // + ['toc']=>array() { // + []=>integer() // + } // + ['version']=>string() // + ['version_distribution']=>array() { // + []=>integer() // + []=>integer() // + ['2.5']=>integer() // + } // + ['xing_flags']=>array() { // + ['bytes']=>boolean() // + ['frames']=>boolean() // + ['toc']=>boolean() // + ['vbr_scale']=>boolean() // + } // + ['xing_flags_raw']=>string() // + } // + ['video']=>array() { // + ['bitrate']=>integer() // + ['bitrate_mode']=>string() // + ['frame_rate']=>double() // + ['framesize_horizontal']=>integer() // + ['framesize_vertical']=>integer() // + ['pixel_aspect_ratio']=>double() // + ['pixel_aspect_ratio_text']=>string() // + ['raw']=>array() { // + ['bitrate']=>integer() // + ['constrained_param_flag']=>integer() // + ['frame_rate']=>integer() // + ['framesize_horizontal']=>integer() // + ['framesize_vertical']=>integer() // + ['intra_quant_flag']=>integer() // + ['marker_bit']=>integer() // + ['pixel_aspect_ratio']=>integer() // + ['vbv_buffer_size']=>integer() // + } // + } // + } // + + + ['nsv']=>array() { // NSV - Nullsoft Streaming Video + ['NSVf']=>array() { // + ['TOC_entries_1']=>integer() // + ['TOC_entries_2']=>integer() // + ['file_size']=>integer() // + ['header_length']=>integer() // + ['identifier']=>string() // + ['meta_size']=>integer() // + ['metadata']=>string() // + ['playtime_ms']=>integer() // + } // + ['NSVs']=>array() { // + ['audio_codec']=>string() // + ['frame_rate']=>double() // + ['framerate_index']=>integer() // + ['identifier']=>string() // + ['offset']=>integer() // + ['resolution_x']=>integer() // + ['resolution_y']=>integer() // + ['unknown1b']=>integer() // + ['unknown1c']=>integer() // + ['unknown1d']=>integer() // + ['unknown2a']=>integer() // + ['unknown2b']=>integer() // + ['unknown2c']=>integer() // + ['unknown2d']=>integer() // + ['unknown3a']=>integer() // + ['unknown3b']=>integer() // + ['unknown3c']=>integer() // + ['unknown3d']=>integer() // + ['video_codec']=>string() // + } // + ['comments']=>array() { // + ['aspect']=>string() // + ['title']=>string() // + } // + } // + + + ['ofr']=>array() { // OFR (OptimFROG) - lossless audio compression + ['COMP']=>array() { // + []=>array() { // + ['channel_configuration']=>string() // + ['crc_32']=>boolean() // + ['encoder']=>string() // + ['offset']=>integer() // + ['raw']=>array() { // + ['algorithm_id']=>integer() // + ['channel_configuration']=>integer() // + ['encoder_id']=>integer() // + ['sample_type']=>integer() // + } // + ['sample_count']=>integer() // + ['sample_type']=>string() // + ['size']=>integer() // + } // + } // + ['HEAD']=>array() { // + ['offset']=>integer() // + ['size']=>integer() // + } // + ['OFR ']=>array() { // + ['channel_config']=>integer() // + ['channels']=>integer() // + ['compression']=>string() // + ['encoder']=>string() // + ['offset']=>integer() // + ['raw']=>array() { // + ['compression']=>integer() // + ['encoder_id']=>integer() // + ['sample_type']=>integer() // + } // + ['sample_rate']=>integer() // + ['sample_type']=>string() // + ['size']=>integer() // + ['total_samples']=>integer() // + } // + ['TAIL']=>array() { // + ['offset']=>integer() // + ['size']=>integer() // + } // + } // + + + ['ogg']=>array() { // OGG - container format for Ogg Vorbis, OggFLAC, Speex, etc + ['bitrate_average']=>double() // + ['bitrate_max']=>integer() // + ['bitrate_min']=>integer() // + ['bitrate_nominal']=>integer() // + ['bitstreamversion']=>integer() // + ['blocksize_large']=>integer() // + ['blocksize_small']=>integer() // + ['comments']=>array() { // array of array of strings containing best data from any available metainformation tag (APE, ID3v2, ID3v1, Lyrics3, Vorbis, ASF, RIFF, Real, etc.) + []=>array() // can be anything, usually 'artist', 'title', etc. Contains array of one or more values (eg: multiple artists are possible) + } // + ['comments_raw']=>array() { // + []=>array() { // + ['dataoffset']=>integer() // + ['key']=>string() // + ['size']=>integer() // + ['value']=>string() // + } // + } // + ['numberofchannels']=>integer() // + ['pageheader']=>array() { // + []=>array() { // + ['flags']=>array() { // + ['bos']=>boolean() // + ['eos']=>boolean() // + ['fresh']=>boolean() // + } // + ['flags_raw']=>integer() // + ['header_end_offset']=>integer() // + ['packet_type']=>integer() // + ['page_checksum']=>double() // + ['page_end_offset']=>integer() // + ['page_length']=>integer() // + ['page_segments']=>integer() // + ['page_seqno']=>integer() // + ['page_start_offset']=>integer() // + ['pcm_abs_position']=>integer() // + ['segment_table']=>array() { // + []=>integer() // + } // + ['stream_serialno']=>integer() // + ['stream_structver']=>integer() // + ['stream_type']=>string() // + } // + ['eos']=>array() { // + ['flags']=>array() { // + ['bos']=>boolean() // + ['eos']=>boolean() // + ['fresh']=>boolean() // + } // + ['flags_raw']=>integer() // + ['header_end_offset']=>integer() // + ['page_checksum']=>double() // + ['page_end_offset']=>integer() // + ['page_length']=>integer() // + ['page_segments']=>integer() // + ['page_seqno']=>integer() // + ['page_start_offset']=>integer() // + ['pcm_abs_position']=>integer() // + ['segment_table']=>array() { // + []=>integer() // + } // + ['stream_serialno']=>integer() // + ['stream_structver']=>integer() // + } // + } // + ['samplerate']=>integer() // + ['samples']=>integer() // + ['stop_bit']=>integer() // + ['vendor']=>string() // + } // + + + ['png']=>array() { // PNG (Portable Network Graphics) - still image + ['IDAT']=>array() { // + []=>array() { // + ['header']=>array() { // + ['crc']=>integer() // + ['data_length']=>integer() // + ['flags']=>array() { // + ['ancilliary']=>boolean() // + ['private']=>boolean() // + ['reserved']=>boolean() // + ['safe_to_copy']=>boolean() // + } // + ['type_raw']=>double() // + ['type_text']=>string() // + } // + } // + } // + ['IEND']=>array() { // + ['header']=>array() { // + ['crc']=>double() // + ['data']=>string() // + ['data_length']=>integer() // + ['flags']=>array() { // + ['ancilliary']=>boolean() // + ['private']=>boolean() // + ['reserved']=>boolean() // + ['safe_to_copy']=>boolean() // + } // + ['type_raw']=>double() // + ['type_text']=>string() // + } // + } // + ['IHDR']=>array() { // + ['color_type']=>array() { // + ['alpha']=>boolean() // + ['palette']=>boolean() // + ['true_color']=>boolean() // + } // + ['compression_method_text']=>string() // + ['header']=>array() { // + ['crc']=>double() // + ['data']=>string() // + ['data_length']=>integer() // + ['flags']=>array() { // + ['ancilliary']=>boolean() // + ['private']=>boolean() // + ['reserved']=>boolean() // + ['safe_to_copy']=>boolean() // + } // + ['type_raw']=>double() // + ['type_text']=>string() // + } // + ['height']=>integer() // + ['raw']=>array() { // + ['bit_depth']=>integer() // + ['color_type']=>integer() // + ['compression_method']=>integer() // + ['filter_method']=>integer() // + ['interlace_method']=>integer() // + } // + ['width']=>integer() // + } // + ['PLTE']=>array() { // + ['header']=>array() { // + ['crc']=>double() // + ['data']=>string() // + ['data_length']=>integer() // + ['flags']=>array() { // + ['ancilliary']=>boolean() // + ['private']=>boolean() // + ['reserved']=>boolean() // + ['safe_to_copy']=>boolean() // + } // + ['type_raw']=>double() // + ['type_text']=>string() // + } // + []=>integer() // + } // + ['comments']=>array() { // array of array of strings containing best data from any available metainformation tag (APE, ID3v2, ID3v1, Lyrics3, Vorbis, ASF, RIFF, Real, etc.) + []=>array() // can be anything, usually 'artist', 'title', etc. Contains array of one or more values (eg: multiple artists are possible) + } // + ['gAMA']=>array() { // + ['gamma']=>double() // + ['header']=>array() { // + ['crc']=>integer() // + ['data']=>string() // + ['data_length']=>integer() // + ['flags']=>array() { // + ['ancilliary']=>boolean() // + ['private']=>boolean() // + ['reserved']=>boolean() // + ['safe_to_copy']=>boolean() // + } // + ['type_raw']=>double() // + ['type_text']=>string() // + } // + } // + ['oFFs']=>array() { // + ['header']=>array() { // + ['crc']=>double() // + ['data']=>string() // + ['data_length']=>integer() // + ['flags']=>array() { // + ['ancilliary']=>boolean() // + ['private']=>boolean() // + ['reserved']=>boolean() // + ['safe_to_copy']=>boolean() // + } // + ['type_raw']=>double() // + ['type_text']=>string() // + } // + ['position_x']=>integer() // + ['position_y']=>integer() // + ['unit']=>string() // + ['unit_specifier']=>integer() // + } // + ['pHYs']=>array() { // + ['header']=>array() { // + ['crc']=>integer() // + ['data']=>string() // + ['data_length']=>integer() // + ['flags']=>array() { // + ['ancilliary']=>boolean() // + ['private']=>boolean() // + ['reserved']=>boolean() // + ['safe_to_copy']=>boolean() // + } // + ['type_raw']=>double() // + ['type_text']=>string() // + } // + ['pixels_per_unit_x']=>integer() // + ['pixels_per_unit_y']=>integer() // + ['unit']=>string() // + ['unit_specifier']=>integer() // + } // + ['pcLb']=>array() { // + ['header']=>array() { // + ['crc']=>double() // + ['data']=>string() // + ['data_length']=>integer() // + ['flags']=>array() { // + ['ancilliary']=>boolean() // + ['private']=>boolean() // + ['reserved']=>boolean() // + ['safe_to_copy']=>boolean() // + } // + ['type_raw']=>double() // + ['type_text']=>string() // + } // + } // + ['tEXt']=>array() { // + ['header']=>array() { // + ['crc']=>integer() // + ['data']=>string() // + ['data_length']=>integer() // + ['flags']=>array() { // + ['ancilliary']=>boolean() // + ['private']=>boolean() // + ['reserved']=>boolean() // + ['safe_to_copy']=>boolean() // + } // + ['type_raw']=>double() // + ['type_text']=>string() // + } // + ['keyword']=>string() // + ['text']=>string() // + } // + ['tIME']=>array() { // + ['day']=>integer() // + ['header']=>array() { // + ['crc']=>integer() // + ['data']=>string() // + ['data_length']=>integer() // + ['flags']=>array() { // + ['ancilliary']=>boolean() // + ['private']=>boolean() // + ['reserved']=>boolean() // + ['safe_to_copy']=>boolean() // + } // + ['type_raw']=>double() // + ['type_text']=>string() // + } // + ['hour']=>integer() // + ['minute']=>integer() // + ['month']=>integer() // + ['second']=>integer() // + ['unix']=>integer() // + ['year']=>integer() // + } // + ['tRNS']=>array() { // + ['header']=>array() { // + ['crc']=>double() // + ['data']=>string() // + ['data_length']=>integer() // + ['flags']=>array() { // + ['ancilliary']=>boolean() // + ['private']=>boolean() // + ['reserved']=>boolean() // + ['safe_to_copy']=>boolean() // + } // + ['type_raw']=>double() // + ['type_text']=>string() // + } // + ['transparent_color_blue']=>integer() // + ['transparent_color_green']=>integer() // + ['transparent_color_red']=>integer() // + } // + ['zTXt']=>array() { // + ['compressed_text']=>string() // + ['compression_method']=>integer() // + ['compression_method_text']=>string() // + ['header']=>array() { // + ['crc']=>double() // + ['data']=>string() // + ['data_length']=>integer() // + ['flags']=>array() { // + ['ancilliary']=>boolean() // + ['private']=>boolean() // + ['reserved']=>boolean() // + ['safe_to_copy']=>boolean() // + } // + ['type_raw']=>double() // + ['type_text']=>string() // + } // + ['keyword']=>string() // + ['text']=>string() // + } // + } // + + + ['quicktime']=>array() { // Quicktime - video/audio + ['']=>array() { // + ['name']=>boolean() // + ['offset']=>integer() // + ['size']=>integer() // + } // + ['audio']=>array() { // + ['bit_depth']=>integer() // + ['channels']=>integer() // + ['codec']=>string() // + ['sample_rate']=>double() // + } // + ['free']=>array() { // + ['name']=>string() // + ['offset']=>integer() // + ['size']=>integer() // + } // + ['mdat']=>array() { // + ['name']=>string() // + ['offset']=>integer() // + ['size']=>integer() // + } // + ['moov']=>array() { // + ['hierarchy']=>string() // + ['name']=>string() // + ['offset']=>integer() // + ['size']=>integer() // + ['subatoms']=>array() // This is an undocumentably-complex recursive array, typically containing a huge amount of seemingly disorganized data. Avoid this like the plague. + } // + ['time_scale']=>integer() // + ['display_scale']=>integer() // 1 = normal; 0.5 = half; 2 = double + ['video']=>array() { // + ['codec']=>string() // + ['color_depth']=>integer() // + ['color_depth_name']=>string() // + ['resolution_x']=>double() // + ['resolution_y']=>double() // + } // + ['wide']=>array() { // + ['name']=>string() // + ['offset']=>integer() // + ['size']=>integer() // + } // + } // + + + ['real']=>array() { // Real (RealAudio / RealVideo) - audio/video + ['chunks']=>array() { // + []=>array() { // + ['file_version']=>integer() // + ['headers_count']=>integer() // + ['length']=>integer() // + ['name']=>string() // + ['object_version']=>integer() // + ['offset']=>integer() // + } // + []=>array() { // + ['avg_bit_rate']=>integer() // + ['avg_packet_size']=>integer() // + ['data_offset']=>integer() // + ['duration']=>integer() // + ['flags']=>array() { // + ['live_broadcast']=>boolean() // + ['perfect_play']=>boolean() // + ['save_enabled']=>boolean() // + } // + ['flags_raw']=>integer() // + ['index_offset']=>integer() // + ['length']=>integer() // + ['max_bit_rate']=>integer() // + ['max_packet_size']=>integer() // + ['name']=>string() // + ['num_packets']=>integer() // + ['num_streams']=>integer() // + ['object_version']=>integer() // + ['offset']=>integer() // + ['preroll']=>integer() // + } // + } // + ['comments']=>array() { // + ['artist']=>string() // + ['comment']=>string() // + ['title']=>string() // + } // + } // + + + ['riff']=>array() { // RIFF (Resource Interchange File Format) - audio/video container format (AVI, WAV, CDDA, etc) + ['AIFC']=>array() { // + ['COMM']=>array() { // + []=>array() { // + ['data']=>string() // + ['offset']=>integer() // + ['size']=>integer() // + } // + } // + ['FVER']=>array() { // + []=>array() { // + ['data']=>string() // + ['offset']=>integer() // + ['size']=>integer() // + } // + } // + ['INST']=>array() { // + []=>array() { // + ['data']=>string() // + ['offset']=>integer() // + ['size']=>integer() // + } // + } // + ['MARK']=>array() { // + []=>array() { // + ['data']=>string() // + ['offset']=>integer() // + ['size']=>integer() // + } // + } // + ['SSND']=>array() { // + []=>array() { // + ['offset']=>integer() // + ['size']=>integer() // + } // + } // + } // + ['AIFF']=>array() { // + ['(c) ']=>array() { // + []=>array() { // + ['data']=>string() // + ['offset']=>integer() // + ['size']=>integer() // + } // + } // + ['COMM']=>array() { // + []=>array() { // + ['data']=>string() // + ['offset']=>integer() // + ['size']=>integer() // + } // + } // + ['SSND']=>array() { // + []=>array() { // + ['offset']=>integer() // + ['size']=>integer() // + } // + } // + } // + ['AVI ']=>array() { // + ['JUNK']=>array() { // + []=>array() { // + ['data']=>string() // + ['offset']=>integer() // + ['size']=>integer() // + } // + } // + ['hdrl']=>array() { // + ['avih']=>array() { // + []=>array() { // + ['data']=>string() // + ['offset']=>integer() // + ['size']=>integer() // + } // + } // + ['odml']=>array() { // + ['dmlh']=>array() { // + []=>array() { // + ['data']=>string() // + ['offset']=>integer() // + ['size']=>integer() // + } // + } // + } // + ['strl']=>array() { // + ['JUNK']=>array() { // + []=>array() { // + ['offset']=>integer() // + ['size']=>integer() // + } // + } // + ['strf']=>array() { // + []=>array() { // + ['data']=>string() // + ['offset']=>integer() // + ['size']=>integer() // + } // + } // + ['strh']=>array() { // + []=>array() { // + ['data']=>string() // + ['offset']=>integer() // + ['size']=>integer() // + } // + } // + ['strn']=>array() { // + []=>array() { // + ['data']=>string() // + ['offset']=>integer() // + ['size']=>integer() // + } // + } // + } // + } // + ['idx1']=>array() { // + []=>array() { // + ['data']=>string() // + ['offset']=>integer() // + ['size']=>integer() // + } // + } // + ['movi']=>array() { // + ['offset']=>integer() // + ['size']=>integer() // + } // + } // + ['CDDA']=>array() { // + ['fmt ']=>array() { // + []=>array() { // + ['data']=>string() // + ['disc_id']=>integer() // + ['offset']=>integer() // + ['playtime_frames']=>integer() // + ['playtime_seconds']=>double() // + ['size']=>integer() // + ['start_offset_frame']=>integer() // + ['start_offset_seconds']=>double() // + ['track_num']=>integer() // + ['unknown1']=>integer() // + ['unknown6']=>integer() // + ['unknown7']=>integer() // + } // + } // + } // + ['WAVE']=>array() { // + ['DISP']=>array() { // + []=>array() { // + ['data']=>string() // + ['offset']=>integer() // + ['size']=>integer() // + } // + } // + ['INFO']=>array() { // + ['IART']=>array() { // + []=>array() { // + ['data']=>string() // + ['offset']=>integer() // + ['size']=>integer() // + } // + } // + ['ICMT']=>array() { // + []=>array() { // + ['data']=>string() // + ['offset']=>integer() // + ['size']=>integer() // + } // + } // + ['ICOP']=>array() { // + []=>array() { // + ['data']=>string() // + ['offset']=>integer() // + ['size']=>integer() // + } // + } // + ['IENG']=>array() { // + []=>array() { // + ['data']=>string() // + ['offset']=>integer() // + ['size']=>integer() // + } // + } // + ['IGNR']=>array() { // + []=>array() { // + ['data']=>string() // + ['offset']=>integer() // + ['size']=>integer() // + } // + } // + ['IKEY']=>array() { // + []=>array() { // + ['data']=>string() // + ['offset']=>integer() // + ['size']=>integer() // + } // + } // + ['IMED']=>array() { // + []=>array() { // + ['data']=>string() // + ['offset']=>integer() // + ['size']=>integer() // + } // + } // + ['INAM']=>array() { // + []=>array() { // + ['data']=>string() // + ['offset']=>integer() // + ['size']=>integer() // + } // + } // + ['ISBJ']=>array() { // + []=>array() { // + ['data']=>string() // + ['offset']=>integer() // + ['size']=>integer() // + } // + } // + ['ISFT']=>array() { // + []=>array() { // + ['data']=>string() // + ['offset']=>integer() // + ['size']=>integer() // + } // + } // + ['ISRC']=>array() { // + []=>array() { // + ['data']=>string() // + ['offset']=>integer() // + ['size']=>integer() // + } // + } // + ['ISRF']=>array() { // + []=>array() { // + ['data']=>string() // + ['offset']=>integer() // + ['size']=>integer() // + } // + } // + ['ITCH']=>array() { // + []=>array() { // + ['data']=>string() // + ['offset']=>integer() // + ['size']=>integer() // + } // + } // + } // + ['MEXT']=>array() { // + []=>array() { // + ['anciliary_data_length']=>integer() // + ['data']=>string() // + ['flags']=>array() { // + ['anciliary_data_free']=>boolean() // + ['anciliary_data_left']=>boolean() // + ['anciliary_data_right']=>boolean() // + ['homogenous']=>boolean() // + } // + ['offset']=>integer() // + ['raw']=>array() { // + ['anciliary_data_def']=>integer() // + ['sound_information']=>integer() // + } // + ['size']=>integer() // + } // + } // + ['bext']=>array() { // + []=>array() { // + ['author']=>string() // + ['bwf_version']=>integer() // + ['coding_history']=>array() { // + []=>string() // + } // + ['data']=>string() // + ['offset']=>integer() // + ['origin_date']=>string() // + ['origin_date_unix']=>integer() // + ['origin_time']=>string() // + ['reference']=>string() // + ['reserved']=>integer() // + ['size']=>integer() // + ['time_reference']=>integer() // + ['title']=>string() // + } // + } // + ['cart']=>array() { // + []=>array() { // + ['artist']=>string() // + ['category']=>string() // + ['classification']=>string() // + ['client_id']=>string() // + ['cut_id']=>string() // + ['data']=>string() // + ['end_date']=>string() // + ['end_time']=>string() // + ['offset']=>integer() // + ['out_cue']=>string() // + ['post_time']=>array() { // + []=>array() { // + ['timer_value']=>integer() // + ['usage_fourcc']=>string() // + } // + } // + ['producer_app_id']=>string() // + ['producer_app_version']=>string() // + ['size']=>integer() // + ['start_date']=>string() // + ['start_time']=>string() // + ['tag_text']=>array() { // + []=>string() // + } // + ['title']=>string() // + ['url']=>string() // + ['user_defined_text']=>string() // + ['version']=>string() // + ['zero_db_reference']=>integer() // + } // + } // + ['data']=>array() { // + []=>array() { // + ['offset']=>integer() // + ['size']=>integer() // + } // + } // + ['fact']=>array() { // + []=>array() { // + ['data']=>string() // + ['offset']=>integer() // + ['size']=>integer() // + } // + } // + ['fmt ']=>array() { // + []=>array() { // + ['data']=>string() // + ['offset']=>integer() // + ['size']=>integer() // + } // + } // + ['rgad']=>array() { // + []=>array() { // + ['data']=>string() // + ['offset']=>integer() // + ['size']=>integer() // + } // + } // + } // + ['audio']=>array() { // + []=>array() { // + ['bitrate']=>integer() // + ['bits_per_sample']=>integer() // + ['channels']=>integer() // + ['codec']=>string() // + ['sample_rate']=>integer() // + } // + ['bits_per_sample']=>integer() // + ['channels']=>integer() // + ['codec_fourcc']=>string() // + ['codec_name']=>string() // + ['sample_rate']=>integer() // + ['total_samples']=>integer() // + } // + ['comments']=>array() { // array of array of strings containing best data from any available metainformation tag (APE, ID3v2, ID3v1, Lyrics3, Vorbis, ASF, RIFF, Real, etc.) + []=>array() // can be anything, usually 'artist', 'title', etc. Contains array of one or more values (eg: multiple artists are possible) + } // + ['header_size']=>integer() // + ['raw']=>array() { // + ['avih']=>array() { // + ['dwFlags']=>integer() // + ['dwHeight']=>integer() // + ['dwInitialFrames']=>integer() // + ['dwLength']=>integer() // + ['dwMaxBytesPerSec']=>integer() // + ['dwMicroSecPerFrame']=>integer() // + ['dwPaddingGranularity']=>integer() // + ['dwRate']=>integer() // + ['dwScale']=>integer() // + ['dwStart']=>integer() // + ['dwStreams']=>integer() // + ['dwSuggestedBufferSize']=>integer() // + ['dwTotalFrames']=>integer() // + ['dwWidth']=>integer() // + ['flags']=>array() { // + ['capturedfile']=>boolean() // + ['copyrighted']=>boolean() // + ['hasindex']=>boolean() // + ['interleaved']=>boolean() // + ['mustuseindex']=>boolean() // + ['trustcktype']=>boolean() // + } // + } // + ['fact']=>array() { // + ['NumberOfSamples']=>integer() // + } // + ['fmt ']=>array() { // + ['nAvgBytesPerSec']=>integer() // + ['wBitsPerSample']=>integer() // + ['nBlockAlign']=>integer() // + ['nChannels']=>integer() // + ['nSamplesPerSec']=>integer() // + ['wFormatTag']=>integer() // + } // + ['rgad']=>array() { // + ['audiophile']=>array() { // + ['adjustment']=>integer() // + ['name']=>integer() // + ['originator']=>integer() // + ['signbit']=>integer() // + } // + ['fPeakAmplitude']=>double() // + ['nAudiophileRgAdjust']=>integer() // + ['nRadioRgAdjust']=>integer() // + ['radio']=>array() { // + ['adjustment']=>integer() // + ['name']=>integer() // + ['originator']=>integer() // + ['signbit']=>integer() // + } // + } // + ['strf']=>array() { // + ['auds']=>array() { // + []=>array() { // + ['nAvgBytesPerSec']=>integer() // + ['wBitsPerSample']=>integer() // + ['nBlockAlign']=>integer() // + ['nChannels']=>integer() // + ['nSamplesPerSec']=>integer() // + ['wFormatTag']=>integer() // + } // + } // + ['vids']=>array() { // + []=>array() { // + ['biBitCount']=>integer() // + ['biClrImportant']=>integer() // + ['biClrUsed']=>integer() // + ['biHeight']=>integer() // + ['biPlanes']=>integer() // + ['biSize']=>integer() // + ['biSizeImage']=>integer() // + ['biWidth']=>integer() // + ['biXPelsPerMeter']=>integer() // + ['biYPelsPerMeter']=>integer() // + ['fourcc']=>string() // + } // + } // + } // + ['strh']=>array() { // + []=>array() { // + ['dwFlags']=>integer() // + ['dwInitialFrames']=>integer() // + ['dwLength']=>integer() // + ['dwQuality']=>integer() // + ['dwRate']=>integer() // + ['dwSampleSize']=>integer() // + ['dwScale']=>integer() // + ['dwStart']=>integer() // + ['dwSuggestedBufferSize']=>integer() // + ['fccHandler']=>string() // + ['fccType']=>string() // + ['rcFrame']=>integer() // + ['wLanguage']=>integer() // + ['wPriority']=>integer() // + } // + } // + } // + ['rgad']=>array() { // + ['audiophile']=>array() { // + ['adjustment']=>double() // + ['name']=>string() // + ['originator']=>string() // + } // + ['peakamplitude']=>double() // + ['radio']=>array() { // + ['adjustment']=>double() // + ['name']=>string() // + ['originator']=>string() // + } // + } // + ['video']=>array() { // + []=>array() { // + ['codec']=>string() // + ['frame_height']=>integer() // + ['frame_rate']=>double() // + ['frame_width']=>integer() // + } // + } // + ['litewave']=>array() { // http://www.clearjump.com + ['raw']=>array() { // + ['compression_method']=>integer() // 1=lossy; 2=lossless + ['compression_flags']=>integer() // + ['m_dwScale']=>integer() // scalefactor for lossy compression - related to m_wQuality as: $m_wQuality = round((2000 - $m_dwScale) / 20) + ['m_dwBlockSize']=>integer() // number of samples in encoded blocks + ['m_wQuality']=>integer() // quality factor (0=most compressed lossy; 99=best quality lossy; 100=lossless) + ['m_wMarkDistance']=>integer() // distance between marks in bytes + ['m_wReserved']=>integer() // + ['m_dwOrgSize']=>integer() // original file size in bytes + ['m_bFactExists']=>integer() // indicates if 'fact' chunk exists in the original file + ['m_dwRiffChunkSize']=>integer() // riff chunk size in the original file + } // + ['quality_factor']=>integer() // alias of ['raw']['m_wQuality'] + } // + } // + + + ['shn']=>array() { // Shorten - lossless audio compression + ['seektable']=>array() { // + ['length']=>integer() // + ['offset']=>integer() // + ['present']=>boolean() // + } // + ['version']=>integer() // + } // + + + ['swf']=>array() { // SWF - ShockWave Flash (www.openswf.org) + ['header']=>array() { // + ['frame_count']=>integer() // + ['frame_height']=>integer() // + ['frame_width']=>integer() // + ['length']=>integer() // + ['signature']=>string() // + ['version']=>integer() // + } // + ['bgcolor']=>string() // + ['tags']=>array() // + } // + + + ['voc']=>array() { // VOC - SoundBlaster VOC audio format + ['blocks']=>array() { // + []=>array() { // + ['bits_per_sample']=>integer() // + ['block_offset']=>integer() // + ['block_size']=>integer() // + ['block_type_id']=>integer() // + ['channels']=>integer() // + ['compression_name']=>string() // + ['compression_type']=>integer() // + ['pack_method']=>integer() // + ['sample_rate']=>integer() // + ['sample_rate_id']=>integer() // + ['stereo']=>boolean() // + ['time_constant']=>integer() // + ['wFormat']=>integer() // + } // + } // + ['compressed_bits_per_sample']=>integer() // + ['header']=>array() { // + ['datablock_offset']=>integer() // + ['major_version']=>integer() // + ['minor_version']=>integer() // + } // + } // + + + ['vqf']=>array() { // VQF - transform-domain weighted interleave Vector Quantization Format (lossy audio) + ['COMM']=>array() { // + ['bitrate']=>integer() // + ['channel_mode']=>integer() // + ['sample_rate']=>integer() // + ['security_level']=>integer() // + } // + ['DSIZ']=>integer() // + ['comments']=>array() { // array of array of strings containing best data from any available metainformation tag (APE, ID3v2, ID3v1, Lyrics3, Vorbis, ASF, RIFF, Real, etc.) + []=>array() // can be anything, usually 'artist', 'title', etc. Contains array of one or more values (eg: multiple artists are possible) + } // + ['raw']=>array() { // + ['header_tag']=>string() // + ['size']=>integer() // + ['version']=>string() // + } // + } // + + + ['wavpack']=>array() { // WavPack - lossless audio compression + ['bits']=>integer() // + ['crc1']=>double() // + ['crc2']=>integer() // + ['extension']=>string() // + ['extra_bc']=>string() // + ['extras']=>string() // + ['flags_raw']=>integer() // + ['offset']=>integer() // + ['shift']=>integer() // + ['size']=>integer() // + ['total_samples']=>integer() // + ['version']=>integer() // + } // + + + ['zip']=>array() { // ZIP - lossless data compression + ['central_directory']=>array() { // + []=>array() { // + ['compressed_size']=>integer() // + ['compression_method']=>string() // + ['create_version']=>string() // + ['entry_offset']=>integer() // + ['extract_version']=>string() // + ['filename']=>string() // + ['flags']=>array() { // + ['compression_speed']=>string() // + ['data_descriptor_used']=>boolean() // + ['encrypted']=>boolean() // + } // + ['host_os']=>string() // + ['last_modified_timestamp']=>integer() // + ['offset']=>integer() // + ['raw']=>array() { // + ['compressed_size']=>integer() // + ['compression_method']=>integer() // + ['crc_32']=>double() // + ['create_version']=>integer() // + ['disk_number_start']=>integer() // + ['external_file_attrib']=>double() // + ['extra_field_length']=>integer() // + ['extract_version']=>integer() // + ['file_comment_length']=>integer() // + ['filename_length']=>integer() // + ['general_flags']=>integer() // + ['internal_file_attrib']=>integer() // + ['last_mod_file_date']=>integer() // + ['last_mod_file_time']=>integer() // + ['local_header_offset']=>integer() // + ['signature']=>integer() // + ['uncompressed_size']=>integer() // + } // + ['uncompressed_size']=>integer() // + } // + } // + ['comments']=>array() { // + ['comment']=>string() // + } // + ['compressed_size']=>integer() // + ['compression_method']=>string() // + ['compression_speed']=>string() // + ['end_central_directory']=>array() { // + ['comment']=>string() // + ['comment_length']=>integer() // + ['directory_entries_this_disk']=>integer() // + ['directory_entries_total']=>integer() // + ['directory_offset']=>integer() // + ['directory_size']=>integer() // + ['disk_number_current']=>integer() // + ['disk_number_start_directory']=>integer() // + ['offset']=>integer() // + ['signature']=>integer() // + } // + ['entries']=>array() { // + []=>array() { // + ['compressed_size']=>integer() // + ['compression_method']=>string() // + ['extract_version']=>string() // + ['filename']=>string() // + ['flags']=>array() { // + ['compression_speed']=>string() // + ['data_descriptor_used']=>boolean() // + ['encrypted']=>boolean() // + } // + ['host_os']=>string() // + ['last_modified_timestamp']=>integer() // + ['offset']=>integer() // + ['raw']=>array() { // + ['compressed_size']=>integer() // + ['compression_method']=>integer() // + ['crc_32']=>integer() // + ['extra_field_length']=>integer() // + ['extract_version']=>integer() // + ['filename_length']=>integer() // + ['general_flags']=>integer() // + ['last_mod_file_date']=>integer() // + ['last_mod_file_time']=>integer() // + ['signature']=>integer() // + ['uncompressed_size']=>integer() // + } // + ['uncompressed_size']=>integer() // + } // + } // + ['entries_count']=>integer() // + ['files']=>array() { // multidimensional tree-structure array listing of all files and directories in image + []=>array() // entries of type array are directories (key is directory name), may contain files and/or other subdirectories + []=>integer() // entries of type integer are files (key is file name, value is file size in bytes) + } // + ['uncompressed_size']=>integer() // + } // +} // diff --git a/includes/htmlparser/html_parser_inc.php b/includes/htmlparser/html_parser_inc.php new file mode 100644 index 0000000..83b0f66 --- /dev/null +++ b/includes/htmlparser/html_parser_inc.php @@ -0,0 +1,492 @@ +dc=array(" ","\t","\r","\n","<",">","\"","'","=","/"); + $this->nc=array("<",">","=","/"); + $this->qc=array("\"","'"); + $this->sc=array("\r","\n"," ","\t"); + $this->prevstate=array("state"=>0,"word"=>""); + $this->pg=&$grammar; + $this->pos=0; + $this->stacktag=array(); + $this->stacktagpos=-1; + $this->content=array(); + $this->content["contentpos"]=-1; + $this->c=&$this->content; + $this->cp=-1; + $this->quotstate=-1; + $this->allreadyparsed=0; + $this->text=""; + $this->processtag=0; + $this->processpar=0; + $this->processparvalue=0; + $this->slevel=array(0); + $this->slevelpos=0; + $this->quottype=""; + $this->skipto=""; + $this->incomment=0; + $this->tagreg=array(); + $this->wasquot=0; + + if(isset($this->data) && is_array($this->data)) { + $this->content=&$data; + $this->allreadyparsed=1; + return; + } + clearstatcache(); + $this->name=$data; + if (!$datatype) { + $this->name=$name; + $this->data=$data; + $this->length=strlen($this->data); + return; + } + if (!$fp=fopen($this->name,"rb")) { + $this->SetError(1,"Can't open file $this->name.",0,0,"Error"); + return; + } + flock($fp,1); + $this->data=fread($fp,filesize($this->name)); + flock($fp,3); + fclose($fp); + $this->length=strlen($this->data); + } + +/******************************************************************************************** + * Get word from data + ********************************************************************************************/ + function GetWord(&$word) { + $word=""; + $this->wasquot=0; + if ($this->pos>$this->length) return false; + while (1) { + if ($this->pos>$this->length) return false; + if ($this->pos==$this->length) { + $this->pos++; + return true; + } + if ($this->data[$this->pos]=="<") { + if ($this->data[$this->pos+1]=="!") + if ($this->length>6 && $this->length-$this->pos+1>6) { + if (substr($this->data,$this->pos,4)=="") { + $word.="-->"; + $this->pos+=3; + break; + } else + $word.=$this->data[$this->pos++]; + } + if ($this->incomment) break; + } + } + } + if (!$this->processtag) { + if ($this->data[$this->pos]=="<") { + $this->processtag=1; + $this->tagpos=strlen($this->text); + } else { + $this->text.=$this->data[$this->pos++]; + continue; + } + } + if (in_array($this->data[$this->pos],$this->dc)) { + if (($this->data[$this->pos]=="<" || $this->data[$this->pos]==">") && $this->quotstate==-1 && $this->processparvalue) { + $this->processparvalue=0; + return true; + } + if (in_array($this->data[$this->pos],$this->sc) && $this->quotstate==-1) { + $this->text.=$this->data[$this->pos++]; + if (strlen($word)) { + if ($this->processparvalue) $this->processparvalue=0; + return true; + } else + continue; + } + if (!strlen($word)) { + if (in_array($this->data[$this->pos],$this->qc) && $this->processpar) { + if ($this->quotstate==-1) { + $this->wasquot=1; + $this->quotstate*=-1; + $this->quottype=$this->data[$this->pos]; + $this->text.=$this->data[$this->pos++]; + continue; + } elseif ($this->quottype==$this->data[$this->pos]) { + $this->quotstate*=-1; + $this->quottype=$this->data[$this->pos]; + $this->processpar=$this->processparvalue=0; + $this->text.=$this->data[$this->pos++]; + return true; + } + } elseif (in_array($this->data[$this->pos],$this->nc)) { + $word.=$this->data[$this->pos]; + $this->text.=$this->data[$this->pos++]; + if ($this->processparvalue) + continue; + else + return true; + } + } else { + if (in_array($this->data[$this->pos],$this->qc) && $this->processpar) { + if ($this->quotstate==1) { + if ($this->data[$this->pos]==$this->quottype && $this->processparvalue) { + $this->quotstate*=-1; + $this->quottype=$this->data[$this->pos]; + $this->processpar=$this->processparvalue=0; + $this->text.=$this->data[$this->pos++]; +// continue; + } else { + if ($this->data[$this->pos]==$this->quottype) { + $this->quotstate*=-1; + $this->quottype=""; + } + $word.=$this->data[$this->pos]; + $this->text.=$this->data[$this->pos++]; + continue; + } + } + return true; + } else { + if (in_array($this->data[$this->pos],$this->nc)) { + if ($this->quotstate==-1) { + if ($this->processparvalue) { + if($this->data[$this->pos]!="/" && $this->data[$this->pos]!="=") return true; + $word.=$this->data[$this->pos]; + $this->text.=$this->data[$this->pos++]; + continue; + } + } else { + $word.=$this->data[$this->pos]; + $this->text.=$this->data[$this->pos++]; + continue; + } + return true; + } elseif ($this->quotstate==-1 && $this->processparvalue && strlen($word)) { + if ($this->data[$this->pos]==" ") { + $this->text.=$this->data[$this->pos++]; + $this->processparvalue=0; + return true; + } + } + } + } + } + $word.=$this->data[$this->pos]; + $this->text.=$this->data[$this->pos++]; + } + return true; + } + +/******************************************************************************************** + * Parse HTML code + ******************************************************************************************** + | +<[/]tagname> + +in/state 0 1 2 3 4 5 6 7 8 +< 1 -1 -1 -1 -1 -1 -1 -1 -1 +/ -1 7 6 6 6 6 -1 -1 -1 += -1 -1 -1 4 -1 -1 -1 -1 -1 +> -1 -1 -2 -2 -2 -2 -2 -1 -3 +anyword -1 2 3 3 5 3 -1 8 -1 + +-3 end parse close tag +-2 end parse open tag +-1 error + 0 begin parse + 1 got '<', waiting '/' or any word as tag name + 2 got any word as tagname, waiting '/' or '>' or any word as parameter name + 3 got any word as parameter name, waiting '/' or '>' or '=' or any word as parameter name + 4 got '=' waiting '/' or '>' or any word as parameter value + 5 got any word as parameter value, waiting '/' or '>' or any word as parameter name + 6 got '/' waiting '>' + 7 got '/', waiting any word as close tagname + 8 got any word as close tag name, waiting '>' + ********************************************************************************************/ + function Parse() { + $automat=array( +// states 0 1 2 3 4 5 6 7 8 + "0"=>array( 1, -1, -1, -1, -1, -1, -1, -1, -1),// < + "1"=>array(-1, 7, 6, 6, 6, 6, -1, -1, -1),// / + "2"=>array(-1, -1, -1, 4, -1, -1, -1, -1, -1),// = + "3"=>array(-1, -1, -2, -2, -2, -2, -2, -1, -3),// > + "4"=>array(-1, 2, 3, 3, 5, 3, -1, 8, -1) // any word + ); + if (!strlen($this->data)) return; + $instates=array("<"=>0,"/"=>1,"="=>2,">"=>3); + $parcount=0; + $state=0; + $this->c=&$this->content; + $this->cp=&$this->content["contentpos"]; + $this->stacktag[0]["tag"]=&$this->c; + $this->stacktag[0]["level"]=&$this->slevel; + $this->stacktag[0]["levelpos"]=0; + $this->stacktagpos=0; + while(1) { + if (!$isword=$this->GetWord($word)) break; + $w=strtolower($word); + if (!isset($instates[$w])) + $instate=4; + else + $instate=$instates[$w]; +//print htmlspecialchars($word).",$state,$instate,$this->quottype
"; + $state=$automat[$instate][$state]; + if ($this->wasquot && $state==6) $state=5; +//print htmlspecialchars($word).",$state
"; + switch($state) { + case -3:// end parse close tag + if (strlen($this->skipto) && $this->tagname!=$this->skipto) { + $parcount=$state=$this->processpar=$this->processparvalue=$this->processtag=0; + $this->pars=array(); + break; + } else + $this->skipto=""; + $script=($this->tagname=="script") ? 1:0; + $this->AddNewText(substr($this->text,0,$this->tagpos),$script); + $this->AddNewTag(0); + $parcount=$state=$this->processpar=$this->processparvalue=$this->processtag=0; + $this->quottype=""; + $this->quotstate=-1; + $this->text=""; + $this->pars=array(); + $this->tagpos=0; + break; + case -2:// end parse open tag + if (strlen($this->skipto)) { + $parcount=$state=$this->processpar=$this->processparvalue=$this->processtag=0; + $this->pars=array(); + break; + } + $this->AddNewText(substr($this->text,0,$this->tagpos)); + $this->AddNewTag(1,$xmlclose); + $parcount=$state=$this->processpar=$this->processparvalue=$this->processtag=0; + $this->quottype=""; + $this->quotstate=-1; + $this->text=""; + $this->pars=array(); + $this->tagpos=0; + if (isset($this->pg[$this->tagname]["nohavetags"]) && !strlen($this->skipto)) $this->skipto=$this->tagname; + break; + case -1:// Error found + $parcount=$state=$this->processpar=$this->processparvalue=$this->processtag=0; + $this->pars=array(); + if ($this->incomment) { + if (strlen($this->text)) { + $this->AddNewText($this->text); + $this->text=""; + $this->tagpos=0; + } + $this->AddNewText($word,0,1); + $this->incomment=0; + break; + } + if ($word=="<") { + $state=1; + $this->processtag=1; + $this->processparvalue=0; + $this->tagpos=strlen($this->text)-1; + $this->quottype=""; + $this->quotstate=-1; + } + break; + case 2:// got any word as tagname, waiting '/' or '>' or any word as parameter name + $this->tagname=$w; + $xmlclose=0; + if (!ereg("^[a-zA-Z0-9!_-]+$",$this->tagname) || strlen($this->skipto)) { + $parcount=$state=$this->processpar=$this->processparvalue=$this->processtag=0; + $this->quottype=""; + $this->quotstate=-1; + $this->pars=array(); + break; + } + break; + case 3:// got any word as parameter name, waiting '/' or '>' or '=' or any word as parameter name + $this->parname=$w; + if (!ereg("^[a-zA-Z0-9!_-]+$",$this->parname) || strlen($this->skipto)) { + $parcount=$state=$this->processpar=$this->processparvalue=$this->processtag=0; + $this->quottype=""; + $this->quotstate=-1; + $this->pars=array(); + break; + } + $this->processpar=1; + if ($w!="/") { + $parcount++; + $this->pars[$this->parname]["single"]=1; + } else + $xmlclose=1; + break; + case 4:// got '=' waiting '/' or '>' or any word as parameter value + $this->processparvalue=1; + break; + case 5:// got any word as parameter value, waiting '/' or '>' or any word as parameter name + if ($this->parname!="/") { + unset($this->pars[$this->parname]["single"]); + $this->pars[$this->parname]["value"]=$word; + $this->pars[$this->parname]["quot"]=$this->quottype; + } + $this->quottype=""; + $this->processpar=$this->processparvalue=0; + break; + case 6:// got '/' waiting '>' + $xmlclose=1; + break; + case 8:// got any word as close tag name, waiting '>' + $this->tagname=$w; + break; + } + $this->prevstate["states"]=$state; + $this->prevstate["word"]=$word; + } + if (strlen($this->text)) $this->AddNewText($this->text); + } +/******************************************************************************************** + * Add new tag + ********************************************************************************************/ + function AddNewTag($open,$xmlclose=0) { + $actionclose=0; + if (!$open && in_array( $this->tagname, $this->pg ) && $this->pg[$this->tagname]["endtag"]!="absent") $actionclose=1; + + if ($open) + for ($i=$this->stacktagpos;$i>0;$i--) { + $ct=&$this->stacktag[$i]["tag"]; + $t=&$ct[$ct["contentpos"]]; + $tagname=$t["data"]["name"]; + if (isset($this->pg[$tagname]["closeon"])) { + if (isset($this->pg[$tagname]["closeon"]["in"]) && sizeof($this->pg[$tagname]["closeon"]["in"]) && in_array($this->tagname,$this->pg[$tagname]["closeon"]["in"]) + || isset($this->pg[$tagname]["closeon"]["notin"]) && sizeof($this->pg[$tagname]["closeon"]["notin"]) && !in_array($this->tagname,$this->pg[$tagname]["closeon"]["notin"])) { + $actionclose=2; + break; + } + } + if ($actionclose!=2) $i=-1; + } + + if ($actionclose) { + if ($actionclose==1) { + $i=$this->FindTag($this->tagname); + if ($i>-1) + if ($this->tagreg[$this->tagname]!=$this->stacktag[$i]["num"]) + $i=-1; + } + if ($i>-1) { + $this->c=&$this->stacktag[$i]["tag"]; + $this->cp=&$this->c["contentpos"]; + $this->stacktagpos=$i; + if ($actionclose==1) { + $c=&$this->c[$this->c["contentpos"]]["content"]; + $cp=&$this->c[$this->c["contentpos"]]["content"]["contentpos"]; + $cp++; + $c[$cp]["type"]="tag"; + $c[$cp]["data"]["name"]=$this->tagname; + $c[$cp]["data"]["type"]="close"; + if (isset($this->tagreg[$this->tagname])) + if ($this->tagreg[$this->tagname]) + $this->tagreg[$this->tagname]--; + $this->stacktag[$this->stacktagpos]["num"]=$this->tagreg[$this->tagname]; + $this->stacktagpos--; + } + if ($this->stacktagposstacktag)) + for ($i=$this->stacktagpos+1;$istacktag);$i++) + unset($this->stacktag[$i]); + if ($actionclose==1) return; + } + } + $this->cp++; + $this->c[$this->cp]["type"]="tag"; + $this->c[$this->cp]["data"]["name"]=$this->tagname; + $this->c[$this->cp]["data"]["type"]=($open) ? "open" : "close"; + if (!$open) + if (isset($this->tagreg[$this->tagname])) + if ($this->tagreg[$this->tagname]) + $this->tagreg[$this->tagname]--; + if ($xmlclose) $this->c[$this->cp]["xmlclose"]=1; + if (sizeof($this->pars)) $this->c[$this->cp]["pars"]=$this->pars; + if ($open && !$xmlclose && in_array( $this->tagname, $this->pg ) && $this->pg[$this->tagname]["endtag"]!="absent") { + if (!isset($this->tagreg[$this->tagname])) $this->tagreg[$this->tagname]=0; + $this->tagreg[$this->tagname]++; + $this->stacktagpos++; + $this->stacktag[$this->stacktagpos]["tag"]=&$this->c; + $this->stacktag[$this->stacktagpos]["num"]=$this->tagreg[$this->tagname]; + $this->c[$this->cp]["content"]=array(); + $this->c[$this->cp]["content"]["contentpos"]=-1; + $this->c=&$this->c[$this->cp]["content"]; + $this->cp=&$this->c["contentpos"]; + } + } + +/******************************************************************************************** + * Add new text + ********************************************************************************************/ + function AddNewText($text,$script=0,$comment=0) { + if (!strlen($text)) return; + $this->cp++; + if (!$comment) + $this->c[$this->cp]["type"]="text"; + else + $this->c[$this->cp]["type"]="comment"; + if ($script) { + $inputarray=array("/_top/","/top.location.href/","/([ \n]+)?window\.name/","/parent.location/"); + $replarray=array("_echoserver_file_space","parent.frames('_echoserver_file_space').src","//window.name","parent.frames('_echoserver_file_space').src"); +/* + $text=str_replace("_top","_echoserver_file_space",$text); + $text=str_replace("top.location.href","parent.frames('_echoserver_file_space').src",$text); + $text=preg_replace("/([ \n]+)?window\.name/","//window.name",$text); +*/ + $text=preg_replace($inputarray,$replarray,$text); + + } + $this->c[$this->cp]["data"]=$text; + $this->text=""; + } + +/******************************************************************************************** + * Find first tag in stack + ********************************************************************************************/ + function FindTag($tagname) { + for($i=$this->stacktagpos;$i>=0;$i--) + if ($this->stacktag[$i]["tag"][$this->stacktag[$i]["tag"]["contentpos"]]["data"]["name"]==$tagname) + return $i; + return -1; + } +} + +} //_ECHOSERVER_HTML_PARSER +?> diff --git a/includes/htmlparser/htmlgrammar.cmp b/includes/htmlparser/htmlgrammar.cmp new file mode 100644 index 0000000..f4fcf0e --- /dev/null +++ b/includes/htmlparser/htmlgrammar.cmp @@ -0,0 +1 @@ +a:57:{s:3:"pre";a:4:{s:3:"tag";s:3:"pre";s:6:"endtag";s:7:"present";s:10:"nohavetags";s:0:"";s:4:"pars";a:0:{}}s:2:"hr";a:3:{s:3:"tag";s:2:"hr";s:6:"endtag";s:6:"absent";s:4:"pars";a:3:{s:5:"color";a:1:{s:3:"par";s:5:"color";}s:5:"width";a:1:{s:3:"par";s:5:"width";}s:7:"noshade";a:3:{s:3:"par";s:7:"noshade";s:6:"single";s:0:"";s:10:"editmethod";s:40:"_Translator_edit_standart_single_control";}}}s:8:"noframes";a:3:{s:3:"tag";s:8:"noframes";s:6:"endtag";s:7:"present";s:4:"pars";a:0:{}}s:8:"frameset";a:4:{s:3:"tag";s:8:"frameset";s:6:"endtag";s:9:"canabsent";s:14:"pictureforedit";s:12:"tags/tag.gif";s:4:"pars";a:8:{s:4:"rows";a:1:{s:3:"par";s:4:"rows";}s:4:"cols";a:1:{s:3:"par";s:4:"cols";}s:11:"frameborder";a:1:{s:3:"par";s:11:"frameborder";}s:6:"border";a:1:{s:3:"par";s:6:"border";}s:12:"framespacing";a:1:{s:3:"par";s:12:"framespacing";}s:12:"marginheight";a:1:{s:3:"par";s:12:"marginheight";}s:11:"marginwidth";a:1:{s:3:"par";s:11:"marginwidth";}s:8:"noresize";a:1:{s:3:"par";s:8:"noresize";}}}s:5:"frame";a:4:{s:3:"tag";s:5:"frame";s:6:"endtag";s:6:"absent";s:14:"pictureforedit";s:19:"tags/tag_frame2.gif";s:4:"pars";a:10:{s:4:"name";a:1:{s:3:"par";s:4:"name";}s:6:"target";a:1:{s:3:"par";s:6:"target";}s:9:"scrolling";a:1:{s:3:"par";s:9:"scrolling";}s:6:"border";a:1:{s:3:"par";s:6:"border";}s:11:"frameborder";a:1:{s:3:"par";s:11:"frameborder";}s:12:"framespacing";a:1:{s:3:"par";s:12:"framespacing";}s:12:"marginheight";a:1:{s:3:"par";s:12:"marginheight";}s:11:"marginwidth";a:1:{s:3:"par";s:11:"marginwidth";}s:8:"noresize";a:1:{s:3:"par";s:8:"noresize";}s:3:"src";a:3:{s:3:"par";s:3:"src";s:5:"width";s:2:"40";s:6:"height";s:1:"3";}}}s:6:"iframe";a:3:{s:3:"tag";s:6:"iframe";s:14:"pictureforedit";s:20:"tags/tag_iframe2.gif";s:4:"pars";a:0:{}}s:4:"html";a:6:{s:3:"tag";s:4:"html";s:6:"endtag";s:7:"present";s:13:"edittagsafter";s:4:"body";s:7:"comment";s:4:"Html";s:14:"pictureforedit";s:12:"tags/tag.gif";s:4:"pars";a:0:{}}s:4:"meta";a:4:{s:3:"tag";s:4:"meta";s:6:"endtag";s:6:"absent";s:13:"edittagsafter";s:4:"body";s:4:"pars";a:0:{}}s:6:"script";a:6:{s:3:"tag";s:6:"script";s:10:"nohavetags";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:19:"tags/tag_script.gif";s:14:"edittagsbefore";s:6:"script";s:4:"pars";a:2:{s:8:"language";a:1:{s:3:"par";s:8:"language";}s:3:"src";a:3:{s:3:"par";s:3:"src";s:5:"width";s:2:"40";s:6:"height";s:1:"3";}}}s:4:"nobr";a:4:{s:3:"tag";s:4:"nobr";s:7:"closeon";a:2:{s:5:"notin";a:14:{i:0;s:6:"strong";i:1;s:3:"sup";i:2;s:6:"center";i:3;s:4:"font";i:4;s:1:"i";i:5;s:1:"b";i:6;s:1:"u";i:7;s:2:"tt";i:8;s:1:"s";i:9;s:3:"big";i:10;s:5:"small";i:11;s:6:"strike";i:12;s:2:"br";i:13;s:6:"script";}s:2:"in";a:0:{}}s:6:"endtag";s:7:"present";s:4:"pars";a:0:{}}s:1:"p";a:5:{s:3:"tag";s:1:"p";s:7:"closeon";a:2:{s:5:"notin";a:23:{i:0;s:1:"a";i:1;s:3:"map";i:2;s:4:"area";i:3;s:6:"strong";i:4;s:3:"sup";i:5;s:4:"font";i:6;s:1:"i";i:7;s:1:"b";i:8;s:1:"u";i:9;s:2:"tt";i:10;s:3:"img";i:11;s:1:"s";i:12;s:3:"big";i:13;s:5:"small";i:14;s:6:"strike";i:15;s:4:"nobr";i:16;s:2:"br";i:17;s:6:"script";i:18;s:5:"input";i:19;s:6:"select";i:20;s:8:"textarea";i:21;s:6:"option";i:22;s:6:"button";}s:2:"in";a:0:{}}s:6:"endtag";s:9:"canabsent";s:14:"pictureforedit";s:15:"tags/tag_p2.gif";s:4:"pars";a:1:{s:5:"align";a:2:{s:3:"par";s:5:"align";s:10:"editmethod";s:24:"_Translator_edit_p_align";}}}s:2:"th";a:3:{s:3:"tag";s:2:"th";s:6:"endtag";s:6:"absent";s:4:"pars";a:1:{s:5:"align";a:2:{s:3:"par";s:5:"align";s:10:"editmethod";s:25:"_Translator_edit_th_align";}}}s:8:"noscript";a:3:{s:3:"tag";s:8:"noscript";s:6:"endtag";s:7:"present";s:4:"pars";a:0:{}}s:5:"style";a:4:{s:3:"tag";s:5:"style";s:10:"nohavetags";s:0:"";s:6:"endtag";s:7:"present";s:4:"pars";a:0:{}}s:4:"head";a:4:{s:3:"tag";s:4:"head";s:6:"endtag";s:7:"present";s:13:"edittagsafter";s:4:"body";s:4:"pars";a:0:{}}s:6:"center";a:5:{s:3:"tag";s:6:"center";s:6:"endtag";s:7:"present";s:7:"comment";s:6:"Center";s:14:"pictureforedit";s:12:"tags/tag.gif";s:4:"pars";a:0:{}}s:3:"img";a:5:{s:3:"tag";s:3:"img";s:6:"endtag";s:6:"absent";s:7:"comment";s:7:"Picture";s:14:"pictureforedit";s:16:"tags/tag_img.gif";s:4:"pars";a:16:{s:3:"src";a:3:{s:3:"par";s:3:"src";s:5:"width";s:2:"40";s:6:"height";s:1:"3";}s:3:"alt";a:1:{s:3:"par";s:3:"alt";}s:5:"width";a:1:{s:3:"par";s:5:"width";}s:6:"height";a:1:{s:3:"par";s:6:"height";}s:6:"border";a:1:{s:3:"par";s:6:"border";}s:4:"name";a:1:{s:3:"par";s:4:"name";}s:5:"align";a:2:{s:3:"par";s:5:"align";s:10:"editmethod";s:26:"_Translator_edit_img_align";}s:6:"vspace";a:1:{s:3:"par";s:6:"vspace";}s:6:"hspace";a:1:{s:3:"par";s:6:"hspace";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:6:"usemap";a:2:{s:3:"par";s:6:"usemap";s:10:"editmethod";s:32:"_Translator_edit_standart_usemap";}}}s:1:"a";a:8:{s:3:"tag";s:1:"a";s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"edittagsbefore";s:1:"a";s:7:"comment";s:3:"Url";s:14:"pictureforedit";s:14:"tags/tag_a.gif";s:7:"closeon";a:2:{s:5:"notin";a:19:{i:0;s:6:"strong";i:1;s:3:"sup";i:2;s:6:"center";i:3;s:4:"font";i:4;s:1:"i";i:5;s:1:"b";i:6;s:1:"u";i:7;s:2:"tt";i:8;s:3:"img";i:9;s:1:"s";i:10;s:3:"big";i:11;s:5:"small";i:12;s:6:"strike";i:13;s:4:"nobr";i:14;s:2:"br";i:15;s:6:"script";i:16;s:2:"li";i:17;s:2:"ol";i:18;s:2:"ul";}s:2:"in";a:1:{i:0;s:1:"a";}}s:4:"pars";a:24:{s:4:"href";a:3:{s:3:"par";s:4:"href";s:5:"width";s:2:"40";s:6:"height";s:1:"3";}s:4:"name";a:1:{s:3:"par";s:4:"name";}s:8:"hreflang";a:1:{s:3:"par";s:8:"hreflang";}s:3:"rel";a:1:{s:3:"par";s:3:"rel";}s:3:"rev";a:1:{s:3:"par";s:3:"rev";}s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:9:"accesskey";a:1:{s:3:"par";s:9:"accesskey";}s:5:"shape";a:1:{s:3:"par";s:5:"shape";}s:7:"onfocus";a:1:{s:3:"par";s:7:"onfocus";}s:6:"onblur";a:1:{s:3:"par";s:6:"onblur";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}s:8:"tabindex";a:1:{s:3:"par";s:8:"tabindex";}s:6:"target";a:1:{s:3:"par";s:6:"target";}}}s:2:"ul";a:6:{s:3:"tag";s:2:"ul";s:6:"endtag";s:7:"present";s:7:"comment";s:15:"Unsequence list";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"closeon";a:2:{s:5:"notin";a:17:{i:0;s:6:"strong";i:1;s:3:"sup";i:2;s:4:"font";i:3;s:1:"i";i:4;s:1:"b";i:5;s:1:"u";i:6;s:2:"tt";i:7;s:1:"a";i:8;s:1:"s";i:9;s:3:"big";i:10;s:5:"small";i:11;s:6:"strike";i:12;s:2:"br";i:13;s:4:"nobr";i:14;s:6:"script";i:15;s:2:"li";i:16;s:2:"ol";}s:2:"in";a:3:{i:0;s:5:"table";i:1;s:2:"tr";i:2;s:2:"td";}}s:4:"pars";a:17:{s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:3:"dir";a:1:{s:3:"par";s:3:"dir";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:12:"onkeypressed";a:1:{s:3:"par";s:12:"onkeypressed";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}s:4:"type";a:1:{s:3:"par";s:4:"type";}}}s:2:"li";a:7:{s:3:"tag";s:2:"li";s:6:"endtag";s:7:"present";s:7:"comment";s:12:"List element";s:14:"pictureforedit";s:12:"tags/tag.gif";s:13:"nohavesametag";s:0:"";s:7:"closeon";a:2:{s:5:"notin";a:17:{i:0;s:6:"strong";i:1;s:3:"sup";i:2;s:4:"font";i:3;s:1:"i";i:4;s:1:"b";i:5;s:1:"u";i:6;s:2:"tt";i:7;s:1:"a";i:8;s:1:"s";i:9;s:3:"big";i:10;s:5:"small";i:11;s:6:"strike";i:12;s:2:"br";i:13;s:4:"nobr";i:14;s:6:"script";i:15;s:2:"ol";i:16;s:2:"ul";}s:2:"in";a:1:{i:0;s:2:"li";}}s:4:"pars";a:18:{s:5:"value";a:1:{s:3:"par";s:5:"value";}s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:3:"dir";a:1:{s:3:"par";s:3:"dir";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:12:"onkeypressed";a:1:{s:3:"par";s:12:"onkeypressed";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}s:4:"type";a:1:{s:3:"par";s:4:"type";}}}s:2:"ol";a:7:{s:3:"tag";s:2:"ol";s:6:"endtag";s:7:"present";s:7:"comment";s:13:"Sequence list";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"closeon";a:2:{s:5:"notin";a:17:{i:0;s:6:"strong";i:1;s:3:"sup";i:2;s:4:"font";i:3;s:1:"i";i:4;s:1:"b";i:5;s:1:"u";i:6;s:2:"tt";i:7;s:1:"a";i:8;s:1:"s";i:9;s:3:"big";i:10;s:5:"small";i:11;s:6:"strike";i:12;s:2:"br";i:13;s:4:"nobr";i:14;s:6:"script";i:15;s:2:"ol";i:16;s:2:"ul";}s:2:"in";a:1:{i:0;s:2:"ol";}}s:13:"nohavesametag";s:0:"";s:4:"pars";a:18:{s:5:"start";a:1:{s:3:"par";s:5:"start";}s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:3:"dir";a:1:{s:3:"par";s:3:"dir";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:12:"onkeypressed";a:1:{s:3:"par";s:12:"onkeypressed";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}s:4:"type";a:1:{s:3:"par";s:4:"type";}}}s:5:"title";a:5:{s:3:"tag";s:5:"title";s:6:"endtag";s:7:"present";s:7:"comment";s:11:"Page header";s:14:"pictureforedit";s:12:"tags/tag.gif";s:4:"pars";a:0:{}}s:8:"textarea";a:6:{s:3:"tag";s:8:"textarea";s:10:"nohavetags";s:0:"";s:6:"endtag";s:7:"present";s:7:"comment";s:8:"Textarea";s:14:"pictureforedit";s:21:"tags/tag_textarea.gif";s:4:"pars";a:26:{s:8:"disabled";a:3:{s:3:"par";s:8:"disabled";s:6:"single";s:0:"";s:10:"editmethod";s:40:"_Translator_edit_standart_single_control";}s:4:"name";a:1:{s:3:"par";s:4:"name";}s:4:"rows";a:1:{s:3:"par";s:4:"rows";}s:4:"cols";a:1:{s:3:"par";s:4:"cols";}s:9:"accesskey";a:1:{s:3:"par";s:9:"accesskey";}s:4:"wrap";a:1:{s:3:"par";s:4:"wrap";}s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:3:"dir";a:1:{s:3:"par";s:3:"dir";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:7:"onfocus";a:1:{s:3:"par";s:7:"onfocus";}s:6:"onblur";a:1:{s:3:"par";s:6:"onblur";}s:8:"onselect";a:1:{s:3:"par";s:8:"onselect";}s:8:"onchange";a:1:{s:3:"par";s:8:"onchange";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:12:"onkeypressed";a:1:{s:3:"par";s:12:"onkeypressed";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:2:"tr";a:6:{s:3:"tag";s:2:"tr";s:6:"endtag";s:7:"present";s:7:"comment";s:9:"Table row";s:14:"pictureforedit";s:15:"tags/tag_tr.gif";s:13:"edittagsafter";s:2:"td";s:4:"pars";a:20:{s:5:"align";a:2:{s:3:"par";s:5:"align";s:10:"editmethod";s:25:"_Translator_edit_td_align";}s:6:"valign";a:2:{s:3:"par";s:6:"valign";s:10:"editmethod";s:26:"_Translator_edit_td_valign";}s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:3:"dir";a:1:{s:3:"par";s:3:"dir";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:7:"bgcolor";a:1:{s:3:"par";s:7:"bgcolor";}s:10:"background";a:1:{s:3:"par";s:10:"background";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:12:"onkeypressed";a:1:{s:3:"par";s:12:"onkeypressed";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:2:"td";a:6:{s:3:"tag";s:2:"td";s:6:"endtag";s:7:"present";s:7:"comment";s:10:"Table cell";s:14:"pictureforedit";s:15:"tags/tag_td.gif";s:13:"edittagsafter";s:2:"td";s:4:"pars";a:25:{s:7:"colspan";a:1:{s:3:"par";s:7:"colspan";}s:7:"rowspan";a:1:{s:3:"par";s:7:"rowspan";}s:5:"width";a:1:{s:3:"par";s:5:"width";}s:6:"height";a:1:{s:3:"par";s:6:"height";}s:7:"bgcolor";a:1:{s:3:"par";s:7:"bgcolor";}s:10:"background";a:1:{s:3:"par";s:10:"background";}s:5:"align";a:2:{s:3:"par";s:5:"align";s:10:"editmethod";s:25:"_Translator_edit_td_align";}s:6:"valign";a:2:{s:3:"par";s:6:"valign";s:10:"editmethod";s:26:"_Translator_edit_td_valign";}s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:3:"dir";a:1:{s:3:"par";s:3:"dir";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:12:"onkeypressed";a:1:{s:3:"par";s:12:"onkeypressed";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}s:6:"nowrap";a:3:{s:3:"par";s:6:"nowrap";s:6:"single";s:0:"";s:10:"editmethod";s:40:"_Translator_edit_standart_single_control";}}}s:5:"table";a:5:{s:3:"tag";s:5:"table";s:6:"endtag";s:7:"present";s:7:"comment";s:5:"Table";s:14:"pictureforedit";s:18:"tags/tag_table.gif";s:4:"pars";a:26:{s:5:"width";a:1:{s:3:"par";s:5:"width";}s:6:"height";a:1:{s:3:"par";s:6:"height";}s:7:"bgcolor";a:1:{s:3:"par";s:7:"bgcolor";}s:10:"background";a:1:{s:3:"par";s:10:"background";}s:11:"cellspacing";a:1:{s:3:"par";s:11:"cellspacing";}s:11:"cellpadding";a:1:{s:3:"par";s:11:"cellpadding";}s:6:"border";a:1:{s:3:"par";s:6:"border";}s:11:"bordercolor";a:1:{s:3:"par";s:11:"bordercolor";}s:7:"summary";a:1:{s:3:"par";s:7:"summary";}s:5:"align";a:2:{s:3:"par";s:5:"align";s:10:"editmethod";s:28:"_Translator_edit_table_align";}s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:3:"dir";a:1:{s:3:"par";s:3:"dir";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:12:"onkeypressed";a:1:{s:3:"par";s:12:"onkeypressed";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:4:"area";a:5:{s:3:"tag";s:4:"area";s:6:"endtag";s:6:"absent";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"comment";s:10:"Map region";s:4:"pars";a:22:{s:4:"href";a:3:{s:3:"par";s:4:"href";s:5:"width";s:2:"40";s:6:"height";s:1:"3";}s:5:"shape";a:2:{s:3:"par";s:5:"shape";s:10:"editmethod";s:31:"_Translator_edit_standart_shape";}s:6:"coords";a:1:{s:3:"par";s:6:"coords";}s:6:"usemap";a:1:{s:3:"par";s:6:"usemap";}s:3:"alt";a:1:{s:3:"par";s:3:"alt";}s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:9:"accesskey";a:1:{s:3:"par";s:9:"accesskey";}s:7:"onfocus";a:1:{s:3:"par";s:7:"onfocus";}s:6:"onblur";a:1:{s:3:"par";s:6:"onblur";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}s:8:"tabindex";a:1:{s:3:"par";s:8:"tabindex";}}}s:4:"span";a:5:{s:3:"tag";s:4:"span";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"comment";s:4:"span";s:4:"pars";a:13:{s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:1:"b";a:7:{s:3:"tag";s:1:"b";s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:15:"tags/tag_b2.gif";s:7:"comment";s:1:"B";s:7:"closeon";a:1:{s:2:"in";a:1:{i:0;s:1:"b";}}s:4:"pars";a:13:{s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:2:"tt";a:7:{s:3:"tag";s:2:"tt";s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"comment";s:2:"TT";s:7:"closeon";a:1:{s:2:"in";a:1:{i:0;s:2:"tt";}}s:4:"pars";a:13:{s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:1:"i";a:7:{s:3:"tag";s:1:"i";s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:15:"tags/tag_i2.gif";s:7:"comment";s:1:"I";s:7:"closeon";a:1:{s:2:"in";a:1:{i:0;s:1:"i";}}s:4:"pars";a:13:{s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:3:"big";a:7:{s:3:"tag";s:3:"big";s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"comment";s:3:"Big";s:7:"closeon";a:1:{s:2:"in";a:1:{i:0;s:3:"big";}}s:4:"pars";a:13:{s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:5:"small";a:7:{s:3:"tag";s:5:"small";s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"comment";s:5:"Small";s:7:"closeon";a:1:{s:2:"in";a:1:{i:0;s:5:"small";}}s:4:"pars";a:13:{s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:6:"strike";a:7:{s:3:"tag";s:6:"strike";s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"comment";s:6:"Strike";s:7:"closeon";a:1:{s:2:"in";a:1:{i:0;s:6:"strike";}}s:4:"pars";a:13:{s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:1:"s";a:7:{s:3:"tag";s:1:"s";s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"comment";s:1:"S";s:7:"closeon";a:1:{s:2:"in";a:1:{i:0;s:1:"s";}}s:4:"pars";a:13:{s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:1:"u";a:7:{s:3:"tag";s:1:"u";s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:15:"tags/tag_u2.gif";s:7:"comment";s:1:"U";s:7:"closeon";a:1:{s:2:"in";a:1:{i:0;s:1:"u";}}s:4:"pars";a:13:{s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:3:"map";a:7:{s:3:"tag";s:3:"map";s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"comment";s:3:"Map";s:7:"closeon";a:2:{s:5:"notin";a:1:{i:0;s:4:"area";}s:2:"in";a:1:{i:0;s:3:"map";}}s:4:"pars";a:1:{s:4:"name";a:1:{s:3:"par";s:4:"name";}}}s:2:"br";a:4:{s:3:"tag";s:2:"br";s:4:"edit";s:1:"0";s:6:"endtag";s:6:"absent";s:4:"pars";a:5:{s:5:"clear";a:1:{s:3:"par";s:5:"clear";}s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:5:"style";a:1:{s:3:"par";s:5:"style";}}}s:4:"base";a:5:{s:3:"tag";s:4:"base";s:6:"endtag";s:6:"absent";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"comment";s:4:"Base";s:4:"pars";a:2:{s:4:"href";a:3:{s:3:"par";s:4:"href";s:5:"width";s:2:"40";s:6:"height";s:1:"3";}s:6:"target";a:1:{s:3:"par";s:6:"target";}}}s:8:"basefont";a:5:{s:3:"tag";s:8:"basefont";s:6:"endtag";s:6:"absent";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"comment";s:8:"Basefont";s:4:"pars";a:7:{s:2:"id";a:1:{s:3:"par";s:2:"id";}s:4:"size";a:1:{s:3:"par";s:4:"size";}s:5:"color";a:1:{s:3:"par";s:5:"color";}s:4:"face";a:1:{s:3:"par";s:4:"face";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"style";a:1:{s:3:"par";s:5:"style";}}}s:4:"body";a:8:{s:3:"tag";s:4:"body";s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:17:"tags/tag_body.gif";s:13:"edittagsafter";s:4:"body";s:7:"comment";s:9:"Page body";s:7:"closeon";a:1:{s:2:"in";a:1:{i:0;s:4:"body";}}s:4:"pars";a:27:{s:10:"background";a:1:{s:3:"par";s:10:"background";}s:4:"text";a:1:{s:3:"par";s:4:"text";}s:4:"link";a:1:{s:3:"par";s:4:"link";}s:5:"vlink";a:1:{s:3:"par";s:5:"vlink";}s:5:"alink";a:1:{s:3:"par";s:5:"alink";}s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:7:"bgcolor";a:1:{s:3:"par";s:7:"bgcolor";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:9:"topmargin";a:1:{s:3:"par";s:9:"topmargin";}s:10:"leftmargin";a:1:{s:3:"par";s:10:"leftmargin";}s:11:"marginwidth";a:1:{s:3:"par";s:11:"marginwidth";}s:12:"marginheight";a:1:{s:3:"par";s:12:"marginheight";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}s:6:"onload";a:1:{s:3:"par";s:6:"onload";}s:8:"onunload";a:1:{s:3:"par";s:8:"onunload";}}}s:6:"button";a:7:{s:3:"tag";s:6:"button";s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:19:"tags/tag_button.gif";s:7:"comment";s:6:"Button";s:7:"closeon";a:2:{s:5:"notin";a:15:{i:0;s:6:"center";i:1;s:4:"font";i:2;s:1:"i";i:3;s:1:"b";i:4;s:1:"u";i:5;s:2:"tt";i:6;s:1:"a";i:7;s:3:"img";i:8;s:1:"s";i:9;s:3:"big";i:10;s:5:"small";i:11;s:6:"strike";i:12;s:4:"nobr";i:13;s:2:"br";i:14;s:6:"script";}s:2:"in";a:1:{i:0;s:6:"button";}}s:4:"pars";a:18:{s:4:"name";a:1:{s:3:"par";s:4:"name";}s:5:"value";a:1:{s:3:"par";s:5:"value";}s:4:"type";a:1:{s:3:"par";s:4:"type";}s:8:"disabled";a:3:{s:3:"par";s:8:"disabled";s:6:"single";s:0:"";s:10:"editmethod";s:40:"_Translator_edit_standart_single_control";}s:9:"accesskey";a:1:{s:3:"par";s:9:"accesskey";}s:6:"usemap";a:1:{s:3:"par";s:6:"usemap";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}s:7:"onfocus";a:1:{s:3:"par";s:7:"onfocus";}s:6:"onblur";a:1:{s:3:"par";s:6:"onblur";}}}s:3:"div";a:5:{s:3:"tag";s:3:"div";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:16:"tags/tag_div.gif";s:7:"comment";s:8:"Division";s:4:"pars";a:15:{s:2:"id";a:1:{s:3:"par";s:2:"id";}s:4:"name";a:1:{s:3:"par";s:4:"name";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:5:"align";a:2:{s:3:"par";s:5:"align";s:10:"editmethod";s:26:"_Translator_edit_div_align";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:4:"font";a:6:{s:3:"tag";s:4:"font";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:18:"tags/tag_font2.gif";s:7:"comment";s:4:"Font";s:7:"closeon";a:2:{s:5:"notin";a:13:{i:0;s:1:"i";i:1;s:1:"b";i:2;s:1:"u";i:3;s:2:"tt";i:4;s:1:"a";i:5;s:3:"img";i:6;s:1:"s";i:7;s:3:"big";i:8;s:5:"small";i:9;s:6:"strike";i:10;s:4:"nobr";i:11;s:2:"br";i:12;s:6:"script";}s:2:"in";a:0:{}}s:4:"pars";a:9:{s:2:"id";a:1:{s:3:"par";s:2:"id";}s:4:"size";a:1:{s:3:"par";s:4:"size";}s:5:"color";a:1:{s:3:"par";s:5:"color";}s:4:"face";a:1:{s:3:"par";s:4:"face";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:3:"dir";a:1:{s:3:"par";s:3:"dir";}}}s:4:"form";a:5:{s:3:"tag";s:4:"form";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:17:"tags/tag_form.gif";s:7:"comment";s:4:"Form";s:4:"pars";a:23:{s:6:"action";a:3:{s:3:"par";s:6:"action";s:5:"width";s:2:"40";s:6:"height";s:1:"3";}s:6:"method";a:1:{s:3:"par";s:6:"method";}s:7:"enctype";a:1:{s:3:"par";s:7:"enctype";}s:14:"accept-charset";a:1:{s:3:"par";s:14:"accept-charset";}s:6:"accept";a:1:{s:3:"par";s:6:"accept";}s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:6:"target";a:1:{s:3:"par";s:6:"target";}s:8:"onsubmit";a:1:{s:3:"par";s:8:"onsubmit";}s:7:"onreset";a:1:{s:3:"par";s:7:"onreset";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:2:"h1";a:7:{s:3:"tag";s:2:"h1";s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"comment";s:2:"hl";s:7:"closeon";a:2:{s:5:"notin";a:15:{i:0;s:6:"strong";i:1;s:3:"sup";i:2;s:4:"font";i:3;s:1:"i";i:4;s:1:"b";i:5;s:1:"u";i:6;s:2:"tt";i:7;s:1:"a";i:8;s:1:"s";i:9;s:3:"big";i:10;s:5:"small";i:11;s:6:"strike";i:12;s:4:"nobr";i:13;s:2:"br";i:14;s:6:"script";}s:2:"in";a:1:{i:0;s:2:"h1";}}s:4:"pars";a:17:{s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:3:"dir";a:1:{s:3:"par";s:3:"dir";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"align";a:1:{s:3:"par";s:5:"align";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:2:"h2";a:7:{s:3:"tag";s:2:"h2";s:7:"closeon";a:2:{s:5:"notin";a:15:{i:0;s:6:"strong";i:1;s:3:"sup";i:2;s:4:"font";i:3;s:1:"i";i:4;s:1:"b";i:5;s:1:"u";i:6;s:2:"tt";i:7;s:1:"a";i:8;s:1:"s";i:9;s:3:"big";i:10;s:5:"small";i:11;s:6:"strike";i:12;s:4:"nobr";i:13;s:2:"br";i:14;s:6:"script";}s:2:"in";a:1:{i:0;s:2:"h2";}}s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"comment";s:2:"h2";s:4:"pars";a:17:{s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:3:"dir";a:1:{s:3:"par";s:3:"dir";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"align";a:1:{s:3:"par";s:5:"align";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:2:"h3";a:7:{s:3:"tag";s:2:"h3";s:7:"closeon";a:2:{s:5:"notin";a:15:{i:0;s:6:"strong";i:1;s:3:"sup";i:2;s:4:"font";i:3;s:1:"i";i:4;s:1:"b";i:5;s:1:"u";i:6;s:2:"tt";i:7;s:1:"a";i:8;s:1:"s";i:9;s:3:"big";i:10;s:5:"small";i:11;s:6:"strike";i:12;s:4:"nobr";i:13;s:2:"br";i:14;s:6:"script";}s:2:"in";a:1:{i:0;s:2:"h3";}}s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"comment";s:2:"h3";s:4:"pars";a:17:{s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:3:"dir";a:1:{s:3:"par";s:3:"dir";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"align";a:1:{s:3:"par";s:5:"align";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:2:"h4";a:7:{s:3:"tag";s:2:"h4";s:7:"closeon";a:2:{s:5:"notin";a:15:{i:0;s:6:"strong";i:1;s:3:"sup";i:2;s:4:"font";i:3;s:1:"i";i:4;s:1:"b";i:5;s:1:"u";i:6;s:2:"tt";i:7;s:1:"a";i:8;s:1:"s";i:9;s:3:"big";i:10;s:5:"small";i:11;s:6:"strike";i:12;s:4:"nobr";i:13;s:2:"br";i:14;s:6:"script";}s:2:"in";a:1:{i:0;s:2:"h4";}}s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"comment";s:2:"h4";s:4:"pars";a:17:{s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:3:"dir";a:1:{s:3:"par";s:3:"dir";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"align";a:1:{s:3:"par";s:5:"align";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:2:"h5";a:7:{s:3:"tag";s:2:"h5";s:7:"closeon";a:2:{s:5:"notin";a:15:{i:0;s:6:"strong";i:1;s:3:"sup";i:2;s:4:"font";i:3;s:1:"i";i:4;s:1:"b";i:5;s:1:"u";i:6;s:2:"tt";i:7;s:1:"a";i:8;s:1:"s";i:9;s:3:"big";i:10;s:5:"small";i:11;s:6:"strike";i:12;s:4:"nobr";i:13;s:2:"br";i:14;s:6:"script";}s:2:"in";a:1:{i:0;s:2:"h5";}}s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"comment";s:2:"h5";s:4:"pars";a:17:{s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:3:"dir";a:1:{s:3:"par";s:3:"dir";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"align";a:1:{s:3:"par";s:5:"align";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:2:"h6";a:7:{s:3:"tag";s:2:"h6";s:7:"closeon";a:2:{s:5:"notin";a:15:{i:0;s:6:"strong";i:1;s:3:"sup";i:2;s:4:"font";i:3;s:1:"i";i:4;s:1:"b";i:5;s:1:"u";i:6;s:2:"tt";i:7;s:1:"a";i:8;s:1:"s";i:9;s:3:"big";i:10;s:5:"small";i:11;s:6:"strike";i:12;s:4:"nobr";i:13;s:2:"br";i:14;s:6:"script";}s:2:"in";a:1:{i:0;s:2:"h6";}}s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"comment";s:2:"h6";s:4:"pars";a:17:{s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:3:"dir";a:1:{s:3:"par";s:3:"dir";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"align";a:1:{s:3:"par";s:5:"align";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:5:"input";a:5:{s:3:"tag";s:5:"input";s:6:"endtag";s:6:"absent";s:14:"pictureforedit";s:18:"tags/tag_input.gif";s:7:"comment";s:18:"Form input element";s:4:"pars";a:38:{s:4:"type";a:2:{s:3:"par";s:4:"type";s:10:"editmethod";s:27:"_Translator_edit_input_type";}s:4:"name";a:1:{s:3:"par";s:4:"name";}s:5:"value";a:1:{s:3:"par";s:5:"value";}s:7:"checked";a:3:{s:3:"par";s:7:"checked";s:6:"single";s:0:"";s:10:"editmethod";s:40:"_Translator_edit_standart_single_control";}s:8:"disabled";a:3:{s:3:"par";s:8:"disabled";s:6:"single";s:0:"";s:10:"editmethod";s:40:"_Translator_edit_standart_single_control";}s:8:"readonly";a:3:{s:3:"par";s:8:"readonly";s:6:"single";s:0:"";s:10:"editmethod";s:40:"_Translator_edit_standart_single_control";}s:4:"size";a:1:{s:3:"par";s:4:"size";}s:9:"maxlength";a:1:{s:3:"par";s:9:"maxlength";}s:3:"src";a:3:{s:3:"par";s:3:"src";s:5:"width";s:2:"40";s:6:"height";s:1:"3";}s:5:"width";a:1:{s:3:"par";s:5:"width";}s:6:"height";a:1:{s:3:"par";s:6:"height";}s:3:"alt";a:1:{s:3:"par";s:3:"alt";}s:6:"border";a:1:{s:3:"par";s:6:"border";}s:8:"tabindex";a:1:{s:3:"par";s:8:"tabindex";}s:9:"accesskey";a:1:{s:3:"par";s:9:"accesskey";}s:7:"onfocus";a:1:{s:3:"par";s:7:"onfocus";}s:6:"onblur";a:1:{s:3:"par";s:6:"onblur";}s:8:"onselect";a:1:{s:3:"par";s:8:"onselect";}s:8:"onchange";a:1:{s:3:"par";s:8:"onchange";}s:6:"accept";a:1:{s:3:"par";s:6:"accept";}s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:3:"dir";a:1:{s:3:"par";s:3:"dir";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:5:"align";a:1:{s:3:"par";s:5:"align";}s:5:"shape";a:1:{s:3:"par";s:5:"shape";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:5:"label";a:7:{s:3:"tag";s:5:"label";s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"comment";s:5:"Label";s:7:"closeon";a:1:{s:2:"in";a:1:{i:0;s:5:"label";}}s:4:"pars";a:20:{s:3:"for";a:1:{s:3:"par";s:3:"for";}s:9:"accesskey";a:1:{s:3:"par";s:9:"accesskey";}s:7:"onfocus";a:1:{s:3:"par";s:7:"onfocus";}s:6:"onblur";a:1:{s:3:"par";s:6:"onblur";}s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:3:"dir";a:1:{s:3:"par";s:3:"dir";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:6:"select";a:8:{s:3:"tag";s:6:"select";s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"edittagsbefore";s:6:"select";s:14:"pictureforedit";s:19:"tags/tag_select.gif";s:7:"comment";s:6:"Select";s:7:"closeon";a:2:{s:5:"notin";a:1:{i:0;s:6:"option";}s:2:"in";a:1:{i:0;s:6:"select";}}s:4:"pars";a:24:{s:8:"disabled";a:3:{s:3:"par";s:8:"disabled";s:6:"single";s:0:"";s:10:"editmethod";s:40:"_Translator_edit_standart_single_control";}s:8:"multiple";a:3:{s:3:"par";s:8:"multiple";s:6:"single";s:0:"";s:10:"editmethod";s:40:"_Translator_edit_standart_single_control";}s:4:"name";a:1:{s:3:"par";s:4:"name";}s:4:"size";a:1:{s:3:"par";s:4:"size";}s:5:"width";a:1:{s:3:"par";s:5:"width";}s:9:"accesskey";a:1:{s:3:"par";s:9:"accesskey";}s:7:"onfocus";a:1:{s:3:"par";s:7:"onfocus";}s:6:"onblur";a:1:{s:3:"par";s:6:"onblur";}s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:3:"dir";a:1:{s:3:"par";s:3:"dir";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}}}s:6:"option";a:7:{s:3:"tag";s:6:"option";s:7:"closeon";a:2:{s:5:"notin";a:15:{i:0;s:6:"strong";i:1;s:3:"sup";i:2;s:4:"font";i:3;s:1:"i";i:4;s:1:"b";i:5;s:1:"u";i:6;s:2:"tt";i:7;s:1:"a";i:8;s:1:"s";i:9;s:3:"big";i:10;s:5:"small";i:11;s:6:"strike";i:12;s:2:"br";i:13;s:4:"nobr";i:14;s:6:"script";}s:2:"in";a:2:{i:0;s:6:"option";i:1;s:6:"option";}}s:13:"nohavesametag";s:0:"";s:6:"endtag";s:7:"present";s:14:"pictureforedit";s:12:"tags/tag.gif";s:7:"comment";s:6:"Option";s:4:"pars";a:20:{s:4:"name";a:1:{s:3:"par";s:4:"name";}s:5:"value";a:1:{s:3:"par";s:5:"value";}s:9:"accesskey";a:1:{s:3:"par";s:9:"accesskey";}s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:3:"dir";a:1:{s:3:"par";s:3:"dir";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:7:"onclick";a:1:{s:3:"par";s:7:"onclick";}s:10:"ondblclick";a:1:{s:3:"par";s:10:"ondblclick";}s:11:"onmousedown";a:1:{s:3:"par";s:11:"onmousedown";}s:9:"onmouseup";a:1:{s:3:"par";s:9:"onmouseup";}s:11:"onmouseover";a:1:{s:3:"par";s:11:"onmouseover";}s:11:"onmousemove";a:1:{s:3:"par";s:11:"onmousemove";}s:10:"onmouseout";a:1:{s:3:"par";s:10:"onmouseout";}s:10:"onkeypress";a:1:{s:3:"par";s:10:"onkeypress";}s:9:"onkeydown";a:1:{s:3:"par";s:9:"onkeydown";}s:7:"onkeyup";a:1:{s:3:"par";s:7:"onkeyup";}s:8:"selected";a:3:{s:3:"par";s:8:"selected";s:6:"single";s:0:"";s:10:"editmethod";s:40:"_Translator_edit_standart_single_control";}}}s:4:"link";a:4:{s:3:"tag";s:4:"link";s:6:"endtag";s:6:"absent";s:14:"pictureforedit";s:17:"tags/tag_link.gif";s:4:"pars";a:13:{s:3:"rel";a:1:{s:3:"par";s:3:"rel";}s:3:"rev";a:1:{s:3:"par";s:3:"rev";}s:4:"href";a:1:{s:3:"par";s:4:"href";}s:6:"target";a:1:{s:3:"par";s:6:"target";}s:5:"media";a:1:{s:3:"par";s:5:"media";}s:8:"hreflang";a:1:{s:3:"par";s:8:"hreflang";}s:7:"charset";a:1:{s:3:"par";s:7:"charset";}s:5:"title";a:1:{s:3:"par";s:5:"title";}s:3:"dir";a:1:{s:3:"par";s:3:"dir";}s:4:"lang";a:1:{s:3:"par";s:4:"lang";}s:5:"style";a:1:{s:3:"par";s:5:"style";}s:2:"id";a:1:{s:3:"par";s:2:"id";}s:5:"class";a:1:{s:3:"par";s:5:"class";}}}s:21:"EDIT_TAGS_AFTER_TABLE";a:2:{i:0;s:4:"body";i:1;s:2:"td";}} \ No newline at end of file diff --git a/includes/htmlparser/htmlgrammar.dat b/includes/htmlparser/htmlgrammar.dat new file mode 100644 index 0000000..8329ad8 --- /dev/null +++ b/includes/htmlparser/htmlgrammar.dat @@ -0,0 +1,1176 @@ +/* +Îïèñàíèå ïàðàìåòðîâ ãðàììàòèêè +The grammar parameters description. + +endtag= "present | canabsent | absent" + present - çàêðûòèå òåãà îáÿçàòåëüíî äîëæíî ïðèñóòñòâîâàòü + - close tag must present + + canabsent - çàêðûòèå òåãà ìîæåò îòñóòñòâîâàòü, íî ìîæåò è ïðèñóòñòâîâàòü + - close tag can absent + + absent - çàêðûòèå òåãà âñåãäà îòñóòñòâóåò + - close tag always absent + +nohavesametag - òýã íå ìîæåò ñîäåðæàòü â ñåáå ñåáÿ ñàìîãî, åñëè âíóòðè òýãà âñòðå÷àåòñÿ + îí ñàì, òî òåã çàêðûâàåòñÿ + - tag can't contains itself inside. If it has itself inside then it must + be closed before itself + +nohavetags - òåã ñîäåðæèò òîëüêî òåêñò. Äàæå åñëè âíóòðè íåãî âñòðå÷àþòñÿ òýãè, òî + îíè òðàêòóþòñÿ êàê òåêñò. + - tag can has text only. If it has tags inside then these tags will be to interpret + as text. + +closeon = - "tagname|...|!tagname|..." - ïðèíóäèòåëüíîå çàêðûòèå òýãà ïðè âñòðå÷å îòêðûòèÿ + äðóãîãî òåãà. ìîæíî äåëàòü ïåðå÷èñëåíèÿ ìíîæåñòâà òåãîâ èëè îòðèöàíèå ìíîæåñòâà + òåãîâ, íàïðèìåð closeon="a|b|i|!form" òî åñòü çàêðûâàòü òåã ïðè âñòðå÷å îòêðûòèÿ + òåãîâ A,B,I è åñëè ýòî ê òîìó æå íå òåã FORM + - "tagname|...|!tagname|..." - rules for closing tag. |tagname|... list says + that tag must to be closed before first tagname if it exist inside it. + |!tagname|!.... list says then tag must to be closed if it has not a tagname + tag inside. You must combine these lists into one closeon part. For example: + closeon="a|b|i|!form" or closeon="a" or closeon="!form". + +Âû òàêæå ìîæåòå äîáàâëÿòü ñâîè ïàðàìåòðû â ãðàììàòèêó åñëè Âàì íàäî èñïîëüçîâàòü èõ â äàëüíåéøåì. +Íàïðèìåð, Âàì íàäî óêàçàòü êàêîé ôóíêöèåé âû áóäåòå îáðàáàòûâàòü òåã óæå ïîñëå ïàðñèíãà +òîãäà ïèøèòå, íàïðèìåð, function="MyFunction". +À ïîñëå ïðîöåññà ïàðñèíãà êîäà, ïðîáåãàÿ ïî äåðåâó òýãîâ çàãëÿäûâàåòå â ãðàììàòèêó ýòîãî òåãà +è, íàïðèìåð, âûçûâàåòå äëÿ íåãî ôóíêöèþ ñ ýòèì èìåíåì. +Òî÷íî òàê æå ìîæíî äîáàâëÿòü ïàðàìåòðû â îïèñàíèå ïàðàìåòðîâ òåãîâ. + +You can add your own parameters into grammar if it needs. For example, you need walk +through parsed grammar tree and if you meet someone tag you need process it by someone function. +In this case you can add special parameter for this tag, for example: function="MyFunction" and +after HTML parse process you will be walk through HTML tree and see into +grammar when you meet tag, and if it has function field you will call function by name in this field for this tag. +Using the same method you can add own parameter fields on tag parameters into grammar. + +I used this grammar in my HTML tag visualizer and you can see my additional parameter +"editmethod" for this with function names which I called in my visualizer. +you can see my additional parameter "pictureforedit" too. It needs for the pictogram for each tag. +I used so "width" and "height" additional parameters for visualize fields of tag with certain +width and height. + +BUT THESE ADDITIONAL PARAMETERS NOT NEEDS FOR HTML PARSER! IT WILL NEED AFTER ONLY! FOR +MY SPECIAL NEEDS. + +Ïðåêîìïèëåíàÿ ãðàììàòèêà èìååò ñëåäóþùóþ ñòðóêòóðó: +structure of precompiled grammar is: + +array( + "tagname"=>array( + "internal parameter name"=>value, + ..... + "pars"=>array( + "parname"=>array( + "parparameter name"=>value, + ..... + ), + ..... + ) + ) + .... +) +íàïðèìåð, ó âàñ â îòïàðñåíîì äåðåâå åñòü òåã . Åãî ïîëíûé íàáîð ïàðàìåòðîâ +â ïðåêîìïèëåíîé ãðàììàòèêå $grammar ìîæíî ïîñìîòðåòü ñëåäóþùèì îáðàçîì + +For example, if you have tag A into parsed tree, then you can see the +grammar for A tag as + +PrintArray($grammar["a"]) + +à òîëüêî íàáîð ïàðàìåòðîâ òåãà, íàïðèìåð òàê + +and parameters only for tag A as + +PrintArray($grammar["a"]["pars"]) +*/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/includes/htmlparser/htmlgrammarparser.inc b/includes/htmlparser/htmlgrammarparser.inc new file mode 100644 index 0000000..259fcce --- /dev/null +++ b/includes/htmlparser/htmlgrammarparser.inc @@ -0,0 +1,478 @@ +firstprev=array("state"=>0,"word"=>""); + $this->secondprev=array("state"=>0,"word"=>""); + $this->line=0; + $this->pos=0; + $this->errors=array(); + $this->errpos=-1; + $this->incomment=-1; + $this->allreadyparsed=0; + $this->pg=array(); + $this->pgpos=-1; + $this->quotstate=-1; + $this->iseof=false; + $this->firststate=0; + $this->secondstate=0; + $this->mode=1; + + if(gettype($this->data)=="array") { + $this->pg=&$data; + $this->allreadyparsed=1; + return; + } + clearstatcache(); + $this->name=$data; + if (!file_exists($this->name)) { + $this->SetError(1,"File $this->name not exists.",0,0,"Error"); + return; + } + if (!$fp=fopen($this->name,"r")) { + $this->SetError(1,"Can't open file $this->name.",0,0,"Error"); + return; + } + flock($fp,1); + $this->data=fread($fp,filesize($this->name)); + flock($fp,3); + fclose($fp); + $this->length=strlen($this->data); + } + +/******************************************************************************************** + * Store parser's errors and warnings + ********************************************************************************************/ + function SetError($e,$str,$line=0,$column=0,$errtype="Warning") { + $this->errors[++$this->errpos]["type"]=$errtype; + $this->errors[$this->errpos]["code"]=$e; + $this->err=$e; + $this->errstr="$errtype: $e, $str"; + if ($line) { + if (strlen($this->name)) + $this->errstr.="object $this->name"; + $this->errstr.=" Line $line, Column $column"; + } + $this->errors[$this->errpos]["str"]=$this->errstr."
\r\n"; + } + +/******************************************************************************************** + * Print parser's errors and warnings + ********************************************************************************************/ + function PrintErrors() { + for ($i=0;$i<=$this->errpos;$i++) + print $this->errors[$i]["str"]; + } + +/******************************************************************************************** + * Get word from data + ********************************************************************************************/ + function GetWord($word) { + $word=""; + $found=0; + $iter=0; + if ($this->pos>$this->length) + return false; + while (!$found) { + if ($this->pos>$this->length) + return false; + if ($this->pos==$this->length) { + $this->pos++; + return $word; + } + switch($this->data[$this->pos]) { + case "*": + if ($this->quotstate==1) { + $word.=$this->data[$this->pos++]; + $this->column++; + break; + } + $this->column++; + $this->pos++; + if ($word[0]=="/") + $found=1; + $word.=$this->data[$this->pos-1]; + break; + case "/": + if ($this->quotstate==1) { + $word.=$this->data[$this->pos++]; + $this->column++; + break; + } + $this->column++; + $this->pos++; + if ($word[0]=="*") + $found=1; + $word.=$this->data[$this->pos-1]; + break; + case " ": + case "\r": + case "\t": + if ($this->quotstate==1) { + $word.=$this->data[$this->pos++]; + $this->column++; + break; + } + $this->column++; + $this->pos++; + if (strlen($word)) + $found=1; + break; + case "\n": + if ($this->quotstate==1) { + $word.=$this->data[$this->pos++]; + $this->column++; + break; + } + $this->column=0; + $this->line++; + $this->pos++; + if (strlen($word)) + $found=1; + break; + case ">": + case "<": + case "=": + if ($this->quotstate==1) { + $word.=$this->data[$this->pos++]; + $this->column++; + } else { + if (!strlen($word)) { + $word=$this->data[$this->pos++]; + $this->column++; + } + $found=1; + } + break; + case "\"": + if ($this->pos) { + if ($this->data[$this->pos-1]=="\\") { + $word.=$this->data[$this->pos++]; + $this->column++; + } else { + if (!strlen($word)) { + $this->quotstate*=-1; + $word=$this->data[$this->pos++]; + $this->column++; + } + $found=1; + } + } else { + $word=$this->data[$this->pos++]; + $this->column++; + $found=1; + } + break; + default: + $this->column++; + $word.=$this->data[$this->pos++]; + } + } + return true; + } + +/******************************************************************************************** + * Parse grammar first step + ******************************************************************************************** +Parse +< [] [] > + +in/state 0 1 2 3 +< 1 -1 -1 1 +[ -1 2 -1 -1 +] -1 -1 1 -1 +> -1 3 -1 -1 +word -1 1 2 -1 +EOF -1 -1 -1 -2 + +-2 end parse + 0 begin parse, waiting '<' + 1 got '<' need to parse parameters, or wait '>' or wait '[' + 2 got '[' or ']' need to parse parameters + 3 got '>', waiting eof or '<' + + ********************************************************************************************/ + function ParseFirst($word) { + if ($this->iseof) { + $this->firstprev["state"]=0; + $this->firstprev["word"]=""; + return true; + } + $automat=array( + "0"=>array( 1, -1, -1, 1), + "1"=>array(-1, 2, -1, -1), + "2"=>array(-1, -1, 1, -1), + "3"=>array(-1, 3, -1, -1), + "4"=>array(-1, 1, 2, -1), + "5"=>array(-1, -1, -1, -2) + ); + switch($word) { + case "<": + $instate=0; + $this->pgpos++; + $this->parpos=-1; + break; + case "[": + $this->parpos++; + $instate=1; + break; + case "]": + $instate=2; + break; + case ">": + $instate=3; + break; + default: + $instate=4; + break; + } + $this->firststate=$automat[$instate][$this->firststate]; + if ($this->firststate==-1) return false; + switch ($this->firststate) { + case 1: + $this->mode=1; + if ($this->firstprev["state"]==1) + if (!$this->ParseSecond($word)) return false; + break; + case 2: + switch($this->firstprev["state"]) { + case 1: + $this->mode=2; + break; + case 3: + case 2: + if ($this->firstprev["state"]==2) + $this->mode=2; + else + $this->mode=1; + break; + } + if ($this->firstprev["state"]==2) + if (!$this->ParseSecond($word)) return false; + break; + case 3: + if (isset($this->pg[$this->pgpos]["tag"]["nohavesametag"])) + $this->pg[$this->pgpos]["tag"]["closeon"]["in"][]=$this->tagname; + break; + } + $this->firstprev["state"]=$this->firststate; + $this->firstprev["word"]=$word; + return true; + } + +/******************************************************************************************** + * Parse grammar second step + ******************************************************************************************** + +Parse +par1="value" par2=value + +in/state 0 1 2 3 4 += -1 2 -1 3 -1 +" -1 -1 3 4 -1 +word 1 -1 4 3 1 +EOF -1 -1 -1 -1 -1 + +-3 end parse by '>' +-2 end parse by ']' + 0 begin parse waiting parname + 1 got parname, waiting '=' or new parname + 2 got '=' waiting any word as value or first '"' + 3 collect words to next '"' + 4 got parvalue, waiting new parname + ********************************************************************************************/ + function ParseSecond($word) { + if ($this->iseof) return false; + $automat=array( + "0"=>array(-1, 2, -1, 3, -1), + "1"=>array(-1, -1, 3, 4, -1), + "2"=>array( 1, 1, 4, 3, 1), + "3"=>array(-1, -1, -1, -1, -1) + ); + switch($word) { + case "=": + $instate=0; + break; + case "\"": + $instate=1; + break; + default: + $instate=2; + break; + } + $this->secondstate=$automat[$instate][$this->secondstate]; + if ($this->secondstate==-1) return false; + switch ($this->secondstate) { + case 1: + $this->parname=$word; + if (!ereg("[a-zA-Z_-]+([0-9]+)?",$word)) { + $this->SetError(1,"Fatal error.",$this->line,$this->column,"Error"); + return false; + } + switch($this->mode) { + case 1: + $this->pg[$this->pgpos]["tag"][$this->parname]=""; + break; + case 2: + $this->pg[$this->pgpos]["pars"][$this->parpos][$this->parname]=""; + break; + } + break; + case 4: + switch($this->mode) { + case 1: + if ($this->secondprev["state"]==3) + $this->pg[$this->pgpos]["tag"][$this->parname]=$this->secondprev["word"]; + else + $this->pg[$this->pgpos]["tag"][$this->parname]=$word; + if ($this->parname=="closeon") { + $notexists=array(); + $exists=array(); + $this->ParseCloseOn($this->pg[$this->pgpos]["tag"][$this->parname],&$notexists,&$exists); + $this->pg[$this->pgpos]["tag"][$this->parname]=array(); + $this->pg[$this->pgpos]["tag"][$this->parname]["notin"]=$notexists; + $this->pg[$this->pgpos]["tag"][$this->parname]["in"]=$exists; + } elseif ($this->parname=="tag") + $this->tagname=$this->pg[$this->pgpos]["tag"]["tag"]; + break; + case 2: + if ($this->secondprev["state"]==3) + $this->pg[$this->pgpos]["pars"][$this->parpos][$this->parname]=$this->secondprev["word"]; + else + $this->pg[$this->pgpos]["pars"][$this->parpos][$this->parname]=$word; + break; + } + break; + } + $this->secondprev["state"]=$this->secondstate; + $this->secondprev["word"]=$word; + return true; + } + +/******************************************************************************************** + * Parse closeon structure + ********************************************************************************************/ + function ParseCloseOn($str,$notexists,$exists) { + $arr=explode("|",$str); + if (!is_array($arr)) { + if (!strlen($str)) + return; + else + $arr[]=$str; + } + for ($i=0;$iallreadyparsed) return true; + $this->line=1; + while(1) { + $isword=$this->GetWord(&$word); + if (!$isword) $this->iseof=true; + switch (strtolower($word)) { + case "/*"; + $this->incomment*=-1; + break; + case "*/"; + if ($this->incomment!=1) { + $this->SetError(1,"Not found begin of comment operator.",$this->line,$this->column,"Error"); + return; + } + $this->incomment*=-1; + break; + default: + if ($this->incomment==1) break; + if (!$this->ParseFirst($word)) { + $this->SetError(1,"Fatal error",$this->line,$this->column,"Error"); + return false; + } + break; + } + if ($this->iseof) break; + } + if ($this->incomment==1) { + $this->SetError(1,"Not found end of comment operator.",$this->line,$this->column,"Error"); + return false; + } + $this->PrepareGrammar(); + return true; + } +/******************************************************************************************** + * Prepare grammar for future using + ********************************************************************************************/ + function PrepareGrammar() { + $edittagsaftertable=$this->ScanGrammar(); + $l=sizeof($this->pg); + for ($i=0;$i<$l;$i++) { + $this->pg[$this->pg[$i]["tag"]["tag"]]=$this->pg[$i]["tag"]; + if (isset($this->pg[$i]["pars"])) { + $n=sizeof($this->pg[$i]["pars"]); + for ($j=0;$j<$n;$j++) + $this->pg[$this->pg[$i]["tag"]["tag"]]["pars"][$this->pg[$i]["pars"][$j]["par"]]=$this->pg[$i]["pars"][$j]; + } else + $this->pg[$this->pg[$i]["tag"]["tag"]]["pars"]=array(); + unset($this->pg["$i"]); + } + $this->pg["EDIT_TAGS_AFTER_TABLE"]=$edittagsaftertable; + } +/******************************************************************************************** + * Scan grammar for creating edittagsafter table + ********************************************************************************************/ + function ScanGrammar() { + $edittagsaftertable=array(); + for ($i=0;$ipg);$i++) + if (isset($this->pg[$i]["tag"]["edittagsafter"])) + if (!in_array($this->pg[$i]["tag"]["edittagsafter"],$edittagsaftertable)) $edittagsaftertable[]=$this->pg[$i]["tag"]["edittagsafter"]; + return $edittagsaftertable; + } +/******************************************************************************************** + * Save precompiled grammar in file + ********************************************************************************************/ + function SaveGrammar($name) { + $str=serialize($this->pg); + if (!$fp=fopen($name,"w")) + print "
Error: Can't create file $name. Unable to save grammar.
"; + flock($fp,2); + fwrite($fp,$str,strlen($str)); + flock($fp,3); + fclose($fp); + } +} + +} //_ECHOSERVER_HTML_GRAMMARPARSER +?> diff --git a/includes/htmlparser/readme.eng.txt b/includes/htmlparser/readme.eng.txt new file mode 100644 index 0000000..96a9da1 --- /dev/null +++ b/includes/htmlparser/readme.eng.txt @@ -0,0 +1,19 @@ +testhtmlparser.php - example of using +rebuildgrammar.php - grammar compiler +htmlgrammar.dat - grammar +htmlgrammar.cmp - compiled grammar +test.html - html file for test the html parser +common.inc - additional functions +imgsrcchange.inc - this example shows how to change src parameter for all img tags. + +Setup is very simple. You need copy all files into someone catalog on +your web server and after this call http://..../testhtmlparser.php +As result you will have start page of Russian PHPClub site which placed into the test.html file. +This page was formed by tag's tree after parse process. Bellow you can see +printed tag's tree. + +All questions you can send to +anton@concord.ru + +or by ICQ +3180528 diff --git a/includes/htmlparser/rebuildgrammar.php b/includes/htmlparser/rebuildgrammar.php new file mode 100644 index 0000000..9b5bdbb --- /dev/null +++ b/includes/htmlparser/rebuildgrammar.php @@ -0,0 +1,13 @@ +Parse(); +$p->PrintErrors(); +$p->SaveGrammar("htmlgrammar.cmp"); +print "Done."; +//PrintArray($p->pg); + +?> diff --git a/includes/htmlpure/Filter/CNBC.php b/includes/htmlpure/Filter/CNBC.php new file mode 100644 index 0000000..f8bfdde --- /dev/null +++ b/includes/htmlpure/Filter/CNBC.php @@ -0,0 +1,56 @@ +#s'; + $pre_replace = '\6'; + $ret = preg_replace($pre_regex, $pre_replace, $html); + return $ret; + } + + public function postFilter($html, $config, &$context) { + $width = '\1'; + $height = '\2'; + + /* the CNBC embed size may be fixed by the swf app and so it may never be possible to override + // @config->def->info['bitweaver']['CNBC'] params width and height will force the size of the video + if( !empty( $config->def->info['bitweaver']['CNBC'] ) ){ + $moviesize = $config->def->info['bitweaver']['CNBC']; + $width = $moviesize['width']; + $height = $moviesize['height']; + } + */ + + $src_url='http://plus.cnbc.com/rssvideosearch/action/player/id/\3/code/cnbcplayershare'; + + $post_regex = '#([A-Za-z0-9\-_]+)#'; + $post_replace = '
+ + + + + + + + + + +
'; + $ret = preg_replace($post_regex, $post_replace, $html); + return $ret; + } + +} + diff --git a/includes/htmlpure/Filter/SafeIframe.php b/includes/htmlpure/Filter/SafeIframe.php new file mode 100644 index 0000000..2b9a415 --- /dev/null +++ b/includes/htmlpure/Filter/SafeIframe.php @@ -0,0 +1,22 @@ +#'; + $pre_replace = '\1'; + return preg_replace($pre_regex, $pre_replace, preg_replace("/<\/iframe>/", "", $html)); + } + + public function postFilter($html, $config, $context) { + $post_regex = '#(.+?)#'; + return preg_replace_callback($post_regex, array($this, 'postFilterCallback'), $html); + } + + protected function postFilterCallback($matches) { + return ''; + } +} + diff --git a/includes/htmlpure/Filter/YouTube.php b/includes/htmlpure/Filter/YouTube.php new file mode 100644 index 0000000..53615d4 --- /dev/null +++ b/includes/htmlpure/Filter/YouTube.php @@ -0,0 +1,46 @@ +#s'; + $pre_replace = '\6'; + $ret = preg_replace($pre_regex, $pre_replace, $html); + return $ret; + } + + // @config->def->info['bitweaver']['YouTube'] params width and height will force the size of the video + public function postFilter($html, $config, &$context) { + $width = '\1'; + $height = '\2'; + if( !empty( $config->def->info['bitweaver']['YouTube'] ) ){ + $moviesize = $config->def->info['bitweaver']['YouTube']; + $width = $moviesize['width']; + $height = $moviesize['height']; + } + + $post_regex = '#([A-Za-z0-9\-_]+)#'; + $post_replace = '
'. + ''. + ''. + ''. + ''. + ''. + '
'; + $ret = preg_replace($post_regex, $post_replace, $html); + return $ret; + } + +} + diff --git a/includes/is_email.php b/includes/is_email.php new file mode 100644 index 0000000..3a29fbb --- /dev/null +++ b/includes/is_email.php @@ -0,0 +1,529 @@ + + * Test schema documentation Copyright © 2010, Daniel Marschall
+ * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of Dominic Sayers nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @package is_email + * @author Dominic Sayers + * @copyright 2008-2010 Dominic Sayers + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://www.dominicsayers.com/isemail + * @version 2.10.1 - Amended DNS lookup logic. Also, in is_email_statustext.php, changed $type to integer to allow for additional types (starting with SMTP codes) + */ + +// The quality of this code has been improved greatly by using PHPLint +// Copyright (c) 2010 Umberto Salsi +// This is free software; see the license for copying conditions. +// More info: http://www.icosaedro.it/phplint/ +/*. + require_module 'standard'; + require_module 'pcre'; +.*/ +/** + * Check that an email address conforms to RFCs 5321, 5322 and others + * + * @param string $email The email address to check + * @param boolean $checkDNS If true then a DNS check for A and MX records will be made + * @param mixed $errorlevel If true then return an integer error or warning number rather than true or false + */ +/*.mixed.*/ function is_email ($email, $checkDNS = false, $errorlevel = false) { + // Check that $email is a valid address. Read the following RFCs to understand the constraints: + // (http://tools.ietf.org/html/rfc5321) + // (http://tools.ietf.org/html/rfc5322) + // (http://tools.ietf.org/html/rfc4291#section-2.2) + // (http://tools.ietf.org/html/rfc1123#section-2.1) + // (http://tools.ietf.org/html/rfc3696) (guidance only) + + // $errorlevel Behaviour + // --------------- --------------------------------------------------------------------------- + // E_ERROR Return validation failures only. For technically valid addresses return + // ISEMAIL_VALID + // E_WARNING Return warnings for unlikely but technically valid addresses. This includes + // addresses at TLDs (e.g. johndoe@com), addresses with FWS and comments, + // addresses that are quoted and addresses that contain no alphabetic or + // numeric characters. + // true Same as E_ERROR + // false Return true for valid addresses, false for invalid ones. No warnings. + // + // Errors can be distinguished from warnings if ($return_value > ISEMAIL_ERROR) +// version 2.0: Enhance $diagnose parameter to $errorlevel +// revision 2.5: some syntax changes to make it more PHPLint-friendly. Should be functionally identical. + + if (!defined('ISEMAIL_VALID')) { + // No errors + define('ISEMAIL_VALID' , 0); + // Warnings (valid address but unlikely in the real world) + define('ISEMAIL_WARNING' , 64); + define('ISEMAIL_TLD' , 65); + define('ISEMAIL_TLDNUMERIC' , 66); + define('ISEMAIL_QUOTEDSTRING' , 67); + define('ISEMAIL_COMMENTS' , 68); + define('ISEMAIL_FWS' , 69); + define('ISEMAIL_ADDRESSLITERAL' , 70); + define('ISEMAIL_UNLIKELYINITIAL' , 71); + define('ISEMAIL_SINGLEGROUPELISION' , 72); + define('ISEMAIL_DOMAINNOTFOUND' , 73); + define('ISEMAIL_MXNOTFOUND' , 74); + // Errors (invalid address) + define('ISEMAIL_ERROR' , 128); + define('ISEMAIL_TOOLONG' , 129); + define('ISEMAIL_NOAT' , 130); + define('ISEMAIL_NOLOCALPART' , 131); + define('ISEMAIL_NODOMAIN' , 132); + define('ISEMAIL_ZEROLENGTHELEMENT' , 133); + define('ISEMAIL_BADCOMMENT_START' , 134); + define('ISEMAIL_BADCOMMENT_END' , 135); + define('ISEMAIL_UNESCAPEDDELIM' , 136); + define('ISEMAIL_EMPTYELEMENT' , 137); + define('ISEMAIL_UNESCAPEDSPECIAL' , 138); + define('ISEMAIL_LOCALTOOLONG' , 139); +// define('ISEMAIL_IPV4BADPREFIX' , 140); + define('ISEMAIL_IPV6BADPREFIXMIXED' , 141); + define('ISEMAIL_IPV6BADPREFIX' , 142); + define('ISEMAIL_IPV6GROUPCOUNT' , 143); + define('ISEMAIL_IPV6DOUBLEDOUBLECOLON' , 144); + define('ISEMAIL_IPV6BADCHAR' , 145); + define('ISEMAIL_IPV6TOOMANYGROUPS' , 146); + define('ISEMAIL_DOMAINEMPTYELEMENT' , 147); + define('ISEMAIL_DOMAINELEMENTTOOLONG' , 148); + define('ISEMAIL_DOMAINBADCHAR' , 149); + define('ISEMAIL_DOMAINTOOLONG' , 150); + define('ISEMAIL_IPV6SINGLECOLONSTART' , 151); + define('ISEMAIL_IPV6SINGLECOLONEND' , 152); + // Unexpected errors +// define('ISEMAIL_BADPARAMETER' , 190); +// define('ISEMAIL_NOTDEFINED' , 191); +// revision 2.1: Redefined unexpected error constants so they don't clash with the ISEMAIL_WARNING bit +// revision 2.5: Undefined unused constants + } + + if (is_bool($errorlevel)) { + if ((bool) $errorlevel) { + $diagnose = true; + $warn = false; + } else { + $diagnose = false; + $warn = false; + } + } else { + switch ((int) $errorlevel) { + case E_WARNING: + $diagnose = true; + $warn = true; + break; + case E_ERROR: + $diagnose = true; + $warn = false; + break; + default: + $diagnose = false; + $warn = false; + } + } + + if ($diagnose) /*.mixed.*/ $return_status = ISEMAIL_VALID; else $return_status = true; +// version 2.0: Enhance $diagnose parameter to $errorlevel + + // the upper limit on address lengths should normally be considered to be 254 + // (http://www.rfc-editor.org/errata_search.php?rfc=3696) + // NB My erratum has now been verified by the IETF so the correct answer is 254 + // + // The maximum total length of a reverse-path or forward-path is 256 + // characters (including the punctuation and element separators) + // (http://tools.ietf.org/html/rfc5321#section-4.5.3.1.3) + // NB There is a mandatory 2-character wrapper round the actual address + $emailLength = strlen($email); +// revision 1.17: Max length reduced to 254 (see above) + if ($emailLength > 254) if ($diagnose) return ISEMAIL_TOOLONG; else return false; // Too long + + // Contemporary email addresses consist of a "local part" separated from + // a "domain part" (a fully-qualified domain name) by an at-sign ("@"). + // (http://tools.ietf.org/html/rfc3696#section-3) + $atIndex = strrpos($email,'@'); + + if ($atIndex === false) if ($diagnose) return ISEMAIL_NOAT; else return false; // No at-sign + if ($atIndex === 0) if ($diagnose) return ISEMAIL_NOLOCALPART; else return false; // No local part + if ($atIndex === $emailLength - 1) if ($diagnose) return ISEMAIL_NODOMAIN; else return false; // No domain part +// revision 1.14: Length test bug suggested by Andrew Campbell of Gloucester, MA + + // Sanitize comments + // - remove nested comments, quotes and dots in comments + // - remove parentheses and dots from quoted strings + $braceDepth = 0; + $inQuote = false; + $escapeThisChar = false; + + for ($i = 0; $i < $emailLength; ++$i) { + $char = $email[$i]; + $replaceChar = false; + + if ($char === '\\') $escapeThisChar = !$escapeThisChar; // Escape the next character? + else { + switch ($char) { + case '(': + if ($escapeThisChar) $replaceChar = true; + else if ($inQuote) $replaceChar = true; + else if ($braceDepth++ > 0) $replaceChar = true; // Increment brace depth + + break; + case ')': + if ($escapeThisChar) $replaceChar = true; + else if ($inQuote) $replaceChar = true; + else { + if (--$braceDepth > 0) $replaceChar = true; // Decrement brace depth + if ($braceDepth < 0) $braceDepth = 0; + } + + break; + case '"': + if ($escapeThisChar) $replaceChar = true; + else if ($braceDepth === 0) $inQuote = !$inQuote; // Are we inside a quoted string? + else $replaceChar = true; + + break; + case '.': + if ($escapeThisChar) $replaceChar = true; // Dots don't help us either + else if ($braceDepth > 0) $replaceChar = true; + + break; + default: + } + + $escapeThisChar = false; +// if ($replaceChar) $email[$i] = 'x'; // Replace the offending character with something harmless +// revision 1.12: Line above replaced because PHPLint doesn't like that syntax + if ($replaceChar) $email = (string) substr_replace($email, 'x', $i, 1); // Replace the offending character with something harmless + } + } + + $localPart = substr($email, 0, $atIndex); + $domain = substr($email, $atIndex + 1); + $FWS = "(?:(?:(?:[ \\t]*(?:\\r\\n))?[ \\t]+)|(?:[ \\t]+(?:(?:\\r\\n)[ \\t]+)*))"; // Folding white space + $dotArray = /*. (array[]) .*/ array(); + + // Let's check the local part for RFC compliance... + // + // local-part = dot-atom / quoted-string / obs-local-part + // obs-local-part = word *("." word) + // (http://tools.ietf.org/html/rfc5322#section-3.4.1) + // + // Problem: need to distinguish between "first.last" and "first"."last" + // (i.e. one element or two). And I suck at regular expressions. + $dotArray = preg_split('/\\.(?=(?:[^\\"]*\\"[^\\"]*\\")*(?![^\\"]*\\"))/m', $localPart); + $partLength = 0; + + foreach ($dotArray as $arrayMember) { + $element = (string) $arrayMember; + // Remove any leading or trailing FWS + $new_element = preg_replace("/^$FWS|$FWS\$/", '', $element); + if ($warn && ($element !== $new_element)) $return_status = ISEMAIL_FWS; // FWS is unlikely in the real world + $element = $new_element; +// version 2.3: Warning condition added + $elementLength = strlen($element); + + if ($elementLength === 0) if ($diagnose) return ISEMAIL_ZEROLENGTHELEMENT; else return false; // Can't have empty element (consecutive dots or dots at the start or end) +// revision 1.15: Speed up the test and get rid of "uninitialized string offset" notices from PHP + + // We need to remove any valid comments (i.e. those at the start or end of the element) + if ($element[0] === '(') { + if ($warn) $return_status = ISEMAIL_COMMENTS; // Comments are unlikely in the real world +// version 2.0: Warning condition added + $indexBrace = strpos($element, ')'); + if ($indexBrace !== false) { + if (preg_match('/(? 0) + if ($diagnose) return ISEMAIL_BADCOMMENT_START; else return false; // Illegal characters in comment + $element = substr($element, $indexBrace + 1, $elementLength - $indexBrace - 1); + $elementLength = strlen($element); + } + } + + if ($element[$elementLength - 1] === ')') { + if ($warn) $return_status = ISEMAIL_COMMENTS; // Comments are unlikely in the real world +// version 2.0: Warning condition added + $indexBrace = strrpos($element, '('); + if ($indexBrace !== false) { + if (preg_match('/(? 0) + if ($diagnose) return ISEMAIL_BADCOMMENT_END; else return false; // Illegal characters in comment + $element = substr($element, 0, $indexBrace); + $elementLength = strlen($element); + } + } + + // Remove any remaining leading or trailing FWS around the element (having removed any comments) + $new_element = preg_replace("/^$FWS|$FWS\$/", '', $element); + if ($warn && ($element !== $new_element)) $return_status = ISEMAIL_FWS; // FWS is unlikely in the real world + $element = $new_element; +// version 2.0: Warning condition added + + // What's left counts towards the maximum length for this part + if ($partLength > 0) $partLength++; // for the dot + $partLength += strlen($element); + + // Each dot-delimited component can be an atom or a quoted string + // (because of the obs-local-part provision) + if (preg_match('/^"(?:.)*"$/s', $element) > 0) { + // Quoted-string tests: + if ($warn) $return_status = ISEMAIL_QUOTEDSTRING; // Quoted string is unlikely in the real world +// version 2.0: Warning condition added + // Remove any FWS + $element = preg_replace("/(? 0) if ($diagnose) return ISEMAIL_UNESCAPEDDELIM; else return false; // ", CR, LF and NUL must be escaped +// version 2.0: allow ""@example.com because it's technically valid + } else { + // Unquoted string tests: + // + // Period (".") may...appear, but may not be used to start or end the + // local part, nor may two or more consecutive periods appear. + // (http://tools.ietf.org/html/rfc3696#section-3) + // + // A zero-length element implies a period at the beginning or end of the + // local part, or two periods together. Either way it's not allowed. + if ($element === '') if ($diagnose) return ISEMAIL_EMPTYELEMENT; else return false; // Dots in wrong place + + // Any ASCII graphic (printing) character other than the + // at-sign ("@"), backslash, double quote, comma, or square brackets may + // appear without quoting. If any of that list of excluded characters + // are to appear, they must be quoted + // (http://tools.ietf.org/html/rfc3696#section-3) + // + // Any excluded characters? i.e. 0x00-0x20, (, ), <, >, [, ], :, ;, @, \, comma, period, " + if (preg_match('/[\\x00-\\x20\\(\\)<>\\[\\]:;@\\\\,\\."]/', $element) > 0) if ($diagnose) return ISEMAIL_UNESCAPEDSPECIAL; else return false; // These characters must be in a quoted string + if ($warn && (preg_match('/^\\w+/', $element) === 0)) $return_status = ISEMAIL_UNLIKELYINITIAL; // First character is an odd one + } + } + + if ($partLength > 64) if ($diagnose) return ISEMAIL_LOCALTOOLONG; else return false; // Local part must be 64 characters or less + + // Now let's check the domain part... + + // The domain name can also be replaced by an IP address in square brackets + // (http://tools.ietf.org/html/rfc3696#section-3) + // (http://tools.ietf.org/html/rfc5321#section-4.1.3) + // (http://tools.ietf.org/html/rfc4291#section-2.2) + if (preg_match('/^\\[(.)+]$/', $domain) === 1) { + // It's an address-literal + if ($warn) $return_status = ISEMAIL_ADDRESSLITERAL; // Quoted string is unlikely in the real world +// version 2.0: Warning condition added + $addressLiteral = substr($domain, 1, strlen($domain) - 2); + $groupMax = 8; +// revision 2.1: new IPv6 testing strategy + $matchesIP = array(); + $colon = ':'; // Revision 2.7: Daniel Marschall's new IPv6 testing strategy + $double_colon = '::'; + + // Extract IPv4 part from the end of the address-literal (if there is one) + if (preg_match('/\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/', $addressLiteral, $matchesIP) > 0) { + $index = strrpos($addressLiteral, $matchesIP[0]); + + if ($index === 0) { + // Nothing there except a valid IPv4 address, so... + if ($diagnose) return $return_status; else return true; +// version 2.0: return warning if one is set + } else { +//- // Assume it's an attempt at a mixed address (IPv6 + IPv4) +//- if ($addressLiteral[$index - 1] !== $colon) if ($diagnose) return ISEMAIL_IPV4BADPREFIX; else return false; // Character preceding IPv4 address must be ':' +// revision 2.1: new IPv6 testing strategy + if (substr($addressLiteral, 0, 5) !== 'IPv6:') if ($diagnose) return ISEMAIL_IPV6BADPREFIXMIXED; else return false; // RFC5321 section 4.1.3 +//- +//- $IPv6 = substr($addressLiteral, 5, ($index === 7) ? 2 : $index - 6); +//- $groupMax = 6; +// revision 2.1: new IPv6 testing strategy + $IPv6 = substr($addressLiteral, 5, $index - 5) . '0000:0000'; // Convert IPv4 part to IPv6 format + } + } else { + // It must be an attempt at pure IPv6 + if (substr($addressLiteral, 0, 5) !== 'IPv6:') if ($diagnose) return ISEMAIL_IPV6BADPREFIX; else return false; // RFC5321 section 4.1.3 + $IPv6 = substr($addressLiteral, 5); +//- $groupMax = 8; +// revision 2.1: new IPv6 testing strategy + } + + $matchesIP = explode($colon, $IPv6); // Revision 2.7: Daniel Marschall's new IPv6 testing strategy + $groupCount = count($matchesIP); + $index = strpos($IPv6,$double_colon); + + if ($index === false) { + // We need exactly the right number of groups + if ($groupCount !== $groupMax) if ($diagnose) return ISEMAIL_IPV6GROUPCOUNT; else return false; // RFC5321 section 4.1.3 + } else { + if ($index !== strrpos($IPv6,$double_colon)) if ($diagnose) return ISEMAIL_IPV6DOUBLEDOUBLECOLON; else return false; // More than one '::' + if ($index === 0 || $index === (strlen($IPv6) - 2)) $groupMax++; // RFC 4291 allows :: at the start or end of an address with 7 other groups in addition + if ($groupCount > $groupMax) if ($diagnose) return ISEMAIL_IPV6TOOMANYGROUPS; else return false; // Too many IPv6 groups in address + if ($groupCount === $groupMax) $return_status = ISEMAIL_SINGLEGROUPELISION; // Eliding a single group with :: is deprecated by RFCs 5321 & 5952 + } + + // Check for single : at start and end of address + // Revision 2.7: Daniel Marschall's new IPv6 testing strategy + if ((substr($IPv6, 0, 1) === $colon) && (substr($IPv6, 1, 1) !== $colon)) if ($diagnose) return ISEMAIL_IPV6SINGLECOLONSTART; else return false; // Address starts with a single colon + if ((substr($IPv6, -1) === $colon) && (substr($IPv6, -2, 1) !== $colon)) if ($diagnose) return ISEMAIL_IPV6SINGLECOLONEND; else return false; // Address ends with a single colon + + // Check for unmatched characters + if (count(preg_grep('/^[0-9A-Fa-f]{0,4}$/', $matchesIP, PREG_GREP_INVERT)) !== 0) if ($diagnose) return ISEMAIL_IPV6BADCHAR; else return false; // Illegal characters in address + // It's a valid IPv6 address, so... + if ($diagnose) return $return_status; else return true; +// revision 2.1: bug fix: now correctly return warning status + } else { + // It's a domain name... + + // The syntax of a legal Internet host name was specified in RFC-952 + // One aspect of host name syntax is hereby changed: the + // restriction on the first character is relaxed to allow either a + // letter or a digit. + // (http://tools.ietf.org/html/rfc1123#section-2.1) + // + // NB RFC 1123 updates RFC 1035, but this is not currently apparent from reading RFC 1035. + // + // Most common applications, including email and the Web, will generally not + // permit...escaped strings + // (http://tools.ietf.org/html/rfc3696#section-2) + // + // the better strategy has now become to make the "at least one period" test, + // to verify LDH conformance (including verification that the apparent TLD name + // is not all-numeric) + // (http://tools.ietf.org/html/rfc3696#section-2) + // + // Characters outside the set of alphabetic characters, digits, and hyphen MUST NOT appear in domain name + // labels for SMTP clients or servers + // (http://tools.ietf.org/html/rfc5321#section-4.1.2) + // + // RFC5321 precludes the use of a trailing dot in a domain name for SMTP purposes + // (http://tools.ietf.org/html/rfc5321#section-4.1.2) + $dotArray = preg_split('/\\.(?=(?:[^\\"]*\\"[^\\"]*\\")*(?![^\\"]*\\"))/m', $domain); + $partLength = 0; + $element = ''; // Since we use $element after the foreach loop let's make sure it has a value +// revision 1.13: Line above added because PHPLint now checks for Definitely Assigned Variables + + if ($warn && (count($dotArray) === 1)) $return_status = ISEMAIL_TLD; // The mail host probably isn't a TLD +// version 2.0: downgraded to a warning + + foreach ($dotArray as $arrayMember) { + $element = (string) $arrayMember; + // Remove any leading or trailing FWS + $new_element = preg_replace("/^$FWS|$FWS\$/", '', $element); + if ($warn && ($element !== $new_element)) $return_status = ISEMAIL_FWS; // FWS is unlikely in the real world + $element = $new_element; +// version 2.0: Warning condition added + $elementLength = strlen($element); + + // Each dot-delimited component must be of type atext + // A zero-length element implies a period at the beginning or end of the + // local part, or two periods together. Either way it's not allowed. + if ($elementLength === 0) if ($diagnose) return ISEMAIL_DOMAINEMPTYELEMENT; else return false; // Dots in wrong place +// revision 1.15: Speed up the test and get rid of "uninitialized string offset" notices from PHP + + // Then we need to remove all valid comments (i.e. those at the start or end of the element + if ($element[0] === '(') { + if ($warn) $return_status = ISEMAIL_COMMENTS; // Comments are unlikely in the real world +// version 2.0: Warning condition added + $indexBrace = strpos($element, ')'); + if ($indexBrace !== false) { + if (preg_match('/(? 0) + if ($diagnose) return ISEMAIL_BADCOMMENT_START; else return false; // Illegal characters in comment +// revision 1.17: Fixed name of constant (also spotted by turboflash - thanks!) + $element = substr($element, $indexBrace + 1, $elementLength - $indexBrace - 1); + $elementLength = strlen($element); + } + } + + if ($element[$elementLength - 1] === ')') { + if ($warn) $return_status = ISEMAIL_COMMENTS; // Comments are unlikely in the real world +// version 2.0: Warning condition added + $indexBrace = strrpos($element, '('); + if ($indexBrace !== false) { + if (preg_match('/(? 0) + if ($diagnose) return ISEMAIL_BADCOMMENT_END; else return false; // Illegal characters in comment +// revision 1.17: Fixed name of constant (also spotted by turboflash - thanks!) + $element = substr($element, 0, $indexBrace); + $elementLength = strlen($element); + } + } + + // Remove any leading or trailing FWS around the element (inside any comments) + $new_element = preg_replace("/^$FWS|$FWS\$/", '', $element); + if ($warn && ($element !== $new_element)) $return_status = ISEMAIL_FWS; // FWS is unlikely in the real world + $element = $new_element; +// version 2.0: Warning condition added + + // What's left counts towards the maximum length for this part + if ($partLength > 0) $partLength++; // for the dot + $partLength += strlen($element); + + // The DNS defines domain name syntax very generally -- a + // string of labels each containing up to 63 8-bit octets, + // separated by dots, and with a maximum total of 255 + // octets. + // (http://tools.ietf.org/html/rfc1123#section-6.1.3.5) + if ($elementLength > 63) if ($diagnose) return ISEMAIL_DOMAINELEMENTTOOLONG; else return false; // Label must be 63 characters or less + + // Any ASCII graphic (printing) character other than the + // at-sign ("@"), backslash, double quote, comma, or square brackets may + // appear without quoting. If any of that list of excluded characters + // are to appear, they must be quoted + // (http://tools.ietf.org/html/rfc3696#section-3) + // + // If the hyphen is used, it is not permitted to appear at + // either the beginning or end of a label. + // (http://tools.ietf.org/html/rfc3696#section-2) + // + // Any excluded characters? i.e. 0x00-0x20, (, ), <, >, [, ], :, ;, @, \, comma, period, " + if (preg_match('/[\\x00-\\x20\\(\\)<>\\[\\]:;@\\\\,\\."]|^-|-$/', $element) > 0) if ($diagnose) return ISEMAIL_DOMAINBADCHAR; else return false; // Illegal character in domain name + } + + if ($partLength > 255) if ($diagnose) return ISEMAIL_DOMAINTOOLONG; else return false; // Domain part must be 255 characters or less (http://tools.ietf.org/html/rfc1123#section-6.1.3.5) + + if ($warn && (preg_match('/^[0-9]+$/', $element) > 0)) $return_status = ISEMAIL_TLDNUMERIC; // TLD probably isn't all-numeric (http://www.apps.ietf.org/rfc/rfc3696.html#sec-2) +// version 2.0: Downgraded to a warning + + // Check DNS? + if ($diagnose && ($return_status === ISEMAIL_VALID) && $checkDNS && function_exists('checkdnsrr')) { + // Revision 2.10: Amended DNS logic + // An A-record is not required unless there are no MX-records + // for a domain. Obvious when you think about it. + // (http://tools.ietf.org/html/rfc5321#section-5) + // (http://tools.ietf.org/html/rfc2181#section-10.3) + // (http://tools.ietf.org/html/rfc1035) + if (!(checkdnsrr($domain, 'MX'))) { + $result = @dns_get_record($domain, DNS_A); + + if ((is_bool($result) && !(bool) $result)) + $return_status = ISEMAIL_DOMAINNOTFOUND; // Neither MX- nor A-record for domain can be found + else $return_status = ISEMAIL_MXNOTFOUND; // MX-record for domain can't be found + } + } + } + + // Eliminate all other factors, and the one which remains must be the truth. + // (Sherlock Holmes, The Sign of Four) + if ($diagnose) return $return_status; else return true; +// version 2.0: return warning if one is set +} +?> diff --git a/includes/jpeg_metadata_tk/COPYING.txt b/includes/jpeg_metadata_tk/COPYING.txt new file mode 100644 index 0000000..5b6e7c6 --- /dev/null +++ b/includes/jpeg_metadata_tk/COPYING.txt @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/includes/jpeg_metadata_tk/EXIF.php b/includes/jpeg_metadata_tk/EXIF.php new file mode 100644 index 0000000..8751dcd --- /dev/null +++ b/includes/jpeg_metadata_tk/EXIF.php @@ -0,0 +1,2764 @@ + 1.10 : added function get_EXIF_TIFF to allow extracting EXIF from a TIFF file +* 1.10 -> 1.11 : added functionality to allow decoding of XMP and Photoshop IRB information +* embedded within the EXIF data +* added checks for http and ftp wrappers, as these are not supported +* changed interpret_IFD to allow thumbnail links to work when +* toolkit is portable across directories +* +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* +* License: This file is part of the PHP JPEG Metadata Toolkit. +* +* The PHP JPEG Metadata Toolkit is free software; you can +* redistribute it and/or modify it under the terms of the +* GNU General Public License as published by the Free Software +* Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* The PHP JPEG Metadata Toolkit is distributed in the hope +* that it will be useful, but WITHOUT ANY WARRANTY; without +* even the implied warranty of MERCHANTABILITY or FITNESS +* FOR A PARTICULAR PURPOSE. See the GNU General Public License +* for more details. +* +* You should have received a copy of the GNU General Public +* License along with the PHP JPEG Metadata Toolkit; if not, +* write to the Free Software Foundation, Inc., 59 Temple +* Place, Suite 330, Boston, MA 02111-1307 USA +* +* If you require a different license for commercial or other +* purposes, please contact the author: evan@ozhiker.com +* +******************************************************************************/ + + +// TODO : Thoroughly test the functions for writing EXIF segments +// TODO : Figure out a way to allow EXIF to function normally with HTTP and FTP wrappers +// TODO : Implement EXIF decoding of Device Setting Description field +// TODO : Implement EXIF decoding of SpatialFrequencyResponse field +// TODO : Implement EXIF decoding of OECF field +// TODO : Implement EXIF decoding of SubjectArea field +// TODO : Add a put_EXIF_TIFF function + +/****************************************************************************** +* +* Initialisation +* +******************************************************************************/ + + +if ( !isset( $GLOBALS['HIDE_UNKNOWN_TAGS'] ) ) $GLOBALS['HIDE_UNKNOWN_TAGS']= FALSE; +if ( !isset( $GLOBALS['SHOW_BINARY_DATA_HEX'] ) ) $GLOBALS['SHOW_BINARY_DATA_HEX'] = FALSE; +if ( !isset( $GLOBALS['SHOW_BINARY_DATA_TEXT'] ) ) $GLOBALS['SHOW_BINARY_DATA_TEXT'] = FALSE; + + +include_once 'EXIF_Tags.php'; +include_once 'EXIF_Makernote.php'; +include_once 'PIM.php'; +include_once 'Unicode.php'; +include_once 'JPEG.php'; +include_once 'IPTC.php'; +include_once 'Photoshop_IRB.php'; // Change: as of version 1.11 - Required for TIFF with embedded IRB +include_once 'XMP.php'; // Change: as of version 1.11 - Required for TIFF with embedded XMP +include_once 'pjmt_utils.php'; // Change: as of version 1.11 - Required for directory portability + + + + + + + + +/****************************************************************************** +* +* Function: get_EXIF_JPEG +* +* Description: Retrieves information from a Exchangeable Image File Format (EXIF) +* APP1 segment and returns it in an array. +* +* Parameters: filename - the filename of the JPEG image to process +* +* Returns: OutputArray - Array of EXIF records +* FALSE - If an error occured in decoding +* +******************************************************************************/ + +function get_EXIF_JPEG( $filename ) +{ + // Change: Added as of version 1.11 + // Check if a wrapper is being used - these are not currently supported (see notes at top of file) + if ( ( stristr ( $filename, "http://" ) != FALSE ) || ( stristr ( $filename, "ftp://" ) != FALSE ) ) + { + // A HTTP or FTP wrapper is being used - show a warning and abort + error_log( "HTTP and FTP wrappers are currently not supported with EXIF - See EXIF functionality documentation - a local file must be specified
+ To work on an internet file, copy it locally to start with:

\n + $newfilename = tempnam ( \$dir, \"tmpexif\" );
\n + copy ( \"http://whatever.com\", \$newfilename );

\n" ); + return FALSE; + } + + // get the JPEG headers + $jpeg_header_data = get_jpeg_header_data( $filename ); + + + // Flag that an EXIF segment has not been found yet + $EXIF_Location = -1; + + //Cycle through the header segments + for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) + { + // If we find an APP1 header, + if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP1" ) == 0 ) + { + // And if it has the EXIF label, + if ( ( strncmp ( $jpeg_header_data[$i]['SegData'], "Exif\x00\x00", 6) == 0 ) || + ( strncmp ( $jpeg_header_data[$i]['SegData'], "Exif\x00\xFF", 6) == 0 ) ) // For some reason, some files have a faulty EXIF name which has a 0xFF in it + { + // Save the location of the EXIF segment + $EXIF_Location = $i; + } + } + + } + + // Check if an EXIF segment was found + if ( $EXIF_Location == -1 ) + { + // Couldn't find any EXIF block to decode + return FALSE; + } + + $filehnd = @fopen($filename, 'rb'); + + // Check if the file opened successfully + if ( ! $filehnd ) + { + // Could't open the file - exit + error_log( "Could not open file $filename" ); + return FALSE; + } + + fseek( $filehnd, $jpeg_header_data[$EXIF_Location]['SegDataStart'] + 6 ); + + // Decode the Exif segment into an array and return it + $exif_data = process_TIFF_Header( $filehnd, "TIFF" ); + + + + // Close File + fclose($filehnd); + return $exif_data; +} + +/****************************************************************************** +* End of Function: get_EXIF_JPEG +******************************************************************************/ + + + +/****************************************************************************** +* +* Function: put_EXIF_JPEG +* +* Description: Stores information into a Exchangeable Image File Format (EXIF) +* APP1 segment from an EXIF array. +* +* WARNING: Because the EXIF standard allows pointers to data +* outside the APP1 segment, if there are any such pointers in +* a makernote, this function will DAMAGE them since it will not +* be aware that there is an external pointer. This will often +* happen with Makernotes that include an embedded thumbnail. +* This damage could be prevented where makernotes can be decoded, +* but currently this is not implemented. +* +* +* Parameters: exif_data - The array of EXIF data to insert into the JPEG header +* jpeg_header_data - The JPEG header into which the EXIF data +* should be stored, as from get_jpeg_header_data +* +* Returns: jpeg_header_data - JPEG header array with the EXIF segment inserted +* FALSE - If an error occured +* +******************************************************************************/ + +function put_EXIF_JPEG( $exif_data, $jpeg_header_data ) +{ + // pack the EXIF data into its proper format for a JPEG file + $packed_data = get_TIFF_Packed_Data( $exif_data ); + if ( $packed_data === FALSE ) + { + return $jpeg_header_data; + } + + $packed_data = "Exif\x00\x00$packed_data"; + + //Cycle through the header segments + for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) + { + // If we find an APP1 header, + if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP1" ) == 0 ) + { + // And if it has the EXIF label, + if ( ( strncmp ( $jpeg_header_data[$i]['SegData'], "Exif\x00\x00", 6) == 0 ) || + ( strncmp ( $jpeg_header_data[$i]['SegData'], "Exif\x00\xFF", 6) == 0 ) ) // For some reason, some files have a faulty EXIF name which has a 0xFF in it + { + // Found a preexisting EXIF block - Replace it with the new one and return. + $jpeg_header_data[$i]['SegData'] = $packed_data; + return $jpeg_header_data; + } + } + } + + // No preexisting segment segment found, insert a new one at the start of the header data. + + // Determine highest position of an APP segment at or below APP3, so we can put the + // new APP3 at this position + + + $highest_APP = -1; + + //Cycle through the header segments + for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) + { + // Check if we have found an APP segment at or below APP3, + if ( ( $jpeg_header_data[$i]['SegType'] >= 0xE0 ) && ( $jpeg_header_data[$i]['SegType'] <= 0xE3 ) ) + { + // Found an APP segment at or below APP12 + $highest_APP = $i; + } + } + + // No preexisting EXIF block found, insert a new one at the start of the header data. + array_splice($jpeg_header_data, $highest_APP + 1 , 0, array( array( "SegType" => 0xE1, + "SegName" => "APP1", + "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE1 ], + "SegData" => $packed_data ) ) ); + return $jpeg_header_data; + +} + +/****************************************************************************** +* End of Function: put_EXIF_JPEG +******************************************************************************/ + + + + +/****************************************************************************** +* +* Function: get_Meta_JPEG +* +* Description: Retrieves information from a Meta APP3 segment and returns it +* in an array. Uses information supplied by the +* get_jpeg_header_data function. +* The Meta segment has the same format as an EXIF segment, but +* uses different tags +* +* Parameters: filename - the filename of the JPEG image to process +* +* Returns: OutputArray - Array of Meta records +* FALSE - If an error occured in decoding +* +******************************************************************************/ + +function get_Meta_JPEG( $filename ) +{ + // Change: Added as of version 1.11 + // Check if a wrapper is being used - these are not currently supported (see notes at top of file) + if ( ( stristr ( $filename, "http://" ) != FALSE ) || ( stristr ( $filename, "ftp://" ) != FALSE ) ) + { + // A HTTP or FTP wrapper is being used - show a warning and abort + echo "HTTP and FTP wrappers are currently not supported with Meta - See EXIF/Meta functionality documentation - a local file must be specified
"; + echo "To work on an internet file, copy it locally to start with:

\n"; + echo "\$newfilename = tempnam ( \$dir, \"tmpmeta\" );
\n"; + echo "copy ( \"http://whatever.com\", \$newfilename );

\n"; + return FALSE; + } + + // get the JPEG headers + $jpeg_header_data = get_jpeg_header_data( $filename ); + + + // Flag that an Meta segment has not been found yet + $Meta_Location = -1; + + //Cycle through the header segments + for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) + { + // If we find an APP3 header, + if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP3" ) == 0 ) + { + // And if it has the Meta label, + if ( ( strncmp ( $jpeg_header_data[$i]['SegData'], "Meta\x00\x00", 6) == 0 ) || + ( strncmp ( $jpeg_header_data[$i]['SegData'], "META\x00\x00", 6) == 0 ) ) + { + // Save the location of the Meta segment + $Meta_Location = $i; + } + } + } + + // Check if an EXIF segment was found + if ( $Meta_Location == -1 ) + { + // Couldn't find any Meta block to decode + return FALSE; + } + + + $filehnd = @fopen($filename, 'rb'); + + // Check if the file opened successfully + if ( ! $filehnd ) + { + // Could't open the file - exit + error_log( "Could not open file $filename" ); + return FALSE; + } + + fseek( $filehnd, $jpeg_header_data[$Meta_Location]['SegDataStart'] + 6 ); + + // Decode the Meta segment into an array and return it + $meta = process_TIFF_Header( $filehnd, "Meta" ); + + // Close File + fclose($filehnd); + + return $meta; +} + +/****************************************************************************** +* End of Function: get_Meta +******************************************************************************/ + + + + + + + +/****************************************************************************** +* +* Function: put_Meta_JPEG +* +* Description: Stores information into a Meta APP3 segment from a Meta array. +* +* +* WARNING: Because the Meta (EXIF) standard allows pointers to data +* outside the APP1 segment, if there are any such pointers in +* a makernote, this function will DAMAGE them since it will not +* be aware that there is an external pointer. This will often +* happen with Makernotes that include an embedded thumbnail. +* This damage could be prevented where makernotes can be decoded, +* but currently this is not implemented. +* +* +* Parameters: meta_data - The array of Meta data to insert into the JPEG header +* jpeg_header_data - The JPEG header into which the Meta data +* should be stored, as from get_jpeg_header_data +* +* Returns: jpeg_header_data - JPEG header array with the Meta segment inserted +* FALSE - If an error occured +* +******************************************************************************/ + +function put_Meta_JPEG( $meta_data, $jpeg_header_data ) +{ + // pack the Meta data into its proper format for a JPEG file + $packed_data = get_TIFF_Packed_Data( $meta_data ); + if ( $packed_data === FALSE ) + { + return $jpeg_header_data; + } + + $packed_data = "Meta\x00\x00$packed_data"; + + //Cycle through the header segments + for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) + { + // If we find an APP1 header, + if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP3" ) == 0 ) + { + // And if it has the Meta label, + if ( ( strncmp ( $jpeg_header_data[$i]['SegData'], "Meta\x00\x00", 6) == 0 ) || + ( strncmp ( $jpeg_header_data[$i]['SegData'], "META\x00\x00", 6) == 0 ) ) + { + // Found a preexisting Meta block - Replace it with the new one and return. + $jpeg_header_data[$i]['SegData'] = $packed_data; + return $jpeg_header_data; + } + } + } + // No preexisting segment segment found, insert a new one at the start of the header data. + + // Determine highest position of an APP segment at or below APP3, so we can put the + // new APP3 at this position + + + $highest_APP = -1; + + //Cycle through the header segments + for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) + { + // Check if we have found an APP segment at or below APP3, + if ( ( $jpeg_header_data[$i]['SegType'] >= 0xE0 ) && ( $jpeg_header_data[$i]['SegType'] <= 0xE3 ) ) + { + // Found an APP segment at or below APP12 + $highest_APP = $i; + } + } + + // No preexisting Meta block found, insert a new one at the start of the header data. + array_splice($jpeg_header_data, $highest_APP + 1 , 0, array( array( "SegType" => 0xE3, + "SegName" => "APP3", + "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE1 ], + "SegData" => $packed_data ) ) ); + return $jpeg_header_data; + +} + +/****************************************************************************** +* End of Function: put_Meta_JPEG +******************************************************************************/ + + + +/****************************************************************************** +* +* Function: get_EXIF_TIFF +* +* Description: Retrieves information from a Exchangeable Image File Format (EXIF) +* within a TIFF file and returns it in an array. +* +* Parameters: filename - the filename of the TIFF image to process +* +* Returns: OutputArray - Array of EXIF records +* FALSE - If an error occured in decoding +* +******************************************************************************/ + +function get_EXIF_TIFF( $filename ) +{ + // Change: Added as of version 1.11 + // Check if a wrapper is being used - these are not currently supported (see notes at top of file) + if ( ( stristr ( $filename, "http://" ) != FALSE ) || ( stristr ( $filename, "ftp://" ) != FALSE ) ) + { + // A HTTP or FTP wrapper is being used - show a warning and abort + echo "HTTP and FTP wrappers are currently not supported with TIFF - See EXIF/TIFF functionality documentation - a local file must be specified
"; + echo "To work on an internet file, copy it locally to start with:

\n"; + echo "\$newfilename = tempnam ( \$dir, \"tmptiff\" );
\n"; + echo "copy ( \"http://whatever.com\", \$newfilename );

\n"; + return FALSE; + } + + + $filehnd = @fopen($filename, 'rb'); + + // Check if the file opened successfully + if ( ! $filehnd ) + { + // Could't open the file - exit + error_log( "Could not open file $filename" ); + return FALSE; + } + + // Decode the Exif segment into an array and return it + $exif_data = process_TIFF_Header( $filehnd, "TIFF" ); + + // Close File + fclose($filehnd); + return $exif_data; +} + +/****************************************************************************** +* End of Function: get_EXIF_TIFF +******************************************************************************/ + + + + +/****************************************************************************** +* +* Function: Interpret_EXIF_to_HTML +* +* Description: Generates html detailing the contents an APP1 EXIF array +* which was retrieved with a get_EXIF_.... function. +* Can also be used for APP3 Meta arrays. +* +* Parameters: Exif_array - the EXIF array,as read from get_EXIF_.... +* filename - the name of the Image file being processed ( used +* by scripts which displays EXIF thumbnails) +* +* Returns: output_str - A string containing the HTML +* +******************************************************************************/ + +function Interpret_EXIF_to_HTML( $Exif_array, $filename ) +{ + // Create the string to receive the html output + $output_str = ""; + + // Check if the array to process is valid + if ( $Exif_array === FALSE ) + { + // Exif Array is not valid - abort processing + return $output_str; + } + + // Ouput the heading according to what type of tags were used in processing + if ( $Exif_array[ 'Tags Name' ] == "TIFF" ) + { + $output_str .= "

Contains Exchangeable Image File Format (EXIF) Information

\n"; + } + else if ( $Exif_array[ 'Tags Name' ] == "Meta" ) + { + $output_str .= "

Contains META Information (APP3)

\n"; + } + else + { + $output_str .= "

Contains " . $Exif_array[ 'Tags Name' ] . " Information

\n"; + } + + + // Check that there are actually items to process in the array + if ( count( $Exif_array ) < 1 ) + { + // No items to process in array - abort processing + return $output_str; + } + + // Output secondary heading + $output_str .= "

Main Image Information

\n"; + + // Interpret the zeroth IFD to html + $output_str .= interpret_IFD( $Exif_array[0], $filename, $Exif_array['Byte_Align'] ); + + // Check if there is a first IFD to process + if ( array_key_exists( 1, $Exif_array ) ) + { + // There is a first IFD for a thumbnail + // Add a heading for it to the output + $output_str .= "

Thumbnail Information

\n"; + + // Interpret the IFD to html and add it to the output + $output_str .= interpret_IFD( $Exif_array[1], $filename, $Exif_array['Byte_Align'] ); + } + + // Cycle through any other IFD's + $i = 2; + while ( array_key_exists( $i, $Exif_array ) ) + { + // Add a heading for the IFD + $output_str .= "

Image File Directory (IFD) $i Information

\n"; + + // Interpret the IFD to html and add it to the output + $output_str .= interpret_IFD( $Exif_array[$i], $filename, $Exif_array['Byte_Align'] ); + $i++; + } + + // Return the resulting HTML + return $output_str; +} + +/****************************************************************************** +* End of Function: Interpret_EXIF_to_HTML +******************************************************************************/ + + + + + + + + + + + + + + + + +/****************************************************************************** +* +* INTERNAL FUNCTIONS +* +******************************************************************************/ + + + + + + + + + + + +/****************************************************************************** +* +* Internal Function: get_TIFF_Packed_Data +* +* Description: Packs TIFF IFD data from EXIF or Meta into a form ready for +* either a JPEG EXIF/Meta segment or a TIFF file +* This function attempts to protect the contents of an EXIF makernote, +* by ensuring that it remains in the same position relative to the +* TIFF header +* +* Parameters: tiff_data - the EXIF array,as read from get_EXIF_JPEG or get_Meta_JPEG +* +* Returns: packed_data - A string containing packed segment +* +******************************************************************************/ + +function get_TIFF_Packed_Data( $tiff_data ) +{ + // Check that the segment is valid + if ( $tiff_data === FALSE ) + { + return FALSE; + } + + // Get the byte alignment + $Byte_Align = $tiff_data['Byte_Align']; + + // Add the Byte Alignment to the Packed data + $packed_data = $Byte_Align; + + // Add the TIFF ID to the Packed Data + $packed_data .= put_IFD_Data_Type( 42, 3, $Byte_Align ); + + // Create a string for the makernote + $makernote = ""; + + // Check if the makernote exists + if ( $tiff_data[ 'Makernote_Tag' ] !== FALSE ) + { + // A makernote exists - We need to ensure that it stays in the same position as it was + // Put the Makernote before any of the IFD's by padding zeros to the correct offset + $makernote .= str_repeat("\x00",( $tiff_data[ 'Makernote_Tag' ][ 'Offset' ] - 8 ) ); + $makernote .= $tiff_data[ 'Makernote_Tag' ]['Data']; + } + + // Calculage where the zeroth ifd will be + $ifd_offset = strlen( $makernote ) + 8; + + // Add the Zeroth IFD pointer to the packed data + $packed_data .= put_IFD_Data_Type( $ifd_offset, 4, $Byte_Align ); + + // Add the makernote to the packed data (if there was one) + $packed_data .= $makernote; + + //Add the IFD's to the packed data + $packed_data .= get_IFD_Array_Packed_Data( $tiff_data, $ifd_offset, $Byte_Align ); + + // Return the result + return $packed_data; +} + +/****************************************************************************** +* End of Function: get_TIFF_Packed_Data +******************************************************************************/ + + + + +/****************************************************************************** +* +* Internal Function: get_IFD_Array_Packed_Data +* +* Description: Packs a chain of IFD's from EXIF or Meta segments into a form +* ready for either a JPEG EXIF/Meta segment or a TIFF file +* +* Parameters: ifd_data - the IFD chain array, as read from get_EXIF_JPEG or get_Meta_JPEG +* Zero_IFD_offset - The offset to the first IFD from the start of the TIFF header +* Byte_Align - the Byte alignment to use - "MM" or "II" +* +* Returns: packed_data - A string containing packed IFD's +* +******************************************************************************/ + +function get_IFD_Array_Packed_Data( $ifd_data, $Zero_IFD_offset, $Byte_Align ) +{ + // Create a string to receive the packed output + $packed_data = ""; + + // Count the IFDs + $ifd_count = 0; + foreach( $ifd_data as $key => $IFD ) + { + // Make sure we only count the IFD's, not other information keys + if ( is_numeric( $key ) ) + { + $ifd_count++; + } + } + + + // Cycle through each IFD, + for ( $ifdno = 0; $ifdno < $ifd_count; $ifdno++ ) + { + // Check if this IFD is the last one + if ( $ifdno == $ifd_count - 1 ) + { + // This IFD is the last one, get it's packed data + $packed_data .= get_IFD_Packed_Data( $ifd_data[ $ifdno ], $Zero_IFD_offset +strlen($packed_data), $Byte_Align, FALSE ); + } + else + { + // This IFD is NOT the last one, get it's packed data + $packed_data .= get_IFD_Packed_Data( $ifd_data[ $ifdno ], $Zero_IFD_offset +strlen($packed_data), $Byte_Align, TRUE ); + } + + } + + // Return the packed output + return $packed_data; +} + +/****************************************************************************** +* End of Function: get_IFD_Array_Packed_Data +******************************************************************************/ + + + +/****************************************************************************** +* +* Internal Function: get_IFD_Packed_Data +* +* Description: Packs an IFD from EXIF or Meta segments into a form +* ready for either a JPEG EXIF/Meta segment or a TIFF file +* +* Parameters: ifd_data - the IFD chain array, as read from get_EXIF_JPEG or get_Meta_JPEG +* IFD_offset - The offset to the IFD from the start of the TIFF header +* Byte_Align - the Byte alignment to use - "MM" or "II" +* Another_IFD - boolean - false if this is the last IFD in the chain +* - true if it is not the last +* +* Returns: packed_data - A string containing packed IFD's +* +******************************************************************************/ + +function get_IFD_Packed_Data( $ifd_data, $IFD_offset, $Byte_Align, $Another_IFD ) +{ + + $ifd_body_str = ""; + $ifd_data_str = ""; + + $Tag_Definitions_Name = $ifd_data[ 'Tags Name' ]; + + + // Count the Tags in this IFD + $tag_count = 0; + foreach( $ifd_data as $key => $tag ) + { + // Make sure we only count the Tags, not other information keys + if ( is_numeric( $key ) ) + { + $tag_count++; + } + } + + // Add the Tag count to the packed data + $packed_data = put_IFD_Data_Type( $tag_count, 3, $Byte_Align ); + + // Calculate the total length of the IFD (without the offset data) + $IFD_len = 2 + $tag_count * 12 + 4; + + + // Cycle through each tag + foreach( $ifd_data as $key => $tag ) + { + // Make sure this is a tag, not another information key + if ( is_numeric( $key ) ) + { + + // Add the tag number to the packed data + $ifd_body_str .= put_IFD_Data_Type( $tag[ 'Tag Number' ], 3, $Byte_Align ); + + // Add the Data type to the packed data + $ifd_body_str .= put_IFD_Data_Type( $tag['Data Type'], 3, $Byte_Align ); + + // Check if this is a Print Image Matching entry + if ( $tag['Type'] == "PIM" ) + { + // This is a Print Image Matching entry, + // encode it + $data = Encode_PIM( $tag, $Byte_Align ); + } + // Check if this is a IPTC/NAA Record within the EXIF IFD + else if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) && + ( $tag[ 'Tag Number' ] == 33723 ) ) + { + // This is a IPTC/NAA Record, encode it + $data = put_IPTC( $tag['Data'] ); + } + // Change: Check for embedded XMP as of version 1.11 + // Check if this is a XMP Record within the EXIF IFD + else if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) && + ( $tag[ 'Tag Number' ] == 700 ) ) + { + // This is a XMP Record, encode it + $data = write_XMP_array_to_text( $tag['Data'] ); + } + // Change: Check for embedded IRB as of version 1.11 + // Check if this is a Photoshop IRB Record within the EXIF IFD + else if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) && + ( $tag[ 'Tag Number' ] == 34377 ) ) + { + // This is a Photoshop IRB Record, encode it + $data = pack_Photoshop_IRB_Data( $tag['Data'] ); + } + // Exif Thumbnail Offset + else if ( ( $tag[ 'Tag Number' ] == 513 ) && ( $Tag_Definitions_Name == "TIFF" ) ) + { + // The Exif Thumbnail Offset is a pointer but of type Long, not Unknown + // Hence we need to put the data into the packed string separately + // Calculate the thumbnail offset + $data_offset = $IFD_offset + $IFD_len + strlen($ifd_data_str); + + // Create the Offset for the IFD + $data = put_IFD_Data_Type( $data_offset, 4, $Byte_Align ); + + // Store the thumbnail + $ifd_data_str .= $tag['Data']; + } + // Exif Thumbnail Length + else if ( ( $tag[ 'Tag Number' ] == 514 ) && ( $Tag_Definitions_Name == "TIFF" ) ) + { + // Encode the Thumbnail Length + $data = put_IFD_Data_Type( strlen($ifd_data[513]['Data']), 4, $Byte_Align ); + } + // Sub-IFD + else if ( $tag['Type'] == "SubIFD" ) + { + // This is a Sub-IFD + // Calculate the offset to the start of the Sub-IFD + $data_offset = $IFD_offset + $IFD_len + strlen($ifd_data_str); + // Get the packed data for the IFD chain as the data for this tag + $data = get_IFD_Array_Packed_Data( $tag['Data'], $data_offset, $Byte_Align ); + } + else + { + // Not a special tag + + // Create a string to receive the data + $data = ""; + + // Check if this is a type Unknown tag + if ( $tag['Data Type'] != 7 ) + { + // NOT type Unknown + // Cycle through each data value and add it to the data string + foreach( $tag[ 'Data' ] as $data_val ) + { + $data .= put_IFD_Data_Type( $data_val, $tag['Data Type'], $Byte_Align ); + } + } + else + { + // This is a type Unknown - just add the data as is to the data string + $data .= $tag[ 'Data' ]; + } + } + + // Pad the data string out to at least 4 bytes + $data = str_pad ( $data, 4, "\x00" ); + + + // Check if the data type is an ASCII String or type Unknown + if ( ( $tag['Data Type'] == 2 ) || ( $tag['Data Type'] == 7 ) ) + { + // This is an ASCII String or type Unknown + // Add the Length of the string to the packed data as the Count + $ifd_body_str .= put_IFD_Data_Type( strlen($data), 4, $Byte_Align ); + } + else + { + // Add the array count to the packed data as the Count + $ifd_body_str .= put_IFD_Data_Type( count($tag[ 'Data' ]), 4, $Byte_Align ); + } + + + // Check if the data is over 4 bytes long + if ( strlen( $data ) > 4 ) + { + // Data is longer than 4 bytes - it needs to be offset + // Check if this entry is the Maker Note + if ( ( $Tag_Definitions_Name == "EXIF" ) && ( $tag[ 'Tag Number' ] == 37500 ) ) + { + // This is the makernote - It will have already been stored + // at its original offset to help preserve it + // all we need to do is add the Offset to the IFD packed data + $data_offset = $tag[ 'Offset' ]; + + $ifd_body_str .= put_IFD_Data_Type( $data_offset, 4, $Byte_Align ); + } + else + { + // This is NOT the makernote + // Calculate the data offset + $data_offset = $IFD_offset + $IFD_len + strlen($ifd_data_str); + + // Add the offset to the IFD packed data + $ifd_body_str .= put_IFD_Data_Type( $data_offset, 4, $Byte_Align ); + + // Add the data to the offset packed data + $ifd_data_str .= $data; + } + } + else + { + // Data is less than or equal to 4 bytes - Add it to the packed IFD data as is + $ifd_body_str .= $data; + } + + } + } + + // Assemble the IFD body onto the packed data + $packed_data .= $ifd_body_str; + + // Check if there is another IFD after this one + if( $Another_IFD === TRUE ) + { + // There is another IFD after this + // Calculate the Next-IFD offset so that it goes immediately after this IFD + $next_ifd_offset = $IFD_offset + $IFD_len + strlen($ifd_data_str); + } + else + { + // There is NO IFD after this - indicate with offset=0 + $next_ifd_offset = 0; + } + + // Add the Next-IFD offset to the packed data + $packed_data .= put_IFD_Data_Type( $next_ifd_offset, 4, $Byte_Align ); + + // Add the offset data to the packed data + $packed_data .= $ifd_data_str; + + // Return the resulting packed data + return $packed_data; +} + +/****************************************************************************** +* End of Function: get_IFD_Packed_Data +******************************************************************************/ + + + + + +/****************************************************************************** +* +* Internal Function: process_TIFF_Header +* +* Description: Decodes the information stored in a TIFF header and it's +* Image File Directories (IFD's). This information is returned +* in an array +* +* Parameters: filehnd - The handle of a open image file, positioned at the +* start of the TIFF header +* Tag_Definitions_Name - The name of the Tag Definitions group +* within the global array IFD_Tag_Definitions +* +* +* Returns: OutputArray - Array of IFD records +* FALSE - If an error occured in decoding +* +******************************************************************************/ + +function process_TIFF_Header( $filehnd, $Tag_Definitions_Name ) +{ + + + // Save the file position where the TIFF header starts, as offsets are relative to this position + $Tiff_start_pos = ftell( $filehnd ); + + + + // Read the eight bytes of the TIFF header + $DataStr = network_safe_fread( $filehnd, 8 ); + + // Check that we did get all eight bytes + if ( strlen( $DataStr ) != 8 ) + { + return FALSE; // Couldn't read the TIFF header properly + } + + $pos = 0; + // First two bytes indicate the byte alignment - should be 'II' or 'MM' + // II = Intel (LSB first, MSB last - Little Endian) + // MM = Motorola (MSB first, LSB last - Big Endian) + $Byte_Align = substr( $DataStr, $pos, 2 ); + + + + // Check the Byte Align Characters for validity + if ( ( $Byte_Align != "II" ) && ( $Byte_Align != "MM" ) ) + { + // Byte align field is invalid - we won't be able to decode file + return FALSE; + } + + // Skip over the Byte Align field which was just read + $pos += 2; + + // Next two bytes are TIFF ID - should be value 42 with the appropriate byte alignment + $TIFF_ID = substr( $DataStr, $pos, 2 ); + + if ( get_IFD_Data_Type( $TIFF_ID, 3, $Byte_Align ) != 42 ) + { + // TIFF header ID not found + return FALSE; + } + + // Skip over the TIFF ID field which was just read + $pos += 2; + + + // Next four bytes are the offset to the first IFD + $offset_str = substr( $DataStr, $pos, 4 ); + $offset = get_IFD_Data_Type( $offset_str, 4, $Byte_Align ); + + // Done reading TIFF Header + + + // Move to first IFD + + if ( fseek( $filehnd, $Tiff_start_pos + $offset ) !== 0 ) + { + // Error seeking to position of first IFD + return FALSE; + } + + + + // Flag that a makernote has not been found yet + $GLOBALS[ "Maker_Note_Tag" ] = FALSE; + + // Read the IFD chain into an array + $Output_Array = read_Multiple_IFDs( $filehnd, $Tiff_start_pos, $Byte_Align, $Tag_Definitions_Name ); + + // Check if a makernote was found + if ( $GLOBALS[ "Maker_Note_Tag" ] != FALSE ) + { + // Makernote was found - Process it + // The makernote needs to be processed after all other + // tags as it may require some of the other tags in order + // to be processed properly + $GLOBALS[ "Maker_Note_Tag" ] = Read_Makernote_Tag( $GLOBALS[ "Maker_Note_Tag" ], $Output_Array, $filehnd ); + + } + + $Output_Array[ 'Makernote_Tag' ] = $GLOBALS[ "Maker_Note_Tag" ]; + + // Save the Name of the Tags used in the output array + $Output_Array[ 'Tags Name' ] = $Tag_Definitions_Name; + + + + // Save the Byte alignment + $Output_Array['Byte_Align'] = $Byte_Align; + + + // Return the output array + return $Output_Array ; +} + +/****************************************************************************** +* End of Function: process_TIFF_Header +******************************************************************************/ + + + + + + +/****************************************************************************** +* +* Internal Function: read_Multiple_IFDs +* +* Description: Reads and interprets a chain of standard Image File Directories (IFD's), +* and returns the entries in an array. This chain is made up from IFD's +* which have a pointer to the next IFD. IFD's are read until the next +* pointer indicates there are no more +* +* Parameters: filehnd - a handle for the image file being read, positioned at the +* start of the IFD chain +* Tiff_offset - The offset of the TIFF header from the start of the file +* Byte_Align - either "MM" or "II" indicating Motorola or Intel Byte alignment +* Tag_Definitions_Name - The name of the Tag Definitions group within the global array IFD_Tag_Definitions +* local_offsets - True indicates that offset data should be interpreted as being relative to the start of the currrent entry +* False (normal) indicates offests are relative to start of Tiff header as per IFD standard +* read_next_ptr - True (normal) indicates that a pointer to the next IFD should be read at the end of the IFD +* False indicates that no pointer follows the IFD +* +* +* Returns: OutputArray - Array of IFD entries +* +******************************************************************************/ + +function read_Multiple_IFDs( $filehnd, $Tiff_offset, $Byte_Align, $Tag_Definitions_Name, $local_offsets = FALSE, $read_next_ptr = TRUE ) +{ + // Start at the offset of the first IFD + $Next_Offset = 0; + + do + { + // Read an IFD + list($IFD_Array , $Next_Offset) = read_IFD_universal( $filehnd, $Tiff_offset, $Byte_Align, $Tag_Definitions_Name, $local_offsets, $read_next_ptr ); + + // Move to the position of the next IFD + if ( fseek( $filehnd, $Tiff_offset + $Next_Offset ) !== 0 ) + { + // Error seeking to position of next IFD + error_log( "Error: Corrupted EXIF" ); + return FALSE; + } + + $Output_Array[] = $IFD_Array; + + + } while ( $Next_Offset != 0 ); // Until the Next IFD Offset is zero + + + // return resulting array + + return $Output_Array ; +} + +/****************************************************************************** +* End of Function: read_Multiple_IFDs +******************************************************************************/ + + + + + + + +/****************************************************************************** +* +* Internal Function: read_IFD_universal +* +* Description: Reads and interprets a standard or Non-standard Image File +* Directory (IFD), and returns the entries in an array +* +* Parameters: filehnd - a handle for the image file being read, positioned at the start +* of the IFD +* Tiff_offset - The offset of the TIFF header from the start of the file +* Byte_Align - either "MM" or "II" indicating Motorola or Intel Byte alignment +* Tag_Definitions_Name - The name of the Tag Definitions group within the global array IFD_Tag_Definitions +* local_offsets - True indicates that offset data should be interpreted as being relative to the start of the currrent entry +* False (normal) indicates offests are relative to start of Tiff header as per IFD standard +* read_next_ptr - True (normal) indicates that a pointer to the next IFD should be read at the end of the IFD +* False indicates that no pointer follows the IFD +* +* Returns: OutputArray - Array of IFD entries +* Next_Offset - Offset to next IFD (zero = no next IFD) +* +******************************************************************************/ + +function read_IFD_universal( $filehnd, $Tiff_offset, $Byte_Align, $Tag_Definitions_Name, $local_offsets = FALSE, $read_next_ptr = TRUE ) +{ + if ( ( $filehnd == NULL ) || ( feof( $filehnd ) ) ) + { + return array (FALSE , 0); + } + + // Record the Name of the Tag Group used for this IFD in the output array + $OutputArray[ 'Tags Name' ] = $Tag_Definitions_Name; + + // Record the offset of the TIFF header in the output array + $OutputArray[ 'Tiff Offset' ] = $Tiff_offset; + + // First 2 bytes of IFD are number of entries in the IFD + $No_Entries_str = network_safe_fread( $filehnd, 2 ); + $No_Entries = get_IFD_Data_Type( $No_Entries_str, 3, $Byte_Align ); + + + // If the data is corrupt, the number of entries may be huge, which will cause errors + // This is often caused by a lack of a Next-IFD pointer + if ( $No_Entries> 10000 ) + { + // Huge number of entries - abort + // error_log( "Error: huge number of EXIF entries - EXIF is probably Corrupted" ); + + return array ( FALSE , 0); + } + + // If the data is corrupt or just stupid, the number of entries may zero, + // Indicate this by returning false + if ( $No_Entries === 0 ) + { + // No entries - abort + return array ( FALSE , 0); + } + + // Save the file position where first IFD record starts as non-standard offsets + // need to know this to calculate an absolute offset + $IFD_first_rec_pos = ftell( $filehnd ); + + + // Read in the IFD structure + $IFD_Data = network_safe_fread( $filehnd, 12 * $No_Entries ); + + // Check if the entire IFD was able to be read + if ( strlen( $IFD_Data ) != (12 * $No_Entries) ) + { + // Couldn't read the IFD Data properly, Some Casio files have no Next IFD pointer, hence cause this error + error_log( "Error: EXIF Corrupted" ); + return array(FALSE, 0); + } + + + // Last 4 bytes of a standard IFD are the offset to the next IFD + // Some NON-Standard IFD implementations do not have this, hence causing problems if it is read + + // If the Next IFD pointer has been requested to be read, + if ( $read_next_ptr ) + { + // Read the pointer to the next IFD + + $Next_Offset_str = network_safe_fread( $filehnd, 4 ); + $Next_Offset = get_IFD_Data_Type( $Next_Offset_str, 4, $Byte_Align ); + } + else + { + // Otherwise set the pointer to zero ( no next IFD ) + $Next_Offset = 0; + } + + + + // Initialise current position to the start + $pos = 0; + + + // Loop for reading IFD entries + + for ( $i = 0; $i < $No_Entries; $i++ ) + { + // First 2 bytes of IFD entry are the tag number ( Unsigned Short ) + $Tag_No_str = substr( $IFD_Data, $pos, 2 ); + $Tag_No = get_IFD_Data_Type( $Tag_No_str, 3, $Byte_Align ); + $pos += 2; + + // Next 2 bytes of IFD entry are the data format ( Unsigned Short ) + $Data_Type_str = substr( $IFD_Data, $pos, 2 ); + $Data_Type = get_IFD_Data_Type( $Data_Type_str, 3, $Byte_Align ); + $pos += 2; + + // If Datatype is not between 1 and 12, then skip this entry, it is probably corrupted or custom + if (( $Data_Type > 12 ) || ( $Data_Type < 1 ) ) + { + $pos += 8; + continue 1; // Stop trying to process the tag any further and skip to the next one + } + + // Next 4 bytes of IFD entry are the data count ( Unsigned Long ) + $Data_Count_str = substr( $IFD_Data, $pos, 4 ); + $Data_Count = get_IFD_Data_Type( $Data_Count_str, 4, $Byte_Align ); + $pos += 4; + + if ( $Data_Count > 100000 ) + { + // error_log( "Error: huge EXIF data count - EXIF is probably Corrupted" ); + + // Some Casio files have no Next IFD pointer, hence cause errors + + return array ( FALSE , 0); + } + + // Total Data size is the Data Count multiplied by the size of the Data Type + $Total_Data_Size = $GLOBALS['IFD_Data_Sizes'][ $Data_Type ] * $Data_Count; + + $Data_Start_pos = -1; + + // If the total data size is larger than 4 bytes, then the data part is the offset to the real data + if ( $Total_Data_Size > 4 ) + { + // Not enough room for data - offset provided instead + $Data_Offset_str = substr( $IFD_Data, $pos, 4 ); + $Data_Start_pos = get_IFD_Data_Type( $Data_Offset_str, 4, $Byte_Align ); + + + // In some NON-STANDARD makernotes, the offset is relative to the start of the current IFD entry + if ( $local_offsets ) + { + // This is a NON-Standard IFD, seek relative to the start of the current tag + fseek( $filehnd, $IFD_first_rec_pos + $pos - 8 + $Data_Start_pos ); + } + else + { + // This is a normal IFD, seek relative to the start of the TIFF header + fseek( $filehnd, $Tiff_offset + $Data_Start_pos ); + } + + // Read the data block from the offset position + $DataStr = network_safe_fread( $filehnd, $Total_Data_Size ); + } + else + { + // The data block is less than 4 bytes, and is provided in the IFD entry, so read it + $DataStr = substr( $IFD_Data, $pos, $Total_Data_Size ); + } + + // Increment the position past the data + $pos += 4; + + + // Now create the entry for output array + + $Data_Array = array( ); + + + // Read the data items from the data block + + if ( ( $Data_Type != 2 ) && ( $Data_Type != 7 ) ) + { + // The data type is Numerical, Read the data items from the data block + for ( $j = 0; $j < $Data_Count; $j++ ) + { + $Part_Data_Str = substr( $DataStr, $j * $GLOBALS['IFD_Data_Sizes'][ $Data_Type ], $GLOBALS['IFD_Data_Sizes'][ $Data_Type ] ); + $Data_Array[] = get_IFD_Data_Type( $Part_Data_Str, $Data_Type, $Byte_Align ); + } + } + elseif ( $Data_Type == 2 ) + { + // The data type is String(s) (type 2) + + // Strip the last terminating Null + $DataStr = substr( $DataStr, 0, strlen($DataStr)-1 ); + + // Split the data block into multiple strings whereever there is a Null + $Data_Array = explode( "\x00", $DataStr ); + } + else + { + // The data type is Unknown (type 7) + // Do nothing to data + $Data_Array = $DataStr; + } + + + // If this is a Sub-IFD entry, + if ( ( array_key_exists( $Tag_No, $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name] ) ) && + ( "SubIFD" == $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ]['Type'] ) ) + { + // This is a Sub-IFD entry, go and process the data forming Sub-IFD and use its output array as the new data for this entry + fseek( $filehnd, $Tiff_offset + $Data_Array[0] ); + $Data_Array = read_Multiple_IFDs( $filehnd, $Tiff_offset, $Byte_Align, $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ]['Tags Name'] ); + } + + $desc = ""; + $units = ""; + + // Check if this tag exists in the list of tag definitions, + + if ( array_key_exists ( $Tag_No, $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name]) ) + { + + if ( array_key_exists ( 'Description', $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ] ) ) + { + $desc = $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ]['Description']; + } + + if ( array_key_exists ( 'Units', $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ] ) ) + { + $units = $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ]['Units']; + } + + // Tag exists in definitions, append details to output array + $OutputArray[ $Tag_No ] = array ( "Tag Number" => $Tag_No, + "Tag Name" => $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ]['Name'], + "Tag Description" => $desc, + "Data Type" => $Data_Type, + "Type" => $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ]['Type'], + "Units" => $units, + "Data" => $Data_Array ); + + } + else + { + // Tag doesnt exist in definitions, append unknown details to output array + + $OutputArray[ $Tag_No ] = array ( "Tag Number" => $Tag_No, + "Tag Name" => "Unknown Tag #" . $Tag_No, + "Tag Description" => "", + "Data Type" => $Data_Type, + "Type" => "Unknown", + "Units" => "", + "Data" => $Data_Array ); + } + + + + // Some information of type "Unknown" (type 7) might require information about + // how it's position and byte alignment in order to be decoded + if ( $Data_Type == 7 ) + { + $OutputArray[ $Tag_No ]['Offset'] = $Data_Start_pos; + $OutputArray[ $Tag_No ]['Byte Align'] = $Byte_Align; + } + + + //////////////////////////////////////////////////////////////////////// + // Special Data handling + //////////////////////////////////////////////////////////////////////// + + + // Check if this is a Print Image Matching entry + if ( $OutputArray[ $Tag_No ]['Type'] == "PIM" ) + { + // This is a Print Image Matching entry, decode it. + $OutputArray[ $Tag_No ] = Decode_PIM( $OutputArray[ $Tag_No ], $Tag_Definitions_Name ); + } + + + // Interpret the entry into a text string using a custom interpreter + $text_val = get_Tag_Text_Value( $OutputArray[ $Tag_No ], $Tag_Definitions_Name ); + + // Check if a text string was generated + if ( $text_val !== FALSE ) + { + // A string was generated, append it to the output array entry + $OutputArray[ $Tag_No ]['Text Value'] = $text_val; + $OutputArray[ $Tag_No ]['Decoded'] = TRUE; + } + else + { + // A string was NOT generated, append a generic string to the output array entry + $OutputArray[ $Tag_No ]['Text Value'] = get_IFD_value_as_text( $OutputArray[ $Tag_No ] ) . " " . $units; + $OutputArray[ $Tag_No ]['Decoded'] = FALSE; + } + + + + + // Check if this entry is the Maker Note + if ( ( $Tag_Definitions_Name == "EXIF" ) && ( $Tag_No == 37500 ) ) + { + + // Save some extra information which will allow Makernote Decoding with the output array entry + $OutputArray[ $Tag_No ]['Offset'] = $Data_Start_pos; + $OutputArray[ $Tag_No ][ 'Tiff Offset' ] = $Tiff_offset; + $OutputArray[ $Tag_No ]['ByteAlign'] = $Byte_Align; + + // Save a pointer to this entry for Maker note processing later + $GLOBALS[ "Maker_Note_Tag" ] = & $OutputArray[ $Tag_No ]; + } + + + // Check if this is a IPTC/NAA Record within the EXIF IFD + if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) && + ( $Tag_No == 33723 ) ) + { + // This is a IPTC/NAA Record, interpret it and put result in the data for this entry + $OutputArray[ $Tag_No ]['Data'] = get_IPTC( $DataStr ); + $OutputArray[ $Tag_No ]['Decoded'] = TRUE; + } + // Change: Check for embedded XMP as of version 1.11 + // Check if this is a XMP Record within the EXIF IFD + if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) && + ( $Tag_No == 700 ) ) + { + // This is a XMP Record, interpret it and put result in the data for this entry + $OutputArray[ $Tag_No ]['Data'] = read_XMP_array_from_text( $DataStr ); + $OutputArray[ $Tag_No ]['Decoded'] = TRUE; + } + + // Change: Check for embedded IRB as of version 1.11 + // Check if this is a Photoshop IRB Record within the EXIF IFD + if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) && + ( $Tag_No == 34377 ) ) + { + // This is a Photoshop IRB Record, interpret it and put result in the data for this entry + $OutputArray[ $Tag_No ]['Data'] = unpack_Photoshop_IRB_Data( $DataStr ); + $OutputArray[ $Tag_No ]['Decoded'] = TRUE; + } + + // Exif Thumbnail + // Check that both the thumbnail length and offset entries have been processed, + // and that this is one of them + if ( ( ( ( $Tag_No == 513 ) && ( array_key_exists( 514, $OutputArray ) ) ) || + ( ( $Tag_No == 514 ) && ( array_key_exists( 513, $OutputArray ) ) ) ) && + ( $Tag_Definitions_Name == "TIFF" ) ) + { + // Seek to the start of the thumbnail using the offset entry + fseek( $filehnd, $Tiff_offset + $OutputArray[513]['Data'][0] ); + + // Read the thumbnail data, and replace the offset data with the thumbnail + $OutputArray[513]['Data'] = network_safe_fread( $filehnd, $OutputArray[514]['Data'][0] ); + } + + + // Casio Thumbnail + // Check that both the thumbnail length and offset entries have been processed, + // and that this is one of them + if ( ( ( ( $Tag_No == 0x0004 ) && ( array_key_exists( 0x0003, $OutputArray ) ) ) || + ( ( $Tag_No == 0x0003 ) && ( array_key_exists( 0x0004, $OutputArray ) ) ) ) && + ( $Tag_Definitions_Name == "Casio Type 2" ) ) + { + // Seek to the start of the thumbnail using the offset entry + fseek( $filehnd, $Tiff_offset + $OutputArray[0x0004]['Data'][0] ); + + // Read the thumbnail data, and replace the offset data with the thumbnail + $OutputArray[0x0004]['Data'] = network_safe_fread( $filehnd, $OutputArray[0x0003]['Data'][0] ); + } + + // Minolta Thumbnail + // Check that both the thumbnail length and offset entries have been processed, + // and that this is one of them + if ( ( ( ( $Tag_No == 0x0088 ) && ( array_key_exists( 0x0089, $OutputArray ) ) ) || + ( ( $Tag_No == 0x0089 ) && ( array_key_exists( 0x0088, $OutputArray ) ) ) ) && + ( $Tag_Definitions_Name == "Olympus" ) ) + { + + // Seek to the start of the thumbnail using the offset entry + fseek( $filehnd, $Tiff_offset + $OutputArray[0x0088]['Data'][0] ); + + // Read the thumbnail data, and replace the offset data with the thumbnail + $OutputArray[0x0088]['Data'] = network_safe_fread( $filehnd, $OutputArray[0x0089]['Data'][0] ); + + // Sometimes the minolta thumbnail data is empty (or the offset is corrupt, which results in the same thing) + + // Check if the thumbnail data exists + if ( $OutputArray[0x0088]['Data'] != "" ) + { + // Thumbnail exists + + // Minolta Thumbnails are missing their first 0xFF for some reason, + // which is replaced with some weird character, so fix this + $OutputArray[0x0088]['Data']{0} = "\xFF"; + } + else + { + // Thumbnail doesnt exist - make it obvious + $OutputArray[0x0088]['Data'] = FALSE; + } + } + + } + + + + + + + + // Return the array of IFD entries and the offset to the next IFD + + return array ($OutputArray , $Next_Offset); +} + + + +/****************************************************************************** +* End of Function: read_IFD_universal +******************************************************************************/ + + + + + + + + + + + + +/****************************************************************************** +* +* Internal Function: get_Tag_Text_Value +* +* Description: Attempts to interpret an IFD entry into a text string using the +* information in the IFD_Tag_Definitions global array. +* +* Parameters: Tag - The IFD entry to process +* Tag_Definitions_Name - The name of the tag definitions to use from within the IFD_Tag_Definitions global array +* +* Returns: String - if the tag was successfully decoded into a text string +* FALSE - if the tag could not be decoded using the information +* in the IFD_Tag_Definitions global array +* +******************************************************************************/ + +function get_Tag_Text_Value( $Tag, $Tag_Definitions_Name ) +{ + // Check what format the entry is specified as + + if ( $Tag['Type'] == "String" ) + { + // Format is Text String + + // If "Unknown" (type 7) data type, + if ( $Tag['Data Type'] == 7 ) + { + // Return data as is. + return $Tag['Data']; + } + else + { + // Otherwise return the default string value of the datatype + return get_IFD_value_as_text( $Tag ); + } + } + else if ( $Tag['Type'] == "Character Coded String" ) + { + // Format is Character Coded String (First 8 characters indicate coding scheme) + + // Convert Data to a string + if ( $Tag['Data Type'] == 7 ) + { + // If it is type "Unknown" (type 7) use data as is + $data = $Tag['Data']; + } + else + { + // Otherwise use the default string value of the datatype + $data = get_IFD_value_as_text( $Tag ); + } + + // Some implementations allow completely data with no Coding Scheme Name, + // so we need to handle this to avoid errors + if ( trim( $data ) == "" ) + { + return ""; + } + + // Extract the Coding Scheme Name from the first 8 characters + $char_code = substr( $data, 0, 8 ); + + // Extract the Data part from after the first 8 characters + $characters = substr( $data, 8 ); + + // Check coding scheme and interpret as neccessary + + if ( $char_code === "ASCII\x00\x00\x00" ) + { + // ASCII coding - return data as is. + return $characters; + } + elseif ( ( $char_code === "UNICODE\x00" ) || + ( $char_code === "Unicode\x00" ) ) // Note lowercase is non standard + { + // Unicode coding - interpret and return result. + return xml_UTF16_clean( $characters, TRUE ); + } + else + { + // Unknown coding - return string indicating this + return "Unsupported character coding : \"$char_code\"\n\"" . trim($characters) . "\""; + } + break; + } + else if ( $Tag['Type'] == "Numeric" ) + { + // Format is numeric - return default text value with any required units text appended + if ( array_key_exists ( 'Units', $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag["Tag Number"] ] ) ) + { + $units = $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag["Tag Number"] ]['Units']; + } + else + { + $units = ""; + } + return get_IFD_value_as_text( $Tag ) . " " . $units; + } + else if ( $Tag['Type'] == "Lookup" ) + { + // Format is a Lookup Table + + // Get a numeric value to use in lookup + + if ( is_array( $Tag['Data'] ) ) + { + // If data is an array, use first element + $first_val = $Tag['Data'][0]; + } + else if ( is_string( $Tag['Data'] ) ) + { + // If data is a string, use the first character + $first_val = ord($Tag['Data']{0}); + } + else + { + // Otherwise use the data as is + $first_val = $Tag['Data']; + } + + // Check if the data value exists in the lookup table for this IFD entry + if ( array_key_exists( $first_val, $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag["Tag Number"] ] ) ) + { + // Data value exists in lookup table - return the matching string + return $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag["Tag Number"] ][ $first_val ]; + } + else + { + // Data value doesnt exist in lookup table - return explanation string + return "Unknown Reserved value $first_val "; + } + } + else if ( $Tag['Type'] == "Special" ) + { + // Format is special - interpret to text with special handlers + return get_Special_Tag_Text_Value( $Tag, $Tag_Definitions_Name ); + } + else if ( $Tag['Type'] == "PIM" ) + { + // Format is Print Image Matching info - interpret with custom handler + return get_PIM_Text_Value( $Tag, $Tag_Definitions_Name ); + } + else if ( $Tag['Type'] == "SubIFD" ) + { + // Format is a Sub-IFD - this has no text value + return ""; + } + else + { + // Unknown Format - Couldn't interpret using the IFD_Tag_Definitions global array information + return FALSE; + } +} + +/****************************************************************************** +* End of Function: get_Tag_Text_Value +******************************************************************************/ + + + + + + +/****************************************************************************** +* +* Internal Function: get_Special_Tag_Text_Value +* +* Description: Interprets an IFD entry marked as "Special" in the IFD_Tag_Definitions +* global array into a text string using custom handlers +* +* Parameters: Tag - The IFD entry to process +* Tag_Definitions_Name - The name of the tag definitions to use from within the IFD_Tag_Definitions global array +* +* Returns: String - if the tag was successfully decoded into a text string +* FALSE - if the tag could not be decoded +* +******************************************************************************/ + +function get_Special_Tag_Text_Value( $Tag, $Tag_Definitions_Name ) +{ + // Check what type of IFD is being decoded + + if ( $Tag_Definitions_Name == "TIFF" ) + { + // This is a TIFF IFD (bottom level) + + // Check what tag number the IFD entry has. + switch ( $Tag['Tag Number'] ) + { + case 530: // YCbCr Sub Sampling Entry + + // Data contains two numerical values + + if ( ( $Tag['Data'][0] == 2 ) && ( $Tag['Data'][1] == 1 ) ) + { + // Values are 2,1 - hence YCbCr 4:2:2 + return "YCbCr 4:2:2 ratio of chrominance components to the luminance components"; + } + elseif ( ( $Tag['Data'][0] == 2 ) && ( $Tag['Data'][1] == 2 ) ) + { + // Values are 2,2 - hence YCbCr 4:2:0 + return "YCbCr 4:2:0 ratio of chrominance components to the luminance components"; + } + else + { + // Other values are unknown + return "Unknown Reserved value (" . $Tag['Data'][0] . ")"; + } + break; + + default: + return FALSE; + } + } + else if ( $Tag_Definitions_Name == "EXIF" ) + { + // This is an EXIF IFD + + // Check what tag number the IFD entry has. + switch ( $Tag['Tag Number'] ) + { + + case 37121: // Components configuration + + // Data contains 4 numerical values indicating component type + + $output_str = ""; + + // Cycle through each component + for ( $Num = 0; $Num < 4; $Num++ ) + { + // Construct first part of text string + $output_str .= "Component " . ( $Num + 1 ) . ": "; + + // Construct second part of text string via + // lookup using numerical value + + $value = ord( $Tag['Data']{$Num} ); + switch( $value ) + { + case 0: + $output_str .= "Does not exist\n"; + break; + case 1: + $output_str .= "Y (Luminance)\n"; + break; + case 2: + $output_str .= "Cb (Chroma minus Blue)\n"; + break; + case 3: + $output_str .= "Cr (Chroma minus Red)\n"; + break; + case 4: + $output_str .= "Red\n"; + break; + case 5: + $output_str .= "Green\n"; + break; + case 6: + $output_str .= "Blue\n"; + break; + default: + $output_str .= "Unknown value $value\n"; + }; + } + + // Return the completed string + + return $output_str; + break; + + + + case 41730: // Colour Filter Array Pattern + + // The first two characters are a SHORT for Horizontal repeat pixel unit - + $n_max = get_IFD_Data_Type( substr( $Tag['Data'], 0, 2 ), 3, $Tag['Byte Align'] ); + + // The next two characters are a SHORT for Vertical repeat pixel unit - + $m_max = get_IFD_Data_Type( substr( $Tag['Data'], 2, 2 ), 3, $Tag['Byte Align'] ); + + + // At least one camera type appears to have byte reversed values for N_Max and M_Max + // Check if they need reversing + if ( $n_max > 256 ) + { + $n_max = $n_max/256 + 256*($n_max%256); + } + + if ( $m_max > 256 ) + { + $m_max = $m_max/256 + 256*($m_max%256); + } + + + $output_str = ""; + + + // Cycle through all the elements in the resulting 2 dimensional array, + for( $m = 1; $m <= $m_max; $m++ ) + { + for( $n = 1; $n <= $n_max; $n++ ) + { + + // Append text from a lookup table according to + // the value read for this element + if( isset( $Tag['Data']{($n_max*($m-1)+$n+3)} ) ) { + switch ( ord($Tag['Data']{($n_max*($m-1)+$n+3)}) ) + { + case 0: + $output_str .= "RED "; + break; + case 1: + $output_str .= "GREEN "; + break; + case 2: + $output_str .= "BLUE "; + break; + case 3: + $output_str .= "CYAN "; + break; + case 4: + $output_str .= "MAGENTA "; + break; + case 5: + $output_str .= "YELLOW "; + break; + case 6: + $output_str .= "WHITE "; + break; + default: + $output_str .= "Unknown "; + break; + }; + } + }; + $output_str .= "\n"; + }; + + // Return the resulting string + return $output_str; + break; + + default: + return FALSE; + } + } + else + { + // Unknown IFD type, see if it is part of a makernote + return get_Makernote_Text_Value( $Tag, $Tag_Definitions_Name ); + } + + +} + +/****************************************************************************** +* End of Function: get_Tag_Text_Value +******************************************************************************/ + + + + + + + + +/****************************************************************************** +* +* Function: interpret_IFD +* +* Description: Generates html detailing the contents a single IFD. +* +* Parameters: IFD_array - the array containing an IFD +* filename - the name of the Image file being processed ( used +* by scripts which displays EXIF thumbnails) +* +* Returns: output_str - A string containing the HTML +* +******************************************************************************/ + +function interpret_IFD( $IFD_array, $filename ) +{ + // Create the output string with the table tag + $output_str = "\n"; + + // Create an extra output string to receive any supplementary html + // which cannot go inside the table + $extra_IFD_str = ""; + + // Check that the IFD array is valid + if ( ( $IFD_array === FALSE ) || ( $IFD_array === NULL ) ) + { + // the IFD array is NOT valid - exit + return ""; + } + + // Check if this is an EXIF IFD and if there is a makernote present + if ( ( $IFD_array['Tags Name'] === "EXIF" ) && + ( ! array_key_exists( 37500, $IFD_array ) ) ) + { + + // This is an EXIF IFD but NO makernote is present - Add a message to the output + $extra_IFD_str .= "

No Makernote Present

"; + } + + // Cycle through each tag in the IFD + + foreach( $IFD_array as $Tag_ID => $Exif_Tag ) + { + + // Ignore the non numeric elements - they aren't tags + if ( ! is_numeric ( $Tag_ID ) ) + { + // Skip Tags Name + } + // Check if the Tag has been decoded successfully + else if ( $Exif_Tag['Decoded'] == TRUE ) + { + // This tag has been successfully decoded + + // Table cells won't get drawn with nothing in them - + // Ensure that at least a non breaking space exists in them + + if ( trim($Exif_Tag['Text Value']) == "" ) + { + $Exif_Tag['Text Value'] = " "; + } + + // Check if the tag is a sub-IFD + if ( $Exif_Tag['Type'] == "SubIFD" ) + { + // This is a sub-IFD tag + // Add a sub-heading for the sub-IFD + $extra_IFD_str .= "

" . $Exif_Tag['Tag Name'] . " contents

"; + + // Cycle through each sub-IFD in the chain + foreach ( $Exif_Tag['Data'] as $subIFD ) + { + // Interpret this sub-IFD and add the html to the secondary output + $extra_IFD_str .= interpret_IFD( $subIFD, $filename ); + } + } + // Check if the tag is a makernote + else if ( $Exif_Tag['Type'] == "Maker Note" ) + { + // This is a Makernote Tag + // Add a sub-heading for the Makernote + $extra_IFD_str .= "

Maker Note Contents

"; + + // Interpret the Makernote and add the html to the secondary output + $extra_IFD_str .= Interpret_Makernote_to_HTML( $Exif_Tag, $filename ); + } + // Check if this is a IPTC/NAA Record within the EXIF IFD + else if ( $Exif_Tag['Type'] == "IPTC" ) + { + // This is a IPTC/NAA Record, interpret it and output to the secondary html + $extra_IFD_str .= "

Contains IPTC/NAA Embedded in EXIF

"; + $extra_IFD_str .=Interpret_IPTC_to_HTML( $Exif_Tag['Data'] ); + } + // Change: Check for embedded XMP as of version 1.11 + // Check if this is a XMP Record within the EXIF IFD + else if ( $Exif_Tag['Type'] == "XMP" ) + { + // This is a XMP Record, interpret it and output to the secondary html + $extra_IFD_str .= "

Contains XMP Embedded in EXIF

"; + $extra_IFD_str .= Interpret_XMP_to_HTML( $Exif_Tag['Data'] ); + } + // Change: Check for embedded IRB as of version 1.11 + // Check if this is a Photoshop IRB Record within the EXIF IFD + else if ( $Exif_Tag['Type'] == "IRB" ) + { + // This is a Photoshop IRB Record, interpret it and output to the secondary html + $extra_IFD_str .= "

Contains Photoshop IRB Embedded in EXIF

"; + $extra_IFD_str .= Interpret_IRB_to_HTML( $Exif_Tag['Data'], $filename ); + } + // Check if the tag is Numeric + else if ( $Exif_Tag['Type'] == "Numeric" ) + { + // Numeric Tag - Output text value as is. + $output_str .= "\n"; + } + else + { + // Other tag - Output text as preformatted + $output_str .= "\n"; + } + + } + else + { + // Tag has NOT been decoded successfully + // Hence it is either an unknown tag, or one which + // requires processing at the time of html construction + + // Table cells won't get drawn with nothing in them - + // Ensure that at least a non breaking space exists in them + + if ( trim($Exif_Tag['Text Value']) == "" ) + { + $Exif_Tag['Text Value'] = " "; + } + + // Check if this tag is the first IFD Thumbnail + if ( ( $IFD_array['Tags Name'] == "TIFF" ) && + ( $Tag_ID == 513 ) ) + { + // This is the first IFD thumbnail - Add html to the output + + // Change: as of version 1.11 - Changed to make thumbnail link portable across directories + // Build the path of the thumbnail script and its filename parameter to put in a url + $link_str = get_relative_path( dirname(__FILE__) . "/get_exif_thumb.php" , getcwd ( ) ); + $link_str .= "?filename="; + $link_str .= get_relative_path( $filename, dirname(__FILE__) ); + + // Add thumbnail link to html + $output_str .= "\n"; + } + // Check if this is the Makernote + else if ( $Exif_Tag['Type'] == "Maker Note" ) + { + // This is the makernote, but has not been decoded + // Add a message to the secondary output + $extra_IFD_str .= "

Makernote Coding Unknown

\n"; + } + else + { + // This is an Unknown Tag + + // Check if the user wants to hide unknown tags + if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] === FALSE ) + { + // User wants to display unknown tags + + // Check if the Data is an ascii string + if ( $Exif_Tag['Data Type'] == 2 ) + { + // This is a Ascii String field - add it preformatted to the output + $output_str .= "\n"; + } + else + { + // Not an ASCII string - add it as is to the output + $output_str .= "\n"; + } + } + } + } + } + + // Close the table in the output + $output_str .= "
" . $Exif_Tag['Tag Name'] . "" . $Exif_Tag['Text Value'] . "
" . $Exif_Tag['Tag Name'] . "
" . trim( $Exif_Tag['Text Value']) . "
" . $Exif_Tag['Tag Name'] . "
" . $Exif_Tag['Tag Name'] . "
" . trim( $Exif_Tag['Text Value'] ) . "
" . $Exif_Tag['Tag Name'] . "" . trim( $Exif_Tag['Text Value'] ) . "
\n"; + + // Add the secondary output at the end of the main output + $output_str .= "$extra_IFD_str\n"; + + // Return the resulting html + return $output_str; +} + +/****************************************************************************** +* End of Function: interpret_IFD +******************************************************************************/ + + + + + + + + + + + + + + + +/****************************************************************************** +* +* Function: get_IFD_Data_Type +* +* Description: Decodes an IFD field value from a binary data string, using +* information supplied about the data type and byte alignment of +* the stored data. +* This function should be used for all datatypes except ASCII strings +* +* Parameters: input_data - a binary data string containing the IFD value, +* must be exact length of the value +* data_type - a number representing the IFD datatype as per the +* TIFF 6.0 specification: +* 1 = Unsigned 8-bit Byte +* 2 = ASCII String +* 3 = Unsigned 16-bit Short +* 4 = Unsigned 32-bit Long +* 5 = Unsigned 2x32-bit Rational +* 6 = Signed 8-bit Byte +* 7 = Undefined +* 8 = Signed 16-bit Short +* 9 = Signed 32-bit Long +* 10 = Signed 2x32-bit Rational +* 11 = 32-bit Float +* 12 = 64-bit Double +* Byte_Align - Indicates the byte alignment of the data. +* MM = Motorola, MSB first, Big Endian +* II = Intel, LSB first, Little Endian +* +* Returns: output - the value of the data (string or numeric) +* +******************************************************************************/ + +function get_IFD_Data_Type( $input_data, $data_type, $Byte_Align ) +{ + $value = NULL; + if( !empty( $input_data ) ) { + // Check if this is a Unsigned Byte, Unsigned Short or Unsigned Long + if (( $data_type == 1 ) || ( $data_type == 3 ) || ( $data_type == 4 )) + { + // This is a Unsigned Byte, Unsigned Short or Unsigned Long + + // Check the byte alignment to see if the bytes need tp be reversed + if ( $Byte_Align == "II" ) + { + // This is in Intel format, reverse it + $input_data = strrev ( $input_data ); + } + + // Convert the binary string to a number and return it + return hexdec( bin2hex( $input_data ) ); + } + // Check if this is a ASCII string type + elseif ( $data_type == 2 ) + { + // Null terminated ASCII string(s) + // The input data may represent multiple strings, as the + // 'count' field represents the total bytes, not the number of strings + // Hence this should not be processed here, as it would have + // to return multiple values instead of a single value + + error_log( "Error - ASCII Strings should not be processed in get_IFD_Data_Type" ); + return "Error Should never get here"; //explode( "\x00", $input_data ); + } + // Check if this is a Unsigned rational type + elseif ( $data_type == 5 ) + { + // This is a Unsigned rational type + + // Check the byte alignment to see if the bytes need to be reversed + if ( $Byte_Align == "MM" ) + { + // Motorola MSB first byte aligment + // Unpack the Numerator and denominator and return them + return @unpack( 'NNumerator/NDenominator', $input_data ); + } + else + { + // Intel LSB first byte aligment + // Unpack the Numerator and denominator and return them + return @unpack( 'VNumerator/VDenominator', $input_data ); + } + } + // Check if this is a Signed Byte, Signed Short or Signed Long + elseif ( ( $data_type == 6 ) || ( $data_type == 8 ) || ( $data_type == 9 ) ) + { + // This is a Signed Byte, Signed Short or Signed Long + + // Check the byte alignment to see if the bytes need to be reversed + if ( $Byte_Align == "II" ) + { + //Intel format, reverse the bytes + $input_data = strrev ( $input_data ); + } + + // Convert the binary string to an Unsigned number + $value = hexdec( bin2hex( $input_data ) ); + + // Convert to signed number + + // Check if it is a Byte above 128 (i.e. a negative number) + if ( ( $data_type == 6 ) && ( $value > 128 ) ) + { + // number should be negative - make it negative + return $value - 256; + } + + // Check if it is a Short above 32767 (i.e. a negative number) + if ( ( $data_type == 8 ) && ( $value > 32767 ) ) + { + // number should be negative - make it negative + return $value - 65536; + } + + // Check if it is a Long above 2147483648 (i.e. a negative number) + if ( ( $data_type == 9 ) && ( $value > 2147483648 ) ) + { + // number should be negative - make it negative + return $value - 4294967296; + } + + // Return the signed number + return $value; + } + // Check if this is Undefined type + elseif ( $data_type == 7 ) + { + // Custom Data - Do nothing + return $input_data; + } + // Check if this is a Signed Rational type + elseif ( $data_type == 10 ) + { + // This is a Signed Rational type + + // Signed Long not available with endian in unpack , use unsigned and convert + + // Check the byte alignment to see if the bytes need to be reversed + if ( $Byte_Align == "MM" ) + { + // Motorola MSB first byte aligment + // Unpack the Numerator and denominator + $value = @unpack( 'NNumerator/NDenominator', $input_data ); + } + else + { + // Intel LSB first byte aligment + // Unpack the Numerator and denominator + $value = @unpack( 'VNumerator/VDenominator', $input_data ); + } + + // Convert the numerator to a signed number + // Check if it is above 2147483648 (i.e. a negative number) + if ( $value['Numerator'] > 2147483648 ) + { + // number is negative + $value['Numerator'] -= 4294967296; + } + + // Convert the denominator to a signed number + // Check if it is above 2147483648 (i.e. a negative number) + if ( $value['Denominator'] > 2147483648 ) + { + // number is negative + $value['Denominator'] -= 4294967296; + } + + // Return the Signed Rational value + return $value; + } + // Check if this is a Float type + elseif ( $data_type == 11 ) + { + // IEEE 754 Float + // TODO - EXIF - IFD datatype Float not implemented yet + return "FLOAT NOT IMPLEMENTED YET"; + } + // Check if this is a Double type + elseif ( $data_type == 12 ) + { + // IEEE 754 Double + // TODO - EXIF - IFD datatype Double not implemented yet + return "DOUBLE NOT IMPLEMENTED YET"; + } + else + { + // Error - Invalid Datatype + return "Invalid Datatype $data_type"; + + } + } else { + return "Invalid Datatype $data_type"; + } + +} + +/****************************************************************************** +* End of Function: get_IFD_Data_Type +******************************************************************************/ + + + + + + +/****************************************************************************** +* +* Function: put_IFD_Data_Type +* +* Description: Encodes an IFD field from a value to a binary data string, using +* information supplied about the data type and byte alignment of +* the stored data. +* +* Parameters: input_data - an IFD data value, numeric or string +* data_type - a number representing the IFD datatype as per the +* TIFF 6.0 specification: +* 1 = Unsigned 8-bit Byte +* 2 = ASCII String +* 3 = Unsigned 16-bit Short +* 4 = Unsigned 32-bit Long +* 5 = Unsigned 2x32-bit Rational +* 6 = Signed 8-bit Byte +* 7 = Undefined +* 8 = Signed 16-bit Short +* 9 = Signed 32-bit Long +* 10 = Signed 2x32-bit Rational +* 11 = 32-bit Float +* 12 = 64-bit Double +* Byte_Align - Indicates the byte alignment of the data. +* MM = Motorola, MSB first, Big Endian +* II = Intel, LSB first, Little Endian +* +* Returns: output - the packed binary string of the data +* +******************************************************************************/ + +function put_IFD_Data_Type( $input_data, $data_type, $Byte_Align ) +{ + // Process according to the datatype + switch ( $data_type ) + { + case 1: // Unsigned Byte - return character as is + return chr($input_data); + break; + + case 2: // ASCII String + // Return the string with terminating null + return $input_data . "\x00"; + break; + + case 3: // Unsigned Short + // Check byte alignment + if ( $Byte_Align == "II" ) + { + // Intel/Little Endian - pack the short and return + return pack( "v", $input_data ); + } + else + { + // Motorola/Big Endian - pack the short and return + return pack( "n", $input_data ); + } + break; + + case 4: // Unsigned Long + // Check byte alignment + if ( $Byte_Align == "II" ) + { + // Intel/Little Endian - pack the long and return + return pack( "V", $input_data ); + } + else + { + // Motorola/Big Endian - pack the long and return + return pack( "N", $input_data ); + } + break; + + case 5: // Unsigned Rational + // Check byte alignment + if ( $Byte_Align == "II" ) + { + // Intel/Little Endian - pack the two longs and return + return pack( "VV", $input_data['Numerator'], $input_data['Denominator'] ); + } + else + { + // Motorola/Big Endian - pack the two longs and return + return pack( "NN", $input_data['Numerator'], $input_data['Denominator'] ); + } + break; + + case 6: // Signed Byte + // Check if number is negative + if ( $input_data < 0 ) + { + // Number is negative - return signed character + return chr( $input_data + 256 ); + } + else + { + // Number is positive - return character + return chr( $input_data ); + } + break; + + case 7: // Unknown - return as is + return $input_data; + break; + + case 8: // Signed Short + // Check if number is negative + if ( $input_data < 0 ) + { + // Number is negative - make signed value + $input_data = $input_data + 65536; + } + // Check byte alignment + if ( $Byte_Align == "II" ) + { + // Intel/Little Endian - pack the short and return + return pack( "v", $input_data ); + } + else + { + // Motorola/Big Endian - pack the short and return + return pack( "n", $input_data ); + } + break; + + case 9: // Signed Long + // Check if number is negative + if ( $input_data < 0 ) + { + // Number is negative - make signed value + $input_data = $input_data + 4294967296; + } + // Check byte alignment + if ( $Byte_Align == "II" ) + { + // Intel/Little Endian - pack the long and return + return pack( "v", $input_data ); + } + else + { + // Motorola/Big Endian - pack the long and return + return pack( "n", $input_data ); + } + break; + + case 10: // Signed Rational + // Check if numerator is negative + if ( $input_data['Numerator'] < 0 ) + { + // Number is numerator - make signed value + $input_data['Numerator'] = $input_data['Numerator'] + 4294967296; + } + // Check if denominator is negative + if ( $input_data['Denominator'] < 0 ) + { + // Number is denominator - make signed value + $input_data['Denominator'] = $input_data['Denominator'] + 4294967296; + } + // Check byte alignment + if ( $Byte_Align == "II" ) + { + // Intel/Little Endian - pack the two longs and return + return pack( "VV", $input_data['Numerator'], $input_data['Denominator'] ); + } + else + { + // Motorola/Big Endian - pack the two longs and return + return pack( "NN", $input_data['Numerator'], $input_data['Denominator'] ); + } + break; + + case 11: // Float + // IEEE 754 Float + // TODO - EXIF - IFD datatype Float not implemented yet + return "FLOAT NOT IMPLEMENTED YET"; + break; + + case 12: // Double + // IEEE 754 Double + // TODO - EXIF - IFD datatype Double not implemented yet + return "DOUBLE NOT IMPLEMENTED YET"; + break; + + default: + // Error - Invalid Datatype + return "Invalid Datatype $data_type"; + break; + + } + + // Shouldn't get here + return FALSE; +} + +/****************************************************************************** +* End of Function: put_IFD_Data_Type +******************************************************************************/ + + + + + +/****************************************************************************** +* +* Function: get_IFD_value_as_text +* +* Description: Decodes an IFD field value from a binary data string, using +* information supplied about the data type and byte alignment of +* the stored data. +* This function should be used for all datatypes except ASCII strings +* +* Parameters: input_data - a binary data string containing the IFD value, +* must be exact length of the value +* data_type - a number representing the IFD datatype as per the +* TIFF 6.0 specification: +* 1 = Unsigned 8-bit Byte +* 2 = ASCII String +* 3 = Unsigned 16-bit Short +* 4 = Unsigned 32-bit Long +* 5 = Unsigned 2x32-bit Rational +* 6 = Signed 8-bit Byte +* 7 = Undefined +* 8 = Signed 16-bit Short +* 9 = Signed 32-bit Long +* 10 = Signed 2x32-bit Rational +* 11 = 32-bit Float +* 12 = 64-bit Double +* Byte_Align - Indicates the byte alignment of the data. +* MM = Motorola, MSB first, Big Endian +* II = Intel, LSB first, Little Endian +* +* Returns: output - the value of the data (string or numeric) +* +******************************************************************************/ + +function get_IFD_value_as_text( $Exif_Tag ) +{ + // Create a string to receive the output text + $output_str = ""; + +if( !empty( $Exif_Tag['Data Type'] ) ) { + // Select Processing method according to the datatype + switch ($Exif_Tag['Data Type']) + { + case 1 : // Unsigned Byte + case 3 : // Unsigned Short + case 4 : // Unsigned Long + case 6 : // Signed Byte + case 8 : // Signed Short + case 9 : // Signed Long + + // Cycle through each of the values for this tag + foreach ( $Exif_Tag['Data'] as $val ) + { + // Check that this isn't the first value, + if ( $output_str != "" ) + { + // This isn't the first value, Add a Comma and Newline to the output + $output_str .= ",\n"; + } + // Add the Value to the output + $output_str .= $val; + } + break; + + case 2 : // ASCII + // Append all the strings together, separated by Newlines + $output_str .= implode ( "\n", $Exif_Tag['Data']); + break; + + case 5 : // Unsigned Rational + case 10: // Signed Rational + + // Cycle through each of the values for this tag + foreach ( $Exif_Tag['Data'] as $val ) + { + // Check that this isn't the first value, + if ( $output_str != "" ) + { + // This isn't the first value, Add a Comma and Newline to the output + $output_str .= ",\n"; + } + + // Add the Full Value to the output + $output_str .= $val['Numerator'] ."/" . $val['Denominator']; + + // Check if division by zero might be a problem + if ( $val['Denominator'] != 0 ) + { + // Denominator is not zero, Add the Decimal Value to the output text + $output_str .= " (" . ($val['Numerator'] / $val['Denominator']) . ")"; + } + } + break; + + case 11: // Float + case 12: // Double + // TODO - EXIF - IFD datatype Double and Float not implemented yet + $output_str .= "Float and Double not implemented yet"; + break; + + case 7 : // Undefined + // Unless the User has asked to see the raw binary data, this + // type should not be displayed + + // Check if the user has requested to see the binary data in hex + if ( $GLOBALS['SHOW_BINARY_DATA_HEX'] == TRUE) + { + // User has requested to see the binary data in hex + // Add the value in hex + $output_str .= "( " . strlen( $Exif_Tag['Data'] ) . " bytes of binary data ): " . bin2hex( $Exif_Tag['Data'] ) ; + } + // Check if the user has requested to see the binary data as is + else if ( $GLOBALS['SHOW_BINARY_DATA_TEXT'] == TRUE) + { + // User has requested to see the binary data as is + // Add the value as is + $output_str .= "( " . strlen( $Exif_Tag['Data'] ) . " bytes of binary data ): " . $Exif_Tag['Data'] ; + } + else + { + // User has NOT requested to see binary data, + // Add a message indicating the number of bytes to the output + $output_str .= "( " . strlen( $Exif_Tag['Data'] ) . " bytes of binary data ) " ; + } + break; + + default : + // Error - Unknown IFD datatype + $output_str .= "Error - Exif tag data type (" . $Exif_Tag['Data Type'] .") is invalid"; + break; + } +} + + // Return the resulting text string + return $output_str; +} + +/****************************************************************************** +* End of Function: get_IFD_value_as_text +******************************************************************************/ + + + + +/****************************************************************************** +* Global Variable: IFD_Data_Sizes +* +* Contents: The sizes (in bytes) of each EXIF IFD Datatype, indexed by +* their datatype number +* +******************************************************************************/ + +$GLOBALS['IFD_Data_Sizes'] = array( 1 => 1, // Unsigned Byte + 2 => 1, // ASCII String + 3 => 2, // Unsigned Short + 4 => 4, // Unsigned Long + 5 => 8, // Unsigned Rational + 6 => 1, // Signed Byte + 7 => 1, // Undefined + 8 => 2, // Signed Short + 9 => 4, // Signed Long + 10 => 8, // Signed Rational + 11 => 4, // Float + 12 => 8 ); // Double + +/****************************************************************************** +* End of Global Variable: IFD_Data_Sizes +******************************************************************************/ + + + + + + +?> diff --git a/includes/jpeg_metadata_tk/EXIF_Makernote.php b/includes/jpeg_metadata_tk/EXIF_Makernote.php new file mode 100644 index 0000000..ceb82f4 --- /dev/null +++ b/includes/jpeg_metadata_tk/EXIF_Makernote.php @@ -0,0 +1,334 @@ + 1.11 : changed makernotes directory definition to allow +* the toolkit to be portable across directories +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* +* License: This file is part of the PHP JPEG Metadata Toolkit. +* +* The PHP JPEG Metadata Toolkit is free software; you can +* redistribute it and/or modify it under the terms of the +* GNU General Public License as published by the Free Software +* Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* The PHP JPEG Metadata Toolkit is distributed in the hope +* that it will be useful, but WITHOUT ANY WARRANTY; without +* even the implied warranty of MERCHANTABILITY or FITNESS +* FOR A PARTICULAR PURPOSE. See the GNU General Public License +* for more details. +* +* You should have received a copy of the GNU General Public +* License along with the PHP JPEG Metadata Toolkit; if not, +* write to the Free Software Foundation, Inc., 59 Temple +* Place, Suite 330, Boston, MA 02111-1307 USA +* +* If you require a different license for commercial or other +* purposes, please contact the author: evan@ozhiker.com +* +******************************************************************************/ + + +// Create the Makernote Parser and Interpreter Function Array + +$GLOBALS['Makernote_Function_Array'] = array( "Read_Makernote_Tag" => array( ), + "get_Makernote_Text_Value" => array( ), + "Interpret_Makernote_to_HTML" => array( ) ); + + +// Include the Main TIFF and EXIF Tags array + +include_once 'EXIF.php'; + + + +/****************************************************************************** +* +* Include the Makernote Scripts +* +******************************************************************************/ + +// Set the Makernotes Directory + +$dir = dirname(__FILE__) . "/Makernotes/"; // Change: as of version 1.11 - to allow directory portability + +// Open the directory +$dir_hnd = @opendir ( $dir ); + +// Cycle through each of the files in the Makernotes directory + +while ( ( $file = readdir( $dir_hnd ) ) !== false ) +{ + // Check if the current item is a file + if ( is_file ( $dir . $file ) ) + { + // Item is a file, break it into it's parts + $path_parts = pathinfo( $dir . $file ); + + // Check if the extension is php + if ( $path_parts["extension"] == "php" ) + { + // This is a php script - include it + include_once ($dir . $file) ; + } + } +} +// close the directory +closedir( $dir_hnd ); + + + + + + + + + + +/****************************************************************************** +* +* Function: Read_Makernote_Tag +* +* Description: Attempts to decodes the Makernote tag supplied, returning the +* new tag with the decoded information attached. +* +* Parameters: Makernote_Tag - the element of an EXIF array containing the +* makernote, as returned from get_EXIF_JPEG +* EXIF_Array - the entire EXIF array containing the +* makernote, as returned from get_EXIF_JPEG, in +* case more information is required for decoding +* filehnd - an open file handle for the file containing the +* makernote - does not have to be positioned at the +* start of the makernote +* +* +* Returns: Makernote_Tag - the Makernote_Tag from the parameters, but +* modified to contain the decoded information +* +******************************************************************************/ + +function Read_Makernote_Tag( $Makernote_Tag, $EXIF_Array, $filehnd ) +{ + + // Check if the Makernote is present but empty - this sometimes happens + if ( ( strlen( $Makernote_Tag['Data'] ) === 0 ) || + ( is_string( $Makernote_Tag['Data'] ) && $Makernote_Tag['Data'] === str_repeat ( "\x00", strlen( $Makernote_Tag['Data'] )) ) ) + { + // Modify the makernote to display that it is empty + $Makernote_Tag['Decoded Data'] = "Empty"; + $Makernote_Tag['Makernote Type'] = "Empty"; + $Makernote_Tag['Makernote Tags'] = "Empty"; + $Makernote_Tag['Decoded'] = TRUE; + + // Return the new makernote + return $Makernote_Tag; + } + + // Check if the Make Field exists in the TIFF IFD + if ( array_key_exists ( 271, $EXIF_Array[0] ) ) + { + // A Make tag exists in IFD0, collapse multiple strings (if any), and save result + $Make_Field = implode ( "\n", $EXIF_Array[0][271]['Data']); + } + else + { + // No Make field found + $Make_Field = ""; + } + + // Cycle through each of the "Read_Makernote_Tag" functions + + foreach( $GLOBALS['Makernote_Function_Array']['Read_Makernote_Tag'] as $func ) + { + // Run the current function, and save the result + $New_Makernote_Tag = $func( $Makernote_Tag, $EXIF_Array, $filehnd, $Make_Field ); + + // Check if a valid result was returned + if ( $New_Makernote_Tag !== FALSE ) + { + // A valid result was returned - stop cycling + break; + } + } + + // Check if a valid result was returned + if ( $New_Makernote_Tag === false ) + { + // A valid result was NOT returned - construct a makernote tag representing this + $New_Makernote_Tag = $Makernote_Tag; + $New_Makernote_Tag['Decoded'] = FALSE; + $New_Makernote_Tag['Makernote Type'] = "Unknown Makernote"; + } + + // Return the new makernote tag + return $New_Makernote_Tag; + +} + +/****************************************************************************** +* End of Function: Read_Makernote_Tag +******************************************************************************/ + + + + + + + + + +/****************************************************************************** +* +* Function: get_Makernote_Text_Value +* +* Description: Attempts to provide a text value for any makernote tag marked +* as type special. Returns false no handler could be found to +* process the tag +* +* Parameters: Exif_Tag - the element of an the Makernote array containing the +* tag in question, as returned from Read_Makernote_Tag +* Tag_Definitions_Name - The name of the Tag Definitions group +* within the global array IFD_Tag_Definitions +* +* +* Returns: output - the text value for the tag +* FALSE - If no handler could be found to process this tag, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Makernote_Text_Value( $Tag, $Tag_Definitions_Name ) +{ + + // Cycle through each of the "get_Makernote_Text_Value" functions + + foreach( $GLOBALS['Makernote_Function_Array']['get_Makernote_Text_Value'] as $func ) + { + // Run the current function, and save the result + $Text_Val = $func( $Tag, $Tag_Definitions_Name ); + + // Check if a valid result was returned + if ( $Text_Val !== FALSE ) + { + // valid result - return it + return $Text_Val; + } + } + + // No Special tag handler found for this tag - return false + return FALSE; + +} + + +/****************************************************************************** +* End of Function: get_Makernote_Text_Value +******************************************************************************/ + + + + + + + + +/****************************************************************************** +* +* Function: Interpret_Makernote_to_HTML +* +* Description: Attempts to interpret a makernote into html. +* +* Parameters: Makernote_Tag - the element of an EXIF array containing the +* makernote, as returned from get_EXIF_JPEG +* filename - the name of the JPEG file being processed ( used +* by scripts which display embedded thumbnails) +* +* +* Returns: output - the html representing the makernote +* +******************************************************************************/ + +function Interpret_Makernote_to_HTML( $Makernote_tag, $filename ) +{ + + // Create a string to receive the HTML + $output_str = ""; + + // Check if the makernote tag is valid + if ( $Makernote_tag === FALSE ) + { + // No makernote info - return + return $output_str; + } + + + // Check if the makernote has been marked as unknown + if ( $Makernote_tag['Makernote Type'] == "Unknown Makernote" ) + { + // Makernote is unknown - return message + $output_str .= "

Unknown Makernote Coding

\n"; + return $output_str; + } + else + { + // Makernote is known - add a heading to the output + $output_str .= "

Makernote Coding: " . $Makernote_tag['Makernote Type'] . "

\n"; + } + + // Check if this is an empty makernote + if ( $Makernote_tag['Makernote Type'] == "Empty" ) + { + // It is empty - don't try to interpret + return $output_str; + } + + // Cycle through each of the "Interpret_Makernote_to_HTML" functions + + foreach( $GLOBALS['Makernote_Function_Array']['Interpret_Makernote_to_HTML'] as $func ) + { + // Run the current function, and save the result + $html_text = $func( $Makernote_tag, $filename ); + + // Check if a valid result was returned + if ( $html_text !== FALSE ) + { + // valid result - return it + return $output_str . $html_text; + } + } + + // No Interpreter function handled the makernote - return a message + + $output_str .= "

Could not Decode Makernote, it may be corrupted or empty

\n"; + + return $output_str; + + +} + +/****************************************************************************** +* End of Function: Interpret_Makernote_to_HTML +******************************************************************************/ + + + +?> diff --git a/includes/jpeg_metadata_tk/EXIF_Tags.php b/includes/jpeg_metadata_tk/EXIF_Tags.php new file mode 100644 index 0000000..cb36f18 --- /dev/null +++ b/includes/jpeg_metadata_tk/EXIF_Tags.php @@ -0,0 +1,915 @@ + 1.11 : Added TIFF compression types ZIP, LZW and JPEG +* Added embedded XMP tag +* Added embedded Photoshop IRB tag +* Fixed GPS tags after testing +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* +* License: This file is part of the PHP JPEG Metadata Toolkit. +* +* The PHP JPEG Metadata Toolkit is free software; you can +* redistribute it and/or modify it under the terms of the +* GNU General Public License as published by the Free Software +* Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* The PHP JPEG Metadata Toolkit is distributed in the hope +* that it will be useful, but WITHOUT ANY WARRANTY; without +* even the implied warranty of MERCHANTABILITY or FITNESS +* FOR A PARTICULAR PURPOSE. See the GNU General Public License +* for more details. +* +* You should have received a copy of the GNU General Public +* License along with the PHP JPEG Metadata Toolkit; if not, +* write to the Free Software Foundation, Inc., 59 Temple +* Place, Suite 330, Boston, MA 02111-1307 USA +* +* If you require a different license for commercial or other +* purposes, please contact the author: evan@ozhiker.com +* +******************************************************************************/ + + + + + + +/****************************************************************************** +* Global Variable: IFD_Tag_Definitions +* +* Contents: This array defines the fields for the TIFF, EXIF, Interoperability, +* GPS, Meta, Kodak Special Effects and Kodak Borders IFD's. +* It is indexed by the IFD Type, then the Tag number +* +******************************************************************************/ + +$GLOBALS[ "IFD_Tag_Definitions" ] = array( + + +/*****************************************************************************/ +/* */ +/* TIFF Tags */ +/* */ +/*****************************************************************************/ + + +"TIFF" => array( + + +256 => array( 'Name' => "Image Width", + 'Description' => "Width of image in pixels (number of columns)", + 'Type' => "Numeric", + 'Units' => "pixels" ), + +257 => array( 'Name' => "Image Length", + 'Description' => "Height of image in pixels (number of rows)", + 'Type' => "Numeric", + 'Units' => "pixels" ), + +258 => array( 'Name' => "Bits Per Sample", + 'Description' => "Number of bits recorded per sample (a sample is usually one colour (Red, Green or Blue) of one pixel)", + 'Type' => "Numeric", + 'Units' => "bits ( for each colour component )" ), + + +259 => array( 'Name' => "Compression", + 'Description' => "Specifies what type of compression is used 1 = uncompressed, 6 = JPEG compression (thumbnails only), Other = reserved", + 'Type' => "Lookup", + 1 => "Uncompressed", + 5 => "LZW Compression", + 6 => "Thumbnail compressed with JPEG compression", + 7 => "JPEG Compression", + 8 => "ZIP Compression" ), // Change: Added TIFF compression types as of version 1.11 + +262 => array( 'Name' => "Photometric Interpretation", + 'Description' => "Specifies Pixel Composition - 0 or 1 = monochrome, 2 = RGB, 3 = Palatte Colour, 4 = Transparency Mask, 6 = YCbCr", + 'Type' => "Lookup", + 2 => "RGB (Red Green Blue)", + 6 => "YCbCr (Luminance, Chroma minus Blue, and Chroma minus Red)" ), + +274 => array( 'Name' => "Orientation", + 'Description' => "Specifies the orientation of the image.\n +1 = Row 0 top, column 0 left\n +2 = Row 0 top, column 0 right\n +3 = Row 0 bottom, column 0 right\n +4 = Row 0 bottom, column 0 left\n +5 = Row 0 left, column 0 top\n +6 = Row 0 right, column 0 top\n +7 = Row 0 right, column 0 bottom\n +8 = Row 0 left, column 0 bottom", + 'Type' => "Lookup", + 1 => "No Rotation, No Flip \n(Row 0 is at the visual top of the image,\n and column 0 is the visual left-hand side)", + 2 => "No Rotation, Flipped Horizontally \n(Row 0 is at the visual top of the image,\n and column 0 is the visual right-hand side)", + 3 => "Rotated 180 degrees, No Flip \n(Row 0 is at the visual bottom of the image,\n and column 0 is the visual right-hand side)", + 4 => "No Rotation, Flipped Vertically \n(Row 0 is at the visual bottom of the image,\n and column 0 is the visual left-hand side)", + 5 => "Flipped Horizontally, Rotated 90 degrees counter clockwise \n(Row 0 is at the visual left-hand side of of the image,\n and column 0 is the visual top)", + 6 => "No Flip, Rotated 90 degrees clockwise \n(Row 0 is at the visual right-hand side of of the image,\n and column 0 is the visual top)", + 7 => "Flipped Horizontally, Rotated 90 degrees clockwise \n(Row 0 is at the visual right-hand side of of the image,\n and column 0 is the visual bottom)", + 8 => "No Flip, Rotated 90 degrees counter clockwise \n(Row 0 is at the visual left-hand side of of the image,\n and column 0 is the visual bottom)" ), +277 => array( 'Name' => "Samples Per Pixel", + 'Description' => "Number of recorded samples (colours) per pixel - usually 1 for B&W, grayscale, and palette-colour, usually 3 for RGB and YCbCr", + 'Type' => "Numeric", + 'Units' => "Components (colours)" ), + +284 => array( 'Name' => "Planar Configuration", + 'Description' => "Specifies whether pixel components are recorded in chunky or planar format - 1 = Chunky, 2 = Planar", + 'Type' => "Lookup", + 1 => "Chunky Format", + 2 => "Planar Format" ), + +530 => array( 'Name' => "YCbCr Sub-Sampling", + 'Description' => "Specifies ratio of chrominance to luminance components - [2, 1] = YCbCr4:2:2, [2, 2] = YCbCr4:2:0", + 'Type' => "Special" ), + + +531 => array( 'Name' => "YCbCr Positioning", + 'Description' => "Specifies location of chrominance and luminance components - 1 = centered, 2 = co-sited", + 'Type' => "Lookup", + 1 => "Chrominance components Centred in relation to luminance components", + 2 => "Chrominance and luminance components Co-Sited" ), + + +282 => array( 'Name' => "X Resolution", + 'Description' => "Number of columns (pixels) per \'ResolutionUnit\'", + 'Type' => "Numeric", + 'Units'=> "pixels per 'Resolution Unit' " ), + +283 => array( 'Name' => "Y Resolution", + 'Description' => "Number of rows (pixels) per \'ResolutionUnit\'", + 'Type' => "Numeric", + 'Units'=> "pixels per 'Resolution Unit' " ), + +296 => array( 'Name' => "Resolution Unit", + 'Description' => "Units for measuring XResolution and YResolution - 1 = No units, 2 = Inches, 3 = Centimetres", + 'Type' => "Lookup", + 2 => "Inches", + 3 => "Centimetres" ), + +273 => array( 'Name' => "Strip Offsets", + 'Type' => "Numeric", + 'Units'=> "bytes offset" ), + +278 => array( 'Name' => "Rows Per Strip", + 'Type' => "Numeric", + 'Units'=> "rows" ), + +279 => array( 'Name' => "Strip Byte Counts", + 'Type' => "Numeric", + 'Units'=> "bytes" ), + +513 => array( 'Name' => "Exif Thumbnail (JPEG Interchange Format)", + 'Type' => "Special" ), + +514 => array( 'Name' => "Exif Thumbnail Length (JPEG Interchange Format Length)", + 'Type' => "Numeric", + 'Units'=> "bytes" ), + +301 => array( 'Name' => "Transfer Function", + 'Type' => "Numeric", + 'Units'=> "" ), + +318 => array( 'Name' => "White Point Chromaticity", + 'Type' => "Numeric", + 'Units'=> "(x,y coordinates on a 1931 CIE xy chromaticity diagram)" ), + +319 => array( 'Name' => "Primary Chromaticities", + 'Type' => "Numeric", + 'Units'=> "(Red x,y, Green x,y, Blue x,y coordinates on a 1931 CIE xy chromaticity diagram)" ), + +529 => array( 'Name' => "YCbCr Coefficients", + 'Description' => "Transform Coefficients for transformation from RGB to YCbCr", + 'Type' => "Numeric", + 'Units'=> "(LumaRed, LumaGreen, LumaBlue [proportions of red, green, and blue in luminance])" ), + +532 => array( 'Name' => "Reference Black point and White point", + 'Type' => "Numeric", + 'Units'=> "(R or Y White Headroom, R or Y Black Footroom, G or Cb White Headroom, G or Cb Black Footroom, B or Cr White Headroom, B or Cr Black Footroom)" ), + +306 => array( 'Name' => "Date and Time", + 'Type' => "Numeric", + 'Units'=> " (Format: YYYY:MM:DD HH:mm:SS)" ), + +270 => array( 'Name' => "Image Description", + 'Type' => "String" ), + +271 => array( 'Name' => "Make (Manufacturer)", + 'Type' => "String" ), + +272 => array( 'Name' => "Model", + 'Type' => "String" ), + +305 => array( 'Name' => "Software or Firmware", + 'Type' => "String" ), + +315 => array( 'Name' => "Artist Name", + 'Type' => "String" ), + +700 => array( 'Name' => "Embedded XMP Block", // Change: Added embedded XMP as of version 1.11 + 'Type' => "XMP" ), + +33432 => array( 'Name' => "Copyright Information", + 'Type' => "String" ), + +34665 => array( 'Name' => "EXIF Image File Directory (IFD)", + 'Type' => "SubIFD", + 'Tags Name' => "EXIF" ), + +33723 => array( 'Name' => "IPTC Records", + 'Type' => "IPTC" ), + +34377 => array( 'Name' => "Embedded Photoshop IRB", // Change: Added embedded IRB as of version 1.11 + 'Type' => "IRB" ), + +34853 => array( 'Name' => "GPS Info Image File Directory (IFD)", // Change: Moved GPS IFD tag to correct location as of version 1.11 + 'Type' => "SubIFD", + 'Tags Name' => "GPS" ), + +50341 => array( 'Name' => "Print Image Matching Info", + 'Type' => "PIM" ), + +), + + +/*****************************************************************************/ +/* */ +/* EXIF Tags */ +/* */ +/*****************************************************************************/ + + +'EXIF' => array ( + +// Exif IFD +36864 => array( 'Name' => "Exif Version", + 'Type' => "String" ), + +40965 => array( 'Name' => "Interoperability Image File Directory (IFD)", + 'Type' => "SubIFD", + 'Tags Name' => "Interoperability" ), + +// Change: removed GPS IFD tag from here as it was incorrect location - as of version 1.11 + +40960 => array( 'Name' => "FlashPix Version", + 'Type' => "String" ), + +40961 => array( 'Name' => "Colour Space", + 'Type' => "Lookup", + 1 => "sRGB", + 0xFFFF => "Uncalibrated" ), + +40962 => array( 'Name' => "Pixel X Dimension", + 'Type' => "Numeric", + 'Units'=> "pixels" ), + +40963 => array( 'Name' => "Pixel Y Dimension", + 'Type' => "Numeric", + 'Units' => "pixels" ), + +37121 => array( 'Name' => "Components Configuration", + 'Type' => "Special" ), + +37122 => array( 'Name' => "Compressed Bits Per Pixel", + 'Type' => "Numeric", + 'Units' => "bits" ), + +37500 => array( 'Name' => "Maker Note", + 'Type' => "Maker Note" ), + +37510 => array( 'Name' => "User Comment", + 'Type' => "Character Coded String" ), + +40964 => array( 'Name' => "Related Sound File", + 'Type' => "String" ), + +36867 => array( 'Name' => "Date and Time of Original", + 'Type' => "String", + 'Units' => " (Format: YYYY:MM:DD HH:mm:SS)" ), + +36868 => array( 'Name' => "Date and Time when Digitized", + 'Type' => "String", + 'Units' => " (Format: YYYY:MM:DD HH:mm:SS)" ), + +37520 => array( 'Name' => "Sub Second Time", + 'Type' => "String" ), + +37521 => array( 'Name' => "Sub Second Time of Original", + 'Type' => "String" ), + +37522 => array( 'Name' => "Sub Second Time when Digitized", + 'Type' => "String" ), + +33434 => array( 'Name' => "Exposure Time", + 'Type' => "Numeric", + 'Units' => "seconds" ), + +37377 => array( 'Name' => "APEX Shutter Speed Value (Tv)", + 'Type' => "Numeric" ), + +37378 => array( 'Name' => "APEX Aperture Value (Av)", + 'Type' => "Numeric" ), + +37379 => array( 'Name' => "APEX Brightness Value (Bv)", + 'Type' => "Numeric" ), + +37380 => array( 'Name' => "APEX Exposure Bias Value (Exposure Compensation)", + 'Type' => "Numeric", + 'Units' => "EV" ), + +42240 => array( 'Name' => "Gamma Compensation for Playback", + 'Type' => "Numeric" ), + + +37381 => array( 'Name' => "APEX Maximum Aperture Value", + 'Type' => "Numeric" ), + +37382 => array( 'Name' => "Subject Distance", + 'Type' => "Numeric", + 'Units' => "metres" ), + +37383 => array( 'Name' => "Metering Mode", + 'Type' => "Lookup", + 0 => "Unknown", + 1 => "Average", + 2 => "Center Weighted Average", + 3 => "Spot", + 4 => "Multi Spot", + 5 => "Pattern", + 6 => "Partial", + 255 => "Other" ), + +37384 => array( 'Name' => "Light Source", + 'Type' => "Lookup", + 0 => "Unknown", + 1 => "Daylight", + 2 => "Fluorescent", + 3 => "Tungsten (incandescent light)", + 4 => "Flash", + 9 => "Fine weather", + 10 => "Cloudy weather", + 11 => "Shade", + 12 => "Daylight fluorescent (D 5700 – 7100K)", + 13 => "Day white fluorescent (N 4600 – 5400K)", + 14 => "Cool white fluorescent (W 3900 – 4500K)", + 15 => "White fluorescent (WW 3200 – 3700K)", + 17 => "Standard light A", + 18 => "Standard light B", + 19 => "Standard light C", + 20 => "D55", + 21 => "D65", + 22 => "D75", + 23 => "D50", + 24 => "ISO studio tungsten", + 255 => "Other" ), + +37385 => array( 'Name' => "Flash", + 'Type' => "Lookup", + 0 => "Flash did not fire", + 1 => "Flash fired", + 5 => "Strobe return light not detected", + 7 => "Strobe return light detected", + 9 => "Flash fired, compulsory flash mode", + 13 => "Flash fired, compulsory flash mode, return light not detected", + 15 => "Flash fired, compulsory flash mode, return light detected", + 16 => "Flash did not fire, compulsory flash suppression mode", + 24 => "Flash did not fire, auto mode", + 25 => "Flash fired, auto mode", + 29 => "Flash fired, auto mode, return light not detected", + 31 => "Flash fired, auto mode, return light detected", + 32 => "No flash function", + 65 => "Flash fired, red-eye reduction mode", + 69 => "Flash fired, red-eye reduction mode, return light not detected", + 71 => "Flash fired, red-eye reduction mode, return light detected", + 73 => "Flash fired, compulsory flash mode, red-eye reduction mode", + 77 => "Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected", + 79 => "Flash fired, compulsory flash mode, red-eye reduction mode, return light detected", + 89 => "Flash fired, auto mode, red-eye reduction mode", + 93 => "Flash fired, auto mode, return light not detected, red-eye reduction mode", + 95 => "Flash fired, auto mode, return light detected, red-eye reduction mode" ), + +37386 => array( 'Name' => "FocalLength", + 'Type' => "Numeric", + 'Units' => "mm" ), + +37396 => array( 'Name' => "Subject Area", + 'Type' => "Numeric", + 'Units' => "( Two Values: x,y coordinates, Three Values: x,y coordinates, diameter, Four Values: center x,y coordinates, width, height)" ), + +33437 => array( 'Name' => "Aperture F Number", + 'Type' => "Numeric" ), + +34850 => array( 'Name' => "Exposure Program", + 'Type' => "Lookup", + 0 => "Not defined", + 1 => "Manual", + 2 => "Normal program", + 3 => "Aperture priority", + 4 => "Shutter priority", + 5 => "Creative program (biased toward depth of field)", + 6 => "Action program (biased toward fast shutter speed)", + 7 => "Portrait mode (for closeup photos with the background out of focus)", + 8 => "Landscape mode (for landscape photos with the background in focus)" ), + +34852 => array( 'Name' => "Spectral Sensitivity", + 'Type' => "String" ), + +34855 => array( 'Name' => "ISO Speed Ratings", + 'Type' => "Numeric" ), + +34856 => array( 'Name' => "Opto-Electronic Conversion Function", + 'Type' => "Unknown" ), + +41483 => array( 'Name' => "Flash Energy", + 'Type' => "Numeric", + 'Units' => "Beam Candle Power Seconds (BCPS)" ), + +41484 => array( 'Name' => "Spatial Frequency Response", + 'Type' => "Unknown" ), + +41486 => array( 'Name' => "Focal Plane X Resolution", + 'Type' => "Numeric", + 'Units' => "pixels per 'Focal Plane Resolution Unit'" ), + +41487 => array( 'Name' => "Focal Plane Y Resolution", + 'Type' => "Numeric", + 'Units' => "pixels per 'Focal Plane Resolution Unit'" ), + +41488 => array( 'Name' => "Focal Plane Resolution Unit", + 'Type' => "Lookup", + 2 => "Inches", + 3 => "Centimetres" ), + +41492 => array( 'Name' => "Subject Location", + 'Type' => "Numeric", + 'Units' => "(x,y pixel coordinates of subject)" ), + +41493 => array( 'Name' => "Exposure Index", + 'Type' => "Numeric" ), + +41495 => array( 'Name' => "Sensing Method", + 'Type' => "Lookup", + 1 => "Not defined", + 2 => "One-chip colour area sensor", + 3 => "Two-chip colour area sensor", + 4 => "Three-chip colour area sensor", + 5 => "Colour sequential area sensor", + 7 => "Trilinear sensor", + 8 => "Colour sequential linear sensor" ), + +41728 => array( 'Name' => "File Source", + 'Type' => "Lookup", + 3 => "Digital Still Camera" ), + +41729 => array( 'Name' => "Scene Type", + 'Type' => "Lookup", + 1 => "A directly photographed image" ), + +41730 => array( 'Name' => "Colour Filter Array Pattern", + 'Type' => "Special" ), + +41985 => array( 'Name' => "Special Processing (Custom Rendered)", + 'Type' => "Lookup", + 0 => "Normal process", + 1 => "Custom process" ), + +41986 => array( 'Name' => "Exposure Mode", + 'Type' => "Lookup", + 0 => "Auto exposure", + 1 => "Manual exposure", + 2 => "Auto bracket" ), + +41987 => array( 'Name' => "White Balance", + 'Type' => "Lookup", + 0 => "Auto white balance", + 1 => "Manual white balance" ), + +41988 => array( 'Name' => "Digital Zoom Ratio", + 'Type' => "Numeric", + 'Units' => " ( Zero = Digital Zoom Not Used )" ), + +41989 => array( 'Name' => "Equivalent Focal Length In 35mm Film", + 'Type' => "Numeric", + 'Units' => "mm" ), + +41990 => array( 'Name' => "Scene Capture Type", + 'Type' => "Lookup", + 0 => "Standard", + 1 => "Landscape", + 2 => "Portrait", + 3 => "Night scene" ), + +41991 => array( 'Name' => "Gain Control", + 'Type' => "Lookup", + 0 => "None", + 1 => "Low gain up", + 2 => "High gain up", + 3 => "Low gain down", + 4 => "High gain down" ), + +41992 => array( 'Name' => "Contrast", + 'Type' => "Lookup", + 0 => "Normal", + 1 => "Soft", + 2 => "Hard" ), + +41993 => array( 'Name' => "Saturation", + 'Type' => "Lookup", + 0 => "Normal", + 1 => "Low saturation", + 2 => "High saturation" ), + +41994 => array( 'Name' => "Sharpness", + 'Type' => "Lookup", + 0 => "Normal", + 1 => "Soft", + 2 => "Hard" ), + +41995 => array( 'Name' => "Device Setting Description", + 'Type' => "Unknown" ), + +41996 => array( 'Name' => "Subject Distance Range", + 'Type' => "Lookup", + 0 => "Unknown", + 1 => "Macro", + 2 => "Close view", + 3 => "Distant view" ), + +42016 => array( 'Name' => "Image Unique ID", + 'Type' => "String" ), + + + +// 11 => "ACDComment", +// 255 => "NewSubfileType" + + +), + + + + +/*****************************************************************************/ +/* */ +/* Interoperability Tags */ +/* */ +/*****************************************************************************/ + +"Interoperability" => array( + +1 => array( 'Name' => "Interoperability Index", + 'Type' => "String" ), + +2 => array( 'Name' => "Interoperability Version", + 'Type' => "String" ), + +4096 => array( 'Name' => "Related Image File Format", + 'Type' => "String" ), + +4097 => array( 'Name' => "Related Image File Width", + 'Type' => "Numeric", + 'Units' => "pixels" ), + +4098 => array( 'Name' => "Related Image File Length", + 'Type' => "Numeric", + 'Units' => "pixels " ) + +), + + +/*****************************************************************************/ +/* */ +/* GPS Tags */ +/* */ +/*****************************************************************************/ + +"GPS" => array( + +0 => array( 'Name' => "GPS Tag Version", + 'Type' => "Numeric", + 'Units' => "(e.g.: 2.2.0.0 = Version 2.2 )" ), + +1 => array( 'Name' => "North or South Latitude", + 'Type' => "String" ), + +2 => array( 'Name' => "Latitude", + 'Type' => "Numeric", + 'Units' => "(Degrees Minutes Seconds North or South)" ), + +3 => array( 'Name' => "East or West Longitude", + 'Type' => "String" ), + +4 => array( 'Name' => "Longitude", + 'Type' => "Numeric", + 'Units' => "(Degrees Minutes Seconds East or West)" ), + +5 => array( 'Name' => "Altitude Reference", + 'Type' => "Lookup", + 0 => "Sea Level", + 1 => "Sea level reference (negative value)" ), + +6 => array( 'Name' => "Altitude", + 'Type' => "Numeric", + 'Units' => "Metres with respect to Altitude Reference" ), + +7 => array( 'Name' => "GPS Time (atomic clock)", + 'Type' => "Numeric", + 'Units' => "(Hours Minutes Seconds)" ), + +8 => array( 'Name' => "GPS Satellites used for Measurement", + 'Type' => "String" ), + +9 => array( 'Name' => "GPS Receiver Status", + 'Type' => "Lookup", + 'A' => "Measurement in progress", // Change: Fixed tag values as of version 1.11 + 'V' => "Measurement Interoperability" ), + +10 => array( 'Name' => "GPS Measurement Mode", + 'Type' => "Lookup", + 2 => "2-dimensional measurement", // Change: Fixed tag values as of version 1.11 + 3 => "3-dimensional measurement" ), + +11 => array( 'Name' => "Measurement Precision", + 'Type' => "Numeric", + 'Units' => "(Data Degree of Precision, Horizontal for 2D, Position for 3D)" ), + +12 => array( 'Name' => "Speed Unit", + 'Type' => "Lookup", + 'K' => "Kilometers per Hour", // Change: Fixed tag values as of version 1.11 + 'M' => "Miles per Hour", + 'N' => "Knots" ), + +13 => array( 'Name' => "Speed of GPS receiver", + 'Type' => "Numeric", + 'Units' => "Speed Units" ), + +14 => array( 'Name' => "Reference for direction of Movement", + 'Type' => "Lookup", // Change: Fixed tag values as of version 1.11 + 'T' => "True North", + 'M' => "Magnetic North" ), + +15 => array( 'Name' => "Direction of Movement", + 'Type' => "Numeric", + 'Units' => "Degrees relative to Movement Direction Reference" ), + +16 => array( 'Name' => "Reference for Direction of Image", + 'Type' => "Lookup", + 'T' => "True North", // Change: Fixed tag values as of version 1.11 + 'M' => "Magnetic North" ), + +17 => array( 'Name' => "Direction of Image", + 'Type' => "Numeric", + 'Units' => "Degrees relative to Image Direction Reference" ), + +18 => array( 'Name' => "Geodetic Survey Datum Used", + 'Type' => "String" ), + +19 => array( 'Name' => "Destination - North or South Latitude", + 'Type' => "String" ), + +20 => array( 'Name' => "Latitude of Destination", + 'Type' => "Numeric", + 'Units' => "(Degrees Minutes Seconds North or South)" ), + +21 => array( 'Name' => "Destination - East or West Longitude", + 'Type' => "String" ), + +22 => array( 'Name' => "Longitude of Destination", + 'Type' => "Numeric", + 'Units' => "(Degrees Minutes Seconds East or West)" ), + +23 => array( 'Name' => "Reference for Bearing of Destination", + 'Type' => "Lookup", + 'T' => "True North", // Change: Fixed tag values as of version 1.11 + 'M' => "Magnetic North" ), + +24 => array( 'Name' => "Bearing of Destination", + 'Type' => "Numeric", + 'Units' => "Degrees relative to Destination Bearing Reference" ), + +25 => array( 'Name' => "Units for Distance to Destination", + 'Type' => "Lookup", + 'K' => "Kilometres", // Change: Fixed tag values as of version 1.11 + 'M' => "Miles", + 'N' => "Nautical Miles" ), + +26 => array( 'Name' => "Distance to Destination", + 'Type' => "Numeric", + 'Units' => "Destination Distance Units" ), + +27 => array( 'Name' => "Name of GPS Processing Method", + 'Type' => "Character Coded String" ), + +28 => array( 'Name' => "Name of GPS Area", + 'Type' => "Character Coded String" ), + +29 => array( 'Name' => "GPS Date", + 'Type' => "Numeric", + 'Units'=> " (Format: YYYY:MM:DD HH:mm:SS)" ), + +30 => array( 'Name' => "GPS Differential Correction", + 'Type' => "Lookup", + 0 => "Measurement without differential correction", + 1 => "Differential correction applied" ), + +), + + + + + + + + + +/*****************************************************************************/ +/* */ +/* META (App3) Tags */ +/* */ +/*****************************************************************************/ + +"Meta" => array( + + +50000 => array( 'Name' => "CaptureDevice.FilmProductCode", + 'Type' => "Unknown" ), + +50001 => array( 'Name' => "DigitalProcess.ImageSourceEK", + 'Type' => "Unknown" ), + +50002 => array( 'Name' => "CaptureConditions.PAR", + 'Type' => "Unknown" ), + +50003 => array( 'Name' => "CaptureDevice.CameraOwner.EK", + 'Type' => "Character Coded String" ), + +50004 => array( 'Name' => "CaptureDevice.SerialNumber.Camera", + 'Type' => "Unknown" ), + +50005 => array( 'Name' => "SceneContent.GroupCaption.UserSelectGroupTitle", + 'Type' => "Unknown" ), + +50006 => array( 'Name' => "OutputOrder.Information.DealerIDNumber", + 'Type' => "Unknown" ), + +50007 => array( 'Name' => "CaptureDevice.FID", + 'Type' => "Unknown" ), + +50008 => array( 'Name' => "OutputOrder.Information.EnvelopeNumber", + 'Type' => "Unknown" ), + +50009 => array( 'Name' => "OutputOrder.SimpleRenderInst.FrameNumber", + 'Type' => "Unknown" ), + +50010 => array( 'Name' => "CaptureDevice.FilmCategory", + 'Type' => "Unknown" ), + +50011 => array( 'Name' => "CaptureDevice.FilmGencode", + 'Type' => "Unknown" ), + +50012 => array( 'Name' => "CaptureDevice.Scanner.ModelAndVersion", + 'Type' => "Unknown" ), + +50013 => array( 'Name' => "CaptureDevice.FilmSize", + 'Type' => "Unknown" ), + +50014 => array( 'Name' => "DigitalProcess.History.SBARGBShifts", + 'Type' => "Unknown" ), + +50015 => array( 'Name' => "DigitalProcess.History.SBAInputImageColourspace", + 'Type' => "Unknown" ), + +50016 => array( 'Name' => "DigitalProcess.History.SBAInputImageBitDepth", + 'Type' => "Unknown" ), + +50017 => array( 'Name' => "DigitalProcess.History.SBAExposureRecord", + 'Type' => "Unknown" ), + +50018 => array( 'Name' => "DigitalProcess.History.UserAdjSBARGBShifts", + 'Type' => "Unknown" ), + +50019 => array( 'Name' => "DigitalProcess.ImageRotationStatus", + 'Type' => "Unknown" ), + +50020 => array( 'Name' => "DigitalProcess.RollGuid.Elements", + 'Type' => "Unknown" ), + +50021 => array( 'Name' => "ImageContainer.MetadataNumber", + 'Type' => "String" ), + +50022 => array( 'Name' => "DigitalProcess.History.EditTagArray", + 'Type' => "Unknown" ), + +50023 => array( 'Name' => "CaptureConditions.Magnification", + 'Type' => "Unknown" ), + +50028 => array( 'Name' => "CaptureDevice.NativePhysicalXResolution", + 'Type' => "Unknown" ), + +50029 => array( 'Name' => "CaptureDevice.NativePhysicalYResolution", + 'Type' => "Unknown" ), + +50030 => array( 'Name' => "Kodak Special Effects IFD", + 'Type' => "SubIFD", + 'Tags Name' => "KodakSpecialEffects" ), + +50031 => array( 'Name' => "Kodak Borders IFD", + 'Type' => "SubIFD", + 'Tags Name' => "KodakBorders" ), + +50042 => array( 'Name' => "CaptureDevice.NativePhysicalResolutionUnit", + 'Type' => "Unknown" ), + +50200 => array( 'Name' => "ImageContainer.SourceImageDirectory", + 'Type' => "Unknown" ), + +50201 => array( 'Name' => "ImageContainer.SourceImageFileName", + 'Type' => "Unknown" ), + +50202 => array( 'Name' => "ImageContainer.SourceImageVolumeName", + 'Type' => "Unknown" ), + +50284 => array( 'Name' => "CaptureConditions.PrintQuantity", + 'Type' => "Unknown" ), + +50286 => array( 'Name' => "DigitalProcess.ImagePrintStatus", + 'Type' => "Unknown" ) + +), + + + +/*****************************************************************************/ +/* */ +/* Kodak Special Effects IFD Tags */ +/* */ +/*****************************************************************************/ + +"KodakSpecialEffects" => array( + +0 => array( 'Name' => "Digital Effects Version", + 'Type' => "Numeric" ), + +1 => array( 'Name' => "Digital Effects Name", + 'Type' => "Character Coded String" ), + +2 => array( 'Name' => "Digital Effects Type", + 'Type' => "Lookup", + 0 => "None Applied" ) + +), + +/*****************************************************************************/ +/* */ +/* Kodak Borders IFD Tags */ +/* */ +/*****************************************************************************/ + +"KodakBorders" => array( + +0 => array( 'Name' => "Borders Version", + 'Type' => "Numeric" ), + +1 => array( 'Name' => "Border Name", + 'Type' => "Character Coded String" ), + +2 => array( 'Name' => "Border ID", + 'Type' => "Numeric" ), + +3 => array( 'Name' => "Border Location", + 'Type' => "Lookup" ), + +4 => array( 'Name' => "Border Type", + 'Type' => "Lookup", + 0 => "None" ), + +8 => array( 'Name' => "Watermark Type", + 'Type' => "Lookup", + 0 => "None" ) + +), + +); + +/****************************************************************************** +* End of Global Variable: IFD_Tag_Definitions +******************************************************************************/ + +?> \ No newline at end of file diff --git a/includes/jpeg_metadata_tk/Edit_File_Info.php b/includes/jpeg_metadata_tk/Edit_File_Info.php new file mode 100644 index 0000000..7b19e70 --- /dev/null +++ b/includes/jpeg_metadata_tk/Edit_File_Info.php @@ -0,0 +1,575 @@ + 1.11 : Changed displayed toolkit version numbers to reference Toolkit_Version.php +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* +* License: This file is part of the PHP JPEG Metadata Toolkit. +* +* The PHP JPEG Metadata Toolkit is free software; you can +* redistribute it and/or modify it under the terms of the +* GNU General Public License as published by the Free Software +* Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* The PHP JPEG Metadata Toolkit is distributed in the hope +* that it will be useful, but WITHOUT ANY WARRANTY; without +* even the implied warranty of MERCHANTABILITY or FITNESS +* FOR A PARTICULAR PURPOSE. See the GNU General Public License +* for more details. +* +* You should have received a copy of the GNU General Public +* License along with the PHP JPEG Metadata Toolkit; if not, +* write to the Free Software Foundation, Inc., 59 Temple +* Place, Suite 330, Boston, MA 02111-1307 USA +* +* If you require a different license for commercial or other +* purposes, please contact the author: evan@ozhiker.com +* +***************************************************************************/ + + + + require_once 'Toolkit_Version.php'; // Change: added as of version 1.11 + + // Check for operation modes 2 or 3 + // i.e. $filename is defined, and $new_ps_file_info_array is not + if ( ( ! isset( $new_ps_file_info_array ) ) && + ( isset( $filename ) ) && + ( is_string( $filename ) ) ) + { + // Hide any unknown EXIF tags + $GLOBALS['HIDE_UNKNOWN_TAGS'] = TRUE; + + // Accessing the existing file info for the specified file requires these includes + require_once 'JPEG.php'; + require_once 'XMP.php'; + require_once 'Photoshop_IRB.php'; + require_once 'EXIF.php'; + require_once 'Photoshop_File_Info.php'; + + // Retrieve the header information from the JPEG file + $jpeg_header_data = get_jpeg_header_data( $filename ); + + // Retrieve EXIF information from the JPEG file + $Exif_array = get_EXIF_JPEG( $filename ); + + // Retrieve XMP information from the JPEG file + $XMP_array = read_XMP_array_from_text( get_XMP_text( $jpeg_header_data ) ); + + // Retrieve Photoshop IRB information from the JPEG file + $IRB_array = get_Photoshop_IRB( $jpeg_header_data ); + + // Retrieve Photoshop File Info from the three previous arrays + $new_ps_file_info_array = get_photoshop_file_info( $Exif_array, $XMP_array, $IRB_array ); + + + + // Check if there is an array of defaults available + if ( ( isset( $default_ps_file_info_array) ) && + ( is_array( $default_ps_file_info_array) ) ) + { + // There are defaults defined + + // Check if there is a default for the date defined + if ( ( ! array_key_exists( 'date', $default_ps_file_info_array ) ) || + ( ( array_key_exists( 'date', $default_ps_file_info_array ) ) && + ( $default_ps_file_info_array['date'] == '' ) ) ) + { + // No default for the date defined + // figure out a default from the file + + // Check if there is a EXIF Tag 36867 "Date and Time of Original" + if ( ( $Exif_array != FALSE ) && + ( array_key_exists( 0, $Exif_array ) ) && + ( array_key_exists( 34665, $Exif_array[0] ) ) && + ( array_key_exists( 0, $Exif_array[0][34665] ) ) && + ( array_key_exists( 36867, $Exif_array[0][34665][0] ) ) ) + { + // Tag "Date and Time of Original" found - use it for the default date + $default_ps_file_info_array['date'] = $Exif_array[0][34665][0][36867]['Data'][0]; + $default_ps_file_info_array['date'] = preg_replace( "/(\d\d\d\d):(\d\d):(\d\d)( \d\d:\d\d:\d\d)/", "$1-$2-$3", $default_ps_file_info_array['date'] ); + } + // Check if there is a EXIF Tag 36868 "Date and Time when Digitized" + else if ( ( $Exif_array != FALSE ) && + ( array_key_exists( 0, $Exif_array ) ) && + ( array_key_exists( 34665, $Exif_array[0] ) ) && + ( array_key_exists( 0, $Exif_array[0][34665] ) ) && + ( array_key_exists( 36868, $Exif_array[0][34665][0] ) ) ) + { + // Tag "Date and Time when Digitized" found - use it for the default date + $default_ps_file_info_array['date'] = $Exif_array[0][34665][0][36868]['Data'][0]; + $default_ps_file_info_array['date'] = preg_replace( "/(\d\d\d\d):(\d\d):(\d\d)( \d\d:\d\d:\d\d)/", "$1-$2-$3", $default_ps_file_info_array['date'] ); + } + // Check if there is a EXIF Tag 306 "Date and Time" + else if ( ( $Exif_array != FALSE ) && + ( array_key_exists( 0, $Exif_array ) ) && + ( array_key_exists( 306, $Exif_array[0] ) ) ) + { + // Tag "Date and Time" found - use it for the default date + $default_ps_file_info_array['date'] = $Exif_array[0][306]['Data'][0]; + $default_ps_file_info_array['date'] = preg_replace( "/(\d\d\d\d):(\d\d):(\d\d)( \d\d:\d\d:\d\d)/", "$1-$2-$3", $default_ps_file_info_array['date'] ); + } + else + { + // Couldn't find an EXIF date in the image + // Set default date as creation date of file + $default_ps_file_info_array['date'] = date ("Y-m-d", filectime( $filename )); + } + } + + // Cycle through all the elements of the default values array + foreach( $default_ps_file_info_array as $def_key =>$default_item ) + { + // Check if the current element is Keywords or + // Supplemental Categories as these are arrays + // and need to be treated differently + if ( ( strcasecmp( $def_key, "keywords" ) == 0 ) || + ( strcasecmp( $def_key, "supplementalcategories" ) == 0 ) ) + { + // Keywords or Supplemental Categories found + // Check if the File Info from the file is empty for this element + // and if there are default values in this array element + if ( ( count( $new_ps_file_info_array[ $def_key ] ) == 0 ) && + ( is_array( $default_item ) ) && + ( count( $default_item ) >= 0 ) ) + { + // The existing file info is empty, and there are + // defaults - add them + $new_ps_file_info_array[ $def_key ] = $default_item; + } + } + // Otherwise, this is not an array element, just check if it is blank in the existing file info + else if ( trim( $new_ps_file_info_array[ $def_key ] ) == "" ) + { + // The existing file info is blank, add the default value + $new_ps_file_info_array[ $def_key ] = $default_item; + } + + } + } + } + // Check for operation mode 4 - $new_ps_file_info_array and $filename are not defined, + else if ( ( ( !isset($new_ps_file_info_array) ) || ( ! is_array($new_ps_file_info_array) ) ) && + ( ( !isset($filename) ) || ( ! is_string( $filename ) ) ) ) + { + // No filename or new_ps_file_info_array defined, create a blank file info array to display + $new_ps_file_info_array = array( + "title" => "", + "author" => "", + "authorsposition" => "", + "caption" => "", + "captionwriter" => "", + "jobname" => "", + "copyrightstatus" => "", + "copyrightnotice" => "", + "ownerurl" => "", + "keywords" => array(), + "category" => "", + "supplementalcategories" => array(), + "date" => "", + "city" => "", + "state" => "", + "country" => "", + "credit" => "", + "source" => "", + "headline" => "", + "instructions" => "", + "transmissionreference" => "", + "urgency" => "" ); + } + + + +/*************************************************************************** +* +* Now output the actual HTML form +* +***************************************************************************/ + +?> + + + + +
+ + + "; ?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Title + + "; + ?> +
+ Author + + "; + ?> +
+ Authors Position + + - Note: not used in Photoshop 7 or higher"; + ?> +
+ Description + + +
+ Description Writer + + "; + ?> +
+ Keywords + + +
+ Copyright Status + + +
+ Copyright Notice + + +
+ Copyright Info URL + + \n"; + if ($new_ps_file_info_array[ 'ownerurl' ] != "" ) + { + echo " (". $new_ps_file_info_array[ 'ownerurl' ] .")\n"; + } + ?> + +
+ Category + + \n"; + ?> + +
+ Supplemental Categories + + +
+ Date Created + + "; + ?> + (Note date must be YYYY-MM-DD format) +
+ City + + "; + ?> +
+ State + + "; + ?> +
+ Country + + "; + ?> +
+ Credit + + "; + ?> +
+ Source + + "; + ?> +
+ Headline + + +
+ Instructions + + +
+ Transmission Reference + + +
+ Job Name + + - Note: not used in Photoshop CS"; + ?> +
+ Urgency + + +
+
+ + + +
+ +
+
+

Powered by: PHP JPEG Metadata Toolkit version , Copyright (C) 2004 Evan Hunter

+
+
diff --git a/includes/jpeg_metadata_tk/IPTC.php b/includes/jpeg_metadata_tk/IPTC.php new file mode 100644 index 0000000..4499c67 --- /dev/null +++ b/includes/jpeg_metadata_tk/IPTC.php @@ -0,0 +1,691 @@ + 1.01 : changed get_IPTC to return partial data when error occurs +* 1.01 -> 1.10 : changed put_IPTC to check if the incoming IPTC block is valid +* changed Interpret_IPTC_to_HTML, adding nl2br functions for each text field, +* so that multiline text displays properly +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* +* License: This file is part of the PHP JPEG Metadata Toolkit. +* +* The PHP JPEG Metadata Toolkit is free software; you can +* redistribute it and/or modify it under the terms of the +* GNU General Public License as published by the Free Software +* Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* The PHP JPEG Metadata Toolkit is distributed in the hope +* that it will be useful, but WITHOUT ANY WARRANTY; without +* even the implied warranty of MERCHANTABILITY or FITNESS +* FOR A PARTICULAR PURPOSE. See the GNU General Public License +* for more details. +* +* You should have received a copy of the GNU General Public +* License along with the PHP JPEG Metadata Toolkit; if not, +* write to the Free Software Foundation, Inc., 59 Temple +* Place, Suite 330, Boston, MA 02111-1307 USA +* +* If you require a different license for commercial or other +* purposes, please contact the author: evan@ozhiker.com +* +******************************************************************************/ + + +// TODO: Not all of the IPTC fields have been tested properly + +/****************************************************************************** +* +* Function: get_IPTC +* +* Description: Extracts IPTC-NAA IIM data from the string provided, and returns +* the information as an array +* +* Parameters: Data_Str - the string containing the IPTC-NAA IIM records. Must +* be exact length of the IPTC-NAA IIM data. +* +* Returns: OutputArray - Array of IPTC-NAA IIM records +* FALSE - If an error occured in decoding +* +******************************************************************************/ + +function get_IPTC( $Data_Str ) +{ + + // Initialise the start position + $pos = 0; + // Create the array to receive the data + $OutputArray = array( ); + + // Cycle through the IPTC records, decoding and storing them + while( $pos < strlen($Data_Str) ) + { + // TODO - Extended Dataset record not supported + + // Check if there is sufficient data for reading the record + if ( strlen( substr($Data_Str,$pos) ) < 5 ) + { + // Not enough data left for a record - Probably corrupt data - ERROR + // Change: changed to return partial data as of revision 1.01 + return $OutputArray; + } + + // Unpack data from IPTC record: + // First byte - IPTC Tag Marker - always 28 + // Second byte - IPTC Record Number + // Third byte - IPTC Dataset Number + // Fourth and fifth bytes - two byte size value + $iptc_raw = unpack( "CIPTC_Tag_Marker/CIPTC_Record_No/CIPTC_Dataset_No/nIPTC_Size", substr($Data_Str,$pos) ); + + // Skip position over the unpacked data + $pos += 5; + + // Construct the IPTC type string eg 2:105 + $iptctype = sprintf( "%01d:%02d", $iptc_raw['IPTC_Record_No'], $iptc_raw['IPTC_Dataset_No']); + + // Check if there is sufficient data for reading the record contents + if ( strlen( substr( $Data_Str, $pos, $iptc_raw['IPTC_Size'] ) ) !== $iptc_raw['IPTC_Size'] ) + { + // Not enough data left for the record content - Probably corrupt data - ERROR + // Change: changed to return partial data as of revision 1.01 + return $OutputArray; + } + + // Add the IPTC record to the output array + $OutputArray[] = array( "IPTC_Type" => $iptctype , + "RecName" => (isset( $GLOBALS["IPTC_Entry_Names"][$iptctype] ) ? $GLOBALS["IPTC_Entry_Names"][$iptctype] : ''), + "RecDesc" => (isset( $GLOBALS["IPTC_Entry_Descriptions"][$iptctype] ) ? $GLOBALS[ "IPTC_Entry_Descriptions" ][ $iptctype ] : ''), + "RecData" => substr( $Data_Str, $pos, $iptc_raw['IPTC_Size'] ) ); + + // Skip over the IPTC record data + $pos += $iptc_raw['IPTC_Size']; + } + return $OutputArray; + +} + + +/****************************************************************************** +* End of Function: get_IPTC +******************************************************************************/ + + + + +/****************************************************************************** +* +* Function: put_IPTC +* +* Description: Encodes an array of IPTC-NAA records into a string encoded +* as IPTC-NAA IIM. (The reverse of get_IPTC) +* +* Parameters: new_IPTC_block - the IPTC-NAA array to be encoded. Should be +* the same format as that received from get_IPTC +* +* Returns: iptc_packed_data - IPTC-NAA IIM encoded string +* +******************************************************************************/ + + +function put_IPTC( $new_IPTC_block ) +{ + // Check if the incoming IPTC block is valid + if ( $new_IPTC_block == FALSE ) + { + // Invalid IPTC block - abort + return FALSE; + } + // Initialise the packed output data string + $iptc_packed_data = ""; + + // Cycle through each record in the new IPTC block + foreach ($new_IPTC_block as $record) + { + // Extract the Record Number and Dataset Number from the IPTC_Type field + list($IPTC_Record, $IPTC_Dataset) = sscanf( $record['IPTC_Type'], "%d:%d"); + + // Write the IPTC-NAA IIM Tag Marker, Record Number, Dataset Number and Data Size to the packed output data string + $iptc_packed_data .= pack( "CCCn", 28, $IPTC_Record, $IPTC_Dataset, strlen($record['RecData']) ); + + // Write the IPTC-NAA IIM Data to the packed output data string + $iptc_packed_data .= $record['RecData']; + } + + // Return the IPTC-NAA IIM data + return $iptc_packed_data; +} + +/****************************************************************************** +* End of Function: put_IPTC +******************************************************************************/ + + + +/****************************************************************************** +* +* Function: Interpret_IPTC_to_HTML +* +* Description: Generates html detailing the contents a IPTC-NAA IIM array +* which was retrieved with the get_IPTC function +* +* Parameters: IPTC_info - the IPTC-NAA IIM array,as read from get_IPTC +* +* Returns: OutputStr - A string containing the HTML +* +******************************************************************************/ + +function Interpret_IPTC_to_HTML( $IPTC_info ) +{ + // Create a string to receive the HTML + $output_str =""; + + // Check if the IPTC + if ( $IPTC_info !== FALSE ) + { + + + // Add Heading to HTML + $output_str .= "

IPTC-NAA Record

\n"; + + // Add Table to HTML + $output_str .= "\n\n"; + + // Cycle through each of the IPTC-NAA IIM records + foreach( $IPTC_info as $IPTC_Record ) + { + // Check if the record is a known IPTC field + $Record_Name = $IPTC_Record['RecName']; + if ( $Record_Name == "" ) + { + // Record is an unknown field - add message to HTML + $output_str .= "\n"; + } + else + { + // Record is a recognised IPTC field - Process it accordingly + + switch ( $IPTC_Record['IPTC_Type'] ) + { + case "1:00": // Envelope Record:Model Version + case "1:22": // Envelope Record:File Format Version + $output_str .= "\n"; + break; + + case "1:90": // Envelope Record:Coded Character Set + $output_str .= "\n"; + break; + // TODO: Implement decoding of IPTC record 1:90 + + case "1:20": // Envelope Record:File Format + + $formatno = hexdec( bin2hex( $IPTC_Record['RecData'] ) ); + + // Lookup file format from lookup-table + if ( array_key_exists( $formatno, $GLOBALS[ "IPTC_File Formats" ] ) ) + { + // Entry was found in lookup table - add it to HTML + $output_str .= "\n"; + } + else + { + // No matching entry was found in lookup table - add message to html + $output_str .= "\n"; + } + break; + + + case "2:00": // Application Record:Record Version + $output_str .= "\n"; + break; + + case "2:42": // Application Record: Action Advised + + // Looup Action + if ( $IPTC_Record['RecData'] == "01" ) + { + $output_str .= "\n"; + } + elseif ( $IPTC_Record['RecData'] == "02" ) + { + $output_str .= "\n"; + } + elseif ( $IPTC_Record['RecData'] == "03" ) + { + $output_str .= "\n"; + } + elseif ( $IPTC_Record['RecData'] == "04" ) + { + $output_str .= "\n"; + } + else + { + // Unknown Action + $output_str .= "\n"; + } + break; + + case "2:08": // Application Record:Editorial Update + if ( $IPTC_Record['RecData'] == "01" ) + { + // Additional Language + $output_str .= "\n"; + } + else + { + // Unknown Value + $output_str .= "\n"; + } + break; + + case "2:30": // Application Record:Release Date + case "2:37": // Application Record:Expiration Date + case "2:47": // Application Record:Reference Date + case "2:55": // Application Record:Date Created + case "2:62": // Application Record:Digital Creation Date + case "1:70": // Envelope Record:Date Sent + $date_array = unpack( "a4Year/a2Month/A2Day", $IPTC_Record['RecData'] ); + $output_str .= "\n"; + break; + + case "2:35": // Application Record:Release Time + case "2:38": // Application Record:Expiration Time + case "2:60": // Application Record:Time Created + case "2:63": // Application Record:Digital Creation Time + case "1:80": // Envelope Record:Time Sent + $time_array = unpack( "a2Hour/a2Minute/A2Second/APlusMinus/A4Timezone", $IPTC_Record['RecData'] ); + $output_str .= "\n"; + break; + + case "2:75": // Application Record:Object Cycle + // Lookup Value + if ( $IPTC_Record['RecData'] == "a" ) + { + $output_str .= "\n"; + } + elseif ( $IPTC_Record['RecData'] == "p" ) + { + $output_str .= "\n"; + } + elseif ( $IPTC_Record['RecData'] == "b" ) + { + $output_str .= "\n"; + } + else + { + // Unknown Value + $output_str .= "\n"; + } + break; + + case "2:125": // Application Record:Rasterised Caption + $output_str .= "\n"; + break; + // TODO: Display Rasterised Caption for IPTC record 2:125 + + case "2:130": // Application Record:Image Type + // Lookup Number of Components + if ( $IPTC_Record['RecData']{0} == "0" ) + { + $output_str .= "\n"; + break; + + case "2:131": // Application Record:Image Orientation + // Lookup value + if ( $IPTC_Record['RecData'] == "L" ) + { + $output_str .= "\n"; + } + elseif ( $IPTC_Record['RecData'] == "P" ) + { + $output_str .= "\n"; + } + elseif ( $IPTC_Record['RecData'] == "S" ) + { + $output_str .= "\n"; + } + else + { + // Unknown Orientation Value + $output_str .= "\n"; + } + break; + + default: // All other records + $output_str .= "\n"; + break; + } + } + } + + // Add Table End to HTML + $output_str .= "
Unknown IPTC field '". htmlentities( $IPTC_Record['IPTC_Type'] ). "' :" . nl2br( HTML_UTF8_Escape( $IPTC_Record['RecData'] ) ) ."
$Record_Name" . hexdec( bin2hex( $IPTC_Record['RecData'] ) ) ."
$Record_NameDecoding not yet implemented
\n (Hex Data: " . bin2hex( $IPTC_Record['RecData'] ) .")
File Format". $GLOBALS[ "IPTC_File Formats" ][$formatno] . "
File FormatUnknown File Format ($formatno)
IPTC Version" . hexdec( bin2hex( $IPTC_Record['RecData'] ) ) ."
$Record_NameKill
$Record_NameReplace
$Record_NameAppend
$Record_NameReference
$Record_NameUnknown : " . nl2br( HTML_UTF8_Escape( $IPTC_Record['RecData'] ) ) ."
$Record_NameAdditional language
$Record_NameUnknown : " . nl2br( HTML_UTF8_Escape( $IPTC_Record['RecData'] ) ) ."
$Record_Name" . nl2br( HTML_UTF8_Escape( $date_array['Day'] . "/" . $date_array['Month'] . "/" . $date_array['Year'] ) ) ."
$Record_Name" . nl2br( HTML_UTF8_Escape( $time_array['Hour'] . ":" . $time_array['Minute'] . ":" . $time_array['Second'] . " ". $time_array['PlusMinus'] . $time_array['Timezone'] ) ) ."
$Record_NameMorning
$Record_NameEvening
$Record_NameBoth Morning and Evening
$Record_NameUnknown : " . nl2br( HTML_UTF8_Escape( $IPTC_Record['RecData'] ) ) ."
$Record_Name460x128 pixel black and white caption image
$Record_NameNo Objectdata"; + } + elseif ( $IPTC_Record['RecData']{0} == "9" ) + { + $output_str .= "
$Record_NameSupplemental objects related to other objectdata"; + } + else + { + $output_str .= "
$Record_NameNumber of Colour Components : " . nl2br( HTML_UTF8_Escape( $IPTC_Record['RecData']{0} ) ); + } + + // Lookup current objectdata colour + if ( $GLOBALS['ImageType_Names'][ $IPTC_Record['RecData']{1} ] == "" ) + { + $output_str .= ", Unknown : " . nl2br( HTML_UTF8_Escape( $IPTC_Record['RecData']{1} ) ); + } + else + { + $output_str .= ", " . nl2br( HTML_UTF8_Escape( $GLOBALS['ImageType_Names'][ $IPTC_Record['RecData']{1} ] ) ); + } + $output_str .= "
$Record_NameLandscape
$Record_NamePortrait
$Record_NameSquare
$Record_NameUnknown : " . nl2br( HTML_UTF8_Escape( $IPTC_Record['RecData'] ) ) ."
$Record_Name" .nl2br( HTML_UTF8_Escape( $IPTC_Record['RecData'] ) ) ."

\n"; + } + + // Return HTML + return $output_str; +} + + +/****************************************************************************** +* End of Function: Interpret_IPTC_to_HTML +******************************************************************************/ + + + +/****************************************************************************** +* Global Variable: IPTC_Entry_Names +* +* Contents: The names of the IPTC-NAA IIM fields +* +******************************************************************************/ + +$GLOBALS[ "IPTC_Entry_Names" ] = array( +// Envelope Record +"1:00" => "Model Version", +"1:05" => "Destination", +"1:20" => "File Format", +"1:22" => "File Format Version", +"1:30" => "Service Identifier", +"1:40" => "Envelope Number", +"1:50" => "Product ID", +"1:60" => "Envelope Priority", +"1:70" => "Date Sent", +"1:80" => "Time Sent", +"1:90" => "Coded Character Set", +"1:100" => "UNO (Unique Name of Object)", +"1:120" => "ARM Identifier", +"1:122" => "ARM Version", + +// Application Record +"2:00" => "Record Version", +"2:03" => "Object Type Reference", +"2:05" => "Object Name (Title)", +"2:07" => "Edit Status", +"2:08" => "Editorial Update", +"2:10" => "Urgency", +"2:12" => "Subject Reference", +"2:15" => "Category", +"2:20" => "Supplemental Category", +"2:22" => "Fixture Identifier", +"2:25" => "Keywords", +"2:26" => "Content Location Code", +"2:27" => "Content Location Name", +"2:30" => "Release Date", +"2:35" => "Release Time", +"2:37" => "Expiration Date", +"2:35" => "Expiration Time", +"2:40" => "Special Instructions", +"2:42" => "Action Advised", +"2:45" => "Reference Service", +"2:47" => "Reference Date", +"2:50" => "Reference Number", +"2:55" => "Date Created", +"2:60" => "Time Created", +"2:62" => "Digital Creation Date", +"2:63" => "Digital Creation Time", +"2:65" => "Originating Program", +"2:70" => "Program Version", +"2:75" => "Object Cycle", +"2:80" => "By-Line (Author)", +"2:85" => "By-Line Title (Author Position) [Not used in Photoshop 7]", +"2:90" => "City", +"2:92" => "Sub-Location", +"2:95" => "Province/State", +"2:100" => "Country/Primary Location Code", +"2:101" => "Country/Primary Location Name", +"2:103" => "Original Transmission Reference", +"2:105" => "Headline", +"2:110" => "Credit", +"2:115" => "Source", +"2:116" => "Copyright Notice", +"2:118" => "Contact", +"2:120" => "Caption/Abstract", +"2:122" => "Caption Writer/Editor", +"2:125" => "Rasterized Caption", +"2:130" => "Image Type", +"2:131" => "Image Orientation", +"2:135" => "Language Identifier", +"2:150" => "Audio Type", +"2:151" => "Audio Sampling Rate", +"2:152" => "Audio Sampling Resolution", +"2:153" => "Audio Duration", +"2:154" => "Audio Outcue", +"2:200" => "ObjectData Preview File Format", +"2:201" => "ObjectData Preview File Format Version", +"2:202" => "ObjectData Preview Data", + +// Pre-ObjectData Descriptor Record +"7:10" => "Size Mode", +"7:20" => "Max Subfile Size", +"7:90" => "ObjectData Size Announced", +"7:95" => "Maximum ObjectData Size", + +// ObjectData Record +"8:10" => "Subfile", + +// Post ObjectData Descriptor Record +"9:10" => "Confirmed ObjectData Size" + +); + +/****************************************************************************** +* End of Global Variable: IPTC_Entry_Names +******************************************************************************/ + + + + + +/****************************************************************************** +* Global Variable: IPTC_Entry_Descriptions +* +* Contents: The Descriptions of the IPTC-NAA IIM fields +* +******************************************************************************/ + +$GLOBALS[ "IPTC_Entry_Descriptions" ] = array( +// Envelope Record +"1:00" => "2 byte binary version number", +"1:05" => "Max 1024 characters of Destination", +"1:20" => "2 byte binary file format number, see IPTC-NAA V4 Appendix A", +"1:22" => "Binary version number of file format", +"1:30" => "Max 10 characters of Service Identifier", +"1:40" => "8 Character Envelope Number", +"1:50" => "Product ID - Max 32 characters", +"1:60" => "Envelope Priority - 1 numeric characters", +"1:70" => "Date Sent - 8 numeric characters CCYYMMDD", +"1:80" => "Time Sent - 11 characters HHMMSS±HHMM", +"1:90" => "Coded Character Set - Max 32 characters", +"1:100" => "UNO (Unique Name of Object) - 14 to 80 characters", +"1:120" => "ARM Identifier - 2 byte binary number", +"1:122" => "ARM Version - 2 byte binary number", + +// Application Record +"2:00" => "Record Version - 2 byte binary number", +"2:03" => "Object Type Reference - 3 plus 0 to 64 Characters", +"2:05" => "Object Name (Title) - Max 64 characters", +"2:07" => "Edit Status - Max 64 characters", +"2:08" => "Editorial Update - 2 numeric characters", +"2:10" => "Urgency - 1 numeric character", +"2:12" => "Subject Reference - 13 to 236 characters", +"2:15" => "Category - Max 3 characters", +"2:20" => "Supplemental Category - Max 32 characters", +"2:22" => "Fixture Identifier - Max 32 characters", +"2:25" => "Keywords - Max 64 characters", +"2:26" => "Content Location Code - 3 characters", +"2:27" => "Content Location Name - Max 64 characters", +"2:30" => "Release Date - 8 numeric characters CCYYMMDD", +"2:35" => "Release Time - 11 characters HHMMSS±HHMM", +"2:37" => "Expiration Date - 8 numeric characters CCYYMMDD", +"2:35" => "Expiration Time - 11 characters HHMMSS±HHMM", +"2:40" => "Special Instructions - Max 256 Characters", +"2:42" => "Action Advised - 2 numeric characters", +"2:45" => "Reference Service - Max 10 characters", +"2:47" => "Reference Date - 8 numeric characters CCYYMMDD", +"2:50" => "Reference Number - 8 characters", +"2:55" => "Date Created - 8 numeric characters CCYYMMDD", +"2:60" => "Time Created - 11 characters HHMMSS±HHMM", +"2:62" => "Digital Creation Date - 8 numeric characters CCYYMMDD", +"2:63" => "Digital Creation Time - 11 characters HHMMSS±HHMM", +"2:65" => "Originating Program - Max 32 characters", +"2:70" => "Program Version - Max 10 characters", +"2:75" => "Object Cycle - 1 character", +"2:80" => "By-Line (Author) - Max 32 Characters", +"2:85" => "By-Line Title (Author Position) - Max 32 characters", +"2:90" => "City - Max 32 Characters", +"2:92" => "Sub-Location - Max 32 characters", +"2:95" => "Province/State - Max 32 Characters", +"2:100" => "Country/Primary Location Code - 3 alphabetic characters", +"2:101" => "Country/Primary Location Name - Max 64 characters", +"2:103" => "Original Transmission Reference - Max 32 characters", +"2:105" => "Headline - Max 256 Characters", +"2:110" => "Credit - Max 32 Characters", +"2:115" => "Source - Max 32 Characters", +"2:116" => "Copyright Notice - Max 128 Characters", +"2:118" => "Contact - Max 128 characters", +"2:120" => "Caption/Abstract - Max 2000 Characters", +"2:122" => "Caption Writer/Editor - Max 32 Characters", +"2:125" => "Rasterized Caption - 7360 bytes, 1 bit per pixel, 460x128pixel image", +"2:130" => "Image Type - 2 characters", +"2:131" => "Image Orientation - 1 alphabetic character", +"2:135" => "Language Identifier - 2 or 3 aphabetic characters", +"2:150" => "Audio Type - 2 characters", +"2:151" => "Audio Sampling Rate - 6 numeric characters", +"2:152" => "Audio Sampling Resolution - 2 numeric characters", +"2:153" => "Audio Duration - 6 numeric characters", +"2:154" => "Audio Outcue - Max 64 characters", +"2:200" => "ObjectData Preview File Format - 2 byte binary number", +"2:201" => "ObjectData Preview File Format Version - 2 byte binary number", +"2:202" => "ObjectData Preview Data - Max 256000 binary bytes", + +// Pre-ObjectData Descriptor Record +"7:10" => "Size Mode - 1 numeric character", +"7:20" => "Max Subfile Size", +"7:90" => "ObjectData Size Announced", +"7:95" => "Maximum ObjectData Size", + +// ObjectData Record +"8:10" => "Subfile", + +// Post ObjectData Descriptor Record +"9:10" => "Confirmed ObjectData Size" + +); + +/****************************************************************************** +* End of Global Variable: IPTC_Entry_Descriptions +******************************************************************************/ + + + + +/****************************************************************************** +* Global Variable: IPTC_File Formats +* +* Contents: The names of the IPTC-NAA IIM File Formats for field 1:20 +* +******************************************************************************/ + +$GLOBALS[ "IPTC_File Formats" ] = array( +00 => "No ObjectData", +01 => "IPTC-NAA Digital Newsphoto Parameter Record", +02 => "IPTC7901 Recommended Message Format", +03 => "Tagged Image File Format (Adobe/Aldus Image data)", +04 => "Illustrator (Adobe Graphics data)", +05 => "AppleSingle (Apple Computer Inc)", +06 => "NAA 89-3 (ANPA 1312)", +07 => "MacBinary II", +08 => "IPTC Unstructured Character Oriented File Format (UCOFF)", +09 => "United Press International ANPA 1312 variant", +10 => "United Press International Down-Load Message", +11 => "JPEG File Interchange (JFIF)", +12 => "Photo-CD Image-Pac (Eastman Kodak)", +13 => "Microsoft Bit Mapped Graphics File [*.BMP]", +14 => "Digital Audio File [*.WAV] (Microsoft & Creative Labs)", +15 => "Audio plus Moving Video [*.AVI] (Microsoft)", +16 => "PC DOS/Windows Executable Files [*.COM][*.EXE]", +17 => "Compressed Binary File [*.ZIP] (PKWare Inc)", +18 => "Audio Interchange File Format AIFF (Apple Computer Inc)", +19 => "RIFF Wave (Microsoft Corporation)", +20 => "Freehand (Macromedia/Aldus)", +21 => "Hypertext Markup Language - HTML (The Internet Society)", +22 => "MPEG 2 Audio Layer 2 (Musicom), ISO/IEC", +23 => "MPEG 2 Audio Layer 3, ISO/IEC", +24 => "Portable Document File (*.PDF) Adobe", +25 => "News Industry Text Format (NITF)", +26 => "Tape Archive (*.TAR)", +27 => "Tidningarnas Telegrambyrå NITF version (TTNITF DTD)", +28 => "Ritzaus Bureau NITF version (RBNITF DTD)", +29 => "Corel Draw [*.CDR]" +); + + +/****************************************************************************** +* End of Global Variable: IPTC_File Formats +******************************************************************************/ + +/****************************************************************************** +* Global Variable: ImageType_Names +* +* Contents: The names of the colour components for IPTC-NAA IIM field 2:130 +* +******************************************************************************/ + +$GLOBALS['ImageType_Names'] = array( "M" => "Monochrome", + "Y" => "Yellow Component", + "M" => "Magenta Component", + "C" => "Cyan Component", + "K" => "Black Component", + "R" => "Red Component", + "G" => "Green Component", + "B" => "Blue Component", + "T" => "Text Only", + "F" => "Full colour composite, frame sequential", + "L" => "Full colour composite, line sequential", + "P" => "Full colour composite, pixel sequential", + "S" => "Full colour composite, special interleaving" ); + + + +/****************************************************************************** +* End of Global Variable: ImageType_Names +******************************************************************************/ + +?> diff --git a/includes/jpeg_metadata_tk/JFIF.php b/includes/jpeg_metadata_tk/JFIF.php new file mode 100644 index 0000000..90f314f --- /dev/null +++ b/includes/jpeg_metadata_tk/JFIF.php @@ -0,0 +1,438 @@ + 1.11 : changed Interpret_JFXX_to_HTML to allow thumbnail links to work when +* toolkit is portable across directories +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* +* License: This file is part of the PHP JPEG Metadata Toolkit. +* +* The PHP JPEG Metadata Toolkit is free software; you can +* redistribute it and/or modify it under the terms of the +* GNU General Public License as published by the Free Software +* Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* The PHP JPEG Metadata Toolkit is distributed in the hope +* that it will be useful, but WITHOUT ANY WARRANTY; without +* even the implied warranty of MERCHANTABILITY or FITNESS +* FOR A PARTICULAR PURPOSE. See the GNU General Public License +* for more details. +* +* You should have received a copy of the GNU General Public +* License along with the PHP JPEG Metadata Toolkit; if not, +* write to the Free Software Foundation, Inc., 59 Temple +* Place, Suite 330, Boston, MA 02111-1307 USA +* +* If you require a different license for commercial or other +* purposes, please contact the author: evan@ozhiker.com +* +******************************************************************************/ + +include_once 'pjmt_utils.php'; // Change: as of version 1.11 - added to allow directory portability + +/****************************************************************************** +* +* Function: get_JFIF +* +* Description: Retrieves information from a JPEG File Interchange Format (JFIF) +* segment and returns it in an array. Uses information supplied by +* the get_jpeg_header_data function +* +* Parameters: jpeg_header_data - a JPEG header data array in the same format +* as from get_jpeg_header_data +* +* Returns: JFIF_data - an array of JFIF data +* FALSE - if a JFIF segment could not be found +* +******************************************************************************/ + +function get_JFIF( $jpeg_header_data ) +{ + //Cycle through the header segments + for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) + { + // If we find an APP0 header, + if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP0" ) == 0 ) + { + // And if it has the JFIF label, + if( strncmp ( $jpeg_header_data[$i]['SegData'], "JFIF\x00", 5) == 0 ) + { + // Found a JPEG File Interchange Format (JFIF) Block + + // unpack the JFIF data from the incoming string + // First is the JFIF label string + // Then a two byte version number + // Then a byte, units identifier, ( 0 = aspect ration, 1 = dpi, 2 = dpcm) + // Then a two byte int X-Axis pixel Density (resolution) + // Then a two byte int Y-Axis pixel Density (resolution) + // Then a byte X-Axis JFIF thumbnail size + // Then a byte Y-Axis JFIF thumbnail size + // Then the uncompressed RGB JFIF thumbnail data + + $JFIF_data = unpack( 'a5JFIF/C2Version/CUnits/nXDensity/nYDensity/CThumbX/CThumbY/a*ThumbData', $jpeg_header_data[$i]['SegData'] ); + + return $JFIF_data; + } + } + } + return FALSE; +} + +/****************************************************************************** +* End of Function: get_JFIF +******************************************************************************/ + + + + +/****************************************************************************** +* +* Function: put_JFIF +* +* Description: Creates a new JFIF segment from an array of JFIF data in the +* same format as would be retrieved from get_JFIF, and inserts +* this segment into the supplied JPEG header array +* +* Parameters: jpeg_header_data - a JPEG header data array in the same format +* as from get_jpeg_header_data, into which the +* new JFIF segment will be put +* new_JFIF_array - a JFIF information array in the same format as +* from get_JFIF, to create the new segment +* +* Returns: jpeg_header_data - the JPEG header data array with the new +* JFIF segment added +* +******************************************************************************/ + +function put_JFIF( $jpeg_header_data, $new_JFIF_array ) +{ + // pack the JFIF data into its proper format for a JPEG file + $packed_data = pack( 'a5CCCnnCCa*',"JFIF\x00", $new_JFIF_array['Version1'], $new_JFIF_array['Version2'], $new_JFIF_array['Units'], $new_JFIF_array['XDensity'], $new_JFIF_array['YDensity'], $new_JFIF_array['ThumbX'], $new_JFIF_array['ThumbY'], $new_JFIF_array['ThumbData'] ); + + //Cycle through the header segments + for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) + { + // If we find an APP0 header, + if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP0" ) == 0 ) + { + // And if it has the JFIF label, + if( strncmp ( $jpeg_header_data[$i]['SegData'], "JFIF\x00", 5) == 0 ) + { + // Found a preexisting JFIF block - Replace it with the new one and return. + $jpeg_header_data[$i]['SegData'] = $packed_data; + return $jpeg_header_data; + } + } + } + + // No preexisting JFIF block found, insert a new one at the start of the header data. + array_splice($jpeg_header_data, 0 , 0, array( array( "SegType" => 0xE0, + "SegName" => "APP0", + "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE0 ], + "SegData" => $packed_data ) ) ); + return $jpeg_header_data; +} + +/****************************************************************************** +* End of Function: put_JFIF +******************************************************************************/ + + + + + + + + +/****************************************************************************** +* +* Function: Interpret_JFIF_to_HTML +* +* Description: Generates html showing the JFIF information contained in +* a JFIF data array, as retrieved with get_JFIF +* +* Parameters: JFIF_array - a JFIF data array, as from get_JFIF +* filename - the name of the JPEG file being processed ( used +* by the script which displays the JFIF thumbnail) +* +* +* Returns: output - the HTML string +* +******************************************************************************/ + +function Interpret_JFIF_to_HTML( $JFIF_array, $filename ) +{ + $output = ""; + if ( $JFIF_array !== FALSE ) + { + $output .= "

Contains JPEG File Interchange Format (JFIF) Information

\n"; + $output .= "\n\n"; + $output .= "\n"; + if ( $JFIF_array['Units'] == 0 ) + { + $output .= "\n"; + } + elseif ( $JFIF_array['Units'] == 1 ) + { + $output .= "\n"; + } + elseif ( $JFIF_array['Units'] == 2 ) + { + $output .= "\n"; + } + + $output .= "\n"; + // TODO Implement JFIF Thumbnail display + } + else + { + $output .= "None\n"; + } + + $output .= "
JFIF version: ". sprintf( "%d.%02d", $JFIF_array['Version1'], $JFIF_array['Version2'] ) . "
Pixel Aspect Ratio: " . $JFIF_array['XDensity'] ." x " . $JFIF_array['YDensity'] . "
Resolution: " . $JFIF_array['XDensity'] ." x " . $JFIF_array['YDensity'] . " pixels per inch
Resolution: " . $JFIF_array['XDensity'] ." x " . $JFIF_array['YDensity'] . " pixels per cm
JFIF (uncompressed) thumbnail: "; + if ( ( $JFIF_array['ThumbX'] != 0 ) && ( $JFIF_array['ThumbY'] != 0 ) ) + { + $output .= $JFIF_array['ThumbX'] ." x " . $JFIF_array['ThumbY'] . " pixels, Thumbnail Display Not Yet Implemented

\n"; + } + + return $output; + +} + + +/****************************************************************************** +* End of Function: Interpret_JFIF_to_HTML +******************************************************************************/ + + + + + + + + + + +/****************************************************************************** +* +* Function: get_JFXX +* +* Description: Retrieves information from a JPEG File Interchange Format Extension (JFXX) +* segment and returns it in an array. Uses information supplied by +* the get_jpeg_header_data function +* +* Parameters: jpeg_header_data - a JPEG header data array in the same format +* as from get_jpeg_header_data +* +* Returns: JFXX_data - an array of JFXX data +* FALSE - if a JFXX segment could not be found +* +******************************************************************************/ + +function get_JFXX( $jpeg_header_data ) +{ + //Cycle through the header segments + for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) + { + // If we find an APP0 header, + if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP0" ) == 0 ) + { + // And if it has the JFIF label, + if( strncmp ( $jpeg_header_data[$i]['SegData'], "JFXX\x00", 5) == 0 ) + { + // Found a JPEG File Interchange Format Extension (JFXX) Block + + // unpack the JFXX data from the incoming string + // First is the 5 byte JFXX label string + // Then a 1 byte Extension code, indicating Thumbnail Format + // Then the thumbnail data + + $JFXX_data = unpack( 'a5JFXX/CExtension_Code/a*ThumbData', $jpeg_header_data[$i]['SegData'] ); + return $JFXX_data; + } + } + } + return FALSE; +} + +/****************************************************************************** +* End of Function: get_JFXX +******************************************************************************/ + + + + +/****************************************************************************** +* +* Function: put_JFXX +* +* Description: Creates a new JFXX segment from an array of JFXX data in the +* same format as would be retrieved from get_JFXX, and inserts +* this segment into the supplied JPEG header array +* +* Parameters: jpeg_header_data - a JPEG header data array in the same format +* as from get_jpeg_header_data, into which the +* new JFXX segment will be put +* new_JFXX_array - a JFXX information array in the same format as +* from get_JFXX, to create the new segment +* +* Returns: jpeg_header_data - the JPEG header data array with the new +* JFXX segment added +* +******************************************************************************/ + +function put_JFXX( $jpeg_header_data, $new_JFXX_array ) +{ + // pack the JFXX data into its proper format for a JPEG file + $packed_data = pack( 'a5Ca*',"JFXX\x00", $new_JFXX_array['Extension_Code'], $new_JFXX_array['ThumbData'] ); + + $JFIF_pos = -1; + + //Cycle through the header segments + for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) + { + // If we find an APP0 header, + if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP0" ) == 0 ) + { + // And if it has the JFXX label, + if( strncmp ( $jpeg_header_data[$i]['SegData'], "JFXX\x00", 5) == 0 ) + { + // Found a preexisting JFXX block - Replace it with the new one and return. + $jpeg_header_data[$i]['SegData'] = $packed_data; + return $jpeg_header_data; + } + + // if it has the JFIF label, + if( strncmp ( $jpeg_header_data[$i][SegData], "JFIF\x00", 5) == 0 ) + { + // Found a preexisting JFIF block - Mark it in case we need to insert the JFXX after it + $JFIF_pos = $i; + } + } + } + + + // No preexisting JFXX block found + + // Check if a JFIF segment was found, + if ( $JFIF_pos !== -1 ) + { + // A pre-existing JFIF segment was found, + // insert the new JFXX segment after it. + array_splice($jpeg_header_data, $JFIF_pos +1 , 0, array ( array( "SegType" => 0xE0, + "SegName" => "APP0", + "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE0 ], + "SegData" => $packed_data ) ) ); + + } + else + { + // No pre-existing JFIF segment was found, + // insert a new JFIF and the new JFXX segment at the start of the array. + + // Insert new JFXX segment + array_splice($jpeg_header_data, 0 , 0, array( array( "SegType" => 0xE0, + "SegName" => "APP0", + "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE0 ], + "SegData" => $packed_data ) ) ); + + // Create a new JFIF to be inserted at the start of + // the array, with generic values + $packed_data = pack( 'a5CCCnnCCa*',"JFIF\x00", 1, 2, 1, 72, 72, 0, 0, "" ); + + array_splice($jpeg_header_data, 0 , 0, array( array( "SegType" => 0xE0, + "SegName" => "APP0", + "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE0 ], + "SegData" => $packed_data ) ) ); + } + + + return $jpeg_header_data; +} + +/****************************************************************************** +* End of Function: put_JFIF +******************************************************************************/ + + + +/****************************************************************************** +* +* Function: Interpret_JFXX_to_HTML +* +* Description: Generates html showing the JFXX thumbnail contained in +* a JFXX data array, as retrieved with get_JFXX +* +* Parameters: JFXX_array - a JFXX information array in the same format as +* from get_JFXX, to create the new segment +* filename - the name of the JPEG file being processed ( used +* by the script which displays the JFXX thumbnail) +* +* Returns: output - the Html string +* +******************************************************************************/ + +function Interpret_JFXX_to_HTML( $JFXX_array, $filename ) +{ + $output = ""; + if ( $JFXX_array !== FALSE ) + { + $output .= "

Contains JPEG File Interchange Extension Format (JFXX) Thumbnail

\n"; + switch ( $JFXX_array['Extension_Code'] ) + { + case 0x10 : $output .= "

JFXX Thumbnail is JPEG Encoded

\n"; + + // Change: as of version 1.11 - Changed to make thumbnail link portable across directories + // Build the path of the thumbnail script and its filename parameter to put in a url + $link_str = get_relative_path( dirname(__FILE__) . "/get_JFXX_thumb.php" , getcwd ( ) ); + $link_str .= "?filename="; + $link_str .= get_relative_path( $filename, dirname(__FILE__) ); + + // Add thumbnail link to html + $output .= "\n"; + break; + case 0x11 : $output .= "

JFXX Thumbnail is Encoded 1 byte/pixel

\n"; + $output .= "

Thumbnail Display Not Implemented Yet

\n"; + break; + case 0x13 : $output .= "

JFXX Thumbnail is Encoded 3 bytes/pixel

\n"; + $output .= "

Thumbnail Display Not Implemented Yet

\n"; + break; + default : $output .= "

JFXX Thumbnail is Encoded with Unknown format

\n"; + break; + + // TODO: Implement JFXX one and three bytes per pixel thumbnail decoding + } + + } + + return $output; + +} + +/****************************************************************************** +* End of Function: Interpret_JFXX_to_HTML +******************************************************************************/ + + + + +?> \ No newline at end of file diff --git a/includes/jpeg_metadata_tk/JPEG.php b/includes/jpeg_metadata_tk/JPEG.php new file mode 100644 index 0000000..2004192 --- /dev/null +++ b/includes/jpeg_metadata_tk/JPEG.php @@ -0,0 +1,973 @@ + 1.10 : changed put_jpeg_header_data to check if the data +* being written exists +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* +* License: This file is part of the PHP JPEG Metadata Toolkit. +* +* The PHP JPEG Metadata Toolkit is free software; you can +* redistribute it and/or modify it under the terms of the +* GNU General Public License as published by the Free Software +* Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* The PHP JPEG Metadata Toolkit is distributed in the hope +* that it will be useful, but WITHOUT ANY WARRANTY; without +* even the implied warranty of MERCHANTABILITY or FITNESS +* FOR A PARTICULAR PURPOSE. See the GNU General Public License +* for more details. +* +* You should have received a copy of the GNU General Public +* License along with the PHP JPEG Metadata Toolkit; if not, +* write to the Free Software Foundation, Inc., 59 Temple +* Place, Suite 330, Boston, MA 02111-1307 USA +* +* If you require a different license for commercial or other +* purposes, please contact the author: evan@ozhiker.com +* +******************************************************************************/ + + + + +/****************************************************************************** +* +* Function: get_jpeg_header_data +* +* Description: Reads all the JPEG header segments from an JPEG image file into an +* array +* +* Parameters: filename - the filename of the file to JPEG file to read +* +* Returns: headerdata - Array of JPEG header segments +* FALSE - if headers could not be read +* +******************************************************************************/ + +function get_jpeg_header_data( $filename ) +{ + + // prevent refresh from aborting file operations and hosing file + ignore_user_abort(true); + + + // Attempt to open the jpeg file - the at symbol supresses the error message about + // not being able to open files. The file_exists would have been used, but it + // does not work with files fetched over http or ftp. + $filehnd = @fopen($filename, 'rb'); + + // Check if the file opened successfully + if ( ! $filehnd ) + { + // Could't open the file - exit + error_log( "Could not open file $filename" ); + return FALSE; + } + + + // Read the first two characters + $data = network_safe_fread( $filehnd, 2 ); + + // Check that the first two characters are 0xFF 0xDA (SOI - Start of image) + if ( $data != "\xFF\xD8" ) + { + // No SOI (FF D8) at start of file - This probably isn't a JPEG file - close file and return; + error_log( "This probably is not a JPEG file" ); + fclose($filehnd); + return FALSE; + } + + + // Read the third character + $data = network_safe_fread( $filehnd, 2 ); + + // Check that the third character is 0xFF (Start of first segment header) + if ( $data{0} != "\xFF" ) + { + // NO FF found - close file and return - JPEG is probably corrupted + fclose($filehnd); + return FALSE; + } + + // Flag that we havent yet hit the compressed image data + $hit_compressed_image_data = FALSE; + + + // Cycle through the file until, one of: 1) an EOI (End of image) marker is hit, + // 2) we have hit the compressed image data (no more headers are allowed after data) + // 3) or end of file is hit + + while ( ( $data{1} != "\xD9" ) && (! $hit_compressed_image_data) && ( ! feof( $filehnd ) )) + { + // Found a segment to look at. + // Check that the segment marker is not a Restart marker - restart markers don't have size or data after them + if ( ( ord($data{1}) < 0xD0 ) || ( ord($data{1}) > 0xD7 ) ) + { + // Segment isn't a Restart marker + // Read the next two bytes (size) + $sizestr = network_safe_fread( $filehnd, 2 ); + + // convert the size bytes to an integer + $decodedsize = unpack ("nsize", $sizestr); + + // Save the start position of the data + $segdatastart = ftell( $filehnd ); + + // Read the segment data with length indicated by the previously read size + $segdata = network_safe_fread( $filehnd, $decodedsize['size'] - 2 ); + + + // Store the segment information in the output array + $headerdata[] = array( "SegType" => ord($data{1}), + "SegName" => $GLOBALS[ "JPEG_Segment_Names" ][ ord($data{1}) ], + "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ ord($data{1}) ], + "SegDataStart" => $segdatastart, + "SegData" => $segdata ); + } + + // If this is a SOS (Start Of Scan) segment, then there is no more header data - the compressed image data follows + if ( $data{1} == "\xDA" ) + { + // Flag that we have hit the compressed image data - exit loop as no more headers available. + $hit_compressed_image_data = TRUE; + } + else + { + // Not an SOS - Read the next two bytes - should be the segment marker for the next segment + $data = network_safe_fread( $filehnd, 2 ); + + // Check that the first byte of the two is 0xFF as it should be for a marker + if ( $data{0} != "\xFF" ) + { + // NO FF found - close file and return - JPEG is probably corrupted + fclose($filehnd); + return FALSE; + } + } + } + + // Close File + fclose($filehnd); + // Alow the user to abort from now on + ignore_user_abort(false); + + // Return the header data retrieved + return $headerdata; +} + + +/****************************************************************************** +* End of Function: get_jpeg_header_data +******************************************************************************/ + + + + +/****************************************************************************** +* +* Function: put_jpeg_header_data +* +* Description: Writes JPEG header data into a JPEG file. Takes an array in the +* same format as from get_jpeg_header_data, and combines it with +* the image data of an existing JPEG file, to create a new JPEG file +* WARNING: As this function will replace all JPEG headers, +* including SOF etc, it is best to read the jpeg headers +* from a file, alter them, then put them back on the same +* file. If a SOF segment wer to be transfered from one +* file to another, the image could become unreadable unless +* the images were idenical size and configuration +* +* +* Parameters: old_filename - the JPEG file from which the image data will be retrieved +* new_filename - the name of the new JPEG to create (can be same as old_filename) +* jpeg_header_data - a JPEG header data array in the same format +* as from get_jpeg_header_data +* +* Returns: TRUE - on Success +* FALSE - on Failure +* +******************************************************************************/ + +function put_jpeg_header_data( $old_filename, $new_filename, $jpeg_header_data ) +{ + + // Change: added check to ensure data exists, as of revision 1.10 + // Check if the data to be written exists + if ( $jpeg_header_data == FALSE ) + { + // Data to be written not valid - abort + return FALSE; + } + + // extract the compressed image data from the old file + $compressed_image_data = get_jpeg_image_data( $old_filename ); + + // Check if the extraction worked + if ( ( $compressed_image_data === FALSE ) || ( $compressed_image_data === NULL ) ) + { + // Couldn't get image data from old file + return FALSE; + } + + + // Cycle through new headers + foreach ($jpeg_header_data as $segno => $segment) + { + // Check that this header is smaller than the maximum size + if ( strlen($segment['SegData']) > 0xfffd ) + { + // Could't open the file - exit + error_log( "A Header is too large to fit in JPEG segment" ); + return FALSE; + } + } + + ignore_user_abort(true); ## prevent refresh from aborting file operations and hosing file + + + // Attempt to open the new jpeg file + $newfilehnd = @fopen($new_filename, 'wb'); + // Check if the file opened successfully + if ( ! $newfilehnd ) + { + // Could't open the file - exit + error_log( "Could not open file $new_filename" ); + return FALSE; + } + + // Write SOI + fwrite( $newfilehnd, "\xFF\xD8" ); + + // Cycle through new headers, writing them to the new file + foreach ($jpeg_header_data as $segno => $segment) + { + + // Write segment marker + fwrite( $newfilehnd, sprintf( "\xFF%c", $segment['SegType'] ) ); + + // Write segment size + fwrite( $newfilehnd, pack( "n", strlen($segment['SegData']) + 2 ) ); + + // Write segment data + fwrite( $newfilehnd, $segment['SegData'] ); + } + + // Write the compressed image data + fwrite( $newfilehnd, $compressed_image_data ); + + // Write EOI + fwrite( $newfilehnd, "\xFF\xD9" ); + + // Close File + fclose($newfilehnd); + + // Alow the user to abort from now on + ignore_user_abort(false); + + + return TRUE; + +} + +/****************************************************************************** +* End of Function: put_jpeg_header_data +******************************************************************************/ + + + +/****************************************************************************** +* +* Function: get_jpeg_Comment +* +* Description: Retreives the contents of the JPEG Comment (COM = 0xFFFE) segment if one +* exists +* +* Parameters: jpeg_header_data - the JPEG header data, as retrieved +* from the get_jpeg_header_data function +* +* Returns: string - Contents of the Comment segement +* FALSE - if the comment segment couldnt be found +* +******************************************************************************/ + +function get_jpeg_Comment( $jpeg_header_data ) +{ + //Cycle through the header segments until COM is found or we run out of segments + $i = 0; + while ( ( $i < count( $jpeg_header_data) ) && ( $jpeg_header_data[$i]['SegName'] != "COM" ) ) + { + $i++; + } + + // Check if a COM segment has been found + if ( $i < count( $jpeg_header_data) ) + { + // A COM segment was found, return it's contents + return $jpeg_header_data[$i]['SegData']; + } + else + { + // No COM segment found + return FALSE; + } +} + +/****************************************************************************** +* End of Function: get_jpeg_Comment +******************************************************************************/ + + +/****************************************************************************** +* +* Function: put_jpeg_Comment +* +* Description: Creates a new JPEG Comment segment from a string, and inserts +* this segment into the supplied JPEG header array +* +* Parameters: jpeg_header_data - a JPEG header data array in the same format +* as from get_jpeg_header_data, into which the +* new Comment segment will be put +* $new_Comment - a string containing the new Comment +* +* Returns: jpeg_header_data - the JPEG header data array with the new +* JPEG Comment segment added +* +******************************************************************************/ + +function put_jpeg_Comment( $jpeg_header_data, $new_Comment ) +{ + //Cycle through the header segments + for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) + { + // If we find an COM header, + if ( strcmp ( $jpeg_header_data[$i]['SegName'], "COM" ) == 0 ) + { + // Found a preexisting Comment block - Replace it with the new one and return. + $jpeg_header_data[$i]['SegData'] = $new_Comment; + return $jpeg_header_data; + } + } + + + + // No preexisting Comment block found, find where to put it by searching for the highest app segment + $i = 0; + while ( ( $i < count( $jpeg_header_data ) ) && ( $jpeg_header_data[$i]["SegType"] >= 0xE0 ) ) + { + $i++; + } + + + // insert a Comment segment new at the position found of the header data. + array_splice($jpeg_header_data, $i , 0, array( array( "SegType" => 0xFE, + "SegName" => $GLOBALS[ "JPEG_Segment_Names" ][ 0xFE ], + "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xFE ], + "SegData" => $new_Comment ) ) ); + return $jpeg_header_data; +} + +/****************************************************************************** +* End of Function: put_jpeg_Comment +******************************************************************************/ + + + + +/****************************************************************************** +* +* Function: Interpret_Comment_to_HTML +* +* Description: Generates html showing the contents of any JPEG Comment segment +* +* Parameters: jpeg_header_data - the JPEG header data, as retrieved +* from the get_jpeg_header_data function +* +* Returns: output - the HTML +* +******************************************************************************/ + +function Interpret_Comment_to_HTML( $jpeg_header_data ) +{ + // Create a string to receive the output + $output = ""; + + // read the comment segment + $comment = get_jpeg_Comment( $jpeg_header_data ); + + // Check if the comment segment was valid + if ( $comment !== FALSE ) + { + // Comment exists - add it to the output + $output .= "

JPEG Comment

\n"; + $output .= "

$comment

\n"; + } + + // Return the result + return $output; +} + +/****************************************************************************** +* End of Function: Interpret_Comment_to_HTML +******************************************************************************/ + + + + +/****************************************************************************** +* +* Function: get_jpeg_intrinsic_values +* +* Description: Retreives information about the intrinsic characteristics of the +* jpeg image, such as Bits per Component, Height and Width. +* +* Parameters: jpeg_header_data - the JPEG header data, as retrieved +* from the get_jpeg_header_data function +* +* Returns: array - An array containing the intrinsic JPEG values +* FALSE - if the comment segment couldnt be found +* +******************************************************************************/ + +function get_jpeg_intrinsic_values( $jpeg_header_data ) +{ + // Create a blank array for the output + $Outputarray = array( ); + + //Cycle through the header segments until Start Of Frame (SOF) is found or we run out of segments + $i = 0; + while ( ( $i < count( $jpeg_header_data) ) && ( substr( $jpeg_header_data[$i]['SegName'], 0, 3 ) != "SOF" ) ) + { + $i++; + } + + // Check if a SOF segment has been found + if ( substr( $jpeg_header_data[$i]['SegName'], 0, 3 ) == "SOF" ) + { + // SOF segment was found, extract the information + + $data = $jpeg_header_data[$i]['SegData']; + + // First byte is Bits per component + $Outputarray['Bits per Component'] = ord( $data{0} ); + + // Second and third bytes are Image Height + $Outputarray['Image Height'] = ord( $data{ 1 } ) * 256 + ord( $data{ 2 } ); + + // Forth and fifth bytes are Image Width + $Outputarray['Image Width'] = ord( $data{ 3 } ) * 256 + ord( $data{ 4 } ); + + // Sixth byte is number of components + $numcomponents = ord( $data{ 5 } ); + + // Following this is a table containing information about the components + for( $i = 0; $i < $numcomponents; $i++ ) + { + $Outputarray['Components'][] = array ( 'Component Identifier' => ord( $data{ 6 + $i * 3 } ), + 'Horizontal Sampling Factor' => ( ord( $data{ 7 + $i * 3 } ) & 0xF0 ) / 16, + 'Vertical Sampling Factor' => ( ord( $data{ 7 + $i * 3 } ) & 0x0F ), + 'Quantization table destination selector' => ord( $data{ 8 + $i * 3 } ) ); + } + } + else + { + // Couldn't find Start Of Frame segment, hence can't retrieve info + return FALSE; + } + + return $Outputarray; +} + + +/****************************************************************************** +* End of Function: get_jpeg_intrinsic_values +******************************************************************************/ + + + + + +/****************************************************************************** +* +* Function: Interpret_intrinsic_values_to_HTML +* +* Description: Generates html showing some of the intrinsic JPEG values which +* were retrieved with the get_jpeg_intrinsic_values function +* +* Parameters: values - the JPEG intrinsic values, as read from get_jpeg_intrinsic_values +* +* Returns: OutputStr - A string containing the HTML +* +******************************************************************************/ + +function Interpret_intrinsic_values_to_HTML( $values ) +{ + // Check values are valid + if ( $values != FALSE ) + { + // Write Heading + $OutputStr = "

Intrinsic JPEG Information

\n"; + + // Create Table + $OutputStr .= "\n"; + + // Put image height and width into table + $OutputStr .= "\n"; + $OutputStr .= "\n"; + + // Put colour depth into table + if ( count( $values['Components'] ) == 1 ) + { + $OutputStr .= "\n"; + } + else + { + $OutputStr .= "\n"; + } + + // Close Table + $OutputStr .= "
Image Height" . $values['Image Height'] . " pixels
Image Width" . $values['Image Width'] . " pixels
Colour Depth" . $values['Bits per Component'] . " bit Monochrome
Colour Depth" . ($values['Bits per Component'] * count( $values['Components'] ) ) . " bit
\n"; + + // Return html + return $OutputStr; + } +} + +/****************************************************************************** +* End of Function: Interpret_intrinsic_values_to_HTML +******************************************************************************/ + + + + + + + +/****************************************************************************** +* +* Function: get_jpeg_image_data +* +* Description: Retrieves the compressed image data part of the JPEG file +* +* Parameters: filename - the filename of the JPEG file to read +* +* Returns: compressed_data - A string containing the compressed data +* FALSE - if retrieval failed +* +******************************************************************************/ + +function get_jpeg_image_data( $filename ) +{ + + // prevent refresh from aborting file operations and hosing file + ignore_user_abort(true); + + // Attempt to open the jpeg file + $filehnd = @fopen($filename, 'rb'); + + // Check if the file opened successfully + if ( ! $filehnd ) + { + // Could't open the file - exit + return FALSE; + } + + + // Read the first two characters + $data = network_safe_fread( $filehnd, 2 ); + + // Check that the first two characters are 0xFF 0xDA (SOI - Start of image) + if ( $data != "\xFF\xD8" ) + { + // No SOI (FF D8) at start of file - close file and return; + fclose($filehnd); + return FALSE; + } + + + + // Read the third character + $data = network_safe_fread( $filehnd, 2 ); + + // Check that the third character is 0xFF (Start of first segment header) + if ( $data{0} != "\xFF" ) + { + // NO FF found - close file and return + fclose($filehnd); + return; + } + + // Flag that we havent yet hit the compressed image data + $hit_compressed_image_data = FALSE; + + + // Cycle through the file until, one of: 1) an EOI (End of image) marker is hit, + // 2) we have hit the compressed image data (no more headers are allowed after data) + // 3) or end of file is hit + + while ( ( $data{1} != "\xD9" ) && (! $hit_compressed_image_data) && ( ! feof( $filehnd ) )) + { + // Found a segment to look at. + // Check that the segment marker is not a Restart marker - restart markers don't have size or data after them + if ( ( ord($data{1}) < 0xD0 ) || ( ord($data{1}) > 0xD7 ) ) + { + // Segment isn't a Restart marker + // Read the next two bytes (size) + $sizestr = network_safe_fread( $filehnd, 2 ); + + // convert the size bytes to an integer + $decodedsize = unpack ("nsize", $sizestr); + + // Read the segment data with length indicated by the previously read size + $segdata = network_safe_fread( $filehnd, $decodedsize['size'] - 2 ); + } + + // If this is a SOS (Start Of Scan) segment, then there is no more header data - the compressed image data follows + if ( $data{1} == "\xDA" ) + { + // Flag that we have hit the compressed image data - exit loop after reading the data + $hit_compressed_image_data = TRUE; + + // read the rest of the file in + // Can't use the filesize function to work out + // how much to read, as it won't work for files being read by http or ftp + // So instead read 1Mb at a time till EOF + + $compressed_data = ""; + do + { + $compressed_data .= network_safe_fread( $filehnd, 1048576 ); + } while( ! feof( $filehnd ) ); + + // Strip off EOI and anything after + $EOI_pos = strpos( $compressed_data, "\xFF\xD9" ); + $compressed_data = substr( $compressed_data, 0, $EOI_pos ); + } + else + { + // Not an SOS - Read the next two bytes - should be the segment marker for the next segment + $data = network_safe_fread( $filehnd, 2 ); + + // Check that the first byte of the two is 0xFF as it should be for a marker + if ( $data{0} != "\xFF" ) + { + // Problem - NO FF foundclose file and return"; + fclose($filehnd); + return; + } + } + } + + // Close File + fclose($filehnd); + + // Alow the user to abort from now on + ignore_user_abort(false); + + + // Return the compressed data if it was found + if ( $hit_compressed_image_data ) + { + return $compressed_data; + } + else + { + return FALSE; + } +} + + +/****************************************************************************** +* End of Function: get_jpeg_image_data +******************************************************************************/ + + + + + + + +/****************************************************************************** +* +* Function: Generate_JPEG_APP_Segment_HTML +* +* Description: Generates html showing information about the Application (APP) +* segments which are present in the JPEG file +* +* Parameters: jpeg_header_data - the JPEG header data, as retrieved +* from the get_jpeg_header_data function +* +* Returns: output - A string containing the HTML +* +******************************************************************************/ + +function Generate_JPEG_APP_Segment_HTML( $jpeg_header_data ) +{ + if ( $jpeg_header_data == FALSE ) + { + return ""; + } + + + // Write Heading + $output = "

Application Metadata Segments

\n"; + + // Create table + $output .= "\n"; + + + // Cycle through each segment in the array + + foreach( $jpeg_header_data as $jpeg_header ) + { + + // Check if the segment is a APP segment + + if ( ( $jpeg_header['SegType'] >= 0xE0 ) && ( $jpeg_header['SegType'] <= 0xEF ) ) + { + // This is an APP segment + + // Read APP Segment Name - a Null terminated string at the start of the segment + $seg_name = strtok($jpeg_header['SegData'], "\x00"); + + // Some Segment names are either too long or not meaningfull, so + // we should clean them up + + if ( $seg_name == "http://ns.adobe.com/xap/1.0/" ) + { + $seg_name = "XAP/RDF (\"http://ns.adobe.com/xap/1.0/\")"; + } + elseif ( $seg_name == "Photoshop 3.0" ) + { + $seg_name = "Photoshop IRB (\"Photoshop 3.0\")"; + } + elseif ( ( strncmp ( $seg_name, "[picture info]", 14) == 0 ) || + ( strncmp ( $seg_name, "\x0a\x09\x09\x09\x09[picture info]", 19) == 0 ) ) + { + $seg_name = "[picture info]"; + } + elseif ( strncmp ( $seg_name, "Type=", 5) == 0 ) + { + $seg_name = "Epson Info"; + } + elseif ( ( strncmp ( $seg_name, "HHHHHHHHHHHHHHH", 15) == 0 ) || + ( strncmp ( $seg_name, "@s33", 5) == 0 ) ) + { + $seg_name = "HP segment full of \"HHHHH\""; + } + + + // Clean the segment name so it doesn't cause problems with HTML + $seg_name = htmlentities( $seg_name ); + + // Output a Table row containing this APP segment + $output .= "\n"; + } + } + + // Close the table + $output .= "
$seg_name" . $jpeg_header['SegName'] . "" . strlen( $jpeg_header['SegData']). " bytes
\n"; + + // Return the HTML + return $output; +} + + +/****************************************************************************** +* End of Function: Generate_JPEG_APP_Segment_HTML +******************************************************************************/ + + + + +/****************************************************************************** +* +* Function: network_safe_fread +* +* Description: Retrieves data from a file. This function is required since +* the fread function will not always return the requested number +* of characters when reading from a network stream or pipe +* +* Parameters: file_handle - the handle of a file to read from +* length - the number of bytes requested +* +* Returns: data - the data read from the file. may be less than the number +* requested if EOF was hit +* +******************************************************************************/ + +function network_safe_fread( $file_handle, $length ) +{ + // Create blank string to receive data + $data = ""; + + // Keep reading data from the file until either EOF occurs or we have + // retrieved the requested number of bytes + + while ( ( !feof( $file_handle ) ) && ( strlen($data) < $length ) ) + { + $data .= fread( $file_handle, $length-strlen($data) ); + } + + // return the data read + return $data; +} + +/****************************************************************************** +* End of Function: network_safe_fread +******************************************************************************/ + + + + +/****************************************************************************** +* Global Variable: JPEG_Segment_Names +* +* Contents: The names of the JPEG segment markers, indexed by their marker number +* +******************************************************************************/ + +$GLOBALS[ "JPEG_Segment_Names" ] = array( + +0xC0 => "SOF0", 0xC1 => "SOF1", 0xC2 => "SOF2", 0xC3 => "SOF4", +0xC5 => "SOF5", 0xC6 => "SOF6", 0xC7 => "SOF7", 0xC8 => "JPG", +0xC9 => "SOF9", 0xCA => "SOF10", 0xCB => "SOF11", 0xCD => "SOF13", +0xCE => "SOF14", 0xCF => "SOF15", +0xC4 => "DHT", 0xCC => "DAC", + +0xD0 => "RST0", 0xD1 => "RST1", 0xD2 => "RST2", 0xD3 => "RST3", +0xD4 => "RST4", 0xD5 => "RST5", 0xD6 => "RST6", 0xD7 => "RST7", + +0xD8 => "SOI", 0xD9 => "EOI", 0xDA => "SOS", 0xDB => "DQT", +0xDC => "DNL", 0xDD => "DRI", 0xDE => "DHP", 0xDF => "EXP", + +0xE0 => "APP0", 0xE1 => "APP1", 0xE2 => "APP2", 0xE3 => "APP3", +0xE4 => "APP4", 0xE5 => "APP5", 0xE6 => "APP6", 0xE7 => "APP7", +0xE8 => "APP8", 0xE9 => "APP9", 0xEA => "APP10", 0xEB => "APP11", +0xEC => "APP12", 0xED => "APP13", 0xEE => "APP14", 0xEF => "APP15", + + +0xF0 => "JPG0", 0xF1 => "JPG1", 0xF2 => "JPG2", 0xF3 => "JPG3", +0xF4 => "JPG4", 0xF5 => "JPG5", 0xF6 => "JPG6", 0xF7 => "JPG7", +0xF8 => "JPG8", 0xF9 => "JPG9", 0xFA => "JPG10", 0xFB => "JPG11", +0xFC => "JPG12", 0xFD => "JPG13", + +0xFE => "COM", 0x01 => "TEM", 0x02 => "RES", + +); + +/****************************************************************************** +* End of Global Variable: JPEG_Segment_Names +******************************************************************************/ + + +/****************************************************************************** +* Global Variable: JPEG_Segment_Descriptions +* +* Contents: The descriptions of the JPEG segment markers, indexed by their marker number +* +******************************************************************************/ + +$GLOBALS[ "JPEG_Segment_Descriptions" ] = array( + +/* JIF Marker byte pairs in JPEG Interchange Format sequence */ +0xC0 => "Start Of Frame (SOF) Huffman - Baseline DCT", +0xC1 => "Start Of Frame (SOF) Huffman - Extended sequential DCT", +0xC2 => "Start Of Frame Huffman - Progressive DCT (SOF2)", +0xC3 => "Start Of Frame Huffman - Spatial (sequential) lossless (SOF3)", +0xC5 => "Start Of Frame Huffman - Differential sequential DCT (SOF5)", +0xC6 => "Start Of Frame Huffman - Differential progressive DCT (SOF6)", +0xC7 => "Start Of Frame Huffman - Differential spatial (SOF7)", +0xC8 => "Start Of Frame Arithmetic - Reserved for JPEG extensions (JPG)", +0xC9 => "Start Of Frame Arithmetic - Extended sequential DCT (SOF9)", +0xCA => "Start Of Frame Arithmetic - Progressive DCT (SOF10)", +0xCB => "Start Of Frame Arithmetic - Spatial (sequential) lossless (SOF11)", +0xCD => "Start Of Frame Arithmetic - Differential sequential DCT (SOF13)", +0xCE => "Start Of Frame Arithmetic - Differential progressive DCT (SOF14)", +0xCF => "Start Of Frame Arithmetic - Differential spatial (SOF15)", +0xC4 => "Define Huffman Table(s) (DHT)", +0xCC => "Define Arithmetic coding conditioning(s) (DAC)", + +0xD0 => "Restart with modulo 8 count 0 (RST0)", +0xD1 => "Restart with modulo 8 count 1 (RST1)", +0xD2 => "Restart with modulo 8 count 2 (RST2)", +0xD3 => "Restart with modulo 8 count 3 (RST3)", +0xD4 => "Restart with modulo 8 count 4 (RST4)", +0xD5 => "Restart with modulo 8 count 5 (RST5)", +0xD6 => "Restart with modulo 8 count 6 (RST6)", +0xD7 => "Restart with modulo 8 count 7 (RST7)", + +0xD8 => "Start of Image (SOI)", +0xD9 => "End of Image (EOI)", +0xDA => "Start of Scan (SOS)", +0xDB => "Define quantization Table(s) (DQT)", +0xDC => "Define Number of Lines (DNL)", +0xDD => "Define Restart Interval (DRI)", +0xDE => "Define Hierarchical progression (DHP)", +0xDF => "Expand Reference Component(s) (EXP)", + +0xE0 => "Application Field 0 (APP0) - usually JFIF or JFXX", +0xE1 => "Application Field 1 (APP1) - usually EXIF or XMP/RDF", +0xE2 => "Application Field 2 (APP2) - usually Flashpix", +0xE3 => "Application Field 3 (APP3)", +0xE4 => "Application Field 4 (APP4)", +0xE5 => "Application Field 5 (APP5)", +0xE6 => "Application Field 6 (APP6)", +0xE7 => "Application Field 7 (APP7)", + +0xE8 => "Application Field 8 (APP8)", +0xE9 => "Application Field 9 (APP9)", +0xEA => "Application Field 10 (APP10)", +0xEB => "Application Field 11 (APP11)", +0xEC => "Application Field 12 (APP12) - usually [picture info]", +0xED => "Application Field 13 (APP13) - usually photoshop IRB / IPTC", +0xEE => "Application Field 14 (APP14)", +0xEF => "Application Field 15 (APP15)", + + +0xF0 => "Reserved for JPEG extensions (JPG0)", +0xF1 => "Reserved for JPEG extensions (JPG1)", +0xF2 => "Reserved for JPEG extensions (JPG2)", +0xF3 => "Reserved for JPEG extensions (JPG3)", +0xF4 => "Reserved for JPEG extensions (JPG4)", +0xF5 => "Reserved for JPEG extensions (JPG5)", +0xF6 => "Reserved for JPEG extensions (JPG6)", +0xF7 => "Reserved for JPEG extensions (JPG7)", +0xF8 => "Reserved for JPEG extensions (JPG8)", +0xF9 => "Reserved for JPEG extensions (JPG9)", +0xFA => "Reserved for JPEG extensions (JPG10)", +0xFB => "Reserved for JPEG extensions (JPG11)", +0xFC => "Reserved for JPEG extensions (JPG12)", +0xFD => "Reserved for JPEG extensions (JPG13)", + +0xFE => "Comment (COM)", +0x01 => "For temp private use arith code (TEM)", +0x02 => "Reserved (RES)", + +); + +/****************************************************************************** +* End of Global Variable: JPEG_Segment_Descriptions +******************************************************************************/ + + + +?> diff --git a/includes/jpeg_metadata_tk/Makernotes/Pentax.php b/includes/jpeg_metadata_tk/Makernotes/Pentax.php new file mode 100644 index 0000000..10194a6 --- /dev/null +++ b/includes/jpeg_metadata_tk/Makernotes/Pentax.php @@ -0,0 +1,353 @@ + array( 'Name' => "Capture Mode", + 'Type' => "Lookup", + 0 => "Auto", + 1 => "Night-scene", + 2 => "Manual", + 4 => "Multiple" ), + +0x0002 => array( 'Name' => "Quality Level", + 'Type' => "Lookup", + 0 => "Good", + 1 => "Better", + 2 => "Best" ), + +0x0003 => array( 'Name' => "Focus Mode", + 'Type' => "Lookup", + 2 => "Custom", + 3 => "Auto" ), + +0x0004 => array( 'Name' => "Flash Mode", + 'Type' => "Lookup", + 1 => "Auto", + 2 => "Flash on", + 4 => "Flash off", + 6 => "Red-eye Reduction" ), + +0x0007 => array( 'Name' => "White Balance", + 'Type' => "Lookup", + 0 => "Auto", + 1 => "Daylight", + 2 => "Shade", + 3 => "Tungsten", + 4 => "Fluorescent", + 5 => "Manual" ), + + +0x000a => array( 'Name' => "Digital Zoom", + 'Type' => "Numeric", + 'Units' => " (0 = Off)" ), + +0x000b => array( 'Name' => "Sharpness", + 'Type' => "Lookup", + 0 => "Normal", + 1 => "Soft", + 2 => "Hard" ), + +0x000c => array( 'Name' => "Contrast", + 'Type' => "Lookup", + 0 => "Normal", + 1 => "Low", + 2 => "High" ), + +0x000d => array( 'Name' => "Saturation", + 'Type' => "Lookup", + 0 => "Normal", + 1 => "Low", + 2 => "High" ), + +0x0014 => array( 'Name' => "ISO Speed", + 'Type' => "Lookup", + 10 => "100", + 16 => "200", + 100 => "100", + 200 => "200" ), + +0x0017 => array( 'Name' => "Colour", + 'Type' => "Lookup", + 1 => "Normal", + 2 => "Black & White", + 3 => "Sepia" ), + +0x0e00 => array( 'Name' => "Print Image Matching Info", + 'Type' => "PIM" ), + +0x1000 => array( 'Name' => "Time Zone", + 'Type' => "String" ), + +0x1001 => array( 'Name' => "Daylight Savings", + 'Type' => "String" ), + + + + + +); + +/****************************************************************************** +* End of Global Variable: IFD_Tag_Definitions, Pentax +******************************************************************************/ + + + +?> diff --git a/includes/jpeg_metadata_tk/Makernotes/agfa.php b/includes/jpeg_metadata_tk/Makernotes/agfa.php new file mode 100644 index 0000000..214c70d --- /dev/null +++ b/includes/jpeg_metadata_tk/Makernotes/agfa.php @@ -0,0 +1,117 @@ + diff --git a/includes/jpeg_metadata_tk/Makernotes/canon.php b/includes/jpeg_metadata_tk/Makernotes/canon.php new file mode 100644 index 0000000..a261506 --- /dev/null +++ b/includes/jpeg_metadata_tk/Makernotes/canon.php @@ -0,0 +1,748 @@ + $value ) + { + // Check that the value exists + if ( $value !== NULL ) + { + // Process the settings according to their offset + if ( $offset == 0 ) + { + // Do Not Show this Field ( Number of Bytes in Tag ) + } + else if ( $offset == 2 ) + { + if ( $value == 0 ) + { + $output_str .= "Self timer not used\n"; + } + else + { + $output_str .= "Self timer length : ". ($value/10) . " seconds\n"; + } + } + else if ( ( $offset == 23 ) && ( $Exif_Tag['Data'][25] != 0 )) + { + $output_str .= "Maximum Focal Length of Lens: " . ($value / $Exif_Tag['Data'][25]) . "mm\n"; + } + else if ( ( $offset == 24 ) && ( $Exif_Tag['Data'][25] != 0 )) + { + $output_str .= "Minimum Focal Length of Lens: " . ($value / $Exif_Tag['Data'][25]) . "mm\n"; + } + else if ( $offset == 25 ) + { + // Do Not Show this Field ( Focal Length units per mm ) + } + else if ( $offset == 29 ) + { + if ( $value & 0x4000 == 0x4000 ) + { + $output_str .= "External E-TTL Flash\n"; + } + if ( $value & 0x2000 == 0x2000 ) + { + $output_str .= "Internal Flash\n"; + } + if ( $value & 0x0800 == 0x0800 ) + { + $output_str .= "Flash FP sync used\n"; + } + if ( $value & 0x0080 == 0x0080 ) + { + $output_str .= "Second (Rear) curtain flash sync used\n"; + } + if ( $value & 0x0008 == 0x0008 ) + { + $output_str .= "Flash FP sync enabled\n"; + } + + } + else if ( array_key_exists( $offset, $GLOBALS[ "Canon_Camera_Settings_1_Tag_Values" ] ) ) + { + if ( array_key_exists( $value, $GLOBALS[ "Canon_Camera_Settings_1_Tag_Values" ][$offset] ) ) + { + $output_str .= $GLOBALS[ "Canon_Camera_Settings_1_Tag_Values" ][$offset]['Name'] . ": " . $GLOBALS[ "Canon_Camera_Settings_1_Tag_Values" ][$offset][$value] . "\n"; + } + else + { + if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == FALSE ) + { + $output_str .= $GLOBALS[ "Canon_Camera_Settings_1_Tag_Values" ][$offset]['Name'] . ": Unknown Value ($value)\n"; + } + } + } + else + { + if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == FALSE ) + { + // Unknown Canon camera setting + $output_str .= " Unknown Setting ($offset), value: $value\n"; + } + } + } + + } + // Return the text string + return $output_str; + break; + + + // CAMERA SETTINGS 2 + case 4: + // Create an output string + $output_str = ""; + + // Cycle through each of the camera settings Values + foreach( $Exif_Tag['Data'] as $offset => $value ) + { + // Check that the value exists + if ( $value !== NULL ) + { + // Process the settings according to their offset + if ( $offset == 0 ) + { + // Do Not Show this Field ( Number of Bytes in Tag ) + } + else if ( $offset == 9 ) + { + $output_str .= "Sequence Number in a continuous burst : $value\n"; + } + else if ( $offset == 14 ) + { + $output_str .= "Number of Focus Points Available: ". ( ( $value & 0xF000 ) / 0x1000 ) . "\n"; + + if ( $value & 0x0004 == 0x0004 ) + { + $output_str .= "Left Focus Point Used\n"; + } + if ( $value & 0x0002 == 0x0002 ) + { + $output_str .= "Centre Focus Point Used\n"; + } + if ( $value & 0x0001 == 0x0001 ) + { + $output_str .= "Right Focus Point Used\n"; + } + } + else if ( $offset == 19 ) + { + $output_str .= "Subject distance: $value (units either mm or cm)\n"; + } + else if ( array_key_exists( $offset, $GLOBALS[ "Canon_Camera_Settings_2_Tag_Values" ] ) ) + { + if ( array_key_exists( $value, $GLOBALS[ "Canon_Camera_Settings_2_Tag_Values" ][$offset] ) ) + { + $output_str .= $GLOBALS[ "Canon_Camera_Settings_2_Tag_Values" ][$offset]['Name'] . ": " . $GLOBALS[ "Canon_Camera_Settings_2_Tag_Values" ][$offset][$value] . "\n"; + } + else + { + if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == FALSE ) + { + $output_str .= $GLOBALS[ "Canon_Camera_Settings_2_Tag_Values" ][$offset]['Name'] . ": Unknown Value ($value)\n"; + } + } + } + else + { + if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == FALSE ) + { + $output_str .= " Unknown Setting ($offset), value: $value\n"; + } + } + } + + } + // Return the text string + return $output_str; + break; + + + // Serial Number + case 12: + $output_str = sprintf ( "%04X%05d", (($Exif_Tag['Data'][0] & 0xFF00)/256), ($Exif_Tag['Data'][0] & 0x00FF) ); + break; + + + // Custom Functions + case 15: + // Create an output string + $output_str = ""; + + // The size element is the first of the value array + // get rid of it + $tmparray = $Exif_Tag['Data']; + array_shift ( $tmparray ); + + // Cycle through each of the custom functions + foreach( $tmparray as $valorder => $value ) + { + // Figure out the function number and value + $funcno = ( $value & 0xFF00 ) / 256; + $funcval = $value & 0x00FF; + + // Check if the function exists in the lookup table of custom functions + if ( array_key_exists( $funcno, $GLOBALS[ "Canon_Custom_Functions_Tag_Values" ] ) ) + { + // Function Exists in lookup table, + // Check if value exists for this function in the lookup table + if ( array_key_exists( $funcval, $GLOBALS[ "Canon_Custom_Functions_Tag_Values" ][$funcno] ) ) + { + // Value exists - Add it to the output text + $output_str .= $GLOBALS[ "Canon_Custom_Functions_Tag_Values" ][$funcno]['Name'] . ": " . $GLOBALS[ "Canon_Custom_Functions_Tag_Values" ][$funcno][$funcval] . "\n"; + } + else + { + if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == FALSE ) + { + // Value doesn't exist - Add a message to the output text + $output_str .= $GLOBALS[ "Canon_Custom_Functions_Tag_Values" ][$funcno]['Name'] . ": Unknown Value ($value)\n"; + } + } + } + else + { + if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == FALSE ) + { + // Function doesn't exist in lookup table - add a message to the output text + $output_str .= "Unknown Custom Function ($funcno), value: $funcval\n"; + } + } + } + // Return the resulting string + return $output_str; + break; + + default : + return FALSE; + } + + return FALSE; +} + +/****************************************************************************** +* End of Function: get_Canon_Text_Value +******************************************************************************/ + + + + + + + + + + + + + + + + +/****************************************************************************** +* Global Variable: IFD_Tag_Definitions, Canon +* +* Contents: This global variable provides definitions of the known Canon +* Makernote tags, indexed by their tag number. +* +******************************************************************************/ + +$GLOBALS[ "IFD_Tag_Definitions" ]['Canon'] = array( + +1 => array( 'Name' => "Camera Settings 1", + 'Type' => "Special" ), + +4 => array( 'Name' => "Camera Settings 2", + 'Type' => "Special" ), + +6 => array( 'Name' => "Image Type", + 'Type' => "String" ), + +7 => array( 'Name' => "Firmware Version", + 'Type' => "String" ), + +8 => array( 'Name' => "Image Number", + 'Type' => "Numeric" ), + +9 => array( 'Name' => "Owner Name", + 'Type' => "String" ), + +12 => array( 'Name' => "Camera Serial Number", + 'Type' => "Special" ), + +15 => array( 'Name' => "Custom Functions", + 'Type' => "Special" ) + +); + + +/****************************************************************************** +* End of Global Variable: IFD_Tag_Definitions, Canon +******************************************************************************/ + + + + + + + + + + + + + + + + + + +/****************************************************************************** +* Global Variable: Canon_Camera_Settings_1_Tag_Values +* +* Contents: This global variable provides definitions for the Canon Camera +* Settings 1 Makernote tag, indexed by their offset. +* +******************************************************************************/ + +$GLOBALS[ "Canon_Camera_Settings_1_Tag_Values" ] = array( + +1 => array( 'Name' => "Macro Mode", + 1 => "Macro", + 2 => "Normal ( Not Macro )" ), + +3 => array( 'Name' => "Quality", + 2 => "Normal", + 3 => "Fine", + 5 => "Superfine" ), + +4 => array( 'Name' => "Flash Mode", + 0 => "Flash Not Fired", + 1 => "Auto", + 2 => "On", + 3 => "Red Eye Reduction", + 4 => "Slow Synchro", + 5 => "Auto + Red Eye Reduction", + 6 => "On + Red Eye Reduction", + 16 => "External Flash" ), + +5 => array( 'Name' => "Continuous drive mode", + 0 => "Single Frame or Timer Mode", + 1 => "Continuous" ), + +7 => array( 'Name' => "Focus Mode", + 0 => "One-Shot", + 1 => "AI Servo", + 2 => "AI Focus", + 3 => "Manual Focus", + 4 => "Single", + 5 => "Continuous", + 6 => "Manual Focus" ), + +10 => array( 'Name' => "Image Size", + 0 => "Large", + 1 => "Medium", + 2 => "Small" ), + +11 => array( 'Name' => "Easy shooting Mode", + 0 => "Full Auto", + 1 => "Manual", + 2 => "Landscape", + 3 => "Fast Shutter", + 4 => "Slow Shutter", + 5 => "Night", + 6 => "Black & White", + 7 => "Sepia", + 8 => "Portrait", + 9 => "Sports", + 10 => "Macro / Close-Up", + 11 => "Pan Focus" ), + + +12 => array( 'Name' => "Digital Zoom", + 0 => "No Digital Zoom", + 1 => "2x", + 2 => "4x" ), + +13 => array( 'Name' => "Contrast", + 0 => "Normal", + 1 => "High", + 65535 => "Low" ), + +14 => array( 'Name' => "Saturation", + 0 => "Normal", + 1 => "High", + 65535 => "Low" ), + +15 => array( 'Name' => "Sharpness", + 0 => "Normal", + 1 => "High", + 65535 => "Low" ), + +16 => array( 'Name' => "ISO Speed", + 0 => "Check ISOSpeedRatings EXIF tag for ISO Speed", + 15 => "Auto ISO", + 16 => "ISO 50", + 17 => "ISO 100", + 18 => "ISO 200", + 19 => "ISO 400" ), + +17 => array( 'Name' => "Metering Mode", + 3 => "Evaluative", + 4 => "Partial", + 5 => "Centre Weighted" ), + +18 => array( 'Name' => "Focus Type", + 0 => "Manual", + 1 => "Auto", + 3 => "Close-up (Macro)", + 8 => "Locked (Pan Mode)" ), + +19 => array( 'Name' => "Auto Focus Point Selected", + 12288 => "None (Manual Focus)", + 12289 => "Auto Selected", + 12290 => "Right", + 12291 => "Centre", + 12292 => "Left" ), + +20 => array( 'Name' => "Exposure Mode", + 0 => "Easy Shooting (See Easy Shooting Mode)", + 1 => "Program", + 2 => "Tv-Priority", + 3 => "Av-Priority", + 4 => "Manual", + 5 => "A-DEP" ), + +28 => array( 'Name' => "Flash Activity", + 0 => "Flash Did Not Fire", + 1 => "Flash Fired" ), + +32 => array( 'Name' => "Focus Mode", + 0 => "Focus Mode: Single", + 1 => "Focus Mode: Continuous" ) + +); + +/****************************************************************************** +* End of Global Variable: Canon_Camera_Settings_1_Tag_Values +******************************************************************************/ + + + + +/****************************************************************************** +* Global Variable: Canon_Camera_Settings_2_Tag_Values +* +* Contents: This global variable provides definitions for the Canon Camera +* Settings 2 Makernote tag, indexed by their offset. +* +******************************************************************************/ + +$GLOBALS[ "Canon_Camera_Settings_2_Tag_Values" ] = array( + +7 => array ( 'Name' => "White Balance", + 0 => "Auto", + 1 => "Sunny", + 2 => "Cloudy", + 3 => "Tungsten", + 4 => "Flourescent", + 5 => "Flash", + 6 => "Custom" ), + +15 => array( 'Name' => "Flash Bias", + 0xffc0 => "-2 EV", + 0xffcc => "-1.67 EV", + 0xffd0 => "-1.5 EV", + 0xffd4 => "-1.33 EV", + 0xffe0 => "-1 EV", + 0xffec => "-0.67 EV", + 0xfff0 => "-0.5 EV", + 0xfff4 => "-0.33 EV", + 0x0000 => "0 EV", + 0x000c => "0.33 EV", + 0x0010 => "0.5 EV", + 0x0014 => "0.67 EV", + 0x0020 => "1 EV", + 0x002c => "1.33 EV", + 0x0030 => "1.5 EV", + 0x0034 => "1.67 EV", + 0x0040 => "2 EV" ), +); + +/****************************************************************************** +* End of Global Variable: Canon_Camera_Settings_2_Tag_Values +******************************************************************************/ + + + + + + +/****************************************************************************** +* Global Variable: Canon_Custom_Functions_Tag_Values +* +* Contents: This global variable provides definitions for the Canon Custom +* Functions Makernote tag, indexed by their offset. +* +******************************************************************************/ + +$GLOBALS[ "Canon_Custom_Functions_Tag_Values" ] = array( + +1 => array ( 'Name' => "Long Exposure Noise Reduction", + 0 => "Off", + 1 => "On" ), + +2 => array ( 'Name' => "Shutter/Auto Exposure-lock buttons", + 0 => "AF/AE lock", + 1 => "AE lock/AF", + 2 => "AF/AF lock", + 3 => "AE+release/AE+AF" ), + +3 => array ( 'Name' => "Mirror lockup", + 0 => "Disable", + 1 => "Enable" ), + +4 => array ( 'Name' => "Tv/Av and exposure level", + 0 => "1/2 stop", + 1 => "1/3 stop" ), + +5 => array ( 'Name' => "AF-assist light", + 0 => "On (Auto)", + 1 => "Off" ), + +6 => array ( 'Name' => "Shutter speed in Av mode", + 0 => "Automatic", + 1 => "1/200 (fixed)" ), + +7 => array ( 'Name' => "Auto-Exposure Bracketting sequence/auto cancellation", + 0 => "0,-,+ / Enabled", + 1 => "0,-,+ / Disabled", + 2 => "-,0,+ / Enabled", + 3 => "-,0,+ / Disabled" ), + +8 => array ( 'Name' => "Shutter Curtain Sync", + 0 => "1st Curtain Sync", + 1 => "2nd Curtain Sync" ), + +9 => array ( 'Name' => "Lens Auto-Focus stop button Function Switch", + 0 => "AF stop", + 1 => "Operate AF", + 2 => "Lock AE and start timer" ), + +10 => array ( 'Name' => "Auto reduction of fill flash", + 0 => "Enable", + 1 => "Disable" ), + +11 => array ( 'Name' => "Menu button return position", + 0 => "Top", + 1 => "Previous (volatile)", + 2 => "Previous" ), + +12 => array ( 'Name' => "SET button function when shooting", + 0 => "Not Assigned", + 1 => "Change Quality", + 2 => "Change ISO Speed", + 3 => "Select Parameters" ), + +13 => array ( 'Name' => "Sensor cleaning", + 0 => "Disable", + 1 => "Enable" ) + + +); + +/****************************************************************************** +* End of Global Variable: Canon_Custom_Functions_Tag_Values +******************************************************************************/ + + +?> diff --git a/includes/jpeg_metadata_tk/Makernotes/casio.php b/includes/jpeg_metadata_tk/Makernotes/casio.php new file mode 100644 index 0000000..d1cd74e --- /dev/null +++ b/includes/jpeg_metadata_tk/Makernotes/casio.php @@ -0,0 +1,575 @@ + 1.11 : changed get_Casio_Makernote_Html to allow thumbnail links to work when +* toolkit is portable across directories +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* This file may be used freely for non-commercial purposes.For +* commercial uses please contact the author: evan@ozhiker.com +* +******************************************************************************/ + + +// Add the parser and interpreter functions to the list of Makernote parsers and interpreters. + +$GLOBALS['Makernote_Function_Array']['Read_Makernote_Tag'][] = "get_Casio_Makernote"; +$GLOBALS['Makernote_Function_Array']['get_Makernote_Text_Value'][] = "get_Casio_Text_Value"; +$GLOBALS['Makernote_Function_Array']['Interpret_Makernote_to_HTML'][] = "get_Casio_Makernote_Html"; + +include_once dirname(__FILE__) .'/../pjmt_utils.php'; // Change: as of version 1.11 - added to allow directory portability + + +/****************************************************************************** +* +* Function: get_Casio_Makernote +* +* Description: Decodes the Makernote tag and returns the new tag with the decoded +* information attached. Returns false if this is not a makernote +* that can be processed with this script +* +* Parameters: Makernote_Tag - the element of an EXIF array containing the +* makernote, as returned from get_EXIF_JPEG +* EXIF_Array - the entire EXIF array containing the +* makernote, as returned from get_EXIF_JPEG, in +* case more information is required for decoding +* filehnd - an open file handle for the file containing the +* makernote - does not have to be positioned at the +* start of the makernote +* Make_Field - The contents of the EXIF Make field, to aid +* determining whether this script can decode +* the makernote +* +* +* Returns: Makernote_Tag - the Makernote_Tag from the parameters, but +* modified to contain the decoded information +* FALSE - If this script could not decode the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Casio_Makernote( $Makernote_Tag, $EXIF_Array, $filehnd, $Make_Field ) +{ + + // Check if the Make Field contains the word Casio + if ( stristr( $Make_Field, "Casio" ) === FALSE ) + { + return FALSE; + } + + + if ( substr( $Makernote_Tag['Data'],0 , 6 ) == "QVC\x00\x00\x00" ) + { + + // Seek to the start of the IFD + fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] + 6 ); + + $Makernote_Tag['ByteAlign'] = "MM"; + + // Read the IFD(s) into an array + $Makernote_Tag['Decoded Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'], $Makernote_Tag['ByteAlign'], "Casio Type 2" ); + + // Save some information into the Tag element to aid interpretation + $Makernote_Tag['Decoded'] = TRUE; + $Makernote_Tag['Makernote Type'] = "Casio Type 2"; + $Makernote_Tag['Makernote Tags'] = "Casio Type 2"; + + // Return the new tag + return $Makernote_Tag; + } + else + { + // Seek to the start of the IFD + fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] + 0 ); + + $Makernote_Tag['ByteAlign'] = "MM"; + + // Read the IFD(s) into an array + $Makernote_Tag['Decoded Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'], $Makernote_Tag['ByteAlign'], "Casio Type 1" ); + + // Save some information into the Tag element to aid interpretation + $Makernote_Tag['Decoded'] = TRUE; + $Makernote_Tag['Makernote Type'] = "Casio Type 1"; + $Makernote_Tag['Makernote Tags'] = "Casio Type 1"; + + // Return the new tag + return $Makernote_Tag; + } +} + +/****************************************************************************** +* End of Function: get_Casio_Makernote +******************************************************************************/ + + + + +/****************************************************************************** +* +* Function: get_Casio_Text_Value +* +* Description: Provides a text value for any tag marked as special for makernotes +* that this script can decode. Returns false if this is not a makernote +* that can be processed with this script +* +* Parameters: Exif_Tag - the element of an the Makernote array containing the +* tag in question, as returned from get_Casio_Makernote +* Tag_Definitions_Name - The name of the Tag Definitions group +* within the global array IFD_Tag_Definitions +* +* +* Returns: output - the text value for the tag +* FALSE - If this script could not decode the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Casio_Text_Value( $Exif_Tag, $Tag_Definitions_Name ) +{ + + // Check that this tag uses the Casio tag definitions, otherwise it can't be decoded here + if ( $Tag_Definitions_Name == "Casio Type 2" ) + { + // Tag Uses Casio Type 2 Tag definitions + // Process the tag according to it's tag number + if ( $Exif_Tag['Tag Number'] == 0x001D ) + { + return $Exif_Tag['Data'][0]/10 . $Exif_Tag['Units']; + } + else + { + return FALSE; + } + } + else if ( $Tag_Definitions_Name == "Casio Type 1" ) + { + // Tag Uses Casio Type 1 Tags + return FALSE; + } + else + { + // Tag does NOT use Casio Tag definitions + return FALSE; + } + + // Shouldn't get here + return FALSE; + +} + +/****************************************************************************** +* End of Function: get_Casio_Text_Value +******************************************************************************/ + + + + + + + +/****************************************************************************** +* +* Function: get_Casio_Makernote_Html +* +* Description: Attempts to interpret a makernote into html. Returns false if +* it is not a makernote that can be processed with this script +* +* Parameters: Makernote_Tag - the element of an EXIF array containing the +* makernote, as returned from get_EXIF_JPEG +* filename - the name of the JPEG file being processed ( used +* by scripts which display embedded thumbnails) +* +* +* Returns: output - the html representing the makernote +* FALSE - If this script could not interpret the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Casio_Makernote_Html( $Makernote_tag, $filename ) +{ + // Check that this tag uses the Casio tags, otherwise it can't be interpreted here + if ( ( $Makernote_tag['Makernote Type'] != "Casio Type 1" ) && + ( $Makernote_tag['Makernote Type'] != "Casio Type 2" ) ) + { + // Not Casio tags - can't interpret with this function + return FALSE; + } + + // Casio Thumbnail (Tag 4) + if ( ( array_key_exists( 4, $Makernote_tag['Decoded Data'][0] ) ) && + ( $Makernote_tag['Makernote Tags'] == "Casio Type 2" ) ) + { + // Change: as of version 1.11 - Changed to make thumbnail link portable across directories + // Build the path of the thumbnail script and its filename parameter to put in a url + $link_str = get_relative_path( dirname(__FILE__) . "/get_casio_thumb.php" , getcwd ( ) ); + $link_str .= "?filename="; + $link_str .= get_relative_path( $filename, dirname(__FILE__) ); + + // Add thumbnail link to html + $Makernote_tag['Decoded Data'][0][4]['Text Value'] = "\n"; + $Makernote_tag['Decoded Data'][0][4]['Type'] = "String"; + } + + + // Casio Thumbnail (Tag 8192) + if ( ( array_key_exists( 8192, $Makernote_tag['Decoded Data'][0] ) ) && + ( $Makernote_tag['Makernote Tags'] == "Casio Type 2" ) ) + { + // Change: as of version 1.11 - Changed to make thumbnail link portable across directories + // Build the path of the thumbnail script and its filename parameter to put in a url + $link_str = get_relative_path( dirname(__FILE__) . "/.." . "/get_casio_thumb.php" , getcwd ( ) ); + $link_str .= "?filename="; + $link_str .= get_relative_path( $filename, dirname(__FILE__) . "/.." ); + + // Add thumbnail link to html + $Makernote_tag['Decoded Data'][0][8192]['Text Value'] = "\n"; + $Makernote_tag['Decoded Data'][0][8192]['Type'] = "String"; + } + + + // Check if there are two thumbnail offset tags + if ( ( array_key_exists( 4, $Makernote_tag['Decoded Data'][0] ) ) && + ( array_key_exists( 8192, $Makernote_tag['Decoded Data'][0] ) ) ) + { + // There are two copies of the thumbnail offset - Remove one + array_splice( $Makernote_tag['Decoded Data'][0], 4, 1); + } + + + // Interpret the IFD and return the html + return interpret_IFD( $Makernote_tag['Decoded Data'][0], $filename ); + +} + +/****************************************************************************** +* End of Function: get_Casio_Makernote_Html +******************************************************************************/ + + + + + + +/****************************************************************************** +* Global Variable: IFD_Tag_Definitions, Casio Type 1 +* +* Contents: This global variable provides definitions of the known Casio Type 1 +* Makernote tags, indexed by their tag number. +* +******************************************************************************/ + + +$GLOBALS[ "IFD_Tag_Definitions" ]["Casio Type 1"] = array( + +1 => array( 'Name' => "Recording Mode", + 'Type' => "Lookup", + 1 => "Single Shutter", + 2 => "Panorama", + 3 => "Night Scene", + 4 => "Portrait", + 5 => "Landscape" ), + +2 => array( 'Name' => "Quality", + 'Type' => "Lookup", + 1 => "Economy", + 2 => "Normal", + 3 => "Fine" ), + +3 => array( 'Name' => "Focusing Mode", + 'Type' => "Lookup", + 2 => "Macro", + 3 => "Auto Focus", + 4 => "Manual Focus", + 5 => "Infinity" ), + +4 => array( 'Name' => "Flash Mode", + 'Type' => "Lookup", + 1 => "Auto", + 2 => "On", + 3 => "Off", + 4 => "Off" ), + +5 => array( 'Name' => "Flash Intensity", + 'Type' => "Lookup", + 11 => "Weak", + 13 => "Normal", + 15 => "Strong" ), + +6 => array( 'Name' => "Object Distance", + 'Type' => "Numeric", + 'Units' => "mm" ), + +7 => array( 'Name' => "White Balance", + 'Type' => "Lookup", + 1 => "Auto", + 2 => "Tungsten", + 3 => "Daylight", + 4 => "Flourescent", + 5 => "Shade", + 129 => "Manual" ), + +10 => array( 'Name' => "Digital Zoom", + 'Type' => "Lookup", + 0x10000 => "Off", + 0x10001 => "2x Digital Zoom", + 0x20000 => "2x Digital Zoom", + 0x40000 => "4x Digital Zoom" ), + +11 => array( 'Name' => "Sharpness", + 'Type' => "Lookup", + 0 => "Normal", + 1 => "Soft", + 2 => "Hard" ), + +12 => array( 'Name' => "Contrast", + 'Type' => "Lookup", + 0 => "Normal", + 1 => "Low", + 2 => "High" ), + +13 => array( 'Name' => "Saturation", + 'Type' => "Lookup", + 0 => "Normal", + 1 => "Low", + 2 => "High" ), + +20 => array( 'Name' => "CCD Sensitivity", + 'Type' => "Lookup", + 64 => "Normal", + 125 => "+1.0", + 250 => "+2.0", + 244 => "+3.0", + 80 => "Normal (ISO 80 equivalent)", + 100 => "High" ), + +); + +/****************************************************************************** +* End of Global Variable: IFD_Tag_Definitions, Casio Type 1 +******************************************************************************/ + + + + + + +/****************************************************************************** +* Global Variable: IFD_Tag_Definitions, Casio Type 2 +* +* Contents: This global variable provides definitions of the known Casio Type 2 +* Makernote tags, indexed by their tag number. +* +******************************************************************************/ + +$GLOBALS[ "IFD_Tag_Definitions" ]["Casio Type 2"] = array( + +0x0002 => array( 'Name' => "Preview Thumbnail Dimensions", + 'Type' => "Numeric", + 'Units' => "(x,y pixels)" ), + +0x0003 => array( 'Name' => "Preview Thumbnail Size", + 'Type' => "Numeric", + 'Units' => "bytes" ), + +0x0004 => array( 'Name' => "Preview Thumbnail", // thumbnail offset + 'Type' => "Numeric" ), + + +0x0008 => array( 'Name' => "Quality Mode", + 'Type' => "Lookup", + 1 => "Fine", + 2 => "Super Fine" ), + +0x0009 => array( 'Name' => "Image Size", + 'Type' => "Lookup", + 20 => "2288 x 1712 pixels", + 36 => "3008 x 2008 pixels", + 5 => "2048 x 1536 pixels", + 4 => "1600 x 1200 pixels", + 21 => "2592 x 1944 pixels", + 0 => "640 x 480 pixels", + 22 => "2304 x 1728 pixels" ), + +0x000D => array( 'Name' => "Focus Mode", + 'Type' => "Lookup", + 0 => "Normal", + 1 => "Macro" ), + + +0x0014 => array( 'Name' => "Iso Sensitivity", + 'Type' => "Lookup", + 3 => "50", + 4 => "64", + 6 => "100", + 9 => "200" ), + + +0x0019 => array( 'Name' => "White Balance", + 'Type' => "Lookup", + 0 => "Auto", + 1 => "Daylight", + 2 => "Shade", + 3 => "Tungsten", + 4 => "Fluorescent", + 5 => "Manual" ), + +0x001D => array( 'Name' => "Focal Length", + 'Type' => "Special", + 'Units' => "mm" ), + +0x001F => array( 'Name' => "Saturation", + 'Type' => "Lookup", + 0 => "-1", + 1 => "Normal", + 2 => "+1", ), + +0x0020 => array( 'Name' => "Contrast", + 'Type' => "Lookup", + 0 => "-1", + 1 => "Normal", + 2 => "+1", ), + +0x0021 => array( 'Name' => "Sharpness", + 'Type' => "Lookup", + 0 => "-1", + 1 => "Normal", + 2 => "+1", ), + + +0x0e00 => array( 'Name' => "Print Image Matching Info", + 'Type' => "PIM" ), + + +0x2000 => array( 'Name' => "Casio Preview Thumbnail", // thumbnail offset + 'Type' => "String" ), + + + +0x2011 => array( 'Name' => "White Balance Bias", + 'Type' => "Numeric" ), + + +0x2012 => array( 'Name' => "White Balance", + 'Type' => "Lookup", + 12 => "Flash", + 0 => "Manual", + 1 => "Auto?", + 4 => "Flash?", ), + +0x2022 => array( 'Name' => "Object Distance", + 'Type' => "Numeric", + 'Units' => "mm" ), + + +0x2034 => array( 'Name' => "Flash Distance", + 'Type' => "Numeric", + 'Units' => " (0=Off)" ), + +0x3000 => array( 'Name' => "Record Mode", + 'Type' => "Lookup", + 2 => "Normal Mode" ), + +0x3001 => array( 'Name' => "Self Timer?", + 'Type' => "Lookup", + 1 => "Off?" ), + + +0x3002 => array( 'Name' => "Quality", + 'Type' => "Lookup", + 3 => "Fine" ), + +0x3003 => array( 'Name' => "Focus Mode", + 'Type' => "Lookup", + 6 => "Multi-Area Auto Focus", + 1 => "Fixation" ), + + +0x3006 => array( 'Name' => "Time Zone", + 'Type' => "String" ), + + +0x3007 => array( 'Name' => "Bestshot Mode", + 'Type' => "Lookup", + 0 => "Off", + 1 => "On?" ), + + +0x3014 => array( 'Name' => "CCD ISO Sensitivity", + 'Type' => "Numeric" ), + + + +0x3015 => array( 'Name' => "Colour Mode", + 'Type' => "Lookup", + 0 => "Off" ), + + +0x3016 => array( 'Name' => "Enhancement", + 'Type' => "Lookup", + 0 => "Off" ), + +0x3017 => array( 'Name' => "Filter", + 'Type' => "Lookup", + 0 => "Off" ), + + +); + +/****************************************************************************** +* End of Global Variable: IFD_Tag_Definitions, Casio Type 2 +******************************************************************************/ + + + + + + + + + + + + + + + + + + +?> \ No newline at end of file diff --git a/includes/jpeg_metadata_tk/Makernotes/epson.php b/includes/jpeg_metadata_tk/Makernotes/epson.php new file mode 100644 index 0000000..16057bb --- /dev/null +++ b/includes/jpeg_metadata_tk/Makernotes/epson.php @@ -0,0 +1,119 @@ + diff --git a/includes/jpeg_metadata_tk/Makernotes/fujifilm.php b/includes/jpeg_metadata_tk/Makernotes/fujifilm.php new file mode 100644 index 0000000..5d135cd --- /dev/null +++ b/includes/jpeg_metadata_tk/Makernotes/fujifilm.php @@ -0,0 +1,344 @@ + array( 'Name' => "Version", + 'Type' => "String" ), + +4096 => array( 'Name' => "Quality", + 'Type' => "String" ), + +4097 => array( 'Name' => "Sharpness", + 'Type' => "Lookup", + 1 => "Softest", + 2 => "Soft", + 3 => "Normal", + 4 => "Hard", + 5 => "Hardest" ), + +4098 => array( 'Name' => "White Balance", + 'Type' => "Lookup", + 0 => "Auto", + 256 => "Daylight", + 512 => "Cloudy", + 768 => "DaylightColour-fluorescence", + 769 => "DaywhiteColour-fluorescence", + 770 => "White-fluorescence", + 1024 => "Incandenscense", + 3840 => "Custom white balance" ), + +4099 => array( 'Name' => "Colour Saturation", + 'Type' => "Lookup", + 0 => "Normal", + 256 => "High", + 512 => "Low" ), + +4100 => array( 'Name' => "Tone (Contrast)", + 'Type' => "Lookup", + 0 => "Normal", + 256 => "High", + 512 => "Low" ), + +4112 => array( 'Name' => "Flash Mode", + 'Type' => "Lookup", + 0 => "Auto", + 1 => "On", + 2 => "Off", + 3 => "Red-eye Reduction" ), + +4113 => array( 'Name' => "Flash Strength", + 'Type' => "Numeric", + 'Units' => "EV" ), + +4128 => array( 'Name' => "Macro", + 'Type' => "Lookup", + 0 => "Off", + 1 => "On" ), + +4129 => array( 'Name' => "Focus Mode", + 'Type' => "Lookup", + 0 => "Auto Focus", + 1 => "Manual Focus" ), + +4144 => array( 'Name' => "Slow Sync", + 'Type' => "Lookup", + 0 => "Off", + 1 => "On" ), + +4145 => array( 'Name' => "Picture Mode", + 'Type' => "Lookup", + 0 => "Auto", + 1 => "Portrait Scene", + 2 => "Landscape Scene", + 4 => "Sports Scene", + 5 => "Night Scene", + 6 => "Program AE", + 256 => "Aperture priority AE", + 512 => "Shutter priority AE", + 768 => "Manual Exposure" ), + +4352 => array( 'Name' => "Continuous taking or auto bracketing mode", + 'Type' => "Lookup", + 0 => "Off", + 1 => "On" ), + +4864 => array( 'Name' => "Blur Warning", + 'Type' => "Lookup", + 0 => "No Blur Warning", + 1 => "Blur Warning" ), + +4865 => array( 'Name' => "Focus warning", + 'Type' => "Lookup", + 0 => "Auto Focus Good", + 1 => "Out of Focus" ), + +4866 => array( 'Name' => "Auto Exposure Warning", + 'Type' => "Lookup", + 0 => "Auto Exposure Good", + 1 => "Over exposure (>1/1000s,F11)" ) + +); + +/****************************************************************************** +* End of Global Variable: IFD_Tag_Definitions, Fujifilm +******************************************************************************/ + + + + + +?> diff --git a/includes/jpeg_metadata_tk/Makernotes/konica_minolta.php b/includes/jpeg_metadata_tk/Makernotes/konica_minolta.php new file mode 100644 index 0000000..17bbfbd --- /dev/null +++ b/includes/jpeg_metadata_tk/Makernotes/konica_minolta.php @@ -0,0 +1,745 @@ + array ( 'Name' => "Exposure Mode", + 'Type' => "Lookup", + 0 => "P", + 1 => "A", + 2 => "S", + 3 => "M" ), + +3 => array ( 'Name' => "Flash Mode", + 'Type' => "Lookup", + 0 => "Normal", + 1 => "Red-eye reduction", + 2 => "Rear flash sync", + 3 => "Wireless" ), + +4 => array ( 'Name' => "White Balance", + 'Type' => "Lookup", + 0 => "Auto", + 1 => "Daylight", + 2 => "Cloudy", + 3 => "Tungsten", + 5 => "Custom", + 7 => "Fluorescent", + 8 => "Fluorescent 2", + 11 => "Custom 2", + 12 => "Custom 3" ), + +5 => array ( 'Name' => "Image Size", + 'Type' => "Lookup", + 0 => "2560 x 1920 (2048x1536 - DiMAGE 5 only)", + 1 => "1600 x 1200", + 2 => "1280 x 960", + 3 => "640 x 480" ), + + +6 => array ( 'Name' => "Image Quality", + 'Type' => "Lookup", + 0 => "Raw", + 1 => "Super Fine", + 2 => "Fine", + 3 => "Standard", + 4 => "Economy", + 5 => "Extra Fine" ), + +7 => array ( 'Name' => "Shooting Mode", + 'Type' => "Lookup", + 0 => "Single", + 1 => "Continuous", + 2 => "Self-timer", + 4 => "Bracketing", + 5 => "Interval", + 6 => "UHS Continuous", + 7 => "HS Continuous" ), + + +8 => array ( 'Name' => "Metering Mode", + 'Type' => "Lookup", + 0 => "Multi-Segment", + 1 => "Centre Weighted", + 2 => "Spot" ), + + +9 => array ( 'Name' => "Apex Film Speed Value", + 'Type' => "Special" ), + +// 09 FilmSpeed , APEX Film Speed Value , Speed value = x/8-1 , ISO= (2^(x/8-1))*3.125 + + +10 => array ( 'Name' => "Apex Shutter Speed Time Value", + 'Type' => "Special", + 'Units' => "Seconds?" ), + +// APEX Time Value , Time value = x/8-6 , ShutterSpeed = 2^( (48-x)/8 ), ! Due to rounding error x=8 should be displayed as 30 sec. + +11 => array ( 'Name' => "Apex Aperture Value", + 'Type' => "Special" ), + +// APEX Aperture Value ApertureValue = x/8-1 , Aperture = 2^( x/16-0.5 ) + + +12 => array ( 'Name' => "Macro Mode", + 'Type' => "Lookup", + 0 => "Off", + 1 => "On" ), + +13 => array ( 'Name' => "Digital Zoom", + 'Type' => "Lookup", + 0 => "Off", + 1 => "Electronic magnification was used", + 2 => "Digital zoom 2x" ), + + +14 => array ( 'Name' => "Exposure Compensation", + 'Type' => "Special", + 'Units' => "EV" ), + +// EV = x/3 -2 Exposure compensation in EV + + +15 => array ( 'Name' => "Bracket Step", + 'Type' => "Lookup", + 0 => "1/3 EV", + 1 => "2/3 EV", + 2 => "1 EV" ), + + +17 => array ( 'Name' => "Interval Length", + 'Type' => "Special", + 'Units' => "Min" ), + +// interval is x+1 min (used with interval mode) + + +18 => array ( 'Name' => "Interval Number", + 'Type' => "Numeric", + 'Units' => "frames" ), + +19 => array ( 'Name' => "Focal Length", + 'Type' => "Special", + 'Units' => "mm" ), + +// x / 256 is real focal length in mm , x / 256 * 3.9333 is 35-mm equivalent + + +20 => array ( 'Name' => "Focus Distance", + 'Type' => "Numeric", + 'Units' => "mm ( 0 = Infinity)" ), + + +21 => array ( 'Name' => "Flash Fired", + 'Type' => "Lookup", + 0 => "No", + 1 => "Yes" ), + +22 => array ( 'Name' => "Date", + 'Type' => "Special" ), + +// yyyymmdd , year = x/65536 , month = x/256-x/65536*256 , day = x%256 + +23 => array ( 'Name' => "Time", + 'Type' => "Special" ), + +// hhhhmmss , hour = x/65536 , minute = x/256-x/65536*256 , second = x%256 + + +24 => array ( 'Name' => "Max Aperture at this focal length", + 'Type' => "Special" ), + +// Fno = 2^(x/16-0.5) + + +27 => array ( 'Name' => "File Number Memory", + 'Type' => "Lookup", + 0 => "Off", + 1 => "On" ), + +28 => array ( 'Name' => "Last File Number", + 'Type' => "Numeric", + 'Units' => " ( 0 = File Number Memory is Off)" ), + + +29 => array ( 'Name' => "White Balance Red", + 'Type' => "Special" ), + +// x/256 - red white balance coefficient used for this picture + + +30 => array ( 'Name' => "White Balance Green", + 'Type' => "Special" ), + +// x/256 - green white balance coefficient used for this picture + +31 => array ( 'Name' => "White Balance Blue", + 'Type' => "Special" ), + +// x/256 - blue white balance coefficient used for this picture + + +32 => array ( 'Name' => "Saturation", + 'Type' => "Special" ), + +// x-3 = saturation + + +33 => array ( 'Name' => "Contrast", + 'Type' => "Special" ), + +// x-3 - contrast + + +34 => array ( 'Name' => "Sharpness", + 'Type' => "Lookup", + 0 => "Hard", + 1 => "Normal", + 2 => "Soft" ), + + +35 => array ( 'Name' => "Subject Program", + 'Type' => "Lookup", + 0 => "none", + 1 => "portrait", + 2 => "text", + 3 => "night portrait", + 4 => "sunset", + 5 => "sports action" ), + + +36 => array ( 'Name' => "Flash Compensation", + 'Type' => "Special", + 'Units' => "EV" ), + +// (x-6)/3 = flash compensation in EV + + +37 => array ( 'Name' => "ISO Setting", + 'Type' => "Lookup", + 0 => "100", + 1 => "200", + 2 => "400", + 3 => "800", + 4 => "auto", + 5 => "64" ), + + +38 => array ( 'Name' => "Camera Model", + 'Type' => "Lookup", + 0 => "DiMAGE 7", + 1 => "DiMAGE 5", + 2 => "DiMAGE S304", + 3 => "DiMAGE S404", + 4 => "DiMAGE 7i", + 5 => "DiMAGE 7Hi", + 6 => "DiMAGE A1", + 7 => "DiMAGE S414" ), + + +39 => array ( 'Name' => "Interval Mode", + 'Type' => "Lookup", + 0 => "Still Image", + 1 => "Time-lapse Movie" ), + + +40 => array ( 'Name' => "Folder Name", + 'Type' => "Lookup", + 0 => "Standard Form", + 1 => "Data Form" ), + + +41 => array ( 'Name' => "Color Mode", + 'Type' => "Lookup", + 0 => "Natural Color", + 1 => "Black & White", + 2 => "Vivid Color", + 3 => "Solarization", + 4 => "Adobe RGB" ), + + +42 => array ( 'Name' => "Color Filter", + 'Type' => "Special" ), + +// x-3 = color filter + + +43 => array ( 'Name' => "Black & White Filter", + 'Type' => "Numeric" ), + + + +44 => array ( 'Name' => "Internal Flash", + 'Type' => "Lookup", + 0 => "Not Fired", + 1 => "Fired" ), + + + +45 => array ( 'Name' => "Apex Brightness Value", + 'Type' => "Special" ), + +// Brightness Value = x/8-6 + + + + +46 => array ( 'Name' => "Spot Focus Point X Coordinate", + 'Type' => "Numeric" ), + + + +47 => array ( 'Name' => "Spot Focus Point Y Coordinate", + 'Type' => "Numeric" ), + + + +48 => array ( 'Name' => "Wide Focus Zone", + 'Type' => "Lookup", + 0 => "No Zone or AF Failed", + 1 => "Center Zone (Horizontal Orientation)", + 2 => "Center Zone (Vertical Orientation)", + 3 => "Left Zone", + 4 => "Right Zone" ), + + +49 => array ( 'Name' => "Focus Mode", + 'Type' => "Lookup", + 0 => "Auto Focus", + 1 => "Manual Focus" ), + + +50 => array ( 'Name' => "Focus Area", + 'Type' => "Lookup", + 0 => "Wide Focus (normal)", + 1 => "Spot Focus" ), + + +51 => array ( 'Name' => "DEC Switch Position", + 'Type' => "Lookup", + 0 => "Exposure", + 1 => "Contrast", + 2 => "Saturation", + 3 => "Filter" ), + + + +); + +/****************************************************************************** +* End of Global Variable: Minolta_Camera_Setting_Definitions +******************************************************************************/ + + + + +?> diff --git a/includes/jpeg_metadata_tk/Makernotes/kyocera.php b/includes/jpeg_metadata_tk/Makernotes/kyocera.php new file mode 100644 index 0000000..bb5f17e --- /dev/null +++ b/includes/jpeg_metadata_tk/Makernotes/kyocera.php @@ -0,0 +1,241 @@ + array( 'Name' => "Kyocera Proprietory Format Thumbnail", + 'Type' => "Unknown" ), + +0x0E00 => array( 'Name' => "Print Image Matching Info", + 'Type' => "PIM" ), + +); + +/****************************************************************************** +* End of Global Variable: IFD_Tag_Definitions, Kyocera +******************************************************************************/ + + + + + + + + +?> diff --git a/includes/jpeg_metadata_tk/Makernotes/nikon.php b/includes/jpeg_metadata_tk/Makernotes/nikon.php new file mode 100644 index 0000000..b860d51 --- /dev/null +++ b/includes/jpeg_metadata_tk/Makernotes/nikon.php @@ -0,0 +1,731 @@ + array( 'Name' => "Quality", + 'Description' => "1: VGA Basic, 2: VGA Normal, 3: VGA Fine, 4: SXGA Basic, 5: SXGA Normal, 6: SXGA Fine", + 'Type' => "Lookup", + 1 => "VGA (640x480) Basic", + 2 => "VGA (640x480) Normal", + 3 => "VGA (640x480) Fine", + 4 => "SXGA (1280x960) Basic", + 5 => "SXGA (1280x960) Normal", + 6 => "SXGA (1280x960) Fine", + 7 => "Unknown, Possibly XGA (1024x768) Basic", + 8 => "Unknown, Possibly XGA (1024x768) Basic", + 9 => "Unknown, Possibly XGA (1024x768) Basic", + 10 => "UXGA (1600x1200) Basic", + 11 => "UXGA (1600x1200) Normal", + 12 => "UXGA (1600x1200) Fine" ), + +4 => array( 'Name' => "Colour Mode", + 'Description' => "1: Colour, 2: Monochrome.", + 'Type' => "Lookup", + 1 => "Colour", + 2 => "Monochrome" ), + +5 => array( 'Name' => "Image Adjustment", + 'Description' => "0: Normal, 1: Bright+, 2: Bright-, 3: Contrast+, 4: Contrast-.", + 'Type' => "Lookup", + 0 => "Normal", + 1 => "Bright+", + 2 => "Bright-", + 3 => "Contrast+", + 4 => "Contrast-" ), + +6 => array( 'Name' => "CCD Sensitivity", + 'Description' => "0: ISO80, 2: ISO160, 4: ISO320, 5: ISO100", + 'Type' => "Lookup", + 0 => "ISO 80", + 2 => "ISO 160", + 4 => "ISO 320", + 5 => "ISO 100" ), + +7 => array( 'Name' => "White Balance", + 'Description' => "0: Auto, 1: Preset, 2: Daylight, 3: Incandescense, 4: Fluorescence, 5: Cloudy, 6: SpeedLight", + 'Type' => "Lookup", + 0 => "Auto", + 1 => "Preset", + 2 => "Daylight", + 3 => "Incandescense", + 4 => "Flourescence", + 5 => "Cloudy", + 6 => "Speedlight" ), + +8 => array( 'Name' => "Focus", + 'Description' => "If infinite focus, value is '1/0'.", + 'Type' => "Numeric" ), + +10 => array( 'Name' => "Digital Zoom", + 'Description' => "'160/100' means 1.6x digital zoom, '0/100' means no digital zoom (optical zoom only).", + 'Type' => "Numeric" ), + +11 => array( 'Name' => "Converter", + 'Description' => "If Fisheye Converter is used, value is 1", + 'Type' => "Lookup", + 0 => "No Converter Used", + 1 => "Fish-eye Converter Used" ) + +); + +/****************************************************************************** +* End of Global Variable: IFD_Tag_Definitions, Nikon Type 1 +******************************************************************************/ + + + + + +/****************************************************************************** +* Global Variable: IFD_Tag_Definitions, Nikon Type 3 +* +* Contents: This global variable provides definitions of the known Nikon Type 3 +* Makernote tags, indexed by their tag number. +* +******************************************************************************/ + +$GLOBALS[ "IFD_Tag_Definitions" ]["Nikon Type 3"] = array( + + +1 => array( 'Name' => "Nikon Makernote Version", + 'Type' => "Special" ), + +2 => array( 'Name' => "ISO Speed Used", + 'Type' => "Special" ), + +3 => array( 'Name' => "Colour Mode", + 'Type' => "String" ), + +4 => array( 'Name' => "Quality", + 'Type' => "String" ), + +5 => array( 'Name' => "White Balance", + 'Type' => "String" ), + +6 => array( 'Name' => "Sharpening", + 'Type' => "String" ), + +7 => array( 'Name' => "Focus Mode", + 'Type' => "String" ), + +8 => array( 'Name' => "Flash Setting", + 'Type' => "String" ), + +9 => array( 'Name' => "Auto Flash Mode", + 'Type' => "String" ), + +11 => array( 'Name' => "White Balance Bias Value", + 'Type' => "Numeric", + 'Units' => "(Units Approx: 100 Mired per increment)" ), + +12 => array( 'Name' => "White Balance Red, Blue Coefficients?", + 'Type' => "Numeric" ), + +15 => array( 'Name' => "ISO Selection?", + 'Type' => "String" ), + + +18 => array( 'Name' => "Flash Compensation", + 'Type' => "Lookup", + 0x06 => "+1.0 EV", + 0x04 => "+0.7 EV", + 0x03 => "+0.5 EV", + 0x02 => "+0.3 EV", + 0x00 => "0.0 EV", + 0xfe => "-0.3 EV", + 0xfd => "-0.5 EV", + 0xfc => "-0.7 EV", + 0xfa => "-1.0 EV", + 0xf8 => "-1.3 EV", + 0xf7 => "-1.5 EV", + 0xf6 => "-1.7 EV", + 0xf4 => "-2.0 EV", + 0xf2 => "-2.3 EV", + 0xf1 => "-2.5 EV", + 0xf0 => "-2.7 EV", + 0xee => "-3.0 EV" ), + +19 => array( 'Name' => "ISO Speed Requested", + 'Type' => "Special", + 'Units' => "(May be different to Speed Used when Auto ISO is on)" ), + + +22 => array( 'Name' => "Photo corner coordinates", + 'Type' => "Numeric", + 'Units' => "Pixels" ), + +24 => array( 'Name' => "Flash Bracket Compensation Applied", + 'Type' => "Lookup", + 0x06 => "+1.0 EV", + 0x04 => "+0.7 EV", + 0x03 => "+0.5 EV", + 0x02 => "+0.3 EV", + 0x00 => "0.0 EV", + 0xfe => "-0.3 EV", + 0xfd => "-0.5 EV", + 0xfc => "-0.7 EV", + 0xfa => "-1.0 EV", + 0xf8 => "-1.3 EV", + 0xf7 => "-1.5 EV", + 0xf6 => "-1.7 EV", + 0xf4 => "-2.0 EV", + 0xf2 => "-2.3 EV", + 0xf1 => "-2.5 EV", + 0xf0 => "-2.7 EV", + 0xee => "-3.0 EV" ), + +25 => array( 'Name' => "AE Bracket Compensation Applied", + 'Type' => "Numeric", + 'Units' => "EV" ), + +128 => array( 'Name' => "Image Adjustment?", + 'Type' => "String" ), + +129 => array( 'Name' => "Tone Compensation (Contrast)", + 'Type' => "String" ), + +130 => array( 'Name' => "Auxiliary Lens (Adapter)", + 'Type' => "String" ), + +131 => array( 'Name' => "Lens Type?", + 'Type' => "Lookup", + 6 => "Nikon D series Lens", + 14 => "Nikon G series Lens" ), + +132 => array( 'Name' => "Lens Min/Max Focal Length, Min/Max Aperture", + 'Type' => "Numeric", + 'Units' => " mm, mm, F#, F#" ), + +133 => array( 'Name' => "Manual Focus Distance?", + 'Type' => "Numeric"), + +134 => array( 'Name' => "Digital Zoom Factor?", + 'Type' => "Numeric" ), + +135 => array( 'Name' => "Flash Used", + 'Type' => "Lookup", + 0 => "Flash Not Used", + 9 => "Flash Fired" ), + +136 => array( 'Name' => "Auto Focus Area", + 'Description' => "byte 1 : AF Mode: 00 = single area, 01 = Dynamic Area, 02 = Closest Subject\n + byte 2 : AF Area Selected : 00 = Centre, 01 = Top, 02 = Bottom, 03 = Left, 04 = Right\n + byte 3 : Unknown, always zero\n + byte 4 : Properly focused Area(s) : bit 0 = Centre, bit 1 = Top, bit 2 = Bottom, bit 3 = Left, bit 4 = Right", + 'Type' => "Special" ), + +137 => array( 'Name' => "Bracketing & Shooting Mode", + 'Description' => "bit 0&1 (0 = single frame, 1 = continuous,2=timer, 3=remote timer? 4 = remote?\n + bit 4, Bracketing on or off\n + bit 6, white Balance Bracketing on", + 'Type' => "Special" ), + +141 => array( 'Name' => "Colour Mode", + 'Description' =>"1a = Portrait sRGB, 2 = Adobe RGB, 3a = Landscape sRGB", + 'Type' => "String" ), + +143 => array( 'Name' => "Scene Mode?", + 'Type' => "Numeric" ), + +144 => array( 'Name' => "Lighting Type", + 'Type' => "String" ), + +146 => array( 'Name' => "Hue Adjustment", + 'Type' => "Numeric", + 'Units' => "Degrees" ), + +148 => array( 'Name' => "Saturation?", + 'Type' => "Lookup", + -3 => "Black and White", + -2 => "-2", + -1 => "-1", + 0 => "Normal", + 1 => "+1", + 2 => "+2" ), + +149 => array( 'Name' => "Noise Reduction", + 'Type' => "String" ), + +167 => array( 'Name' => "Total Number of Shutter Releases for Camera", + 'Type' => "Numeric", + 'Units' => "Shutter Releases" ), + +169 => array( 'Name' => "Image optimisation", + 'Type' => "String" ), + +170 => array( 'Name' => "Saturation", + 'Type' => "String" ), + +171 => array( 'Name' => "Digital Vari-Program", + 'Type' => "String" ) + + +// Tags that exist but are unknown: 10, 13, 14, 16, 17, 23, 24, 138, 139, 145, +// 151, 152, 160, 162 163, 165, 166, 168 + + +); + +/****************************************************************************** +* End of Global Variable: IFD_Tag_Definitions, Nikon Type 3 +******************************************************************************/ + + + +?> diff --git a/includes/jpeg_metadata_tk/Makernotes/olympus.php b/includes/jpeg_metadata_tk/Makernotes/olympus.php new file mode 100644 index 0000000..a9717b0 --- /dev/null +++ b/includes/jpeg_metadata_tk/Makernotes/olympus.php @@ -0,0 +1,486 @@ + 1.11 : changed get_Olympus_Makernote_Html to allow thumbnail links to work when +* toolkit is portable across directories +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* This file may be used freely for non-commercial purposes.For +* commercial uses please contact the author: evan@ozhiker.com +* +******************************************************************************/ + + + +// Add the parser and interpreter functions to the list of Makernote parsers and interpreters. + +$GLOBALS['Makernote_Function_Array']['Read_Makernote_Tag'][] = "get_Olympus_Makernote"; +$GLOBALS['Makernote_Function_Array']['get_Makernote_Text_Value'][] = "get_Olympus_Text_Value"; +$GLOBALS['Makernote_Function_Array']['Interpret_Makernote_to_HTML'][] = "get_Olympus_Makernote_Html"; + + + + +include_once dirname(__FILE__) .'/../pjmt_utils.php'; // Change: as of version 1.11 - added to allow directory portability + + + + +/****************************************************************************** +* +* Function: get_Olympus_Makernote +* +* Description: Decodes the Makernote tag and returns the new tag with the decoded +* information attached. Returns false if this is not a makernote +* that can be processed with this script +* +* Parameters: Makernote_Tag - the element of an EXIF array containing the +* makernote, as returned from get_EXIF_JPEG +* EXIF_Array - the entire EXIF array containing the +* makernote, as returned from get_EXIF_JPEG, in +* case more information is required for decoding +* filehnd - an open file handle for the file containing the +* makernote - does not have to be positioned at the +* start of the makernote +* Make_Field - The contents of the EXIF Make field, to aid +* determining whether this script can decode +* the makernote +* +* +* Returns: Makernote_Tag - the Makernote_Tag from the parameters, but +* modified to contain the decoded information +* FALSE - If this script could not decode the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Olympus_Makernote( $Makernote_Tag, $EXIF_Array, $filehnd, $Make_Field ) +{ + + // Check if the Make Field contains the word Olympus + if ( stristr( $Make_Field, "Olympus" ) === FALSE ) + { + return FALSE; + } + + // Check if the header exists at the start of the Makernote + if ( ( substr( $Makernote_Tag['Data'], 0, 7 ) != "OLYMP\x00\x01" ) && + ( substr( $Makernote_Tag['Data'], 0, 7 ) != "OLYMP\x00\x02" ) ) + { + // This isn't a Olympus Makernote, abort + return FALSE ; + } + + + + // Seek to the start of the IFD + fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] + 8 ); + + // Read the IFD(s) into an array + $Makernote_Tag['Decoded Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'], $Makernote_Tag['ByteAlign'], "Olympus" ); + + // Save some information into the Tag element to aid interpretation + $Makernote_Tag['Decoded'] = TRUE; + $Makernote_Tag['Makernote Type'] = "Olympus"; + $Makernote_Tag['Makernote Tags'] = "Olympus"; + + + // Return the new tag + return $Makernote_Tag; +} + +/****************************************************************************** +* End of Function: get_Olympus_Makernote +******************************************************************************/ + + + + + + + +/****************************************************************************** +* +* Function: get_Olympus_Text_Value +* +* Description: Provides a text value for any tag marked as special for makernotes +* that this script can decode. Returns false if this is not a makernote +* that can be processed with this script +* +* Parameters: Exif_Tag - the element of an the Makernote array containing the +* tag in question, as returned from get_Olympus_Makernote +* Tag_Definitions_Name - The name of the Tag Definitions group +* within the global array IFD_Tag_Definitions +* +* +* Returns: output - the text value for the tag +* FALSE - If this script could not decode the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Olympus_Text_Value( $Exif_Tag, $Tag_Definitions_Name ) +{ + // Check that this tag uses the Olympus tags, otherwise it can't be decoded here + if ( $Tag_Definitions_Name !== "Olympus" ) + { + // Not an Olympus tag - can't decode it + return FALSE; + } + + + // Process the tag acording to it's tag number, to produce a text value + if ( $Exif_Tag['Tag Number'] == 0x200 ) + { + // Special Mode Tag + + // Add info from the first value to the output string + switch ( $Exif_Tag['Data'][0] ) + { + case 0: $outputstr = "Normal\n"; + break; + case 2: $outputstr = "Fast\n"; + break; + case 3: $outputstr = "Panorama\n"; + break; + default: $outputstr = "Unknown Mode ( " . $Exif_Tag['Data'][0] . " )\n"; + break; + } + + // Add info from the second value to the output string + $outputstr .= "Sequence Number: " . $Exif_Tag['Data'][1] . "\n"; + + // Add info from the third value to the output string + switch ( $Exif_Tag['Data'][2] ) + { + case 0: // Do nothing + break; + case 1: $outputstr .= "Panorama Direction: Left to Right\n"; + break; + case 2: $outputstr .= "Panorama Direction: Right to Left\n"; + break; + case 3: $outputstr .= "Panorama Direction: Bottom to Top\n"; + break; + case 4: $outputstr .= "Panorama Direction: Top to Bottom\n"; + break; + default: $outputstr .= "Unknown Panorama Direction\n"; + break; + } + + // Return the output string + return $outputstr; + } + else + { + // Unknown special tag - can't process it here + return FALSE; + } + + // Unknown special tag - can't process it here + return FALSE; + +} + +/****************************************************************************** +* End of Function: get_Olympus_Text_Value +******************************************************************************/ + + + + +/****************************************************************************** +* +* Function: get_Olympus_Makernote_Html +* +* Description: Attempts to interpret a makernote into html. Returns false if +* it is not a makernote that can be processed with this script +* +* Parameters: Makernote_Tag - the element of an EXIF array containing the +* makernote, as returned from get_EXIF_JPEG +* filename - the name of the JPEG file being processed ( used +* by scripts which display embedded thumbnails) +* +* +* Returns: output - the html representing the makernote +* FALSE - If this script could not interpret the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Olympus_Makernote_Html( $Makernote_tag, $filename ) +{ + + // Check that this tag uses the Olympus tags, otherwise it can't be interpreted here + if ( $Makernote_tag['Makernote Tags'] != "Olympus" ) + { + // Not Olympus tags - can't interpret with this function + return FALSE; + } + + // Check if the Decoded data is valid + if ( $Makernote_tag['Decoded Data'][0] === FALSE ) + { + // Decoded data is not valid - can't interpret with this function + return FALSE; + } + + // Minolta Thumbnail 1 + if ( ( array_key_exists( 0x0088, $Makernote_tag['Decoded Data'][0] ) ) && + ( $Makernote_tag['Makernote Tags'] == "Olympus" ) ) + { + // Change: as of version 1.11 - Changed to make thumbnail link portable across directories + // Build the path of the thumbnail script and its filename parameter to put in a url + $link_str = get_relative_path( dirname(__FILE__) . "/../get_minolta_thumb.php" , getcwd ( ) ); + $link_str .= "?filename="; + $link_str .= get_relative_path( $filename, dirname(__FILE__) ."/.." ); + + // Add thumbnail link to html + $Makernote_tag['Decoded Data'][0][0x0088]['Text Value'] = ""; + + $Makernote_tag['Decoded Data'][0][0x0088]['Type'] = "String"; + } + // Minolta Thumbnail 2 + if ( ( array_key_exists( 0x0081, $Makernote_tag['Decoded Data'][0] ) ) && + ( $Makernote_tag['Makernote Tags'] == "Olympus" ) ) + { + // Change: as of version 1.11 - Changed to make thumbnail link portable across directories + // Build the path of the thumbnail script and its filename parameter to put in a url + $link_str = get_relative_path( dirname(__FILE__) . " /../get_minolta_thumb.php" , getcwd ( ) ); + $link_str .= "?filename="; + $link_str .= get_relative_path( $filename, dirname(__FILE__) ."/.." ); + + // Add thumbnail link to html + $Makernote_tag['Decoded Data'][0][0x0081]['Text Value'] = ""; + $Makernote_tag['Decoded Data'][0][0x0081]['Type'] = "String"; + } + + // Interpret the IFD and return the HTML + return interpret_IFD( $Makernote_tag['Decoded Data'][0], $filename ); + +} + +/****************************************************************************** +* End of Function: get_Olympus_Makernote_Html +******************************************************************************/ + + + + + + + + + + + + +/****************************************************************************** +* Global Variable: IFD_Tag_Definitions, Olympus +* +* Contents: This global variable provides definitions of the known Olympus +* Makernote tags, indexed by their tag number. +* It also includes Minolta and Agfa tags, as they use many of the +* same tags +* +******************************************************************************/ + +$GLOBALS[ "IFD_Tag_Definitions" ]["Olympus"] = array( + +0x0000 => array( 'Name' => "Makernote Version", // Minolta + 'Type' => "String" ), + +0x0001 => array( 'Name' => "Camera Settings", // Minolta + 'Type' => "Special" ), + +0x0003 => array( 'Name' => "Camera Settings", // Minolta + 'Type' => "Special" ), + +0x0040 => array( 'Name' => "Compressed Image Size", // Minolta + 'Type' => "Numeric", + 'Units' => "Bytes" ), + +0x0081 => array( 'Name' => "Minolta Thumbnail", // Minolta + 'Type' => "Special" ), + +0x0088 => array( 'Name' => "Minolta Thumbnail", // Minolta + 'Type' => "Special" ), + +0x0089 => array( 'Name' => "Minolta Thumbnail Length", // Minolta + 'Type' => "Numeric", + 'Units' => "bytes" ), + +0x0101 => array( 'Name' => "Colour Mode", // Minolta + 'Type' => "Lookup", + 0 => "Natural Colour", + 1 => "Black & White", + 2 => "Vivid colour", + 3 => "Solarization", + 4 => "AdobeRGB" ), + +0x0102 => array( 'Name' => "Image Quality", // Minolta + 'Type' => "Lookup", + 0 => "Raw", + 1 => "Super Fine", + 2 => "Fine", + 3 => "Standard", + 4 => "Extra Fine" ), + +0x0103 => array( 'Name' => "Image Quality?", // Minolta + 'Type' => "Lookup", + 0 => "Raw", + 1 => "Super Fine", + 2 => "Fine", + 3 => "Standard", + 4 => "Extra Fine" ), + + + +0x0200 => array( 'Name' => "Special Mode", + 'Type' => "Special" ), + + +0x0201 => array( 'Name' => "JPEG Quality", + 'Type' => "Lookup", + 1 => "Standard Quality", + 2 => "High Quality", + 3 => "Super High Quality" ), + +0x0202 => array( 'Name' => "Macro", + 'Type' => "Lookup", + 0 => "Normal (Not Macro)", + 1 => "Macro" ), + +0x0204 => array( 'Name' => "Digital Zoom", + 'Type' => "Numeric", + 'Units' => " x Digital Zoom, (0 or 1 = normal)" ), + +0x0207 => array( 'Name' => "Firmware Version", + 'Type' => "String" ), + + +0x0208 => array( 'Name' => "Picture Info Data", + 'Type' => "String" ), + +0x0209 => array( 'Name' => "Camera ID", + 'Type' => "String" ), + + +0x020B => array( 'Name' => "Image Width", // Epson Tag + 'Type' => "Pixels" ), + +0x020C => array( 'Name' => "Image Height", // Epson Tag + 'Type' => "Pixels" ), + +0x020D => array( 'Name' => "Original Manufacturer Model?", // Epson Tag + 'Type' => "String" ), + +0x0E00 => array( 'Name'=> "Print Image Matching Info", // Minolta Tag + 'Type' => "PIM" ), + +0x1004 => array( 'Name' => "Flash Mode", + 'Type' => "Numeric" ), + +0x1006 => array( 'Name' => "Bracket", + 'Type' => "Numeric" ), + +0x100B => array( 'Name' => "Focus Mode", + 'Type' => "Numeric" ), + +0x100C => array( 'Name' => "Focus Distance", + 'Type' => "Numeric" ), + +0x100D => array( 'Name' => "Zoom", + 'Type' => "Numeric" ), + +0x100E => array( 'Name' => "Macro Focus", + 'Type' => "Numeric" ), + +0x100F => array( 'Name' => "Sharpness", + 'Type' => "Numeric" ), + +0x1011 => array( 'Name' => "Colour Matrix", + 'Type' => "Numeric" ), + +0x1012 => array( 'Name' => "Black Level", + 'Type' => "Numeric" ), + +0x1015 => array( 'Name' => "White Balance", + 'Type' => "Numeric" ), + +0x1017 => array( 'Name' => "Red Bias", + 'Type' => "Numeric" ), + +0x1018 => array( 'Name' => "Blue Bias", + 'Type' => "Numeric" ), + +0x101A => array( 'Name' => "Serial Number", + 'Type' => "Numeric" ), + +0x1023 => array( 'Name' => "Flash Bias", + 'Type' => "Numeric" ), + +0x1029 => array( 'Name' => "Contrast", + 'Type' => "Numeric" ), + +0x102A => array( 'Name' => "Sharpness Factor", + 'Type' => "Numeric" ), + +0x102B => array( 'Name' => "Colour Control", + 'Type' => "Numeric" ), + +0x102C => array( 'Name' => "Valid Bits", + 'Type' => "Numeric" ), + +0x102D => array( 'Name' => "Coring Filter", + 'Type' => "Numeric" ), + +0x102E => array( 'Name' => "Final Width", + 'Type' => "Numeric" ), + +0x102F => array( 'Name' => "Final Height", + 'Type' => "Numeric" ), + +0x1034 => array( 'Name' => "Compression Ratio", + 'Type' => "Numeric" ), + + + +); + + +/****************************************************************************** +* End of Global Variable: IFD_Tag_Definitions, Olympus +******************************************************************************/ + + + + + +?> \ No newline at end of file diff --git a/includes/jpeg_metadata_tk/Makernotes/panasonic.php b/includes/jpeg_metadata_tk/Makernotes/panasonic.php new file mode 100644 index 0000000..2da827f --- /dev/null +++ b/includes/jpeg_metadata_tk/Makernotes/panasonic.php @@ -0,0 +1,292 @@ + array( 'Name' => "Quality Mode", + 'Type' => "Numeric" ), + +0x02 => array( 'Name' => "Version", + 'Type' => "String" ), + +0x1c => array( 'Name' => "Macro Mode", + 'Type' => "Lookup", + 1 => "On", + 2 => "Off" ), + +0x1f => array( 'Name' => "Record Mode", + 'Type' => "Lookup", + 1 => "Normal", + 2 => "Portrait", + 9 => "Macro" ), + +0xE00 => array( 'Name' => "Print Image Matching Info", + 'Type' => "PIM" ), + +); + +/****************************************************************************** +* End of Global Variable: IFD_Tag_Definitions, Panasonic +******************************************************************************/ + + + + + +?> diff --git a/includes/jpeg_metadata_tk/Makernotes/ricoh.php b/includes/jpeg_metadata_tk/Makernotes/ricoh.php new file mode 100644 index 0000000..f858bbc --- /dev/null +++ b/includes/jpeg_metadata_tk/Makernotes/ricoh.php @@ -0,0 +1,415 @@ +"; + + // Replace the semicolon dividers with line break html tags + $output_str .= str_replace ( ";", "
\n", $Makernote_tag['Data'] ); + + // Close the html + $output_str .= ""; + + // Return the html + return $output_str; + } + // Check if this makernote is a Ricoh Empty makernote + else if ( $Makernote_tag['Makernote Type'] == "Ricoh Empty Makernote" ) + { + // Do Nothing + return ""; + } + else + { + // Don't recognise the Makernote type - not a Ricoh makernote + return FALSE; + } + + // shouldn't get here + return FALSE; +} + +/****************************************************************************** +* End of Function: get_Ricoh_Makernote_Html +******************************************************************************/ + + + + + + + + +/****************************************************************************** +* +* Function: get_Ricoh_Text_Value +* +* Description: Provides a text value for any tag marked as special for makernotes +* that this script can decode. Returns false if this is not a makernote +* that can be processed with this script +* +* Parameters: Exif_Tag - the element of an the Makernote array containing the +* tag in question, as returned from get_Ricoh_Makernote +* Tag_Definitions_Name - The name of the Tag Definitions group +* within the global array IFD_Tag_Definitions +* +* +* Returns: output - the text value for the tag +* FALSE - If this script could not decode the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Ricoh_Text_Value( $Exif_Tag, $Tag_Definitions_Name ) +{ + + // Check that this tag uses the Ricoh tags, otherwise it can't be decoded here + if ( $Tag_Definitions_Name == "Ricoh" ) + { + + // Process the tag acording to it's tag number, to produce a text value + if ( $Exif_Tag['Tag Number'] == 0x0002 ) // Version tag + { + $tmp = implode ( "\x00", $Exif_Tag['Data']); + return "\"" .HTML_UTF8_Escape( $tmp ) . "\" (" . bin2hex( $tmp ) . " hex)"; + } + + } + + // Unknown tag or tag definitions + return FALSE; + +} + +/****************************************************************************** +* End of Function: get_Ricoh_Text_Value +******************************************************************************/ + + + + + + + + + + + +/****************************************************************************** +* Global Variable: IFD_Tag_Definitions, Ricoh +* +* Contents: This global variable provides definitions of the known Ricoh +* Makernote tags, indexed by their tag number. +* +******************************************************************************/ + +$GLOBALS[ "IFD_Tag_Definitions" ]["Ricoh"] = array( + + +0x0001 => array( 'Name' => "Makernote Data Type", + 'Type' => "String" ), + +0x0002 => array( 'Name' => "Version", + 'Type' => "Special" ), + +0x0e00 => array( 'Name' => "Print Image Matching Info", + 'Type' => "PIM" ), + +0x2001 => array( 'Name' => "Ricoh Camera Info Makernote Sub-IFD", + 'Type' => "Special" ), + +); + +/****************************************************************************** +* End of Global Variable: IFD_Tag_Definitions, Ricoh +******************************************************************************/ + + + + + + + + +/****************************************************************************** +* Global Variable: IFD_Tag_Definitions, RicohSubIFD +* +* Contents: This global variable provides definitions of the known Ricoh +* Camera Info Sub-IFD Makernote tags, indexed by their tag number. +* +******************************************************************************/ + +$GLOBALS[ "IFD_Tag_Definitions" ]["RicohSubIFD"] = array( + +); + +/****************************************************************************** +* End of Global Variable: IFD_Tag_Definitions, RicohSubIFD +******************************************************************************/ + + + + + + +?> \ No newline at end of file diff --git a/includes/jpeg_metadata_tk/Makernotes/sony.php b/includes/jpeg_metadata_tk/Makernotes/sony.php new file mode 100644 index 0000000..2690d81 --- /dev/null +++ b/includes/jpeg_metadata_tk/Makernotes/sony.php @@ -0,0 +1,244 @@ + array( 'Name' => "Print Image Matching Info", + 'Type' => "PIM" ), + +); + +/****************************************************************************** +* End of Global Variable: IFD_Tag_Definitions, Sony +******************************************************************************/ + + + + + + + + + + + +?> diff --git a/includes/jpeg_metadata_tk/PIM.php b/includes/jpeg_metadata_tk/PIM.php new file mode 100644 index 0000000..70be02d --- /dev/null +++ b/includes/jpeg_metadata_tk/PIM.php @@ -0,0 +1,279 @@ + $PI_tag, 'Data' => substr($data_part, $a*6+2, 4) , 'Decoded' => False ); + } + } + + } + + // Return the updated tag + return $newtag; + +} + +/****************************************************************************** +* End of Function: Decode_PIM +******************************************************************************/ + + + + +/****************************************************************************** +* +* Function: Encode_PIM +* +* Description: Encodes the contents of a EXIF tag containing Print Image +* Matching information, and returns the contents as a packed binary string +* +* Parameters: tag - An EXIF tag containing Print Image Matching information +* as from get_EXIF_JPEG +* Byte_Align - the Byte alignment to use - "MM" or "II" +* +* Returns: packed_data - The packed binary string representing the PIM data +* +******************************************************************************/ + +function Encode_PIM( $tag, $Byte_Align) +{ + + // Create a string to receive the packed data + $packed_data = ""; + + // Check that this tag is for Print Image Matching Info + if ( $tag['Type'] == "PIM" ) + { + // Check that the tag has been decoded - otherwise we don't need to do anything + if ( ( is_array( $tag['Data'] ) ) && + ( count ( $tag['Data'] ) > 0 ) ) + { + // Add the header to the packed data + $packed_data .= "PrintIM\x00"; + + // Add the version to the packed data + $packed_data .= $tag['Data']['Version'] . "\x00"; + + // Create a string to receive the tag data + $tag_data_str = ""; + + // Cycle through each tag + $tag_count = 0; + foreach( $tag['Data'] as $key => $curr_tag ) + { + // Make sure this is a tag and not supplementary info + if ( is_numeric( $key ) ) + { + // Count how many tags are created + $tag_count++; + + // Add the tag number to the packed tag data + $tag_data_str .= put_IFD_Data_Type( $curr_tag['Tag Number'], 3, $Byte_Align ); + + // Add the tag data to the packed tag data + $tag_data_str .= $curr_tag['Data']; + } + } + + // Add the tag count to the packed data + $packed_data .= put_IFD_Data_Type( $tag_count, 3, $Byte_Align ); + + // Add the packed tag data to the packed data + $packed_data .= $tag_data_str; + } + } + + // Return the resulting packed data + return $packed_data; + +} + +/****************************************************************************** +* End of Function: Encode_PIM +******************************************************************************/ + + + + + + + + + + +/****************************************************************************** +* +* Function: get_PIM_Text_Value +* +* Description: Interprets the contents of a EXIF tag containing Print Image +* Matching information, and returns content as as a text string +* +* Parameters: tag - An EXIF tag containing Print Image Matching information +* as from get_EXIF_JPEG +* Tag_Definitions_Name - The name of the Tag Definitions group +* within the global array IFD_Tag_Definitions +* +* Returns: output_str - The text string representing the PIM info +* +******************************************************************************/ + +function get_PIM_Text_Value( $Tag, $Tag_Definitions_Name ) +{ + + // Create a string to receive the output + $output_str = ""; + + // Check if the PIM tag has been decoded + if ( ( is_array( $Tag['Data'] ) ) && + ( count ( $Tag['Data'] ) > 0 ) ) + { + // The tag has been decoded + + // Add the Version to the output + $output_str = "Version: " . $Tag['Data']['Version'] . "\n"; + + // Check if the user wants to hide unknown tags + if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == FALSE ) + { + // The user wants to see unknown tags + // Cycle through each tag + foreach ( $Tag['Data'] as $PIM_tag_Key => $PIM_tag ) + { + // Check that the tag is not the version array element + if ( $PIM_tag_Key !== 'Version' ) + { + // Add the tag to the output + $output_str .= "Unknown Tag " . $PIM_tag['Tag Number'] . ": (" . strlen( $PIM_tag['Data'] ) . " bytes of data)\n"; + } + } + } + } + + // Return the output text + return $output_str; +} + +/****************************************************************************** +* End of Function: get_PIM_Text_Value +******************************************************************************/ + + + +?> diff --git a/includes/jpeg_metadata_tk/Photoshop_File_Info.php b/includes/jpeg_metadata_tk/Photoshop_File_Info.php new file mode 100644 index 0000000..1da7a8c --- /dev/null +++ b/includes/jpeg_metadata_tk/Photoshop_File_Info.php @@ -0,0 +1,2498 @@ + 1.11 : Changed displayed toolkit version numbers to reference Toolkit_Version.php +* +* URL: http://electronics.ozhiker.com +* +* License: This file is part of the PHP JPEG Metadata Toolkit. +* +* The PHP JPEG Metadata Toolkit is free software; you can +* redistribute it and/or modify it under the terms of the +* GNU General Public License as published by the Free Software +* Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* The PHP JPEG Metadata Toolkit is distributed in the hope +* that it will be useful, but WITHOUT ANY WARRANTY; without +* even the implied warranty of MERCHANTABILITY or FITNESS +* FOR A PARTICULAR PURPOSE. See the GNU General Public License +* for more details. +* +* You should have received a copy of the GNU General Public +* License along with the PHP JPEG Metadata Toolkit; if not, +* write to the Free Software Foundation, Inc., 59 Temple +* Place, Suite 330, Boston, MA 02111-1307 USA +* +* If you require a different license for commercial or other +* purposes, please contact the author: evan@ozhiker.com +* +******************************************************************************/ + +// TODO: XMP sections: XAPMM, TIFF, EXIF + + +require_once 'Toolkit_Version.php'; // Change: added as of version 1.11 + + +/****************************************************************************** +* Global Variable: Software Name +* +* Contents: The string that is appended to fields which store the name of +* the software editor. +* +******************************************************************************/ + +$GLOBALS[ "Software Name" ] = "(PHP JPEG Metadata Toolkit v" . $GLOBALS['Toolkit_Version'] . ")"; // Change: Changed version numbers to reference Toolkit_Version.php - as of version 1.11 + +/****************************************************************************** +* End of Global Variable: Software Name +******************************************************************************/ + + + + + + +/****************************************************************************** +* +* Function: get_photoshop_file_info +* +* Description: Retrieves Photoshop 'File Info' metadata in the same way that Photoshop +* does. The results are returned in an array as below: +* +* $file_info_array = array( +* "title" => "", +* "author" => "", +* "authorsposition" => "", // Note: Not used in Photoshop 7 or higher +* "caption" => "", +* "captionwriter" => "", +* "jobname" => "", // Note: Not used in Photoshop CS +* "copyrightstatus" => "", +* "copyrightnotice" => "", +* "ownerurl" => "", +* "keywords" => array( 0 => "", 1 => "", ... ), +* "category" => "", // Note: Max 3 characters +* "supplementalcategories" => array( 0 => "", 1 => "", ... ), +* "date" => "", // Note: DATE MUST BE IN YYYY-MM-DD format +* "city" => "", +* "state" => "", +* "country" => "", +* "credit" => "", +* "source" => "", +* "headline" => "", +* "instructions" => "", +* "transmissionreference" => "", +* "urgency" => "" ); +* +* Parameters: Exif_array - an array containing the EXIF information to be +* searched, as retrieved by get_EXIF_JPEG. (saves having to parse the EXIF again) +* XMP_array - an array containing the XMP information to be +* searched, as retrieved by read_XMP_array_from_text. (saves having to parse the XMP again) +* IRB_array - an array containing the Photoshop IRB information +* to be searched, as retrieved by get_Photoshop_IRB. (saves having to parse the IRB again) +* +* Returns: outputarray - an array as above, containing the Photoshop File Info data +* +******************************************************************************/ + +function get_photoshop_file_info( $Exif_array, $XMP_array, $IRB_array ) +{ + + // Create a blank array to receive the output + $outputarray = array( + "title" => "", + "author" => "", + "authorsposition" => "", + "caption" => "", + "captionwriter" => "", + "jobname" => "", + "copyrightstatus" => "", + "copyrightnotice" => "", + "ownerurl" => "", + "keywords" => array(), + "category" => "", + "supplementalcategories" => array(), + "date" => "", + "city" => "", + "state" => "", + "country" => "", + "credit" => "", + "source" => "", + "headline" => "", + "instructions" => "", + "transmissionreference" => "", + "urgency" => "" ); + + + /***************************************/ + + // XMP Processing + + + // Retrieve the dublin core section from the XMP header + + // Extract the Dublin Core section from the XMP + $dublincore_block = find_XMP_block( $XMP_array, "dc" ); + + // Check that the Dublin Core section exists + if ( $dublincore_block != FALSE ) + { + // Dublin Core Description Field contains caption + // Extract Description + $Item = find_XMP_item( $dublincore_block, "dc:description" ); + + // Check if Description Tag existed + if ( $Item != FALSE ) + { + // Ensure that the Description value exists and save it. + if ( ( array_key_exists( 'children', $Item ) ) && + ( $Item['children'][0]['tag'] == "rdf:Alt" ) && + ( array_key_exists( 'value', $Item['children'][0]['children'][0] ) ) ) + { + $outputarray = add_to_field( $outputarray, 'caption' , HTML_UTF8_Escape( $Item['children'][0]['children'][0]['value'] ), "\n" ); + } + } + + /***************************************/ + + // Dublin Core Creator Field contains author + // Extract Description + $Item = find_XMP_item( $dublincore_block, "dc:creator" ); + + // Check if Creator Tag existed + if ( $Item != FALSE ) + { + // Ensure that the Creator value exists and save it. + if ( ( !empty( $Item['children'][0] ) ) && + ( $Item['children'][0]['tag'] =="rdf:Seq" ) && + ( !empty( $Item['children'][0]['children'][0]['empty'] ) ) ) + { + $outputarray = add_to_field( $outputarray, 'author' , HTML_UTF8_Escape( $Item['children'][0]['children'][0]['value'] ), "\n" ); + } + } + + /***************************************/ + + // Dublin Core Title Field contains title + // Extract Title + $Item = find_XMP_item( $dublincore_block, "dc:title" ); + + // Check if Title Tag existed + if ( $Item != FALSE ) + { + // Ensure that the Title value exists and save it. + if ( ( array_key_exists( 'children', $Item ) ) && + ( $Item['children'][0]['tag'] =="rdf:Alt" ) && + ( array_key_exists( 'value', $Item['children'][0]['children'][0] ) ) ) + { + + $outputarray = add_to_field( $outputarray, 'title' , HTML_UTF8_Escape( $Item['children'][0]['children'][0]['value'] ), "," ); + } + } + + /***************************************/ + + // Dublin Core Rights Field contains copyrightnotice + // Extract Rights + $Item = find_XMP_item( $dublincore_block, "dc:rights" ); + + // Check if Rights Tag existed + if ( $Item != FALSE ) + { + // Ensure that the Rights value exists and save it. + if ( ( array_key_exists( 'children', $Item ) ) && + ( $Item['children'][0]['tag'] =="rdf:Alt" ) && + ( array_key_exists( 'value', $Item['children'][0]['children'][0] ) ) ) + { + + $outputarray = add_to_field( $outputarray, 'copyrightnotice' , HTML_UTF8_Escape( $Item['children'][0]['children'][0]['value'] ), "," ); + } + } + + /***************************************/ + + // Dublin Core Subject Field contains keywords + // Extract Subject + $Item = find_XMP_item( $dublincore_block, "dc:subject" ); + + // Check if Subject Tag existed + if ( $Item != FALSE ) + { + // Ensure that the Subject values exist + if ( ( array_key_exists( 'children', $Item ) ) && ( $Item['children'][0]['tag'] =="rdf:Bag" ) ) + { + // Cycle through each Subject value and save them + foreach ( $Item['children'][0]['children'] as $keywords ) + { + if ( ! in_array ( HTML_UTF8_Escape( $keywords['value'] ), $outputarray['keywords'])) + { + if ( array_key_exists( 'value', $keywords ) ) + { + $outputarray['keywords'][] = HTML_UTF8_Escape( $keywords['value'] ); + } + } + } + } + } + + + } + + /***************************************/ + + // Find the Photoshop Information within the XMP block + $photoshop_block = find_XMP_block( $XMP_array, "photoshop" ); + + // Check that the Photoshop Information exists + if ( $photoshop_block != FALSE ) + { + // The Photoshop CaptionWriter tag contains captionwriter - Find it + $Item = find_XMP_item( $photoshop_block, "photoshop:CaptionWriter" ); + + // Check that the CaptionWriter Field exists and save the value + if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) + { + $outputarray = add_to_field( $outputarray, 'captionwriter' , HTML_UTF8_Escape( $Item['value'] ), "," ); + } + + /***************************************/ + + // The Photoshop Headline tag contains headline - Find it + $Item = find_XMP_item( $photoshop_block, "photoshop:Headline" ); + + // Check that the Headline Field exists and save the value + if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) + { + $outputarray = add_to_field( $outputarray, 'headline' , HTML_UTF8_Escape( $Item['value'] ), "," ); + } + + /***************************************/ + + // The Photoshop Instructions tag contains instructions - Find it + $Item = find_XMP_item( $photoshop_block, "photoshop:Instructions" ); + + // Check that the Instructions Field exists and save the value + if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) + { + $outputarray = add_to_field( $outputarray, 'instructions' , HTML_UTF8_Escape( $Item['value'] ), "\n" ); + } + + /***************************************/ + + // The Photoshop AuthorsPosition tag contains authorsposition - Find it + $Item = find_XMP_item( $photoshop_block, "photoshop:AuthorsPosition" ); + + // Check that the AuthorsPosition Field exists and save the value + if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) + { + $outputarray = add_to_field( $outputarray, 'authorsposition' , HTML_UTF8_Escape( $Item['value'] ), "," ); + } + + /***************************************/ + + // The Photoshop Credit tag contains credit - Find it + $Item = find_XMP_item( $photoshop_block, "photoshop:Credit" ); + + // Check that the Credit Field exists and save the value + if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) + { + $outputarray = add_to_field( $outputarray, 'credit' , HTML_UTF8_Escape( $Item['value'] ), "," ); + } + + /***************************************/ + + // The Photoshop Source tag contains source - Find it + $Item = find_XMP_item( $photoshop_block, "photoshop:Source" ); + + // Check that the Credit Field exists and save the value + if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) + { + $outputarray = add_to_field( $outputarray, 'source' , HTML_UTF8_Escape( $Item['value'] ), "," ); + } + + /***************************************/ + + // The Photoshop City tag contains city - Find it + $Item = find_XMP_item( $photoshop_block, "photoshop:City" ); + + // Check that the City Field exists and save the value + if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) + { + $outputarray = add_to_field( $outputarray, 'city' , HTML_UTF8_Escape( $Item['value'] ), "," ); + } + + /***************************************/ + + // The Photoshop State tag contains state - Find it + $Item = find_XMP_item( $photoshop_block, "photoshop:State" ); + + // Check that the State Field exists and save the value + if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) + { + $outputarray = add_to_field( $outputarray, 'state' , HTML_UTF8_Escape( $Item['value'] ), "," ); + } + + /***************************************/ + + // The Photoshop Country tag contains country - Find it + $Item = find_XMP_item( $photoshop_block, "photoshop:Country" ); + + // Check that the Country Field exists and save the value + if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) + { + $outputarray = add_to_field( $outputarray, 'country' , HTML_UTF8_Escape( $Item['value'] ), "," ); + } + + /***************************************/ + + // The Photoshop TransmissionReference tag contains transmissionreference - Find it + $Item = find_XMP_item( $photoshop_block, "photoshop:TransmissionReference" ); + + // Check that the TransmissionReference Field exists and save the value + if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) + { + $outputarray = add_to_field( $outputarray, 'transmissionreference' , HTML_UTF8_Escape( $Item['value'] ), "," ); + } + + /***************************************/ + + // The Photoshop Category tag contains category - Find it + $Item = find_XMP_item( $photoshop_block, "photoshop:Category" ); + + // Check that the TransmissionReference Field exists and save the value + if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) + { + $outputarray = add_to_field( $outputarray, 'category' , HTML_UTF8_Escape( $Item['value'] ), "," ); + } + + /***************************************/ + + // The Photoshop DateCreated tag contains date - Find it + $Item = find_XMP_item( $photoshop_block, "photoshop:DateCreated" ); + + // Check that the DateCreated Field exists and save the value + if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) + { + $outputarray = add_to_field( $outputarray, 'date' , HTML_UTF8_Escape( $Item['value'] ), "," ); + } + + /***************************************/ + + // The Photoshop Urgency tag contains urgency - Find it + $Item = find_XMP_item( $photoshop_block, "photoshop:Urgency" ); + + // Check that the Urgency Field exists and save the value + if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) + { + $outputarray = add_to_field( $outputarray, 'urgency' , HTML_UTF8_Escape( $Item['value'] ), "," ); + } + + /***************************************/ + + // The Photoshop SupplementalCategories tag contains supplementalcategories - Find it + $Item = find_XMP_item( $photoshop_block, "photoshop:SupplementalCategories" ); + + // Check that the SupplementalCategories Field exists + if ( $Item != FALSE ) + { + // Check that the values exist + if ( ( array_key_exists( 'children', $Item ) ) && ( $Item['children'][0]['tag'] =="rdf:Bag" ) ) + { + // Cycle through the values and save them + foreach ( $Item['children'][0]['children'] as $sup_category ) + { + if ( ( array_key_exists( 'value', $sup_category ) ) && + ( ! in_array ( HTML_UTF8_Escape( $sup_category['value'] ), $outputarray['supplementalcategories'])) ) + { + if ( array_key_exists( 'value', $sup_category ) ) + { + $outputarray['supplementalcategories'][] = HTML_UTF8_Escape( $sup_category['value'] ); + } + } + } + } + } + + } + + /***************************************/ + + // Find the Job Reference Information within the XMP block + $job_block = find_XMP_block( $XMP_array, "xapBJ" ); + + // Check that the Job Reference Information exists + if ( $job_block != FALSE ) + { + // The JobRef Field contains jobname - Find it + $Item = find_XMP_item( $job_block, "xapBJ:JobRef" ); + + // Check that the JobRef Field exists + if ( $Item != FALSE ) + { + // Check that the value exists and save it + if ( ( array_key_exists( 'children', $Item ) ) && + ( $Item['children'][0]['tag'] =="rdf:Bag" ) && + ( array_key_exists( 'children', $Item['children'][0] ) ) && + ( $Item['children'][0]['children'][0]['tag'] =="rdf:li" ) && + ( array_key_exists( 'children', $Item['children'][0]['children'][0] ) ) && + ( $Item['children'][0]['children'][0]['children'][0]['tag'] =="stJob:name" ) && + ( array_key_exists( 'value', $Item['children'][0]['children'][0]['children'][0] ) ) ) + { + $outputarray = add_to_field( $outputarray, 'jobname' , HTML_UTF8_Escape( $Item['children'][0]['children'][0]['children'][0]['value'] ), "," ); + } + } + } + + + /***************************************/ + + // Find the Rights Information within the XMP block + $rights_block = find_XMP_block( $XMP_array, "xapRights" ); + + // Check that the Rights Information exists + if ( $rights_block != FALSE ) + { + // The WebStatement Field contains ownerurl - Find it + $Item = find_XMP_item( $rights_block, "xapRights:WebStatement" ); + + // Check that the WebStatement Field exists and save the value + if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) + { + $outputarray = add_to_field( $outputarray, 'ownerurl' , HTML_UTF8_Escape( $Item['value'] ), "\n" ); + } + + /***************************************/ + + // The Marked Field contains copyrightstatus - Find it + $Item = find_XMP_item( $rights_block, "xapRights:Marked" ); + + // Check that the Marked Field exists and save the value + if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) + { + if ( $Item['value'] == "True" ) + { + $outputarray = add_to_field( $outputarray, 'copyrightstatus' , "Copyrighted Work", "," ); + } + else + { + $outputarray = add_to_field( $outputarray, 'copyrightstatus' , "Public Domain", "," ); + } + } + + } + + + + + + /***************************************/ + + // Photoshop IRB Processing + + // Check that the Photoshop IRB exists + if ( $IRB_array != FALSE ) + { + // Create a translation table to convert carriage returns to linefeeds + $irbtrans = array("\x0d" => "\x0a"); + + // The Photoshop IRB Copyright flag (0x040A) contains copyrightstatus - find it + $IRB_copyright_flag = find_Photoshop_IRB_Resource( $IRB_array, 0x040A ); + + // Check if the Copyright flag Field exists, and save the value + if( $IRB_copyright_flag != FALSE ) + { + // Check the value of the copyright flag + if ( hexdec( bin2hex( $IRB_copyright_flag['ResData'] ) ) == 1 ) + { + // Save the value + $outputarray = add_to_field( $outputarray, 'copyrightstatus' , "Copyrighted Work", "," ); + } + else + { + // Do nothing - copyrightstatus will be set to unmarked if still blank at end + } + } + + /***************************************/ + + // The Photoshop IRB URL (0x040B) contains ownerurl - find it + $IRB_url = find_Photoshop_IRB_Resource( $IRB_array, 0x040B ); + + // Check if the URL Field exists and save the value + if( $IRB_url != FALSE ) + { + $outputarray = add_to_field( $outputarray, 'ownerurl' , strtr( $IRB_url['ResData'], $irbtrans ), "\n" ); + } + + /***************************************/ + + // Extract any IPTC block from the Photoshop IRB information + $IPTC_array = get_Photoshop_IPTC( $IRB_array ); + + // Check if the IPTC block exits + if ( ( $IPTC_array != FALSE ) && ( count( $IPTC_array ) != 0 ) ) + { + // The IPTC Caption/Abstract Field contains caption - find it + $record = find_IPTC_Resource( $IPTC_array, "2:120" ); + + // Check if the Caption/Abstract Field exists and save the value + if ( $record != FALSE ) + { + $outputarray = add_to_field( $outputarray, 'caption' , strtr( $record['RecData'], $irbtrans ), "\n" ); + } + + /***************************************/ + + // The IPTC Caption Writer/Editor Field contains captionwriter - find it + $record = find_IPTC_Resource( $IPTC_array, "2:122" ); + + // Check if the Caption Writer/Editor Field exists and save the value + if ( $record != FALSE ) + { + $outputarray = add_to_field( $outputarray, 'captionwriter' , strtr( $record['RecData'], $irbtrans ), "\n" ); + } + + /***************************************/ + + // The IPTC Headline Field contains headline - find it + $record = find_IPTC_Resource( $IPTC_array, "2:105" ); + + // Check if the Headline Field exists and save the value + if ( $record != FALSE ) + { + $outputarray = add_to_field( $outputarray, 'headline' , strtr( $record['RecData'], $irbtrans ), "\n" ); + } + + /***************************************/ + + // The IPTC Special Instructions Field contains instructions - find it + $record = find_IPTC_Resource( $IPTC_array, "2:40" ); + + // Check if the Special Instructions Field exists and save the value + if ( $record != FALSE ) + { + $outputarray = add_to_field( $outputarray, 'instructions' , strtr( $record['RecData'], $irbtrans ), "\n" ); + } + + /***************************************/ + + // The IPTC By-Line Field contains author - find it + $record = find_IPTC_Resource( $IPTC_array, "2:80" ); + + // Check if the By-Line Field exists and save the value + if ( $record != FALSE ) + { + $outputarray = add_to_field( $outputarray, 'author' , strtr( $record['RecData'], $irbtrans ), "\n" ); + } + + /***************************************/ + + // The IPTC By-Line Title Field contains authorsposition - find it + $record = find_IPTC_Resource( $IPTC_array, "2:85" ); + + // Check if the By-Line Title Field exists and save the value + if ( $record != FALSE ) + { + $outputarray = add_to_field( $outputarray, 'authorsposition' , strtr( $record['RecData'], $irbtrans ), "\n" ); + } + + /***************************************/ + + // The IPTC Credit Field contains credit - find it + $record = find_IPTC_Resource( $IPTC_array, "2:110" ); + + // Check if the Credit Field exists and save the value + if ( $record != FALSE ) + { + $outputarray = add_to_field( $outputarray, 'credit' , strtr( $record['RecData'], $irbtrans ), "\n" ); + } + + /***************************************/ + + // The IPTC Source Field contains source - find it + $record = find_IPTC_Resource( $IPTC_array, "2:115" ); + + // Check if the Source Field exists and save the value + if ( $record != FALSE ) + { + $outputarray = add_to_field( $outputarray, 'source' , strtr( $record['RecData'], $irbtrans ), "\n" ); + } + + /***************************************/ + + // The IPTC Object Name Field contains title - find it + $record = find_IPTC_Resource( $IPTC_array, "2:05" ); + + // Check if the Object Name Field exists and save the value + if ( $record != FALSE ) + { + $outputarray = add_to_field( $outputarray, 'title' , strtr( $record['RecData'], $irbtrans ), "\n" ); + } + + /***************************************/ + + // The IPTC Date Created Field contains date - find it + $record = find_IPTC_Resource( $IPTC_array, "2:55" ); + + // Check if the Date Created Field exists and save the value + if ( $record != FALSE ) + { + $date_array = unpack( "a4Year/a2Month/A2Day", $record['RecData'] ); + $tmpdate = $date_array['Year'] . "-" . $date_array['Month'] . "-" . $date_array['Day']; + $outputarray = add_to_field( $outputarray, 'date' , strtr( $tmpdate, $irbtrans ), "," ); + + } + + /***************************************/ + + // The IPTC City Field contains city - find it + $record = find_IPTC_Resource( $IPTC_array, "2:90" ); + + // Check if the City Field exists and save the value + if ( $record != FALSE ) + { + $outputarray = add_to_field( $outputarray, 'city' , strtr( $record['RecData'], $irbtrans ), "\n" ); + } + + /***************************************/ + + // The IPTC Province/State Field contains state - find it + $record = find_IPTC_Resource( $IPTC_array, "2:95" ); + + // Check if the Province/State Field exists and save the value + if ( $record != FALSE ) + { + $outputarray = add_to_field( $outputarray, 'state' , strtr( $record['RecData'], $irbtrans ), "\n" ); + } + + /***************************************/ + + // The IPTC Country/Primary Location Name Field contains country - find it + $record = find_IPTC_Resource( $IPTC_array, "2:101" ); + + // Check if the Country/Primary Location Name Field exists and save the value + if ( $record != FALSE ) + { + $outputarray = add_to_field( $outputarray, 'country' , strtr( $record['RecData'], $irbtrans ), "\n" ); + } + + /***************************************/ + + // The IPTC Original Transmission Reference Field contains transmissionreference - find it + $record = find_IPTC_Resource( $IPTC_array, "2:103" ); + + // Check if the Original Transmission Reference Field exists and save the value + if ( $record != FALSE ) + { + $outputarray = add_to_field( $outputarray, 'transmissionreference' , strtr( $record['RecData'], $irbtrans ), "\n" ); + } + + /***************************************/ + + // The IPTC Category Field contains category - find it + $record = find_IPTC_Resource( $IPTC_array, "2:15" ); + + // Check if the Category Field exists and save the value + if ( $record != FALSE ) + { + $outputarray = add_to_field( $outputarray, 'category' , strtr( $record['RecData'], $irbtrans ), "\n" ); + } + + + /***************************************/ + + // Cycle through the IPTC records looking for Supplemental Category records + foreach ($IPTC_array as $record) + { + // Check if a Supplemental Category record has been found + if ( $record['IPTC_Type'] == "2:20" ) + { + // A Supplemental Category record has been found, save it's value if the value doesn't already exist + if ( ! in_array ( $record['RecData'], $outputarray['supplementalcategories'])) + { + $outputarray['supplementalcategories'][] = strtr( $record['RecData'], array("\x0a" => "", "\x0d" => " ") ) ; + } + } + } + + + /***************************************/ + + // The IPTC Urgency Field contains urgency - find it + $record = find_IPTC_Resource( $IPTC_array, "2:10" ); + + // Check if the Urgency Field exists and save the value + if ( $record != FALSE ) + { + $outputarray = add_to_field( $outputarray, 'urgency' , strtr( $record['RecData'], $irbtrans ), "\n" ); + } + + + + /***************************************/ + + // Cycle through the IPTC records looking for Keywords records + foreach ($IPTC_array as $record) + { + // Check if a Keywords record has been found + if ( $record['IPTC_Type'] == "2:25" ) + { + // A Keywords record has been found, save it's value if the value doesn't already exist + if ( ! in_array ( $record['RecData'], $outputarray['keywords'])) + { + $outputarray['keywords'][] = strtr( $record['RecData'], array("\x0a" => "", "\x0d" => " ") ) ; + } + } + } + + + /***************************************/ + + // The IPTC Copyright Notice Field contains copyrightnotice - find it + $record = find_IPTC_Resource( $IPTC_array, "2:116" ); + + // Check if the Copyright Field exists and save the value + if ( $record != FALSE ) + { + $outputarray = add_to_field( $outputarray, 'copyrightnotice' , strtr( $record['RecData'], $irbtrans ), "\n" ); + } + + } + } + + + + + /***************************************/ + + // EXIF Processing + + + // Retreive Information from the EXIF data if it exists + + if ( ( $Exif_array != FALSE ) || ( count( $Exif_array ) == 0 ) ) + { + // Check the Image Description Tag - it can contain the caption + if ( array_key_exists( 270, $Exif_array[0] ) ) + { + $outputarray = add_to_field( $outputarray, 'caption' , $Exif_array[0][270]['Data'][0], "\n" ); + } + + /***************************************/ + + // Check the Copyright Information Tag - it contains the copyrightnotice + if ( array_key_exists( 33432, $Exif_array[0] ) ) + { + $outputarray = add_to_field( $outputarray, 'copyrightnotice' , HTML_UTF8_UnEscape( $Exif_array[0][33432]['Data'][0] ), "\n" ); + } + + /***************************************/ + + // Check the Artist Name Tag - it contains the author + if ( array_key_exists( 315, $Exif_array[0] ) ) + { + $outputarray = add_to_field( $outputarray, 'author' , HTML_UTF8_UnEscape( $Exif_array[0][315]['Data'][0] ), "\n" ); + } + + } + + + /***************************/ + + // FINISHED RETRIEVING INFORMATION + + // Perform final processing + + + // Check if any urgency information was found + if ( $outputarray["urgency"] == "" ) + { + // No urgency information was found - set it to default (None) + $outputarray["urgency"] = "none"; + } + + // Check if any copyrightstatus information was found + if ( $outputarray["copyrightstatus"] == "" ) + { + // No copyrightstatus information was found - set it to default (Unmarked) + $outputarray["copyrightstatus"] = "unmarked"; + } + + // Return the resulting Photoshop File Info Array + return $outputarray; + +} + +/****************************************************************************** +* End of Function: get_photoshop_file_info +******************************************************************************/ + + + + + + +/****************************************************************************** +* +* Function: put_photoshop_file_info +* +* Description: Stores Photoshop 'File Info' metadata in the same way that Photoshop +* does. The 'File Info' metadata must be in an array similar to that +* returned by get_photoshop_file_info, as follows: +* +* $file_info_array = array( +* "title" => "", +* "author" => "", +* "authorsposition" => "", // Note: Not used in Photoshop 7 or higher +* "caption" => "", +* "captionwriter" => "", +* "jobname" => "", // Note: Not used in Photoshop CS +* "copyrightstatus" => "", +* "copyrightnotice" => "", +* "ownerurl" => "", +* "keywords" => array( 0 => "", 1 => "", ... ), +* "category" => "", // Note: Max 3 characters +* "supplementalcategories" => array( 0 => "", 1 => "", ... ), +* "date" => "", // Note: DATE MUST BE IN YYYY-MM-DD format +* "city" => "", +* "state" => "", +* "country" => "", +* "credit" => "", +* "source" => "", +* "headline" => "", +* "instructions" => "", +* "transmissionreference" => "", +* "urgency" => "" ); +* +* Parameters: jpeg_header_data - a JPEG header data array in the same format +* as from get_jpeg_header_data. This contains the +* header information which is to be updated. +* new_ps_file_info_array - An array as above, which contains the +* 'File Info' metadata information to be +* written. +* Old_Exif_array - an array containing the EXIF information to be +* updated, as retrieved by get_EXIF_JPEG. (saves having to parse the EXIF again) +* Old_XMP_array - an array containing the XMP information to be +* updated, as retrieved by read_XMP_array_from_text. (saves having to parse the XMP again) +* Old_IRB_array - an array containing the Photoshop IRB information +* to be updated, as retrieved by get_Photoshop_IRB. (saves having to parse the IRB again) +* +* Returns: jpeg_header_data - a JPEG header data array in the same format +* as from get_jpeg_header_data, containing the +* Photshop 'File Info' metadata. This can then +* be written to a file using put_jpeg_header_data. +* +******************************************************************************/ + +function put_photoshop_file_info( $jpeg_header_data, $new_ps_file_info_array, $Old_Exif_array, $Old_XMP_array, $Old_IRB_array ) +{ + /*******************************************/ + // PREPROCESSING + + // Check that the date is in the correct format (YYYY-MM-DD) + + // Explode the date into pieces using the - symbol + $date_pieces = explode( "-", $new_ps_file_info_array[ 'date' ] ); + + // If there are not 3 pieces to the date, it is invalid + if ( count( $date_pieces ) != 3 ) + { + // INVALID DATE + echo "Invalid Date - must be YYYY-MM-DD format
"; + return FALSE; + } + + // Cycle through each piece of the date + foreach( $date_pieces as $piece ) + { + // If the piece is not numeric, then the date is invalid. + if ( ! is_numeric( $piece ) ) + { + // INVALID DATE + echo "Invalid Date - must be YYYY-MM-DD format
"; + return FALSE; + } + } + + // Make a unix timestamp at midnight on the date specified + $date_stamp = mktime( 0,0,0, $date_pieces[1], $date_pieces[2], $date_pieces[0] ); + + + + + // Create a translation table to remove carriage return characters + $trans = array( "\x0d" => "" ); + + // Cycle through each of the File Info elements + foreach( $new_ps_file_info_array as $valkey => $val ) + { + // If the element is 'Keywords' or 'Supplemental Categories', then + // it is an array, and needs to be treated as one + if ( ( $valkey != 'supplementalcategories' ) && ( $valkey != 'keywords' ) ) + { + // Not Keywords or Supplemental Categories + // Convert escaped HTML characters to UTF8 and remove carriage returns + $new_ps_file_info_array[ $valkey ] = strtr( HTML_UTF8_UnEscape( $val ), $trans ); + } + else + { + // Either Keywords or Supplemental Categories + // Cycle through the array, + foreach( $val as $subvalkey => $subval ) + { + // Convert escaped HTML characters to UTF8 and remove carriage returns + $new_ps_file_info_array[ $valkey ][ $subvalkey ] = strtr( HTML_UTF8_UnEscape( $subval ), $trans ); + } + } + } + + + + + + /*******************************************/ + + // EXIF Processing + + + // Check if the EXIF array exists + if( $Old_Exif_array == FALSE ) + { + // EXIF Array doesn't exist - create a new one + $new_Exif_array = array ( 'Byte_Align' => "MM", + 'Makernote_Tag' => false, + 'Tags Name' => "TIFF", + 0 => array( "Tags Name" => "TIFF" ) ); + } + else + { + // EXIF Array Does Exist - use it + $new_Exif_array = $Old_Exif_array; + } + + + + // Update the EXIF Image Description Tag with the new value + $new_Exif_array[0][270] = array ( "Tag Name" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 270 ]['Name'], + "Tag Number" => 270, + "Data Type" => 2, + "Type" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 270 ]['Type'], + "Data" => array( HTML_UTF8_Escape( $new_ps_file_info_array[ 'caption' ]) )); + + // Update the EXIF Artist Name Tag with the new value + $new_Exif_array[0][315] = array ( "Tag Name" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 315 ]['Name'], + "Tag Number" => 315, + "Data Type" => 2, + "Type" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 315 ]['Type'], + "Data" => array( HTML_UTF8_Escape( $new_ps_file_info_array[ 'author' ] ) ) ); + + // Update the EXIF Copyright Information Tag with the new value + $new_Exif_array[0][33432] = array ( "Tag Name" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 33432 ]['Name'], + "Tag Number" => 33432, + "Data Type" => 2, + "Type" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 33432 ]['Type'], + "Data" => array( HTML_UTF8_Escape( $new_ps_file_info_array[ 'copyrightnotice' ]) ) ); + + + // Photoshop checks if the "Date and Time of Original" and "Date and Time when Digitized" tags exist + // If they don't exist, it means that the EXIF date may be wiped out if it is changed, so Photoshop + // copies the EXIF date to these two tags + + if ( ( array_key_exists( 306, $new_Exif_array[0] ) )&& + ( array_key_exists( 34665, $new_Exif_array[0] ) ) && + ( array_key_exists( 0, $new_Exif_array[0][34665] ) ) ) + { + // Replace "Date and Time of Original" if it doesn't exist + if ( ! array_key_exists( 36867, $new_Exif_array[0][34665][0] ) ) + { + $new_Exif_array[0][34665][0][36867] = array ( "Tag Name" => $GLOBALS[ "IFD_Tag_Definitions" ]['EXIF'][ 36867 ]['Name'], + "Tag Number" => 36867, + "Data Type" => 2, + "Type" => $GLOBALS[ "IFD_Tag_Definitions" ]['EXIF'][ 36867 ]['Type'], + "Data" => $new_Exif_array[0][306]['Data'] ); + } + + // Replace "Date and Time when Digitized" if it doesn't exist + if ( ! array_key_exists( 36868, $new_Exif_array[0][34665][0] ) ) + { + $new_Exif_array[0][34665][0][36868] = array ( "Tag Name" => $GLOBALS[ "IFD_Tag_Definitions" ]['EXIF'][ 36868 ]['Name'], + "Tag Number" => 36868, + "Data Type" => 2, + "Type" => $GLOBALS[ "IFD_Tag_Definitions" ]['EXIF'][ 36868 ]['Type'], + "Data" => $new_Exif_array[0][306]['Data'] ); + } + } + + + // Photoshop changes the EXIF date Tag (306) to the current date, not the date that was entered in File Info + $exif_date = date ( "Y:m:d H:i:s" ); + + // Update the EXIF Date and Time Tag with the new value + $new_Exif_array[0][306] = array ( "Tag Name" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 306 ]['Name'], + "Tag Number" => 306, + "Data Type" => 2, + "Type" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 306 ]['Type'], + "Data" => array( $exif_date ) ); + + + + // Photoshop replaces the EXIF Software or Firmware Tag with "Adobe Photoshop ..." + // This toolkit instead preserves existing value and appends the toolkit name to the end of it + + // Check if the EXIF Software or Firmware Tag exists + if ( array_key_exists( 305, $new_Exif_array[0] ) ) + { + // An existing EXIF Software or Firmware Tag was found + // Check if the existing Software or Firmware Tag already contains the Toolkit's name + if ( stristr ( $new_Exif_array[0][305]['Data'][0], $GLOBALS[ "Software Name" ]) == FALSE ) + { + // Toolkit Name string not found in the existing Software/Firmware string - append it. + $firmware_str = $new_Exif_array[0][305]['Data'][0] . " " . $GLOBALS[ "Software Name" ]; + } + else + { + // Toolkit name already exists in Software/Firmware string - don't put another copy in the string + $firmware_str = $new_Exif_array[0][305]['Data'][0]; + } + } + else + { + // No Software/Firmware string exists - create one + $firmware_str = $GLOBALS[ "Software Name" ]; + } + + // Update the EXIF Software/Firmware Tag with the new value + $new_Exif_array[0][305] = array( "Tag Name" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 305 ]['Name'], + "Tag Number" => 305, + "Data Type" => 2, + "Type" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 305 ]['Type'], + "Data" => array( HTML_UTF8_Escape( $firmware_str ) ) ); + + + + + + /*******************************************/ + + // Photoshop IRB Processing + + + // Check if there is an existing Photoshop IRB array + if ($Old_IRB_array == FALSE ) + { + // No existing IRB array - create one + $new_IRB_array = array(); + } + else + { + // There is an existing Photoshop IRB array - use it + $new_IRB_array = $Old_IRB_array; + } + + // Remove any existing Copyright Flag, URL, or IPTC resources - these will be re-written + foreach( $new_IRB_array as $resno => $res ) + { + if ( ( $res[ 'ResID' ] == 0x040A ) || + ( $res[ 'ResID' ] == 0x040B ) || + ( $res[ 'ResID' ] == 0x0404 ) ) + { + array_splice( $new_IRB_array, $resno, 1 ); + } + } + + + // Add a new Copyright Flag resource + if ( $new_ps_file_info_array[ 'copyrightstatus' ] == "Copyrighted Work" ) + { + $PS_copyright_flag = "\x01"; // Copyrighted + } + else + { + $PS_copyright_flag = "\x00"; // Public domain or Unmarked + } + $new_IRB_array[] = array( 'ResID' => 0x040A, + 'ResName' => $GLOBALS[ "Photoshop_ID_Names" ][0x040A], + 'ResDesc' => $GLOBALS[ "Photoshop_ID_Descriptions" ][0x040A], + 'ResEmbeddedName' => "", + 'ResData' => $PS_copyright_flag ); + + + + // Add a new URL resource + $new_IRB_array[] = array( 'ResID' => 0x040B, + 'ResName' => $GLOBALS[ "Photoshop_ID_Names" ][0x040B], + 'ResDesc' => $GLOBALS[ "Photoshop_ID_Descriptions" ][0x040B], + 'ResEmbeddedName' => "", + 'ResData' => $new_ps_file_info_array[ 'ownerurl' ] ); + + + + // Create IPTC resource + + // IPTC requires date to be in the following format YYYYMMDD + $iptc_date = date( "Ymd", $date_stamp ); + + // Create the new IPTC array + $new_IPTC_array = array ( + 0 => + array ( + 'IPTC_Type' => '2:00', + 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:00'], + 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:00'], + 'RecData' => "\x00\x02", + ), + 1 => + array ( + 'IPTC_Type' => '2:120', + 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:120'], + 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:120'], + 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'caption' ] ), 0 , 2000 ), + ), + 2 => + array ( + 'IPTC_Type' => '2:122', + 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:122'], + 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:122'], + 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'captionwriter' ] ), 0 , 32 ), + ), + 3 => + array ( + 'IPTC_Type' => '2:105', + 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:105'], + 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:105'], + 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'headline' ] ), 0 , 256 ), + ), + 4 => + array ( + 'IPTC_Type' => '2:40', + 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:40'], + 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:40'], + 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'instructions' ] ), 0, 256 ), + ), + 5 => + array ( + 'IPTC_Type' => '2:80', + 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:80'], + 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:80'], + 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'author' ] ), 0, 32 ), + ), + 6 => + array ( + 'IPTC_Type' => '2:85', + 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:85'], + 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:85'], + 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'authorsposition' ] ), 0, 32 ), + ), + 7 => + array ( + 'IPTC_Type' => '2:110', + 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:110'], + 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:110'], + 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'credit' ] ), 0, 32 ), + ), + 8 => + array ( + 'IPTC_Type' => '2:115', + 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:115'], + 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:115'], + 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'source' ] ), 0, 32 ), + ), + 9 => + array ( + 'IPTC_Type' => '2:05', + 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:05'], + 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:05'], + 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'title' ] ), 0, 64 ), + ), + 10 => + array ( + 'IPTC_Type' => '2:55', + 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:55'], + 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:55'], + 'RecData' => "$iptc_date", + ), + 11 => + array ( + 'IPTC_Type' => '2:90', + 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:90'], + 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:90'], + 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'city' ] ), 0, 32 ), + ), + 12 => + array ( + 'IPTC_Type' => '2:95', + 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:95'], + 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:95'], + 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'state' ] ), 0, 32 ), + ), + 13 => + array ( + 'IPTC_Type' => '2:101', + 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:101'], + 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:101'], + 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'country' ] ), 0, 64 ), + ), + 14 => + array ( + 'IPTC_Type' => '2:103', + 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:103'], + 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:103'], + 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'transmissionreference' ] ), 0, 32 ), + ), + 15 => + array ( + 'IPTC_Type' => '2:15', + 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:15'], + 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:15'], + 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'category' ] ), 0, 3 ), + ), + 21 => + array ( + 'IPTC_Type' => '2:116', + 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:10'], + 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:10'], + 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'copyrightnotice' ] ), 0, 128 ), + ), + ); + + // Check the value of urgency is valid + if ( ( $new_ps_file_info_array[ 'urgency' ] > 0 ) && ( $new_ps_file_info_array[ 'urgency' ] < 9 ) ) + { + // Add the Urgency item to the IPTC array + $new_IPTC_array[] = array ( + 'IPTC_Type' => '2:10', + 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:10'], + 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:10'], + 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'urgency' ] ), 0, 1 ), + ); + } + + // Cycle through the Supplemental Categories, + foreach( $new_ps_file_info_array[ 'supplementalcategories' ] as $supcat ) + { + // Add this Supplemental Category to the IPTC array + $new_IPTC_array[] = array ( + 'IPTC_Type' => '2:20', + 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:20'], + 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:20'], + 'RecData' => HTML_UTF8_Escape( $supcat ), + ); + } + + + // Cycle through the Keywords, + foreach( $new_ps_file_info_array[ 'keywords' ] as $keyword ) + { + // Add this Keyword to the IPTC array + $new_IPTC_array[] = array ( + 'IPTC_Type' => '2:25', + 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:25'], + 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:25'], + 'RecData' => $keyword, + ); + } + + + /***********************************/ + + // XMP Processing + + // Check if XMP existed previously + if ($Old_XMP_array == FALSE ) + { + // XMP didn't exist - create a new one based on a blank structure + $new_XMP_array = XMP_Check( $GLOBALS[ 'Blank XMP Structure' ], array( ) ); + } + else + { + // XMP does exist + // Some old XMP processors used x:xapmeta, check for this + if ( $Old_XMP_array[0]['tag'] == 'x:xapmeta' ) + { + // x:xapmeta found - change it to x:xmpmeta + $Old_XMP_array[0]['tag'] = 'x:xmpmeta'; + } + + // Ensure that the existing XMP has all required fields, and add any that are missing + $new_XMP_array = XMP_Check( $GLOBALS[ 'Blank XMP Structure' ], $Old_XMP_array ); + } + + + // Process the XMP Photoshop block + + // Find the Photoshop Information within the XMP block + $photoshop_block = & find_XMP_block( $new_XMP_array, "photoshop" ); + + // The Photoshop CaptionWriter tag contains captionwriter - Find it and Update the value + $Item = & find_XMP_item( $photoshop_block, "photoshop:CaptionWriter" ); + $Item[ 'value' ] = $new_ps_file_info_array[ 'captionwriter' ]; + + // The Photoshop Category tag contains category - Find it and Update the value + $Item = & find_XMP_item( $photoshop_block, "photoshop:Category" ); + $Item[ 'value' ] = $new_ps_file_info_array[ 'category' ]; + + // The Photoshop DateCreated tag contains date - Find it and Update the value + $Item = & find_XMP_item( $photoshop_block, "photoshop:DateCreated" ); + $Item[ 'value' ] = $new_ps_file_info_array[ 'date' ]; + + // The Photoshop City tag contains city - Find it and Update the value + $Item = & find_XMP_item( $photoshop_block, "photoshop:City" ); + $Item[ 'value' ] = $new_ps_file_info_array[ 'city' ]; + + // The Photoshop State tag contains state - Find it and Update the value + $Item = & find_XMP_item( $photoshop_block, "photoshop:State" ); + $Item[ 'value' ] = $new_ps_file_info_array[ 'state' ]; + + // The Photoshop Country tag contains country - Find it and Update the value + $Item = & find_XMP_item( $photoshop_block, "photoshop:Country" ); + $Item[ 'value' ] = $new_ps_file_info_array[ 'country' ]; + + // The Photoshop AuthorsPosition tag contains authorsposition - Find it and Update the value + $Item = & find_XMP_item( $photoshop_block, "photoshop:AuthorsPosition" ); + $Item[ 'value' ] = $new_ps_file_info_array[ 'authorsposition' ]; + + // The Photoshop Credit tag contains credit - Find it and Update the value + $Item = & find_XMP_item( $photoshop_block, "photoshop:Credit" ); + $Item[ 'value' ] = $new_ps_file_info_array[ 'credit' ]; + + // The Photoshop Source tag contains source - Find it and Update the value + $Item = & find_XMP_item( $photoshop_block, "photoshop:Source" ); + $Item[ 'value' ] = $new_ps_file_info_array[ 'source' ]; + + // The Photoshop Headline tag contains headline - Find it and Update the value + $Item = & find_XMP_item( $photoshop_block, "photoshop:Headline" ); + $Item[ 'value' ] = $new_ps_file_info_array[ 'headline' ]; + + // The Photoshop Instructions tag contains instructions - Find it and Update the value + $Item = & find_XMP_item( $photoshop_block, "photoshop:Instructions" ); + $Item[ 'value' ] = $new_ps_file_info_array[ 'instructions' ]; + + // The Photoshop TransmissionReference tag contains transmissionreference - Find it and Update the value + $Item = & find_XMP_item( $photoshop_block, "photoshop:TransmissionReference" ); + $Item[ 'value' ] = $new_ps_file_info_array[ 'transmissionreference' ]; + + // The Photoshop Urgency tag contains urgency - Find it and Update the value + $Item = & find_XMP_item( $photoshop_block, "photoshop:Urgency" ); + $Item[ 'value' ] = $new_ps_file_info_array[ 'urgency' ]; + + // The Photoshop SupplementalCategories tag contains supplementalcategories - Find it + $Item = & find_XMP_item( $photoshop_block, "photoshop:SupplementalCategories" ); + + // Create an array to receive the XML list items for the Supplemental Categories + $new_supcat_array = array( ); + + // Cycle through the Supplemental Categories + foreach ( $new_ps_file_info_array[ 'supplementalcategories' ] as $sup_category ) + { + // Add a new list item for this Supplemental Category + $new_supcat_array[] = array( 'tag' => 'rdf:li', 'value' => $sup_category ); + } + + // Add the array of Supplemental Category List Items to the Photoshop SupplementalCategories tag + $Item[ 'children' ][ 0 ][ 'children' ] = $new_supcat_array; + + + + // Process the XMP XAP block + + // Find the XAP Information within the XMP block + $XAP_block = & find_XMP_block( $new_XMP_array, "xap" ); + + // The XAP CreateDate tag contains date XMP was first created - Find it and Update the value + $Item = & find_XMP_item( $XAP_block, "xap:CreateDate" ); + + // Check if the CreateDate is blank + if ( $Item[ 'value' ] == "" ) + { + // CreateDate is blank - we must have just added it - set it to the current date + $Item[ 'value' ] = date( "Y-m-d\TH:i:s" ); + $Item[ 'value' ] .= get_Local_Timezone_Offset( ); + } + + + // The XAP ModifyDate tag contains last resource change date - Find it and Update the value to the current date + $Item = & find_XMP_item( $XAP_block, "xap:ModifyDate" ); + $Item[ 'value' ] = date( "Y-m-d\TH:i:s" ); + $Item[ 'value' ] .= get_Local_Timezone_Offset( ); + + // The XAP ModifyDate tag contains last XMP change date - Find it and Update the value to the current date + $Item = & find_XMP_item( $XAP_block, "xap:MetadataDate" ); + $Item[ 'value' ] = date( "Y-m-d\TH:i:s" ); + $Item[ 'value' ] .= get_Local_Timezone_Offset( ); + + + + // The XAP CreatorTool tag contains name of the software editor - Find it + $Item = & find_XMP_item( $XAP_block, "xap:CreatorTool" ); + + // Photoshop replaces the CreatorTool with "Adobe Photoshop ..." + // This toolkit instead preserves existing value and appends the toolkit name to the end of it + + // Check if a CreatorTool already exists + if ( $Item[ 'value' ] != "" ) + { + // An existing CreatorTool was found + // Check if the existing CreatorTool already contains the Toolkit's name + if ( stristr ( $Item[ 'value' ], $GLOBALS[ "Software Name" ]) == FALSE ) + { + // Toolkit Name string not found in the existing CreatorTool string - append it. + $Item[ 'value' ] = $Item[ 'value' ] . " " . $GLOBALS[ "Software Name" ]; + } + else + { + // Toolkit name already exists in CreatorTool string - leave as is + } + } + else + { + // No CreatorTool string exists - create one + $Item[ 'value' ] = $GLOBALS[ "Software Name" ]; + } + + + + + // Process the XMP Basic Job Information block + + // Find the XAP Basic Job Information within the XMP block + $XAPBJ_block = & find_XMP_block( $new_XMP_array, "xapBJ" ); + + // The XAP Basic Job JobRef tag contains urgency - Find it and Update the value + $Item = & find_XMP_item( $XAPBJ_block, "xapBJ:JobRef" ); + $Item[ 'children' ][ 0 ][ 'children' ] = + array( array ( 'tag' => 'rdf:li', + 'attributes' => array ( 'rdf:parseType' => 'Resource' ), + 'children' => array ( 0 => array ( 'tag' => 'stJob:name', + 'value' => $new_ps_file_info_array[ 'jobname' ] ), + ), + ), + ); + + + + + // Process the XMP XAP Rights Information block + + // Find the XAP Rights Information within the XMP block + $XAPRights_block = & find_XMP_block( $new_XMP_array, "xapRights" ); + + + + // The XAP Rights Marked tag should only be present if copyrightstatus is 'Copyrighted Work' or 'Public Domain' + // If copyrightstatus 'Unmarked' or anything else, the XAP Rights Marked tag should be missing + + + // Remove any existing XAP Rights Marked tags - they will be replaced + foreach( $XAPRights_block as $tagno => $tag ) + { + if ( $tag[ 'tag' ] == "xapRights:Marked" ) + { + array_splice( $XAPRights_block, $tagno, 1 ); + } + } + + // Check the value of the copyrightstatus flag + if ( $new_ps_file_info_array[ 'copyrightstatus' ] == "Copyrighted Work" ) + { + // Copyrighted - add the tag + $XAPRights_block[] = array ( 'tag' => 'xapRights:Marked', 'value' => 'True' ); + } + else if ( $new_ps_file_info_array[ 'copyrightstatus' ] == "Public Domain" ) + { + // Public domain - add the tag + $XAPRights_block[] = array ( 'tag' => 'xapRights:Marked', 'value' => 'False' ); + } + else + { + // Unmarked or Other - Do nothing - don't add a Marked tag + } + + + // The XAP Rights WebStatement tag contains ownerurl - Find it and Update the value + $Item = & find_XMP_item( $XAPRights_block, "xapRights:WebStatement" ); + $Item[ 'value' ] = $new_ps_file_info_array[ 'ownerurl' ]; + + + + + // Process the XMP Dublin Core block + + // Find the Dublin Core Information within the XMP block + $DC_block = & find_XMP_block( $new_XMP_array, "dc" ); + + + // The Dublin Core description tag contains caption - Find it and Update the value + $Item = & find_XMP_item( $DC_block, "dc:description" ); + $Item[ 'children' ][ 0 ][ 'children' ] = array( array( 'tag' => "rdf:li", + 'value' => $new_ps_file_info_array[ 'caption' ], + 'attributes' => array( 'xml:lang' => "x-default" ) ) ); + + + // The Dublin Core title tag contains title - Find it and Update the value + $Item = & find_XMP_item( $DC_block, "dc:title" ); + $Item[ 'children' ][ 0 ][ 'children' ] = array( array( 'tag' => "rdf:li", + 'value' => $new_ps_file_info_array[ 'title' ], + 'attributes' => array( 'xml:lang' => "x-default" ) ) ); + + + // The Dublin Core rights tag contains copyrightnotice - Find it and Update the value + $Item = & find_XMP_item( $DC_block, "dc:rights" ); + $Item[ 'children' ][ 0 ][ 'children' ] = array( array( 'tag' => "rdf:li", + 'value' => $new_ps_file_info_array[ 'copyrightnotice' ], + 'attributes' => array( 'xml:lang' => "x-default" ) ) ); + + // The Dublin Core creator tag contains author - Find it and Update the value + $Item = & find_XMP_item( $DC_block, "dc:creator" ); + $Item[ 'children' ][ 0 ][ 'children' ] = array( array( 'tag' => "rdf:li", + 'value' => $new_ps_file_info_array[ 'author' ]) ); + + // The Dublin Core subject tag contains keywords - Find it + $Item = & find_XMP_item( $DC_block, "dc:subject" ); + + // Create an array to receive the Keywords List Items + $new_keywords_array = array( ); + + // Cycle through each keyword + foreach( $new_ps_file_info_array[ 'keywords' ] as $keyword ) + { + // Add a List item for this keyword + $new_keywords_array[] = array( 'tag' => "rdf:li", 'value' => $keyword ); + } + // Add the Keywords List Items array to the Dublin Core subject tag + $Item[ 'children' ][ 0 ][ 'children' ] = $new_keywords_array; + + + + /***************************************/ + + // FINISHED UPDATING VALUES + + // Insert the new IPTC array into the Photoshop IRB array + $new_IRB_array = put_Photoshop_IPTC( $new_IRB_array, $new_IPTC_array ); + + // Write the EXIF array to the JPEG header + $jpeg_header_data = put_EXIF_JPEG( $new_Exif_array, $jpeg_header_data ); + + // Convert the XMP array to XMP text + $xmp_text = write_XMP_array_to_text( $new_XMP_array ); + + // Write the XMP text to the JPEG Header + $jpeg_header_data = put_XMP_text( $jpeg_header_data, $xmp_text ); + + // Write the Photoshop IRB array to the JPEG header + $jpeg_header_data = put_Photoshop_IRB( $jpeg_header_data, $new_IRB_array ); + + return $jpeg_header_data; + +} + +/****************************************************************************** +* End of Function: put_photoshop_file_info +******************************************************************************/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +/****************************************************************************** +* +* INTERNAL FUNCTIONS +* +******************************************************************************/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +/****************************************************************************** +* +* Function: get_Local_Timezone_Offset +* +* Description: Returns a string indicating the time difference between the local +* timezone and GMT in hours and minutes, e.g. +10:00 or -06:30 +* +* Parameters: None +* +* Returns: $tz_str - a string containing the timezone offset +* +******************************************************************************/ + +function get_Local_Timezone_Offset( ) +{ + // Retrieve the Timezone offset in seconds + $tz_seconds = date( "Z" ); + + // Check if the offset is less than zero + if ( $tz_seconds < 0 ) + { + // Offset is less than zero - add a Minus sign to the output + $tz_str = "-"; + } + else + { + // Offset is greater than or equal to zero - add a Plus sign to the output + $tz_str = "+"; + } + + // Add the absolute offset to the output, formatted as HH:MM + $tz_str .= gmdate( "H:i", abs($tz_seconds) ); + + // Return the result + return $tz_str; +} + +/****************************************************************************** +* End of Function: get_Local_Timezone_Offset +******************************************************************************/ + + + +/****************************************************************************** +* +* Function: XMP_Check +* +* Description: Checks a given XMP array against a reference array, and adds any +* missing blocks and tags +* +* NOTE: This is a recursive function +* +* Parameters: reference_array - The standard XMP array which contains all required tags +* check_array - The XMP array to check +* +* Returns: output - a string containing the timezone offset +* +******************************************************************************/ + +function XMP_Check( $reference_array, $check_array) +{ + // Cycle through each of the elements of the reference XMP array + foreach( $reference_array as $valkey => $val ) + { + + // Search for the current reference tag within the XMP array to be checked + $tagpos = find_XMP_Tag( $check_array, $val ); + + // Check if the tag was found + if ( $tagpos === FALSE ) + { + // Tag not found - Add tag to array being checked + $tagpos = count( $check_array ); + $check_array[ $tagpos ] = $val; + } + + // Check if the reference tag has children + if ( array_key_exists( 'children', $val ) ) + { + // Reference tag has children - these need to be checked too + + // Determine if the array being checked has children for this tag + if ( ! array_key_exists( 'children', $check_array[ $tagpos ] ) ) + { + // Array being checked has no children - add a blank children array + $check_array[ $tagpos ][ 'children' ] = array( ); + } + + // Recurse, checking the children tags against the reference children + $check_array[ $tagpos ][ 'children' ] = XMP_Check( $val[ 'children' ] , $check_array[ $tagpos ][ 'children' ] ); + } + else + { + // No children - don't need to check anything else + } + } + + // Return the checked XMP array + return $check_array; +} + + +/****************************************************************************** +* End of Function: XMP_Check +******************************************************************************/ + + + + +/****************************************************************************** +* +* Function: find_XMP_Tag +* +* Description: Searches one level of an XMP array for a specific tag, and +* returns the tag position. Does not descend the XMP tree. +* +* Parameters: XMP_array - The XMP array which should be searched +* tag - The XMP tag to search for (in same format as would be found in XMP array) +* +* Returns: output - a string containing the timezone offset +* +******************************************************************************/ + +function find_XMP_Tag( $XMP_array, $tag ) +{ + $namespacestr = ""; + + // Some tags have a namespace attribute which defines them (i.e. rdf:Description tags) + + // Check if the tag being searched for has attributs + if ( array_key_exists( 'attributes', $tag ) ) + { + // Tag has attributes - cycle through them + foreach( $tag['attributes'] as $key => $val ) + { + // Check if the current attribute is the namespace attribute - i.e. starts with xmlns: + if ( strcasecmp( substr($key,0,6), "xmlns:" ) == 0 ) + { + // Found a namespace attribute - save it for later. + $namespacestr = $key; + } + } + } + + + + // Cycle through the elements of the XMP array to be searched. + foreach( $XMP_array as $valkey => $val ) + { + + // Check if the current element is a rdf:Description tag + if ( strcasecmp ( $tag[ 'tag' ], 'rdf:Description' ) == 0 ) + { + // Current element is a rdf:Description tag + // Check if the namespace attribute is the same as in the tag that is being searched for + if ( array_key_exists( $namespacestr, $val['attributes'] ) ) + { + // Namespace is the same - this is the correct tag - return it's position + return $valkey; + } + } + // Otherwise check if the current element has the same name as the tag in question + else if ( strcasecmp ( $val[ 'tag' ], $tag[ 'tag' ] ) == 0 ) + { + // Tags have same name - this is the correct tag - return it's position + return $valkey; + } + } + + // Cycled through all tags without finding the correct one - return error value + return FALSE; +} + +/****************************************************************************** +* End of Function: find_XMP_Tag +******************************************************************************/ + + + + +/****************************************************************************** +* +* Function: create_GUID +* +* Description: Creates a Globally Unique IDentifier, in the format that is used +* by XMP (and Windows). This value is not guaranteed to be 100% unique, +* but it is ridiculously unlikely that two identical values will be produced +* +* Parameters: none +* +* Returns: output - a string containing the timezone offset +* +******************************************************************************/ + +function create_GUID( ) +{ + // Create a md5 sum of a random number - this is a 32 character hex string + $raw_GUID = md5( uniqid( getmypid() . rand( ) . (double)microtime()*1000000, TRUE ) ); + + // Format the string into 8-4-4-4-12 (numbers are the number of characters in each block) + return substr($raw_GUID,0,8) . "-" . substr($raw_GUID,8,4) . "-" . substr($raw_GUID,12,4) . "-" . substr($raw_GUID,16,4) . "-" . substr($raw_GUID,20,12); +} + +/****************************************************************************** +* End of Function: create_GUID +******************************************************************************/ + + + + + +/****************************************************************************** +* +* Function: add_to_field +* +* Description: Adds a value to a particular field in a Photoshop File Info array, +* first checking whether the value is already there. If the value is +* already in the array, it is not changed, otherwise the value is appended +* to whatever is already in that field of the array +* +* Parameters: field_array - The Photoshop File Info array to receive the new value +* field - The File Info field which the value is for +* value - The value to be written into the File Info +* separator - The string to place between values when having to append the value +* +* Returns: output - the Photoshop File Info array with the value added +* +******************************************************************************/ + +function add_to_field( $field_array, $field, $value, $separator ) +{ + // Check if the value is blank + if ( $value == "" ) + { + // Value is blank - return File Info array unchanged + return $field_array; + } + + // Check if the value can be found anywhere within the existing value for this field + if ( stristr ( $field_array[ $field ], $value ) == FALSE) + { + // Value could not be found + // Check if the existing value for the field is blank + if ( $field_array[$field] != "" ) + { + // Existing value for field is not blank - append a separator + $field_array[$field] .= $separator; + } + // Append the value to the field + $field_array[$field] .= $value; + } + + // Return the File Info Array + return $field_array; +} + +/****************************************************************************** +* End of Function: add_to_field +******************************************************************************/ + + + +/****************************************************************************** +* +* Function: find_IPTC_Resource +* +* Description: Searches an IPTC array for a particular record, and returns it if found +* +* Parameters: IPTC_array - The IPTC array to search +* record_type - The IPTC record number to search for (e.g. 2:151 ) +* +* Returns: output - the contents of the record if found +* FALSE - otherwise +* +******************************************************************************/ + +function find_IPTC_Resource( $IPTC_array, $record_type ) +{ + // Cycle through the ITPC records + foreach ($IPTC_array as $record) + { + // Check the IPTC type against the required type + if ( $record['IPTC_Type'] == $record_type ) + { + // IPTC type matches - return this record + return $record; + } + } + + // No matching record found - return error code + return FALSE; +} + +/****************************************************************************** +* End of Function: find_IPTC_Resource +******************************************************************************/ + + + + +/****************************************************************************** +* +* Function: find_Photoshop_IRB_Resource +* +* Description: Searches a Photoshop IRB array for a particular resource, and returns it if found +* +* Parameters: IRB_array - The IRB array to search +* resource_ID - The IRB resource number to search for (e.g. 0x03F9 ) +* +* Returns: output - the contents of the resource if found +* FALSE - otherwise +* +******************************************************************************/ + +function find_Photoshop_IRB_Resource( $IRB_array, $resource_ID ) +{ + // Cycle through the IRB resources + foreach( $IRB_array as $IRB_Resource ) + { + // Check the IRB resource ID against the required ID + if ( $resource_ID == $IRB_Resource['ResID'] ) + { + // Resource ID matches - return this resource + return $IRB_Resource; + } + } + + // No matching resource found - return error code + return FALSE; +} + +/****************************************************************************** +* End of Function: find_Photoshop_IRB_Resource +******************************************************************************/ + + + + + + + + +/****************************************************************************** +* +* Function: find_XMP_item +* +* Description: Searches a one level of a XMP array for a particular item by name, and returns it if found. +* Does not descend through the XMP array +* +* Parameters: Item_Array - The XMP array to search +* item_name - The name of the tag to serch for (e.g. photoshop:CaptionWriter ) +* +* Returns: output - the contents of the tag if found +* FALSE - otherwise +* +******************************************************************************/ + +function find_XMP_item( & $Item_Array, $item_name ) +{ + // Cycle through the top level of the XMP array + foreach( $Item_Array as $Item_Key => $Item ) + { + // Check this tag name against the required tag name + if( $Item['tag'] == $item_name ) + { + // The tag names match - return the item + return $Item_Array[ $Item_Key ]; + } + } + + // No matching tag found - return error code + return FALSE; +} + +/****************************************************************************** +* End of Function: find_XMP_item +******************************************************************************/ + + + + + +/****************************************************************************** +* +* Function: find_XMP_block +* +* Description: Searches a for a particular rdf:Description block within a XMP array, and returns its children if found. +* +* Parameters: XMP_array - The XMP array to search as returned by read_XMP_array_from_text +* block_name - The namespace of the XMP block to be found (e.g. photoshop or xapRights ) +* +* Returns: output - the children of the tag if found +* FALSE - otherwise +* +******************************************************************************/ + +function find_XMP_block( & $XMP_array, $block_name ) +{ + // Check that the rdf:RDF section can be found (which contains the rdf:Description tags + if ( ( $XMP_array !== FALSE ) && + ( ( $XMP_array[0]['tag'] == "x:xapmeta" ) || + ( $XMP_array[0]['tag'] == "x:xmpmeta" ) ) && + ( $XMP_array[0]['children'][0]['tag'] == "rdf:RDF" ) ) + { + // Found rdf:RDF + // Make it's children easily accessible + $RDF_Contents = $XMP_array[0]['children'][0]['children']; + + // Cycle through the children (rdf:Description tags) + foreach ($RDF_Contents as $RDF_Key => $RDF_Item) + { + // Check if this is a rdf:description tag that has children + if ( ( $RDF_Item['tag'] == "rdf:Description" ) && + ( array_key_exists( 'children', $RDF_Item ) ) ) + { + // RDF Description tag has children, + // Cycle through it's attributes + foreach( $RDF_Item['attributes'] as $key => $val ) + { + // Check if this attribute matches the namespace block name required + if ( $key == "xmlns:$block_name" ) + { + // Namespace matches required block name - return it's children + return $XMP_array[0]['children'][0]['children'][ $RDF_Key ]['children']; + } + } + } + } + } + + // No matching rdf:Description block found + return FALSE; +} + +/****************************************************************************** +* End of Function: find_XMP_block +******************************************************************************/ + + + + + + + + + +/****************************************************************************** +* Global Variable: Blank XMP Structure +* +* Contents: A template XMP array which can be used to create a new XMP segment +* +******************************************************************************/ + +// Create a GUID to be used in this template array +$new_GUID = create_GUID( ); + +$GLOBALS[ 'Blank XMP Structure' ] = +array ( + 0 => + array ( + 'tag' => 'x:xmpmeta', + 'attributes' => + array ( + 'xmlns:x' => 'adobe:ns:meta/', + 'x:xmptk' => 'XMP toolkit 3.0-28, framework 1.6', + ), + 'children' => + array ( + 0 => + array ( + 'tag' => 'rdf:RDF', + 'attributes' => + array ( + 'xmlns:rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', + 'xmlns:iX' => 'http://ns.adobe.com/iX/1.0/', + ), + 'children' => + array ( + 1 => + array ( + 'tag' => 'rdf:Description', + 'attributes' => + array ( + 'rdf:about' => "uuid:$new_GUID", + 'xmlns:pdf' => 'http://ns.adobe.com/pdf/1.3/', + ), + ), + 2 => + array ( + 'tag' => 'rdf:Description', + 'attributes' => + array ( + 'rdf:about' => "uuid:$new_GUID", + 'xmlns:photoshop' => 'http://ns.adobe.com/photoshop/1.0/', + ), + 'children' => + array ( + 0 => + array ( + 'tag' => 'photoshop:CaptionWriter', + 'value' => '', + ), + 1 => + array ( + 'tag' => 'photoshop:Category', + 'value' => '', + ), + 2 => + array ( + 'tag' => 'photoshop:DateCreated', + 'value' => '', + ), + 3 => + array ( + 'tag' => 'photoshop:City', + 'value' => '', + ), + 4 => + array ( + 'tag' => 'photoshop:State', + 'value' => '', + ), + 5 => + array ( + 'tag' => 'photoshop:Country', + 'value' => '', + ), + 6 => + array ( + 'tag' => 'photoshop:Credit', + 'value' => '', + ), + 7 => + array ( + 'tag' => 'photoshop:Source', + 'value' => '', + ), + 8 => + array ( + 'tag' => 'photoshop:Headline', + 'value' => '', + ), + 9 => + array ( + 'tag' => 'photoshop:Instructions', + 'value' => '', + ), + 10 => + array ( + 'tag' => 'photoshop:TransmissionReference', + 'value' => '', + ), + 11 => + array ( + 'tag' => 'photoshop:Urgency', + 'value' => '', + ), + 12 => + array ( + 'tag' => 'photoshop:SupplementalCategories', + 'children' => + array ( + 0 => + array ( + 'tag' => 'rdf:Bag', + ), + ), + ), + 13 => + array ( + 'tag' => 'photoshop:AuthorsPosition', + 'value' => '', + ), + ), + ), + 4 => + array ( + 'tag' => 'rdf:Description', + 'attributes' => + array ( + 'rdf:about' => "uuid:$new_GUID", + 'xmlns:xap' => 'http://ns.adobe.com/xap/1.0/', + ), + 'children' => + array ( + 0 => + array ( + 'tag' => 'xap:CreateDate', + 'value' => '', + ), + 1 => + array ( + 'tag' => 'xap:ModifyDate', + 'value' => '', + ), + 2 => + array ( + 'tag' => 'xap:MetadataDate', + 'value' => '', + ), + 3 => + array ( + 'tag' => 'xap:CreatorTool', + 'value' => '', + ), + ), + ), + 5 => + array ( + 'tag' => 'rdf:Description', + 'attributes' => + array ( + 'about' => "uuid:$new_GUID", + 'xmlns:stJob' => 'http://ns.adobe.com/xap/1.0/sType/Job#', + 'xmlns:xapBJ' => 'http://ns.adobe.com/xap/1.0/bj/', + ), + 'children' => + array ( + 0 => + array ( + 'tag' => 'xapBJ:JobRef', + 'children' => + array ( + 0 => + array ( + 'tag' => 'rdf:Bag', + 'children' => + array ( + ), + ), + ), + ), + ), + ), + 6 => + array ( + 'tag' => 'rdf:Description', + 'attributes' => + array ( + 'rdf:about' => "uuid:$new_GUID", + 'xmlns:xapRights' => 'http://ns.adobe.com/xap/1.0/rights/', + ), + 'children' => + array ( + 1 => + array ( + 'tag' => 'xapRights:WebStatement', + 'value' => '', + ), + ), + ), + 7 => + array ( + 'tag' => 'rdf:Description', + 'attributes' => + array ( + 'rdf:about' => "uuid:$new_GUID", + 'xmlns:dc' => 'http://purl.org/dc/elements/1.1/', + ), + 'children' => + array ( + 0 => + array ( + 'tag' => 'dc:format', + 'value' => 'image/jpeg', + ), + 1 => + array ( + 'tag' => 'dc:title', + 'children' => + array ( + 0 => + array ( + 'tag' => 'rdf:Alt', + ), + ), + ), + 2 => + array ( + 'tag' => 'dc:description', + 'children' => + array ( + 0 => + array ( + 'tag' => 'rdf:Alt', + ), + ), + ), + 3 => + array ( + 'tag' => 'dc:rights', + 'children' => + array ( + 0 => + array ( + 'tag' => 'rdf:Alt', + ), + ), + ), + 4 => + array ( + 'tag' => 'dc:creator', + 'children' => + array ( + 0 => + array ( + 'tag' => 'rdf:Seq', + ), + ), + ), + 5 => + array ( + 'tag' => 'dc:subject', + 'children' => + array ( + 0 => + array ( + 'tag' => 'rdf:Bag', + ), + ), + ), + ), + ), + +/* 0 => + array ( + 'tag' => 'rdf:Description', + 'attributes' => + array ( + 'rdf:about' => "uuid:$new_GUID", + 'xmlns:exif' => 'http://ns.adobe.com/exif/1.0/', + ), + 'children' => + array ( + +//EXIF DATA GOES HERE - Not Implemented yet + ), + ), +*/ +/* + 2 => + array ( + 'tag' => 'rdf:Description', + 'attributes' => + array ( + 'rdf:about' => "uuid:$new_GUID", + 'xmlns:tiff' => 'http://ns.adobe.com/tiff/1.0/', + ), + 'children' => + array ( +// TIFF DATA GOES HERE - Not Implemented yet + 0 => + array ( + 'tag' => 'tiff:Make', + 'value' => 'NIKON CORPORATION', + ), + ), + ), +*/ +/* + 3 => + array ( + 'tag' => 'rdf:Description', + 'attributes' => + array ( + 'rdf:about' => "uuid:$new_GUID", + 'xmlns:stRef' => 'http://ns.adobe.com/xap/1.0/sType/ResourceRef#', + 'xmlns:xapMM' => 'http://ns.adobe.com/xap/1.0/mm/', + ), + 'children' => + array ( +// XAPMM DATA GOES HERE - Not Implemented yet + 0 => + array ( + 'tag' => 'xapMM:DocumentID', + 'value' => 'adobe:docid:photoshop:dceba4c2-e699-11d8-94b2-b6ec48319f2d', + ), + 1 => + array ( + 'tag' => 'xapMM:DerivedFrom', + 'attributes' => + array ( + 'rdf:parseType' => 'Resource', + ), + 'children' => + array ( + 0 => + array ( + 'tag' => 'stRef:documentID', + 'value' => 'adobe:docid:photoshop:5144475b-e698-11d8-94b2-b6ec48319f2d', + ), + 1 => + array ( + 'tag' => 'stRef:instanceID', + 'value' => "uuid:$new_GUID", + ), + ), + ), + ), + ), +*/ + + ), + ), + ), + ), +); + + + +/****************************************************************************** +* End of Global Variable: Blank XMP Structure +******************************************************************************/ + + + + + +?> diff --git a/includes/jpeg_metadata_tk/Photoshop_IRB.php b/includes/jpeg_metadata_tk/Photoshop_IRB.php new file mode 100644 index 0000000..fa09b49 --- /dev/null +++ b/includes/jpeg_metadata_tk/Photoshop_IRB.php @@ -0,0 +1,1514 @@ + 1.02 : changed get_Photoshop_IRB to work with corrupted +* resource names which Photoshop can still read +* 1.02 -> 1.03 : Fixed put_Photoshop_IRB to output "Photoshop 3.0\x00" +* string with every APP13 segment, not just the first one +* 1.03 -> 1.10 : changed get_Photoshop_IRB to fix processing of embedded resource names, +* after discovering that Photoshop does not process +* resource names according to the standard : +* "Adobe Photoshop 6.0 File Formats Specification, Version 6.0, Release 2, November 2000" +* This is an update to the change 1.00 -> 1.02, which was not fully correct +* changed put_Photoshop_IRB to fix the writing of embedded resource name, +* to avoid creating blank resources, and to fix a problem +* causing the IRB block to be incorrectly positioned if no APP segments existed. +* changed get_Photoshop_IPTC to initialise the output array correctly. +* 1.10 -> 1.11 : Moved code out of get_Photoshop_IRB into new function unpack_Photoshop_IRB_Data +* to allow reading of IRB blocks embedded within EXIF (for TIFF Files) +* Moved code out of put_Photoshop_IRB into new function pack_Photoshop_IRB_Data +* to allow writing of IRB blocks embedded within EXIF (for TIFF Files) +* Enabled the usage of $GLOBALS['HIDE_UNKNOWN_TAGS'] to hide unknown resources +* changed Interpret_IRB_to_HTML to allow thumbnail links to work when +* toolkit is portable across directories +* +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* +* License: This file is part of the PHP JPEG Metadata Toolkit. +* +* The PHP JPEG Metadata Toolkit is free software; you can +* redistribute it and/or modify it under the terms of the +* GNU General Public License as published by the Free Software +* Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* The PHP JPEG Metadata Toolkit is distributed in the hope +* that it will be useful, but WITHOUT ANY WARRANTY; without +* even the implied warranty of MERCHANTABILITY or FITNESS +* FOR A PARTICULAR PURPOSE. See the GNU General Public License +* for more details. +* +* You should have received a copy of the GNU General Public +* License along with the PHP JPEG Metadata Toolkit; if not, +* write to the Free Software Foundation, Inc., 59 Temple +* Place, Suite 330, Boston, MA 02111-1307 USA +* +* If you require a different license for commercial or other +* purposes, please contact the author: evan@ozhiker.com +* +******************************************************************************/ + +// Change: as of version 1.11 - added to ensure the HIDE_UNKNOWN_TAGS variable is set even if EXIF.php is not included +if ( !isset( $GLOBALS['HIDE_UNKNOWN_TAGS'] ) ) $GLOBALS['HIDE_UNKNOWN_TAGS']= FALSE; + +include_once 'IPTC.php'; +include_once 'Unicode.php'; + + + +// TODO: Many Photoshop IRB resources not interpeted +// TODO: Obtain a copy of the Photoshop CS File Format Specification +// TODO: Find out what Photoshop IRB resources 1061, 1062 & 1064 are +// TODO: Test get_Photoshop_IRB and put_Photoshop_IRB with multiple APP13 segments + +/****************************************************************************** +* +* Function: get_Photoshop_IRB +* +* Description: Retrieves the Photoshop Information Resource Block (IRB) information +* from an App13 JPEG segment and returns it as an array. This may +* include IPTC-NAA IIM Information. Uses information +* supplied by the get_jpeg_header_data function +* +* Parameters: jpeg_header_data - a JPEG header data array in the same format +* as from get_jpeg_header_data +* +* Returns: IRBdata - The array of Photoshop IRB records +* FALSE - if an APP 13 Photoshop IRB segment could not be found, +* or if an error occured +* +******************************************************************************/ + +function get_Photoshop_IRB( $jpeg_header_data ) +{ + // Photoshop Image Resource blocks can span several JPEG APP13 segments, so we need to join them up if there are more than one + $joined_IRB = ""; + + + //Cycle through the header segments + for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) + { + // If we find an APP13 header, + if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP13" ) == 0 ) + { + // And if it has the photoshop label, + if( strncmp ( $jpeg_header_data[$i]['SegData'], "Photoshop 3.0\x00", 14) == 0 ) + { + // join it to the other previous IRB data + $joined_IRB .= substr ( $jpeg_header_data[$i]['SegData'], 14 ); + } + } + } + + // If there was some Photoshop IRB information found, + if ( $joined_IRB != "" ) + { + // Found a Photoshop Image Resource Block - extract it. + // Change: Moved code into unpack_Photoshop_IRB_Data to allow TIFF reading as of 1.11 + return unpack_Photoshop_IRB_Data( $joined_IRB ); + + } + else + { + // No Photoshop IRB found + return FALSE; + } + +} + +/****************************************************************************** +* End of Function: get_Photoshop_IRB +******************************************************************************/ + + + + + + + + + + +/****************************************************************************** +* +* Function: put_Photoshop_IRB +* +* Description: Adds or modifies the Photoshop Information Resource Block (IRB) +* information from an App13 JPEG segment. If a Photoshop IRB already +* exists, it is replaced, otherwise a new one is inserted, using the +* supplied data. Uses information supplied by the get_jpeg_header_data +* function +* +* Parameters: jpeg_header_data - a JPEG header data array in the same format +* as from get_jpeg_header_data +* new_IRB_data - an array of the data to be stored in the Photoshop +* IRB segment. Should be in the same format as received +* from get_Photoshop_IRB +* +* Returns: jpeg_header_data - the JPEG header data array with the +* Photoshop IRB added. +* FALSE - if an error occured +* +******************************************************************************/ + +function put_Photoshop_IRB( $jpeg_header_data, $new_IRB_data ) +{ + // Delete all existing Photoshop IRB blocks - the new one will replace them + + //Cycle through the header segments + for( $i = 0; $i < count( $jpeg_header_data ) ; $i++ ) + { + // If we find an APP13 header, + if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP13" ) == 0 ) + { + // And if it has the photoshop label, + if( strncmp ( $jpeg_header_data[$i]['SegData'], "Photoshop 3.0\x00", 14) == 0 ) + { + // Delete the block information - it needs to be rebuilt + array_splice( $jpeg_header_data, $i, 1 ); + } + } + } + + + // Now we have deleted the pre-existing blocks + + + // Retrieve the Packed Photoshop IRB Data + // Change: Moved code into pack_Photoshop_IRB_Data to allow TIFF writing as of 1.11 + $packed_IRB_data = pack_Photoshop_IRB_Data( $new_IRB_data ); + + // Change : This section changed to fix incorrect positioning of IRB segment, as of revision 1.10 + // when there are no APP segments present + + //Cycle through the header segments in reverse order (to find where to put the APP13 block - after any APP0 to APP12 blocks) + $i = count( $jpeg_header_data ) - 1; + while (( $i >= 0 ) && ( ( $jpeg_header_data[$i]['SegType'] > 0xED ) || ( $jpeg_header_data[$i]['SegType'] < 0xE0 ) ) ) + { + $i--; + } + + + + // Cycle through the packed output data until it's size is less than 32000 bytes, outputting each 32000 byte block to an APP13 segment + while ( strlen( $packed_IRB_data ) > 32000 ) + { + // Change: Fixed put_Photoshop_IRB to output "Photoshop 3.0\x00" string with every APP13 segment, not just the first one, as of 1.03 + + // Write a 32000 byte APP13 segment + array_splice($jpeg_header_data, $i +1 , 0, array( "SegType" => 0xED, + "SegName" => "APP13", + "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xED ], + "SegData" => "Photoshop 3.0\x00" . substr( $packed_IRB_data,0,32000) ) ); + + // Delete the 32000 bytes from the packed output data, that were just output + $packed_IRB_data = substr_replace($packed_IRB_data, '', 0, 32000); + $i++; + } + + // Write the last block of packed output data to an APP13 segment - Note array_splice doesn't work with multidimensional arrays, hence inserting a blank string + array_splice($jpeg_header_data, $i + 1 , 0, "" ); + $jpeg_header_data[$i + 1] = array( "SegType" => 0xED, + "SegName" => "APP13", + "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xED ], + "SegData" => "Photoshop 3.0\x00" . $packed_IRB_data ); + + return $jpeg_header_data; +} + +/****************************************************************************** +* End of Function: put_Photoshop_IRB +******************************************************************************/ + + + + + + + + +/****************************************************************************** +* +* Function: get_Photoshop_IPTC +* +* Description: Retrieves IPTC-NAA IIM information from within a Photoshop +* IRB (if it is present) and returns it in an array. Uses +* information supplied by the get_jpeg_header_data function +* +* Parameters: Photoshop_IRB_data - an array of Photoshop IRB records, as +* returned from get_Photoshop_IRB +* +* Returns: IPTC_Data_Out - The array of IPTC-NAA IIM records +* FALSE - if an IPTC-NAA IIM record could not be found, or if +* an error occured +* +******************************************************************************/ + +function get_Photoshop_IPTC( $Photoshop_IRB_data ) +{ + + // Change: Initialise array correctly, as of revision 1.10 + $IPTC_Data_Out = array(); + + //Cycle through the Photoshop 8BIM records looking for the IPTC-NAA record + for( $i = 0; $i < count( $Photoshop_IRB_data ); $i++ ) + { + // Check if each record is a IPTC record (which has id 0x0404) + if ( $Photoshop_IRB_data[$i]['ResID'] == 0x0404 ) + { + // We've found an IPTC block - Decode it + $IPTC_Data_Out = get_IPTC( $Photoshop_IRB_data[$i]['ResData'] ); + } + } + + // If there was no records put into the output array, + if ( count( $IPTC_Data_Out ) == 0 ) + { + // Then return false + return FALSE; + } + else + { + // Otherwise return the array + return $IPTC_Data_Out; + } + +} +/****************************************************************************** +* End of Function: get_Photoshop_IPTC +******************************************************************************/ + + + + + + +/****************************************************************************** +* +* Function: put_Photoshop_IPTC +* +* Description: Inserts a new IPTC-NAA IIM resource into a Photoshop +* IRB, or replaces an the existing resource if one is present. +* Uses information supplied by the get_Photoshop_IRB function +* +* Parameters: Photoshop_IRB_data - an array of Photoshop IRB records, as +* returned from get_Photoshop_IRB, into +* which the IPTC-NAA IIM record will be inserted +* new_IPTC_block - an array of IPTC-NAA records in the same format +* as those returned by get_Photoshop_IPTC +* +* Returns: Photoshop_IRB_data - The Photoshop IRB array with the +* IPTC-NAA IIM resource inserted +* +******************************************************************************/ + +function put_Photoshop_IPTC( $Photoshop_IRB_data, $new_IPTC_block ) +{ + $iptc_block_pos = -1; + + //Cycle through the 8BIM records looking for the IPTC-NAA record + for( $i = 0; $i < count( $Photoshop_IRB_data ); $i++ ) + { + // Check if each record is a IPTC record (which has id 0x0404) + if ( $Photoshop_IRB_data[$i]['ResID'] == 0x0404 ) + { + // We've found an IPTC block - save the position + $iptc_block_pos = $i; + } + } + + // If no IPTC block was found, create a new one + if ( $iptc_block_pos == -1 ) + { + // New block position will be at the end of the array + $iptc_block_pos = count( $Photoshop_IRB_data ); + } + + + // Write the new IRB resource to the Photoshop IRB array with no data + $Photoshop_IRB_data[$iptc_block_pos] = array( "ResID" => 0x0404, + "ResName" => $GLOBALS['Photoshop_ID_Names'][ 0x0404 ], + "ResDesc" => $GLOBALS[ "Photoshop_ID_Descriptions" ][ 0x0404 ], + "ResEmbeddedName" => "\x00\x00", + "ResData" => put_IPTC( $new_IPTC_block ) ); + + + // Return the modified IRB + return $Photoshop_IRB_data; +} + +/****************************************************************************** +* End of Function: put_Photoshop_IPTC +******************************************************************************/ + + + + + + + + +/****************************************************************************** +* +* Function: Interpret_IRB_to_HTML +* +* Description: Generates html showing the information contained in a Photoshop +* IRB data array, as retrieved with get_Photoshop_IRB, including +* any IPTC-NAA IIM records found. +* +* Please note that the following resource numbers are not currently +* decoded: ( Many of these do not apply to JPEG images) +* 0x03E9, 0x03EE, 0x03EF, 0x03F0, 0x03F1, 0x03F2, 0x03F6, 0x03F9, +* 0x03FA, 0x03FB, 0x03FD, 0x03FE, 0x0400, 0x0401, 0x0402, 0x0405, +* 0x040E, 0x040F, 0x0410, 0x0412, 0x0413, 0x0415, 0x0416, 0x0417, +* 0x041B, 0x041C, 0x041D, 0x0BB7 +* +* ( Also these Obsolete resource numbers) +* 0x03E8, 0x03EB, 0x03FC, 0x03FF, 0x0403 +* +* +* Parameters: IRB_array - a Photoshop IRB data array as from get_Photoshop_IRB +* filename - the name of the JPEG file being processed ( used +* by the script which displays the Photoshop thumbnail) +* +* +* Returns: output_str - the HTML string +* +******************************************************************************/ + +function Interpret_IRB_to_HTML( $IRB_array, $filename ) +{ + // Create a string to receive the HTML + $output_str = ""; + + // Check if the Photoshop IRB array is valid + if ( $IRB_array !== FALSE ) + { + + // Create another string to receive secondary HTML to be appended at the end + $secondary_output_str = ""; + + // Add the Heading to the HTML + $output_str .= "

Contains Photoshop Information Resource Block (IRB)

"; + + // Add Table to the HTML + $output_str .= "\n"; + + // Cycle through each of the Photoshop IRB records, creating HTML for each + foreach( $IRB_array as $IRB_Resource ) + { + // Check if the entry is a known Photoshop IRB resource + + // Get the Name of the Resource + if ( array_key_exists( $IRB_Resource['ResID'], $GLOBALS[ "Photoshop_ID_Names" ] ) ) + { + $Resource_Name = $GLOBALS['Photoshop_ID_Names'][ $IRB_Resource['ResID'] ]; + } + else + { + // Change: Added check for $GLOBALS['HIDE_UNKNOWN_TAGS'] to allow hiding of unknown resources as of 1.11 + if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == TRUE ) + { + continue; + } + else + { + // Unknown Resource - Make appropriate name + $Resource_Name = "Unknown Resource (". $IRB_Resource['ResID'] .")"; + } + } + + // Add HTML for the resource as appropriate + switch ( $IRB_Resource['ResID'] ) + { + + case 0x0404 : // IPTC-NAA IIM Record + $secondary_output_str .= Interpret_IPTC_to_HTML( get_IPTC( $IRB_Resource['ResData'] ) ); + break; + + case 0x040B : // URL + $output_str .= "\n"; + break; + + case 0x040A : // Copyright Marked + if ( hexdec( bin2hex( $IRB_Resource['ResData'] ) ) == 1 ) + { + $output_str .= "\n"; + } + else + { + $output_str .= "\n"; + } + break; + + case 0x040D : // Global Lighting Angle + $output_str .= "\n"; + break; + + case 0x0419 : // Global Altitude + $output_str .= "\n"; + break; + + case 0x0421 : // Version Info + $output_str .= "\n"; + break; + + case 0x0411 : // ICC Untagged + if ( $IRB_Resource['ResData'] == "\x01" ) + { + $output_str .= "\n"; + } + else + { + $output_str .= "\n"; + } + break; + + case 0x041A : // Slices + $output_str .= "\n"; + + break; + + + case 0x0408 : // Grid and Guides information + $output_str .= "\n"; + + case 0x0406 : // JPEG Quality + $Qual_Info = unpack("nQuality/nFormat/nScans/Cconst", $IRB_Resource['ResData'] ); + $output_str .= "\n"; + break; + + case 0x0409 : // Thumbnail Resource + case 0x040C : // Thumbnail Resource + $thumb_data = unpack("NFormat/NWidth/NHeight/NWidthBytes/NSize/NCompressedSize/nBitsPixel/nPlanes", $IRB_Resource['ResData'] ); + $output_str .= "\n"; + break; + + case 0x0414 : // Document Specific ID's + $output_str .= "\n"; + break; + + case 0x041E : // URL List + $URL_count = hexdec( bin2hex( substr( $IRB_Resource['ResData'], 0, 4 ) ) ); + $output_str .= "\n"; + break; + case 0x03F4 : // Grayscale and multichannel halftoning information. + $output_str .= "\n"; + break; + case 0x03F5 : // Color halftoning information + $output_str .= "\n"; + break; + + case 0x03F7 : // Grayscale and multichannel transfer function. + $output_str .= "\n"; + break; + + case 0x03F8 : // Color transfer functions + $output_str .= "\n"; + break; + + case 0x03F3 : // Print Flags + $output_str .= "\n"; + break; + + case 0x2710 : // Print Flags Information + $PrintFlags = unpack( "nVersion/CCentCrop/Cjunk/NBleedWidth/nBleedWidthScale", $IRB_Resource['ResData'] ); + $output_str .= "\n"; + break; + + case 0x03ED : // Resolution Info + $ResInfo = unpack( "nhRes_int/nhResdec/nhResUnit/nwidthUnit/nvRes_int/nvResdec/nvResUnit/nheightUnit", $IRB_Resource['ResData'] ); + $output_str .= "\n"; + break; + + default : // All other records + $output_str .= "\n"; + + } + + } + + // Add the table end to the HTML + $output_str .= "
$Resource_Name" . htmlentities( $IRB_Resource['ResData'] ) ."
$Resource_Name
Image is Copyrighted Material
$Resource_Name
Image is Not Copyrighted Material
$Resource_Name
Global lighting angle for effects layer = " . hexdec( bin2hex( $IRB_Resource['ResData'] ) ) . " degrees
$Resource_Name
Global Altitude = " . hexdec( bin2hex( $IRB_Resource['ResData'] ) ) . "
$Resource_Name
\n";
+                                        $output_str .= "Version = " . hexdec( bin2hex( substr( $IRB_Resource['ResData'], 0, 4 ) ) ) . "\n";
+                                        $output_str .= "Has Real Merged Data = " . ord( $IRB_Resource['ResData']{4} ) . "\n";
+                                        $writer_size = hexdec( bin2hex( substr( $IRB_Resource['ResData'], 5, 4 ) ) ) * 2;
+
+                                        $output_str .= "Writer Name = " . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], 9, $writer_size ), TRUE ) . "\n";
+                                        $reader_size = hexdec( bin2hex( substr( $IRB_Resource['ResData'], 9 + $writer_size , 4 ) ) ) * 2;
+                                        $output_str .= "Reader Name = " . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], 13 + $writer_size, $reader_size ), TRUE ) . "\n";
+                                        $output_str .= "File Version = " . hexdec( bin2hex( substr( $IRB_Resource['ResData'], 13 + $writer_size + $reader_size, 4 ) ) ) . "\n";
+                                        $output_str .=  "
$Resource_Name
Intentionally untagged - any assumed ICC profile handling disabled
$Resource_Name
Unknown value (0x" .bin2hex( $IRB_Resource['ResData'] ). ")
$Resource_Name"; + + // Unpack the first 24 bytes + $Slices_Info = unpack("NVersion/NBound_top/NBound_left/NBound_bottom/NBound_right/NStringlen", $IRB_Resource['ResData'] ); + $output_str .= "Version = " . $Slices_Info['Version'] . "
\n"; + $output_str .= "Bounding Rectangle = Top:" . $Slices_Info['Bound_top'] . ", Left:" . $Slices_Info['Bound_left'] . ", Bottom:" . $Slices_Info['Bound_bottom'] . ", Right:" . $Slices_Info['Bound_right'] . " (Pixels)
\n"; + $Slicepos = 24; + + // Extract a Unicode String + $output_str .= "Text = '" . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], 24, $Slices_Info['Stringlen']*2), TRUE ) . "'
\n"; + $Slicepos += $Slices_Info['Stringlen'] * 2; + + // Unpack the number of Slices + $Num_Slices = hexdec( bin2hex( substr( $IRB_Resource['ResData'], $Slicepos, 4 ) ) ); + $output_str .= "Number of Slices = " . $Num_Slices . "\n"; + $Slicepos += 4; + + // Cycle through the slices + for( $i = 1; $i <= $Num_Slices; $i++ ) + { + $output_str .= "

Slice $i:
\n"; + + // Unpack the first 16 bytes of the slice + $SliceA = unpack("NID/NGroupID/NOrigin/NStringlen", substr($IRB_Resource['ResData'], $Slicepos ) ); + $Slicepos += 16; + $output_str .= "ID = " . $SliceA['ID'] . "
\n"; + $output_str .= "Group ID = " . $SliceA['GroupID'] . "
\n"; + $output_str .= "Origin = " . $SliceA['Origin'] . "
\n"; + + // Extract a Unicode String + $output_str .= "Text = '" . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], $Slicepos, $SliceA['Stringlen']*2), TRUE ) . "'
\n"; + $Slicepos += $SliceA['Stringlen'] * 2; + + // Unpack the next 24 bytes of the slice + $SliceB = unpack("NType/NLeftPos/NTopPos/NRightPos/NBottomPos/NURLlen", substr($IRB_Resource['ResData'], $Slicepos ) ); + $Slicepos += 24; + $output_str .= "Type = " . $SliceB['Type'] . "
\n"; + $output_str .= "Position = Top:" . $SliceB['TopPos'] . ", Left:" . $SliceB['LeftPos'] . ", Bottom:" . $SliceB['BottomPos'] . ", Right:" . $SliceB['RightPos'] . " (Pixels)
\n"; + + // Extract a Unicode String + $output_str .= "URL = " . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], $Slicepos, $SliceB['URLlen']*2), TRUE ) . "
\n"; + $Slicepos += $SliceB['URLlen'] * 2; + + // Unpack the length of a Unicode String + $Targetlen = hexdec( bin2hex( substr( $IRB_Resource['ResData'], $Slicepos, 4 ) ) ); + $Slicepos += 4; + // Extract a Unicode String + $output_str .= "Target = '" . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], $Slicepos, $Targetlen*2), TRUE ) . "'
\n"; + $Slicepos += $Targetlen * 2; + + // Unpack the length of a Unicode String + $Messagelen = hexdec( bin2hex( substr( $IRB_Resource['ResData'], $Slicepos, 4 ) ) ); + $Slicepos += 4; + // Extract a Unicode String + $output_str .= "Message = '" . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], $Slicepos, $Messagelen*2), TRUE ) . "'
\n"; + $Slicepos += $Messagelen * 2; + + // Unpack the length of a Unicode String + $AltTaglen = hexdec( bin2hex( substr( $IRB_Resource['ResData'], $Slicepos, 4 ) ) ); + $Slicepos += 4; + // Extract a Unicode String + $output_str .= "Alt Tag = '" . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], $Slicepos, $AltTaglen*2), TRUE ) . "'
\n"; + $Slicepos += $AltTaglen * 2; + + // Unpack the HTML flag + if ( ord( $IRB_Resource['ResData']{ $Slicepos } ) === 0x01 ) + { + $output_str .= "Cell Text is HTML
\n"; + } + else + { + $output_str .= "Cell Text is NOT HTML
\n"; + } + $Slicepos++; + + // Unpack the length of a Unicode String + $CellTextlen = hexdec( bin2hex( substr( $IRB_Resource['ResData'], $Slicepos, 4 ) ) ); + $Slicepos += 4; + // Extract a Unicode String + $output_str .= "Cell Text = '" . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], $Slicepos, $CellTextlen*2), TRUE ) . "'
\n"; + $Slicepos += $CellTextlen * 2; + + + // Unpack the last 12 bytes of the slice + $SliceC = unpack("NAlignH/NAlignV/CAlpha/CRed/CGreen/CBlue", substr($IRB_Resource['ResData'], $Slicepos ) ); + $Slicepos += 12; + $output_str .= "Alignment = Horizontal:" . $SliceC['AlignH'] . ", Vertical:" . $SliceC['AlignV'] . "
\n"; + $output_str .= "Alpha Colour = " . $SliceC['Alpha'] . "
\n"; + $output_str .= "Red = " . $SliceC['Red'] . "
\n"; + $output_str .= "Green = " . $SliceC['Green'] . "
\n"; + $output_str .= "Blue = " . $SliceC['Blue'] . "\n"; + } + + $output_str .= "
$Resource_Name"; + + // Unpack the Grids info + $Grid_Info = unpack("NVersion/NGridCycleH/NGridCycleV/NGuideCount", $IRB_Resource['ResData'] ); + $output_str .= "Version = " . $Grid_Info['Version'] . "
\n"; + $output_str .= "Grid Cycle = " . $Grid_Info['GridCycleH']/32 . " Pixel(s) x " . $Grid_Info['GridCycleV']/32 . " Pixel(s)
\n"; + $output_str .= "Number of Guides = " . $Grid_Info['GuideCount'] . "\n"; + + // Cycle through the Guides + for( $i = 0; $i < $Grid_Info['GuideCount']; $i++ ) + { + // Unpack the info for this guide + $Guide_Info = unpack("NLocation/CDirection", substr($IRB_Resource['ResData'],16+$i*5,5) ); + $output_str .= "
Guide $i : Location = " . $Guide_Info['Location']/32 . " Pixel(s) from edge"; + if ( $Guide_Info['Direction'] === 0 ) + { + $output_str .= ", Vertical\n"; + } + else + { + $output_str .= ", Horizontal\n"; + } + } + break; + $output_str .= "
$Resource_Name"; + switch ( $Qual_Info['Quality'] ) + { + case 0xFFFD: + $output_str .= "Quality 1 (Low)
\n"; + break; + case 0xFFFE: + $output_str .= "Quality 2 (Low)
\n"; + break; + case 0xFFFF: + $output_str .= "Quality 3 (Low)
\n"; + break; + case 0x0000: + $output_str .= "Quality 4 (Low)
\n"; + break; + case 0x0001: + $output_str .= "Quality 5 (Medium)
\n"; + break; + case 0x0002: + $output_str .= "Quality 6 (Medium)
\n"; + break; + case 0x0003: + $output_str .= "Quality 7 (Medium)
\n"; + break; + case 0x0004: + $output_str .= "Quality 8 (High)
\n"; + break; + case 0x0005: + $output_str .= "Quality 9 (High)
\n"; + break; + case 0x0006: + $output_str .= "Quality 10 (Maximum)
\n"; + break; + case 0x0007: + $output_str .= "Quality 11 (Maximum)
\n"; + break; + case 0x0008: + $output_str .= "Quality 12 (Maximum)
\n"; + break; + default: + $output_str .= "Unknown Quality (" . $Qual_Info['Quality'] . ")
\n"; + break; + } + + switch ( $Qual_Info['Format'] ) + { + case 0x0000: + $output_str .= "Standard Format\n"; + break; + case 0x0001: + $output_str .= "Optimised Format\n"; + break; + case 0x0101: + $output_str .= "Progressive Format
\n"; + break; + default: + $output_str .= "Unknown Format (" . $Qual_Info['Format'] .")\n"; + break; + } + if ( $Qual_Info['Format'] == 0x0101 ) + { + switch ( $Qual_Info['Scans'] ) + { + case 0x0001: + $output_str .= "3 Scans\n"; + break; + case 0x0002: + $output_str .= "4 Scans\n"; + break; + case 0x0003: + $output_str .= "5 Scans\n"; + break; + default: + $output_str .= "Unknown number of scans (" . $Qual_Info['Scans'] .")\n"; + break; + } + } + $output_str .= "
$Resource_Name
\n";
+                                        $output_str .= "Format = " . (( $thumb_data['Format'] == 1 ) ? "JPEG RGB\n" :  "Raw RGB\n");
+                                        $output_str .= "Width = " . $thumb_data['Width'] . "\n";
+                                        $output_str .= "Height = " . $thumb_data['Height'] . "\n";
+                                        $output_str .= "Padded Row Bytes = " . $thumb_data['WidthBytes'] . " bytes\n";
+                                        $output_str .= "Total Size = " . $thumb_data['Size'] . " bytes\n";
+                                        $output_str .= "Compressed Size = " . $thumb_data['CompressedSize'] . " bytes\n";
+                                        $output_str .= "Bits per Pixel = " . $thumb_data['BitsPixel'] . " bits\n";
+                                        $output_str .= "Number of planes = " . $thumb_data['Planes'] . " bytes\n";
+
+                                        // Change: as of version 1.11 - Changed to make thumbnail link portable across directories
+                                        // Build the path of the thumbnail script and its filename parameter to put in a url
+                                        $link_str = get_relative_path( dirname(__FILE__) . "/get_ps_thumb.php" , getcwd ( ) );
+                                        $link_str .= "?filename=";
+                                        $link_str .= get_relative_path( $filename, dirname(__FILE__) );
+
+                                        // Add thumbnail link to html
+                                        $output_str .= "Thumbnail Data:
\n"; + + $output_str .= "
$Resource_Name
" . hexdec( bin2hex( $IRB_Resource['ResData'] ) ) . "
$Resource_Name\n"; + $output_str .= "$URL_count URL's in list
\n"; + $urlstr = substr( $IRB_Resource['ResData'], 4 ); + // TODO: Check if URL List in Photoshop IRB works + for( $i = 0; $i < $URL_count; $i++ ) + { + $url_data = unpack( "NLong/NID/NURLSize", $urlstr ); + $output_str .= "URL $i info: long = " . $url_data['Long'] .", "; + $output_str .= "ID = " . $url_data['ID'] . ", "; + $urlstr = substr( $urlstr, 12 ); + $url = substr( $urlstr, 0, $url_data['URLSize'] ); + $output_str .= "URL = " . HTML_UTF16_Escape( $url, TRUE ) . "
\n"; + } + $output_str .= "
$Resource_Name
\n";
+                                        $output_str .= Interpret_Halftone( $IRB_Resource['ResData'] );
+                                        $output_str .= "
$Resource_Name
\n";
+                                        $output_str .= "Cyan Halftoning Info:\n" . Interpret_Halftone( substr( $IRB_Resource['ResData'], 0, 18 ) ) . "\n\n";
+                                        $output_str .= "Magenta Halftoning Info:\n" . Interpret_Halftone( substr( $IRB_Resource['ResData'], 18, 18 ) ) . "\n\n";
+                                        $output_str .= "Yellow Halftoning Info:\n" . Interpret_Halftone( substr( $IRB_Resource['ResData'], 36, 18 ) ) . "\n";
+                                        $output_str .= "Black Halftoning Info:\n" . Interpret_Halftone( substr( $IRB_Resource['ResData'], 54, 18 ) ) . "\n";
+                                        $output_str .= "
$Resource_Name
\n";
+                                        $output_str .= Interpret_Transfer_Function( substr( $IRB_Resource['ResData'], 0, 28 ) ) ;
+                                        $output_str .= "
$Resource_Name
\n";
+                                        $output_str .= "Red Transfer Function:   \n" . Interpret_Transfer_Function( substr( $IRB_Resource['ResData'], 0, 28 ) ) . "\n\n";
+                                        $output_str .= "Green Transfer Function: \n" . Interpret_Transfer_Function( substr( $IRB_Resource['ResData'], 28, 28 ) ) . "\n\n";
+                                        $output_str .= "Blue Transfer Function:  \n" . Interpret_Transfer_Function( substr( $IRB_Resource['ResData'], 56, 28 ) ) . "\n";
+                                        $output_str .= "
$Resource_Name
\n";
+                                        if ( $IRB_Resource['ResData']{0} == "\x01" )
+                                        {
+                                                $output_str .= "Labels Selected\n";
+                                        }
+                                        else
+                                        {
+                                                $output_str .= "Labels Not Selected\n";
+                                        }
+                                        if ( $IRB_Resource['ResData']{1} == "\x01" )
+                                        {
+                                                $output_str .= "Crop Marks Selected\n";
+                                        }
+                                        else
+                                        {
+                                                $output_str .= "Crop Marks Not Selected\n";
+                                        }
+                                        if ( $IRB_Resource['ResData']{2} == "\x01" )
+                                        {
+                                                $output_str .= "Color Bars Selected\n";
+                                        }
+                                        else
+                                        {
+                                                $output_str .= "Color Bars Not Selected\n";
+                                        }
+                                        if ( $IRB_Resource['ResData']{3} == "\x01" )
+                                        {
+                                                $output_str .= "Registration Marks Selected\n";
+                                        }
+                                        else
+                                        {
+                                                $output_str .= "Registration Marks Not Selected\n";
+                                        }
+                                        if ( $IRB_Resource['ResData']{4} == "\x01" )
+                                        {
+                                                $output_str .= "Negative Selected\n";
+                                        }
+                                        else
+                                        {
+                                                $output_str .= "Negative Not Selected\n";
+                                        }
+                                        if ( $IRB_Resource['ResData']{5} == "\x01" )
+                                        {
+                                                $output_str .= "Flip Selected\n";
+                                        }
+                                        else
+                                        {
+                                                $output_str .= "Flip Not Selected\n";
+                                        }
+                                        if ( $IRB_Resource['ResData']{6} == "\x01" )
+                                        {
+                                                $output_str .= "Interpolate Selected\n";
+                                        }
+                                        else
+                                        {
+                                                $output_str .= "Interpolate Not Selected\n";
+                                        }
+                                        if ( $IRB_Resource['ResData']{7} == "\x01" )
+                                        {
+                                                $output_str .= "Caption Selected";
+                                        }
+                                        else
+                                        {
+                                                $output_str .= "Caption Not Selected";
+                                        }
+                                        $output_str .= "
$Resource_Name
\n";
+                                        $output_str .= "Version = " . $PrintFlags['Version'] . "\n";
+                                        $output_str .= "Centre Crop Marks = " . $PrintFlags['CentCrop'] . "\n";
+                                        $output_str .= "Bleed Width = " . $PrintFlags['BleedWidth'] . "\n";
+                                        $output_str .= "Bleed Width Scale = " . $PrintFlags['BleedWidthScale'];
+                                        $output_str .= "
$Resource_Name
\n";
+                                        $output_str .= "Horizontal Resolution = " . ($ResInfo['hRes_int'] + $ResInfo['hResdec']/65536) . " pixels per Inch\n";
+                                        $output_str .= "Vertical Resolution = " . ($ResInfo['vRes_int'] + $ResInfo['vResdec']/65536) . " pixels per Inch\n";
+                                        if ( $ResInfo['hResUnit'] == 1 )
+                                        {
+                                                $output_str .= "Display units for Horizontal Resolution = Pixels per Inch\n";
+                                        }
+                                        elseif ( $ResInfo['hResUnit'] == 2 )
+                                        {
+                                                $output_str .= "Display units for Horizontal Resolution = Pixels per Centimetre\n";
+                                        }
+                                        else
+                                        {
+                                                $output_str .= "Display units for Horizontal Resolution = Unknown Value (". $ResInfo['hResUnit'] .")\n";
+                                        }
+
+                                        if ( $ResInfo['vResUnit'] == 1 )
+                                        {
+                                                $output_str .= "Display units for Vertical Resolution = Pixels per Inch\n";
+                                        }
+                                        elseif ( $ResInfo['vResUnit'] == 2 )
+                                        {
+                                                $output_str .= "Display units for Vertical Resolution = Pixels per Centimetre\n";
+                                        }
+                                        else
+                                        {
+                                                $output_str .= "Display units for Vertical Resolution = Unknown Value (". $ResInfo['vResUnit'] .")\n";
+                                        }
+
+                                        if ( $ResInfo['widthUnit'] == 1 )
+                                        {
+                                                $output_str .= "Display units for Image Width = Inches\n";
+                                        }
+                                        elseif ( $ResInfo['widthUnit'] == 2 )
+                                        {
+                                                $output_str .= "Display units for Image Width = Centimetres\n";
+                                        }
+                                        elseif ( $ResInfo['widthUnit'] == 3 )
+                                        {
+                                                $output_str .= "Display units for Image Width = Points\n";
+                                        }
+                                        elseif ( $ResInfo['widthUnit'] == 4 )
+                                        {
+                                                $output_str .= "Display units for Image Width = Picas\n";
+                                        }
+                                        elseif ( $ResInfo['widthUnit'] == 5 )
+                                        {
+                                                $output_str .= "Display units for Image Width = Columns\n";
+                                        }
+                                        else
+                                        {
+                                                $output_str .= "Display units for Image Width = Unknown Value (". $ResInfo['widthUnit'] .")\n";
+                                        }
+
+                                        if ( $ResInfo['heightUnit'] == 1 )
+                                        {
+                                                $output_str .= "Display units for Image Height = Inches";
+                                        }
+                                        elseif ( $ResInfo['heightUnit'] == 2 )
+                                        {
+                                                $output_str .= "Display units for Image Height = Centimetres";
+                                        }
+                                        elseif ( $ResInfo['heightUnit'] == 3 )
+                                        {
+                                                $output_str .= "Display units for Image Height = Points";
+                                        }
+                                        elseif ( $ResInfo['heightUnit'] == 4 )
+                                        {
+                                                $output_str .= "Display units for Image Height = Picas";
+                                        }
+                                        elseif ( $ResInfo['heightUnit'] == 5 )
+                                        {
+                                                $output_str .= "Display units for Image Height = Columns";
+                                        }
+                                        else
+                                        {
+                                                $output_str .= "Display units for Image Height = Unknown Value (". $ResInfo['heightUnit'] .")";
+                                        }
+                                        $output_str .= "
$Resource_NameRESOURCE DECODING NOT IMPLEMENTED YET
" . strlen( $IRB_Resource['ResData'] ) . " bytes
\n"; + + // Add any secondary output to the HTML + $output_str .= $secondary_output_str; + + } + + // Return the HTML + return $output_str; +} + +/****************************************************************************** +* End of Function: Interpret_IRB_to_HTML +******************************************************************************/ + + + + + + +/****************************************************************************** +* +* INTERNAL FUNCTIONS +* +******************************************************************************/ + + + + + + + +/****************************************************************************** +* +* Function: unpack_Photoshop_IRB_Data +* +* Description: Extracts Photoshop Information Resource Block (IRB) information +* from a binary string containing the IRB, as read from a file +* +* Parameters: IRB_Data - The binary string containing the IRB +* +* Returns: IRBdata - The array of Photoshop IRB records +* +******************************************************************************/ + +function unpack_Photoshop_IRB_Data( $IRB_Data ) +{ + $pos = 0; + + // Cycle through the IRB and extract its records - Records are started with 8BIM, so cycle until no more instances of 8BIM can be found + while ( ( $pos < strlen( $IRB_Data ) ) && ( ($pos = strpos( $IRB_Data, "8BIM", $pos) ) !== FALSE ) ) + { + // Skip the position over the 8BIM characters + $pos += 4; + + // Next two characters are the record ID - denoting what type of record it is. + $ID = ord( $IRB_Data{ $pos } ) * 256 + ord( $IRB_Data{ $pos +1 } ); + + // Skip the positionover the two record ID characters + $pos += 2; + + // Next comes a Record Name - usually not used, but it should be a null terminated string, padded with 0x00 to be an even length + $namestartpos = $pos; + + // Change: Fixed processing of embedded resource names, as of revision 1.10 + + // NOTE: Photoshop does not process resource names according to the standard : + // "Adobe Photoshop 6.0 File Formats Specification, Version 6.0, Release 2, November 2000" + // + // The resource name is actually formatted as follows: + // One byte name length, followed by the null terminated ascii name string. + // The field is then padded with a Null character if required, to ensure that the + // total length of the name length and name is even. + + // Name - process it + // Get the length + $namelen = ord ( $IRB_Data{ $namestartpos } ); + + // Total length of name and length info must be even, hence name length must be odd + // Check if the name length is even, + if ( $namelen % 2 == 0 ) + { + // add one to length to make it odd + $namelen ++; + } + // Extract the name + $resembeddedname = trim( substr ( $IRB_Data, $namestartpos+1, $namelen) ); + $pos += $namelen + 1; + + + // Next is a four byte size field indicating the size in bytes of the record's data - MSB first + $datasize = ord( $IRB_Data{ $pos } ) * 16777216 + ord( $IRB_Data{ $pos + 1 } ) * 65536 + + ord( $IRB_Data{ $pos + 2 } ) * 256 + ord( $IRB_Data{ $pos + 3 } ); + $pos += 4; + + // The record is stored padded with 0x00 characters to make the size even, so we need to calculate the stored size + $storedsize = $datasize + ($datasize % 2); + + $resdata = substr ( $IRB_Data, $pos, $datasize ); + + // Get the description for this resource + // Check if this is a Path information Resource, since they have a range of ID's + if ( ( $ID >= 0x07D0 ) && ( $ID <= 0x0BB6 ) ) + { + $ResDesc = "ID Info : Path Information (saved paths)."; + } + else + { + if ( array_key_exists( $ID, $GLOBALS[ "Photoshop_ID_Descriptions" ] ) ) + { + $ResDesc = $GLOBALS[ "Photoshop_ID_Descriptions" ][ $ID ]; + } + else + { + $ResDesc = ""; + } + } + + // Get the Name of the Resource + if ( array_key_exists( $ID, $GLOBALS[ "Photoshop_ID_Names" ] ) ) + { + $ResName = $GLOBALS['Photoshop_ID_Names'][ $ID ]; + } + else + { + $ResName = ""; + } + + + // Store the Resource in the array to be returned + + $IRB_Array[] = array( "ResID" => $ID, + "ResName" => $ResName, + "ResDesc" => $ResDesc, + "ResEmbeddedName" => $resembeddedname, + "ResData" => $resdata ); + + // Jump over the data to the next record + $pos += $storedsize; + } + + // Return the array created + return $IRB_Array; +} + +/****************************************************************************** +* End of Function: unpack_Photoshop_IRB_Data +******************************************************************************/ + + + + + + + + + + + +/****************************************************************************** +* +* Function: pack_Photoshop_IRB_Data +* +* Description: Packs a Photoshop Information Resource Block (IRB) array into it's +* binary form, which can be written to a file +* +* Parameters: IRB_data - an Photoshop IRB array to be converted. Should be in +* the same format as received from get_Photoshop_IRB +* +* Returns: packed_IRB_data - the binary string of packed IRB data +* +******************************************************************************/ + +function pack_Photoshop_IRB_Data( $IRB_data ) +{ + $packed_IRB_data = ""; + + // Cycle through each resource in the IRB, + foreach ($IRB_data as $resource) + { + + // Change: Fix to avoid creating blank resources, as of revision 1.10 + + // Check if there is actually any data for this resource + if( strlen( $resource['ResData'] ) == 0 ) + { + // No data for resource - skip it + continue; + } + + // Append the 8BIM tag, and resource ID to the packed output data + $packed_IRB_data .= pack("a4n", "8BIM", $resource['ResID'] ); + + + // Change: Fixed processing of embedded resource names, as of revision 1.10 + + // NOTE: Photoshop does not process resource names according to the standard : + // "Adobe Photoshop 6.0 File Formats Specification, Version 6.0, Release 2, November 2000" + // + // The resource name is actually formatted as follows: + // One byte name length, followed by the null terminated ascii name string. + // The field is then padded with a Null character if required, to ensure that the + // total length of the name length and name is even. + + // Append Name Size + $packed_IRB_data .= pack( "c", strlen(trim($resource['ResEmbeddedName']))); + + // Append the Resource Name to the packed output data + $packed_IRB_data .= trim($resource['ResEmbeddedName']); + + // If the resource name is even length, then with the addition of + // the size it becomes odd and needs to be padded to an even number + if ( strlen( trim($resource['ResEmbeddedName']) ) % 2 == 0 ) + { + // then it needs to be evened up by appending another null + $packed_IRB_data .= "\x00"; + } + + // Append the resource data size to the packed output data + $packed_IRB_data .= pack("N", strlen( $resource['ResData'] ) ); + + // Append the resource data to the packed output data + $packed_IRB_data .= $resource['ResData']; + + // If the resource data is odd length, + if ( strlen( $resource['ResData'] ) % 2 == 1 ) + { + // then it needs to be evened up by appending another null + $packed_IRB_data .= "\x00"; + } + } + + // Return the packed data string + return $packed_IRB_data; +} + +/****************************************************************************** +* End of Function: pack_Photoshop_IRB_Data +******************************************************************************/ + + + + + + + + +/****************************************************************************** +* +* Internal Function: Interpret_Transfer_Function +* +* Description: Used by Interpret_IRB_to_HTML to interpret Color transfer functions +* for Photoshop IRB resource 0x03F8. Converts the transfer function +* information to a human readable version. +* +* Parameters: Transfer_Function_Binary - a 28 byte Ink curves structure string +* +* Returns: output_str - the text string containing the transfer function +* information +* +******************************************************************************/ + +function Interpret_Transfer_Function( $Transfer_Function_Binary ) +{ + // Unpack the Transfer function information + $Trans_vals = unpack ( "n13Curve/nOverride", $Transfer_Function_Binary ); + + $output_str = "Transfer Function Points: "; + + // Cycle through each of the Transfer function array values + foreach ( $Trans_vals as $Key => $val ) + { + // Check if the value should be negative + if ($val > 32768 ) + { + // Value should be negative - make it so + $val = $val - 65536; + } + // Check that the Override item is not getting in this list, and + // that the value is not -1, which means ignored + if ( ( $Key != "Override" ) && ( $val != -1 ) ) + { + // This is a valid transfer function point, output it + $output_str .= $val/10 . "%, "; + } + } + + // Output the override info + if ( $Trans_vals['Override'] == 0 ) + { + $output_str .= "\nOverride: Let printer supply curve"; + } + else + { + $output_str .= "\nOverride: Override printer’s default transfer curve"; + } + + // Return the result + return $output_str; +} + +/****************************************************************************** +* End of Function: Interpret_Transfer_Function +******************************************************************************/ + + + + + +/****************************************************************************** +* +* Internal Function: Interpret_Halftone +* +* Description: Used by Interpret_IRB_to_HTML to interpret Color halftoning information +* for Photoshop IRB resource 0x03F5. Converts the halftoning info +* to a human readable version. +* +* Parameters: Transfer_Function_Binary - a 18 byte Halftone screen parameter +& structure string +* +* Returns: output_str - the text string containing the transfer function +* information +* +******************************************************************************/ + +function Interpret_Halftone( $Halftone_Binary ) +{ + // Create a string to receive the output + $output_str = ""; + + // Unpack the binary data into an array + $HalftoneInfo = unpack( "nFreqVal_int/nFreqVal_dec/nFreqScale/nAngle_int/nAngle_dec/nShapeCode/NMisc/CAccurate/CDefault", $Halftone_Binary ); + + // Interpret Ink Screen Frequency + $output_str .= "Ink Screen Frequency = " . ($HalftoneInfo['FreqVal_int'] + $HalftoneInfo['FreqVal_dec']/65536) . " lines per Inch\n"; + if ( $HalftoneInfo['FreqScale'] == 1 ) + { + $output_str .= "Display units for Ink Screen Frequency = Inches\n"; + } + else + { + $output_str .= "Display units for Ink Screen Frequency = Centimetres\n"; + } + + // Interpret Angle for screen + $output_str .= "Angle for screen = " . ($HalftoneInfo['Angle_int'] + $HalftoneInfo['Angle_dec']/65536) . " degrees\n"; + + // Interpret Shape of Halftone Dots + if ($HalftoneInfo['ShapeCode'] > 32768 ) + { + $HalftoneInfo['ShapeCode'] = $HalftoneInfo['ShapeCode'] - 65536; + } + if ( $HalftoneInfo['ShapeCode'] == 0 ) + { + $output_str .= "Shape of Halftone Dots = Round\n"; + } + elseif ( $HalftoneInfo['ShapeCode'] == 1 ) + { + $output_str .= "Shape of Halftone Dots = Ellipse\n"; + } + elseif ( $HalftoneInfo['ShapeCode'] == 2 ) + { + $output_str .= "Shape of Halftone Dots = Line\n"; + } + elseif ( $HalftoneInfo['ShapeCode'] == 3 ) + { + $output_str .= "Shape of Halftone Dots = Square\n"; + } + elseif ( $HalftoneInfo['ShapeCode'] == 4 ) + { + $output_str .= "Shape of Halftone Dots = Cross\n"; + } + elseif ( $HalftoneInfo['ShapeCode'] == 6 ) + { + $output_str .= "Shape of Halftone Dots = Diamond\n"; + } + else + { + $output_str .= "Shape of Halftone Dots = Unknown shape (" . $HalftoneInfo['ShapeCode'] . ")\n"; + } + + // Interpret Accurate Screens + if ( $HalftoneInfo['Accurate'] == 1 ) + { + $output_str .= "Use Accurate Screens Selected\n"; + } + else + { + $output_str .= "Use Other (not Accurate) Screens Selected\n"; + } + + // Interpret Printer Default Screens + if ( $HalftoneInfo['Default'] == 1 ) + { + $output_str .= "Use printer’s default screens\n"; + } + else + { + $output_str .= "Use Other (not Printer Default) Screens Selected\n"; + } + + // Return Text + return $output_str; + +} + +/****************************************************************************** +* End of Global Variable: Interpret_Halftone +******************************************************************************/ + + + + + + + + + + + + +/****************************************************************************** +* Global Variable: Photoshop_ID_Names +* +* Contents: The Names of the Photoshop IRB resources, indexed by their +* resource number +* +******************************************************************************/ + +$GLOBALS[ "Photoshop_ID_Names" ] = array( +0x03E8 => "Number of channels, rows, columns, depth, and mode. (Obsolete)", +0x03E9 => "Macintosh print manager info ", +0x03EB => "Indexed color table (Obsolete)", +0x03ED => "Resolution Info", +0x03EE => "Alpha Channel Names", +0x03EF => "Display Info", +0x03F0 => "Caption String", +0x03F1 => "Border information", +0x03F2 => "Background color", +0x03F3 => "Print flags", +0x03F4 => "Grayscale and multichannel halftoning information", +0x03F5 => "Color halftoning information", +0x03F6 => "Duotone halftoning information", +0x03F7 => "Grayscale and multichannel transfer function", +0x03F8 => "Color transfer functions", +0x03F9 => "Duotone transfer functions", +0x03FA => "Duotone image information", +0x03FB => "Black and white values", +0x03FC => "Obsolete Resource.", +0x03FD => "EPS options", +0x03FE => "Quick Mask information", +0x03FF => "Obsolete Resource", +0x0400 => "Layer state information", +0x0401 => "Working path (not saved)", +0x0402 => "Layers group information", +0x0403 => "Obsolete Resource", +0x0404 => "IPTC-NAA record", +0x0405 => "Raw Format Image mode", +0x0406 => "JPEG quality", +0x0408 => "Grid and guides information", +0x0409 => "Thumbnail resource", +0x040A => "Copyright flag", +0x040B => "URL", +0x040C => "Thumbnail resource", +0x040D => "Global Angle", +0x040E => "Color samplers resource", +0x040F => "ICC Profile", +0x0410 => "Watermark", +0x0411 => "ICC Untagged", +0x0412 => "Effects visible", +0x0413 => "Spot Halftone", +0x0414 => "Document Specific IDs", +0x0415 => "Unicode Alpha Names", +0x0416 => "Indexed Color Table Count", +0x0417 => "Tansparent Index. Index of transparent color, if any.", +0x0419 => "Global Altitude", +0x041A => "Slices", +0x041B => "Workflow URL", +0x041C => "Jump To XPEP", +0x041D => "Alpha Identifiers", +0x041E => "URL List", +0x0421 => "Version Info", +0x0BB7 => "Name of clipping path.", +0x2710 => "Print flags information" +); + +/****************************************************************************** +* End of Global Variable: Photoshop_ID_Names +******************************************************************************/ + + + + + +/****************************************************************************** +* Global Variable: Photoshop_ID_Descriptions +* +* Contents: The Descriptions of the Photoshop IRB resources, indexed by their +* resource number +* +******************************************************************************/ + +$GLOBALS[ "Photoshop_ID_Descriptions" ] = array( +0x03E8 => "Obsolete—Photoshop 2.0 only. number of channels, rows, columns, depth, and mode.", +0x03E9 => "Optional. Macintosh print manager print info record.", +0x03EB => "Obsolete—Photoshop 2.0 only. Contains the indexed color table.", +0x03ED => "ResolutionInfo structure. See Appendix A in Photoshop SDK Guide.pdf", +0x03EE => "Names of the alpha channels as a series of Pascal strings.", +0x03EF => "DisplayInfo structure. See Appendix A in Photoshop SDK Guide.pdf", +0x03F0 => "Optional. The caption as a Pascal string.", +0x03F1 => "Border information. border width, border units", +0x03F2 => "Background color.", +0x03F3 => "Print flags. labels, crop marks, color bars, registration marks, negative, flip, interpolate, caption.", +0x03F4 => "Grayscale and multichannel halftoning information.", +0x03F5 => "Color halftoning information.", +0x03F6 => "Duotone halftoning information.", +0x03F7 => "Grayscale and multichannel transfer function.", +0x03F8 => "Color transfer functions.", +0x03F9 => "Duotone transfer functions.", +0x03FA => "Duotone image information.", +0x03FB => "Effective black and white values for the dot range.", +0x03FC => "Obsolete Resource.", +0x03FD => "EPS options.", +0x03FE => "Quick Mask information. Quick Mask channel ID, Mask initially empty.", +0x03FF => "Obsolete Resource.", +0x0400 => "Layer state information. Index of target layer.", +0x0401 => "Working path (not saved).", +0x0402 => "Layers group information. Group ID for the dragging groups. Layers in a group have the same group ID.", +0x0403 => "Obsolete Resource.", +0x0404 => "IPTC-NAA record. This contains the File Info... information. See the IIMV4.pdf document.", +0x0405 => "Image mode for raw format files.", +0x0406 => "JPEG quality. Private.", +0x0408 => "Grid and guides information.", +0x0409 => "Thumbnail resource.", +0x040A => "Copyright flag. Boolean indicating whether image is copyrighted. Can be set via Property suite or by user in File Info...", +0x040B => "URL. Handle of a text string with uniform resource locator. Can be set via Property suite or by user in File Info...", +0x040C => "Thumbnail resource.", +0x040D => "Global Angle. Global lighting angle for effects layer.", +0x040E => "Color samplers resource.", +0x040F => "ICC Profile. The raw bytes of an ICC format profile, see the ICC34.pdf and ICC34.h files from the Internation Color Consortium.", +0x0410 => "Watermark.", +0x0411 => "ICC Untagged. Disables any assumed profile handling when opening the file. 1 = intentionally untagged.", +0x0412 => "Effects visible. Show/hide all the effects layer.", +0x0413 => "Spot Halftone. Version, length, variable length data.", +0x0414 => "Document specific IDs for layer identification", +0x0415 => "Unicode Alpha Names. Length and the string", +0x0416 => "Indexed Color Table Count. Number of colors in table that are actually defined", +0x0417 => "Transparent Index. Index of transparent color, if any.", +0x0419 => "Global Altitude.", +0x041A => "Slices.", +0x041B => "Workflow URL. Length, string.", +0x041C => "Jump To XPEP. Major version, Minor version, Count. Table which can include: Dirty flag, Mod date.", +0x041D => "Alpha Identifiers.", +0x041E => "URL List. Count of URLs, IDs, and strings", +0x0421 => "Version Info. Version, HasRealMergedData, string of writer name, string of reader name, file version.", +0x0BB7 => "Name of clipping path.", +0x2710 => "Print flags information. Version, Center crop marks, Bleed width value, Bleed width scale." +); + +/****************************************************************************** +* End of Global Variable: Photoshop_ID_Descriptions +******************************************************************************/ + + + + + + +?> \ No newline at end of file diff --git a/includes/jpeg_metadata_tk/PictureInfo.php b/includes/jpeg_metadata_tk/PictureInfo.php new file mode 100644 index 0000000..05edd00 --- /dev/null +++ b/includes/jpeg_metadata_tk/PictureInfo.php @@ -0,0 +1,284 @@ + $App12_PI_Head, "Picture Info" => substr( $App12_PI_Text, 0, $pos + 5 ) ); + } + else + { + return array( "Header" => $App12_PI_Head, "Picture Info" => $App12_PI_Text ); + } + } + + // No Picture Info Segment Found - Return False + return array( FALSE, FALSE ); +} + +/****************************************************************************** +* End of Function: get_jpeg_header_data +******************************************************************************/ + + + + + +/****************************************************************************** +* +* Function: put_jpeg_App12_Pic_Info +* +* Description: Writes Picture Info text into an App12 JPEG segment. Uses information +* supplied by the get_jpeg_header_data function. If no App12 exists +* already a new one is created, otherwise it replaces the old one +* +* Parameters: jpeg_header_data - a JPEG header data array in the same format +* as from get_jpeg_header_data +* new_Pic_Info_Text - The Picture Info Text, including any header +* that is required +* +* Returns: jpeg_header_data - the JPEG header array with the new Picture +* info segment inserted +* FALSE - if an error occured +* +******************************************************************************/ + +function put_jpeg_App12_Pic_Info( $jpeg_header_data, $new_Pic_Info_Text ) +{ + + //Cycle through the header segments + for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) + { + // Check if we have found an APP12 header, + if ( strcmp ( $jpeg_header_data[$i][SegName], "APP12" ) == 0 ) + { + // Found an APP12 segment + // Check if the APP12 has one of the correct labels (headers) + // for a picture info segment + if ( ( strncmp ( $jpeg_header_data[$i]['SegData'], "[picture info]", 14) == 0 ) || + ( strncmp ( $jpeg_header_data[$i]['SegData'], "\x0a\x09\x09\x09\x09[picture info]", 19) == 0 ) || + ( strncmp ( $jpeg_header_data[$i]['SegData'], "SEIKO EPSON CORP. \x00", 20) == 0 ) || + ( strncmp ( $jpeg_header_data[$i]['SegData'], "Agfa Gevaert \x00", 16) == 0 ) || + ( strncmp ( $jpeg_header_data[$i]['SegData'], "SanyoElectricDSC\x00", 17) == 0 ) || + ( strncmp ( $jpeg_header_data[$i]['SegData'], "Type=", 5) == 0 ) || + ( strncmp ( $jpeg_header_data[$i]['SegData'], "OLYMPUS OPTICAL CO.,LTD.", 24) == 0 ) ) + { + // Found a preexisting Picture Info segment - Replace it with the new one and return. + $jpeg_header_data[$i][SegData] = $new_Pic_Info_Text; + return $jpeg_header_data; + } + } + } + + // No preexisting Picture Info segment found, insert a new one at the start of the header data. + + // Determine highest position of an APP segment at or below APP12, so we can put the + // new APP12 at this position + + + $highest_APP = -1; + + //Cycle through the header segments + for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) + { + // Check if we have found an APP segment at or below APP12, + if ( ( $jpeg_header_data[$i]['SegType'] >= 0xE0 ) && ( $jpeg_header_data[$i]['SegType'] <= 0xEC ) ) + { + // Found an APP segment at or below APP12 + $highest_APP = $i; + } + } + + // Insert the new Picture Info segment + array_splice($jpeg_header_data, $highest_APP + 1 , 0, array( array( "SegType" => 0xEC, + "SegName" => "APP12", + "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xEC ], + "SegData" => $new_Pic_Info_Text ) ) ); + + return $jpeg_header_data; + + +} + +/****************************************************************************** +* End of Function: put_jpeg_header_data +******************************************************************************/ + + + +/****************************************************************************** +* +* Function: Interpret_App12_Pic_Info_to_HTML +* +* Description: Generates html showing the contents of any JPEG App12 Picture +* Info segment +* +* Parameters: jpeg_header_data - the JPEG header data, as retrieved +* from the get_jpeg_header_data function +* +* Returns: output - the HTML +* +******************************************************************************/ + +function Interpret_App12_Pic_Info_to_HTML( $jpeg_header_data ) +{ + // Create a string to receive the output + $output = ""; + + // read the App12 Picture Info segment + $PI = get_jpeg_App12_Pic_Info( $jpeg_header_data ); + + // Check if the Picture Info segment was valid + if ( $PI !== array(FALSE, FALSE) ) + { + // Picture Info exists - add it to the output + $output .= "

Picture Info Text

\n"; + $output .= "

Header: " . HTML_UTF8_Escape( $PI['Header'] ) . "

\n"; + $output .= "

Picture Info Text:

" . HTML_UTF8_Escape( $PI['Picture Info'] ) . "
\n"; + } + + // Return the result + return $output; +} + +/****************************************************************************** +* End of Function: Interpret_App12_Pic_Info_to_HTML +******************************************************************************/ + + + +?> diff --git a/includes/jpeg_metadata_tk/Toolkit_Version.php b/includes/jpeg_metadata_tk/Toolkit_Version.php new file mode 100644 index 0000000..99be1f3 --- /dev/null +++ b/includes/jpeg_metadata_tk/Toolkit_Version.php @@ -0,0 +1,50 @@ + \ No newline at end of file diff --git a/includes/jpeg_metadata_tk/Unicode.php b/includes/jpeg_metadata_tk/Unicode.php new file mode 100644 index 0000000..19cbc5b --- /dev/null +++ b/includes/jpeg_metadata_tk/Unicode.php @@ -0,0 +1,1227 @@ + 1.10 : Added the following functions: +* smart_HTML_Entities +* smart_htmlspecialchars +* HTML_UTF16_UnEscape +* HTML_UTF8_UnEscape +* changed HTML_UTF8_Escape and HTML_UTF16_Escape to +* use smart_htmlspecialchars, so that characters which +* were already escaped would remain intact +* +* +* URL: http://electronics.ozhiker.com +* +* License: This file is part of the PHP JPEG Metadata Toolkit. +* +* The PHP JPEG Metadata Toolkit is free software; you can +* redistribute it and/or modify it under the terms of the +* GNU General Public License as published by the Free Software +* Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* The PHP JPEG Metadata Toolkit is distributed in the hope +* that it will be useful, but WITHOUT ANY WARRANTY; without +* even the implied warranty of MERCHANTABILITY or FITNESS +* FOR A PARTICULAR PURPOSE. See the GNU General Public License +* for more details. +* +* You should have received a copy of the GNU General Public +* License along with the PHP JPEG Metadata Toolkit; if not, +* write to the Free Software Foundation, Inc., 59 Temple +* Place, Suite 330, Boston, MA 02111-1307 USA +* +* If you require a different license for commercial or other +* purposes, please contact the author: evan@ozhiker.com +* +******************************************************************************/ + + +// TODO: UTF-16 functions have not been tested fully + + + +/****************************************************************************** +* +* Unicode UTF-8 Encoding Functions +* +* Description: UTF-8 is a Unicode encoding system in which extended characters +* use only the upper half (128 values) of the byte range, thus it +* allows the use of normal 7-bit ASCII text. +* 7-Bit ASCII will pass straight through UTF-8 encoding/decoding without change +* +* +* The encoding is as follows: +* Unicode Value : Binary representation (x=data bit) +*-------------------------------------------------------------------------------- +* U-00000000 - U-0000007F: 0xxxxxxx <- This is 7-bit ASCII +* U-00000080 - U-000007FF: 110xxxxx 10xxxxxx +* U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx +* U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx +* U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx +* U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx +*-------------------------------------------------------------------------------- +* +******************************************************************************/ + + + + +/****************************************************************************** +* +* Unicode UTF-16 Encoding Functions +* +* Description: UTF-16 is a Unicode encoding system uses 16 bit values for representing +* characters. +* It also has an extended set of characters available by the use +* of surrogate pairs, which are a pair of 16 bit values, giving a +* total data length of 20 useful bits. +* +* +* The encoding is as follows: +* Unicode Value : Binary representation (x=data bit) +*-------------------------------------------------------------------------------- +* U-000000 - U-00D7FF: xxxxxxxx xxxxxxxx +* U-00D800 - U-00DBFF: Not available - used for high surrogate pairs +* U-00DC00 - U-00DFFF: Not available - used for low surrogate pairs + U-00E000 - U-00FFFF: xxxxxxxx xxxxxxxx +* U-010000 - U-10FFFF: 110110ww wwxxxxxx 110111xx xxxxxxxx ( wwww = (uni-0x10000)/0x10000 ) +*-------------------------------------------------------------------------------- +* +* Surrogate pair Calculations +* +* $hi = ($uni - 0x10000) / 0x400 + 0xD800; +* $lo = ($uni - 0x10000) % 0x400 + 0xDC00; +* +* +* $uni = 0x10000 + ($hi - 0xD800) * 0x400 + ($lo - 0xDC00); +* +* +******************************************************************************/ + + + + + + +/****************************************************************************** +* +* Function: UTF8_fix +* +* Description: Checks a string for badly formed Unicode UTF-8 coding and +* returns the same string containing only the parts which +* were properly formed UTF-8 data. +* +* Parameters: utf8_text - a string with possibly badly formed UTF-8 data +* +* Returns: output - the well formed UTF-8 version of the string +* +******************************************************************************/ + +function UTF8_fix( $utf8_text ) +{ + // Initialise the current position in the string + $pos = 0; + + // Create a string to accept the well formed output + $output = "" ; + + // Cycle through each group of bytes, ensuring the coding is correct + while ( $pos < strlen( $utf8_text ) ) + { + // Retreive the current numerical character value + $chval = ord($utf8_text{$pos}); + + // Check what the first character is - it will tell us how many bytes the + // Unicode value covers + + if ( ( $chval >= 0x00 ) && ( $chval <= 0x7F ) ) + { + // 1 Byte UTF-8 Unicode (7-Bit ASCII) Character + $bytes = 1; + } + else if ( ( $chval >= 0xC0 ) && ( $chval <= 0xDF ) ) + { + // 2 Byte UTF-8 Unicode Character + $bytes = 2; + } + else if ( ( $chval >= 0xE0 ) && ( $chval <= 0xEF ) ) + { + // 3 Byte UTF-8 Unicode Character + $bytes = 3; + } + else if ( ( $chval >= 0xF0 ) && ( $chval <= 0xF7 ) ) + { + // 4 Byte UTF-8 Unicode Character + $bytes = 4; + } + else if ( ( $chval >= 0xF8 ) && ( $chval <= 0xFB ) ) + { + // 5 Byte UTF-8 Unicode Character + $bytes = 5; + } + else if ( ( $chval >= 0xFC ) && ( $chval <= 0xFD ) ) + { + // 6 Byte UTF-8 Unicode Character + $bytes = 6; + } + else + { + // Invalid Code - skip character and do nothing + $bytes = 0; + $pos++; + } + + + // check that there is enough data remaining to read + if (($pos + $bytes - 1) < strlen( $utf8_text ) ) + { + // Cycle through the number of bytes specified, + // copying them to the output string + while ( $bytes > 0 ) + { + $output .= $utf8_text{$pos}; + $pos++; + $bytes--; + } + } + else + { + break; + } + } + + // Return the result + return $output; +} + +/****************************************************************************** +* End of Function: UTF8_fix +******************************************************************************/ + + + + + + + + + +/****************************************************************************** +* +* Function: UTF16_fix +* +* Description: Checks a string for badly formed Unicode UTF-16 coding and +* returns the same string containing only the parts which +* were properly formed UTF-16 data. +* +* Parameters: utf16_text - a string with possibly badly formed UTF-16 data +* MSB_first - True will cause processing as Big Endian UTF-16 (Motorola, MSB first) +* False will cause processing as Little Endian UTF-16 (Intel, LSB first) +* +* Returns: output - the well formed UTF-16 version of the string +* +******************************************************************************/ + +function UTF16_fix( $utf16_text, $MSB_first ) +{ + // Initialise the current position in the string + $pos = 0; + + // Create a string to accept the well formed output + $output = "" ; + + // Cycle through each group of bytes, ensuring the coding is correct + while ( $pos < strlen( $utf16_text ) ) + { + // Retreive the current numerical character value + $chval1 = ord($utf16_text{$pos}); + + // Skip over character just read + $pos++; + + // Check if there is another character available + if ( $pos < strlen( $utf16_text ) ) + { + // Another character is available - get it for the second half of the UTF-16 value + $chval2 = ord( $utf16_text{$pos} ); + } + else + { + // Error - no second byte to this UTF-16 value - end processing + continue 1; + } + + // Skip over character just read + $pos++; + + // Calculate the 16 bit unicode value + if ( $MSB_first ) + { + // Big Endian + $UTF16_val = $chval1 * 0x100 + $chval2; + } + else + { + // Little Endian + $UTF16_val = $chval2 * 0x100 + $chval1; + } + + + + if ( ( ( $UTF16_val >= 0x0000 ) && ( $UTF16_val <= 0xD7FF ) ) || + ( ( $UTF16_val >= 0xE000 ) && ( $UTF16_val <= 0xFFFF ) ) ) + { + // Normal Character (Non Surrogate pair) + // Add it to the output + $output .= chr( $chval1 ) . chr ( $chval2 ); + } + else if ( ( $UTF16_val >= 0xD800 ) && ( $UTF16_val <= 0xDBFF ) ) + { + // High surrogate of a surrogate pair + // Now we need to read the low surrogate + // Check if there is another 2 characters available + if ( ( $pos + 3 ) < strlen( $utf16_text ) ) + { + // Another 2 characters are available - get them + $chval3 = ord( $utf16_text{$pos} ); + $chval4 = ord( $utf16_text{$pos+1} ); + + // Calculate the second 16 bit unicode value + if ( $MSB_first ) + { + // Big Endian + $UTF16_val2 = $chval3 * 0x100 + $chval4; + } + else + { + // Little Endian + $UTF16_val2 = $chval4 * 0x100 + $chval3; + } + + // Check that this is a low surrogate + if ( ( $UTF16_val2 >= 0xDC00 ) && ( $UTF16_val2 <= 0xDFFF ) ) + { + // Low surrogate found following high surrogate + // Add both to the output + $output .= chr( $chval1 ) . chr ( $chval2 ) . chr( $chval3 ) . chr ( $chval4 ); + + // Skip over the low surrogate + $pos += 2; + } + else + { + // Low surrogate not found after high surrogate + // Don't add either to the output + // Only the High surrogate is skipped and processing continues after it + } + + } + else + { + // Error - not enough data for low surrogate - end processing + continue 1; + } + + } + else + { + // Low surrogate of a surrogate pair + // This should not happen - it means this is a lone low surrogate + // Dont add it to the output + } + + } + + // Return the result + return $output; +} + +/****************************************************************************** +* End of Function: UTF16_fix +******************************************************************************/ + + + + + +/****************************************************************************** +* +* Function: UTF8_to_unicode_array +* +* Description: Converts a string encoded with Unicode UTF-8, to an array of +* numbers which represent unicode character numbers +* +* Parameters: utf8_text - a string containing the UTF-8 data +* +* Returns: output - the array containing the unicode character numbers +* +******************************************************************************/ + +function UTF8_to_unicode_array( $utf8_text ) +{ + // Create an array to receive the unicode character numbers output + $output = array( ); + + // Cycle through the characters in the UTF-8 string + for ( $pos = 0; $pos < strlen( $utf8_text ); $pos++ ) + { + // Retreive the current numerical character value + $chval = ord($utf8_text{$pos}); + + // Check what the first character is - it will tell us how many bytes the + // Unicode value covers + + if ( ( $chval >= 0x00 ) && ( $chval <= 0x7F ) ) + { + // 1 Byte UTF-8 Unicode (7-Bit ASCII) Character + $bytes = 1; + $outputval = $chval; // Since 7-bit ASCII is unaffected, the output equals the input + } + else if ( ( $chval >= 0xC0 ) && ( $chval <= 0xDF ) ) + { + // 2 Byte UTF-8 Unicode + $bytes = 2; + $outputval = $chval & 0x1F; // The first byte is bitwise ANDed with 0x1F to remove the leading 110b + } + else if ( ( $chval >= 0xE0 ) && ( $chval <= 0xEF ) ) + { + // 3 Byte UTF-8 Unicode + $bytes = 3; + $outputval = $chval & 0x0F; // The first byte is bitwise ANDed with 0x0F to remove the leading 1110b + } + else if ( ( $chval >= 0xF0 ) && ( $chval <= 0xF7 ) ) + { + // 4 Byte UTF-8 Unicode + $bytes = 4; + $outputval = $chval & 0x07; // The first byte is bitwise ANDed with 0x07 to remove the leading 11110b + } + else if ( ( $chval >= 0xF8 ) && ( $chval <= 0xFB ) ) + { + // 5 Byte UTF-8 Unicode + $bytes = 5; + $outputval = $chval & 0x03; // The first byte is bitwise ANDed with 0x03 to remove the leading 111110b + } + else if ( ( $chval >= 0xFC ) && ( $chval <= 0xFD ) ) + { + // 6 Byte UTF-8 Unicode + $bytes = 6; + $outputval = $chval & 0x01; // The first byte is bitwise ANDed with 0x01 to remove the leading 1111110b + } + else + { + // Invalid Code - do nothing + $bytes = 0; + } + + // Check if the byte was valid + if ( $bytes !== 0 ) + { + // The byte was valid + + // Check if there is enough data left in the UTF-8 string to allow the + // retrieval of the remainder of this unicode character + if ( $pos + $bytes - 1 < strlen( $utf8_text ) ) + { + // The UTF-8 string is long enough + + // Cycle through the number of bytes required, + // minus the first one which has already been done + while ( $bytes > 1 ) + { + $pos++; + $bytes--; + + // Each remaining byte is coded with 6 bits of data and 10b on the high + // order bits. Hence we need to shift left by 6 bits (0x40) then add the + // current characer after it has been bitwise ANDed with 0x3F to remove the + // highest two bits. + $outputval = $outputval*0x40 + ( (ord($utf8_text{$pos})) & 0x3F ); + } + + // Add the calculated Unicode number to the output array + $output[] = $outputval; + } + } + + } + + // Return the resulting array + return $output; +} + +/****************************************************************************** +* End of Function: UTF8_to_unicode_array +******************************************************************************/ + + + + + +/****************************************************************************** +* +* Function: UTF16_to_unicode_array +* +* Description: Converts a string encoded with Unicode UTF-16, to an array of +* numbers which represent unicode character numbers +* +* Parameters: utf16_text - a string containing the UTF-16 data +* MSB_first - True will cause processing as Big Endian UTF-16 (Motorola, MSB first) +* False will cause processing as Little Endian UTF-16 (Intel, LSB first) +* +* Returns: output - the array containing the unicode character numbers +* +******************************************************************************/ + +function UTF16_to_unicode_array( $utf16_text, $MSB_first ) +{ + // Create an array to receive the unicode character numbers output + $output = array( ); + + + // Initialise the current position in the string + $pos = 0; + + // Cycle through each group of bytes, ensuring the coding is correct + while ( $pos < strlen( $utf16_text ) ) + { + // Retreive the current numerical character value + $chval1 = ord($utf16_text{$pos}); + + // Skip over character just read + $pos++; + + // Check if there is another character available + if ( $pos < strlen( $utf16_text ) ) + { + // Another character is available - get it for the second half of the UTF-16 value + $chval2 = ord( $utf16_text{$pos} ); + } + else + { + // Error - no second byte to this UTF-16 value - end processing + continue 1; + } + + // Skip over character just read + $pos++; + + // Calculate the 16 bit unicode value + if ( $MSB_first ) + { + // Big Endian + $UTF16_val = $chval1 * 0x100 + $chval2; + } + else + { + // Little Endian + $UTF16_val = $chval2 * 0x100 + $chval1; + } + + + if ( ( ( $UTF16_val >= 0x0000 ) && ( $UTF16_val <= 0xD7FF ) ) || + ( ( $UTF16_val >= 0xE000 ) && ( $UTF16_val <= 0xFFFF ) ) ) + { + // Normal Character (Non Surrogate pair) + // Add it to the output + $output[] = $UTF16_val; + } + else if ( ( $UTF16_val >= 0xD800 ) && ( $UTF16_val <= 0xDBFF ) ) + { + // High surrogate of a surrogate pair + // Now we need to read the low surrogate + // Check if there is another 2 characters available + if ( ( $pos + 3 ) < strlen( $utf16_text ) ) + { + // Another 2 characters are available - get them + $chval3 = ord( $utf16_text{$pos} ); + $chval4 = ord( $utf16_text{$pos+1} ); + + // Calculate the second 16 bit unicode value + if ( $MSB_first ) + { + // Big Endian + $UTF16_val2 = $chval3 * 0x100 + $chval4; + } + else + { + // Little Endian + $UTF16_val2 = $chval4 * 0x100 + $chval3; + } + + // Check that this is a low surrogate + if ( ( $UTF16_val2 >= 0xDC00 ) && ( $UTF16_val2 <= 0xDFFF ) ) + { + // Low surrogate found following high surrogate + // Add both to the output + $output[] = 0x10000 + ( ( $UTF16_val - 0xD800 ) * 0x400 ) + ( $UTF16_val2 - 0xDC00 ); + + // Skip over the low surrogate + $pos += 2; + } + else + { + // Low surrogate not found after high surrogate + // Don't add either to the output + // The high surrogate is skipped and processing continued + } + + } + else + { + // Error - not enough data for low surrogate - end processing + continue 1; + } + + } + else + { + // Low surrogate of a surrogate pair + // This should not happen - it means this is a lone low surrogate + // Don't add it to the output + } + + } + + // Return the result + return $output; + + +} + +/****************************************************************************** +* End of Function: UTF16_to_unicode_array +******************************************************************************/ + + + + + + + +/****************************************************************************** +* +* Function: unicode_array_to_UTF8 +* +* Description: Converts an array of unicode character numbers to a string +* encoded by UTF-8 +* +* Parameters: unicode_array - the array containing unicode character numbers +* +* Returns: output - the UTF-8 encoded string representing the data +* +******************************************************************************/ + +function unicode_array_to_UTF8( $unicode_array ) +{ + + // Create a string to receive the UTF-8 output + $output = ""; + + // Cycle through each Unicode character number + foreach( $unicode_array as $unicode_char ) + { + // Check which range the current unicode character lies in + if ( ( $unicode_char >= 0x00 ) && ( $unicode_char <= 0x7F ) ) + { + // 1 Byte UTF-8 Unicode (7-Bit ASCII) Character + + $output .= chr($unicode_char); // Output is equal to input for 7-bit ASCII + } + else if ( ( $unicode_char >= 0x80 ) && ( $unicode_char <= 0x7FF ) ) + { + // 2 Byte UTF-8 Unicode - binary encode data as : 110xxxxx 10xxxxxx + + $output .= chr(0xC0 + ($unicode_char/0x40)); + $output .= chr(0x80 + ($unicode_char & 0x3F)); + } + else if ( ( $unicode_char >= 0x800 ) && ( $unicode_char <= 0xFFFF ) ) + { + // 3 Byte UTF-8 Unicode - binary encode data as : 1110xxxx 10xxxxxx 10xxxxxx + + $output .= chr(0xE0 + ($unicode_char/0x1000)); + $output .= chr(0x80 + (($unicode_char/0x40) & 0x3F)); + $output .= chr(0x80 + ($unicode_char & 0x3F)); + } + else if ( ( $unicode_char >= 0x10000 ) && ( $unicode_char <= 0x1FFFFF ) ) + { + // 4 Byte UTF-8 Unicode - binary encode data as : 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + + $output .= chr(0xF0 + ($unicode_char/0x40000)); + $output .= chr(0x80 + (($unicode_char/0x1000) & 0x3F)); + $output .= chr(0x80 + (($unicode_char/0x40) & 0x3F)); + $output .= chr(0x80 + ($unicode_char & 0x3F)); + } + else if ( ( $unicode_char >= 0x200000 ) && ( $unicode_char <= 0x3FFFFFF ) ) + { + // 5 Byte UTF-8 Unicode - binary encode data as : 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + + $output .= chr(0xF8 + ($unicode_char/0x1000000)); + $output .= chr(0x80 + (($unicode_char/0x40000) & 0x3F)); + $output .= chr(0x80 + (($unicode_char/0x1000) & 0x3F)); + $output .= chr(0x80 + (($unicode_char/0x40) & 0x3F)); + $output .= chr(0x80 + ($unicode_char & 0x3F)); + } + else if ( ( $unicode_char >= 0x4000000 ) && ( $unicode_char <= 0x7FFFFFFF ) ) + { + // 6 Byte UTF-8 Unicode - binary encode data as : 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + + $output .= chr(0xFC + ($unicode_char/0x40000000)); + $output .= chr(0x80 + (($unicode_char/0x1000000) & 0x3F)); + $output .= chr(0x80 + (($unicode_char/0x40000) & 0x3F)); + $output .= chr(0x80 + (($unicode_char/0x1000) & 0x3F)); + $output .= chr(0x80 + (($unicode_char/0x40) & 0x3F)); + $output .= chr(0x80 + ($unicode_char & 0x3F)); + } + else + { + // Invalid Code - do nothing + } + + } + + // Return resulting UTF-8 String + return $output; +} + +/****************************************************************************** +* End of Function: unicode_array_to_UTF8 +******************************************************************************/ + + + + + + + + + +/****************************************************************************** +* +* Function: unicode_array_to_UTF16 +* +* Description: Converts an array of unicode character numbers to a string +* encoded by UTF-16 +* +* Parameters: unicode_array - the array containing unicode character numbers +* MSB_first - True will cause processing as Big Endian UTF-16 (Motorola, MSB first) +* False will cause processing as Little Endian UTF-16 (Intel, LSB first) +* +* Returns: output - the UTF-16 encoded string representing the data +* +******************************************************************************/ + +function unicode_array_to_UTF16( $unicode_array, $MSB_first ) +{ + + // Create a string to receive the UTF-16 output + $output = ""; + + // Cycle through each Unicode character number + foreach( $unicode_array as $unicode_char ) + { + // Check which range the current unicode character lies in + if ( ( ( $unicode_char >= 0x0000 ) && ( $unicode_char <= 0xD7FF ) ) || + ( ( $unicode_char >= 0xE000 ) && ( $unicode_char <= 0xFFFF ) ) ) + { + // Normal 16 Bit Character (Not a Surrogate Pair) + + // Check what byte order should be used + if ( $MSB_first ) + { + // Big Endian + $output .= chr( $unicode_char / 0x100 ) . chr( $unicode_char % 0x100 ) ; + } + else + { + // Little Endian + $output .= chr( $unicode_char % 0x100 ) . chr( $unicode_char / 0x100 ) ; + } + + } + else if ( ( $unicode_char >= 0x10000 ) && ( $unicode_char <= 0x10FFFF ) ) + { + // Surrogate Pair required + + // Calculate Surrogates + $High_Surrogate = ( ( $unicode_char - 0x10000 ) / 0x400 ) + 0xD800; + $Low_Surrogate = ( ( $unicode_char - 0x10000 ) % 0x400 ) + 0xDC00; + + // Check what byte order should be used + if ( $MSB_first ) + { + // Big Endian + $output .= chr( $High_Surrogate / 0x100 ) . chr( $High_Surrogate % 0x100 ); + $output .= chr( $Low_Surrogate / 0x100 ) . chr( $Low_Surrogate % 0x100 ); + } + else + { + // Little Endian + $output .= chr( $High_Surrogate % 0x100 ) . chr( $High_Surrogate / 0x100 ); + $output .= chr( $Low_Surrogate % 0x100 ) . chr( $Low_Surrogate / 0x100 ); + } + } + else + { + // Invalid UTF-16 codepoint + // Unicode value should never be between 0xD800 and 0xDFFF + // Do not output this point - there is no way to encode it in UTF-16 + } + + } + + // Return resulting UTF-16 String + return $output; +} + +/****************************************************************************** +* End of Function: unicode_array_to_UTF16 +******************************************************************************/ + + + + + +/****************************************************************************** +* +* Function: xml_UTF8_clean +* +* Description: XML has specific requirements about the characters that are +* allowed, and characters that must be escaped. +* This function ensures that all characters in the given string +* are valid, and that characters such as Quotes, Greater than, +* Less than and Ampersand are properly escaped. Newlines and Tabs +* are also escaped. +* Note - Do not use this on constructed XML which includes tags, +* as it will escape the tags. It is designed to be used +* on the tag and attribute names, attribute values, and text. +* +* Parameters: utf8_text - a string containing the UTF-8 data +* +* Returns: output - the array containing the unicode character numbers +* +******************************************************************************/ + +function xml_UTF8_clean( $UTF8_text ) +{ + // Ensure that the Unicode UTF8 encoding is valid. + + $UTF8_text = UTF8_fix( $UTF8_text ); + + + // XML only allows characters in the following unicode ranges + // #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] + // Hence we need to delete any characters that dont fit this + + // Convert the UTF-8 string to an array of unicode character numbers + $unicode_array = UTF8_to_unicode_array( $UTF8_text ); + + // Create a new array to receive the valid unicode character numbers + $new_unicode_array = array( ); + + // Cycle through the unicode character numbers + foreach( $unicode_array as $unichar ) + { + // Check if the unicode character number is valid for XML + if ( ( $unichar == 0x09 ) || + ( $unichar == 0x0A ) || + ( $unichar == 0x0D ) || + ( ( $unichar >= 0x20 ) && ( $unichar <= 0xD7FF ) ) || + ( ( $unichar >= 0xE000 ) && ( $unichar <= 0xFFFD ) ) || + ( ( $unichar >= 0x10000 ) && ( $unichar <= 0x10FFFF ) ) ) + { + // Unicode character is valid for XML - add it to the valid characters array + $new_unicode_array[] = $unichar; + } + + } + + // Convert the array of valid unicode character numbers back to UTF-8 encoded text + $UTF8_text = unicode_array_to_UTF8( $new_unicode_array ); + + // Escape any special HTML characters present + $UTF8_text = htmlspecialchars ( $UTF8_text, ENT_QUOTES ); + + // Escape CR, LF and TAB characters, so that they are kept and not treated as expendable white space + $trans = array( "\x09" => " ", "\x0A" => " ", "\x0D" => " " ); + $UTF8_text = strtr( $UTF8_text, $trans ); + + // Return the resulting XML valid string + return $UTF8_text; +} + +/****************************************************************************** +* End of Function: xml_UTF8_clean +******************************************************************************/ + + + + + + + + + +/****************************************************************************** +* +* Function: xml_UTF16_clean +* +* Description: XML has specific requirements about the characters that are +* allowed, and characters that must be escaped. +* This function ensures that all characters in the given string +* are valid, and that characters such as Quotes, Greater than, +* Less than and Ampersand are properly escaped. Newlines and Tabs +* are also escaped. +* Note - Do not use this on constructed XML which includes tags, +* as it will escape the tags. It is designed to be used +* on the tag and attribute names, attribute values, and text. +* +* Parameters: utf16_text - a string containing the UTF-16 data +* MSB_first - True will cause processing as Big Endian UTF-16 (Motorola, MSB first) +* False will cause processing as Little Endian UTF-16 (Intel, LSB first) +* +* Returns: output - the array containing the unicode character numbers +* +******************************************************************************/ + +function xml_UTF16_clean( $UTF16_text, $MSB_first ) +{ + // Ensure that the Unicode UTF16 encoding is valid. + + $UTF16_text = UTF16_fix( $UTF16_text, $MSB_first ); + + + // XML only allows characters in the following unicode ranges + // #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] + // Hence we need to delete any characters that dont fit this + + // Convert the UTF-16 string to an array of unicode character numbers + $unicode_array = UTF16_to_unicode_array( $UTF16_text, $MSB_first ); + + // Create a new array to receive the valid unicode character numbers + $new_unicode_array = array( ); + + // Cycle through the unicode character numbers + foreach( $unicode_array as $unichar ) + { + // Check if the unicode character number is valid for XML + if ( ( $unichar == 0x09 ) || + ( $unichar == 0x0A ) || + ( $unichar == 0x0D ) || + ( ( $unichar >= 0x20 ) && ( $unichar <= 0xD7FF ) ) || + ( ( $unichar >= 0xE000 ) && ( $unichar <= 0xFFFD ) ) || + ( ( $unichar >= 0x10000 ) && ( $unichar <= 0x10FFFF ) ) ) + { + // Unicode character is valid for XML - add it to the valid characters array + $new_unicode_array[] = $unichar; + } + + } + + // Convert the array of valid unicode character numbers back to UTF-16 encoded text + $UTF16_text = unicode_array_to_UTF16( $new_unicode_array, $MSB_first ); + + // Escape any special HTML characters present + $UTF16_text = htmlspecialchars ( $UTF16_text, ENT_QUOTES ); + + // Escape CR, LF and TAB characters, so that they are kept and not treated as expendable white space + $trans = array( "\x09" => " ", "\x0A" => " ", "\x0D" => " " ); + $UTF16_text = strtr( $UTF16_text, $trans ); + + // Return the resulting XML valid string + return $UTF16_text; +} + +/****************************************************************************** +* End of Function: xml_UTF16_clean +******************************************************************************/ + + + + + + +/****************************************************************************** +* +* Function: HTML_UTF8_Escape +* +* Description: A HTML page can display UTF-8 data properly if it has a +* META http-equiv="Content-Type" tag with the content attribute +* including the value: "charset=utf-8". +* Otherwise the ISO-8859-1 character set is usually assumed, and +* Unicode values above 0x7F must be escaped. +* This function takes a UTF-8 encoded string and escapes the +* characters above 0x7F as well as reserved HTML characters such +* as Quotes, Greater than, Less than and Ampersand. +* +* Parameters: utf8_text - a string containing the UTF-8 data +* +* Returns: htmloutput - a string containing the HTML equivalent +* +******************************************************************************/ + +function HTML_UTF8_Escape( $UTF8_text ) +{ + + // Ensure that the Unicode UTF8 encoding is valid. + $UTF8_text = UTF8_fix( $UTF8_text ); + + // Change: changed to use smart_htmlspecialchars, so that characters which were already escaped would remain intact, as of revision 1.10 + // Escape any special HTML characters present + $UTF8_text = smart_htmlspecialchars( $UTF8_text, ENT_QUOTES ); + + // Convert the UTF-8 string to an array of unicode character numbers + $unicode_array = UTF8_to_unicode_array( $UTF8_text ); + + // Create a string to receive the escaped HTML + $htmloutput = ""; + + // Cycle through the unicode character numbers + foreach( $unicode_array as $unichar ) + { + // Check if the character needs to be escaped + if ( ( $unichar >= 0x00 ) && ( $unichar <= 0x7F ) ) + { + // Character is less than 0x7F - add it to the html as is + $htmloutput .= chr( $unichar ); + } + else + { + // Character is greater than 0x7F - escape it and add it to the html + $htmloutput .= "&#x" . dechex($unichar) . ";"; + } + } + + // Return the resulting escaped HTML + return $htmloutput; +} + +/****************************************************************************** +* End of Function: HTML_UTF8_Escape +******************************************************************************/ + + + +/****************************************************************************** +* +* Function: HTML_UTF8_UnEscape +* +* Description: Converts HTML which contains escaped decimal or hex characters +* into UTF-8 text +* +* Parameters: HTML_text - a string containing the HTML text to convert +* +* Returns: utfoutput - a string containing the UTF-8 equivalent +* +******************************************************************************/ + +function HTML_UTF8_UnEscape( $HTML_text ) +{ + preg_match_all( "/\&\#(\d+);/", $HTML_text, $matches); + preg_match_all( "/\&\#[x|X]([A|B|C|D|E|F|a|b|c|d|e|f|0-9]+);/", $HTML_text, $hexmatches); + foreach( $hexmatches[1] as $index => $match ) + { + $matches[0][] = $hexmatches[0][$index]; + $matches[1][] = hexdec( $match ); + } + + for ( $i = 0; $i < count( $matches[ 0 ] ); $i++ ) + { + $trans = array( $matches[0][$i] => unicode_array_to_UTF8( array( $matches[1][$i] ) ) ); + + $HTML_text = strtr( $HTML_text , $trans ); + } + return $HTML_text; +} + +/****************************************************************************** +* End of Function: HTML_UTF8_UnEscape +******************************************************************************/ + + + + + + +/****************************************************************************** +* +* Function: HTML_UTF16_Escape +* +* Description: A HTML page can display UTF-16 data properly if it has a +* META http-equiv="Content-Type" tag with the content attribute +* including the value: "charset=utf-16". +* Otherwise the ISO-8859-1 character set is usually assumed, and +* Unicode values above 0x7F must be escaped. +* This function takes a UTF-16 encoded string and escapes the +* characters above 0x7F as well as reserved HTML characters such +* as Quotes, Greater than, Less than and Ampersand. +* +* Parameters: utf16_text - a string containing the UTF-16 data +* MSB_first - True will cause processing as Big Endian UTF-16 (Motorola, MSB first) +* False will cause processing as Little Endian UTF-16 (Intel, LSB first) +* +* Returns: htmloutput - a string containing the HTML equivalent +* +******************************************************************************/ + +function HTML_UTF16_Escape( $UTF16_text, $MSB_first ) +{ + + // Ensure that the Unicode UTF16 encoding is valid. + $UTF16_text = UTF16_fix( $UTF16_text, $MSB_first ); + + // Change: changed to use smart_htmlspecialchars, so that characters which were already escaped would remain intact, as of revision 1.10 + // Escape any special HTML characters present + $UTF16_text = smart_htmlspecialchars( $UTF16_text ); + + // Convert the UTF-16 string to an array of unicode character numbers + $unicode_array = UTF16_to_unicode_array( $UTF16_text, $MSB_first ); + + // Create a string to receive the escaped HTML + $htmloutput = ""; + + // Cycle through the unicode character numbers + foreach( $unicode_array as $unichar ) + { + // Check if the character needs to be escaped + if ( ( $unichar >= 0x00 ) && ( $unichar <= 0x7F ) ) + { + // Character is less than 0x7F - add it to the html as is + $htmloutput .= chr( $unichar ); + } + else + { + // Character is greater than 0x7F - escape it and add it to the html + $htmloutput .= "&#x" . dechex($unichar) . ";"; + } + } + + // Return the resulting escaped HTML + return $htmloutput; +} + +/****************************************************************************** +* End of Function: HTML_UTF16_Escape +******************************************************************************/ + + +/****************************************************************************** +* +* Function: HTML_UTF16_UnEscape +* +* Description: Converts HTML which contains escaped decimal or hex characters +* into UTF-16 text +* +* Parameters: HTML_text - a string containing the HTML text to be converted +* MSB_first - True will cause processing as Big Endian UTF-16 (Motorola, MSB first) +* False will cause processing as Little Endian UTF-16 (Intel, LSB first) +* +* Returns: utfoutput - a string containing the UTF-16 equivalent +* +******************************************************************************/ + +function HTML_UTF16_UnEscape( $HTML_text, $MSB_first ) +{ + $utf8_text = HTML_UTF8_UnEscape( $HTML_text ); + + return unicode_array_to_UTF16( UTF8_to_unicode_array( $utf8_text ), $MSB_first ); +} + +/****************************************************************************** +* End of Function: HTML_UTF16_UnEscape +******************************************************************************/ + + + + +/****************************************************************************** +* +* Function: smart_HTML_Entities +* +* Description: Performs the same function as HTML_Entities, but leaves entities +* that are already escaped intact. +* +* Parameters: HTML_text - a string containing the HTML text to be escaped +* +* Returns: HTML_text_out - a string containing the escaped HTML text +* +******************************************************************************/ + +function smart_HTML_Entities( $HTML_text ) +{ + // Get a table containing the HTML entities translations + $translation_table = get_html_translation_table( HTML_ENTITIES ); + + // Change the ampersand to translate to itself, to avoid getting & + $translation_table[ chr(38) ] = '&'; + + // Perform replacements + // Regular expression says: find an ampersand, check the text after it, + // if the text after it is not one of the following, then replace the ampersand + // with & + // a) any combination of up to 4 letters (upper or lower case) with at least 2 or 3 non whitespace characters, then a semicolon + // b) a hash symbol, then between 2 and 7 digits + // c) a hash symbol, an 'x' character, then between 2 and 7 digits + // d) a hash symbol, an 'X' character, then between 2 and 7 digits + return preg_replace( "/&(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,7}|#x[0-9]{2,7}|#X[0-9]{2,7};)/","&" , strtr( $HTML_text, $translation_table ) ); +} + +/****************************************************************************** +* End of Function: smart_HTML_Entities +******************************************************************************/ + + + +/****************************************************************************** +* +* Function: smart_htmlspecialchars +* +* Description: Performs the same function as htmlspecialchars, but leaves characters +* that are already escaped intact. +* +* Parameters: HTML_text - a string containing the HTML text to be escaped +* +* Returns: HTML_text_out - a string containing the escaped HTML text +* +******************************************************************************/ + +function smart_htmlspecialchars( $HTML_text ) +{ + // Get a table containing the HTML special characters translations + $translation_table=get_html_translation_table (HTML_SPECIALCHARS); + + // Change the ampersand to translate to itself, to avoid getting & + $translation_table[ chr(38) ] = '&'; + + // Perform replacements + // Regular expression says: find an ampersand, check the text after it, + // if the text after it is not one of the following, then replace the ampersand + // with & + // a) any combination of up to 4 letters (upper or lower case) with at least 2 or 3 non whitespace characters, then a semicolon + // b) a hash symbol, then between 2 and 7 digits + // c) a hash symbol, an 'x' character, then between 2 and 7 digits + // d) a hash symbol, an 'X' character, then between 2 and 7 digits + return preg_replace( "/&(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,7}|#x[0-9]{2,7}|#X[0-9]{2,7};)/","&" , strtr( $HTML_text, $translation_table ) ); +} + +/****************************************************************************** +* End of Function: smart_htmlspecialchars +******************************************************************************/ + + +?> \ No newline at end of file diff --git a/includes/jpeg_metadata_tk/Write_File_Info.php b/includes/jpeg_metadata_tk/Write_File_Info.php new file mode 100644 index 0000000..efa402a --- /dev/null +++ b/includes/jpeg_metadata_tk/Write_File_Info.php @@ -0,0 +1,196 @@ + + + + + + + + + + Writing Photoshop File Info Metadata + + + + +

Powered by: PHP JPEG Metadata Toolkit version , Copyright (C) 2004 Evan Hunter

+
+
+ + $var_val ) + { + $new_ps_file_info_array[ $var_key ] = stripslashes( $var_val ); + } + + // Keywords should be an array - explode it on newline boundarys + $new_ps_file_info_array[ 'keywords' ] = explode( "\n", trim( $new_ps_file_info_array[ 'keywords' ] ) ); + + // Supplemental Categories should be an array - explode it on newline boundarys + $new_ps_file_info_array[ 'supplementalcategories' ] = explode( "\n", trim( $new_ps_file_info_array[ 'supplementalcategories' ] ) ); + + // Make the filename easier to access + $filename = $new_ps_file_info_array[ 'filename' ]; + + // Protect against hackers editing other files + $path_parts = pathinfo( $filename ); + if ( strcasecmp( $path_parts["extension"], "jpg" ) != 0 ) + { + echo "Incorrect File Type - JPEG Only\n"; + exit( ); + } + // Change: removed limitation on file being in current directory - as of version 1.11 + + // Retrieve the header information + $jpeg_header_data = get_jpeg_header_data( $filename ); + + // Retreive the EXIF, XMP and Photoshop IRB information from + // the existing file, so that it can be updated + $Exif_array = get_EXIF_JPEG( $filename ); + $XMP_array = read_XMP_array_from_text( get_XMP_text( $jpeg_header_data ) ); + $IRB_array = get_Photoshop_IRB( $jpeg_header_data ); + + // Update the JPEG header information with the new Photoshop File Info + $jpeg_header_data = put_photoshop_file_info( $jpeg_header_data, $new_ps_file_info_array, $Exif_array, $XMP_array, $IRB_array ); + + // Check if the Update worked + if ( $jpeg_header_data == FALSE ) + { + // Update of file info didn't work - output error message + echo "Error - Failure update Photoshop File Info : $filename
\n"; + + // Output HTML with the form and data which was + // sent, to allow the user to fix it + + $outputfilename = $filename; + require_once "Edit_File_info.php"; + echo "\n"; + echo "\n"; + + // Abort processing + exit( ); + } + + // Attempt to write the new JPEG file + if ( FALSE == put_jpeg_header_data( $filename, $filename, $jpeg_header_data ) ) + { + // Writing of the new file didn't work - output error message + echo "Error - Failure to write new JPEG : $filename
\n"; + + // Output HTML with the form and data which was + // sent, to allow the user to fix it + + $outputfilename = $filename; + require_once "Edit_File_info.php"; + echo "\n"; + echo "\n"; + + // Abort processing + exit( ); + } + + + // Writing of new JPEG succeeded + + // Output information about new file + + echo "

DONE! - $filename updated

\n"; + echo "

View Full Metatdata Information

\n"; + echo "

Re-Edit Photoshop File Info

\n"; + echo "

\n"; + echo "

Below is the updated image, you can save it and look at the changed metadata in your favorite image editor

\n"; + echo "

\n"; + + + ?> + +
+
+
+
+ + +

Powered by: PHP JPEG Metadata Toolkit version , Copyright (C) 2004 Evan Hunter

+ +
+
+ + + + diff --git a/includes/jpeg_metadata_tk/XML.php b/includes/jpeg_metadata_tk/XML.php new file mode 100644 index 0000000..2ce4668 --- /dev/null +++ b/includes/jpeg_metadata_tk/XML.php @@ -0,0 +1,396 @@ + 1.10 : Changed read_xml_array_from_text to fix problem that +* caused the whitespace (especially newlines) to be +* destroyed when converting xml text to an xml array +* +* URL: http://electronics.ozhiker.com +* +* License: This file is part of the PHP JPEG Metadata Toolkit. +* +* The PHP JPEG Metadata Toolkit is free software; you can +* redistribute it and/or modify it under the terms of the +* GNU General Public License as published by the Free Software +* Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* The PHP JPEG Metadata Toolkit is distributed in the hope +* that it will be useful, but WITHOUT ANY WARRANTY; without +* even the implied warranty of MERCHANTABILITY or FITNESS +* FOR A PARTICULAR PURPOSE. See the GNU General Public License +* for more details. +* +* You should have received a copy of the GNU General Public +* License along with the PHP JPEG Metadata Toolkit; if not, +* write to the Free Software Foundation, Inc., 59 Temple +* Place, Suite 330, Boston, MA 02111-1307 USA +* +* If you require a different license for commercial or other +* purposes, please contact the author: evan@ozhiker.com +* +******************************************************************************/ + +include_once 'Unicode.php'; // Unicode is required as XML is always Unicode encoded + + +/****************************************************************************** +* +* Function: read_xml_array_from_text +* +* Description: Parses a string containing XML, and returns the resulting +* tree structure array, which contains all the XML information. +* Note: White space and comments in the XML are ignored +* Note: All text information contained in the tree structure +* is encoded as Unicode UTF-8. Hence text will appear as +* normal ASCII except where there is an extended character. +* +* Parameters: xmltext - a string containing the XML to be parsed +* +* Returns: output - the tree structure array containing the XML information +* FALSE - if an error occured +* +******************************************************************************/ + +function read_xml_array_from_text( $xmltext ) +{ + // Check if there actually is any text to parse + if ( trim( $xmltext ) == "" ) + { + return FALSE; + } + + // Create an instance of a xml parser to parse the XML text + $xml_parser = xml_parser_create( "UTF-8" ); + + + // Change: Fixed problem that caused the whitespace (especially newlines) to be destroyed when converting xml text to an xml array, as of revision 1.10 + + // We would like to remove unneccessary white space, but this will also + // remove things like newlines ( ) in the XML values, so white space + // will have to be removed later + if ( xml_parser_set_option($xml_parser,XML_OPTION_SKIP_WHITE,0) == FALSE ) + { + // Error setting case folding - destroy the parser and return + xml_parser_free($xml_parser); + return FALSE; + } + + // to use XML code correctly we have to turn case folding + // (uppercasing) off. XML is case sensitive and upper + // casing is in reality XML standards violation + if ( xml_parser_set_option($xml_parser,XML_OPTION_CASE_FOLDING,0) == FALSE ) + { + // Error setting case folding - destroy the parser and return + xml_parser_free($xml_parser); + return FALSE; + } + + // Parse the XML text into a array structure + if ( xml_parse_into_struct($xml_parser, $xmltext, $vals, $index) == 0 ) + { + // Error Parsing XML - destroy the parser and return + xml_parser_free($xml_parser); + return FALSE; + } + + // Destroy the xml parser + xml_parser_free($xml_parser); + + + // Change: Fixed problem that caused the whitespace (especially newlines) to be destroyed when converting xml text to an xml array, as of revision 1.10 + + // Since the xml was processed with whitespace enabled, it will have many values which are + // only whitespace. These need to be removed to make a sensible array. + + $newvals = array( ); + + // Cycle through each of the items + foreach( $vals as $valno => $val ) + { + // If the item has a whitespace only value, remove it + if ( ( array_key_exists( 'value', $val ) ) && (trim( $val[ 'value' ] ) == "" ) ) + { + unset( $val[ 'value' ] ); + } + // If the item has a value (which will be non blank now) or is of type other than cdata, add it to the new array + if ( ( $val[ 'type' ] != 'cdata' ) || ( array_key_exists( 'value', $val ) ) ) + { + $newvals[] = $val; + } + + } + + // The xml_parse_into_struct function returns a flat version + // of the XML data, where each tag has a level number attached. + // This is very difficult to work with, so it needs to be + // converted to a tree structure before being returned + return xml_get_children($newvals, $i=0); + +} + +/****************************************************************************** +* End of Function: read_xml_array_from_text +******************************************************************************/ + + + + + +/****************************************************************************** +* +* Function: write_xml_array_to_text +* +* Description: Takes a tree structure array (in the same format as returned +* by read_xml_array_from_text, and constructs a string containing +* the equivalent XML. This function is recursive, and produces +* XML which has correct indents. +* Note: All text information contained in the tree structure +* can be either 7-bit ASCII or encoded as Unicode UTF-8, +* since UTF-8 passes 7-bit ASCII text unchanged. +* +* Parameters: xmlarray - the tree structure array containing the information to +* be converted to XML +* indentlevel - the indent level of the top level tags (usually zero) +* +* Returns: output - the string containing the equivalent XML +* FALSE - if an error occured +* +******************************************************************************/ + +function write_xml_array_to_text( $xmlarray, $indentlevel ) +{ + // Create a string to receive the XML + $output_xml_text = ""; + + + // Cycle through each xml element at this level + foreach ($xmlarray as $xml_elem) + { + + // Add the indent, then the cleaned tag name to the output + $output_xml_text .= str_repeat ( " ", $indentlevel ) . "<" . xml_UTF8_clean( $xml_elem['tag'] ); + + // Check if there are any attributes for this tag + if (array_key_exists('attributes',$xml_elem)) + { + // There are attributes + // Cycle through each attribute for this tag + foreach ($xml_elem['attributes'] as $xml_attr_name => $xml_attr_val) + { + // Add the cleaned attribute name, and cleaned attribute value to the output + $output_xml_text .= " ". xml_UTF8_clean( $xml_attr_name ) ." ='" . xml_UTF8_clean( $xml_attr_val ) ."'"; + } + } + + // Add the 'greater-than' to close this tag to the output + $output_xml_text .= ">"; + + // Check if this element has any text inside it. + if (array_key_exists('value',$xml_elem) ) + { + // There is text for this element - clean it and add it to the output + $output_xml_text .= xml_UTF8_clean( $xml_elem['value'] ); + } + + // Check if there are any lower levels contained by this element + if (array_key_exists('children',$xml_elem) ) + { + // There are sub-elements for this element + + // Add a newline to the output, so the sub-elements start on a fresh line + $output_xml_text .= "\n"; + + // Recursively call this function to output the sub-elements, and add the result to the output + $output_xml_text .= write_xml_array_to_text( $xml_elem['children'], $indentlevel + 1 ); + + // Add an indent to the output for the closing tag, since we are on a new line due to the sub-elements + $output_xml_text .= str_repeat ( " ", $indentlevel ); + } + + // Add the cleaned closing tag to the output + $output_xml_text .= "\n"; + } + + // Return the XML text + return $output_xml_text; +} + +/****************************************************************************** +* End of Function: write_xml_array_to_text +******************************************************************************/ + + + + + + + + + + + + + + + + + + + + + + + +/****************************************************************************** +* +* INTERNAL FUNCTIONS +* +******************************************************************************/ + + + + + + +/****************************************************************************** +* +* Internal Function: xml_get_children +* +* Description: Used by the read_xml_array_from_text function. +* This function recursively converts the values retrieved from +* the xml_parse_into_struct function into a tree structure array, +* which is much more useful and easier to use. +* +* Parameters: input_xml_array - the flat array of XML elements retrieved +* from xml_parse_into_struct +* $item_num - the number of the element at which the conversion +* should start (usually zero when called from another +* function, this is used for recursion) +* +* Returns: children - the tree structure array containing XML elements +* FALSE - if an error occured +* +******************************************************************************/ + +function xml_get_children( &$input_xml_array, &$item_num ) +{ + + // Make an array to receive the output XML tree structure + $children = array(); + + + // Cycle through all the elements of the input XML array + while ( $item_num < count( $input_xml_array ) ) + { + // Retrieve the current array element + $v = &$input_xml_array[ $item_num++ ]; + + // Check what type of XML array element this is, and process accordingly + + switch ( $v['type'] ) + { + case 'cdata': // This is a non parsed Character Data tag + case 'complete': // This is a pair of XML matching tags possibly with text (but no tags) inside + $children[] = xml_get_child( $v ); + break; + + case 'open': // This is a single opening tag + // Recursively get the children for this opening tag + $children[] = xml_get_child( $v, xml_get_children( $input_xml_array, $item_num ) ); + break; // This is a single opening tag + + case 'close': // This is a single closing tag + break 2; // leave "while" loop (and the function) + } + } + + // Return the results + return $children; +} + +/****************************************************************************** +* End of Function: xml_get_children +******************************************************************************/ + + +/****************************************************************************** +* +* Internal Function: xml_get_child +* +* Description: Used by the xml_get_children function. +* Takes an element from an array provided by xml_parse_into_struct +* and returns an element for a tree structure array +* +* Parameters: input_xml_item - the item from the array provided by xml_parse_into_struct +* children - an array of sub-elements to be added to the tree +* structure array. Null or missing value indicate no +* sub-elements are to be added. +* +* Returns: child - the element for a tree structure array +* FALSE - if an error occured +* +******************************************************************************/ + +function xml_get_child( &$input_xml_item, $children = NULL ) +{ + // Create an array to receive the child structure + $child = array(); + + // If the input item has the 'tag' element set, copy it to the child + if ( isset( $input_xml_item['tag'] ) ) + { + $child['tag'] = $input_xml_item['tag'] ; + } + + // If the input item has the 'value' element set, copy it to the child + if ( isset( $input_xml_item['value'] ) ) + { + $child['value'] = $input_xml_item['value'] ; + } + + // If the input item has the 'attributes' element set, copy it to the child + if ( isset( $input_xml_item['attributes'] ) ) + { + $child['attributes'] = $input_xml_item['attributes']; + } + + // If children have been specified, add them to the child + if ( is_array( $children ) ) + { + $child['children'] = $children; + } + + // Return the child structure + return $child; +} + +/****************************************************************************** +* End of Function: xml_get_children +******************************************************************************/ + + + + + + + + + + + + +?> \ No newline at end of file diff --git a/includes/jpeg_metadata_tk/XMP.php b/includes/jpeg_metadata_tk/XMP.php new file mode 100644 index 0000000..0948be1 --- /dev/null +++ b/includes/jpeg_metadata_tk/XMP.php @@ -0,0 +1,1063 @@ + 1.04 : changed put_IPTC to fix a bug preventing the correct +* insertion of a XMP block where none existed previously +* +* 1.04 -> 1.10 : changed put_XMP_text to fix some array indexes which were missing qoutes +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* +* License: This file is part of the PHP JPEG Metadata Toolkit. +* +* The PHP JPEG Metadata Toolkit is free software; you can +* redistribute it and/or modify it under the terms of the +* GNU General Public License as published by the Free Software +* Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* The PHP JPEG Metadata Toolkit is distributed in the hope +* that it will be useful, but WITHOUT ANY WARRANTY; without +* even the implied warranty of MERCHANTABILITY or FITNESS +* FOR A PARTICULAR PURPOSE. See the GNU General Public License +* for more details. +* +* You should have received a copy of the GNU General Public +* License along with the PHP JPEG Metadata Toolkit; if not, +* write to the Free Software Foundation, Inc., 59 Temple +* Place, Suite 330, Boston, MA 02111-1307 USA +* +* If you require a different license for commercial or other +* purposes, please contact the author: evan@ozhiker.com +* +******************************************************************************/ + +include_once 'XML.php'; + + + + + +/****************************************************************************** +* +* Function: get_XMP_text +* +* Description: Retrieves the Extensible Metadata Platform (XMP) information +* from an App1 JPEG segment and returns the raw XML text as a +* string. This includes the Resource Description Framework (RDF) +* information and may include Dublin Core Metadata Initiative (DCMI) +* information. Uses information supplied by the get_jpeg_header_data +* function +* +* Parameters: jpeg_header_data - a JPEG header data array in the same format +* as from get_jpeg_header_data +* +* Returns: xmp_data - the string of raw XML text +* FALSE - if an APP 1 XMP segment could not be found, +* or if an error occured +* +******************************************************************************/ + +function get_XMP_text( $jpeg_header_data ) +{ + //Cycle through the header segments + for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) + { + // If we find an APP1 header, + if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP1" ) == 0 ) + { + // And if it has the Adobe XMP/RDF label (http://ns.adobe.com/xap/1.0/\x00) , + if( strncmp ( $jpeg_header_data[$i]['SegData'], "http://ns.adobe.com/xap/1.0/\x00", 29) == 0 ) + { + // Found a XMP/RDF block + // Return the XMP text + $xmp_data = substr ( $jpeg_header_data[$i]['SegData'], 29 ); + + return $xmp_data; + } + } + } + return FALSE; +} + +/****************************************************************************** +* End of Function: get_XMP_text +******************************************************************************/ + + + + + + +/****************************************************************************** +* +* Function: put_XMP_text +* +* Description: Adds or modifies the Extensible Metadata Platform (XMP) information +* in an App1 JPEG segment. If a XMP segment already exists, it is +* replaced, otherwise a new one is inserted, using the supplied data. +* Uses information supplied by the get_jpeg_header_data function +* +* Parameters: jpeg_header_data - a JPEG header data array in the same format +* as from get_jpeg_header_data +* newXMP - a string containing the XMP text to be stored in the XMP +* segment. Should be constructed using the write_XMP_array_to_text +* function +* +* Returns: jpeg_header_data - the JPEG header data array with the +* XMP segment added. +* FALSE - if an error occured +* +******************************************************************************/ + +function put_XMP_text( $jpeg_header_data, $newXMP ) +{ + //Cycle through the header segments + for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) + { + // If we find an APP1 header, + if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP1" ) == 0 ) + { + // And if it has the Adobe XMP/RDF label (http://ns.adobe.com/xap/1.0/\x00) , + if( strncmp ( $jpeg_header_data[$i]['SegData'], "http://ns.adobe.com/xap/1.0/\x00", 29) == 0 ) + { + // Found a preexisting XMP/RDF block - Replace it with the new one and return. + $jpeg_header_data[$i]['SegData'] = "http://ns.adobe.com/xap/1.0/\x00" . $newXMP; + return $jpeg_header_data; + } + } + } + + // No pre-existing XMP/RDF found - insert a new one after any pre-existing APP0 or APP1 blocks + // Change: changed to initialise $i properly as of revision 1.04 + $i = 0; + // Loop until a block is found that isn't an APP0 or APP1 + while ( ( $jpeg_header_data[$i]['SegName'] == "APP0" ) || ( $jpeg_header_data[$i]['SegName'] == "APP1" ) ) + { + $i++; + } + + + + // Insert a new XMP/RDF APP1 segment at the specified point. + // Change: changed to properly construct array element as of revision 1.04 - requires two array statements not one, requires insertion at $i, not $i - 1 + array_splice($jpeg_header_data, $i, 0, array( array( "SegType" => 0xE1, + "SegName" => "APP1", + "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE1 ], + "SegData" => "http://ns.adobe.com/xap/1.0/\x00" . $newXMP ) ) ); + + // Return the headers with the new segment inserted + return $jpeg_header_data; +} + +/****************************************************************************** +* End of Function: put_XMP_text +******************************************************************************/ + + + + + + +/****************************************************************************** +* +* Function: read_XMP_array_from_text +* +* Description: An alias for read_xml_array_from_text. +* Parses a string containing XMP data (XML), and returns the resulting +* tree structure array, which contains all the XMP (XML) information. +* Note: White space and comments in the XMP data (XML) are ignored +* Note: All text information contained in the tree structure +* is encoded as Unicode UTF-8. Hence text will appear as +* normal ASCII except where there is an extended character. +* +* Parameters: xmptext - a string containing the XMP data (XML) to be parsed +* +* Returns: output - the tree structure array containing the XMP (XML) information +* FALSE - if an error occured +* +******************************************************************************/ + +function read_XMP_array_from_text( $xmptext ) +{ + return read_xml_array_from_text( $xmptext ); +} + +/****************************************************************************** +* End of Function: read_XMP_array_from_text +******************************************************************************/ + + + + + +/****************************************************************************** +* +* Function: write_XMP_array_to_text +* +* Description: Takes a tree structure array containing XMP (in the same format +* as returned by read_XMP_array_from_text, and constructs a string +* containing the equivalent XMP, including the XMP Packet header +* and trailer. Produces XMP text which has correct indents, encoded +* using UTF-8. +* Note: All text information contained in the tree structure +* can be either 7-bit ASCII or encoded as Unicode UTF-8, +* since UTF-8 passes 7-bit ASCII text unchanged. +* +* Parameters: xmparray - the tree structure array containing the information to +* be converted to XMP text +* +* Returns: output_XMP_text - the string containing the equivalent XMP text +* +******************************************************************************/ + +function write_XMP_array_to_text( $xmparray ) +{ + // Add the XMP packet header + // The sequence 0xEFBBBF is the UTF-8 encoded version of the Unicode “zero + // width non-breaking space character” (U+FEFF), which is used for detecting + // whether UTF-16 or UTF-8 is being used. + $output_XMP_text = "\n"; + + // Photoshop Seems to add this, but there doesn't appear to be + // any information on what it means + // TODO : XMP, Find out what the adobe-xap-filters tag means + $output_XMP_text .= "\n"; + + // Add the XML text + $output_XMP_text .= write_xml_array_to_text( $xmparray, 0 ); + + + // The XMP standard recommends adding 2-4k of white space at the + // end for in place editing, so we will add it to the XML now + $output_XMP_text .= str_repeat(" \n", 30); + + // Add the XMP packet trailer + $output_XMP_text .= ""; + + // Return the resulting XMP text + return $output_XMP_text; +} + +/****************************************************************************** +* End of Function: write_XMP_array_to_text +******************************************************************************/ + + + + + + +/****************************************************************************** +* +* Function: Interpret_XMP_to_HTML +* +* Description: Generates html showing the information contained in an Extensible +* Metadata Platform (XMP) tree structure array, as retrieved +* with read_XMP_array_from_text +* +* Parameters: XMP_array - a XMP tree structure array as from read_XMP_array_from_text +* +* Returns: output - the HTML string +* +******************************************************************************/ + +function Interpret_XMP_to_HTML( $XMP_array ) +{ + // Create a string to receive the output html + $output =""; + + // Check if the XMP tree structure array is valid + if ( $XMP_array !== FALSE ) + { + // Check if there is a rdf:RDF tag at either the first or second level + if ( ( $XMP_array[0]['tag'] == "x:xapmeta" ) && ( $XMP_array[0]['children'][0]['tag'] == "rdf:RDF" ) ) + { + // RDF found at second level - Save it's position + $RDF_Contents = &$XMP_array[0]['children'][0]['children']; + } + else if ( ( $XMP_array[0]['tag'] == "x:xmpmeta" ) && ( $XMP_array[0]['children'][0]['tag'] == "rdf:RDF" ) ) + { + // RDF found at second level - Save it's position + $RDF_Contents = &$XMP_array[0]['children'][0]['children']; + } + else if ( $XMP_array[0]['tag'] == "rdf:RDF" ) + { + // RDF found at first level - Save it's position + $RDF_Contents = &$XMP_array[0]['children']; + } + else + { + // RDF section not found - abort + return ""; + } + + // Add heading to html output + $output .= "

Contains Extensible Metadata Platform (XMP) / Resource Description Framework (RDF) Information

\n"; + + // Cycle through each of the items in the RDF tree array, and process them + foreach ($RDF_Contents as $RDF_Item) + { + // Check if the item is a rdf:Description tag - these are the only ones that can be processed + + if ( ( $RDF_Item['tag'] == "rdf:Description" ) && ( array_key_exists( 'children', $RDF_Item ) ) ) + { + // Item is a rdf:Description tag. + + // Cycle through each of the attributes for this tag, looking + // for a xmlns: attribute, which tells us what Namespace the + // sub-items will be in. + foreach( $RDF_Item['attributes'] as $key => $val ) + { + // Check for the xmlns: namespace attribute + if ( substr( $key,0,6) == "xmlns:" ) + { + // Found a xmlns: attribute + // Extract the namespace string + // Add heading to the HTML according to which Namespace the RDF items have + switch ( substr( $key,6) ) + { + case "photoshop": + $output .= "

Photoshop RDF Segment

\n"; + break; + case "xapBJ": + $output .= "

Basic Job Ticket RDF Segment

\n"; + break; + case "xapMM": + $output .= "

Media Management RDF Segment

\n"; + break; + case "xapRights": + $output .= "

Rights Management RDF Segment

\n"; + break; + case "dc": + $output .= "

Dublin Core Metadata Initiative RDF Segment

\n"; + break; + case "xmp": + case "xap": + $output .= "

XMP Basic Segment

\n"; + break; + case "xmpTPg": + $output .= "

XMP Paged-Text Segment

\n"; + break; + case "xmpTPg": + $output .= "

Adobe PDF Segment

\n"; + break; + case "tiff": + $output .= "

XMP - embedded TIFF Segment

\n"; + break; + case "exif": + $output .= "

XMP - embedded EXIF Segment

\n"; + break; + case "xapGImg": // Sub Category - Do nothing + break; + case "stDim": // Sub Category - Do nothing + break; + case "stEvt": // Sub Category - Do nothing + break; + case "stRef": // Sub Category - Do nothing + break; + case "stVer": // Sub Category - Do nothing + break; + case "stJob": // Sub Category - Do nothing + break; + + default: + $output .= "

Unknown RDF Segment '" . substr( $key,6) . "'

\n"; + break; + } + + + } + + } + + // Add the start of the table to the HTML output + $output .= "\n\n"; + + + // Check if this element has sub-items + + if ( array_key_exists( 'children', $RDF_Item ) ) + { + + // Cycle through each of the sub-items + foreach( $RDF_Item['children'] as $child_item ) + { + // Get an interpretation of the sub-item's caption and value + list($tag_caption, $value_str) = Interpret_RDF_Item( $child_item ); + + // Escape the text of the caption for html + $tag_caption = HTML_UTF8_Escape( $tag_caption ); + // Escape the text of the value for html and turn newlines to
+ $value_str = nl2br( HTML_UTF8_Escape( $value_str ) ); + + // Check if the value is empty - if it is, put a no-break-space in + // to ensure the table cell gets drawn + if ( $value_str == "" ) + { + $value_str = " "; + } + // Add the table row to the output html + $output .= "\n"; + } + } + + // Add the end of the table to the html + $output .= "\n
" . $tag_caption . ":" . $value_str . "
\n"; + + + } + else + { + // Don't know how to process tags other than rdf:Description - do nothing + } + } + + + + } + // Return the resulting HTML + return $output; +} + +/****************************************************************************** +* End of Function: Interpret_XMP_to_HTML +******************************************************************************/ + + + + + + + + + + + + + + + + + + +/****************************************************************************** +* +* INTERNAL FUNCTIONS +* +******************************************************************************/ + + + + + + + + + + + + +/****************************************************************************** +* +* Internal Function: Interpret_RDF_Item +* +* Description: Used by Interpret_XMP_to_HTML +* Used by get_RDF_field_html_value +* Used by interpret_RDF_collection +* Generates a caption and text representation of the value of a +* particular RDF item. +* +* Parameters: Item - The RDF item to evaluate +* +* Returns: tag_caption - the caption of the tag +* value_str - the text representation of the value +* +******************************************************************************/ + +function Interpret_RDF_Item( $Item ) +{ + + // TODO: Many RDF items have not been tested - only photoshop 7.0 and CS items + + // Create a string to receive the HTML output + $value_str = ""; + + // Check if the item has is in the lookup table of tag captions + if ( array_key_exists( $Item['tag'], $GLOBALS[ 'XMP_tag_captions' ] ) ) + { + // Item is in list of captions, get the caption + $tag_caption = $GLOBALS[ 'XMP_tag_captions' ][ $Item['tag'] ]; + } + else + { + // Item has no caption - make one + $tag_caption = "Unknown field " . $Item['tag']; + } + + + // Process specially the item according to it's tag + switch ( $Item['tag'] ) + { + + case "photoshop:DateCreated": // This is in year month day order + // Extract the year,month and day + list( $year, $month, $day ) = sscanf( $Item['value'], "%d-%d-%d" ); + // Make a new date string with Day, Month, Year + $value_str = "$day/$month/$year"; + break; + + default : + $value_str = get_RDF_field_html_value( $Item ); + break; + } + + + + + // Return the captiona and value + return array($tag_caption, $value_str); +} + + +/****************************************************************************** +* End of Function: Interpret_RDF_Item +******************************************************************************/ + + + + + +/****************************************************************************** +* +* Internal Function: get_RDF_field_html_value +* +* Description: Attempts to build a text representation of the value of an RDF +* item. This includes handling any collections or sub-resources. +* +* Parameters: rdf_item - The RDF item to evaluate +* +* Returns: output_str - the text representation of the field value +* +******************************************************************************/ + +function get_RDF_field_html_value( $rdf_item ) +{ + // Create a string to receive the output text + $output_str = ""; + + // Check if the item has a value + if ( array_key_exists( 'value', $rdf_item ) ) + { + // The item does have a value - add it to the text + $output_str .= $rdf_item['value']; + } + + // Check if the item has any attributes + if ( array_key_exists( 'attributes', $rdf_item ) ) + { + // Cycle through each of the attributes + foreach( $rdf_item['attributes'] as $key => $val ) + { + // Check if this attribute is rdf:parseType = 'Resource' i.e. a sub-resource indicator + if ( ( $key == "rdf:parseType" ) && ( $val == "Resource" ) ) + { + // This item has a attribute indicating sub-resources + // Check that the item has sub items + if ( array_key_exists( 'children', $rdf_item ) ) + { + // The item does have sub-items, + // Cycle through each, Interpreting them and adding the result to the output text + foreach( $rdf_item['children'] as $child ) + { + list($tag_caption, $value_str) = Interpret_RDF_Item( $child ); + $output_str .= "$tag_caption = $value_str\n"; + } + // The output text will have an extra \n on it - remove it + $output_str = rtrim( $output_str ); + } + } + } + } + // If the item did not have sub-resources, it may still have sub-items - check for this + else if ( array_key_exists( 'children', $rdf_item ) ) + { + // Non-resource Sub-items found, Cycle through each + foreach( $rdf_item['children'] as $child_item ) + { + // Check if this sub-item has a tag + if ( array_key_exists( 'tag', $child_item ) ) + { + // Sub item has a tag, Process it according to the tag + switch ( $child_item[ 'tag' ] ) + { + // Collections + case "rdf:Alt": + $output_str .= "List of Alternates:\n"; + $output_str .= interpret_RDF_collection( $child_item ); + break; + + case "rdf:Bag": + $output_str .= "Unordered List:\n"; + $output_str .= interpret_RDF_collection( $child_item ); + break; + + case "rdf:Seq": + $output_str .= "Ordered List:\n"; + $output_str .= interpret_RDF_collection( $child_item ); + break; + + // Sub-Resource + case "rdf:Description": + // Check that the item has sub items + if ( array_key_exists( 'children', $child_item ) ) + { + // The item does have sub-items, + // Cycle through each, Interpreting them and adding the result to the output text + foreach( $child_item['children'] as $child ) + { + list($tag_caption, $value_str) = Interpret_RDF_Item( $child ); + $output_str .= "$tag_caption = $value_str\n"; + } + // The output text will have an extra \n on it - remove it + $output_str = rtrim( $output_str ); + } + break; + + // Other + default: + $output_str .= "Unknown Sub Item type:". $child_item[ 'tag' ]. "\n"; + break; + } + } // sub-item Has no tags, look for a value + else if ( array_key_exists( 'value', $child_item ) ) + { + $output_str .= $rdf_item['value'] . "\n"; + } + else + { + // no info - do nothing + } + + } + } + + // return the resulting value string + return $output_str; +} + +/****************************************************************************** +* End of Function: get_RDF_field_html_value +******************************************************************************/ + + + + + + + + +/****************************************************************************** +* +* Internal Function: interpret_RDF_collection +* +* Description: Attempts to build a text representation of the value of an RDF +* collection item. This includes handling any sub-collections or +* sub-resources. +* +* Parameters: rdf_item - The RDF collection item to evaluate +* +* Returns: output_str - the text representation of the collection value +* +******************************************************************************/ + +function interpret_RDF_collection( $item ) +{ + // Create a string to receive the output + $output_str = ""; + + // Check if the collection item has sub-items + if ( array_key_exists( 'children', $item ) ) + { + + // Cycle through each of the sub-items + foreach( $item['children'] as $list_item ) + { + // Check that the sub item has a tag, and don't process it if it doesn't + if ( ! array_key_exists( 'tag', $list_item ) ) + { + continue 1; + } + + // Check that the sub-item tag is either rdf:li or rdf:_1 .... + // This signifies it is a list item of the collection + if ( ( $list_item['tag'] == "rdf:li" ) || + ( preg_match ( "rdf:_\d+", $list_item['tag'] ) == 1 ) ) + { + // A List item has been found + // Check if there are sub-resources, + // starting by checking if there are attributes + if ( array_key_exists( 'attributes', $list_item ) ) + { + // Cycle through each of the attributes + foreach( $list_item['attributes'] as $key => $val ) + { + // Check if this attribute is rdf:parseType = 'Resource' i.e. a sub-resource indicator + if ( ( $key == "rdf:parseType" ) && ( $val == "Resource" ) ) + { + // This item has a attribute indicating sub-resources + // Check that the item has sub items + if ( array_key_exists( 'children', $list_item ) ) + { + // The item does have sub-items, + // Cycle through each, Interpreting them and adding the result to the output text + foreach( $list_item['children'] as $child ) + { + list($tag_caption, $value_str) = Interpret_RDF_Item( $child ); + $output_str .= "$tag_caption = $value_str\n"; + } + // The output text will have an extra \n on it - remove it + $output_str = rtrim( $output_str ); + } + } + } + } + + // Check if the list item has a value + if ( array_key_exists( 'value', $list_item ) ) + { + // Value found, add it to the output + $output_str .= get_RDF_field_html_value( $list_item ) . "\n"; + } + + } + } + // The list of sub-items formed will have a trailing \n, remove it. + $output_str = rtrim( $output_str ); + + } + else + { + // No sub-items in collection - can't do anything + } + + // Return the output value + return $output_str; + +} + +/****************************************************************************** +* End of Function: interpret_RDF_collection +******************************************************************************/ + + + + + + + + + + + + + + + +/****************************************************************************** +* Global Variable: XMP_tag_captions +* +* Contents: The Captions of the known XMP fields, indexed by their field name +* +******************************************************************************/ + +$GLOBALS[ 'XMP_tag_captions' ] = array ( + +"dc:contributor" => "Other Contributor(s)", +"dc:coverage" => "Coverage (scope)", +"dc:creator" => "Creator(s) (Authors)", +"dc:date" => "Date", +"dc:description" => "Description (Caption)", +"dc:format" => "MIME Data Format", +"dc:identifier" => "Unique Resource Identifer", +"dc:language" => "Language(s)", +"dc:publisher" => "Publisher(s)", +"dc:relation" => "Relations to other documents", +"dc:rights" => "Rights Statement", +"dc:source" => "Source (from which this Resource is derived)", +"dc:subject" => "Subject and Keywords", +"dc:title" => "Title", +"dc:type" => "Resource Type", + +"xmp:Advisory" => "Externally Editied Properties", +"xmp:BaseURL" => "Base URL for relative URL's", +"xmp:CreateDate" => "Original Creation Date", +"xmp:CreatorTool" => "Creator Tool", +"xmp:Identifier" => "Identifier(s)", +"xmp:MetadataDate" => "Metadata Last Modify Date", +"xmp:ModifyDate" => "Resource Last Modify Date", +"xmp:Nickname" => "Nickname", +"xmp:Thumbnails" => "Thumbnails", + +"xmpidq:Scheme" => "Identification Scheme", + +// These are not in spec but Photoshop CS seems to use them +"xap:Advisory" => "Externally Editied Properties", +"xap:BaseURL" => "Base URL for relative URL's", +"xap:CreateDate" => "Original Creation Date", +"xap:CreatorTool" => "Creator Tool", +"xap:Identifier" => "Identifier(s)", +"xap:MetadataDate" => "Metadata Last Modify Date", +"xap:ModifyDate" => "Resource Last Modify Date", +"xap:Nickname" => "Nickname", +"xap:Thumbnails" => "Thumbnails", +"xapidq:Scheme" => "Identification Scheme", + + +"xapRights:Certificate" => "Certificate", +"xapRights:Copyright" => "Copyright", +"xapRights:Marked" => "Marked", +"xapRights:Owner" => "Owner", +"xapRights:UsageTerms" => "Legal Terms of Usage", +"xapRights:WebStatement" => "Web Page describing rights statement (Owner URL)", + +"xapMM:ContainedResources" => "Contained Resources", +"xapMM:ContributorResources" => "Contributor Resources", +"xapMM:DerivedFrom" => "Derived From", +"xapMM:DocumentID" => "Document ID", +"xapMM:History" => "History", +"xapMM:LastURL" => "Last Written URL", +"xapMM:ManagedFrom" => "Managed From", +"xapMM:Manager" => "Asset Management System", +"xapMM:ManageTo" => "Manage To", +"xapMM:xmpMM:ManageUI" => "Managed Resource URI", +"xapMM:ManagerVariant" => "Particular Variant of Asset Management System", +"xapMM:RenditionClass" => "Rendition Class", +"xapMM:RenditionParams" => "Rendition Parameters", +"xapMM:RenditionOf" => "Rendition Of", +"xapMM:SaveID" => "Save ID", +"xapMM:VersionID" => "Version ID", +"xapMM:Versions" => "Versions", + +"xapBJ:JobRef" => "Job Reference", + +"xmpTPg:MaxPageSize" => "Largest Page Size", +"xmpTPg:NPages" => "Number of pages", + +"pdf:Keywords" => "Keywords", +"pdf:PDFVersion" => "PDF file version", +"pdf:Producer" => "PDF Creation Tool", + +"photoshop:AuthorsPosition" => "Authors Position", +"photoshop:CaptionWriter" => "Caption Writer", +"photoshop:Category" => "Category", +"photoshop:City" => "City", +"photoshop:Country" => "Country", +"photoshop:Credit" => "Credit", +"photoshop:DateCreated" => "Creation Date", +"photoshop:Headline" => "Headline", +"photoshop:History" => "History", // Not in XMP spec +"photoshop:Instructions" => "Instructions", +"photoshop:Source" => "Source", +"photoshop:State" => "State", +"photoshop:SupplementalCategories" => "Supplemental Categories", +"photoshop:TransmissionReference" => "Technical (Transmission) Reference", +"photoshop:Urgency" => "Urgency", + + +"tiff:ImageWidth" => "Image Width", +"tiff:ImageLength" => "Image Length", +"tiff:BitsPerSample" => "Bits Per Sample", +"tiff:Compression" => "Compression", +"tiff:PhotometricInterpretation" => "Photometric Interpretation", +"tiff:Orientation" => "Orientation", +"tiff:SamplesPerPixel" => "Samples Per Pixel", +"tiff:PlanarConfiguration" => "Planar Configuration", +"tiff:YCbCrSubSampling" => "YCbCr Sub-Sampling", +"tiff:YCbCrPositioning" => "YCbCr Positioning", +"tiff:XResolution" => "X Resolution", +"tiff:YResolution" => "Y Resolution", +"tiff:ResolutionUnit" => "Resolution Unit", +"tiff:TransferFunction" => "Transfer Function", +"tiff:WhitePoint" => "White Point", +"tiff:PrimaryChromaticities" => "Primary Chromaticities", +"tiff:YCbCrCoefficients" => "YCbCr Coefficients", +"tiff:ReferenceBlackWhite" => "Black & White Reference", +"tiff:DateTime" => "Date & Time", +"tiff:ImageDescription" => "Image Description", +"tiff:Make" => "Make", +"tiff:Model" => "Model", +"tiff:Software" => "Software", +"tiff:Artist" => "Artist", +"tiff:Copyright" => "Copyright", + + +"exif:ExifVersion" => "Exif Version", +"exif:FlashpixVersion" => "Flash pix Version", +"exif:ColorSpace" => "Color Space", +"exif:ComponentsConfiguration" => "Components Configuration", +"exif:CompressedBitsPerPixel" => "Compressed Bits Per Pixel", +"exif:PixelXDimension" => "Pixel X Dimension", +"exif:PixelYDimension" => "Pixel Y Dimension", +"exif:MakerNote" => "Maker Note", +"exif:UserComment" => "User Comment", +"exif:RelatedSoundFile" => "Related Sound File", +"exif:DateTimeOriginal" => "Date & Time of Original", +"exif:DateTimeDigitized" => "Date & Time Digitized", +"exif:ExposureTime" => "Exposure Time", +"exif:FNumber" => "F Number", +"exif:ExposureProgram" => "Exposure Program", +"exif:SpectralSensitivity" => "Spectral Sensitivity", +"exif:ISOSpeedRatings" => "ISO Speed Ratings", +"exif:OECF" => "Opto-Electronic Conversion Function", +"exif:ShutterSpeedValue" => "Shutter Speed Value", +"exif:ApertureValue" => "Aperture Value", +"exif:BrightnessValue" => "Brightness Value", +"exif:ExposureBiasValue" => "Exposure Bias Value", +"exif:MaxApertureValue" => "Max Aperture Value", +"exif:SubjectDistance" => "Subject Distance", +"exif:MeteringMode" => "Metering Mode", +"exif:LightSource" => "Light Source", +"exif:Flash" => "Flash", +"exif:FocalLength" => "Focal Length", +"exif:SubjectArea" => "Subject Area", +"exif:FlashEnergy" => "Flash Energy", +"exif:SpatialFrequencyResponse" => "Spatial Frequency Response", +"exif:FocalPlaneXResolution" => "Focal Plane X Resolution", +"exif:FocalPlaneYResolution" => "Focal Plane Y Resolution", +"exif:FocalPlaneResolutionUnit" => "Focal Plane Resolution Unit", +"exif:SubjectLocation" => "Subject Location", +"exif:SensingMethod" => "Sensing Method", +"exif:FileSource" => "File Source", +"exif:SceneType" => "Scene Type", +"exif:CFAPattern" => "Colour Filter Array Pattern", +"exif:CustomRendered" => "Custom Rendered", +"exif:ExposureMode" => "Exposure Mode", +"exif:WhiteBalance" => "White Balance", +"exif:DigitalZoomRatio" => "Digital Zoom Ratio", +"exif:FocalLengthIn35mmFilm" => "Focal Length In 35mm Film", +"exif:SceneCaptureType" => "Scene Capture Type", +"exif:GainControl" => "Gain Control", +"exif:Contrast" => "Contrast", +"exif:Saturation" => "Saturation", +"exif:Sharpness" => "Sharpness", +"exif:DeviceSettingDescription" => "Device Setting Description", +"exif:SubjectDistanceRange" => "Subject Distance Range", +"exif:ImageUniqueID" => "Image Unique ID", +"exif:GPSVersionID" => "GPS Version ID", +"exif:GPSLatitude" => "GPS Latitude", +"exif:GPSLongitude" => "GPS Longitude", +"exif:GPSAltitudeRef" => "GPS Altitude Reference", +"exif:GPSAltitude" => "GPS Altitude", +"exif:GPSTimeStamp" => "GPS Time Stamp", +"exif:GPSSatellites" => "GPS Satellites", +"exif:GPSStatus" => "GPS Status", +"exif:GPSMeasureMode" => "GPS Measure Mode", +"exif:GPSDOP" => "GPS Degree Of Precision", +"exif:GPSSpeedRef" => "GPS Speed Reference", +"exif:GPSSpeed" => "GPS Speed", +"exif:GPSTrackRef" => "GPS Track Reference", +"exif:GPSTrack" => "GPS Track", +"exif:GPSImgDirectionRef" => "GPS Image Direction Reference", +"exif:GPSImgDirection" => "GPS Image Direction", +"exif:GPSMapDatum" => "GPS Map Datum", +"exif:GPSDestLatitude" => "GPS Destination Latitude", +"exif:GPSDestLongitude" => "GPS Destnation Longitude", +"exif:GPSDestBearingRef" => "GPS Destination Bearing Reference", +"exif:GPSDestBearing" => "GPS Destination Bearing", +"exif:GPSDestDistanceRef" => "GPS Destination Distance Reference", +"exif:GPSDestDistance" => "GPS Destination Distance", +"exif:GPSProcessingMethod" => "GPS Processing Method", +"exif:GPSAreaInformation" => "GPS Area Information", +"exif:GPSDifferential" => "GPS Differential", + +"stDim:w" => "Width", +"stDim:h" => "Height", +"stDim:unit" => "Units", + +"xapGImg:height" => "Height", +"xapGImg:width" => "Width", +"xapGImg:format" => "Format", +"xapGImg:image" => "Image", + +"stEvt:action" => "Action", +"stEvt:instanceID" => "Instance ID", +"stEvt:parameters" => "Parameters", +"stEvt:softwareAgent" => "Software Agent", +"stEvt:when" => "When", + +"stRef:instanceID" => "Instance ID", +"stRef:documentID" => "Document ID", +"stRef:versionID" => "Version ID", +"stRef:renditionClass" => "Rendition Class", +"stRef:renditionParams" => "Rendition Parameters", +"stRef:manager" => "Asset Management System", +"stRef:managerVariant" => "Particular Variant of Asset Management System", +"stRef:manageTo" => "Manage To", +"stRef:manageUI" => "Managed Resource URI", + +"stVer:comments" => "", +"stVer:event" => "", +"stVer:modifyDate" => "", +"stVer:modifier" => "", +"stVer:version" => "", + + + +"stJob:name" => "Job Name", +"stJob:id" => "Unique Job ID", +"stJob:url" => "URL for External Job Management File", + +// Exif Flash +"exif:Fired" => "Fired", +"exif:Return" => "Return", +"exif:Mode" => "Mode", +"exif:Function" => "Function", +"exif:RedEyeMode" => "Red Eye Mode", + +// Exif OECF/SFR +"exif:Columns" => "Columns", +"exif:Rows" => "Rows", +"exif:Names" => "Names", +"exif:Values" => "Values", + +// Exif CFAPattern +"exif:Columns" => "Columns", +"exif:Rows" => "Rows", +"exif:Values" => "Values", + + +// Exif DeviceSettings +"exif:Columns" => "Columns", +"exif:Rows" => "Rows", +"exif:Settings" => "Settings", + + + +); + +/****************************************************************************** +* End of Global Variable: XMP_tag_captions +******************************************************************************/ + + +?> \ No newline at end of file diff --git a/includes/jpeg_metadata_tk/documentation/Camera_List_1.0.pdf b/includes/jpeg_metadata_tk/documentation/Camera_List_1.0.pdf new file mode 100644 index 0000000..711e2a7 Binary files /dev/null and b/includes/jpeg_metadata_tk/documentation/Camera_List_1.0.pdf differ diff --git a/includes/jpeg_metadata_tk/documentation/Edit_File_Info_Example.php b/includes/jpeg_metadata_tk/documentation/Edit_File_Info_Example.php new file mode 100644 index 0000000..b8a9f24 --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/Edit_File_Info_Example.php @@ -0,0 +1,248 @@ + + + + + + + + + + + No image filename defined\n"; + echo "\n"; + echo "\n"; + echo "

No image filename defined - use GET method with field: jpeg_fname

\n"; + echo "

PHP JPEG Metadata Toolkit version " . $GLOBALS['Toolkit_Version'] . ", Copyright (C) 2004 Evan Hunter

\n"; // Change: displayed toolkit version numbers to reference Toolkit_Version.php - as of version 1.11 + echo "\n"; + exit( ); + + } + else + { + $filename = $GLOBALS['HTTP_GET_VARS']['jpeg_fname']; + } + ?> + + + Edit Photoshop File Info details for <?php $filename ?> + + + +

Powered by: PHP JPEG Metadata Toolkit version , Copyright (C) 2004 Evan Hunter

+
+
+ + + Edit Photoshop File Info details for $filename"; + + // Output a link to display the full metadata + echo "

View Full Metatdata Information

\n"; + + + // Display a small copy of the image + echo "

"; + + // Define defaults for the fields - These are only used where the image has blank fields + $default_ps_file_info_array = array ( + 'title' => "", + 'author' => "Evan Hunter", + 'authorsposition' => "", + 'caption' => "", + 'captionwriter' => "Evan Hunter", + 'jobname' => "", + 'copyrightstatus' => "Copyrighted Work", + 'copyrightnotice' => "Copyright (c) Evan Hunter 2004", + 'ownerurl' => "http://www.ozhiker.com", + 'keywords' => array(), + 'category' => "", + 'supplementalcategories'=> array(), + 'date' => "", + 'city' => "", + 'state' => "Tasmania", + 'country' => "Australia", + 'credit' => "Evan Hunter", + 'source' => "Evan Hunter", + 'headline' => "", + 'instructions' => "", + 'transmissionreference' => "", + 'urgency' => "" + ); + + // outputfilename must always be defined, as it specifies the + // file which will be changed + + // These two lines create a temporary copy of the file + // which will be the one that is edited, keeping + // the original intact. - This would not be required if you wanted + // to change the original - in that case just set $outputfilename = $filename + $outputfilename = get_next_filename( ); + copy( $filename, $outputfilename ); + + + + + // Include the File Info Editor. + + include "Edit_File_Info.php"; + + ?> + + + + + + + + + + + + + + + + + + + + + + + + 'z' ) ) + { + $filename = 'a'; + } + + + // Check if the names are up to 'z' + if( $filename == 'z' ) + { + // Name is at z - the next one should be 'a' + $new_filename = 'a'; + } + else + { + // The name is not 'z' add one to it to get the next value + $new_filename = chr( ord( $filename ) + 1 ); + } + + // Write the next temp file letter back into the file + $Fhnd = fopen ("next_temp_file.dat", "w"); + fwrite ($Fhnd, $new_filename); + fclose ($Fhnd); + + // return the filename + return "temp_$filename.jpg"; +} + +/****************************************************************************** +* End of Function: get_next_filename +******************************************************************************/ + + + +?> \ No newline at end of file diff --git a/includes/jpeg_metadata_tk/documentation/Example.php b/includes/jpeg_metadata_tk/documentation/Example.php new file mode 100644 index 0000000..86ac497 --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/Example.php @@ -0,0 +1,218 @@ + + + + + + + + + + + No image filename defined\n"; + echo "\n"; + echo "\n"; + echo "

No image filename defined - use GET method with field: jpeg_fname

\n"; + echo "

PHP JPEG Metadata Toolkit version " . $GLOBALS['Toolkit_Version'] . ", Copyright (C) 2004 Evan Hunter

\n"; // Change: displayed toolkit version numbers to reference Toolkit_Version.php - as of version 1.11 + echo "\n"; + exit( ); + } + else + { + $filename = $GLOBALS['HTTP_GET_VARS']['jpeg_fname']; + } + + + // Output the title + echo "Metadata details for $filename"; + + // Retrieve the header information + $jpeg_header_data = get_jpeg_header_data( $filename ); + + ?> + + + + + +

Interpreted using: PHP JPEG Metadata Toolkit version , Copyright (C) 2004 Evan Hunter

+
+
+ +

Metadata for ""

+
+ + + +

" >Click here to edit the Photoshop File Info for this file

+
+ + + + + + +
+
+
+ + + + +
+
+
+ + + + +
+
+
+ + + + +
+
+
+ + + + +
+
+
+ + + + +
+
+
+ + + + +
+
+
+ + + + +
+
+
+ + + + +
+
+
+ + + + +
+
+
+ + + +

Original Image

+ "; ?> + + +
+
+
+

Interpreted using:

+

PHP JPEG Metadata Toolkit version , Copyright (C) 2004 Evan Hunter

+ + + + \ No newline at end of file diff --git a/includes/jpeg_metadata_tk/documentation/TIFFExample.php b/includes/jpeg_metadata_tk/documentation/TIFFExample.php new file mode 100644 index 0000000..873d18f --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/TIFFExample.php @@ -0,0 +1,121 @@ + + + + + + + + + + + No image filename defined\n"; + echo "\n"; + echo "\n"; + echo "

No image filename defined - use GET method with field: tiff_fname

\n"; + echo "

PHP JPEG Metadata Toolkit version " . $GLOBALS['Toolkit_Version'] . ", Copyright (C) 2004 Evan Hunter

\n"; // Change: displayed toolkit version numbers to reference Toolkit_Version.php - as of version 1.11 + echo "\n"; + exit( ); + } + else + { + $filename = $GLOBALS['HTTP_GET_VARS']['tiff_fname']; + } + + + // Output the title + echo "Metadata details for $filename"; + + + + ?> + + + + + +

Interpreted using: PHP JPEG Metadata Toolkit version , Copyright (C) 2004 Evan Hunter

+
+ +

Metadata for ""

+ + + + + +
+
+
+

Interpreted using:

+

PHP JPEG Metadata Toolkit version , Copyright (C) 2004 Evan Hunter

+ + + + \ No newline at end of file diff --git a/includes/jpeg_metadata_tk/documentation/changes.html b/includes/jpeg_metadata_tk/documentation/changes.html new file mode 100644 index 0000000..e85f5b7 --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/changes.html @@ -0,0 +1,257 @@ + + + + + + + The PHP JPEG Metadata Toolkit - Documentation + + + +
+

The PHP JPEG Metadata Toolkit - Documentation

+
+ + Go to Documentation - Home + +
+ +

Changes List

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
VersionsFileChanges
1.00 -> 1.01IPTC.phpChanged get_IPTC to return partial data when error occurs
1.00 -> 1.02Photoshop_IRB.phpChanged get_Photoshop_IRB to work with corrupted resource names which Photoshop can still read
1.02 -> 1.03Photoshop_IRB.phpFixed put_Photoshop_IRB to output "Photoshop 3.0\x00" string with every APP13 segment, not just the first one
1.03 -> 1.04XMP.phpChanged put_IPTC to fix a bug preventing the correct insertion of a XMP block where none existed previously
1.04 -> 1.10XMP.phpChanged put_XMP_text to fix some array indexes which were missing qoutes
IPTC.php + Changed put_IPTC to check if the incoming IPTC block is valid
+ Changed Interpret_IPTC_to_HTML, adding nl2br functions for each text field, + so that multiline text displays properly +
XML.php + Changed read_xml_array_from_text to fix problem that caused the whitespace (especially newlines) to be + destroyed when converting xml text to an xml array +
Photoshop_IRB.php + Changed get_Photoshop_IRB to fix processing of embedded resource names, + after discovering that Photoshop does not process + resource names according to the standard :
+ "Adobe Photoshop 6.0 File Formats Specification, Version 6.0, Release 2, November 2000"
+ This is an update to the change 1.00 -> 1.02, which was not fully correct

+ Changed put_Photoshop_IRB to fix the writing of embedded resource name, + to avoid creating blank resources, and to fix a problem + causing the IRB block to be incorrectly positioned if no APP segments existed.

+ Changed get_Photoshop_IPTC to initialise the output array correctly. +
Unicode.php + Added the following functions:
+ smart_HTML_Entities
+ smart_htmlspecialchars
+ HTML_UTF16_UnEscape
+ HTML_UTF8_UnEscape

+ Changed HTML_UTF8_Escape and HTML_UTF16_Escape to + use smart_htmlspecialchars, so that characters which + were already escaped would remain intact +
JPEG.php + changed put_jpeg_header_data to check if the data being written exists +
Example.php + Changed name of GET parameter from "filename" to "jpeg_fname" + to stop script-kiddies using the google command "allinurl:*.php?filename=*" + to find servers to attack +
EXIF.php + added function get_EXIF_TIFF to allow extracting EXIF from a TIFF file +
Photoshop_File_Info.php + New File - see documentation for more information +
Edit_File_Info.php + New File - see documentation for more information +
Edit_File_Info_Example.php + New File - see documentation for more information +
Write_File_Info.php + New File - see documentation for more information +
1.10 -> 1.11EXIF.php + Added functionality to allow decoding of XMP and Photoshop IRB information embedded within the EXIF data
+ Added checks for http and ftp wrappers, as these are not supported
+ Changed interpret_IFD to allow thumbnail links to work when toolkit is portable across directories +
Makernotes/casio.phpChanged get_Casio_Makernote_Html to allow thumbnail links to work when toolkit is portable across directories
Makernotes/olympus.phpChanged get_Olympus_Makernote_Html to allow thumbnail links to work when toolkit is portable across directories
JFIF.phpChanged Interpret_JFXX_to_HTML to allow thumbnail links to work when toolkit is portable across directories
Photoshop_IRB.php + Moved code out of get_Photoshop_IRB into new function unpack_Photoshop_IRB_Data to allow reading of IRB blocks embedded within EXIF (for TIFF Files)
+ Moved code out of put_Photoshop_IRB into new function pack_Photoshop_IRB_Data to allow writing of IRB blocks embedded within EXIF (for TIFF Files)
+ Enabled the usage of $GLOBALS['HIDE_UNKNOWN_TAGS'] to hide unknown resources
+ Changed Interpret_IRB_to_HTML to allow thumbnail links to work when toolkit is portable across directories +
EXIF_Makernote.php + Changed makernotes directory definition to allow the toolkit to be portable across directories +
EXIF_Tags.php + Added TIFF compression types ZIP, LZW and JPEG
+ Added embedded XMP tag
+ Added embedded Photoshop IRB tag
+ Fixed GPS tags after testing +
Example.php + Changed displayed toolkit version numbers to reference Toolkit_Version.php + Changed this example file to be easily relocatable +
TIFFExample.php + Changed displayed toolkit version numbers to reference Toolkit_Version.php +
Write_File_Info.php + Changed displayed toolkit version numbers to reference Toolkit_Version.php
+ Changed error reporting to no errors
+ Removed limitation on file being in current directory +
Edit_File_Info_Example.php + Changed displayed toolkit version numbers to reference Toolkit_Version.php +
Photoshop_File_Info.php + Changed displayed toolkit version numbers to reference Toolkit_Version.php +
Edit_File_Info.php + Changed displayed toolkit version numbers to reference Toolkit_Version.php +
get_ps_thumb.php + Added support for Photoshop IRB thumbnails which are embedded within EXIF information (used in TIFF files) +
Toolkit_Version.php + New File - Added file to provide a single storage place for current version number +
pjmt_utils.php + New File - Added file to provide utility functions for the toolkit +
+ +
+ +
+ +
+ + + \ No newline at end of file diff --git a/includes/jpeg_metadata_tk/documentation/css_terms.html b/includes/jpeg_metadata_tk/documentation/css_terms.html new file mode 100644 index 0000000..648f6cd --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/css_terms.html @@ -0,0 +1,171 @@ + + + + + + + The PHP Metadata Toolkit - Documentation + + + +
+

The PHP JPEG Metadata Toolkit - Documentation

+
+ + Go to Documentation - Home + +
+ +

Customising the HTML look via style sheets

+

+ Below is a list of style classes which have been used in the HTML for the PHP JPEG Metadata Toolkit. + These allow the look of the output HTML to be changed via the use of cascading style sheets. + +

+ +

There are two ways to do this:

+
    +
  1. Embedded Style sheets
  2. +
  3. External Style sheets
  4. +
+ +

Examples

+ +

Both of the following two sections would cause the following visual effects:

+

    +
  • The page will have a dark grey background colour (#505050)
  • +
  • The default text colour for the page will be off-white (#F0F0F0)
  • +
  • Links (Anchors) will be orange
  • +
  • Main headings for the EXIF section will be red
  • +
  • Secondary headings for the EXIF section will be orange
  • +
  • The outer border of the Tables for the EXIF section will be a thin dark yellow (#909000) solid line
  • +
+
+
+
+ +

Embedded Style Sheets

+ +

Include the following in the <HEAD> section of the HTML page:

+
+        <META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css">
+        <STYLE TYPE="text/css" MEDIA="screen, print, projection">
+        <!--
+                BODY { background-color:#505050; color:#F0F0F0 }
+                a  { color:orange  }
+                .EXIF_Main_Heading { color:red }
+                .EXIF_Secondary_Heading{ color: orange}
+                .EXIF_Table {  border-collapse: collapse ; border: 1px solid #909000}
+        -->
+        </STYLE>
+                
+ + +

External Style Sheets

+

Include the following in the <HEAD> section of the HTML page:

+
+        <link rel=StyleSheet href="style.css" type="text/css">
+                
+

Create an external file called "style.css" containing the following:

+
+        BODY { background-color:#505050; color:#F0F0F0 }
+        a  { color:orange  }
+        .EXIF_Main_Heading { color:red }
+        .EXIF_Secondary_Heading{ color: orange}
+        .EXIF_Table {  border-collapse: collapse ; border: 1px solid #909000}
+                
+ + + +
+
+ +

Cascading Style Sheets Class List

+

Any of the following classes may be used to customise the look of the html output from the PHP JPEG Metadata Toolkit

+
    +
  • JPEG_Intrinsic_Main_Heading
  • +
  • JPEG_Intrinsic_Table
  • +
  • JPEG_Intrinsic_Table_Row
  • +
  • JPEG_Intrinsic_Caption_Cell
  • +
  • JPEG_Intrinsic_Value_Cell
  • +
+
    +
  • JPEG_APP_Segments_Main_Heading
  • +
  • JPEG_APP_Segments_Table
  • +
  • JPEG_APP_Segments_Table_Row
  • +
  • JPEG_APP_Segments_Caption_Cell
  • +
  • JPEG_APP_Segments_Type_Cell
  • +
  • JPEG_APP_Segments_Size_Cell
  • +
+
    +
  • XMP_Main_Heading
  • +
  • XMP_Secondary_Heading
  • +
  • XMP_Table
  • +
  • XMP_Table_Row
  • +
  • XMP_Caption_Cell
  • +
  • XMP_Value_Cell
  • +
+
    +
  • JFIF_Main_Heading
  • +
  • JFIF_Table
  • +
  • JFIF_Table_Row
  • +
  • JFIF_Caption_Cell
  • +
  • JFIF_Value_Cell
  • +
  • JFIF_Thumbnail
  • +
+
    +
  • JFXX_Main_Heading
  • +
  • JFXX_Text
  • +
  • JFXX_Thumbnail
  • +
  • JFXX_Thumbnail_Link
  • +
+
    +
  • JPEG_Comment_Main_Heading
  • +
  • JPEG_Comment_Text
  • +
+
    +
  • Picture_Info_Main_Heading
  • +
  • Picture_Info_Caption_Text
  • +
  • Picture_Info_Value_Text
  • +
+
    +
  • Photoshop_Main_Heading
  • +
  • Photoshop_Table
  • +
  • Photoshop_Table_Row
  • +
  • Photoshop_Caption_Cell
  • +
  • Photoshop_Value_Cell
  • +
  • Photoshop_Thumbnail
  • +
  • Photoshop_Thumbnail_Link
  • +
+
    +
  • IPTC_Main_Heading
  • +
  • IPTC_Table
  • +
  • IPTC_Table_Row
  • +
  • IPTC_Caption_Cell
  • +
  • IPTC_Value_Cell
  • +
+
    +
  • EXIF_Main_Heading
  • +
  • EXIF_Secondary_Heading
  • +
  • EXIF_Table
  • +
  • EXIF_Table_Row
  • +
  • EXIF_Caption_Cell
  • +
  • EXIF_Value_Cell
  • +
  • EXIF_First_IFD_Thumb
  • +
  • EXIF_First_IFD_Thumb_Link
  • +
  • EXIF_Minolta_Thumb
  • +
  • EXIF_Minolta_Thumb_Link
  • +
  • EXIF_Casio_Thumb
  • +
  • EXIF_Casio_Thumb_Link
  • +
  • EXIF_Makernote_Small_Heading
  • +
  • EXIF_Makernote_Text
  • +
+ + +
+
+ +
+ + + diff --git a/includes/jpeg_metadata_tk/documentation/edit_write_file_info.html b/includes/jpeg_metadata_tk/documentation/edit_write_file_info.html new file mode 100644 index 0000000..d9ab787 --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/edit_write_file_info.html @@ -0,0 +1,272 @@ + + + + + + + The PHP JPEG Metadata Toolkit - Documentation + + + +
+

The PHP JPEG Metadata Toolkit - Documentation

+
+ + Go to Documentation - Examples + +
+ +

Example Photoshop "File Info" Editor Scripts

+ +

+ These two example scripts areincluded with the PHP JPEG Metadata Toolkit. + They show how the library can be used to edit metadata over the internet + in exactly the same format as Photoshop"s "File Info" dialog box. + This is an explanation of some of the commands used in the script. +

+ + + Click here to see the Edit_File_Info_Example.php +

+ + Click here to see the Write_File_Info.php +
+ +
+
+
+ +

Edit_File_Info_Example.php

+ +

Overview

+

+ This script utilises another script called Edit_File_Info.php.
+ Edit_File_Info.php outputs the HTML required to display a HTML form which emulates + the Photoshop "File Info" dialog box. +

+

+ Edit_File_Info.php has four modes of operation: +

+
    +
  1. + If $new_ps_file_info_array is defined then it's data will be used + to fill the fields. +
  2. +
  3. + If $new_ps_file_info_array is not defined but $filename is + defined, then the file info fields will be filled from the + metadata in the file specified +
  4. +
  5. + If $new_ps_file_info_array is not defined but $filename + and $default_ps_file_info_array are defined, then the + file info fields will be filled from the metadata in the + file specified, but where fields are blank, they will be + filled from $default_ps_file_info_array +
  6. +
  7. + Otherwise the fields will be blank +
  8. +
+ + + + +
+
+ +

Forced Field Values

+ +

+ If the variable $new_ps_file_info_array before Edit_File_Info.php is included, then it + forces the values of the fields to be loaded from $new_ps_file_info_array +

+ +

+ NOTE: +

    +
  • date must be in YYYY-MM-DD format
  • +
  • coyrightstatus must be either "Copyrighted Work", "Public Domain", or "Unknown"
  • +
  • urgency must be a single digit between 1 and 8 inclusive
  • +
  • category must be 3 characters or less
  • +
  • authors position is not used in Photoshop 7 or higher
  • +
  • jobname is not used in Photoshop CS or higher
  • +
+

+ +

Here is some example code for defining $new_ps_file_info_array

+
+                        $new_ps_file_info_array = array (
+                                                                'title'                 => "Frenchmans Cap",
+                                                                'author'                => "Evan Hunter",
+                                                                'authorsposition'       => "",
+                                                                'caption'               => "A view of Frenchmans cap from a bus on the Lyell Highway",
+                                                                'captionwriter'         => "Evan Hunter",
+                                                                'jobname'               => "",
+                                                                'copyrightstatus'       => "Copyrighted Work",
+                                                                'copyrightnotice'       => "Copyright (c) Evan Hunter 2004",
+                                                                'ownerurl'              => "http://www.ozhiker.com",
+                                                                'keywords'              => array( "Frenchmans Cap", "Franklin Gordon National Park", "Tasmania" ),
+                                                                'category'              => "abc",
+                                                                'supplementalcategories'=> array( "Supp Category1", "Supp Category2" ),
+                                                                'date'                  => "2004-05-11",
+                                                                'city'                  => "Franklin Gordon National Park",
+                                                                'state'                 => "Tasmania",
+                                                                'country'               => "Australia",
+                                                                'credit'                => "Evan Hunter",
+                                                                'source'                => "Evan Hunter",
+                                                                'headline'              => "Frenchmans Cap from Lyell Highway",
+                                                                'instructions'          => "No Instructions",
+                                                                'transmissionreference' => "12345",
+                                                                'urgency'               => "3"
+                                                                );
+                
+ +
+
+ + +

Default Field Values

+ +

+ The variable $default_ps_file_info_array sets the default values of the File Info fields. + These are only loaded into the fields where File Info has been loaded from a file, but + the fields in question are still blank. It has similar definition requirements to $new_ps_file_info_array. +

+ +
+                        // Define defaults for the fields - These are only used where the image has blank fields
+                        $default_ps_file_info_array = array (
+                                                                'title'                 => "",
+                                                                'author'                => "Evan Hunter",
+                                                                'authorsposition'       => "",
+                                                                'caption'               => "",
+                                                                'captionwriter'         => "Evan Hunter",
+                                                                'jobname'               => "",
+                                                                'copyrightstatus'       => "Copyrighted Work",
+                                                                'copyrightnotice'       => "Copyright (c) Evan Hunter 2004",
+                                                                'ownerurl'              => "http://www.ozhiker.com",
+                                                                'keywords'              => array(),
+                                                                'category'              => "",
+                                                                'supplementalcategories'=> array(),
+                                                                'date'                  => "",
+                                                                'city'                  => "",
+                                                                'state'                 => "Tasmania",
+                                                                'country'               => "Australia",
+                                                                'credit'                => "Evan Hunter",
+                                                                'source'                => "Evan Hunter",
+                                                                'headline'              => "",
+                                                                'instructions'          => "",
+                                                                'transmissionreference' => "",
+                                                                'urgency'               => ""
+                                                                );
+                
+ +
+
+ +

Input Filename

+

+ When the $filename variable is defined before the inclusion of Edit_File_Info.php, it defines + which JPEG file should be searched to fill the File Info fields +

+
+                        $filename = "test.jpg";
+                
+ +
+
+ +

Output Filename

+

+ The $outputfilename variable must always be defined before the inclusion of Edit_File_Info.php. + It defines the target image which will be modified with the information that the user enters. + It can be the same as $filename. +

+
+                        $outputfilename = "test.jpg";
+                
+ +

Including Edit_File_Info.php

+

+ After $outputfilename has been defined, and any of the other variables have been defined as required, + Edit_File_Info.php should be included. It outputs the HTML for the File Info Editor. +

+
+                        include "Edit_File_Info.php";
+                
+ +
+
+ + +

Write_File_Info.php

+

Overview

+

+ This script receives the File Info data from the user via the HTML POST method.
+

+ + +

Retrieving the POSTed variables

+

+ The field data is retrieved as follows: +

+
+                        $new_ps_file_info_array = $GLOBALS['HTTP_POST_VARS'];
+                
+ +
+
+ +

Preparing the field data

+

+ The POSTed field data has some characters escaped with backslashes, which need to be removed. + Two of the fields should also be arrays which and need to be split. +

+
+                        // Some characters are escaped with backslashes in HTML Posted variable
+                        // Cycle through each of the HTML Posted variables, and strip out the slashes
+                        foreach( $new_ps_file_info_array as $var_key => $var_val )
+                        {
+                                $new_ps_file_info_array[ $var_key ] = stripslashes( $var_val );
+                        }
+
+                        // Keywords should be an array - explode it on newline boundarys
+                        $new_ps_file_info_array[ 'keywords' ] = explode( "\n", trim( $new_ps_file_info_array[ 'keywords' ] ) );
+
+                        // Supplemental Categories should be an array - explode it on newline boundarys
+                        $new_ps_file_info_array[ 'supplementalcategories' ] = explode( "\n", trim( $new_ps_file_info_array[ 'supplementalcategories' ] ) );
+                
+ +
+
+ +

Writing the File Info

+

+ Now the File Info data can be written to a file. The following code results in + the $jpeg_header_data being updated with the new Photoshop File Info, and it can + then be written to a JPEG file as normal using put_jpeg_header_data: +

+
+                        // Retrieve the header information
+                        $jpeg_header_data = get_jpeg_header_data( $filename );
+
+                        // Retreive the EXIF, XMP and Photoshop IRB information from
+                        // the existing file, so that it can be updated
+                        $Exif_array = get_EXIF_JPEG( $filename );
+                        $XMP_array = read_XMP_array_from_text( get_XMP_text( $jpeg_header_data ) );
+                        $IRB_array = get_Photoshop_IRB( $jpeg_header_data );
+
+                        // Update the JPEG header information with the new Photoshop File Info
+                        $jpeg_header_data = put_photoshop_file_info( $jpeg_header_data, $new_ps_file_info_array, $Exif_array, $XMP_array, $IRB_array );
+                
+ +
+
+ + + +
+ + + \ No newline at end of file diff --git a/includes/jpeg_metadata_tk/documentation/example.html b/includes/jpeg_metadata_tk/documentation/example.html new file mode 100644 index 0000000..7f922de --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/example.html @@ -0,0 +1,131 @@ + + + + + + + The PHP JPEG Metadata Toolkit - Documentation + + + +
+

The PHP JPEG Metadata Toolkit - Documentation

+
+ + Go to Documentation - Examples + +
+ +

Example Metadata Display Script

+ +

+ This example script is included with the PHP JPEG Metadata Toolkit. It shows how the library + can be used to display detailed metadata from a JPEG image file. This is an explanation of some of the + commands used in the script. +

+ + + Click here to see the script + +
+ +
+
+
+ +

Error Reporting

+

+ Although this library has been tested to be error free on a large number of images, + it is good programming to turn error reporting off, to ensure that users don't see + any error messages. This is not just cosmetic, it helps hide the details of the program + from hackers. +

+
+        // Turn off Error Reporting
+        error_reporting ( 0 );
+                
+ +

Includes

+

Ensure that the correct files are included:

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
JPEG.phpFor accessing basic JPEG functions - most programs will need this
EXIF.phpFor accessing Exchangeable Image File Format (EXIF) APP1 segments
JFIF.phpFor accessing JPEG File Interchange Format and JFIF Extension segments
Photoshop_IRB.phpFor accessing Photoshop IRB & IPTC-NAA IIM segments
PictureInfo.phpFor accessing APP12 Picture Info segments
XMP.phpFor accessing APP1 XMP / RDF / Dublin Core segments
+ +
+
+ +

Displaying Metadata Information

+ +

+ Displaying metadata in HTML format from a PHP script is very simple. For example, + the following will display any EXIF information in a JPEG image: +

+ +
+        echo Interpret_EXIF_to_HTML( get_EXIF_JPEG( $filename ), $filename );
+                
+ +
+
+ +

To display the Photoshop IRB and IPTC-NAA information, use:

+
+        echo Interpret_IRB_to_HTML( get_Photoshop_IRB( get_jpeg_header_data( $filename ) ), $filename );
+                
+ +
+
+ +

+ Note: The HTML returned from these functions is just a fragment, and requires + <HTML>, <HEAD> and <BODY> tags to be provided. +

+ +
+
+ +

Writing Metadata Information

+

+ Although this is not shown in the example script, writing metadata is also very simple. For example, the following will allow + the EXIF data in an image to be modified, and written to a file called "output.jpg" : +

+
+        $exif_data = get_EXIF_JPEG( $filename );
+
+        // Modify the $exif_data array here
+
+        $jpeg_header_data = put_EXIF_JPEG( $exif_data, $jpeg_header_data );
+        put_jpeg_header_data( $filename, "output.jpg", $jpeg_header_data );
+                
+ +
+
+ +
+ + + \ No newline at end of file diff --git a/includes/jpeg_metadata_tk/documentation/examples.html b/includes/jpeg_metadata_tk/documentation/examples.html new file mode 100644 index 0000000..172b123 --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/examples.html @@ -0,0 +1,62 @@ + + + + + + + The PHP JPEG Metadata Toolkit - Documentation + + + +
+

The PHP JPEG Metadata Toolkit - Documentation

+
+ + Go to Documentation - Home + +
+ +

Example Scripts

+ +

+ Included with the PHP JPEG Metadata Toolkit are several example scripts, which show how the library + can be used to retrieve and store the metadata in a JPEG image file. Below are explanations of some of the + commands used in the scripts. +

+ + + + + + + + + + + + + + + + + +
+
+ + + + + \ No newline at end of file diff --git a/includes/jpeg_metadata_tk/documentation/exif.html b/includes/jpeg_metadata_tk/documentation/exif.html new file mode 100644 index 0000000..f67c1fd --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/exif.html @@ -0,0 +1,284 @@ + + + + + + + The PHP JPEG Metadata Toolkit - Documentation + + + +
+

The PHP JPEG Metadata Toolkit - Documentation

+
+ + Go to Documentation - Home + +
+ +

EXIF and Kodak "Meta" Function Reference

+ +
+ Example.php + + Displays detailed information about the metadata in a JPEG file. +
+ TIFFExample.php + + Displays detailed information about the metadata in a JPEG file. +
+ Edit_File_Info_Example.php,
Write_File_Info.php
+
+ Allows editing of metadata over the internet in exactly the same format as + Photoshop"s "File Info" dialog box. +
+ + + + + + + + + + + + + + + + + + + + + + +
Function:get_EXIF_JPEG
Description: + Retrieves information from a Exchangeable Image File Format (EXIF) + APP1 segment and returns it in an array. Uses information + supplied by the get_jpeg_header_data function +
Parameters:filenamethe filename of the JPEG image to process
Returns:OutputArrayArray of EXIF records
FALSEIf an error occured in decoding
+ + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Function:put_EXIF_JPEG
Description: + Stores information into a Exchangeable Image File Format (EXIF) + APP1 segment from an EXIF array.
+
+ WARNING: Because the EXIF standard allows pointers to data + outside the APP1 segment, if there are any such pointers in + a makernote, this function will DAMAGE them since it will not + be aware that there is an external pointer. This will often + happen with Makernotes that include an embedded thumbnail. + This damage could be prevented where makernotes can be decoded, + but currently this is not implemented. +
Parameters:exif_dataThe array of EXIF data to insert into the JPEG header
jpeg_header_dataThe JPEG header into which the EXIF data should be stored, as from get_jpeg_header_data
Returns:jpeg_header_dataJPEG header array with the EXIF segment inserted
FALSEIf an error occured
+ + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
Function:get_Meta_JPEG
Description: +

+ Retrieves information from a Meta APP3 segment and returns it + in an array. Uses information supplied by the + get_jpeg_header_data function. +

+

+ The Meta segment has the same format as an EXIF segment, but + uses different tags +

+
Parameters:filenamethe filename of the JPEG image to process
Returns:OutputArrayArray of Meta records
FALSEIf an error occured in decoding
+ + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Function:put_Meta_JPEG
Description: + Stores information into a Meta APP3 segment from a Meta array.
+
+ WARNING: Because the Meta (EXIF) standard allows pointers to data + outside the APP1 segment, if there are any such pointers in + a makernote, this function will DAMAGE them since it will not + be aware that there is an external pointer. +
Parameters:meta_dataThe array of Meta data to insert into the JPEG header
jpeg_header_dataThe JPEG header into which the Meta data should be stored, as from get_jpeg_header_data
Returns:jpeg_header_dataJPEG header array with the Meta segment inserted
FALSEIf an error occured
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
Function:get_EXIF_TIFF
Description: +

+ Retrieves information from a Exchangeable Image File Format (EXIF) + within a TIFF file and returns it in an array. +

+
Parameters:filenamethe filename of the TIFF image to process
Returns:OutputArrayArray of EXIF records
FALSEIf an error occured in decoding
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
Function:Interpret_EXIF_to_HTML
Description: +

+ Generates html detailing the contents an APP1 EXIF array + which was retrieved with a get_EXIF_JPEG function. + Can also be used for APP3 Meta arrays. +

+
Parameters:Exif_arraythe EXIF array,as read from get_EXIF_JPEG
filenamethe name of the Image file being processed (used by scripts which displays EXIF thumbnails)
Returns:output_str A string containing the HTML
+ + +

Notes:

+

+ Unfortunately, because EXIF data may be distributed anywhere + throughout an image file, rather than just being in one block, + it is impossible to pass just a string containing only the EXIF + information. Hence it is neccessary to be able to seek to + any point in the file. This causes the HTTP and FTP wrappers + not to work - i.e. the EXIF functions will only work with local + files. +

+ To work on an internet file, copy it locally to start with: +

+
+ $newfilename = tempnam ( $dir, "tmpexif" );
+ copy ( "http://whatever.com", $newfilename ); +
+ +

+ There are three global variables which can be set to alter the look of any HTML + output from Interpret_EXIF_to_HTML: +

+
+ $GLOBALS['HIDE_UNKNOWN_TAGS'];
+ $GLOBALS['SHOW_BINARY_DATA_HEX'];
+ $GLOBALS['SHOW_BINARY_DATA_TEXT']; +
+

These variables are all boolean and all default to FALSE

+ + +
+ + + \ No newline at end of file diff --git a/includes/jpeg_metadata_tk/documentation/index.html b/includes/jpeg_metadata_tk/documentation/index.html new file mode 100644 index 0000000..8d21c64 --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/index.html @@ -0,0 +1,60 @@ + + + + + + + The PHP JPEG Metadata Toolkit - Documentation + + + +
+

The PHP JPEG Metadata Toolkit - Documentation

+
+ + + + \ No newline at end of file diff --git a/includes/jpeg_metadata_tk/documentation/intro.html b/includes/jpeg_metadata_tk/documentation/intro.html new file mode 100644 index 0000000..b40cfe0 --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/intro.html @@ -0,0 +1,75 @@ + + + + + + + The PHP JPEG Metadata Toolkit - Documentation + + + +
+

The PHP JPEG Metadata Toolkit - Documentation

+
+ + Go to Documentation - Home + +
+ +

Introduction

+ +

+ The PHP JPEG Metadata Toolkit is a library of functions which allows manipulation of many types of metadata + that reside in a JPEG image file. +

+
+ +

The metadata that can be accessed by this library include:

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
EXIF, DCF & TIFF/EP - including makernotesContains Digital Camera Settings. Can contain a thumbnail.
XMP, RDF & Dublin Core, including multiple language supportCan contain Digital camera settings and text headings/captions.
Photoshop IRB & IPTC-NAA IIMContains Photoshop settings and can contain text headings/captions. Can contain a thumbnail.
Picture InfoContains Digital Camera Settings for older cameras
JFIF & JFIF ExtensionContains limited info about the image and can contain a thumbnail
Intrinsic JPEG ValuesContains limited info configuration of the image
+
+
+ +

Other features

+

    +
  • Has been tested with over 450 popular digital cameras
  • +
  • Provides access to lots of metadata for which php has no built in support
  • +
  • Works with many files that have corrupted metadata.
  • +
  • Customisable look of the HTML output via style sheets
  • +
+ +
+

Requirements

+

The toolkit requires PHP 4 or higher - Has been tested with PHP 4.3.7 and 4.3.8

+
+
+ +
+ + + \ No newline at end of file diff --git a/includes/jpeg_metadata_tk/documentation/jfif.html b/includes/jpeg_metadata_tk/documentation/jfif.html new file mode 100644 index 0000000..f00cb45 --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/jfif.html @@ -0,0 +1,237 @@ + + + + + + + The PHP JPEG Metadata Toolkit - Documentation + + + +
+

The PHP JPEG Metadata Toolkit - Documentation

+
+ + Go to Documentation - Home + +
+ +

JFIF & JFIF Extension Function Reference

+

+ The "App 0" segment is often used by the JPEG File Interchange Format (JFIF) information. + Sometimes, there is an additional "App 0" segment for the JFIF Extension (JFXX) information. +

+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
Function:get_JFIF
Description: + Retrieves information from a JPEG File Interchange Format (JFIF) + segment and returns it in an array. Uses information supplied by + the get_jpeg_header_data function +
Parameters:jpeg_header_dataa JPEG header data array in the same format as from get_jpeg_header_data
Returns:JFIF_dataan array of JFIF data
FALSEif a JFIF segment could not be found
+ + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
Function:put_JFIF
Description: + Creates a new JFIF segment from an array of JFIF data in the + same format as would be retrieved from get_JFIF, and inserts + this segment into the supplied JPEG header array +
Parameters:jpeg_header_dataa JPEG header data array in the same format as from get_jpeg_header_data, into which the new JFIF segment will be put
new_JFIF_arraya JFIF information array in the same format as from get_JFIF, to create the new segment
Returns:jpeg_header_datathe JPEG header data array with the new JFIF segment added
+ + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
Function:Interpret_JFIF_to_HTML
Description: + Generates html showing the JFIF information contained in a JFIF data array, as retrieved with get_JFIF +
Parameters:JFIF_arraya JFIF data array, as from get_JFIF
filenamethe name of the JPEG file being processed (used by the script which displays the JFIF thumbnail)
Returns:outputthe HTML string
+ + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
Function:get_JFXX
Description: + Retrieves information from a JPEG File Interchange Format Extension (JFXX) + segment and returns it in an array. Uses information supplied by + the get_jpeg_header_data function +
Parameters:jpeg_header_dataa JPEG header data array in the same format as from get_jpeg_header_data
Returns:JFXX_dataan array of JFXX data
FALSEif a JFXX segment could not be found
+ + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
Function:put_JFXX
Description: + Creates a new JFXX segment from an array of JFXX data in the + same format as would be retrieved from get_JFXX, and inserts + this segment into the supplied JPEG header array +
Parameters:jpeg_header_dataa JPEG header data array in the same format as from get_jpeg_header_data, into which the new JFXX segment will be put
new_JFXX_arraya JFXX information array in the same format as from get_JFXX, to create the new segment
Returns:jpeg_header_datathe JPEG header data array with the new JFXX segment added
+ + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
Function:Interpret_JFXX_to_HTML
Description: + Generates html showing the JFXX thumbnail contained in a JFXX data array, as retrieved with get_JFXX +
Parameters:JFXX_arraya JFXX information array in the same format as from get_JFXX, to create the new segment
filenamethe name of the JPEG file being processed (used by the script which displays the JFXX thumbnail)
Returns:outputthe Html string
+ + +
+
+ +
+ + + \ No newline at end of file diff --git a/includes/jpeg_metadata_tk/documentation/jpeg.html b/includes/jpeg_metadata_tk/documentation/jpeg.html new file mode 100644 index 0000000..36a59a7 --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/jpeg.html @@ -0,0 +1,303 @@ + + + + + + + The PHP JPEG Metadata Toolkit - Documentation + + + +
+

The PHP JPEG Metadata Toolkit - Documentation

+
+ + Go to Documentation - Home + +
+ +

JPEG & Intrinsic JPEG Values Function Reference

+

+ Functions for performing basic operations on a JPEG file +

+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
Function:get_jpeg_header_data
Description: + Reads all the JPEG header segments from an JPEG image file into an array +
Parameters:filenamethe filename of the file to JPEG file to read
Returns:headerdataArray of JPEG header segments
FALSEif headers could not be read
+ + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Function:put_jpeg_header_data
Description: + Writes JPEG header data into a JPEG file. Takes an array in the + same format as from get_jpeg_header_data, and combines it with + the image data of an existing JPEG file, to create a new JPEG file
+
+ WARNING: As this function will replace all JPEG headers, + including SOF etc, it is best to read the jpeg headers + from a file, alter them, then put them back on the same + file. If a SOF segment wer to be transfered from one + file to another, the image could become unreadable unless + the images were idenical size and configuration +
Parameters:old_filenamethe JPEG file from which the image data will be retrieved
new_filenamethe name of the new JPEG to create (can be same as old_filename)
jpeg_header_dataa JPEG header data array in the same format as from get_jpeg_header_data
Returns:TRUEon Success
FALSEon Failure
+ + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
Function:get_jpeg_Comment
Description: + Retreives the contents of the JPEG Comment (COM = 0xFFFE) segment if one exists +
Parameters:jpeg_header_datathe JPEG header data, as retrieved from the get_jpeg_header_data function
Returns:stringContents of the Comment segement
FALSEif the comment segment couldnt be found
+ + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + +
Function:Interpret_Comment_to_HTML
Description: + Generates html showing the contents of any JPEG Comment segment +
Parameters:jpeg_header_datathe JPEG header data, as retrieved from the get_jpeg_header_data function
Returns:outputthe HTML
+ + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
Function:get_jpeg_intrinsic_values
Description: + Retreives information about the intrinsic characteristics of the jpeg image, such as Bits per Component, Height and Width. +
Parameters:jpeg_header_datathe JPEG header data, as retrieved from the get_jpeg_header_data function
Returns:arrayAn array containing the intrinsic JPEG values
FALSEif the comment segment couldnt be found
+ + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + +
Function:Interpret_intrinsic_values_to_HTML
Description: + Generates html showing some of the intrinsic JPEG values which were retrieved with the get_jpeg_intrinsic_values function +
Parameters:valuesthe JPEG intrinsic values, as read from get_jpeg_intrinsic_values
Returns:OutputStrA string containing the HTML
+ + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
Function:get_jpeg_image_data
Description: + Retrieves the compressed image data part of the JPEG file +
Parameters:filenamethe filename of the JPEG file to read
Returns:compressed_dataA string containing the compressed data
FALSEif retrieval failed
+ + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + +
Function:Generate_JPEG_APP_Segment_HTML
Description: + Generates html showing information about the Application (APP) segments which are present in the JPEG file +
Parameters:jpeg_header_datathe JPEG header data, as retrieved from the get_jpeg_header_data function
Returns:outputA string containing the HTML
+ + +
+
+ +
+ + + \ No newline at end of file diff --git a/includes/jpeg_metadata_tk/documentation/photoshop.html b/includes/jpeg_metadata_tk/documentation/photoshop.html new file mode 100644 index 0000000..c8c8009 --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/photoshop.html @@ -0,0 +1,262 @@ + + + + + + + The PHP JPEG Metadata Toolkit - Documentation + + + +
+

The PHP JPEG Metadata Toolkit - Documentation

+
+ + Go to Documentation - Home + +
+ +

Photoshop IRB & IPTC-NAA IIM Function Reference

+

+ The Photoshop Image Resource Block (IRB) resides in + the "App 13" segment of a JPEG image. + It contains photoshop information but can also contain the + IPTC-NAA IIM record. (International Press Telecommunications Council, Newspaper Association of America, Information Interchange Model) +

+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
Function:get_Photoshop_IRB
Description: + Retrieves the Photoshop Information Resource Block (IRB) information + from an App13 JPEG segment and returns it as an array. This may + include IPTC-NAA IIM Information. Uses information + supplied by the get_jpeg_header_data function +
Parameters:jpeg_header_dataa JPEG header data array in the same format as from get_jpeg_header_data
Returns:IRBdataThe array of Photoshop IRB records
FALSEif an APP 13 Photoshop IRB segment could not be found, or if an error occured
+ + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Function:put_Photoshop_IRB
Description: + Adds or modifies the Photoshop Information Resource Block (IRB) + information from an App13 JPEG segment. If a Photoshop IRB already + exists, it is replaced, otherwise a new one is inserted, using the + supplied data. Uses information supplied by the get_jpeg_header_data + function +
Parameters:jpeg_header_dataa JPEG header data array in the same format as from get_jpeg_header_data
new_IRB_data + an array of the data to be stored in the Photoshop + IRB segment. Should be in the same format as received + from get_Photoshop_IRB +
Returns:jpeg_header_datathe JPEG header data array with the Photoshop IRB added.
FALSEif an error occured
+ + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
Function:get_Photoshop_IPTC
Description: + Retrieves IPTC-NAA IIM information from within a Photoshop + IRB (if it is present) and returns it in an array. Uses + information supplied by the get_jpeg_header_data function +
Parameters:Photoshop_IRB_dataan array of Photoshop IRB records, as returned from get_Photoshop_IRB
Returns:IPTC_Data_OutThe array of IPTC-NAA IIM records
FALSEif an IPTC-NAA IIM record could not be found, or if an error occured
+ + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
Function:put_Photoshop_IPTC
Description: + Inserts a new IPTC-NAA IIM resource into a Photoshop + IRB, or replaces an the existing resource if one is present. + Uses information supplied by the get_Photoshop_IRB function +
Parameters:Photoshop_IRB_dataan array of Photoshop IRB records, as returned from get_Photoshop_IRB, into which the IPTC-NAA IIM record will be inserted
new_IPTC_blockan array of IPTC-NAA records in the same format as those returned by get_Photoshop_IPTC
Returns:Photoshop_IRB_dataThe Photoshop IRB array with the IPTC-NAA IIM resource inserted
+ + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Function:Interpret_IRB_to_HTML
Description: + Generates html showing the information contained in a Photoshop + IRB data array, as retrieved with get_Photoshop_IRB, including + any IPTC-NAA IIM records found.
+
+ Note: The following resource numbers are not currently + decoded: ( Many of these do not apply to JPEG images)
+ 0x03E9, 0x03EE, 0x03EF, 0x03F0, 0x03F1, 0x03F2, 0x03F6, 0x03F9, + 0x03FA, 0x03FB, 0x03FD, 0x03FE, 0x0400, 0x0401, 0x0402, 0x0405, + 0x040E, 0x040F, 0x0410, 0x0412, 0x0413, 0x0415, 0x0416, 0x0417, + 0x041B, 0x041C, 0x041D, 0x0BB7
+
+ ( Also these Obsolete resource numbers)
+ 0x03E8, 0x03EB, 0x03FC, 0x03FF, 0x0403 +
Parameters:IRB_arraya Photoshop IRB data array as from get_Photoshop_IRB
filenamethe name of the JPEG file being processed (used by the script which displays the Photoshop thumbnail)
Returns:output_strthe HTML string
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + +
Function:Interpret_IPTC_to_HTML
Description: + Generates html detailing the contents a IPTC-NAA IIM array which was retrieved with the get_Photoshop_IPTC function +
Parameters:IPTC_infothe IPTC-NAA IIM array,as read from get_IPTC
Returns:OutputStrA string containing the HTML
+ + +
+
+ + +
+ + + \ No newline at end of file diff --git a/includes/jpeg_metadata_tk/documentation/photoshop_file_info.html b/includes/jpeg_metadata_tk/documentation/photoshop_file_info.html new file mode 100644 index 0000000..35e5f5a --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/photoshop_file_info.html @@ -0,0 +1,162 @@ + + + + + + + The PHP JPEG Metadata Toolkit - Documentation + + + +
+

The PHP JPEG Metadata Toolkit - Documentation

+
+ + Go to Documentation - Home + +
+ +

Photoshop File Info Function Reference

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Function:get_photoshop_file_info
Description: + Retrieves Photoshop 'File Info' metadata in the same way that Photoshop + does. The results are returned in an array as below:
+
+                $file_info_array = array(
+                        "title"                  => "",
+                        "author"                 => "",
+                        "authorsposition"        => "",      // Note: Not used in Photoshop 7 or higher
+                        "caption"                => "",
+                        "captionwriter"          => "",
+                        "jobname"                => "",      // Note: Not used in Photoshop CS
+                        "copyrightstatus"        => "",
+                        "copyrightnotice"        => "",
+                        "ownerurl"               => "",
+                        "keywords"               => array( 0 => "", 1 => "", ... ),
+                        "category"               => "",     // Note: Max 3 characters
+                        "supplementalcategories" => array( 0 => "", 1 => "", ... ),
+                        "date"                   => "",     // Note: DATE MUST BE IN YYYY-MM-DD format
+                        "city"                   => "",
+                        "state"                  => "",
+                        "country"                => "",
+                        "credit"                 => "",
+                        "source"                 => "",
+                        "headline"               => "",
+                        "instructions"           => "",
+                        "transmissionreference"  => "",
+                        "urgency"                => "" );
+                                        
+
Parameters:Exif_arrayan array containing the EXIF information to be searched, as retrieved by get_EXIF_JPEG.
XMP_arrayan array containing the XMP information to be searched, as retrieved by read_XMP_array_from_text.
IRB_arrayan array containing the Photoshop IRB information to be searched, as retrieved by get_Photoshop_IRB.
Returns:outputarrayan array as above, containing the Photoshop File Info data
+ + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Function:put_photoshop_file_info
Description: + Stores Photoshop "File Info" metadata in the same way that Photoshop + does. The "File Info" metadata must be in an array similar to that + returned by get_photoshop_file_info, as follows:
+
+                $file_info_array = array(
+                        "title"                  => "",
+                        "author"                 => "",
+                        "authorsposition"        => "",      // Note: Not used in Photoshop 7 or higher
+                        "caption"                => "",
+                        "captionwriter"          => "",
+                        "jobname"                => "",      // Note: Not used in Photoshop CS
+                        "copyrightstatus"        => "",
+                        "copyrightnotice"        => "",
+                        "ownerurl"               => "",
+                        "keywords"               => array( 0 => "", 1 => "", ... ),
+                        "category"               => "",     // Note: Max 3 characters
+                        "supplementalcategories" => array( 0 => "", 1 => "", ... ),
+                        "date"                   => "",     // Note: DATE MUST BE IN YYYY-MM-DD format
+                        "city"                   => "",
+                        "state"                  => "",
+                        "country"                => "",
+                        "credit"                 => "",
+                        "source"                 => "",
+                        "headline"               => "",
+                        "instructions"           => "",
+                        "transmissionreference"  => "",
+                        "urgency"                => "" );
+                                        
+
Parameters:jpeg_header_dataa JPEG header data array in the same format as from get_jpeg_header_data. + This contains the header information which is to be updated.
new_ps_file_info_arrayAn array as above, which contains the "File Info" metadata information to be written.
Old_Exif_arrayan array containing the EXIF information to be updated, as retrieved by get_EXIF_JPEG.
Old_XMP_arrayan array containing the XMP information to be updated, as retrieved by read_XMP_array_from_text.
Old_IRB_arrayan array containing the Photoshop IRB information to be updated, as retrieved by get_Photoshop_IRB.
Returns:jpeg_header_dataa JPEG header data array in the same format as from get_jpeg_header_data, + containing the Photshop "File Info" metadata. + This can then be written to a file using put_jpeg_header_data.
FALSEIf an error occured
+ +
+
+ + \ No newline at end of file diff --git a/includes/jpeg_metadata_tk/documentation/picture_info.html b/includes/jpeg_metadata_tk/documentation/picture_info.html new file mode 100644 index 0000000..44ab6aa --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/picture_info.html @@ -0,0 +1,133 @@ + + + + + + + The PHP JPEG Metadata Toolkit - Documentation + + + +
+

The PHP JPEG Metadata Toolkit - Documentation

+
+ + Go to Documentation - Home + +
+ +

Picture Info Function Reference

+

+ The "App 12" segment is used by many older digital cameras, and contains + text called "Picture Info" +

+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
Function:get_jpeg_App12_Pic_Info
Description: + Retrieves the Picture Info text information from an App12 + JPEG segment and returns it as a string. Uses information + supplied by the get_jpeg_header_data function +
Parameters:jpeg_header_dataa JPEG header data array in the same format as from get_jpeg_header_data
Returns:App12_Head, App12_TextThe text preceeding the Picture Info (often the camera manufacturer's name), plus the Picture Info Text
FALSE, FALSEif an APP 12 Picture Info segment could not be found
+ + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Function:put_jpeg_App12_Pic_Info
Description: + Writes Picture Info text into an App12 JPEG segment. Uses information + supplied by the get_jpeg_header_data function. If no App12 exists + already a new one is created, otherwise it replaces the old one +
Parameters:jpeg_header_dataa JPEG header data array in the same format as from get_jpeg_header_data
new_Pic_Info_TextThe Picture Info Text, including any header that is required
Returns:jpeg_header_datathe JPEG header array with the new Picture info segment inserted
FALSEif an error occured
+ + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + +
Function:Interpret_App12_Pic_Info_to_HTML
Description: + Generates html showing the contents of any JPEG App12 Picture Info segment +
Parameters:jpeg_header_datathe JPEG header data, as retrieved from the get_jpeg_header_data function
Returns:outputthe HTML
+ + +
+
+ + +
+ + + \ No newline at end of file diff --git a/includes/jpeg_metadata_tk/documentation/style.css b/includes/jpeg_metadata_tk/documentation/style.css new file mode 100644 index 0000000..ae5ccff --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/style.css @@ -0,0 +1,29 @@ +body { + margin:0 0 0 0; + background: #A4C7A0; +} + +.headerbar { background:#505060; + color:#C0C070; + } +.maintext { margin-left:10%; + margin-right:10%; + } + + +a:link { + color: #00f; + background: transparent; +} + +a:visited { + color: #800080; + background: transparent; +} + +a:active { + color: green; + background: #FFD700 +} + + diff --git a/includes/jpeg_metadata_tk/documentation/tiffexample.html b/includes/jpeg_metadata_tk/documentation/tiffexample.html new file mode 100644 index 0000000..5e47aaf --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/tiffexample.html @@ -0,0 +1,57 @@ + + + + + + + The PHP JPEG Metadata Toolkit - Documentation + + + +
+

The PHP JPEG Metadata Toolkit - Documentation

+
+ + Go to Documentation - Examples + +
+ +

Example TIFF Metadata Display Script

+ +

+ This example script is included with the PHP JPEG Metadata Toolkit. It shows how the library + can be used to display detailed metadata from a TIFF image file. This is an explanation of some of the + commands used in the script. +

+ +
+
+ + Click here to see this script + +
+
+
+

This script is essentially a subset of Example.php

+
+
+
+ +

TIFF EXIF

+

+ The only metadata that can be stored in a TIFF file is TIFF/EXIF information. + Displaying this information metadata in HTML format from a PHP script is very simple: +

+
+
+                echo Interpret_EXIF_to_HTML( get_EXIF_TIFF( $filename ), $filename );
+
+                
+ +
+
+ +
+ + + \ No newline at end of file diff --git a/includes/jpeg_metadata_tk/documentation/todo.html b/includes/jpeg_metadata_tk/documentation/todo.html new file mode 100644 index 0000000..8d4e5d7 --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/todo.html @@ -0,0 +1,65 @@ + + + + + + + The PHP JPEG Metadata Toolkit - Documentation + + + +
+

The PHP JPEG Metadata Toolkit - Documentation

+
+ + Go to Documentation - Home + +
+ +

Further Work Required

+ +

+ If you would like to contribute, and extend the toolkit, here are some + things that are yet to be done +

+ +
    +
  • Obtain format specifications for more Makernotes
  • +
  • Find out definitions of Print Image Matching Info tags
  • +
  • Obtain a copy of the Photoshop CS File Format Specification
  • +
  • Find out what the adobe-xap-filters tag means in XMP
  • +
  • Fully Test Functions to write EXIF data
  • +
  • Test the use of EXIF functions on TIFF Files - only JPEG's tested so far
  • +
  • Figure out a way to allow EXIF to function normally with HTTP and FTP wrappers
  • +
  • Implement decoding of Adobe segment
  • +
  • Find definition of Ducky segment and implement decoding
  • +
  • Implement decoding of Apple plist segment
  • +
  • Implement decoding of ICC Profiles
  • +
  • Implement EXIF decoding of Device Setting Description field
  • +
  • Implement EXIF decoding of SpatialFrequencyResponse field
  • +
  • Implement EXIF decoding of User comment
  • +
  • Implement EXIF decoding of OECF field
  • +
  • Implement EXIF decoding of SubjectArea field
  • +
  • Implement EXIF decoding of IFD datatype Float
  • +
  • Implement EXIF decoding of IFD datatype Double
  • +
  • Test those IPTC fields which are not used by Photoshop
  • +
  • Implement support for IPTC Extended Dataset record
  • +
  • Implement decoding of IPTC record 1:90 ( Coded Character Set )
  • +
  • Implement Display of Rasterised Caption for IPTC record 2:125
  • +
  • Implement JFIF Thumbnail display
  • +
  • Implement JFXX one and three bytes per pixel thumbnail decoding
  • +
  • Implement decoding for the many Photoshop IRB resources which are currently not supported
  • +
  • Find out what Photoshop IRB resources 1061, 1062 & 1064 are
  • +
  • Test whether the URL List field in Photoshop IRB works - No sample files available
  • +
  • Test get_Photoshop_IRB and put_Photoshop_IRB with multiple APP13 segments
  • +
  • Test the Unicode UTF-16 functions that have not been tested fully
  • +
  • Test the many RDF items that have not yet been tested - only those used by photoshop 7.0 and CS have been tested
  • +
  • Implement decoding of One Byte Per Pixel encoded JFXX Thumbnail
  • +
  • Implement decoding of Three Bytes Per Pixel encoded JFXX Thumbnail
  • +
+ +
+
+
+ + \ No newline at end of file diff --git a/includes/jpeg_metadata_tk/documentation/xmp.html b/includes/jpeg_metadata_tk/documentation/xmp.html new file mode 100644 index 0000000..894546f --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/xmp.html @@ -0,0 +1,228 @@ + + + + + + + The PHP JPEG Metadata Toolkit - Documentation + + + +
+

The PHP JPEG Metadata Toolkit - Documentation

+
+ + Go to Documentation - Home + +
+ +

XMP / RDF / Dublin Core Function Reference

+

+ The Extensible Metadata Platform (XMP) information resides in + the "App 1" segment of a JPEG image. + An XMP segment is XML based and contains + Resource Description Framework (RDF) data, which itself can + contain the Dublin Core Metadata Initiative (DCMI) information. +

+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
Function:get_XMP_text
Description: + Retrieves the Extensible Metadata Platform (XMP) information + from an App1 JPEG segment and returns the raw XML text as a + string. This includes the Resource Description Framework (RDF) + information and may include Dublin Core Metadata Initiative (DCMI) + information. Uses information supplied by the get_jpeg_header_data + function +
Parameters:jpeg_header_dataa JPEG header data array in the same format as from get_jpeg_header_data
Returns:xmp_datathe string of raw XML text
FALSEif an APP 1 XMP segment could not be found, or if an error occured
+ + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Function:put_XMP_text
Description: + Adds or modifies the Extensible Metadata Platform (XMP) information + in an App1 JPEG segment. If a XMP segment already exists, it is + replaced, otherwise a new one is inserted, using the supplied data. + Uses information supplied by the get_jpeg_header_data function +
Parameters:jpeg_header_dataa JPEG header data array in the same format as from get_jpeg_header_data
newXMPa string containing the XMP text to be stored in the XMP segment. Should be constructed using the write_XMP_array_to_text function
Returns:jpeg_header_datathe JPEG header data array with the XMP segment added.
FALSEif an error occured
+ + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
Function:read_XMP_array_from_text
Description: +

+ An alias for read_xml_array_from_text.
+ Parses a string containing XMP data (XML), and returns the resulting + tree structure array, which contains all the XMP (XML) information.
+ Note: White space and comments in the XMP data (XML) are ignored
+ Note: All text information contained in the tree structure
+ is encoded as Unicode UTF-8. Hence text will appear as + normal ASCII except where there is an extended character. +

+
Parameters:xmptexta string containing the XMP data (XML) to be parsed
Returns:outputthe tree structure array containing the XMP (XML) information
FALSEif an error occured
+ + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + +
Function:write_XMP_array_to_text
Description: +

+ Takes a tree structure array containing XMP (in the same format + as returned by read_XMP_array_from_text, and constructs a string + containing the equivalent XMP, including the XMP Packet header + and trailer. Produces XMP text which has correct indents, encoded + using UTF-8.
+ Note: All text information contained in the tree structure + can be either 7-bit ASCII or encoded as Unicode UTF-8, + since UTF-8 passes 7-bit ASCII text unchanged. +

+
Parameters:xmparraythe tree structure array containing the information to be converted to XMP text
Returns:output_XMP_textthe string containing the equivalent XMP text
+ + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + +
Function:Interpret_XMP_to_HTML
Description: +

+ Generates html showing the information contained in an Extensible + Metadata Platform (XMP) tree structure array, as retrieved + with read_XMP_array_from_text +

+
Parameters:XMP_arraya XMP tree structure array as from read_XMP_array_from_text
Returns:outputthe HTML string
+ + +
+
+
+
+ + +
+ + + \ No newline at end of file diff --git a/includes/jpeg_metadata_tk/get_JFXX_thumb.php b/includes/jpeg_metadata_tk/get_JFXX_thumb.php new file mode 100644 index 0000000..801417d --- /dev/null +++ b/includes/jpeg_metadata_tk/get_JFXX_thumb.php @@ -0,0 +1,109 @@ + +* +* Author: Evan Hunter +* +* Date: 23/7/2004 +* +* Project: PHP JPEG Metadata Toolkit +* +* Revision: 1.00 +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* +* License: This file is part of the PHP JPEG Metadata Toolkit. +* +* The PHP JPEG Metadata Toolkit is free software; you can +* redistribute it and/or modify it under the terms of the +* GNU General Public License as published by the Free Software +* Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* The PHP JPEG Metadata Toolkit is distributed in the hope +* that it will be useful, but WITHOUT ANY WARRANTY; without +* even the implied warranty of MERCHANTABILITY or FITNESS +* FOR A PARTICULAR PURPOSE. See the GNU General Public License +* for more details. +* +* You should have received a copy of the GNU General Public +* License along with the PHP JPEG Metadata Toolkit; if not, +* write to the Free Software Foundation, Inc., 59 Temple +* Place, Suite 330, Boston, MA 02111-1307 USA +* +* If you require a different license for commercial or other +* purposes, please contact the author: evan@ozhiker.com +* +******************************************************************************/ + + + // Ensure that nothing can write to the standard io, before we get the header out + ob_start( ); + + + require_once 'JPEG.php'; + require_once 'JFIF.php'; + + + // retrieve the filename from the URL parameters + + $filename = $GLOBALS['HTTP_GET_VARS']['filename']; + + // Retrieve the JPEG header Data + + $jpeg_header_data = get_jpeg_header_data( $filename ); + + // Retrieve any JFXX data in the file + + $JFXX_array = get_JFXX( $jpeg_header_data ); + + // Check if JFXX data was retrieved + + if ( $JFXX_array === FALSE ) + { + // No JFXX data could be retrieved - abort + ob_end_clean ( ); + echo "

JFXX Data could not be retrieved

\n"; + return; + } + + // Check the JFXX extension code which indicates what format + // the thumbnail is encoded with + + if ( $JFXX_array['Extension_Code'] == 0x10 ) // JPEG Encoding + { + // JPEG Encoding - Output JPEG Data + ob_end_clean ( ); + header("Content-type: image/jpeg"); + print $JFXX_array['ThumbData']; + return; + } + else if ( $JFXX_array['Extension_Code'] == 0x11 ) // One Byte Per Pixel Encoding + { + // TODO: Implement decoding of One Byte Per Pixel encoded JFXX Thumbnail + return; + } + else if ( $JFXX_array['Extension_Code'] == 0x13 ) // Three Bytes Per Pixel Encoding + { + // TODO: Implement decoding of Three Bytes Per Pixel encoded JFXX Thumbnail + return; + } + else + { + // Invalid Extension Value - abort + return; + } + + + +?> diff --git a/includes/jpeg_metadata_tk/get_casio_thumb.php b/includes/jpeg_metadata_tk/get_casio_thumb.php new file mode 100644 index 0000000..f75b558 --- /dev/null +++ b/includes/jpeg_metadata_tk/get_casio_thumb.php @@ -0,0 +1,126 @@ + +* +* Author: Evan Hunter +* +* Date: 23/7/2004 +* +* Project: PHP JPEG Metadata Toolkit +* +* Revision: 1.00 +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* +* License: This file is part of the PHP JPEG Metadata Toolkit. +* +* The PHP JPEG Metadata Toolkit is free software; you can +* redistribute it and/or modify it under the terms of the +* GNU General Public License as published by the Free Software +* Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* The PHP JPEG Metadata Toolkit is distributed in the hope +* that it will be useful, but WITHOUT ANY WARRANTY; without +* even the implied warranty of MERCHANTABILITY or FITNESS +* FOR A PARTICULAR PURPOSE. See the GNU General Public License +* for more details. +* +* You should have received a copy of the GNU General Public +* License along with the PHP JPEG Metadata Toolkit; if not, +* write to the Free Software Foundation, Inc., 59 Temple +* Place, Suite 330, Boston, MA 02111-1307 USA +* +* If you require a different license for commercial or other +* purposes, please contact the author: evan@ozhiker.com +* +******************************************************************************/ + + // Ensure that nothing can write to the standard io, before we get the header out + ob_start( ); + + + require_once 'EXIF.php'; + + + // retrieve the filename from the URL parameters + + $filename = $GLOBALS['HTTP_GET_VARS']['filename']; + + + // Retrieve any EXIF data in the file + + $Exif_array = get_EXIF_JPEG( $filename ); + + // Check if any EXIF data was retrieved + if ( $Exif_array === FALSE ) + { + // No EXIF data was found - abort + ob_end_clean ( ); + echo "

Error getting EXIF Information

\n"; + return; + } + + + // Check that there is at least the Zeroth IFD in the array + if ( count( $Exif_array ) < 1 ) + { + // Couldn't find the zeroth IFD + return; + } + + + // Check that the EXIF IFD exists + if ( array_key_exists( 34665, $Exif_array[0] ) ) + { + // Found the EXIF IFD, + + // Check that the makernote tag exists + if ( array_key_exists( 37500, $Exif_array[0][34665]['Data'][0] ) ) + { + // Makernote Exists + + // Check that the Makernote is Casio Type 2 + if ( $Exif_array[0][34665]['Data'][0][37500]['Makernote Tags'] == "Casio Type 2" ) + { + // Makernote is Casio Type 2 + // Check if an IFD exists for the makernote + if ( array_key_exists( 0, $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'] ) ) + { + // Check if the Thumbnail offset tag 8192 exists + if ( array_key_exists( 8192, $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'][0] ) ) + { + // Found Thumbnail - Display it + ob_end_clean ( ); + header("Content-type: image/jpeg"); + print $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'][0][8192]['Data']; + } + // Check if the Thumbnail offset tag 4 exists + else if ( array_key_exists( 4, $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'][0] ) ) + { + // Found Thumbnail - Display it + ob_end_clean ( ); + header("Content-type: image/jpeg"); + print $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'][0][4]['Data']; + } + } + + } + } + } + else + { + } + + return; + +?> diff --git a/includes/jpeg_metadata_tk/get_exif_thumb.php b/includes/jpeg_metadata_tk/get_exif_thumb.php new file mode 100644 index 0000000..0f40090 --- /dev/null +++ b/includes/jpeg_metadata_tk/get_exif_thumb.php @@ -0,0 +1,98 @@ + +* +* Author: Evan Hunter +* +* Date: 23/7/2004 +* +* Project: PHP JPEG Metadata Toolkit +* +* Revision: 1.00 +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* +* License: This file is part of the PHP JPEG Metadata Toolkit. +* +* The PHP JPEG Metadata Toolkit is free software; you can +* redistribute it and/or modify it under the terms of the +* GNU General Public License as published by the Free Software +* Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* The PHP JPEG Metadata Toolkit is distributed in the hope +* that it will be useful, but WITHOUT ANY WARRANTY; without +* even the implied warranty of MERCHANTABILITY or FITNESS +* FOR A PARTICULAR PURPOSE. See the GNU General Public License +* for more details. +* +* You should have received a copy of the GNU General Public +* License along with the PHP JPEG Metadata Toolkit; if not, +* write to the Free Software Foundation, Inc., 59 Temple +* Place, Suite 330, Boston, MA 02111-1307 USA +* +* If you require a different license for commercial or other +* purposes, please contact the author: evan@ozhiker.com +* +******************************************************************************/ + + // Ensure that nothing can write to the standard io, before we get the header out + ob_start( ); + + + require_once 'JPEG.php'; + require_once 'EXIF.php'; + + + // retrieve the filename from the URL parameters + + $filename = $GLOBALS['HTTP_GET_VARS']['filename']; + + // Retrieve any EXIF data in the file + + $Exif_array = get_EXIF_JPEG( $filename ); + + // Check if EXIF data was retrieved + + if ( $Exif_array === FALSE ) + { + // No EXIF data could be retrieved - abort + ob_end_clean ( ); + echo "

EXIF segment could not be retrieved

\n"; + return; + } + + + // Check if the First IFD exists ( The First IFD is actually the second, since there is also the zeroth IFD) + if ( count( $Exif_array ) < 2 ) + { + ob_end_clean ( ); + echo "

Couldn't find Thumbnail IFD

\n"; + return; + } + + // Check if the First IFD contains the Thumbnail tag + if ( array_key_exists( 513, $Exif_array[1] ) ) + { + // Output the thumbnail + ob_end_clean ( ); + header("Content-type: image/jpeg"); + print $Exif_array[1][513]['Data']; + } + else + { + ob_end_clean ( ); + echo "

Couldn't find Thumbnail Tag

\n"; + return; + } + +?> diff --git a/includes/jpeg_metadata_tk/get_minolta_thumb.php b/includes/jpeg_metadata_tk/get_minolta_thumb.php new file mode 100644 index 0000000..eecea8a --- /dev/null +++ b/includes/jpeg_metadata_tk/get_minolta_thumb.php @@ -0,0 +1,195 @@ + +* +* Author: Evan Hunter +* +* Date: 23/7/2004 +* +* Project: PHP JPEG Metadata Toolkit +* +* Revision: 1.00 +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* +* License: This file is part of the PHP JPEG Metadata Toolkit. +* +* The PHP JPEG Metadata Toolkit is free software; you can +* redistribute it and/or modify it under the terms of the +* GNU General Public License as published by the Free Software +* Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* The PHP JPEG Metadata Toolkit is distributed in the hope +* that it will be useful, but WITHOUT ANY WARRANTY; without +* even the implied warranty of MERCHANTABILITY or FITNESS +* FOR A PARTICULAR PURPOSE. See the GNU General Public License +* for more details. +* +* You should have received a copy of the GNU General Public +* License along with the PHP JPEG Metadata Toolkit; if not, +* write to the Free Software Foundation, Inc., 59 Temple +* Place, Suite 330, Boston, MA 02111-1307 USA +* +* If you require a different license for commercial or other +* purposes, please contact the author: evan@ozhiker.com +* +******************************************************************************/ + + + // Ensure that nothing can write to the standard io, before we get the header out + ob_start( ); + + + require_once 'EXIF.php'; + + + // retrieve the filename from the URL parameters + + $filename = $GLOBALS['HTTP_GET_VARS']['filename']; + + // Retrieve any EXIF data in the file + + $Exif_array = get_EXIF_JPEG( $filename ); + + + // Check if any EXIF data was retrieved + if ( $Exif_array === FALSE ) + { + // No EXIF data was found - abort + ob_end_clean ( ); + echo "

Error getting EXIF Information

\n"; + return; + } + + + // Check that there is at least the Zeroth IFD in the array + if ( count( $Exif_array ) < 1 ) + { + ob_end_clean ( ); + echo "

Couldn't find TIFF IFD 0

\n"; + return; + } + + + + // Check that the EXIF IFD exists + if ( array_key_exists( 34665, $Exif_array[0] ) ) + { + // Found the EXIF IFD, + + // Check that the makernote tag exists + if ( array_key_exists( 37500, $Exif_array[0][34665]['Data'][0] ) ) + { + // Makernote Exists + + // Check that the Makernote is Olympus + if ( $Exif_array[0][34665]['Data'][0][37500]['Makernote Tags'] == "Olympus" ) + { + // Makernote is Olympus + // Check if an IFD exists for the makernote + if ( array_key_exists( 0, $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'] ) ) + { + // Check if the Thumbnail tag 0x0088 exists + if ( array_key_exists( 0x0088, $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'][0] ) ) + { + // Found a Thumbnail + // Get the data + $data = $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'][0][0x0088]['Data']; + + // Sometimes the Minolta thumbnails are corrupt as there is no data + // Check that the data is OK + if ( $data !== FALSE ) + { + // Minolta thumbnails seem to have the first byte incorrect - this could possibly be a counter in case the thumbnail needs to span more than one tag + // Restore the first byte of the jpeg thumbnail + $data{0} = "\xff"; + + // Display the thumbnail + ob_end_clean ( ); + header("Content-type: image/jpeg"); + print $data; + } + else + { + // Thumbnail data is missing - display message + ob_end_clean ( ); + echo "

Thumbnail missing

\n"; + } + } + // Check if the Thumbnail tag 0x0081 exists + else if ( array_key_exists( 0x0081, $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'][0] ) ) + { + // Found a Thumbnail + // Get the data + $data = $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'][0][0x0081]['Data']; + + // Sometimes the Minolta thumbnails are corrupt as there is no data + // Check that the data is OK + if ( $data !== FALSE ) + { + // Minolta thumbnails seem to have the first byte incorrect - this could possibly be a counter in case the thumbnail needs to span more than one tag + // Restore the first byte of the jpeg thumbnail + $data{0} = "\xff"; + + // Display the thumbnail + ob_end_clean ( ); + header("Content-type: image/jpeg"); + print $data ; + } + else + { + // Thumbnail data is missing - display message + ob_end_clean ( ); + echo "

Thumbnail missing

\n"; + } + } + else + { + // Couldn't find a Minolta thumbnail tag - display message + ob_end_clean ( ); + echo "

Couldn't find Minolta Thumbnail Tag

\n"; + } + } + else + { + // Couldn't find an IFD in the Makernote tag - display message + ob_end_clean ( ); + echo "

Makernote Doesn't contain IFD 0

\n"; + } + + } + else + { + // Makernote does not use Olympus tags - display message + ob_end_clean ( ); + echo "

Not an Olympus Makernote

\n"; + } + } + else + { + // Couldn't find Makernote tag - display message + ob_end_clean ( ); + echo "

Couldn't find Makernote

\n"; + } + } + else + { + // Couldn't find the EXIF IFD - display message + ob_end_clean ( ); + echo "

Couldn't find Exif IFD

\n"; + } + + + return; + +?> diff --git a/includes/jpeg_metadata_tk/get_ps_thumb.php b/includes/jpeg_metadata_tk/get_ps_thumb.php new file mode 100644 index 0000000..966c328 --- /dev/null +++ b/includes/jpeg_metadata_tk/get_ps_thumb.php @@ -0,0 +1,176 @@ + +* +* Author: Evan Hunter +* +* Date: 23/7/2004 +* +* Project: PHP JPEG Metadata Toolkit +* +* Revision: 1.11 +* +* 1.00 -> 1.11 : Added support for Photoshop IRB thumbnails which are +* embedded within EXIF information (used in TIFF files) +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* +* License: This file is part of the PHP JPEG Metadata Toolkit. +* +* The PHP JPEG Metadata Toolkit is free software; you can +* redistribute it and/or modify it under the terms of the +* GNU General Public License as published by the Free Software +* Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* The PHP JPEG Metadata Toolkit is distributed in the hope +* that it will be useful, but WITHOUT ANY WARRANTY; without +* even the implied warranty of MERCHANTABILITY or FITNESS +* FOR A PARTICULAR PURPOSE. See the GNU General Public License +* for more details. +* +* You should have received a copy of the GNU General Public +* License along with the PHP JPEG Metadata Toolkit; if not, +* write to the Free Software Foundation, Inc., 59 Temple +* Place, Suite 330, Boston, MA 02111-1307 USA +* +* If you require a different license for commercial or other +* purposes, please contact the author: evan@ozhiker.com +* +******************************************************************************/ + + // Ensure that nothing can write to the standard io, before we get the header out + ob_start( ); + + + // retrieve the filename from the URL parameters + + $filename = $GLOBALS['HTTP_GET_VARS']['filename']; + + // Change: Check for file extension rather than assuming JPEG as of 1.11 + // Retrieve the Filename Extension + $path_parts = pathinfo( $filename ); + + // Check if the Extension is JPEG + if ( ( strcasecmp( $path_parts["extension"], "jpg" ) == 0 ) || + ( strcasecmp( $path_parts["extension"], "jpeg" ) == 0 ) ) + { + // JPEG Extension + + require_once 'JPEG.php'; + require_once 'Photoshop_IRB.php'; + + // Retrieve the JPEG header Data + + $jpeg_header_data = get_jpeg_header_data( $filename ); + + // Retrieve any Photoshop IRB data in the file + + $IRB_array = get_Photoshop_IRB( $jpeg_header_data ); + + // Check if Photoshop IRB data was retrieved + + if ( $IRB_array === FALSE ) + { + // No Photoshop IRB data could be retrieved - abort + ob_end_clean ( ); + echo "

Photoshop IRB could not be retrieved from the JPEG file

\n"; + return; + } + + // Cycle through the resources in the Photoshop IRB + // Until either a thumbnail resource is found or + // there are no more resources + $i = 0; + while ( ( $i < count( $IRB_array ) ) && + ( $IRB_array[$i]['ResID'] != 0x0409 ) && + ( $IRB_array[$i]['ResID'] != 0x040C ) ) + { + $i++; + } + + + // Check if a thumbnail was found + if ( $i < count( $IRB_array ) ) + { + // A thumbnail was found, Display it + ob_end_clean ( ); + header("Content-type: image/jpeg"); + print substr( $IRB_array[$i]['ResData'] , 28 ); + } + } + // Change: Add support for TIFF Photoshop IRB thumbnails as of 1.11 + // Check if file has TIFF extension + else if ( ( strcasecmp( $path_parts["extension"], "tif" ) == 0 ) || + ( strcasecmp( $path_parts["extension"], "tiff" ) == 0 ) ) + { + // TIFF Extension + + require_once 'EXIF.php'; + + // Retrieve the EXIF info + $exif_array = get_EXIF_TIFF( $filename ); + + // Retrieve any Photoshop IRB data in the EXIF + if ( ( array_key_exists( 0, $exif_array ) ) && + ( array_key_exists( 34377, $exif_array[0] ) ) && + ( array_key_exists( 'Data', $exif_array[0][34377] ) ) ) + { + $IRB_array = $exif_array[0][34377]['Data']; + + // Check if Photoshop IRB data was retrieved + + if ( $IRB_array === FALSE ) + { + // No Photoshop IRB data could be retrieved - abort + ob_end_clean ( ); + echo "

Photoshop IRB could not be retrieved from the TIFF file

\n"; + return; + } + + // Cycle through the resources in the Photoshop IRB + // Until either a thumbnail resource is found or + // there are no more resources + $i = 0; + while ( ( $i < count( $IRB_array ) ) && + ( $IRB_array[$i]['ResID'] != 0x0409 ) && + ( $IRB_array[$i]['ResID'] != 0x040C ) ) + { + $i++; + } + + + // Check if a thumbnail was found + if ( $i < count( $IRB_array ) ) + { + // A thumbnail was found, Display it + ob_end_clean ( ); + header("Content-type: image/jpeg"); + print substr( $IRB_array[$i]['ResData'] , 28 ); + } + } + else + { + // Embedded Photoshop IRB block not found + ob_end_clean ( ); + echo "No Photoshop IRB found within EXIF"; + } + } + else + { + // Unknown extension + ob_end_clean ( ); + echo "Unknown file Type"; + } + + +?> diff --git a/includes/jpeg_metadata_tk/pjmt_utils.php b/includes/jpeg_metadata_tk/pjmt_utils.php new file mode 100644 index 0000000..07579b3 --- /dev/null +++ b/includes/jpeg_metadata_tk/pjmt_utils.php @@ -0,0 +1,150 @@ + \ No newline at end of file diff --git a/includes/mailman.cfg b/includes/mailman.cfg new file mode 100644 index 0000000..4a087e3 --- /dev/null +++ b/includes/mailman.cfg @@ -0,0 +1,15 @@ +private_roster = 2 +send_reminders = False +max_message_size = 0 +convert_html_to_plaintext = False +default_member_moderation = True +generic_nonmember_action = 3 +msg_footer = """_______________________________________________ +%(real_name)s mailing list +%(real_name)s@%(host_name)s +""" +digest_footer = """_______________________________________________ +%(real_name)s mailing list +%(real_name)s@%(host_name)s +""" +mlist.Save() diff --git a/includes/mailman_lib.php b/includes/mailman_lib.php new file mode 100644 index 0000000..e654ebf --- /dev/null +++ b/includes/mailman_lib.php @@ -0,0 +1,366 @@ + spider +*/ + +function mailman_verify_list( $pListName ) { + $error = NULL; + if( $matches = preg_match( '/[^A-Za-z0-9\-\_]/', $pListName ) ) { + $error = tra( 'Invalid mailing list name' ).': '.tra( 'List names can only contain letters and numbers' ); + } else { + $lists = mailman_list_lists(); + if( !empty( $lists[strtolower($pListName)] ) ) { + $error = tra( 'Invalid mailing list name' ).': '.tra( 'List already exists' ); + } + } + return $error; +} + +function mailman_list_lists() { + $ret = array(); + if( $ret_code = mailman_command( 'list_lists', $output) ) { + mailman_fatal(tra('Unable to list lists.'), $ret_code); + } + else { + foreach( $output as $o ) { + if( strpos( $o, '-' ) ) { + list( $name, $desc ) = split( '-', $o ); + $ret[strtolower( trim( $name ) )] = trim( $desc ); + } + } + } + return( $ret ); +} + + +function mailman_list_members( $pListName ) { + $ret = array(); + $options = escapeshellarg( $pListName ); + if( $ret = mailman_command( 'list_members', $output, $options ) ) { + // mailman_fatal(tra('Unable to get members for list: ').$pListName, $ret); + } + return( $output ); +} + +// pParamHash follows naming convention off config_list --help usage instructions +function mailman_config_list( $pParamHash ){ + $options = ' -i '.escapeshellarg(UTIL_PKG_PATH.'mailman.cfg'); + $options .= ' '.escapeshellarg( $pParamHash['listname'] ); + if( $ret = mailman_command( 'config_list', $output, $options) ) { + return (tra('Unable to configure list: ').$pParamHash['listname'].":".$ret); + } +} + +// pParamHash follows naming convention off newlist --help usage instructions +function mailman_newlist( $pParamHash ) { + $error = NULL; + if( !($error = mailman_verify_list( $pParamHash['listname'] )) ) { + $options = ""; + if( !empty( $pParamHash['listhost'] ) ) { + $options .= ' -e '.escapeshellarg( $pParamHash['listhost'] ); + } + $options .= ' -q '.escapeshellarg( $pParamHash['listname'] ); + $options .= ' '.escapeshellarg( $pParamHash['listadmin-addr'] ).' '; + $options .= ' '.escapeshellarg( $pParamHash['admin-password'] ).' '; + + if( $ret = mailman_command( 'newlist', $output, $options ) ) { + return (tra('Unable to create list: ').$pParamHash['listname'].":".$ret); + } + + $options = ' -i '.escapeshellarg(UTIL_PKG_PATH.'mailman.cfg'); + $options .= ' '.escapeshellarg( $pParamHash['listname'] ); + if( $ret = mailman_command( 'config_list', $output, $options) ) { + // @TODO if this fails we should roll back the newlist created + return (tra('Unable to configure list: ').$pParamHash['listname'].":".$ret); + } + + $newList = $pParamHash['listname']; + $mailman = mailman_get_mailman_command(); + $newAliases = " +## $newList mailing list +$newList: \"|$mailman post $newList\" +$newList-admin: \"|$mailman admin $newList\" +$newList-bounces: \"|$mailman bounces $newList\" +$newList-confirm: \"|$mailman confirm $newList\" +$newList-join: \"|$mailman join $newList\" +$newList-leave: \"|$mailman leave $newList\" +$newList-owner: \"|$mailman owner $newList\" +$newList-request: \"|$mailman request $newList\" +$newList-subscribe: \"|$mailman subscribe $newList\" +$newList-unsubscribe: \"|$mailman unsubscribe $newList\""; + + // Make sure we unlock the semaphore + ignore_user_abort(true); + // Get a lock. flock is not reliable (NFS, FAT, etc) + // so we use a semaphore instead. + $sem_key = ftok(mailman_get_aliases_file(), 'm'); + if( $sem_key != -1 ) { + // Get the semaphore + $sem = sem_get($sem_key); + if( $sem ) { + if( sem_acquire($sem) ) { + if( $fh = fopen( mailman_get_aliases_file(), 'a' ) ) { + fwrite( $fh, $newAliases ); + fclose( $fh ); + mailman_newalias(); + } else { + $error = "Could not open /etc/aliases for appending."; + } + if( !sem_release($sem) ) { + $error = "Error releasing a sempahore."; + } + } else { + $error = "Unable to aquire a semaphore."; + } + } else { + $error = "Unable to get a semaphore."; + } + } else { + $error = "Couldn't create semaphore key."; + } + // Let the user cancel again + ignore_user_abort(false); + } + return $error; +} + +function mailman_remove_member( $pListName, $pEmail ) { + $ret = ''; + if( $fullCommand = mailman_get_command( 'remove_members' ) ) { + $cmd = "echo ".escapeshellarg( $pEmail )." | $fullCommand -f - ".escapeshellarg( $pListName ); + exec( $cmd, $ret ); + } else { + bit_error_log( 'Groups mailman command failed (remove_members) File not found: '.$fullCommand ); + } +} + +function mailman_setmoderated( $pListName, $pModerated = TRUE ) { + $ret = FALSE; + if( $fullCommand = mailman_get_command( 'withlist' ) ) { + $cmd = $fullCommand." -q -l -r mailman_lib.setDefaultModerationFlag ".escapeshellarg( $pListName )." ".( $pModerated ? 1 : 0 ); + $cmd = "/bin/sh -c \"PYTHONPATH=".UTIL_PKG_PATH." $cmd\""; + exec( $cmd, $ret ); + } else { + bit_error_log( 'Groups mailman command failed (withlist) File not found: '.$fullCommand ); + } + return $ret; +} + +function mailman_setmoderator( $pListName, $pEmail ) { + $ret = ''; + if( $fullCommand = mailman_get_command( 'withlist' ) ) { + $cmd = $fullCommand." -q -l -r mailman_lib.setMemberModeratedFlag ".escapeshellarg( $pListName )." ".escapeshellarg( $pEmail ); + $cmd = "/bin/sh -c \"PYTHONPATH=".UTIL_PKG_PATH." $cmd\""; + exec( $cmd, $ret ); + } else { + bit_error_log( 'Groups mailman command failed (withlist) File not found: '.$fullCommand ); + } + return $ret; +} + +function mailman_addmember( $pListName, $pEmail, $pType = NULL ) { + $ret = ''; + if( $fullCommand = mailman_get_command( 'add_members' ) ) { + switch( $pType){ + case "digest": + $cmd = "echo ".escapeshellarg( $pEmail )." | $fullCommand -d - ".escapeshellarg( $pListName ); + break; + case "email": + default: + $cmd = "echo ".escapeshellarg( $pEmail )." | $fullCommand -r - ".escapeshellarg( $pListName ); + break; + } + exec( $cmd, $ret ); + + /** + * its not enough to rely on the add_members call + * add_members is ignored by mailman if the user is already a member + * bw relies on mailman_addmember to toggle if a user wants to receive a digest or not + * to keep things simple in bw we conveniently handle the toggle here + **/ + if( !empty( $pType ) ){ + mailman_setsubscriptiontype( $pListName, $pEmail, $pType ); + } + } else { + bit_error_log( 'Groups mailman command failed (add_members) File not found: '.$fullCommand ); + } +} + +function mailman_findmember( $pListName, $pEmail ) { + $options = ' -l '.escapeshellarg( $pListName ).' '.escapeshellarg( $pEmail ); + if( $ret = mailman_command( 'find_member', $output, $options ) ) { + mailman_fatal(tra('Unable to find member in list: ').$pListName, $ret); + } + return $output; +} + +function mailman_setsubscriptiontype( $pListName, $pEmail, $pType ) { + if( $fullCommand = mailman_get_command( 'withlist' ) ) { + $cmd = $fullCommand." -q -l -r mailman_lib.setSubscriptionType ".escapeshellarg( $pListName )." ".escapeshellarg( $pEmail )." ".( $pType == 'digest' ? 1 : 0 ); + $cmd = "/bin/sh -c \"PYTHONPATH=".UTIL_PKG_PATH." $cmd\""; + exec( $cmd, $ret ); + }else{ + bit_error_log( 'Groups mailman command failed (withlist) File not found: '.$fullCommand ); + } + return $ret; +} + +function mailman_getsubscriptiontype( $pListName, $pEmail ){ + // even though setSubscriptionType returns a string or false we return an array because thats what exec returns + if( $fullCommand = mailman_get_command( 'withlist' ) ) { + $cmd = $fullCommand." -l -r mailman_lib.getSubscriptionType ".escapeshellarg( $pListName )." ".escapeshellarg( $pEmail ); + $cmd = "/bin/sh -c \"PYTHONPATH=".UTIL_PKG_PATH." $cmd\""; + exec( $cmd, $ret ); + }else{ + bit_error_log( 'Groups mailman command failed (withlist) File not found: '.$fullCommand ); + } + return $ret; +} + +function mailman_rmlist( $pListName ) { + $error = NULL; + if( mailman_verify_list( $pListName ) ) { + $options = ' -a '.escapeshellarg( $pListName ); + if( $ret = mailman_command( 'rmlist', $output, $options ) ) { + mailman_fatal(tra('Unable to remove list: ').$pListName, $ret); + } + + $newList = $pListName; + + $mailman = mailman_get_mailman_command(); + + $aliasesLines = array( + "## $newList mailing list", + "$newList", + "$newList-admin", + "$newList-bounces", + "$newList-confirm", + "$newList-join", + "$newList-leave", + "$newList-owner", + "$newList-request", + "$newList-subscribe", + "$newList-unsubscribe" + ); + // Make sure we unlock the semaphore + ignore_user_abort(true); + // Get a lock. flock is not reliable (NFS, FAT, etc) + // so we use a semaphore instead. + $sem_key = ftok(mailman_get_aliases_file(), 'm'); + if( $sem_key != -1 ) { + // Get the semaphore + $sem = sem_get($sem_key); + if( $sem ) { + if( sem_acquire($sem) ) { + if( $fh = fopen( mailman_get_aliases_file(), 'r+' ) ) { + // cull out all aliase lines for the mailing list and rewrite the file + $newContents = ''; + while( $line = fgets( $fh ) ) { + @list( $alias, $value ) = split( ':', $line ); + $alias = trim( $alias ); + if( !in_array($alias, $aliasesLines) ) { + $newContents .= $line; + } + } + + // Truncate the file + if( ftruncate($fh, 0) != 1) { + $error = "Unable to truncate /etc/aliases"; + } else { + if( !rewind($fh) ) { + $error = "Unable to seek /etc/aliases"; + } + else { + if( empty( $newContents ) ) { + $error = "Empty aliases for /etc/aliases"; + } elseif( !fwrite( $fh, $newContents ) ) { + $error = "Could not write new /etc/aliases"; + } + } + } + + fclose( $fh ); + mailman_newalias(); + } else { + $error = "Could not open /etc/aliases for appending."; + } + + if( !sem_release($sem) ) { + $error = "Error releasing a sempahore."; + } + } else { + $error = "Unable to aquire a semaphore."; + } + } else { + $error = "Unable to get a semaphore."; + } + } else { + $error = "Couldn't create semaphore key."; + } + // Let the user cancel again + ignore_user_abort(false); + } + return $error; +} + +function mailman_newalias() { + global $gBitSystem; + exec( $gBitSystem->getConfig('server_newaliases_cmd', '/usr/bin/newaliases' )); +} + +function mailman_get_aliases_file() { + global $gBitSystem; + return $gBitSystem->getConfig('server_aliases_file', '/etc/aliases'); +} + +function mailman_command( $pCommand, &$output, $pOptions=NULL ) { + $ret_code = NULL; + if( $fullCommand = mailman_get_command( $pCommand ) ) { + $cmd = $fullCommand.' '.$pOptions; + if( !defined( 'IS_LIVE' ) || !IS_LIVE ) { + bit_error_log( 'mailman LOG: '.$cmd ); + } + exec( $cmd, $output, $ret_code ); + if( $ret_code ) { + bit_error_log('Error running command: '. $cmd . ' Command returned: '.$ret_code); + } + } else { + bit_error_log( 'Groups mailman command failed ('.$pCommand.'): File not found: '.$fullCommand ); + } + return $ret_code; +} + +function mailman_fatal($pMessage, $pRetCode) { + global $gBitSystem; + $gBitSystem->fatalError($pMessage.tra(' Command returned: ').$pRetCode); + die; +} + +function mailman_get_mailman_command() { + global $gBitSystem; + $mailman = $gBitSystem->getConfig( 'server_mailman_cmd', '/usr/lib/mailman/mail/mailman' ); + return $mailman; +} + +function mailman_get_command( $pCommand ) { + global $gBitSystem; + $ret = NULL; + // Support for legacy configurations + $fullCommand = $gBitSystem->getConfig( 'server_mailman_bin' ) .'/'.$pCommand; + $fullCommand = str_replace( '//', '/', $fullCommand ); + if( file_exists( $fullCommand ) ) { + $ret = $fullCommand; + } + return $ret; +} + +?> diff --git a/includes/mailman_lib.py b/includes/mailman_lib.py new file mode 100644 index 0000000..8301873 --- /dev/null +++ b/includes/mailman_lib.py @@ -0,0 +1,27 @@ +from Mailman import mm_cfg +from Mailman.Errors import NotAMemberError +from Mailman.mm_cfg import Digests + +def setMemberModeratedFlag (mlist, addr): + mlist.moderator.append(addr) + mlist.Save() + +def setDefaultModerationFlag(mlist, val): + mlist.default_member_moderation = int(val); + for member in mlist.getMembers(): + mlist.setMemberOption(member, mm_cfg.Moderate, int(val)) + mlist.Save() + +def getSubscriptionType(mlist, addr): + try: + if mlist.getMemberOption(addr, Digests): + print "digest" + else: + print "email" + except NotAMemberError: + print 0 + +def setSubscriptionType(mlist, addr, val): + mlist.setMemberOption(addr, mm_cfg.Digests, int(val)) + mlist.Save() + diff --git a/includes/markdown.php b/includes/markdown.php new file mode 100644 index 0000000..10e7954 --- /dev/null +++ b/includes/markdown.php @@ -0,0 +1,1732 @@ + +# +# Original Markdown +# Copyright (c) 2004-2006 John Gruber +# +# + + +define( 'MARKDOWN_VERSION', "1.0.1n" ); # Sat 10 Oct 2009 + + +# +# Global default settings: +# + +# Change to ">" for HTML output +@define( 'MARKDOWN_EMPTY_ELEMENT_SUFFIX', " />"); + +# Define the width of a tab for code blocks. +@define( 'MARKDOWN_TAB_WIDTH', 4 ); + + +# +# WordPress settings: +# + +# Change to false to remove Markdown from posts and/or comments. +@define( 'MARKDOWN_WP_POSTS', true ); +@define( 'MARKDOWN_WP_COMMENTS', true ); + + + +### Standard Function Interface ### + +@define( 'MARKDOWN_PARSER_CLASS', 'Markdown_Parser' ); + +function Markdown($text) { +# +# Initialize the parser and return the result of its transform method. +# + # Setup static parser variable. + static $parser; + if (!isset($parser)) { + $parser_class = MARKDOWN_PARSER_CLASS; + $parser = new $parser_class; + } + + # Transform text using parser. + return $parser->transform($text); +} + + +### WordPress Plugin Interface ### + +/* +Plugin Name: Markdown +Plugin URI: http://michelf.com/projects/php-markdown/ +Description: Markdown syntax allows you to write using an easy-to-read, easy-to-write plain text format. Based on the original Perl version by John Gruber. More... +Version: 1.0.1n +Author: Michel Fortin +Author URI: http://michelf.com/ +*/ + +if (isset($wp_version)) { + # More details about how it works here: + # + + # Post content and excerpts + # - Remove WordPress paragraph generator. + # - Run Markdown on excerpt, then remove all tags. + # - Add paragraph tag around the excerpt, but remove it for the excerpt rss. + if (MARKDOWN_WP_POSTS) { + remove_filter('the_content', 'wpautop'); + remove_filter('the_content_rss', 'wpautop'); + remove_filter('the_excerpt', 'wpautop'); + add_filter('the_content', 'Markdown', 6); + add_filter('the_content_rss', 'Markdown', 6); + add_filter('get_the_excerpt', 'Markdown', 6); + add_filter('get_the_excerpt', 'trim', 7); + add_filter('the_excerpt', 'mdwp_add_p'); + add_filter('the_excerpt_rss', 'mdwp_strip_p'); + + remove_filter('content_save_pre', 'balanceTags', 50); + remove_filter('excerpt_save_pre', 'balanceTags', 50); + add_filter('the_content', 'balanceTags', 50); + add_filter('get_the_excerpt', 'balanceTags', 9); + } + + # Comments + # - Remove WordPress paragraph generator. + # - Remove WordPress auto-link generator. + # - Scramble important tags before passing them to the kses filter. + # - Run Markdown on excerpt then remove paragraph tags. + if (MARKDOWN_WP_COMMENTS) { + remove_filter('comment_text', 'wpautop', 30); + remove_filter('comment_text', 'make_clickable'); + add_filter('pre_comment_content', 'Markdown', 6); + add_filter('pre_comment_content', 'mdwp_hide_tags', 8); + add_filter('pre_comment_content', 'mdwp_show_tags', 12); + add_filter('get_comment_text', 'Markdown', 6); + add_filter('get_comment_excerpt', 'Markdown', 6); + add_filter('get_comment_excerpt', 'mdwp_strip_p', 7); + + global $mdwp_hidden_tags, $mdwp_placeholders; + $mdwp_hidden_tags = explode(' ', + '

 
  • '); + $mdwp_placeholders = explode(' ', str_rot13( + 'pEj07ZbbBZ U1kqgh4w4p pre2zmeN6K QTi31t9pre ol0MP1jzJR '. + 'ML5IjmbRol ulANi1NsGY J7zRLJqPul liA8ctl16T K9nhooUHli')); + } + + function mdwp_add_p($text) { + if (!preg_match('{^$|^<(p|ul|ol|dl|pre|blockquote)>}i', $text)) { + $text = '

    '.$text.'

    '; + $text = preg_replace('{\n{2,}}', "

    \n\n

    ", $text); + } + return $text; + } + + function mdwp_strip_p($t) { return preg_replace('{}i', '', $t); } + + function mdwp_hide_tags($text) { + global $mdwp_hidden_tags, $mdwp_placeholders; + return str_replace($mdwp_hidden_tags, $mdwp_placeholders, $text); + } + function mdwp_show_tags($text) { + global $mdwp_hidden_tags, $mdwp_placeholders; + return str_replace($mdwp_placeholders, $mdwp_hidden_tags, $text); + } +} + + +### bBlog Plugin Info ### + +function identify_modifier_markdown() { + return array( + 'name' => 'markdown', + 'type' => 'modifier', + 'nicename' => 'Markdown', + 'description' => 'A text-to-HTML conversion tool for web writers', + 'authors' => 'Michel Fortin and John Gruber', + 'licence' => 'BSD-like', + 'version' => MARKDOWN_VERSION, + 'help' => 'Markdown syntax allows you to write using an easy-to-read, easy-to-write plain text format. Based on the original Perl version by John Gruber. More...' + ); +} + + +### Smarty Modifier Interface ### + +function smarty_modifier_markdown($text) { + return Markdown($text); +} + + +### Textile Compatibility Mode ### + +# Rename this file to "classTextile.php" and it can replace Textile everywhere. + +if (strcasecmp(substr(__FILE__, -16), "classTextile.php") == 0) { + # Try to include PHP SmartyPants. Should be in the same directory. + @include_once 'smartypants.php'; + # Fake Textile class. It calls Markdown instead. + class Textile { + function TextileThis($text, $lite='', $encode='') { + if ($lite == '' && $encode == '') $text = Markdown($text); + if (function_exists('SmartyPants')) $text = SmartyPants($text); + return $text; + } + # Fake restricted version: restrictions are not supported for now. + function TextileRestricted($text, $lite='', $noimage='') { + return $this->TextileThis($text, $lite); + } + # Workaround to ensure compatibility with TextPattern 4.0.3. + function blockLite($text) { return $text; } + } +} + + + +# +# Markdown Parser Class +# + +class Markdown_Parser { + + # Regex to match balanced [brackets]. + # Needed to insert a maximum bracked depth while converting to PHP. + public $nested_brackets_depth = 6; + public $nested_brackets_re; + + public $nested_url_parenthesis_depth = 4; + public $nested_url_parenthesis_re; + + # Table of hash values for escaped characters: + public $escape_chars = '\`*_{}[]()>#+-.!'; + public $escape_chars_re; + + # Change to ">" for HTML output. + public $empty_element_suffix = MARKDOWN_EMPTY_ELEMENT_SUFFIX; + public $tab_width = MARKDOWN_TAB_WIDTH; + + # Change to `true` to disallow markup or entities. + public $no_markup = false; + public $no_entities = false; + + # Predefined urls and titles for reference links and images. + public $predef_urls = array(); + public $predef_titles = array(); + + + function Markdown_Parser() { + # + # Constructor function. Initialize appropriate member variables. + # + $this->_initDetab(); + $this->prepareItalicsAndBold(); + + $this->nested_brackets_re = + str_repeat('(?>[^\[\]]+|\[', $this->nested_brackets_depth). + str_repeat('\])*', $this->nested_brackets_depth); + + $this->nested_url_parenthesis_re = + str_repeat('(?>[^()\s]+|\(', $this->nested_url_parenthesis_depth). + str_repeat('(?>\)))*', $this->nested_url_parenthesis_depth); + + $this->escape_chars_re = '['.preg_quote($this->escape_chars).']'; + + # Sort document, block, and span gamut in ascendent priority order. + asort($this->document_gamut); + asort($this->block_gamut); + asort($this->span_gamut); + } + + + # Internal hashes used during transformation. + public $urls = array(); + public $titles = array(); + public $html_hashes = array(); + + # Status flag to avoid invalid nesting. + public $in_anchor = false; + + + function setup() { + # + # Called before the transformation process starts to setup parser + # states. + # + # Clear global hashes. + $this->urls = $this->predef_urls; + $this->titles = $this->predef_titles; + $this->html_hashes = array(); + + $in_anchor = false; + } + + function teardown() { + # + # Called after the transformation process to clear any variable + # which may be taking up memory unnecessarly. + # + $this->urls = array(); + $this->titles = array(); + $this->html_hashes = array(); + } + + + function transform($text) { + # + # Main function. Performs some preprocessing on the input text + # and pass it through the document gamut. + # + $this->setup(); + + # Remove UTF-8 BOM and marker character in input, if present. + $text = preg_replace('{^\xEF\xBB\xBF|\x1A}', '', $text); + + # Standardize line endings: + # DOS to Unix and Mac to Unix + $text = preg_replace('{\r\n?}', "\n", $text); + + # Make sure $text ends with a couple of newlines: + $text .= "\n\n"; + + # Convert all tabs to spaces. + $text = $this->detab($text); + + # Turn block-level HTML blocks into hash entries + $text = $this->hashHTMLBlocks($text); + + # Strip any lines consisting only of spaces and tabs. + # This makes subsequent regexen easier to write, because we can + # match consecutive blank lines with /\n+/ instead of something + # contorted like /[ ]*\n+/ . + $text = preg_replace('/^[ ]+$/m', '', $text); + + # Run document gamut methods. + foreach ($this->document_gamut as $method => $priority) { + $text = $this->$method($text); + } + + $this->teardown(); + + return $text . "\n"; + } + + public $document_gamut = array( + # Strip link definitions, store in hashes. + "stripLinkDefinitions" => 20, + + "runBasicBlockGamut" => 30, + ); + + + function stripLinkDefinitions($text) { + # + # Strips link definitions from text, stores the URLs and titles in + # hash references. + # + $less_than_tab = $this->tab_width - 1; + + # Link defs are in the form: ^[id]: url "optional title" + $text = preg_replace_callback('{ + ^[ ]{0,'.$less_than_tab.'}\[(.+)\][ ]?: # id = $1 + [ ]* + \n? # maybe *one* newline + [ ]* + (?: + <(.+?)> # url = $2 + | + (\S+?) # url = $3 + ) + [ ]* + \n? # maybe one newline + [ ]* + (?: + (?<=\s) # lookbehind for whitespace + ["(] + (.*?) # title = $4 + [")] + [ ]* + )? # title is optional + (?:\n+|\Z) + }xm', + array(&$this, '_stripLinkDefinitions_callback'), + $text); + return $text; + } + function _stripLinkDefinitions_callback($matches) { + $link_id = strtolower($matches[1]); + $url = $matches[2] == '' ? $matches[3] : $matches[2]; + $this->urls[$link_id] = $url; + $this->titles[$link_id] =& $matches[4]; + return ''; # String that will replace the block + } + + + function hashHTMLBlocks($text) { + if ($this->no_markup) return $text; + + $less_than_tab = $this->tab_width - 1; + + # Hashify HTML blocks: + # We only want to do this for block-level HTML tags, such as headers, + # lists, and tables. That's because we still want to wrap

    s around + # "paragraphs" that are wrapped in non-block-level tags, such as anchors, + # phrase emphasis, and spans. The list of tags we're looking for is + # hard-coded: + # + # * List "a" is made of tags which can be both inline or block-level. + # These will be treated block-level when the start tag is alone on + # its line, otherwise they're not matched here and will be taken as + # inline later. + # * List "b" is made of tags which are always block-level; + # + $block_tags_a_re = 'ins|del'; + $block_tags_b_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|'. + 'script|noscript|form|fieldset|iframe|math'; + + # Regular expression for the content of a block tag. + $nested_tags_level = 4; + $attr = ' + (?> # optional tag attributes + \s # starts with whitespace + (?> + [^>"/]+ # text outside quotes + | + /+(?!>) # slash not followed by ">" + | + "[^"]*" # text inside double quotes (tolerate ">") + | + \'[^\']*\' # text inside single quotes (tolerate ">") + )* + )? + '; + $content = + str_repeat(' + (?> + [^<]+ # content without tag + | + <\2 # nested opening tag + '.$attr.' # attributes + (?> + /> + | + >', $nested_tags_level). # end of opening tag + '.*?'. # last level nested tag content + str_repeat(' + # closing nested tag + ) + | + <(?!/\2\s*> # other tags with a different name + ) + )*', + $nested_tags_level); + $content2 = str_replace('\2', '\3', $content); + + # First, look for nested blocks, e.g.: + #

    + #
    + # tags for inner block must be indented. + #
    + #
    + # + # The outermost tags must start at the left margin for this to match, and + # the inner nested divs must be indented. + # We need to do this before the next, more liberal match, because the next + # match will start at the first `
    ` and stop at the first `
    `. + $text = preg_replace_callback('{(?> + (?> + (?<=\n\n) # Starting after a blank line + | # or + \A\n? # the beginning of the doc + ) + ( # save in $1 + + # Match from `\n` to `\n`, handling nested tags + # in between. + + [ ]{0,'.$less_than_tab.'} + <('.$block_tags_b_re.')# start tag = $2 + '.$attr.'> # attributes followed by > and \n + '.$content.' # content, support nesting + # the matching end tag + [ ]* # trailing spaces/tabs + (?=\n+|\Z) # followed by a newline or end of document + + | # Special version for tags of group a. + + [ ]{0,'.$less_than_tab.'} + <('.$block_tags_a_re.')# start tag = $3 + '.$attr.'>[ ]*\n # attributes followed by > + '.$content2.' # content, support nesting + # the matching end tag + [ ]* # trailing spaces/tabs + (?=\n+|\Z) # followed by a newline or end of document + + | # Special case just for
    . It was easier to make a special + # case than to make the other regex more complicated. + + [ ]{0,'.$less_than_tab.'} + <(hr) # start tag = $2 + '.$attr.' # attributes + /?> # the matching end tag + [ ]* + (?=\n{2,}|\Z) # followed by a blank line or end of document + + | # Special case for standalone HTML comments: + + [ ]{0,'.$less_than_tab.'} + (?s: + + ) + [ ]* + (?=\n{2,}|\Z) # followed by a blank line or end of document + + | # PHP and ASP-style processor instructions ( + ) + [ ]* + (?=\n{2,}|\Z) # followed by a blank line or end of document + + ) + )}Sxmi', + array(&$this, '_hashHTMLBlocks_callback'), + $text); + + return $text; + } + function _hashHTMLBlocks_callback($matches) { + $text = $matches[1]; + $key = $this->hashBlock($text); + return "\n\n$key\n\n"; + } + + + function hashPart($text, $boundary = 'X') { + # + # Called whenever a tag must be hashed when a function insert an atomic + # element in the text stream. Passing $text to through this function gives + # a unique text-token which will be reverted back when calling unhash. + # + # The $boundary argument specify what character should be used to surround + # the token. By convension, "B" is used for block elements that needs not + # to be wrapped into paragraph tags at the end, ":" is used for elements + # that are word separators and "X" is used in the general case. + # + # Swap back any tag hash found in $text so we do not have to `unhash` + # multiple times at the end. + $text = $this->unhash($text); + + # Then hash the block. + static $i = 0; + $key = "$boundary\x1A" . ++$i . $boundary; + $this->html_hashes[$key] = $text; + return $key; # String that will replace the tag. + } + + + function hashBlock($text) { + # + # Shortcut function for hashPart with block-level boundaries. + # + return $this->hashPart($text, 'B'); + } + + + public $block_gamut = array( + # + # These are all the transformations that form block-level + # tags like paragraphs, headers, and list items. + # + "doHeaders" => 10, + "doHorizontalRules" => 20, + + "doLists" => 40, + "doCodeBlocks" => 50, + "doBlockQuotes" => 60, + ); + + function runBlockGamut($text) { + # + # Run block gamut tranformations. + # + # We need to escape raw HTML in Markdown source before doing anything + # else. This need to be done for each block, and not only at the + # begining in the Markdown function since hashed blocks can be part of + # list items and could have been indented. Indented blocks would have + # been seen as a code block in a previous pass of hashHTMLBlocks. + $text = $this->hashHTMLBlocks($text); + + return $this->runBasicBlockGamut($text); + } + + function runBasicBlockGamut($text) { + # + # Run block gamut tranformations, without hashing HTML blocks. This is + # useful when HTML blocks are known to be already hashed, like in the first + # whole-document pass. + # + foreach ($this->block_gamut as $method => $priority) { + $text = $this->$method($text); + } + + # Finally form paragraph and restore hashed blocks. + $text = $this->formParagraphs($text); + + return $text; + } + + + function doHorizontalRules($text) { + # Do Horizontal Rules: + return preg_replace( + '{ + ^[ ]{0,3} # Leading space + ([-*_]) # $1: First marker + (?> # Repeated marker group + [ ]{0,2} # Zero, one, or two spaces. + \1 # Marker character + ){2,} # Group repeated at least twice + [ ]* # Tailing spaces + $ # End of line. + }mx', + "\n".$this->hashBlock("empty_element_suffix")."\n", + $text); + } + + + public $span_gamut = array( + # + # These are all the transformations that occur *within* block-level + # tags like paragraphs, headers, and list items. + # + # Process character escapes, code spans, and inline HTML + # in one shot. + "parseSpan" => -30, + + # Process anchor and image tags. Images must come first, + # because ![foo][f] looks like an anchor. + "doImages" => 10, + "doAnchors" => 20, + + # Make links out of things like `` + # Must come after doAnchors, because you can use < and > + # delimiters in inline links like [this](). + "doAutoLinks" => 30, + "encodeAmpsAndAngles" => 40, + + "doItalicsAndBold" => 50, + "doHardBreaks" => 60, + ); + + function runSpanGamut($text) { + # + # Run span gamut tranformations. + # + foreach ($this->span_gamut as $method => $priority) { + $text = $this->$method($text); + } + + return $text; + } + + + function doHardBreaks($text) { + # Do hard breaks: + return preg_replace_callback('/ {2,}\n/', + array(&$this, '_doHardBreaks_callback'), $text); + } + function _doHardBreaks_callback($matches) { + return $this->hashPart("empty_element_suffix\n"); + } + + + function doAnchors($text) { + # + # Turn Markdown link shortcuts into XHTML tags. + # + if ($this->in_anchor) return $text; + $this->in_anchor = true; + + # + # First, handle reference-style links: [link text] [id] + # + $text = preg_replace_callback('{ + ( # wrap whole match in $1 + \[ + ('.$this->nested_brackets_re.') # link text = $2 + \] + + [ ]? # one optional space + (?:\n[ ]*)? # one optional newline followed by spaces + + \[ + (.*?) # id = $3 + \] + ) + }xs', + array(&$this, '_doAnchors_reference_callback'), $text); + + # + # Next, inline-style links: [link text](url "optional title") + # + $text = preg_replace_callback('{ + ( # wrap whole match in $1 + \[ + ('.$this->nested_brackets_re.') # link text = $2 + \] + \( # literal paren + [ \n]* + (?: + <(.+?)> # href = $3 + | + ('.$this->nested_url_parenthesis_re.') # href = $4 + ) + [ \n]* + ( # $5 + ([\'"]) # quote char = $6 + (.*?) # Title = $7 + \6 # matching quote + [ \n]* # ignore any spaces/tabs between closing quote and ) + )? # title is optional + \) + ) + }xs', + array(&$this, '_doAnchors_inline_callback'), $text); + + # + # Last, handle reference-style shortcuts: [link text] + # These must come last in case you've also got [link text][1] + # or [link text](/foo) + # + $text = preg_replace_callback('{ + ( # wrap whole match in $1 + \[ + ([^\[\]]+) # link text = $2; can\'t contain [ or ] + \] + ) + }xs', + array(&$this, '_doAnchors_reference_callback'), $text); + + $this->in_anchor = false; + return $text; + } + function _doAnchors_reference_callback($matches) { + $whole_match = $matches[1]; + $link_text = $matches[2]; + $link_id =& $matches[3]; + + if ($link_id == "") { + # for shortcut links like [this][] or [this]. + $link_id = $link_text; + } + + # lower-case and turn embedded newlines into spaces + $link_id = strtolower($link_id); + $link_id = preg_replace('{[ ]?\n}', ' ', $link_id); + + if (isset($this->urls[$link_id])) { + $url = $this->urls[$link_id]; + $url = $this->encodeAttribute($url); + + $result = "titles[$link_id] ) ) { + $title = $this->titles[$link_id]; + $title = $this->encodeAttribute($title); + $result .= " title=\"$title\""; + } + + $link_text = $this->runSpanGamut($link_text); + $result .= ">$link_text"; + $result = $this->hashPart($result); + } + else { + $result = $whole_match; + } + return $result; + } + function _doAnchors_inline_callback($matches) { + $whole_match = $matches[1]; + $link_text = $this->runSpanGamut($matches[2]); + $url = $matches[3] == '' ? $matches[4] : $matches[3]; + $title =& $matches[7]; + + $url = $this->encodeAttribute($url); + + $result = "encodeAttribute($title); + $result .= " title=\"$title\""; + } + + $link_text = $this->runSpanGamut($link_text); + $result .= ">$link_text"; + + return $this->hashPart($result); + } + + + function doImages($text) { + # + # Turn Markdown image shortcuts into tags. + # + # + # First, handle reference-style labeled images: ![alt text][id] + # + $text = preg_replace_callback('{ + ( # wrap whole match in $1 + !\[ + ('.$this->nested_brackets_re.') # alt text = $2 + \] + + [ ]? # one optional space + (?:\n[ ]*)? # one optional newline followed by spaces + + \[ + (.*?) # id = $3 + \] + + ) + }xs', + array(&$this, '_doImages_reference_callback'), $text); + + # + # Next, handle inline images: ![alt text](url "optional title") + # Don't forget: encode * and _ + # + $text = preg_replace_callback('{ + ( # wrap whole match in $1 + !\[ + ('.$this->nested_brackets_re.') # alt text = $2 + \] + \s? # One optional whitespace character + \( # literal paren + [ \n]* + (?: + <(\S*)> # src url = $3 + | + ('.$this->nested_url_parenthesis_re.') # src url = $4 + ) + [ \n]* + ( # $5 + ([\'"]) # quote char = $6 + (.*?) # title = $7 + \6 # matching quote + [ \n]* + )? # title is optional + \) + ) + }xs', + array(&$this, '_doImages_inline_callback'), $text); + + return $text; + } + function _doImages_reference_callback($matches) { + $whole_match = $matches[1]; + $alt_text = $matches[2]; + $link_id = strtolower($matches[3]); + + if ($link_id == "") { + $link_id = strtolower($alt_text); # for shortcut links like ![this][]. + } + + $alt_text = $this->encodeAttribute($alt_text); + if (isset($this->urls[$link_id])) { + $url = $this->encodeAttribute($this->urls[$link_id]); + $result = "\"$alt_text\"";titles[$link_id])) { + $title = $this->titles[$link_id]; + $title = $this->encodeAttribute($title); + $result .= " title=\"$title\""; + } + $result .= $this->empty_element_suffix; + $result = $this->hashPart($result); + } + else { + # If there's no such link ID, leave intact: + $result = $whole_match; + } + + return $result; + } + function _doImages_inline_callback($matches) { + $whole_match = $matches[1]; + $alt_text = $matches[2]; + $url = $matches[3] == '' ? $matches[4] : $matches[3]; + $title =& $matches[7]; + + $alt_text = $this->encodeAttribute($alt_text); + $url = $this->encodeAttribute($url); + $result = "\"$alt_text\"";encodeAttribute($title); + $result .= " title=\"$title\""; # $title already quoted + } + $result .= $this->empty_element_suffix; + + return $this->hashPart($result); + } + + + function doHeaders($text) { + # Setext-style headers: + # Header 1 + # ======== + # + # Header 2 + # -------- + # + $text = preg_replace_callback('{ ^(.+?)[ ]*\n(=+|-+)[ ]*\n+ }mx', + array(&$this, '_doHeaders_callback_setext'), $text); + + # atx-style headers: + # # Header 1 + # ## Header 2 + # ## Header 2 with closing hashes ## + # ... + # ###### Header 6 + # + $text = preg_replace_callback('{ + ^(\#{1,6}) # $1 = string of #\'s + [ ]* + (.+?) # $2 = Header text + [ ]* + \#* # optional closing #\'s (not counted) + \n+ + }xm', + array(&$this, '_doHeaders_callback_atx'), $text); + + return $text; + } + function _doHeaders_callback_setext($matches) { + # Terrible hack to check we haven't found an empty list item. + if ($matches[2] == '-' && preg_match('{^-(?: |$)}', $matches[1])) + return $matches[0]; + + $level = $matches[2]{0} == '=' ? 1 : 2; + $block = "".$this->runSpanGamut($matches[1]).""; + return "\n" . $this->hashBlock($block) . "\n\n"; + } + function _doHeaders_callback_atx($matches) { + $level = strlen($matches[1]); + $block = "".$this->runSpanGamut($matches[2]).""; + return "\n" . $this->hashBlock($block) . "\n\n"; + } + + + function doLists($text) { + # + # Form HTML ordered (numbered) and unordered (bulleted) lists. + # + $less_than_tab = $this->tab_width - 1; + + # Re-usable patterns to match list item bullets and number markers: + $marker_ul_re = '[*+-]'; + $marker_ol_re = '\d+[.]'; + $marker_any_re = "(?:$marker_ul_re|$marker_ol_re)"; + + $markers_relist = array( + $marker_ul_re => $marker_ol_re, + $marker_ol_re => $marker_ul_re, + ); + + foreach ($markers_relist as $marker_re => $other_marker_re) { + # Re-usable pattern to match any entirel ul or ol list: + $whole_list_re = ' + ( # $1 = whole list + ( # $2 + ([ ]{0,'.$less_than_tab.'}) # $3 = number of spaces + ('.$marker_re.') # $4 = first list item marker + [ ]+ + ) + (?s:.+?) + ( # $5 + \z + | + \n{2,} + (?=\S) + (?! # Negative lookahead for another list item marker + [ ]* + '.$marker_re.'[ ]+ + ) + | + (?= # Lookahead for another kind of list + \n + \3 # Must have the same indentation + '.$other_marker_re.'[ ]+ + ) + ) + ) + '; // mx + + # We use a different prefix before nested lists than top-level lists. + # See extended comment in _ProcessListItems(). + + if ($this->list_level) { + $text = preg_replace_callback('{ + ^ + '.$whole_list_re.' + }mx', + array(&$this, '_doLists_callback'), $text); + } + else { + $text = preg_replace_callback('{ + (?:(?<=\n)\n|\A\n?) # Must eat the newline + '.$whole_list_re.' + }mx', + array(&$this, '_doLists_callback'), $text); + } + } + + return $text; + } + function _doLists_callback($matches) { + # Re-usable patterns to match list item bullets and number markers: + $marker_ul_re = '[*+-]'; + $marker_ol_re = '\d+[.]'; + $marker_any_re = "(?:$marker_ul_re|$marker_ol_re)"; + + $list = $matches[1]; + $list_type = preg_match("/$marker_ul_re/", $matches[4]) ? "ul" : "ol"; + + $marker_any_re = ( $list_type == "ul" ? $marker_ul_re : $marker_ol_re ); + + $list .= "\n"; + $result = $this->processListItems($list, $marker_any_re); + + $result = $this->hashBlock("<$list_type>\n" . $result . ""); + return "\n". $result ."\n\n"; + } + + public $list_level = 0; + + function processListItems($list_str, $marker_any_re) { + # + # Process the contents of a single ordered or unordered list, splitting it + # into individual list items. + # + # The $this->list_level global keeps track of when we're inside a list. + # Each time we enter a list, we increment it; when we leave a list, + # we decrement. If it's zero, we're not in a list anymore. + # + # We do this because when we're not inside a list, we want to treat + # something like this: + # + # I recommend upgrading to version + # 8. Oops, now this line is treated + # as a sub-list. + # + # As a single paragraph, despite the fact that the second line starts + # with a digit-period-space sequence. + # + # Whereas when we're inside a list (or sub-list), that line will be + # treated as the start of a sub-list. What a kludge, huh? This is + # an aspect of Markdown's syntax that's hard to parse perfectly + # without resorting to mind-reading. Perhaps the solution is to + # change the syntax rules such that sub-lists must start with a + # starting cardinal number; e.g. "1." or "a.". + + $this->list_level++; + + # trim trailing blank lines: + $list_str = preg_replace("/\n{2,}\\z/", "\n", $list_str); + + $list_str = preg_replace_callback('{ + (\n)? # leading line = $1 + (^[ ]*) # leading whitespace = $2 + ('.$marker_any_re.' # list marker and space = $3 + (?:[ ]+|(?=\n)) # space only required if item is not empty + ) + ((?s:.*?)) # list item text = $4 + (?:(\n+(?=\n))|\n) # tailing blank line = $5 + (?= \n* (\z | \2 ('.$marker_any_re.') (?:[ ]+|(?=\n)))) + }xm', + array(&$this, '_processListItems_callback'), $list_str); + + $this->list_level--; + return $list_str; + } + function _processListItems_callback($matches) { + $item = $matches[4]; + $leading_line =& $matches[1]; + $leading_space =& $matches[2]; + $marker_space = $matches[3]; + $tailing_blank_line =& $matches[5]; + + if ($leading_line || $tailing_blank_line || + preg_match('/\n{2,}/', $item)) + { + # Replace marker with the appropriate whitespace indentation + $item = $leading_space . str_repeat(' ', strlen($marker_space)) . $item; + $item = $this->runBlockGamut($this->outdent($item)."\n"); + } + else { + # Recursion for sub-lists: + $item = $this->doLists($this->outdent($item)); + $item = preg_replace('/\n+$/', '', $item); + $item = $this->runSpanGamut($item); + } + + return "
  • " . $item . "
  • \n"; + } + + + function doCodeBlocks($text) { + # + # Process Markdown `
    ` blocks.
    +	#
    +		$text = preg_replace_callback('{
    +				(?:\n\n|\A\n?)
    +				(	            # $1 = the code block -- one or more lines, starting with a space/tab
    +				  (?>
    +					[ ]{'.$this->tab_width.'}  # Lines must start with a tab or a tab-width of spaces
    +					.*\n+
    +				  )+
    +				)
    +				((?=^[ ]{0,'.$this->tab_width.'}\S)|\Z)	# Lookahead for non-space at line-start, or end of doc
    +			}xm',
    +			array(&$this, '_doCodeBlocks_callback'), $text);
    +
    +		return $text;
    +	}
    +	function _doCodeBlocks_callback($matches) {
    +		$codeblock = $matches[1];
    +
    +		$codeblock = $this->outdent($codeblock);
    +		$codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES);
    +
    +		# trim leading newlines and trailing newlines
    +		$codeblock = preg_replace('/\A\n+|\n+\z/', '', $codeblock);
    +
    +		$codeblock = "
    $codeblock\n
    "; + return "\n\n".$this->hashBlock($codeblock)."\n\n"; + } + + + function makeCodeSpan($code) { + # + # Create a code span markup for $code. Called from handleSpanToken. + # + $code = htmlspecialchars(trim($code), ENT_NOQUOTES); + return $this->hashPart("$code"); + } + + + public $em_relist = array( + '' => '(?:(? '(?<=\S|^)(? '(?<=\S|^)(? '(?:(? '(?<=\S|^)(? '(?<=\S|^)(? '(?:(? '(?<=\S|^)(? '(?<=\S|^)(?em_relist as $em => $em_re) { + foreach ($this->strong_relist as $strong => $strong_re) { + # Construct list of allowed token expressions. + $token_relist = array(); + if (isset($this->em_strong_relist["$em$strong"])) { + $token_relist[] = $this->em_strong_relist["$em$strong"]; + } + $token_relist[] = $em_re; + $token_relist[] = $strong_re; + + # Construct master expression from list. + $token_re = '{('. implode('|', $token_relist) .')}'; + $this->em_strong_prepared_relist["$em$strong"] = $token_re; + } + } + } + + function doItalicsAndBold($text) { + $token_stack = array(''); + $text_stack = array(''); + $em = ''; + $strong = ''; + $tree_char_em = false; + + while (1) { + # + # Get prepared regular expression for seraching emphasis tokens + # in current context. + # + $token_re = $this->em_strong_prepared_relist["$em$strong"]; + + # + # Each loop iteration search for the next emphasis token. + # Each token is then passed to handleSpanToken. + # + $parts = preg_split($token_re, $text, 2, PREG_SPLIT_DELIM_CAPTURE); + $text_stack[0] .= $parts[0]; + $token =& $parts[1]; + $text =& $parts[2]; + + if (empty($token)) { + # Reached end of text span: empty stack without emitting. + # any more emphasis. + while ($token_stack[0]) { + $text_stack[1] .= array_shift($token_stack); + $text_stack[0] .= array_shift($text_stack); + } + break; + } + + $token_len = strlen($token); + if ($tree_char_em) { + # Reached closing marker while inside a three-char emphasis. + if ($token_len == 3) { + # Three-char closing marker, close em and strong. + array_shift($token_stack); + $span = array_shift($text_stack); + $span = $this->runSpanGamut($span); + $span = "$span"; + $text_stack[0] .= $this->hashPart($span); + $em = ''; + $strong = ''; + } else { + # Other closing marker: close one em or strong and + # change current token state to match the other + $token_stack[0] = str_repeat($token{0}, 3-$token_len); + $tag = $token_len == 2 ? "strong" : "em"; + $span = $text_stack[0]; + $span = $this->runSpanGamut($span); + $span = "<$tag>$span"; + $text_stack[0] = $this->hashPart($span); + $$tag = ''; # $$tag stands for $em or $strong + } + $tree_char_em = false; + } else if ($token_len == 3) { + if ($em) { + # Reached closing marker for both em and strong. + # Closing strong marker: + for ($i = 0; $i < 2; ++$i) { + $shifted_token = array_shift($token_stack); + $tag = strlen($shifted_token) == 2 ? "strong" : "em"; + $span = array_shift($text_stack); + $span = $this->runSpanGamut($span); + $span = "<$tag>$span"; + $text_stack[0] .= $this->hashPart($span); + $$tag = ''; # $$tag stands for $em or $strong + } + } else { + # Reached opening three-char emphasis marker. Push on token + # stack; will be handled by the special condition above. + $em = $token{0}; + $strong = "$em$em"; + array_unshift($token_stack, $token); + array_unshift($text_stack, ''); + $tree_char_em = true; + } + } else if ($token_len == 2) { + if ($strong) { + # Unwind any dangling emphasis marker: + if (strlen($token_stack[0]) == 1) { + $text_stack[1] .= array_shift($token_stack); + $text_stack[0] .= array_shift($text_stack); + } + # Closing strong marker: + array_shift($token_stack); + $span = array_shift($text_stack); + $span = $this->runSpanGamut($span); + $span = "$span"; + $text_stack[0] .= $this->hashPart($span); + $strong = ''; + } else { + array_unshift($token_stack, $token); + array_unshift($text_stack, ''); + $strong = $token; + } + } else { + # Here $token_len == 1 + if ($em) { + if (strlen($token_stack[0]) == 1) { + # Closing emphasis marker: + array_shift($token_stack); + $span = array_shift($text_stack); + $span = $this->runSpanGamut($span); + $span = "$span"; + $text_stack[0] .= $this->hashPart($span); + $em = ''; + } else { + $text_stack[0] .= $token; + } + } else { + array_unshift($token_stack, $token); + array_unshift($text_stack, ''); + $em = $token; + } + } + } + return $text_stack[0]; + } + + + function doBlockQuotes($text) { + $text = preg_replace_callback('/ + ( # Wrap whole match in $1 + (?> + ^[ ]*>[ ]? # ">" at the start of a line + .+\n # rest of the first line + (.+\n)* # subsequent consecutive lines + \n* # blanks + )+ + ) + /xm', + array(&$this, '_doBlockQuotes_callback'), $text); + + return $text; + } + function _doBlockQuotes_callback($matches) { + $bq = $matches[1]; + # trim one level of quoting - trim whitespace-only lines + $bq = preg_replace('/^[ ]*>[ ]?|^[ ]+$/m', '', $bq); + $bq = $this->runBlockGamut($bq); # recurse + + $bq = preg_replace('/^/m', " ", $bq); + # These leading spaces cause problem with
     content, 
    +		# so we need to fix that:
    +		$bq = preg_replace_callback('{(\s*
    .+?
    )}sx', + array(&$this, '_doBlockQuotes_callback2'), $bq); + + return "\n". $this->hashBlock("
    \n$bq\n
    ")."\n\n"; + } + function _doBlockQuotes_callback2($matches) { + $pre = $matches[1]; + $pre = preg_replace('/^ /m', '', $pre); + return $pre; + } + + + function formParagraphs($text) { + # + # Params: + # $text - string to process with html

    tags + # + # Strip leading and trailing lines: + $text = preg_replace('/\A\n+|\n+\z/', '', $text); + + $grafs = preg_split('/\n{2,}/', $text, -1, PREG_SPLIT_NO_EMPTY); + + # + # Wrap

    tags and unhashify HTML blocks + # + foreach ($grafs as $key => $value) { + if (!preg_match('/^B\x1A[0-9]+B$/', $value)) { + # Is a paragraph. + $value = $this->runSpanGamut($value); + $value = preg_replace('/^([ ]*)/', "

    ", $value); + $value .= "

    "; + $grafs[$key] = $this->unhash($value); + } + else { + # Is a block. + # Modify elements of @grafs in-place... + $graf = $value; + $block = $this->html_hashes[$graf]; + $graf = $block; +// if (preg_match('{ +// \A +// ( # $1 =
    tag +//
    ]* +// \b +// markdown\s*=\s* ([\'"]) # $2 = attr quote char +// 1 +// \2 +// [^>]* +// > +// ) +// ( # $3 = contents +// .* +// ) +// (
    ) # $4 = closing tag +// \z +// }xs', $block, $matches)) +// { +// list(, $div_open, , $div_content, $div_close) = $matches; +// +// # We can't call Markdown(), because that resets the hash; +// # that initialization code should be pulled into its own sub, though. +// $div_content = $this->hashHTMLBlocks($div_content); +// +// # Run document gamut methods on the content. +// foreach ($this->document_gamut as $method => $priority) { +// $div_content = $this->$method($div_content); +// } +// +// $div_open = preg_replace( +// '{\smarkdown\s*=\s*([\'"]).+?\1}', '', $div_open); +// +// $graf = $div_open . "\n" . $div_content . "\n" . $div_close; +// } + $grafs[$key] = $graf; + } + } + + return implode("\n\n", $grafs); + } + + + function encodeAttribute($text) { + # + # Encode text for a double-quoted HTML attribute. This function + # is *not* suitable for attributes enclosed in single quotes. + # + $text = $this->encodeAmpsAndAngles($text); + $text = str_replace('"', '"', $text); + return $text; + } + + + function encodeAmpsAndAngles($text) { + # + # Smart processing for ampersands and angle brackets that need to + # be encoded. Valid character entities are left alone unless the + # no-entities mode is set. + # + if ($this->no_entities) { + $text = str_replace('&', '&', $text); + } else { + # Ampersand-encoding based entirely on Nat Irons's Amputator + # MT plugin: + $text = preg_replace('/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/', + '&', $text);; + } + # Encode remaining <'s + $text = str_replace('<', '<', $text); + + return $text; + } + + + function doAutoLinks($text) { + $text = preg_replace_callback('{<((https?|ftp|dict):[^\'">\s]+)>}i', + array(&$this, '_doAutoLinks_url_callback'), $text); + + # Email addresses: + $text = preg_replace_callback('{ + < + (?:mailto:)? + ( + (?: + [-!#$%&\'*+/=?^_`.{|}~\w\x80-\xFF]+ + | + ".*?" + ) + \@ + (?: + [-a-z0-9\x80-\xFF]+(\.[-a-z0-9\x80-\xFF]+)*\.[a-z]+ + | + \[[\d.a-fA-F:]+\] # IPv4 & IPv6 + ) + ) + > + }xi', + array(&$this, '_doAutoLinks_email_callback'), $text); + + return $text; + } + function _doAutoLinks_url_callback($matches) { + $url = $this->encodeAttribute($matches[1]); + $link = "$url"; + return $this->hashPart($link); + } + function _doAutoLinks_email_callback($matches) { + $address = $matches[1]; + $link = $this->encodeEmailAddress($address); + return $this->hashPart($link); + } + + + function encodeEmailAddress($addr) { + # + # Input: an email address, e.g. "foo@example.com" + # + # Output: the email address as a mailto link, with each character + # of the address encoded as either a decimal or hex entity, in + # the hopes of foiling most address harvesting spam bots. E.g.: + # + #

    foo@exampl + # e.com

    + # + # Based by a filter by Matthew Wickline, posted to BBEdit-Talk. + # With some optimizations by Milian Wolff. + # + $addr = "mailto:" . $addr; + $chars = preg_split('/(? $char) { + $ord = ord($char); + # Ignore non-ascii chars. + if ($ord < 128) { + $r = ($seed * (1 + $key)) % 100; # Pseudo-random function. + # roughly 10% raw, 45% hex, 45% dec + # '@' *must* be encoded. I insist. + if ($r > 90 && $char != '@') /* do nothing */; + else if ($r < 45) $chars[$key] = '&#x'.dechex($ord).';'; + else $chars[$key] = '&#'.$ord.';'; + } + } + + $addr = implode('', $chars); + $text = implode('', array_slice($chars, 7)); # text without `mailto:` + $addr = "$text"; + + return $addr; + } + + + function parseSpan($str) { + # + # Take the string $str and parse it into tokens, hashing embeded HTML, + # escaped characters and handling code spans. + # + $output = ''; + + $span_re = '{ + ( + \\\\'.$this->escape_chars_re.' + | + (?no_markup ? '' : ' + | + # comment + | + <\?.*?\?> | <%.*?%> # processing instruction + | + <[/!$]?[-a-zA-Z0-9:_]+ # regular tags + (?> + \s + (?>[^"\'>]+|"[^"]*"|\'[^\']*\')* + )? + > + ').' + ) + }xs'; + + while (1) { + # + # Each loop iteration seach for either the next tag, the next + # openning code span marker, or the next escaped character. + # Each token is then passed to handleSpanToken. + # + $parts = preg_split($span_re, $str, 2, PREG_SPLIT_DELIM_CAPTURE); + + # Create token from text preceding tag. + if ($parts[0] != "") { + $output .= $parts[0]; + } + + # Check if we reach the end. + if (isset($parts[1])) { + $output .= $this->handleSpanToken($parts[1], $parts[2]); + $str = $parts[2]; + } + else { + break; + } + } + + return $output; + } + + + function handleSpanToken($token, &$str) { + # + # Handle $token provided by parseSpan by determining its nature and + # returning the corresponding value that should replace it. + # + switch ($token{0}) { + case "\\": + return $this->hashPart("&#". ord($token{1}). ";"); + case "`": + # Search for end marker in remaining text. + if (preg_match('/^(.*?[^`])'.preg_quote($token).'(?!`)(.*)$/sm', + $str, $matches)) + { + $str = $matches[2]; + $codespan = $this->makeCodeSpan($matches[1]); + return $this->hashPart($codespan); + } + return $token; // return as text since no ending marker found. + default: + return $this->hashPart($token); + } + } + + + function outdent($text) { + # + # Remove one level of line-leading tabs or spaces + # + return preg_replace('/^(\t|[ ]{1,'.$this->tab_width.'})/m', '', $text); + } + + + # String length function for detab. `_initDetab` will create a function to + # hanlde UTF-8 if the default function does not exist. + public $utf8_strlen = 'mb_strlen'; + + function detab($text) { + # + # Replace tabs with the appropriate amount of space. + # + # For each line we separate the line in blocks delemited by + # tab characters. Then we reconstruct every line by adding the + # appropriate number of space between each blocks. + + $text = preg_replace_callback('/^.*\t.*$/m', + array(&$this, '_detab_callback'), $text); + + return $text; + } + function _detab_callback($matches) { + $line = $matches[0]; + $strlen = $this->utf8_strlen; # strlen function for UTF-8. + + # Split in blocks. + $blocks = explode("\t", $line); + # Add each blocks to the line. + $line = $blocks[0]; + unset($blocks[0]); # Do not add first block twice. + foreach ($blocks as $block) { + # Calculate amount of space, insert spaces, insert block. + $amount = $this->tab_width - + $strlen($line, 'UTF-8') % $this->tab_width; + $line .= str_repeat(" ", $amount) . $block; + } + return $line; + } + function _initDetab() { + # + # Check for the availability of the function in the `utf8_strlen` property + # (initially `mb_strlen`). If the function is not available, create a + # function that will loosely count the number of UTF-8 characters with a + # regular expression. + # + if (function_exists($this->utf8_strlen)) return; + $this->utf8_strlen = create_function('$text', 'return preg_match_all( + "/[\\\\x00-\\\\xBF]|[\\\\xC0-\\\\xFF][\\\\x80-\\\\xBF]*/", + $text, $m);'); + } + + + function unhash($text) { + # + # Swap back in all the tags hashed by _HashHTMLBlocks. + # + return preg_replace_callback('/(.)\x1A[0-9]+\1/', + array(&$this, '_unhash_callback'), $text); + } + function _unhash_callback($matches) { + return $this->html_hashes[$matches[0]]; + } + +} + +/* + +PHP Markdown +============ + +Description +----------- + +This is a PHP translation of the original Markdown formatter written in +Perl by John Gruber. + +Markdown is a text-to-HTML filter; it translates an easy-to-read / +easy-to-write structured text format into HTML. Markdown's text format +is most similar to that of plain text email, and supports features such +as headers, *emphasis*, code blocks, blockquotes, and links. + +Markdown's syntax is designed not as a generic markup language, but +specifically to serve as a front-end to (X)HTML. You can use span-level +HTML tags anywhere in a Markdown document, and you can use block level +HTML tags (like
    and as well). + +For more information about Markdown's syntax, see: + + + + +Bugs +---- + +To file bug reports please send email to: + + + +Please include with your report: (1) the example input; (2) the output you +expected; (3) the output Markdown actually produced. + + +Version History +--------------- + +See the readme file for detailed release notes for this version. + + +Copyright and License +--------------------- + +PHP Markdown +Copyright (c) 2004-2009 Michel Fortin + +All rights reserved. + +Based on Markdown +Copyright (c) 2003-2006 John Gruber + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name "Markdown" nor the names of its contributors may + be used to endorse or promote products derived from this software + without specific prior written permission. + +This software is provided by the copyright holders and contributors "as +is" and any express or implied warranties, including, but not limited +to, the implied warranties of merchantability and fitness for a +particular purpose are disclaimed. In no event shall the copyright owner +or contributors be liable for any direct, indirect, incidental, special, +exemplary, or consequential damages (including, but not limited to, +procurement of substitute goods or services; loss of use, data, or +profits; or business interruption) however caused and on any theory of +liability, whether in contract, strict liability, or tort (including +negligence or otherwise) arising in any way out of the use of this +software, even if advised of the possibility of such damage. + +*/ +?> diff --git a/includes/mime_lib.php b/includes/mime_lib.php new file mode 100644 index 0000000..6757fe0 --- /dev/null +++ b/includes/mime_lib.php @@ -0,0 +1,32 @@ + diff --git a/includes/mimetypes.php b/includes/mimetypes.php new file mode 100644 index 0000000..e3b3dd4 --- /dev/null +++ b/includes/mimetypes.php @@ -0,0 +1,133 @@ + \"" $1 "\","}}' >> mimetypes.php.new +// done +// echo ");" +static $mimetypes = array( + "ez" => "application/andrew-inset", + "hqx" => "application/mac-binhex40", + "cpt" => "application/mac-compactpro", + "doc" => "application/msword", + "bin" => "application/octet-stream", + "oda" => "application/oda", + "pdf" => "application/pdf", + "ai" => "application/postscript", + "rtf" => "application/rtf", + "smi" => "application/smil", + "mif" => "application/vnd.mif", + "ppt" => "application/vnd.ms-powerpoint", + "xls" => "application/vnd.ms-excel", + "bcpio" => "application/x-bcpio", + "vcd" => "application/x-cdlink", + "pgn" => "application/x-chess-pgn", + "cpio" => "application/x-cpio", + "csh" => "application/x-csh", + "dcr" => "application/x-director", + "dvi" => "application/x-dvi", + "spl" => "application/x-futuresplash", + "gtar" => "application/x-gtar", + "hdf" => "application/x-hdf", + "js" => "application/x-javascript", + "skp" => "application/x-koan", + "latex" => "application/x-latex", + "nc" => "application/x-netcdf", + "sh" => "application/x-sh", + "shar" => "application/x-shar", + "swf" => "application/x-shockwave-flash", + "sit" => "application/x-stuffit", + "sv4cpio" => "application/x-sv4cpio", + "sv4crc" => "application/x-sv4crc", + "tar" => "application/x-tar", + "tcl" => "application/x-tcl", + "tex" => "application/x-tex", + "texinfo" => "application/x-texinfo", + "t" => "application/x-troff", + "man" => "application/x-troff-man", + "me" => "application/x-troff-me", + "ms" => "application/x-troff-ms", + "ustar" => "application/x-ustar", + "src" => "application/x-wais-source", + "zip" => "application/zip", + "au" => "audio/basic", + "mid" => "audio/midi", + "mpga" => "audio/mpeg", + "aif" => "audio/x-aiff", + "ram" => "audio/x-pn-realaudio", + "rpm" => "audio/x-pn-realaudio-plugin", + "ra" => "audio/x-realaudio", + "wav" => "audio/x-wav", + "pdb" => "chemical/x-pdb", + "gif" => "image/gif", + "ief" => "image/ief", + "jpeg" => "image/jpeg", + "png" => "image/png", + "tiff" => "image/tiff", + "ras" => "image/x-cmu-raster", + "pnm" => "image/x-portable-anymap", + "pbm" => "image/x-portable-bitmap", + "pgm" => "image/x-portable-graymap", + "ppm" => "image/x-portable-pixmap", + "rgb" => "image/x-rgb", + "xbm" => "image/x-xbitmap", + "xpm" => "image/x-xpixmap", + "xwd" => "image/x-xwindowdump", + "igs" => "model/iges", + "msh" => "model/mesh", + "wrl" => "model/vrml", + "css" => "text/css", + "html" => "text/html", + "asc" => "text/plain", + "rtx" => "text/richtext", + "rtf" => "text/rtf", + "sgml" => "text/sgml", + "tsv" => "text/tab-separated-values", + "etx" => "text/x-setext", + "xml" => "text/xml", + "mpeg" => "video/mpeg", + "qt" => "video/quicktime", + "avi" => "video/x-msvideo", + "movie" => "video/x-sgi-movie", + "ice" => "x-conference/x-cooltalk", + "dms" => "application/octet-stream", + "eps" => "application/postscript", + "smil" => "application/smil", + "dir" => "application/x-director", + "skd" => "application/x-koan", + "cdf" => "application/x-netcdf", + "texi" => "application/x-texinfo", + "tr" => "application/x-troff", + "snd" => "audio/basic", + "midi" => "audio/midi", + "mp2" => "audio/mpeg", + "aiff" => "audio/x-aiff", + "rm" => "audio/x-pn-realaudio", + "xyz" => "chemical/x-pdb", + "jpg" => "image/jpeg", + "tif" => "image/tiff", + "iges" => "model/iges", + "mesh" => "model/mesh", + "vrml" => "model/vrml", + "htm" => "text/html", + "txt" => "text/plain", + "sgm" => "text/sgml", + "mpg" => "video/mpeg", + "mov" => "video/quicktime", + "lha" => "application/octet-stream", + "ps" => "application/postscript", + "dxr" => "application/x-director", + "skt" => "application/x-koan", + "roff" => "application/x-troff", + "kar" => "audio/midi", + "mp3" => "audio/mpeg", + "aifc" => "audio/x-aiff", + "jpe" => "image/jpeg", + "silo" => "model/mesh", + "mpe" => "video/mpeg", + "lzh" => "application/octet-stream", + "skm" => "application/x-koan", + "exe" => "application/octet-stream", + "class" => "application/octet-stream" +); +?> diff --git a/includes/pclzip_lib.php b/includes/pclzip_lib.php new file mode 100644 index 0000000..e62a292 --- /dev/null +++ b/includes/pclzip_lib.php @@ -0,0 +1,4018 @@ +zipname = $p_zipname; + $this->zip_fd = 0; + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 1); + return; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : + // create($p_filelist, $p_add_dir="", $p_remove_dir="") + // create($p_filelist, $p_option, $p_option_value, ...) + // Description : + // This method supports two different synopsis. The first one is historical. + // This method creates a Zip Archive. The Zip file is created in the + // filesystem. The files and directories indicated in $p_filelist + // are added in the archive. See the parameters description for the + // supported format of $p_filelist. + // When a directory is in the list, the directory and its content is added + // in the archive. + // In this synopsis, the function takes an optional variable list of + // options. See bellow the supported options. + // Parameters : + // $p_filelist : An array containing file or directory names, or + // a string containing one filename or one directory name, or + // a string containing a list of filenames and/or directory + // names separated by spaces. + // $p_add_dir : A path to add before the real path of the archived file, + // in order to have it memorized in the archive. + // $p_remove_dir : A path to remove from the real path of the file to archive, + // in order to have a shorter path memorized in the archive. + // When $p_add_dir and $p_remove_dir are set, $p_remove_dir + // is removed first, before $p_add_dir is added. + // Options : + // PCLZIP_OPT_ADD_PATH : + // PCLZIP_OPT_REMOVE_PATH : + // PCLZIP_OPT_REMOVE_ALL_PATH : + // PCLZIP_CB_PRE_ADD : + // PCLZIP_CB_POST_ADD : + // Return Values : + // 0 on failure, + // The list of the added files, with a status of the add action. + // (see PclZip::listContent() for list entry format) + // -------------------------------------------------------------------------------- +// function create($p_filelist, $p_add_dir="", $p_remove_dir="") + function create($p_filelist /*, options */) + { + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, 'PclZip::create', "filelist='$p_filelist', ..."); + $v_result=1; + // ----- Reset the error handler + $this->privErrorReset(); + // ----- Set default values + $v_options = array(); + $v_add_path = ""; + $v_remove_path = ""; + $v_remove_all_path = false; + $v_options[PCLZIP_OPT_NO_COMPRESSION] = FALSE; + // ----- Look for variable options arguments + $v_size = func_num_args(); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "$v_size arguments passed to the method"); + // ----- Look for arguments + if ($v_size > 1) { + // ----- Get the arguments + $v_arg_list = &func_get_args(); + // ----- Remove form the options list the first argument + array_shift($v_arg_list); + $v_size--; + // ----- Look for first arg + if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Variable list of options detected"); + // ----- Parse the options + $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, + array (PCLZIP_OPT_REMOVE_PATH => 'optional', + PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', + PCLZIP_OPT_ADD_PATH => 'optional', + PCLZIP_CB_PRE_ADD => 'optional', + PCLZIP_CB_POST_ADD => 'optional', + PCLZIP_OPT_NO_COMPRESSION => 'optional' )); + if ($v_result != 1) { + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0); + return 0; + } + // ----- Set the arguments + if (isset($v_options[PCLZIP_OPT_ADD_PATH])) { + $v_add_path = $v_options[PCLZIP_OPT_ADD_PATH]; + } + if (isset($v_options[PCLZIP_OPT_REMOVE_PATH])) { + $v_remove_path = $v_options[PCLZIP_OPT_REMOVE_PATH]; + } + if (isset($v_options[PCLZIP_OPT_REMOVE_ALL_PATH])) { + $v_remove_all_path = $v_options[PCLZIP_OPT_REMOVE_ALL_PATH]; + } + } + // ----- Look for 2 args + // Here we need to support the first historic synopsis of the + // method. + else { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Static synopsis"); + // ----- Get the first argument + $v_add_path = $v_arg_list[0]; + // ----- Look for the optional second argument + if ($v_size == 2) { + $v_remove_path = $v_arg_list[1]; + } + else if ($v_size > 2) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments"); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return 0; + } + } + } + // ----- Trace + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "add_path='$v_add_path', remove_path='$v_remove_path', remove_all_path='".($v_remove_all_path?'true':'false')."'"); + // ----- Look if the $p_filelist is really an array + $p_result_list = array(); + if (is_array($p_filelist)) + { + // ----- Call the create fct + $v_result = $this->privCreate($p_filelist, $p_result_list, $v_add_path, $v_remove_path, $v_remove_all_path, $v_options); + } + // ----- Look if the $p_filelist is a string + else if (is_string($p_filelist)) + { + // ----- Create a list with the elements from the string + $v_list = explode(PCLZIP_SEPARATOR, $p_filelist); + // ----- Call the create fct + $v_result = $this->privCreate($v_list, $p_result_list, $v_add_path, $v_remove_path, $v_remove_all_path, $v_options); + } + // ----- Invalid variable + else + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_filelist"); + $v_result = PCLZIP_ERR_INVALID_PARAMETER; + } + if ($v_result != 1) + { + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0); + return 0; + } + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $p_result_list); + return $p_result_list; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : + // add($p_filelist, $p_add_dir="", $p_remove_dir="") + // add($p_filelist, $p_option, $p_option_value, ...) + // Description : + // This method supports two synopsis. The first one is historical. + // This methods add the list of files in an existing archive. + // If a file with the same name already exists, it is added at the end of the + // archive, the first one is still present. + // If the archive does not exist, it is created. + // Parameters : + // $p_filelist : An array containing file or directory names, or + // a string containing one filename or one directory name, or + // a string containing a list of filenames and/or directory + // names separated by spaces. + // $p_add_dir : A path to add before the real path of the archived file, + // in order to have it memorized in the archive. + // $p_remove_dir : A path to remove from the real path of the file to archive, + // in order to have a shorter path memorized in the archive. + // When $p_add_dir and $p_remove_dir are set, $p_remove_dir + // is removed first, before $p_add_dir is added. + // Options : + // PCLZIP_OPT_ADD_PATH : + // PCLZIP_OPT_REMOVE_PATH : + // PCLZIP_OPT_REMOVE_ALL_PATH : + // PCLZIP_CB_PRE_ADD : + // PCLZIP_CB_POST_ADD : + // Return Values : + // 0 on failure, + // The list of the added files, with a status of the add action. + // (see PclZip::listContent() for list entry format) + // -------------------------------------------------------------------------------- +// function add($p_filelist, $p_add_dir="", $p_remove_dir="") + function add($p_filelist /* options */) + { + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, 'PclZip::add', "filelist='$p_filelist', ..."); + $v_result=1; + // ----- Reset the error handler + $this->privErrorReset(); + // ----- Set default values + $v_options = array(); + $v_add_path = ""; + $v_remove_path = ""; + $v_remove_all_path = false; + $v_options[PCLZIP_OPT_NO_COMPRESSION] = FALSE; + // ----- Look for variable options arguments + $v_size = func_num_args(); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "$v_size arguments passed to the method"); + // ----- Look for arguments + if ($v_size > 1) { + // ----- Get the arguments + $v_arg_list = &func_get_args(); + // ----- Remove form the options list the first argument + array_shift($v_arg_list); + $v_size--; + // ----- Look for first arg + if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Variable list of options detected"); + // ----- Parse the options + $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, + array (PCLZIP_OPT_REMOVE_PATH => 'optional', + PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', + PCLZIP_OPT_ADD_PATH => 'optional', + PCLZIP_CB_PRE_ADD => 'optional', + PCLZIP_CB_POST_ADD => 'optional', + PCLZIP_OPT_NO_COMPRESSION => 'optional' )); + if ($v_result != 1) { + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0); + return 0; + } + // ----- Set the arguments + if (isset($v_options[PCLZIP_OPT_ADD_PATH])) { + $v_add_path = $v_options[PCLZIP_OPT_ADD_PATH]; + } + if (isset($v_options[PCLZIP_OPT_REMOVE_PATH])) { + $v_remove_path = $v_options[PCLZIP_OPT_REMOVE_PATH]; + } + if (isset($v_options[PCLZIP_OPT_REMOVE_ALL_PATH])) { + $v_remove_all_path = $v_options[PCLZIP_OPT_REMOVE_ALL_PATH]; + } + } + // ----- Look for 2 args + // Here we need to support the first historic synopsis of the + // method. + else { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Static synopsis"); + // ----- Get the first argument + $v_add_path = $v_arg_list[0]; + // ----- Look for the optional second argument + if ($v_size == 2) { + $v_remove_path = $v_arg_list[1]; + } + else if ($v_size > 2) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments"); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return 0; + } + } + } + // ----- Trace + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "add_path='$v_add_path', remove_path='$v_remove_path', remove_all_path='".($v_remove_all_path?'true':'false')."'"); + // ----- Look if the $p_filelist is really an array + $p_result_list = array(); + if (is_array($p_filelist)) + { + // ----- Call the create fct + $v_result = $this->privAdd($p_filelist, $p_result_list, $v_add_path, $v_remove_path, $v_remove_all_path, $v_options); + } + // ----- Look if the $p_filelist is a string + else if (is_string($p_filelist)) + { + // ----- Create a list with the elements from the string + $v_list = explode(PCLZIP_SEPARATOR, $p_filelist); + // ----- Call the create fct + $v_result = $this->privAdd($v_list, $p_result_list, $v_add_path, $v_remove_path, $v_remove_all_path, $v_options); + } + // ----- Invalid variable + else + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_filelist"); + $v_result = PCLZIP_ERR_INVALID_PARAMETER; + } + if ($v_result != 1) + { + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0); + return 0; + } + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $p_result_list); + return $p_result_list; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : listContent() + // Description : + // This public method, gives the list of the files and directories, with their + // properties. + // The properties of each entries in the list are (used also in other functions) : + // filename : Name of the file. For a create or add action it is the filename + // given by the user. For an extract function it is the filename + // of the extracted file. + // stored_filename : Name of the file / directory stored in the archive. + // size : Size of the stored file. + // compressed_size : Size of the file's data compressed in the archive + // (without the headers overhead) + // mtime : Last known modification date of the file (UNIX timestamp) + // comment : Comment associated with the file + // folder : true | false + // index : index of the file in the archive + // status : status of the action (depending of the action) : + // Values are : + // ok : OK ! + // filtered : the file / dir is not extracted (filtered by user) + // already_a_directory : the file can not be extracted because a + // directory with the same name already exists + // write_protected : the file can not be extracted because a file + // with the same name already exists and is + // write protected + // newer_exist : the file was not extracted because a newer file exists + // path_creation_fail : the file is not extracted because the folder + // does not exists and can not be created + // write_error : the file was not extracted because there was a + // error while writing the file + // read_error : the file was not extracted because there was a error + // while reading the file + // invalid_header : the file was not extracted because of an archive + // format error (bad file header) + // Note that each time a method can continue operating when there + // is an action error on a file, the error is only logged in the file status. + // Return Values : + // 0 on an unrecoverable failure, + // The list of the files in the archive. + // -------------------------------------------------------------------------------- + function listContent() + { + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, 'PclZip::listContent', ""); + $v_result=1; + // ----- Reset the error handler + $this->privErrorReset(); + // ----- Check archive + if (!$this->privCheckFormat()) { + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0); + return(0); + } + // ----- Call the extracting fct + $p_list = array(); + if (($v_result = $this->privList($p_list)) != 1) + { + unset($p_list); + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0, PclZip::errorInfo()); + return(0); + } + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $p_list); + return $p_list; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : + // extract($p_path="./", $p_remove_path="") + // extract([$p_option, $p_option_value, ...]) + // Description : + // This method supports two synopsis. The first one is historical. + // This method extract all the files / directories from the archive to the + // folder indicated in $p_path. + // If you want to ignore the 'root' part of path of the memorized files + // you can indicate this in the optional $p_remove_path parameter. + // By default, if a newer file with the same name already exists, the + // file is not extracted. + // + // If both PCLZIP_OPT_PATH and PCLZIP_OPT_ADD_PATH aoptions + // are used, the path indicated in PCLZIP_OPT_ADD_PATH is append + // at the end of the path value of PCLZIP_OPT_PATH. + // Parameters : + // $p_path : Path where the files and directories are to be extracted + // $p_remove_path : First part ('root' part) of the memorized path + // (if any similar) to remove while extracting. + // Options : + // PCLZIP_OPT_PATH : + // PCLZIP_OPT_ADD_PATH : + // PCLZIP_OPT_REMOVE_PATH : + // PCLZIP_OPT_REMOVE_ALL_PATH : + // PCLZIP_CB_PRE_EXTRACT : + // PCLZIP_CB_POST_EXTRACT : + // Return Values : + // 0 or a negative value on failure, + // The list of the extracted files, with a status of the action. + // (see PclZip::listContent() for list entry format) + // -------------------------------------------------------------------------------- + //function extract($p_path="./", $p_remove_path="") + function extract(/* options */) + { + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::extract", ""); + $v_result=1; + // ----- Reset the error handler + $this->privErrorReset(); + // ----- Check archive + if (!$this->privCheckFormat()) { + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0); + return(0); + } + // ----- Set default values + $v_options = array(); + $v_path = "./"; + $v_remove_path = ""; + $v_remove_all_path = false; + // ----- Look for variable options arguments + $v_size = func_num_args(); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "$v_size arguments passed to the method"); + // ----- Default values for option + $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE; + // ----- Look for arguments + if ($v_size > 0) { + // ----- Get the arguments + $v_arg_list = &func_get_args(); + // ----- Look for first arg + if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Variable list of options"); + // ----- Parse the options + $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, + array (PCLZIP_OPT_PATH => 'optional', + PCLZIP_OPT_REMOVE_PATH => 'optional', + PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', + PCLZIP_OPT_ADD_PATH => 'optional', + PCLZIP_CB_PRE_EXTRACT => 'optional', + PCLZIP_CB_POST_EXTRACT => 'optional', + PCLZIP_OPT_SET_CHMOD => 'optional', + PCLZIP_OPT_BY_NAME => 'optional', + PCLZIP_OPT_BY_EREG => 'optional', + PCLZIP_OPT_BY_PREG => 'optional', + PCLZIP_OPT_BY_INDEX => 'optional', + PCLZIP_OPT_EXTRACT_AS_STRING => 'optional' )); + if ($v_result != 1) { + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0); + return 0; + } + // ----- Set the arguments + if (isset($v_options[PCLZIP_OPT_PATH])) { + $v_path = $v_options[PCLZIP_OPT_PATH]; + } + if (isset($v_options[PCLZIP_OPT_REMOVE_PATH])) { + $v_remove_path = $v_options[PCLZIP_OPT_REMOVE_PATH]; + } + if (isset($v_options[PCLZIP_OPT_REMOVE_ALL_PATH])) { + $v_remove_all_path = $v_options[PCLZIP_OPT_REMOVE_ALL_PATH]; + } + if (isset($v_options[PCLZIP_OPT_ADD_PATH])) { + // ----- Check for '/' in last path char + if ((strlen($v_path) > 0) && (substr($v_path, -1) != '/')) { + $v_path .= '/'; + } + $v_path .= $v_options[PCLZIP_OPT_ADD_PATH]; + } + } + // ----- Look for 2 args + // Here we need to support the first historic synopsis of the + // method. + else { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Static synopsis"); + // ----- Get the first argument + $v_path = $v_arg_list[0]; + // ----- Look for the optional second argument + if ($v_size == 2) { + $v_remove_path = $v_arg_list[1]; + } + else if ($v_size > 2) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments"); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0, PclZip::errorInfo()); + return 0; + } + } + } + // ----- Trace + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "path='$v_path', remove_path='$v_remove_path', remove_all_path='".($v_remove_path?'true':'false')."'"); + // ----- Call the extracting fct + $p_list = array(); + if (($v_result = $this->privExtractByRule($p_list, $v_path, $v_remove_path, $v_remove_all_path, $v_options)) != 1) + { + unset($p_list); + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0, PclZip::errorInfo()); + return(0); + } + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $p_list); + return $p_list; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : + // extractByIndex($p_index, $p_path="./", $p_remove_path="") + // extractByIndex($p_index, [$p_option, $p_option_value, ...]) + // Description : + // This method supports two synopsis. The first one is historical. + // This method is doing a partial extract of the archive. + // The extracted files or folders are identified by their index in the + // archive (from 0 to n). + // Note that if the index identify a folder, only the folder entry is + // extracted, not all the files included in the archive. + // Parameters : + // $p_index : A single index (integer) or a string of indexes of files to + // extract. The form of the string is "0,4-6,8-12" with only numbers + // and '-' for range or ',' to separate ranges. No spaces or ';' + // are allowed. + // $p_path : Path where the files and directories are to be extracted + // $p_remove_path : First part ('root' part) of the memorized path + // (if any similar) to remove while extracting. + // Options : + // PCLZIP_OPT_PATH : + // PCLZIP_OPT_ADD_PATH : + // PCLZIP_OPT_REMOVE_PATH : + // PCLZIP_OPT_REMOVE_ALL_PATH : + // PCLZIP_OPT_EXTRACT_AS_STRING : The files are extracted as strings and + // not as files. + // The resulting content is in a new field 'content' in the file + // structure. + // This option must be used alone (any other options are ignored). + // PCLZIP_CB_PRE_EXTRACT : + // PCLZIP_CB_POST_EXTRACT : + // Return Values : + // 0 on failure, + // The list of the extracted files, with a status of the action. + // (see PclZip::listContent() for list entry format) + // -------------------------------------------------------------------------------- + function extractByIndex($p_index /* $options */) + { + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::extractByIndex", "index='$p_index', ..."); + $v_result=1; + // ----- Reset the error handler + $this->privErrorReset(); + // ----- Check archive + if (!$this->privCheckFormat()) { + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0); + return(0); + } + // ----- Set default values + $v_options = array(); + $v_path = "./"; + $v_remove_path = ""; + $v_remove_all_path = false; + // ----- Look for variable options arguments + $v_size = func_num_args(); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "$v_size arguments passed to the method"); + // ----- Default values for option + $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE; + // ----- Look for arguments + if ($v_size > 1) { + // ----- Get the arguments + $v_arg_list = &func_get_args(); + // ----- Remove form the options list the first argument + array_shift($v_arg_list); + $v_size--; + // ----- Look for first arg + if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Variable list of options"); + // ----- Parse the options + $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, + array (PCLZIP_OPT_PATH => 'optional', + PCLZIP_OPT_REMOVE_PATH => 'optional', + PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', + PCLZIP_OPT_EXTRACT_AS_STRING => 'optional', + PCLZIP_OPT_ADD_PATH => 'optional', + PCLZIP_CB_PRE_EXTRACT => 'optional', + PCLZIP_CB_POST_EXTRACT => 'optional', + PCLZIP_OPT_SET_CHMOD => 'optional' )); + if ($v_result != 1) { + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0); + return 0; + } + // ----- Set the arguments + if (isset($v_options[PCLZIP_OPT_PATH])) { + $v_path = $v_options[PCLZIP_OPT_PATH]; + } + if (isset($v_options[PCLZIP_OPT_REMOVE_PATH])) { + $v_remove_path = $v_options[PCLZIP_OPT_REMOVE_PATH]; + } + if (isset($v_options[PCLZIP_OPT_REMOVE_ALL_PATH])) { + $v_remove_all_path = $v_options[PCLZIP_OPT_REMOVE_ALL_PATH]; + } + if (isset($v_options[PCLZIP_OPT_ADD_PATH])) { + // ----- Check for '/' in last path char + if ((strlen($v_path) > 0) && (substr($v_path, -1) != '/')) { + $v_path .= '/'; + } + $v_path .= $v_options[PCLZIP_OPT_ADD_PATH]; + } + if (!isset($v_options[PCLZIP_OPT_EXTRACT_AS_STRING])) { + $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE; + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Option PCLZIP_OPT_EXTRACT_AS_STRING not set."); + } + else { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Option PCLZIP_OPT_EXTRACT_AS_STRING set."); + } + } + // ----- Look for 2 args + // Here we need to support the first historic synopsis of the + // method. + else { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Static synopsis"); + // ----- Get the first argument + $v_path = $v_arg_list[0]; + // ----- Look for the optional second argument + if ($v_size == 2) { + $v_remove_path = $v_arg_list[1]; + } + else if ($v_size > 2) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments"); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return 0; + } + } + } + // ----- Trace + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "index='$p_index', path='$v_path', remove_path='$v_remove_path', remove_all_path='".($v_remove_path?'true':'false')."'"); + // ----- Trick + // Here I want to reuse extractByRule(), so I need to parse the $p_index + // with privParseOptions() + $v_arg_trick = array (PCLZIP_OPT_BY_INDEX, $p_index); + $v_options_trick = array(); + $v_result = $this->privParseOptions($v_arg_trick, sizeof($v_arg_trick), $v_options_trick, + array (PCLZIP_OPT_BY_INDEX => 'optional' )); + if ($v_result != 1) { + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0); + return 0; + } + $v_options[PCLZIP_OPT_BY_INDEX] = $v_options_trick[PCLZIP_OPT_BY_INDEX]; + // ----- Call the extracting fct + if (($v_result = $this->privExtractByRule($p_list, $v_path, $v_remove_path, $v_remove_all_path, $v_options)) != 1) { + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0, PclZip::errorInfo()); + return(0); + } + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $p_list); + return $p_list; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : + // delete([$p_option, $p_option_value, ...]) + // Description : + // Parameters : + // None + // Options : + // PCLZIP_OPT_BY_INDEX : + // Return Values : + // 0 on failure, + // The list of the files which are still present in the archive. + // (see PclZip::listContent() for list entry format) + // -------------------------------------------------------------------------------- + function delete(/* options */) + { + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::delete", ""); + $v_result=1; + // ----- Reset the error handler + $this->privErrorReset(); + // ----- Check archive + if (!$this->privCheckFormat()) { + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0); + return(0); + } + // ----- Set default values + $v_options = array(); + // ----- Look for variable options arguments + $v_size = func_num_args(); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "$v_size arguments passed to the method"); + // ----- Look for no arguments + if ($v_size <= 0) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Missing arguments"); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0, PclZip::errorInfo()); + return 0; + } + // ----- Get the arguments + $v_arg_list = &func_get_args(); + // ----- Parse the options + $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, + array (PCLZIP_OPT_BY_NAME => 'optional', + PCLZIP_OPT_BY_EREG => 'optional', + PCLZIP_OPT_BY_PREG => 'optional', + PCLZIP_OPT_BY_INDEX => 'optional' )); + if ($v_result != 1) { + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0); + return 0; + } + // ----- Check that at least one rule is set + if ( (!isset($v_options[PCLZIP_OPT_BY_NAME])) + && (!isset($v_options[PCLZIP_OPT_BY_EREG])) + && (!isset($v_options[PCLZIP_OPT_BY_PREG])) + && (!isset($v_options[PCLZIP_OPT_BY_INDEX]))) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "At least one filtering rule must be set"); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0, PclZip::errorInfo()); + return 0; + } + // ----- Call the delete fct + $v_list = array(); + if (($v_result = $this->privDeleteByRule($v_list, $v_options)) != 1) + { + unset($v_list); + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0, PclZip::errorInfo()); + return(0); + } + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_list); + return $v_list; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : deleteByIndex() + // Description : + // ***** Deprecated ***** + // delete(PCLZIP_OPT_BY_INDEX, $p_index) should be prefered. + // -------------------------------------------------------------------------------- + function deleteByIndex($p_index) + { + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::deleteByIndex", "index='$p_index'"); + $p_list = $this->delete(PCLZIP_OPT_BY_INDEX, $p_index); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $p_list); + return $p_list; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : properties() + // Description : + // This method gives the properties of the archive. + // The properties are : + // nb : Number of files in the archive + // comment : Comment associated with the archive file + // status : not_exist, ok + // Parameters : + // None + // Return Values : + // 0 on failure, + // An array with the archive properties. + // -------------------------------------------------------------------------------- + function properties() + { + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::properties", ""); + // ----- Reset the error handler + $this->privErrorReset(); + // ----- Check archive + if (!$this->privCheckFormat()) { + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0); + return(0); + } + // ----- Default properties + $v_prop = array(); + $v_prop['comment'] = ''; + $v_prop['nb'] = 0; + $v_prop['status'] = 'not_exist'; + // ----- Look if file exists + if (@is_file($this->zipname)) + { + // ----- Open the zip file + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Open file in binary read mode"); + if (($this->zip_fd = @fopen($this->zipname, 'rb')) == 0) + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in binary read mode'); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), 0); + return 0; + } + // ----- Read the central directory informations + $v_central_dir = array(); + if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) + { + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0); + return 0; + } + // ----- Close the zip file + $this->privCloseFd(); + // ----- Set the user attributes + $v_prop['comment'] = $v_central_dir['comment']; + $v_prop['nb'] = $v_central_dir['entries']; + $v_prop['status'] = 'ok'; + } + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_prop); + return $v_prop; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : duplicate() + // Description : + // This method creates an archive by copying the content of an other one. If + // the archive already exist, it is replaced by the new one without any warning. + // Parameters : + // $p_archive : The filename of a valid archive, or + // a valid PclZip object. + // Return Values : + // 1 on success. + // 0 or a negative value on error (error code). + // -------------------------------------------------------------------------------- + function duplicate($p_archive) + { + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::duplicate", ""); + $v_result = 1; + // ----- Reset the error handler + $this->privErrorReset(); + // ----- Look if the $p_archive is a PclZip object + if ((is_object($p_archive)) && (get_class($p_archive) == 'pclzip')) + { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "The parameter is valid PclZip object '".$p_archive->zipname."'"); + // ----- Duplicate the archive + $v_result = $this->privDuplicate($p_archive->zipname); + } + // ----- Look if the $p_archive is a string (so a filename) + else if (is_string($p_archive)) + { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "The parameter is a filename '$p_archive'"); + // ----- Check that $p_archive is a valid zip file + // TBC : Should also check the archive format + if (!is_file($p_archive)) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_FILE, "No file with filename '".$p_archive."'"); + $v_result = PCLZIP_ERR_MISSING_FILE; + } + else { + // ----- Duplicate the archive + $v_result = $this->privDuplicate($p_archive); + } + } + // ----- Invalid variable + else + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_archive_to_add"); + $v_result = PCLZIP_ERR_INVALID_PARAMETER; + } + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : merge() + // Description : + // This method merge the $p_archive_to_add archive at the end of the current + // one ($this). + // If the archive ($this) does not exist, the merge becomes a duplicate. + // If the $p_archive_to_add archive does not exist, the merge is a success. + // Parameters : + // $p_archive_to_add : It can be directly the filename of a valid zip archive, + // or a PclZip object archive. + // Return Values : + // 1 on success, + // 0 or negative values on error (see below). + // -------------------------------------------------------------------------------- + function merge($p_archive_to_add) + { + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::merge", ""); + $v_result = 1; + // ----- Reset the error handler + $this->privErrorReset(); + // ----- Check archive + if (!$this->privCheckFormat()) { + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0); + return(0); + } + // ----- Look if the $p_archive_to_add is a PclZip object + if ((is_object($p_archive_to_add)) && (get_class($p_archive_to_add) == 'pclzip')) + { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "The parameter is valid PclZip object"); + // ----- Merge the archive + $v_result = $this->privMerge($p_archive_to_add); + } + // ----- Look if the $p_archive_to_add is a string (so a filename) + else if (is_string($p_archive_to_add)) + { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "The parameter is a filename"); + // ----- Create a temporary archive + $v_object_archive = new PclZip($p_archive_to_add); + // ----- Merge the archive + $v_result = $this->privMerge($v_object_archive); + } + // ----- Invalid variable + else + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_archive_to_add"); + $v_result = PCLZIP_ERR_INVALID_PARAMETER; + } + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : errorCode() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + function errorCode() + { + if (PCLZIP_ERROR_EXTERNAL == 1) { + return(PclErrorCode()); + } + else { + return($this->error_code); + } + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : errorName() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + function errorName($p_with_code=false) + { + $v_name = array ( PCLZIP_ERR_NO_ERROR => 'PCLZIP_ERR_NO_ERROR', + PCLZIP_ERR_WRITE_OPEN_FAIL => 'PCLZIP_ERR_WRITE_OPEN_FAIL', + PCLZIP_ERR_READ_OPEN_FAIL => 'PCLZIP_ERR_READ_OPEN_FAIL', + PCLZIP_ERR_INVALID_PARAMETER => 'PCLZIP_ERR_INVALID_PARAMETER', + PCLZIP_ERR_MISSING_FILE => 'PCLZIP_ERR_MISSING_FILE', + PCLZIP_ERR_FILENAME_TOO_LONG => 'PCLZIP_ERR_FILENAME_TOO_LONG', + PCLZIP_ERR_INVALID_ZIP => 'PCLZIP_ERR_INVALID_ZIP', + PCLZIP_ERR_BAD_EXTRACTED_FILE => 'PCLZIP_ERR_BAD_EXTRACTED_FILE', + PCLZIP_ERR_DIR_CREATE_FAIL => 'PCLZIP_ERR_DIR_CREATE_FAIL', + PCLZIP_ERR_BAD_EXTENSION => 'PCLZIP_ERR_BAD_EXTENSION', + PCLZIP_ERR_BAD_FORMAT => 'PCLZIP_ERR_BAD_FORMAT', + PCLZIP_ERR_DELETE_FILE_FAIL => 'PCLZIP_ERR_DELETE_FILE_FAIL', + PCLZIP_ERR_RENAME_FILE_FAIL => 'PCLZIP_ERR_RENAME_FILE_FAIL', + PCLZIP_ERR_BAD_CHECKSUM => 'PCLZIP_ERR_BAD_CHECKSUM', + PCLZIP_ERR_INVALID_ARCHIVE_ZIP => 'PCLZIP_ERR_INVALID_ARCHIVE_ZIP', + PCLZIP_ERR_MISSING_OPTION_VALUE => 'PCLZIP_ERR_MISSING_OPTION_VALUE', + PCLZIP_ERR_INVALID_OPTION_VALUE => 'PCLZIP_ERR_INVALID_OPTION_VALUE' ); + if (isset($v_name[$this->error_code])) { + $v_value = $v_name[$this->error_code]; + } + else { + $v_value = 'NoName'; + } + if ($p_with_code) { + return($v_value.' ('.$this->error_code.')'); + } + else { + return($v_value); + } + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : errorInfo() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + function errorInfo($p_full=false) + { + if (PCLZIP_ERROR_EXTERNAL == 1) { + return(PclErrorString()); + } + else { + if ($p_full) { + return($this->errorName(true)." : ".$this->error_string); + } + else { + return($this->error_string." [code ".$this->error_code."]"); + } + } + } + // -------------------------------------------------------------------------------- +// -------------------------------------------------------------------------------- +// ***** UNDER THIS LINE ARE DEFINED PRIVATE INTERNAL FUNCTIONS ***** +// ***** ***** +// ***** THESES FUNCTIONS MUST NOT BE USED DIRECTLY ***** +// -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : privCheckFormat() + // Description : + // This method check that the archive exists and is a valid zip archive. + // Several level of check exists. (futur) + // Parameters : + // $p_level : Level of check. Default 0. + // 0 : Check the first bytes (magic codes) (default value)) + // 1 : 0 + Check the central directory (futur) + // 2 : 1 + Check each file header (futur) + // Return Values : + // true on success, + // false on error, the error code is set. + // -------------------------------------------------------------------------------- + function privCheckFormat($p_level=0) + { + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privCheckFormat", ""); + $v_result = true; + // ----- Reset the error handler + $this->privErrorReset(); + // ----- Look if the file exits + if (!is_file($this->zipname)) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_FILE, "Missing archive file '".$this->zipname."'"); + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, false, PclZip::errorInfo()); + return(false); + } + // ----- Check that the file is readeable + if (!is_readable($this->zipname)) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to read archive '".$this->zipname."'"); + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, false, PclZip::errorInfo()); + return(false); + } + // ----- Check the magic code + // TBC + // ----- Check the central header + // TBC + // ----- Check each file header + // TBC + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : privParseOptions() + // Description : + // This internal methods reads the variable list of arguments ($p_options_list, + // $p_size) and generate an array with the options and values ($v_result_list). + // $v_requested_options contains the options that can be present and those that + // must be present. + // $v_requested_options is an array, with the option value as key, and 'optional', + // or 'mandatory' as value. + // Parameters : + // See above. + // Return Values : + // 1 on success. + // 0 on failure. + // -------------------------------------------------------------------------------- + function privParseOptions(&$p_options_list, $p_size, &$v_result_list, $v_requested_options) + { + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privParseOptions", ""); + $v_result=1; + // ----- Read the options + $i=0; + while ($i<$p_size) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Looking for table index $i, option = '".PclZipUtilOptionText($p_options_list[$i])."(".$p_options_list[$i].")'"); + // ----- Check if the option is requested + if (!isset($v_requested_options[$p_options_list[$i]])) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid optional parameter '".$p_options_list[$i]."' for this method"); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + // ----- Look for next option + switch ($p_options_list[$i]) { + // ----- Look for options that request a path value + case PCLZIP_OPT_PATH : + case PCLZIP_OPT_REMOVE_PATH : + case PCLZIP_OPT_ADD_PATH : + // ----- Check the number of parameters + if (($i+1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + // ----- Get the value + $v_result_list[$p_options_list[$i]] = PclZipUtilTranslateWinPath($p_options_list[$i+1], false); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "".PclZipUtilOptionText($p_options_list[$i])." = '".$v_result_list[$p_options_list[$i]]."'"); + $i++; + break; + // ----- Look for options that request an array of string for value + case PCLZIP_OPT_BY_NAME : + // ----- Check the number of parameters + if (($i+1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + // ----- Get the value + if (is_string($p_options_list[$i+1])) { + $v_result_list[$p_options_list[$i]][0] = $p_options_list[$i+1]; + } + else if (is_array($p_options_list[$i+1])) { + $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1]; + } + else { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Wrong parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + ////--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "".PclZipUtilOptionText($p_options_list[$i])." = '".$v_result_list[$p_options_list[$i]]."'"); + $i++; + break; + // ----- Look for options that request an EREG or PREG expression + case PCLZIP_OPT_BY_EREG : + case PCLZIP_OPT_BY_PREG : + // ----- Check the number of parameters + if (($i+1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + // ----- Get the value + if (is_string($p_options_list[$i+1])) { + $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1]; + } + else { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Wrong parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "".PclZipUtilOptionText($p_options_list[$i])." = '".$v_result_list[$p_options_list[$i]]."'"); + $i++; + break; + // ----- Look for options that request an array of index + case PCLZIP_OPT_BY_INDEX : + // ----- Check the number of parameters + if (($i+1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + // ----- Get the value + $v_work_list = array(); + if (is_string($p_options_list[$i+1])) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Index value is a string '".$p_options_list[$i+1]."'"); + // ----- Remove spaces + $p_options_list[$i+1] = strtr($p_options_list[$i+1], ' ', ''); + // ----- Parse items + $v_work_list = explode(",", $p_options_list[$i+1]); + } + else if (is_integer($p_options_list[$i+1])) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Index value is an integer '".$p_options_list[$i+1]."'"); + $v_work_list[0] = $p_options_list[$i+1].'-'.$p_options_list[$i+1]; + } + else if (is_array($p_options_list[$i+1])) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Index value is an array"); + $v_work_list = $p_options_list[$i+1]; + } + else { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Value must be integer, string or array for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + // ----- Reduce the index list + // each index item in the list must be a couple with a start and + // an end value : [0,3], [5-5], [8-10], ... + // ----- Check the format of each item + $v_sort_flag=false; + $v_sort_value=0; + for ($j=0; $j= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + // ----- Get the value + $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1]; + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "".PclZipUtilOptionText($p_options_list[$i])." = '".$v_result_list[$p_options_list[$i]]."'"); + $i++; + break; + // ----- Look for options that request a call-back + case PCLZIP_CB_PRE_EXTRACT : + case PCLZIP_CB_POST_EXTRACT : + case PCLZIP_CB_PRE_ADD : + case PCLZIP_CB_POST_ADD : + /* for futur use + case PCLZIP_CB_PRE_DELETE : + case PCLZIP_CB_POST_DELETE : + case PCLZIP_CB_PRE_LIST : + case PCLZIP_CB_POST_LIST : + */ + // ----- Check the number of parameters + if (($i+1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + // ----- Get the value + $v_function_name = $p_options_list[$i+1]; + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "call-back ".PclZipUtilOptionText($p_options_list[$i])." = '".$v_function_name."'"); + // ----- Check that the value is a valid existing function + if (!function_exists($v_function_name)) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Function '".$v_function_name."()' is not an existing function for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + // ----- Set the attribute + $v_result_list[$p_options_list[$i]] = $v_function_name; + $i++; + break; + default : + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Unknown optional parameter '".$p_options_list[$i]."'"); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + // ----- Next options + $i++; + } + // ----- Look for mandatory options + for ($key=reset($v_requested_options); $key=key($v_requested_options); $key=next($v_requested_options)) { + // ----- Look for mandatory option + if ($v_requested_options[$key] == 'mandatory') { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Detect a mandatory option : ".PclZipUtilOptionText($key)."(".$key.")"); + // ----- Look if present + if (!isset($v_result_list[$key])) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Missing mandatory parameter ".PclZipUtilOptionText($key)."(".$key.")"); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + } + } + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : privCreate() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privCreate($p_list, &$p_result_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, &$p_options) + { + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privCreate", "list, result_list, add_dir='$p_add_dir', remove_dir='$p_remove_dir'"); + $v_result=1; + $v_list_detail = array(); + // ----- Open the file in write mode + if (($v_result = $this->privOpenFd('wb')) != 1) + { + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // ----- Add the list of files + $v_result = $this->privAddList($p_list, $p_result_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, $p_options); + // ----- Close + $this->privCloseFd(); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : privAdd() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privAdd($p_list, &$p_result_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, &$p_options) + { + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privAdd", "list, result_list, add_dir='$p_add_dir', remove_dir='$p_remove_dir'"); + $v_result=1; + $v_list_detail = array(); + // ----- Look if the archive exists or is empty + if ((!is_file($this->zipname)) || (filesize($this->zipname) == 0)) + { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Archive does not exist, or is empty, create it."); + // ----- Do a create + $v_result = $this->privCreate($p_list, $p_result_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, $p_options); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // ----- Open the zip file + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Open file in binary read mode"); + if (($v_result=$this->privOpenFd('rb')) != 1) + { + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // ----- Read the central directory informations + $v_central_dir = array(); + if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) + { + $this->privCloseFd(); + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // ----- Go to beginning of File + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position in file : ".ftell($this->zip_fd)."'"); + @rewind($this->zip_fd); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position in file : ".ftell($this->zip_fd)."'"); + // ----- Creates a temporay file + $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp'; + // ----- Open the temporary file in write mode + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Open file in binary read mode"); + if (($v_zip_temp_fd = @fopen($v_zip_temp_name, 'wb')) == 0) + { + $this->privCloseFd(); + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_zip_temp_name.'\' in binary write mode'); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + // ----- Copy the files from the archive to the temporary file + // TBC : Here I should better append the file and go back to erase the central dir + $v_size = $v_central_dir['offset']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Read $v_read_size bytes"); + $v_buffer = fread($this->zip_fd, $v_read_size); + @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + // ----- Swap the file descriptor + // Here is a trick : I swap the temporary fd with the zip fd, in order to use + // the following methods on the temporary fil and not the real archive + $v_swap = $this->zip_fd; + $this->zip_fd = $v_zip_temp_fd; + $v_zip_temp_fd = $v_swap; + // ----- Add the files + $v_header_list = array(); + if (($v_result = $this->privAddFileList($p_list, $v_header_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, $p_options)) != 1) + { + fclose($v_zip_temp_fd); + $this->privCloseFd(); + @unlink($v_zip_temp_name); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // ----- Store the offset of the central dir + $v_offset = @ftell($this->zip_fd); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "New offset of central dir : $v_offset"); + // ----- Copy the block of file headers from the old archive + $v_size = $v_central_dir['size']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Read $v_read_size bytes"); + $v_buffer = @fread($v_zip_temp_fd, $v_read_size); + @fwrite($this->zip_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + // ----- Create the Central Dir files header + for ($i=0, $v_count=0; $iprivWriteCentralFileHeader($v_header_list[$i])) != 1) { + fclose($v_zip_temp_fd); + $this->privCloseFd(); + @unlink($v_zip_temp_name); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + $v_count++; + } + // ----- Transform the header to a 'usable' info + $this->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]); + } + // ----- Zip file comment + $v_comment = ''; + // ----- Calculate the size of the central header + $v_size = @ftell($this->zip_fd)-$v_offset; + // ----- Create the central dir footer + if (($v_result = $this->privWriteCentralHeader($v_count+$v_central_dir['entries'], $v_size, $v_offset, $v_comment)) != 1) + { + // ----- Reset the file list + unset($v_header_list); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // ----- Swap back the file descriptor + $v_swap = $this->zip_fd; + $this->zip_fd = $v_zip_temp_fd; + $v_zip_temp_fd = $v_swap; + // ----- Close + $this->privCloseFd(); + // ----- Close the temporary file + @fclose($v_zip_temp_fd); + // ----- Delete the zip file + // TBC : I should test the result ... + @unlink($this->zipname); + // ----- Rename the temporary file + // TBC : I should test the result ... + //@rename($v_zip_temp_name, $this->zipname); + PclZipUtilRename($v_zip_temp_name, $this->zipname); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : privOpenFd() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + function privOpenFd($p_mode) + { + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privOpenFd", 'mode='.$p_mode); + $v_result=1; + // ----- Look if already open + if ($this->zip_fd != 0) + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Zip file \''.$this->zipname.'\' already open'); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + // ----- Open the zip file + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Open file in '.$p_mode.' mode'); + if (($this->zip_fd = @fopen($this->zipname, $p_mode)) == 0) + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in '.$p_mode.' mode'); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : privCloseFd() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + function privCloseFd() + { + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privCloseFd", ""); + $v_result=1; + if ($this->zip_fd != 0) + @fclose($this->zip_fd); + $this->zip_fd = 0; + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : privAddList() + // Description : + // $p_add_dir and $p_remove_dir will give the ability to memorize a path which is + // different from the real path of the file. This is usefull if you want to have PclTar + // running in any directory, and memorize relative path from an other directory. + // Parameters : + // $p_list : An array containing the file or directory names to add in the tar + // $p_result_list : list of added files with their properties (specially the status field) + // $p_add_dir : Path to add in the filename path archived + // $p_remove_dir : Path to remove in the filename path archived + // Return Values : + // -------------------------------------------------------------------------------- + function privAddList($p_list, &$p_result_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, &$p_options) + { + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privAddList", "list, add_dir='$p_add_dir', remove_dir='$p_remove_dir'"); + $v_result=1; + // ----- Add the files + $v_header_list = array(); + if (($v_result = $this->privAddFileList($p_list, $v_header_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, $p_options)) != 1) + { + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // ----- Store the offset of the central dir + $v_offset = @ftell($this->zip_fd); + // ----- Create the Central Dir files header + for ($i=0,$v_count=0; $iprivWriteCentralFileHeader($v_header_list[$i])) != 1) { + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + $v_count++; + } + // ----- Transform the header to a 'usable' info + $this->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]); + } + // ----- Zip file comment + $v_comment = ''; + // ----- Calculate the size of the central header + $v_size = @ftell($this->zip_fd)-$v_offset; + // ----- Create the central dir footer + if (($v_result = $this->privWriteCentralHeader($v_count, $v_size, $v_offset, $v_comment)) != 1) + { + // ----- Reset the file list + unset($v_header_list); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : privAddFileList() + // Description : + // $p_add_dir and $p_remove_dir will give the ability to memorize a path which is + // different from the real path of the file. This is usefull if you want to + // run the lib in any directory, and memorize relative path from an other directory. + // Parameters : + // $p_list : An array containing the file or directory names to add in the tar + // $p_result_list : list of added files with their properties (specially the status field) + // $p_add_dir : Path to add in the filename path archived + // $p_remove_dir : Path to remove in the filename path archived + // Return Values : + // -------------------------------------------------------------------------------- + function privAddFileList($p_list, &$p_result_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, &$p_options) + { + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privAddFileList", "list, add_dir='$p_add_dir', remove_dir='$p_remove_dir'"); + $v_result=1; + $v_header = array(); + // ----- Recuperate the current number of elt in list + $v_nb = sizeof($p_result_list); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Before add, list have $v_nb elements"); + // ----- Loop on the files + for ($j=0; ($j 0xFF) + { + // ----- Error log + PclZip::privErrorLog(-5, "File name is too long (max. 255) : '$p_filename'"); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + */ + // ----- Look if it is a file or a dir with no all pathnre move + if ((is_file($p_filename)) || ((is_dir($p_filename)) && !$p_remove_all_dir)) { + // ----- Add the file + if (($v_result = $this->privAddFile($p_filename, $v_header, $p_add_dir, $p_remove_dir, $p_remove_all_dir, $p_options)) != 1) + { + // ----- Return status + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // ----- Store the file infos + $p_result_list[$v_nb++] = $v_header; + } + // ----- Look for directory + if (is_dir($p_filename)) + { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "$p_filename is a directory"); + // ----- Look for path + if ($p_filename != ".") + $v_path = $p_filename."/"; + else + $v_path = ""; + // ----- Read the directory for files and sub-directories + $p_hdir = opendir($p_filename); + $p_hitem = readdir($p_hdir); // '.' directory + $p_hitem = readdir($p_hdir); // '..' directory + while ($p_hitem = readdir($p_hdir)) + { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Looking for $p_hitem in the directory"); + // ----- Look for a file + if (is_file($v_path.$p_hitem)) + { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Add the file '".$v_path.$p_hitem."'"); + // ----- Add the file + if (($v_result = $this->privAddFile($v_path.$p_hitem, $v_header, $p_add_dir, $p_remove_dir, $p_remove_all_dir, $p_options)) != 1) + { + // ----- Return status + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // ----- Store the file infos + $p_result_list[$v_nb++] = $v_header; + } + // ----- Recursive call to privAddFileList() + else + { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Add the directory '".$v_path.$p_hitem."'"); + // ----- Need an array as parameter + $p_temp_list[0] = $v_path.$p_hitem; + $v_result = $this->privAddFileList($p_temp_list, $p_result_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, $p_options); + // ----- Update the number of elements of the list + $v_nb = sizeof($p_result_list); + } + } + // ----- Free memory for the recursive loop + unset($p_temp_list); + unset($p_hdir); + unset($p_hitem); + } + } + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "After add, list have $v_nb elements"); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : privAddFile() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privAddFile($p_filename, &$p_header, $p_add_dir, $p_remove_dir, $p_remove_all_dir, &$p_options) + { + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privAddFile", "filename='$p_filename', add_dir='$p_add_dir', remove_dir='$p_remove_dir'"); + $v_result=1; + if ($p_filename == "") + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid file list parameter (invalid or empty list)"); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + // ----- Calculate the stored filename + $v_stored_filename = $p_filename; + // ----- Look for all path to remove + if ($p_remove_all_dir) { + $v_stored_filename = basename($p_filename); + } + // ----- Look for partial path remove + else if ($p_remove_dir != "") + { + if (substr($p_remove_dir, -1) != '/') + $p_remove_dir .= "/"; + if ((substr($p_filename, 0, 2) == "./") || (substr($p_remove_dir, 0, 2) == "./")) + { + if ((substr($p_filename, 0, 2) == "./") && (substr($p_remove_dir, 0, 2) != "./")) + $p_remove_dir = "./".$p_remove_dir; + if ((substr($p_filename, 0, 2) != "./") && (substr($p_remove_dir, 0, 2) == "./")) + $p_remove_dir = substr($p_remove_dir, 2); + } + $v_compare = PclZipUtilPathInclusion($p_remove_dir, $p_filename); + if ($v_compare > 0) +// if (substr($p_filename, 0, strlen($p_remove_dir)) == $p_remove_dir) + { + if ($v_compare == 2) { + $v_stored_filename = ""; + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Path to remove is the current folder"); + } + else { + $v_stored_filename = substr($p_filename, strlen($p_remove_dir)); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Remove path '$p_remove_dir' in file '$p_filename' = '$v_stored_filename'"); + } + } + } + // ----- Look for path to add + if ($p_add_dir != "") + { + if (substr($p_add_dir, -1) == "/") + $v_stored_filename = $p_add_dir.$v_stored_filename; + else + $v_stored_filename = $p_add_dir."/".$v_stored_filename; + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Add path '$p_add_dir' in file '$p_filename' = '$v_stored_filename'"); + } + // ----- Filename (reduce the path of stored name) + $v_stored_filename = PclZipUtilPathReduction($v_stored_filename); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Filename (reduced) '$v_stored_filename', strlen ".strlen($v_stored_filename)); + /* filename length moved after call-back in release 1.3 + // ----- Check the path length + if (strlen($v_stored_filename) > 0xFF) + { + // ----- Error log + PclZip::privErrorLog(-5, "Stored file name is too long (max. 255) : '$v_stored_filename'"); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + */ + // ----- Set the file properties + clearstatcache(); + $p_header['version'] = 20; + $p_header['version_extracted'] = 10; + $p_header['flag'] = 0; + $p_header['compression'] = 0; + $p_header['mtime'] = filemtime($p_filename); + $p_header['crc'] = 0; + $p_header['compressed_size'] = 0; + $p_header['size'] = filesize($p_filename); + $p_header['filename_len'] = strlen($p_filename); + $p_header['extra_len'] = 0; + $p_header['comment_len'] = 0; + $p_header['disk'] = 0; + $p_header['internal'] = 0; + $p_header['external'] = (is_file($p_filename)?0xFE49FFE0:0x41FF0010); + $p_header['offset'] = 0; + $p_header['filename'] = $p_filename; + $p_header['stored_filename'] = $v_stored_filename; + $p_header['extra'] = ''; + $p_header['comment'] = ''; + $p_header['status'] = 'ok'; + $p_header['index'] = -1; + // ----- Look for pre-add callback + if (isset($p_options[PCLZIP_CB_PRE_ADD])) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "A pre-callback '".$p_options[PCLZIP_CB_PRE_ADD]."()') is defined for the extraction"); + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_header, $v_local_header); + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. + eval('$v_result = '.$p_options[PCLZIP_CB_PRE_ADD].'(PCLZIP_CB_PRE_ADD, $v_local_header);'); + if ($v_result == 0) { + // ----- Change the file status + $p_header['status'] = "skipped"; + $v_result = 1; + } + // ----- Update the informations + // Only some fields can be modified + if ($p_header['stored_filename'] != $v_local_header['stored_filename']) { + $p_header['stored_filename'] = PclZipUtilPathReduction($v_local_header['stored_filename']); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "New stored filename is '".$p_header['stored_filename']."'"); + } + } + // ----- Look for empty stored filename + if ($p_header['stored_filename'] == "") { + $p_header['status'] = "filtered"; + } + // ----- Check the path length + if (strlen($p_header['stored_filename']) > 0xFF) { + $p_header['status'] = 'filename_too_long'; + } + // ----- Look if no error, or file not skipped + if ($p_header['status'] == 'ok') { + // ----- Look for a file + if (is_file($p_filename)) + { + // ----- Open the source file + if (($v_file = @fopen($p_filename, "rb")) == 0) { + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to open file '$p_filename' in binary read mode"); + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + if ($p_options[PCLZIP_OPT_NO_COMPRESSION]) { + // ----- Read the file content + $v_content_compressed = @fread($v_file, $p_header['size']); + // ----- Calculate the CRC + $p_header['crc'] = crc32($v_content_compressed); + } + else { + // ----- Read the file content + $v_content = @fread($v_file, $p_header['size']); + // ----- Calculate the CRC + $p_header['crc'] = crc32($v_content); + // ----- Compress the file + $v_content_compressed = gzdeflate($v_content); + } + // ----- Set header parameters + $p_header['compressed_size'] = strlen($v_content_compressed); + $p_header['compression'] = 8; + // ----- Call the header generation + if (($v_result = $this->privWriteFileHeader($p_header)) != 1) { + @fclose($v_file); + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // ----- Write the compressed content + $v_binary_data = pack('a'.$p_header['compressed_size'], $v_content_compressed); + @fwrite($this->zip_fd, $v_binary_data, $p_header['compressed_size']); + // ----- Close the file + @fclose($v_file); + } + // ----- Look for a directory + else + { + // ----- Set the file properties + $p_header['filename'] .= '/'; + $p_header['filename_len']++; + $p_header['size'] = 0; + $p_header['external'] = 0x41FF0010; // Value for a folder : to be checked + // ----- Call the header generation + if (($v_result = $this->privWriteFileHeader($p_header)) != 1) + { + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + } + } + // ----- Look for pre-add callback + if (isset($p_options[PCLZIP_CB_POST_ADD])) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "A post-callback '".$p_options[PCLZIP_CB_POST_ADD]."()') is defined for the extraction"); + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_header, $v_local_header); + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. + eval('$v_result = '.$p_options[PCLZIP_CB_POST_ADD].'(PCLZIP_CB_POST_ADD, $v_local_header);'); + if ($v_result == 0) { + // ----- Ignored + $v_result = 1; + } + // ----- Update the informations + // Nothing can be modified + } + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : privWriteFileHeader() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privWriteFileHeader(&$p_header) + { + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privWriteFileHeader", 'file="'.$p_header['filename'].'", stored as "'.$p_header['stored_filename'].'"'); + $v_result=1; + // TBC + //for(reset($p_header); $key = key($p_header); next($p_header)) { + // //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "header[$key] = ".$p_header[$key]); + //} + // ----- Store the offset position of the file + $p_header['offset'] = ftell($this->zip_fd); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, 'File offset of the header :'.$p_header['offset']); + // ----- Transform UNIX mtime to DOS format mdate/mtime + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Date : \''.date("d/m/y H:i:s", $p_header['mtime']).'\''); + $v_date = getdate($p_header['mtime']); + $v_mtime = ($v_date['hours']<<11) + ($v_date['minutes']<<5) + $v_date['seconds']/2; + $v_mdate = (($v_date['year']-1980)<<9) + ($v_date['mon']<<5) + $v_date['mday']; + // ----- Packed data + $v_binary_data = pack("VvvvvvVVVvv", 0x04034b50, $p_header['version'], $p_header['flag'], + $p_header['compression'], $v_mtime, $v_mdate, + $p_header['crc'], $p_header['compressed_size'], $p_header['size'], + strlen($p_header['stored_filename']), $p_header['extra_len']); + // ----- Write the first 148 bytes of the header in the archive + fputs($this->zip_fd, $v_binary_data, 30); + // ----- Write the variable fields + if (strlen($p_header['stored_filename']) != 0) + { + fputs($this->zip_fd, $p_header['stored_filename'], strlen($p_header['stored_filename'])); + } + if ($p_header['extra_len'] != 0) + { + fputs($this->zip_fd, $p_header['extra'], $p_header['extra_len']); + } + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : privWriteCentralFileHeader() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privWriteCentralFileHeader(&$p_header) + { + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privWriteCentralFileHeader", 'file="'.$p_header['filename'].'", stored as "'.$p_header['stored_filename'].'"'); + $v_result=1; + // TBC + //for(reset($p_header); $key = key($p_header); next($p_header)) { + // //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "header[$key] = ".$p_header[$key]); + //} + // ----- Transform UNIX mtime to DOS format mdate/mtime + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Date : \''.date("d/m/y H:i:s", $p_header['mtime']).'\''); + $v_date = getdate($p_header['mtime']); + $v_mtime = ($v_date['hours']<<11) + ($v_date['minutes']<<5) + $v_date['seconds']/2; + $v_mdate = (($v_date['year']-1980)<<9) + ($v_date['mon']<<5) + $v_date['mday']; + // ----- Packed data + $v_binary_data = pack("VvvvvvvVVVvvvvvVV", 0x02014b50, $p_header['version'], $p_header['version_extracted'], + $p_header['flag'], $p_header['compression'], $v_mtime, $v_mdate, $p_header['crc'], + $p_header['compressed_size'], $p_header['size'], + strlen($p_header['stored_filename']), $p_header['extra_len'], $p_header['comment_len'], + $p_header['disk'], $p_header['internal'], $p_header['external'], $p_header['offset']); + // ----- Write the 42 bytes of the header in the zip file + fputs($this->zip_fd, $v_binary_data, 46); + // ----- Write the variable fields + if (strlen($p_header['stored_filename']) != 0) + { + fputs($this->zip_fd, $p_header['stored_filename'], strlen($p_header['stored_filename'])); + } + if ($p_header['extra_len'] != 0) + { + fputs($this->zip_fd, $p_header['extra'], $p_header['extra_len']); + } + if ($p_header['comment_len'] != 0) + { + fputs($this->zip_fd, $p_header['comment'], $p_header['comment_len']); + } + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : privWriteCentralHeader() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privWriteCentralHeader($p_nb_entries, $p_size, $p_offset, $p_comment) + { + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privWriteCentralHeader", 'nb_entries='.$p_nb_entries.', size='.$p_size.', offset='.$p_offset.', comment="'.$p_comment.'"'); + $v_result=1; + // ----- Packed data + $v_binary_data = pack("VvvvvVVv", 0x06054b50, 0, 0, $p_nb_entries, $p_nb_entries, $p_size, $p_offset, strlen($p_comment)); + // ----- Write the 22 bytes of the header in the zip file + fputs($this->zip_fd, $v_binary_data, 22); + // ----- Write the variable fields + if (strlen($p_comment) != 0) + { + fputs($this->zip_fd, $p_comment, strlen($p_comment)); + } + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : privList() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privList(&$p_list) + { + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privList", "list"); + $v_result=1; + // ----- Open the zip file + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Open file in binary read mode"); + if (($this->zip_fd = @fopen($this->zipname, 'rb')) == 0) + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in binary read mode'); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + // ----- Read the central directory informations + $v_central_dir = array(); + if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) + { + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // ----- Go to beginning of Central Dir + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Offset : ".$v_central_dir['offset']."'"); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Position in file : ".ftell($this->zip_fd)."'"); + @rewind($this->zip_fd); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Position in file : ".ftell($this->zip_fd)."'"); + if (@fseek($this->zip_fd, $v_central_dir['offset'])) + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Position in file : ".ftell($this->zip_fd)."'"); + // ----- Read each entry + for ($i=0; $i<$v_central_dir['entries']; $i++) + { + // ----- Read the file header + if (($v_result = $this->privReadCentralFileHeader($v_header)) != 1) + { + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + $v_header['index'] = $i; + // ----- Get the only interesting attributes + $this->privConvertHeader2FileInfo($v_header, $p_list[$i]); + unset($v_header); + } + // ----- Close the zip file + $this->privCloseFd(); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : privConvertHeader2FileInfo() + // Description : + // This function takes the file informations from the central directory + // entries and extract the interesting parameters that will be given back. + // The resulting file infos are set in the array $p_info + // $p_info['filename'] : Filename with full path. Given by user (add), + // extracted in the filesystem (extract). + // $p_info['stored_filename'] : Stored filename in the archive. + // $p_info['size'] = Size of the file. + // $p_info['compressed_size'] = Compressed size of the file. + // $p_info['mtime'] = Last modification date of the file. + // $p_info['comment'] = Comment associated with the file. + // $p_info['folder'] = true/false : indicates if the entry is a folder or not. + // $p_info['status'] = status of the action on the file. + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privConvertHeader2FileInfo($p_header, &$p_info) + { + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privConvertHeader2FileInfo", "Filename='".$p_header['filename']."'"); + $v_result=1; + // ----- Get the interesting attributes + $p_info['filename'] = $p_header['filename']; + $p_info['stored_filename'] = $p_header['stored_filename']; + $p_info['size'] = $p_header['size']; + $p_info['compressed_size'] = $p_header['compressed_size']; + $p_info['mtime'] = $p_header['mtime']; + $p_info['comment'] = $p_header['comment']; + $p_info['folder'] = (($p_header['external']&0x00000010)==0x00000010); + $p_info['index'] = $p_header['index']; + $p_info['status'] = $p_header['status']; + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : privExtractByRule() + // Description : + // Extract a file or directory depending of rules (by index, by name, ...) + // Parameters : + // $p_file_list : An array where will be placed the properties of each + // extracted file + // $p_path : Path to add while writing the extracted files + // $p_remove_path : Path to remove (from the file memorized path) while writing the + // extracted files. If the path does not match the file path, + // the file is extracted with its memorized path. + // $p_remove_path does not apply to 'list' mode. + // $p_path and $p_remove_path are commulative. + // Return Values : + // 1 on success,0 or less on error (see error code list) + // -------------------------------------------------------------------------------- + function privExtractByRule(&$p_file_list, $p_path, $p_remove_path, $p_remove_all_path, &$p_options) + { + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privExtractByRule", "path='$p_path', remove_path='$p_remove_path', remove_all_path='".($p_remove_all_path?'true':'false')."'"); + $v_result=1; + // ----- Check the path + if (($p_path == "") || ((substr($p_path, 0, 1) != "/") && (substr($p_path, 0, 3) != "../") && (substr($p_path,1,2)!=":/"))) + $p_path = "./".$p_path; + // ----- Reduce the path last (and duplicated) '/' + if (($p_path != "./") && ($p_path != "/")) + { + // ----- Look for the path end '/' + while (substr($p_path, -1) == "/") + { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Destination path [$p_path] ends by '/'"); + $p_path = substr($p_path, 0, strlen($p_path)-1); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Modified to [$p_path]"); + } + } + // ----- Look for path to remove format (should end by /) + if (($p_remove_path != "") && (substr($p_remove_path, -1) != '/')) + { + $p_remove_path .= '/'; + } + $p_remove_path_size = strlen($p_remove_path); + // ----- Open the zip file + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Open file in binary read mode"); + if (($v_result = $this->privOpenFd('rb')) != 1) + { + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // ----- Read the central directory informations + $v_central_dir = array(); + if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) + { + // ----- Close the zip file + $this->privCloseFd(); + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // ----- Start at beginning of Central Dir + $v_pos_entry = $v_central_dir['offset']; + // ----- Read each entry + $j_start = 0; + for ($i=0, $v_nb_extracted=0; $i<$v_central_dir['entries']; $i++) + { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Read next file header entry : '$i'"); + // ----- Read next Central dir entry + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Position before rewind : ".ftell($this->zip_fd)."'"); + @rewind($this->zip_fd); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Position after rewind : ".ftell($this->zip_fd)."'"); + if (@fseek($this->zip_fd, $v_pos_entry)) + { + // ----- Close the zip file + $this->privCloseFd(); + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Position after fseek : ".ftell($this->zip_fd)."'"); + // ----- Read the file header + $v_header = array(); + if (($v_result = $this->privReadCentralFileHeader($v_header)) != 1) + { + // ----- Close the zip file + $this->privCloseFd(); + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // ----- Store the index + $v_header['index'] = $i; + // ----- Store the file position + $v_pos_entry = ftell($this->zip_fd); + // ----- Look for the specific extract rules + $v_extract = false; + // ----- Look for extract by name rule + if ( (isset($p_options[PCLZIP_OPT_BY_NAME])) + && ($p_options[PCLZIP_OPT_BY_NAME] != 0)) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Extract with rule 'ByName'"); + // ----- Look if the filename is in the list + for ($j=0; ($j strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) + && (substr($v_header['stored_filename'], 0, strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) == $p_options[PCLZIP_OPT_BY_NAME][$j])) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "The directory is in the file path"); + $v_extract = true; + } + } + // ----- Look for a filename + elseif ($v_header['stored_filename'] == $p_options[PCLZIP_OPT_BY_NAME][$j]) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "The file is the right one."); + $v_extract = true; + } + } + } + // ----- Look for extract by ereg rule + else if ( (isset($p_options[PCLZIP_OPT_BY_EREG])) + && ($p_options[PCLZIP_OPT_BY_EREG] != "")) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Extract by ereg '".$p_options[PCLZIP_OPT_BY_EREG]."'"); + if (ereg($p_options[PCLZIP_OPT_BY_EREG], $v_header['stored_filename'])) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Filename match the regular expression"); + $v_extract = true; + } + } + // ----- Look for extract by preg rule + else if ( (isset($p_options[PCLZIP_OPT_BY_PREG])) + && ($p_options[PCLZIP_OPT_BY_PREG] != "")) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Extract with rule 'ByEreg'"); + if (preg_match($p_options[PCLZIP_OPT_BY_PREG], $v_header['stored_filename'])) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Filename match the regular expression"); + $v_extract = true; + } + } + // ----- Look for extract by index rule + else if ( (isset($p_options[PCLZIP_OPT_BY_INDEX])) + && ($p_options[PCLZIP_OPT_BY_INDEX] != 0)) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Extract with rule 'ByIndex'"); + // ----- Look if the index is in the list + for ($j=$j_start; ($j=$p_options[PCLZIP_OPT_BY_INDEX][$j]['start']) && ($i<=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end'])) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Found as part of an index range"); + $v_extract = true; + } + if ($i>=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end']) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Do not look this index range for next loop"); + $j_start = $j+1; + } + if ($p_options[PCLZIP_OPT_BY_INDEX][$j]['start']>$i) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Index range is greater than index, stop loop"); + break; + } + } + } + // ----- Look for no rule, which means extract all the archive + else { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Extract with no rule (extract all)"); + $v_extract = true; + } + // ----- Look for real extraction + if ($v_extract) + { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Extracting file '".$v_header['filename']."', index '$i'"); + // ----- Go to the file position + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position before rewind : ".ftell($this->zip_fd)."'"); + @rewind($this->zip_fd); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position after rewind : ".ftell($this->zip_fd)."'"); + if (@fseek($this->zip_fd, $v_header['offset'])) + { + // ----- Close the zip file + $this->privCloseFd(); + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position after fseek : ".ftell($this->zip_fd)."'"); + // ----- Look for extraction as string + if ($p_options[PCLZIP_OPT_EXTRACT_AS_STRING]) { + // ----- Extracting the file + if (($v_result = $this->privExtractFileAsString($v_header, $v_string)) != 1) + { + // ----- Close the zip file + $this->privCloseFd(); + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // ----- Get the only interesting attributes + if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted])) != 1) + { + // ----- Close the zip file + $this->privCloseFd(); + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // ----- Set the file content + $p_file_list[$v_nb_extracted]['content'] = $v_string; + // ----- Next extracted file + $v_nb_extracted++; + } + else { + // ----- Extracting the file + if (($v_result = $this->privExtractFile($v_header, $p_path, $p_remove_path, $p_remove_all_path, $p_options)) != 1) + { + // ----- Close the zip file + $this->privCloseFd(); + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // ----- Get the only interesting attributes + if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted++])) != 1) + { + // ----- Close the zip file + $this->privCloseFd(); + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + } + } + } + // ----- Close the zip file + $this->privCloseFd(); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : privExtractFile() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privExtractFile(&$p_entry, $p_path, $p_remove_path, $p_remove_all_path, &$p_options) + { + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, 'PclZip::privExtractFile', "path='$p_path', remove_path='$p_remove_path', remove_all_path='".($p_remove_all_path?'true':'false')."'"); + $v_result=1; + // ----- Read the file header + if (($v_result = $this->privReadFileHeader($v_header)) != 1) + { + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Found file '".$v_header['filename']."', size '".$v_header['size']."'"); + // ----- Check that the file header is coherent with $p_entry info + // TBC + // ----- Look for all path to remove + if ($p_remove_all_path == true) { + // ----- Get the basename of the path + $p_entry['filename'] = basename($p_entry['filename']); + } + // ----- Look for path to remove + else if ($p_remove_path != "") + { + //if (strcmp($p_remove_path, $p_entry['filename'])==0) + if (PclZipUtilPathInclusion($p_remove_path, $p_entry['filename']) == 2) + { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "The folder is the same as the removed path '".$p_entry['filename']."'"); + // ----- Change the file status + $p_entry['status'] = "filtered"; + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + $p_remove_path_size = strlen($p_remove_path); + if (substr($p_entry['filename'], 0, $p_remove_path_size) == $p_remove_path) + { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Found path '$p_remove_path' to remove in file '".$p_entry['filename']."'"); + // ----- Remove the path + $p_entry['filename'] = substr($p_entry['filename'], $p_remove_path_size); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Resulting file is '".$p_entry['filename']."'"); + } + } + // ----- Add the path + if ($p_path != '') + { + $p_entry['filename'] = $p_path."/".$p_entry['filename']; + } + // ----- Look for pre-extract callback + if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "A pre-callback '".$p_options[PCLZIP_CB_PRE_EXTRACT]."()') is defined for the extraction"); + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_entry, $v_local_header); + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. + eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'(PCLZIP_CB_PRE_EXTRACT, $v_local_header);'); + if ($v_result == 0) { + // ----- Change the file status + $p_entry['status'] = "skipped"; + $v_result = 1; + } + // ----- Update the informations + // Only some fields can be modified + $p_entry['filename'] = $v_local_header['filename']; + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "New filename is '".$p_entry['filename']."'"); + } + // ----- Trace + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Extracting file (with path) '".$p_entry['filename']."', size '$v_header[size]'"); + // ----- Look if extraction should be done + if ($p_entry['status'] == 'ok') { + // ----- Look for specific actions while the file exist + if (file_exists($p_entry['filename'])) + { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "File '".$p_entry['filename']."' already exists"); + // ----- Look if file is a directory + if (is_dir($p_entry['filename'])) + { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Existing file '".$p_entry['filename']."' is a directory"); + // ----- Change the file status + $p_entry['status'] = "already_a_directory"; + // ----- Return + ////--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + //return $v_result; + } + // ----- Look if file is write protected + else if (!is_writeable($p_entry['filename'])) + { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Existing file '".$p_entry['filename']."' is write protected"); + // ----- Change the file status + $p_entry['status'] = "write_protected"; + // ----- Return + ////--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + //return $v_result; + } + // ----- Look if the extracted file is older + else if (filemtime($p_entry['filename']) > $p_entry['mtime']) + { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Existing file '".$p_entry['filename']."' is newer (".date("l dS of F Y h:i:s A", filemtime($p_entry['filename'])).") than the extracted file (".date("l dS of F Y h:i:s A", $p_entry['mtime']).")"); + // ----- Change the file status + $p_entry['status'] = "newer_exist"; + // ----- Return + ////--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + //return $v_result; + } + } + // ----- Check the directory availability and create it if necessary + else { + if ((($p_entry['external']&0x00000010)==0x00000010) || (substr($p_entry['filename'], -1) == '/')) + $v_dir_to_check = $p_entry['filename']; + else if (!strstr($p_entry['filename'], "/")) + $v_dir_to_check = ""; + else + $v_dir_to_check = dirname($p_entry['filename']); + if (($v_result = $this->privDirCheck($v_dir_to_check, (($p_entry['external']&0x00000010)==0x00000010))) != 1) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Unable to create path for '".$p_entry['filename']."'"); + // ----- Change the file status + $p_entry['status'] = "path_creation_fail"; + // ----- Return + ////--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + //return $v_result; + $v_result = 1; + } + } + } + // ----- Look if extraction should be done + if ($p_entry['status'] == 'ok') { + // ----- Do the extraction (if not a folder) + if (!(($p_entry['external']&0x00000010)==0x00000010)) + { + // ----- Look for not compressed file + if ($p_entry['compressed_size'] == $p_entry['size']) + { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Extracting an un-compressed file"); + // ----- Opening destination file + if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) + { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Error while opening '".$p_entry['filename']."' in write binary mode"); + // ----- Change the file status + $p_entry['status'] = "write_error"; + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Reading '".$p_entry['size']."' bytes"); + // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks + $v_size = $p_entry['compressed_size']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Read $v_read_size bytes"); + $v_buffer = fread($this->zip_fd, $v_read_size); + $v_binary_data = pack('a'.$v_read_size, $v_buffer); + @fwrite($v_dest_file, $v_binary_data, $v_read_size); + $v_size -= $v_read_size; + } + // ----- Closing the destination file + fclose($v_dest_file); + // ----- Change the file mtime + touch($p_entry['filename'], $p_entry['mtime']); + } + else + { + // ----- Trace + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Extracting a compressed file"); + // ----- Opening destination file + if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Error while opening '".$p_entry['filename']."' in write binary mode"); + // ----- Change the file status + $p_entry['status'] = "write_error"; + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Reading '".$p_entry['size']."' bytes"); + // ----- Read the compressed file in a buffer (one shot) + $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']); + // ----- Decompress the file + $v_file_content = gzinflate($v_buffer); + unset($v_buffer); + // ----- Write the uncompressed data + @fwrite($v_dest_file, $v_file_content, $p_entry['size']); + unset($v_file_content); + // ----- Closing the destination file + @fclose($v_dest_file); + // ----- Change the file mtime + touch($p_entry['filename'], $p_entry['mtime']); + } + // ----- Look for chmod option + if (isset($p_options[PCLZIP_OPT_SET_CHMOD])) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "chmod option activated '".$p_options[PCLZIP_OPT_SET_CHMOD]."'"); + // ----- Change the mode of the file + chmod($p_entry['filename'], $p_options[PCLZIP_OPT_SET_CHMOD]); + } + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Extraction done"); + } + } + // ----- Look for post-extract callback + if (isset($p_options[PCLZIP_CB_POST_EXTRACT])) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "A post-callback '".$p_options[PCLZIP_CB_POST_EXTRACT]."()') is defined for the extraction"); + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_entry, $v_local_header); + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. + eval('$v_result = '.$p_options[PCLZIP_CB_POST_EXTRACT].'(PCLZIP_CB_POST_EXTRACT, $v_local_header);'); + } + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : privExtractFileAsString() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privExtractFileAsString(&$p_entry, &$p_string) + { + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, 'PclZip::privExtractFileAsString', "p_entry['filename']='".$p_entry['filename']."'"); + $v_result=1; + // ----- Read the file header + $v_header = array(); + if (($v_result = $this->privReadFileHeader($v_header)) != 1) + { + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Found file '".$v_header['filename']."', size '".$v_header['size']."'"); + // ----- Check that the file header is coherent with $p_entry info + // TBC + // ----- Trace + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Extracting file in string (with path) '".$p_entry['filename']."', size '$v_header[size]'"); + // ----- Do the extraction (if not a folder) + if (!(($p_entry['external']&0x00000010)==0x00000010)) + { + // ----- Look for not compressed file + if ($p_entry['compressed_size'] == $p_entry['size']) + { + // ----- Trace + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Extracting an un-compressed file"); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Reading '".$p_entry['size']."' bytes"); + // ----- Reading the file + $p_string = fread($this->zip_fd, $p_entry['compressed_size']); + } + else + { + // ----- Trace + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Extracting a compressed file"); + // ----- Reading the file + $v_data = fread($this->zip_fd, $p_entry['compressed_size']); + // ----- Decompress the file + $p_string = gzinflate($v_data); + } + // ----- Trace + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Extraction done"); + } + else { + // TBC : error : can not extract a folder in a string + } + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : privReadFileHeader() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privReadFileHeader(&$p_header) + { + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privReadFileHeader", ""); + $v_result=1; + // ----- Read the 4 bytes signature + $v_binary_data = @fread($this->zip_fd, 4); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Binary data is : '".sprintf("%08x", $v_binary_data)."'"); + $v_data = unpack('Vid', $v_binary_data); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Binary signature is : '".sprintf("0x%08x", $v_data['id'])."'"); + // ----- Check signature + if ($v_data['id'] != 0x04034b50) + { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Invalid File header"); + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Invalid archive structure'); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + // ----- Read the first 42 bytes of the header + $v_binary_data = fread($this->zip_fd, 26); + // ----- Look for invalid block size + if (strlen($v_binary_data) != 26) + { + $p_header['filename'] = ""; + $p_header['status'] = "invalid_header"; + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Invalid block size : ".strlen($v_binary_data)); + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid block size : ".strlen($v_binary_data)); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + // ----- Extract the values + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Header : '".$v_binary_data."'"); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Header (Hex) : '".bin2hex($v_binary_data)."'"); + $v_data = unpack('vversion/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len', $v_binary_data); + // ----- Get filename + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "File name length : ".$v_data['filename_len']); + $p_header['filename'] = fread($this->zip_fd, $v_data['filename_len']); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Filename : \''.$p_header['filename'].'\''); + // ----- Get extra_fields + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Extra field length : ".$v_data['extra_len']); + if ($v_data['extra_len'] != 0) { + $p_header['extra'] = fread($this->zip_fd, $v_data['extra_len']); + } + else { + $p_header['extra'] = ''; + } + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Extra field : \''.bin2hex($p_header['extra']).'\''); + // ----- Extract properties + $p_header['compression'] = $v_data['compression']; + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Compression method : \''.bin2hex($p_header['compression']).'\''); + $p_header['size'] = $v_data['size']; + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Size : \''.$p_header['size'].'\''); + $p_header['compressed_size'] = $v_data['compressed_size']; + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Compressed Size : \''.$p_header['compressed_size'].'\''); + $p_header['crc'] = $v_data['crc']; + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'CRC : \''.$p_header['crc'].'\''); + $p_header['flag'] = $v_data['flag']; + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Flag : \''.$p_header['flag'].'\''); + // ----- Recuperate date in UNIX format + $p_header['mdate'] = $v_data['mdate']; + $p_header['mtime'] = $v_data['mtime']; + if ($p_header['mdate'] && $p_header['mtime']) + { + // ----- Extract time + $v_hour = ($p_header['mtime'] & 0xF800) >> 11; + $v_minute = ($p_header['mtime'] & 0x07E0) >> 5; + $v_seconde = ($p_header['mtime'] & 0x001F)*2; + // ----- Extract date + $v_year = (($p_header['mdate'] & 0xFE00) >> 9) + 1980; + $v_month = ($p_header['mdate'] & 0x01E0) >> 5; + $v_day = $p_header['mdate'] & 0x001F; + // ----- Get UNIX date format + $p_header['mtime'] = mktime($v_hour, $v_minute, $v_seconde, $v_month, $v_day, $v_year); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Date : \''.date("d/m/y H:i:s", $p_header['mtime']).'\''); + } + else + { + $p_header['mtime'] = time(); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Date is actual : \''.date("d/m/y H:i:s", $p_header['mtime']).'\''); + } + // ----- Other informations + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Compression type : ".$v_data['compression']); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Version : ".$v_data['version']); + // TBC + //for(reset($v_data); $key = key($v_data); next($v_data)) { + // //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Attribut[$key] = ".$v_data[$key]); + //} + // ----- Set the stored filename + $p_header['stored_filename'] = $p_header['filename']; + // ----- Set the status field + $p_header['status'] = "ok"; + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : privReadCentralFileHeader() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privReadCentralFileHeader(&$p_header) + { + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privReadCentralFileHeader", ""); + $v_result=1; + // ----- Read the 4 bytes signature + $v_binary_data = @fread($this->zip_fd, 4); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Binary data is : '".sprintf("%08x", $v_binary_data)."'"); + $v_data = unpack('Vid', $v_binary_data); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Binary signature is : '".sprintf("0x%08x", $v_data['id'])."'"); + // ----- Check signature + if ($v_data['id'] != 0x02014b50) + { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Invalid Central Dir File signature"); + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Invalid archive structure'); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + // ----- Read the first 42 bytes of the header + $v_binary_data = fread($this->zip_fd, 42); + // ----- Look for invalid block size + if (strlen($v_binary_data) != 42) + { + $p_header['filename'] = ""; + $p_header['status'] = "invalid_header"; + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Invalid block size : ".strlen($v_binary_data)); + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid block size : ".strlen($v_binary_data)); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + // ----- Extract the values + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Header : '".$v_binary_data."'"); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Header (Hex) : '".bin2hex($v_binary_data)."'"); + $p_header = unpack('vversion/vversion_extracted/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len/vcomment_len/vdisk/vinternal/Vexternal/Voffset', $v_binary_data); + // ----- Get filename + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "File name length : ".$p_header['filename_len']); + if ($p_header['filename_len'] != 0) + $p_header['filename'] = fread($this->zip_fd, $p_header['filename_len']); + else + $p_header['filename'] = ''; + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Filename : \''.$p_header['filename'].'\''); + // ----- Get extra + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Extra length : ".$p_header['extra_len']); + if ($p_header['extra_len'] != 0) + $p_header['extra'] = fread($this->zip_fd, $p_header['extra_len']); + else + $p_header['extra'] = ''; + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Extra : \''.$p_header['extra'].'\''); + // ----- Get comment + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Comment length : ".$p_header['comment_len']); + if ($p_header['comment_len'] != 0) + $p_header['comment'] = fread($this->zip_fd, $p_header['comment_len']); + else + $p_header['comment'] = ''; + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Comment : \''.$p_header['comment'].'\''); + // ----- Extract properties + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Version : \''.($p_header['version']/10).'.'.($p_header['version']%10).'\''); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Version need to extract : \''.($p_header['version_extracted']/10).'.'.($p_header['version_extracted']%10).'\''); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Size : \''.$p_header['size'].'\''); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Compressed Size : \''.$p_header['compressed_size'].'\''); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'CRC : \''.$p_header['crc'].'\''); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Flag : \''.$p_header['flag'].'\''); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Offset : \''.$p_header['offset'].'\''); + // ----- Recuperate date in UNIX format + if ($p_header['mdate'] && $p_header['mtime']) + { + // ----- Extract time + $v_hour = ($p_header['mtime'] & 0xF800) >> 11; + $v_minute = ($p_header['mtime'] & 0x07E0) >> 5; + $v_seconde = ($p_header['mtime'] & 0x001F)*2; + // ----- Extract date + $v_year = (($p_header['mdate'] & 0xFE00) >> 9) + 1980; + $v_month = ($p_header['mdate'] & 0x01E0) >> 5; + $v_day = $p_header['mdate'] & 0x001F; + // ----- Get UNIX date format + $p_header['mtime'] = mktime($v_hour, $v_minute, $v_seconde, $v_month, $v_day, $v_year); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Date : \''.date("d/m/y H:i:s", $p_header['mtime']).'\''); + } + else + { + $p_header['mtime'] = time(); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Date is actual : \''.date("d/m/y H:i:s", $p_header['mtime']).'\''); + } + // ----- Set the stored filename + $p_header['stored_filename'] = $p_header['filename']; + // ----- Set default status to ok + $p_header['status'] = 'ok'; + // ----- Look if it is a directory + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Internal (Hex) : '".sprintf("Ox%04X", $p_header['internal'])."'"); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "External (Hex) : '".sprintf("Ox%04X", $p_header['external'])."' (".(($p_header['external']&0x00000010)==0x00000010?'is a folder':'is a file').')'); + if (substr($p_header['filename'], -1) == '/') + { + $p_header['external'] = 0x41FF0010; + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Force folder external : \''.$p_header['external'].'\''); + } + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Header of filename : \''.$p_header['filename'].'\''); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : privReadEndCentralDir() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privReadEndCentralDir(&$p_central_dir) + { + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privReadEndCentralDir", ""); + $v_result=1; + // ----- Go to the end of the zip file + $v_size = filesize($this->zipname); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Size of the file :$v_size"); + @fseek($this->zip_fd, $v_size); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Position at end of zip file : \''.ftell($this->zip_fd).'\''); + if (@ftell($this->zip_fd) != $v_size) + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to go to the end of the archive \''.$this->zipname.'\''); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + // ----- First try : look if this is an archive with no commentaries (most of the time) + // in this case the end of central dir is at 22 bytes of the file end + $v_found = 0; + if ($v_size > 26) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Look for central dir with no comment'); + @fseek($this->zip_fd, $v_size-22); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Position after min central position : \''.ftell($this->zip_fd).'\''); + if (($v_pos = @ftell($this->zip_fd)) != ($v_size-22)) + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to seek back to the middle of the archive \''.$this->zipname.'\''); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + // ----- Read for bytes + $v_binary_data = @fread($this->zip_fd, 4); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Binary data is : '".sprintf("%08x", $v_binary_data)."'"); + $v_data = unpack('Vid', $v_binary_data); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Binary signature is : '".sprintf("0x%08x", $v_data['id'])."'"); + // ----- Check signature + if ($v_data['id'] == 0x06054b50) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Found central dir at the default position."); + $v_found = 1; + } + $v_pos = ftell($this->zip_fd); + } + // ----- Go back to the maximum possible size of the Central Dir End Record + if (!$v_found) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Start extended search of end central dir'); + $v_maximum_size = 65557; // 0xFFFF + 22; + if ($v_maximum_size > $v_size) + $v_maximum_size = $v_size; + @fseek($this->zip_fd, $v_size-$v_maximum_size); + if (@ftell($this->zip_fd) != ($v_size-$v_maximum_size)) + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to seek back to the middle of the archive \''.$this->zipname.'\''); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Position after max central position : \''.ftell($this->zip_fd).'\''); + // ----- Read byte per byte in order to find the signature + $v_pos = ftell($this->zip_fd); + $v_bytes = 0x00000000; + while ($v_pos < $v_size) + { + // ----- Read a byte + $v_byte = @fread($this->zip_fd, 1); + // ----- Add the byte + $v_bytes = ($v_bytes << 8) | Ord($v_byte); + // ----- Compare the bytes + if ($v_bytes == 0x504b0506) + { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Found End Central Dir signature at position : \''.ftell($this->zip_fd).'\''); + $v_pos++; + break; + } + $v_pos++; + } + // ----- Look if not found end of central dir + if ($v_pos == $v_size) + { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Unable to find End of Central Dir Record signature"); + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Unable to find End of Central Dir Record signature"); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + } + // ----- Read the first 18 bytes of the header + $v_binary_data = fread($this->zip_fd, 18); + // ----- Look for invalid block size + if (strlen($v_binary_data) != 18) + { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Invalid End of Central Dir Record size : ".strlen($v_binary_data)); + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid End of Central Dir Record size : ".strlen($v_binary_data)); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + // ----- Extract the values + ////--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Central Dir Record : '".$v_binary_data."'"); + ////--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Central Dir Record (Hex) : '".bin2hex($v_binary_data)."'"); + $v_data = unpack('vdisk/vdisk_start/vdisk_entries/ventries/Vsize/Voffset/vcomment_size', $v_binary_data); + // ----- Check the global size + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Comment length : ".$v_data['comment_size']); + if (($v_pos + $v_data['comment_size'] + 18) != $v_size) + { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Fail to find the right signature"); + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Fail to find the right signature"); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + // ----- Get comment + if ($v_data['comment_size'] != 0) + $p_central_dir['comment'] = fread($this->zip_fd, $v_data['comment_size']); + else + $p_central_dir['comment'] = ''; + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Comment : \''.$p_central_dir['comment'].'\''); + $p_central_dir['entries'] = $v_data['entries']; + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Nb of entries : \''.$p_central_dir['entries'].'\''); + $p_central_dir['disk_entries'] = $v_data['disk_entries']; + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Nb of entries for this disk : \''.$p_central_dir['disk_entries'].'\''); + $p_central_dir['offset'] = $v_data['offset']; + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Offset of Central Dir : \''.$p_central_dir['offset'].'\''); + $p_central_dir['size'] = $v_data['size']; + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Size of Central Dir : \''.$p_central_dir['size'].'\''); + $p_central_dir['disk'] = $v_data['disk']; + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Disk number : \''.$p_central_dir['disk'].'\''); + $p_central_dir['disk_start'] = $v_data['disk_start']; + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Start disk number : \''.$p_central_dir['disk_start'].'\''); + // TBC + //for(reset($p_central_dir); $key = key($p_central_dir); next($p_central_dir)) { + // //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "central_dir[$key] = ".$p_central_dir[$key]); + //} + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : privDeleteByRule() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privDeleteByRule(&$p_result_list, &$p_options) + { + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privDeleteByRule", ""); + $v_result=1; + $v_list_detail = array(); + // ----- Open the zip file + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Open file in binary read mode"); + if (($v_result=$this->privOpenFd('rb')) != 1) + { + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // ----- Read the central directory informations + $v_central_dir = array(); + if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) + { + $this->privCloseFd(); + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // ----- Go to beginning of File + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position in file : ".ftell($this->zip_fd)."'"); + @rewind($this->zip_fd); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position in file : ".ftell($this->zip_fd)."'"); + // ----- Scan all the files + // ----- Start at beginning of Central Dir + $v_pos_entry = $v_central_dir['offset']; + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position before rewind : ".ftell($this->zip_fd)."'"); + @rewind($this->zip_fd); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position after rewind : ".ftell($this->zip_fd)."'"); + if (@fseek($this->zip_fd, $v_pos_entry)) + { + // ----- Close the zip file + $this->privCloseFd(); + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position after fseek : ".ftell($this->zip_fd)."'"); + // ----- Read each entry + $v_header_list = array(); + $j_start = 0; + for ($i=0, $v_nb_extracted=0; $i<$v_central_dir['entries']; $i++) + { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Read next file header entry (index '$i')"); + // ----- Read the file header + $v_header_list[$v_nb_extracted] = array(); + if (($v_result = $this->privReadCentralFileHeader($v_header_list[$v_nb_extracted])) != 1) + { + // ----- Close the zip file + $this->privCloseFd(); + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Filename (index '$i') : '".$v_header_list[$v_nb_extracted]['stored_filename']."'"); + // ----- Store the index + $v_header_list[$v_nb_extracted]['index'] = $i; + // ----- Look for the specific extract rules + $v_found = false; + // ----- Look for extract by name rule + if ( (isset($p_options[PCLZIP_OPT_BY_NAME])) + && ($p_options[PCLZIP_OPT_BY_NAME] != 0)) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Extract with rule 'ByName'"); + // ----- Look if the filename is in the list + for ($j=0; ($j strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) + && (substr($v_header_list[$v_nb_extracted]['stored_filename'], 0, strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) == $p_options[PCLZIP_OPT_BY_NAME][$j])) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "The directory is in the file path"); + $v_found = true; + } + elseif ( (($v_header_list[$v_nb_extracted]['external']&0x00000010)==0x00000010) /* Indicates a folder */ + && ($v_header_list[$v_nb_extracted]['stored_filename'].'/' == $p_options[PCLZIP_OPT_BY_NAME][$j])) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "The entry is the searched directory"); + $v_found = true; + } + } + // ----- Look for a filename + elseif ($v_header_list[$v_nb_extracted]['stored_filename'] == $p_options[PCLZIP_OPT_BY_NAME][$j]) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "The file is the right one."); + $v_found = true; + } + } + } + // ----- Look for extract by ereg rule + else if ( (isset($p_options[PCLZIP_OPT_BY_EREG])) + && ($p_options[PCLZIP_OPT_BY_EREG] != "")) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Extract by ereg '".$p_options[PCLZIP_OPT_BY_EREG]."'"); + if (ereg($p_options[PCLZIP_OPT_BY_EREG], $v_header_list[$v_nb_extracted]['stored_filename'])) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Filename match the regular expression"); + $v_found = true; + } + } + // ----- Look for extract by preg rule + else if ( (isset($p_options[PCLZIP_OPT_BY_PREG])) + && ($p_options[PCLZIP_OPT_BY_PREG] != "")) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Extract with rule 'ByEreg'"); + if (preg_match($p_options[PCLZIP_OPT_BY_PREG], $v_header_list[$v_nb_extracted]['stored_filename'])) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Filename match the regular expression"); + $v_found = true; + } + } + // ----- Look for extract by index rule + else if ( (isset($p_options[PCLZIP_OPT_BY_INDEX])) + && ($p_options[PCLZIP_OPT_BY_INDEX] != 0)) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Extract with rule 'ByIndex'"); + // ----- Look if the index is in the list + for ($j=$j_start; ($j=$p_options[PCLZIP_OPT_BY_INDEX][$j]['start']) && ($i<=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end'])) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Found as part of an index range"); + $v_found = true; + } + if ($i>=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end']) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Do not look this index range for next loop"); + $j_start = $j+1; + } + if ($p_options[PCLZIP_OPT_BY_INDEX][$j]['start']>$i) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Index range is greater than index, stop loop"); + break; + } + } + } + // ----- Look for deletion + if ($v_found) + { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "File '".$v_header_list[$v_nb_extracted]['stored_filename']."', index '$i' need to be deleted"); + unset($v_header_list[$v_nb_extracted]); + } + else + { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "File '".$v_header_list[$v_nb_extracted]['stored_filename']."', index '$i' will not be deleted"); + $v_nb_extracted++; + } + } + // ----- Look if something need to be deleted + if ($v_nb_extracted > 0) { + // ----- Creates a temporay file + $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp'; + // ----- Creates a temporary zip archive + $v_temp_zip = new PclZip($v_zip_temp_name); + // ----- Open the temporary zip file in write mode + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Open file in binary write mode"); + if (($v_result = $v_temp_zip->privOpenFd('wb')) != 1) { + $this->privCloseFd(); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // ----- Look which file need to be kept + for ($i=0; $izip_fd)."'"); + @rewind($this->zip_fd); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position after rewind : ".ftell($this->zip_fd)."'"); + if (@fseek($this->zip_fd, $v_header_list[$i]['offset'])) { + // ----- Close the zip file + $this->privCloseFd(); + $v_temp_zip->privCloseFd(); + @unlink($v_zip_temp_name); + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position after fseek : ".ftell($this->zip_fd)."'"); + // ----- Read the file header + if (($v_result = $this->privReadFileHeader($v_header_list[$i])) != 1) { + // ----- Close the zip file + $this->privCloseFd(); + $v_temp_zip->privCloseFd(); + @unlink($v_zip_temp_name); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // ----- Write the file header + if (($v_result = $v_temp_zip->privWriteFileHeader($v_header_list[$i])) != 1) { + // ----- Close the zip file + $this->privCloseFd(); + $v_temp_zip->privCloseFd(); + @unlink($v_zip_temp_name); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Offset for this file is '".$v_header_list[$i]['offset']."'"); + // ----- Read/write the data block + if (($v_result = PclZipUtilCopyBlock($this->zip_fd, $v_temp_zip->zip_fd, $v_header_list[$i]['compressed_size'])) != 1) { + // ----- Close the zip file + $this->privCloseFd(); + $v_temp_zip->privCloseFd(); + @unlink($v_zip_temp_name); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + } + // ----- Store the offset of the central dir + $v_offset = @ftell($v_temp_zip->zip_fd); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "New offset of central dir : $v_offset"); + // ----- Re-Create the Central Dir files header + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Creates the new central directory"); + for ($i=0; $iprivWriteCentralFileHeader($v_header_list[$i])) != 1) { + $v_temp_zip->privCloseFd(); + $this->privCloseFd(); + @unlink($v_zip_temp_name); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // ----- Transform the header to a 'usable' info + $v_temp_zip->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]); + } + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Creates the central directory footer"); + // ----- Zip file comment + $v_comment = ''; + // ----- Calculate the size of the central header + $v_size = @ftell($v_temp_zip->zip_fd)-$v_offset; + // ----- Create the central dir footer + if (($v_result = $v_temp_zip->privWriteCentralHeader(sizeof($v_header_list), $v_size, $v_offset, $v_comment)) != 1) { + // ----- Reset the file list + unset($v_header_list); + $v_temp_zip->privCloseFd(); + $this->privCloseFd(); + @unlink($v_zip_temp_name); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // ----- Close + $v_temp_zip->privCloseFd(); + $this->privCloseFd(); + // ----- Delete the zip file + // TBC : I should test the result ... + @unlink($this->zipname); + // ----- Rename the temporary file + // TBC : I should test the result ... + //@rename($v_zip_temp_name, $this->zipname); + PclZipUtilRename($v_zip_temp_name, $this->zipname); + // ----- Destroy the temporary archive + unset($v_temp_zip); + } + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : privDirCheck() + // Description : + // Check if a directory exists, if not it creates it and all the parents directory + // which may be useful. + // Parameters : + // $p_dir : Directory path to check. + // Return Values : + // 1 : OK + // -1 : Unable to create directory + // -------------------------------------------------------------------------------- + function privDirCheck($p_dir, $p_is_dir=false) + { + $v_result = 1; + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privDirCheck", "entry='$p_dir', is_dir='".($p_is_dir?"true":"false")."'"); + // ----- Remove the final '/' + if (($p_is_dir) && (substr($p_dir, -1)=='/')) + { + $p_dir = substr($p_dir, 0, strlen($p_dir)-1); + } + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Looking for entry '$p_dir'"); + // ----- Check the directory availability + if ((is_dir($p_dir)) || ($p_dir == "")) + { + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, "'$p_dir' is a directory"); + return 1; + } + // ----- Extract parent directory + $p_parent_dir = dirname($p_dir); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Parent directory is '$p_parent_dir'"); + // ----- Just a check + if ($p_parent_dir != $p_dir) + { + // ----- Look for parent directory + if ($p_parent_dir != "") + { + if (($v_result = $this->privDirCheck($p_parent_dir)) != 1) + { + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + } + } + // ----- Create the directory + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Create directory '$p_dir'"); + if (!@mkdir($p_dir, 0777)) + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_DIR_CREATE_FAIL, "Unable to create directory '$p_dir'"); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result, "Directory '$p_dir' created"); + return $v_result; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : privMerge() + // Description : + // If $p_archive_to_add does not exist, the function exit with a success result. + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privMerge(&$p_archive_to_add) + { + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privMerge", "archive='".$p_archive_to_add->zipname."'"); + $v_result=1; + // ----- Look if the archive_to_add exists + if (!is_file($p_archive_to_add->zipname)) + { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Archive to add does not exist. End of merge."); + // ----- Nothing to merge, so merge is a success + $v_result = 1; + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // ----- Look if the archive exists + if (!is_file($this->zipname)) + { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Archive does not exist, duplicate the archive_to_add."); + // ----- Do a duplicate + $v_result = $this->privDuplicate($p_archive_to_add->zipname); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // ----- Open the zip file + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Open file in binary read mode"); + if (($v_result=$this->privOpenFd('rb')) != 1) + { + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // ----- Read the central directory informations + $v_central_dir = array(); + if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) + { + $this->privCloseFd(); + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // ----- Go to beginning of File + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position in zip : ".ftell($this->zip_fd)."'"); + @rewind($this->zip_fd); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position in zip : ".ftell($this->zip_fd)."'"); + // ----- Open the archive_to_add file + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Open archive_to_add in binary read mode"); + if (($v_result=$p_archive_to_add->privOpenFd('rb')) != 1) + { + $this->privCloseFd(); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // ----- Read the central directory informations + $v_central_dir_to_add = array(); + if (($v_result = $p_archive_to_add->privReadEndCentralDir($v_central_dir_to_add)) != 1) + { + $this->privCloseFd(); + $p_archive_to_add->privCloseFd(); + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // ----- Go to beginning of File + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position in archive_to_add : ".ftell($p_archive_to_add->zip_fd)."'"); + @rewind($p_archive_to_add->zip_fd); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position in archive_to_add : ".ftell($p_archive_to_add->zip_fd)."'"); + // ----- Creates a temporay file + $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp'; + // ----- Open the temporary file in write mode + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Open file in binary read mode"); + if (($v_zip_temp_fd = @fopen($v_zip_temp_name, 'wb')) == 0) + { + $this->privCloseFd(); + $p_archive_to_add->privCloseFd(); + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_zip_temp_name.'\' in binary write mode'); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + // ----- Copy the files from the archive to the temporary file + // TBC : Here I should better append the file and go back to erase the central dir + $v_size = $v_central_dir['offset']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Read $v_read_size bytes"); + $v_buffer = fread($this->zip_fd, $v_read_size); + @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + // ----- Copy the files from the archive_to_add into the temporary file + $v_size = $v_central_dir_to_add['offset']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Read $v_read_size bytes"); + $v_buffer = fread($p_archive_to_add->zip_fd, $v_read_size); + @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + // ----- Store the offset of the central dir + $v_offset = @ftell($v_zip_temp_fd); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "New offset of central dir : $v_offset"); + // ----- Copy the block of file headers from the old archive + $v_size = $v_central_dir['size']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Read $v_read_size bytes"); + $v_buffer = @fread($this->zip_fd, $v_read_size); + @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + // ----- Copy the block of file headers from the archive_to_add + $v_size = $v_central_dir_to_add['size']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Read $v_read_size bytes"); + $v_buffer = @fread($p_archive_to_add->zip_fd, $v_read_size); + @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + // ----- Zip file comment + // TBC : I should merge the two comments + $v_comment = ''; + // ----- Calculate the size of the (new) central header + $v_size = @ftell($v_zip_temp_fd)-$v_offset; + // ----- Swap the file descriptor + // Here is a trick : I swap the temporary fd with the zip fd, in order to use + // the following methods on the temporary fil and not the real archive fd + $v_swap = $this->zip_fd; + $this->zip_fd = $v_zip_temp_fd; + $v_zip_temp_fd = $v_swap; + // ----- Create the central dir footer + if (($v_result = $this->privWriteCentralHeader($v_central_dir['entries']+$v_central_dir_to_add['entries'], $v_size, $v_offset, $v_comment)) != 1) + { + $this->privCloseFd(); + $p_archive_to_add->privCloseFd(); + @fclose($v_zip_temp_fd); + $this->zip_fd = null; + // ----- Reset the file list + unset($v_header_list); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // ----- Swap back the file descriptor + $v_swap = $this->zip_fd; + $this->zip_fd = $v_zip_temp_fd; + $v_zip_temp_fd = $v_swap; + // ----- Close + $this->privCloseFd(); + $p_archive_to_add->privCloseFd(); + // ----- Close the temporary file + @fclose($v_zip_temp_fd); + // ----- Delete the zip file + // TBC : I should test the result ... + @unlink($this->zipname); + // ----- Rename the temporary file + // TBC : I should test the result ... + //@rename($v_zip_temp_name, $this->zipname); + PclZipUtilRename($v_zip_temp_name, $this->zipname); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : privDuplicate() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privDuplicate($p_archive_filename) + { + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privDuplicate", "archive_filename='$p_archive_filename'"); + $v_result=1; + // ----- Look if the $p_archive_filename exists + if (!is_file($p_archive_filename)) + { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Archive to duplicate does not exist. End of duplicate."); + // ----- Nothing to duplicate, so duplicate is a success. + $v_result = 1; + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // ----- Open the zip file + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Open file in binary read mode"); + if (($v_result=$this->privOpenFd('wb')) != 1) + { + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // ----- Open the temporary file in write mode + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Open file in binary read mode"); + if (($v_zip_temp_fd = @fopen($p_archive_filename, 'rb')) == 0) + { + $this->privCloseFd(); + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive file \''.$p_archive_filename.'\' in binary write mode'); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); + return PclZip::errorCode(); + } + // ----- Copy the files from the archive to the temporary file + // TBC : Here I should better append the file and go back to erase the central dir + $v_size = filesize($p_archive_filename); + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Read $v_read_size bytes"); + $v_buffer = fread($v_zip_temp_fd, $v_read_size); + @fwrite($this->zip_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + // ----- Close + $this->privCloseFd(); + // ----- Close the temporary file + @fclose($v_zip_temp_fd); + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : privErrorLog() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + function privErrorLog($p_error_code=0, $p_error_string='') + { + if (PCLZIP_ERROR_EXTERNAL == 1) { + PclError($p_error_code, $p_error_string); + } + else { + $this->error_code = $p_error_code; + $this->error_string = $p_error_string; + } + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : privErrorReset() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + function privErrorReset() + { + if (PCLZIP_ERROR_EXTERNAL == 1) { + PclErrorReset(); + } + else { + $this->error_code = 1; + $this->error_string = ''; + } + } + // -------------------------------------------------------------------------------- + } + // End of class + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : PclZipUtilPathReduction() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function PclZipUtilPathReduction($p_dir) + { + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZipUtilPathReduction", "dir='$p_dir'"); + $v_result = ""; + // ----- Look for not empty path + if ($p_dir != "") + { + // ----- Explode path by directory names + $v_list = explode("/", $p_dir); + // ----- Study directories from last to first + for ($i=sizeof($v_list)-1; $i>=0; $i--) + { + // ----- Look for current path + if ($v_list[$i] == ".") + { + // ----- Ignore this directory + // Should be the first $i=0, but no check is done + } + else if ($v_list[$i] == "..") + { + // ----- Ignore it and ignore the $i-1 + $i--; + } + else if (($v_list[$i] == "") && ($i!=(sizeof($v_list)-1)) && ($i!=0)) + { + // ----- Ignore only the double '//' in path, + // but not the first and last '/' + } + else + { + $v_result = $v_list[$i].($i!=(sizeof($v_list)-1)?"/".$v_result:""); + } + } + } + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : PclZipUtilPathInclusion() + // Description : + // This function indicates if the path $p_path is under the $p_dir tree. Or, + // said in an other way, if the file or sub-dir $p_path is inside the dir + // $p_dir. + // The function indicates also if the path is exactly the same as the dir. + // This function supports path with duplicated '/' like '//', but does not + // support '.' or '..' statements. + // Parameters : + // Return Values : + // 0 if $p_path is not inside directory $p_dir + // 1 if $p_path is inside directory $p_dir + // 2 if $p_path is exactly the same as $p_dir + // -------------------------------------------------------------------------------- + function PclZipUtilPathInclusion($p_dir, $p_path) + { + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZipUtilPathInclusion", "dir='$p_dir', path='$p_path'"); + $v_result = 1; + // ----- Explode dir and path by directory separator + $v_list_dir = explode("/", $p_dir); + $v_list_dir_size = sizeof($v_list_dir); + $v_list_path = explode("/", $p_path); + $v_list_path_size = sizeof($v_list_path); + // ----- Study directories paths + $i = 0; + $j = 0; + while (($i < $v_list_dir_size) && ($j < $v_list_path_size) && ($v_result)) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Working on dir($i)='".$v_list_dir[$i]."' and path($j)='".$v_list_path[$j]."'"); + // ----- Look for empty dir (path reduction) + if ($v_list_dir[$i] == '') { + $i++; + continue; + } + if ($v_list_path[$j] == '') { + $j++; + continue; + } + // ----- Compare the items + if (($v_list_dir[$i] != $v_list_path[$j]) && ($v_list_dir[$i] != '') && ( $v_list_path[$j] != '')) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Items ($i,$j) are different"); + $v_result = 0; + } + // ----- Next items + $i++; + $j++; + } + // ----- Look if everything seems to be the same + if ($v_result) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Look for tie break"); + // ----- Skip all the empty items + while (($j < $v_list_path_size) && ($v_list_path[$j] == '')) $j++; + while (($i < $v_list_dir_size) && ($v_list_dir[$i] == '')) $i++; + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Looking on dir($i)='".($i < $v_list_dir_size?$v_list_dir[$i]:'')."' and path($j)='".($j < $v_list_path_size?$v_list_path[$j]:'')."'"); + if (($i >= $v_list_dir_size) && ($j >= $v_list_path_size)) { + // ----- There are exactly the same + $v_result = 2; + } + else if ($i < $v_list_dir_size) { + // ----- The path is shorter than the dir + $v_result = 0; + } + } + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : PclZipUtilCopyBlock() + // Description : + // Parameters : + // $p_mode : read/write compression mode + // 0 : src & dest normal + // 1 : src gzip, dest normal + // 2 : src normal, dest gzip + // 3 : src & dest gzip + // Return Values : + // -------------------------------------------------------------------------------- + function PclZipUtilCopyBlock($p_src, $p_dest, $p_size, $p_mode=0) + { + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZipUtilCopyBlock", "size=$p_size, mode=$p_mode"); + $v_result = 1; + if ($p_mode==0) + { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Src offset before read :".(@ftell($p_src))); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Dest offset before write :".(@ftell($p_dest))); + while ($p_size != 0) + { + $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Read $v_read_size bytes"); + $v_buffer = @fread($p_src, $v_read_size); + @fwrite($p_dest, $v_buffer, $v_read_size); + $p_size -= $v_read_size; + } + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Src offset after read :".(@ftell($p_src))); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Dest offset after write :".(@ftell($p_dest))); + } + else if ($p_mode==1) + { + while ($p_size != 0) + { + $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Read $v_read_size bytes"); + $v_buffer = @gzread($p_src, $v_read_size); + @fwrite($p_dest, $v_buffer, $v_read_size); + $p_size -= $v_read_size; + } + } + else if ($p_mode==2) + { + while ($p_size != 0) + { + $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Read $v_read_size bytes"); + $v_buffer = @fread($p_src, $v_read_size); + @gzwrite($p_dest, $v_buffer, $v_read_size); + $p_size -= $v_read_size; + } + } + else if ($p_mode==3) + { + while ($p_size != 0) + { + $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Read $v_read_size bytes"); + $v_buffer = @gzread($p_src, $v_read_size); + @gzwrite($p_dest, $v_buffer, $v_read_size); + $p_size -= $v_read_size; + } + } + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : PclZipUtilRename() + // Description : + // This function tries to do a simple rename() function. If it fails, it + // tries to copy the $p_src file in a new $p_dest file and then unlink the + // first one. + // Parameters : + // $p_src : Old filename + // $p_dest : New filename + // Return Values : + // 1 on success, 0 on failure. + // -------------------------------------------------------------------------------- + function PclZipUtilRename($p_src, $p_dest) + { + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZipUtilRename", "source=$p_src, destination=$p_dest"); + $v_result = 1; + // ----- Try to rename the files + if (!@rename($p_src, $p_dest)) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Fail to rename file, try copy+unlink"); + // ----- Try to copy & unlink the src + if (!@copy($p_src, $p_dest)) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Fail to copy file"); + $v_result = 0; + } + else if (!@unlink($p_src)) { + //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Fail to unlink old filename"); + $v_result = 0; + } + } + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : PclZipUtilOptionText() + // Description : + // Translate option value in text. Mainly for debug purpose. + // Parameters : + // $p_option : the option value. + // Return Values : + // The option text value. + // -------------------------------------------------------------------------------- + function PclZipUtilOptionText($p_option) + { + //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZipUtilOptionText", "option='".$p_option."'"); + switch ($p_option) { + case PCLZIP_OPT_PATH : + $v_result = 'PCLZIP_OPT_PATH'; + break; + case PCLZIP_OPT_ADD_PATH : + $v_result = 'PCLZIP_OPT_ADD_PATH'; + break; + case PCLZIP_OPT_REMOVE_PATH : + $v_result = 'PCLZIP_OPT_REMOVE_PATH'; + break; + case PCLZIP_OPT_REMOVE_ALL_PATH : + $v_result = 'PCLZIP_OPT_REMOVE_ALL_PATH'; + break; + case PCLZIP_OPT_EXTRACT_AS_STRING : + $v_result = 'PCLZIP_OPT_EXTRACT_AS_STRING'; + break; + case PCLZIP_OPT_SET_CHMOD : + $v_result = 'PCLZIP_OPT_SET_CHMOD'; + break; + case PCLZIP_OPT_BY_NAME : + $v_result = 'PCLZIP_OPT_BY_NAME'; + break; + case PCLZIP_OPT_BY_INDEX : + $v_result = 'PCLZIP_OPT_BY_INDEX'; + break; + case PCLZIP_OPT_BY_EREG : + $v_result = 'PCLZIP_OPT_BY_EREG'; + break; + case PCLZIP_OPT_BY_PREG : + $v_result = 'PCLZIP_OPT_BY_PREG'; + break; + case PCLZIP_CB_PRE_EXTRACT : + $v_result = 'PCLZIP_CB_PRE_EXTRACT'; + break; + case PCLZIP_CB_POST_EXTRACT : + $v_result = 'PCLZIP_CB_POST_EXTRACT'; + break; + case PCLZIP_CB_PRE_ADD : + $v_result = 'PCLZIP_CB_PRE_ADD'; + break; + case PCLZIP_CB_POST_ADD : + $v_result = 'PCLZIP_CB_POST_ADD'; + break; + default : + $v_result = 'Unknown'; + } + // ----- Return + //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); + return $v_result; + } + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // Function : PclZipUtilTranslateWinPath() + // Description : + // Translate windows path by replacing '\' by '/' and optionally removing + // drive letter. + // Parameters : + // $p_path : path to translate. + // $p_remove_disk_letter : true | false + // Return Values : + // The path translated. + // -------------------------------------------------------------------------------- + function PclZipUtilTranslateWinPath($p_path, $p_remove_disk_letter=true) + { + if (stristr(php_uname(), 'windows')) { + // ----- Look for potential disk letter + if (($p_remove_disk_letter) && (($v_position = strpos($p_path, ':')) != false)) { + $p_path = substr($p_path, $v_position+1); + } + // ----- Change potential windows directory separator + if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) { + $p_path = strtr($p_path, '\\', '/'); + } + } + return $p_path; + } + // -------------------------------------------------------------------------------- +?> diff --git a/includes/pear/Archive/Tar.php b/includes/pear/Archive/Tar.php new file mode 100644 index 0000000..61eff98 --- /dev/null +++ b/includes/pear/Archive/Tar.php @@ -0,0 +1,1909 @@ + + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * @category File_Formats + * @package Archive_Tar + * @author Vincent Blavet + * @copyright 1997-2008 The Authors + * @license http://www.opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Tar.php 295988 2010-03-09 08:39:37Z mrook $ + * @link http://pear.php.net/package/Archive_Tar + */ + +require_once 'PEAR.php'; + + +define ('ARCHIVE_TAR_ATT_SEPARATOR', 90001); +define ('ARCHIVE_TAR_END_BLOCK', pack("a512", '')); + +/** +* Creates a (compressed) Tar archive +* +* @author Vincent Blavet +* @version $Revision: 295988 $ +* @license http://www.opensource.org/licenses/bsd-license.php New BSD License +* @package Archive_Tar +*/ +class Archive_Tar extends PEAR +{ + /** + * @var string Name of the Tar + */ + var $_tarname=''; + + /** + * @var boolean if true, the Tar file will be gzipped + */ + var $_compress=false; + + /** + * @var string Type of compression : 'none', 'gz' or 'bz2' + */ + var $_compress_type='none'; + + /** + * @var string Explode separator + */ + var $_separator=' '; + + /** + * @var file descriptor + */ + var $_file=0; + + /** + * @var string Local Tar name of a remote Tar (http:// or ftp://) + */ + var $_temp_tarname=''; + + /** + * @var string regular expression for ignoring files or directories + */ + var $_ignore_regexp=''; + + // {{{ constructor + /** + * Archive_Tar Class constructor. This flavour of the constructor only + * declare a new Archive_Tar object, identifying it by the name of the + * tar file. + * If the compress argument is set the tar will be read or created as a + * gzip or bz2 compressed TAR file. + * + * @param string $p_tarname The name of the tar archive to create + * @param string $p_compress can be null, 'gz' or 'bz2'. This + * parameter indicates if gzip or bz2 compression + * is required. For compatibility reason the + * boolean value 'true' means 'gz'. + * @access public + */ + function Archive_Tar($p_tarname, $p_compress = null) + { + $this->PEAR(); + $this->_compress = false; + $this->_compress_type = 'none'; + if (($p_compress === null) || ($p_compress == '')) { + if (@file_exists($p_tarname)) { + if ($fp = @fopen($p_tarname, "rb")) { + // look for gzip magic cookie + $data = fread($fp, 2); + fclose($fp); + if ($data == "\37\213") { + $this->_compress = true; + $this->_compress_type = 'gz'; + // No sure it's enought for a magic code .... + } elseif ($data == "BZ") { + $this->_compress = true; + $this->_compress_type = 'bz2'; + } + } + } else { + // probably a remote file or some file accessible + // through a stream interface + if (substr($p_tarname, -2) == 'gz') { + $this->_compress = true; + $this->_compress_type = 'gz'; + } elseif ((substr($p_tarname, -3) == 'bz2') || + (substr($p_tarname, -2) == 'bz')) { + $this->_compress = true; + $this->_compress_type = 'bz2'; + } + } + } else { + if (($p_compress === true) || ($p_compress == 'gz')) { + $this->_compress = true; + $this->_compress_type = 'gz'; + } else if ($p_compress == 'bz2') { + $this->_compress = true; + $this->_compress_type = 'bz2'; + } else { + $this->_error("Unsupported compression type '$p_compress'\n". + "Supported types are 'gz' and 'bz2'.\n"); + return false; + } + } + $this->_tarname = $p_tarname; + if ($this->_compress) { // assert zlib or bz2 extension support + if ($this->_compress_type == 'gz') + $extname = 'zlib'; + else if ($this->_compress_type == 'bz2') + $extname = 'bz2'; + + if (!extension_loaded($extname)) { + PEAR::loadExtension($extname); + } + if (!extension_loaded($extname)) { + $this->_error("The extension '$extname' couldn't be found.\n". + "Please make sure your version of PHP was built ". + "with '$extname' support.\n"); + return false; + } + } + } + // }}} + + // {{{ destructor + function _Archive_Tar() + { + $this->_close(); + // ----- Look for a local copy to delete + if ($this->_temp_tarname != '') + @unlink($this->_temp_tarname); + $this->_PEAR(); + } + // }}} + + // {{{ create() + /** + * This method creates the archive file and add the files / directories + * that are listed in $p_filelist. + * If a file with the same name exist and is writable, it is replaced + * by the new tar. + * The method return false and a PEAR error text. + * The $p_filelist parameter can be an array of string, each string + * representing a filename or a directory name with their path if + * needed. It can also be a single string with names separated by a + * single blank. + * For each directory added in the archive, the files and + * sub-directories are also added. + * See also createModify() method for more details. + * + * @param array $p_filelist An array of filenames and directory names, or a + * single string with names separated by a single + * blank space. + * @return true on success, false on error. + * @see createModify() + * @access public + */ + function create($p_filelist) + { + return $this->createModify($p_filelist, '', ''); + } + // }}} + + // {{{ add() + /** + * This method add the files / directories that are listed in $p_filelist in + * the archive. If the archive does not exist it is created. + * The method return false and a PEAR error text. + * The files and directories listed are only added at the end of the archive, + * even if a file with the same name is already archived. + * See also createModify() method for more details. + * + * @param array $p_filelist An array of filenames and directory names, or a + * single string with names separated by a single + * blank space. + * @return true on success, false on error. + * @see createModify() + * @access public + */ + function add($p_filelist) + { + return $this->addModify($p_filelist, '', ''); + } + // }}} + + // {{{ extract() + function extract($p_path='') + { + return $this->extractModify($p_path, ''); + } + // }}} + + // {{{ listContent() + function listContent() + { + $v_list_detail = array(); + + if ($this->_openRead()) { + if (!$this->_extractList('', $v_list_detail, "list", '', '')) { + unset($v_list_detail); + $v_list_detail = 0; + } + $this->_close(); + } + + return $v_list_detail; + } + // }}} + + // {{{ createModify() + /** + * This method creates the archive file and add the files / directories + * that are listed in $p_filelist. + * If the file already exists and is writable, it is replaced by the + * new tar. It is a create and not an add. If the file exists and is + * read-only or is a directory it is not replaced. The method return + * false and a PEAR error text. + * The $p_filelist parameter can be an array of string, each string + * representing a filename or a directory name with their path if + * needed. It can also be a single string with names separated by a + * single blank. + * The path indicated in $p_remove_dir will be removed from the + * memorized path of each file / directory listed when this path + * exists. By default nothing is removed (empty path '') + * The path indicated in $p_add_dir will be added at the beginning of + * the memorized path of each file / directory listed. However it can + * be set to empty ''. The adding of a path is done after the removing + * of path. + * The path add/remove ability enables the user to prepare an archive + * for extraction in a different path than the origin files are. + * See also addModify() method for file adding properties. + * + * @param array $p_filelist An array of filenames and directory names, + * or a single string with names separated by + * a single blank space. + * @param string $p_add_dir A string which contains a path to be added + * to the memorized path of each element in + * the list. + * @param string $p_remove_dir A string which contains a path to be + * removed from the memorized path of each + * element in the list, when relevant. + * @return boolean true on success, false on error. + * @access public + * @see addModify() + */ + function createModify($p_filelist, $p_add_dir, $p_remove_dir='') + { + $v_result = true; + + if (!$this->_openWrite()) + return false; + + if ($p_filelist != '') { + if (is_array($p_filelist)) + $v_list = $p_filelist; + elseif (is_string($p_filelist)) + $v_list = explode($this->_separator, $p_filelist); + else { + $this->_cleanFile(); + $this->_error('Invalid file list'); + return false; + } + + $v_result = $this->_addList($v_list, $p_add_dir, $p_remove_dir); + } + + if ($v_result) { + $this->_writeFooter(); + $this->_close(); + } else + $this->_cleanFile(); + + return $v_result; + } + // }}} + + // {{{ addModify() + /** + * This method add the files / directories listed in $p_filelist at the + * end of the existing archive. If the archive does not yet exists it + * is created. + * The $p_filelist parameter can be an array of string, each string + * representing a filename or a directory name with their path if + * needed. It can also be a single string with names separated by a + * single blank. + * The path indicated in $p_remove_dir will be removed from the + * memorized path of each file / directory listed when this path + * exists. By default nothing is removed (empty path '') + * The path indicated in $p_add_dir will be added at the beginning of + * the memorized path of each file / directory listed. However it can + * be set to empty ''. The adding of a path is done after the removing + * of path. + * The path add/remove ability enables the user to prepare an archive + * for extraction in a different path than the origin files are. + * If a file/dir is already in the archive it will only be added at the + * end of the archive. There is no update of the existing archived + * file/dir. However while extracting the archive, the last file will + * replace the first one. This results in a none optimization of the + * archive size. + * If a file/dir does not exist the file/dir is ignored. However an + * error text is send to PEAR error. + * If a file/dir is not readable the file/dir is ignored. However an + * error text is send to PEAR error. + * + * @param array $p_filelist An array of filenames and directory + * names, or a single string with names + * separated by a single blank space. + * @param string $p_add_dir A string which contains a path to be + * added to the memorized path of each + * element in the list. + * @param string $p_remove_dir A string which contains a path to be + * removed from the memorized path of + * each element in the list, when + * relevant. + * @return true on success, false on error. + * @access public + */ + function addModify($p_filelist, $p_add_dir, $p_remove_dir='') + { + $v_result = true; + + if (!$this->_isArchive()) + $v_result = $this->createModify($p_filelist, $p_add_dir, + $p_remove_dir); + else { + if (is_array($p_filelist)) + $v_list = $p_filelist; + elseif (is_string($p_filelist)) + $v_list = explode($this->_separator, $p_filelist); + else { + $this->_error('Invalid file list'); + return false; + } + + $v_result = $this->_append($v_list, $p_add_dir, $p_remove_dir); + } + + return $v_result; + } + // }}} + + // {{{ addString() + /** + * This method add a single string as a file at the + * end of the existing archive. If the archive does not yet exists it + * is created. + * + * @param string $p_filename A string which contains the full + * filename path that will be associated + * with the string. + * @param string $p_string The content of the file added in + * the archive. + * @return true on success, false on error. + * @access public + */ + function addString($p_filename, $p_string) + { + $v_result = true; + + if (!$this->_isArchive()) { + if (!$this->_openWrite()) { + return false; + } + $this->_close(); + } + + if (!$this->_openAppend()) + return false; + + // Need to check the get back to the temporary file ? .... + $v_result = $this->_addString($p_filename, $p_string); + + $this->_writeFooter(); + + $this->_close(); + + return $v_result; + } + // }}} + + // {{{ extractModify() + /** + * This method extract all the content of the archive in the directory + * indicated by $p_path. When relevant the memorized path of the + * files/dir can be modified by removing the $p_remove_path path at the + * beginning of the file/dir path. + * While extracting a file, if the directory path does not exists it is + * created. + * While extracting a file, if the file already exists it is replaced + * without looking for last modification date. + * While extracting a file, if the file already exists and is write + * protected, the extraction is aborted. + * While extracting a file, if a directory with the same name already + * exists, the extraction is aborted. + * While extracting a directory, if a file with the same name already + * exists, the extraction is aborted. + * While extracting a file/directory if the destination directory exist + * and is write protected, or does not exist but can not be created, + * the extraction is aborted. + * If after extraction an extracted file does not show the correct + * stored file size, the extraction is aborted. + * When the extraction is aborted, a PEAR error text is set and false + * is returned. However the result can be a partial extraction that may + * need to be manually cleaned. + * + * @param string $p_path The path of the directory where the + * files/dir need to by extracted. + * @param string $p_remove_path Part of the memorized path that can be + * removed if present at the beginning of + * the file/dir path. + * @return boolean true on success, false on error. + * @access public + * @see extractList() + */ + function extractModify($p_path, $p_remove_path) + { + $v_result = true; + $v_list_detail = array(); + + if ($v_result = $this->_openRead()) { + $v_result = $this->_extractList($p_path, $v_list_detail, + "complete", 0, $p_remove_path); + $this->_close(); + } + + return $v_result; + } + // }}} + + // {{{ extractInString() + /** + * This method extract from the archive one file identified by $p_filename. + * The return value is a string with the file content, or NULL on error. + * @param string $p_filename The path of the file to extract in a string. + * @return a string with the file content or NULL. + * @access public + */ + function extractInString($p_filename) + { + if ($this->_openRead()) { + $v_result = $this->_extractInString($p_filename); + $this->_close(); + } else { + $v_result = NULL; + } + + return $v_result; + } + // }}} + + // {{{ extractList() + /** + * This method extract from the archive only the files indicated in the + * $p_filelist. These files are extracted in the current directory or + * in the directory indicated by the optional $p_path parameter. + * If indicated the $p_remove_path can be used in the same way as it is + * used in extractModify() method. + * @param array $p_filelist An array of filenames and directory names, + * or a single string with names separated + * by a single blank space. + * @param string $p_path The path of the directory where the + * files/dir need to by extracted. + * @param string $p_remove_path Part of the memorized path that can be + * removed if present at the beginning of + * the file/dir path. + * @return true on success, false on error. + * @access public + * @see extractModify() + */ + function extractList($p_filelist, $p_path='', $p_remove_path='') + { + $v_result = true; + $v_list_detail = array(); + + if (is_array($p_filelist)) + $v_list = $p_filelist; + elseif (is_string($p_filelist)) + $v_list = explode($this->_separator, $p_filelist); + else { + $this->_error('Invalid string list'); + return false; + } + + if ($v_result = $this->_openRead()) { + $v_result = $this->_extractList($p_path, $v_list_detail, "partial", + $v_list, $p_remove_path); + $this->_close(); + } + + return $v_result; + } + // }}} + + // {{{ setAttribute() + /** + * This method set specific attributes of the archive. It uses a variable + * list of parameters, in the format attribute code + attribute values : + * $arch->setAttribute(ARCHIVE_TAR_ATT_SEPARATOR, ','); + * @param mixed $argv variable list of attributes and values + * @return true on success, false on error. + * @access public + */ + function setAttribute() + { + $v_result = true; + + // ----- Get the number of variable list of arguments + if (($v_size = func_num_args()) == 0) { + return true; + } + + // ----- Get the arguments + $v_att_list = &func_get_args(); + + // ----- Read the attributes + $i=0; + while ($i<$v_size) { + + // ----- Look for next option + switch ($v_att_list[$i]) { + // ----- Look for options that request a string value + case ARCHIVE_TAR_ATT_SEPARATOR : + // ----- Check the number of parameters + if (($i+1) >= $v_size) { + $this->_error('Invalid number of parameters for ' + .'attribute ARCHIVE_TAR_ATT_SEPARATOR'); + return false; + } + + // ----- Get the value + $this->_separator = $v_att_list[$i+1]; + $i++; + break; + + default : + $this->_error('Unknow attribute code '.$v_att_list[$i].''); + return false; + } + + // ----- Next attribute + $i++; + } + + return $v_result; + } + // }}} + + // {{{ setIgnoreRegexp() + /** + * This method sets the regular expression for ignoring files and directories + * at import, for example: + * $arch->setIgnoreRegexp("#CVS|\.svn#"); + * @param string $regexp regular expression defining which files or directories to ignore + * @access public + */ + function setIgnoreRegexp($regexp) + { + $this->_ignore_regexp = $regexp; + } + // }}} + + // {{{ setIgnoreList() + /** + * This method sets the regular expression for ignoring all files and directories + * matching the filenames in the array list at import, for example: + * $arch->setIgnoreList(array('CVS', '.svn', 'bin/tool')); + * @param array $list a list of file or directory names to ignore + * @access public + */ + function setIgnoreList($list) + { + $regexp = str_replace(array('#', '.', '^', '$'), array('\#', '\.', '\^', '\$'), $list); + $regexp = '#/'.join('$|/', $list).'#'; + $this->setIgnoreRegexp($regexp); + } + // }}} + + // {{{ _error() + function _error($p_message) + { + // ----- To be completed + $this->raiseError($p_message); + } + // }}} + + // {{{ _warning() + function _warning($p_message) + { + // ----- To be completed + $this->raiseError($p_message); + } + // }}} + + // {{{ _isArchive() + function _isArchive($p_filename=NULL) + { + if ($p_filename == NULL) { + $p_filename = $this->_tarname; + } + clearstatcache(); + return @is_file($p_filename) && !@is_link($p_filename); + } + // }}} + + // {{{ _openWrite() + function _openWrite() + { + if ($this->_compress_type == 'gz') + $this->_file = @gzopen($this->_tarname, "wb9"); + else if ($this->_compress_type == 'bz2') + $this->_file = @bzopen($this->_tarname, "w"); + else if ($this->_compress_type == 'none') + $this->_file = @fopen($this->_tarname, "wb"); + else + $this->_error('Unknown or missing compression type (' + .$this->_compress_type.')'); + + if ($this->_file == 0) { + $this->_error('Unable to open in write mode \'' + .$this->_tarname.'\''); + return false; + } + + return true; + } + // }}} + + // {{{ _openRead() + function _openRead() + { + if (strtolower(substr($this->_tarname, 0, 7)) == 'http://') { + + // ----- Look if a local copy need to be done + if ($this->_temp_tarname == '') { + $this->_temp_tarname = uniqid('tar').'.tmp'; + if (!$v_file_from = @fopen($this->_tarname, 'rb')) { + $this->_error('Unable to open in read mode \'' + .$this->_tarname.'\''); + $this->_temp_tarname = ''; + return false; + } + if (!$v_file_to = @fopen($this->_temp_tarname, 'wb')) { + $this->_error('Unable to open in write mode \'' + .$this->_temp_tarname.'\''); + $this->_temp_tarname = ''; + return false; + } + while ($v_data = @fread($v_file_from, 1024)) + @fwrite($v_file_to, $v_data); + @fclose($v_file_from); + @fclose($v_file_to); + } + + // ----- File to open if the local copy + $v_filename = $this->_temp_tarname; + + } else + // ----- File to open if the normal Tar file + $v_filename = $this->_tarname; + + if ($this->_compress_type == 'gz') + $this->_file = @gzopen($v_filename, "rb"); + else if ($this->_compress_type == 'bz2') + $this->_file = @bzopen($v_filename, "r"); + else if ($this->_compress_type == 'none') + $this->_file = @fopen($v_filename, "rb"); + else + $this->_error('Unknown or missing compression type (' + .$this->_compress_type.')'); + + if ($this->_file == 0) { + $this->_error('Unable to open in read mode \''.$v_filename.'\''); + return false; + } + + return true; + } + // }}} + + // {{{ _openReadWrite() + function _openReadWrite() + { + if ($this->_compress_type == 'gz') + $this->_file = @gzopen($this->_tarname, "r+b"); + else if ($this->_compress_type == 'bz2') { + $this->_error('Unable to open bz2 in read/write mode \'' + .$this->_tarname.'\' (limitation of bz2 extension)'); + return false; + } else if ($this->_compress_type == 'none') + $this->_file = @fopen($this->_tarname, "r+b"); + else + $this->_error('Unknown or missing compression type (' + .$this->_compress_type.')'); + + if ($this->_file == 0) { + $this->_error('Unable to open in read/write mode \'' + .$this->_tarname.'\''); + return false; + } + + return true; + } + // }}} + + // {{{ _close() + function _close() + { + //if (isset($this->_file)) { + if (is_resource($this->_file)) { + if ($this->_compress_type == 'gz') + @gzclose($this->_file); + else if ($this->_compress_type == 'bz2') + @bzclose($this->_file); + else if ($this->_compress_type == 'none') + @fclose($this->_file); + else + $this->_error('Unknown or missing compression type (' + .$this->_compress_type.')'); + + $this->_file = 0; + } + + // ----- Look if a local copy need to be erase + // Note that it might be interesting to keep the url for a time : ToDo + if ($this->_temp_tarname != '') { + @unlink($this->_temp_tarname); + $this->_temp_tarname = ''; + } + + return true; + } + // }}} + + // {{{ _cleanFile() + function _cleanFile() + { + $this->_close(); + + // ----- Look for a local copy + if ($this->_temp_tarname != '') { + // ----- Remove the local copy but not the remote tarname + @unlink($this->_temp_tarname); + $this->_temp_tarname = ''; + } else { + // ----- Remove the local tarname file + @unlink($this->_tarname); + } + $this->_tarname = ''; + + return true; + } + // }}} + + // {{{ _writeBlock() + function _writeBlock($p_binary_data, $p_len=null) + { + if (is_resource($this->_file)) { + if ($p_len === null) { + if ($this->_compress_type == 'gz') + @gzputs($this->_file, $p_binary_data); + else if ($this->_compress_type == 'bz2') + @bzwrite($this->_file, $p_binary_data); + else if ($this->_compress_type == 'none') + @fputs($this->_file, $p_binary_data); + else + $this->_error('Unknown or missing compression type (' + .$this->_compress_type.')'); + } else { + if ($this->_compress_type == 'gz') + @gzputs($this->_file, $p_binary_data, $p_len); + else if ($this->_compress_type == 'bz2') + @bzwrite($this->_file, $p_binary_data, $p_len); + else if ($this->_compress_type == 'none') + @fputs($this->_file, $p_binary_data, $p_len); + else + $this->_error('Unknown or missing compression type (' + .$this->_compress_type.')'); + + } + } + return true; + } + // }}} + + // {{{ _readBlock() + function _readBlock() + { + $v_block = null; + if (is_resource($this->_file)) { + if ($this->_compress_type == 'gz') + $v_block = @gzread($this->_file, 512); + else if ($this->_compress_type == 'bz2') + $v_block = @bzread($this->_file, 512); + else if ($this->_compress_type == 'none') + $v_block = @fread($this->_file, 512); + else + $this->_error('Unknown or missing compression type (' + .$this->_compress_type.')'); + } + return $v_block; + } + // }}} + + // {{{ _jumpBlock() + function _jumpBlock($p_len=null) + { + if (is_resource($this->_file)) { + if ($p_len === null) + $p_len = 1; + + if ($this->_compress_type == 'gz') { + @gzseek($this->_file, gztell($this->_file)+($p_len*512)); + } + else if ($this->_compress_type == 'bz2') { + // ----- Replace missing bztell() and bzseek() + for ($i=0; $i<$p_len; $i++) + $this->_readBlock(); + } else if ($this->_compress_type == 'none') + @fseek($this->_file, $p_len*512, SEEK_CUR); + else + $this->_error('Unknown or missing compression type (' + .$this->_compress_type.')'); + + } + return true; + } + // }}} + + // {{{ _writeFooter() + function _writeFooter() + { + if (is_resource($this->_file)) { + // ----- Write the last 0 filled block for end of archive + $v_binary_data = pack('a1024', ''); + $this->_writeBlock($v_binary_data); + } + return true; + } + // }}} + + // {{{ _addList() + function _addList($p_list, $p_add_dir, $p_remove_dir) + { + $v_result=true; + $v_header = array(); + + // ----- Remove potential windows directory separator + $p_add_dir = $this->_translateWinPath($p_add_dir); + $p_remove_dir = $this->_translateWinPath($p_remove_dir, false); + + if (!$this->_file) { + $this->_error('Invalid file descriptor'); + return false; + } + + if (sizeof($p_list) == 0) + return true; + + foreach ($p_list as $v_filename) { + if (!$v_result) { + break; + } + + // ----- Skip the current tar name + if ($v_filename == $this->_tarname) + continue; + + if ($v_filename == '') + continue; + + // ----- ignore files and directories matching the ignore regular expression + if ($this->_ignore_regexp && preg_match($this->_ignore_regexp, '/'.$v_filename)) { + $this->_warning("File '$v_filename' ignored"); + continue; + } + + if (!file_exists($v_filename)) { + $this->_warning("File '$v_filename' does not exist"); + continue; + } + + // ----- Add the file or directory header + if (!$this->_addFile($v_filename, $v_header, $p_add_dir, $p_remove_dir)) + return false; + + if (@is_dir($v_filename) && !@is_link($v_filename)) { + if (!($p_hdir = opendir($v_filename))) { + $this->_warning("Directory '$v_filename' can not be read"); + continue; + } + while (false !== ($p_hitem = readdir($p_hdir))) { + if (($p_hitem != '.') && ($p_hitem != '..')) { + if ($v_filename != ".") + $p_temp_list[0] = $v_filename.'/'.$p_hitem; + else + $p_temp_list[0] = $p_hitem; + + $v_result = $this->_addList($p_temp_list, + $p_add_dir, + $p_remove_dir); + } + } + + unset($p_temp_list); + unset($p_hdir); + unset($p_hitem); + } + } + + return $v_result; + } + // }}} + + // {{{ _addFile() + function _addFile($p_filename, &$p_header, $p_add_dir, $p_remove_dir) + { + if (!$this->_file) { + $this->_error('Invalid file descriptor'); + return false; + } + + if ($p_filename == '') { + $this->_error('Invalid file name'); + return false; + } + + // ----- Calculate the stored filename + $p_filename = $this->_translateWinPath($p_filename, false);; + $v_stored_filename = $p_filename; + if (strcmp($p_filename, $p_remove_dir) == 0) { + return true; + } + if ($p_remove_dir != '') { + if (substr($p_remove_dir, -1) != '/') + $p_remove_dir .= '/'; + + if (substr($p_filename, 0, strlen($p_remove_dir)) == $p_remove_dir) + $v_stored_filename = substr($p_filename, strlen($p_remove_dir)); + } + $v_stored_filename = $this->_translateWinPath($v_stored_filename); + if ($p_add_dir != '') { + if (substr($p_add_dir, -1) == '/') + $v_stored_filename = $p_add_dir.$v_stored_filename; + else + $v_stored_filename = $p_add_dir.'/'.$v_stored_filename; + } + + $v_stored_filename = $this->_pathReduction($v_stored_filename); + + if ($this->_isArchive($p_filename)) { + if (($v_file = @fopen($p_filename, "rb")) == 0) { + $this->_warning("Unable to open file '".$p_filename + ."' in binary read mode"); + return true; + } + + if (!$this->_writeHeader($p_filename, $v_stored_filename)) + return false; + + while (($v_buffer = fread($v_file, 512)) != '') { + $v_binary_data = pack("a512", "$v_buffer"); + $this->_writeBlock($v_binary_data); + } + + fclose($v_file); + + } else { + // ----- Only header for dir + if (!$this->_writeHeader($p_filename, $v_stored_filename)) + return false; + } + + return true; + } + // }}} + + // {{{ _addString() + function _addString($p_filename, $p_string) + { + if (!$this->_file) { + $this->_error('Invalid file descriptor'); + return false; + } + + if ($p_filename == '') { + $this->_error('Invalid file name'); + return false; + } + + // ----- Calculate the stored filename + $p_filename = $this->_translateWinPath($p_filename, false);; + + if (!$this->_writeHeaderBlock($p_filename, strlen($p_string), + time(), 384, "", 0, 0)) + return false; + + $i=0; + while (($v_buffer = substr($p_string, (($i++)*512), 512)) != '') { + $v_binary_data = pack("a512", $v_buffer); + $this->_writeBlock($v_binary_data); + } + + return true; + } + // }}} + + // {{{ _writeHeader() + function _writeHeader($p_filename, $p_stored_filename) + { + if ($p_stored_filename == '') + $p_stored_filename = $p_filename; + $v_reduce_filename = $this->_pathReduction($p_stored_filename); + + if (strlen($v_reduce_filename) > 99) { + if (!$this->_writeLongHeader($v_reduce_filename)) + return false; + } + + $v_info = lstat($p_filename); + $v_uid = sprintf("%07s", DecOct($v_info[4])); + $v_gid = sprintf("%07s", DecOct($v_info[5])); + $v_perms = sprintf("%07s", DecOct($v_info['mode'] & 000777)); + + $v_mtime = sprintf("%011s", DecOct($v_info['mtime'])); + + $v_linkname = ''; + + if (@is_link($p_filename)) { + $v_typeflag = '2'; + $v_linkname = readlink($p_filename); + $v_size = sprintf("%011s", DecOct(0)); + } elseif (@is_dir($p_filename)) { + $v_typeflag = "5"; + $v_size = sprintf("%011s", DecOct(0)); + } else { + $v_typeflag = '0'; + clearstatcache(); + $v_size = sprintf("%011s", DecOct($v_info['size'])); + } + + $v_magic = 'ustar '; + + $v_version = ' '; + + if (function_exists('posix_getpwuid')) + { + $userinfo = posix_getpwuid($v_info[4]); + $groupinfo = posix_getgrgid($v_info[5]); + + $v_uname = $userinfo['name']; + $v_gname = $groupinfo['name']; + } + else + { + $v_uname = ''; + $v_gname = ''; + } + + $v_devmajor = ''; + + $v_devminor = ''; + + $v_prefix = ''; + + $v_binary_data_first = pack("a100a8a8a8a12a12", + $v_reduce_filename, $v_perms, $v_uid, + $v_gid, $v_size, $v_mtime); + $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12", + $v_typeflag, $v_linkname, $v_magic, + $v_version, $v_uname, $v_gname, + $v_devmajor, $v_devminor, $v_prefix, ''); + + // ----- Calculate the checksum + $v_checksum = 0; + // ..... First part of the header + for ($i=0; $i<148; $i++) + $v_checksum += ord(substr($v_binary_data_first,$i,1)); + // ..... Ignore the checksum value and replace it by ' ' (space) + for ($i=148; $i<156; $i++) + $v_checksum += ord(' '); + // ..... Last part of the header + for ($i=156, $j=0; $i<512; $i++, $j++) + $v_checksum += ord(substr($v_binary_data_last,$j,1)); + + // ----- Write the first 148 bytes of the header in the archive + $this->_writeBlock($v_binary_data_first, 148); + + // ----- Write the calculated checksum + $v_checksum = sprintf("%06s ", DecOct($v_checksum)); + $v_binary_data = pack("a8", $v_checksum); + $this->_writeBlock($v_binary_data, 8); + + // ----- Write the last 356 bytes of the header in the archive + $this->_writeBlock($v_binary_data_last, 356); + + return true; + } + // }}} + + // {{{ _writeHeaderBlock() + function _writeHeaderBlock($p_filename, $p_size, $p_mtime=0, $p_perms=0, + $p_type='', $p_uid=0, $p_gid=0) + { + $p_filename = $this->_pathReduction($p_filename); + + if (strlen($p_filename) > 99) { + if (!$this->_writeLongHeader($p_filename)) + return false; + } + + if ($p_type == "5") { + $v_size = sprintf("%011s", DecOct(0)); + } else { + $v_size = sprintf("%011s", DecOct($p_size)); + } + + $v_uid = sprintf("%07s", DecOct($p_uid)); + $v_gid = sprintf("%07s", DecOct($p_gid)); + $v_perms = sprintf("%07s", DecOct($p_perms & 000777)); + + $v_mtime = sprintf("%11s", DecOct($p_mtime)); + + $v_linkname = ''; + + $v_magic = 'ustar '; + + $v_version = ' '; + + if (function_exists('posix_getpwuid')) + { + $userinfo = posix_getpwuid($p_uid); + $groupinfo = posix_getgrgid($p_gid); + + $v_uname = $userinfo['name']; + $v_gname = $groupinfo['name']; + } + else + { + $v_uname = ''; + $v_gname = ''; + } + + $v_devmajor = ''; + + $v_devminor = ''; + + $v_prefix = ''; + + $v_binary_data_first = pack("a100a8a8a8a12A12", + $p_filename, $v_perms, $v_uid, $v_gid, + $v_size, $v_mtime); + $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12", + $p_type, $v_linkname, $v_magic, + $v_version, $v_uname, $v_gname, + $v_devmajor, $v_devminor, $v_prefix, ''); + + // ----- Calculate the checksum + $v_checksum = 0; + // ..... First part of the header + for ($i=0; $i<148; $i++) + $v_checksum += ord(substr($v_binary_data_first,$i,1)); + // ..... Ignore the checksum value and replace it by ' ' (space) + for ($i=148; $i<156; $i++) + $v_checksum += ord(' '); + // ..... Last part of the header + for ($i=156, $j=0; $i<512; $i++, $j++) + $v_checksum += ord(substr($v_binary_data_last,$j,1)); + + // ----- Write the first 148 bytes of the header in the archive + $this->_writeBlock($v_binary_data_first, 148); + + // ----- Write the calculated checksum + $v_checksum = sprintf("%06s ", DecOct($v_checksum)); + $v_binary_data = pack("a8", $v_checksum); + $this->_writeBlock($v_binary_data, 8); + + // ----- Write the last 356 bytes of the header in the archive + $this->_writeBlock($v_binary_data_last, 356); + + return true; + } + // }}} + + // {{{ _writeLongHeader() + function _writeLongHeader($p_filename) + { + $v_size = sprintf("%11s ", DecOct(strlen($p_filename))); + + $v_typeflag = 'L'; + + $v_linkname = ''; + + $v_magic = ''; + + $v_version = ''; + + $v_uname = ''; + + $v_gname = ''; + + $v_devmajor = ''; + + $v_devminor = ''; + + $v_prefix = ''; + + $v_binary_data_first = pack("a100a8a8a8a12a12", + '././@LongLink', 0, 0, 0, $v_size, 0); + $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12", + $v_typeflag, $v_linkname, $v_magic, + $v_version, $v_uname, $v_gname, + $v_devmajor, $v_devminor, $v_prefix, ''); + + // ----- Calculate the checksum + $v_checksum = 0; + // ..... First part of the header + for ($i=0; $i<148; $i++) + $v_checksum += ord(substr($v_binary_data_first,$i,1)); + // ..... Ignore the checksum value and replace it by ' ' (space) + for ($i=148; $i<156; $i++) + $v_checksum += ord(' '); + // ..... Last part of the header + for ($i=156, $j=0; $i<512; $i++, $j++) + $v_checksum += ord(substr($v_binary_data_last,$j,1)); + + // ----- Write the first 148 bytes of the header in the archive + $this->_writeBlock($v_binary_data_first, 148); + + // ----- Write the calculated checksum + $v_checksum = sprintf("%06s ", DecOct($v_checksum)); + $v_binary_data = pack("a8", $v_checksum); + $this->_writeBlock($v_binary_data, 8); + + // ----- Write the last 356 bytes of the header in the archive + $this->_writeBlock($v_binary_data_last, 356); + + // ----- Write the filename as content of the block + $i=0; + while (($v_buffer = substr($p_filename, (($i++)*512), 512)) != '') { + $v_binary_data = pack("a512", "$v_buffer"); + $this->_writeBlock($v_binary_data); + } + + return true; + } + // }}} + + // {{{ _readHeader() + function _readHeader($v_binary_data, &$v_header) + { + if (strlen($v_binary_data)==0) { + $v_header['filename'] = ''; + return true; + } + + if (strlen($v_binary_data) != 512) { + $v_header['filename'] = ''; + $this->_error('Invalid block size : '.strlen($v_binary_data)); + return false; + } + + if (!is_array($v_header)) { + $v_header = array(); + } + // ----- Calculate the checksum + $v_checksum = 0; + // ..... First part of the header + for ($i=0; $i<148; $i++) + $v_checksum+=ord(substr($v_binary_data,$i,1)); + // ..... Ignore the checksum value and replace it by ' ' (space) + for ($i=148; $i<156; $i++) + $v_checksum += ord(' '); + // ..... Last part of the header + for ($i=156; $i<512; $i++) + $v_checksum+=ord(substr($v_binary_data,$i,1)); + + $v_data = unpack("a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/" + ."a8checksum/a1typeflag/a100link/a6magic/a2version/" + ."a32uname/a32gname/a8devmajor/a8devminor", + $v_binary_data); + + // ----- Extract the checksum + $v_header['checksum'] = OctDec(trim($v_data['checksum'])); + if ($v_header['checksum'] != $v_checksum) { + $v_header['filename'] = ''; + + // ----- Look for last block (empty block) + if (($v_checksum == 256) && ($v_header['checksum'] == 0)) + return true; + + $this->_error('Invalid checksum for file "'.$v_data['filename'] + .'" : '.$v_checksum.' calculated, ' + .$v_header['checksum'].' expected'); + return false; + } + + // ----- Extract the properties + $v_header['filename'] = $v_data['filename']; + if ($this->_maliciousFilename($v_header['filename'])) { + $this->_error('Malicious .tar detected, file "' . $v_header['filename'] . + '" will not install in desired directory tree'); + return false; + } + $v_header['mode'] = OctDec(trim($v_data['mode'])); + $v_header['uid'] = OctDec(trim($v_data['uid'])); + $v_header['gid'] = OctDec(trim($v_data['gid'])); + $v_header['size'] = OctDec(trim($v_data['size'])); + $v_header['mtime'] = OctDec(trim($v_data['mtime'])); + if (($v_header['typeflag'] = $v_data['typeflag']) == "5") { + $v_header['size'] = 0; + } + $v_header['link'] = trim($v_data['link']); + /* ----- All these fields are removed form the header because + they do not carry interesting info + $v_header[magic] = trim($v_data[magic]); + $v_header[version] = trim($v_data[version]); + $v_header[uname] = trim($v_data[uname]); + $v_header[gname] = trim($v_data[gname]); + $v_header[devmajor] = trim($v_data[devmajor]); + $v_header[devminor] = trim($v_data[devminor]); + */ + + return true; + } + // }}} + + // {{{ _maliciousFilename() + /** + * Detect and report a malicious file name + * + * @param string $file + * @return bool + * @access private + */ + function _maliciousFilename($file) + { + if (strpos($file, '/../') !== false) { + return true; + } + if (strpos($file, '../') === 0) { + return true; + } + return false; + } + // }}} + + // {{{ _readLongHeader() + function _readLongHeader(&$v_header) + { + $v_filename = ''; + $n = floor($v_header['size']/512); + for ($i=0; $i<$n; $i++) { + $v_content = $this->_readBlock(); + $v_filename .= $v_content; + } + if (($v_header['size'] % 512) != 0) { + $v_content = $this->_readBlock(); + $v_filename .= trim($v_content); + } + + // ----- Read the next header + $v_binary_data = $this->_readBlock(); + + if (!$this->_readHeader($v_binary_data, $v_header)) + return false; + + $v_filename = trim($v_filename); + $v_header['filename'] = $v_filename; + if ($this->_maliciousFilename($v_filename)) { + $this->_error('Malicious .tar detected, file "' . $v_filename . + '" will not install in desired directory tree'); + return false; + } + + return true; + } + // }}} + + // {{{ _extractInString() + /** + * This method extract from the archive one file identified by $p_filename. + * The return value is a string with the file content, or NULL on error. + * @param string $p_filename The path of the file to extract in a string. + * @return a string with the file content or NULL. + * @access private + */ + function _extractInString($p_filename) + { + $v_result_str = ""; + + While (strlen($v_binary_data = $this->_readBlock()) != 0) + { + if (!$this->_readHeader($v_binary_data, $v_header)) + return NULL; + + if ($v_header['filename'] == '') + continue; + + // ----- Look for long filename + if ($v_header['typeflag'] == 'L') { + if (!$this->_readLongHeader($v_header)) + return NULL; + } + + if ($v_header['filename'] == $p_filename) { + if ($v_header['typeflag'] == "5") { + $this->_error('Unable to extract in string a directory ' + .'entry {'.$v_header['filename'].'}'); + return NULL; + } else { + $n = floor($v_header['size']/512); + for ($i=0; $i<$n; $i++) { + $v_result_str .= $this->_readBlock(); + } + if (($v_header['size'] % 512) != 0) { + $v_content = $this->_readBlock(); + $v_result_str .= substr($v_content, 0, + ($v_header['size'] % 512)); + } + return $v_result_str; + } + } else { + $this->_jumpBlock(ceil(($v_header['size']/512))); + } + } + + return NULL; + } + // }}} + + // {{{ _extractList() + function _extractList($p_path, &$p_list_detail, $p_mode, + $p_file_list, $p_remove_path) + { + $v_result=true; + $v_nb = 0; + $v_extract_all = true; + $v_listing = false; + + $p_path = $this->_translateWinPath($p_path, false); + if ($p_path == '' || (substr($p_path, 0, 1) != '/' + && substr($p_path, 0, 3) != "../" && !strpos($p_path, ':'))) { + $p_path = "./".$p_path; + } + $p_remove_path = $this->_translateWinPath($p_remove_path); + + // ----- Look for path to remove format (should end by /) + if (($p_remove_path != '') && (substr($p_remove_path, -1) != '/')) + $p_remove_path .= '/'; + $p_remove_path_size = strlen($p_remove_path); + + switch ($p_mode) { + case "complete" : + $v_extract_all = TRUE; + $v_listing = FALSE; + break; + case "partial" : + $v_extract_all = FALSE; + $v_listing = FALSE; + break; + case "list" : + $v_extract_all = FALSE; + $v_listing = TRUE; + break; + default : + $this->_error('Invalid extract mode ('.$p_mode.')'); + return false; + } + + clearstatcache(); + + while (strlen($v_binary_data = $this->_readBlock()) != 0) + { + $v_extract_file = FALSE; + $v_extraction_stopped = 0; + + if (!$this->_readHeader($v_binary_data, $v_header)) + return false; + + if ($v_header['filename'] == '') { + continue; + } + + // ----- Look for long filename + if ($v_header['typeflag'] == 'L') { + if (!$this->_readLongHeader($v_header)) + return false; + } + + if ((!$v_extract_all) && (is_array($p_file_list))) { + // ----- By default no unzip if the file is not found + $v_extract_file = false; + + for ($i=0; $i strlen($p_file_list[$i])) + && (substr($v_header['filename'], 0, strlen($p_file_list[$i])) + == $p_file_list[$i])) { + $v_extract_file = TRUE; + break; + } + } + + // ----- It is a file, so compare the file names + elseif ($p_file_list[$i] == $v_header['filename']) { + $v_extract_file = TRUE; + break; + } + } + } else { + $v_extract_file = TRUE; + } + + // ----- Look if this file need to be extracted + if (($v_extract_file) && (!$v_listing)) + { + if (($p_remove_path != '') + && (substr($v_header['filename'], 0, $p_remove_path_size) + == $p_remove_path)) + $v_header['filename'] = substr($v_header['filename'], + $p_remove_path_size); + if (($p_path != './') && ($p_path != '/')) { + while (substr($p_path, -1) == '/') + $p_path = substr($p_path, 0, strlen($p_path)-1); + + if (substr($v_header['filename'], 0, 1) == '/') + $v_header['filename'] = $p_path.$v_header['filename']; + else + $v_header['filename'] = $p_path.'/'.$v_header['filename']; + } + if (file_exists($v_header['filename'])) { + if ( (@is_dir($v_header['filename'])) + && ($v_header['typeflag'] == '')) { + $this->_error('File '.$v_header['filename'] + .' already exists as a directory'); + return false; + } + if ( ($this->_isArchive($v_header['filename'])) + && ($v_header['typeflag'] == "5")) { + $this->_error('Directory '.$v_header['filename'] + .' already exists as a file'); + return false; + } + if (!is_writeable($v_header['filename'])) { + $this->_error('File '.$v_header['filename'] + .' already exists and is write protected'); + return false; + } + if (filemtime($v_header['filename']) > $v_header['mtime']) { + // To be completed : An error or silent no replace ? + } + } + + // ----- Check the directory availability and create it if necessary + elseif (($v_result + = $this->_dirCheck(($v_header['typeflag'] == "5" + ?$v_header['filename'] + :dirname($v_header['filename'])))) != 1) { + $this->_error('Unable to create path for '.$v_header['filename']); + return false; + } + + if ($v_extract_file) { + if ($v_header['typeflag'] == "5") { + if (!@file_exists($v_header['filename'])) { + if (!@mkdir($v_header['filename'], 0777)) { + $this->_error('Unable to create directory {' + .$v_header['filename'].'}'); + return false; + } + } + } elseif ($v_header['typeflag'] == "2") { + if (@file_exists($v_header['filename'])) { + @unlink($v_header['filename']); + } + if (!@symlink($v_header['link'], $v_header['filename'])) { + $this->_error('Unable to extract symbolic link {' + .$v_header['filename'].'}'); + return false; + } + } else { + if (($v_dest_file = @fopen($v_header['filename'], "wb")) == 0) { + $this->_error('Error while opening {'.$v_header['filename'] + .'} in write binary mode'); + return false; + } else { + $n = floor($v_header['size']/512); + for ($i=0; $i<$n; $i++) { + $v_content = $this->_readBlock(); + fwrite($v_dest_file, $v_content, 512); + } + if (($v_header['size'] % 512) != 0) { + $v_content = $this->_readBlock(); + fwrite($v_dest_file, $v_content, ($v_header['size'] % 512)); + } + + @fclose($v_dest_file); + + // ----- Change the file mode, mtime + @touch($v_header['filename'], $v_header['mtime']); + if ($v_header['mode'] & 0111) { + // make file executable, obey umask + $mode = fileperms($v_header['filename']) | (~umask() & 0111); + @chmod($v_header['filename'], $mode); + } + } + + // ----- Check the file size + clearstatcache(); + if (filesize($v_header['filename']) != $v_header['size']) { + $this->_error('Extracted file '.$v_header['filename'] + .' does not have the correct file size \'' + .filesize($v_header['filename']) + .'\' ('.$v_header['size'] + .' expected). Archive may be corrupted.'); + return false; + } + } + } else { + $this->_jumpBlock(ceil(($v_header['size']/512))); + } + } else { + $this->_jumpBlock(ceil(($v_header['size']/512))); + } + + /* TBC : Seems to be unused ... + if ($this->_compress) + $v_end_of_file = @gzeof($this->_file); + else + $v_end_of_file = @feof($this->_file); + */ + + if ($v_listing || $v_extract_file || $v_extraction_stopped) { + // ----- Log extracted files + if (($v_file_dir = dirname($v_header['filename'])) + == $v_header['filename']) + $v_file_dir = ''; + if ((substr($v_header['filename'], 0, 1) == '/') && ($v_file_dir == '')) + $v_file_dir = '/'; + + $p_list_detail[$v_nb++] = $v_header; + if (is_array($p_file_list) && (count($p_list_detail) == count($p_file_list))) { + return true; + } + } + } + + return true; + } + // }}} + + // {{{ _openAppend() + function _openAppend() + { + if (filesize($this->_tarname) == 0) + return $this->_openWrite(); + + if ($this->_compress) { + $this->_close(); + + if (!@rename($this->_tarname, $this->_tarname.".tmp")) { + $this->_error('Error while renaming \''.$this->_tarname + .'\' to temporary file \''.$this->_tarname + .'.tmp\''); + return false; + } + + if ($this->_compress_type == 'gz') + $v_temp_tar = @gzopen($this->_tarname.".tmp", "rb"); + elseif ($this->_compress_type == 'bz2') + $v_temp_tar = @bzopen($this->_tarname.".tmp", "r"); + + if ($v_temp_tar == 0) { + $this->_error('Unable to open file \''.$this->_tarname + .'.tmp\' in binary read mode'); + @rename($this->_tarname.".tmp", $this->_tarname); + return false; + } + + if (!$this->_openWrite()) { + @rename($this->_tarname.".tmp", $this->_tarname); + return false; + } + + if ($this->_compress_type == 'gz') { + while (!@gzeof($v_temp_tar)) { + $v_buffer = @gzread($v_temp_tar, 512); + if ($v_buffer == ARCHIVE_TAR_END_BLOCK) { + // do not copy end blocks, we will re-make them + // after appending + continue; + } + $v_binary_data = pack("a512", $v_buffer); + $this->_writeBlock($v_binary_data); + } + + @gzclose($v_temp_tar); + } + elseif ($this->_compress_type == 'bz2') { + while (strlen($v_buffer = @bzread($v_temp_tar, 512)) > 0) { + if ($v_buffer == ARCHIVE_TAR_END_BLOCK) { + continue; + } + $v_binary_data = pack("a512", $v_buffer); + $this->_writeBlock($v_binary_data); + } + + @bzclose($v_temp_tar); + } + + if (!@unlink($this->_tarname.".tmp")) { + $this->_error('Error while deleting temporary file \'' + .$this->_tarname.'.tmp\''); + } + + } else { + // ----- For not compressed tar, just add files before the last + // one or two 512 bytes block + if (!$this->_openReadWrite()) + return false; + + clearstatcache(); + $v_size = filesize($this->_tarname); + + // We might have zero, one or two end blocks. + // The standard is two, but we should try to handle + // other cases. + fseek($this->_file, $v_size - 1024); + if (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) { + fseek($this->_file, $v_size - 1024); + } + elseif (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) { + fseek($this->_file, $v_size - 512); + } + } + + return true; + } + // }}} + + // {{{ _append() + function _append($p_filelist, $p_add_dir='', $p_remove_dir='') + { + if (!$this->_openAppend()) + return false; + + if ($this->_addList($p_filelist, $p_add_dir, $p_remove_dir)) + $this->_writeFooter(); + + $this->_close(); + + return true; + } + // }}} + + // {{{ _dirCheck() + + /** + * Check if a directory exists and create it (including parent + * dirs) if not. + * + * @param string $p_dir directory to check + * + * @return bool TRUE if the directory exists or was created + */ + function _dirCheck($p_dir) + { + clearstatcache(); + if ((@is_dir($p_dir)) || ($p_dir == '')) + return true; + + $p_parent_dir = dirname($p_dir); + + if (($p_parent_dir != $p_dir) && + ($p_parent_dir != '') && + (!$this->_dirCheck($p_parent_dir))) + return false; + + if (!@mkdir($p_dir, 0777)) { + $this->_error("Unable to create directory '$p_dir'"); + return false; + } + + return true; + } + + // }}} + + // {{{ _pathReduction() + + /** + * Compress path by changing for example "/dir/foo/../bar" to "/dir/bar", + * rand emove double slashes. + * + * @param string $p_dir path to reduce + * + * @return string reduced path + * + * @access private + * + */ + function _pathReduction($p_dir) + { + $v_result = ''; + + // ----- Look for not empty path + if ($p_dir != '') { + // ----- Explode path by directory names + $v_list = explode('/', $p_dir); + + // ----- Study directories from last to first + for ($i=sizeof($v_list)-1; $i>=0; $i--) { + // ----- Look for current path + if ($v_list[$i] == ".") { + // ----- Ignore this directory + // Should be the first $i=0, but no check is done + } + else if ($v_list[$i] == "..") { + // ----- Ignore it and ignore the $i-1 + $i--; + } + else if ( ($v_list[$i] == '') + && ($i!=(sizeof($v_list)-1)) + && ($i!=0)) { + // ----- Ignore only the double '//' in path, + // but not the first and last / + } else { + $v_result = $v_list[$i].($i!=(sizeof($v_list)-1)?'/' + .$v_result:''); + } + } + } + $v_result = strtr($v_result, '\\', '/'); + return $v_result; + } + + // }}} + + // {{{ _translateWinPath() + function _translateWinPath($p_path, $p_remove_disk_letter=true) + { + if (defined('OS_WINDOWS') && OS_WINDOWS) { + // ----- Look for potential disk letter + if ( ($p_remove_disk_letter) + && (($v_position = strpos($p_path, ':')) != false)) { + $p_path = substr($p_path, $v_position+1); + } + // ----- Change potential windows directory separator + if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) { + $p_path = strtr($p_path, '\\', '/'); + } + } + return $p_path; + } + // }}} + +} +?> diff --git a/includes/pear/Auth.php b/includes/pear/Auth.php new file mode 100644 index 0000000..f34b8f6 --- /dev/null +++ b/includes/pear/Auth.php @@ -0,0 +1,1330 @@ + + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version CVS: $Id$ + * @link http://pear.php.net/package/Auth + */ + +/** + * Returned if session exceeds idle time + */ +define('AUTH_IDLED', -1); +/** + * Returned if session has expired + */ +define('AUTH_EXPIRED', -2); +/** + * Returned if container is unable to authenticate user/password pair + */ +define('AUTH_WRONG_LOGIN', -3); +/** + * Returned if a container method is not supported. + */ +define('AUTH_METHOD_NOT_SUPPORTED', -4); +/** + * Returned if new Advanced security system detects a breach + */ +define('AUTH_SECURITY_BREACH', -5); +/** + * Returned if checkAuthCallback says session should not continue. + */ +define('AUTH_CALLBACK_ABORT', -6); + +/** + * Auth Log level - INFO + */ +define('AUTH_LOG_INFO', 6); +/** + * Auth Log level - DEBUG + */ +define('AUTH_LOG_DEBUG', 7); + +/** + * Auth Advanced Security - IP Checks + */ +define('AUTH_ADV_IPCHECK', 1); +/** + * Auth Advanced Security - User Agent Checks + */ +define('AUTH_ADV_USERAGENT', 2); +/** + * Auth Advanced Security - Challenge Response + */ +define('AUTH_ADV_CHALLENGE', 3); + + +/** + * PEAR::Auth + * + * The PEAR::Auth class provides methods for creating an + * authentication system using PHP. + * + * @category Authentication + * @package Auth + * @author Martin Jansen + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version Release: @package_version@ File: $Revision$ + * @link http://pear.php.net/package/Auth + */ +class Auth { + + // {{{ properties + + /** + * Auth lifetime in seconds + * + * If this variable is set to 0, auth never expires + * + * @var integer + * @see setExpire(), checkAuth() + */ + private $expire = 0; + + /** + * Has the auth session expired? + * + * @var bool + * @see checkAuth() + */ + private $expired = false; + + /** + * Maximum idletime in seconds + * + * The difference to $expire is, that the idletime gets + * refreshed each time checkAuth() is called. If this + * variable is set to 0, idletime is never checked. + * + * @var integer + * @see setIdle(), checkAuth() + */ + private $idle = 0; + + /** + * Is the maximum idletime over? + * + * @var boolean + * @see checkAuth() + */ + private $idled = false; + + /** + * Storage object + * + * @var object + * @see Auth(), validateLogin() + */ + private $storage = ''; + + /** + * User-defined function that creates the login screen + * + * @var string + */ + private $loginFunction = ''; + + /** + * Should the login form be displayed + * + * @var bool + * @see setShowlogin() + */ + private $showLogin = true; + + /** + * Is Login Allowed from this page + * + * @var bool + * @see setAllowLogin + */ + private $allowLogin = true; + + /** + * Current authentication status + * + * @var string + */ + private $status = ''; + + /** + * Username + * + * @var string + */ + private $username = ''; + + /** + * Password + * + * @var string + */ + private $password = ''; + + /** + * checkAuth callback function name + * + * @var string + * @see setCheckAuthCallback() + */ + private $checkAuthCallback = ''; + + /** + * Login callback function name + * + * @var string + * @see setLoginCallback() + */ + private $loginCallback = ''; + + /** + * Failed Login callback function name + * + * @var string + * @see setFailedLoginCallback() + */ + private $loginFailedCallback = ''; + + /** + * Logout callback function name + * + * @var string + * @see setLogoutCallback() + */ + private $logoutCallback = ''; + + /** + * Auth session-array name + * + * @var string + */ + private $_sessionName = '_authsession'; + + /** + * Package Version + * + * @var string + */ + private $version = "@version@"; + + /** + * Flag to use advanced security + * When set extra checks will be made to see if the + * user's IP or useragent have changed across requests. + * Turned off by default to preserve BC. + * + * @var mixed Boolean to turn all advanced security options on or off + * Array containing named values turning specific advanced + * security features on or off individually + * array( + * AUTH_ADV_IPCHECK => true, + * AUTH_ADV_USERAGENT => true, + * AUTH_ADV_CHALLENGE => true, + * ); + */ + private $advancedsecurity = false; + + /** + * Username key in POST array + * + * @var string + */ + private $_postUsername = 'username'; + + /** + * Password key in POST array + * + * @var string + */ + private $_postPassword = 'password'; + + /** + * Holds a reference to the session auth variable + * @var array + */ + private $session; + + /** + * Holds a reference to the global server variable + * @var array + */ + private $server; + + /** + * Holds a reference to the global post variable + * @var array + */ + private $post; + + /** + * Holds a reference to the global cookie variable + * @var array + */ + private $cookie; + + /** + * A hash to hold various superglobals as reference + * @var array + */ + private $authdata; + + /** + * How many times has checkAuth been called + * @var int + */ + private $authChecks = 0; + + /** + * PEAR::Log object + * + * @var object Log + */ + private $logger = null; + + /** + * Whether to enable logging of behaviour + * + * @var boolean + */ + private $enableLogging = false; + + /** + * Whether to regenerate session id everytime start is called + * + * @var boolean + */ + private $regenerateSessionId = false; + + // }}} + // {{{ Auth() [constructor] + + /** + * Constructor + * + * Set up the storage driver. + * + * @param string Type of the storage driver + * @param mixed Additional options for the storage driver + * (example: if you are using DB as the storage + * driver, you have to pass the dsn string here) + * + * @param string Name of the function that creates the login form + * @param boolean Should the login form be displayed if necessary? + * @return void + */ + function Auth($storageDriver, $options = '', $loginFunction = '', $showLogin = true) + { + $this->applyAuthOptions($options); + + // Start the session suppress error if already started + if(!session_id()){ + @session_start(); + if(!session_id()) { + // Throw error + include_once 'PEAR.php'; + PEAR::throwError('Session could not be started by Auth, ' + .'possibly headers are already sent, try putting ' + .'ob_start in the beginning of your script'); + } + } + + // Make Sure Auth session variable is there + if(!isset($_SESSION[$this->_sessionName])) { + $_SESSION[$this->_sessionName] = array(); + } + + // Assign Some globals to internal references, this will replace _importGlobalVariable + $this->session =& $_SESSION[$this->_sessionName]; + $this->server =& $_SERVER; + $this->post =& $_POST; + $this->cookie =& $_COOKIE; + + if ($loginFunction != '' && is_callable($loginFunction)) { + $this->loginFunction = $loginFunction; + } + + if (is_bool($showLogin)) { + $this->showLogin = $showLogin; + } + + if (is_object($storageDriver)) { + $this->storage =& $storageDriver; + // Pass a reference to auth to the container, ugly but works + // this is used by the DB container to use method setAuthData not staticaly. + $this->storage->_auth_obj =& $this; + } else { + // $this->storage = $this->_factory($storageDriver, $options); + // + $this->storage_driver = $storageDriver; + $this->storage_options =& $options; + } + } + + // }}} + // {{{ applyAuthOptions() + + /** + * Set the Auth options + * + * Some options which are Auth specific will be applied + * the rest will be left for usage by the container + * + * @param array An array of Auth options + * @return array The options which were not applied + */ + private function &applyAuthOptions(&$options) + { + if(is_array($options)){ + if (!empty($options['sessionName'])) { + $this->_sessionName = $options['sessionName']; + unset($options['sessionName']); + } + if (isset($options['allowLogin'])) { + $this->allowLogin = $options['allowLogin']; + unset($options['allowLogin']); + } + if (!empty($options['postUsername'])) { + $this->_postUsername = $options['postUsername']; + unset($options['postUsername']); + } + if (!empty($options['postPassword'])) { + $this->_postPassword = $options['postPassword']; + unset($options['postPassword']); + } + if (isset($options['advancedsecurity'])) { + $this->advancedsecurity = $options['advancedsecurity']; + unset($options['advancedsecurity']); + } + if (isset($options['enableLogging'])) { + $this->enableLogging = $options['enableLogging']; + unset($options['enableLogging']); + } + if (isset($options['regenerateSessionId']) && is_bool($options['regenerateSessionId'])) { + $this->regenerateSessionId = $options['regenerateSessionId']; + } + } + return($options); + } + + // }}} + // {{{ _loadStorage() + + /** + * Load Storage Driver if not already loaded + * + * Suspend storage instantiation to make Auth lighter to use + * for calls which do not require login + * + * @return bool True if the conainer is loaded, false if the container + * is already loaded + */ + private function _loadStorage() + { + if(!is_object($this->storage)) { + $this->storage =& $this->_factory($this->storage_driver, + $this->storage_options); + $this->storage->_auth_obj =& $this; + $this->log('Loaded storage container ('.$this->storage_driver.')', AUTH_LOG_DEBUG); + return(true); + } + return(false); + } + + // }}} + // {{{ _factory() + + /** + * Return a storage driver based on $driver and $options + * + * @static + * @param string $driver Type of storage class to return + * @param string $options Optional parameters for the storage class + * @return object Object Storage object + */ + private function &_factory($driver, $options = '') + { + $storage_class = 'Auth_Container_' . $driver; + include_once 'Auth/Container/' . $driver . '.php'; + $obj = new $storage_class($options); + return $obj; + } + + // }}} + // {{{ assignData() + + /** + * Assign data from login form to internal values + * + * This function takes the values for username and password + * from $HTTP_POST_VARS/$_POST and assigns them to internal variables. + * If you wish to use another source apart from $HTTP_POST_VARS/$_POST, + * you have to derive this function. + * + * @global $HTTP_POST_VARS, $_POST + * @see Auth + * @return void + */ + private function assignData() + { + $this->log('Auth::assignData() called.', AUTH_LOG_DEBUG); + + if ( isset($this->post[$this->_postUsername]) + && $this->post[$this->_postUsername] != '') { + $this->username = (get_magic_quotes_gpc() == 1 + ? stripslashes($this->post[$this->_postUsername]) + : $this->post[$this->_postUsername]); + } + if ( isset($this->post[$this->_postPassword]) + && $this->post[$this->_postPassword] != '') { + $this->password = (get_magic_quotes_gpc() == 1 + ? stripslashes($this->post[$this->_postPassword]) + : $this->post[$this->_postPassword] ); + } + } + + // }}} + // {{{ start() + + /** + * Start new auth session + * + * @return void + */ + public function start() + { + $this->log('Auth::start() called.', AUTH_LOG_DEBUG); + + // #10729 - Regenerate session id here if we are generating it on every + // page load. + if ($this->regenerateSessionId) { + session_regenerate_id(true); + } + + $this->assignData(); + if (!$this->checkAuth() && $this->allowLogin) { + $this->login(); + } + } + + // }}} + // {{{ login() + + /** + * Login function + * + * @return void + */ + private function login() + { + $this->log('Auth::login() called.', AUTH_LOG_DEBUG); + + $login_ok = false; + $this->_loadStorage(); + + // Check if using challenge response + (isset($this->post['authsecret']) && $this->post['authsecret'] == 1) + ? $usingChap = true + : $usingChap = false; + + + // When the user has already entered a username, we have to validate it. + if (!empty($this->username)) { + if (true === $this->storage->fetchData($this->username, $this->password, $usingChap)) { + $this->session['challengekey'] = md5($this->username.$this->password); + $login_ok = true; + $this->log('Successful login.', AUTH_LOG_INFO); + } + } + + if (!empty($this->username) && $login_ok) { + $this->setAuth($this->username); + if (is_callable($this->loginCallback)) { + $this->log('Calling loginCallback ('.$this->loginCallback.').', AUTH_LOG_DEBUG); + call_user_func_array($this->loginCallback, array($this->username, &$this)); + } + } + + // If the login failed or the user entered no username, + // output the login screen again. + if (!empty($this->username) && !$login_ok) { + $this->log('Incorrect login.', AUTH_LOG_INFO); + $this->status = AUTH_WRONG_LOGIN; + if (is_callable($this->loginFailedCallback)) { + $this->log('Calling loginFailedCallback ('.$this->loginFailedCallback.').', AUTH_LOG_DEBUG); + call_user_func_array($this->loginFailedCallback, array($this->username, &$this)); + } + } + + if ((empty($this->username) || !$login_ok) && $this->showLogin) { + $this->log('Rendering Login Form.', AUTH_LOG_INFO); + if (is_callable($this->loginFunction)) { + $this->log('Calling loginFunction ('.$this->loginFunction.').', AUTH_LOG_DEBUG); + call_user_func_array($this->loginFunction, array($this->username, $this->status, &$this)); + } else { + // BC fix Auth used to use drawLogin for this + // call is sub classes implement this + if (is_callable(array($this, 'drawLogin'))) { + $this->log('Calling Auth::drawLogin()', AUTH_LOG_DEBUG); + return $this->drawLogin($this->username, $this); + } + + $this->log('Using default Auth_Frontend_Html', AUTH_LOG_DEBUG); + + // New Login form + include_once 'Auth/Frontend/Html.php'; + return Auth_Frontend_Html::render($this, $this->username); + } + } else { + return; + } + } + + // }}} + // {{{ setExpire() + + /** + * Set the maximum expire time + * + * @param integer time in seconds + * @param bool add time to current expire time or not + * @return void + */ + public function setExpire($time, $add = false) + { + $add ? $this->expire += $time : $this->expire = $time; + } + + // }}} + // {{{ setIdle() + + /** + * Set the maximum idle time + * + * @param integer time in seconds + * @param bool add time to current maximum idle time or not + * @return void + */ + public function setIdle($time, $add = false) + { + $add ? $this->idle += $time : $this->idle = $time; + } + + // }}} + // {{{ setSessionName() + + /** + * Set name of the session to a customized value. + * + * If you are using multiple instances of PEAR::Auth + * on the same domain, you can change the name of + * session per application via this function. + * This will chnage the name of the session variable + * auth uses to store it's data in the session + * + * @param string New name for the session + * @return void + */ + public function setSessionName($name = 'session') + { + $this->_sessionName = '_auth_'.$name; + // Make Sure Auth session variable is there + if(!isset($_SESSION[$this->_sessionName])) { + $_SESSION[$this->_sessionName] = array(); + } + $this->session =& $_SESSION[$this->_sessionName]; + } + + // }}} + // {{{ setShowLogin() + + /** + * Should the login form be displayed if necessary? + * + * @param bool show login form or not + * @return void + */ + public function setShowLogin($showLogin = true) + { + $this->showLogin = $showLogin; + } + + // }}} + // {{{ setAllowLogin() + + /** + * Is Login Allowed from this page? + * + * @param bool allow login from this page or not + * @return void + */ + public function setAllowLogin($allowLogin = true) + { + $this->allowLogin = $allowLogin; + } + + // }}} + // {{{ setCheckAuthCallback() + + /** + * Register a callback function to be called whenever the validity of the login is checked + * The function will receive two parameters, the username and a reference to the auth object. + * + * @param string callback function name + * @return void + * @since Method available since Release 1.4.3 + */ + public function setCheckAuthCallback($checkAuthCallback) + { + $this->checkAuthCallback = $checkAuthCallback; + } + + // }}} + // {{{ setLoginCallback() + + /** + * Register a callback function to be called on user login. + * The function will receive two parameters, the username and a reference to the auth object. + * + * @param string callback function name + * @return void + * @see setLogoutCallback() + */ + public function setLoginCallback($loginCallback) + { + $this->loginCallback = $loginCallback; + } + + // }}} + // {{{ setFailedLoginCallback() + + /** + * Register a callback function to be called on failed user login. + * The function will receive two parameters, the username and a reference to the auth object. + * + * @param string callback function name + * @return void + */ + public function setFailedLoginCallback($loginFailedCallback) + { + $this->loginFailedCallback = $loginFailedCallback; + } + + // }}} + // {{{ setLogoutCallback() + + /** + * Register a callback function to be called on user logout. + * The function will receive three parameters, the username and a reference to the auth object. + * + * @param string callback function name + * @return void + * @see setLoginCallback() + */ + public function setLogoutCallback($logoutCallback) + { + $this->logoutCallback = $logoutCallback; + } + + // }}} + // {{{ setAuthData() + + /** + * Register additional information that is to be stored + * in the session. + * + * @param string Name of the data field + * @param mixed Value of the data field + * @param boolean Should existing data be overwritten? (default + * is true) + * @return void + */ + public function setAuthData($name, $value, $overwrite = true) + { + if (!empty($this->session['data'][$name]) && $overwrite == false) { + return; + } + $this->session['data'][$name] = $value; + } + + // }}} + // {{{ getAuthData() + + /** + * Get additional information that is stored in the session. + * + * If no value for the first parameter is passed, the method will + * return all data that is currently stored. + * + * @param string Name of the data field + * @return mixed Value of the data field. + */ + public function getAuthData($name = null) + { + if (!isset($this->session['data'])) { + return null; + } + if(!isset($name)) { + return $this->session['data']; + } + if (isset($name) && isset($this->session['data'][$name])) { + return $this->session['data'][$name]; + } + return null; + } + + // }}} + // {{{ setAuth() + + /** + * Register variable in a session telling that the user + * has logged in successfully + * + * @param string Username + * @return void + */ + public function setAuth($username) + { + $this->log('Auth::setAuth() called.', AUTH_LOG_DEBUG); + + // #10729 - Regenerate session id here only if generating at login only + // Don't do it if we are regenerating on every request so we don't + // regenerate it twice in one request. + if (!$this->regenerateSessionId) { + // #2021 - Change the session id to avoid session fixation attacks php 4.3.3 > + session_regenerate_id(true); + } + + if (!isset($this->session) || !is_array($this->session)) { + $this->session = array(); + } + + if (!isset($this->session['data'])) { + $this->session['data'] = array(); + } + + $this->session['sessionip'] = isset($this->server['REMOTE_ADDR']) + ? $this->server['REMOTE_ADDR'] + : ''; + $this->session['sessionuseragent'] = isset($this->server['HTTP_USER_AGENT']) + ? $this->server['HTTP_USER_AGENT'] + : ''; + $this->session['sessionforwardedfor'] = isset($this->server['HTTP_X_FORWARDED_FOR']) + ? $this->server['HTTP_X_FORWARDED_FOR'] + : ''; + + // This should be set by the container to something more safe + // Like md5(passwd.microtime) + if(empty($this->session['challengekey'])) { + $this->session['challengekey'] = md5($username.microtime()); + } + + $this->session['challengecookie'] = md5($this->session['challengekey'].microtime()); + setcookie('authchallenge', $this->session['challengecookie'], 0, '/'); + + $this->session['registered'] = true; + $this->session['username'] = $username; + $this->session['timestamp'] = time(); + $this->session['idle'] = time(); + } + + // }}} + // {{{ setAdvancedSecurity() + + /** + * Enables advanced security checks + * + * Currently only ip change and useragent change + * are detected + * @todo Add challenge cookies - Create a cookie which changes every time + * and contains some challenge key which the server can verify with + * a session var cookie might need to be crypted (user pass) + * @param bool Enable or disable + * @return void + */ + public function setAdvancedSecurity($flag=true) + { + $this->advancedsecurity = $flag; + } + + // }}} + // {{{ checkAuth() + + /** + * Checks if there is a session with valid auth information. + * + * @return boolean Whether or not the user is authenticated. + */ + public function checkAuth() + { + $this->log('Auth::checkAuth() called.', AUTH_LOG_DEBUG); + $this->authChecks++; + if (isset($this->session)) { + // Check if authentication session is expired + if ( $this->expire > 0 + && isset($this->session['timestamp']) + && ($this->session['timestamp'] + $this->expire) < time()) { + $this->log('Session Expired', AUTH_LOG_INFO); + $this->expired = true; + $this->status = AUTH_EXPIRED; + $this->logout(); + return false; + } + + // Check if maximum idle time is reached + if ( $this->idle > 0 + && isset($this->session['idle']) + && ($this->session['idle'] + $this->idle) < time()) { + $this->log('Session Idle Time Reached', AUTH_LOG_INFO); + $this->idled = true; + $this->status = AUTH_IDLED; + $this->logout(); + return false; + } + + if ( isset($this->session['registered']) + && isset($this->session['username']) + && $this->session['registered'] == true + && $this->session['username'] != '') { + Auth::updateIdle(); + + if ($this->_isAdvancedSecurityEnabled()) { + $this->log('Advanced Security Mode Enabled.', AUTH_LOG_DEBUG); + + // Only Generate the challenge once + if ( $this->authChecks == 1 + && $this->_isAdvancedSecurityEnabled(AUTH_ADV_CHALLENGE)) { + $this->log('Generating new Challenge Cookie.', AUTH_LOG_DEBUG); + $this->session['challengecookieold'] = $this->session['challengecookie']; + $this->session['challengecookie'] = md5($this->session['challengekey'].microtime()); + setcookie('authchallenge', $this->session['challengecookie'], 0, '/'); + } + + // Check for ip change + if ( $this->_isAdvancedSecurityEnabled(AUTH_ADV_IPCHECK) + && isset($this->server['REMOTE_ADDR']) + && $this->session['sessionip'] != $this->server['REMOTE_ADDR']) { + $this->log('Security Breach. Remote IP Address changed.', AUTH_LOG_INFO); + // Check if the IP of the user has changed, if so we + // assume a man in the middle attack and log him out + $this->expired = true; + $this->status = AUTH_SECURITY_BREACH; + $this->logout(); + return false; + } + + // Check for ip change (if connected via proxy) + if ( $this->_isAdvancedSecurityEnabled(AUTH_ADV_IPCHECK) + && isset($this->server['HTTP_X_FORWARDED_FOR']) + && $this->session['sessionforwardedfor'] != $this->server['HTTP_X_FORWARDED_FOR']) { + $this->log('Security Breach. Forwarded For IP Address changed.', AUTH_LOG_INFO); + // Check if the IP of the user connecting via proxy has + // changed, if so we assume a man in the middle attack + // and log him out. + $this->expired = true; + $this->status = AUTH_SECURITY_BREACH; + $this->logout(); + return false; + } + + // Check for useragent change + if ( $this->_isAdvancedSecurityEnabled(AUTH_ADV_USERAGENT) + && isset($this->server['HTTP_USER_AGENT']) + && $this->session['sessionuseragent'] != $this->server['HTTP_USER_AGENT']) { + $this->log('Security Breach. User Agent changed.', AUTH_LOG_INFO); + // Check if the User-Agent of the user has changed, if + // so we assume a man in the middle attack and log him out + $this->expired = true; + $this->status = AUTH_SECURITY_BREACH; + $this->logout(); + return false; + } + + // Check challenge cookie here, if challengecookieold is not set + // this is the first time and check is skipped + // TODO when user open two pages similtaneuly (open in new window,open + // in tab) auth breach is caused find out a way around that if possible + if ( $this->_isAdvancedSecurityEnabled(AUTH_ADV_CHALLENGE) + && isset($this->session['challengecookieold']) + && $this->session['challengecookieold'] != $this->cookie['authchallenge']) { + $this->log('Security Breach. Challenge Cookie mismatch.', AUTH_LOG_INFO); + $this->expired = true; + $this->status = AUTH_SECURITY_BREACH; + $this->logout(); + $this->login(); + return false; + } + } + + if (is_callable($this->checkAuthCallback)) { + $this->log('Calling checkAuthCallback ('.$this->checkAuthCallback.').', AUTH_LOG_DEBUG); + $checkCallback = call_user_func_array($this->checkAuthCallback, array($this->username, &$this)); + if ($checkCallback == false) { + $this->log('checkAuthCallback failed.', AUTH_LOG_INFO); + $this->expired = true; + $this->status = AUTH_CALLBACK_ABORT; + $this->logout(); + return false; + } + } + + $this->log('Session OK.', AUTH_LOG_INFO); + return true; + } + } else { + $this->log('Unable to locate session storage.', AUTH_LOG_DEBUG); + return false; + } + $this->log('No login session.', AUTH_LOG_DEBUG); + return false; + } + + // }}} + // {{{ staticCheckAuth() [static] + + /** + * Statically checks if there is a session with valid auth information. + * + * @access public + * @see checkAuth + * @return boolean Whether or not the user is authenticated. + */ + public static function staticCheckAuth($options = null) + { + static $staticAuth; + if(!isset($staticAuth)) { + $staticAuth = new Auth('null', $options); + } + $staticAuth->log('Auth::staticCheckAuth() called', AUTH_LOG_DEBUG); + return $staticAuth->checkAuth(); + } + + // }}} + // {{{ getAuth() + + /** + * Has the user been authenticated? + * + * Is there a valid login session. Previously this was different from + * checkAuth() but now it is just an alias. + * + * @return bool True if the user is logged in, otherwise false. + */ + public function getAuth() + { + $this->log('Auth::getAuth() called.', AUTH_LOG_DEBUG); + return $this->checkAuth(); + } + + // }}} + // {{{ logout() + + /** + * Logout function + * + * This function clears any auth tokens in the currently + * active session and executes the logout callback function, + * if any + * + * @return void + */ + public function logout() + { + $this->log('Auth::logout() called.', AUTH_LOG_DEBUG); + + if (is_callable($this->logoutCallback) && isset($this->session['username'])) { + $this->log('Calling logoutCallback ('.$this->logoutCallback.').', AUTH_LOG_DEBUG); + call_user_func_array($this->logoutCallback, array($this->session['username'], &$this)); + } + + $this->username = ''; + $this->password = ''; + + $this->session = null; + } + + // }}} + // {{{ updateIdle() + + /** + * Update the idletime + * + * @return void + */ + public function updateIdle() + { + $this->session['idle'] = time(); + } + + // }}} + // {{{ getUsername() + + /** + * Get the username + * + * @return string + */ + public function getUsername() + { + if (isset($this->session['username'])) { + return($this->session['username']); + } + return(''); + } + + // }}} + // {{{ getStatus() + + /** + * Get the current status + * + * @return string + */ + public function getStatus() + { + return $this->status; + } + + // }}} + // {{{ getPostUsernameField() + + /** + * Gets the post varible used for the username + * + * @return string + */ + public function getPostUsernameField() + { + return($this->_postUsername); + } + + // }}} + // {{{ getPostPasswordField() + + /** + * Gets the post varible used for the username + * + * @return string + */ + public function getPostPasswordField() + { + return($this->_postPassword); + } + + // }}} + // {{{ sessionValidThru() + + /** + * Returns the time up to the session is valid + * + * @return integer + */ + public function sessionValidThru() + { + if (!isset($this->session['idle'])) { + return 0; + } + if ($this->idle == 0) { + return 0; + } + return ($this->session['idle'] + $this->idle); + } + + // }}} + // {{{ listUsers() + + /** + * List all users that are currently available in the storage + * container + * + * @return array + */ + public function listUsers() + { + $this->log('Auth::listUsers() called.', AUTH_LOG_DEBUG); + $this->_loadStorage(); + return $this->storage->listUsers(); + } + + // }}} + // {{{ addUser() + + /** + * Add user to the storage container + * + * @param string Username + * @param string Password + * @param mixed Additional parameters + * @return mixed True on success, PEAR error object on error + * and AUTH_METHOD_NOT_SUPPORTED otherwise. + */ + public function addUser($username, $password, $additional = '') + { + $this->log('Auth::addUser() called.', AUTH_LOG_DEBUG); + $this->_loadStorage(); + return $this->storage->addUser($username, $password, $additional); + } + + // }}} + // {{{ removeUser() + + /** + * Remove user from the storage container + * + * @param string Username + * @return mixed True on success, PEAR error object on error + * and AUTH_METHOD_NOT_SUPPORTED otherwise. + */ + public function removeUser($username) + { + $this->log('Auth::removeUser() called.', AUTH_LOG_DEBUG); + $this->_loadStorage(); + return $this->storage->removeUser($username); + } + + // }}} + // {{{ changePassword() + + /** + * Change password for user in the storage container + * + * @param string Username + * @param string The new password + * @return mixed True on success, PEAR error object on error + * and AUTH_METHOD_NOT_SUPPORTED otherwise. + */ + public function changePassword($username, $password) + { + $this->log('Auth::changePassword() called', AUTH_LOG_DEBUG); + $this->_loadStorage(); + return $this->storage->changePassword($username, $password); + } + + // }}} + // {{{ log() + + /** + * Log a message from the Auth system + * + * @param string The message to log + * @param string The log level to log the message under. See the Log documentation for more info. + * @return boolean + */ + public function log($message, $level = AUTH_LOG_DEBUG) + { + if (!$this->enableLogging) return false; + + $this->_loadLogger(); + + $this->logger->log('AUTH: '.$message, $level); + } + + // }}} + // {{{ _loadLogger() + + /** + * Load Log object if not already loaded + * + * Suspend logger instantiation to make Auth lighter to use + * for calls which do not require logging + * + * @return bool True if the logger is loaded, false if the logger + * is already loaded + */ + private function _loadLogger() + { + if(is_null($this->logger)) { + if (!class_exists('Log')) { + include_once 'Log.php'; + } + $this->logger =& Log::singleton('null', + null, + 'auth['.getmypid().']', + array(), + AUTH_LOG_DEBUG); + return(true); + } + return(false); + } + + // }}} + // {{{ attachLogObserver() + + /** + * Attach an Observer to the Auth Log Source + * + * @param object Log_Observer A Log Observer instance + * @return boolean + */ + public function attachLogObserver(&$observer) { + + $this->_loadLogger(); + + return $this->logger->attach($observer); + + } + + // }}} + // {{{ _isAdvancedSecurityEnabled() + + /** + * Is advanced security enabled? + * + * Pass one of the Advanced Security constants as the first parameter + * to check if that advanced security check is enabled. + * + * @param integer + * @return boolean + */ + private function _isAdvancedSecurityEnabled($feature = null) { + + if (is_null($feature)) { + + if ($this->advancedsecurity === true) + return true; + + if ( is_array($this->advancedsecurity) + && in_array(true, $this->advancedsecurity, true)) + return true; + + return false; + + } else { + + if (is_array($this->advancedsecurity)) { + + if ( isset($this->advancedsecurity[$feature]) + && $this->advancedsecurity[$feature] == true) + return true; + + return false; + + } + + return (bool)$this->advancedsecurity; + + } + + } + + // }}} + +} +?> diff --git a/includes/pear/Auth/Anonymous.php b/includes/pear/Auth/Anonymous.php new file mode 100644 index 0000000..ec29bd6 --- /dev/null +++ b/includes/pear/Auth/Anonymous.php @@ -0,0 +1,138 @@ + + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version CVS: $Id: Anonymous.php 289651 2009-10-15 04:39:07Z aashley $ + * @link http://pear.php.net/package/Auth + * @since File available since Release 1.3.0 + */ + +/** + * Include Auth package + */ +require_once 'Auth.php'; + +/** + * Anonymous Authentication + * + * This class provides anonymous authentication if username and password + * were not supplied + * + * @category Authentication + * @package Auth + * @author Yavor Shahpasov + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version Release: @package_version@ File: $Revision: 289651 $ + * @link http://pear.php.net/package/Auth + * @since Class available since Release 1.3.0 + */ +class Auth_Anonymous extends Auth +{ + + // {{{ properties + + /** + * Whether to allow anonymous authentication + * + * @var boolean + */ + var $allow_anonymous = true; + + /** + * Username to use for anonymous user + * + * @var string + */ + var $anonymous_username = 'anonymous'; + + // }}} + // {{{ Auth_Anonymous() [constructor] + + /** + * Pass all parameters to Parent Auth class + * + * Set up the storage driver. + * + * @param string Type of the storage driver + * @param mixed Additional options for the storage driver + * (example: if you are using DB as the storage + * driver, you have to pass the dsn string here) + * + * @param string Name of the function that creates the login form + * @param boolean Should the login form be displayed if necessary? + * @return void + * @see Auth::Auth() + */ + function Auth_Anonymous($storageDriver, $options = '', $loginFunction = '', $showLogin = true) { + parent::Auth($storageDriver, $options, $loginFunction, $showLogin); + } + + // }}} + // {{{ login() + + /** + * Login function + * + * If no username & password is passed then login as the username + * provided in $this->anonymous_username else call standard login() + * function. + * + * @return void + * @access private + * @see Auth::login() + */ + function login() { + if ( $this->allow_anonymous + && empty($this->username) + && empty($this->password) ) { + $this->setAuth($this->anonymous_username); + if (is_callable($this->loginCallback)) { + call_user_func_array($this->loginCallback, array($this->username, $this) ); + } + } else { + // Call normal login system + parent::login(); + } + } + + // }}} + // {{{ forceLogin() + + /** + * Force the user to login + * + * Calling this function forces the user to provide a real username and + * password before continuing. + * + * @return void + */ + function forceLogin() { + $this->allow_anonymous = false; + if( !empty($this->session['username']) && $this->session['username'] == $this->anonymous_username ) { + $this->logout(); + } + } + + // }}} + +} + +?> diff --git a/includes/pear/Auth/Auth.php b/includes/pear/Auth/Auth.php new file mode 100644 index 0000000..61ffe5c --- /dev/null +++ b/includes/pear/Auth/Auth.php @@ -0,0 +1,30 @@ + + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version CVS: $Id: Auth.php 208437 2006-03-02 06:53:08Z aashley $ + * @link http://pear.php.net/package/Auth + * @deprecated File deprecated since Release 1.2.0 + */ + +/** + * Include Auth package + */ +require_once 'Auth.php'; + +?> diff --git a/includes/pear/Auth/Container.php b/includes/pear/Auth/Container.php new file mode 100644 index 0000000..5724d7d --- /dev/null +++ b/includes/pear/Auth/Container.php @@ -0,0 +1,262 @@ + + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version CVS: $Id: Container.php 294935 2010-02-12 00:05:45Z clockwerx $ + * @link http://pear.php.net/package/Auth + */ + +/** + * Storage class for fetching login data + * + * @category Authentication + * @package Auth + * @author Martin Jansen + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version Release: @package_version@ File: $Revision: 294935 $ + * @link http://pear.php.net/package/Auth + */ +class Auth_Container +{ + + // {{{ properties + + /** + * User that is currently selected from the storage container. + * + * @access public + */ + var $activeUser = ""; + + /** + * The Auth object this container is attached to. + * + * @access public + */ + var $_auth_obj = null; + + // }}} + // {{{ Auth_Container() [constructor] + + /** + * Constructor + * + * Has to be overwritten by each storage class + * + * @access public + */ + function Auth_Container() + { + } + + // }}} + // {{{ fetchData() + + /** + * Fetch data from storage container + * + * Has to be overwritten by each storage class + * + * @access public + */ + function fetchData($username, $password, $isChallengeResponse=false) + { + $this->log('Auth_Container::fetchData() called.', AUTH_LOG_DEBUG); + } + + // }}} + // {{{ verifyPassword() + + /** + * Crypt and verfiy the entered password + * + * @param string Entered password + * @param string Password from the data container (usually this password + * is already encrypted. + * @param string Type of algorithm with which the password from + * the container has been crypted. (md5, crypt etc.) + * Defaults to "md5". + * @return bool True, if the passwords match + */ + function verifyPassword($password1, $password2, $cryptType = "md5") + { + $this->log('Auth_Container::verifyPassword() called.', AUTH_LOG_DEBUG); + switch ($cryptType) { + case "crypt" : + return ((string)crypt($password1, $password2) === (string)$password2); + break; + case "none" : + case "" : + return ((string)$password1 === (string)$password2); + break; + case "md5" : + return ((string)md5($password1) === (string)$password2); + break; + default : + if (function_exists($cryptType)) { + return ((string)$cryptType($password1) === (string)$password2); + } elseif (method_exists($this,$cryptType)) { + return ((string)$this->$cryptType($password1) === (string)$password2); + } else { + return false; + } + break; + } + } + + // }}} + // {{{ supportsChallengeResponse() + + /** + * Returns true if the container supports Challenge Response + * password authentication + */ + function supportsChallengeResponse() + { + return(false); + } + + // }}} + // {{{ getCryptType() + + /** + * Returns the crypt current crypt type of the container + * + * @return string + */ + function getCryptType() + { + return(''); + } + + // }}} + // {{{ listUsers() + + /** + * List all users that are available from the storage container + */ + function listUsers() + { + $this->log('Auth_Container::listUsers() called.', AUTH_LOG_DEBUG); + return AUTH_METHOD_NOT_SUPPORTED; + } + + // }}} + // {{{ getUser() + + /** + * Returns a user assoc array + * + * Containers which want should overide this + * + * @param string The username + */ + function getUser($username) + { + $this->log('Auth_Container::getUser() called.', AUTH_LOG_DEBUG); + $users = $this->listUsers(); + if ($users === AUTH_METHOD_NOT_SUPPORTED) { + return AUTH_METHOD_NOT_SUPPORTED; + } + for ($i=0; $c = count($users), $i<$c; $i++) { + if ($users[$i]['username'] == $username) { + return $users[$i]; + } + } + return false; + } + + // }}} + // {{{ addUser() + + /** + * Add a new user to the storage container + * + * @param string Username + * @param string Password + * @param array Additional information + * + * @return boolean + */ + function addUser($username, $password, $additional=null) + { + $this->log('Auth_Container::addUser() called.', AUTH_LOG_DEBUG); + return AUTH_METHOD_NOT_SUPPORTED; + } + + // }}} + // {{{ removeUser() + + /** + * Remove user from the storage container + * + * @param string Username + */ + function removeUser($username) + { + $this->log('Auth_Container::removeUser() called.', AUTH_LOG_DEBUG); + return AUTH_METHOD_NOT_SUPPORTED; + } + + // }}} + // {{{ changePassword() + + /** + * Change password for user in the storage container + * + * @param string Username + * @param string The new password + */ + function changePassword($username, $password) + { + $this->log('Auth_Container::changePassword() called.', AUTH_LOG_DEBUG); + return AUTH_METHOD_NOT_SUPPORTED; + } + + // }}} + // {{{ log() + + /** + * Log a message to the Auth log + * + * @param string The message + * @param int + * @return boolean + */ + function log($message, $level = AUTH_LOG_DEBUG) { + + if (is_null($this->_auth_obj)) { + + return false; + + } else { + + return $this->_auth_obj->log($message, $level); + + } + + } + + // }}} + +} + +?> diff --git a/includes/pear/Auth/Container/Array.php b/includes/pear/Auth/Container/Array.php new file mode 100644 index 0000000..334080c --- /dev/null +++ b/includes/pear/Auth/Container/Array.php @@ -0,0 +1,161 @@ + + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version CVS: $Id: Array.php 237449 2007-06-12 03:11:27Z aashley $ + * @since File available since Release 1.4.0 + */ + +/** + * Include Auth_Container base class + */ +require_once "Auth/Container.php"; +/** + * Include PEAR package for error handling + */ +require_once "PEAR.php"; + +/** + * Storage driver for fetching authentication data from a PHP Array + * + * This container takes two options when configuring: + * + * cryptType: The crypt used to store the password. Currently recognised + * are: none, md5 and crypt. default: none + * users: A named array of usernames and passwords. + * Ex: + * array( + * 'guest' => '084e0343a0486ff05530df6c705c8bb4', // password guest + * 'georg' => 'fc77dba827fcc88e0243404572c51325' // password georg + * ) + * + * Usage Example: + * array( + * 'guest' => '084e0343a0486ff05530df6c705c8bb4', // password guest + * 'georg' => 'fc77dba827fcc88e0243404572c51325' // password georg + * ), + * 'cryptType'=>'md5', + * ); + * + * $auth = new Auth("Array", $AuthOptions); + * ?> + * + * @category Authentication + * @package Auth + * @author georg_1 at have2 dot com + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version Release: @package_version@ File: $Revision: 237449 $ + * @since File available since Release 1.4.0 + */ + +class Auth_Container_Array extends Auth_Container { + + // {{{ properties + + /** + * The users and their password to authenticate against + * + * @var array $users + */ + var $users; + + /** + * The cryptType used on the passwords + * + * @var string $cryptType + */ + var $cryptType = 'none'; + + // }}} + // {{{ Auth_Container_Array() + + /** + * Constructor for Array Container + * + * @param array $data Options for the container + * @return void + */ + function Auth_Container_Array($data) + { + if (!is_array($data)) { + PEAR::raiseError('The options for Auth_Container_Array must be an array'); + } + if (isset($data['users']) && is_array($data['users'])) { + $this->users = $data['users']; + } else { + $this->users = array(); + PEAR::raiseError('Auth_Container_Array: no user data found in options array'); + } + if (isset($data['cryptType'])) { + $this->cryptType = $data['cryptType']; + } + } + + // }}} + // {{{ fetchData() + + /** + * Get user information from array + * + * This function uses the given username to fetch the corresponding + * login data from the array. If an account that matches the passed + * username and password is found, the function returns true. + * Otherwise it returns false. + * + * @param string Username + * @param string Password + * @return boolean|PEAR_Error Error object or boolean + */ + function fetchData($user, $pass) + { + $this->log('Auth_Container_Array::fetchData() called.', AUTH_LOG_DEBUG); + if ( isset($this->users[$user]) + && $this->verifyPassword($pass, $this->users[$user], $this->cryptType)) { + return true; + } + return false; + } + + // }}} + // {{{ listUsers() + + /** + * Returns a list of users available within the container + * + * @return array + */ + function listUsers() + { + $this->log('Auth_Container_Array::listUsers() called.', AUTH_LOG_DEBUG); + $ret = array(); + foreach ($this->users as $username => $password) { + $ret[]['username'] = $username; + } + return $ret; + } + + // }}} + +} + +?> diff --git a/includes/pear/Auth/Container/DB.php b/includes/pear/Auth/Container/DB.php new file mode 100644 index 0000000..9137d39 --- /dev/null +++ b/includes/pear/Auth/Container/DB.php @@ -0,0 +1,639 @@ + + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version CVS: $Id: DB.php 256753 2008-04-04 07:57:02Z aashley $ + * @link http://pear.php.net/package/Auth + */ + +/** + * Include Auth_Container base class + */ +require_once 'Auth/Container.php'; +/** + * Include PEAR DB + */ +require_once 'DB.php'; + +/** + * Storage driver for fetching login data from a database + * + * This storage driver can use all databases which are supported + * by the PEAR DB abstraction layer to fetch login data. + * + * @category Authentication + * @package Auth + * @author Martin Jansen + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version Release: @package_version@ File: $Revision: 256753 $ + * @link http://pear.php.net/package/Auth + */ +class Auth_Container_DB extends Auth_Container +{ + + // {{{ properties + + /** + * Additional options for the storage container + * @var array + */ + var $options = array(); + + /** + * DB object + * @var object + */ + var $db = null; + var $dsn = ''; + + /** + * User that is currently selected from the DB. + * @var string + */ + var $activeUser = ''; + + // }}} + // {{{ Auth_Container_DB [constructor] + + /** + * Constructor of the container class + * + * Save the initial options passed to the container. Initiation of the DB + * connection is no longer performed here and is only done when needed. + * + * @param string Connection data or DB object + * @return object Returns an error object if something went wrong + */ + function Auth_Container_DB($dsn) + { + $this->_setDefaults(); + + if (is_array($dsn)) { + $this->_parseOptions($dsn); + + if (empty($this->options['dsn'])) { + PEAR::raiseError('No connection parameters specified!'); + } + } else { + $this->options['dsn'] = $dsn; + } + } + + // }}} + // {{{ _connect() + + /** + * Connect to database by using the given DSN string + * + * @access private + * @param string DSN string + * @return mixed Object on error, otherwise bool + */ + function _connect($dsn) + { + $this->log('Auth_Container_DB::_connect() called.', AUTH_LOG_DEBUG); + + if (is_string($dsn) || is_array($dsn)) { + $this->db = DB::Connect($dsn, $this->options['db_options']); + } elseif (is_subclass_of($dsn, 'db_common')) { + $this->db = $dsn; + } elseif (DB::isError($dsn)) { + return PEAR::raiseError($dsn->getMessage(), $dsn->getCode()); + } else { + return PEAR::raiseError('The given dsn was not valid in file ' . __FILE__ . ' at line ' . __LINE__, + 41, + PEAR_ERROR_RETURN, + null, + null + ); + } + + if (DB::isError($this->db) || PEAR::isError($this->db)) { + return PEAR::raiseError($this->db->getMessage(), $this->db->getCode()); + } else { + return true; + } + } + + // }}} + // {{{ _prepare() + + /** + * Prepare database connection + * + * This function checks if we have already opened a connection to + * the database. If that's not the case, a new connection is opened. + * + * @access private + * @return mixed True or a DB error object. + */ + function _prepare() + { + if (!DB::isConnection($this->db)) { + $res = $this->_connect($this->options['dsn']); + if (DB::isError($res) || PEAR::isError($res)) { + return $res; + } + } + if ($this->options['auto_quote'] && $this->db->dsn['phptype'] != 'sqlite') { + if (strpos('.', $this->options['table']) === false) { + $this->options['final_table'] = $this->db->quoteIdentifier($this->options['table']); + } else { + $t = explode('.', $this->options['table']); + for ($i = 0, $count = count($t); $i < $count; $i++) + $t[$i] = $this->db->quoteIdentifier($t[$i]); + $this->options['final_table'] = implode('.', $t); + } + $this->options['final_usernamecol'] = $this->db->quoteIdentifier($this->options['usernamecol']); + $this->options['final_passwordcol'] = $this->db->quoteIdentifier($this->options['passwordcol']); + } else { + $this->options['final_table'] = $this->options['table']; + $this->options['final_usernamecol'] = $this->options['usernamecol']; + $this->options['final_passwordcol'] = $this->options['passwordcol']; + } + return true; + } + + // }}} + // {{{ query() + + /** + * Prepare query to the database + * + * This function checks if we have already opened a connection to + * the database. If that's not the case, a new connection is opened. + * After that the query is passed to the database. + * + * @access public + * @param string Query string + * @return mixed a DB_result object or DB_OK on success, a DB + * or PEAR error on failure + */ + function query($query) + { + $err = $this->_prepare(); + if ($err !== true) { + return $err; + } + return $this->db->query($query); + } + + // }}} + // {{{ _setDefaults() + + /** + * Set some default options + * + * @access private + * @return void + */ + function _setDefaults() + { + $this->options['table'] = 'auth'; + $this->options['usernamecol'] = 'username'; + $this->options['passwordcol'] = 'password'; + $this->options['dsn'] = ''; + $this->options['db_fields'] = ''; + $this->options['cryptType'] = 'md5'; + $this->options['db_options'] = array(); + $this->options['db_where'] = ''; + $this->options['auto_quote'] = true; + } + + // }}} + // {{{ _parseOptions() + + /** + * Parse options passed to the container class + * + * @access private + * @param array + */ + function _parseOptions($array) + { + foreach ($array as $key => $value) { + if (isset($this->options[$key])) { + $this->options[$key] = $value; + } + } + } + + // }}} + // {{{ _quoteDBFields() + + /** + * Quote the db_fields option to avoid the possibility of SQL injection. + * + * @access private + * @return string A properly quoted string that can be concatenated into a + * SELECT clause. + */ + function _quoteDBFields() + { + if (isset($this->options['db_fields'])) { + if (is_array($this->options['db_fields'])) { + if ($this->options['auto_quote']) { + $fields = array(); + foreach ($this->options['db_fields'] as $field) { + $fields[] = $this->db->quoteIdentifier($field); + } + return implode(', ', $fields); + } else { + return implode(', ', $this->options['db_fields']); + } + } else { + if (strlen($this->options['db_fields']) > 0) { + if ($this->options['auto_quote']) { + return $this->db->quoteIdentifier($this->options['db_fields']); + } else { + return $this->options['db_fields']; + } + } + } + } + + return ''; + } + + // }}} + // {{{ fetchData() + + /** + * Get user information from database + * + * This function uses the given username to fetch + * the corresponding login data from the database + * table. If an account that matches the passed username + * and password is found, the function returns true. + * Otherwise it returns false. + * + * @param string Username + * @param string Password + * @param boolean If true password is secured using a md5 hash + * the frontend and auth are responsible for making sure the container supports + * challenge response password authentication + * @return mixed Error object or boolean + */ + function fetchData($username, $password, $isChallengeResponse=false) + { + $this->log('Auth_Container_DB::fetchData() called.', AUTH_LOG_DEBUG); + // Prepare for a database query + $err = $this->_prepare(); + if ($err !== true) { + return PEAR::raiseError($err->getMessage(), $err->getCode()); + } + + // Find if db_fields contains a *, if so assume all columns are selected + if (is_string($this->options['db_fields']) + && strstr($this->options['db_fields'], '*')) { + $sql_from = "*"; + } else { + $sql_from = $this->options['final_usernamecol']. + ", ".$this->options['final_passwordcol']; + + if (strlen($fields = $this->_quoteDBFields()) > 0) { + $sql_from .= ', '.$fields; + } + } + + $query = "SELECT ".$sql_from. + " FROM ".$this->options['final_table']. + " WHERE ".$this->options['final_usernamecol']." = ".$this->db->quoteSmart($username); + + // check if there is an optional parameter db_where + if ($this->options['db_where'] != '') { + // there is one, so add it to the query + $query .= " AND ".$this->options['db_where']; + } + + $this->log('Running SQL against DB: '.$query, AUTH_LOG_DEBUG); + + $res = $this->db->getRow($query, null, DB_FETCHMODE_ASSOC); + + if (DB::isError($res)) { + return PEAR::raiseError($res->getMessage(), $res->getCode()); + } + + if (!is_array($res)) { + $this->activeUser = ''; + return false; + } + + // Perform trimming here before the hashihg + $password = trim($password, "\r\n"); + $res[$this->options['passwordcol']] = trim($res[$this->options['passwordcol']], "\r\n"); + + // If using Challenge Response md5 the pass with the secret + if ($isChallengeResponse) { + $res[$this->options['passwordcol']] = md5($res[$this->options['passwordcol']] + .$this->_auth_obj->session['loginchallenege']); + + // UGLY cannot avoid without modifying verifyPassword + if ($this->options['cryptType'] == 'md5') { + $res[$this->options['passwordcol']] = md5($res[$this->options['passwordcol']]); + } + + //print " Hashed Password [{$res[$this->options['passwordcol']]}]
    \n"; + } + + if ($this->verifyPassword($password, + $res[$this->options['passwordcol']], + $this->options['cryptType'])) { + // Store additional field values in the session + foreach ($res as $key => $value) { + if ($key == $this->options['passwordcol'] || + $key == $this->options['usernamecol']) { + continue; + } + + $this->log('Storing additional field: '.$key, AUTH_LOG_DEBUG); + + // Use reference to the auth object if exists + // This is because the auth session variable can change so a + // static call to setAuthData does not make sence + $this->_auth_obj->setAuthData($key, $value); + } + return true; + } + $this->activeUser = $res[$this->options['usernamecol']]; + return false; + } + + // }}} + // {{{ listUsers() + + /** + * Returns a list of users from the container + * + * @return mixed + * @access public + */ + function listUsers() + { + $this->log('Auth_Container_DB::listUsers() called.', AUTH_LOG_DEBUG); + $err = $this->_prepare(); + if ($err !== true) { + return PEAR::raiseError($err->getMessage(), $err->getCode()); + } + + $retVal = array(); + + // Find if db_fields contains a *, if so assume all col are selected + if ( is_string($this->options['db_fields']) + && strstr($this->options['db_fields'], '*')) { + $sql_from = "*"; + } else { + $sql_from = $this->options['final_usernamecol']. + ", ".$this->options['final_passwordcol']; + + if (strlen($fields = $this->_quoteDBFields()) > 0) { + $sql_from .= ', '.$fields; + } + } + + $query = sprintf("SELECT %s FROM %s", + $sql_from, + $this->options['final_table'] + ); + + // check if there is an optional parameter db_where + if ($this->options['db_where'] != '') { + // there is one, so add it to the query + $query .= " WHERE ".$this->options['db_where']; + } + + $this->log('Running SQL against DB: '.$query, AUTH_LOG_DEBUG); + + $res = $this->db->getAll($query, null, DB_FETCHMODE_ASSOC); + + if (DB::isError($res)) { + return PEAR::raiseError($res->getMessage(), $res->getCode()); + } else { + foreach ($res as $user) { + $user['username'] = $user[$this->options['usernamecol']]; + $retVal[] = $user; + } + } + $this->log('Found '.count($retVal).' users.', AUTH_LOG_DEBUG); + return $retVal; + } + + // }}} + // {{{ addUser() + + /** + * Add user to the storage container + * + * @access public + * @param string Username + * @param string Password + * @param mixed Additional information that are stored in the DB + * + * @return mixed True on success, otherwise error object + */ + function addUser($username, $password, $additional = "") + { + $this->log('Auth_Container_DB::addUser() called.', AUTH_LOG_DEBUG); + $err = $this->_prepare(); + if ($err !== true) { + return PEAR::raiseError($err->getMessage(), $err->getCode()); + } + + if ( isset($this->options['cryptType']) + && $this->options['cryptType'] == 'none') { + $cryptFunction = 'strval'; + } elseif ( isset($this->options['cryptType']) + && function_exists($this->options['cryptType'])) { + $cryptFunction = $this->options['cryptType']; + } else { + $cryptFunction = 'md5'; + } + + $password = $cryptFunction($password); + + $additional_key = ''; + $additional_value = ''; + + if (is_array($additional)) { + foreach ($additional as $key => $value) { + if ($this->options['auto_quote']) { + $additional_key .= ', ' . $this->db->quoteIdentifier($key); + } else { + $additional_key .= ', ' . $key; + } + $additional_value .= ", " . $this->db->quoteSmart($value); + } + } + + $query = sprintf("INSERT INTO %s (%s, %s%s) VALUES (%s, %s%s)", + $this->options['final_table'], + $this->options['final_usernamecol'], + $this->options['final_passwordcol'], + $additional_key, + $this->db->quoteSmart($username), + $this->db->quoteSmart($password), + $additional_value + ); + + $this->log('Running SQL against DB: '.$query, AUTH_LOG_DEBUG); + + $res = $this->query($query); + + if (DB::isError($res)) { + return PEAR::raiseError($res->getMessage(), $res->getCode()); + } else { + return true; + } + } + + // }}} + // {{{ removeUser() + + /** + * Remove user from the storage container + * + * @access public + * @param string Username + * + * @return mixed True on success, otherwise error object + */ + function removeUser($username) + { + $this->log('Auth_Container_DB::removeUser() called.', AUTH_LOG_DEBUG); + + $err = $this->_prepare(); + if ($err !== true) { + return PEAR::raiseError($err->getMessage(), $err->getCode()); + } + + // check if there is an optional parameter db_where + if ($this->options['db_where'] != '') { + // there is one, so add it to the query + $where = " AND ".$this->options['db_where']; + } else { + $where = ''; + } + + $query = sprintf("DELETE FROM %s WHERE %s = %s %s", + $this->options['final_table'], + $this->options['final_usernamecol'], + $this->db->quoteSmart($username), + $where + ); + + $this->log('Running SQL against DB: '.$query, AUTH_LOG_DEBUG); + + $res = $this->query($query); + + if (DB::isError($res)) { + return PEAR::raiseError($res->getMessage(), $res->getCode()); + } else { + return true; + } + } + + // }}} + // {{{ changePassword() + + /** + * Change password for user in the storage container + * + * @param string Username + * @param string The new password (plain text) + */ + function changePassword($username, $password) + { + $this->log('Auth_Container_DB::changePassword() called.', AUTH_LOG_DEBUG); + $err = $this->_prepare(); + if ($err !== true) { + return PEAR::raiseError($err->getMessage(), $err->getCode()); + } + + if ( isset($this->options['cryptType']) + && $this->options['cryptType'] == 'none') { + $cryptFunction = 'strval'; + } elseif ( isset($this->options['cryptType']) + && function_exists($this->options['cryptType'])) { + $cryptFunction = $this->options['cryptType']; + } else { + $cryptFunction = 'md5'; + } + + $password = $cryptFunction($password); + + // check if there is an optional parameter db_where + if ($this->options['db_where'] != '') { + // there is one, so add it to the query + $where = " AND ".$this->options['db_where']; + } else { + $where = ''; + } + + $query = sprintf("UPDATE %s SET %s = %s WHERE %s = %s %s", + $this->options['final_table'], + $this->options['final_passwordcol'], + $this->db->quoteSmart($password), + $this->options['final_usernamecol'], + $this->db->quoteSmart($username), + $where + ); + + $this->log('Running SQL against DB: '.$query, AUTH_LOG_DEBUG); + + $res = $this->query($query); + + if (DB::isError($res)) { + return PEAR::raiseError($res->getMessage(), $res->getCode()); + } else { + return true; + } + } + + // }}} + // {{{ supportsChallengeResponse() + + /** + * Determine if this container supports + * password authentication with challenge response + * + * @return bool + * @access public + */ + function supportsChallengeResponse() + { + return in_array($this->options['cryptType'], array('md5', 'none', '')); + } + + // }}} + // {{{ getCryptType() + + /** + * Returns the selected crypt type for this container + */ + function getCryptType() + { + return($this->options['cryptType']); + } + + // }}} + +} +?> diff --git a/includes/pear/Auth/Container/DBLite.php b/includes/pear/Auth/Container/DBLite.php new file mode 100644 index 0000000..2996108 --- /dev/null +++ b/includes/pear/Auth/Container/DBLite.php @@ -0,0 +1,320 @@ + + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version CVS: $Id: DBLite.php 256753 2008-04-04 07:57:02Z aashley $ + * @link http://pear.php.net/package/Auth + * @since File available since Release 1.3.0 + */ + +/** + * Include Auth_Container base class + */ +require_once 'Auth/Container.php'; +/** + * Include PEAR DB package + */ +require_once 'DB.php'; + +/** + * A lighter storage driver for fetching login data from a database + * + * This driver is derived from the DB storage container but + * with the user manipulation function removed for smaller file size + * by the PEAR DB abstraction layer to fetch login data. + * + * @category Authentication + * @package Auth + * @author Martin Jansen + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version Release: @package_version@ File: $Revision: 256753 $ + * @link http://pear.php.net/package/Auth + * @since Class available since Release 1.3.0 + */ +class Auth_Container_DBLite extends Auth_Container +{ + + // {{{ properties + + /** + * Additional options for the storage container + * @var array + */ + var $options = array(); + + /** + * DB object + * @var object + */ + var $db = null; + var $dsn = ''; + + /** + * User that is currently selected from the DB. + * @var string + */ + var $activeUser = ''; + + // }}} + // {{{ Auth_Container_DBLite() [constructor] + + /** + * Constructor of the container class + * + * Initate connection to the database via PEAR::DB + * + * @param string Connection data or DB object + * @return object Returns an error object if something went wrong + */ + function Auth_Container_DBLite($dsn) + { + $this->options['table'] = 'auth'; + $this->options['usernamecol'] = 'username'; + $this->options['passwordcol'] = 'password'; + $this->options['dsn'] = ''; + $this->options['db_fields'] = ''; + $this->options['cryptType'] = 'md5'; + $this->options['db_options'] = array(); + $this->options['db_where'] = ''; + $this->options['auto_quote'] = true; + + if (is_array($dsn)) { + $this->_parseOptions($dsn); + if (empty($this->options['dsn'])) { + PEAR::raiseError('No connection parameters specified!'); + } + } else { + $this->options['dsn'] = $dsn; + } + } + + // }}} + // {{{ _connect() + + /** + * Connect to database by using the given DSN string + * + * @access private + * @param string DSN string + * @return mixed Object on error, otherwise bool + */ + function _connect(&$dsn) + { + $this->log('Auth_Container_DBLite::_connect() called.', AUTH_LOG_DEBUG); + if (is_string($dsn) || is_array($dsn)) { + $this->db =& DB::connect($dsn, $this->options['db_options']); + } elseif (is_subclass_of($dsn, "db_common")) { + $this->db =& $dsn; + } else { + return PEAR::raiseError("Invalid dsn or db object given"); + } + + if (DB::isError($this->db) || PEAR::isError($this->db)) { + return PEAR::raiseError($this->db->getMessage(), $this->db->getCode()); + } else { + return true; + } + } + + // }}} + // {{{ _prepare() + + /** + * Prepare database connection + * + * This function checks if we have already opened a connection to + * the database. If that's not the case, a new connection is opened. + * + * @access private + * @return mixed True or a DB error object. + */ + function _prepare() + { + if (!DB::isConnection($this->db)) { + $res = $this->_connect($this->options['dsn']); + if (DB::isError($res) || PEAR::isError($res)) { + return $res; + } + } + if ($this->options['auto_quote'] && $this->db->dsn['phptype'] != 'sqlite') { + if (strpos('.', $this->options['table']) === false) { + $this->options['final_table'] = $this->db->quoteIdentifier($this->options['table']); + } else { + $t = explode('.', $this->options['table']); + for ($i = 0, $count = count($t); $i < $count; $i++) + $t[$i] = $this->db->quoteIdentifier($t[$i]); + $this->options['final_table'] = implode('.', $t); + } + $this->options['final_usernamecol'] = $this->db->quoteIdentifier($this->options['usernamecol']); + $this->options['final_passwordcol'] = $this->db->quoteIdentifier($this->options['passwordcol']); + } else { + $this->options['final_table'] = $this->options['table']; + $this->options['final_usernamecol'] = $this->options['usernamecol']; + $this->options['final_passwordcol'] = $this->options['passwordcol']; + } + return true; + } + + // }}} + // {{{ _parseOptions() + + /** + * Parse options passed to the container class + * + * @access private + * @param array + */ + function _parseOptions($array) + { + foreach ($array as $key => $value) { + if (isset($this->options[$key])) { + $this->options[$key] = $value; + } + } + } + + // }}} + // {{{ _quoteDBFields() + + /** + * Quote the db_fields option to avoid the possibility of SQL injection. + * + * @access private + * @return string A properly quoted string that can be concatenated into a + * SELECT clause. + */ + function _quoteDBFields() + { + if (isset($this->options['db_fields'])) { + if (is_array($this->options['db_fields'])) { + if ($this->options['auto_quote']) { + $fields = array(); + foreach ($this->options['db_fields'] as $field) { + $fields[] = $this->db->quoteIdentifier($field); + } + return implode(', ', $fields); + } else { + return implode(', ', $this->options['db_fields']); + } + } else { + if (strlen($this->options['db_fields']) > 0) { + if ($this->options['auto_quote']) { + return $this->db->quoteIdentifier($this->options['db_fields']); + } else { + $this->options['db_fields']; + } + } + } + } + + return ''; + } + + // }}} + // {{{ fetchData() + + /** + * Get user information from database + * + * This function uses the given username to fetch + * the corresponding login data from the database + * table. If an account that matches the passed username + * and password is found, the function returns true. + * Otherwise it returns false. + * + * @param string Username + * @param string Password + * @return mixed Error object or boolean + */ + function fetchData($username, $password) + { + $this->log('Auth_Container_DBLite::fetchData() called.', AUTH_LOG_DEBUG); + // Prepare for a database query + $err = $this->_prepare(); + if ($err !== true) { + return PEAR::raiseError($err->getMessage(), $err->getCode()); + } + + // Find if db_fields contains a *, if so assume all col are selected + if (is_string($this->options['db_fields']) + && strstr($this->options['db_fields'], '*')) { + $sql_from = "*"; + } else { + $sql_from = $this->options['final_usernamecol']. + ", ".$this->options['final_passwordcol']; + + if (strlen($fields = $this->_quoteDBFields()) > 0) { + $sql_from .= ', '.$fields; + } + } + + $query = "SELECT ".$sql_from. + " FROM ".$this->options['final_table']. + " WHERE ".$this->options['final_usernamecol']." = ".$this->db->quoteSmart($username); + + // check if there is an optional parameter db_where + if ($this->options['db_where'] != '') { + // there is one, so add it to the query + $query .= " AND ".$this->options['db_where']; + } + + $this->log('Running SQL against DB: '.$query, AUTH_LOG_DEBUG); + + $res = $this->db->getRow($query, null, DB_FETCHMODE_ASSOC); + + if (DB::isError($res)) { + return PEAR::raiseError($res->getMessage(), $res->getCode()); + } + if (!is_array($res)) { + $this->activeUser = ''; + return false; + } + if ($this->verifyPassword(trim($password, "\r\n"), + trim($res[$this->options['passwordcol']], "\r\n"), + $this->options['cryptType'])) { + // Store additional field values in the session + foreach ($res as $key => $value) { + if ($key == $this->options['passwordcol'] || + $key == $this->options['usernamecol']) { + continue; + } + + $this->log('Storing additional field: '.$key, AUTH_LOG_DEBUG); + + // Use reference to the auth object if exists + // This is because the auth session variable can change so a static call to setAuthData does not make sence + if (is_object($this->_auth_obj)) { + $this->_auth_obj->setAuthData($key, $value); + } else { + Auth::setAuthData($key, $value); + } + } + $this->activeUser = $res[$this->options['usernamecol']]; + return true; + } + $this->activeUser = $res[$this->options['usernamecol']]; + return false; + } + + // }}} + +} +?> diff --git a/includes/pear/Auth/Container/File.php b/includes/pear/Auth/Container/File.php new file mode 100644 index 0000000..2b8274c --- /dev/null +++ b/includes/pear/Auth/Container/File.php @@ -0,0 +1,314 @@ + + * @author Martin Jansen + * @author Mika Tuupola + * @author Michael Wallner + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version CVS: $Id: File.php 237449 2007-06-12 03:11:27Z aashley $ + * @link http://pear.php.net/package/Auth + */ + +/** + * Include PEAR File_Passwd package + */ +require_once "File/Passwd.php"; +/** + * Include Auth_Container base class + */ +require_once "Auth/Container.php"; +/** + * Include PEAR package for error handling + */ +require_once "PEAR.php"; + +/** + * Storage driver for fetching login data from an encrypted password file. + * + * This storage container can handle CVS pserver style passwd files. + * + * @category Authentication + * @package Auth + * @author Stefan Ekman + * @author Martin Jansen + * @author Mika Tuupola + * @author Michael Wallner + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version Release: @package_version@ File: $Revision: 237449 $ + * @link http://pear.php.net/package/Auth + */ +class Auth_Container_File extends Auth_Container +{ + + // {{{ properties + + /** + * Path to passwd file + * + * @var string + */ + var $pwfile = ''; + + /** + * Options for container + * + * @var array + */ + var $options = array(); + + // }}} + // {{{ Auth_Container_File() [constructor] + + /** + * Constructor of the container class + * + * @param string $filename path to passwd file + * @return object Auth_Container_File new Auth_Container_File object + */ + function Auth_Container_File($filename) { + $this->_setDefaults(); + + // Only file is a valid option here + if(is_array($filename)) { + $this->pwfile = $filename['file']; + $this->_parseOptions($filename); + } else { + $this->pwfile = $filename; + } + } + + // }}} + // {{{ fetchData() + + /** + * Authenticate an user + * + * @param string username + * @param string password + * @return mixed boolean|PEAR_Error + */ + function fetchData($user, $pass) + { + $this->log('Auth_Container_File::fetchData() called.', AUTH_LOG_DEBUG); + return File_Passwd::staticAuth($this->options['type'], $this->pwfile, $user, $pass); + } + + // }}} + // {{{ listUsers() + + /** + * List all available users + * + * @return array + */ + function listUsers() + { + $this->log('Auth_Container_File::listUsers() called.', AUTH_LOG_DEBUG); + + $pw_obj = &$this->_load(); + if (PEAR::isError($pw_obj)) { + return array(); + } + + $users = $pw_obj->listUser(); + if (!is_array($users)) { + return array(); + } + + foreach ($users as $key => $value) { + $retVal[] = array("username" => $key, + "password" => $value['passwd'], + "cvsuser" => $value['system']); + } + + $this->log('Found '.count($retVal).' users.', AUTH_LOG_DEBUG); + + return $retVal; + } + + // }}} + // {{{ addUser() + + /** + * Add a new user to the storage container + * + * @param string username + * @param string password + * @param mixed Additional parameters to File_Password_*::addUser() + * + * @return boolean + */ + function addUser($user, $pass, $additional='') + { + $this->log('Auth_Container_File::addUser() called.', AUTH_LOG_DEBUG); + $params = array($user, $pass); + if (is_array($additional)) { + foreach ($additional as $item) { + $params[] = $item; + } + } else { + $params[] = $additional; + } + + $pw_obj = &$this->_load(); + if (PEAR::isError($pw_obj)) { + return false; + } + + $res = call_user_func_array(array(&$pw_obj, 'addUser'), $params); + if (PEAR::isError($res)) { + return false; + } + + $res = $pw_obj->save(); + if (PEAR::isError($res)) { + return false; + } + + return true; + } + + // }}} + // {{{ removeUser() + + /** + * Remove user from the storage container + * + * @param string Username + * @return boolean + */ + function removeUser($user) + { + $this->log('Auth_Container_File::removeUser() called.', AUTH_LOG_DEBUG); + $pw_obj = &$this->_load(); + if (PEAR::isError($pw_obj)) { + return false; + } + + $res = $pw_obj->delUser($user); + if (PEAR::isError($res)) { + return false; + } + + $res = $pw_obj->save(); + if (PEAR::isError($res)) { + return false; + } + + return true; + } + + // }}} + // {{{ changePassword() + + /** + * Change password for user in the storage container + * + * @param string Username + * @param string The new password + */ + function changePassword($username, $password) + { + $this->log('Auth_Container_File::changePassword() called.', AUTH_LOG_DEBUG); + $pw_obj = &$this->_load(); + if (PEAR::isError($pw_obj)) { + return false; + } + + $res = $pw_obj->changePasswd($username, $password); + if (PEAR::isError($res)) { + return false; + } + + $res = $pw_obj->save(); + if (PEAR::isError($res)) { + return false; + } + + return true; + } + + // }}} + // {{{ _load() + + /** + * Load and initialize the File_Passwd object + * + * @return object File_Passwd_Cvs|PEAR_Error + */ + function &_load() + { + static $pw_obj; + + if (!isset($pw_obj)) { + $this->log('Instanciating File_Password object of type '.$this->options['type'], AUTH_LOG_DEBUG); + $pw_obj = File_Passwd::factory($this->options['type']); + if (PEAR::isError($pw_obj)) { + return $pw_obj; + } + + $pw_obj->setFile($this->pwfile); + + $res = $pw_obj->load(); + if (PEAR::isError($res)) { + return $res; + } + } + + return $pw_obj; + } + + // }}} + // {{{ _setDefaults() + + /** + * Set some default options + * + * @access private + * @return void + */ + function _setDefaults() + { + $this->options['type'] = 'Cvs'; + } + + // }}} + // {{{ _parseOptions() + + /** + * Parse options passed to the container class + * + * @access private + * @param array + */ + function _parseOptions($array) + { + foreach ($array as $key => $value) { + if (isset($this->options[$key])) { + $this->options[$key] = $value; + } + } + } + + // }}} + +} +?> diff --git a/includes/pear/Auth/Container/IMAP.php b/includes/pear/Auth/Container/IMAP.php new file mode 100644 index 0000000..2768483 --- /dev/null +++ b/includes/pear/Auth/Container/IMAP.php @@ -0,0 +1,210 @@ + + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version CVS: $Id: IMAP.php 237449 2007-06-12 03:11:27Z aashley $ + * @link http://pear.php.net/package/Auth + * @since File available since Release 1.2.0 + */ + +/** + * Include Auth_Container base class + */ +require_once "Auth/Container.php"; + +/** + * Include PEAR class for error handling + */ +require_once "PEAR.php"; + +/** + * Storage driver for fetching login data from an IMAP server + * + * This class is based on LDAP containers, but it very simple. + * By default it connects to localhost:143 + * The constructor will first check if the host:port combination is + * actually reachable. This behaviour can be disabled. + * It then tries to create an IMAP stream (without opening a mailbox) + * If you wish to pass extended options to the connections, you may + * do so by specifying protocol options. + * + * To use this storage containers, you have to use the + * following syntax: + * + * 'mail.example.com', + * 'port' => 143, + * ); + * $myAuth = new Auth('IMAP', $params); + * ... + * + * By default we connect without any protocol options set. However, some + * servers require you to connect with the notls or norsh options set. + * To do this you need to add the following value to the params array: + * 'baseDSN' => '/imap/notls/norsh' + * + * To connect to an SSL IMAP server: + * 'baseDSN' => '/imap/ssl' + * + * To connect to an SSL IMAP server with a self-signed certificate: + * 'baseDSN' => '/imap/ssl/novalidate-cert' + * + * Further options may be available and can be found on the php site at + * http://www.php.net/manual/function.imap-open.php + * + * @category Authentication + * @package Auth + * @author Jeroen Houben + * @author Cipriano Groenendal + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version Release: @package_version@ File: $Revision: 237449 $ + * @link http://pear.php.net/package/Auth + * @since Class available since Release 1.2.0 + */ +class Auth_Container_IMAP extends Auth_Container +{ + + // {{{ properties + + /** + * Options for the class + * @var array + */ + var $options = array(); + + // }}} + // {{{ Auth_Container_IMAP() [constructor] + + /** + * Constructor of the container class + * + * @param $params associative array with host, port, baseDSN, checkServer + * and userattr key + * @return object Returns an error object if something went wrong + * @todo Use PEAR Net_IMAP if IMAP extension not loaded + */ + function Auth_Container_IMAP($params) + { + if (!extension_loaded('imap')) { + return PEAR::raiseError('Cannot use IMAP authentication, ' + .'IMAP extension not loaded!', 41, PEAR_ERROR_DIE); + } + $this->_setDefaults(); + + // set parameters (if any) + if (is_array($params)) { + $this->_parseOptions($params); + } + + if ($this->options['checkServer']) { + $this->_checkServer($this->options['timeout']); + } + return true; + } + + // }}} + // {{{ _setDefaults() + + /** + * Set some default options + * + * @access private + */ + function _setDefaults() + { + $this->options['host'] = 'localhost'; + $this->options['port'] = 143; + $this->options['baseDSN'] = ''; + $this->options['checkServer'] = true; + $this->options['timeout'] = 20; + } + + // }}} + // {{{ _checkServer() + + /** + * Check if the given server and port are reachable + * + * @access private + */ + function _checkServer() { + $this->log('Auth_Container_IMAP::_checkServer() called.', AUTH_LOG_DEBUG); + $fp = @fsockopen ($this->options['host'], $this->options['port'], + $errno, $errstr, $this->options['timeout']); + if (is_resource($fp)) { + @fclose($fp); + } else { + $message = "Error connecting to IMAP server " + . $this->options['host'] + . ":" . $this->options['port']; + return PEAR::raiseError($message, 41); + } + } + + // }}} + // {{{ _parseOptions() + + /** + * Parse options passed to the container class + * + * @access private + * @param array + */ + function _parseOptions($array) + { + foreach ($array as $key => $value) { + $this->options[$key] = $value; + } + } + + // }}} + // {{{ fetchData() + + /** + * Try to open a IMAP stream using $username / $password + * + * @param string Username + * @param string Password + * @return boolean + */ + function fetchData($username, $password) + { + $this->log('Auth_Container_IMAP::fetchData() called.', AUTH_LOG_DEBUG); + $dsn = '{'.$this->options['host'].':'.$this->options['port'].$this->options['baseDSN'].'}'; + $conn = @imap_open ($dsn, $username, $password, OP_HALFOPEN); + if (is_resource($conn)) { + $this->log('Successfully connected to IMAP server.', AUTH_LOG_DEBUG); + $this->activeUser = $username; + @imap_close($conn); + return true; + } else { + $this->log('Connection to IMAP server failed.', AUTH_LOG_DEBUG); + $this->activeUser = ''; + return false; + } + } + + // }}} + +} +?> diff --git a/includes/pear/Auth/Container/KADM5.php b/includes/pear/Auth/Container/KADM5.php new file mode 100644 index 0000000..2853580 --- /dev/null +++ b/includes/pear/Auth/Container/KADM5.php @@ -0,0 +1,171 @@ + + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version CVS: $Id: KADM5.php 237449 2007-06-12 03:11:27Z aashley $ + * @link http://pear.php.net/package/Auth + * @since File available since Release 1.4.0 + */ + +/** + * Include Auth_Container base class + */ +require_once 'Auth/Container.php'; +/** + * Include PEAR for error handling + */ +require_once 'PEAR.php'; + +/** + * Storage driver for Authentication on a Kerberos V server. + * + * Available options: + * hostname: The hostname of the kerberos server + * realm: The Kerberos V realm + * timeout: The timeout for checking the server + * checkServer: Set to true to check if the server is running when + * constructing the object + * + * @category Authentication + * @package Auth + * @author Andrew Teixeira + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version Release: @package_version@ File: $Revision: 237449 $ + * @link http://pear.php.net/package/Auth + * @since Class available since Release 1.4.0 + */ +class Auth_Container_KADM5 extends Auth_Container { + + // {{{ properties + + /** + * Options for the class + * @var string + */ + var $options = array(); + + // }}} + // {{{ Auth_Container_KADM5() + + /** + * Constructor of the container class + * + * $options can have these keys: + * 'hostname' The hostname of the kerberos server + * 'realm' The Kerberos V realm + * 'timeout' The timeout for checking the server + * 'checkServer' Set to true to check if the server is running when + * constructing the object + * + * @param $options associative array + * @return object Returns an error object if something went wrong + */ + function Auth_Container_KADM5($options) { + if (!extension_loaded('kadm5')) { + return PEAR::raiseError("Cannot use Kerberos V authentication, KADM5 extension not loaded!", 41, PEAR_ERROR_DIE); + } + + $this->_setDefaults(); + + if (isset($options['hostname'])) { + $this->options['hostname'] = $options['hostname']; + } + if (isset($options['realm'])) { + $this->options['realm'] = $options['realm']; + } + if (isset($options['timeout'])) { + $this->options['timeout'] = $options['timeout']; + } + if (isset($options['checkServer'])) { + $this->options['checkServer'] = $options['checkServer']; + } + + if ($this->options['checkServer']) { + $this->_checkServer(); + } + } + + // }}} + // {{{ fetchData() + + /** + * Try to login to the KADM5 server + * + * @param string Username + * @param string Password + * @return boolean + */ + function fetchData($username, $password) { + $this->log('Auth_Container_KADM5::fetchData() called.', AUTH_LOG_DEBUG); + if ( ($username == NULL) || ($password == NULL) ) { + return false; + } + + $server = $this->options['hostname']; + $realm = $this->options['realm']; + $check = @kadm5_init_with_password($server, $realm, $username, $password); + + if ($check == false) { + return false; + } else { + return true; + } + } + + // }}} + // {{{ _setDefaults() + + /** + * Set some default options + * + * @access private + */ + function _setDefaults() { + $this->options['hostname'] = 'localhost'; + $this->options['realm'] = NULL; + $this->options['timeout'] = 10; + $this->options['checkServer'] = false; + } + + // }}} + // {{{ _checkServer() + + /** + * Check if the given server and port are reachable + * + * @access private + */ + function _checkServer() { + $fp = @fsockopen ($this->options['hostname'], 88, $errno, $errstr, $this->options['timeout']); + if (is_resource($fp)) { + @fclose($fp); + } else { + $message = "Error connecting to Kerberos V server " + .$this->options['hostname'].":".$this->options['port']; + return PEAR::raiseError($message, 41, PEAR_ERROR_DIE); + } + } + + // }}} + +} + +?> diff --git a/includes/pear/Auth/Container/LDAP.php b/includes/pear/Auth/Container/LDAP.php new file mode 100644 index 0000000..eaf35cf --- /dev/null +++ b/includes/pear/Auth/Container/LDAP.php @@ -0,0 +1,766 @@ + + * @author Adam Ashley + * @author Hugues Peeters + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version CVS: $Id: LDAP.php 237449 2007-06-12 03:11:27Z aashley $ + * @link http://pear.php.net/package/Auth + */ + +/** + * Include Auth_Container base class + */ +require_once "Auth/Container.php"; +/** + * Include PEAR package for error handling + */ +require_once "PEAR.php"; + +/** + * Storage driver for fetching login data from LDAP + * + * This class is heavily based on the DB and File containers. By default it + * connects to localhost:389 and searches for uid=$username with the scope + * "sub". If no search base is specified, it will try to determine it via + * the namingContexts attribute. It takes its parameters in a hash, connects + * to the ldap server, binds anonymously, searches for the user, and tries + * to bind as the user with the supplied password. When a group was set, it + * will look for group membership of the authenticated user. If all goes + * well the authentication was successful. + * + * Parameters: + * + * host: localhost (default), ldap.netsols.de or 127.0.0.1 + * port: 389 (default) or 636 or whereever your server runs + * url: ldap://localhost:389/ + * useful for ldaps://, works only with openldap2 ? + * it will be preferred over host and port + * version: LDAP version to use, ususally 2 (default) or 3, + * must be an integer! + * referrals: If set, determines whether the LDAP library automatically + * follows referrals returned by LDAP servers or not. Possible + * values are true (default) or false. + * binddn: If set, searching for user will be done after binding + * as this user, if not set the bind will be anonymous. + * This is reported to make the container work with MS + * Active Directory, but should work with any server that + * is configured this way. + * This has to be a complete dn for now (basedn and + * userdn will not be appended). + * bindpw: The password to use for binding with binddn + * basedn: the base dn of your server + * userdn: gets prepended to basedn when searching for user + * userscope: Scope for user searching: one, sub (default), or base + * userattr: the user attribute to search for (default: uid) + * userfilter: filter that will be added to the search filter + * this way: (&(userattr=username)(userfilter)) + * default: (objectClass=posixAccount) + * attributes: array of additional attributes to fetch from entry. + * these will added to auth data and can be retrieved via + * Auth::getAuthData(). An empty array will fetch all attributes, + * array('') will fetch no attributes at all (default) + * If you add 'dn' as a value to this array, the users DN that was + * used for binding will be added to auth data as well. + * attrformat: The returned format of the additional data defined in the + * 'attributes' option. Two formats are available. + * LDAP returns data formatted in a + * multidimensional array where each array starts with a + * 'count' element providing the number of attributes in the + * entry, or the number of values for attributes. When set + * to this format, the only way to retrieve data from the + * Auth object is by calling getAuthData('attributes'). + * AUTH returns data formatted in a + * structure more compliant with other Auth Containers, + * where each attribute element can be directly called by + * getAuthData() method from Auth. + * For compatibily with previous LDAP container versions, + * the default format is LDAP. + * groupdn: gets prepended to basedn when searching for group + * groupattr: the group attribute to search for (default: cn) + * groupfilter: filter that will be added to the search filter when + * searching for a group: + * (&(groupattr=group)(memberattr=username)(groupfilter)) + * default: (objectClass=groupOfUniqueNames) + * memberattr : the attribute of the group object where the user dn + * may be found (default: uniqueMember) + * memberisdn: whether the memberattr is the dn of the user (default) + * or the value of userattr (usually uid) + * group: the name of group to search for + * groupscope: Scope for group searching: one, sub (default), or base + * start_tls: enable/disable the use of START_TLS encrypted connection + * (default: false) + * debug: Enable/Disable debugging output (default: false) + * try_all: Whether to try all user accounts returned from the search + * or just the first one. (default: false) + * + * To use this storage container, you have to use the following syntax: + * + * 'localhost', + * 'port' => '389', + * 'version' => 3, + * 'basedn' => 'o=netsols,c=de', + * 'userattr' => 'uid' + * 'binddn' => 'cn=admin,o=netsols,c=de', + * 'bindpw' => 'password')); + * + * $a2 = new Auth('LDAP', array( + * 'url' => 'ldaps://ldap.netsols.de', + * 'basedn' => 'o=netsols,c=de', + * 'userscope' => 'one', + * 'userdn' => 'ou=People', + * 'groupdn' => 'ou=Groups', + * 'groupfilter' => '(objectClass=posixGroup)', + * 'memberattr' => 'memberUid', + * 'memberisdn' => false, + * 'group' => 'admin' + * )); + * + * $a3 = new Auth('LDAP', array( + * 'host' => 'ldap.netsols.de', + * 'port' => 389, + * 'version' => 3, + * 'referrals' => false, + * 'basedn' => 'dc=netsols,dc=de', + * 'binddn' => 'cn=Jan Wagner,cn=Users,dc=netsols,dc=de', + * 'bindpw' => 'password', + * 'userattr' => 'samAccountName', + * 'userfilter' => '(objectClass=user)', + * 'attributes' => array(''), + * 'group' => 'testing', + * 'groupattr' => 'samAccountName', + * 'groupfilter' => '(objectClass=group)', + * 'memberattr' => 'member', + * 'memberisdn' => true, + * 'groupdn' => 'cn=Users', + * 'groupscope' => 'one', + * 'debug' => true); + * + * The parameter values have to correspond + * to the ones for your LDAP server of course. + * + * When talking to a Microsoft ActiveDirectory server you have to + * use 'samaccountname' as the 'userattr' and follow special rules + * to translate the ActiveDirectory directory names into 'basedn'. + * The 'basedn' for the default 'Users' folder on an ActiveDirectory + * server for the ActiveDirectory Domain (which is not related to + * its DNS name) "win2000.example.org" would be: + * "CN=Users, DC=win2000, DC=example, DC=org' + * where every component of the domain name becomes a DC attribute + * of its own. If you want to use a custom users folder you have to + * replace "CN=Users" with a sequence of "OU" attributes that specify + * the path to your custom folder in reverse order. + * So the ActiveDirectory folder + * "win2000.example.org\Custom\Accounts" + * would become + * "OU=Accounts, OU=Custom, DC=win2000, DC=example, DC=org' + * + * It seems that binding anonymously to an Active Directory + * is not allowed, so you have to set binddn and bindpw for + * user searching. + * + * LDAP Referrals need to be set to false for AD to work sometimes. + * + * Example a3 shows a full blown and tested example for connection to + * Windows 2000 Active Directory with group mebership checking + * + * Note also that if you want an encrypted connection to an MS LDAP + * server, then, on your webserver, you must specify + * TLS_REQCERT never + * in /etc/ldap/ldap.conf or in the webserver user's ~/.ldaprc (which + * may or may not be read depending on your configuration). + * + * + * @category Authentication + * @package Auth + * @author Jan Wagner + * @author Adam Ashley + * @author Hugues Peeters + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version Release: @package_version@ File: $Revision: 237449 $ + * @link http://pear.php.net/package/Auth + */ +class Auth_Container_LDAP extends Auth_Container +{ + + // {{{ properties + + /** + * Options for the class + * @var array + */ + var $options = array(); + + /** + * Connection ID of LDAP Link + * @var string + */ + var $conn_id = false; + + // }}} + + // {{{ Auth_Container_LDAP() [constructor] + + /** + * Constructor of the container class + * + * @param $params, associative hash with host,port,basedn and userattr key + * @return object Returns an error object if something went wrong + */ + function Auth_Container_LDAP($params) + { + if (false === extension_loaded('ldap')) { + return PEAR::raiseError('Auth_Container_LDAP: LDAP Extension not loaded', + 41, PEAR_ERROR_DIE); + } + + $this->_setDefaults(); + + if (is_array($params)) { + $this->_parseOptions($params); + } + } + + // }}} + // {{{ _prepare() + + /** + * Prepare LDAP connection + * + * This function checks if we have already opened a connection to + * the LDAP server. If that's not the case, a new connection is opened. + * + * @access private + * @return mixed True or a PEAR error object. + */ + function _prepare() + { + if (!$this->_isValidLink()) { + $res = $this->_connect(); + if (PEAR::isError($res)) { + return $res; + } + } + return true; + } + + // }}} + // {{{ _connect() + + /** + * Connect to the LDAP server using the global options + * + * @access private + * @return object Returns a PEAR error object if an error occurs. + */ + function _connect() + { + $this->log('Auth_Container_LDAP::_connect() called.', AUTH_LOG_DEBUG); + // connect + if (isset($this->options['url']) && $this->options['url'] != '') { + $this->log('Connecting with URL', AUTH_LOG_DEBUG); + $conn_params = array($this->options['url']); + } else { + $this->log('Connecting with host:port', AUTH_LOG_DEBUG); + $conn_params = array($this->options['host'], $this->options['port']); + } + + if (($this->conn_id = @call_user_func_array('ldap_connect', $conn_params)) === false) { + $this->log('Connection to server failed.', AUTH_LOG_DEBUG); + $this->log('LDAP ERROR: '.ldap_errno($this->conn_id).': '.ldap_error($this->conn_id), AUTH_LOG_DEBUG); + return PEAR::raiseError('Auth_Container_LDAP: Could not connect to server.', 41); + } + $this->log('Successfully connected to server', AUTH_LOG_DEBUG); + + // switch LDAP version + if (is_numeric($this->options['version']) && $this->options['version'] > 2) { + $this->log("Switching to LDAP version {$this->options['version']}", AUTH_LOG_DEBUG); + @ldap_set_option($this->conn_id, LDAP_OPT_PROTOCOL_VERSION, $this->options['version']); + + // start TLS if available + if (isset($this->options['start_tls']) && $this->options['start_tls']) { + $this->log("Starting TLS session", AUTH_LOG_DEBUG); + if (@ldap_start_tls($this->conn_id) === false) { + $this->log('Could not start TLS session', AUTH_LOG_DEBUG); + $this->log('LDAP ERROR: '.ldap_errno($this->conn_id).': '.ldap_error($this->conn_id), AUTH_LOG_DEBUG); + return PEAR::raiseError('Auth_Container_LDAP: Could not start tls.', 41); + } + } + } + + // switch LDAP referrals + if (is_bool($this->options['referrals'])) { + $this->log("Switching LDAP referrals to " . (($this->options['referrals']) ? 'true' : 'false'), AUTH_LOG_DEBUG); + if (@ldap_set_option($this->conn_id, LDAP_OPT_REFERRALS, $this->options['referrals']) === false) { + $this->log('Could not change LDAP referrals options', AUTH_LOG_DEBUG); + $this->log('LDAP ERROR: '.ldap_errno($this->conn_id).': '.ldap_error($this->conn_id), AUTH_LOG_DEBUG); + } + } + + // bind with credentials or anonymously + if (strlen($this->options['binddn']) && strlen($this->options['bindpw'])) { + $this->log('Binding with credentials', AUTH_LOG_DEBUG); + $bind_params = array($this->conn_id, $this->options['binddn'], $this->options['bindpw']); + } else { + $this->log('Binding anonymously', AUTH_LOG_DEBUG); + $bind_params = array($this->conn_id); + } + + // bind for searching + if ((@call_user_func_array('ldap_bind', $bind_params)) === false) { + $this->log('Bind failed', AUTH_LOG_DEBUG); + $this->log('LDAP ERROR: '.ldap_errno($this->conn_id).': '.ldap_error($this->conn_id), AUTH_LOG_DEBUG); + $this->_disconnect(); + return PEAR::raiseError("Auth_Container_LDAP: Could not bind to LDAP server.", 41); + } + $this->log('Binding was successful', AUTH_LOG_DEBUG); + + return true; + } + + // }}} + // {{{ _disconnect() + + /** + * Disconnects (unbinds) from ldap server + * + * @access private + */ + function _disconnect() + { + $this->log('Auth_Container_LDAP::_disconnect() called.', AUTH_LOG_DEBUG); + if ($this->_isValidLink()) { + $this->log('disconnecting from server'); + @ldap_unbind($this->conn_id); + } + } + + // }}} + // {{{ _getBaseDN() + + /** + * Tries to find Basedn via namingContext Attribute + * + * @access private + */ + function _getBaseDN() + { + $this->log('Auth_Container_LDAP::_getBaseDN() called.', AUTH_LOG_DEBUG); + $err = $this->_prepare(); + if ($err !== true) { + return PEAR::raiseError($err->getMessage(), $err->getCode()); + } + + if ($this->options['basedn'] == "" && $this->_isValidLink()) { + $this->log("basedn not set, searching via namingContexts.", AUTH_LOG_DEBUG); + + $result_id = @ldap_read($this->conn_id, "", "(objectclass=*)", array("namingContexts")); + + if (@ldap_count_entries($this->conn_id, $result_id) == 1) { + + $this->log("got result for namingContexts", AUTH_LOG_DEBUG); + + $entry_id = @ldap_first_entry($this->conn_id, $result_id); + $attrs = @ldap_get_attributes($this->conn_id, $entry_id); + $basedn = $attrs['namingContexts'][0]; + + if ($basedn != "") { + $this->log("result for namingContexts was $basedn", AUTH_LOG_DEBUG); + $this->options['basedn'] = $basedn; + } + } + @ldap_free_result($result_id); + } + + // if base ist still not set, raise error + if ($this->options['basedn'] == "") { + return PEAR::raiseError("Auth_Container_LDAP: LDAP search base not specified!", 41); + } + return true; + } + + // }}} + // {{{ _isValidLink() + + /** + * determines whether there is a valid ldap conenction or not + * + * @accessd private + * @return boolean + */ + function _isValidLink() + { + if (is_resource($this->conn_id)) { + if (get_resource_type($this->conn_id) == 'ldap link') { + return true; + } + } + return false; + } + + // }}} + // {{{ _setDefaults() + + /** + * Set some default options + * + * @access private + */ + function _setDefaults() + { + $this->options['url'] = ''; + $this->options['host'] = 'localhost'; + $this->options['port'] = '389'; + $this->options['version'] = 2; + $this->options['referrals'] = true; + $this->options['binddn'] = ''; + $this->options['bindpw'] = ''; + $this->options['basedn'] = ''; + $this->options['userdn'] = ''; + $this->options['userscope'] = 'sub'; + $this->options['userattr'] = 'uid'; + $this->options['userfilter'] = '(objectClass=posixAccount)'; + $this->options['attributes'] = array(''); // no attributes + $this->options['attrformat'] = 'AUTH'; // returns attribute like other Auth containers + $this->options['group'] = ''; + $this->options['groupdn'] = ''; + $this->options['groupscope'] = 'sub'; + $this->options['groupattr'] = 'cn'; + $this->options['groupfilter'] = '(objectClass=groupOfUniqueNames)'; + $this->options['memberattr'] = 'uniqueMember'; + $this->options['memberisdn'] = true; + $this->options['start_tls'] = false; + $this->options['debug'] = false; + $this->options['try_all'] = false; // Try all user ids returned not just the first one + } + + // }}} + // {{{ _parseOptions() + + /** + * Parse options passed to the container class + * + * @access private + * @param array + */ + function _parseOptions($array) + { + $array = $this->_setV12OptionsToV13($array); + + foreach ($array as $key => $value) { + if (array_key_exists($key, $this->options)) { + if ($key == 'attributes') { + if (is_array($value)) { + $this->options[$key] = $value; + } else { + $this->options[$key] = explode(',', $value); + } + } else { + $this->options[$key] = $value; + } + } + } + } + + // }}} + // {{{ _setV12OptionsToV13() + + /** + * Adapt deprecated options from Auth 1.2 LDAP to Auth 1.3 LDAP + * + * @author Hugues Peeters + * @access private + * @param array + * @return array + */ + function _setV12OptionsToV13($array) + { + if (isset($array['useroc'])) + $array['userfilter'] = "(objectClass=".$array['useroc'].")"; + if (isset($array['groupoc'])) + $array['groupfilter'] = "(objectClass=".$array['groupoc'].")"; + if (isset($array['scope'])) + $array['userscope'] = $array['scope']; + + return $array; + } + + // }}} + // {{{ _scope2function() + + /** + * Get search function for scope + * + * @param string scope + * @return string ldap search function + */ + function _scope2function($scope) + { + switch($scope) { + case 'one': + $function = 'ldap_list'; + break; + case 'base': + $function = 'ldap_read'; + break; + default: + $function = 'ldap_search'; + break; + } + return $function; + } + + // }}} + // {{{ fetchData() + + /** + * Fetch data from LDAP server + * + * Searches the LDAP server for the given username/password + * combination. Escapes all LDAP meta characters in username + * before performing the query. + * + * @param string Username + * @param string Password + * @return boolean + */ + function fetchData($username, $password) + { + $this->log('Auth_Container_LDAP::fetchData() called.', AUTH_LOG_DEBUG); + $err = $this->_prepare(); + if ($err !== true) { + return PEAR::raiseError($err->getMessage(), $err->getCode()); + } + + $err = $this->_getBaseDN(); + if ($err !== true) { + return PEAR::raiseError($err->getMessage(), $err->getCode()); + } + + // UTF8 Encode username for LDAPv3 + if (@ldap_get_option($this->conn_id, LDAP_OPT_PROTOCOL_VERSION, $ver) && $ver == 3) { + $this->log('UTF8 encoding username for LDAPv3', AUTH_LOG_DEBUG); + $username = utf8_encode($username); + } + + // make search filter + $filter = sprintf('(&(%s=%s)%s)', + $this->options['userattr'], + $this->_quoteFilterString($username), + $this->options['userfilter']); + + // make search base dn + $search_basedn = $this->options['userdn']; + if ($search_basedn != '' && substr($search_basedn, -1) != ',') { + $search_basedn .= ','; + } + $search_basedn .= $this->options['basedn']; + + // attributes + $searchAttributes = $this->options['attributes']; + + // make functions params array + $func_params = array($this->conn_id, $search_basedn, $filter, $searchAttributes); + + // search function to use + $func_name = $this->_scope2function($this->options['userscope']); + + $this->log("Searching with $func_name and filter $filter in $search_basedn", AUTH_LOG_DEBUG); + + // search + if (($result_id = @call_user_func_array($func_name, $func_params)) === false) { + $this->log('User not found', AUTH_LOG_DEBUG); + } elseif (@ldap_count_entries($this->conn_id, $result_id) >= 1) { // did we get some possible results? + + $this->log('User(s) found', AUTH_LOG_DEBUG); + + $first = true; + $entry_id = null; + + do { + + // then get the user dn + if ($first) { + $entry_id = @ldap_first_entry($this->conn_id, $result_id); + $first = false; + } else { + $entry_id = @ldap_next_entry($this->conn_id, $entry_id); + if ($entry_id === false) + break; + } + $user_dn = @ldap_get_dn($this->conn_id, $entry_id); + + // as the dn is not fetched as an attribute, we save it anyway + if (is_array($searchAttributes) && in_array('dn', $searchAttributes)) { + $this->log('Saving DN to AuthData', AUTH_LOG_DEBUG); + $this->_auth_obj->setAuthData('dn', $user_dn); + } + + // fetch attributes + if ($attributes = @ldap_get_attributes($this->conn_id, $entry_id)) { + + if (is_array($attributes) && isset($attributes['count']) && + $attributes['count'] > 0) { + + // ldap_get_attributes() returns a specific multi dimensional array + // format containing all the attributes and where each array starts + // with a 'count' element providing the number of attributes in the + // entry, or the number of values for attribute. For compatibility + // reasons, it remains the default format returned by LDAP container + // setAuthData(). + // The code below optionally returns attributes in another format, + // more compliant with other Auth containers, where each attribute + // element are directly set in the 'authData' list. This option is + // enabled by setting 'attrformat' to + // 'AUTH' in the 'options' array. + // eg. $this->options['attrformat'] = 'AUTH' + + if ( strtoupper($this->options['attrformat']) == 'AUTH' ) { + $this->log('Saving attributes to Auth data in AUTH format', AUTH_LOG_DEBUG); + unset ($attributes['count']); + foreach ($attributes as $attributeName => $attributeValue ) { + if (is_int($attributeName)) continue; + if (is_array($attributeValue) && isset($attributeValue['count'])) { + unset ($attributeValue['count']); + } + if (count($attributeValue)<=1) $attributeValue = $attributeValue[0]; + $this->log('Storing additional field: '.$attributeName, AUTH_LOG_DEBUG); + $this->_auth_obj->setAuthData($attributeName, $attributeValue); + } + } + else + { + $this->log('Saving attributes to Auth data in LDAP format', AUTH_LOG_DEBUG); + $this->_auth_obj->setAuthData('attributes', $attributes); + } + } + } + @ldap_free_result($result_id); + + // need to catch an empty password as openldap seems to return TRUE + // if anonymous binding is allowed + if ($password != "") { + $this->log("Bind as $user_dn", AUTH_LOG_DEBUG); + + // try binding as this user with the supplied password + if (@ldap_bind($this->conn_id, $user_dn, $password)) { + $this->log('Bind successful', AUTH_LOG_DEBUG); + + // check group if appropiate + if (strlen($this->options['group'])) { + // decide whether memberattr value is a dn or the username + $this->log('Checking group membership', AUTH_LOG_DEBUG); + $return = $this->checkGroup(($this->options['memberisdn']) ? $user_dn : $username); + $this->_disconnect(); + return $return; + } else { + $this->log('Authenticated', AUTH_LOG_DEBUG); + $this->_disconnect(); + return true; // user authenticated + } // checkGroup + } // bind + } // non-empty password + } while ($this->options['try_all'] == true); // interate through entries + } // get results + // default + $this->log('NOT authenticated!', AUTH_LOG_DEBUG); + $this->_disconnect(); + return false; + } + + // }}} + // {{{ checkGroup() + + /** + * Validate group membership + * + * Searches the LDAP server for group membership of the + * supplied username. Quotes all LDAP filter meta characters in + * the user name before querying the LDAP server. + * + * @param string Distinguished Name of the authenticated User + * @return boolean + */ + function checkGroup($user) + { + $this->log('Auth_Container_LDAP::checkGroup() called.', AUTH_LOG_DEBUG); + $err = $this->_prepare(); + if ($err !== true) { + return PEAR::raiseError($err->getMessage(), $err->getCode()); + } + + // make filter + $filter = sprintf('(&(%s=%s)(%s=%s)%s)', + $this->options['groupattr'], + $this->options['group'], + $this->options['memberattr'], + $this->_quoteFilterString($user), + $this->options['groupfilter']); + + // make search base dn + $search_basedn = $this->options['groupdn']; + if ($search_basedn != '' && substr($search_basedn, -1) != ',') { + $search_basedn .= ','; + } + $search_basedn .= $this->options['basedn']; + + $func_params = array($this->conn_id, $search_basedn, $filter, + array($this->options['memberattr'])); + $func_name = $this->_scope2function($this->options['groupscope']); + + $this->log("Searching with $func_name and filter $filter in $search_basedn", AUTH_LOG_DEBUG); + + // search + if (($result_id = @call_user_func_array($func_name, $func_params)) != false) { + if (@ldap_count_entries($this->conn_id, $result_id) == 1) { + @ldap_free_result($result_id); + $this->log('User is member of group', AUTH_LOG_DEBUG); + return true; + } + } + // default + $this->log('User is NOT member of group', AUTH_LOG_DEBUG); + return false; + } + + // }}} + // {{{ _quoteFilterString() + + /** + * Escapes LDAP filter special characters as defined in RFC 2254. + * + * @access private + * @param string Filter String + */ + function _quoteFilterString($filter_str) + { + $metas = array( '\\', '*', '(', ')', "\x00"); + $quoted_metas = array('\\\\', '\*', '\(', '\)', "\\\x00"); + return str_replace($metas, $quoted_metas, $filter_str); + } + + // }}} + +} + +?> diff --git a/includes/pear/Auth/Container/MDB.php b/includes/pear/Auth/Container/MDB.php new file mode 100644 index 0000000..019a122 --- /dev/null +++ b/includes/pear/Auth/Container/MDB.php @@ -0,0 +1,625 @@ + + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version CVS: $Id: MDB.php 256753 2008-04-04 07:57:02Z aashley $ + * @link http://pear.php.net/package/Auth + * @since File available since Release 1.2.3 + */ + +/** + * Include Auth_Container base class + */ +require_once 'Auth/Container.php'; +/** + * Include PEAR MDB package + */ +require_once 'MDB.php'; + +/** + * Storage driver for fetching login data from a database + * + * This storage driver can use all databases which are supported + * by the PEAR MDB abstraction layer to fetch login data. + * + * @category Authentication + * @package Auth + * @author Lorenzo Alberton + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version Release: @package_version@ File: $Revision: 256753 $ + * @link http://pear.php.net/package/Auth + * @since Class available since Release 1.2.3 + */ +class Auth_Container_MDB extends Auth_Container +{ + + // {{{ properties + + /** + * Additional options for the storage container + * @var array + */ + var $options = array(); + + /** + * MDB object + * @var object + */ + var $db = null; + var $dsn = ''; + + /** + * User that is currently selected from the DB. + * @var string + */ + var $activeUser = ''; + + // }}} + // {{{ Auth_Container_MDB() [constructor] + + /** + * Constructor of the container class + * + * Initate connection to the database via PEAR::MDB + * + * @param string Connection data or MDB object + * @return object Returns an error object if something went wrong + */ + function Auth_Container_MDB($dsn) + { + $this->_setDefaults(); + + if (is_array($dsn)) { + $this->_parseOptions($dsn); + if (empty($this->options['dsn'])) { + PEAR::raiseError('No connection parameters specified!'); + } + } else { + $this->options['dsn'] = $dsn; + } + } + + // }}} + // {{{ _connect() + + /** + * Connect to database by using the given DSN string + * + * @access private + * @param mixed DSN string | array | mdb object + * @return mixed Object on error, otherwise bool + */ + function _connect($dsn) + { + $this->log('Auth_Container_MDB::_connect() called.', AUTH_LOG_DEBUG); + if (is_string($dsn) || is_array($dsn)) { + $this->db =& MDB::connect($dsn, $this->options['db_options']); + } elseif (is_subclass_of($dsn, 'mdb_common')) { + $this->db = $dsn; + } elseif (is_object($dsn) && MDB::isError($dsn)) { + return PEAR::raiseError($dsn->getMessage(), $dsn->code); + } else { + return PEAR::raiseError('The given dsn was not valid in file ' . __FILE__ . ' at line ' . __LINE__, + 41, + PEAR_ERROR_RETURN, + null, + null + ); + + } + + if (MDB::isError($this->db) || PEAR::isError($this->db)) { + return PEAR::raiseError($this->db->getMessage(), $this->db->code); + } + + if ($this->options['auto_quote']) { + if (strpos('.', $this->options['table']) === false) { + $this->options['final_table'] = $this->db->quoteIdentifier($this->options['table']); + } else { + $t = explode('.', $this->options['table']); + for ($i = 0, $count = count($t); $i < $count; $i++) + $t[$i] = $this->db->quoteIdentifier($t[$i]); + $this->options['final_table'] = implode('.', $t); + } + $this->options['final_usernamecol'] = $this->db->quoteIdentifier($this->options['usernamecol']); + $this->options['final_passwordcol'] = $this->db->quoteIdentifier($this->options['passwordcol']); + } else { + $this->options['final_table'] = $this->options['table']; + $this->options['final_usernamecol'] = $this->options['usernamecol']; + $this->options['final_passwordcol'] = $this->options['passwordcol']; + } + + return true; + } + + // }}} + // {{{ _prepare() + + /** + * Prepare database connection + * + * This function checks if we have already opened a connection to + * the database. If that's not the case, a new connection is opened. + * + * @access private + * @return mixed True or a MDB error object. + */ + function _prepare() + { + if (is_subclass_of($this->db, 'mdb_common')) { + return true; + } + return $this->_connect($this->options['dsn']); + } + + // }}} + // {{{ query() + + /** + * Prepare query to the database + * + * This function checks if we have already opened a connection to + * the database. If that's not the case, a new connection is opened. + * After that the query is passed to the database. + * + * @access public + * @param string Query string + * @return mixed a MDB_result object or MDB_OK on success, a MDB + * or PEAR error on failure + */ + function query($query) + { + $this->log('Auth_Container_MDB::query() called.', AUTH_LOG_DEBUG); + $err = $this->_prepare(); + if ($err !== true) { + return $err; + } + return $this->db->query($query); + } + + // }}} + // {{{ _setDefaults() + + /** + * Set some default options + * + * @access private + * @return void + */ + function _setDefaults() + { + $this->options['table'] = 'auth'; + $this->options['usernamecol'] = 'username'; + $this->options['passwordcol'] = 'password'; + $this->options['dsn'] = ''; + $this->options['db_fields'] = ''; + $this->options['cryptType'] = 'md5'; + $this->options['db_options'] = array(); + $this->options['db_where'] = ''; + $this->options['auto_quote'] = true; + } + + // }}} + // {{{ _parseOptions() + + /** + * Parse options passed to the container class + * + * @access private + * @param array + */ + function _parseOptions($array) + { + foreach ($array as $key => $value) { + if (isset($this->options[$key])) { + $this->options[$key] = $value; + } + } + } + + // }}} + // {{{ _quoteDBFields() + + /** + * Quote the db_fields option to avoid the possibility of SQL injection. + * + * @access private + * @return string A properly quoted string that can be concatenated into a + * SELECT clause. + */ + function _quoteDBFields() + { + if (isset($this->options['db_fields'])) { + if (is_array($this->options['db_fields'])) { + if ($this->options['auto_quote']) { + $fields = array(); + foreach ($this->options['db_fields'] as $field) { + $fields[] = $this->db->quoteIdentifier($field); + } + return implode(', ', $fields); + } else { + return implode(', ', $this->options['db_fields']); + } + } else { + if (strlen($this->options['db_fields']) > 0) { + if ($this->options['auto_quote']) { + return $this->db->quoteIdentifier($this->options['db_fields']); + } else { + return $this->options['db_fields']; + } + } + } + } + + return ''; + } + + // }}} + // {{{ fetchData() + + /** + * Get user information from database + * + * This function uses the given username to fetch + * the corresponding login data from the database + * table. If an account that matches the passed username + * and password is found, the function returns true. + * Otherwise it returns false. + * + * @param string Username + * @param string Password + * @param boolean If true password is secured using a md5 hash + * the frontend and auth are responsible for making sure the container supports + * challenge response password authentication + * @return mixed Error object or boolean + */ + function fetchData($username, $password, $isChallengeResponse=false) + { + $this->log('Auth_Container_MDB::fetchData() called.', AUTH_LOG_DEBUG); + // Prepare for a database query + $err = $this->_prepare(); + if ($err !== true) { + return PEAR::raiseError($err->getMessage(), $err->getCode()); + } + + //Check if db_fields contains a *, if so assume all columns are selected + if (is_string($this->options['db_fields']) + && strstr($this->options['db_fields'], '*')) { + $sql_from = '*'; + } else { + $sql_from = $this->options['final_usernamecol']. + ", ".$this->options['final_passwordcol']; + + if (strlen($fields = $this->_quoteDBFields()) > 0) { + $sql_from .= ', '.$fields; + } + } + + $query = sprintf("SELECT %s FROM %s WHERE %s = %s", + $sql_from, + $this->options['final_table'], + $this->options['final_usernamecol'], + $this->db->getTextValue($username) + ); + + // check if there is an optional parameter db_where + if ($this->options['db_where'] != '') { + // there is one, so add it to the query + $query .= " AND ".$this->options['db_where']; + } + + $this->log('Running SQL against MDB: '.$query, AUTH_LOG_DEBUG); + + $res = $this->db->getRow($query, null, null, null, MDB_FETCHMODE_ASSOC); + + if (MDB::isError($res) || PEAR::isError($res)) { + return PEAR::raiseError($res->getMessage(), $res->getCode()); + } + if (!is_array($res)) { + $this->activeUser = ''; + return false; + } + + // Perform trimming here before the hashing + $password = trim($password, "\r\n"); + $res[$this->options['passwordcol']] = trim($res[$this->options['passwordcol']], "\r\n"); + + // If using Challenge Response md5 the pass with the secret + if ($isChallengeResponse) { + $res[$this->options['passwordcol']] = + md5($res[$this->options['passwordcol']].$this->_auth_obj->session['loginchallenege']); + // UGLY cannot avoid without modifying verifyPassword + if ($this->options['cryptType'] == 'md5') { + $res[$this->options['passwordcol']] = md5($res[$this->options['passwordcol']]); + } + } + + if ($this->verifyPassword($password, + $res[$this->options['passwordcol']], + $this->options['cryptType'])) { + // Store additional field values in the session + foreach ($res as $key => $value) { + if ($key == $this->options['passwordcol'] || + $key == $this->options['usernamecol']) { + continue; + } + + $this->log('Storing additional field: '.$key, AUTH_LOG_DEBUG); + // Use reference to the auth object if exists + // This is because the auth session variable can change so a static + // call to setAuthData does not make sense + $this->_auth_obj->setAuthData($key, $value); + } + return true; + } + + $this->activeUser = $res[$this->options['usernamecol']]; + return false; + } + + // }}} + // {{{ listUsers() + + /** + * Returns a list of users from the container + * + * @return mixed array|PEAR_Error + * @access public + */ + function listUsers() + { + $this->log('Auth_Container_MDB::listUsers() called.', AUTH_LOG_DEBUG); + $err = $this->_prepare(); + if ($err !== true) { + return PEAR::raiseError($err->getMessage(), $err->getCode()); + } + + $retVal = array(); + + //Check if db_fields contains a *, if so assume all columns are selected + if ( is_string($this->options['db_fields']) + && strstr($this->options['db_fields'], '*')) { + $sql_from = '*'; + } else { + $sql_from = $this->options['final_usernamecol'] + .', '.$this->options['final_passwordcol']; + + if (strlen($fields = $this->_quoteDBFields()) > 0) { + $sql_from .= ', '.$fields; + } + } + + $query = sprintf('SELECT %s FROM %s', + $sql_from, + $this->options['final_table'] + ); + + // check if there is an optional parameter db_where + if ($this->options['db_where'] != '') { + // there is one, so add it to the query + $query .= " WHERE ".$this->options['db_where']; + } + + $this->log('Running SQL against MDB: '.$query, AUTH_LOG_DEBUG); + + $res = $this->db->getAll($query, null, null, null, MDB_FETCHMODE_ASSOC); + + if (MDB::isError($res)) { + return PEAR::raiseError($res->getMessage(), $res->getCode()); + } else { + foreach ($res as $user) { + $user['username'] = $user[$this->options['usernamecol']]; + $retVal[] = $user; + } + } + $this->log('Found '.count($retVal).' users.', AUTH_LOG_DEBUG); + return $retVal; + } + + // }}} + // {{{ addUser() + + /** + * Add user to the storage container + * + * @access public + * @param string Username + * @param string Password + * @param mixed Additional information that are stored in the DB + * + * @return mixed True on success, otherwise error object + */ + function addUser($username, $password, $additional = "") + { + $this->log('Auth_Container_MDB::addUser() called.', AUTH_LOG_DEBUG); + $err = $this->_prepare(); + if ($err !== true) { + return PEAR::raiseError($err->getMessage(), $err->getCode()); + } + + if (isset($this->options['cryptType']) && $this->options['cryptType'] == 'none') { + $cryptFunction = 'strval'; + } elseif (isset($this->options['cryptType']) && function_exists($this->options['cryptType'])) { + $cryptFunction = $this->options['cryptType']; + } else { + $cryptFunction = 'md5'; + } + + $password = $cryptFunction($password); + + $additional_key = ''; + $additional_value = ''; + + if (is_array($additional)) { + foreach ($additional as $key => $value) { + if ($this->options['auto_quote']) { + $additional_key .= ', ' . $this->db->quoteIdentifier($key); + } else { + $additional_key .= ', ' . $key; + } + $additional_value .= ', ' . $this->db->getTextValue($value); + } + } + + $query = sprintf("INSERT INTO %s (%s, %s%s) VALUES (%s, %s%s)", + $this->options['final_table'], + $this->options['final_usernamecol'], + $this->options['final_passwordcol'], + $additional_key, + $this->db->getTextValue($username), + $this->db->getTextValue($password), + $additional_value + ); + + $this->log('Running SQL against MDB: '.$query, AUTH_LOG_DEBUG); + + $res = $this->query($query); + + if (MDB::isError($res)) { + return PEAR::raiseError($res->getMessage(), $res->code); + } + return true; + } + + // }}} + // {{{ removeUser() + + /** + * Remove user from the storage container + * + * @access public + * @param string Username + * + * @return mixed True on success, otherwise error object + */ + function removeUser($username) + { + $this->log('Auth_Container_MDB::removeUser() called.', AUTH_LOG_DEBUG); + $err = $this->_prepare(); + if ($err !== true) { + return PEAR::raiseError($err->getMessage(), $err->getCode()); + } + + $query = sprintf("DELETE FROM %s WHERE %s = %s", + $this->options['final_table'], + $this->options['final_usernamecol'], + $this->db->getTextValue($username) + ); + + // check if there is an optional parameter db_where + if ($this->options['db_where'] != '') { + // there is one, so add it to the query + $query .= " AND ".$this->options['db_where']; + } + + $this->log('Running SQL against MDB: '.$query, AUTH_LOG_DEBUG); + + $res = $this->query($query); + + if (MDB::isError($res)) { + return PEAR::raiseError($res->getMessage(), $res->code); + } + return true; + } + + // }}} + // {{{ changePassword() + + /** + * Change password for user in the storage container + * + * @param string Username + * @param string The new password (plain text) + */ + function changePassword($username, $password) + { + $this->log('Auth_Container_MDB::changePassword() called.', AUTH_LOG_DEBUG); + $err = $this->_prepare(); + if ($err !== true) { + return PEAR::raiseError($err->getMessage(), $err->getCode()); + } + + if (isset($this->options['cryptType']) && $this->options['cryptType'] == 'none') { + $cryptFunction = 'strval'; + } elseif (isset($this->options['cryptType']) && function_exists($this->options['cryptType'])) { + $cryptFunction = $this->options['cryptType']; + } else { + $cryptFunction = 'md5'; + } + + $password = $cryptFunction($password); + + $query = sprintf("UPDATE %s SET %s = %s WHERE %s = %s", + $this->options['final_table'], + $this->options['final_passwordcol'], + $this->db->getTextValue($password), + $this->options['final_usernamecol'], + $this->db->getTextValue($username) + ); + + // check if there is an optional parameter db_where + if ($this->options['db_where'] != '') { + // there is one, so add it to the query + $query .= " AND ".$this->options['db_where']; + } + + $this->log('Running SQL against MDB: '.$query, AUTH_LOG_DEBUG); + + $res = $this->query($query); + + if (MDB::isError($res)) { + return PEAR::raiseError($res->getMessage(), $res->code); + } + return true; + } + + // }}} + // {{{ supportsChallengeResponse() + + /** + * Determine if this container supports + * password authentication with challenge response + * + * @return bool + * @access public + */ + function supportsChallengeResponse() + { + return in_array($this->options['cryptType'], array('md5', 'none', '')); + } + + // }}} + // {{{ getCryptType() + + /** + * Returns the selected crypt type for this container + * + * @return string Function used to crypt the password + */ + function getCryptType() + { + return $this->options['cryptType']; + } + + // }}} + +} +?> diff --git a/includes/pear/Auth/Container/MDB2.php b/includes/pear/Auth/Container/MDB2.php new file mode 100644 index 0000000..a3c26b7 --- /dev/null +++ b/includes/pear/Auth/Container/MDB2.php @@ -0,0 +1,624 @@ + + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version CVS: $Id: MDB2.php 256753 2008-04-04 07:57:02Z aashley $ + * @link http://pear.php.net/package/Auth + * @since File available since Release 1.3.0 + */ + +/** + * Include Auth_Container base class + */ +require_once 'Auth/Container.php'; +/** + * Include PEAR MDB2 package + */ +require_once 'MDB2.php'; + +/** + * Storage driver for fetching login data from a database + * + * This storage driver can use all databases which are supported + * by the PEAR MDB2 abstraction layer to fetch login data. + * + * @category Authentication + * @package Auth + * @author Lorenzo Alberton + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version Release: @package_version@ File: $Revision: 256753 $ + * @link http://pear.php.net/package/Auth + * @since Class available since Release 1.3.0 + */ +class Auth_Container_MDB2 extends Auth_Container +{ + + // {{{ properties + + /** + * Additional options for the storage container + * @var array + */ + var $options = array(); + + /** + * MDB object + * @var object + */ + var $db = null; + var $dsn = ''; + + /** + * User that is currently selected from the DB. + * @var string + */ + var $activeUser = ''; + + // }}} + // {{{ Auth_Container_MDB2() [constructor] + + /** + * Constructor of the container class + * + * Initate connection to the database via PEAR::MDB2 + * + * @param string Connection data or MDB2 object + * @return object Returns an error object if something went wrong + */ + function Auth_Container_MDB2($dsn) + { + $this->_setDefaults(); + + if (is_array($dsn)) { + $this->_parseOptions($dsn); + if (empty($this->options['dsn'])) { + PEAR::raiseError('No connection parameters specified!'); + } + } else { + $this->options['dsn'] = $dsn; + } + } + + // }}} + // {{{ _connect() + + /** + * Connect to database by using the given DSN string + * + * @access private + * @param mixed DSN string | array | mdb object + * @return mixed Object on error, otherwise bool + */ + function _connect($dsn) + { + $this->log('Auth_Container_MDB2::_connect() called.', AUTH_LOG_DEBUG); + if (is_string($dsn) || is_array($dsn)) { + $this->db =& MDB2::connect($dsn, $this->options['db_options']); + } elseif (is_subclass_of($dsn, 'MDB2_Driver_Common')) { + $this->db = $dsn; + } elseif (is_object($dsn) && MDB2::isError($dsn)) { + return PEAR::raiseError($dsn->getMessage(), $dsn->code); + } else { + return PEAR::raiseError('The given dsn was not valid in file ' . __FILE__ . ' at line ' . __LINE__, + 41, + PEAR_ERROR_RETURN, + null, + null + ); + + } + + if (MDB2::isError($this->db) || PEAR::isError($this->db)) { + return PEAR::raiseError($this->db->getMessage(), $this->db->code); + } + + if ($this->options['auto_quote']) { + if (strpos('.', $this->options['table']) === false) { + $this->options['final_table'] = $this->db->quoteIdentifier($this->options['table'], true); + } else { + $t = explode('.', $this->options['table']); + for ($i = 0, $count = count($t); $i < $count; $i++) + $t[$i] = $this->db->quoteIdentifier($t[$i], true); + $this->options['final_table'] = implode('.', $t); + } + $this->options['final_usernamecol'] = $this->db->quoteIdentifier($this->options['usernamecol'], true); + $this->options['final_passwordcol'] = $this->db->quoteIdentifier($this->options['passwordcol'], true); + } else { + $this->options['final_table'] = $this->options['table']; + $this->options['final_usernamecol'] = $this->options['usernamecol']; + $this->options['final_passwordcol'] = $this->options['passwordcol']; + } + + return true; + } + + // }}} + // {{{ _prepare() + + /** + * Prepare database connection + * + * This function checks if we have already opened a connection to + * the database. If that's not the case, a new connection is opened. + * + * @access private + * @return mixed True or a MDB error object. + */ + function _prepare() + { + if (is_subclass_of($this->db, 'MDB2_Driver_Common')) { + return true; + } + return $this->_connect($this->options['dsn']); + } + + // }}} + // {{{ query() + + /** + * Prepare query to the database + * + * This function checks if we have already opened a connection to + * the database. If that's not the case, a new connection is opened. + * After that the query is passed to the database. + * + * @access public + * @param string Query string + * @return mixed a MDB_result object or MDB_OK on success, a MDB + * or PEAR error on failure + */ + function query($query) + { + $this->log('Auth_Container_MDB2::query() called.', AUTH_LOG_DEBUG); + $err = $this->_prepare(); + if ($err !== true) { + return $err; + } + return $this->db->exec($query); + } + + // }}} + // {{{ _setDefaults() + + /** + * Set some default options + * + * @access private + * @return void + */ + function _setDefaults() + { + $this->options['table'] = 'auth'; + $this->options['usernamecol'] = 'username'; + $this->options['passwordcol'] = 'password'; + $this->options['dsn'] = ''; + $this->options['db_fields'] = ''; + $this->options['cryptType'] = 'md5'; + $this->options['db_options'] = array(); + $this->options['db_where'] = ''; + $this->options['auto_quote'] = true; + } + + // }}} + // {{{ _parseOptions() + + /** + * Parse options passed to the container class + * + * @access private + * @param array + */ + function _parseOptions($array) + { + foreach ($array as $key => $value) { + if (isset($this->options[$key])) { + $this->options[$key] = $value; + } + } + } + + // }}} + // {{{ _quoteDBFields() + + /** + * Quote the db_fields option to avoid the possibility of SQL injection. + * + * @access private + * @return string A properly quoted string that can be concatenated into a + * SELECT clause. + */ + function _quoteDBFields() + { + if (isset($this->options['db_fields'])) { + if (is_array($this->options['db_fields'])) { + if ($this->options['auto_quote']) { + $fields = array(); + foreach ($this->options['db_fields'] as $field) { + $fields[] = $this->db->quoteIdentifier($field, true); + } + return implode(', ', $fields); + } else { + return implode(', ', $this->options['db_fields']); + } + } else { + if (strlen($this->options['db_fields']) > 0) { + if ($this->options['auto_quote']) { + return $this->db->quoteIdentifier($this->options['db_fields'], true); + } else { + return $this->options['db_fields']; + } + } + } + } + + return ''; + } + + // }}} + // {{{ fetchData() + + /** + * Get user information from database + * + * This function uses the given username to fetch + * the corresponding login data from the database + * table. If an account that matches the passed username + * and password is found, the function returns true. + * Otherwise it returns false. + * + * @param string Username + * @param string Password + * @param boolean If true password is secured using a md5 hash + * the frontend and auth are responsible for making sure the container supports + * challenge response password authentication + * @return mixed Error object or boolean + */ + function fetchData($username, $password, $isChallengeResponse=false) + { + $this->log('Auth_Container_MDB2::fetchData() called.', AUTH_LOG_DEBUG); + // Prepare for a database query + $err = $this->_prepare(); + if ($err !== true) { + return PEAR::raiseError($err->getMessage(), $err->getCode()); + } + + //Check if db_fields contains a *, if so assume all columns are selected + if (is_string($this->options['db_fields']) + && strstr($this->options['db_fields'], '*')) { + $sql_from = '*'; + } else { + $sql_from = $this->options['final_usernamecol']. + ", ".$this->options['final_passwordcol']; + + if (strlen($fields = $this->_quoteDBFields()) > 0) { + $sql_from .= ', '.$fields; + } + } + $query = sprintf("SELECT %s FROM %s WHERE %s = %s", + $sql_from, + $this->options['final_table'], + $this->options['final_usernamecol'], + $this->db->quote($username, 'text') + ); + + // check if there is an optional parameter db_where + if ($this->options['db_where'] != '') { + // there is one, so add it to the query + $query .= " AND ".$this->options['db_where']; + } + + $this->log('Running SQL against MDB2: '.$query, AUTH_LOG_DEBUG); + + $res = $this->db->queryRow($query, null, MDB2_FETCHMODE_ASSOC); + if (MDB2::isError($res) || PEAR::isError($res)) { + return PEAR::raiseError($res->getMessage(), $res->getCode()); + } + if (!is_array($res)) { + $this->activeUser = ''; + return false; + } + + // Perform trimming here before the hashing + $password = trim($password, "\r\n"); + $res[$this->options['passwordcol']] = trim($res[$this->options['passwordcol']], "\r\n"); + // If using Challenge Response md5 the pass with the secret + if ($isChallengeResponse) { + $res[$this->options['passwordcol']] = + md5($res[$this->options['passwordcol']].$this->_auth_obj->session['loginchallenege']); + // UGLY cannot avoid without modifying verifyPassword + if ($this->options['cryptType'] == 'md5') { + $res[$this->options['passwordcol']] = md5($res[$this->options['passwordcol']]); + } + } + if ($this->verifyPassword($password, + $res[$this->options['passwordcol']], + $this->options['cryptType'])) { + // Store additional field values in the session + foreach ($res as $key => $value) { + if ($key == $this->options['passwordcol'] || + $key == $this->options['usernamecol']) { + continue; + } + + $this->log('Storing additional field: '.$key, AUTH_LOG_DEBUG); + + // Use reference to the auth object if exists + // This is because the auth session variable can change so a static call to setAuthData does not make sense + $this->_auth_obj->setAuthData($key, $value); + } + return true; + } + + $this->activeUser = $res[$this->options['usernamecol']]; + return false; + } + + // }}} + // {{{ listUsers() + + /** + * Returns a list of users from the container + * + * @return mixed array|PEAR_Error + * @access public + */ + function listUsers() + { + $this->log('Auth_Container_MDB2::listUsers() called.', AUTH_LOG_DEBUG); + $err = $this->_prepare(); + if ($err !== true) { + return PEAR::raiseError($err->getMessage(), $err->getCode()); + } + + $retVal = array(); + + //Check if db_fields contains a *, if so assume all columns are selected + if ( is_string($this->options['db_fields']) + && strstr($this->options['db_fields'], '*')) { + $sql_from = '*'; + } else { + $sql_from = $this->options['final_usernamecol']. + ", ".$this->options['final_passwordcol']; + + if (strlen($fields = $this->_quoteDBFields()) > 0) { + $sql_from .= ', '.$fields; + } + } + + $query = sprintf('SELECT %s FROM %s', + $sql_from, + $this->options['final_table'] + ); + + // check if there is an optional parameter db_where + if ($this->options['db_where'] != '') { + // there is one, so add it to the query + $query .= " WHERE ".$this->options['db_where']; + } + + $this->log('Running SQL against MDB2: '.$query, AUTH_LOG_DEBUG); + + $res = $this->db->queryAll($query, null, MDB2_FETCHMODE_ASSOC); + if (MDB2::isError($res)) { + return PEAR::raiseError($res->getMessage(), $res->getCode()); + } else { + foreach ($res as $user) { + $user['username'] = $user[$this->options['usernamecol']]; + $retVal[] = $user; + } + } + $this->log('Found '.count($retVal).' users.', AUTH_LOG_DEBUG); + return $retVal; + } + + // }}} + // {{{ addUser() + + /** + * Add user to the storage container + * + * @access public + * @param string Username + * @param string Password + * @param mixed Additional information that are stored in the DB + * + * @return mixed True on success, otherwise error object + */ + function addUser($username, $password, $additional = "") + { + $this->log('Auth_Container_MDB2::addUser() called.', AUTH_LOG_DEBUG); + + // Prepare for a database query + $err = $this->_prepare(); + if ($err !== true) { + return PEAR::raiseError($err->getMessage(), $err->getCode()); + } + + if (isset($this->options['cryptType']) && $this->options['cryptType'] == 'none') { + $cryptFunction = 'strval'; + } elseif (isset($this->options['cryptType']) && function_exists($this->options['cryptType'])) { + $cryptFunction = $this->options['cryptType']; + } else { + $cryptFunction = 'md5'; + } + + $password = $cryptFunction($password); + + $additional_key = ''; + $additional_value = ''; + + if (is_array($additional)) { + foreach ($additional as $key => $value) { + if ($this->options['auto_quote']) { + $additional_key .= ', ' . $this->db->quoteIdentifier($key, true); + } else { + $additional_key .= ', ' . $key; + } + $additional_value .= ', ' . $this->db->quote($value, 'text'); + } + } + + $query = sprintf("INSERT INTO %s (%s, %s%s) VALUES (%s, %s%s)", + $this->options['final_table'], + $this->options['final_usernamecol'], + $this->options['final_passwordcol'], + $additional_key, + $this->db->quote($username, 'text'), + $this->db->quote($password, 'text'), + $additional_value + ); + + $this->log('Running SQL against MDB2: '.$query, AUTH_LOG_DEBUG); + + $res = $this->query($query); + + if (MDB2::isError($res)) { + return PEAR::raiseError($res->getMessage(), $res->code); + } + return true; + } + + // }}} + // {{{ removeUser() + + /** + * Remove user from the storage container + * + * @access public + * @param string Username + * + * @return mixed True on success, otherwise error object + */ + function removeUser($username) + { + $this->log('Auth_Container_MDB2::removeUser() called.', AUTH_LOG_DEBUG); + // Prepare for a database query + $err = $this->_prepare(); + if ($err !== true) { + return PEAR::raiseError($err->getMessage(), $err->getCode()); + } + + $query = sprintf("DELETE FROM %s WHERE %s = %s", + $this->options['final_table'], + $this->options['final_usernamecol'], + $this->db->quote($username, 'text') + ); + + // check if there is an optional parameter db_where + if ($this->options['db_where'] != '') { + // there is one, so add it to the query + $query .= " AND ".$this->options['db_where']; + } + + $this->log('Running SQL against MDB2: '.$query, AUTH_LOG_DEBUG); + + $res = $this->query($query); + + if (MDB2::isError($res)) { + return PEAR::raiseError($res->getMessage(), $res->code); + } + return true; + } + + // }}} + // {{{ changePassword() + + /** + * Change password for user in the storage container + * + * @param string Username + * @param string The new password (plain text) + */ + function changePassword($username, $password) + { + $this->log('Auth_Container_MDB2::changePassword() called.', AUTH_LOG_DEBUG); + // Prepare for a database query + $err = $this->_prepare(); + if ($err !== true) { + return PEAR::raiseError($err->getMessage(), $err->getCode()); + } + + if (isset($this->options['cryptType']) && $this->options['cryptType'] == 'none') { + $cryptFunction = 'strval'; + } elseif (isset($this->options['cryptType']) && function_exists($this->options['cryptType'])) { + $cryptFunction = $this->options['cryptType']; + } else { + $cryptFunction = 'md5'; + } + + $password = $cryptFunction($password); + + $query = sprintf("UPDATE %s SET %s = %s WHERE %s = %s", + $this->options['final_table'], + $this->options['final_passwordcol'], + $this->db->quote($password, 'text'), + $this->options['final_usernamecol'], + $this->db->quote($username, 'text') + ); + + // check if there is an optional parameter db_where + if ($this->options['db_where'] != '') { + // there is one, so add it to the query + $query .= " AND ".$this->options['db_where']; + } + + $this->log('Running SQL against MDB2: '.$query, AUTH_LOG_DEBUG); + + $res = $this->query($query); + + if (MDB2::isError($res)) { + return PEAR::raiseError($res->getMessage(), $res->code); + } + return true; + } + + // }}} + // {{{ supportsChallengeResponse() + + /** + * Determine if this container supports + * password authentication with challenge response + * + * @return bool + * @access public + */ + function supportsChallengeResponse() + { + return in_array($this->options['cryptType'], array('md5', 'none', '')); + } + + // }}} + // {{{ getCryptType() + + /** + * Returns the selected crypt type for this container + * + * @return string Function used to crypt the password + */ + function getCryptType() + { + return $this->options['cryptType']; + } + + // }}} + +} +?> diff --git a/includes/pear/Auth/Container/Multiple.php b/includes/pear/Auth/Container/Multiple.php new file mode 100644 index 0000000..08f131d --- /dev/null +++ b/includes/pear/Auth/Container/Multiple.php @@ -0,0 +1,188 @@ + + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version CVS: $Id: Multiple.php 289653 2009-10-15 04:50:43Z aashley $ + * @since File available since Release 1.5.0 + */ + +/** + * Include Auth_Container base class + */ +require_once "Auth/Container.php"; +/** + * Include PEAR package for error handling + */ +require_once "PEAR.php"; + +/** + * Storage driver for using multiple storage drivers in a fall through fashion + * + * This storage driver provides a mechanism for working through multiple + * storage drivers until either one allows successful login or the list is + * exhausted. + * + * This container takes an array of options of the following form: + * + * array( + * array( + * 'type' => , + * 'options' => , + * ), + * ); + * + * Full example: + * + * $options = array( + * array( + * 'type' => 'DB', + * 'options' => array( + * 'dsn' => "mysql://user:password@localhost/database", + * ), + * ), + * array( + * 'type' => 'Array', + * 'options' => array( + * 'cryptType' => 'md5', + * 'users' => array( + * 'admin' => md5('password'), + * ), + * ), + * ), + * ); + * + * $auth = new Auth('Multiple', $options); + * + * @category Authentication + * @package Auth + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version Release: @package_version@ File: $Revision: 289653 $ + * @since File available since Release 1.5.0 + */ + +class Auth_Container_Multiple extends Auth_Container { + + // {{{ properties + + /** + * The options for each container + * + * @var array $options + */ + var $options = array(); + + /** + * The instanciated containers + * + * @var array $containers + */ + var $containers = array(); + + // }}} + // {{{ Auth_Container_Multiple() + + /** + * Constructor for Array Container + * + * @param array $data Options for the container + * @return void + */ + function Auth_Container_Multiple($options) + { + if (!is_array($options)) { + PEAR::raiseError('The options for Auth_Container_Multiple must be an array'); + } + if (count($options) < 1) { + PEAR::raiseError('You must define at least one sub container to use in Auth_Container_Multiple'); + } + foreach ($options as $option) { + if (!isset($option['type'])) { + PEAR::raiseError('No type defined for sub container'); + } + } + $this->options = $options; + } + + // }}} + // {{{ fetchData() + + /** + * Get user information from array + * + * This function uses the given username to fetch the corresponding + * login data from the array. If an account that matches the passed + * username and password is found, the function returns true. + * Otherwise it returns false. + * + * @param string Username + * @param string Password + * @return boolean|PEAR_Error Error object or boolean + */ + function fetchData($user, $pass) + { + $this->log('Auth_Container_Multiple::fetchData() called.', AUTH_LOG_DEBUG); + + foreach ($this->options as $key => $options) { + + $this->log('Using Container '.$key.' of type '.$options['type'].'.', AUTH_LOG_DEBUG); + + if (isset($this->containers[$key]) && is_a($this->containers[$key], 'Auth_Container')) { + + $container = &$this->containers[$key]; + + } else { + + $this->containers[$key] = &$this->_auth_obj->_factory($options['type'], $options['options']); + $this->containers[$key]->_auth_obj = &$this->_auth_obj; + $container = &$this->containers[$key]; + + } + + $result = $container->fetchData($user, $pass); + + if (PEAR::isError($result)) { + + $this->log('Container '.$key.': '.$result->getMessage(), AUTH_LOG_DEBUG); + return $result; + + } elseif ($result == true) { + + $this->log('Container '.$key.': Authentication successful.', AUTH_LOG_DEBUG); + return true; + + } else { + + $this->log('Container '.$key.': Authentication failed.', AUTH_LOG_DEBUG); + + } + + } + + $this->log('Auth_Container_Multiple: All containers rejected user credentials.', AUTH_LOG_DEBUG); + + return false; + + } + + // }}} + +} + +?> diff --git a/includes/pear/Auth/Container/NetVPOPMaild.php b/includes/pear/Auth/Container/NetVPOPMaild.php new file mode 100644 index 0000000..634fab5 --- /dev/null +++ b/includes/pear/Auth/Container/NetVPOPMaild.php @@ -0,0 +1,129 @@ + + * @author Stefan Ekman + * @author Martin Jansen + * @author Mika Tuupola + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @link http://pear.php.net/package/Auth + * @since File available since Release 1.2.0 + */ + +/** + * Include Auth_Container base class + */ +require_once 'Auth/Container.php'; +/** + * Include PEAR package for error handling + */ +require_once 'PEAR.php'; +/** + * Include PEAR Net_Vpopmaild package + */ +require_once 'Net/Vpopmaild.php'; + +/** + * Storage driver for Authentication on a Vpopmaild server. + * + * @category Authentication + * @package Auth + * @author Martin Jansen + * @author Mika Tuupola + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version Release: 1.5.4 File: $Revision: 256741 $ + * @link http://pear.php.net/package/Auth + * @since Class available since Release 1.6.0 + */ +class Auth_Container_Vpopmaild extends Auth_Container +{ + + /** + * Vpopmaild Server + * @var string + */ + var $server = 'localhost'; + + /** + * Vpopmaild Server port + * @var string + */ + var $port = 89; + + /** + * Constructor of the container class + * + * @param $server string server or server:port combination + * @return object Returns an error object if something went wrong + */ + function Auth_Container_Vpopmaild($server=null) + { + if (isset($server) && !is_null($server)) { + if (is_array($server)) { + if (isset($server['host'])) { + $this->server = $server['host']; + } + if (isset($server['port'])) { + $this->port = $server['port']; + } + } else { + if (strstr($server, ':')) { + $serverparts = explode(':', trim($server)); + $this->server = $serverparts[0]; + $this->port = $serverparts[1]; + } else { + $this->server = $server; + } + } + } + } + + /** + * fetchData() + * + * Try to login to the Vpopmaild server + * + * @param string username + * @param string password + * + * @return boolean + */ + function fetchData($username, $password) + { + $this->log('Auth_Container_Vpopmaild::fetchData() called.', AUTH_LOG_DEBUG); + $vpopmaild =& new Net_Vpopmaild(); + // Connect + try { + $res = $vpopmaild->connect($this->server, $this->port, $this->method); + } catch (Net_Vpopmaild_FatalException $e) { + $this->log('Connection to Vpopmaild server failed.', AUTH_LOG_DEBUG); + return PEAR::raiseError($e->getMessage(), $e->getCode()); + } + // Authenticate + try { + $result = $vpopmaild->clogin($username, $password); + $vpopmaild->quit(); + } catch (Net_Vpopmaild_Exception $e) { + return PEAR::raiseError($e->getMessage(), $e->getCode()); + } + return $result; + } +} +?> diff --git a/includes/pear/Auth/Container/PEAR.php b/includes/pear/Auth/Container/PEAR.php new file mode 100644 index 0000000..1de9b82 --- /dev/null +++ b/includes/pear/Auth/Container/PEAR.php @@ -0,0 +1,165 @@ + + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version CVS: $Id: PEAR.php 289652 2009-10-15 04:42:18Z aashley $ + * @link http://pear.php.net/package/Auth + * @since File available since Release 1.3.0 + */ + +/** + * Include PEAR HTTP_Client. + */ +require_once 'HTTP/Client.php'; +/** + * Include Auth_Container base class + */ +require_once 'Auth/Container.php'; + +/** + * Storage driver for authenticating against PEAR website + * + * This driver provides a method for authenticating against the pear.php.net + * authentication system. + * + * Supports two options: + * - "url": The base URL with schema to authenticate against + * - "karma": An array of karma levels which the user needs one of. + * When empty, no karma level is required. + * + * @category Authentication + * @package Auth + * @author Yavor Shahpasov + * @author Adam Ashley + * @author Adam Harvey + * @copyright 2001-2007 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version Release: @package_version@ File: $Revision: 289652 $ + * @link http://pear.php.net/package/Auth + * @since Class available since Release 1.3.0 + */ +class Auth_Container_Pear extends Auth_Container +{ + // {{{ properties + + /** + * URL to connect to, with schema + * + * @var string + */ + var $url = 'https://pear.php.net/rest-login.php/'; + + /** + * Array of karma levels the user can have. + * A user needs only one of the levels to succeed login. + * No levels mean that only username and password need to match + * + * @var array + */ + var $karma = array(); + + // }}} + // {{{ Auth_Container_Pear() [constructor] + + /** + * Constructor + * + * Accepts options "url" and "karma", see class docs. + * + * @param array $data Array of options + * + * @return void + */ + function Auth_Container_Pear($data = null) + { + if (!is_array($data)) { + PEAR::raiseError('The options for Auth_Container_Pear must be an array'); + } + if (isset($data['karma'])) { + if (is_array($data['karma'])) { + $this->karma = $data['karma']; + } else { + $this->karma = array($data['karma']); + } + } + + if (isset($data['url'])) { + $this->url = $data['url']; + } + } + + // }}} + // {{{ fetchData() + + /** + * Get user information from pear.php.net + * + * This function uses the given username and password to authenticate + * against the pear.php.net website + * + * @param string Username + * @param string Password + * @return mixed Error object or boolean + */ + function fetchData($username, $password) + { + $this->log('Auth_Container_PEAR::fetchData() called.', AUTH_LOG_DEBUG); + + $client = new HTTP_Client; + + $this->log('Auth_Container_PEAR::fetchData() getting salt.', AUTH_LOG_DEBUG); + $code = $client->get($this->url . '/getsalt'); + if ($code instanceof PEAR_Error) { + return $code; + } + if ($code != 200) { + return PEAR::raiseError('Bad response to salt request.', $code); + } + $resp = $client->currentResponse(); + $salt = $resp['body']; + + $this->log('Auth_Container_PEAR::fetchData() calling validate.', AUTH_LOG_DEBUG); + $postOptions = array( + 'username' => $username, + 'password' => md5($salt . md5($password)) + ); + if (is_array($this->karma) && count($this->karma) > 0) { + $postOptions['karma'] = implode(',', $this->karma); + } + + $code = $client->post($this->url . '/validate', $postOptions); + if ($code instanceof PEAR_Error) { + return $code; + } + if ($code != 200) { + return PEAR::raiseError('Bad response to validate request.', $code); + } + $resp = $client->currentResponse(); + + list($code, $message) = explode(' ', $resp['body'], 2); + if ($code != 8) { + return PEAR::raiseError($message, $code); + } + return true; + } + + // }}} + +} +?> diff --git a/includes/pear/Auth/Container/POP3.php b/includes/pear/Auth/Container/POP3.php new file mode 100644 index 0000000..0bfab1b --- /dev/null +++ b/includes/pear/Auth/Container/POP3.php @@ -0,0 +1,145 @@ + + * @author Martin Jansen + * @author Mika Tuupola + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version CVS: $Id: POP3.php 237449 2007-06-12 03:11:27Z aashley $ + * @link http://pear.php.net/package/Auth + * @since File available since Release 1.2.0 + */ + +/** + * Include Auth_Container base class + */ +require_once 'Auth/Container.php'; +/** + * Include PEAR package for error handling + */ +require_once 'PEAR.php'; +/** + * Include PEAR Net_POP3 package + */ +require_once 'Net/POP3.php'; + +/** + * Storage driver for Authentication on a POP3 server. + * + * @category Authentication + * @package Auth + * @author Martin Jansen + * @author Mika Tuupola + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version Release: @package_version@ File: $Revision: 237449 $ + * @link http://pear.php.net/package/Auth + * @since Class available since Release 1.2.0 + */ +class Auth_Container_POP3 extends Auth_Container +{ + + // {{{ properties + + /** + * POP3 Server + * @var string + */ + var $server='localhost'; + + /** + * POP3 Server port + * @var string + */ + var $port='110'; + + /** + * POP3 Authentication method + * + * Prefered POP3 authentication method. Acceptable values: + * Boolean TRUE - Use Net_POP3's autodetection + * String 'DIGEST-MD5','CRAM-MD5','LOGIN','PLAIN','APOP','USER' + * - Attempt this authentication style first + * then fallback to autodetection. + * @var mixed + */ + var $method=true; + + // }}} + // {{{ Auth_Container_POP3() [constructor] + + /** + * Constructor of the container class + * + * @param $server string server or server:port combination + * @return object Returns an error object if something went wrong + */ + function Auth_Container_POP3($server=null) + { + if (isset($server) && !is_null($server)) { + if (is_array($server)) { + if (isset($server['host'])) { + $this->server = $server['host']; + } + if (isset($server['port'])) { + $this->port = $server['port']; + } + if (isset($server['method'])) { + $this->method = $server['method']; + } + } else { + if (strstr($server, ':')) { + $serverparts = explode(':', trim($server)); + $this->server = $serverparts[0]; + $this->port = $serverparts[1]; + } else { + $this->server = $server; + } + } + } + } + + // }}} + // {{{ fetchData() + + /** + * Try to login to the POP3 server + * + * @param string Username + * @param string Password + * @return boolean + */ + function fetchData($username, $password) + { + $this->log('Auth_Container_POP3::fetchData() called.', AUTH_LOG_DEBUG); + $pop3 =& new Net_POP3(); + $res = $pop3->connect($this->server, $this->port, $this->method); + if (!$res) { + $this->log('Connection to POP3 server failed.', AUTH_LOG_DEBUG); + return $res; + } + $result = $pop3->login($username, $password); + $pop3->disconnect(); + return $result; + } + + // }}} + +} +?> diff --git a/includes/pear/Auth/Container/RADIUS.php b/includes/pear/Auth/Container/RADIUS.php new file mode 100644 index 0000000..585b641 --- /dev/null +++ b/includes/pear/Auth/Container/RADIUS.php @@ -0,0 +1,182 @@ + + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version CVS: $Id: RADIUS.php 237449 2007-06-12 03:11:27Z aashley $ + * @link http://pear.php.net/package/Auth + * @since File available since Release 1.2.0 + */ + +/** + * Include Auth_Container base class + */ +require_once "Auth/Container.php"; +/** + * Include PEAR Auth_RADIUS package + */ +require_once "Auth/RADIUS.php"; + +/** + * Storage driver for authenticating users against RADIUS servers. + * + * @category Authentication + * @package Auth + * @author Michael Bretterklieber + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version Release: @package_version@ File: $Revision: 237449 $ + * @link http://pear.php.net/package/Auth + * @since Class available since Release 1.2.0 + */ +class Auth_Container_RADIUS extends Auth_Container +{ + + // {{{ properties + + /** + * Contains a RADIUS object + * @var object + */ + var $radius; + + /** + * Contains the authentication type + * @var string + */ + var $authtype; + + // }}} + // {{{ Auth_Container_RADIUS() [constructor] + + /** + * Constructor of the container class. + * + * $options can have these keys: + * 'servers' an array containing an array: servername, port, + * sharedsecret, timeout, maxtries + * 'configfile' The filename of the configuration file + * 'authtype' The type of authentication, one of: PAP, CHAP_MD5, + * MSCHAPv1, MSCHAPv2, default is PAP + * + * @param $options associative array + * @return object Returns an error object if something went wrong + */ + function Auth_Container_RADIUS($options) + { + $this->authtype = 'PAP'; + if (isset($options['authtype'])) { + $this->authtype = $options['authtype']; + } + $classname = 'Auth_RADIUS_' . $this->authtype; + if (!class_exists($classname)) { + PEAR::raiseError("Unknown Authtype, please use one of: " + ."PAP, CHAP_MD5, MSCHAPv1, MSCHAPv2!", 41, PEAR_ERROR_DIE); + } + + $this->radius = new $classname; + + if (isset($options['configfile'])) { + $this->radius->setConfigfile($options['configfile']); + } + + $servers = $options['servers']; + if (is_array($servers)) { + foreach ($servers as $server) { + $servername = $server[0]; + $port = isset($server[1]) ? $server[1] : 0; + $sharedsecret = isset($server[2]) ? $server[2] : 'testing123'; + $timeout = isset($server[3]) ? $server[3] : 3; + $maxtries = isset($server[4]) ? $server[4] : 3; + $this->radius->addServer($servername, $port, $sharedsecret, $timeout, $maxtries); + } + } + + if (!$this->radius->start()) { + PEAR::raiseError($this->radius->getError(), 41, PEAR_ERROR_DIE); + } + } + + // }}} + // {{{ fetchData() + + /** + * Authenticate + * + * @param string Username + * @param string Password + * @return bool true on success, false on reject + */ + function fetchData($username, $password, $challenge = null) + { + $this->log('Auth_Container_RADIUS::fetchData() called.', AUTH_LOG_DEBUG); + + switch($this->authtype) { + case 'CHAP_MD5': + case 'MSCHAPv1': + if (isset($challenge)) { + $this->radius->challenge = $challenge; + $this->radius->chapid = 1; + $this->radius->response = pack('H*', $password); + } else { + require_once 'Crypt/CHAP.php'; + $classname = 'Crypt_' . $this->authtype; + $crpt = new $classname; + $crpt->password = $password; + $this->radius->challenge = $crpt->challenge; + $this->radius->chapid = $crpt->chapid; + $this->radius->response = $crpt->challengeResponse(); + } + break; + + case 'MSCHAPv2': + require_once 'Crypt/CHAP.php'; + $crpt = new Crypt_MSCHAPv2; + $crpt->username = $username; + $crpt->password = $password; + $this->radius->challenge = $crpt->authChallenge; + $this->radius->peerChallenge = $crpt->peerChallenge; + $this->radius->chapid = $crpt->chapid; + $this->radius->response = $crpt->challengeResponse(); + break; + + default: + $this->radius->password = $password; + break; + } + + $this->radius->username = $username; + + $this->radius->putAuthAttributes(); + $result = $this->radius->send(); + if (PEAR::isError($result)) { + return false; + } + + $this->radius->getAttributes(); +// just for debugging +// $this->radius->dumpAttributes(); + + return $result; + } + + // }}} + +} +?> diff --git a/includes/pear/Auth/Container/SAP.php b/includes/pear/Auth/Container/SAP.php new file mode 100644 index 0000000..58ce168 --- /dev/null +++ b/includes/pear/Auth/Container/SAP.php @@ -0,0 +1,179 @@ + + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version CVS: $Id: SAP.php 302205 2010-08-14 14:08:08Z clockwerx $ + * @link http://pear.php.net/package/Auth + * @since File available since Release 1.4.0 + */ + +/** + * Include Auth_Container base class + */ +require_once 'Auth/Container.php'; +/** + * Include PEAR for error handling + */ +require_once 'PEAR.php'; + +/** + * Performs authentication against a SAP system using the SAPRFC PHP extension. + * + * When the option GETSSO2 is TRUE (default) + * the Single Sign-On (SSO) ticket is retrieved + * and stored as an Auth attribute called 'sap' + * in order to be reused for consecutive connections. + * + * @category Authentication + * @package Auth + * @author Stoyan Stefanov + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version Release: @package_version@ File: $Revision: 302205 $ + * @since Class available since Release 1.4.0 + */ +class Auth_Container_SAP extends Auth_Container { + + // {{{ properties + + /** + * @var array Default options + */ + var $options = array( + 'CLIENT' => '000', + 'LANG' => 'EN', + 'GETSSO2' => true, + ); + + // }}} + // {{{ Auth_Container_SAP() + + /** + * Class constructor. Checks that required options + * are present and that the SAPRFC extension is loaded + * + * Options that can be passed and their defaults: + *
    +     * array(
    +     *   'ASHOST' => "",
    +     *   'SYSNR'  => "",
    +     *   'CLIENT' => "000",
    +     *   'GWHOST' =>"",
    +     *   'GWSERV' =>"",
    +     *   'MSHOST' =>"",
    +     *   'R3NAME' =>"",
    +     *   'GROUP'  =>"",
    +     *   'LANG'   =>"EN",
    +     *   'TRACE'  =>"",
    +     *   'GETSSO2'=> true
    +     * )
    +     * 
    + * + * @param array array of options. + * @return void + */ + function Auth_Container_SAP($options) + { + $saprfc_loaded = PEAR::loadExtension('saprfc'); + if (!$saprfc_loaded) { + return PEAR::raiseError('Cannot use SAP authentication, ' + .'SAPRFC extension not loaded!'); + } + if (empty($options['R3NAME']) && empty($options['ASHOST'])) { + return PEAR::raiseError('R3NAME or ASHOST required for authentication'); + } + $this->options = array_merge($this->options, $options); + } + + // }}} + // {{{ fetchData() + + /** + * Performs username and password check + * + * @param string Username + * @param string Password + * @return boolean TRUE on success (valid user), FALSE otherwise + */ + function fetchData($username, $password) + { + $this->log('Auth_Container_SAP::fetchData() called.', AUTH_LOG_DEBUG); + $connection_options = $this->options; + $connection_options['USER'] = $username; + $connection_options['PASSWD'] = $password; + $rfc = saprfc_open($connection_options); + if (!$rfc) { + $message = "Couldn't connect to the SAP system."; + $error = $this->getError(); + if ($error['message']) { + $message .= ': ' . $error['message']; + } + PEAR::raiseError($message, null, null, null, @$error['all']); + return false; + } else { + if (!empty($this->options['GETSSO2'])) { + $this->log('Attempting to retrieve SSO2 ticket.', AUTH_LOG_DEBUG); + if ($ticket = @saprfc_get_ticket($rfc)) { + $this->options['MYSAPSSO2'] = $ticket; + unset($this->options['GETSSO2']); + $this->_auth_obj->setAuthData('sap', $this->options); + } else { + PEAR::raiseError("SSO ticket retrieval failed"); + } + } + @saprfc_close($rfc); + return true; + } + + } + + // }}} + // {{{ getError() + + /** + * Retrieves the last error from the SAP connection + * and returns it as an array. + * + * @return array Array of error information + */ + function getError() + { + + $error = array(); + $sap_error = saprfc_error(); + if (empty($err)) { + return $error; + } + $err = explode("n", $sap_error); + foreach ($err AS $line) { + $item = explode(':', $line); + $error[strtolower(trim($item[0]))] = trim($item[1]); + } + $error['all'] = $sap_error; + return $error; + } + + // }}} + +} + +?> diff --git a/includes/pear/Auth/Container/SMBPasswd.php b/includes/pear/Auth/Container/SMBPasswd.php new file mode 100644 index 0000000..15c3e75 --- /dev/null +++ b/includes/pear/Auth/Container/SMBPasswd.php @@ -0,0 +1,182 @@ + + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version CVS: $Id: SMBPasswd.php 237449 2007-06-12 03:11:27Z aashley $ + * @link http://pear.php.net/package/Auth + * @since File available since Release 1.2.3 + */ + +/** + * Include PEAR File_SMBPasswd + */ +require_once "File/SMBPasswd.php"; +/** + * Include Auth_Container Base file + */ +require_once "Auth/Container.php"; +/** + * Include PEAR class for error handling + */ +require_once "PEAR.php"; + +/** + * Storage driver for fetching login data from an SAMBA smbpasswd file. + * + * This storage container can handle SAMBA smbpasswd files. + * + * Example: + * $a = new Auth("SMBPasswd", '/usr/local/private/smbpasswd'); + * $a->start(); + * if ($a->getAuth()) { + * printf ("AUTH OK
    \n"); + * $a->logout(); + * } + * + * @category Authentication + * @package Auth + * @author Michael Bretterklieber + * @author Adam Ashley + * @package Auth + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version Release: @package_version@ File: $Revision: 237449 $ + * @link http://pear.php.net/package/Auth + * @since Class available since Release 1.2.3 + */ +class Auth_Container_SMBPasswd extends Auth_Container +{ + + // {{{ properties + + /** + * File_SMBPasswd object + * @var object + */ + var $pwfile; + + // }}} + + // {{{ Auth_Container_SMBPasswd() [constructor] + + /** + * Constructor of the container class + * + * @param $filename string filename for a passwd type file + * @return object Returns an error object if something went wrong + */ + function Auth_Container_SMBPasswd($filename) + { + $this->pwfile = new File_SMBPasswd($filename,0); + + if (!$this->pwfile->load()) { + PEAR::raiseError("Error while reading file contents.", 41, PEAR_ERROR_DIE); + return; + } + + } + + // }}} + // {{{ fetchData() + + /** + * Get user information from pwfile + * + * @param string Username + * @param string Password + * @return boolean + */ + function fetchData($username, $password) + { + $this->log('Auth_Container_SMBPasswd::fetchData() called.', AUTH_LOG_DEBUG); + return $this->pwfile->verifyAccount($username, $password); + } + + // }}} + // {{{ listUsers() + + function listUsers() + { + $this->log('Auth_Container_SMBPasswd::fetchData() called.', AUTH_LOG_DEBUG); + return $this->pwfile->getAccounts(); + } + + // }}} + // {{{ addUser() + + /** + * Add a new user to the storage container + * + * @param string Username + * @param string Password + * @param array Additional information + * + * @return boolean + */ + function addUser($username, $password, $additional = '') + { + $this->log('Auth_Container_SMBPasswd::addUser() called.', AUTH_LOG_DEBUG); + $res = $this->pwfile->addUser($user, $additional['userid'], $pass); + if ($res === true) { + return $this->pwfile->save(); + } + return $res; + } + + // }}} + // {{{ removeUser() + + /** + * Remove user from the storage container + * + * @param string Username + */ + function removeUser($username) + { + $this->log('Auth_Container_SMBPasswd::removeUser() called.', AUTH_LOG_DEBUG); + $res = $this->pwfile->delUser($username); + if ($res === true) { + return $this->pwfile->save(); + } + return $res; + } + + // }}} + // {{{ changePassword() + + /** + * Change password for user in the storage container + * + * @param string Username + * @param string The new password + */ + function changePassword($username, $password) + { + $this->log('Auth_Container_SMBPasswd::changePassword() called.', AUTH_LOG_DEBUG); + $res = $this->pwfile->modUser($username, '', $password); + if ($res === true) { + return $this->pwfile->save(); + } + return $res; + } + + // }}} + +} +?> diff --git a/includes/pear/Auth/Container/SOAP.php b/includes/pear/Auth/Container/SOAP.php new file mode 100644 index 0000000..2e11e6b --- /dev/null +++ b/includes/pear/Auth/Container/SOAP.php @@ -0,0 +1,229 @@ + + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version CVS: $Id: SOAP.php 237449 2007-06-12 03:11:27Z aashley $ + * @link http://pear.php.net/package/Auth + * @since File available since Release 1.2.0 + */ + +/** + * Include Auth_Container base class + */ +require_once "Auth/Container.php"; +/** + * Include PEAR package for error handling + */ +require_once "PEAR.php"; +/** + * Include PEAR SOAP_Client + */ +require_once 'SOAP/Client.php'; + +/** + * Storage driver for fetching login data from SOAP + * + * This class takes one parameter (options), where + * you specify the following fields: endpoint, namespace, + * method, encoding, usernamefield and passwordfield. + * + * You can use specify features of your SOAP service + * by providing its parameters in an associative manner by + * using the '_features' array through the options parameter. + * + * The 'matchpassword' option should be set to false if your + * webservice doesn't return (username,password) pairs, but + * instead returns error when the login is invalid. + * + * Example usage: + * + * 'http://your.soap.service/endpoint', + * 'namespace' => 'urn:/Your/Namespace', + * 'method' => 'get', + * 'encoding' => 'UTF-8', + * 'usernamefield' => 'login', + * 'passwordfield' => 'password', + * 'matchpasswords' => false, + * '_features' => array ( + * 'example_feature' => 'example_value', + * 'another_example' => '' + * ) + * ); + * $auth = new Auth('SOAP', $options, 'loginFunction'); + * $auth->start(); + * + * ... + * + * ?> + * + * @category Authentication + * @package Auth + * @author Bruno Pedro + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version Release: @package_version@ File: $Revision: 237449 $ + * @link http://pear.php.net/package/Auth + * @since Class available since Release 1.2.0 + */ +class Auth_Container_SOAP extends Auth_Container +{ + + // {{{ properties + + /** + * Required options for the class + * @var array + * @access private + */ + var $_requiredOptions = array( + 'endpoint', + 'namespace', + 'method', + 'encoding', + 'usernamefield', + 'passwordfield', + ); + + /** + * Options for the class + * @var array + * @access private + */ + var $_options = array(); + + /** + * Optional SOAP features + * @var array + * @access private + */ + var $_features = array(); + + /** + * The SOAP response + * @var array + * @access public + */ + var $soapResponse = array(); + + /** + * The SOAP client + * @var mixed + * @access public + */ + var $soapClient = null; + + // }}} + // {{{ Auth_Container_SOAP() [constructor] + + /** + * Constructor of the container class + * + * @param $options, associative array with endpoint, namespace, method, + * usernamefield, passwordfield and optional features + */ + function Auth_Container_SOAP($options) + { + $this->_options = $options; + if (!isset($this->_options['matchpasswords'])) { + $this->_options['matchpasswords'] = true; + } + if (!empty($this->_options['_features'])) { + $this->_features = $this->_options['_features']; + unset($this->_options['_features']); + } + } + + // }}} + // {{{ fetchData() + + /** + * Fetch data from SOAP service + * + * Requests the SOAP service for the given username/password + * combination. + * + * @param string Username + * @param string Password + * @return mixed Returns the SOAP response or false if something went wrong + */ + function fetchData($username, $password) + { + $this->log('Auth_Container_SOAP::fetchData() called.', AUTH_LOG_DEBUG); + // check if all required options are set + if (array_intersect($this->_requiredOptions, array_keys($this->_options)) != $this->_requiredOptions) { + return false; + } else { + // create a SOAP client and set encoding + $this->soapClient = new SOAP_Client($this->_options['endpoint']); + $this->soapClient->setEncoding($this->_options['encoding']); + } + + // set the trace option if requested + if (isset($this->_options['trace'])) { + $this->soapClient->__options['trace'] = true; + } + + // set the timeout option if requested + if (isset($this->_options['timeout'])) { + $this->soapClient->__options['timeout'] = $this->_options['timeout']; + } + + // assign username and password fields + $usernameField = new SOAP_Value($this->_options['usernamefield'],'string', $username); + $passwordField = new SOAP_Value($this->_options['passwordfield'],'string', $password); + $SOAPParams = array($usernameField, $passwordField); + + // assign optional features + foreach ($this->_features as $fieldName => $fieldValue) { + $SOAPParams[] = new SOAP_Value($fieldName, 'string', $fieldValue); + } + + // make SOAP call + $this->soapResponse = $this->soapClient->call( + $this->_options['method'], + $SOAPParams, + array('namespace' => $this->_options['namespace']) + ); + + if (!PEAR::isError($this->soapResponse)) { + if ($this->_options['matchpasswords']) { + // check if passwords match + if ($password == $this->soapResponse->{$this->_options['passwordfield']}) { + return true; + } else { + return false; + } + } else { + return true; + } + } else { + return false; + } + } + + // }}} + +} +?> diff --git a/includes/pear/Auth/Container/SOAP5.php b/includes/pear/Auth/Container/SOAP5.php new file mode 100644 index 0000000..698c685 --- /dev/null +++ b/includes/pear/Auth/Container/SOAP5.php @@ -0,0 +1,268 @@ + + * @author Marcel Oelke + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version CVS: $Id: SOAP5.php 238999 2007-07-02 08:25:41Z aashley $ + * @since File available since Release 1.4.0 + */ + +/** + * Include Auth_Container base class + */ +require_once "Auth/Container.php"; +/** + * Include PEAR package for error handling + */ +require_once "PEAR.php"; + +/** + * Storage driver for fetching login data from SOAP using the PHP5 Builtin SOAP + * functions. This is a modification of the SOAP Storage driver from Bruno Pedro + * thats using the PEAR SOAP Package. + * + * This class takes one parameter (options), where + * you specify the following fields: + * * location and uri, or wsdl file + * * method to call on the SOAP service + * * usernamefield, the name of the parameter where the username is supplied + * * passwordfield, the name of the parameter where the password is supplied + * * matchpassword, whether to look for the password in the response from + * the function call or assume that no errors means user + * authenticated. + * + * See http://www.php.net/manual/en/ref.soap.php for further details + * on options for the PHP5 SoapClient which are passed through. + * + * Example usage without WSDL: + * + * NULL, + * 'location' => 'http://your.soap.service/endpoint', + * 'uri' => 'urn:/Your/Namespace', + * 'method' => 'checkAuth', + * 'usernamefield' => 'username', + * 'passwordfield' => 'password', + * 'matchpasswords' => false, + * '_features' => array ( + * 'extra_parameter' => 'example_value', + * 'another_parameter' => 'foobar' + * ) + * ); + * + * $auth = new Auth('SOAP5', $options); + * $auth->start(); + * + * ?> + * + * Example usage with WSDL: + * + * 'http://your.soap.service/wsdl', + * 'method' => 'checkAuth', + * 'usernamefield' => 'username', + * 'passwordfield' => 'password', + * 'matchpasswords' => false, + * '_features' => array ( + * 'extra_parameter' => 'example_value', + * 'another_parameter' => 'foobar' + * ) + * ); + * + * $auth = new Auth('SOAP5', $options); + * $auth->start(); + * + * ?> + * + * @category Authentication + * @package Auth + * @author Based upon Auth_Container_SOAP by Bruno Pedro + * @author Marcel Oelke + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version Release: @package_version@ File: $Revision: 238999 $ + * @since Class available since Release 1.4.0 + */ +class Auth_Container_SOAP5 extends Auth_Container +{ + + // {{{ properties + + /** + * Required options for the class + * @var array + * @access private + */ + var $_requiredOptions = array( + 'location', + 'uri', + 'method', + 'usernamefield', + 'passwordfield', + 'wsdl', + ); + + /** + * Options for the class + * @var array + * @access private + */ + var $_options = array(); + + /** + * Optional SOAP features + * @var array + * @access private + */ + var $_features = array(); + + /** + * The SOAP response + * @var array + * @access public + */ + var $soapResponse = array(); + + // }}} + // {{{ Auth_Container_SOAP5() + + /** + * Constructor of the container class + * + * @param $options, associative array with endpoint, namespace, method, + * usernamefield, passwordfield and optional features + */ + function Auth_Container_SOAP5($options) + { + $this->_setDefaults(); + + foreach ($options as $name => $value) { + $this->_options[$name] = $value; + } + + if (!empty($this->_options['_features'])) { + $this->_features = $this->_options['_features']; + unset($this->_options['_features']); + } + } + + // }}} + // {{{ fetchData() + + /** + * Fetch data from SOAP service + * + * Requests the SOAP service for the given username/password + * combination. + * + * @param string Username + * @param string Password + * @return mixed Returns the SOAP response or false if something went wrong + */ + function fetchData($username, $password) + { + $this->log('Auth_Container_SOAP5::fetchData() called.', AUTH_LOG_DEBUG); + $result = $this->_validateOptions(); + if (PEAR::isError($result)) + return $result; + + // create a SOAP client + $soapClient = new SoapClient($this->_options["wsdl"], $this->_options); + + $params = array(); + // first, assign the optional features + foreach ($this->_features as $fieldName => $fieldValue) { + $params[$fieldName] = $fieldValue; + } + // assign username and password ... + $params[$this->_options['usernamefield']] = $username; + $params[$this->_options['passwordfield']] = $password; + + try { + $this->soapResponse = $soapClient->__soapCall($this->_options['method'], $params); + + if ($this->_options['matchpasswords']) { + // check if passwords match + if ($password == $this->soapResponse[$this->_options['passwordfield']]) { + return true; + } else { + return false; + } + } else { + return true; + } + } catch (SoapFault $e) { + return PEAR::raiseError("Error retrieving authentication data. Received SOAP Fault: ".$e->faultstring, $e->faultcode); + } + } + + // }}} + // {{{ _validateOptions() + + /** + * Validate that the options passed to the container class are enough for us to proceed + * + * @access private + * @param array + */ + function _validateOptions() + { + if ( ( is_null($this->_options['wsdl']) + && is_null($this->_options['location']) + && is_null($this->_options['uri'])) + || ( is_null($this->_options['wsdl']) + && ( is_null($this->_options['location']) + || is_null($this->_options['uri'])))) { + return PEAR::raiseError('Either a WSDL file or a location/uri pair must be specified.'); + } + if (is_null($this->_options['method'])) { + return PEAR::raiseError('A method to call on the soap service must be specified.'); + } + return true; + } + + // }}} + // {{{ _setDefaults() + + /** + * Set some default options + * + * @access private + * @return void + */ + function _setDefaults() + { + $this->_options['wsdl'] = null; + $this->_options['location'] = null; + $this->_options['uri'] = null; + $this->_options['method'] = null; + $this->_options['usernamefield'] = 'username'; + $this->_options['passwordfield'] = 'password'; + $this->_options['matchpasswords'] = true; + } + + // }}} + +} +?> diff --git a/includes/pear/Auth/Container/vpopmail.php b/includes/pear/Auth/Container/vpopmail.php new file mode 100644 index 0000000..2350ca7 --- /dev/null +++ b/includes/pear/Auth/Container/vpopmail.php @@ -0,0 +1,88 @@ + + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version CVS: $Id: vpopmail.php 237449 2007-06-12 03:11:27Z aashley $ + * @link http://pear.php.net/package/Auth + * @since File available since Release 1.2.0 + */ + +/** + * Include Auth_Container base class + */ +require_once "Auth/Container.php"; +/** + * Include PEAR package for error handling + */ +require_once "PEAR.php"; + +/** + * Storage driver for fetching login data from vpopmail + * + * @category Authentication + * @package Auth + * @author Stanislav Grozev + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version Release: @package_version@ File: $Revision: 237449 $ + * @link http://pear.php.net/package/Auth + * @since Class available since Release 1.2.0 + */ +class Auth_Container_vpopmail extends Auth_Container { + + // {{{ Constructor + + /** + * Constructor of the container class + * + * @return void + */ + function Auth_Container_vpopmail() + { + if (!extension_loaded('vpopmail')) { + return PEAR::raiseError('Cannot use VPOPMail authentication, ' + .'VPOPMail extension not loaded!', 41, PEAR_ERROR_DIE); + } + } + + // }}} + // {{{ fetchData() + + /** + * Get user information from vpopmail + * + * @param string Username - has to be valid email address + * @param string Password + * @return boolean + */ + function fetchData($username, $password) + { + $this->log('Auth_Container_vpopmail::fetchData() called.', AUTH_LOG_DEBUG); + $userdata = array(); + $userdata = preg_split("/@/", $username, 2); + $result = @vpopmail_auth_user($userdata[0], $userdata[1], $password); + + return $result; + } + + // }}} + +} +?> diff --git a/includes/pear/Auth/Controller.php b/includes/pear/Auth/Controller.php new file mode 100644 index 0000000..92d9f80 --- /dev/null +++ b/includes/pear/Auth/Controller.php @@ -0,0 +1,302 @@ + + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version CVS: $Id: Controller.php 237449 2007-06-12 03:11:27Z aashley $ + * @link http://pear.php.net/package/Auth + * @since File available since Release 1.3.0 + */ + +/** + * Controlls access to a group of php access + * and redirects to a predefined login page as + * needed + * + * In all pages + * + * include_once('Auth.php'); + * include_once('Auth/Controller.php'); + * $_auth = new Auth('File', 'passwd'); + * $authController = new Auth_Controller($_auth, 'login.php', 'index.php'); + * $authController->start(); + * + * + * In login.php + * + * include_once('Auth.php'); + * include_once('Auth/Controller.php'); + * $_auth = new Auth('File', 'passwd'); + * $authController = new Auth_Controller($_auth, 'login.php', 'index.php'); + * $authController->start(); + * if( $authController->isAuthorised() ){ + * $authController->redirectBack(); + * } + * + * + * @category Authentication + * @author Yavor Shahpasov + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version Release: @package_version@ File: $Revision: 237449 $ + * @link http://pear.php.net/package/Auth + * @since Class available since Release 1.3.0 + */ +class Auth_Controller +{ + + // {{{ properties + + /** + * The Auth instance this controller is managing + * + * @var object Auth + */ + var $auth = null; + + /** + * The login URL + * @var string + * */ + var $login = null; + + /** + * The default index page to use when the caller page is not set + * + * @var string + */ + var $default = null; + + /** + * If this is set to true after a succesfull login the + * Auth_Controller::redirectBack() is invoked automatically + * + * @var boolean + */ + var $autoRedirectBack = false; + + // }}} + // {{{ Auth_Controller() [constructor] + + /** + * Constructor + * + * @param Auth An auth instance + * @param string The login page + * @param string The default page to go to if return page is not set + * @param array Some rules about which urls need to be sent to the login page + * @return void + * @todo Add a list of urls which need redirection + */ + function Auth_Controller(&$auth_obj, $login='login.php', $default='index.php', $accessList=array()) + { + $this->auth =& $auth_obj; + $this->_loginPage = $login; + $this->_defaultPage = $default; + @session_start(); + if (!empty($_GET['return']) && $_GET['return'] && !strstr($_GET['return'], $this->_loginPage)) { + $this->auth->setAuthData('returnUrl', $_GET['return']); + } + + if(!empty($_GET['authstatus']) && $this->auth->status == '') { + $this->auth->status = $_GET['authstatus']; + } + } + + // }}} + // {{{ setAutoRedirectBack() + + /** + * Enables auto redirection when login is done + * + * @param bool Sets the autoRedirectBack flag to this + * @see Auth_Controller::autoRedirectBack + * @return void + */ + function setAutoRedirectBack($flag = true) + { + $this->autoRedirectBack = $flag; + } + + // }}} + // {{{ redirectBack() + + /** + * Redirects Back to the calling page + * + * @return void + */ + function redirectBack() + { + // If redirectback go there + // else go to the default page + + $returnUrl = $this->auth->getAuthData('returnUrl'); + if(!$returnUrl) { + $returnUrl = $this->_defaultPage; + } + + // Add some entropy to the return to make it unique + // avoind problems with cached pages and proxies + if(strpos($returnUrl, '?') === false) { + $returnUrl .= '?'; + } + $returnUrl .= uniqid(''); + + // Track the auth status + if($this->auth->status != '') { + $url .= '&authstatus='.$this->auth->status; + } + header('Location:'.$returnUrl); + print("You could not be redirected to $returnUrl"); + } + + // }}} + // {{{ redirectLogin() + + /** + * Redirects to the login Page if not authorised + * + * put return page on the query or in auth + * + * @return void + */ + function redirectLogin() + { + // Go to the login Page + + // For Auth, put some check to avoid infinite redirects, this should at least exclude + // the login page + + $url = $this->_loginPage; + if(strpos($url, '?') === false) { + $url .= '?'; + } + + if(!strstr($_SERVER['PHP_SELF'], $this->_loginPage)) { + $url .= 'return='.urlencode($_SERVER['PHP_SELF']); + } + + // Track the auth status + if($this->auth->status != '') { + $url .= '&authstatus='.$this->auth->status; + } + + header('Location:'.$url); + print("You could not be redirected to $url"); + } + + // }}} + // {{{ start() + + /** + * Starts the Auth Procedure + * + * If the page requires login the user is redirected to the login page + * otherwise the Auth::start is called to initialize Auth + * + * @return void + * @todo Implement an access list which specifies which urls/pages need login and which do not + */ + function start() + { + // Check the accessList here + // ACL should be a list of urls with allow/deny + // If allow set allowLogin to false + // Some wild card matching should be implemented ?,* + if(!strstr($_SERVER['PHP_SELF'], $this->_loginPage) && !$this->auth->checkAuth()) { + $this->redirectLogin(); + } else { + $this->auth->start(); + // Logged on and on login page + if(strstr($_SERVER['PHP_SELF'], $this->_loginPage) && $this->auth->checkAuth()){ + $this->autoRedirectBack ? + $this->redirectBack() : + null ; + } + } + + + } + + // }}} + // {{{ isAuthorised() + + /** + * Checks is the user is logged on + * @see Auth::checkAuth() + */ + function isAuthorised() + { + return($this->auth->checkAuth()); + } + + // }}} + // {{{ checkAuth() + + /** + * Proxy call to auth + * @see Auth::checkAuth() + */ + function checkAuth() + { + return($this->auth->checkAuth()); + } + + // }}} + // {{{ logout() + + /** + * Proxy call to auth + * @see Auth::logout() + */ + function logout() + { + return($this->auth->logout()); + } + + // }}} + // {{{ getUsername() + + /** + * Proxy call to auth + * @see Auth::getUsername() + */ + function getUsername() + { + return($this->auth->getUsername()); + } + + // }}} + // {{{ getStatus() + + /** + * Proxy call to auth + * @see Auth::getStatus() + */ + function getStatus() + { + return($this->auth->getStatus()); + } + + // }}} + +} + +?> diff --git a/includes/pear/Auth/Frontend/Html.php b/includes/pear/Auth/Frontend/Html.php new file mode 100644 index 0000000..6719e92 --- /dev/null +++ b/includes/pear/Auth/Frontend/Html.php @@ -0,0 +1,142 @@ + + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version CVS: $Id: Html.php 237449 2007-06-12 03:11:27Z aashley $ + * @link http://pear.php.net/package/Auth + * @since File available since Release 1.3.0 + */ + +/** + * Standard Html Login form + * + * @category Authentication + * @package Auth + * @author Yavor Shahpasov + * @author Adam Ashley + * @copyright 2001-2006 The PHP Group + * @license http://www.php.net/license/3_01.txt PHP License 3.01 + * @version Release: @package_version@ File: $Revision: 237449 $ + * @link http://pear.php.net/package/Auth + * @since Class available since Release 1.3.0 + */ +class Auth_Frontend_Html { + + // {{{ render() + + /** + * Displays the login form + * + * @param object The calling auth instance + * @param string The previously used username + * @return void + */ + function render(&$caller, $username = '') { + $loginOnClick = 'return true;'; + + // Try To Use Challene response + // TODO javascript might need some improvement for work on other browsers + if($caller->advancedsecurity && $caller->storage->supportsChallengeResponse() ) { + + // Init the secret cookie + $caller->session['loginchallenege'] = md5(microtime()); + + print "\n"; + print ''."\n";; + print "\n"; + + $loginOnClick = ' return securePassword(); '; + } + + print '
    '."\n"; + + $status = ''; + if (!empty($caller->status) && $caller->status == AUTH_EXPIRED) { + $status = 'Your session has expired. Please login again!'."\n"; + } else if (!empty($caller->status) && $caller->status == AUTH_IDLED) { + $status = 'You have been idle for too long. Please login again!'."\n"; + } else if (!empty ($caller->status) && $caller->status == AUTH_WRONG_LOGIN) { + $status = 'Wrong login data!'."\n"; + } else if (!empty ($caller->status) && $caller->status == AUTH_SECURITY_BREACH) { + $status = 'Security problem detected. '."\n"; + } + + print '
    '."\n"; + print '
    '."\n"; + print ''."\n"; + print ' '."\n"; + print ''."\n"; + print ''."\n"; + print ' '."\n"; + print ' '."\n"; + print ''."\n"; + print ''."\n"; + print ' '."\n"; + print ' '."\n"; + print ''."\n"; + print ''."\n"; + + //onClick=" '.$loginOnClick.' " + print ' '."\n"; + print ''."\n"; + print '
    Login ' + .$status.'
    Username:
    Password:
    '."\n"; + + // Might be a good idea to make the variable name variable + print ''; + print ''."\n"; + print ''."\n"; + } + + // }}} + +} + +?> diff --git a/includes/pear/Console/Getopt.php b/includes/pear/Console/Getopt.php new file mode 100644 index 0000000..eb65df2 --- /dev/null +++ b/includes/pear/Console/Getopt.php @@ -0,0 +1,360 @@ + + * @license http://www.php.net/license/3_0.txt PHP 3.0 + * @version CVS: $Id: Getopt.php 306067 2010-12-08 00:13:31Z dufuz $ + * @link http://pear.php.net/package/Console_Getopt + */ + +require_once 'PEAR.php'; + +/** + * Command-line options parsing class. + * + * @category Console + * @package Console_Getopt + * @author Andrei Zmievski + * @license http://www.php.net/license/3_0.txt PHP 3.0 + * @link http://pear.php.net/package/Console_Getopt + */ +class Console_Getopt +{ + + /** + * Parses the command-line options. + * + * The first parameter to this function should be the list of command-line + * arguments without the leading reference to the running program. + * + * The second parameter is a string of allowed short options. Each of the + * option letters can be followed by a colon ':' to specify that the option + * requires an argument, or a double colon '::' to specify that the option + * takes an optional argument. + * + * The third argument is an optional array of allowed long options. The + * leading '--' should not be included in the option name. Options that + * require an argument should be followed by '=', and options that take an + * option argument should be followed by '=='. + * + * The return value is an array of two elements: the list of parsed + * options and the list of non-option command-line arguments. Each entry in + * the list of parsed options is a pair of elements - the first one + * specifies the option, and the second one specifies the option argument, + * if there was one. + * + * Long and short options can be mixed. + * + * Most of the semantics of this function are based on GNU getopt_long(). + * + * @param array $args an array of command-line arguments + * @param string $short_options specifies the list of allowed short options + * @param array $long_options specifies the list of allowed long options + * @param boolean $skip_unknown suppresses Console_Getopt: unrecognized option + * + * @return array two-element array containing the list of parsed options and + * the non-option arguments + */ + public static function getopt2($args, $short_options, $long_options = null, $skip_unknown = false) + { + return Console_Getopt::doGetopt(2, $args, $short_options, $long_options, $skip_unknown); + } + + /** + * This function expects $args to start with the script name (POSIX-style). + * Preserved for backwards compatibility. + * + * @param array $args an array of command-line arguments + * @param string $short_options specifies the list of allowed short options + * @param array $long_options specifies the list of allowed long options + * + * @see getopt2() + * @return array two-element array containing the list of parsed options and + * the non-option arguments + */ + public static function getopt($args, $short_options, $long_options = null, $skip_unknown = false) + { + return Console_Getopt::doGetopt(1, $args, $short_options, $long_options, $skip_unknown); + } + + /** + * The actual implementation of the argument parsing code. + * + * @param int $version Version to use + * @param array $args an array of command-line arguments + * @param string $short_options specifies the list of allowed short options + * @param array $long_options specifies the list of allowed long options + * @param boolean $skip_unknown suppresses Console_Getopt: unrecognized option + * + * @return array + */ + public static function doGetopt($version, $args, $short_options, $long_options = null, $skip_unknown = false) + { + // in case you pass directly readPHPArgv() as the first arg + if (PEAR::isError($args)) { + return $args; + } + + if (empty($args)) { + return array(array(), array()); + } + + $non_opts = $opts = array(); + + settype($args, 'array'); + + if ($long_options) { + sort($long_options); + } + + /* + * Preserve backwards compatibility with callers that relied on + * erroneous POSIX fix. + */ + if ($version < 2) { + if (isset($args[0]{0}) && $args[0]{0} != '-') { + array_shift($args); + } + } + + reset($args); + while (list($i, $arg) = each($args)) { + /* The special element '--' means explicit end of + options. Treat the rest of the arguments as non-options + and end the loop. */ + if ($arg == '--') { + $non_opts = array_merge($non_opts, array_slice($args, $i + 1)); + break; + } + + if ($arg{0} != '-' || (strlen($arg) > 1 && $arg{1} == '-' && !$long_options)) { + $non_opts = array_merge($non_opts, array_slice($args, $i)); + break; + } elseif (strlen($arg) > 1 && $arg{1} == '-') { + $error = Console_Getopt::_parseLongOption(substr($arg, 2), + $long_options, + $opts, + $args, + $skip_unknown); + if (PEAR::isError($error)) { + return $error; + } + } elseif ($arg == '-') { + // - is stdin + $non_opts = array_merge($non_opts, array_slice($args, $i)); + break; + } else { + $error = Console_Getopt::_parseShortOption(substr($arg, 1), + $short_options, + $opts, + $args, + $skip_unknown); + if (PEAR::isError($error)) { + return $error; + } + } + } + + return array($opts, $non_opts); + } + + /** + * Parse short option + * + * @param string $arg Argument + * @param string[] $short_options Available short options + * @param string[][] &$opts + * @param string[] &$args + * @param boolean $skip_unknown suppresses Console_Getopt: unrecognized option + * + * @return void + */ + public static function _parseShortOption($arg, $short_options, &$opts, &$args, $skip_unknown) + { + for ($i = 0; $i < strlen($arg); $i++) { + $opt = $arg{$i}; + $opt_arg = null; + + /* Try to find the short option in the specifier string. */ + if (($spec = strstr($short_options, $opt)) === false || $arg{$i} == ':') { + if ($skip_unknown === true) { + break; + } + + $msg = "Console_Getopt: unrecognized option -- $opt"; + return PEAR::raiseError($msg); + } + + if (strlen($spec) > 1 && $spec{1} == ':') { + if (strlen($spec) > 2 && $spec{2} == ':') { + if ($i + 1 < strlen($arg)) { + /* Option takes an optional argument. Use the remainder of + the arg string if there is anything left. */ + $opts[] = array($opt, substr($arg, $i + 1)); + break; + } + } else { + /* Option requires an argument. Use the remainder of the arg + string if there is anything left. */ + if ($i + 1 < strlen($arg)) { + $opts[] = array($opt, substr($arg, $i + 1)); + break; + } else if (list(, $opt_arg) = each($args)) { + /* Else use the next argument. */; + if (Console_Getopt::_isShortOpt($opt_arg) + || Console_Getopt::_isLongOpt($opt_arg)) { + $msg = "option requires an argument --$opt"; + return PEAR::raiseError("Console_Getopt:" . $msg); + } + } else { + $msg = "option requires an argument --$opt"; + return PEAR::raiseError("Console_Getopt:" . $msg); + } + } + } + + $opts[] = array($opt, $opt_arg); + } + } + + /** + * Checks if an argument is a short option + * + * @param string $arg Argument to check + * + * @return bool + */ + private static function _isShortOpt($arg) + { + return strlen($arg) == 2 && $arg[0] == '-' + && preg_match('/[a-zA-Z]/', $arg[1]); + } + + /** + * Checks if an argument is a long option + * + * @param string $arg Argument to check + * + * @return bool + */ + private static function _isLongOpt($arg) + { + return strlen($arg) > 2 && $arg[0] == '-' && $arg[1] == '-' && + preg_match('/[a-zA-Z]+$/', substr($arg, 2)); + } + + /** + * Parse long option + * + * @param string $arg Argument + * @param string[] $long_options Available long options + * @param string[][] &$opts + * @param string[] &$args + * + * @return void|PEAR_Error + */ + private static function _parseLongOption($arg, $long_options, &$opts, &$args, $skip_unknown) + { + @list($opt, $opt_arg) = explode('=', $arg, 2); + + $opt_len = strlen($opt); + + for ($i = 0; $i < count($long_options); $i++) { + $long_opt = $long_options[$i]; + $opt_start = substr($long_opt, 0, $opt_len); + + $long_opt_name = str_replace('=', '', $long_opt); + + /* Option doesn't match. Go on to the next one. */ + if ($long_opt_name != $opt) { + continue; + } + + $opt_rest = substr($long_opt, $opt_len); + + /* Check that the options uniquely matches one of the allowed + options. */ + if ($i + 1 < count($long_options)) { + $next_option_rest = substr($long_options[$i + 1], $opt_len); + } else { + $next_option_rest = ''; + } + + if ($opt_rest != '' && $opt{0} != '=' && + $i + 1 < count($long_options) && + $opt == substr($long_options[$i+1], 0, $opt_len) && + $next_option_rest != '' && + $next_option_rest{0} != '=') { + + $msg = "Console_Getopt: option --$opt is ambiguous"; + return PEAR::raiseError($msg); + } + + if (substr($long_opt, -1) == '=') { + if (substr($long_opt, -2) != '==') { + /* Long option requires an argument. + Take the next argument if one wasn't specified. */; + if (!strlen($opt_arg) && !(list(, $opt_arg) = each($args))) { + $msg = "Console_Getopt: option requires an argument --$opt"; + return PEAR::raiseError($msg); + } + + if (Console_Getopt::_isShortOpt($opt_arg) + || Console_Getopt::_isLongOpt($opt_arg)) { + $msg = "Console_Getopt: option requires an argument --$opt"; + return PEAR::raiseError($msg); + } + } + } else if ($opt_arg) { + $msg = "Console_Getopt: option --$opt doesn't allow an argument"; + return PEAR::raiseError($msg); + } + + $opts[] = array('--' . $opt, $opt_arg); + return; + } + + if ($skip_unknown === true) { + return; + } + + return PEAR::raiseError("Console_Getopt: unrecognized option --$opt"); + } + + /** + * Safely read the $argv PHP array across different PHP configurations. + * Will take care on register_globals and register_argc_argv ini directives + * + * @return mixed the $argv PHP array or PEAR error if not registered + */ + public static function readPHPArgv() + { + global $argv; + if (!is_array($argv)) { + if (!@is_array($_SERVER['argv'])) { + if (!@is_array($GLOBALS['HTTP_SERVER_VARS']['argv'])) { + $msg = "Could not read cmd args (register_argc_argv=Off?)"; + return PEAR::raiseError("Console_Getopt: " . $msg); + } + return $GLOBALS['HTTP_SERVER_VARS']['argv']; + } + return $_SERVER['argv']; + } + return $argv; + } + +} diff --git a/includes/pear/HTTP.php b/includes/pear/HTTP.php new file mode 100644 index 0000000..6ebd6db --- /dev/null +++ b/includes/pear/HTTP.php @@ -0,0 +1,548 @@ + + * @author Sterling Hughes + * @author Tomas V.V.Cox + * @author Richard Heyes + * @author Philippe Jausions + * @author Michael Wallner + * @copyright 2002-2008 The Authors + * @license http://www.opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: HTTP.php,v 1.56 2008/08/31 20:15:43 jausions Exp $ + * @link http://pear.php.net/package/HTTP + */ + +/** + * Miscellaneous HTTP Utilities + * + * PEAR::HTTP provides static shorthand methods for generating HTTP dates, + * issueing HTTP HEAD requests, building absolute URIs, firing redirects and + * negotiating user preferred language. + * + * @category HTTP + * @package HTTP + * @author Stig Bakken + * @author Sterling Hughes + * @author Tomas V.V.Cox + * @author Richard Heyes + * @author Philippe Jausions + * @author Michael Wallner + * @license http://www.opensource.org/licenses/bsd-license.php New BSD License + * @abstract + * @version Release: $Revision: 1.56 $ + * @link http://pear.php.net/package/HTTP + */ +class HTTP +{ + /** + * Formats a RFC compliant GMT date HTTP header. This function honors the + * "y2k_compliance" php.ini directive and formats the GMT date corresponding + * to either RFC850 or RFC822. + * + * @param mixed $time unix timestamp or date (default = current time) + * + * @return mixed GMT date string, or false for an invalid $time parameter + * @access public + * @static + */ + function Date($time = null) + { + if (!isset($time)) { + $time = time(); + } elseif (!is_numeric($time) && (-1 === $time = strtotime($time))) { + return false; + } + + // RFC822 or RFC850 + $format = ini_get('y2k_compliance') ? 'D, d M Y' : 'l, d-M-y'; + + return gmdate($format .' H:i:s \G\M\T', $time); + } + + /** + * Negotiates language with the user's browser through the Accept-Language + * HTTP header or the user's host address. Language codes are generally in + * the form "ll" for a language spoken in only one country, or "ll-CC" for a + * language spoken in a particular country. For example, U.S. English is + * "en-US", while British English is "en-UK". Portugese as spoken in + * Portugal is "pt-PT", while Brazilian Portugese is "pt-BR". + * + * Quality factors in the Accept-Language: header are supported, e.g.: + * Accept-Language: en-UK;q=0.7, en-US;q=0.6, no, dk;q=0.8 + * + * + * require_once 'HTTP.php'; + * $langs = array( + * 'en' => 'locales/en', + * 'en-US' => 'locales/en', + * 'en-UK' => 'locales/en', + * 'de' => 'locales/de', + * 'de-DE' => 'locales/de', + * 'de-AT' => 'locales/de', + * ); + * $neg = HTTP::negotiateLanguage($langs); + * $dir = $langs[$neg]; + * + * + * @param array $supported An associative array of supported languages, + * whose values must evaluate to true. + * @param string $default The default language to use if none is found. + * + * @return string The negotiated language result or the supplied default. + * @static + * @access public + */ + function negotiateLanguage($supported, $default = 'en-US') + { + $supp = array(); + foreach ($supported as $lang => $isSupported) { + if ($isSupported) { + $supp[strtolower($lang)] = $lang; + } + } + + if (!count($supp)) { + return $default; + } + + if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { + $match = HTTP::_matchAccept($_SERVER['HTTP_ACCEPT_LANGUAGE'], + $supp); + if (!is_null($match)) { + return $match; + } + } + + if (isset($_SERVER['REMOTE_HOST'])) { + $lang = strtolower(end($h = explode('.', $_SERVER['REMOTE_HOST']))); + if (isset($supp[$lang])) { + return $supp[$lang]; + } + } + + return $default; + } + + /** + * Negotiates charset with the user's browser through the Accept-Charset + * HTTP header. + * + * Quality factors in the Accept-Charset: header are supported, e.g.: + * Accept-Language: en-UK;q=0.7, en-US;q=0.6, no, dk;q=0.8 + * + * + * require_once 'HTTP.php'; + * $charsets = array( + * 'UTF-8', + * 'ISO-8859-1', + * ); + * $charset = HTTP::negotiateCharset($charsets); + * + * + * @param array $supported An array of supported charsets + * @param string $default The default charset to use if none is found. + * + * @return string The negotiated language result or the supplied default. + * @static + * @author Philippe Jausions + * @access public + * @since 1.4.1 + */ + function negotiateCharset($supported, $default = 'ISO-8859-1') + { + $supp = array(); + foreach ($supported as $charset) { + $supp[strtolower($charset)] = $charset; + } + + if (!count($supp)) { + return $default; + } + + if (isset($_SERVER['HTTP_ACCEPT_CHARSET'])) { + $match = HTTP::_matchAccept($_SERVER['HTTP_ACCEPT_CHARSET'], + $supp); + if (!is_null($match)) { + return $match; + } + } + + return $default; + } + + /** + * Negotiates content type with the user's browser through the Accept + * HTTP header. + * + * Quality factors in the Accept: header are supported, e.g.: + * Accept: application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8 + * + * + * require_once 'HTTP.php'; + * $contentType = array( + * 'application/xhtml+xml', + * 'application/xml', + * 'text/html', + * 'text/plain', + * ); + * $mime = HTTP::negotiateContentType($contentType); + * + * + * @param array $supported An associative array of supported MIME types. + * @param string $default The default type to use if none match. + * + * @return string The negotiated MIME type result or the supplied default. + * @static + * @author Philippe Jausions + * @access public + * @since 1.4.1 + */ + function negotiateMimeType($supported, $default) + { + $supp = array(); + foreach ($supported as $type) { + $supp[strtolower($type)] = $type; + } + + if (!count($supp)) { + return $default; + } + + if (isset($_SERVER['HTTP_ACCEPT'])) { + $accepts = HTTP::_sortAccept($_SERVER['HTTP_ACCEPT']); + + foreach ($accepts as $type => $q) { + if (substr($type, -2) != '/*') { + if (isset($supp[$type])) { + return $supp[$type]; + } + continue; + } + if ($type == '*/*') { + return array_shift($supp); + } + list($general, $specific) = explode('/', $type); + $general .= '/'; + $len = strlen($general); + foreach ($supp as $mime => $t) { + if (strncasecmp($general, $mime, $len) == 0) { + return $t; + } + } + } + } + + return $default; + } + + /** + * Parses a weighed "Accept" HTTP header and matches it against a list + * of supported options + * + * @param string $header The HTTP "Accept" header to parse + * @param array $supported A list of supported values + * + * @return string|NULL a matched option, or NULL if no match + * @access private + * @static + */ + function _matchAccept($header, $supported) + { + $matches = HTTP::_sortAccept($header); + foreach ($matches as $key => $q) { + if (isset($supported[$key])) { + return $supported[$key]; + } + } + // If any (i.e. "*") is acceptable, return the first supported format + if (isset($matches['*'])) { + return array_shift($supported); + } + return null; + } + + /** + * Parses and sorts a weighed "Accept" HTTP header + * + * @param string $header The HTTP "Accept" header to parse + * + * @return array a sorted list of "accept" options + * @access private + * @static + */ + function _sortAccept($header) + { + $matches = array(); + foreach (explode(',', $header) as $option) { + $option = array_map('trim', explode(';', $option)); + + $l = strtolower($option[0]); + if (isset($option[1])) { + $q = (float) str_replace('q=', '', $option[1]); + } else { + $q = null; + // Assign default low weight for generic values + if ($l == '*/*') { + $q = 0.01; + } elseif (substr($l, -1) == '*') { + $q = 0.02; + } + } + // Unweighted values, get high weight by their position in the + // list + $matches[$l] = isset($q) ? $q : 1000 - count($matches); + } + arsort($matches, SORT_NUMERIC); + return $matches; + } + + /** + * Sends a "HEAD" HTTP command to a server and returns the headers + * as an associative array. + * + * Example output could be: + * + * Array + * ( + * [response_code] => 200 // The HTTP response code + * [response] => HTTP/1.1 200 OK // The full HTTP response string + * [Date] => Fri, 11 Jan 2002 01:41:44 GMT + * [Server] => Apache/1.3.20 (Unix) PHP/4.1.1 + * [X-Powered-By] => PHP/4.1.1 + * [Connection] => close + * [Content-Type] => text/html + * ) + * + * + * @param string $url A valid URL, e.g.: http://pear.php.net/credits.php + * @param integer $timeout Timeout in seconds (default = 10) + * + * @return array Returns associative array of response headers on success + * or PEAR error on failure. + * @static + * @access public + * @see HTTP_Client::head() + * @see HTTP_Request + */ + function head($url, $timeout = 10) + { + $p = parse_url($url); + if (!isset($p['scheme'])) { + $p = parse_url(HTTP::absoluteURI($url)); + } elseif ($p['scheme'] != 'http') { + return HTTP::raiseError('Unsupported protocol: '. $p['scheme']); + } + + $port = isset($p['port']) ? $p['port'] : 80; + + if (!$fp = @fsockopen($p['host'], $port, $eno, $estr, $timeout)) { + return HTTP::raiseError("Connection error: $estr ($eno)"); + } + + $path = !empty($p['path']) ? $p['path'] : '/'; + $path .= !empty($p['query']) ? '?' . $p['query'] : ''; + + fputs($fp, "HEAD $path HTTP/1.0\r\n"); + fputs($fp, 'Host: ' . $p['host'] . ':' . $port . "\r\n"); + fputs($fp, "Connection: close\r\n\r\n"); + + $response = rtrim(fgets($fp, 4096)); + if (preg_match("|^HTTP/[^\s]*\s(.*?)\s|", $response, $status)) { + $headers['response_code'] = $status[1]; + } + $headers['response'] = $response; + + while ($line = fgets($fp, 4096)) { + if (!trim($line)) { + break; + } + if (($pos = strpos($line, ':')) !== false) { + $header = substr($line, 0, $pos); + $value = trim(substr($line, $pos + 1)); + + $headers[$header] = $value; + } + } + fclose($fp); + return $headers; + } + + /** + * This function redirects the client. This is done by issuing + * a "Location" header and exiting if wanted. If you set $rfc2616 to true + * HTTP will output a hypertext note with the location of the redirect. + * + * @param string $url URL where the redirect should go to. + * @param bool $exit Whether to exit immediately after redirection. + * @param bool $rfc2616 Wheter to output a hypertext note where we're + * redirecting to (Redirecting to + * ....) + * + * @return boolean Returns TRUE on succes (or exits) or FALSE if headers + * have already been sent. + * @static + * @access public + */ + function redirect($url, $exit = true, $rfc2616 = false) + { + if (headers_sent()) { + return false; + } + + $url = HTTP::absoluteURI($url); + header('Location: '. $url); + + if ($rfc2616 && isset($_SERVER['REQUEST_METHOD']) + && $_SERVER['REQUEST_METHOD'] != 'HEAD') { + echo ' +

    Redirecting to: ' + .htmlspecialchars($url).'.

    +'; + } + if ($exit) { + exit; + } + return true; + } + + /** + * This function returns the absolute URI for the partial URL passed. + * The current scheme (HTTP/HTTPS), host server, port, current script + * location are used if necessary to resolve any relative URLs. + * + * Offsets potentially created by PATH_INFO are taken care of to resolve + * relative URLs to the current script. + * + * You can choose a new protocol while resolving the URI. This is + * particularly useful when redirecting a web browser using relative URIs + * and to switch from HTTP to HTTPS, or vice-versa, at the same time. + * + * @param string $url Absolute or relative URI the redirect should + * go to. + * @param string $protocol Protocol to use when redirecting URIs. + * @param integer $port A new port number. + * + * @return string The absolute URI. + * @author Philippe Jausions + * @static + * @access public + */ + function absoluteURI($url = null, $protocol = null, $port = null) + { + // filter CR/LF + $url = str_replace(array("\r", "\n"), ' ', $url); + + // Mess around protocol and port with already absolute URIs + if (preg_match('!^([a-z0-9]+)://!i', $url)) { + if (empty($protocol) && empty($port)) { + return $url; + } + if (!empty($protocol)) { + $url = $protocol .':'. end($array = explode(':', $url, 2)); + } + if (!empty($port)) { + $url = preg_replace('!^(([a-z0-9]+)://[^/:]+)(:[\d]+)?!i', + '\1:'. $port, $url); + } + return $url; + } + + $host = 'localhost'; + if (!empty($_SERVER['HTTP_HOST'])) { + list($host) = explode(':', $_SERVER['HTTP_HOST']); + } elseif (!empty($_SERVER['SERVER_NAME'])) { + list($host) = explode(':', $_SERVER['SERVER_NAME']); + } + + if (empty($protocol)) { + if (isset($_SERVER['HTTPS']) && !strcasecmp($_SERVER['HTTPS'], 'on')) { + $protocol = 'https'; + } else { + $protocol = 'http'; + } + if (!isset($port) || $port != intval($port)) { + $port = isset($_SERVER['SERVER_PORT']) ? $_SERVER['SERVER_PORT'] : 80; + } + } + + if ($protocol == 'http' && $port == 80) { + unset($port); + } + if ($protocol == 'https' && $port == 443) { + unset($port); + } + + $server = $protocol.'://'.$host.(isset($port) ? ':'.$port : ''); + + $uriAll = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] + : $_SERVER['PHP_SELF']; + if (false !== ($q = strpos($uriAll, '?'))) { + $uriBase = substr($uriAll, 0, $q); + } else { + $uriBase = $uriAll; + } + if (!strlen($url) || $url{0} == '#') { + $url = $uriAll.$url; + } elseif ($url{0} == '?') { + $url = $uriBase.$url; + } + if ($url{0} == '/') { + return $server . $url; + } + + // Adjust for PATH_INFO if needed + if (isset($_SERVER['PATH_INFO']) && strlen($_SERVER['PATH_INFO'])) { + $path = dirname(substr($uriBase, 0, + -strlen($_SERVER['PATH_INFO']))); + } else { + /** + * Fixes bug #12672 PHP_SELF ending on / causes incorrect redirects + * + * @link http://pear.php.net/bugs/12672 + */ + $path = dirname($uriBase.'-'); + } + + if (substr($path = strtr($path, '\\', '/'), -1) != '/') { + $path .= '/'; + } + + return $server . $path . $url; + } + + /** + * Raise Error + * + * Lazy raising of PEAR_Errors. + * + * @param mixed $error Error + * @param integer $code Error code + * + * @return object PEAR_Error + * @static + * @access protected + */ + function raiseError($error = null, $code = null) + { + include_once 'PEAR.php'; + return PEAR::raiseError($error, $code); + } +} + +?> \ No newline at end of file diff --git a/includes/pear/HTTP/Download.php b/includes/pear/HTTP/Download.php new file mode 100644 index 0000000..8cfc348 --- /dev/null +++ b/includes/pear/HTTP/Download.php @@ -0,0 +1,1243 @@ + + * @copyright 2003-2005 Michael Wallner + * @license BSD, revised + * @version CVS: $Id: Download.php 304423 2010-10-15 13:36:46Z clockwerx $ + * @link http://pear.php.net/package/HTTP_Download + */ + +// {{{ includes +/** + * Requires PEAR + */ +require_once 'PEAR.php'; + +/** + * Requires HTTP_Header + */ +require_once 'HTTP/Header.php'; +// }}} + +// {{{ constants +/**#@+ Use with HTTP_Download::setContentDisposition() **/ +/** + * Send data as attachment + */ +define('HTTP_DOWNLOAD_ATTACHMENT', 'attachment'); +/** + * Send data inline + */ +define('HTTP_DOWNLOAD_INLINE', 'inline'); +/**#@-**/ + +/**#@+ Use with HTTP_Download::sendArchive() **/ +/** + * Send as uncompressed tar archive + */ +define('HTTP_DOWNLOAD_TAR', 'TAR'); +/** + * Send as gzipped tar archive + */ +define('HTTP_DOWNLOAD_TGZ', 'TGZ'); +/** + * Send as bzip2 compressed tar archive + */ +define('HTTP_DOWNLOAD_BZ2', 'BZ2'); +/** + * Send as zip archive + */ +define('HTTP_DOWNLOAD_ZIP', 'ZIP'); +/**#@-**/ + +/**#@+ + * Error constants + */ +define('HTTP_DOWNLOAD_E_HEADERS_SENT', -1); +define('HTTP_DOWNLOAD_E_NO_EXT_ZLIB', -2); +define('HTTP_DOWNLOAD_E_NO_EXT_MMAGIC', -3); +define('HTTP_DOWNLOAD_E_INVALID_FILE', -4); +define('HTTP_DOWNLOAD_E_INVALID_PARAM', -5); +define('HTTP_DOWNLOAD_E_INVALID_RESOURCE', -6); +define('HTTP_DOWNLOAD_E_INVALID_REQUEST', -7); +define('HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE', -8); +define('HTTP_DOWNLOAD_E_INVALID_ARCHIVE_TYPE', -9); +/**#@-**/ +// }}} + +/** + * Send HTTP Downloads/Responses. + * + * With this package you can handle (hidden) downloads. + * It supports partial downloads, resuming and sending + * raw data ie. from database BLOBs. + * + * ATTENTION: + * You shouldn't use this package together with ob_gzhandler or + * zlib.output_compression enabled in your php.ini, especially + * if you want to send already gzipped data! + * + * @access public + * @version $Revision: 304423 $ + */ +class HTTP_Download +{ + // {{{ protected member variables + /** + * Path to file for download + * + * @see HTTP_Download::setFile() + * @access protected + * @var string + */ + var $file = ''; + + /** + * Data for download + * + * @see HTTP_Download::setData() + * @access protected + * @var string + */ + var $data = null; + + /** + * Resource handle for download + * + * @see HTTP_Download::setResource() + * @access protected + * @var int + */ + var $handle = null; + + /** + * Whether to gzip the download + * + * @access protected + * @var bool + */ + var $gzip = false; + + /** + * Whether to allow caching of the download on the clients side + * + * @access protected + * @var bool + */ + var $cache = true; + + /** + * Size of download + * + * @access protected + * @var int + */ + var $size = 0; + + /** + * Last modified + * + * @access protected + * @var int + */ + var $lastModified = 0; + + /** + * HTTP headers + * + * @access protected + * @var array + */ + var $headers = array( + 'Content-Type' => 'application/x-octetstream', + 'Pragma' => 'cache', + 'Cache-Control' => 'public, must-revalidate, max-age=0', + 'Accept-Ranges' => 'bytes', + 'X-Sent-By' => 'PEAR::HTTP::Download' + ); + + /** + * HTTP_Header + * + * @access protected + * @var object + */ + var $HTTP = null; + + /** + * ETag + * + * @access protected + * @var string + */ + var $etag = ''; + + /** + * Buffer Size + * + * @access protected + * @var int + */ + var $bufferSize = 2097152; + + /** + * Throttle Delay + * + * @access protected + * @var float + */ + var $throttleDelay = 0; + + /** + * Sent Bytes + * + * @access public + * @var int + */ + var $sentBytes = 0; + + /** + * Startup error + * + * @var PEAR_Error + * @access protected + */ + var $_error = null; + // }}} + + // {{{ constructor + /** + * Constructor + * + * Set supplied parameters. + * + * @access public + * @param array $params associative array of parameters + * one of: + *
      + *
    • 'file' => path to file for download
    • + *
    • 'data' => raw data for download
    • + *
    • 'resource' => resource handle for download
    • + *
    + * and any of: + *
      + *
    • 'cache' => whether to allow cs caching
    • + *
    • 'gzip' => whether to gzip the download
    • + *
    • 'lastmodified' => unix timestamp
    • + *
    • 'contenttype' => content type of download
    • + *
    • 'contentdisposition' => content disposition
    • + *
    • 'buffersize' => amount of bytes to buffer
    • + *
    • 'throttledelay' => amount of secs to sleep
    • + *
    • 'cachecontrol' => cache privacy and validity
    • + *
    + * + * 'Content-Disposition' is not HTTP compliant, but most browsers + * follow this header, so it was borrowed from MIME standard. + * + * It looks like this: + * "Content-Disposition: attachment; filename=example.tgz". + * + * @see HTTP_Download::setContentDisposition() + */ + function HTTP_Download($params = array()) + { + $this->HTTP = &new HTTP_Header; + $this->_error = $this->setParams($params); + } + // }}} + + // {{{ public methods + /** + * Set parameters + * + * Set supplied parameters through its accessor methods. + * + * @access public + * @return mixed Returns true on success or PEAR_Error on failure. + * @param array $params associative array of parameters + * + * @see HTTP_Download::HTTP_Download() + */ + function setParams($params) + { + $error = $this->_getError(); + if ($error !== null) { + return $error; + } + foreach((array) $params as $param => $value){ + $method = 'set'. $param; + + if (!method_exists($this, $method)) { + return PEAR::raiseError( + "Method '$method' doesn't exist.", + HTTP_DOWNLOAD_E_INVALID_PARAM + ); + } + + $e = call_user_func_array(array(&$this, $method), (array) $value); + + if (PEAR::isError($e)) { + return $e; + } + } + return true; + } + + /** + * Set path to file for download + * + * The Last-Modified header will be set to files filemtime(), actually. + * Returns PEAR_Error (HTTP_DOWNLOAD_E_INVALID_FILE) if file doesn't exist. + * Sends HTTP 404 or 403 status if $send_error is set to true. + * + * @access public + * @return mixed Returns true on success or PEAR_Error on failure. + * @param string $file path to file for download + * @param bool $send_error whether to send HTTP/404 or 403 if + * the file wasn't found or is not readable + */ + function setFile($file, $send_error = true) + { + $error = $this->_getError(); + if ($error !== null) { + return $error; + } + $file = realpath($file); + if (!is_file($file)) { + if ($send_error) { + $this->HTTP->sendStatusCode(404); + } + return PEAR::raiseError( + "File '$file' not found.", + HTTP_DOWNLOAD_E_INVALID_FILE + ); + } + if (!is_readable($file)) { + if ($send_error) { + $this->HTTP->sendStatusCode(403); + } + return PEAR::raiseError( + "Cannot read file '$file'.", + HTTP_DOWNLOAD_E_INVALID_FILE + ); + } + $this->setLastModified(filemtime($file)); + $this->file = $file; + $this->size = filesize($file); + return true; + } + + /** + * Set data for download + * + * Set $data to null if you want to unset this. + * + * @access public + * @return void + * @param $data raw data to send + */ + function setData($data = null) + { + $this->data = $data; + $this->size = strlen($data); + } + + /** + * Set resource for download + * + * The resource handle supplied will be closed after sending the download. + * Returns a PEAR_Error (HTTP_DOWNLOAD_E_INVALID_RESOURCE) if $handle + * is no valid resource. Set $handle to null if you want to unset this. + * + * @access public + * @return mixed Returns true on success or PEAR_Error on failure. + * @param int $handle resource handle + */ + function setResource($handle = null) + { + $error = $this->_getError(); + if ($error !== null) { + return $error; + } + if (!isset($handle)) { + $this->handle = null; + $this->size = 0; + return true; + } + + if (is_resource($handle)) { + $this->handle = $handle; + $filestats = fstat($handle); + $this->size = isset($filestats['size']) ? $filestats['size'] + : -1; + return true; + } + + return PEAR::raiseError( + "Handle '$handle' is no valid resource.", + HTTP_DOWNLOAD_E_INVALID_RESOURCE + ); + } + + /** + * Whether to gzip the download + * + * Returns a PEAR_Error (HTTP_DOWNLOAD_E_NO_EXT_ZLIB) + * if ext/zlib is not available/loadable. + * + * @access public + * @return mixed Returns true on success or PEAR_Error on failure. + * @param bool $gzip whether to gzip the download + */ + function setGzip($gzip = false) + { + $error = $this->_getError(); + if ($error !== null) { + return $error; + } + if ($gzip && !PEAR::loadExtension('zlib')){ + return PEAR::raiseError( + 'GZIP compression (ext/zlib) not available.', + HTTP_DOWNLOAD_E_NO_EXT_ZLIB + ); + } + $this->gzip = (bool) $gzip; + return true; + } + + /** + * Whether to allow caching + * + * If set to true (default) we'll send some headers that are commonly + * used for caching purposes like ETag, Cache-Control and Last-Modified. + * + * If caching is disabled, we'll send the download no matter if it + * would actually be cached at the client side. + * + * @access public + * @return void + * @param bool $cache whether to allow caching + */ + function setCache($cache = true) + { + $this->cache = (bool) $cache; + } + + /** + * Whether to allow proxies to cache + * + * If set to 'private' proxies shouldn't cache the response. + * This setting defaults to 'public' and affects only cached responses. + * + * @access public + * @return bool + * @param string $cache private or public + * @param int $maxage maximum age of the client cache entry + */ + function setCacheControl($cache = 'public', $maxage = 0) + { + switch ($cache = strToLower($cache)) + { + case 'private': + case 'public': + $this->headers['Cache-Control'] = + $cache .', must-revalidate, max-age='. abs($maxage); + return true; + break; + } + return false; + } + + /** + * Set ETag + * + * Sets a user-defined ETag for cache-validation. The ETag is usually + * generated by HTTP_Download through its payload information. + * + * @access public + * @return void + * @param string $etag Entity tag used for strong cache validation. + */ + function setETag($etag = null) + { + $this->etag = (string) $etag; + } + + /** + * Set Size of Buffer + * + * The amount of bytes specified as buffer size is the maximum amount + * of data read at once from resources or files. The default size is 2M + * (2097152 bytes). Be aware that if you enable gzip compression and + * you set a very low buffer size that the actual file size may grow + * due to added gzip headers for each sent chunk of the specified size. + * + * Returns PEAR_Error (HTTP_DOWNLOAD_E_INVALID_PARAM) if $size is not + * greater than 0 bytes. + * + * @access public + * @return mixed Returns true on success or PEAR_Error on failure. + * @param int $bytes Amount of bytes to use as buffer. + */ + function setBufferSize($bytes = 2097152) + { + $error = $this->_getError(); + if ($error !== null) { + return $error; + } + if (0 >= $bytes) { + return PEAR::raiseError( + 'Buffer size must be greater than 0 bytes ('. $bytes .' given)', + HTTP_DOWNLOAD_E_INVALID_PARAM); + } + $this->bufferSize = abs($bytes); + return true; + } + + /** + * Set Throttle Delay + * + * Set the amount of seconds to sleep after each chunck that has been + * sent. One can implement some sort of throttle through adjusting the + * buffer size and the throttle delay. With the following settings + * HTTP_Download will sleep a second after each 25 K of data sent. + * + * + * Array( + * 'throttledelay' => 1, + * 'buffersize' => 1024 * 25, + * ) + * + * + * Just be aware that if gzipp'ing is enabled, decreasing the chunk size + * too much leads to proportionally increased network traffic due to added + * gzip header and bottom bytes around each chunk. + * + * @access public + * @return void + * @param float $seconds Amount of seconds to sleep after each + * chunk that has been sent. + */ + function setThrottleDelay($seconds = 0) + { + $this->throttleDelay = abs($seconds) * 1000; + } + + /** + * Set "Last-Modified" + * + * This is usually determined by filemtime() in HTTP_Download::setFile() + * If you set raw data for download with HTTP_Download::setData() and you + * want do send an appropiate "Last-Modified" header, you should call this + * method. + * + * @access public + * @return void + * @param int unix timestamp + */ + function setLastModified($last_modified) + { + $this->lastModified = $this->headers['Last-Modified'] = (int) $last_modified; + } + + /** + * Set Content-Disposition header + * + * @see HTTP_Download::HTTP_Download + * + * @access public + * @return void + * @param string $disposition whether to send the download + * inline or as attachment + * @param string $file_name the filename to display in + * the browser's download window + * + * Example: + * + * $HTTP_Download->setContentDisposition( + * HTTP_DOWNLOAD_ATTACHMENT, + * 'download.tgz' + * ); + * + */ + function setContentDisposition( $disposition = HTTP_DOWNLOAD_ATTACHMENT, + $file_name = null) + { + $cd = $disposition; + if (isset($file_name)) { + $cd .= '; filename="' . $file_name . '"'; + } elseif ($this->file) { + $cd .= '; filename="' . basename($this->file) . '"'; + } + $this->headers['Content-Disposition'] = $cd; + } + + /** + * Set content type of the download + * + * Default content type of the download will be 'application/x-octetstream'. + * Returns PEAR_Error (HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE) if + * $content_type doesn't seem to be valid. + * + * @access public + * @return mixed Returns true on success or PEAR_Error on failure. + * @param string $content_type content type of file for download + */ + function setContentType($content_type = 'application/x-octetstream') + { + $error = $this->_getError(); + if ($error !== null) { + return $error; + } + if (!preg_match('/^[a-z]+\w*\/[a-z]+[\w.;= -]*$/', $content_type)) { + return PEAR::raiseError( + "Invalid content type '$content_type' supplied.", + HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE + ); + } + $this->headers['Content-Type'] = $content_type; + return true; + } + + /** + * Guess content type of file + * + * First we try to use PEAR::MIME_Type, if installed, to detect the content + * type, else we check if ext/mime_magic is loaded and properly configured. + * + * Returns PEAR_Error if: + * o if PEAR::MIME_Type failed to detect a proper content type + * (HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE) + * o ext/magic.mime is not installed, or not properly configured + * (HTTP_DOWNLOAD_E_NO_EXT_MMAGIC) + * o mime_content_type() couldn't guess content type or returned + * a content type considered to be bogus by setContentType() + * (HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE) + * + * @access public + * @return mixed Returns true on success or PEAR_Error on failure. + */ + function guessContentType() + { + $error = $this->_getError(); + if ($error !== null) { + return $error; + } + if (class_exists('MIME_Type') || @include_once 'MIME/Type.php') { + if (PEAR::isError($mime_type = MIME_Type::autoDetect($this->file))) { + return PEAR::raiseError($mime_type->getMessage(), + HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE); + } + return $this->setContentType($mime_type); + } + if (!function_exists('mime_content_type')) { + return PEAR::raiseError( + 'This feature requires ext/mime_magic!', + HTTP_DOWNLOAD_E_NO_EXT_MMAGIC + ); + } + if (!is_file(ini_get('mime_magic.magicfile'))) { + return PEAR::raiseError( + 'ext/mime_magic is loaded but not properly configured!', + HTTP_DOWNLOAD_E_NO_EXT_MMAGIC + ); + } + if (!$content_type = @mime_content_type($this->file)) { + return PEAR::raiseError( + 'Couldn\'t guess content type with mime_content_type().', + HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE + ); + } + return $this->setContentType($content_type); + } + + /** + * Send + * + * Returns PEAR_Error if: + * o HTTP headers were already sent (HTTP_DOWNLOAD_E_HEADERS_SENT) + * o HTTP Range was invalid (HTTP_DOWNLOAD_E_INVALID_REQUEST) + * + * @access public + * @return mixed Returns true on success or PEAR_Error on failure. + * @param bool $autoSetContentDisposition Whether to set the + * Content-Disposition header if it isn't already. + */ + function send($autoSetContentDisposition = true) + { + $error = $this->_getError(); + if ($error !== null) { + return $error; + } + if (headers_sent()) { + return PEAR::raiseError( + 'Headers already sent.', + HTTP_DOWNLOAD_E_HEADERS_SENT + ); + } + + if (!ini_get('safe_mode')) { + @set_time_limit(0); + } + + if ($autoSetContentDisposition && + !isset($this->headers['Content-Disposition'])) { + $this->setContentDisposition(); + } + + if ($this->cache) { + $this->headers['ETag'] = $this->generateETag(); + if ($this->isCached()) { + $this->HTTP->sendStatusCode(304); + $this->sendHeaders(); + return true; + } + } else { + unset($this->headers['Last-Modified']); + } + + if (ob_get_level()) { + while (@ob_end_clean()); + } + + if ($this->gzip) { + @ob_start('ob_gzhandler'); + } else { + ob_start(); + } + + $this->sentBytes = 0; + + // Known content length? + $end = ($this->size >= 0) ? max($this->size - 1, 0) : '*'; + + if ($end != '*' && $this->isRangeRequest()) { + $chunks = $this->getChunks(); + if (empty($chunks)) { + $this->HTTP->sendStatusCode(200); + $chunks = array(array(0, $end)); + + } elseif (PEAR::isError($chunks)) { + ob_end_clean(); + $this->HTTP->sendStatusCode(416); + return $chunks; + + } else { + $this->HTTP->sendStatusCode(206); + } + } else { + $this->HTTP->sendStatusCode(200); + $chunks = array(array(0, $end)); + if (!$this->gzip && count(ob_list_handlers()) < 2 && $end != '*') { + $this->headers['Content-Length'] = $this->size; + } + } + + $this->sendChunks($chunks); + + ob_end_flush(); + flush(); + return true; + } + + /** + * Static send + * + * @see HTTP_Download::HTTP_Download() + * @see HTTP_Download::send() + * + * @static + * @access public + * @return mixed Returns true on success or PEAR_Error on failure. + * @param array $params associative array of parameters + * @param bool $guess whether HTTP_Download::guessContentType() + * should be called + */ + function staticSend($params, $guess = false) + { + $d = &new HTTP_Download(); + $e = $d->setParams($params); + if (PEAR::isError($e)) { + return $e; + } + if ($guess) { + $e = $d->guessContentType(); + if (PEAR::isError($e)) { + return $e; + } + } + return $d->send(); + } + + /** + * Send a bunch of files or directories as an archive + * + * Example: + * + * require_once 'HTTP/Download.php'; + * HTTP_Download::sendArchive( + * 'myArchive.tgz', + * '/var/ftp/pub/mike', + * HTTP_DOWNLOAD_TGZ, + * '', + * '/var/ftp/pub' + * ); + * + * + * @see Archive_Tar::createModify() + * @deprecated use HTTP_Download_Archive::send() + * @static + * @access public + * @return mixed Returns true on success or PEAR_Error on failure. + * @param string $name name the sent archive should have + * @param mixed $files files/directories + * @param string $type archive type + * @param string $add_path path that should be prepended to the files + * @param string $strip_path path that should be stripped from the files + */ + function sendArchive( $name, + $files, + $type = HTTP_DOWNLOAD_TGZ, + $add_path = '', + $strip_path = '') + { + require_once 'HTTP/Download/Archive.php'; + return HTTP_Download_Archive::send($name, $files, $type, + $add_path, $strip_path); + } + // }}} + + // {{{ protected methods + /** + * Generate ETag + * + * @access protected + * @return string + */ + function generateETag() + { + if (!$this->etag) { + if ($this->data) { + $md5 = md5($this->data); + } else { + $mtime = time(); + $ino = 0; + $size = mt_rand(); + extract(is_resource($this->handle) ? fstat($this->handle) + : stat($this->file)); + $md5 = md5($mtime .'='. $ino .'='. $size); + } + $this->etag = '"' . $md5 . '-' . crc32($md5) . '"'; + } + return $this->etag; + } + + /** + * Send multiple chunks + * + * @access protected + * @return mixed Returns true on success or PEAR_Error on failure. + * @param array $chunks + */ + function sendChunks($chunks) + { + if (count($chunks) == 1) { + return $this->sendChunk(current($chunks)); + } + + $bound = uniqid('HTTP_DOWNLOAD-', true); + $cType = $this->headers['Content-Type']; + $this->headers['Content-Type'] = + 'multipart/byteranges; boundary=' . $bound; + $this->sendHeaders(); + foreach ($chunks as $chunk){ + $this->sendChunk($chunk, $cType, $bound); + } + #echo "\r\n--$bound--\r\n"; + return true; + } + + /** + * Send chunk of data + * + * @access protected + * @return mixed Returns true on success or PEAR_Error on failure. + * @param array $chunk start and end offset of the chunk to send + * @param string $cType actual content type + * @param string $bound boundary for multipart/byteranges + */ + function sendChunk($chunk, $cType = null, $bound = null) + { + list($offset, $lastbyte) = $chunk; + $length = ($lastbyte - $offset) + 1; + + $range = $offset . '-' . $lastbyte . '/' + . (($this->size >= 0) ? $this->size : '*'); + + if (isset($cType, $bound)) { + echo "\r\n--$bound\r\n", + "Content-Type: $cType\r\n", + "Content-Range: bytes $range\r\n\r\n"; + } else { + if ($lastbyte != '*' && $this->isRangeRequest()) { + $this->headers['Content-Length'] = $length; + $this->headers['Content-Range'] = 'bytes '. $range; + } + $this->sendHeaders(); + } + + if ($this->data) { + while (($length -= $this->bufferSize) > 0) { + $this->flush(substr($this->data, $offset, $this->bufferSize)); + $this->throttleDelay and $this->sleep(); + $offset += $this->bufferSize; + } + if ($length) { + $this->flush(substr($this->data, $offset, $this->bufferSize + $length)); + } + } else { + if (!is_resource($this->handle)) { + $this->handle = fopen($this->file, 'rb'); + } + fseek($this->handle, $offset); + if ($lastbyte == '*') { + while (!feof($this->handle)) { + $this->flush(fread($this->handle, $this->bufferSize)); + $this->throttleDelay and $this->sleep(); + } + } else { + while (($length -= $this->bufferSize) > 0) { + $this->flush(fread($this->handle, $this->bufferSize)); + $this->throttleDelay and $this->sleep(); + } + if ($length) { + $this->flush(fread($this->handle, $this->bufferSize + $length)); + } + } + } + return true; + } + + /** + * Get chunks to send + * + * @access protected + * @return array Chunk list or PEAR_Error on invalid range request + * @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35 + */ + function getChunks() + { + $end = ($this->size >= 0) ? max($this->size - 1, 0) : '*'; + + // Trying to handle ranges on content with unknown length is too + // big of a mess (impossible to determine if a range is valid) + if ($end == '*') { + return array(); + } + + $ranges = $this->getRanges(); + if (empty($ranges)) { + return array(); + } + + $parts = array(); + $satisfiable = false; + foreach (explode(',', $ranges) as $chunk){ + list($o, $e) = explode('-', trim($chunk)); + + // If the last-byte-pos value is present, it MUST be greater than + // or equal to the first-byte-pos in that byte-range-spec, or the + // byte- range-spec is syntactically invalid. The recipient of a + // byte-range- set that includes one or more syntactically invalid + // byte-range-spec values MUST ignore the header field that + // includes that byte-range- set. + if ($e !== '' && $o !== '' && $e < $o) { + return array(); + } + + // If the last-byte-pos value is absent, or if the value is + // greater than or equal to the current length of the entity-body, + // last-byte-pos is taken to be equal to one less than the current + // length of the entity- body in bytes. + if ($e === '' || $e > $end) { + $e = $end; + } + + // A suffix-byte-range-spec is used to specify the suffix of the + // entity-body, of a length given by the suffix-length value. (That + // is, this form specifies the last N bytes of an entity-body.) If + // the entity is shorter than the specified suffix-length, the + // entire entity-body is used. + if ($o === '') { + // If a syntactically valid byte-range-set includes at least + // one suffix-byte-range-spec with a non-zero suffix-length, + // then the byte-range-set is satisfiable. + $satisfiable |= ($e != 0); + + $o = max($this->size - $e, 0); + $e = $end; + + } elseif ($o <= $end) { + // If a syntactically valid byte-range-set includes at least + // one byte- range-spec whose first-byte-pos is less than the + // current length of the entity-body, then the byte-range-set + // is satisfiable. + $satisfiable = true; + } else { + continue; + } + + $parts[] = array($o, $e); + } + + // If the byte-range-set is unsatisfiable, the server SHOULD return a + // response with a status of 416 (Requested range not satisfiable). + if (!$satisfiable) { + $error = PEAR::raiseError('Error processing range request', + HTTP_DOWNLOAD_E_INVALID_REQUEST); + return $error; + } + //$this->sortChunks($parts); + return $this->mergeChunks($parts); + } + + /** + * Sorts the ranges to be in ascending order + * + * @param array &$chunks ranges to sort + * + * @return void + * @access protected + * @static + * @author Philippe Jausions + */ + function sortChunks(&$chunks) + { + $sortFunc = create_function('$a,$b', + 'if ($a[0] == $b[0]) { + if ($a[1] == $b[1]) { + return 0; + } + return (($a[1] != "*" && $a[1] < $b[1]) + || $b[1] == "*") ? -1 : 1; + } + + return ($a[0] < $b[0]) ? -1 : 1;'); + + usort($chunks, $sortFunc); + } + + /** + * Merges consecutive chunks to avoid overlaps + * + * @param array $chunks Ranges to merge + * + * @return array merged ranges + * @access protected + * @static + * @author Philippe Jausions + */ + function mergeChunks($chunks) + { + do { + $count = count($chunks); + $merged = array(current($chunks)); + $j = 0; + for ($i = 1; $i < count($chunks); ++$i) { + list($o, $e) = $chunks[$i]; + if ($merged[$j][1] == '*') { + if ($merged[$j][0] <= $o) { + continue; + } elseif ($e == '*' || $merged[$j][0] <= $e) { + $merged[$j][0] = min($merged[$j][0], $o); + } else { + $merged[++$j] = $chunks[$i]; + } + } elseif ($merged[$j][0] <= $o && $o <= $merged[$j][1]) { + $merged[$j][1] = ($e == '*') ? '*' : max($e, $merged[$j][1]); + } elseif ($merged[$j][0] <= $e && $e <= $merged[$j][1]) { + $merged[$j][0] = min($o, $merged[$j][0]); + } else { + $merged[++$j] = $chunks[$i]; + } + } + if ($count == count($merged)) { + break; + } + $chunks = $merged; + } while (true); + return $merged; + } + + /** + * Check if range is requested + * + * @access protected + * @return bool + */ + function isRangeRequest() + { + if (!isset($_SERVER['HTTP_RANGE']) || !count($this->getRanges())) { + return false; + } + return $this->isValidRange(); + } + + /** + * Get range request + * + * @access protected + * @return array + */ + function getRanges() + { + return preg_match('/^bytes=((\d+-|\d+-\d+|-\d+)(, ?(\d+-|\d+-\d+|-\d+))*)$/', + @$_SERVER['HTTP_RANGE'], $matches) ? $matches[1] : array(); + } + + /** + * Check if entity is cached + * + * @access protected + * @return bool + */ + function isCached() + { + return ( + (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && + $this->lastModified == strtotime(current($a = explode( + ';', $_SERVER['HTTP_IF_MODIFIED_SINCE'])))) || + (isset($_SERVER['HTTP_IF_NONE_MATCH']) && + $this->compareAsterisk('HTTP_IF_NONE_MATCH', $this->etag)) + ); + } + + /** + * Check if entity hasn't changed + * + * @access protected + * @return bool + */ + function isValidRange() + { + if (isset($_SERVER['HTTP_IF_MATCH']) && + !$this->compareAsterisk('HTTP_IF_MATCH', $this->etag)) { + return false; + } + if (isset($_SERVER['HTTP_IF_RANGE']) && + $_SERVER['HTTP_IF_RANGE'] !== $this->etag && + strtotime($_SERVER['HTTP_IF_RANGE']) !== $this->lastModified) { + return false; + } + if (isset($_SERVER['HTTP_IF_UNMODIFIED_SINCE'])) { + $lm = current($a = explode(';', $_SERVER['HTTP_IF_UNMODIFIED_SINCE'])); + if (strtotime($lm) !== $this->lastModified) { + return false; + } + } + if (isset($_SERVER['HTTP_UNLESS_MODIFIED_SINCE'])) { + $lm = current($a = explode(';', $_SERVER['HTTP_UNLESS_MODIFIED_SINCE'])); + if (strtotime($lm) !== $this->lastModified) { + return false; + } + } + return true; + } + + /** + * Compare against an asterisk or check for equality + * + * @access protected + * @return bool + * @param string key for the $_SERVER array + * @param string string to compare + */ + function compareAsterisk($svar, $compare) + { + foreach (array_map('trim', explode(',', $_SERVER[$svar])) as $request) { + if ($request === '*' || $request === $compare) { + return true; + } + } + return false; + } + + /** + * Send HTTP headers + * + * @access protected + * @return void + */ + function sendHeaders() + { + foreach ($this->headers as $header => $value) { + $this->HTTP->setHeader($header, $value); + } + $this->HTTP->sendHeaders(); + /* NSAPI won't output anything if we did this */ + if (strncasecmp(PHP_SAPI, 'nsapi', 5)) { + if (ob_get_level()) { + ob_flush(); + } + flush(); + } + } + + /** + * Flush + * + * @access protected + * @return void + * @param string $data + */ + function flush($data = '') + { + if ($dlen = strlen($data)) { + $this->sentBytes += $dlen; + echo $data; + } + ob_flush(); + flush(); + } + + /** + * Sleep + * + * @access protected + * @return void + */ + function sleep() + { + if (OS_WINDOWS) { + com_message_pump($this->throttleDelay); + } else { + usleep($this->throttleDelay * 1000); + } + } + + /** + * Returns and clears startup error + * + * @return NULL|PEAR_Error startup error if one exists + * @access protected + */ + function _getError() + { + $error = null; + if (PEAR::isError($this->_error)) { + $error = $this->_error; + $this->_error = null; + } + return $error; + } + // }}} +} +?> diff --git a/includes/pear/HTTP/Download/Archive.php b/includes/pear/HTTP/Download/Archive.php new file mode 100644 index 0000000..4eadbd7 --- /dev/null +++ b/includes/pear/HTTP/Download/Archive.php @@ -0,0 +1,122 @@ + + * @copyright 2003-2005 Michael Wallner + * @license BSD, revisewd + * @version CVS: $Id: Archive.php 304423 2010-10-15 13:36:46Z clockwerx $ + * @link http://pear.php.net/package/HTTP_Download + */ + +/** + * Requires HTTP_Download + */ +require_once 'HTTP/Download.php'; + +/** + * Requires System + */ +require_once 'System.php'; + +/** + * HTTP_Download_Archive + * + * Helper class for sending Archives. + * + * @access public + * @version $Revision: 304423 $ + */ +class HTTP_Download_Archive +{ + /** + * Send a bunch of files or directories as an archive + * + * Example: + * + * require_once 'HTTP/Download/Archive.php'; + * HTTP_Download_Archive::send( + * 'myArchive.tgz', + * '/var/ftp/pub/mike', + * HTTP_DOWNLOAD_BZ2, + * '', + * '/var/ftp/pub' + * ); + * + * + * @see Archive_Tar::createModify() + * @static + * @access public + * @return mixed Returns true on success or PEAR_Error on failure. + * @param string $name name the sent archive should have + * @param mixed $files files/directories + * @param string $type archive type + * @param string $add_path path that should be prepended to the files + * @param string $strip_path path that should be stripped from the files + */ + function send($name, $files, $type = HTTP_DOWNLOAD_TGZ, $add_path = '', $strip_path = '') + { + $tmp = System::mktemp(); + + switch ($type = strToUpper($type)) + { + case HTTP_DOWNLOAD_TAR: + include_once 'Archive/Tar.php'; + $arc = &new Archive_Tar($tmp); + $content_type = 'x-tar'; + break; + + case HTTP_DOWNLOAD_TGZ: + include_once 'Archive/Tar.php'; + $arc = &new Archive_Tar($tmp, 'gz'); + $content_type = 'x-gzip'; + break; + + case HTTP_DOWNLOAD_BZ2: + include_once 'Archive/Tar.php'; + $arc = &new Archive_Tar($tmp, 'bz2'); + $content_type = 'x-bzip2'; + break; + + case HTTP_DOWNLOAD_ZIP: + include_once 'Archive/Zip.php'; + $arc = &new Archive_Zip($tmp); + $content_type = 'x-zip'; + break; + + default: + return PEAR::raiseError( + 'Archive type not supported: ' . $type, + HTTP_DOWNLOAD_E_INVALID_ARCHIVE_TYPE + ); + } + + if ($type == HTTP_DOWNLOAD_ZIP) { + $options = array( 'add_path' => $add_path, + 'remove_path' => $strip_path); + if (!$arc->create($files, $options)) { + return PEAR::raiseError('Archive creation failed.'); + } + } else { + if (!$e = $arc->createModify($files, $add_path, $strip_path)) { + return PEAR::raiseError('Archive creation failed.'); + } + if (PEAR::isError($e)) { + return $e; + } + } + unset($arc); + + $dl = &new HTTP_Download(array('file' => $tmp)); + $dl->setContentType('application/' . $content_type); + $dl->setContentDisposition(HTTP_DOWNLOAD_ATTACHMENT, $name); + return $dl->send(); + } +} +?> diff --git a/includes/pear/HTTP/Download/PgLOB.php b/includes/pear/HTTP/Download/PgLOB.php new file mode 100644 index 0000000..d1c989e --- /dev/null +++ b/includes/pear/HTTP/Download/PgLOB.php @@ -0,0 +1,177 @@ + + * @copyright 2003-2005 Michael Wallner + * @license BSD, revised + * @version CVS: $Id: PgLOB.php 304423 2010-10-15 13:36:46Z clockwerx $ + * @link http://pear.php.net/package/HTTP_Download + */ + +$GLOBALS['_HTTP_Download_PgLOB_Connection'] = null; +stream_register_wrapper('pglob', 'HTTP_Download_PgLOB'); + +/** + * PgSQL large object stream interface for HTTP_Download + * + * Usage: + * + * require_once 'HTTP/Download.php'; + * require_once 'HTTP/Download/PgLOB.php'; + * $db = &DB::connect('pgsql://user:pass@host/db'); + * // or $db = pg_connect(...); + * $lo = HTTP_Download_PgLOB::open($db, 12345); + * $dl = &new HTTP_Download; + * $dl->setResource($lo); + * $dl->send() + * + * + * @access public + * @version $Revision: 304423 $ + */ +class HTTP_Download_PgLOB +{ + /** + * Set Connection + * + * @static + * @access public + * @return bool + * @param mixed $conn + */ + function setConnection($conn) + { + if (is_a($conn, 'DB_Common')) { + $conn = $conn->dbh; + } elseif ( is_a($conn, 'MDB_Common') || + is_a($conn, 'MDB2_Driver_Common')) { + $conn = $conn->connection; + } + if ($isResource = is_resource($conn)) { + $GLOBALS['_HTTP_Download_PgLOB_Connection'] = $conn; + } + return $isResource; + } + + /** + * Get Connection + * + * @static + * @access public + * @return resource + */ + function getConnection() + { + if (is_resource($GLOBALS['_HTTP_Download_PgLOB_Connection'])) { + return $GLOBALS['_HTTP_Download_PgLOB_Connection']; + } + return null; + } + + /** + * Open + * + * @static + * @access public + * @return resource + * @param mixed $conn + * @param int $loid + * @param string $mode + */ + function open($conn, $loid, $mode = 'rb') + { + HTTP_Download_PgLOB::setConnection($conn); + return fopen('pglob:///'. $loid, $mode); + } + + /**#@+ + * Stream Interface Implementation + * @internal + */ + var $ID = 0; + var $size = 0; + var $conn = null; + var $handle = null; + + function stream_open($path, $mode) + { + if (!$this->conn = HTTP_Download_PgLOB::getConnection()) { + return false; + } + if (!preg_match('/(\d+)/', $path, $matches)) { + return false; + } + $this->ID = $matches[1]; + + if (!pg_query($this->conn, 'BEGIN')) { + return false; + } + + $this->handle = pg_lo_open($this->conn, $this->ID, $mode); + if (!is_resource($this->handle)) { + return false; + } + + // fetch size of lob + pg_lo_seek($this->handle, 0, PGSQL_SEEK_END); + $this->size = (int) pg_lo_tell($this->handle); + pg_lo_seek($this->handle, 0, PGSQL_SEEK_SET); + + return true; + } + + function stream_read($length) + { + return pg_lo_read($this->handle, $length); + } + + function stream_seek($offset, $whence = SEEK_SET) + { + return pg_lo_seek($this->handle, $offset, $whence); + } + + function stream_tell() + { + return pg_lo_tell($this->handle); + } + + function stream_eof() + { + return pg_lo_tell($this->handle) >= $this->size; + } + + function stream_flush() + { + return true; + } + + function stream_stat() + { + return array('size' => $this->size, 'ino' => $this->ID); + } + + function stream_write($data) + { + return pg_lo_write($this->handle, $data); + } + + function stream_close() + { + if (pg_lo_close($this->handle)) { + return pg_query($this->conn, 'COMMIT'); + } else { + pg_query($this->conn ,'ROLLBACK'); + return false; + } + } + /**#@-*/ +} + +?> diff --git a/includes/pear/HTTP/Header.php b/includes/pear/HTTP/Header.php new file mode 100644 index 0000000..14026ad --- /dev/null +++ b/includes/pear/HTTP/Header.php @@ -0,0 +1,537 @@ + + * @author Davey Shafik + * @author Michael Wallner + * @copyright 2003-2005 The Authors + * @license BSD, revised + * @version CVS: $Id: Header.php 304418 2010-10-15 13:18:02Z clockwerx $ + * @link http://pear.php.net/package/HTTP_Header + */ + +/** + * Requires HTTP + */ +require_once 'HTTP.php'; + +/**#@+ + * Information Codes + */ +define('HTTP_HEADER_STATUS_100', '100 Continue'); +define('HTTP_HEADER_STATUS_101', '101 Switching Protocols'); +define('HTTP_HEADER_STATUS_102', '102 Processing'); +define('HTTP_HEADER_STATUS_INFORMATIONAL',1); +/**#@-*/ + +/**#+ + * Success Codes + */ +define('HTTP_HEADER_STATUS_200', '200 OK'); +define('HTTP_HEADER_STATUS_201', '201 Created'); +define('HTTP_HEADER_STATUS_202', '202 Accepted'); +define('HTTP_HEADER_STATUS_203', '203 Non-Authoritative Information'); +define('HTTP_HEADER_STATUS_204', '204 No Content'); +define('HTTP_HEADER_STATUS_205', '205 Reset Content'); +define('HTTP_HEADER_STATUS_206', '206 Partial Content'); +define('HTTP_HEADER_STATUS_207', '207 Multi-Status'); +define('HTTP_HEADER_STATUS_SUCCESSFUL',2); +/**#@-*/ + +/**#@+ + * Redirection Codes + */ +define('HTTP_HEADER_STATUS_300', '300 Multiple Choices'); +define('HTTP_HEADER_STATUS_301', '301 Moved Permanently'); +define('HTTP_HEADER_STATUS_302', '302 Found'); +define('HTTP_HEADER_STATUS_303', '303 See Other'); +define('HTTP_HEADER_STATUS_304', '304 Not Modified'); +define('HTTP_HEADER_STATUS_305', '305 Use Proxy'); +define('HTTP_HEADER_STATUS_306', '306 (Unused)'); +define('HTTP_HEADER_STATUS_307', '307 Temporary Redirect'); +define('HTTP_HEADER_STATUS_REDIRECT',3); +/**#@-*/ + +/**#@+ + * Error Codes + */ +define('HTTP_HEADER_STATUS_400', '400 Bad Request'); +define('HTTP_HEADER_STATUS_401', '401 Unauthorized'); +define('HTTP_HEADER_STATUS_402', '402 Payment Granted'); +define('HTTP_HEADER_STATUS_403', '403 Forbidden'); +define('HTTP_HEADER_STATUS_404', '404 File Not Found'); +define('HTTP_HEADER_STATUS_405', '405 Method Not Allowed'); +define('HTTP_HEADER_STATUS_406', '406 Not Acceptable'); +define('HTTP_HEADER_STATUS_407', '407 Proxy Authentication Required'); +define('HTTP_HEADER_STATUS_408', '408 Request Time-out'); +define('HTTP_HEADER_STATUS_409', '409 Conflict'); +define('HTTP_HEADER_STATUS_410', '410 Gone'); +define('HTTP_HEADER_STATUS_411', '411 Length Required'); +define('HTTP_HEADER_STATUS_412', '412 Precondition Failed'); +define('HTTP_HEADER_STATUS_413', '413 Request Entity Too Large'); +define('HTTP_HEADER_STATUS_414', '414 Request-URI Too Large'); +define('HTTP_HEADER_STATUS_415', '415 Unsupported Media Type'); +define('HTTP_HEADER_STATUS_416', '416 Requested range not satisfiable'); +define('HTTP_HEADER_STATUS_417', '417 Expectation Failed'); +define('HTTP_HEADER_STATUS_422', '422 Unprocessable Entity'); +define('HTTP_HEADER_STATUS_423', '423 Locked'); +define('HTTP_HEADER_STATUS_424', '424 Failed Dependency'); +define('HTTP_HEADER_STATUS_CLIENT_ERROR',4); +/**#@-*/ + +/**#@+ + * Server Errors + */ +define('HTTP_HEADER_STATUS_500', '500 Internal Server Error'); +define('HTTP_HEADER_STATUS_501', '501 Not Implemented'); +define('HTTP_HEADER_STATUS_502', '502 Bad Gateway'); +define('HTTP_HEADER_STATUS_503', '503 Service Unavailable'); +define('HTTP_HEADER_STATUS_504', '504 Gateway Time-out'); +define('HTTP_HEADER_STATUS_505', '505 HTTP Version not supported'); +define('HTTP_HEADER_STATUS_507', '507 Insufficient Storage'); +define('HTTP_HEADER_STATUS_SERVER_ERROR',5); +/**#@-*/ + +/** + * HTTP_Header + * + * @package HTTP_Header + * @category HTTP + * @access public + * @version $Revision: 304418 $ + */ +class HTTP_Header extends HTTP +{ + /** + * Default Headers + * + * The values that are set as default, are the same as PHP sends by default. + * + * @var array + * @access private + */ + var $_headers = array( + 'content-type' => 'text/html', + 'pragma' => 'no-cache', + 'cache-control' => 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0' + ); + + /** + * HTTP version + * + * @var string + * @access private + */ + var $_httpVersion = '1.0'; + + /** + * @var bool + * @access public + */ + var $prettify = false; + + /** + * Constructor + * + * Sets HTTP version. + * + * @access public + * @return object HTTP_Header + */ + function HTTP_Header() + { + if (isset($_SERVER['SERVER_PROTOCOL'])) { + $this->setHttpVersion(substr($_SERVER['SERVER_PROTOCOL'], -3)); + } + } + + /** + * Set HTTP version + * + * @access public + * @return bool Returns true on success or false if version doesn't + * match 1.0 or 1.1 (note: 1 will result in 1.0) + * @param mixed $version HTTP version, either 1.0 or 1.1 + */ + function setHttpVersion($version) + { + $version = round((float) $version, 1); + if ($version < 1.0 || $version > 1.1) { + return false; + } + $this->_httpVersion = sprintf('%0.1f', $version); + return true; + } + + /** + * Get HTTP version + * + * @access public + * @return string + */ + function getHttpVersion() + { + return $this->_httpVersion; + } + + /** + * Set Header + * + * The default value for the Last-Modified header will be current + * date and atime if $value is omitted. + * + * @access public + * @return bool Returns true on success or false if $key was empty or + * $value was not of an scalar type. + * @param string $key The name of the header. + * @param string $value The value of the header. (NULL to unset header) + */ + function setHeader($key, $value = null) + { + if (empty($key) || (isset($value) && !is_scalar($value))) { + return false; + } + + $key = strToLower($key); + if ($key == 'last-modified') { + if (!isset($value)) { + $value = HTTP::Date(time()); + } elseif (is_numeric($value)) { + $value = HTTP::Date($value); + } + } + + if (isset($value)) { + $this->_headers[$key] = $value; + } else { + unset($this->_headers[$key]); + } + + return true; + } + + /** + * Get Header + * + * If $key is omitted, all stored headers will be returned. + * + * @access public + * @return mixed Returns string value of the requested header, + * array values of all headers or false if header $key + * is not set. + * @param string $key The name of the header to fetch. + */ + function getHeader($key = null) + { + if (!isset($key)) { + return $this->_headers; + } + + $key = strToLower($key); + + if (!isset($this->_headers[$key])) { + return false; + } + + return $this->_headers[$key]; + } + + /** + * Send Headers + * + * Send out the header that you set via setHeader(). + * + * @access public + * @return bool Returns true on success or false if headers are already + * sent. + * @param array $keys Headers to (not) send, see $include. + * @param array $include If true only $keys matching headers will be + * sent, if false only header not matching $keys will be + * sent. + */ + function sendHeaders($keys = array(), $include = true) + { + if (headers_sent()) { + return false; + } + + if (count($keys)) { + array_change_key_case($keys, CASE_LOWER); + foreach ($this->_headers as $key => $value) { + if ($include ? in_array($key, $keys) : !in_array($key, $keys)) { + header(($this->prettify ? uctitle($key) : $key) .': '. $value); + } + } + } else { + foreach ($this->_headers as $header => $value) { + header(($this->prettify ? uctitle($header) : $header) .': '. $value); + } + } + return true; + } + + /** + * Send Satus Code + * + * Send out the given HTTP-Status code. Use this for example when you + * want to tell the client this page is cached, then you would call + * sendStatusCode(304). + * + * @see HTTP_Header_Cache::exitIfCached() + * + * @access public + * @return bool Returns true on success or false if headers are already + * sent. + * @param int $code The status code to send, i.e. 404, 304, 200, etc. + */ + function sendStatusCode($code) + { + if (headers_sent()) { + return false; + } + + if ($code == (int) $code && defined('HTTP_HEADER_STATUS_'. $code)) { + $code = constant('HTTP_HEADER_STATUS_'. $code); + } + + if (strncasecmp(PHP_SAPI, 'cgi', 3)) { + header('HTTP/'. $this->_httpVersion .' '. $code); + } else { + header('Status: '. $code); + } + return true; + } + + /** + * Date to Timestamp + * + * Converts dates like + * Mon, 31 Mar 2003 15:26:34 GMT + * Tue, 15 Nov 1994 12:45:26 GMT + * into a timestamp, strtotime() didn't do it in older versions. + * + * @deprecated Use PHPs strtotime() instead. + * @access public + * @return mixed Returns int unix timestamp or false if the date doesn't + * seem to be a valid GMT date. + * @param string $date The GMT date. + */ + function dateToTimestamp($date) + { + static $months = array( + null => 0, 'Jan' => 1, 'Feb' => 2, 'Mar' => 3, 'Apr' => 4, + 'May' => 5, 'Jun' => 6, 'Jul' => 7, 'Aug' => 8, 'Sep' => 9, + 'Oct' => 10, 'Nov' => 11, 'Dec' => 12 + ); + + if (-1 < $timestamp = strToTime($date)) { + return $timestamp; + } + + if (!preg_match('~[^,]*,\s(\d+)\s(\w+)\s(\d+)\s(\d+):(\d+):(\d+).*~', + $date, $m)) { + return false; + } + + // [0] => Mon, 31 Mar 2003 15:42:55 GMT + // [1] => 31 [2] => Mar [3] => 2003 [4] => 15 [5] => 42 [6] => 55 + return mktime($m[4], $m[5], $m[6], $months[$m[2]], $m[1], $m[3]); + } + + /** + * Redirect + * + * This function redirects the client. This is done by issuing a Location + * header and exiting. Additionally to HTTP::redirect() you can also add + * parameters to the url. + * + * If you dont need parameters to be added, simply use HTTP::redirect() + * otherwise use HTTP_Header::redirect(). + * + * @see HTTP::redirect() + * @author Wolfram Kriesing + * @access public + * @return void + * @param string $url The URL to redirect to, if none is given it + * redirects to the current page. + * @param array $param Array of query string parameters to add; usually + * a set of key => value pairs; if an array entry consists + * only of an value it is used as key and the respective + * value is fetched from $GLOBALS[$value] + * @param bool $session Whether the session name/id should be added + */ + function redirect($url = null, $param = array(), $session = false) + { + if (!isset($url)) { + $url = $_SERVER['PHP_SELF']; + } + + $qs = array(); + + if ($session) { + $qs[] = session_name() .'='. session_id(); + } + + if (is_array($param) && count($param)) { + if (count($param)) { + foreach ($param as $key => $val) { + if (is_string($key)) { + $qs[] = urlencode($key) .'='. urlencode($val); + } else { + $qs[] = urlencode($val) .'='. urlencode(@$GLOBALS[$val]); + } + } + } + } + + if ($qstr = implode('&', $qs)) { + $purl = parse_url($url); + $url .= (isset($purl['query']) ? '&' : '?') . $qstr; + } + + parent::redirect($url); + } + + /**#@+ + * @author Davey Shafik + * @param int $http_code HTTP Code to check + * @access public + */ + + /** + * Return HTTP Status Code Type + * + * @return int|false + */ + function getStatusType($http_code) + { + if(is_int($http_code) && defined('HTTP_HEADER_STATUS_' .$http_code) || defined($http_code)) { + $type = substr($http_code,0,1); + switch ($type) { + case HTTP_HEADER_STATUS_INFORMATIONAL: + case HTTP_HEADER_STATUS_SUCCESSFUL: + case HTTP_HEADER_STATUS_REDIRECT: + case HTTP_HEADER_STATUS_CLIENT_ERROR: + case HTTP_HEADER_STATUS_SERVER_ERROR: + return $type; + break; + default: + return false; + break; + } + } else { + return false; + } + } + + /** + * Return Status Code Message + * + * @return string|false + */ + function getStatusText($http_code) + { + if ($this->getStatusType($http_code)) { + if (is_int($http_code) && defined('HTTP_HEADER_STATUS_' .$http_code)) { + return substr(constant('HTTP_HEADER_STATUS_' .$http_code),4); + } else { + return substr($http_code,4); + } + } else { + return false; + } + } + + /** + * Checks if HTTP Status code is Information (1xx) + * + * @return boolean + */ + function isInformational($http_code) + { + if ($status_type = $this->getStatusType($http_code)) { + return $status_type{0} == HTTP_HEADER_STATUS_INFORMATIONAL; + } else { + return false; + } + } + + /** + * Checks if HTTP Status code is Successful (2xx) + * + * @return boolean + */ + function isSuccessful($http_code) + { + if ($status_type = $this->getStatusType($http_code)) { + return $status_type{0} == HTTP_HEADER_STATUS_SUCCESSFUL; + } else { + return false; + } + } + + /** + * Checks if HTTP Status code is a Redirect (3xx) + * + * @return boolean + */ + function isRedirect($http_code) + { + if ($status_type = $this->getStatusType($http_code)) { + return $status_type{0} == HTTP_HEADER_STATUS_REDIRECT; + } else { + return false; + } + } + + /** + * Checks if HTTP Status code is a Client Error (4xx) + * + * @return boolean + */ + function isClientError($http_code) + { + if ($status_type = $this->getStatusType($http_code)) { + return $status_type{0} == HTTP_HEADER_STATUS_CLIENT_ERROR; + } else { + return false; + } + } + + /** + * Checks if HTTP Status code is Server Error (5xx) + * + * @return boolean + */ + function isServerError($http_code) + { + if ($status_type = $this->getStatusType($http_code)) { + return $status_type{0} == HTTP_HEADER_STATUS_SERVER_ERROR; + } else { + return false; + } + } + + /** + * Checks if HTTP Status code is Server OR Client Error (4xx or 5xx) + * + * @return boolean + */ + function isError($http_code) + { + if ($status_type = $this->getStatusType($http_code)) { + return (($status_type == HTTP_HEADER_STATUS_CLIENT_ERROR) || ($status_type == HTTP_HEADER_STATUS_SERVER_ERROR)) ? true : false; + } else { + return false; + } + } + /**#@-*/ +} +?> diff --git a/includes/pear/HTTP/Header/Cache.php b/includes/pear/HTTP/Header/Cache.php new file mode 100644 index 0000000..f6ae867 --- /dev/null +++ b/includes/pear/HTTP/Header/Cache.php @@ -0,0 +1,238 @@ + + * @author Michael Wallner + * @copyright 2003-2005 The Authors + * @license BSD, revised + * @version CVS: $Id: Cache.php 304418 2010-10-15 13:18:02Z clockwerx $ + * @link http://pear.php.net/package/HTTP_Header + */ + +/** + * Requires HTTP_Header + */ +require_once 'HTTP/Header.php'; + +/** + * HTTP_Header_Cache + * + * This package provides methods to easier handle caching of HTTP pages. That + * means that the pages can be cached at the client (user agent or browser) and + * your application only needs to send "hey client you already have the pages". + * + * Which is done by sending the HTTP-Status "304 Not Modified", so that your + * application load and the network traffic can be reduced, since you only need + * to send the complete page once. This is really an advantage e.g. for + * generated style sheets, or simply pages that do only change rarely. + * + * Usage: + * + * require_once 'HTTP/Header/Cache.php'; + * $httpCache = new HTTP_Header_Cache(4, 'weeks'); + * $httpCache->sendHeaders(); + * // your code goes here + * + * + * @package HTTP_Header + * @category HTTP + * @access public + * @version $Revision: 304418 $ + */ +class HTTP_Header_Cache extends HTTP_Header +{ + /** + * Constructor + * + * Set the amount of time to cache. + * + * @access public + * @return object HTTP_Header_Cache + * @param int $expires + * @param string $unit + */ + function HTTP_Header_Cache($expires = 0, $unit = 'seconds') + { + parent::HTTP_Header(); + $this->setHeader('Pragma', 'cache'); + $this->setHeader('Last-Modified', $this->getCacheStart()); + $this->setHeader('Cache-Control', 'private, must-revalidate, max-age=0'); + + if ($expires) { + if (!$this->isOlderThan($expires, $unit)) { + $this->exitCached(); + } + $this->setHeader('Last-Modified', time()); + } + } + + /** + * Get Cache Start + * + * Returns the unix timestamp of the If-Modified-Since HTTP header or the + * current time if the header was not sent by the client. + * + * @access public + * @return int unix timestamp + */ + function getCacheStart() + { + if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && !$this->isPost()) { + return strtotime(current($array = explode(';', + $_SERVER['HTTP_IF_MODIFIED_SINCE']))); + } + return time(); + } + + /** + * Is Older Than + * + * You can call it like this: + * + * $httpCache->isOlderThan(1, 'day'); + * $httpCache->isOlderThan(47, 'days'); + * + * $httpCache->isOlderThan(1, 'week'); + * $httpCache->isOlderThan(3, 'weeks'); + * + * $httpCache->isOlderThan(1, 'hour'); + * $httpCache->isOlderThan(5, 'hours'); + * + * $httpCache->isOlderThan(1, 'minute'); + * $httpCache->isOlderThan(15, 'minutes'); + * + * $httpCache->isOlderThan(1, 'second'); + * $httpCache->isOlderThan(15); + * + * + * If you specify something greater than "weeks" as time untit, it just + * works approximatly, because a month is taken to consist of 4.3 weeks. + * + * @access public + * @return bool Returns true if requested page is older than specified. + * @param int $time The amount of time. + * @param string $unit The unit of the time amount - (year[s], month[s], + * week[s], day[s], hour[s], minute[s], second[s]). + */ + function isOlderThan($time = 0, $unit = 'seconds') + { + if (!isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) || $this->isPost()) { + return true; + } + if (!$time) { + return false; + } + + switch (strtolower($unit)) + { + case 'year': + case 'years': + $time *= 12; + case 'month': + case 'months': + $time *= 4.3; + case 'week': + case 'weeks': + $time *= 7; + case 'day': + case 'days': + $time *= 24; + case 'hour': + case 'hours': + $time *= 60; + case 'minute': + case 'minutes': + $time *= 60; + } + + return (time() - $this->getCacheStart()) > $time; + } + + /** + * Is Cached + * + * Check whether we can consider to be cached on the client side. + * + * @access public + * @return bool Whether the page/resource is considered to be cached. + * @param int $lastModified Unix timestamp of last modification. + */ + function isCached($lastModified = 0) + { + if ($this->isPost()) { + return false; + } + if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && !$lastModified) { + return true; + } + if (!$seconds = time() - $lastModified) { + return false; + } + return !$this->isOlderThan($seconds); + } + + /** + * Is Post + * + * Check if request method is "POST". + * + * @access public + * @return bool + */ + function isPost() + { + return isset($_SERVER['REQUEST_METHOD']) and + 'POST' == $_SERVER['REQUEST_METHOD']; + } + + /** + * Exit If Cached + * + * Exit with "HTTP 304 Not Modified" if we consider to be cached. + * + * @access public + * @return void + * @param int $lastModified Unix timestamp of last modification. + */ + function exitIfCached($lastModified = 0) + { + if ($this->isCached($lastModified)) { + $this->exitCached(); + } + } + + /** + * Exit Cached + * + * Exit with "HTTP 304 Not Modified". + * + * @access public + * @return void + */ + function exitCached() + { + $this->sendHeaders(); + $this->sendStatusCode(304); + exit; + } + + /** + * Set Last Modified + * + * @access public + * @return void + * @param int $lastModified The unix timestamp of last modification. + */ + function setLastModified($lastModified = null) + { + $this->setHeader('Last-Modified', $lastModified); + } +} +?> diff --git a/includes/pear/Image/GraphViz.php b/includes/pear/Image/GraphViz.php new file mode 100644 index 0000000..d7de7c5 --- /dev/null +++ b/includes/pear/Image/GraphViz.php @@ -0,0 +1,1005 @@ + and + * Sebastian Bergmann . All rights reserved. + * + * LICENSE: This source file is subject to version 3.0 of the PHP license + * that is available through the world-wide-web at the following URI: + * http://www.php.net/license/3_0.txt. If you did not receive a copy of + * the PHP License and are unable to obtain it through the web, please + * send a note to license@php.net so we can mail you a copy immediately. + * + * @category Image + * @package Image_GraphViz + * @author Dr. Volker Göbbels + * @author Sebastian Bergmann + * @author Karsten Dambekalns + * @author Michael Lively Jr. + * @author Philippe Jausions + * @copyright 2001-2007 Dr. Volker Göbbels and Sebastian Bergmann + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: GraphViz.php 304688 2010-10-24 05:21:17Z clockwerx $ + * @link http://pear.php.net/package/Image_GraphViz + * @link http://www.graphviz.org/ + * @since File available since Release 0.1.0 + */ + +/** + * Required PEAR classes + */ +require_once 'System.php'; + +/** + * Interface to AT&T's GraphViz tools. + * + * The GraphViz class allows for the creation of and to work with directed + * and undirected graphs and their visualization with AT&T's GraphViz tools. + * + * + * addNode( + * 'Node1', + * array( + * 'URL' => 'http://link1', + * 'label' => 'This is a label', + * 'shape' => 'box' + * ) + * ); + * + * $graph->addNode( + * 'Node2', + * array( + * 'URL' => 'http://link2', + * 'fontsize' => '14' + * ) + * ); + * + * $graph->addNode( + * 'Node3', + * array( + * 'URL' => 'http://link3', + * 'fontsize' => '20' + * ) + * ); + * + * $graph->addEdge( + * array( + * 'Node1' => 'Node2' + * ), + * array( + * 'label' => 'Edge Label' + * ) + * ); + * + * $graph->addEdge( + * array( + * 'Node1' => 'Node2' + * ), + * array( + * 'color' => 'red' + * ) + * ); + * + * $graph->image(); + * ?> + * + * + * @category Image + * @package Image_GraphViz + * @author Sebastian Bergmann + * @author Dr. Volker Göbbels + * @author Karsten Dambekalns + * @author Michael Lively Jr. + * @author Philippe Jausions + * @copyright 2001-2007 Dr. Volker Göbbels and Sebastian Bergmann + * @license http://www.php.net/license/3_0.txt The PHP License, Version 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Image_GraphViz + * @link http://www.graphviz.org/ + * @since Class available since Release 0.1 + */ +class Image_GraphViz +{ + /** + * Base path to GraphViz commands + * + * @var string + */ + var $binPath = ''; + + /** + * Path to GraphViz/dot command + * + * @var string + */ + var $dotCommand = 'dot'; + + /** + * Path to GraphViz/neato command + * + * @var string + */ + var $neatoCommand = 'neato'; + + /** + * Representation of the graph + * + * @var array + */ + var $graph = array('edgesFrom' => array(), + 'nodes' => array(), + 'attributes' => array(), + 'directed' => true, + 'clusters' => array(), + 'subgraphs' => array(), + 'name' => 'G', + 'strict' => true, + ); + + /** + * Whether to return PEAR_Error instance on failures instead of FALSE + * + * @var boolean + */ + protected $_returnFalseOnError = true; + + /** + * Constructor. + * + * Setting the name of the Graph is useful for including multiple image + * maps on one page. If not set, the graph will be named 'G'. + * + * @param boolean $directed Directed (TRUE) or undirected (FALSE) graph. + * Note: You MUST pass a boolean, and not just + * an expression that evaluates to TRUE or + * FALSE (i.e. NULL, empty string, 0 will NOT + * work) + * @param array $attributes Attributes of the graph + * @param string $name Name of the Graph + * @param boolean $strict Whether to collapse multiple edges between + * same nodes + * @param boolean $returnError Set to TRUE to return PEAR_Error instances + * on failures instead of FALSE + */ + public function Image_GraphViz($directed = true, $attributes = array(), + $name = 'G', $strict = true, $returnError = false) + { + $this->setDirected($directed); + $this->setAttributes($attributes); + $this->graph['name'] = $name; + $this->graph['strict'] = (boolean)$strict; + + $this->_returnFalseOnError = !$returnError; + } + + /** + * Outputs image of the graph in a given format + * + * This methods send HTTP headers + * + * @param string $format Format of the output image. This may be one + * of the formats supported by GraphViz. + * @param string $command "dot" or "neato" + * + * @return boolean TRUE on success, FALSE or PEAR_Error otherwise + */ + public function image($format = 'svg', $command = null) + { + $file = $this->saveParsedGraph(); + if (!$file || PEAR::isError($file)) { + return $file; + } + + $outputfile = $file . '.' . $format; + + $rendered = $this->renderDotFile($file, $outputfile, $format, + $command); + if ($rendered !== true) { + return $rendered; + } + + $sendContentLengthHeader = true; + + switch (strtolower($format)) { + case 'gif': + case 'png': + case 'bmp': + case 'jpeg': + case 'tiff': + header('Content-Type: image/' . $format); + break; + + case 'tif': + header('Content-Type: image/tiff'); + break; + + case 'jpg': + header('Content-Type: image/jpeg'); + break; + + case 'ico': + header('Content-Type: image/x-icon'); + break; + + case 'wbmp': + header('Content-Type: image/vnd.wap.wbmp'); + break; + + case 'pdf': + header('Content-Type: application/pdf'); + break; + + case 'mif': + header('Content-Type: application/vnd.mif'); + break; + + case 'vrml': + header('Content-Type: application/x-vrml'); + break; + + case 'svg': + header('Content-Type: image/svg+xml'); + break; + + case 'plain': + case 'plain-ext': + header('Content-Type: text/plain'); + break; + + default: + header('Content-Type: application/octet-stream'); + $sendContentLengthHeader = false; + } + + if ($sendContentLengthHeader) { + header('Content-Length: ' . filesize($outputfile)); + } + + $return = true; + if (readfile($outputfile) === false) { + $return = false; + } + @unlink($outputfile); + + return $return; + } + + /** + * Returns image (data) of the graph in a given format. + * + * @param string $format Format of the output image. This may be one + * of the formats supported by GraphViz. + * @param string $command "dot" or "neato" + * + * @return string The image (data) created by GraphViz, FALSE or PEAR_Error + * on error + * @since Method available since Release 1.1.0 + */ + public function fetch($format = 'svg', $command = null) + { + $file = $this->saveParsedGraph(); + if (!$file || PEAR::isError($file)) { + return $file; + } + + $outputfile = $file . '.' . $format; + + $rendered = $this->renderDotFile($file, $outputfile, $format, + $command); + if ($rendered !== true) { + return $rendered; + } + + @unlink($file); + + $fp = fopen($outputfile, 'rb'); + + if (!$fp) { + if ($this->_returnFalseOnError) { + return false; + } + $error = PEAR::raiseError('Could not read rendered file'); + return $error; + } + + $data = fread($fp, filesize($outputfile)); + fclose($fp); + @unlink($outputfile); + + return $data; + } + + /** + * Renders a given dot file into a given format. + * + * @param string $dotfile The absolute path of the dot file to use. + * @param string $outputfile The absolute path of the file to save to. + * @param string $format Format of the output image. This may be one + * of the formats supported by GraphViz. + * @param string $command "dot" or "neato" + * + * @return boolean TRUE if the file was saved, FALSE or PEAR_Error + * otherwise. + */ + public function renderDotFile($dotfile, $outputfile, $format = 'svg', + $command = null) + { + if (!file_exists($dotfile)) { + if ($this->_returnFalseOnError) { + return false; + } + $error = PEAR::raiseError('Could not find dot file'); + return $error; + } + + $oldmtime = file_exists($outputfile) ? filemtime($outputfile) : 0; + + switch ($command) { + case 'dot': + case 'neato': + break; + default: + $command = $this->graph['directed'] ? 'dot' : 'neato'; + } + $command_orig = $command; + + $command = $this->binPath.(($command == 'dot') ? $this->dotCommand + : $this->neatoCommand); + + $command .= ' -T'.escapeshellarg($format) + .' -o'.escapeshellarg($outputfile) + .' '.escapeshellarg($dotfile) + .' 2>&1'; + exec($command, $msg, $return_val); + + clearstatcache(); + if (file_exists($outputfile) && filemtime($outputfile) > $oldmtime + && $return_val == 0) { + return true; + } elseif ($this->_returnFalseOnError) { + return false; + } + $error = PEAR::raiseError($command_orig.' command failed: ' + .implode("\n", $msg)); + return $error; + } + + /** + * Adds a cluster to the graph. + * + * A cluster is a subgraph with a rectangle around it. + * + * @param string $id ID. + * @param array $title Title. + * @param array $attributes Attributes of the cluster. + * @param string $group ID of group to nest cluster into + * + * @return void + * @see addSubgraph() + */ + public function addCluster($id, $title, $attributes = array(), $group = 'default') + { + $this->graph['clusters'][$id]['title'] = $title; + $this->graph['clusters'][$id]['attributes'] = $attributes; + $this->graph['clusters'][$id]['embedIn'] = $group; + } + + /** + * Adds a subgraph to the graph. + * + * @param string $id ID. + * @param array $title Title. + * @param array $attributes Attributes of the cluster. + * @param string $group ID of group to nest subgraph into + * + * @return void + */ + public function addSubgraph($id, $title, $attributes = array(), $group = 'default') + { + $this->graph['subgraphs'][$id]['title'] = $title; + $this->graph['subgraphs'][$id]['attributes'] = $attributes; + $this->graph['subgraphs'][$id]['embedIn'] = $group; + } + + /** + * Adds a note to the graph. + * + * @param string $name Name of the node. + * @param array $attributes Attributes of the node. + * @param string $group Group of the node. + * + * @return void + */ + public function addNode($name, $attributes = array(), $group = 'default') + { + $this->graph['nodes'][$group][$name] = $attributes; + } + + /** + * Removes a node from the graph. + * + * This method doesn't remove edges associated with the node. + * + * @param string $name Name of the node to be removed. + * @param string $group Group of the node. + * + * @return void + */ + public function removeNode($name, $group = 'default') + { + if (isset($this->graph['nodes'][$group][$name])) { + unset($this->graph['nodes'][$group][$name]); + } + } + + /** + * Adds an edge to the graph. + * + * Examples: + * + * $g->addEdge(array('node1' => 'node2')); + * $attr = array( + * 'label' => '+1', + * 'style' => 'dashed', + * ); + * $g->addEdge(array('node3' => 'node4'), $attr); + * + * // With port specification + * $g->addEdge(array('node5' => 'node6'), $attr, array('node6' => 'portA')); + * $g->addEdge(array('node7' => 'node8'), null, array('node7' => 'portC', + * 'node8' => 'portD')); + * + * + * @param array $edge Start => End node of the edge. + * @param array $attributes Attributes of the edge. + * @param array $ports Start node => port, End node => port + * + * @return integer an edge ID that can be used with {@link removeEdge()} + */ + public function addEdge($edge, $attributes = array(), $ports = array()) + { + if (!is_array($edge)) { + return; + } + + $from = key($edge); + $to = $edge[$from]; + $info = array(); + + if (is_array($ports)) { + if (array_key_exists($from, $ports)) { + $info['portFrom'] = $ports[$from]; + } + + if (array_key_exists($to, $ports)) { + $info['portTo'] = $ports[$to]; + } + } + + if (is_array($attributes)) { + $info['attributes'] = $attributes; + } + + if (!empty($this->graph['strict'])) { + if (!isset($this->graph['edgesFrom'][$from][$to][0])) { + $this->graph['edgesFrom'][$from][$to][0] = $info; + } else { + $this->graph['edgesFrom'][$from][$to][0] = array_merge($this->graph['edgesFrom'][$from][$to][0], $info); + } + } else { + $this->graph['edgesFrom'][$from][$to][] = $info; + } + + return count($this->graph['edgesFrom'][$from][$to]) - 1; + } + + /** + * Removes an edge from the graph. + * + * @param array $edge Start and End node of the edge to be removed. + * @param integer $id specific edge ID (only usefull when multiple edges + * exist between the same 2 nodes) + * + * @return void + */ + public function removeEdge($edge, $id = null) + { + if (!is_array($edge)) { + return; + } + + $from = key($edge); + $to = $edge[$from]; + + if (!is_null($id)) { + if (isset($this->graph['edgesFrom'][$from][$to][$id])) { + unset($this->graph['edgesFrom'][$from][$to][$id]); + + if (count($this->graph['edgesFrom'][$from][$to]) == 0) { + unset($this->graph['edgesFrom'][$from][$to]); + } + } + } elseif (isset($this->graph['edgesFrom'][$from][$to])) { + unset($this->graph['edgesFrom'][$from][$to]); + } + } + + /** + * Adds attributes to the graph. + * + * @param array $attributes Attributes to be added to the graph. + * + * @return void + */ + public function addAttributes($attributes) + { + if (is_array($attributes)) { + $this->graph['attributes'] = array_merge($this->graph['attributes'], $attributes); + } + } + + /** + * Sets attributes of the graph. + * + * @param array $attributes Attributes to be set for the graph. + * + * @return void + */ + public function setAttributes($attributes) + { + if (is_array($attributes)) { + $this->graph['attributes'] = $attributes; + } + } + + /** + * Escapes an (attribute) array + * + * Detects if an attribute is , contains double-quotes, etc... + * + * @param array $input input to escape + * + * @return array input escaped + */ + protected function _escapeArray($input) + { + $output = array(); + + foreach ((array)$input as $k => $v) { + switch ($k) { + case 'label': + case 'headlabel': + case 'taillabel': + $v = $this->_escape($v, true); + break; + default: + $v = $this->_escape($v); + $k = $this->_escape($k); + } + + $output[$k] = $v; + } + + return $output; + } + + /** + * Returns a safe "ID" in DOT syntax + * + * @param string $input string to use as "ID" + * @param boolean $html whether to attempt detecting HTML-like content + * + * @return string + */ + protected function _escape($input, $html = false) + { + switch (strtolower($input)) { + case 'node': + case 'edge': + case 'graph': + case 'digraph': + case 'subgraph': + case 'strict': + return '"'.$input.'"'; + } + + if (is_bool($input)) { + return ($input) ? 'true' : 'false'; + } + + if ($html && (strpos($input, '') !== false)) { + return '<'.$input.'>'; + } + + if (preg_match('/^([a-z_][a-z_0-9]*|-?(\.[0-9]+|[0-9]+(\.[0-9]*)?))$/i', + $input)) { + return $input; + } + + return '"'.str_replace(array("\r\n", "\n", "\r", '"'), + array('\n', '\n', '\n', '\"'), $input).'"'; + } + + /** + * Sets directed/undirected flag for the graph. + * + * Note: You MUST pass a boolean, and not just an expression that evaluates + * to TRUE or FALSE (i.e. NULL, empty string, 0 will not work) + * + * @param boolean $directed Directed (TRUE) or undirected (FALSE) graph. + * + * @return void + */ + public function setDirected($directed) + { + if (is_bool($directed)) { + $this->graph['directed'] = $directed; + } + } + + /** + * Loads a graph from a file in Image_GraphViz format + * + * @param string $file File to load graph from. + * + * @return void + */ + public function load($file) + { + if ($serializedGraph = implode('', @file($file))) { + $g = unserialize($serializedGraph); + + if (!is_array($g)) { + return; + } + + // Convert old storage format to new one + $defaults = array('edgesFrom' => array(), + 'nodes' => array(), + 'attributes' => array(), + 'directed' => true, + 'clusters' => array(), + 'subgraphs' => array(), + 'name' => 'G', + 'strict' => true, + ); + + $this->graph = array_merge($defaults, $g); + + if (isset($this->graph['edges'])) { + foreach ($this->graph['edges'] as $id => $nodes) { + $attr = (isset($this->graph['edgeAttributes'][$id])) + ? $this->graph['edgeAttributes'][$id] + : array(); + + $this->addEdge($nodes, $attr); + } + + unset($this->graph['edges']); + unset($this->graph['edgeAttributes']); + } + } + } + + /** + * Save graph to file in Image_GraphViz format + * + * This saves the serialized version of the instance, not the + * rendered graph. + * + * @param string $file File to save the graph to. + * + * @return string File the graph was saved to, FALSE or PEAR_Error on + * failure. + */ + public function save($file = '') + { + $serializedGraph = serialize($this->graph); + + if (empty($file)) { + $file = System::mktemp('graph_'); + } + + if ($fp = @fopen($file, 'wb')) { + @fputs($fp, $serializedGraph); + @fclose($fp); + + return $file; + } + + if ($this->_returnFalseOnError) { + return false; + } + $error = PEAR::raiseError('Could not save serialized graph instance'); + return $error; + } + + /** + * Returns a list of sub-groups for a given parent group + * + * @param string $parent Group ID + * + * @return array list of group IDs + */ + protected function _getSubgraphs($parent) + { + $subgraphs = array(); + foreach ($this->graph['clusters'] as $id => $info) { + if ($info['embedIn'] === $parent) { + $subgraphs[] = $id; + } + } + foreach ($this->graph['subgraphs'] as $id => $info) { + if ($info['embedIn'] === $parent) { + $subgraphs[] = $id; + } + } + return $subgraphs; + } + + /** + * Returns a list of cluster/subgraph IDs + * + * @return array + */ + protected function _getGroups() + { + $groups = array_merge(array_keys($this->graph['clusters']), + array_keys($this->graph['subgraphs'])); + return array_unique($groups); + } + + /** + * Returns a list of top groups + * + * @return array + */ + protected function _getTopGraphs() + { + $top = array(); + $groups = $this->_getGroups(); + + foreach ($groups as $id) { + $isTop = ($id === 'default'); + if (isset($this->graph['clusters'][$id]) + && $this->graph['clusters'][$id]['embedIn'] === 'default') { + $isTop = true; + } + if (isset($this->graph['subgraphs'][$id]) + && $this->graph['subgraphs'][$id]['embedIn'] === 'default') { + $isTop = true; + } + if ($isTop) { + $top[] = $id; + } + } + + return array_unique($top); + } + + /** + * Parses the graph into GraphViz markup. + * + * @return string GraphViz markup + */ + public function parse() + { + $parsedGraph = (empty($this->graph['strict'])) ? '' : 'strict '; + $parsedGraph .= (empty($this->graph['directed'])) ? 'graph ' : 'digraph '; + $parsedGraph .= $this->_escape($this->graph['name'])." {\n"; + + $indent = ' '; + + $attr = $this->_escapeArray($this->graph['attributes']); + + foreach ($attr as $key => $value) { + $parsedGraph .= $indent.$key.'='.$value.";\n"; + } + + $groups = $this->_getGroups(); + foreach ($this->graph['nodes'] as $group => $nodes) { + if (!in_array($group, $groups)) { + $parsedGraph .= $this->_nodes($nodes, $indent); + } + } + $tops = $this->_getTopGraphs(); + foreach ($tops as $group) { + $parsedGraph .= $this->_subgraph($group, $indent); + } + + if (!empty($this->graph['directed'])) { + $separator = ' -> '; + } else { + $separator = ' -- '; + } + + foreach ($this->graph['edgesFrom'] as $from => $toNodes) { + $from = $this->_escape($from); + + foreach ($toNodes as $to => $edges) { + $to = $this->_escape($to); + + foreach ($edges as $info) { + $f = $from; + $t = $to; + + if (array_key_exists('portFrom', $info)) { + $f .= ':'.$this->_escape($info['portFrom']); + } + + if (array_key_exists('portTo', $info)) { + $t .= ':'.$this->_escape($info['portTo']); + } + + $parsedGraph .= $indent.$f.$separator.$t; + + if (!empty($info['attributes'])) { + $attributeList = array(); + + foreach ($this->_escapeArray($info['attributes']) as $key => $value) { + switch ($key) { + case 'lhead': + case 'ltail': + if (strncasecmp($value, 'cluster', 7)) { + $value = 'cluster_'.$value; + } + break; + } + $attributeList[] = $key.'='.$value; + } + + $parsedGraph .= ' [ '.implode(',', $attributeList).' ]'; + } + + $parsedGraph .= ";\n"; + } + } + } + + return $parsedGraph . "}\n"; + } + + /** + * Output nodes + * + * @param array $nodes nodes list + * @param string $indent space indentation + * + * @return string output + */ + protected function _nodes($nodes, $indent) + { + $parsedGraph = ''; + foreach ($nodes as $node => $attributes) { + $parsedGraph .= $indent.$this->_escape($node); + + $attributeList = array(); + + foreach ($this->_escapeArray($attributes) as $key => $value) { + $attributeList[] = $key.'='.$value; + } + + if (!empty($attributeList)) { + $parsedGraph .= ' [ '.implode(',', $attributeList).' ]'; + } + + $parsedGraph .= ";\n"; + } + return $parsedGraph; + } + + /** + * Generates output for a group + * + * @return string output + */ + protected function _subgraph($group, &$indent) + { + $parsedGraph = ''; + $nodes = $this->graph['nodes'][$group]; + + if ($group !== 'default') { + $type = null; + $_group = $this->_escape($group); + + if (isset($this->graph['clusters'][$group])) { + $type = 'clusters'; + if (strncasecmp($group, 'cluster', 7)) { + $_group = $this->_escape('cluster_'.$group); + } + } elseif (isset($this->graph['subgraphs'][$group])) { + $type = 'subgraphs'; + } + $parsedGraph .= $indent.'subgraph '.$_group." {\n"; + + $indent .= ' '; + + if ($type !== null && isset($this->graph[$type][$group])) { + $cluster = $this->graph[$type][$group]; + $_attr = $this->_escapeArray($cluster['attributes']); + + $attr = array(); + foreach ($_attr as $key => $value) { + $attr[] = $key.'='.$value; + } + + if (strlen($cluster['title'])) { + $attr[] = 'label=' + .$this->_escape($cluster['title'], true); + } + + if ($attr) { + $parsedGraph .= $indent.'graph [ '.implode(',', $attr) + ." ];\n"; + } + } + } + + $parsedGraph .= $this->_nodes($nodes, $indent); + + foreach ($this->_getSubgraphs($group) as $_group) { + $parsedGraph .= $this->_subgraph($_group, $indent); + } + + if ($group !== 'default') { + $indent = substr($indent, 0, -4); + + $parsedGraph .= $indent."}\n"; + } + + return $parsedGraph; + } + + /** + * Saves GraphViz markup to file (in DOT language) + * + * @param string $file File to write the GraphViz markup to. + * + * @return string File to which the GraphViz markup was written, FALSE or + * or PEAR_Error on failure. + */ + public function saveParsedGraph($file = '') + { + $parsedGraph = $this->parse(); + + if (!empty($parsedGraph)) { + if (empty($file)) { + $file = System::mktemp('graph_'); + } + + if ($fp = @fopen($file, 'wb')) { + @fputs($fp, $parsedGraph); + @fclose($fp); + + return $file; + } + } + + if ($this->_returnFalseOnError) { + return false; + } + $error = PEAR::raiseError('Could not save graph'); + return $error; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ +?> diff --git a/includes/pear/MIME/Type.php b/includes/pear/MIME/Type.php new file mode 100644 index 0000000..77491d2 --- /dev/null +++ b/includes/pear/MIME/Type.php @@ -0,0 +1,598 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @link http://pear.php.net/package/MIME_Type + */ + +require_once 'PEAR.php'; + +$_fileCmd = &PEAR::getStaticProperty('MIME_Type', 'fileCmd'); +$_fileCmd = 'file'; + +/** + * Class for working with MIME types + * + * @category MIME + * @package MIME_Type + * @author Ian Eure + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version Release: @version@ + * @link http://pear.php.net/package/MIME_Type + */ +class MIME_Type +{ + /** + * The MIME media type + * + * @var string + */ + var $media = ''; + + /** + * The MIME media sub-type + * + * @var string + */ + var $subType = ''; + + /** + * Optional MIME parameters + * + * @var array + */ + var $parameters = array(); + + /** + * List of valid media types. + * A media type is the string in front of the slash. + * The media type of "text/xml" would be "text". + * + * @var array + */ + var $validMediaTypes = array( + 'text', + 'image', + 'audio', + 'video', + 'application', + 'multipart', + 'message' + ); + + /** + * If the finfo functions shall be used when they are available + * + * @var boolean + */ + var $useFinfo = true; + + /** + * If mime_content_type shall be used when available + * + * @var boolean + */ + var $useMimeContentType = true; + + /** + * If the file command shall be used when available + * + * @var boolean + */ + var $useFileCmd = true; + + /** + * If the in-built file extension detection shall be used + * + * @var boolean + */ + var $useExtension = true; + + /** + * Path to the "magic" file database. + * If NULL, the default one is used + * + * @var string + */ + var $magicFile = null; + + + /** + * Constructor. + * + * If $type is set, if will be parsed and the appropriate class vars set. + * If not, you get an empty class. + * This is useful, but not quite as useful as parsing a type. + * + * @param string $type MIME type + * + * @return void + */ + function MIME_Type($type = false) + { + if ($type) { + $this->parse($type); + } + } + + + /** + * Parse a mime-type and set the class variables. + * + * @param string $type MIME type to parse + * + * @return boolean True if the type has been parsed, false if not + */ + function parse($type) + { + if ($type instanceof PEAR_Error) { + return false; + } + + $this->media = $this->getMedia($type); + $this->subType = $this->getSubType($type); + $this->parameters = array(); + if (MIME_Type::hasParameters($type)) { + include_once 'MIME/Type/Parameter.php'; + foreach (MIME_Type::getParameters($type) as $param) { + $param = new MIME_Type_Parameter($param); + $this->parameters[$param->name] = $param; + } + } + + return true; + } + + + /** + * Does this type have any parameters? + * + * @param string $type MIME type to check + * + * @return boolean true if $type has parameters, false otherwise + * @static + */ + function hasParameters($type) + { + if (strstr($type, ';')) { + return true; + } + return false; + } + + + /** + * Get a MIME type's parameters + * + * @param string $type MIME type to get parameters of + * + * @return array $type's parameters + * @static + */ + function getParameters($type) + { + $params = array(); + $tmp = explode(';', $type); + for ($i = 1; $i < count($tmp); $i++) { + $params[] = trim($tmp[$i]); + } + return $params; + } + + + /** + * Strip parameters from a MIME type string. + * + * @param string $type MIME type string + * + * @return string MIME type with parameters removed + * @static + */ + function stripParameters($type) + { + if (strstr($type, ';')) { + return substr($type, 0, strpos($type, ';')); + } + return $type; + } + + + /** + * Removes comments from a media type, subtype or parameter. + * + * @param string $string String to strip comments from + * @param string &$comment Comment is stored in there. + * Do not set it to NULL if you want the comment. + * + * @return string String without comments + * @static + */ + function stripComments($string, &$comment) + { + if (strpos($string, '(') === false) { + return $string; + } + + $inquote = false; + $escaped = false; + $incomment = 0; + $newstring = ''; + + for ($n = 0; $n < strlen($string); $n++) { + if ($escaped) { + if ($incomment == 0) { + $newstring .= $string[$n]; + } else if ($comment !== null) { + $comment .= $string[$n]; + } + $escaped = false; + } else if ($string[$n] == '\\') { + $escaped = true; + } else if (!$inquote && $incomment > 0 && $string[$n] == ')') { + $incomment--; + if ($incomment == 0 && $comment !== null) { + $comment .= ' '; + } + } else if (!$inquote && $string[$n] == '(') { + $incomment++; + } else if ($string[$n] == '"') { + if ($inquote) { + $inquote = false; + } else { + $inquote = true; + } + } else if ($incomment == 0) { + $newstring .= $string[$n]; + } else if ($comment !== null) { + $comment .= $string[$n]; + } + } + + if ($comment !== null) { + $comment = trim($comment); + } + + return $newstring; + } + + + /** + * Get a MIME type's media + * Note: 'media' refers to the portion before the first slash + * + * @param string $type MIME type to get media of + * + * @return string $type's media + * @static + */ + function getMedia($type) + { + $tmp = explode('/', $type); + return strtolower(trim(MIME_Type::stripComments($tmp[0], $null))); + } + + + /** + * Get a MIME type's subtype + * + * @param string $type MIME type to get subtype of + * + * @return string $type's subtype, null if invalid mime type + * @static + */ + function getSubType($type) + { + $tmp = explode('/', $type); + if (!isset($tmp[1])) { + return null; + } + $tmp = explode(';', $tmp[1]); + return strtolower(trim(MIME_Type::stripComments($tmp[0], $null))); + } + + + /** + * Create a textual MIME type from object values + * + * This function performs the opposite function of parse(). + * + * @return string MIME type string + */ + function get() + { + $type = strtolower($this->media . '/' . $this->subType); + if (count($this->parameters)) { + foreach ($this->parameters as $key => $null) { + $type .= '; ' . $this->parameters[$key]->get(); + } + } + return $type; + } + + + /** + * Is this type experimental? + * + * Note: Experimental types are denoted by a leading 'x-' in the media or + * subtype, e.g. text/x-vcard or x-world/x-vrml. + * + * @param string $type MIME type to check + * + * @return boolean true if $type is experimental, false otherwise + * @static + */ + function isExperimental($type) + { + if (substr(MIME_Type::getMedia($type), 0, 2) == 'x-' + || substr(MIME_Type::getSubType($type), 0, 2) == 'x-' + ) { + return true; + } + return false; + } + + + /** + * Is this a vendor MIME type? + * + * Note: Vendor types are denoted with a leading 'vnd. in the subtype. + * + * @param string $type MIME type to check + * + * @return boolean true if $type is a vendor type, false otherwise + * @static + */ + function isVendor($type) + { + if (substr(MIME_Type::getSubType($type), 0, 4) == 'vnd.') { + return true; + } + return false; + } + + + /** + * Is this a wildcard type? + * + * @param string $type MIME type to check + * + * @return boolean true if $type is a wildcard, false otherwise + * @static + */ + function isWildcard($type) + { + if ($type == '*/*' || MIME_Type::getSubtype($type) == '*') { + return true; + } + return false; + } + + + /** + * Perform a wildcard match on a MIME type + * + * Example: + * MIME_Type::wildcardMatch('image/*', 'image/png') + * + * @param string $card Wildcard to check against + * @param string $type MIME type to check + * + * @return boolean true if there was a match, false otherwise + * @static + */ + function wildcardMatch($card, $type) + { + if (!MIME_Type::isWildcard($card)) { + return false; + } + + if ($card == '*/*') { + return true; + } + + if (MIME_Type::getMedia($card) == MIME_Type::getMedia($type)) { + return true; + } + + return false; + } + + + /** + * Add a parameter to this type + * + * @param string $name Attribute name + * @param string $value Attribute value + * @param string $comment Comment for this parameter + * + * @return void + */ + function addParameter($name, $value, $comment = false) + { + $tmp = new MIME_Type_Parameter(); + + $tmp->name = $name; + $tmp->value = $value; + $tmp->comment = $comment; + $this->parameters[$name] = $tmp; + } + + + /** + * Remove a parameter from this type + * + * @param string $name Parameter name + * + * @return void + */ + function removeParameter($name) + { + unset($this->parameters[$name]); + } + + + /** + * Autodetect a file's MIME-type + * + * This function may be called staticly. + * + * @param string $file Path to the file to get the type of + * @param bool $params Append MIME parameters if true + * + * @return string $file's MIME-type on success, PEAR_Error otherwise + * + * @since 1.0.0beta1 + * @static + */ + function autoDetect($file, $params = false) + { + $isStatic = !(isset($this) && get_class($this) == __CLASS__); + if ($isStatic) { + $t = new MIME_Type(); + return $t->_autoDetect($file, $params); + } else { + $type = $this->_autoDetect($file, $params); + if (!$type instanceof PEAR_Error) { + $this->parse($type); + } + return $type; + } + } + + /** + * Autodetect a file's MIME-type + * + * @param string $file Path to the file to get the type of + * @param bool $params Append MIME parameters if true + * + * @return string $file's MIME-type on success, PEAR_Error otherwise + * + * @since 1.3.0 + * + * @internal Tries to use fileinfo extension at first. If that + * does not work, mime_magic is used. If this is also not available + * or does not succeed, "file" command is tried to be executed with + * System_Command. When that fails, too, then we use our in-built + * extension-to-mimetype-mapping list. + */ + function _autoDetect($file, $params = false) + { + // Sanity checks + if (!file_exists($file)) { + return PEAR::raiseError("File \"$file\" doesn't exist"); + } + + if (!is_readable($file)) { + return PEAR::raiseError("File \"$file\" is not readable"); + } + + if ($this->useFinfo && function_exists('finfo_file')) { + $finfo = finfo_open(FILEINFO_MIME, $this->magicFile); + if ($finfo) { + $type = finfo_file($finfo, $file); + finfo_close($finfo); + if ($type !== false && $type !== '') { + return MIME_Type::_handleDetection($type, $params); + } + } + } + + if ($this->useMimeContentType && function_exists('mime_content_type')) { + $type = mime_content_type($file); + if ($type !== false && $type !== '') { + return MIME_Type::_handleDetection($type, $params); + } + } + + if ($this->useFileCmd) { + @include_once 'System/Command.php'; + if (class_exists('System_Command')) { + $type = MIME_Type::_fileAutoDetect($file); + if ($type !== false && $type !== '') { + return MIME_Type::_handleDetection($type, $params); + } + } + } + + if ($this->useExtension) { + include_once 'MIME/Type/Extension.php'; + $mte = new MIME_Type_Extension(); + return $mte->getMIMEType($file); + } + + return PEAR::raiseError("Sorry, couldn't determine file type."); + } + + + /** + * Handles a detected MIME type and modifies it if necessary. + * + * @param string $type MIME Type of a file + * @param bool $params Append MIME parameters if true + * + * @return string $file's MIME-type on success, PEAR_Error otherwise + * @static + */ + function _handleDetection($type, $params) + { + // _fileAutoDetect() may have returned an error. + if (PEAR::isError($type)) { + return $type; + } + + // Don't return an empty string + if (!$type || !strlen($type)) { + return PEAR::raiseError("Sorry, couldn't determine file type."); + } + + // Strip parameters if present & requested + if (MIME_Type::hasParameters($type) && !$params) { + $type = MIME_Type::stripParameters($type); + } + + return $type; + } + + + /** + * Autodetect a file's MIME-type with 'file' and System_Command + * + * This function may be called staticly. + * + * @param string $file Path to the file to get the type of + * + * @return string $file's MIME-type + * + * @since 1.0.0beta1 + * @static + */ + function _fileAutoDetect($file) + { + $cmd = new System_Command(); + $magic = ''; + + // Make sure we have the 'file' command. + $fileCmd = PEAR::getStaticProperty('MIME_Type', 'fileCmd'); + if (!$cmd->which($fileCmd)) { + unset($cmd); + return PEAR::raiseError("Can't find file command \"{$fileCmd}\""); + } + if (strlen($this->magicFile)) { + $magic = '--magic-file ' . escapeshellarg($this->magicFile); + } + + $cmd->pushCommand($fileCmd, $magic, "-bi " . escapeshellarg($file)); + $res = $cmd->execute(); + unset($cmd); + + return $res; + } + +} \ No newline at end of file diff --git a/includes/pear/MIME/Type/Extension.php b/includes/pear/MIME/Type/Extension.php new file mode 100644 index 0000000..7f3da7a --- /dev/null +++ b/includes/pear/MIME/Type/Extension.php @@ -0,0 +1,292 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @link http://pear.php.net/package/MIME_Type + */ + +require_once 'PEAR.php'; + +/** + * Class for mapping file extensions to MIME types. + * + * @category MIME + * @package MIME_Type + * @author Christian Schmidt + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version Release: @version@ + * @link http://pear.php.net/package/MIME_Type + */ +class MIME_Type_Extension +{ + /** + * Mapping between file extension and MIME type. + * + * @internal The array is sorted alphabetically by value and with primary + * extension first. Be careful about not adding duplicate keys - PHP + * silently ignores duplicates. The following command can be used for + * checking for duplicates: + * grep "=> '" Extension.php | cut -d\' -f2 | sort | uniq -d + * application/octet-stream is generally used as fallback when no other + * MIME-type can be found, but the array does not contain a lot of such + * unknown extension. One entry exists, though, to allow detection of + * file extension for this MIME-type. + * + * @var array + */ + var $extensionToType = array ( + 'ez' => 'application/andrew-inset', + 'atom' => 'application/atom+xml', + 'jar' => 'application/java-archive', + 'hqx' => 'application/mac-binhex40', + 'cpt' => 'application/mac-compactpro', + 'mathml' => 'application/mathml+xml', + 'doc' => 'application/msword', + 'dat' => 'application/octet-stream', + 'oda' => 'application/oda', + 'ogg' => 'application/ogg', + 'pdf' => 'application/pdf', + 'ai' => 'application/postscript', + 'eps' => 'application/postscript', + 'ps' => 'application/postscript', + 'rdf' => 'application/rdf+xml', + 'rss' => 'application/rss+xml', + 'smi' => 'application/smil', + 'smil' => 'application/smil', + 'gram' => 'application/srgs', + 'grxml' => 'application/srgs+xml', + 'kml' => 'application/vnd.google-earth.kml+xml', + 'kmz' => 'application/vnd.google-earth.kmz', + 'mif' => 'application/vnd.mif', + 'xul' => 'application/vnd.mozilla.xul+xml', + 'xls' => 'application/vnd.ms-excel', + 'xlb' => 'application/vnd.ms-excel', + 'xlt' => 'application/vnd.ms-excel', + 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12', + 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12', + 'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12', + 'xltm' => 'application/vnd.ms-excel.template.macroEnabled.12', + 'docm' => 'application/vnd.ms-word.document.macroEnabled.12', + 'dotm' => 'application/vnd.ms-word.template.macroEnabled.12', + 'ppam' => 'application/vnd.ms-powerpoint.addin.macroEnabled.12', + 'pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12', + 'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12', + 'potm' => 'application/vnd.ms-powerpoint.template.macroEnabled.12', + 'ppt' => 'application/vnd.ms-powerpoint', + 'pps' => 'application/vnd.ms-powerpoint', + 'odc' => 'application/vnd.oasis.opendocument.chart', + 'odb' => 'application/vnd.oasis.opendocument.database', + 'odf' => 'application/vnd.oasis.opendocument.formula', + 'odg' => 'application/vnd.oasis.opendocument.graphics', + 'otg' => 'application/vnd.oasis.opendocument.graphics-template', + 'odi' => 'application/vnd.oasis.opendocument.image', + 'odp' => 'application/vnd.oasis.opendocument.presentation', + 'otp' => 'application/vnd.oasis.opendocument.presentation-template', + 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', + 'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template', + 'odt' => 'application/vnd.oasis.opendocument.text', + 'odm' => 'application/vnd.oasis.opendocument.text-master', + 'ott' => 'application/vnd.oasis.opendocument.text-template', + 'oth' => 'application/vnd.oasis.opendocument.text-web', + 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', + 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', + 'vsd' => 'application/vnd.visio', + 'wbxml' => 'application/vnd.wap.wbxml', + 'wmlc' => 'application/vnd.wap.wmlc', + 'wmlsc' => 'application/vnd.wap.wmlscriptc', + 'vxml' => 'application/voicexml+xml', + 'bcpio' => 'application/x-bcpio', + 'vcd' => 'application/x-cdlink', + 'pgn' => 'application/x-chess-pgn', + 'cpio' => 'application/x-cpio', + 'csh' => 'application/x-csh', + 'dcr' => 'application/x-director', + 'dir' => 'application/x-director', + 'dxr' => 'application/x-director', + 'dvi' => 'application/x-dvi', + 'spl' => 'application/x-futuresplash', + 'tgz' => 'application/x-gtar', + 'gtar' => 'application/x-gtar', + 'hdf' => 'application/x-hdf', + 'js' => 'application/x-javascript', + 'skp' => 'application/x-koan', + 'skd' => 'application/x-koan', + 'skt' => 'application/x-koan', + 'skm' => 'application/x-koan', + 'latex' => 'application/x-latex', + 'nc' => 'application/x-netcdf', + 'cdf' => 'application/x-netcdf', + 'sh' => 'application/x-sh', + 'shar' => 'application/x-shar', + 'swf' => 'application/x-shockwave-flash', + 'sit' => 'application/x-stuffit', + 'sv4cpio' => 'application/x-sv4cpio', + 'sv4crc' => 'application/x-sv4crc', + 'tar' => 'application/x-tar', + 'tcl' => 'application/x-tcl', + 'tex' => 'application/x-tex', + 'texinfo' => 'application/x-texinfo', + 'texi' => 'application/x-texinfo', + 't' => 'application/x-troff', + 'tr' => 'application/x-troff', + 'roff' => 'application/x-troff', + 'man' => 'application/x-troff-man', + 'me' => 'application/x-troff-me', + 'ms' => 'application/x-troff-ms', + 'ustar' => 'application/x-ustar', + 'src' => 'application/x-wais-source', + 'xhtml' => 'application/xhtml+xml', + 'xht' => 'application/xhtml+xml', + 'xslt' => 'application/xslt+xml', + 'xml' => 'application/xml', + 'xsl' => 'application/xml', + 'dtd' => 'application/xml-dtd', + 'zip' => 'application/zip', + 'au' => 'audio/basic', + 'snd' => 'audio/basic', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'kar' => 'audio/midi', + 'mpga' => 'audio/mpeg', + 'mp2' => 'audio/mpeg', + 'mp3' => 'audio/mpeg', + 'aif' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'aifc' => 'audio/x-aiff', + 'm3u' => 'audio/x-mpegurl', + 'wma' => 'audio/x-ms-wma', + 'wax' => 'audio/x-ms-wax', + 'ram' => 'audio/x-pn-realaudio', + 'ra' => 'audio/x-pn-realaudio', + 'rm' => 'application/vnd.rn-realmedia', + 'wav' => 'audio/x-wav', + 'pdb' => 'chemical/x-pdb', + 'xyz' => 'chemical/x-xyz', + 'bmp' => 'image/bmp', + 'cgm' => 'image/cgm', + 'gif' => 'image/gif', + 'ief' => 'image/ief', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'jpe' => 'image/jpeg', + 'png' => 'image/png', + 'svg' => 'image/svg+xml', + 'tiff' => 'image/tiff', + 'tif' => 'image/tiff', + 'djvu' => 'image/vnd.djvu', + 'djv' => 'image/vnd.djvu', + 'wbmp' => 'image/vnd.wap.wbmp', + 'ras' => 'image/x-cmu-raster', + 'ico' => 'image/x-icon', + 'pnm' => 'image/x-portable-anymap', + 'pbm' => 'image/x-portable-bitmap', + 'pgm' => 'image/x-portable-graymap', + 'ppm' => 'image/x-portable-pixmap', + 'rgb' => 'image/x-rgb', + 'xbm' => 'image/x-xbitmap', + 'psd' => 'image/x-photoshop', + 'xpm' => 'image/x-xpixmap', + 'xwd' => 'image/x-xwindowdump', + 'eml' => 'message/rfc822', + 'igs' => 'model/iges', + 'iges' => 'model/iges', + 'msh' => 'model/mesh', + 'mesh' => 'model/mesh', + 'silo' => 'model/mesh', + 'wrl' => 'model/vrml', + 'vrml' => 'model/vrml', + 'ics' => 'text/calendar', + 'ifb' => 'text/calendar', + 'css' => 'text/css', + 'csv' => 'text/csv', + 'html' => 'text/html', + 'htm' => 'text/html', + 'txt' => 'text/plain', + 'asc' => 'text/plain', + 'rtx' => 'text/richtext', + 'rtf' => 'text/rtf', + 'sgml' => 'text/sgml', + 'sgm' => 'text/sgml', + 'tsv' => 'text/tab-separated-values', + 'wml' => 'text/vnd.wap.wml', + 'wmls' => 'text/vnd.wap.wmlscript', + 'etx' => 'text/x-setext', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpe' => 'video/mpeg', + 'qt' => 'video/quicktime', + 'mov' => 'video/quicktime', + 'mxu' => 'video/vnd.mpegurl', + 'm4u' => 'video/vnd.mpegurl', + 'flv' => 'video/x-flv', + 'asf' => 'video/x-ms-asf', + 'asx' => 'video/x-ms-asf', + 'wmv' => 'video/x-ms-wmv', + 'wm' => 'video/x-ms-wm', + 'wmx' => 'video/x-ms-wmx', + 'avi' => 'video/x-msvideo', + 'ogv' => 'video/ogg', + 'movie' => 'video/x-sgi-movie', + 'ice' => 'x-conference/x-cooltalk', + ); + + + + /** + * Autodetect a file's MIME-type. + * + * @param string $file Path to the file to get the type of + * + * @return string $file's MIME-type on success, PEAR_Error otherwise + */ + function getMIMEType($file) + { + $extension = substr(strrchr($file, '.'), 1); + if ($extension === false) { + return PEAR::raiseError("File has no extension."); + } + + if (!isset($this->extensionToType[$extension])) { + return PEAR::raiseError("Sorry, couldn't determine file type."); + } + + return $this->extensionToType[$extension]; + } + + + + /** + * Return default MIME-type for the specified extension. + * + * @param string $type MIME-type + * + * @return string A file extension without leading period. + */ + function getExtension($type) + { + include_once 'MIME/Type.php'; + // Strip parameters and comments. + $type = MIME_Type::getMedia($type) . '/' . MIME_Type::getSubType($type); + + $extension = array_search($type, $this->extensionToType); + if ($extension === false) { + return PEAR::raiseError("Sorry, couldn't determine extension."); + } + return $extension; + } + +} + +?> \ No newline at end of file diff --git a/includes/pear/MIME/Type/Parameter.php b/includes/pear/MIME/Type/Parameter.php new file mode 100644 index 0000000..759fd58 --- /dev/null +++ b/includes/pear/MIME/Type/Parameter.php @@ -0,0 +1,170 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @link http://pear.php.net/package/MIME_Type + */ + +/** + * Class for working with MIME type parameters + * + * @category File + * @package MIME_Type + * @author Ian Eure + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version Release: @version@ + * @link http://pear.php.net/package/MIME_Type + */ +class MIME_Type_Parameter +{ + /** + * Parameter name + * + * @var string + */ + var $name; + + /** + * Parameter value + * + * @var string + */ + var $value; + + /** + * Parameter comment + * + * @var string + */ + var $comment; + + + /** + * Constructor. + * + * @param string $param MIME parameter to parse, if set. + * + * @return void + */ + function MIME_Type_Parameter($param = false) + { + if ($param) { + $this->parse($param); + } + } + + + /** + * Parse a MIME type parameter and set object fields + * + * @param string $param MIME type parameter to parse + * + * @return void + */ + function parse($param) + { + $comment = ''; + $param = MIME_Type::stripComments($param, $comment); + $this->name = $this->getAttribute($param); + $this->value = $this->getValue($param); + $this->comment = $comment; + } + + + /** + * Get a parameter attribute (e.g. name) + * + * @param string $param MIME type parameter + * + * @return string Attribute name + * @static + */ + function getAttribute($param) + { + $tmp = explode('=', $param); + return trim($tmp[0]); + } + + + /** + * Get a parameter value + * + * @param string $param MIME type parameter + * + * @return string Value + * @static + */ + function getValue($param) + { + $tmp = explode('=', $param, 2); + $value = $tmp[1]; + $value = trim($value); + if ($value[0] == '"' && $value[strlen($value)-1] == '"') { + $value = substr($value, 1, -1); + } + $value = str_replace('\\"', '"', $value); + return $value; + } + + + /** + * Get a parameter comment + * + * @param string $param MIME type parameter + * + * @return string Parameter comment + * @see hasComment() + * @static + */ + function getComment($param) + { + $cs = strpos($param, '('); + if ($cs === false) { + return null; + } + $comment = substr($param, $cs); + return trim($comment, '() '); + } + + + /** + * Does this parameter have a comment? + * + * @param string $param MIME type parameter + * + * @return boolean true if $param has a comment, false otherwise + * @static + */ + function hasComment($param) + { + if (strstr($param, '(')) { + return true; + } + return false; + } + + + /** + * Get a string representation of this parameter + * + * This function performs the oppsite of parse() + * + * @return string String representation of parameter + */ + function get() + { + $val = $this->name . '="' . str_replace('"', '\\"', $this->value) . '"'; + if ($this->comment) { + $val .= ' (' . $this->comment . ')'; + } + return $val; + } +} +?> \ No newline at end of file diff --git a/includes/pear/OS/Guess.php b/includes/pear/OS/Guess.php new file mode 100644 index 0000000..6f41f32 --- /dev/null +++ b/includes/pear/OS/Guess.php @@ -0,0 +1,338 @@ + + * @author Gregory Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since PEAR 0.1 + */ + +// {{{ uname examples + +// php_uname() without args returns the same as 'uname -a', or a PHP-custom +// string for Windows. +// PHP versions prior to 4.3 return the uname of the host where PHP was built, +// as of 4.3 it returns the uname of the host running the PHP code. +// +// PC RedHat Linux 7.1: +// Linux host.example.com 2.4.2-2 #1 Sun Apr 8 20:41:30 EDT 2001 i686 unknown +// +// PC Debian Potato: +// Linux host 2.4.17 #2 SMP Tue Feb 12 15:10:04 CET 2002 i686 unknown +// +// PC FreeBSD 3.3: +// FreeBSD host.example.com 3.3-STABLE FreeBSD 3.3-STABLE #0: Mon Feb 21 00:42:31 CET 2000 root@example.com:/usr/src/sys/compile/CONFIG i386 +// +// PC FreeBSD 4.3: +// FreeBSD host.example.com 4.3-RELEASE FreeBSD 4.3-RELEASE #1: Mon Jun 25 11:19:43 EDT 2001 root@example.com:/usr/src/sys/compile/CONFIG i386 +// +// PC FreeBSD 4.5: +// FreeBSD host.example.com 4.5-STABLE FreeBSD 4.5-STABLE #0: Wed Feb 6 23:59:23 CET 2002 root@example.com:/usr/src/sys/compile/CONFIG i386 +// +// PC FreeBSD 4.5 w/uname from GNU shellutils: +// FreeBSD host.example.com 4.5-STABLE FreeBSD 4.5-STABLE #0: Wed Feb i386 unknown +// +// HP 9000/712 HP-UX 10: +// HP-UX iq B.10.10 A 9000/712 2008429113 two-user license +// +// HP 9000/712 HP-UX 10 w/uname from GNU shellutils: +// HP-UX host B.10.10 A 9000/712 unknown +// +// IBM RS6000/550 AIX 4.3: +// AIX host 3 4 000003531C00 +// +// AIX 4.3 w/uname from GNU shellutils: +// AIX host 3 4 000003531C00 unknown +// +// SGI Onyx IRIX 6.5 w/uname from GNU shellutils: +// IRIX64 host 6.5 01091820 IP19 mips +// +// SGI Onyx IRIX 6.5: +// IRIX64 host 6.5 01091820 IP19 +// +// SparcStation 20 Solaris 8 w/uname from GNU shellutils: +// SunOS host.example.com 5.8 Generic_108528-12 sun4m sparc +// +// SparcStation 20 Solaris 8: +// SunOS host.example.com 5.8 Generic_108528-12 sun4m sparc SUNW,SPARCstation-20 +// +// Mac OS X (Darwin) +// Darwin home-eden.local 7.5.0 Darwin Kernel Version 7.5.0: Thu Aug 5 19:26:16 PDT 2004; root:xnu/xnu-517.7.21.obj~3/RELEASE_PPC Power Macintosh +// +// Mac OS X early versions +// + +// }}} + +/* TODO: + * - define endianness, to allow matchSignature("bigend") etc. + */ + +/** + * Retrieves information about the current operating system + * + * This class uses php_uname() to grok information about the current OS + * + * @category pear + * @package PEAR + * @author Stig Bakken + * @author Gregory Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 0.1 + */ +class OS_Guess +{ + var $sysname; + var $nodename; + var $cpu; + var $release; + var $extra; + + function OS_Guess($uname = null) + { + list($this->sysname, + $this->release, + $this->cpu, + $this->extra, + $this->nodename) = $this->parseSignature($uname); + } + + function parseSignature($uname = null) + { + static $sysmap = array( + 'HP-UX' => 'hpux', + 'IRIX64' => 'irix', + ); + static $cpumap = array( + 'i586' => 'i386', + 'i686' => 'i386', + 'ppc' => 'powerpc', + ); + if ($uname === null) { + $uname = php_uname(); + } + $parts = preg_split('/\s+/', trim($uname)); + $n = count($parts); + + $release = $machine = $cpu = ''; + $sysname = $parts[0]; + $nodename = $parts[1]; + $cpu = $parts[$n-1]; + $extra = ''; + if ($cpu == 'unknown') { + $cpu = $parts[$n - 2]; + } + + switch ($sysname) { + case 'AIX' : + $release = "$parts[3].$parts[2]"; + break; + case 'Windows' : + switch ($parts[1]) { + case '95/98': + $release = '9x'; + break; + default: + $release = $parts[1]; + break; + } + $cpu = 'i386'; + break; + case 'Linux' : + $extra = $this->_detectGlibcVersion(); + // use only the first two digits from the kernel version + $release = preg_replace('/^([0-9]+\.[0-9]+).*/', '\1', $parts[2]); + break; + case 'Mac' : + $sysname = 'darwin'; + $nodename = $parts[2]; + $release = $parts[3]; + if ($cpu == 'Macintosh') { + if ($parts[$n - 2] == 'Power') { + $cpu = 'powerpc'; + } + } + break; + case 'Darwin' : + if ($cpu == 'Macintosh') { + if ($parts[$n - 2] == 'Power') { + $cpu = 'powerpc'; + } + } + $release = preg_replace('/^([0-9]+\.[0-9]+).*/', '\1', $parts[2]); + break; + default: + $release = preg_replace('/-.*/', '', $parts[2]); + break; + } + + if (isset($sysmap[$sysname])) { + $sysname = $sysmap[$sysname]; + } else { + $sysname = strtolower($sysname); + } + if (isset($cpumap[$cpu])) { + $cpu = $cpumap[$cpu]; + } + return array($sysname, $release, $cpu, $extra, $nodename); + } + + function _detectGlibcVersion() + { + static $glibc = false; + if ($glibc !== false) { + return $glibc; // no need to run this multiple times + } + $major = $minor = 0; + include_once "System.php"; + // Use glibc's header file to + // get major and minor version number: + if (@file_exists('/usr/include/features.h') && + @is_readable('/usr/include/features.h')) { + if (!@file_exists('/usr/bin/cpp') || !@is_executable('/usr/bin/cpp')) { + $features_file = fopen('/usr/include/features.h', 'rb'); + while (!feof($features_file)) { + $line = fgets($features_file, 8192); + if (!$line || (strpos($line, '#define') === false)) { + continue; + } + if (strpos($line, '__GLIBC__')) { + // major version number #define __GLIBC__ version + $line = preg_split('/\s+/', $line); + $glibc_major = trim($line[2]); + if (isset($glibc_minor)) { + break; + } + continue; + } + + if (strpos($line, '__GLIBC_MINOR__')) { + // got the minor version number + // #define __GLIBC_MINOR__ version + $line = preg_split('/\s+/', $line); + $glibc_minor = trim($line[2]); + if (isset($glibc_major)) { + break; + } + continue; + } + } + fclose($features_file); + if (!isset($glibc_major) || !isset($glibc_minor)) { + return $glibc = ''; + } + return $glibc = 'glibc' . trim($glibc_major) . "." . trim($glibc_minor) ; + } // no cpp + + $tmpfile = System::mktemp("glibctest"); + $fp = fopen($tmpfile, "w"); + fwrite($fp, "#include \n__GLIBC__ __GLIBC_MINOR__\n"); + fclose($fp); + $cpp = popen("/usr/bin/cpp $tmpfile", "r"); + while ($line = fgets($cpp, 1024)) { + if ($line{0} == '#' || trim($line) == '') { + continue; + } + + if (list($major, $minor) = explode(' ', trim($line))) { + break; + } + } + pclose($cpp); + unlink($tmpfile); + } // features.h + + if (!($major && $minor) && @is_link('/lib/libc.so.6')) { + // Let's try reading the libc.so.6 symlink + if (preg_match('/^libc-(.*)\.so$/', basename(readlink('/lib/libc.so.6')), $matches)) { + list($major, $minor) = explode('.', $matches[1]); + } + } + + if (!($major && $minor)) { + return $glibc = ''; + } + + return $glibc = "glibc{$major}.{$minor}"; + } + + function getSignature() + { + if (empty($this->extra)) { + return "{$this->sysname}-{$this->release}-{$this->cpu}"; + } + return "{$this->sysname}-{$this->release}-{$this->cpu}-{$this->extra}"; + } + + function getSysname() + { + return $this->sysname; + } + + function getNodename() + { + return $this->nodename; + } + + function getCpu() + { + return $this->cpu; + } + + function getRelease() + { + return $this->release; + } + + function getExtra() + { + return $this->extra; + } + + function matchSignature($match) + { + $fragments = is_array($match) ? $match : explode('-', $match); + $n = count($fragments); + $matches = 0; + if ($n > 0) { + $matches += $this->_matchFragment($fragments[0], $this->sysname); + } + if ($n > 1) { + $matches += $this->_matchFragment($fragments[1], $this->release); + } + if ($n > 2) { + $matches += $this->_matchFragment($fragments[2], $this->cpu); + } + if ($n > 3) { + $matches += $this->_matchFragment($fragments[3], $this->extra); + } + return ($matches == $n); + } + + function _matchFragment($fragment, $value) + { + if (strcspn($fragment, '*?') < strlen($fragment)) { + $reg = '/^' . str_replace(array('*', '?', '/'), array('.*', '.', '\\/'), $fragment) . '\\z/'; + return preg_match($reg, $value); + } + return ($fragment == '*' || !strcasecmp($fragment, $value)); + } + +} +/* + * Local Variables: + * indent-tabs-mode: nil + * c-basic-offset: 4 + * End: + */ \ No newline at end of file diff --git a/includes/pear/PEAR.php b/includes/pear/PEAR.php new file mode 100644 index 0000000..48830fd --- /dev/null +++ b/includes/pear/PEAR.php @@ -0,0 +1,1040 @@ + + * @author Stig Bakken + * @author Tomas V.V.Cox + * @author Greg Beaver + * @copyright 1997-2010 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 0.1 + */ + +/**#@+ + * ERROR constants + */ +define('PEAR_ERROR_RETURN', 1); +define('PEAR_ERROR_PRINT', 2); +define('PEAR_ERROR_TRIGGER', 4); +define('PEAR_ERROR_DIE', 8); +define('PEAR_ERROR_CALLBACK', 16); +/** + * WARNING: obsolete + * @deprecated + */ +define('PEAR_ERROR_EXCEPTION', 32); +/**#@-*/ +define('PEAR_ZE2', (function_exists('version_compare') && + version_compare(zend_version(), "2-dev", "ge"))); + +if (substr(PHP_OS, 0, 3) == 'WIN') { + define('OS_WINDOWS', true); + define('OS_UNIX', false); + define('PEAR_OS', 'Windows'); +} else { + define('OS_WINDOWS', false); + define('OS_UNIX', true); + define('PEAR_OS', 'Unix'); // blatant assumption +} + +$GLOBALS['_PEAR_default_error_mode'] = PEAR_ERROR_RETURN; +$GLOBALS['_PEAR_default_error_options'] = E_USER_NOTICE; +$GLOBALS['_PEAR_destructor_object_list'] = array(); +$GLOBALS['_PEAR_shutdown_funcs'] = array(); +$GLOBALS['_PEAR_error_handler_stack'] = array(); + +@ini_set('track_errors', true); + +/** + * Base class for other PEAR classes. Provides rudimentary + * emulation of destructors. + * + * If you want a destructor in your class, inherit PEAR and make a + * destructor method called _yourclassname (same name as the + * constructor, but with a "_" prefix). Also, in your constructor you + * have to call the PEAR constructor: $this->PEAR();. + * The destructor method will be called without parameters. Note that + * at in some SAPI implementations (such as Apache), any output during + * the request shutdown (in which destructors are called) seems to be + * discarded. If you need to get any debug information from your + * destructor, use error_log(), syslog() or something similar. + * + * IMPORTANT! To use the emulated destructors you need to create the + * objects by reference: $obj =& new PEAR_child; + * + * @category pear + * @package PEAR + * @author Stig Bakken + * @author Tomas V.V. Cox + * @author Greg Beaver + * @copyright 1997-2006 The PHP Group + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @see PEAR_Error + * @since Class available since PHP 4.0.2 + * @link http://pear.php.net/manual/en/core.pear.php#core.pear.pear + */ +class PEAR +{ + /** + * Whether to enable internal debug messages. + * + * @var bool + */ + private $_debug = false; + + /** + * Default error mode for this object. + * + * @var int + */ + private $_default_error_mode = null; + + /** + * Default error options used for this object when error mode + * is PEAR_ERROR_TRIGGER. + * + * @var int + */ + private $_default_error_options = null; + + /** + * Default error handler (callback) for this object, if error mode is + * PEAR_ERROR_CALLBACK. + * + * @var string + */ + private $_default_error_handler = ''; + + /** + * Which class to use for error objects. + * + * @var string + */ + private $_error_class = 'PEAR_Error'; + + /** + * An array of expected errors. + * + * @var array + */ + private $_expected_errors = array(); + + /** + * Constructor. Registers this object in + * $_PEAR_destructor_object_list for destructor emulation if a + * destructor object exists. + * + * @param string $error_class (optional) which class to use for + * error objects, defaults to PEAR_Error. + * @return void + */ + public function PEAR($error_class = null) + { + $classname = strtolower(get_class($this)); + if ($this->_debug) { + print "PEAR constructor called, class=$classname\n"; + } + + if ($error_class !== null) { + $this->_error_class = $error_class; + } + + while ($classname && strcasecmp($classname, "pear")) { + $destructor = "_$classname"; + if (method_exists($this, $destructor)) { + global $_PEAR_destructor_object_list; + $_PEAR_destructor_object_list[] = &$this; + if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) { + register_shutdown_function("_PEAR_call_destructors"); + $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true; + } + break; + } else { + $classname = get_parent_class($classname); + } + } + } + + /** + * Destructor (the emulated type of...). Does nothing right now, + * but is included for forward compatibility, so subclass + * destructors should always call it. + * + * See the note in the class desciption about output from + * destructors. + * + * @return void + */ + public function _PEAR() { + if ($this->_debug) { + printf("PEAR destructor called, class=%s\n", strtolower(get_class($this))); + } + } + + /** + * If you have a class that's mostly/entirely static, and you need static + * properties, you can use this method to simulate them. Eg. in your method(s) + * do this: $myVar = &PEAR::getStaticProperty('myclass', 'myVar'); + * You MUST use a reference, or they will not persist! + * + * @access public + * @param string $class The calling classname, to prevent clashes + * @param string $var The variable to retrieve. + * @return mixed A reference to the variable. If not set it will be + * auto initialised to NULL. + */ + public static function &getStaticProperty($class, $var) + { + static $properties; + if (!isset($properties[$class])) { + $properties[$class] = array(); + } + + if (!array_key_exists($var, $properties[$class])) { + $properties[$class][$var] = null; + } + + return $properties[$class][$var]; + } + + /** + * Use this function to register a shutdown method for static + * classes. + * + * @access public + * @param mixed $func The function name (or array of class/method) to call + * @param mixed $args The arguments to pass to the function + * @return void + */ + public static function registerShutdownFunc($func, $args = array()) + { + // if we are called statically, there is a potential + // that no shutdown func is registered. Bug #6445 + if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) { + register_shutdown_function("_PEAR_call_destructors"); + $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true; + } + $GLOBALS['_PEAR_shutdown_funcs'][] = array($func, $args); + } + + /** + * Tell whether a value is a PEAR error. + * + * @param mixed $data the value to test + * @param int $code if $data is an error object, return true + * only if $code is a string and + * $obj->getMessage() == $code or + * $code is an integer and $obj->getCode() == $code + * @return bool true if parameter is an error + */ + public static function isError($data, $code = null) + { + if (!is_object($data)) { + return false; + } + if (!is_a($data, 'PEAR_Error')) { + return false; + } + + if (is_null($code)) { + return true; + } elseif (is_string($code)) { + return $data->getMessage() == $code; + } + + return $data->getCode() == $code; + } + + /** + * Sets how errors generated by this object should be handled. + * Can be invoked both in objects and statically. If called + * statically, setErrorHandling sets the default behaviour for all + * PEAR objects. If called in an object, setErrorHandling sets + * the default behaviour for that object. + * + * @param int $mode + * One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT, + * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE, + * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION. + * + * @param mixed $options + * When $mode is PEAR_ERROR_TRIGGER, this is the error level (one + * of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR). + * + * When $mode is PEAR_ERROR_CALLBACK, this parameter is expected + * to be the callback function or method. A callback + * function is a string with the name of the function, a + * callback method is an array of two elements: the element + * at index 0 is the object, and the element at index 1 is + * the name of the method to call in the object. + * + * When $mode is PEAR_ERROR_PRINT or PEAR_ERROR_DIE, this is + * a printf format string used when printing the error + * message. + * + * @return void + * @see PEAR_ERROR_RETURN + * @see PEAR_ERROR_PRINT + * @see PEAR_ERROR_TRIGGER + * @see PEAR_ERROR_DIE + * @see PEAR_ERROR_CALLBACK + * @see PEAR_ERROR_EXCEPTION + * + * @since PHP 4.0.5 + */ + public function setErrorHandling($mode = null, $options = null) + { + if (isset($this) && is_a($this, 'PEAR')) { + $setmode = &$this->_default_error_mode; + $setoptions = &$this->_default_error_options; + } else { + $setmode = &$GLOBALS['_PEAR_default_error_mode']; + $setoptions = &$GLOBALS['_PEAR_default_error_options']; + } + + switch ($mode) { + case PEAR_ERROR_EXCEPTION: + case PEAR_ERROR_RETURN: + case PEAR_ERROR_PRINT: + case PEAR_ERROR_TRIGGER: + case PEAR_ERROR_DIE: + case null: + $setmode = $mode; + $setoptions = $options; + break; + + case PEAR_ERROR_CALLBACK: + $setmode = $mode; + // class/object method callback + if (is_callable($options)) { + $setoptions = $options; + } else { + trigger_error("invalid error callback", E_USER_WARNING); + } + break; + + default: + trigger_error("invalid error mode", E_USER_WARNING); + break; + } + } + + /** + * This method is used to tell which errors you expect to get. + * Expected errors are always returned with error mode + * PEAR_ERROR_RETURN. Expected error codes are stored in a stack, + * and this method pushes a new element onto it. The list of + * expected errors are in effect until they are popped off the + * stack with the popExpect() method. + * + * Note that this method can not be called statically + * + * @param mixed $code a single error code or an array of error codes to expect + * + * @return int the new depth of the "expected errors" stack + */ + public function expectError($code = '*') + { + if (is_array($code)) { + array_push($this->_expected_errors, $code); + } else { + array_push($this->_expected_errors, array($code)); + } + return count($this->_expected_errors); + } + + /** + * This method pops one element off the expected error codes + * stack. + * + * @return array the list of error codes that were popped + */ + public function popExpect() + { + return array_pop($this->_expected_errors); + } + + /** + * This method checks unsets an error code if available + * + * @param mixed error code + * @return bool true if the error code was unset, false otherwise + * @since PHP 4.3.0 + */ + private function _checkDelExpect($error_code) + { + $deleted = false; + foreach ($this->_expected_errors as $key => $error_array) { + if (in_array($error_code, $error_array)) { + unset($this->_expected_errors[$key][array_search($error_code, $error_array)]); + $deleted = true; + } + + // clean up empty arrays + if (0 == count($this->_expected_errors[$key])) { + unset($this->_expected_errors[$key]); + } + } + + return $deleted; + } + + /** + * This method deletes all occurences of the specified element from + * the expected error codes stack. + * + * @param mixed $error_code error code that should be deleted + * @return mixed list of error codes that were deleted or error + * @since PHP 4.3.0 + */ + public function delExpect($error_code) + { + $deleted = false; + if ((is_array($error_code) && (0 != count($error_code)))) { + // $error_code is a non-empty array here; we walk through it trying + // to unset all values + foreach ($error_code as $key => $error) { + $deleted = $this->_checkDelExpect($error) ? true : false; + } + + return $deleted ? true : PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME + } elseif (!empty($error_code)) { + // $error_code comes alone, trying to unset it + if ($this->_checkDelExpect($error_code)) { + return true; + } + + return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME + } + + // $error_code is empty + return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME + } + + /** + * This method is a wrapper that returns an instance of the + * configured error class with this object's default error + * handling applied. If the $mode and $options parameters are not + * specified, the object's defaults are used. + * + * @param mixed $message a text error message or a PEAR error object + * + * @param int $code a numeric error code (it is up to your class + * to define these if you want to use codes) + * + * @param int $mode One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT, + * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE, + * PEAR_ERROR_CALLBACK, PEAR_ERROR_EXCEPTION. + * + * @param mixed $options If $mode is PEAR_ERROR_TRIGGER, this parameter + * specifies the PHP-internal error level (one of + * E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR). + * If $mode is PEAR_ERROR_CALLBACK, this + * parameter specifies the callback function or + * method. In other error modes this parameter + * is ignored. + * + * @param string $userinfo If you need to pass along for example debug + * information, this parameter is meant for that. + * + * @param string $error_class The returned error object will be + * instantiated from this class, if specified. + * + * @param bool $skipmsg If true, raiseError will only pass error codes, + * the error message parameter will be dropped. + * + * @return object a PEAR error object + * @see PEAR::setErrorHandling + * @since PHP 4.0.5 + */ + public function &raiseError($message = null, + $code = null, + $mode = null, + $options = null, + $userinfo = null, + $error_class = null, + $skipmsg = false) + { + // The error is yet a PEAR error object + if (is_object($message)) { + $code = $message->getCode(); + $userinfo = $message->getUserInfo(); + $error_class = $message->getType(); + $message->error_message_prefix = ''; + $message = $message->getMessage(); + } + + if ( + isset($this) && + isset($this->_expected_errors) && + count($this->_expected_errors) > 0 && + count($exp = end($this->_expected_errors)) + ) { + if ($exp[0] == "*" || + (is_int(reset($exp)) && in_array($code, $exp)) || + (is_string(reset($exp)) && in_array($message, $exp)) + ) { + $mode = PEAR_ERROR_RETURN; + } + } + + // No mode given, try global ones + if ($mode === null) { + // Class error handler + if (isset($this) && isset($this->_default_error_mode)) { + $mode = $this->_default_error_mode; + $options = $this->_default_error_options; + // Global error handler + } elseif (isset($GLOBALS['_PEAR_default_error_mode'])) { + $mode = $GLOBALS['_PEAR_default_error_mode']; + $options = $GLOBALS['_PEAR_default_error_options']; + } + } + + if ($error_class !== null) { + $ec = $error_class; + } elseif (isset($this) && isset($this->_error_class)) { + $ec = $this->_error_class; + } else { + $ec = 'PEAR_Error'; + } + + if (intval(PHP_VERSION) < 5) { + // little non-eval hack to fix bug #12147 + include 'PEAR/FixPHP5PEARWarnings.php'; + return $a; + } + + if ($skipmsg) { + $a = new $ec($code, $mode, $options, $userinfo); + } else { + $a = new $ec($message, $code, $mode, $options, $userinfo); + } + + return $a; + } + + /** + * Simpler form of raiseError with fewer options. In most cases + * message, code and userinfo are enough. + * + * @param mixed $message a text error message or a PEAR error object + * + * @param int $code a numeric error code (it is up to your class + * to define these if you want to use codes) + * + * @param string $userinfo If you need to pass along for example debug + * information, this parameter is meant for that. + * + * @return object a PEAR error object + * @see PEAR::raiseError + */ + public function &throwError($message = null, $code = null, $userinfo = null) + { + if (isset($this) && is_a($this, 'PEAR')) { + $a = &$this->raiseError($message, $code, null, null, $userinfo); + return $a; + } + + $a = &PEAR::raiseError($message, $code, null, null, $userinfo); + return $a; + } + + public static function staticPushErrorHandling($mode, $options = null) + { + $stack = &$GLOBALS['_PEAR_error_handler_stack']; + $def_mode = &$GLOBALS['_PEAR_default_error_mode']; + $def_options = &$GLOBALS['_PEAR_default_error_options']; + $stack[] = array($def_mode, $def_options); + switch ($mode) { + case PEAR_ERROR_EXCEPTION: + case PEAR_ERROR_RETURN: + case PEAR_ERROR_PRINT: + case PEAR_ERROR_TRIGGER: + case PEAR_ERROR_DIE: + case null: + $def_mode = $mode; + $def_options = $options; + break; + + case PEAR_ERROR_CALLBACK: + $def_mode = $mode; + // class/object method callback + if (is_callable($options)) { + $def_options = $options; + } else { + trigger_error("invalid error callback", E_USER_WARNING); + } + break; + + default: + trigger_error("invalid error mode", E_USER_WARNING); + break; + } + $stack[] = array($mode, $options); + return true; + } + + public static function staticPopErrorHandling() + { + $stack = &$GLOBALS['_PEAR_error_handler_stack']; + $setmode = &$GLOBALS['_PEAR_default_error_mode']; + $setoptions = &$GLOBALS['_PEAR_default_error_options']; + array_pop($stack); + list($mode, $options) = $stack[sizeof($stack) - 1]; + array_pop($stack); + switch ($mode) { + case PEAR_ERROR_EXCEPTION: + case PEAR_ERROR_RETURN: + case PEAR_ERROR_PRINT: + case PEAR_ERROR_TRIGGER: + case PEAR_ERROR_DIE: + case null: + $setmode = $mode; + $setoptions = $options; + break; + + case PEAR_ERROR_CALLBACK: + $setmode = $mode; + // class/object method callback + if (is_callable($options)) { + $setoptions = $options; + } else { + trigger_error("invalid error callback", E_USER_WARNING); + } + break; + + default: + trigger_error("invalid error mode", E_USER_WARNING); + break; + } + return true; + } + + /** + * Push a new error handler on top of the error handler options stack. With this + * you can easily override the actual error handler for some code and restore + * it later with popErrorHandling. + * + * @param mixed $mode (same as setErrorHandling) + * @param mixed $options (same as setErrorHandling) + * + * @return bool Always true + * + * @see PEAR::setErrorHandling + */ + public function pushErrorHandling($mode, $options = null) + { + $stack = &$GLOBALS['_PEAR_error_handler_stack']; + if (isset($this) && is_a($this, 'PEAR')) { + $def_mode = &$this->_default_error_mode; + $def_options = &$this->_default_error_options; + } else { + $def_mode = &$GLOBALS['_PEAR_default_error_mode']; + $def_options = &$GLOBALS['_PEAR_default_error_options']; + } + $stack[] = array($def_mode, $def_options); + + if (isset($this) && is_a($this, 'PEAR')) { + $this->setErrorHandling($mode, $options); + } else { + PEAR::setErrorHandling($mode, $options); + } + $stack[] = array($mode, $options); + return true; + } + + /** + * Pop the last error handler used + * + * @return bool Always true + * + * @see PEAR::pushErrorHandling + */ + public function popErrorHandling() + { + $stack = &$GLOBALS['_PEAR_error_handler_stack']; + array_pop($stack); + list($mode, $options) = $stack[sizeof($stack) - 1]; + array_pop($stack); + if (isset($this) && is_a($this, 'PEAR')) { + $this->setErrorHandling($mode, $options); + } else { + PEAR::setErrorHandling($mode, $options); + } + return true; + } + + /** + * OS independant PHP extension load. Remember to take care + * on the correct extension name for case sensitive OSes. + * + * @param string $ext The extension name + * @return bool Success or not on the dl() call + */ + public function loadExtension($ext) + { + if (extension_loaded($ext)) { + return true; + } + + // if either returns true dl() will produce a FATAL error, stop that + if ( + function_exists('dl') === false || + ini_get('enable_dl') != 1 || + ini_get('safe_mode') == 1 + ) { + return false; + } + + if (OS_WINDOWS) { + $suffix = '.dll'; + } elseif (PHP_OS == 'HP-UX') { + $suffix = '.sl'; + } elseif (PHP_OS == 'AIX') { + $suffix = '.a'; + } elseif (PHP_OS == 'OSX') { + $suffix = '.bundle'; + } else { + $suffix = '.so'; + } + + return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix); + } +} + +if (PEAR_ZE2) { + include_once 'PEAR5.php'; +} + +function _PEAR_call_destructors() +{ + global $_PEAR_destructor_object_list; + if (is_array($_PEAR_destructor_object_list) && + sizeof($_PEAR_destructor_object_list)) + { + reset($_PEAR_destructor_object_list); + if (PEAR_ZE2) { + $destructLifoExists = PEAR5::getStaticProperty('PEAR', 'destructlifo'); + } else { + $destructLifoExists = PEAR::getStaticProperty('PEAR', 'destructlifo'); + } + + if ($destructLifoExists) { + $_PEAR_destructor_object_list = array_reverse($_PEAR_destructor_object_list); + } + + while (list($k, $objref) = each($_PEAR_destructor_object_list)) { + $classname = get_class($objref); + while ($classname) { + $destructor = "_$classname"; + if (method_exists($objref, $destructor)) { + $objref->$destructor(); + break; + } else { + $classname = get_parent_class($classname); + } + } + } + // Empty the object list to ensure that destructors are + // not called more than once. + $_PEAR_destructor_object_list = array(); + } + + // Now call the shutdown functions + if ( + isset($GLOBALS['_PEAR_shutdown_funcs']) && + is_array($GLOBALS['_PEAR_shutdown_funcs']) && + !empty($GLOBALS['_PEAR_shutdown_funcs']) + ) { + foreach ($GLOBALS['_PEAR_shutdown_funcs'] as $value) { + call_user_func_array($value[0], $value[1]); + } + } +} + +/** + * Standard PEAR error class for PHP 4 + * + * This class is supserseded by {@link PEAR_Exception} in PHP 5 + * + * @category pear + * @package PEAR + * @author Stig Bakken + * @author Tomas V.V. Cox + * @author Gregory Beaver + * @copyright 1997-2006 The PHP Group + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/manual/en/core.pear.pear-error.php + * @see PEAR::raiseError(), PEAR::throwError() + * @since Class available since PHP 4.0.2 + */ +class PEAR_Error +{ + var $error_message_prefix = ''; + var $mode = PEAR_ERROR_RETURN; + var $level = E_USER_NOTICE; + var $code = -1; + var $message = ''; + var $userinfo = ''; + var $backtrace = null; + + /** + * PEAR_Error constructor + * + * @param string $message message + * + * @param int $code (optional) error code + * + * @param int $mode (optional) error mode, one of: PEAR_ERROR_RETURN, + * PEAR_ERROR_PRINT, PEAR_ERROR_DIE, PEAR_ERROR_TRIGGER, + * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION + * + * @param mixed $options (optional) error level, _OR_ in the case of + * PEAR_ERROR_CALLBACK, the callback function or object/method + * tuple. + * + * @param string $userinfo (optional) additional user/debug info + * + */ + public function PEAR_Error($message = 'unknown error', $code = null, + $mode = null, $options = null, $userinfo = null) + { + if ($mode === null) { + $mode = PEAR_ERROR_RETURN; + } + $this->message = $message; + $this->code = $code; + $this->mode = $mode; + $this->userinfo = $userinfo; + + if (PEAR_ZE2) { + $skiptrace = PEAR5::getStaticProperty('PEAR_Error', 'skiptrace'); + } else { + $skiptrace = PEAR::getStaticProperty('PEAR_Error', 'skiptrace'); + } + + if (!$skiptrace) { + $this->backtrace = debug_backtrace(); + if (isset($this->backtrace[0]) && isset($this->backtrace[0]['object'])) { + unset($this->backtrace[0]['object']); + } + } + + if ($mode & PEAR_ERROR_CALLBACK) { + $this->level = E_USER_NOTICE; + $this->callback = $options; + } else { + if ($options === null) { + $options = E_USER_NOTICE; + } + + $this->level = $options; + $this->callback = null; + } + + if ($this->mode & PEAR_ERROR_PRINT) { + if (is_null($options) || is_int($options)) { + $format = "%s"; + } else { + $format = $options; + } + + printf($format, $this->getMessage()); + } + + if ($this->mode & PEAR_ERROR_TRIGGER) { + trigger_error($this->getMessage(), $this->level); + } + + if ($this->mode & PEAR_ERROR_DIE) { + $msg = $this->getMessage(); + if (is_null($options) || is_int($options)) { + $format = "%s"; + if (substr($msg, -1) != "\n") { + $msg .= "\n"; + } + } else { + $format = $options; + } + die(sprintf($format, $msg)); + } + + if ($this->mode & PEAR_ERROR_CALLBACK && is_callable($this->callback)) { + call_user_func($this->callback, $this); + } + + if ($this->mode & PEAR_ERROR_EXCEPTION) { + trigger_error("PEAR_ERROR_EXCEPTION is obsolete, use class PEAR_Exception for exceptions", E_USER_WARNING); + eval('$e = new Exception($this->message, $this->code);throw($e);'); + } + } + + /** + * Get the error mode from an error object. + * + * @return int error mode + */ + public function getMode() + { + return $this->mode; + } + + /** + * Get the callback function/method from an error object. + * + * @return mixed callback function or object/method array + */ + public function getCallback() + { + return $this->callback; + } + + /** + * Get the error message from an error object. + * + * @return string full error message + */ + public function getMessage() + { + return ($this->error_message_prefix . $this->message); + } + + /** + * Get error code from an error object + * + * @return int error code + */ + public function getCode() + { + return $this->code; + } + + /** + * Get the name of this error/exception. + * + * @return string error/exception name (type) + */ + public function getType() + { + return get_class($this); + } + + /** + * Get additional user-supplied information. + * + * @return string user-supplied information + */ + public function getUserInfo() + { + return $this->userinfo; + } + + /** + * Get additional debug information supplied by the application. + * + * @return string debug information + */ + public function getDebugInfo() + { + return $this->getUserInfo(); + } + + /** + * Get the call backtrace from where the error was generated. + * Supported with PHP 4.3.0 or newer. + * + * @param int $frame (optional) what frame to fetch + * @return array Backtrace, or NULL if not available. + */ + public function getBacktrace($frame = null) + { + if (defined('PEAR_IGNORE_BACKTRACE')) { + return null; + } + if ($frame === null) { + return $this->backtrace; + } + return $this->backtrace[$frame]; + } + + public function addUserInfo($info) + { + if (empty($this->userinfo)) { + $this->userinfo = $info; + } else { + $this->userinfo .= " ** $info"; + } + } + + public function __toString() + { + return $this->getMessage(); + } + + /** + * Make a string representation of this object. + * + * @return string a string with an object summary + */ + public function toString() + { + $modes = array(); + $levels = array(E_USER_NOTICE => 'notice', + E_USER_WARNING => 'warning', + E_USER_ERROR => 'error'); + if ($this->mode & PEAR_ERROR_CALLBACK) { + if (is_array($this->callback)) { + $callback = (is_object($this->callback[0]) ? + strtolower(get_class($this->callback[0])) : + $this->callback[0]) . '::' . + $this->callback[1]; + } else { + $callback = $this->callback; + } + return sprintf('[%s: message="%s" code=%d mode=callback '. + 'callback=%s prefix="%s" info="%s"]', + strtolower(get_class($this)), $this->message, $this->code, + $callback, $this->error_message_prefix, + $this->userinfo); + } + if ($this->mode & PEAR_ERROR_PRINT) { + $modes[] = 'print'; + } + if ($this->mode & PEAR_ERROR_TRIGGER) { + $modes[] = 'trigger'; + } + if ($this->mode & PEAR_ERROR_DIE) { + $modes[] = 'die'; + } + if ($this->mode & PEAR_ERROR_RETURN) { + $modes[] = 'return'; + } + return sprintf('[%s: message="%s" code=%d mode=%s level=%s '. + 'prefix="%s" info="%s"]', + strtolower(get_class($this)), $this->message, $this->code, + implode("|", $modes), $levels[$this->level], + $this->error_message_prefix, + $this->userinfo); + } +} + +/* + * Local Variables: + * mode: php + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/includes/pear/PEAR/Autoloader.php b/includes/pear/PEAR/Autoloader.php new file mode 100644 index 0000000..b833dc6 --- /dev/null +++ b/includes/pear/PEAR/Autoloader.php @@ -0,0 +1,218 @@ + + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/manual/en/core.ppm.php#core.ppm.pear-autoloader + * @since File available since Release 0.1 + * @deprecated File deprecated in Release 1.4.0a1 + */ + +// /* vim: set expandtab tabstop=4 shiftwidth=4: */ + +if (!extension_loaded("overload")) { + // die hard without ext/overload + die("Rebuild PHP with the `overload' extension to use PEAR_Autoloader"); +} + +/** + * Include for PEAR_Error and PEAR classes + */ +require_once "PEAR.php"; + +/** + * This class is for objects where you want to separate the code for + * some methods into separate classes. This is useful if you have a + * class with not-frequently-used methods that contain lots of code + * that you would like to avoid always parsing. + * + * The PEAR_Autoloader class provides autoloading and aggregation. + * The autoloading lets you set up in which classes the separated + * methods are found. Aggregation is the technique used to import new + * methods, an instance of each class providing separated methods is + * stored and called every time the aggregated method is called. + * + * @category pear + * @package PEAR + * @author Stig Bakken + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/manual/en/core.ppm.php#core.ppm.pear-autoloader + * @since File available since Release 0.1 + * @deprecated File deprecated in Release 1.4.0a1 + */ +class PEAR_Autoloader extends PEAR +{ + // {{{ properties + + /** + * Map of methods and classes where they are defined + * + * @var array + * + * @access private + */ + var $_autoload_map = array(); + + /** + * Map of methods and aggregate objects + * + * @var array + * + * @access private + */ + var $_method_map = array(); + + // }}} + // {{{ addAutoload() + + /** + * Add one or more autoload entries. + * + * @param string $method which method to autoload + * + * @param string $classname (optional) which class to find the method in. + * If the $method parameter is an array, this + * parameter may be omitted (and will be ignored + * if not), and the $method parameter will be + * treated as an associative array with method + * names as keys and class names as values. + * + * @return void + * + * @access public + */ + function addAutoload($method, $classname = null) + { + if (is_array($method)) { + array_walk($method, create_function('$a,&$b', '$b = strtolower($b);')); + $this->_autoload_map = array_merge($this->_autoload_map, $method); + } else { + $this->_autoload_map[strtolower($method)] = $classname; + } + } + + // }}} + // {{{ removeAutoload() + + /** + * Remove an autoload entry. + * + * @param string $method which method to remove the autoload entry for + * + * @return bool TRUE if an entry was removed, FALSE if not + * + * @access public + */ + function removeAutoload($method) + { + $method = strtolower($method); + $ok = isset($this->_autoload_map[$method]); + unset($this->_autoload_map[$method]); + return $ok; + } + + // }}} + // {{{ addAggregateObject() + + /** + * Add an aggregate object to this object. If the specified class + * is not defined, loading it will be attempted following PEAR's + * file naming scheme. All the methods in the class will be + * aggregated, except private ones (name starting with an + * underscore) and constructors. + * + * @param string $classname what class to instantiate for the object. + * + * @return void + * + * @access public + */ + function addAggregateObject($classname) + { + $classname = strtolower($classname); + if (!class_exists($classname)) { + $include_file = preg_replace('/[^a-z0-9]/i', '_', $classname); + include_once $include_file; + } + $obj =& new $classname; + $methods = get_class_methods($classname); + foreach ($methods as $method) { + // don't import priviate methods and constructors + if ($method{0} != '_' && $method != $classname) { + $this->_method_map[$method] = $obj; + } + } + } + + // }}} + // {{{ removeAggregateObject() + + /** + * Remove an aggregate object. + * + * @param string $classname the class of the object to remove + * + * @return bool TRUE if an object was removed, FALSE if not + * + * @access public + */ + function removeAggregateObject($classname) + { + $ok = false; + $classname = strtolower($classname); + reset($this->_method_map); + while (list($method, $obj) = each($this->_method_map)) { + if (is_a($obj, $classname)) { + unset($this->_method_map[$method]); + $ok = true; + } + } + return $ok; + } + + // }}} + // {{{ __call() + + /** + * Overloaded object call handler, called each time an + * undefined/aggregated method is invoked. This method repeats + * the call in the right aggregate object and passes on the return + * value. + * + * @param string $method which method that was called + * + * @param string $args An array of the parameters passed in the + * original call + * + * @return mixed The return value from the aggregated method, or a PEAR + * error if the called method was unknown. + */ + function __call($method, $args, &$retval) + { + $method = strtolower($method); + if (empty($this->_method_map[$method]) && isset($this->_autoload_map[$method])) { + $this->addAggregateObject($this->_autoload_map[$method]); + } + if (isset($this->_method_map[$method])) { + $retval = call_user_func_array(array($this->_method_map[$method], $method), $args); + return true; + } + return false; + } + + // }}} +} + +overload("PEAR_Autoloader"); + +?> diff --git a/includes/pear/PEAR/Builder.php b/includes/pear/PEAR/Builder.php new file mode 100644 index 0000000..b4fb430 --- /dev/null +++ b/includes/pear/PEAR/Builder.php @@ -0,0 +1,518 @@ + + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 0.1 + * + * TODO: log output parameters in PECL command line + * TODO: msdev path in configuration + */ + +/** + * Needed for extending PEAR_Builder + */ +require_once 'PEAR/Common.php'; +require_once 'PEAR/PackageFile.php'; + +/** + * Class to handle building (compiling) extensions. + * + * @category pear + * @package PEAR + * @author Stig Bakken + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @PEAR-VER@ + * @link http://pear.php.net/package/PEAR + * @since Class available since PHP 4.0.2 + * @see http://pear.php.net/manual/en/core.ppm.pear-builder.php + */ +class PEAR_Builder extends PEAR_Common +{ + var $php_api_version = 0; + var $zend_module_api_no = 0; + var $zend_extension_api_no = 0; + + var $extensions_built = array(); + + /** + * @var string Used for reporting when it is not possible to pass function + * via extra parameter, e.g. log, msdevCallback + */ + var $current_callback = null; + + // used for msdev builds + var $_lastline = null; + var $_firstline = null; + + /** + * PEAR_Builder constructor. + * + * @param object $ui user interface object (instance of PEAR_Frontend_*) + * + * @access public + */ + function PEAR_Builder(&$ui) + { + parent::PEAR_Common(); + $this->setFrontendObject($ui); + } + + /** + * Build an extension from source on windows. + * requires msdev + */ + function _build_win32($descfile, $callback = null) + { + if (is_object($descfile)) { + $pkg = $descfile; + $descfile = $pkg->getPackageFile(); + } else { + $pf = &new PEAR_PackageFile($this->config, $this->debug); + $pkg = &$pf->fromPackageFile($descfile, PEAR_VALIDATE_NORMAL); + if (PEAR::isError($pkg)) { + return $pkg; + } + } + $dir = dirname($descfile); + $old_cwd = getcwd(); + + if (!file_exists($dir) || !is_dir($dir) || !chdir($dir)) { + return $this->raiseError("could not chdir to $dir"); + } + + // packages that were in a .tar have the packagefile in this directory + $vdir = $pkg->getPackage() . '-' . $pkg->getVersion(); + if (file_exists($dir) && is_dir($vdir)) { + if (!chdir($vdir)) { + return $this->raiseError("could not chdir to " . realpath($vdir)); + } + + $dir = getcwd(); + } + + $this->log(2, "building in $dir"); + + $dsp = $pkg->getPackage().'.dsp'; + if (!file_exists("$dir/$dsp")) { + return $this->raiseError("The DSP $dsp does not exist."); + } + // XXX TODO: make release build type configurable + $command = 'msdev '.$dsp.' /MAKE "'.$pkg->getPackage(). ' - Release"'; + + $err = $this->_runCommand($command, array(&$this, 'msdevCallback')); + if (PEAR::isError($err)) { + return $err; + } + + // figure out the build platform and type + $platform = 'Win32'; + $buildtype = 'Release'; + if (preg_match('/.*?'.$pkg->getPackage().'\s-\s(\w+)\s(.*?)-+/i',$this->_firstline,$matches)) { + $platform = $matches[1]; + $buildtype = $matches[2]; + } + + if (preg_match('/(.*)?\s-\s(\d+).*?(\d+)/', $this->_lastline, $matches)) { + if ($matches[2]) { + // there were errors in the build + return $this->raiseError("There were errors during compilation."); + } + $out = $matches[1]; + } else { + return $this->raiseError("Did not understand the completion status returned from msdev.exe."); + } + + // msdev doesn't tell us the output directory :/ + // open the dsp, find /out and use that directory + $dsptext = join(file($dsp),''); + + // this regex depends on the build platform and type having been + // correctly identified above. + $regex ='/.*?!IF\s+"\$\(CFG\)"\s+==\s+("'. + $pkg->getPackage().'\s-\s'. + $platform.'\s'. + $buildtype.'").*?'. + '\/out:"(.*?)"/is'; + + if ($dsptext && preg_match($regex, $dsptext, $matches)) { + // what we get back is a relative path to the output file itself. + $outfile = realpath($matches[2]); + } else { + return $this->raiseError("Could not retrieve output information from $dsp."); + } + // realpath returns false if the file doesn't exist + if ($outfile && copy($outfile, "$dir/$out")) { + $outfile = "$dir/$out"; + } + + $built_files[] = array( + 'file' => "$outfile", + 'php_api' => $this->php_api_version, + 'zend_mod_api' => $this->zend_module_api_no, + 'zend_ext_api' => $this->zend_extension_api_no, + ); + + return $built_files; + } + // }}} + + // {{{ msdevCallback() + function msdevCallback($what, $data) + { + if (!$this->_firstline) + $this->_firstline = $data; + $this->_lastline = $data; + call_user_func($this->current_callback, $what, $data); + } + + /** + * @param string + * @param string + * @param array + * @access private + */ + function _harvestInstDir($dest_prefix, $dirname, &$built_files) + { + $d = opendir($dirname); + if (!$d) + return false; + + $ret = true; + while (($ent = readdir($d)) !== false) { + if ($ent{0} == '.') + continue; + + $full = $dirname . DIRECTORY_SEPARATOR . $ent; + if (is_dir($full)) { + if (!$this->_harvestInstDir( + $dest_prefix . DIRECTORY_SEPARATOR . $ent, + $full, $built_files)) { + $ret = false; + break; + } + } else { + $dest = $dest_prefix . DIRECTORY_SEPARATOR . $ent; + $built_files[] = array( + 'file' => $full, + 'dest' => $dest, + 'php_api' => $this->php_api_version, + 'zend_mod_api' => $this->zend_module_api_no, + 'zend_ext_api' => $this->zend_extension_api_no, + ); + } + } + closedir($d); + return $ret; + } + + /** + * Build an extension from source. Runs "phpize" in the source + * directory, but compiles in a temporary directory + * (TMPDIR/pear-build-USER/PACKAGE-VERSION). + * + * @param string|PEAR_PackageFile_v* $descfile path to XML package description file, or + * a PEAR_PackageFile object + * + * @param mixed $callback callback function used to report output, + * see PEAR_Builder::_runCommand for details + * + * @return array an array of associative arrays with built files, + * format: + * array( array( 'file' => '/path/to/ext.so', + * 'php_api' => YYYYMMDD, + * 'zend_mod_api' => YYYYMMDD, + * 'zend_ext_api' => YYYYMMDD ), + * ... ) + * + * @access public + * + * @see PEAR_Builder::_runCommand + */ + function build($descfile, $callback = null, $options = array()) + { + if (preg_match('/(\\/|\\\\|^)([^\\/\\\\]+)?php(.+)?$/', + $this->config->get('php_bin'), $matches)) { + if (isset($matches[2]) && strlen($matches[2]) && + trim($matches[2]) != trim($this->config->get('php_prefix'))) { + $this->log(0, 'WARNING: php_bin ' . $this->config->get('php_bin') . + ' appears to have a prefix ' . $matches[2] . ', but' . + ' config variable php_prefix does not match'); + } + + if (isset($matches[3]) && strlen($matches[3]) && + trim($matches[3]) != trim($this->config->get('php_suffix'))) { + $this->log(0, 'WARNING: php_bin ' . $this->config->get('php_bin') . + ' appears to have a suffix ' . $matches[3] . ', but' . + ' config variable php_suffix does not match'); + } + } + + $this->current_callback = $callback; + if (PEAR_OS == "Windows") { + return $this->_build_win32($descfile, $callback); + } + + if (PEAR_OS != 'Unix') { + return $this->raiseError("building extensions not supported on this platform"); + } + + if (is_object($descfile)) { + $pkg = $descfile; + $descfile = $pkg->getPackageFile(); + if (is_a($pkg, 'PEAR_PackageFile_v1')) { + $dir = dirname($descfile); + } else { + $dir = $pkg->_config->get('temp_dir') . '/' . $pkg->getName(); + // automatically delete at session end + $this->addTempFile($dir); + } + } else { + $pf = &new PEAR_PackageFile($this->config); + $pkg = &$pf->fromPackageFile($descfile, PEAR_VALIDATE_NORMAL); + if (PEAR::isError($pkg)) { + return $pkg; + } + $dir = dirname($descfile); + } + + // Find config. outside of normal path - e.g. config.m4 + foreach (array_keys($pkg->getInstallationFileList()) as $item) { + if (stristr(basename($item), 'config.m4') && dirname($item) != '.') { + $dir .= DIRECTORY_SEPARATOR . dirname($item); + break; + } + } + + $old_cwd = getcwd(); + if (!file_exists($dir) || !is_dir($dir) || !chdir($dir)) { + return $this->raiseError("could not chdir to $dir"); + } + + $vdir = $pkg->getPackage() . '-' . $pkg->getVersion(); + if (is_dir($vdir)) { + chdir($vdir); + } + + $dir = getcwd(); + $this->log(2, "building in $dir"); + putenv('PATH=' . $this->config->get('bin_dir') . ':' . getenv('PATH')); + $err = $this->_runCommand($this->config->get('php_prefix') + . "phpize" . + $this->config->get('php_suffix'), + array(&$this, 'phpizeCallback')); + if (PEAR::isError($err)) { + return $err; + } + + if (!$err) { + return $this->raiseError("`phpize' failed"); + } + + // Figure out what params have been passed in to us already - formatting fixing + $opts = array(); + if (!empty($options)) { + foreach ($options as $op) { + $op = str_replace('--', '', $op); + list($name, $value) = explode('=', $op); + $opts[] = $name; + } + } + + // {{{ start of interactive part + $configure_command = "$dir/configure"; + $configure_options = $pkg->getConfigureOptions(); + if ($configure_options) { + foreach ($configure_options as $o) { + // skip params that have been passed already + if (in_array($o['name'], $opts)) { + continue; + } + + $default = array_key_exists('default', $o) ? $o['default'] : null; + list($r) = $this->ui->userDialog('build', + array($o['prompt']), + array('text'), + array($default)); + if (substr($o['name'], 0, 5) == 'with-' && + ($r == 'yes' || $r == 'autodetect')) { + $configure_command .= " --$o[name]"; + } else { + $configure_command .= " --$o[name]=".trim($r); + } + } + } + // }}} end of interactive part + + // Set any options that were passed in. + if (!empty($options)) { + foreach ($options as $op) { + $configure_command .= ' ' . $op; + } + } + + // FIXME make configurable + if (!$user = getenv('USER')){ + $user = 'defaultuser'; + } + + $tmpdir = $this->config->get('temp_dir'); + $build_basedir = System::mktemp(' -t "' . $tmpdir . '" -d "pear-build-' . $user . '"'); + $build_dir = "$build_basedir/$vdir"; + $inst_dir = "$build_basedir/install-$vdir"; + $this->log(1, "building in $build_dir"); + if (is_dir($build_dir)) { + System::rm(array('-rf', $build_dir)); + } + + if (!System::mkDir(array('-p', $build_dir))) { + return $this->raiseError("could not create build dir: $build_dir"); + } + + $this->addTempFile($build_dir); + if (!System::mkDir(array('-p', $inst_dir))) { + return $this->raiseError("could not create temporary install dir: $inst_dir"); + } + $this->addTempFile($inst_dir); + + $make_command = getenv('MAKE') ? getenv('MAKE') : 'make'; + + $to_run = array( + $configure_command, + $make_command, + "$make_command INSTALL_ROOT=\"$inst_dir\" install", + "find \"$inst_dir\" | xargs ls -dils" + ); + if (!file_exists($build_dir) || !is_dir($build_dir) || !chdir($build_dir)) { + return $this->raiseError("could not chdir to $build_dir"); + } + + putenv('PHP_PEAR_VERSION=1.9.4'); + foreach ($to_run as $cmd) { + $err = $this->_runCommand($cmd, $callback); + if (PEAR::isError($err)) { + chdir($old_cwd); + return $err; + } + if (!$err) { + chdir($old_cwd); + return $this->raiseError("`$cmd' failed"); + } + } + if (!($dp = opendir("modules"))) { + chdir($old_cwd); + return $this->raiseError("no `modules' directory found"); + } + + $built_files = array(); + $prefix = exec($this->config->get('php_prefix') + . "php-config" . + $this->config->get('php_suffix') . " --prefix"); + $ext_dir = $this->config->get('ext_dir'); + if (!$ext_dir) { + $ext_dir = $prefix; + } + $this->_harvestInstDir($ext_dir, $inst_dir . DIRECTORY_SEPARATOR . $prefix, $built_files); + + chdir($old_cwd); + return $built_files; + } + + /** + * Message callback function used when running the "phpize" + * program. Extracts the API numbers used. Ignores other message + * types than "cmdoutput". + * + * @param string $what the type of message + * @param mixed $data the message + * + * @return void + * + * @access public + */ + function phpizeCallback($what, $data) + { + if ($what != 'cmdoutput') { + return; + } + $this->log(1, rtrim($data)); + if (preg_match('/You should update your .aclocal.m4/', $data)) { + return; + } + $matches = array(); + if (preg_match('/^\s+(\S[^:]+):\s+(\d{8})/', $data, $matches)) { + $member = preg_replace('/[^a-z]/', '_', strtolower($matches[1])); + $apino = (int)$matches[2]; + if (isset($this->$member)) { + $this->$member = $apino; + //$msg = sprintf("%-22s : %d", $matches[1], $apino); + //$this->log(1, $msg); + } + } + } + + /** + * Run an external command, using a message callback to report + * output. The command will be run through popen and output is + * reported for every line with a "cmdoutput" message with the + * line string, including newlines, as payload. + * + * @param string $command the command to run + * + * @param mixed $callback (optional) function to use as message + * callback + * + * @return bool whether the command was successful (exit code 0 + * means success, any other means failure) + * + * @access private + */ + function _runCommand($command, $callback = null) + { + $this->log(1, "running: $command"); + $pp = popen("$command 2>&1", "r"); + if (!$pp) { + return $this->raiseError("failed to run `$command'"); + } + if ($callback && $callback[0]->debug == 1) { + $olddbg = $callback[0]->debug; + $callback[0]->debug = 2; + } + + while ($line = fgets($pp, 1024)) { + if ($callback) { + call_user_func($callback, 'cmdoutput', $line); + } else { + $this->log(2, rtrim($line)); + } + } + if ($callback && isset($olddbg)) { + $callback[0]->debug = $olddbg; + } + + $exitcode = is_resource($pp) ? pclose($pp) : -1; + return ($exitcode == 0); + } + + function log($level, $msg) + { + if ($this->current_callback) { + if ($this->debug >= $level) { + call_user_func($this->current_callback, 'output', $msg); + } + return; + } + return PEAR_Common::log($level, $msg); + } +} \ No newline at end of file diff --git a/includes/pear/PEAR/ChannelFile.php b/includes/pear/PEAR/ChannelFile.php new file mode 100644 index 0000000..9b27f28 --- /dev/null +++ b/includes/pear/PEAR/ChannelFile.php @@ -0,0 +1,1559 @@ + + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a1 + */ + +/** + * Needed for error handling + */ +require_once 'PEAR/ErrorStack.php'; +require_once 'PEAR/XMLParser.php'; +require_once 'PEAR/Common.php'; + +/** + * Error code if the channel.xml tag does not contain a valid version + */ +define('PEAR_CHANNELFILE_ERROR_NO_VERSION', 1); +/** + * Error code if the channel.xml tag version is not supported (version 1.0 is the only supported version, + * currently + */ +define('PEAR_CHANNELFILE_ERROR_INVALID_VERSION', 2); + +/** + * Error code if parsing is attempted with no xml extension + */ +define('PEAR_CHANNELFILE_ERROR_NO_XML_EXT', 3); + +/** + * Error code if creating the xml parser resource fails + */ +define('PEAR_CHANNELFILE_ERROR_CANT_MAKE_PARSER', 4); + +/** + * Error code used for all sax xml parsing errors + */ +define('PEAR_CHANNELFILE_ERROR_PARSER_ERROR', 5); + +/**#@+ + * Validation errors + */ +/** + * Error code when channel name is missing + */ +define('PEAR_CHANNELFILE_ERROR_NO_NAME', 6); +/** + * Error code when channel name is invalid + */ +define('PEAR_CHANNELFILE_ERROR_INVALID_NAME', 7); +/** + * Error code when channel summary is missing + */ +define('PEAR_CHANNELFILE_ERROR_NO_SUMMARY', 8); +/** + * Error code when channel summary is multi-line + */ +define('PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY', 9); +/** + * Error code when channel server is missing for protocol + */ +define('PEAR_CHANNELFILE_ERROR_NO_HOST', 10); +/** + * Error code when channel server is invalid for protocol + */ +define('PEAR_CHANNELFILE_ERROR_INVALID_HOST', 11); +/** + * Error code when a mirror name is invalid + */ +define('PEAR_CHANNELFILE_ERROR_INVALID_MIRROR', 21); +/** + * Error code when a mirror type is invalid + */ +define('PEAR_CHANNELFILE_ERROR_INVALID_MIRRORTYPE', 22); +/** + * Error code when an attempt is made to generate xml, but the parsed content is invalid + */ +define('PEAR_CHANNELFILE_ERROR_INVALID', 23); +/** + * Error code when an empty package name validate regex is passed in + */ +define('PEAR_CHANNELFILE_ERROR_EMPTY_REGEX', 24); +/** + * Error code when a tag has no version + */ +define('PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION', 25); +/** + * Error code when a tag has no name + */ +define('PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME', 26); +/** + * Error code when a tag has no name + */ +define('PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME', 27); +/** + * Error code when a tag has no version attribute + */ +define('PEAR_CHANNELFILE_ERROR_NOVALIDATE_VERSION', 28); +/** + * Error code when a mirror does not exist but is called for in one of the set* + * methods. + */ +define('PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND', 32); +/** + * Error code when a server port is not numeric + */ +define('PEAR_CHANNELFILE_ERROR_INVALID_PORT', 33); +/** + * Error code when contains no version attribute + */ +define('PEAR_CHANNELFILE_ERROR_NO_STATICVERSION', 34); +/** + * Error code when contains no type attribute in a protocol definition + */ +define('PEAR_CHANNELFILE_ERROR_NOBASEURLTYPE', 35); +/** + * Error code when a mirror is defined and the channel.xml represents the __uri pseudo-channel + */ +define('PEAR_CHANNELFILE_URI_CANT_MIRROR', 36); +/** + * Error code when ssl attribute is present and is not "yes" + */ +define('PEAR_CHANNELFILE_ERROR_INVALID_SSL', 37); +/**#@-*/ + +/** + * Mirror types allowed. Currently only internet servers are recognized. + */ +$GLOBALS['_PEAR_CHANNELS_MIRROR_TYPES'] = array('server'); + + +/** + * The Channel handling class + * + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a1 + */ +class PEAR_ChannelFile +{ + /** + * @access private + * @var PEAR_ErrorStack + * @access private + */ + var $_stack; + + /** + * Supported channel.xml versions, for parsing + * @var array + * @access private + */ + var $_supportedVersions = array('1.0'); + + /** + * Parsed channel information + * @var array + * @access private + */ + var $_channelInfo; + + /** + * index into the subchannels array, used for parsing xml + * @var int + * @access private + */ + var $_subchannelIndex; + + /** + * index into the mirrors array, used for parsing xml + * @var int + * @access private + */ + var $_mirrorIndex; + + /** + * Flag used to determine the validity of parsed content + * @var boolean + * @access private + */ + var $_isValid = false; + + function PEAR_ChannelFile() + { + $this->_stack = &new PEAR_ErrorStack('PEAR_ChannelFile'); + $this->_stack->setErrorMessageTemplate($this->_getErrorMessage()); + $this->_isValid = false; + } + + /** + * @return array + * @access protected + */ + function _getErrorMessage() + { + return + array( + PEAR_CHANNELFILE_ERROR_INVALID_VERSION => + 'While parsing channel.xml, an invalid version number "%version% was passed in, expecting one of %versions%', + PEAR_CHANNELFILE_ERROR_NO_VERSION => + 'No version number found in tag', + PEAR_CHANNELFILE_ERROR_NO_XML_EXT => + '%error%', + PEAR_CHANNELFILE_ERROR_CANT_MAKE_PARSER => + 'Unable to create XML parser', + PEAR_CHANNELFILE_ERROR_PARSER_ERROR => + '%error%', + PEAR_CHANNELFILE_ERROR_NO_NAME => + 'Missing channel name', + PEAR_CHANNELFILE_ERROR_INVALID_NAME => + 'Invalid channel %tag% "%name%"', + PEAR_CHANNELFILE_ERROR_NO_SUMMARY => + 'Missing channel summary', + PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY => + 'Channel summary should be on one line, but is multi-line', + PEAR_CHANNELFILE_ERROR_NO_HOST => + 'Missing channel server for %type% server', + PEAR_CHANNELFILE_ERROR_INVALID_HOST => + 'Server name "%server%" is invalid for %type% server', + PEAR_CHANNELFILE_ERROR_INVALID_MIRROR => + 'Invalid mirror name "%name%", mirror type %type%', + PEAR_CHANNELFILE_ERROR_INVALID_MIRRORTYPE => + 'Invalid mirror type "%type%"', + PEAR_CHANNELFILE_ERROR_INVALID => + 'Cannot generate xml, contents are invalid', + PEAR_CHANNELFILE_ERROR_EMPTY_REGEX => + 'packagenameregex cannot be empty', + PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION => + '%parent% %protocol% function has no version', + PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME => + '%parent% %protocol% function has no name', + PEAR_CHANNELFILE_ERROR_NOBASEURLTYPE => + '%parent% rest baseurl has no type', + PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME => + 'Validation package has no name in tag', + PEAR_CHANNELFILE_ERROR_NOVALIDATE_VERSION => + 'Validation package "%package%" has no version', + PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND => + 'Mirror "%mirror%" does not exist', + PEAR_CHANNELFILE_ERROR_INVALID_PORT => + 'Port "%port%" must be numeric', + PEAR_CHANNELFILE_ERROR_NO_STATICVERSION => + ' tag must contain version attribute', + PEAR_CHANNELFILE_URI_CANT_MIRROR => + 'The __uri pseudo-channel cannot have mirrors', + PEAR_CHANNELFILE_ERROR_INVALID_SSL => + '%server% has invalid ssl attribute "%ssl%" can only be yes or not present', + ); + } + + /** + * @param string contents of package.xml file + * @return bool success of parsing + */ + function fromXmlString($data) + { + if (preg_match('/_supportedVersions)) { + $this->_stack->push(PEAR_CHANNELFILE_ERROR_INVALID_VERSION, 'error', + array('version' => $channelversion[1])); + return false; + } + $parser = new PEAR_XMLParser; + $result = $parser->parse($data); + if ($result !== true) { + if ($result->getCode() == 1) { + $this->_stack->push(PEAR_CHANNELFILE_ERROR_NO_XML_EXT, 'error', + array('error' => $result->getMessage())); + } else { + $this->_stack->push(PEAR_CHANNELFILE_ERROR_CANT_MAKE_PARSER, 'error'); + } + return false; + } + $this->_channelInfo = $parser->getData(); + return true; + } else { + $this->_stack->push(PEAR_CHANNELFILE_ERROR_NO_VERSION, 'error', array('xml' => $data)); + return false; + } + } + + /** + * @return array + */ + function toArray() + { + if (!$this->_isValid && !$this->validate()) { + return false; + } + return $this->_channelInfo; + } + + /** + * @param array + * @static + * @return PEAR_ChannelFile|false false if invalid + */ + function &fromArray($data, $compatibility = false, $stackClass = 'PEAR_ErrorStack') + { + $a = new PEAR_ChannelFile($compatibility, $stackClass); + $a->_fromArray($data); + if (!$a->validate()) { + $a = false; + return $a; + } + return $a; + } + + /** + * Unlike {@link fromArray()} this does not do any validation + * @param array + * @static + * @return PEAR_ChannelFile + */ + function &fromArrayWithErrors($data, $compatibility = false, + $stackClass = 'PEAR_ErrorStack') + { + $a = new PEAR_ChannelFile($compatibility, $stackClass); + $a->_fromArray($data); + return $a; + } + + /** + * @param array + * @access private + */ + function _fromArray($data) + { + $this->_channelInfo = $data; + } + + /** + * Wrapper to {@link PEAR_ErrorStack::getErrors()} + * @param boolean determines whether to purge the error stack after retrieving + * @return array + */ + function getErrors($purge = false) + { + return $this->_stack->getErrors($purge); + } + + /** + * Unindent given string (?) + * + * @param string $str The string that has to be unindented. + * @return string + * @access private + */ + function _unIndent($str) + { + // remove leading newlines + $str = preg_replace('/^[\r\n]+/', '', $str); + // find whitespace at the beginning of the first line + $indent_len = strspn($str, " \t"); + $indent = substr($str, 0, $indent_len); + $data = ''; + // remove the same amount of whitespace from following lines + foreach (explode("\n", $str) as $line) { + if (substr($line, 0, $indent_len) == $indent) { + $data .= substr($line, $indent_len) . "\n"; + } + } + return $data; + } + + /** + * Parse a channel.xml file. Expects the name of + * a channel xml file as input. + * + * @param string $descfile name of channel xml file + * @return bool success of parsing + */ + function fromXmlFile($descfile) + { + if (!file_exists($descfile) || !is_file($descfile) || !is_readable($descfile) || + (!$fp = fopen($descfile, 'r'))) { + require_once 'PEAR.php'; + return PEAR::raiseError("Unable to open $descfile"); + } + + // read the whole thing so we only get one cdata callback + // for each block of cdata + fclose($fp); + $data = file_get_contents($descfile); + return $this->fromXmlString($data); + } + + /** + * Parse channel information from different sources + * + * This method is able to extract information about a channel + * from an .xml file or a string + * + * @access public + * @param string Filename of the source or the source itself + * @return bool + */ + function fromAny($info) + { + if (is_string($info) && file_exists($info) && strlen($info) < 255) { + $tmp = substr($info, -4); + if ($tmp == '.xml') { + $info = $this->fromXmlFile($info); + } else { + $fp = fopen($info, "r"); + $test = fread($fp, 5); + fclose($fp); + if ($test == "fromXmlFile($info); + } + } + if (PEAR::isError($info)) { + require_once 'PEAR.php'; + return PEAR::raiseError($info); + } + } + if (is_string($info)) { + $info = $this->fromXmlString($info); + } + return $info; + } + + /** + * Return an XML document based on previous parsing and modifications + * + * @return string XML data + * + * @access public + */ + function toXml() + { + if (!$this->_isValid && !$this->validate()) { + $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID); + return false; + } + if (!isset($this->_channelInfo['attribs']['version'])) { + $this->_channelInfo['attribs']['version'] = '1.0'; + } + $channelInfo = $this->_channelInfo; + $ret = "\n"; + $ret .= " + $channelInfo[name] + " . htmlspecialchars($channelInfo['summary'])." +"; + if (isset($channelInfo['suggestedalias'])) { + $ret .= ' ' . $channelInfo['suggestedalias'] . "\n"; + } + if (isset($channelInfo['validatepackage'])) { + $ret .= ' ' . + htmlspecialchars($channelInfo['validatepackage']['_content']) . + "\n"; + } + $ret .= " \n"; + $ret .= ' _makeRestXml($channelInfo['servers']['primary']['rest'], ' '); + } + $ret .= " \n"; + if (isset($channelInfo['servers']['mirror'])) { + $ret .= $this->_makeMirrorsXml($channelInfo); + } + $ret .= " \n"; + $ret .= ""; + return str_replace("\r", "\n", str_replace("\r\n", "\n", $ret)); + } + + /** + * Generate the tag + * @access private + */ + function _makeRestXml($info, $indent) + { + $ret = $indent . "\n"; + if (isset($info['baseurl']) && !isset($info['baseurl'][0])) { + $info['baseurl'] = array($info['baseurl']); + } + + if (isset($info['baseurl'])) { + foreach ($info['baseurl'] as $url) { + $ret .= "$indent \n"; + } + } + $ret .= $indent . "\n"; + return $ret; + } + + /** + * Generate the tag + * @access private + */ + function _makeMirrorsXml($channelInfo) + { + $ret = ""; + if (!isset($channelInfo['servers']['mirror'][0])) { + $channelInfo['servers']['mirror'] = array($channelInfo['servers']['mirror']); + } + foreach ($channelInfo['servers']['mirror'] as $mirror) { + $ret .= ' _makeRestXml($mirror['rest'], ' '); + } + $ret .= " \n"; + } else { + $ret .= "/>\n"; + } + } + return $ret; + } + + /** + * Generate the tag + * @access private + */ + function _makeFunctionsXml($functions, $indent, $rest = false) + { + $ret = ''; + if (!isset($functions[0])) { + $functions = array($functions); + } + foreach ($functions as $function) { + $ret .= "$indent\n"; + } + return $ret; + } + + /** + * Validation error. Also marks the object contents as invalid + * @param error code + * @param array error information + * @access private + */ + function _validateError($code, $params = array()) + { + $this->_stack->push($code, 'error', $params); + $this->_isValid = false; + } + + /** + * Validation warning. Does not mark the object contents invalid. + * @param error code + * @param array error information + * @access private + */ + function _validateWarning($code, $params = array()) + { + $this->_stack->push($code, 'warning', $params); + } + + /** + * Validate parsed file. + * + * @access public + * @return boolean + */ + function validate() + { + $this->_isValid = true; + $info = $this->_channelInfo; + if (empty($info['name'])) { + $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_NAME); + } elseif (!$this->validChannelServer($info['name'])) { + if ($info['name'] != '__uri') { + $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME, array('tag' => 'name', + 'name' => $info['name'])); + } + } + if (empty($info['summary'])) { + $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SUMMARY); + } elseif (strpos(trim($info['summary']), "\n") !== false) { + $this->_validateWarning(PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY, + array('summary' => $info['summary'])); + } + if (isset($info['suggestedalias'])) { + if (!$this->validChannelServer($info['suggestedalias'])) { + $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME, + array('tag' => 'suggestedalias', 'name' =>$info['suggestedalias'])); + } + } + if (isset($info['localalias'])) { + if (!$this->validChannelServer($info['localalias'])) { + $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME, + array('tag' => 'localalias', 'name' =>$info['localalias'])); + } + } + if (isset($info['validatepackage'])) { + if (!isset($info['validatepackage']['_content'])) { + $this->_validateError(PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME); + } + if (!isset($info['validatepackage']['attribs']['version'])) { + $content = isset($info['validatepackage']['_content']) ? + $info['validatepackage']['_content'] : + null; + $this->_validateError(PEAR_CHANNELFILE_ERROR_NOVALIDATE_VERSION, + array('package' => $content)); + } + } + + if (isset($info['servers']['primary']['attribs'], $info['servers']['primary']['attribs']['port']) && + !is_numeric($info['servers']['primary']['attribs']['port'])) { + $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_PORT, + array('port' => $info['servers']['primary']['attribs']['port'])); + } + + if (isset($info['servers']['primary']['attribs'], $info['servers']['primary']['attribs']['ssl']) && + $info['servers']['primary']['attribs']['ssl'] != 'yes') { + $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_SSL, + array('ssl' => $info['servers']['primary']['attribs']['ssl'], + 'server' => $info['name'])); + } + + if (isset($info['servers']['primary']['rest']) && + isset($info['servers']['primary']['rest']['baseurl'])) { + $this->_validateFunctions('rest', $info['servers']['primary']['rest']['baseurl']); + } + if (isset($info['servers']['mirror'])) { + if ($this->_channelInfo['name'] == '__uri') { + $this->_validateError(PEAR_CHANNELFILE_URI_CANT_MIRROR); + } + if (!isset($info['servers']['mirror'][0])) { + $info['servers']['mirror'] = array($info['servers']['mirror']); + } + foreach ($info['servers']['mirror'] as $mirror) { + if (!isset($mirror['attribs']['host'])) { + $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_HOST, + array('type' => 'mirror')); + } elseif (!$this->validChannelServer($mirror['attribs']['host'])) { + $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_HOST, + array('server' => $mirror['attribs']['host'], 'type' => 'mirror')); + } + if (isset($mirror['attribs']['ssl']) && $mirror['attribs']['ssl'] != 'yes') { + $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_SSL, + array('ssl' => $info['ssl'], 'server' => $mirror['attribs']['host'])); + } + if (isset($mirror['rest'])) { + $this->_validateFunctions('rest', $mirror['rest']['baseurl'], + $mirror['attribs']['host']); + } + } + } + return $this->_isValid; + } + + /** + * @param string rest - protocol name this function applies to + * @param array the functions + * @param string the name of the parent element (mirror name, for instance) + */ + function _validateFunctions($protocol, $functions, $parent = '') + { + if (!isset($functions[0])) { + $functions = array($functions); + } + + foreach ($functions as $function) { + if (!isset($function['_content']) || empty($function['_content'])) { + $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME, + array('parent' => $parent, 'protocol' => $protocol)); + } + + if ($protocol == 'rest') { + if (!isset($function['attribs']['type']) || + empty($function['attribs']['type'])) { + $this->_validateError(PEAR_CHANNELFILE_ERROR_NOBASEURLTYPE, + array('parent' => $parent, 'protocol' => $protocol)); + } + } else { + if (!isset($function['attribs']['version']) || + empty($function['attribs']['version'])) { + $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION, + array('parent' => $parent, 'protocol' => $protocol)); + } + } + } + } + + /** + * Test whether a string contains a valid channel server. + * @param string $ver the package version to test + * @return bool + */ + function validChannelServer($server) + { + if ($server == '__uri') { + return true; + } + return (bool) preg_match(PEAR_CHANNELS_SERVER_PREG, $server); + } + + /** + * @return string|false + */ + function getName() + { + if (isset($this->_channelInfo['name'])) { + return $this->_channelInfo['name']; + } + + return false; + } + + /** + * @return string|false + */ + function getServer() + { + if (isset($this->_channelInfo['name'])) { + return $this->_channelInfo['name']; + } + + return false; + } + + /** + * @return int|80 port number to connect to + */ + function getPort($mirror = false) + { + if ($mirror) { + if ($mir = $this->getMirror($mirror)) { + if (isset($mir['attribs']['port'])) { + return $mir['attribs']['port']; + } + + if ($this->getSSL($mirror)) { + return 443; + } + + return 80; + } + + return false; + } + + if (isset($this->_channelInfo['servers']['primary']['attribs']['port'])) { + return $this->_channelInfo['servers']['primary']['attribs']['port']; + } + + if ($this->getSSL()) { + return 443; + } + + return 80; + } + + /** + * @return bool Determines whether secure sockets layer (SSL) is used to connect to this channel + */ + function getSSL($mirror = false) + { + if ($mirror) { + if ($mir = $this->getMirror($mirror)) { + if (isset($mir['attribs']['ssl'])) { + return true; + } + + return false; + } + + return false; + } + + if (isset($this->_channelInfo['servers']['primary']['attribs']['ssl'])) { + return true; + } + + return false; + } + + /** + * @return string|false + */ + function getSummary() + { + if (isset($this->_channelInfo['summary'])) { + return $this->_channelInfo['summary']; + } + + return false; + } + + /** + * @param string protocol type + * @param string Mirror name + * @return array|false + */ + function getFunctions($protocol, $mirror = false) + { + if ($this->getName() == '__uri') { + return false; + } + + $function = $protocol == 'rest' ? 'baseurl' : 'function'; + if ($mirror) { + if ($mir = $this->getMirror($mirror)) { + if (isset($mir[$protocol][$function])) { + return $mir[$protocol][$function]; + } + } + + return false; + } + + if (isset($this->_channelInfo['servers']['primary'][$protocol][$function])) { + return $this->_channelInfo['servers']['primary'][$protocol][$function]; + } + + return false; + } + + /** + * @param string Protocol type + * @param string Function name (null to return the + * first protocol of the type requested) + * @param string Mirror name, if any + * @return array + */ + function getFunction($type, $name = null, $mirror = false) + { + $protocols = $this->getFunctions($type, $mirror); + if (!$protocols) { + return false; + } + + foreach ($protocols as $protocol) { + if ($name === null) { + return $protocol; + } + + if ($protocol['_content'] != $name) { + continue; + } + + return $protocol; + } + + return false; + } + + /** + * @param string protocol type + * @param string protocol name + * @param string version + * @param string mirror name + * @return boolean + */ + function supports($type, $name = null, $mirror = false, $version = '1.0') + { + $protocols = $this->getFunctions($type, $mirror); + if (!$protocols) { + return false; + } + + foreach ($protocols as $protocol) { + if ($protocol['attribs']['version'] != $version) { + continue; + } + + if ($name === null) { + return true; + } + + if ($protocol['_content'] != $name) { + continue; + } + + return true; + } + + return false; + } + + /** + * Determines whether a channel supports Representational State Transfer (REST) protocols + * for retrieving channel information + * @param string + * @return bool + */ + function supportsREST($mirror = false) + { + if ($mirror == $this->_channelInfo['name']) { + $mirror = false; + } + + if ($mirror) { + if ($mir = $this->getMirror($mirror)) { + return isset($mir['rest']); + } + + return false; + } + + return isset($this->_channelInfo['servers']['primary']['rest']); + } + + /** + * Get the URL to access a base resource. + * + * Hyperlinks in the returned xml will be used to retrieve the proper information + * needed. This allows extreme extensibility and flexibility in implementation + * @param string Resource Type to retrieve + */ + function getBaseURL($resourceType, $mirror = false) + { + if ($mirror == $this->_channelInfo['name']) { + $mirror = false; + } + + if ($mirror) { + $mir = $this->getMirror($mirror); + if (!$mir) { + return false; + } + + $rest = $mir['rest']; + } else { + $rest = $this->_channelInfo['servers']['primary']['rest']; + } + + if (!isset($rest['baseurl'][0])) { + $rest['baseurl'] = array($rest['baseurl']); + } + + foreach ($rest['baseurl'] as $baseurl) { + if (strtolower($baseurl['attribs']['type']) == strtolower($resourceType)) { + return $baseurl['_content']; + } + } + + return false; + } + + /** + * Since REST does not implement RPC, provide this as a logical wrapper around + * resetFunctions for REST + * @param string|false mirror name, if any + */ + function resetREST($mirror = false) + { + return $this->resetFunctions('rest', $mirror); + } + + /** + * Empty all protocol definitions + * @param string protocol type + * @param string|false mirror name, if any + */ + function resetFunctions($type, $mirror = false) + { + if ($mirror) { + if (isset($this->_channelInfo['servers']['mirror'])) { + $mirrors = $this->_channelInfo['servers']['mirror']; + if (!isset($mirrors[0])) { + $mirrors = array($mirrors); + } + + foreach ($mirrors as $i => $mir) { + if ($mir['attribs']['host'] == $mirror) { + if (isset($this->_channelInfo['servers']['mirror'][$i][$type])) { + unset($this->_channelInfo['servers']['mirror'][$i][$type]); + } + + return true; + } + } + + return false; + } + + return false; + } + + if (isset($this->_channelInfo['servers']['primary'][$type])) { + unset($this->_channelInfo['servers']['primary'][$type]); + } + + return true; + } + + /** + * Set a channel's protocols to the protocols supported by pearweb + */ + function setDefaultPEARProtocols($version = '1.0', $mirror = false) + { + switch ($version) { + case '1.0' : + $this->resetREST($mirror); + + if (!isset($this->_channelInfo['servers'])) { + $this->_channelInfo['servers'] = array('primary' => + array('rest' => array())); + } elseif (!isset($this->_channelInfo['servers']['primary'])) { + $this->_channelInfo['servers']['primary'] = array('rest' => array()); + } + + return true; + break; + default : + return false; + break; + } + } + + /** + * @return array + */ + function getMirrors() + { + if (isset($this->_channelInfo['servers']['mirror'])) { + $mirrors = $this->_channelInfo['servers']['mirror']; + if (!isset($mirrors[0])) { + $mirrors = array($mirrors); + } + + return $mirrors; + } + + return array(); + } + + /** + * Get the unserialized XML representing a mirror + * @return array|false + */ + function getMirror($server) + { + foreach ($this->getMirrors() as $mirror) { + if ($mirror['attribs']['host'] == $server) { + return $mirror; + } + } + + return false; + } + + /** + * @param string + * @return string|false + * @error PEAR_CHANNELFILE_ERROR_NO_NAME + * @error PEAR_CHANNELFILE_ERROR_INVALID_NAME + */ + function setName($name) + { + return $this->setServer($name); + } + + /** + * Set the socket number (port) that is used to connect to this channel + * @param integer + * @param string|false name of the mirror server, or false for the primary + */ + function setPort($port, $mirror = false) + { + if ($mirror) { + if (!isset($this->_channelInfo['servers']['mirror'])) { + $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND, + array('mirror' => $mirror)); + return false; + } + + if (isset($this->_channelInfo['servers']['mirror'][0])) { + foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) { + if ($mirror == $mir['attribs']['host']) { + $this->_channelInfo['servers']['mirror'][$i]['attribs']['port'] = $port; + return true; + } + } + + return false; + } elseif ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) { + $this->_channelInfo['servers']['mirror']['attribs']['port'] = $port; + $this->_isValid = false; + return true; + } + } + + $this->_channelInfo['servers']['primary']['attribs']['port'] = $port; + $this->_isValid = false; + return true; + } + + /** + * Set the socket number (port) that is used to connect to this channel + * @param bool Determines whether to turn on SSL support or turn it off + * @param string|false name of the mirror server, or false for the primary + */ + function setSSL($ssl = true, $mirror = false) + { + if ($mirror) { + if (!isset($this->_channelInfo['servers']['mirror'])) { + $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND, + array('mirror' => $mirror)); + return false; + } + + if (isset($this->_channelInfo['servers']['mirror'][0])) { + foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) { + if ($mirror == $mir['attribs']['host']) { + if (!$ssl) { + if (isset($this->_channelInfo['servers']['mirror'][$i] + ['attribs']['ssl'])) { + unset($this->_channelInfo['servers']['mirror'][$i]['attribs']['ssl']); + } + } else { + $this->_channelInfo['servers']['mirror'][$i]['attribs']['ssl'] = 'yes'; + } + + return true; + } + } + + return false; + } elseif ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) { + if (!$ssl) { + if (isset($this->_channelInfo['servers']['mirror']['attribs']['ssl'])) { + unset($this->_channelInfo['servers']['mirror']['attribs']['ssl']); + } + } else { + $this->_channelInfo['servers']['mirror']['attribs']['ssl'] = 'yes'; + } + + $this->_isValid = false; + return true; + } + } + + if ($ssl) { + $this->_channelInfo['servers']['primary']['attribs']['ssl'] = 'yes'; + } else { + if (isset($this->_channelInfo['servers']['primary']['attribs']['ssl'])) { + unset($this->_channelInfo['servers']['primary']['attribs']['ssl']); + } + } + + $this->_isValid = false; + return true; + } + + /** + * @param string + * @return string|false + * @error PEAR_CHANNELFILE_ERROR_NO_SERVER + * @error PEAR_CHANNELFILE_ERROR_INVALID_SERVER + */ + function setServer($server, $mirror = false) + { + if (empty($server)) { + $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SERVER); + return false; + } elseif (!$this->validChannelServer($server)) { + $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME, + array('tag' => 'name', 'name' => $server)); + return false; + } + + if ($mirror) { + $found = false; + foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) { + if ($mirror == $mir['attribs']['host']) { + $found = true; + break; + } + } + + if (!$found) { + $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND, + array('mirror' => $mirror)); + return false; + } + + $this->_channelInfo['mirror'][$i]['attribs']['host'] = $server; + return true; + } + + $this->_channelInfo['name'] = $server; + return true; + } + + /** + * @param string + * @return boolean success + * @error PEAR_CHANNELFILE_ERROR_NO_SUMMARY + * @warning PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY + */ + function setSummary($summary) + { + if (empty($summary)) { + $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SUMMARY); + return false; + } elseif (strpos(trim($summary), "\n") !== false) { + $this->_validateWarning(PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY, + array('summary' => $summary)); + } + + $this->_channelInfo['summary'] = $summary; + return true; + } + + /** + * @param string + * @param boolean determines whether the alias is in channel.xml or local + * @return boolean success + */ + function setAlias($alias, $local = false) + { + if (!$this->validChannelServer($alias)) { + $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME, + array('tag' => 'suggestedalias', 'name' => $alias)); + return false; + } + + if ($local) { + $this->_channelInfo['localalias'] = $alias; + } else { + $this->_channelInfo['suggestedalias'] = $alias; + } + + return true; + } + + /** + * @return string + */ + function getAlias() + { + if (isset($this->_channelInfo['localalias'])) { + return $this->_channelInfo['localalias']; + } + if (isset($this->_channelInfo['suggestedalias'])) { + return $this->_channelInfo['suggestedalias']; + } + if (isset($this->_channelInfo['name'])) { + return $this->_channelInfo['name']; + } + return ''; + } + + /** + * Set the package validation object if it differs from PEAR's default + * The class must be includeable via changing _ in the classname to path separator, + * but no checking of this is made. + * @param string|false pass in false to reset to the default packagename regex + * @return boolean success + */ + function setValidationPackage($validateclass, $version) + { + if (empty($validateclass)) { + unset($this->_channelInfo['validatepackage']); + } + $this->_channelInfo['validatepackage'] = array('_content' => $validateclass); + $this->_channelInfo['validatepackage']['attribs'] = array('version' => $version); + } + + /** + * Add a protocol to the provides section + * @param string protocol type + * @param string protocol version + * @param string protocol name, if any + * @param string mirror name, if this is a mirror's protocol + * @return bool + */ + function addFunction($type, $version, $name = '', $mirror = false) + { + if ($mirror) { + return $this->addMirrorFunction($mirror, $type, $version, $name); + } + + $set = array('attribs' => array('version' => $version), '_content' => $name); + if (!isset($this->_channelInfo['servers']['primary'][$type]['function'])) { + if (!isset($this->_channelInfo['servers'])) { + $this->_channelInfo['servers'] = array('primary' => + array($type => array())); + } elseif (!isset($this->_channelInfo['servers']['primary'])) { + $this->_channelInfo['servers']['primary'] = array($type => array()); + } + + $this->_channelInfo['servers']['primary'][$type]['function'] = $set; + $this->_isValid = false; + return true; + } elseif (!isset($this->_channelInfo['servers']['primary'][$type]['function'][0])) { + $this->_channelInfo['servers']['primary'][$type]['function'] = array( + $this->_channelInfo['servers']['primary'][$type]['function']); + } + + $this->_channelInfo['servers']['primary'][$type]['function'][] = $set; + return true; + } + /** + * Add a protocol to a mirror's provides section + * @param string mirror name (server) + * @param string protocol type + * @param string protocol version + * @param string protocol name, if any + */ + function addMirrorFunction($mirror, $type, $version, $name = '') + { + if (!isset($this->_channelInfo['servers']['mirror'])) { + $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND, + array('mirror' => $mirror)); + return false; + } + + $setmirror = false; + if (isset($this->_channelInfo['servers']['mirror'][0])) { + foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) { + if ($mirror == $mir['attribs']['host']) { + $setmirror = &$this->_channelInfo['servers']['mirror'][$i]; + break; + } + } + } else { + if ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) { + $setmirror = &$this->_channelInfo['servers']['mirror']; + } + } + + if (!$setmirror) { + $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND, + array('mirror' => $mirror)); + return false; + } + + $set = array('attribs' => array('version' => $version), '_content' => $name); + if (!isset($setmirror[$type]['function'])) { + $setmirror[$type]['function'] = $set; + $this->_isValid = false; + return true; + } elseif (!isset($setmirror[$type]['function'][0])) { + $setmirror[$type]['function'] = array($setmirror[$type]['function']); + } + + $setmirror[$type]['function'][] = $set; + $this->_isValid = false; + return true; + } + + /** + * @param string Resource Type this url links to + * @param string URL + * @param string|false mirror name, if this is not a primary server REST base URL + */ + function setBaseURL($resourceType, $url, $mirror = false) + { + if ($mirror) { + if (!isset($this->_channelInfo['servers']['mirror'])) { + $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND, + array('mirror' => $mirror)); + return false; + } + + $setmirror = false; + if (isset($this->_channelInfo['servers']['mirror'][0])) { + foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) { + if ($mirror == $mir['attribs']['host']) { + $setmirror = &$this->_channelInfo['servers']['mirror'][$i]; + break; + } + } + } else { + if ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) { + $setmirror = &$this->_channelInfo['servers']['mirror']; + } + } + } else { + $setmirror = &$this->_channelInfo['servers']['primary']; + } + + $set = array('attribs' => array('type' => $resourceType), '_content' => $url); + if (!isset($setmirror['rest'])) { + $setmirror['rest'] = array(); + } + + if (!isset($setmirror['rest']['baseurl'])) { + $setmirror['rest']['baseurl'] = $set; + $this->_isValid = false; + return true; + } elseif (!isset($setmirror['rest']['baseurl'][0])) { + $setmirror['rest']['baseurl'] = array($setmirror['rest']['baseurl']); + } + + foreach ($setmirror['rest']['baseurl'] as $i => $url) { + if ($url['attribs']['type'] == $resourceType) { + $this->_isValid = false; + $setmirror['rest']['baseurl'][$i] = $set; + return true; + } + } + + $setmirror['rest']['baseurl'][] = $set; + $this->_isValid = false; + return true; + } + + /** + * @param string mirror server + * @param int mirror http port + * @return boolean + */ + function addMirror($server, $port = null) + { + if ($this->_channelInfo['name'] == '__uri') { + return false; // the __uri channel cannot have mirrors by definition + } + + $set = array('attribs' => array('host' => $server)); + if (is_numeric($port)) { + $set['attribs']['port'] = $port; + } + + if (!isset($this->_channelInfo['servers']['mirror'])) { + $this->_channelInfo['servers']['mirror'] = $set; + return true; + } + + if (!isset($this->_channelInfo['servers']['mirror'][0])) { + $this->_channelInfo['servers']['mirror'] = + array($this->_channelInfo['servers']['mirror']); + } + + $this->_channelInfo['servers']['mirror'][] = $set; + return true; + } + + /** + * Retrieve the name of the validation package for this channel + * @return string|false + */ + function getValidationPackage() + { + if (!$this->_isValid && !$this->validate()) { + return false; + } + + if (!isset($this->_channelInfo['validatepackage'])) { + return array('attribs' => array('version' => 'default'), + '_content' => 'PEAR_Validate'); + } + + return $this->_channelInfo['validatepackage']; + } + + /** + * Retrieve the object that can be used for custom validation + * @param string|false the name of the package to validate. If the package is + * the channel validation package, PEAR_Validate is returned + * @return PEAR_Validate|false false is returned if the validation package + * cannot be located + */ + function &getValidationObject($package = false) + { + if (!class_exists('PEAR_Validate')) { + require_once 'PEAR/Validate.php'; + } + + if (!$this->_isValid) { + if (!$this->validate()) { + $a = false; + return $a; + } + } + + if (isset($this->_channelInfo['validatepackage'])) { + if ($package == $this->_channelInfo['validatepackage']) { + // channel validation packages are always validated by PEAR_Validate + $val = &new PEAR_Validate; + return $val; + } + + if (!class_exists(str_replace('.', '_', + $this->_channelInfo['validatepackage']['_content']))) { + if ($this->isIncludeable(str_replace('_', '/', + $this->_channelInfo['validatepackage']['_content']) . '.php')) { + include_once str_replace('_', '/', + $this->_channelInfo['validatepackage']['_content']) . '.php'; + $vclass = str_replace('.', '_', + $this->_channelInfo['validatepackage']['_content']); + $val = &new $vclass; + } else { + $a = false; + return $a; + } + } else { + $vclass = str_replace('.', '_', + $this->_channelInfo['validatepackage']['_content']); + $val = &new $vclass; + } + } else { + $val = &new PEAR_Validate; + } + + return $val; + } + + function isIncludeable($path) + { + $possibilities = explode(PATH_SEPARATOR, ini_get('include_path')); + foreach ($possibilities as $dir) { + if (file_exists($dir . DIRECTORY_SEPARATOR . $path) + && is_readable($dir . DIRECTORY_SEPARATOR . $path)) { + return true; + } + } + + return false; + } + + /** + * This function is used by the channel updater and retrieves a value set by + * the registry, or the current time if it has not been set + * @return string + */ + function lastModified() + { + if (isset($this->_channelInfo['_lastmodified'])) { + return $this->_channelInfo['_lastmodified']; + } + + return time(); + } +} \ No newline at end of file diff --git a/includes/pear/PEAR/ChannelFile/Parser.php b/includes/pear/PEAR/ChannelFile/Parser.php new file mode 100644 index 0000000..5416c3d --- /dev/null +++ b/includes/pear/PEAR/ChannelFile/Parser.php @@ -0,0 +1,68 @@ + + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a1 + */ + +/** + * base xml parser class + */ +require_once 'PEAR/XMLParser.php'; +require_once 'PEAR/ChannelFile.php'; +/** + * Parser for channel.xml + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a1 + */ +class PEAR_ChannelFile_Parser extends PEAR_XMLParser +{ + var $_config; + var $_logger; + var $_registry; + + function setConfig(&$c) + { + $this->_config = &$c; + $this->_registry = &$c->getRegistry(); + } + + function setLogger(&$l) + { + $this->_logger = &$l; + } + + function parse($data, $file) + { + if (PEAR::isError($err = parent::parse($data, $file))) { + return $err; + } + + $ret = new PEAR_ChannelFile; + $ret->setConfig($this->_config); + if (isset($this->_logger)) { + $ret->setLogger($this->_logger); + } + + $ret->fromArray($this->_unserializedData); + // make sure the filelist is in the easy to read format needed + $ret->flattenFilelist(); + $ret->setPackagefile($file, $archive); + return $ret; + } +} \ No newline at end of file diff --git a/includes/pear/PEAR/Command.php b/includes/pear/PEAR/Command.php new file mode 100644 index 0000000..5dd3cc5 --- /dev/null +++ b/includes/pear/PEAR/Command.php @@ -0,0 +1,414 @@ + + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 0.1 + */ + +/** + * Needed for error handling + */ +require_once 'PEAR.php'; +require_once 'PEAR/Frontend.php'; +require_once 'PEAR/XMLParser.php'; + +/** + * List of commands and what classes they are implemented in. + * @var array command => implementing class + */ +$GLOBALS['_PEAR_Command_commandlist'] = array(); + +/** + * List of commands and their descriptions + * @var array command => description + */ +$GLOBALS['_PEAR_Command_commanddesc'] = array(); + +/** + * List of shortcuts to common commands. + * @var array shortcut => command + */ +$GLOBALS['_PEAR_Command_shortcuts'] = array(); + +/** + * Array of command objects + * @var array class => object + */ +$GLOBALS['_PEAR_Command_objects'] = array(); + +/** + * PEAR command class, a simple factory class for administrative + * commands. + * + * How to implement command classes: + * + * - The class must be called PEAR_Command_Nnn, installed in the + * "PEAR/Common" subdir, with a method called getCommands() that + * returns an array of the commands implemented by the class (see + * PEAR/Command/Install.php for an example). + * + * - The class must implement a run() function that is called with three + * params: + * + * (string) command name + * (array) assoc array with options, freely defined by each + * command, for example: + * array('force' => true) + * (array) list of the other parameters + * + * The run() function returns a PEAR_CommandResponse object. Use + * these methods to get information: + * + * int getStatus() Returns PEAR_COMMAND_(SUCCESS|FAILURE|PARTIAL) + * *_PARTIAL means that you need to issue at least + * one more command to complete the operation + * (used for example for validation steps). + * + * string getMessage() Returns a message for the user. Remember, + * no HTML or other interface-specific markup. + * + * If something unexpected happens, run() returns a PEAR error. + * + * - DON'T OUTPUT ANYTHING! Return text for output instead. + * + * - DON'T USE HTML! The text you return will be used from both Gtk, + * web and command-line interfaces, so for now, keep everything to + * plain text. + * + * - DON'T USE EXIT OR DIE! Always use pear errors. From static + * classes do PEAR::raiseError(), from other classes do + * $this->raiseError(). + * @category pear + * @package PEAR + * @author Stig Bakken + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 0.1 + */ +class PEAR_Command +{ + // {{{ factory() + + /** + * Get the right object for executing a command. + * + * @param string $command The name of the command + * @param object $config Instance of PEAR_Config object + * + * @return object the command object or a PEAR error + * + * @access public + * @static + */ + function &factory($command, &$config) + { + if (empty($GLOBALS['_PEAR_Command_commandlist'])) { + PEAR_Command::registerCommands(); + } + if (isset($GLOBALS['_PEAR_Command_shortcuts'][$command])) { + $command = $GLOBALS['_PEAR_Command_shortcuts'][$command]; + } + if (!isset($GLOBALS['_PEAR_Command_commandlist'][$command])) { + $a = PEAR::raiseError("unknown command `$command'"); + return $a; + } + $class = $GLOBALS['_PEAR_Command_commandlist'][$command]; + if (!class_exists($class)) { + require_once $GLOBALS['_PEAR_Command_objects'][$class]; + } + if (!class_exists($class)) { + $a = PEAR::raiseError("unknown command `$command'"); + return $a; + } + $ui =& PEAR_Command::getFrontendObject(); + $obj = &new $class($ui, $config); + return $obj; + } + + // }}} + // {{{ & getObject() + function &getObject($command) + { + $class = $GLOBALS['_PEAR_Command_commandlist'][$command]; + if (!class_exists($class)) { + require_once $GLOBALS['_PEAR_Command_objects'][$class]; + } + if (!class_exists($class)) { + return PEAR::raiseError("unknown command `$command'"); + } + $ui =& PEAR_Command::getFrontendObject(); + $config = &PEAR_Config::singleton(); + $obj = &new $class($ui, $config); + return $obj; + } + + // }}} + // {{{ & getFrontendObject() + + /** + * Get instance of frontend object. + * + * @return object|PEAR_Error + * @static + */ + function &getFrontendObject() + { + $a = &PEAR_Frontend::singleton(); + return $a; + } + + // }}} + // {{{ & setFrontendClass() + + /** + * Load current frontend class. + * + * @param string $uiclass Name of class implementing the frontend + * + * @return object the frontend object, or a PEAR error + * @static + */ + function &setFrontendClass($uiclass) + { + $a = &PEAR_Frontend::setFrontendClass($uiclass); + return $a; + } + + // }}} + // {{{ setFrontendType() + + /** + * Set current frontend. + * + * @param string $uitype Name of the frontend type (for example "CLI") + * + * @return object the frontend object, or a PEAR error + * @static + */ + function setFrontendType($uitype) + { + $uiclass = 'PEAR_Frontend_' . $uitype; + return PEAR_Command::setFrontendClass($uiclass); + } + + // }}} + // {{{ registerCommands() + + /** + * Scan through the Command directory looking for classes + * and see what commands they implement. + * + * @param bool (optional) if FALSE (default), the new list of + * commands should replace the current one. If TRUE, + * new entries will be merged with old. + * + * @param string (optional) where (what directory) to look for + * classes, defaults to the Command subdirectory of + * the directory from where this file (__FILE__) is + * included. + * + * @return bool TRUE on success, a PEAR error on failure + * + * @access public + * @static + */ + function registerCommands($merge = false, $dir = null) + { + $parser = new PEAR_XMLParser; + if ($dir === null) { + $dir = dirname(__FILE__) . '/Command'; + } + if (!is_dir($dir)) { + return PEAR::raiseError("registerCommands: opendir($dir) '$dir' does not exist or is not a directory"); + } + $dp = @opendir($dir); + if (empty($dp)) { + return PEAR::raiseError("registerCommands: opendir($dir) failed"); + } + if (!$merge) { + $GLOBALS['_PEAR_Command_commandlist'] = array(); + } + + while ($file = readdir($dp)) { + if ($file{0} == '.' || substr($file, -4) != '.xml') { + continue; + } + + $f = substr($file, 0, -4); + $class = "PEAR_Command_" . $f; + // List of commands + if (empty($GLOBALS['_PEAR_Command_objects'][$class])) { + $GLOBALS['_PEAR_Command_objects'][$class] = "$dir/" . $f . '.php'; + } + + $parser->parse(file_get_contents("$dir/$file")); + $implements = $parser->getData(); + foreach ($implements as $command => $desc) { + if ($command == 'attribs') { + continue; + } + + if (isset($GLOBALS['_PEAR_Command_commandlist'][$command])) { + return PEAR::raiseError('Command "' . $command . '" already registered in ' . + 'class "' . $GLOBALS['_PEAR_Command_commandlist'][$command] . '"'); + } + + $GLOBALS['_PEAR_Command_commandlist'][$command] = $class; + $GLOBALS['_PEAR_Command_commanddesc'][$command] = $desc['summary']; + if (isset($desc['shortcut'])) { + $shortcut = $desc['shortcut']; + if (isset($GLOBALS['_PEAR_Command_shortcuts'][$shortcut])) { + return PEAR::raiseError('Command shortcut "' . $shortcut . '" already ' . + 'registered to command "' . $command . '" in class "' . + $GLOBALS['_PEAR_Command_commandlist'][$command] . '"'); + } + $GLOBALS['_PEAR_Command_shortcuts'][$shortcut] = $command; + } + + if (isset($desc['options']) && $desc['options']) { + foreach ($desc['options'] as $oname => $option) { + if (isset($option['shortopt']) && strlen($option['shortopt']) > 1) { + return PEAR::raiseError('Option "' . $oname . '" short option "' . + $option['shortopt'] . '" must be ' . + 'only 1 character in Command "' . $command . '" in class "' . + $class . '"'); + } + } + } + } + } + + ksort($GLOBALS['_PEAR_Command_shortcuts']); + ksort($GLOBALS['_PEAR_Command_commandlist']); + @closedir($dp); + return true; + } + + // }}} + // {{{ getCommands() + + /** + * Get the list of currently supported commands, and what + * classes implement them. + * + * @return array command => implementing class + * + * @access public + * @static + */ + function getCommands() + { + if (empty($GLOBALS['_PEAR_Command_commandlist'])) { + PEAR_Command::registerCommands(); + } + return $GLOBALS['_PEAR_Command_commandlist']; + } + + // }}} + // {{{ getShortcuts() + + /** + * Get the list of command shortcuts. + * + * @return array shortcut => command + * + * @access public + * @static + */ + function getShortcuts() + { + if (empty($GLOBALS['_PEAR_Command_shortcuts'])) { + PEAR_Command::registerCommands(); + } + return $GLOBALS['_PEAR_Command_shortcuts']; + } + + // }}} + // {{{ getGetoptArgs() + + /** + * Compiles arguments for getopt. + * + * @param string $command command to get optstring for + * @param string $short_args (reference) short getopt format + * @param array $long_args (reference) long getopt format + * + * @return void + * + * @access public + * @static + */ + function getGetoptArgs($command, &$short_args, &$long_args) + { + if (empty($GLOBALS['_PEAR_Command_commandlist'])) { + PEAR_Command::registerCommands(); + } + if (isset($GLOBALS['_PEAR_Command_shortcuts'][$command])) { + $command = $GLOBALS['_PEAR_Command_shortcuts'][$command]; + } + if (!isset($GLOBALS['_PEAR_Command_commandlist'][$command])) { + return null; + } + $obj = &PEAR_Command::getObject($command); + return $obj->getGetoptArgs($command, $short_args, $long_args); + } + + // }}} + // {{{ getDescription() + + /** + * Get description for a command. + * + * @param string $command Name of the command + * + * @return string command description + * + * @access public + * @static + */ + function getDescription($command) + { + if (!isset($GLOBALS['_PEAR_Command_commanddesc'][$command])) { + return null; + } + return $GLOBALS['_PEAR_Command_commanddesc'][$command]; + } + + // }}} + // {{{ getHelp() + + /** + * Get help for command. + * + * @param string $command Name of the command to return help for + * + * @access public + * @static + */ + function getHelp($command) + { + $cmds = PEAR_Command::getCommands(); + if (isset($GLOBALS['_PEAR_Command_shortcuts'][$command])) { + $command = $GLOBALS['_PEAR_Command_shortcuts'][$command]; + } + if (isset($cmds[$command])) { + $obj = &PEAR_Command::getObject($command); + return $obj->getHelp($command); + } + return false; + } + // }}} +} \ No newline at end of file diff --git a/includes/pear/PEAR/Command/Auth.php b/includes/pear/PEAR/Command/Auth.php new file mode 100644 index 0000000..5376bc3 --- /dev/null +++ b/includes/pear/PEAR/Command/Auth.php @@ -0,0 +1,81 @@ + + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 0.1 + * @deprecated since 1.8.0alpha1 + */ + +/** + * base class + */ +require_once 'PEAR/Command/Channels.php'; + +/** + * PEAR commands for login/logout + * + * @category pear + * @package PEAR + * @author Stig Bakken + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 0.1 + * @deprecated since 1.8.0alpha1 + */ +class PEAR_Command_Auth extends PEAR_Command_Channels +{ + var $commands = array( + 'login' => array( + 'summary' => 'Connects and authenticates to remote server [Deprecated in favor of channel-login]', + 'shortcut' => 'li', + 'function' => 'doLogin', + 'options' => array(), + 'doc' => ' +WARNING: This function is deprecated in favor of using channel-login + +Log in to a remote channel server. If is not supplied, +the default channel is used. To use remote functions in the installer +that require any kind of privileges, you need to log in first. The +username and password you enter here will be stored in your per-user +PEAR configuration (~/.pearrc on Unix-like systems). After logging +in, your username and password will be sent along in subsequent +operations on the remote server.', + ), + 'logout' => array( + 'summary' => 'Logs out from the remote server [Deprecated in favor of channel-logout]', + 'shortcut' => 'lo', + 'function' => 'doLogout', + 'options' => array(), + 'doc' => ' +WARNING: This function is deprecated in favor of using channel-logout + +Logs out from the remote server. This command does not actually +connect to the remote server, it only deletes the stored username and +password from your user configuration.', + ) + + ); + + /** + * PEAR_Command_Auth constructor. + * + * @access public + */ + function PEAR_Command_Auth(&$ui, &$config) + { + parent::PEAR_Command_Channels($ui, $config); + } +} \ No newline at end of file diff --git a/includes/pear/PEAR/Command/Auth.xml b/includes/pear/PEAR/Command/Auth.xml new file mode 100644 index 0000000..590193d --- /dev/null +++ b/includes/pear/PEAR/Command/Auth.xml @@ -0,0 +1,30 @@ + + + Connects and authenticates to remote server [Deprecated in favor of channel-login] + doLogin + li + + <channel name> +WARNING: This function is deprecated in favor of using channel-login + +Log in to a remote channel server. If <channel name> is not supplied, +the default channel is used. To use remote functions in the installer +that require any kind of privileges, you need to log in first. The +username and password you enter here will be stored in your per-user +PEAR configuration (~/.pearrc on Unix-like systems). After logging +in, your username and password will be sent along in subsequent +operations on the remote server. + + + Logs out from the remote server [Deprecated in favor of channel-logout] + doLogout + lo + + +WARNING: This function is deprecated in favor of using channel-logout + +Logs out from the remote server. This command does not actually +connect to the remote server, it only deletes the stored username and +password from your user configuration. + + \ No newline at end of file diff --git a/includes/pear/PEAR/Command/Build.php b/includes/pear/PEAR/Command/Build.php new file mode 100644 index 0000000..67a61d3 --- /dev/null +++ b/includes/pear/PEAR/Command/Build.php @@ -0,0 +1,85 @@ + + * @author Tomas V.V.Cox + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 0.1 + */ + +/** + * base class + */ +require_once 'PEAR/Command/Common.php'; + +/** + * PEAR commands for building extensions. + * + * @category pear + * @package PEAR + * @author Stig Bakken + * @author Tomas V.V.Cox + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 0.1 + */ +class PEAR_Command_Build extends PEAR_Command_Common +{ + var $commands = array( + 'build' => array( + 'summary' => 'Build an Extension From C Source', + 'function' => 'doBuild', + 'shortcut' => 'b', + 'options' => array(), + 'doc' => '[package.xml] +Builds one or more extensions contained in a package.' + ), + ); + + /** + * PEAR_Command_Build constructor. + * + * @access public + */ + function PEAR_Command_Build(&$ui, &$config) + { + parent::PEAR_Command_Common($ui, $config); + } + + function doBuild($command, $options, $params) + { + require_once 'PEAR/Builder.php'; + if (sizeof($params) < 1) { + $params[0] = 'package.xml'; + } + + $builder = &new PEAR_Builder($this->ui); + $this->debug = $this->config->get('verbose'); + $err = $builder->build($params[0], array(&$this, 'buildCallback')); + if (PEAR::isError($err)) { + return $err; + } + + return true; + } + + function buildCallback($what, $data) + { + if (($what == 'cmdoutput' && $this->debug > 1) || + ($what == 'output' && $this->debug > 0)) { + $this->ui->outputData(rtrim($data), 'build'); + } + } +} \ No newline at end of file diff --git a/includes/pear/PEAR/Command/Build.xml b/includes/pear/PEAR/Command/Build.xml new file mode 100644 index 0000000..ec4e6f5 --- /dev/null +++ b/includes/pear/PEAR/Command/Build.xml @@ -0,0 +1,10 @@ + + + Build an Extension From C Source + doBuild + b + + [package.xml] +Builds one or more extensions contained in a package. + + \ No newline at end of file diff --git a/includes/pear/PEAR/Command/Channels.php b/includes/pear/PEAR/Command/Channels.php new file mode 100644 index 0000000..5ab8f42 --- /dev/null +++ b/includes/pear/PEAR/Command/Channels.php @@ -0,0 +1,883 @@ + + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a1 + */ + +/** + * base class + */ +require_once 'PEAR/Command/Common.php'; + +define('PEAR_COMMAND_CHANNELS_CHANNEL_EXISTS', -500); + +/** + * PEAR commands for managing channels. + * + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a1 + */ +class PEAR_Command_Channels extends PEAR_Command_Common +{ + var $commands = array( + 'list-channels' => array( + 'summary' => 'List Available Channels', + 'function' => 'doList', + 'shortcut' => 'lc', + 'options' => array(), + 'doc' => ' +List all available channels for installation. +', + ), + 'update-channels' => array( + 'summary' => 'Update the Channel List', + 'function' => 'doUpdateAll', + 'shortcut' => 'uc', + 'options' => array(), + 'doc' => ' +List all installed packages in all channels. +' + ), + 'channel-delete' => array( + 'summary' => 'Remove a Channel From the List', + 'function' => 'doDelete', + 'shortcut' => 'cde', + 'options' => array(), + 'doc' => ' +Delete a channel from the registry. You may not +remove any channel that has installed packages. +' + ), + 'channel-add' => array( + 'summary' => 'Add a Channel', + 'function' => 'doAdd', + 'shortcut' => 'ca', + 'options' => array(), + 'doc' => ' +Add a private channel to the channel list. Note that all +public channels should be synced using "update-channels". +Parameter may be either a local file or remote URL to a +channel.xml. +' + ), + 'channel-update' => array( + 'summary' => 'Update an Existing Channel', + 'function' => 'doUpdate', + 'shortcut' => 'cu', + 'options' => array( + 'force' => array( + 'shortopt' => 'f', + 'doc' => 'will force download of new channel.xml if an existing channel name is used', + ), + 'channel' => array( + 'shortopt' => 'c', + 'arg' => 'CHANNEL', + 'doc' => 'will force download of new channel.xml if an existing channel name is used', + ), +), + 'doc' => '[|] +Update a channel in the channel list directly. Note that all +public channels can be synced using "update-channels". +Parameter may be a local or remote channel.xml, or the name of +an existing channel. +' + ), + 'channel-info' => array( + 'summary' => 'Retrieve Information on a Channel', + 'function' => 'doInfo', + 'shortcut' => 'ci', + 'options' => array(), + 'doc' => ' +List the files in an installed package. +' + ), + 'channel-alias' => array( + 'summary' => 'Specify an alias to a channel name', + 'function' => 'doAlias', + 'shortcut' => 'cha', + 'options' => array(), + 'doc' => ' +Specify a specific alias to use for a channel name. +The alias may not be an existing channel name or +alias. +' + ), + 'channel-discover' => array( + 'summary' => 'Initialize a Channel from its server', + 'function' => 'doDiscover', + 'shortcut' => 'di', + 'options' => array(), + 'doc' => '[|] +Initialize a channel from its server and create a local channel.xml. +If is in the format ":@" then + and will be set as the login username/password for +. Use caution when passing the username/password in this way, as +it may allow other users on your computer to briefly view your username/ +password via the system\'s process list. +' + ), + 'channel-login' => array( + 'summary' => 'Connects and authenticates to remote channel server', + 'shortcut' => 'cli', + 'function' => 'doLogin', + 'options' => array(), + 'doc' => ' +Log in to a remote channel server. If is not supplied, +the default channel is used. To use remote functions in the installer +that require any kind of privileges, you need to log in first. The +username and password you enter here will be stored in your per-user +PEAR configuration (~/.pearrc on Unix-like systems). After logging +in, your username and password will be sent along in subsequent +operations on the remote server.', + ), + 'channel-logout' => array( + 'summary' => 'Logs out from the remote channel server', + 'shortcut' => 'clo', + 'function' => 'doLogout', + 'options' => array(), + 'doc' => ' +Logs out from a remote channel server. If is not supplied, +the default channel is used. This command does not actually connect to the +remote server, it only deletes the stored username and password from your user +configuration.', + ), + ); + + /** + * PEAR_Command_Registry constructor. + * + * @access public + */ + function PEAR_Command_Channels(&$ui, &$config) + { + parent::PEAR_Command_Common($ui, $config); + } + + function _sortChannels($a, $b) + { + return strnatcasecmp($a->getName(), $b->getName()); + } + + function doList($command, $options, $params) + { + $reg = &$this->config->getRegistry(); + $registered = $reg->getChannels(); + usort($registered, array(&$this, '_sortchannels')); + $i = $j = 0; + $data = array( + 'caption' => 'Registered Channels:', + 'border' => true, + 'headline' => array('Channel', 'Alias', 'Summary') + ); + foreach ($registered as $channel) { + $data['data'][] = array($channel->getName(), + $channel->getAlias(), + $channel->getSummary()); + } + + if (count($registered) === 0) { + $data = '(no registered channels)'; + } + $this->ui->outputData($data, $command); + return true; + } + + function doUpdateAll($command, $options, $params) + { + $reg = &$this->config->getRegistry(); + $channels = $reg->getChannels(); + + $success = true; + foreach ($channels as $channel) { + if ($channel->getName() != '__uri') { + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $err = $this->doUpdate('channel-update', + $options, + array($channel->getName())); + if (PEAR::isError($err)) { + $this->ui->outputData($err->getMessage(), $command); + $success = false; + } else { + $success &= $err; + } + } + } + return $success; + } + + function doInfo($command, $options, $params) + { + if (count($params) !== 1) { + return $this->raiseError("No channel specified"); + } + + $reg = &$this->config->getRegistry(); + $channel = strtolower($params[0]); + if ($reg->channelExists($channel)) { + $chan = $reg->getChannel($channel); + if (PEAR::isError($chan)) { + return $this->raiseError($chan); + } + } else { + if (strpos($channel, '://')) { + $downloader = &$this->getDownloader(); + $tmpdir = $this->config->get('temp_dir'); + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $loc = $downloader->downloadHttp($channel, $this->ui, $tmpdir); + PEAR::staticPopErrorHandling(); + if (PEAR::isError($loc)) { + return $this->raiseError('Cannot open "' . $channel . + '" (' . $loc->getMessage() . ')'); + } else { + $contents = implode('', file($loc)); + } + } else { + if (!file_exists($params[0])) { + return $this->raiseError('Unknown channel "' . $channel . '"'); + } + + $fp = fopen($params[0], 'r'); + if (!$fp) { + return $this->raiseError('Cannot open "' . $params[0] . '"'); + } + + $contents = ''; + while (!feof($fp)) { + $contents .= fread($fp, 1024); + } + fclose($fp); + } + + if (!class_exists('PEAR_ChannelFile')) { + require_once 'PEAR/ChannelFile.php'; + } + + $chan = new PEAR_ChannelFile; + $chan->fromXmlString($contents); + $chan->validate(); + if ($errs = $chan->getErrors(true)) { + foreach ($errs as $err) { + $this->ui->outputData($err['level'] . ': ' . $err['message']); + } + return $this->raiseError('Channel file "' . $params[0] . '" is not valid'); + } + } + + if (!$chan) { + return $this->raiseError('Serious error: Channel "' . $params[0] . + '" has a corrupted registry entry'); + } + + $channel = $chan->getName(); + $caption = 'Channel ' . $channel . ' Information:'; + $data1 = array( + 'caption' => $caption, + 'border' => true); + $data1['data']['server'] = array('Name and Server', $chan->getName()); + if ($chan->getAlias() != $chan->getName()) { + $data1['data']['alias'] = array('Alias', $chan->getAlias()); + } + + $data1['data']['summary'] = array('Summary', $chan->getSummary()); + $validate = $chan->getValidationPackage(); + $data1['data']['vpackage'] = array('Validation Package Name', $validate['_content']); + $data1['data']['vpackageversion'] = + array('Validation Package Version', $validate['attribs']['version']); + $d = array(); + $d['main'] = $data1; + + $data['data'] = array(); + $data['caption'] = 'Server Capabilities'; + $data['headline'] = array('Type', 'Version/REST type', 'Function Name/REST base'); + if ($chan->supportsREST()) { + if ($chan->supportsREST()) { + $funcs = $chan->getFunctions('rest'); + if (!isset($funcs[0])) { + $funcs = array($funcs); + } + foreach ($funcs as $protocol) { + $data['data'][] = array('rest', $protocol['attribs']['type'], + $protocol['_content']); + } + } + } else { + $data['data'][] = array('No supported protocols'); + } + + $d['protocols'] = $data; + $data['data'] = array(); + $mirrors = $chan->getMirrors(); + if ($mirrors) { + $data['caption'] = 'Channel ' . $channel . ' Mirrors:'; + unset($data['headline']); + foreach ($mirrors as $mirror) { + $data['data'][] = array($mirror['attribs']['host']); + $d['mirrors'] = $data; + } + + foreach ($mirrors as $i => $mirror) { + $data['data'] = array(); + $data['caption'] = 'Mirror ' . $mirror['attribs']['host'] . ' Capabilities'; + $data['headline'] = array('Type', 'Version/REST type', 'Function Name/REST base'); + if ($chan->supportsREST($mirror['attribs']['host'])) { + if ($chan->supportsREST($mirror['attribs']['host'])) { + $funcs = $chan->getFunctions('rest', $mirror['attribs']['host']); + if (!isset($funcs[0])) { + $funcs = array($funcs); + } + + foreach ($funcs as $protocol) { + $data['data'][] = array('rest', $protocol['attribs']['type'], + $protocol['_content']); + } + } + } else { + $data['data'][] = array('No supported protocols'); + } + $d['mirrorprotocols' . $i] = $data; + } + } + $this->ui->outputData($d, 'channel-info'); + } + + // }}} + + function doDelete($command, $options, $params) + { + if (count($params) !== 1) { + return $this->raiseError('channel-delete: no channel specified'); + } + + $reg = &$this->config->getRegistry(); + if (!$reg->channelExists($params[0])) { + return $this->raiseError('channel-delete: channel "' . $params[0] . '" does not exist'); + } + + $channel = $reg->channelName($params[0]); + if ($channel == 'pear.php.net') { + return $this->raiseError('Cannot delete the pear.php.net channel'); + } + + if ($channel == 'pecl.php.net') { + return $this->raiseError('Cannot delete the pecl.php.net channel'); + } + + if ($channel == 'doc.php.net') { + return $this->raiseError('Cannot delete the doc.php.net channel'); + } + + if ($channel == '__uri') { + return $this->raiseError('Cannot delete the __uri pseudo-channel'); + } + + if (PEAR::isError($err = $reg->listPackages($channel))) { + return $err; + } + + if (count($err)) { + return $this->raiseError('Channel "' . $channel . + '" has installed packages, cannot delete'); + } + + if (!$reg->deleteChannel($channel)) { + return $this->raiseError('Channel "' . $channel . '" deletion failed'); + } else { + $this->config->deleteChannel($channel); + $this->ui->outputData('Channel "' . $channel . '" deleted', $command); + } + } + + function doAdd($command, $options, $params) + { + if (count($params) !== 1) { + return $this->raiseError('channel-add: no channel file specified'); + } + + if (strpos($params[0], '://')) { + $downloader = &$this->getDownloader(); + $tmpdir = $this->config->get('temp_dir'); + if (!file_exists($tmpdir)) { + require_once 'System.php'; + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $err = System::mkdir(array('-p', $tmpdir)); + PEAR::staticPopErrorHandling(); + if (PEAR::isError($err)) { + return $this->raiseError('channel-add: temp_dir does not exist: "' . + $tmpdir . + '" - You can change this location with "pear config-set temp_dir"'); + } + } + + if (!is_writable($tmpdir)) { + return $this->raiseError('channel-add: temp_dir is not writable: "' . + $tmpdir . + '" - You can change this location with "pear config-set temp_dir"'); + } + + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $loc = $downloader->downloadHttp($params[0], $this->ui, $tmpdir, null, false); + PEAR::staticPopErrorHandling(); + if (PEAR::isError($loc)) { + return $this->raiseError('channel-add: Cannot open "' . $params[0] . + '" (' . $loc->getMessage() . ')'); + } + + list($loc, $lastmodified) = $loc; + $contents = implode('', file($loc)); + } else { + $lastmodified = $fp = false; + if (file_exists($params[0])) { + $fp = fopen($params[0], 'r'); + } + + if (!$fp) { + return $this->raiseError('channel-add: cannot open "' . $params[0] . '"'); + } + + $contents = ''; + while (!feof($fp)) { + $contents .= fread($fp, 1024); + } + fclose($fp); + } + + if (!class_exists('PEAR_ChannelFile')) { + require_once 'PEAR/ChannelFile.php'; + } + + $channel = new PEAR_ChannelFile; + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $result = $channel->fromXmlString($contents); + PEAR::staticPopErrorHandling(); + if (!$result) { + $exit = false; + if (count($errors = $channel->getErrors(true))) { + foreach ($errors as $error) { + $this->ui->outputData(ucfirst($error['level'] . ': ' . $error['message'])); + if (!$exit) { + $exit = $error['level'] == 'error' ? true : false; + } + } + if ($exit) { + return $this->raiseError('channel-add: invalid channel.xml file'); + } + } + } + + $reg = &$this->config->getRegistry(); + if ($reg->channelExists($channel->getName())) { + return $this->raiseError('channel-add: Channel "' . $channel->getName() . + '" exists, use channel-update to update entry', PEAR_COMMAND_CHANNELS_CHANNEL_EXISTS); + } + + $ret = $reg->addChannel($channel, $lastmodified); + if (PEAR::isError($ret)) { + return $ret; + } + + if (!$ret) { + return $this->raiseError('channel-add: adding Channel "' . $channel->getName() . + '" to registry failed'); + } + + $this->config->setChannels($reg->listChannels()); + $this->config->writeConfigFile(); + $this->ui->outputData('Adding Channel "' . $channel->getName() . '" succeeded', $command); + } + + function doUpdate($command, $options, $params) + { + if (count($params) !== 1) { + return $this->raiseError("No channel file specified"); + } + + $tmpdir = $this->config->get('temp_dir'); + if (!file_exists($tmpdir)) { + require_once 'System.php'; + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $err = System::mkdir(array('-p', $tmpdir)); + PEAR::staticPopErrorHandling(); + if (PEAR::isError($err)) { + return $this->raiseError('channel-add: temp_dir does not exist: "' . + $tmpdir . + '" - You can change this location with "pear config-set temp_dir"'); + } + } + + if (!is_writable($tmpdir)) { + return $this->raiseError('channel-add: temp_dir is not writable: "' . + $tmpdir . + '" - You can change this location with "pear config-set temp_dir"'); + } + + $reg = &$this->config->getRegistry(); + $lastmodified = false; + if ((!file_exists($params[0]) || is_dir($params[0])) + && $reg->channelExists(strtolower($params[0]))) { + $c = $reg->getChannel(strtolower($params[0])); + if (PEAR::isError($c)) { + return $this->raiseError($c); + } + + $this->ui->outputData("Updating channel \"$params[0]\"", $command); + $dl = &$this->getDownloader(array()); + // if force is specified, use a timestamp of "1" to force retrieval + $lastmodified = isset($options['force']) ? false : $c->lastModified(); + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $contents = $dl->downloadHttp('http://' . $c->getName() . '/channel.xml', + $this->ui, $tmpdir, null, $lastmodified); + PEAR::staticPopErrorHandling(); + if (PEAR::isError($contents)) { + // Attempt to fall back to https + $this->ui->outputData("Channel \"$params[0]\" is not responding over http://, failed with message: " . $contents->getMessage()); + $this->ui->outputData("Trying channel \"$params[0]\" over https:// instead"); + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $contents = $dl->downloadHttp('https://' . $c->getName() . '/channel.xml', + $this->ui, $tmpdir, null, $lastmodified); + PEAR::staticPopErrorHandling(); + if (PEAR::isError($contents)) { + return $this->raiseError('Cannot retrieve channel.xml for channel "' . + $c->getName() . '" (' . $contents->getMessage() . ')'); + } + } + + list($contents, $lastmodified) = $contents; + if (!$contents) { + $this->ui->outputData("Channel \"$params[0]\" is up to date"); + return; + } + + $contents = implode('', file($contents)); + if (!class_exists('PEAR_ChannelFile')) { + require_once 'PEAR/ChannelFile.php'; + } + + $channel = new PEAR_ChannelFile; + $channel->fromXmlString($contents); + if (!$channel->getErrors()) { + // security check: is the downloaded file for the channel we got it from? + if (strtolower($channel->getName()) != strtolower($c->getName())) { + if (!isset($options['force'])) { + return $this->raiseError('ERROR: downloaded channel definition file' . + ' for channel "' . $channel->getName() . '" from channel "' . + strtolower($c->getName()) . '"'); + } + + $this->ui->log(0, 'WARNING: downloaded channel definition file' . + ' for channel "' . $channel->getName() . '" from channel "' . + strtolower($c->getName()) . '"'); + } + } + } else { + if (strpos($params[0], '://')) { + $dl = &$this->getDownloader(); + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $loc = $dl->downloadHttp($params[0], + $this->ui, $tmpdir, null, $lastmodified); + PEAR::staticPopErrorHandling(); + if (PEAR::isError($loc)) { + return $this->raiseError("Cannot open " . $params[0] . + ' (' . $loc->getMessage() . ')'); + } + + list($loc, $lastmodified) = $loc; + $contents = implode('', file($loc)); + } else { + $fp = false; + if (file_exists($params[0])) { + $fp = fopen($params[0], 'r'); + } + + if (!$fp) { + return $this->raiseError("Cannot open " . $params[0]); + } + + $contents = ''; + while (!feof($fp)) { + $contents .= fread($fp, 1024); + } + fclose($fp); + } + + if (!class_exists('PEAR_ChannelFile')) { + require_once 'PEAR/ChannelFile.php'; + } + + $channel = new PEAR_ChannelFile; + $channel->fromXmlString($contents); + } + + $exit = false; + if (count($errors = $channel->getErrors(true))) { + foreach ($errors as $error) { + $this->ui->outputData(ucfirst($error['level'] . ': ' . $error['message'])); + if (!$exit) { + $exit = $error['level'] == 'error' ? true : false; + } + } + if ($exit) { + return $this->raiseError('Invalid channel.xml file'); + } + } + + if (!$reg->channelExists($channel->getName())) { + return $this->raiseError('Error: Channel "' . $channel->getName() . + '" does not exist, use channel-add to add an entry'); + } + + $ret = $reg->updateChannel($channel, $lastmodified); + if (PEAR::isError($ret)) { + return $ret; + } + + if (!$ret) { + return $this->raiseError('Updating Channel "' . $channel->getName() . + '" in registry failed'); + } + + $this->config->setChannels($reg->listChannels()); + $this->config->writeConfigFile(); + $this->ui->outputData('Update of Channel "' . $channel->getName() . '" succeeded'); + } + + function &getDownloader() + { + if (!class_exists('PEAR_Downloader')) { + require_once 'PEAR/Downloader.php'; + } + $a = new PEAR_Downloader($this->ui, array(), $this->config); + return $a; + } + + function doAlias($command, $options, $params) + { + if (count($params) === 1) { + return $this->raiseError('No channel alias specified'); + } + + if (count($params) !== 2 || (!empty($params[1]) && $params[1]{0} == '-')) { + return $this->raiseError( + 'Invalid format, correct is: channel-alias channel alias'); + } + + $reg = &$this->config->getRegistry(); + if (!$reg->channelExists($params[0], true)) { + $extra = ''; + if ($reg->isAlias($params[0])) { + $extra = ' (use "channel-alias ' . $reg->channelName($params[0]) . ' ' . + strtolower($params[1]) . '")'; + } + + return $this->raiseError('"' . $params[0] . '" is not a valid channel' . $extra); + } + + if ($reg->isAlias($params[1])) { + return $this->raiseError('Channel "' . $reg->channelName($params[1]) . '" is ' . + 'already aliased to "' . strtolower($params[1]) . '", cannot re-alias'); + } + + $chan = &$reg->getChannel($params[0]); + if (PEAR::isError($chan)) { + return $this->raiseError('Corrupt registry? Error retrieving channel "' . $params[0] . + '" information (' . $chan->getMessage() . ')'); + } + + // make it a local alias + if (!$chan->setAlias(strtolower($params[1]), true)) { + return $this->raiseError('Alias "' . strtolower($params[1]) . + '" is not a valid channel alias'); + } + + $reg->updateChannel($chan); + $this->ui->outputData('Channel "' . $chan->getName() . '" aliased successfully to "' . + strtolower($params[1]) . '"'); + } + + /** + * The channel-discover command + * + * @param string $command command name + * @param array $options option_name => value + * @param array $params list of additional parameters. + * $params[0] should contain a string with either: + * - or + * - :@ + * @return null|PEAR_Error + */ + function doDiscover($command, $options, $params) + { + if (count($params) !== 1) { + return $this->raiseError("No channel server specified"); + } + + // Look for the possible input format ":@" + if (preg_match('/^(.+):(.+)@(.+)\\z/', $params[0], $matches)) { + $username = $matches[1]; + $password = $matches[2]; + $channel = $matches[3]; + } else { + $channel = $params[0]; + } + + $reg = &$this->config->getRegistry(); + if ($reg->channelExists($channel)) { + if (!$reg->isAlias($channel)) { + return $this->raiseError("Channel \"$channel\" is already initialized", PEAR_COMMAND_CHANNELS_CHANNEL_EXISTS); + } + + return $this->raiseError("A channel alias named \"$channel\" " . + 'already exists, aliasing channel "' . $reg->channelName($channel) + . '"'); + } + + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $err = $this->doAdd($command, $options, array('http://' . $channel . '/channel.xml')); + $this->popErrorHandling(); + if (PEAR::isError($err)) { + if ($err->getCode() === PEAR_COMMAND_CHANNELS_CHANNEL_EXISTS) { + return $this->raiseError("Discovery of channel \"$channel\" failed (" . + $err->getMessage() . ')'); + } + // Attempt fetch via https + $this->ui->outputData("Discovering channel $channel over http:// failed with message: " . $err->getMessage()); + $this->ui->outputData("Trying to discover channel $channel over https:// instead"); + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $err = $this->doAdd($command, $options, array('https://' . $channel . '/channel.xml')); + $this->popErrorHandling(); + if (PEAR::isError($err)) { + return $this->raiseError("Discovery of channel \"$channel\" failed (" . + $err->getMessage() . ')'); + } + } + + // Store username/password if they were given + // Arguably we should do a logintest on the channel here, but since + // that's awkward on a REST-based channel (even "pear login" doesn't + // do it for those), and XML-RPC is deprecated, it's fairly pointless. + if (isset($username)) { + $this->config->set('username', $username, 'user', $channel); + $this->config->set('password', $password, 'user', $channel); + $this->config->store(); + $this->ui->outputData("Stored login for channel \"$channel\" using username \"$username\"", $command); + } + + $this->ui->outputData("Discovery of channel \"$channel\" succeeded", $command); + } + + /** + * Execute the 'login' command. + * + * @param string $command command name + * @param array $options option_name => value + * @param array $params list of additional parameters + * + * @return bool TRUE on success or + * a PEAR error on failure + * + * @access public + */ + function doLogin($command, $options, $params) + { + $reg = &$this->config->getRegistry(); + + // If a parameter is supplied, use that as the channel to log in to + $channel = isset($params[0]) ? $params[0] : $this->config->get('default_channel'); + + $chan = $reg->getChannel($channel); + if (PEAR::isError($chan)) { + return $this->raiseError($chan); + } + + $server = $this->config->get('preferred_mirror', null, $channel); + $username = $this->config->get('username', null, $channel); + if (empty($username)) { + $username = isset($_ENV['USER']) ? $_ENV['USER'] : null; + } + $this->ui->outputData("Logging in to $server.", $command); + + list($username, $password) = $this->ui->userDialog( + $command, + array('Username', 'Password'), + array('text', 'password'), + array($username, '') + ); + $username = trim($username); + $password = trim($password); + + $ourfile = $this->config->getConfFile('user'); + if (!$ourfile) { + $ourfile = $this->config->getConfFile('system'); + } + + $this->config->set('username', $username, 'user', $channel); + $this->config->set('password', $password, 'user', $channel); + + if ($chan->supportsREST()) { + $ok = true; + } + + if ($ok !== true) { + return $this->raiseError('Login failed!'); + } + + $this->ui->outputData("Logged in.", $command); + // avoid changing any temporary settings changed with -d + $ourconfig = new PEAR_Config($ourfile, $ourfile); + $ourconfig->set('username', $username, 'user', $channel); + $ourconfig->set('password', $password, 'user', $channel); + $ourconfig->store(); + + return true; + } + + /** + * Execute the 'logout' command. + * + * @param string $command command name + * @param array $options option_name => value + * @param array $params list of additional parameters + * + * @return bool TRUE on success or + * a PEAR error on failure + * + * @access public + */ + function doLogout($command, $options, $params) + { + $reg = &$this->config->getRegistry(); + + // If a parameter is supplied, use that as the channel to log in to + $channel = isset($params[0]) ? $params[0] : $this->config->get('default_channel'); + + $chan = $reg->getChannel($channel); + if (PEAR::isError($chan)) { + return $this->raiseError($chan); + } + + $server = $this->config->get('preferred_mirror', null, $channel); + $this->ui->outputData("Logging out from $server.", $command); + $this->config->remove('username', 'user', $channel); + $this->config->remove('password', 'user', $channel); + $this->config->store(); + return true; + } +} \ No newline at end of file diff --git a/includes/pear/PEAR/Command/Channels.xml b/includes/pear/PEAR/Command/Channels.xml new file mode 100644 index 0000000..47b7206 --- /dev/null +++ b/includes/pear/PEAR/Command/Channels.xml @@ -0,0 +1,123 @@ + + + List Available Channels + doList + lc + + +List all available channels for installation. + + + + Update the Channel List + doUpdateAll + uc + + +List all installed packages in all channels. + + + + Remove a Channel From the List + doDelete + cde + + <channel name> +Delete a channel from the registry. You may not +remove any channel that has installed packages. + + + + Add a Channel + doAdd + ca + + <channel.xml> +Add a private channel to the channel list. Note that all +public channels should be synced using "update-channels". +Parameter may be either a local file or remote URL to a +channel.xml. + + + + Update an Existing Channel + doUpdate + cu + + + f + will force download of new channel.xml if an existing channel name is used + + + c + will force download of new channel.xml if an existing channel name is used + CHANNEL + + + [<channel.xml>|<channel name>] +Update a channel in the channel list directly. Note that all +public channels can be synced using "update-channels". +Parameter may be a local or remote channel.xml, or the name of +an existing channel. + + + + Retrieve Information on a Channel + doInfo + ci + + <package> +List the files in an installed package. + + + + Specify an alias to a channel name + doAlias + cha + + <channel> <alias> +Specify a specific alias to use for a channel name. +The alias may not be an existing channel name or +alias. + + + + Initialize a Channel from its server + doDiscover + di + + [<channel.xml>|<channel name>] +Initialize a channel from its server and create a local channel.xml. +If <channel name> is in the format "<username>:<password>@<channel>" then +<username> and <password> will be set as the login username/password for +<channel>. Use caution when passing the username/password in this way, as +it may allow other users on your computer to briefly view your username/ +password via the system's process list. + + + + Connects and authenticates to remote channel server + doLogin + cli + + <channel name> +Log in to a remote channel server. If <channel name> is not supplied, +the default channel is used. To use remote functions in the installer +that require any kind of privileges, you need to log in first. The +username and password you enter here will be stored in your per-user +PEAR configuration (~/.pearrc on Unix-like systems). After logging +in, your username and password will be sent along in subsequent +operations on the remote server. + + + Logs out from the remote channel server + doLogout + clo + + <channel name> +Logs out from a remote channel server. If <channel name> is not supplied, +the default channel is used. This command does not actually connect to the +remote server, it only deletes the stored username and password from your user +configuration. + + \ No newline at end of file diff --git a/includes/pear/PEAR/Command/Common.php b/includes/pear/PEAR/Command/Common.php new file mode 100644 index 0000000..69b5cf2 --- /dev/null +++ b/includes/pear/PEAR/Command/Common.php @@ -0,0 +1,273 @@ + + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 0.1 + */ + +/** + * base class + */ +require_once 'PEAR.php'; + +/** + * PEAR commands base class + * + * @category pear + * @package PEAR + * @author Stig Bakken + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 0.1 + */ +class PEAR_Command_Common extends PEAR +{ + /** + * PEAR_Config object used to pass user system and configuration + * on when executing commands + * + * @var PEAR_Config + */ + var $config; + /** + * @var PEAR_Registry + * @access protected + */ + var $_registry; + + /** + * User Interface object, for all interaction with the user. + * @var object + */ + var $ui; + + var $_deps_rel_trans = array( + 'lt' => '<', + 'le' => '<=', + 'eq' => '=', + 'ne' => '!=', + 'gt' => '>', + 'ge' => '>=', + 'has' => '==' + ); + + var $_deps_type_trans = array( + 'pkg' => 'package', + 'ext' => 'extension', + 'php' => 'PHP', + 'prog' => 'external program', + 'ldlib' => 'external library for linking', + 'rtlib' => 'external runtime library', + 'os' => 'operating system', + 'websrv' => 'web server', + 'sapi' => 'SAPI backend' + ); + + /** + * PEAR_Command_Common constructor. + * + * @access public + */ + function PEAR_Command_Common(&$ui, &$config) + { + parent::PEAR(); + $this->config = &$config; + $this->ui = &$ui; + } + + /** + * Return a list of all the commands defined by this class. + * @return array list of commands + * @access public + */ + function getCommands() + { + $ret = array(); + foreach (array_keys($this->commands) as $command) { + $ret[$command] = $this->commands[$command]['summary']; + } + + return $ret; + } + + /** + * Return a list of all the command shortcuts defined by this class. + * @return array shortcut => command + * @access public + */ + function getShortcuts() + { + $ret = array(); + foreach (array_keys($this->commands) as $command) { + if (isset($this->commands[$command]['shortcut'])) { + $ret[$this->commands[$command]['shortcut']] = $command; + } + } + + return $ret; + } + + function getOptions($command) + { + $shortcuts = $this->getShortcuts(); + if (isset($shortcuts[$command])) { + $command = $shortcuts[$command]; + } + + if (isset($this->commands[$command]) && + isset($this->commands[$command]['options'])) { + return $this->commands[$command]['options']; + } + + return null; + } + + function getGetoptArgs($command, &$short_args, &$long_args) + { + $short_args = ''; + $long_args = array(); + if (empty($this->commands[$command]) || empty($this->commands[$command]['options'])) { + return; + } + + reset($this->commands[$command]['options']); + while (list($option, $info) = each($this->commands[$command]['options'])) { + $larg = $sarg = ''; + if (isset($info['arg'])) { + if ($info['arg']{0} == '(') { + $larg = '=='; + $sarg = '::'; + $arg = substr($info['arg'], 1, -1); + } else { + $larg = '='; + $sarg = ':'; + $arg = $info['arg']; + } + } + + if (isset($info['shortopt'])) { + $short_args .= $info['shortopt'] . $sarg; + } + + $long_args[] = $option . $larg; + } + } + + /** + * Returns the help message for the given command + * + * @param string $command The command + * @return mixed A fail string if the command does not have help or + * a two elements array containing [0]=>help string, + * [1]=> help string for the accepted cmd args + */ + function getHelp($command) + { + $config = &PEAR_Config::singleton(); + if (!isset($this->commands[$command])) { + return "No such command \"$command\""; + } + + $help = null; + if (isset($this->commands[$command]['doc'])) { + $help = $this->commands[$command]['doc']; + } + + if (empty($help)) { + // XXX (cox) Fallback to summary if there is no doc (show both?) + if (!isset($this->commands[$command]['summary'])) { + return "No help for command \"$command\""; + } + $help = $this->commands[$command]['summary']; + } + + if (preg_match_all('/{config\s+([^\}]+)}/e', $help, $matches)) { + foreach($matches[0] as $k => $v) { + $help = preg_replace("/$v/", $config->get($matches[1][$k]), $help); + } + } + + return array($help, $this->getHelpArgs($command)); + } + + /** + * Returns the help for the accepted arguments of a command + * + * @param string $command + * @return string The help string + */ + function getHelpArgs($command) + { + if (isset($this->commands[$command]['options']) && + count($this->commands[$command]['options'])) + { + $help = "Options:\n"; + foreach ($this->commands[$command]['options'] as $k => $v) { + if (isset($v['arg'])) { + if ($v['arg'][0] == '(') { + $arg = substr($v['arg'], 1, -1); + $sapp = " [$arg]"; + $lapp = "[=$arg]"; + } else { + $sapp = " $v[arg]"; + $lapp = "=$v[arg]"; + } + } else { + $sapp = $lapp = ""; + } + + if (isset($v['shortopt'])) { + $s = $v['shortopt']; + $help .= " -$s$sapp, --$k$lapp\n"; + } else { + $help .= " --$k$lapp\n"; + } + + $p = " "; + $doc = rtrim(str_replace("\n", "\n$p", $v['doc'])); + $help .= " $doc\n"; + } + + return $help; + } + + return null; + } + + function run($command, $options, $params) + { + if (empty($this->commands[$command]['function'])) { + // look for shortcuts + foreach (array_keys($this->commands) as $cmd) { + if (isset($this->commands[$cmd]['shortcut']) && $this->commands[$cmd]['shortcut'] == $command) { + if (empty($this->commands[$cmd]['function'])) { + return $this->raiseError("unknown command `$command'"); + } else { + $func = $this->commands[$cmd]['function']; + } + $command = $cmd; + + //$command = $this->commands[$cmd]['function']; + break; + } + } + } else { + $func = $this->commands[$command]['function']; + } + + return $this->$func($command, $options, $params); + } +} \ No newline at end of file diff --git a/includes/pear/PEAR/Command/Config.php b/includes/pear/PEAR/Command/Config.php new file mode 100644 index 0000000..8561ee5 --- /dev/null +++ b/includes/pear/PEAR/Command/Config.php @@ -0,0 +1,414 @@ + + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 0.1 + */ + +/** + * base class + */ +require_once 'PEAR/Command/Common.php'; + +/** + * PEAR commands for managing configuration data. + * + * @category pear + * @package PEAR + * @author Stig Bakken + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 0.1 + */ +class PEAR_Command_Config extends PEAR_Command_Common +{ + var $commands = array( + 'config-show' => array( + 'summary' => 'Show All Settings', + 'function' => 'doConfigShow', + 'shortcut' => 'csh', + 'options' => array( + 'channel' => array( + 'shortopt' => 'c', + 'doc' => 'show configuration variables for another channel', + 'arg' => 'CHAN', + ), +), + 'doc' => '[layer] +Displays all configuration values. An optional argument +may be used to tell which configuration layer to display. Valid +configuration layers are "user", "system" and "default". To display +configurations for different channels, set the default_channel +configuration variable and run config-show again. +', + ), + 'config-get' => array( + 'summary' => 'Show One Setting', + 'function' => 'doConfigGet', + 'shortcut' => 'cg', + 'options' => array( + 'channel' => array( + 'shortopt' => 'c', + 'doc' => 'show configuration variables for another channel', + 'arg' => 'CHAN', + ), +), + 'doc' => ' [layer] +Displays the value of one configuration parameter. The +first argument is the name of the parameter, an optional second argument +may be used to tell which configuration layer to look in. Valid configuration +layers are "user", "system" and "default". If no layer is specified, a value +will be picked from the first layer that defines the parameter, in the order +just specified. The configuration value will be retrieved for the channel +specified by the default_channel configuration variable. +', + ), + 'config-set' => array( + 'summary' => 'Change Setting', + 'function' => 'doConfigSet', + 'shortcut' => 'cs', + 'options' => array( + 'channel' => array( + 'shortopt' => 'c', + 'doc' => 'show configuration variables for another channel', + 'arg' => 'CHAN', + ), +), + 'doc' => ' [layer] +Sets the value of one configuration parameter. The first argument is +the name of the parameter, the second argument is the new value. Some +parameters are subject to validation, and the command will fail with +an error message if the new value does not make sense. An optional +third argument may be used to specify in which layer to set the +configuration parameter. The default layer is "user". The +configuration value will be set for the current channel, which +is controlled by the default_channel configuration variable. +', + ), + 'config-help' => array( + 'summary' => 'Show Information About Setting', + 'function' => 'doConfigHelp', + 'shortcut' => 'ch', + 'options' => array(), + 'doc' => '[parameter] +Displays help for a configuration parameter. Without arguments it +displays help for all configuration parameters. +', + ), + 'config-create' => array( + 'summary' => 'Create a Default configuration file', + 'function' => 'doConfigCreate', + 'shortcut' => 'coc', + 'options' => array( + 'windows' => array( + 'shortopt' => 'w', + 'doc' => 'create a config file for a windows install', + ), + ), + 'doc' => ' +Create a default configuration file with all directory configuration +variables set to subdirectories of , and save it as . +This is useful especially for creating a configuration file for a remote +PEAR installation (using the --remoteconfig option of install, upgrade, +and uninstall). +', + ), + ); + + /** + * PEAR_Command_Config constructor. + * + * @access public + */ + function PEAR_Command_Config(&$ui, &$config) + { + parent::PEAR_Command_Common($ui, $config); + } + + function doConfigShow($command, $options, $params) + { + $layer = null; + if (is_array($params)) { + $layer = isset($params[0]) ? $params[0] : null; + } + + // $params[0] -> the layer + if ($error = $this->_checkLayer($layer)) { + return $this->raiseError("config-show:$error"); + } + + $keys = $this->config->getKeys(); + sort($keys); + $channel = isset($options['channel']) ? $options['channel'] : + $this->config->get('default_channel'); + $reg = &$this->config->getRegistry(); + if (!$reg->channelExists($channel)) { + return $this->raiseError('Channel "' . $channel . '" does not exist'); + } + + $channel = $reg->channelName($channel); + $data = array('caption' => 'Configuration (channel ' . $channel . '):'); + foreach ($keys as $key) { + $type = $this->config->getType($key); + $value = $this->config->get($key, $layer, $channel); + if ($type == 'password' && $value) { + $value = '********'; + } + + if ($value === false) { + $value = 'false'; + } elseif ($value === true) { + $value = 'true'; + } + + $data['data'][$this->config->getGroup($key)][] = array($this->config->getPrompt($key) , $key, $value); + } + + foreach ($this->config->getLayers() as $layer) { + $data['data']['Config Files'][] = array(ucfirst($layer) . ' Configuration File', 'Filename' , $this->config->getConfFile($layer)); + } + + $this->ui->outputData($data, $command); + return true; + } + + function doConfigGet($command, $options, $params) + { + $args_cnt = is_array($params) ? count($params) : 0; + switch ($args_cnt) { + case 1: + $config_key = $params[0]; + $layer = null; + break; + case 2: + $config_key = $params[0]; + $layer = $params[1]; + if ($error = $this->_checkLayer($layer)) { + return $this->raiseError("config-get:$error"); + } + break; + case 0: + default: + return $this->raiseError("config-get expects 1 or 2 parameters"); + } + + $reg = &$this->config->getRegistry(); + $channel = isset($options['channel']) ? $options['channel'] : $this->config->get('default_channel'); + if (!$reg->channelExists($channel)) { + return $this->raiseError('Channel "' . $channel . '" does not exist'); + } + + $channel = $reg->channelName($channel); + $this->ui->outputData($this->config->get($config_key, $layer, $channel), $command); + return true; + } + + function doConfigSet($command, $options, $params) + { + // $param[0] -> a parameter to set + // $param[1] -> the value for the parameter + // $param[2] -> the layer + $failmsg = ''; + if (count($params) < 2 || count($params) > 3) { + $failmsg .= "config-set expects 2 or 3 parameters"; + return PEAR::raiseError($failmsg); + } + + if (isset($params[2]) && ($error = $this->_checkLayer($params[2]))) { + $failmsg .= $error; + return PEAR::raiseError("config-set:$failmsg"); + } + + $channel = isset($options['channel']) ? $options['channel'] : $this->config->get('default_channel'); + $reg = &$this->config->getRegistry(); + if (!$reg->channelExists($channel)) { + return $this->raiseError('Channel "' . $channel . '" does not exist'); + } + + $channel = $reg->channelName($channel); + if ($params[0] == 'default_channel' && !$reg->channelExists($params[1])) { + return $this->raiseError('Channel "' . $params[1] . '" does not exist'); + } + + if ($params[0] == 'preferred_mirror' + && ( + !$reg->mirrorExists($channel, $params[1]) && + (!$reg->channelExists($params[1]) || $channel != $params[1]) + ) + ) { + $msg = 'Channel Mirror "' . $params[1] . '" does not exist'; + $msg .= ' in your registry for channel "' . $channel . '".'; + $msg .= "\n" . 'Attempt to run "pear channel-update ' . $channel .'"'; + $msg .= ' if you believe this mirror should exist as you may'; + $msg .= ' have outdated channel information.'; + return $this->raiseError($msg); + } + + if (count($params) == 2) { + array_push($params, 'user'); + $layer = 'user'; + } else { + $layer = $params[2]; + } + + array_push($params, $channel); + if (!call_user_func_array(array(&$this->config, 'set'), $params)) { + array_pop($params); + $failmsg = "config-set (" . implode(", ", $params) . ") failed, channel $channel"; + } else { + $this->config->store($layer); + } + + if ($failmsg) { + return $this->raiseError($failmsg); + } + + $this->ui->outputData('config-set succeeded', $command); + return true; + } + + function doConfigHelp($command, $options, $params) + { + if (empty($params)) { + $params = $this->config->getKeys(); + } + + $data['caption'] = "Config help" . ((count($params) == 1) ? " for $params[0]" : ''); + $data['headline'] = array('Name', 'Type', 'Description'); + $data['border'] = true; + foreach ($params as $name) { + $type = $this->config->getType($name); + $docs = $this->config->getDocs($name); + if ($type == 'set') { + $docs = rtrim($docs) . "\nValid set: " . + implode(' ', $this->config->getSetValues($name)); + } + + $data['data'][] = array($name, $type, $docs); + } + + $this->ui->outputData($data, $command); + } + + function doConfigCreate($command, $options, $params) + { + if (count($params) != 2) { + return PEAR::raiseError('config-create: must have 2 parameters, root path and ' . + 'filename to save as'); + } + + $root = $params[0]; + // Clean up the DIRECTORY_SEPARATOR mess + $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR; + $root = preg_replace(array('!\\\\+!', '!/+!', "!$ds2+!"), + array('/', '/', '/'), + $root); + if ($root{0} != '/') { + if (!isset($options['windows'])) { + return PEAR::raiseError('Root directory must be an absolute path beginning ' . + 'with "/", was: "' . $root . '"'); + } + + if (!preg_match('/^[A-Za-z]:/', $root)) { + return PEAR::raiseError('Root directory must be an absolute path beginning ' . + 'with "\\" or "C:\\", was: "' . $root . '"'); + } + } + + $windows = isset($options['windows']); + if ($windows) { + $root = str_replace('/', '\\', $root); + } + + if (!file_exists($params[1]) && !@touch($params[1])) { + return PEAR::raiseError('Could not create "' . $params[1] . '"'); + } + + $params[1] = realpath($params[1]); + $config = &new PEAR_Config($params[1], '#no#system#config#', false, false); + if ($root{strlen($root) - 1} == '/') { + $root = substr($root, 0, strlen($root) - 1); + } + + $config->noRegistry(); + $config->set('php_dir', $windows ? "$root\\pear\\php" : "$root/pear/php", 'user'); + $config->set('data_dir', $windows ? "$root\\pear\\data" : "$root/pear/data"); + $config->set('www_dir', $windows ? "$root\\pear\\www" : "$root/pear/www"); + $config->set('cfg_dir', $windows ? "$root\\pear\\cfg" : "$root/pear/cfg"); + $config->set('ext_dir', $windows ? "$root\\pear\\ext" : "$root/pear/ext"); + $config->set('doc_dir', $windows ? "$root\\pear\\docs" : "$root/pear/docs"); + $config->set('test_dir', $windows ? "$root\\pear\\tests" : "$root/pear/tests"); + $config->set('cache_dir', $windows ? "$root\\pear\\cache" : "$root/pear/cache"); + $config->set('download_dir', $windows ? "$root\\pear\\download" : "$root/pear/download"); + $config->set('temp_dir', $windows ? "$root\\pear\\temp" : "$root/pear/temp"); + $config->set('bin_dir', $windows ? "$root\\pear" : "$root/pear"); + $config->writeConfigFile(); + $this->_showConfig($config); + $this->ui->outputData('Successfully created default configuration file "' . $params[1] . '"', + $command); + } + + function _showConfig(&$config) + { + $params = array('user'); + $keys = $config->getKeys(); + sort($keys); + $channel = 'pear.php.net'; + $data = array('caption' => 'Configuration (channel ' . $channel . '):'); + foreach ($keys as $key) { + $type = $config->getType($key); + $value = $config->get($key, 'user', $channel); + if ($type == 'password' && $value) { + $value = '********'; + } + + if ($value === false) { + $value = 'false'; + } elseif ($value === true) { + $value = 'true'; + } + $data['data'][$config->getGroup($key)][] = + array($config->getPrompt($key) , $key, $value); + } + + foreach ($config->getLayers() as $layer) { + $data['data']['Config Files'][] = + array(ucfirst($layer) . ' Configuration File', 'Filename' , + $config->getConfFile($layer)); + } + + $this->ui->outputData($data, 'config-show'); + return true; + } + + /** + * Checks if a layer is defined or not + * + * @param string $layer The layer to search for + * @return mixed False on no error or the error message + */ + function _checkLayer($layer = null) + { + if (!empty($layer) && $layer != 'default') { + $layers = $this->config->getLayers(); + if (!in_array($layer, $layers)) { + return " only the layers: \"" . implode('" or "', $layers) . "\" are supported"; + } + } + + return false; + } +} \ No newline at end of file diff --git a/includes/pear/PEAR/Command/Config.xml b/includes/pear/PEAR/Command/Config.xml new file mode 100644 index 0000000..f64a925 --- /dev/null +++ b/includes/pear/PEAR/Command/Config.xml @@ -0,0 +1,92 @@ + + + Show All Settings + doConfigShow + csh + + + c + show configuration variables for another channel + CHAN + + + [layer] +Displays all configuration values. An optional argument +may be used to tell which configuration layer to display. Valid +configuration layers are "user", "system" and "default". To display +configurations for different channels, set the default_channel +configuration variable and run config-show again. + + + + Show One Setting + doConfigGet + cg + + + c + show configuration variables for another channel + CHAN + + + <parameter> [layer] +Displays the value of one configuration parameter. The +first argument is the name of the parameter, an optional second argument +may be used to tell which configuration layer to look in. Valid configuration +layers are "user", "system" and "default". If no layer is specified, a value +will be picked from the first layer that defines the parameter, in the order +just specified. The configuration value will be retrieved for the channel +specified by the default_channel configuration variable. + + + + Change Setting + doConfigSet + cs + + + c + show configuration variables for another channel + CHAN + + + <parameter> <value> [layer] +Sets the value of one configuration parameter. The first argument is +the name of the parameter, the second argument is the new value. Some +parameters are subject to validation, and the command will fail with +an error message if the new value does not make sense. An optional +third argument may be used to specify in which layer to set the +configuration parameter. The default layer is "user". The +configuration value will be set for the current channel, which +is controlled by the default_channel configuration variable. + + + + Show Information About Setting + doConfigHelp + ch + + [parameter] +Displays help for a configuration parameter. Without arguments it +displays help for all configuration parameters. + + + + Create a Default configuration file + doConfigCreate + coc + + + w + create a config file for a windows install + + + <root path> <filename> +Create a default configuration file with all directory configuration +variables set to subdirectories of <root path>, and save it as <filename>. +This is useful especially for creating a configuration file for a remote +PEAR installation (using the --remoteconfig option of install, upgrade, +and uninstall). + + + \ No newline at end of file diff --git a/includes/pear/PEAR/Command/Install.php b/includes/pear/PEAR/Command/Install.php new file mode 100644 index 0000000..79e68db --- /dev/null +++ b/includes/pear/PEAR/Command/Install.php @@ -0,0 +1,1268 @@ + + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 0.1 + */ + +/** + * base class + */ +require_once 'PEAR/Command/Common.php'; + +/** + * PEAR commands for installation or deinstallation/upgrading of + * packages. + * + * @category pear + * @package PEAR + * @author Stig Bakken + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 0.1 + */ +class PEAR_Command_Install extends PEAR_Command_Common +{ + // {{{ properties + + var $commands = array( + 'install' => array( + 'summary' => 'Install Package', + 'function' => 'doInstall', + 'shortcut' => 'i', + 'options' => array( + 'force' => array( + 'shortopt' => 'f', + 'doc' => 'will overwrite newer installed packages', + ), + 'loose' => array( + 'shortopt' => 'l', + 'doc' => 'do not check for recommended dependency version', + ), + 'nodeps' => array( + 'shortopt' => 'n', + 'doc' => 'ignore dependencies, install anyway', + ), + 'register-only' => array( + 'shortopt' => 'r', + 'doc' => 'do not install files, only register the package as installed', + ), + 'soft' => array( + 'shortopt' => 's', + 'doc' => 'soft install, fail silently, or upgrade if already installed', + ), + 'nobuild' => array( + 'shortopt' => 'B', + 'doc' => 'don\'t build C extensions', + ), + 'nocompress' => array( + 'shortopt' => 'Z', + 'doc' => 'request uncompressed files when downloading', + ), + 'installroot' => array( + 'shortopt' => 'R', + 'arg' => 'DIR', + 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT), use packagingroot for RPM', + ), + 'packagingroot' => array( + 'shortopt' => 'P', + 'arg' => 'DIR', + 'doc' => 'root directory used when packaging files, like RPM packaging', + ), + 'ignore-errors' => array( + 'doc' => 'force install even if there were errors', + ), + 'alldeps' => array( + 'shortopt' => 'a', + 'doc' => 'install all required and optional dependencies', + ), + 'onlyreqdeps' => array( + 'shortopt' => 'o', + 'doc' => 'install all required dependencies', + ), + 'offline' => array( + 'shortopt' => 'O', + 'doc' => 'do not attempt to download any urls or contact channels', + ), + 'pretend' => array( + 'shortopt' => 'p', + 'doc' => 'Only list the packages that would be downloaded', + ), + ), + 'doc' => '[channel/] ... +Installs one or more PEAR packages. You can specify a package to +install in four ways: + +"Package-1.0.tgz" : installs from a local file + +"http://example.com/Package-1.0.tgz" : installs from +anywhere on the net. + +"package.xml" : installs the package described in +package.xml. Useful for testing, or for wrapping a PEAR package in +another package manager such as RPM. + +"Package[-version/state][.tar]" : queries your default channel\'s server +({config master_server}) and downloads the newest package with +the preferred quality/state ({config preferred_state}). + +To retrieve Package version 1.1, use "Package-1.1," to retrieve +Package state beta, use "Package-beta." To retrieve an uncompressed +file, append .tar (make sure there is no file by the same name first) + +To download a package from another channel, prefix with the channel name like +"channel/Package" + +More than one package may be specified at once. It is ok to mix these +four ways of specifying packages. +'), + 'upgrade' => array( + 'summary' => 'Upgrade Package', + 'function' => 'doInstall', + 'shortcut' => 'up', + 'options' => array( + 'channel' => array( + 'shortopt' => 'c', + 'doc' => 'upgrade packages from a specific channel', + 'arg' => 'CHAN', + ), + 'force' => array( + 'shortopt' => 'f', + 'doc' => 'overwrite newer installed packages', + ), + 'loose' => array( + 'shortopt' => 'l', + 'doc' => 'do not check for recommended dependency version', + ), + 'nodeps' => array( + 'shortopt' => 'n', + 'doc' => 'ignore dependencies, upgrade anyway', + ), + 'register-only' => array( + 'shortopt' => 'r', + 'doc' => 'do not install files, only register the package as upgraded', + ), + 'nobuild' => array( + 'shortopt' => 'B', + 'doc' => 'don\'t build C extensions', + ), + 'nocompress' => array( + 'shortopt' => 'Z', + 'doc' => 'request uncompressed files when downloading', + ), + 'installroot' => array( + 'shortopt' => 'R', + 'arg' => 'DIR', + 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)', + ), + 'ignore-errors' => array( + 'doc' => 'force install even if there were errors', + ), + 'alldeps' => array( + 'shortopt' => 'a', + 'doc' => 'install all required and optional dependencies', + ), + 'onlyreqdeps' => array( + 'shortopt' => 'o', + 'doc' => 'install all required dependencies', + ), + 'offline' => array( + 'shortopt' => 'O', + 'doc' => 'do not attempt to download any urls or contact channels', + ), + 'pretend' => array( + 'shortopt' => 'p', + 'doc' => 'Only list the packages that would be downloaded', + ), + ), + 'doc' => ' ... +Upgrades one or more PEAR packages. See documentation for the +"install" command for ways to specify a package. + +When upgrading, your package will be updated if the provided new +package has a higher version number (use the -f option if you need to +upgrade anyway). + +More than one package may be specified at once. +'), + 'upgrade-all' => array( + 'summary' => 'Upgrade All Packages [Deprecated in favor of calling upgrade with no parameters]', + 'function' => 'doUpgradeAll', + 'shortcut' => 'ua', + 'options' => array( + 'channel' => array( + 'shortopt' => 'c', + 'doc' => 'upgrade packages from a specific channel', + 'arg' => 'CHAN', + ), + 'nodeps' => array( + 'shortopt' => 'n', + 'doc' => 'ignore dependencies, upgrade anyway', + ), + 'register-only' => array( + 'shortopt' => 'r', + 'doc' => 'do not install files, only register the package as upgraded', + ), + 'nobuild' => array( + 'shortopt' => 'B', + 'doc' => 'don\'t build C extensions', + ), + 'nocompress' => array( + 'shortopt' => 'Z', + 'doc' => 'request uncompressed files when downloading', + ), + 'installroot' => array( + 'shortopt' => 'R', + 'arg' => 'DIR', + 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT), use packagingroot for RPM', + ), + 'ignore-errors' => array( + 'doc' => 'force install even if there were errors', + ), + 'loose' => array( + 'doc' => 'do not check for recommended dependency version', + ), + ), + 'doc' => ' +WARNING: This function is deprecated in favor of using the upgrade command with no params + +Upgrades all packages that have a newer release available. Upgrades are +done only if there is a release available of the state specified in +"preferred_state" (currently {config preferred_state}), or a state considered +more stable. +'), + 'uninstall' => array( + 'summary' => 'Un-install Package', + 'function' => 'doUninstall', + 'shortcut' => 'un', + 'options' => array( + 'nodeps' => array( + 'shortopt' => 'n', + 'doc' => 'ignore dependencies, uninstall anyway', + ), + 'register-only' => array( + 'shortopt' => 'r', + 'doc' => 'do not remove files, only register the packages as not installed', + ), + 'installroot' => array( + 'shortopt' => 'R', + 'arg' => 'DIR', + 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)', + ), + 'ignore-errors' => array( + 'doc' => 'force install even if there were errors', + ), + 'offline' => array( + 'shortopt' => 'O', + 'doc' => 'do not attempt to uninstall remotely', + ), + ), + 'doc' => '[channel/] ... +Uninstalls one or more PEAR packages. More than one package may be +specified at once. Prefix with channel name to uninstall from a +channel not in your default channel ({config default_channel}) +'), + 'bundle' => array( + 'summary' => 'Unpacks a Pecl Package', + 'function' => 'doBundle', + 'shortcut' => 'bun', + 'options' => array( + 'destination' => array( + 'shortopt' => 'd', + 'arg' => 'DIR', + 'doc' => 'Optional destination directory for unpacking (defaults to current path or "ext" if exists)', + ), + 'force' => array( + 'shortopt' => 'f', + 'doc' => 'Force the unpacking even if there were errors in the package', + ), + ), + 'doc' => ' +Unpacks a Pecl Package into the selected location. It will download the +package if needed. +'), + 'run-scripts' => array( + 'summary' => 'Run Post-Install Scripts bundled with a package', + 'function' => 'doRunScripts', + 'shortcut' => 'rs', + 'options' => array( + ), + 'doc' => ' +Run post-installation scripts in package , if any exist. +'), + ); + + // }}} + // {{{ constructor + + /** + * PEAR_Command_Install constructor. + * + * @access public + */ + function PEAR_Command_Install(&$ui, &$config) + { + parent::PEAR_Command_Common($ui, $config); + } + + // }}} + + /** + * For unit testing purposes + */ + function &getDownloader(&$ui, $options, &$config) + { + if (!class_exists('PEAR_Downloader')) { + require_once 'PEAR/Downloader.php'; + } + $a = &new PEAR_Downloader($ui, $options, $config); + return $a; + } + + /** + * For unit testing purposes + */ + function &getInstaller(&$ui) + { + if (!class_exists('PEAR_Installer')) { + require_once 'PEAR/Installer.php'; + } + $a = &new PEAR_Installer($ui); + return $a; + } + + function enableExtension($binaries, $type) + { + if (!($phpini = $this->config->get('php_ini', null, 'pear.php.net'))) { + return PEAR::raiseError('configuration option "php_ini" is not set to php.ini location'); + } + $ini = $this->_parseIni($phpini); + if (PEAR::isError($ini)) { + return $ini; + } + $line = 0; + if ($type == 'extsrc' || $type == 'extbin') { + $search = 'extensions'; + $enable = 'extension'; + } else { + $search = 'zend_extensions'; + ob_start(); + phpinfo(INFO_GENERAL); + $info = ob_get_contents(); + ob_end_clean(); + $debug = function_exists('leak') ? '_debug' : ''; + $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : ''; + $enable = 'zend_extension' . $debug . $ts; + } + foreach ($ini[$search] as $line => $extension) { + if (in_array($extension, $binaries, true) || in_array( + $ini['extension_dir'] . DIRECTORY_SEPARATOR . $extension, $binaries, true)) { + // already enabled - assume if one is, all are + return true; + } + } + if ($line) { + $newini = array_slice($ini['all'], 0, $line); + } else { + $newini = array(); + } + foreach ($binaries as $binary) { + if (!$this->config->get('ext_dir') && $ini['extension_dir']) { + $binary = basename($binary); + } + $newini[] = $enable . '="' . $binary . '"' . (OS_UNIX ? "\n" : "\r\n"); + } + $newini = array_merge($newini, array_slice($ini['all'], $line)); + $fp = @fopen($phpini, 'wb'); + if (!$fp) { + return PEAR::raiseError('cannot open php.ini "' . $phpini . '" for writing'); + } + foreach ($newini as $line) { + fwrite($fp, $line); + } + fclose($fp); + return true; + } + + function disableExtension($binaries, $type) + { + if (!($phpini = $this->config->get('php_ini', null, 'pear.php.net'))) { + return PEAR::raiseError('configuration option "php_ini" is not set to php.ini location'); + } + $ini = $this->_parseIni($phpini); + if (PEAR::isError($ini)) { + return $ini; + } + $line = 0; + if ($type == 'extsrc' || $type == 'extbin') { + $search = 'extensions'; + $enable = 'extension'; + } else { + $search = 'zend_extensions'; + ob_start(); + phpinfo(INFO_GENERAL); + $info = ob_get_contents(); + ob_end_clean(); + $debug = function_exists('leak') ? '_debug' : ''; + $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : ''; + $enable = 'zend_extension' . $debug . $ts; + } + $found = false; + foreach ($ini[$search] as $line => $extension) { + if (in_array($extension, $binaries, true) || in_array( + $ini['extension_dir'] . DIRECTORY_SEPARATOR . $extension, $binaries, true)) { + $found = true; + break; + } + } + if (!$found) { + // not enabled + return true; + } + $fp = @fopen($phpini, 'wb'); + if (!$fp) { + return PEAR::raiseError('cannot open php.ini "' . $phpini . '" for writing'); + } + if ($line) { + $newini = array_slice($ini['all'], 0, $line); + // delete the enable line + $newini = array_merge($newini, array_slice($ini['all'], $line + 1)); + } else { + $newini = array_slice($ini['all'], 1); + } + foreach ($newini as $line) { + fwrite($fp, $line); + } + fclose($fp); + return true; + } + + function _parseIni($filename) + { + if (!file_exists($filename)) { + return PEAR::raiseError('php.ini "' . $filename . '" does not exist'); + } + + if (filesize($filename) > 300000) { + return PEAR::raiseError('php.ini "' . $filename . '" is too large, aborting'); + } + + ob_start(); + phpinfo(INFO_GENERAL); + $info = ob_get_contents(); + ob_end_clean(); + $debug = function_exists('leak') ? '_debug' : ''; + $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : ''; + $zend_extension_line = 'zend_extension' . $debug . $ts; + $all = @file($filename); + if (!$all) { + return PEAR::raiseError('php.ini "' . $filename .'" could not be read'); + } + $zend_extensions = $extensions = array(); + // assume this is right, but pull from the php.ini if it is found + $extension_dir = ini_get('extension_dir'); + foreach ($all as $linenum => $line) { + $line = trim($line); + if (!$line) { + continue; + } + if ($line[0] == ';') { + continue; + } + if (strtolower(substr($line, 0, 13)) == 'extension_dir') { + $line = trim(substr($line, 13)); + if ($line[0] == '=') { + $x = trim(substr($line, 1)); + $x = explode(';', $x); + $extension_dir = str_replace('"', '', array_shift($x)); + continue; + } + } + if (strtolower(substr($line, 0, 9)) == 'extension') { + $line = trim(substr($line, 9)); + if ($line[0] == '=') { + $x = trim(substr($line, 1)); + $x = explode(';', $x); + $extensions[$linenum] = str_replace('"', '', array_shift($x)); + continue; + } + } + if (strtolower(substr($line, 0, strlen($zend_extension_line))) == + $zend_extension_line) { + $line = trim(substr($line, strlen($zend_extension_line))); + if ($line[0] == '=') { + $x = trim(substr($line, 1)); + $x = explode(';', $x); + $zend_extensions[$linenum] = str_replace('"', '', array_shift($x)); + continue; + } + } + } + return array( + 'extensions' => $extensions, + 'zend_extensions' => $zend_extensions, + 'extension_dir' => $extension_dir, + 'all' => $all, + ); + } + + // {{{ doInstall() + + function doInstall($command, $options, $params) + { + if (!class_exists('PEAR_PackageFile')) { + require_once 'PEAR/PackageFile.php'; + } + + if (isset($options['installroot']) && isset($options['packagingroot'])) { + return $this->raiseError('ERROR: cannot use both --installroot and --packagingroot'); + } + + $reg = &$this->config->getRegistry(); + $channel = isset($options['channel']) ? $options['channel'] : $this->config->get('default_channel'); + if (!$reg->channelExists($channel)) { + return $this->raiseError('Channel "' . $channel . '" does not exist'); + } + + if (empty($this->installer)) { + $this->installer = &$this->getInstaller($this->ui); + } + + if ($command == 'upgrade' || $command == 'upgrade-all') { + // If people run the upgrade command but pass nothing, emulate a upgrade-all + if ($command == 'upgrade' && empty($params)) { + return $this->doUpgradeAll($command, $options, $params); + } + $options['upgrade'] = true; + } else { + $packages = $params; + } + + $instreg = &$reg; // instreg used to check if package is installed + if (isset($options['packagingroot']) && !isset($options['upgrade'])) { + $packrootphp_dir = $this->installer->_prependPath( + $this->config->get('php_dir', null, 'pear.php.net'), + $options['packagingroot']); + $instreg = new PEAR_Registry($packrootphp_dir); // other instreg! + + if ($this->config->get('verbose') > 2) { + $this->ui->outputData('using package root: ' . $options['packagingroot']); + } + } + + $abstractpackages = $otherpackages = array(); + // parse params + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + + foreach ($params as $param) { + if (strpos($param, 'http://') === 0) { + $otherpackages[] = $param; + continue; + } + + if (strpos($param, 'channel://') === false && @file_exists($param)) { + if (isset($options['force'])) { + $otherpackages[] = $param; + continue; + } + + $pkg = new PEAR_PackageFile($this->config); + $pf = $pkg->fromAnyFile($param, PEAR_VALIDATE_DOWNLOADING); + if (PEAR::isError($pf)) { + $otherpackages[] = $param; + continue; + } + + $exists = $reg->packageExists($pf->getPackage(), $pf->getChannel()); + $pversion = $reg->packageInfo($pf->getPackage(), 'version', $pf->getChannel()); + $version_compare = version_compare($pf->getVersion(), $pversion, '<='); + if ($exists && $version_compare) { + if ($this->config->get('verbose')) { + $this->ui->outputData('Ignoring installed package ' . + $reg->parsedPackageNameToString( + array('package' => $pf->getPackage(), + 'channel' => $pf->getChannel()), true)); + } + continue; + } + $otherpackages[] = $param; + continue; + } + + $e = $reg->parsePackageName($param, $channel); + if (PEAR::isError($e)) { + $otherpackages[] = $param; + } else { + $abstractpackages[] = $e; + } + } + PEAR::staticPopErrorHandling(); + + // if there are any local package .tgz or remote static url, we can't + // filter. The filter only works for abstract packages + if (count($abstractpackages) && !isset($options['force'])) { + // when not being forced, only do necessary upgrades/installs + if (isset($options['upgrade'])) { + $abstractpackages = $this->_filterUptodatePackages($abstractpackages, $command); + } else { + $count = count($abstractpackages); + foreach ($abstractpackages as $i => $package) { + if (isset($package['group'])) { + // do not filter out install groups + continue; + } + + if ($instreg->packageExists($package['package'], $package['channel'])) { + if ($count > 1) { + if ($this->config->get('verbose')) { + $this->ui->outputData('Ignoring installed package ' . + $reg->parsedPackageNameToString($package, true)); + } + unset($abstractpackages[$i]); + } elseif ($count === 1) { + // Lets try to upgrade it since it's already installed + $options['upgrade'] = true; + } + } + } + } + $abstractpackages = + array_map(array($reg, 'parsedPackageNameToString'), $abstractpackages); + } elseif (count($abstractpackages)) { + $abstractpackages = + array_map(array($reg, 'parsedPackageNameToString'), $abstractpackages); + } + + $packages = array_merge($abstractpackages, $otherpackages); + if (!count($packages)) { + $c = ''; + if (isset($options['channel'])){ + $c .= ' in channel "' . $options['channel'] . '"'; + } + $this->ui->outputData('Nothing to ' . $command . $c); + return true; + } + + $this->downloader = &$this->getDownloader($this->ui, $options, $this->config); + $errors = $downloaded = $binaries = array(); + $downloaded = &$this->downloader->download($packages); + if (PEAR::isError($downloaded)) { + return $this->raiseError($downloaded); + } + + $errors = $this->downloader->getErrorMsgs(); + if (count($errors)) { + $err = array(); + $err['data'] = array(); + foreach ($errors as $error) { + if ($error !== null) { + $err['data'][] = array($error); + } + } + + if (!empty($err['data'])) { + $err['headline'] = 'Install Errors'; + $this->ui->outputData($err); + } + + if (!count($downloaded)) { + return $this->raiseError("$command failed"); + } + } + + $data = array( + 'headline' => 'Packages that would be Installed' + ); + + if (isset($options['pretend'])) { + foreach ($downloaded as $package) { + $data['data'][] = array($reg->parsedPackageNameToString($package->getParsedPackage())); + } + $this->ui->outputData($data, 'pretend'); + return true; + } + + $this->installer->setOptions($options); + $this->installer->sortPackagesForInstall($downloaded); + if (PEAR::isError($err = $this->installer->setDownloadedPackages($downloaded))) { + $this->raiseError($err->getMessage()); + return true; + } + + $binaries = $extrainfo = array(); + foreach ($downloaded as $param) { + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $info = $this->installer->install($param, $options); + PEAR::staticPopErrorHandling(); + if (PEAR::isError($info)) { + $oldinfo = $info; + $pkg = &$param->getPackageFile(); + if ($info->getCode() != PEAR_INSTALLER_NOBINARY) { + if (!($info = $pkg->installBinary($this->installer))) { + $this->ui->outputData('ERROR: ' .$oldinfo->getMessage()); + continue; + } + + // we just installed a different package than requested, + // let's change the param and info so that the rest of this works + $param = $info[0]; + $info = $info[1]; + } + } + + if (!is_array($info)) { + return $this->raiseError("$command failed"); + } + + if ($param->getPackageType() == 'extsrc' || + $param->getPackageType() == 'extbin' || + $param->getPackageType() == 'zendextsrc' || + $param->getPackageType() == 'zendextbin' + ) { + $pkg = &$param->getPackageFile(); + if ($instbin = $pkg->getInstalledBinary()) { + $instpkg = &$instreg->getPackage($instbin, $pkg->getChannel()); + } else { + $instpkg = &$instreg->getPackage($pkg->getPackage(), $pkg->getChannel()); + } + + foreach ($instpkg->getFilelist() as $name => $atts) { + $pinfo = pathinfo($atts['installed_as']); + if (!isset($pinfo['extension']) || + in_array($pinfo['extension'], array('c', 'h')) + ) { + continue; // make sure we don't match php_blah.h + } + + if ((strpos($pinfo['basename'], 'php_') === 0 && + $pinfo['extension'] == 'dll') || + // most unices + $pinfo['extension'] == 'so' || + // hp-ux + $pinfo['extension'] == 'sl') { + $binaries[] = array($atts['installed_as'], $pinfo); + break; + } + } + + if (count($binaries)) { + foreach ($binaries as $pinfo) { + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $ret = $this->enableExtension(array($pinfo[0]), $param->getPackageType()); + PEAR::staticPopErrorHandling(); + if (PEAR::isError($ret)) { + $extrainfo[] = $ret->getMessage(); + if ($param->getPackageType() == 'extsrc' || + $param->getPackageType() == 'extbin') { + $exttype = 'extension'; + } else { + ob_start(); + phpinfo(INFO_GENERAL); + $info = ob_get_contents(); + ob_end_clean(); + $debug = function_exists('leak') ? '_debug' : ''; + $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : ''; + $exttype = 'zend_extension' . $debug . $ts; + } + $extrainfo[] = 'You should add "' . $exttype . '=' . + $pinfo[1]['basename'] . '" to php.ini'; + } else { + $extrainfo[] = 'Extension ' . $instpkg->getProvidesExtension() . + ' enabled in php.ini'; + } + } + } + } + + if ($this->config->get('verbose') > 0) { + $chan = $param->getChannel(); + $label = $reg->parsedPackageNameToString( + array( + 'channel' => $chan, + 'package' => $param->getPackage(), + 'version' => $param->getVersion(), + )); + $out = array('data' => "$command ok: $label"); + if (isset($info['release_warnings'])) { + $out['release_warnings'] = $info['release_warnings']; + } + $this->ui->outputData($out, $command); + + if (!isset($options['register-only']) && !isset($options['offline'])) { + if ($this->config->isDefinedLayer('ftp')) { + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $info = $this->installer->ftpInstall($param); + PEAR::staticPopErrorHandling(); + if (PEAR::isError($info)) { + $this->ui->outputData($info->getMessage()); + $this->ui->outputData("remote install failed: $label"); + } else { + $this->ui->outputData("remote install ok: $label"); + } + } + } + } + + $deps = $param->getDeps(); + if ($deps) { + if (isset($deps['group'])) { + $groups = $deps['group']; + if (!isset($groups[0])) { + $groups = array($groups); + } + + foreach ($groups as $group) { + if ($group['attribs']['name'] == 'default') { + // default group is always installed, unless the user + // explicitly chooses to install another group + continue; + } + $extrainfo[] = $param->getPackage() . ': Optional feature ' . + $group['attribs']['name'] . ' available (' . + $group['attribs']['hint'] . ')'; + } + + $extrainfo[] = $param->getPackage() . + ': To install optional features use "pear install ' . + $reg->parsedPackageNameToString( + array('package' => $param->getPackage(), + 'channel' => $param->getChannel()), true) . + '#featurename"'; + } + } + + $pkg = &$instreg->getPackage($param->getPackage(), $param->getChannel()); + // $pkg may be NULL if install is a 'fake' install via --packagingroot + if (is_object($pkg)) { + $pkg->setConfig($this->config); + if ($list = $pkg->listPostinstallScripts()) { + $pn = $reg->parsedPackageNameToString(array('channel' => + $param->getChannel(), 'package' => $param->getPackage()), true); + $extrainfo[] = $pn . ' has post-install scripts:'; + foreach ($list as $file) { + $extrainfo[] = $file; + } + $extrainfo[] = $param->getPackage() . + ': Use "pear run-scripts ' . $pn . '" to finish setup.'; + $extrainfo[] = 'DO NOT RUN SCRIPTS FROM UNTRUSTED SOURCES'; + } + } + } + + if (count($extrainfo)) { + foreach ($extrainfo as $info) { + $this->ui->outputData($info); + } + } + + return true; + } + + // }}} + // {{{ doUpgradeAll() + + function doUpgradeAll($command, $options, $params) + { + $reg = &$this->config->getRegistry(); + $upgrade = array(); + + if (isset($options['channel'])) { + $channels = array($options['channel']); + } else { + $channels = $reg->listChannels(); + } + + foreach ($channels as $channel) { + if ($channel == '__uri') { + continue; + } + + // parse name with channel + foreach ($reg->listPackages($channel) as $name) { + $upgrade[] = $reg->parsedPackageNameToString(array( + 'channel' => $channel, + 'package' => $name + )); + } + } + + $err = $this->doInstall($command, $options, $upgrade); + if (PEAR::isError($err)) { + $this->ui->outputData($err->getMessage(), $command); + } + } + + // }}} + // {{{ doUninstall() + + function doUninstall($command, $options, $params) + { + if (count($params) < 1) { + return $this->raiseError("Please supply the package(s) you want to uninstall"); + } + + if (empty($this->installer)) { + $this->installer = &$this->getInstaller($this->ui); + } + + if (isset($options['remoteconfig'])) { + $e = $this->config->readFTPConfigFile($options['remoteconfig']); + if (!PEAR::isError($e)) { + $this->installer->setConfig($this->config); + } + } + + $reg = &$this->config->getRegistry(); + $newparams = array(); + $binaries = array(); + $badparams = array(); + foreach ($params as $pkg) { + $channel = $this->config->get('default_channel'); + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $parsed = $reg->parsePackageName($pkg, $channel); + PEAR::staticPopErrorHandling(); + if (!$parsed || PEAR::isError($parsed)) { + $badparams[] = $pkg; + continue; + } + $package = $parsed['package']; + $channel = $parsed['channel']; + $info = &$reg->getPackage($package, $channel); + if ($info === null && + ($channel == 'pear.php.net' || $channel == 'pecl.php.net')) { + // make sure this isn't a package that has flipped from pear to pecl but + // used a package.xml 1.0 + $testc = ($channel == 'pear.php.net') ? 'pecl.php.net' : 'pear.php.net'; + $info = &$reg->getPackage($package, $testc); + if ($info !== null) { + $channel = $testc; + } + } + if ($info === null) { + $badparams[] = $pkg; + } else { + $newparams[] = &$info; + // check for binary packages (this is an alias for those packages if so) + if ($installedbinary = $info->getInstalledBinary()) { + $this->ui->log('adding binary package ' . + $reg->parsedPackageNameToString(array('channel' => $channel, + 'package' => $installedbinary), true)); + $newparams[] = &$reg->getPackage($installedbinary, $channel); + } + // add the contents of a dependency group to the list of installed packages + if (isset($parsed['group'])) { + $group = $info->getDependencyGroup($parsed['group']); + if ($group) { + $installed = $reg->getInstalledGroup($group); + if ($installed) { + foreach ($installed as $i => $p) { + $newparams[] = &$installed[$i]; + } + } + } + } + } + } + $err = $this->installer->sortPackagesForUninstall($newparams); + if (PEAR::isError($err)) { + $this->ui->outputData($err->getMessage(), $command); + return true; + } + $params = $newparams; + // twist this to use it to check on whether dependent packages are also being uninstalled + // for circular dependencies like subpackages + $this->installer->setUninstallPackages($newparams); + $params = array_merge($params, $badparams); + $binaries = array(); + foreach ($params as $pkg) { + $this->installer->pushErrorHandling(PEAR_ERROR_RETURN); + if ($err = $this->installer->uninstall($pkg, $options)) { + $this->installer->popErrorHandling(); + if (PEAR::isError($err)) { + $this->ui->outputData($err->getMessage(), $command); + continue; + } + if ($pkg->getPackageType() == 'extsrc' || + $pkg->getPackageType() == 'extbin' || + $pkg->getPackageType() == 'zendextsrc' || + $pkg->getPackageType() == 'zendextbin') { + if ($instbin = $pkg->getInstalledBinary()) { + continue; // this will be uninstalled later + } + + foreach ($pkg->getFilelist() as $name => $atts) { + $pinfo = pathinfo($atts['installed_as']); + if (!isset($pinfo['extension']) || + in_array($pinfo['extension'], array('c', 'h'))) { + continue; // make sure we don't match php_blah.h + } + if ((strpos($pinfo['basename'], 'php_') === 0 && + $pinfo['extension'] == 'dll') || + // most unices + $pinfo['extension'] == 'so' || + // hp-ux + $pinfo['extension'] == 'sl') { + $binaries[] = array($atts['installed_as'], $pinfo); + break; + } + } + if (count($binaries)) { + foreach ($binaries as $pinfo) { + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $ret = $this->disableExtension(array($pinfo[0]), $pkg->getPackageType()); + PEAR::staticPopErrorHandling(); + if (PEAR::isError($ret)) { + $extrainfo[] = $ret->getMessage(); + if ($pkg->getPackageType() == 'extsrc' || + $pkg->getPackageType() == 'extbin') { + $exttype = 'extension'; + } else { + ob_start(); + phpinfo(INFO_GENERAL); + $info = ob_get_contents(); + ob_end_clean(); + $debug = function_exists('leak') ? '_debug' : ''; + $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : ''; + $exttype = 'zend_extension' . $debug . $ts; + } + $this->ui->outputData('Unable to remove "' . $exttype . '=' . + $pinfo[1]['basename'] . '" from php.ini', $command); + } else { + $this->ui->outputData('Extension ' . $pkg->getProvidesExtension() . + ' disabled in php.ini', $command); + } + } + } + } + $savepkg = $pkg; + if ($this->config->get('verbose') > 0) { + if (is_object($pkg)) { + $pkg = $reg->parsedPackageNameToString($pkg); + } + $this->ui->outputData("uninstall ok: $pkg", $command); + } + if (!isset($options['offline']) && is_object($savepkg) && + defined('PEAR_REMOTEINSTALL_OK')) { + if ($this->config->isDefinedLayer('ftp')) { + $this->installer->pushErrorHandling(PEAR_ERROR_RETURN); + $info = $this->installer->ftpUninstall($savepkg); + $this->installer->popErrorHandling(); + if (PEAR::isError($info)) { + $this->ui->outputData($info->getMessage()); + $this->ui->outputData("remote uninstall failed: $pkg"); + } else { + $this->ui->outputData("remote uninstall ok: $pkg"); + } + } + } + } else { + $this->installer->popErrorHandling(); + if (!is_object($pkg)) { + return $this->raiseError("uninstall failed: $pkg"); + } + $pkg = $reg->parsedPackageNameToString($pkg); + } + } + + return true; + } + + // }}} + + + // }}} + // {{{ doBundle() + /* + (cox) It just downloads and untars the package, does not do + any check that the PEAR_Installer::_installFile() does. + */ + + function doBundle($command, $options, $params) + { + $opts = array( + 'force' => true, + 'nodeps' => true, + 'soft' => true, + 'downloadonly' => true + ); + $downloader = &$this->getDownloader($this->ui, $opts, $this->config); + $reg = &$this->config->getRegistry(); + if (count($params) < 1) { + return $this->raiseError("Please supply the package you want to bundle"); + } + + if (isset($options['destination'])) { + if (!is_dir($options['destination'])) { + System::mkdir('-p ' . $options['destination']); + } + $dest = realpath($options['destination']); + } else { + $pwd = getcwd(); + $dir = $pwd . DIRECTORY_SEPARATOR . 'ext'; + $dest = is_dir($dir) ? $dir : $pwd; + } + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $err = $downloader->setDownloadDir($dest); + PEAR::staticPopErrorHandling(); + if (PEAR::isError($err)) { + return PEAR::raiseError('download directory "' . $dest . + '" is not writeable.'); + } + $result = &$downloader->download(array($params[0])); + if (PEAR::isError($result)) { + return $result; + } + if (!isset($result[0])) { + return $this->raiseError('unable to unpack ' . $params[0]); + } + $pkgfile = &$result[0]->getPackageFile(); + $pkgname = $pkgfile->getName(); + $pkgversion = $pkgfile->getVersion(); + + // Unpacking ------------------------------------------------- + $dest .= DIRECTORY_SEPARATOR . $pkgname; + $orig = $pkgname . '-' . $pkgversion; + + $tar = &new Archive_Tar($pkgfile->getArchiveFile()); + if (!$tar->extractModify($dest, $orig)) { + return $this->raiseError('unable to unpack ' . $pkgfile->getArchiveFile()); + } + $this->ui->outputData("Package ready at '$dest'"); + // }}} + } + + // }}} + + function doRunScripts($command, $options, $params) + { + if (!isset($params[0])) { + return $this->raiseError('run-scripts expects 1 parameter: a package name'); + } + + $reg = &$this->config->getRegistry(); + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $parsed = $reg->parsePackageName($params[0], $this->config->get('default_channel')); + PEAR::staticPopErrorHandling(); + if (PEAR::isError($parsed)) { + return $this->raiseError($parsed); + } + + $package = &$reg->getPackage($parsed['package'], $parsed['channel']); + if (!is_object($package)) { + return $this->raiseError('Could not retrieve package "' . $params[0] . '" from registry'); + } + + $package->setConfig($this->config); + $package->runPostinstallScripts(); + $this->ui->outputData('Install scripts complete', $command); + return true; + } + + /** + * Given a list of packages, filter out those ones that are already up to date + * + * @param $packages: packages, in parsed array format ! + * @return list of packages that can be upgraded + */ + function _filterUptodatePackages($packages, $command) + { + $reg = &$this->config->getRegistry(); + $latestReleases = array(); + + $ret = array(); + foreach ($packages as $package) { + if (isset($package['group'])) { + $ret[] = $package; + continue; + } + + $channel = $package['channel']; + $name = $package['package']; + if (!$reg->packageExists($name, $channel)) { + $ret[] = $package; + continue; + } + + if (!isset($latestReleases[$channel])) { + // fill in cache for this channel + $chan = &$reg->getChannel($channel); + if (PEAR::isError($chan)) { + return $this->raiseError($chan); + } + + $base2 = false; + $preferred_mirror = $this->config->get('preferred_mirror', null, $channel); + if ($chan->supportsREST($preferred_mirror) && + ( + //($base2 = $chan->getBaseURL('REST1.4', $preferred_mirror)) || + ($base = $chan->getBaseURL('REST1.0', $preferred_mirror)) + ) + ) { + $dorest = true; + } + + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + if (!isset($package['state'])) { + $state = $this->config->get('preferred_state', null, $channel); + } else { + $state = $package['state']; + } + + if ($dorest) { + if ($base2) { + $rest = &$this->config->getREST('1.4', array()); + $base = $base2; + } else { + $rest = &$this->config->getREST('1.0', array()); + } + + $installed = array_flip($reg->listPackages($channel)); + $latest = $rest->listLatestUpgrades($base, $state, $installed, $channel, $reg); + } + + PEAR::staticPopErrorHandling(); + if (PEAR::isError($latest)) { + $this->ui->outputData('Error getting channel info from ' . $channel . + ': ' . $latest->getMessage()); + continue; + } + + $latestReleases[$channel] = array_change_key_case($latest); + } + + // check package for latest release + $name_lower = strtolower($name); + if (isset($latestReleases[$channel][$name_lower])) { + // if not set, up to date + $inst_version = $reg->packageInfo($name, 'version', $channel); + $channel_version = $latestReleases[$channel][$name_lower]['version']; + if (version_compare($channel_version, $inst_version, 'le')) { + // installed version is up-to-date + continue; + } + + // maintain BC + if ($command == 'upgrade-all') { + $this->ui->outputData(array('data' => 'Will upgrade ' . + $reg->parsedPackageNameToString($package)), $command); + } + $ret[] = $package; + } + } + + return $ret; + } +} \ No newline at end of file diff --git a/includes/pear/PEAR/Command/Install.xml b/includes/pear/PEAR/Command/Install.xml new file mode 100644 index 0000000..1b1e933 --- /dev/null +++ b/includes/pear/PEAR/Command/Install.xml @@ -0,0 +1,276 @@ + + + Install Package + doInstall + i + + + f + will overwrite newer installed packages + + + l + do not check for recommended dependency version + + + n + ignore dependencies, install anyway + + + r + do not install files, only register the package as installed + + + s + soft install, fail silently, or upgrade if already installed + + + B + don't build C extensions + + + Z + request uncompressed files when downloading + + + R + root directory used when installing files (ala PHP's INSTALL_ROOT), use packagingroot for RPM + DIR + + + P + root directory used when packaging files, like RPM packaging + DIR + + + + force install even if there were errors + + + a + install all required and optional dependencies + + + o + install all required dependencies + + + O + do not attempt to download any urls or contact channels + + + p + Only list the packages that would be downloaded + + + [channel/]<package> ... +Installs one or more PEAR packages. You can specify a package to +install in four ways: + +"Package-1.0.tgz" : installs from a local file + +"http://example.com/Package-1.0.tgz" : installs from +anywhere on the net. + +"package.xml" : installs the package described in +package.xml. Useful for testing, or for wrapping a PEAR package in +another package manager such as RPM. + +"Package[-version/state][.tar]" : queries your default channel's server +({config master_server}) and downloads the newest package with +the preferred quality/state ({config preferred_state}). + +To retrieve Package version 1.1, use "Package-1.1," to retrieve +Package state beta, use "Package-beta." To retrieve an uncompressed +file, append .tar (make sure there is no file by the same name first) + +To download a package from another channel, prefix with the channel name like +"channel/Package" + +More than one package may be specified at once. It is ok to mix these +four ways of specifying packages. + + + + Upgrade Package + doInstall + up + + + c + upgrade packages from a specific channel + CHAN + + + f + overwrite newer installed packages + + + l + do not check for recommended dependency version + + + n + ignore dependencies, upgrade anyway + + + r + do not install files, only register the package as upgraded + + + B + don't build C extensions + + + Z + request uncompressed files when downloading + + + R + root directory used when installing files (ala PHP's INSTALL_ROOT) + DIR + + + + force install even if there were errors + + + a + install all required and optional dependencies + + + o + install all required dependencies + + + O + do not attempt to download any urls or contact channels + + + p + Only list the packages that would be downloaded + + + <package> ... +Upgrades one or more PEAR packages. See documentation for the +"install" command for ways to specify a package. + +When upgrading, your package will be updated if the provided new +package has a higher version number (use the -f option if you need to +upgrade anyway). + +More than one package may be specified at once. + + + + Upgrade All Packages [Deprecated in favor of calling upgrade with no parameters] + doUpgradeAll + ua + + + c + upgrade packages from a specific channel + CHAN + + + n + ignore dependencies, upgrade anyway + + + r + do not install files, only register the package as upgraded + + + B + don't build C extensions + + + Z + request uncompressed files when downloading + + + R + root directory used when installing files (ala PHP's INSTALL_ROOT), use packagingroot for RPM + DIR + + + + force install even if there were errors + + + + do not check for recommended dependency version + + + +WARNING: This function is deprecated in favor of using the upgrade command with no params + +Upgrades all packages that have a newer release available. Upgrades are +done only if there is a release available of the state specified in +"preferred_state" (currently {config preferred_state}), or a state considered +more stable. + + + + Un-install Package + doUninstall + un + + + n + ignore dependencies, uninstall anyway + + + r + do not remove files, only register the packages as not installed + + + R + root directory used when installing files (ala PHP's INSTALL_ROOT) + DIR + + + + force install even if there were errors + + + O + do not attempt to uninstall remotely + + + [channel/]<package> ... +Uninstalls one or more PEAR packages. More than one package may be +specified at once. Prefix with channel name to uninstall from a +channel not in your default channel ({config default_channel}) + + + + Unpacks a Pecl Package + doBundle + bun + + + d + Optional destination directory for unpacking (defaults to current path or "ext" if exists) + DIR + + + f + Force the unpacking even if there were errors in the package + + + <package> +Unpacks a Pecl Package into the selected location. It will download the +package if needed. + + + + Run Post-Install Scripts bundled with a package + doRunScripts + rs + + <package> +Run post-installation scripts in package <package>, if any exist. + + + \ No newline at end of file diff --git a/includes/pear/PEAR/Command/Mirror.php b/includes/pear/PEAR/Command/Mirror.php new file mode 100644 index 0000000..4f646b2 --- /dev/null +++ b/includes/pear/PEAR/Command/Mirror.php @@ -0,0 +1,139 @@ + + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.2.0 + */ + +/** + * base class + */ +require_once 'PEAR/Command/Common.php'; + +/** + * PEAR commands for providing file mirrors + * + * @category pear + * @package PEAR + * @author Alexander Merz + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.2.0 + */ +class PEAR_Command_Mirror extends PEAR_Command_Common +{ + var $commands = array( + 'download-all' => array( + 'summary' => 'Downloads each available package from the default channel', + 'function' => 'doDownloadAll', + 'shortcut' => 'da', + 'options' => array( + 'channel' => + array( + 'shortopt' => 'c', + 'doc' => 'specify a channel other than the default channel', + 'arg' => 'CHAN', + ), + ), + 'doc' => ' +Requests a list of available packages from the default channel ({config default_channel}) +and downloads them to current working directory. Note: only +packages within preferred_state ({config preferred_state}) will be downloaded' + ), + ); + + /** + * PEAR_Command_Mirror constructor. + * + * @access public + * @param object PEAR_Frontend a reference to an frontend + * @param object PEAR_Config a reference to the configuration data + */ + function PEAR_Command_Mirror(&$ui, &$config) + { + parent::PEAR_Command_Common($ui, $config); + } + + /** + * For unit-testing + */ + function &factory($a) + { + $a = &PEAR_Command::factory($a, $this->config); + return $a; + } + + /** + * retrieves a list of avaible Packages from master server + * and downloads them + * + * @access public + * @param string $command the command + * @param array $options the command options before the command + * @param array $params the stuff after the command name + * @return bool true if succesful + * @throw PEAR_Error + */ + function doDownloadAll($command, $options, $params) + { + $savechannel = $this->config->get('default_channel'); + $reg = &$this->config->getRegistry(); + $channel = isset($options['channel']) ? $options['channel'] : + $this->config->get('default_channel'); + if (!$reg->channelExists($channel)) { + $this->config->set('default_channel', $savechannel); + return $this->raiseError('Channel "' . $channel . '" does not exist'); + } + $this->config->set('default_channel', $channel); + + $this->ui->outputData('Using Channel ' . $this->config->get('default_channel')); + $chan = $reg->getChannel($channel); + if (PEAR::isError($chan)) { + return $this->raiseError($chan); + } + + if ($chan->supportsREST($this->config->get('preferred_mirror')) && + $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) { + $rest = &$this->config->getREST('1.0', array()); + $remoteInfo = array_flip($rest->listPackages($base, $channel)); + } + + if (PEAR::isError($remoteInfo)) { + return $remoteInfo; + } + + $cmd = &$this->factory("download"); + if (PEAR::isError($cmd)) { + return $cmd; + } + + $this->ui->outputData('Using Preferred State of ' . + $this->config->get('preferred_state')); + $this->ui->outputData('Gathering release information, please wait...'); + + /** + * Error handling not necessary, because already done by + * the download command + */ + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $err = $cmd->run('download', array('downloadonly' => true), array_keys($remoteInfo)); + PEAR::staticPopErrorHandling(); + $this->config->set('default_channel', $savechannel); + if (PEAR::isError($err)) { + $this->ui->outputData($err->getMessage()); + } + + return true; + } +} \ No newline at end of file diff --git a/includes/pear/PEAR/Command/Mirror.xml b/includes/pear/PEAR/Command/Mirror.xml new file mode 100644 index 0000000..fe8be9d --- /dev/null +++ b/includes/pear/PEAR/Command/Mirror.xml @@ -0,0 +1,18 @@ + + + Downloads each available package from the default channel + doDownloadAll + da + + + c + specify a channel other than the default channel + CHAN + + + +Requests a list of available packages from the default channel ({config default_channel}) +and downloads them to current working directory. Note: only +packages within preferred_state ({config preferred_state}) will be downloaded + + \ No newline at end of file diff --git a/includes/pear/PEAR/Command/Package.php b/includes/pear/PEAR/Command/Package.php new file mode 100644 index 0000000..44c647b --- /dev/null +++ b/includes/pear/PEAR/Command/Package.php @@ -0,0 +1,1124 @@ + + * @author Martin Jansen + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 0.1 + */ + +/** + * base class + */ +require_once 'PEAR/Command/Common.php'; + +/** + * PEAR commands for login/logout + * + * @category pear + * @package PEAR + * @author Stig Bakken + * @author Martin Jansen + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 0.1 + */ + +class PEAR_Command_Package extends PEAR_Command_Common +{ + var $commands = array( + 'package' => array( + 'summary' => 'Build Package', + 'function' => 'doPackage', + 'shortcut' => 'p', + 'options' => array( + 'nocompress' => array( + 'shortopt' => 'Z', + 'doc' => 'Do not gzip the package file' + ), + 'showname' => array( + 'shortopt' => 'n', + 'doc' => 'Print the name of the packaged file.', + ), + ), + 'doc' => '[descfile] [descfile2] +Creates a PEAR package from its description file (usually called +package.xml). If a second packagefile is passed in, then +the packager will check to make sure that one is a package.xml +version 1.0, and the other is a package.xml version 2.0. The +package.xml version 1.0 will be saved as "package.xml" in the archive, +and the other as "package2.xml" in the archive" +' + ), + 'package-validate' => array( + 'summary' => 'Validate Package Consistency', + 'function' => 'doPackageValidate', + 'shortcut' => 'pv', + 'options' => array(), + 'doc' => ' +', + ), + 'cvsdiff' => array( + 'summary' => 'Run a "cvs diff" for all files in a package', + 'function' => 'doCvsDiff', + 'shortcut' => 'cd', + 'options' => array( + 'quiet' => array( + 'shortopt' => 'q', + 'doc' => 'Be quiet', + ), + 'reallyquiet' => array( + 'shortopt' => 'Q', + 'doc' => 'Be really quiet', + ), + 'date' => array( + 'shortopt' => 'D', + 'doc' => 'Diff against revision of DATE', + 'arg' => 'DATE', + ), + 'release' => array( + 'shortopt' => 'R', + 'doc' => 'Diff against tag for package release REL', + 'arg' => 'REL', + ), + 'revision' => array( + 'shortopt' => 'r', + 'doc' => 'Diff against revision REV', + 'arg' => 'REV', + ), + 'context' => array( + 'shortopt' => 'c', + 'doc' => 'Generate context diff', + ), + 'unified' => array( + 'shortopt' => 'u', + 'doc' => 'Generate unified diff', + ), + 'ignore-case' => array( + 'shortopt' => 'i', + 'doc' => 'Ignore case, consider upper- and lower-case letters equivalent', + ), + 'ignore-whitespace' => array( + 'shortopt' => 'b', + 'doc' => 'Ignore changes in amount of white space', + ), + 'ignore-blank-lines' => array( + 'shortopt' => 'B', + 'doc' => 'Ignore changes that insert or delete blank lines', + ), + 'brief' => array( + 'doc' => 'Report only whether the files differ, no details', + ), + 'dry-run' => array( + 'shortopt' => 'n', + 'doc' => 'Don\'t do anything, just pretend', + ), + ), + 'doc' => ' +Compares all the files in a package. Without any options, this +command will compare the current code with the last checked-in code. +Using the -r or -R option you may compare the current code with that +of a specific release. +', + ), + 'svntag' => array( + 'summary' => 'Set SVN Release Tag', + 'function' => 'doSvnTag', + 'shortcut' => 'sv', + 'options' => array( + 'quiet' => array( + 'shortopt' => 'q', + 'doc' => 'Be quiet', + ), + 'slide' => array( + 'shortopt' => 'F', + 'doc' => 'Move (slide) tag if it exists', + ), + 'delete' => array( + 'shortopt' => 'd', + 'doc' => 'Remove tag', + ), + 'dry-run' => array( + 'shortopt' => 'n', + 'doc' => 'Don\'t do anything, just pretend', + ), + ), + 'doc' => ' [files...] + Sets a SVN tag on all files in a package. Use this command after you have + packaged a distribution tarball with the "package" command to tag what + revisions of what files were in that release. If need to fix something + after running svntag once, but before the tarball is released to the public, + use the "slide" option to move the release tag. + + to include files (such as a second package.xml, or tests not included in the + release), pass them as additional parameters. + ', + ), + 'cvstag' => array( + 'summary' => 'Set CVS Release Tag', + 'function' => 'doCvsTag', + 'shortcut' => 'ct', + 'options' => array( + 'quiet' => array( + 'shortopt' => 'q', + 'doc' => 'Be quiet', + ), + 'reallyquiet' => array( + 'shortopt' => 'Q', + 'doc' => 'Be really quiet', + ), + 'slide' => array( + 'shortopt' => 'F', + 'doc' => 'Move (slide) tag if it exists', + ), + 'delete' => array( + 'shortopt' => 'd', + 'doc' => 'Remove tag', + ), + 'dry-run' => array( + 'shortopt' => 'n', + 'doc' => 'Don\'t do anything, just pretend', + ), + ), + 'doc' => ' [files...] +Sets a CVS tag on all files in a package. Use this command after you have +packaged a distribution tarball with the "package" command to tag what +revisions of what files were in that release. If need to fix something +after running cvstag once, but before the tarball is released to the public, +use the "slide" option to move the release tag. + +to include files (such as a second package.xml, or tests not included in the +release), pass them as additional parameters. +', + ), + 'package-dependencies' => array( + 'summary' => 'Show package dependencies', + 'function' => 'doPackageDependencies', + 'shortcut' => 'pd', + 'options' => array(), + 'doc' => ' or or +List all dependencies the package has. +Can take a tgz / tar file, package.xml or a package name of an installed package.' + ), + 'sign' => array( + 'summary' => 'Sign a package distribution file', + 'function' => 'doSign', + 'shortcut' => 'si', + 'options' => array( + 'verbose' => array( + 'shortopt' => 'v', + 'doc' => 'Display GnuPG output', + ), + ), + 'doc' => ' +Signs a package distribution (.tar or .tgz) file with GnuPG.', + ), + 'makerpm' => array( + 'summary' => 'Builds an RPM spec file from a PEAR package', + 'function' => 'doMakeRPM', + 'shortcut' => 'rpm', + 'options' => array( + 'spec-template' => array( + 'shortopt' => 't', + 'arg' => 'FILE', + 'doc' => 'Use FILE as RPM spec file template' + ), + 'rpm-pkgname' => array( + 'shortopt' => 'p', + 'arg' => 'FORMAT', + 'doc' => 'Use FORMAT as format string for RPM package name, %s is replaced +by the PEAR package name, defaults to "PEAR::%s".', + ), + ), + 'doc' => ' + +Creates an RPM .spec file for wrapping a PEAR package inside an RPM +package. Intended to be used from the SPECS directory, with the PEAR +package tarball in the SOURCES directory: + +$ pear makerpm ../SOURCES/Net_Socket-1.0.tgz +Wrote RPM spec file PEAR::Net_Geo-1.0.spec +$ rpm -bb PEAR::Net_Socket-1.0.spec +... +Wrote: /usr/src/redhat/RPMS/i386/PEAR::Net_Socket-1.0-1.i386.rpm +', + ), + 'convert' => array( + 'summary' => 'Convert a package.xml 1.0 to package.xml 2.0 format', + 'function' => 'doConvert', + 'shortcut' => 'c2', + 'options' => array( + 'flat' => array( + 'shortopt' => 'f', + 'doc' => 'do not beautify the filelist.', + ), + ), + 'doc' => '[descfile] [descfile2] +Converts a package.xml in 1.0 format into a package.xml +in 2.0 format. The new file will be named package2.xml by default, +and package.xml will be used as the old file by default. +This is not the most intelligent conversion, and should only be +used for automated conversion or learning the format. +' + ), + ); + + var $output; + + /** + * PEAR_Command_Package constructor. + * + * @access public + */ + function PEAR_Command_Package(&$ui, &$config) + { + parent::PEAR_Command_Common($ui, $config); + } + + function _displayValidationResults($err, $warn, $strict = false) + { + foreach ($err as $e) { + $this->output .= "Error: $e\n"; + } + foreach ($warn as $w) { + $this->output .= "Warning: $w\n"; + } + $this->output .= sprintf('Validation: %d error(s), %d warning(s)'."\n", + sizeof($err), sizeof($warn)); + if ($strict && count($err) > 0) { + $this->output .= "Fix these errors and try again."; + return false; + } + return true; + } + + function &getPackager() + { + if (!class_exists('PEAR_Packager')) { + require_once 'PEAR/Packager.php'; + } + $a = &new PEAR_Packager; + return $a; + } + + function &getPackageFile($config, $debug = false) + { + if (!class_exists('PEAR_Common')) { + require_once 'PEAR/Common.php'; + } + if (!class_exists('PEAR_PackageFile')) { + require_once 'PEAR/PackageFile.php'; + } + $a = &new PEAR_PackageFile($config, $debug); + $common = new PEAR_Common; + $common->ui = $this->ui; + $a->setLogger($common); + return $a; + } + + function doPackage($command, $options, $params) + { + $this->output = ''; + $pkginfofile = isset($params[0]) ? $params[0] : 'package.xml'; + $pkg2 = isset($params[1]) ? $params[1] : null; + if (!$pkg2 && !isset($params[0]) && file_exists('package2.xml')) { + $pkg2 = 'package2.xml'; + } + + $packager = &$this->getPackager(); + $compress = empty($options['nocompress']) ? true : false; + $result = $packager->package($pkginfofile, $compress, $pkg2); + if (PEAR::isError($result)) { + return $this->raiseError($result); + } + + // Don't want output, only the package file name just created + if (isset($options['showname'])) { + $this->output = $result; + } + + if ($this->output) { + $this->ui->outputData($this->output, $command); + } + + return true; + } + + function doPackageValidate($command, $options, $params) + { + $this->output = ''; + if (count($params) < 1) { + $params[0] = 'package.xml'; + } + + $obj = &$this->getPackageFile($this->config, $this->_debug); + $obj->rawReturn(); + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $info = $obj->fromTgzFile($params[0], PEAR_VALIDATE_NORMAL); + if (PEAR::isError($info)) { + $info = $obj->fromPackageFile($params[0], PEAR_VALIDATE_NORMAL); + } else { + $archive = $info->getArchiveFile(); + $tar = &new Archive_Tar($archive); + $tar->extract(dirname($info->getPackageFile())); + $info->setPackageFile(dirname($info->getPackageFile()) . DIRECTORY_SEPARATOR . + $info->getPackage() . '-' . $info->getVersion() . DIRECTORY_SEPARATOR . + basename($info->getPackageFile())); + } + + PEAR::staticPopErrorHandling(); + if (PEAR::isError($info)) { + return $this->raiseError($info); + } + + $valid = false; + if ($info->getPackagexmlVersion() == '2.0') { + if ($valid = $info->validate(PEAR_VALIDATE_NORMAL)) { + $info->flattenFileList(); + $valid = $info->validate(PEAR_VALIDATE_PACKAGING); + } + } else { + $valid = $info->validate(PEAR_VALIDATE_PACKAGING); + } + + $err = $warn = array(); + if ($errors = $info->getValidationWarnings()) { + foreach ($errors as $error) { + if ($error['level'] == 'warning') { + $warn[] = $error['message']; + } else { + $err[] = $error['message']; + } + } + } + + $this->_displayValidationResults($err, $warn); + $this->ui->outputData($this->output, $command); + return true; + } + + function doSvnTag($command, $options, $params) + { + $this->output = ''; + $_cmd = $command; + if (count($params) < 1) { + $help = $this->getHelp($command); + return $this->raiseError("$command: missing parameter: $help[0]"); + } + + $packageFile = realpath($params[0]); + $dir = dirname($packageFile); + $dir = substr($dir, strrpos($dir, DIRECTORY_SEPARATOR) + 1); + $obj = &$this->getPackageFile($this->config, $this->_debug); + $info = $obj->fromAnyFile($packageFile, PEAR_VALIDATE_NORMAL); + if (PEAR::isError($info)) { + return $this->raiseError($info); + } + + $err = $warn = array(); + if (!$info->validate()) { + foreach ($info->getValidationWarnings() as $error) { + if ($error['level'] == 'warning') { + $warn[] = $error['message']; + } else { + $err[] = $error['message']; + } + } + } + + if (!$this->_displayValidationResults($err, $warn, true)) { + $this->ui->outputData($this->output, $command); + return $this->raiseError('SVN tag failed'); + } + + $version = $info->getVersion(); + $package = $info->getName(); + $svntag = "$package-$version"; + + if (isset($options['delete'])) { + return $this->_svnRemoveTag($version, $package, $svntag, $packageFile, $options); + } + + $path = $this->_svnFindPath($packageFile); + + // Check if there are any modified files + $fp = popen('svn st --xml ' . dirname($packageFile), "r"); + $out = ''; + while ($line = fgets($fp, 1024)) { + $out .= rtrim($line)."\n"; + } + pclose($fp); + + if (!isset($options['quiet']) && strpos($out, 'item="modified"')) { + $params = array(array( + 'name' => 'modified', + 'type' => 'yesno', + 'default' => 'no', + 'prompt' => 'You have files in your SVN checkout (' . $path['from'] . ') that have been modified but not commited, do you still want to tag ' . $version . '?', + )); + $answers = $this->ui->confirmDialog($params); + + if (!in_array($answers['modified'], array('y', 'yes', 'on', '1'))) { + return true; + } + } + + if (isset($options['slide'])) { + $this->_svnRemoveTag($version, $package, $svntag, $packageFile, $options); + } + + // Check if tag already exists + $releaseTag = $path['local']['base'] . 'tags' . DIRECTORY_SEPARATOR . $svntag; + $existsCommand = 'svn ls ' . $path['base'] . 'tags/'; + + $fp = popen($existsCommand, "r"); + $out = ''; + while ($line = fgets($fp, 1024)) { + $out .= rtrim($line)."\n"; + } + pclose($fp); + + if (in_array($svntag . DIRECTORY_SEPARATOR, explode("\n", $out))) { + $this->ui->outputData($this->output, $command); + return $this->raiseError('SVN tag ' . $svntag . ' for ' . $package . ' already exists.'); + } elseif (file_exists($path['local']['base'] . 'tags') === false) { + return $this->raiseError('Can not locate the tags directory at ' . $path['local']['base'] . 'tags'); + } elseif (is_writeable($path['local']['base'] . 'tags') === false) { + return $this->raiseError('Can not write to the tag directory at ' . $path['local']['base'] . 'tags'); + } else { + $makeCommand = 'svn mkdir ' . $releaseTag; + $this->output .= "+ $makeCommand\n"; + if (empty($options['dry-run'])) { + // We need to create the tag dir. + $fp = popen($makeCommand, "r"); + $out = ''; + while ($line = fgets($fp, 1024)) { + $out .= rtrim($line)."\n"; + } + pclose($fp); + $this->output .= "$out\n"; + } + } + + $command = 'svn'; + if (isset($options['quiet'])) { + $command .= ' -q'; + } + + $command .= ' copy --parents '; + + $dir = dirname($packageFile); + $dir = substr($dir, strrpos($dir, DIRECTORY_SEPARATOR) + 1); + $files = array_keys($info->getFilelist()); + if (!in_array(basename($packageFile), $files)) { + $files[] = basename($packageFile); + } + + array_shift($params); + if (count($params)) { + // add in additional files to be tagged (package files and such) + $files = array_merge($files, $params); + } + + $commands = array(); + foreach ($files as $file) { + if (!file_exists($file)) { + $file = $dir . DIRECTORY_SEPARATOR . $file; + } + $commands[] = $command . ' ' . escapeshellarg($file) . ' ' . + escapeshellarg($releaseTag . DIRECTORY_SEPARATOR . $file); + } + + $this->output .= implode("\n", $commands) . "\n"; + if (empty($options['dry-run'])) { + foreach ($commands as $command) { + $fp = popen($command, "r"); + while ($line = fgets($fp, 1024)) { + $this->output .= rtrim($line)."\n"; + } + pclose($fp); + } + } + + $command = 'svn ci -m "Tagging the ' . $version . ' release" ' . $releaseTag . "\n"; + $this->output .= "+ $command\n"; + if (empty($options['dry-run'])) { + $fp = popen($command, "r"); + while ($line = fgets($fp, 1024)) { + $this->output .= rtrim($line)."\n"; + } + pclose($fp); + } + + $this->ui->outputData($this->output, $_cmd); + return true; + } + + function _svnFindPath($file) + { + $xml = ''; + $command = "svn info --xml $file"; + $fp = popen($command, "r"); + while ($line = fgets($fp, 1024)) { + $xml .= rtrim($line)."\n"; + } + pclose($fp); + $url_tag = strpos($xml, ''); + $url = substr($xml, $url_tag + 5, strpos($xml, '', $url_tag + 5) - ($url_tag + 5)); + + $path = array(); + $path['from'] = substr($url, 0, strrpos($url, '/')); + $path['base'] = substr($path['from'], 0, strrpos($path['from'], '/') + 1); + + // Figure out the local paths - see http://pear.php.net/bugs/17463 + $pos = strpos($file, DIRECTORY_SEPARATOR . 'trunk' . DIRECTORY_SEPARATOR); + if ($pos === false) { + $pos = strpos($file, DIRECTORY_SEPARATOR . 'branches' . DIRECTORY_SEPARATOR); + } + $path['local']['base'] = substr($file, 0, $pos + 1); + + return $path; + } + + function _svnRemoveTag($version, $package, $tag, $packageFile, $options) + { + $command = 'svn'; + + if (isset($options['quiet'])) { + $command .= ' -q'; + } + + $command .= ' remove'; + $command .= ' -m "Removing tag for the ' . $version . ' release."'; + + $path = $this->_svnFindPath($packageFile); + $command .= ' ' . $path['base'] . 'tags/' . $tag; + + + if ($this->config->get('verbose') > 1) { + $this->output .= "+ $command\n"; + } + + $this->output .= "+ $command\n"; + if (empty($options['dry-run'])) { + $fp = popen($command, "r"); + while ($line = fgets($fp, 1024)) { + $this->output .= rtrim($line)."\n"; + } + pclose($fp); + } + + $this->ui->outputData($this->output, $command); + return true; + } + + function doCvsTag($command, $options, $params) + { + $this->output = ''; + $_cmd = $command; + if (count($params) < 1) { + $help = $this->getHelp($command); + return $this->raiseError("$command: missing parameter: $help[0]"); + } + + $packageFile = realpath($params[0]); + $obj = &$this->getPackageFile($this->config, $this->_debug); + $info = $obj->fromAnyFile($packageFile, PEAR_VALIDATE_NORMAL); + if (PEAR::isError($info)) { + return $this->raiseError($info); + } + + $err = $warn = array(); + if (!$info->validate()) { + foreach ($info->getValidationWarnings() as $error) { + if ($error['level'] == 'warning') { + $warn[] = $error['message']; + } else { + $err[] = $error['message']; + } + } + } + + if (!$this->_displayValidationResults($err, $warn, true)) { + $this->ui->outputData($this->output, $command); + return $this->raiseError('CVS tag failed'); + } + + $version = $info->getVersion(); + $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $version); + $cvstag = "RELEASE_$cvsversion"; + $files = array_keys($info->getFilelist()); + $command = 'cvs'; + if (isset($options['quiet'])) { + $command .= ' -q'; + } + + if (isset($options['reallyquiet'])) { + $command .= ' -Q'; + } + + $command .= ' tag'; + if (isset($options['slide'])) { + $command .= ' -F'; + } + + if (isset($options['delete'])) { + $command .= ' -d'; + } + + $command .= ' ' . $cvstag . ' ' . escapeshellarg($params[0]); + array_shift($params); + if (count($params)) { + // add in additional files to be tagged + $files = array_merge($files, $params); + } + + $dir = dirname($packageFile); + $dir = substr($dir, strrpos($dir, '/') + 1); + foreach ($files as $file) { + if (!file_exists($file)) { + $file = $dir . DIRECTORY_SEPARATOR . $file; + } + $command .= ' ' . escapeshellarg($file); + } + + if ($this->config->get('verbose') > 1) { + $this->output .= "+ $command\n"; + } + + $this->output .= "+ $command\n"; + if (empty($options['dry-run'])) { + $fp = popen($command, "r"); + while ($line = fgets($fp, 1024)) { + $this->output .= rtrim($line)."\n"; + } + pclose($fp); + } + + $this->ui->outputData($this->output, $_cmd); + return true; + } + + function doCvsDiff($command, $options, $params) + { + $this->output = ''; + if (sizeof($params) < 1) { + $help = $this->getHelp($command); + return $this->raiseError("$command: missing parameter: $help[0]"); + } + + $file = realpath($params[0]); + $obj = &$this->getPackageFile($this->config, $this->_debug); + $info = $obj->fromAnyFile($file, PEAR_VALIDATE_NORMAL); + if (PEAR::isError($info)) { + return $this->raiseError($info); + } + + $err = $warn = array(); + if (!$info->validate()) { + foreach ($info->getValidationWarnings() as $error) { + if ($error['level'] == 'warning') { + $warn[] = $error['message']; + } else { + $err[] = $error['message']; + } + } + } + + if (!$this->_displayValidationResults($err, $warn, true)) { + $this->ui->outputData($this->output, $command); + return $this->raiseError('CVS diff failed'); + } + + $info1 = $info->getFilelist(); + $files = $info1; + $cmd = "cvs"; + if (isset($options['quiet'])) { + $cmd .= ' -q'; + unset($options['quiet']); + } + + if (isset($options['reallyquiet'])) { + $cmd .= ' -Q'; + unset($options['reallyquiet']); + } + + if (isset($options['release'])) { + $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $options['release']); + $cvstag = "RELEASE_$cvsversion"; + $options['revision'] = $cvstag; + unset($options['release']); + } + + $execute = true; + if (isset($options['dry-run'])) { + $execute = false; + unset($options['dry-run']); + } + + $cmd .= ' diff'; + // the rest of the options are passed right on to "cvs diff" + foreach ($options as $option => $optarg) { + $arg = $short = false; + if (isset($this->commands[$command]['options'][$option])) { + $arg = $this->commands[$command]['options'][$option]['arg']; + $short = $this->commands[$command]['options'][$option]['shortopt']; + } + $cmd .= $short ? " -$short" : " --$option"; + if ($arg && $optarg) { + $cmd .= ($short ? '' : '=') . escapeshellarg($optarg); + } + } + + foreach ($files as $file) { + $cmd .= ' ' . escapeshellarg($file['name']); + } + + if ($this->config->get('verbose') > 1) { + $this->output .= "+ $cmd\n"; + } + + if ($execute) { + $fp = popen($cmd, "r"); + while ($line = fgets($fp, 1024)) { + $this->output .= rtrim($line)."\n"; + } + pclose($fp); + } + + $this->ui->outputData($this->output, $command); + return true; + } + + function doPackageDependencies($command, $options, $params) + { + // $params[0] -> the PEAR package to list its information + if (count($params) !== 1) { + return $this->raiseError("bad parameter(s), try \"help $command\""); + } + + $obj = &$this->getPackageFile($this->config, $this->_debug); + if (is_file($params[0]) || strpos($params[0], '.xml') > 0) { + $info = $obj->fromAnyFile($params[0], PEAR_VALIDATE_NORMAL); + } else { + $reg = $this->config->getRegistry(); + $info = $obj->fromArray($reg->packageInfo($params[0])); + } + + if (PEAR::isError($info)) { + return $this->raiseError($info); + } + + $deps = $info->getDeps(); + if (is_array($deps)) { + if ($info->getPackagexmlVersion() == '1.0') { + $data = array( + 'caption' => 'Dependencies for pear/' . $info->getPackage(), + 'border' => true, + 'headline' => array("Required?", "Type", "Name", "Relation", "Version"), + ); + + foreach ($deps as $d) { + if (isset($d['optional'])) { + if ($d['optional'] == 'yes') { + $req = 'No'; + } else { + $req = 'Yes'; + } + } else { + $req = 'Yes'; + } + + if (isset($this->_deps_rel_trans[$d['rel']])) { + $rel = $this->_deps_rel_trans[$d['rel']]; + } else { + $rel = $d['rel']; + } + + if (isset($this->_deps_type_trans[$d['type']])) { + $type = ucfirst($this->_deps_type_trans[$d['type']]); + } else { + $type = $d['type']; + } + + if (isset($d['name'])) { + $name = $d['name']; + } else { + $name = ''; + } + + if (isset($d['version'])) { + $version = $d['version']; + } else { + $version = ''; + } + + $data['data'][] = array($req, $type, $name, $rel, $version); + } + } else { // package.xml 2.0 dependencies display + require_once 'PEAR/Dependency2.php'; + $deps = $info->getDependencies(); + $reg = &$this->config->getRegistry(); + if (is_array($deps)) { + $d = new PEAR_Dependency2($this->config, array(), ''); + $data = array( + 'caption' => 'Dependencies for ' . $info->getPackage(), + 'border' => true, + 'headline' => array("Required?", "Type", "Name", 'Versioning', 'Group'), + ); + foreach ($deps as $type => $subd) { + $req = ($type == 'required') ? 'Yes' : 'No'; + if ($type == 'group') { + $group = $subd['attribs']['name']; + } else { + $group = ''; + } + + if (!isset($subd[0])) { + $subd = array($subd); + } + + foreach ($subd as $groupa) { + foreach ($groupa as $deptype => $depinfo) { + if ($deptype == 'attribs') { + continue; + } + + if ($deptype == 'pearinstaller') { + $deptype = 'pear Installer'; + } + + if (!isset($depinfo[0])) { + $depinfo = array($depinfo); + } + + foreach ($depinfo as $inf) { + $name = ''; + if (isset($inf['channel'])) { + $alias = $reg->channelAlias($inf['channel']); + if (!$alias) { + $alias = '(channel?) ' .$inf['channel']; + } + $name = $alias . '/'; + + } + if (isset($inf['name'])) { + $name .= $inf['name']; + } elseif (isset($inf['pattern'])) { + $name .= $inf['pattern']; + } else { + $name .= ''; + } + + if (isset($inf['uri'])) { + $name .= ' [' . $inf['uri'] . ']'; + } + + if (isset($inf['conflicts'])) { + $ver = 'conflicts'; + } else { + $ver = $d->_getExtraString($inf); + } + + $data['data'][] = array($req, ucfirst($deptype), $name, + $ver, $group); + } + } + } + } + } + } + + $this->ui->outputData($data, $command); + return true; + } + + // Fallback + $this->ui->outputData("This package does not have any dependencies.", $command); + } + + function doSign($command, $options, $params) + { + // should move most of this code into PEAR_Packager + // so it'll be easy to implement "pear package --sign" + if (count($params) !== 1) { + return $this->raiseError("bad parameter(s), try \"help $command\""); + } + + require_once 'System.php'; + require_once 'Archive/Tar.php'; + + if (!file_exists($params[0])) { + return $this->raiseError("file does not exist: $params[0]"); + } + + $obj = $this->getPackageFile($this->config, $this->_debug); + $info = $obj->fromTgzFile($params[0], PEAR_VALIDATE_NORMAL); + if (PEAR::isError($info)) { + return $this->raiseError($info); + } + + $tar = new Archive_Tar($params[0]); + + $tmpdir = $this->config->get('temp_dir'); + $tmpdir = System::mktemp(' -t "' . $tmpdir . '" -d pearsign'); + if (!$tar->extractList('package2.xml package.xml package.sig', $tmpdir)) { + return $this->raiseError("failed to extract tar file"); + } + + if (file_exists("$tmpdir/package.sig")) { + return $this->raiseError("package already signed"); + } + + $packagexml = 'package.xml'; + if (file_exists("$tmpdir/package2.xml")) { + $packagexml = 'package2.xml'; + } + + if (file_exists("$tmpdir/package.sig")) { + unlink("$tmpdir/package.sig"); + } + + if (!file_exists("$tmpdir/$packagexml")) { + return $this->raiseError("Extracted file $tmpdir/$packagexml not found."); + } + + $input = $this->ui->userDialog($command, + array('GnuPG Passphrase'), + array('password')); + if (!isset($input[0])) { + //use empty passphrase + $input[0] = ''; + } + + $devnull = (isset($options['verbose'])) ? '' : ' 2>/dev/null'; + $gpg = popen("gpg --batch --passphrase-fd 0 --armor --detach-sign --output $tmpdir/package.sig $tmpdir/$packagexml" . $devnull, "w"); + if (!$gpg) { + return $this->raiseError("gpg command failed"); + } + + fwrite($gpg, "$input[0]\n"); + if (pclose($gpg) || !file_exists("$tmpdir/package.sig")) { + return $this->raiseError("gpg sign failed"); + } + + if (!$tar->addModify("$tmpdir/package.sig", '', $tmpdir)) { + return $this->raiseError('failed adding signature to file'); + } + + $this->ui->outputData("Package signed.", $command); + return true; + } + + /** + * For unit testing purposes + */ + function &getInstaller(&$ui) + { + if (!class_exists('PEAR_Installer')) { + require_once 'PEAR/Installer.php'; + } + $a = &new PEAR_Installer($ui); + return $a; + } + + /** + * For unit testing purposes + */ + function &getCommandPackaging(&$ui, &$config) + { + if (!class_exists('PEAR_Command_Packaging')) { + if ($fp = @fopen('PEAR/Command/Packaging.php', 'r', true)) { + fclose($fp); + include_once 'PEAR/Command/Packaging.php'; + } + } + + if (class_exists('PEAR_Command_Packaging')) { + $a = &new PEAR_Command_Packaging($ui, $config); + } else { + $a = null; + } + + return $a; + } + + function doMakeRPM($command, $options, $params) + { + + // Check to see if PEAR_Command_Packaging is installed, and + // transparently switch to use the "make-rpm-spec" command from it + // instead, if it does. Otherwise, continue to use the old version + // of "makerpm" supplied with this package (PEAR). + $packaging_cmd = $this->getCommandPackaging($this->ui, $this->config); + if ($packaging_cmd !== null) { + $this->ui->outputData('PEAR_Command_Packaging is installed; using '. + 'newer "make-rpm-spec" command instead'); + return $packaging_cmd->run('make-rpm-spec', $options, $params); + } + + $this->ui->outputData('WARNING: "pear makerpm" is no longer available; an '. + 'improved version is available via "pear make-rpm-spec", which '. + 'is available by installing PEAR_Command_Packaging'); + return true; + } + + function doConvert($command, $options, $params) + { + $packagexml = isset($params[0]) ? $params[0] : 'package.xml'; + $newpackagexml = isset($params[1]) ? $params[1] : dirname($packagexml) . + DIRECTORY_SEPARATOR . 'package2.xml'; + $pkg = &$this->getPackageFile($this->config, $this->_debug); + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $pf = $pkg->fromPackageFile($packagexml, PEAR_VALIDATE_NORMAL); + PEAR::staticPopErrorHandling(); + if (PEAR::isError($pf)) { + if (is_array($pf->getUserInfo())) { + foreach ($pf->getUserInfo() as $warning) { + $this->ui->outputData($warning['message']); + } + } + return $this->raiseError($pf); + } + + if (is_a($pf, 'PEAR_PackageFile_v2')) { + $this->ui->outputData($packagexml . ' is already a package.xml version 2.0'); + return true; + } + + $gen = &$pf->getDefaultGenerator(); + $newpf = &$gen->toV2(); + $newpf->setPackagefile($newpackagexml); + $gen = &$newpf->getDefaultGenerator(); + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $state = (isset($options['flat']) ? PEAR_VALIDATE_PACKAGING : PEAR_VALIDATE_NORMAL); + $saved = $gen->toPackageFile(dirname($newpackagexml), $state, basename($newpackagexml)); + PEAR::staticPopErrorHandling(); + if (PEAR::isError($saved)) { + if (is_array($saved->getUserInfo())) { + foreach ($saved->getUserInfo() as $warning) { + $this->ui->outputData($warning['message']); + } + } + + $this->ui->outputData($saved->getMessage()); + return true; + } + + $this->ui->outputData('Wrote new version 2.0 package.xml to "' . $saved . '"'); + return true; + } +} \ No newline at end of file diff --git a/includes/pear/PEAR/Command/Package.xml b/includes/pear/PEAR/Command/Package.xml new file mode 100644 index 0000000..d1aff9d --- /dev/null +++ b/includes/pear/PEAR/Command/Package.xml @@ -0,0 +1,237 @@ + + + Build Package + doPackage + p + + + Z + Do not gzip the package file + + + n + Print the name of the packaged file. + + + [descfile] [descfile2] +Creates a PEAR package from its description file (usually called +package.xml). If a second packagefile is passed in, then +the packager will check to make sure that one is a package.xml +version 1.0, and the other is a package.xml version 2.0. The +package.xml version 1.0 will be saved as "package.xml" in the archive, +and the other as "package2.xml" in the archive" + + + + Validate Package Consistency + doPackageValidate + pv + + + + + + Run a "cvs diff" for all files in a package + doCvsDiff + cd + + + q + Be quiet + + + Q + Be really quiet + + + D + Diff against revision of DATE + DATE + + + R + Diff against tag for package release REL + REL + + + r + Diff against revision REV + REV + + + c + Generate context diff + + + u + Generate unified diff + + + i + Ignore case, consider upper- and lower-case letters equivalent + + + b + Ignore changes in amount of white space + + + B + Ignore changes that insert or delete blank lines + + + + Report only whether the files differ, no details + + + n + Don't do anything, just pretend + + + <package.xml> +Compares all the files in a package. Without any options, this +command will compare the current code with the last checked-in code. +Using the -r or -R option you may compare the current code with that +of a specific release. + + + + Set SVN Release Tag + doSvnTag + sv + + + q + Be quiet + + + F + Move (slide) tag if it exists + + + d + Remove tag + + + n + Don't do anything, just pretend + + + <package.xml> [files...] + Sets a SVN tag on all files in a package. Use this command after you have + packaged a distribution tarball with the "package" command to tag what + revisions of what files were in that release. If need to fix something + after running svntag once, but before the tarball is released to the public, + use the "slide" option to move the release tag. + + to include files (such as a second package.xml, or tests not included in the + release), pass them as additional parameters. + + + + Set CVS Release Tag + doCvsTag + ct + + + q + Be quiet + + + Q + Be really quiet + + + F + Move (slide) tag if it exists + + + d + Remove tag + + + n + Don't do anything, just pretend + + + <package.xml> [files...] +Sets a CVS tag on all files in a package. Use this command after you have +packaged a distribution tarball with the "package" command to tag what +revisions of what files were in that release. If need to fix something +after running cvstag once, but before the tarball is released to the public, +use the "slide" option to move the release tag. + +to include files (such as a second package.xml, or tests not included in the +release), pass them as additional parameters. + + + + Show package dependencies + doPackageDependencies + pd + + <package-file> or <package.xml> or <install-package-name> +List all dependencies the package has. +Can take a tgz / tar file, package.xml or a package name of an installed package. + + + Sign a package distribution file + doSign + si + + + v + Display GnuPG output + + + <package-file> +Signs a package distribution (.tar or .tgz) file with GnuPG. + + + Builds an RPM spec file from a PEAR package + doMakeRPM + rpm + + + t + Use FILE as RPM spec file template + FILE + + + p + Use FORMAT as format string for RPM package name, %s is replaced +by the PEAR package name, defaults to "PEAR::%s". + FORMAT + + + <package-file> + +Creates an RPM .spec file for wrapping a PEAR package inside an RPM +package. Intended to be used from the SPECS directory, with the PEAR +package tarball in the SOURCES directory: + +$ pear makerpm ../SOURCES/Net_Socket-1.0.tgz +Wrote RPM spec file PEAR::Net_Geo-1.0.spec +$ rpm -bb PEAR::Net_Socket-1.0.spec +... +Wrote: /usr/src/redhat/RPMS/i386/PEAR::Net_Socket-1.0-1.i386.rpm + + + + Convert a package.xml 1.0 to package.xml 2.0 format + doConvert + c2 + + + f + do not beautify the filelist. + + + [descfile] [descfile2] +Converts a package.xml in 1.0 format into a package.xml +in 2.0 format. The new file will be named package2.xml by default, +and package.xml will be used as the old file by default. +This is not the most intelligent conversion, and should only be +used for automated conversion or learning the format. + + + \ No newline at end of file diff --git a/includes/pear/PEAR/Command/Pickle.php b/includes/pear/PEAR/Command/Pickle.php new file mode 100644 index 0000000..7fe8da7 --- /dev/null +++ b/includes/pear/PEAR/Command/Pickle.php @@ -0,0 +1,421 @@ + + * @copyright 2005-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.1 + */ + +/** + * base class + */ +require_once 'PEAR/Command/Common.php'; + +/** + * PEAR commands for login/logout + * + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 2005-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.1 + */ + +class PEAR_Command_Pickle extends PEAR_Command_Common +{ + var $commands = array( + 'pickle' => array( + 'summary' => 'Build PECL Package', + 'function' => 'doPackage', + 'shortcut' => 'pi', + 'options' => array( + 'nocompress' => array( + 'shortopt' => 'Z', + 'doc' => 'Do not gzip the package file' + ), + 'showname' => array( + 'shortopt' => 'n', + 'doc' => 'Print the name of the packaged file.', + ), + ), + 'doc' => '[descfile] +Creates a PECL package from its package2.xml file. + +An automatic conversion will be made to a package.xml 1.0 and written out to +disk in the current directory as "package.xml". Note that +only simple package.xml 2.0 will be converted. package.xml 2.0 with: + + - dependency types other than required/optional PECL package/ext/php/pearinstaller + - more than one extsrcrelease or zendextsrcrelease + - zendextbinrelease, extbinrelease, phprelease, or bundle release type + - dependency groups + - ignore tags in release filelist + - tasks other than replace + - custom roles + +will cause pickle to fail, and output an error message. If your package2.xml +uses any of these features, you are best off using PEAR_PackageFileManager to +generate both package.xml. +' + ), + ); + + /** + * PEAR_Command_Package constructor. + * + * @access public + */ + function PEAR_Command_Pickle(&$ui, &$config) + { + parent::PEAR_Command_Common($ui, $config); + } + + /** + * For unit-testing ease + * + * @return PEAR_Packager + */ + function &getPackager() + { + if (!class_exists('PEAR_Packager')) { + require_once 'PEAR/Packager.php'; + } + + $a = &new PEAR_Packager; + return $a; + } + + /** + * For unit-testing ease + * + * @param PEAR_Config $config + * @param bool $debug + * @param string|null $tmpdir + * @return PEAR_PackageFile + */ + function &getPackageFile($config, $debug = false) + { + if (!class_exists('PEAR_Common')) { + require_once 'PEAR/Common.php'; + } + + if (!class_exists('PEAR_PackageFile')) { + require_once 'PEAR/PackageFile.php'; + } + + $a = &new PEAR_PackageFile($config, $debug); + $common = new PEAR_Common; + $common->ui = $this->ui; + $a->setLogger($common); + return $a; + } + + function doPackage($command, $options, $params) + { + $this->output = ''; + $pkginfofile = isset($params[0]) ? $params[0] : 'package2.xml'; + $packager = &$this->getPackager(); + if (PEAR::isError($err = $this->_convertPackage($pkginfofile))) { + return $err; + } + + $compress = empty($options['nocompress']) ? true : false; + $result = $packager->package($pkginfofile, $compress, 'package.xml'); + if (PEAR::isError($result)) { + return $this->raiseError($result); + } + + // Don't want output, only the package file name just created + if (isset($options['showname'])) { + $this->ui->outputData($result, $command); + } + + return true; + } + + function _convertPackage($packagexml) + { + $pkg = &$this->getPackageFile($this->config); + $pf2 = &$pkg->fromPackageFile($packagexml, PEAR_VALIDATE_NORMAL); + if (!is_a($pf2, 'PEAR_PackageFile_v2')) { + return $this->raiseError('Cannot process "' . + $packagexml . '", is not a package.xml 2.0'); + } + + require_once 'PEAR/PackageFile/v1.php'; + $pf = new PEAR_PackageFile_v1; + $pf->setConfig($this->config); + if ($pf2->getPackageType() != 'extsrc' && $pf2->getPackageType() != 'zendextsrc') { + return $this->raiseError('Cannot safely convert "' . $packagexml . + '", is not an extension source package. Using a PEAR_PackageFileManager-based ' . + 'script is an option'); + } + + if (is_array($pf2->getUsesRole())) { + return $this->raiseError('Cannot safely convert "' . $packagexml . + '", contains custom roles. Using a PEAR_PackageFileManager-based script or ' . + 'the convert command is an option'); + } + + if (is_array($pf2->getUsesTask())) { + return $this->raiseError('Cannot safely convert "' . $packagexml . + '", contains custom tasks. Using a PEAR_PackageFileManager-based script or ' . + 'the convert command is an option'); + } + + $deps = $pf2->getDependencies(); + if (isset($deps['group'])) { + return $this->raiseError('Cannot safely convert "' . $packagexml . + '", contains dependency groups. Using a PEAR_PackageFileManager-based script ' . + 'or the convert command is an option'); + } + + if (isset($deps['required']['subpackage']) || + isset($deps['optional']['subpackage'])) { + return $this->raiseError('Cannot safely convert "' . $packagexml . + '", contains subpackage dependencies. Using a PEAR_PackageFileManager-based '. + 'script is an option'); + } + + if (isset($deps['required']['os'])) { + return $this->raiseError('Cannot safely convert "' . $packagexml . + '", contains os dependencies. Using a PEAR_PackageFileManager-based '. + 'script is an option'); + } + + if (isset($deps['required']['arch'])) { + return $this->raiseError('Cannot safely convert "' . $packagexml . + '", contains arch dependencies. Using a PEAR_PackageFileManager-based '. + 'script is an option'); + } + + $pf->setPackage($pf2->getPackage()); + $pf->setSummary($pf2->getSummary()); + $pf->setDescription($pf2->getDescription()); + foreach ($pf2->getMaintainers() as $maintainer) { + $pf->addMaintainer($maintainer['role'], $maintainer['handle'], + $maintainer['name'], $maintainer['email']); + } + + $pf->setVersion($pf2->getVersion()); + $pf->setDate($pf2->getDate()); + $pf->setLicense($pf2->getLicense()); + $pf->setState($pf2->getState()); + $pf->setNotes($pf2->getNotes()); + $pf->addPhpDep($deps['required']['php']['min'], 'ge'); + if (isset($deps['required']['php']['max'])) { + $pf->addPhpDep($deps['required']['php']['max'], 'le'); + } + + if (isset($deps['required']['package'])) { + if (!isset($deps['required']['package'][0])) { + $deps['required']['package'] = array($deps['required']['package']); + } + + foreach ($deps['required']['package'] as $dep) { + if (!isset($dep['channel'])) { + return $this->raiseError('Cannot safely convert "' . $packagexml . '"' . + ' contains uri-based dependency on a package. Using a ' . + 'PEAR_PackageFileManager-based script is an option'); + } + + if ($dep['channel'] != 'pear.php.net' + && $dep['channel'] != 'pecl.php.net' + && $dep['channel'] != 'doc.php.net') { + return $this->raiseError('Cannot safely convert "' . $packagexml . '"' . + ' contains dependency on a non-standard channel package. Using a ' . + 'PEAR_PackageFileManager-based script is an option'); + } + + if (isset($dep['conflicts'])) { + return $this->raiseError('Cannot safely convert "' . $packagexml . '"' . + ' contains conflicts dependency. Using a ' . + 'PEAR_PackageFileManager-based script is an option'); + } + + if (isset($dep['exclude'])) { + $this->ui->outputData('WARNING: exclude tags are ignored in conversion'); + } + + if (isset($dep['min'])) { + $pf->addPackageDep($dep['name'], $dep['min'], 'ge'); + } + + if (isset($dep['max'])) { + $pf->addPackageDep($dep['name'], $dep['max'], 'le'); + } + } + } + + if (isset($deps['required']['extension'])) { + if (!isset($deps['required']['extension'][0])) { + $deps['required']['extension'] = array($deps['required']['extension']); + } + + foreach ($deps['required']['extension'] as $dep) { + if (isset($dep['conflicts'])) { + return $this->raiseError('Cannot safely convert "' . $packagexml . '"' . + ' contains conflicts dependency. Using a ' . + 'PEAR_PackageFileManager-based script is an option'); + } + + if (isset($dep['exclude'])) { + $this->ui->outputData('WARNING: exclude tags are ignored in conversion'); + } + + if (isset($dep['min'])) { + $pf->addExtensionDep($dep['name'], $dep['min'], 'ge'); + } + + if (isset($dep['max'])) { + $pf->addExtensionDep($dep['name'], $dep['max'], 'le'); + } + } + } + + if (isset($deps['optional']['package'])) { + if (!isset($deps['optional']['package'][0])) { + $deps['optional']['package'] = array($deps['optional']['package']); + } + + foreach ($deps['optional']['package'] as $dep) { + if (!isset($dep['channel'])) { + return $this->raiseError('Cannot safely convert "' . $packagexml . '"' . + ' contains uri-based dependency on a package. Using a ' . + 'PEAR_PackageFileManager-based script is an option'); + } + + if ($dep['channel'] != 'pear.php.net' + && $dep['channel'] != 'pecl.php.net' + && $dep['channel'] != 'doc.php.net') { + return $this->raiseError('Cannot safely convert "' . $packagexml . '"' . + ' contains dependency on a non-standard channel package. Using a ' . + 'PEAR_PackageFileManager-based script is an option'); + } + + if (isset($dep['exclude'])) { + $this->ui->outputData('WARNING: exclude tags are ignored in conversion'); + } + + if (isset($dep['min'])) { + $pf->addPackageDep($dep['name'], $dep['min'], 'ge', 'yes'); + } + + if (isset($dep['max'])) { + $pf->addPackageDep($dep['name'], $dep['max'], 'le', 'yes'); + } + } + } + + if (isset($deps['optional']['extension'])) { + if (!isset($deps['optional']['extension'][0])) { + $deps['optional']['extension'] = array($deps['optional']['extension']); + } + + foreach ($deps['optional']['extension'] as $dep) { + if (isset($dep['exclude'])) { + $this->ui->outputData('WARNING: exclude tags are ignored in conversion'); + } + + if (isset($dep['min'])) { + $pf->addExtensionDep($dep['name'], $dep['min'], 'ge', 'yes'); + } + + if (isset($dep['max'])) { + $pf->addExtensionDep($dep['name'], $dep['max'], 'le', 'yes'); + } + } + } + + $contents = $pf2->getContents(); + $release = $pf2->getReleases(); + if (isset($releases[0])) { + return $this->raiseError('Cannot safely process "' . $packagexml . '" contains ' + . 'multiple extsrcrelease/zendextsrcrelease tags. Using a PEAR_PackageFileManager-based script ' . + 'or the convert command is an option'); + } + + if ($configoptions = $pf2->getConfigureOptions()) { + foreach ($configoptions as $option) { + $default = isset($option['default']) ? $option['default'] : false; + $pf->addConfigureOption($option['name'], $option['prompt'], $default); + } + } + + if (isset($release['filelist']['ignore'])) { + return $this->raiseError('Cannot safely process "' . $packagexml . '" contains ' + . 'ignore tags. Using a PEAR_PackageFileManager-based script or the convert' . + ' command is an option'); + } + + if (isset($release['filelist']['install']) && + !isset($release['filelist']['install'][0])) { + $release['filelist']['install'] = array($release['filelist']['install']); + } + + if (isset($contents['dir']['attribs']['baseinstalldir'])) { + $baseinstalldir = $contents['dir']['attribs']['baseinstalldir']; + } else { + $baseinstalldir = false; + } + + if (!isset($contents['dir']['file'][0])) { + $contents['dir']['file'] = array($contents['dir']['file']); + } + + foreach ($contents['dir']['file'] as $file) { + if ($baseinstalldir && !isset($file['attribs']['baseinstalldir'])) { + $file['attribs']['baseinstalldir'] = $baseinstalldir; + } + + $processFile = $file; + unset($processFile['attribs']); + if (count($processFile)) { + foreach ($processFile as $name => $task) { + if ($name != $pf2->getTasksNs() . ':replace') { + return $this->raiseError('Cannot safely process "' . $packagexml . + '" contains tasks other than replace. Using a ' . + 'PEAR_PackageFileManager-based script is an option.'); + } + $file['attribs']['replace'][] = $task; + } + } + + if (!in_array($file['attribs']['role'], PEAR_Common::getFileRoles())) { + return $this->raiseError('Cannot safely convert "' . $packagexml . + '", contains custom roles. Using a PEAR_PackageFileManager-based script ' . + 'or the convert command is an option'); + } + + if (isset($release['filelist']['install'])) { + foreach ($release['filelist']['install'] as $installas) { + if ($installas['attribs']['name'] == $file['attribs']['name']) { + $file['attribs']['install-as'] = $installas['attribs']['as']; + } + } + } + + $pf->addFile('/', $file['attribs']['name'], $file['attribs']); + } + + if ($pf2->getChangeLog()) { + $this->ui->outputData('WARNING: changelog is not translated to package.xml ' . + '1.0, use PEAR_PackageFileManager-based script if you need changelog-' . + 'translation for package.xml 1.0'); + } + + $gen = &$pf->getDefaultGenerator(); + $gen->toPackageFile('.'); + } +} \ No newline at end of file diff --git a/includes/pear/PEAR/Command/Pickle.xml b/includes/pear/PEAR/Command/Pickle.xml new file mode 100644 index 0000000..721ecea --- /dev/null +++ b/includes/pear/PEAR/Command/Pickle.xml @@ -0,0 +1,36 @@ + + + Build PECL Package + doPackage + pi + + + Z + Do not gzip the package file + + + n + Print the name of the packaged file. + + + [descfile] +Creates a PECL package from its package2.xml file. + +An automatic conversion will be made to a package.xml 1.0 and written out to +disk in the current directory as "package.xml". Note that +only simple package.xml 2.0 will be converted. package.xml 2.0 with: + + - dependency types other than required/optional PECL package/ext/php/pearinstaller + - more than one extsrcrelease or zendextsrcrelease + - zendextbinrelease, extbinrelease, phprelease, or bundle release type + - dependency groups + - ignore tags in release filelist + - tasks other than replace + - custom roles + +will cause pickle to fail, and output an error message. If your package2.xml +uses any of these features, you are best off using PEAR_PackageFileManager to +generate both package.xml. + + + \ No newline at end of file diff --git a/includes/pear/PEAR/Command/Registry.php b/includes/pear/PEAR/Command/Registry.php new file mode 100644 index 0000000..4110123 --- /dev/null +++ b/includes/pear/PEAR/Command/Registry.php @@ -0,0 +1,1145 @@ + + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 0.1 + */ + +/** + * base class + */ +require_once 'PEAR/Command/Common.php'; + +/** + * PEAR commands for registry manipulation + * + * @category pear + * @package PEAR + * @author Stig Bakken + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 0.1 + */ +class PEAR_Command_Registry extends PEAR_Command_Common +{ + var $commands = array( + 'list' => array( + 'summary' => 'List Installed Packages In The Default Channel', + 'function' => 'doList', + 'shortcut' => 'l', + 'options' => array( + 'channel' => array( + 'shortopt' => 'c', + 'doc' => 'list installed packages from this channel', + 'arg' => 'CHAN', + ), + 'allchannels' => array( + 'shortopt' => 'a', + 'doc' => 'list installed packages from all channels', + ), + 'channelinfo' => array( + 'shortopt' => 'i', + 'doc' => 'output fully channel-aware data, even on failure', + ), + ), + 'doc' => ' +If invoked without parameters, this command lists the PEAR packages +installed in your php_dir ({config php_dir}). With a parameter, it +lists the files in a package. +', + ), + 'list-files' => array( + 'summary' => 'List Files In Installed Package', + 'function' => 'doFileList', + 'shortcut' => 'fl', + 'options' => array(), + 'doc' => ' +List the files in an installed package. +' + ), + 'shell-test' => array( + 'summary' => 'Shell Script Test', + 'function' => 'doShellTest', + 'shortcut' => 'st', + 'options' => array(), + 'doc' => ' [[relation] version] +Tests if a package is installed in the system. Will exit(1) if it is not. + The version comparison operator. One of: + <, lt, <=, le, >, gt, >=, ge, ==, =, eq, !=, <>, ne + The version to compare with +'), + 'info' => array( + 'summary' => 'Display information about a package', + 'function' => 'doInfo', + 'shortcut' => 'in', + 'options' => array(), + 'doc' => ' +Displays information about a package. The package argument may be a +local package file, an URL to a package file, or the name of an +installed package.' + ) + ); + + /** + * PEAR_Command_Registry constructor. + * + * @access public + */ + function PEAR_Command_Registry(&$ui, &$config) + { + parent::PEAR_Command_Common($ui, $config); + } + + function _sortinfo($a, $b) + { + $apackage = isset($a['package']) ? $a['package'] : $a['name']; + $bpackage = isset($b['package']) ? $b['package'] : $b['name']; + return strcmp($apackage, $bpackage); + } + + function doList($command, $options, $params) + { + $reg = &$this->config->getRegistry(); + $channelinfo = isset($options['channelinfo']); + if (isset($options['allchannels']) && !$channelinfo) { + return $this->doListAll($command, array(), $params); + } + + if (isset($options['allchannels']) && $channelinfo) { + // allchannels with $channelinfo + unset($options['allchannels']); + $channels = $reg->getChannels(); + $errors = array(); + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + foreach ($channels as $channel) { + $options['channel'] = $channel->getName(); + $ret = $this->doList($command, $options, $params); + + if (PEAR::isError($ret)) { + $errors[] = $ret; + } + } + + PEAR::staticPopErrorHandling(); + if (count($errors)) { + // for now, only give first error + return PEAR::raiseError($errors[0]); + } + + return true; + } + + if (count($params) === 1) { + return $this->doFileList($command, $options, $params); + } + + if (isset($options['channel'])) { + if (!$reg->channelExists($options['channel'])) { + return $this->raiseError('Channel "' . $options['channel'] .'" does not exist'); + } + + $channel = $reg->channelName($options['channel']); + } else { + $channel = $this->config->get('default_channel'); + } + + $installed = $reg->packageInfo(null, null, $channel); + usort($installed, array(&$this, '_sortinfo')); + + $data = array( + 'caption' => 'Installed packages, channel ' . + $channel . ':', + 'border' => true, + 'headline' => array('Package', 'Version', 'State'), + 'channel' => $channel, + ); + if ($channelinfo) { + $data['headline'] = array('Channel', 'Package', 'Version', 'State'); + } + + if (count($installed) && !isset($data['data'])) { + $data['data'] = array(); + } + + foreach ($installed as $package) { + $pobj = $reg->getPackage(isset($package['package']) ? + $package['package'] : $package['name'], $channel); + if ($channelinfo) { + $packageinfo = array($pobj->getChannel(), $pobj->getPackage(), $pobj->getVersion(), + $pobj->getState() ? $pobj->getState() : null); + } else { + $packageinfo = array($pobj->getPackage(), $pobj->getVersion(), + $pobj->getState() ? $pobj->getState() : null); + } + $data['data'][] = $packageinfo; + } + + if (count($installed) === 0) { + if (!$channelinfo) { + $data = '(no packages installed from channel ' . $channel . ')'; + } else { + $data = array( + 'caption' => 'Installed packages, channel ' . + $channel . ':', + 'border' => true, + 'channel' => $channel, + 'data' => array(array('(no packages installed)')), + ); + } + } + + $this->ui->outputData($data, $command); + return true; + } + + function doListAll($command, $options, $params) + { + // This duplicate code is deprecated over + // list --channelinfo, which gives identical + // output for list and list --allchannels. + $reg = &$this->config->getRegistry(); + $installed = $reg->packageInfo(null, null, null); + foreach ($installed as $channel => $packages) { + usort($packages, array($this, '_sortinfo')); + $data = array( + 'caption' => 'Installed packages, channel ' . $channel . ':', + 'border' => true, + 'headline' => array('Package', 'Version', 'State'), + 'channel' => $channel + ); + + foreach ($packages as $package) { + $p = isset($package['package']) ? $package['package'] : $package['name']; + $pobj = $reg->getPackage($p, $channel); + $data['data'][] = array($pobj->getPackage(), $pobj->getVersion(), + $pobj->getState() ? $pobj->getState() : null); + } + + // Adds a blank line after each section + $data['data'][] = array(); + + if (count($packages) === 0) { + $data = array( + 'caption' => 'Installed packages, channel ' . $channel . ':', + 'border' => true, + 'data' => array(array('(no packages installed)'), array()), + 'channel' => $channel + ); + } + $this->ui->outputData($data, $command); + } + return true; + } + + function doFileList($command, $options, $params) + { + if (count($params) !== 1) { + return $this->raiseError('list-files expects 1 parameter'); + } + + $reg = &$this->config->getRegistry(); + $fp = false; + if (!is_dir($params[0]) && (file_exists($params[0]) || $fp = @fopen($params[0], 'r'))) { + if ($fp) { + fclose($fp); + } + + if (!class_exists('PEAR_PackageFile')) { + require_once 'PEAR/PackageFile.php'; + } + + $pkg = &new PEAR_PackageFile($this->config, $this->_debug); + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $info = &$pkg->fromAnyFile($params[0], PEAR_VALIDATE_NORMAL); + PEAR::staticPopErrorHandling(); + $headings = array('Package File', 'Install Path'); + $installed = false; + } else { + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $parsed = $reg->parsePackageName($params[0], $this->config->get('default_channel')); + PEAR::staticPopErrorHandling(); + if (PEAR::isError($parsed)) { + return $this->raiseError($parsed); + } + + $info = &$reg->getPackage($parsed['package'], $parsed['channel']); + $headings = array('Type', 'Install Path'); + $installed = true; + } + + if (PEAR::isError($info)) { + return $this->raiseError($info); + } + + if ($info === null) { + return $this->raiseError("`$params[0]' not installed"); + } + + $list = ($info->getPackagexmlVersion() == '1.0' || $installed) ? + $info->getFilelist() : $info->getContents(); + if ($installed) { + $caption = 'Installed Files For ' . $params[0]; + } else { + $caption = 'Contents of ' . basename($params[0]); + } + + $data = array( + 'caption' => $caption, + 'border' => true, + 'headline' => $headings); + if ($info->getPackagexmlVersion() == '1.0' || $installed) { + foreach ($list as $file => $att) { + if ($installed) { + if (empty($att['installed_as'])) { + continue; + } + $data['data'][] = array($att['role'], $att['installed_as']); + } else { + if (isset($att['baseinstalldir']) && !in_array($att['role'], + array('test', 'data', 'doc'))) { + $dest = $att['baseinstalldir'] . DIRECTORY_SEPARATOR . + $file; + } else { + $dest = $file; + } + switch ($att['role']) { + case 'test': + case 'data': + case 'doc': + $role = $att['role']; + if ($role == 'test') { + $role .= 's'; + } + $dest = $this->config->get($role . '_dir') . DIRECTORY_SEPARATOR . + $info->getPackage() . DIRECTORY_SEPARATOR . $dest; + break; + case 'php': + default: + $dest = $this->config->get('php_dir') . DIRECTORY_SEPARATOR . + $dest; + } + $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR; + $dest = preg_replace(array('!\\\\+!', '!/!', "!$ds2+!"), + array(DIRECTORY_SEPARATOR, + DIRECTORY_SEPARATOR, + DIRECTORY_SEPARATOR), + $dest); + $file = preg_replace('!/+!', '/', $file); + $data['data'][] = array($file, $dest); + } + } + } else { // package.xml 2.0, not installed + if (!isset($list['dir']['file'][0])) { + $list['dir']['file'] = array($list['dir']['file']); + } + + foreach ($list['dir']['file'] as $att) { + $att = $att['attribs']; + $file = $att['name']; + $role = &PEAR_Installer_Role::factory($info, $att['role'], $this->config); + $role->setup($this, $info, $att, $file); + if (!$role->isInstallable()) { + $dest = '(not installable)'; + } else { + $dest = $role->processInstallation($info, $att, $file, ''); + if (PEAR::isError($dest)) { + $dest = '(Unknown role "' . $att['role'] . ')'; + } else { + list(,, $dest) = $dest; + } + } + $data['data'][] = array($file, $dest); + } + } + + $this->ui->outputData($data, $command); + return true; + } + + function doShellTest($command, $options, $params) + { + if (count($params) < 1) { + return PEAR::raiseError('ERROR, usage: pear shell-test packagename [[relation] version]'); + } + + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $reg = &$this->config->getRegistry(); + $info = $reg->parsePackageName($params[0], $this->config->get('default_channel')); + if (PEAR::isError($info)) { + exit(1); // invalid package name + } + + $package = $info['package']; + $channel = $info['channel']; + // "pear shell-test Foo" + if (!$reg->packageExists($package, $channel)) { + if ($channel == 'pecl.php.net') { + if ($reg->packageExists($package, 'pear.php.net')) { + $channel = 'pear.php.net'; // magically change channels for extensions + } + } + } + + if (count($params) === 1) { + if (!$reg->packageExists($package, $channel)) { + exit(1); + } + // "pear shell-test Foo 1.0" + } elseif (count($params) === 2) { + $v = $reg->packageInfo($package, 'version', $channel); + if (!$v || !version_compare("$v", "{$params[1]}", "ge")) { + exit(1); + } + // "pear shell-test Foo ge 1.0" + } elseif (count($params) === 3) { + $v = $reg->packageInfo($package, 'version', $channel); + if (!$v || !version_compare("$v", "{$params[2]}", $params[1])) { + exit(1); + } + } else { + PEAR::staticPopErrorHandling(); + $this->raiseError("$command: expects 1 to 3 parameters"); + exit(1); + } + } + + function doInfo($command, $options, $params) + { + if (count($params) !== 1) { + return $this->raiseError('pear info expects 1 parameter'); + } + + $info = $fp = false; + $reg = &$this->config->getRegistry(); + if (is_file($params[0]) && !is_dir($params[0]) && + (file_exists($params[0]) || $fp = @fopen($params[0], 'r')) + ) { + if ($fp) { + fclose($fp); + } + + if (!class_exists('PEAR_PackageFile')) { + require_once 'PEAR/PackageFile.php'; + } + + $pkg = &new PEAR_PackageFile($this->config, $this->_debug); + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $obj = &$pkg->fromAnyFile($params[0], PEAR_VALIDATE_NORMAL); + PEAR::staticPopErrorHandling(); + if (PEAR::isError($obj)) { + $uinfo = $obj->getUserInfo(); + if (is_array($uinfo)) { + foreach ($uinfo as $message) { + if (is_array($message)) { + $message = $message['message']; + } + $this->ui->outputData($message); + } + } + + return $this->raiseError($obj); + } + + if ($obj->getPackagexmlVersion() != '1.0') { + return $this->_doInfo2($command, $options, $params, $obj, false); + } + + $info = $obj->toArray(); + } else { + $parsed = $reg->parsePackageName($params[0], $this->config->get('default_channel')); + if (PEAR::isError($parsed)) { + return $this->raiseError($parsed); + } + + $package = $parsed['package']; + $channel = $parsed['channel']; + $info = $reg->packageInfo($package, null, $channel); + if (isset($info['old'])) { + $obj = $reg->getPackage($package, $channel); + return $this->_doInfo2($command, $options, $params, $obj, true); + } + } + + if (PEAR::isError($info)) { + return $info; + } + + if (empty($info)) { + $this->raiseError("No information found for `$params[0]'"); + return; + } + + unset($info['filelist']); + unset($info['dirtree']); + unset($info['changelog']); + if (isset($info['xsdversion'])) { + $info['package.xml version'] = $info['xsdversion']; + unset($info['xsdversion']); + } + + if (isset($info['packagerversion'])) { + $info['packaged with PEAR version'] = $info['packagerversion']; + unset($info['packagerversion']); + } + + $keys = array_keys($info); + $longtext = array('description', 'summary'); + foreach ($keys as $key) { + if (is_array($info[$key])) { + switch ($key) { + case 'maintainers': { + $i = 0; + $mstr = ''; + foreach ($info[$key] as $m) { + if ($i++ > 0) { + $mstr .= "\n"; + } + $mstr .= $m['name'] . " <"; + if (isset($m['email'])) { + $mstr .= $m['email']; + } else { + $mstr .= $m['handle'] . '@php.net'; + } + $mstr .= "> ($m[role])"; + } + $info[$key] = $mstr; + break; + } + case 'release_deps': { + $i = 0; + $dstr = ''; + foreach ($info[$key] as $d) { + if (isset($this->_deps_rel_trans[$d['rel']])) { + $rel = $this->_deps_rel_trans[$d['rel']]; + } else { + $rel = $d['rel']; + } + if (isset($this->_deps_type_trans[$d['type']])) { + $type = ucfirst($this->_deps_type_trans[$d['type']]); + } else { + $type = $d['type']; + } + if (isset($d['name'])) { + $name = $d['name'] . ' '; + } else { + $name = ''; + } + if (isset($d['version'])) { + $version = $d['version'] . ' '; + } else { + $version = ''; + } + if (isset($d['optional']) && $d['optional'] == 'yes') { + $optional = ' (optional)'; + } else { + $optional = ''; + } + $dstr .= "$type $name$rel $version$optional\n"; + } + $info[$key] = $dstr; + break; + } + case 'provides' : { + $debug = $this->config->get('verbose'); + if ($debug < 2) { + $pstr = 'Classes: '; + } else { + $pstr = ''; + } + $i = 0; + foreach ($info[$key] as $p) { + if ($debug < 2 && $p['type'] != "class") { + continue; + } + // Only print classes when verbosity mode is < 2 + if ($debug < 2) { + if ($i++ > 0) { + $pstr .= ", "; + } + $pstr .= $p['name']; + } else { + if ($i++ > 0) { + $pstr .= "\n"; + } + $pstr .= ucfirst($p['type']) . " " . $p['name']; + if (isset($p['explicit']) && $p['explicit'] == 1) { + $pstr .= " (explicit)"; + } + } + } + $info[$key] = $pstr; + break; + } + case 'configure_options' : { + foreach ($info[$key] as $i => $p) { + $info[$key][$i] = array_map(null, array_keys($p), array_values($p)); + $info[$key][$i] = array_map(create_function('$a', + 'return join(" = ",$a);'), $info[$key][$i]); + $info[$key][$i] = implode(', ', $info[$key][$i]); + } + $info[$key] = implode("\n", $info[$key]); + break; + } + default: { + $info[$key] = implode(", ", $info[$key]); + break; + } + } + } + + if ($key == '_lastmodified') { + $hdate = date('Y-m-d', $info[$key]); + unset($info[$key]); + $info['Last Modified'] = $hdate; + } elseif ($key == '_lastversion') { + $info['Previous Installed Version'] = $info[$key] ? $info[$key] : '- None -'; + unset($info[$key]); + } else { + $info[$key] = trim($info[$key]); + if (in_array($key, $longtext)) { + $info[$key] = preg_replace('/ +/', ' ', $info[$key]); + } + } + } + + $caption = 'About ' . $info['package'] . '-' . $info['version']; + $data = array( + 'caption' => $caption, + 'border' => true); + foreach ($info as $key => $value) { + $key = ucwords(trim(str_replace('_', ' ', $key))); + $data['data'][] = array($key, $value); + } + $data['raw'] = $info; + + $this->ui->outputData($data, 'package-info'); + } + + /** + * @access private + */ + function _doInfo2($command, $options, $params, &$obj, $installed) + { + $reg = &$this->config->getRegistry(); + $caption = 'About ' . $obj->getChannel() . '/' .$obj->getPackage() . '-' . + $obj->getVersion(); + $data = array( + 'caption' => $caption, + 'border' => true); + switch ($obj->getPackageType()) { + case 'php' : + $release = 'PEAR-style PHP-based Package'; + break; + case 'extsrc' : + $release = 'PECL-style PHP extension (source code)'; + break; + case 'zendextsrc' : + $release = 'PECL-style Zend extension (source code)'; + break; + case 'extbin' : + $release = 'PECL-style PHP extension (binary)'; + break; + case 'zendextbin' : + $release = 'PECL-style Zend extension (binary)'; + break; + case 'bundle' : + $release = 'Package bundle (collection of packages)'; + break; + } + $extends = $obj->getExtends(); + $extends = $extends ? + $obj->getPackage() . ' (extends ' . $extends . ')' : $obj->getPackage(); + if ($src = $obj->getSourcePackage()) { + $extends .= ' (source package ' . $src['channel'] . '/' . $src['package'] . ')'; + } + + $info = array( + 'Release Type' => $release, + 'Name' => $extends, + 'Channel' => $obj->getChannel(), + 'Summary' => preg_replace('/ +/', ' ', $obj->getSummary()), + 'Description' => preg_replace('/ +/', ' ', $obj->getDescription()), + ); + $info['Maintainers'] = ''; + foreach (array('lead', 'developer', 'contributor', 'helper') as $role) { + $leads = $obj->{"get{$role}s"}(); + if (!$leads) { + continue; + } + + if (isset($leads['active'])) { + $leads = array($leads); + } + + foreach ($leads as $lead) { + if (!empty($info['Maintainers'])) { + $info['Maintainers'] .= "\n"; + } + + $active = $lead['active'] == 'no' ? ', inactive' : ''; + $info['Maintainers'] .= $lead['name'] . ' <'; + $info['Maintainers'] .= $lead['email'] . "> ($role$active)"; + } + } + + $info['Release Date'] = $obj->getDate(); + if ($time = $obj->getTime()) { + $info['Release Date'] .= ' ' . $time; + } + + $info['Release Version'] = $obj->getVersion() . ' (' . $obj->getState() . ')'; + $info['API Version'] = $obj->getVersion('api') . ' (' . $obj->getState('api') . ')'; + $info['License'] = $obj->getLicense(); + $uri = $obj->getLicenseLocation(); + if ($uri) { + if (isset($uri['uri'])) { + $info['License'] .= ' (' . $uri['uri'] . ')'; + } else { + $extra = $obj->getInstalledLocation($info['filesource']); + if ($extra) { + $info['License'] .= ' (' . $uri['filesource'] . ')'; + } + } + } + + $info['Release Notes'] = $obj->getNotes(); + if ($compat = $obj->getCompatible()) { + if (!isset($compat[0])) { + $compat = array($compat); + } + + $info['Compatible with'] = ''; + foreach ($compat as $package) { + $info['Compatible with'] .= $package['channel'] . '/' . $package['name'] . + "\nVersions >= " . $package['min'] . ', <= ' . $package['max']; + if (isset($package['exclude'])) { + if (is_array($package['exclude'])) { + $package['exclude'] = implode(', ', $package['exclude']); + } + + if (!isset($info['Not Compatible with'])) { + $info['Not Compatible with'] = ''; + } else { + $info['Not Compatible with'] .= "\n"; + } + $info['Not Compatible with'] .= $package['channel'] . '/' . + $package['name'] . "\nVersions " . $package['exclude']; + } + } + } + + $usesrole = $obj->getUsesrole(); + if ($usesrole) { + if (!isset($usesrole[0])) { + $usesrole = array($usesrole); + } + + foreach ($usesrole as $roledata) { + if (isset($info['Uses Custom Roles'])) { + $info['Uses Custom Roles'] .= "\n"; + } else { + $info['Uses Custom Roles'] = ''; + } + + if (isset($roledata['package'])) { + $rolepackage = $reg->parsedPackageNameToString($roledata, true); + } else { + $rolepackage = $roledata['uri']; + } + $info['Uses Custom Roles'] .= $roledata['role'] . ' (' . $rolepackage . ')'; + } + } + + $usestask = $obj->getUsestask(); + if ($usestask) { + if (!isset($usestask[0])) { + $usestask = array($usestask); + } + + foreach ($usestask as $taskdata) { + if (isset($info['Uses Custom Tasks'])) { + $info['Uses Custom Tasks'] .= "\n"; + } else { + $info['Uses Custom Tasks'] = ''; + } + + if (isset($taskdata['package'])) { + $taskpackage = $reg->parsedPackageNameToString($taskdata, true); + } else { + $taskpackage = $taskdata['uri']; + } + $info['Uses Custom Tasks'] .= $taskdata['task'] . ' (' . $taskpackage . ')'; + } + } + + $deps = $obj->getDependencies(); + $info['Required Dependencies'] = 'PHP version ' . $deps['required']['php']['min']; + if (isset($deps['required']['php']['max'])) { + $info['Required Dependencies'] .= '-' . $deps['required']['php']['max'] . "\n"; + } else { + $info['Required Dependencies'] .= "\n"; + } + + if (isset($deps['required']['php']['exclude'])) { + if (!isset($info['Not Compatible with'])) { + $info['Not Compatible with'] = ''; + } else { + $info['Not Compatible with'] .= "\n"; + } + + if (is_array($deps['required']['php']['exclude'])) { + $deps['required']['php']['exclude'] = + implode(', ', $deps['required']['php']['exclude']); + } + $info['Not Compatible with'] .= "PHP versions\n " . + $deps['required']['php']['exclude']; + } + + $info['Required Dependencies'] .= 'PEAR installer version'; + if (isset($deps['required']['pearinstaller']['max'])) { + $info['Required Dependencies'] .= 's ' . + $deps['required']['pearinstaller']['min'] . '-' . + $deps['required']['pearinstaller']['max']; + } else { + $info['Required Dependencies'] .= ' ' . + $deps['required']['pearinstaller']['min'] . ' or newer'; + } + + if (isset($deps['required']['pearinstaller']['exclude'])) { + if (!isset($info['Not Compatible with'])) { + $info['Not Compatible with'] = ''; + } else { + $info['Not Compatible with'] .= "\n"; + } + + if (is_array($deps['required']['pearinstaller']['exclude'])) { + $deps['required']['pearinstaller']['exclude'] = + implode(', ', $deps['required']['pearinstaller']['exclude']); + } + $info['Not Compatible with'] .= "PEAR installer\n Versions " . + $deps['required']['pearinstaller']['exclude']; + } + + foreach (array('Package', 'Extension') as $type) { + $index = strtolower($type); + if (isset($deps['required'][$index])) { + if (isset($deps['required'][$index]['name'])) { + $deps['required'][$index] = array($deps['required'][$index]); + } + + foreach ($deps['required'][$index] as $package) { + if (isset($package['conflicts'])) { + $infoindex = 'Not Compatible with'; + if (!isset($info['Not Compatible with'])) { + $info['Not Compatible with'] = ''; + } else { + $info['Not Compatible with'] .= "\n"; + } + } else { + $infoindex = 'Required Dependencies'; + $info[$infoindex] .= "\n"; + } + + if ($index == 'extension') { + $name = $package['name']; + } else { + if (isset($package['channel'])) { + $name = $package['channel'] . '/' . $package['name']; + } else { + $name = '__uri/' . $package['name'] . ' (static URI)'; + } + } + + $info[$infoindex] .= "$type $name"; + if (isset($package['uri'])) { + $info[$infoindex] .= "\n Download URI: $package[uri]"; + continue; + } + + if (isset($package['max']) && isset($package['min'])) { + $info[$infoindex] .= " \n Versions " . + $package['min'] . '-' . $package['max']; + } elseif (isset($package['min'])) { + $info[$infoindex] .= " \n Version " . + $package['min'] . ' or newer'; + } elseif (isset($package['max'])) { + $info[$infoindex] .= " \n Version " . + $package['max'] . ' or older'; + } + + if (isset($package['recommended'])) { + $info[$infoindex] .= "\n Recommended version: $package[recommended]"; + } + + if (isset($package['exclude'])) { + if (!isset($info['Not Compatible with'])) { + $info['Not Compatible with'] = ''; + } else { + $info['Not Compatible with'] .= "\n"; + } + + if (is_array($package['exclude'])) { + $package['exclude'] = implode(', ', $package['exclude']); + } + + $package['package'] = $package['name']; // for parsedPackageNameToString + if (isset($package['conflicts'])) { + $info['Not Compatible with'] .= '=> except '; + } + $info['Not Compatible with'] .= 'Package ' . + $reg->parsedPackageNameToString($package, true); + $info['Not Compatible with'] .= "\n Versions " . $package['exclude']; + } + } + } + } + + if (isset($deps['required']['os'])) { + if (isset($deps['required']['os']['name'])) { + $dep['required']['os']['name'] = array($dep['required']['os']['name']); + } + + foreach ($dep['required']['os'] as $os) { + if (isset($os['conflicts']) && $os['conflicts'] == 'yes') { + if (!isset($info['Not Compatible with'])) { + $info['Not Compatible with'] = ''; + } else { + $info['Not Compatible with'] .= "\n"; + } + $info['Not Compatible with'] .= "$os[name] Operating System"; + } else { + $info['Required Dependencies'] .= "\n"; + $info['Required Dependencies'] .= "$os[name] Operating System"; + } + } + } + + if (isset($deps['required']['arch'])) { + if (isset($deps['required']['arch']['pattern'])) { + $dep['required']['arch']['pattern'] = array($dep['required']['os']['pattern']); + } + + foreach ($dep['required']['arch'] as $os) { + if (isset($os['conflicts']) && $os['conflicts'] == 'yes') { + if (!isset($info['Not Compatible with'])) { + $info['Not Compatible with'] = ''; + } else { + $info['Not Compatible with'] .= "\n"; + } + $info['Not Compatible with'] .= "OS/Arch matching pattern '/$os[pattern]/'"; + } else { + $info['Required Dependencies'] .= "\n"; + $info['Required Dependencies'] .= "OS/Arch matching pattern '/$os[pattern]/'"; + } + } + } + + if (isset($deps['optional'])) { + foreach (array('Package', 'Extension') as $type) { + $index = strtolower($type); + if (isset($deps['optional'][$index])) { + if (isset($deps['optional'][$index]['name'])) { + $deps['optional'][$index] = array($deps['optional'][$index]); + } + + foreach ($deps['optional'][$index] as $package) { + if (isset($package['conflicts']) && $package['conflicts'] == 'yes') { + $infoindex = 'Not Compatible with'; + if (!isset($info['Not Compatible with'])) { + $info['Not Compatible with'] = ''; + } else { + $info['Not Compatible with'] .= "\n"; + } + } else { + $infoindex = 'Optional Dependencies'; + if (!isset($info['Optional Dependencies'])) { + $info['Optional Dependencies'] = ''; + } else { + $info['Optional Dependencies'] .= "\n"; + } + } + + if ($index == 'extension') { + $name = $package['name']; + } else { + if (isset($package['channel'])) { + $name = $package['channel'] . '/' . $package['name']; + } else { + $name = '__uri/' . $package['name'] . ' (static URI)'; + } + } + + $info[$infoindex] .= "$type $name"; + if (isset($package['uri'])) { + $info[$infoindex] .= "\n Download URI: $package[uri]"; + continue; + } + + if ($infoindex == 'Not Compatible with') { + // conflicts is only used to say that all versions conflict + continue; + } + + if (isset($package['max']) && isset($package['min'])) { + $info[$infoindex] .= " \n Versions " . + $package['min'] . '-' . $package['max']; + } elseif (isset($package['min'])) { + $info[$infoindex] .= " \n Version " . + $package['min'] . ' or newer'; + } elseif (isset($package['max'])) { + $info[$infoindex] .= " \n Version " . + $package['min'] . ' or older'; + } + + if (isset($package['recommended'])) { + $info[$infoindex] .= "\n Recommended version: $package[recommended]"; + } + + if (isset($package['exclude'])) { + if (!isset($info['Not Compatible with'])) { + $info['Not Compatible with'] = ''; + } else { + $info['Not Compatible with'] .= "\n"; + } + + if (is_array($package['exclude'])) { + $package['exclude'] = implode(', ', $package['exclude']); + } + + $info['Not Compatible with'] .= "Package $package\n Versions " . + $package['exclude']; + } + } + } + } + } + + if (isset($deps['group'])) { + if (!isset($deps['group'][0])) { + $deps['group'] = array($deps['group']); + } + + foreach ($deps['group'] as $group) { + $info['Dependency Group ' . $group['attribs']['name']] = $group['attribs']['hint']; + $groupindex = $group['attribs']['name'] . ' Contents'; + $info[$groupindex] = ''; + foreach (array('Package', 'Extension') as $type) { + $index = strtolower($type); + if (isset($group[$index])) { + if (isset($group[$index]['name'])) { + $group[$index] = array($group[$index]); + } + + foreach ($group[$index] as $package) { + if (!empty($info[$groupindex])) { + $info[$groupindex] .= "\n"; + } + + if ($index == 'extension') { + $name = $package['name']; + } else { + if (isset($package['channel'])) { + $name = $package['channel'] . '/' . $package['name']; + } else { + $name = '__uri/' . $package['name'] . ' (static URI)'; + } + } + + if (isset($package['uri'])) { + if (isset($package['conflicts']) && $package['conflicts'] == 'yes') { + $info[$groupindex] .= "Not Compatible with $type $name"; + } else { + $info[$groupindex] .= "$type $name"; + } + + $info[$groupindex] .= "\n Download URI: $package[uri]"; + continue; + } + + if (isset($package['conflicts']) && $package['conflicts'] == 'yes') { + $info[$groupindex] .= "Not Compatible with $type $name"; + continue; + } + + $info[$groupindex] .= "$type $name"; + if (isset($package['max']) && isset($package['min'])) { + $info[$groupindex] .= " \n Versions " . + $package['min'] . '-' . $package['max']; + } elseif (isset($package['min'])) { + $info[$groupindex] .= " \n Version " . + $package['min'] . ' or newer'; + } elseif (isset($package['max'])) { + $info[$groupindex] .= " \n Version " . + $package['min'] . ' or older'; + } + + if (isset($package['recommended'])) { + $info[$groupindex] .= "\n Recommended version: $package[recommended]"; + } + + if (isset($package['exclude'])) { + if (!isset($info['Not Compatible with'])) { + $info['Not Compatible with'] = ''; + } else { + $info[$groupindex] .= "Not Compatible with\n"; + } + + if (is_array($package['exclude'])) { + $package['exclude'] = implode(', ', $package['exclude']); + } + $info[$groupindex] .= " Package $package\n Versions " . + $package['exclude']; + } + } + } + } + } + } + + if ($obj->getPackageType() == 'bundle') { + $info['Bundled Packages'] = ''; + foreach ($obj->getBundledPackages() as $package) { + if (!empty($info['Bundled Packages'])) { + $info['Bundled Packages'] .= "\n"; + } + + if (isset($package['uri'])) { + $info['Bundled Packages'] .= '__uri/' . $package['name']; + $info['Bundled Packages'] .= "\n (URI: $package[uri]"; + } else { + $info['Bundled Packages'] .= $package['channel'] . '/' . $package['name']; + } + } + } + + $info['package.xml version'] = '2.0'; + if ($installed) { + if ($obj->getLastModified()) { + $info['Last Modified'] = date('Y-m-d H:i', $obj->getLastModified()); + } + + $v = $obj->getLastInstalledVersion(); + $info['Previous Installed Version'] = $v ? $v : '- None -'; + } + + foreach ($info as $key => $value) { + $data['data'][] = array($key, $value); + } + + $data['raw'] = $obj->getArray(); // no validation needed + $this->ui->outputData($data, 'package-info'); + } +} \ No newline at end of file diff --git a/includes/pear/PEAR/Command/Registry.xml b/includes/pear/PEAR/Command/Registry.xml new file mode 100644 index 0000000..9f4e214 --- /dev/null +++ b/includes/pear/PEAR/Command/Registry.xml @@ -0,0 +1,58 @@ + + + List Installed Packages In The Default Channel + doList + l + + + c + list installed packages from this channel + CHAN + + + a + list installed packages from all channels + + + i + output fully channel-aware data, even on failure + + + <package> +If invoked without parameters, this command lists the PEAR packages +installed in your php_dir ({config php_dir}). With a parameter, it +lists the files in a package. + + + + List Files In Installed Package + doFileList + fl + + <package> +List the files in an installed package. + + + + Shell Script Test + doShellTest + st + + <package> [[relation] version] +Tests if a package is installed in the system. Will exit(1) if it is not. + <relation> The version comparison operator. One of: + <, lt, <=, le, >, gt, >=, ge, ==, =, eq, !=, <>, ne + <version> The version to compare with + + + + Display information about a package + doInfo + in + + <package> +Displays information about a package. The package argument may be a +local package file, an URL to a package file, or the name of an +installed package. + + \ No newline at end of file diff --git a/includes/pear/PEAR/Command/Remote.php b/includes/pear/PEAR/Command/Remote.php new file mode 100644 index 0000000..52d0aee --- /dev/null +++ b/includes/pear/PEAR/Command/Remote.php @@ -0,0 +1,810 @@ + + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 0.1 + */ + +/** + * base class + */ +require_once 'PEAR/Command/Common.php'; +require_once 'PEAR/REST.php'; + +/** + * PEAR commands for remote server querying + * + * @category pear + * @package PEAR + * @author Stig Bakken + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 0.1 + */ +class PEAR_Command_Remote extends PEAR_Command_Common +{ + var $commands = array( + 'remote-info' => array( + 'summary' => 'Information About Remote Packages', + 'function' => 'doRemoteInfo', + 'shortcut' => 'ri', + 'options' => array(), + 'doc' => ' +Get details on a package from the server.', + ), + 'list-upgrades' => array( + 'summary' => 'List Available Upgrades', + 'function' => 'doListUpgrades', + 'shortcut' => 'lu', + 'options' => array( + 'channelinfo' => array( + 'shortopt' => 'i', + 'doc' => 'output fully channel-aware data, even on failure', + ), + ), + 'doc' => '[preferred_state] +List releases on the server of packages you have installed where +a newer version is available with the same release state (stable etc.) +or the state passed as the second parameter.' + ), + 'remote-list' => array( + 'summary' => 'List Remote Packages', + 'function' => 'doRemoteList', + 'shortcut' => 'rl', + 'options' => array( + 'channel' => + array( + 'shortopt' => 'c', + 'doc' => 'specify a channel other than the default channel', + 'arg' => 'CHAN', + ) + ), + 'doc' => ' +Lists the packages available on the configured server along with the +latest stable release of each package.', + ), + 'search' => array( + 'summary' => 'Search remote package database', + 'function' => 'doSearch', + 'shortcut' => 'sp', + 'options' => array( + 'channel' => + array( + 'shortopt' => 'c', + 'doc' => 'specify a channel other than the default channel', + 'arg' => 'CHAN', + ), + 'allchannels' => array( + 'shortopt' => 'a', + 'doc' => 'search packages from all known channels', + ), + 'channelinfo' => array( + 'shortopt' => 'i', + 'doc' => 'output fully channel-aware data, even on failure', + ), + ), + 'doc' => '[packagename] [packageinfo] +Lists all packages which match the search parameters. The first +parameter is a fragment of a packagename. The default channel +will be used unless explicitly overridden. The second parameter +will be used to match any portion of the summary/description', + ), + 'list-all' => array( + 'summary' => 'List All Packages', + 'function' => 'doListAll', + 'shortcut' => 'la', + 'options' => array( + 'channel' => + array( + 'shortopt' => 'c', + 'doc' => 'specify a channel other than the default channel', + 'arg' => 'CHAN', + ), + 'channelinfo' => array( + 'shortopt' => 'i', + 'doc' => 'output fully channel-aware data, even on failure', + ), + ), + 'doc' => ' +Lists the packages available on the configured server along with the +latest stable release of each package.', + ), + 'download' => array( + 'summary' => 'Download Package', + 'function' => 'doDownload', + 'shortcut' => 'd', + 'options' => array( + 'nocompress' => array( + 'shortopt' => 'Z', + 'doc' => 'download an uncompressed (.tar) file', + ), + ), + 'doc' => '... +Download package tarballs. The files will be named as suggested by the +server, for example if you download the DB package and the latest stable +version of DB is 1.6.5, the downloaded file will be DB-1.6.5.tgz.', + ), + 'clear-cache' => array( + 'summary' => 'Clear Web Services Cache', + 'function' => 'doClearCache', + 'shortcut' => 'cc', + 'options' => array(), + 'doc' => ' +Clear the REST cache. See also the cache_ttl configuration +parameter. +', + ), + ); + + /** + * PEAR_Command_Remote constructor. + * + * @access public + */ + function PEAR_Command_Remote(&$ui, &$config) + { + parent::PEAR_Command_Common($ui, $config); + } + + function _checkChannelForStatus($channel, $chan) + { + if (PEAR::isError($chan)) { + $this->raiseError($chan); + } + if (!is_a($chan, 'PEAR_ChannelFile')) { + return $this->raiseError('Internal corruption error: invalid channel "' . + $channel . '"'); + } + $rest = new PEAR_REST($this->config); + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $mirror = $this->config->get('preferred_mirror', null, + $channel); + $a = $rest->downloadHttp('http://' . $channel . + '/channel.xml', $chan->lastModified()); + PEAR::staticPopErrorHandling(); + if (!PEAR::isError($a) && $a) { + $this->ui->outputData('WARNING: channel "' . $channel . '" has ' . + 'updated its protocols, use "' . PEAR_RUNTYPE . ' channel-update ' . $channel . + '" to update'); + } + } + + function doRemoteInfo($command, $options, $params) + { + if (sizeof($params) != 1) { + return $this->raiseError("$command expects one param: the remote package name"); + } + $savechannel = $channel = $this->config->get('default_channel'); + $reg = &$this->config->getRegistry(); + $package = $params[0]; + $parsed = $reg->parsePackageName($package, $channel); + if (PEAR::isError($parsed)) { + return $this->raiseError('Invalid package name "' . $package . '"'); + } + + $channel = $parsed['channel']; + $this->config->set('default_channel', $channel); + $chan = $reg->getChannel($channel); + if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) { + return $e; + } + + $mirror = $this->config->get('preferred_mirror'); + if ($chan->supportsREST($mirror) && $base = $chan->getBaseURL('REST1.0', $mirror)) { + $rest = &$this->config->getREST('1.0', array()); + $info = $rest->packageInfo($base, $parsed['package'], $channel); + } + + if (!isset($info)) { + return $this->raiseError('No supported protocol was found'); + } + + if (PEAR::isError($info)) { + $this->config->set('default_channel', $savechannel); + return $this->raiseError($info); + } + + if (!isset($info['name'])) { + return $this->raiseError('No remote package "' . $package . '" was found'); + } + + $installed = $reg->packageInfo($info['name'], null, $channel); + $info['installed'] = $installed['version'] ? $installed['version'] : '- no -'; + if (is_array($info['installed'])) { + $info['installed'] = $info['installed']['release']; + } + + $this->ui->outputData($info, $command); + $this->config->set('default_channel', $savechannel); + + return true; + } + + function doRemoteList($command, $options, $params) + { + $savechannel = $channel = $this->config->get('default_channel'); + $reg = &$this->config->getRegistry(); + if (isset($options['channel'])) { + $channel = $options['channel']; + if (!$reg->channelExists($channel)) { + return $this->raiseError('Channel "' . $channel . '" does not exist'); + } + + $this->config->set('default_channel', $channel); + } + + $chan = $reg->getChannel($channel); + if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) { + return $e; + } + + $list_options = false; + if ($this->config->get('preferred_state') == 'stable') { + $list_options = true; + } + + $available = array(); + if ($chan->supportsREST($this->config->get('preferred_mirror')) && + $base = $chan->getBaseURL('REST1.1', $this->config->get('preferred_mirror')) + ) { + // use faster list-all if available + $rest = &$this->config->getREST('1.1', array()); + $available = $rest->listAll($base, $list_options, true, false, false, $chan->getName()); + } elseif ($chan->supportsREST($this->config->get('preferred_mirror')) && + $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) { + $rest = &$this->config->getREST('1.0', array()); + $available = $rest->listAll($base, $list_options, true, false, false, $chan->getName()); + } + + if (PEAR::isError($available)) { + $this->config->set('default_channel', $savechannel); + return $this->raiseError($available); + } + + $i = $j = 0; + $data = array( + 'caption' => 'Channel ' . $channel . ' Available packages:', + 'border' => true, + 'headline' => array('Package', 'Version'), + 'channel' => $channel + ); + + if (count($available) == 0) { + $data = '(no packages available yet)'; + } else { + foreach ($available as $name => $info) { + $version = (isset($info['stable']) && $info['stable']) ? $info['stable'] : '-n/a-'; + $data['data'][] = array($name, $version); + } + } + $this->ui->outputData($data, $command); + $this->config->set('default_channel', $savechannel); + return true; + } + + function doListAll($command, $options, $params) + { + $savechannel = $channel = $this->config->get('default_channel'); + $reg = &$this->config->getRegistry(); + if (isset($options['channel'])) { + $channel = $options['channel']; + if (!$reg->channelExists($channel)) { + return $this->raiseError("Channel \"$channel\" does not exist"); + } + + $this->config->set('default_channel', $channel); + } + + $list_options = false; + if ($this->config->get('preferred_state') == 'stable') { + $list_options = true; + } + + $chan = $reg->getChannel($channel); + if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) { + return $e; + } + + if ($chan->supportsREST($this->config->get('preferred_mirror')) && + $base = $chan->getBaseURL('REST1.1', $this->config->get('preferred_mirror'))) { + // use faster list-all if available + $rest = &$this->config->getREST('1.1', array()); + $available = $rest->listAll($base, $list_options, false, false, false, $chan->getName()); + } elseif ($chan->supportsREST($this->config->get('preferred_mirror')) && + $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) { + $rest = &$this->config->getREST('1.0', array()); + $available = $rest->listAll($base, $list_options, false, false, false, $chan->getName()); + } + + if (PEAR::isError($available)) { + $this->config->set('default_channel', $savechannel); + return $this->raiseError('The package list could not be fetched from the remote server. Please try again. (Debug info: "' . $available->getMessage() . '")'); + } + + $data = array( + 'caption' => 'All packages [Channel ' . $channel . ']:', + 'border' => true, + 'headline' => array('Package', 'Latest', 'Local'), + 'channel' => $channel, + ); + + if (isset($options['channelinfo'])) { + // add full channelinfo + $data['caption'] = 'Channel ' . $channel . ' All packages:'; + $data['headline'] = array('Channel', 'Package', 'Latest', 'Local', + 'Description', 'Dependencies'); + } + $local_pkgs = $reg->listPackages($channel); + + foreach ($available as $name => $info) { + $installed = $reg->packageInfo($name, null, $channel); + if (is_array($installed['version'])) { + $installed['version'] = $installed['version']['release']; + } + $desc = $info['summary']; + if (isset($params[$name])) { + $desc .= "\n\n".$info['description']; + } + + if (isset($options['mode'])) { + if ($options['mode'] == 'installed' && !isset($installed['version'])) { + continue; + } + if ($options['mode'] == 'notinstalled' && isset($installed['version'])) { + continue; + } + if ($options['mode'] == 'upgrades' + && (!isset($installed['version']) || version_compare($installed['version'], + $info['stable'], '>='))) { + continue; + } + } + $pos = array_search(strtolower($name), $local_pkgs); + if ($pos !== false) { + unset($local_pkgs[$pos]); + } + + if (isset($info['stable']) && !$info['stable']) { + $info['stable'] = null; + } + + if (isset($options['channelinfo'])) { + // add full channelinfo + if ($info['stable'] === $info['unstable']) { + $state = $info['state']; + } else { + $state = 'stable'; + } + $latest = $info['stable'].' ('.$state.')'; + $local = ''; + if (isset($installed['version'])) { + $inst_state = $reg->packageInfo($name, 'release_state', $channel); + $local = $installed['version'].' ('.$inst_state.')'; + } + + $packageinfo = array( + $channel, + $name, + $latest, + $local, + isset($desc) ? $desc : null, + isset($info['deps']) ? $info['deps'] : null, + ); + } else { + $packageinfo = array( + $reg->channelAlias($channel) . '/' . $name, + isset($info['stable']) ? $info['stable'] : null, + isset($installed['version']) ? $installed['version'] : null, + isset($desc) ? $desc : null, + isset($info['deps']) ? $info['deps'] : null, + ); + } + $data['data'][$info['category']][] = $packageinfo; + } + + if (isset($options['mode']) && in_array($options['mode'], array('notinstalled', 'upgrades'))) { + $this->config->set('default_channel', $savechannel); + $this->ui->outputData($data, $command); + return true; + } + + foreach ($local_pkgs as $name) { + $info = &$reg->getPackage($name, $channel); + $data['data']['Local'][] = array( + $reg->channelAlias($channel) . '/' . $info->getPackage(), + '', + $info->getVersion(), + $info->getSummary(), + $info->getDeps() + ); + } + + $this->config->set('default_channel', $savechannel); + $this->ui->outputData($data, $command); + return true; + } + + function doSearch($command, $options, $params) + { + if ((!isset($params[0]) || empty($params[0])) + && (!isset($params[1]) || empty($params[1]))) + { + return $this->raiseError('no valid search string supplied'); + } + + $channelinfo = isset($options['channelinfo']); + $reg = &$this->config->getRegistry(); + if (isset($options['allchannels'])) { + // search all channels + unset($options['allchannels']); + $channels = $reg->getChannels(); + $errors = array(); + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + foreach ($channels as $channel) { + if ($channel->getName() != '__uri') { + $options['channel'] = $channel->getName(); + $ret = $this->doSearch($command, $options, $params); + if (PEAR::isError($ret)) { + $errors[] = $ret; + } + } + } + + PEAR::staticPopErrorHandling(); + if (count($errors) !== 0) { + // for now, only give first error + return PEAR::raiseError($errors[0]); + } + + return true; + } + + $savechannel = $channel = $this->config->get('default_channel'); + $package = strtolower($params[0]); + $summary = isset($params[1]) ? $params[1] : false; + if (isset($options['channel'])) { + $reg = &$this->config->getRegistry(); + $channel = $options['channel']; + if (!$reg->channelExists($channel)) { + return $this->raiseError('Channel "' . $channel . '" does not exist'); + } + + $this->config->set('default_channel', $channel); + } + + $chan = $reg->getChannel($channel); + if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) { + return $e; + } + + if ($chan->supportsREST($this->config->get('preferred_mirror')) && + $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) { + $rest = &$this->config->getREST('1.0', array()); + $available = $rest->listAll($base, false, false, $package, $summary, $chan->getName()); + } + + if (PEAR::isError($available)) { + $this->config->set('default_channel', $savechannel); + return $this->raiseError($available); + } + + if (!$available && !$channelinfo) { + // clean exit when not found, no error ! + $data = 'no packages found that match pattern "' . $package . '", for channel '.$channel.'.'; + $this->ui->outputData($data); + $this->config->set('default_channel', $channel); + return true; + } + + if ($channelinfo) { + $data = array( + 'caption' => 'Matched packages, channel ' . $channel . ':', + 'border' => true, + 'headline' => array('Channel', 'Package', 'Stable/(Latest)', 'Local'), + 'channel' => $channel + ); + } else { + $data = array( + 'caption' => 'Matched packages, channel ' . $channel . ':', + 'border' => true, + 'headline' => array('Package', 'Stable/(Latest)', 'Local'), + 'channel' => $channel + ); + } + + if (!$available && $channelinfo) { + unset($data['headline']); + $data['data'] = 'No packages found that match pattern "' . $package . '".'; + $available = array(); + } + + foreach ($available as $name => $info) { + $installed = $reg->packageInfo($name, null, $channel); + $desc = $info['summary']; + if (isset($params[$name])) + $desc .= "\n\n".$info['description']; + + if (!isset($info['stable']) || !$info['stable']) { + $version_remote = 'none'; + } else { + if ($info['unstable']) { + $version_remote = $info['unstable']; + } else { + $version_remote = $info['stable']; + } + $version_remote .= ' ('.$info['state'].')'; + } + $version = is_array($installed['version']) ? $installed['version']['release'] : + $installed['version']; + if ($channelinfo) { + $packageinfo = array( + $channel, + $name, + $version_remote, + $version, + $desc, + ); + } else { + $packageinfo = array( + $name, + $version_remote, + $version, + $desc, + ); + } + $data['data'][$info['category']][] = $packageinfo; + } + + $this->ui->outputData($data, $command); + $this->config->set('default_channel', $channel); + return true; + } + + function &getDownloader($options) + { + if (!class_exists('PEAR_Downloader')) { + require_once 'PEAR/Downloader.php'; + } + $a = &new PEAR_Downloader($this->ui, $options, $this->config); + return $a; + } + + function doDownload($command, $options, $params) + { + // make certain that dependencies are ignored + $options['downloadonly'] = 1; + + // eliminate error messages for preferred_state-related errors + /* TODO: Should be an option, but until now download does respect + prefered state */ + /* $options['ignorepreferred_state'] = 1; */ + // eliminate error messages for preferred_state-related errors + + $downloader = &$this->getDownloader($options); + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $e = $downloader->setDownloadDir(getcwd()); + PEAR::staticPopErrorHandling(); + if (PEAR::isError($e)) { + return $this->raiseError('Current directory is not writeable, cannot download'); + } + + $errors = array(); + $downloaded = array(); + $err = $downloader->download($params); + if (PEAR::isError($err)) { + return $err; + } + + $errors = $downloader->getErrorMsgs(); + if (count($errors)) { + foreach ($errors as $error) { + if ($error !== null) { + $this->ui->outputData($error); + } + } + + return $this->raiseError("$command failed"); + } + + $downloaded = $downloader->getDownloadedPackages(); + foreach ($downloaded as $pkg) { + $this->ui->outputData("File $pkg[file] downloaded", $command); + } + + return true; + } + + function downloadCallback($msg, $params = null) + { + if ($msg == 'done') { + $this->bytes_downloaded = $params; + } + } + + function doListUpgrades($command, $options, $params) + { + require_once 'PEAR/Common.php'; + if (isset($params[0]) && !is_array(PEAR_Common::betterStates($params[0]))) { + return $this->raiseError($params[0] . ' is not a valid state (stable/beta/alpha/devel/etc.) try "pear help list-upgrades"'); + } + + $savechannel = $channel = $this->config->get('default_channel'); + $reg = &$this->config->getRegistry(); + foreach ($reg->listChannels() as $channel) { + $inst = array_flip($reg->listPackages($channel)); + if (!count($inst)) { + continue; + } + + if ($channel == '__uri') { + continue; + } + + $this->config->set('default_channel', $channel); + $state = empty($params[0]) ? $this->config->get('preferred_state') : $params[0]; + + $caption = $channel . ' Available Upgrades'; + $chan = $reg->getChannel($channel); + if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) { + return $e; + } + + $latest = array(); + $base2 = false; + $preferred_mirror = $this->config->get('preferred_mirror'); + if ($chan->supportsREST($preferred_mirror) && + ( + //($base2 = $chan->getBaseURL('REST1.4', $preferred_mirror)) || + ($base = $chan->getBaseURL('REST1.0', $preferred_mirror)) + ) + + ) { + if ($base2) { + $rest = &$this->config->getREST('1.4', array()); + $base = $base2; + } else { + $rest = &$this->config->getREST('1.0', array()); + } + + if (empty($state) || $state == 'any') { + $state = false; + } else { + $caption .= ' (' . implode(', ', PEAR_Common::betterStates($state, true)) . ')'; + } + + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $latest = $rest->listLatestUpgrades($base, $state, $inst, $channel, $reg); + PEAR::staticPopErrorHandling(); + } + + if (PEAR::isError($latest)) { + $this->ui->outputData($latest->getMessage()); + continue; + } + + $caption .= ':'; + if (PEAR::isError($latest)) { + $this->config->set('default_channel', $savechannel); + return $latest; + } + + $data = array( + 'caption' => $caption, + 'border' => 1, + 'headline' => array('Channel', 'Package', 'Local', 'Remote', 'Size'), + 'channel' => $channel + ); + + foreach ((array)$latest as $pkg => $info) { + $package = strtolower($pkg); + if (!isset($inst[$package])) { + // skip packages we don't have installed + continue; + } + + extract($info); + $inst_version = $reg->packageInfo($package, 'version', $channel); + $inst_state = $reg->packageInfo($package, 'release_state', $channel); + if (version_compare("$version", "$inst_version", "le")) { + // installed version is up-to-date + continue; + } + + if ($filesize >= 20480) { + $filesize += 1024 - ($filesize % 1024); + $fs = sprintf("%dkB", $filesize / 1024); + } elseif ($filesize > 0) { + $filesize += 103 - ($filesize % 103); + $fs = sprintf("%.1fkB", $filesize / 1024.0); + } else { + $fs = " -"; // XXX center instead + } + + $data['data'][] = array($channel, $pkg, "$inst_version ($inst_state)", "$version ($state)", $fs); + } + + if (isset($options['channelinfo'])) { + if (empty($data['data'])) { + unset($data['headline']); + if (count($inst) == 0) { + $data['data'] = '(no packages installed)'; + } else { + $data['data'] = '(no upgrades available)'; + } + } + $this->ui->outputData($data, $command); + } else { + if (empty($data['data'])) { + $this->ui->outputData('Channel ' . $channel . ': No upgrades available'); + } else { + $this->ui->outputData($data, $command); + } + } + } + + $this->config->set('default_channel', $savechannel); + return true; + } + + function doClearCache($command, $options, $params) + { + $cache_dir = $this->config->get('cache_dir'); + $verbose = $this->config->get('verbose'); + $output = ''; + if (!file_exists($cache_dir) || !is_dir($cache_dir)) { + return $this->raiseError("$cache_dir does not exist or is not a directory"); + } + + if (!($dp = @opendir($cache_dir))) { + return $this->raiseError("opendir($cache_dir) failed: $php_errormsg"); + } + + if ($verbose >= 1) { + $output .= "reading directory $cache_dir\n"; + } + + $num = 0; + while ($ent = readdir($dp)) { + if (preg_match('/rest.cache(file|id)\\z/', $ent)) { + $path = $cache_dir . DIRECTORY_SEPARATOR . $ent; + if (file_exists($path)) { + $ok = @unlink($path); + } else { + $ok = false; + $php_errormsg = ''; + } + + if ($ok) { + if ($verbose >= 2) { + $output .= "deleted $path\n"; + } + $num++; + } elseif ($verbose >= 1) { + $output .= "failed to delete $path $php_errormsg\n"; + } + } + } + + closedir($dp); + if ($verbose >= 1) { + $output .= "$num cache entries cleared\n"; + } + + $this->ui->outputData(rtrim($output), $command); + return $num; + } +} \ No newline at end of file diff --git a/includes/pear/PEAR/Command/Remote.xml b/includes/pear/PEAR/Command/Remote.xml new file mode 100644 index 0000000..b4f6100 --- /dev/null +++ b/includes/pear/PEAR/Command/Remote.xml @@ -0,0 +1,109 @@ + + + Information About Remote Packages + doRemoteInfo + ri + + <package> +Get details on a package from the server. + + + List Available Upgrades + doListUpgrades + lu + + + i + output fully channel-aware data, even on failure + + + [preferred_state] +List releases on the server of packages you have installed where +a newer version is available with the same release state (stable etc.) +or the state passed as the second parameter. + + + List Remote Packages + doRemoteList + rl + + + c + specify a channel other than the default channel + CHAN + + + +Lists the packages available on the configured server along with the +latest stable release of each package. + + + Search remote package database + doSearch + sp + + + c + specify a channel other than the default channel + CHAN + + + a + search packages from all known channels + + + i + output fully channel-aware data, even on failure + + + [packagename] [packageinfo] +Lists all packages which match the search parameters. The first +parameter is a fragment of a packagename. The default channel +will be used unless explicitly overridden. The second parameter +will be used to match any portion of the summary/description + + + List All Packages + doListAll + la + + + c + specify a channel other than the default channel + CHAN + + + i + output fully channel-aware data, even on failure + + + +Lists the packages available on the configured server along with the +latest stable release of each package. + + + Download Package + doDownload + d + + + Z + download an uncompressed (.tar) file + + + <package>... +Download package tarballs. The files will be named as suggested by the +server, for example if you download the DB package and the latest stable +version of DB is 1.6.5, the downloaded file will be DB-1.6.5.tgz. + + + Clear Web Services Cache + doClearCache + cc + + +Clear the XML-RPC/REST cache. See also the cache_ttl configuration +parameter. + + + \ No newline at end of file diff --git a/includes/pear/PEAR/Command/Test.php b/includes/pear/PEAR/Command/Test.php new file mode 100644 index 0000000..fcfa6f6 --- /dev/null +++ b/includes/pear/PEAR/Command/Test.php @@ -0,0 +1,337 @@ + + * @author Martin Jansen + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 0.1 + */ + +/** + * base class + */ +require_once 'PEAR/Command/Common.php'; + +/** + * PEAR commands for login/logout + * + * @category pear + * @package PEAR + * @author Stig Bakken + * @author Martin Jansen + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 0.1 + */ + +class PEAR_Command_Test extends PEAR_Command_Common +{ + var $commands = array( + 'run-tests' => array( + 'summary' => 'Run Regression Tests', + 'function' => 'doRunTests', + 'shortcut' => 'rt', + 'options' => array( + 'recur' => array( + 'shortopt' => 'r', + 'doc' => 'Run tests in child directories, recursively. 4 dirs deep maximum', + ), + 'ini' => array( + 'shortopt' => 'i', + 'doc' => 'actual string of settings to pass to php in format " -d setting=blah"', + 'arg' => 'SETTINGS' + ), + 'realtimelog' => array( + 'shortopt' => 'l', + 'doc' => 'Log test runs/results as they are run', + ), + 'quiet' => array( + 'shortopt' => 'q', + 'doc' => 'Only display detail for failed tests', + ), + 'simple' => array( + 'shortopt' => 's', + 'doc' => 'Display simple output for all tests', + ), + 'package' => array( + 'shortopt' => 'p', + 'doc' => 'Treat parameters as installed packages from which to run tests', + ), + 'phpunit' => array( + 'shortopt' => 'u', + 'doc' => 'Search parameters for AllTests.php, and use that to run phpunit-based tests +If none is found, all .phpt tests will be tried instead.', + ), + 'tapoutput' => array( + 'shortopt' => 't', + 'doc' => 'Output run-tests.log in TAP-compliant format', + ), + 'cgi' => array( + 'shortopt' => 'c', + 'doc' => 'CGI php executable (needed for tests with POST/GET section)', + 'arg' => 'PHPCGI', + ), + 'coverage' => array( + 'shortopt' => 'x', + 'doc' => 'Generate a code coverage report (requires Xdebug 2.0.0+)', + ), + ), + 'doc' => '[testfile|dir ...] +Run regression tests with PHP\'s regression testing script (run-tests.php).', + ), + ); + + var $output; + + /** + * PEAR_Command_Test constructor. + * + * @access public + */ + function PEAR_Command_Test(&$ui, &$config) + { + parent::PEAR_Command_Common($ui, $config); + } + + function doRunTests($command, $options, $params) + { + if (isset($options['phpunit']) && isset($options['tapoutput'])) { + return $this->raiseError('ERROR: cannot use both --phpunit and --tapoutput at the same time'); + } + + require_once 'PEAR/Common.php'; + require_once 'System.php'; + $log = new PEAR_Common; + $log->ui = &$this->ui; // slightly hacky, but it will work + $tests = array(); + $depth = isset($options['recur']) ? 14 : 1; + + if (!count($params)) { + $params[] = '.'; + } + + if (isset($options['package'])) { + $oldparams = $params; + $params = array(); + $reg = &$this->config->getRegistry(); + foreach ($oldparams as $param) { + $pname = $reg->parsePackageName($param, $this->config->get('default_channel')); + if (PEAR::isError($pname)) { + return $this->raiseError($pname); + } + + $package = &$reg->getPackage($pname['package'], $pname['channel']); + if (!$package) { + return PEAR::raiseError('Unknown package "' . + $reg->parsedPackageNameToString($pname) . '"'); + } + + $filelist = $package->getFilelist(); + foreach ($filelist as $name => $atts) { + if (isset($atts['role']) && $atts['role'] != 'test') { + continue; + } + + if (isset($options['phpunit']) && preg_match('/AllTests\.php\\z/i', $name)) { + $params[] = $atts['installed_as']; + continue; + } elseif (!preg_match('/\.phpt\\z/', $name)) { + continue; + } + $params[] = $atts['installed_as']; + } + } + } + + foreach ($params as $p) { + if (is_dir($p)) { + if (isset($options['phpunit'])) { + $dir = System::find(array($p, '-type', 'f', + '-maxdepth', $depth, + '-name', 'AllTests.php')); + if (count($dir)) { + foreach ($dir as $p) { + $p = realpath($p); + if (!count($tests) || + (count($tests) && strlen($p) < strlen($tests[0]))) { + // this is in a higher-level directory, use this one instead. + $tests = array($p); + } + } + } + continue; + } + + $args = array($p, '-type', 'f', '-name', '*.phpt'); + } else { + if (isset($options['phpunit'])) { + if (preg_match('/AllTests\.php\\z/i', $p)) { + $p = realpath($p); + if (!count($tests) || + (count($tests) && strlen($p) < strlen($tests[0]))) { + // this is in a higher-level directory, use this one instead. + $tests = array($p); + } + } + continue; + } + + if (file_exists($p) && preg_match('/\.phpt$/', $p)) { + $tests[] = $p; + continue; + } + + if (!preg_match('/\.phpt\\z/', $p)) { + $p .= '.phpt'; + } + + $args = array(dirname($p), '-type', 'f', '-name', $p); + } + + if (!isset($options['recur'])) { + $args[] = '-maxdepth'; + $args[] = 1; + } + + $dir = System::find($args); + $tests = array_merge($tests, $dir); + } + + $ini_settings = ''; + if (isset($options['ini'])) { + $ini_settings .= $options['ini']; + } + + if (isset($_ENV['TEST_PHP_INCLUDE_PATH'])) { + $ini_settings .= " -d include_path={$_ENV['TEST_PHP_INCLUDE_PATH']}"; + } + + if ($ini_settings) { + $this->ui->outputData('Using INI settings: "' . $ini_settings . '"'); + } + + $skipped = $passed = $failed = array(); + $tests_count = count($tests); + $this->ui->outputData('Running ' . $tests_count . ' tests', $command); + $start = time(); + if (isset($options['realtimelog']) && file_exists('run-tests.log')) { + unlink('run-tests.log'); + } + + if (isset($options['tapoutput'])) { + $tap = '1..' . $tests_count . "\n"; + } + + require_once 'PEAR/RunTest.php'; + $run = new PEAR_RunTest($log, $options); + $run->tests_count = $tests_count; + + if (isset($options['coverage']) && extension_loaded('xdebug')){ + $run->xdebug_loaded = true; + } else { + $run->xdebug_loaded = false; + } + + $j = $i = 1; + foreach ($tests as $t) { + if (isset($options['realtimelog'])) { + $fp = @fopen('run-tests.log', 'a'); + if ($fp) { + fwrite($fp, "Running test [$i / $tests_count] $t..."); + fclose($fp); + } + } + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + if (isset($options['phpunit'])) { + $result = $run->runPHPUnit($t, $ini_settings); + } else { + $result = $run->run($t, $ini_settings, $j); + } + PEAR::staticPopErrorHandling(); + if (PEAR::isError($result)) { + $this->ui->log($result->getMessage()); + continue; + } + + if (isset($options['tapoutput'])) { + $tap .= $result[0] . ' ' . $i . $result[1] . "\n"; + continue; + } + + if (isset($options['realtimelog'])) { + $fp = @fopen('run-tests.log', 'a'); + if ($fp) { + fwrite($fp, "$result\n"); + fclose($fp); + } + } + + if ($result == 'FAILED') { + $failed[] = $t; + } + if ($result == 'PASSED') { + $passed[] = $t; + } + if ($result == 'SKIPPED') { + $skipped[] = $t; + } + + $j++; + } + + $total = date('i:s', time() - $start); + if (isset($options['tapoutput'])) { + $fp = @fopen('run-tests.log', 'w'); + if ($fp) { + fwrite($fp, $tap, strlen($tap)); + fclose($fp); + $this->ui->outputData('wrote TAP-format log to "' .realpath('run-tests.log') . + '"', $command); + } + } else { + if (count($failed)) { + $output = "TOTAL TIME: $total\n"; + $output .= count($passed) . " PASSED TESTS\n"; + $output .= count($skipped) . " SKIPPED TESTS\n"; + $output .= count($failed) . " FAILED TESTS:\n"; + foreach ($failed as $failure) { + $output .= $failure . "\n"; + } + + $mode = isset($options['realtimelog']) ? 'a' : 'w'; + $fp = @fopen('run-tests.log', $mode); + + if ($fp) { + fwrite($fp, $output, strlen($output)); + fclose($fp); + $this->ui->outputData('wrote log to "' . realpath('run-tests.log') . '"', $command); + } + } elseif (file_exists('run-tests.log') && !is_dir('run-tests.log')) { + @unlink('run-tests.log'); + } + } + $this->ui->outputData('TOTAL TIME: ' . $total); + $this->ui->outputData(count($passed) . ' PASSED TESTS', $command); + $this->ui->outputData(count($skipped) . ' SKIPPED TESTS', $command); + if (count($failed)) { + $this->ui->outputData(count($failed) . ' FAILED TESTS:', $command); + foreach ($failed as $failure) { + $this->ui->outputData($failure, $command); + } + } + + return true; + } +} \ No newline at end of file diff --git a/includes/pear/PEAR/Command/Test.xml b/includes/pear/PEAR/Command/Test.xml new file mode 100644 index 0000000..bbe9fcc --- /dev/null +++ b/includes/pear/PEAR/Command/Test.xml @@ -0,0 +1,54 @@ + + + Run Regression Tests + doRunTests + rt + + + r + Run tests in child directories, recursively. 4 dirs deep maximum + + + i + actual string of settings to pass to php in format " -d setting=blah" + SETTINGS + + + l + Log test runs/results as they are run + + + q + Only display detail for failed tests + + + s + Display simple output for all tests + + + p + Treat parameters as installed packages from which to run tests + + + u + Search parameters for AllTests.php, and use that to run phpunit-based tests +If none is found, all .phpt tests will be tried instead. + + + t + Output run-tests.log in TAP-compliant format + + + c + CGI php executable (needed for tests with POST/GET section) + PHPCGI + + + x + Generate a code coverage report (requires Xdebug 2.0.0+) + + + [testfile|dir ...] +Run regression tests with PHP's regression testing script (run-tests.php). + + \ No newline at end of file diff --git a/includes/pear/PEAR/Common.php b/includes/pear/PEAR/Common.php new file mode 100644 index 0000000..f98bf2c --- /dev/null +++ b/includes/pear/PEAR/Common.php @@ -0,0 +1,837 @@ + + * @author Tomas V. V. Cox + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 0.1.0 + * @deprecated File deprecated since Release 1.4.0a1 + */ + +/** + * Include error handling + */ +require_once 'PEAR.php'; + +/** + * PEAR_Common error when an invalid PHP file is passed to PEAR_Common::analyzeSourceCode() + */ +define('PEAR_COMMON_ERROR_INVALIDPHP', 1); +define('_PEAR_COMMON_PACKAGE_NAME_PREG', '[A-Za-z][a-zA-Z0-9_]+'); +define('PEAR_COMMON_PACKAGE_NAME_PREG', '/^' . _PEAR_COMMON_PACKAGE_NAME_PREG . '\\z/'); + +// this should allow: 1, 1.0, 1.0RC1, 1.0dev, 1.0dev123234234234, 1.0a1, 1.0b1, 1.0pl1 +define('_PEAR_COMMON_PACKAGE_VERSION_PREG', '\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?'); +define('PEAR_COMMON_PACKAGE_VERSION_PREG', '/^' . _PEAR_COMMON_PACKAGE_VERSION_PREG . '\\z/i'); + +// XXX far from perfect :-) +define('_PEAR_COMMON_PACKAGE_DOWNLOAD_PREG', '(' . _PEAR_COMMON_PACKAGE_NAME_PREG . + ')(-([.0-9a-zA-Z]+))?'); +define('PEAR_COMMON_PACKAGE_DOWNLOAD_PREG', '/^' . _PEAR_COMMON_PACKAGE_DOWNLOAD_PREG . + '\\z/'); + +define('_PEAR_CHANNELS_NAME_PREG', '[A-Za-z][a-zA-Z0-9\.]+'); +define('PEAR_CHANNELS_NAME_PREG', '/^' . _PEAR_CHANNELS_NAME_PREG . '\\z/'); + +// this should allow any dns or IP address, plus a path - NO UNDERSCORES ALLOWED +define('_PEAR_CHANNELS_SERVER_PREG', '[a-zA-Z0-9\-]+(?:\.[a-zA-Z0-9\-]+)*(\/[a-zA-Z0-9\-]+)*'); +define('PEAR_CHANNELS_SERVER_PREG', '/^' . _PEAR_CHANNELS_SERVER_PREG . '\\z/i'); + +define('_PEAR_CHANNELS_PACKAGE_PREG', '(' ._PEAR_CHANNELS_SERVER_PREG . ')\/(' + . _PEAR_COMMON_PACKAGE_NAME_PREG . ')'); +define('PEAR_CHANNELS_PACKAGE_PREG', '/^' . _PEAR_CHANNELS_PACKAGE_PREG . '\\z/i'); + +define('_PEAR_COMMON_CHANNEL_DOWNLOAD_PREG', '(' . _PEAR_CHANNELS_NAME_PREG . ')::(' + . _PEAR_COMMON_PACKAGE_NAME_PREG . ')(-([.0-9a-zA-Z]+))?'); +define('PEAR_COMMON_CHANNEL_DOWNLOAD_PREG', '/^' . _PEAR_COMMON_CHANNEL_DOWNLOAD_PREG . '\\z/'); + +/** + * List of temporary files and directories registered by + * PEAR_Common::addTempFile(). + * @var array + */ +$GLOBALS['_PEAR_Common_tempfiles'] = array(); + +/** + * Valid maintainer roles + * @var array + */ +$GLOBALS['_PEAR_Common_maintainer_roles'] = array('lead','developer','contributor','helper'); + +/** + * Valid release states + * @var array + */ +$GLOBALS['_PEAR_Common_release_states'] = array('alpha','beta','stable','snapshot','devel'); + +/** + * Valid dependency types + * @var array + */ +$GLOBALS['_PEAR_Common_dependency_types'] = array('pkg','ext','php','prog','ldlib','rtlib','os','websrv','sapi'); + +/** + * Valid dependency relations + * @var array + */ +$GLOBALS['_PEAR_Common_dependency_relations'] = array('has','eq','lt','le','gt','ge','not', 'ne'); + +/** + * Valid file roles + * @var array + */ +$GLOBALS['_PEAR_Common_file_roles'] = array('php','ext','test','doc','data','src','script'); + +/** + * Valid replacement types + * @var array + */ +$GLOBALS['_PEAR_Common_replacement_types'] = array('php-const', 'pear-config', 'package-info'); + +/** + * Valid "provide" types + * @var array + */ +$GLOBALS['_PEAR_Common_provide_types'] = array('ext', 'prog', 'class', 'function', 'feature', 'api'); + +/** + * Valid "provide" types + * @var array + */ +$GLOBALS['_PEAR_Common_script_phases'] = array('pre-install', 'post-install', 'pre-uninstall', 'post-uninstall', 'pre-build', 'post-build', 'pre-configure', 'post-configure', 'pre-setup', 'post-setup'); + +/** + * Class providing common functionality for PEAR administration classes. + * @category pear + * @package PEAR + * @author Stig Bakken + * @author Tomas V. V. Cox + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a1 + * @deprecated This class will disappear, and its components will be spread + * into smaller classes, like the AT&T breakup, as of Release 1.4.0a1 + */ +class PEAR_Common extends PEAR +{ + /** + * User Interface object (PEAR_Frontend_* class). If null, + * the log() method uses print. + * @var object + */ + var $ui = null; + + /** + * Configuration object (PEAR_Config). + * @var PEAR_Config + */ + var $config = null; + + /** stack of elements, gives some sort of XML context */ + var $element_stack = array(); + + /** name of currently parsed XML element */ + var $current_element; + + /** array of attributes of the currently parsed XML element */ + var $current_attributes = array(); + + /** assoc with information about a package */ + var $pkginfo = array(); + + var $current_path = null; + + /** + * Flag variable used to mark a valid package file + * @var boolean + * @access private + */ + var $_validPackageFile; + + /** + * PEAR_Common constructor + * + * @access public + */ + function PEAR_Common() + { + parent::PEAR(); + $this->config = &PEAR_Config::singleton(); + $this->debug = $this->config->get('verbose'); + } + + /** + * PEAR_Common destructor + * + * @access private + */ + function _PEAR_Common() + { + // doesn't work due to bug #14744 + //$tempfiles = $this->_tempfiles; + $tempfiles =& $GLOBALS['_PEAR_Common_tempfiles']; + while ($file = array_shift($tempfiles)) { + if (@is_dir($file)) { + if (!class_exists('System')) { + require_once 'System.php'; + } + + System::rm(array('-rf', $file)); + } elseif (file_exists($file)) { + unlink($file); + } + } + } + + /** + * Register a temporary file or directory. When the destructor is + * executed, all registered temporary files and directories are + * removed. + * + * @param string $file name of file or directory + * + * @return void + * + * @access public + */ + function addTempFile($file) + { + if (!class_exists('PEAR_Frontend')) { + require_once 'PEAR/Frontend.php'; + } + PEAR_Frontend::addTempFile($file); + } + + /** + * Wrapper to System::mkDir(), creates a directory as well as + * any necessary parent directories. + * + * @param string $dir directory name + * + * @return bool TRUE on success, or a PEAR error + * + * @access public + */ + function mkDirHier($dir) + { + // Only used in Installer - move it there ? + $this->log(2, "+ create dir $dir"); + if (!class_exists('System')) { + require_once 'System.php'; + } + return System::mkDir(array('-p', $dir)); + } + + /** + * Logging method. + * + * @param int $level log level (0 is quiet, higher is noisier) + * @param string $msg message to write to the log + * + * @return void + * + * @access public + * @static + */ + function log($level, $msg, $append_crlf = true) + { + if ($this->debug >= $level) { + if (!class_exists('PEAR_Frontend')) { + require_once 'PEAR/Frontend.php'; + } + + $ui = &PEAR_Frontend::singleton(); + if (is_a($ui, 'PEAR_Frontend')) { + $ui->log($msg, $append_crlf); + } else { + print "$msg\n"; + } + } + } + + /** + * Create and register a temporary directory. + * + * @param string $tmpdir (optional) Directory to use as tmpdir. + * Will use system defaults (for example + * /tmp or c:\windows\temp) if not specified + * + * @return string name of created directory + * + * @access public + */ + function mkTempDir($tmpdir = '') + { + $topt = $tmpdir ? array('-t', $tmpdir) : array(); + $topt = array_merge($topt, array('-d', 'pear')); + if (!class_exists('System')) { + require_once 'System.php'; + } + + if (!$tmpdir = System::mktemp($topt)) { + return false; + } + + $this->addTempFile($tmpdir); + return $tmpdir; + } + + /** + * Set object that represents the frontend to be used. + * + * @param object Reference of the frontend object + * @return void + * @access public + */ + function setFrontendObject(&$ui) + { + $this->ui = &$ui; + } + + /** + * Return an array containing all of the states that are more stable than + * or equal to the passed in state + * + * @param string Release state + * @param boolean Determines whether to include $state in the list + * @return false|array False if $state is not a valid release state + */ + function betterStates($state, $include = false) + { + static $states = array('snapshot', 'devel', 'alpha', 'beta', 'stable'); + $i = array_search($state, $states); + if ($i === false) { + return false; + } + if ($include) { + $i--; + } + return array_slice($states, $i + 1); + } + + /** + * Get the valid roles for a PEAR package maintainer + * + * @return array + * @static + */ + function getUserRoles() + { + return $GLOBALS['_PEAR_Common_maintainer_roles']; + } + + /** + * Get the valid package release states of packages + * + * @return array + * @static + */ + function getReleaseStates() + { + return $GLOBALS['_PEAR_Common_release_states']; + } + + /** + * Get the implemented dependency types (php, ext, pkg etc.) + * + * @return array + * @static + */ + function getDependencyTypes() + { + return $GLOBALS['_PEAR_Common_dependency_types']; + } + + /** + * Get the implemented dependency relations (has, lt, ge etc.) + * + * @return array + * @static + */ + function getDependencyRelations() + { + return $GLOBALS['_PEAR_Common_dependency_relations']; + } + + /** + * Get the implemented file roles + * + * @return array + * @static + */ + function getFileRoles() + { + return $GLOBALS['_PEAR_Common_file_roles']; + } + + /** + * Get the implemented file replacement types in + * + * @return array + * @static + */ + function getReplacementTypes() + { + return $GLOBALS['_PEAR_Common_replacement_types']; + } + + /** + * Get the implemented file replacement types in + * + * @return array + * @static + */ + function getProvideTypes() + { + return $GLOBALS['_PEAR_Common_provide_types']; + } + + /** + * Get the implemented file replacement types in + * + * @return array + * @static + */ + function getScriptPhases() + { + return $GLOBALS['_PEAR_Common_script_phases']; + } + + /** + * Test whether a string contains a valid package name. + * + * @param string $name the package name to test + * + * @return bool + * + * @access public + */ + function validPackageName($name) + { + return (bool)preg_match(PEAR_COMMON_PACKAGE_NAME_PREG, $name); + } + + /** + * Test whether a string contains a valid package version. + * + * @param string $ver the package version to test + * + * @return bool + * + * @access public + */ + function validPackageVersion($ver) + { + return (bool)preg_match(PEAR_COMMON_PACKAGE_VERSION_PREG, $ver); + } + + /** + * @param string $path relative or absolute include path + * @return boolean + * @static + */ + function isIncludeable($path) + { + if (file_exists($path) && is_readable($path)) { + return true; + } + + $ipath = explode(PATH_SEPARATOR, ini_get('include_path')); + foreach ($ipath as $include) { + $test = realpath($include . DIRECTORY_SEPARATOR . $path); + if (file_exists($test) && is_readable($test)) { + return true; + } + } + + return false; + } + + function _postProcessChecks($pf) + { + if (!PEAR::isError($pf)) { + return $this->_postProcessValidPackagexml($pf); + } + + $errs = $pf->getUserinfo(); + if (is_array($errs)) { + foreach ($errs as $error) { + $e = $this->raiseError($error['message'], $error['code'], null, null, $error); + } + } + + return $pf; + } + + /** + * Returns information about a package file. Expects the name of + * a gzipped tar file as input. + * + * @param string $file name of .tgz file + * + * @return array array with package information + * + * @access public + * @deprecated use PEAR_PackageFile->fromTgzFile() instead + * + */ + function infoFromTgzFile($file) + { + $packagefile = &new PEAR_PackageFile($this->config); + $pf = &$packagefile->fromTgzFile($file, PEAR_VALIDATE_NORMAL); + return $this->_postProcessChecks($pf); + } + + /** + * Returns information about a package file. Expects the name of + * a package xml file as input. + * + * @param string $descfile name of package xml file + * + * @return array array with package information + * + * @access public + * @deprecated use PEAR_PackageFile->fromPackageFile() instead + * + */ + function infoFromDescriptionFile($descfile) + { + $packagefile = &new PEAR_PackageFile($this->config); + $pf = &$packagefile->fromPackageFile($descfile, PEAR_VALIDATE_NORMAL); + return $this->_postProcessChecks($pf); + } + + /** + * Returns information about a package file. Expects the contents + * of a package xml file as input. + * + * @param string $data contents of package.xml file + * + * @return array array with package information + * + * @access public + * @deprecated use PEAR_PackageFile->fromXmlstring() instead + * + */ + function infoFromString($data) + { + $packagefile = &new PEAR_PackageFile($this->config); + $pf = &$packagefile->fromXmlString($data, PEAR_VALIDATE_NORMAL, false); + return $this->_postProcessChecks($pf); + } + + /** + * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 + * @return array + */ + function _postProcessValidPackagexml(&$pf) + { + if (!is_a($pf, 'PEAR_PackageFile_v2')) { + $this->pkginfo = $pf->toArray(); + return $this->pkginfo; + } + + // sort of make this into a package.xml 1.0-style array + // changelog is not converted to old format. + $arr = $pf->toArray(true); + $arr = array_merge($arr, $arr['old']); + unset($arr['old'], $arr['xsdversion'], $arr['contents'], $arr['compatible'], + $arr['channel'], $arr['uri'], $arr['dependencies'], $arr['phprelease'], + $arr['extsrcrelease'], $arr['zendextsrcrelease'], $arr['extbinrelease'], + $arr['zendextbinrelease'], $arr['bundle'], $arr['lead'], $arr['developer'], + $arr['helper'], $arr['contributor']); + $arr['filelist'] = $pf->getFilelist(); + $this->pkginfo = $arr; + return $arr; + } + + /** + * Returns package information from different sources + * + * This method is able to extract information about a package + * from a .tgz archive or from a XML package definition file. + * + * @access public + * @param string Filename of the source ('package.xml', '.tgz') + * @return string + * @deprecated use PEAR_PackageFile->fromAnyFile() instead + */ + function infoFromAny($info) + { + if (is_string($info) && file_exists($info)) { + $packagefile = &new PEAR_PackageFile($this->config); + $pf = &$packagefile->fromAnyFile($info, PEAR_VALIDATE_NORMAL); + if (PEAR::isError($pf)) { + $errs = $pf->getUserinfo(); + if (is_array($errs)) { + foreach ($errs as $error) { + $e = $this->raiseError($error['message'], $error['code'], null, null, $error); + } + } + + return $pf; + } + + return $this->_postProcessValidPackagexml($pf); + } + + return $info; + } + + /** + * Return an XML document based on the package info (as returned + * by the PEAR_Common::infoFrom* methods). + * + * @param array $pkginfo package info + * + * @return string XML data + * + * @access public + * @deprecated use a PEAR_PackageFile_v* object's generator instead + */ + function xmlFromInfo($pkginfo) + { + $config = &PEAR_Config::singleton(); + $packagefile = &new PEAR_PackageFile($config); + $pf = &$packagefile->fromArray($pkginfo); + $gen = &$pf->getDefaultGenerator(); + return $gen->toXml(PEAR_VALIDATE_PACKAGING); + } + + /** + * Validate XML package definition file. + * + * @param string $info Filename of the package archive or of the + * package definition file + * @param array $errors Array that will contain the errors + * @param array $warnings Array that will contain the warnings + * @param string $dir_prefix (optional) directory where source files + * may be found, or empty if they are not available + * @access public + * @return boolean + * @deprecated use the validation of PEAR_PackageFile objects + */ + function validatePackageInfo($info, &$errors, &$warnings, $dir_prefix = '') + { + $config = &PEAR_Config::singleton(); + $packagefile = &new PEAR_PackageFile($config); + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + if (strpos($info, 'fromXmlString($info, PEAR_VALIDATE_NORMAL, ''); + } else { + $pf = &$packagefile->fromAnyFile($info, PEAR_VALIDATE_NORMAL); + } + + PEAR::staticPopErrorHandling(); + if (PEAR::isError($pf)) { + $errs = $pf->getUserinfo(); + if (is_array($errs)) { + foreach ($errs as $error) { + if ($error['level'] == 'error') { + $errors[] = $error['message']; + } else { + $warnings[] = $error['message']; + } + } + } + + return false; + } + + return true; + } + + /** + * Build a "provides" array from data returned by + * analyzeSourceCode(). The format of the built array is like + * this: + * + * array( + * 'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'), + * ... + * ) + * + * + * @param array $srcinfo array with information about a source file + * as returned by the analyzeSourceCode() method. + * + * @return void + * + * @access public + * + */ + function buildProvidesArray($srcinfo) + { + $file = basename($srcinfo['source_file']); + $pn = ''; + if (isset($this->_packageName)) { + $pn = $this->_packageName; + } + + $pnl = strlen($pn); + foreach ($srcinfo['declared_classes'] as $class) { + $key = "class;$class"; + if (isset($this->pkginfo['provides'][$key])) { + continue; + } + + $this->pkginfo['provides'][$key] = + array('file'=> $file, 'type' => 'class', 'name' => $class); + if (isset($srcinfo['inheritance'][$class])) { + $this->pkginfo['provides'][$key]['extends'] = + $srcinfo['inheritance'][$class]; + } + } + + foreach ($srcinfo['declared_methods'] as $class => $methods) { + foreach ($methods as $method) { + $function = "$class::$method"; + $key = "function;$function"; + if ($method{0} == '_' || !strcasecmp($method, $class) || + isset($this->pkginfo['provides'][$key])) { + continue; + } + + $this->pkginfo['provides'][$key] = + array('file'=> $file, 'type' => 'function', 'name' => $function); + } + } + + foreach ($srcinfo['declared_functions'] as $function) { + $key = "function;$function"; + if ($function{0} == '_' || isset($this->pkginfo['provides'][$key])) { + continue; + } + + if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) { + $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\""; + } + + $this->pkginfo['provides'][$key] = + array('file'=> $file, 'type' => 'function', 'name' => $function); + } + } + + /** + * Analyze the source code of the given PHP file + * + * @param string Filename of the PHP file + * @return mixed + * @access public + */ + function analyzeSourceCode($file) + { + if (!class_exists('PEAR_PackageFile_v2_Validator')) { + require_once 'PEAR/PackageFile/v2/Validator.php'; + } + + $a = new PEAR_PackageFile_v2_Validator; + return $a->analyzeSourceCode($file); + } + + function detectDependencies($any, $status_callback = null) + { + if (!function_exists("token_get_all")) { + return false; + } + + if (PEAR::isError($info = $this->infoFromAny($any))) { + return $this->raiseError($info); + } + + if (!is_array($info)) { + return false; + } + + $deps = array(); + $used_c = $decl_c = $decl_f = $decl_m = array(); + foreach ($info['filelist'] as $file => $fa) { + $tmp = $this->analyzeSourceCode($file); + $used_c = @array_merge($used_c, $tmp['used_classes']); + $decl_c = @array_merge($decl_c, $tmp['declared_classes']); + $decl_f = @array_merge($decl_f, $tmp['declared_functions']); + $decl_m = @array_merge($decl_m, $tmp['declared_methods']); + $inheri = @array_merge($inheri, $tmp['inheritance']); + } + + $used_c = array_unique($used_c); + $decl_c = array_unique($decl_c); + $undecl_c = array_diff($used_c, $decl_c); + + return array('used_classes' => $used_c, + 'declared_classes' => $decl_c, + 'declared_methods' => $decl_m, + 'declared_functions' => $decl_f, + 'undeclared_classes' => $undecl_c, + 'inheritance' => $inheri, + ); + } + + /** + * Download a file through HTTP. Considers suggested file name in + * Content-disposition: header and can run a callback function for + * different events. The callback will be called with two + * parameters: the callback type, and parameters. The implemented + * callback types are: + * + * 'setup' called at the very beginning, parameter is a UI object + * that should be used for all output + * 'message' the parameter is a string with an informational message + * 'saveas' may be used to save with a different file name, the + * parameter is the filename that is about to be used. + * If a 'saveas' callback returns a non-empty string, + * that file name will be used as the filename instead. + * Note that $save_dir will not be affected by this, only + * the basename of the file. + * 'start' download is starting, parameter is number of bytes + * that are expected, or -1 if unknown + * 'bytesread' parameter is the number of bytes read so far + * 'done' download is complete, parameter is the total number + * of bytes read + * 'connfailed' if the TCP connection fails, this callback is called + * with array(host,port,errno,errmsg) + * 'writefailed' if writing to disk fails, this callback is called + * with array(destfile,errmsg) + * + * If an HTTP proxy has been configured (http_proxy PEAR_Config + * setting), the proxy will be used. + * + * @param string $url the URL to download + * @param object $ui PEAR_Frontend_* instance + * @param object $config PEAR_Config instance + * @param string $save_dir (optional) directory to save file in + * @param mixed $callback (optional) function/method to call for status + * updates + * + * @return string Returns the full path of the downloaded file or a PEAR + * error on failure. If the error is caused by + * socket-related errors, the error object will + * have the fsockopen error code available through + * getCode(). + * + * @access public + * @deprecated in favor of PEAR_Downloader::downloadHttp() + */ + function downloadHttp($url, &$ui, $save_dir = '.', $callback = null) + { + if (!class_exists('PEAR_Downloader')) { + require_once 'PEAR/Downloader.php'; + } + return PEAR_Downloader::downloadHttp($url, $ui, $save_dir, $callback); + } +} + +require_once 'PEAR/Config.php'; +require_once 'PEAR/PackageFile.php'; \ No newline at end of file diff --git a/includes/pear/PEAR/Config.php b/includes/pear/PEAR/Config.php new file mode 100644 index 0000000..f6fb9ee --- /dev/null +++ b/includes/pear/PEAR/Config.php @@ -0,0 +1,2092 @@ + + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 0.1 + */ + +/** + * Required for error handling + */ +require_once 'PEAR.php'; +require_once 'PEAR/Registry.php'; +require_once 'PEAR/Installer/Role.php'; +require_once 'System.php'; + +/** + * Last created PEAR_Config instance. + * @var object + */ +$GLOBALS['_PEAR_Config_instance'] = null; +if (!defined('PEAR_INSTALL_DIR') || !PEAR_INSTALL_DIR) { + $PEAR_INSTALL_DIR = PHP_LIBDIR . DIRECTORY_SEPARATOR . 'pear'; +} else { + $PEAR_INSTALL_DIR = PEAR_INSTALL_DIR; +} + +// Below we define constants with default values for all configuration +// parameters except username/password. All of them can have their +// defaults set through environment variables. The reason we use the +// PHP_ prefix is for some security, PHP protects environment +// variables starting with PHP_*. + +// default channel and preferred mirror is based on whether we are invoked through +// the "pear" or the "pecl" command +if (!defined('PEAR_RUNTYPE')) { + define('PEAR_RUNTYPE', 'pear'); +} + +if (PEAR_RUNTYPE == 'pear') { + define('PEAR_CONFIG_DEFAULT_CHANNEL', 'pear.php.net'); +} else { + define('PEAR_CONFIG_DEFAULT_CHANNEL', 'pecl.php.net'); +} + +if (getenv('PHP_PEAR_SYSCONF_DIR')) { + define('PEAR_CONFIG_SYSCONFDIR', getenv('PHP_PEAR_SYSCONF_DIR')); +} elseif (getenv('SystemRoot')) { + define('PEAR_CONFIG_SYSCONFDIR', getenv('SystemRoot')); +} else { + define('PEAR_CONFIG_SYSCONFDIR', PHP_SYSCONFDIR); +} + +// Default for master_server +if (getenv('PHP_PEAR_MASTER_SERVER')) { + define('PEAR_CONFIG_DEFAULT_MASTER_SERVER', getenv('PHP_PEAR_MASTER_SERVER')); +} else { + define('PEAR_CONFIG_DEFAULT_MASTER_SERVER', 'pear.php.net'); +} + +// Default for http_proxy +if (getenv('PHP_PEAR_HTTP_PROXY')) { + define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', getenv('PHP_PEAR_HTTP_PROXY')); +} elseif (getenv('http_proxy')) { + define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', getenv('http_proxy')); +} else { + define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', ''); +} + +// Default for php_dir +if (getenv('PHP_PEAR_INSTALL_DIR')) { + define('PEAR_CONFIG_DEFAULT_PHP_DIR', getenv('PHP_PEAR_INSTALL_DIR')); +} else { + if (@file_exists($PEAR_INSTALL_DIR) && is_dir($PEAR_INSTALL_DIR)) { + define('PEAR_CONFIG_DEFAULT_PHP_DIR', $PEAR_INSTALL_DIR); + } else { + define('PEAR_CONFIG_DEFAULT_PHP_DIR', $PEAR_INSTALL_DIR); + } +} + +// Default for ext_dir +if (getenv('PHP_PEAR_EXTENSION_DIR')) { + define('PEAR_CONFIG_DEFAULT_EXT_DIR', getenv('PHP_PEAR_EXTENSION_DIR')); +} else { + if (ini_get('extension_dir')) { + define('PEAR_CONFIG_DEFAULT_EXT_DIR', ini_get('extension_dir')); + } elseif (defined('PEAR_EXTENSION_DIR') && + file_exists(PEAR_EXTENSION_DIR) && is_dir(PEAR_EXTENSION_DIR)) { + define('PEAR_CONFIG_DEFAULT_EXT_DIR', PEAR_EXTENSION_DIR); + } elseif (defined('PHP_EXTENSION_DIR')) { + define('PEAR_CONFIG_DEFAULT_EXT_DIR', PHP_EXTENSION_DIR); + } else { + define('PEAR_CONFIG_DEFAULT_EXT_DIR', '.'); + } +} + +// Default for doc_dir +if (getenv('PHP_PEAR_DOC_DIR')) { + define('PEAR_CONFIG_DEFAULT_DOC_DIR', getenv('PHP_PEAR_DOC_DIR')); +} else { + define('PEAR_CONFIG_DEFAULT_DOC_DIR', + $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'docs'); +} + +// Default for bin_dir +if (getenv('PHP_PEAR_BIN_DIR')) { + define('PEAR_CONFIG_DEFAULT_BIN_DIR', getenv('PHP_PEAR_BIN_DIR')); +} else { + define('PEAR_CONFIG_DEFAULT_BIN_DIR', PHP_BINDIR); +} + +// Default for data_dir +if (getenv('PHP_PEAR_DATA_DIR')) { + define('PEAR_CONFIG_DEFAULT_DATA_DIR', getenv('PHP_PEAR_DATA_DIR')); +} else { + define('PEAR_CONFIG_DEFAULT_DATA_DIR', + $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'data'); +} + +// Default for cfg_dir +if (getenv('PHP_PEAR_CFG_DIR')) { + define('PEAR_CONFIG_DEFAULT_CFG_DIR', getenv('PHP_PEAR_CFG_DIR')); +} else { + define('PEAR_CONFIG_DEFAULT_CFG_DIR', + $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'cfg'); +} + +// Default for www_dir +if (getenv('PHP_PEAR_WWW_DIR')) { + define('PEAR_CONFIG_DEFAULT_WWW_DIR', getenv('PHP_PEAR_WWW_DIR')); +} else { + define('PEAR_CONFIG_DEFAULT_WWW_DIR', + $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'www'); +} + +// Default for test_dir +if (getenv('PHP_PEAR_TEST_DIR')) { + define('PEAR_CONFIG_DEFAULT_TEST_DIR', getenv('PHP_PEAR_TEST_DIR')); +} else { + define('PEAR_CONFIG_DEFAULT_TEST_DIR', + $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'tests'); +} + +// Default for temp_dir +if (getenv('PHP_PEAR_TEMP_DIR')) { + define('PEAR_CONFIG_DEFAULT_TEMP_DIR', getenv('PHP_PEAR_TEMP_DIR')); +} else { + define('PEAR_CONFIG_DEFAULT_TEMP_DIR', + System::tmpdir() . DIRECTORY_SEPARATOR . 'pear' . + DIRECTORY_SEPARATOR . 'temp'); +} + +// Default for cache_dir +if (getenv('PHP_PEAR_CACHE_DIR')) { + define('PEAR_CONFIG_DEFAULT_CACHE_DIR', getenv('PHP_PEAR_CACHE_DIR')); +} else { + define('PEAR_CONFIG_DEFAULT_CACHE_DIR', + System::tmpdir() . DIRECTORY_SEPARATOR . 'pear' . + DIRECTORY_SEPARATOR . 'cache'); +} + +// Default for download_dir +if (getenv('PHP_PEAR_DOWNLOAD_DIR')) { + define('PEAR_CONFIG_DEFAULT_DOWNLOAD_DIR', getenv('PHP_PEAR_DOWNLOAD_DIR')); +} else { + define('PEAR_CONFIG_DEFAULT_DOWNLOAD_DIR', + System::tmpdir() . DIRECTORY_SEPARATOR . 'pear' . + DIRECTORY_SEPARATOR . 'download'); +} + +// Default for php_bin +if (getenv('PHP_PEAR_PHP_BIN')) { + define('PEAR_CONFIG_DEFAULT_PHP_BIN', getenv('PHP_PEAR_PHP_BIN')); +} else { + define('PEAR_CONFIG_DEFAULT_PHP_BIN', PEAR_CONFIG_DEFAULT_BIN_DIR. + DIRECTORY_SEPARATOR.'php'.(OS_WINDOWS ? '.exe' : '')); +} + +// Default for verbose +if (getenv('PHP_PEAR_VERBOSE')) { + define('PEAR_CONFIG_DEFAULT_VERBOSE', getenv('PHP_PEAR_VERBOSE')); +} else { + define('PEAR_CONFIG_DEFAULT_VERBOSE', 1); +} + +// Default for preferred_state +if (getenv('PHP_PEAR_PREFERRED_STATE')) { + define('PEAR_CONFIG_DEFAULT_PREFERRED_STATE', getenv('PHP_PEAR_PREFERRED_STATE')); +} else { + define('PEAR_CONFIG_DEFAULT_PREFERRED_STATE', 'stable'); +} + +// Default for umask +if (getenv('PHP_PEAR_UMASK')) { + define('PEAR_CONFIG_DEFAULT_UMASK', getenv('PHP_PEAR_UMASK')); +} else { + define('PEAR_CONFIG_DEFAULT_UMASK', decoct(umask())); +} + +// Default for cache_ttl +if (getenv('PHP_PEAR_CACHE_TTL')) { + define('PEAR_CONFIG_DEFAULT_CACHE_TTL', getenv('PHP_PEAR_CACHE_TTL')); +} else { + define('PEAR_CONFIG_DEFAULT_CACHE_TTL', 3600); +} + +// Default for sig_type +if (getenv('PHP_PEAR_SIG_TYPE')) { + define('PEAR_CONFIG_DEFAULT_SIG_TYPE', getenv('PHP_PEAR_SIG_TYPE')); +} else { + define('PEAR_CONFIG_DEFAULT_SIG_TYPE', 'gpg'); +} + +// Default for sig_bin +if (getenv('PHP_PEAR_SIG_BIN')) { + define('PEAR_CONFIG_DEFAULT_SIG_BIN', getenv('PHP_PEAR_SIG_BIN')); +} else { + define('PEAR_CONFIG_DEFAULT_SIG_BIN', + System::which( + 'gpg', OS_WINDOWS ? 'c:\gnupg\gpg.exe' : '/usr/local/bin/gpg')); +} + +// Default for sig_keydir +if (getenv('PHP_PEAR_SIG_KEYDIR')) { + define('PEAR_CONFIG_DEFAULT_SIG_KEYDIR', getenv('PHP_PEAR_SIG_KEYDIR')); +} else { + define('PEAR_CONFIG_DEFAULT_SIG_KEYDIR', + PEAR_CONFIG_SYSCONFDIR . DIRECTORY_SEPARATOR . 'pearkeys'); +} + +/** + * This is a class for storing configuration data, keeping track of + * which are system-defined, user-defined or defaulted. + * @category pear + * @package PEAR + * @author Stig Bakken + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 0.1 + */ +class PEAR_Config extends PEAR +{ + /** + * Array of config files used. + * + * @var array layer => config file + */ + var $files = array( + 'system' => '', + 'user' => '', + ); + + var $layers = array(); + + /** + * Configuration data, two-dimensional array where the first + * dimension is the config layer ('user', 'system' and 'default'), + * and the second dimension is keyname => value. + * + * The order in the first dimension is important! Earlier + * layers will shadow later ones when a config value is + * requested (if a 'user' value exists, it will be returned first, + * then 'system' and finally 'default'). + * + * @var array layer => array(keyname => value, ...) + */ + var $configuration = array( + 'user' => array(), + 'system' => array(), + 'default' => array(), + ); + + /** + * Configuration values that can be set for a channel + * + * All other configuration values can only have a global value + * @var array + * @access private + */ + var $_channelConfigInfo = array( + 'php_dir', 'ext_dir', 'doc_dir', 'bin_dir', 'data_dir', 'cfg_dir', + 'test_dir', 'www_dir', 'php_bin', 'php_prefix', 'php_suffix', 'username', + 'password', 'verbose', 'preferred_state', 'umask', 'preferred_mirror', 'php_ini' + ); + + /** + * Channels that can be accessed + * @see setChannels() + * @var array + * @access private + */ + var $_channels = array('pear.php.net', 'pecl.php.net', '__uri'); + + /** + * This variable is used to control the directory values returned + * @see setInstallRoot(); + * @var string|false + * @access private + */ + var $_installRoot = false; + + /** + * If requested, this will always refer to the registry + * contained in php_dir + * @var PEAR_Registry + */ + var $_registry = array(); + + /** + * @var array + * @access private + */ + var $_regInitialized = array(); + + /** + * @var bool + * @access private + */ + var $_noRegistry = false; + + /** + * amount of errors found while parsing config + * @var integer + * @access private + */ + var $_errorsFound = 0; + var $_lastError = null; + + /** + * Information about the configuration data. Stores the type, + * default value and a documentation string for each configuration + * value. + * + * @var array layer => array(infotype => value, ...) + */ + var $configuration_info = array( + // Channels/Internet Access + 'default_channel' => array( + 'type' => 'string', + 'default' => PEAR_CONFIG_DEFAULT_CHANNEL, + 'doc' => 'the default channel to use for all non explicit commands', + 'prompt' => 'Default Channel', + 'group' => 'Internet Access', + ), + 'preferred_mirror' => array( + 'type' => 'string', + 'default' => PEAR_CONFIG_DEFAULT_CHANNEL, + 'doc' => 'the default server or mirror to use for channel actions', + 'prompt' => 'Default Channel Mirror', + 'group' => 'Internet Access', + ), + 'remote_config' => array( + 'type' => 'password', + 'default' => '', + 'doc' => 'ftp url of remote configuration file to use for synchronized install', + 'prompt' => 'Remote Configuration File', + 'group' => 'Internet Access', + ), + 'auto_discover' => array( + 'type' => 'integer', + 'default' => 1, + 'doc' => 'whether to automatically discover new channels', + 'prompt' => 'Auto-discover new Channels', + 'group' => 'Internet Access', + ), + // Internet Access + 'master_server' => array( + 'type' => 'string', + 'default' => 'pear.php.net', + 'doc' => 'name of the main PEAR server [NOT USED IN THIS VERSION]', + 'prompt' => 'PEAR server [DEPRECATED]', + 'group' => 'Internet Access', + ), + 'http_proxy' => array( + 'type' => 'string', + 'default' => PEAR_CONFIG_DEFAULT_HTTP_PROXY, + 'doc' => 'HTTP proxy (host:port) to use when downloading packages', + 'prompt' => 'HTTP Proxy Server Address', + 'group' => 'Internet Access', + ), + // File Locations + 'php_dir' => array( + 'type' => 'directory', + 'default' => PEAR_CONFIG_DEFAULT_PHP_DIR, + 'doc' => 'directory where .php files are installed', + 'prompt' => 'PEAR directory', + 'group' => 'File Locations', + ), + 'ext_dir' => array( + 'type' => 'directory', + 'default' => PEAR_CONFIG_DEFAULT_EXT_DIR, + 'doc' => 'directory where loadable extensions are installed', + 'prompt' => 'PHP extension directory', + 'group' => 'File Locations', + ), + 'doc_dir' => array( + 'type' => 'directory', + 'default' => PEAR_CONFIG_DEFAULT_DOC_DIR, + 'doc' => 'directory where documentation is installed', + 'prompt' => 'PEAR documentation directory', + 'group' => 'File Locations', + ), + 'bin_dir' => array( + 'type' => 'directory', + 'default' => PEAR_CONFIG_DEFAULT_BIN_DIR, + 'doc' => 'directory where executables are installed', + 'prompt' => 'PEAR executables directory', + 'group' => 'File Locations', + ), + 'data_dir' => array( + 'type' => 'directory', + 'default' => PEAR_CONFIG_DEFAULT_DATA_DIR, + 'doc' => 'directory where data files are installed', + 'prompt' => 'PEAR data directory', + 'group' => 'File Locations (Advanced)', + ), + 'cfg_dir' => array( + 'type' => 'directory', + 'default' => PEAR_CONFIG_DEFAULT_CFG_DIR, + 'doc' => 'directory where modifiable configuration files are installed', + 'prompt' => 'PEAR configuration file directory', + 'group' => 'File Locations (Advanced)', + ), + 'www_dir' => array( + 'type' => 'directory', + 'default' => PEAR_CONFIG_DEFAULT_WWW_DIR, + 'doc' => 'directory where www frontend files (html/js) are installed', + 'prompt' => 'PEAR www files directory', + 'group' => 'File Locations (Advanced)', + ), + 'test_dir' => array( + 'type' => 'directory', + 'default' => PEAR_CONFIG_DEFAULT_TEST_DIR, + 'doc' => 'directory where regression tests are installed', + 'prompt' => 'PEAR test directory', + 'group' => 'File Locations (Advanced)', + ), + 'cache_dir' => array( + 'type' => 'directory', + 'default' => PEAR_CONFIG_DEFAULT_CACHE_DIR, + 'doc' => 'directory which is used for web service cache', + 'prompt' => 'PEAR Installer cache directory', + 'group' => 'File Locations (Advanced)', + ), + 'temp_dir' => array( + 'type' => 'directory', + 'default' => PEAR_CONFIG_DEFAULT_TEMP_DIR, + 'doc' => 'directory which is used for all temp files', + 'prompt' => 'PEAR Installer temp directory', + 'group' => 'File Locations (Advanced)', + ), + 'download_dir' => array( + 'type' => 'directory', + 'default' => PEAR_CONFIG_DEFAULT_DOWNLOAD_DIR, + 'doc' => 'directory which is used for all downloaded files', + 'prompt' => 'PEAR Installer download directory', + 'group' => 'File Locations (Advanced)', + ), + 'php_bin' => array( + 'type' => 'file', + 'default' => PEAR_CONFIG_DEFAULT_PHP_BIN, + 'doc' => 'PHP CLI/CGI binary for executing scripts', + 'prompt' => 'PHP CLI/CGI binary', + 'group' => 'File Locations (Advanced)', + ), + 'php_prefix' => array( + 'type' => 'string', + 'default' => '', + 'doc' => '--program-prefix for php_bin\'s ./configure, used for pecl installs', + 'prompt' => '--program-prefix passed to PHP\'s ./configure', + 'group' => 'File Locations (Advanced)', + ), + 'php_suffix' => array( + 'type' => 'string', + 'default' => '', + 'doc' => '--program-suffix for php_bin\'s ./configure, used for pecl installs', + 'prompt' => '--program-suffix passed to PHP\'s ./configure', + 'group' => 'File Locations (Advanced)', + ), + 'php_ini' => array( + 'type' => 'file', + 'default' => '', + 'doc' => 'location of php.ini in which to enable PECL extensions on install', + 'prompt' => 'php.ini location', + 'group' => 'File Locations (Advanced)', + ), + // Maintainers + 'username' => array( + 'type' => 'string', + 'default' => '', + 'doc' => '(maintainers) your PEAR account name', + 'prompt' => 'PEAR username (for maintainers)', + 'group' => 'Maintainers', + ), + 'password' => array( + 'type' => 'password', + 'default' => '', + 'doc' => '(maintainers) your PEAR account password', + 'prompt' => 'PEAR password (for maintainers)', + 'group' => 'Maintainers', + ), + // Advanced + 'verbose' => array( + 'type' => 'integer', + 'default' => PEAR_CONFIG_DEFAULT_VERBOSE, + 'doc' => 'verbosity level +0: really quiet +1: somewhat quiet +2: verbose +3: debug', + 'prompt' => 'Debug Log Level', + 'group' => 'Advanced', + ), + 'preferred_state' => array( + 'type' => 'set', + 'default' => PEAR_CONFIG_DEFAULT_PREFERRED_STATE, + 'doc' => 'the installer will prefer releases with this state when installing packages without a version or state specified', + 'valid_set' => array( + 'stable', 'beta', 'alpha', 'devel', 'snapshot'), + 'prompt' => 'Preferred Package State', + 'group' => 'Advanced', + ), + 'umask' => array( + 'type' => 'mask', + 'default' => PEAR_CONFIG_DEFAULT_UMASK, + 'doc' => 'umask used when creating files (Unix-like systems only)', + 'prompt' => 'Unix file mask', + 'group' => 'Advanced', + ), + 'cache_ttl' => array( + 'type' => 'integer', + 'default' => PEAR_CONFIG_DEFAULT_CACHE_TTL, + 'doc' => 'amount of secs where the local cache is used and not updated', + 'prompt' => 'Cache TimeToLive', + 'group' => 'Advanced', + ), + 'sig_type' => array( + 'type' => 'set', + 'default' => PEAR_CONFIG_DEFAULT_SIG_TYPE, + 'doc' => 'which package signature mechanism to use', + 'valid_set' => array('gpg'), + 'prompt' => 'Package Signature Type', + 'group' => 'Maintainers', + ), + 'sig_bin' => array( + 'type' => 'string', + 'default' => PEAR_CONFIG_DEFAULT_SIG_BIN, + 'doc' => 'which package signature mechanism to use', + 'prompt' => 'Signature Handling Program', + 'group' => 'Maintainers', + ), + 'sig_keyid' => array( + 'type' => 'string', + 'default' => '', + 'doc' => 'which key to use for signing with', + 'prompt' => 'Signature Key Id', + 'group' => 'Maintainers', + ), + 'sig_keydir' => array( + 'type' => 'directory', + 'default' => PEAR_CONFIG_DEFAULT_SIG_KEYDIR, + 'doc' => 'directory where signature keys are located', + 'prompt' => 'Signature Key Directory', + 'group' => 'Maintainers', + ), + // __channels is reserved - used for channel-specific configuration + ); + + /** + * Constructor. + * + * @param string file to read user-defined options from + * @param string file to read system-wide defaults from + * @param bool determines whether a registry object "follows" + * the value of php_dir (is automatically created + * and moved when php_dir is changed) + * @param bool if true, fails if configuration files cannot be loaded + * + * @access public + * + * @see PEAR_Config::singleton + */ + function PEAR_Config($user_file = '', $system_file = '', $ftp_file = false, + $strict = true) + { + $this->PEAR(); + PEAR_Installer_Role::initializeConfig($this); + $sl = DIRECTORY_SEPARATOR; + if (empty($user_file)) { + if (OS_WINDOWS) { + $user_file = PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.ini'; + } else { + $user_file = getenv('HOME') . $sl . '.pearrc'; + } + } + + if (empty($system_file)) { + $system_file = PEAR_CONFIG_SYSCONFDIR . $sl; + if (OS_WINDOWS) { + $system_file .= 'pearsys.ini'; + } else { + $system_file .= 'pear.conf'; + } + } + + $this->layers = array_keys($this->configuration); + $this->files['user'] = $user_file; + $this->files['system'] = $system_file; + if ($user_file && file_exists($user_file)) { + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $this->readConfigFile($user_file, 'user', $strict); + $this->popErrorHandling(); + if ($this->_errorsFound > 0) { + return; + } + } + + if ($system_file && @file_exists($system_file)) { + $this->mergeConfigFile($system_file, false, 'system', $strict); + if ($this->_errorsFound > 0) { + return; + } + + } + + if (!$ftp_file) { + $ftp_file = $this->get('remote_config'); + } + + if ($ftp_file && defined('PEAR_REMOTEINSTALL_OK')) { + $this->readFTPConfigFile($ftp_file); + } + + foreach ($this->configuration_info as $key => $info) { + $this->configuration['default'][$key] = $info['default']; + } + + $this->_registry['default'] = &new PEAR_Registry($this->configuration['default']['php_dir']); + $this->_registry['default']->setConfig($this, false); + $this->_regInitialized['default'] = false; + //$GLOBALS['_PEAR_Config_instance'] = &$this; + } + + /** + * Return the default locations of user and system configuration files + * @static + */ + function getDefaultConfigFiles() + { + $sl = DIRECTORY_SEPARATOR; + if (OS_WINDOWS) { + return array( + 'user' => PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.ini', + 'system' => PEAR_CONFIG_SYSCONFDIR . $sl . 'pearsys.ini' + ); + } + + return array( + 'user' => getenv('HOME') . $sl . '.pearrc', + 'system' => PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.conf' + ); + } + + /** + * Static singleton method. If you want to keep only one instance + * of this class in use, this method will give you a reference to + * the last created PEAR_Config object if one exists, or create a + * new object. + * + * @param string (optional) file to read user-defined options from + * @param string (optional) file to read system-wide defaults from + * + * @return object an existing or new PEAR_Config instance + * + * @access public + * + * @see PEAR_Config::PEAR_Config + */ + function &singleton($user_file = '', $system_file = '', $strict = true) + { + if (is_object($GLOBALS['_PEAR_Config_instance'])) { + return $GLOBALS['_PEAR_Config_instance']; + } + + $t_conf = &new PEAR_Config($user_file, $system_file, false, $strict); + if ($t_conf->_errorsFound > 0) { + return $t_conf->lastError; + } + + $GLOBALS['_PEAR_Config_instance'] = &$t_conf; + return $GLOBALS['_PEAR_Config_instance']; + } + + /** + * Determine whether any configuration files have been detected, and whether a + * registry object can be retrieved from this configuration. + * @return bool + * @since PEAR 1.4.0a1 + */ + function validConfiguration() + { + if ($this->isDefinedLayer('user') || $this->isDefinedLayer('system')) { + return true; + } + + return false; + } + + /** + * Reads configuration data from a file. All existing values in + * the config layer are discarded and replaced with data from the + * file. + * @param string file to read from, if NULL or not specified, the + * last-used file for the same layer (second param) is used + * @param string config layer to insert data into ('user' or 'system') + * @return bool TRUE on success or a PEAR error on failure + */ + function readConfigFile($file = null, $layer = 'user', $strict = true) + { + if (empty($this->files[$layer])) { + return $this->raiseError("unknown config layer `$layer'"); + } + + if ($file === null) { + $file = $this->files[$layer]; + } + + $data = $this->_readConfigDataFrom($file); + if (PEAR::isError($data)) { + if (!$strict) { + return true; + } + + $this->_errorsFound++; + $this->lastError = $data; + + return $data; + } + + $this->files[$layer] = $file; + $this->_decodeInput($data); + $this->configuration[$layer] = $data; + $this->_setupChannels(); + if (!$this->_noRegistry && ($phpdir = $this->get('php_dir', $layer, 'pear.php.net'))) { + $this->_registry[$layer] = &new PEAR_Registry($phpdir); + $this->_registry[$layer]->setConfig($this, false); + $this->_regInitialized[$layer] = false; + } else { + unset($this->_registry[$layer]); + } + return true; + } + + /** + * @param string url to the remote config file, like ftp://www.example.com/pear/config.ini + * @return true|PEAR_Error + */ + function readFTPConfigFile($path) + { + do { // poor man's try + if (!class_exists('PEAR_FTP')) { + if (!class_exists('PEAR_Common')) { + require_once 'PEAR/Common.php'; + } + if (PEAR_Common::isIncludeable('PEAR/FTP.php')) { + require_once 'PEAR/FTP.php'; + } + } + + if (!class_exists('PEAR_FTP')) { + return PEAR::raiseError('PEAR_RemoteInstaller must be installed to use remote config'); + } + + $this->_ftp = &new PEAR_FTP; + $this->_ftp->pushErrorHandling(PEAR_ERROR_RETURN); + $e = $this->_ftp->init($path); + if (PEAR::isError($e)) { + $this->_ftp->popErrorHandling(); + return $e; + } + + $tmp = System::mktemp('-d'); + PEAR_Common::addTempFile($tmp); + $e = $this->_ftp->get(basename($path), $tmp . DIRECTORY_SEPARATOR . + 'pear.ini', false, FTP_BINARY); + if (PEAR::isError($e)) { + $this->_ftp->popErrorHandling(); + return $e; + } + + PEAR_Common::addTempFile($tmp . DIRECTORY_SEPARATOR . 'pear.ini'); + $this->_ftp->disconnect(); + $this->_ftp->popErrorHandling(); + $this->files['ftp'] = $tmp . DIRECTORY_SEPARATOR . 'pear.ini'; + $e = $this->readConfigFile(null, 'ftp'); + if (PEAR::isError($e)) { + return $e; + } + + $fail = array(); + foreach ($this->configuration_info as $key => $val) { + if (in_array($this->getGroup($key), + array('File Locations', 'File Locations (Advanced)')) && + $this->getType($key) == 'directory') { + // any directory configs must be set for this to work + if (!isset($this->configuration['ftp'][$key])) { + $fail[] = $key; + } + } + } + + if (!count($fail)) { + return true; + } + + $fail = '"' . implode('", "', $fail) . '"'; + unset($this->files['ftp']); + unset($this->configuration['ftp']); + return PEAR::raiseError('ERROR: Ftp configuration file must set all ' . + 'directory configuration variables. These variables were not set: ' . + $fail); + } while (false); // poor man's catch + unset($this->files['ftp']); + return PEAR::raiseError('no remote host specified'); + } + + /** + * Reads the existing configurations and creates the _channels array from it + */ + function _setupChannels() + { + $set = array_flip(array_values($this->_channels)); + foreach ($this->configuration as $layer => $data) { + $i = 1000; + if (isset($data['__channels']) && is_array($data['__channels'])) { + foreach ($data['__channels'] as $channel => $info) { + $set[$channel] = $i++; + } + } + } + $this->_channels = array_values(array_flip($set)); + $this->setChannels($this->_channels); + } + + function deleteChannel($channel) + { + $ch = strtolower($channel); + foreach ($this->configuration as $layer => $data) { + if (isset($data['__channels']) && isset($data['__channels'][$ch])) { + unset($this->configuration[$layer]['__channels'][$ch]); + } + } + + $this->_channels = array_flip($this->_channels); + unset($this->_channels[$ch]); + $this->_channels = array_flip($this->_channels); + } + + /** + * Merges data into a config layer from a file. Does the same + * thing as readConfigFile, except it does not replace all + * existing values in the config layer. + * @param string file to read from + * @param bool whether to overwrite existing data (default TRUE) + * @param string config layer to insert data into ('user' or 'system') + * @param string if true, errors are returned if file opening fails + * @return bool TRUE on success or a PEAR error on failure + */ + function mergeConfigFile($file, $override = true, $layer = 'user', $strict = true) + { + if (empty($this->files[$layer])) { + return $this->raiseError("unknown config layer `$layer'"); + } + + if ($file === null) { + $file = $this->files[$layer]; + } + + $data = $this->_readConfigDataFrom($file); + if (PEAR::isError($data)) { + if (!$strict) { + return true; + } + + $this->_errorsFound++; + $this->lastError = $data; + + return $data; + } + + $this->_decodeInput($data); + if ($override) { + $this->configuration[$layer] = + PEAR_Config::arrayMergeRecursive($this->configuration[$layer], $data); + } else { + $this->configuration[$layer] = + PEAR_Config::arrayMergeRecursive($data, $this->configuration[$layer]); + } + + $this->_setupChannels(); + if (!$this->_noRegistry && ($phpdir = $this->get('php_dir', $layer, 'pear.php.net'))) { + $this->_registry[$layer] = &new PEAR_Registry($phpdir); + $this->_registry[$layer]->setConfig($this, false); + $this->_regInitialized[$layer] = false; + } else { + unset($this->_registry[$layer]); + } + return true; + } + + /** + * @param array + * @param array + * @return array + * @static + */ + function arrayMergeRecursive($arr2, $arr1) + { + $ret = array(); + foreach ($arr2 as $key => $data) { + if (!isset($arr1[$key])) { + $ret[$key] = $data; + unset($arr1[$key]); + continue; + } + if (is_array($data)) { + if (!is_array($arr1[$key])) { + $ret[$key] = $arr1[$key]; + unset($arr1[$key]); + continue; + } + $ret[$key] = PEAR_Config::arrayMergeRecursive($arr1[$key], $arr2[$key]); + unset($arr1[$key]); + } + } + + return array_merge($ret, $arr1); + } + + /** + * Writes data into a config layer from a file. + * + * @param string|null file to read from, or null for default + * @param string config layer to insert data into ('user' or + * 'system') + * @param string|null data to write to config file or null for internal data [DEPRECATED] + * @return bool TRUE on success or a PEAR error on failure + */ + function writeConfigFile($file = null, $layer = 'user', $data = null) + { + $this->_lazyChannelSetup($layer); + if ($layer == 'both' || $layer == 'all') { + foreach ($this->files as $type => $file) { + $err = $this->writeConfigFile($file, $type, $data); + if (PEAR::isError($err)) { + return $err; + } + } + return true; + } + + if (empty($this->files[$layer])) { + return $this->raiseError("unknown config file type `$layer'"); + } + + if ($file === null) { + $file = $this->files[$layer]; + } + + $data = ($data === null) ? $this->configuration[$layer] : $data; + $this->_encodeOutput($data); + $opt = array('-p', dirname($file)); + if (!@System::mkDir($opt)) { + return $this->raiseError("could not create directory: " . dirname($file)); + } + + if (file_exists($file) && is_file($file) && !is_writeable($file)) { + return $this->raiseError("no write access to $file!"); + } + + $fp = @fopen($file, "w"); + if (!$fp) { + return $this->raiseError("PEAR_Config::writeConfigFile fopen('$file','w') failed ($php_errormsg)"); + } + + $contents = "#PEAR_Config 0.9\n" . serialize($data); + if (!@fwrite($fp, $contents)) { + return $this->raiseError("PEAR_Config::writeConfigFile: fwrite failed ($php_errormsg)"); + } + return true; + } + + /** + * Reads configuration data from a file and returns the parsed data + * in an array. + * + * @param string file to read from + * @return array configuration data or a PEAR error on failure + */ + private function _readConfigDataFrom($file) + { + $fp = false; + if (file_exists($file)) { + $fp = @fopen($file, "r"); + } + + if (!$fp) { + return $this->raiseError("PEAR_Config::readConfigFile fopen('$file','r') failed"); + } + + $size = filesize($file); + fclose($fp); + $contents = file_get_contents($file); + if (empty($contents)) { + return $this->raiseError('Configuration file "' . $file . '" is empty'); + } + + $version = false; + if (preg_match('/^#PEAR_Config\s+(\S+)\s+/si', $contents, $matches)) { + $version = $matches[1]; + $contents = substr($contents, strlen($matches[0])); + } else { + // Museum config file + if (substr($contents,0,2) == 'a:') { + $version = '0.1'; + } + } + + if ($version && version_compare("$version", '1', '<')) { + // no '@', it is possible that unserialize + // raises a notice but it seems to block IO to + // STDOUT if a '@' is used and a notice is raise + $data = unserialize($contents); + + if (!is_array($data) && !$data) { + if ($contents == serialize(false)) { + $data = array(); + } else { + $err = $this->raiseError("PEAR_Config: bad data in $file"); + return $err; + } + } + if (!is_array($data)) { + if (strlen(trim($contents)) > 0) { + $error = "PEAR_Config: bad data in $file"; + $err = $this->raiseError($error); + return $err; + } + + $data = array(); + } + // add parsing of newer formats here... + } else { + $err = $this->raiseError("$file: unknown version `$version'"); + return $err; + } + + return $data; + } + + /** + * Gets the file used for storing the config for a layer + * + * @param string $layer 'user' or 'system' + */ + function getConfFile($layer) + { + return $this->files[$layer]; + } + + /** + * @param string Configuration class name, used for detecting duplicate calls + * @param array information on a role as parsed from its xml file + * @return true|PEAR_Error + * @access private + */ + function _addConfigVars($class, $vars) + { + static $called = array(); + if (isset($called[$class])) { + return; + } + + $called[$class] = 1; + if (count($vars) > 3) { + return $this->raiseError('Roles can only define 3 new config variables or less'); + } + + foreach ($vars as $name => $var) { + if (!is_array($var)) { + return $this->raiseError('Configuration information must be an array'); + } + + if (!isset($var['type'])) { + return $this->raiseError('Configuration information must contain a type'); + } elseif (!in_array($var['type'], + array('string', 'mask', 'password', 'directory', 'file', 'set'))) { + return $this->raiseError( + 'Configuration type must be one of directory, file, string, ' . + 'mask, set, or password'); + } + if (!isset($var['default'])) { + return $this->raiseError( + 'Configuration information must contain a default value ("default" index)'); + } + + if (is_array($var['default'])) { + $real_default = ''; + foreach ($var['default'] as $config_var => $val) { + if (strpos($config_var, 'text') === 0) { + $real_default .= $val; + } elseif (strpos($config_var, 'constant') === 0) { + if (!defined($val)) { + return $this->raiseError( + 'Unknown constant "' . $val . '" requested in ' . + 'default value for configuration variable "' . + $name . '"'); + } + + $real_default .= constant($val); + } elseif (isset($this->configuration_info[$config_var])) { + $real_default .= + $this->configuration_info[$config_var]['default']; + } else { + return $this->raiseError( + 'Unknown request for "' . $config_var . '" value in ' . + 'default value for configuration variable "' . + $name . '"'); + } + } + $var['default'] = $real_default; + } + + if ($var['type'] == 'integer') { + $var['default'] = (integer) $var['default']; + } + + if (!isset($var['doc'])) { + return $this->raiseError( + 'Configuration information must contain a summary ("doc" index)'); + } + + if (!isset($var['prompt'])) { + return $this->raiseError( + 'Configuration information must contain a simple prompt ("prompt" index)'); + } + + if (!isset($var['group'])) { + return $this->raiseError( + 'Configuration information must contain a simple group ("group" index)'); + } + + if (isset($this->configuration_info[$name])) { + return $this->raiseError('Configuration variable "' . $name . + '" already exists'); + } + + $this->configuration_info[$name] = $var; + // fix bug #7351: setting custom config variable in a channel fails + $this->_channelConfigInfo[] = $name; + } + + return true; + } + + /** + * Encodes/scrambles configuration data before writing to files. + * Currently, 'password' values will be base64-encoded as to avoid + * that people spot cleartext passwords by accident. + * + * @param array (reference) array to encode values in + * @return bool TRUE on success + * @access private + */ + function _encodeOutput(&$data) + { + foreach ($data as $key => $value) { + if ($key == '__channels') { + foreach ($data['__channels'] as $channel => $blah) { + $this->_encodeOutput($data['__channels'][$channel]); + } + } + + if (!isset($this->configuration_info[$key])) { + continue; + } + + $type = $this->configuration_info[$key]['type']; + switch ($type) { + // we base64-encode passwords so they are at least + // not shown in plain by accident + case 'password': { + $data[$key] = base64_encode($data[$key]); + break; + } + case 'mask': { + $data[$key] = octdec($data[$key]); + break; + } + } + } + + return true; + } + + /** + * Decodes/unscrambles configuration data after reading from files. + * + * @param array (reference) array to encode values in + * @return bool TRUE on success + * @access private + * + * @see PEAR_Config::_encodeOutput + */ + function _decodeInput(&$data) + { + if (!is_array($data)) { + return true; + } + + foreach ($data as $key => $value) { + if ($key == '__channels') { + foreach ($data['__channels'] as $channel => $blah) { + $this->_decodeInput($data['__channels'][$channel]); + } + } + + if (!isset($this->configuration_info[$key])) { + continue; + } + + $type = $this->configuration_info[$key]['type']; + switch ($type) { + case 'password': { + $data[$key] = base64_decode($data[$key]); + break; + } + case 'mask': { + $data[$key] = decoct($data[$key]); + break; + } + } + } + + return true; + } + + /** + * Retrieve the default channel. + * + * On startup, channels are not initialized, so if the default channel is not + * pear.php.net, then initialize the config. + * @param string registry layer + * @return string|false + */ + function getDefaultChannel($layer = null) + { + $ret = false; + if ($layer === null) { + foreach ($this->layers as $layer) { + if (isset($this->configuration[$layer]['default_channel'])) { + $ret = $this->configuration[$layer]['default_channel']; + break; + } + } + } elseif (isset($this->configuration[$layer]['default_channel'])) { + $ret = $this->configuration[$layer]['default_channel']; + } + + if ($ret == 'pear.php.net' && defined('PEAR_RUNTYPE') && PEAR_RUNTYPE == 'pecl') { + $ret = 'pecl.php.net'; + } + + if ($ret) { + if ($ret != 'pear.php.net') { + $this->_lazyChannelSetup(); + } + + return $ret; + } + + return PEAR_CONFIG_DEFAULT_CHANNEL; + } + + /** + * Returns a configuration value, prioritizing layers as per the + * layers property. + * + * @param string config key + * @return mixed the config value, or NULL if not found + * @access public + */ + function get($key, $layer = null, $channel = false) + { + if (!isset($this->configuration_info[$key])) { + return null; + } + + if ($key == '__channels') { + return null; + } + + if ($key == 'default_channel') { + return $this->getDefaultChannel($layer); + } + + if (!$channel) { + $channel = $this->getDefaultChannel(); + } elseif ($channel != 'pear.php.net') { + $this->_lazyChannelSetup(); + } + $channel = strtolower($channel); + + $test = (in_array($key, $this->_channelConfigInfo)) ? + $this->_getChannelValue($key, $layer, $channel) : + null; + if ($test !== null) { + if ($this->_installRoot) { + if (in_array($this->getGroup($key), + array('File Locations', 'File Locations (Advanced)')) && + $this->getType($key) == 'directory') { + return $this->_prependPath($test, $this->_installRoot); + } + } + return $test; + } + + if ($layer === null) { + foreach ($this->layers as $layer) { + if (isset($this->configuration[$layer][$key])) { + $test = $this->configuration[$layer][$key]; + if ($this->_installRoot) { + if (in_array($this->getGroup($key), + array('File Locations', 'File Locations (Advanced)')) && + $this->getType($key) == 'directory') { + return $this->_prependPath($test, $this->_installRoot); + } + } + + if ($key == 'preferred_mirror') { + $reg = &$this->getRegistry(); + if (is_object($reg)) { + $chan = &$reg->getChannel($channel); + if (PEAR::isError($chan)) { + return $channel; + } + + if (!$chan->getMirror($test) && $chan->getName() != $test) { + return $channel; // mirror does not exist + } + } + } + return $test; + } + } + } elseif (isset($this->configuration[$layer][$key])) { + $test = $this->configuration[$layer][$key]; + if ($this->_installRoot) { + if (in_array($this->getGroup($key), + array('File Locations', 'File Locations (Advanced)')) && + $this->getType($key) == 'directory') { + return $this->_prependPath($test, $this->_installRoot); + } + } + + if ($key == 'preferred_mirror') { + $reg = &$this->getRegistry(); + if (is_object($reg)) { + $chan = &$reg->getChannel($channel); + if (PEAR::isError($chan)) { + return $channel; + } + + if (!$chan->getMirror($test) && $chan->getName() != $test) { + return $channel; // mirror does not exist + } + } + } + + return $test; + } + + return null; + } + + /** + * Returns a channel-specific configuration value, prioritizing layers as per the + * layers property. + * + * @param string config key + * @return mixed the config value, or NULL if not found + * @access private + */ + function _getChannelValue($key, $layer, $channel) + { + if ($key == '__channels' || $channel == 'pear.php.net') { + return null; + } + + $ret = null; + if ($layer === null) { + foreach ($this->layers as $ilayer) { + if (isset($this->configuration[$ilayer]['__channels'][$channel][$key])) { + $ret = $this->configuration[$ilayer]['__channels'][$channel][$key]; + break; + } + } + } elseif (isset($this->configuration[$layer]['__channels'][$channel][$key])) { + $ret = $this->configuration[$layer]['__channels'][$channel][$key]; + } + + if ($key != 'preferred_mirror') { + return $ret; + } + + + if ($ret !== null) { + $reg = &$this->getRegistry($layer); + if (is_object($reg)) { + $chan = &$reg->getChannel($channel); + if (PEAR::isError($chan)) { + return $channel; + } + + if (!$chan->getMirror($ret) && $chan->getName() != $ret) { + return $channel; // mirror does not exist + } + } + + return $ret; + } + + if ($channel != $this->getDefaultChannel($layer)) { + return $channel; // we must use the channel name as the preferred mirror + // if the user has not chosen an alternate + } + + return $this->getDefaultChannel($layer); + } + + /** + * Set a config value in a specific layer (defaults to 'user'). + * Enforces the types defined in the configuration_info array. An + * integer config variable will be cast to int, and a set config + * variable will be validated against its legal values. + * + * @param string config key + * @param string config value + * @param string (optional) config layer + * @param string channel to set this value for, or null for global value + * @return bool TRUE on success, FALSE on failure + */ + function set($key, $value, $layer = 'user', $channel = false) + { + if ($key == '__channels') { + return false; + } + + if (!isset($this->configuration[$layer])) { + return false; + } + + if ($key == 'default_channel') { + // can only set this value globally + $channel = 'pear.php.net'; + if ($value != 'pear.php.net') { + $this->_lazyChannelSetup($layer); + } + } + + if ($key == 'preferred_mirror') { + if ($channel == '__uri') { + return false; // can't set the __uri pseudo-channel's mirror + } + + $reg = &$this->getRegistry($layer); + if (is_object($reg)) { + $chan = &$reg->getChannel($channel ? $channel : 'pear.php.net'); + if (PEAR::isError($chan)) { + return false; + } + + if (!$chan->getMirror($value) && $chan->getName() != $value) { + return false; // mirror does not exist + } + } + } + + if (!isset($this->configuration_info[$key])) { + return false; + } + + extract($this->configuration_info[$key]); + switch ($type) { + case 'integer': + $value = (int)$value; + break; + case 'set': { + // If a valid_set is specified, require the value to + // be in the set. If there is no valid_set, accept + // any value. + if ($valid_set) { + reset($valid_set); + if ((key($valid_set) === 0 && !in_array($value, $valid_set)) || + (key($valid_set) !== 0 && empty($valid_set[$value]))) + { + return false; + } + } + break; + } + } + + if (!$channel) { + $channel = $this->get('default_channel', null, 'pear.php.net'); + } + + if (!in_array($channel, $this->_channels)) { + $this->_lazyChannelSetup($layer); + $reg = &$this->getRegistry($layer); + if ($reg) { + $channel = $reg->channelName($channel); + } + + if (!in_array($channel, $this->_channels)) { + return false; + } + } + + if ($channel != 'pear.php.net') { + if (in_array($key, $this->_channelConfigInfo)) { + $this->configuration[$layer]['__channels'][$channel][$key] = $value; + return true; + } + + return false; + } + + if ($key == 'default_channel') { + if (!isset($reg)) { + $reg = &$this->getRegistry($layer); + if (!$reg) { + $reg = &$this->getRegistry(); + } + } + + if ($reg) { + $value = $reg->channelName($value); + } + + if (!$value) { + return false; + } + } + + $this->configuration[$layer][$key] = $value; + if ($key == 'php_dir' && !$this->_noRegistry) { + if (!isset($this->_registry[$layer]) || + $value != $this->_registry[$layer]->install_dir) { + $this->_registry[$layer] = &new PEAR_Registry($value); + $this->_regInitialized[$layer] = false; + $this->_registry[$layer]->setConfig($this, false); + } + } + + return true; + } + + function _lazyChannelSetup($uselayer = false) + { + if ($this->_noRegistry) { + return; + } + + $merge = false; + foreach ($this->_registry as $layer => $p) { + if ($uselayer && $uselayer != $layer) { + continue; + } + + if (!$this->_regInitialized[$layer]) { + if ($layer == 'default' && isset($this->_registry['user']) || + isset($this->_registry['system'])) { + // only use the default registry if there are no alternatives + continue; + } + + if (!is_object($this->_registry[$layer])) { + if ($phpdir = $this->get('php_dir', $layer, 'pear.php.net')) { + $this->_registry[$layer] = &new PEAR_Registry($phpdir); + $this->_registry[$layer]->setConfig($this, false); + $this->_regInitialized[$layer] = false; + } else { + unset($this->_registry[$layer]); + return; + } + } + + $this->setChannels($this->_registry[$layer]->listChannels(), $merge); + $this->_regInitialized[$layer] = true; + $merge = true; + } + } + } + + /** + * Set the list of channels. + * + * This should be set via a call to {@link PEAR_Registry::listChannels()} + * @param array + * @param bool + * @return bool success of operation + */ + function setChannels($channels, $merge = false) + { + if (!is_array($channels)) { + return false; + } + + if ($merge) { + $this->_channels = array_merge($this->_channels, $channels); + } else { + $this->_channels = $channels; + } + + foreach ($channels as $channel) { + $channel = strtolower($channel); + if ($channel == 'pear.php.net') { + continue; + } + + foreach ($this->layers as $layer) { + if (!isset($this->configuration[$layer]['__channels'])) { + $this->configuration[$layer]['__channels'] = array(); + } + if (!isset($this->configuration[$layer]['__channels'][$channel]) + || !is_array($this->configuration[$layer]['__channels'][$channel])) { + $this->configuration[$layer]['__channels'][$channel] = array(); + } + } + } + + return true; + } + + /** + * Get the type of a config value. + * + * @param string config key + * + * @return string type, one of "string", "integer", "file", + * "directory", "set" or "password". + * + * @access public + * + */ + function getType($key) + { + if (isset($this->configuration_info[$key])) { + return $this->configuration_info[$key]['type']; + } + return false; + } + + /** + * Get the documentation for a config value. + * + * @param string config key + * @return string documentation string + * + * @access public + * + */ + function getDocs($key) + { + if (isset($this->configuration_info[$key])) { + return $this->configuration_info[$key]['doc']; + } + + return false; + } + + /** + * Get the short documentation for a config value. + * + * @param string config key + * @return string short documentation string + * + * @access public + * + */ + function getPrompt($key) + { + if (isset($this->configuration_info[$key])) { + return $this->configuration_info[$key]['prompt']; + } + + return false; + } + + /** + * Get the parameter group for a config key. + * + * @param string config key + * @return string parameter group + * + * @access public + * + */ + function getGroup($key) + { + if (isset($this->configuration_info[$key])) { + return $this->configuration_info[$key]['group']; + } + + return false; + } + + /** + * Get the list of parameter groups. + * + * @return array list of parameter groups + * + * @access public + * + */ + function getGroups() + { + $tmp = array(); + foreach ($this->configuration_info as $key => $info) { + $tmp[$info['group']] = 1; + } + + return array_keys($tmp); + } + + /** + * Get the list of the parameters in a group. + * + * @param string $group parameter group + * @return array list of parameters in $group + * + * @access public + * + */ + function getGroupKeys($group) + { + $keys = array(); + foreach ($this->configuration_info as $key => $info) { + if ($info['group'] == $group) { + $keys[] = $key; + } + } + + return $keys; + } + + /** + * Get the list of allowed set values for a config value. Returns + * NULL for config values that are not sets. + * + * @param string config key + * @return array enumerated array of set values, or NULL if the + * config key is unknown or not a set + * + * @access public + * + */ + function getSetValues($key) + { + if (isset($this->configuration_info[$key]) && + isset($this->configuration_info[$key]['type']) && + $this->configuration_info[$key]['type'] == 'set') + { + $valid_set = $this->configuration_info[$key]['valid_set']; + reset($valid_set); + if (key($valid_set) === 0) { + return $valid_set; + } + + return array_keys($valid_set); + } + + return null; + } + + /** + * Get all the current config keys. + * + * @return array simple array of config keys + * + * @access public + */ + function getKeys() + { + $keys = array(); + foreach ($this->layers as $layer) { + $test = $this->configuration[$layer]; + if (isset($test['__channels'])) { + foreach ($test['__channels'] as $channel => $configs) { + $keys = array_merge($keys, $configs); + } + } + + unset($test['__channels']); + $keys = array_merge($keys, $test); + + } + return array_keys($keys); + } + + /** + * Remove the a config key from a specific config layer. + * + * @param string config key + * @param string (optional) config layer + * @param string (optional) channel (defaults to default channel) + * @return bool TRUE on success, FALSE on failure + * + * @access public + */ + function remove($key, $layer = 'user', $channel = null) + { + if ($channel === null) { + $channel = $this->getDefaultChannel(); + } + + if ($channel !== 'pear.php.net') { + if (isset($this->configuration[$layer]['__channels'][$channel][$key])) { + unset($this->configuration[$layer]['__channels'][$channel][$key]); + return true; + } + } + + if (isset($this->configuration[$layer][$key])) { + unset($this->configuration[$layer][$key]); + return true; + } + + return false; + } + + /** + * Temporarily remove an entire config layer. USE WITH CARE! + * + * @param string config key + * @param string (optional) config layer + * @return bool TRUE on success, FALSE on failure + * + * @access public + */ + function removeLayer($layer) + { + if (isset($this->configuration[$layer])) { + $this->configuration[$layer] = array(); + return true; + } + + return false; + } + + /** + * Stores configuration data in a layer. + * + * @param string config layer to store + * @return bool TRUE on success, or PEAR error on failure + * + * @access public + */ + function store($layer = 'user', $data = null) + { + return $this->writeConfigFile(null, $layer, $data); + } + + /** + * Tells what config layer that gets to define a key. + * + * @param string config key + * @param boolean return the defining channel + * + * @return string|array the config layer, or an empty string if not found. + * + * if $returnchannel, the return is an array array('layer' => layername, + * 'channel' => channelname), or an empty string if not found + * + * @access public + */ + function definedBy($key, $returnchannel = false) + { + foreach ($this->layers as $layer) { + $channel = $this->getDefaultChannel(); + if ($channel !== 'pear.php.net') { + if (isset($this->configuration[$layer]['__channels'][$channel][$key])) { + if ($returnchannel) { + return array('layer' => $layer, 'channel' => $channel); + } + return $layer; + } + } + + if (isset($this->configuration[$layer][$key])) { + if ($returnchannel) { + return array('layer' => $layer, 'channel' => 'pear.php.net'); + } + return $layer; + } + } + + return ''; + } + + /** + * Tells whether a given key exists as a config value. + * + * @param string config key + * @return bool whether exists in this object + * + * @access public + */ + function isDefined($key) + { + foreach ($this->layers as $layer) { + if (isset($this->configuration[$layer][$key])) { + return true; + } + } + + return false; + } + + /** + * Tells whether a given config layer exists. + * + * @param string config layer + * @return bool whether exists in this object + * + * @access public + */ + function isDefinedLayer($layer) + { + return isset($this->configuration[$layer]); + } + + /** + * Returns the layers defined (except the 'default' one) + * + * @return array of the defined layers + */ + function getLayers() + { + $cf = $this->configuration; + unset($cf['default']); + return array_keys($cf); + } + + function apiVersion() + { + return '1.1'; + } + + /** + * @return PEAR_Registry + */ + function &getRegistry($use = null) + { + $layer = $use === null ? 'user' : $use; + if (isset($this->_registry[$layer])) { + return $this->_registry[$layer]; + } elseif ($use === null && isset($this->_registry['system'])) { + return $this->_registry['system']; + } elseif ($use === null && isset($this->_registry['default'])) { + return $this->_registry['default']; + } elseif ($use) { + $a = false; + return $a; + } + + // only go here if null was passed in + echo "CRITICAL ERROR: Registry could not be initialized from any value"; + exit(1); + } + + /** + * This is to allow customization like the use of installroot + * @param PEAR_Registry + * @return bool + */ + function setRegistry(&$reg, $layer = 'user') + { + if ($this->_noRegistry) { + return false; + } + + if (!in_array($layer, array('user', 'system'))) { + return false; + } + + $this->_registry[$layer] = &$reg; + if (is_object($reg)) { + $this->_registry[$layer]->setConfig($this, false); + } + + return true; + } + + function noRegistry() + { + $this->_noRegistry = true; + } + + /** + * @return PEAR_REST + */ + function &getREST($version, $options = array()) + { + $version = str_replace('.', '', $version); + if (!class_exists($class = 'PEAR_REST_' . $version)) { + require_once 'PEAR/REST/' . $version . '.php'; + } + + $remote = &new $class($this, $options); + return $remote; + } + + /** + * The ftp server is set in {@link readFTPConfigFile()}. It exists only if a + * remote configuration file has been specified + * @return PEAR_FTP|false + */ + function &getFTP() + { + if (isset($this->_ftp)) { + return $this->_ftp; + } + + $a = false; + return $a; + } + + function _prependPath($path, $prepend) + { + if (strlen($prepend) > 0) { + if (OS_WINDOWS && preg_match('/^[a-z]:/i', $path)) { + if (preg_match('/^[a-z]:/i', $prepend)) { + $prepend = substr($prepend, 2); + } elseif ($prepend{0} != '\\') { + $prepend = "\\$prepend"; + } + $path = substr($path, 0, 2) . $prepend . substr($path, 2); + } else { + $path = $prepend . $path; + } + } + return $path; + } + + /** + * @param string|false installation directory to prepend to all _dir variables, or false to + * disable + */ + function setInstallRoot($root) + { + if (substr($root, -1) == DIRECTORY_SEPARATOR) { + $root = substr($root, 0, -1); + } + $old = $this->_installRoot; + $this->_installRoot = $root; + if (($old != $root) && !$this->_noRegistry) { + foreach (array_keys($this->_registry) as $layer) { + if ($layer == 'ftp' || !isset($this->_registry[$layer])) { + continue; + } + $this->_registry[$layer] = + &new PEAR_Registry($this->get('php_dir', $layer, 'pear.php.net')); + $this->_registry[$layer]->setConfig($this, false); + $this->_regInitialized[$layer] = false; + } + } + } +} diff --git a/includes/pear/PEAR/Dependency2.php b/includes/pear/PEAR/Dependency2.php new file mode 100644 index 0000000..6d78964 --- /dev/null +++ b/includes/pear/PEAR/Dependency2.php @@ -0,0 +1,1362 @@ + + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a1 + */ + +/** + * Required for the PEAR_VALIDATE_* constants + */ +require_once 'PEAR/Validate.php'; + +/** + * Dependency check for PEAR packages + * + * This class handles both version 1.0 and 2.0 dependencies + * WARNING: *any* changes to this class must be duplicated in the + * test_PEAR_Dependency2 class found in tests/PEAR_Dependency2/setup.php.inc, + * or unit tests will not actually validate the changes + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @PEAR-VER@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a1 + */ +class PEAR_Dependency2 +{ + /** + * One of the PEAR_VALIDATE_* states + * @see PEAR_VALIDATE_NORMAL + * @var integer + */ + var $_state; + + /** + * Command-line options to install/upgrade/uninstall commands + * @param array + */ + var $_options; + + /** + * @var OS_Guess + */ + var $_os; + + /** + * @var PEAR_Registry + */ + var $_registry; + + /** + * @var PEAR_Config + */ + var $_config; + + /** + * @var PEAR_DependencyDB + */ + var $_dependencydb; + + /** + * Output of PEAR_Registry::parsedPackageName() + * @var array + */ + var $_currentPackage; + + /** + * @param PEAR_Config + * @param array installation options + * @param array format of PEAR_Registry::parsedPackageName() + * @param int installation state (one of PEAR_VALIDATE_*) + */ + function PEAR_Dependency2(&$config, $installoptions, $package, + $state = PEAR_VALIDATE_INSTALLING) + { + $this->_config = &$config; + if (!class_exists('PEAR_DependencyDB')) { + require_once 'PEAR/DependencyDB.php'; + } + + if (isset($installoptions['packagingroot'])) { + // make sure depdb is in the right location + $config->setInstallRoot($installoptions['packagingroot']); + } + + $this->_registry = &$config->getRegistry(); + $this->_dependencydb = &PEAR_DependencyDB::singleton($config); + if (isset($installoptions['packagingroot'])) { + $config->setInstallRoot(false); + } + + $this->_options = $installoptions; + $this->_state = $state; + if (!class_exists('OS_Guess')) { + require_once 'OS/Guess.php'; + } + + $this->_os = new OS_Guess; + $this->_currentPackage = $package; + } + + function _getExtraString($dep) + { + $extra = ' ('; + if (isset($dep['uri'])) { + return ''; + } + + if (isset($dep['recommended'])) { + $extra .= 'recommended version ' . $dep['recommended']; + } else { + if (isset($dep['min'])) { + $extra .= 'version >= ' . $dep['min']; + } + + if (isset($dep['max'])) { + if ($extra != ' (') { + $extra .= ', '; + } + $extra .= 'version <= ' . $dep['max']; + } + + if (isset($dep['exclude'])) { + if (!is_array($dep['exclude'])) { + $dep['exclude'] = array($dep['exclude']); + } + + if ($extra != ' (') { + $extra .= ', '; + } + + $extra .= 'excluded versions: '; + foreach ($dep['exclude'] as $i => $exclude) { + if ($i) { + $extra .= ', '; + } + $extra .= $exclude; + } + } + } + + $extra .= ')'; + if ($extra == ' ()') { + $extra = ''; + } + + return $extra; + } + + /** + * This makes unit-testing a heck of a lot easier + */ + function getPHP_OS() + { + return PHP_OS; + } + + /** + * This makes unit-testing a heck of a lot easier + */ + function getsysname() + { + return $this->_os->getSysname(); + } + + /** + * Specify a dependency on an OS. Use arch for detailed os/processor information + * + * There are two generic OS dependencies that will be the most common, unix and windows. + * Other options are linux, freebsd, darwin (OS X), sunos, irix, hpux, aix + */ + function validateOsDependency($dep) + { + if ($this->_state != PEAR_VALIDATE_INSTALLING && $this->_state != PEAR_VALIDATE_DOWNLOADING) { + return true; + } + + if ($dep['name'] == '*') { + return true; + } + + $not = isset($dep['conflicts']) ? true : false; + switch (strtolower($dep['name'])) { + case 'windows' : + if ($not) { + if (strtolower(substr($this->getPHP_OS(), 0, 3)) == 'win') { + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { + return $this->raiseError("Cannot install %s on Windows"); + } + + return $this->warning("warning: Cannot install %s on Windows"); + } + } else { + if (strtolower(substr($this->getPHP_OS(), 0, 3)) != 'win') { + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { + return $this->raiseError("Can only install %s on Windows"); + } + + return $this->warning("warning: Can only install %s on Windows"); + } + } + break; + case 'unix' : + $unices = array('linux', 'freebsd', 'darwin', 'sunos', 'irix', 'hpux', 'aix'); + if ($not) { + if (in_array($this->getSysname(), $unices)) { + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { + return $this->raiseError("Cannot install %s on any Unix system"); + } + + return $this->warning( "warning: Cannot install %s on any Unix system"); + } + } else { + if (!in_array($this->getSysname(), $unices)) { + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { + return $this->raiseError("Can only install %s on a Unix system"); + } + + return $this->warning("warning: Can only install %s on a Unix system"); + } + } + break; + default : + if ($not) { + if (strtolower($dep['name']) == strtolower($this->getSysname())) { + if (!isset($this->_options['nodeps']) && + !isset($this->_options['force'])) { + return $this->raiseError('Cannot install %s on ' . $dep['name'] . + ' operating system'); + } + + return $this->warning('warning: Cannot install %s on ' . + $dep['name'] . ' operating system'); + } + } else { + if (strtolower($dep['name']) != strtolower($this->getSysname())) { + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { + return $this->raiseError('Cannot install %s on ' . + $this->getSysname() . + ' operating system, can only install on ' . $dep['name']); + } + + return $this->warning('warning: Cannot install %s on ' . + $this->getSysname() . + ' operating system, can only install on ' . $dep['name']); + } + } + } + return true; + } + + /** + * This makes unit-testing a heck of a lot easier + */ + function matchSignature($pattern) + { + return $this->_os->matchSignature($pattern); + } + + /** + * Specify a complex dependency on an OS/processor/kernel version, + * Use OS for simple operating system dependency. + * + * This is the only dependency that accepts an eregable pattern. The pattern + * will be matched against the php_uname() output parsed by OS_Guess + */ + function validateArchDependency($dep) + { + if ($this->_state != PEAR_VALIDATE_INSTALLING) { + return true; + } + + $not = isset($dep['conflicts']) ? true : false; + if (!$this->matchSignature($dep['pattern'])) { + if (!$not) { + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { + return $this->raiseError('%s Architecture dependency failed, does not ' . + 'match "' . $dep['pattern'] . '"'); + } + + return $this->warning('warning: %s Architecture dependency failed, does ' . + 'not match "' . $dep['pattern'] . '"'); + } + + return true; + } + + if ($not) { + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { + return $this->raiseError('%s Architecture dependency failed, required "' . + $dep['pattern'] . '"'); + } + + return $this->warning('warning: %s Architecture dependency failed, ' . + 'required "' . $dep['pattern'] . '"'); + } + + return true; + } + + /** + * This makes unit-testing a heck of a lot easier + */ + function extension_loaded($name) + { + return extension_loaded($name); + } + + /** + * This makes unit-testing a heck of a lot easier + */ + function phpversion($name = null) + { + if ($name !== null) { + return phpversion($name); + } + + return phpversion(); + } + + function validateExtensionDependency($dep, $required = true) + { + if ($this->_state != PEAR_VALIDATE_INSTALLING && + $this->_state != PEAR_VALIDATE_DOWNLOADING) { + return true; + } + + $loaded = $this->extension_loaded($dep['name']); + $extra = $this->_getExtraString($dep); + if (isset($dep['exclude'])) { + if (!is_array($dep['exclude'])) { + $dep['exclude'] = array($dep['exclude']); + } + } + + if (!isset($dep['min']) && !isset($dep['max']) && + !isset($dep['recommended']) && !isset($dep['exclude']) + ) { + if ($loaded) { + if (isset($dep['conflicts'])) { + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { + return $this->raiseError('%s conflicts with PHP extension "' . + $dep['name'] . '"' . $extra); + } + + return $this->warning('warning: %s conflicts with PHP extension "' . + $dep['name'] . '"' . $extra); + } + + return true; + } + + if (isset($dep['conflicts'])) { + return true; + } + + if ($required) { + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { + return $this->raiseError('%s requires PHP extension "' . + $dep['name'] . '"' . $extra); + } + + return $this->warning('warning: %s requires PHP extension "' . + $dep['name'] . '"' . $extra); + } + + return $this->warning('%s can optionally use PHP extension "' . + $dep['name'] . '"' . $extra); + } + + if (!$loaded) { + if (isset($dep['conflicts'])) { + return true; + } + + if (!$required) { + return $this->warning('%s can optionally use PHP extension "' . + $dep['name'] . '"' . $extra); + } + + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { + return $this->raiseError('%s requires PHP extension "' . $dep['name'] . + '"' . $extra); + } + + return $this->warning('warning: %s requires PHP extension "' . $dep['name'] . + '"' . $extra); + } + + $version = (string) $this->phpversion($dep['name']); + if (empty($version)) { + $version = '0'; + } + + $fail = false; + if (isset($dep['min']) && !version_compare($version, $dep['min'], '>=')) { + $fail = true; + } + + if (isset($dep['max']) && !version_compare($version, $dep['max'], '<=')) { + $fail = true; + } + + if ($fail && !isset($dep['conflicts'])) { + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { + return $this->raiseError('%s requires PHP extension "' . $dep['name'] . + '"' . $extra . ', installed version is ' . $version); + } + + return $this->warning('warning: %s requires PHP extension "' . $dep['name'] . + '"' . $extra . ', installed version is ' . $version); + } elseif ((isset($dep['min']) || isset($dep['max'])) && !$fail && isset($dep['conflicts'])) { + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { + return $this->raiseError('%s conflicts with PHP extension "' . + $dep['name'] . '"' . $extra . ', installed version is ' . $version); + } + + return $this->warning('warning: %s conflicts with PHP extension "' . + $dep['name'] . '"' . $extra . ', installed version is ' . $version); + } + + if (isset($dep['exclude'])) { + foreach ($dep['exclude'] as $exclude) { + if (version_compare($version, $exclude, '==')) { + if (isset($dep['conflicts'])) { + continue; + } + + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { + return $this->raiseError('%s is not compatible with PHP extension "' . + $dep['name'] . '" version ' . + $exclude); + } + + return $this->warning('warning: %s is not compatible with PHP extension "' . + $dep['name'] . '" version ' . + $exclude); + } elseif (version_compare($version, $exclude, '!=') && isset($dep['conflicts'])) { + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { + return $this->raiseError('%s conflicts with PHP extension "' . + $dep['name'] . '"' . $extra . ', installed version is ' . $version); + } + + return $this->warning('warning: %s conflicts with PHP extension "' . + $dep['name'] . '"' . $extra . ', installed version is ' . $version); + } + } + } + + if (isset($dep['recommended'])) { + if (version_compare($version, $dep['recommended'], '==')) { + return true; + } + + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { + return $this->raiseError('%s dependency: PHP extension ' . $dep['name'] . + ' version "' . $version . '"' . + ' is not the recommended version "' . $dep['recommended'] . + '", but may be compatible, use --force to install'); + } + + return $this->warning('warning: %s dependency: PHP extension ' . + $dep['name'] . ' version "' . $version . '"' . + ' is not the recommended version "' . $dep['recommended'].'"'); + } + + return true; + } + + function validatePhpDependency($dep) + { + if ($this->_state != PEAR_VALIDATE_INSTALLING && + $this->_state != PEAR_VALIDATE_DOWNLOADING) { + return true; + } + + $version = $this->phpversion(); + $extra = $this->_getExtraString($dep); + if (isset($dep['exclude'])) { + if (!is_array($dep['exclude'])) { + $dep['exclude'] = array($dep['exclude']); + } + } + + if (isset($dep['min'])) { + if (!version_compare($version, $dep['min'], '>=')) { + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { + return $this->raiseError('%s requires PHP' . + $extra . ', installed version is ' . $version); + } + + return $this->warning('warning: %s requires PHP' . + $extra . ', installed version is ' . $version); + } + } + + if (isset($dep['max'])) { + if (!version_compare($version, $dep['max'], '<=')) { + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { + return $this->raiseError('%s requires PHP' . + $extra . ', installed version is ' . $version); + } + + return $this->warning('warning: %s requires PHP' . + $extra . ', installed version is ' . $version); + } + } + + if (isset($dep['exclude'])) { + foreach ($dep['exclude'] as $exclude) { + if (version_compare($version, $exclude, '==')) { + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { + return $this->raiseError('%s is not compatible with PHP version ' . + $exclude); + } + + return $this->warning( + 'warning: %s is not compatible with PHP version ' . + $exclude); + } + } + } + + return true; + } + + /** + * This makes unit-testing a heck of a lot easier + */ + function getPEARVersion() + { + return '1.9.4'; + } + + function validatePearinstallerDependency($dep) + { + $pearversion = $this->getPEARVersion(); + if (array_key_exists('PEAR_RUNTESTS_PEAR_VERSION_RETURN', $GLOBALS)) { + // A means for testing pear-core in local repositories. + return $GLOBALS['PEAR_RUNTESTS_PEAR_VERSION_RETURN']; + } + $extra = $this->_getExtraString($dep); + if (isset($dep['exclude'])) { + if (!is_array($dep['exclude'])) { + $dep['exclude'] = array($dep['exclude']); + } + } + + if (version_compare($pearversion, $dep['min'], '<')) { + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { + return $this->raiseError('%s requires PEAR Installer' . $extra . + ', installed version is ' . $pearversion); + } + + return $this->warning('warning: %s requires PEAR Installer' . $extra . + ', installed version is ' . $pearversion); + } + + if (isset($dep['max'])) { + if (version_compare($pearversion, $dep['max'], '>')) { + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { + return $this->raiseError('%s requires PEAR Installer' . $extra . + ', installed version is ' . $pearversion); + } + + return $this->warning('warning: %s requires PEAR Installer' . $extra . + ', installed version is ' . $pearversion); + } + } + + if (isset($dep['exclude'])) { + if (!isset($dep['exclude'][0])) { + $dep['exclude'] = array($dep['exclude']); + } + + foreach ($dep['exclude'] as $exclude) { + if (version_compare($exclude, $pearversion, '==')) { + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { + return $this->raiseError('%s is not compatible with PEAR Installer ' . + 'version ' . $exclude); + } + + return $this->warning('warning: %s is not compatible with PEAR ' . + 'Installer version ' . $exclude); + } + } + } + + return true; + } + + function validateSubpackageDependency($dep, $required, $params) + { + return $this->validatePackageDependency($dep, $required, $params); + } + + /** + * @param array dependency information (2.0 format) + * @param boolean whether this is a required dependency + * @param array a list of downloaded packages to be installed, if any + * @param boolean if true, then deps on pear.php.net that fail will also check + * against pecl.php.net packages to accomodate extensions that have + * moved to pecl.php.net from pear.php.net + */ + function validatePackageDependency($dep, $required, $params, $depv1 = false) + { + if ($this->_state != PEAR_VALIDATE_INSTALLING && + $this->_state != PEAR_VALIDATE_DOWNLOADING) { + return true; + } + + if (isset($dep['providesextension'])) { + if ($this->extension_loaded($dep['providesextension'])) { + $save = $dep; + $subdep = $dep; + $subdep['name'] = $subdep['providesextension']; + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + $ret = $this->validateExtensionDependency($subdep, $required); + PEAR::popErrorHandling(); + if (!PEAR::isError($ret)) { + return true; + } + } + } + + if ($this->_state == PEAR_VALIDATE_INSTALLING) { + return $this->_validatePackageInstall($dep, $required, $depv1); + } + + if ($this->_state == PEAR_VALIDATE_DOWNLOADING) { + return $this->_validatePackageDownload($dep, $required, $params, $depv1); + } + } + + function _validatePackageDownload($dep, $required, $params, $depv1 = false) + { + $dep['package'] = $dep['name']; + if (isset($dep['uri'])) { + $dep['channel'] = '__uri'; + } + + $depname = $this->_registry->parsedPackageNameToString($dep, true); + $found = false; + foreach ($params as $param) { + if ($param->isEqual( + array('package' => $dep['name'], + 'channel' => $dep['channel']))) { + $found = true; + break; + } + + if ($depv1 && $dep['channel'] == 'pear.php.net') { + if ($param->isEqual( + array('package' => $dep['name'], + 'channel' => 'pecl.php.net'))) { + $found = true; + break; + } + } + } + + if (!$found && isset($dep['providesextension'])) { + foreach ($params as $param) { + if ($param->isExtension($dep['providesextension'])) { + $found = true; + break; + } + } + } + + if ($found) { + $version = $param->getVersion(); + $installed = false; + $downloaded = true; + } else { + if ($this->_registry->packageExists($dep['name'], $dep['channel'])) { + $installed = true; + $downloaded = false; + $version = $this->_registry->packageinfo($dep['name'], 'version', + $dep['channel']); + } else { + if ($dep['channel'] == 'pecl.php.net' && $this->_registry->packageExists($dep['name'], + 'pear.php.net')) { + $installed = true; + $downloaded = false; + $version = $this->_registry->packageinfo($dep['name'], 'version', + 'pear.php.net'); + } else { + $version = 'not installed or downloaded'; + $installed = false; + $downloaded = false; + } + } + } + + $extra = $this->_getExtraString($dep); + if (isset($dep['exclude']) && !is_array($dep['exclude'])) { + $dep['exclude'] = array($dep['exclude']); + } + + if (!isset($dep['min']) && !isset($dep['max']) && + !isset($dep['recommended']) && !isset($dep['exclude']) + ) { + if ($installed || $downloaded) { + $installed = $installed ? 'installed' : 'downloaded'; + if (isset($dep['conflicts'])) { + $rest = ''; + if ($version) { + $rest = ", $installed version is " . $version; + } + + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { + return $this->raiseError('%s conflicts with package "' . $depname . '"' . $extra . $rest); + } + + return $this->warning('warning: %s conflicts with package "' . $depname . '"' . $extra . $rest); + } + + return true; + } + + if (isset($dep['conflicts'])) { + return true; + } + + if ($required) { + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { + return $this->raiseError('%s requires package "' . $depname . '"' . $extra); + } + + return $this->warning('warning: %s requires package "' . $depname . '"' . $extra); + } + + return $this->warning('%s can optionally use package "' . $depname . '"' . $extra); + } + + if (!$installed && !$downloaded) { + if (isset($dep['conflicts'])) { + return true; + } + + if ($required) { + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { + return $this->raiseError('%s requires package "' . $depname . '"' . $extra); + } + + return $this->warning('warning: %s requires package "' . $depname . '"' . $extra); + } + + return $this->warning('%s can optionally use package "' . $depname . '"' . $extra); + } + + $fail = false; + if (isset($dep['min']) && version_compare($version, $dep['min'], '<')) { + $fail = true; + } + + if (isset($dep['max']) && version_compare($version, $dep['max'], '>')) { + $fail = true; + } + + if ($fail && !isset($dep['conflicts'])) { + $installed = $installed ? 'installed' : 'downloaded'; + $dep['package'] = $dep['name']; + $dep = $this->_registry->parsedPackageNameToString($dep, true); + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { + return $this->raiseError('%s requires package "' . $depname . '"' . + $extra . ", $installed version is " . $version); + } + + return $this->warning('warning: %s requires package "' . $depname . '"' . + $extra . ", $installed version is " . $version); + } elseif ((isset($dep['min']) || isset($dep['max'])) && !$fail && + isset($dep['conflicts']) && !isset($dep['exclude'])) { + $installed = $installed ? 'installed' : 'downloaded'; + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { + return $this->raiseError('%s conflicts with package "' . $depname . '"' . $extra . + ", $installed version is " . $version); + } + + return $this->warning('warning: %s conflicts with package "' . $depname . '"' . + $extra . ", $installed version is " . $version); + } + + if (isset($dep['exclude'])) { + $installed = $installed ? 'installed' : 'downloaded'; + foreach ($dep['exclude'] as $exclude) { + if (version_compare($version, $exclude, '==') && !isset($dep['conflicts'])) { + if (!isset($this->_options['nodeps']) && + !isset($this->_options['force']) + ) { + return $this->raiseError('%s is not compatible with ' . + $installed . ' package "' . + $depname . '" version ' . + $exclude); + } + + return $this->warning('warning: %s is not compatible with ' . + $installed . ' package "' . + $depname . '" version ' . + $exclude); + } elseif (version_compare($version, $exclude, '!=') && isset($dep['conflicts'])) { + $installed = $installed ? 'installed' : 'downloaded'; + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { + return $this->raiseError('%s conflicts with package "' . $depname . '"' . + $extra . ", $installed version is " . $version); + } + + return $this->warning('warning: %s conflicts with package "' . $depname . '"' . + $extra . ", $installed version is " . $version); + } + } + } + + if (isset($dep['recommended'])) { + $installed = $installed ? 'installed' : 'downloaded'; + if (version_compare($version, $dep['recommended'], '==')) { + return true; + } + + if (!$found && $installed) { + $param = $this->_registry->getPackage($dep['name'], $dep['channel']); + } + + if ($param) { + $found = false; + foreach ($params as $parent) { + if ($parent->isEqual($this->_currentPackage)) { + $found = true; + break; + } + } + + if ($found) { + if ($param->isCompatible($parent)) { + return true; + } + } else { // this is for validPackage() calls + $parent = $this->_registry->getPackage($this->_currentPackage['package'], + $this->_currentPackage['channel']); + if ($parent !== null && $param->isCompatible($parent)) { + return true; + } + } + } + + if (!isset($this->_options['nodeps']) && !isset($this->_options['force']) && + !isset($this->_options['loose']) + ) { + return $this->raiseError('%s dependency package "' . $depname . + '" ' . $installed . ' version ' . $version . + ' is not the recommended version ' . $dep['recommended'] . + ', but may be compatible, use --force to install'); + } + + return $this->warning('warning: %s dependency package "' . $depname . + '" ' . $installed . ' version ' . $version . + ' is not the recommended version ' . $dep['recommended']); + } + + return true; + } + + function _validatePackageInstall($dep, $required, $depv1 = false) + { + return $this->_validatePackageDownload($dep, $required, array(), $depv1); + } + + /** + * Verify that uninstalling packages passed in to command line is OK. + * + * @param PEAR_Installer $dl + * @return PEAR_Error|true + */ + function validatePackageUninstall(&$dl) + { + if (PEAR::isError($this->_dependencydb)) { + return $this->_dependencydb; + } + + $params = array(); + // construct an array of "downloaded" packages to fool the package dependency checker + // into using these to validate uninstalls of circular dependencies + $downloaded = &$dl->getUninstallPackages(); + foreach ($downloaded as $i => $pf) { + if (!class_exists('PEAR_Downloader_Package')) { + require_once 'PEAR/Downloader/Package.php'; + } + $dp = &new PEAR_Downloader_Package($dl); + $dp->setPackageFile($downloaded[$i]); + $params[$i] = &$dp; + } + + // check cache + $memyselfandI = strtolower($this->_currentPackage['channel']) . '/' . + strtolower($this->_currentPackage['package']); + if (isset($dl->___uninstall_package_cache)) { + $badpackages = $dl->___uninstall_package_cache; + if (isset($badpackages[$memyselfandI]['warnings'])) { + foreach ($badpackages[$memyselfandI]['warnings'] as $warning) { + $dl->log(0, $warning[0]); + } + } + + if (isset($badpackages[$memyselfandI]['errors'])) { + foreach ($badpackages[$memyselfandI]['errors'] as $error) { + if (is_array($error)) { + $dl->log(0, $error[0]); + } else { + $dl->log(0, $error->getMessage()); + } + } + + if (isset($this->_options['nodeps']) || isset($this->_options['force'])) { + return $this->warning( + 'warning: %s should not be uninstalled, other installed packages depend ' . + 'on this package'); + } + + return $this->raiseError( + '%s cannot be uninstalled, other installed packages depend on this package'); + } + + return true; + } + + // first, list the immediate parents of each package to be uninstalled + $perpackagelist = array(); + $allparents = array(); + foreach ($params as $i => $param) { + $a = array( + 'channel' => strtolower($param->getChannel()), + 'package' => strtolower($param->getPackage()) + ); + + $deps = $this->_dependencydb->getDependentPackages($a); + if ($deps) { + foreach ($deps as $d) { + $pardeps = $this->_dependencydb->getDependencies($d); + foreach ($pardeps as $dep) { + if (strtolower($dep['dep']['channel']) == $a['channel'] && + strtolower($dep['dep']['name']) == $a['package']) { + if (!isset($perpackagelist[$a['channel'] . '/' . $a['package']])) { + $perpackagelist[$a['channel'] . '/' . $a['package']] = array(); + } + $perpackagelist[$a['channel'] . '/' . $a['package']][] + = array($d['channel'] . '/' . $d['package'], $dep); + if (!isset($allparents[$d['channel'] . '/' . $d['package']])) { + $allparents[$d['channel'] . '/' . $d['package']] = array(); + } + if (!isset($allparents[$d['channel'] . '/' . $d['package']][$a['channel'] . '/' . $a['package']])) { + $allparents[$d['channel'] . '/' . $d['package']][$a['channel'] . '/' . $a['package']] = array(); + } + $allparents[$d['channel'] . '/' . $d['package']] + [$a['channel'] . '/' . $a['package']][] + = array($d, $dep); + } + } + } + } + } + + // next, remove any packages from the parents list that are not installed + $remove = array(); + foreach ($allparents as $parent => $d1) { + foreach ($d1 as $d) { + if ($this->_registry->packageExists($d[0][0]['package'], $d[0][0]['channel'])) { + continue; + } + $remove[$parent] = true; + } + } + + // next remove any packages from the parents list that are not passed in for + // uninstallation + foreach ($allparents as $parent => $d1) { + foreach ($d1 as $d) { + foreach ($params as $param) { + if (strtolower($param->getChannel()) == $d[0][0]['channel'] && + strtolower($param->getPackage()) == $d[0][0]['package']) { + // found it + continue 3; + } + } + $remove[$parent] = true; + } + } + + // remove all packages whose dependencies fail + // save which ones failed for error reporting + $badchildren = array(); + do { + $fail = false; + foreach ($remove as $package => $unused) { + if (!isset($allparents[$package])) { + continue; + } + + foreach ($allparents[$package] as $kid => $d1) { + foreach ($d1 as $depinfo) { + if ($depinfo[1]['type'] != 'optional') { + if (isset($badchildren[$kid])) { + continue; + } + $badchildren[$kid] = true; + $remove[$kid] = true; + $fail = true; + continue 2; + } + } + } + if ($fail) { + // start over, we removed some children + continue 2; + } + } + } while ($fail); + + // next, construct the list of packages that can't be uninstalled + $badpackages = array(); + $save = $this->_currentPackage; + foreach ($perpackagelist as $package => $packagedeps) { + foreach ($packagedeps as $parent) { + if (!isset($remove[$parent[0]])) { + continue; + } + + $packagename = $this->_registry->parsePackageName($parent[0]); + $packagename['channel'] = $this->_registry->channelAlias($packagename['channel']); + $pa = $this->_registry->getPackage($packagename['package'], $packagename['channel']); + $packagename['package'] = $pa->getPackage(); + $this->_currentPackage = $packagename; + // parent is not present in uninstall list, make sure we can actually + // uninstall it (parent dep is optional) + $parentname['channel'] = $this->_registry->channelAlias($parent[1]['dep']['channel']); + $pa = $this->_registry->getPackage($parent[1]['dep']['name'], $parent[1]['dep']['channel']); + $parentname['package'] = $pa->getPackage(); + $parent[1]['dep']['package'] = $parentname['package']; + $parent[1]['dep']['channel'] = $parentname['channel']; + if ($parent[1]['type'] == 'optional') { + $test = $this->_validatePackageUninstall($parent[1]['dep'], false, $dl); + if ($test !== true) { + $badpackages[$package]['warnings'][] = $test; + } + } else { + $test = $this->_validatePackageUninstall($parent[1]['dep'], true, $dl); + if ($test !== true) { + $badpackages[$package]['errors'][] = $test; + } + } + } + } + + $this->_currentPackage = $save; + $dl->___uninstall_package_cache = $badpackages; + if (isset($badpackages[$memyselfandI])) { + if (isset($badpackages[$memyselfandI]['warnings'])) { + foreach ($badpackages[$memyselfandI]['warnings'] as $warning) { + $dl->log(0, $warning[0]); + } + } + + if (isset($badpackages[$memyselfandI]['errors'])) { + foreach ($badpackages[$memyselfandI]['errors'] as $error) { + if (is_array($error)) { + $dl->log(0, $error[0]); + } else { + $dl->log(0, $error->getMessage()); + } + } + + if (isset($this->_options['nodeps']) || isset($this->_options['force'])) { + return $this->warning( + 'warning: %s should not be uninstalled, other installed packages depend ' . + 'on this package'); + } + + return $this->raiseError( + '%s cannot be uninstalled, other installed packages depend on this package'); + } + } + + return true; + } + + function _validatePackageUninstall($dep, $required, $dl) + { + $depname = $this->_registry->parsedPackageNameToString($dep, true); + $version = $this->_registry->packageinfo($dep['package'], 'version', $dep['channel']); + if (!$version) { + return true; + } + + $extra = $this->_getExtraString($dep); + if (isset($dep['exclude']) && !is_array($dep['exclude'])) { + $dep['exclude'] = array($dep['exclude']); + } + + if (isset($dep['conflicts'])) { + return true; // uninstall OK - these packages conflict (probably installed with --force) + } + + if (!isset($dep['min']) && !isset($dep['max'])) { + if (!$required) { + return $this->warning('"' . $depname . '" can be optionally used by ' . + 'installed package %s' . $extra); + } + + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { + return $this->raiseError('"' . $depname . '" is required by ' . + 'installed package %s' . $extra); + } + + return $this->warning('warning: "' . $depname . '" is required by ' . + 'installed package %s' . $extra); + } + + $fail = false; + if (isset($dep['min']) && version_compare($version, $dep['min'], '>=')) { + $fail = true; + } + + if (isset($dep['max']) && version_compare($version, $dep['max'], '<=')) { + $fail = true; + } + + // we re-use this variable, preserve the original value + $saverequired = $required; + if (!$required) { + return $this->warning($depname . $extra . ' can be optionally used by installed package' . + ' "%s"'); + } + + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { + return $this->raiseError($depname . $extra . ' is required by installed package' . + ' "%s"'); + } + + return $this->raiseError('warning: ' . $depname . $extra . + ' is required by installed package "%s"'); + } + + /** + * validate a downloaded package against installed packages + * + * As of PEAR 1.4.3, this will only validate + * + * @param array|PEAR_Downloader_Package|PEAR_PackageFile_v1|PEAR_PackageFile_v2 + * $pkg package identifier (either + * array('package' => blah, 'channel' => blah) or an array with + * index 'info' referencing an object) + * @param PEAR_Downloader $dl + * @param array $params full list of packages to install + * @return true|PEAR_Error + */ + function validatePackage($pkg, &$dl, $params = array()) + { + if (is_array($pkg) && isset($pkg['info'])) { + $deps = $this->_dependencydb->getDependentPackageDependencies($pkg['info']); + } else { + $deps = $this->_dependencydb->getDependentPackageDependencies($pkg); + } + + $fail = false; + if ($deps) { + if (!class_exists('PEAR_Downloader_Package')) { + require_once 'PEAR/Downloader/Package.php'; + } + + $dp = &new PEAR_Downloader_Package($dl); + if (is_object($pkg)) { + $dp->setPackageFile($pkg); + } else { + $dp->setDownloadURL($pkg); + } + + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + foreach ($deps as $channel => $info) { + foreach ($info as $package => $ds) { + foreach ($params as $packd) { + if (strtolower($packd->getPackage()) == strtolower($package) && + $packd->getChannel() == $channel) { + $dl->log(3, 'skipping installed package check of "' . + $this->_registry->parsedPackageNameToString( + array('channel' => $channel, 'package' => $package), + true) . + '", version "' . $packd->getVersion() . '" will be ' . + 'downloaded and installed'); + continue 2; // jump to next package + } + } + + foreach ($ds as $d) { + $checker = &new PEAR_Dependency2($this->_config, $this->_options, + array('channel' => $channel, 'package' => $package), $this->_state); + $dep = $d['dep']; + $required = $d['type'] == 'required'; + $ret = $checker->_validatePackageDownload($dep, $required, array(&$dp)); + if (is_array($ret)) { + $dl->log(0, $ret[0]); + } elseif (PEAR::isError($ret)) { + $dl->log(0, $ret->getMessage()); + $fail = true; + } + } + } + } + PEAR::popErrorHandling(); + } + + if ($fail) { + return $this->raiseError( + '%s cannot be installed, conflicts with installed packages'); + } + + return true; + } + + /** + * validate a package.xml 1.0 dependency + */ + function validateDependency1($dep, $params = array()) + { + if (!isset($dep['optional'])) { + $dep['optional'] = 'no'; + } + + list($newdep, $type) = $this->normalizeDep($dep); + if (!$newdep) { + return $this->raiseError("Invalid Dependency"); + } + + if (method_exists($this, "validate{$type}Dependency")) { + return $this->{"validate{$type}Dependency"}($newdep, $dep['optional'] == 'no', + $params, true); + } + } + + /** + * Convert a 1.0 dep into a 2.0 dep + */ + function normalizeDep($dep) + { + $types = array( + 'pkg' => 'Package', + 'ext' => 'Extension', + 'os' => 'Os', + 'php' => 'Php' + ); + + if (!isset($types[$dep['type']])) { + return array(false, false); + } + + $type = $types[$dep['type']]; + + $newdep = array(); + switch ($type) { + case 'Package' : + $newdep['channel'] = 'pear.php.net'; + case 'Extension' : + case 'Os' : + $newdep['name'] = $dep['name']; + break; + } + + $dep['rel'] = PEAR_Dependency2::signOperator($dep['rel']); + switch ($dep['rel']) { + case 'has' : + return array($newdep, $type); + break; + case 'not' : + $newdep['conflicts'] = true; + break; + case '>=' : + case '>' : + $newdep['min'] = $dep['version']; + if ($dep['rel'] == '>') { + $newdep['exclude'] = $dep['version']; + } + break; + case '<=' : + case '<' : + $newdep['max'] = $dep['version']; + if ($dep['rel'] == '<') { + $newdep['exclude'] = $dep['version']; + } + break; + case 'ne' : + case '!=' : + $newdep['min'] = '0'; + $newdep['max'] = '100000'; + $newdep['exclude'] = $dep['version']; + break; + case '==' : + $newdep['min'] = $dep['version']; + $newdep['max'] = $dep['version']; + break; + } + if ($type == 'Php') { + if (!isset($newdep['min'])) { + $newdep['min'] = '4.4.0'; + } + + if (!isset($newdep['max'])) { + $newdep['max'] = '6.0.0'; + } + } + return array($newdep, $type); + } + + /** + * Converts text comparing operators to them sign equivalents + * + * Example: 'ge' to '>=' + * + * @access public + * @param string Operator + * @return string Sign equivalent + */ + function signOperator($operator) + { + switch($operator) { + case 'lt': return '<'; + case 'le': return '<='; + case 'gt': return '>'; + case 'ge': return '>='; + case 'eq': return '=='; + case 'ne': return '!='; + default: + return $operator; + } + } + + function raiseError($msg) + { + if (isset($this->_options['ignore-errors'])) { + return $this->warning($msg); + } + + return PEAR::raiseError(sprintf($msg, $this->_registry->parsedPackageNameToString( + $this->_currentPackage, true))); + } + + function warning($msg) + { + return array(sprintf($msg, $this->_registry->parsedPackageNameToString( + $this->_currentPackage, true))); + } +} \ No newline at end of file diff --git a/includes/pear/PEAR/DependencyDB.php b/includes/pear/PEAR/DependencyDB.php new file mode 100644 index 0000000..e3ad0ec --- /dev/null +++ b/includes/pear/PEAR/DependencyDB.php @@ -0,0 +1,757 @@ + + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a1 + */ + +/** + * Needed for error handling + */ +require_once 'PEAR.php'; +require_once 'PEAR/Config.php'; + +$GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'] = array(); +/** + * Track dependency relationships between installed packages + * @category pear + * @package PEAR + * @author Greg Beaver + * @author Tomas V.V.Cox + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a1 + */ +class PEAR_DependencyDB +{ + // {{{ properties + + /** + * This is initialized by {@link setConfig()} + * @var PEAR_Config + */ + private $_config; + /** + * This is initialized by {@link setConfig()} + * @var PEAR_Registry + */ + private $_registry; + /** + * Filename of the dependency DB (usually .depdb) + * @var string + */ + private $_depdb = false; + /** + * File name of the lockfile (usually .depdblock) + * @var string + */ + private $_lockfile = false; + /** + * Open file resource for locking the lockfile + * @var resource|false + */ + private $_lockFp = false; + /** + * API version of this class, used to validate a file on-disk + * @var string + */ + private $_version = '1.0'; + /** + * Cached dependency database file + * @var array|null + */ + private $_cache; + + // }}} + // {{{ & singleton() + + /** + * Get a raw dependency database. Calls setConfig() and assertDepsDB() + * @param PEAR_Config + * @param string|false full path to the dependency database, or false to use default + * @return PEAR_DependencyDB|PEAR_Error + */ + public static function &singleton(&$config, $depdb = false) + { + $phpdir = $config->get('php_dir', null, 'pear.php.net'); + if (!isset($GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'][$phpdir])) { + $a = new PEAR_DependencyDB; + $GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'][$phpdir] = &$a; + $a->setConfig($config, $depdb); + $e = $a->assertDepsDB(); + if (PEAR::isError($e)) { + return $e; + } + } + + return $GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'][$phpdir]; + } + + /** + * Set up the registry/location of dependency DB + * @param PEAR_Config|false + * @param string|false full path to the dependency database, or false to use default + */ + public function setConfig(&$config, $depdb = false) + { + if (!$config) { + $this->_config = &PEAR_Config::singleton(); + } else { + $this->_config = &$config; + } + + $this->_registry = &$this->_config->getRegistry(); + if (!$depdb) { + $this->_depdb = $this->_config->get('php_dir', null, 'pear.php.net') . + DIRECTORY_SEPARATOR . '.depdb'; + } else { + $this->_depdb = $depdb; + } + + $this->_lockfile = dirname($this->_depdb) . DIRECTORY_SEPARATOR . '.depdblock'; + } + // }}} + + public function hasWriteAccess() + { + if (!file_exists($this->_depdb)) { + $dir = $this->_depdb; + while ($dir && $dir != '.') { + $dir = dirname($dir); // cd .. + if ($dir != '.' && file_exists($dir)) { + if (is_writeable($dir)) { + return true; + } + + return false; + } + } + + return false; + } + + return is_writeable($this->_depdb); + } + + // {{{ assertDepsDB() + + /** + * Create the dependency database, if it doesn't exist. Error if the database is + * newer than the code reading it. + * @return void|PEAR_Error + */ + public function assertDepsDB() + { + if (!is_file($this->_depdb)) { + $this->rebuildDB(); + return; + } + + $depdb = $this->_getDepDB(); + // Datatype format has been changed, rebuild the Deps DB + if ($depdb['_version'] < $this->_version) { + $this->rebuildDB(); + } + + if ($depdb['_version']{0} > $this->_version{0}) { + return PEAR::raiseError('Dependency database is version ' . + $depdb['_version'] . ', and we are version ' . + $this->_version . ', cannot continue'); + } + } + + /** + * Get a list of installed packages that depend on this package + * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array + * @return array|false + */ + public function getDependentPackages(&$pkg) + { + $data = $this->_getDepDB(); + if (is_object($pkg)) { + $channel = strtolower($pkg->getChannel()); + $package = strtolower($pkg->getPackage()); + } else { + $channel = strtolower($pkg['channel']); + $package = strtolower($pkg['package']); + } + + if (isset($data['packages'][$channel][$package])) { + return $data['packages'][$channel][$package]; + } + + return false; + } + + /** + * Get a list of the actual dependencies of installed packages that depend on + * a package. + * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array + * @return array|false + */ + public function getDependentPackageDependencies(&$pkg) + { + $data = $this->_getDepDB(); + if (is_object($pkg)) { + $channel = strtolower($pkg->getChannel()); + $package = strtolower($pkg->getPackage()); + } else { + $channel = strtolower($pkg['channel']); + $package = strtolower($pkg['package']); + } + + $depend = $this->getDependentPackages($pkg); + if (!$depend) { + return false; + } + + $dependencies = array(); + foreach ($depend as $info) { + $temp = $this->getDependencies($info); + foreach ($temp as $dep) { + if ( + isset($dep['dep'], $dep['dep']['channel'], $dep['dep']['name']) && + strtolower($dep['dep']['channel']) == $channel && + strtolower($dep['dep']['name']) == $package + ) { + if (!isset($dependencies[$info['channel']])) { + $dependencies[$info['channel']] = array(); + } + + if (!isset($dependencies[$info['channel']][$info['package']])) { + $dependencies[$info['channel']][$info['package']] = array(); + } + $dependencies[$info['channel']][$info['package']][] = $dep; + } + } + } + + return $dependencies; + } + + /** + * Get a list of dependencies of this installed package + * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array + * @return array|false + */ + public function getDependencies(&$pkg) + { + if (is_object($pkg)) { + $channel = strtolower($pkg->getChannel()); + $package = strtolower($pkg->getPackage()); + } else { + $channel = strtolower($pkg['channel']); + $package = strtolower($pkg['package']); + } + + $data = $this->_getDepDB(); + if (isset($data['dependencies'][$channel][$package])) { + return $data['dependencies'][$channel][$package]; + } + + return false; + } + + /** + * Determine whether $parent depends on $child, near or deep + * @param array|PEAR_PackageFile_v2|PEAR_PackageFile_v2 + * @param array|PEAR_PackageFile_v2|PEAR_PackageFile_v2 + */ + public function dependsOn($parent, $child) + { + $c = array(); + $this->_getDepDB(); + return $this->_dependsOn($parent, $child, $c); + } + + private function _dependsOn($parent, $child, &$checked) + { + if (is_object($parent)) { + $channel = strtolower($parent->getChannel()); + $package = strtolower($parent->getPackage()); + } else { + $channel = strtolower($parent['channel']); + $package = strtolower($parent['package']); + } + + if (is_object($child)) { + $depchannel = strtolower($child->getChannel()); + $deppackage = strtolower($child->getPackage()); + } else { + $depchannel = strtolower($child['channel']); + $deppackage = strtolower($child['package']); + } + + if (isset($checked[$channel][$package][$depchannel][$deppackage])) { + return false; // avoid endless recursion + } + + $checked[$channel][$package][$depchannel][$deppackage] = true; + if (!isset($this->_cache['dependencies'][$channel][$package])) { + return false; + } + + foreach ($this->_cache['dependencies'][$channel][$package] as $info) { + if (isset($info['dep']['uri'])) { + if (is_object($child)) { + if ($info['dep']['uri'] == $child->getURI()) { + return true; + } + } elseif (isset($child['uri'])) { + if ($info['dep']['uri'] == $child['uri']) { + return true; + } + } + return false; + } + + if (strtolower($info['dep']['channel']) == $depchannel && + strtolower($info['dep']['name']) == $deppackage) { + return true; + } + } + + foreach ($this->_cache['dependencies'][$channel][$package] as $info) { + if (isset($info['dep']['uri'])) { + if ($this->_dependsOn(array( + 'uri' => $info['dep']['uri'], + 'package' => $info['dep']['name']), $child, $checked)) { + return true; + } + } else { + if ($this->_dependsOn(array( + 'channel' => $info['dep']['channel'], + 'package' => $info['dep']['name']), $child, $checked)) { + return true; + } + } + } + + return false; + } + + /** + * Register dependencies of a package that is being installed or upgraded + * @param PEAR_PackageFile_v2|PEAR_PackageFile_v2 + */ + public function installPackage(&$package) + { + $data = $this->_getDepDB(); + unset($this->_cache); + $this->_setPackageDeps($data, $package); + $this->_writeDepDB($data); + } + + /** + * Remove dependencies of a package that is being uninstalled, or upgraded. + * + * Upgraded packages first uninstall, then install + * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array If an array, then it must have + * indices 'channel' and 'package' + */ + public function uninstallPackage(&$pkg) + { + $data = $this->_getDepDB(); + unset($this->_cache); + if (is_object($pkg)) { + $channel = strtolower($pkg->getChannel()); + $package = strtolower($pkg->getPackage()); + } else { + $channel = strtolower($pkg['channel']); + $package = strtolower($pkg['package']); + } + + if (!isset($data['dependencies'][$channel][$package])) { + return true; + } + + foreach ($data['dependencies'][$channel][$package] as $dep) { + $found = false; + $depchannel = isset($dep['dep']['uri']) ? '__uri' : strtolower($dep['dep']['channel']); + $depname = strtolower($dep['dep']['name']); + if (isset($data['packages'][$depchannel][$depname])) { + foreach ($data['packages'][$depchannel][$depname] as $i => $info) { + if ($info['channel'] == $channel && $info['package'] == $package) { + $found = true; + break; + } + } + } + + if ($found) { + unset($data['packages'][$depchannel][$depname][$i]); + if (!count($data['packages'][$depchannel][$depname])) { + unset($data['packages'][$depchannel][$depname]); + if (!count($data['packages'][$depchannel])) { + unset($data['packages'][$depchannel]); + } + } else { + $data['packages'][$depchannel][$depname] = + array_values($data['packages'][$depchannel][$depname]); + } + } + } + + unset($data['dependencies'][$channel][$package]); + if (!count($data['dependencies'][$channel])) { + unset($data['dependencies'][$channel]); + } + + if (!count($data['dependencies'])) { + unset($data['dependencies']); + } + + if (!count($data['packages'])) { + unset($data['packages']); + } + + $this->_writeDepDB($data); + } + + /** + * Rebuild the dependency DB by reading registry entries. + * @return true|PEAR_Error + */ + public function rebuildDB() + { + $depdb = array('_version' => $this->_version); + if (!$this->hasWriteAccess()) { + // allow startup for read-only with older Registry + return $depdb; + } + + $packages = $this->_registry->listAllPackages(); + if (PEAR::isError($packages)) { + return $packages; + } + + foreach ($packages as $channel => $ps) { + foreach ($ps as $package) { + $package = $this->_registry->getPackage($package, $channel); + if (PEAR::isError($package)) { + return $package; + } + $this->_setPackageDeps($depdb, $package); + } + } + + $error = $this->_writeDepDB($depdb); + if (PEAR::isError($error)) { + return $error; + } + + $this->_cache = $depdb; + return true; + } + + /** + * Register usage of the dependency DB to prevent race conditions + * @param int one of the LOCK_* constants + * @return true|PEAR_Error + */ + private function _lock($mode = LOCK_EX) + { + if (stristr(php_uname(), 'Windows 9')) { + return true; + } + + if ($mode != LOCK_UN && is_resource($this->_lockFp)) { + // XXX does not check type of lock (LOCK_SH/LOCK_EX) + return true; + } + + $open_mode = 'w'; + // XXX People reported problems with LOCK_SH and 'w' + if ($mode === LOCK_SH) { + if (!file_exists($this->_lockfile)) { + touch($this->_lockfile); + } elseif (!is_file($this->_lockfile)) { + return PEAR::raiseError('could not create Dependency lock file, ' . + 'it exists and is not a regular file'); + } + $open_mode = 'r'; + } + + if (!is_resource($this->_lockFp)) { + $this->_lockFp = @fopen($this->_lockfile, $open_mode); + } + + if (!is_resource($this->_lockFp)) { + return PEAR::raiseError("could not create Dependency lock file" . + (isset($php_errormsg) ? ": " . $php_errormsg : "")); + } + + if (!(int)flock($this->_lockFp, $mode)) { + switch ($mode) { + case LOCK_SH: $str = 'shared'; break; + case LOCK_EX: $str = 'exclusive'; break; + case LOCK_UN: $str = 'unlock'; break; + default: $str = 'unknown'; break; + } + + return PEAR::raiseError("could not acquire $str lock ($this->_lockfile)"); + } + + return true; + } + + /** + * Release usage of dependency DB + * @return true|PEAR_Error + */ + private function _unlock() + { + $ret = $this->_lock(LOCK_UN); + if (is_resource($this->_lockFp)) { + fclose($this->_lockFp); + } + $this->_lockFp = null; + return $ret; + } + + /** + * Load the dependency database from disk, or return the cache + * @return array|PEAR_Error + */ + private function _getDepDB() + { + if (!$this->hasWriteAccess()) { + return array('_version' => $this->_version); + } + + if (isset($this->_cache)) { + return $this->_cache; + } + + if (!$fp = fopen($this->_depdb, 'r')) { + $err = PEAR::raiseError("Could not open dependencies file `".$this->_depdb."'"); + return $err; + } + + $rt = get_magic_quotes_runtime(); + set_magic_quotes_runtime(0); + clearstatcache(); + fclose($fp); + $data = unserialize(file_get_contents($this->_depdb)); + set_magic_quotes_runtime($rt); + $this->_cache = $data; + return $data; + } + + /** + * Write out the dependency database to disk + * @param array the database + * @return true|PEAR_Error + */ + private function _writeDepDB(&$deps) + { + if (PEAR::isError($e = $this->_lock(LOCK_EX))) { + return $e; + } + + if (!$fp = fopen($this->_depdb, 'wb')) { + $this->_unlock(); + return PEAR::raiseError("Could not open dependencies file `".$this->_depdb."' for writing"); + } + + $rt = get_magic_quotes_runtime(); + set_magic_quotes_runtime(0); + fwrite($fp, serialize($deps)); + set_magic_quotes_runtime($rt); + fclose($fp); + $this->_unlock(); + $this->_cache = $deps; + return true; + } + + /** + * Register all dependencies from a package in the dependencies database, in essence + * "installing" the package's dependency information + * @param array the database + * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 + */ + private function _setPackageDeps(&$data, &$pkg) + { + $pkg->setConfig($this->_config); + if ($pkg->getPackagexmlVersion() == '1.0') { + $gen = &$pkg->getDefaultGenerator(); + $deps = $gen->dependenciesToV2(); + } else { + $deps = $pkg->getDeps(true); + } + + if (!$deps) { + return; + } + + if (!is_array($data)) { + $data = array(); + } + + if (!isset($data['dependencies'])) { + $data['dependencies'] = array(); + } + + $channel = strtolower($pkg->getChannel()); + $package = strtolower($pkg->getPackage()); + + if (!isset($data['dependencies'][$channel])) { + $data['dependencies'][$channel] = array(); + } + + $data['dependencies'][$channel][$package] = array(); + if (isset($deps['required']['package'])) { + if (!isset($deps['required']['package'][0])) { + $deps['required']['package'] = array($deps['required']['package']); + } + + foreach ($deps['required']['package'] as $dep) { + $this->_registerDep($data, $pkg, $dep, 'required'); + } + } + + if (isset($deps['optional']['package'])) { + if (!isset($deps['optional']['package'][0])) { + $deps['optional']['package'] = array($deps['optional']['package']); + } + + foreach ($deps['optional']['package'] as $dep) { + $this->_registerDep($data, $pkg, $dep, 'optional'); + } + } + + if (isset($deps['required']['subpackage'])) { + if (!isset($deps['required']['subpackage'][0])) { + $deps['required']['subpackage'] = array($deps['required']['subpackage']); + } + + foreach ($deps['required']['subpackage'] as $dep) { + $this->_registerDep($data, $pkg, $dep, 'required'); + } + } + + if (isset($deps['optional']['subpackage'])) { + if (!isset($deps['optional']['subpackage'][0])) { + $deps['optional']['subpackage'] = array($deps['optional']['subpackage']); + } + + foreach ($deps['optional']['subpackage'] as $dep) { + $this->_registerDep($data, $pkg, $dep, 'optional'); + } + } + + if (isset($deps['group'])) { + if (!isset($deps['group'][0])) { + $deps['group'] = array($deps['group']); + } + + foreach ($deps['group'] as $group) { + if (isset($group['package'])) { + if (!isset($group['package'][0])) { + $group['package'] = array($group['package']); + } + + foreach ($group['package'] as $dep) { + $this->_registerDep($data, $pkg, $dep, 'optional', + $group['attribs']['name']); + } + } + + if (isset($group['subpackage'])) { + if (!isset($group['subpackage'][0])) { + $group['subpackage'] = array($group['subpackage']); + } + + foreach ($group['subpackage'] as $dep) { + $this->_registerDep($data, $pkg, $dep, 'optional', + $group['attribs']['name']); + } + } + } + } + + if ($data['dependencies'][$channel][$package] == array()) { + unset($data['dependencies'][$channel][$package]); + if (!count($data['dependencies'][$channel])) { + unset($data['dependencies'][$channel]); + } + } + } + + /** + * @param array the database + * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 + * @param array the specific dependency + * @param required|optional whether this is a required or an optional dep + * @param string|false dependency group this dependency is from, or false for ordinary dep + */ + private function _registerDep(&$data, &$pkg, $dep, $type, $group = false) + { + $info = array( + 'dep' => $dep, + 'type' => $type, + 'group' => $group + ); + + $dep = array_map('strtolower', $dep); + $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri'; + if (!isset($data['dependencies'])) { + $data['dependencies'] = array(); + } + + $channel = strtolower($pkg->getChannel()); + $package = strtolower($pkg->getPackage()); + + if (!isset($data['dependencies'][$channel])) { + $data['dependencies'][$channel] = array(); + } + + if (!isset($data['dependencies'][$channel][$package])) { + $data['dependencies'][$channel][$package] = array(); + } + + $data['dependencies'][$channel][$package][] = $info; + if (isset($data['packages'][$depchannel][$dep['name']])) { + $found = false; + foreach ($data['packages'][$depchannel][$dep['name']] as $i => $p) { + if ($p['channel'] == $channel && $p['package'] == $package) { + $found = true; + break; + } + } + } else { + if (!isset($data['packages'])) { + $data['packages'] = array(); + } + + if (!isset($data['packages'][$depchannel])) { + $data['packages'][$depchannel] = array(); + } + + if (!isset($data['packages'][$depchannel][$dep['name']])) { + $data['packages'][$depchannel][$dep['name']] = array(); + } + + $found = false; + } + + if (!$found) { + $data['packages'][$depchannel][$dep['name']][] = array( + 'channel' => $channel, + 'package' => $package + ); + } + } +} diff --git a/includes/pear/PEAR/Downloader.php b/includes/pear/PEAR/Downloader.php new file mode 100644 index 0000000..b42a210 --- /dev/null +++ b/includes/pear/PEAR/Downloader.php @@ -0,0 +1,1766 @@ + + * @author Stig Bakken + * @author Tomas V. V. Cox + * @author Martin Jansen + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.3.0 + */ + +/** + * Needed for constants, extending + */ +require_once 'PEAR/Common.php'; + +define('PEAR_INSTALLER_OK', 1); +define('PEAR_INSTALLER_FAILED', 0); +define('PEAR_INSTALLER_SKIPPED', -1); +define('PEAR_INSTALLER_ERROR_NO_PREF_STATE', 2); + +/** + * Administration class used to download anything from the internet (PEAR Packages, + * static URLs, xml files) + * + * @category pear + * @package PEAR + * @author Greg Beaver + * @author Stig Bakken + * @author Tomas V. V. Cox + * @author Martin Jansen + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.3.0 + */ +class PEAR_Downloader extends PEAR_Common +{ + /** + * @var PEAR_Registry + * @access private + */ + var $_registry; + + /** + * Preferred Installation State (snapshot, devel, alpha, beta, stable) + * @var string|null + * @access private + */ + var $_preferredState; + + /** + * Options from command-line passed to Install. + * + * Recognized options:
    + * - onlyreqdeps : install all required dependencies as well + * - alldeps : install all dependencies, including optional + * - installroot : base relative path to install files in + * - force : force a download even if warnings would prevent it + * - nocompress : download uncompressed tarballs + * @see PEAR_Command_Install + * @access private + * @var array + */ + var $_options; + + /** + * Downloaded Packages after a call to download(). + * + * Format of each entry: + * + * + * array('pkg' => 'package_name', 'file' => '/path/to/local/file', + * 'info' => array() // parsed package.xml + * ); + * + * @access private + * @var array + */ + var $_downloadedPackages = array(); + + /** + * Packages slated for download. + * + * This is used to prevent downloading a package more than once should it be a dependency + * for two packages to be installed. + * Format of each entry: + * + *
    +     * array('package_name1' => parsed package.xml, 'package_name2' => parsed package.xml,
    +     * );
    +     * 
    + * @access private + * @var array + */ + var $_toDownload = array(); + + /** + * Array of every package installed, with names lower-cased. + * + * Format: + * + * array('package1' => 0, 'package2' => 1, ); + * + * @var array + */ + var $_installed = array(); + + /** + * @var array + * @access private + */ + var $_errorStack = array(); + + /** + * @var boolean + * @access private + */ + var $_internalDownload = false; + + /** + * Temporary variable used in sorting packages by dependency in {@link sortPkgDeps()} + * @var array + * @access private + */ + var $_packageSortTree; + + /** + * Temporary directory, or configuration value where downloads will occur + * @var string + */ + var $_downloadDir; + + /** + * @param PEAR_Frontend_* + * @param array + * @param PEAR_Config + */ + function PEAR_Downloader(&$ui, $options, &$config) + { + parent::PEAR_Common(); + $this->_options = $options; + $this->config = &$config; + $this->_preferredState = $this->config->get('preferred_state'); + $this->ui = &$ui; + if (!$this->_preferredState) { + // don't inadvertantly use a non-set preferred_state + $this->_preferredState = null; + } + + if (isset($this->_options['installroot'])) { + $this->config->setInstallRoot($this->_options['installroot']); + } + $this->_registry = &$config->getRegistry(); + + if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) { + $this->_installed = $this->_registry->listAllPackages(); + foreach ($this->_installed as $key => $unused) { + if (!count($unused)) { + continue; + } + $strtolower = create_function('$a','return strtolower($a);'); + array_walk($this->_installed[$key], $strtolower); + } + } + } + + /** + * Attempt to discover a channel's remote capabilities from + * its server name + * @param string + * @return boolean + */ + function discover($channel) + { + $this->log(1, 'Attempting to discover channel "' . $channel . '"...'); + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + $callback = $this->ui ? array(&$this, '_downloadCallback') : null; + if (!class_exists('System')) { + require_once 'System.php'; + } + + $tmpdir = $this->config->get('temp_dir'); + $tmp = System::mktemp('-d -t "' . $tmpdir . '"'); + $a = $this->downloadHttp('http://' . $channel . '/channel.xml', $this->ui, $tmp, $callback, false); + PEAR::popErrorHandling(); + if (PEAR::isError($a)) { + // Attempt to fallback to https automatically. + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + $this->log(1, 'Attempting fallback to https instead of http on channel "' . $channel . '"...'); + $a = $this->downloadHttp('https://' . $channel . '/channel.xml', $this->ui, $tmp, $callback, false); + PEAR::popErrorHandling(); + if (PEAR::isError($a)) { + return false; + } + } + + list($a, $lastmodified) = $a; + if (!class_exists('PEAR_ChannelFile')) { + require_once 'PEAR/ChannelFile.php'; + } + + $b = new PEAR_ChannelFile; + if ($b->fromXmlFile($a)) { + unlink($a); + if ($this->config->get('auto_discover')) { + $this->_registry->addChannel($b, $lastmodified); + $alias = $b->getName(); + if ($b->getName() == $this->_registry->channelName($b->getAlias())) { + $alias = $b->getAlias(); + } + + $this->log(1, 'Auto-discovered channel "' . $channel . + '", alias "' . $alias . '", adding to registry'); + } + + return true; + } + + unlink($a); + return false; + } + + /** + * For simpler unit-testing + * @param PEAR_Downloader + * @return PEAR_Downloader_Package + */ + function &newDownloaderPackage(&$t) + { + if (!class_exists('PEAR_Downloader_Package')) { + require_once 'PEAR/Downloader/Package.php'; + } + $a = &new PEAR_Downloader_Package($t); + return $a; + } + + /** + * For simpler unit-testing + * @param PEAR_Config + * @param array + * @param array + * @param int + */ + function &getDependency2Object(&$c, $i, $p, $s) + { + if (!class_exists('PEAR_Dependency2')) { + require_once 'PEAR/Dependency2.php'; + } + $z = &new PEAR_Dependency2($c, $i, $p, $s); + return $z; + } + + function &download($params) + { + if (!count($params)) { + $a = array(); + return $a; + } + + if (!isset($this->_registry)) { + $this->_registry = &$this->config->getRegistry(); + } + + $channelschecked = array(); + // convert all parameters into PEAR_Downloader_Package objects + foreach ($params as $i => $param) { + $params[$i] = &$this->newDownloaderPackage($this); + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $err = $params[$i]->initialize($param); + PEAR::staticPopErrorHandling(); + if (!$err) { + // skip parameters that were missed by preferred_state + continue; + } + + if (PEAR::isError($err)) { + if (!isset($this->_options['soft']) && $err->getMessage() !== '') { + $this->log(0, $err->getMessage()); + } + + $params[$i] = false; + if (is_object($param)) { + $param = $param->getChannel() . '/' . $param->getPackage(); + } + + if (!isset($this->_options['soft'])) { + $this->log(2, 'Package "' . $param . '" is not valid'); + } + + // Message logged above in a specific verbose mode, passing null to not show up on CLI + $this->pushError(null, PEAR_INSTALLER_SKIPPED); + } else { + do { + if ($params[$i] && $params[$i]->getType() == 'local') { + // bug #7090 skip channel.xml check for local packages + break; + } + + if ($params[$i] && !isset($channelschecked[$params[$i]->getChannel()]) && + !isset($this->_options['offline']) + ) { + $channelschecked[$params[$i]->getChannel()] = true; + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + if (!class_exists('System')) { + require_once 'System.php'; + } + + $curchannel = &$this->_registry->getChannel($params[$i]->getChannel()); + if (PEAR::isError($curchannel)) { + PEAR::staticPopErrorHandling(); + return $this->raiseError($curchannel); + } + + if (PEAR::isError($dir = $this->getDownloadDir())) { + PEAR::staticPopErrorHandling(); + break; + } + + $mirror = $this->config->get('preferred_mirror', null, $params[$i]->getChannel()); + $url = 'http://' . $mirror . '/channel.xml'; + $a = $this->downloadHttp($url, $this->ui, $dir, null, $curchannel->lastModified()); + + PEAR::staticPopErrorHandling(); + if (PEAR::isError($a) || !$a) { + // Attempt fallback to https automatically + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + $a = $this->downloadHttp('https://' . $mirror . + '/channel.xml', $this->ui, $dir, null, $curchannel->lastModified()); + + PEAR::staticPopErrorHandling(); + if (PEAR::isError($a) || !$a) { + break; + } + } + $this->log(0, 'WARNING: channel "' . $params[$i]->getChannel() . '" has ' . + 'updated its protocols, use "' . PEAR_RUNTYPE . ' channel-update ' . $params[$i]->getChannel() . + '" to update'); + } + } while (false); + + if ($params[$i] && !isset($this->_options['downloadonly'])) { + if (isset($this->_options['packagingroot'])) { + $checkdir = $this->_prependPath( + $this->config->get('php_dir', null, $params[$i]->getChannel()), + $this->_options['packagingroot']); + } else { + $checkdir = $this->config->get('php_dir', + null, $params[$i]->getChannel()); + } + + while ($checkdir && $checkdir != '/' && !file_exists($checkdir)) { + $checkdir = dirname($checkdir); + } + + if ($checkdir == '.') { + $checkdir = '/'; + } + + if (!is_writeable($checkdir)) { + return PEAR::raiseError('Cannot install, php_dir for channel "' . + $params[$i]->getChannel() . '" is not writeable by the current user'); + } + } + } + } + + unset($channelschecked); + PEAR_Downloader_Package::removeDuplicates($params); + if (!count($params)) { + $a = array(); + return $a; + } + + if (!isset($this->_options['nodeps']) && !isset($this->_options['offline'])) { + $reverify = true; + while ($reverify) { + $reverify = false; + foreach ($params as $i => $param) { + //PHP Bug 40768 / PEAR Bug #10944 + //Nested foreaches fail in PHP 5.2.1 + key($params); + $ret = $params[$i]->detectDependencies($params); + if (PEAR::isError($ret)) { + $reverify = true; + $params[$i] = false; + PEAR_Downloader_Package::removeDuplicates($params); + if (!isset($this->_options['soft'])) { + $this->log(0, $ret->getMessage()); + } + continue 2; + } + } + } + } + + if (isset($this->_options['offline'])) { + $this->log(3, 'Skipping dependency download check, --offline specified'); + } + + if (!count($params)) { + $a = array(); + return $a; + } + + while (PEAR_Downloader_Package::mergeDependencies($params)); + PEAR_Downloader_Package::removeDuplicates($params, true); + $errorparams = array(); + if (PEAR_Downloader_Package::detectStupidDuplicates($params, $errorparams)) { + if (count($errorparams)) { + foreach ($errorparams as $param) { + $name = $this->_registry->parsedPackageNameToString($param->getParsedPackage()); + $this->pushError('Duplicate package ' . $name . ' found', PEAR_INSTALLER_FAILED); + } + $a = array(); + return $a; + } + } + + PEAR_Downloader_Package::removeInstalled($params); + if (!count($params)) { + $this->pushError('No valid packages found', PEAR_INSTALLER_FAILED); + $a = array(); + return $a; + } + + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + $err = $this->analyzeDependencies($params); + PEAR::popErrorHandling(); + if (!count($params)) { + $this->pushError('No valid packages found', PEAR_INSTALLER_FAILED); + $a = array(); + return $a; + } + + $ret = array(); + $newparams = array(); + if (isset($this->_options['pretend'])) { + return $params; + } + + $somefailed = false; + foreach ($params as $i => $package) { + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $pf = &$params[$i]->download(); + PEAR::staticPopErrorHandling(); + if (PEAR::isError($pf)) { + if (!isset($this->_options['soft'])) { + $this->log(1, $pf->getMessage()); + $this->log(0, 'Error: cannot download "' . + $this->_registry->parsedPackageNameToString($package->getParsedPackage(), + true) . + '"'); + } + $somefailed = true; + continue; + } + + $newparams[] = &$params[$i]; + $ret[] = array( + 'file' => $pf->getArchiveFile(), + 'info' => &$pf, + 'pkg' => $pf->getPackage() + ); + } + + if ($somefailed) { + // remove params that did not download successfully + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + $err = $this->analyzeDependencies($newparams, true); + PEAR::popErrorHandling(); + if (!count($newparams)) { + $this->pushError('Download failed', PEAR_INSTALLER_FAILED); + $a = array(); + return $a; + } + } + + $this->_downloadedPackages = $ret; + return $newparams; + } + + /** + * @param array all packages to be installed + */ + function analyzeDependencies(&$params, $force = false) + { + if (isset($this->_options['downloadonly'])) { + return; + } + + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $redo = true; + $reset = $hasfailed = $failed = false; + while ($redo) { + $redo = false; + foreach ($params as $i => $param) { + $deps = $param->getDeps(); + if (!$deps) { + $depchecker = &$this->getDependency2Object($this->config, $this->getOptions(), + $param->getParsedPackage(), PEAR_VALIDATE_DOWNLOADING); + $send = $param->getPackageFile(); + + $installcheck = $depchecker->validatePackage($send, $this, $params); + if (PEAR::isError($installcheck)) { + if (!isset($this->_options['soft'])) { + $this->log(0, $installcheck->getMessage()); + } + $hasfailed = true; + $params[$i] = false; + $reset = true; + $redo = true; + $failed = false; + PEAR_Downloader_Package::removeDuplicates($params); + continue 2; + } + continue; + } + + if (!$reset && $param->alreadyValidated() && !$force) { + continue; + } + + if (count($deps)) { + $depchecker = &$this->getDependency2Object($this->config, $this->getOptions(), + $param->getParsedPackage(), PEAR_VALIDATE_DOWNLOADING); + $send = $param->getPackageFile(); + if ($send === null) { + $send = $param->getDownloadURL(); + } + + $installcheck = $depchecker->validatePackage($send, $this, $params); + if (PEAR::isError($installcheck)) { + if (!isset($this->_options['soft'])) { + $this->log(0, $installcheck->getMessage()); + } + $hasfailed = true; + $params[$i] = false; + $reset = true; + $redo = true; + $failed = false; + PEAR_Downloader_Package::removeDuplicates($params); + continue 2; + } + + $failed = false; + if (isset($deps['required']) && is_array($deps['required'])) { + foreach ($deps['required'] as $type => $dep) { + // note: Dependency2 will never return a PEAR_Error if ignore-errors + // is specified, so soft is needed to turn off logging + if (!isset($dep[0])) { + if (PEAR::isError($e = $depchecker->{"validate{$type}Dependency"}($dep, + true, $params))) { + $failed = true; + if (!isset($this->_options['soft'])) { + $this->log(0, $e->getMessage()); + } + } elseif (is_array($e) && !$param->alreadyValidated()) { + if (!isset($this->_options['soft'])) { + $this->log(0, $e[0]); + } + } + } else { + foreach ($dep as $d) { + if (PEAR::isError($e = + $depchecker->{"validate{$type}Dependency"}($d, + true, $params))) { + $failed = true; + if (!isset($this->_options['soft'])) { + $this->log(0, $e->getMessage()); + } + } elseif (is_array($e) && !$param->alreadyValidated()) { + if (!isset($this->_options['soft'])) { + $this->log(0, $e[0]); + } + } + } + } + } + + if (isset($deps['optional']) && is_array($deps['optional'])) { + foreach ($deps['optional'] as $type => $dep) { + if (!isset($dep[0])) { + if (PEAR::isError($e = + $depchecker->{"validate{$type}Dependency"}($dep, + false, $params))) { + $failed = true; + if (!isset($this->_options['soft'])) { + $this->log(0, $e->getMessage()); + } + } elseif (is_array($e) && !$param->alreadyValidated()) { + if (!isset($this->_options['soft'])) { + $this->log(0, $e[0]); + } + } + } else { + foreach ($dep as $d) { + if (PEAR::isError($e = + $depchecker->{"validate{$type}Dependency"}($d, + false, $params))) { + $failed = true; + if (!isset($this->_options['soft'])) { + $this->log(0, $e->getMessage()); + } + } elseif (is_array($e) && !$param->alreadyValidated()) { + if (!isset($this->_options['soft'])) { + $this->log(0, $e[0]); + } + } + } + } + } + } + + $groupname = $param->getGroup(); + if (isset($deps['group']) && $groupname) { + if (!isset($deps['group'][0])) { + $deps['group'] = array($deps['group']); + } + + $found = false; + foreach ($deps['group'] as $group) { + if ($group['attribs']['name'] == $groupname) { + $found = true; + break; + } + } + + if ($found) { + unset($group['attribs']); + foreach ($group as $type => $dep) { + if (!isset($dep[0])) { + if (PEAR::isError($e = + $depchecker->{"validate{$type}Dependency"}($dep, + false, $params))) { + $failed = true; + if (!isset($this->_options['soft'])) { + $this->log(0, $e->getMessage()); + } + } elseif (is_array($e) && !$param->alreadyValidated()) { + if (!isset($this->_options['soft'])) { + $this->log(0, $e[0]); + } + } + } else { + foreach ($dep as $d) { + if (PEAR::isError($e = + $depchecker->{"validate{$type}Dependency"}($d, + false, $params))) { + $failed = true; + if (!isset($this->_options['soft'])) { + $this->log(0, $e->getMessage()); + } + } elseif (is_array($e) && !$param->alreadyValidated()) { + if (!isset($this->_options['soft'])) { + $this->log(0, $e[0]); + } + } + } + } + } + } + } + } else { + foreach ($deps as $dep) { + if (PEAR::isError($e = $depchecker->validateDependency1($dep, $params))) { + $failed = true; + if (!isset($this->_options['soft'])) { + $this->log(0, $e->getMessage()); + } + } elseif (is_array($e) && !$param->alreadyValidated()) { + if (!isset($this->_options['soft'])) { + $this->log(0, $e[0]); + } + } + } + } + $params[$i]->setValidated(); + } + + if ($failed) { + $hasfailed = true; + $params[$i] = false; + $reset = true; + $redo = true; + $failed = false; + PEAR_Downloader_Package::removeDuplicates($params); + continue 2; + } + } + } + + PEAR::staticPopErrorHandling(); + if ($hasfailed && (isset($this->_options['ignore-errors']) || + isset($this->_options['nodeps']))) { + // this is probably not needed, but just in case + if (!isset($this->_options['soft'])) { + $this->log(0, 'WARNING: dependencies failed'); + } + } + } + + /** + * Retrieve the directory that downloads will happen in + * @access private + * @return string + */ + function getDownloadDir() + { + if (isset($this->_downloadDir)) { + return $this->_downloadDir; + } + + $downloaddir = $this->config->get('download_dir'); + if (empty($downloaddir) || (is_dir($downloaddir) && !is_writable($downloaddir))) { + if (is_dir($downloaddir) && !is_writable($downloaddir)) { + $this->log(0, 'WARNING: configuration download directory "' . $downloaddir . + '" is not writeable. Change download_dir config variable to ' . + 'a writeable dir to avoid this warning'); + } + + if (!class_exists('System')) { + require_once 'System.php'; + } + + if (PEAR::isError($downloaddir = System::mktemp('-d'))) { + return $downloaddir; + } + $this->log(3, '+ tmp dir created at ' . $downloaddir); + } + + if (!is_writable($downloaddir)) { + if (PEAR::isError(System::mkdir(array('-p', $downloaddir))) || + !is_writable($downloaddir)) { + return PEAR::raiseError('download directory "' . $downloaddir . + '" is not writeable. Change download_dir config variable to ' . + 'a writeable dir'); + } + } + + return $this->_downloadDir = $downloaddir; + } + + function setDownloadDir($dir) + { + if (!@is_writable($dir)) { + if (PEAR::isError(System::mkdir(array('-p', $dir)))) { + return PEAR::raiseError('download directory "' . $dir . + '" is not writeable. Change download_dir config variable to ' . + 'a writeable dir'); + } + } + $this->_downloadDir = $dir; + } + + function configSet($key, $value, $layer = 'user', $channel = false) + { + $this->config->set($key, $value, $layer, $channel); + $this->_preferredState = $this->config->get('preferred_state', null, $channel); + if (!$this->_preferredState) { + // don't inadvertantly use a non-set preferred_state + $this->_preferredState = null; + } + } + + function setOptions($options) + { + $this->_options = $options; + } + + function getOptions() + { + return $this->_options; + } + + + /** + * @param array output of {@link parsePackageName()} + * @access private + */ + function _getPackageDownloadUrl($parr) + { + $curchannel = $this->config->get('default_channel'); + $this->configSet('default_channel', $parr['channel']); + // getDownloadURL returns an array. On error, it only contains information + // on the latest release as array(version, info). On success it contains + // array(version, info, download url string) + $state = isset($parr['state']) ? $parr['state'] : $this->config->get('preferred_state'); + if (!$this->_registry->channelExists($parr['channel'])) { + do { + if ($this->config->get('auto_discover') && $this->discover($parr['channel'])) { + break; + } + + $this->configSet('default_channel', $curchannel); + return PEAR::raiseError('Unknown remote channel: ' . $parr['channel']); + } while (false); + } + + $chan = &$this->_registry->getChannel($parr['channel']); + if (PEAR::isError($chan)) { + return $chan; + } + + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $version = $this->_registry->packageInfo($parr['package'], 'version', $parr['channel']); + $stability = $this->_registry->packageInfo($parr['package'], 'stability', $parr['channel']); + // package is installed - use the installed release stability level + if (!isset($parr['state']) && $stability !== null) { + $state = $stability['release']; + } + PEAR::staticPopErrorHandling(); + $base2 = false; + + $preferred_mirror = $this->config->get('preferred_mirror'); + if (!$chan->supportsREST($preferred_mirror) || + ( + !($base2 = $chan->getBaseURL('REST1.3', $preferred_mirror)) + && + !($base = $chan->getBaseURL('REST1.0', $preferred_mirror)) + ) + ) { + return $this->raiseError($parr['channel'] . ' is using a unsupported protocol - This should never happen.'); + } + + if ($base2) { + $rest = &$this->config->getREST('1.3', $this->_options); + $base = $base2; + } else { + $rest = &$this->config->getREST('1.0', $this->_options); + } + + $downloadVersion = false; + if (!isset($parr['version']) && !isset($parr['state']) && $version + && !PEAR::isError($version) + && !isset($this->_options['downloadonly']) + ) { + $downloadVersion = $version; + } + + $url = $rest->getDownloadURL($base, $parr, $state, $downloadVersion, $chan->getName()); + if (PEAR::isError($url)) { + $this->configSet('default_channel', $curchannel); + return $url; + } + + if ($parr['channel'] != $curchannel) { + $this->configSet('default_channel', $curchannel); + } + + if (!is_array($url)) { + return $url; + } + + $url['raw'] = false; // no checking is necessary for REST + if (!is_array($url['info'])) { + return PEAR::raiseError('Invalid remote dependencies retrieved from REST - ' . + 'this should never happen'); + } + + if (!isset($this->_options['force']) && + !isset($this->_options['downloadonly']) && + $version && + !PEAR::isError($version) && + !isset($parr['group']) + ) { + if (version_compare($version, $url['version'], '=')) { + return PEAR::raiseError($this->_registry->parsedPackageNameToString( + $parr, true) . ' is already installed and is the same as the ' . + 'released version ' . $url['version'], -976); + } + + if (version_compare($version, $url['version'], '>')) { + return PEAR::raiseError($this->_registry->parsedPackageNameToString( + $parr, true) . ' is already installed and is newer than detected ' . + 'released version ' . $url['version'], -976); + } + } + + if (isset($url['info']['required']) || $url['compatible']) { + require_once 'PEAR/PackageFile/v2.php'; + $pf = new PEAR_PackageFile_v2; + $pf->setRawChannel($parr['channel']); + if ($url['compatible']) { + $pf->setRawCompatible($url['compatible']); + } + } else { + require_once 'PEAR/PackageFile/v1.php'; + $pf = new PEAR_PackageFile_v1; + } + + $pf->setRawPackage($url['package']); + $pf->setDeps($url['info']); + if ($url['compatible']) { + $pf->setCompatible($url['compatible']); + } + + $pf->setRawState($url['stability']); + $url['info'] = &$pf; + if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) { + $ext = '.tar'; + } else { + $ext = '.tgz'; + } + + if (is_array($url) && isset($url['url'])) { + $url['url'] .= $ext; + } + + return $url; + } + + /** + * @param array dependency array + * @access private + */ + function _getDepPackageDownloadUrl($dep, $parr) + { + $xsdversion = isset($dep['rel']) ? '1.0' : '2.0'; + $curchannel = $this->config->get('default_channel'); + if (isset($dep['uri'])) { + $xsdversion = '2.0'; + $chan = &$this->_registry->getChannel('__uri'); + if (PEAR::isError($chan)) { + return $chan; + } + + $version = $this->_registry->packageInfo($dep['name'], 'version', '__uri'); + $this->configSet('default_channel', '__uri'); + } else { + if (isset($dep['channel'])) { + $remotechannel = $dep['channel']; + } else { + $remotechannel = 'pear.php.net'; + } + + if (!$this->_registry->channelExists($remotechannel)) { + do { + if ($this->config->get('auto_discover')) { + if ($this->discover($remotechannel)) { + break; + } + } + return PEAR::raiseError('Unknown remote channel: ' . $remotechannel); + } while (false); + } + + $chan = &$this->_registry->getChannel($remotechannel); + if (PEAR::isError($chan)) { + return $chan; + } + + $version = $this->_registry->packageInfo($dep['name'], 'version', $remotechannel); + $this->configSet('default_channel', $remotechannel); + } + + $state = isset($parr['state']) ? $parr['state'] : $this->config->get('preferred_state'); + if (isset($parr['state']) && isset($parr['version'])) { + unset($parr['state']); + } + + if (isset($dep['uri'])) { + $info = &$this->newDownloaderPackage($this); + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $err = $info->initialize($dep); + PEAR::staticPopErrorHandling(); + if (!$err) { + // skip parameters that were missed by preferred_state + return PEAR::raiseError('Cannot initialize dependency'); + } + + if (PEAR::isError($err)) { + if (!isset($this->_options['soft'])) { + $this->log(0, $err->getMessage()); + } + + if (is_object($info)) { + $param = $info->getChannel() . '/' . $info->getPackage(); + } + return PEAR::raiseError('Package "' . $param . '" is not valid'); + } + return $info; + } elseif ($chan->supportsREST($this->config->get('preferred_mirror')) + && + ( + ($base2 = $chan->getBaseURL('REST1.3', $this->config->get('preferred_mirror'))) + || + ($base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) + ) + ) { + if ($base2) { + $base = $base2; + $rest = &$this->config->getREST('1.3', $this->_options); + } else { + $rest = &$this->config->getREST('1.0', $this->_options); + } + + $url = $rest->getDepDownloadURL($base, $xsdversion, $dep, $parr, + $state, $version, $chan->getName()); + if (PEAR::isError($url)) { + return $url; + } + + if ($parr['channel'] != $curchannel) { + $this->configSet('default_channel', $curchannel); + } + + if (!is_array($url)) { + return $url; + } + + $url['raw'] = false; // no checking is necessary for REST + if (!is_array($url['info'])) { + return PEAR::raiseError('Invalid remote dependencies retrieved from REST - ' . + 'this should never happen'); + } + + if (isset($url['info']['required'])) { + if (!class_exists('PEAR_PackageFile_v2')) { + require_once 'PEAR/PackageFile/v2.php'; + } + $pf = new PEAR_PackageFile_v2; + $pf->setRawChannel($remotechannel); + } else { + if (!class_exists('PEAR_PackageFile_v1')) { + require_once 'PEAR/PackageFile/v1.php'; + } + $pf = new PEAR_PackageFile_v1; + + } + $pf->setRawPackage($url['package']); + $pf->setDeps($url['info']); + if ($url['compatible']) { + $pf->setCompatible($url['compatible']); + } + + $pf->setRawState($url['stability']); + $url['info'] = &$pf; + if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) { + $ext = '.tar'; + } else { + $ext = '.tgz'; + } + + if (is_array($url) && isset($url['url'])) { + $url['url'] .= $ext; + } + + return $url; + } + + return $this->raiseError($parr['channel'] . ' is using a unsupported protocol - This should never happen.'); + } + + /** + * @deprecated in favor of _getPackageDownloadUrl + */ + function getPackageDownloadUrl($package, $version = null, $channel = false) + { + if ($version) { + $package .= "-$version"; + } + if ($this === null || $this->_registry === null) { + $package = "http://pear.php.net/get/$package"; + } else { + $chan = $this->_registry->getChannel($channel); + if (PEAR::isError($chan)) { + return ''; + } + $package = "http://" . $chan->getServer() . "/get/$package"; + } + if (!extension_loaded("zlib")) { + $package .= '?uncompress=yes'; + } + return $package; + } + + /** + * Retrieve a list of downloaded packages after a call to {@link download()}. + * + * Also resets the list of downloaded packages. + * @return array + */ + function getDownloadedPackages() + { + $ret = $this->_downloadedPackages; + $this->_downloadedPackages = array(); + $this->_toDownload = array(); + return $ret; + } + + function _downloadCallback($msg, $params = null) + { + switch ($msg) { + case 'saveas': + $this->log(1, "downloading $params ..."); + break; + case 'done': + $this->log(1, '...done: ' . number_format($params, 0, '', ',') . ' bytes'); + break; + case 'bytesread': + static $bytes; + if (empty($bytes)) { + $bytes = 0; + } + if (!($bytes % 10240)) { + $this->log(1, '.', false); + } + $bytes += $params; + break; + case 'start': + if($params[1] == -1) { + $length = "Unknown size"; + } else { + $length = number_format($params[1], 0, '', ',')." bytes"; + } + $this->log(1, "Starting to download {$params[0]} ($length)"); + break; + } + if (method_exists($this->ui, '_downloadCallback')) + $this->ui->_downloadCallback($msg, $params); + } + + function _prependPath($path, $prepend) + { + if (strlen($prepend) > 0) { + if (OS_WINDOWS && preg_match('/^[a-z]:/i', $path)) { + if (preg_match('/^[a-z]:/i', $prepend)) { + $prepend = substr($prepend, 2); + } elseif ($prepend{0} != '\\') { + $prepend = "\\$prepend"; + } + $path = substr($path, 0, 2) . $prepend . substr($path, 2); + } else { + $path = $prepend . $path; + } + } + return $path; + } + + /** + * @param string + * @param integer + */ + function pushError($errmsg, $code = -1) + { + array_push($this->_errorStack, array($errmsg, $code)); + } + + function getErrorMsgs() + { + $msgs = array(); + $errs = $this->_errorStack; + foreach ($errs as $err) { + $msgs[] = $err[0]; + } + $this->_errorStack = array(); + return $msgs; + } + + /** + * for BC + * + * @deprecated + */ + function sortPkgDeps(&$packages, $uninstall = false) + { + $uninstall ? + $this->sortPackagesForUninstall($packages) : + $this->sortPackagesForInstall($packages); + } + + /** + * Sort a list of arrays of array(downloaded packagefilename) by dependency. + * + * This uses the topological sort method from graph theory, and the + * Structures_Graph package to properly sort dependencies for installation. + * @param array an array of downloaded PEAR_Downloader_Packages + * @return array array of array(packagefilename, package.xml contents) + */ + function sortPackagesForInstall(&$packages) + { + require_once 'Structures/Graph.php'; + require_once 'Structures/Graph/Node.php'; + require_once 'Structures/Graph/Manipulator/TopologicalSorter.php'; + $depgraph = new Structures_Graph(true); + $nodes = array(); + $reg = &$this->config->getRegistry(); + foreach ($packages as $i => $package) { + $pname = $reg->parsedPackageNameToString( + array( + 'channel' => $package->getChannel(), + 'package' => strtolower($package->getPackage()), + )); + $nodes[$pname] = new Structures_Graph_Node; + $nodes[$pname]->setData($packages[$i]); + $depgraph->addNode($nodes[$pname]); + } + + $deplinks = array(); + foreach ($nodes as $package => $node) { + $pf = &$node->getData(); + $pdeps = $pf->getDeps(true); + if (!$pdeps) { + continue; + } + + if ($pf->getPackagexmlVersion() == '1.0') { + foreach ($pdeps as $dep) { + if ($dep['type'] != 'pkg' || + (isset($dep['optional']) && $dep['optional'] == 'yes')) { + continue; + } + + $dname = $reg->parsedPackageNameToString( + array( + 'channel' => 'pear.php.net', + 'package' => strtolower($dep['name']), + )); + + if (isset($nodes[$dname])) { + if (!isset($deplinks[$dname])) { + $deplinks[$dname] = array(); + } + + $deplinks[$dname][$package] = 1; + // dependency is in installed packages + continue; + } + + $dname = $reg->parsedPackageNameToString( + array( + 'channel' => 'pecl.php.net', + 'package' => strtolower($dep['name']), + )); + + if (isset($nodes[$dname])) { + if (!isset($deplinks[$dname])) { + $deplinks[$dname] = array(); + } + + $deplinks[$dname][$package] = 1; + // dependency is in installed packages + continue; + } + } + } else { + // the only ordering we care about is: + // 1) subpackages must be installed before packages that depend on them + // 2) required deps must be installed before packages that depend on them + if (isset($pdeps['required']['subpackage'])) { + $t = $pdeps['required']['subpackage']; + if (!isset($t[0])) { + $t = array($t); + } + + $this->_setupGraph($t, $reg, $deplinks, $nodes, $package); + } + + if (isset($pdeps['group'])) { + if (!isset($pdeps['group'][0])) { + $pdeps['group'] = array($pdeps['group']); + } + + foreach ($pdeps['group'] as $group) { + if (isset($group['subpackage'])) { + $t = $group['subpackage']; + if (!isset($t[0])) { + $t = array($t); + } + + $this->_setupGraph($t, $reg, $deplinks, $nodes, $package); + } + } + } + + if (isset($pdeps['optional']['subpackage'])) { + $t = $pdeps['optional']['subpackage']; + if (!isset($t[0])) { + $t = array($t); + } + + $this->_setupGraph($t, $reg, $deplinks, $nodes, $package); + } + + if (isset($pdeps['required']['package'])) { + $t = $pdeps['required']['package']; + if (!isset($t[0])) { + $t = array($t); + } + + $this->_setupGraph($t, $reg, $deplinks, $nodes, $package); + } + + if (isset($pdeps['group'])) { + if (!isset($pdeps['group'][0])) { + $pdeps['group'] = array($pdeps['group']); + } + + foreach ($pdeps['group'] as $group) { + if (isset($group['package'])) { + $t = $group['package']; + if (!isset($t[0])) { + $t = array($t); + } + + $this->_setupGraph($t, $reg, $deplinks, $nodes, $package); + } + } + } + } + } + + $this->_detectDepCycle($deplinks); + foreach ($deplinks as $dependent => $parents) { + foreach ($parents as $parent => $unused) { + $nodes[$dependent]->connectTo($nodes[$parent]); + } + } + + $installOrder = Structures_Graph_Manipulator_TopologicalSorter::sort($depgraph); + $ret = array(); + for ($i = 0, $count = count($installOrder); $i < $count; $i++) { + foreach ($installOrder[$i] as $index => $sortedpackage) { + $data = &$installOrder[$i][$index]->getData(); + $ret[] = &$nodes[$reg->parsedPackageNameToString( + array( + 'channel' => $data->getChannel(), + 'package' => strtolower($data->getPackage()), + ))]->getData(); + } + } + + $packages = $ret; + return; + } + + /** + * Detect recursive links between dependencies and break the cycles + * + * @param array + * @access private + */ + function _detectDepCycle(&$deplinks) + { + do { + $keepgoing = false; + foreach ($deplinks as $dep => $parents) { + foreach ($parents as $parent => $unused) { + // reset the parent cycle detector + $this->_testCycle(null, null, null); + if ($this->_testCycle($dep, $deplinks, $parent)) { + $keepgoing = true; + unset($deplinks[$dep][$parent]); + if (count($deplinks[$dep]) == 0) { + unset($deplinks[$dep]); + } + + continue 3; + } + } + } + } while ($keepgoing); + } + + function _testCycle($test, $deplinks, $dep) + { + static $visited = array(); + if ($test === null) { + $visited = array(); + return; + } + + // this happens when a parent has a dep cycle on another dependency + // but the child is not part of the cycle + if (isset($visited[$dep])) { + return false; + } + + $visited[$dep] = 1; + if ($test == $dep) { + return true; + } + + if (isset($deplinks[$dep])) { + if (in_array($test, array_keys($deplinks[$dep]), true)) { + return true; + } + + foreach ($deplinks[$dep] as $parent => $unused) { + if ($this->_testCycle($test, $deplinks, $parent)) { + return true; + } + } + } + + return false; + } + + /** + * Set up the dependency for installation parsing + * + * @param array $t dependency information + * @param PEAR_Registry $reg + * @param array $deplinks list of dependency links already established + * @param array $nodes all existing package nodes + * @param string $package parent package name + * @access private + */ + function _setupGraph($t, $reg, &$deplinks, &$nodes, $package) + { + foreach ($t as $dep) { + $depchannel = !isset($dep['channel']) ? '__uri': $dep['channel']; + $dname = $reg->parsedPackageNameToString( + array( + 'channel' => $depchannel, + 'package' => strtolower($dep['name']), + )); + + if (isset($nodes[$dname])) { + if (!isset($deplinks[$dname])) { + $deplinks[$dname] = array(); + } + $deplinks[$dname][$package] = 1; + } + } + } + + function _dependsOn($a, $b) + { + return $this->_checkDepTree(strtolower($a->getChannel()), strtolower($a->getPackage()), $b); + } + + function _checkDepTree($channel, $package, $b, $checked = array()) + { + $checked[$channel][$package] = true; + if (!isset($this->_depTree[$channel][$package])) { + return false; + } + + if (isset($this->_depTree[$channel][$package][strtolower($b->getChannel())] + [strtolower($b->getPackage())])) { + return true; + } + + foreach ($this->_depTree[$channel][$package] as $ch => $packages) { + foreach ($packages as $pa => $true) { + if ($this->_checkDepTree($ch, $pa, $b, $checked)) { + return true; + } + } + } + + return false; + } + + function _sortInstall($a, $b) + { + if (!$a->getDeps() && !$b->getDeps()) { + return 0; // neither package has dependencies, order is insignificant + } + if ($a->getDeps() && !$b->getDeps()) { + return 1; // $a must be installed after $b because $a has dependencies + } + if (!$a->getDeps() && $b->getDeps()) { + return -1; // $b must be installed after $a because $b has dependencies + } + // both packages have dependencies + if ($this->_dependsOn($a, $b)) { + return 1; + } + if ($this->_dependsOn($b, $a)) { + return -1; + } + return 0; + } + + /** + * Download a file through HTTP. Considers suggested file name in + * Content-disposition: header and can run a callback function for + * different events. The callback will be called with two + * parameters: the callback type, and parameters. The implemented + * callback types are: + * + * 'setup' called at the very beginning, parameter is a UI object + * that should be used for all output + * 'message' the parameter is a string with an informational message + * 'saveas' may be used to save with a different file name, the + * parameter is the filename that is about to be used. + * If a 'saveas' callback returns a non-empty string, + * that file name will be used as the filename instead. + * Note that $save_dir will not be affected by this, only + * the basename of the file. + * 'start' download is starting, parameter is number of bytes + * that are expected, or -1 if unknown + * 'bytesread' parameter is the number of bytes read so far + * 'done' download is complete, parameter is the total number + * of bytes read + * 'connfailed' if the TCP/SSL connection fails, this callback is called + * with array(host,port,errno,errmsg) + * 'writefailed' if writing to disk fails, this callback is called + * with array(destfile,errmsg) + * + * If an HTTP proxy has been configured (http_proxy PEAR_Config + * setting), the proxy will be used. + * + * @param string $url the URL to download + * @param object $ui PEAR_Frontend_* instance + * @param object $config PEAR_Config instance + * @param string $save_dir directory to save file in + * @param mixed $callback function/method to call for status + * updates + * @param false|string|array $lastmodified header values to check against for caching + * use false to return the header values from this download + * @param false|array $accept Accept headers to send + * @param false|string $channel Channel to use for retrieving authentication + * @return string|array Returns the full path of the downloaded file or a PEAR + * error on failure. If the error is caused by + * socket-related errors, the error object will + * have the fsockopen error code available through + * getCode(). If caching is requested, then return the header + * values. + * + * @access public + */ + function downloadHttp($url, &$ui, $save_dir = '.', $callback = null, $lastmodified = null, + $accept = false, $channel = false) + { + static $redirect = 0; + // always reset , so we are clean case of error + $wasredirect = $redirect; + $redirect = 0; + if ($callback) { + call_user_func($callback, 'setup', array(&$ui)); + } + + $info = parse_url($url); + if (!isset($info['scheme']) || !in_array($info['scheme'], array('http', 'https'))) { + return PEAR::raiseError('Cannot download non-http URL "' . $url . '"'); + } + + if (!isset($info['host'])) { + return PEAR::raiseError('Cannot download from non-URL "' . $url . '"'); + } + + $host = isset($info['host']) ? $info['host'] : null; + $port = isset($info['port']) ? $info['port'] : null; + $path = isset($info['path']) ? $info['path'] : null; + + if (isset($this)) { + $config = &$this->config; + } else { + $config = &PEAR_Config::singleton(); + } + + $proxy_host = $proxy_port = $proxy_user = $proxy_pass = ''; + if ($config->get('http_proxy') && + $proxy = parse_url($config->get('http_proxy'))) { + $proxy_host = isset($proxy['host']) ? $proxy['host'] : null; + if (isset($proxy['scheme']) && $proxy['scheme'] == 'https') { + $proxy_host = 'ssl://' . $proxy_host; + } + $proxy_port = isset($proxy['port']) ? $proxy['port'] : 8080; + $proxy_user = isset($proxy['user']) ? urldecode($proxy['user']) : null; + $proxy_pass = isset($proxy['pass']) ? urldecode($proxy['pass']) : null; + + if ($callback) { + call_user_func($callback, 'message', "Using HTTP proxy $host:$port"); + } + } + + if (empty($port)) { + $port = (isset($info['scheme']) && $info['scheme'] == 'https') ? 443 : 80; + } + + $scheme = (isset($info['scheme']) && $info['scheme'] == 'https') ? 'https' : 'http'; + + if ($proxy_host != '') { + $fp = @fsockopen($proxy_host, $proxy_port, $errno, $errstr); + if (!$fp) { + if ($callback) { + call_user_func($callback, 'connfailed', array($proxy_host, $proxy_port, + $errno, $errstr)); + } + return PEAR::raiseError("Connection to `$proxy_host:$proxy_port' failed: $errstr", $errno); + } + + if ($lastmodified === false || $lastmodified) { + $request = "GET $url HTTP/1.1\r\n"; + $request .= "Host: $host\r\n"; + } else { + $request = "GET $url HTTP/1.0\r\n"; + $request .= "Host: $host\r\n"; + } + } else { + $network_host = $host; + if (isset($info['scheme']) && $info['scheme'] == 'https') { + $network_host = 'ssl://' . $host; + } + + $fp = @fsockopen($network_host, $port, $errno, $errstr); + if (!$fp) { + if ($callback) { + call_user_func($callback, 'connfailed', array($host, $port, + $errno, $errstr)); + } + return PEAR::raiseError("Connection to `$host:$port' failed: $errstr", $errno); + } + + if ($lastmodified === false || $lastmodified) { + $request = "GET $path HTTP/1.1\r\n"; + $request .= "Host: $host\r\n"; + } else { + $request = "GET $path HTTP/1.0\r\n"; + $request .= "Host: $host\r\n"; + } + } + + $ifmodifiedsince = ''; + if (is_array($lastmodified)) { + if (isset($lastmodified['Last-Modified'])) { + $ifmodifiedsince = 'If-Modified-Since: ' . $lastmodified['Last-Modified'] . "\r\n"; + } + + if (isset($lastmodified['ETag'])) { + $ifmodifiedsince .= "If-None-Match: $lastmodified[ETag]\r\n"; + } + } else { + $ifmodifiedsince = ($lastmodified ? "If-Modified-Since: $lastmodified\r\n" : ''); + } + + $request .= $ifmodifiedsince . + "User-Agent: PEAR/1.9.4/PHP/" . PHP_VERSION . "\r\n"; + + if (isset($this)) { // only pass in authentication for non-static calls + $username = $config->get('username', null, $channel); + $password = $config->get('password', null, $channel); + if ($username && $password) { + $tmp = base64_encode("$username:$password"); + $request .= "Authorization: Basic $tmp\r\n"; + } + } + + if ($proxy_host != '' && $proxy_user != '') { + $request .= 'Proxy-Authorization: Basic ' . + base64_encode($proxy_user . ':' . $proxy_pass) . "\r\n"; + } + + if ($accept) { + $request .= 'Accept: ' . implode(', ', $accept) . "\r\n"; + } + + $request .= "Connection: close\r\n"; + $request .= "\r\n"; + fwrite($fp, $request); + $headers = array(); + $reply = 0; + while (trim($line = fgets($fp, 1024))) { + if (preg_match('/^([^:]+):\s+(.*)\s*\\z/', $line, $matches)) { + $headers[strtolower($matches[1])] = trim($matches[2]); + } elseif (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) { + $reply = (int)$matches[1]; + if ($reply == 304 && ($lastmodified || ($lastmodified === false))) { + return false; + } + + if (!in_array($reply, array(200, 301, 302, 303, 305, 307))) { + return PEAR::raiseError("File $scheme://$host:$port$path not valid (received: $line)"); + } + } + } + + if ($reply != 200) { + if (!isset($headers['location'])) { + return PEAR::raiseError("File $scheme://$host:$port$path not valid (redirected but no location)"); + } + + if ($wasredirect > 4) { + return PEAR::raiseError("File $scheme://$host:$port$path not valid (redirection looped more than 5 times)"); + } + + $redirect = $wasredirect + 1; + return $this->downloadHttp($headers['location'], + $ui, $save_dir, $callback, $lastmodified, $accept); + } + + if (isset($headers['content-disposition']) && + preg_match('/\sfilename=\"([^;]*\S)\"\s*(;|\\z)/', $headers['content-disposition'], $matches)) { + $save_as = basename($matches[1]); + } else { + $save_as = basename($url); + } + + if ($callback) { + $tmp = call_user_func($callback, 'saveas', $save_as); + if ($tmp) { + $save_as = $tmp; + } + } + + $dest_file = $save_dir . DIRECTORY_SEPARATOR . $save_as; + if (is_link($dest_file)) { + return PEAR::raiseError('SECURITY ERROR: Will not write to ' . $dest_file . ' as it is symlinked to ' . readlink($dest_file) . ' - Possible symlink attack'); + } + + if (!$wp = @fopen($dest_file, 'wb')) { + fclose($fp); + if ($callback) { + call_user_func($callback, 'writefailed', array($dest_file, $php_errormsg)); + } + return PEAR::raiseError("could not open $dest_file for writing"); + } + + $length = isset($headers['content-length']) ? $headers['content-length'] : -1; + + $bytes = 0; + if ($callback) { + call_user_func($callback, 'start', array(basename($dest_file), $length)); + } + + while ($data = fread($fp, 1024)) { + $bytes += strlen($data); + if ($callback) { + call_user_func($callback, 'bytesread', $bytes); + } + if (!@fwrite($wp, $data)) { + fclose($fp); + if ($callback) { + call_user_func($callback, 'writefailed', array($dest_file, $php_errormsg)); + } + return PEAR::raiseError("$dest_file: write failed ($php_errormsg)"); + } + } + + fclose($fp); + fclose($wp); + if ($callback) { + call_user_func($callback, 'done', $bytes); + } + + if ($lastmodified === false || $lastmodified) { + if (isset($headers['etag'])) { + $lastmodified = array('ETag' => $headers['etag']); + } + + if (isset($headers['last-modified'])) { + if (is_array($lastmodified)) { + $lastmodified['Last-Modified'] = $headers['last-modified']; + } else { + $lastmodified = $headers['last-modified']; + } + } + return array($dest_file, $lastmodified, $headers); + } + return $dest_file; + } +} \ No newline at end of file diff --git a/includes/pear/PEAR/Downloader/Package.php b/includes/pear/PEAR/Downloader/Package.php new file mode 100644 index 0000000..03a53ee --- /dev/null +++ b/includes/pear/PEAR/Downloader/Package.php @@ -0,0 +1,1988 @@ + + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a1 + */ + +/** + * Error code when parameter initialization fails because no releases + * exist within preferred_state, but releases do exist + */ +define('PEAR_DOWNLOADER_PACKAGE_STATE', -1003); +/** + * Error code when parameter initialization fails because no releases + * exist that will work with the existing PHP version + */ +define('PEAR_DOWNLOADER_PACKAGE_PHPVERSION', -1004); + +/** + * Coordinates download parameters and manages their dependencies + * prior to downloading them. + * + * Input can come from three sources: + * + * - local files (archives or package.xml) + * - remote files (downloadable urls) + * - abstract package names + * + * The first two elements are handled cleanly by PEAR_PackageFile, but the third requires + * accessing pearweb's xml-rpc interface to determine necessary dependencies, and the + * format returned of dependencies is slightly different from that used in package.xml. + * + * This class hides the differences between these elements, and makes automatic + * dependency resolution a piece of cake. It also manages conflicts when + * two classes depend on incompatible dependencies, or differing versions of the same + * package dependency. In addition, download will not be attempted if the php version is + * not supported, PEAR installer version is not supported, or non-PECL extensions are not + * installed. + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @PEAR-VER@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a1 + */ +class PEAR_Downloader_Package +{ + /** + * @var PEAR_Downloader + */ + var $_downloader; + /** + * @var PEAR_Config + */ + var $_config; + /** + * @var PEAR_Registry + */ + var $_registry; + /** + * Used to implement packagingroot properly + * @var PEAR_Registry + */ + var $_installRegistry; + /** + * @var PEAR_PackageFile_v1|PEAR_PackageFile|v2 + */ + var $_packagefile; + /** + * @var array + */ + var $_parsedname; + /** + * @var array + */ + var $_downloadURL; + /** + * @var array + */ + var $_downloadDeps = array(); + /** + * @var boolean + */ + var $_valid = false; + /** + * @var boolean + */ + var $_analyzed = false; + /** + * if this or a parent package was invoked with Package-state, this is set to the + * state variable. + * + * This allows temporary reassignment of preferred_state for a parent package and all of + * its dependencies. + * @var string|false + */ + var $_explicitState = false; + /** + * If this package is invoked with Package#group, this variable will be true + */ + var $_explicitGroup = false; + /** + * Package type local|url + * @var string + */ + var $_type; + /** + * Contents of package.xml, if downloaded from a remote channel + * @var string|false + * @access private + */ + var $_rawpackagefile; + /** + * @var boolean + * @access private + */ + var $_validated = false; + + /** + * @param PEAR_Downloader + */ + function PEAR_Downloader_Package(&$downloader) + { + $this->_downloader = &$downloader; + $this->_config = &$this->_downloader->config; + $this->_registry = &$this->_config->getRegistry(); + $options = $downloader->getOptions(); + if (isset($options['packagingroot'])) { + $this->_config->setInstallRoot($options['packagingroot']); + $this->_installRegistry = &$this->_config->getRegistry(); + $this->_config->setInstallRoot(false); + } else { + $this->_installRegistry = &$this->_registry; + } + $this->_valid = $this->_analyzed = false; + } + + /** + * Parse the input and determine whether this is a local file, a remote uri, or an + * abstract package name. + * + * This is the heart of the PEAR_Downloader_Package(), and is used in + * {@link PEAR_Downloader::download()} + * @param string + * @return bool|PEAR_Error + */ + function initialize($param) + { + $origErr = $this->_fromFile($param); + if ($this->_valid) { + return true; + } + + $options = $this->_downloader->getOptions(); + if (isset($options['offline'])) { + if (PEAR::isError($origErr) && !isset($options['soft'])) { + foreach ($origErr->getUserInfo() as $userInfo) { + if (isset($userInfo['message'])) { + $this->_downloader->log(0, $userInfo['message']); + } + } + + $this->_downloader->log(0, $origErr->getMessage()); + } + + return PEAR::raiseError('Cannot download non-local package "' . $param . '"'); + } + + $err = $this->_fromUrl($param); + if (PEAR::isError($err) || !$this->_valid) { + if ($this->_type == 'url') { + if (PEAR::isError($err) && !isset($options['soft'])) { + $this->_downloader->log(0, $err->getMessage()); + } + + return PEAR::raiseError("Invalid or missing remote package file"); + } + + $err = $this->_fromString($param); + if (PEAR::isError($err) || !$this->_valid) { + if (PEAR::isError($err) && $err->getCode() == PEAR_DOWNLOADER_PACKAGE_STATE) { + return false; // instruct the downloader to silently skip + } + + if (isset($this->_type) && $this->_type == 'local' && PEAR::isError($origErr)) { + if (is_array($origErr->getUserInfo())) { + foreach ($origErr->getUserInfo() as $err) { + if (is_array($err)) { + $err = $err['message']; + } + + if (!isset($options['soft'])) { + $this->_downloader->log(0, $err); + } + } + } + + if (!isset($options['soft'])) { + $this->_downloader->log(0, $origErr->getMessage()); + } + + if (is_array($param)) { + $param = $this->_registry->parsedPackageNameToString($param, true); + } + + if (!isset($options['soft'])) { + $this->_downloader->log(2, "Cannot initialize '$param', invalid or missing package file"); + } + + // Passing no message back - already logged above + return PEAR::raiseError(); + } + + if (PEAR::isError($err) && !isset($options['soft'])) { + $this->_downloader->log(0, $err->getMessage()); + } + + if (is_array($param)) { + $param = $this->_registry->parsedPackageNameToString($param, true); + } + + if (!isset($options['soft'])) { + $this->_downloader->log(2, "Cannot initialize '$param', invalid or missing package file"); + } + + // Passing no message back - already logged above + return PEAR::raiseError(); + } + } + + return true; + } + + /** + * Retrieve any non-local packages + * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|PEAR_Error + */ + function &download() + { + if (isset($this->_packagefile)) { + return $this->_packagefile; + } + + if (isset($this->_downloadURL['url'])) { + $this->_isvalid = false; + $info = $this->getParsedPackage(); + foreach ($info as $i => $p) { + $info[$i] = strtolower($p); + } + + $err = $this->_fromUrl($this->_downloadURL['url'], + $this->_registry->parsedPackageNameToString($this->_parsedname, true)); + $newinfo = $this->getParsedPackage(); + foreach ($newinfo as $i => $p) { + $newinfo[$i] = strtolower($p); + } + + if ($info != $newinfo) { + do { + if ($info['channel'] == 'pecl.php.net' && $newinfo['channel'] == 'pear.php.net') { + $info['channel'] = 'pear.php.net'; + if ($info == $newinfo) { + // skip the channel check if a pecl package says it's a PEAR package + break; + } + } + if ($info['channel'] == 'pear.php.net' && $newinfo['channel'] == 'pecl.php.net') { + $info['channel'] = 'pecl.php.net'; + if ($info == $newinfo) { + // skip the channel check if a pecl package says it's a PEAR package + break; + } + } + + return PEAR::raiseError('CRITICAL ERROR: We are ' . + $this->_registry->parsedPackageNameToString($info) . ', but the file ' . + 'downloaded claims to be ' . + $this->_registry->parsedPackageNameToString($this->getParsedPackage())); + } while (false); + } + + if (PEAR::isError($err) || !$this->_valid) { + return $err; + } + } + + $this->_type = 'local'; + return $this->_packagefile; + } + + function &getPackageFile() + { + return $this->_packagefile; + } + + function &getDownloader() + { + return $this->_downloader; + } + + function getType() + { + return $this->_type; + } + + /** + * Like {@link initialize()}, but operates on a dependency + */ + function fromDepURL($dep) + { + $this->_downloadURL = $dep; + if (isset($dep['uri'])) { + $options = $this->_downloader->getOptions(); + if (!extension_loaded("zlib") || isset($options['nocompress'])) { + $ext = '.tar'; + } else { + $ext = '.tgz'; + } + + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + $err = $this->_fromUrl($dep['uri'] . $ext); + PEAR::popErrorHandling(); + if (PEAR::isError($err)) { + if (!isset($options['soft'])) { + $this->_downloader->log(0, $err->getMessage()); + } + + return PEAR::raiseError('Invalid uri dependency "' . $dep['uri'] . $ext . '", ' . + 'cannot download'); + } + } else { + $this->_parsedname = + array( + 'package' => $dep['info']->getPackage(), + 'channel' => $dep['info']->getChannel(), + 'version' => $dep['version'] + ); + if (!isset($dep['nodefault'])) { + $this->_parsedname['group'] = 'default'; // download the default dependency group + $this->_explicitGroup = false; + } + + $this->_rawpackagefile = $dep['raw']; + } + } + + function detectDependencies($params) + { + $options = $this->_downloader->getOptions(); + if (isset($options['downloadonly'])) { + return; + } + + if (isset($options['offline'])) { + $this->_downloader->log(3, 'Skipping dependency download check, --offline specified'); + return; + } + + $pname = $this->getParsedPackage(); + if (!$pname) { + return; + } + + $deps = $this->getDeps(); + if (!$deps) { + return; + } + + if (isset($deps['required'])) { // package.xml 2.0 + return $this->_detect2($deps, $pname, $options, $params); + } + + return $this->_detect1($deps, $pname, $options, $params); + } + + function setValidated() + { + $this->_validated = true; + } + + function alreadyValidated() + { + return $this->_validated; + } + + /** + * Remove packages to be downloaded that are already installed + * @param array of PEAR_Downloader_Package objects + * @static + */ + function removeInstalled(&$params) + { + if (!isset($params[0])) { + return; + } + + $options = $params[0]->_downloader->getOptions(); + if (!isset($options['downloadonly'])) { + foreach ($params as $i => $param) { + $package = $param->getPackage(); + $channel = $param->getChannel(); + // remove self if already installed with this version + // this does not need any pecl magic - we only remove exact matches + if ($param->_installRegistry->packageExists($package, $channel)) { + $packageVersion = $param->_installRegistry->packageInfo($package, 'version', $channel); + if (version_compare($packageVersion, $param->getVersion(), '==')) { + if (!isset($options['force'])) { + $info = $param->getParsedPackage(); + unset($info['version']); + unset($info['state']); + if (!isset($options['soft'])) { + $param->_downloader->log(1, 'Skipping package "' . + $param->getShortName() . + '", already installed as version ' . $packageVersion); + } + $params[$i] = false; + } + } elseif (!isset($options['force']) && !isset($options['upgrade']) && + !isset($options['soft'])) { + $info = $param->getParsedPackage(); + $param->_downloader->log(1, 'Skipping package "' . + $param->getShortName() . + '", already installed as version ' . $packageVersion); + $params[$i] = false; + } + } + } + } + + PEAR_Downloader_Package::removeDuplicates($params); + } + + function _detect2($deps, $pname, $options, $params) + { + $this->_downloadDeps = array(); + $groupnotfound = false; + foreach (array('package', 'subpackage') as $packagetype) { + // get required dependency group + if (isset($deps['required'][$packagetype])) { + if (isset($deps['required'][$packagetype][0])) { + foreach ($deps['required'][$packagetype] as $dep) { + if (isset($dep['conflicts'])) { + // skip any package that this package conflicts with + continue; + } + $ret = $this->_detect2Dep($dep, $pname, 'required', $params); + if (is_array($ret)) { + $this->_downloadDeps[] = $ret; + } elseif (PEAR::isError($ret) && !isset($options['soft'])) { + $this->_downloader->log(0, $ret->getMessage()); + } + } + } else { + $dep = $deps['required'][$packagetype]; + if (!isset($dep['conflicts'])) { + // skip any package that this package conflicts with + $ret = $this->_detect2Dep($dep, $pname, 'required', $params); + if (is_array($ret)) { + $this->_downloadDeps[] = $ret; + } elseif (PEAR::isError($ret) && !isset($options['soft'])) { + $this->_downloader->log(0, $ret->getMessage()); + } + } + } + } + + // get optional dependency group, if any + if (isset($deps['optional'][$packagetype])) { + $skipnames = array(); + if (!isset($deps['optional'][$packagetype][0])) { + $deps['optional'][$packagetype] = array($deps['optional'][$packagetype]); + } + + foreach ($deps['optional'][$packagetype] as $dep) { + $skip = false; + if (!isset($options['alldeps'])) { + $dep['package'] = $dep['name']; + if (!isset($options['soft'])) { + $this->_downloader->log(3, 'Notice: package "' . + $this->_registry->parsedPackageNameToString($this->getParsedPackage(), + true) . '" optional dependency "' . + $this->_registry->parsedPackageNameToString(array('package' => + $dep['name'], 'channel' => 'pear.php.net'), true) . + '" will not be automatically downloaded'); + } + $skipnames[] = $this->_registry->parsedPackageNameToString($dep, true); + $skip = true; + unset($dep['package']); + } + + $ret = $this->_detect2Dep($dep, $pname, 'optional', $params); + if (PEAR::isError($ret) && !isset($options['soft'])) { + $this->_downloader->log(0, $ret->getMessage()); + } + + if (!$ret) { + $dep['package'] = $dep['name']; + $skip = count($skipnames) ? + $skipnames[count($skipnames) - 1] : ''; + if ($skip == + $this->_registry->parsedPackageNameToString($dep, true)) { + array_pop($skipnames); + } + } + + if (!$skip && is_array($ret)) { + $this->_downloadDeps[] = $ret; + } + } + + if (count($skipnames)) { + if (!isset($options['soft'])) { + $this->_downloader->log(1, 'Did not download optional dependencies: ' . + implode(', ', $skipnames) . + ', use --alldeps to download automatically'); + } + } + } + + // get requested dependency group, if any + $groupname = $this->getGroup(); + $explicit = $this->_explicitGroup; + if (!$groupname) { + if (!$this->canDefault()) { + continue; + } + + $groupname = 'default'; // try the default dependency group + } + + if ($groupnotfound) { + continue; + } + + if (isset($deps['group'])) { + if (isset($deps['group']['attribs'])) { + if (strtolower($deps['group']['attribs']['name']) == strtolower($groupname)) { + $group = $deps['group']; + } elseif ($explicit) { + if (!isset($options['soft'])) { + $this->_downloader->log(0, 'Warning: package "' . + $this->_registry->parsedPackageNameToString($pname, true) . + '" has no dependency ' . 'group named "' . $groupname . '"'); + } + + $groupnotfound = true; + continue; + } + } else { + $found = false; + foreach ($deps['group'] as $group) { + if (strtolower($group['attribs']['name']) == strtolower($groupname)) { + $found = true; + break; + } + } + + if (!$found) { + if ($explicit) { + if (!isset($options['soft'])) { + $this->_downloader->log(0, 'Warning: package "' . + $this->_registry->parsedPackageNameToString($pname, true) . + '" has no dependency ' . 'group named "' . $groupname . '"'); + } + } + + $groupnotfound = true; + continue; + } + } + } + + if (isset($group) && isset($group[$packagetype])) { + if (isset($group[$packagetype][0])) { + foreach ($group[$packagetype] as $dep) { + $ret = $this->_detect2Dep($dep, $pname, 'dependency group "' . + $group['attribs']['name'] . '"', $params); + if (is_array($ret)) { + $this->_downloadDeps[] = $ret; + } elseif (PEAR::isError($ret) && !isset($options['soft'])) { + $this->_downloader->log(0, $ret->getMessage()); + } + } + } else { + $ret = $this->_detect2Dep($group[$packagetype], $pname, + 'dependency group "' . + $group['attribs']['name'] . '"', $params); + if (is_array($ret)) { + $this->_downloadDeps[] = $ret; + } elseif (PEAR::isError($ret) && !isset($options['soft'])) { + $this->_downloader->log(0, $ret->getMessage()); + } + } + } + } + } + + function _detect2Dep($dep, $pname, $group, $params) + { + if (isset($dep['conflicts'])) { + return true; + } + + $options = $this->_downloader->getOptions(); + if (isset($dep['uri'])) { + return array('uri' => $dep['uri'], 'dep' => $dep);; + } + + $testdep = $dep; + $testdep['package'] = $dep['name']; + if (PEAR_Downloader_Package::willDownload($testdep, $params)) { + $dep['package'] = $dep['name']; + if (!isset($options['soft'])) { + $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group . + ' dependency "' . + $this->_registry->parsedPackageNameToString($dep, true) . + '", will be installed'); + } + return false; + } + + $options = $this->_downloader->getOptions(); + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + if ($this->_explicitState) { + $pname['state'] = $this->_explicitState; + } + + $url = $this->_downloader->_getDepPackageDownloadUrl($dep, $pname); + if (PEAR::isError($url)) { + PEAR::popErrorHandling(); + return $url; + } + + $dep['package'] = $dep['name']; + $ret = $this->_analyzeDownloadURL($url, 'dependency', $dep, $params, $group == 'optional' && + !isset($options['alldeps']), true); + PEAR::popErrorHandling(); + if (PEAR::isError($ret)) { + if (!isset($options['soft'])) { + $this->_downloader->log(0, $ret->getMessage()); + } + + return false; + } + + // check to see if a dep is already installed and is the same or newer + if (!isset($dep['min']) && !isset($dep['max']) && !isset($dep['recommended'])) { + $oper = 'has'; + } else { + $oper = 'gt'; + } + + // do not try to move this before getDepPackageDownloadURL + // we can't determine whether upgrade is necessary until we know what + // version would be downloaded + if (!isset($options['force']) && $this->isInstalled($ret, $oper)) { + $version = $this->_installRegistry->packageInfo($dep['name'], 'version', $dep['channel']); + $dep['package'] = $dep['name']; + if (!isset($options['soft'])) { + $this->_downloader->log(3, $this->getShortName() . ': Skipping ' . $group . + ' dependency "' . + $this->_registry->parsedPackageNameToString($dep, true) . + '" version ' . $url['version'] . ', already installed as version ' . + $version); + } + + return false; + } + + if (isset($dep['nodefault'])) { + $ret['nodefault'] = true; + } + + return $ret; + } + + function _detect1($deps, $pname, $options, $params) + { + $this->_downloadDeps = array(); + $skipnames = array(); + foreach ($deps as $dep) { + $nodownload = false; + if (isset ($dep['type']) && $dep['type'] === 'pkg') { + $dep['channel'] = 'pear.php.net'; + $dep['package'] = $dep['name']; + switch ($dep['rel']) { + case 'not' : + continue 2; + case 'ge' : + case 'eq' : + case 'gt' : + case 'has' : + $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ? + 'required' : + 'optional'; + if (PEAR_Downloader_Package::willDownload($dep, $params)) { + $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group + . ' dependency "' . + $this->_registry->parsedPackageNameToString($dep, true) . + '", will be installed'); + continue 2; + } + $fakedp = new PEAR_PackageFile_v1; + $fakedp->setPackage($dep['name']); + // skip internet check if we are not upgrading (bug #5810) + if (!isset($options['upgrade']) && $this->isInstalled( + $fakedp, $dep['rel'])) { + $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group + . ' dependency "' . + $this->_registry->parsedPackageNameToString($dep, true) . + '", is already installed'); + continue 2; + } + } + + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + if ($this->_explicitState) { + $pname['state'] = $this->_explicitState; + } + + $url = $this->_downloader->_getDepPackageDownloadUrl($dep, $pname); + $chan = 'pear.php.net'; + if (PEAR::isError($url)) { + // check to see if this is a pecl package that has jumped + // from pear.php.net to pecl.php.net channel + if (!class_exists('PEAR_Dependency2')) { + require_once 'PEAR/Dependency2.php'; + } + + $newdep = PEAR_Dependency2::normalizeDep($dep); + $newdep = $newdep[0]; + $newdep['channel'] = 'pecl.php.net'; + $chan = 'pecl.php.net'; + $url = $this->_downloader->_getDepPackageDownloadUrl($newdep, $pname); + $obj = &$this->_installRegistry->getPackage($dep['name']); + if (PEAR::isError($url)) { + PEAR::popErrorHandling(); + if ($obj !== null && $this->isInstalled($obj, $dep['rel'])) { + $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ? + 'required' : + 'optional'; + $dep['package'] = $dep['name']; + if (!isset($options['soft'])) { + $this->_downloader->log(3, $this->getShortName() . + ': Skipping ' . $group . ' dependency "' . + $this->_registry->parsedPackageNameToString($dep, true) . + '", already installed as version ' . $obj->getVersion()); + } + $skip = count($skipnames) ? + $skipnames[count($skipnames) - 1] : ''; + if ($skip == + $this->_registry->parsedPackageNameToString($dep, true)) { + array_pop($skipnames); + } + continue; + } else { + if (isset($dep['optional']) && $dep['optional'] == 'yes') { + $this->_downloader->log(2, $this->getShortName() . + ': Skipping optional dependency "' . + $this->_registry->parsedPackageNameToString($dep, true) . + '", no releases exist'); + continue; + } else { + return $url; + } + } + } + } + + PEAR::popErrorHandling(); + if (!isset($options['alldeps'])) { + if (isset($dep['optional']) && $dep['optional'] == 'yes') { + if (!isset($options['soft'])) { + $this->_downloader->log(3, 'Notice: package "' . + $this->getShortName() . + '" optional dependency "' . + $this->_registry->parsedPackageNameToString( + array('channel' => $chan, 'package' => + $dep['name']), true) . + '" will not be automatically downloaded'); + } + $skipnames[] = $this->_registry->parsedPackageNameToString( + array('channel' => $chan, 'package' => + $dep['name']), true); + $nodownload = true; + } + } + + if (!isset($options['alldeps']) && !isset($options['onlyreqdeps'])) { + if (!isset($dep['optional']) || $dep['optional'] == 'no') { + if (!isset($options['soft'])) { + $this->_downloader->log(3, 'Notice: package "' . + $this->getShortName() . + '" required dependency "' . + $this->_registry->parsedPackageNameToString( + array('channel' => $chan, 'package' => + $dep['name']), true) . + '" will not be automatically downloaded'); + } + $skipnames[] = $this->_registry->parsedPackageNameToString( + array('channel' => $chan, 'package' => + $dep['name']), true); + $nodownload = true; + } + } + + // check to see if a dep is already installed + // do not try to move this before getDepPackageDownloadURL + // we can't determine whether upgrade is necessary until we know what + // version would be downloaded + if (!isset($options['force']) && $this->isInstalled( + $url, $dep['rel'])) { + $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ? + 'required' : + 'optional'; + $dep['package'] = $dep['name']; + if (isset($newdep)) { + $version = $this->_installRegistry->packageInfo($newdep['name'], 'version', $newdep['channel']); + } else { + $version = $this->_installRegistry->packageInfo($dep['name'], 'version'); + } + + $dep['version'] = $url['version']; + if (!isset($options['soft'])) { + $this->_downloader->log(3, $this->getShortName() . ': Skipping ' . $group . + ' dependency "' . + $this->_registry->parsedPackageNameToString($dep, true) . + '", already installed as version ' . $version); + } + + $skip = count($skipnames) ? + $skipnames[count($skipnames) - 1] : ''; + if ($skip == + $this->_registry->parsedPackageNameToString($dep, true)) { + array_pop($skipnames); + } + + continue; + } + + if ($nodownload) { + continue; + } + + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + if (isset($newdep)) { + $dep = $newdep; + } + + $dep['package'] = $dep['name']; + $ret = $this->_analyzeDownloadURL($url, 'dependency', $dep, $params, + isset($dep['optional']) && $dep['optional'] == 'yes' && + !isset($options['alldeps']), true); + PEAR::popErrorHandling(); + if (PEAR::isError($ret)) { + if (!isset($options['soft'])) { + $this->_downloader->log(0, $ret->getMessage()); + } + continue; + } + + $this->_downloadDeps[] = $ret; + } + } + + if (count($skipnames)) { + if (!isset($options['soft'])) { + $this->_downloader->log(1, 'Did not download dependencies: ' . + implode(', ', $skipnames) . + ', use --alldeps or --onlyreqdeps to download automatically'); + } + } + } + + function setDownloadURL($pkg) + { + $this->_downloadURL = $pkg; + } + + /** + * Set the package.xml object for this downloaded package + * + * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 $pkg + */ + function setPackageFile(&$pkg) + { + $this->_packagefile = &$pkg; + } + + function getShortName() + { + return $this->_registry->parsedPackageNameToString(array('channel' => $this->getChannel(), + 'package' => $this->getPackage()), true); + } + + function getParsedPackage() + { + if (isset($this->_packagefile) || isset($this->_parsedname)) { + return array('channel' => $this->getChannel(), + 'package' => $this->getPackage(), + 'version' => $this->getVersion()); + } + + return false; + } + + function getDownloadURL() + { + return $this->_downloadURL; + } + + function canDefault() + { + if (isset($this->_downloadURL) && isset($this->_downloadURL['nodefault'])) { + return false; + } + + return true; + } + + function getPackage() + { + if (isset($this->_packagefile)) { + return $this->_packagefile->getPackage(); + } elseif (isset($this->_downloadURL['info'])) { + return $this->_downloadURL['info']->getPackage(); + } + + return false; + } + + /** + * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 + */ + function isSubpackage(&$pf) + { + if (isset($this->_packagefile)) { + return $this->_packagefile->isSubpackage($pf); + } elseif (isset($this->_downloadURL['info'])) { + return $this->_downloadURL['info']->isSubpackage($pf); + } + + return false; + } + + function getPackageType() + { + if (isset($this->_packagefile)) { + return $this->_packagefile->getPackageType(); + } elseif (isset($this->_downloadURL['info'])) { + return $this->_downloadURL['info']->getPackageType(); + } + + return false; + } + + function isBundle() + { + if (isset($this->_packagefile)) { + return $this->_packagefile->getPackageType() == 'bundle'; + } + + return false; + } + + function getPackageXmlVersion() + { + if (isset($this->_packagefile)) { + return $this->_packagefile->getPackagexmlVersion(); + } elseif (isset($this->_downloadURL['info'])) { + return $this->_downloadURL['info']->getPackagexmlVersion(); + } + + return '1.0'; + } + + function getChannel() + { + if (isset($this->_packagefile)) { + return $this->_packagefile->getChannel(); + } elseif (isset($this->_downloadURL['info'])) { + return $this->_downloadURL['info']->getChannel(); + } + + return false; + } + + function getURI() + { + if (isset($this->_packagefile)) { + return $this->_packagefile->getURI(); + } elseif (isset($this->_downloadURL['info'])) { + return $this->_downloadURL['info']->getURI(); + } + + return false; + } + + function getVersion() + { + if (isset($this->_packagefile)) { + return $this->_packagefile->getVersion(); + } elseif (isset($this->_downloadURL['version'])) { + return $this->_downloadURL['version']; + } + + return false; + } + + function isCompatible($pf) + { + if (isset($this->_packagefile)) { + return $this->_packagefile->isCompatible($pf); + } elseif (isset($this->_downloadURL['info'])) { + return $this->_downloadURL['info']->isCompatible($pf); + } + + return true; + } + + function setGroup($group) + { + $this->_parsedname['group'] = $group; + } + + function getGroup() + { + if (isset($this->_parsedname['group'])) { + return $this->_parsedname['group']; + } + + return ''; + } + + function isExtension($name) + { + if (isset($this->_packagefile)) { + return $this->_packagefile->isExtension($name); + } elseif (isset($this->_downloadURL['info'])) { + if ($this->_downloadURL['info']->getPackagexmlVersion() == '2.0') { + return $this->_downloadURL['info']->getProvidesExtension() == $name; + } + + return false; + } + + return false; + } + + function getDeps() + { + if (isset($this->_packagefile)) { + $ver = $this->_packagefile->getPackagexmlVersion(); + if (version_compare($ver, '2.0', '>=')) { + return $this->_packagefile->getDeps(true); + } + + return $this->_packagefile->getDeps(); + } elseif (isset($this->_downloadURL['info'])) { + $ver = $this->_downloadURL['info']->getPackagexmlVersion(); + if (version_compare($ver, '2.0', '>=')) { + return $this->_downloadURL['info']->getDeps(true); + } + + return $this->_downloadURL['info']->getDeps(); + } + + return array(); + } + + /** + * @param array Parsed array from {@link PEAR_Registry::parsePackageName()} or a dependency + * returned from getDepDownloadURL() + */ + function isEqual($param) + { + if (is_object($param)) { + $channel = $param->getChannel(); + $package = $param->getPackage(); + if ($param->getURI()) { + $param = array( + 'channel' => $param->getChannel(), + 'package' => $param->getPackage(), + 'version' => $param->getVersion(), + 'uri' => $param->getURI(), + ); + } else { + $param = array( + 'channel' => $param->getChannel(), + 'package' => $param->getPackage(), + 'version' => $param->getVersion(), + ); + } + } else { + if (isset($param['uri'])) { + if ($this->getChannel() != '__uri') { + return false; + } + return $param['uri'] == $this->getURI(); + } + + $package = isset($param['package']) ? $param['package'] : $param['info']->getPackage(); + $channel = isset($param['channel']) ? $param['channel'] : $param['info']->getChannel(); + if (isset($param['rel'])) { + if (!class_exists('PEAR_Dependency2')) { + require_once 'PEAR/Dependency2.php'; + } + + $newdep = PEAR_Dependency2::normalizeDep($param); + $newdep = $newdep[0]; + } elseif (isset($param['min'])) { + $newdep = $param; + } + } + + if (isset($newdep)) { + if (!isset($newdep['min'])) { + $newdep['min'] = '0'; + } + + if (!isset($newdep['max'])) { + $newdep['max'] = '100000000000000000000'; + } + + // use magic to support pecl packages suddenly jumping to the pecl channel + // we need to support both dependency possibilities + if ($channel == 'pear.php.net' && $this->getChannel() == 'pecl.php.net') { + if ($package == $this->getPackage()) { + $channel = 'pecl.php.net'; + } + } + if ($channel == 'pecl.php.net' && $this->getChannel() == 'pear.php.net') { + if ($package == $this->getPackage()) { + $channel = 'pear.php.net'; + } + } + + return (strtolower($package) == strtolower($this->getPackage()) && + $channel == $this->getChannel() && + version_compare($newdep['min'], $this->getVersion(), '<=') && + version_compare($newdep['max'], $this->getVersion(), '>=')); + } + + // use magic to support pecl packages suddenly jumping to the pecl channel + if ($channel == 'pecl.php.net' && $this->getChannel() == 'pear.php.net') { + if (strtolower($package) == strtolower($this->getPackage())) { + $channel = 'pear.php.net'; + } + } + + if (isset($param['version'])) { + return (strtolower($package) == strtolower($this->getPackage()) && + $channel == $this->getChannel() && + $param['version'] == $this->getVersion()); + } + + return strtolower($package) == strtolower($this->getPackage()) && + $channel == $this->getChannel(); + } + + function isInstalled($dep, $oper = '==') + { + if (!$dep) { + return false; + } + + if ($oper != 'ge' && $oper != 'gt' && $oper != 'has' && $oper != '==') { + return false; + } + + if (is_object($dep)) { + $package = $dep->getPackage(); + $channel = $dep->getChannel(); + if ($dep->getURI()) { + $dep = array( + 'uri' => $dep->getURI(), + 'version' => $dep->getVersion(), + ); + } else { + $dep = array( + 'version' => $dep->getVersion(), + ); + } + } else { + if (isset($dep['uri'])) { + $channel = '__uri'; + $package = $dep['dep']['name']; + } else { + $channel = $dep['info']->getChannel(); + $package = $dep['info']->getPackage(); + } + } + + $options = $this->_downloader->getOptions(); + $test = $this->_installRegistry->packageExists($package, $channel); + if (!$test && $channel == 'pecl.php.net') { + // do magic to allow upgrading from old pecl packages to new ones + $test = $this->_installRegistry->packageExists($package, 'pear.php.net'); + $channel = 'pear.php.net'; + } + + if ($test) { + if (isset($dep['uri'])) { + if ($this->_installRegistry->packageInfo($package, 'uri', '__uri') == $dep['uri']) { + return true; + } + } + + if (isset($options['upgrade'])) { + $packageVersion = $this->_installRegistry->packageInfo($package, 'version', $channel); + if (version_compare($packageVersion, $dep['version'], '>=')) { + return true; + } + + return false; + } + + return true; + } + + return false; + } + + /** + * Detect duplicate package names with differing versions + * + * If a user requests to install Date 1.4.6 and Date 1.4.7, + * for instance, this is a logic error. This method + * detects this situation. + * + * @param array $params array of PEAR_Downloader_Package objects + * @param array $errorparams empty array + * @return array array of stupid duplicated packages in PEAR_Downloader_Package obejcts + */ + function detectStupidDuplicates($params, &$errorparams) + { + $existing = array(); + foreach ($params as $i => $param) { + $package = $param->getPackage(); + $channel = $param->getChannel(); + $group = $param->getGroup(); + if (!isset($existing[$channel . '/' . $package])) { + $existing[$channel . '/' . $package] = array(); + } + + if (!isset($existing[$channel . '/' . $package][$group])) { + $existing[$channel . '/' . $package][$group] = array(); + } + + $existing[$channel . '/' . $package][$group][] = $i; + } + + $indices = array(); + foreach ($existing as $package => $groups) { + foreach ($groups as $group => $dupes) { + if (count($dupes) > 1) { + $indices = $indices + $dupes; + } + } + } + + $indices = array_unique($indices); + foreach ($indices as $index) { + $errorparams[] = $params[$index]; + } + + return count($errorparams); + } + + /** + * @param array + * @param bool ignore install groups - for final removal of dupe packages + * @static + */ + function removeDuplicates(&$params, $ignoreGroups = false) + { + $pnames = array(); + foreach ($params as $i => $param) { + if (!$param) { + continue; + } + + if ($param->getPackage()) { + $group = $ignoreGroups ? '' : $param->getGroup(); + $pnames[$i] = $param->getChannel() . '/' . + $param->getPackage() . '-' . $param->getVersion() . '#' . $group; + } + } + + $pnames = array_unique($pnames); + $unset = array_diff(array_keys($params), array_keys($pnames)); + $testp = array_flip($pnames); + foreach ($params as $i => $param) { + if (!$param) { + $unset[] = $i; + continue; + } + + if (!is_a($param, 'PEAR_Downloader_Package')) { + $unset[] = $i; + continue; + } + + $group = $ignoreGroups ? '' : $param->getGroup(); + if (!isset($testp[$param->getChannel() . '/' . $param->getPackage() . '-' . + $param->getVersion() . '#' . $group])) { + $unset[] = $i; + } + } + + foreach ($unset as $i) { + unset($params[$i]); + } + + $ret = array(); + foreach ($params as $i => $param) { + $ret[] = &$params[$i]; + } + + $params = array(); + foreach ($ret as $i => $param) { + $params[] = &$ret[$i]; + } + } + + function explicitState() + { + return $this->_explicitState; + } + + function setExplicitState($s) + { + $this->_explicitState = $s; + } + + /** + * @static + */ + function mergeDependencies(&$params) + { + $bundles = $newparams = array(); + foreach ($params as $i => $param) { + if (!$param->isBundle()) { + continue; + } + + $bundles[] = $i; + $pf = &$param->getPackageFile(); + $newdeps = array(); + $contents = $pf->getBundledPackages(); + if (!is_array($contents)) { + $contents = array($contents); + } + + foreach ($contents as $file) { + $filecontents = $pf->getFileContents($file); + $dl = &$param->getDownloader(); + $options = $dl->getOptions(); + if (PEAR::isError($dir = $dl->getDownloadDir())) { + return $dir; + } + + $fp = @fopen($dir . DIRECTORY_SEPARATOR . $file, 'wb'); + if (!$fp) { + continue; + } + + // FIXME do symlink check + + fwrite($fp, $filecontents, strlen($filecontents)); + fclose($fp); + if ($s = $params[$i]->explicitState()) { + $obj->setExplicitState($s); + } + + $obj = &new PEAR_Downloader_Package($params[$i]->getDownloader()); + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + if (PEAR::isError($dir = $dl->getDownloadDir())) { + PEAR::popErrorHandling(); + return $dir; + } + + $e = $obj->_fromFile($a = $dir . DIRECTORY_SEPARATOR . $file); + PEAR::popErrorHandling(); + if (PEAR::isError($e)) { + if (!isset($options['soft'])) { + $dl->log(0, $e->getMessage()); + } + continue; + } + + $j = &$obj; + if (!PEAR_Downloader_Package::willDownload($j, + array_merge($params, $newparams)) && !$param->isInstalled($j)) { + $newparams[] = &$j; + } + } + } + + foreach ($bundles as $i) { + unset($params[$i]); // remove bundles - only their contents matter for installation + } + + PEAR_Downloader_Package::removeDuplicates($params); // strip any unset indices + if (count($newparams)) { // add in bundled packages for install + foreach ($newparams as $i => $unused) { + $params[] = &$newparams[$i]; + } + $newparams = array(); + } + + foreach ($params as $i => $param) { + $newdeps = array(); + foreach ($param->_downloadDeps as $dep) { + $merge = array_merge($params, $newparams); + if (!PEAR_Downloader_Package::willDownload($dep, $merge) + && !$param->isInstalled($dep) + ) { + $newdeps[] = $dep; + } else { + //var_dump($dep); + // detect versioning conflicts here + } + } + + // convert the dependencies into PEAR_Downloader_Package objects for the next time around + $params[$i]->_downloadDeps = array(); + foreach ($newdeps as $dep) { + $obj = &new PEAR_Downloader_Package($params[$i]->getDownloader()); + if ($s = $params[$i]->explicitState()) { + $obj->setExplicitState($s); + } + + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + $e = $obj->fromDepURL($dep); + PEAR::popErrorHandling(); + if (PEAR::isError($e)) { + if (!isset($options['soft'])) { + $obj->_downloader->log(0, $e->getMessage()); + } + continue; + } + + $e = $obj->detectDependencies($params); + if (PEAR::isError($e)) { + if (!isset($options['soft'])) { + $obj->_downloader->log(0, $e->getMessage()); + } + } + + $j = &$obj; + $newparams[] = &$j; + } + } + + if (count($newparams)) { + foreach ($newparams as $i => $unused) { + $params[] = &$newparams[$i]; + } + return true; + } + + return false; + } + + + /** + * @static + */ + function willDownload($param, $params) + { + if (!is_array($params)) { + return false; + } + + foreach ($params as $obj) { + if ($obj->isEqual($param)) { + return true; + } + } + + return false; + } + + /** + * For simpler unit-testing + * @param PEAR_Config + * @param int + * @param string + */ + function &getPackagefileObject(&$c, $d) + { + $a = &new PEAR_PackageFile($c, $d); + return $a; + } + + /** + * This will retrieve from a local file if possible, and parse out + * a group name as well. The original parameter will be modified to reflect this. + * @param string|array can be a parsed package name as well + * @access private + */ + function _fromFile(&$param) + { + $saveparam = $param; + if (is_string($param)) { + if (!@file_exists($param)) { + $test = explode('#', $param); + $group = array_pop($test); + if (@file_exists(implode('#', $test))) { + $this->setGroup($group); + $param = implode('#', $test); + $this->_explicitGroup = true; + } + } + + if (@is_file($param)) { + $this->_type = 'local'; + $options = $this->_downloader->getOptions(); + $pkg = &$this->getPackagefileObject($this->_config, $this->_downloader->_debug); + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + $pf = &$pkg->fromAnyFile($param, PEAR_VALIDATE_INSTALLING); + PEAR::popErrorHandling(); + if (PEAR::isError($pf)) { + $this->_valid = false; + $param = $saveparam; + return $pf; + } + $this->_packagefile = &$pf; + if (!$this->getGroup()) { + $this->setGroup('default'); // install the default dependency group + } + return $this->_valid = true; + } + } + $param = $saveparam; + return $this->_valid = false; + } + + function _fromUrl($param, $saveparam = '') + { + if (!is_array($param) && (preg_match('#^(http|https|ftp)://#', $param))) { + $options = $this->_downloader->getOptions(); + $this->_type = 'url'; + $callback = $this->_downloader->ui ? + array(&$this->_downloader, '_downloadCallback') : null; + $this->_downloader->pushErrorHandling(PEAR_ERROR_RETURN); + if (PEAR::isError($dir = $this->_downloader->getDownloadDir())) { + $this->_downloader->popErrorHandling(); + return $dir; + } + + $this->_downloader->log(3, 'Downloading "' . $param . '"'); + $file = $this->_downloader->downloadHttp($param, $this->_downloader->ui, + $dir, $callback, null, false, $this->getChannel()); + $this->_downloader->popErrorHandling(); + if (PEAR::isError($file)) { + if (!empty($saveparam)) { + $saveparam = ", cannot download \"$saveparam\""; + } + $err = PEAR::raiseError('Could not download from "' . $param . + '"' . $saveparam . ' (' . $file->getMessage() . ')'); + return $err; + } + + if ($this->_rawpackagefile) { + require_once 'Archive/Tar.php'; + $tar = &new Archive_Tar($file); + $packagexml = $tar->extractInString('package2.xml'); + if (!$packagexml) { + $packagexml = $tar->extractInString('package.xml'); + } + + if (str_replace(array("\n", "\r"), array('',''), $packagexml) != + str_replace(array("\n", "\r"), array('',''), $this->_rawpackagefile)) { + if ($this->getChannel() != 'pear.php.net') { + return PEAR::raiseError('CRITICAL ERROR: package.xml downloaded does ' . + 'not match value returned from xml-rpc'); + } + + // be more lax for the existing PEAR packages that have not-ok + // characters in their package.xml + $this->_downloader->log(0, 'CRITICAL WARNING: The "' . + $this->getPackage() . '" package has invalid characters in its ' . + 'package.xml. The next version of PEAR may not be able to install ' . + 'this package for security reasons. Please open a bug report at ' . + 'http://pear.php.net/package/' . $this->getPackage() . '/bugs'); + } + } + + // whew, download worked! + $pkg = &$this->getPackagefileObject($this->_config, $this->_downloader->debug); + + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + $pf = &$pkg->fromAnyFile($file, PEAR_VALIDATE_INSTALLING); + PEAR::popErrorHandling(); + if (PEAR::isError($pf)) { + if (is_array($pf->getUserInfo())) { + foreach ($pf->getUserInfo() as $err) { + if (is_array($err)) { + $err = $err['message']; + } + + if (!isset($options['soft'])) { + $this->_downloader->log(0, "Validation Error: $err"); + } + } + } + + if (!isset($options['soft'])) { + $this->_downloader->log(0, $pf->getMessage()); + } + + ///FIXME need to pass back some error code that we can use to match with to cancel all further operations + /// At least stop all deps of this package from being installed + $out = $saveparam ? $saveparam : $param; + $err = PEAR::raiseError('Download of "' . $out . '" succeeded, but it is not a valid package archive'); + $this->_valid = false; + return $err; + } + + $this->_packagefile = &$pf; + $this->setGroup('default'); // install the default dependency group + return $this->_valid = true; + } + + return $this->_valid = false; + } + + /** + * + * @param string|array pass in an array of format + * array( + * 'package' => 'pname', + * ['channel' => 'channame',] + * ['version' => 'version',] + * ['state' => 'state',]) + * or a string of format [channame/]pname[-version|-state] + */ + function _fromString($param) + { + $options = $this->_downloader->getOptions(); + $channel = $this->_config->get('default_channel'); + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + $pname = $this->_registry->parsePackageName($param, $channel); + PEAR::popErrorHandling(); + if (PEAR::isError($pname)) { + if ($pname->getCode() == 'invalid') { + $this->_valid = false; + return false; + } + + if ($pname->getCode() == 'channel') { + $parsed = $pname->getUserInfo(); + if ($this->_downloader->discover($parsed['channel'])) { + if ($this->_config->get('auto_discover')) { + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + $pname = $this->_registry->parsePackageName($param, $channel); + PEAR::popErrorHandling(); + } else { + if (!isset($options['soft'])) { + $this->_downloader->log(0, 'Channel "' . $parsed['channel'] . + '" is not initialized, use ' . + '"pear channel-discover ' . $parsed['channel'] . '" to initialize ' . + 'or pear config-set auto_discover 1'); + } + } + } + + if (PEAR::isError($pname)) { + if (!isset($options['soft'])) { + $this->_downloader->log(0, $pname->getMessage()); + } + + if (is_array($param)) { + $param = $this->_registry->parsedPackageNameToString($param); + } + + $err = PEAR::raiseError('invalid package name/package file "' . $param . '"'); + $this->_valid = false; + return $err; + } + } else { + if (!isset($options['soft'])) { + $this->_downloader->log(0, $pname->getMessage()); + } + + $err = PEAR::raiseError('invalid package name/package file "' . $param . '"'); + $this->_valid = false; + return $err; + } + } + + if (!isset($this->_type)) { + $this->_type = 'rest'; + } + + $this->_parsedname = $pname; + $this->_explicitState = isset($pname['state']) ? $pname['state'] : false; + $this->_explicitGroup = isset($pname['group']) ? true : false; + + $info = $this->_downloader->_getPackageDownloadUrl($pname); + if (PEAR::isError($info)) { + if ($info->getCode() != -976 && $pname['channel'] == 'pear.php.net') { + // try pecl + $pname['channel'] = 'pecl.php.net'; + if ($test = $this->_downloader->_getPackageDownloadUrl($pname)) { + if (!PEAR::isError($test)) { + $info = PEAR::raiseError($info->getMessage() . ' - package ' . + $this->_registry->parsedPackageNameToString($pname, true) . + ' can be installed with "pecl install ' . $pname['package'] . + '"'); + } else { + $pname['channel'] = 'pear.php.net'; + } + } else { + $pname['channel'] = 'pear.php.net'; + } + } + + return $info; + } + + $this->_rawpackagefile = $info['raw']; + $ret = $this->_analyzeDownloadURL($info, $param, $pname); + if (PEAR::isError($ret)) { + return $ret; + } + + if ($ret) { + $this->_downloadURL = $ret; + return $this->_valid = (bool) $ret; + } + } + + /** + * @param array output of package.getDownloadURL + * @param string|array|object information for detecting packages to be downloaded, and + * for errors + * @param array name information of the package + * @param array|null packages to be downloaded + * @param bool is this an optional dependency? + * @param bool is this any kind of dependency? + * @access private + */ + function _analyzeDownloadURL($info, $param, $pname, $params = null, $optional = false, + $isdependency = false) + { + if (!is_string($param) && PEAR_Downloader_Package::willDownload($param, $params)) { + return false; + } + + if ($info === false) { + $saveparam = !is_string($param) ? ", cannot download \"$param\"" : ''; + + // no releases exist + return PEAR::raiseError('No releases for package "' . + $this->_registry->parsedPackageNameToString($pname, true) . '" exist' . $saveparam); + } + + if (strtolower($info['info']->getChannel()) != strtolower($pname['channel'])) { + $err = false; + if ($pname['channel'] == 'pecl.php.net') { + if ($info['info']->getChannel() != 'pear.php.net') { + $err = true; + } + } elseif ($info['info']->getChannel() == 'pecl.php.net') { + if ($pname['channel'] != 'pear.php.net') { + $err = true; + } + } else { + $err = true; + } + + if ($err) { + return PEAR::raiseError('SECURITY ERROR: package in channel "' . $pname['channel'] . + '" retrieved another channel\'s name for download! ("' . + $info['info']->getChannel() . '")'); + } + } + + $preferred_state = $this->_config->get('preferred_state'); + if (!isset($info['url'])) { + $package_version = $this->_registry->packageInfo($info['info']->getPackage(), + 'version', $info['info']->getChannel()); + if ($this->isInstalled($info)) { + if ($isdependency && version_compare($info['version'], $package_version, '<=')) { + // ignore bogus errors of "failed to download dependency" + // if it is already installed and the one that would be + // downloaded is older or the same version (Bug #7219) + return false; + } + } + + if ($info['version'] === $package_version) { + if (!isset($options['soft'])) { + $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] . + '/' . $pname['package'] . '-' . $package_version. ', additionally the suggested version' . + ' (' . $package_version . ') is the same as the locally installed one.'); + } + + return false; + } + + if (version_compare($info['version'], $package_version, '<=')) { + if (!isset($options['soft'])) { + $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] . + '/' . $pname['package'] . '-' . $package_version . ', additionally the suggested version' . + ' (' . $info['version'] . ') is a lower version than the locally installed one (' . $package_version . ').'); + } + + return false; + } + + $instead = ', will instead download version ' . $info['version'] . + ', stability "' . $info['info']->getState() . '"'; + // releases exist, but we failed to get any + if (isset($this->_downloader->_options['force'])) { + if (isset($pname['version'])) { + $vs = ', version "' . $pname['version'] . '"'; + } elseif (isset($pname['state'])) { + $vs = ', stability "' . $pname['state'] . '"'; + } elseif ($param == 'dependency') { + if (!class_exists('PEAR_Common')) { + require_once 'PEAR/Common.php'; + } + + if (!in_array($info['info']->getState(), + PEAR_Common::betterStates($preferred_state, true))) { + if ($optional) { + // don't spit out confusing error message + return $this->_downloader->_getPackageDownloadUrl( + array('package' => $pname['package'], + 'channel' => $pname['channel'], + 'version' => $info['version'])); + } + $vs = ' within preferred state "' . $preferred_state . + '"'; + } else { + if (!class_exists('PEAR_Dependency2')) { + require_once 'PEAR/Dependency2.php'; + } + + if ($optional) { + // don't spit out confusing error message + return $this->_downloader->_getPackageDownloadUrl( + array('package' => $pname['package'], + 'channel' => $pname['channel'], + 'version' => $info['version'])); + } + $vs = PEAR_Dependency2::_getExtraString($pname); + $instead = ''; + } + } else { + $vs = ' within preferred state "' . $preferred_state . '"'; + } + + if (!isset($options['soft'])) { + $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] . + '/' . $pname['package'] . $vs . $instead); + } + + // download the latest release + return $this->_downloader->_getPackageDownloadUrl( + array('package' => $pname['package'], + 'channel' => $pname['channel'], + 'version' => $info['version'])); + } else { + if (isset($info['php']) && $info['php']) { + $err = PEAR::raiseError('Failed to download ' . + $this->_registry->parsedPackageNameToString( + array('channel' => $pname['channel'], + 'package' => $pname['package']), + true) . + ', latest release is version ' . $info['php']['v'] . + ', but it requires PHP version "' . + $info['php']['m'] . '", use "' . + $this->_registry->parsedPackageNameToString( + array('channel' => $pname['channel'], 'package' => $pname['package'], + 'version' => $info['php']['v'])) . '" to install', + PEAR_DOWNLOADER_PACKAGE_PHPVERSION); + return $err; + } + + // construct helpful error message + if (isset($pname['version'])) { + $vs = ', version "' . $pname['version'] . '"'; + } elseif (isset($pname['state'])) { + $vs = ', stability "' . $pname['state'] . '"'; + } elseif ($param == 'dependency') { + if (!class_exists('PEAR_Common')) { + require_once 'PEAR/Common.php'; + } + + if (!in_array($info['info']->getState(), + PEAR_Common::betterStates($preferred_state, true))) { + if ($optional) { + // don't spit out confusing error message, and don't die on + // optional dep failure! + return $this->_downloader->_getPackageDownloadUrl( + array('package' => $pname['package'], + 'channel' => $pname['channel'], + 'version' => $info['version'])); + } + $vs = ' within preferred state "' . $preferred_state . '"'; + } else { + if (!class_exists('PEAR_Dependency2')) { + require_once 'PEAR/Dependency2.php'; + } + + if ($optional) { + // don't spit out confusing error message, and don't die on + // optional dep failure! + return $this->_downloader->_getPackageDownloadUrl( + array('package' => $pname['package'], + 'channel' => $pname['channel'], + 'version' => $info['version'])); + } + $vs = PEAR_Dependency2::_getExtraString($pname); + } + } else { + $vs = ' within preferred state "' . $this->_downloader->config->get('preferred_state') . '"'; + } + + $options = $this->_downloader->getOptions(); + // this is only set by the "download-all" command + if (isset($options['ignorepreferred_state'])) { + $err = PEAR::raiseError( + 'Failed to download ' . $this->_registry->parsedPackageNameToString( + array('channel' => $pname['channel'], 'package' => $pname['package']), + true) + . $vs . + ', latest release is version ' . $info['version'] . + ', stability "' . $info['info']->getState() . '", use "' . + $this->_registry->parsedPackageNameToString( + array('channel' => $pname['channel'], 'package' => $pname['package'], + 'version' => $info['version'])) . '" to install', + PEAR_DOWNLOADER_PACKAGE_STATE); + return $err; + } + + // Checks if the user has a package installed already and checks the release against + // the state against the installed package, this allows upgrades for packages + // with lower stability than the preferred_state + $stability = $this->_registry->packageInfo($pname['package'], 'stability', $pname['channel']); + if (!$this->isInstalled($info) + || !in_array($info['info']->getState(), PEAR_Common::betterStates($stability['release'], true)) + ) { + $err = PEAR::raiseError( + 'Failed to download ' . $this->_registry->parsedPackageNameToString( + array('channel' => $pname['channel'], 'package' => $pname['package']), + true) + . $vs . + ', latest release is version ' . $info['version'] . + ', stability "' . $info['info']->getState() . '", use "' . + $this->_registry->parsedPackageNameToString( + array('channel' => $pname['channel'], 'package' => $pname['package'], + 'version' => $info['version'])) . '" to install'); + return $err; + } + } + } + + if (isset($info['deprecated']) && $info['deprecated']) { + $this->_downloader->log(0, + 'WARNING: "' . + $this->_registry->parsedPackageNameToString( + array('channel' => $info['info']->getChannel(), + 'package' => $info['info']->getPackage()), true) . + '" is deprecated in favor of "' . + $this->_registry->parsedPackageNameToString($info['deprecated'], true) . + '"'); + } + + return $info; + } +} \ No newline at end of file diff --git a/includes/pear/PEAR/ErrorStack.php b/includes/pear/PEAR/ErrorStack.php new file mode 100644 index 0000000..2cdddfc --- /dev/null +++ b/includes/pear/PEAR/ErrorStack.php @@ -0,0 +1,985 @@ + + * @copyright 2004-2008 Greg Beaver + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR_ErrorStack + */ + +/** + * Singleton storage + * + * Format: + *
    + * array(
    + *  'package1' => PEAR_ErrorStack object,
    + *  'package2' => PEAR_ErrorStack object,
    + *  ...
    + * )
    + * 
    + * @access private + * @global array $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] + */ +$GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] = array(); + +/** + * Global error callback (default) + * + * This is only used if set to non-false. * is the default callback for + * all packages, whereas specific packages may set a default callback + * for all instances, regardless of whether they are a singleton or not. + * + * To exclude non-singletons, only set the local callback for the singleton + * @see PEAR_ErrorStack::setDefaultCallback() + * @access private + * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'] + */ +$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'] = array( + '*' => false, +); + +/** + * Global Log object (default) + * + * This is only used if set to non-false. Use to set a default log object for + * all stacks, regardless of instantiation order or location + * @see PEAR_ErrorStack::setDefaultLogger() + * @access private + * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] + */ +$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = false; + +/** + * Global Overriding Callback + * + * This callback will override any error callbacks that specific loggers have set. + * Use with EXTREME caution + * @see PEAR_ErrorStack::staticPushCallback() + * @access private + * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] + */ +$GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array(); + +/**#@+ + * One of four possible return values from the error Callback + * @see PEAR_ErrorStack::_errorCallback() + */ +/** + * If this is returned, then the error will be both pushed onto the stack + * and logged. + */ +define('PEAR_ERRORSTACK_PUSHANDLOG', 1); +/** + * If this is returned, then the error will only be pushed onto the stack, + * and not logged. + */ +define('PEAR_ERRORSTACK_PUSH', 2); +/** + * If this is returned, then the error will only be logged, but not pushed + * onto the error stack. + */ +define('PEAR_ERRORSTACK_LOG', 3); +/** + * If this is returned, then the error is completely ignored. + */ +define('PEAR_ERRORSTACK_IGNORE', 4); +/** + * If this is returned, then the error is logged and die() is called. + */ +define('PEAR_ERRORSTACK_DIE', 5); +/**#@-*/ + +/** + * Error code for an attempt to instantiate a non-class as a PEAR_ErrorStack in + * the singleton method. + */ +define('PEAR_ERRORSTACK_ERR_NONCLASS', 1); + +/** + * Error code for an attempt to pass an object into {@link PEAR_ErrorStack::getMessage()} + * that has no __toString() method + */ +define('PEAR_ERRORSTACK_ERR_OBJTOSTRING', 2); +/** + * Error Stack Implementation + * + * Usage: + * + * // global error stack + * $global_stack = &PEAR_ErrorStack::singleton('MyPackage'); + * // local error stack + * $local_stack = new PEAR_ErrorStack('MyPackage'); + * + * @author Greg Beaver + * @version @package_version@ + * @package PEAR_ErrorStack + * @category Debugging + * @copyright 2004-2008 Greg Beaver + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR_ErrorStack + */ +class PEAR_ErrorStack { + /** + * Errors are stored in the order that they are pushed on the stack. + * @since 0.4alpha Errors are no longer organized by error level. + * This renders pop() nearly unusable, and levels could be more easily + * handled in a callback anyway + * @var array + * @access private + */ + var $_errors = array(); + + /** + * Storage of errors by level. + * + * Allows easy retrieval and deletion of only errors from a particular level + * @since PEAR 1.4.0dev + * @var array + * @access private + */ + var $_errorsByLevel = array(); + + /** + * Package name this error stack represents + * @var string + * @access protected + */ + var $_package; + + /** + * Determines whether a PEAR_Error is thrown upon every error addition + * @var boolean + * @access private + */ + var $_compat = false; + + /** + * If set to a valid callback, this will be used to generate the error + * message from the error code, otherwise the message passed in will be + * used + * @var false|string|array + * @access private + */ + var $_msgCallback = false; + + /** + * If set to a valid callback, this will be used to generate the error + * context for an error. For PHP-related errors, this will be a file + * and line number as retrieved from debug_backtrace(), but can be + * customized for other purposes. The error might actually be in a separate + * configuration file, or in a database query. + * @var false|string|array + * @access protected + */ + var $_contextCallback = false; + + /** + * If set to a valid callback, this will be called every time an error + * is pushed onto the stack. The return value will be used to determine + * whether to allow an error to be pushed or logged. + * + * The return value must be one an PEAR_ERRORSTACK_* constant + * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG + * @var false|string|array + * @access protected + */ + var $_errorCallback = array(); + + /** + * PEAR::Log object for logging errors + * @var false|Log + * @access protected + */ + var $_logger = false; + + /** + * Error messages - designed to be overridden + * @var array + * @abstract + */ + var $_errorMsgs = array(); + + /** + * Set up a new error stack + * + * @param string $package name of the package this error stack represents + * @param callback $msgCallback callback used for error message generation + * @param callback $contextCallback callback used for context generation, + * defaults to {@link getFileLine()} + * @param boolean $throwPEAR_Error + */ + function PEAR_ErrorStack($package, $msgCallback = false, $contextCallback = false, + $throwPEAR_Error = false) + { + $this->_package = $package; + $this->setMessageCallback($msgCallback); + $this->setContextCallback($contextCallback); + $this->_compat = $throwPEAR_Error; + } + + /** + * Return a single error stack for this package. + * + * Note that all parameters are ignored if the stack for package $package + * has already been instantiated + * @param string $package name of the package this error stack represents + * @param callback $msgCallback callback used for error message generation + * @param callback $contextCallback callback used for context generation, + * defaults to {@link getFileLine()} + * @param boolean $throwPEAR_Error + * @param string $stackClass class to instantiate + * @static + * @return PEAR_ErrorStack + */ + function &singleton($package, $msgCallback = false, $contextCallback = false, + $throwPEAR_Error = false, $stackClass = 'PEAR_ErrorStack') + { + if (isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) { + return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]; + } + if (!class_exists($stackClass)) { + if (function_exists('debug_backtrace')) { + $trace = debug_backtrace(); + } + PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_NONCLASS, + 'exception', array('stackclass' => $stackClass), + 'stack class "%stackclass%" is not a valid class name (should be like PEAR_ErrorStack)', + false, $trace); + } + $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package] = + new $stackClass($package, $msgCallback, $contextCallback, $throwPEAR_Error); + + return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]; + } + + /** + * Internal error handler for PEAR_ErrorStack class + * + * Dies if the error is an exception (and would have died anyway) + * @access private + */ + function _handleError($err) + { + if ($err['level'] == 'exception') { + $message = $err['message']; + if (isset($_SERVER['REQUEST_URI'])) { + echo '
    '; + } else { + echo "\n"; + } + var_dump($err['context']); + die($message); + } + } + + /** + * Set up a PEAR::Log object for all error stacks that don't have one + * @param Log $log + * @static + */ + function setDefaultLogger(&$log) + { + if (is_object($log) && method_exists($log, 'log') ) { + $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log; + } elseif (is_callable($log)) { + $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log; + } + } + + /** + * Set up a PEAR::Log object for this error stack + * @param Log $log + */ + function setLogger(&$log) + { + if (is_object($log) && method_exists($log, 'log') ) { + $this->_logger = &$log; + } elseif (is_callable($log)) { + $this->_logger = &$log; + } + } + + /** + * Set an error code => error message mapping callback + * + * This method sets the callback that can be used to generate error + * messages for any instance + * @param array|string Callback function/method + */ + function setMessageCallback($msgCallback) + { + if (!$msgCallback) { + $this->_msgCallback = array(&$this, 'getErrorMessage'); + } else { + if (is_callable($msgCallback)) { + $this->_msgCallback = $msgCallback; + } + } + } + + /** + * Get an error code => error message mapping callback + * + * This method returns the current callback that can be used to generate error + * messages + * @return array|string|false Callback function/method or false if none + */ + function getMessageCallback() + { + return $this->_msgCallback; + } + + /** + * Sets a default callback to be used by all error stacks + * + * This method sets the callback that can be used to generate error + * messages for a singleton + * @param array|string Callback function/method + * @param string Package name, or false for all packages + * @static + */ + function setDefaultCallback($callback = false, $package = false) + { + if (!is_callable($callback)) { + $callback = false; + } + $package = $package ? $package : '*'; + $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$package] = $callback; + } + + /** + * Set a callback that generates context information (location of error) for an error stack + * + * This method sets the callback that can be used to generate context + * information for an error. Passing in NULL will disable context generation + * and remove the expensive call to debug_backtrace() + * @param array|string|null Callback function/method + */ + function setContextCallback($contextCallback) + { + if ($contextCallback === null) { + return $this->_contextCallback = false; + } + if (!$contextCallback) { + $this->_contextCallback = array(&$this, 'getFileLine'); + } else { + if (is_callable($contextCallback)) { + $this->_contextCallback = $contextCallback; + } + } + } + + /** + * Set an error Callback + * If set to a valid callback, this will be called every time an error + * is pushed onto the stack. The return value will be used to determine + * whether to allow an error to be pushed or logged. + * + * The return value must be one of the ERRORSTACK_* constants. + * + * This functionality can be used to emulate PEAR's pushErrorHandling, and + * the PEAR_ERROR_CALLBACK mode, without affecting the integrity of + * the error stack or logging + * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG + * @see popCallback() + * @param string|array $cb + */ + function pushCallback($cb) + { + array_push($this->_errorCallback, $cb); + } + + /** + * Remove a callback from the error callback stack + * @see pushCallback() + * @return array|string|false + */ + function popCallback() + { + if (!count($this->_errorCallback)) { + return false; + } + return array_pop($this->_errorCallback); + } + + /** + * Set a temporary overriding error callback for every package error stack + * + * Use this to temporarily disable all existing callbacks (can be used + * to emulate the @ operator, for instance) + * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG + * @see staticPopCallback(), pushCallback() + * @param string|array $cb + * @static + */ + function staticPushCallback($cb) + { + array_push($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'], $cb); + } + + /** + * Remove a temporary overriding error callback + * @see staticPushCallback() + * @return array|string|false + * @static + */ + function staticPopCallback() + { + $ret = array_pop($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK']); + if (!is_array($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'])) { + $GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array(); + } + return $ret; + } + + /** + * Add an error to the stack + * + * If the message generator exists, it is called with 2 parameters. + * - the current Error Stack object + * - an array that is in the same format as an error. Available indices + * are 'code', 'package', 'time', 'params', 'level', and 'context' + * + * Next, if the error should contain context information, this is + * handled by the context grabbing method. + * Finally, the error is pushed onto the proper error stack + * @param int $code Package-specific error code + * @param string $level Error level. This is NOT spell-checked + * @param array $params associative array of error parameters + * @param string $msg Error message, or a portion of it if the message + * is to be generated + * @param array $repackage If this error re-packages an error pushed by + * another package, place the array returned from + * {@link pop()} in this parameter + * @param array $backtrace Protected parameter: use this to pass in the + * {@link debug_backtrace()} that should be used + * to find error context + * @return PEAR_Error|array if compatibility mode is on, a PEAR_Error is also + * thrown. If a PEAR_Error is returned, the userinfo + * property is set to the following array: + * + * + * array( + * 'code' => $code, + * 'params' => $params, + * 'package' => $this->_package, + * 'level' => $level, + * 'time' => time(), + * 'context' => $context, + * 'message' => $msg, + * //['repackage' => $err] repackaged error array/Exception class + * ); + * + * + * Normally, the previous array is returned. + */ + function push($code, $level = 'error', $params = array(), $msg = false, + $repackage = false, $backtrace = false) + { + $context = false; + // grab error context + if ($this->_contextCallback) { + if (!$backtrace) { + $backtrace = debug_backtrace(); + } + $context = call_user_func($this->_contextCallback, $code, $params, $backtrace); + } + + // save error + $time = explode(' ', microtime()); + $time = $time[1] + $time[0]; + $err = array( + 'code' => $code, + 'params' => $params, + 'package' => $this->_package, + 'level' => $level, + 'time' => $time, + 'context' => $context, + 'message' => $msg, + ); + + if ($repackage) { + $err['repackage'] = $repackage; + } + + // set up the error message, if necessary + if ($this->_msgCallback) { + $msg = call_user_func_array($this->_msgCallback, + array(&$this, $err)); + $err['message'] = $msg; + } + $push = $log = true; + $die = false; + // try the overriding callback first + $callback = $this->staticPopCallback(); + if ($callback) { + $this->staticPushCallback($callback); + } + if (!is_callable($callback)) { + // try the local callback next + $callback = $this->popCallback(); + if (is_callable($callback)) { + $this->pushCallback($callback); + } else { + // try the default callback + $callback = isset($GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package]) ? + $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package] : + $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK']['*']; + } + } + if (is_callable($callback)) { + switch(call_user_func($callback, $err)){ + case PEAR_ERRORSTACK_IGNORE: + return $err; + break; + case PEAR_ERRORSTACK_PUSH: + $log = false; + break; + case PEAR_ERRORSTACK_LOG: + $push = false; + break; + case PEAR_ERRORSTACK_DIE: + $die = true; + break; + // anything else returned has the same effect as pushandlog + } + } + if ($push) { + array_unshift($this->_errors, $err); + if (!isset($this->_errorsByLevel[$err['level']])) { + $this->_errorsByLevel[$err['level']] = array(); + } + $this->_errorsByLevel[$err['level']][] = &$this->_errors[0]; + } + if ($log) { + if ($this->_logger || $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']) { + $this->_log($err); + } + } + if ($die) { + die(); + } + if ($this->_compat && $push) { + return $this->raiseError($msg, $code, null, null, $err); + } + return $err; + } + + /** + * Static version of {@link push()} + * + * @param string $package Package name this error belongs to + * @param int $code Package-specific error code + * @param string $level Error level. This is NOT spell-checked + * @param array $params associative array of error parameters + * @param string $msg Error message, or a portion of it if the message + * is to be generated + * @param array $repackage If this error re-packages an error pushed by + * another package, place the array returned from + * {@link pop()} in this parameter + * @param array $backtrace Protected parameter: use this to pass in the + * {@link debug_backtrace()} that should be used + * to find error context + * @return PEAR_Error|array if compatibility mode is on, a PEAR_Error is also + * thrown. see docs for {@link push()} + * @static + */ + function staticPush($package, $code, $level = 'error', $params = array(), + $msg = false, $repackage = false, $backtrace = false) + { + $s = &PEAR_ErrorStack::singleton($package); + if ($s->_contextCallback) { + if (!$backtrace) { + if (function_exists('debug_backtrace')) { + $backtrace = debug_backtrace(); + } + } + } + return $s->push($code, $level, $params, $msg, $repackage, $backtrace); + } + + /** + * Log an error using PEAR::Log + * @param array $err Error array + * @param array $levels Error level => Log constant map + * @access protected + */ + function _log($err) + { + if ($this->_logger) { + $logger = &$this->_logger; + } else { + $logger = &$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']; + } + if (is_a($logger, 'Log')) { + $levels = array( + 'exception' => PEAR_LOG_CRIT, + 'alert' => PEAR_LOG_ALERT, + 'critical' => PEAR_LOG_CRIT, + 'error' => PEAR_LOG_ERR, + 'warning' => PEAR_LOG_WARNING, + 'notice' => PEAR_LOG_NOTICE, + 'info' => PEAR_LOG_INFO, + 'debug' => PEAR_LOG_DEBUG); + if (isset($levels[$err['level']])) { + $level = $levels[$err['level']]; + } else { + $level = PEAR_LOG_INFO; + } + $logger->log($err['message'], $level, $err); + } else { // support non-standard logs + call_user_func($logger, $err); + } + } + + + /** + * Pop an error off of the error stack + * + * @return false|array + * @since 0.4alpha it is no longer possible to specify a specific error + * level to return - the last error pushed will be returned, instead + */ + function pop() + { + $err = @array_shift($this->_errors); + if (!is_null($err)) { + @array_pop($this->_errorsByLevel[$err['level']]); + if (!count($this->_errorsByLevel[$err['level']])) { + unset($this->_errorsByLevel[$err['level']]); + } + } + return $err; + } + + /** + * Pop an error off of the error stack, static method + * + * @param string package name + * @return boolean + * @since PEAR1.5.0a1 + */ + function staticPop($package) + { + if ($package) { + if (!isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) { + return false; + } + return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->pop(); + } + } + + /** + * Determine whether there are any errors on the stack + * @param string|array Level name. Use to determine if any errors + * of level (string), or levels (array) have been pushed + * @return boolean + */ + function hasErrors($level = false) + { + if ($level) { + return isset($this->_errorsByLevel[$level]); + } + return count($this->_errors); + } + + /** + * Retrieve all errors since last purge + * + * @param boolean set in order to empty the error stack + * @param string level name, to return only errors of a particular severity + * @return array + */ + function getErrors($purge = false, $level = false) + { + if (!$purge) { + if ($level) { + if (!isset($this->_errorsByLevel[$level])) { + return array(); + } else { + return $this->_errorsByLevel[$level]; + } + } else { + return $this->_errors; + } + } + if ($level) { + $ret = $this->_errorsByLevel[$level]; + foreach ($this->_errorsByLevel[$level] as $i => $unused) { + // entries are references to the $_errors array + $this->_errorsByLevel[$level][$i] = false; + } + // array_filter removes all entries === false + $this->_errors = array_filter($this->_errors); + unset($this->_errorsByLevel[$level]); + return $ret; + } + $ret = $this->_errors; + $this->_errors = array(); + $this->_errorsByLevel = array(); + return $ret; + } + + /** + * Determine whether there are any errors on a single error stack, or on any error stack + * + * The optional parameter can be used to test the existence of any errors without the need of + * singleton instantiation + * @param string|false Package name to check for errors + * @param string Level name to check for a particular severity + * @return boolean + * @static + */ + function staticHasErrors($package = false, $level = false) + { + if ($package) { + if (!isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) { + return false; + } + return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->hasErrors($level); + } + foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) { + if ($obj->hasErrors($level)) { + return true; + } + } + return false; + } + + /** + * Get a list of all errors since last purge, organized by package + * @since PEAR 1.4.0dev BC break! $level is now in the place $merge used to be + * @param boolean $purge Set to purge the error stack of existing errors + * @param string $level Set to a level name in order to retrieve only errors of a particular level + * @param boolean $merge Set to return a flat array, not organized by package + * @param array $sortfunc Function used to sort a merged array - default + * sorts by time, and should be good for most cases + * @static + * @return array + */ + function staticGetErrors($purge = false, $level = false, $merge = false, + $sortfunc = array('PEAR_ErrorStack', '_sortErrors')) + { + $ret = array(); + if (!is_callable($sortfunc)) { + $sortfunc = array('PEAR_ErrorStack', '_sortErrors'); + } + foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) { + $test = $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->getErrors($purge, $level); + if ($test) { + if ($merge) { + $ret = array_merge($ret, $test); + } else { + $ret[$package] = $test; + } + } + } + if ($merge) { + usort($ret, $sortfunc); + } + return $ret; + } + + /** + * Error sorting function, sorts by time + * @access private + */ + function _sortErrors($a, $b) + { + if ($a['time'] == $b['time']) { + return 0; + } + if ($a['time'] < $b['time']) { + return 1; + } + return -1; + } + + /** + * Standard file/line number/function/class context callback + * + * This function uses a backtrace generated from {@link debug_backtrace()} + * and so will not work at all in PHP < 4.3.0. The frame should + * reference the frame that contains the source of the error. + * @return array|false either array('file' => file, 'line' => line, + * 'function' => function name, 'class' => class name) or + * if this doesn't work, then false + * @param unused + * @param integer backtrace frame. + * @param array Results of debug_backtrace() + * @static + */ + function getFileLine($code, $params, $backtrace = null) + { + if ($backtrace === null) { + return false; + } + $frame = 0; + $functionframe = 1; + if (!isset($backtrace[1])) { + $functionframe = 0; + } else { + while (isset($backtrace[$functionframe]['function']) && + $backtrace[$functionframe]['function'] == 'eval' && + isset($backtrace[$functionframe + 1])) { + $functionframe++; + } + } + if (isset($backtrace[$frame])) { + if (!isset($backtrace[$frame]['file'])) { + $frame++; + } + $funcbacktrace = $backtrace[$functionframe]; + $filebacktrace = $backtrace[$frame]; + $ret = array('file' => $filebacktrace['file'], + 'line' => $filebacktrace['line']); + // rearrange for eval'd code or create function errors + if (strpos($filebacktrace['file'], '(') && + preg_match(';^(.*?)\((\d+)\) : (.*?)\\z;', $filebacktrace['file'], + $matches)) { + $ret['file'] = $matches[1]; + $ret['line'] = $matches[2] + 0; + } + if (isset($funcbacktrace['function']) && isset($backtrace[1])) { + if ($funcbacktrace['function'] != 'eval') { + if ($funcbacktrace['function'] == '__lambda_func') { + $ret['function'] = 'create_function() code'; + } else { + $ret['function'] = $funcbacktrace['function']; + } + } + } + if (isset($funcbacktrace['class']) && isset($backtrace[1])) { + $ret['class'] = $funcbacktrace['class']; + } + return $ret; + } + return false; + } + + /** + * Standard error message generation callback + * + * This method may also be called by a custom error message generator + * to fill in template values from the params array, simply + * set the third parameter to the error message template string to use + * + * The special variable %__msg% is reserved: use it only to specify + * where a message passed in by the user should be placed in the template, + * like so: + * + * Error message: %msg% - internal error + * + * If the message passed like so: + * + * + * $stack->push(ERROR_CODE, 'error', array(), 'server error 500'); + * + * + * The returned error message will be "Error message: server error 500 - + * internal error" + * @param PEAR_ErrorStack + * @param array + * @param string|false Pre-generated error message template + * @static + * @return string + */ + function getErrorMessage(&$stack, $err, $template = false) + { + if ($template) { + $mainmsg = $template; + } else { + $mainmsg = $stack->getErrorMessageTemplate($err['code']); + } + $mainmsg = str_replace('%__msg%', $err['message'], $mainmsg); + if (is_array($err['params']) && count($err['params'])) { + foreach ($err['params'] as $name => $val) { + if (is_array($val)) { + // @ is needed in case $val is a multi-dimensional array + $val = @implode(', ', $val); + } + if (is_object($val)) { + if (method_exists($val, '__toString')) { + $val = $val->__toString(); + } else { + PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_OBJTOSTRING, + 'warning', array('obj' => get_class($val)), + 'object %obj% passed into getErrorMessage, but has no __toString() method'); + $val = 'Object'; + } + } + $mainmsg = str_replace('%' . $name . '%', $val, $mainmsg); + } + } + return $mainmsg; + } + + /** + * Standard Error Message Template generator from code + * @return string + */ + function getErrorMessageTemplate($code) + { + if (!isset($this->_errorMsgs[$code])) { + return '%__msg%'; + } + return $this->_errorMsgs[$code]; + } + + /** + * Set the Error Message Template array + * + * The array format must be: + *
    +     * array(error code => 'message template',...)
    +     * 
    + * + * Error message parameters passed into {@link push()} will be used as input + * for the error message. If the template is 'message %foo% was %bar%', and the + * parameters are array('foo' => 'one', 'bar' => 'six'), the error message returned will + * be 'message one was six' + * @return string + */ + function setErrorMessageTemplate($template) + { + $this->_errorMsgs = $template; + } + + + /** + * emulate PEAR::raiseError() + * + * @return PEAR_Error + */ + function raiseError() + { + require_once 'PEAR.php'; + $args = func_get_args(); + return call_user_func_array(array('PEAR', 'raiseError'), $args); + } +} +$stack = &PEAR_ErrorStack::singleton('PEAR_ErrorStack'); +$stack->pushCallback(array('PEAR_ErrorStack', '_handleError')); +?> diff --git a/includes/pear/PEAR/ErrorStack5.php b/includes/pear/PEAR/ErrorStack5.php new file mode 100644 index 0000000..52714d5 --- /dev/null +++ b/includes/pear/PEAR/ErrorStack5.php @@ -0,0 +1,921 @@ + + * @version 0.1alpha (E_STRICT) + * @package PEAR_ErrorStack + * @category Debugging + * @license http://opensource.org/licenses/bsd-license.php New BSD License + */ + +/** + * PEAR_Exception is used by default + */ +require_once 'PEAR/Exception.php'; + +class PEAR_ErrorStack_Exception extends PEAR_Exception{} + +/**#@+ + * One of four possible return values from the error Callback + * @see PEAR_ErrorStack::_errorCallback() + */ +/** + * If this is returned, then the error will be both pushed onto the stack + * and logged. + */ +define('PEAR_ERRORSTACK_PUSHANDLOG', 1); +/** + * If this is returned, then the error will only be pushed onto the stack, + * and not logged. + */ +define('PEAR_ERRORSTACK_PUSH', 2); +/** + * If this is returned, then the error will only be logged, but not pushed + * onto the error stack. + */ +define('PEAR_ERRORSTACK_LOG', 3); +/** + * If this is returned, then the error is completely ignored. + */ +define('PEAR_ERRORSTACK_IGNORE', 4); +/**#@-*/ + +/** + * Error code for an attempt to instantiate a non-class as a PEAR_ErrorStack in + * the singleton method. + */ +define('PEAR_ERRORSTACK_ERR_NONCLASS', 1); + +/** + * Error code for an attempt to pass an object into {@link PEAR_ErrorStack::getMessage()} + * that has no __toString() method + */ +define('PEAR_ERRORSTACK_ERR_OBJTOSTRING', 2); +/** + * Error Stack Implementation + * + * Usage: + * + * // global error stack + * $global_stack = &PEAR_ErrorStack::singleton('MyPackage'); + * // local error stack + * $local_stack = new PEAR_ErrorStack('MyPackage'); + * + * @copyright 2004 Gregory Beaver + * @package PEAR_ErrorStack + * @license http://opensource.org/licenses/bsd-license.php New BSD License + */ +class PEAR_ErrorStack { + /** + * Singleton storage + * + * Format: + *
    +     * array(
    +     *  'package1' => PEAR_ErrorStack object,
    +     *  'package2' => PEAR_ErrorStack object,
    +     *  ...
    +     * )
    +     * 
    + */ + static protected $singleton; + + /** + * Global error callback (default) + * + * This is only used if set to non-false. * is the default callback for + * all packages, whereas specific packages may set a default callback + * for all instances, regardless of whether they are a singleton or not. + * + * To exclude non-singletons, only set the local callback for the singleton + * @see PEAR_ErrorStack::setDefaultCallback() + */ + static protected $globalcallback = + array( + '*' => false, + ); + + /** + * Global Log object (default) + * + * This is only used if set to non-false. Use to set a default log object for + * all stacks, regardless of instantiation order or location + * @see PEAR_ErrorStack::setDefaultLogger() + */ + static protected $globallogger = false; + + /** + * PEAR_Warning callback + * + * This is only used if set to non-false. Use to set a default log object for + * all stacks, regardless of instantiation order or location + * @see PEAR_ErrorStack::setDefaultLogger() + */ + static protected $pearwarningcallback = false; + + /** + * Global Overriding Callback + * + * This callback will override any error callbacks that specific loggers have set. + * Use with EXTREME caution + * @see PEAR_ErrorStack::staticPushCallback() + * @access private + * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] + */ + static protected $overridecallback = array(); + + /** + * Errors are stored in the order that they are pushed on the stack. + * @since 0.4alpha Errors are no longer organized by error level. + * This renders pop() nearly unusable, and levels could be more easily + * handled in a callback anyway + * @var array + */ + private $_errors = array(); + + /** + * Storage of errors by level. + * + * Allows easy retrieval and deletion of only errors from a particular level + * @since PEAR 1.4.0dev + * @var array + */ + private $_errorsByLevel = array(); + + /** + * Package name this error stack represents + * @var string + */ + protected $_package; + + /** + * Determines whether a PEAR_Error is thrown upon every error addition + * @var boolean + */ + private $_compat = false; + + /** + * If set to a valid callback, this will be used to generate the error + * message from the error code, otherwise the message passed in will be + * used + * @var false|string|array + */ + private $_msgCallback = false; + + /** + * If set to a valid callback, this will be used to generate the error + * context for an error. For PHP-related errors, this will be a file + * and line number as retrieved from debug_backtrace(), but can be + * customized for other purposes. The error might actually be in a separate + * configuration file, or in a database query. + * @var false|string|array + */ + public $contextCallback = false; + + /** + * If set to a valid callback, this will be called every time an error + * is pushed onto the stack. The return value will be used to determine + * whether to allow an error to be pushed or logged. + * + * The return value must be one an PEAR_ERRORSTACK_* constant + * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG + * @var false|string|array + */ + protected $_errorCallback = array(); + + /** + * PEAR::Log object for logging errors + * @var false|Log + */ + protected $_logger = false; + + /** + * Error messages - designed to be overridden + * @var array + * @abstract + */ + protected $_errorMsgs = array(); + + /** + * Set up a new error stack + * + * @param string $package name of the package this error stack represents + * @param callback $msgCallback callback used for error message generation + * @param callback $contextCallback callback used for context generation, + * defaults to {@link getFileLine()} + * @param boolean $throwPEAR_Error (ignored) + */ + public function PEAR_ErrorStack($package, $msgCallback = false, $contextCallback = false, + $throwPEAR_Error = false) + { + $this->_package = $package; + $this->setMessageCallback($msgCallback); + $this->setContextCallback($contextCallback); + $this->_compat = false; + } + + /** + * Return a single error stack for this package. + * + * Note that all parameters are ignored if the stack for package $package + * has already been instantiated + * @param string $package name of the package this error stack represents + * @param callback $msgCallback callback used for error message generation + * @param callback $contextCallback callback used for context generation, + * defaults to {@link getFileLine()} + * @param boolean $throwPEAR_Error + * @param string $exceptionClass exception class to instantiate if + * in PHP 5 + * @param string $stackClass class to instantiate + * @static + * @return PEAR_ErrorStack + */ + static public function singleton($package, $msgCallback = false, $contextCallback = false, + $throwPEAR_Error = false, $stackClass = 'PEAR_ErrorStack') + { + if (isset(self::$singleton[$package])) { + return self::$singleton[$package]; + } + if (!class_exists($stackClass)) { + throw PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_NONCLASS, + 'exception', array('stackclass' => $stackClass), + 'stack class "%stackclass%" is not a valid class name (should be like PEAR_ErrorStack)', + false, $trace); + } + return self::$singleton[$package] = + new $stackClass($package, $msgCallback, $contextCallback, $throwPEAR_Error); + } + + + /** + * Set up a PEAR::Log object for all error stacks that don't have one + * @param Log $log + * @static + */ + static public function setDefaultLogger($log) + { + self::$globallogger = $log; + } + + /** + * Set up a PEAR::Log object for this error stack + * @param Log $log + */ + public function setLogger($log) + { + $this->_logger = $log; + } + + /** + * Set an error code => error message mapping callback + * + * This method sets the callback that can be used to generate error + * messages for any instance + * @param array|string Callback function/method + */ + public function setMessageCallback($msgCallback) + { + if (!$msgCallback) { + $this->_msgCallback = array($this, 'getErrorMessage'); + } else { + if (is_callable($msgCallback)) { + $this->_msgCallback = $msgCallback; + } + } + } + + /** + * Get an error code => error message mapping callback + * + * This method returns the current callback that can be used to generate error + * messages + * @return array|string|false Callback function/method or false if none + */ + public function getMessageCallback() + { + return $this->_msgCallback; + } + + /** + * Sets a default callback to be used by all error stacks + * + * This method sets the callback that can be used to generate error + * messages for a singleton + * @param array|string Callback function/method + * @param string Package name, or false for all packages + */ + static public function setDefaultCallback($callback = false, $package = false) + { + if (!is_callable($callback)) { + $callback = false; + } + $package = $package ? $package : '*'; + self::$globalcallback[$package] = $callback; + } + + /** + * Do not use + * @access private + */ + static public function setPEARWarningCallback($callback) + { + self::$pearwarningcallback = $callback; + } + + /** + * Set an error code => error message mapping callback + * + * This method sets the callback that can be used to generate context + * information for an error. Passing in NULL will disable context generation + * and remove the expensive call to debug_backtrace() + * @param array|string Callback function/method + */ + public function setContextCallback($contextCallback) + { + if ($contextCallback === null) { + return $this->contextCallback = false; + } + if (!$contextCallback) { + $this->contextCallback = array($this, 'getFileLine'); + } else { + if (is_callable($contextCallback)) { + $this->contextCallback = $contextCallback; + } + } + } + + /** + * Set an error Callback + * If set to a valid callback, this will be called every time an error + * is pushed onto the stack. The return value will be used to determine + * whether to allow an error to be pushed or logged. + * + * The return value must be one of the ERRORSTACK_* constants. + * + * This functionality can be used to emulate PEAR's pushErrorHandling, and + * the PEAR_ERROR_CALLBACK mode, without affecting the integrity of + * the error stack or logging + * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG + * @see popCallback() + * @param string|array $cb + */ + public function pushCallback($cb) + { + array_push($this->_errorCallback, $cb); + } + + /** + * Remove a callback from the error callback stack + * @see pushCallback() + * @return array|string|false + */ + public function popCallback() + { + if (!count($this->_errorCallback)) { + return false; + } + return array_pop($this->_errorCallback); + } + + /** + * Set a temporary overriding error callback for every package error stack + * + * Use this to temporarily disable all existing callbacks (can be used + * to emulate the @ operator, for instance) + * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG + * @see staticPopCallback(), pushCallback() + * @param string|array $cb + */ + static public function staticPushCallback($cb) + { + array_push(self::$overridecallback, $cb); + } + + /** + * Remove a temporary overriding error callback + * @see staticPushCallback() + * @return array|string|false + */ + static public function staticPopCallback() + { + $ret = array_pop(self::$overridecallback); + if (!is_array(self::$overridecallback)) { + self::$overridecallback = array(); + } + return $ret; + } + + /** + * demote an exception to a non-fatal error (default is warning) + * @param Exception + * @param string + */ + public function demoteException($e, $level = 'warning') + { + $this->push(get_class($e), $level, array(), + $e->getMessage(), $e, $e->getTrace()); + } + + /** + * promote a warning into a PEAR_Exception + * + * It is probably best to do this manually, to take advantage of the + * use of exception classnames to categorize errors + * @return PEAR_Exception + */ + public function promoteWarning($warning, $exceptionclass = 'PEAR_Exception') + { + return new $exceptionclass($warning['message'], + array($warning), $warning['code']); + } + + /** + * Add an error to the stack + * + * If the message generator exists, it is called with 2 parameters. + * - the current Error Stack object + * - an array that is in the same format as an error. Available indices + * are 'code', 'package', 'time', 'params', 'level', and 'context' + * + * Next, if the error should contain context information, this is + * handled by the context grabbing method. + * Finally, the error is pushed onto the proper error stack + * @param int $code Package-specific error code + * @param string $level Error level. This is NOT spell-checked + * @param array $params associative array of error parameters + * @param string $msg Error message, or a portion of it if the message + * is to be generated + * @param array $repackage If this error re-packages an error pushed by + * another package, place the array returned from + * {@link pop()} in this parameter + * @param array $backtrace Protected parameter: use this to pass in the + * {@link debug_backtrace()} that should be used + * to find error context + * @return PEAR_Error|array|Exception + * if compatibility mode is on, a PEAR_Error is also + * thrown. If the class Exception exists, then one + * is returned to allow code like: + * + * throw ($stack->push(MY_ERROR_CODE, 'error', array('username' => 'grob'))); + * + * + * The errorData property of the exception class will be set to the array + * that would normally be returned. If a PEAR_Error is returned, the userinfo + * property is set to the array + * + * Otherwise, an array is returned in this format: + * + * array( + * 'code' => $code, + * 'params' => $params, + * 'package' => $this->_package, + * 'level' => $level, + * 'time' => time(), + * 'context' => $context, + * 'message' => $msg, + * //['repackage' => $err] repackaged error array + * ); + * + */ + public function push($code, $level = 'error', $params = array(), $msg = false, + $repackage = false, $backtrace = false) + { + $context = false; + // grab error context + if ($this->contextCallback) { + if (!$backtrace) { + $backtrace = debug_backtrace(); + } + $context = call_user_func($this->contextCallback, $code, $params, $backtrace); + } + + // save error + $time = explode(' ', microtime()); + $time = $time[1] + $time[0]; + $err = array( + 'code' => $code, + 'params' => $params, + 'package' => $this->_package, + 'level' => $level, + 'time' => $time, + 'context' => $context, + 'message' => $msg, + ); + + // set up the error message, if necessary + if ($this->_msgCallback) { + $msg = call_user_func_array($this->_msgCallback, + array($this, $err)); + $err['message'] = $msg; + } + + + if ($repackage) { + $err['repackage'] = $repackage; + } + $push = $log = true; + // try the overriding callback first + $callback = $this->staticPopCallback(); + if ($callback) { + $this->staticPushCallback($callback); + } + if (!is_callable($callback)) { + // try the local callback next + $callback = $this->popCallback(); + if (is_callable($callback)) { + $this->pushCallback($callback); + } else { + // try the default callback + $callback = isset(self::$globalcallback[$this->_package]) ? + self::$globalcallback[$this->_package] : + self::$globalcallback['*']; + } + } + if (is_callable($callback)) { + switch(call_user_func($callback, $err)){ + case PEAR_ERRORSTACK_IGNORE: + return $err; + break; + case PEAR_ERRORSTACK_PUSH: + $log = false; + break; + case PEAR_ERRORSTACK_LOG: + $push = false; + break; + // anything else returned has the same effect as pushandlog + } + } + if ($push) { + if (is_callable(self::$pearwarningcallback)) { + call_user_func(self::$pearwarningcallback, $err); + } + array_unshift($this->_errors, $err); + $this->_errorsByLevel[$err['level']][] = &$this->_errors[0]; + } + if ($log) { + if ($this->_logger || self::$globallogger) { + $this->_log($err); + } + } + return $err; + } + + /** + * Static version of {@link push()} + * + * @param string $package Package name this error belongs to + * @param int $code Package-specific error code + * @param string $level Error level. This is NOT spell-checked + * @param array $params associative array of error parameters + * @param string $msg Error message, or a portion of it if the message + * is to be generated + * @param array $repackage If this error re-packages an error pushed by + * another package, place the array returned from + * {@link pop()} in this parameter + * @param array $backtrace Protected parameter: use this to pass in the + * {@link debug_backtrace()} that should be used + * to find error context + * @return PEAR_Error|null|Exception + * if compatibility mode is on, a PEAR_Error is also + * thrown. If the class Exception exists, then one + * is returned to allow code like: + * + * throw ($stack->push(MY_ERROR_CODE, 'error', array('username' => 'grob'))); + * + * @static + */ + static public function staticPush($package, $code, $level = 'error', $params = array(), + $msg = false, $repackage = false, $backtrace = false) + { + $s = PEAR_ErrorStack::singleton($package); + if ($s->contextCallback) { + if (!$backtrace) { + $backtrace = debug_backtrace(); + } + } + return $s->push($code, $level, $params, $msg, $repackage, $backtrace); + } + + /** + * Log an error using PEAR::Log + * @param array $err Error array + * @param array $levels Error level => Log constant map + * @access protected + */ + protected function _log($err, $levels = array( + 'exception' => PEAR_LOG_CRIT, + 'alert' => PEAR_LOG_ALERT, + 'critical' => PEAR_LOG_CRIT, + 'error' => PEAR_LOG_ERR, + 'warning' => PEAR_LOG_WARNING, + 'notice' => PEAR_LOG_NOTICE, + 'info' => PEAR_LOG_INFO, + 'debug' => PEAR_LOG_DEBUG)) + { + if (isset($levels[$err['level']])) { + $level = $levels[$err['level']]; + } else { + $level = PEAR_LOG_INFO; + } + if ($this->_logger) { + $this->_logger->log($err['message'], $level, $err); + } else { + self::$globallogger->log($err['message'], $level, $err); + } + } + + + /** + * Pop an error off of the error stack + * + * @return false|array + * @since 0.4alpha it is no longer possible to specify a specific error + * level to return - the last error pushed will be returned, instead + */ + public function pop() + { + return @array_shift($this->_errors); + } + + /** + * Determine whether there are any errors on the stack + * @param string|array Level name. Use to determine if any errors + * of level (string), or levels (array) have been pushed + * @return boolean + */ + public function hasErrors($level = false) + { + if ($level) { + return isset($this->_errorsByLevel[$level]); + } + return count($this->_errors); + } + + /** + * Retrieve all errors since last purge + * + * @param boolean set in order to empty the error stack + * @param string level name, to return only errors of a particular severity + * @return array + */ + public function getErrors($purge = false, $level = false) + { + if (!$purge) { + if ($level) { + if (!isset($this->_errorsByLevel[$level])) { + return array(); + } else { + return $this->_errorsByLevel[$level]; + } + } else { + return $this->_errors; + } + } + if ($level) { + $ret = $this->_errorsByLevel[$level]; + foreach ($this->_errorsByLevel[$level] as $i => $unused) { + // entries are references to the $_errors array + $this->_errorsByLevel[$level][$i] = false; + } + // array_filter removes all entries === false + $this->_errors = array_filter($this->_errors); + unset($this->_errorsByLevel[$level]); + return $ret; + } + $ret = $this->_errors; + $this->_errors = array(); + $this->_errorsByLevel = array(); + return $ret; + } + + /** + * Determine whether there are any errors on a single error stack, or on any error stack + * + * The optional parameter can be used to test the existence of any errors without the need of + * singleton instantiation + * @param string|false Package name to check for errors + * @param string Level name to check for a particular severity + * @return boolean + */ + static public function staticHasErrors($package = false, $level = false) + { + if ($package) { + if (!isset(self::$singleton[$package])) { + return false; + } + return self::$singleton[$package]->hasErrors($level); + } + foreach (self::$singleton as $package => $obj) { + if ($obj->hasErrors($level)) { + return true; + } + } + return false; + } + + /** + * Get a list of all errors since last purge, organized by package + * @since PEAR 1.4.0dev BC break! $level is now in the place $merge used to be + * @param boolean $clearStack Set to purge the error stack of existing errors + * @param string $level Set to a level name in order to retrieve only errors of a particular level + * @param boolean $merge Set to return a flat array, not organized by package + * @param array $sortfunc Function used to sort a merged array - default + * sorts by time, and should be good for most cases + * @return array + */ + static public function staticGetErrors($purge = false, $level = false, $merge = false, $sortfunc = array('PEAR_ErrorStack', '_sortErrors')) + { + $ret = array(); + if (!is_callable($sortfunc)) { + $sortfunc = array('PEAR_ErrorStack', '_sortErrors'); + } + foreach (self::$singleton as $package => $obj) { + $test = self::$singleton[$package]->getErrors($purge, $level); + if ($test) { + if ($merge) { + $ret = array_merge($ret, $test); + } else { + $ret[$package] = $test; + } + } + } + if ($merge) { + usort($ret, $sortfunc); + } + return $ret; + } + + /** + * Error sorting function, sorts by time + * @access private + */ + function _sortErrors($a, $b) + { + if ($a['time'] == $b['time']) { + return 0; + } + if ($a['time'] < $b['time']) { + return 1; + } + return -1; + } + + /** + * Standard file/line number/function/class context callback + * + * This function uses a backtrace generated from {@link debug_backtrace()} + * and so will not work at all in PHP < 4.3.0. The frame should + * reference the frame that contains the source of the error. + * @return array|false either array('file' => file, 'line' => line, + * 'function' => function name, 'class' => class name) or + * if this doesn't work, then false + * @param unused + * @param integer backtrace frame. + * @param array Results of debug_backtrace() + */ + static public function getFileLine($code, $params, $backtrace = null) + { + if ($backtrace === null) { + return false; + } + $frame = 0; + $functionframe = 1; + if (!isset($backtrace[1])) { + $functionframe = 0; + } else { + while (isset($backtrace[$functionframe]['function']) && + $backtrace[$functionframe]['function'] == 'eval' && + isset($backtrace[$functionframe + 1])) { + $functionframe++; + } + } + if (isset($backtrace[$frame])) { + if (!isset($backtrace[$frame]['file'])) { + $frame++; + } + $funcbacktrace = $backtrace[$functionframe]; + $filebacktrace = $backtrace[$frame]; + $ret = array('file' => $filebacktrace['file'], + 'line' => $filebacktrace['line']); + // rearrange for eval'd code or create function errors + if (strpos($filebacktrace['file'], '(') && + preg_match(';^(.*?)\((\d+)\) : (.*?)\\z;', $filebacktrace['file'], + $matches)) { + $ret['file'] = $matches[1]; + $ret['line'] = $matches[2] + 0; + } + if (isset($funcbacktrace['function']) && isset($backtrace[1])) { + if ($funcbacktrace['function'] != 'eval') { + if ($funcbacktrace['function'] == '__lambda_func') { + $ret['function'] = 'create_function() code'; + } else { + $ret['function'] = $funcbacktrace['function']; + } + } + } + if (isset($funcbacktrace['class']) && isset($backtrace[1])) { + $ret['class'] = $funcbacktrace['class']; + } + return $ret; + } + return false; + } + + /** + * Standard error message generation callback + * + * This method may also be called by a custom error message generator + * to fill in template values from the params array, simply + * set the third parameter to the error message template string to use + * + * The special variable %__msg% is reserved: use it only to specify + * where a message passed in by the user should be placed in the template, + * like so: + * + * Error message: %msg% - internal error + * + * If the message passed like so: + * + * + * $stack->push(ERROR_CODE, 'error', array(), 'server error 500'); + * + * + * The returned error message will be "Error message: server error 500 - + * internal error" + * @param PEAR_ErrorStack + * @param array + * @param string|false Pre-generated error message template + * @static + * @return string + */ + public function getErrorMessage(&$stack, $err, $template = false) + { + if ($template) { + $mainmsg = $template; + } else { + $mainmsg = $stack->getErrorMessageTemplate($err['code']); + } + $mainmsg = str_replace('%__msg%', $err['message'], $mainmsg); + if (count($err['params'])) { + foreach ($err['params'] as $name => $val) { + if (is_array($val)) { + // @ is needed in case $val is a multi-dimensional array + $val = @implode(', ', $val); + } + if (is_object($val)) { + if (method_exists($val, '__toString')) { + $val = $val->__toString(); + } else { + throw PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_OBJTOSTRING, + 'warning', array('obj' => get_class($val)), + 'object %obj% passed into getErrorMessage, but has no __toString() method'); + $val = 'Object'; + } + } + $mainmsg = str_replace('%' . $name . '%', $val, $mainmsg); + } + } + return $mainmsg; + } + + /** + * Standard Error Message Template generator from code + * @return string + */ + public function getErrorMessageTemplate($code) + { + if (!isset($this->_errorMsgs[$code])) { + return '%__msg%'; + } + return $this->_errorMsgs[$code]; + } + + /** + * Set the Error Message Template array + * + * The array format must be: + *
    +     * array(error code => 'message template',...)
    +     * 
    + * + * Error message parameters passed into {@link push()} will be used as input + * for the error message. If the template is 'message %foo% was %bar%', and the + * parameters are array('foo' => 'one', 'bar' => 'six'), the error message returned will + * be 'message one was six' + * @return string + */ + public function setErrorMessageTemplate($template) + { + $this->_errorMsgs = $template; + } +} +PEAR_ErrorStack::singleton('PEAR_ErrorStack', false, null, false, 'PEAR_ErrorStack_Exception'); +?> \ No newline at end of file diff --git a/includes/pear/PEAR/Exception.php b/includes/pear/PEAR/Exception.php new file mode 100644 index 0000000..85eaf2c --- /dev/null +++ b/includes/pear/PEAR/Exception.php @@ -0,0 +1,389 @@ + + * @author Hans Lellelid + * @author Bertrand Mansion + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR_Exception + * @since File available since Release 1.0.0 + */ + + +/** + * Base PEAR_Exception Class + * + * 1) Features: + * + * - Nestable exceptions (throw new PEAR_Exception($msg, $prev_exception)) + * - Definable triggers, shot when exceptions occur + * - Pretty and informative error messages + * - Added more context info available (like class, method or cause) + * - cause can be a PEAR_Exception or an array of mixed + * PEAR_Exceptions/PEAR_ErrorStack warnings + * - callbacks for specific exception classes and their children + * + * 2) Ideas: + * + * - Maybe a way to define a 'template' for the output + * + * 3) Inherited properties from PHP Exception Class: + * + * protected $message + * protected $code + * protected $line + * protected $file + * private $trace + * + * 4) Inherited methods from PHP Exception Class: + * + * __clone + * __construct + * getMessage + * getCode + * getFile + * getLine + * getTraceSafe + * getTraceSafeAsString + * __toString + * + * 5) Usage example + * + * + * require_once 'PEAR/Exception.php'; + * + * class Test { + * function foo() { + * throw new PEAR_Exception('Error Message', ERROR_CODE); + * } + * } + * + * function myLogger($pear_exception) { + * echo $pear_exception->getMessage(); + * } + * // each time a exception is thrown the 'myLogger' will be called + * // (its use is completely optional) + * PEAR_Exception::addObserver('myLogger'); + * $test = new Test; + * try { + * $test->foo(); + * } catch (PEAR_Exception $e) { + * print $e; + * } + * + * + * @category pear + * @package PEAR_Exception + * @author Tomas V.V.Cox + * @author Hans Lellelid + * @author Bertrand Mansion + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR_Exception + * @since Class available since Release 1.0.0 + * + */ +class PEAR_Exception extends Exception +{ + const OBSERVER_PRINT = -2; + const OBSERVER_TRIGGER = -4; + const OBSERVER_DIE = -8; + protected $cause; + private static $_observers = array(); + private static $_uniqueid = 0; + private $_trace; + + /** + * Supported signatures: + * - PEAR_Exception(string $message); + * - PEAR_Exception(string $message, int $code); + * - PEAR_Exception(string $message, Exception $cause); + * - PEAR_Exception(string $message, Exception $cause, int $code); + * - PEAR_Exception(string $message, PEAR_Error $cause); + * - PEAR_Exception(string $message, PEAR_Error $cause, int $code); + * - PEAR_Exception(string $message, array $causes); + * - PEAR_Exception(string $message, array $causes, int $code); + * @param string exception message + * @param int|Exception|PEAR_Error|array|null exception cause + * @param int|null exception code or null + */ + public function __construct($message, $p2 = null, $p3 = null) + { + if (is_int($p2)) { + $code = $p2; + $this->cause = null; + } elseif (is_object($p2) || is_array($p2)) { + // using is_object allows both Exception and PEAR_Error + if (is_object($p2) && !($p2 instanceof Exception)) { + if (!class_exists('PEAR_Error') || !($p2 instanceof PEAR_Error)) { + throw new PEAR_Exception('exception cause must be Exception, ' . + 'array, or PEAR_Error'); + } + } + $code = $p3; + if (is_array($p2) && isset($p2['message'])) { + // fix potential problem of passing in a single warning + $p2 = array($p2); + } + $this->cause = $p2; + } else { + $code = null; + $this->cause = null; + } + parent::__construct($message, $code); + $this->signal(); + } + + /** + * @param mixed $callback - A valid php callback, see php func is_callable() + * - A PEAR_Exception::OBSERVER_* constant + * - An array(const PEAR_Exception::OBSERVER_*, + * mixed $options) + * @param string $label The name of the observer. Use this if you want + * to remove it later with removeObserver() + */ + public static function addObserver($callback, $label = 'default') + { + self::$_observers[$label] = $callback; + } + + public static function removeObserver($label = 'default') + { + unset(self::$_observers[$label]); + } + + /** + * @return int unique identifier for an observer + */ + public static function getUniqueId() + { + return self::$_uniqueid++; + } + + private function signal() + { + foreach (self::$_observers as $func) { + if (is_callable($func)) { + call_user_func($func, $this); + continue; + } + settype($func, 'array'); + switch ($func[0]) { + case self::OBSERVER_PRINT : + $f = (isset($func[1])) ? $func[1] : '%s'; + printf($f, $this->getMessage()); + break; + case self::OBSERVER_TRIGGER : + $f = (isset($func[1])) ? $func[1] : E_USER_NOTICE; + trigger_error($this->getMessage(), $f); + break; + case self::OBSERVER_DIE : + $f = (isset($func[1])) ? $func[1] : '%s'; + die(printf($f, $this->getMessage())); + break; + default: + trigger_error('invalid observer type', E_USER_WARNING); + } + } + } + + /** + * Return specific error information that can be used for more detailed + * error messages or translation. + * + * This method may be overridden in child exception classes in order + * to add functionality not present in PEAR_Exception and is a placeholder + * to define API + * + * The returned array must be an associative array of parameter => value like so: + *
    +     * array('name' => $name, 'context' => array(...))
    +     * 
    + * @return array + */ + public function getErrorData() + { + return array(); + } + + /** + * Returns the exception that caused this exception to be thrown + * @access public + * @return Exception|array The context of the exception + */ + public function getCause() + { + return $this->cause; + } + + /** + * Function must be public to call on caused exceptions + * @param array + */ + public function getCauseMessage(&$causes) + { + $trace = $this->getTraceSafe(); + $cause = array('class' => get_class($this), + 'message' => $this->message, + 'file' => 'unknown', + 'line' => 'unknown'); + if (isset($trace[0])) { + if (isset($trace[0]['file'])) { + $cause['file'] = $trace[0]['file']; + $cause['line'] = $trace[0]['line']; + } + } + $causes[] = $cause; + if ($this->cause instanceof PEAR_Exception) { + $this->cause->getCauseMessage($causes); + } elseif ($this->cause instanceof Exception) { + $causes[] = array('class' => get_class($this->cause), + 'message' => $this->cause->getMessage(), + 'file' => $this->cause->getFile(), + 'line' => $this->cause->getLine()); + } elseif (class_exists('PEAR_Error') && $this->cause instanceof PEAR_Error) { + $causes[] = array('class' => get_class($this->cause), + 'message' => $this->cause->getMessage(), + 'file' => 'unknown', + 'line' => 'unknown'); + } elseif (is_array($this->cause)) { + foreach ($this->cause as $cause) { + if ($cause instanceof PEAR_Exception) { + $cause->getCauseMessage($causes); + } elseif ($cause instanceof Exception) { + $causes[] = array('class' => get_class($cause), + 'message' => $cause->getMessage(), + 'file' => $cause->getFile(), + 'line' => $cause->getLine()); + } elseif (class_exists('PEAR_Error') && $cause instanceof PEAR_Error) { + $causes[] = array('class' => get_class($cause), + 'message' => $cause->getMessage(), + 'file' => 'unknown', + 'line' => 'unknown'); + } elseif (is_array($cause) && isset($cause['message'])) { + // PEAR_ErrorStack warning + $causes[] = array( + 'class' => $cause['package'], + 'message' => $cause['message'], + 'file' => isset($cause['context']['file']) ? + $cause['context']['file'] : + 'unknown', + 'line' => isset($cause['context']['line']) ? + $cause['context']['line'] : + 'unknown', + ); + } + } + } + } + + public function getTraceSafe() + { + if (!isset($this->_trace)) { + $this->_trace = $this->getTrace(); + if (empty($this->_trace)) { + $backtrace = debug_backtrace(); + $this->_trace = array($backtrace[count($backtrace)-1]); + } + } + return $this->_trace; + } + + public function getErrorClass() + { + $trace = $this->getTraceSafe(); + return $trace[0]['class']; + } + + public function getErrorMethod() + { + $trace = $this->getTraceSafe(); + return $trace[0]['function']; + } + + public function __toString() + { + if (isset($_SERVER['REQUEST_URI'])) { + return $this->toHtml(); + } + return $this->toText(); + } + + public function toHtml() + { + $trace = $this->getTraceSafe(); + $causes = array(); + $this->getCauseMessage($causes); + $html = '' . "\n"; + foreach ($causes as $i => $cause) { + $html .= '\n"; + } + $html .= '' . "\n" + . '' + . '' + . '' . "\n"; + + foreach ($trace as $k => $v) { + $html .= '' + . '' + . '' . "\n"; + } + $html .= '' + . '' + . '' . "\n" + . '
    ' + . str_repeat('-', $i) . ' ' . $cause['class'] . ': ' + . htmlspecialchars($cause['message']) . ' in ' . $cause['file'] . ' ' + . 'on line ' . $cause['line'] . '' + . "
    Exception trace
    #FunctionLocation
    ' . $k . ''; + if (!empty($v['class'])) { + $html .= $v['class'] . $v['type']; + } + $html .= $v['function']; + $args = array(); + if (!empty($v['args'])) { + foreach ($v['args'] as $arg) { + if (is_null($arg)) $args[] = 'null'; + elseif (is_array($arg)) $args[] = 'Array'; + elseif (is_object($arg)) $args[] = 'Object('.get_class($arg).')'; + elseif (is_bool($arg)) $args[] = $arg ? 'true' : 'false'; + elseif (is_int($arg) || is_double($arg)) $args[] = $arg; + else { + $arg = (string)$arg; + $str = htmlspecialchars(substr($arg, 0, 16)); + if (strlen($arg) > 16) $str .= '…'; + $args[] = "'" . $str . "'"; + } + } + } + $html .= '(' . implode(', ',$args) . ')' + . '' . (isset($v['file']) ? $v['file'] : 'unknown') + . ':' . (isset($v['line']) ? $v['line'] : 'unknown') + . '
    ' . ($k+1) . '{main} 
    '; + return $html; + } + + public function toText() + { + $causes = array(); + $this->getCauseMessage($causes); + $causeMsg = ''; + foreach ($causes as $i => $cause) { + $causeMsg .= str_repeat(' ', $i) . $cause['class'] . ': ' + . $cause['message'] . ' in ' . $cause['file'] + . ' on line ' . $cause['line'] . "\n"; + } + return $causeMsg . $this->getTraceAsString(); + } +} \ No newline at end of file diff --git a/includes/pear/PEAR/FixPHP5PEARWarnings.php b/includes/pear/PEAR/FixPHP5PEARWarnings.php new file mode 100644 index 0000000..be5dc3c --- /dev/null +++ b/includes/pear/PEAR/FixPHP5PEARWarnings.php @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/includes/pear/PEAR/Frontend.php b/includes/pear/PEAR/Frontend.php new file mode 100644 index 0000000..b7447b5 --- /dev/null +++ b/includes/pear/PEAR/Frontend.php @@ -0,0 +1,228 @@ + + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a1 + */ + +/** + * Include error handling + */ +//require_once 'PEAR.php'; + +/** + * Which user interface class is being used. + * @var string class name + */ +$GLOBALS['_PEAR_FRONTEND_CLASS'] = 'PEAR_Frontend_CLI'; + +/** + * Instance of $_PEAR_Command_uiclass. + * @var object + */ +$GLOBALS['_PEAR_FRONTEND_SINGLETON'] = null; + +/** + * Singleton-based frontend for PEAR user input/output + * + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a1 + */ +class PEAR_Frontend extends PEAR +{ + /** + * Retrieve the frontend object + * @return PEAR_Frontend_CLI|PEAR_Frontend_Web|PEAR_Frontend_Gtk + * @static + */ + function &singleton($type = null) + { + if ($type === null) { + if (!isset($GLOBALS['_PEAR_FRONTEND_SINGLETON'])) { + $a = false; + return $a; + } + return $GLOBALS['_PEAR_FRONTEND_SINGLETON']; + } + + $a = PEAR_Frontend::setFrontendClass($type); + return $a; + } + + /** + * Set the frontend class that will be used by calls to {@link singleton()} + * + * Frontends are expected to conform to the PEAR naming standard of + * _ => DIRECTORY_SEPARATOR (PEAR_Frontend_CLI is in PEAR/Frontend/CLI.php) + * @param string $uiclass full class name + * @return PEAR_Frontend + * @static + */ + function &setFrontendClass($uiclass) + { + if (is_object($GLOBALS['_PEAR_FRONTEND_SINGLETON']) && + is_a($GLOBALS['_PEAR_FRONTEND_SINGLETON'], $uiclass)) { + return $GLOBALS['_PEAR_FRONTEND_SINGLETON']; + } + + if (!class_exists($uiclass)) { + $file = str_replace('_', '/', $uiclass) . '.php'; + if (PEAR_Frontend::isIncludeable($file)) { + include_once $file; + } + } + + if (class_exists($uiclass)) { + $obj = &new $uiclass; + // quick test to see if this class implements a few of the most + // important frontend methods + if (is_a($obj, 'PEAR_Frontend')) { + $GLOBALS['_PEAR_FRONTEND_SINGLETON'] = &$obj; + $GLOBALS['_PEAR_FRONTEND_CLASS'] = $uiclass; + return $obj; + } + + $err = PEAR::raiseError("not a frontend class: $uiclass"); + return $err; + } + + $err = PEAR::raiseError("no such class: $uiclass"); + return $err; + } + + /** + * Set the frontend class that will be used by calls to {@link singleton()} + * + * Frontends are expected to be a descendant of PEAR_Frontend + * @param PEAR_Frontend + * @return PEAR_Frontend + * @static + */ + function &setFrontendObject($uiobject) + { + if (is_object($GLOBALS['_PEAR_FRONTEND_SINGLETON']) && + is_a($GLOBALS['_PEAR_FRONTEND_SINGLETON'], get_class($uiobject))) { + return $GLOBALS['_PEAR_FRONTEND_SINGLETON']; + } + + if (!is_a($uiobject, 'PEAR_Frontend')) { + $err = PEAR::raiseError('not a valid frontend class: (' . + get_class($uiobject) . ')'); + return $err; + } + + $GLOBALS['_PEAR_FRONTEND_SINGLETON'] = &$uiobject; + $GLOBALS['_PEAR_FRONTEND_CLASS'] = get_class($uiobject); + return $uiobject; + } + + /** + * @param string $path relative or absolute include path + * @return boolean + * @static + */ + function isIncludeable($path) + { + if (file_exists($path) && is_readable($path)) { + return true; + } + + $fp = @fopen($path, 'r', true); + if ($fp) { + fclose($fp); + return true; + } + + return false; + } + + /** + * @param PEAR_Config + */ + function setConfig(&$config) + { + } + + /** + * This can be overridden to allow session-based temporary file management + * + * By default, all files are deleted at the end of a session. The web installer + * needs to be able to sustain a list over many sessions in order to support + * user interaction with install scripts + */ + function addTempFile($file) + { + $GLOBALS['_PEAR_Common_tempfiles'][] = $file; + } + + /** + * Log an action + * + * @param string $msg the message to log + * @param boolean $append_crlf + * @return boolean true + * @abstract + */ + function log($msg, $append_crlf = true) + { + } + + /** + * Run a post-installation script + * + * @param array $scripts array of post-install scripts + * @abstract + */ + function runPostinstallScripts(&$scripts) + { + } + + /** + * Display human-friendly output formatted depending on the + * $command parameter. + * + * This should be able to handle basic output data with no command + * @param mixed $data data structure containing the information to display + * @param string $command command from which this method was called + * @abstract + */ + function outputData($data, $command = '_default') + { + } + + /** + * Display a modal form dialog and return the given input + * + * A frontend that requires multiple requests to retrieve and process + * data must take these needs into account, and implement the request + * handling code. + * @param string $command command from which this method was called + * @param array $prompts associative array. keys are the input field names + * and values are the description + * @param array $types array of input field types (text, password, + * etc.) keys have to be the same like in $prompts + * @param array $defaults array of default values. again keys have + * to be the same like in $prompts. Do not depend + * on a default value being set. + * @return array input sent by the user + * @abstract + */ + function userDialog($command, $prompts, $types = array(), $defaults = array()) + { + } +} \ No newline at end of file diff --git a/includes/pear/PEAR/Frontend/CLI.php b/includes/pear/PEAR/Frontend/CLI.php new file mode 100644 index 0000000..8c52e4e --- /dev/null +++ b/includes/pear/PEAR/Frontend/CLI.php @@ -0,0 +1,751 @@ + + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 0.1 + */ +/** + * base class + */ +require_once 'PEAR/Frontend.php'; + +/** + * Command-line Frontend for the PEAR Installer + * @category pear + * @package PEAR + * @author Stig Bakken + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 0.1 + */ +class PEAR_Frontend_CLI extends PEAR_Frontend +{ + /** + * What type of user interface this frontend is for. + * @var string + * @access public + */ + var $type = 'CLI'; + var $lp = ''; // line prefix + + var $params = array(); + var $term = array( + 'bold' => '', + 'normal' => '', + ); + + function PEAR_Frontend_CLI() + { + parent::PEAR(); + $term = getenv('TERM'); //(cox) $_ENV is empty for me in 4.1.1 + if (function_exists('posix_isatty') && !posix_isatty(1)) { + // output is being redirected to a file or through a pipe + } elseif ($term) { + if (preg_match('/^(xterm|vt220|linux)/', $term)) { + $this->term['bold'] = sprintf("%c%c%c%c", 27, 91, 49, 109); + $this->term['normal'] = sprintf("%c%c%c", 27, 91, 109); + } elseif (preg_match('/^vt100/', $term)) { + $this->term['bold'] = sprintf("%c%c%c%c%c%c", 27, 91, 49, 109, 0, 0); + $this->term['normal'] = sprintf("%c%c%c%c%c", 27, 91, 109, 0, 0); + } + } elseif (OS_WINDOWS) { + // XXX add ANSI codes here + } + } + + /** + * @param object PEAR_Error object + */ + function displayError($e) + { + return $this->_displayLine($e->getMessage()); + } + + /** + * @param object PEAR_Error object + */ + function displayFatalError($eobj) + { + $this->displayError($eobj); + if (class_exists('PEAR_Config')) { + $config = &PEAR_Config::singleton(); + if ($config->get('verbose') > 5) { + if (function_exists('debug_print_backtrace')) { + debug_print_backtrace(); + exit(1); + } + + $raised = false; + foreach (debug_backtrace() as $i => $frame) { + if (!$raised) { + if (isset($frame['class']) + && strtolower($frame['class']) == 'pear' + && strtolower($frame['function']) == 'raiseerror' + ) { + $raised = true; + } else { + continue; + } + } + + $frame['class'] = !isset($frame['class']) ? '' : $frame['class']; + $frame['type'] = !isset($frame['type']) ? '' : $frame['type']; + $frame['function'] = !isset($frame['function']) ? '' : $frame['function']; + $frame['line'] = !isset($frame['line']) ? '' : $frame['line']; + $this->_displayLine("#$i: $frame[class]$frame[type]$frame[function] $frame[line]"); + } + } + } + + exit(1); + } + + /** + * Instruct the runInstallScript method to skip a paramgroup that matches the + * id value passed in. + * + * This method is useful for dynamically configuring which sections of a post-install script + * will be run based on the user's setup, which is very useful for making flexible + * post-install scripts without losing the cross-Frontend ability to retrieve user input + * @param string + */ + function skipParamgroup($id) + { + $this->_skipSections[$id] = true; + } + + function runPostinstallScripts(&$scripts) + { + foreach ($scripts as $i => $script) { + $this->runInstallScript($scripts[$i]->_params, $scripts[$i]->_obj); + } + } + + /** + * @param array $xml contents of postinstallscript tag + * @param object $script post-installation script + * @param string install|upgrade + */ + function runInstallScript($xml, &$script) + { + $this->_skipSections = array(); + if (!is_array($xml) || !isset($xml['paramgroup'])) { + $script->run(array(), '_default'); + return; + } + + $completedPhases = array(); + if (!isset($xml['paramgroup'][0])) { + $xml['paramgroup'] = array($xml['paramgroup']); + } + + foreach ($xml['paramgroup'] as $group) { + if (isset($this->_skipSections[$group['id']])) { + // the post-install script chose to skip this section dynamically + continue; + } + + if (isset($group['name'])) { + $paramname = explode('::', $group['name']); + if ($lastgroup['id'] != $paramname[0]) { + continue; + } + + $group['name'] = $paramname[1]; + if (!isset($answers)) { + return; + } + + if (isset($answers[$group['name']])) { + switch ($group['conditiontype']) { + case '=' : + if ($answers[$group['name']] != $group['value']) { + continue 2; + } + break; + case '!=' : + if ($answers[$group['name']] == $group['value']) { + continue 2; + } + break; + case 'preg_match' : + if (!@preg_match('/' . $group['value'] . '/', + $answers[$group['name']])) { + continue 2; + } + break; + default : + return; + } + } + } + + $lastgroup = $group; + if (isset($group['instructions'])) { + $this->_display($group['instructions']); + } + + if (!isset($group['param'][0])) { + $group['param'] = array($group['param']); + } + + if (isset($group['param'])) { + if (method_exists($script, 'postProcessPrompts')) { + $prompts = $script->postProcessPrompts($group['param'], $group['id']); + if (!is_array($prompts) || count($prompts) != count($group['param'])) { + $this->outputData('postinstall', 'Error: post-install script did not ' . + 'return proper post-processed prompts'); + $prompts = $group['param']; + } else { + foreach ($prompts as $i => $var) { + if (!is_array($var) || !isset($var['prompt']) || + !isset($var['name']) || + ($var['name'] != $group['param'][$i]['name']) || + ($var['type'] != $group['param'][$i]['type']) + ) { + $this->outputData('postinstall', 'Error: post-install script ' . + 'modified the variables or prompts, severe security risk. ' . + 'Will instead use the defaults from the package.xml'); + $prompts = $group['param']; + } + } + } + + $answers = $this->confirmDialog($prompts); + } else { + $answers = $this->confirmDialog($group['param']); + } + } + + if ((isset($answers) && $answers) || !isset($group['param'])) { + if (!isset($answers)) { + $answers = array(); + } + + array_unshift($completedPhases, $group['id']); + if (!$script->run($answers, $group['id'])) { + $script->run($completedPhases, '_undoOnError'); + return; + } + } else { + $script->run($completedPhases, '_undoOnError'); + return; + } + } + } + + /** + * Ask for user input, confirm the answers and continue until the user is satisfied + * @param array an array of arrays, format array('name' => 'paramname', 'prompt' => + * 'text to display', 'type' => 'string'[, default => 'default value']) + * @return array + */ + function confirmDialog($params) + { + $answers = $prompts = $types = array(); + foreach ($params as $param) { + $prompts[$param['name']] = $param['prompt']; + $types[$param['name']] = $param['type']; + $answers[$param['name']] = isset($param['default']) ? $param['default'] : ''; + } + + $tried = false; + do { + if ($tried) { + $i = 1; + foreach ($answers as $var => $value) { + if (!strlen($value)) { + echo $this->bold("* Enter an answer for #" . $i . ": ({$prompts[$var]})\n"); + } + $i++; + } + } + + $answers = $this->userDialog('', $prompts, $types, $answers); + $tried = true; + } while (is_array($answers) && count(array_filter($answers)) != count($prompts)); + + return $answers; + } + + function userDialog($command, $prompts, $types = array(), $defaults = array(), $screensize = 20) + { + if (!is_array($prompts)) { + return array(); + } + + $testprompts = array_keys($prompts); + $result = $defaults; + + reset($prompts); + if (count($prompts) === 1) { + foreach ($prompts as $key => $prompt) { + $type = $types[$key]; + $default = @$defaults[$key]; + print "$prompt "; + if ($default) { + print "[$default] "; + } + print ": "; + + $line = fgets(STDIN, 2048); + $result[$key] = ($default && trim($line) == '') ? $default : trim($line); + } + + return $result; + } + + $first_run = true; + while (true) { + $descLength = max(array_map('strlen', $prompts)); + $descFormat = "%-{$descLength}s"; + $last = count($prompts); + + $i = 0; + foreach ($prompts as $n => $var) { + $res = isset($result[$n]) ? $result[$n] : null; + printf("%2d. $descFormat : %s\n", ++$i, $prompts[$n], $res); + } + print "\n1-$last, 'all', 'abort', or Enter to continue: "; + + $tmp = trim(fgets(STDIN, 1024)); + if (empty($tmp)) { + break; + } + + if ($tmp == 'abort') { + return false; + } + + if (isset($testprompts[(int)$tmp - 1])) { + $var = $testprompts[(int)$tmp - 1]; + $desc = $prompts[$var]; + $current = @$result[$var]; + print "$desc [$current] : "; + $tmp = trim(fgets(STDIN, 1024)); + if ($tmp !== '') { + $result[$var] = $tmp; + } + } elseif ($tmp == 'all') { + foreach ($prompts as $var => $desc) { + $current = $result[$var]; + print "$desc [$current] : "; + $tmp = trim(fgets(STDIN, 1024)); + if (trim($tmp) !== '') { + $result[$var] = trim($tmp); + } + } + } + + $first_run = false; + } + + return $result; + } + + function userConfirm($prompt, $default = 'yes') + { + trigger_error("PEAR_Frontend_CLI::userConfirm not yet converted", E_USER_ERROR); + static $positives = array('y', 'yes', 'on', '1'); + static $negatives = array('n', 'no', 'off', '0'); + print "$this->lp$prompt [$default] : "; + $fp = fopen("php://stdin", "r"); + $line = fgets($fp, 2048); + fclose($fp); + $answer = strtolower(trim($line)); + if (empty($answer)) { + $answer = $default; + } + if (in_array($answer, $positives)) { + return true; + } + if (in_array($answer, $negatives)) { + return false; + } + if (in_array($default, $positives)) { + return true; + } + return false; + } + + function outputData($data, $command = '_default') + { + switch ($command) { + case 'channel-info': + foreach ($data as $type => $section) { + if ($type == 'main') { + $section['data'] = array_values($section['data']); + } + + $this->outputData($section); + } + break; + case 'install': + case 'upgrade': + case 'upgrade-all': + if (is_array($data) && isset($data['release_warnings'])) { + $this->_displayLine(''); + $this->_startTable(array( + 'border' => false, + 'caption' => 'Release Warnings' + )); + $this->_tableRow(array($data['release_warnings']), null, array(1 => array('wrap' => 55))); + $this->_endTable(); + $this->_displayLine(''); + } + + $this->_displayLine(is_array($data) ? $data['data'] : $data); + break; + case 'search': + $this->_startTable($data); + if (isset($data['headline']) && is_array($data['headline'])) { + $this->_tableRow($data['headline'], array('bold' => true), array(1 => array('wrap' => 55))); + } + + $packages = array(); + foreach($data['data'] as $category) { + foreach($category as $name => $pkg) { + $packages[$pkg[0]] = $pkg; + } + } + + $p = array_keys($packages); + natcasesort($p); + foreach ($p as $name) { + $this->_tableRow($packages[$name], null, array(1 => array('wrap' => 55))); + } + + $this->_endTable(); + break; + case 'list-all': + if (!isset($data['data'])) { + $this->_displayLine('No packages in channel'); + break; + } + + $this->_startTable($data); + if (isset($data['headline']) && is_array($data['headline'])) { + $this->_tableRow($data['headline'], array('bold' => true), array(1 => array('wrap' => 55))); + } + + $packages = array(); + foreach($data['data'] as $category) { + foreach($category as $name => $pkg) { + $packages[$pkg[0]] = $pkg; + } + } + + $p = array_keys($packages); + natcasesort($p); + foreach ($p as $name) { + $pkg = $packages[$name]; + unset($pkg[4], $pkg[5]); + $this->_tableRow($pkg, null, array(1 => array('wrap' => 55))); + } + + $this->_endTable(); + break; + case 'config-show': + $data['border'] = false; + $opts = array( + 0 => array('wrap' => 30), + 1 => array('wrap' => 20), + 2 => array('wrap' => 35) + ); + + $this->_startTable($data); + if (isset($data['headline']) && is_array($data['headline'])) { + $this->_tableRow($data['headline'], array('bold' => true), $opts); + } + + foreach ($data['data'] as $group) { + foreach ($group as $value) { + if ($value[2] == '') { + $value[2] = ""; + } + + $this->_tableRow($value, null, $opts); + } + } + + $this->_endTable(); + break; + case 'remote-info': + $d = $data; + $data = array( + 'caption' => 'Package details:', + 'border' => false, + 'data' => array( + array("Latest", $data['stable']), + array("Installed", $data['installed']), + array("Package", $data['name']), + array("License", $data['license']), + array("Category", $data['category']), + array("Summary", $data['summary']), + array("Description", $data['description']), + ), + ); + + if (isset($d['deprecated']) && $d['deprecated']) { + $conf = &PEAR_Config::singleton(); + $reg = $conf->getRegistry(); + $name = $reg->parsedPackageNameToString($d['deprecated'], true); + $data['data'][] = array('Deprecated! use', $name); + } + default: { + if (is_array($data)) { + $this->_startTable($data); + $count = count($data['data'][0]); + if ($count == 2) { + $opts = array(0 => array('wrap' => 25), + 1 => array('wrap' => 48) + ); + } elseif ($count == 3) { + $opts = array(0 => array('wrap' => 30), + 1 => array('wrap' => 20), + 2 => array('wrap' => 35) + ); + } else { + $opts = null; + } + if (isset($data['headline']) && is_array($data['headline'])) { + $this->_tableRow($data['headline'], + array('bold' => true), + $opts); + } + + if (is_array($data['data'])) { + foreach($data['data'] as $row) { + $this->_tableRow($row, null, $opts); + } + } else { + $this->_tableRow(array($data['data']), null, $opts); + } + $this->_endTable(); + } else { + $this->_displayLine($data); + } + } + } + } + + function log($text, $append_crlf = true) + { + if ($append_crlf) { + return $this->_displayLine($text); + } + + return $this->_display($text); + } + + function bold($text) + { + if (empty($this->term['bold'])) { + return strtoupper($text); + } + + return $this->term['bold'] . $text . $this->term['normal']; + } + + function _displayHeading($title) + { + print $this->lp.$this->bold($title)."\n"; + print $this->lp.str_repeat("=", strlen($title))."\n"; + } + + function _startTable($params = array()) + { + $params['table_data'] = array(); + $params['widest'] = array(); // indexed by column + $params['highest'] = array(); // indexed by row + $params['ncols'] = 0; + $this->params = $params; + } + + function _tableRow($columns, $rowparams = array(), $colparams = array()) + { + $highest = 1; + for ($i = 0; $i < count($columns); $i++) { + $col = &$columns[$i]; + if (isset($colparams[$i]) && !empty($colparams[$i]['wrap'])) { + $col = wordwrap($col, $colparams[$i]['wrap']); + } + + if (strpos($col, "\n") !== false) { + $multiline = explode("\n", $col); + $w = 0; + foreach ($multiline as $n => $line) { + $len = strlen($line); + if ($len > $w) { + $w = $len; + } + } + $lines = count($multiline); + } else { + $w = strlen($col); + } + + if (isset($this->params['widest'][$i])) { + if ($w > $this->params['widest'][$i]) { + $this->params['widest'][$i] = $w; + } + } else { + $this->params['widest'][$i] = $w; + } + + $tmp = count_chars($columns[$i], 1); + // handle unix, mac and windows formats + $lines = (isset($tmp[10]) ? $tmp[10] : (isset($tmp[13]) ? $tmp[13] : 0)) + 1; + if ($lines > $highest) { + $highest = $lines; + } + } + + if (count($columns) > $this->params['ncols']) { + $this->params['ncols'] = count($columns); + } + + $new_row = array( + 'data' => $columns, + 'height' => $highest, + 'rowparams' => $rowparams, + 'colparams' => $colparams, + ); + $this->params['table_data'][] = $new_row; + } + + function _endTable() + { + extract($this->params); + if (!empty($caption)) { + $this->_displayHeading($caption); + } + + if (count($table_data) === 0) { + return; + } + + if (!isset($width)) { + $width = $widest; + } else { + for ($i = 0; $i < $ncols; $i++) { + if (!isset($width[$i])) { + $width[$i] = $widest[$i]; + } + } + } + + $border = false; + if (empty($border)) { + $cellstart = ''; + $cellend = ' '; + $rowend = ''; + $padrowend = false; + $borderline = ''; + } else { + $cellstart = '| '; + $cellend = ' '; + $rowend = '|'; + $padrowend = true; + $borderline = '+'; + foreach ($width as $w) { + $borderline .= str_repeat('-', $w + strlen($cellstart) + strlen($cellend) - 1); + $borderline .= '+'; + } + } + + if ($borderline) { + $this->_displayLine($borderline); + } + + for ($i = 0; $i < count($table_data); $i++) { + extract($table_data[$i]); + if (!is_array($rowparams)) { + $rowparams = array(); + } + + if (!is_array($colparams)) { + $colparams = array(); + } + + $rowlines = array(); + if ($height > 1) { + for ($c = 0; $c < count($data); $c++) { + $rowlines[$c] = preg_split('/(\r?\n|\r)/', $data[$c]); + if (count($rowlines[$c]) < $height) { + $rowlines[$c] = array_pad($rowlines[$c], $height, ''); + } + } + } else { + for ($c = 0; $c < count($data); $c++) { + $rowlines[$c] = array($data[$c]); + } + } + + for ($r = 0; $r < $height; $r++) { + $rowtext = ''; + for ($c = 0; $c < count($data); $c++) { + if (isset($colparams[$c])) { + $attribs = array_merge($rowparams, $colparams); + } else { + $attribs = $rowparams; + } + + $w = isset($width[$c]) ? $width[$c] : 0; + //$cell = $data[$c]; + $cell = $rowlines[$c][$r]; + $l = strlen($cell); + if ($l > $w) { + $cell = substr($cell, 0, $w); + } + + if (isset($attribs['bold'])) { + $cell = $this->bold($cell); + } + + if ($l < $w) { + // not using str_pad here because we may + // add bold escape characters to $cell + $cell .= str_repeat(' ', $w - $l); + } + + $rowtext .= $cellstart . $cell . $cellend; + } + + if (!$border) { + $rowtext = rtrim($rowtext); + } + + $rowtext .= $rowend; + $this->_displayLine($rowtext); + } + } + + if ($borderline) { + $this->_displayLine($borderline); + } + } + + function _displayLine($text) + { + print "$this->lp$text\n"; + } + + function _display($text) + { + print $text; + } +} \ No newline at end of file diff --git a/includes/pear/PEAR/Installer.php b/includes/pear/PEAR/Installer.php new file mode 100644 index 0000000..867b9ed --- /dev/null +++ b/includes/pear/PEAR/Installer.php @@ -0,0 +1,1753 @@ + + * @author Tomas V.V. Cox + * @author Martin Jansen + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 0.1 + */ + +/** + * Used for installation groups in package.xml 2.0 and platform exceptions + */ +require_once 'OS/Guess.php'; +require_once 'PEAR/Downloader.php'; + +define('PEAR_INSTALLER_NOBINARY', -240); +/** + * Administration class used to install PEAR packages and maintain the + * installed package database. + * + * @category pear + * @package PEAR + * @author Stig Bakken + * @author Tomas V.V. Cox + * @author Martin Jansen + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 0.1 + */ +class PEAR_Installer extends PEAR_Downloader +{ + /** name of the package directory, for example Foo-1.0 + * @var string + */ + var $pkgdir; + + /** directory where PHP code files go + * @var string + */ + var $phpdir; + + /** directory where PHP extension files go + * @var string + */ + var $extdir; + + /** directory where documentation goes + * @var string + */ + var $docdir; + + /** installation root directory (ala PHP's INSTALL_ROOT or + * automake's DESTDIR + * @var string + */ + var $installroot = ''; + + /** debug level + * @var int + */ + var $debug = 1; + + /** temporary directory + * @var string + */ + var $tmpdir; + + /** + * PEAR_Registry object used by the installer + * @var PEAR_Registry + */ + var $registry; + + /** + * array of PEAR_Downloader_Packages + * @var array + */ + var $_downloadedPackages; + + /** List of file transactions queued for an install/upgrade/uninstall. + * + * Format: + * array( + * 0 => array("rename => array("from-file", "to-file")), + * 1 => array("delete" => array("file-to-delete")), + * ... + * ) + * + * @var array + */ + var $file_operations = array(); + + /** + * PEAR_Installer constructor. + * + * @param object $ui user interface object (instance of PEAR_Frontend_*) + * + * @access public + */ + function PEAR_Installer(&$ui) + { + parent::PEAR_Common(); + $this->setFrontendObject($ui); + $this->debug = $this->config->get('verbose'); + } + + function setOptions($options) + { + $this->_options = $options; + } + + function setConfig(&$config) + { + $this->config = &$config; + $this->_registry = &$config->getRegistry(); + } + + function _removeBackups($files) + { + foreach ($files as $path) { + $this->addFileOperation('removebackup', array($path)); + } + } + + /** + * Delete a package's installed files, does not remove empty directories. + * + * @param string package name + * @param string channel name + * @param bool if true, then files are backed up first + * @return bool TRUE on success, or a PEAR error on failure + * @access protected + */ + function _deletePackageFiles($package, $channel = false, $backup = false) + { + if (!$channel) { + $channel = 'pear.php.net'; + } + + if (!strlen($package)) { + return $this->raiseError("No package to uninstall given"); + } + + if (strtolower($package) == 'pear' && $channel == 'pear.php.net') { + // to avoid race conditions, include all possible needed files + require_once 'PEAR/Task/Common.php'; + require_once 'PEAR/Task/Replace.php'; + require_once 'PEAR/Task/Unixeol.php'; + require_once 'PEAR/Task/Windowseol.php'; + require_once 'PEAR/PackageFile/v1.php'; + require_once 'PEAR/PackageFile/v2.php'; + require_once 'PEAR/PackageFile/Generator/v1.php'; + require_once 'PEAR/PackageFile/Generator/v2.php'; + } + + $filelist = $this->_registry->packageInfo($package, 'filelist', $channel); + if ($filelist == null) { + return $this->raiseError("$channel/$package not installed"); + } + + $ret = array(); + foreach ($filelist as $file => $props) { + if (empty($props['installed_as'])) { + continue; + } + + $path = $props['installed_as']; + if ($backup) { + $this->addFileOperation('backup', array($path)); + $ret[] = $path; + } + + $this->addFileOperation('delete', array($path)); + } + + if ($backup) { + return $ret; + } + + return true; + } + + /** + * @param string filename + * @param array attributes from tag in package.xml + * @param string path to install the file in + * @param array options from command-line + * @access private + */ + function _installFile($file, $atts, $tmp_path, $options) + { + // return if this file is meant for another platform + static $os; + if (!isset($this->_registry)) { + $this->_registry = &$this->config->getRegistry(); + } + + if (isset($atts['platform'])) { + if (empty($os)) { + $os = new OS_Guess; + } + + if (strlen($atts['platform']) && $atts['platform']{0} == '!') { + $negate = true; + $platform = substr($atts['platform'], 1); + } else { + $negate = false; + $platform = $atts['platform']; + } + + if ((bool) $os->matchSignature($platform) === $negate) { + $this->log(3, "skipped $file (meant for $atts[platform], we are ".$os->getSignature().")"); + return PEAR_INSTALLER_SKIPPED; + } + } + + $channel = $this->pkginfo->getChannel(); + // assemble the destination paths + switch ($atts['role']) { + case 'src': + case 'extsrc': + $this->source_files++; + return; + case 'doc': + case 'data': + case 'test': + $dest_dir = $this->config->get($atts['role'] . '_dir', null, $channel) . + DIRECTORY_SEPARATOR . $this->pkginfo->getPackage(); + unset($atts['baseinstalldir']); + break; + case 'ext': + case 'php': + $dest_dir = $this->config->get($atts['role'] . '_dir', null, $channel); + break; + case 'script': + $dest_dir = $this->config->get('bin_dir', null, $channel); + break; + default: + return $this->raiseError("Invalid role `$atts[role]' for file $file"); + } + + $save_destdir = $dest_dir; + if (!empty($atts['baseinstalldir'])) { + $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir']; + } + + if (dirname($file) != '.' && empty($atts['install-as'])) { + $dest_dir .= DIRECTORY_SEPARATOR . dirname($file); + } + + if (empty($atts['install-as'])) { + $dest_file = $dest_dir . DIRECTORY_SEPARATOR . basename($file); + } else { + $dest_file = $dest_dir . DIRECTORY_SEPARATOR . $atts['install-as']; + } + $orig_file = $tmp_path . DIRECTORY_SEPARATOR . $file; + + // Clean up the DIRECTORY_SEPARATOR mess + $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR; + list($dest_file, $orig_file) = preg_replace(array('!\\\\+!', '!/!', "!$ds2+!"), + array(DIRECTORY_SEPARATOR, + DIRECTORY_SEPARATOR, + DIRECTORY_SEPARATOR), + array($dest_file, $orig_file)); + $final_dest_file = $installed_as = $dest_file; + if (isset($this->_options['packagingroot'])) { + $installedas_dest_dir = dirname($final_dest_file); + $installedas_dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file); + $final_dest_file = $this->_prependPath($final_dest_file, $this->_options['packagingroot']); + } else { + $installedas_dest_dir = dirname($final_dest_file); + $installedas_dest_file = $installedas_dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file); + } + + $dest_dir = dirname($final_dest_file); + $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file); + if (preg_match('~/\.\.(/|\\z)|^\.\./~', str_replace('\\', '/', $dest_file))) { + return $this->raiseError("SECURITY ERROR: file $file (installed to $dest_file) contains parent directory reference ..", PEAR_INSTALLER_FAILED); + } + + if ( + empty($this->_options['register-only']) && + (!file_exists($dest_dir) || !is_dir($dest_dir)) + ) { + if (!$this->mkDirHier($dest_dir)) { + return $this->raiseError("failed to mkdir $dest_dir", + PEAR_INSTALLER_FAILED); + } + $this->log(3, "+ mkdir $dest_dir"); + } + + // pretty much nothing happens if we are only registering the install + if (empty($this->_options['register-only'])) { + if (empty($atts['replacements'])) { + if (!file_exists($orig_file)) { + return $this->raiseError("file $orig_file does not exist", + PEAR_INSTALLER_FAILED); + } + + if (!@copy($orig_file, $dest_file)) { + return $this->raiseError("failed to write $dest_file: $php_errormsg", + PEAR_INSTALLER_FAILED); + } + + $this->log(3, "+ cp $orig_file $dest_file"); + if (isset($atts['md5sum'])) { + $md5sum = md5_file($dest_file); + } + } else { + // file with replacements + if (!file_exists($orig_file)) { + return $this->raiseError("file does not exist", + PEAR_INSTALLER_FAILED); + } + + $contents = file_get_contents($orig_file); + if ($contents === false) { + $contents = ''; + } + + if (isset($atts['md5sum'])) { + $md5sum = md5($contents); + } + + $subst_from = $subst_to = array(); + foreach ($atts['replacements'] as $a) { + $to = ''; + if ($a['type'] == 'php-const') { + if (preg_match('/^[a-z0-9_]+\\z/i', $a['to'])) { + eval("\$to = $a[to];"); + } else { + if (!isset($options['soft'])) { + $this->log(0, "invalid php-const replacement: $a[to]"); + } + continue; + } + } elseif ($a['type'] == 'pear-config') { + if ($a['to'] == 'master_server') { + $chan = $this->_registry->getChannel($channel); + if (!PEAR::isError($chan)) { + $to = $chan->getServer(); + } else { + $to = $this->config->get($a['to'], null, $channel); + } + } else { + $to = $this->config->get($a['to'], null, $channel); + } + + if (is_null($to)) { + if (!isset($options['soft'])) { + $this->log(0, "invalid pear-config replacement: $a[to]"); + } + + continue; + } + } elseif ($a['type'] == 'package-info') { + if ($t = $this->pkginfo->packageInfo($a['to'])) { + $to = $t; + } else { + if (!isset($options['soft'])) { + $this->log(0, "invalid package-info replacement: $a[to]"); + } + + continue; + } + } + + if (!is_null($to)) { + $subst_from[] = $a['from']; + $subst_to[] = $to; + } + } + + $this->log(3, "doing ".sizeof($subst_from)." substitution(s) for $final_dest_file"); + if (sizeof($subst_from)) { + $contents = str_replace($subst_from, $subst_to, $contents); + } + + $wp = @fopen($dest_file, "wb"); + if (!is_resource($wp)) { + return $this->raiseError("failed to create $dest_file: $php_errormsg", + PEAR_INSTALLER_FAILED); + } + + if (@fwrite($wp, $contents) === false) { + return $this->raiseError("failed writing to $dest_file: $php_errormsg", + PEAR_INSTALLER_FAILED); + } + + fclose($wp); + } + + // check the md5 + if (isset($md5sum)) { + if (strtolower($md5sum) === strtolower($atts['md5sum'])) { + $this->log(2, "md5sum ok: $final_dest_file"); + } else { + if (empty($options['force'])) { + // delete the file + if (file_exists($dest_file)) { + unlink($dest_file); + } + + if (!isset($options['ignore-errors'])) { + return $this->raiseError("bad md5sum for file $final_dest_file", + PEAR_INSTALLER_FAILED); + } + + if (!isset($options['soft'])) { + $this->log(0, "warning : bad md5sum for file $final_dest_file"); + } + } else { + if (!isset($options['soft'])) { + $this->log(0, "warning : bad md5sum for file $final_dest_file"); + } + } + } + } + + // set file permissions + if (!OS_WINDOWS) { + $umask = $this->config->get('umask'); + if ($atts['role'] == 'script') { + $mode = 0777 & ~(int)octdec($umask); + $this->log(3, "+ chmod +x $dest_file"); + } else { + $mode = 0666 & ~(int)octdec($umask); + } + + if ($atts['role'] != 'src') { + $this->addFileOperation("chmod", array($mode, $dest_file)); + if (!@chmod($dest_file, $mode)) { + if (!isset($options['soft'])) { + $this->log(0, "failed to change mode of $dest_file: $php_errormsg"); + } + } + } + } + + if ($atts['role'] == 'src') { + rename($dest_file, $final_dest_file); + $this->log(2, "renamed source file $dest_file to $final_dest_file"); + } else { + $this->addFileOperation("rename", array($dest_file, $final_dest_file, + $atts['role'] == 'ext')); + } + } + + // Store the full path where the file was installed for easy unistall + if ($atts['role'] != 'script') { + $loc = $this->config->get($atts['role'] . '_dir'); + } else { + $loc = $this->config->get('bin_dir'); + } + + if ($atts['role'] != 'src') { + $this->addFileOperation("installed_as", array($file, $installed_as, + $loc, + dirname(substr($installedas_dest_file, strlen($loc))))); + } + + //$this->log(2, "installed: $dest_file"); + return PEAR_INSTALLER_OK; + } + + /** + * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 + * @param string filename + * @param array attributes from tag in package.xml + * @param string path to install the file in + * @param array options from command-line + * @access private + */ + function _installFile2(&$pkg, $file, &$real_atts, $tmp_path, $options) + { + $atts = $real_atts; + if (!isset($this->_registry)) { + $this->_registry = &$this->config->getRegistry(); + } + + $channel = $pkg->getChannel(); + // assemble the destination paths + $roles = PEAR_Installer_Role::getValidRoles($pkg->getPackageType()); + if (!in_array($atts['attribs']['role'], $roles)) { + return $this->raiseError('Invalid role `' . $atts['attribs']['role'] . + "' for file $file"); + } + + $role = &PEAR_Installer_Role::factory($pkg, $atts['attribs']['role'], $this->config); + $err = $role->setup($this, $pkg, $atts['attribs'], $file); + if (PEAR::isError($err)) { + return $err; + } + + if (!$role->isInstallable()) { + return; + } + + $info = $role->processInstallation($pkg, $atts['attribs'], $file, $tmp_path); + if (PEAR::isError($info)) { + return $info; + } + + list($save_destdir, $dest_dir, $dest_file, $orig_file) = $info; + if (preg_match('~/\.\.(/|\\z)|^\.\./~', str_replace('\\', '/', $dest_file))) { + return $this->raiseError("SECURITY ERROR: file $file (installed to $dest_file) contains parent directory reference ..", PEAR_INSTALLER_FAILED); + } + + $final_dest_file = $installed_as = $dest_file; + if (isset($this->_options['packagingroot'])) { + $final_dest_file = $this->_prependPath($final_dest_file, $this->_options['packagingroot']); + } + + $dest_dir = dirname($final_dest_file); + $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file); + + if (empty($this->_options['register-only'])) { + if (!file_exists($dest_dir) || !is_dir($dest_dir)) { + if (!$this->mkDirHier($dest_dir)) { + return $this->raiseError("failed to mkdir $dest_dir", + PEAR_INSTALLER_FAILED); + } + $this->log(3, "+ mkdir $dest_dir"); + } + } + + $attribs = $atts['attribs']; + unset($atts['attribs']); + // pretty much nothing happens if we are only registering the install + if (empty($this->_options['register-only'])) { + if (!count($atts)) { // no tasks + if (!file_exists($orig_file)) { + return $this->raiseError("file $orig_file does not exist", + PEAR_INSTALLER_FAILED); + } + + if (!@copy($orig_file, $dest_file)) { + return $this->raiseError("failed to write $dest_file: $php_errormsg", + PEAR_INSTALLER_FAILED); + } + + $this->log(3, "+ cp $orig_file $dest_file"); + if (isset($attribs['md5sum'])) { + $md5sum = md5_file($dest_file); + } + } else { // file with tasks + if (!file_exists($orig_file)) { + return $this->raiseError("file $orig_file does not exist", + PEAR_INSTALLER_FAILED); + } + + $contents = file_get_contents($orig_file); + if ($contents === false) { + $contents = ''; + } + + if (isset($attribs['md5sum'])) { + $md5sum = md5($contents); + } + + foreach ($atts as $tag => $raw) { + $tag = str_replace(array($pkg->getTasksNs() . ':', '-'), array('', '_'), $tag); + $task = "PEAR_Task_$tag"; + $task = &new $task($this->config, $this, PEAR_TASK_INSTALL); + if (!$task->isScript()) { // scripts are only handled after installation + $task->init($raw, $attribs, $pkg->getLastInstalledVersion()); + $res = $task->startSession($pkg, $contents, $final_dest_file); + if ($res === false) { + continue; // skip this file + } + + if (PEAR::isError($res)) { + return $res; + } + + $contents = $res; // save changes + } + + $wp = @fopen($dest_file, "wb"); + if (!is_resource($wp)) { + return $this->raiseError("failed to create $dest_file: $php_errormsg", + PEAR_INSTALLER_FAILED); + } + + if (fwrite($wp, $contents) === false) { + return $this->raiseError("failed writing to $dest_file: $php_errormsg", + PEAR_INSTALLER_FAILED); + } + + fclose($wp); + } + } + + // check the md5 + if (isset($md5sum)) { + // Make sure the original md5 sum matches with expected + if (strtolower($md5sum) === strtolower($attribs['md5sum'])) { + $this->log(2, "md5sum ok: $final_dest_file"); + + if (isset($contents)) { + // set md5 sum based on $content in case any tasks were run. + $real_atts['attribs']['md5sum'] = md5($contents); + } + } else { + if (empty($options['force'])) { + // delete the file + if (file_exists($dest_file)) { + unlink($dest_file); + } + + if (!isset($options['ignore-errors'])) { + return $this->raiseError("bad md5sum for file $final_dest_file", + PEAR_INSTALLER_FAILED); + } + + if (!isset($options['soft'])) { + $this->log(0, "warning : bad md5sum for file $final_dest_file"); + } + } else { + if (!isset($options['soft'])) { + $this->log(0, "warning : bad md5sum for file $final_dest_file"); + } + } + } + } else { + $real_atts['attribs']['md5sum'] = md5_file($dest_file); + } + + //set file permissions + if (!OS_WINDOWS) { + if ($role->isExecutable()) { + $mode = 0777 & ~(int)octdec($this->config->get('umask')); + $this->log(3, "+ chmod +x $dest_file"); + } else { + $mode = 0666 & ~(int)octdec($this->config->get('umask')); + } + + if ($attribs['role'] != 'src') { + $this->addFileOperation("chmod", array($mode, $dest_file)); + if (!@chmod($dest_file, $mode)) { + if (!isset($options['soft'])) { + $this->log(0, "failed to change mode of $dest_file: $php_errormsg"); + } + } + } + } + + if ($attribs['role'] == 'src') { + rename($dest_file, $final_dest_file); + $this->log(2, "renamed source file $dest_file to $final_dest_file"); + } else { + $this->addFileOperation("rename", array($dest_file, $final_dest_file, $role->isExtension())); + } + } + + // Store the full path where the file was installed for easy uninstall + if ($attribs['role'] != 'src') { + $loc = $this->config->get($role->getLocationConfig(), null, $channel); + $this->addFileOperation('installed_as', array($file, $installed_as, + $loc, + dirname(substr($installed_as, strlen($loc))))); + } + + //$this->log(2, "installed: $dest_file"); + return PEAR_INSTALLER_OK; + } + + /** + * Add a file operation to the current file transaction. + * + * @see startFileTransaction() + * @param string $type This can be one of: + * - rename: rename a file ($data has 3 values) + * - backup: backup an existing file ($data has 1 value) + * - removebackup: clean up backups created during install ($data has 1 value) + * - chmod: change permissions on a file ($data has 2 values) + * - delete: delete a file ($data has 1 value) + * - rmdir: delete a directory if empty ($data has 1 value) + * - installed_as: mark a file as installed ($data has 4 values). + * @param array $data For all file operations, this array must contain the + * full path to the file or directory that is being operated on. For + * the rename command, the first parameter must be the file to rename, + * the second its new name, the third whether this is a PHP extension. + * + * The installed_as operation contains 4 elements in this order: + * 1. Filename as listed in the filelist element from package.xml + * 2. Full path to the installed file + * 3. Full path from the php_dir configuration variable used in this + * installation + * 4. Relative path from the php_dir that this file is installed in + */ + function addFileOperation($type, $data) + { + if (!is_array($data)) { + return $this->raiseError('Internal Error: $data in addFileOperation' + . ' must be an array, was ' . gettype($data)); + } + + if ($type == 'chmod') { + $octmode = decoct($data[0]); + $this->log(3, "adding to transaction: $type $octmode $data[1]"); + } else { + $this->log(3, "adding to transaction: $type " . implode(" ", $data)); + } + $this->file_operations[] = array($type, $data); + } + + function startFileTransaction($rollback_in_case = false) + { + if (count($this->file_operations) && $rollback_in_case) { + $this->rollbackFileTransaction(); + } + $this->file_operations = array(); + } + + function commitFileTransaction() + { + //first, check permissions and such manually + $errors = array(); + foreach ($this->file_operations as $key => $tr) { + list($type, $data) = $tr; + switch ($type) { + case 'rename': + if (!file_exists($data[0])) { + $errors[] = "cannot rename file $data[0], doesn't exist"; + } + + // check that dest dir. is writable + if (!is_writable(dirname($data[1]))) { + $errors[] = "permission denied ($type): $data[1]"; + } + break; + case 'chmod': + // check that file is writable + if (!is_writable($data[1])) { + $errors[] = "permission denied ($type): $data[1] " . decoct($data[0]); + } + break; + case 'delete': + if (!file_exists($data[0])) { + $this->log(2, "warning: file $data[0] doesn't exist, can't be deleted"); + } + + // check that directory is writable + if (file_exists($data[0])) { + if (!is_writable(dirname($data[0]))) { + $errors[] = "permission denied ($type): $data[0]"; + } else { + // make sure the file to be deleted can be opened for writing + $fp = false; + if (!is_dir($data[0]) && + (!is_writable($data[0]) || !($fp = @fopen($data[0], 'a')))) { + $errors[] = "permission denied ($type): $data[0]"; + } elseif ($fp) { + fclose($fp); + } + } + + /* Verify we are not deleting a file owned by another package + * This can happen when a file moves from package A to B in + * an upgrade ala http://pear.php.net/17986 + */ + $info = array( + 'package' => strtolower($this->pkginfo->getName()), + 'channel' => strtolower($this->pkginfo->getChannel()), + ); + $result = $this->_registry->checkFileMap($data[0], $info, '1.1'); + if (is_array($result)) { + $res = array_diff($result, $info); + if (!empty($res)) { + $new = $this->_registry->getPackage($result[1], $result[0]); + $this->file_operations[$key] = false; + $pkginfoName = $this->pkginfo->getName(); + $newChannel = $new->getChannel(); + $newPackage = $new->getName(); + $this->log(3, "file $data[0] was scheduled for removal from $pkginfoName but is owned by $newChannel/$newPackage, removal has been cancelled."); + } + } + } + break; + } + } + + $n = count($this->file_operations); + $this->log(2, "about to commit $n file operations for " . $this->pkginfo->getName()); + + $m = count($errors); + if ($m > 0) { + foreach ($errors as $error) { + if (!isset($this->_options['soft'])) { + $this->log(1, $error); + } + } + + if (!isset($this->_options['ignore-errors'])) { + return false; + } + } + + $this->_dirtree = array(); + // really commit the transaction + foreach ($this->file_operations as $i => $tr) { + if (!$tr) { + // support removal of non-existing backups + continue; + } + + list($type, $data) = $tr; + switch ($type) { + case 'backup': + if (!file_exists($data[0])) { + $this->file_operations[$i] = false; + break; + } + + if (!@copy($data[0], $data[0] . '.bak')) { + $this->log(1, 'Could not copy ' . $data[0] . ' to ' . $data[0] . + '.bak ' . $php_errormsg); + return false; + } + $this->log(3, "+ backup $data[0] to $data[0].bak"); + break; + case 'removebackup': + if (file_exists($data[0] . '.bak') && is_writable($data[0] . '.bak')) { + unlink($data[0] . '.bak'); + $this->log(3, "+ rm backup of $data[0] ($data[0].bak)"); + } + break; + case 'rename': + $test = file_exists($data[1]) ? @unlink($data[1]) : null; + if (!$test && file_exists($data[1])) { + if ($data[2]) { + $extra = ', this extension must be installed manually. Rename to "' . + basename($data[1]) . '"'; + } else { + $extra = ''; + } + + if (!isset($this->_options['soft'])) { + $this->log(1, 'Could not delete ' . $data[1] . ', cannot rename ' . + $data[0] . $extra); + } + + if (!isset($this->_options['ignore-errors'])) { + return false; + } + } + + // permissions issues with rename - copy() is far superior + $perms = @fileperms($data[0]); + if (!@copy($data[0], $data[1])) { + $this->log(1, 'Could not rename ' . $data[0] . ' to ' . $data[1] . + ' ' . $php_errormsg); + return false; + } + + // copy over permissions, otherwise they are lost + @chmod($data[1], $perms); + @unlink($data[0]); + $this->log(3, "+ mv $data[0] $data[1]"); + break; + case 'chmod': + if (!@chmod($data[1], $data[0])) { + $this->log(1, 'Could not chmod ' . $data[1] . ' to ' . + decoct($data[0]) . ' ' . $php_errormsg); + return false; + } + + $octmode = decoct($data[0]); + $this->log(3, "+ chmod $octmode $data[1]"); + break; + case 'delete': + if (file_exists($data[0])) { + if (!@unlink($data[0])) { + $this->log(1, 'Could not delete ' . $data[0] . ' ' . + $php_errormsg); + return false; + } + $this->log(3, "+ rm $data[0]"); + } + break; + case 'rmdir': + if (file_exists($data[0])) { + do { + $testme = opendir($data[0]); + while (false !== ($entry = readdir($testme))) { + if ($entry == '.' || $entry == '..') { + continue; + } + closedir($testme); + break 2; // this directory is not empty and can't be + // deleted + } + + closedir($testme); + if (!@rmdir($data[0])) { + $this->log(1, 'Could not rmdir ' . $data[0] . ' ' . + $php_errormsg); + return false; + } + $this->log(3, "+ rmdir $data[0]"); + } while (false); + } + break; + case 'installed_as': + $this->pkginfo->setInstalledAs($data[0], $data[1]); + if (!isset($this->_dirtree[dirname($data[1])])) { + $this->_dirtree[dirname($data[1])] = true; + $this->pkginfo->setDirtree(dirname($data[1])); + + while(!empty($data[3]) && dirname($data[3]) != $data[3] && + $data[3] != '/' && $data[3] != '\\') { + $this->pkginfo->setDirtree($pp = + $this->_prependPath($data[3], $data[2])); + $this->_dirtree[$pp] = true; + $data[3] = dirname($data[3]); + } + } + break; + } + } + + $this->log(2, "successfully committed $n file operations"); + $this->file_operations = array(); + return true; + } + + function rollbackFileTransaction() + { + $n = count($this->file_operations); + $this->log(2, "rolling back $n file operations"); + foreach ($this->file_operations as $tr) { + list($type, $data) = $tr; + switch ($type) { + case 'backup': + if (file_exists($data[0] . '.bak')) { + if (file_exists($data[0] && is_writable($data[0]))) { + unlink($data[0]); + } + @copy($data[0] . '.bak', $data[0]); + $this->log(3, "+ restore $data[0] from $data[0].bak"); + } + break; + case 'removebackup': + if (file_exists($data[0] . '.bak') && is_writable($data[0] . '.bak')) { + unlink($data[0] . '.bak'); + $this->log(3, "+ rm backup of $data[0] ($data[0].bak)"); + } + break; + case 'rename': + @unlink($data[0]); + $this->log(3, "+ rm $data[0]"); + break; + case 'mkdir': + @rmdir($data[0]); + $this->log(3, "+ rmdir $data[0]"); + break; + case 'chmod': + break; + case 'delete': + break; + case 'installed_as': + $this->pkginfo->setInstalledAs($data[0], false); + break; + } + } + $this->pkginfo->resetDirtree(); + $this->file_operations = array(); + } + + function mkDirHier($dir) + { + $this->addFileOperation('mkdir', array($dir)); + return parent::mkDirHier($dir); + } + + /** + * Download any files and their dependencies, if necessary + * + * @param array a mixed list of package names, local files, or package.xml + * @param PEAR_Config + * @param array options from the command line + * @param array this is the array that will be populated with packages to + * install. Format of each entry: + * + * + * array('pkg' => 'package_name', 'file' => '/path/to/local/file', + * 'info' => array() // parsed package.xml + * ); + * + * @param array this will be populated with any error messages + * @param false private recursion variable + * @param false private recursion variable + * @param false private recursion variable + * @deprecated in favor of PEAR_Downloader + */ + function download($packages, $options, &$config, &$installpackages, + &$errors, $installed = false, $willinstall = false, $state = false) + { + // trickiness: initialize here + parent::PEAR_Downloader($this->ui, $options, $config); + $ret = parent::download($packages); + $errors = $this->getErrorMsgs(); + $installpackages = $this->getDownloadedPackages(); + trigger_error("PEAR Warning: PEAR_Installer::download() is deprecated " . + "in favor of PEAR_Downloader class", E_USER_WARNING); + return $ret; + } + + function _parsePackageXml(&$descfile) + { + // Parse xml file + $pkg = new PEAR_PackageFile($this->config, $this->debug); + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $p = &$pkg->fromAnyFile($descfile, PEAR_VALIDATE_INSTALLING); + PEAR::staticPopErrorHandling(); + if (PEAR::isError($p)) { + if (is_array($p->getUserInfo())) { + foreach ($p->getUserInfo() as $err) { + $loglevel = $err['level'] == 'error' ? 0 : 1; + if (!isset($this->_options['soft'])) { + $this->log($loglevel, ucfirst($err['level']) . ': ' . $err['message']); + } + } + } + return $this->raiseError('Installation failed: invalid package file'); + } + + $descfile = $p->getPackageFile(); + return $p; + } + + /** + * Set the list of PEAR_Downloader_Package objects to allow more sane + * dependency validation + * @param array + */ + function setDownloadedPackages(&$pkgs) + { + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + $err = $this->analyzeDependencies($pkgs); + PEAR::popErrorHandling(); + if (PEAR::isError($err)) { + return $err; + } + $this->_downloadedPackages = &$pkgs; + } + + /** + * Set the list of PEAR_Downloader_Package objects to allow more sane + * dependency validation + * @param array + */ + function setUninstallPackages(&$pkgs) + { + $this->_downloadedPackages = &$pkgs; + } + + function getInstallPackages() + { + return $this->_downloadedPackages; + } + + /** + * Installs the files within the package file specified. + * + * @param string|PEAR_Downloader_Package $pkgfile path to the package file, + * or a pre-initialized packagefile object + * @param array $options + * recognized options: + * - installroot : optional prefix directory for installation + * - force : force installation + * - register-only : update registry but don't install files + * - upgrade : upgrade existing install + * - soft : fail silently + * - nodeps : ignore dependency conflicts/missing dependencies + * - alldeps : install all dependencies + * - onlyreqdeps : install only required dependencies + * + * @return array|PEAR_Error package info if successful + */ + function install($pkgfile, $options = array()) + { + $this->_options = $options; + $this->_registry = &$this->config->getRegistry(); + if (is_object($pkgfile)) { + $dlpkg = &$pkgfile; + $pkg = $pkgfile->getPackageFile(); + $pkgfile = $pkg->getArchiveFile(); + $descfile = $pkg->getPackageFile(); + } else { + $descfile = $pkgfile; + $pkg = $this->_parsePackageXml($descfile); + if (PEAR::isError($pkg)) { + return $pkg; + } + } + + $tmpdir = dirname($descfile); + if (realpath($descfile) != realpath($pkgfile)) { + // Use the temp_dir since $descfile can contain the download dir path + $tmpdir = $this->config->get('temp_dir', null, 'pear.php.net'); + $tmpdir = System::mktemp('-d -t "' . $tmpdir . '"'); + + $tar = new Archive_Tar($pkgfile); + if (!$tar->extract($tmpdir)) { + return $this->raiseError("unable to unpack $pkgfile"); + } + } + + $name = $pkg->getName(); + $channel = $pkg->getChannel(); + if (isset($this->_options['packagingroot'])) { + $regdir = $this->_prependPath( + $this->config->get('php_dir', null, 'pear.php.net'), + $this->_options['packagingroot']); + + $packrootphp_dir = $this->_prependPath( + $this->config->get('php_dir', null, $channel), + $this->_options['packagingroot']); + } + + if (isset($options['installroot'])) { + $this->config->setInstallRoot($options['installroot']); + $this->_registry = &$this->config->getRegistry(); + $installregistry = &$this->_registry; + $this->installroot = ''; // all done automagically now + $php_dir = $this->config->get('php_dir', null, $channel); + } else { + $this->config->setInstallRoot(false); + $this->_registry = &$this->config->getRegistry(); + if (isset($this->_options['packagingroot'])) { + $installregistry = &new PEAR_Registry($regdir); + if (!$installregistry->channelExists($channel, true)) { + // we need to fake a channel-discover of this channel + $chanobj = $this->_registry->getChannel($channel, true); + $installregistry->addChannel($chanobj); + } + $php_dir = $packrootphp_dir; + } else { + $installregistry = &$this->_registry; + $php_dir = $this->config->get('php_dir', null, $channel); + } + $this->installroot = ''; + } + + // checks to do when not in "force" mode + if (empty($options['force']) && + (file_exists($this->config->get('php_dir')) && + is_dir($this->config->get('php_dir')))) { + $testp = $channel == 'pear.php.net' ? $name : array($channel, $name); + $instfilelist = $pkg->getInstallationFileList(true); + if (PEAR::isError($instfilelist)) { + return $instfilelist; + } + + // ensure we have the most accurate registry + $installregistry->flushFileMap(); + $test = $installregistry->checkFileMap($instfilelist, $testp, '1.1'); + if (PEAR::isError($test)) { + return $test; + } + + if (sizeof($test)) { + $pkgs = $this->getInstallPackages(); + $found = false; + foreach ($pkgs as $param) { + if ($pkg->isSubpackageOf($param)) { + $found = true; + break; + } + } + + if ($found) { + // subpackages can conflict with earlier versions of parent packages + $parentreg = $installregistry->packageInfo($param->getPackage(), null, $param->getChannel()); + $tmp = $test; + foreach ($tmp as $file => $info) { + if (is_array($info)) { + if (strtolower($info[1]) == strtolower($param->getPackage()) && + strtolower($info[0]) == strtolower($param->getChannel()) + ) { + if (isset($parentreg['filelist'][$file])) { + unset($parentreg['filelist'][$file]); + } else{ + $pos = strpos($file, '/'); + $basedir = substr($file, 0, $pos); + $file2 = substr($file, $pos + 1); + if (isset($parentreg['filelist'][$file2]['baseinstalldir']) + && $parentreg['filelist'][$file2]['baseinstalldir'] === $basedir + ) { + unset($parentreg['filelist'][$file2]); + } + } + + unset($test[$file]); + } + } else { + if (strtolower($param->getChannel()) != 'pear.php.net') { + continue; + } + + if (strtolower($info) == strtolower($param->getPackage())) { + if (isset($parentreg['filelist'][$file])) { + unset($parentreg['filelist'][$file]); + } else{ + $pos = strpos($file, '/'); + $basedir = substr($file, 0, $pos); + $file2 = substr($file, $pos + 1); + if (isset($parentreg['filelist'][$file2]['baseinstalldir']) + && $parentreg['filelist'][$file2]['baseinstalldir'] === $basedir + ) { + unset($parentreg['filelist'][$file2]); + } + } + + unset($test[$file]); + } + } + } + + $pfk = &new PEAR_PackageFile($this->config); + $parentpkg = &$pfk->fromArray($parentreg); + $installregistry->updatePackage2($parentpkg); + } + + if ($param->getChannel() == 'pecl.php.net' && isset($options['upgrade'])) { + $tmp = $test; + foreach ($tmp as $file => $info) { + if (is_string($info)) { + // pear.php.net packages are always stored as strings + if (strtolower($info) == strtolower($param->getPackage())) { + // upgrading existing package + unset($test[$file]); + } + } + } + } + + if (count($test)) { + $msg = "$channel/$name: conflicting files found:\n"; + $longest = max(array_map("strlen", array_keys($test))); + $fmt = "%${longest}s (%s)\n"; + foreach ($test as $file => $info) { + if (!is_array($info)) { + $info = array('pear.php.net', $info); + } + $info = $info[0] . '/' . $info[1]; + $msg .= sprintf($fmt, $file, $info); + } + + if (!isset($options['ignore-errors'])) { + return $this->raiseError($msg); + } + + if (!isset($options['soft'])) { + $this->log(0, "WARNING: $msg"); + } + } + } + } + + $this->startFileTransaction(); + + // Figure out what channel to use and if the package exists or not + $usechannel = $channel; + if ($channel == 'pecl.php.net') { + $test = $installregistry->packageExists($name, $channel); + if (!$test) { + $test = $installregistry->packageExists($name, 'pear.php.net'); + $usechannel = 'pear.php.net'; + } + } else { + $test = $installregistry->packageExists($name, $channel); + } + + // checks to do only when installing new packages + if (empty($options['upgrade']) && empty($options['soft'])) { + if (empty($options['force']) && $test) { + return $this->raiseError("$channel/$name is already installed"); + } + } else { + // Upgrade + if ($test) { + $v1 = $installregistry->packageInfo($name, 'version', $usechannel); + $v2 = $pkg->getVersion(); + $cmp = version_compare("$v1", "$v2", 'gt'); + if (empty($options['force']) && !version_compare("$v2", "$v1", 'gt')) { + return $this->raiseError("upgrade to a newer version ($v2 is not newer than $v1)"); + } + } + } + + // Do cleanups for upgrade and install, remove old release's files first + if ($test && empty($options['register-only'])) { + $err = $this->_deletePackageFiles($name, $usechannel, true); + if (PEAR::isError($err)) { + if (!isset($options['ignore-errors'])) { + return $this->raiseError($err); + } + + if (!isset($options['soft'])) { + $this->log(0, 'WARNING: ' . $err->getMessage()); + } + } else { + $backedup = $err; + } + } + + // Copy files to dest dir + + // info from the package it self we want to access from _installFile + $this->pkginfo = &$pkg; + // used to determine whether we should build any C code + $this->source_files = 0; + + $savechannel = $this->config->get('default_channel'); + if (empty($options['register-only']) && !is_dir($php_dir)) { + if (PEAR::isError(System::mkdir(array('-p'), $php_dir))) { + return $this->raiseError("no installation destination directory '$php_dir'\n"); + } + } + + if (substr($pkgfile, -4) != '.xml') { + $tmpdir .= DIRECTORY_SEPARATOR . $name . '-' . $pkg->getVersion(); + } + + $this->configSet('default_channel', $channel); + // install files + + $filelist = $pkg->getInstallationFilelist(); + if (PEAR::isError($filelist)) { + return $filelist; + } + + $p = &$installregistry->getPackage($name, $channel); + $dirtree = (empty($options['register-only']) && $p) ? $p->getDirTree() : false; + + $pkg->resetFilelist(); + $version = $installregistry->packageInfo($pkg->getPackage(), 'version', $pkg->getChannel()); + $pkg->setLastInstalledVersion($version); + foreach ($filelist as $file => $atts) { + $this->expectError(PEAR_INSTALLER_FAILED); + if ($pkg->getPackagexmlVersion() == '1.0') { + $res = $this->_installFile($file, $atts, $tmpdir, $options); + } else { + $res = $this->_installFile2($pkg, $file, $atts, $tmpdir, $options); + } + $this->popExpect(); + + if (PEAR::isError($res)) { + if (empty($options['ignore-errors'])) { + $this->rollbackFileTransaction(); + if ($res->getMessage() == "file does not exist") { + $this->raiseError("file $file in package.xml does not exist"); + } + + return $this->raiseError($res); + } + + if (!isset($options['soft'])) { + $this->log(0, "Warning: " . $res->getMessage()); + } + } + + $real = isset($atts['attribs']) ? $atts['attribs'] : $atts; + if ($res == PEAR_INSTALLER_OK && $real['role'] != 'src') { + // Register files that were installed + $pkg->installedFile($file, $atts); + } + } + + // compile and install source files + if ($this->source_files > 0 && empty($options['nobuild'])) { + if (!isset($options['__compile_configureoptions'])) { + $options['__compile_configureoptions'] = array(); + } + + if (PEAR::isError($err = + $this->_compileSourceFiles($savechannel, $pkg, $options['__compile_configureoptions']))) { + return $err; + } + } + + if (isset($backedup)) { + $this->_removeBackups($backedup); + } + + if (!$this->commitFileTransaction()) { + $this->rollbackFileTransaction(); + $this->configSet('default_channel', $savechannel); + return $this->raiseError("commit failed", PEAR_INSTALLER_FAILED); + } + + // See if package already exists + $usechannel = $channel; + if ($channel == 'pecl.php.net') { + $test = $installregistry->packageExists($name, $channel); + if (!$test) { + $test = $installregistry->packageExists($name, 'pear.php.net'); + $usechannel = 'pear.php.net'; + } + } else { + $test = $installregistry->packageExists($name, $channel); + } + + $ret = false; + $installphase = 'install'; + $oldversion = false; + // Register that the package is installed + if (empty($options['upgrade'])) { + // if 'force' is used, replace the info in registry + if (!empty($options['force']) && $test) { + $oldversion = $installregistry->packageInfo($name, 'version', $usechannel); + $installregistry->deletePackage($name, $usechannel); + } + $ret = $installregistry->addPackage2($pkg); + } else { + if ($dirtree) { + $this->startFileTransaction(); + // attempt to delete empty directories + uksort($dirtree, array($this, '_sortDirs')); + foreach($dirtree as $dir => $notused) { + $this->addFileOperation('rmdir', array($dir)); + } + $this->commitFileTransaction(); + } + + // new: upgrade installs a package if it isn't installed + if (!$test) { + $ret = $installregistry->addPackage2($pkg); + } else { + if ($usechannel != $channel) { + $installregistry->deletePackage($name, $usechannel); + $ret = $installregistry->addPackage2($pkg); + } else { + $ret = $installregistry->updatePackage2($pkg); + } + $installphase = 'upgrade'; + } + } + + if (!$ret) { + $this->configSet('default_channel', $savechannel); + return $this->raiseError("Adding package $channel/$name to registry failed"); + } + // }}} + + $this->configSet('default_channel', $savechannel); + if (class_exists('PEAR_Task_Common')) { // this is auto-included if any tasks exist + if (PEAR_Task_Common::hasPostinstallTasks()) { + PEAR_Task_Common::runPostinstallTasks($installphase); + } + } + + return $pkg->toArray(true); + } + + /** + * @param string + * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 + */ + function _compileSourceFiles($savechannel, &$filelist, $configure_options = array()) + { + require_once 'PEAR/Builder.php'; + $this->log(1, "$this->source_files source files, building"); + $bob = &new PEAR_Builder($this->ui); + $bob->debug = $this->debug; + $built = $bob->build($filelist, array(&$this, '_buildCallback'), $configure_options); + if (PEAR::isError($built)) { + $this->rollbackFileTransaction(); + $this->configSet('default_channel', $savechannel); + return $built; + } + + $this->log(1, "\nBuild process completed successfully"); + foreach ($built as $ext) { + $bn = basename($ext['file']); + list($_ext_name, $_ext_suff) = explode('.', $bn); + if ($_ext_suff == '.so' || $_ext_suff == '.dll') { + if (extension_loaded($_ext_name)) { + $this->raiseError("Extension '$_ext_name' already loaded. " . + 'Please unload it in your php.ini file ' . + 'prior to install or upgrade'); + } + $role = 'ext'; + } else { + $role = 'src'; + } + + $dest = $ext['dest']; + $packagingroot = ''; + if (isset($this->_options['packagingroot'])) { + $packagingroot = $this->_options['packagingroot']; + } + + $copyto = $this->_prependPath($dest, $packagingroot); + $extra = $copyto != $dest ? " as '$copyto'" : ''; + $this->log(1, "Installing '$dest'$extra"); + + $copydir = dirname($copyto); + // pretty much nothing happens if we are only registering the install + if (empty($this->_options['register-only'])) { + if (!file_exists($copydir) || !is_dir($copydir)) { + if (!$this->mkDirHier($copydir)) { + return $this->raiseError("failed to mkdir $copydir", + PEAR_INSTALLER_FAILED); + } + + $this->log(3, "+ mkdir $copydir"); + } + + if (!@copy($ext['file'], $copyto)) { + return $this->raiseError("failed to write $copyto ($php_errormsg)", PEAR_INSTALLER_FAILED); + } + + $this->log(3, "+ cp $ext[file] $copyto"); + $this->addFileOperation('rename', array($ext['file'], $copyto)); + if (!OS_WINDOWS) { + $mode = 0666 & ~(int)octdec($this->config->get('umask')); + $this->addFileOperation('chmod', array($mode, $copyto)); + if (!@chmod($copyto, $mode)) { + $this->log(0, "failed to change mode of $copyto ($php_errormsg)"); + } + } + } + + $data = array( + 'role' => $role, + 'name' => $bn, + 'installed_as' => $dest, + 'php_api' => $ext['php_api'], + 'zend_mod_api' => $ext['zend_mod_api'], + 'zend_ext_api' => $ext['zend_ext_api'], + ); + + if ($filelist->getPackageXmlVersion() == '1.0') { + $filelist->installedFile($bn, $data); + } else { + $filelist->installedFile($bn, array('attribs' => $data)); + } + } + } + + function &getUninstallPackages() + { + return $this->_downloadedPackages; + } + + /** + * Uninstall a package + * + * This method removes all files installed by the application, and then + * removes any empty directories. + * @param string package name + * @param array Command-line options. Possibilities include: + * + * - installroot: base installation dir, if not the default + * - register-only : update registry but don't remove files + * - nodeps: do not process dependencies of other packages to ensure + * uninstallation does not break things + */ + function uninstall($package, $options = array()) + { + $installRoot = isset($options['installroot']) ? $options['installroot'] : ''; + $this->config->setInstallRoot($installRoot); + + $this->installroot = ''; + $this->_registry = &$this->config->getRegistry(); + if (is_object($package)) { + $channel = $package->getChannel(); + $pkg = $package; + $package = $pkg->getPackage(); + } else { + $pkg = false; + $info = $this->_registry->parsePackageName($package, + $this->config->get('default_channel')); + $channel = $info['channel']; + $package = $info['package']; + } + + $savechannel = $this->config->get('default_channel'); + $this->configSet('default_channel', $channel); + if (!is_object($pkg)) { + $pkg = $this->_registry->getPackage($package, $channel); + } + + if (!$pkg) { + $this->configSet('default_channel', $savechannel); + return $this->raiseError($this->_registry->parsedPackageNameToString( + array( + 'channel' => $channel, + 'package' => $package + ), true) . ' not installed'); + } + + if ($pkg->getInstalledBinary()) { + // this is just an alias for a binary package + return $this->_registry->deletePackage($package, $channel); + } + + $filelist = $pkg->getFilelist(); + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + if (!class_exists('PEAR_Dependency2')) { + require_once 'PEAR/Dependency2.php'; + } + + $depchecker = &new PEAR_Dependency2($this->config, $options, + array('channel' => $channel, 'package' => $package), + PEAR_VALIDATE_UNINSTALLING); + $e = $depchecker->validatePackageUninstall($this); + PEAR::staticPopErrorHandling(); + if (PEAR::isError($e)) { + if (!isset($options['ignore-errors'])) { + return $this->raiseError($e); + } + + if (!isset($options['soft'])) { + $this->log(0, 'WARNING: ' . $e->getMessage()); + } + } elseif (is_array($e)) { + if (!isset($options['soft'])) { + $this->log(0, $e[0]); + } + } + + $this->pkginfo = &$pkg; + // pretty much nothing happens if we are only registering the uninstall + if (empty($options['register-only'])) { + // Delete the files + $this->startFileTransaction(); + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + if (PEAR::isError($err = $this->_deletePackageFiles($package, $channel))) { + PEAR::popErrorHandling(); + $this->rollbackFileTransaction(); + $this->configSet('default_channel', $savechannel); + if (!isset($options['ignore-errors'])) { + return $this->raiseError($err); + } + + if (!isset($options['soft'])) { + $this->log(0, 'WARNING: ' . $err->getMessage()); + } + } else { + PEAR::popErrorHandling(); + } + + if (!$this->commitFileTransaction()) { + $this->rollbackFileTransaction(); + if (!isset($options['ignore-errors'])) { + return $this->raiseError("uninstall failed"); + } + + if (!isset($options['soft'])) { + $this->log(0, 'WARNING: uninstall failed'); + } + } else { + $this->startFileTransaction(); + $dirtree = $pkg->getDirTree(); + if ($dirtree === false) { + $this->configSet('default_channel', $savechannel); + return $this->_registry->deletePackage($package, $channel); + } + + // attempt to delete empty directories + uksort($dirtree, array($this, '_sortDirs')); + foreach($dirtree as $dir => $notused) { + $this->addFileOperation('rmdir', array($dir)); + } + + if (!$this->commitFileTransaction()) { + $this->rollbackFileTransaction(); + if (!isset($options['ignore-errors'])) { + return $this->raiseError("uninstall failed"); + } + + if (!isset($options['soft'])) { + $this->log(0, 'WARNING: uninstall failed'); + } + } + } + } + + $this->configSet('default_channel', $savechannel); + // Register that the package is no longer installed + return $this->_registry->deletePackage($package, $channel); + } + + /** + * Sort a list of arrays of array(downloaded packagefilename) by dependency. + * + * It also removes duplicate dependencies + * @param array an array of PEAR_PackageFile_v[1/2] objects + * @return array|PEAR_Error array of array(packagefilename, package.xml contents) + */ + function sortPackagesForUninstall(&$packages) + { + $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->config); + if (PEAR::isError($this->_dependencyDB)) { + return $this->_dependencyDB; + } + usort($packages, array(&$this, '_sortUninstall')); + } + + function _sortUninstall($a, $b) + { + if (!$a->getDeps() && !$b->getDeps()) { + return 0; // neither package has dependencies, order is insignificant + } + if ($a->getDeps() && !$b->getDeps()) { + return -1; // $a must be installed after $b because $a has dependencies + } + if (!$a->getDeps() && $b->getDeps()) { + return 1; // $b must be installed after $a because $b has dependencies + } + // both packages have dependencies + if ($this->_dependencyDB->dependsOn($a, $b)) { + return -1; + } + if ($this->_dependencyDB->dependsOn($b, $a)) { + return 1; + } + return 0; + } + + function _sortDirs($a, $b) + { + if (strnatcmp($a, $b) == -1) return 1; + if (strnatcmp($a, $b) == 1) return -1; + return 0; + } + + function _buildCallback($what, $data) + { + if (($what == 'cmdoutput' && $this->debug > 1) || + ($what == 'output' && $this->debug > 0)) { + $this->ui->outputData(rtrim($data), 'build'); + } + } +} diff --git a/includes/pear/PEAR/Installer/Role.php b/includes/pear/PEAR/Installer/Role.php new file mode 100644 index 0000000..2eba3da --- /dev/null +++ b/includes/pear/PEAR/Installer/Role.php @@ -0,0 +1,267 @@ + + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a1 + */ + +/** + * base class for installer roles + */ +require_once 'PEAR/Installer/Role/Common.php'; +require_once 'PEAR/XMLParser.php'; +/** + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a1 + */ +class PEAR_Installer_Role +{ + /** + * Set up any additional configuration variables that file roles require + * + * Never call this directly, it is called by the PEAR_Config constructor + * @param PEAR_Config + */ + public static function initializeConfig(&$config) + { + if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) { + PEAR_Installer_Role::registerRoles(); + } + + foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $class => $info) { + if (!$info['config_vars']) { + continue; + } + + $config->_addConfigVars($class, $info['config_vars']); + } + } + + /** + * @param PEAR_PackageFile_v2 + * @param string role name + * @param PEAR_Config + * @return PEAR_Installer_Role_Common + */ + public static function &factory($pkg, $role, &$config) + { + if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) { + PEAR_Installer_Role::registerRoles(); + } + + if (!in_array($role, PEAR_Installer_Role::getValidRoles($pkg->getPackageType()))) { + $a = false; + return $a; + } + + $a = 'PEAR_Installer_Role_' . ucfirst($role); + if (!class_exists($a)) { + require_once str_replace('_', '/', $a) . '.php'; + } + + $b = new $a($config); + return $b; + } + + /** + * Get a list of file roles that are valid for the particular release type. + * + * For instance, src files serve no purpose in regular php releases. + * @param string + * @param bool clear cache + * @return array + */ + public static function getValidRoles($release, $clear = false) + { + if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) { + PEAR_Installer_Role::registerRoles(); + } + + static $ret = array(); + if ($clear) { + $ret = array(); + } + + if (isset($ret[$release])) { + return $ret[$release]; + } + + $ret[$release] = array(); + foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) { + if (in_array($release, $okreleases['releasetypes'])) { + $ret[$release][] = strtolower(str_replace('PEAR_Installer_Role_', '', $role)); + } + } + + return $ret[$release]; + } + + /** + * Get a list of roles that require their files to be installed + * + * Most roles must be installed, but src and package roles, for instance + * are pseudo-roles. src files are compiled into a new extension. Package + * roles are actually fully bundled releases of a package + * @param bool clear cache + * @return array + */ + public static function getInstallableRoles($clear = false) + { + if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) { + PEAR_Installer_Role::registerRoles(); + } + + static $ret; + if ($clear) { + unset($ret); + } + + if (isset($ret)) { + return $ret; + } + + $ret = array(); + foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) { + if ($okreleases['installable']) { + $ret[] = strtolower(str_replace('PEAR_Installer_Role_', '', $role)); + } + } + + return $ret; + } + + /** + * Return an array of roles that are affected by the baseinstalldir attribute + * + * Most roles ignore this attribute, and instead install directly into: + * PackageName/filepath + * so a tests file tests/file.phpt is installed into PackageName/tests/filepath.php + * @param bool clear cache + * @return array + */ + public static function getBaseinstallRoles($clear = false) + { + if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) { + PEAR_Installer_Role::registerRoles(); + } + + static $ret; + if ($clear) { + unset($ret); + } + + if (isset($ret)) { + return $ret; + } + + $ret = array(); + foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) { + if ($okreleases['honorsbaseinstall']) { + $ret[] = strtolower(str_replace('PEAR_Installer_Role_', '', $role)); + } + } + + return $ret; + } + + /** + * Return an array of file roles that should be analyzed for PHP content at package time, + * like the "php" role. + * @param bool clear cache + * @return array + */ + public static function getPhpRoles($clear = false) + { + if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) { + PEAR_Installer_Role::registerRoles(); + } + + static $ret; + if ($clear) { + unset($ret); + } + + if (isset($ret)) { + return $ret; + } + + $ret = array(); + foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) { + if ($okreleases['phpfile']) { + $ret[] = strtolower(str_replace('PEAR_Installer_Role_', '', $role)); + } + } + + return $ret; + } + + /** + * Scan through the Command directory looking for classes + * and see what commands they implement. + * @param string which directory to look for classes, defaults to + * the Installer/Roles subdirectory of + * the directory from where this file (__FILE__) is + * included. + * + * @return bool TRUE on success, a PEAR error on failure + */ + public static function registerRoles($dir = null) + { + $GLOBALS['_PEAR_INSTALLER_ROLES'] = array(); + $parser = new PEAR_XMLParser; + if ($dir === null) { + $dir = dirname(__FILE__) . '/Role'; + } + + if (!file_exists($dir) || !is_dir($dir)) { + return PEAR::raiseError("registerRoles: opendir($dir) failed: does not exist/is not directory"); + } + + $dp = @opendir($dir); + if (empty($dp)) { + return PEAR::raiseError("registerRoles: opendir($dir) failed: $php_errmsg"); + } + + while ($entry = readdir($dp)) { + if ($entry{0} == '.' || substr($entry, -4) != '.xml') { + continue; + } + + $class = "PEAR_Installer_Role_".substr($entry, 0, -4); + // List of roles + if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'][$class])) { + $file = "$dir/$entry"; + $parser->parse(file_get_contents($file)); + $data = $parser->getData(); + if (!is_array($data['releasetypes'])) { + $data['releasetypes'] = array($data['releasetypes']); + } + + $GLOBALS['_PEAR_INSTALLER_ROLES'][$class] = $data; + } + } + + closedir($dp); + ksort($GLOBALS['_PEAR_INSTALLER_ROLES']); + PEAR_Installer_Role::getBaseinstallRoles(true); + PEAR_Installer_Role::getInstallableRoles(true); + PEAR_Installer_Role::getPhpRoles(true); + PEAR_Installer_Role::getValidRoles('****', true); + return true; + } +} diff --git a/includes/pear/PEAR/Installer/Role/Cfg.php b/includes/pear/PEAR/Installer/Role/Cfg.php new file mode 100644 index 0000000..23af39c --- /dev/null +++ b/includes/pear/PEAR/Installer/Role/Cfg.php @@ -0,0 +1,106 @@ + + * @copyright 2007-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.7.0 + */ + +/** + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 2007-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.7.0 + */ +class PEAR_Installer_Role_Cfg extends PEAR_Installer_Role_Common +{ + /** + * @var PEAR_Installer + */ + var $installer; + + /** + * the md5 of the original file + * + * @var unknown_type + */ + var $md5 = null; + + /** + * Do any unusual setup here + * @param PEAR_Installer + * @param PEAR_PackageFile_v2 + * @param array file attributes + * @param string file name + */ + function setup(&$installer, $pkg, $atts, $file) + { + $this->installer = &$installer; + $reg = &$this->installer->config->getRegistry(); + $package = $reg->getPackage($pkg->getPackage(), $pkg->getChannel()); + if ($package) { + $filelist = $package->getFilelist(); + if (isset($filelist[$file]) && isset($filelist[$file]['md5sum'])) { + $this->md5 = $filelist[$file]['md5sum']; + } + } + } + + function processInstallation($pkg, $atts, $file, $tmp_path, $layer = null) + { + $test = parent::processInstallation($pkg, $atts, $file, $tmp_path, $layer); + if (@file_exists($test[2]) && @file_exists($test[3])) { + $md5 = md5_file($test[2]); + // configuration has already been installed, check for mods + if ($md5 !== $this->md5 && $md5 !== md5_file($test[3])) { + // configuration has been modified, so save our version as + // configfile-version + $old = $test[2]; + $test[2] .= '.new-' . $pkg->getVersion(); + // backup original and re-install it + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + $tmpcfg = $this->config->get('temp_dir'); + $newloc = System::mkdir(array('-p', $tmpcfg)); + if (!$newloc) { + // try temp_dir + $newloc = System::mktemp(array('-d')); + if (!$newloc || PEAR::isError($newloc)) { + PEAR::popErrorHandling(); + return PEAR::raiseError('Could not save existing configuration file '. + $old . ', unable to install. Please set temp_dir ' . + 'configuration variable to a writeable location and try again'); + } + } else { + $newloc = $tmpcfg; + } + + $temp_file = $newloc . DIRECTORY_SEPARATOR . uniqid('savefile'); + if (!@copy($old, $temp_file)) { + PEAR::popErrorHandling(); + return PEAR::raiseError('Could not save existing configuration file '. + $old . ', unable to install. Please set temp_dir ' . + 'configuration variable to a writeable location and try again'); + } + + PEAR::popErrorHandling(); + $this->installer->log(0, "WARNING: configuration file $old is being installed as $test[2], you should manually merge in changes to the existing configuration file"); + $this->installer->addFileOperation('rename', array($temp_file, $old, false)); + $this->installer->addFileOperation('delete', array($temp_file)); + } + } + + return $test; + } +} \ No newline at end of file diff --git a/includes/pear/PEAR/Installer/Role/Cfg.xml b/includes/pear/PEAR/Installer/Role/Cfg.xml new file mode 100644 index 0000000..7a415dc --- /dev/null +++ b/includes/pear/PEAR/Installer/Role/Cfg.xml @@ -0,0 +1,15 @@ + + php + extsrc + extbin + zendextsrc + zendextbin + 1 + cfg_dir + + 1 + + + + + \ No newline at end of file diff --git a/includes/pear/PEAR/Installer/Role/Common.php b/includes/pear/PEAR/Installer/Role/Common.php new file mode 100644 index 0000000..b2b2a61 --- /dev/null +++ b/includes/pear/PEAR/Installer/Role/Common.php @@ -0,0 +1,189 @@ + + * @copyright 1997-2006 The PHP Group + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a1 + */ +/** + * Base class for all installation roles. + * + * This class allows extensibility of file roles. Packages with complex + * customization can now provide custom file roles along with the possibility of + * adding configuration values to match. + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2006 The PHP Group + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a1 + */ +class PEAR_Installer_Role_Common +{ + /** + * @var PEAR_Config + * @access protected + */ + var $config; + + /** + * @param PEAR_Config + */ + function PEAR_Installer_Role_Common(&$config) + { + $this->config = $config; + } + + /** + * Retrieve configuration information about a file role from its XML info + * + * @param string $role Role Classname, as in "PEAR_Installer_Role_Data" + * @return array + */ + function getInfo($role) + { + if (empty($GLOBALS['_PEAR_INSTALLER_ROLES'][$role])) { + return PEAR::raiseError('Unknown Role class: "' . $role . '"'); + } + return $GLOBALS['_PEAR_INSTALLER_ROLES'][$role]; + } + + /** + * This is called for each file to set up the directories and files + * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 + * @param array attributes from the tag + * @param string file name + * @return array an array consisting of: + * + * 1 the original, pre-baseinstalldir installation directory + * 2 the final installation directory + * 3 the full path to the final location of the file + * 4 the location of the pre-installation file + */ + function processInstallation($pkg, $atts, $file, $tmp_path, $layer = null) + { + $role = $this->getRoleFromClass(); + $info = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . $role); + if (PEAR::isError($info)) { + return $info; + } + + if (!$info['locationconfig']) { + return false; + } + + if ($info['honorsbaseinstall']) { + $dest_dir = $save_destdir = $this->config->get($info['locationconfig'], $layer, + $pkg->getChannel()); + if (!empty($atts['baseinstalldir'])) { + $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir']; + } + } elseif ($info['unusualbaseinstall']) { + $dest_dir = $save_destdir = $this->config->get($info['locationconfig'], + $layer, $pkg->getChannel()) . DIRECTORY_SEPARATOR . $pkg->getPackage(); + if (!empty($atts['baseinstalldir'])) { + $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir']; + } + } else { + $save_destdir = $dest_dir = $this->config->get($info['locationconfig'], + $layer, $pkg->getChannel()) . DIRECTORY_SEPARATOR . $pkg->getPackage(); + } + + if (dirname($file) != '.' && empty($atts['install-as'])) { + $dest_dir .= DIRECTORY_SEPARATOR . dirname($file); + } + + if (empty($atts['install-as'])) { + $dest_file = $dest_dir . DIRECTORY_SEPARATOR . basename($file); + } else { + $dest_file = $dest_dir . DIRECTORY_SEPARATOR . $atts['install-as']; + } + + $orig_file = $tmp_path . DIRECTORY_SEPARATOR . $file; + + // Clean up the DIRECTORY_SEPARATOR mess + $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR; + + list($dest_dir, $dest_file, $orig_file) = preg_replace(array('!\\\\+!', '!/!', "!$ds2+!"), + array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR, + DIRECTORY_SEPARATOR), + array($dest_dir, $dest_file, $orig_file)); + + return array($save_destdir, $dest_dir, $dest_file, $orig_file); + } + + /** + * Get the name of the configuration variable that specifies the location of this file + * @return string|false + */ + function getLocationConfig() + { + $role = ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this)))); + $info = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . $role); + if (PEAR::isError($info)) { + return $info; + } + + return $info['locationconfig']; + } + + /** + * Do any unusual setup here + * @param PEAR_Installer + * @param PEAR_PackageFile_v2 + * @param array file attributes + * @param string file name + */ + function setup(&$installer, $pkg, $atts, $file) + { + } + + function isExecutable() + { + $role = $this->getRoleFromClass(); + $info = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . $role); + if (PEAR::isError($info)) { + return $info; + } + + return $info['executable']; + } + + function isInstallable() + { + $role = $this->getRoleFromClass(); + $info = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . $role); + if (PEAR::isError($info)) { + return $info; + } + + return $info['installable']; + } + + function isExtension() + { + $role = $this->getRoleFromClass(); + $info = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . $role); + if (PEAR::isError($info)) { + return $info; + } + + return $info['phpextension']; + } + + function getRoleFromClass() + { + $lower = strtolower(get_class($this)); + return ucfirst(str_replace('pear_installer_role_', '', $lower)); + } +} \ No newline at end of file diff --git a/includes/pear/PEAR/Installer/Role/Data.php b/includes/pear/PEAR/Installer/Role/Data.php new file mode 100644 index 0000000..27d05d4 --- /dev/null +++ b/includes/pear/PEAR/Installer/Role/Data.php @@ -0,0 +1,28 @@ + + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a1 + */ + +/** + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a1 + */ +class PEAR_Installer_Role_Data extends PEAR_Installer_Role_Common {} +?> \ No newline at end of file diff --git a/includes/pear/PEAR/Installer/Role/Data.xml b/includes/pear/PEAR/Installer/Role/Data.xml new file mode 100644 index 0000000..eae6372 --- /dev/null +++ b/includes/pear/PEAR/Installer/Role/Data.xml @@ -0,0 +1,15 @@ + + php + extsrc + extbin + zendextsrc + zendextbin + 1 + data_dir + + + + + + + \ No newline at end of file diff --git a/includes/pear/PEAR/Installer/Role/Doc.php b/includes/pear/PEAR/Installer/Role/Doc.php new file mode 100644 index 0000000..5194e6e --- /dev/null +++ b/includes/pear/PEAR/Installer/Role/Doc.php @@ -0,0 +1,28 @@ + + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a1 + */ + +/** + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a1 + */ +class PEAR_Installer_Role_Doc extends PEAR_Installer_Role_Common {} +?> \ No newline at end of file diff --git a/includes/pear/PEAR/Installer/Role/Doc.xml b/includes/pear/PEAR/Installer/Role/Doc.xml new file mode 100644 index 0000000..173afba --- /dev/null +++ b/includes/pear/PEAR/Installer/Role/Doc.xml @@ -0,0 +1,15 @@ + + php + extsrc + extbin + zendextsrc + zendextbin + 1 + doc_dir + + + + + + + \ No newline at end of file diff --git a/includes/pear/PEAR/Installer/Role/Ext.php b/includes/pear/PEAR/Installer/Role/Ext.php new file mode 100644 index 0000000..ad8988c --- /dev/null +++ b/includes/pear/PEAR/Installer/Role/Ext.php @@ -0,0 +1,28 @@ + + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a1 + */ + +/** + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a1 + */ +class PEAR_Installer_Role_Ext extends PEAR_Installer_Role_Common {} +?> \ No newline at end of file diff --git a/includes/pear/PEAR/Installer/Role/Ext.xml b/includes/pear/PEAR/Installer/Role/Ext.xml new file mode 100644 index 0000000..e2940fe --- /dev/null +++ b/includes/pear/PEAR/Installer/Role/Ext.xml @@ -0,0 +1,12 @@ + + extbin + zendextbin + 1 + ext_dir + 1 + + + + 1 + + \ No newline at end of file diff --git a/includes/pear/PEAR/Installer/Role/Php.php b/includes/pear/PEAR/Installer/Role/Php.php new file mode 100644 index 0000000..ee7f935 --- /dev/null +++ b/includes/pear/PEAR/Installer/Role/Php.php @@ -0,0 +1,28 @@ + + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a1 + */ + +/** + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a1 + */ +class PEAR_Installer_Role_Php extends PEAR_Installer_Role_Common {} +?> \ No newline at end of file diff --git a/includes/pear/PEAR/Installer/Role/Php.xml b/includes/pear/PEAR/Installer/Role/Php.xml new file mode 100644 index 0000000..6b9a0e6 --- /dev/null +++ b/includes/pear/PEAR/Installer/Role/Php.xml @@ -0,0 +1,15 @@ + + php + extsrc + extbin + zendextsrc + zendextbin + 1 + php_dir + 1 + + 1 + + + + \ No newline at end of file diff --git a/includes/pear/PEAR/Installer/Role/Script.php b/includes/pear/PEAR/Installer/Role/Script.php new file mode 100644 index 0000000..8259305 --- /dev/null +++ b/includes/pear/PEAR/Installer/Role/Script.php @@ -0,0 +1,28 @@ + + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a1 + */ + +/** + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a1 + */ +class PEAR_Installer_Role_Script extends PEAR_Installer_Role_Common {} +?> \ No newline at end of file diff --git a/includes/pear/PEAR/Installer/Role/Script.xml b/includes/pear/PEAR/Installer/Role/Script.xml new file mode 100644 index 0000000..e732cf2 --- /dev/null +++ b/includes/pear/PEAR/Installer/Role/Script.xml @@ -0,0 +1,15 @@ + + php + extsrc + extbin + zendextsrc + zendextbin + 1 + bin_dir + 1 + + + 1 + + + \ No newline at end of file diff --git a/includes/pear/PEAR/Installer/Role/Src.php b/includes/pear/PEAR/Installer/Role/Src.php new file mode 100644 index 0000000..3d114d4 --- /dev/null +++ b/includes/pear/PEAR/Installer/Role/Src.php @@ -0,0 +1,34 @@ + + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a1 + */ + +/** + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a1 + */ +class PEAR_Installer_Role_Src extends PEAR_Installer_Role_Common +{ + function setup(&$installer, $pkg, $atts, $file) + { + $installer->source_files++; + } +} +?> \ No newline at end of file diff --git a/includes/pear/PEAR/Installer/Role/Src.xml b/includes/pear/PEAR/Installer/Role/Src.xml new file mode 100644 index 0000000..1034834 --- /dev/null +++ b/includes/pear/PEAR/Installer/Role/Src.xml @@ -0,0 +1,12 @@ + + extsrc + zendextsrc + 1 + temp_dir + + + + + + + \ No newline at end of file diff --git a/includes/pear/PEAR/Installer/Role/Test.php b/includes/pear/PEAR/Installer/Role/Test.php new file mode 100644 index 0000000..06747f7 --- /dev/null +++ b/includes/pear/PEAR/Installer/Role/Test.php @@ -0,0 +1,28 @@ + + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a1 + */ + +/** + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a1 + */ +class PEAR_Installer_Role_Test extends PEAR_Installer_Role_Common {} +?> \ No newline at end of file diff --git a/includes/pear/PEAR/Installer/Role/Test.xml b/includes/pear/PEAR/Installer/Role/Test.xml new file mode 100644 index 0000000..51d5b89 --- /dev/null +++ b/includes/pear/PEAR/Installer/Role/Test.xml @@ -0,0 +1,15 @@ + + php + extsrc + extbin + zendextsrc + zendextbin + 1 + test_dir + + + + + + + \ No newline at end of file diff --git a/includes/pear/PEAR/Installer/Role/Www.php b/includes/pear/PEAR/Installer/Role/Www.php new file mode 100644 index 0000000..c463c8b --- /dev/null +++ b/includes/pear/PEAR/Installer/Role/Www.php @@ -0,0 +1,28 @@ + + * @copyright 2007-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.7.0 + */ + +/** + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 2007-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.7.0 + */ +class PEAR_Installer_Role_Www extends PEAR_Installer_Role_Common {} +?> \ No newline at end of file diff --git a/includes/pear/PEAR/Installer/Role/Www.xml b/includes/pear/PEAR/Installer/Role/Www.xml new file mode 100644 index 0000000..7598be3 --- /dev/null +++ b/includes/pear/PEAR/Installer/Role/Www.xml @@ -0,0 +1,15 @@ + + php + extsrc + extbin + zendextsrc + zendextbin + 1 + www_dir + 1 + + + + + + \ No newline at end of file diff --git a/includes/pear/PEAR/PackageFile.php b/includes/pear/PEAR/PackageFile.php new file mode 100644 index 0000000..2a29145 --- /dev/null +++ b/includes/pear/PEAR/PackageFile.php @@ -0,0 +1,503 @@ + + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a1 + */ + +/** + * needed for PEAR_VALIDATE_* constants + */ +require_once 'PEAR/Validate.php'; +/** + * Error code if the package.xml tag does not contain a valid version + */ +define('PEAR_PACKAGEFILE_ERROR_NO_PACKAGEVERSION', 1); +/** + * Error code if the package.xml tag version is not supported (version 1.0 and 1.1 are the only supported versions, + * currently + */ +define('PEAR_PACKAGEFILE_ERROR_INVALID_PACKAGEVERSION', 2); +/** + * Abstraction for the package.xml package description file + * + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @PEAR-VER@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a1 + */ +class PEAR_PackageFile +{ + /** + * @var PEAR_Config + */ + var $_config; + var $_debug; + + var $_logger = false; + /** + * @var boolean + */ + var $_rawReturn = false; + + /** + * helper for extracting Archive_Tar errors + * @var array + * @access private + */ + var $_extractErrors = array(); + + /** + * + * @param PEAR_Config $config + * @param ? $debug + * @param string @tmpdir Optional temporary directory for uncompressing + * files + */ + function PEAR_PackageFile(&$config, $debug = false) + { + $this->_config = $config; + $this->_debug = $debug; + } + + /** + * Turn off validation - return a parsed package.xml without checking it + * + * This is used by the package-validate command + */ + function rawReturn() + { + $this->_rawReturn = true; + } + + function setLogger(&$l) + { + $this->_logger = &$l; + } + + /** + * Create a PEAR_PackageFile_Parser_v* of a given version. + * @param int $version + * @return PEAR_PackageFile_Parser_v1|PEAR_PackageFile_Parser_v1 + */ + function &parserFactory($version) + { + if (!in_array($version{0}, array('1', '2'))) { + $a = false; + return $a; + } + + include_once 'PEAR/PackageFile/Parser/v' . $version{0} . '.php'; + $version = $version{0}; + $class = "PEAR_PackageFile_Parser_v$version"; + $a = new $class; + return $a; + } + + /** + * For simpler unit-testing + * @return string + */ + function getClassPrefix() + { + return 'PEAR_PackageFile_v'; + } + + /** + * Create a PEAR_PackageFile_v* of a given version. + * @param int $version + * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|false + */ + function &factory($version) + { + if (!in_array($version{0}, array('1', '2'))) { + $a = false; + return $a; + } + + include_once 'PEAR/PackageFile/v' . $version{0} . '.php'; + $version = $version{0}; + $class = $this->getClassPrefix() . $version; + $a = new $class; + return $a; + } + + /** + * Create a PEAR_PackageFile_v* from its toArray() method + * + * WARNING: no validation is performed, the array is assumed to be valid, + * always parse from xml if you want validation. + * @param array $arr + * @return PEAR_PackageFileManager_v1|PEAR_PackageFileManager_v2|PEAR_Error + * @uses factory() to construct the returned object. + */ + function &fromArray($arr) + { + if (isset($arr['xsdversion'])) { + $obj = &$this->factory($arr['xsdversion']); + if ($this->_logger) { + $obj->setLogger($this->_logger); + } + + $obj->setConfig($this->_config); + $obj->fromArray($arr); + return $obj; + } + + // The extensive tests are necessary due to changes in PHP 5.4. + if (is_array($arr) + && array_key_exists('package', $arr) + && is_array($arr['package']) + && array_key_exists('attribs', $arr['package']) + && is_array($arr['package']['attribs']) + && array_key_exists('version', $arr['package']['attribs']) + && !empty($arr['package']['attribs']['version'])) + { + $obj = &$this->factory($arr['package']['attribs']['version']); + } else { + $obj = &$this->factory('1.0'); + } + if (!$obj) { + return PEAR::raiseError('Invalid package version.'); + } + + if ($this->_logger) { + $obj->setLogger($this->_logger); + } + + $obj->setConfig($this->_config); + $obj->fromArray($arr); + return $obj; + } + + /** + * Create a PEAR_PackageFile_v* from an XML string. + * @access public + * @param string $data contents of package.xml file + * @param int $state package state (one of PEAR_VALIDATE_* constants) + * @param string $file full path to the package.xml file (and the files + * it references) + * @param string $archive optional name of the archive that the XML was + * extracted from, if any + * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2 + * @uses parserFactory() to construct a parser to load the package. + */ + function &fromXmlString($data, $state, $file, $archive = false) + { + if (preg_match('/]+version=[\'"]([0-9]+\.[0-9]+)[\'"]/', $data, $packageversion)) { + if (!in_array($packageversion[1], array('1.0', '2.0', '2.1'))) { + return PEAR::raiseError('package.xml version "' . $packageversion[1] . + '" is not supported, only 1.0, 2.0, and 2.1 are supported.'); + } + + $object = &$this->parserFactory($packageversion[1]); + if ($this->_logger) { + $object->setLogger($this->_logger); + } + + $object->setConfig($this->_config); + $pf = $object->parse($data, $file, $archive); + if (PEAR::isError($pf)) { + return $pf; + } + + if ($this->_rawReturn) { + return $pf; + } + + if (!$pf->validate($state)) {; + if ($this->_config->get('verbose') > 0 + && $this->_logger && $pf->getValidationWarnings(false) + ) { + foreach ($pf->getValidationWarnings(false) as $warning) { + $this->_logger->log(0, 'ERROR: ' . $warning['message']); + } + } + + $a = PEAR::raiseError('Parsing of package.xml from file "' . $file . '" failed', + 2, null, null, $pf->getValidationWarnings()); + return $a; + } + + if ($this->_logger && $pf->getValidationWarnings(false)) { + foreach ($pf->getValidationWarnings() as $warning) { + $this->_logger->log(0, 'WARNING: ' . $warning['message']); + } + } + + if (method_exists($pf, 'flattenFilelist')) { + $pf->flattenFilelist(); // for v2 + } + + return $pf; + } elseif (preg_match('/]+version=[\'"]([^"\']+)[\'"]/', $data, $packageversion)) { + $a = PEAR::raiseError('package.xml file "' . $file . + '" has unsupported package.xml version "' . $packageversion[1] . '"'); + return $a; + } else { + if (!class_exists('PEAR_ErrorStack')) { + require_once 'PEAR/ErrorStack.php'; + } + + PEAR_ErrorStack::staticPush('PEAR_PackageFile', + PEAR_PACKAGEFILE_ERROR_NO_PACKAGEVERSION, + 'warning', array('xml' => $data), 'package.xml "' . $file . + '" has no package.xml version'); + $object = &$this->parserFactory('1.0'); + $object->setConfig($this->_config); + $pf = $object->parse($data, $file, $archive); + if (PEAR::isError($pf)) { + return $pf; + } + + if ($this->_rawReturn) { + return $pf; + } + + if (!$pf->validate($state)) { + $a = PEAR::raiseError('Parsing of package.xml from file "' . $file . '" failed', + 2, null, null, $pf->getValidationWarnings()); + return $a; + } + + if ($this->_logger && $pf->getValidationWarnings(false)) { + foreach ($pf->getValidationWarnings() as $warning) { + $this->_logger->log(0, 'WARNING: ' . $warning['message']); + } + } + + if (method_exists($pf, 'flattenFilelist')) { + $pf->flattenFilelist(); // for v2 + } + + return $pf; + } + } + + /** + * Register a temporary file or directory. When the destructor is + * executed, all registered temporary files and directories are + * removed. + * + * @param string $file name of file or directory + * @return void + */ + function addTempFile($file) + { + $GLOBALS['_PEAR_Common_tempfiles'][] = $file; + } + + /** + * Create a PEAR_PackageFile_v* from a compresed Tar or Tgz file. + * @access public + * @param string contents of package.xml file + * @param int package state (one of PEAR_VALIDATE_* constants) + * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2 + * @using Archive_Tar to extract the files + * @using fromPackageFile() to load the package after the package.xml + * file is extracted. + */ + function &fromTgzFile($file, $state) + { + if (!class_exists('Archive_Tar')) { + require_once 'Archive/Tar.php'; + } + + $tar = new Archive_Tar($file); + if ($this->_debug <= 1) { + $tar->pushErrorHandling(PEAR_ERROR_RETURN); + } + + $content = $tar->listContent(); + if ($this->_debug <= 1) { + $tar->popErrorHandling(); + } + + if (!is_array($content)) { + if (is_string($file) && strlen($file < 255) && + (!file_exists($file) || !@is_file($file))) { + $ret = PEAR::raiseError("could not open file \"$file\""); + return $ret; + } + + $file = realpath($file); + $ret = PEAR::raiseError("Could not get contents of package \"$file\"". + '. Invalid tgz file.'); + return $ret; + } + + if (!count($content) && !@is_file($file)) { + $ret = PEAR::raiseError("could not open file \"$file\""); + return $ret; + } + + $xml = null; + $origfile = $file; + foreach ($content as $file) { + $name = $file['filename']; + if ($name == 'package2.xml') { // allow a .tgz to distribute both versions + $xml = $name; + break; + } + + if ($name == 'package.xml') { + $xml = $name; + break; + } elseif (preg_match('/package.xml$/', $name, $match)) { + $xml = $name; + break; + } + } + + $tmpdir = System::mktemp('-t "' . $this->_config->get('temp_dir') . '" -d pear'); + if ($tmpdir === false) { + $ret = PEAR::raiseError("there was a problem with getting the configured temp directory"); + return $ret; + } + + PEAR_PackageFile::addTempFile($tmpdir); + + $this->_extractErrors(); + PEAR::staticPushErrorHandling(PEAR_ERROR_CALLBACK, array($this, '_extractErrors')); + + if (!$xml || !$tar->extractList(array($xml), $tmpdir)) { + $extra = implode("\n", $this->_extractErrors()); + if ($extra) { + $extra = ' ' . $extra; + } + + PEAR::staticPopErrorHandling(); + $ret = PEAR::raiseError('could not extract the package.xml file from "' . + $origfile . '"' . $extra); + return $ret; + } + + PEAR::staticPopErrorHandling(); + $ret = &PEAR_PackageFile::fromPackageFile("$tmpdir/$xml", $state, $origfile); + return $ret; + } + + /** + * helper callback for extracting Archive_Tar errors + * + * @param PEAR_Error|null $err + * @return array + * @access private + */ + function _extractErrors($err = null) + { + static $errors = array(); + if ($err === null) { + $e = $errors; + $errors = array(); + return $e; + } + $errors[] = $err->getMessage(); + } + + /** + * Create a PEAR_PackageFile_v* from a package.xml file. + * + * @access public + * @param string $descfile name of package xml file + * @param int $state package state (one of PEAR_VALIDATE_* constants) + * @param string|false $archive name of the archive this package.xml came + * from, if any + * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2 + * @uses PEAR_PackageFile::fromXmlString to create the oject after the + * XML is loaded from the package.xml file. + */ + function &fromPackageFile($descfile, $state, $archive = false) + { + $fp = false; + if (is_string($descfile) && strlen($descfile) < 255 && + ( + !file_exists($descfile) || !is_file($descfile) || !is_readable($descfile) + || (!$fp = @fopen($descfile, 'r')) + ) + ) { + $a = PEAR::raiseError("Unable to open $descfile"); + return $a; + } + + // read the whole thing so we only get one cdata callback + // for each block of cdata + fclose($fp); + $data = file_get_contents($descfile); + $ret = &PEAR_PackageFile::fromXmlString($data, $state, $descfile, $archive); + return $ret; + } + + /** + * Create a PEAR_PackageFile_v* from a .tgz archive or package.xml file. + * + * This method is able to extract information about a package from a .tgz + * archive or from a XML package definition file. + * + * @access public + * @param string $info file name + * @param int $state package state (one of PEAR_VALIDATE_* constants) + * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2 + * @uses fromPackageFile() if the file appears to be XML + * @uses fromTgzFile() to load all non-XML files + */ + function &fromAnyFile($info, $state) + { + if (is_dir($info)) { + $dir_name = realpath($info); + if (file_exists($dir_name . '/package.xml')) { + $info = PEAR_PackageFile::fromPackageFile($dir_name . '/package.xml', $state); + } elseif (file_exists($dir_name . '/package2.xml')) { + $info = PEAR_PackageFile::fromPackageFile($dir_name . '/package2.xml', $state); + } else { + $info = PEAR::raiseError("No package definition found in '$info' directory"); + } + + return $info; + } + + $fp = false; + if (is_string($info) && strlen($info) < 255 && + (file_exists($info) || ($fp = @fopen($info, 'r'))) + ) { + + if ($fp) { + fclose($fp); + } + + $tmp = substr($info, -4); + if ($tmp == '.xml') { + $info = &PEAR_PackageFile::fromPackageFile($info, $state); + } elseif ($tmp == '.tar' || $tmp == '.tgz') { + $info = &PEAR_PackageFile::fromTgzFile($info, $state); + } else { + $fp = fopen($info, 'r'); + $test = fread($fp, 5); + fclose($fp); + if ($test == ' + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a1 + */ +/** + * needed for PEAR_VALIDATE_* constants + */ +require_once 'PEAR/Validate.php'; +require_once 'System.php'; +require_once 'PEAR/PackageFile/v2.php'; +/** + * This class converts a PEAR_PackageFile_v1 object into any output format. + * + * Supported output formats include array, XML string, and a PEAR_PackageFile_v2 + * object, for converting package.xml 1.0 into package.xml 2.0 with no sweat. + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @PEAR-VER@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a1 + */ +class PEAR_PackageFile_Generator_v1 +{ + /** + * @var PEAR_PackageFile_v1 + */ + var $_packagefile; + function PEAR_PackageFile_Generator_v1(&$packagefile) + { + $this->_packagefile = &$packagefile; + } + + function getPackagerVersion() + { + return '1.9.4'; + } + + /** + * @param PEAR_Packager + * @param bool if true, a .tgz is written, otherwise a .tar is written + * @param string|null directory in which to save the .tgz + * @return string|PEAR_Error location of package or error object + */ + function toTgz(&$packager, $compress = true, $where = null) + { + require_once 'Archive/Tar.php'; + if ($where === null) { + if (!($where = System::mktemp(array('-d')))) { + return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: mktemp failed'); + } + } elseif (!@System::mkDir(array('-p', $where))) { + return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: "' . $where . '" could' . + ' not be created'); + } + if (file_exists($where . DIRECTORY_SEPARATOR . 'package.xml') && + !is_file($where . DIRECTORY_SEPARATOR . 'package.xml')) { + return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: unable to save package.xml as' . + ' "' . $where . DIRECTORY_SEPARATOR . 'package.xml"'); + } + if (!$this->_packagefile->validate(PEAR_VALIDATE_PACKAGING)) { + return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: invalid package file'); + } + $pkginfo = $this->_packagefile->getArray(); + $ext = $compress ? '.tgz' : '.tar'; + $pkgver = $pkginfo['package'] . '-' . $pkginfo['version']; + $dest_package = getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext; + if (file_exists(getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext) && + !is_file(getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext)) { + return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: cannot create tgz file "' . + getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext . '"'); + } + if ($pkgfile = $this->_packagefile->getPackageFile()) { + $pkgdir = dirname(realpath($pkgfile)); + $pkgfile = basename($pkgfile); + } else { + return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: package file object must ' . + 'be created from a real file'); + } + // {{{ Create the package file list + $filelist = array(); + $i = 0; + + foreach ($this->_packagefile->getFilelist() as $fname => $atts) { + $file = $pkgdir . DIRECTORY_SEPARATOR . $fname; + if (!file_exists($file)) { + return PEAR::raiseError("File does not exist: $fname"); + } else { + $filelist[$i++] = $file; + if (!isset($atts['md5sum'])) { + $this->_packagefile->setFileAttribute($fname, 'md5sum', md5_file($file)); + } + $packager->log(2, "Adding file $fname"); + } + } + // }}} + $packagexml = $this->toPackageFile($where, PEAR_VALIDATE_PACKAGING, 'package.xml', true); + if ($packagexml) { + $tar =& new Archive_Tar($dest_package, $compress); + $tar->setErrorHandling(PEAR_ERROR_RETURN); // XXX Don't print errors + // ----- Creates with the package.xml file + $ok = $tar->createModify(array($packagexml), '', $where); + if (PEAR::isError($ok)) { + return $ok; + } elseif (!$ok) { + return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: tarball creation failed'); + } + // ----- Add the content of the package + if (!$tar->addModify($filelist, $pkgver, $pkgdir)) { + return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: tarball creation failed'); + } + return $dest_package; + } + } + + /** + * @param string|null directory to place the package.xml in, or null for a temporary dir + * @param int one of the PEAR_VALIDATE_* constants + * @param string name of the generated file + * @param bool if true, then no analysis will be performed on role="php" files + * @return string|PEAR_Error path to the created file on success + */ + function toPackageFile($where = null, $state = PEAR_VALIDATE_NORMAL, $name = 'package.xml', + $nofilechecking = false) + { + if (!$this->_packagefile->validate($state, $nofilechecking)) { + return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: invalid package.xml', + null, null, null, $this->_packagefile->getValidationWarnings()); + } + if ($where === null) { + if (!($where = System::mktemp(array('-d')))) { + return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: mktemp failed'); + } + } elseif (!@System::mkDir(array('-p', $where))) { + return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: "' . $where . '" could' . + ' not be created'); + } + $newpkgfile = $where . DIRECTORY_SEPARATOR . $name; + $np = @fopen($newpkgfile, 'wb'); + if (!$np) { + return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: unable to save ' . + "$name as $newpkgfile"); + } + fwrite($np, $this->toXml($state, true)); + fclose($np); + return $newpkgfile; + } + + /** + * fix both XML encoding to be UTF8, and replace standard XML entities < > " & ' + * + * @param string $string + * @return string + * @access private + */ + function _fixXmlEncoding($string) + { + if (version_compare(phpversion(), '5.0.0', 'lt')) { + $string = utf8_encode($string); + } + return strtr($string, array( + '&' => '&', + '>' => '>', + '<' => '<', + '"' => '"', + '\'' => ''' )); + } + + /** + * Return an XML document based on the package info (as returned + * by the PEAR_Common::infoFrom* methods). + * + * @return string XML data + */ + function toXml($state = PEAR_VALIDATE_NORMAL, $nofilevalidation = false) + { + $this->_packagefile->setDate(date('Y-m-d')); + if (!$this->_packagefile->validate($state, $nofilevalidation)) { + return false; + } + $pkginfo = $this->_packagefile->getArray(); + static $maint_map = array( + "handle" => "user", + "name" => "name", + "email" => "email", + "role" => "role", + ); + $ret = "\n"; + $ret .= "\n"; + $ret .= "\n" . +" $pkginfo[package]"; + if (isset($pkginfo['extends'])) { + $ret .= "\n$pkginfo[extends]"; + } + $ret .= + "\n ".$this->_fixXmlEncoding($pkginfo['summary'])."\n" . +" ".trim($this->_fixXmlEncoding($pkginfo['description']))."\n \n" . +" \n"; + foreach ($pkginfo['maintainers'] as $maint) { + $ret .= " \n"; + foreach ($maint_map as $idx => $elm) { + $ret .= " <$elm>"; + $ret .= $this->_fixXmlEncoding($maint[$idx]); + $ret .= "\n"; + } + $ret .= " \n"; + } + $ret .= " \n"; + $ret .= $this->_makeReleaseXml($pkginfo, false, $state); + if (isset($pkginfo['changelog']) && count($pkginfo['changelog']) > 0) { + $ret .= " \n"; + foreach ($pkginfo['changelog'] as $oldrelease) { + $ret .= $this->_makeReleaseXml($oldrelease, true); + } + $ret .= " \n"; + } + $ret .= "\n"; + return $ret; + } + + // }}} + // {{{ _makeReleaseXml() + + /** + * Generate part of an XML description with release information. + * + * @param array $pkginfo array with release information + * @param bool $changelog whether the result will be in a changelog element + * + * @return string XML data + * + * @access private + */ + function _makeReleaseXml($pkginfo, $changelog = false, $state = PEAR_VALIDATE_NORMAL) + { + // XXX QUOTE ENTITIES IN PCDATA, OR EMBED IN CDATA BLOCKS!! + $indent = $changelog ? " " : ""; + $ret = "$indent \n"; + if (!empty($pkginfo['version'])) { + $ret .= "$indent $pkginfo[version]\n"; + } + if (!empty($pkginfo['release_date'])) { + $ret .= "$indent $pkginfo[release_date]\n"; + } + if (!empty($pkginfo['release_license'])) { + $ret .= "$indent $pkginfo[release_license]\n"; + } + if (!empty($pkginfo['release_state'])) { + $ret .= "$indent $pkginfo[release_state]\n"; + } + if (!empty($pkginfo['release_notes'])) { + $ret .= "$indent ".trim($this->_fixXmlEncoding($pkginfo['release_notes'])) + ."\n$indent \n"; + } + if (!empty($pkginfo['release_warnings'])) { + $ret .= "$indent ".$this->_fixXmlEncoding($pkginfo['release_warnings'])."\n"; + } + if (isset($pkginfo['release_deps']) && sizeof($pkginfo['release_deps']) > 0) { + $ret .= "$indent \n"; + foreach ($pkginfo['release_deps'] as $dep) { + $ret .= "$indent _fixXmlEncoding($c['name']) . "\""; + if (isset($c['default'])) { + $ret .= " default=\"" . $this->_fixXmlEncoding($c['default']) . "\""; + } + $ret .= " prompt=\"" . $this->_fixXmlEncoding($c['prompt']) . "\""; + $ret .= "/>\n"; + } + $ret .= "$indent \n"; + } + if (isset($pkginfo['provides'])) { + foreach ($pkginfo['provides'] as $key => $what) { + $ret .= "$indent recursiveXmlFilelist($pkginfo['filelist']); + } else { + foreach ($pkginfo['filelist'] as $file => $fa) { + if (!isset($fa['role'])) { + $fa['role'] = ''; + } + $ret .= "$indent _fixXmlEncoding($fa['baseinstalldir']) . '"'; + } + if (isset($fa['md5sum'])) { + $ret .= " md5sum=\"$fa[md5sum]\""; + } + if (isset($fa['platform'])) { + $ret .= " platform=\"$fa[platform]\""; + } + if (!empty($fa['install-as'])) { + $ret .= ' install-as="' . + $this->_fixXmlEncoding($fa['install-as']) . '"'; + } + $ret .= ' name="' . $this->_fixXmlEncoding($file) . '"'; + if (empty($fa['replacements'])) { + $ret .= "/>\n"; + } else { + $ret .= ">\n"; + foreach ($fa['replacements'] as $r) { + $ret .= "$indent $v) { + $ret .= " $k=\"" . $this->_fixXmlEncoding($v) .'"'; + } + $ret .= "/>\n"; + } + $ret .= "$indent \n"; + } + } + } + $ret .= "$indent \n"; + } + $ret .= "$indent \n"; + return $ret; + } + + /** + * @param array + * @access protected + */ + function recursiveXmlFilelist($list) + { + $this->_dirs = array(); + foreach ($list as $file => $attributes) { + $this->_addDir($this->_dirs, explode('/', dirname($file)), $file, $attributes); + } + return $this->_formatDir($this->_dirs); + } + + /** + * @param array + * @param array + * @param string|null + * @param array|null + * @access private + */ + function _addDir(&$dirs, $dir, $file = null, $attributes = null) + { + if ($dir == array() || $dir == array('.')) { + $dirs['files'][basename($file)] = $attributes; + return; + } + $curdir = array_shift($dir); + if (!isset($dirs['dirs'][$curdir])) { + $dirs['dirs'][$curdir] = array(); + } + $this->_addDir($dirs['dirs'][$curdir], $dir, $file, $attributes); + } + + /** + * @param array + * @param string + * @param string + * @access private + */ + function _formatDir($dirs, $indent = '', $curdir = '') + { + $ret = ''; + if (!count($dirs)) { + return ''; + } + if (isset($dirs['dirs'])) { + uksort($dirs['dirs'], 'strnatcasecmp'); + foreach ($dirs['dirs'] as $dir => $contents) { + $usedir = "$curdir/$dir"; + $ret .= "$indent \n"; + $ret .= $this->_formatDir($contents, "$indent ", $usedir); + $ret .= "$indent \n"; + } + } + if (isset($dirs['files'])) { + uksort($dirs['files'], 'strnatcasecmp'); + foreach ($dirs['files'] as $file => $attribs) { + $ret .= $this->_formatFile($file, $attribs, $indent); + } + } + return $ret; + } + + /** + * @param string + * @param array + * @param string + * @access private + */ + function _formatFile($file, $attributes, $indent) + { + $ret = "$indent _fixXmlEncoding($attributes['baseinstalldir']) . '"'; + } + if (isset($attributes['md5sum'])) { + $ret .= " md5sum=\"$attributes[md5sum]\""; + } + if (isset($attributes['platform'])) { + $ret .= " platform=\"$attributes[platform]\""; + } + if (!empty($attributes['install-as'])) { + $ret .= ' install-as="' . + $this->_fixXmlEncoding($attributes['install-as']) . '"'; + } + $ret .= ' name="' . $this->_fixXmlEncoding($file) . '"'; + if (empty($attributes['replacements'])) { + $ret .= "/>\n"; + } else { + $ret .= ">\n"; + foreach ($attributes['replacements'] as $r) { + $ret .= "$indent $v) { + $ret .= " $k=\"" . $this->_fixXmlEncoding($v) .'"'; + } + $ret .= "/>\n"; + } + $ret .= "$indent \n"; + } + return $ret; + } + + // {{{ _unIndent() + + /** + * Unindent given string (?) + * + * @param string $str The string that has to be unindented. + * @return string + * @access private + */ + function _unIndent($str) + { + // remove leading newlines + $str = preg_replace('/^[\r\n]+/', '', $str); + // find whitespace at the beginning of the first line + $indent_len = strspn($str, " \t"); + $indent = substr($str, 0, $indent_len); + $data = ''; + // remove the same amount of whitespace from following lines + foreach (explode("\n", $str) as $line) { + if (substr($line, 0, $indent_len) == $indent) { + $data .= substr($line, $indent_len) . "\n"; + } + } + return $data; + } + + /** + * @return array + */ + function dependenciesToV2() + { + $arr = array(); + $this->_convertDependencies2_0($arr); + return $arr['dependencies']; + } + + /** + * Convert a package.xml version 1.0 into version 2.0 + * + * Note that this does a basic conversion, to allow more advanced + * features like bundles and multiple releases + * @param string the classname to instantiate and return. This must be + * PEAR_PackageFile_v2 or a descendant + * @param boolean if true, only valid, deterministic package.xml 1.0 as defined by the + * strictest parameters will be converted + * @return PEAR_PackageFile_v2|PEAR_Error + */ + function &toV2($class = 'PEAR_PackageFile_v2', $strict = false) + { + if ($strict) { + if (!$this->_packagefile->validate()) { + $a = PEAR::raiseError('invalid package.xml version 1.0 cannot be converted' . + ' to version 2.0', null, null, null, + $this->_packagefile->getValidationWarnings(true)); + return $a; + } + } + + $arr = array( + 'attribs' => array( + 'version' => '2.0', + 'xmlns' => 'http://pear.php.net/dtd/package-2.0', + 'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0', + 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', + 'xsi:schemaLocation' => "http://pear.php.net/dtd/tasks-1.0\n" . +"http://pear.php.net/dtd/tasks-1.0.xsd\n" . +"http://pear.php.net/dtd/package-2.0\n" . +'http://pear.php.net/dtd/package-2.0.xsd', + ), + 'name' => $this->_packagefile->getPackage(), + 'channel' => 'pear.php.net', + ); + $arr['summary'] = $this->_packagefile->getSummary(); + $arr['description'] = $this->_packagefile->getDescription(); + $maintainers = $this->_packagefile->getMaintainers(); + foreach ($maintainers as $maintainer) { + if ($maintainer['role'] != 'lead') { + continue; + } + $new = array( + 'name' => $maintainer['name'], + 'user' => $maintainer['handle'], + 'email' => $maintainer['email'], + 'active' => 'yes', + ); + $arr['lead'][] = $new; + } + + if (!isset($arr['lead'])) { // some people... you know? + $arr['lead'] = array( + 'name' => 'unknown', + 'user' => 'unknown', + 'email' => 'noleadmaintainer@example.com', + 'active' => 'no', + ); + } + + if (count($arr['lead']) == 1) { + $arr['lead'] = $arr['lead'][0]; + } + + foreach ($maintainers as $maintainer) { + if ($maintainer['role'] == 'lead') { + continue; + } + $new = array( + 'name' => $maintainer['name'], + 'user' => $maintainer['handle'], + 'email' => $maintainer['email'], + 'active' => 'yes', + ); + $arr[$maintainer['role']][] = $new; + } + + if (isset($arr['developer']) && count($arr['developer']) == 1) { + $arr['developer'] = $arr['developer'][0]; + } + + if (isset($arr['contributor']) && count($arr['contributor']) == 1) { + $arr['contributor'] = $arr['contributor'][0]; + } + + if (isset($arr['helper']) && count($arr['helper']) == 1) { + $arr['helper'] = $arr['helper'][0]; + } + + $arr['date'] = $this->_packagefile->getDate(); + $arr['version'] = + array( + 'release' => $this->_packagefile->getVersion(), + 'api' => $this->_packagefile->getVersion(), + ); + $arr['stability'] = + array( + 'release' => $this->_packagefile->getState(), + 'api' => $this->_packagefile->getState(), + ); + $licensemap = + array( + 'php' => 'http://www.php.net/license', + 'php license' => 'http://www.php.net/license', + 'lgpl' => 'http://www.gnu.org/copyleft/lesser.html', + 'bsd' => 'http://www.opensource.org/licenses/bsd-license.php', + 'bsd style' => 'http://www.opensource.org/licenses/bsd-license.php', + 'bsd-style' => 'http://www.opensource.org/licenses/bsd-license.php', + 'mit' => 'http://www.opensource.org/licenses/mit-license.php', + 'gpl' => 'http://www.gnu.org/copyleft/gpl.html', + 'apache' => 'http://www.opensource.org/licenses/apache2.0.php' + ); + + if (isset($licensemap[strtolower($this->_packagefile->getLicense())])) { + $arr['license'] = array( + 'attribs' => array('uri' => + $licensemap[strtolower($this->_packagefile->getLicense())]), + '_content' => $this->_packagefile->getLicense() + ); + } else { + // don't use bogus uri + $arr['license'] = $this->_packagefile->getLicense(); + } + + $arr['notes'] = $this->_packagefile->getNotes(); + $temp = array(); + $arr['contents'] = $this->_convertFilelist2_0($temp); + $this->_convertDependencies2_0($arr); + $release = ($this->_packagefile->getConfigureOptions() || $this->_isExtension) ? + 'extsrcrelease' : 'phprelease'; + if ($release == 'extsrcrelease') { + $arr['channel'] = 'pecl.php.net'; + $arr['providesextension'] = $arr['name']; // assumption + } + + $arr[$release] = array(); + if ($this->_packagefile->getConfigureOptions()) { + $arr[$release]['configureoption'] = $this->_packagefile->getConfigureOptions(); + foreach ($arr[$release]['configureoption'] as $i => $opt) { + $arr[$release]['configureoption'][$i] = array('attribs' => $opt); + } + if (count($arr[$release]['configureoption']) == 1) { + $arr[$release]['configureoption'] = $arr[$release]['configureoption'][0]; + } + } + + $this->_convertRelease2_0($arr[$release], $temp); + if ($release == 'extsrcrelease' && count($arr[$release]) > 1) { + // multiple extsrcrelease tags added in PEAR 1.4.1 + $arr['dependencies']['required']['pearinstaller']['min'] = '1.4.1'; + } + + if ($cl = $this->_packagefile->getChangelog()) { + foreach ($cl as $release) { + $rel = array(); + $rel['version'] = + array( + 'release' => $release['version'], + 'api' => $release['version'], + ); + if (!isset($release['release_state'])) { + $release['release_state'] = 'stable'; + } + + $rel['stability'] = + array( + 'release' => $release['release_state'], + 'api' => $release['release_state'], + ); + if (isset($release['release_date'])) { + $rel['date'] = $release['release_date']; + } else { + $rel['date'] = date('Y-m-d'); + } + + if (isset($release['release_license'])) { + if (isset($licensemap[strtolower($release['release_license'])])) { + $uri = $licensemap[strtolower($release['release_license'])]; + } else { + $uri = 'http://www.example.com'; + } + $rel['license'] = array( + 'attribs' => array('uri' => $uri), + '_content' => $release['release_license'] + ); + } else { + $rel['license'] = $arr['license']; + } + + if (!isset($release['release_notes'])) { + $release['release_notes'] = 'no release notes'; + } + + $rel['notes'] = $release['release_notes']; + $arr['changelog']['release'][] = $rel; + } + } + + $ret = new $class; + $ret->setConfig($this->_packagefile->_config); + if (isset($this->_packagefile->_logger) && is_object($this->_packagefile->_logger)) { + $ret->setLogger($this->_packagefile->_logger); + } + + $ret->fromArray($arr); + return $ret; + } + + /** + * @param array + * @param bool + * @access private + */ + function _convertDependencies2_0(&$release, $internal = false) + { + $peardep = array('pearinstaller' => + array('min' => '1.4.0b1')); // this is a lot safer + $required = $optional = array(); + $release['dependencies'] = array('required' => array()); + if ($this->_packagefile->hasDeps()) { + foreach ($this->_packagefile->getDeps() as $dep) { + if (!isset($dep['optional']) || $dep['optional'] == 'no') { + $required[] = $dep; + } else { + $optional[] = $dep; + } + } + foreach (array('required', 'optional') as $arr) { + $deps = array(); + foreach ($$arr as $dep) { + // organize deps by dependency type and name + if (!isset($deps[$dep['type']])) { + $deps[$dep['type']] = array(); + } + if (isset($dep['name'])) { + $deps[$dep['type']][$dep['name']][] = $dep; + } else { + $deps[$dep['type']][] = $dep; + } + } + do { + if (isset($deps['php'])) { + $php = array(); + if (count($deps['php']) > 1) { + $php = $this->_processPhpDeps($deps['php']); + } else { + if (!isset($deps['php'][0])) { + list($key, $blah) = each ($deps['php']); // stupid buggy versions + $deps['php'] = array($blah[0]); + } + $php = $this->_processDep($deps['php'][0]); + if (!$php) { + break; // poor mans throw + } + } + $release['dependencies'][$arr]['php'] = $php; + } + } while (false); + do { + if (isset($deps['pkg'])) { + $pkg = array(); + $pkg = $this->_processMultipleDepsName($deps['pkg']); + if (!$pkg) { + break; // poor mans throw + } + $release['dependencies'][$arr]['package'] = $pkg; + } + } while (false); + do { + if (isset($deps['ext'])) { + $pkg = array(); + $pkg = $this->_processMultipleDepsName($deps['ext']); + $release['dependencies'][$arr]['extension'] = $pkg; + } + } while (false); + // skip sapi - it's not supported so nobody will have used it + // skip os - it's not supported in 1.0 + } + } + if (isset($release['dependencies']['required'])) { + $release['dependencies']['required'] = + array_merge($peardep, $release['dependencies']['required']); + } else { + $release['dependencies']['required'] = $peardep; + } + if (!isset($release['dependencies']['required']['php'])) { + $release['dependencies']['required']['php'] = + array('min' => '4.0.0'); + } + $order = array(); + $bewm = $release['dependencies']['required']; + $order['php'] = $bewm['php']; + $order['pearinstaller'] = $bewm['pearinstaller']; + isset($bewm['package']) ? $order['package'] = $bewm['package'] :0; + isset($bewm['extension']) ? $order['extension'] = $bewm['extension'] :0; + $release['dependencies']['required'] = $order; + } + + /** + * @param array + * @access private + */ + function _convertFilelist2_0(&$package) + { + $ret = array('dir' => + array( + 'attribs' => array('name' => '/'), + 'file' => array() + ) + ); + $package['platform'] = + $package['install-as'] = array(); + $this->_isExtension = false; + foreach ($this->_packagefile->getFilelist() as $name => $file) { + $file['name'] = $name; + if (isset($file['role']) && $file['role'] == 'src') { + $this->_isExtension = true; + } + if (isset($file['replacements'])) { + $repl = $file['replacements']; + unset($file['replacements']); + } else { + unset($repl); + } + if (isset($file['install-as'])) { + $package['install-as'][$name] = $file['install-as']; + unset($file['install-as']); + } + if (isset($file['platform'])) { + $package['platform'][$name] = $file['platform']; + unset($file['platform']); + } + $file = array('attribs' => $file); + if (isset($repl)) { + foreach ($repl as $replace ) { + $file['tasks:replace'][] = array('attribs' => $replace); + } + if (count($repl) == 1) { + $file['tasks:replace'] = $file['tasks:replace'][0]; + } + } + $ret['dir']['file'][] = $file; + } + return $ret; + } + + /** + * Post-process special files with install-as/platform attributes and + * make the release tag. + * + * This complex method follows this work-flow to create the release tags: + * + *
    +     * - if any install-as/platform exist, create a generic release and fill it with
    +     *   o  tags for 
    +     *   o  tags for 
    +     *   o  tags for 
    +     *   o  tags for 
    +     * - create a release for each platform encountered and fill with
    +     *   o  tags for 
    +     *   o  tags for 
    +     *   o  tags for 
    +     *   o  tags for 
    +     *   o  tags for 
    +     *   o  tags for 
    +     *   o  tags for 
    +     * 
    + * + * It does this by accessing the $package parameter, which contains an array with + * indices: + * + * - platform: mapping of file => OS the file should be installed on + * - install-as: mapping of file => installed name + * - osmap: mapping of OS => list of files that should be installed + * on that OS + * - notosmap: mapping of OS => list of files that should not be + * installed on that OS + * + * @param array + * @param array + * @access private + */ + function _convertRelease2_0(&$release, $package) + { + //- if any install-as/platform exist, create a generic release and fill it with + if (count($package['platform']) || count($package['install-as'])) { + $generic = array(); + $genericIgnore = array(); + foreach ($package['install-as'] as $file => $as) { + //o tags for + if (!isset($package['platform'][$file])) { + $generic[] = $file; + continue; + } + //o tags for + if (isset($package['platform'][$file]) && + $package['platform'][$file]{0} == '!') { + $generic[] = $file; + continue; + } + //o tags for + if (isset($package['platform'][$file]) && + $package['platform'][$file]{0} != '!') { + $genericIgnore[] = $file; + continue; + } + } + foreach ($package['platform'] as $file => $platform) { + if (isset($package['install-as'][$file])) { + continue; + } + if ($platform{0} != '!') { + //o tags for + $genericIgnore[] = $file; + } + } + if (count($package['platform'])) { + $oses = $notplatform = $platform = array(); + foreach ($package['platform'] as $file => $os) { + // get a list of oses + if ($os{0} == '!') { + if (isset($oses[substr($os, 1)])) { + continue; + } + $oses[substr($os, 1)] = count($oses); + } else { + if (isset($oses[$os])) { + continue; + } + $oses[$os] = count($oses); + } + } + //- create a release for each platform encountered and fill with + foreach ($oses as $os => $releaseNum) { + $release[$releaseNum]['installconditions']['os']['name'] = $os; + $release[$releaseNum]['filelist'] = array('install' => array(), + 'ignore' => array()); + foreach ($package['install-as'] as $file => $as) { + //o tags for + if (!isset($package['platform'][$file])) { + $release[$releaseNum]['filelist']['install'][] = + array( + 'attribs' => array( + 'name' => $file, + 'as' => $as, + ), + ); + continue; + } + //o tags for + // + if (isset($package['platform'][$file]) && + $package['platform'][$file] == $os) { + $release[$releaseNum]['filelist']['install'][] = + array( + 'attribs' => array( + 'name' => $file, + 'as' => $as, + ), + ); + continue; + } + //o tags for + // + if (isset($package['platform'][$file]) && + $package['platform'][$file] != "!$os" && + $package['platform'][$file]{0} == '!') { + $release[$releaseNum]['filelist']['install'][] = + array( + 'attribs' => array( + 'name' => $file, + 'as' => $as, + ), + ); + continue; + } + //o tags for + // + if (isset($package['platform'][$file]) && + $package['platform'][$file] == "!$os") { + $release[$releaseNum]['filelist']['ignore'][] = + array( + 'attribs' => array( + 'name' => $file, + ), + ); + continue; + } + //o tags for + // + if (isset($package['platform'][$file]) && + $package['platform'][$file]{0} != '!' && + $package['platform'][$file] != $os) { + $release[$releaseNum]['filelist']['ignore'][] = + array( + 'attribs' => array( + 'name' => $file, + ), + ); + continue; + } + } + foreach ($package['platform'] as $file => $platform) { + if (isset($package['install-as'][$file])) { + continue; + } + //o tags for + if ($platform == "!$os") { + $release[$releaseNum]['filelist']['ignore'][] = + array( + 'attribs' => array( + 'name' => $file, + ), + ); + continue; + } + //o tags for + if ($platform{0} != '!' && $platform != $os) { + $release[$releaseNum]['filelist']['ignore'][] = + array( + 'attribs' => array( + 'name' => $file, + ), + ); + } + } + if (!count($release[$releaseNum]['filelist']['install'])) { + unset($release[$releaseNum]['filelist']['install']); + } + if (!count($release[$releaseNum]['filelist']['ignore'])) { + unset($release[$releaseNum]['filelist']['ignore']); + } + } + if (count($generic) || count($genericIgnore)) { + $release[count($oses)] = array(); + if (count($generic)) { + foreach ($generic as $file) { + if (isset($package['install-as'][$file])) { + $installas = $package['install-as'][$file]; + } else { + $installas = $file; + } + $release[count($oses)]['filelist']['install'][] = + array( + 'attribs' => array( + 'name' => $file, + 'as' => $installas, + ) + ); + } + } + if (count($genericIgnore)) { + foreach ($genericIgnore as $file) { + $release[count($oses)]['filelist']['ignore'][] = + array( + 'attribs' => array( + 'name' => $file, + ) + ); + } + } + } + // cleanup + foreach ($release as $i => $rel) { + if (isset($rel['filelist']['install']) && + count($rel['filelist']['install']) == 1) { + $release[$i]['filelist']['install'] = + $release[$i]['filelist']['install'][0]; + } + if (isset($rel['filelist']['ignore']) && + count($rel['filelist']['ignore']) == 1) { + $release[$i]['filelist']['ignore'] = + $release[$i]['filelist']['ignore'][0]; + } + } + if (count($release) == 1) { + $release = $release[0]; + } + } else { + // no platform atts, but some install-as atts + foreach ($package['install-as'] as $file => $value) { + $release['filelist']['install'][] = + array( + 'attribs' => array( + 'name' => $file, + 'as' => $value + ) + ); + } + if (count($release['filelist']['install']) == 1) { + $release['filelist']['install'] = $release['filelist']['install'][0]; + } + } + } + } + + /** + * @param array + * @return array + * @access private + */ + function _processDep($dep) + { + if ($dep['type'] == 'php') { + if ($dep['rel'] == 'has') { + // come on - everyone has php! + return false; + } + } + $php = array(); + if ($dep['type'] != 'php') { + $php['name'] = $dep['name']; + if ($dep['type'] == 'pkg') { + $php['channel'] = 'pear.php.net'; + } + } + switch ($dep['rel']) { + case 'gt' : + $php['min'] = $dep['version']; + $php['exclude'] = $dep['version']; + break; + case 'ge' : + if (!isset($dep['version'])) { + if ($dep['type'] == 'php') { + if (isset($dep['name'])) { + $dep['version'] = $dep['name']; + } + } + } + $php['min'] = $dep['version']; + break; + case 'lt' : + $php['max'] = $dep['version']; + $php['exclude'] = $dep['version']; + break; + case 'le' : + $php['max'] = $dep['version']; + break; + case 'eq' : + $php['min'] = $dep['version']; + $php['max'] = $dep['version']; + break; + case 'ne' : + $php['exclude'] = $dep['version']; + break; + case 'not' : + $php['conflicts'] = 'yes'; + break; + } + return $php; + } + + /** + * @param array + * @return array + */ + function _processPhpDeps($deps) + { + $test = array(); + foreach ($deps as $dep) { + $test[] = $this->_processDep($dep); + } + $min = array(); + $max = array(); + foreach ($test as $dep) { + if (!$dep) { + continue; + } + if (isset($dep['min'])) { + $min[$dep['min']] = count($min); + } + if (isset($dep['max'])) { + $max[$dep['max']] = count($max); + } + } + if (count($min) > 0) { + uksort($min, 'version_compare'); + } + if (count($max) > 0) { + uksort($max, 'version_compare'); + } + if (count($min)) { + // get the highest minimum + $min = array_pop($a = array_flip($min)); + } else { + $min = false; + } + if (count($max)) { + // get the lowest maximum + $max = array_shift($a = array_flip($max)); + } else { + $max = false; + } + if ($min) { + $php['min'] = $min; + } + if ($max) { + $php['max'] = $max; + } + $exclude = array(); + foreach ($test as $dep) { + if (!isset($dep['exclude'])) { + continue; + } + $exclude[] = $dep['exclude']; + } + if (count($exclude)) { + $php['exclude'] = $exclude; + } + return $php; + } + + /** + * process multiple dependencies that have a name, like package deps + * @param array + * @return array + * @access private + */ + function _processMultipleDepsName($deps) + { + $ret = $tests = array(); + foreach ($deps as $name => $dep) { + foreach ($dep as $d) { + $tests[$name][] = $this->_processDep($d); + } + } + + foreach ($tests as $name => $test) { + $max = $min = $php = array(); + $php['name'] = $name; + foreach ($test as $dep) { + if (!$dep) { + continue; + } + if (isset($dep['channel'])) { + $php['channel'] = 'pear.php.net'; + } + if (isset($dep['conflicts']) && $dep['conflicts'] == 'yes') { + $php['conflicts'] = 'yes'; + } + if (isset($dep['min'])) { + $min[$dep['min']] = count($min); + } + if (isset($dep['max'])) { + $max[$dep['max']] = count($max); + } + } + if (count($min) > 0) { + uksort($min, 'version_compare'); + } + if (count($max) > 0) { + uksort($max, 'version_compare'); + } + if (count($min)) { + // get the highest minimum + $min = array_pop($a = array_flip($min)); + } else { + $min = false; + } + if (count($max)) { + // get the lowest maximum + $max = array_shift($a = array_flip($max)); + } else { + $max = false; + } + if ($min) { + $php['min'] = $min; + } + if ($max) { + $php['max'] = $max; + } + $exclude = array(); + foreach ($test as $dep) { + if (!isset($dep['exclude'])) { + continue; + } + $exclude[] = $dep['exclude']; + } + if (count($exclude)) { + $php['exclude'] = $exclude; + } + $ret[] = $php; + } + return $ret; + } +} +?> \ No newline at end of file diff --git a/includes/pear/PEAR/PackageFile/Generator/v2.php b/includes/pear/PEAR/PackageFile/Generator/v2.php new file mode 100644 index 0000000..8ff90e2 --- /dev/null +++ b/includes/pear/PEAR/PackageFile/Generator/v2.php @@ -0,0 +1,893 @@ + + * @author Stephan Schmidt (original XML_Serializer code) + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a1 + */ +/** + * file/dir manipulation routines + */ +require_once 'System.php'; +require_once 'XML/Util.php'; + +/** + * This class converts a PEAR_PackageFile_v2 object into any output format. + * + * Supported output formats include array, XML string (using S. Schmidt's + * XML_Serializer, slightly customized) + * @category pear + * @package PEAR + * @author Greg Beaver + * @author Stephan Schmidt (original XML_Serializer code) + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @PEAR-VER@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a1 + */ +class PEAR_PackageFile_Generator_v2 +{ + /** + * default options for the serialization + * @access private + * @var array $_defaultOptions + */ + var $_defaultOptions = array( + 'indent' => ' ', // string used for indentation + 'linebreak' => "\n", // string used for newlines + 'typeHints' => false, // automatically add type hin attributes + 'addDecl' => true, // add an XML declaration + 'defaultTagName' => 'XML_Serializer_Tag', // tag used for indexed arrays or invalid names + 'classAsTagName' => false, // use classname for objects in indexed arrays + 'keyAttribute' => '_originalKey', // attribute where original key is stored + 'typeAttribute' => '_type', // attribute for type (only if typeHints => true) + 'classAttribute' => '_class', // attribute for class of objects (only if typeHints => true) + 'scalarAsAttributes' => false, // scalar values (strings, ints,..) will be serialized as attribute + 'prependAttributes' => '', // prepend string for attributes + 'indentAttributes' => false, // indent the attributes, if set to '_auto', it will indent attributes so they all start at the same column + 'mode' => 'simplexml', // use 'simplexml' to use parent name as tagname if transforming an indexed array + 'addDoctype' => false, // add a doctype declaration + 'doctype' => null, // supply a string or an array with id and uri ({@see XML_Util::getDoctypeDeclaration()} + 'rootName' => 'package', // name of the root tag + 'rootAttributes' => array( + 'version' => '2.0', + 'xmlns' => 'http://pear.php.net/dtd/package-2.0', + 'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0', + 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', + 'xsi:schemaLocation' => 'http://pear.php.net/dtd/tasks-1.0 +http://pear.php.net/dtd/tasks-1.0.xsd +http://pear.php.net/dtd/package-2.0 +http://pear.php.net/dtd/package-2.0.xsd', + ), // attributes of the root tag + 'attributesArray' => 'attribs', // all values in this key will be treated as attributes + 'contentName' => '_content', // this value will be used directly as content, instead of creating a new tag, may only be used in conjuction with attributesArray + 'beautifyFilelist' => false, + 'encoding' => 'UTF-8', + ); + + /** + * options for the serialization + * @access private + * @var array $options + */ + var $options = array(); + + /** + * current tag depth + * @var integer $_tagDepth + */ + var $_tagDepth = 0; + + /** + * serilialized representation of the data + * @var string $_serializedData + */ + var $_serializedData = null; + /** + * @var PEAR_PackageFile_v2 + */ + var $_packagefile; + /** + * @param PEAR_PackageFile_v2 + */ + function PEAR_PackageFile_Generator_v2(&$packagefile) + { + $this->_packagefile = &$packagefile; + if (isset($this->_packagefile->encoding)) { + $this->_defaultOptions['encoding'] = $this->_packagefile->encoding; + } + } + + /** + * @return string + */ + function getPackagerVersion() + { + return '1.9.4'; + } + + /** + * @param PEAR_Packager + * @param bool generate a .tgz or a .tar + * @param string|null temporary directory to package in + */ + function toTgz(&$packager, $compress = true, $where = null) + { + $a = null; + return $this->toTgz2($packager, $a, $compress, $where); + } + + /** + * Package up both a package.xml and package2.xml for the same release + * @param PEAR_Packager + * @param PEAR_PackageFile_v1 + * @param bool generate a .tgz or a .tar + * @param string|null temporary directory to package in + */ + function toTgz2(&$packager, &$pf1, $compress = true, $where = null) + { + require_once 'Archive/Tar.php'; + if (!$this->_packagefile->isEquivalent($pf1)) { + return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: "' . + basename($pf1->getPackageFile()) . + '" is not equivalent to "' . basename($this->_packagefile->getPackageFile()) + . '"'); + } + + if ($where === null) { + if (!($where = System::mktemp(array('-d')))) { + return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: mktemp failed'); + } + } elseif (!@System::mkDir(array('-p', $where))) { + return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: "' . $where . '" could' . + ' not be created'); + } + + $file = $where . DIRECTORY_SEPARATOR . 'package.xml'; + if (file_exists($file) && !is_file($file)) { + return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: unable to save package.xml as' . + ' "' . $file .'"'); + } + + if (!$this->_packagefile->validate(PEAR_VALIDATE_PACKAGING)) { + return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: invalid package.xml'); + } + + $ext = $compress ? '.tgz' : '.tar'; + $pkgver = $this->_packagefile->getPackage() . '-' . $this->_packagefile->getVersion(); + $dest_package = getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext; + if (file_exists($dest_package) && !is_file($dest_package)) { + return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: cannot create tgz file "' . + $dest_package . '"'); + } + + $pkgfile = $this->_packagefile->getPackageFile(); + if (!$pkgfile) { + return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: package file object must ' . + 'be created from a real file'); + } + + $pkgdir = dirname(realpath($pkgfile)); + $pkgfile = basename($pkgfile); + + // {{{ Create the package file list + $filelist = array(); + $i = 0; + $this->_packagefile->flattenFilelist(); + $contents = $this->_packagefile->getContents(); + if (isset($contents['bundledpackage'])) { // bundles of packages + $contents = $contents['bundledpackage']; + if (!isset($contents[0])) { + $contents = array($contents); + } + + $packageDir = $where; + foreach ($contents as $i => $package) { + $fname = $package; + $file = $pkgdir . DIRECTORY_SEPARATOR . $fname; + if (!file_exists($file)) { + return $packager->raiseError("File does not exist: $fname"); + } + + $tfile = $packageDir . DIRECTORY_SEPARATOR . $fname; + System::mkdir(array('-p', dirname($tfile))); + copy($file, $tfile); + $filelist[$i++] = $tfile; + $packager->log(2, "Adding package $fname"); + } + } else { // normal packages + $contents = $contents['dir']['file']; + if (!isset($contents[0])) { + $contents = array($contents); + } + + $packageDir = $where; + foreach ($contents as $i => $file) { + $fname = $file['attribs']['name']; + $atts = $file['attribs']; + $orig = $file; + $file = $pkgdir . DIRECTORY_SEPARATOR . $fname; + if (!file_exists($file)) { + return $packager->raiseError("File does not exist: $fname"); + } + + $origperms = fileperms($file); + $tfile = $packageDir . DIRECTORY_SEPARATOR . $fname; + unset($orig['attribs']); + if (count($orig)) { // file with tasks + // run any package-time tasks + $contents = file_get_contents($file); + foreach ($orig as $tag => $raw) { + $tag = str_replace( + array($this->_packagefile->getTasksNs() . ':', '-'), + array('', '_'), $tag); + $task = "PEAR_Task_$tag"; + $task = &new $task($this->_packagefile->_config, + $this->_packagefile->_logger, + PEAR_TASK_PACKAGE); + $task->init($raw, $atts, null); + $res = $task->startSession($this->_packagefile, $contents, $tfile); + if (!$res) { + continue; // skip this task + } + + if (PEAR::isError($res)) { + return $res; + } + + $contents = $res; // save changes + System::mkdir(array('-p', dirname($tfile))); + $wp = fopen($tfile, "wb"); + fwrite($wp, $contents); + fclose($wp); + } + } + + if (!file_exists($tfile)) { + System::mkdir(array('-p', dirname($tfile))); + copy($file, $tfile); + } + + chmod($tfile, $origperms); + $filelist[$i++] = $tfile; + $this->_packagefile->setFileAttribute($fname, 'md5sum', md5_file($tfile), $i - 1); + $packager->log(2, "Adding file $fname"); + } + } + // }}} + + $name = $pf1 !== null ? 'package2.xml' : 'package.xml'; + $packagexml = $this->toPackageFile($where, PEAR_VALIDATE_PACKAGING, $name); + if ($packagexml) { + $tar =& new Archive_Tar($dest_package, $compress); + $tar->setErrorHandling(PEAR_ERROR_RETURN); // XXX Don't print errors + // ----- Creates with the package.xml file + $ok = $tar->createModify(array($packagexml), '', $where); + if (PEAR::isError($ok)) { + return $packager->raiseError($ok); + } elseif (!$ok) { + return $packager->raiseError('PEAR_Packagefile_v2::toTgz(): adding ' . $name . + ' failed'); + } + + // ----- Add the content of the package + if (!$tar->addModify($filelist, $pkgver, $where)) { + return $packager->raiseError( + 'PEAR_Packagefile_v2::toTgz(): tarball creation failed'); + } + + // add the package.xml version 1.0 + if ($pf1 !== null) { + $pfgen = &$pf1->getDefaultGenerator(); + $packagexml1 = $pfgen->toPackageFile($where, PEAR_VALIDATE_PACKAGING, 'package.xml', true); + if (!$tar->addModify(array($packagexml1), '', $where)) { + return $packager->raiseError( + 'PEAR_Packagefile_v2::toTgz(): adding package.xml failed'); + } + } + + return $dest_package; + } + } + + function toPackageFile($where = null, $state = PEAR_VALIDATE_NORMAL, $name = 'package.xml') + { + if (!$this->_packagefile->validate($state)) { + return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: invalid package.xml', + null, null, null, $this->_packagefile->getValidationWarnings()); + } + + if ($where === null) { + if (!($where = System::mktemp(array('-d')))) { + return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: mktemp failed'); + } + } elseif (!@System::mkDir(array('-p', $where))) { + return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: "' . $where . '" could' . + ' not be created'); + } + + $newpkgfile = $where . DIRECTORY_SEPARATOR . $name; + $np = @fopen($newpkgfile, 'wb'); + if (!$np) { + return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: unable to save ' . + "$name as $newpkgfile"); + } + fwrite($np, $this->toXml($state)); + fclose($np); + return $newpkgfile; + } + + function &toV2() + { + return $this->_packagefile; + } + + /** + * Return an XML document based on the package info (as returned + * by the PEAR_Common::infoFrom* methods). + * + * @return string XML data + */ + function toXml($state = PEAR_VALIDATE_NORMAL, $options = array()) + { + $this->_packagefile->setDate(date('Y-m-d')); + $this->_packagefile->setTime(date('H:i:s')); + if (!$this->_packagefile->validate($state)) { + return false; + } + + if (is_array($options)) { + $this->options = array_merge($this->_defaultOptions, $options); + } else { + $this->options = $this->_defaultOptions; + } + + $arr = $this->_packagefile->getArray(); + if (isset($arr['filelist'])) { + unset($arr['filelist']); + } + + if (isset($arr['_lastversion'])) { + unset($arr['_lastversion']); + } + + // Fix the notes a little bit + if (isset($arr['notes'])) { + // This trims out the indenting, needs fixing + $arr['notes'] = "\n" . trim($arr['notes']) . "\n"; + } + + if (isset($arr['changelog']) && !empty($arr['changelog'])) { + // Fix for inconsistency how the array is filled depending on the changelog release amount + if (!isset($arr['changelog']['release'][0])) { + $release = $arr['changelog']['release']; + unset($arr['changelog']['release']); + + $arr['changelog']['release'] = array(); + $arr['changelog']['release'][0] = $release; + } + + foreach (array_keys($arr['changelog']['release']) as $key) { + $c =& $arr['changelog']['release'][$key]; + if (isset($c['notes'])) { + // This trims out the indenting, needs fixing + $c['notes'] = "\n" . trim($c['notes']) . "\n"; + } + } + } + + if ($state ^ PEAR_VALIDATE_PACKAGING && !isset($arr['bundle'])) { + $use = $this->_recursiveXmlFilelist($arr['contents']['dir']['file']); + unset($arr['contents']['dir']['file']); + if (isset($use['dir'])) { + $arr['contents']['dir']['dir'] = $use['dir']; + } + if (isset($use['file'])) { + $arr['contents']['dir']['file'] = $use['file']; + } + $this->options['beautifyFilelist'] = true; + } + + $arr['attribs']['packagerversion'] = '1.9.4'; + if ($this->serialize($arr, $options)) { + return $this->_serializedData . "\n"; + } + + return false; + } + + + function _recursiveXmlFilelist($list) + { + $dirs = array(); + if (isset($list['attribs'])) { + $file = $list['attribs']['name']; + unset($list['attribs']['name']); + $attributes = $list['attribs']; + $this->_addDir($dirs, explode('/', dirname($file)), $file, $attributes); + } else { + foreach ($list as $a) { + $file = $a['attribs']['name']; + $attributes = $a['attribs']; + unset($a['attribs']); + $this->_addDir($dirs, explode('/', dirname($file)), $file, $attributes, $a); + } + } + $this->_formatDir($dirs); + $this->_deFormat($dirs); + return $dirs; + } + + function _addDir(&$dirs, $dir, $file = null, $attributes = null, $tasks = null) + { + if (!$tasks) { + $tasks = array(); + } + if ($dir == array() || $dir == array('.')) { + $dirs['file'][basename($file)] = $tasks; + $attributes['name'] = basename($file); + $dirs['file'][basename($file)]['attribs'] = $attributes; + return; + } + $curdir = array_shift($dir); + if (!isset($dirs['dir'][$curdir])) { + $dirs['dir'][$curdir] = array(); + } + $this->_addDir($dirs['dir'][$curdir], $dir, $file, $attributes, $tasks); + } + + function _formatDir(&$dirs) + { + if (!count($dirs)) { + return array(); + } + $newdirs = array(); + if (isset($dirs['dir'])) { + $newdirs['dir'] = $dirs['dir']; + } + if (isset($dirs['file'])) { + $newdirs['file'] = $dirs['file']; + } + $dirs = $newdirs; + if (isset($dirs['dir'])) { + uksort($dirs['dir'], 'strnatcasecmp'); + foreach ($dirs['dir'] as $dir => $contents) { + $this->_formatDir($dirs['dir'][$dir]); + } + } + if (isset($dirs['file'])) { + uksort($dirs['file'], 'strnatcasecmp'); + }; + } + + function _deFormat(&$dirs) + { + if (!count($dirs)) { + return array(); + } + $newdirs = array(); + if (isset($dirs['dir'])) { + foreach ($dirs['dir'] as $dir => $contents) { + $newdir = array(); + $newdir['attribs']['name'] = $dir; + $this->_deFormat($contents); + foreach ($contents as $tag => $val) { + $newdir[$tag] = $val; + } + $newdirs['dir'][] = $newdir; + } + if (count($newdirs['dir']) == 1) { + $newdirs['dir'] = $newdirs['dir'][0]; + } + } + if (isset($dirs['file'])) { + foreach ($dirs['file'] as $name => $file) { + $newdirs['file'][] = $file; + } + if (count($newdirs['file']) == 1) { + $newdirs['file'] = $newdirs['file'][0]; + } + } + $dirs = $newdirs; + } + + /** + * reset all options to default options + * + * @access public + * @see setOption(), XML_Unserializer() + */ + function resetOptions() + { + $this->options = $this->_defaultOptions; + } + + /** + * set an option + * + * You can use this method if you do not want to set all options in the constructor + * + * @access public + * @see resetOption(), XML_Serializer() + */ + function setOption($name, $value) + { + $this->options[$name] = $value; + } + + /** + * sets several options at once + * + * You can use this method if you do not want to set all options in the constructor + * + * @access public + * @see resetOption(), XML_Unserializer(), setOption() + */ + function setOptions($options) + { + $this->options = array_merge($this->options, $options); + } + + /** + * serialize data + * + * @access public + * @param mixed $data data to serialize + * @return boolean true on success, pear error on failure + */ + function serialize($data, $options = null) + { + // if options have been specified, use them instead + // of the previously defined ones + if (is_array($options)) { + $optionsBak = $this->options; + if (isset($options['overrideOptions']) && $options['overrideOptions'] == true) { + $this->options = array_merge($this->_defaultOptions, $options); + } else { + $this->options = array_merge($this->options, $options); + } + } else { + $optionsBak = null; + } + + // start depth is zero + $this->_tagDepth = 0; + $this->_serializedData = ''; + // serialize an array + if (is_array($data)) { + $tagName = isset($this->options['rootName']) ? $this->options['rootName'] : 'array'; + $this->_serializedData .= $this->_serializeArray($data, $tagName, $this->options['rootAttributes']); + } + + // add doctype declaration + if ($this->options['addDoctype'] === true) { + $this->_serializedData = XML_Util::getDoctypeDeclaration($tagName, $this->options['doctype']) + . $this->options['linebreak'] + . $this->_serializedData; + } + + // build xml declaration + if ($this->options['addDecl']) { + $atts = array(); + $encoding = isset($this->options['encoding']) ? $this->options['encoding'] : null; + $this->_serializedData = XML_Util::getXMLDeclaration('1.0', $encoding) + . $this->options['linebreak'] + . $this->_serializedData; + } + + + if ($optionsBak !== null) { + $this->options = $optionsBak; + } + + return true; + } + + /** + * get the result of the serialization + * + * @access public + * @return string serialized XML + */ + function getSerializedData() + { + if ($this->_serializedData === null) { + return $this->raiseError('No serialized data available. Use XML_Serializer::serialize() first.', XML_SERIALIZER_ERROR_NO_SERIALIZATION); + } + return $this->_serializedData; + } + + /** + * serialize any value + * + * This method checks for the type of the value and calls the appropriate method + * + * @access private + * @param mixed $value + * @param string $tagName + * @param array $attributes + * @return string + */ + function _serializeValue($value, $tagName = null, $attributes = array()) + { + if (is_array($value)) { + $xml = $this->_serializeArray($value, $tagName, $attributes); + } elseif (is_object($value)) { + $xml = $this->_serializeObject($value, $tagName); + } else { + $tag = array( + 'qname' => $tagName, + 'attributes' => $attributes, + 'content' => $value + ); + $xml = $this->_createXMLTag($tag); + } + return $xml; + } + + /** + * serialize an array + * + * @access private + * @param array $array array to serialize + * @param string $tagName name of the root tag + * @param array $attributes attributes for the root tag + * @return string $string serialized data + * @uses XML_Util::isValidName() to check, whether key has to be substituted + */ + function _serializeArray(&$array, $tagName = null, $attributes = array()) + { + $_content = null; + + /** + * check for special attributes + */ + if ($this->options['attributesArray'] !== null) { + if (isset($array[$this->options['attributesArray']])) { + $attributes = $array[$this->options['attributesArray']]; + unset($array[$this->options['attributesArray']]); + } + /** + * check for special content + */ + if ($this->options['contentName'] !== null) { + if (isset($array[$this->options['contentName']])) { + $_content = $array[$this->options['contentName']]; + unset($array[$this->options['contentName']]); + } + } + } + + /* + * if mode is set to simpleXML, check whether + * the array is associative or indexed + */ + if (is_array($array) && $this->options['mode'] == 'simplexml') { + $indexed = true; + if (!count($array)) { + $indexed = false; + } + foreach ($array as $key => $val) { + if (!is_int($key)) { + $indexed = false; + break; + } + } + + if ($indexed && $this->options['mode'] == 'simplexml') { + $string = ''; + foreach ($array as $key => $val) { + if ($this->options['beautifyFilelist'] && $tagName == 'dir') { + if (!isset($this->_curdir)) { + $this->_curdir = ''; + } + $savedir = $this->_curdir; + if (isset($val['attribs'])) { + if ($val['attribs']['name'] == '/') { + $this->_curdir = '/'; + } else { + if ($this->_curdir == '/') { + $this->_curdir = ''; + } + $this->_curdir .= '/' . $val['attribs']['name']; + } + } + } + $string .= $this->_serializeValue( $val, $tagName, $attributes); + if ($this->options['beautifyFilelist'] && $tagName == 'dir') { + $string .= ' '; + if (empty($savedir)) { + unset($this->_curdir); + } else { + $this->_curdir = $savedir; + } + } + + $string .= $this->options['linebreak']; + // do indentation + if ($this->options['indent'] !== null && $this->_tagDepth > 0) { + $string .= str_repeat($this->options['indent'], $this->_tagDepth); + } + } + return rtrim($string); + } + } + + if ($this->options['scalarAsAttributes'] === true) { + foreach ($array as $key => $value) { + if (is_scalar($value) && (XML_Util::isValidName($key) === true)) { + unset($array[$key]); + $attributes[$this->options['prependAttributes'].$key] = $value; + } + } + } + + // check for empty array => create empty tag + if (empty($array)) { + $tag = array( + 'qname' => $tagName, + 'content' => $_content, + 'attributes' => $attributes + ); + + } else { + $this->_tagDepth++; + $tmp = $this->options['linebreak']; + foreach ($array as $key => $value) { + // do indentation + if ($this->options['indent'] !== null && $this->_tagDepth > 0) { + $tmp .= str_repeat($this->options['indent'], $this->_tagDepth); + } + + // copy key + $origKey = $key; + // key cannot be used as tagname => use default tag + $valid = XML_Util::isValidName($key); + if (PEAR::isError($valid)) { + if ($this->options['classAsTagName'] && is_object($value)) { + $key = get_class($value); + } else { + $key = $this->options['defaultTagName']; + } + } + $atts = array(); + if ($this->options['typeHints'] === true) { + $atts[$this->options['typeAttribute']] = gettype($value); + if ($key !== $origKey) { + $atts[$this->options['keyAttribute']] = (string)$origKey; + } + + } + if ($this->options['beautifyFilelist'] && $key == 'dir') { + if (!isset($this->_curdir)) { + $this->_curdir = ''; + } + $savedir = $this->_curdir; + if (isset($value['attribs'])) { + if ($value['attribs']['name'] == '/') { + $this->_curdir = '/'; + } else { + $this->_curdir .= '/' . $value['attribs']['name']; + } + } + } + + if (is_string($value) && $value && ($value{strlen($value) - 1} == "\n")) { + $value .= str_repeat($this->options['indent'], $this->_tagDepth); + } + $tmp .= $this->_createXMLTag(array( + 'qname' => $key, + 'attributes' => $atts, + 'content' => $value ) + ); + if ($this->options['beautifyFilelist'] && $key == 'dir') { + if (isset($value['attribs'])) { + $tmp .= ' '; + if (empty($savedir)) { + unset($this->_curdir); + } else { + $this->_curdir = $savedir; + } + } + } + $tmp .= $this->options['linebreak']; + } + + $this->_tagDepth--; + if ($this->options['indent']!==null && $this->_tagDepth>0) { + $tmp .= str_repeat($this->options['indent'], $this->_tagDepth); + } + + if (trim($tmp) === '') { + $tmp = null; + } + + $tag = array( + 'qname' => $tagName, + 'content' => $tmp, + 'attributes' => $attributes + ); + } + if ($this->options['typeHints'] === true) { + if (!isset($tag['attributes'][$this->options['typeAttribute']])) { + $tag['attributes'][$this->options['typeAttribute']] = 'array'; + } + } + + $string = $this->_createXMLTag($tag, false); + return $string; + } + + /** + * create a tag from an array + * this method awaits an array in the following format + * array( + * 'qname' => $tagName, + * 'attributes' => array(), + * 'content' => $content, // optional + * 'namespace' => $namespace // optional + * 'namespaceUri' => $namespaceUri // optional + * ) + * + * @access private + * @param array $tag tag definition + * @param boolean $replaceEntities whether to replace XML entities in content or not + * @return string $string XML tag + */ + function _createXMLTag($tag, $replaceEntities = true) + { + if ($this->options['indentAttributes'] !== false) { + $multiline = true; + $indent = str_repeat($this->options['indent'], $this->_tagDepth); + + if ($this->options['indentAttributes'] == '_auto') { + $indent .= str_repeat(' ', (strlen($tag['qname'])+2)); + + } else { + $indent .= $this->options['indentAttributes']; + } + } else { + $indent = $multiline = false; + } + + if (is_array($tag['content'])) { + if (empty($tag['content'])) { + $tag['content'] = ''; + } + } elseif(is_scalar($tag['content']) && (string)$tag['content'] == '') { + $tag['content'] = ''; + } + + if (is_scalar($tag['content']) || is_null($tag['content'])) { + if ($this->options['encoding'] == 'UTF-8' && + version_compare(phpversion(), '5.0.0', 'lt') + ) { + $tag['content'] = utf8_encode($tag['content']); + } + + if ($replaceEntities === true) { + $replaceEntities = XML_UTIL_ENTITIES_XML; + } + + $tag = XML_Util::createTagFromArray($tag, $replaceEntities, $multiline, $indent, $this->options['linebreak']); + } elseif (is_array($tag['content'])) { + $tag = $this->_serializeArray($tag['content'], $tag['qname'], $tag['attributes']); + } elseif (is_object($tag['content'])) { + $tag = $this->_serializeObject($tag['content'], $tag['qname'], $tag['attributes']); + } elseif (is_resource($tag['content'])) { + settype($tag['content'], 'string'); + $tag = XML_Util::createTagFromArray($tag, $replaceEntities); + } + return $tag; + } +} \ No newline at end of file diff --git a/includes/pear/PEAR/PackageFile/Parser/v1.php b/includes/pear/PEAR/PackageFile/Parser/v1.php new file mode 100644 index 0000000..bbf5989 --- /dev/null +++ b/includes/pear/PEAR/PackageFile/Parser/v1.php @@ -0,0 +1,459 @@ + + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a1 + */ +/** + * package.xml abstraction class + */ +require_once 'PEAR/PackageFile/v1.php'; +/** + * Parser for package.xml version 1.0 + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @PEAR-VER@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a1 + */ +class PEAR_PackageFile_Parser_v1 +{ + var $_registry; + var $_config; + var $_logger; + /** + * BC hack to allow PEAR_Common::infoFromString() to sort of + * work with the version 2.0 format - there's no filelist though + * @param PEAR_PackageFile_v2 + */ + function fromV2($packagefile) + { + $info = $packagefile->getArray(true); + $ret = new PEAR_PackageFile_v1; + $ret->fromArray($info['old']); + } + + function setConfig(&$c) + { + $this->_config = &$c; + $this->_registry = &$c->getRegistry(); + } + + function setLogger(&$l) + { + $this->_logger = &$l; + } + + /** + * @param string contents of package.xml file, version 1.0 + * @return bool success of parsing + */ + function &parse($data, $file, $archive = false) + { + if (!extension_loaded('xml')) { + return PEAR::raiseError('Cannot create xml parser for parsing package.xml, no xml extension'); + } + $xp = xml_parser_create(); + if (!$xp) { + $a = &PEAR::raiseError('Cannot create xml parser for parsing package.xml'); + return $a; + } + xml_set_object($xp, $this); + xml_set_element_handler($xp, '_element_start_1_0', '_element_end_1_0'); + xml_set_character_data_handler($xp, '_pkginfo_cdata_1_0'); + xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, false); + + $this->element_stack = array(); + $this->_packageInfo = array('provides' => array()); + $this->current_element = false; + unset($this->dir_install); + $this->_packageInfo['filelist'] = array(); + $this->filelist =& $this->_packageInfo['filelist']; + $this->dir_names = array(); + $this->in_changelog = false; + $this->d_i = 0; + $this->cdata = ''; + $this->_isValid = true; + + if (!xml_parse($xp, $data, 1)) { + $code = xml_get_error_code($xp); + $line = xml_get_current_line_number($xp); + xml_parser_free($xp); + $a = &PEAR::raiseError(sprintf("XML error: %s at line %d", + $str = xml_error_string($code), $line), 2); + return $a; + } + + xml_parser_free($xp); + + $pf = new PEAR_PackageFile_v1; + $pf->setConfig($this->_config); + if (isset($this->_logger)) { + $pf->setLogger($this->_logger); + } + $pf->setPackagefile($file, $archive); + $pf->fromArray($this->_packageInfo); + return $pf; + } + // {{{ _unIndent() + + /** + * Unindent given string + * + * @param string $str The string that has to be unindented. + * @return string + * @access private + */ + function _unIndent($str) + { + // remove leading newlines + $str = preg_replace('/^[\r\n]+/', '', $str); + // find whitespace at the beginning of the first line + $indent_len = strspn($str, " \t"); + $indent = substr($str, 0, $indent_len); + $data = ''; + // remove the same amount of whitespace from following lines + foreach (explode("\n", $str) as $line) { + if (substr($line, 0, $indent_len) == $indent) { + $data .= substr($line, $indent_len) . "\n"; + } elseif (trim(substr($line, 0, $indent_len))) { + $data .= ltrim($line); + } + } + return $data; + } + + // Support for package DTD v1.0: + // {{{ _element_start_1_0() + + /** + * XML parser callback for ending elements. Used for version 1.0 + * packages. + * + * @param resource $xp XML parser resource + * @param string $name name of ending element + * + * @return void + * + * @access private + */ + function _element_start_1_0($xp, $name, $attribs) + { + array_push($this->element_stack, $name); + $this->current_element = $name; + $spos = sizeof($this->element_stack) - 2; + $this->prev_element = ($spos >= 0) ? $this->element_stack[$spos] : ''; + $this->current_attributes = $attribs; + $this->cdata = ''; + switch ($name) { + case 'dir': + if ($this->in_changelog) { + break; + } + if (array_key_exists('name', $attribs) && $attribs['name'] != '/') { + $attribs['name'] = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'), + $attribs['name']); + if (strrpos($attribs['name'], '/') === strlen($attribs['name']) - 1) { + $attribs['name'] = substr($attribs['name'], 0, + strlen($attribs['name']) - 1); + } + if (strpos($attribs['name'], '/') === 0) { + $attribs['name'] = substr($attribs['name'], 1); + } + $this->dir_names[] = $attribs['name']; + } + if (isset($attribs['baseinstalldir'])) { + $this->dir_install = $attribs['baseinstalldir']; + } + if (isset($attribs['role'])) { + $this->dir_role = $attribs['role']; + } + break; + case 'file': + if ($this->in_changelog) { + break; + } + if (isset($attribs['name'])) { + $path = ''; + if (count($this->dir_names)) { + foreach ($this->dir_names as $dir) { + $path .= $dir . '/'; + } + } + $path .= preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'), + $attribs['name']); + unset($attribs['name']); + $this->current_path = $path; + $this->filelist[$path] = $attribs; + // Set the baseinstalldir only if the file don't have this attrib + if (!isset($this->filelist[$path]['baseinstalldir']) && + isset($this->dir_install)) + { + $this->filelist[$path]['baseinstalldir'] = $this->dir_install; + } + // Set the Role + if (!isset($this->filelist[$path]['role']) && isset($this->dir_role)) { + $this->filelist[$path]['role'] = $this->dir_role; + } + } + break; + case 'replace': + if (!$this->in_changelog) { + $this->filelist[$this->current_path]['replacements'][] = $attribs; + } + break; + case 'maintainers': + $this->_packageInfo['maintainers'] = array(); + $this->m_i = 0; // maintainers array index + break; + case 'maintainer': + // compatibility check + if (!isset($this->_packageInfo['maintainers'])) { + $this->_packageInfo['maintainers'] = array(); + $this->m_i = 0; + } + $this->_packageInfo['maintainers'][$this->m_i] = array(); + $this->current_maintainer =& $this->_packageInfo['maintainers'][$this->m_i]; + break; + case 'changelog': + $this->_packageInfo['changelog'] = array(); + $this->c_i = 0; // changelog array index + $this->in_changelog = true; + break; + case 'release': + if ($this->in_changelog) { + $this->_packageInfo['changelog'][$this->c_i] = array(); + $this->current_release = &$this->_packageInfo['changelog'][$this->c_i]; + } else { + $this->current_release = &$this->_packageInfo; + } + break; + case 'deps': + if (!$this->in_changelog) { + $this->_packageInfo['release_deps'] = array(); + } + break; + case 'dep': + // dependencies array index + if (!$this->in_changelog) { + $this->d_i++; + isset($attribs['type']) ? ($attribs['type'] = strtolower($attribs['type'])) : false; + $this->_packageInfo['release_deps'][$this->d_i] = $attribs; + } + break; + case 'configureoptions': + if (!$this->in_changelog) { + $this->_packageInfo['configure_options'] = array(); + } + break; + case 'configureoption': + if (!$this->in_changelog) { + $this->_packageInfo['configure_options'][] = $attribs; + } + break; + case 'provides': + if (empty($attribs['type']) || empty($attribs['name'])) { + break; + } + $attribs['explicit'] = true; + $this->_packageInfo['provides']["$attribs[type];$attribs[name]"] = $attribs; + break; + case 'package' : + if (isset($attribs['version'])) { + $this->_packageInfo['xsdversion'] = trim($attribs['version']); + } else { + $this->_packageInfo['xsdversion'] = '1.0'; + } + if (isset($attribs['packagerversion'])) { + $this->_packageInfo['packagerversion'] = $attribs['packagerversion']; + } + break; + } + } + + // }}} + // {{{ _element_end_1_0() + + /** + * XML parser callback for ending elements. Used for version 1.0 + * packages. + * + * @param resource $xp XML parser resource + * @param string $name name of ending element + * + * @return void + * + * @access private + */ + function _element_end_1_0($xp, $name) + { + $data = trim($this->cdata); + switch ($name) { + case 'name': + switch ($this->prev_element) { + case 'package': + $this->_packageInfo['package'] = $data; + break; + case 'maintainer': + $this->current_maintainer['name'] = $data; + break; + } + break; + case 'extends' : + $this->_packageInfo['extends'] = $data; + break; + case 'summary': + $this->_packageInfo['summary'] = $data; + break; + case 'description': + $data = $this->_unIndent($this->cdata); + $this->_packageInfo['description'] = $data; + break; + case 'user': + $this->current_maintainer['handle'] = $data; + break; + case 'email': + $this->current_maintainer['email'] = $data; + break; + case 'role': + $this->current_maintainer['role'] = $data; + break; + case 'version': + if ($this->in_changelog) { + $this->current_release['version'] = $data; + } else { + $this->_packageInfo['version'] = $data; + } + break; + case 'date': + if ($this->in_changelog) { + $this->current_release['release_date'] = $data; + } else { + $this->_packageInfo['release_date'] = $data; + } + break; + case 'notes': + // try to "de-indent" release notes in case someone + // has been over-indenting their xml ;-) + // Trim only on the right side + $data = rtrim($this->_unIndent($this->cdata)); + if ($this->in_changelog) { + $this->current_release['release_notes'] = $data; + } else { + $this->_packageInfo['release_notes'] = $data; + } + break; + case 'warnings': + if ($this->in_changelog) { + $this->current_release['release_warnings'] = $data; + } else { + $this->_packageInfo['release_warnings'] = $data; + } + break; + case 'state': + if ($this->in_changelog) { + $this->current_release['release_state'] = $data; + } else { + $this->_packageInfo['release_state'] = $data; + } + break; + case 'license': + if ($this->in_changelog) { + $this->current_release['release_license'] = $data; + } else { + $this->_packageInfo['release_license'] = $data; + } + break; + case 'dep': + if ($data && !$this->in_changelog) { + $this->_packageInfo['release_deps'][$this->d_i]['name'] = $data; + } + break; + case 'dir': + if ($this->in_changelog) { + break; + } + array_pop($this->dir_names); + break; + case 'file': + if ($this->in_changelog) { + break; + } + if ($data) { + $path = ''; + if (count($this->dir_names)) { + foreach ($this->dir_names as $dir) { + $path .= $dir . '/'; + } + } + $path .= $data; + $this->filelist[$path] = $this->current_attributes; + // Set the baseinstalldir only if the file don't have this attrib + if (!isset($this->filelist[$path]['baseinstalldir']) && + isset($this->dir_install)) + { + $this->filelist[$path]['baseinstalldir'] = $this->dir_install; + } + // Set the Role + if (!isset($this->filelist[$path]['role']) && isset($this->dir_role)) { + $this->filelist[$path]['role'] = $this->dir_role; + } + } + break; + case 'maintainer': + if (empty($this->_packageInfo['maintainers'][$this->m_i]['role'])) { + $this->_packageInfo['maintainers'][$this->m_i]['role'] = 'lead'; + } + $this->m_i++; + break; + case 'release': + if ($this->in_changelog) { + $this->c_i++; + } + break; + case 'changelog': + $this->in_changelog = false; + break; + } + array_pop($this->element_stack); + $spos = sizeof($this->element_stack) - 1; + $this->current_element = ($spos > 0) ? $this->element_stack[$spos] : ''; + $this->cdata = ''; + } + + // }}} + // {{{ _pkginfo_cdata_1_0() + + /** + * XML parser callback for character data. Used for version 1.0 + * packages. + * + * @param resource $xp XML parser resource + * @param string $name character data + * + * @return void + * + * @access private + */ + function _pkginfo_cdata_1_0($xp, $data) + { + if (isset($this->cdata)) { + $this->cdata .= $data; + } + } + + // }}} +} +?> \ No newline at end of file diff --git a/includes/pear/PEAR/PackageFile/Parser/v2.php b/includes/pear/PEAR/PackageFile/Parser/v2.php new file mode 100644 index 0000000..cb81041 --- /dev/null +++ b/includes/pear/PEAR/PackageFile/Parser/v2.php @@ -0,0 +1,113 @@ + + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a1 + */ +/** + * base xml parser class + */ +require_once 'PEAR/XMLParser.php'; +require_once 'PEAR/PackageFile/v2.php'; +/** + * Parser for package.xml version 2.0 + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @PEAR-VER@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a1 + */ +class PEAR_PackageFile_Parser_v2 extends PEAR_XMLParser +{ + var $_config; + var $_logger; + var $_registry; + + function setConfig(&$c) + { + $this->_config = &$c; + $this->_registry = &$c->getRegistry(); + } + + function setLogger(&$l) + { + $this->_logger = &$l; + } + /** + * Unindent given string + * + * @param string $str The string that has to be unindented. + * @return string + * @access private + */ + function _unIndent($str) + { + // remove leading newlines + $str = preg_replace('/^[\r\n]+/', '', $str); + // find whitespace at the beginning of the first line + $indent_len = strspn($str, " \t"); + $indent = substr($str, 0, $indent_len); + $data = ''; + // remove the same amount of whitespace from following lines + foreach (explode("\n", $str) as $line) { + if (substr($line, 0, $indent_len) == $indent) { + $data .= substr($line, $indent_len) . "\n"; + } else { + $data .= $line . "\n"; + } + } + return $data; + } + + /** + * post-process data + * + * @param string $data + * @param string $element element name + */ + function postProcess($data, $element) + { + if ($element == 'notes') { + return trim($this->_unIndent($data)); + } + return trim($data); + } + + /** + * @param string + * @param string file name of the package.xml + * @param string|false name of the archive this package.xml came from, if any + * @param string class name to instantiate and return. This must be PEAR_PackageFile_v2 or + * a subclass + * @return PEAR_PackageFile_v2 + */ + function &parse($data, $file, $archive = false, $class = 'PEAR_PackageFile_v2') + { + if (PEAR::isError($err = parent::parse($data, $file))) { + return $err; + } + + $ret = new $class; + $ret->encoding = $this->encoding; + $ret->setConfig($this->_config); + if (isset($this->_logger)) { + $ret->setLogger($this->_logger); + } + + $ret->fromArray($this->_unserializedData); + $ret->setPackagefile($file, $archive); + return $ret; + } +} \ No newline at end of file diff --git a/includes/pear/PEAR/PackageFile/v1.php b/includes/pear/PEAR/PackageFile/v1.php new file mode 100644 index 0000000..2884cc4 --- /dev/null +++ b/includes/pear/PEAR/PackageFile/v1.php @@ -0,0 +1,1612 @@ + + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a1 + */ +/** + * For error handling + */ +require_once 'PEAR/ErrorStack.php'; + +/** + * Error code if parsing is attempted with no xml extension + */ +define('PEAR_PACKAGEFILE_ERROR_NO_XML_EXT', 3); + +/** + * Error code if creating the xml parser resource fails + */ +define('PEAR_PACKAGEFILE_ERROR_CANT_MAKE_PARSER', 4); + +/** + * Error code used for all sax xml parsing errors + */ +define('PEAR_PACKAGEFILE_ERROR_PARSER_ERROR', 5); + +/** + * Error code used when there is no name + */ +define('PEAR_PACKAGEFILE_ERROR_NO_NAME', 6); + +/** + * Error code when a package name is not valid + */ +define('PEAR_PACKAGEFILE_ERROR_INVALID_NAME', 7); + +/** + * Error code used when no summary is parsed + */ +define('PEAR_PACKAGEFILE_ERROR_NO_SUMMARY', 8); + +/** + * Error code for summaries that are more than 1 line + */ +define('PEAR_PACKAGEFILE_ERROR_MULTILINE_SUMMARY', 9); + +/** + * Error code used when no description is present + */ +define('PEAR_PACKAGEFILE_ERROR_NO_DESCRIPTION', 10); + +/** + * Error code used when no license is present + */ +define('PEAR_PACKAGEFILE_ERROR_NO_LICENSE', 11); + +/** + * Error code used when a version number is not present + */ +define('PEAR_PACKAGEFILE_ERROR_NO_VERSION', 12); + +/** + * Error code used when a version number is invalid + */ +define('PEAR_PACKAGEFILE_ERROR_INVALID_VERSION', 13); + +/** + * Error code when release state is missing + */ +define('PEAR_PACKAGEFILE_ERROR_NO_STATE', 14); + +/** + * Error code when release state is invalid + */ +define('PEAR_PACKAGEFILE_ERROR_INVALID_STATE', 15); + +/** + * Error code when release state is missing + */ +define('PEAR_PACKAGEFILE_ERROR_NO_DATE', 16); + +/** + * Error code when release state is invalid + */ +define('PEAR_PACKAGEFILE_ERROR_INVALID_DATE', 17); + +/** + * Error code when no release notes are found + */ +define('PEAR_PACKAGEFILE_ERROR_NO_NOTES', 18); + +/** + * Error code when no maintainers are found + */ +define('PEAR_PACKAGEFILE_ERROR_NO_MAINTAINERS', 19); + +/** + * Error code when a maintainer has no handle + */ +define('PEAR_PACKAGEFILE_ERROR_NO_MAINTHANDLE', 20); + +/** + * Error code when a maintainer has no handle + */ +define('PEAR_PACKAGEFILE_ERROR_NO_MAINTROLE', 21); + +/** + * Error code when a maintainer has no name + */ +define('PEAR_PACKAGEFILE_ERROR_NO_MAINTNAME', 22); + +/** + * Error code when a maintainer has no email + */ +define('PEAR_PACKAGEFILE_ERROR_NO_MAINTEMAIL', 23); + +/** + * Error code when a maintainer has no handle + */ +define('PEAR_PACKAGEFILE_ERROR_INVALID_MAINTROLE', 24); + +/** + * Error code when a dependency is not a PHP dependency, but has no name + */ +define('PEAR_PACKAGEFILE_ERROR_NO_DEPNAME', 25); + +/** + * Error code when a dependency has no type (pkg, php, etc.) + */ +define('PEAR_PACKAGEFILE_ERROR_NO_DEPTYPE', 26); + +/** + * Error code when a dependency has no relation (lt, ge, has, etc.) + */ +define('PEAR_PACKAGEFILE_ERROR_NO_DEPREL', 27); + +/** + * Error code when a dependency is not a 'has' relation, but has no version + */ +define('PEAR_PACKAGEFILE_ERROR_NO_DEPVERSION', 28); + +/** + * Error code when a dependency has an invalid relation + */ +define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPREL', 29); + +/** + * Error code when a dependency has an invalid type + */ +define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPTYPE', 30); + +/** + * Error code when a dependency has an invalid optional option + */ +define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPOPTIONAL', 31); + +/** + * Error code when a dependency is a pkg dependency, and has an invalid package name + */ +define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPNAME', 32); + +/** + * Error code when a dependency has a channel="foo" attribute, and foo is not a registered channel + */ +define('PEAR_PACKAGEFILE_ERROR_UNKNOWN_DEPCHANNEL', 33); + +/** + * Error code when rel="has" and version attribute is present. + */ +define('PEAR_PACKAGEFILE_ERROR_DEPVERSION_IGNORED', 34); + +/** + * Error code when type="php" and dependency name is present + */ +define('PEAR_PACKAGEFILE_ERROR_DEPNAME_IGNORED', 35); + +/** + * Error code when a configure option has no name + */ +define('PEAR_PACKAGEFILE_ERROR_NO_CONFNAME', 36); + +/** + * Error code when a configure option has no name + */ +define('PEAR_PACKAGEFILE_ERROR_NO_CONFPROMPT', 37); + +/** + * Error code when a file in the filelist has an invalid role + */ +define('PEAR_PACKAGEFILE_ERROR_INVALID_FILEROLE', 38); + +/** + * Error code when a file in the filelist has no role + */ +define('PEAR_PACKAGEFILE_ERROR_NO_FILEROLE', 39); + +/** + * Error code when analyzing a php source file that has parse errors + */ +define('PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE', 40); + +/** + * Error code when analyzing a php source file reveals a source element + * without a package name prefix + */ +define('PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX', 41); + +/** + * Error code when an unknown channel is specified + */ +define('PEAR_PACKAGEFILE_ERROR_UNKNOWN_CHANNEL', 42); + +/** + * Error code when no files are found in the filelist + */ +define('PEAR_PACKAGEFILE_ERROR_NO_FILES', 43); + +/** + * Error code when a file is not valid php according to _analyzeSourceCode() + */ +define('PEAR_PACKAGEFILE_ERROR_INVALID_FILE', 44); + +/** + * Error code when the channel validator returns an error or warning + */ +define('PEAR_PACKAGEFILE_ERROR_CHANNELVAL', 45); + +/** + * Error code when a php5 package is packaged in php4 (analysis doesn't work) + */ +define('PEAR_PACKAGEFILE_ERROR_PHP5', 46); + +/** + * Error code when a file is listed in package.xml but does not exist + */ +define('PEAR_PACKAGEFILE_ERROR_FILE_NOTFOUND', 47); + +/** + * Error code when a + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a1 + */ +class PEAR_PackageFile_v1 +{ + /** + * @access private + * @var PEAR_ErrorStack + * @access private + */ + var $_stack; + + /** + * A registry object, used to access the package name validation regex for non-standard channels + * @var PEAR_Registry + * @access private + */ + var $_registry; + + /** + * An object that contains a log method that matches PEAR_Common::log's signature + * @var object + * @access private + */ + var $_logger; + + /** + * Parsed package information + * @var array + * @access private + */ + var $_packageInfo; + + /** + * path to package.xml + * @var string + * @access private + */ + var $_packageFile; + + /** + * path to package .tgz or false if this is a local/extracted package.xml + * @var string + * @access private + */ + var $_archiveFile; + + /** + * @var int + * @access private + */ + var $_isValid = 0; + + /** + * Determines whether this packagefile was initialized only with partial package info + * + * If this package file was constructed via parsing REST, it will only contain + * + * - package name + * - channel name + * - dependencies + * @var boolean + * @access private + */ + var $_incomplete = true; + + /** + * @param bool determines whether to return a PEAR_Error object, or use the PEAR_ErrorStack + * @param string Name of Error Stack class to use. + */ + function PEAR_PackageFile_v1() + { + $this->_stack = &new PEAR_ErrorStack('PEAR_PackageFile_v1'); + $this->_stack->setErrorMessageTemplate($this->_getErrorMessage()); + $this->_isValid = 0; + } + + function installBinary($installer) + { + return false; + } + + function isExtension($name) + { + return false; + } + + function setConfig(&$config) + { + $this->_config = &$config; + $this->_registry = &$config->getRegistry(); + } + + function setRequestedGroup() + { + // placeholder + } + + /** + * For saving in the registry. + * + * Set the last version that was installed + * @param string + */ + function setLastInstalledVersion($version) + { + $this->_packageInfo['_lastversion'] = $version; + } + + /** + * @return string|false + */ + function getLastInstalledVersion() + { + if (isset($this->_packageInfo['_lastversion'])) { + return $this->_packageInfo['_lastversion']; + } + return false; + } + + function getInstalledBinary() + { + return false; + } + + function listPostinstallScripts() + { + return false; + } + + function initPostinstallScripts() + { + return false; + } + + function setLogger(&$logger) + { + if ($logger && (!is_object($logger) || !method_exists($logger, 'log'))) { + return PEAR::raiseError('Logger must be compatible with PEAR_Common::log'); + } + $this->_logger = &$logger; + } + + function setPackagefile($file, $archive = false) + { + $this->_packageFile = $file; + $this->_archiveFile = $archive ? $archive : $file; + } + + function getPackageFile() + { + return isset($this->_packageFile) ? $this->_packageFile : false; + } + + function getPackageType() + { + return 'php'; + } + + function getArchiveFile() + { + return $this->_archiveFile; + } + + function packageInfo($field) + { + if (!is_string($field) || empty($field) || + !isset($this->_packageInfo[$field])) { + return false; + } + return $this->_packageInfo[$field]; + } + + function setDirtree($path) + { + if (!isset($this->_packageInfo['dirtree'])) { + $this->_packageInfo['dirtree'] = array(); + } + $this->_packageInfo['dirtree'][$path] = true; + } + + function getDirtree() + { + if (isset($this->_packageInfo['dirtree']) && count($this->_packageInfo['dirtree'])) { + return $this->_packageInfo['dirtree']; + } + return false; + } + + function resetDirtree() + { + unset($this->_packageInfo['dirtree']); + } + + function fromArray($pinfo) + { + $this->_incomplete = false; + $this->_packageInfo = $pinfo; + } + + function isIncomplete() + { + return $this->_incomplete; + } + + function getChannel() + { + return 'pear.php.net'; + } + + function getUri() + { + return false; + } + + function getTime() + { + return false; + } + + function getExtends() + { + if (isset($this->_packageInfo['extends'])) { + return $this->_packageInfo['extends']; + } + return false; + } + + /** + * @return array + */ + function toArray() + { + if (!$this->validate(PEAR_VALIDATE_NORMAL)) { + return false; + } + return $this->getArray(); + } + + function getArray() + { + return $this->_packageInfo; + } + + function getName() + { + return $this->getPackage(); + } + + function getPackage() + { + if (isset($this->_packageInfo['package'])) { + return $this->_packageInfo['package']; + } + return false; + } + + /** + * WARNING - don't use this unless you know what you are doing + */ + function setRawPackage($package) + { + $this->_packageInfo['package'] = $package; + } + + function setPackage($package) + { + $this->_packageInfo['package'] = $package; + $this->_isValid = false; + } + + function getVersion() + { + if (isset($this->_packageInfo['version'])) { + return $this->_packageInfo['version']; + } + return false; + } + + function setVersion($version) + { + $this->_packageInfo['version'] = $version; + $this->_isValid = false; + } + + function clearMaintainers() + { + unset($this->_packageInfo['maintainers']); + } + + function getMaintainers() + { + if (isset($this->_packageInfo['maintainers'])) { + return $this->_packageInfo['maintainers']; + } + return false; + } + + /** + * Adds a new maintainer - no checking of duplicates is performed, use + * updatemaintainer for that purpose. + */ + function addMaintainer($role, $handle, $name, $email) + { + $this->_packageInfo['maintainers'][] = + array('handle' => $handle, 'role' => $role, 'email' => $email, 'name' => $name); + $this->_isValid = false; + } + + function updateMaintainer($role, $handle, $name, $email) + { + $found = false; + if (!isset($this->_packageInfo['maintainers']) || + !is_array($this->_packageInfo['maintainers'])) { + return $this->addMaintainer($role, $handle, $name, $email); + } + foreach ($this->_packageInfo['maintainers'] as $i => $maintainer) { + if ($maintainer['handle'] == $handle) { + $found = $i; + break; + } + } + if ($found !== false) { + unset($this->_packageInfo['maintainers'][$found]); + $this->_packageInfo['maintainers'] = + array_values($this->_packageInfo['maintainers']); + } + $this->addMaintainer($role, $handle, $name, $email); + } + + function deleteMaintainer($handle) + { + $found = false; + foreach ($this->_packageInfo['maintainers'] as $i => $maintainer) { + if ($maintainer['handle'] == $handle) { + $found = $i; + break; + } + } + if ($found !== false) { + unset($this->_packageInfo['maintainers'][$found]); + $this->_packageInfo['maintainers'] = + array_values($this->_packageInfo['maintainers']); + return true; + } + return false; + } + + function getState() + { + if (isset($this->_packageInfo['release_state'])) { + return $this->_packageInfo['release_state']; + } + return false; + } + + function setRawState($state) + { + $this->_packageInfo['release_state'] = $state; + } + + function setState($state) + { + $this->_packageInfo['release_state'] = $state; + $this->_isValid = false; + } + + function getDate() + { + if (isset($this->_packageInfo['release_date'])) { + return $this->_packageInfo['release_date']; + } + return false; + } + + function setDate($date) + { + $this->_packageInfo['release_date'] = $date; + $this->_isValid = false; + } + + function getLicense() + { + if (isset($this->_packageInfo['release_license'])) { + return $this->_packageInfo['release_license']; + } + return false; + } + + function setLicense($date) + { + $this->_packageInfo['release_license'] = $date; + $this->_isValid = false; + } + + function getSummary() + { + if (isset($this->_packageInfo['summary'])) { + return $this->_packageInfo['summary']; + } + return false; + } + + function setSummary($summary) + { + $this->_packageInfo['summary'] = $summary; + $this->_isValid = false; + } + + function getDescription() + { + if (isset($this->_packageInfo['description'])) { + return $this->_packageInfo['description']; + } + return false; + } + + function setDescription($desc) + { + $this->_packageInfo['description'] = $desc; + $this->_isValid = false; + } + + function getNotes() + { + if (isset($this->_packageInfo['release_notes'])) { + return $this->_packageInfo['release_notes']; + } + return false; + } + + function setNotes($notes) + { + $this->_packageInfo['release_notes'] = $notes; + $this->_isValid = false; + } + + function getDeps() + { + if (isset($this->_packageInfo['release_deps'])) { + return $this->_packageInfo['release_deps']; + } + return false; + } + + /** + * Reset dependencies prior to adding new ones + */ + function clearDeps() + { + unset($this->_packageInfo['release_deps']); + } + + function addPhpDep($version, $rel) + { + $this->_isValid = false; + $this->_packageInfo['release_deps'][] = + array('type' => 'php', + 'rel' => $rel, + 'version' => $version); + } + + function addPackageDep($name, $version, $rel, $optional = 'no') + { + $this->_isValid = false; + $dep = + array('type' => 'pkg', + 'name' => $name, + 'rel' => $rel, + 'optional' => $optional); + if ($rel != 'has' && $rel != 'not') { + $dep['version'] = $version; + } + $this->_packageInfo['release_deps'][] = $dep; + } + + function addExtensionDep($name, $version, $rel, $optional = 'no') + { + $this->_isValid = false; + $this->_packageInfo['release_deps'][] = + array('type' => 'ext', + 'name' => $name, + 'rel' => $rel, + 'version' => $version, + 'optional' => $optional); + } + + /** + * WARNING - do not use this function directly unless you know what you're doing + */ + function setDeps($deps) + { + $this->_packageInfo['release_deps'] = $deps; + } + + function hasDeps() + { + return isset($this->_packageInfo['release_deps']) && + count($this->_packageInfo['release_deps']); + } + + function getDependencyGroup($group) + { + return false; + } + + function isCompatible($pf) + { + return false; + } + + function isSubpackageOf($p) + { + return $p->isSubpackage($this); + } + + function isSubpackage($p) + { + return false; + } + + function dependsOn($package, $channel) + { + if (strtolower($channel) != 'pear.php.net') { + return false; + } + if (!($deps = $this->getDeps())) { + return false; + } + foreach ($deps as $dep) { + if ($dep['type'] != 'pkg') { + continue; + } + if (strtolower($dep['name']) == strtolower($package)) { + return true; + } + } + return false; + } + + function getConfigureOptions() + { + if (isset($this->_packageInfo['configure_options'])) { + return $this->_packageInfo['configure_options']; + } + return false; + } + + function hasConfigureOptions() + { + return isset($this->_packageInfo['configure_options']) && + count($this->_packageInfo['configure_options']); + } + + function addConfigureOption($name, $prompt, $default = false) + { + $o = array('name' => $name, 'prompt' => $prompt); + if ($default !== false) { + $o['default'] = $default; + } + if (!isset($this->_packageInfo['configure_options'])) { + $this->_packageInfo['configure_options'] = array(); + } + $this->_packageInfo['configure_options'][] = $o; + } + + function clearConfigureOptions() + { + unset($this->_packageInfo['configure_options']); + } + + function getProvides() + { + if (isset($this->_packageInfo['provides'])) { + return $this->_packageInfo['provides']; + } + return false; + } + + function getProvidesExtension() + { + return false; + } + + function addFile($dir, $file, $attrs) + { + $dir = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'), $dir); + if ($dir == '/' || $dir == '') { + $dir = ''; + } else { + $dir .= '/'; + } + $file = $dir . $file; + $file = preg_replace('![\\/]+!', '/', $file); + $this->_packageInfo['filelist'][$file] = $attrs; + } + + function getInstallationFilelist() + { + return $this->getFilelist(); + } + + function getFilelist() + { + if (isset($this->_packageInfo['filelist'])) { + return $this->_packageInfo['filelist']; + } + return false; + } + + function setFileAttribute($file, $attr, $value) + { + $this->_packageInfo['filelist'][$file][$attr] = $value; + } + + function resetFilelist() + { + $this->_packageInfo['filelist'] = array(); + } + + function setInstalledAs($file, $path) + { + if ($path) { + return $this->_packageInfo['filelist'][$file]['installed_as'] = $path; + } + unset($this->_packageInfo['filelist'][$file]['installed_as']); + } + + function installedFile($file, $atts) + { + if (isset($this->_packageInfo['filelist'][$file])) { + $this->_packageInfo['filelist'][$file] = + array_merge($this->_packageInfo['filelist'][$file], $atts); + } else { + $this->_packageInfo['filelist'][$file] = $atts; + } + } + + function getChangelog() + { + if (isset($this->_packageInfo['changelog'])) { + return $this->_packageInfo['changelog']; + } + return false; + } + + function getPackagexmlVersion() + { + return '1.0'; + } + + /** + * Wrapper to {@link PEAR_ErrorStack::getErrors()} + * @param boolean determines whether to purge the error stack after retrieving + * @return array + */ + function getValidationWarnings($purge = true) + { + return $this->_stack->getErrors($purge); + } + + // }}} + /** + * Validation error. Also marks the object contents as invalid + * @param error code + * @param array error information + * @access private + */ + function _validateError($code, $params = array()) + { + $this->_stack->push($code, 'error', $params, false, false, debug_backtrace()); + $this->_isValid = false; + } + + /** + * Validation warning. Does not mark the object contents invalid. + * @param error code + * @param array error information + * @access private + */ + function _validateWarning($code, $params = array()) + { + $this->_stack->push($code, 'warning', $params, false, false, debug_backtrace()); + } + + /** + * @param integer error code + * @access protected + */ + function _getErrorMessage() + { + return array( + PEAR_PACKAGEFILE_ERROR_NO_NAME => + 'Missing Package Name', + PEAR_PACKAGEFILE_ERROR_NO_SUMMARY => + 'No summary found', + PEAR_PACKAGEFILE_ERROR_MULTILINE_SUMMARY => + 'Summary should be on one line', + PEAR_PACKAGEFILE_ERROR_NO_DESCRIPTION => + 'Missing description', + PEAR_PACKAGEFILE_ERROR_NO_LICENSE => + 'Missing license', + PEAR_PACKAGEFILE_ERROR_NO_VERSION => + 'No release version found', + PEAR_PACKAGEFILE_ERROR_NO_STATE => + 'No release state found', + PEAR_PACKAGEFILE_ERROR_NO_DATE => + 'No release date found', + PEAR_PACKAGEFILE_ERROR_NO_NOTES => + 'No release notes found', + PEAR_PACKAGEFILE_ERROR_NO_LEAD => + 'Package must have at least one lead maintainer', + PEAR_PACKAGEFILE_ERROR_NO_MAINTAINERS => + 'No maintainers found, at least one must be defined', + PEAR_PACKAGEFILE_ERROR_NO_MAINTHANDLE => + 'Maintainer %index% has no handle (user ID at channel server)', + PEAR_PACKAGEFILE_ERROR_NO_MAINTROLE => + 'Maintainer %index% has no role', + PEAR_PACKAGEFILE_ERROR_NO_MAINTNAME => + 'Maintainer %index% has no name', + PEAR_PACKAGEFILE_ERROR_NO_MAINTEMAIL => + 'Maintainer %index% has no email', + PEAR_PACKAGEFILE_ERROR_NO_DEPNAME => + 'Dependency %index% is not a php dependency, and has no name', + PEAR_PACKAGEFILE_ERROR_NO_DEPREL => + 'Dependency %index% has no relation (rel)', + PEAR_PACKAGEFILE_ERROR_NO_DEPTYPE => + 'Dependency %index% has no type', + PEAR_PACKAGEFILE_ERROR_DEPNAME_IGNORED => + 'PHP Dependency %index% has a name attribute of "%name%" which will be' . + ' ignored!', + PEAR_PACKAGEFILE_ERROR_NO_DEPVERSION => + 'Dependency %index% is not a rel="has" or rel="not" dependency, ' . + 'and has no version', + PEAR_PACKAGEFILE_ERROR_NO_DEPPHPVERSION => + 'Dependency %index% is a type="php" dependency, ' . + 'and has no version', + PEAR_PACKAGEFILE_ERROR_DEPVERSION_IGNORED => + 'Dependency %index% is a rel="%rel%" dependency, versioning is ignored', + PEAR_PACKAGEFILE_ERROR_INVALID_DEPOPTIONAL => + 'Dependency %index% has invalid optional value "%opt%", should be yes or no', + PEAR_PACKAGEFILE_PHP_NO_NOT => + 'Dependency %index%: php dependencies cannot use "not" rel, use "ne"' . + ' to exclude specific versions', + PEAR_PACKAGEFILE_ERROR_NO_CONFNAME => + 'Configure Option %index% has no name', + PEAR_PACKAGEFILE_ERROR_NO_CONFPROMPT => + 'Configure Option %index% has no prompt', + PEAR_PACKAGEFILE_ERROR_NO_FILES => + 'No files in section of package.xml', + PEAR_PACKAGEFILE_ERROR_NO_FILEROLE => + 'File "%file%" has no role, expecting one of "%roles%"', + PEAR_PACKAGEFILE_ERROR_INVALID_FILEROLE => + 'File "%file%" has invalid role "%role%", expecting one of "%roles%"', + PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME => + 'File "%file%" cannot start with ".", cannot package or install', + PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE => + 'Parser error: invalid PHP found in file "%file%"', + PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX => + 'in %file%: %type% "%name%" not prefixed with package name "%package%"', + PEAR_PACKAGEFILE_ERROR_INVALID_FILE => + 'Parser error: invalid PHP file "%file%"', + PEAR_PACKAGEFILE_ERROR_CHANNELVAL => + 'Channel validator error: field "%field%" - %reason%', + PEAR_PACKAGEFILE_ERROR_PHP5 => + 'Error, PHP5 token encountered in %file%, analysis should be in PHP5', + PEAR_PACKAGEFILE_ERROR_FILE_NOTFOUND => + 'File "%file%" in package.xml does not exist', + PEAR_PACKAGEFILE_ERROR_NON_ISO_CHARS => + 'Package.xml contains non-ISO-8859-1 characters, and may not validate', + ); + } + + /** + * Validate XML package definition file. + * + * @access public + * @return boolean + */ + function validate($state = PEAR_VALIDATE_NORMAL, $nofilechecking = false) + { + if (($this->_isValid & $state) == $state) { + return true; + } + $this->_isValid = true; + $info = $this->_packageInfo; + if (empty($info['package'])) { + $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_NAME); + $this->_packageName = $pn = 'unknown'; + } else { + $this->_packageName = $pn = $info['package']; + } + + if (empty($info['summary'])) { + $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_SUMMARY); + } elseif (strpos(trim($info['summary']), "\n") !== false) { + $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_MULTILINE_SUMMARY, + array('summary' => $info['summary'])); + } + if (empty($info['description'])) { + $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DESCRIPTION); + } + if (empty($info['release_license'])) { + $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_LICENSE); + } + if (empty($info['version'])) { + $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_VERSION); + } + if (empty($info['release_state'])) { + $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_STATE); + } + if (empty($info['release_date'])) { + $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DATE); + } + if (empty($info['release_notes'])) { + $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_NOTES); + } + if (empty($info['maintainers'])) { + $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTAINERS); + } else { + $haslead = false; + $i = 1; + foreach ($info['maintainers'] as $m) { + if (empty($m['handle'])) { + $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTHANDLE, + array('index' => $i)); + } + if (empty($m['role'])) { + $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTROLE, + array('index' => $i, 'roles' => PEAR_Common::getUserRoles())); + } elseif ($m['role'] == 'lead') { + $haslead = true; + } + if (empty($m['name'])) { + $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTNAME, + array('index' => $i)); + } + if (empty($m['email'])) { + $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTEMAIL, + array('index' => $i)); + } + $i++; + } + if (!$haslead) { + $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_LEAD); + } + } + if (!empty($info['release_deps'])) { + $i = 1; + foreach ($info['release_deps'] as $d) { + if (!isset($d['type']) || empty($d['type'])) { + $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPTYPE, + array('index' => $i, 'types' => PEAR_Common::getDependencyTypes())); + continue; + } + if (!isset($d['rel']) || empty($d['rel'])) { + $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPREL, + array('index' => $i, 'rels' => PEAR_Common::getDependencyRelations())); + continue; + } + if (!empty($d['optional'])) { + if (!in_array($d['optional'], array('yes', 'no'))) { + $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_DEPOPTIONAL, + array('index' => $i, 'opt' => $d['optional'])); + } + } + if ($d['rel'] != 'has' && $d['rel'] != 'not' && empty($d['version'])) { + $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPVERSION, + array('index' => $i)); + } elseif (($d['rel'] == 'has' || $d['rel'] == 'not') && !empty($d['version'])) { + $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_DEPVERSION_IGNORED, + array('index' => $i, 'rel' => $d['rel'])); + } + if ($d['type'] == 'php' && !empty($d['name'])) { + $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_DEPNAME_IGNORED, + array('index' => $i, 'name' => $d['name'])); + } elseif ($d['type'] != 'php' && empty($d['name'])) { + $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPNAME, + array('index' => $i)); + } + if ($d['type'] == 'php' && empty($d['version'])) { + $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPPHPVERSION, + array('index' => $i)); + } + if (($d['rel'] == 'not') && ($d['type'] == 'php')) { + $this->_validateError(PEAR_PACKAGEFILE_PHP_NO_NOT, + array('index' => $i)); + } + $i++; + } + } + if (!empty($info['configure_options'])) { + $i = 1; + foreach ($info['configure_options'] as $c) { + if (empty($c['name'])) { + $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_CONFNAME, + array('index' => $i)); + } + if (empty($c['prompt'])) { + $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_CONFPROMPT, + array('index' => $i)); + } + $i++; + } + } + if (empty($info['filelist'])) { + $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_FILES); + $errors[] = 'no files'; + } else { + foreach ($info['filelist'] as $file => $fa) { + if (empty($fa['role'])) { + $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_FILEROLE, + array('file' => $file, 'roles' => PEAR_Common::getFileRoles())); + continue; + } elseif (!in_array($fa['role'], PEAR_Common::getFileRoles())) { + $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILEROLE, + array('file' => $file, 'role' => $fa['role'], 'roles' => PEAR_Common::getFileRoles())); + } + if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~', str_replace('\\', '/', $file))) { + // file contains .. parent directory or . cur directory references + $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME, + array('file' => $file)); + } + if (isset($fa['install-as']) && + preg_match('~/\.\.?(/|\\z)|^\.\.?/~', + str_replace('\\', '/', $fa['install-as']))) { + // install-as contains .. parent directory or . cur directory references + $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME, + array('file' => $file . ' [installed as ' . $fa['install-as'] . ']')); + } + if (isset($fa['baseinstalldir']) && + preg_match('~/\.\.?(/|\\z)|^\.\.?/~', + str_replace('\\', '/', $fa['baseinstalldir']))) { + // install-as contains .. parent directory or . cur directory references + $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME, + array('file' => $file . ' [baseinstalldir ' . $fa['baseinstalldir'] . ']')); + } + } + } + if (isset($this->_registry) && $this->_isValid) { + $chan = $this->_registry->getChannel('pear.php.net'); + if (PEAR::isError($chan)) { + $this->_validateError(PEAR_PACKAGEFILE_ERROR_CHANNELVAL, $chan->getMessage()); + return $this->_isValid = 0; + } + $validator = $chan->getValidationObject(); + $validator->setPackageFile($this); + $validator->validate($state); + $failures = $validator->getFailures(); + foreach ($failures['errors'] as $error) { + $this->_validateError(PEAR_PACKAGEFILE_ERROR_CHANNELVAL, $error); + } + foreach ($failures['warnings'] as $warning) { + $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_CHANNELVAL, $warning); + } + } + if ($this->_isValid && $state == PEAR_VALIDATE_PACKAGING && !$nofilechecking) { + if ($this->_analyzePhpFiles()) { + $this->_isValid = true; + } + } + if ($this->_isValid) { + return $this->_isValid = $state; + } + return $this->_isValid = 0; + } + + function _analyzePhpFiles() + { + if (!$this->_isValid) { + return false; + } + if (!isset($this->_packageFile)) { + return false; + } + $dir_prefix = dirname($this->_packageFile); + $common = new PEAR_Common; + $log = isset($this->_logger) ? array(&$this->_logger, 'log') : + array($common, 'log'); + $info = $this->getFilelist(); + foreach ($info as $file => $fa) { + if (!file_exists($dir_prefix . DIRECTORY_SEPARATOR . $file)) { + $this->_validateError(PEAR_PACKAGEFILE_ERROR_FILE_NOTFOUND, + array('file' => realpath($dir_prefix) . DIRECTORY_SEPARATOR . $file)); + continue; + } + if ($fa['role'] == 'php' && $dir_prefix) { + call_user_func_array($log, array(1, "Analyzing $file")); + $srcinfo = $this->_analyzeSourceCode($dir_prefix . DIRECTORY_SEPARATOR . $file); + if ($srcinfo) { + $this->_buildProvidesArray($srcinfo); + } + } + } + $this->_packageName = $pn = $this->getPackage(); + $pnl = strlen($pn); + if (isset($this->_packageInfo['provides'])) { + foreach ((array) $this->_packageInfo['provides'] as $key => $what) { + if (isset($what['explicit'])) { + // skip conformance checks if the provides entry is + // specified in the package.xml file + continue; + } + extract($what); + if ($type == 'class') { + if (!strncasecmp($name, $pn, $pnl)) { + continue; + } + $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX, + array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn)); + } elseif ($type == 'function') { + if (strstr($name, '::') || !strncasecmp($name, $pn, $pnl)) { + continue; + } + $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX, + array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn)); + } + } + } + return $this->_isValid; + } + + /** + * Get the default xml generator object + * + * @return PEAR_PackageFile_Generator_v1 + */ + function &getDefaultGenerator() + { + if (!class_exists('PEAR_PackageFile_Generator_v1')) { + require_once 'PEAR/PackageFile/Generator/v1.php'; + } + $a = &new PEAR_PackageFile_Generator_v1($this); + return $a; + } + + /** + * Get the contents of a file listed within the package.xml + * @param string + * @return string + */ + function getFileContents($file) + { + if ($this->_archiveFile == $this->_packageFile) { // unpacked + $dir = dirname($this->_packageFile); + $file = $dir . DIRECTORY_SEPARATOR . $file; + $file = str_replace(array('/', '\\'), + array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR), $file); + if (file_exists($file) && is_readable($file)) { + return implode('', file($file)); + } + } else { // tgz + if (!class_exists('Archive_Tar')) { + require_once 'Archive/Tar.php'; + } + $tar = &new Archive_Tar($this->_archiveFile); + $tar->pushErrorHandling(PEAR_ERROR_RETURN); + if ($file != 'package.xml' && $file != 'package2.xml') { + $file = $this->getPackage() . '-' . $this->getVersion() . '/' . $file; + } + $file = $tar->extractInString($file); + $tar->popErrorHandling(); + if (PEAR::isError($file)) { + return PEAR::raiseError("Cannot locate file '$file' in archive"); + } + return $file; + } + } + + // {{{ analyzeSourceCode() + /** + * Analyze the source code of the given PHP file + * + * @param string Filename of the PHP file + * @return mixed + * @access private + */ + function _analyzeSourceCode($file) + { + if (!function_exists("token_get_all")) { + return false; + } + if (!defined('T_DOC_COMMENT')) { + define('T_DOC_COMMENT', T_COMMENT); + } + if (!defined('T_INTERFACE')) { + define('T_INTERFACE', -1); + } + if (!defined('T_IMPLEMENTS')) { + define('T_IMPLEMENTS', -1); + } + if (!$fp = @fopen($file, "r")) { + return false; + } + fclose($fp); + $contents = file_get_contents($file); + $tokens = token_get_all($contents); +/* + for ($i = 0; $i < sizeof($tokens); $i++) { + @list($token, $data) = $tokens[$i]; + if (is_string($token)) { + var_dump($token); + } else { + print token_name($token) . ' '; + var_dump(rtrim($data)); + } + } +*/ + $look_for = 0; + $paren_level = 0; + $bracket_level = 0; + $brace_level = 0; + $lastphpdoc = ''; + $current_class = ''; + $current_interface = ''; + $current_class_level = -1; + $current_function = ''; + $current_function_level = -1; + $declared_classes = array(); + $declared_interfaces = array(); + $declared_functions = array(); + $declared_methods = array(); + $used_classes = array(); + $used_functions = array(); + $extends = array(); + $implements = array(); + $nodeps = array(); + $inquote = false; + $interface = false; + for ($i = 0; $i < sizeof($tokens); $i++) { + if (is_array($tokens[$i])) { + list($token, $data) = $tokens[$i]; + } else { + $token = $tokens[$i]; + $data = ''; + } + if ($inquote) { + if ($token != '"' && $token != T_END_HEREDOC) { + continue; + } else { + $inquote = false; + continue; + } + } + switch ($token) { + case T_WHITESPACE : + continue; + case ';': + if ($interface) { + $current_function = ''; + $current_function_level = -1; + } + break; + case '"': + case T_START_HEREDOC: + $inquote = true; + break; + case T_CURLY_OPEN: + case T_DOLLAR_OPEN_CURLY_BRACES: + case '{': $brace_level++; continue 2; + case '}': + $brace_level--; + if ($current_class_level == $brace_level) { + $current_class = ''; + $current_class_level = -1; + } + if ($current_function_level == $brace_level) { + $current_function = ''; + $current_function_level = -1; + } + continue 2; + case '[': $bracket_level++; continue 2; + case ']': $bracket_level--; continue 2; + case '(': $paren_level++; continue 2; + case ')': $paren_level--; continue 2; + case T_INTERFACE: + $interface = true; + case T_CLASS: + if (($current_class_level != -1) || ($current_function_level != -1)) { + $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE, + array('file' => $file)); + return false; + } + case T_FUNCTION: + case T_NEW: + case T_EXTENDS: + case T_IMPLEMENTS: + $look_for = $token; + continue 2; + case T_STRING: + if (version_compare(zend_version(), '2.0', '<')) { + if (in_array(strtolower($data), + array('public', 'private', 'protected', 'abstract', + 'interface', 'implements', 'throw') + )) { + $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_PHP5, + array($file)); + } + } + if ($look_for == T_CLASS) { + $current_class = $data; + $current_class_level = $brace_level; + $declared_classes[] = $current_class; + } elseif ($look_for == T_INTERFACE) { + $current_interface = $data; + $current_class_level = $brace_level; + $declared_interfaces[] = $current_interface; + } elseif ($look_for == T_IMPLEMENTS) { + $implements[$current_class] = $data; + } elseif ($look_for == T_EXTENDS) { + $extends[$current_class] = $data; + } elseif ($look_for == T_FUNCTION) { + if ($current_class) { + $current_function = "$current_class::$data"; + $declared_methods[$current_class][] = $data; + } elseif ($current_interface) { + $current_function = "$current_interface::$data"; + $declared_methods[$current_interface][] = $data; + } else { + $current_function = $data; + $declared_functions[] = $current_function; + } + $current_function_level = $brace_level; + $m = array(); + } elseif ($look_for == T_NEW) { + $used_classes[$data] = true; + } + $look_for = 0; + continue 2; + case T_VARIABLE: + $look_for = 0; + continue 2; + case T_DOC_COMMENT: + case T_COMMENT: + if (preg_match('!^/\*\*\s!', $data)) { + $lastphpdoc = $data; + if (preg_match_all('/@nodep\s+(\S+)/', $lastphpdoc, $m)) { + $nodeps = array_merge($nodeps, $m[1]); + } + } + continue 2; + case T_DOUBLE_COLON: + if (!($tokens[$i - 1][0] == T_WHITESPACE || $tokens[$i - 1][0] == T_STRING)) { + $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE, + array('file' => $file)); + return false; + } + $class = $tokens[$i - 1][1]; + if (strtolower($class) != 'parent') { + $used_classes[$class] = true; + } + continue 2; + } + } + return array( + "source_file" => $file, + "declared_classes" => $declared_classes, + "declared_interfaces" => $declared_interfaces, + "declared_methods" => $declared_methods, + "declared_functions" => $declared_functions, + "used_classes" => array_diff(array_keys($used_classes), $nodeps), + "inheritance" => $extends, + "implements" => $implements, + ); + } + + /** + * Build a "provides" array from data returned by + * analyzeSourceCode(). The format of the built array is like + * this: + * + * array( + * 'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'), + * ... + * ) + * + * + * @param array $srcinfo array with information about a source file + * as returned by the analyzeSourceCode() method. + * + * @return void + * + * @access private + * + */ + function _buildProvidesArray($srcinfo) + { + if (!$this->_isValid) { + return false; + } + $file = basename($srcinfo['source_file']); + $pn = $this->getPackage(); + $pnl = strlen($pn); + foreach ($srcinfo['declared_classes'] as $class) { + $key = "class;$class"; + if (isset($this->_packageInfo['provides'][$key])) { + continue; + } + $this->_packageInfo['provides'][$key] = + array('file'=> $file, 'type' => 'class', 'name' => $class); + if (isset($srcinfo['inheritance'][$class])) { + $this->_packageInfo['provides'][$key]['extends'] = + $srcinfo['inheritance'][$class]; + } + } + foreach ($srcinfo['declared_methods'] as $class => $methods) { + foreach ($methods as $method) { + $function = "$class::$method"; + $key = "function;$function"; + if ($method{0} == '_' || !strcasecmp($method, $class) || + isset($this->_packageInfo['provides'][$key])) { + continue; + } + $this->_packageInfo['provides'][$key] = + array('file'=> $file, 'type' => 'function', 'name' => $function); + } + } + + foreach ($srcinfo['declared_functions'] as $function) { + $key = "function;$function"; + if ($function{0} == '_' || isset($this->_packageInfo['provides'][$key])) { + continue; + } + if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) { + $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\""; + } + $this->_packageInfo['provides'][$key] = + array('file'=> $file, 'type' => 'function', 'name' => $function); + } + } + + // }}} +} +?> diff --git a/includes/pear/PEAR/PackageFile/v2.php b/includes/pear/PEAR/PackageFile/v2.php new file mode 100644 index 0000000..27310d4 --- /dev/null +++ b/includes/pear/PEAR/PackageFile/v2.php @@ -0,0 +1,2049 @@ + + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a1 + */ +/** + * For error handling + */ +require_once 'PEAR/ErrorStack.php'; +/** + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a1 + */ +class PEAR_PackageFile_v2 +{ + + /** + * Parsed package information + * @var array + * @access private + */ + var $_packageInfo = array(); + + /** + * path to package .tgz or false if this is a local/extracted package.xml + * @var string|false + * @access private + */ + var $_archiveFile; + + /** + * path to package .xml or false if this is an abstract parsed-from-string xml + * @var string|false + * @access private + */ + var $_packageFile; + + /** + * This is used by file analysis routines to log progress information + * @var PEAR_Common + * @access protected + */ + var $_logger; + + /** + * This is set to the highest validation level that has been validated + * + * If the package.xml is invalid or unknown, this is set to 0. If + * normal validation has occurred, this is set to PEAR_VALIDATE_NORMAL. If + * downloading/installation validation has occurred it is set to PEAR_VALIDATE_DOWNLOADING + * or INSTALLING, and so on up to PEAR_VALIDATE_PACKAGING. This allows validation + * "caching" to occur, which is particularly important for package validation, so + * that PHP files are not validated twice + * @var int + * @access private + */ + var $_isValid = 0; + + /** + * True if the filelist has been validated + * @param bool + */ + var $_filesValid = false; + + /** + * @var PEAR_Registry + * @access protected + */ + var $_registry; + + /** + * @var PEAR_Config + * @access protected + */ + var $_config; + + /** + * Optional Dependency group requested for installation + * @var string + * @access private + */ + var $_requestedGroup = false; + + /** + * @var PEAR_ErrorStack + * @access protected + */ + var $_stack; + + /** + * Namespace prefix used for tasks in this package.xml - use tasks: whenever possible + */ + var $_tasksNs; + + /** + * Determines whether this packagefile was initialized only with partial package info + * + * If this package file was constructed via parsing REST, it will only contain + * + * - package name + * - channel name + * - dependencies + * @var boolean + * @access private + */ + var $_incomplete = true; + + /** + * @var PEAR_PackageFile_v2_Validator + */ + var $_v2Validator; + + /** + * The constructor merely sets up the private error stack + */ + function PEAR_PackageFile_v2() + { + $this->_stack = new PEAR_ErrorStack('PEAR_PackageFile_v2', false, null); + $this->_isValid = false; + } + + /** + * To make unit-testing easier + * @param PEAR_Frontend_* + * @param array options + * @param PEAR_Config + * @return PEAR_Downloader + * @access protected + */ + function &getPEARDownloader(&$i, $o, &$c) + { + $z = &new PEAR_Downloader($i, $o, $c); + return $z; + } + + /** + * To make unit-testing easier + * @param PEAR_Config + * @param array options + * @param array package name as returned from {@link PEAR_Registry::parsePackageName()} + * @param int PEAR_VALIDATE_* constant + * @return PEAR_Dependency2 + * @access protected + */ + function &getPEARDependency2(&$c, $o, $p, $s = PEAR_VALIDATE_INSTALLING) + { + if (!class_exists('PEAR_Dependency2')) { + require_once 'PEAR/Dependency2.php'; + } + $z = &new PEAR_Dependency2($c, $o, $p, $s); + return $z; + } + + function getInstalledBinary() + { + return isset($this->_packageInfo['#binarypackage']) ? $this->_packageInfo['#binarypackage'] : + false; + } + + /** + * Installation of source package has failed, attempt to download and install the + * binary version of this package. + * @param PEAR_Installer + * @return array|false + */ + function installBinary(&$installer) + { + if (!OS_WINDOWS) { + $a = false; + return $a; + } + if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') { + $releasetype = $this->getPackageType() . 'release'; + if (!is_array($installer->getInstallPackages())) { + $a = false; + return $a; + } + foreach ($installer->getInstallPackages() as $p) { + if ($p->isExtension($this->_packageInfo['providesextension'])) { + if ($p->getPackageType() != 'extsrc' && $p->getPackageType() != 'zendextsrc') { + $a = false; + return $a; // the user probably downloaded it separately + } + } + } + if (isset($this->_packageInfo[$releasetype]['binarypackage'])) { + $installer->log(0, 'Attempting to download binary version of extension "' . + $this->_packageInfo['providesextension'] . '"'); + $params = $this->_packageInfo[$releasetype]['binarypackage']; + if (!is_array($params) || !isset($params[0])) { + $params = array($params); + } + if (isset($this->_packageInfo['channel'])) { + foreach ($params as $i => $param) { + $params[$i] = array('channel' => $this->_packageInfo['channel'], + 'package' => $param, 'version' => $this->getVersion()); + } + } + $dl = &$this->getPEARDownloader($installer->ui, $installer->getOptions(), + $installer->config); + $verbose = $dl->config->get('verbose'); + $dl->config->set('verbose', -1); + foreach ($params as $param) { + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + $ret = $dl->download(array($param)); + PEAR::popErrorHandling(); + if (is_array($ret) && count($ret)) { + break; + } + } + $dl->config->set('verbose', $verbose); + if (is_array($ret)) { + if (count($ret) == 1) { + $pf = $ret[0]->getPackageFile(); + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + $err = $installer->install($ret[0]); + PEAR::popErrorHandling(); + if (is_array($err)) { + $this->_packageInfo['#binarypackage'] = $ret[0]->getPackage(); + // "install" self, so all dependencies will work transparently + $this->_registry->addPackage2($this); + $installer->log(0, 'Download and install of binary extension "' . + $this->_registry->parsedPackageNameToString( + array('channel' => $pf->getChannel(), + 'package' => $pf->getPackage()), true) . '" successful'); + $a = array($ret[0], $err); + return $a; + } + $installer->log(0, 'Download and install of binary extension "' . + $this->_registry->parsedPackageNameToString( + array('channel' => $pf->getChannel(), + 'package' => $pf->getPackage()), true) . '" failed'); + } + } + } + } + $a = false; + return $a; + } + + /** + * @return string|false Extension name + */ + function getProvidesExtension() + { + if (in_array($this->getPackageType(), + array('extsrc', 'extbin', 'zendextsrc', 'zendextbin'))) { + if (isset($this->_packageInfo['providesextension'])) { + return $this->_packageInfo['providesextension']; + } + } + return false; + } + + /** + * @param string Extension name + * @return bool + */ + function isExtension($extension) + { + if (in_array($this->getPackageType(), + array('extsrc', 'extbin', 'zendextsrc', 'zendextbin'))) { + return $this->_packageInfo['providesextension'] == $extension; + } + return false; + } + + /** + * Tests whether every part of the package.xml 1.0 is represented in + * this package.xml 2.0 + * @param PEAR_PackageFile_v1 + * @return bool + */ + function isEquivalent($pf1) + { + if (!$pf1) { + return true; + } + if ($this->getPackageType() == 'bundle') { + return false; + } + $this->_stack->getErrors(true); + if (!$pf1->validate(PEAR_VALIDATE_NORMAL)) { + return false; + } + $pass = true; + if ($pf1->getPackage() != $this->getPackage()) { + $this->_differentPackage($pf1->getPackage()); + $pass = false; + } + if ($pf1->getVersion() != $this->getVersion()) { + $this->_differentVersion($pf1->getVersion()); + $pass = false; + } + if (trim($pf1->getSummary()) != $this->getSummary()) { + $this->_differentSummary($pf1->getSummary()); + $pass = false; + } + if (preg_replace('/\s+/', '', $pf1->getDescription()) != + preg_replace('/\s+/', '', $this->getDescription())) { + $this->_differentDescription($pf1->getDescription()); + $pass = false; + } + if ($pf1->getState() != $this->getState()) { + $this->_differentState($pf1->getState()); + $pass = false; + } + if (!strstr(preg_replace('/\s+/', '', $this->getNotes()), + preg_replace('/\s+/', '', $pf1->getNotes()))) { + $this->_differentNotes($pf1->getNotes()); + $pass = false; + } + $mymaintainers = $this->getMaintainers(); + $yourmaintainers = $pf1->getMaintainers(); + for ($i1 = 0; $i1 < count($yourmaintainers); $i1++) { + $reset = false; + for ($i2 = 0; $i2 < count($mymaintainers); $i2++) { + if ($mymaintainers[$i2]['handle'] == $yourmaintainers[$i1]['handle']) { + if ($mymaintainers[$i2]['role'] != $yourmaintainers[$i1]['role']) { + $this->_differentRole($mymaintainers[$i2]['handle'], + $yourmaintainers[$i1]['role'], $mymaintainers[$i2]['role']); + $pass = false; + } + if ($mymaintainers[$i2]['email'] != $yourmaintainers[$i1]['email']) { + $this->_differentEmail($mymaintainers[$i2]['handle'], + $yourmaintainers[$i1]['email'], $mymaintainers[$i2]['email']); + $pass = false; + } + if ($mymaintainers[$i2]['name'] != $yourmaintainers[$i1]['name']) { + $this->_differentName($mymaintainers[$i2]['handle'], + $yourmaintainers[$i1]['name'], $mymaintainers[$i2]['name']); + $pass = false; + } + unset($mymaintainers[$i2]); + $mymaintainers = array_values($mymaintainers); + unset($yourmaintainers[$i1]); + $yourmaintainers = array_values($yourmaintainers); + $reset = true; + break; + } + } + if ($reset) { + $i1 = -1; + } + } + $this->_unmatchedMaintainers($mymaintainers, $yourmaintainers); + $filelist = $this->getFilelist(); + foreach ($pf1->getFilelist() as $file => $atts) { + if (!isset($filelist[$file])) { + $this->_missingFile($file); + $pass = false; + } + } + return $pass; + } + + function _differentPackage($package) + { + $this->_stack->push(__FUNCTION__, 'error', array('package' => $package, + 'self' => $this->getPackage()), + 'package.xml 1.0 package "%package%" does not match "%self%"'); + } + + function _differentVersion($version) + { + $this->_stack->push(__FUNCTION__, 'error', array('version' => $version, + 'self' => $this->getVersion()), + 'package.xml 1.0 version "%version%" does not match "%self%"'); + } + + function _differentState($state) + { + $this->_stack->push(__FUNCTION__, 'error', array('state' => $state, + 'self' => $this->getState()), + 'package.xml 1.0 state "%state%" does not match "%self%"'); + } + + function _differentRole($handle, $role, $selfrole) + { + $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle, + 'role' => $role, 'self' => $selfrole), + 'package.xml 1.0 maintainer "%handle%" role "%role%" does not match "%self%"'); + } + + function _differentEmail($handle, $email, $selfemail) + { + $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle, + 'email' => $email, 'self' => $selfemail), + 'package.xml 1.0 maintainer "%handle%" email "%email%" does not match "%self%"'); + } + + function _differentName($handle, $name, $selfname) + { + $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle, + 'name' => $name, 'self' => $selfname), + 'package.xml 1.0 maintainer "%handle%" name "%name%" does not match "%self%"'); + } + + function _unmatchedMaintainers($my, $yours) + { + if ($my) { + array_walk($my, create_function('&$i, $k', '$i = $i["handle"];')); + $this->_stack->push(__FUNCTION__, 'error', array('handles' => $my), + 'package.xml 2.0 has unmatched extra maintainers "%handles%"'); + } + if ($yours) { + array_walk($yours, create_function('&$i, $k', '$i = $i["handle"];')); + $this->_stack->push(__FUNCTION__, 'error', array('handles' => $yours), + 'package.xml 1.0 has unmatched extra maintainers "%handles%"'); + } + } + + function _differentNotes($notes) + { + $truncnotes = strlen($notes) < 25 ? $notes : substr($notes, 0, 24) . '...'; + $truncmynotes = strlen($this->getNotes()) < 25 ? $this->getNotes() : + substr($this->getNotes(), 0, 24) . '...'; + $this->_stack->push(__FUNCTION__, 'error', array('notes' => $truncnotes, + 'self' => $truncmynotes), + 'package.xml 1.0 release notes "%notes%" do not match "%self%"'); + } + + function _differentSummary($summary) + { + $truncsummary = strlen($summary) < 25 ? $summary : substr($summary, 0, 24) . '...'; + $truncmysummary = strlen($this->getsummary()) < 25 ? $this->getSummary() : + substr($this->getsummary(), 0, 24) . '...'; + $this->_stack->push(__FUNCTION__, 'error', array('summary' => $truncsummary, + 'self' => $truncmysummary), + 'package.xml 1.0 summary "%summary%" does not match "%self%"'); + } + + function _differentDescription($description) + { + $truncdescription = trim(strlen($description) < 25 ? $description : substr($description, 0, 24) . '...'); + $truncmydescription = trim(strlen($this->getDescription()) < 25 ? $this->getDescription() : + substr($this->getdescription(), 0, 24) . '...'); + $this->_stack->push(__FUNCTION__, 'error', array('description' => $truncdescription, + 'self' => $truncmydescription), + 'package.xml 1.0 description "%description%" does not match "%self%"'); + } + + function _missingFile($file) + { + $this->_stack->push(__FUNCTION__, 'error', array('file' => $file), + 'package.xml 1.0 file "%file%" is not present in '); + } + + /** + * WARNING - do not use this function unless you know what you're doing + */ + function setRawState($state) + { + if (!isset($this->_packageInfo['stability'])) { + $this->_packageInfo['stability'] = array(); + } + $this->_packageInfo['stability']['release'] = $state; + } + + /** + * WARNING - do not use this function unless you know what you're doing + */ + function setRawCompatible($compatible) + { + $this->_packageInfo['compatible'] = $compatible; + } + + /** + * WARNING - do not use this function unless you know what you're doing + */ + function setRawPackage($package) + { + $this->_packageInfo['name'] = $package; + } + + /** + * WARNING - do not use this function unless you know what you're doing + */ + function setRawChannel($channel) + { + $this->_packageInfo['channel'] = $channel; + } + + function setRequestedGroup($group) + { + $this->_requestedGroup = $group; + } + + function getRequestedGroup() + { + if (isset($this->_requestedGroup)) { + return $this->_requestedGroup; + } + return false; + } + + /** + * For saving in the registry. + * + * Set the last version that was installed + * @param string + */ + function setLastInstalledVersion($version) + { + $this->_packageInfo['_lastversion'] = $version; + } + + /** + * @return string|false + */ + function getLastInstalledVersion() + { + if (isset($this->_packageInfo['_lastversion'])) { + return $this->_packageInfo['_lastversion']; + } + return false; + } + + /** + * Determines whether this package.xml has post-install scripts or not + * @return array|false + */ + function listPostinstallScripts() + { + $filelist = $this->getFilelist(); + $contents = $this->getContents(); + $contents = $contents['dir']['file']; + if (!is_array($contents) || !isset($contents[0])) { + $contents = array($contents); + } + $taskfiles = array(); + foreach ($contents as $file) { + $atts = $file['attribs']; + unset($file['attribs']); + if (count($file)) { + $taskfiles[$atts['name']] = $file; + } + } + $common = new PEAR_Common; + $common->debug = $this->_config->get('verbose'); + $this->_scripts = array(); + $ret = array(); + foreach ($taskfiles as $name => $tasks) { + if (!isset($filelist[$name])) { + // ignored files will not be in the filelist + continue; + } + $atts = $filelist[$name]; + foreach ($tasks as $tag => $raw) { + $task = $this->getTask($tag); + $task = &new $task($this->_config, $common, PEAR_TASK_INSTALL); + if ($task->isScript()) { + $ret[] = $filelist[$name]['installed_as']; + } + } + } + if (count($ret)) { + return $ret; + } + return false; + } + + /** + * Initialize post-install scripts for running + * + * This method can be used to detect post-install scripts, as the return value + * indicates whether any exist + * @return bool + */ + function initPostinstallScripts() + { + $filelist = $this->getFilelist(); + $contents = $this->getContents(); + $contents = $contents['dir']['file']; + if (!is_array($contents) || !isset($contents[0])) { + $contents = array($contents); + } + $taskfiles = array(); + foreach ($contents as $file) { + $atts = $file['attribs']; + unset($file['attribs']); + if (count($file)) { + $taskfiles[$atts['name']] = $file; + } + } + $common = new PEAR_Common; + $common->debug = $this->_config->get('verbose'); + $this->_scripts = array(); + foreach ($taskfiles as $name => $tasks) { + if (!isset($filelist[$name])) { + // file was not installed due to installconditions + continue; + } + $atts = $filelist[$name]; + foreach ($tasks as $tag => $raw) { + $taskname = $this->getTask($tag); + $task = &new $taskname($this->_config, $common, PEAR_TASK_INSTALL); + if (!$task->isScript()) { + continue; // scripts are only handled after installation + } + $lastversion = isset($this->_packageInfo['_lastversion']) ? + $this->_packageInfo['_lastversion'] : null; + $task->init($raw, $atts, $lastversion); + $res = $task->startSession($this, $atts['installed_as']); + if (!$res) { + continue; // skip this file + } + if (PEAR::isError($res)) { + return $res; + } + $assign = &$task; + $this->_scripts[] = &$assign; + } + } + if (count($this->_scripts)) { + return true; + } + return false; + } + + function runPostinstallScripts() + { + if ($this->initPostinstallScripts()) { + $ui = &PEAR_Frontend::singleton(); + if ($ui) { + $ui->runPostinstallScripts($this->_scripts, $this); + } + } + } + + + /** + * Convert a recursive set of
    and tags into a single tag with + * tags. + */ + function flattenFilelist() + { + if (isset($this->_packageInfo['bundle'])) { + return; + } + $filelist = array(); + if (isset($this->_packageInfo['contents']['dir']['dir'])) { + $this->_getFlattenedFilelist($filelist, $this->_packageInfo['contents']['dir']); + if (!isset($filelist[1])) { + $filelist = $filelist[0]; + } + $this->_packageInfo['contents']['dir']['file'] = $filelist; + unset($this->_packageInfo['contents']['dir']['dir']); + } else { + // else already flattened but check for baseinstalldir propagation + if (isset($this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'])) { + if (isset($this->_packageInfo['contents']['dir']['file'][0])) { + foreach ($this->_packageInfo['contents']['dir']['file'] as $i => $file) { + if (isset($file['attribs']['baseinstalldir'])) { + continue; + } + $this->_packageInfo['contents']['dir']['file'][$i]['attribs']['baseinstalldir'] + = $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir']; + } + } else { + if (!isset($this->_packageInfo['contents']['dir']['file']['attribs']['baseinstalldir'])) { + $this->_packageInfo['contents']['dir']['file']['attribs']['baseinstalldir'] + = $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir']; + } + } + } + } + } + + /** + * @param array the final flattened file list + * @param array the current directory being processed + * @param string|false any recursively inherited baeinstalldir attribute + * @param string private recursion variable + * @return array + * @access protected + */ + function _getFlattenedFilelist(&$files, $dir, $baseinstall = false, $path = '') + { + if (isset($dir['attribs']) && isset($dir['attribs']['baseinstalldir'])) { + $baseinstall = $dir['attribs']['baseinstalldir']; + } + if (isset($dir['dir'])) { + if (!isset($dir['dir'][0])) { + $dir['dir'] = array($dir['dir']); + } + foreach ($dir['dir'] as $subdir) { + if (!isset($subdir['attribs']) || !isset($subdir['attribs']['name'])) { + $name = '*unknown*'; + } else { + $name = $subdir['attribs']['name']; + } + $newpath = empty($path) ? $name : + $path . '/' . $name; + $this->_getFlattenedFilelist($files, $subdir, + $baseinstall, $newpath); + } + } + if (isset($dir['file'])) { + if (!isset($dir['file'][0])) { + $dir['file'] = array($dir['file']); + } + foreach ($dir['file'] as $file) { + $attrs = $file['attribs']; + $name = $attrs['name']; + if ($baseinstall && !isset($attrs['baseinstalldir'])) { + $attrs['baseinstalldir'] = $baseinstall; + } + $attrs['name'] = empty($path) ? $name : $path . '/' . $name; + $attrs['name'] = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'), + $attrs['name']); + $file['attribs'] = $attrs; + $files[] = $file; + } + } + } + + function setConfig(&$config) + { + $this->_config = &$config; + $this->_registry = &$config->getRegistry(); + } + + function setLogger(&$logger) + { + if (!is_object($logger) || !method_exists($logger, 'log')) { + return PEAR::raiseError('Logger must be compatible with PEAR_Common::log'); + } + $this->_logger = &$logger; + } + + /** + * WARNING - do not use this function directly unless you know what you're doing + */ + function setDeps($deps) + { + $this->_packageInfo['dependencies'] = $deps; + } + + /** + * WARNING - do not use this function directly unless you know what you're doing + */ + function setCompatible($compat) + { + $this->_packageInfo['compatible'] = $compat; + } + + function setPackagefile($file, $archive = false) + { + $this->_packageFile = $file; + $this->_archiveFile = $archive ? $archive : $file; + } + + /** + * Wrapper to {@link PEAR_ErrorStack::getErrors()} + * @param boolean determines whether to purge the error stack after retrieving + * @return array + */ + function getValidationWarnings($purge = true) + { + return $this->_stack->getErrors($purge); + } + + function getPackageFile() + { + return $this->_packageFile; + } + + function getArchiveFile() + { + return $this->_archiveFile; + } + + + /** + * Directly set the array that defines this packagefile + * + * WARNING: no validation. This should only be performed by internal methods + * inside PEAR or by inputting an array saved from an existing PEAR_PackageFile_v2 + * @param array + */ + function fromArray($pinfo) + { + unset($pinfo['old']); + unset($pinfo['xsdversion']); + // If the changelog isn't an array then it was passed in as an empty tag + if (isset($pinfo['changelog']) && !is_array($pinfo['changelog'])) { + unset($pinfo['changelog']); + } + $this->_incomplete = false; + $this->_packageInfo = $pinfo; + } + + function isIncomplete() + { + return $this->_incomplete; + } + + /** + * @return array + */ + function toArray($forreg = false) + { + if (!$this->validate(PEAR_VALIDATE_NORMAL)) { + return false; + } + return $this->getArray($forreg); + } + + function getArray($forReg = false) + { + if ($forReg) { + $arr = $this->_packageInfo; + $arr['old'] = array(); + $arr['old']['version'] = $this->getVersion(); + $arr['old']['release_date'] = $this->getDate(); + $arr['old']['release_state'] = $this->getState(); + $arr['old']['release_license'] = $this->getLicense(); + $arr['old']['release_notes'] = $this->getNotes(); + $arr['old']['release_deps'] = $this->getDeps(); + $arr['old']['maintainers'] = $this->getMaintainers(); + $arr['xsdversion'] = '2.0'; + return $arr; + } else { + $info = $this->_packageInfo; + unset($info['dirtree']); + if (isset($info['_lastversion'])) { + unset($info['_lastversion']); + } + if (isset($info['#binarypackage'])) { + unset($info['#binarypackage']); + } + return $info; + } + } + + function packageInfo($field) + { + $arr = $this->getArray(true); + if ($field == 'state') { + return $arr['stability']['release']; + } + if ($field == 'api-version') { + return $arr['version']['api']; + } + if ($field == 'api-state') { + return $arr['stability']['api']; + } + if (isset($arr['old'][$field])) { + if (!is_string($arr['old'][$field])) { + return null; + } + return $arr['old'][$field]; + } + if (isset($arr[$field])) { + if (!is_string($arr[$field])) { + return null; + } + return $arr[$field]; + } + return null; + } + + function getName() + { + return $this->getPackage(); + } + + function getPackage() + { + if (isset($this->_packageInfo['name'])) { + return $this->_packageInfo['name']; + } + return false; + } + + function getChannel() + { + if (isset($this->_packageInfo['uri'])) { + return '__uri'; + } + if (isset($this->_packageInfo['channel'])) { + return strtolower($this->_packageInfo['channel']); + } + return false; + } + + function getUri() + { + if (isset($this->_packageInfo['uri'])) { + return $this->_packageInfo['uri']; + } + return false; + } + + function getExtends() + { + if (isset($this->_packageInfo['extends'])) { + return $this->_packageInfo['extends']; + } + return false; + } + + function getSummary() + { + if (isset($this->_packageInfo['summary'])) { + return $this->_packageInfo['summary']; + } + return false; + } + + function getDescription() + { + if (isset($this->_packageInfo['description'])) { + return $this->_packageInfo['description']; + } + return false; + } + + function getMaintainers($raw = false) + { + if (!isset($this->_packageInfo['lead'])) { + return false; + } + if ($raw) { + $ret = array('lead' => $this->_packageInfo['lead']); + (isset($this->_packageInfo['developer'])) ? + $ret['developer'] = $this->_packageInfo['developer'] :null; + (isset($this->_packageInfo['contributor'])) ? + $ret['contributor'] = $this->_packageInfo['contributor'] :null; + (isset($this->_packageInfo['helper'])) ? + $ret['helper'] = $this->_packageInfo['helper'] :null; + return $ret; + } else { + $ret = array(); + $leads = isset($this->_packageInfo['lead'][0]) ? $this->_packageInfo['lead'] : + array($this->_packageInfo['lead']); + foreach ($leads as $lead) { + $s = $lead; + $s['handle'] = $s['user']; + unset($s['user']); + $s['role'] = 'lead'; + $ret[] = $s; + } + if (isset($this->_packageInfo['developer'])) { + $leads = isset($this->_packageInfo['developer'][0]) ? + $this->_packageInfo['developer'] : + array($this->_packageInfo['developer']); + foreach ($leads as $maintainer) { + $s = $maintainer; + $s['handle'] = $s['user']; + unset($s['user']); + $s['role'] = 'developer'; + $ret[] = $s; + } + } + if (isset($this->_packageInfo['contributor'])) { + $leads = isset($this->_packageInfo['contributor'][0]) ? + $this->_packageInfo['contributor'] : + array($this->_packageInfo['contributor']); + foreach ($leads as $maintainer) { + $s = $maintainer; + $s['handle'] = $s['user']; + unset($s['user']); + $s['role'] = 'contributor'; + $ret[] = $s; + } + } + if (isset($this->_packageInfo['helper'])) { + $leads = isset($this->_packageInfo['helper'][0]) ? + $this->_packageInfo['helper'] : + array($this->_packageInfo['helper']); + foreach ($leads as $maintainer) { + $s = $maintainer; + $s['handle'] = $s['user']; + unset($s['user']); + $s['role'] = 'helper'; + $ret[] = $s; + } + } + return $ret; + } + return false; + } + + function getLeads() + { + if (isset($this->_packageInfo['lead'])) { + return $this->_packageInfo['lead']; + } + return false; + } + + function getDevelopers() + { + if (isset($this->_packageInfo['developer'])) { + return $this->_packageInfo['developer']; + } + return false; + } + + function getContributors() + { + if (isset($this->_packageInfo['contributor'])) { + return $this->_packageInfo['contributor']; + } + return false; + } + + function getHelpers() + { + if (isset($this->_packageInfo['helper'])) { + return $this->_packageInfo['helper']; + } + return false; + } + + function setDate($date) + { + if (!isset($this->_packageInfo['date'])) { + // ensure that the extends tag is set up in the right location + $this->_packageInfo = $this->_insertBefore($this->_packageInfo, + array('time', 'version', + 'stability', 'license', 'notes', 'contents', 'compatible', + 'dependencies', 'providesextension', 'srcpackage', 'srcuri', + 'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', + 'zendextbinrelease', 'bundle', 'changelog'), array(), 'date'); + } + $this->_packageInfo['date'] = $date; + $this->_isValid = 0; + } + + function setTime($time) + { + $this->_isValid = 0; + if (!isset($this->_packageInfo['time'])) { + // ensure that the time tag is set up in the right location + $this->_packageInfo = $this->_insertBefore($this->_packageInfo, + array('version', + 'stability', 'license', 'notes', 'contents', 'compatible', + 'dependencies', 'providesextension', 'srcpackage', 'srcuri', + 'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', + 'zendextbinrelease', 'bundle', 'changelog'), $time, 'time'); + } + $this->_packageInfo['time'] = $time; + } + + function getDate() + { + if (isset($this->_packageInfo['date'])) { + return $this->_packageInfo['date']; + } + return false; + } + + function getTime() + { + if (isset($this->_packageInfo['time'])) { + return $this->_packageInfo['time']; + } + return false; + } + + /** + * @param package|api version category to return + */ + function getVersion($key = 'release') + { + if (isset($this->_packageInfo['version'][$key])) { + return $this->_packageInfo['version'][$key]; + } + return false; + } + + function getStability() + { + if (isset($this->_packageInfo['stability'])) { + return $this->_packageInfo['stability']; + } + return false; + } + + function getState($key = 'release') + { + if (isset($this->_packageInfo['stability'][$key])) { + return $this->_packageInfo['stability'][$key]; + } + return false; + } + + function getLicense($raw = false) + { + if (isset($this->_packageInfo['license'])) { + if ($raw) { + return $this->_packageInfo['license']; + } + if (is_array($this->_packageInfo['license'])) { + return $this->_packageInfo['license']['_content']; + } else { + return $this->_packageInfo['license']; + } + } + return false; + } + + function getLicenseLocation() + { + if (!isset($this->_packageInfo['license']) || !is_array($this->_packageInfo['license'])) { + return false; + } + return $this->_packageInfo['license']['attribs']; + } + + function getNotes() + { + if (isset($this->_packageInfo['notes'])) { + return $this->_packageInfo['notes']; + } + return false; + } + + /** + * Return the tag contents, if any + * @return array|false + */ + function getUsesrole() + { + if (isset($this->_packageInfo['usesrole'])) { + return $this->_packageInfo['usesrole']; + } + return false; + } + + /** + * Return the tag contents, if any + * @return array|false + */ + function getUsestask() + { + if (isset($this->_packageInfo['usestask'])) { + return $this->_packageInfo['usestask']; + } + return false; + } + + /** + * This should only be used to retrieve filenames and install attributes + */ + function getFilelist($preserve = false) + { + if (isset($this->_packageInfo['filelist']) && !$preserve) { + return $this->_packageInfo['filelist']; + } + $this->flattenFilelist(); + if ($contents = $this->getContents()) { + $ret = array(); + if (!isset($contents['dir'])) { + return false; + } + if (!isset($contents['dir']['file'][0])) { + $contents['dir']['file'] = array($contents['dir']['file']); + } + foreach ($contents['dir']['file'] as $file) { + $name = $file['attribs']['name']; + if (!$preserve) { + $file = $file['attribs']; + } + $ret[$name] = $file; + } + if (!$preserve) { + $this->_packageInfo['filelist'] = $ret; + } + return $ret; + } + return false; + } + + /** + * Return configure options array, if any + * + * @return array|false + */ + function getConfigureOptions() + { + if ($this->getPackageType() != 'extsrc' && $this->getPackageType() != 'zendextsrc') { + return false; + } + + $releases = $this->getReleases(); + if (isset($releases[0])) { + $releases = $releases[0]; + } + + if (isset($releases['configureoption'])) { + if (!isset($releases['configureoption'][0])) { + $releases['configureoption'] = array($releases['configureoption']); + } + + for ($i = 0; $i < count($releases['configureoption']); $i++) { + $releases['configureoption'][$i] = $releases['configureoption'][$i]['attribs']; + } + + return $releases['configureoption']; + } + + return false; + } + + /** + * This is only used at install-time, after all serialization + * is over. + */ + function resetFilelist() + { + $this->_packageInfo['filelist'] = array(); + } + + /** + * Retrieve a list of files that should be installed on this computer + * @return array + */ + function getInstallationFilelist($forfilecheck = false) + { + $contents = $this->getFilelist(true); + if (isset($contents['dir']['attribs']['baseinstalldir'])) { + $base = $contents['dir']['attribs']['baseinstalldir']; + } + if (isset($this->_packageInfo['bundle'])) { + return PEAR::raiseError( + 'Exception: bundles should be handled in download code only'); + } + $release = $this->getReleases(); + if ($release) { + if (!isset($release[0])) { + if (!isset($release['installconditions']) && !isset($release['filelist'])) { + if ($forfilecheck) { + return $this->getFilelist(); + } + return $contents; + } + $release = array($release); + } + $depchecker = &$this->getPEARDependency2($this->_config, array(), + array('channel' => $this->getChannel(), 'package' => $this->getPackage()), + PEAR_VALIDATE_INSTALLING); + foreach ($release as $instance) { + if (isset($instance['installconditions'])) { + $installconditions = $instance['installconditions']; + if (is_array($installconditions)) { + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + foreach ($installconditions as $type => $conditions) { + if (!isset($conditions[0])) { + $conditions = array($conditions); + } + foreach ($conditions as $condition) { + $ret = $depchecker->{"validate{$type}Dependency"}($condition); + if (PEAR::isError($ret)) { + PEAR::popErrorHandling(); + continue 3; // skip this release + } + } + } + PEAR::popErrorHandling(); + } + } + // this is the release to use + if (isset($instance['filelist'])) { + // ignore files + if (isset($instance['filelist']['ignore'])) { + $ignore = isset($instance['filelist']['ignore'][0]) ? + $instance['filelist']['ignore'] : + array($instance['filelist']['ignore']); + foreach ($ignore as $ig) { + unset ($contents[$ig['attribs']['name']]); + } + } + // install files as this name + if (isset($instance['filelist']['install'])) { + $installas = isset($instance['filelist']['install'][0]) ? + $instance['filelist']['install'] : + array($instance['filelist']['install']); + foreach ($installas as $as) { + $contents[$as['attribs']['name']]['attribs']['install-as'] = + $as['attribs']['as']; + } + } + } + if ($forfilecheck) { + foreach ($contents as $file => $attrs) { + $contents[$file] = $attrs['attribs']; + } + } + return $contents; + } + } else { // simple release - no installconditions or install-as + if ($forfilecheck) { + return $this->getFilelist(); + } + return $contents; + } + // no releases matched + return PEAR::raiseError('No releases in package.xml matched the existing operating ' . + 'system, extensions installed, or architecture, cannot install'); + } + + /** + * This is only used at install-time, after all serialization + * is over. + * @param string file name + * @param string installed path + */ + function setInstalledAs($file, $path) + { + if ($path) { + return $this->_packageInfo['filelist'][$file]['installed_as'] = $path; + } + unset($this->_packageInfo['filelist'][$file]['installed_as']); + } + + function getInstalledLocation($file) + { + if (isset($this->_packageInfo['filelist'][$file]['installed_as'])) { + return $this->_packageInfo['filelist'][$file]['installed_as']; + } + return false; + } + + /** + * This is only used at install-time, after all serialization + * is over. + */ + function installedFile($file, $atts) + { + if (isset($this->_packageInfo['filelist'][$file])) { + $this->_packageInfo['filelist'][$file] = + array_merge($this->_packageInfo['filelist'][$file], $atts['attribs']); + } else { + $this->_packageInfo['filelist'][$file] = $atts['attribs']; + } + } + + /** + * Retrieve the contents tag + */ + function getContents() + { + if (isset($this->_packageInfo['contents'])) { + return $this->_packageInfo['contents']; + } + return false; + } + + /** + * @param string full path to file + * @param string attribute name + * @param string attribute value + * @param int risky but fast - use this to choose a file based on its position in the list + * of files. Index is zero-based like PHP arrays. + * @return bool success of operation + */ + function setFileAttribute($filename, $attr, $value, $index = false) + { + $this->_isValid = 0; + if (in_array($attr, array('role', 'name', 'baseinstalldir'))) { + $this->_filesValid = false; + } + if ($index !== false && + isset($this->_packageInfo['contents']['dir']['file'][$index]['attribs'])) { + $this->_packageInfo['contents']['dir']['file'][$index]['attribs'][$attr] = $value; + return true; + } + if (!isset($this->_packageInfo['contents']['dir']['file'])) { + return false; + } + $files = $this->_packageInfo['contents']['dir']['file']; + if (!isset($files[0])) { + $files = array($files); + $ind = false; + } else { + $ind = true; + } + foreach ($files as $i => $file) { + if (isset($file['attribs'])) { + if ($file['attribs']['name'] == $filename) { + if ($ind) { + $this->_packageInfo['contents']['dir']['file'][$i]['attribs'][$attr] = $value; + } else { + $this->_packageInfo['contents']['dir']['file']['attribs'][$attr] = $value; + } + return true; + } + } + } + return false; + } + + function setDirtree($path) + { + if (!isset($this->_packageInfo['dirtree'])) { + $this->_packageInfo['dirtree'] = array(); + } + $this->_packageInfo['dirtree'][$path] = true; + } + + function getDirtree() + { + if (isset($this->_packageInfo['dirtree']) && count($this->_packageInfo['dirtree'])) { + return $this->_packageInfo['dirtree']; + } + return false; + } + + function resetDirtree() + { + unset($this->_packageInfo['dirtree']); + } + + /** + * Determines whether this package claims it is compatible with the version of + * the package that has a recommended version dependency + * @param PEAR_PackageFile_v2|PEAR_PackageFile_v1|PEAR_Downloader_Package + * @return boolean + */ + function isCompatible($pf) + { + if (!isset($this->_packageInfo['compatible'])) { + return false; + } + if (!isset($this->_packageInfo['channel'])) { + return false; + } + $me = $pf->getVersion(); + $compatible = $this->_packageInfo['compatible']; + if (!isset($compatible[0])) { + $compatible = array($compatible); + } + $found = false; + foreach ($compatible as $info) { + if (strtolower($info['name']) == strtolower($pf->getPackage())) { + if (strtolower($info['channel']) == strtolower($pf->getChannel())) { + $found = true; + break; + } + } + } + if (!$found) { + return false; + } + if (isset($info['exclude'])) { + if (!isset($info['exclude'][0])) { + $info['exclude'] = array($info['exclude']); + } + foreach ($info['exclude'] as $exclude) { + if (version_compare($me, $exclude, '==')) { + return false; + } + } + } + if (version_compare($me, $info['min'], '>=') && version_compare($me, $info['max'], '<=')) { + return true; + } + return false; + } + + /** + * @return array|false + */ + function getCompatible() + { + if (isset($this->_packageInfo['compatible'])) { + return $this->_packageInfo['compatible']; + } + return false; + } + + function getDependencies() + { + if (isset($this->_packageInfo['dependencies'])) { + return $this->_packageInfo['dependencies']; + } + return false; + } + + function isSubpackageOf($p) + { + return $p->isSubpackage($this); + } + + /** + * Determines whether the passed in package is a subpackage of this package. + * + * No version checking is done, only name verification. + * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 + * @return bool + */ + function isSubpackage($p) + { + $sub = array(); + if (isset($this->_packageInfo['dependencies']['required']['subpackage'])) { + $sub = $this->_packageInfo['dependencies']['required']['subpackage']; + if (!isset($sub[0])) { + $sub = array($sub); + } + } + if (isset($this->_packageInfo['dependencies']['optional']['subpackage'])) { + $sub1 = $this->_packageInfo['dependencies']['optional']['subpackage']; + if (!isset($sub1[0])) { + $sub1 = array($sub1); + } + $sub = array_merge($sub, $sub1); + } + if (isset($this->_packageInfo['dependencies']['group'])) { + $group = $this->_packageInfo['dependencies']['group']; + if (!isset($group[0])) { + $group = array($group); + } + foreach ($group as $deps) { + if (isset($deps['subpackage'])) { + $sub2 = $deps['subpackage']; + if (!isset($sub2[0])) { + $sub2 = array($sub2); + } + $sub = array_merge($sub, $sub2); + } + } + } + foreach ($sub as $dep) { + if (strtolower($dep['name']) == strtolower($p->getPackage())) { + if (isset($dep['channel'])) { + if (strtolower($dep['channel']) == strtolower($p->getChannel())) { + return true; + } + } else { + if ($dep['uri'] == $p->getURI()) { + return true; + } + } + } + } + return false; + } + + function dependsOn($package, $channel) + { + if (!($deps = $this->getDependencies())) { + return false; + } + foreach (array('package', 'subpackage') as $type) { + foreach (array('required', 'optional') as $needed) { + if (isset($deps[$needed][$type])) { + if (!isset($deps[$needed][$type][0])) { + $deps[$needed][$type] = array($deps[$needed][$type]); + } + foreach ($deps[$needed][$type] as $dep) { + $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri'; + if (strtolower($dep['name']) == strtolower($package) && + $depchannel == $channel) { + return true; + } + } + } + } + if (isset($deps['group'])) { + if (!isset($deps['group'][0])) { + $dep['group'] = array($deps['group']); + } + foreach ($deps['group'] as $group) { + if (isset($group[$type])) { + if (!is_array($group[$type])) { + $group[$type] = array($group[$type]); + } + foreach ($group[$type] as $dep) { + $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri'; + if (strtolower($dep['name']) == strtolower($package) && + $depchannel == $channel) { + return true; + } + } + } + } + } + } + return false; + } + + /** + * Get the contents of a dependency group + * @param string + * @return array|false + */ + function getDependencyGroup($name) + { + $name = strtolower($name); + if (!isset($this->_packageInfo['dependencies']['group'])) { + return false; + } + $groups = $this->_packageInfo['dependencies']['group']; + if (!isset($groups[0])) { + $groups = array($groups); + } + foreach ($groups as $group) { + if (strtolower($group['attribs']['name']) == $name) { + return $group; + } + } + return false; + } + + /** + * Retrieve a partial package.xml 1.0 representation of dependencies + * + * a very limited representation of dependencies is returned by this method. + * The tag for excluding certain versions of a dependency is + * completely ignored. In addition, dependency groups are ignored, with the + * assumption that all dependencies in dependency groups are also listed in + * the optional group that work with all dependency groups + * @param boolean return package.xml 2.0 tag + * @return array|false + */ + function getDeps($raw = false, $nopearinstaller = false) + { + if (isset($this->_packageInfo['dependencies'])) { + if ($raw) { + return $this->_packageInfo['dependencies']; + } + $ret = array(); + $map = array( + 'php' => 'php', + 'package' => 'pkg', + 'subpackage' => 'pkg', + 'extension' => 'ext', + 'os' => 'os', + 'pearinstaller' => 'pkg', + ); + foreach (array('required', 'optional') as $type) { + $optional = ($type == 'optional') ? 'yes' : 'no'; + if (!isset($this->_packageInfo['dependencies'][$type]) + || empty($this->_packageInfo['dependencies'][$type])) { + continue; + } + foreach ($this->_packageInfo['dependencies'][$type] as $dtype => $deps) { + if ($dtype == 'pearinstaller' && $nopearinstaller) { + continue; + } + if (!isset($deps[0])) { + $deps = array($deps); + } + foreach ($deps as $dep) { + if (!isset($map[$dtype])) { + // no support for arch type + continue; + } + if ($dtype == 'pearinstaller') { + $dep['name'] = 'PEAR'; + $dep['channel'] = 'pear.php.net'; + } + $s = array('type' => $map[$dtype]); + if (isset($dep['channel'])) { + $s['channel'] = $dep['channel']; + } + if (isset($dep['uri'])) { + $s['uri'] = $dep['uri']; + } + if (isset($dep['name'])) { + $s['name'] = $dep['name']; + } + if (isset($dep['conflicts'])) { + $s['rel'] = 'not'; + } else { + if (!isset($dep['min']) && + !isset($dep['max'])) { + $s['rel'] = 'has'; + $s['optional'] = $optional; + } elseif (isset($dep['min']) && + isset($dep['max'])) { + $s['rel'] = 'ge'; + $s1 = $s; + $s1['rel'] = 'le'; + $s['version'] = $dep['min']; + $s1['version'] = $dep['max']; + if (isset($dep['channel'])) { + $s1['channel'] = $dep['channel']; + } + if ($dtype != 'php') { + $s['name'] = $dep['name']; + $s1['name'] = $dep['name']; + } + $s['optional'] = $optional; + $s1['optional'] = $optional; + $ret[] = $s1; + } elseif (isset($dep['min'])) { + if (isset($dep['exclude']) && + $dep['exclude'] == $dep['min']) { + $s['rel'] = 'gt'; + } else { + $s['rel'] = 'ge'; + } + $s['version'] = $dep['min']; + $s['optional'] = $optional; + if ($dtype != 'php') { + $s['name'] = $dep['name']; + } + } elseif (isset($dep['max'])) { + if (isset($dep['exclude']) && + $dep['exclude'] == $dep['max']) { + $s['rel'] = 'lt'; + } else { + $s['rel'] = 'le'; + } + $s['version'] = $dep['max']; + $s['optional'] = $optional; + if ($dtype != 'php') { + $s['name'] = $dep['name']; + } + } + } + $ret[] = $s; + } + } + } + if (count($ret)) { + return $ret; + } + } + return false; + } + + /** + * @return php|extsrc|extbin|zendextsrc|zendextbin|bundle|false + */ + function getPackageType() + { + if (isset($this->_packageInfo['phprelease'])) { + return 'php'; + } + if (isset($this->_packageInfo['extsrcrelease'])) { + return 'extsrc'; + } + if (isset($this->_packageInfo['extbinrelease'])) { + return 'extbin'; + } + if (isset($this->_packageInfo['zendextsrcrelease'])) { + return 'zendextsrc'; + } + if (isset($this->_packageInfo['zendextbinrelease'])) { + return 'zendextbin'; + } + if (isset($this->_packageInfo['bundle'])) { + return 'bundle'; + } + return false; + } + + /** + * @return array|false + */ + function getReleases() + { + $type = $this->getPackageType(); + if ($type != 'bundle') { + $type .= 'release'; + } + if ($this->getPackageType() && isset($this->_packageInfo[$type])) { + return $this->_packageInfo[$type]; + } + return false; + } + + /** + * @return array + */ + function getChangelog() + { + if (isset($this->_packageInfo['changelog'])) { + return $this->_packageInfo['changelog']; + } + return false; + } + + function hasDeps() + { + return isset($this->_packageInfo['dependencies']); + } + + function getPackagexmlVersion() + { + if (isset($this->_packageInfo['zendextsrcrelease'])) { + return '2.1'; + } + if (isset($this->_packageInfo['zendextbinrelease'])) { + return '2.1'; + } + return '2.0'; + } + + /** + * @return array|false + */ + function getSourcePackage() + { + if (isset($this->_packageInfo['extbinrelease']) || + isset($this->_packageInfo['zendextbinrelease'])) { + return array('channel' => $this->_packageInfo['srcchannel'], + 'package' => $this->_packageInfo['srcpackage']); + } + return false; + } + + function getBundledPackages() + { + if (isset($this->_packageInfo['bundle'])) { + return $this->_packageInfo['contents']['bundledpackage']; + } + return false; + } + + function getLastModified() + { + if (isset($this->_packageInfo['_lastmodified'])) { + return $this->_packageInfo['_lastmodified']; + } + return false; + } + + /** + * Get the contents of a file listed within the package.xml + * @param string + * @return string + */ + function getFileContents($file) + { + if ($this->_archiveFile == $this->_packageFile) { // unpacked + $dir = dirname($this->_packageFile); + $file = $dir . DIRECTORY_SEPARATOR . $file; + $file = str_replace(array('/', '\\'), + array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR), $file); + if (file_exists($file) && is_readable($file)) { + return implode('', file($file)); + } + } else { // tgz + $tar = &new Archive_Tar($this->_archiveFile); + $tar->pushErrorHandling(PEAR_ERROR_RETURN); + if ($file != 'package.xml' && $file != 'package2.xml') { + $file = $this->getPackage() . '-' . $this->getVersion() . '/' . $file; + } + $file = $tar->extractInString($file); + $tar->popErrorHandling(); + if (PEAR::isError($file)) { + return PEAR::raiseError("Cannot locate file '$file' in archive"); + } + return $file; + } + } + + function &getRW() + { + if (!class_exists('PEAR_PackageFile_v2_rw')) { + require_once 'PEAR/PackageFile/v2/rw.php'; + } + $a = new PEAR_PackageFile_v2_rw; + foreach (get_object_vars($this) as $name => $unused) { + if (!isset($this->$name)) { + continue; + } + if ($name == '_config' || $name == '_logger'|| $name == '_registry' || + $name == '_stack') { + $a->$name = &$this->$name; + } else { + $a->$name = $this->$name; + } + } + return $a; + } + + function &getDefaultGenerator() + { + if (!class_exists('PEAR_PackageFile_Generator_v2')) { + require_once 'PEAR/PackageFile/Generator/v2.php'; + } + $a = &new PEAR_PackageFile_Generator_v2($this); + return $a; + } + + function analyzeSourceCode($file, $string = false) + { + if (!isset($this->_v2Validator) || + !is_a($this->_v2Validator, 'PEAR_PackageFile_v2_Validator')) { + if (!class_exists('PEAR_PackageFile_v2_Validator')) { + require_once 'PEAR/PackageFile/v2/Validator.php'; + } + $this->_v2Validator = new PEAR_PackageFile_v2_Validator; + } + return $this->_v2Validator->analyzeSourceCode($file, $string); + } + + function validate($state = PEAR_VALIDATE_NORMAL) + { + if (!isset($this->_packageInfo) || !is_array($this->_packageInfo)) { + return false; + } + if (!isset($this->_v2Validator) || + !is_a($this->_v2Validator, 'PEAR_PackageFile_v2_Validator')) { + if (!class_exists('PEAR_PackageFile_v2_Validator')) { + require_once 'PEAR/PackageFile/v2/Validator.php'; + } + $this->_v2Validator = new PEAR_PackageFile_v2_Validator; + } + if (isset($this->_packageInfo['xsdversion'])) { + unset($this->_packageInfo['xsdversion']); + } + return $this->_v2Validator->validate($this, $state); + } + + function getTasksNs() + { + if (!isset($this->_tasksNs)) { + if (isset($this->_packageInfo['attribs'])) { + foreach ($this->_packageInfo['attribs'] as $name => $value) { + if ($value == 'http://pear.php.net/dtd/tasks-1.0') { + $this->_tasksNs = str_replace('xmlns:', '', $name); + break; + } + } + } + } + return $this->_tasksNs; + } + + /** + * Determine whether a task name is a valid task. Custom tasks may be defined + * using subdirectories by putting a "-" in the name, as in + * + * Note that this method will auto-load the task class file and test for the existence + * of the name with "-" replaced by "_" as in PEAR/Task/mycustom/task.php makes class + * PEAR_Task_mycustom_task + * @param string + * @return boolean + */ + function getTask($task) + { + $this->getTasksNs(); + // transform all '-' to '/' and 'tasks:' to '' so tasks:replace becomes replace + $task = str_replace(array($this->_tasksNs . ':', '-'), array('', ' '), $task); + $taskfile = str_replace(' ', '/', ucwords($task)); + $task = str_replace(array(' ', '/'), '_', ucwords($task)); + if (class_exists("PEAR_Task_$task")) { + return "PEAR_Task_$task"; + } + $fp = @fopen("PEAR/Task/$taskfile.php", 'r', true); + if ($fp) { + fclose($fp); + require_once "PEAR/Task/$taskfile.php"; + return "PEAR_Task_$task"; + } + return false; + } + + /** + * Key-friendly array_splice + * @param tagname to splice a value in before + * @param mixed the value to splice in + * @param string the new tag name + */ + function _ksplice($array, $key, $value, $newkey) + { + $offset = array_search($key, array_keys($array)); + $after = array_slice($array, $offset); + $before = array_slice($array, 0, $offset); + $before[$newkey] = $value; + return array_merge($before, $after); + } + + /** + * @param array a list of possible keys, in the order they may occur + * @param mixed contents of the new package.xml tag + * @param string tag name + * @access private + */ + function _insertBefore($array, $keys, $contents, $newkey) + { + foreach ($keys as $key) { + if (isset($array[$key])) { + return $array = $this->_ksplice($array, $key, $contents, $newkey); + } + } + $array[$newkey] = $contents; + return $array; + } + + /** + * @param subsection of {@link $_packageInfo} + * @param array|string tag contents + * @param array format: + *
    +     * array(
    +     *   tagname => array(list of tag names that follow this one),
    +     *   childtagname => array(list of child tag names that follow this one),
    +     * )
    +     * 
    + * + * This allows construction of nested tags + * @access private + */ + function _mergeTag($manip, $contents, $order) + { + if (count($order)) { + foreach ($order as $tag => $curorder) { + if (!isset($manip[$tag])) { + // ensure that the tag is set up + $manip = $this->_insertBefore($manip, $curorder, array(), $tag); + } + if (count($order) > 1) { + $manip[$tag] = $this->_mergeTag($manip[$tag], $contents, array_slice($order, 1)); + return $manip; + } + } + } else { + return $manip; + } + if (is_array($manip[$tag]) && !empty($manip[$tag]) && isset($manip[$tag][0])) { + $manip[$tag][] = $contents; + } else { + if (!count($manip[$tag])) { + $manip[$tag] = $contents; + } else { + $manip[$tag] = array($manip[$tag]); + $manip[$tag][] = $contents; + } + } + return $manip; + } +} +?> diff --git a/includes/pear/PEAR/PackageFile/v2/Validator.php b/includes/pear/PEAR/PackageFile/v2/Validator.php new file mode 100644 index 0000000..7ce2fb3 --- /dev/null +++ b/includes/pear/PEAR/PackageFile/v2/Validator.php @@ -0,0 +1,2154 @@ + + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a8 + */ +/** + * Private validation class used by PEAR_PackageFile_v2 - do not use directly, its + * sole purpose is to split up the PEAR/PackageFile/v2.php file to make it smaller + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a8 + * @access private + */ +class PEAR_PackageFile_v2_Validator +{ + /** + * @var array + */ + var $_packageInfo; + /** + * @var PEAR_PackageFile_v2 + */ + var $_pf; + /** + * @var PEAR_ErrorStack + */ + var $_stack; + /** + * @var int + */ + var $_isValid = 0; + /** + * @var int + */ + var $_filesValid = 0; + /** + * @var int + */ + var $_curState = 0; + /** + * @param PEAR_PackageFile_v2 + * @param int + */ + function validate(&$pf, $state = PEAR_VALIDATE_NORMAL) + { + $this->_pf = &$pf; + $this->_curState = $state; + $this->_packageInfo = $this->_pf->getArray(); + $this->_isValid = $this->_pf->_isValid; + $this->_filesValid = $this->_pf->_filesValid; + $this->_stack = &$pf->_stack; + $this->_stack->getErrors(true); + if (($this->_isValid & $state) == $state) { + return true; + } + if (!isset($this->_packageInfo) || !is_array($this->_packageInfo)) { + return false; + } + if (!isset($this->_packageInfo['attribs']['version']) || + ($this->_packageInfo['attribs']['version'] != '2.0' && + $this->_packageInfo['attribs']['version'] != '2.1') + ) { + $this->_noPackageVersion(); + } + $structure = + array( + 'name', + 'channel|uri', + '*extends', // can't be multiple, but this works fine + 'summary', + 'description', + '+lead', // these all need content checks + '*developer', + '*contributor', + '*helper', + 'date', + '*time', + 'version', + 'stability', + 'license->?uri->?filesource', + 'notes', + 'contents', //special validation needed + '*compatible', + 'dependencies', //special validation needed + '*usesrole', + '*usestask', // reserve these for 1.4.0a1 to implement + // this will allow a package.xml to gracefully say it + // needs a certain package installed in order to implement a role or task + '*providesextension', + '*srcpackage|*srcuri', + '+phprelease|+extsrcrelease|+extbinrelease|' . + '+zendextsrcrelease|+zendextbinrelease|bundle', //special validation needed + '*changelog', + ); + $test = $this->_packageInfo; + if (isset($test['dependencies']) && + isset($test['dependencies']['required']) && + isset($test['dependencies']['required']['pearinstaller']) && + isset($test['dependencies']['required']['pearinstaller']['min']) && + version_compare('1.9.4', + $test['dependencies']['required']['pearinstaller']['min'], '<') + ) { + $this->_pearVersionTooLow($test['dependencies']['required']['pearinstaller']['min']); + return false; + } + // ignore post-installation array fields + if (array_key_exists('filelist', $test)) { + unset($test['filelist']); + } + if (array_key_exists('_lastmodified', $test)) { + unset($test['_lastmodified']); + } + if (array_key_exists('#binarypackage', $test)) { + unset($test['#binarypackage']); + } + if (array_key_exists('old', $test)) { + unset($test['old']); + } + if (array_key_exists('_lastversion', $test)) { + unset($test['_lastversion']); + } + if (!$this->_stupidSchemaValidate($structure, $test, '')) { + return false; + } + if (empty($this->_packageInfo['name'])) { + $this->_tagCannotBeEmpty('name'); + } + $test = isset($this->_packageInfo['uri']) ? 'uri' :'channel'; + if (empty($this->_packageInfo[$test])) { + $this->_tagCannotBeEmpty($test); + } + if (is_array($this->_packageInfo['license']) && + (!isset($this->_packageInfo['license']['_content']) || + empty($this->_packageInfo['license']['_content']))) { + $this->_tagCannotBeEmpty('license'); + } elseif (empty($this->_packageInfo['license'])) { + $this->_tagCannotBeEmpty('license'); + } + if (empty($this->_packageInfo['summary'])) { + $this->_tagCannotBeEmpty('summary'); + } + if (empty($this->_packageInfo['description'])) { + $this->_tagCannotBeEmpty('description'); + } + if (empty($this->_packageInfo['date'])) { + $this->_tagCannotBeEmpty('date'); + } + if (empty($this->_packageInfo['notes'])) { + $this->_tagCannotBeEmpty('notes'); + } + if (isset($this->_packageInfo['time']) && empty($this->_packageInfo['time'])) { + $this->_tagCannotBeEmpty('time'); + } + if (isset($this->_packageInfo['dependencies'])) { + $this->_validateDependencies(); + } + if (isset($this->_packageInfo['compatible'])) { + $this->_validateCompatible(); + } + if (!isset($this->_packageInfo['bundle'])) { + if (empty($this->_packageInfo['contents'])) { + $this->_tagCannotBeEmpty('contents'); + } + if (!isset($this->_packageInfo['contents']['dir'])) { + $this->_filelistMustContainDir('contents'); + return false; + } + if (isset($this->_packageInfo['contents']['file'])) { + $this->_filelistCannotContainFile('contents'); + return false; + } + } + $this->_validateMaintainers(); + $this->_validateStabilityVersion(); + $fail = false; + if (array_key_exists('usesrole', $this->_packageInfo)) { + $roles = $this->_packageInfo['usesrole']; + if (!is_array($roles) || !isset($roles[0])) { + $roles = array($roles); + } + foreach ($roles as $role) { + if (!isset($role['role'])) { + $this->_usesroletaskMustHaveRoleTask('usesrole', 'role'); + $fail = true; + } else { + if (!isset($role['channel'])) { + if (!isset($role['uri'])) { + $this->_usesroletaskMustHaveChannelOrUri($role['role'], 'usesrole'); + $fail = true; + } + } elseif (!isset($role['package'])) { + $this->_usesroletaskMustHavePackage($role['role'], 'usesrole'); + $fail = true; + } + } + } + } + if (array_key_exists('usestask', $this->_packageInfo)) { + $roles = $this->_packageInfo['usestask']; + if (!is_array($roles) || !isset($roles[0])) { + $roles = array($roles); + } + foreach ($roles as $role) { + if (!isset($role['task'])) { + $this->_usesroletaskMustHaveRoleTask('usestask', 'task'); + $fail = true; + } else { + if (!isset($role['channel'])) { + if (!isset($role['uri'])) { + $this->_usesroletaskMustHaveChannelOrUri($role['task'], 'usestask'); + $fail = true; + } + } elseif (!isset($role['package'])) { + $this->_usesroletaskMustHavePackage($role['task'], 'usestask'); + $fail = true; + } + } + } + } + + if ($fail) { + return false; + } + + $list = $this->_packageInfo['contents']; + if (isset($list['dir']) && is_array($list['dir']) && isset($list['dir'][0])) { + $this->_multipleToplevelDirNotAllowed(); + return $this->_isValid = 0; + } + + $this->_validateFilelist(); + $this->_validateRelease(); + if (!$this->_stack->hasErrors()) { + $chan = $this->_pf->_registry->getChannel($this->_pf->getChannel(), true); + if (PEAR::isError($chan)) { + $this->_unknownChannel($this->_pf->getChannel()); + } else { + $valpack = $chan->getValidationPackage(); + // for channel validator packages, always use the default PEAR validator. + // otherwise, they can't be installed or packaged + $validator = $chan->getValidationObject($this->_pf->getPackage()); + if (!$validator) { + $this->_stack->push(__FUNCTION__, 'error', + array('channel' => $chan->getName(), + 'package' => $this->_pf->getPackage(), + 'name' => $valpack['_content'], + 'version' => $valpack['attribs']['version']), + 'package "%channel%/%package%" cannot be properly validated without ' . + 'validation package "%channel%/%name%-%version%"'); + return $this->_isValid = 0; + } + $validator->setPackageFile($this->_pf); + $validator->validate($state); + $failures = $validator->getFailures(); + foreach ($failures['errors'] as $error) { + $this->_stack->push(__FUNCTION__, 'error', $error, + 'Channel validator error: field "%field%" - %reason%'); + } + foreach ($failures['warnings'] as $warning) { + $this->_stack->push(__FUNCTION__, 'warning', $warning, + 'Channel validator warning: field "%field%" - %reason%'); + } + } + } + + $this->_pf->_isValid = $this->_isValid = !$this->_stack->hasErrors('error'); + if ($this->_isValid && $state == PEAR_VALIDATE_PACKAGING && !$this->_filesValid) { + if ($this->_pf->getPackageType() == 'bundle') { + if ($this->_analyzeBundledPackages()) { + $this->_filesValid = $this->_pf->_filesValid = true; + } else { + $this->_pf->_isValid = $this->_isValid = 0; + } + } else { + if (!$this->_analyzePhpFiles()) { + $this->_pf->_isValid = $this->_isValid = 0; + } else { + $this->_filesValid = $this->_pf->_filesValid = true; + } + } + } + + if ($this->_isValid) { + return $this->_pf->_isValid = $this->_isValid = $state; + } + + return $this->_pf->_isValid = $this->_isValid = 0; + } + + function _stupidSchemaValidate($structure, $xml, $root) + { + if (!is_array($xml)) { + $xml = array(); + } + $keys = array_keys($xml); + reset($keys); + $key = current($keys); + while ($key == 'attribs' || $key == '_contents') { + $key = next($keys); + } + $unfoundtags = $optionaltags = array(); + $ret = true; + $mismatch = false; + foreach ($structure as $struc) { + if ($key) { + $tag = $xml[$key]; + } + $test = $this->_processStructure($struc); + if (isset($test['choices'])) { + $loose = true; + foreach ($test['choices'] as $choice) { + if ($key == $choice['tag']) { + $key = next($keys); + while ($key == 'attribs' || $key == '_contents') { + $key = next($keys); + } + $unfoundtags = $optionaltags = array(); + $mismatch = false; + if ($key && $key != $choice['tag'] && isset($choice['multiple'])) { + $unfoundtags[] = $choice['tag']; + $optionaltags[] = $choice['tag']; + if ($key) { + $mismatch = true; + } + } + $ret &= $this->_processAttribs($choice, $tag, $root); + continue 2; + } else { + $unfoundtags[] = $choice['tag']; + $mismatch = true; + } + if (!isset($choice['multiple']) || $choice['multiple'] != '*') { + $loose = false; + } else { + $optionaltags[] = $choice['tag']; + } + } + if (!$loose) { + $this->_invalidTagOrder($unfoundtags, $key, $root); + return false; + } + } else { + if ($key != $test['tag']) { + if (isset($test['multiple']) && $test['multiple'] != '*') { + $unfoundtags[] = $test['tag']; + $this->_invalidTagOrder($unfoundtags, $key, $root); + return false; + } else { + if ($key) { + $mismatch = true; + } + $unfoundtags[] = $test['tag']; + $optionaltags[] = $test['tag']; + } + if (!isset($test['multiple'])) { + $this->_invalidTagOrder($unfoundtags, $key, $root); + return false; + } + continue; + } else { + $unfoundtags = $optionaltags = array(); + $mismatch = false; + } + $key = next($keys); + while ($key == 'attribs' || $key == '_contents') { + $key = next($keys); + } + if ($key && $key != $test['tag'] && isset($test['multiple'])) { + $unfoundtags[] = $test['tag']; + $optionaltags[] = $test['tag']; + $mismatch = true; + } + $ret &= $this->_processAttribs($test, $tag, $root); + continue; + } + } + if (!$mismatch && count($optionaltags)) { + // don't error out on any optional tags + $unfoundtags = array_diff($unfoundtags, $optionaltags); + } + if (count($unfoundtags)) { + $this->_invalidTagOrder($unfoundtags, $key, $root); + } elseif ($key) { + // unknown tags + $this->_invalidTagOrder('*no tags allowed here*', $key, $root); + while ($key = next($keys)) { + $this->_invalidTagOrder('*no tags allowed here*', $key, $root); + } + } + return $ret; + } + + function _processAttribs($choice, $tag, $context) + { + if (isset($choice['attribs'])) { + if (!is_array($tag)) { + $tag = array($tag); + } + $tags = $tag; + if (!isset($tags[0])) { + $tags = array($tags); + } + $ret = true; + foreach ($tags as $i => $tag) { + if (!is_array($tag) || !isset($tag['attribs'])) { + foreach ($choice['attribs'] as $attrib) { + if ($attrib{0} != '?') { + $ret &= $this->_tagHasNoAttribs($choice['tag'], + $context); + continue 2; + } + } + } + foreach ($choice['attribs'] as $attrib) { + if ($attrib{0} != '?') { + if (!isset($tag['attribs'][$attrib])) { + $ret &= $this->_tagMissingAttribute($choice['tag'], + $attrib, $context); + } + } + } + } + return $ret; + } + return true; + } + + function _processStructure($key) + { + $ret = array(); + if (count($pieces = explode('|', $key)) > 1) { + $ret['choices'] = array(); + foreach ($pieces as $piece) { + $ret['choices'][] = $this->_processStructure($piece); + } + return $ret; + } + $multi = $key{0}; + if ($multi == '+' || $multi == '*') { + $ret['multiple'] = $key{0}; + $key = substr($key, 1); + } + if (count($attrs = explode('->', $key)) > 1) { + $ret['tag'] = array_shift($attrs); + $ret['attribs'] = $attrs; + } else { + $ret['tag'] = $key; + } + return $ret; + } + + function _validateStabilityVersion() + { + $structure = array('release', 'api'); + $a = $this->_stupidSchemaValidate($structure, $this->_packageInfo['version'], ''); + $a &= $this->_stupidSchemaValidate($structure, $this->_packageInfo['stability'], ''); + if ($a) { + if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/', + $this->_packageInfo['version']['release'])) { + $this->_invalidVersion('release', $this->_packageInfo['version']['release']); + } + if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/', + $this->_packageInfo['version']['api'])) { + $this->_invalidVersion('api', $this->_packageInfo['version']['api']); + } + if (!in_array($this->_packageInfo['stability']['release'], + array('snapshot', 'devel', 'alpha', 'beta', 'stable'))) { + $this->_invalidState('release', $this->_packageInfo['stability']['release']); + } + if (!in_array($this->_packageInfo['stability']['api'], + array('devel', 'alpha', 'beta', 'stable'))) { + $this->_invalidState('api', $this->_packageInfo['stability']['api']); + } + } + } + + function _validateMaintainers() + { + $structure = + array( + 'name', + 'user', + 'email', + 'active', + ); + foreach (array('lead', 'developer', 'contributor', 'helper') as $type) { + if (!isset($this->_packageInfo[$type])) { + continue; + } + if (isset($this->_packageInfo[$type][0])) { + foreach ($this->_packageInfo[$type] as $lead) { + $this->_stupidSchemaValidate($structure, $lead, '<' . $type . '>'); + } + } else { + $this->_stupidSchemaValidate($structure, $this->_packageInfo[$type], + '<' . $type . '>'); + } + } + } + + function _validatePhpDep($dep, $installcondition = false) + { + $structure = array( + 'min', + '*max', + '*exclude', + ); + $type = $installcondition ? '' : ''; + $this->_stupidSchemaValidate($structure, $dep, $type); + if (isset($dep['min'])) { + if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/', + $dep['min'])) { + $this->_invalidVersion($type . '', $dep['min']); + } + } + if (isset($dep['max'])) { + if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/', + $dep['max'])) { + $this->_invalidVersion($type . '', $dep['max']); + } + } + if (isset($dep['exclude'])) { + if (!is_array($dep['exclude'])) { + $dep['exclude'] = array($dep['exclude']); + } + foreach ($dep['exclude'] as $exclude) { + if (!preg_match( + '/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/', + $exclude)) { + $this->_invalidVersion($type . '', $exclude); + } + } + } + } + + function _validatePearinstallerDep($dep) + { + $structure = array( + 'min', + '*max', + '*recommended', + '*exclude', + ); + $this->_stupidSchemaValidate($structure, $dep, ''); + if (isset($dep['min'])) { + if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/', + $dep['min'])) { + $this->_invalidVersion('', + $dep['min']); + } + } + if (isset($dep['max'])) { + if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/', + $dep['max'])) { + $this->_invalidVersion('', + $dep['max']); + } + } + if (isset($dep['recommended'])) { + if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/', + $dep['recommended'])) { + $this->_invalidVersion('', + $dep['recommended']); + } + } + if (isset($dep['exclude'])) { + if (!is_array($dep['exclude'])) { + $dep['exclude'] = array($dep['exclude']); + } + foreach ($dep['exclude'] as $exclude) { + if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/', + $exclude)) { + $this->_invalidVersion('', + $exclude); + } + } + } + } + + function _validatePackageDep($dep, $group, $type = '') + { + if (isset($dep['uri'])) { + if (isset($dep['conflicts'])) { + $structure = array( + 'name', + 'uri', + 'conflicts', + '*providesextension', + ); + } else { + $structure = array( + 'name', + 'uri', + '*providesextension', + ); + } + } else { + if (isset($dep['conflicts'])) { + $structure = array( + 'name', + 'channel', + '*min', + '*max', + '*exclude', + 'conflicts', + '*providesextension', + ); + } else { + $structure = array( + 'name', + 'channel', + '*min', + '*max', + '*recommended', + '*exclude', + '*nodefault', + '*providesextension', + ); + } + } + if (isset($dep['name'])) { + $type .= '' . $dep['name'] . ''; + } + $this->_stupidSchemaValidate($structure, $dep, '' . $group . $type); + if (isset($dep['uri']) && (isset($dep['min']) || isset($dep['max']) || + isset($dep['recommended']) || isset($dep['exclude']))) { + $this->_uriDepsCannotHaveVersioning('' . $group . $type); + } + if (isset($dep['channel']) && strtolower($dep['channel']) == '__uri') { + $this->_DepchannelCannotBeUri('' . $group . $type); + } + if (isset($dep['min'])) { + if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/', + $dep['min'])) { + $this->_invalidVersion('' . $group . $type . '', $dep['min']); + } + } + if (isset($dep['max'])) { + if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/', + $dep['max'])) { + $this->_invalidVersion('' . $group . $type . '', $dep['max']); + } + } + if (isset($dep['recommended'])) { + if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/', + $dep['recommended'])) { + $this->_invalidVersion('' . $group . $type . '', + $dep['recommended']); + } + } + if (isset($dep['exclude'])) { + if (!is_array($dep['exclude'])) { + $dep['exclude'] = array($dep['exclude']); + } + foreach ($dep['exclude'] as $exclude) { + if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/', + $exclude)) { + $this->_invalidVersion('' . $group . $type . '', + $exclude); + } + } + } + } + + function _validateSubpackageDep($dep, $group) + { + $this->_validatePackageDep($dep, $group, ''); + if (isset($dep['providesextension'])) { + $this->_subpackageCannotProvideExtension(isset($dep['name']) ? $dep['name'] : ''); + } + if (isset($dep['conflicts'])) { + $this->_subpackagesCannotConflict(isset($dep['name']) ? $dep['name'] : ''); + } + } + + function _validateExtensionDep($dep, $group = false, $installcondition = false) + { + if (isset($dep['conflicts'])) { + $structure = array( + 'name', + '*min', + '*max', + '*exclude', + 'conflicts', + ); + } else { + $structure = array( + 'name', + '*min', + '*max', + '*recommended', + '*exclude', + ); + } + if ($installcondition) { + $type = ''; + } else { + $type = '' . $group . ''; + } + if (isset($dep['name'])) { + $type .= '' . $dep['name'] . ''; + } + $this->_stupidSchemaValidate($structure, $dep, $type); + if (isset($dep['min'])) { + if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/', + $dep['min'])) { + $this->_invalidVersion(substr($type, 1) . '_invalidVersion(substr($type, 1) . '_invalidVersion(substr($type, 1) . '_invalidVersion(substr($type, 1) . '' : ''; + if ($this->_stupidSchemaValidate($structure, $dep, $type)) { + if ($dep['name'] == '*') { + if (array_key_exists('conflicts', $dep)) { + $this->_cannotConflictWithAllOs($type); + } + } + } + } + + function _validateArchDep($dep, $installcondition = false) + { + $structure = array( + 'pattern', + '*conflicts', + ); + $type = $installcondition ? '' : ''; + $this->_stupidSchemaValidate($structure, $dep, $type); + } + + function _validateInstallConditions($cond, $release) + { + $structure = array( + '*php', + '*extension', + '*os', + '*arch', + ); + if (!$this->_stupidSchemaValidate($structure, + $cond, $release)) { + return false; + } + foreach (array('php', 'extension', 'os', 'arch') as $type) { + if (isset($cond[$type])) { + $iter = $cond[$type]; + if (!is_array($iter) || !isset($iter[0])) { + $iter = array($iter); + } + foreach ($iter as $package) { + if ($type == 'extension') { + $this->{"_validate{$type}Dep"}($package, false, true); + } else { + $this->{"_validate{$type}Dep"}($package, true); + } + } + } + } + } + + function _validateDependencies() + { + $structure = array( + 'required', + '*optional', + '*group->name->hint' + ); + if (!$this->_stupidSchemaValidate($structure, + $this->_packageInfo['dependencies'], '')) { + return false; + } + foreach (array('required', 'optional') as $simpledep) { + if (isset($this->_packageInfo['dependencies'][$simpledep])) { + if ($simpledep == 'optional') { + $structure = array( + '*package', + '*subpackage', + '*extension', + ); + } else { + $structure = array( + 'php', + 'pearinstaller', + '*package', + '*subpackage', + '*extension', + '*os', + '*arch', + ); + } + if ($this->_stupidSchemaValidate($structure, + $this->_packageInfo['dependencies'][$simpledep], + "<$simpledep>")) { + foreach (array('package', 'subpackage', 'extension') as $type) { + if (isset($this->_packageInfo['dependencies'][$simpledep][$type])) { + $iter = $this->_packageInfo['dependencies'][$simpledep][$type]; + if (!isset($iter[0])) { + $iter = array($iter); + } + foreach ($iter as $package) { + if ($type != 'extension') { + if (isset($package['uri'])) { + if (isset($package['channel'])) { + $this->_UrlOrChannel($type, + $package['name']); + } + } else { + if (!isset($package['channel'])) { + $this->_NoChannel($type, $package['name']); + } + } + } + $this->{"_validate{$type}Dep"}($package, "<$simpledep>"); + } + } + } + if ($simpledep == 'optional') { + continue; + } + foreach (array('php', 'pearinstaller', 'os', 'arch') as $type) { + if (isset($this->_packageInfo['dependencies'][$simpledep][$type])) { + $iter = $this->_packageInfo['dependencies'][$simpledep][$type]; + if (!isset($iter[0])) { + $iter = array($iter); + } + foreach ($iter as $package) { + $this->{"_validate{$type}Dep"}($package); + } + } + } + } + } + } + if (isset($this->_packageInfo['dependencies']['group'])) { + $groups = $this->_packageInfo['dependencies']['group']; + if (!isset($groups[0])) { + $groups = array($groups); + } + $structure = array( + '*package', + '*subpackage', + '*extension', + ); + foreach ($groups as $group) { + if ($this->_stupidSchemaValidate($structure, $group, '')) { + if (!PEAR_Validate::validGroupName($group['attribs']['name'])) { + $this->_invalidDepGroupName($group['attribs']['name']); + } + foreach (array('package', 'subpackage', 'extension') as $type) { + if (isset($group[$type])) { + $iter = $group[$type]; + if (!isset($iter[0])) { + $iter = array($iter); + } + foreach ($iter as $package) { + if ($type != 'extension') { + if (isset($package['uri'])) { + if (isset($package['channel'])) { + $this->_UrlOrChannelGroup($type, + $package['name'], + $group['name']); + } + } else { + if (!isset($package['channel'])) { + $this->_NoChannelGroup($type, + $package['name'], + $group['name']); + } + } + } + $this->{"_validate{$type}Dep"}($package, ''); + } + } + } + } + } + } + } + + function _validateCompatible() + { + $compat = $this->_packageInfo['compatible']; + if (!isset($compat[0])) { + $compat = array($compat); + } + $required = array('name', 'channel', 'min', 'max', '*exclude'); + foreach ($compat as $package) { + $type = ''; + if (is_array($package) && array_key_exists('name', $package)) { + $type .= '' . $package['name'] . ''; + } + $this->_stupidSchemaValidate($required, $package, $type); + if (is_array($package) && array_key_exists('min', $package)) { + if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/', + $package['min'])) { + $this->_invalidVersion(substr($type, 1) . '_invalidVersion(substr($type, 1) . '_invalidVersion(substr($type, 1) . '_NoBundledPackages(); + } + if (!is_array($list['bundledpackage']) || !isset($list['bundledpackage'][0])) { + return $this->_AtLeast2BundledPackages(); + } + foreach ($list['bundledpackage'] as $package) { + if (!is_string($package)) { + $this->_bundledPackagesMustBeFilename(); + } + } + } + + function _validateFilelist($list = false, $allowignore = false, $dirs = '') + { + $iscontents = false; + if (!$list) { + $iscontents = true; + $list = $this->_packageInfo['contents']; + if (isset($this->_packageInfo['bundle'])) { + return $this->_validateBundle($list); + } + } + if ($allowignore) { + $struc = array( + '*install->name->as', + '*ignore->name' + ); + } else { + $struc = array( + '*dir->name->?baseinstalldir', + '*file->name->role->?baseinstalldir->?md5sum' + ); + if (isset($list['dir']) && isset($list['file'])) { + // stave off validation errors without requiring a set order. + $_old = $list; + if (isset($list['attribs'])) { + $list = array('attribs' => $_old['attribs']); + } + $list['dir'] = $_old['dir']; + $list['file'] = $_old['file']; + } + } + if (!isset($list['attribs']) || !isset($list['attribs']['name'])) { + $unknown = $allowignore ? '' : '
    '; + $dirname = $iscontents ? '' : $unknown; + } else { + $dirname = ''; + if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~', + str_replace('\\', '/', $list['attribs']['name']))) { + // file contains .. parent directory or . cur directory + $this->_invalidDirName($list['attribs']['name']); + } + } + $res = $this->_stupidSchemaValidate($struc, $list, $dirname); + if ($allowignore && $res) { + $ignored_or_installed = array(); + $this->_pf->getFilelist(); + $fcontents = $this->_pf->getContents(); + $filelist = array(); + if (!isset($fcontents['dir']['file'][0])) { + $fcontents['dir']['file'] = array($fcontents['dir']['file']); + } + foreach ($fcontents['dir']['file'] as $file) { + $filelist[$file['attribs']['name']] = true; + } + if (isset($list['install'])) { + if (!isset($list['install'][0])) { + $list['install'] = array($list['install']); + } + foreach ($list['install'] as $file) { + if (!isset($filelist[$file['attribs']['name']])) { + $this->_notInContents($file['attribs']['name'], 'install'); + continue; + } + if (array_key_exists($file['attribs']['name'], $ignored_or_installed)) { + $this->_multipleInstallAs($file['attribs']['name']); + } + if (!isset($ignored_or_installed[$file['attribs']['name']])) { + $ignored_or_installed[$file['attribs']['name']] = array(); + } + $ignored_or_installed[$file['attribs']['name']][] = 1; + if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~', + str_replace('\\', '/', $file['attribs']['as']))) { + // file contains .. parent directory or . cur directory references + $this->_invalidFileInstallAs($file['attribs']['name'], + $file['attribs']['as']); + } + } + } + if (isset($list['ignore'])) { + if (!isset($list['ignore'][0])) { + $list['ignore'] = array($list['ignore']); + } + foreach ($list['ignore'] as $file) { + if (!isset($filelist[$file['attribs']['name']])) { + $this->_notInContents($file['attribs']['name'], 'ignore'); + continue; + } + if (array_key_exists($file['attribs']['name'], $ignored_or_installed)) { + $this->_ignoreAndInstallAs($file['attribs']['name']); + } + } + } + } + if (!$allowignore && isset($list['file'])) { + if (is_string($list['file'])) { + $this->_oldStyleFileNotAllowed(); + return false; + } + if (!isset($list['file'][0])) { + // single file + $list['file'] = array($list['file']); + } + foreach ($list['file'] as $i => $file) + { + if (isset($file['attribs']) && isset($file['attribs']['name'])) { + if ($file['attribs']['name']{0} == '.' && + $file['attribs']['name']{1} == '/') { + // name is something like "./doc/whatever.txt" + $this->_invalidFileName($file['attribs']['name'], $dirname); + } + if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~', + str_replace('\\', '/', $file['attribs']['name']))) { + // file contains .. parent directory or . cur directory + $this->_invalidFileName($file['attribs']['name'], $dirname); + } + } + if (isset($file['attribs']) && isset($file['attribs']['role'])) { + if (!$this->_validateRole($file['attribs']['role'])) { + if (isset($this->_packageInfo['usesrole'])) { + $roles = $this->_packageInfo['usesrole']; + if (!isset($roles[0])) { + $roles = array($roles); + } + foreach ($roles as $role) { + if ($role['role'] = $file['attribs']['role']) { + $msg = 'This package contains role "%role%" and requires ' . + 'package "%package%" to be used'; + if (isset($role['uri'])) { + $params = array('role' => $role['role'], + 'package' => $role['uri']); + } else { + $params = array('role' => $role['role'], + 'package' => $this->_pf->_registry-> + parsedPackageNameToString(array('package' => + $role['package'], 'channel' => $role['channel']), + true)); + } + $this->_stack->push('_mustInstallRole', 'error', $params, $msg); + } + } + } + $this->_invalidFileRole($file['attribs']['name'], + $dirname, $file['attribs']['role']); + } + } + if (!isset($file['attribs'])) { + continue; + } + $save = $file['attribs']; + if ($dirs) { + $save['name'] = $dirs . '/' . $save['name']; + } + unset($file['attribs']); + if (count($file) && $this->_curState != PEAR_VALIDATE_DOWNLOADING) { // has tasks + foreach ($file as $task => $value) { + if ($tagClass = $this->_pf->getTask($task)) { + if (!is_array($value) || !isset($value[0])) { + $value = array($value); + } + foreach ($value as $v) { + $ret = call_user_func(array($tagClass, 'validateXml'), + $this->_pf, $v, $this->_pf->_config, $save); + if (is_array($ret)) { + $this->_invalidTask($task, $ret, isset($save['name']) ? + $save['name'] : ''); + } + } + } else { + if (isset($this->_packageInfo['usestask'])) { + $roles = $this->_packageInfo['usestask']; + if (!isset($roles[0])) { + $roles = array($roles); + } + foreach ($roles as $role) { + if ($role['task'] = $task) { + $msg = 'This package contains task "%task%" and requires ' . + 'package "%package%" to be used'; + if (isset($role['uri'])) { + $params = array('task' => $role['task'], + 'package' => $role['uri']); + } else { + $params = array('task' => $role['task'], + 'package' => $this->_pf->_registry-> + parsedPackageNameToString(array('package' => + $role['package'], 'channel' => $role['channel']), + true)); + } + $this->_stack->push('_mustInstallTask', 'error', + $params, $msg); + } + } + } + $this->_unknownTask($task, $save['name']); + } + } + } + } + } + if (isset($list['ignore'])) { + if (!$allowignore) { + $this->_ignoreNotAllowed('ignore'); + } + } + if (isset($list['install'])) { + if (!$allowignore) { + $this->_ignoreNotAllowed('install'); + } + } + if (isset($list['file'])) { + if ($allowignore) { + $this->_fileNotAllowed('file'); + } + } + if (isset($list['dir'])) { + if ($allowignore) { + $this->_fileNotAllowed('dir'); + } else { + if (!isset($list['dir'][0])) { + $list['dir'] = array($list['dir']); + } + foreach ($list['dir'] as $dir) { + if (isset($dir['attribs']) && isset($dir['attribs']['name'])) { + if ($dir['attribs']['name'] == '/' || + !isset($this->_packageInfo['contents']['dir']['dir'])) { + // always use nothing if the filelist has already been flattened + $newdirs = ''; + } elseif ($dirs == '') { + $newdirs = $dir['attribs']['name']; + } else { + $newdirs = $dirs . '/' . $dir['attribs']['name']; + } + } else { + $newdirs = $dirs; + } + $this->_validateFilelist($dir, $allowignore, $newdirs); + } + } + } + } + + function _validateRelease() + { + if (isset($this->_packageInfo['phprelease'])) { + $release = 'phprelease'; + if (isset($this->_packageInfo['providesextension'])) { + $this->_cannotProvideExtension($release); + } + if (isset($this->_packageInfo['srcpackage']) || isset($this->_packageInfo['srcuri'])) { + $this->_cannotHaveSrcpackage($release); + } + $releases = $this->_packageInfo['phprelease']; + if (!is_array($releases)) { + return true; + } + if (!isset($releases[0])) { + $releases = array($releases); + } + foreach ($releases as $rel) { + $this->_stupidSchemaValidate(array( + '*installconditions', + '*filelist', + ), $rel, ''); + } + } + foreach (array('', 'zend') as $prefix) { + $releasetype = $prefix . 'extsrcrelease'; + if (isset($this->_packageInfo[$releasetype])) { + $release = $releasetype; + if (!isset($this->_packageInfo['providesextension'])) { + $this->_mustProvideExtension($release); + } + if (isset($this->_packageInfo['srcpackage']) || isset($this->_packageInfo['srcuri'])) { + $this->_cannotHaveSrcpackage($release); + } + $releases = $this->_packageInfo[$releasetype]; + if (!is_array($releases)) { + return true; + } + if (!isset($releases[0])) { + $releases = array($releases); + } + foreach ($releases as $rel) { + $this->_stupidSchemaValidate(array( + '*installconditions', + '*configureoption->name->prompt->?default', + '*binarypackage', + '*filelist', + ), $rel, '<' . $releasetype . '>'); + if (isset($rel['binarypackage'])) { + if (!is_array($rel['binarypackage']) || !isset($rel['binarypackage'][0])) { + $rel['binarypackage'] = array($rel['binarypackage']); + } + foreach ($rel['binarypackage'] as $bin) { + if (!is_string($bin)) { + $this->_binaryPackageMustBePackagename(); + } + } + } + } + } + $releasetype = 'extbinrelease'; + if (isset($this->_packageInfo[$releasetype])) { + $release = $releasetype; + if (!isset($this->_packageInfo['providesextension'])) { + $this->_mustProvideExtension($release); + } + if (isset($this->_packageInfo['channel']) && + !isset($this->_packageInfo['srcpackage'])) { + $this->_mustSrcPackage($release); + } + if (isset($this->_packageInfo['uri']) && !isset($this->_packageInfo['srcuri'])) { + $this->_mustSrcuri($release); + } + $releases = $this->_packageInfo[$releasetype]; + if (!is_array($releases)) { + return true; + } + if (!isset($releases[0])) { + $releases = array($releases); + } + foreach ($releases as $rel) { + $this->_stupidSchemaValidate(array( + '*installconditions', + '*filelist', + ), $rel, '<' . $releasetype . '>'); + } + } + } + if (isset($this->_packageInfo['bundle'])) { + $release = 'bundle'; + if (isset($this->_packageInfo['providesextension'])) { + $this->_cannotProvideExtension($release); + } + if (isset($this->_packageInfo['srcpackage']) || isset($this->_packageInfo['srcuri'])) { + $this->_cannotHaveSrcpackage($release); + } + $releases = $this->_packageInfo['bundle']; + if (!is_array($releases) || !isset($releases[0])) { + $releases = array($releases); + } + foreach ($releases as $rel) { + $this->_stupidSchemaValidate(array( + '*installconditions', + '*filelist', + ), $rel, ''); + } + } + foreach ($releases as $rel) { + if (is_array($rel) && array_key_exists('installconditions', $rel)) { + $this->_validateInstallConditions($rel['installconditions'], + "<$release>"); + } + if (is_array($rel) && array_key_exists('filelist', $rel)) { + if ($rel['filelist']) { + + $this->_validateFilelist($rel['filelist'], true); + } + } + } + } + + /** + * This is here to allow role extension through plugins + * @param string + */ + function _validateRole($role) + { + return in_array($role, PEAR_Installer_Role::getValidRoles($this->_pf->getPackageType())); + } + + function _pearVersionTooLow($version) + { + $this->_stack->push(__FUNCTION__, 'error', + array('version' => $version), + 'This package.xml requires PEAR version %version% to parse properly, we are ' . + 'version 1.9.4'); + } + + function _invalidTagOrder($oktags, $actual, $root) + { + $this->_stack->push(__FUNCTION__, 'error', + array('oktags' => $oktags, 'actual' => $actual, 'root' => $root), + 'Invalid tag order in %root%, found <%actual%> expected one of "%oktags%"'); + } + + function _ignoreNotAllowed($type) + { + $this->_stack->push(__FUNCTION__, 'error', array('type' => $type), + '<%type%> is not allowed inside global , only inside ' . + '//, use and only'); + } + + function _fileNotAllowed($type) + { + $this->_stack->push(__FUNCTION__, 'error', array('type' => $type), + '<%type%> is not allowed inside release , only inside ' . + ', use and only'); + } + + function _oldStyleFileNotAllowed() + { + $this->_stack->push(__FUNCTION__, 'error', array(), + 'Old-style name is not allowed. Use' . + ''); + } + + function _tagMissingAttribute($tag, $attr, $context) + { + $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag, + 'attribute' => $attr, 'context' => $context), + 'tag <%tag%> in context "%context%" has no attribute "%attribute%"'); + } + + function _tagHasNoAttribs($tag, $context) + { + $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag, + 'context' => $context), + 'tag <%tag%> has no attributes in context "%context%"'); + } + + function _invalidInternalStructure() + { + $this->_stack->push(__FUNCTION__, 'exception', array(), + 'internal array was not generated by compatible parser, or extreme parser error, cannot continue'); + } + + function _invalidFileRole($file, $dir, $role) + { + $this->_stack->push(__FUNCTION__, 'error', array( + 'file' => $file, 'dir' => $dir, 'role' => $role, + 'roles' => PEAR_Installer_Role::getValidRoles($this->_pf->getPackageType())), + 'File "%file%" in directory "%dir%" has invalid role "%role%", should be one of %roles%'); + } + + function _invalidFileName($file, $dir) + { + $this->_stack->push(__FUNCTION__, 'error', array( + 'file' => $file), + 'File "%file%" in directory "%dir%" cannot begin with "./" or contain ".."'); + } + + function _invalidFileInstallAs($file, $as) + { + $this->_stack->push(__FUNCTION__, 'error', array( + 'file' => $file, 'as' => $as), + 'File "%file%" cannot contain "./" or contain ".."'); + } + + function _invalidDirName($dir) + { + $this->_stack->push(__FUNCTION__, 'error', array( + 'dir' => $file), + 'Directory "%dir%" cannot begin with "./" or contain ".."'); + } + + function _filelistCannotContainFile($filelist) + { + $this->_stack->push(__FUNCTION__, 'error', array('tag' => $filelist), + '<%tag%> can only contain , contains . Use ' . + ' as the first dir element'); + } + + function _filelistMustContainDir($filelist) + { + $this->_stack->push(__FUNCTION__, 'error', array('tag' => $filelist), + '<%tag%> must contain . Use as the ' . + 'first dir element'); + } + + function _tagCannotBeEmpty($tag) + { + $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag), + '<%tag%> cannot be empty (<%tag%/>)'); + } + + function _UrlOrChannel($type, $name) + { + $this->_stack->push(__FUNCTION__, 'error', array('type' => $type, + 'name' => $name), + 'Required dependency <%type%> "%name%" can have either url OR ' . + 'channel attributes, and not both'); + } + + function _NoChannel($type, $name) + { + $this->_stack->push(__FUNCTION__, 'error', array('type' => $type, + 'name' => $name), + 'Required dependency <%type%> "%name%" must have either url OR ' . + 'channel attributes'); + } + + function _UrlOrChannelGroup($type, $name, $group) + { + $this->_stack->push(__FUNCTION__, 'error', array('type' => $type, + 'name' => $name, 'group' => $group), + 'Group "%group%" dependency <%type%> "%name%" can have either url OR ' . + 'channel attributes, and not both'); + } + + function _NoChannelGroup($type, $name, $group) + { + $this->_stack->push(__FUNCTION__, 'error', array('type' => $type, + 'name' => $name, 'group' => $group), + 'Group "%group%" dependency <%type%> "%name%" must have either url OR ' . + 'channel attributes'); + } + + function _unknownChannel($channel) + { + $this->_stack->push(__FUNCTION__, 'error', array('channel' => $channel), + 'Unknown channel "%channel%"'); + } + + function _noPackageVersion() + { + $this->_stack->push(__FUNCTION__, 'error', array(), + 'package.xml tag has no version attribute, or version is not 2.0'); + } + + function _NoBundledPackages() + { + $this->_stack->push(__FUNCTION__, 'error', array(), + 'No tag was found in , required for bundle packages'); + } + + function _AtLeast2BundledPackages() + { + $this->_stack->push(__FUNCTION__, 'error', array(), + 'At least 2 packages must be bundled in a bundle package'); + } + + function _ChannelOrUri($name) + { + $this->_stack->push(__FUNCTION__, 'error', array('name' => $name), + 'Bundled package "%name%" can have either a uri or a channel, not both'); + } + + function _noChildTag($child, $tag) + { + $this->_stack->push(__FUNCTION__, 'error', array('child' => $child, 'tag' => $tag), + 'Tag <%tag%> is missing child tag <%child%>'); + } + + function _invalidVersion($type, $value) + { + $this->_stack->push(__FUNCTION__, 'error', array('type' => $type, 'value' => $value), + 'Version type <%type%> is not a valid version (%value%)'); + } + + function _invalidState($type, $value) + { + $states = array('stable', 'beta', 'alpha', 'devel'); + if ($type != 'api') { + $states[] = 'snapshot'; + } + if (strtolower($value) == 'rc') { + $this->_stack->push(__FUNCTION__, 'error', + array('version' => $this->_packageInfo['version']['release']), + 'RC is not a state, it is a version postfix, try %version%RC1, stability beta'); + } + $this->_stack->push(__FUNCTION__, 'error', array('type' => $type, 'value' => $value, + 'types' => $states), + 'Stability type <%type%> is not a valid stability (%value%), must be one of ' . + '%types%'); + } + + function _invalidTask($task, $ret, $file) + { + switch ($ret[0]) { + case PEAR_TASK_ERROR_MISSING_ATTRIB : + $info = array('attrib' => $ret[1], 'task' => $task, 'file' => $file); + $msg = 'task <%task%> is missing attribute "%attrib%" in file %file%'; + break; + case PEAR_TASK_ERROR_NOATTRIBS : + $info = array('task' => $task, 'file' => $file); + $msg = 'task <%task%> has no attributes in file %file%'; + break; + case PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE : + $info = array('attrib' => $ret[1], 'values' => $ret[3], + 'was' => $ret[2], 'task' => $task, 'file' => $file); + $msg = 'task <%task%> attribute "%attrib%" has the wrong value "%was%" '. + 'in file %file%, expecting one of "%values%"'; + break; + case PEAR_TASK_ERROR_INVALID : + $info = array('reason' => $ret[1], 'task' => $task, 'file' => $file); + $msg = 'task <%task%> in file %file% is invalid because of "%reason%"'; + break; + } + $this->_stack->push(__FUNCTION__, 'error', $info, $msg); + } + + function _unknownTask($task, $file) + { + $this->_stack->push(__FUNCTION__, 'error', array('task' => $task, 'file' => $file), + 'Unknown task "%task%" passed in file '); + } + + function _subpackageCannotProvideExtension($name) + { + $this->_stack->push(__FUNCTION__, 'error', array('name' => $name), + 'Subpackage dependency "%name%" cannot use , ' . + 'only package dependencies can use this tag'); + } + + function _subpackagesCannotConflict($name) + { + $this->_stack->push(__FUNCTION__, 'error', array('name' => $name), + 'Subpackage dependency "%name%" cannot use , ' . + 'only package dependencies can use this tag'); + } + + function _cannotProvideExtension($release) + { + $this->_stack->push(__FUNCTION__, 'error', array('release' => $release), + '<%release%> packages cannot use , only extbinrelease, extsrcrelease, zendextsrcrelease, and zendextbinrelease can provide a PHP extension'); + } + + function _mustProvideExtension($release) + { + $this->_stack->push(__FUNCTION__, 'error', array('release' => $release), + '<%release%> packages must use to indicate which PHP extension is provided'); + } + + function _cannotHaveSrcpackage($release) + { + $this->_stack->push(__FUNCTION__, 'error', array('release' => $release), + '<%release%> packages cannot specify a source code package, only extension binaries may use the tag'); + } + + function _mustSrcPackage($release) + { + $this->_stack->push(__FUNCTION__, 'error', array('release' => $release), + '/ packages must specify a source code package with '); + } + + function _mustSrcuri($release) + { + $this->_stack->push(__FUNCTION__, 'error', array('release' => $release), + '/ packages must specify a source code package with '); + } + + function _uriDepsCannotHaveVersioning($type) + { + $this->_stack->push(__FUNCTION__, 'error', array('type' => $type), + '%type%: dependencies with a tag cannot have any versioning information'); + } + + function _conflictingDepsCannotHaveVersioning($type) + { + $this->_stack->push(__FUNCTION__, 'error', array('type' => $type), + '%type%: conflicting dependencies cannot have versioning info, use to ' . + 'exclude specific versions of a dependency'); + } + + function _DepchannelCannotBeUri($type) + { + $this->_stack->push(__FUNCTION__, 'error', array('type' => $type), + '%type%: channel cannot be __uri, this is a pseudo-channel reserved for uri ' . + 'dependencies only'); + } + + function _bundledPackagesMustBeFilename() + { + $this->_stack->push(__FUNCTION__, 'error', array(), + ' tags must contain only the filename of a package release ' . + 'in the bundle'); + } + + function _binaryPackageMustBePackagename() + { + $this->_stack->push(__FUNCTION__, 'error', array(), + ' tags must contain the name of a package that is ' . + 'a compiled version of this extsrc/zendextsrc package'); + } + + function _fileNotFound($file) + { + $this->_stack->push(__FUNCTION__, 'error', array('file' => $file), + 'File "%file%" in package.xml does not exist'); + } + + function _notInContents($file, $tag) + { + $this->_stack->push(__FUNCTION__, 'error', array('file' => $file, 'tag' => $tag), + '<%tag% name="%file%"> is invalid, file is not in '); + } + + function _cannotValidateNoPathSet() + { + $this->_stack->push(__FUNCTION__, 'error', array(), + 'Cannot validate files, no path to package file is set (use setPackageFile())'); + } + + function _usesroletaskMustHaveChannelOrUri($role, $tag) + { + $this->_stack->push(__FUNCTION__, 'error', array('role' => $role, 'tag' => $tag), + '<%tag%> for role "%role%" must contain either , or and '); + } + + function _usesroletaskMustHavePackage($role, $tag) + { + $this->_stack->push(__FUNCTION__, 'error', array('role' => $role, 'tag' => $tag), + '<%tag%> for role "%role%" must contain '); + } + + function _usesroletaskMustHaveRoleTask($tag, $type) + { + $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag, 'type' => $type), + '<%tag%> must contain <%type%> defining the %type% to be used'); + } + + function _cannotConflictWithAllOs($type) + { + $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag), + '%tag% cannot conflict with all OSes'); + } + + function _invalidDepGroupName($name) + { + $this->_stack->push(__FUNCTION__, 'error', array('name' => $name), + 'Invalid dependency group name "%name%"'); + } + + function _multipleToplevelDirNotAllowed() + { + $this->_stack->push(__FUNCTION__, 'error', array(), + 'Multiple top-level tags are not allowed. Enclose them ' . + 'in a '); + } + + function _multipleInstallAs($file) + { + $this->_stack->push(__FUNCTION__, 'error', array('file' => $file), + 'Only one tag is allowed for file "%file%"'); + } + + function _ignoreAndInstallAs($file) + { + $this->_stack->push(__FUNCTION__, 'error', array('file' => $file), + 'Cannot have both and tags for file "%file%"'); + } + + function _analyzeBundledPackages() + { + if (!$this->_isValid) { + return false; + } + if (!$this->_pf->getPackageType() == 'bundle') { + return false; + } + if (!isset($this->_pf->_packageFile)) { + return false; + } + $dir_prefix = dirname($this->_pf->_packageFile); + $common = new PEAR_Common; + $log = isset($this->_pf->_logger) ? array(&$this->_pf->_logger, 'log') : + array($common, 'log'); + $info = $this->_pf->getContents(); + $info = $info['bundledpackage']; + if (!is_array($info)) { + $info = array($info); + } + $pkg = &new PEAR_PackageFile($this->_pf->_config); + foreach ($info as $package) { + if (!file_exists($dir_prefix . DIRECTORY_SEPARATOR . $package)) { + $this->_fileNotFound($dir_prefix . DIRECTORY_SEPARATOR . $package); + $this->_isValid = 0; + continue; + } + call_user_func_array($log, array(1, "Analyzing bundled package $package")); + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + $ret = $pkg->fromAnyFile($dir_prefix . DIRECTORY_SEPARATOR . $package, + PEAR_VALIDATE_NORMAL); + PEAR::popErrorHandling(); + if (PEAR::isError($ret)) { + call_user_func_array($log, array(0, "ERROR: package $package is not a valid " . + 'package')); + $inf = $ret->getUserInfo(); + if (is_array($inf)) { + foreach ($inf as $err) { + call_user_func_array($log, array(1, $err['message'])); + } + } + return false; + } + } + return true; + } + + function _analyzePhpFiles() + { + if (!$this->_isValid) { + return false; + } + if (!isset($this->_pf->_packageFile)) { + $this->_cannotValidateNoPathSet(); + return false; + } + $dir_prefix = dirname($this->_pf->_packageFile); + $common = new PEAR_Common; + $log = isset($this->_pf->_logger) ? array(&$this->_pf->_logger, 'log') : + array(&$common, 'log'); + $info = $this->_pf->getContents(); + if (!$info || !isset($info['dir']['file'])) { + $this->_tagCannotBeEmpty('contents>_fileNotFound($dir_prefix . DIRECTORY_SEPARATOR . $file); + $this->_isValid = 0; + continue; + } + if (in_array($fa['role'], PEAR_Installer_Role::getPhpRoles()) && $dir_prefix) { + call_user_func_array($log, array(1, "Analyzing $file")); + $srcinfo = $this->analyzeSourceCode($dir_prefix . DIRECTORY_SEPARATOR . $file); + if ($srcinfo) { + $provides = array_merge($provides, $this->_buildProvidesArray($srcinfo)); + } + } + } + $this->_packageName = $pn = $this->_pf->getPackage(); + $pnl = strlen($pn); + foreach ($provides as $key => $what) { + if (isset($what['explicit']) || !$what) { + // skip conformance checks if the provides entry is + // specified in the package.xml file + continue; + } + extract($what); + if ($type == 'class') { + if (!strncasecmp($name, $pn, $pnl)) { + continue; + } + $this->_stack->push(__FUNCTION__, 'warning', + array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn), + 'in %file%: %type% "%name%" not prefixed with package name "%package%"'); + } elseif ($type == 'function') { + if (strstr($name, '::') || !strncasecmp($name, $pn, $pnl)) { + continue; + } + $this->_stack->push(__FUNCTION__, 'warning', + array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn), + 'in %file%: %type% "%name%" not prefixed with package name "%package%"'); + } + } + return $this->_isValid; + } + + /** + * Analyze the source code of the given PHP file + * + * @param string Filename of the PHP file + * @param boolean whether to analyze $file as the file contents + * @return mixed + */ + function analyzeSourceCode($file, $string = false) + { + if (!function_exists("token_get_all")) { + $this->_stack->push(__FUNCTION__, 'error', array('file' => $file), + 'Parser error: token_get_all() function must exist to analyze source code, PHP may have been compiled with --disable-tokenizer'); + return false; + } + + if (!defined('T_DOC_COMMENT')) { + define('T_DOC_COMMENT', T_COMMENT); + } + + if (!defined('T_INTERFACE')) { + define('T_INTERFACE', -1); + } + + if (!defined('T_IMPLEMENTS')) { + define('T_IMPLEMENTS', -1); + } + + if ($string) { + $contents = $file; + } else { + if (!$fp = @fopen($file, "r")) { + return false; + } + fclose($fp); + $contents = file_get_contents($file); + } + + // Silence this function so we can catch PHP Warnings and show our own custom message + $tokens = @token_get_all($contents); + if (isset($php_errormsg)) { + if (isset($this->_stack)) { + $pn = $this->_pf->getPackage(); + $this->_stack->push(__FUNCTION__, 'warning', + array('file' => $file, 'package' => $pn), + 'in %file%: Could not process file for unkown reasons,' . + ' possibly a PHP parse error in %file% from %package%'); + } + } +/* + for ($i = 0; $i < sizeof($tokens); $i++) { + @list($token, $data) = $tokens[$i]; + if (is_string($token)) { + var_dump($token); + } else { + print token_name($token) . ' '; + var_dump(rtrim($data)); + } + } +*/ + $look_for = 0; + $paren_level = 0; + $bracket_level = 0; + $brace_level = 0; + $lastphpdoc = ''; + $current_class = ''; + $current_interface = ''; + $current_class_level = -1; + $current_function = ''; + $current_function_level = -1; + $declared_classes = array(); + $declared_interfaces = array(); + $declared_functions = array(); + $declared_methods = array(); + $used_classes = array(); + $used_functions = array(); + $extends = array(); + $implements = array(); + $nodeps = array(); + $inquote = false; + $interface = false; + for ($i = 0; $i < sizeof($tokens); $i++) { + if (is_array($tokens[$i])) { + list($token, $data) = $tokens[$i]; + } else { + $token = $tokens[$i]; + $data = ''; + } + + if ($inquote) { + if ($token != '"' && $token != T_END_HEREDOC) { + continue; + } else { + $inquote = false; + continue; + } + } + + switch ($token) { + case T_WHITESPACE : + continue; + case ';': + if ($interface) { + $current_function = ''; + $current_function_level = -1; + } + break; + case '"': + case T_START_HEREDOC: + $inquote = true; + break; + case T_CURLY_OPEN: + case T_DOLLAR_OPEN_CURLY_BRACES: + case '{': $brace_level++; continue 2; + case '}': + $brace_level--; + if ($current_class_level == $brace_level) { + $current_class = ''; + $current_class_level = -1; + } + if ($current_function_level == $brace_level) { + $current_function = ''; + $current_function_level = -1; + } + continue 2; + case '[': $bracket_level++; continue 2; + case ']': $bracket_level--; continue 2; + case '(': $paren_level++; continue 2; + case ')': $paren_level--; continue 2; + case T_INTERFACE: + $interface = true; + case T_CLASS: + if (($current_class_level != -1) || ($current_function_level != -1)) { + if (isset($this->_stack)) { + $this->_stack->push(__FUNCTION__, 'error', array('file' => $file), + 'Parser error: invalid PHP found in file "%file%"'); + } else { + PEAR::raiseError("Parser error: invalid PHP found in file \"$file\"", + PEAR_COMMON_ERROR_INVALIDPHP); + } + + return false; + } + case T_FUNCTION: + case T_NEW: + case T_EXTENDS: + case T_IMPLEMENTS: + $look_for = $token; + continue 2; + case T_STRING: + if (version_compare(zend_version(), '2.0', '<')) { + if (in_array(strtolower($data), + array('public', 'private', 'protected', 'abstract', + 'interface', 'implements', 'throw') + ) + ) { + if (isset($this->_stack)) { + $this->_stack->push(__FUNCTION__, 'warning', array( + 'file' => $file), + 'Error, PHP5 token encountered in %file%,' . + ' analysis should be in PHP5'); + } else { + PEAR::raiseError('Error: PHP5 token encountered in ' . $file . + 'packaging should be done in PHP 5'); + return false; + } + } + } + + if ($look_for == T_CLASS) { + $current_class = $data; + $current_class_level = $brace_level; + $declared_classes[] = $current_class; + } elseif ($look_for == T_INTERFACE) { + $current_interface = $data; + $current_class_level = $brace_level; + $declared_interfaces[] = $current_interface; + } elseif ($look_for == T_IMPLEMENTS) { + $implements[$current_class] = $data; + } elseif ($look_for == T_EXTENDS) { + $extends[$current_class] = $data; + } elseif ($look_for == T_FUNCTION) { + if ($current_class) { + $current_function = "$current_class::$data"; + $declared_methods[$current_class][] = $data; + } elseif ($current_interface) { + $current_function = "$current_interface::$data"; + $declared_methods[$current_interface][] = $data; + } else { + $current_function = $data; + $declared_functions[] = $current_function; + } + + $current_function_level = $brace_level; + $m = array(); + } elseif ($look_for == T_NEW) { + $used_classes[$data] = true; + } + + $look_for = 0; + continue 2; + case T_VARIABLE: + $look_for = 0; + continue 2; + case T_DOC_COMMENT: + case T_COMMENT: + if (preg_match('!^/\*\*\s!', $data)) { + $lastphpdoc = $data; + if (preg_match_all('/@nodep\s+(\S+)/', $lastphpdoc, $m)) { + $nodeps = array_merge($nodeps, $m[1]); + } + } + continue 2; + case T_DOUBLE_COLON: + $token = $tokens[$i - 1][0]; + if (!($token == T_WHITESPACE || $token == T_STRING || $token == T_STATIC)) { + if (isset($this->_stack)) { + $this->_stack->push(__FUNCTION__, 'warning', array('file' => $file), + 'Parser error: invalid PHP found in file "%file%"'); + } else { + PEAR::raiseError("Parser error: invalid PHP found in file \"$file\"", + PEAR_COMMON_ERROR_INVALIDPHP); + } + + return false; + } + + $class = $tokens[$i - 1][1]; + if (strtolower($class) != 'parent') { + $used_classes[$class] = true; + } + + continue 2; + } + } + + return array( + "source_file" => $file, + "declared_classes" => $declared_classes, + "declared_interfaces" => $declared_interfaces, + "declared_methods" => $declared_methods, + "declared_functions" => $declared_functions, + "used_classes" => array_diff(array_keys($used_classes), $nodeps), + "inheritance" => $extends, + "implements" => $implements, + ); + } + + /** + * Build a "provides" array from data returned by + * analyzeSourceCode(). The format of the built array is like + * this: + * + * array( + * 'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'), + * ... + * ) + * + * + * @param array $srcinfo array with information about a source file + * as returned by the analyzeSourceCode() method. + * + * @return void + * + * @access private + * + */ + function _buildProvidesArray($srcinfo) + { + if (!$this->_isValid) { + return array(); + } + + $providesret = array(); + $file = basename($srcinfo['source_file']); + $pn = isset($this->_pf) ? $this->_pf->getPackage() : ''; + $pnl = strlen($pn); + foreach ($srcinfo['declared_classes'] as $class) { + $key = "class;$class"; + if (isset($providesret[$key])) { + continue; + } + + $providesret[$key] = + array('file'=> $file, 'type' => 'class', 'name' => $class); + if (isset($srcinfo['inheritance'][$class])) { + $providesret[$key]['extends'] = + $srcinfo['inheritance'][$class]; + } + } + + foreach ($srcinfo['declared_methods'] as $class => $methods) { + foreach ($methods as $method) { + $function = "$class::$method"; + $key = "function;$function"; + if ($method{0} == '_' || !strcasecmp($method, $class) || + isset($providesret[$key])) { + continue; + } + + $providesret[$key] = + array('file'=> $file, 'type' => 'function', 'name' => $function); + } + } + + foreach ($srcinfo['declared_functions'] as $function) { + $key = "function;$function"; + if ($function{0} == '_' || isset($providesret[$key])) { + continue; + } + + if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) { + $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\""; + } + + $providesret[$key] = + array('file'=> $file, 'type' => 'function', 'name' => $function); + } + + return $providesret; + } +} \ No newline at end of file diff --git a/includes/pear/PEAR/PackageFile/v2/rw.php b/includes/pear/PEAR/PackageFile/v2/rw.php new file mode 100644 index 0000000..4320d33 --- /dev/null +++ b/includes/pear/PEAR/PackageFile/v2/rw.php @@ -0,0 +1,1607 @@ + + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a8 + */ +/** + * For base class + */ +require_once 'PEAR/PackageFile/v2.php'; +/** + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a8 + */ +class PEAR_PackageFile_v2_rw extends PEAR_PackageFile_v2 +{ + /** + * @param string Extension name + * @return bool success of operation + */ + function setProvidesExtension($extension) + { + if (in_array($this->getPackageType(), + array('extsrc', 'extbin', 'zendextsrc', 'zendextbin'))) { + if (!isset($this->_packageInfo['providesextension'])) { + // ensure that the channel tag is set up in the right location + $this->_packageInfo = $this->_insertBefore($this->_packageInfo, + array('usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease', + 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', + 'bundle', 'changelog'), + $extension, 'providesextension'); + } + $this->_packageInfo['providesextension'] = $extension; + return true; + } + return false; + } + + function setPackage($package) + { + $this->_isValid = 0; + if (!isset($this->_packageInfo['attribs'])) { + $this->_packageInfo = array_merge(array('attribs' => array( + 'version' => '2.0', + 'xmlns' => 'http://pear.php.net/dtd/package-2.0', + 'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0', + 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', + 'xsi:schemaLocation' => 'http://pear.php.net/dtd/tasks-1.0 + http://pear.php.net/dtd/tasks-1.0.xsd + http://pear.php.net/dtd/package-2.0 + http://pear.php.net/dtd/package-2.0.xsd', + )), $this->_packageInfo); + } + if (!isset($this->_packageInfo['name'])) { + return $this->_packageInfo = array_merge(array('name' => $package), + $this->_packageInfo); + } + $this->_packageInfo['name'] = $package; + } + + /** + * set this as a package.xml version 2.1 + * @access private + */ + function _setPackageVersion2_1() + { + $info = array( + 'version' => '2.1', + 'xmlns' => 'http://pear.php.net/dtd/package-2.1', + 'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0', + 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', + 'xsi:schemaLocation' => 'http://pear.php.net/dtd/tasks-1.0 + http://pear.php.net/dtd/tasks-1.0.xsd + http://pear.php.net/dtd/package-2.1 + http://pear.php.net/dtd/package-2.1.xsd', + ); + if (!isset($this->_packageInfo['attribs'])) { + $this->_packageInfo = array_merge(array('attribs' => $info), $this->_packageInfo); + } else { + $this->_packageInfo['attribs'] = $info; + } + } + + function setUri($uri) + { + unset($this->_packageInfo['channel']); + $this->_isValid = 0; + if (!isset($this->_packageInfo['uri'])) { + // ensure that the uri tag is set up in the right location + $this->_packageInfo = $this->_insertBefore($this->_packageInfo, + array('extends', 'summary', 'description', 'lead', + 'developer', 'contributor', 'helper', 'date', 'time', 'version', + 'stability', 'license', 'notes', 'contents', 'compatible', + 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', + 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease', + 'extbinrelease', 'bundle', 'changelog'), $uri, 'uri'); + } + $this->_packageInfo['uri'] = $uri; + } + + function setChannel($channel) + { + unset($this->_packageInfo['uri']); + $this->_isValid = 0; + if (!isset($this->_packageInfo['channel'])) { + // ensure that the channel tag is set up in the right location + $this->_packageInfo = $this->_insertBefore($this->_packageInfo, + array('extends', 'summary', 'description', 'lead', + 'developer', 'contributor', 'helper', 'date', 'time', 'version', + 'stability', 'license', 'notes', 'contents', 'compatible', + 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', + 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease', + 'extbinrelease', 'bundle', 'changelog'), $channel, 'channel'); + } + $this->_packageInfo['channel'] = $channel; + } + + function setExtends($extends) + { + $this->_isValid = 0; + if (!isset($this->_packageInfo['extends'])) { + // ensure that the extends tag is set up in the right location + $this->_packageInfo = $this->_insertBefore($this->_packageInfo, + array('summary', 'description', 'lead', + 'developer', 'contributor', 'helper', 'date', 'time', 'version', + 'stability', 'license', 'notes', 'contents', 'compatible', + 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', + 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease', + 'extbinrelease', 'bundle', 'changelog'), $extends, 'extends'); + } + $this->_packageInfo['extends'] = $extends; + } + + function setSummary($summary) + { + $this->_isValid = 0; + if (!isset($this->_packageInfo['summary'])) { + // ensure that the summary tag is set up in the right location + $this->_packageInfo = $this->_insertBefore($this->_packageInfo, + array('description', 'lead', + 'developer', 'contributor', 'helper', 'date', 'time', 'version', + 'stability', 'license', 'notes', 'contents', 'compatible', + 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', + 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease', + 'extbinrelease', 'bundle', 'changelog'), $summary, 'summary'); + } + $this->_packageInfo['summary'] = $summary; + } + + function setDescription($desc) + { + $this->_isValid = 0; + if (!isset($this->_packageInfo['description'])) { + // ensure that the description tag is set up in the right location + $this->_packageInfo = $this->_insertBefore($this->_packageInfo, + array('lead', + 'developer', 'contributor', 'helper', 'date', 'time', 'version', + 'stability', 'license', 'notes', 'contents', 'compatible', + 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', + 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease', + 'extbinrelease', 'bundle', 'changelog'), $desc, 'description'); + } + $this->_packageInfo['description'] = $desc; + } + + /** + * Adds a new maintainer - no checking of duplicates is performed, use + * updatemaintainer for that purpose. + */ + function addMaintainer($role, $handle, $name, $email, $active = 'yes') + { + if (!in_array($role, array('lead', 'developer', 'contributor', 'helper'))) { + return false; + } + if (isset($this->_packageInfo[$role])) { + if (!isset($this->_packageInfo[$role][0])) { + $this->_packageInfo[$role] = array($this->_packageInfo[$role]); + } + $this->_packageInfo[$role][] = + array( + 'name' => $name, + 'user' => $handle, + 'email' => $email, + 'active' => $active, + ); + } else { + $testarr = array('lead', + 'developer', 'contributor', 'helper', 'date', 'time', 'version', + 'stability', 'license', 'notes', 'contents', 'compatible', + 'dependencies', 'providesextension', 'usesrole', 'usestask', + 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', + 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'); + foreach (array('lead', 'developer', 'contributor', 'helper') as $testrole) { + array_shift($testarr); + if ($role == $testrole) { + break; + } + } + if (!isset($this->_packageInfo[$role])) { + // ensure that the extends tag is set up in the right location + $this->_packageInfo = $this->_insertBefore($this->_packageInfo, $testarr, + array(), $role); + } + $this->_packageInfo[$role] = + array( + 'name' => $name, + 'user' => $handle, + 'email' => $email, + 'active' => $active, + ); + } + $this->_isValid = 0; + } + + function updateMaintainer($newrole, $handle, $name, $email, $active = 'yes') + { + $found = false; + foreach (array('lead', 'developer', 'contributor', 'helper') as $role) { + if (!isset($this->_packageInfo[$role])) { + continue; + } + $info = $this->_packageInfo[$role]; + if (!isset($info[0])) { + if (isset($info['user']) && $info['user'] == $handle) { + $found = true; + break; + } + } + foreach ($info as $i => $maintainer) { + if (!isset($maintainer['user'])) { + continue; + } + if ($maintainer['user'] == $handle) { + $found = $i; + break 2; + } + } + } + if ($found === false) { + return $this->addMaintainer($newrole, $handle, $name, $email, $active); + } + if ($found !== false) { + if ($found === true) { + unset($this->_packageInfo[$role]); + } else { + unset($this->_packageInfo[$role][$found]); + $this->_packageInfo[$role] = array_values($this->_packageInfo[$role]); + } + } + $this->addMaintainer($newrole, $handle, $name, $email, $active); + $this->_isValid = 0; + } + + function deleteMaintainer($handle) + { + $found = false; + foreach (array('lead', 'developer', 'contributor', 'helper') as $role) { + if (!isset($this->_packageInfo[$role])) { + continue; + } + if (!isset($this->_packageInfo[$role][0])) { + $this->_packageInfo[$role] = array($this->_packageInfo[$role]); + } + foreach ($this->_packageInfo[$role] as $i => $maintainer) { + if ($maintainer['user'] == $handle) { + $found = $i; + break; + } + } + if ($found !== false) { + unset($this->_packageInfo[$role][$found]); + if (!count($this->_packageInfo[$role]) && $role == 'lead') { + $this->_isValid = 0; + } + if (!count($this->_packageInfo[$role])) { + unset($this->_packageInfo[$role]); + return true; + } + $this->_packageInfo[$role] = + array_values($this->_packageInfo[$role]); + if (count($this->_packageInfo[$role]) == 1) { + $this->_packageInfo[$role] = $this->_packageInfo[$role][0]; + } + return true; + } + if (count($this->_packageInfo[$role]) == 1) { + $this->_packageInfo[$role] = $this->_packageInfo[$role][0]; + } + } + return false; + } + + function setReleaseVersion($version) + { + if (isset($this->_packageInfo['version']) && + isset($this->_packageInfo['version']['release'])) { + unset($this->_packageInfo['version']['release']); + } + $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $version, array( + 'version' => array('stability', 'license', 'notes', 'contents', 'compatible', + 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', + 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease', + 'extbinrelease', 'bundle', 'changelog'), + 'release' => array('api'))); + $this->_isValid = 0; + } + + function setAPIVersion($version) + { + if (isset($this->_packageInfo['version']) && + isset($this->_packageInfo['version']['api'])) { + unset($this->_packageInfo['version']['api']); + } + $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $version, array( + 'version' => array('stability', 'license', 'notes', 'contents', 'compatible', + 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', + 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease', + 'extbinrelease', 'bundle', 'changelog'), + 'api' => array())); + $this->_isValid = 0; + } + + /** + * snapshot|devel|alpha|beta|stable + */ + function setReleaseStability($state) + { + if (isset($this->_packageInfo['stability']) && + isset($this->_packageInfo['stability']['release'])) { + unset($this->_packageInfo['stability']['release']); + } + $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $state, array( + 'stability' => array('license', 'notes', 'contents', 'compatible', + 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', + 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease', + 'extbinrelease', 'bundle', 'changelog'), + 'release' => array('api'))); + $this->_isValid = 0; + } + + /** + * @param devel|alpha|beta|stable + */ + function setAPIStability($state) + { + if (isset($this->_packageInfo['stability']) && + isset($this->_packageInfo['stability']['api'])) { + unset($this->_packageInfo['stability']['api']); + } + $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $state, array( + 'stability' => array('license', 'notes', 'contents', 'compatible', + 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', + 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease', + 'extbinrelease', 'bundle', 'changelog'), + 'api' => array())); + $this->_isValid = 0; + } + + function setLicense($license, $uri = false, $filesource = false) + { + if (!isset($this->_packageInfo['license'])) { + // ensure that the license tag is set up in the right location + $this->_packageInfo = $this->_insertBefore($this->_packageInfo, + array('notes', 'contents', 'compatible', + 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', + 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease', + 'extbinrelease', 'bundle', 'changelog'), 0, 'license'); + } + if ($uri || $filesource) { + $attribs = array(); + if ($uri) { + $attribs['uri'] = $uri; + } + $uri = true; // for test below + if ($filesource) { + $attribs['filesource'] = $filesource; + } + } + $license = $uri ? array('attribs' => $attribs, '_content' => $license) : $license; + $this->_packageInfo['license'] = $license; + $this->_isValid = 0; + } + + function setNotes($notes) + { + $this->_isValid = 0; + if (!isset($this->_packageInfo['notes'])) { + // ensure that the notes tag is set up in the right location + $this->_packageInfo = $this->_insertBefore($this->_packageInfo, + array('contents', 'compatible', + 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', + 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease', + 'extbinrelease', 'bundle', 'changelog'), $notes, 'notes'); + } + $this->_packageInfo['notes'] = $notes; + } + + /** + * This is only used at install-time, after all serialization + * is over. + * @param string file name + * @param string installed path + */ + function setInstalledAs($file, $path) + { + if ($path) { + return $this->_packageInfo['filelist'][$file]['installed_as'] = $path; + } + unset($this->_packageInfo['filelist'][$file]['installed_as']); + } + + /** + * This is only used at install-time, after all serialization + * is over. + */ + function installedFile($file, $atts) + { + if (isset($this->_packageInfo['filelist'][$file])) { + $this->_packageInfo['filelist'][$file] = + array_merge($this->_packageInfo['filelist'][$file], $atts['attribs']); + } else { + $this->_packageInfo['filelist'][$file] = $atts['attribs']; + } + } + + /** + * Reset the listing of package contents + * @param string base installation dir for the whole package, if any + */ + function clearContents($baseinstall = false) + { + $this->_filesValid = false; + $this->_isValid = 0; + if (!isset($this->_packageInfo['contents'])) { + $this->_packageInfo = $this->_insertBefore($this->_packageInfo, + array('compatible', + 'dependencies', 'providesextension', 'usesrole', 'usestask', + 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', + 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', + 'bundle', 'changelog'), array(), 'contents'); + } + if ($this->getPackageType() != 'bundle') { + $this->_packageInfo['contents'] = + array('dir' => array('attribs' => array('name' => '/'))); + if ($baseinstall) { + $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'] = $baseinstall; + } + } else { + $this->_packageInfo['contents'] = array('bundledpackage' => array()); + } + } + + /** + * @param string relative path of the bundled package. + */ + function addBundledPackage($path) + { + if ($this->getPackageType() != 'bundle') { + return false; + } + $this->_filesValid = false; + $this->_isValid = 0; + $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $path, array( + 'contents' => array('compatible', 'dependencies', 'providesextension', + 'usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease', + 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', + 'bundle', 'changelog'), + 'bundledpackage' => array())); + } + + /** + * @param string file name + * @param PEAR_Task_Common a read/write task + */ + function addTaskToFile($filename, $task) + { + if (!method_exists($task, 'getXml')) { + return false; + } + if (!method_exists($task, 'getName')) { + return false; + } + if (!method_exists($task, 'validate')) { + return false; + } + if (!$task->validate()) { + return false; + } + if (!isset($this->_packageInfo['contents']['dir']['file'])) { + return false; + } + $this->getTasksNs(); // discover the tasks namespace if not done already + $files = $this->_packageInfo['contents']['dir']['file']; + if (!isset($files[0])) { + $files = array($files); + $ind = false; + } else { + $ind = true; + } + foreach ($files as $i => $file) { + if (isset($file['attribs'])) { + if ($file['attribs']['name'] == $filename) { + if ($ind) { + $t = isset($this->_packageInfo['contents']['dir']['file'][$i] + ['attribs'][$this->_tasksNs . + ':' . $task->getName()]) ? + $this->_packageInfo['contents']['dir']['file'][$i] + ['attribs'][$this->_tasksNs . + ':' . $task->getName()] : false; + if ($t && !isset($t[0])) { + $this->_packageInfo['contents']['dir']['file'][$i] + [$this->_tasksNs . ':' . $task->getName()] = array($t); + } + $this->_packageInfo['contents']['dir']['file'][$i][$this->_tasksNs . + ':' . $task->getName()][] = $task->getXml(); + } else { + $t = isset($this->_packageInfo['contents']['dir']['file'] + ['attribs'][$this->_tasksNs . + ':' . $task->getName()]) ? $this->_packageInfo['contents']['dir']['file'] + ['attribs'][$this->_tasksNs . + ':' . $task->getName()] : false; + if ($t && !isset($t[0])) { + $this->_packageInfo['contents']['dir']['file'] + [$this->_tasksNs . ':' . $task->getName()] = array($t); + } + $this->_packageInfo['contents']['dir']['file'][$this->_tasksNs . + ':' . $task->getName()][] = $task->getXml(); + } + return true; + } + } + } + return false; + } + + /** + * @param string path to the file + * @param string filename + * @param array extra attributes + */ + function addFile($dir, $file, $attrs) + { + if ($this->getPackageType() == 'bundle') { + return false; + } + $this->_filesValid = false; + $this->_isValid = 0; + $dir = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'), $dir); + if ($dir == '/' || $dir == '') { + $dir = ''; + } else { + $dir .= '/'; + } + $attrs['name'] = $dir . $file; + if (!isset($this->_packageInfo['contents'])) { + // ensure that the contents tag is set up + $this->_packageInfo = $this->_insertBefore($this->_packageInfo, + array('compatible', 'dependencies', 'providesextension', 'usesrole', 'usestask', + 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', + 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', + 'bundle', 'changelog'), array(), 'contents'); + } + if (isset($this->_packageInfo['contents']['dir']['file'])) { + if (!isset($this->_packageInfo['contents']['dir']['file'][0])) { + $this->_packageInfo['contents']['dir']['file'] = + array($this->_packageInfo['contents']['dir']['file']); + } + $this->_packageInfo['contents']['dir']['file'][]['attribs'] = $attrs; + } else { + $this->_packageInfo['contents']['dir']['file']['attribs'] = $attrs; + } + } + + /** + * @param string Dependent package name + * @param string Dependent package's channel name + * @param string minimum version of specified package that this release is guaranteed to be + * compatible with + * @param string maximum version of specified package that this release is guaranteed to be + * compatible with + * @param string versions of specified package that this release is not compatible with + */ + function addCompatiblePackage($name, $channel, $min, $max, $exclude = false) + { + $this->_isValid = 0; + $set = array( + 'name' => $name, + 'channel' => $channel, + 'min' => $min, + 'max' => $max, + ); + if ($exclude) { + $set['exclude'] = $exclude; + } + $this->_isValid = 0; + $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $set, array( + 'compatible' => array('dependencies', 'providesextension', 'usesrole', 'usestask', + 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', + 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog') + )); + } + + /** + * Removes the tag entirely + */ + function resetUsesrole() + { + if (isset($this->_packageInfo['usesrole'])) { + unset($this->_packageInfo['usesrole']); + } + } + + /** + * @param string + * @param string package name or uri + * @param string channel name if non-uri + */ + function addUsesrole($role, $packageOrUri, $channel = false) { + $set = array('role' => $role); + if ($channel) { + $set['package'] = $packageOrUri; + $set['channel'] = $channel; + } else { + $set['uri'] = $packageOrUri; + } + $this->_isValid = 0; + $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $set, array( + 'usesrole' => array('usestask', 'srcpackage', 'srcuri', + 'phprelease', 'extsrcrelease', 'extbinrelease', + 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog') + )); + } + + /** + * Removes the tag entirely + */ + function resetUsestask() + { + if (isset($this->_packageInfo['usestask'])) { + unset($this->_packageInfo['usestask']); + } + } + + + /** + * @param string + * @param string package name or uri + * @param string channel name if non-uri + */ + function addUsestask($task, $packageOrUri, $channel = false) { + $set = array('task' => $task); + if ($channel) { + $set['package'] = $packageOrUri; + $set['channel'] = $channel; + } else { + $set['uri'] = $packageOrUri; + } + $this->_isValid = 0; + $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $set, array( + 'usestask' => array('srcpackage', 'srcuri', + 'phprelease', 'extsrcrelease', 'extbinrelease', + 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog') + )); + } + + /** + * Remove all compatible tags + */ + function clearCompatible() + { + unset($this->_packageInfo['compatible']); + } + + /** + * Reset dependencies prior to adding new ones + */ + function clearDeps() + { + if (!isset($this->_packageInfo['dependencies'])) { + $this->_packageInfo = $this->_mergeTag($this->_packageInfo, array(), + array( + 'dependencies' => array('providesextension', 'usesrole', 'usestask', + 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', + 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'))); + } + $this->_packageInfo['dependencies'] = array(); + } + + /** + * @param string minimum PHP version allowed + * @param string maximum PHP version allowed + * @param array $exclude incompatible PHP versions + */ + function setPhpDep($min, $max = false, $exclude = false) + { + $this->_isValid = 0; + $dep = + array( + 'min' => $min, + ); + if ($max) { + $dep['max'] = $max; + } + if ($exclude) { + if (count($exclude) == 1) { + $exclude = $exclude[0]; + } + $dep['exclude'] = $exclude; + } + if (isset($this->_packageInfo['dependencies']['required']['php'])) { + $this->_stack->push(__FUNCTION__, 'warning', array('dep' => + $this->_packageInfo['dependencies']['required']['php']), + 'warning: PHP dependency already exists, overwriting'); + unset($this->_packageInfo['dependencies']['required']['php']); + } + $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep, + array( + 'dependencies' => array('providesextension', 'usesrole', 'usestask', + 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', + 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), + 'required' => array('optional', 'group'), + 'php' => array('pearinstaller', 'package', 'subpackage', 'extension', 'os', 'arch') + )); + return true; + } + + /** + * @param string minimum allowed PEAR installer version + * @param string maximum allowed PEAR installer version + * @param string recommended PEAR installer version + * @param array incompatible version of the PEAR installer + */ + function setPearinstallerDep($min, $max = false, $recommended = false, $exclude = false) + { + $this->_isValid = 0; + $dep = + array( + 'min' => $min, + ); + if ($max) { + $dep['max'] = $max; + } + if ($recommended) { + $dep['recommended'] = $recommended; + } + if ($exclude) { + if (count($exclude) == 1) { + $exclude = $exclude[0]; + } + $dep['exclude'] = $exclude; + } + if (isset($this->_packageInfo['dependencies']['required']['pearinstaller'])) { + $this->_stack->push(__FUNCTION__, 'warning', array('dep' => + $this->_packageInfo['dependencies']['required']['pearinstaller']), + 'warning: PEAR Installer dependency already exists, overwriting'); + unset($this->_packageInfo['dependencies']['required']['pearinstaller']); + } + $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep, + array( + 'dependencies' => array('providesextension', 'usesrole', 'usestask', + 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', + 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), + 'required' => array('optional', 'group'), + 'pearinstaller' => array('package', 'subpackage', 'extension', 'os', 'arch') + )); + } + + /** + * Mark a package as conflicting with this package + * @param string package name + * @param string package channel + * @param string extension this package provides, if any + * @param string|false minimum version required + * @param string|false maximum version allowed + * @param array|false versions to exclude from installation + */ + function addConflictingPackageDepWithChannel($name, $channel, + $providesextension = false, $min = false, $max = false, $exclude = false) + { + $this->_isValid = 0; + $dep = $this->_constructDep($name, $channel, false, $min, $max, false, + $exclude, $providesextension, false, true); + $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep, + array( + 'dependencies' => array('providesextension', 'usesrole', 'usestask', + 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', + 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), + 'required' => array('optional', 'group'), + 'package' => array('subpackage', 'extension', 'os', 'arch') + )); + } + + /** + * Mark a package as conflicting with this package + * @param string package name + * @param string package channel + * @param string extension this package provides, if any + */ + function addConflictingPackageDepWithUri($name, $uri, $providesextension = false) + { + $this->_isValid = 0; + $dep = + array( + 'name' => $name, + 'uri' => $uri, + 'conflicts' => '', + ); + if ($providesextension) { + $dep['providesextension'] = $providesextension; + } + $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep, + array( + 'dependencies' => array('providesextension', 'usesrole', 'usestask', + 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', + 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), + 'required' => array('optional', 'group'), + 'package' => array('subpackage', 'extension', 'os', 'arch') + )); + } + + function addDependencyGroup($name, $hint) + { + $this->_isValid = 0; + $this->_packageInfo = $this->_mergeTag($this->_packageInfo, + array('attribs' => array('name' => $name, 'hint' => $hint)), + array( + 'dependencies' => array('providesextension', 'usesrole', 'usestask', + 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', + 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), + 'group' => array(), + )); + } + + /** + * @param string package name + * @param string|false channel name, false if this is a uri + * @param string|false uri name, false if this is a channel + * @param string|false minimum version required + * @param string|false maximum version allowed + * @param string|false recommended installation version + * @param array|false versions to exclude from installation + * @param string extension this package provides, if any + * @param bool if true, tells the installer to ignore the default optional dependency group + * when installing this package + * @param bool if true, tells the installer to negate this dependency (conflicts) + * @return array + * @access private + */ + function _constructDep($name, $channel, $uri, $min, $max, $recommended, $exclude, + $providesextension = false, $nodefault = false, + $conflicts = false) + { + $dep = + array( + 'name' => $name, + ); + if ($channel) { + $dep['channel'] = $channel; + } elseif ($uri) { + $dep['uri'] = $uri; + } + if ($min) { + $dep['min'] = $min; + } + if ($max) { + $dep['max'] = $max; + } + if ($recommended) { + $dep['recommended'] = $recommended; + } + if ($exclude) { + if (is_array($exclude) && count($exclude) == 1) { + $exclude = $exclude[0]; + } + $dep['exclude'] = $exclude; + } + if ($conflicts) { + $dep['conflicts'] = ''; + } + if ($nodefault) { + $dep['nodefault'] = ''; + } + if ($providesextension) { + $dep['providesextension'] = $providesextension; + } + return $dep; + } + + /** + * @param package|subpackage + * @param string group name + * @param string package name + * @param string package channel + * @param string minimum version + * @param string maximum version + * @param string recommended version + * @param array|false optional excluded versions + * @param string extension this package provides, if any + * @param bool if true, tells the installer to ignore the default optional dependency group + * when installing this package + * @return bool false if the dependency group has not been initialized with + * {@link addDependencyGroup()}, or a subpackage is added with + * a providesextension + */ + function addGroupPackageDepWithChannel($type, $groupname, $name, $channel, $min = false, + $max = false, $recommended = false, $exclude = false, + $providesextension = false, $nodefault = false) + { + if ($type == 'subpackage' && $providesextension) { + return false; // subpackages must be php packages + } + $dep = $this->_constructDep($name, $channel, false, $min, $max, $recommended, $exclude, + $providesextension, $nodefault); + return $this->_addGroupDependency($type, $dep, $groupname); + } + + /** + * @param package|subpackage + * @param string group name + * @param string package name + * @param string package uri + * @param string extension this package provides, if any + * @param bool if true, tells the installer to ignore the default optional dependency group + * when installing this package + * @return bool false if the dependency group has not been initialized with + * {@link addDependencyGroup()} + */ + function addGroupPackageDepWithURI($type, $groupname, $name, $uri, $providesextension = false, + $nodefault = false) + { + if ($type == 'subpackage' && $providesextension) { + return false; // subpackages must be php packages + } + $dep = $this->_constructDep($name, false, $uri, false, false, false, false, + $providesextension, $nodefault); + return $this->_addGroupDependency($type, $dep, $groupname); + } + + /** + * @param string group name (must be pre-existing) + * @param string extension name + * @param string minimum version allowed + * @param string maximum version allowed + * @param string recommended version + * @param array incompatible versions + */ + function addGroupExtensionDep($groupname, $name, $min = false, $max = false, + $recommended = false, $exclude = false) + { + $this->_isValid = 0; + $dep = $this->_constructDep($name, false, false, $min, $max, $recommended, $exclude); + return $this->_addGroupDependency('extension', $dep, $groupname); + } + + /** + * @param package|subpackage|extension + * @param array dependency contents + * @param string name of the dependency group to add this to + * @return boolean + * @access private + */ + function _addGroupDependency($type, $dep, $groupname) + { + $arr = array('subpackage', 'extension'); + if ($type != 'package') { + array_shift($arr); + } + if ($type == 'extension') { + array_shift($arr); + } + if (!isset($this->_packageInfo['dependencies']['group'])) { + return false; + } else { + if (!isset($this->_packageInfo['dependencies']['group'][0])) { + if ($this->_packageInfo['dependencies']['group']['attribs']['name'] == $groupname) { + $this->_packageInfo['dependencies']['group'] = $this->_mergeTag( + $this->_packageInfo['dependencies']['group'], $dep, + array( + $type => $arr + )); + $this->_isValid = 0; + return true; + } else { + return false; + } + } else { + foreach ($this->_packageInfo['dependencies']['group'] as $i => $group) { + if ($group['attribs']['name'] == $groupname) { + $this->_packageInfo['dependencies']['group'][$i] = $this->_mergeTag( + $this->_packageInfo['dependencies']['group'][$i], $dep, + array( + $type => $arr + )); + $this->_isValid = 0; + return true; + } + } + return false; + } + } + } + + /** + * @param optional|required + * @param string package name + * @param string package channel + * @param string minimum version + * @param string maximum version + * @param string recommended version + * @param string extension this package provides, if any + * @param bool if true, tells the installer to ignore the default optional dependency group + * when installing this package + * @param array|false optional excluded versions + */ + function addPackageDepWithChannel($type, $name, $channel, $min = false, $max = false, + $recommended = false, $exclude = false, + $providesextension = false, $nodefault = false) + { + if (!in_array($type, array('optional', 'required'), true)) { + $type = 'required'; + } + $this->_isValid = 0; + $arr = array('optional', 'group'); + if ($type != 'required') { + array_shift($arr); + } + $dep = $this->_constructDep($name, $channel, false, $min, $max, $recommended, $exclude, + $providesextension, $nodefault); + $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep, + array( + 'dependencies' => array('providesextension', 'usesrole', 'usestask', + 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', + 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), + $type => $arr, + 'package' => array('subpackage', 'extension', 'os', 'arch') + )); + } + + /** + * @param optional|required + * @param string name of the package + * @param string uri of the package + * @param string extension this package provides, if any + * @param bool if true, tells the installer to ignore the default optional dependency group + * when installing this package + */ + function addPackageDepWithUri($type, $name, $uri, $providesextension = false, + $nodefault = false) + { + $this->_isValid = 0; + $arr = array('optional', 'group'); + if ($type != 'required') { + array_shift($arr); + } + $dep = $this->_constructDep($name, false, $uri, false, false, false, false, + $providesextension, $nodefault); + $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep, + array( + 'dependencies' => array('providesextension', 'usesrole', 'usestask', + 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', + 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), + $type => $arr, + 'package' => array('subpackage', 'extension', 'os', 'arch') + )); + } + + /** + * @param optional|required optional, required + * @param string package name + * @param string package channel + * @param string minimum version + * @param string maximum version + * @param string recommended version + * @param array incompatible versions + * @param bool if true, tells the installer to ignore the default optional dependency group + * when installing this package + */ + function addSubpackageDepWithChannel($type, $name, $channel, $min = false, $max = false, + $recommended = false, $exclude = false, + $nodefault = false) + { + $this->_isValid = 0; + $arr = array('optional', 'group'); + if ($type != 'required') { + array_shift($arr); + } + $dep = $this->_constructDep($name, $channel, false, $min, $max, $recommended, $exclude, + $nodefault); + $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep, + array( + 'dependencies' => array('providesextension', 'usesrole', 'usestask', + 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', + 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), + $type => $arr, + 'subpackage' => array('extension', 'os', 'arch') + )); + } + + /** + * @param optional|required optional, required + * @param string package name + * @param string package uri for download + * @param bool if true, tells the installer to ignore the default optional dependency group + * when installing this package + */ + function addSubpackageDepWithUri($type, $name, $uri, $nodefault = false) + { + $this->_isValid = 0; + $arr = array('optional', 'group'); + if ($type != 'required') { + array_shift($arr); + } + $dep = $this->_constructDep($name, false, $uri, false, false, false, false, $nodefault); + $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep, + array( + 'dependencies' => array('providesextension', 'usesrole', 'usestask', + 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', + 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), + $type => $arr, + 'subpackage' => array('extension', 'os', 'arch') + )); + } + + /** + * @param optional|required optional, required + * @param string extension name + * @param string minimum version + * @param string maximum version + * @param string recommended version + * @param array incompatible versions + */ + function addExtensionDep($type, $name, $min = false, $max = false, $recommended = false, + $exclude = false) + { + $this->_isValid = 0; + $arr = array('optional', 'group'); + if ($type != 'required') { + array_shift($arr); + } + $dep = $this->_constructDep($name, false, false, $min, $max, $recommended, $exclude); + $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep, + array( + 'dependencies' => array('providesextension', 'usesrole', 'usestask', + 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', + 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), + $type => $arr, + 'extension' => array('os', 'arch') + )); + } + + /** + * @param string Operating system name + * @param boolean true if this package cannot be installed on this OS + */ + function addOsDep($name, $conflicts = false) + { + $this->_isValid = 0; + $dep = array('name' => $name); + if ($conflicts) { + $dep['conflicts'] = ''; + } + $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep, + array( + 'dependencies' => array('providesextension', 'usesrole', 'usestask', + 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', + 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), + 'required' => array('optional', 'group'), + 'os' => array('arch') + )); + } + + /** + * @param string Architecture matching pattern + * @param boolean true if this package cannot be installed on this architecture + */ + function addArchDep($pattern, $conflicts = false) + { + $this->_isValid = 0; + $dep = array('pattern' => $pattern); + if ($conflicts) { + $dep['conflicts'] = ''; + } + $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep, + array( + 'dependencies' => array('providesextension', 'usesrole', 'usestask', + 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', + 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), + 'required' => array('optional', 'group'), + 'arch' => array() + )); + } + + /** + * Set the kind of package, and erase all release tags + * + * - a php package is a PEAR-style package + * - an extbin package is a PECL-style extension binary + * - an extsrc package is a PECL-style source for a binary + * - an zendextbin package is a PECL-style zend extension binary + * - an zendextsrc package is a PECL-style source for a zend extension binary + * - a bundle package is a collection of other pre-packaged packages + * @param php|extbin|extsrc|zendextsrc|zendextbin|bundle + * @return bool success + */ + function setPackageType($type) + { + $this->_isValid = 0; + if (!in_array($type, array('php', 'extbin', 'extsrc', 'zendextsrc', + 'zendextbin', 'bundle'))) { + return false; + } + + if (in_array($type, array('zendextsrc', 'zendextbin'))) { + $this->_setPackageVersion2_1(); + } + + if ($type != 'bundle') { + $type .= 'release'; + } + + foreach (array('phprelease', 'extbinrelease', 'extsrcrelease', + 'zendextsrcrelease', 'zendextbinrelease', 'bundle') as $test) { + unset($this->_packageInfo[$test]); + } + + if (!isset($this->_packageInfo[$type])) { + // ensure that the release tag is set up + $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('changelog'), + array(), $type); + } + + $this->_packageInfo[$type] = array(); + return true; + } + + /** + * @return bool true if package type is set up + */ + function addRelease() + { + if ($type = $this->getPackageType()) { + if ($type != 'bundle') { + $type .= 'release'; + } + $this->_packageInfo = $this->_mergeTag($this->_packageInfo, array(), + array($type => array('changelog'))); + return true; + } + return false; + } + + /** + * Get the current release tag in order to add to it + * @param bool returns only releases that have installcondition if true + * @return array|null + */ + function &_getCurrentRelease($strict = true) + { + if ($p = $this->getPackageType()) { + if ($strict) { + if ($p == 'extsrc' || $p == 'zendextsrc') { + $a = null; + return $a; + } + } + if ($p != 'bundle') { + $p .= 'release'; + } + if (isset($this->_packageInfo[$p][0])) { + return $this->_packageInfo[$p][count($this->_packageInfo[$p]) - 1]; + } else { + return $this->_packageInfo[$p]; + } + } else { + $a = null; + return $a; + } + } + + /** + * Add a file to the current release that should be installed under a different name + * @param string path to file + * @param string name the file should be installed as + */ + function addInstallAs($path, $as) + { + $r = &$this->_getCurrentRelease(); + if ($r === null) { + return false; + } + $this->_isValid = 0; + $r = $this->_mergeTag($r, array('attribs' => array('name' => $path, 'as' => $as)), + array( + 'filelist' => array(), + 'install' => array('ignore') + )); + } + + /** + * Add a file to the current release that should be ignored + * @param string path to file + * @return bool success of operation + */ + function addIgnore($path) + { + $r = &$this->_getCurrentRelease(); + if ($r === null) { + return false; + } + $this->_isValid = 0; + $r = $this->_mergeTag($r, array('attribs' => array('name' => $path)), + array( + 'filelist' => array(), + 'ignore' => array() + )); + } + + /** + * Add an extension binary package for this extension source code release + * + * Note that the package must be from the same channel as the extension source package + * @param string + */ + function addBinarypackage($package) + { + if ($this->getPackageType() != 'extsrc' && $this->getPackageType() != 'zendextsrc') { + return false; + } + $r = &$this->_getCurrentRelease(false); + if ($r === null) { + return false; + } + $this->_isValid = 0; + $r = $this->_mergeTag($r, $package, + array( + 'binarypackage' => array('filelist'), + )); + } + + /** + * Add a configureoption to an extension source package + * @param string + * @param string + * @param string + */ + function addConfigureOption($name, $prompt, $default = null) + { + if ($this->getPackageType() != 'extsrc' && $this->getPackageType() != 'zendextsrc') { + return false; + } + + $r = &$this->_getCurrentRelease(false); + if ($r === null) { + return false; + } + + $opt = array('attribs' => array('name' => $name, 'prompt' => $prompt)); + if ($default !== null) { + $opt['attribs']['default'] = $default; + } + + $this->_isValid = 0; + $r = $this->_mergeTag($r, $opt, + array( + 'configureoption' => array('binarypackage', 'filelist'), + )); + } + + /** + * Set an installation condition based on php version for the current release set + * @param string minimum version + * @param string maximum version + * @param false|array incompatible versions of PHP + */ + function setPhpInstallCondition($min, $max, $exclude = false) + { + $r = &$this->_getCurrentRelease(); + if ($r === null) { + return false; + } + $this->_isValid = 0; + if (isset($r['installconditions']['php'])) { + unset($r['installconditions']['php']); + } + $dep = array('min' => $min, 'max' => $max); + if ($exclude) { + if (is_array($exclude) && count($exclude) == 1) { + $exclude = $exclude[0]; + } + $dep['exclude'] = $exclude; + } + if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') { + $r = $this->_mergeTag($r, $dep, + array( + 'installconditions' => array('configureoption', 'binarypackage', + 'filelist'), + 'php' => array('extension', 'os', 'arch') + )); + } else { + $r = $this->_mergeTag($r, $dep, + array( + 'installconditions' => array('filelist'), + 'php' => array('extension', 'os', 'arch') + )); + } + } + + /** + * @param optional|required optional, required + * @param string extension name + * @param string minimum version + * @param string maximum version + * @param string recommended version + * @param array incompatible versions + */ + function addExtensionInstallCondition($name, $min = false, $max = false, $recommended = false, + $exclude = false) + { + $r = &$this->_getCurrentRelease(); + if ($r === null) { + return false; + } + $this->_isValid = 0; + $dep = $this->_constructDep($name, false, false, $min, $max, $recommended, $exclude); + if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') { + $r = $this->_mergeTag($r, $dep, + array( + 'installconditions' => array('configureoption', 'binarypackage', + 'filelist'), + 'extension' => array('os', 'arch') + )); + } else { + $r = $this->_mergeTag($r, $dep, + array( + 'installconditions' => array('filelist'), + 'extension' => array('os', 'arch') + )); + } + } + + /** + * Set an installation condition based on operating system for the current release set + * @param string OS name + * @param bool whether this OS is incompatible with the current release + */ + function setOsInstallCondition($name, $conflicts = false) + { + $r = &$this->_getCurrentRelease(); + if ($r === null) { + return false; + } + $this->_isValid = 0; + if (isset($r['installconditions']['os'])) { + unset($r['installconditions']['os']); + } + $dep = array('name' => $name); + if ($conflicts) { + $dep['conflicts'] = ''; + } + if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') { + $r = $this->_mergeTag($r, $dep, + array( + 'installconditions' => array('configureoption', 'binarypackage', + 'filelist'), + 'os' => array('arch') + )); + } else { + $r = $this->_mergeTag($r, $dep, + array( + 'installconditions' => array('filelist'), + 'os' => array('arch') + )); + } + } + + /** + * Set an installation condition based on architecture for the current release set + * @param string architecture pattern + * @param bool whether this arch is incompatible with the current release + */ + function setArchInstallCondition($pattern, $conflicts = false) + { + $r = &$this->_getCurrentRelease(); + if ($r === null) { + return false; + } + $this->_isValid = 0; + if (isset($r['installconditions']['arch'])) { + unset($r['installconditions']['arch']); + } + $dep = array('pattern' => $pattern); + if ($conflicts) { + $dep['conflicts'] = ''; + } + if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') { + $r = $this->_mergeTag($r, $dep, + array( + 'installconditions' => array('configureoption', 'binarypackage', + 'filelist'), + 'arch' => array() + )); + } else { + $r = $this->_mergeTag($r, $dep, + array( + 'installconditions' => array('filelist'), + 'arch' => array() + )); + } + } + + /** + * For extension binary releases, this is used to specify either the + * static URI to a source package, or the package name and channel of the extsrc/zendextsrc + * package it is based on. + * @param string Package name, or full URI to source package (extsrc/zendextsrc type) + */ + function setSourcePackage($packageOrUri) + { + $this->_isValid = 0; + if (isset($this->_packageInfo['channel'])) { + $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('phprelease', + 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', + 'bundle', 'changelog'), + $packageOrUri, 'srcpackage'); + } else { + $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('phprelease', + 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', + 'bundle', 'changelog'), $packageOrUri, 'srcuri'); + } + } + + /** + * Generate a valid change log entry from the current package.xml + * @param string|false + */ + function generateChangeLogEntry($notes = false) + { + return array( + 'version' => + array( + 'release' => $this->getVersion('release'), + 'api' => $this->getVersion('api'), + ), + 'stability' => + $this->getStability(), + 'date' => $this->getDate(), + 'license' => $this->getLicense(true), + 'notes' => $notes ? $notes : $this->getNotes() + ); + } + + /** + * @param string release version to set change log notes for + * @param array output of {@link generateChangeLogEntry()} + */ + function setChangelogEntry($releaseversion, $contents) + { + if (!isset($this->_packageInfo['changelog'])) { + $this->_packageInfo['changelog']['release'] = $contents; + return; + } + if (!isset($this->_packageInfo['changelog']['release'][0])) { + if ($this->_packageInfo['changelog']['release']['version']['release'] == $releaseversion) { + $this->_packageInfo['changelog']['release'] = array( + $this->_packageInfo['changelog']['release']); + } else { + $this->_packageInfo['changelog']['release'] = array( + $this->_packageInfo['changelog']['release']); + return $this->_packageInfo['changelog']['release'][] = $contents; + } + } + foreach($this->_packageInfo['changelog']['release'] as $index => $changelog) { + if (isset($changelog['version']) && + strnatcasecmp($changelog['version']['release'], $releaseversion) == 0) { + $curlog = $index; + } + } + if (isset($curlog)) { + $this->_packageInfo['changelog']['release'][$curlog] = $contents; + } else { + $this->_packageInfo['changelog']['release'][] = $contents; + } + } + + /** + * Remove the changelog entirely + */ + function clearChangeLog() + { + unset($this->_packageInfo['changelog']); + } +} \ No newline at end of file diff --git a/includes/pear/PEAR/Packager.php b/includes/pear/PEAR/Packager.php new file mode 100644 index 0000000..057a714 --- /dev/null +++ b/includes/pear/PEAR/Packager.php @@ -0,0 +1,201 @@ + + * @author Tomas V. V. Cox + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 0.1 + */ + +/** + * base class + */ +require_once 'PEAR/Common.php'; +require_once 'PEAR/PackageFile.php'; +require_once 'System.php'; + +/** + * Administration class used to make a PEAR release tarball. + * + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 0.1 + */ +class PEAR_Packager extends PEAR_Common +{ + /** + * @var PEAR_Registry + */ + var $_registry; + + function package($pkgfile = null, $compress = true, $pkg2 = null) + { + // {{{ validate supplied package.xml file + if (empty($pkgfile)) { + $pkgfile = 'package.xml'; + } + + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $pkg = &new PEAR_PackageFile($this->config, $this->debug); + $pf = &$pkg->fromPackageFile($pkgfile, PEAR_VALIDATE_NORMAL); + $main = &$pf; + PEAR::staticPopErrorHandling(); + if (PEAR::isError($pf)) { + if (is_array($pf->getUserInfo())) { + foreach ($pf->getUserInfo() as $error) { + $this->log(0, 'Error: ' . $error['message']); + } + } + + $this->log(0, $pf->getMessage()); + return $this->raiseError("Cannot package, errors in package file"); + } + + foreach ($pf->getValidationWarnings() as $warning) { + $this->log(1, 'Warning: ' . $warning['message']); + } + + // }}} + if ($pkg2) { + $this->log(0, 'Attempting to process the second package file'); + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $pf2 = &$pkg->fromPackageFile($pkg2, PEAR_VALIDATE_NORMAL); + PEAR::staticPopErrorHandling(); + if (PEAR::isError($pf2)) { + if (is_array($pf2->getUserInfo())) { + foreach ($pf2->getUserInfo() as $error) { + $this->log(0, 'Error: ' . $error['message']); + } + } + $this->log(0, $pf2->getMessage()); + return $this->raiseError("Cannot package, errors in second package file"); + } + + foreach ($pf2->getValidationWarnings() as $warning) { + $this->log(1, 'Warning: ' . $warning['message']); + } + + if ($pf2->getPackagexmlVersion() == '2.0' || + $pf2->getPackagexmlVersion() == '2.1' + ) { + $main = &$pf2; + $other = &$pf; + } else { + $main = &$pf; + $other = &$pf2; + } + + if ($main->getPackagexmlVersion() != '2.0' && + $main->getPackagexmlVersion() != '2.1') { + return PEAR::raiseError('Error: cannot package two package.xml version 1.0, can ' . + 'only package together a package.xml 1.0 and package.xml 2.0'); + } + + if ($other->getPackagexmlVersion() != '1.0') { + return PEAR::raiseError('Error: cannot package two package.xml version 2.0, can ' . + 'only package together a package.xml 1.0 and package.xml 2.0'); + } + } + + $main->setLogger($this); + if (!$main->validate(PEAR_VALIDATE_PACKAGING)) { + foreach ($main->getValidationWarnings() as $warning) { + $this->log(0, 'Error: ' . $warning['message']); + } + return $this->raiseError("Cannot package, errors in package"); + } + + foreach ($main->getValidationWarnings() as $warning) { + $this->log(1, 'Warning: ' . $warning['message']); + } + + if ($pkg2) { + $other->setLogger($this); + $a = false; + if (!$other->validate(PEAR_VALIDATE_NORMAL) || $a = !$main->isEquivalent($other)) { + foreach ($other->getValidationWarnings() as $warning) { + $this->log(0, 'Error: ' . $warning['message']); + } + + foreach ($main->getValidationWarnings() as $warning) { + $this->log(0, 'Error: ' . $warning['message']); + } + + if ($a) { + return $this->raiseError('The two package.xml files are not equivalent!'); + } + + return $this->raiseError("Cannot package, errors in package"); + } + + foreach ($other->getValidationWarnings() as $warning) { + $this->log(1, 'Warning: ' . $warning['message']); + } + + $gen = &$main->getDefaultGenerator(); + $tgzfile = $gen->toTgz2($this, $other, $compress); + if (PEAR::isError($tgzfile)) { + return $tgzfile; + } + + $dest_package = basename($tgzfile); + $pkgdir = dirname($pkgfile); + + // TAR the Package ------------------------------------------- + $this->log(1, "Package $dest_package done"); + if (file_exists("$pkgdir/CVS/Root")) { + $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $pf->getVersion()); + $cvstag = "RELEASE_$cvsversion"; + $this->log(1, 'Tag the released code with "pear cvstag ' . + $main->getPackageFile() . '"'); + $this->log(1, "(or set the CVS tag $cvstag by hand)"); + } elseif (file_exists("$pkgdir/.svn")) { + $svnversion = preg_replace('/[^a-z0-9]/i', '.', $pf->getVersion()); + $svntag = $pf->getName() . "-$svnversion"; + $this->log(1, 'Tag the released code with "pear svntag ' . + $main->getPackageFile() . '"'); + $this->log(1, "(or set the SVN tag $svntag by hand)"); + } + } else { // this branch is executed for single packagefile packaging + $gen = &$pf->getDefaultGenerator(); + $tgzfile = $gen->toTgz($this, $compress); + if (PEAR::isError($tgzfile)) { + $this->log(0, $tgzfile->getMessage()); + return $this->raiseError("Cannot package, errors in package"); + } + + $dest_package = basename($tgzfile); + $pkgdir = dirname($pkgfile); + + // TAR the Package ------------------------------------------- + $this->log(1, "Package $dest_package done"); + if (file_exists("$pkgdir/CVS/Root")) { + $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $pf->getVersion()); + $cvstag = "RELEASE_$cvsversion"; + $this->log(1, "Tag the released code with `pear cvstag $pkgfile'"); + $this->log(1, "(or set the CVS tag $cvstag by hand)"); + } elseif (file_exists("$pkgdir/.svn")) { + $svnversion = preg_replace('/[^a-z0-9]/i', '.', $pf->getVersion()); + $svntag = $pf->getName() . "-$svnversion"; + $this->log(1, "Tag the released code with `pear svntag $pkgfile'"); + $this->log(1, "(or set the SVN tag $svntag by hand)"); + } + } + + return $dest_package; + } +} \ No newline at end of file diff --git a/includes/pear/PEAR/REST.php b/includes/pear/PEAR/REST.php new file mode 100644 index 0000000..76a13cd --- /dev/null +++ b/includes/pear/PEAR/REST.php @@ -0,0 +1,483 @@ + + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a1 + */ + +/** + * For downloading xml files + */ +require_once 'PEAR.php'; +require_once 'PEAR/XMLParser.php'; + +/** + * Intelligently retrieve data, following hyperlinks if necessary, and re-directing + * as well + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a1 + */ +class PEAR_REST +{ + var $config; + var $_options; + + function PEAR_REST(&$config, $options = array()) + { + $this->config = &$config; + $this->_options = $options; + } + + /** + * Retrieve REST data, but always retrieve the local cache if it is available. + * + * This is useful for elements that should never change, such as information on a particular + * release + * @param string full URL to this resource + * @param array|false contents of the accept-encoding header + * @param boolean if true, xml will be returned as a string, otherwise, xml will be + * parsed using PEAR_XMLParser + * @return string|array + */ + function retrieveCacheFirst($url, $accept = false, $forcestring = false, $channel = false) + { + $cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR . + md5($url) . 'rest.cachefile'; + + if (file_exists($cachefile)) { + return unserialize(implode('', file($cachefile))); + } + + return $this->retrieveData($url, $accept, $forcestring, $channel); + } + + /** + * Retrieve a remote REST resource + * @param string full URL to this resource + * @param array|false contents of the accept-encoding header + * @param boolean if true, xml will be returned as a string, otherwise, xml will be + * parsed using PEAR_XMLParser + * @return string|array + */ + function retrieveData($url, $accept = false, $forcestring = false, $channel = false) + { + $cacheId = $this->getCacheId($url); + if ($ret = $this->useLocalCache($url, $cacheId)) { + return $ret; + } + + $file = $trieddownload = false; + if (!isset($this->_options['offline'])) { + $trieddownload = true; + $file = $this->downloadHttp($url, $cacheId ? $cacheId['lastChange'] : false, $accept, $channel); + } + + if (PEAR::isError($file)) { + if ($file->getCode() !== -9276) { + return $file; + } + + $trieddownload = false; + $file = false; // use local copy if available on socket connect error + } + + if (!$file) { + $ret = $this->getCache($url); + if (!PEAR::isError($ret) && $trieddownload) { + // reset the age of the cache if the server says it was unmodified + $result = $this->saveCache($url, $ret, null, true, $cacheId); + if (PEAR::isError($result)) { + return PEAR::raiseError($result->getMessage()); + } + } + + return $ret; + } + + if (is_array($file)) { + $headers = $file[2]; + $lastmodified = $file[1]; + $content = $file[0]; + } else { + $headers = array(); + $lastmodified = false; + $content = $file; + } + + if ($forcestring) { + $result = $this->saveCache($url, $content, $lastmodified, false, $cacheId); + if (PEAR::isError($result)) { + return PEAR::raiseError($result->getMessage()); + } + + return $content; + } + + if (isset($headers['content-type'])) { + switch ($headers['content-type']) { + case 'text/xml' : + case 'application/xml' : + case 'text/plain' : + if ($headers['content-type'] === 'text/plain') { + $check = substr($content, 0, 5); + if ($check !== 'parse($content); + PEAR::popErrorHandling(); + if (PEAR::isError($err)) { + return PEAR::raiseError('Invalid xml downloaded from "' . $url . '": ' . + $err->getMessage()); + } + $content = $parser->getData(); + case 'text/html' : + default : + // use it as a string + } + } else { + // assume XML + $parser = new PEAR_XMLParser; + $parser->parse($content); + $content = $parser->getData(); + } + + $result = $this->saveCache($url, $content, $lastmodified, false, $cacheId); + if (PEAR::isError($result)) { + return PEAR::raiseError($result->getMessage()); + } + + return $content; + } + + function useLocalCache($url, $cacheid = null) + { + if ($cacheid === null) { + $cacheidfile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR . + md5($url) . 'rest.cacheid'; + if (!file_exists($cacheidfile)) { + return false; + } + + $cacheid = unserialize(implode('', file($cacheidfile))); + } + + $cachettl = $this->config->get('cache_ttl'); + // If cache is newer than $cachettl seconds, we use the cache! + if (time() - $cacheid['age'] < $cachettl) { + return $this->getCache($url); + } + + return false; + } + + function getCacheId($url) + { + $cacheidfile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR . + md5($url) . 'rest.cacheid'; + + if (!file_exists($cacheidfile)) { + return false; + } + + $ret = unserialize(implode('', file($cacheidfile))); + return $ret; + } + + function getCache($url) + { + $cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR . + md5($url) . 'rest.cachefile'; + + if (!file_exists($cachefile)) { + return PEAR::raiseError('No cached content available for "' . $url . '"'); + } + + return unserialize(implode('', file($cachefile))); + } + + /** + * @param string full URL to REST resource + * @param string original contents of the REST resource + * @param array HTTP Last-Modified and ETag headers + * @param bool if true, then the cache id file should be regenerated to + * trigger a new time-to-live value + */ + function saveCache($url, $contents, $lastmodified, $nochange = false, $cacheid = null) + { + $cache_dir = $this->config->get('cache_dir'); + $d = $cache_dir . DIRECTORY_SEPARATOR . md5($url); + $cacheidfile = $d . 'rest.cacheid'; + $cachefile = $d . 'rest.cachefile'; + + if (!is_dir($cache_dir)) { + if (System::mkdir(array('-p', $cache_dir)) === false) { + return PEAR::raiseError("The value of config option cache_dir ($cache_dir) is not a directory and attempts to create the directory failed."); + } + } + + if ($cacheid === null && $nochange) { + $cacheid = unserialize(implode('', file($cacheidfile))); + } + + $idData = serialize(array( + 'age' => time(), + 'lastChange' => ($nochange ? $cacheid['lastChange'] : $lastmodified), + )); + + $result = $this->saveCacheFile($cacheidfile, $idData); + if (PEAR::isError($result)) { + return $result; + } elseif ($nochange) { + return true; + } + + $result = $this->saveCacheFile($cachefile, serialize($contents)); + if (PEAR::isError($result)) { + if (file_exists($cacheidfile)) { + @unlink($cacheidfile); + } + + return $result; + } + + return true; + } + + function saveCacheFile($file, $contents) + { + $len = strlen($contents); + + $cachefile_fp = @fopen($file, 'xb'); // x is the O_CREAT|O_EXCL mode + if ($cachefile_fp !== false) { // create file + if (fwrite($cachefile_fp, $contents, $len) < $len) { + fclose($cachefile_fp); + return PEAR::raiseError("Could not write $file."); + } + } else { // update file + $cachefile_lstat = lstat($file); + $cachefile_fp = @fopen($file, 'wb'); + if (!$cachefile_fp) { + return PEAR::raiseError("Could not open $file for writing."); + } + + $cachefile_fstat = fstat($cachefile_fp); + if ( + $cachefile_lstat['mode'] == $cachefile_fstat['mode'] && + $cachefile_lstat['ino'] == $cachefile_fstat['ino'] && + $cachefile_lstat['dev'] == $cachefile_fstat['dev'] && + $cachefile_fstat['nlink'] === 1 + ) { + if (fwrite($cachefile_fp, $contents, $len) < $len) { + fclose($cachefile_fp); + return PEAR::raiseError("Could not write $file."); + } + } else { + fclose($cachefile_fp); + $link = function_exists('readlink') ? readlink($file) : $file; + return PEAR::raiseError('SECURITY ERROR: Will not write to ' . $file . ' as it is symlinked to ' . $link . ' - Possible symlink attack'); + } + } + + fclose($cachefile_fp); + return true; + } + + /** + * Efficiently Download a file through HTTP. Returns downloaded file as a string in-memory + * This is best used for small files + * + * If an HTTP proxy has been configured (http_proxy PEAR_Config + * setting), the proxy will be used. + * + * @param string $url the URL to download + * @param string $save_dir directory to save file in + * @param false|string|array $lastmodified header values to check against for caching + * use false to return the header values from this download + * @param false|array $accept Accept headers to send + * @return string|array Returns the contents of the downloaded file or a PEAR + * error on failure. If the error is caused by + * socket-related errors, the error object will + * have the fsockopen error code available through + * getCode(). If caching is requested, then return the header + * values. + * + * @access public + */ + function downloadHttp($url, $lastmodified = null, $accept = false, $channel = false) + { + static $redirect = 0; + // always reset , so we are clean case of error + $wasredirect = $redirect; + $redirect = 0; + + $info = parse_url($url); + if (!isset($info['scheme']) || !in_array($info['scheme'], array('http', 'https'))) { + return PEAR::raiseError('Cannot download non-http URL "' . $url . '"'); + } + + if (!isset($info['host'])) { + return PEAR::raiseError('Cannot download from non-URL "' . $url . '"'); + } + + $host = isset($info['host']) ? $info['host'] : null; + $port = isset($info['port']) ? $info['port'] : null; + $path = isset($info['path']) ? $info['path'] : null; + $schema = (isset($info['scheme']) && $info['scheme'] == 'https') ? 'https' : 'http'; + + $proxy_host = $proxy_port = $proxy_user = $proxy_pass = ''; + if ($this->config->get('http_proxy')&& + $proxy = parse_url($this->config->get('http_proxy')) + ) { + $proxy_host = isset($proxy['host']) ? $proxy['host'] : null; + if ($schema === 'https') { + $proxy_host = 'ssl://' . $proxy_host; + } + + $proxy_port = isset($proxy['port']) ? $proxy['port'] : 8080; + $proxy_user = isset($proxy['user']) ? urldecode($proxy['user']) : null; + $proxy_pass = isset($proxy['pass']) ? urldecode($proxy['pass']) : null; + $proxy_schema = (isset($proxy['scheme']) && $proxy['scheme'] == 'https') ? 'https' : 'http'; + } + + if (empty($port)) { + $port = (isset($info['scheme']) && $info['scheme'] == 'https') ? 443 : 80; + } + + if (isset($proxy['host'])) { + $request = "GET $url HTTP/1.1\r\n"; + } else { + $request = "GET $path HTTP/1.1\r\n"; + } + + $request .= "Host: $host\r\n"; + $ifmodifiedsince = ''; + if (is_array($lastmodified)) { + if (isset($lastmodified['Last-Modified'])) { + $ifmodifiedsince = 'If-Modified-Since: ' . $lastmodified['Last-Modified'] . "\r\n"; + } + + if (isset($lastmodified['ETag'])) { + $ifmodifiedsince .= "If-None-Match: $lastmodified[ETag]\r\n"; + } + } else { + $ifmodifiedsince = ($lastmodified ? "If-Modified-Since: $lastmodified\r\n" : ''); + } + + $request .= $ifmodifiedsince . + "User-Agent: PEAR/1.9.4/PHP/" . PHP_VERSION . "\r\n"; + + $username = $this->config->get('username', null, $channel); + $password = $this->config->get('password', null, $channel); + + if ($username && $password) { + $tmp = base64_encode("$username:$password"); + $request .= "Authorization: Basic $tmp\r\n"; + } + + if ($proxy_host != '' && $proxy_user != '') { + $request .= 'Proxy-Authorization: Basic ' . + base64_encode($proxy_user . ':' . $proxy_pass) . "\r\n"; + } + + if ($accept) { + $request .= 'Accept: ' . implode(', ', $accept) . "\r\n"; + } + + $request .= "Accept-Encoding:\r\n"; + $request .= "Connection: close\r\n"; + $request .= "\r\n"; + + if ($proxy_host != '') { + $fp = @fsockopen($proxy_host, $proxy_port, $errno, $errstr, 15); + if (!$fp) { + return PEAR::raiseError("Connection to `$proxy_host:$proxy_port' failed: $errstr", -9276); + } + } else { + if ($schema === 'https') { + $host = 'ssl://' . $host; + } + + $fp = @fsockopen($host, $port, $errno, $errstr); + if (!$fp) { + return PEAR::raiseError("Connection to `$host:$port' failed: $errstr", $errno); + } + } + + fwrite($fp, $request); + + $headers = array(); + $reply = 0; + while ($line = trim(fgets($fp, 1024))) { + if (preg_match('/^([^:]+):\s+(.*)\s*\\z/', $line, $matches)) { + $headers[strtolower($matches[1])] = trim($matches[2]); + } elseif (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) { + $reply = (int)$matches[1]; + if ($reply == 304 && ($lastmodified || ($lastmodified === false))) { + return false; + } + + if (!in_array($reply, array(200, 301, 302, 303, 305, 307))) { + return PEAR::raiseError("File $schema://$host:$port$path not valid (received: $line)"); + } + } + } + + if ($reply != 200) { + if (!isset($headers['location'])) { + return PEAR::raiseError("File $schema://$host:$port$path not valid (redirected but no location)"); + } + + if ($wasredirect > 4) { + return PEAR::raiseError("File $schema://$host:$port$path not valid (redirection looped more than 5 times)"); + } + + $redirect = $wasredirect + 1; + return $this->downloadHttp($headers['location'], $lastmodified, $accept, $channel); + } + + $length = isset($headers['content-length']) ? $headers['content-length'] : -1; + + $data = ''; + while ($chunk = @fread($fp, 8192)) { + $data .= $chunk; + } + fclose($fp); + + if ($lastmodified === false || $lastmodified) { + if (isset($headers['etag'])) { + $lastmodified = array('ETag' => $headers['etag']); + } + + if (isset($headers['last-modified'])) { + if (is_array($lastmodified)) { + $lastmodified['Last-Modified'] = $headers['last-modified']; + } else { + $lastmodified = $headers['last-modified']; + } + } + + return array($data, $lastmodified, $headers); + } + + return $data; + } +} \ No newline at end of file diff --git a/includes/pear/PEAR/REST/10.php b/includes/pear/PEAR/REST/10.php new file mode 100644 index 0000000..7540efa --- /dev/null +++ b/includes/pear/PEAR/REST/10.php @@ -0,0 +1,871 @@ + + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a12 + */ + +/** + * For downloading REST xml/txt files + */ +require_once 'PEAR/REST.php'; + +/** + * Implement REST 1.0 + * + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a12 + */ +class PEAR_REST_10 +{ + /** + * @var PEAR_REST + */ + var $_rest; + function PEAR_REST_10($config, $options = array()) + { + $this->_rest = &new PEAR_REST($config, $options); + } + + /** + * Retrieve information about a remote package to be downloaded from a REST server + * + * @param string $base The uri to prepend to all REST calls + * @param array $packageinfo an array of format: + *
    +     *  array(
    +     *   'package' => 'packagename',
    +     *   'channel' => 'channelname',
    +     *  ['state' => 'alpha' (or valid state),]
    +     *  -or-
    +     *  ['version' => '1.whatever']
    +     * 
    + * @param string $prefstate Current preferred_state config variable value + * @param bool $installed the installed version of this package to compare against + * @return array|false|PEAR_Error see {@link _returnDownloadURL()} + */ + function getDownloadURL($base, $packageinfo, $prefstate, $installed, $channel = false) + { + $states = $this->betterStates($prefstate, true); + if (!$states) { + return PEAR::raiseError('"' . $prefstate . '" is not a valid state'); + } + + $channel = $packageinfo['channel']; + $package = $packageinfo['package']; + $state = isset($packageinfo['state']) ? $packageinfo['state'] : null; + $version = isset($packageinfo['version']) ? $packageinfo['version'] : null; + $restFile = $base . 'r/' . strtolower($package) . '/allreleases.xml'; + + $info = $this->_rest->retrieveData($restFile, false, false, $channel); + if (PEAR::isError($info)) { + return PEAR::raiseError('No releases available for package "' . + $channel . '/' . $package . '"'); + } + + if (!isset($info['r'])) { + return false; + } + + $release = $found = false; + if (!is_array($info['r']) || !isset($info['r'][0])) { + $info['r'] = array($info['r']); + } + + foreach ($info['r'] as $release) { + if (!isset($this->_rest->_options['force']) && ($installed && + version_compare($release['v'], $installed, '<'))) { + continue; + } + + if (isset($state)) { + // try our preferred state first + if ($release['s'] == $state) { + $found = true; + break; + } + // see if there is something newer and more stable + // bug #7221 + if (in_array($release['s'], $this->betterStates($state), true)) { + $found = true; + break; + } + } elseif (isset($version)) { + if ($release['v'] == $version) { + $found = true; + break; + } + } else { + if (in_array($release['s'], $states)) { + $found = true; + break; + } + } + } + + return $this->_returnDownloadURL($base, $package, $release, $info, $found, false, $channel); + } + + function getDepDownloadURL($base, $xsdversion, $dependency, $deppackage, + $prefstate = 'stable', $installed = false, $channel = false) + { + $states = $this->betterStates($prefstate, true); + if (!$states) { + return PEAR::raiseError('"' . $prefstate . '" is not a valid state'); + } + + $channel = $dependency['channel']; + $package = $dependency['name']; + $state = isset($dependency['state']) ? $dependency['state'] : null; + $version = isset($dependency['version']) ? $dependency['version'] : null; + $restFile = $base . 'r/' . strtolower($package) . '/allreleases.xml'; + + $info = $this->_rest->retrieveData($restFile, false, false, $channel); + if (PEAR::isError($info)) { + return PEAR::raiseError('Package "' . $deppackage['channel'] . '/' . $deppackage['package'] + . '" dependency "' . $channel . '/' . $package . '" has no releases'); + } + + if (!is_array($info) || !isset($info['r'])) { + return false; + } + + $exclude = array(); + $min = $max = $recommended = false; + if ($xsdversion == '1.0') { + switch ($dependency['rel']) { + case 'ge' : + $min = $dependency['version']; + break; + case 'gt' : + $min = $dependency['version']; + $exclude = array($dependency['version']); + break; + case 'eq' : + $recommended = $dependency['version']; + break; + case 'lt' : + $max = $dependency['version']; + $exclude = array($dependency['version']); + break; + case 'le' : + $max = $dependency['version']; + break; + case 'ne' : + $exclude = array($dependency['version']); + break; + } + } else { + $min = isset($dependency['min']) ? $dependency['min'] : false; + $max = isset($dependency['max']) ? $dependency['max'] : false; + $recommended = isset($dependency['recommended']) ? + $dependency['recommended'] : false; + if (isset($dependency['exclude'])) { + if (!isset($dependency['exclude'][0])) { + $exclude = array($dependency['exclude']); + } + } + } + $release = $found = false; + if (!is_array($info['r']) || !isset($info['r'][0])) { + $info['r'] = array($info['r']); + } + foreach ($info['r'] as $release) { + if (!isset($this->_rest->_options['force']) && ($installed && + version_compare($release['v'], $installed, '<'))) { + continue; + } + if (in_array($release['v'], $exclude)) { // skip excluded versions + continue; + } + // allow newer releases to say "I'm OK with the dependent package" + if ($xsdversion == '2.0' && isset($release['co'])) { + if (!is_array($release['co']) || !isset($release['co'][0])) { + $release['co'] = array($release['co']); + } + foreach ($release['co'] as $entry) { + if (isset($entry['x']) && !is_array($entry['x'])) { + $entry['x'] = array($entry['x']); + } elseif (!isset($entry['x'])) { + $entry['x'] = array(); + } + if ($entry['c'] == $deppackage['channel'] && + strtolower($entry['p']) == strtolower($deppackage['package']) && + version_compare($deppackage['version'], $entry['min'], '>=') && + version_compare($deppackage['version'], $entry['max'], '<=') && + !in_array($release['v'], $entry['x'])) { + $recommended = $release['v']; + break; + } + } + } + if ($recommended) { + if ($release['v'] != $recommended) { // if we want a specific + // version, then skip all others + continue; + } else { + if (!in_array($release['s'], $states)) { + // the stability is too low, but we must return the + // recommended version if possible + return $this->_returnDownloadURL($base, $package, $release, $info, true, false, $channel); + } + } + } + if ($min && version_compare($release['v'], $min, 'lt')) { // skip too old versions + continue; + } + if ($max && version_compare($release['v'], $max, 'gt')) { // skip too new versions + continue; + } + if ($installed && version_compare($release['v'], $installed, '<')) { + continue; + } + if (in_array($release['s'], $states)) { // if in the preferred state... + $found = true; // ... then use it + break; + } + } + return $this->_returnDownloadURL($base, $package, $release, $info, $found, false, $channel); + } + + /** + * Take raw data and return the array needed for processing a download URL + * + * @param string $base REST base uri + * @param string $package Package name + * @param array $release an array of format array('v' => version, 's' => state) + * describing the release to download + * @param array $info list of all releases as defined by allreleases.xml + * @param bool|null $found determines whether the release was found or this is the next + * best alternative. If null, then versions were skipped because + * of PHP dependency + * @return array|PEAR_Error + * @access private + */ + function _returnDownloadURL($base, $package, $release, $info, $found, $phpversion = false, $channel = false) + { + if (!$found) { + $release = $info['r'][0]; + } + + $packageLower = strtolower($package); + $pinfo = $this->_rest->retrieveCacheFirst($base . 'p/' . $packageLower . '/' . + 'info.xml', false, false, $channel); + if (PEAR::isError($pinfo)) { + return PEAR::raiseError('Package "' . $package . + '" does not have REST info xml available'); + } + + $releaseinfo = $this->_rest->retrieveCacheFirst($base . 'r/' . $packageLower . '/' . + $release['v'] . '.xml', false, false, $channel); + if (PEAR::isError($releaseinfo)) { + return PEAR::raiseError('Package "' . $package . '" Version "' . $release['v'] . + '" does not have REST xml available'); + } + + $packagexml = $this->_rest->retrieveCacheFirst($base . 'r/' . $packageLower . '/' . + 'deps.' . $release['v'] . '.txt', false, true, $channel); + if (PEAR::isError($packagexml)) { + return PEAR::raiseError('Package "' . $package . '" Version "' . $release['v'] . + '" does not have REST dependency information available'); + } + + $packagexml = unserialize($packagexml); + if (!$packagexml) { + $packagexml = array(); + } + + $allinfo = $this->_rest->retrieveData($base . 'r/' . $packageLower . + '/allreleases.xml', false, false, $channel); + if (PEAR::isError($allinfo)) { + return $allinfo; + } + + if (!is_array($allinfo['r']) || !isset($allinfo['r'][0])) { + $allinfo['r'] = array($allinfo['r']); + } + + $compatible = false; + foreach ($allinfo['r'] as $release) { + if ($release['v'] != $releaseinfo['v']) { + continue; + } + + if (!isset($release['co'])) { + break; + } + + $compatible = array(); + if (!is_array($release['co']) || !isset($release['co'][0])) { + $release['co'] = array($release['co']); + } + + foreach ($release['co'] as $entry) { + $comp = array(); + $comp['name'] = $entry['p']; + $comp['channel'] = $entry['c']; + $comp['min'] = $entry['min']; + $comp['max'] = $entry['max']; + if (isset($entry['x']) && !is_array($entry['x'])) { + $comp['exclude'] = $entry['x']; + } + + $compatible[] = $comp; + } + + if (count($compatible) == 1) { + $compatible = $compatible[0]; + } + + break; + } + + $deprecated = false; + if (isset($pinfo['dc']) && isset($pinfo['dp'])) { + if (is_array($pinfo['dp'])) { + $deprecated = array('channel' => (string) $pinfo['dc'], + 'package' => trim($pinfo['dp']['_content'])); + } else { + $deprecated = array('channel' => (string) $pinfo['dc'], + 'package' => trim($pinfo['dp'])); + } + } + + $return = array( + 'version' => $releaseinfo['v'], + 'info' => $packagexml, + 'package' => $releaseinfo['p']['_content'], + 'stability' => $releaseinfo['st'], + 'compatible' => $compatible, + 'deprecated' => $deprecated, + ); + + if ($found) { + $return['url'] = $releaseinfo['g']; + return $return; + } + + $return['php'] = $phpversion; + return $return; + } + + function listPackages($base, $channel = false) + { + $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel); + if (PEAR::isError($packagelist)) { + return $packagelist; + } + + if (!is_array($packagelist) || !isset($packagelist['p'])) { + return array(); + } + + if (!is_array($packagelist['p'])) { + $packagelist['p'] = array($packagelist['p']); + } + + return $packagelist['p']; + } + + /** + * List all categories of a REST server + * + * @param string $base base URL of the server + * @return array of categorynames + */ + function listCategories($base, $channel = false) + { + $categories = array(); + + // c/categories.xml does not exist; + // check for every package its category manually + // This is SLOOOWWWW : /// + $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel); + if (PEAR::isError($packagelist)) { + return $packagelist; + } + + if (!is_array($packagelist) || !isset($packagelist['p'])) { + $ret = array(); + return $ret; + } + + if (!is_array($packagelist['p'])) { + $packagelist['p'] = array($packagelist['p']); + } + + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + foreach ($packagelist['p'] as $package) { + $inf = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml', false, false, $channel); + if (PEAR::isError($inf)) { + PEAR::popErrorHandling(); + return $inf; + } + $cat = $inf['ca']['_content']; + if (!isset($categories[$cat])) { + $categories[$cat] = $inf['ca']; + } + } + + return array_values($categories); + } + + /** + * List a category of a REST server + * + * @param string $base base URL of the server + * @param string $category name of the category + * @param boolean $info also download full package info + * @return array of packagenames + */ + function listCategory($base, $category, $info = false, $channel = false) + { + // gives '404 Not Found' error when category doesn't exist + $packagelist = $this->_rest->retrieveData($base.'c/'.urlencode($category).'/packages.xml', false, false, $channel); + if (PEAR::isError($packagelist)) { + return $packagelist; + } + + if (!is_array($packagelist) || !isset($packagelist['p'])) { + return array(); + } + + if (!is_array($packagelist['p']) || + !isset($packagelist['p'][0])) { // only 1 pkg + $packagelist = array($packagelist['p']); + } else { + $packagelist = $packagelist['p']; + } + + if ($info == true) { + // get individual package info + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + foreach ($packagelist as $i => $packageitem) { + $url = sprintf('%s'.'r/%s/latest.txt', + $base, + strtolower($packageitem['_content'])); + $version = $this->_rest->retrieveData($url, false, false, $channel); + if (PEAR::isError($version)) { + break; // skipit + } + $url = sprintf('%s'.'r/%s/%s.xml', + $base, + strtolower($packageitem['_content']), + $version); + $info = $this->_rest->retrieveData($url, false, false, $channel); + if (PEAR::isError($info)) { + break; // skipit + } + $packagelist[$i]['info'] = $info; + } + PEAR::popErrorHandling(); + } + + return $packagelist; + } + + + function listAll($base, $dostable, $basic = true, $searchpackage = false, $searchsummary = false, $channel = false) + { + $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel); + if (PEAR::isError($packagelist)) { + return $packagelist; + } + if ($this->_rest->config->get('verbose') > 0) { + $ui = &PEAR_Frontend::singleton(); + $ui->log('Retrieving data...0%', true); + } + $ret = array(); + if (!is_array($packagelist) || !isset($packagelist['p'])) { + return $ret; + } + if (!is_array($packagelist['p'])) { + $packagelist['p'] = array($packagelist['p']); + } + + // only search-packagename = quicksearch ! + if ($searchpackage && (!$searchsummary || empty($searchpackage))) { + $newpackagelist = array(); + foreach ($packagelist['p'] as $package) { + if (!empty($searchpackage) && stristr($package, $searchpackage) !== false) { + $newpackagelist[] = $package; + } + } + $packagelist['p'] = $newpackagelist; + } + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + $next = .1; + foreach ($packagelist['p'] as $progress => $package) { + if ($this->_rest->config->get('verbose') > 0) { + if ($progress / count($packagelist['p']) >= $next) { + if ($next == .5) { + $ui->log('50%', false); + } else { + $ui->log('.', false); + } + $next += .1; + } + } + + if ($basic) { // remote-list command + if ($dostable) { + $latest = $this->_rest->retrieveData($base . 'r/' . strtolower($package) . + '/stable.txt', false, false, $channel); + } else { + $latest = $this->_rest->retrieveData($base . 'r/' . strtolower($package) . + '/latest.txt', false, false, $channel); + } + if (PEAR::isError($latest)) { + $latest = false; + } + $info = array('stable' => $latest); + } else { // list-all command + $inf = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml', false, false, $channel); + if (PEAR::isError($inf)) { + PEAR::popErrorHandling(); + return $inf; + } + if ($searchpackage) { + $found = (!empty($searchpackage) && stristr($package, $searchpackage) !== false); + if (!$found && !(isset($searchsummary) && !empty($searchsummary) + && (stristr($inf['s'], $searchsummary) !== false + || stristr($inf['d'], $searchsummary) !== false))) + { + continue; + }; + } + $releases = $this->_rest->retrieveData($base . 'r/' . strtolower($package) . + '/allreleases.xml', false, false, $channel); + if (PEAR::isError($releases)) { + continue; + } + if (!isset($releases['r'][0])) { + $releases['r'] = array($releases['r']); + } + unset($latest); + unset($unstable); + unset($stable); + unset($state); + foreach ($releases['r'] as $release) { + if (!isset($latest)) { + if ($dostable && $release['s'] == 'stable') { + $latest = $release['v']; + $state = 'stable'; + } + if (!$dostable) { + $latest = $release['v']; + $state = $release['s']; + } + } + if (!isset($stable) && $release['s'] == 'stable') { + $stable = $release['v']; + if (!isset($unstable)) { + $unstable = $stable; + } + } + if (!isset($unstable) && $release['s'] != 'stable') { + $latest = $unstable = $release['v']; + $state = $release['s']; + } + if (isset($latest) && !isset($state)) { + $state = $release['s']; + } + if (isset($latest) && isset($stable) && isset($unstable)) { + break; + } + } + $deps = array(); + if (!isset($unstable)) { + $unstable = false; + $state = 'stable'; + if (isset($stable)) { + $latest = $unstable = $stable; + } + } else { + $latest = $unstable; + } + if (!isset($latest)) { + $latest = false; + } + if ($latest) { + $d = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/deps.' . + $latest . '.txt', false, false, $channel); + if (!PEAR::isError($d)) { + $d = unserialize($d); + if ($d) { + if (isset($d['required'])) { + if (!class_exists('PEAR_PackageFile_v2')) { + require_once 'PEAR/PackageFile/v2.php'; + } + if (!isset($pf)) { + $pf = new PEAR_PackageFile_v2; + } + $pf->setDeps($d); + $tdeps = $pf->getDeps(); + } else { + $tdeps = $d; + } + foreach ($tdeps as $dep) { + if ($dep['type'] !== 'pkg') { + continue; + } + $deps[] = $dep; + } + } + } + } + if (!isset($stable)) { + $stable = '-n/a-'; + } + if (!$searchpackage) { + $info = array('stable' => $latest, 'summary' => $inf['s'], 'description' => + $inf['d'], 'deps' => $deps, 'category' => $inf['ca']['_content'], + 'unstable' => $unstable, 'state' => $state); + } else { + $info = array('stable' => $stable, 'summary' => $inf['s'], 'description' => + $inf['d'], 'deps' => $deps, 'category' => $inf['ca']['_content'], + 'unstable' => $unstable, 'state' => $state); + } + } + $ret[$package] = $info; + } + PEAR::popErrorHandling(); + return $ret; + } + + function listLatestUpgrades($base, $pref_state, $installed, $channel, &$reg) + { + $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel); + if (PEAR::isError($packagelist)) { + return $packagelist; + } + + $ret = array(); + if (!is_array($packagelist) || !isset($packagelist['p'])) { + return $ret; + } + + if (!is_array($packagelist['p'])) { + $packagelist['p'] = array($packagelist['p']); + } + + foreach ($packagelist['p'] as $package) { + if (!isset($installed[strtolower($package)])) { + continue; + } + + $inst_version = $reg->packageInfo($package, 'version', $channel); + $inst_state = $reg->packageInfo($package, 'release_state', $channel); + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + $info = $this->_rest->retrieveData($base . 'r/' . strtolower($package) . + '/allreleases.xml', false, false, $channel); + PEAR::popErrorHandling(); + if (PEAR::isError($info)) { + continue; // no remote releases + } + + if (!isset($info['r'])) { + continue; + } + + $release = $found = false; + if (!is_array($info['r']) || !isset($info['r'][0])) { + $info['r'] = array($info['r']); + } + + // $info['r'] is sorted by version number + usort($info['r'], array($this, '_sortReleasesByVersionNumber')); + foreach ($info['r'] as $release) { + if ($inst_version && version_compare($release['v'], $inst_version, '<=')) { + // not newer than the one installed + break; + } + + // new version > installed version + if (!$pref_state) { + // every state is a good state + $found = true; + break; + } else { + $new_state = $release['s']; + // if new state >= installed state: go + if (in_array($new_state, $this->betterStates($inst_state, true))) { + $found = true; + break; + } else { + // only allow to lower the state of package, + // if new state >= preferred state: go + if (in_array($new_state, $this->betterStates($pref_state, true))) { + $found = true; + break; + } + } + } + } + + if (!$found) { + continue; + } + + $relinfo = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/' . + $release['v'] . '.xml', false, false, $channel); + if (PEAR::isError($relinfo)) { + return $relinfo; + } + + $ret[$package] = array( + 'version' => $release['v'], + 'state' => $release['s'], + 'filesize' => $relinfo['f'], + ); + } + + return $ret; + } + + function packageInfo($base, $package, $channel = false) + { + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + $pinfo = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml', false, false, $channel); + if (PEAR::isError($pinfo)) { + PEAR::popErrorHandling(); + return PEAR::raiseError('Unknown package: "' . $package . '" in channel "' . $channel . '"' . "\n". 'Debug: ' . + $pinfo->getMessage()); + } + + $releases = array(); + $allreleases = $this->_rest->retrieveData($base . 'r/' . strtolower($package) . + '/allreleases.xml', false, false, $channel); + if (!PEAR::isError($allreleases)) { + if (!class_exists('PEAR_PackageFile_v2')) { + require_once 'PEAR/PackageFile/v2.php'; + } + + if (!is_array($allreleases['r']) || !isset($allreleases['r'][0])) { + $allreleases['r'] = array($allreleases['r']); + } + + $pf = new PEAR_PackageFile_v2; + foreach ($allreleases['r'] as $release) { + $ds = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/deps.' . + $release['v'] . '.txt', false, false, $channel); + if (PEAR::isError($ds)) { + continue; + } + + if (!isset($latest)) { + $latest = $release['v']; + } + + $pf->setDeps(unserialize($ds)); + $ds = $pf->getDeps(); + $info = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) + . '/' . $release['v'] . '.xml', false, false, $channel); + + if (PEAR::isError($info)) { + continue; + } + + $releases[$release['v']] = array( + 'doneby' => $info['m'], + 'license' => $info['l'], + 'summary' => $info['s'], + 'description' => $info['d'], + 'releasedate' => $info['da'], + 'releasenotes' => $info['n'], + 'state' => $release['s'], + 'deps' => $ds ? $ds : array(), + ); + } + } else { + $latest = ''; + } + + PEAR::popErrorHandling(); + if (isset($pinfo['dc']) && isset($pinfo['dp'])) { + if (is_array($pinfo['dp'])) { + $deprecated = array('channel' => (string) $pinfo['dc'], + 'package' => trim($pinfo['dp']['_content'])); + } else { + $deprecated = array('channel' => (string) $pinfo['dc'], + 'package' => trim($pinfo['dp'])); + } + } else { + $deprecated = false; + } + + if (!isset($latest)) { + $latest = ''; + } + + return array( + 'name' => $pinfo['n'], + 'channel' => $pinfo['c'], + 'category' => $pinfo['ca']['_content'], + 'stable' => $latest, + 'license' => $pinfo['l'], + 'summary' => $pinfo['s'], + 'description' => $pinfo['d'], + 'releases' => $releases, + 'deprecated' => $deprecated, + ); + } + + /** + * Return an array containing all of the states that are more stable than + * or equal to the passed in state + * + * @param string Release state + * @param boolean Determines whether to include $state in the list + * @return false|array False if $state is not a valid release state + */ + function betterStates($state, $include = false) + { + static $states = array('snapshot', 'devel', 'alpha', 'beta', 'stable'); + $i = array_search($state, $states); + if ($i === false) { + return false; + } + + if ($include) { + $i--; + } + + return array_slice($states, $i + 1); + } + + /** + * Sort releases by version number + * + * @access private + */ + function _sortReleasesByVersionNumber($a, $b) + { + if (version_compare($a['v'], $b['v'], '=')) { + return 0; + } + + if (version_compare($a['v'], $b['v'], '>')) { + return -1; + } + + if (version_compare($a['v'], $b['v'], '<')) { + return 1; + } + } +} \ No newline at end of file diff --git a/includes/pear/PEAR/REST/11.php b/includes/pear/PEAR/REST/11.php new file mode 100644 index 0000000..6d2fcd8 --- /dev/null +++ b/includes/pear/PEAR/REST/11.php @@ -0,0 +1,341 @@ + + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.3 + */ + +/** + * For downloading REST xml/txt files + */ +require_once 'PEAR/REST.php'; + +/** + * Implement REST 1.1 + * + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.3 + */ +class PEAR_REST_11 +{ + /** + * @var PEAR_REST + */ + var $_rest; + + function PEAR_REST_11($config, $options = array()) + { + $this->_rest = &new PEAR_REST($config, $options); + } + + function listAll($base, $dostable, $basic = true, $searchpackage = false, $searchsummary = false, $channel = false) + { + $categorylist = $this->_rest->retrieveData($base . 'c/categories.xml', false, false, $channel); + if (PEAR::isError($categorylist)) { + return $categorylist; + } + + $ret = array(); + if (!is_array($categorylist['c']) || !isset($categorylist['c'][0])) { + $categorylist['c'] = array($categorylist['c']); + } + + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + + foreach ($categorylist['c'] as $progress => $category) { + $category = $category['_content']; + $packagesinfo = $this->_rest->retrieveData($base . + 'c/' . urlencode($category) . '/packagesinfo.xml', false, false, $channel); + + if (PEAR::isError($packagesinfo)) { + continue; + } + + if (!is_array($packagesinfo) || !isset($packagesinfo['pi'])) { + continue; + } + + if (!is_array($packagesinfo['pi']) || !isset($packagesinfo['pi'][0])) { + $packagesinfo['pi'] = array($packagesinfo['pi']); + } + + foreach ($packagesinfo['pi'] as $packageinfo) { + if (empty($packageinfo)) { + continue; + } + + $info = $packageinfo['p']; + $package = $info['n']; + $releases = isset($packageinfo['a']) ? $packageinfo['a'] : false; + unset($latest); + unset($unstable); + unset($stable); + unset($state); + + if ($releases) { + if (!isset($releases['r'][0])) { + $releases['r'] = array($releases['r']); + } + + foreach ($releases['r'] as $release) { + if (!isset($latest)) { + if ($dostable && $release['s'] == 'stable') { + $latest = $release['v']; + $state = 'stable'; + } + if (!$dostable) { + $latest = $release['v']; + $state = $release['s']; + } + } + + if (!isset($stable) && $release['s'] == 'stable') { + $stable = $release['v']; + if (!isset($unstable)) { + $unstable = $stable; + } + } + + if (!isset($unstable) && $release['s'] != 'stable') { + $unstable = $release['v']; + $state = $release['s']; + } + + if (isset($latest) && !isset($state)) { + $state = $release['s']; + } + + if (isset($latest) && isset($stable) && isset($unstable)) { + break; + } + } + } + + if ($basic) { // remote-list command + if (!isset($latest)) { + $latest = false; + } + + if ($dostable) { + // $state is not set if there are no releases + if (isset($state) && $state == 'stable') { + $ret[$package] = array('stable' => $latest); + } else { + $ret[$package] = array('stable' => '-n/a-'); + } + } else { + $ret[$package] = array('stable' => $latest); + } + + continue; + } + + // list-all command + if (!isset($unstable)) { + $unstable = false; + $state = 'stable'; + if (isset($stable)) { + $latest = $unstable = $stable; + } + } else { + $latest = $unstable; + } + + if (!isset($latest)) { + $latest = false; + } + + $deps = array(); + if ($latest && isset($packageinfo['deps'])) { + if (!is_array($packageinfo['deps']) || + !isset($packageinfo['deps'][0]) + ) { + $packageinfo['deps'] = array($packageinfo['deps']); + } + + $d = false; + foreach ($packageinfo['deps'] as $dep) { + if ($dep['v'] == $latest) { + $d = unserialize($dep['d']); + } + } + + if ($d) { + if (isset($d['required'])) { + if (!class_exists('PEAR_PackageFile_v2')) { + require_once 'PEAR/PackageFile/v2.php'; + } + + if (!isset($pf)) { + $pf = new PEAR_PackageFile_v2; + } + + $pf->setDeps($d); + $tdeps = $pf->getDeps(); + } else { + $tdeps = $d; + } + + foreach ($tdeps as $dep) { + if ($dep['type'] !== 'pkg') { + continue; + } + + $deps[] = $dep; + } + } + } + + $info = array( + 'stable' => $latest, + 'summary' => $info['s'], + 'description' => $info['d'], + 'deps' => $deps, + 'category' => $info['ca']['_content'], + 'unstable' => $unstable, + 'state' => $state + ); + $ret[$package] = $info; + } + } + + PEAR::popErrorHandling(); + return $ret; + } + + /** + * List all categories of a REST server + * + * @param string $base base URL of the server + * @return array of categorynames + */ + function listCategories($base, $channel = false) + { + $categorylist = $this->_rest->retrieveData($base . 'c/categories.xml', false, false, $channel); + if (PEAR::isError($categorylist)) { + return $categorylist; + } + + if (!is_array($categorylist) || !isset($categorylist['c'])) { + return array(); + } + + if (isset($categorylist['c']['_content'])) { + // only 1 category + $categorylist['c'] = array($categorylist['c']); + } + + return $categorylist['c']; + } + + /** + * List packages in a category of a REST server + * + * @param string $base base URL of the server + * @param string $category name of the category + * @param boolean $info also download full package info + * @return array of packagenames + */ + function listCategory($base, $category, $info = false, $channel = false) + { + if ($info == false) { + $url = '%s'.'c/%s/packages.xml'; + } else { + $url = '%s'.'c/%s/packagesinfo.xml'; + } + $url = sprintf($url, + $base, + urlencode($category)); + + // gives '404 Not Found' error when category doesn't exist + $packagelist = $this->_rest->retrieveData($url, false, false, $channel); + if (PEAR::isError($packagelist)) { + return $packagelist; + } + if (!is_array($packagelist)) { + return array(); + } + + if ($info == false) { + if (!isset($packagelist['p'])) { + return array(); + } + if (!is_array($packagelist['p']) || + !isset($packagelist['p'][0])) { // only 1 pkg + $packagelist = array($packagelist['p']); + } else { + $packagelist = $packagelist['p']; + } + return $packagelist; + } + + // info == true + if (!isset($packagelist['pi'])) { + return array(); + } + + if (!is_array($packagelist['pi']) || + !isset($packagelist['pi'][0])) { // only 1 pkg + $packagelist_pre = array($packagelist['pi']); + } else { + $packagelist_pre = $packagelist['pi']; + } + + $packagelist = array(); + foreach ($packagelist_pre as $i => $item) { + // compatibility with r/.xml + if (isset($item['a']['r'][0])) { + // multiple releases + $item['p']['v'] = $item['a']['r'][0]['v']; + $item['p']['st'] = $item['a']['r'][0]['s']; + } elseif (isset($item['a'])) { + // first and only release + $item['p']['v'] = $item['a']['r']['v']; + $item['p']['st'] = $item['a']['r']['s']; + } + + $packagelist[$i] = array('attribs' => $item['p']['r'], + '_content' => $item['p']['n'], + 'info' => $item['p']); + } + + return $packagelist; + } + + /** + * Return an array containing all of the states that are more stable than + * or equal to the passed in state + * + * @param string Release state + * @param boolean Determines whether to include $state in the list + * @return false|array False if $state is not a valid release state + */ + function betterStates($state, $include = false) + { + static $states = array('snapshot', 'devel', 'alpha', 'beta', 'stable'); + $i = array_search($state, $states); + if ($i === false) { + return false; + } + if ($include) { + $i--; + } + return array_slice($states, $i + 1); + } +} +?> \ No newline at end of file diff --git a/includes/pear/PEAR/REST/13.php b/includes/pear/PEAR/REST/13.php new file mode 100644 index 0000000..f38cb62 --- /dev/null +++ b/includes/pear/PEAR/REST/13.php @@ -0,0 +1,299 @@ + + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a12 + */ + +/** + * For downloading REST xml/txt files + */ +require_once 'PEAR/REST.php'; +require_once 'PEAR/REST/10.php'; + +/** + * Implement REST 1.3 + * + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a12 + */ +class PEAR_REST_13 extends PEAR_REST_10 +{ + /** + * Retrieve information about a remote package to be downloaded from a REST server + * + * This is smart enough to resolve the minimum PHP version dependency prior to download + * @param string $base The uri to prepend to all REST calls + * @param array $packageinfo an array of format: + *
    +     *  array(
    +     *   'package' => 'packagename',
    +     *   'channel' => 'channelname',
    +     *  ['state' => 'alpha' (or valid state),]
    +     *  -or-
    +     *  ['version' => '1.whatever']
    +     * 
    + * @param string $prefstate Current preferred_state config variable value + * @param bool $installed the installed version of this package to compare against + * @return array|false|PEAR_Error see {@link _returnDownloadURL()} + */ + function getDownloadURL($base, $packageinfo, $prefstate, $installed, $channel = false) + { + $states = $this->betterStates($prefstate, true); + if (!$states) { + return PEAR::raiseError('"' . $prefstate . '" is not a valid state'); + } + + $channel = $packageinfo['channel']; + $package = $packageinfo['package']; + $state = isset($packageinfo['state']) ? $packageinfo['state'] : null; + $version = isset($packageinfo['version']) ? $packageinfo['version'] : null; + $restFile = $base . 'r/' . strtolower($package) . '/allreleases2.xml'; + + $info = $this->_rest->retrieveData($restFile, false, false, $channel); + if (PEAR::isError($info)) { + return PEAR::raiseError('No releases available for package "' . + $channel . '/' . $package . '"'); + } + + if (!isset($info['r'])) { + return false; + } + + $release = $found = false; + if (!is_array($info['r']) || !isset($info['r'][0])) { + $info['r'] = array($info['r']); + } + + $skippedphp = false; + foreach ($info['r'] as $release) { + if (!isset($this->_rest->_options['force']) && ($installed && + version_compare($release['v'], $installed, '<'))) { + continue; + } + + if (isset($state)) { + // try our preferred state first + if ($release['s'] == $state) { + if (!isset($version) && version_compare($release['m'], phpversion(), '>')) { + // skip releases that require a PHP version newer than our PHP version + $skippedphp = $release; + continue; + } + $found = true; + break; + } + + // see if there is something newer and more stable + // bug #7221 + if (in_array($release['s'], $this->betterStates($state), true)) { + if (!isset($version) && version_compare($release['m'], phpversion(), '>')) { + // skip releases that require a PHP version newer than our PHP version + $skippedphp = $release; + continue; + } + $found = true; + break; + } + } elseif (isset($version)) { + if ($release['v'] == $version) { + if (!isset($this->_rest->_options['force']) && + !isset($version) && + version_compare($release['m'], phpversion(), '>')) { + // skip releases that require a PHP version newer than our PHP version + $skippedphp = $release; + continue; + } + $found = true; + break; + } + } else { + if (in_array($release['s'], $states)) { + if (version_compare($release['m'], phpversion(), '>')) { + // skip releases that require a PHP version newer than our PHP version + $skippedphp = $release; + continue; + } + $found = true; + break; + } + } + } + + if (!$found && $skippedphp) { + $found = null; + } + + return $this->_returnDownloadURL($base, $package, $release, $info, $found, $skippedphp, $channel); + } + + function getDepDownloadURL($base, $xsdversion, $dependency, $deppackage, + $prefstate = 'stable', $installed = false, $channel = false) + { + $states = $this->betterStates($prefstate, true); + if (!$states) { + return PEAR::raiseError('"' . $prefstate . '" is not a valid state'); + } + + $channel = $dependency['channel']; + $package = $dependency['name']; + $state = isset($dependency['state']) ? $dependency['state'] : null; + $version = isset($dependency['version']) ? $dependency['version'] : null; + $restFile = $base . 'r/' . strtolower($package) .'/allreleases2.xml'; + + $info = $this->_rest->retrieveData($restFile, false, false, $channel); + if (PEAR::isError($info)) { + return PEAR::raiseError('Package "' . $deppackage['channel'] . '/' . $deppackage['package'] + . '" dependency "' . $channel . '/' . $package . '" has no releases'); + } + + if (!is_array($info) || !isset($info['r'])) { + return false; + } + + $exclude = array(); + $min = $max = $recommended = false; + if ($xsdversion == '1.0') { + $pinfo['package'] = $dependency['name']; + $pinfo['channel'] = 'pear.php.net'; // this is always true - don't change this + switch ($dependency['rel']) { + case 'ge' : + $min = $dependency['version']; + break; + case 'gt' : + $min = $dependency['version']; + $exclude = array($dependency['version']); + break; + case 'eq' : + $recommended = $dependency['version']; + break; + case 'lt' : + $max = $dependency['version']; + $exclude = array($dependency['version']); + break; + case 'le' : + $max = $dependency['version']; + break; + case 'ne' : + $exclude = array($dependency['version']); + break; + } + } else { + $pinfo['package'] = $dependency['name']; + $min = isset($dependency['min']) ? $dependency['min'] : false; + $max = isset($dependency['max']) ? $dependency['max'] : false; + $recommended = isset($dependency['recommended']) ? + $dependency['recommended'] : false; + if (isset($dependency['exclude'])) { + if (!isset($dependency['exclude'][0])) { + $exclude = array($dependency['exclude']); + } + } + } + + $skippedphp = $found = $release = false; + if (!is_array($info['r']) || !isset($info['r'][0])) { + $info['r'] = array($info['r']); + } + + foreach ($info['r'] as $release) { + if (!isset($this->_rest->_options['force']) && ($installed && + version_compare($release['v'], $installed, '<'))) { + continue; + } + + if (in_array($release['v'], $exclude)) { // skip excluded versions + continue; + } + + // allow newer releases to say "I'm OK with the dependent package" + if ($xsdversion == '2.0' && isset($release['co'])) { + if (!is_array($release['co']) || !isset($release['co'][0])) { + $release['co'] = array($release['co']); + } + + foreach ($release['co'] as $entry) { + if (isset($entry['x']) && !is_array($entry['x'])) { + $entry['x'] = array($entry['x']); + } elseif (!isset($entry['x'])) { + $entry['x'] = array(); + } + + if ($entry['c'] == $deppackage['channel'] && + strtolower($entry['p']) == strtolower($deppackage['package']) && + version_compare($deppackage['version'], $entry['min'], '>=') && + version_compare($deppackage['version'], $entry['max'], '<=') && + !in_array($release['v'], $entry['x'])) { + if (version_compare($release['m'], phpversion(), '>')) { + // skip dependency releases that require a PHP version + // newer than our PHP version + $skippedphp = $release; + continue; + } + + $recommended = $release['v']; + break; + } + } + } + + if ($recommended) { + if ($release['v'] != $recommended) { // if we want a specific + // version, then skip all others + continue; + } + + if (!in_array($release['s'], $states)) { + // the stability is too low, but we must return the + // recommended version if possible + return $this->_returnDownloadURL($base, $package, $release, $info, true, false, $channel); + } + } + + if ($min && version_compare($release['v'], $min, 'lt')) { // skip too old versions + continue; + } + + if ($max && version_compare($release['v'], $max, 'gt')) { // skip too new versions + continue; + } + + if ($installed && version_compare($release['v'], $installed, '<')) { + continue; + } + + if (in_array($release['s'], $states)) { // if in the preferred state... + if (version_compare($release['m'], phpversion(), '>')) { + // skip dependency releases that require a PHP version + // newer than our PHP version + $skippedphp = $release; + continue; + } + + $found = true; // ... then use it + break; + } + } + + if (!$found && $skippedphp) { + $found = null; + } + + return $this->_returnDownloadURL($base, $package, $release, $info, $found, $skippedphp, $channel); + } +} \ No newline at end of file diff --git a/includes/pear/PEAR/REST/14.php b/includes/pear/PEAR/REST/14.php new file mode 100644 index 0000000..3358a46 --- /dev/null +++ b/includes/pear/PEAR/REST/14.php @@ -0,0 +1,120 @@ + + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: $ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.9 + */ + +/** + * For downloading REST xml/txt files + */ +require_once 'PEAR/REST.php'; +require_once 'PEAR/REST/13.php'; + +/** + * Implement REST 1.4 + * + * @category pear + * @package PEAR + * @author Helgi Þormar Þorbjörnsson + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.9 + */ +class PEAR_REST_14 extends PEAR_REST_13 +{ + function listLatestUpgrades($base, $pref_state, $installed, $channel, &$reg) + { + $packagelist = $this->_rest->retrieveData($base . 'p/latestpackages.xml', false, false, $channel); + if (PEAR::isError($packagelist)) { + return $packagelist; + } + + $ret = array(); + if (!is_array($packagelist) || !isset($packagelist['p'])) { + return $ret; + } + + if (isset($packagelist['p']['n'])) { + $packagelist['p'] = array($packagelist['p']); + } + + foreach ($packagelist['p'] as $package) { + if (!isset($installed[strtolower($package['n'])])) { + continue; + } + + $inst_version = $reg->packageInfo($package['n'], 'version', $channel); + $inst_state = $reg->packageInfo($package['n'], 'release_state', $channel); + + + $release = $found = false; + $data = array(); + if (isset($package['alpha'])) { + $data['alpha'] = $package['alpha']; + } + + if (isset($package['beta'])) { + $data['beta'] = $package['beta']; + } + + if (isset($package['stable'])) { + $data['stable'] = $package['stable']; + } + + foreach ($data as $state => $release) { + if ($inst_version && version_compare($release['v'], $inst_version, '<=')) { + // not newer than the one installed + break; + } + + // new version > installed version + if (!$pref_state) { + // every state is a good state + $found = true; + $release['state'] = $state; + break; + } else { + $new_state = $state; + // if new state >= installed state: go + if (in_array($new_state, $this->betterStates($inst_state, true))) { + $found = true; + $release['state'] = $state; + break; + } else { + // only allow to lower the state of package, + // if new state >= preferred state: go + if (in_array($new_state, $this->betterStates($pref_state, true))) { + $found = true; + $release['state'] = $state; + break; + } + } + } + } + + if (!$found) { + continue; + } + + $ret[$package] = array( + 'version' => $release['v'], + 'state' => $release['s'], + 'filesize' => $release['f'], + ); + } + + return $ret; + } +} \ No newline at end of file diff --git a/includes/pear/PEAR/Registry.php b/includes/pear/PEAR/Registry.php new file mode 100644 index 0000000..02168db --- /dev/null +++ b/includes/pear/PEAR/Registry.php @@ -0,0 +1,2339 @@ + + * @author Tomas V. V. Cox + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 0.1 + */ + +/** + * for PEAR_Error + */ +require_once 'PEAR.php'; +require_once 'PEAR/DependencyDB.php'; + +define('PEAR_REGISTRY_ERROR_LOCK', -2); +define('PEAR_REGISTRY_ERROR_FORMAT', -3); +define('PEAR_REGISTRY_ERROR_FILE', -4); +define('PEAR_REGISTRY_ERROR_CONFLICT', -5); +define('PEAR_REGISTRY_ERROR_CHANNEL_FILE', -6); + +/** + * Administration class used to maintain the installed package database. + * @category pear + * @package PEAR + * @author Stig Bakken + * @author Tomas V. V. Cox + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a1 + */ +class PEAR_Registry extends PEAR +{ + /** + * File containing all channel information. + * @var string + */ + var $channels = ''; + + /** Directory where registry files are stored. + * @var string + */ + var $statedir = ''; + + /** File where the file map is stored + * @var string + */ + var $filemap = ''; + + /** Directory where registry files for channels are stored. + * @var string + */ + var $channelsdir = ''; + + /** Name of file used for locking the registry + * @var string + */ + var $lockfile = ''; + + /** File descriptor used during locking + * @var resource + */ + var $lock_fp = null; + + /** Mode used during locking + * @var int + */ + var $lock_mode = 0; // XXX UNUSED + + /** Cache of package information. Structure: + * array( + * 'package' => array('id' => ... ), + * ... ) + * @var array + */ + var $pkginfo_cache = array(); + + /** Cache of file map. Structure: + * array( '/path/to/file' => 'package', ... ) + * @var array + */ + var $filemap_cache = array(); + + /** + * @var false|PEAR_ChannelFile + */ + var $_pearChannel; + + /** + * @var false|PEAR_ChannelFile + */ + var $_peclChannel; + + /** + * @var false|PEAR_ChannelFile + */ + var $_docChannel; + + /** + * @var PEAR_DependencyDB + */ + var $_dependencyDB; + + /** + * @var PEAR_Config + */ + var $_config; + + /** + * PEAR_Registry constructor. + * + * @param string (optional) PEAR install directory (for .php files) + * @param PEAR_ChannelFile PEAR_ChannelFile object representing the PEAR channel, if + * default values are not desired. Only used the very first time a PEAR + * repository is initialized + * @param PEAR_ChannelFile PEAR_ChannelFile object representing the PECL channel, if + * default values are not desired. Only used the very first time a PEAR + * repository is initialized + * + * @access public + */ + function PEAR_Registry($pear_install_dir = PEAR_INSTALL_DIR, $pear_channel = false, + $pecl_channel = false) + { + parent::PEAR(); + $this->setInstallDir($pear_install_dir); + $this->_pearChannel = $pear_channel; + $this->_peclChannel = $pecl_channel; + $this->_config = false; + } + + function setInstallDir($pear_install_dir = PEAR_INSTALL_DIR) + { + $ds = DIRECTORY_SEPARATOR; + $this->install_dir = $pear_install_dir; + $this->channelsdir = $pear_install_dir.$ds.'.channels'; + $this->statedir = $pear_install_dir.$ds.'.registry'; + $this->filemap = $pear_install_dir.$ds.'.filemap'; + $this->lockfile = $pear_install_dir.$ds.'.lock'; + } + + function hasWriteAccess() + { + if (!file_exists($this->install_dir)) { + $dir = $this->install_dir; + while ($dir && $dir != '.') { + $olddir = $dir; + $dir = dirname($dir); + if ($dir != '.' && file_exists($dir)) { + if (is_writeable($dir)) { + return true; + } + + return false; + } + + if ($dir == $olddir) { // this can happen in safe mode + return @is_writable($dir); + } + } + + return false; + } + + return is_writeable($this->install_dir); + } + + function setConfig(&$config, $resetInstallDir = true) + { + $this->_config = &$config; + if ($resetInstallDir) { + $this->setInstallDir($config->get('php_dir')); + } + } + + function _initializeChannelDirs() + { + static $running = false; + if (!$running) { + $running = true; + $ds = DIRECTORY_SEPARATOR; + if (!is_dir($this->channelsdir) || + !file_exists($this->channelsdir . $ds . 'pear.php.net.reg') || + !file_exists($this->channelsdir . $ds . 'pecl.php.net.reg') || + !file_exists($this->channelsdir . $ds . 'doc.php.net.reg') || + !file_exists($this->channelsdir . $ds . '__uri.reg')) { + if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) { + $pear_channel = $this->_pearChannel; + if (!is_a($pear_channel, 'PEAR_ChannelFile') || !$pear_channel->validate()) { + if (!class_exists('PEAR_ChannelFile')) { + require_once 'PEAR/ChannelFile.php'; + } + + $pear_channel = new PEAR_ChannelFile; + $pear_channel->setAlias('pear'); + $pear_channel->setServer('pear.php.net'); + $pear_channel->setSummary('PHP Extension and Application Repository'); + $pear_channel->setDefaultPEARProtocols(); + $pear_channel->setBaseURL('REST1.0', 'http://pear.php.net/rest/'); + $pear_channel->setBaseURL('REST1.1', 'http://pear.php.net/rest/'); + $pear_channel->setBaseURL('REST1.3', 'http://pear.php.net/rest/'); + //$pear_channel->setBaseURL('REST1.4', 'http://pear.php.net/rest/'); + } else { + $pear_channel->setServer('pear.php.net'); + $pear_channel->setAlias('pear'); + } + + $pear_channel->validate(); + $this->_addChannel($pear_channel); + } + + if (!file_exists($this->channelsdir . $ds . 'pecl.php.net.reg')) { + $pecl_channel = $this->_peclChannel; + if (!is_a($pecl_channel, 'PEAR_ChannelFile') || !$pecl_channel->validate()) { + if (!class_exists('PEAR_ChannelFile')) { + require_once 'PEAR/ChannelFile.php'; + } + + $pecl_channel = new PEAR_ChannelFile; + $pecl_channel->setAlias('pecl'); + $pecl_channel->setServer('pecl.php.net'); + $pecl_channel->setSummary('PHP Extension Community Library'); + $pecl_channel->setDefaultPEARProtocols(); + $pecl_channel->setBaseURL('REST1.0', 'http://pecl.php.net/rest/'); + $pecl_channel->setBaseURL('REST1.1', 'http://pecl.php.net/rest/'); + $pecl_channel->setValidationPackage('PEAR_Validator_PECL', '1.0'); + } else { + $pecl_channel->setServer('pecl.php.net'); + $pecl_channel->setAlias('pecl'); + } + + $pecl_channel->validate(); + $this->_addChannel($pecl_channel); + } + + if (!file_exists($this->channelsdir . $ds . 'doc.php.net.reg')) { + $doc_channel = $this->_docChannel; + if (!is_a($doc_channel, 'PEAR_ChannelFile') || !$doc_channel->validate()) { + if (!class_exists('PEAR_ChannelFile')) { + require_once 'PEAR/ChannelFile.php'; + } + + $doc_channel = new PEAR_ChannelFile; + $doc_channel->setAlias('phpdocs'); + $doc_channel->setServer('doc.php.net'); + $doc_channel->setSummary('PHP Documentation Team'); + $doc_channel->setDefaultPEARProtocols(); + $doc_channel->setBaseURL('REST1.0', 'http://doc.php.net/rest/'); + $doc_channel->setBaseURL('REST1.1', 'http://doc.php.net/rest/'); + $doc_channel->setBaseURL('REST1.3', 'http://doc.php.net/rest/'); + } else { + $doc_channel->setServer('doc.php.net'); + $doc_channel->setAlias('doc'); + } + + $doc_channel->validate(); + $this->_addChannel($doc_channel); + } + + if (!file_exists($this->channelsdir . $ds . '__uri.reg')) { + if (!class_exists('PEAR_ChannelFile')) { + require_once 'PEAR/ChannelFile.php'; + } + + $private = new PEAR_ChannelFile; + $private->setName('__uri'); + $private->setDefaultPEARProtocols(); + $private->setBaseURL('REST1.0', '****'); + $private->setSummary('Pseudo-channel for static packages'); + $this->_addChannel($private); + } + $this->_rebuildFileMap(); + } + + $running = false; + } + } + + function _initializeDirs() + { + $ds = DIRECTORY_SEPARATOR; + // XXX Compatibility code should be removed in the future + // rename all registry files if any to lowercase + if (!OS_WINDOWS && file_exists($this->statedir) && is_dir($this->statedir) && + $handle = opendir($this->statedir)) { + $dest = $this->statedir . $ds; + while (false !== ($file = readdir($handle))) { + if (preg_match('/^.*[A-Z].*\.reg\\z/', $file)) { + rename($dest . $file, $dest . strtolower($file)); + } + } + closedir($handle); + } + + $this->_initializeChannelDirs(); + if (!file_exists($this->filemap)) { + $this->_rebuildFileMap(); + } + $this->_initializeDepDB(); + } + + function _initializeDepDB() + { + if (!isset($this->_dependencyDB)) { + static $initializing = false; + if (!$initializing) { + $initializing = true; + if (!$this->_config) { // never used? + $file = OS_WINDOWS ? 'pear.ini' : '.pearrc'; + $this->_config = &new PEAR_Config($this->statedir . DIRECTORY_SEPARATOR . + $file); + $this->_config->setRegistry($this); + $this->_config->set('php_dir', $this->install_dir); + } + + $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->_config); + if (PEAR::isError($this->_dependencyDB)) { + // attempt to recover by removing the dep db + if (file_exists($this->_config->get('php_dir', null, 'pear.php.net') . + DIRECTORY_SEPARATOR . '.depdb')) { + @unlink($this->_config->get('php_dir', null, 'pear.php.net') . + DIRECTORY_SEPARATOR . '.depdb'); + } + + $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->_config); + if (PEAR::isError($this->_dependencyDB)) { + echo $this->_dependencyDB->getMessage(); + echo 'Unrecoverable error'; + exit(1); + } + } + + $initializing = false; + } + } + } + + /** + * PEAR_Registry destructor. Makes sure no locks are forgotten. + * + * @access private + */ + function _PEAR_Registry() + { + parent::_PEAR(); + if (is_resource($this->lock_fp)) { + $this->_unlock(); + } + } + + /** + * Make sure the directory where we keep registry files exists. + * + * @return bool TRUE if directory exists, FALSE if it could not be + * created + * + * @access private + */ + function _assertStateDir($channel = false) + { + if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') { + return $this->_assertChannelStateDir($channel); + } + + static $init = false; + if (!file_exists($this->statedir)) { + if (!$this->hasWriteAccess()) { + return false; + } + + require_once 'System.php'; + if (!System::mkdir(array('-p', $this->statedir))) { + return $this->raiseError("could not create directory '{$this->statedir}'"); + } + $init = true; + } elseif (!is_dir($this->statedir)) { + return $this->raiseError('Cannot create directory ' . $this->statedir . ', ' . + 'it already exists and is not a directory'); + } + + $ds = DIRECTORY_SEPARATOR; + if (!file_exists($this->channelsdir)) { + if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg') || + !file_exists($this->channelsdir . $ds . 'pecl.php.net.reg') || + !file_exists($this->channelsdir . $ds . 'doc.php.net.reg') || + !file_exists($this->channelsdir . $ds . '__uri.reg')) { + $init = true; + } + } elseif (!is_dir($this->channelsdir)) { + return $this->raiseError('Cannot create directory ' . $this->channelsdir . ', ' . + 'it already exists and is not a directory'); + } + + if ($init) { + static $running = false; + if (!$running) { + $running = true; + $this->_initializeDirs(); + $running = false; + $init = false; + } + } else { + $this->_initializeDepDB(); + } + + return true; + } + + /** + * Make sure the directory where we keep registry files exists for a non-standard channel. + * + * @param string channel name + * @return bool TRUE if directory exists, FALSE if it could not be + * created + * + * @access private + */ + function _assertChannelStateDir($channel) + { + $ds = DIRECTORY_SEPARATOR; + if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') { + if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) { + $this->_initializeChannelDirs(); + } + return $this->_assertStateDir($channel); + } + + $channelDir = $this->_channelDirectoryName($channel); + if (!is_dir($this->channelsdir) || + !file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) { + $this->_initializeChannelDirs(); + } + + if (!file_exists($channelDir)) { + if (!$this->hasWriteAccess()) { + return false; + } + + require_once 'System.php'; + if (!System::mkdir(array('-p', $channelDir))) { + return $this->raiseError("could not create directory '" . $channelDir . + "'"); + } + } elseif (!is_dir($channelDir)) { + return $this->raiseError("could not create directory '" . $channelDir . + "', already exists and is not a directory"); + } + + return true; + } + + /** + * Make sure the directory where we keep registry files for channels exists + * + * @return bool TRUE if directory exists, FALSE if it could not be + * created + * + * @access private + */ + function _assertChannelDir() + { + if (!file_exists($this->channelsdir)) { + if (!$this->hasWriteAccess()) { + return false; + } + + require_once 'System.php'; + if (!System::mkdir(array('-p', $this->channelsdir))) { + return $this->raiseError("could not create directory '{$this->channelsdir}'"); + } + } elseif (!is_dir($this->channelsdir)) { + return $this->raiseError("could not create directory '{$this->channelsdir}" . + "', it already exists and is not a directory"); + } + + if (!file_exists($this->channelsdir . DIRECTORY_SEPARATOR . '.alias')) { + if (!$this->hasWriteAccess()) { + return false; + } + + require_once 'System.php'; + if (!System::mkdir(array('-p', $this->channelsdir . DIRECTORY_SEPARATOR . '.alias'))) { + return $this->raiseError("could not create directory '{$this->channelsdir}/.alias'"); + } + } elseif (!is_dir($this->channelsdir . DIRECTORY_SEPARATOR . '.alias')) { + return $this->raiseError("could not create directory '{$this->channelsdir}" . + "/.alias', it already exists and is not a directory"); + } + + return true; + } + + /** + * Get the name of the file where data for a given package is stored. + * + * @param string channel name, or false if this is a PEAR package + * @param string package name + * + * @return string registry file name + * + * @access public + */ + function _packageFileName($package, $channel = false) + { + if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') { + return $this->_channelDirectoryName($channel) . DIRECTORY_SEPARATOR . + strtolower($package) . '.reg'; + } + + return $this->statedir . DIRECTORY_SEPARATOR . strtolower($package) . '.reg'; + } + + /** + * Get the name of the file where data for a given channel is stored. + * @param string channel name + * @return string registry file name + */ + function _channelFileName($channel, $noaliases = false) + { + if (!$noaliases) { + if (file_exists($this->_getChannelAliasFileName($channel))) { + $channel = implode('', file($this->_getChannelAliasFileName($channel))); + } + } + return $this->channelsdir . DIRECTORY_SEPARATOR . str_replace('/', '_', + strtolower($channel)) . '.reg'; + } + + /** + * @param string + * @return string + */ + function _getChannelAliasFileName($alias) + { + return $this->channelsdir . DIRECTORY_SEPARATOR . '.alias' . + DIRECTORY_SEPARATOR . str_replace('/', '_', strtolower($alias)) . '.txt'; + } + + /** + * Get the name of a channel from its alias + */ + function _getChannelFromAlias($channel) + { + if (!$this->_channelExists($channel)) { + if ($channel == 'pear.php.net') { + return 'pear.php.net'; + } + + if ($channel == 'pecl.php.net') { + return 'pecl.php.net'; + } + + if ($channel == 'doc.php.net') { + return 'doc.php.net'; + } + + if ($channel == '__uri') { + return '__uri'; + } + + return false; + } + + $channel = strtolower($channel); + if (file_exists($this->_getChannelAliasFileName($channel))) { + // translate an alias to an actual channel + return implode('', file($this->_getChannelAliasFileName($channel))); + } + + return $channel; + } + + /** + * Get the alias of a channel from its alias or its name + */ + function _getAlias($channel) + { + if (!$this->_channelExists($channel)) { + if ($channel == 'pear.php.net') { + return 'pear'; + } + + if ($channel == 'pecl.php.net') { + return 'pecl'; + } + + if ($channel == 'doc.php.net') { + return 'phpdocs'; + } + + return false; + } + + $channel = $this->_getChannel($channel); + if (PEAR::isError($channel)) { + return $channel; + } + + return $channel->getAlias(); + } + + /** + * Get the name of the file where data for a given package is stored. + * + * @param string channel name, or false if this is a PEAR package + * @param string package name + * + * @return string registry file name + * + * @access public + */ + function _channelDirectoryName($channel) + { + if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') { + return $this->statedir; + } + + $ch = $this->_getChannelFromAlias($channel); + if (!$ch) { + $ch = $channel; + } + + return $this->statedir . DIRECTORY_SEPARATOR . strtolower('.channel.' . + str_replace('/', '_', $ch)); + } + + function _openPackageFile($package, $mode, $channel = false) + { + if (!$this->_assertStateDir($channel)) { + return null; + } + + if (!in_array($mode, array('r', 'rb')) && !$this->hasWriteAccess()) { + return null; + } + + $file = $this->_packageFileName($package, $channel); + if (!file_exists($file) && $mode == 'r' || $mode == 'rb') { + return null; + } + + $fp = @fopen($file, $mode); + if (!$fp) { + return null; + } + + return $fp; + } + + function _closePackageFile($fp) + { + fclose($fp); + } + + function _openChannelFile($channel, $mode) + { + if (!$this->_assertChannelDir()) { + return null; + } + + if (!in_array($mode, array('r', 'rb')) && !$this->hasWriteAccess()) { + return null; + } + + $file = $this->_channelFileName($channel); + if (!file_exists($file) && $mode == 'r' || $mode == 'rb') { + return null; + } + + $fp = @fopen($file, $mode); + if (!$fp) { + return null; + } + + return $fp; + } + + function _closeChannelFile($fp) + { + fclose($fp); + } + + function _rebuildFileMap() + { + if (!class_exists('PEAR_Installer_Role')) { + require_once 'PEAR/Installer/Role.php'; + } + + $channels = $this->_listAllPackages(); + $files = array(); + foreach ($channels as $channel => $packages) { + foreach ($packages as $package) { + $version = $this->_packageInfo($package, 'version', $channel); + $filelist = $this->_packageInfo($package, 'filelist', $channel); + if (!is_array($filelist)) { + continue; + } + + foreach ($filelist as $name => $attrs) { + if (isset($attrs['attribs'])) { + $attrs = $attrs['attribs']; + } + + // it is possible for conflicting packages in different channels to + // conflict with data files/doc files + if ($name == 'dirtree') { + continue; + } + + if (isset($attrs['role']) && !in_array($attrs['role'], + PEAR_Installer_Role::getInstallableRoles())) { + // these are not installed + continue; + } + + if (isset($attrs['role']) && !in_array($attrs['role'], + PEAR_Installer_Role::getBaseinstallRoles())) { + $attrs['baseinstalldir'] = $package; + } + + if (isset($attrs['baseinstalldir'])) { + $file = $attrs['baseinstalldir'].DIRECTORY_SEPARATOR.$name; + } else { + $file = $name; + } + + $file = preg_replace(',^/+,', '', $file); + if ($channel != 'pear.php.net') { + if (!isset($files[$attrs['role']])) { + $files[$attrs['role']] = array(); + } + $files[$attrs['role']][$file] = array(strtolower($channel), + strtolower($package)); + } else { + if (!isset($files[$attrs['role']])) { + $files[$attrs['role']] = array(); + } + $files[$attrs['role']][$file] = strtolower($package); + } + } + } + } + + + $this->_assertStateDir(); + if (!$this->hasWriteAccess()) { + return false; + } + + $fp = @fopen($this->filemap, 'wb'); + if (!$fp) { + return false; + } + + $this->filemap_cache = $files; + fwrite($fp, serialize($files)); + fclose($fp); + return true; + } + + function _readFileMap() + { + if (!file_exists($this->filemap)) { + return array(); + } + + $fp = @fopen($this->filemap, 'r'); + if (!$fp) { + return $this->raiseError('PEAR_Registry: could not open filemap "' . $this->filemap . '"', PEAR_REGISTRY_ERROR_FILE, null, null, $php_errormsg); + } + + clearstatcache(); + $rt = get_magic_quotes_runtime(); + set_magic_quotes_runtime(0); + $fsize = filesize($this->filemap); + fclose($fp); + $data = file_get_contents($this->filemap); + set_magic_quotes_runtime($rt); + $tmp = unserialize($data); + if (!$tmp && $fsize > 7) { + return $this->raiseError('PEAR_Registry: invalid filemap data', PEAR_REGISTRY_ERROR_FORMAT, null, null, $data); + } + + $this->filemap_cache = $tmp; + return true; + } + + /** + * Lock the registry. + * + * @param integer lock mode, one of LOCK_EX, LOCK_SH or LOCK_UN. + * See flock manual for more information. + * + * @return bool TRUE on success, FALSE if locking failed, or a + * PEAR error if some other error occurs (such as the + * lock file not being writable). + * + * @access private + */ + function _lock($mode = LOCK_EX) + { + if (stristr(php_uname(), 'Windows 9')) { + return true; + } + + if ($mode != LOCK_UN && is_resource($this->lock_fp)) { + // XXX does not check type of lock (LOCK_SH/LOCK_EX) + return true; + } + + if (!$this->_assertStateDir()) { + if ($mode == LOCK_EX) { + return $this->raiseError('Registry directory is not writeable by the current user'); + } + + return true; + } + + $open_mode = 'w'; + // XXX People reported problems with LOCK_SH and 'w' + if ($mode === LOCK_SH || $mode === LOCK_UN) { + if (!file_exists($this->lockfile)) { + touch($this->lockfile); + } + $open_mode = 'r'; + } + + if (!is_resource($this->lock_fp)) { + $this->lock_fp = @fopen($this->lockfile, $open_mode); + } + + if (!is_resource($this->lock_fp)) { + $this->lock_fp = null; + return $this->raiseError("could not create lock file" . + (isset($php_errormsg) ? ": " . $php_errormsg : "")); + } + + if (!(int)flock($this->lock_fp, $mode)) { + switch ($mode) { + case LOCK_SH: $str = 'shared'; break; + case LOCK_EX: $str = 'exclusive'; break; + case LOCK_UN: $str = 'unlock'; break; + default: $str = 'unknown'; break; + } + + //is resource at this point, close it on error. + fclose($this->lock_fp); + $this->lock_fp = null; + return $this->raiseError("could not acquire $str lock ($this->lockfile)", + PEAR_REGISTRY_ERROR_LOCK); + } + + return true; + } + + function _unlock() + { + $ret = $this->_lock(LOCK_UN); + if (is_resource($this->lock_fp)) { + fclose($this->lock_fp); + } + + $this->lock_fp = null; + return $ret; + } + + function _packageExists($package, $channel = false) + { + return file_exists($this->_packageFileName($package, $channel)); + } + + /** + * Determine whether a channel exists in the registry + * + * @param string Channel name + * @param bool if true, then aliases will be ignored + * @return boolean + */ + function _channelExists($channel, $noaliases = false) + { + $a = file_exists($this->_channelFileName($channel, $noaliases)); + if (!$a && $channel == 'pear.php.net') { + return true; + } + + if (!$a && $channel == 'pecl.php.net') { + return true; + } + + if (!$a && $channel == 'doc.php.net') { + return true; + } + + return $a; + } + + /** + * Determine whether a mirror exists within the deafult channel in the registry + * + * @param string Channel name + * @param string Mirror name + * + * @return boolean + */ + function _mirrorExists($channel, $mirror) + { + $data = $this->_channelInfo($channel); + if (!isset($data['servers']['mirror'])) { + return false; + } + + foreach ($data['servers']['mirror'] as $m) { + if ($m['attribs']['host'] == $mirror) { + return true; + } + } + + return false; + } + + /** + * @param PEAR_ChannelFile Channel object + * @param donotuse + * @param string Last-Modified HTTP tag from remote request + * @return boolean|PEAR_Error True on creation, false if it already exists + */ + function _addChannel($channel, $update = false, $lastmodified = false) + { + if (!is_a($channel, 'PEAR_ChannelFile')) { + return false; + } + + if (!$channel->validate()) { + return false; + } + + if (file_exists($this->_channelFileName($channel->getName()))) { + if (!$update) { + return false; + } + + $checker = $this->_getChannel($channel->getName()); + if (PEAR::isError($checker)) { + return $checker; + } + + if ($channel->getAlias() != $checker->getAlias()) { + if (file_exists($this->_getChannelAliasFileName($checker->getAlias()))) { + @unlink($this->_getChannelAliasFileName($checker->getAlias())); + } + } + } else { + if ($update && !in_array($channel->getName(), array('pear.php.net', 'pecl.php.net', 'doc.php.net'))) { + return false; + } + } + + $ret = $this->_assertChannelDir(); + if (PEAR::isError($ret)) { + return $ret; + } + + $ret = $this->_assertChannelStateDir($channel->getName()); + if (PEAR::isError($ret)) { + return $ret; + } + + if ($channel->getAlias() != $channel->getName()) { + if (file_exists($this->_getChannelAliasFileName($channel->getAlias())) && + $this->_getChannelFromAlias($channel->getAlias()) != $channel->getName()) { + $channel->setAlias($channel->getName()); + } + + if (!$this->hasWriteAccess()) { + return false; + } + + $fp = @fopen($this->_getChannelAliasFileName($channel->getAlias()), 'w'); + if (!$fp) { + return false; + } + + fwrite($fp, $channel->getName()); + fclose($fp); + } + + if (!$this->hasWriteAccess()) { + return false; + } + + $fp = @fopen($this->_channelFileName($channel->getName()), 'wb'); + if (!$fp) { + return false; + } + + $info = $channel->toArray(); + if ($lastmodified) { + $info['_lastmodified'] = $lastmodified; + } else { + $info['_lastmodified'] = date('r'); + } + + fwrite($fp, serialize($info)); + fclose($fp); + return true; + } + + /** + * Deletion fails if there are any packages installed from the channel + * @param string|PEAR_ChannelFile channel name + * @return boolean|PEAR_Error True on deletion, false if it doesn't exist + */ + function _deleteChannel($channel) + { + if (!is_string($channel)) { + if (!is_a($channel, 'PEAR_ChannelFile')) { + return false; + } + + if (!$channel->validate()) { + return false; + } + $channel = $channel->getName(); + } + + if ($this->_getChannelFromAlias($channel) == '__uri') { + return false; + } + + if ($this->_getChannelFromAlias($channel) == 'pecl.php.net') { + return false; + } + + if ($this->_getChannelFromAlias($channel) == 'doc.php.net') { + return false; + } + + if (!$this->_channelExists($channel)) { + return false; + } + + if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') { + return false; + } + + $channel = $this->_getChannelFromAlias($channel); + if ($channel == 'pear.php.net') { + return false; + } + + $test = $this->_listChannelPackages($channel); + if (count($test)) { + return false; + } + + $test = @rmdir($this->_channelDirectoryName($channel)); + if (!$test) { + return false; + } + + $file = $this->_getChannelAliasFileName($this->_getAlias($channel)); + if (file_exists($file)) { + $test = @unlink($file); + if (!$test) { + return false; + } + } + + $file = $this->_channelFileName($channel); + $ret = true; + if (file_exists($file)) { + $ret = @unlink($file); + } + + return $ret; + } + + /** + * Determine whether a channel exists in the registry + * @param string Channel Alias + * @return boolean + */ + function _isChannelAlias($alias) + { + return file_exists($this->_getChannelAliasFileName($alias)); + } + + /** + * @param string|null + * @param string|null + * @param string|null + * @return array|null + * @access private + */ + function _packageInfo($package = null, $key = null, $channel = 'pear.php.net') + { + if ($package === null) { + if ($channel === null) { + $channels = $this->_listChannels(); + $ret = array(); + foreach ($channels as $channel) { + $channel = strtolower($channel); + $ret[$channel] = array(); + $packages = $this->_listPackages($channel); + foreach ($packages as $package) { + $ret[$channel][] = $this->_packageInfo($package, null, $channel); + } + } + + return $ret; + } + + $ps = $this->_listPackages($channel); + if (!count($ps)) { + return array(); + } + return array_map(array(&$this, '_packageInfo'), + $ps, array_fill(0, count($ps), null), + array_fill(0, count($ps), $channel)); + } + + $fp = $this->_openPackageFile($package, 'r', $channel); + if ($fp === null) { + return null; + } + + $rt = get_magic_quotes_runtime(); + set_magic_quotes_runtime(0); + clearstatcache(); + $this->_closePackageFile($fp); + $data = file_get_contents($this->_packageFileName($package, $channel)); + set_magic_quotes_runtime($rt); + $data = unserialize($data); + if ($key === null) { + return $data; + } + + // compatibility for package.xml version 2.0 + if (isset($data['old'][$key])) { + return $data['old'][$key]; + } + + if (isset($data[$key])) { + return $data[$key]; + } + + return null; + } + + /** + * @param string Channel name + * @param bool whether to strictly retrieve info of channels, not just aliases + * @return array|null + */ + function _channelInfo($channel, $noaliases = false) + { + if (!$this->_channelExists($channel, $noaliases)) { + return null; + } + + $fp = $this->_openChannelFile($channel, 'r'); + if ($fp === null) { + return null; + } + + $rt = get_magic_quotes_runtime(); + set_magic_quotes_runtime(0); + clearstatcache(); + $this->_closeChannelFile($fp); + $data = file_get_contents($this->_channelFileName($channel)); + set_magic_quotes_runtime($rt); + $data = unserialize($data); + return $data; + } + + function _listChannels() + { + $channellist = array(); + if (!file_exists($this->channelsdir) || !is_dir($this->channelsdir)) { + return array('pear.php.net', 'pecl.php.net', 'doc.php.net', '__uri'); + } + + $dp = opendir($this->channelsdir); + while ($ent = readdir($dp)) { + if ($ent{0} == '.' || substr($ent, -4) != '.reg') { + continue; + } + + if ($ent == '__uri.reg') { + $channellist[] = '__uri'; + continue; + } + + $channellist[] = str_replace('_', '/', substr($ent, 0, -4)); + } + + closedir($dp); + if (!in_array('pear.php.net', $channellist)) { + $channellist[] = 'pear.php.net'; + } + + if (!in_array('pecl.php.net', $channellist)) { + $channellist[] = 'pecl.php.net'; + } + + if (!in_array('doc.php.net', $channellist)) { + $channellist[] = 'doc.php.net'; + } + + + if (!in_array('__uri', $channellist)) { + $channellist[] = '__uri'; + } + + natsort($channellist); + return $channellist; + } + + function _listPackages($channel = false) + { + if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') { + return $this->_listChannelPackages($channel); + } + + if (!file_exists($this->statedir) || !is_dir($this->statedir)) { + return array(); + } + + $pkglist = array(); + $dp = opendir($this->statedir); + if (!$dp) { + return $pkglist; + } + + while ($ent = readdir($dp)) { + if ($ent{0} == '.' || substr($ent, -4) != '.reg') { + continue; + } + + $pkglist[] = substr($ent, 0, -4); + } + closedir($dp); + return $pkglist; + } + + function _listChannelPackages($channel) + { + $pkglist = array(); + if (!file_exists($this->_channelDirectoryName($channel)) || + !is_dir($this->_channelDirectoryName($channel))) { + return array(); + } + + $dp = opendir($this->_channelDirectoryName($channel)); + if (!$dp) { + return $pkglist; + } + + while ($ent = readdir($dp)) { + if ($ent{0} == '.' || substr($ent, -4) != '.reg') { + continue; + } + $pkglist[] = substr($ent, 0, -4); + } + + closedir($dp); + return $pkglist; + } + + function _listAllPackages() + { + $ret = array(); + foreach ($this->_listChannels() as $channel) { + $ret[$channel] = $this->_listPackages($channel); + } + + return $ret; + } + + /** + * Add an installed package to the registry + * @param string package name + * @param array package info (parsed by PEAR_Common::infoFrom*() methods) + * @return bool success of saving + * @access private + */ + function _addPackage($package, $info) + { + if ($this->_packageExists($package)) { + return false; + } + + $fp = $this->_openPackageFile($package, 'wb'); + if ($fp === null) { + return false; + } + + $info['_lastmodified'] = time(); + fwrite($fp, serialize($info)); + $this->_closePackageFile($fp); + if (isset($info['filelist'])) { + $this->_rebuildFileMap(); + } + + return true; + } + + /** + * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 + * @return bool + * @access private + */ + function _addPackage2($info) + { + if (!is_a($info, 'PEAR_PackageFile_v1') && !is_a($info, 'PEAR_PackageFile_v2')) { + return false; + } + + if (!$info->validate()) { + if (class_exists('PEAR_Common')) { + $ui = PEAR_Frontend::singleton(); + if ($ui) { + foreach ($info->getValidationWarnings() as $err) { + $ui->log($err['message'], true); + } + } + } + return false; + } + + $channel = $info->getChannel(); + $package = $info->getPackage(); + $save = $info; + if ($this->_packageExists($package, $channel)) { + return false; + } + + if (!$this->_channelExists($channel, true)) { + return false; + } + + $info = $info->toArray(true); + if (!$info) { + return false; + } + + $fp = $this->_openPackageFile($package, 'wb', $channel); + if ($fp === null) { + return false; + } + + $info['_lastmodified'] = time(); + fwrite($fp, serialize($info)); + $this->_closePackageFile($fp); + $this->_rebuildFileMap(); + return true; + } + + /** + * @param string Package name + * @param array parsed package.xml 1.0 + * @param bool this parameter is only here for BC. Don't use it. + * @access private + */ + function _updatePackage($package, $info, $merge = true) + { + $oldinfo = $this->_packageInfo($package); + if (empty($oldinfo)) { + return false; + } + + $fp = $this->_openPackageFile($package, 'w'); + if ($fp === null) { + return false; + } + + if (is_object($info)) { + $info = $info->toArray(); + } + $info['_lastmodified'] = time(); + + $newinfo = $info; + if ($merge) { + $info = array_merge($oldinfo, $info); + } else { + $diff = $info; + } + + fwrite($fp, serialize($info)); + $this->_closePackageFile($fp); + if (isset($newinfo['filelist'])) { + $this->_rebuildFileMap(); + } + + return true; + } + + /** + * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 + * @return bool + * @access private + */ + function _updatePackage2($info) + { + if (!$this->_packageExists($info->getPackage(), $info->getChannel())) { + return false; + } + + $fp = $this->_openPackageFile($info->getPackage(), 'w', $info->getChannel()); + if ($fp === null) { + return false; + } + + $save = $info; + $info = $save->getArray(true); + $info['_lastmodified'] = time(); + fwrite($fp, serialize($info)); + $this->_closePackageFile($fp); + $this->_rebuildFileMap(); + return true; + } + + /** + * @param string Package name + * @param string Channel name + * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|null + * @access private + */ + function &_getPackage($package, $channel = 'pear.php.net') + { + $info = $this->_packageInfo($package, null, $channel); + if ($info === null) { + return $info; + } + + $a = $this->_config; + if (!$a) { + $this->_config = &new PEAR_Config; + $this->_config->set('php_dir', $this->statedir); + } + + if (!class_exists('PEAR_PackageFile')) { + require_once 'PEAR/PackageFile.php'; + } + + $pkg = &new PEAR_PackageFile($this->_config); + $pf = &$pkg->fromArray($info); + return $pf; + } + + /** + * @param string channel name + * @param bool whether to strictly retrieve channel names + * @return PEAR_ChannelFile|PEAR_Error + * @access private + */ + function &_getChannel($channel, $noaliases = false) + { + $ch = false; + if ($this->_channelExists($channel, $noaliases)) { + $chinfo = $this->_channelInfo($channel, $noaliases); + if ($chinfo) { + if (!class_exists('PEAR_ChannelFile')) { + require_once 'PEAR/ChannelFile.php'; + } + + $ch = &PEAR_ChannelFile::fromArrayWithErrors($chinfo); + } + } + + if ($ch) { + if ($ch->validate()) { + return $ch; + } + + foreach ($ch->getErrors(true) as $err) { + $message = $err['message'] . "\n"; + } + + $ch = PEAR::raiseError($message); + return $ch; + } + + if ($this->_getChannelFromAlias($channel) == 'pear.php.net') { + // the registry is not properly set up, so use defaults + if (!class_exists('PEAR_ChannelFile')) { + require_once 'PEAR/ChannelFile.php'; + } + + $pear_channel = new PEAR_ChannelFile; + $pear_channel->setServer('pear.php.net'); + $pear_channel->setAlias('pear'); + $pear_channel->setSummary('PHP Extension and Application Repository'); + $pear_channel->setDefaultPEARProtocols(); + $pear_channel->setBaseURL('REST1.0', 'http://pear.php.net/rest/'); + $pear_channel->setBaseURL('REST1.1', 'http://pear.php.net/rest/'); + $pear_channel->setBaseURL('REST1.3', 'http://pear.php.net/rest/'); + return $pear_channel; + } + + if ($this->_getChannelFromAlias($channel) == 'pecl.php.net') { + // the registry is not properly set up, so use defaults + if (!class_exists('PEAR_ChannelFile')) { + require_once 'PEAR/ChannelFile.php'; + } + $pear_channel = new PEAR_ChannelFile; + $pear_channel->setServer('pecl.php.net'); + $pear_channel->setAlias('pecl'); + $pear_channel->setSummary('PHP Extension Community Library'); + $pear_channel->setDefaultPEARProtocols(); + $pear_channel->setBaseURL('REST1.0', 'http://pecl.php.net/rest/'); + $pear_channel->setBaseURL('REST1.1', 'http://pecl.php.net/rest/'); + $pear_channel->setValidationPackage('PEAR_Validator_PECL', '1.0'); + return $pear_channel; + } + + if ($this->_getChannelFromAlias($channel) == 'doc.php.net') { + // the registry is not properly set up, so use defaults + if (!class_exists('PEAR_ChannelFile')) { + require_once 'PEAR/ChannelFile.php'; + } + + $doc_channel = new PEAR_ChannelFile; + $doc_channel->setServer('doc.php.net'); + $doc_channel->setAlias('phpdocs'); + $doc_channel->setSummary('PHP Documentation Team'); + $doc_channel->setDefaultPEARProtocols(); + $doc_channel->setBaseURL('REST1.0', 'http://doc.php.net/rest/'); + $doc_channel->setBaseURL('REST1.1', 'http://doc.php.net/rest/'); + $doc_channel->setBaseURL('REST1.3', 'http://doc.php.net/rest/'); + return $doc_channel; + } + + + if ($this->_getChannelFromAlias($channel) == '__uri') { + // the registry is not properly set up, so use defaults + if (!class_exists('PEAR_ChannelFile')) { + require_once 'PEAR/ChannelFile.php'; + } + + $private = new PEAR_ChannelFile; + $private->setName('__uri'); + $private->setDefaultPEARProtocols(); + $private->setBaseURL('REST1.0', '****'); + $private->setSummary('Pseudo-channel for static packages'); + return $private; + } + + return $ch; + } + + /** + * @param string Package name + * @param string Channel name + * @return bool + */ + function packageExists($package, $channel = 'pear.php.net') + { + if (PEAR::isError($e = $this->_lock(LOCK_SH))) { + return $e; + } + $ret = $this->_packageExists($package, $channel); + $this->_unlock(); + return $ret; + } + + /** + * @param string channel name + * @param bool if true, then aliases will be ignored + * @return bool + */ + function channelExists($channel, $noaliases = false) + { + if (PEAR::isError($e = $this->_lock(LOCK_SH))) { + return $e; + } + $ret = $this->_channelExists($channel, $noaliases); + $this->_unlock(); + return $ret; + } + + /** + * @param string channel name mirror is in + * @param string mirror name + * + * @return bool + */ + function mirrorExists($channel, $mirror) + { + if (PEAR::isError($e = $this->_lock(LOCK_SH))) { + return $e; + } + + $ret = $this->_mirrorExists($channel, $mirror); + $this->_unlock(); + return $ret; + } + + /** + * Determines whether the parameter is an alias of a channel + * @param string + * @return bool + */ + function isAlias($alias) + { + if (PEAR::isError($e = $this->_lock(LOCK_SH))) { + return $e; + } + $ret = $this->_isChannelAlias($alias); + $this->_unlock(); + return $ret; + } + + /** + * @param string|null + * @param string|null + * @param string + * @return array|null + */ + function packageInfo($package = null, $key = null, $channel = 'pear.php.net') + { + if (PEAR::isError($e = $this->_lock(LOCK_SH))) { + return $e; + } + $ret = $this->_packageInfo($package, $key, $channel); + $this->_unlock(); + return $ret; + } + + /** + * Retrieve a raw array of channel data. + * + * Do not use this, instead use {@link getChannel()} for normal + * operations. Array structure is undefined in this method + * @param string channel name + * @param bool whether to strictly retrieve information only on non-aliases + * @return array|null|PEAR_Error + */ + function channelInfo($channel = null, $noaliases = false) + { + if (PEAR::isError($e = $this->_lock(LOCK_SH))) { + return $e; + } + $ret = $this->_channelInfo($channel, $noaliases); + $this->_unlock(); + return $ret; + } + + /** + * @param string + */ + function channelName($channel) + { + if (PEAR::isError($e = $this->_lock(LOCK_SH))) { + return $e; + } + $ret = $this->_getChannelFromAlias($channel); + $this->_unlock(); + return $ret; + } + + /** + * @param string + */ + function channelAlias($channel) + { + if (PEAR::isError($e = $this->_lock(LOCK_SH))) { + return $e; + } + $ret = $this->_getAlias($channel); + $this->_unlock(); + return $ret; + } + + function listPackages($channel = false) + { + if (PEAR::isError($e = $this->_lock(LOCK_SH))) { + return $e; + } + $ret = $this->_listPackages($channel); + $this->_unlock(); + return $ret; + } + + function listAllPackages() + { + if (PEAR::isError($e = $this->_lock(LOCK_SH))) { + return $e; + } + $ret = $this->_listAllPackages(); + $this->_unlock(); + return $ret; + } + + function listChannels() + { + if (PEAR::isError($e = $this->_lock(LOCK_SH))) { + return $e; + } + $ret = $this->_listChannels(); + $this->_unlock(); + return $ret; + } + + /** + * Add an installed package to the registry + * @param string|PEAR_PackageFile_v1|PEAR_PackageFile_v2 package name or object + * that will be passed to {@link addPackage2()} + * @param array package info (parsed by PEAR_Common::infoFrom*() methods) + * @return bool success of saving + */ + function addPackage($package, $info) + { + if (is_object($info)) { + return $this->addPackage2($info); + } + if (PEAR::isError($e = $this->_lock(LOCK_EX))) { + return $e; + } + $ret = $this->_addPackage($package, $info); + $this->_unlock(); + if ($ret) { + if (!class_exists('PEAR_PackageFile_v1')) { + require_once 'PEAR/PackageFile/v1.php'; + } + $pf = new PEAR_PackageFile_v1; + $pf->setConfig($this->_config); + $pf->fromArray($info); + $this->_dependencyDB->uninstallPackage($pf); + $this->_dependencyDB->installPackage($pf); + } + return $ret; + } + + function addPackage2($info) + { + if (!is_object($info)) { + return $this->addPackage($info['package'], $info); + } + if (PEAR::isError($e = $this->_lock(LOCK_EX))) { + return $e; + } + $ret = $this->_addPackage2($info); + $this->_unlock(); + if ($ret) { + $this->_dependencyDB->uninstallPackage($info); + $this->_dependencyDB->installPackage($info); + } + return $ret; + } + + /** + * For future expandibility purposes, separate this + * @param PEAR_ChannelFile + */ + function updateChannel($channel, $lastmodified = null) + { + if ($channel->getName() == '__uri') { + return false; + } + return $this->addChannel($channel, $lastmodified, true); + } + + /** + * Deletion fails if there are any packages installed from the channel + * @param string|PEAR_ChannelFile channel name + * @return boolean|PEAR_Error True on deletion, false if it doesn't exist + */ + function deleteChannel($channel) + { + if (PEAR::isError($e = $this->_lock(LOCK_EX))) { + return $e; + } + + $ret = $this->_deleteChannel($channel); + $this->_unlock(); + if ($ret && is_a($this->_config, 'PEAR_Config')) { + $this->_config->setChannels($this->listChannels()); + } + + return $ret; + } + + /** + * @param PEAR_ChannelFile Channel object + * @param string Last-Modified header from HTTP for caching + * @return boolean|PEAR_Error True on creation, false if it already exists + */ + function addChannel($channel, $lastmodified = false, $update = false) + { + if (!is_a($channel, 'PEAR_ChannelFile') || !$channel->validate()) { + if (!$channel->validate()) { + $msg = ''; + $errors = $channel->getErrors(); + foreach ($errors as $error) { + $msg .= $error['message'] . ', '; + } + return PEAR::raiseError(substr($msg, 0, -2)); + } + return false; + } + + if (PEAR::isError($e = $this->_lock(LOCK_EX))) { + return $e; + } + + $ret = $this->_addChannel($channel, $update, $lastmodified); + $this->_unlock(); + if (!$update && $ret && is_a($this->_config, 'PEAR_Config')) { + $this->_config->setChannels($this->listChannels()); + } + + return $ret; + } + + function deletePackage($package, $channel = 'pear.php.net') + { + if (PEAR::isError($e = $this->_lock(LOCK_EX))) { + return $e; + } + + $file = $this->_packageFileName($package, $channel); + $ret = file_exists($file) ? @unlink($file) : false; + $this->_rebuildFileMap(); + $this->_unlock(); + $p = array('channel' => $channel, 'package' => $package); + $this->_dependencyDB->uninstallPackage($p); + return $ret; + } + + function updatePackage($package, $info, $merge = true) + { + if (is_object($info)) { + return $this->updatePackage2($info, $merge); + } + if (PEAR::isError($e = $this->_lock(LOCK_EX))) { + return $e; + } + $ret = $this->_updatePackage($package, $info, $merge); + $this->_unlock(); + if ($ret) { + if (!class_exists('PEAR_PackageFile_v1')) { + require_once 'PEAR/PackageFile/v1.php'; + } + $pf = new PEAR_PackageFile_v1; + $pf->setConfig($this->_config); + $pf->fromArray($this->packageInfo($package)); + $this->_dependencyDB->uninstallPackage($pf); + $this->_dependencyDB->installPackage($pf); + } + return $ret; + } + + function updatePackage2($info) + { + + if (!is_object($info)) { + return $this->updatePackage($info['package'], $info, $merge); + } + + if (!$info->validate(PEAR_VALIDATE_DOWNLOADING)) { + return false; + } + + if (PEAR::isError($e = $this->_lock(LOCK_EX))) { + return $e; + } + + $ret = $this->_updatePackage2($info); + $this->_unlock(); + if ($ret) { + $this->_dependencyDB->uninstallPackage($info); + $this->_dependencyDB->installPackage($info); + } + + return $ret; + } + + /** + * @param string channel name + * @param bool whether to strictly return raw channels (no aliases) + * @return PEAR_ChannelFile|PEAR_Error + */ + function &getChannel($channel, $noaliases = false) + { + if (PEAR::isError($e = $this->_lock(LOCK_SH))) { + return $e; + } + $ret = &$this->_getChannel($channel, $noaliases); + $this->_unlock(); + if (!$ret) { + return PEAR::raiseError('Unknown channel: ' . $channel); + } + return $ret; + } + + /** + * @param string package name + * @param string channel name + * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|null + */ + function &getPackage($package, $channel = 'pear.php.net') + { + if (PEAR::isError($e = $this->_lock(LOCK_SH))) { + return $e; + } + $pf = &$this->_getPackage($package, $channel); + $this->_unlock(); + return $pf; + } + + /** + * Get PEAR_PackageFile_v[1/2] objects representing the contents of + * a dependency group that are installed. + * + * This is used at uninstall-time + * @param array + * @return array|false + */ + function getInstalledGroup($group) + { + $ret = array(); + if (isset($group['package'])) { + if (!isset($group['package'][0])) { + $group['package'] = array($group['package']); + } + foreach ($group['package'] as $package) { + $depchannel = isset($package['channel']) ? $package['channel'] : '__uri'; + $p = &$this->getPackage($package['name'], $depchannel); + if ($p) { + $save = &$p; + $ret[] = &$save; + } + } + } + if (isset($group['subpackage'])) { + if (!isset($group['subpackage'][0])) { + $group['subpackage'] = array($group['subpackage']); + } + foreach ($group['subpackage'] as $package) { + $depchannel = isset($package['channel']) ? $package['channel'] : '__uri'; + $p = &$this->getPackage($package['name'], $depchannel); + if ($p) { + $save = &$p; + $ret[] = &$save; + } + } + } + if (!count($ret)) { + return false; + } + return $ret; + } + + /** + * @param string channel name + * @return PEAR_Validate|false + */ + function &getChannelValidator($channel) + { + $chan = $this->getChannel($channel); + if (PEAR::isError($chan)) { + return $chan; + } + $val = $chan->getValidationObject(); + return $val; + } + + /** + * @param string channel name + * @return array an array of PEAR_ChannelFile objects representing every installed channel + */ + function &getChannels() + { + $ret = array(); + if (PEAR::isError($e = $this->_lock(LOCK_SH))) { + return $e; + } + foreach ($this->_listChannels() as $channel) { + $e = &$this->_getChannel($channel); + if (!$e || PEAR::isError($e)) { + continue; + } + $ret[] = $e; + } + $this->_unlock(); + return $ret; + } + + /** + * Test whether a file or set of files belongs to a package. + * + * If an array is passed in + * @param string|array file path, absolute or relative to the pear + * install dir + * @param string|array name of PEAR package or array('package' => name, 'channel' => + * channel) of a package that will be ignored + * @param string API version - 1.1 will exclude any files belonging to a package + * @param array private recursion variable + * @return array|false which package and channel the file belongs to, or an empty + * string if the file does not belong to an installed package, + * or belongs to the second parameter's package + */ + function checkFileMap($path, $package = false, $api = '1.0', $attrs = false) + { + if (is_array($path)) { + static $notempty; + if (empty($notempty)) { + if (!class_exists('PEAR_Installer_Role')) { + require_once 'PEAR/Installer/Role.php'; + } + $notempty = create_function('$a','return !empty($a);'); + } + $package = is_array($package) ? array(strtolower($package[0]), strtolower($package[1])) + : strtolower($package); + $pkgs = array(); + foreach ($path as $name => $attrs) { + if (is_array($attrs)) { + if (isset($attrs['install-as'])) { + $name = $attrs['install-as']; + } + if (!in_array($attrs['role'], PEAR_Installer_Role::getInstallableRoles())) { + // these are not installed + continue; + } + if (!in_array($attrs['role'], PEAR_Installer_Role::getBaseinstallRoles())) { + $attrs['baseinstalldir'] = is_array($package) ? $package[1] : $package; + } + if (isset($attrs['baseinstalldir'])) { + $name = $attrs['baseinstalldir'] . DIRECTORY_SEPARATOR . $name; + } + } + $pkgs[$name] = $this->checkFileMap($name, $package, $api, $attrs); + if (PEAR::isError($pkgs[$name])) { + return $pkgs[$name]; + } + } + return array_filter($pkgs, $notempty); + } + if (empty($this->filemap_cache)) { + if (PEAR::isError($e = $this->_lock(LOCK_SH))) { + return $e; + } + $err = $this->_readFileMap(); + $this->_unlock(); + if (PEAR::isError($err)) { + return $err; + } + } + if (!$attrs) { + $attrs = array('role' => 'php'); // any old call would be for PHP role only + } + if (isset($this->filemap_cache[$attrs['role']][$path])) { + if ($api >= '1.1' && $this->filemap_cache[$attrs['role']][$path] == $package) { + return false; + } + return $this->filemap_cache[$attrs['role']][$path]; + } + $l = strlen($this->install_dir); + if (substr($path, 0, $l) == $this->install_dir) { + $path = preg_replace('!^'.DIRECTORY_SEPARATOR.'+!', '', substr($path, $l)); + } + if (isset($this->filemap_cache[$attrs['role']][$path])) { + if ($api >= '1.1' && $this->filemap_cache[$attrs['role']][$path] == $package) { + return false; + } + return $this->filemap_cache[$attrs['role']][$path]; + } + return false; + } + + /** + * Force a reload of the filemap + * @since 1.5.0RC3 + */ + function flushFileMap() + { + $this->filemap_cache = null; + clearstatcache(); // ensure that the next read gets the full, current filemap + } + + /** + * Get the expected API version. Channels API is version 1.1, as it is backwards + * compatible with 1.0 + * @return string + */ + function apiVersion() + { + return '1.1'; + } + + /** + * Parse a package name, or validate a parsed package name array + * @param string|array pass in an array of format + * array( + * 'package' => 'pname', + * ['channel' => 'channame',] + * ['version' => 'version',] + * ['state' => 'state',] + * ['group' => 'groupname']) + * or a string of format + * [channel://][channame/]pname[-version|-state][/group=groupname] + * @return array|PEAR_Error + */ + function parsePackageName($param, $defaultchannel = 'pear.php.net') + { + $saveparam = $param; + if (is_array($param)) { + // convert to string for error messages + $saveparam = $this->parsedPackageNameToString($param); + // process the array + if (!isset($param['package'])) { + return PEAR::raiseError('parsePackageName(): array $param ' . + 'must contain a valid package name in index "param"', + 'package', null, null, $param); + } + if (!isset($param['uri'])) { + if (!isset($param['channel'])) { + $param['channel'] = $defaultchannel; + } + } else { + $param['channel'] = '__uri'; + } + } else { + $components = @parse_url((string) $param); + if (isset($components['scheme'])) { + if ($components['scheme'] == 'http') { + // uri package + $param = array('uri' => $param, 'channel' => '__uri'); + } elseif($components['scheme'] != 'channel') { + return PEAR::raiseError('parsePackageName(): only channel:// uris may ' . + 'be downloaded, not "' . $param . '"', 'invalid', null, null, $param); + } + } + if (!isset($components['path'])) { + return PEAR::raiseError('parsePackageName(): array $param ' . + 'must contain a valid package name in "' . $param . '"', + 'package', null, null, $param); + } + if (isset($components['host'])) { + // remove the leading "/" + $components['path'] = substr($components['path'], 1); + } + if (!isset($components['scheme'])) { + if (strpos($components['path'], '/') !== false) { + if ($components['path']{0} == '/') { + return PEAR::raiseError('parsePackageName(): this is not ' . + 'a package name, it begins with "/" in "' . $param . '"', + 'invalid', null, null, $param); + } + $parts = explode('/', $components['path']); + $components['host'] = array_shift($parts); + if (count($parts) > 1) { + $components['path'] = array_pop($parts); + $components['host'] .= '/' . implode('/', $parts); + } else { + $components['path'] = implode('/', $parts); + } + } else { + $components['host'] = $defaultchannel; + } + } else { + if (strpos($components['path'], '/')) { + $parts = explode('/', $components['path']); + $components['path'] = array_pop($parts); + $components['host'] .= '/' . implode('/', $parts); + } + } + + if (is_array($param)) { + $param['package'] = $components['path']; + } else { + $param = array( + 'package' => $components['path'] + ); + if (isset($components['host'])) { + $param['channel'] = $components['host']; + } + } + if (isset($components['fragment'])) { + $param['group'] = $components['fragment']; + } + if (isset($components['user'])) { + $param['user'] = $components['user']; + } + if (isset($components['pass'])) { + $param['pass'] = $components['pass']; + } + if (isset($components['query'])) { + parse_str($components['query'], $param['opts']); + } + // check for extension + $pathinfo = pathinfo($param['package']); + if (isset($pathinfo['extension']) && + in_array(strtolower($pathinfo['extension']), array('tgz', 'tar'))) { + $param['extension'] = $pathinfo['extension']; + $param['package'] = substr($pathinfo['basename'], 0, + strlen($pathinfo['basename']) - 4); + } + // check for version + if (strpos($param['package'], '-')) { + $test = explode('-', $param['package']); + if (count($test) != 2) { + return PEAR::raiseError('parsePackageName(): only one version/state ' . + 'delimiter "-" is allowed in "' . $saveparam . '"', + 'version', null, null, $param); + } + list($param['package'], $param['version']) = $test; + } + } + // validation + $info = $this->channelExists($param['channel']); + if (PEAR::isError($info)) { + return $info; + } + if (!$info) { + return PEAR::raiseError('unknown channel "' . $param['channel'] . + '" in "' . $saveparam . '"', 'channel', null, null, $param); + } + $chan = $this->getChannel($param['channel']); + if (PEAR::isError($chan)) { + return $chan; + } + if (!$chan) { + return PEAR::raiseError("Exception: corrupt registry, could not " . + "retrieve channel " . $param['channel'] . " information", + 'registry', null, null, $param); + } + $param['channel'] = $chan->getName(); + $validate = $chan->getValidationObject(); + $vpackage = $chan->getValidationPackage(); + // validate package name + if (!$validate->validPackageName($param['package'], $vpackage['_content'])) { + return PEAR::raiseError('parsePackageName(): invalid package name "' . + $param['package'] . '" in "' . $saveparam . '"', + 'package', null, null, $param); + } + if (isset($param['group'])) { + if (!PEAR_Validate::validGroupName($param['group'])) { + return PEAR::raiseError('parsePackageName(): dependency group "' . $param['group'] . + '" is not a valid group name in "' . $saveparam . '"', 'group', null, null, + $param); + } + } + if (isset($param['state'])) { + if (!in_array(strtolower($param['state']), $validate->getValidStates())) { + return PEAR::raiseError('parsePackageName(): state "' . $param['state'] + . '" is not a valid state in "' . $saveparam . '"', + 'state', null, null, $param); + } + } + if (isset($param['version'])) { + if (isset($param['state'])) { + return PEAR::raiseError('parsePackageName(): cannot contain both ' . + 'a version and a stability (state) in "' . $saveparam . '"', + 'version/state', null, null, $param); + } + // check whether version is actually a state + if (in_array(strtolower($param['version']), $validate->getValidStates())) { + $param['state'] = strtolower($param['version']); + unset($param['version']); + } else { + if (!$validate->validVersion($param['version'])) { + return PEAR::raiseError('parsePackageName(): "' . $param['version'] . + '" is neither a valid version nor a valid state in "' . + $saveparam . '"', 'version/state', null, null, $param); + } + } + } + return $param; + } + + /** + * @param array + * @return string + */ + function parsedPackageNameToString($parsed, $brief = false) + { + if (is_string($parsed)) { + return $parsed; + } + if (is_object($parsed)) { + $p = $parsed; + $parsed = array( + 'package' => $p->getPackage(), + 'channel' => $p->getChannel(), + 'version' => $p->getVersion(), + ); + } + if (isset($parsed['uri'])) { + return $parsed['uri']; + } + if ($brief) { + if ($channel = $this->channelAlias($parsed['channel'])) { + return $channel . '/' . $parsed['package']; + } + } + $upass = ''; + if (isset($parsed['user'])) { + $upass = $parsed['user']; + if (isset($parsed['pass'])) { + $upass .= ':' . $parsed['pass']; + } + $upass = "$upass@"; + } + $ret = 'channel://' . $upass . $parsed['channel'] . '/' . $parsed['package']; + if (isset($parsed['version']) || isset($parsed['state'])) { + $ver = isset($parsed['version']) ? $parsed['version'] : ''; + $ver .= isset($parsed['state']) ? $parsed['state'] : ''; + $ret .= '-' . $ver; + } + if (isset($parsed['extension'])) { + $ret .= '.' . $parsed['extension']; + } + if (isset($parsed['opts'])) { + $ret .= '?'; + foreach ($parsed['opts'] as $name => $value) { + $parsed['opts'][$name] = "$name=$value"; + } + $ret .= implode('&', $parsed['opts']); + } + if (isset($parsed['group'])) { + $ret .= '#' . $parsed['group']; + } + return $ret; + } +} diff --git a/includes/pear/PEAR/RunTest.php b/includes/pear/PEAR/RunTest.php new file mode 100644 index 0000000..a916af4 --- /dev/null +++ b/includes/pear/PEAR/RunTest.php @@ -0,0 +1,973 @@ + + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.3.3 + */ + +/** + * for error handling + */ +require_once 'PEAR.php'; +require_once 'PEAR/Config.php'; + +define('DETAILED', 1); +putenv("PHP_PEAR_RUNTESTS=1"); + +/** + * Simplified version of PHP's test suite + * + * Try it with: + * + * $ php -r 'include "../PEAR/RunTest.php"; $t=new PEAR_RunTest; $o=$t->run("./pear_system.phpt");print_r($o);' + * + * + * @category pear + * @package PEAR + * @author Tomas V.V.Cox + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.3.3 + */ +class PEAR_RunTest +{ + var $_headers = array(); + var $_logger; + var $_options; + var $_php; + var $tests_count; + var $xdebug_loaded; + /** + * Saved value of php executable, used to reset $_php when we + * have a test that uses cgi + * + * @var unknown_type + */ + var $_savephp; + var $ini_overwrites = array( + 'output_handler=', + 'open_basedir=', + 'safe_mode=0', + 'disable_functions=', + 'output_buffering=Off', + 'display_errors=1', + 'log_errors=0', + 'html_errors=0', + 'track_errors=1', + 'report_memleaks=0', + 'report_zend_debug=0', + 'docref_root=', + 'docref_ext=.html', + 'error_prepend_string=', + 'error_append_string=', + 'auto_prepend_file=', + 'auto_append_file=', + 'magic_quotes_runtime=0', + 'xdebug.default_enable=0', + 'allow_url_fopen=1', + ); + + /** + * An object that supports the PEAR_Common->log() signature, or null + * @param PEAR_Common|null + */ + function PEAR_RunTest($logger = null, $options = array()) + { + if (!defined('E_DEPRECATED')) { + define('E_DEPRECATED', 0); + } + if (!defined('E_STRICT')) { + define('E_STRICT', 0); + } + $this->ini_overwrites[] = 'error_reporting=' . (E_ALL & ~(E_DEPRECATED | E_STRICT)); + if (is_null($logger)) { + require_once 'PEAR/Common.php'; + $logger = new PEAR_Common; + } + $this->_logger = $logger; + $this->_options = $options; + + $conf = &PEAR_Config::singleton(); + $this->_php = $conf->get('php_bin'); + } + + /** + * Taken from php-src/run-tests.php + * + * @param string $commandline command name + * @param array $env + * @param string $stdin standard input to pass to the command + * @return unknown + */ + function system_with_timeout($commandline, $env = null, $stdin = null) + { + $data = ''; + if (version_compare(phpversion(), '5.0.0', '<')) { + $proc = proc_open($commandline, array( + 0 => array('pipe', 'r'), + 1 => array('pipe', 'w'), + 2 => array('pipe', 'w') + ), $pipes); + } else { + $proc = proc_open($commandline, array( + 0 => array('pipe', 'r'), + 1 => array('pipe', 'w'), + 2 => array('pipe', 'w') + ), $pipes, null, $env, array('suppress_errors' => true)); + } + + if (!$proc) { + return false; + } + + if (is_string($stdin)) { + fwrite($pipes[0], $stdin); + } + fclose($pipes[0]); + + while (true) { + /* hide errors from interrupted syscalls */ + $r = $pipes; + $e = $w = null; + $n = @stream_select($r, $w, $e, 60); + + if ($n === 0) { + /* timed out */ + $data .= "\n ** ERROR: process timed out **\n"; + proc_terminate($proc); + return array(1234567890, $data); + } else if ($n > 0) { + $line = fread($pipes[1], 8192); + if (strlen($line) == 0) { + /* EOF */ + break; + } + $data .= $line; + } + } + if (function_exists('proc_get_status')) { + $stat = proc_get_status($proc); + if ($stat['signaled']) { + $data .= "\nTermsig=".$stat['stopsig']; + } + } + $code = proc_close($proc); + if (function_exists('proc_get_status')) { + $code = $stat['exitcode']; + } + return array($code, $data); + } + + /** + * Turns a PHP INI string into an array + * + * Turns -d "include_path=/foo/bar" into this: + * array( + * 'include_path' => array( + * 'operator' => '-d', + * 'value' => '/foo/bar', + * ) + * ) + * Works both with quotes and without + * + * @param string an PHP INI string, -d "include_path=/foo/bar" + * @return array + */ + function iniString2array($ini_string) + { + if (!$ini_string) { + return array(); + } + $split = preg_split('/[\s]|=/', $ini_string, -1, PREG_SPLIT_NO_EMPTY); + $key = $split[1][0] == '"' ? substr($split[1], 1) : $split[1]; + $value = $split[2][strlen($split[2]) - 1] == '"' ? substr($split[2], 0, -1) : $split[2]; + // FIXME review if this is really the struct to go with + $array = array($key => array('operator' => $split[0], 'value' => $value)); + return $array; + } + + function settings2array($settings, $ini_settings) + { + foreach ($settings as $setting) { + if (strpos($setting, '=') !== false) { + $setting = explode('=', $setting, 2); + $name = trim(strtolower($setting[0])); + $value = trim($setting[1]); + $ini_settings[$name] = $value; + } + } + return $ini_settings; + } + + function settings2params($ini_settings) + { + $settings = ''; + foreach ($ini_settings as $name => $value) { + if (is_array($value)) { + $operator = $value['operator']; + $value = $value['value']; + } else { + $operator = '-d'; + } + $value = addslashes($value); + $settings .= " $operator \"$name=$value\""; + } + return $settings; + } + + function _preparePhpBin($php, $file, $ini_settings) + { + $file = escapeshellarg($file); + // This was fixed in php 5.3 and is not needed after that + if (OS_WINDOWS && version_compare(PHP_VERSION, '5.3', '<')) { + $cmd = '"'.escapeshellarg($php).' '.$ini_settings.' -f ' . $file .'"'; + } else { + $cmd = $php . $ini_settings . ' -f ' . $file; + } + + return $cmd; + } + + function runPHPUnit($file, $ini_settings = '') + { + if (!file_exists($file) && file_exists(getcwd() . DIRECTORY_SEPARATOR . $file)) { + $file = realpath(getcwd() . DIRECTORY_SEPARATOR . $file); + } elseif (file_exists($file)) { + $file = realpath($file); + } + + $cmd = $this->_preparePhpBin($this->_php, $file, $ini_settings); + if (isset($this->_logger)) { + $this->_logger->log(2, 'Running command "' . $cmd . '"'); + } + + $savedir = getcwd(); // in case the test moves us around + chdir(dirname($file)); + echo `$cmd`; + chdir($savedir); + return 'PASSED'; // we have no way of knowing this information so assume passing + } + + /** + * Runs an individual test case. + * + * @param string The filename of the test + * @param array|string INI settings to be applied to the test run + * @param integer Number what the current running test is of the + * whole test suite being runned. + * + * @return string|object Returns PASSED, WARNED, FAILED depending on how the + * test came out. + * PEAR Error when the tester it self fails + */ + function run($file, $ini_settings_cmd_line = array(), $test_number = 1) + { + if (isset($this->_savephp)) { + $this->_php = $this->_savephp; + unset($this->_savephp); + } + if (empty($this->_options['cgi'])) { + // try to see if php-cgi is in the path + $res = $this->system_with_timeout('php-cgi -v'); + if (false !== $res && !(is_array($res) && in_array($res[0], array(-1, 127)))) { + $this->_options['cgi'] = 'php-cgi'; + } + } + if (1 < $len = strlen($this->tests_count)) { + $test_number = str_pad($test_number, $len, ' ', STR_PAD_LEFT); + $test_nr = "[$test_number/$this->tests_count] "; + } else { + $test_nr = ''; + } + + $file = realpath($file); + $section_text = $this->_readFile($file); + if (PEAR::isError($section_text)) { + return $section_text; + } + + if (isset($section_text['POST_RAW']) && isset($section_text['UPLOAD'])) { + return PEAR::raiseError("Cannot contain both POST_RAW and UPLOAD in test file: $file"); + } + + $cwd = getcwd(); + + $pass_options = ''; + if (!empty($this->_options['ini'])) { + $pass_options = $this->_options['ini']; + } + + if (is_string($ini_settings_cmd_line)) { + $ini_settings_cmd_line = $this->iniString2array($ini_settings_cmd_line); + } + + $ini_settings_base = $this->settings2array($this->ini_overwrites, $ini_settings_cmd_line); + if ($section_text['INI']) { + if (strpos($section_text['INI'], '{PWD}') !== false) { + $section_text['INI'] = str_replace('{PWD}', dirname($file), $section_text['INI']); + } + $ini = preg_split( "/[\n\r]+/", $section_text['INI']); + $ini_settings_phpt_section = $this->settings2array($ini, array()); + $ini_settings_phpt_section = $this->settings2params($ini_settings_phpt_section); + } else { + $ini_settings_phpt_section = ''; + } + $ini_settings_base = $this->settings2params($ini_settings_base); + $ini_settings_all = $ini_settings_base . ' ' . $ini_settings_phpt_section; + + $shortname = str_replace($cwd . DIRECTORY_SEPARATOR, '', $file); + + $tested = trim($section_text['TEST']); + $tested.= !isset($this->_options['simple']) ? "[$shortname]" : ' '; + + if (!empty($section_text['POST']) || !empty($section_text['POST_RAW']) || + !empty($section_text['UPLOAD']) || !empty($section_text['GET']) || + !empty($section_text['COOKIE']) || !empty($section_text['EXPECTHEADERS'])) { + if (empty($this->_options['cgi'])) { + if (!isset($this->_options['quiet'])) { + $this->_logger->log(0, "SKIP $test_nr$tested (reason: --cgi option needed for this test, type 'pear help run-tests')"); + } + if (isset($this->_options['tapoutput'])) { + return array('ok', ' # skip --cgi option needed for this test, "pear help run-tests" for info'); + } + return 'SKIPPED'; + } + $this->_savephp = $this->_php; + $this->_php = $this->_options['cgi']; + } + + $temp_dir = realpath(dirname($file)); + $main_file_name = basename($file, 'phpt'); + $diff_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'diff'; + $log_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'log'; + $exp_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'exp'; + $output_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'out'; + $memcheck_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'mem'; + $temp_file = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'php'; + $temp_skipif = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'skip.php'; + $temp_clean = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'clean.php'; + $tmp_post = $temp_dir . DIRECTORY_SEPARATOR . uniqid('phpt.'); + + // unlink old test results + $this->_cleanupOldFiles($file); + + // Check if test should be skipped. + $res = $this->_runSkipIf($section_text, $temp_skipif, $tested, $ini_settings_base); + if (count($res) != 2) { + return $res; + } + $info = $res['info']; + $warn = $res['warn']; + + // We've satisfied the preconditions - run the test! + if (isset($this->_options['coverage']) && $this->xdebug_loaded) { + $xdebug_file = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'xdebug'; + $text = "\n" . 'function coverage_shutdown() {' . + "\n" . ' $xdebug = var_export(xdebug_get_code_coverage(), true);'; + if (!function_exists('file_put_contents')) { + $text .= "\n" . ' $fh = fopen(\'' . $xdebug_file . '\', "wb");' . + "\n" . ' if ($fh !== false) {' . + "\n" . ' fwrite($fh, $xdebug);' . + "\n" . ' fclose($fh);' . + "\n" . ' }'; + } else { + $text .= "\n" . ' file_put_contents(\'' . $xdebug_file . '\', $xdebug);'; + } + + // Workaround for http://pear.php.net/bugs/bug.php?id=17292 + $lines = explode("\n", $section_text['FILE']); + $numLines = count($lines); + $namespace = ''; + $coverage_shutdown = 'coverage_shutdown'; + + if ( + substr($lines[0], 0, 2) == 'save_text($temp_file, "save_text($temp_file, $section_text['FILE']); + } + + $args = $section_text['ARGS'] ? ' -- '.$section_text['ARGS'] : ''; + $cmd = $this->_preparePhpBin($this->_php, $temp_file, $ini_settings_all); + $cmd.= "$args 2>&1"; + if (isset($this->_logger)) { + $this->_logger->log(2, 'Running command "' . $cmd . '"'); + } + + // Reset environment from any previous test. + $env = $this->_resetEnv($section_text, $temp_file); + + $section_text = $this->_processUpload($section_text, $file); + if (PEAR::isError($section_text)) { + return $section_text; + } + + if (array_key_exists('POST_RAW', $section_text) && !empty($section_text['POST_RAW'])) { + $post = trim($section_text['POST_RAW']); + $raw_lines = explode("\n", $post); + + $request = ''; + $started = false; + foreach ($raw_lines as $i => $line) { + if (empty($env['CONTENT_TYPE']) && + preg_match('/^Content-Type:(.*)/i', $line, $res)) { + $env['CONTENT_TYPE'] = trim(str_replace("\r", '', $res[1])); + continue; + } + if ($started) { + $request .= "\n"; + } + $started = true; + $request .= $line; + } + + $env['CONTENT_LENGTH'] = strlen($request); + $env['REQUEST_METHOD'] = 'POST'; + + $this->save_text($tmp_post, $request); + $cmd = "$this->_php $pass_options $ini_settings_all \"$temp_file\" 2>&1 < \"$tmp_post\""; + } elseif (array_key_exists('POST', $section_text) && !empty($section_text['POST'])) { + $post = trim($section_text['POST']); + $this->save_text($tmp_post, $post); + $content_length = strlen($post); + + $env['REQUEST_METHOD'] = 'POST'; + $env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'; + $env['CONTENT_LENGTH'] = $content_length; + + $cmd = "$this->_php $pass_options $ini_settings_all \"$temp_file\" 2>&1 < \"$tmp_post\""; + } else { + $env['REQUEST_METHOD'] = 'GET'; + $env['CONTENT_TYPE'] = ''; + $env['CONTENT_LENGTH'] = ''; + } + + if (OS_WINDOWS && isset($section_text['RETURNS'])) { + ob_start(); + system($cmd, $return_value); + $out = ob_get_contents(); + ob_end_clean(); + $section_text['RETURNS'] = (int) trim($section_text['RETURNS']); + $returnfail = ($return_value != $section_text['RETURNS']); + } else { + $returnfail = false; + $stdin = isset($section_text['STDIN']) ? $section_text['STDIN'] : null; + $out = $this->system_with_timeout($cmd, $env, $stdin); + $return_value = $out[0]; + $out = $out[1]; + } + + $output = preg_replace('/\r\n/', "\n", trim($out)); + + if (isset($tmp_post) && realpath($tmp_post) && file_exists($tmp_post)) { + @unlink(realpath($tmp_post)); + } + chdir($cwd); // in case the test moves us around + + $this->_testCleanup($section_text, $temp_clean); + + /* when using CGI, strip the headers from the output */ + $output = $this->_stripHeadersCGI($output); + + if (isset($section_text['EXPECTHEADERS'])) { + $testheaders = $this->_processHeaders($section_text['EXPECTHEADERS']); + $missing = array_diff_assoc($testheaders, $this->_headers); + $changed = ''; + foreach ($missing as $header => $value) { + if (isset($this->_headers[$header])) { + $changed .= "-$header: $value\n+$header: "; + $changed .= $this->_headers[$header]; + } else { + $changed .= "-$header: $value\n"; + } + } + if ($missing) { + // tack on failed headers to output: + $output .= "\n====EXPECTHEADERS FAILURE====:\n$changed"; + } + } + // Does the output match what is expected? + do { + if (isset($section_text['EXPECTF']) || isset($section_text['EXPECTREGEX'])) { + if (isset($section_text['EXPECTF'])) { + $wanted = trim($section_text['EXPECTF']); + } else { + $wanted = trim($section_text['EXPECTREGEX']); + } + $wanted_re = preg_replace('/\r\n/', "\n", $wanted); + if (isset($section_text['EXPECTF'])) { + $wanted_re = preg_quote($wanted_re, '/'); + // Stick to basics + $wanted_re = str_replace("%s", ".+?", $wanted_re); //not greedy + $wanted_re = str_replace("%i", "[+\-]?[0-9]+", $wanted_re); + $wanted_re = str_replace("%d", "[0-9]+", $wanted_re); + $wanted_re = str_replace("%x", "[0-9a-fA-F]+", $wanted_re); + $wanted_re = str_replace("%f", "[+\-]?\.?[0-9]+\.?[0-9]*(E-?[0-9]+)?", $wanted_re); + $wanted_re = str_replace("%c", ".", $wanted_re); + // %f allows two points "-.0.0" but that is the best *simple* expression + } + + /* DEBUG YOUR REGEX HERE + var_dump($wanted_re); + print(str_repeat('=', 80) . "\n"); + var_dump($output); + */ + if (!$returnfail && preg_match("/^$wanted_re\$/s", $output)) { + if (file_exists($temp_file)) { + unlink($temp_file); + } + if (array_key_exists('FAIL', $section_text)) { + break; + } + if (!isset($this->_options['quiet'])) { + $this->_logger->log(0, "PASS $test_nr$tested$info"); + } + if (isset($this->_options['tapoutput'])) { + return array('ok', ' - ' . $tested); + } + return 'PASSED'; + } + } else { + if (isset($section_text['EXPECTFILE'])) { + $f = $temp_dir . '/' . trim($section_text['EXPECTFILE']); + if (!($fp = @fopen($f, 'rb'))) { + return PEAR::raiseError('--EXPECTFILE-- section file ' . + $f . ' not found'); + } + fclose($fp); + $section_text['EXPECT'] = file_get_contents($f); + } + + if (isset($section_text['EXPECT'])) { + $wanted = preg_replace('/\r\n/', "\n", trim($section_text['EXPECT'])); + } else { + $wanted = ''; + } + + // compare and leave on success + if (!$returnfail && 0 == strcmp($output, $wanted)) { + if (file_exists($temp_file)) { + unlink($temp_file); + } + if (array_key_exists('FAIL', $section_text)) { + break; + } + if (!isset($this->_options['quiet'])) { + $this->_logger->log(0, "PASS $test_nr$tested$info"); + } + if (isset($this->_options['tapoutput'])) { + return array('ok', ' - ' . $tested); + } + return 'PASSED'; + } + } + } while (false); + + if (array_key_exists('FAIL', $section_text)) { + // we expect a particular failure + // this is only used for testing PEAR_RunTest + $expectf = isset($section_text['EXPECTF']) ? $wanted_re : null; + $faildiff = $this->generate_diff($wanted, $output, null, $expectf); + $faildiff = preg_replace('/\r/', '', $faildiff); + $wanted = preg_replace('/\r/', '', trim($section_text['FAIL'])); + if ($faildiff == $wanted) { + if (!isset($this->_options['quiet'])) { + $this->_logger->log(0, "PASS $test_nr$tested$info"); + } + if (isset($this->_options['tapoutput'])) { + return array('ok', ' - ' . $tested); + } + return 'PASSED'; + } + unset($section_text['EXPECTF']); + $output = $faildiff; + if (isset($section_text['RETURNS'])) { + return PEAR::raiseError('Cannot have both RETURNS and FAIL in the same test: ' . + $file); + } + } + + // Test failed so we need to report details. + $txt = $warn ? 'WARN ' : 'FAIL '; + $this->_logger->log(0, $txt . $test_nr . $tested . $info); + + // write .exp + $res = $this->_writeLog($exp_filename, $wanted); + if (PEAR::isError($res)) { + return $res; + } + + // write .out + $res = $this->_writeLog($output_filename, $output); + if (PEAR::isError($res)) { + return $res; + } + + // write .diff + $returns = isset($section_text['RETURNS']) ? + array(trim($section_text['RETURNS']), $return_value) : null; + $expectf = isset($section_text['EXPECTF']) ? $wanted_re : null; + $data = $this->generate_diff($wanted, $output, $returns, $expectf); + $res = $this->_writeLog($diff_filename, $data); + if (PEAR::isError($res)) { + return $res; + } + + // write .log + $data = " +---- EXPECTED OUTPUT +$wanted +---- ACTUAL OUTPUT +$output +---- FAILED +"; + + if ($returnfail) { + $data .= " +---- EXPECTED RETURN +$section_text[RETURNS] +---- ACTUAL RETURN +$return_value +"; + } + + $res = $this->_writeLog($log_filename, $data); + if (PEAR::isError($res)) { + return $res; + } + + if (isset($this->_options['tapoutput'])) { + $wanted = explode("\n", $wanted); + $wanted = "# Expected output:\n#\n#" . implode("\n#", $wanted); + $output = explode("\n", $output); + $output = "#\n#\n# Actual output:\n#\n#" . implode("\n#", $output); + return array($wanted . $output . 'not ok', ' - ' . $tested); + } + return $warn ? 'WARNED' : 'FAILED'; + } + + function generate_diff($wanted, $output, $rvalue, $wanted_re) + { + $w = explode("\n", $wanted); + $o = explode("\n", $output); + $wr = explode("\n", $wanted_re); + $w1 = array_diff_assoc($w, $o); + $o1 = array_diff_assoc($o, $w); + $o2 = $w2 = array(); + foreach ($w1 as $idx => $val) { + if (!$wanted_re || !isset($wr[$idx]) || !isset($o1[$idx]) || + !preg_match('/^' . $wr[$idx] . '\\z/', $o1[$idx])) { + $w2[sprintf("%03d<", $idx)] = sprintf("%03d- ", $idx + 1) . $val; + } + } + foreach ($o1 as $idx => $val) { + if (!$wanted_re || !isset($wr[$idx]) || + !preg_match('/^' . $wr[$idx] . '\\z/', $val)) { + $o2[sprintf("%03d>", $idx)] = sprintf("%03d+ ", $idx + 1) . $val; + } + } + $diff = array_merge($w2, $o2); + ksort($diff); + $extra = $rvalue ? "##EXPECTED: $rvalue[0]\r\n##RETURNED: $rvalue[1]" : ''; + return implode("\r\n", $diff) . $extra; + } + + // Write the given text to a temporary file, and return the filename. + function save_text($filename, $text) + { + if (!$fp = fopen($filename, 'w')) { + return PEAR::raiseError("Cannot open file '" . $filename . "' (save_text)"); + } + fwrite($fp, $text); + fclose($fp); + if (1 < DETAILED) echo " +FILE $filename {{{ +$text +}}} +"; + } + + function _cleanupOldFiles($file) + { + $temp_dir = realpath(dirname($file)); + $mainFileName = basename($file, 'phpt'); + $diff_filename = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'diff'; + $log_filename = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'log'; + $exp_filename = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'exp'; + $output_filename = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'out'; + $memcheck_filename = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'mem'; + $temp_file = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'php'; + $temp_skipif = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'skip.php'; + $temp_clean = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'clean.php'; + $tmp_post = $temp_dir . DIRECTORY_SEPARATOR . uniqid('phpt.'); + + // unlink old test results + @unlink($diff_filename); + @unlink($log_filename); + @unlink($exp_filename); + @unlink($output_filename); + @unlink($memcheck_filename); + @unlink($temp_file); + @unlink($temp_skipif); + @unlink($tmp_post); + @unlink($temp_clean); + } + + function _runSkipIf($section_text, $temp_skipif, $tested, $ini_settings) + { + $info = ''; + $warn = false; + if (array_key_exists('SKIPIF', $section_text) && trim($section_text['SKIPIF'])) { + $this->save_text($temp_skipif, $section_text['SKIPIF']); + $output = $this->system_with_timeout("$this->_php $ini_settings -f \"$temp_skipif\""); + $output = $output[1]; + $loutput = ltrim($output); + unlink($temp_skipif); + if (!strncasecmp('skip', $loutput, 4)) { + $skipreason = "SKIP $tested"; + if (preg_match('/^\s*skip\s*(.+)\s*/i', $output, $m)) { + $skipreason .= '(reason: ' . $m[1] . ')'; + } + if (!isset($this->_options['quiet'])) { + $this->_logger->log(0, $skipreason); + } + if (isset($this->_options['tapoutput'])) { + return array('ok', ' # skip ' . $reason); + } + return 'SKIPPED'; + } + + if (!strncasecmp('info', $loutput, 4) + && preg_match('/^\s*info\s*(.+)\s*/i', $output, $m)) { + $info = " (info: $m[1])"; + } + + if (!strncasecmp('warn', $loutput, 4) + && preg_match('/^\s*warn\s*(.+)\s*/i', $output, $m)) { + $warn = true; /* only if there is a reason */ + $info = " (warn: $m[1])"; + } + } + + return array('warn' => $warn, 'info' => $info); + } + + function _stripHeadersCGI($output) + { + $this->headers = array(); + if (!empty($this->_options['cgi']) && + $this->_php == $this->_options['cgi'] && + preg_match("/^(.*?)(?:\n\n(.*)|\\z)/s", $output, $match)) { + $output = isset($match[2]) ? trim($match[2]) : ''; + $this->_headers = $this->_processHeaders($match[1]); + } + + return $output; + } + + /** + * Return an array that can be used with array_diff() to compare headers + * + * @param string $text + */ + function _processHeaders($text) + { + $headers = array(); + $rh = preg_split("/[\n\r]+/", $text); + foreach ($rh as $line) { + if (strpos($line, ':')!== false) { + $line = explode(':', $line, 2); + $headers[trim($line[0])] = trim($line[1]); + } + } + return $headers; + } + + function _readFile($file) + { + // Load the sections of the test file. + $section_text = array( + 'TEST' => '(unnamed test)', + 'SKIPIF' => '', + 'GET' => '', + 'COOKIE' => '', + 'POST' => '', + 'ARGS' => '', + 'INI' => '', + 'CLEAN' => '', + ); + + if (!is_file($file) || !$fp = fopen($file, "r")) { + return PEAR::raiseError("Cannot open test file: $file"); + } + + $section = ''; + while (!feof($fp)) { + $line = fgets($fp); + + // Match the beginning of a section. + if (preg_match('/^--([_A-Z]+)--/', $line, $r)) { + $section = $r[1]; + $section_text[$section] = ''; + continue; + } elseif (empty($section)) { + fclose($fp); + return PEAR::raiseError("Invalid sections formats in test file: $file"); + } + + // Add to the section text. + $section_text[$section] .= $line; + } + fclose($fp); + + return $section_text; + } + + function _writeLog($logname, $data) + { + if (!$log = fopen($logname, 'w')) { + return PEAR::raiseError("Cannot create test log - $logname"); + } + fwrite($log, $data); + fclose($log); + } + + function _resetEnv($section_text, $temp_file) + { + $env = $_ENV; + $env['REDIRECT_STATUS'] = ''; + $env['QUERY_STRING'] = ''; + $env['PATH_TRANSLATED'] = ''; + $env['SCRIPT_FILENAME'] = ''; + $env['REQUEST_METHOD'] = ''; + $env['CONTENT_TYPE'] = ''; + $env['CONTENT_LENGTH'] = ''; + if (!empty($section_text['ENV'])) { + if (strpos($section_text['ENV'], '{PWD}') !== false) { + $section_text['ENV'] = str_replace('{PWD}', dirname($temp_file), $section_text['ENV']); + } + foreach (explode("\n", trim($section_text['ENV'])) as $e) { + $e = explode('=', trim($e), 2); + if (!empty($e[0]) && isset($e[1])) { + $env[$e[0]] = $e[1]; + } + } + } + if (array_key_exists('GET', $section_text)) { + $env['QUERY_STRING'] = trim($section_text['GET']); + } else { + $env['QUERY_STRING'] = ''; + } + if (array_key_exists('COOKIE', $section_text)) { + $env['HTTP_COOKIE'] = trim($section_text['COOKIE']); + } else { + $env['HTTP_COOKIE'] = ''; + } + $env['REDIRECT_STATUS'] = '1'; + $env['PATH_TRANSLATED'] = $temp_file; + $env['SCRIPT_FILENAME'] = $temp_file; + + return $env; + } + + function _processUpload($section_text, $file) + { + if (array_key_exists('UPLOAD', $section_text) && !empty($section_text['UPLOAD'])) { + $upload_files = trim($section_text['UPLOAD']); + $upload_files = explode("\n", $upload_files); + + $request = "Content-Type: multipart/form-data; boundary=---------------------------20896060251896012921717172737\n" . + "-----------------------------20896060251896012921717172737\n"; + foreach ($upload_files as $fileinfo) { + $fileinfo = explode('=', $fileinfo); + if (count($fileinfo) != 2) { + return PEAR::raiseError("Invalid UPLOAD section in test file: $file"); + } + if (!realpath(dirname($file) . '/' . $fileinfo[1])) { + return PEAR::raiseError("File for upload does not exist: $fileinfo[1] " . + "in test file: $file"); + } + $file_contents = file_get_contents(dirname($file) . '/' . $fileinfo[1]); + $fileinfo[1] = basename($fileinfo[1]); + $request .= "Content-Disposition: form-data; name=\"$fileinfo[0]\"; filename=\"$fileinfo[1]\"\n"; + $request .= "Content-Type: text/plain\n\n"; + $request .= $file_contents . "\n" . + "-----------------------------20896060251896012921717172737\n"; + } + + if (array_key_exists('POST', $section_text) && !empty($section_text['POST'])) { + // encode POST raw + $post = trim($section_text['POST']); + $post = explode('&', $post); + foreach ($post as $i => $post_info) { + $post_info = explode('=', $post_info); + if (count($post_info) != 2) { + return PEAR::raiseError("Invalid POST data in test file: $file"); + } + $post_info[0] = rawurldecode($post_info[0]); + $post_info[1] = rawurldecode($post_info[1]); + $post[$i] = $post_info; + } + foreach ($post as $post_info) { + $request .= "Content-Disposition: form-data; name=\"$post_info[0]\"\n\n"; + $request .= $post_info[1] . "\n" . + "-----------------------------20896060251896012921717172737\n"; + } + unset($section_text['POST']); + } + $section_text['POST_RAW'] = $request; + } + + return $section_text; + } + + function _testCleanup($section_text, $temp_clean) + { + if ($section_text['CLEAN']) { + // perform test cleanup + $this->save_text($temp_clean, $section_text['CLEAN']); + $output = $this->system_with_timeout("$this->_php \"$temp_clean\" 2>&1"); + if (strlen($output[1])) { + echo "BORKED --CLEAN-- section! output:\n", $output[1]; + } + if (file_exists($temp_clean)) { + unlink($temp_clean); + } + } + } +} diff --git a/includes/pear/PEAR/Start.php b/includes/pear/PEAR/Start.php new file mode 100644 index 0000000..da69246 --- /dev/null +++ b/includes/pear/PEAR/Start.php @@ -0,0 +1,427 @@ + 'Installation base ($prefix)', + 'temp_dir' => 'Temporary directory for processing', + 'download_dir' => 'Temporary directory for downloads', + 'bin_dir' => 'Binaries directory', + 'php_dir' => 'PHP code directory ($php_dir)', + 'doc_dir' => 'Documentation directory', + 'data_dir' => 'Data directory', + 'cfg_dir' => 'User-modifiable configuration files directory', + 'www_dir' => 'Public Web Files directory', + 'test_dir' => 'Tests directory', + 'pear_conf' => 'Name of configuration file', + ); + + var $localInstall; + var $PEARConfig; + var $tarball = array(); + + function PEAR_Start() + { + parent::PEAR(); + if (OS_WINDOWS) { + $this->configPrompt['php_bin'] = 'Path to CLI php.exe'; + $this->config[] = 'php_bin'; + $this->prefix = getcwd(); + + if (!@is_dir($this->prefix)) { + if (@is_dir('c:\php5')) { + $this->prefix = 'c:\php5'; + } elseif (@is_dir('c:\php4')) { + $this->prefix = 'c:\php4'; + } elseif (@is_dir('c:\php')) { + $this->prefix = 'c:\php'; + } + } + + $slash = "\\"; + if (strrpos($this->prefix, '\\') === (strlen($this->prefix) - 1)) { + $slash = ''; + } + + $this->localInstall = false; + $this->bin_dir = '$prefix'; + $this->temp_dir = '$prefix' . $slash . 'tmp'; + $this->download_dir = '$prefix' . $slash . 'tmp'; + $this->php_dir = '$prefix' . $slash . 'pear'; + $this->doc_dir = '$prefix' . $slash . 'docs'; + $this->data_dir = '$prefix' . $slash . 'data'; + $this->test_dir = '$prefix' . $slash . 'tests'; + $this->www_dir = '$prefix' . $slash . 'www'; + $this->cfg_dir = '$prefix' . $slash . 'cfg'; + $this->pear_conf = PEAR_CONFIG_SYSCONFDIR . '\\pear.ini'; + /* + * Detects php.exe + */ + $this->validPHPBin = true; + if ($t = $this->safeGetenv('PHP_PEAR_PHP_BIN')) { + $this->php_bin = dirname($t); + } elseif ($t = $this->safeGetenv('PHP_BIN')) { + $this->php_bin = dirname($t); + } elseif ($t = System::which('php')) { + $this->php_bin = dirname($t); + } elseif (is_file($this->prefix . '\cli\php.exe')) { + $this->php_bin = $this->prefix . '\cli'; + } elseif (is_file($this->prefix . '\php.exe')) { + $this->php_bin = $this->prefix; + } + $phpexe = OS_WINDOWS ? '\\php.exe' : '/php'; + if ($this->php_bin && !is_file($this->php_bin . $phpexe)) { + $this->php_bin = ''; + } else { + if (strpos($this->php_bin, ':') === 0) { + $this->php_bin = getcwd() . DIRECTORY_SEPARATOR . $this->php_bin; + } + } + if (!is_file($this->php_bin . $phpexe)) { + if (is_file('c:/php/cli/php.exe')) { + $this->php_bin = 'c"\\php\\cli'; + } elseif (is_file('c:/php5/php.exe')) { + $this->php_bin = 'c:\\php5'; + } elseif (is_file('c:/php4/cli/php.exe')) { + $this->php_bin = 'c:\\php4\\cli'; + } else { + $this->validPHPBin = false; + } + } + } else { + $this->prefix = dirname(PHP_BINDIR); + $this->pear_conf = PEAR_CONFIG_SYSCONFDIR . '/pear.conf'; + if (get_current_user() != 'root') { + $this->prefix = $this->safeGetenv('HOME') . '/pear'; + $this->pear_conf = $this->safeGetenv('HOME') . '.pearrc'; + } + $this->bin_dir = '$prefix/bin'; + $this->php_dir = '$prefix/share/pear'; + $this->temp_dir = '/tmp/pear/install'; + $this->download_dir = '/tmp/pear/install'; + $this->doc_dir = '$prefix/docs'; + $this->www_dir = '$prefix/www'; + $this->cfg_dir = '$prefix/cfg'; + $this->data_dir = '$prefix/data'; + $this->test_dir = '$prefix/tests'; + // check if the user has installed PHP with PHP or GNU layout + if (@is_dir("$this->prefix/lib/php/.registry")) { + $this->php_dir = '$prefix/lib/php'; + } elseif (@is_dir("$this->prefix/share/pear/lib/.registry")) { + $this->php_dir = '$prefix/share/pear/lib'; + $this->doc_dir = '$prefix/share/pear/docs'; + $this->data_dir = '$prefix/share/pear/data'; + $this->test_dir = '$prefix/share/pear/tests'; + } elseif (@is_dir("$this->prefix/share/php/.registry")) { + $this->php_dir = '$prefix/share/php'; + } + } + } + + function safeGetenv($var) + { + if (is_array($_ENV) && isset($_ENV[$var])) { + return $_ENV[$var]; + } + + return getenv($var); + } + + function show($stuff) + { + print $stuff; + } + + function locatePackagesToInstall() + { + $dp = @opendir(dirname(__FILE__) . '/go-pear-tarballs'); + if (empty($dp)) { + return PEAR::raiseError("while locating packages to install: opendir('" . + dirname(__FILE__) . "/go-pear-tarballs') failed"); + } + + $potentials = array(); + while (false !== ($entry = readdir($dp))) { + if ($entry{0} == '.' || !in_array(substr($entry, -4), array('.tar', '.tgz'))) { + continue; + } + $potentials[] = $entry; + } + + closedir($dp); + $notfound = array(); + foreach ($this->corePackages as $package) { + foreach ($potentials as $i => $candidate) { + if (preg_match('/^' . $package . '-' . _PEAR_COMMON_PACKAGE_VERSION_PREG + . '\.(tar|tgz)\\z/', $candidate)) { + $this->tarball[$package] = dirname(__FILE__) . '/go-pear-tarballs/' . $candidate; + unset($potentials[$i]); + continue 2; + } + } + + $notfound[] = $package; + } + + if (count($notfound)) { + return PEAR::raiseError("No tarballs found for core packages: " . + implode(', ', $notfound)); + } + + $this->tarball = array_merge($this->tarball, $potentials); + } + + function setupTempStuff() + { + if (!($this->ptmp = System::mktemp(array('-d')))) { + $this->show("System's Tempdir failed, trying to use \$prefix/tmp ..."); + $res = System::mkDir(array($this->prefix . '/tmp')); + if (!$res) { + return PEAR::raiseError('mkdir ' . $this->prefix . '/tmp ... failed'); + } + + $_temp = tempnam($this->prefix . '/tmp', 'gope'); + System::rm(array('-rf', $_temp)); + System::mkdir(array('-p','-m', '0700', $_temp)); + $this->ptmp = $this->prefix . '/tmp'; + $ok = @chdir($this->ptmp); + + if (!$ok) { // This should not happen, really ;) + $this->bail('chdir ' . $this->ptmp . ' ... failed'); + } + + print "ok\n"; + + // Adjust TEMPDIR envvars + if (!isset($_ENV)) { + $_ENV = array(); + }; + $_ENV['TMPDIR'] = $_ENV['TEMP'] = $this->prefix . '/tmp'; + } + + return @chdir($this->ptmp); + } + + /** + * Try to detect the kind of SAPI used by the + * the given php.exe. + * @author Pierrre-Alain Joye + */ + function win32DetectPHPSAPI() + { + if ($this->php_bin != '') { + if (OS_WINDOWS) { + exec('"' . $this->php_bin . '\\php.exe" -v', $res); + } else { + exec('"' . $this->php_bin . '/php" -v', $res); + } + + if (is_array($res)) { + if (isset($res[0]) && strpos($res[0],"(cli)")) { + return 'cli'; + } + + if (isset($res[0]) && strpos($res[0],"cgi")) { + return 'cgi'; + } + + if (isset($res[0]) && strpos($res[0],"cgi-fcgi")) { + return 'cgi'; + } + + return 'unknown'; + } + } + + return 'unknown'; + } + + function doInstall() + { + print "Beginning install...\n"; + // finish php_bin config + if (OS_WINDOWS) { + $this->php_bin .= '\\php.exe'; + } else { + $this->php_bin .= '/php'; + } + $this->PEARConfig = &PEAR_Config::singleton($this->pear_conf, $this->pear_conf); + $this->PEARConfig->set('preferred_state', 'stable'); + foreach ($this->config as $var) { + if ($var == 'pear_conf' || $var == 'prefix') { + continue; + } + $this->PEARConfig->set($var, $this->$var); + } + + $this->PEARConfig->store(); +// $this->PEARConfig->set('verbose', 6); + print "Configuration written to $this->pear_conf...\n"; + $this->registry = &$this->PEARConfig->getRegistry(); + print "Initialized registry...\n"; + $install = &PEAR_Command::factory('install', $this->PEARConfig); + print "Preparing to install...\n"; + $options = array( + 'nodeps' => true, + 'force' => true, + 'upgrade' => true, + ); + foreach ($this->tarball as $pkg => $src) { + print "installing $src...\n"; + } + $install->run('install', $options, array_values($this->tarball)); + } + + function postProcessConfigVars() + { + foreach ($this->config as $n => $var) { + for ($m = 1; $m <= count($this->config); $m++) { + $var2 = $this->config[$m]; + $this->$var = str_replace('$'.$var2, $this->$var2, $this->$var); + } + } + + foreach ($this->config as $var) { + $dir = $this->$var; + + if (!preg_match('/_dir\\z/', $var)) { + continue; + } + + if (!@is_dir($dir)) { + if (!System::mkDir(array('-p', $dir))) { + $root = OS_WINDOWS ? 'administrator' : 'root'; + return PEAR::raiseError("Unable to create {$this->configPrompt[$var]} $dir. +Run this script as $root or pick another location.\n"); + } + } + } + } + + /** + * Get the php.ini file used with the current + * process or with the given php.exe + * + * Horrible hack, but well ;) + * + * Not used yet, will add the support later + * @author Pierre-Alain Joye + */ + function getPhpiniPath() + { + $pathIni = get_cfg_var('cfg_file_path'); + if ($pathIni && is_file($pathIni)) { + return $pathIni; + } + + // Oh well, we can keep this too :) + // I dunno if get_cfg_var() is safe on every OS + if (OS_WINDOWS) { + // on Windows, we can be pretty sure that there is a php.ini + // file somewhere + do { + $php_ini = PHP_CONFIG_FILE_PATH . DIRECTORY_SEPARATOR . 'php.ini'; + if (@file_exists($php_ini)) { + break; + } + $php_ini = 'c:\winnt\php.ini'; + if (@file_exists($php_ini)) { + break; + } + $php_ini = 'c:\windows\php.ini'; + } while (false); + } else { + $php_ini = PHP_CONFIG_FILE_PATH . DIRECTORY_SEPARATOR . 'php.ini'; + } + + if (@is_file($php_ini)) { + return $php_ini; + } + + // We re running in hackz&troubles :) + ob_implicit_flush(false); + ob_start(); + phpinfo(INFO_GENERAL); + $strInfo = ob_get_contents(); + ob_end_clean(); + ob_implicit_flush(true); + + if (php_sapi_name() != 'cli') { + $strInfo = strip_tags($strInfo,''); + $arrayInfo = explode("", $strInfo ); + $cli = false; + } else { + $arrayInfo = explode("\n", $strInfo); + $cli = true; + } + + foreach ($arrayInfo as $val) { + if (strpos($val,"php.ini")) { + if ($cli) { + list(,$pathIni) = explode('=>', $val); + } else { + $pathIni = strip_tags(trim($val)); + } + $pathIni = trim($pathIni); + if (is_file($pathIni)) { + return $pathIni; + } + } + } + + return false; + } +} +?> diff --git a/includes/pear/PEAR/Start/CLI.php b/includes/pear/PEAR/Start/CLI.php new file mode 100644 index 0000000..a5ea9c5 --- /dev/null +++ b/includes/pear/PEAR/Start/CLI.php @@ -0,0 +1,616 @@ +tty = OS_WINDOWS ? @fopen('\con', 'r') : @fopen('/dev/tty', 'r'); + + if (!$this->tty) { + $this->tty = fopen('php://stdin', 'r'); + } + $this->origpwd = getcwd(); + $this->config = array_keys($this->configPrompt); + + // make indices run from 1... + array_unshift($this->config, ""); + unset($this->config[0]); + reset($this->config); + $this->descLength = max(array_map('strlen', $this->configPrompt)); + $this->descFormat = "%-{$this->descLength}s"; + $this->first = key($this->config); + end($this->config); + $this->last = key($this->config); + PEAR_Command::setFrontendType('CLI'); + } + + function _PEAR_Start_CLI() + { + if ($this->tty) { + @fclose($this->tty); + } + } + + function run() + { + if (PEAR::isError($err = $this->locatePackagesToInstall())) { + return $err; + } + $this->startupQuestion(); + $this->setupTempStuff(); + $this->getInstallLocations(); + $this->displayPreamble(); + if (PEAR::isError($err = $this->postProcessConfigVars())) { + return $err; + } + $this->doInstall(); + $this->finishInstall(); + } + + function startupQuestion() + { + if (OS_WINDOWS) { + print " +Are you installing a system-wide PEAR or a local copy? +(system|local) [system] : "; + $tmp = trim(fgets($this->tty, 1024)); + if (!empty($tmp) && strtolower($tmp) !== 'system') { + print "Please confirm local copy by typing 'yes' : "; + $tmp = trim(fgets($this->tty, 1024)); + if (strtolower($tmp) == 'yes') { + $slash = "\\"; + if (strrpos($this->prefix, '\\') === (strlen($this->prefix) - 1)) { + $slash = ''; + } + + $this->localInstall = true; + $this->pear_conf = '$prefix' . $slash . 'pear.ini'; + } + } + } else { + if (get_current_user() == 'root') { + return; + } + $this->pear_conf = $this->safeGetenv('HOME') . '/.pearrc'; + } + } + + function getInstallLocations() + { + while (true) { + print " +Below is a suggested file layout for your new PEAR installation. To +change individual locations, type the number in front of the +directory. Type 'all' to change all of them or simply press Enter to +accept these locations. + +"; + + foreach ($this->config as $n => $var) { + $fullvar = $this->$var; + foreach ($this->config as $blah => $unused) { + foreach ($this->config as $m => $var2) { + $fullvar = str_replace('$'.$var2, $this->$var2, $fullvar); + } + } + printf("%2d. $this->descFormat : %s\n", $n, $this->configPrompt[$var], $fullvar); + } + + print "\n$this->first-$this->last, 'all' or Enter to continue: "; + $tmp = trim(fgets($this->tty, 1024)); + if (empty($tmp)) { + if (OS_WINDOWS && !$this->validPHPBin) { + echo "**ERROR** +Please, enter the php.exe path. + +"; + } else { + break; + } + } + + if (isset($this->config[(int)$tmp])) { + $var = $this->config[(int)$tmp]; + $desc = $this->configPrompt[$var]; + $current = $this->$var; + if (WIN32GUI && $var != 'pear_conf'){ + $tmp = $this->win32BrowseForFolder("Choose a Folder for $desc [$current] :"); + $tmp.= '\\'; + } else { + print "(Use \$prefix as a shortcut for '$this->prefix', etc.) +$desc [$current] : "; + $tmp = trim(fgets($this->tty, 1024)); + } + $old = $this->$var; + $this->$var = $$var = $tmp; + if (OS_WINDOWS && $var=='php_bin') { + if ($this->validatePhpExecutable($tmp)) { + $this->php_bin = $tmp; + } else { + $this->php_bin = $old; + } + } + } elseif ($tmp == 'all') { + foreach ($this->config as $n => $var) { + $desc = $this->configPrompt[$var]; + $current = $this->$var; + print "$desc [$current] : "; + $tmp = trim(fgets($this->tty, 1024)); + if (!empty($tmp)) { + $this->$var = $tmp; + } + } + } + } + } + + function validatePhpExecutable($tmp) + { + if (OS_WINDOWS) { + if (strpos($tmp, 'php.exe')) { + $tmp = str_replace('php.exe', '', $tmp); + } + if (file_exists($tmp . DIRECTORY_SEPARATOR . 'php.exe')) { + $tmp = $tmp . DIRECTORY_SEPARATOR . 'php.exe'; + $this->php_bin_sapi = $this->win32DetectPHPSAPI(); + if ($this->php_bin_sapi=='cgi'){ + print " +****************************************************************************** +NOTICE! We found php.exe under $this->php_bin, it uses a $this->php_bin_sapi SAPI. +PEAR commandline tool works well with it. +If you have a CLI php.exe available, we recommend using it. + +Press Enter to continue..."; + $tmp = trim(fgets($this->tty, 1024)); + } elseif ($this->php_bin_sapi=='unknown') { + print " +****************************************************************************** +WARNING! We found php.exe under $this->php_bin, it uses an $this->php_bin_sapi SAPI. +PEAR commandline tool has NOT been tested with it. +If you have a CLI (or CGI) php.exe available, we strongly recommend using it. + +Press Enter to continue..."; + $tmp = trim(fgets($this->tty, 1024)); + } + echo "php.exe (sapi: $this->php_bin_sapi) found.\n\n"; + return $this->validPHPBin = true; + } else { + echo "**ERROR**: not a folder, or no php.exe found in this folder. +Press Enter to continue..."; + $tmp = trim(fgets($this->tty, 1024)); + return $this->validPHPBin = false; + } + } + } + + /** + * Create a vbs script to browse the getfolder dialog, called + * by cscript, if it's available. + * $label is the label text in the header of the dialog box + * + * TODO: + * - Do not show Control panel + * - Replace WSH with calls to w32 as soon as callbacks work + * @author Pierrre-Alain Joye + */ + function win32BrowseForFolder($label) + { + static $wshSaved=false; + static $cscript=''; + $wsh_browserfolder = 'Option Explicit +Dim ArgObj, var1, var2, sa, sFld +Set ArgObj = WScript.Arguments +Const BIF_EDITBOX = &H10 +Const BIF_NEWDIALOGSTYLE = &H40 +Const BIF_RETURNONLYFSDIRS = &H0001 +Const BIF_DONTGOBELOWDOMAIN = &H0002 +Const BIF_STATUSTEXT = &H0004 +Const BIF_RETURNFSANCESTORS = &H0008 +Const BIF_VALIDATE = &H0020 +Const BIF_BROWSEFORCOMPUTER = &H1000 +Const BIF_BROWSEFORPRINTER = &H2000 +Const BIF_BROWSEINCLUDEFILES = &H4000 +Const OFN_LONGNAMES = &H200000 +Const OFN_NOLONGNAMES = &H40000 +Const ssfDRIVES = &H11 +Const ssfNETWORK = &H12 +Set sa = CreateObject("Shell.Application") +var1=ArgObj(0) +Set sFld = sa.BrowseForFolder(0, var1, BIF_EDITBOX + BIF_VALIDATE + BIF_BROWSEINCLUDEFILES + BIF_RETURNFSANCESTORS+BIF_NEWDIALOGSTYLE , ssfDRIVES ) +if not sFld is nothing Then + if not left(sFld.items.item.path,1)=":" Then + WScript.Echo sFld.items.item.path + Else + WScript.Echo "invalid" + End If +Else + WScript.Echo "cancel" +End If +'; + if( !$wshSaved){ + $cscript = $this->ptmp . DIRECTORY_SEPARATOR . "bf.vbs"; + $fh = fopen($cscript, "wb+"); + fwrite($fh, $wsh_browserfolder, strlen($wsh_browserfolder)); + fclose($fh); + $wshSaved = true; + } + + exec('cscript ' . escapeshellarg($cscript) . ' "' . escapeshellarg($label) . '" //noLogo', $arPath); + if (!count($arPath) || $arPath[0]=='' || $arPath[0]=='cancel') { + return ''; + } elseif ($arPath[0]=='invalid') { + echo "Invalid Path.\n"; + return ''; + } + + @unlink($cscript); + return $arPath[0]; + } + + function displayPreamble() + { + if (OS_WINDOWS) { + /* + * Checks PHP SAPI version under windows/CLI + */ + if ($this->php_bin == '') { + print " +We do not find any php.exe, please select the php.exe folder (CLI is +recommended, usually in c:\php\cli\php.exe) +"; + $this->validPHPBin = false; + } elseif (strlen($this->php_bin)) { + $this->php_bin_sapi = $this->win32DetectPHPSAPI(); + $this->validPHPBin = true; + switch ($this->php_bin_sapi) { + case 'cli': + break; + case 'cgi': + case 'cgi-fcgi': + print " +*NOTICE* +We found php.exe under $this->php_bin, it uses a $this->php_bin_sapi SAPI. PEAR commandline +tool works well with it, if you have a CLI php.exe available, we +recommend using it. +"; + break; + default: + print " +*WARNING* +We found php.exe under $this->php_bin, it uses an unknown SAPI. PEAR commandline +tool has not been tested with it, if you have a CLI (or CGI) php.exe available, +we strongly recommend using it. + +"; + break; + } + } + } + } + + function finishInstall() + { + $sep = OS_WINDOWS ? ';' : ':'; + $include_path = explode($sep, ini_get('include_path')); + if (OS_WINDOWS) { + $found = false; + $t = strtolower($this->php_dir); + foreach ($include_path as $path) { + if ($t == strtolower($path)) { + $found = true; + break; + } + } + } else { + $found = in_array($this->php_dir, $include_path); + } + if (!$found) { + print " +****************************************************************************** +WARNING! The include_path defined in the currently used php.ini does not +contain the PEAR PHP directory you just specified: +<$this->php_dir> +If the specified directory is also not in the include_path used by +your scripts, you will have problems getting any PEAR packages working. +"; + + if ($php_ini = $this->getPhpiniPath()) { + print "\n\nWould you like to alter php.ini <$php_ini>? [Y/n] : "; + $alter_phpini = !stristr(fgets($this->tty, 1024), "n"); + if ($alter_phpini) { + $this->alterPhpIni($php_ini); + } else { + if (OS_WINDOWS) { + print " +Please look over your php.ini file to make sure +$this->php_dir is in your include_path."; + } else { + print " +I will add a workaround for this in the 'pear' command to make sure +the installer works, but please look over your php.ini or Apache +configuration to make sure $this->php_dir is in your include_path. +"; + } + } + } + + print " +Current include path : ".ini_get('include_path')." +Configured directory : $this->php_dir +Currently used php.ini (guess) : $php_ini +"; + + print "Press Enter to continue: "; + fgets($this->tty, 1024); + } + + $pear_cmd = $this->bin_dir . DIRECTORY_SEPARATOR . 'pear'; + $pear_cmd = OS_WINDOWS ? strtolower($pear_cmd).'.bat' : $pear_cmd; + + // check that the installed pear and the one in the path are the same (if any) + $pear_old = System::which(OS_WINDOWS ? 'pear.bat' : 'pear', $this->bin_dir); + if ($pear_old && ($pear_old != $pear_cmd)) { + // check if it is a link or symlink + $islink = OS_WINDOWS ? false : is_link($pear_old) ; + if ($islink && readlink($pear_old) != $pear_cmd) { + print "\n** WARNING! The link $pear_old does not point to the " . + "installed $pear_cmd\n"; + } elseif (!$this->localInstall && is_writable($pear_old) && !is_dir($pear_old)) { + rename($pear_old, "{$pear_old}_old"); + print "\n** WARNING! Backed up old pear to {$pear_old}_old\n"; + } else { + print "\n** WARNING! Old version found at $pear_old, please remove it or ". + "be sure to use the new $pear_cmd command\n"; + } + } + + print "\nThe 'pear' command is now at your service at $pear_cmd\n"; + + // Alert the user if the pear cmd is not in PATH + $old_dir = $pear_old ? dirname($pear_old) : false; + if (!$this->which('pear', $old_dir)) { + print " +** The 'pear' command is not currently in your PATH, so you need to +** use '$pear_cmd' until you have added +** '$this->bin_dir' to your PATH environment variable. + +"; + + print "Run it without parameters to see the available actions, try 'pear list' +to see what packages are installed, or 'pear help' for help. + +For more information about PEAR, see: + + http://pear.php.net/faq.php + http://pear.php.net/manual/ + +Thanks for using go-pear! + +"; + } + + if (OS_WINDOWS && !$this->localInstall) { + $this->win32CreateRegEnv(); + } + } + + /** + * System::which() does not allow path exclusion + */ + function which($program, $dont_search_in = false) + { + if (OS_WINDOWS) { + if ($_path = $this->safeGetEnv('Path')) { + $dirs = explode(';', $_path); + } else { + $dirs = explode(';', $this->safeGetEnv('PATH')); + } + foreach ($dirs as $i => $dir) { + $dirs[$i] = strtolower(realpath($dir)); + } + if ($dont_search_in) { + $dont_search_in = strtolower(realpath($dont_search_in)); + } + if ($dont_search_in && + ($key = array_search($dont_search_in, $dirs)) !== false) + { + unset($dirs[$key]); + } + + foreach ($dirs as $dir) { + $dir = str_replace('\\\\', '\\', $dir); + if (!strlen($dir)) { + continue; + } + if ($dir{strlen($dir) - 1} != '\\') { + $dir .= '\\'; + } + $tmp = $dir . $program; + $info = pathinfo($tmp); + if (isset($info['extension']) && in_array(strtolower($info['extension']), + array('exe', 'com', 'bat', 'cmd'))) { + if (file_exists($tmp)) { + return strtolower($tmp); + } + } elseif (file_exists($ret = $tmp . '.exe') || + file_exists($ret = $tmp . '.com') || + file_exists($ret = $tmp . '.bat') || + file_exists($ret = $tmp . '.cmd')) { + return strtolower($ret); + } + } + } else { + $dirs = explode(':', $this->safeGetEnv('PATH')); + if ($dont_search_in && + ($key = array_search($dont_search_in, $dirs)) !== false) + { + unset($dirs[$key]); + } + foreach ($dirs as $dir) { + if (is_executable("$dir/$program")) { + return "$dir/$program"; + } + } + } + return false; + } + + /** + * Not optimized, but seems to work, if some nice + * peardev will test it? :) + * + * @author Pierre-Alain Joye + */ + function alterPhpIni($pathIni='') + { + $foundAt = array(); + $iniSep = OS_WINDOWS ? ';' : ':'; + + if ($pathIni=='') { + $pathIni = $this->getPhpiniPath(); + } + + $arrayIni = file($pathIni); + $i=0; + $found=0; + + // Looks for each active include_path directives + foreach ($arrayIni as $iniLine) { + $iniLine = trim($iniLine); + $iniLine = str_replace(array("\n", "\r"), array('', ''), $iniLine); + if (preg_match("/^\s*include_path/", $iniLine)) { + $foundAt[] = $i; + $found++; + } + $i++; + } + + if ($found) { + $includeLine = $arrayIni[$foundAt[0]]; + list(, $currentPath) = explode('=', $includeLine); + + $currentPath = trim($currentPath); + if (substr($currentPath,0,1) == '"') { + $currentPath = substr($currentPath, 1, strlen($currentPath) - 2); + } + + $arrayPath = explode($iniSep, $currentPath); + $newPath = array(); + if ($arrayPath[0]=='.') { + $newPath[0] = '.'; + $newPath[1] = $this->php_dir; + array_shift($arrayPath); + } else { + $newPath[0] = $this->php_dir; + } + + foreach ($arrayPath as $path) { + $newPath[]= $path; + } + } else { + $newPath = array(); + $newPath[0] = '.'; + $newPath[1] = $this->php_dir; + $foundAt[] = count($arrayIni); // add a new line if none is present + } + $nl = OS_WINDOWS ? "\r\n" : "\n"; + $includepath = 'include_path="' . implode($iniSep,$newPath) . '"'; + $newInclude = "$nl$nl;***** Added by go-pear$nl" . + $includepath . + $nl . ";*****" . + $nl . $nl; + + $arrayIni[$foundAt[0]] = $newInclude; + + for ($i=1; $i<$found; $i++) { + $arrayIni[$foundAt[$i]]=';' . trim($arrayIni[$foundAt[$i]]); + } + + $newIni = implode("", $arrayIni); + if (!($fh = @fopen($pathIni, "wb+"))) { + $prefixIni = $this->prefix . DIRECTORY_SEPARATOR . "php.ini-gopear"; + $fh = fopen($prefixIni, "wb+"); + if (!$fh) { + echo " +****************************************************************************** +WARNING: Cannot write to $pathIni nor in $this->prefix/php.ini-gopear. Please +modify manually your php.ini by adding: + +$includepath + +"; + return false; + } else { + fwrite($fh, $newIni, strlen($newIni)); + fclose($fh); + echo " +****************************************************************************** +WARNING: Cannot write to $pathIni, but php.ini was successfully created +at <$this->prefix/php.ini-gopear>. Please replace the file <$pathIni> with +<$prefixIni> or modify your php.ini by adding: + +$includepath + +"; + + } + } else { + fwrite($fh, $newIni, strlen($newIni)); + fclose($fh); + echo " +php.ini <$pathIni> include_path updated. +"; + } + return true; + } + + /** + * Generates a registry addOn for Win32 platform + * This addon set PEAR environment variables + * @author Pierrre-Alain Joye + */ + function win32CreateRegEnv() + { + $nl = "\r\n"; + $reg ='REGEDIT4'.$nl. + '[HKEY_CURRENT_USER\Environment]'. $nl . + '"PHP_PEAR_SYSCONF_DIR"="' . addslashes($this->prefix) . '"' . $nl . + '"PHP_PEAR_INSTALL_DIR"="' . addslashes($this->php_dir) . '"' . $nl . + '"PHP_PEAR_DOC_DIR"="' . addslashes($this->doc_dir) . '"' . $nl . + '"PHP_PEAR_BIN_DIR"="' . addslashes($this->bin_dir) . '"' . $nl . + '"PHP_PEAR_DATA_DIR"="' . addslashes($this->data_dir) . '"' . $nl . + '"PHP_PEAR_PHP_BIN"="' . addslashes($this->php_bin) . '"' . $nl . + '"PHP_PEAR_TEST_DIR"="' . addslashes($this->test_dir) . '"' . $nl; + + $fh = fopen($this->prefix . DIRECTORY_SEPARATOR . 'PEAR_ENV.reg', 'wb'); + if($fh){ + fwrite($fh, $reg, strlen($reg)); + fclose($fh); + echo " + +* WINDOWS ENVIRONMENT VARIABLES * +For convenience, a REG file is available under {$this->prefix}PEAR_ENV.reg . +This file creates ENV variables for the current user. + +Double-click this file to add it to the current user registry. + +"; + } + } + + function displayHTMLProgress() + { + } +} +?> \ No newline at end of file diff --git a/includes/pear/PEAR/Task/Common.php b/includes/pear/PEAR/Task/Common.php new file mode 100644 index 0000000..0ca5ccf --- /dev/null +++ b/includes/pear/PEAR/Task/Common.php @@ -0,0 +1,202 @@ + + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a1 + */ +/**#@+ + * Error codes for task validation routines + */ +define('PEAR_TASK_ERROR_NOATTRIBS', 1); +define('PEAR_TASK_ERROR_MISSING_ATTRIB', 2); +define('PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE', 3); +define('PEAR_TASK_ERROR_INVALID', 4); +/**#@-*/ +define('PEAR_TASK_PACKAGE', 1); +define('PEAR_TASK_INSTALL', 2); +define('PEAR_TASK_PACKAGEANDINSTALL', 3); +/** + * A task is an operation that manipulates the contents of a file. + * + * Simple tasks operate on 1 file. Multiple tasks are executed after all files have been + * processed and installed, and are designed to operate on all files containing the task. + * The Post-install script task simply takes advantage of the fact that it will be run + * after installation, replace is a simple task. + * + * Combining tasks is possible, but ordering is significant. + * + * + * + * + * + * + * This will first replace any instance of @data-dir@ in the test.php file + * with the path to the current data directory. Then, it will include the + * test.php file and run the script it contains to configure the package post-installation. + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a1 + * @abstract + */ +class PEAR_Task_Common +{ + /** + * Valid types for this version are 'simple' and 'multiple' + * + * - simple tasks operate on the contents of a file and write out changes to disk + * - multiple tasks operate on the contents of many files and write out the + * changes directly to disk + * + * Child task classes must override this property. + * @access protected + */ + var $type = 'simple'; + /** + * Determines which install phase this task is executed under + */ + var $phase = PEAR_TASK_INSTALL; + /** + * @access protected + */ + var $config; + /** + * @access protected + */ + var $registry; + /** + * @access protected + */ + var $logger; + /** + * @access protected + */ + var $installphase; + /** + * @param PEAR_Config + * @param PEAR_Common + */ + function PEAR_Task_Common(&$config, &$logger, $phase) + { + $this->config = &$config; + $this->registry = &$config->getRegistry(); + $this->logger = &$logger; + $this->installphase = $phase; + if ($this->type == 'multiple') { + $GLOBALS['_PEAR_TASK_POSTINSTANCES'][get_class($this)][] = &$this; + } + } + + /** + * Validate the basic contents of a task tag. + * @param PEAR_PackageFile_v2 + * @param array + * @param PEAR_Config + * @param array the entire parsed tag + * @return true|array On error, return an array in format: + * array(PEAR_TASK_ERROR_???[, param1][, param2][, ...]) + * + * For PEAR_TASK_ERROR_MISSING_ATTRIB, pass the attribute name in + * For PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, pass the attribute name and an array + * of legal values in + * @static + * @abstract + */ + function validateXml($pkg, $xml, $config, $fileXml) + { + } + + /** + * Initialize a task instance with the parameters + * @param array raw, parsed xml + * @param array attributes from the tag containing this task + * @param string|null last installed version of this package + * @abstract + */ + function init($xml, $fileAttributes, $lastVersion) + { + } + + /** + * Begin a task processing session. All multiple tasks will be processed after each file + * has been successfully installed, all simple tasks should perform their task here and + * return any errors using the custom throwError() method to allow forward compatibility + * + * This method MUST NOT write out any changes to disk + * @param PEAR_PackageFile_v2 + * @param string file contents + * @param string the eventual final file location (informational only) + * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail + * (use $this->throwError), otherwise return the new contents + * @abstract + */ + function startSession($pkg, $contents, $dest) + { + } + + /** + * This method is used to process each of the tasks for a particular multiple class + * type. Simple tasks need not implement this method. + * @param array an array of tasks + * @access protected + * @static + * @abstract + */ + function run($tasks) + { + } + + /** + * @static + * @final + */ + function hasPostinstallTasks() + { + return isset($GLOBALS['_PEAR_TASK_POSTINSTANCES']); + } + + /** + * @static + * @final + */ + function runPostinstallTasks() + { + foreach ($GLOBALS['_PEAR_TASK_POSTINSTANCES'] as $class => $tasks) { + $err = call_user_func(array($class, 'run'), + $GLOBALS['_PEAR_TASK_POSTINSTANCES'][$class]); + if ($err) { + return PEAR_Task_Common::throwError($err); + } + } + unset($GLOBALS['_PEAR_TASK_POSTINSTANCES']); + } + + /** + * Determines whether a role is a script + * @return bool + */ + function isScript() + { + return $this->type == 'script'; + } + + function throwError($msg, $code = -1) + { + include_once 'PEAR.php'; + return PEAR::raiseError($msg, $code); + } +} +?> \ No newline at end of file diff --git a/includes/pear/PEAR/Task/Postinstallscript.php b/includes/pear/PEAR/Task/Postinstallscript.php new file mode 100644 index 0000000..6281340 --- /dev/null +++ b/includes/pear/PEAR/Task/Postinstallscript.php @@ -0,0 +1,323 @@ + + * + * PHP versions 4 and 5 + * + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a1 + */ +/** + * Base class + */ +require_once 'PEAR/Task/Common.php'; +/** + * Implements the postinstallscript file task. + * + * Note that post-install scripts are handled separately from installation, by the + * "pear run-scripts" command + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a1 + */ +class PEAR_Task_Postinstallscript extends PEAR_Task_Common +{ + var $type = 'script'; + var $_class; + var $_params; + var $_obj; + /** + * + * @var PEAR_PackageFile_v2 + */ + var $_pkg; + var $_contents; + var $phase = PEAR_TASK_INSTALL; + + /** + * Validate the raw xml at parsing-time. + * + * This also attempts to validate the script to make sure it meets the criteria + * for a post-install script + * @param PEAR_PackageFile_v2 + * @param array The XML contents of the tag + * @param PEAR_Config + * @param array the entire parsed tag + * @static + */ + function validateXml($pkg, $xml, $config, $fileXml) + { + if ($fileXml['role'] != 'php') { + return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' . + $fileXml['name'] . '" must be role="php"'); + } + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + $file = $pkg->getFileContents($fileXml['name']); + if (PEAR::isError($file)) { + PEAR::popErrorHandling(); + return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' . + $fileXml['name'] . '" is not valid: ' . + $file->getMessage()); + } elseif ($file === null) { + return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' . + $fileXml['name'] . '" could not be retrieved for processing!'); + } else { + $analysis = $pkg->analyzeSourceCode($file, true); + if (!$analysis) { + PEAR::popErrorHandling(); + $warnings = ''; + foreach ($pkg->getValidationWarnings() as $warn) { + $warnings .= $warn['message'] . "\n"; + } + return array(PEAR_TASK_ERROR_INVALID, 'Analysis of post-install script "' . + $fileXml['name'] . '" failed: ' . $warnings); + } + if (count($analysis['declared_classes']) != 1) { + PEAR::popErrorHandling(); + return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' . + $fileXml['name'] . '" must declare exactly 1 class'); + } + $class = $analysis['declared_classes'][0]; + if ($class != str_replace(array('/', '.php'), array('_', ''), + $fileXml['name']) . '_postinstall') { + PEAR::popErrorHandling(); + return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' . + $fileXml['name'] . '" class "' . $class . '" must be named "' . + str_replace(array('/', '.php'), array('_', ''), + $fileXml['name']) . '_postinstall"'); + } + if (!isset($analysis['declared_methods'][$class])) { + PEAR::popErrorHandling(); + return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' . + $fileXml['name'] . '" must declare methods init() and run()'); + } + $methods = array('init' => 0, 'run' => 1); + foreach ($analysis['declared_methods'][$class] as $method) { + if (isset($methods[$method])) { + unset($methods[$method]); + } + } + if (count($methods)) { + PEAR::popErrorHandling(); + return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' . + $fileXml['name'] . '" must declare methods init() and run()'); + } + } + PEAR::popErrorHandling(); + $definedparams = array(); + $tasksNamespace = $pkg->getTasksNs() . ':'; + if (!isset($xml[$tasksNamespace . 'paramgroup']) && isset($xml['paramgroup'])) { + // in order to support the older betas, which did not expect internal tags + // to also use the namespace + $tasksNamespace = ''; + } + if (isset($xml[$tasksNamespace . 'paramgroup'])) { + $params = $xml[$tasksNamespace . 'paramgroup']; + if (!is_array($params) || !isset($params[0])) { + $params = array($params); + } + foreach ($params as $param) { + if (!isset($param[$tasksNamespace . 'id'])) { + return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' . + $fileXml['name'] . '" must have ' . + 'an ' . $tasksNamespace . 'id> tag'); + } + if (isset($param[$tasksNamespace . 'name'])) { + if (!in_array($param[$tasksNamespace . 'name'], $definedparams)) { + return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' . + $fileXml['name'] . '" ' . $tasksNamespace . + 'paramgroup> id "' . $param[$tasksNamespace . 'id'] . + '" parameter "' . $param[$tasksNamespace . 'name'] . + '" has not been previously defined'); + } + if (!isset($param[$tasksNamespace . 'conditiontype'])) { + return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' . + $fileXml['name'] . '" ' . $tasksNamespace . + 'paramgroup> id "' . $param[$tasksNamespace . 'id'] . + '" must have a ' . $tasksNamespace . + 'conditiontype> tag containing either "=", ' . + '"!=", or "preg_match"'); + } + if (!in_array($param[$tasksNamespace . 'conditiontype'], + array('=', '!=', 'preg_match'))) { + return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' . + $fileXml['name'] . '" ' . $tasksNamespace . + 'paramgroup> id "' . $param[$tasksNamespace . 'id'] . + '" must have a ' . $tasksNamespace . + 'conditiontype> tag containing either "=", ' . + '"!=", or "preg_match"'); + } + if (!isset($param[$tasksNamespace . 'value'])) { + return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' . + $fileXml['name'] . '" ' . $tasksNamespace . + 'paramgroup> id "' . $param[$tasksNamespace . 'id'] . + '" must have a ' . $tasksNamespace . + 'value> tag containing expected parameter value'); + } + } + if (isset($param[$tasksNamespace . 'instructions'])) { + if (!is_string($param[$tasksNamespace . 'instructions'])) { + return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' . + $fileXml['name'] . '" ' . $tasksNamespace . + 'paramgroup> id "' . $param[$tasksNamespace . 'id'] . + '" ' . $tasksNamespace . 'instructions> must be simple text'); + } + } + if (!isset($param[$tasksNamespace . 'param'])) { + continue; // is no longer required + } + $subparams = $param[$tasksNamespace . 'param']; + if (!is_array($subparams) || !isset($subparams[0])) { + $subparams = array($subparams); + } + foreach ($subparams as $subparam) { + if (!isset($subparam[$tasksNamespace . 'name'])) { + return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' . + $fileXml['name'] . '" parameter for ' . + $tasksNamespace . 'paramgroup> id "' . + $param[$tasksNamespace . 'id'] . '" must have ' . + 'a ' . $tasksNamespace . 'name> tag'); + } + if (!preg_match('/[a-zA-Z0-9]+/', + $subparam[$tasksNamespace . 'name'])) { + return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' . + $fileXml['name'] . '" parameter "' . + $subparam[$tasksNamespace . 'name'] . + '" for ' . $tasksNamespace . 'paramgroup> id "' . + $param[$tasksNamespace . 'id'] . + '" is not a valid name. Must contain only alphanumeric characters'); + } + if (!isset($subparam[$tasksNamespace . 'prompt'])) { + return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' . + $fileXml['name'] . '" parameter "' . + $subparam[$tasksNamespace . 'name'] . + '" for ' . $tasksNamespace . 'paramgroup> id "' . + $param[$tasksNamespace . 'id'] . + '" must have a ' . $tasksNamespace . 'prompt> tag'); + } + if (!isset($subparam[$tasksNamespace . 'type'])) { + return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' . + $fileXml['name'] . '" parameter "' . + $subparam[$tasksNamespace . 'name'] . + '" for ' . $tasksNamespace . 'paramgroup> id "' . + $param[$tasksNamespace . 'id'] . + '" must have a ' . $tasksNamespace . 'type> tag'); + } + $definedparams[] = $param[$tasksNamespace . 'id'] . '::' . + $subparam[$tasksNamespace . 'name']; + } + } + } + return true; + } + + /** + * Initialize a task instance with the parameters + * @param array raw, parsed xml + * @param array attributes from the tag containing this task + * @param string|null last installed version of this package, if any (useful for upgrades) + */ + function init($xml, $fileattribs, $lastversion) + { + $this->_class = str_replace('/', '_', $fileattribs['name']); + $this->_filename = $fileattribs['name']; + $this->_class = str_replace ('.php', '', $this->_class) . '_postinstall'; + $this->_params = $xml; + $this->_lastversion = $lastversion; + } + + /** + * Strip the tasks: namespace from internal params + * + * @access private + */ + function _stripNamespace($params = null) + { + if ($params === null) { + $params = array(); + if (!is_array($this->_params)) { + return; + } + foreach ($this->_params as $i => $param) { + if (is_array($param)) { + $param = $this->_stripNamespace($param); + } + $params[str_replace($this->_pkg->getTasksNs() . ':', '', $i)] = $param; + } + $this->_params = $params; + } else { + $newparams = array(); + foreach ($params as $i => $param) { + if (is_array($param)) { + $param = $this->_stripNamespace($param); + } + $newparams[str_replace($this->_pkg->getTasksNs() . ':', '', $i)] = $param; + } + return $newparams; + } + } + + /** + * Unlike other tasks, the installed file name is passed in instead of the file contents, + * because this task is handled post-installation + * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 + * @param string file name + * @return bool|PEAR_Error false to skip this file, PEAR_Error to fail + * (use $this->throwError) + */ + function startSession($pkg, $contents) + { + if ($this->installphase != PEAR_TASK_INSTALL) { + return false; + } + // remove the tasks: namespace if present + $this->_pkg = $pkg; + $this->_stripNamespace(); + $this->logger->log(0, 'Including external post-installation script "' . + $contents . '" - any errors are in this script'); + include_once $contents; + if (class_exists($this->_class)) { + $this->logger->log(0, 'Inclusion succeeded'); + } else { + return $this->throwError('init of post-install script class "' . $this->_class + . '" failed'); + } + $this->_obj = new $this->_class; + $this->logger->log(1, 'running post-install script "' . $this->_class . '->init()"'); + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + $res = $this->_obj->init($this->config, $pkg, $this->_lastversion); + PEAR::popErrorHandling(); + if ($res) { + $this->logger->log(0, 'init succeeded'); + } else { + return $this->throwError('init of post-install script "' . $this->_class . + '->init()" failed'); + } + $this->_contents = $contents; + return true; + } + + /** + * No longer used + * @see PEAR_PackageFile_v2::runPostinstallScripts() + * @param array an array of tasks + * @param string install or upgrade + * @access protected + * @static + */ + function run() + { + } +} +?> \ No newline at end of file diff --git a/includes/pear/PEAR/Task/Postinstallscript/rw.php b/includes/pear/PEAR/Task/Postinstallscript/rw.php new file mode 100644 index 0000000..c48db6c --- /dev/null +++ b/includes/pear/PEAR/Task/Postinstallscript/rw.php @@ -0,0 +1,169 @@ + - read/write version + * + * PHP versions 4 and 5 + * + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a10 + */ +/** + * Base class + */ +require_once 'PEAR/Task/Postinstallscript.php'; +/** + * Abstracts the postinstallscript file task xml. + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a10 + */ +class PEAR_Task_Postinstallscript_rw extends PEAR_Task_Postinstallscript +{ + /** + * parent package file object + * + * @var PEAR_PackageFile_v2_rw + */ + var $_pkg; + /** + * Enter description here... + * + * @param PEAR_PackageFile_v2_rw $pkg + * @param PEAR_Config $config + * @param PEAR_Frontend $logger + * @param array $fileXml + * @return PEAR_Task_Postinstallscript_rw + */ + function PEAR_Task_Postinstallscript_rw(&$pkg, &$config, &$logger, $fileXml) + { + parent::PEAR_Task_Common($config, $logger, PEAR_TASK_PACKAGE); + $this->_contents = $fileXml; + $this->_pkg = &$pkg; + $this->_params = array(); + } + + function validate() + { + return $this->validateXml($this->_pkg, $this->_params, $this->config, $this->_contents); + } + + function getName() + { + return 'postinstallscript'; + } + + /** + * add a simple to the post-install script + * + * Order is significant, so call this method in the same + * sequence the users should see the paramgroups. The $params + * parameter should either be the result of a call to {@link getParam()} + * or an array of calls to getParam(). + * + * Use {@link addConditionTypeGroup()} to add a containing + * a tag + * @param string $id id as seen by the script + * @param array|false $params array of getParam() calls, or false for no params + * @param string|false $instructions + */ + function addParamGroup($id, $params = false, $instructions = false) + { + if ($params && isset($params[0]) && !isset($params[1])) { + $params = $params[0]; + } + $stuff = + array( + $this->_pkg->getTasksNs() . ':id' => $id, + ); + if ($instructions) { + $stuff[$this->_pkg->getTasksNs() . ':instructions'] = $instructions; + } + if ($params) { + $stuff[$this->_pkg->getTasksNs() . ':param'] = $params; + } + $this->_params[$this->_pkg->getTasksNs() . ':paramgroup'][] = $stuff; + } + + /** + * add a complex to the post-install script with conditions + * + * This inserts a with + * + * Order is significant, so call this method in the same + * sequence the users should see the paramgroups. The $params + * parameter should either be the result of a call to {@link getParam()} + * or an array of calls to getParam(). + * + * Use {@link addParamGroup()} to add a simple + * + * @param string $id id as seen by the script + * @param string $oldgroup id of the section referenced by + * + * @param string $param name of the from the older section referenced + * by + * @param string $value value to match of the parameter + * @param string $conditiontype one of '=', '!=', 'preg_match' + * @param array|false $params array of getParam() calls, or false for no params + * @param string|false $instructions + */ + function addConditionTypeGroup($id, $oldgroup, $param, $value, $conditiontype = '=', + $params = false, $instructions = false) + { + if ($params && isset($params[0]) && !isset($params[1])) { + $params = $params[0]; + } + $stuff = array( + $this->_pkg->getTasksNs() . ':id' => $id, + ); + if ($instructions) { + $stuff[$this->_pkg->getTasksNs() . ':instructions'] = $instructions; + } + $stuff[$this->_pkg->getTasksNs() . ':name'] = $oldgroup . '::' . $param; + $stuff[$this->_pkg->getTasksNs() . ':conditiontype'] = $conditiontype; + $stuff[$this->_pkg->getTasksNs() . ':value'] = $value; + if ($params) { + $stuff[$this->_pkg->getTasksNs() . ':param'] = $params; + } + $this->_params[$this->_pkg->getTasksNs() . ':paramgroup'][] = $stuff; + } + + function getXml() + { + return $this->_params; + } + + /** + * Use to set up a param tag for use in creating a paramgroup + * @static + */ + function getParam($name, $prompt, $type = 'string', $default = null) + { + if ($default !== null) { + return + array( + $this->_pkg->getTasksNs() . ':name' => $name, + $this->_pkg->getTasksNs() . ':prompt' => $prompt, + $this->_pkg->getTasksNs() . ':type' => $type, + $this->_pkg->getTasksNs() . ':default' => $default + ); + } + return + array( + $this->_pkg->getTasksNs() . ':name' => $name, + $this->_pkg->getTasksNs() . ':prompt' => $prompt, + $this->_pkg->getTasksNs() . ':type' => $type, + ); + } +} +?> \ No newline at end of file diff --git a/includes/pear/PEAR/Task/Replace.php b/includes/pear/PEAR/Task/Replace.php new file mode 100644 index 0000000..4a36b96 --- /dev/null +++ b/includes/pear/PEAR/Task/Replace.php @@ -0,0 +1,176 @@ + + * + * PHP versions 4 and 5 + * + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a1 + */ +/** + * Base class + */ +require_once 'PEAR/Task/Common.php'; +/** + * Implements the replace file task. + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a1 + */ +class PEAR_Task_Replace extends PEAR_Task_Common +{ + var $type = 'simple'; + var $phase = PEAR_TASK_PACKAGEANDINSTALL; + var $_replacements; + + /** + * Validate the raw xml at parsing-time. + * @param PEAR_PackageFile_v2 + * @param array raw, parsed xml + * @param PEAR_Config + * @static + */ + function validateXml($pkg, $xml, $config, $fileXml) + { + if (!isset($xml['attribs'])) { + return array(PEAR_TASK_ERROR_NOATTRIBS); + } + if (!isset($xml['attribs']['type'])) { + return array(PEAR_TASK_ERROR_MISSING_ATTRIB, 'type'); + } + if (!isset($xml['attribs']['to'])) { + return array(PEAR_TASK_ERROR_MISSING_ATTRIB, 'to'); + } + if (!isset($xml['attribs']['from'])) { + return array(PEAR_TASK_ERROR_MISSING_ATTRIB, 'from'); + } + if ($xml['attribs']['type'] == 'pear-config') { + if (!in_array($xml['attribs']['to'], $config->getKeys())) { + return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'to', $xml['attribs']['to'], + $config->getKeys()); + } + } elseif ($xml['attribs']['type'] == 'php-const') { + if (defined($xml['attribs']['to'])) { + return true; + } else { + return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'to', $xml['attribs']['to'], + array('valid PHP constant')); + } + } elseif ($xml['attribs']['type'] == 'package-info') { + if (in_array($xml['attribs']['to'], + array('name', 'summary', 'channel', 'notes', 'extends', 'description', + 'release_notes', 'license', 'release-license', 'license-uri', + 'version', 'api-version', 'state', 'api-state', 'release_date', + 'date', 'time'))) { + return true; + } else { + return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'to', $xml['attribs']['to'], + array('name', 'summary', 'channel', 'notes', 'extends', 'description', + 'release_notes', 'license', 'release-license', 'license-uri', + 'version', 'api-version', 'state', 'api-state', 'release_date', + 'date', 'time')); + } + } else { + return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'type', $xml['attribs']['type'], + array('pear-config', 'package-info', 'php-const')); + } + return true; + } + + /** + * Initialize a task instance with the parameters + * @param array raw, parsed xml + * @param unused + */ + function init($xml, $attribs) + { + $this->_replacements = isset($xml['attribs']) ? array($xml) : $xml; + } + + /** + * Do a package.xml 1.0 replacement, with additional package-info fields available + * + * See validateXml() source for the complete list of allowed fields + * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 + * @param string file contents + * @param string the eventual final file location (informational only) + * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail + * (use $this->throwError), otherwise return the new contents + */ + function startSession($pkg, $contents, $dest) + { + $subst_from = $subst_to = array(); + foreach ($this->_replacements as $a) { + $a = $a['attribs']; + $to = ''; + if ($a['type'] == 'pear-config') { + if ($this->installphase == PEAR_TASK_PACKAGE) { + return false; + } + if ($a['to'] == 'master_server') { + $chan = $this->registry->getChannel($pkg->getChannel()); + if (!PEAR::isError($chan)) { + $to = $chan->getServer(); + } else { + $this->logger->log(0, "$dest: invalid pear-config replacement: $a[to]"); + return false; + } + } else { + if ($this->config->isDefinedLayer('ftp')) { + // try the remote config file first + $to = $this->config->get($a['to'], 'ftp', $pkg->getChannel()); + if (is_null($to)) { + // then default to local + $to = $this->config->get($a['to'], null, $pkg->getChannel()); + } + } else { + $to = $this->config->get($a['to'], null, $pkg->getChannel()); + } + } + if (is_null($to)) { + $this->logger->log(0, "$dest: invalid pear-config replacement: $a[to]"); + return false; + } + } elseif ($a['type'] == 'php-const') { + if ($this->installphase == PEAR_TASK_PACKAGE) { + return false; + } + if (defined($a['to'])) { + $to = constant($a['to']); + } else { + $this->logger->log(0, "$dest: invalid php-const replacement: $a[to]"); + return false; + } + } else { + if ($t = $pkg->packageInfo($a['to'])) { + $to = $t; + } else { + $this->logger->log(0, "$dest: invalid package-info replacement: $a[to]"); + return false; + } + } + if (!is_null($to)) { + $subst_from[] = $a['from']; + $subst_to[] = $to; + } + } + $this->logger->log(3, "doing " . sizeof($subst_from) . + " substitution(s) for $dest"); + if (sizeof($subst_from)) { + $contents = str_replace($subst_from, $subst_to, $contents); + } + return $contents; + } +} +?> \ No newline at end of file diff --git a/includes/pear/PEAR/Task/Replace/rw.php b/includes/pear/PEAR/Task/Replace/rw.php new file mode 100644 index 0000000..28496d1 --- /dev/null +++ b/includes/pear/PEAR/Task/Replace/rw.php @@ -0,0 +1,61 @@ + - read/write version + * + * PHP versions 4 and 5 + * + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a10 + */ +/** + * Base class + */ +require_once 'PEAR/Task/Replace.php'; +/** + * Abstracts the replace task xml. + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a10 + */ +class PEAR_Task_Replace_rw extends PEAR_Task_Replace +{ + function PEAR_Task_Replace_rw(&$pkg, &$config, &$logger, $fileXml) + { + parent::PEAR_Task_Common($config, $logger, PEAR_TASK_PACKAGE); + $this->_contents = $fileXml; + $this->_pkg = &$pkg; + $this->_params = array(); + } + + function validate() + { + return $this->validateXml($this->_pkg, $this->_params, $this->config, $this->_contents); + } + + function setInfo($from, $to, $type) + { + $this->_params = array('attribs' => array('from' => $from, 'to' => $to, 'type' => $type)); + } + + function getName() + { + return 'replace'; + } + + function getXml() + { + return $this->_params; + } +} +?> \ No newline at end of file diff --git a/includes/pear/PEAR/Task/Unixeol.php b/includes/pear/PEAR/Task/Unixeol.php new file mode 100644 index 0000000..2c20313 --- /dev/null +++ b/includes/pear/PEAR/Task/Unixeol.php @@ -0,0 +1,77 @@ + + * + * PHP versions 4 and 5 + * + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a1 + */ +/** + * Base class + */ +require_once 'PEAR/Task/Common.php'; +/** + * Implements the unix line endings file task. + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a1 + */ +class PEAR_Task_Unixeol extends PEAR_Task_Common +{ + var $type = 'simple'; + var $phase = PEAR_TASK_PACKAGE; + var $_replacements; + + /** + * Validate the raw xml at parsing-time. + * @param PEAR_PackageFile_v2 + * @param array raw, parsed xml + * @param PEAR_Config + * @static + */ + function validateXml($pkg, $xml, $config, $fileXml) + { + if ($xml != '') { + return array(PEAR_TASK_ERROR_INVALID, 'no attributes allowed'); + } + return true; + } + + /** + * Initialize a task instance with the parameters + * @param array raw, parsed xml + * @param unused + */ + function init($xml, $attribs) + { + } + + /** + * Replace all line endings with line endings customized for the current OS + * + * See validateXml() source for the complete list of allowed fields + * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 + * @param string file contents + * @param string the eventual final file location (informational only) + * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail + * (use $this->throwError), otherwise return the new contents + */ + function startSession($pkg, $contents, $dest) + { + $this->logger->log(3, "replacing all line endings with \\n in $dest"); + return preg_replace("/\r\n|\n\r|\r|\n/", "\n", $contents); + } +} +?> \ No newline at end of file diff --git a/includes/pear/PEAR/Task/Unixeol/rw.php b/includes/pear/PEAR/Task/Unixeol/rw.php new file mode 100644 index 0000000..5348fac --- /dev/null +++ b/includes/pear/PEAR/Task/Unixeol/rw.php @@ -0,0 +1,56 @@ + - read/write version + * + * PHP versions 4 and 5 + * + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a10 + */ +/** + * Base class + */ +require_once 'PEAR/Task/Unixeol.php'; +/** + * Abstracts the unixeol task xml. + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a10 + */ +class PEAR_Task_Unixeol_rw extends PEAR_Task_Unixeol +{ + function PEAR_Task_Unixeol_rw(&$pkg, &$config, &$logger, $fileXml) + { + parent::PEAR_Task_Common($config, $logger, PEAR_TASK_PACKAGE); + $this->_contents = $fileXml; + $this->_pkg = &$pkg; + $this->_params = array(); + } + + function validate() + { + return true; + } + + function getName() + { + return 'unixeol'; + } + + function getXml() + { + return ''; + } +} +?> \ No newline at end of file diff --git a/includes/pear/PEAR/Task/Windowseol.php b/includes/pear/PEAR/Task/Windowseol.php new file mode 100644 index 0000000..d2bd2c8 --- /dev/null +++ b/includes/pear/PEAR/Task/Windowseol.php @@ -0,0 +1,77 @@ + + * + * PHP versions 4 and 5 + * + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a1 + */ +/** + * Base class + */ +require_once 'PEAR/Task/Common.php'; +/** + * Implements the windows line endsings file task. + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a1 + */ +class PEAR_Task_Windowseol extends PEAR_Task_Common +{ + var $type = 'simple'; + var $phase = PEAR_TASK_PACKAGE; + var $_replacements; + + /** + * Validate the raw xml at parsing-time. + * @param PEAR_PackageFile_v2 + * @param array raw, parsed xml + * @param PEAR_Config + * @static + */ + function validateXml($pkg, $xml, $config, $fileXml) + { + if ($xml != '') { + return array(PEAR_TASK_ERROR_INVALID, 'no attributes allowed'); + } + return true; + } + + /** + * Initialize a task instance with the parameters + * @param array raw, parsed xml + * @param unused + */ + function init($xml, $attribs) + { + } + + /** + * Replace all line endings with windows line endings + * + * See validateXml() source for the complete list of allowed fields + * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 + * @param string file contents + * @param string the eventual final file location (informational only) + * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail + * (use $this->throwError), otherwise return the new contents + */ + function startSession($pkg, $contents, $dest) + { + $this->logger->log(3, "replacing all line endings with \\r\\n in $dest"); + return preg_replace("/\r\n|\n\r|\r|\n/", "\r\n", $contents); + } +} +?> \ No newline at end of file diff --git a/includes/pear/PEAR/Task/Windowseol/rw.php b/includes/pear/PEAR/Task/Windowseol/rw.php new file mode 100644 index 0000000..e86e4f0 --- /dev/null +++ b/includes/pear/PEAR/Task/Windowseol/rw.php @@ -0,0 +1,56 @@ + - read/write version + * + * PHP versions 4 and 5 + * + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a10 + */ +/** + * Base class + */ +require_once 'PEAR/Task/Windowseol.php'; +/** + * Abstracts the windowseol task xml. + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a10 + */ +class PEAR_Task_Windowseol_rw extends PEAR_Task_Windowseol +{ + function PEAR_Task_Windowseol_rw(&$pkg, &$config, &$logger, $fileXml) + { + parent::PEAR_Task_Common($config, $logger, PEAR_TASK_PACKAGE); + $this->_contents = $fileXml; + $this->_pkg = &$pkg; + $this->_params = array(); + } + + function validate() + { + return true; + } + + function getName() + { + return 'windowseol'; + } + + function getXml() + { + return ''; + } +} +?> \ No newline at end of file diff --git a/includes/pear/PEAR/Validate.php b/includes/pear/PEAR/Validate.php new file mode 100644 index 0000000..3c2471b --- /dev/null +++ b/includes/pear/PEAR/Validate.php @@ -0,0 +1,629 @@ + + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a1 + */ +/**#@+ + * Constants for install stage + */ +define('PEAR_VALIDATE_INSTALLING', 1); +define('PEAR_VALIDATE_UNINSTALLING', 2); // this is not bit-mapped like the others +define('PEAR_VALIDATE_NORMAL', 3); +define('PEAR_VALIDATE_DOWNLOADING', 4); // this is not bit-mapped like the others +define('PEAR_VALIDATE_PACKAGING', 7); +/**#@-*/ +require_once 'PEAR/Common.php'; +require_once 'PEAR/Validator/PECL.php'; + +/** + * Validation class for package.xml - channel-level advanced validation + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a1 + */ +class PEAR_Validate +{ + var $packageregex = _PEAR_COMMON_PACKAGE_NAME_PREG; + /** + * @var PEAR_PackageFile_v1|PEAR_PackageFile_v2 + */ + var $_packagexml; + /** + * @var int one of the PEAR_VALIDATE_* constants + */ + var $_state = PEAR_VALIDATE_NORMAL; + /** + * Format: ('error' => array('field' => name, 'reason' => reason), 'warning' => same) + * @var array + * @access private + */ + var $_failures = array('error' => array(), 'warning' => array()); + + /** + * Override this method to handle validation of normal package names + * @param string + * @return bool + * @access protected + */ + function _validPackageName($name) + { + return (bool) preg_match('/^' . $this->packageregex . '\\z/', $name); + } + + /** + * @param string package name to validate + * @param string name of channel-specific validation package + * @final + */ + function validPackageName($name, $validatepackagename = false) + { + if ($validatepackagename) { + if (strtolower($name) == strtolower($validatepackagename)) { + return (bool) preg_match('/^[a-zA-Z0-9_]+(?:\.[a-zA-Z0-9_]+)*\\z/', $name); + } + } + return $this->_validPackageName($name); + } + + /** + * This validates a bundle name, and bundle names must conform + * to the PEAR naming convention, so the method is final and static. + * @param string + * @final + * @static + */ + function validGroupName($name) + { + return (bool) preg_match('/^' . _PEAR_COMMON_PACKAGE_NAME_PREG . '\\z/', $name); + } + + /** + * Determine whether $state represents a valid stability level + * @param string + * @return bool + * @static + * @final + */ + function validState($state) + { + return in_array($state, array('snapshot', 'devel', 'alpha', 'beta', 'stable')); + } + + /** + * Get a list of valid stability levels + * @return array + * @static + * @final + */ + function getValidStates() + { + return array('snapshot', 'devel', 'alpha', 'beta', 'stable'); + } + + /** + * Determine whether a version is a properly formatted version number that can be used + * by version_compare + * @param string + * @return bool + * @static + * @final + */ + function validVersion($ver) + { + return (bool) preg_match(PEAR_COMMON_PACKAGE_VERSION_PREG, $ver); + } + + /** + * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 + */ + function setPackageFile(&$pf) + { + $this->_packagexml = &$pf; + } + + /** + * @access private + */ + function _addFailure($field, $reason) + { + $this->_failures['errors'][] = array('field' => $field, 'reason' => $reason); + } + + /** + * @access private + */ + function _addWarning($field, $reason) + { + $this->_failures['warnings'][] = array('field' => $field, 'reason' => $reason); + } + + function getFailures() + { + $failures = $this->_failures; + $this->_failures = array('warnings' => array(), 'errors' => array()); + return $failures; + } + + /** + * @param int one of the PEAR_VALIDATE_* constants + */ + function validate($state = null) + { + if (!isset($this->_packagexml)) { + return false; + } + if ($state !== null) { + $this->_state = $state; + } + $this->_failures = array('warnings' => array(), 'errors' => array()); + $this->validatePackageName(); + $this->validateVersion(); + $this->validateMaintainers(); + $this->validateDate(); + $this->validateSummary(); + $this->validateDescription(); + $this->validateLicense(); + $this->validateNotes(); + if ($this->_packagexml->getPackagexmlVersion() == '1.0') { + $this->validateState(); + $this->validateFilelist(); + } elseif ($this->_packagexml->getPackagexmlVersion() == '2.0' || + $this->_packagexml->getPackagexmlVersion() == '2.1') { + $this->validateTime(); + $this->validateStability(); + $this->validateDeps(); + $this->validateMainFilelist(); + $this->validateReleaseFilelist(); + //$this->validateGlobalTasks(); + $this->validateChangelog(); + } + return !((bool) count($this->_failures['errors'])); + } + + /** + * @access protected + */ + function validatePackageName() + { + if ($this->_state == PEAR_VALIDATE_PACKAGING || + $this->_state == PEAR_VALIDATE_NORMAL) { + if (($this->_packagexml->getPackagexmlVersion() == '2.0' || + $this->_packagexml->getPackagexmlVersion() == '2.1') && + $this->_packagexml->getExtends()) { + $version = $this->_packagexml->getVersion() . ''; + $name = $this->_packagexml->getPackage(); + $test = array_shift($a = explode('.', $version)); + if ($test == '0') { + return true; + } + $vlen = strlen($test); + $majver = substr($name, strlen($name) - $vlen); + while ($majver && !is_numeric($majver{0})) { + $majver = substr($majver, 1); + } + if ($majver != $test) { + $this->_addWarning('package', "package $name extends package " . + $this->_packagexml->getExtends() . ' and so the name should ' . + 'have a postfix equal to the major version like "' . + $this->_packagexml->getExtends() . $test . '"'); + return true; + } elseif (substr($name, 0, strlen($name) - $vlen) != + $this->_packagexml->getExtends()) { + $this->_addWarning('package', "package $name extends package " . + $this->_packagexml->getExtends() . ' and so the name must ' . + 'be an extension like "' . $this->_packagexml->getExtends() . + $test . '"'); + return true; + } + } + } + if (!$this->validPackageName($this->_packagexml->getPackage())) { + $this->_addFailure('name', 'package name "' . + $this->_packagexml->getPackage() . '" is invalid'); + return false; + } + } + + /** + * @access protected + */ + function validateVersion() + { + if ($this->_state != PEAR_VALIDATE_PACKAGING) { + if (!$this->validVersion($this->_packagexml->getVersion())) { + $this->_addFailure('version', + 'Invalid version number "' . $this->_packagexml->getVersion() . '"'); + } + return false; + } + $version = $this->_packagexml->getVersion(); + $versioncomponents = explode('.', $version); + if (count($versioncomponents) != 3) { + $this->_addWarning('version', + 'A version number should have 3 decimals (x.y.z)'); + return true; + } + $name = $this->_packagexml->getPackage(); + // version must be based upon state + switch ($this->_packagexml->getState()) { + case 'snapshot' : + return true; + case 'devel' : + if ($versioncomponents[0] . 'a' == '0a') { + return true; + } + if ($versioncomponents[0] == 0) { + $versioncomponents[0] = '0'; + $this->_addWarning('version', + 'version "' . $version . '" should be "' . + implode('.' ,$versioncomponents) . '"'); + } else { + $this->_addWarning('version', + 'packages with devel stability must be < version 1.0.0'); + } + return true; + break; + case 'alpha' : + case 'beta' : + // check for a package that extends a package, + // like Foo and Foo2 + if ($this->_state == PEAR_VALIDATE_PACKAGING) { + if (substr($versioncomponents[2], 1, 2) == 'rc') { + $this->_addFailure('version', 'Release Candidate versions ' . + 'must have capital RC, not lower-case rc'); + return false; + } + } + if (!$this->_packagexml->getExtends()) { + if ($versioncomponents[0] == '1') { + if ($versioncomponents[2]{0} == '0') { + if ($versioncomponents[2] == '0') { + // version 1.*.0000 + $this->_addWarning('version', + 'version 1.' . $versioncomponents[1] . + '.0 probably should not be alpha or beta'); + return true; + } elseif (strlen($versioncomponents[2]) > 1) { + // version 1.*.0RC1 or 1.*.0beta24 etc. + return true; + } else { + // version 1.*.0 + $this->_addWarning('version', + 'version 1.' . $versioncomponents[1] . + '.0 probably should not be alpha or beta'); + return true; + } + } else { + $this->_addWarning('version', + 'bugfix versions (1.3.x where x > 0) probably should ' . + 'not be alpha or beta'); + return true; + } + } elseif ($versioncomponents[0] != '0') { + $this->_addWarning('version', + 'major versions greater than 1 are not allowed for packages ' . + 'without an tag or an identical postfix (foo2 v2.0.0)'); + return true; + } + if ($versioncomponents[0] . 'a' == '0a') { + return true; + } + if ($versioncomponents[0] == 0) { + $versioncomponents[0] = '0'; + $this->_addWarning('version', + 'version "' . $version . '" should be "' . + implode('.' ,$versioncomponents) . '"'); + } + } else { + $vlen = strlen($versioncomponents[0] . ''); + $majver = substr($name, strlen($name) - $vlen); + while ($majver && !is_numeric($majver{0})) { + $majver = substr($majver, 1); + } + if (($versioncomponents[0] != 0) && $majver != $versioncomponents[0]) { + $this->_addWarning('version', 'first version number "' . + $versioncomponents[0] . '" must match the postfix of ' . + 'package name "' . $name . '" (' . + $majver . ')'); + return true; + } + if ($versioncomponents[0] == $majver) { + if ($versioncomponents[2]{0} == '0') { + if ($versioncomponents[2] == '0') { + // version 2.*.0000 + $this->_addWarning('version', + "version $majver." . $versioncomponents[1] . + '.0 probably should not be alpha or beta'); + return false; + } elseif (strlen($versioncomponents[2]) > 1) { + // version 2.*.0RC1 or 2.*.0beta24 etc. + return true; + } else { + // version 2.*.0 + $this->_addWarning('version', + "version $majver." . $versioncomponents[1] . + '.0 cannot be alpha or beta'); + return true; + } + } else { + $this->_addWarning('version', + "bugfix versions ($majver.x.y where y > 0) should " . + 'not be alpha or beta'); + return true; + } + } elseif ($versioncomponents[0] != '0') { + $this->_addWarning('version', + "only versions 0.x.y and $majver.x.y are allowed for alpha/beta releases"); + return true; + } + if ($versioncomponents[0] . 'a' == '0a') { + return true; + } + if ($versioncomponents[0] == 0) { + $versioncomponents[0] = '0'; + $this->_addWarning('version', + 'version "' . $version . '" should be "' . + implode('.' ,$versioncomponents) . '"'); + } + } + return true; + break; + case 'stable' : + if ($versioncomponents[0] == '0') { + $this->_addWarning('version', 'versions less than 1.0.0 cannot ' . + 'be stable'); + return true; + } + if (!is_numeric($versioncomponents[2])) { + if (preg_match('/\d+(rc|a|alpha|b|beta)\d*/i', + $versioncomponents[2])) { + $this->_addWarning('version', 'version "' . $version . '" or any ' . + 'RC/beta/alpha version cannot be stable'); + return true; + } + } + // check for a package that extends a package, + // like Foo and Foo2 + if ($this->_packagexml->getExtends()) { + $vlen = strlen($versioncomponents[0] . ''); + $majver = substr($name, strlen($name) - $vlen); + while ($majver && !is_numeric($majver{0})) { + $majver = substr($majver, 1); + } + if (($versioncomponents[0] != 0) && $majver != $versioncomponents[0]) { + $this->_addWarning('version', 'first version number "' . + $versioncomponents[0] . '" must match the postfix of ' . + 'package name "' . $name . '" (' . + $majver . ')'); + return true; + } + } elseif ($versioncomponents[0] > 1) { + $this->_addWarning('version', 'major version x in x.y.z may not be greater than ' . + '1 for any package that does not have an tag'); + } + return true; + break; + default : + return false; + break; + } + } + + /** + * @access protected + */ + function validateMaintainers() + { + // maintainers can only be truly validated server-side for most channels + // but allow this customization for those who wish it + return true; + } + + /** + * @access protected + */ + function validateDate() + { + if ($this->_state == PEAR_VALIDATE_NORMAL || + $this->_state == PEAR_VALIDATE_PACKAGING) { + + if (!preg_match('/(\d\d\d\d)\-(\d\d)\-(\d\d)/', + $this->_packagexml->getDate(), $res) || + count($res) < 4 + || !checkdate($res[2], $res[3], $res[1]) + ) { + $this->_addFailure('date', 'invalid release date "' . + $this->_packagexml->getDate() . '"'); + return false; + } + + if ($this->_state == PEAR_VALIDATE_PACKAGING && + $this->_packagexml->getDate() != date('Y-m-d')) { + $this->_addWarning('date', 'Release Date "' . + $this->_packagexml->getDate() . '" is not today'); + } + } + return true; + } + + /** + * @access protected + */ + function validateTime() + { + if (!$this->_packagexml->getTime()) { + // default of no time value set + return true; + } + + // packager automatically sets time, so only validate if pear validate is called + if ($this->_state = PEAR_VALIDATE_NORMAL) { + if (!preg_match('/\d\d:\d\d:\d\d/', + $this->_packagexml->getTime())) { + $this->_addFailure('time', 'invalid release time "' . + $this->_packagexml->getTime() . '"'); + return false; + } + + $result = preg_match('|\d{2}\:\d{2}\:\d{2}|', $this->_packagexml->getTime(), $matches); + if ($result === false || empty($matches)) { + $this->_addFailure('time', 'invalid release time "' . + $this->_packagexml->getTime() . '"'); + return false; + } + } + + return true; + } + + /** + * @access protected + */ + function validateState() + { + // this is the closest to "final" php4 can get + if (!PEAR_Validate::validState($this->_packagexml->getState())) { + if (strtolower($this->_packagexml->getState() == 'rc')) { + $this->_addFailure('state', 'RC is not a state, it is a version ' . + 'postfix, use ' . $this->_packagexml->getVersion() . 'RC1, state beta'); + } + $this->_addFailure('state', 'invalid release state "' . + $this->_packagexml->getState() . '", must be one of: ' . + implode(', ', PEAR_Validate::getValidStates())); + return false; + } + return true; + } + + /** + * @access protected + */ + function validateStability() + { + $ret = true; + $packagestability = $this->_packagexml->getState(); + $apistability = $this->_packagexml->getState('api'); + if (!PEAR_Validate::validState($packagestability)) { + $this->_addFailure('state', 'invalid release stability "' . + $this->_packagexml->getState() . '", must be one of: ' . + implode(', ', PEAR_Validate::getValidStates())); + $ret = false; + } + $apistates = PEAR_Validate::getValidStates(); + array_shift($apistates); // snapshot is not allowed + if (!in_array($apistability, $apistates)) { + $this->_addFailure('state', 'invalid API stability "' . + $this->_packagexml->getState('api') . '", must be one of: ' . + implode(', ', $apistates)); + $ret = false; + } + return $ret; + } + + /** + * @access protected + */ + function validateSummary() + { + return true; + } + + /** + * @access protected + */ + function validateDescription() + { + return true; + } + + /** + * @access protected + */ + function validateLicense() + { + return true; + } + + /** + * @access protected + */ + function validateNotes() + { + return true; + } + + /** + * for package.xml 2.0 only - channels can't use package.xml 1.0 + * @access protected + */ + function validateDependencies() + { + return true; + } + + /** + * for package.xml 1.0 only + * @access private + */ + function _validateFilelist() + { + return true; // placeholder for now + } + + /** + * for package.xml 2.0 only + * @access protected + */ + function validateMainFilelist() + { + return true; // placeholder for now + } + + /** + * for package.xml 2.0 only + * @access protected + */ + function validateReleaseFilelist() + { + return true; // placeholder for now + } + + /** + * @access protected + */ + function validateChangelog() + { + return true; + } + + /** + * @access protected + */ + function validateFilelist() + { + return true; + } + + /** + * @access protected + */ + function validateDeps() + { + return true; + } +} \ No newline at end of file diff --git a/includes/pear/PEAR/Validator/PECL.php b/includes/pear/PEAR/Validator/PECL.php new file mode 100644 index 0000000..7e0769a --- /dev/null +++ b/includes/pear/PEAR/Validator/PECL.php @@ -0,0 +1,63 @@ + + * @copyright 1997-2006 The PHP Group + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a5 + */ +/** + * This is the parent class for all validators + */ +require_once 'PEAR/Validate.php'; +/** + * Channel Validator for the pecl.php.net channel + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a5 + */ +class PEAR_Validator_PECL extends PEAR_Validate +{ + function validateVersion() + { + if ($this->_state == PEAR_VALIDATE_PACKAGING) { + $version = $this->_packagexml->getVersion(); + $versioncomponents = explode('.', $version); + $last = array_pop($versioncomponents); + if (substr($last, 1, 2) == 'rc') { + $this->_addFailure('version', 'Release Candidate versions must have ' . + 'upper-case RC, not lower-case rc'); + return false; + } + } + return true; + } + + function validatePackageName() + { + $ret = parent::validatePackageName(); + if ($this->_packagexml->getPackageType() == 'extsrc' || + $this->_packagexml->getPackageType() == 'zendextsrc') { + if (strtolower($this->_packagexml->getPackage()) != + strtolower($this->_packagexml->getProvidesExtension())) { + $this->_addWarning('providesextension', 'package name "' . + $this->_packagexml->getPackage() . '" is different from extension name "' . + $this->_packagexml->getProvidesExtension() . '"'); + } + } + return $ret; + } +} +?> \ No newline at end of file diff --git a/includes/pear/PEAR/Warning.php b/includes/pear/PEAR/Warning.php new file mode 100644 index 0000000..17ac905 --- /dev/null +++ b/includes/pear/PEAR/Warning.php @@ -0,0 +1,379 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id$ +require_once 'PEAR/Exception.php'; + +/** + * Exception class for internal PEAR_Warning exceptions + * @package PEAR + */ +class PEAR_WarningException extends PEAR_Exception {} + +interface PEAR_WarningInterface +{ + /** + * Get the severity of this warning ('warning', 'notice') + * @return string + */ + public function getLevel(); +} + +/** + * Warning mechanism for PEAR PHP5-only packages. + * + * For users: + * + * Unlike PEAR_ErrorStack, PEAR_Warning is designed to be used on a transactional basis. + * + * + * doSomethingComplex(); + * if (PEAR_Warning::hasWarnings()) { + * $warnings = PEAR_Warning::end(); + * throw new Mypackage_Exception('unclean doSomethingComplex', $warnings); + * } else { + * $c->doSomethingElse(); + * } + * ?> + * + * + * Only warnings that occur between ::begin() and ::end() will be processed. Remember, + * a warning is a non-fatal error, exceptions will be used for unrecoverable errors in + * all PEAR packages, and you should follow this model to be safe! + * + * For developers: + * + * This class can be used globally or locally. For global use, a + * series of static methods have been provided. The class is designed + * for lazy loading, and so the following code will work, and increase + * efficiency on production servers: + * + * + * + * + * + * This means that PEAR_Warning can literally be used without the need for + * require_once 'PEAR/Warning.php';! + * + * You can also pass in an exception class as a warning + * + * + * + * + * + * An interface is provided to allow for severity differentiation + * + * + * _level = $level; + * parent::__construct($message, $p1, $p2); + * } + * + * public function getLevel() + * { + * return $this->_level; + * } + * } + * PEAR_Warning::add(new MyPackage_Warning('some info', 'notice')); + * ?> + * + * + * This can be used with {@link setErrorHandling()} to ignore warnings of different severities + * for complex error situations. + * + * For local situations like an internal warning system for a parser that may become the cause + * of a single PEAR_Exception, PEAR_Warning can also be instantiated and used without any connection + * to the global warning stack. + * @package PEAR + */ +class PEAR_Warning +{ + /** + * properties used for global warning stacks + */ + protected static $_hasWarnings = false; + + protected static $warnings = array(); + protected static $go = false; + protected static $levels = array('warning', 'notice'); + + private static $_observers = array(); + private static $_uniqueid = 0; + /** + * properties used for instantiation of private warning stack + */ + private $_warnings = array(); + private $_go = false; + private $_context; + + /** + * Begin tracking all global warnings + */ + static public function begin() + { + if (class_exists('PEAR_ErrorStack')) { + PEAR_ErrorStack::setPEARWarningCallback(array('PEAR_Warning', '_catch')); + } + self::$go = true; + self::$_hasWarnings = false; + } + + /** + * @return bool + */ + static public function hasWarnings() + { + return self::$_hasWarnings; + } + + /** + * Stop tracking global warnings + * @return array an array of all warnings in array and PEAR_Exception format + * suitable for use as a PEAR_Exception cause + */ + static public function end() + { + if (class_exists('PEAR_ErrorStack')) { + PEAR_ErrorStack::setPEARWarningCallback(false); + } + self::$go = false; + self::$_hasWarnings = false; + $a = self::$warnings; + self::$warnings = array(); + return $a; + } + + /** + * @param mixed A valid callback that accepts either a + * PEAR_Exception or PEAR_ErrorStack-style array + * @param string The name of the observer. Use this if you want + * to remove it later with removeObserver(). + * {@link getUniqueId()} can be used to generate a label + */ + public static function addObserver($callback, $label = 'default') + { + self::$_observers[$label] = $callback; + } + + /** + * @param mixed observer ID + */ + public static function removeObserver($label = 'default') + { + unset(self::$_observers[$label]); + } + + /** + * @return int unique identifier for an observer + */ + public static function getUniqueId() + { + return self::$_uniqueid++; + } + + /** + * Set the warning levels that should be captured by the warning mechanism + * + * WARNING: no error checking or spell checking. + * @param array + */ + public static function setErrorHandling($levels) + { + self::$_levels = $levels; + } + + /** + * Add a warning to the global warning stack. + * + * Note: if you want file/line context, use an exception object + * @param PEAR_Exception|string|int Either pass in an exception to use as the warning, or an + * error code or some other error class differentiation technique + * @param string Package is required if $codeOrException is not a PEAR_Exception object + * @param string Error message, use %param% to do automatic parameter replacement from $params + * @param array Error parameters + * @param string Error level, use the English name + + * @throws PEAR_WarningException if $codeOrException is not a PEAR_Exception and $package is not set + */ + static public function add($codeOrException, $package = '', $msg = '', $params = array(), + $level = 'warning') + { + if ($codeOrException instanceof PEAR_Exception) { + if ($codeOrException instanceof PEAR_WarningInterface) { + if (in_array($codeOrException->getLevel(), self::$levels)) { + self::_signal($codeOrException); + } + } else { + self::_signal($codeOrException); + } + } else { + if (empty($package)) { + throw new PEAR_WarningException('Package must be set for a non-exception warning'); + } + if (in_array($level, self::$levels)) { + $warning = self::_formatWarning($codeOrException, $package, $level, $msg, $params); + self::_signal($warning); + } + } + if (self::$go) { + self::$_hasWarnings = true; + self::$warnings[] = $warning; + } + } + + /** + * @param string the package name, or other context information that can be used + * to differentiate this warning from warnings thrown by other packages + * @throws PEAR_WarningException if $context is not a string + */ + public function __construct($context) + { + if (!is_string($context)) { + throw new PEAR_WarningException('$context constructor argument must be a string'); + } + $this->_context = $context; + } + + /** + * Local stack function for adding a warning - note that package is not needed, as it is + * defined in the constructor. + * + * Note: if you want file/line context, use an exception object + * @param PEAR_Exception|string|int Either pass in an exception to use as the warning, or an + * error code or some other error class differentiation technique + * @param string Error message, use %param% to do automatic parameter replacement from $params + * @param array Error parameters + * @param string Error level, use the English name + */ + public function localAdd($code, $msg = '', $params = array(), $level = 'warning') + { + if ($codeOrException instanceof PEAR_Exception) { + $this->_warnings[] = $codeOrException; + } else { + $warning = self::_formatWarning($codeOrException, $this->_context, $level, $msg); + $this->_warnings[] = $warning; + } + } + + /** + * Begin a local warning stack session + */ + public function localBegin() + { + $this->_warnings = array(); + $this->_go = true; + } + + /** + * End a local warning stack session + * @return array + */ + public function localEnd() + { + $a = $this->_warnings; + $this->_warnings = array(); + $this->_go = false; + return $a; + } + + /** + * Do not use this function directly - it should only be used by PEAR_ErrorStack + * @access private + */ + static public function _catch($err) + { + self::_signal($err); + } + + private static function _signal($warning) + { + foreach (self::$_observers as $func) { + if (is_callable($func)) { + call_user_func($func, $this); + continue; + } + settype($func, 'array'); + switch ($func[0]) { + case PEAR_EXCEPTION_PRINT : + $f = (isset($func[1])) ? $func[1] : '%s'; + printf($f, $this->getMessage()); + break; + case PEAR_EXCEPTION_TRIGGER : + $f = (isset($func[1])) ? $func[1] : E_USER_NOTICE; + trigger_error($this->getMessage(), $f); + break; + case PEAR_EXCEPTION_DIE : + $f = (isset($func[1])) ? $func[1] : '%s'; + die(printf($f, $this->getMessage())); + break; + default: + trigger_error('invalid observer type', E_USER_WARNING); + } + } + } + + static private function _formatWarning($code, $package, $level, $msg, $params, $backtrace) + { + return array('package' => $package, + 'code' => $code, + 'level' => $level, + 'message' => self::_formatMessage($msg, $params), + 'params' => $params); + } + + static private function _formatMessage($msg, $params) + { + if (count($params)) { + foreach ($params as $name => $val) { + if (strpos($msg, '%' . $name . '%') !== false) { + if (is_array($val)) { + // don't pass in an array that you expect to display unless it is 1-dimensional! + $val = implode(', ', $val); + } + if (is_object($val)) { + if (method_exists($val, '__toString')) { + $val = $val->__toString(); + } else { + $val = 'Object'; + } + } + $msg = str_replace('%' . $name . '%', $val, $msg); + } + } + } + return $msg; + } +} +?> \ No newline at end of file diff --git a/includes/pear/PEAR/XMLParser.php b/includes/pear/PEAR/XMLParser.php new file mode 100644 index 0000000..a70988b --- /dev/null +++ b/includes/pear/PEAR/XMLParser.php @@ -0,0 +1,253 @@ + + * @author Stephan Schmidt (original XML_Unserializer code) + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a1 + */ + +/** + * Parser for any xml file + * @category pear + * @package PEAR + * @author Greg Beaver + * @author Stephan Schmidt (original XML_Unserializer code) + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a1 + */ +class PEAR_XMLParser +{ + /** + * unserilialized data + * @var string $_serializedData + */ + var $_unserializedData = null; + + /** + * name of the root tag + * @var string $_root + */ + var $_root = null; + + /** + * stack for all data that is found + * @var array $_dataStack + */ + var $_dataStack = array(); + + /** + * stack for all values that are generated + * @var array $_valStack + */ + var $_valStack = array(); + + /** + * current tag depth + * @var int $_depth + */ + var $_depth = 0; + + /** + * The XML encoding to use + * @var string $encoding + */ + var $encoding = 'ISO-8859-1'; + + /** + * @return array + */ + function getData() + { + return $this->_unserializedData; + } + + /** + * @param string xml content + * @return true|PEAR_Error + */ + function parse($data) + { + if (!extension_loaded('xml')) { + include_once 'PEAR.php'; + return PEAR::raiseError("XML Extension not found", 1); + } + $this->_dataStack = $this->_valStack = array(); + $this->_depth = 0; + + if ( + strpos($data, 'encoding="UTF-8"') + || strpos($data, 'encoding="utf-8"') + || strpos($data, "encoding='UTF-8'") + || strpos($data, "encoding='utf-8'") + ) { + $this->encoding = 'UTF-8'; + } + + if (version_compare(phpversion(), '5.0.0', 'lt') && $this->encoding == 'UTF-8') { + $data = utf8_decode($data); + $this->encoding = 'ISO-8859-1'; + } + + $xp = xml_parser_create($this->encoding); + xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, 0); + xml_set_object($xp, $this); + xml_set_element_handler($xp, 'startHandler', 'endHandler'); + xml_set_character_data_handler($xp, 'cdataHandler'); + if (!xml_parse($xp, $data)) { + $msg = xml_error_string(xml_get_error_code($xp)); + $line = xml_get_current_line_number($xp); + xml_parser_free($xp); + include_once 'PEAR.php'; + return PEAR::raiseError("XML Error: '$msg' on line '$line'", 2); + } + xml_parser_free($xp); + return true; + } + + /** + * Start element handler for XML parser + * + * @access private + * @param object $parser XML parser object + * @param string $element XML element + * @param array $attribs attributes of XML tag + * @return void + */ + function startHandler($parser, $element, $attribs) + { + $this->_depth++; + $this->_dataStack[$this->_depth] = null; + + $val = array( + 'name' => $element, + 'value' => null, + 'type' => 'string', + 'childrenKeys' => array(), + 'aggregKeys' => array() + ); + + if (count($attribs) > 0) { + $val['children'] = array(); + $val['type'] = 'array'; + $val['children']['attribs'] = $attribs; + } + + array_push($this->_valStack, $val); + } + + /** + * post-process data + * + * @param string $data + * @param string $element element name + */ + function postProcess($data, $element) + { + return trim($data); + } + + /** + * End element handler for XML parser + * + * @access private + * @param object XML parser object + * @param string + * @return void + */ + function endHandler($parser, $element) + { + $value = array_pop($this->_valStack); + $data = $this->postProcess($this->_dataStack[$this->_depth], $element); + + // adjust type of the value + switch (strtolower($value['type'])) { + // unserialize an array + case 'array': + if ($data !== '') { + $value['children']['_content'] = $data; + } + + $value['value'] = isset($value['children']) ? $value['children'] : array(); + break; + + /* + * unserialize a null value + */ + case 'null': + $data = null; + break; + + /* + * unserialize any scalar value + */ + default: + settype($data, $value['type']); + $value['value'] = $data; + break; + } + + $parent = array_pop($this->_valStack); + if ($parent === null) { + $this->_unserializedData = &$value['value']; + $this->_root = &$value['name']; + return true; + } + + // parent has to be an array + if (!isset($parent['children']) || !is_array($parent['children'])) { + $parent['children'] = array(); + if ($parent['type'] != 'array') { + $parent['type'] = 'array'; + } + } + + if (!empty($value['name'])) { + // there already has been a tag with this name + if (in_array($value['name'], $parent['childrenKeys'])) { + // no aggregate has been created for this tag + if (!in_array($value['name'], $parent['aggregKeys'])) { + if (isset($parent['children'][$value['name']])) { + $parent['children'][$value['name']] = array($parent['children'][$value['name']]); + } else { + $parent['children'][$value['name']] = array(); + } + array_push($parent['aggregKeys'], $value['name']); + } + array_push($parent['children'][$value['name']], $value['value']); + } else { + $parent['children'][$value['name']] = &$value['value']; + array_push($parent['childrenKeys'], $value['name']); + } + } else { + array_push($parent['children'],$value['value']); + } + array_push($this->_valStack, $parent); + + $this->_depth--; + } + + /** + * Handler for character data + * + * @access private + * @param object XML parser object + * @param string CDATA + * @return void + */ + function cdataHandler($parser, $cdata) + { + $this->_dataStack[$this->_depth] .= $cdata; + } +} \ No newline at end of file diff --git a/includes/pear/PEAR5.php b/includes/pear/PEAR5.php new file mode 100644 index 0000000..4286067 --- /dev/null +++ b/includes/pear/PEAR5.php @@ -0,0 +1,33 @@ + | +// +-----------------------------------------------------------------------------+ +// +/** + * The Graph.php file contains the definition of the Structures_Graph class + * + * @see Structures_Graph + * @package Structures_Graph + */ + +/* dependencies {{{ */ +/** PEAR base classes */ +require_once 'PEAR.php'; +/** Graph Node */ +require_once 'Structures/Graph/Node.php'; +/* }}} */ + +define('STRUCTURES_GRAPH_ERROR_GENERIC', 100); + +/* class Structures_Graph {{{ */ +/** + * The Structures_Graph class represents a graph data structure. + * + * A Graph is a data structure composed by a set of nodes, connected by arcs. + * Graphs may either be directed or undirected. In a directed graph, arcs are + * directional, and can be traveled only one way. In an undirected graph, arcs + * are bidirectional, and can be traveled both ways. + * + * @author Sérgio Carvalho + * @copyright (c) 2004 by Sérgio Carvalho + * @package Structures_Graph + */ +/* }}} */ +class Structures_Graph { + /* fields {{{ */ + /** + * @access private + */ + var $_nodes = array(); + /** + * @access private + */ + var $_directed = false; + /* }}} */ + + /* Constructor {{{ */ + /** + * + * Constructor + * + * @param boolean Set to true if the graph is directed. Set to false if it is not directed. (Optional, defaults to true) + * @access public + */ + function Structures_Graph($directed = true) { + $this->_directed = $directed; + } + /* }}} */ + + /* isDirected {{{ */ + /** + * + * Return true if a graph is directed + * + * @return boolean true if the graph is directed + * @access public + */ + function isDirected() { + return (boolean) $this->_directed; + } + /* }}} */ + + /* addNode {{{ */ + /** + * + * Add a Node to the Graph + * + * @param Structures_Graph_Node The node to be added. + * @access public + */ + function addNode(&$newNode) { + // We only add nodes + if (!is_a($newNode, 'Structures_Graph_Node')) return Pear::raiseError('Structures_Graph::addNode received an object that is not a Structures_Graph_Node', STRUCTURES_GRAPH_ERROR_GENERIC); + // Graphs are node *sets*, so duplicates are forbidden. We allow nodes that are exactly equal, but disallow equal references. + foreach($this->_nodes as $key => $node) { + /* + ZE1 equality operators choke on the recursive cycle introduced by the _graph field in the Node object. + So, we'll check references the hard way (change $this->_nodes[$key] and check if the change reflects in + $node) + */ + $savedData = $this->_nodes[$key]; + $referenceIsEqualFlag = false; + $this->_nodes[$key] = true; + if ($node === true) { + $this->_nodes[$key] = false; + if ($node === false) $referenceIsEqualFlag = true; + } + $this->_nodes[$key] = $savedData; + if ($referenceIsEqualFlag) return Pear::raiseError('Structures_Graph::addNode received an object that is a duplicate for this dataset', STRUCTURES_GRAPH_ERROR_GENERIC); + } + $this->_nodes[] =& $newNode; + $newNode->setGraph($this); + } + /* }}} */ + + /* removeNode (unimplemented) {{{ */ + /** + * + * Remove a Node from the Graph + * + * @todo This is unimplemented + * @param Structures_Graph_Node The node to be removed from the graph + * @access public + */ + function removeNode(&$node) { + } + /* }}} */ + + /* getNodes {{{ */ + /** + * + * Return the node set, in no particular order. For ordered node sets, use a Graph Manipulator insted. + * + * @access public + * @see Structures_Graph_Manipulator_TopologicalSorter + * @return array The set of nodes in this graph + */ + function &getNodes() { + return $this->_nodes; + } + /* }}} */ +} +?> diff --git a/includes/pear/Structures/Graph/Manipulator/AcyclicTest.php b/includes/pear/Structures/Graph/Manipulator/AcyclicTest.php new file mode 100644 index 0000000..fc1ba92 --- /dev/null +++ b/includes/pear/Structures/Graph/Manipulator/AcyclicTest.php @@ -0,0 +1,136 @@ + | +// +-----------------------------------------------------------------------------+ +// +/** + * This file contains the definition of the Structures_Graph_Manipulator_AcyclicTest graph manipulator. + * + * @see Structures_Graph_Manipulator_AcyclicTest + * @package Structures_Graph + */ + +/* dependencies {{{ */ +/** */ +require_once 'PEAR.php'; +/** */ +require_once 'Structures/Graph.php'; +/** */ +require_once 'Structures/Graph/Node.php'; +/* }}} */ + +/* class Structures_Graph_Manipulator_AcyclicTest {{{ */ +/** + * The Structures_Graph_Manipulator_AcyclicTest is a graph manipulator + * which tests whether a graph contains a cycle. + * + * The definition of an acyclic graph used in this manipulator is that of a + * DAG. The graph must be directed, or else it is considered cyclic, even when + * there are no arcs. + * + * @author Sérgio Carvalho + * @copyright (c) 2004 by Sérgio Carvalho + * @package Structures_Graph + */ +class Structures_Graph_Manipulator_AcyclicTest { + /* _nonVisitedInDegree {{{ */ + /** + * + * This is a variant of Structures_Graph::inDegree which does + * not count nodes marked as visited. + * + * @access private + * @return integer Number of non-visited nodes that link to this one + */ + function _nonVisitedInDegree(&$node) { + $result = 0; + $graphNodes =& $node->_graph->getNodes(); + foreach (array_keys($graphNodes) as $key) { + if ((!$graphNodes[$key]->getMetadata('acyclic-test-visited')) && $graphNodes[$key]->connectsTo($node)) $result++; + } + return $result; + + } + /* }}} */ + + /* _isAcyclic {{{ */ + /** + * @access private + */ + function _isAcyclic(&$graph) { + // Mark every node as not visited + $nodes =& $graph->getNodes(); + $nodeKeys = array_keys($nodes); + $refGenerator = array(); + foreach($nodeKeys as $key) { + $refGenerator[] = false; + $nodes[$key]->setMetadata('acyclic-test-visited', $refGenerator[sizeof($refGenerator) - 1]); + } + + // Iteratively peel off leaf nodes + do { + // Find out which nodes are leafs (excluding visited nodes) + $leafNodes = array(); + foreach($nodeKeys as $key) { + if ((!$nodes[$key]->getMetadata('acyclic-test-visited')) && Structures_Graph_Manipulator_AcyclicTest::_nonVisitedInDegree($nodes[$key]) == 0) { + $leafNodes[] =& $nodes[$key]; + } + } + // Mark leafs as visited + for ($i=sizeof($leafNodes) - 1; $i>=0; $i--) { + $visited =& $leafNodes[$i]->getMetadata('acyclic-test-visited'); + $visited = true; + $leafNodes[$i]->setMetadata('acyclic-test-visited', $visited); + } + } while (sizeof($leafNodes) > 0); + + // If graph is a DAG, there should be no non-visited nodes. Let's try to prove otherwise + $result = true; + foreach($nodeKeys as $key) if (!$nodes[$key]->getMetadata('acyclic-test-visited')) $result = false; + + // Cleanup visited marks + foreach($nodeKeys as $key) $nodes[$key]->unsetMetadata('acyclic-test-visited'); + + return $result; + } + /* }}} */ + + /* isAcyclic {{{ */ + /** + * + * isAcyclic returns true if a graph contains no cycles, false otherwise. + * + * @return boolean true iff graph is acyclic + * @access public + */ + function isAcyclic(&$graph) { + // We only test graphs + if (!is_a($graph, 'Structures_Graph')) return Pear::raiseError('Structures_Graph_Manipulator_AcyclicTest::isAcyclic received an object that is not a Structures_Graph', STRUCTURES_GRAPH_ERROR_GENERIC); + if (!$graph->isDirected()) return false; // Only directed graphs may be acyclic + + return Structures_Graph_Manipulator_AcyclicTest::_isAcyclic($graph); + } + /* }}} */ +} +/* }}} */ +?> diff --git a/includes/pear/Structures/Graph/Manipulator/TopologicalSorter.php b/includes/pear/Structures/Graph/Manipulator/TopologicalSorter.php new file mode 100644 index 0000000..98a9fa0 --- /dev/null +++ b/includes/pear/Structures/Graph/Manipulator/TopologicalSorter.php @@ -0,0 +1,153 @@ + | +// +-----------------------------------------------------------------------------+ +// +/** + * This file contains the definition of the Structures_Graph_Manipulator_TopologicalSorter class. + * + * @see Structures_Graph_Manipulator_TopologicalSorter + * @package Structures_Graph + */ + +/* dependencies {{{ */ +/** */ +require_once 'PEAR.php'; +/** */ +require_once 'Structures/Graph.php'; +/** */ +require_once 'Structures/Graph/Node.php'; +/** */ +require_once 'Structures/Graph/Manipulator/AcyclicTest.php'; +/* }}} */ + +/* class Structures_Graph_Manipulator_TopologicalSorter {{{ */ +/** + * The Structures_Graph_Manipulator_TopologicalSorter is a manipulator + * which is able to return the set of nodes in a graph, sorted by topological + * order. + * + * A graph may only be sorted topologically iff it's a DAG. You can test it + * with the Structures_Graph_Manipulator_AcyclicTest. + * + * @author Sérgio Carvalho + * @copyright (c) 2004 by Sérgio Carvalho + * @see Structures_Graph_Manipulator_AcyclicTest + * @package Structures_Graph + */ +class Structures_Graph_Manipulator_TopologicalSorter { + /* _nonVisitedInDegree {{{ */ + /** + * + * This is a variant of Structures_Graph::inDegree which does + * not count nodes marked as visited. + * + * @access private + * @return integer Number of non-visited nodes that link to this one + */ + function _nonVisitedInDegree(&$node) { + $result = 0; + $graphNodes =& $node->_graph->getNodes(); + foreach (array_keys($graphNodes) as $key) { + if ((!$graphNodes[$key]->getMetadata('topological-sort-visited')) && $graphNodes[$key]->connectsTo($node)) $result++; + } + return $result; + + } + /* }}} */ + + /* _sort {{{ */ + /** + * @access private + */ + function _sort(&$graph) { + // Mark every node as not visited + $nodes =& $graph->getNodes(); + $nodeKeys = array_keys($nodes); + $refGenerator = array(); + foreach($nodeKeys as $key) { + $refGenerator[] = false; + $nodes[$key]->setMetadata('topological-sort-visited', $refGenerator[sizeof($refGenerator) - 1]); + } + + // Iteratively peel off leaf nodes + $topologicalLevel = 0; + do { + // Find out which nodes are leafs (excluding visited nodes) + $leafNodes = array(); + foreach($nodeKeys as $key) { + if ((!$nodes[$key]->getMetadata('topological-sort-visited')) && Structures_Graph_Manipulator_TopologicalSorter::_nonVisitedInDegree($nodes[$key]) == 0) { + $leafNodes[] =& $nodes[$key]; + } + } + // Mark leafs as visited + $refGenerator[] = $topologicalLevel; + for ($i=sizeof($leafNodes) - 1; $i>=0; $i--) { + $visited =& $leafNodes[$i]->getMetadata('topological-sort-visited'); + $visited = true; + $leafNodes[$i]->setMetadata('topological-sort-visited', $visited); + $leafNodes[$i]->setMetadata('topological-sort-level', $refGenerator[sizeof($refGenerator) - 1]); + } + $topologicalLevel++; + } while (sizeof($leafNodes) > 0); + + // Cleanup visited marks + foreach($nodeKeys as $key) $nodes[$key]->unsetMetadata('topological-sort-visited'); + } + /* }}} */ + + /* sort {{{ */ + /** + * + * sort returns the graph's nodes, sorted by topological order. + * + * The result is an array with + * as many entries as topological levels. Each entry in this array is an array of nodes within + * the given topological level. + * + * @return array The graph's nodes, sorted by topological order. + * @access public + */ + function sort(&$graph) { + // We only sort graphs + if (!is_a($graph, 'Structures_Graph')) return Pear::raiseError('Structures_Graph_Manipulator_TopologicalSorter::sort received an object that is not a Structures_Graph', STRUCTURES_GRAPH_ERROR_GENERIC); + if (!Structures_Graph_Manipulator_AcyclicTest::isAcyclic($graph)) return Pear::raiseError('Structures_Graph_Manipulator_TopologicalSorter::sort received an graph that has cycles', STRUCTURES_GRAPH_ERROR_GENERIC); + + Structures_Graph_Manipulator_TopologicalSorter::_sort($graph); + $result = array(); + + // Fill out result array + $nodes =& $graph->getNodes(); + $nodeKeys = array_keys($nodes); + foreach($nodeKeys as $key) { + if (!array_key_exists($nodes[$key]->getMetadata('topological-sort-level'), $result)) $result[$nodes[$key]->getMetadata('topological-sort-level')] = array(); + $result[$nodes[$key]->getMetadata('topological-sort-level')][] =& $nodes[$key]; + $nodes[$key]->unsetMetadata('topological-sort-level'); + } + + return $result; + } + /* }}} */ +} +/* }}} */ +?> diff --git a/includes/pear/Structures/Graph/Node.php b/includes/pear/Structures/Graph/Node.php new file mode 100644 index 0000000..95afa2b --- /dev/null +++ b/includes/pear/Structures/Graph/Node.php @@ -0,0 +1,342 @@ + | +// +-----------------------------------------------------------------------------+ +// +/** + * This file contains the definition of the Structures_Graph_Node class + * + * @see Structures_Graph_Node + * @package Structures_Graph + */ + +/* dependencies {{{ */ +/** */ +require_once 'PEAR.php'; +/** */ +require_once 'Structures/Graph.php'; +/* }}} */ + +/* class Structures_Graph_Node {{{ */ +/** + * The Structures_Graph_Node class represents a Node that can be member of a + * graph node set. + * + * A graph node can contain data. Under this API, the node contains default data, + * and key index data. It behaves, thus, both as a regular data node, and as a + * dictionary (or associative array) node. + * + * Regular data is accessed via getData and setData. Key indexed data is accessed + * via getMetadata and setMetadata. + * + * @author Sérgio Carvalho + * @copyright (c) 2004 by Sérgio Carvalho + * @package Structures_Graph + */ +/* }}} */ +class Structures_Graph_Node { + /* fields {{{ */ + /** + * @access private + */ + var $_data = null; + /** @access private */ + var $_metadata = array(); + /** @access private */ + var $_arcs = array(); + /** @access private */ + var $_graph = null; + /* }}} */ + + /* Constructor {{{ */ + /** + * + * Constructor + * + * @access public + */ + function Structures_Graph_Node() { + } + /* }}} */ + + /* getGraph {{{ */ + /** + * + * Node graph getter + * + * @return Structures_Graph Graph where node is stored + * @access public + */ + function &getGraph() { + return $this->_graph; + } + /* }}} */ + + /* setGraph {{{ */ + /** + * + * Node graph setter. This method should not be called directly. Use Graph::addNode instead. + * + * @param Structures_Graph Set the graph for this node. + * @see Structures_Graph::addNode() + * @access public + */ + function setGraph(&$graph) { + $this->_graph =& $graph; + } + /* }}} */ + + /* getData {{{ */ + /** + * + * Node data getter. + * + * Each graph node can contain a reference to one variable. This is the getter for that reference. + * + * @return mixed Data stored in node + * @access public + */ + function &getData() { + return $this->_data; + } + /* }}} */ + + /* setData {{{ */ + /** + * + * Node data setter + * + * Each graph node can contain a reference to one variable. This is the setter for that reference. + * + * @return mixed Data to store in node + * @access public + */ + function setData($data) { + $this->_data =& $data; + } + /* }}} */ + + /* metadataKeyExists {{{ */ + /** + * + * Test for existence of metadata under a given key. + * + * Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an + * associative array or in a dictionary. This method tests whether a given metadata key exists for this node. + * + * @param string Key to test + * @return boolean + * @access public + */ + function metadataKeyExists($key) { + return array_key_exists($key, $this->_metadata); + } + /* }}} */ + + /* getMetadata {{{ */ + /** + * + * Node metadata getter + * + * Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an + * associative array or in a dictionary. This method gets the data under the given key. If the key does + * not exist, an error will be thrown, so testing using metadataKeyExists might be needed. + * + * @param string Key + * @param boolean nullIfNonexistent (defaults to false). + * @return mixed Metadata Data stored in node under given key + * @see metadataKeyExists + * @access public + */ + function &getMetadata($key, $nullIfNonexistent = false) { + if (array_key_exists($key, $this->_metadata)) { + return $this->_metadata[$key]; + } else { + if ($nullIfNonexistent) { + $a = null; + return $a; + } else { + $a = Pear::raiseError('Structures_Graph_Node::getMetadata: Requested key does not exist', STRUCTURES_GRAPH_ERROR_GENERIC); + return $a; + } + } + } + /* }}} */ + + /* unsetMetadata {{{ */ + /** + * + * Delete metadata by key + * + * Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an + * associative array or in a dictionary. This method removes any data that might be stored under the provided key. + * If the key does not exist, no error is thrown, so it is safe using this method without testing for key existence. + * + * @param string Key + * @access public + */ + function unsetMetadata($key) { + if (array_key_exists($key, $this->_metadata)) unset($this->_metadata[$key]); + } + /* }}} */ + + /* setMetadata {{{ */ + /** + * + * Node metadata setter + * + * Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an + * associative array or in a dictionary. This method stores data under the given key. If the key already exists, + * previously stored data is discarded. + * + * @param string Key + * @param mixed Data + * @access public + */ + function setMetadata($key, $data) { + $this->_metadata[$key] =& $data; + } + /* }}} */ + + /* _connectTo {{{ */ + /** @access private */ + function _connectTo(&$destinationNode) { + $this->_arcs[] =& $destinationNode; + } + /* }}} */ + + /* connectTo {{{ */ + /** + * + * Connect this node to another one. + * + * If the graph is not directed, the reverse arc, connecting $destinationNode to $this is also created. + * + * @param Structures_Graph_Node Node to connect to + * @access public + */ + function connectTo(&$destinationNode) { + // We only connect to nodes + if (!is_a($destinationNode, 'Structures_Graph_Node')) return Pear::raiseError('Structures_Graph_Node::connectTo received an object that is not a Structures_Graph_Node', STRUCTURES_GRAPH_ERROR_GENERIC); + // Nodes must already be in graphs to be connected + if ($this->_graph == null) return Pear::raiseError('Structures_Graph_Node::connectTo Tried to connect a node that is not in a graph', STRUCTURES_GRAPH_ERROR_GENERIC); + if ($destinationNode->getGraph() == null) return Pear::raiseError('Structures_Graph_Node::connectTo Tried to connect to a node that is not in a graph', STRUCTURES_GRAPH_ERROR_GENERIC); + // Connect here + $this->_connectTo($destinationNode); + // If graph is undirected, connect back + if (!$this->_graph->isDirected()) { + $destinationNode->_connectTo($this); + } + } + /* }}} */ + + /* getNeighbours {{{ */ + /** + * + * Return nodes connected to this one. + * + * @return array Array of nodes + * @access public + */ + function getNeighbours() { + return $this->_arcs; + } + /* }}} */ + + /* connectsTo {{{ */ + /** + * + * Test wether this node has an arc to the target node + * + * @return boolean True if the two nodes are connected + * @access public + */ + function connectsTo(&$target) { + if (version_compare(PHP_VERSION, '5.0.0') >= 0) { + return in_array($target, $this->getNeighbours(), true); + } + + $copy = $target; + $arcKeys = array_keys($this->_arcs); + foreach($arcKeys as $key) { + /* ZE1 chokes on this expression: + if ($target === $arc) return true; + so, we'll use more convoluted stuff + */ + $arc =& $this->_arcs[$key]; + $target = true; + if ($arc === true) { + $target = false; + if ($arc === false) { + $target = $copy; + return true; + } + } + } + $target = $copy; + return false; + } + /* }}} */ + + /* inDegree {{{ */ + /** + * + * Calculate the in degree of the node. + * + * The indegree for a node is the number of arcs entering the node. For non directed graphs, + * the indegree is equal to the outdegree. + * + * @return integer In degree of the node + * @access public + */ + function inDegree() { + if ($this->_graph == null) return 0; + if (!$this->_graph->isDirected()) return $this->outDegree(); + $result = 0; + $graphNodes =& $this->_graph->getNodes(); + foreach (array_keys($graphNodes) as $key) { + if ($graphNodes[$key]->connectsTo($this)) $result++; + } + return $result; + + } + /* }}} */ + + /* outDegree {{{ */ + /** + * + * Calculate the out degree of the node. + * + * The outdegree for a node is the number of arcs exiting the node. For non directed graphs, + * the outdegree is always equal to the indegree. + * + * @return integer Out degree of the node + * @access public + */ + function outDegree() { + if ($this->_graph == null) return 0; + return sizeof($this->_arcs); + } + /* }}} */ +} +?> diff --git a/includes/pear/System.php b/includes/pear/System.php new file mode 100644 index 0000000..b419605 --- /dev/null +++ b/includes/pear/System.php @@ -0,0 +1,624 @@ + + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 0.1 + */ + +/** + * base class + */ +require_once 'PEAR.php'; +require_once 'Console/Getopt.php'; + +$GLOBALS['_System_temp_files'] = array(); + +/** +* System offers cross plattform compatible system functions +* +* Static functions for different operations. Should work under +* Unix and Windows. The names and usage has been taken from its respectively +* GNU commands. The functions will return (bool) false on error and will +* trigger the error with the PHP trigger_error() function (you can silence +* the error by prefixing a '@' sign after the function call, but this +* is not recommended practice. Instead use an error handler with +* {@link set_error_handler()}). +* +* Documentation on this class you can find in: +* http://pear.php.net/manual/ +* +* Example usage: +* if (!@System::rm('-r file1 dir1')) { +* print "could not delete file1 or dir1"; +* } +* +* In case you need to to pass file names with spaces, +* pass the params as an array: +* +* System::rm(array('-r', $file1, $dir1)); +* +* @category pear +* @package System +* @author Tomas V.V. Cox +* @copyright 1997-2006 The PHP Group +* @license http://opensource.org/licenses/bsd-license.php New BSD License +* @version Release: @package_version@ +* @link http://pear.php.net/package/PEAR +* @since Class available since Release 0.1 +* @static +*/ +class System +{ + /** + * returns the commandline arguments of a function + * + * @param string $argv the commandline + * @param string $short_options the allowed option short-tags + * @param string $long_options the allowed option long-tags + * @return array the given options and there values + */ + public static function _parseArgs($argv, $short_options, $long_options = null) + { + if (!is_array($argv) && $argv !== null) { + /* + // Quote all items that are a short option + $av = preg_split('/(\A| )--?[a-z0-9]+[ =]?((? $a) { + if (empty($a)) { + continue; + } + $argv[$k] = trim($a) ; + } + } + return Console_Getopt::getopt2($argv, $short_options, $long_options); + } + + /** + * Output errors with PHP trigger_error(). You can silence the errors + * with prefixing a "@" sign to the function call: @System::mkdir(..); + * + * @param mixed $error a PEAR error or a string with the error message + * @return bool false + */ + public static function raiseError($error) + { + if (PEAR::isError($error)) { + $error = $error->getMessage(); + } + trigger_error($error, E_USER_WARNING); + return false; + } + + /** + * Creates a nested array representing the structure of a directory + * + * System::_dirToStruct('dir1', 0) => + * Array + * ( + * [dirs] => Array + * ( + * [0] => dir1 + * ) + * + * [files] => Array + * ( + * [0] => dir1/file2 + * [1] => dir1/file3 + * ) + * ) + * @param string $sPath Name of the directory + * @param integer $maxinst max. deep of the lookup + * @param integer $aktinst starting deep of the lookup + * @param bool $silent if true, do not emit errors. + * @return array the structure of the dir + */ + public static function _dirToStruct($sPath, $maxinst, $aktinst = 0, $silent = false) + { + $struct = array('dirs' => array(), 'files' => array()); + if (($dir = @opendir($sPath)) === false) { + if (!$silent) { + System::raiseError("Could not open dir $sPath"); + } + return $struct; // XXX could not open error + } + + $struct['dirs'][] = $sPath = realpath($sPath); // XXX don't add if '.' or '..' ? + $list = array(); + while (false !== ($file = readdir($dir))) { + if ($file != '.' && $file != '..') { + $list[] = $file; + } + } + + closedir($dir); + natsort($list); + if ($aktinst < $maxinst || $maxinst == 0) { + foreach ($list as $val) { + $path = $sPath . DIRECTORY_SEPARATOR . $val; + if (is_dir($path) && !is_link($path)) { + $tmp = System::_dirToStruct($path, $maxinst, $aktinst+1, $silent); + $struct = array_merge_recursive($struct, $tmp); + } else { + $struct['files'][] = $path; + } + } + } + + return $struct; + } + + /** + * Creates a nested array representing the structure of a directory and files + * + * @param array $files Array listing files and dirs + * @return array + * @see System::_dirToStruct() + */ + public static function _multipleToStruct($files) + { + $struct = array('dirs' => array(), 'files' => array()); + settype($files, 'array'); + foreach ($files as $file) { + if (is_dir($file) && !is_link($file)) { + $tmp = System::_dirToStruct($file, 0); + $struct = array_merge_recursive($tmp, $struct); + } else { + if (!in_array($file, $struct['files'])) { + $struct['files'][] = $file; + } + } + } + return $struct; + } + + /** + * The rm command for removing files. + * Supports multiple files and dirs and also recursive deletes + * + * @param string $args the arguments for rm + * @return mixed PEAR_Error or true for success + */ + public static function rm($args) + { + $opts = System::_parseArgs($args, 'rf'); // "f" does nothing but I like it :-) + if (PEAR::isError($opts)) { + return System::raiseError($opts); + } + foreach ($opts[0] as $opt) { + if ($opt[0] == 'r') { + $do_recursive = true; + } + } + $ret = true; + if (isset($do_recursive)) { + $struct = System::_multipleToStruct($opts[1]); + foreach ($struct['files'] as $file) { + if (!@unlink($file)) { + $ret = false; + } + } + + rsort($struct['dirs']); + foreach ($struct['dirs'] as $dir) { + if (!@rmdir($dir)) { + $ret = false; + } + } + } else { + foreach ($opts[1] as $file) { + $delete = (is_dir($file)) ? 'rmdir' : 'unlink'; + if (!@$delete($file)) { + $ret = false; + } + } + } + return $ret; + } + + /** + * Make directories. + * + * The -p option will create parent directories + * @param string $args the name of the director(y|ies) to create + * @return bool True for success + */ + public static function mkDir($args) + { + $opts = System::_parseArgs($args, 'pm:'); + if (PEAR::isError($opts)) { + return System::raiseError($opts); + } + + $mode = 0777; // default mode + foreach ($opts[0] as $opt) { + if ($opt[0] == 'p') { + $create_parents = true; + } elseif ($opt[0] == 'm') { + // if the mode is clearly an octal number (starts with 0) + // convert it to decimal + if (strlen($opt[1]) && $opt[1]{0} == '0') { + $opt[1] = octdec($opt[1]); + } else { + // convert to int + $opt[1] += 0; + } + $mode = $opt[1]; + } + } + + $ret = true; + if (isset($create_parents)) { + foreach ($opts[1] as $dir) { + $dirstack = array(); + while ((!file_exists($dir) || !is_dir($dir)) && + $dir != DIRECTORY_SEPARATOR) { + array_unshift($dirstack, $dir); + $dir = dirname($dir); + } + + while ($newdir = array_shift($dirstack)) { + if (!is_writeable(dirname($newdir))) { + $ret = false; + break; + } + + if (!mkdir($newdir, $mode)) { + $ret = false; + } + } + } + } else { + foreach($opts[1] as $dir) { + if ((@file_exists($dir) || !is_dir($dir)) && !mkdir($dir, $mode)) { + $ret = false; + } + } + } + + return $ret; + } + + /** + * Concatenate files + * + * Usage: + * 1) $var = System::cat('sample.txt test.txt'); + * 2) System::cat('sample.txt test.txt > final.txt'); + * 3) System::cat('sample.txt test.txt >> final.txt'); + * + * Note: as the class use fopen, urls should work also (test that) + * + * @param string $args the arguments + * @return boolean true on success + */ + public static function &cat($args) + { + $ret = null; + $files = array(); + if (!is_array($args)) { + $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY); + } + + $count_args = count($args); + for ($i = 0; $i < $count_args; $i++) { + if ($args[$i] == '>') { + $mode = 'wb'; + $outputfile = $args[$i+1]; + break; + } elseif ($args[$i] == '>>') { + $mode = 'ab+'; + $outputfile = $args[$i+1]; + break; + } else { + $files[] = $args[$i]; + } + } + $outputfd = false; + if (isset($mode)) { + if (!$outputfd = fopen($outputfile, $mode)) { + $err = System::raiseError("Could not open $outputfile"); + return $err; + } + $ret = true; + } + foreach ($files as $file) { + if (!$fd = fopen($file, 'r')) { + System::raiseError("Could not open $file"); + continue; + } + while ($cont = fread($fd, 2048)) { + if (is_resource($outputfd)) { + fwrite($outputfd, $cont); + } else { + $ret .= $cont; + } + } + fclose($fd); + } + if (is_resource($outputfd)) { + fclose($outputfd); + } + return $ret; + } + + /** + * Creates temporary files or directories. This function will remove + * the created files when the scripts finish its execution. + * + * Usage: + * 1) $tempfile = System::mktemp("prefix"); + * 2) $tempdir = System::mktemp("-d prefix"); + * 3) $tempfile = System::mktemp(); + * 4) $tempfile = System::mktemp("-t /var/tmp prefix"); + * + * prefix -> The string that will be prepended to the temp name + * (defaults to "tmp"). + * -d -> A temporary dir will be created instead of a file. + * -t -> The target dir where the temporary (file|dir) will be created. If + * this param is missing by default the env vars TMP on Windows or + * TMPDIR in Unix will be used. If these vars are also missing + * c:\windows\temp or /tmp will be used. + * + * @param string $args The arguments + * @return mixed the full path of the created (file|dir) or false + * @see System::tmpdir() + */ + public static function mktemp($args = null) + { + static $first_time = true; + $opts = System::_parseArgs($args, 't:d'); + if (PEAR::isError($opts)) { + return System::raiseError($opts); + } + + foreach ($opts[0] as $opt) { + if ($opt[0] == 'd') { + $tmp_is_dir = true; + } elseif ($opt[0] == 't') { + $tmpdir = $opt[1]; + } + } + + $prefix = (isset($opts[1][0])) ? $opts[1][0] : 'tmp'; + if (!isset($tmpdir)) { + $tmpdir = System::tmpdir(); + } + + if (!System::mkDir(array('-p', $tmpdir))) { + return false; + } + + $tmp = tempnam($tmpdir, $prefix); + if (isset($tmp_is_dir)) { + unlink($tmp); // be careful possible race condition here + if (!mkdir($tmp, 0700)) { + return System::raiseError("Unable to create temporary directory $tmpdir"); + } + } + + $GLOBALS['_System_temp_files'][] = $tmp; + if (isset($tmp_is_dir)) { + //$GLOBALS['_System_temp_files'][] = dirname($tmp); + } + + if ($first_time) { + PEAR::registerShutdownFunc(array('System', '_removeTmpFiles')); + $first_time = false; + } + + return $tmp; + } + + /** + * Remove temporary files created my mkTemp. This function is executed + * at script shutdown time + */ + public static function _removeTmpFiles() + { + if (count($GLOBALS['_System_temp_files'])) { + $delete = $GLOBALS['_System_temp_files']; + array_unshift($delete, '-r'); + System::rm($delete); + $GLOBALS['_System_temp_files'] = array(); + } + } + + /** + * Get the path of the temporal directory set in the system + * by looking in its environments variables. + * Note: php.ini-recommended removes the "E" from the variables_order setting, + * making unavaible the $_ENV array, that s why we do tests with _ENV + * + * @return string The temporary directory on the system + */ + public static function tmpdir() + { + if (OS_WINDOWS) { + if ($var = isset($_ENV['TMP']) ? $_ENV['TMP'] : getenv('TMP')) { + return $var; + } + if ($var = isset($_ENV['TEMP']) ? $_ENV['TEMP'] : getenv('TEMP')) { + return $var; + } + if ($var = isset($_ENV['USERPROFILE']) ? $_ENV['USERPROFILE'] : getenv('USERPROFILE')) { + return $var; + } + if ($var = isset($_ENV['windir']) ? $_ENV['windir'] : getenv('windir')) { + return $var; + } + return getenv('SystemRoot') . '\temp'; + } + if ($var = isset($_ENV['TMPDIR']) ? $_ENV['TMPDIR'] : getenv('TMPDIR')) { + return $var; + } + return realpath('/tmp'); + } + + /** + * The "which" command (show the full path of a command) + * + * @param string $program The command to search for + * @param mixed $fallback Value to return if $program is not found + * + * @return mixed A string with the full path or false if not found + * @author Stig Bakken + */ + public static function which($program, $fallback = false) + { + // enforce API + if (!is_string($program) || '' == $program) { + return $fallback; + } + + // full path given + if (basename($program) != $program) { + $path_elements[] = dirname($program); + $program = basename($program); + } else { + // Honor safe mode + if (!ini_get('safe_mode') || !$path = ini_get('safe_mode_exec_dir')) { + $path = getenv('PATH'); + if (!$path) { + $path = getenv('Path'); // some OSes are just stupid enough to do this + } + } + $path_elements = explode(PATH_SEPARATOR, $path); + } + + if (OS_WINDOWS) { + $exe_suffixes = getenv('PATHEXT') + ? explode(PATH_SEPARATOR, getenv('PATHEXT')) + : array('.exe','.bat','.cmd','.com'); + // allow passing a command.exe param + if (strpos($program, '.') !== false) { + array_unshift($exe_suffixes, ''); + } + // is_executable() is not available on windows for PHP4 + $pear_is_executable = (function_exists('is_executable')) ? 'is_executable' : 'is_file'; + } else { + $exe_suffixes = array(''); + $pear_is_executable = 'is_executable'; + } + + foreach ($exe_suffixes as $suff) { + foreach ($path_elements as $dir) { + $file = $dir . DIRECTORY_SEPARATOR . $program . $suff; + if (@$pear_is_executable($file)) { + return $file; + } + } + } + return $fallback; + } + + /** + * The "find" command + * + * Usage: + * + * System::find($dir); + * System::find("$dir -type d"); + * System::find("$dir -type f"); + * System::find("$dir -name *.php"); + * System::find("$dir -name *.php -name *.htm*"); + * System::find("$dir -maxdepth 1"); + * + * Params implmented: + * $dir -> Start the search at this directory + * -type d -> return only directories + * -type f -> return only files + * -maxdepth -> max depth of recursion + * -name -> search pattern (bash style). Multiple -name param allowed + * + * @param mixed Either array or string with the command line + * @return array Array of found files + */ + public static function find($args) + { + if (!is_array($args)) { + $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY); + } + $dir = realpath(array_shift($args)); + if (!$dir) { + return array(); + } + $patterns = array(); + $depth = 0; + $do_files = $do_dirs = true; + $args_count = count($args); + for ($i = 0; $i < $args_count; $i++) { + switch ($args[$i]) { + case '-type': + if (in_array($args[$i+1], array('d', 'f'))) { + if ($args[$i+1] == 'd') { + $do_files = false; + } else { + $do_dirs = false; + } + } + $i++; + break; + case '-name': + $name = preg_quote($args[$i+1], '#'); + // our magic characters ? and * have just been escaped, + // so now we change the escaped versions to PCRE operators + $name = strtr($name, array('\?' => '.', '\*' => '.*')); + $patterns[] = '('.$name.')'; + $i++; + break; + case '-maxdepth': + $depth = $args[$i+1]; + break; + } + } + $path = System::_dirToStruct($dir, $depth, 0, true); + if ($do_files && $do_dirs) { + $files = array_merge($path['files'], $path['dirs']); + } elseif ($do_dirs) { + $files = $path['dirs']; + } else { + $files = $path['files']; + } + if (count($patterns)) { + $dsq = preg_quote(DIRECTORY_SEPARATOR, '#'); + $pattern = '#(^|'.$dsq.')'.implode('|', $patterns).'($|'.$dsq.')#'; + $ret = array(); + $files_count = count($files); + for ($i = 0; $i < $files_count; $i++) { + // only search in the part of the file below the current directory + $filepart = basename($files[$i]); + if (preg_match($pattern, $filepart)) { + $ret[] = $files[$i]; + } + } + return $ret; + } + return $files; + } +} diff --git a/includes/pear/System/Command.php b/includes/pear/System/Command.php new file mode 100644 index 0000000..f32e8a1 --- /dev/null +++ b/includes/pear/System/Command.php @@ -0,0 +1,598 @@ + | +// | Author: Dan Allen +// +----------------------------------------------------------------------+ + +// $Id$ + +// }}} +// {{{ includes + +require_once 'PEAR.php'; +require_once 'System.php'; + +// }}} +// {{{ constants + +define('SYSTEM_COMMAND_OK', 1); +define('SYSTEM_COMMAND_ERROR', -1); +define('SYSTEM_COMMAND_NO_SHELL', -2); +define('SYSTEM_COMMAND_INVALID_SHELL', -3); +define('SYSTEM_COMMAND_TMPDIR_ERROR', -4); +define('SYSTEM_COMMAND_INVALID_OPERATOR', -5); +define('SYSTEM_COMMAND_INVALID_COMMAND', -6); +define('SYSTEM_COMMAND_OPERATOR_PLACEMENT',-7); +define('SYSTEM_COMMAND_COMMAND_PLACEMENT', -8); +define('SYSTEM_COMMAND_NOHUP_MISSING', -9); +define('SYSTEM_COMMAND_NO_OUTPUT', -10); +define('SYSTEM_COMMAND_STDERR', -11); +define('SYSTEM_COMMAND_NONZERO_EXIT', -12); + +// }}} + +// {{{ class System_Command + +/** + * The System_Command:: class implements an abstraction for various ways + * of executing commands (directly using the backtick operator, + * as a background task after the script has terminated using + * register_shutdown_function() or as a detached process using nohup). + * + * @author Anders Johannsen + * @author Dan Allen + * @version $Revision$ + */ + +// }}} +class System_Command { + // {{{ properties + + /** + * Array of settings used when creating the shell command + * + * @var array + * @access private + */ + var $options = array(); + + /** + * Array of available shells to use to execute the command + * + * @var array + * @access private + */ + var $shells = array(); + + /** + * Array of available control operators used between commands + * + * @var array + * @access private + */ + var $controlOperators = array(); + + /** + * The system command to be executed + * + * @var string + * @access private + */ + var $systemCommand = null; + + /** + * Previously added part to the command string + * + * @var string + * @access private + */ + var $previousElement = null; + + /** + * Directory for writing stderr output + * + * @var string + * @access private + */ + var $tmpDir = null; + + /** + * To allow the pear error object to accumulate when building + * the command, we use the command status to keep track when + * a pear error is raised + * + * @var int + * @access private + */ + var $commandStatus = 0; + + /** + * Hold initialization PEAR_Error + * + * @var object + * @access private + **/ + var $_initError = null; + + // }}} + // {{{ constructor + + /** + * Class constructor + * + * Defines all necessary constants and sets defaults + * + * @access public + */ + function System_Command($in_shell = null) + { + // Defining constants + $this->options = array( + 'SEQUENCE' => true, + 'SHUTDOWN' => false, + 'SHELL' => $this->which($in_shell), + 'OUTPUT' => true, + 'NOHUP' => false, + 'BACKGROUND' => false, + 'STDERR' => false, + 'AUTORESET' => false + ); + + // prepare the available control operators + $this->controlOperators = array( + 'PIPE' => '|', + 'AND' => '&&', + 'OR' => '||', + 'GROUP' => ';', + 'LFIFO' => '<', + 'RFIFO' => '>', + ); + + // List of allowed/available shells + $this->shells = array( + 'sh', + 'bash', + 'zsh', + 'tcsh', + 'csh', + 'ash', + 'sash', + 'esh', + 'ksh' + ); + + // Find the first available shell + if (empty($this->options['SHELL'])) { + foreach ($this->shells as $shell) { + if ($this->options['SHELL'] = $this->which($shell)) { + break; + } + } + + // see if we still have no shell + if (empty($this->options['SHELL'])) { + $this->_initError =& PEAR::raiseError(null, SYSTEM_COMMAND_NO_SHELL, null, E_USER_WARNING, null, 'System_Command_Error', true); + return; + } + } + + // Caputre a temporary directory for capturing stderr from commands + $this->tmpDir = System::tmpdir(); + if (!System::mkDir("-p {$this->tmpDir}")) { + $this->_initError =& PEAR::raiseError(null, SYSTEM_COMMAND_TMPDIR_ERROR, null, E_USER_WARNING, null, 'System_Command_Error', true); + return; + } + } + + // }}} + // {{{ setOption() + + /** + * Sets the value for an option. Each option should be set to true + * or false; except the 'SHELL' option which should be a string + * naming a shell. The options are: + * + * 'SEQUENCE' Allow a sequence command or not (right now this is always on); + * + * 'SHUTDOWN' Execute commands via a shutdown function; + * + * 'SHELL' Path to shell; + * + * 'OUTPUT' Output stdout from process; + * + * 'NOHUP' Use nohup to detach process; + * + * 'BACKGROUND' Run as a background process with &; + * + * 'STDERR' Output on stderr will raise an error, even if + * the command's exit value is zero. The output from + * stderr can be retrieved using the getDebugInfo() + * method of the Pear_ERROR object returned by + * execute().; + * + * 'AUTORESET' Automatically call reset() after a successful + * call of execute(); + * + * @param string $in_option is a case-sensitive string, + * corresponding to the option + * that should be changed + * @param mixed $in_setting is the new value for the option + * @access public + * @return bool true if succes, else false + */ + function setOption($in_option, $in_setting) + { + if ($this->_initError) { + return $this->_initError; + } + + $option = strtoupper($in_option); + + if (!isset($this->options[$option])) { + PEAR::raiseError(null, SYSTEM_COMMAND_ERROR, null, E_USER_NOTICE, null, 'System_Command_Error', true); + return false; + } + + switch ($option) { + case 'OUTPUT': + case 'SHUTDOWN': + case 'SEQUENCE': + case 'BACKGROUND': + case 'STDERR': + $this->options[$option] = !empty($in_setting); + return true; + break; + + case 'SHELL': + if (($shell = $this->which($in_setting)) !== false) { + $this->options[$option] = $shell; + return true; + } + else { + PEAR::raiseError(null, SYSTEM_COMMAND_NO_SHELL, null, E_USER_NOTICE, $in_setting, 'System_Command_Error', true); + return false; + } + break; + + case 'NOHUP': + if (empty($in_setting)) { + $this->options[$option] = false; + } + else if ($location = $this->which('nohup')) { + $this->options[$option] = $location; + } + else { + PEAR::raiseError(null, SYSTEM_COMMAND_NOHUP_MISSING, null, E_USER_NOTICE, null, 'System_Command_Error', true); + return false; + } + break; + } + } + + // }}} + // {{{ pushCommand() + + /** + * Used to push a command onto the running command to be executed + * + * @param string $in_command binary to be run + * @param string $in_argument either an option or argument value, to be handled appropriately + * @param string $in_argument + * @param ... + * + * @access public + * @return boolean true on success {or System_Command_Error Exception} + */ + function pushCommand($in_command) + { + if ($this->_initError) { + return $this->_initError; + } + + if (!is_null($this->previousElement) && !in_array($this->previousElement, $this->controlOperators)) { + $this->commandStatus = -1; + $error = PEAR::raiseError(null, SYSTEM_COMMAND_COMMAND_PLACEMENT, null, E_USER_WARNING, null, 'System_Command_Error', true); + } + + // check for error here + $command = escapeshellcmd($this->which($in_command)); + if ($command === false) { + $error = PEAR::raiseError(null, SYSTEM_COMMAND_INVALID_COMMAND, null, E_USER_WARNING, null, 'System_Command_Error', true); + } + + $argv = func_get_args(); + array_shift($argv); + foreach($argv as $arg) { + if (strpos($arg, '-') === 0) { + $command .= ' ' . $arg; + } + elseif ($arg != '') { + $command .= ' ' . escapeshellarg($arg); + } + } + + $this->previousElement = $command; + $this->systemCommand .= $command; + + return isset($error) ? $error : true; + } + + // }}} + // {{{ pushOperator() + + /** + * Used to push an operator onto the running command to be executed + * + * @param string $in_operator Either string reprentation of operator or system character + * + * @access public + * @return boolean true on success {or System_Command_Error Exception} + */ + function pushOperator($in_operator) + { + if ($this->_initError) { + return $this->_initError; + } + + $operator = isset($this->controlOperators[$in_operator]) ? $this->controlOperators[$in_operator] : $in_operator; + + if (is_null($this->previousElement) || in_array($this->previousElement, $this->controlOperators)) { + $this->commandStatus = -1; + $error = PEAR::raiseError(null, SYSTEM_COMMAND_OPERATOR_PLACEMENT, null, E_USER_WARNING, null, 'System_Command_Error', true); + } + elseif (!in_array($operator, $this->controlOperators)) { + $this->commandStatus = -1; + $error = PEAR::raiseError(null, SYSTEM_COMMAND_INVALID_OPERATOR, null, E_USER_WARNING, $operator, 'System_Command_Error', true); + } + + $this->previousElement = $operator; + $this->systemCommand .= ' ' . $operator . ' '; + return isset($error) ? $error : true; + } + + // }}} + // {{{ execute() + + /** + * Executes the code according to given options + * + * @return bool true if success {or System_Command_Exception} + * + * @access public + */ + function execute() + { + if ($this->_initError) { + return $this->_initError; + } + + // if the command is empty or if the last element was a control operator, we can't continue + if (is_null($this->previousElement) || $this->commandStatus == -1 || in_array($this->previousElement, $this->controlOperators)) { + return PEAR::raiseError(null, SYSTEM_COMMAND_INVALID_COMMAND, null, E_USER_WARNING, $this->systemCommand, 'System_Command_Error', true); + } + + // Warning about impossible mix of options + if (!empty($this->options['OUTPUT'])) { + if (!empty($this->options['SHUTDOWN']) || !empty($this->options['NOHUP'])) { + return PEAR::raiseError(null, SYSTEM_COMMAND_NO_OUTPUT, null, E_USER_WARNING, null, 'System_Command_Error', true); + } + } + + // if this is not going to stdout, then redirect to /dev/null + if (empty($this->options['OUTPUT'])) { + $this->systemCommand .= ' >/dev/null'; + } + + $suffix = ''; + // run a command immune to hangups, with output to a non-tty + if (!empty($this->options['NOHUP'])) { + $this->systemCommand = $this->options['NOHUP'] . $this->systemCommand; + } + // run a background process (only if not nohup) + elseif (!empty($this->options['BACKGROUND'])) { + $suffix = ' &'; + } + + // Register to be run on shutdown + if (!empty($this->options['SHUTDOWN'])) { + $line = "system(\"{$this->systemCommand}$suffix\");"; + $function = create_function('', $line); + register_shutdown_function($function); + if ($this->options['AUTORESET']) { + $this->reset(); + } + return true; + } + else { + // send stderr to a file so that we can reap the error message + $tmpFile = tempnam($this->tmpDir, 'System_Command-'); + $this->systemCommand .= ' 2>' . $tmpFile . $suffix; + $shellPipe = $this->which('echo') . ' ' . escapeshellarg($this->systemCommand) . ' | ' . $this->options['SHELL']; + exec($shellPipe, $result, $returnVal); + + if ($returnVal !== 0) { + // command returned nonzero; that's always an error + $return = PEAR::raiseError(null, SYSTEM_COMMAND_NONZERO_EXIT, null, E_USER_WARNING, null, 'System_Command_Error', true); + } + else if (!$this->options['STDERR']) { + // caller does not care about stderr; return success + $return = implode("\n", $result); + } + else { + // our caller cares about stderr; check stderr output + clearstatcache(); + if (filesize($tmpFile) > 0) { + // the command actually wrote to stderr + $stderr_output = file_get_contents($tmpFile); + $return = PEAR::raiseError(null, SYSTEM_COMMAND_STDERR, null, E_USER_WARNING, $stderr_output, 'System_Command_Error', true); + } else { + // total success; return stdout gathered by exec() + $return = implode("\n", $result); + } + } + + if ((!PEAR::isError($return)) && ($this->options['AUTORESET'])) { + $this->reset(); + } + + unlink($tmpFile); + return $return; + } + } + + // }}} + // {{{ which() + + /** + * Functionality similiar to unix 'which'. Searches the path + * for the specified program. + * + * @param $cmd name of the executable to search for + * + * @access private + * @return string returns the full path if found, false if not + */ + function which($in_cmd) + { + // only pass non-empty strings to System::which() + if (!is_string($in_cmd) || '' === $in_cmd) { + return(false); + } + + // explicitly pass false as fallback value + return System::which($in_cmd, false); + } + + // }}} + // {{{ reset() + + /** + * Prepare for a new command to be built + * + * @access public + * @return void + */ + function reset() + { + $this->previousElement = null; + $this->systemCommand = null; + $this->commandStatus = 0; + } + + // }}} + // {{{ errorMessage() + + /** + * Return a textual error message for a System_Command error code + * + * @param integer error code + * + * @return string error message, or false if the error code was + * not recognized + */ + function errorMessage($in_value) + { + static $errorMessages; + if (!isset($errorMessages)) { + $errorMessages = array( + SYSTEM_COMMAND_OK => 'no error', + SYSTEM_COMMAND_ERROR => 'unknown error', + SYSTEM_COMMAND_NO_SHELL => 'no shell found', + SYSTEM_COMMAND_INVALID_SHELL => 'invalid shell', + SYSTEM_COMMAND_TMPDIR_ERROR => 'could not create temporary directory', + SYSTEM_COMMAND_INVALID_OPERATOR => 'control operator invalid', + SYSTEM_COMMAND_INVALID_COMMAND => 'invalid system command', + SYSTEM_COMMAND_OPERATOR_PLACEMENT => 'invalid placement of control operator', + SYSTEM_COMMAND_COMMAND_PLACEMENT => 'invalid placement of command', + SYSTEM_COMMAND_NOHUP_MISSING => 'nohup not found on system', + SYSTEM_COMMAND_NO_OUTPUT => 'output not allowed', + SYSTEM_COMMAND_STDERR => 'command wrote to stderr', + SYSTEM_COMMAND_NONZERO_EXIT => 'non-zero exit value from command', + ); + } + + if (System_Command::isError($in_value)) { + $in_value = $in_value->getCode(); + } + + return isset($errorMessages[$in_value]) ? $errorMessages[$in_value] : $errorMessages[SYSTEM_COMMAND_ERROR]; + } + + // }}} + // {{{ isError() + + /** + * Tell whether a result code from a System_Command method is an error + * + * @param int result code + * + * @return bool whether $in_value is an error + * + * @access public + */ + function isError($in_value) + { + return (is_object($in_value) && + (strtolower(get_class($in_value)) == 'system_command_error' || + is_subclass_of($in_value, 'system_command_error'))); + } + + // }}} +} + +// {{{ class System_Command_Error + +/** + * System_Command_Error constructor. + * + * @param mixed System_Command error code, or string with error message. + * @param integer what "error mode" to operate in + * @param integer what error level to use for $mode & PEAR_ERROR_TRIGGER + * @param mixed additional debug info, such as the last query + * + * @access public + * + * @see PEAR_Error + */ + +// }}} +class System_Command_Error extends PEAR_Error +{ + // {{{ properties + + /** + * Message in front of the error message + * @var string $error_message_prefix + */ + var $error_message_prefix = 'System_Command Error: '; + + // }}} + // {{{ constructor + + function System_Command_Error($code = SYSTEM_COMMAND_ERROR, $mode = PEAR_ERROR_RETURN, + $level = E_USER_NOTICE, $debuginfo = null) + { + if (is_int($code)) { + $this->PEAR_Error(System_Command::errorMessage($code), $code, $mode, $level, $debuginfo); + } else { + $this->PEAR_Error("Invalid error code: $code", SYSTEM_COMMAND_ERROR, $mode, $level, $debuginfo); + } + } + + // }}} +} +?> diff --git a/includes/pear/Text/Diff.php b/includes/pear/Text/Diff.php new file mode 100644 index 0000000..4b3de1b --- /dev/null +++ b/includes/pear/Text/Diff.php @@ -0,0 +1,453 @@ +, and is used/adapted with his permission. + * + * $Horde: framework/Text_Diff/Diff.php,v 1.11.2.12 2009/01/06 15:23:41 jan Exp $ + * + * Copyright 2004 Geoffrey T. Dairiki + * Copyright 2004-2009 The Horde Project (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (LGPL). If you did + * not receive this file, see http://opensource.org/licenses/lgpl-license.php. + * + * @package Text_Diff + * @author Geoffrey T. Dairiki + */ +class Text_Diff { + + /** + * Array of changes. + * + * @var array + */ + var $_edits; + + /** + * Computes diffs between sequences of strings. + * + * @param string $engine Name of the diffing engine to use. 'auto' + * will automatically select the best. + * @param array $params Parameters to pass to the diffing engine. + * Normally an array of two arrays, each + * containing the lines from a file. + */ + function Text_Diff($engine, $params) + { + // Backward compatibility workaround. + if (!is_string($engine)) { + $params = array($engine, $params); + $engine = 'auto'; + } + + if ($engine == 'auto') { + $engine = extension_loaded('xdiff') ? 'xdiff' : 'native'; + } else { + $engine = basename($engine); + } + + require_once 'Text/Diff/Engine/' . $engine . '.php'; + $class = 'Text_Diff_Engine_' . $engine; + $diff_engine = new $class(); + + $this->_edits = call_user_func_array(array($diff_engine, 'diff'), $params); + } + + /** + * Returns the array of differences. + */ + function getDiff() + { + return $this->_edits; + } + + /** + * returns the number of new (added) lines in a given diff. + * + * @since Text_Diff 1.1.0 + * @since Horde 3.2 + * + * @return integer The number of new lines + */ + function countAddedLines() + { + $count = 0; + foreach ($this->_edits as $edit) { + if (is_a($edit, 'Text_Diff_Op_add') || + is_a($edit, 'Text_Diff_Op_change')) { + $count += $edit->nfinal(); + } + } + return $count; + } + + /** + * Returns the number of deleted (removed) lines in a given diff. + * + * @since Text_Diff 1.1.0 + * @since Horde 3.2 + * + * @return integer The number of deleted lines + */ + function countDeletedLines() + { + $count = 0; + foreach ($this->_edits as $edit) { + if (is_a($edit, 'Text_Diff_Op_delete') || + is_a($edit, 'Text_Diff_Op_change')) { + $count += $edit->norig(); + } + } + return $count; + } + + /** + * Computes a reversed diff. + * + * Example: + * + * $diff = new Text_Diff($lines1, $lines2); + * $rev = $diff->reverse(); + * + * + * @return Text_Diff A Diff object representing the inverse of the + * original diff. Note that we purposely don't return a + * reference here, since this essentially is a clone() + * method. + */ + function reverse() + { + if (version_compare(zend_version(), '2', '>')) { + $rev = clone($this); + } else { + $rev = $this; + } + $rev->_edits = array(); + foreach ($this->_edits as $edit) { + $rev->_edits[] = $edit->reverse(); + } + return $rev; + } + + /** + * Checks for an empty diff. + * + * @return boolean True if two sequences were identical. + */ + function isEmpty() + { + foreach ($this->_edits as $edit) { + if (!is_a($edit, 'Text_Diff_Op_copy')) { + return false; + } + } + return true; + } + + /** + * Computes the length of the Longest Common Subsequence (LCS). + * + * This is mostly for diagnostic purposes. + * + * @return integer The length of the LCS. + */ + function lcs() + { + $lcs = 0; + foreach ($this->_edits as $edit) { + if (is_a($edit, 'Text_Diff_Op_copy')) { + $lcs += count($edit->orig); + } + } + return $lcs; + } + + /** + * Gets the original set of lines. + * + * This reconstructs the $from_lines parameter passed to the constructor. + * + * @return array The original sequence of strings. + */ + function getOriginal() + { + $lines = array(); + foreach ($this->_edits as $edit) { + if ($edit->orig) { + array_splice($lines, count($lines), 0, $edit->orig); + } + } + return $lines; + } + + /** + * Gets the final set of lines. + * + * This reconstructs the $to_lines parameter passed to the constructor. + * + * @return array The sequence of strings. + */ + function getFinal() + { + $lines = array(); + foreach ($this->_edits as $edit) { + if ($edit->final) { + array_splice($lines, count($lines), 0, $edit->final); + } + } + return $lines; + } + + /** + * Removes trailing newlines from a line of text. This is meant to be used + * with array_walk(). + * + * @param string $line The line to trim. + * @param integer $key The index of the line in the array. Not used. + */ + function trimNewlines(&$line, $key) + { + $line = str_replace(array("\n", "\r"), '', $line); + } + + /** + * Determines the location of the system temporary directory. + * + * @static + * + * @access protected + * + * @return string A directory name which can be used for temp files. + * Returns false if one could not be found. + */ + function _getTempDir() + { + $tmp_locations = array('/tmp', '/var/tmp', 'c:\WUTemp', 'c:\temp', + 'c:\windows\temp', 'c:\winnt\temp'); + + /* Try PHP's upload_tmp_dir directive. */ + $tmp = ini_get('upload_tmp_dir'); + + /* Otherwise, try to determine the TMPDIR environment variable. */ + if (!strlen($tmp)) { + $tmp = getenv('TMPDIR'); + } + + /* If we still cannot determine a value, then cycle through a list of + * preset possibilities. */ + while (!strlen($tmp) && count($tmp_locations)) { + $tmp_check = array_shift($tmp_locations); + if (@is_dir($tmp_check)) { + $tmp = $tmp_check; + } + } + + /* If it is still empty, we have failed, so return false; otherwise + * return the directory determined. */ + return strlen($tmp) ? $tmp : false; + } + + /** + * Checks a diff for validity. + * + * This is here only for debugging purposes. + */ + function _check($from_lines, $to_lines) + { + if (serialize($from_lines) != serialize($this->getOriginal())) { + trigger_error("Reconstructed original doesn't match", E_USER_ERROR); + } + if (serialize($to_lines) != serialize($this->getFinal())) { + trigger_error("Reconstructed final doesn't match", E_USER_ERROR); + } + + $rev = $this->reverse(); + if (serialize($to_lines) != serialize($rev->getOriginal())) { + trigger_error("Reversed original doesn't match", E_USER_ERROR); + } + if (serialize($from_lines) != serialize($rev->getFinal())) { + trigger_error("Reversed final doesn't match", E_USER_ERROR); + } + + $prevtype = null; + foreach ($this->_edits as $edit) { + if ($prevtype == get_class($edit)) { + trigger_error("Edit sequence is non-optimal", E_USER_ERROR); + } + $prevtype = get_class($edit); + } + + return true; + } + +} + +/** + * @package Text_Diff + * @author Geoffrey T. Dairiki + */ +class Text_MappedDiff extends Text_Diff { + + /** + * Computes a diff between sequences of strings. + * + * This can be used to compute things like case-insensitve diffs, or diffs + * which ignore changes in white-space. + * + * @param array $from_lines An array of strings. + * @param array $to_lines An array of strings. + * @param array $mapped_from_lines This array should have the same size + * number of elements as $from_lines. The + * elements in $mapped_from_lines and + * $mapped_to_lines are what is actually + * compared when computing the diff. + * @param array $mapped_to_lines This array should have the same number + * of elements as $to_lines. + */ + function Text_MappedDiff($from_lines, $to_lines, + $mapped_from_lines, $mapped_to_lines) + { + assert(count($from_lines) == count($mapped_from_lines)); + assert(count($to_lines) == count($mapped_to_lines)); + + parent::Text_Diff($mapped_from_lines, $mapped_to_lines); + + $xi = $yi = 0; + for ($i = 0; $i < count($this->_edits); $i++) { + $orig = &$this->_edits[$i]->orig; + if (is_array($orig)) { + $orig = array_slice($from_lines, $xi, count($orig)); + $xi += count($orig); + } + + $final = &$this->_edits[$i]->final; + if (is_array($final)) { + $final = array_slice($to_lines, $yi, count($final)); + $yi += count($final); + } + } + } + +} + +/** + * @package Text_Diff + * @author Geoffrey T. Dairiki + * + * @access private + */ +class Text_Diff_Op { + + var $orig; + var $final; + + function &reverse() + { + trigger_error('Abstract method', E_USER_ERROR); + } + + function norig() + { + return $this->orig ? count($this->orig) : 0; + } + + function nfinal() + { + return $this->final ? count($this->final) : 0; + } + +} + +/** + * @package Text_Diff + * @author Geoffrey T. Dairiki + * + * @access private + */ +class Text_Diff_Op_copy extends Text_Diff_Op { + + function Text_Diff_Op_copy($orig, $final = false) + { + if (!is_array($final)) { + $final = $orig; + } + $this->orig = $orig; + $this->final = $final; + } + + function &reverse() + { + $reverse = &new Text_Diff_Op_copy($this->final, $this->orig); + return $reverse; + } + +} + +/** + * @package Text_Diff + * @author Geoffrey T. Dairiki + * + * @access private + */ +class Text_Diff_Op_delete extends Text_Diff_Op { + + function Text_Diff_Op_delete($lines) + { + $this->orig = $lines; + $this->final = false; + } + + function &reverse() + { + $reverse = &new Text_Diff_Op_add($this->orig); + return $reverse; + } + +} + +/** + * @package Text_Diff + * @author Geoffrey T. Dairiki + * + * @access private + */ +class Text_Diff_Op_add extends Text_Diff_Op { + + function Text_Diff_Op_add($lines) + { + $this->final = $lines; + $this->orig = false; + } + + function &reverse() + { + $reverse = &new Text_Diff_Op_delete($this->final); + return $reverse; + } + +} + +/** + * @package Text_Diff + * @author Geoffrey T. Dairiki + * + * @access private + */ +class Text_Diff_Op_change extends Text_Diff_Op { + + function Text_Diff_Op_change($orig, $final) + { + $this->orig = $orig; + $this->final = $final; + } + + function &reverse() + { + $reverse = &new Text_Diff_Op_change($this->final, $this->orig); + return $reverse; + } + +} diff --git a/includes/pear/Text/Diff/Engine/native.php b/includes/pear/Text/Diff/Engine/native.php new file mode 100644 index 0000000..84168c0 --- /dev/null +++ b/includes/pear/Text/Diff/Engine/native.php @@ -0,0 +1,438 @@ + 2, and some optimizations) are from + * Geoffrey T. Dairiki . The original PHP version of this + * code was written by him, and is used/adapted with his permission. + * + * $Horde: framework/Text_Diff/Diff/Engine/native.php,v 1.7.2.5 2009/01/06 15:23:41 jan Exp $ + * + * Copyright 2004-2009 The Horde Project (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (LGPL). If you did + * not receive this file, see http://opensource.org/licenses/lgpl-license.php. + * + * @author Geoffrey T. Dairiki + * @package Text_Diff + */ +class Text_Diff_Engine_native { + + function diff($from_lines, $to_lines) + { + array_walk($from_lines, array('Text_Diff', 'trimNewlines')); + array_walk($to_lines, array('Text_Diff', 'trimNewlines')); + + $n_from = count($from_lines); + $n_to = count($to_lines); + + $this->xchanged = $this->ychanged = array(); + $this->xv = $this->yv = array(); + $this->xind = $this->yind = array(); + unset($this->seq); + unset($this->in_seq); + unset($this->lcs); + + // Skip leading common lines. + for ($skip = 0; $skip < $n_from && $skip < $n_to; $skip++) { + if ($from_lines[$skip] !== $to_lines[$skip]) { + break; + } + $this->xchanged[$skip] = $this->ychanged[$skip] = false; + } + + // Skip trailing common lines. + $xi = $n_from; $yi = $n_to; + for ($endskip = 0; --$xi > $skip && --$yi > $skip; $endskip++) { + if ($from_lines[$xi] !== $to_lines[$yi]) { + break; + } + $this->xchanged[$xi] = $this->ychanged[$yi] = false; + } + + // Ignore lines which do not exist in both files. + for ($xi = $skip; $xi < $n_from - $endskip; $xi++) { + $xhash[$from_lines[$xi]] = 1; + } + for ($yi = $skip; $yi < $n_to - $endskip; $yi++) { + $line = $to_lines[$yi]; + if (($this->ychanged[$yi] = empty($xhash[$line]))) { + continue; + } + $yhash[$line] = 1; + $this->yv[] = $line; + $this->yind[] = $yi; + } + for ($xi = $skip; $xi < $n_from - $endskip; $xi++) { + $line = $from_lines[$xi]; + if (($this->xchanged[$xi] = empty($yhash[$line]))) { + continue; + } + $this->xv[] = $line; + $this->xind[] = $xi; + } + + // Find the LCS. + $this->_compareseq(0, count($this->xv), 0, count($this->yv)); + + // Merge edits when possible. + $this->_shiftBoundaries($from_lines, $this->xchanged, $this->ychanged); + $this->_shiftBoundaries($to_lines, $this->ychanged, $this->xchanged); + + // Compute the edit operations. + $edits = array(); + $xi = $yi = 0; + while ($xi < $n_from || $yi < $n_to) { + assert($yi < $n_to || $this->xchanged[$xi]); + assert($xi < $n_from || $this->ychanged[$yi]); + + // Skip matching "snake". + $copy = array(); + while ($xi < $n_from && $yi < $n_to + && !$this->xchanged[$xi] && !$this->ychanged[$yi]) { + $copy[] = $from_lines[$xi++]; + ++$yi; + } + if ($copy) { + $edits[] = &new Text_Diff_Op_copy($copy); + } + + // Find deletes & adds. + $delete = array(); + while ($xi < $n_from && $this->xchanged[$xi]) { + $delete[] = $from_lines[$xi++]; + } + + $add = array(); + while ($yi < $n_to && $this->ychanged[$yi]) { + $add[] = $to_lines[$yi++]; + } + + if ($delete && $add) { + $edits[] = &new Text_Diff_Op_change($delete, $add); + } elseif ($delete) { + $edits[] = &new Text_Diff_Op_delete($delete); + } elseif ($add) { + $edits[] = &new Text_Diff_Op_add($add); + } + } + + return $edits; + } + + /** + * Divides the Largest Common Subsequence (LCS) of the sequences (XOFF, + * XLIM) and (YOFF, YLIM) into NCHUNKS approximately equally sized + * segments. + * + * Returns (LCS, PTS). LCS is the length of the LCS. PTS is an array of + * NCHUNKS+1 (X, Y) indexes giving the diving points between sub + * sequences. The first sub-sequence is contained in (X0, X1), (Y0, Y1), + * the second in (X1, X2), (Y1, Y2) and so on. Note that (X0, Y0) == + * (XOFF, YOFF) and (X[NCHUNKS], Y[NCHUNKS]) == (XLIM, YLIM). + * + * This function assumes that the first lines of the specified portions of + * the two files do not match, and likewise that the last lines do not + * match. The caller must trim matching lines from the beginning and end + * of the portions it is going to specify. + */ + function _diag ($xoff, $xlim, $yoff, $ylim, $nchunks) + { + $flip = false; + + if ($xlim - $xoff > $ylim - $yoff) { + /* Things seems faster (I'm not sure I understand why) when the + * shortest sequence is in X. */ + $flip = true; + list ($xoff, $xlim, $yoff, $ylim) + = array($yoff, $ylim, $xoff, $xlim); + } + + if ($flip) { + for ($i = $ylim - 1; $i >= $yoff; $i--) { + $ymatches[$this->xv[$i]][] = $i; + } + } else { + for ($i = $ylim - 1; $i >= $yoff; $i--) { + $ymatches[$this->yv[$i]][] = $i; + } + } + + $this->lcs = 0; + $this->seq[0]= $yoff - 1; + $this->in_seq = array(); + $ymids[0] = array(); + + $numer = $xlim - $xoff + $nchunks - 1; + $x = $xoff; + for ($chunk = 0; $chunk < $nchunks; $chunk++) { + if ($chunk > 0) { + for ($i = 0; $i <= $this->lcs; $i++) { + $ymids[$i][$chunk - 1] = $this->seq[$i]; + } + } + + $x1 = $xoff + (int)(($numer + ($xlim - $xoff) * $chunk) / $nchunks); + for (; $x < $x1; $x++) { + $line = $flip ? $this->yv[$x] : $this->xv[$x]; + if (empty($ymatches[$line])) { + continue; + } + $matches = $ymatches[$line]; + reset($matches); + while (list(, $y) = each($matches)) { + if (empty($this->in_seq[$y])) { + $k = $this->_lcsPos($y); + assert($k > 0); + $ymids[$k] = $ymids[$k - 1]; + break; + } + } + while (list(, $y) = each($matches)) { + if ($y > $this->seq[$k - 1]) { + assert($y <= $this->seq[$k]); + /* Optimization: this is a common case: next match is + * just replacing previous match. */ + $this->in_seq[$this->seq[$k]] = false; + $this->seq[$k] = $y; + $this->in_seq[$y] = 1; + } elseif (empty($this->in_seq[$y])) { + $k = $this->_lcsPos($y); + assert($k > 0); + $ymids[$k] = $ymids[$k - 1]; + } + } + } + } + + $seps[] = $flip ? array($yoff, $xoff) : array($xoff, $yoff); + $ymid = $ymids[$this->lcs]; + for ($n = 0; $n < $nchunks - 1; $n++) { + $x1 = $xoff + (int)(($numer + ($xlim - $xoff) * $n) / $nchunks); + $y1 = $ymid[$n] + 1; + $seps[] = $flip ? array($y1, $x1) : array($x1, $y1); + } + $seps[] = $flip ? array($ylim, $xlim) : array($xlim, $ylim); + + return array($this->lcs, $seps); + } + + function _lcsPos($ypos) + { + $end = $this->lcs; + if ($end == 0 || $ypos > $this->seq[$end]) { + $this->seq[++$this->lcs] = $ypos; + $this->in_seq[$ypos] = 1; + return $this->lcs; + } + + $beg = 1; + while ($beg < $end) { + $mid = (int)(($beg + $end) / 2); + if ($ypos > $this->seq[$mid]) { + $beg = $mid + 1; + } else { + $end = $mid; + } + } + + assert($ypos != $this->seq[$end]); + + $this->in_seq[$this->seq[$end]] = false; + $this->seq[$end] = $ypos; + $this->in_seq[$ypos] = 1; + return $end; + } + + /** + * Finds LCS of two sequences. + * + * The results are recorded in the vectors $this->{x,y}changed[], by + * storing a 1 in the element for each line that is an insertion or + * deletion (ie. is not in the LCS). + * + * The subsequence of file 0 is (XOFF, XLIM) and likewise for file 1. + * + * Note that XLIM, YLIM are exclusive bounds. All line numbers are + * origin-0 and discarded lines are not counted. + */ + function _compareseq ($xoff, $xlim, $yoff, $ylim) + { + /* Slide down the bottom initial diagonal. */ + while ($xoff < $xlim && $yoff < $ylim + && $this->xv[$xoff] == $this->yv[$yoff]) { + ++$xoff; + ++$yoff; + } + + /* Slide up the top initial diagonal. */ + while ($xlim > $xoff && $ylim > $yoff + && $this->xv[$xlim - 1] == $this->yv[$ylim - 1]) { + --$xlim; + --$ylim; + } + + if ($xoff == $xlim || $yoff == $ylim) { + $lcs = 0; + } else { + /* This is ad hoc but seems to work well. $nchunks = + * sqrt(min($xlim - $xoff, $ylim - $yoff) / 2.5); $nchunks = + * max(2,min(8,(int)$nchunks)); */ + $nchunks = min(7, $xlim - $xoff, $ylim - $yoff) + 1; + list($lcs, $seps) + = $this->_diag($xoff, $xlim, $yoff, $ylim, $nchunks); + } + + if ($lcs == 0) { + /* X and Y sequences have no common subsequence: mark all + * changed. */ + while ($yoff < $ylim) { + $this->ychanged[$this->yind[$yoff++]] = 1; + } + while ($xoff < $xlim) { + $this->xchanged[$this->xind[$xoff++]] = 1; + } + } else { + /* Use the partitions to split this problem into subproblems. */ + reset($seps); + $pt1 = $seps[0]; + while ($pt2 = next($seps)) { + $this->_compareseq ($pt1[0], $pt2[0], $pt1[1], $pt2[1]); + $pt1 = $pt2; + } + } + } + + /** + * Adjusts inserts/deletes of identical lines to join changes as much as + * possible. + * + * We do something when a run of changed lines include a line at one end + * and has an excluded, identical line at the other. We are free to + * choose which identical line is included. `compareseq' usually chooses + * the one at the beginning, but usually it is cleaner to consider the + * following identical line to be the "change". + * + * This is extracted verbatim from analyze.c (GNU diffutils-2.7). + */ + function _shiftBoundaries($lines, &$changed, $other_changed) + { + $i = 0; + $j = 0; + + assert('count($lines) == count($changed)'); + $len = count($lines); + $other_len = count($other_changed); + + while (1) { + /* Scan forward to find the beginning of another run of + * changes. Also keep track of the corresponding point in the + * other file. + * + * Throughout this code, $i and $j are adjusted together so that + * the first $i elements of $changed and the first $j elements of + * $other_changed both contain the same number of zeros (unchanged + * lines). + * + * Furthermore, $j is always kept so that $j == $other_len or + * $other_changed[$j] == false. */ + while ($j < $other_len && $other_changed[$j]) { + $j++; + } + + while ($i < $len && ! $changed[$i]) { + assert('$j < $other_len && ! $other_changed[$j]'); + $i++; $j++; + while ($j < $other_len && $other_changed[$j]) { + $j++; + } + } + + if ($i == $len) { + break; + } + + $start = $i; + + /* Find the end of this run of changes. */ + while (++$i < $len && $changed[$i]) { + continue; + } + + do { + /* Record the length of this run of changes, so that we can + * later determine whether the run has grown. */ + $runlength = $i - $start; + + /* Move the changed region back, so long as the previous + * unchanged line matches the last changed one. This merges + * with previous changed regions. */ + while ($start > 0 && $lines[$start - 1] == $lines[$i - 1]) { + $changed[--$start] = 1; + $changed[--$i] = false; + while ($start > 0 && $changed[$start - 1]) { + $start--; + } + assert('$j > 0'); + while ($other_changed[--$j]) { + continue; + } + assert('$j >= 0 && !$other_changed[$j]'); + } + + /* Set CORRESPONDING to the end of the changed run, at the + * last point where it corresponds to a changed run in the + * other file. CORRESPONDING == LEN means no such point has + * been found. */ + $corresponding = $j < $other_len ? $i : $len; + + /* Move the changed region forward, so long as the first + * changed line matches the following unchanged one. This + * merges with following changed regions. Do this second, so + * that if there are no merges, the changed region is moved + * forward as far as possible. */ + while ($i < $len && $lines[$start] == $lines[$i]) { + $changed[$start++] = false; + $changed[$i++] = 1; + while ($i < $len && $changed[$i]) { + $i++; + } + + assert('$j < $other_len && ! $other_changed[$j]'); + $j++; + if ($j < $other_len && $other_changed[$j]) { + $corresponding = $i; + while ($j < $other_len && $other_changed[$j]) { + $j++; + } + } + } + } while ($runlength != $i - $start); + + /* If possible, move the fully-merged run of changes back to a + * corresponding run in the other file. */ + while ($corresponding < $i) { + $changed[--$start] = 1; + $changed[--$i] = 0; + assert('$j > 0'); + while ($other_changed[--$j]) { + continue; + } + assert('$j >= 0 && !$other_changed[$j]'); + } + } + } + +} diff --git a/includes/pear/Text/Diff/Engine/shell.php b/includes/pear/Text/Diff/Engine/shell.php new file mode 100644 index 0000000..7f858cb --- /dev/null +++ b/includes/pear/Text/Diff/Engine/shell.php @@ -0,0 +1,164 @@ + + * @package Text_Diff + * @since 0.3.0 + */ +class Text_Diff_Engine_shell { + + /** + * Path to the diff executable + * + * @var string + */ + var $_diffCommand = 'diff'; + + /** + * Returns the array of differences. + * + * @param array $from_lines lines of text from old file + * @param array $to_lines lines of text from new file + * + * @return array all changes made (array with Text_Diff_Op_* objects) + */ + function diff($from_lines, $to_lines) + { + array_walk($from_lines, array('Text_Diff', 'trimNewlines')); + array_walk($to_lines, array('Text_Diff', 'trimNewlines')); + + $temp_dir = Text_Diff::_getTempDir(); + + // Execute gnu diff or similar to get a standard diff file. + $from_file = tempnam($temp_dir, 'Text_Diff'); + $to_file = tempnam($temp_dir, 'Text_Diff'); + $fp = fopen($from_file, 'w'); + fwrite($fp, implode("\n", $from_lines)); + fclose($fp); + $fp = fopen($to_file, 'w'); + fwrite($fp, implode("\n", $to_lines)); + fclose($fp); + $diff = shell_exec($this->_diffCommand . ' ' . $from_file . ' ' . $to_file); + unlink($from_file); + unlink($to_file); + + if (is_null($diff)) { + // No changes were made + return array(new Text_Diff_Op_copy($from_lines)); + } + + $from_line_no = 1; + $to_line_no = 1; + $edits = array(); + + // Get changed lines by parsing something like: + // 0a1,2 + // 1,2c4,6 + // 1,5d6 + preg_match_all('#^(\d+)(?:,(\d+))?([adc])(\d+)(?:,(\d+))?$#m', $diff, + $matches, PREG_SET_ORDER); + + foreach ($matches as $match) { + if (!isset($match[5])) { + // This paren is not set every time (see regex). + $match[5] = false; + } + + if ($match[3] == 'a') { + $from_line_no--; + } + + if ($match[3] == 'd') { + $to_line_no--; + } + + if ($from_line_no < $match[1] || $to_line_no < $match[4]) { + // copied lines + assert('$match[1] - $from_line_no == $match[4] - $to_line_no'); + array_push($edits, + new Text_Diff_Op_copy( + $this->_getLines($from_lines, $from_line_no, $match[1] - 1), + $this->_getLines($to_lines, $to_line_no, $match[4] - 1))); + } + + switch ($match[3]) { + case 'd': + // deleted lines + array_push($edits, + new Text_Diff_Op_delete( + $this->_getLines($from_lines, $from_line_no, $match[2]))); + $to_line_no++; + break; + + case 'c': + // changed lines + array_push($edits, + new Text_Diff_Op_change( + $this->_getLines($from_lines, $from_line_no, $match[2]), + $this->_getLines($to_lines, $to_line_no, $match[5]))); + break; + + case 'a': + // added lines + array_push($edits, + new Text_Diff_Op_add( + $this->_getLines($to_lines, $to_line_no, $match[5]))); + $from_line_no++; + break; + } + } + + if (!empty($from_lines)) { + // Some lines might still be pending. Add them as copied + array_push($edits, + new Text_Diff_Op_copy( + $this->_getLines($from_lines, $from_line_no, + $from_line_no + count($from_lines) - 1), + $this->_getLines($to_lines, $to_line_no, + $to_line_no + count($to_lines) - 1))); + } + + return $edits; + } + + /** + * Get lines from either the old or new text + * + * @access private + * + * @param array &$text_lines Either $from_lines or $to_lines + * @param int &$line_no Current line number + * @param int $end Optional end line, when we want to chop more + * than one line. + * + * @return array The chopped lines + */ + function _getLines(&$text_lines, &$line_no, $end = false) + { + if (!empty($end)) { + $lines = array(); + // We can shift even more + while ($line_no <= $end) { + array_push($lines, array_shift($text_lines)); + $line_no++; + } + } else { + $lines = array(array_shift($text_lines)); + $line_no++; + } + + return $lines; + } + +} diff --git a/includes/pear/Text/Diff/Engine/string.php b/includes/pear/Text/Diff/Engine/string.php new file mode 100644 index 0000000..9352e60 --- /dev/null +++ b/includes/pear/Text/Diff/Engine/string.php @@ -0,0 +1,250 @@ + + * $patch = file_get_contents('example.patch'); + * $diff = new Text_Diff('string', array($patch)); + * $renderer = new Text_Diff_Renderer_inline(); + * echo $renderer->render($diff); + * + * + * $Horde: framework/Text_Diff/Diff/Engine/string.php,v 1.5.2.7 2009/07/24 13:04:43 jan Exp $ + * + * Copyright 2005 Örjan Persson + * Copyright 2005-2009 The Horde Project (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (LGPL). If you did + * not receive this file, see http://opensource.org/licenses/lgpl-license.php. + * + * @author Örjan Persson + * @package Text_Diff + * @since 0.2.0 + */ +class Text_Diff_Engine_string { + + /** + * Parses a unified or context diff. + * + * First param contains the whole diff and the second can be used to force + * a specific diff type. If the second parameter is 'autodetect', the + * diff will be examined to find out which type of diff this is. + * + * @param string $diff The diff content. + * @param string $mode The diff mode of the content in $diff. One of + * 'context', 'unified', or 'autodetect'. + * + * @return array List of all diff operations. + */ + function diff($diff, $mode = 'autodetect') + { + // Detect line breaks. + $lnbr = "\n"; + if (strpos($diff, "\r\n") !== false) { + $lnbr = "\r\n"; + } elseif (strpos($diff, "\r") !== false) { + $lnbr = "\r"; + } + + // Make sure we have a line break at the EOF. + if (substr($diff, -strlen($lnbr)) != $lnbr) { + $diff .= $lnbr; + } + + if ($mode != 'autodetect' && $mode != 'context' && $mode != 'unified') { + return PEAR::raiseError('Type of diff is unsupported'); + } + + if ($mode == 'autodetect') { + $context = strpos($diff, '***'); + $unified = strpos($diff, '---'); + if ($context === $unified) { + return PEAR::raiseError('Type of diff could not be detected'); + } elseif ($context === false || $unified === false) { + $mode = $context !== false ? 'context' : 'unified'; + } else { + $mode = $context < $unified ? 'context' : 'unified'; + } + } + + // Split by new line and remove the diff header, if there is one. + $diff = explode($lnbr, $diff); + if (($mode == 'context' && strpos($diff[0], '***') === 0) || + ($mode == 'unified' && strpos($diff[0], '---') === 0)) { + array_shift($diff); + array_shift($diff); + } + + if ($mode == 'context') { + return $this->parseContextDiff($diff); + } else { + return $this->parseUnifiedDiff($diff); + } + } + + /** + * Parses an array containing the unified diff. + * + * @param array $diff Array of lines. + * + * @return array List of all diff operations. + */ + function parseUnifiedDiff($diff) + { + $edits = array(); + $end = count($diff) - 1; + for ($i = 0; $i < $end;) { + $diff1 = array(); + switch (substr($diff[$i], 0, 1)) { + case ' ': + do { + $diff1[] = substr($diff[$i], 1); + } while (++$i < $end && substr($diff[$i], 0, 1) == ' '); + $edits[] = new Text_Diff_Op_copy($diff1); + break; + + case '+': + // get all new lines + do { + $diff1[] = substr($diff[$i], 1); + } while (++$i < $end && substr($diff[$i], 0, 1) == '+'); + $edits[] = new Text_Diff_Op_add($diff1); + break; + + case '-': + // get changed or removed lines + $diff2 = array(); + do { + $diff1[] = substr($diff[$i], 1); + } while (++$i < $end && substr($diff[$i], 0, 1) == '-'); + + while ($i < $end && substr($diff[$i], 0, 1) == '+') { + $diff2[] = substr($diff[$i++], 1); + } + if (count($diff2) == 0) { + $edits[] = new Text_Diff_Op_delete($diff1); + } else { + $edits[] = new Text_Diff_Op_change($diff1, $diff2); + } + break; + + default: + $i++; + break; + } + } + + return $edits; + } + + /** + * Parses an array containing the context diff. + * + * @param array $diff Array of lines. + * + * @return array List of all diff operations. + */ + function parseContextDiff(&$diff) + { + $edits = array(); + $i = $max_i = $j = $max_j = 0; + $end = count($diff) - 1; + while ($i < $end && $j < $end) { + while ($i >= $max_i && $j >= $max_j) { + // Find the boundaries of the diff output of the two files + for ($i = $j; + $i < $end && substr($diff[$i], 0, 3) == '***'; + $i++); + for ($max_i = $i; + $max_i < $end && substr($diff[$max_i], 0, 3) != '---'; + $max_i++); + for ($j = $max_i; + $j < $end && substr($diff[$j], 0, 3) == '---'; + $j++); + for ($max_j = $j; + $max_j < $end && substr($diff[$max_j], 0, 3) != '***'; + $max_j++); + } + + // find what hasn't been changed + $array = array(); + while ($i < $max_i && + $j < $max_j && + strcmp($diff[$i], $diff[$j]) == 0) { + $array[] = substr($diff[$i], 2); + $i++; + $j++; + } + + while ($i < $max_i && ($max_j-$j) <= 1) { + if ($diff[$i] != '' && substr($diff[$i], 0, 1) != ' ') { + break; + } + $array[] = substr($diff[$i++], 2); + } + + while ($j < $max_j && ($max_i-$i) <= 1) { + if ($diff[$j] != '' && substr($diff[$j], 0, 1) != ' ') { + break; + } + $array[] = substr($diff[$j++], 2); + } + if (count($array) > 0) { + $edits[] = new Text_Diff_Op_copy($array); + } + + if ($i < $max_i) { + $diff1 = array(); + switch (substr($diff[$i], 0, 1)) { + case '!': + $diff2 = array(); + do { + $diff1[] = substr($diff[$i], 2); + if ($j < $max_j && substr($diff[$j], 0, 1) == '!') { + $diff2[] = substr($diff[$j++], 2); + } + } while (++$i < $max_i && substr($diff[$i], 0, 1) == '!'); + $edits[] = new Text_Diff_Op_change($diff1, $diff2); + break; + + case '+': + do { + $diff1[] = substr($diff[$i], 2); + } while (++$i < $max_i && substr($diff[$i], 0, 1) == '+'); + $edits[] = new Text_Diff_Op_add($diff1); + break; + + case '-': + do { + $diff1[] = substr($diff[$i], 2); + } while (++$i < $max_i && substr($diff[$i], 0, 1) == '-'); + $edits[] = new Text_Diff_Op_delete($diff1); + break; + } + } + + if ($j < $max_j) { + $diff2 = array(); + switch (substr($diff[$j], 0, 1)) { + case '+': + do { + $diff2[] = substr($diff[$j++], 2); + } while ($j < $max_j && substr($diff[$j], 0, 1) == '+'); + $edits[] = new Text_Diff_Op_add($diff2); + break; + + case '-': + do { + $diff2[] = substr($diff[$j++], 2); + } while ($j < $max_j && substr($diff[$j], 0, 1) == '-'); + $edits[] = new Text_Diff_Op_delete($diff2); + break; + } + } + } + + return $edits; + } + +} diff --git a/includes/pear/Text/Diff/Engine/xdiff.php b/includes/pear/Text/Diff/Engine/xdiff.php new file mode 100644 index 0000000..241d2e5 --- /dev/null +++ b/includes/pear/Text/Diff/Engine/xdiff.php @@ -0,0 +1,66 @@ + + * @package Text_Diff + */ +class Text_Diff_Engine_xdiff { + + /** + */ + function diff($from_lines, $to_lines) + { + array_walk($from_lines, array('Text_Diff', 'trimNewlines')); + array_walk($to_lines, array('Text_Diff', 'trimNewlines')); + + /* Convert the two input arrays into strings for xdiff processing. */ + $from_string = implode("\n", $from_lines); + $to_string = implode("\n", $to_lines); + + /* Diff the two strings and convert the result to an array. */ + $diff = xdiff_string_diff($from_string, $to_string, count($to_lines)); + $diff = explode("\n", $diff); + + /* Walk through the diff one line at a time. We build the $edits + * array of diff operations by reading the first character of the + * xdiff output (which is in the "unified diff" format). + * + * Note that we don't have enough information to detect "changed" + * lines using this approach, so we can't add Text_Diff_Op_changed + * instances to the $edits array. The result is still perfectly + * valid, albeit a little less descriptive and efficient. */ + $edits = array(); + foreach ($diff as $line) { + if (!strlen($line)) { + continue; + } + switch ($line[0]) { + case ' ': + $edits[] = &new Text_Diff_Op_copy(array(substr($line, 1))); + break; + + case '+': + $edits[] = &new Text_Diff_Op_add(array(substr($line, 1))); + break; + + case '-': + $edits[] = &new Text_Diff_Op_delete(array(substr($line, 1))); + break; + } + } + + return $edits; + } + +} diff --git a/includes/pear/Text/Diff/Mapped.php b/includes/pear/Text/Diff/Mapped.php new file mode 100644 index 0000000..dc46e5e --- /dev/null +++ b/includes/pear/Text/Diff/Mapped.php @@ -0,0 +1,55 @@ + + */ +class Text_Diff_Mapped extends Text_Diff { + + /** + * Computes a diff between sequences of strings. + * + * This can be used to compute things like case-insensitve diffs, or diffs + * which ignore changes in white-space. + * + * @param array $from_lines An array of strings. + * @param array $to_lines An array of strings. + * @param array $mapped_from_lines This array should have the same size + * number of elements as $from_lines. The + * elements in $mapped_from_lines and + * $mapped_to_lines are what is actually + * compared when computing the diff. + * @param array $mapped_to_lines This array should have the same number + * of elements as $to_lines. + */ + function Text_Diff_Mapped($from_lines, $to_lines, + $mapped_from_lines, $mapped_to_lines) + { + assert(count($from_lines) == count($mapped_from_lines)); + assert(count($to_lines) == count($mapped_to_lines)); + + parent::Text_Diff($mapped_from_lines, $mapped_to_lines); + + $xi = $yi = 0; + for ($i = 0; $i < count($this->_edits); $i++) { + $orig = &$this->_edits[$i]->orig; + if (is_array($orig)) { + $orig = array_slice($from_lines, $xi, count($orig)); + $xi += count($orig); + } + + $final = &$this->_edits[$i]->final; + if (is_array($final)) { + $final = array_slice($to_lines, $yi, count($final)); + $yi += count($final); + } + } + } + +} diff --git a/includes/pear/Text/Diff/Renderer.php b/includes/pear/Text/Diff/Renderer.php new file mode 100644 index 0000000..3a51650 --- /dev/null +++ b/includes/pear/Text/Diff/Renderer.php @@ -0,0 +1,237 @@ + $value) { + $v = '_' . $param; + if (isset($this->$v)) { + $this->$v = $value; + } + } + } + + /** + * Get any renderer parameters. + * + * @return array All parameters of this renderer object. + */ + function getParams() + { + $params = array(); + foreach (get_object_vars($this) as $k => $v) { + if ($k[0] == '_') { + $params[substr($k, 1)] = $v; + } + } + + return $params; + } + + /** + * Renders a diff. + * + * @param Text_Diff $diff A Text_Diff object. + * + * @return string The formatted output. + */ + function render($diff) + { + $xi = $yi = 1; + $block = false; + $context = array(); + + $nlead = $this->_leading_context_lines; + $ntrail = $this->_trailing_context_lines; + + $output = $this->_startDiff(); + + $diffs = $diff->getDiff(); + foreach ($diffs as $i => $edit) { + /* If these are unchanged (copied) lines, and we want to keep + * leading or trailing context lines, extract them from the copy + * block. */ + if (is_a($edit, 'Text_Diff_Op_copy')) { + /* Do we have any diff blocks yet? */ + if (is_array($block)) { + /* How many lines to keep as context from the copy + * block. */ + $keep = $i == count($diffs) - 1 ? $ntrail : $nlead + $ntrail; + if (count($edit->orig) <= $keep) { + /* We have less lines in the block than we want for + * context => keep the whole block. */ + $block[] = $edit; + } else { + if ($ntrail) { + /* Create a new block with as many lines as we need + * for the trailing context. */ + $context = array_slice($edit->orig, 0, $ntrail); + $block[] = new Text_Diff_Op_copy($context); + } + /* @todo */ + $output .= $this->_block($x0, $ntrail + $xi - $x0, + $y0, $ntrail + $yi - $y0, + $block); + $block = false; + } + } + /* Keep the copy block as the context for the next block. */ + $context = $edit->orig; + } else { + /* Don't we have any diff blocks yet? */ + if (!is_array($block)) { + /* Extract context lines from the preceding copy block. */ + $context = array_slice($context, count($context) - $nlead); + $x0 = $xi - count($context); + $y0 = $yi - count($context); + $block = array(); + if ($context) { + $block[] = new Text_Diff_Op_copy($context); + } + } + $block[] = $edit; + } + + if ($edit->orig) { + $xi += count($edit->orig); + } + if ($edit->final) { + $yi += count($edit->final); + } + } + + if (is_array($block)) { + $output .= $this->_block($x0, $xi - $x0, + $y0, $yi - $y0, + $block); + } + + return $output . $this->_endDiff(); + } + + function _block($xbeg, $xlen, $ybeg, $ylen, &$edits) + { + $output = $this->_startBlock($this->_blockHeader($xbeg, $xlen, $ybeg, $ylen)); + + foreach ($edits as $edit) { + switch (strtolower(get_class($edit))) { + case 'text_diff_op_copy': + $output .= $this->_context($edit->orig); + break; + + case 'text_diff_op_add': + $output .= $this->_added($edit->final); + break; + + case 'text_diff_op_delete': + $output .= $this->_deleted($edit->orig); + break; + + case 'text_diff_op_change': + $output .= $this->_changed($edit->orig, $edit->final); + break; + } + } + + return $output . $this->_endBlock(); + } + + function _startDiff() + { + return ''; + } + + function _endDiff() + { + return ''; + } + + function _blockHeader($xbeg, $xlen, $ybeg, $ylen) + { + if ($xlen > 1) { + $xbeg .= ',' . ($xbeg + $xlen - 1); + } + if ($ylen > 1) { + $ybeg .= ',' . ($ybeg + $ylen - 1); + } + + // this matches the GNU Diff behaviour + if ($xlen && !$ylen) { + $ybeg--; + } elseif (!$xlen) { + $xbeg--; + } + + return $xbeg . ($xlen ? ($ylen ? 'c' : 'd') : 'a') . $ybeg; + } + + function _startBlock($header) + { + return $header . "\n"; + } + + function _endBlock() + { + return ''; + } + + function _lines($lines, $prefix = ' ') + { + return $prefix . implode("\n$prefix", $lines) . "\n"; + } + + function _context($lines) + { + return $this->_lines($lines, ' '); + } + + function _added($lines) + { + return $this->_lines($lines, '> '); + } + + function _deleted($lines) + { + return $this->_lines($lines, '< '); + } + + function _changed($orig, $final) + { + return $this->_deleted($orig) . "---\n" . $this->_added($final); + } + +} diff --git a/includes/pear/Text/Diff/Renderer/context.php b/includes/pear/Text/Diff/Renderer/context.php new file mode 100644 index 0000000..af53801 --- /dev/null +++ b/includes/pear/Text/Diff/Renderer/context.php @@ -0,0 +1,77 @@ +_second_block = "--- $ybeg ----\n"; + return "***************\n*** $xbeg ****"; + } + + function _endBlock() + { + return $this->_second_block; + } + + function _context($lines) + { + $this->_second_block .= $this->_lines($lines, ' '); + return $this->_lines($lines, ' '); + } + + function _added($lines) + { + $this->_second_block .= $this->_lines($lines, '+ '); + return ''; + } + + function _deleted($lines) + { + return $this->_lines($lines, '- '); + } + + function _changed($orig, $final) + { + $this->_second_block .= $this->_lines($final, '! '); + return $this->_lines($orig, '! '); + } + +} diff --git a/includes/pear/Text/Diff/Renderer/inline.php b/includes/pear/Text/Diff/Renderer/inline.php new file mode 100644 index 0000000..5dd20d2 --- /dev/null +++ b/includes/pear/Text/Diff/Renderer/inline.php @@ -0,0 +1,172 @@ +'; + + /** + * Suffix for inserted text. + */ + var $_ins_suffix = ''; + + /** + * Prefix for deleted text. + */ + var $_del_prefix = ''; + + /** + * Suffix for deleted text. + */ + var $_del_suffix = ''; + + /** + * Header for each change block. + */ + var $_block_header = ''; + + /** + * What are we currently splitting on? Used to recurse to show word-level + * changes. + */ + var $_split_level = 'lines'; + + function _blockHeader($xbeg, $xlen, $ybeg, $ylen) + { + return $this->_block_header; + } + + function _startBlock($header) + { + return $header; + } + + function _lines($lines, $prefix = ' ', $encode = true) + { + if ($encode) { + array_walk($lines, array(&$this, '_encode')); + } + + if ($this->_split_level == 'words') { + return implode('', $lines); + } else { + return implode("\n", $lines) . "\n"; + } + } + + function _added($lines) + { + array_walk($lines, array(&$this, '_encode')); + $lines[0] = $this->_ins_prefix . $lines[0]; + $lines[count($lines) - 1] .= $this->_ins_suffix; + return $this->_lines($lines, ' ', false); + } + + function _deleted($lines, $words = false) + { + array_walk($lines, array(&$this, '_encode')); + $lines[0] = $this->_del_prefix . $lines[0]; + $lines[count($lines) - 1] .= $this->_del_suffix; + return $this->_lines($lines, ' ', false); + } + + function _changed($orig, $final) + { + /* If we've already split on words, don't try to do so again - just + * display. */ + if ($this->_split_level == 'words') { + $prefix = ''; + while ($orig[0] !== false && $final[0] !== false && + substr($orig[0], 0, 1) == ' ' && + substr($final[0], 0, 1) == ' ') { + $prefix .= substr($orig[0], 0, 1); + $orig[0] = substr($orig[0], 1); + $final[0] = substr($final[0], 1); + } + return $prefix . $this->_deleted($orig) . $this->_added($final); + } + + $text1 = implode("\n", $orig); + $text2 = implode("\n", $final); + + /* Non-printing newline marker. */ + $nl = "\0"; + + /* We want to split on word boundaries, but we need to + * preserve whitespace as well. Therefore we split on words, + * but include all blocks of whitespace in the wordlist. */ + $diff = new Text_Diff('native', + array($this->_splitOnWords($text1, $nl), + $this->_splitOnWords($text2, $nl))); + + /* Get the diff in inline format. */ + $renderer = new Text_Diff_Renderer_inline + (array_merge($this->getParams(), + array('split_level' => 'words'))); + + /* Run the diff and get the output. */ + return str_replace($nl, "\n", $renderer->render($diff)) . "\n"; + } + + function _splitOnWords($string, $newlineEscape = "\n") + { + // Ignore \0; otherwise the while loop will never finish. + $string = str_replace("\0", '', $string); + + $words = array(); + $length = strlen($string); + $pos = 0; + + while ($pos < $length) { + // Eat a word with any preceding whitespace. + $spaces = strspn(substr($string, $pos), " \n"); + $nextpos = strcspn(substr($string, $pos + $spaces), " \n"); + $words[] = str_replace("\n", $newlineEscape, substr($string, $pos, $spaces + $nextpos)); + $pos += $spaces + $nextpos; + } + + return $words; + } + + function _encode(&$string) + { + $string = htmlspecialchars($string); + } + +} diff --git a/includes/pear/Text/Diff/Renderer/unified.php b/includes/pear/Text/Diff/Renderer/unified.php new file mode 100644 index 0000000..f990f72 --- /dev/null +++ b/includes/pear/Text/Diff/Renderer/unified.php @@ -0,0 +1,67 @@ +_lines($lines, ' '); + } + + function _added($lines) + { + return $this->_lines($lines, '+'); + } + + function _deleted($lines) + { + return $this->_lines($lines, '-'); + } + + function _changed($orig, $final) + { + return $this->_deleted($orig) . $this->_added($final); + } + +} diff --git a/includes/pear/Text/Diff/ThreeWay.php b/includes/pear/Text/Diff/ThreeWay.php new file mode 100644 index 0000000..5b0357c --- /dev/null +++ b/includes/pear/Text/Diff/ThreeWay.php @@ -0,0 +1,276 @@ + + */ +class Text_Diff_ThreeWay extends Text_Diff { + + /** + * Conflict counter. + * + * @var integer + */ + var $_conflictingBlocks = 0; + + /** + * Computes diff between 3 sequences of strings. + * + * @param array $orig The original lines to use. + * @param array $final1 The first version to compare to. + * @param array $final2 The second version to compare to. + */ + function Text_Diff_ThreeWay($orig, $final1, $final2) + { + if (extension_loaded('xdiff')) { + $engine = new Text_Diff_Engine_xdiff(); + } else { + $engine = new Text_Diff_Engine_native(); + } + + $this->_edits = $this->_diff3($engine->diff($orig, $final1), + $engine->diff($orig, $final2)); + } + + /** + */ + function mergedOutput($label1 = false, $label2 = false) + { + $lines = array(); + foreach ($this->_edits as $edit) { + if ($edit->isConflict()) { + /* FIXME: this should probably be moved somewhere else. */ + $lines = array_merge($lines, + array('<<<<<<<' . ($label1 ? ' ' . $label1 : '')), + $edit->final1, + array("======="), + $edit->final2, + array('>>>>>>>' . ($label2 ? ' ' . $label2 : ''))); + $this->_conflictingBlocks++; + } else { + $lines = array_merge($lines, $edit->merged()); + } + } + + return $lines; + } + + /** + * @access private + */ + function _diff3($edits1, $edits2) + { + $edits = array(); + $bb = new Text_Diff_ThreeWay_BlockBuilder(); + + $e1 = current($edits1); + $e2 = current($edits2); + while ($e1 || $e2) { + if ($e1 && $e2 && is_a($e1, 'Text_Diff_Op_copy') && is_a($e2, 'Text_Diff_Op_copy')) { + /* We have copy blocks from both diffs. This is the (only) + * time we want to emit a diff3 copy block. Flush current + * diff3 diff block, if any. */ + if ($edit = $bb->finish()) { + $edits[] = $edit; + } + + $ncopy = min($e1->norig(), $e2->norig()); + assert($ncopy > 0); + $edits[] = new Text_Diff_ThreeWay_Op_copy(array_slice($e1->orig, 0, $ncopy)); + + if ($e1->norig() > $ncopy) { + array_splice($e1->orig, 0, $ncopy); + array_splice($e1->final, 0, $ncopy); + } else { + $e1 = next($edits1); + } + + if ($e2->norig() > $ncopy) { + array_splice($e2->orig, 0, $ncopy); + array_splice($e2->final, 0, $ncopy); + } else { + $e2 = next($edits2); + } + } else { + if ($e1 && $e2) { + if ($e1->orig && $e2->orig) { + $norig = min($e1->norig(), $e2->norig()); + $orig = array_splice($e1->orig, 0, $norig); + array_splice($e2->orig, 0, $norig); + $bb->input($orig); + } + + if (is_a($e1, 'Text_Diff_Op_copy')) { + $bb->out1(array_splice($e1->final, 0, $norig)); + } + + if (is_a($e2, 'Text_Diff_Op_copy')) { + $bb->out2(array_splice($e2->final, 0, $norig)); + } + } + + if ($e1 && ! $e1->orig) { + $bb->out1($e1->final); + $e1 = next($edits1); + } + if ($e2 && ! $e2->orig) { + $bb->out2($e2->final); + $e2 = next($edits2); + } + } + } + + if ($edit = $bb->finish()) { + $edits[] = $edit; + } + + return $edits; + } + +} + +/** + * @package Text_Diff + * @author Geoffrey T. Dairiki + * + * @access private + */ +class Text_Diff_ThreeWay_Op { + + function Text_Diff_ThreeWay_Op($orig = false, $final1 = false, $final2 = false) + { + $this->orig = $orig ? $orig : array(); + $this->final1 = $final1 ? $final1 : array(); + $this->final2 = $final2 ? $final2 : array(); + } + + function merged() + { + if (!isset($this->_merged)) { + if ($this->final1 === $this->final2) { + $this->_merged = &$this->final1; + } elseif ($this->final1 === $this->orig) { + $this->_merged = &$this->final2; + } elseif ($this->final2 === $this->orig) { + $this->_merged = &$this->final1; + } else { + $this->_merged = false; + } + } + + return $this->_merged; + } + + function isConflict() + { + return $this->merged() === false; + } + +} + +/** + * @package Text_Diff + * @author Geoffrey T. Dairiki + * + * @access private + */ +class Text_Diff_ThreeWay_Op_copy extends Text_Diff_ThreeWay_Op { + + function Text_Diff_ThreeWay_Op_Copy($lines = false) + { + $this->orig = $lines ? $lines : array(); + $this->final1 = &$this->orig; + $this->final2 = &$this->orig; + } + + function merged() + { + return $this->orig; + } + + function isConflict() + { + return false; + } + +} + +/** + * @package Text_Diff + * @author Geoffrey T. Dairiki + * + * @access private + */ +class Text_Diff_ThreeWay_BlockBuilder { + + function Text_Diff_ThreeWay_BlockBuilder() + { + $this->_init(); + } + + function input($lines) + { + if ($lines) { + $this->_append($this->orig, $lines); + } + } + + function out1($lines) + { + if ($lines) { + $this->_append($this->final1, $lines); + } + } + + function out2($lines) + { + if ($lines) { + $this->_append($this->final2, $lines); + } + } + + function isEmpty() + { + return !$this->orig && !$this->final1 && !$this->final2; + } + + function finish() + { + if ($this->isEmpty()) { + return false; + } else { + $edit = new Text_Diff_ThreeWay_Op($this->orig, $this->final1, $this->final2); + $this->_init(); + return $edit; + } + } + + function _init() + { + $this->orig = $this->final1 = $this->final2 = array(); + } + + function _append(&$array, $lines) + { + array_splice($array, sizeof($array), 0, $lines); + } + +} diff --git a/includes/pear/Text/Diff3.php b/includes/pear/Text/Diff3.php new file mode 100644 index 0000000..e9aea9f --- /dev/null +++ b/includes/pear/Text/Diff3.php @@ -0,0 +1,276 @@ + + */ +class Text_Diff3 extends Text_Diff { + + /** + * Conflict counter. + * + * @var integer + */ + var $_conflictingBlocks = 0; + + /** + * Computes diff between 3 sequences of strings. + * + * @param array $orig The original lines to use. + * @param array $final1 The first version to compare to. + * @param array $final2 The second version to compare to. + */ + function Text_Diff3($orig, $final1, $final2) + { + if (extension_loaded('xdiff')) { + $engine = new Text_Diff_Engine_xdiff(); + } else { + $engine = new Text_Diff_Engine_native(); + } + + $this->_edits = $this->_diff3($engine->diff($orig, $final1), + $engine->diff($orig, $final2)); + } + + /** + */ + function mergedOutput($label1 = false, $label2 = false) + { + $lines = array(); + foreach ($this->_edits as $edit) { + if ($edit->isConflict()) { + /* FIXME: this should probably be moved somewhere else. */ + $lines = array_merge($lines, + array('<<<<<<<' . ($label1 ? ' ' . $label1 : '')), + $edit->final1, + array("======="), + $edit->final2, + array('>>>>>>>' . ($label2 ? ' ' . $label2 : ''))); + $this->_conflictingBlocks++; + } else { + $lines = array_merge($lines, $edit->merged()); + } + } + + return $lines; + } + + /** + * @access private + */ + function _diff3($edits1, $edits2) + { + $edits = array(); + $bb = new Text_Diff3_BlockBuilder(); + + $e1 = current($edits1); + $e2 = current($edits2); + while ($e1 || $e2) { + if ($e1 && $e2 && is_a($e1, 'Text_Diff_Op_copy') && is_a($e2, 'Text_Diff_Op_copy')) { + /* We have copy blocks from both diffs. This is the (only) + * time we want to emit a diff3 copy block. Flush current + * diff3 diff block, if any. */ + if ($edit = $bb->finish()) { + $edits[] = $edit; + } + + $ncopy = min($e1->norig(), $e2->norig()); + assert($ncopy > 0); + $edits[] = new Text_Diff3_Op_copy(array_slice($e1->orig, 0, $ncopy)); + + if ($e1->norig() > $ncopy) { + array_splice($e1->orig, 0, $ncopy); + array_splice($e1->final, 0, $ncopy); + } else { + $e1 = next($edits1); + } + + if ($e2->norig() > $ncopy) { + array_splice($e2->orig, 0, $ncopy); + array_splice($e2->final, 0, $ncopy); + } else { + $e2 = next($edits2); + } + } else { + if ($e1 && $e2) { + if ($e1->orig && $e2->orig) { + $norig = min($e1->norig(), $e2->norig()); + $orig = array_splice($e1->orig, 0, $norig); + array_splice($e2->orig, 0, $norig); + $bb->input($orig); + } + + if (is_a($e1, 'Text_Diff_Op_copy')) { + $bb->out1(array_splice($e1->final, 0, $norig)); + } + + if (is_a($e2, 'Text_Diff_Op_copy')) { + $bb->out2(array_splice($e2->final, 0, $norig)); + } + } + + if ($e1 && ! $e1->orig) { + $bb->out1($e1->final); + $e1 = next($edits1); + } + if ($e2 && ! $e2->orig) { + $bb->out2($e2->final); + $e2 = next($edits2); + } + } + } + + if ($edit = $bb->finish()) { + $edits[] = $edit; + } + + return $edits; + } + +} + +/** + * @package Text_Diff + * @author Geoffrey T. Dairiki + * + * @access private + */ +class Text_Diff3_Op { + + function Text_Diff3_Op($orig = false, $final1 = false, $final2 = false) + { + $this->orig = $orig ? $orig : array(); + $this->final1 = $final1 ? $final1 : array(); + $this->final2 = $final2 ? $final2 : array(); + } + + function merged() + { + if (!isset($this->_merged)) { + if ($this->final1 === $this->final2) { + $this->_merged = &$this->final1; + } elseif ($this->final1 === $this->orig) { + $this->_merged = &$this->final2; + } elseif ($this->final2 === $this->orig) { + $this->_merged = &$this->final1; + } else { + $this->_merged = false; + } + } + + return $this->_merged; + } + + function isConflict() + { + return $this->merged() === false; + } + +} + +/** + * @package Text_Diff + * @author Geoffrey T. Dairiki + * + * @access private + */ +class Text_Diff3_Op_copy extends Text_Diff3_Op { + + function Text_Diff3_Op_Copy($lines = false) + { + $this->orig = $lines ? $lines : array(); + $this->final1 = &$this->orig; + $this->final2 = &$this->orig; + } + + function merged() + { + return $this->orig; + } + + function isConflict() + { + return false; + } + +} + +/** + * @package Text_Diff + * @author Geoffrey T. Dairiki + * + * @access private + */ +class Text_Diff3_BlockBuilder { + + function Text_Diff3_BlockBuilder() + { + $this->_init(); + } + + function input($lines) + { + if ($lines) { + $this->_append($this->orig, $lines); + } + } + + function out1($lines) + { + if ($lines) { + $this->_append($this->final1, $lines); + } + } + + function out2($lines) + { + if ($lines) { + $this->_append($this->final2, $lines); + } + } + + function isEmpty() + { + return !$this->orig && !$this->final1 && !$this->final2; + } + + function finish() + { + if ($this->isEmpty()) { + return false; + } else { + $edit = new Text_Diff3_Op($this->orig, $this->final1, $this->final2); + $this->_init(); + return $edit; + } + } + + function _init() + { + $this->orig = $this->final1 = $this->final2 = array(); + } + + function _append(&$array, $lines) + { + array_splice($array, sizeof($array), 0, $lines); + } + +} diff --git a/includes/pear/Text/Wiki.php b/includes/pear/Text/Wiki.php new file mode 100644 index 0000000..27a9161 --- /dev/null +++ b/includes/pear/Text/Wiki.php @@ -0,0 +1,1550 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Wiki.php 248433 2007-12-17 16:03:48Z justinpatrin $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * The baseline abstract parser class. + */ +require_once 'Text/Wiki/Parse.php'; + +/** + * The baseline abstract render class. + */ +require_once 'Text/Wiki/Render.php'; + +/** + * Parse structured wiki text and render into arbitrary formats such as XHTML. + * + * This is the "master" class for handling the management and convenience + * functions to transform Wiki-formatted text. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: 1.2.1 + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki { + + /** + * + * The default list of rules, in order, to apply to the source text. + * + * @access public + * + * @var array + * + */ + + var $rules = array( + 'Prefilter', + 'Delimiter', + 'Code', + 'Function', + 'Html', + 'Raw', + 'Include', + 'Embed', + 'Anchor', + 'Heading', + 'Toc', + 'Horiz', + 'Break', + 'Blockquote', + 'List', + 'Deflist', + 'Table', + 'Image', + 'Phplookup', + 'Center', + 'Newline', + 'Paragraph', + 'Url', + 'Freelink', + 'Interwiki', + 'Wikilink', + 'Colortext', + 'Strong', + 'Bold', + 'Emphasis', + 'Italic', + 'Underline', + 'Tt', + 'Superscript', + 'Subscript', + 'Revise', + 'Tighten' + ); + + + /** + * + * The list of rules to not-apply to the source text. + * + * @access public + * + * @var array + * + */ + + var $disable = array( + 'Html', + 'Include', + 'Embed' + ); + + + /** + * + * Custom configuration for rules at the parsing stage. + * + * In this array, the key is the parsing rule name, and the value is + * an array of key-value configuration pairs corresponding to the $conf + * property in the target parsing rule. + * + * For example: + * + * + * $parseConf = array( + * 'Include' => array( + * 'base' => '/path/to/scripts/' + * ) + * ); + * + * + * Note that most default rules do not need any parsing configuration. + * + * @access public + * + * @var array + * + */ + + var $parseConf = array(); + + + /** + * + * Custom configuration for rules at the rendering stage. + * + * Because rendering may be different for each target format, the + * first-level element in this array is always a format name (e.g., + * 'Xhtml'). + * + * Within that first level element, the subsequent elements match the + * $parseConf format. That is, the sub-key is the rendering rule name, + * and the sub-value is an array of key-value configuration pairs + * corresponding to the $conf property in the target rendering rule. + * + * @access public + * + * @var array + * + */ + + var $renderConf = array( + 'Docbook' => array(), + 'Latex' => array(), + 'Pdf' => array(), + 'Plain' => array(), + 'Rtf' => array(), + 'Xhtml' => array() + ); + + + /** + * + * Custom configuration for the output format itself. + * + * Even though Text_Wiki will render the tokens from parsed text, + * the format itself may require some configuration. For example, + * RTF needs to know font names and sizes, PDF requires page layout + * information, and DocBook needs a section hierarchy. This array + * matches the $conf property of the the format-level renderer + * (e.g., Text_Wiki_Render_Xhtml). + * + * In this array, the key is the rendering format name, and the value is + * an array of key-value configuration pairs corresponding to the $conf + * property in the rendering format rule. + * + * @access public + * + * @var array + * + */ + + var $formatConf = array( + 'Docbook' => array(), + 'Latex' => array(), + 'Pdf' => array(), + 'Plain' => array(), + 'Rtf' => array(), + 'Xhtml' => array() + ); + + + /** + * + * The delimiter for token numbers of parsed elements in source text. + * + * @access public + * + * @var string + * + */ + + var $delim = "\31"; + + + /** + * + * The tokens generated by rules as the source text is parsed. + * + * As Text_Wiki applies rule classes to the source text, it will + * replace portions of the text with a delimited token number. This + * is the array of those tokens, representing the replaced text and + * any options set by the parser for that replaced text. + * + * The tokens array is sequential; each element is itself a sequential + * array where element 0 is the name of the rule that generated the + * token, and element 1 is an associative array where the key is an + * option name and the value is an option value. + * + * @access private + * + * @var array + * + */ + + var $tokens = array(); + + /** + * How many tokens generated pro rules. + * + * Intended to load only necessary render objects + * + * @access private + * @var array + */ + var $_countRulesTokens = array(); + + + /** + * + * The source text to which rules will be applied. + * + * This text will be transformed in-place, which means that it will + * change as the rules are applied. + * + * @access private + * + * @var string + * + */ + + var $source = ''; + + /** + * The output text + * + * @var string + */ + var $output = ''; + + + /** + * + * Array of rule parsers. + * + * Text_Wiki creates one instance of every rule that is applied to + * the source text; this array holds those instances. The array key + * is the rule name, and the array value is an instance of the rule + * class. + * + * @access private + * + * @var array + * + */ + + var $parseObj = array(); + + + /** + * + * Array of rule renderers. + * + * Text_Wiki creates one instance of every rule that is applied to + * the source text; this array holds those instances. The array key + * is the rule name, and the array value is an instance of the rule + * class. + * + * @access private + * + * @var array + * + */ + + var $renderObj = array(); + + + /** + * + * Array of format renderers. + * + * @access private + * + * @var array + * + */ + + var $formatObj = array(); + + + /** + * + * Array of paths to search, in order, for parsing and rendering rules. + * + * @access private + * + * @var array + * + */ + + var $path = array( + 'parse' => array(), + 'render' => array() + ); + + + + /** + * + * The directory separator character. + * + * @access private + * + * @var string + * + */ + + var $_dirSep = DIRECTORY_SEPARATOR; + + /** + * Temporary configuration variable + * + * @var string + */ + var $renderingType = 'normal'; + + /** + * Stack of rendering callbacks + * + * @var Array + */ + var $_renderCallbacks = array(); + + /** + * Current output block + * + * @var string + */ + var $_block; + + /** + * A stack of blocks + * + * @param Array + */ + var $_blocks; + + /** + * + * Constructor. + * + * **DEPRECATED** + * Please use the singleton() or factory() methods. + * + * @access public + * + * @param array $rules The set of rules to load for this object. Defaults + * to null, which will load the default ruleset for this parser. + */ + + function Text_Wiki($rules = null) + { + if (is_array($rules)) { + $this->rules = array(); + foreach ($rules as $rule) { + $this->rules[] = ucfirst($rule); + } + } + + $this->addPath( + 'parse', + $this->fixPath(dirname(__FILE__)) . 'Wiki/Parse/Default/' + ); + $this->addPath( + 'render', + $this->fixPath(dirname(__FILE__)) . 'Wiki/Render/' + ); + + } + + /** + * Singleton. + * + * This avoids instantiating multiple Text_Wiki instances where a number + * of objects are required in one call, e.g. to save memory in a + * CMS invironment where several parsers are required in a single page. + * + * $single = & singleton(); + * + * or + * + * $single = & singleton('Parser', array('Prefilter', 'Delimiter', 'Code', 'Function', + * 'Html', 'Raw', 'Include', 'Embed', 'Anchor', 'Heading', 'Toc', 'Horiz', + * 'Break', 'Blockquote', 'List', 'Deflist', 'Table', 'Image', 'Phplookup', + * 'Center', 'Newline', 'Paragraph', 'Url', 'Freelink', 'Interwiki', 'Wikilink', + * 'Colortext', 'Strong', 'Bold', 'Emphasis', 'Italic', 'Underline', 'Tt', + * 'Superscript', 'Subscript', 'Revise', 'Tighten')); + * + * Call using a subset of this list. The order of passing rulesets in the + * $rules array is important! + * + * After calling this, call $single->setParseConf(), setRenderConf() or setFormatConf() + * as usual for a constructed object of this class. + * + * The internal static array of singleton objects has no index on the parser + * rules, the only index is on the parser name. So if you call this multiple + * times with different rules but the same parser name, you will get the same + * static parser object each time. + * + * @access public + * @static + * @since Method available since Release 1.1.0 + * @param string $parser The parser to be used (defaults to 'Default'). + * @param array $rules The set of rules to instantiate the object. This + * will only be used when the first call to singleton is made, if included + * in further calls it will be effectively ignored. + * @return &object a reference to the Text_Wiki unique instantiation. + */ + function &singleton($parser = 'Default', $rules = null) + { + static $only = array(); + if (!isset($only[$parser])) { + $ret = & Text_Wiki::factory($parser, $rules); + if (Text_Wiki::isError($ret)) { + return $ret; + } + $only[$parser] =& $ret; + } + return $only[$parser]; + } + + /** + * Returns a Text_Wiki Parser class for the specified parser. + * + * @access public + * @static + * @param string $parser The name of the parse to instantiate + * you need to have Text_Wiki_XXX installed to use $parser = 'XXX', it's E_FATAL + * @param array $rules The rules to pass into the constructor + * {@see Text_Wiki::singleton} for a list of rules + * @return Text_Wiki a Parser object extended from Text_Wiki + */ + function &factory($parser = 'Default', $rules = null) + { + $class = 'Text_Wiki_' . $parser; + $file = str_replace('_', '/', $class).'.php'; + if (!class_exists($class)) { + require_once $file; + if (!class_exists($class)) { + return Text_Wiki::error( + 'Class ' . $class . ' does not exist after requiring '. $file . + ', install package ' . $class . "\n"); + } + } + + $obj =& new $class($rules); + return $obj; + } + + /** + * + * Set parser configuration for a specific rule and key. + * + * @access public + * + * @param string $rule The parse rule to set config for. + * + * @param array|string $arg1 The full config array to use for the + * parse rule, or a conf key in that array. + * + * @param string $arg2 The config value for the key. + * + * @return void + * + */ + + function setParseConf($rule, $arg1, $arg2 = null) + { + $rule = ucwords(strtolower($rule)); + + if (! isset($this->parseConf[$rule])) { + $this->parseConf[$rule] = array(); + } + + // if first arg is an array, use it as the entire + // conf array for the rule. otherwise, treat arg1 + // as a key and arg2 as a value for the rule conf. + if (is_array($arg1)) { + $this->parseConf[$rule] = $arg1; + } else { + $this->parseConf[$rule][$arg1] = $arg2; + } + } + + + /** + * + * Get parser configuration for a specific rule and key. + * + * @access public + * + * @param string $rule The parse rule to get config for. + * + * @param string $key A key in the conf array; if null, + * returns the entire conf array. + * + * @return mixed The whole conf array if no key is specified, + * or the specific conf key value. + * + */ + + function getParseConf($rule, $key = null) + { + $rule = ucwords(strtolower($rule)); + + // the rule does not exist + if (! isset($this->parseConf[$rule])) { + return null; + } + + // no key requested, return the whole array + if (is_null($key)) { + return $this->parseConf[$rule]; + } + + // does the requested key exist? + if (isset($this->parseConf[$rule][$key])) { + // yes, return that value + return $this->parseConf[$rule][$key]; + } else { + // no + return null; + } + } + + + /** + * + * Set renderer configuration for a specific format, rule, and key. + * + * @access public + * + * @param string $format The render format to set config for. + * + * @param string $rule The render rule to set config for in the format. + * + * @param array|string $arg1 The config array, or the config key + * within the render rule. + * + * @param string $arg2 The config value for the key. + * + * @return void + * + */ + + function setRenderConf($format, $rule, $arg1, $arg2 = null) + { + $format = ucwords(strtolower($format)); + $rule = ucwords(strtolower($rule)); + + if (! isset($this->renderConf[$format])) { + $this->renderConf[$format] = array(); + } + + if (! isset($this->renderConf[$format][$rule])) { + $this->renderConf[$format][$rule] = array(); + } + + // if first arg is an array, use it as the entire + // conf array for the render rule. otherwise, treat arg1 + // as a key and arg2 as a value for the render rule conf. + if (is_array($arg1)) { + $this->renderConf[$format][$rule] = $arg1; + } else { + $this->renderConf[$format][$rule][$arg1] = $arg2; + } + } + + + /** + * + * Get renderer configuration for a specific format, rule, and key. + * + * @access public + * + * @param string $format The render format to get config for. + * + * @param string $rule The render format rule to get config for. + * + * @param string $key A key in the conf array; if null, + * returns the entire conf array. + * + * @return mixed The whole conf array if no key is specified, + * or the specific conf key value. + * + */ + + function getRenderConf($format, $rule, $key = null) + { + $format = ucwords(strtolower($format)); + $rule = ucwords(strtolower($rule)); + + if (! isset($this->renderConf[$format]) || + ! isset($this->renderConf[$format][$rule])) { + return null; + } + + // no key requested, return the whole array + if (is_null($key)) { + return $this->renderConf[$format][$rule]; + } + + // does the requested key exist? + if (isset($this->renderConf[$format][$rule][$key])) { + // yes, return that value + return $this->renderConf[$format][$rule][$key]; + } else { + // no + return null; + } + + } + + /** + * + * Set format configuration for a specific rule and key. + * + * @access public + * + * @param string $format The format to set config for. + * + * @param string $key The config key within the format. + * + * @param string $val The config value for the key. + * + * @return void + * + */ + + function setFormatConf($format, $arg1, $arg2 = null) + { + if (! is_array($this->formatConf[$format])) { + $this->formatConf[$format] = array(); + } + + // if first arg is an array, use it as the entire + // conf array for the format. otherwise, treat arg1 + // as a key and arg2 as a value for the format conf. + if (is_array($arg1)) { + $this->formatConf[$format] = $arg1; + } else { + $this->formatConf[$format][$arg1] = $arg2; + } + } + + + + /** + * + * Get configuration for a specific format and key. + * + * @access public + * + * @param string $format The format to get config for. + * + * @param mixed $key A key in the conf array; if null, + * returns the entire conf array. + * + * @return mixed The whole conf array if no key is specified, + * or the specific conf key value. + * + */ + + function getFormatConf($format, $key = null) + { + // the format does not exist + if (! isset($this->formatConf[$format])) { + return null; + } + + // no key requested, return the whole array + if (is_null($key)) { + return $this->formatConf[$format]; + } + + // does the requested key exist? + if (isset($this->formatConf[$format][$key])) { + // yes, return that value + return $this->formatConf[$format][$key]; + } else { + // no + return null; + } + } + + + /** + * + * Inserts a rule into to the rule set. + * + * @access public + * + * @param string $name The name of the rule. Should be different from + * all other keys in the rule set. + * + * @param string $tgt The rule after which to insert this new rule. By + * default (null) the rule is inserted at the end; if set to '', inserts + * at the beginning. + * + * @return void + * + */ + + function insertRule($name, $tgt = null) + { + $name = ucwords(strtolower($name)); + if (! is_null($tgt)) { + $tgt = ucwords(strtolower($tgt)); + } + + // does the rule name to be inserted already exist? + if (in_array($name, $this->rules)) { + // yes, return + return null; + } + + // the target name is not null, and not '', but does not exist + // in the list of rules. this means we're trying to insert after + // a target key, but the target key isn't there. + if (! is_null($tgt) && $tgt != '' && + ! in_array($tgt, $this->rules)) { + return false; + } + + // if $tgt is null, insert at the end. We know this is at the + // end (instead of resetting an existing rule) becuase we exited + // at the top of this method if the rule was already in place. + if (is_null($tgt)) { + $this->rules[] = $name; + return true; + } + + // save a copy of the current rules, then reset the rule set + // so we can insert in the proper place later. + // where to insert the rule? + if ($tgt == '') { + // insert at the beginning + array_unshift($this->rules, $name); + return true; + } + + // insert after the named rule + $tmp = $this->rules; + $this->rules = array(); + + foreach ($tmp as $val) { + $this->rules[] = $val; + if ($val == $tgt) { + $this->rules[] = $name; + } + } + + return true; + + } + + + /** + * + * Delete (remove or unset) a rule from the $rules property. + * + * @access public + * + * @param string $rule The name of the rule to remove. + * + * @return void + * + */ + + function deleteRule($name) + { + $name = ucwords(strtolower($name)); + $key = array_search($name, $this->rules); + if ($key !== false) { + unset($this->rules[$key]); + } + } + + + /** + * + * Change from one rule to another in-place. + * + * @access public + * + * @param string $old The name of the rule to change from. + * + * @param string $new The name of the rule to change to. + * + * @return void + * + */ + + function changeRule($old, $new) + { + $old = ucwords(strtolower($old)); + $new = ucwords(strtolower($new)); + $key = array_search($old, $this->rules); + if ($key !== false) { + // delete the new name , case it was already there + $this->deleteRule($new); + $this->rules[$key] = $new; + } + } + + + /** + * + * Enables a rule so that it is applied when parsing. + * + * @access public + * + * @param string $rule The name of the rule to enable. + * + * @return void + * + */ + + function enableRule($name) + { + $name = ucwords(strtolower($name)); + $key = array_search($name, $this->disable); + if ($key !== false) { + unset($this->disable[$key]); + } + } + + + /** + * + * Disables a rule so that it is not applied when parsing. + * + * @access public + * + * @param string $rule The name of the rule to disable. + * + * @return void + * + */ + + function disableRule($name) + { + $name = ucwords(strtolower($name)); + $key = array_search($name, $this->disable); + if ($key === false) { + $this->disable[] = $name; + } + } + + + /** + * + * Parses and renders the text passed to it, and returns the results. + * + * First, the method parses the source text, applying rules to the + * text as it goes. These rules will modify the source text + * in-place, replacing some text with delimited tokens (and + * populating the $this->tokens array as it goes). + * + * Next, the method renders the in-place tokens into the requested + * output format. + * + * Finally, the method returns the transformed text. Note that the + * source text is transformed in place; once it is transformed, it is + * no longer the same as the original source text. + * + * @access public + * + * @param string $text The source text to which wiki rules should be + * applied, both for parsing and for rendering. + * + * @param string $format The target output format, typically 'xhtml'. + * If a rule does not support a given format, the output from that + * rule is rule-specific. + * + * @return string The transformed wiki text. + * + */ + + function transform($text, $format = 'Xhtml') + { + $this->parse($text); + return $this->render($format); + } + + + /** + * + * Sets the $_source text property, then parses it in place and + * retains tokens in the $_tokens array property. + * + * @access public + * + * @param string $text The source text to which wiki rules should be + * applied, both for parsing and for rendering. + * + * @return void + * + */ + + function parse($text) + { + // set the object property for the source text + $this->source = $text; + + // reset the tokens. + $this->tokens = array(); + $this->_countRulesTokens = array(); + + // apply the parse() method of each requested rule to the source + // text. + foreach ($this->rules as $name) { + // do not parse the rules listed in $disable + if (! in_array($name, $this->disable)) { + + // load the parsing object + $this->loadParseObj($name); + + // load may have failed; only parse if + // an object is in the array now + if (is_object($this->parseObj[$name])) { + $this->parseObj[$name]->parse(); + } + } + } + } + + + /** + * + * Renders tokens back into the source text, based on the requested format. + * + * @access public + * + * @param string $format The target output format, typically 'xhtml'. + * If a rule does not support a given format, the output from that + * rule is rule-specific. + * + * @return string The transformed wiki text. + * + */ + + function render($format = 'Xhtml') + { + // the rendering method we're going to use from each rule + $format = ucwords(strtolower($format)); + + // the eventual output text + $this->output = ''; + + // when passing through the parsed source text, keep track of when + // we are in a delimited section + $in_delim = false; + + // when in a delimited section, capture the token key number + $key = ''; + + // load the format object, or crap out if we can't find it + $result = $this->loadFormatObj($format); + if ($this->isError($result)) { + return $result; + } + + // pre-rendering activity + if (is_object($this->formatObj[$format])) { + $this->output .= $this->formatObj[$format]->pre(); + } + + // load the render objects + foreach (array_keys($this->_countRulesTokens) as $rule) { + $this->loadRenderObj($format, $rule); + } + + if ($this->renderingType == 'preg') { + $this->output = preg_replace_callback('/'.$this->delim.'(\d+)'.$this->delim.'/', + array(&$this, '_renderToken'), + $this->source); + /* +//Damn strtok()! Why does it "skip" empty parts of the string. It's useless now! + } elseif ($this->renderingType == 'strtok') { + echo '
    '.htmlentities($this->source).'
    '; + $t = strtok($this->source, $this->delim); + $inToken = true; + $i = 0; + while ($t !== false) { + echo 'Token: '.$i.'
    "'.htmlentities($t).'"


    '; + if ($inToken) { + //$this->output .= $this->renderObj[$this->tokens[$t][0]]->token($this->tokens[$t][1]); + } else { + $this->output .= $t; + } + $inToken = !$inToken; + $t = strtok($this->delim); + ++$i; + } + */ + } else { + // pass through the parsed source text character by character + $this->_block = ''; + $tokenStack = array(); + $k = strlen($this->source); + for ($i = 0; $i < $k; $i++) { + + // the current character + $char = $this->source{$i}; + + // are alredy in a delimited section? + if ($in_delim) { + + // yes; are we ending the section? + if ($char == $this->delim) { + + if (count($this->_renderCallbacks) == 0) { + $this->output .= $this->_block; + $this->_block = ''; + } + if (isset($opts['type'])) { + if ($opts['type'] == 'start') { + array_push($tokenStack, $rule); + } elseif ($opts['type'] == 'end') { + if ($tokenStack[count($tokenStack) - 1] != $rule) { + return Text_Wiki::error('Unbalanced tokens, check your syntax'); + } else { + array_pop($tokenStack); + } + } + } + + // yes, get the replacement text for the delimited + // token number and unset the flag. + $key = (int)$key; + $rule = $this->tokens[$key][0]; + $opts = $this->tokens[$key][1]; + $this->_block .= $this->renderObj[$rule]->token($opts); + $in_delim = false; + + } else { + + // no, add to the delimited token key number + $key .= $char; + + } + + } else { + + // not currently in a delimited section. + // are we starting into a delimited section? + if ($char == $this->delim) { + // yes, reset the previous key and + // set the flag. + $key = ''; + $in_delim = true; + + } else { + // no, add to the output as-is + $this->_block .= $char; + } + } + } + } + + if (count($this->_renderCallbacks)) { + return $this->error('Render callbacks left over after processing finished'); + } + /* + while (count($this->_renderCallbacks)) { + $this->popRenderCallback(); + } + */ + if (strlen($this->_block)) { + $this->output .= $this->_block; + $this->_block = ''; + } + + // post-rendering activity + if (is_object($this->formatObj[$format])) { + $this->output .= $this->formatObj[$format]->post(); + } + + // return the rendered source text. + return $this->output; + } + + /** + * Renders a token, for use only as an internal callback + * + * @param array Matches from preg_rpelace_callback, [1] is the token number + * @return string The rendered text for the token + * @access private + */ + function _renderToken($matches) { + return $this->renderObj[$this->tokens[$matches[1]][0]]->token($this->tokens[$matches[1]][1]); + } + + function registerRenderCallback($callback) { + $this->_blocks[] = $this->_block; + $this->_block = ''; + $this->_renderCallbacks[] = $callback; + } + + function popRenderCallback() { + if (count($this->_renderCallbacks) == 0) { + return Text_Wiki::error('Render callback popped when no render callbacks in stack'); + } else { + $callback = array_pop($this->_renderCallbacks); + $this->_block = call_user_func($callback, $this->_block); + if (count($this->_blocks)) { + $parentBlock = array_pop($this->_blocks); + $this->_block = $parentBlock.$this->_block; + } + if (count($this->_renderCallbacks) == 0) { + $this->output .= $this->_block; + $this->_block = ''; + } + } + } + + /** + * + * Returns the parsed source text with delimited token placeholders. + * + * @access public + * + * @return string The parsed source text. + * + */ + + function getSource() + { + return $this->source; + } + + + /** + * + * Returns tokens that have been parsed out of the source text. + * + * @access public + * + * @param array $rules If an array of rule names is passed, only return + * tokens matching these rule names. If no array is passed, return all + * tokens. + * + * @return array An array of tokens. + * + */ + + function getTokens($rules = null) + { + if (is_null($rules)) { + return $this->tokens; + } else { + settype($rules, 'array'); + $result = array(); + foreach ($this->tokens as $key => $val) { + if (in_array($val[0], $rules)) { + $result[$key] = $val; + } + } + return $result; + } + } + + + /** + * + * Add a token to the Text_Wiki tokens array, and return a delimited + * token number. + * + * @access public + * + * @param array $options An associative array of options for the new + * token array element. The keys and values are specific to the + * rule, and may or may not be common to other rule options. Typical + * options keys are 'text' and 'type' but may include others. + * + * @param boolean $id_only If true, return only the token number, not + * a delimited token string. + * + * @return string|int By default, return the number of the + * newly-created token array element with a delimiter prefix and + * suffix; however, if $id_only is set to true, return only the token + * number (no delimiters). + * + */ + + function addToken($rule, $options = array(), $id_only = false) + { + // increment the token ID number. note that if you parse + // multiple times with the same Text_Wiki object, the ID number + // will not reset to zero. + static $id; + if (! isset($id)) { + $id = 0; + } else { + $id ++; + } + + // force the options to be an array + settype($options, 'array'); + + // add the token + $this->tokens[$id] = array( + 0 => $rule, + 1 => $options + ); + if (!isset($this->_countRulesTokens[$rule])) { + $this->_countRulesTokens[$rule] = 1; + } else { + ++$this->_countRulesTokens[$rule]; + } + + // return a value + if ($id_only) { + // return the last token number + return $id; + } else { + // return the token number with delimiters + return $this->delim . $id . $this->delim; + } + } + + + /** + * + * Set or re-set a token with specific information, overwriting any + * previous rule name and rule options. + * + * @access public + * + * @param int $id The token number to reset. + * + * @param int $rule The rule name to use. + * + * @param array $options An associative array of options for the + * token array element. The keys and values are specific to the + * rule, and may or may not be common to other rule options. Typical + * options keys are 'text' and 'type' but may include others. + * + * @return void + * + */ + + function setToken($id, $rule, $options = array()) + { + $oldRule = $this->tokens[$id][0]; + // reset the token + $this->tokens[$id] = array( + 0 => $rule, + 1 => $options + ); + if ($rule != $oldRule) { + if (!($this->_countRulesTokens[$oldRule]--)) { + unset($this->_countRulesTokens[$oldRule]); + } + if (!isset($this->_countRulesTokens[$rule])) { + $this->_countRulesTokens[$rule] = 1; + } else { + ++$this->_countRulesTokens[$rule]; + } + } + } + + + /** + * + * Load a rule parser class file. + * + * @access public + * + * @return bool True if loaded, false if not. + * + */ + + function loadParseObj($rule) + { + $rule = ucwords(strtolower($rule)); + $file = $rule . '.php'; + $class = "Text_Wiki_Parse_$rule"; + + if (! class_exists($class)) { + $loc = $this->findFile('parse', $file); + if ($loc) { + // found the class + include_once $loc; + } else { + // can't find the class + $this->parseObj[$rule] = null; + // can't find the class + return $this->error( + "Parse rule '$rule' not found" + ); + } + } + + $this->parseObj[$rule] =& new $class($this); + + } + + + /** + * + * Load a rule-render class file. + * + * @access public + * + * @return bool True if loaded, false if not. + * + */ + + function loadRenderObj($format, $rule) + { + $format = ucwords(strtolower($format)); + $rule = ucwords(strtolower($rule)); + $file = "$format/$rule.php"; + $class = "Text_Wiki_Render_$format" . "_$rule"; + + if (! class_exists($class)) { + // load the class + $loc = $this->findFile('render', $file); + if ($loc) { + // found the class + include_once $loc; + } else { + // can't find the class + return $this->error( + "Render rule '$rule' in format '$format' not found" + ); + } + } + + $this->renderObj[$rule] =& new $class($this); + } + + + /** + * + * Load a format-render class file. + * + * @access public + * + * @return bool True if loaded, false if not. + * + */ + + function loadFormatObj($format) + { + $format = ucwords(strtolower($format)); + $file = $format . '.php'; + $class = "Text_Wiki_Render_$format"; + + if (! class_exists($class)) { + $loc = $this->findFile('render', $file); + if ($loc) { + // found the class + include_once $loc; + } else { + // can't find the class + return $this->error( + "Rendering format class '$class' not found" + ); + } + } + + $this->formatObj[$format] =& new $class($this); + } + + + /** + * + * Add a path to a path array. + * + * @access public + * + * @param string $type The path-type to add (parse or render). + * + * @param string $dir The directory to add to the path-type. + * + * @return void + * + */ + + function addPath($type, $dir) + { + $dir = $this->fixPath($dir); + if (! isset($this->path[$type])) { + $this->path[$type] = array($dir); + } else { + array_unshift($this->path[$type], $dir); + } + } + + + /** + * + * Get the current path array for a path-type. + * + * @access public + * + * @param string $type The path-type to look up (plugin, filter, or + * template). If not set, returns all path types. + * + * @return array The array of paths for the requested type. + * + */ + + function getPath($type = null) + { + if (is_null($type)) { + return $this->path; + } elseif (! isset($this->path[$type])) { + return array(); + } else { + return $this->path[$type]; + } + } + + + /** + * + * Searches a series of paths for a given file. + * + * @param array $type The type of paths to search (template, plugin, + * or filter). + * + * @param string $file The file name to look for. + * + * @return string|bool The full path and file name for the target file, + * or boolean false if the file is not found in any of the paths. + * + */ + + function findFile($type, $file) + { + // get the set of paths + $set = $this->getPath($type); + + // start looping through them + foreach ($set as $path) { + $fullname = $path . $file; + if (file_exists($fullname) && is_readable($fullname)) { + return $fullname; + } + } + + // could not find the file in the set of paths + return false; + } + + + /** + * + * Append a trailing '/' to paths, unless the path is empty. + * + * @access private + * + * @param string $path The file path to fix + * + * @return string The fixed file path + * + */ + + function fixPath($path) + { + $len = strlen($this->_dirSep); + + if (! empty($path) && + substr($path, -1 * $len, $len) != $this->_dirSep) { + return $path . $this->_dirSep; + } else { + return $path; + } + } + + + /** + * + * Simple error-object generator. + * + * @access public + * + * @param string $message The error message. + * + * @return object PEAR_Error + * + */ + + function &error($message) + { + if (! class_exists('PEAR_Error')) { + include_once 'PEAR.php'; + } + return PEAR::throwError($message); + } + + + /** + * + * Simple error checker. + * + * @access public + * + * @param mixed $obj Check if this is a PEAR_Error object or not. + * + * @return bool True if a PEAR_Error, false if not. + * + */ + + function isError(&$obj) + { + return is_a($obj, 'PEAR_Error'); + } +} + +?> diff --git a/includes/pear/Text/Wiki/Creole.php b/includes/pear/Text/Wiki/Creole.php new file mode 100644 index 0000000..255eaac --- /dev/null +++ b/includes/pear/Text/Wiki/Creole.php @@ -0,0 +1,150 @@ + + * + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * + * @link http://pear.php.net/package/Text_Wiki + * + * @version CVS: $Id: Creole.php 274202 2009-01-22 13:28:32Z mic $ + * + */ + +/** + * + * "Master" class for handling the management and convenience + * + */ + +require_once 'Text/Wiki.php'; + +/** + * + * Base Text_Wiki handler class extension for Creole markup + * + * @category Text + * + * @package Text_Wiki + * + * @author Michele Tomaiuolo + * + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * + * @link http://pear.php.net/package/Text_Wiki + * + * @see Text_Wiki::Text_Wiki() + * + */ + +class Text_Wiki_Creole extends Text_Wiki { + + // *single newlines* are handled as in most wikis (ignored) + // if Newline is removed from rules, they will be handled as in word-processors (meaning a paragraph break) + + var $rules = array( + 'Prefilter', + 'Delimiter', + 'Preformatted', + 'Tt', + 'Trim', + 'Break', + 'Raw', + 'Box', + 'Footnote', + 'Heading', + 'Newline', + 'Deflist', + 'Blockquote', + 'Newline', + 'Url', + 'Wikilink', + 'Image', + //'Heading', + 'Table', + 'Center', + 'Horiz', + 'Deflist', + 'List', + 'Address', + 'Paragraph', + 'Superscript', + 'Subscript', + 'Underline', + 'Emphasis', + 'Strong', + //'Italic', + //'Bold', + 'Tighten' + ); + + /** + * Constructor: just adds the path to Creole rules + * + * @access public + * @param array $rules The set of rules to load for this object. + */ + + function Text_Wiki_Creole($rules = null) { + parent::Text_Wiki($rules); + $this->addPath('parse', $this->fixPath(dirname(__FILE__)).'Parse/Creole'); + $this->renderingType = 'char'; + $this->setRenderConf('xhtml', 'center', 'css', 'center'); + $this->setRenderConf('xhtml', 'url', 'target', null); + } + + function checkInnerTags(&$text) { + $started = array(); + $i = false; + while (($i = strpos($text, $this->delim, $i)) !== false) { + $j = strpos($text, $this->delim, $i + 1); + $t = substr($text, $i + 1, $j - $i - 1); + $i = $j + 1; + $rule = strtolower($this->tokens[$t][0]); + $type = $this->tokens[$t][1]['type']; + + if ($type == 'start') { + if (empty($started[$rule])) { + $started[$rule] = 0; + } + $started[$rule] += 1; + } + else if ($type == 'end') { + if (empty($started[$rule])) return false; + + $started[$rule] -= 1; + if (! $started[$rule]) unset($started[$rule]); + } + } + return ! (count($started) > 0); + } + + function restoreRaw($text) { + $i = false; + while (($i = strpos($text, $this->delim, $i)) !== false) { + $j = strpos($text, $this->delim, $i + 1); + $t = substr($text, $i + 1, $j - $i - 1); + $rule = strtolower($this->tokens[$t][0]); + + if ($rule == 'raw') { + $text = str_replace($this->delim. $t. $this->delim, $this->tokens[$t][1]['text'], $text); + } + else { + $i = $j + 1; + } + } + return $text; + } +} + +?> diff --git a/includes/pear/Text/Wiki/Default.php b/includes/pear/Text/Wiki/Default.php new file mode 100644 index 0000000..379cf12 --- /dev/null +++ b/includes/pear/Text/Wiki/Default.php @@ -0,0 +1,27 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Default.php 208363 2006-03-01 16:58:17Z justinpatrin $ + * @link http://pear.php.net/package/Text_Wiki + */ + +require_once('Text/Wiki.php'); + +/** + * This is the parser for the Default ruleset. For now, this simply extends Text_Wiki. + * + * @category Text + * @package Text_Wiki + * @version Release: @package_version@ + * @author Justin Patrin + */ +class Text_Wiki_Default extends Text_Wiki { +} diff --git a/includes/pear/Text/Wiki/Parse.php b/includes/pear/Text/Wiki/Parse.php new file mode 100644 index 0000000..1b14c89 --- /dev/null +++ b/includes/pear/Text/Wiki/Parse.php @@ -0,0 +1,264 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Parse.php 191781 2005-07-29 08:57:29Z toggg $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * Baseline rule class for extension into a "real" parser component. + * + * Text_Wiki_Rule classes do not stand on their own; they are called by a + * Text_Wiki object, typcially in the transform() method. Each rule class + * performs three main activities: parse, process, and render. + * + * The parse() method takes a regex and applies it to the whole block of + * source text at one time. Each match is sent as $matches to the + * process() method. + * + * The process() method acts on the matched text from the source, and + * then processes the source text is some way. This may mean the + * creation of a delimited token using addToken(). In every case, the + * process() method returns the text that should replace the matched text + * from parse(). + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Parse { + + + /** + * + * Configuration options for this parser rule. + * + * @access public + * + * @var string + * + */ + + var $conf = array(); + + + /** + * + * Regular expression to find matching text for this rule. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = null; + + + /** + * + * The name of this rule for new token array elements. + * + * @access public + * + * @var string + * + */ + + var $rule = null; + + + /** + * + * A reference to the calling Text_Wiki object. + * + * This is needed so that each rule has access to the same source + * text, token set, URLs, interwiki maps, page names, etc. + * + * @access public + * + * @var object + */ + + var $wiki = null; + + + /** + * + * Constructor for this parser rule. + * + * @access public + * + * @param object &$obj The calling "parent" Text_Wiki object. + * + */ + + function Text_Wiki_Parse(&$obj) + { + // set the reference to the calling Text_Wiki object; + // this allows us access to the shared source text, token + // array, etc. + $this->wiki =& $obj; + + // set the name of this rule; generally used when adding + // to the tokens array. strip off the Text_Wiki_Parse_ portion. + // text_wiki_parse_ + // 0123456789012345 + $tmp = substr(get_class($this), 16); + $this->rule = ucwords(strtolower($tmp)); + + // override config options for the rule if specified + if (isset($this->wiki->parseConf[$this->rule]) && + is_array($this->wiki->parseConf[$this->rule])) { + + $this->conf = array_merge( + $this->conf, + $this->wiki->parseConf[$this->rule] + ); + + } + } + + + /** + * + * Abstrct method to parse source text for matches. + * + * Applies the rule's regular expression to the source text, passes + * every match to the process() method, and replaces the matched text + * with the results of the processing. + * + * @access public + * + * @see Text_Wiki_Parse::process() + * + */ + + function parse() + { + $this->wiki->source = preg_replace_callback( + $this->regex, + array(&$this, 'process'), + $this->wiki->source + ); + } + + + /** + * + * Abstract method to generate replacements for matched text. + * + * @access public + * + * @param array $matches An array of matches from the parse() method + * as generated by preg_replace_callback. $matches[0] is the full + * matched string, $matches[1] is the first matched pattern, + * $matches[2] is the second matched pattern, and so on. + * + * @return string The processed text replacement; defaults to the + * full matched string (i.e., no changes to the text). + * + * @see Text_Wiki_Parse::parse() + * + */ + + function process(&$matches) + { + return $matches[0]; + } + + + /** + * + * Simple method to safely get configuration key values. + * + * @access public + * + * @param string $key The configuration key. + * + * @param mixed $default If the key does not exist, return this value + * instead. + * + * @return mixed The configuration key value (if it exists) or the + * default value (if not). + * + */ + + function getConf($key, $default = null) + { + if (isset($this->conf[$key])) { + return $this->conf[$key]; + } else { + return $default; + } + } + + + /** + * + * Extract 'attribute="value"' portions of wiki markup. + * + * This kind of markup is typically used only in macros, but is useful + * anywhere. + * + * The syntax is pretty strict; there can be no spaces between the + * option name, the equals, and the first double-quote; the value + * must be surrounded by double-quotes. You can escape characters in + * the value with a backslash, and the backslash will be stripped for + * you. + * + * @access public + * + * @param string $text The "attributes" portion of markup. + * + * @return array An associative array of key-value pairs where the + * key is the option name and the value is the option value. + * + */ + + function getAttrs($text) + { + // find the =" sections; + $tmp = explode('="', trim($text)); + + // basic setup + $k = count($tmp) - 1; + $attrs = array(); + $key = null; + + // loop through the sections + foreach ($tmp as $i => $val) { + + // first element is always the first key + if ($i == 0) { + $key = trim($val); + continue; + } + + // find the last double-quote in the value. + // the part to the left is the value for the last key, + // the part to the right is the next key name + $pos = strrpos($val, '"'); + $attrs[$key] = stripslashes(substr($val, 0, $pos)); + $key = trim(substr($val, $pos+1)); + + } + + return $attrs; + + } +} +?> diff --git a/includes/pear/Text/Wiki/Parse/Creole/Address.php b/includes/pear/Text/Wiki/Parse/Creole/Address.php new file mode 100644 index 0000000..5e1eca1 --- /dev/null +++ b/includes/pear/Text/Wiki/Parse/Creole/Address.php @@ -0,0 +1,67 @@ + + * + * @license LGPL + * + * @version $Id: Address.php 222265 2006-10-23 13:11:27Z mic $ + * + */ + +class Text_Wiki_Parse_Address extends Text_Wiki_Parse { + + /** + * + * The regular expression used to find source text matching this + * rule. + * + * @access public + * + * @var string + * + */ + + var $regex = '/^--([^-].*)$/m'; + + /** + * + * Generates a token entry for the matched text. Token options are: + * + * 'start' => The starting point of the signature. + * + * 'end' => The ending point of the signature. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return A delimited token number to be used as a placeholder in + * the source text. + * + */ + + function process(&$matches) + { + $start = $this->wiki->addToken( + $this->rule, array('type' => 'start') + ); + + $end = $this->wiki->addToken( + $this->rule, array('type' => 'end') + ); + + return "\n" . $start . trim($matches[1]) . $end; + } +} +?> \ No newline at end of file diff --git a/includes/pear/Text/Wiki/Parse/Creole/Blockquote.php b/includes/pear/Text/Wiki/Parse/Creole/Blockquote.php new file mode 100644 index 0000000..2ab6c14 --- /dev/null +++ b/includes/pear/Text/Wiki/Parse/Creole/Blockquote.php @@ -0,0 +1,176 @@ +' at the start of the line, followed by an + * optional space, and then the quote text; each '>' indicates an + * additional level of quoting. + * + * @category Text + * + * @package Text_Wiki + * + * @author Paul M. Jones + * @author Michele Tomaiuolo + * + * @license LGPL + * + * @version $Id: Blockquote.php 230214 2007-02-19 08:57:14Z mic $ + * + */ + +class Text_Wiki_Parse_Blockquote extends Text_Wiki_Parse { + + + /** + * + * Regex for parsing the source text. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = '/\n(([>:]).*\n)(?!([>:]))/Us'; + + + /** + * + * Generates a replacement for the matched text. + * + * Token options are: + * + * 'type' => + * 'start' : the start of a blockquote + * 'end' : the end of a blockquote + * + * 'level' => the indent level (0 for the first level, 1 for the + * second, etc) + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return A series of text and delimited tokens marking the different + * list text and list elements. + * + */ + + function process(&$matches) + { + // the replacement text we will return to parse() + $return = ''; + + // the list of post-processing matches + $list = array(); + + // $matches[1] is the text matched as a list set by parse(); + // create an array called $list that contains a new set of + // matches for the various list-item elements. + preg_match_all( + '=^([>:]+)(.*?\n)=ms', + $matches[1], + $list, + PREG_SET_ORDER + ); + + // a stack of starts and ends; we keep this so that we know what + // indent level we're at. + $stack = array(); + + // loop through each list-item element. + foreach ($list as $key => $val) { + + // $val[0] is the full matched list-item line + // $val[1] is the number of initial '>' chars (indent level) + // $val[2] is the quote text + + // we number levels starting at 1, not zero + $level = strlen($val[1]); + + // get the text of the line + $text = trim($val[2]); + + // add a level to the list? + while ($level > count($stack)) { + + $css = ($val[1][count($stack)] == ':') ? 'remark' : ''; + + // the current indent level is greater than the number + // of stack elements, so we must be starting a new + // level. push the new level onto the stack with a + // dummy value (boolean true)... + array_push($stack, true); + + $return .= "\n\n"; + + // ...and add a start token to the return. + $return .= $this->wiki->addToken( + $this->rule, + array( + 'type' => 'start', + 'level' => $level - 1, + 'css' => $css + ) + ); + + $return .= "\n\n"; + } + + // remove a level? + while (count($stack) > $level) { + + // as long as the stack count is greater than the + // current indent level, we need to end list types. + // continue adding end-list tokens until the stack count + // and the indent level are the same. + array_pop($stack); + + $return .= "\n\n"; + + $return .= $this->wiki->addToken( + $this->rule, + array ( + 'type' => 'end', + 'level' => count($stack) + ) + ); + + $return .= "\n\n"; + } + + // add the line text. + $return .= $text . "\n"; + } + + // the last line may have been indented. go through the stack + // and create end-tokens until the stack is empty. + $return .= "\n\n"; + + while (count($stack) > 0) { + array_pop($stack); + + $return .= "\n\n"; + + $return .= $this->wiki->addToken( + $this->rule, + array ( + 'type' => 'end', + 'level' => count($stack) + ) + ); + + $return .= "\n\n"; + } + + // we're done! send back the replacement text. + return "\n\n$return\n\n"; + } +} +?> \ No newline at end of file diff --git a/includes/pear/Text/Wiki/Parse/Creole/Box.php b/includes/pear/Text/Wiki/Parse/Creole/Box.php new file mode 100644 index 0000000..4660a3d --- /dev/null +++ b/includes/pear/Text/Wiki/Parse/Creole/Box.php @@ -0,0 +1,81 @@ + +* @author Paul M. Jones +* +* @license LGPL +* +* @version $Id: Box.php 240481 2007-07-30 19:11:32Z mic $ +* +*/ + +/** +* +* Parses for bold text. +* +* This class implements a Text_Wiki_Rule to find source text marked for +* strong emphasis (bold) as defined by text surrounded by three +* single-quotes. On parsing, the text itself is left in place, but the +* starting and ending instances of three single-quotes are replaced with +* tokens. +* +* @category Text +* +* @package Text_Wiki +* +* @author Justin Patrin +* @author Paul M. Jones +* +*/ + +class Text_Wiki_Parse_Box extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = '/\n\[\d+\].*/s'; + + + /** + * + * Generates a replacement for the matched text. Token options are: + * + * 'type' => ['start'|'end'] The starting or ending point of the + * emphasized text. The text itself is left in the source. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return A pair of delimited tokens to be used as a placeholder in + * the source text surrounding the text to be emphasized. + * + */ + + function process(&$matches) + { + $start = $this->wiki->addToken($this->rule, array('type' => 'start', 'css' => 'footnotes')); + $end = $this->wiki->addToken($this->rule, array('type' => 'end')); + return $start . $matches[0] . "\n" . $end . "\n\n"; + } +} +?> \ No newline at end of file diff --git a/includes/pear/Text/Wiki/Parse/Creole/Break.php b/includes/pear/Text/Wiki/Parse/Creole/Break.php new file mode 100644 index 0000000..47d12d9 --- /dev/null +++ b/includes/pear/Text/Wiki/Parse/Creole/Break.php @@ -0,0 +1,73 @@ + +* +* @license LGPL +* +* @version $Id: Break.php 230561 2007-02-23 14:19:19Z mic $ +* +*/ + +/** +* +* Parses for explicit line breaks. +* +* This class implements a Text_Wiki_Parse to mark forced line breaks in the +* source text. +* +* @category Text +* +* @package Text_Wiki +* +* @author Paul M. Jones +* +*/ + +class Text_Wiki_Parse_Break extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + //var $regex = "/[ \n]*([\\\][\\\]|\%\%\%)[ \n]*/"; + var $regex = "/ *([\\\][\\\]|\%\%\%)\n?/"; + + + /** + * + * Generates a replacement token for the matched text. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return string A delimited token to be used as a placeholder in + * the source text. + * + */ + + function process(&$matches) + { + return $this->wiki->addToken($this->rule); + } +} + +?> \ No newline at end of file diff --git a/includes/pear/Text/Wiki/Parse/Creole/Center.php b/includes/pear/Text/Wiki/Parse/Creole/Center.php new file mode 100644 index 0000000..3753aca --- /dev/null +++ b/includes/pear/Text/Wiki/Parse/Creole/Center.php @@ -0,0 +1,78 @@ + + * + * @license LGPL + * + * @version $Id: Center.php 240476 2007-07-30 14:22:34Z mic $ + * + */ + +class Text_Wiki_Parse_Center extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = '/^! *(.*?)$/m'; + + /** + * + * Generates a replacement for the matched text. Token options are: + * + * 'type' => ['start'|'end'] The starting or ending point of the + * centered text. The text itself is left in the source. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return string A pair of delimited tokens to be used as a + * placeholder in the source text surrounding the centered text. + * + */ + + function process(&$matches) + { + $start = $this->wiki->addToken( + $this->rule, + array( + 'type' => 'start' + ) + ); + + $end = $this->wiki->addToken( + $this->rule, + array( + 'type' => 'end' + ) + ); + + return $start . trim($matches[1]) . $end . "\n\n"; + } +} +?> diff --git a/includes/pear/Text/Wiki/Parse/Creole/Delimiter.php b/includes/pear/Text/Wiki/Parse/Creole/Delimiter.php new file mode 100644 index 0000000..c53b78a --- /dev/null +++ b/includes/pear/Text/Wiki/Parse/Creole/Delimiter.php @@ -0,0 +1,68 @@ + + * + * @license LGPL + * + * @version $Id: Delimiter.php 222265 2006-10-23 13:11:27Z mic $ + * + */ + +class Text_Wiki_Parse_Delimiter extends Text_Wiki_Parse { + + /** + * + * Constructor. Overrides the Text_Wiki_Parse constructor so that we + * can set the $regex property dynamically (we need to include the + * Text_Wiki $delim character. + * + * @param object &$obj The calling "parent" Text_Wiki object. + * + * @param string $name The token name to use for this rule. + * + */ + + function Text_Wiki_Parse_Delimiter(&$obj) + { + parent::Text_Wiki_Parse($obj); + $this->regex = '/' . $this->wiki->delim . '/'; + } + + + /** + * + * Generates a token entry for the matched text. Token options are: + * + * 'text' => The full matched text. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return A delimited token number to be used as a placeholder in + * the source text. + * + */ + + function process(&$matches) + { + return $this->wiki->addToken( + $this->rule, + array('text' => $this->wiki->delim) + ); + } +} +?> \ No newline at end of file diff --git a/includes/pear/Text/Wiki/Parse/Creole/Emphasis.php b/includes/pear/Text/Wiki/Parse/Creole/Emphasis.php new file mode 100644 index 0000000..979f89e --- /dev/null +++ b/includes/pear/Text/Wiki/Parse/Creole/Emphasis.php @@ -0,0 +1,83 @@ + + * @author Michele Tomaiuolo + * + * @license LGPL + * + * @version $Id: Emphasis.php 242125 2007-09-03 21:16:10Z mic $ + * + */ + +class Text_Wiki_Parse_Emphasis extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = "/\/\/(.+?)\/\//"; + //var $regex = "/(?:\/\/(.+?)\/\/|(?:(?<=[\W_\xFF])\/(?![ \/]))(.+?)(?:(? ['start'|'end'] The starting or ending point of the + * emphasized text. The text itself is left in the source. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return string A pair of delimited tokens to be used as a + * placeholder in the source text surrounding the text to be + * emphasized. + * + */ + + function process(&$matches) + { + $text = $matches[1]; + //$text = $matches[1]/* ? $matches[1] : $matches[2]*/; + + if (! $this->wiki->checkInnerTags($text)) { + return $matches[0]; + } + + $start = $this->wiki->addToken( + $this->rule, + array('type' => 'start') + ); + + $end = $this->wiki->addToken( + $this->rule, + array('type' => 'end') + ); + + return $start . $text . $end; + } +} +?> \ No newline at end of file diff --git a/includes/pear/Text/Wiki/Parse/Creole/Footnote.php b/includes/pear/Text/Wiki/Parse/Creole/Footnote.php new file mode 100644 index 0000000..00e4178 --- /dev/null +++ b/includes/pear/Text/Wiki/Parse/Creole/Footnote.php @@ -0,0 +1,83 @@ + + * @author Michele Tomaiuolo + * + * @license LGPL + * + * @version $Id: Footnote.php 236407 2007-05-26 17:47:24Z mic $ + * + */ + +class Text_Wiki_Parse_Footnote extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = "/(\n)*\[([0-9]+)\]/"; + + + /** + * + * Generates a replacement for the matched text. Token options are: + * + * 'type' => ['start'|'end'] The starting or ending point of the + * emphasized text. The text itself is left in the source. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return A pair of delimited tokens to be used as a placeholder in + * the source text surrounding the text to be emphasized. + * + */ + + function process(&$matches) + { + $id = $matches[2]; + + if ($matches[1] == "\n") { + $matches[1] = "\n\n"; + $name = "fn$id"; + $href = "#ref$id"; + } + else { + $name = "ref$id"; + $href = "#fn$id"; + } + + $token = $this->wiki->addToken( + 'Url', + array('text' => "[$id]", 'href' => $href, 'name' => $name, 'type' => 'inline') + ); + + return $matches[1] . $token; + } +} +?> \ No newline at end of file diff --git a/includes/pear/Text/Wiki/Parse/Creole/Heading.php b/includes/pear/Text/Wiki/Parse/Creole/Heading.php new file mode 100644 index 0000000..eb213c9 --- /dev/null +++ b/includes/pear/Text/Wiki/Parse/Creole/Heading.php @@ -0,0 +1,97 @@ + + * @author Tomaiuolo Michele + * + * @license LGPL + * + * @version $Id: Heading.php 228641 2007-02-01 09:57:36Z mic $ + * + */ + +class Text_Wiki_Parse_Heading extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = '/^(={1,6}) *(.*?) *=*$/m'; + + var $conf = array( + 'id_prefix' => 'toc' + ); + + /** + * + * Generates a replacement for the matched text. Token options are: + * + * 'type' => ['start'|'end'] The starting or ending point of the + * heading text. The text itself is left in the source. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return string A pair of delimited tokens to be used as a + * placeholder in the source text surrounding the heading text. + * + */ + + function process(&$matches) + { + // keep a running count for header IDs. we use this later + // when constructing TOC entries, etc. + static $id; + if (! isset($id)) { + $id = 0; + } + + $prefix = htmlspecialchars($this->getConf('id_prefix')); + + $start = $this->wiki->addToken( + $this->rule, + array( + 'type' => 'start', + 'level' => strlen($matches[1]), + 'text' => trim($matches[2]), + 'id' => $prefix . $id ++ + ) + ); + + $end = $this->wiki->addToken( + $this->rule, + array( + 'type' => 'end', + 'level' => strlen($matches[1]) + ) + ); + + return $start . trim($matches[2]) . $end . "\n\n"; + } +} +?> diff --git a/includes/pear/Text/Wiki/Parse/Creole/Horiz.php b/includes/pear/Text/Wiki/Parse/Creole/Horiz.php new file mode 100644 index 0000000..60c6160 --- /dev/null +++ b/includes/pear/Text/Wiki/Parse/Creole/Horiz.php @@ -0,0 +1,58 @@ + + * + * @license LGPL + * + * @version $Id: Horiz.php 228654 2007-02-01 11:35:53Z mic $ + * + */ + +class Text_Wiki_Parse_Horiz extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = '/^([-]{4,})$/m'; + + + /** + * + * Generates a replacement token for the matched text. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return string A token marking the horizontal rule. + * + */ + + function process(&$matches) + { + return "\n" . $this->wiki->addToken($this->rule) . "\n"; + } +} +?> \ No newline at end of file diff --git a/includes/pear/Text/Wiki/Parse/Creole/Image.php b/includes/pear/Text/Wiki/Parse/Creole/Image.php new file mode 100644 index 0000000..09ddb8b --- /dev/null +++ b/includes/pear/Text/Wiki/Parse/Creole/Image.php @@ -0,0 +1,69 @@ + + * + * @license LGPL + * + * @version $Id: Image.php 243106 2007-09-28 22:02:50Z mic $ + * + */ + + +class Text_Wiki_Parse_Image extends Text_Wiki_Parse { + + /** + * + * Constructor. Overrides the Text_Wiki_Parse constructor so that we + * can set the $regex property dynamically (we need to include the + * Text_Wiki $delim character). + * + * @param object &$obj The calling "parent" Text_Wiki object. + * + * @param string $name The token name to use for this rule. + * + */ + + function Text_Wiki_Parse_Image(&$obj) + { + parent::Text_Wiki_Parse($obj); + $this->regex = '/{{([^' . $this->wiki->delim . ']*)(\|([^' . $this->wiki->delim . ']*))?}}/U'; + } + + + /** + * + * Generates a replacement token for the matched text. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return string A token marking the horizontal rule. + * + */ + + function process(&$matches) + { + $src = trim($matches[1]); + $src = ltrim($src, '/'); + $alt = isset($matches[3]) ? trim($matches[3]) : $src; + + return $this->wiki->addToken( + $this->rule, + array( + 'src' => $src, + 'attr' => array('alt' => $alt, 'title' => $alt) + ) + ); + } + +} +?> \ No newline at end of file diff --git a/includes/pear/Text/Wiki/Parse/Creole/List.php b/includes/pear/Text/Wiki/Parse/Creole/List.php new file mode 100644 index 0000000..36ab590 --- /dev/null +++ b/includes/pear/Text/Wiki/Parse/Creole/List.php @@ -0,0 +1,244 @@ + + * @author Paul M. Jones + * @author Michele Tomaiuolo + * + * @license LGPL + * + * @version $Id: List.php 240550 2007-08-01 07:57:34Z mic $ + * + */ + +class Text_Wiki_Parse_List extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = '/\n((\*[^\#\-\*]|\-[^\-\d\*\#]|\#[^\#\-\*]).*?)\n(?![\*\-#])/s'; + + /** + * + * Generates a replacement for the matched text. Token options are: + * + * 'type' => + * 'bullet_start' : the start of a bullet list + * 'bullet_end' : the end of a bullet list + * 'number_start' : the start of a number list + * 'number_end' : the end of a number list + * 'item_start' : the start of item text (bullet or number) + * 'item_end' : the end of item text (bullet or number) + * 'unknown' : unknown type of list or item + * + * 'level' => the indent level (0 for the first level, 1 for the + * second, etc) + * + * 'count' => the list item number at this level. not needed for + * xhtml, but very useful for PDF and RTF. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return A series of text and delimited tokens marking the different + * list text and list elements. + * + */ + + function process(&$matches) + { + // the replacement text we will return + $return = ''; + + // the list of post-processing matches + $list = array(); + + // a stack of list-start and list-end types; we keep this + // so that we know what kind of list we're working with + // (bullet or number) and what indent level we're at. + $stack = array(); + + // the item count is the number of list items for any + // given list-type on the stack + $itemcount = array(); + + // have we processed the very first list item? + $pastFirst = false; + + // populate $list with this set of matches. $matches[1] is the + // text matched as a list set by parse(). + preg_match_all( + '/^((\*|\-|#)+) *(.*?)$/ms', + $matches[1], + $list, + PREG_SET_ORDER + ); + + if (count($list) === 1 && $matches[0][0] === '*' && $matches[0][1] !== ' ' && strpos($matches[0], '*', 1)) { + return $matches[0]; + } + + // loop through each list-item element. + foreach ($list as $key => $val) { + // $val[0] is the full matched list-item line + // $val[1] is the level (number) + // $val[2] is the type (* or #) + // $val[3] is the list item text + + // how many levels are we indented? (1 means the "root" + // list level, no indenting.) + $stars = $val[1]; + $level = strlen($stars); + $last = $stars[strlen($stars) - 1]; + + // get the list item type + if ($last == '*' || $last == '-') { + $type = 'bullet'; + } elseif ($last == '#') { + $type = 'number'; + } else { + $type = 'unknown'; + } + + // get the text of the list item + $text = $val[3]; + + // remove a level from the list? + while (count($stack) > $level || (count($stack) == $level && $type != $stack[$level - 1])) { + + // so we don't keep counting the stack, we set up a temp + // var for the count. -1 becuase we're going to pop the + // stack in the next command. $tmp will then equal the + // current level of indent. + $tmp = count($stack) - 1; + + // as long as the stack count is greater than the + // current indent level, we need to end list types. + // continue adding end-list tokens until the stack count + // and the indent level are the same. + $return .= $this->wiki->addToken( + $this->rule, + array ( + 'type' => array_pop($stack) . '_list_end', + 'level' => $tmp + ) + ); + + // reset to the current (previous) list type so that + // the new list item matches the proper list type. + if ($tmp) { + $oldtype = $stack[$tmp - 1]; + } + + // reset the item count for the popped indent level + unset($itemcount[$tmp + 1]); + } + + // add a level to the list? + if ($level > count($stack)) { + + // the current indent level is greater than the + // number of stack elements, so we must be starting + // a new list. push the new list type onto the + // stack... + array_push($stack, $type); + + // ...and add a list-start token to the return. + $return .= $this->wiki->addToken( + $this->rule, + array( + 'type' => $type . '_list_start', + 'level' => $level - 1 + ) + ); + } + + // add to the item count for this list (taking into account + // which level we are at). + if (! isset($itemcount[$level])) { + // first count + $itemcount[$level] = 0; + } else { + // increment count + $itemcount[$level]++; + } + + // is this the very first item in the list? + if (! $pastFirst) { + $first = true; + $pastFirst = true; + } else { + $first = false; + } + + // create a list-item starting token. + $start = $this->wiki->addToken( + $this->rule, + array( + 'type' => $type . '_item_start', + 'level' => $level, + 'count' => $itemcount[$level], + 'first' => $first + ) + ); + + // create a list-item ending token. + $end = $this->wiki->addToken( + $this->rule, + array( + 'type' => $type . '_item_end', + 'level' => $level, + 'count' => $itemcount[$level] + ) + ); + + // add the starting token, list-item text, and ending token + // to the return. + $return .= "\n" . $start . $text . $end; + } + + // the last list-item may have been indented. go through the + // list-type stack and create end-list tokens until the stack + // is empty. + while (count($stack) > 0) { + $return .= $this->wiki->addToken( + $this->rule, + array ( + 'type' => array_pop($stack) . '_list_end', + 'level' => count($stack) + ) + ); + } + + // we're done! send back the replacement text. + return "\n\n" . $return . "\n\n"; + } +} +?> \ No newline at end of file diff --git a/includes/pear/Text/Wiki/Parse/Creole/Newline.php b/includes/pear/Text/Wiki/Parse/Creole/Newline.php new file mode 100644 index 0000000..92554ed --- /dev/null +++ b/includes/pear/Text/Wiki/Parse/Creole/Newline.php @@ -0,0 +1,60 @@ + + * + * @license LGPL + * + * @version $Id: Newline.php 240560 2007-08-01 11:00:11Z mic $ + * + */ + +class Text_Wiki_Parse_Newline extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + //var $regex = '/(?\:]|\*[^\*\#]|\*+ )/m'; + var $regex = '/(?|\:|\;|\!|\-\D)/m'; + + + /** + * + * Generates a replacement token for the matched text. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return string A delimited token to be used as a placeholder in + * the source text. + * + */ + + function process(&$matches) + { + return ' '; // $this->wiki->addToken($this->rule); + } +} + +?> \ No newline at end of file diff --git a/includes/pear/Text/Wiki/Parse/Creole/Paragraph.php b/includes/pear/Text/Wiki/Parse/Creole/Paragraph.php new file mode 100644 index 0000000..d23ed33 --- /dev/null +++ b/includes/pear/Text/Wiki/Parse/Creole/Paragraph.php @@ -0,0 +1,139 @@ + + * @author Michele Tomaiuolo + * + * @license LGPL + * + * @version $Id: Paragraph.php 230214 2007-02-19 08:57:14Z mic $ + * + */ + +class Text_Wiki_Parse_Paragraph extends Text_Wiki_Parse { + + /** + * + * The regular expression used to find source text matching this + * rule. + * + * @access public + * + * @var string + * + */ + + var $regex = "/^.+?\n/m"; // (?=[\n\-\|#{=]) + + var $conf = array( + 'skip' => array( + 'address', + 'box', + 'blockquote', + 'code', + 'heading', + 'center', + 'horiz', + 'deflist', + 'table', + 'list', + 'paragraph', + 'preformatted', + 'toc' + ) + ); + + + /** + * + * Generates a token entry for the matched text. Token options are: + * + * 'start' => The starting point of the paragraph. + * + * 'end' => The ending point of the paragraph. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return A delimited token number to be used as a placeholder in + * the source text. + * + */ + + function process(&$matches) + { + $delim = $this->wiki->delim; + + // was anything there? + if (trim($matches[0]) == '') { + return ''; + } + + // does the match start with a delimiter? + if (substr($matches[0], 0, 1) != $delim) { + // no. + + $start = $this->wiki->addToken( + $this->rule, array('type' => 'start') + ); + + $end = $this->wiki->addToken( + $this->rule, array('type' => 'end') + ); + + return $start . trim($matches[0]) . $end; + } + + // the line starts with a delimiter. read in the delimited + // token number, check the token, and see if we should + // skip it. + + // loop starting at the second character (we already know + // the first is a delimiter) until we find another + // delimiter; the text between them is a token key number. + $key = ''; + $len = strlen($matches[0]); + for ($i = 1; $i < $len; $i++) { + $char = $matches[0]{$i}; + if ($char == $delim) { + break; + } else { + $key .= $char; + } + } + + // look at the token and see if it's skippable (if we skip, + // it will not be marked as a paragraph) + $token_type = strtolower($this->wiki->tokens[$key][0]); + $skip = $this->getConf('skip', array()); + + if (in_array($token_type, $skip)) { + // this type of token should not have paragraphs applied to it. + // return the entire matched text. + return $matches[0]; + } else { + + $start = $this->wiki->addToken( + $this->rule, array('type' => 'start') + ); + + $end = $this->wiki->addToken( + $this->rule, array('type' => 'end') + ); + + return $start . trim($matches[0]) . $end; + } + } +} +?> \ No newline at end of file diff --git a/includes/pear/Text/Wiki/Parse/Creole/Prefilter.php b/includes/pear/Text/Wiki/Parse/Creole/Prefilter.php new file mode 100644 index 0000000..ced69b3 --- /dev/null +++ b/includes/pear/Text/Wiki/Parse/Creole/Prefilter.php @@ -0,0 +1,54 @@ + + * @author Michele Tomaiuolo + * + * @license LGPL + * + * @version $Id: Prefilter.php 222265 2006-10-23 13:11:27Z mic $ + * + */ + +class Text_Wiki_Parse_Prefilter extends Text_Wiki_Parse { + + + /** + * + * Simple parsing method. + * + * @access public + * + */ + + function parse() + { + // convert DOS line endings + $this->wiki->source = str_replace("\r\n", "\n", + $this->wiki->source); + + // convert Macintosh line endings + $this->wiki->source = str_replace("\r", "\n", + $this->wiki->source); + + // convert tabs to four-spaces + $this->wiki->source = str_replace("\t", " ", + $this->wiki->source); + + // add extra newlines at the top and end; this + // seems to help many rules. + $this->wiki->source = "\n\n" . $this->wiki->source . "\n\n"; + } + +} +?> \ No newline at end of file diff --git a/includes/pear/Text/Wiki/Parse/Creole/Preformatted.php b/includes/pear/Text/Wiki/Parse/Creole/Preformatted.php new file mode 100644 index 0000000..ac164fb --- /dev/null +++ b/includes/pear/Text/Wiki/Parse/Creole/Preformatted.php @@ -0,0 +1,68 @@ + + * + * @license LGPL + * + * @version $Id: Preformatted.php 240474 2007-07-30 13:14:41Z mic $ + * + */ + +class Text_Wiki_Parse_Preformatted extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = '/\n{{{\n(.*)\n}}}\n/Us'; + + /** + * + * Generates a replacement for the matched text. Token options are: + * + * 'text' => The preformatted text. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return string A token to be used as a placeholder + * in the source text for the preformatted text. + * + */ + + function process(&$matches) + { + // > any line consisting of only indented three closing curly braces + // > will have one space removed from the indentation + // > -- http://www.wikicreole.org/wiki/AddNoWikiEscapeProposal + $find = "/\n( *) }}}/"; + $replace = "\n$1}}}"; + $matches[1] = preg_replace($find, $replace, $matches[1]); + + $token = $this->wiki->addToken( + $this->rule, + array('text' => $matches[1]) + ); + return "\n\n" . $token . "\n\n"; + } +} +?> diff --git a/includes/pear/Text/Wiki/Parse/Creole/Raw.php b/includes/pear/Text/Wiki/Parse/Creole/Raw.php new file mode 100644 index 0000000..6679267 --- /dev/null +++ b/includes/pear/Text/Wiki/Parse/Creole/Raw.php @@ -0,0 +1,61 @@ + + * + * @license LGPL + * + * @version $Id: Raw.php 242165 2007-09-04 19:43:07Z mic $ + * + */ + +class Text_Wiki_Parse_Raw extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = '/~(_|[^ \w\n])/'; + + /** + * + * Generates a replacement for the matched text. Token options are: + * + * 'type' => ['start'|'end'] The starting or ending point of the + * monospaced text. The text itself is encapsulated into a Raw token. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return string A token to be used as a placeholder + * in the source text for the preformatted text. + * + */ + + function process(&$matches) + { + return $this->wiki->addToken( + $this->rule, + array('text' => $matches[1], 'type' => 'escape') + ); + } +} +?> diff --git a/includes/pear/Text/Wiki/Parse/Creole/Strong.php b/includes/pear/Text/Wiki/Parse/Creole/Strong.php new file mode 100644 index 0000000..1d07d15 --- /dev/null +++ b/includes/pear/Text/Wiki/Parse/Creole/Strong.php @@ -0,0 +1,84 @@ + + * @author Michele Tomaiuolo + * + * @license LGPL + * + * @version $Id: Strong.php 242126 2007-09-03 21:17:00Z mic $ + * + */ + +class Text_Wiki_Parse_Strong extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = "/\*\*(.+?)\*\*/"; + //var $regex = "/(?:\*\*(.+?)\*\*|(?:(?<=[\W_\xFF])\*(?![ \*]))(.+?)(?:(? ['start'|'end'] The starting or ending point of the + * emphasized text. The text itself is left in the source. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return A pair of delimited tokens to be used as a placeholder in + * the source text surrounding the text to be emphasized. + * + */ + + function process(&$matches) + { + $text = $matches[1]; + //$text = $matches[1] ? $matches[1] : $matches[2]; + + if (! $this->wiki->checkInnerTags($text)) { + return $matches[0]; + } + + $start = $this->wiki->addToken( + $this->rule, + array('type' => 'start') + ); + + $end = $this->wiki->addToken( + $this->rule, + array('type' => 'end') + ); + + return $start . $text . $end; + } +} +?> \ No newline at end of file diff --git a/includes/pear/Text/Wiki/Parse/Creole/Subscript.php b/includes/pear/Text/Wiki/Parse/Creole/Subscript.php new file mode 100644 index 0000000..596f652 --- /dev/null +++ b/includes/pear/Text/Wiki/Parse/Creole/Subscript.php @@ -0,0 +1,75 @@ + + * @author Michele Tomaiuolo + * + */ + +class Text_Wiki_Parse_Subscript extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = "/\,\,(.*?)\,\,/"; + + /** + * + * Generates a replacement for the matched text. Token options are: + * + * 'type' => ['start'|'end'] The starting or ending point of the + * superscript text. The text itself is left in the source. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return string A pair of delimited tokens to be used as a + * placeholder in the source text surrounding the text to be + * superscripted. + * + */ + + function process(&$matches) + { + if (! $this->wiki->checkInnerTags($matches[1])) { + return $matches[0]; + } + + $start = $this->wiki->addToken( + $this->rule, + array('type' => 'start') + ); + + $end = $this->wiki->addToken( + $this->rule, + array('type' => 'end') + ); + + return $start . $matches[1] . $end; + } +} +?> \ No newline at end of file diff --git a/includes/pear/Text/Wiki/Parse/Creole/Superscript.php b/includes/pear/Text/Wiki/Parse/Creole/Superscript.php new file mode 100644 index 0000000..0f5e38c --- /dev/null +++ b/includes/pear/Text/Wiki/Parse/Creole/Superscript.php @@ -0,0 +1,75 @@ + + * @author Michele Tomaiuolo + * + */ + +class Text_Wiki_Parse_Superscript extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = "/(\^\^(.*?)\^\^|(?<=\d)(st|nd|rd|th|er|e|re|ers|res|nds|de|des|ère|ème|ères|èmes|o|a)(?!\w))/"; + + /** + * + * Generates a replacement for the matched text. Token options are: + * + * 'type' => ['start'|'end'] The starting or ending point of the + * superscript text. The text itself is left in the source. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return string A pair of delimited tokens to be used as a + * placeholder in the source text surrounding the text to be + * superscripted. + * + */ + + function process(&$matches) + { + if (! $this->wiki->checkInnerTags($matches[0])) { + return $matches[0]; + } + + $start = $this->wiki->addToken( + $this->rule, + array('type' => 'start') + ); + + $end = $this->wiki->addToken( + $this->rule, + array('type' => 'end') + ); + + return $start . trim($matches[0], '^') . $end; + } +} +?> \ No newline at end of file diff --git a/includes/pear/Text/Wiki/Parse/Creole/Table.php b/includes/pear/Text/Wiki/Parse/Creole/Table.php new file mode 100644 index 0000000..2ab3284 --- /dev/null +++ b/includes/pear/Text/Wiki/Parse/Creole/Table.php @@ -0,0 +1,207 @@ + + * @author Paul M. Jones + * + * @license LGPL + * + * @version $Id: Table.php 243077 2007-09-28 17:14:58Z mic $ + * + */ + + +class Text_Wiki_Parse_Table extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = '/\n((\|).*)(\n)(?!(\|))/Us'; + + + /** + * + * Generates a replacement for the matched text. + * + * Token options are: + * + * 'type' => + * 'table_start' : the start of a bullet list + * 'table_end' : the end of a bullet list + * 'row_start' : the start of a number list + * 'row_end' : the end of a number list + * 'cell_start' : the start of item text (bullet or number) + * 'cell_end' : the end of item text (bullet or number) + * + * 'cols' => the number of columns in the table (for 'table_start') + * + * 'rows' => the number of rows in the table (for 'table_start') + * + * 'span' => column span (for 'cell_start') + * + * 'attr' => column attribute flag (for 'cell_start') + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return A series of text and delimited tokens marking the different + * table elements and cell text. + * + */ + + function process(&$matches) + { + // our eventual return value + $return = ''; + + // the number of columns in the table + $num_cols = 0; + + // the number of rows in the table + $num_rows = 0; + + // rows are separated by newlines in the matched text + $rows = explode("\n", $matches[1]); + + // loop through each row + foreach ($rows as $row) { + + // increase the row count + $num_rows ++; + + // remove first and last (optional) pipe + $row = substr($row, 1); + if ($row[strlen($row) - 1] == '|') { + $row = substr($row, 0, -1); + } + + // cells are separated by pipes + $cells = explode("|", $row); + + if (count($cells) == 1 && $cells[0][0] == '=' && ($num_rows == 1 || $num_rows == count($rows)) && ! isset($caption)) { + $caption = trim(trim($cells[0], '=')); + + // start the caption... + $return .= $this->wiki->addToken( + $this->rule, + array ('type' => 'caption_start') + ); + + // ...add the content... + $return .= $caption; + + // ...and end the caption. + $return .= $this->wiki->addToken( + $this->rule, + array ('type' => 'caption_end') + ); + } + else { + + // update the column count + if (count($cells) > $num_cols) { + $num_cols = count($cells); + } + + // start a new row + $return .= $this->wiki->addToken( + $this->rule, + array('type' => 'row_start') + ); + + for ($i = 0; $i < count($cells); $i++) { + $cell = $cells[$i]; + + // by default, cells span only one column (their own) + $span = 1; + $attr = ''; + + while ($i + 1 < count($cells) && ! strlen($cells[$i + 1])) { + $i++; + $span++; + } + + if (strlen($cell) > 0 && $cell[0] == '=') { + $attr = 'header'; + $cell = trim($cell, '='); + } + + // start a new cell... + $return .= $this->wiki->addToken( + $this->rule, + array ( + 'type' => 'cell_start', + 'attr' => $attr, + 'span' => $span + ) + ); + + // ...add the content... + $return .= trim($cell); + + // ...and end the cell. + $return .= $this->wiki->addToken( + $this->rule, + array ( + 'type' => 'cell_end', + 'attr' => $attr, + 'span' => $span + ) + ); + } + + // end the row + $return .= $this->wiki->addToken( + $this->rule, + array('type' => 'row_end') + ); + } + } + + // we're done! + return + "\n\n". + $this->wiki->addToken( + $this->rule, + array( + 'type' => 'table_start', + 'rows' => $num_rows, + 'cols' => $num_cols + ) + ). + $return. + $this->wiki->addToken( + $this->rule, + array( + 'type' => 'table_end' + ) + ). + "\n\n"; + } +} +?> \ No newline at end of file diff --git a/includes/pear/Text/Wiki/Parse/Creole/Tighten.php b/includes/pear/Text/Wiki/Parse/Creole/Tighten.php new file mode 100644 index 0000000..8ef1b45 --- /dev/null +++ b/includes/pear/Text/Wiki/Parse/Creole/Tighten.php @@ -0,0 +1,37 @@ + + * + * @license LGPL + * + * @version $Id: Tighten.php 222265 2006-10-23 13:11:27Z mic $ + * + */ + + +class Text_Wiki_Parse_Tighten extends Text_Wiki_Parse { + + + /** + * + * Apply tightening directly to the source text. + * + * @access public + * + */ + + function parse() + { + $this->wiki->source = str_replace("\n", '', + $this->wiki->source); + } +} +?> \ No newline at end of file diff --git a/includes/pear/Text/Wiki/Parse/Creole/Trim.php b/includes/pear/Text/Wiki/Parse/Creole/Trim.php new file mode 100644 index 0000000..f10c9a2 --- /dev/null +++ b/includes/pear/Text/Wiki/Parse/Creole/Trim.php @@ -0,0 +1,75 @@ + + * @author Michele Tomaiuolo + * + */ + +class Text_Wiki_Parse_Trim extends Text_Wiki_Parse { + + + /** + * + * Simple parsing method. + * + * @access public + * + */ + + function parse() + { + // trim lines + $find = "/ *\n */"; + $replace = "\n"; + $this->wiki->source = preg_replace($find, $replace, $this->wiki->source); + + // trim lines with only one dash or star + $find = "/\n[\-\*]\n/"; + $replace = "\n\n"; + $this->wiki->source = preg_replace($find, $replace, $this->wiki->source); + + // finally, compress all instances of 3 or more newlines + // down to two newlines. + $find = "/\n{3,}/m"; + $replace = "\n\n"; + $this->wiki->source = preg_replace($find, $replace, $this->wiki->source); + + // numbered lists + $find = "/(\n[\*\#]*)([\d]+[\.\)]|[\w]\)) /s"; + $replace = "$1# "; + $this->wiki->source = preg_replace($find, $replace, $this->wiki->source); + + // numbers in parentesis are footnotes and references + $find = "/\(([\d][\d]?)\)/"; + $replace = "[$1]"; + $this->wiki->source = preg_replace($find, $replace, $this->wiki->source); + + // add hr before footnotes + $find = "/(\n+\-\-\-\-+\n*)?(\n\[[\d]+\].*)/s"; + $replace = "\n\n----\n\n$2"; + $this->wiki->source = preg_replace($find, $replace, $this->wiki->source); + + /* + // wrap images in tables + $find = "/(?<=\n\n){{([^\|}]*)\|([^}]*)}}(?=\n\n)/"; + $replace = "| {{ $1 | $2 }}\n|= $2"; + $this->wiki->source = preg_replace($find, $replace, $this->wiki->source); + + // wrap images in tables + $find = "/(?<=\n\n){{([^\|}]*)}}(?=\n\n)/"; + $replace = "| {{ $1 }}"; + $this->wiki->source = preg_replace($find, $replace, $this->wiki->source); + */ + } + +} +?> \ No newline at end of file diff --git a/includes/pear/Text/Wiki/Parse/Creole/Tt.php b/includes/pear/Text/Wiki/Parse/Creole/Tt.php new file mode 100644 index 0000000..31072bc --- /dev/null +++ b/includes/pear/Text/Wiki/Parse/Creole/Tt.php @@ -0,0 +1,78 @@ + + * + * @license LGPL + * + * @version $Id: Tt.php 240474 2007-07-30 13:14:41Z mic $ + * + */ + +class Text_Wiki_Parse_Tt extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = '/{{{(.*?)}}}(?!}|{{{)/'; + + /** + * + * Generates a replacement for the matched text. Token options are: + * + * 'type' => ['start'|'end'] The starting or ending point of the + * monospaced text. The text itself is encapsulated into a Raw token. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return string A token to be used as a placeholder + * in the source text for the preformatted text. + * + */ + + function process(&$matches) + { + // remove the sequence }}}{{{ + $find = "/}}}{{{/"; + $replace = ""; + $matches[1] = preg_replace($find, $replace, $matches[1]); + + $start = $this->wiki->addToken( + $this->rule, + array('type' => 'start') + ); + + $raw = $this->wiki->addToken( + 'Raw', + array('text' => $matches[1]) + ); + + $end = $this->wiki->addToken( + $this->rule, + array('type' => 'end') + ); + + return $start . $raw . $end; + } +} +?> diff --git a/includes/pear/Text/Wiki/Parse/Creole/Underline.php b/includes/pear/Text/Wiki/Parse/Creole/Underline.php new file mode 100644 index 0000000..c3d5b14 --- /dev/null +++ b/includes/pear/Text/Wiki/Parse/Creole/Underline.php @@ -0,0 +1,83 @@ + + * @author Michele Tomaiuolo + * + * @license LGPL + * + * @version $Id: Underline.php 242127 2007-09-03 21:29:36Z mic $ + * + */ + +class Text_Wiki_Parse_Underline extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = "/__(.+?)__/"; + //var $regex = "/(?:\_\_(.+?)\_\_|(?:(?<=[\W_\xFF])\_(?![ \_]))(.+?)(?:(? ['start'|'end'] The starting or ending point of the + * superscript text. The text itself is left in the source. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return string A pair of delimited tokens to be used as a + * placeholder in the source text surrounding the text to be + * superscripted. + * + */ + + function process(&$matches) + { + $text = $matches[1]; + //$text = $matches[1] ? $matches[1] : $matches[2]; + + if (! $this->wiki->checkInnerTags($text)) { + return $matches[0]; + } + + $start = $this->wiki->addToken( + $this->rule, + array('type' => 'start') + ); + + $end = $this->wiki->addToken( + $this->rule, + array('type' => 'end') + ); + + return $start . $text . $end; + } +} +?> \ No newline at end of file diff --git a/includes/pear/Text/Wiki/Parse/Creole/Url.php b/includes/pear/Text/Wiki/Parse/Creole/Url.php new file mode 100644 index 0000000..4422a31 --- /dev/null +++ b/includes/pear/Text/Wiki/Parse/Creole/Url.php @@ -0,0 +1,109 @@ + tag (for the 'xhtml' + * format). + * + * @category Text + * + * @package Text_Wiki + * + * @author Michele Tomaiuolo + * + * @license LGPL + * + * @version $Id: Url.php 293784 2010-01-20 18:48:09Z justinpatrin $ + * + */ + +class Text_Wiki_Parse_Url extends Text_Wiki_Parse { + + /** + * + * Constructor. Overrides the Text_Wiki_Parse constructor so that we + * can set the $regex property dynamically (we need to include the + * Text_Wiki $delim character). + * + * @param object &$obj The calling "parent" Text_Wiki object. + * + * @param string $name The token name to use for this rule. + * + */ + + function Text_Wiki_Parse_Url(&$obj) + { + parent::Text_Wiki_Parse($obj); + $this->regex = '/((?:\[\[ *((?:\w+:\/\/|mailto:|\/)[^\|\]\n ]*)( *\| *([^\]\n]*))? *\]\])|((?<=[^\~\w])(https?:\/\/|ftps?:\/\/|mailto:)[^\'\"\n ' . $this->wiki->delim . ']*[A-Za-z0-9\/\?\=\&\~\_#]))/'; + } + + + /** + * + * Generates a replacement for the matched text. + * + * Token options are: + * + * 'href' => the URL link href portion + * + * 'text' => the displayed text of the URL link + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return string A token to be used as a placeholder + * in the source text for the preformatted text. + * + */ + + function process(&$matches) + { + if (isset($matches[2])) $href = trim($matches[2]); + if (isset($matches[4])) $text = trim($matches[4]); + if (isset($matches[5])) $rawurl = $matches[5]; + if (empty($href)) $href = $rawurl; + + if (empty($text)) { + $text = $href; + if (strpos($text, '/') === FALSE) { + $text = str_replace('http://', '', $text); + $text = str_replace('mailto:', '', $text); + } + return $this->wiki->addToken( + $this->rule, + array( + 'type' => 'inline', + 'href' => $href, + 'text' => $text + ) + ); + } else { + return $this->wiki->addToken( + $this->rule, + array( + 'type' => 'start', + 'href' => $href, + 'text' => $text + ) + ) . $text . + $this->wiki->addToken( + $this->rule, + array( + 'type' => 'end', + 'href' => $href, + 'text' => $text + ) + ); + } + } + +} +?> \ No newline at end of file diff --git a/includes/pear/Text/Wiki/Parse/Creole/Wikilink.php b/includes/pear/Text/Wiki/Parse/Creole/Wikilink.php new file mode 100644 index 0000000..19cafc1 --- /dev/null +++ b/includes/pear/Text/Wiki/Parse/Creole/Wikilink.php @@ -0,0 +1,322 @@ + + * @author Paul M. Jones + * @copyright 2005 bertrand Gugger + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Wikilink.php 240474 2007-07-30 13:14:41Z mic $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * Wikilink, Interwiki and Image rules parser class for Mediawiki. + * This class implements a Text_Wiki_Parse to find links marked + * in source by text surrounded by 2 opening/closing brackets as + * [[Wiki page name#Section|Alternate text]] + * On parsing, the link is replaced with a token. + * + * @category Text + * @package Text_Wiki + * @author Bertrand Gugger + * @copyright 2005 bertrand Gugger + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + * @see Text_Wiki_Parse::Text_Wiki_Parse() + */ + +class Text_Wiki_Parse_Wikilink extends Text_Wiki_Parse { + + /** + * Configuration for this rule (Wikilink) + * + * @access public + * @var array + */ + + var $conf = array( + 'spaceUnderscore' => true, + 'project' => array('demo', 'd'), + 'url' => 'http://example.com/en/page=%s', + 'langage' => 'en' + ); + + /** + * Configuration for the Image rule + * + * @access public + * @var array + */ + + var $imageConf = array( + 'prefix' => array('Image', 'image') + ); + + /** + * Configuration for the Interwiki rule + * + * @access public + * @var array + */ + + var $interwikiConf = array( + 'sites' => array( + 'manual' => 'http://www.php.net/manual/en/%s', + 'pear' => 'http://pear.php.net/package/%s', + 'bugs' => 'http://pear.php.net/package/%s/bugs' + ), + 'interlangage' => array('en', 'de', 'fr') + ); + + /** + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * @var string + * @see Text_Wiki_Parse::parse() + */ + + var $regex = '/(?conf; + parent::Text_Wiki_Parse($obj); + + // override config options for image if specified + if (in_array('Image', $this->wiki->disable)) { + $this->imageConf['prefix'] = array(); + } else { + if (isset($this->wiki->parseConf['Image']) && + is_array($this->wiki->parseConf['Image'])) { + $this->imageConf = array_merge( + $this->imageConf, + $this->wiki->parseConf['Image'] + ); + } + } + + // override config options for interwiki if specified + if (in_array('Interwiki', $this->wiki->disable)) { + $this->interwikiConf['sites'] = array(); + $this->interwikiConf['interlangage'] = array(); + } else { + if (isset($this->wiki->parseConf['Interwiki']) && + is_array($this->wiki->parseConf['Interwiki'])) { + $this->interwikiConf = array_merge( + $this->interwikiConf, + $this->wiki->parseConf['Interwiki'] + ); + } + if (empty($this->conf['langage'])) { + $this->interwikiConf['interlangage'] = array(); + } + } + //$this->regex = str_replace('DELIM', $this->wiki->delim, $this->regex); + // convert the list of recognized schemes to a regex OR, +/* $schemes = $this->getConf('schemes', $default['schemes']); + $this->url = str_replace( '#delim#', $this->wiki->delim, + '#(?:' . (is_array($schemes) ? implode('|', $schemes) : $schemes) . ')://' + . $this->getConf('host_regexp', $default['host_regexp']) + . $this->getConf('path_regexp', $default['path_regexp']) .'#'); */ + } + + /** + * Generates a replacement for the matched text. Token options are: + * - 'page' => the name of the target wiki page + * -'anchor' => the optional section in it + * - 'text' => the optional alternate link text + * + * @access public + * @param array &$matches The array of matches from parse(). + * @return string token to be used as replacement + */ + + function process(&$matches) + { + $matches[3] = $this->wiki->restoreRaw($matches[3]); + + // Starting colon ? + $colon = !empty($matches[1]); + $auto = $interlang = $interwiki = $image = $site = ''; + // Prefix ? + if (!empty($matches[2])) { + $prefix = explode(':', substr($matches[2], 0, -1)); + $count = count($prefix); + $i = -1; + // Autolink + if (isset($this->conf['project']) && + in_array(trim($prefix[0]), $this->conf['project'])) { + $auto = trim($prefix[0]); + unset($prefix[0]); + $i = 0; + } + while (++$i < $count) { + $prefix[$i] = trim($prefix[$i]); + // interlangage + if (!$interlang && + in_array($prefix[$i], $this->interwikiConf['interlangage'])) { + $interlang = $prefix[$i]; + unset($prefix[$i]); + continue; + } + // image + if (!$image && in_array($prefix[$i], $this->imageConf['prefix'])) { + $image = $prefix[$i]; + unset($prefix[$i]); + break; + } + // interwiki + if (isset($this->interwikiConf['sites'][$prefix[$i]])) { + $interwiki = $this->interwikiConf['sites'][$prefix[$i]]; + $site = $prefix[$i]; + unset($prefix[$i]); + } + break; + } + if ($prefix) { + $matches[3] = implode(':', $prefix) . ':' . $matches[3]; + } + } + $text = empty($matches[5]) ? $matches[3] : $matches[5]; + $matches[3] = trim($matches[3]); + $matches[4] = empty($matches[4]) ? '' : trim($matches[4]); + if ($this->conf['spaceUnderscore']) { + $matches[3] = preg_replace('/\s+/', '_', $matches[3]); + $matches[4] = preg_replace('/\s+/', '_', $matches[4]); + } + if ($image) { + return $this->image($matches[3] . (empty($matches[4]) ? '' : '#' . $matches[4]), + $text, $interlang, $colon); + } + if (!$interwiki && $interlang && isset($this->conf['url'])) { + if ($interlang == $this->conf['langage']) { + $interlang = ''; + } else { + $interwiki = $this->conf['url']; + $site = isset($this->conf['project']) ? $this->conf['project'][0] : ''; + } + } + if ($interwiki) { + return $this->interwiki($site, $interwiki, + $matches[3] . (empty($matches[4]) ? '' : '#' . $matches[4]), + $text, $interlang, $colon); + } + if ($interlang) { + $matches[3] = $interlang . ':' . $matches[3]; + $text = (empty($matches[5]) ? $interlang . ':' : '') . $text; + } + + $start = $this->wiki->addToken($this->rule, array( + 'type' => 'start', + 'page' => $matches[3], + 'anchor' => (empty($matches[4]) ? '' : $matches[4]), + 'text' => $text + )); + + $end = $this->wiki->addToken($this->rule, array( + 'type' => 'end', + 'page' => $matches[3], + 'anchor' => (empty($matches[4]) ? '' : $matches[4]), + 'text' => $text + )); + + // create and return the replacement token + return $start . $text . $end; + } + + /** + * Generates an image token. Token options are: + * - 'src' => the name of the image file + * - 'attr' => an array of attributes for the image: + * | - 'alt' => the optional alternate image text + * | - 'align => 'left', 'center' or 'right' + * + * @access public + * @param array &$matches The array of matches from parse(). + * @return string token to be used as replacement + */ + + function image($name, $text, $interlang, $colon) + { + $attr = array('alt' => ''); + // scan text for supplementary attibutes + if (strpos($text, '|') !== false) { + $splits = explode('|', $text); + $sep = ''; + foreach ($splits as $split) { + switch (strtolower($split)) { + case 'left': case 'center': case 'right': + $attr['align'] = strtolower($split); + break; + default: + $attr['alt'] .= $sep . $split; + $sep = '|'; + } + } + } else { + $attr['alt'] = $text; + } + $options = array( + 'src' => ($interlang ? $interlang . ':' : '') . $name, + 'attr' => $attr); + + // create and return the replacement token + return $this->wiki->addToken('Image', $options); + } + + /** + * Generates an interwiki token. Token options are: + * - 'page' => the name of the target wiki page + * - 'site' => the key for external site + * - 'url' => the full target url + * - 'text' => the optional alternate link text + * + * @access public + * @param array &$matches The array of matches from parse(). + * @return string token to be used as replacement + */ + + function interwiki($site, $interwiki, $page, $text, $interlang, $colon) + { + if ($interlang) { + $interwiki = preg_replace('/\b' . $this->conf['langage'] . '\b/i', + $interlang, $interwiki); + } + // set the options + $options = array( + 'page' => $page, + 'site' => $site, + 'url' => sprintf($interwiki, $page), + 'text' => $text + ); + + // create and return the replacement token + return $this->wiki->addToken('Interwiki', $options); + } +} +?> diff --git a/includes/pear/Text/Wiki/Parse/Default/Anchor.php b/includes/pear/Text/Wiki/Parse/Default/Anchor.php new file mode 100644 index 0000000..83b57c6 --- /dev/null +++ b/includes/pear/Text/Wiki/Parse/Default/Anchor.php @@ -0,0 +1,87 @@ + +* +* @author Paul M. Jones +* +* @license LGPL +* +* @version $Id: Anchor.php 180591 2005-02-23 17:38:29Z pmjones $ +* +*/ + +/** +* +* This class implements a Text_Wiki_Parse to add an anchor target name +* in the wiki page. +* +* @author Manuel Holtgrewe +* +* @author Paul M. Jones +* +* @category Text +* +* @package Text_Wiki +* +*/ + +class Text_Wiki_Parse_Anchor extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to find source text matching this + * rule. Looks like a macro: [[# anchor_name]] + * + * @access public + * + * @var string + * + */ + + var $regex = '/(\[\[# )([-_A-Za-z0-9.]+?)( .+)?(\]\])/i'; + + + /** + * + * Generates a token entry for the matched text. Token options are: + * + * 'text' => The full matched text, not including the tags. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return A delimited token number to be used as a placeholder in + * the source text. + * + */ + + function process(&$matches) { + + $name = $matches[2]; + $text = $matches[3]; + + $start = $this->wiki->addToken( + $this->rule, + array('type' => 'start', 'name' => $name) + ); + + $end = $this->wiki->addToken( + $this->rule, + array('type' => 'end', 'name' => $name) + ); + + // done, place the script output directly in the source + return $start . trim($text) . $end; + } +} +?> diff --git a/includes/pear/Text/Wiki/Parse/Default/Blockquote.php b/includes/pear/Text/Wiki/Parse/Default/Blockquote.php new file mode 100644 index 0000000..61da770 --- /dev/null +++ b/includes/pear/Text/Wiki/Parse/Default/Blockquote.php @@ -0,0 +1,180 @@ + +* +* @license LGPL +* +* @version $Id: Blockquote.php 222150 2006-10-21 05:56:28Z justinpatrin $ +* +*/ + +/** +* +* Parse for block-quoted text. +* +* Find source text marked as a blockquote, identified by any number of +* greater-than signs '>' at the start of the line, followed by a space, +* and then the quote text; each '>' indicates an additional level of +* quoting. +* +* @category Text +* +* @package Text_Wiki +* +* @author Paul M. Jones +* +*/ + +class Text_Wiki_Parse_Blockquote extends Text_Wiki_Parse { + + + /** + * + * Regex for parsing the source text. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = '/\n((\>).*\n)(?!(\>))/Us'; + + + /** + * + * Generates a replacement for the matched text. + * + * Token options are: + * + * 'type' => + * 'start' : the start of a blockquote + * 'end' : the end of a blockquote + * + * 'level' => the indent level (0 for the first level, 1 for the + * second, etc) + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return A series of text and delimited tokens marking the different + * list text and list elements. + * + */ + + function process(&$matches) + { + // the replacement text we will return to parse() + $return = "\n"; + + // the list of post-processing matches + $list = array(); + + // $matches[1] is the text matched as a list set by parse(); + // create an array called $list that contains a new set of + // matches for the various list-item elements. + preg_match_all( + '=^(\>+) (.*\n)=Ums', + $matches[1], + $list, + PREG_SET_ORDER + ); + + $curLevel = 0; + + // loop through each list-item element. + foreach ($list as $key => $val) { + + // $val[0] is the full matched list-item line + // $val[1] is the number of initial '>' chars (indent level) + // $val[2] is the quote text + + // we number levels starting at 1, not zero + $level = strlen($val[1]); + + // add a level to the list? + while ($level > $curLevel) { + // the current indent level is greater than the number + // of stack elements, so we must be starting a new + // level. push the new level onto the stack with a + // dummy value (boolean true)... + ++$curLevel; + + //$return .= "\n"; + + // ...and add a start token to the return. + $return .= $this->wiki->addToken( + $this->rule, + array( + 'type' => 'start', + 'level' => $curLevel + ) + ); + + //$return .= "\n\n"; + } + + // remove a level? + while ($curLevel > $level) { + + // as long as the stack count is greater than the + // current indent level, we need to end list types. + // continue adding end-list tokens until the stack count + // and the indent level are the same. + + //$return .= "\n\n"; + + $return .= $this->wiki->addToken( + $this->rule, + array ( + 'type' => 'end', + 'level' => $curLevel + ) + ); + + //$return .= "\n"; + --$curLevel; + } + + // add the line text. + $return .= $val[2]; + } + + // the last char of the matched pattern must be \n but we don't + // want this to be inside the tokens + $return = substr($return, 0, -1); + + // the last line may have been indented. go through the stack + // and create end-tokens until the stack is empty. + //$return .= "\n"; + + while ($curLevel > 0) { + $return .= $this->wiki->addToken( + $this->rule, + array ( + 'type' => 'end', + 'level' => $curLevel + ) + ); + --$curLevel; + } + + // put back the trailing \n + $return .= "\n"; + + // we're done! send back the replacement text. + return $return; + } +} +?> \ No newline at end of file diff --git a/includes/pear/Text/Wiki/Parse/Default/Bold.php b/includes/pear/Text/Wiki/Parse/Default/Bold.php new file mode 100644 index 0000000..ba5e965 --- /dev/null +++ b/includes/pear/Text/Wiki/Parse/Default/Bold.php @@ -0,0 +1,79 @@ + +* +* @license LGPL +* +* @version $Id: Bold.php 180591 2005-02-23 17:38:29Z pmjones $ +* +*/ + +/** +* +* Parses for bold text. +* +* This class implements a Text_Wiki_Rule to find source text marked for +* strong emphasis (bold) as defined by text surrounded by three +* single-quotes. On parsing, the text itself is left in place, but the +* starting and ending instances of three single-quotes are replaced with +* tokens. +* +* @category Text +* +* @package Text_Wiki +* +* @author Paul M. Jones +* +*/ + +class Text_Wiki_Parse_Bold extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = "/'''(()|[^'].*)'''/U"; + + + /** + * + * Generates a replacement for the matched text. Token options are: + * + * 'type' => ['start'|'end'] The starting or ending point of the + * emphasized text. The text itself is left in the source. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return A pair of delimited tokens to be used as a placeholder in + * the source text surrounding the text to be emphasized. + * + */ + + function process(&$matches) + { + $start = $this->wiki->addToken($this->rule, array('type' => 'start')); + $end = $this->wiki->addToken($this->rule, array('type' => 'end')); + return $start . $matches[1] . $end; + } +} +?> \ No newline at end of file diff --git a/includes/pear/Text/Wiki/Parse/Default/Break.php b/includes/pear/Text/Wiki/Parse/Default/Break.php new file mode 100644 index 0000000..1a684f1 --- /dev/null +++ b/includes/pear/Text/Wiki/Parse/Default/Break.php @@ -0,0 +1,72 @@ + +* +* @license LGPL +* +* @version $Id: Break.php 180591 2005-02-23 17:38:29Z pmjones $ +* +*/ + +/** +* +* Parses for explicit line breaks. +* +* This class implements a Text_Wiki_Parse to mark forced line breaks in the +* source text. +* +* @category Text +* +* @package Text_Wiki +* +* @author Paul M. Jones +* +*/ + +class Text_Wiki_Parse_Break extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = '/ _\n/'; + + + /** + * + * Generates a replacement token for the matched text. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return string A delimited token to be used as a placeholder in + * the source text. + * + */ + + function process(&$matches) + { + return $this->wiki->addToken($this->rule); + } +} + +?> \ No newline at end of file diff --git a/includes/pear/Text/Wiki/Parse/Default/Center.php b/includes/pear/Text/Wiki/Parse/Default/Center.php new file mode 100644 index 0000000..6ddde51 --- /dev/null +++ b/includes/pear/Text/Wiki/Parse/Default/Center.php @@ -0,0 +1,78 @@ + +* +* @license LGPL +* +* @version $Id: Center.php 180591 2005-02-23 17:38:29Z pmjones $ +* +*/ + +/** +* +* Parses for centered lines of text. +* +* This class implements a Text_Wiki_Parse to find lines marked for centering. +* The line must start with "= " (i.e., an equal-sign followed by a space). +* +* @category Text +* +* @package Text_Wiki +* +* @author Paul M. Jones +* +*/ + +class Text_Wiki_Parse_Center extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to find source text matching this + * rule. + * + * @access public + * + * @var string + * + */ + + var $regex = '/\n\= (.*?)\n/'; + + /** + * + * Generates a token entry for the matched text. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return A delimited token number to be used as a placeholder in + * the source text. + * + */ + + function process(&$matches) + { + $start = $this->wiki->addToken( + $this->rule, + array('type' => 'start') + ); + + $end = $this->wiki->addToken( + $this->rule, + array('type' => 'end') + ); + + return "\n" . $start . $matches[1] . $end . "\n"; + } +} +?> \ No newline at end of file diff --git a/includes/pear/Text/Wiki/Parse/Default/Code.php b/includes/pear/Text/Wiki/Parse/Default/Code.php new file mode 100644 index 0000000..fc2d3c1 --- /dev/null +++ b/includes/pear/Text/Wiki/Parse/Default/Code.php @@ -0,0 +1,99 @@ + +* +* @license LGPL +* +* @version $Id: Code.php 237313 2007-06-09 23:11:25Z justinpatrin $ +* +*/ + +/** +* +* Parses for text marked as a code example block. +* +* This class implements a Text_Wiki_Parse to find sections marked as code +* examples. Blocks are marked as the string on a line by itself, +* followed by the inline code example, and terminated with the string +* on a line by itself. The code example is run through the +* native PHP highlight_string() function to colorize it, then surrounded +* with
    ...
    tags when rendered as XHTML. +* +* @category Text +* +* @package Text_Wiki +* +* @author Paul M. Jones +* +*/ + +class Text_Wiki_Parse_Code extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to find source text matching this + * rule. + * + * @access public + * + * @var string + * + */ + +/* var $regex = '/^(\)\n(.+)\n(\<\/code\>)(\s|$)/Umsi';*/ + var $regex = ';^]*)?>((?:(?R)|.*?)*)\n
    (\s|$);msi'; + + /** + * + * Generates a token entry for the matched text. Token options are: + * + * 'text' => The full matched text, not including the tags. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return A delimited token number to be used as a placeholder in + * the source text. + * + */ + + function process(&$matches) + { + // are there additional attribute arguments? + $args = trim($matches[1]); + + if ($args == '') { + $options = array( + 'text' => $matches[2], + 'attr' => array('type' => '') + ); + } else { + // get the attributes... + $attr = $this->getAttrs($args); + + // ... and make sure we have a 'type' + if (! isset($attr['type'])) { + $attr['type'] = ''; + } + + // retain the options + $options = array( + 'text' => $matches[2], + 'attr' => $attr + ); + } + + return $this->wiki->addToken($this->rule, $options) . $matches[3]; + } +} +?> diff --git a/includes/pear/Text/Wiki/Parse/Default/Colortext.php b/includes/pear/Text/Wiki/Parse/Default/Colortext.php new file mode 100644 index 0000000..5d82cd6 --- /dev/null +++ b/includes/pear/Text/Wiki/Parse/Default/Colortext.php @@ -0,0 +1,89 @@ + +* +* @license LGPL +* +* @version $Id: Colortext.php 180591 2005-02-23 17:38:29Z pmjones $ +* +*/ + +/** +* +* Parses for colorized text. +* +* @category Text +* +* @package Text_Wiki +* +* @author Paul M. Jones +* +*/ + +class Text_Wiki_Parse_Colortext extends Text_Wiki_Parse { + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = "/\#\#(.+?)\|(.+?)\#\#/"; + + + /** + * + * Generates a replacement for the matched text. Token options are: + * + * 'type' => ['start'|'end'] The starting or ending point of the + * emphasized text. The text itself is left in the source. + * + * 'color' => the color indicator + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return string A pair of delimited tokens to be used as a + * placeholder in the source text surrounding the text to be + * emphasized. + * + */ + + function process(&$matches) + { + $start = $this->wiki->addToken( + $this->rule, + array( + 'type' => 'start', + 'color' => $matches[1] + ) + ); + + $end = $this->wiki->addToken( + $this->rule, + array( + 'type' => 'end', + 'color' => $matches[1] + ) + ); + + return $start . $matches[2] . $end; + } +} +?> \ No newline at end of file diff --git a/includes/pear/Text/Wiki/Parse/Default/Deflist.php b/includes/pear/Text/Wiki/Parse/Default/Deflist.php new file mode 100644 index 0000000..a8ee0fb --- /dev/null +++ b/includes/pear/Text/Wiki/Parse/Default/Deflist.php @@ -0,0 +1,122 @@ + +* +* @license LGPL +* +* @version $Id: Deflist.php 180591 2005-02-23 17:38:29Z pmjones $ +* +*/ + +/** +* +* Parses for definition lists. +* +* This class implements a Text_Wiki_Parse to find source text marked as a +* definition list. In short, if a line starts with ':' then it is a +* definition list item; another ':' on the same line indicates the end +* of the definition term and the beginning of the definition narrative. +* The list items must be on sequential lines (no blank lines between +* them) -- a blank line indicates the beginning of a new list. +* +* @category Text +* +* @package Text_Wiki +* +* @author Paul M. Jones +* +*/ + +class Text_Wiki_Parse_Deflist extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = '/\n((: ).*\n)(?!(: |\n))/Us'; + + + /** + * + * Generates a replacement for the matched text. Token options are: + * + * 'type' => + * 'list_start' : the start of a definition list + * 'list_end' : the end of a definition list + * 'term_start' : the start of a definition term + * 'term_end' : the end of a definition term + * 'narr_start' : the start of definition narrative + * 'narr_end' : the end of definition narrative + * 'unknown' : unknown type of definition portion + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return A series of text and delimited tokens marking the different + * list text and list elements. + * + */ + + function process(&$matches) + { + // the replacement text we will return to parse() + $return = ''; + + // the list of post-processing matches + $list = array(); + + // start the deflist + $options = array('type' => 'list_start'); + $return .= $this->wiki->addToken($this->rule, $options); + + // $matches[1] is the text matched as a list set by parse(); + // create an array called $list that contains a new set of + // matches for the various definition-list elements. + preg_match_all( + '/^(: )(.*)?( : )(.*)?$/Ums', + $matches[1], + $list, + PREG_SET_ORDER + ); + + // add each term and narrative + foreach ($list as $key => $val) { + $return .= ( + $this->wiki->addToken($this->rule, array('type' => 'term_start')) . + trim($val[2]) . + $this->wiki->addToken($this->rule, array('type' => 'term_end')) . + $this->wiki->addToken($this->rule, array('type' => 'narr_start')) . + trim($val[4]) . + $this->wiki->addToken($this->rule, array('type' => 'narr_end')) + ); + } + + + // end the deflist + $options = array('type' => 'list_end'); + $return .= $this->wiki->addToken($this->rule, $options); + + // done! + return "\n" . $return . "\n\n"; + } +} +?> \ No newline at end of file diff --git a/includes/pear/Text/Wiki/Parse/Default/Delimiter.php b/includes/pear/Text/Wiki/Parse/Default/Delimiter.php new file mode 100644 index 0000000..8aa4b53 --- /dev/null +++ b/includes/pear/Text/Wiki/Parse/Default/Delimiter.php @@ -0,0 +1,80 @@ + +* +* @license LGPL +* +* @version $Id: Delimiter.php 180591 2005-02-23 17:38:29Z pmjones $ +* +*/ + +/** +* +* Parses for Text_Wiki delimiter characters already in the source text. +* +* This class implements a Text_Wiki_Parse to find instances of the delimiter +* character already embedded in the source text; it extracts them and replaces +* them with a delimited token, then renders them as the delimiter itself +* when the target format is XHTML. +* +* @category Text +* +* @package Text_Wiki +* +* @author Paul M. Jones +* +*/ + +class Text_Wiki_Parse_Delimiter extends Text_Wiki_Parse { + + /** + * + * Constructor. Overrides the Text_Wiki_Parse constructor so that we + * can set the $regex property dynamically (we need to include the + * Text_Wiki $delim character. + * + * @param object &$obj The calling "parent" Text_Wiki object. + * + * @param string $name The token name to use for this rule. + * + */ + + function Text_Wiki_Parse_delimiter(&$obj) + { + parent::Text_Wiki_Parse($obj); + $this->regex = '/' . $this->wiki->delim . '/'; + } + + + /** + * + * Generates a token entry for the matched text. Token options are: + * + * 'text' => The full matched text. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return A delimited token number to be used as a placeholder in + * the source text. + * + */ + + function process(&$matches) + { + return $this->wiki->addToken( + $this->rule, + array('text' => $this->wiki->delim) + ); + } +} +?> \ No newline at end of file diff --git a/includes/pear/Text/Wiki/Parse/Default/Embed.php b/includes/pear/Text/Wiki/Parse/Default/Embed.php new file mode 100644 index 0000000..2d31a71 --- /dev/null +++ b/includes/pear/Text/Wiki/Parse/Default/Embed.php @@ -0,0 +1,106 @@ + +* +* @license LGPL +* +* @version $Id: Embed.php 180591 2005-02-23 17:38:29Z pmjones $ +* +*/ + +/** +* +* Embeds the results of a PHP script at render-time. +* +* This class implements a Text_Wiki_Parse to embed the contents of a URL +* inside the page at render-time. Typically used to get script output. +* This differs from the 'include' rule, which incorporates results at +* parse-time; 'embed' output does not get parsed by Text_Wiki, while +* 'include' ouput does. +* +* This rule is inherently not secure; it allows cross-site scripting to +* occur if the embedded output has +'; + } + } else { + $js = ''; + } + return $js.' +
    +'; + case 'endContent': + return ' +
    +'; + } + } +} +?> diff --git a/includes/pear/Text/Wiki/Render/Xhtml/Horiz.php b/includes/pear/Text/Wiki/Render/Xhtml/Horiz.php new file mode 100644 index 0000000..e480f5e --- /dev/null +++ b/includes/pear/Text/Wiki/Render/Xhtml/Horiz.php @@ -0,0 +1,51 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Horiz.php 191862 2005-07-30 08:03:29Z toggg $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders an horizontal bar in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Horiz extends Text_Wiki_Render { + + var $conf = array( + 'css' => null + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + $css = $this->formatConf(' class="%s"', 'css'); + return "\n"; + } +} +?> diff --git a/includes/pear/Text/Wiki/Render/Xhtml/Html.php b/includes/pear/Text/Wiki/Render/Xhtml/Html.php new file mode 100644 index 0000000..7ca4218 --- /dev/null +++ b/includes/pear/Text/Wiki/Render/Xhtml/Html.php @@ -0,0 +1,47 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Html.php 191862 2005-07-30 08:03:29Z toggg $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders preformated html in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Html extends Text_Wiki_Render { + + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + return $options['text']; + } +} +?> diff --git a/includes/pear/Text/Wiki/Render/Xhtml/Image.php b/includes/pear/Text/Wiki/Render/Xhtml/Image.php new file mode 100644 index 0000000..51def7b --- /dev/null +++ b/includes/pear/Text/Wiki/Render/Xhtml/Image.php @@ -0,0 +1,184 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Image.php 231923 2007-03-15 15:04:50Z justinpatrin $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class inserts an image in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Image extends Text_Wiki_Render { + + var $conf = array( + 'base' => '/', + 'url_base' => null, + 'css' => null, + 'css_link' => null + ); + + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + // note the image source + $src = $options['src']; + + // is the source a local file or URL? + if (strpos($src, '://') === false) { + // the source refers to a local file. + // add the URL base to it. + $src = $this->getConf('base', '/') . $src; + } + + // stephane@metacites.net + // is the image clickable? + if (isset($options['attr']['link'])) { + // yes, the image is clickable. + // are we linked to a URL or a wiki page? + if (strpos($options['attr']['link'], '://')) { + // it's a URL, prefix the URL base + $href = $this->getConf('url_base') . $options['attr']['link']; + } else { + // it's a WikiPage; assume it exists. + /** @todo This needs to honor sprintf wikilinks (pmjones) */ + /** @todo This needs to honor interwiki (pmjones) */ + /** @todo This needs to honor freelinks (pmjones) */ + $href = $this->wiki->getRenderConf('xhtml', 'wikilink', 'view_url') . + $options['attr']['link']; + } + } else { + // image is not clickable. + $href = null; + } + // unset so it won't show up as an attribute + unset($options['attr']['link']); + + // stephane@metacites.net -- 25/07/2004 + // use CSS for all alignment + if (isset($options['attr']['align'])) { + // make sure we have a style attribute + if (!isset($options['attr']['style'])) { + // no style, set up a blank one + $options['attr']['style'] = ''; + } else { + // style exists, add a space + $options['attr']['style'] .= ' '; + } + + if ($options['attr']['align'] == 'center') { + // add a "center" style to the existing style. + $options['attr']['style'] .= + 'display: block; margin-left: auto; margin-right: auto;'; + } else { + // add a float style to the existing style + $options['attr']['style'] .= + 'float: '.$options['attr']['align']; + } + + // unset so it won't show up as an attribute + unset($options['attr']['align']); + } + + // stephane@metacites.net -- 25/07/2004 + // try to guess width and height + if (! isset($options['attr']['width']) && + ! isset($options['attr']['height'])) { + + // does the source refer to a local file or a URL? + if (strpos($src,'://')) { + // is a URL link + $imageFile = $src; + } elseif ($src[0] == '.') { + // reg at dav-muz dot net -- 2005-03-07 + // is a local file on relative path. + $imageFile = $src; # ...don't do anything because it's perfect! + } else { + // is a local file on absolute path. + $imageFile = $_SERVER['DOCUMENT_ROOT'] . $src; + } + + // attempt to get the image size + $imageSize = @getimagesize($imageFile); + + if (is_array($imageSize)) { + $options['attr']['width'] = $imageSize[0]; + $options['attr']['height'] = $imageSize[1]; + } + + } + + // start the HTML output + $output = 'formatConf(' class="%s"', 'css'); + + // add the attributes to the output, and be sure to + // track whether or not we find an "alt" attribute + $alt = false; + foreach ($options['attr'] as $key => $val) { + + // track the 'alt' attribute + if (strtolower($key) == 'alt') { + $alt = true; + } + + // the 'class' attribute overrides the CSS class conf + if (strtolower($key) == 'class') { + $css = null; + } + + $key = $this->textEncode($key); + $val = $this->textEncode($val); + $output .= " $key=\"$val\""; + } + + // always add an "alt" attribute per Stephane Solliec + if (! $alt) { + $alt = $this->textEncode(basename($options['src'])); + $output .= " alt=\"$alt\""; + } + + // end the image tag with the automatic CSS class (if any) + $output .= "$css />"; + + // was the image clickable? + if ($href) { + // yes, add the href and return + $href = $this->textEncode($href); + $css = $this->formatConf(' class="%s"', 'css_link'); + $output = "$output"; + } + + return $output; + } +} +?> diff --git a/includes/pear/Text/Wiki/Render/Xhtml/Include.php b/includes/pear/Text/Wiki/Render/Xhtml/Include.php new file mode 100644 index 0000000..a152c40 --- /dev/null +++ b/includes/pear/Text/Wiki/Render/Xhtml/Include.php @@ -0,0 +1,32 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Include.php 191862 2005-07-30 08:03:29Z toggg $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders included maekup in XHTML. (empty) + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Include extends Text_Wiki_Render { + function token() + { + return ''; + } +} +?> diff --git a/includes/pear/Text/Wiki/Render/Xhtml/Interwiki.php b/includes/pear/Text/Wiki/Render/Xhtml/Interwiki.php new file mode 100644 index 0000000..9686b62 --- /dev/null +++ b/includes/pear/Text/Wiki/Render/Xhtml/Interwiki.php @@ -0,0 +1,103 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Interwiki.php 231896 2007-03-15 00:08:47Z justinpatrin $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders inter wikis links in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Interwiki extends Text_Wiki_Render { + + var $conf = array( + 'sites' => array( + 'MeatBall' => 'http://www.usemod.com/cgi-bin/mb.pl?%s', + 'Advogato' => 'http://advogato.org/%s', + 'Wiki' => 'http://c2.com/cgi/wiki?%s' + ), + 'target' => '_blank', + 'css' => null + ); + + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + $text = $options['text']; + if (isset($options['url'])) { + // calculated by the parser (e.g. Mediawiki) + $href = $options['url']; + } else { + $site = $options['site']; + // toggg 2006/02/05 page name must be url encoded (e.g. may contain spaces) + $page = $this->urlEncode($options['page']); + + if (isset($this->conf['sites'][$site])) { + $href = $this->conf['sites'][$site]; + } else { + return $text; + } + + // old form where page is at end, + // or new form with %s placeholder for sprintf()? + if (strpos($href, '%s') === false) { + // use the old form + $href = $href . $page; + } else { + // use the new form + $href = sprintf($href, $page); + } + } + + // allow for alternative targets + $target = $this->getConf('target'); + + // build base link + $css = $this->formatConf(' class="%s"', 'css'); + $text = $this->textEncode($text); + $output = "textEncode($target); + $output .= " onclick=\"window.open(this.href, '$target');"; + $output .= " return false;\""; + } + + $output .= ">$text"; + + return $output; + } +} +?> diff --git a/includes/pear/Text/Wiki/Render/Xhtml/Italic.php b/includes/pear/Text/Wiki/Render/Xhtml/Italic.php new file mode 100644 index 0000000..b5cbed5 --- /dev/null +++ b/includes/pear/Text/Wiki/Render/Xhtml/Italic.php @@ -0,0 +1,57 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Italic.php 191862 2005-07-30 08:03:29Z toggg $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders italic text in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Italic extends Text_Wiki_Render { + + var $conf = array( + 'css' => null + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + if ($options['type'] == 'start') { + $css = $this->formatConf(' class="%s"', 'css'); + return ""; + } + + if ($options['type'] == 'end') { + return ''; + } + } +} +?> diff --git a/includes/pear/Text/Wiki/Render/Xhtml/List.php b/includes/pear/Text/Wiki/Render/Xhtml/List.php new file mode 100644 index 0000000..8ec06cb --- /dev/null +++ b/includes/pear/Text/Wiki/Render/Xhtml/List.php @@ -0,0 +1,172 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: List.php 200073 2005-11-06 10:38:25Z toggg $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders bullet and ordered lists in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_List extends Text_Wiki_Render { + + var $conf = array( + 'css_ol' => null, + 'css_ol_li' => null, + 'css_ul' => null, + 'css_ul_li' => null + ); + + /** + * + * Renders a token into text matching the requested format. + * + * This rendering method is syntactically and semantically compliant + * with XHTML 1.1 in that sub-lists are part of the previous list item. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + // make nice variables (type, level, count) + extract($options); + + // set up indenting so that the results look nice; we do this + // in two steps to avoid str_pad mathematics. ;-) + $pad = str_pad('', $level, "\t"); + $pad = str_replace("\t", ' ', $pad); + + switch ($type) { + + case 'bullet_list_start': + + // build the base HTML + $css = $this->formatConf(' class="%s"', 'css_ul'); + $html = ""; + + /* + // if this is the opening block for the list, + // put an extra newline in front of it so the + // output looks nice. + if ($level == 0) { + $html = "\n$html"; + } + */ + + // done! + return $html; + break; + + case 'bullet_list_end': + + // build the base HTML + $html = "\n$pad"; + + // if this is the closing block for the list, + // put extra newlines after it so the output + // looks nice. + if ($level == 0) { + $html .= "\n\n"; + } + + // done! + return $html; + break; + + case 'number_list_start': + if (isset($format)) { + $format = ' type="' . $format . '"'; + } else { + $format = ''; + } + // build the base HTML + $css = $this->formatConf(' class="%s"', 'css_ol'); + $html = ""; + + /* + // if this is the opening block for the list, + // put an extra newline in front of it so the + // output looks nice. + if ($level == 0) { + $html = "\n$html"; + } + */ + + // done! + return $html; + break; + + case 'number_list_end': + + // build the base HTML + $html = "\n$pad"; + + // if this is the closing block for the list, + // put extra newlines after it so the output + // looks nice. + if ($level == 0) { + $html .= "\n\n"; + } + + // done! + return $html; + break; + + case 'bullet_item_start': + case 'number_item_start': + + // pick the proper CSS class + if ($type == 'bullet_item_start') { + $css = $this->formatConf(' class="%s"', 'css_ul_li'); + } else { + $css = $this->formatConf(' class="%s"', 'css_ol_li'); + } + + // build the base HTML + $html = "\n$pad"; + + // for the very first item in the list, do nothing. + // but for additional items, be sure to close the + // previous item. + if ($count > 0) { + $html = "$html"; + } + + // done! + return $html; + break; + + case 'bullet_item_end': + case 'number_item_end': + default: + // ignore item endings and all other types. + // item endings are taken care of by the other types + // depending on their place in the list. + return ''; + break; + } + } +} +?> diff --git a/includes/pear/Text/Wiki/Render/Xhtml/Newline.php b/includes/pear/Text/Wiki/Render/Xhtml/Newline.php new file mode 100644 index 0000000..19676c6 --- /dev/null +++ b/includes/pear/Text/Wiki/Render/Xhtml/Newline.php @@ -0,0 +1,35 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Newline.php 191862 2005-07-30 08:03:29Z toggg $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders new lines in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Newline extends Text_Wiki_Render { + + + function token($options) + { + return "
    \n"; + } +} + +?> diff --git a/includes/pear/Text/Wiki/Render/Xhtml/Page.php b/includes/pear/Text/Wiki/Render/Xhtml/Page.php new file mode 100644 index 0000000..d9f638b --- /dev/null +++ b/includes/pear/Text/Wiki/Render/Xhtml/Page.php @@ -0,0 +1,46 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Page.php 191862 2005-07-30 08:03:29Z toggg $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders page markers in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Page extends Text_Wiki_Render { + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + return 'PAGE MARKER HERE*&^%$#^$%*PAGEMARKERHERE'; + } +} +?> diff --git a/includes/pear/Text/Wiki/Render/Xhtml/Paragraph.php b/includes/pear/Text/Wiki/Render/Xhtml/Paragraph.php new file mode 100644 index 0000000..fc73627 --- /dev/null +++ b/includes/pear/Text/Wiki/Render/Xhtml/Paragraph.php @@ -0,0 +1,59 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Paragraph.php 191862 2005-07-30 08:03:29Z toggg $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders paragraphs in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Paragraph extends Text_Wiki_Render { + + var $conf = array( + 'css' => null + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + extract($options); //type + + if ($type == 'start') { + $css = $this->formatConf(' class="%s"', 'css'); + return ""; + } + + if ($type == 'end') { + return "

    \n\n"; + } + } +} +?> diff --git a/includes/pear/Text/Wiki/Render/Xhtml/Phplookup.php b/includes/pear/Text/Wiki/Render/Xhtml/Phplookup.php new file mode 100644 index 0000000..e77b0b7 --- /dev/null +++ b/includes/pear/Text/Wiki/Render/Xhtml/Phplookup.php @@ -0,0 +1,81 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Phplookup.php 231896 2007-03-15 00:08:47Z justinpatrin $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders a link to php functions description in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Phplookup extends Text_Wiki_Render { + + var $conf = array( + 'target' => '_blank', + 'css' => null + ); + + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + $text = trim($options['text']); + $css = $this->formatConf(' class="%s"', 'css'); + + // start the html + $output = "getConf('target', ''); + if ($target && $target != '_self') { + // use a "popup" window. this is XHTML compliant, suggested by + // Aaron Kalin. uses the $target as the new window name. + $target = $this->textEncode($target); + $output .= " onclick=\"window.open(this.href, '$target');"; + $output .= " return false;\""; + } + + // take off the final parens for functions + if (substr($text, -2) == '()') { + $q = substr($text, 0, -2); + } else { + $q = $text; + } + + // toggg 2006/02/05 page name must be url encoded (e.g. may contain spaces) + $q = $this->urlEncode($q); + $text = $this->textEncode($text); + + // finish and return + $output .= " href=\"http://php.net/$q\">$text"; + return $output; + } +} +?> diff --git a/includes/pear/Text/Wiki/Render/Xhtml/Plugin.php b/includes/pear/Text/Wiki/Render/Xhtml/Plugin.php new file mode 100644 index 0000000..048ba80 --- /dev/null +++ b/includes/pear/Text/Wiki/Render/Xhtml/Plugin.php @@ -0,0 +1,47 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Plugin.php 191862 2005-07-30 08:03:29Z toggg $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders wiki plugins in XHTML. (empty) + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Plugin extends Text_Wiki_Render { + + /** + * + * Renders a token into text matching the requested format. + * Plugins produce wiki markup so are processed by parsing, no tokens produced + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + return ''; + } +} +?> diff --git a/includes/pear/Text/Wiki/Render/Xhtml/Prefilter.php b/includes/pear/Text/Wiki/Render/Xhtml/Prefilter.php new file mode 100644 index 0000000..b0ae26e --- /dev/null +++ b/includes/pear/Text/Wiki/Render/Xhtml/Prefilter.php @@ -0,0 +1,34 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Prefilter.php 191862 2005-07-30 08:03:29Z toggg $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class implements a Text_Wiki_Render_Xhtml to "pre-filter" source text so + * that line endings are consistently \n, lines ending in a backslash \ + * are concatenated with the next line, and tabs are converted to spaces. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Prefilter extends Text_Wiki_Render { + function token() + { + return ''; + } +} +?> diff --git a/includes/pear/Text/Wiki/Render/Xhtml/Preformatted.php b/includes/pear/Text/Wiki/Render/Xhtml/Preformatted.php new file mode 100644 index 0000000..a68ce76 --- /dev/null +++ b/includes/pear/Text/Wiki/Render/Xhtml/Preformatted.php @@ -0,0 +1,47 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Preformatted.php 229275 2007-02-07 13:40:44Z mic $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders preformated text in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Preformatted extends Text_Wiki_Render { + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + $text = $this->textEncode($options['text']); + return '
    '.$text.'
    '; + } +} +?> diff --git a/includes/pear/Text/Wiki/Render/Xhtml/Raw.php b/includes/pear/Text/Wiki/Render/Xhtml/Raw.php new file mode 100644 index 0000000..3027153 --- /dev/null +++ b/includes/pear/Text/Wiki/Render/Xhtml/Raw.php @@ -0,0 +1,46 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Raw.php 214538 2006-06-09 21:32:24Z justinpatrin $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders not processed blocks in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Raw extends Text_Wiki_Render { + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + return $this->textEncode($options['text']); + } +} +?> diff --git a/includes/pear/Text/Wiki/Render/Xhtml/Revise.php b/includes/pear/Text/Wiki/Render/Xhtml/Revise.php new file mode 100644 index 0000000..f81fd7f --- /dev/null +++ b/includes/pear/Text/Wiki/Render/Xhtml/Revise.php @@ -0,0 +1,68 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Revise.php 191862 2005-07-30 08:03:29Z toggg $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders revision marks in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Revise extends Text_Wiki_Render { + + var $conf = array( + 'css_ins' => null, + 'css_del' => null + ); + + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + if ($options['type'] == 'del_start') { + $css = $this->formatConf(' class="%s"', 'css_del'); + return ""; + } + + if ($options['type'] == 'del_end') { + return ""; + } + + if ($options['type'] == 'ins_start') { + $css = $this->formatConf(' class="%s"', 'css_ins'); + return ""; + } + + if ($options['type'] == 'ins_end') { + return ""; + } + } +} +?> diff --git a/includes/pear/Text/Wiki/Render/Xhtml/Smiley.php b/includes/pear/Text/Wiki/Render/Xhtml/Smiley.php new file mode 100644 index 0000000..778796b --- /dev/null +++ b/includes/pear/Text/Wiki/Render/Xhtml/Smiley.php @@ -0,0 +1,74 @@ + + * @copyright 2005 bertrand Gugger + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Smiley.php 206940 2006-02-10 23:07:03Z toggg $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * Smiley rule Xhtml render class + * + * @category Text + * @package Text_Wiki + * @author Bertrand Gugger + * @copyright 2005 bertrand Gugger + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + * @see Text_Wiki::Text_Wiki_Render() + */ +class Text_Wiki_Render_Xhtml_Smiley extends Text_Wiki_Render { + + /** + * Configuration keys for this rule + * 'prefix' => the path to smileys images inclusive file name prefix, + * starts with '/' ==> abolute reference + * if no file names prefix but some folder, terminates with '/' + * 'extension' => the file extension (inclusive '.'), e.g. : + * if prefix 'smileys/icon_' and extension '.gif' + * ':)' whose name is 'smile' will give relative file 'smileys/icon_smile.gif' + * if prefix '/image/smileys/' and extension '.png': absolute '/image/smileys/smile.gif' + * 'css' => optional style applied to smileys + * + * @access public + * @var array 'config-key' => mixed config-value + */ + var $conf = array( + 'prefix' => 'images/smiles/icon_', + 'extension' => '.gif', + 'css' => null + ); + + /** + * Renders a token into text matching the requested format. + * process the Smileys + * + * @access public + * @param array $options The "options" portion of the token (second element). + * @return string The text rendered from the token options. + */ + function token($options) + { + $imageFile = $this->getConf('prefix') . $options['name'] . $this->getConf('extension'); + + // attempt to get the image size + $imageSize = @getimagesize($imageFile); + + // return the HTML output + return '' . $options['desc'] . 'formatConf(' class="%s"', 'css') . ' />'; + } +} +?> diff --git a/includes/pear/Text/Wiki/Render/Xhtml/Specialchar.php b/includes/pear/Text/Wiki/Render/Xhtml/Specialchar.php new file mode 100644 index 0000000..4372c02 --- /dev/null +++ b/includes/pear/Text/Wiki/Render/Xhtml/Specialchar.php @@ -0,0 +1,52 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Specialchar.php 191862 2005-07-30 08:03:29Z toggg $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders special characters in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_SpecialChar extends Text_Wiki_Render { + + var $types = array('~bs~' => '\', + '~hs~' => ' ', + '~amp~' => '&', + '~ldq~' => '“', + '~rdq~' => '”', + '~lsq~' => '‘', + '~rsq~' => '’', + '~c~' => '©', + '~--~' => '—', + '" -- "' => '—', + '" -- "' => '—', + '~lt~' => '<', + '~gt~' => '>'); + + function token($options) + { + if (isset($this->types[$options['char']])) { + return $this->types[$options['char']]; + } else { + return '&#'.substr($options['char'], 1, -1).';'; + } + } +} + +?> diff --git a/includes/pear/Text/Wiki/Render/Xhtml/Strong.php b/includes/pear/Text/Wiki/Render/Xhtml/Strong.php new file mode 100644 index 0000000..da816a7 --- /dev/null +++ b/includes/pear/Text/Wiki/Render/Xhtml/Strong.php @@ -0,0 +1,58 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Strong.php 191862 2005-07-30 08:03:29Z toggg $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders text marked as strong in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Strong extends Text_Wiki_Render { + + + var $conf = array( + 'css' => null + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + if ($options['type'] == 'start') { + $css = $this->formatConf(' class="%s"', 'css'); + return ""; + } + + if ($options['type'] == 'end') { + return ''; + } + } +} +?> diff --git a/includes/pear/Text/Wiki/Render/Xhtml/Subscript.php b/includes/pear/Text/Wiki/Render/Xhtml/Subscript.php new file mode 100644 index 0000000..653a110 --- /dev/null +++ b/includes/pear/Text/Wiki/Render/Xhtml/Subscript.php @@ -0,0 +1,57 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Subscript.php 191862 2005-07-30 08:03:29Z toggg $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders subscript text in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Subscript extends Text_Wiki_Render { + + var $conf = array( + 'css' => null + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + if ($options['type'] == 'start') { + $css = $this->formatConf(' class="%s"', 'css'); + return ""; + } + + if ($options['type'] == 'end') { + return ''; + } + } +} +?> diff --git a/includes/pear/Text/Wiki/Render/Xhtml/Superscript.php b/includes/pear/Text/Wiki/Render/Xhtml/Superscript.php new file mode 100644 index 0000000..1a39453 --- /dev/null +++ b/includes/pear/Text/Wiki/Render/Xhtml/Superscript.php @@ -0,0 +1,57 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Superscript.php 191862 2005-07-30 08:03:29Z toggg $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders superscript text in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Superscript extends Text_Wiki_Render { + + var $conf = array( + 'css' => null + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + if ($options['type'] == 'start') { + $css = $this->formatConf(' class="%s"', 'css'); + return ""; + } + + if ($options['type'] == 'end') { + return ''; + } + } +} +?> diff --git a/includes/pear/Text/Wiki/Render/Xhtml/Table.php b/includes/pear/Text/Wiki/Render/Xhtml/Table.php new file mode 100644 index 0000000..670ff7b --- /dev/null +++ b/includes/pear/Text/Wiki/Render/Xhtml/Table.php @@ -0,0 +1,140 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Table.php 202250 2005-12-06 15:29:29Z ritzmo $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders tables in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Table extends Text_Wiki_Render { + + var $conf = array( + 'css_table' => null, + 'css_caption' => null, + 'css_tr' => null, + 'css_th' => null, + 'css_td' => null + ); + + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + // make nice variable names (type, attr, span) + $span = $rowspan = 1; + extract($options); + + // free format + $format = isset($format) ? ' '. $format : ''; + + $pad = ' '; + + switch ($type) { + + case 'table_start': + $css = $this->formatConf(' class="%s"', 'css_table'); + return "\n\n\n"; + break; + + case 'table_end': + return "\n\n"; + break; + + case 'caption_start': + $css = $this->formatConf(' class="%s"', 'css_caption'); + return "\n"; + break; + + case 'caption_end': + return "\n"; + break; + + case 'row_start': + $css = $this->formatConf(' class="%s"', 'css_tr'); + return "$pad\n"; + break; + + case 'row_end': + return "$pad\n"; + break; + + case 'cell_start': + + // base html + $html = $pad . $pad; + + // is this a TH or TD cell? + if ($attr == 'header') { + // start a header cell + $css = $this->formatConf(' class="%s"', 'css_th'); + $html .= "formatConf(' class="%s"', 'css_td'); + $html .= " 1) { + $html .= " colspan=\"$span\""; + } + + // add the row span + if ($rowspan > 1) { + $html .= " rowspan=\"$rowspan\""; + } + + // add alignment + if ($attr != 'header' && $attr != '') { + $html .= " style=\"text-align: $attr;\""; + } + + // done! + $html .= "$format>"; + return $html; + break; + + case 'cell_end': + if ($attr == 'header') { + return "\n"; + } else { + return "\n"; + } + break; + + default: + return ''; + + } + } +} +?> diff --git a/includes/pear/Text/Wiki/Render/Xhtml/Tighten.php b/includes/pear/Text/Wiki/Render/Xhtml/Tighten.php new file mode 100644 index 0000000..30d6dfd --- /dev/null +++ b/includes/pear/Text/Wiki/Render/Xhtml/Tighten.php @@ -0,0 +1,34 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Tighten.php 191862 2005-07-30 08:03:29Z toggg $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class makes the tightening in XHTML. (empty) + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Tighten extends Text_Wiki_Render { + + + function token() + { + return ''; + } +} +?> diff --git a/includes/pear/Text/Wiki/Render/Xhtml/Titlebar.php b/includes/pear/Text/Wiki/Render/Xhtml/Titlebar.php new file mode 100644 index 0000000..d6e24a8 --- /dev/null +++ b/includes/pear/Text/Wiki/Render/Xhtml/Titlebar.php @@ -0,0 +1,57 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Titlebar.php 191862 2005-07-30 08:03:29Z toggg $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders a title bar in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Titlebar extends Text_Wiki_Render { + + var $conf = array( + 'css' => 'titlebar' + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + if ($options['type'] == 'start') { + $css = $this->formatConf(' class="%s"', 'css'); + return ""; + } + + if ($options['type'] == 'end') { + return '
    '; + } + } +} +?> diff --git a/includes/pear/Text/Wiki/Render/Xhtml/Toc.php b/includes/pear/Text/Wiki/Render/Xhtml/Toc.php new file mode 100644 index 0000000..2b68b68 --- /dev/null +++ b/includes/pear/Text/Wiki/Render/Xhtml/Toc.php @@ -0,0 +1,115 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Toc.php 191862 2005-07-30 08:03:29Z toggg $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class inserts a table of content in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Toc extends Text_Wiki_Render { + + var $conf = array( + 'css_list' => null, + 'css_item' => null, + 'title' => 'Table of Contents', + 'div_id' => 'toc', + 'collapse' => true + ); + + var $min = 2; + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + // type, id, level, count, attr + extract($options); + + switch ($type) { + + case 'list_start': + + $css = $this->getConf('css_list'); + $html = ''; + + // collapse div within a table? + if ($this->getConf('collapse')) { + $html .= ''; + $html .= "
    \n"; + } + + // add the div, class, and id + $html .= 'getConf('div_id'); + if ($div_id) { + $html .= " id=\"$div_id\""; + } + + // add the title, and done + $html .= '>'; + $html .= $this->getConf('title'); + return $html; + break; + + case 'list_end': + if ($this->getConf('collapse')) { + return "\n\n
    \n\n"; + } else { + return "\n
    \n\n"; + } + break; + + case 'item_start': + $html = "\n\tgetConf('css_item'); + if ($css) { + $html .= " class=\"$css\""; + } + + $pad = ($level - $this->min); + $html .= " style=\"margin-left: {$pad}em;\">"; + + $html .= ""; + return $html; + break; + + case 'item_end': + return "
    "; + break; + } + } +} +?> diff --git a/includes/pear/Text/Wiki/Render/Xhtml/Tt.php b/includes/pear/Text/Wiki/Render/Xhtml/Tt.php new file mode 100644 index 0000000..3cefe85 --- /dev/null +++ b/includes/pear/Text/Wiki/Render/Xhtml/Tt.php @@ -0,0 +1,58 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Tt.php 191862 2005-07-30 08:03:29Z toggg $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders monospaced text in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Tt extends Text_Wiki_Render { + + + var $conf = array( + 'css' => null + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + if ($options['type'] == 'start') { + $css = $this->formatConf(' class="%s"', 'css'); + return ""; + } + + if ($options['type'] == 'end') { + return ''; + } + } +} +?> diff --git a/includes/pear/Text/Wiki/Render/Xhtml/Underline.php b/includes/pear/Text/Wiki/Render/Xhtml/Underline.php new file mode 100644 index 0000000..a6cbf59 --- /dev/null +++ b/includes/pear/Text/Wiki/Render/Xhtml/Underline.php @@ -0,0 +1,57 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Underline.php 191862 2005-07-30 08:03:29Z toggg $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders underlined text in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Underline extends Text_Wiki_Render { + + var $conf = array( + 'css' => null + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + if ($options['type'] == 'start') { + $css = $this->formatConf(' class="%s"', 'css'); + return ""; + } + + if ($options['type'] == 'end') { + return ''; + } + } +} +?> diff --git a/includes/pear/Text/Wiki/Render/Xhtml/Url.php b/includes/pear/Text/Wiki/Render/Xhtml/Url.php new file mode 100644 index 0000000..9139be7 --- /dev/null +++ b/includes/pear/Text/Wiki/Render/Xhtml/Url.php @@ -0,0 +1,131 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Url.php 236400 2007-05-26 17:15:41Z mic $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders URL links in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Url extends Text_Wiki_Render { + + + var $conf = array( + 'target' => '_blank', + 'images' => true, + 'img_ext' => array('jpg', 'jpeg', 'gif', 'png'), + 'css_inline' => null, + 'css_footnote' => null, + 'css_descr' => null, + 'css_img' => null + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + // create local variables from the options array (text, + // href, type) + extract($options); + + // find the rightmost dot and determine the filename + // extension. + $pos = strrpos($href, '.'); + $ext = strtolower(substr($href, $pos + 1)); + $href = $this->textEncode($href); + + // does the filename extension indicate an image file? + if ($this->getConf('images') && + in_array($ext, $this->getConf('img_ext', array()))) { + + // create alt text for the image + if (! isset($text) || $text == '') { + $text = basename($href); + $text = $this->textEncode($text); + } + + // generate an image tag + $css = $this->formatConf(' class="%s"', 'css_img'); + $start = ""; + + } else { + + // should we build a target clause? + if ($href{0} == '#' || + strtolower(substr($href, 0, 7)) == 'mailto:') { + // targets not allowed for on-page anchors + // and mailto: links. + $target = ''; + } else { + // allow targets on non-anchor non-mailto links + $target = $this->getConf('target'); + } + + // generate a regular link (not an image) + $text = $this->textEncode($text); + $css = $this->formatConf(' class="%s"', "css_$type"); + $start = "textEncode($target); + $start .= " onclick=\"window.open(this.href, '$target');"; + $start .= " return false;\""; + } + + if (isset($name)) { + $start .= " id=\"$name\""; + } + + // finish up output + $start .= ">"; + $end = ""; + + // make numbered references look like footnotes when no + // CSS class specified, make them superscript by default + if ($type == 'footnote' && ! $css) { + $start = '' . $start; + $end = $end . ''; + } + } + + if ($options['type'] == 'start') { + $output = $start; + } else if ($options['type'] == 'end') { + $output = $end; + } else { + $output = $start . $text . $end; + } + return $output; + } +} +?> diff --git a/includes/pear/Text/Wiki/Render/Xhtml/Wikilink.php b/includes/pear/Text/Wiki/Render/Xhtml/Wikilink.php new file mode 100644 index 0000000..b93b000 --- /dev/null +++ b/includes/pear/Text/Wiki/Render/Xhtml/Wikilink.php @@ -0,0 +1,177 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Wikilink.php 224670 2006-12-08 21:25:24Z justinpatrin $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders wiki links in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Wikilink extends Text_Wiki_Render { + + var $conf = array( + 'pages' => array(), // set to null or false to turn off page checks + 'view_url' => 'http://example.com/index.php?page=%s', + 'new_url' => 'http://example.com/new.php?page=%s', + 'new_text' => '?', + 'new_text_pos' => 'after', // 'before', 'after', or null/false + 'css' => null, + 'css_new' => null, + 'exists_callback' => null // call_user_func() callback + ); + + + /** + * + * Renders a token into XHTML. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + // make nice variable names (page, anchor, text) + extract($options); + + // is there a "page existence" callback? + // we need to access it directly instead of through + // getConf() because we'll need a reference (for + // object instance method callbacks). + if (isset($this->conf['exists_callback'])) { + $callback =& $this->conf['exists_callback']; + } else { + $callback = false; + } + + if ($callback) { + // use the callback function + $exists = call_user_func($callback, $page); + } else { + // no callback, go to the naive page array. + $list = $this->getConf('pages'); + if (is_array($list)) { + // yes, check against the page list + $exists = in_array($page, $list); + } else { + // no, assume it exists + $exists = true; + } + } + + $anchor = '#'.$this->urlEncode(substr($anchor, 1)); + + // does the page exist? + if ($exists) { + + // PAGE EXISTS. + + // link to the page view, but we have to build + // the HREF. we support both the old form where + // the page always comes at the end, and the new + // form that uses %s for sprintf() + $href = $this->getConf('view_url'); + + if (strpos($href, '%s') === false) { + // use the old form (page-at-end) + $href = $href . $this->urlEncode($page) . $anchor; + } else { + // use the new form (sprintf format string) + $href = sprintf($href, $this->urlEncode($page)) . $anchor; + } + + // get the CSS class and generate output + $css = ' class="'.$this->textEncode($this->getConf('css')).'"'; + + $start = ''; + $end = ''; + } else { + + // PAGE DOES NOT EXIST. + + // link to a create-page url, but only if new_url is set + $href = $this->getConf('new_url', null); + + // set the proper HREF + if (! $href || trim($href) == '') { + + // no useful href, return the text as it is + //TODO: This is no longer used, need to look closer into this branch + $output = $text; + + } else { + + // yes, link to the new-page href, but we have to build + // it. we support both the old form where + // the page always comes at the end, and the new + // form that uses sprintf() + if (strpos($href, '%s') === false) { + // use the old form + $href = $href . $this->urlEncode($page); + } else { + // use the new form + $href = sprintf($href, $this->urlEncode($page)); + } + } + + // get the appropriate CSS class and new-link text + $css = ' class="'.$this->textEncode($this->getConf('css_new')).'"'; + $new = $this->getConf('new_text'); + + // what kind of linking are we doing? + $pos = $this->getConf('new_text_pos'); + if (! $pos || ! $new) { + // no position (or no new_text), use css only on the page name + + $start = ''; + $end = ''; + } elseif ($pos == 'before') { + // use the new_text BEFORE the page name + $start = ''.$this->textEncode($new).''; + $end = ''; + } else { + // default, use the new_text link AFTER the page name + $start = ''; + $end = ''.$this->textEncode($new).''; + } + } + if (!strlen($text)) { + $start .= $this->textEncode($page); + } + if (isset($type)) { + switch ($type) { + case 'start': + $output = $start; + break; + case 'end': + $output = $end; + break; + } + } else { + $output = $start.$this->textEncode($text).$end; + } + return $output; + } +} +?> diff --git a/includes/pear/XML/Util.php b/includes/pear/XML/Util.php new file mode 100644 index 0000000..f5927b1 --- /dev/null +++ b/includes/pear/XML/Util.php @@ -0,0 +1,911 @@ + + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @category XML + * @package XML_Util + * @author Stephan Schmidt + * @copyright 2003-2008 Stephan Schmidt + * @license http://opensource.org/licenses/bsd-license New BSD License + * @version CVS: $Id: Util.php,v 1.38 2008/11/13 00:03:38 ashnazg Exp $ + * @link http://pear.php.net/package/XML_Util + */ + +/** + * error code for invalid chars in XML name + */ +define('XML_UTIL_ERROR_INVALID_CHARS', 51); + +/** + * error code for invalid chars in XML name + */ +define('XML_UTIL_ERROR_INVALID_START', 52); + +/** + * error code for non-scalar tag content + */ +define('XML_UTIL_ERROR_NON_SCALAR_CONTENT', 60); + +/** + * error code for missing tag name + */ +define('XML_UTIL_ERROR_NO_TAG_NAME', 61); + +/** + * replace XML entities + */ +define('XML_UTIL_REPLACE_ENTITIES', 1); + +/** + * embedd content in a CData Section + */ +define('XML_UTIL_CDATA_SECTION', 5); + +/** + * do not replace entitites + */ +define('XML_UTIL_ENTITIES_NONE', 0); + +/** + * replace all XML entitites + * This setting will replace <, >, ", ' and & + */ +define('XML_UTIL_ENTITIES_XML', 1); + +/** + * replace only required XML entitites + * This setting will replace <, " and & + */ +define('XML_UTIL_ENTITIES_XML_REQUIRED', 2); + +/** + * replace HTML entitites + * @link http://www.php.net/htmlentities + */ +define('XML_UTIL_ENTITIES_HTML', 3); + +/** + * Collapse all empty tags. + */ +define('XML_UTIL_COLLAPSE_ALL', 1); + +/** + * Collapse only empty XHTML tags that have no end tag. + */ +define('XML_UTIL_COLLAPSE_XHTML_ONLY', 2); + +/** + * utility class for working with XML documents + * + + * @category XML + * @package XML_Util + * @author Stephan Schmidt + * @copyright 2003-2008 Stephan Schmidt + * @license http://opensource.org/licenses/bsd-license New BSD License + * @version Release: 1.2.1 + * @link http://pear.php.net/package/XML_Util + */ +class XML_Util +{ + /** + * return API version + * + * @return string $version API version + * @access public + * @static + */ + function apiVersion() + { + return '1.1'; + } + + /** + * replace XML entities + * + * With the optional second parameter, you may select, which + * entities should be replaced. + * + * + * require_once 'XML/Util.php'; + * + * // replace XML entites: + * $string = XML_Util::replaceEntities('This string contains < & >.'); + * + * + * With the optional third parameter, you may pass the character encoding + * + * require_once 'XML/Util.php'; + * + * // replace XML entites in UTF-8: + * $string = XML_Util::replaceEntities( + * 'This string contains < & > as well as ä, ö, ß, à and ê', + * XML_UTIL_ENTITIES_HTML, + * 'UTF-8' + * ); + * + * + * @param string $string string where XML special chars + * should be replaced + * @param int $replaceEntities setting for entities in attribute values + * (one of XML_UTIL_ENTITIES_XML, + * XML_UTIL_ENTITIES_XML_REQUIRED, + * XML_UTIL_ENTITIES_HTML) + * @param string $encoding encoding value (if any)... + * must be a valid encoding as determined + * by the htmlentities() function + * + * @return string string with replaced chars + * @access public + * @static + * @see reverseEntities() + */ + function replaceEntities($string, $replaceEntities = XML_UTIL_ENTITIES_XML, + $encoding = 'ISO-8859-1') + { + switch ($replaceEntities) { + case XML_UTIL_ENTITIES_XML: + return strtr($string, array( + '&' => '&', + '>' => '>', + '<' => '<', + '"' => '"', + '\'' => ''' )); + break; + case XML_UTIL_ENTITIES_XML_REQUIRED: + return strtr($string, array( + '&' => '&', + '<' => '<', + '"' => '"' )); + break; + case XML_UTIL_ENTITIES_HTML: + return htmlentities($string, ENT_COMPAT, $encoding); + break; + } + return $string; + } + + /** + * reverse XML entities + * + * With the optional second parameter, you may select, which + * entities should be reversed. + * + * + * require_once 'XML/Util.php'; + * + * // reverse XML entites: + * $string = XML_Util::reverseEntities('This string contains < & >.'); + * + * + * With the optional third parameter, you may pass the character encoding + * + * require_once 'XML/Util.php'; + * + * // reverse XML entites in UTF-8: + * $string = XML_Util::reverseEntities( + * 'This string contains < & > as well as' + * . ' ä, ö, ß, à and ê', + * XML_UTIL_ENTITIES_HTML, + * 'UTF-8' + * ); + * + * + * @param string $string string where XML special chars + * should be replaced + * @param int $replaceEntities setting for entities in attribute values + * (one of XML_UTIL_ENTITIES_XML, + * XML_UTIL_ENTITIES_XML_REQUIRED, + * XML_UTIL_ENTITIES_HTML) + * @param string $encoding encoding value (if any)... + * must be a valid encoding as determined + * by the html_entity_decode() function + * + * @return string string with replaced chars + * @access public + * @static + * @see replaceEntities() + */ + function reverseEntities($string, $replaceEntities = XML_UTIL_ENTITIES_XML, + $encoding = 'ISO-8859-1') + { + switch ($replaceEntities) { + case XML_UTIL_ENTITIES_XML: + return strtr($string, array( + '&' => '&', + '>' => '>', + '<' => '<', + '"' => '"', + ''' => '\'' )); + break; + case XML_UTIL_ENTITIES_XML_REQUIRED: + return strtr($string, array( + '&' => '&', + '<' => '<', + '"' => '"' )); + break; + case XML_UTIL_ENTITIES_HTML: + return html_entity_decode($string, ENT_COMPAT, $encoding); + break; + } + return $string; + } + + /** + * build an xml declaration + * + * + * require_once 'XML/Util.php'; + * + * // get an XML declaration: + * $xmlDecl = XML_Util::getXMLDeclaration('1.0', 'UTF-8', true); + * + * + * @param string $version xml version + * @param string $encoding character encoding + * @param bool $standalone document is standalone (or not) + * + * @return string xml declaration + * @access public + * @static + * @uses attributesToString() to serialize the attributes of the XML declaration + */ + function getXMLDeclaration($version = '1.0', $encoding = null, + $standalone = null) + { + $attributes = array( + 'version' => $version, + ); + // add encoding + if ($encoding !== null) { + $attributes['encoding'] = $encoding; + } + // add standalone, if specified + if ($standalone !== null) { + $attributes['standalone'] = $standalone ? 'yes' : 'no'; + } + + return sprintf('', + XML_Util::attributesToString($attributes, false)); + } + + /** + * build a document type declaration + * + * + * require_once 'XML/Util.php'; + * + * // get a doctype declaration: + * $xmlDecl = XML_Util::getDocTypeDeclaration('rootTag','myDocType.dtd'); + * + * + * @param string $root name of the root tag + * @param string $uri uri of the doctype definition + * (or array with uri and public id) + * @param string $internalDtd internal dtd entries + * + * @return string doctype declaration + * @access public + * @static + * @since 0.2 + */ + function getDocTypeDeclaration($root, $uri = null, $internalDtd = null) + { + if (is_array($uri)) { + $ref = sprintf(' PUBLIC "%s" "%s"', $uri['id'], $uri['uri']); + } elseif (!empty($uri)) { + $ref = sprintf(' SYSTEM "%s"', $uri); + } else { + $ref = ''; + } + + if (empty($internalDtd)) { + return sprintf('', $root, $ref); + } else { + return sprintf("", $root, $ref, $internalDtd); + } + } + + /** + * create string representation of an attribute list + * + * + * require_once 'XML/Util.php'; + * + * // build an attribute string + * $att = array( + * 'foo' => 'bar', + * 'argh' => 'tomato' + * ); + * + * $attList = XML_Util::attributesToString($att); + * + * + * @param array $attributes attribute array + * @param bool|array $sort sort attribute list alphabetically, + * may also be an assoc array containing + * the keys 'sort', 'multiline', 'indent', + * 'linebreak' and 'entities' + * @param bool $multiline use linebreaks, if more than + * one attribute is given + * @param string $indent string used for indentation of + * multiline attributes + * @param string $linebreak string used for linebreaks of + * multiline attributes + * @param int $entities setting for entities in attribute values + * (one of XML_UTIL_ENTITIES_NONE, + * XML_UTIL_ENTITIES_XML, + * XML_UTIL_ENTITIES_XML_REQUIRED, + * XML_UTIL_ENTITIES_HTML) + * + * @return string string representation of the attributes + * @access public + * @static + * @uses replaceEntities() to replace XML entities in attribute values + * @todo allow sort also to be an options array + */ + function attributesToString($attributes, $sort = true, $multiline = false, + $indent = ' ', $linebreak = "\n", $entities = XML_UTIL_ENTITIES_XML) + { + /* + * second parameter may be an array + */ + if (is_array($sort)) { + if (isset($sort['multiline'])) { + $multiline = $sort['multiline']; + } + if (isset($sort['indent'])) { + $indent = $sort['indent']; + } + if (isset($sort['linebreak'])) { + $multiline = $sort['linebreak']; + } + if (isset($sort['entities'])) { + $entities = $sort['entities']; + } + if (isset($sort['sort'])) { + $sort = $sort['sort']; + } else { + $sort = true; + } + } + $string = ''; + if (is_array($attributes) && !empty($attributes)) { + if ($sort) { + ksort($attributes); + } + if ( !$multiline || count($attributes) == 1) { + foreach ($attributes as $key => $value) { + if ($entities != XML_UTIL_ENTITIES_NONE) { + if ($entities === XML_UTIL_CDATA_SECTION) { + $entities = XML_UTIL_ENTITIES_XML; + } + $value = XML_Util::replaceEntities($value, $entities); + } + $string .= ' ' . $key . '="' . $value . '"'; + } + } else { + $first = true; + foreach ($attributes as $key => $value) { + if ($entities != XML_UTIL_ENTITIES_NONE) { + $value = XML_Util::replaceEntities($value, $entities); + } + if ($first) { + $string .= ' ' . $key . '="' . $value . '"'; + $first = false; + } else { + $string .= $linebreak . $indent . $key . '="' . $value . '"'; + } + } + } + } + return $string; + } + + /** + * Collapses empty tags. + * + * @param string $xml XML + * @param int $mode Whether to collapse all empty tags (XML_UTIL_COLLAPSE_ALL) + * or only XHTML (XML_UTIL_COLLAPSE_XHTML_ONLY) ones. + * + * @return string XML + * @access public + * @static + * @todo PEAR CS - unable to avoid "space after open parens" error + * in the IF branch + */ + function collapseEmptyTags($xml, $mode = XML_UTIL_COLLAPSE_ALL) + { + if ($mode == XML_UTIL_COLLAPSE_XHTML_ONLY) { + return preg_replace( + '/<(area|base(?:font)?|br|col|frame|hr|img|input|isindex|link|meta|' + . 'param)([^>]*)><\/\\1>/s', + '<\\1\\2 />', + $xml); + } else { + return preg_replace('/<(\w+)([^>]*)><\/\\1>/s', '<\\1\\2 />', $xml); + } + } + + /** + * create a tag + * + * This method will call XML_Util::createTagFromArray(), which + * is more flexible. + * + * + * require_once 'XML/Util.php'; + * + * // create an XML tag: + * $tag = XML_Util::createTag('myNs:myTag', + * array('foo' => 'bar'), + * 'This is inside the tag', + * 'http://www.w3c.org/myNs#'); + * + * + * @param string $qname qualified tagname (including namespace) + * @param array $attributes array containg attributes + * @param mixed $content the content + * @param string $namespaceUri URI of the namespace + * @param int $replaceEntities whether to replace XML special chars in + * content, embedd it in a CData section + * or none of both + * @param bool $multiline whether to create a multiline tag where + * each attribute gets written to a single line + * @param string $indent string used to indent attributes + * (_auto indents attributes so they start + * at the same column) + * @param string $linebreak string used for linebreaks + * @param bool $sortAttributes Whether to sort the attributes or not + * + * @return string XML tag + * @access public + * @static + * @see createTagFromArray() + * @uses createTagFromArray() to create the tag + */ + function createTag($qname, $attributes = array(), $content = null, + $namespaceUri = null, $replaceEntities = XML_UTIL_REPLACE_ENTITIES, + $multiline = false, $indent = '_auto', $linebreak = "\n", + $sortAttributes = true) + { + $tag = array( + 'qname' => $qname, + 'attributes' => $attributes + ); + + // add tag content + if ($content !== null) { + $tag['content'] = $content; + } + + // add namespace Uri + if ($namespaceUri !== null) { + $tag['namespaceUri'] = $namespaceUri; + } + + return XML_Util::createTagFromArray($tag, $replaceEntities, $multiline, + $indent, $linebreak, $sortAttributes); + } + + /** + * create a tag from an array + * this method awaits an array in the following format + *
    +     * array(
    +     *     // qualified name of the tag
    +     *     'qname' => $qname        
    +     *
    +     *     // namespace prefix (optional, if qname is specified or no namespace)
    +     *     'namespace' => $namespace    
    +     *
    +     *     // local part of the tagname (optional, if qname is specified)
    +     *     'localpart' => $localpart,   
    +     *
    +     *     // array containing all attributes (optional)
    +     *     'attributes' => array(),      
    +     *
    +     *     // tag content (optional)
    +     *     'content' => $content,     
    +     *
    +     *     // namespaceUri for the given namespace (optional)
    +     *     'namespaceUri' => $namespaceUri 
    +     * )
    +     * 
    + * + * + * require_once 'XML/Util.php'; + * + * $tag = array( + * 'qname' => 'foo:bar', + * 'namespaceUri' => 'http://foo.com', + * 'attributes' => array('key' => 'value', 'argh' => 'fruit&vegetable'), + * 'content' => 'I\'m inside the tag', + * ); + * // creating a tag with qualified name and namespaceUri + * $string = XML_Util::createTagFromArray($tag); + * + * + * @param array $tag tag definition + * @param int $replaceEntities whether to replace XML special chars in + * content, embedd it in a CData section + * or none of both + * @param bool $multiline whether to create a multiline tag where each + * attribute gets written to a single line + * @param string $indent string used to indent attributes + * (_auto indents attributes so they start + * at the same column) + * @param string $linebreak string used for linebreaks + * @param bool $sortAttributes Whether to sort the attributes or not + * + * @return string XML tag + * @access public + * @static + * @see createTag() + * @uses attributesToString() to serialize the attributes of the tag + * @uses splitQualifiedName() to get local part and namespace of a qualified name + * @uses createCDataSection() + * @uses raiseError() + */ + function createTagFromArray($tag, $replaceEntities = XML_UTIL_REPLACE_ENTITIES, + $multiline = false, $indent = '_auto', $linebreak = "\n", + $sortAttributes = true) + { + if (isset($tag['content']) && !is_scalar($tag['content'])) { + return XML_Util::raiseError('Supplied non-scalar value as tag content', + XML_UTIL_ERROR_NON_SCALAR_CONTENT); + } + + if (!isset($tag['qname']) && !isset($tag['localPart'])) { + return XML_Util::raiseError('You must either supply a qualified name ' + . '(qname) or local tag name (localPart).', + XML_UTIL_ERROR_NO_TAG_NAME); + } + + // if no attributes hav been set, use empty attributes + if (!isset($tag['attributes']) || !is_array($tag['attributes'])) { + $tag['attributes'] = array(); + } + + if (isset($tag['namespaces'])) { + foreach ($tag['namespaces'] as $ns => $uri) { + $tag['attributes']['xmlns:' . $ns] = $uri; + } + } + + if (!isset($tag['qname'])) { + // qualified name is not given + + // check for namespace + if (isset($tag['namespace']) && !empty($tag['namespace'])) { + $tag['qname'] = $tag['namespace'] . ':' . $tag['localPart']; + } else { + $tag['qname'] = $tag['localPart']; + } + } elseif (isset($tag['namespaceUri']) && !isset($tag['namespace'])) { + // namespace URI is set, but no namespace + + $parts = XML_Util::splitQualifiedName($tag['qname']); + + $tag['localPart'] = $parts['localPart']; + if (isset($parts['namespace'])) { + $tag['namespace'] = $parts['namespace']; + } + } + + if (isset($tag['namespaceUri']) && !empty($tag['namespaceUri'])) { + // is a namespace given + if (isset($tag['namespace']) && !empty($tag['namespace'])) { + $tag['attributes']['xmlns:' . $tag['namespace']] = + $tag['namespaceUri']; + } else { + // define this Uri as the default namespace + $tag['attributes']['xmlns'] = $tag['namespaceUri']; + } + } + + // check for multiline attributes + if ($multiline === true) { + if ($indent === '_auto') { + $indent = str_repeat(' ', (strlen($tag['qname'])+2)); + } + } + + // create attribute list + $attList = XML_Util::attributesToString($tag['attributes'], + $sortAttributes, $multiline, $indent, $linebreak, $replaceEntities); + if (!isset($tag['content']) || (string)$tag['content'] == '') { + $tag = sprintf('<%s%s />', $tag['qname'], $attList); + } else { + switch ($replaceEntities) { + case XML_UTIL_ENTITIES_NONE: + break; + case XML_UTIL_CDATA_SECTION: + $tag['content'] = XML_Util::createCDataSection($tag['content']); + break; + default: + $tag['content'] = XML_Util::replaceEntities($tag['content'], + $replaceEntities); + break; + } + $tag = sprintf('<%s%s>%s', $tag['qname'], $attList, $tag['content'], + $tag['qname']); + } + return $tag; + } + + /** + * create a start element + * + * + * require_once 'XML/Util.php'; + * + * // create an XML start element: + * $tag = XML_Util::createStartElement('myNs:myTag', + * array('foo' => 'bar') ,'http://www.w3c.org/myNs#'); + * + * + * @param string $qname qualified tagname (including namespace) + * @param array $attributes array containg attributes + * @param string $namespaceUri URI of the namespace + * @param bool $multiline whether to create a multiline tag where each + * attribute gets written to a single line + * @param string $indent string used to indent attributes (_auto indents + * attributes so they start at the same column) + * @param string $linebreak string used for linebreaks + * @param bool $sortAttributes Whether to sort the attributes or not + * + * @return string XML start element + * @access public + * @static + * @see createEndElement(), createTag() + */ + function createStartElement($qname, $attributes = array(), $namespaceUri = null, + $multiline = false, $indent = '_auto', $linebreak = "\n", + $sortAttributes = true) + { + // if no attributes hav been set, use empty attributes + if (!isset($attributes) || !is_array($attributes)) { + $attributes = array(); + } + + if ($namespaceUri != null) { + $parts = XML_Util::splitQualifiedName($qname); + } + + // check for multiline attributes + if ($multiline === true) { + if ($indent === '_auto') { + $indent = str_repeat(' ', (strlen($qname)+2)); + } + } + + if ($namespaceUri != null) { + // is a namespace given + if (isset($parts['namespace']) && !empty($parts['namespace'])) { + $attributes['xmlns:' . $parts['namespace']] = $namespaceUri; + } else { + // define this Uri as the default namespace + $attributes['xmlns'] = $namespaceUri; + } + } + + // create attribute list + $attList = XML_Util::attributesToString($attributes, $sortAttributes, + $multiline, $indent, $linebreak); + $element = sprintf('<%s%s>', $qname, $attList); + return $element; + } + + /** + * create an end element + * + * + * require_once 'XML/Util.php'; + * + * // create an XML start element: + * $tag = XML_Util::createEndElement('myNs:myTag'); + * + * + * @param string $qname qualified tagname (including namespace) + * + * @return string XML end element + * @access public + * @static + * @see createStartElement(), createTag() + */ + function createEndElement($qname) + { + $element = sprintf('', $qname); + return $element; + } + + /** + * create an XML comment + * + * + * require_once 'XML/Util.php'; + * + * // create an XML start element: + * $tag = XML_Util::createComment('I am a comment'); + * + * + * @param string $content content of the comment + * + * @return string XML comment + * @access public + * @static + */ + function createComment($content) + { + $comment = sprintf('', $content); + return $comment; + } + + /** + * create a CData section + * + * + * require_once 'XML/Util.php'; + * + * // create a CData section + * $tag = XML_Util::createCDataSection('I am content.'); + * + * + * @param string $data data of the CData section + * + * @return string CData section with content + * @access public + * @static + */ + function createCDataSection($data) + { + return sprintf('', + preg_replace('/\]\]>/', ']]]]>', strval($data))); + + } + + /** + * split qualified name and return namespace and local part + * + * + * require_once 'XML/Util.php'; + * + * // split qualified tag + * $parts = XML_Util::splitQualifiedName('xslt:stylesheet'); + * + * the returned array will contain two elements: + *
    +     * array(
    +     *     'namespace' => 'xslt',
    +     *     'localPart' => 'stylesheet'
    +     * );
    +     * 
    + * + * @param string $qname qualified tag name + * @param string $defaultNs default namespace (optional) + * + * @return array array containing namespace and local part + * @access public + * @static + */ + function splitQualifiedName($qname, $defaultNs = null) + { + if (strstr($qname, ':')) { + $tmp = explode(':', $qname); + return array( + 'namespace' => $tmp[0], + 'localPart' => $tmp[1] + ); + } + return array( + 'namespace' => $defaultNs, + 'localPart' => $qname + ); + } + + /** + * check, whether string is valid XML name + * + *

    XML names are used for tagname, attribute names and various + * other, lesser known entities.

    + *

    An XML name may only consist of alphanumeric characters, + * dashes, undescores and periods, and has to start with a letter + * or an underscore.

    + * + * + * require_once 'XML/Util.php'; + * + * // verify tag name + * $result = XML_Util::isValidName('invalidTag?'); + * if (is_a($result, 'PEAR_Error')) { + * print 'Invalid XML name: ' . $result->getMessage(); + * } + * + * + * @param string $string string that should be checked + * + * @return mixed true, if string is a valid XML name, PEAR error otherwise + * @access public + * @static + * @todo support for other charsets + * @todo PEAR CS - unable to avoid 85-char limit on second preg_match + */ + function isValidName($string) + { + // check for invalid chars + if (!preg_match('/^[[:alpha:]_]$/', $string{0})) { + return XML_Util::raiseError('XML names may only start with letter ' + . 'or underscore', XML_UTIL_ERROR_INVALID_START); + } + + // check for invalid chars + if (!preg_match('/^([[:alpha:]_]([[:alnum:]\-\.]*)?:)?[[:alpha:]_]([[:alnum:]\_\-\.]+)?$/', + $string) + ) { + return XML_Util::raiseError('XML names may only contain alphanumeric ' + . 'chars, period, hyphen, colon and underscores', + XML_UTIL_ERROR_INVALID_CHARS); + } + // XML name is valid + return true; + } + + /** + * replacement for XML_Util::raiseError + * + * Avoids the necessity to always require + * PEAR.php + * + * @param string $msg error message + * @param int $code error code + * + * @return PEAR_Error + * @access public + * @static + * @todo PEAR CS - should this use include_once instead? + */ + function raiseError($msg, $code) + { + require_once 'PEAR.php'; + return PEAR::raiseError($msg, $code); + } +} +?> diff --git a/includes/phpcontrib_lib.php b/includes/phpcontrib_lib.php new file mode 100644 index 0000000..0b2683f --- /dev/null +++ b/includes/phpcontrib_lib.php @@ -0,0 +1,76 @@ +$val ){ + + $nkey = $key; + $isset = $t==0 ? isset( $to[$key] ) : isset( $to->$key ); + + if( ( $type==EXTR_SKIP && $isset ) + || ( $type==EXTR_IF_EXISTS && !$isset ) ) + continue; + + else if( ( $type==EXTR_PREFIX_SAME && $isset ) + || ( $type==EXTR_PREFIX_ALL ) + || ( $type==EXTR_PREFIX_INVALID && !preg_match( '#^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$#', $key ) ) ) + $nkey = $prefix.$key; + + else if( $type==EXTR_PREFIX_IF_EXISTS ) + if( $isset ) $nkey = $prefix.$key; + else continue; + + if( !preg_match( '#^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$#', $nkey ) ) continue; + + if( $t==1 ) + if( $type & EXTR_REFS ) $to->$nkey = &$arr[$key]; + else $to->$nkey = $val; + else + if( $type & EXTR_REFS ) $to[$nkey] = &$arr[$key]; + else $to[$nkey] = $val; + + $i++; + } + + return $i; +} + +/** + * array_is_indexed + * a crud check if an array is an indexed array with the limitation that + * it works for arrays where every element is as if it had been assigned doing $array[] = $value + */ +function array_is_indexed( $pArray ){ + return (count(array_diff_key($pArray, array_values($pArray))) == 0); +} diff --git a/includes/phpcoord/phpcoord-2.3.php b/includes/phpcoord/phpcoord-2.3.php new file mode 100644 index 0000000..edac27a --- /dev/null +++ b/includes/phpcoord/phpcoord-2.3.php @@ -0,0 +1,785 @@ +toSixFigureString() so that the eastings and northings + // are rounded rather than floored. + // 2.2 - 11 Feb 2006 + // - Used different algorithm for calculating distance between latitudes + // and longitudes - fixes a number of problems with distance calculations + // 2.1 - 22 Dec 2005 + // - Added getOSRefFromSixFigureReference function + // 2.0 - 21 Dec 2005 + // - Completely different object design - conversion functions now through + // objects rather than static functions + // - Updated comments and documentation + // 1.1 - 11 Sep 2005 + // - Added OSGB36/WGS84 data conversions + // 1.0 - 11 Aug 2005 + // - Initial version + //-------------------------------------------------------------------------- + + +/** + * LatLng + * + * @package kernel + */ + class LatLng { + + var $lat; + var $lng; + + + /** + * Create a new LatLng object from the given latitude and longitude + * + * @param lat latitude + * @param lng longitude + */ + function LatLng($lat, $lng) { + $this->lat = $lat; + $this->lng = $lng; + } + + + /** + * Return a string representation of this LatLng object + * + * @return a string representation of this LatLng object + */ + function toString() { + return "(" . $this->lat . ", " . $this->lng . ")"; + } + + + /** + * Calculate the surface distance between this LatLng object and the one + * passed in as a parameter. + * + * @param to a LatLng object to measure the surface distance to + * @return the surface distance + */ + function distance($to) { + $er = 6366.707; + + $latFrom = deg2rad($this->lat); + $latTo = deg2rad($to->lat); + $lngFrom = deg2rad($this->lng); + $lngTo = deg2rad($to->lng); + + $x1 = $er * cos($lngFrom) * sin($latFrom); + $y1 = $er * sin($lngFrom) * sin($latFrom); + $z1 = $er * cos($latFrom); + + $x2 = $er * cos($lngTo) * sin($latTo); + $y2 = $er * sin($lngTo) * sin($latTo); + $z2 = $er * cos($latTo); + + $d = acos(sin($latFrom)*sin($latTo) + cos($latFrom)*cos($latTo)*cos($lngTo-$lngFrom)) * $er; + + return $d; + } + + + /** + * Convert this LatLng object from OSGB36 datum to WGS84 datum. + */ + function OSGB36ToWGS84() { + $airy1830 = new RefEll(6377563.396, 6356256.909); + $a = $airy1830->maj; + $b = $airy1830->min; + $eSquared = $airy1830->ecc; + $phi = deg2rad($this->lat); + $lambda = deg2rad($this->lng); + $v = $a / (sqrt(1 - $eSquared * sinSquared($phi))); + $H = 0; // height + $x = ($v + $H) * cos($phi) * cos($lambda); + $y = ($v + $H) * cos($phi) * sin($lambda); + $z = ((1 - $eSquared) * $v + $H) * sin($phi); + + $tx = 446.448; + $ty = -124.157; + $tz = 542.060; + $s = -0.0000204894; + $rx = deg2rad( 0.00004172222); + $ry = deg2rad( 0.00006861111); + $rz = deg2rad( 0.00023391666); + + $xB = $tx + ($x * (1 + $s)) + (-$rx * $y) + ($ry * $z); + $yB = $ty + ($rz * $x) + ($y * (1 + $s)) + (-$rx * $z); + $zB = $tz + (-$ry * $x) + ($rx * $y) + ($z * (1 + $s)); + + $wgs84 = new RefEll(6378137.000, 6356752.3141); + $a = $wgs84->maj; + $b = $wgs84->min; + $eSquared = $wgs84->ecc; + + $lambdaB = rad2deg(atan($yB / $xB)); + $p = sqrt(($xB * $xB) + ($yB * $yB)); + $phiN = atan($zB / ($p * (1 - $eSquared))); + for ($i = 1; $i < 10; $i++) { + $v = $a / (sqrt(1 - $eSquared * sinSquared($phiN))); + $phiN1 = atan(($zB + ($eSquared * $v * sin($phiN))) / $p); + $phiN = $phiN1; + } + + $phiB = rad2deg($phiN); + + $this->lat = $phiB; + $this->lng = $lambdaB; + } + + + /** + * Convert this LatLng object from WGS84 datum to OSGB36 datum. + */ + function WGS84ToOSGB36() { + $wgs84 = new RefEll(6378137.000, 6356752.3141); + $a = $wgs84->maj; + $b = $wgs84->min; + $eSquared = $wgs84->ecc; + $phi = deg2rad($this->lat); + $lambda = deg2rad($this->lng); + $v = $a / (sqrt(1 - $eSquared * sinSquared($phi))); + $H = 0; // height + $x = ($v + $H) * cos($phi) * cos($lambda); + $y = ($v + $H) * cos($phi) * sin($lambda); + $z = ((1 - $eSquared) * $v + $H) * sin($phi); + + $tx = -446.448; + $ty = 124.157; + $tz = -542.060; + $s = 0.0000204894; + $rx = deg2rad(-0.00004172222); + $ry = deg2rad(-0.00006861111); + $rz = deg2rad(-0.00023391666); + + $xB = $tx + ($x * (1 + $s)) + (-$rx * $y) + ($ry * $z); + $yB = $ty + ($rz * $x) + ($y * (1 + $s)) + (-$rx * $z); + $zB = $tz + (-$ry * $x) + ($rx * $y) + ($z * (1 + $s)); + + $airy1830 = new RefEll(6377563.396, 6356256.909); + $a = $airy1830->maj; + $b = $airy1830->min; + $eSquared = $airy1830->ecc; + + $lambdaB = rad2deg(atan($yB / $xB)); + $p = sqrt(($xB * $xB) + ($yB * $yB)); + $phiN = atan($zB / ($p * (1 - $eSquared))); + for ($i = 1; $i < 10; $i++) { + $v = $a / (sqrt(1 - $eSquared * sinSquared($phiN))); + $phiN1 = atan(($zB + ($eSquared * $v * sin($phiN))) / $p); + $phiN = $phiN1; + } + + $phiB = rad2deg($phiN); + + $this->lat = $phiB; + $this->lng = $lambdaB; + } + + + /** + * Convert this LatLng object into an OSGB grid reference. Note that this + * function does not take into account the bounds of the OSGB grid - + * beyond the bounds of the OSGB grid, the resulting OSRef object has no + * meaning + * + * @return the converted OSGB grid reference + */ + function toOSRef() { + $airy1830 = new RefEll(6377563.396, 6356256.909); + $OSGB_F0 = 0.9996012717; + $N0 = -100000.0; + $E0 = 400000.0; + $phi0 = deg2rad(49.0); + $lambda0 = deg2rad(-2.0); + $a = $airy1830->maj; + $b = $airy1830->min; + $eSquared = $airy1830->ecc; + $phi = deg2rad($this->lat); + $lambda = deg2rad($this->lng); + $E = 0.0; + $N = 0.0; + $n = ($a - $b) / ($a + $b); + $v = $a * $OSGB_F0 * pow(1.0 - $eSquared * sinSquared($phi), -0.5); + $rho = + $a * $OSGB_F0 * (1.0 - $eSquared) * pow(1.0 - $eSquared * sinSquared($phi), -1.5); + $etaSquared = ($v / $rho) - 1.0; + $M = + ($b * $OSGB_F0) + * (((1 + $n + ((5.0 / 4.0) * $n * $n) + ((5.0 / 4.0) * $n * $n * $n)) + * ($phi - $phi0)) + - (((3 * $n) + (3 * $n * $n) + ((21.0 / 8.0) * $n * $n * $n)) + * sin($phi - $phi0) + * cos($phi + $phi0)) + + ((((15.0 / 8.0) * $n * $n) + ((15.0 / 8.0) * $n * $n * $n)) + * sin(2.0 * ($phi - $phi0)) + * cos(2.0 * ($phi + $phi0))) + - (((35.0 / 24.0) * $n * $n * $n) + * sin(3.0 * ($phi - $phi0)) + * cos(3.0 * ($phi + $phi0)))); + $I = $M + $N0; + $II = ($v / 2.0) * sin($phi) * cos($phi); + $III = + ($v / 24.0) + * sin($phi) + * pow(cos($phi), 3.0) + * (5.0 - tanSquared($phi) + (9.0 * $etaSquared)); + $IIIA = + ($v / 720.0) + * sin($phi) + * pow(cos($phi), 5.0) + * (61.0 - (58.0 * tanSquared($phi)) + pow(tan($phi), 4.0)); + $IV = $v * cos($phi); + $V = ($v / 6.0) * pow(cos($phi), 3.0) * (($v / $rho) - tanSquared($phi)); + $VI = + ($v / 120.0) + * pow(cos($phi), 5.0) + * (5.0 + - (18.0 * tanSquared($phi)) + + (pow(tan($phi), 4.0)) + + (14 * $etaSquared) + - (58 * tanSquared($phi) * $etaSquared)); + + $N = + $I + + ($II * pow($lambda - $lambda0, 2.0)) + + ($III * pow($lambda - $lambda0, 4.0)) + + ($IIIA * pow($lambda - $lambda0, 6.0)); + $E = + $E0 + + ($IV * ($lambda - $lambda0)) + + ($V * pow($lambda - $lambda0, 3.0)) + + ($VI * pow($lambda - $lambda0, 5.0)); + + return new OSRef($E, $N); + } + + + /** + * Convert a latitude and longitude to an UTM reference + * + * @return the converted UTM reference + */ + function toUTMRef() { + $wgs84 = new RefEll(6378137, 6356752.314); + $UTM_F0 = 0.9996; + $a = $wgs84->maj; + $eSquared = $wgs84->ecc; + $longitude = $this->lng; + $latitude = $this->lat; + + $latitudeRad = $latitude * (pi() / 180.0); + $longitudeRad = $longitude * (pi() / 180.0); + $longitudeZone = (int) (($longitude + 180.0) / 6.0) + 1; + + // Special zone for Norway + if ($latitude >= 56.0 + && $latitude < 64.0 + && $longitude >= 3.0 + && $longitude < 12.0) { + $longitudeZone = 32; + } + + // Special zones for Svalbard + if ($latitude >= 72.0 && $latitude < 84.0) { + if ($longitude >= 0.0 && $longitude < 9.0) { + $longitudeZone = 31; + } else if ($longitude >= 9.0 && $longitude < 21.0) { + $longitudeZone = 33; + } else if ($longitude >= 21.0 && $longitude < 33.0) { + $longitudeZone = 35; + } else if ($longitude >= 33.0 && $longitude < 42.0) { + $longitudeZone = 37; + } + } + + $longitudeOrigin = ($longitudeZone - 1) * 6 - 180 + 3; + $longitudeOriginRad = $longitudeOrigin * (pi() / 180.0); + + $UTMZone = getUTMLatitudeZoneLetter($latitude); + + $ePrimeSquared = ($eSquared) / (1 - $eSquared); + + $n = $a / sqrt(1 - $eSquared * sin($latitudeRad) * sin($latitudeRad)); + $t = tan($latitudeRad) * tan($latitudeRad); + $c = $ePrimeSquared * cos($latitudeRad) * cos($latitudeRad); + $A = cos($latitudeRad) * ($longitudeRad - $longitudeOriginRad); + + $M = + $a + * ((1 + - $eSquared / 4 + - 3 * $eSquared * $eSquared / 64 + - 5 * $eSquared * $eSquared * $eSquared / 256) + * $latitudeRad + - (3 * $eSquared / 8 + + 3 * $eSquared * $eSquared / 32 + + 45 * $eSquared * $eSquared * $eSquared / 1024) + * sin(2 * $latitudeRad) + + (15 * $eSquared * $eSquared / 256 + + 45 * $eSquared * $eSquared * $eSquared / 1024) + * sin(4 * $latitudeRad) + - (35 * $eSquared * $eSquared * $eSquared / 3072) + * sin(6 * $latitudeRad)); + + $UTMEasting = + (double) ($UTM_F0 + * $n + * ($A + + (1 - $t + $c) * pow($A, 3.0) / 6 + + (5 - 18 * $t + $t * $t + 72 * $c - 58 * $ePrimeSquared) + * pow($A, 5.0) + / 120) + + 500000.0); + + $UTMNorthing = + (double) ($UTM_F0 + * ($M + + $n + * tan($latitudeRad) + * ($A * $A / 2 + + (5 - $t + (9 * $c) + (4 * $c * $c)) * pow($A, 4.0) / 24 + + (61 - (58 * $t) + ($t * $t) + (600 * $c) - (330 * $ePrimeSquared)) + * pow($A, 6.0) + / 720))); + + // Adjust for the southern hemisphere + if ($latitude < 0) { + $UTMNorthing += 10000000.0; + } + + return new UTMRef($UTMEasting, $UTMNorthing, $UTMZone, $longitudeZone); + } + } + +/** + * OSRef + * References given with OSRef are accurate to 1m. + * + * @package kernel + */ + class OSRef { + + var $easting; + var $northing; + + + /** + * Create a new OSRef object representing an OSGB grid reference. Note + * that the parameters for this constructor require eastings and + * northings with 1m accuracy and need to be absolute with respect to + * the whole of the British Grid. For example, to create an OSRef + * object from the six-figure grid reference TG514131, the easting would + * be 651400 and the northing would be 313100. + * + * Grid references with accuracy greater than 1m can be represented + * using floating point values for the easting and northing. For example, + * a value representing an easting or northing accurate to 1mm would be + * given as 651400.0001. + * + * @param easting the easting of the reference (with 1m accuracy) + * @param northing the northing of the reference (with 1m accuracy) + */ + function OSRef($easting, $northing) { + $this->easting = $easting; + $this->northing = $northing; + } + + + /** + * Convert this grid reference into a string showing the exact values + * of the easting and northing. + * + * @return + */ + function toString() { + return "(" . $this->easting . ", " . $this->northing . ")"; + } + + + /** + * Convert this grid reference into a string using a standard six-figure + * grid reference including the two-character designation for the 100km + * square. e.g. TG514131. + * + * @return + */ + function toSixFigureString() { + $hundredkmE = floor($this->easting / 100000); + $hundredkmN = floor($this->northing / 100000); + $firstLetter = ""; + if ($hundredkmN < 5) { + if ($hundredkmE < 5) { + $firstLetter = "S"; + } else { + $firstLetter = "T"; + } + } else if ($hundredkmN < 10) { + if ($hundredkmE < 5) { + $firstLetter = "N"; + } else { + $firstLetter = "O"; + } + } else { + $firstLetter = "H"; + } + + $secondLetter = ""; + $index = 65 + ((4 - ($hundredkmN % 5)) * 5) + ($hundredkmE % 5); + $ti = $index; + if ($index >= 73) $index++; + $secondLetter = chr($index); + + $e = round(($this->easting - (100000 * $hundredkmE)) / 100); + $n = round(($this->northing - (100000 * $hundredkmN)) / 100); + + return sprintf("%s%s%03d%03d", $firstLetter, $secondLetter, $e, $n); + } + + + /** + * Convert this grid reference into a latitude and longitude + * + * @return + */ + function toLatLng() { + $airy1830 = new RefEll(6377563.396, 6356256.909); + $OSGB_F0 = 0.9996012717; + $N0 = -100050.0; + $E0 = 400110.0; + $phi0 = deg2rad(49.0); + $lambda0 = deg2rad(-2.0); + $a = $airy1830->maj; + $b = $airy1830->min; + $eSquared = $airy1830->ecc; + $phi = 0.0; + $lambda = 0.0; + $E = $this->easting; + $N = $this->northing; + $n = ($a - $b) / ($a + $b); + $M = 0.0; + $phiPrime = (($N - $N0) / ($a * $OSGB_F0)) + $phi0; + do { + $M = + ($b * $OSGB_F0) + * (((1 + $n + ((5.0 / 4.0) * $n * $n) + ((5.0 / 4.0) * $n * $n * $n)) + * ($phiPrime - $phi0)) + - (((3 * $n) + (3 * $n * $n) + ((21.0 / 8.0) * $n * $n * $n)) + * sin($phiPrime - $phi0) + * cos($phiPrime + $phi0)) + + ((((15.0 / 8.0) * $n * $n) + ((15.0 / 8.0) * $n * $n * $n)) + * sin(2.0 * ($phiPrime - $phi0)) + * cos(2.0 * ($phiPrime + $phi0))) + - (((35.0 / 24.0) * $n * $n * $n) + * sin(3.0 * ($phiPrime - $phi0)) + * cos(3.0 * ($phiPrime + $phi0)))); + $phiPrime += ($N - $N0 - $M) / ($a * $OSGB_F0); + } while (($N - $N0 - $M) >= 0.001); + $v = $a * $OSGB_F0 * pow(1.0 - $eSquared * sinSquared($phiPrime), -0.5); + $rho = + $a + * $OSGB_F0 + * (1.0 - $eSquared) + * pow(1.0 - $eSquared * sinSquared($phiPrime), -1.5); + $etaSquared = ($v / $rho) - 1.0; + $VII = tan($phiPrime) / (2 * $rho * $v); + $VIII = + (tan($phiPrime) / (24.0 * $rho * pow($v, 3.0))) + * (5.0 + + (3.0 * tanSquared($phiPrime)) + + $etaSquared + - (9.0 * tanSquared($phiPrime) * $etaSquared)); + $IX = + (tan($phiPrime) / (720.0 * $rho * pow($v, 5.0))) + * (61.0 + + (90.0 * tanSquared($phiPrime)) + + (45.0 * tanSquared($phiPrime) * tanSquared($phiPrime))); + $X = sec($phiPrime) / $v; + $XI = + (sec($phiPrime) / (6.0 * $v * $v * $v)) + * (($v / $rho) + (2 * tanSquared($phiPrime))); + $XII = + (sec($phiPrime) / (120.0 * pow($v, 5.0))) + * (5.0 + + (28.0 * tanSquared($phiPrime)) + + (24.0 * tanSquared($phiPrime) * tanSquared($phiPrime))); + $XIIA = + (sec($phiPrime) / (5040.0 * pow($v, 7.0))) + * (61.0 + + (662.0 * tanSquared($phiPrime)) + + (1320.0 * tanSquared($phiPrime) * tanSquared($phiPrime)) + + (720.0 + * tanSquared($phiPrime) + * tanSquared($phiPrime) + * tanSquared($phiPrime))); + $phi = + $phiPrime + - ($VII * pow($E - $E0, 2.0)) + + ($VIII * pow($E - $E0, 4.0)) + - ($IX * pow($E - $E0, 6.0)); + $lambda = + $lambda0 + + ($X * ($E - $E0)) + - ($XI * pow($E - $E0, 3.0)) + + ($XII * pow($E - $E0, 5.0)) + - ($XIIA * pow($E - $E0, 7.0)); + + return new LatLng(rad2deg($phi), rad2deg($lambda)); + } + } + +/** + * UTMRef + * + * @package kernel + */ + class UTMRef { + + var $easting; + var $northing; + var $latZone; + var $lngZone; + + + /** + * Create a new object representing a UTM reference. + * + * @param easting + * @param northing + * @param latZone + * @param lngZone + */ + function UTMRef($easting, $northing, $latZone, $lngZone) { + $this->easting = $easting; + $this->northing = $northing; + $this->latZone = $latZone; + $this->lngZone = $lngZone; + } + + + /** + * Return a string representation of this UTM reference + * + * @return + */ + function toString() { + return $this->lngZone . $this->latZone . " " . + $this->easting . " " . $this->northing; + } + + + /** + * Convert this UTM reference to a latitude and longitude + * + * @return the converted latitude and longitude + */ + function toLatLng() { + $wgs84 = new RefEll(6378137, 6356752.314); + $UTM_F0 = 0.9996; + $a = $wgs84->maj; + $eSquared = $wgs84->ecc; + $ePrimeSquared = $eSquared / (1.0 - $eSquared); + $e1 = (1 - sqrt(1 - $eSquared)) / (1 + sqrt(1 - $eSquared)); + $x = $this->easting - 500000.0;; + $y = $this->northing; + $zoneNumber = $this->lngZone; + $zoneLetter = $this->latZone; + + $longitudeOrigin = ($zoneNumber - 1.0) * 6.0 - 180.0 + 3.0; + + // Correct y for southern hemisphere + if ((ord($zoneLetter) - ord("N")) < 0) { + $y -= 10000000.0; + } + + $m = $y / $UTM_F0; + $mu = + $m + / ($a + * (1.0 + - $eSquared / 4.0 + - 3.0 * $eSquared * $eSquared / 64.0 + - 5.0 + * pow($eSquared, 3.0) + / 256.0)); + + $phi1Rad = + $mu + + (3.0 * $e1 / 2.0 - 27.0 * pow($e1, 3.0) / 32.0) * sin(2.0 * $mu) + + (21.0 * $e1 * $e1 / 16.0 - 55.0 * pow($e1, 4.0) / 32.0) + * sin(4.0 * $mu) + + (151.0 * pow($e1, 3.0) / 96.0) * sin(6.0 * $mu); + + $n = + $a + / sqrt(1.0 - $eSquared * sin($phi1Rad) * sin($phi1Rad)); + $t = tan($phi1Rad) * tan($phi1Rad); + $c = $ePrimeSquared * cos($phi1Rad) * cos($phi1Rad); + $r = + $a + * (1.0 - $eSquared) + / pow( + 1.0 - $eSquared * sin($phi1Rad) * sin($phi1Rad), + 1.5); + $d = $x / ($n * $UTM_F0); + + $latitude = ( + $phi1Rad + - ($n * tan($phi1Rad) / $r) + * ($d * $d / 2.0 + - (5.0 + + (3.0 * $t) + + (10.0 * $c) + - (4.0 * $c * $c) + - (9.0 * $ePrimeSquared)) + * pow($d, 4.0) + / 24.0 + + (61.0 + + (90.0 * $t) + + (298.0 * $c) + + (45.0 * $t * $t) + - (252.0 * $ePrimeSquared) + - (3.0 * $c * $c)) + * pow($d, 6.0) + / 720.0)) * (180.0 / pi()); + + $longitude = $longitudeOrigin + ( + ($d + - (1.0 + 2.0 * $t + $c) * pow($d, 3.0) / 6.0 + + (5.0 + - (2.0 * $c) + + (28.0 * $t) + - (3.0 * $c * $c) + + (8.0 * $ePrimeSquared) + + (24.0 * $t * $t)) + * pow($d, 5.0) + / 120.0) + / cos($phi1Rad)) * (180.0 / pi()); + + return new LatLng($latitude, $longitude); + } + } + +/** + * RefEll + * + * @package kernel + */ + class RefEll { + + var $maj; + var $min; + var $ecc; + + + /** + * Create a new RefEll object to represent a reference ellipsoid + * + * @param maj the major axis + * @param min the minor axis + */ + function RefEll($maj, $min) { + $this->maj = $maj; + $this->min = $min; + $this->ecc = (($maj * $maj) - ($min * $min)) / ($maj * $maj); + } + } + + + // ================================================== Mathematical Functions + + function sinSquared($x) { + return sin($x) * sin($x); + } + + function cosSquared($x) { + return cos($x) * cos($x); + } + + function tanSquared($x) { + return tan($x) * tan($x); + } + + function sec($x) { + return 1.0 / cos($x); + } + + + /** + * Take a string formatted as a six-figure OS grid reference (e.g. + * "TG514131") and return a reference to an OSRef object that represents + * that grid reference. The first character must be H, N, S, O or T. + * The second character can be any uppercase character from A through Z + * excluding I. + * + * @param ref + * @return + * @since 2.1 + */ + function getOSRefFromSixFigureReference($ref) { + $char1 = substr($ref, 0, 1); + $char2 = substr($ref, 1, 1); + $east = substr($ref, 2, 3) * 100; + $north = substr($ref, 5, 3) * 100; + if ($char1 == 'H') { + $north += 1000000; + } else if ($char1 == 'N') { + $north += 500000; + } else if ($char1 == 'O') { + $north += 500000; + $east += 500000; + } else if ($char1 == 'T') { + $east += 500000; + } + $char2ord = ord($char2); + if ($char2ord > 73) $char2ord--; // Adjust for no I + $nx = (($char2ord - 65) % 5) * 100000; + $ny = (4 - floor(($char2ord - 65) / 5)) * 100000; + return new OSRef($east + $nx, $north + $ny); + } + + + /** + * Work out the UTM latitude zone from the latitude + * + * @param latitude + * @return + */ + function getUTMLatitudeZoneLetter($latitude) { + if ((84 >= $latitude) && ($latitude >= 72)) return "X"; + else if (( 72 > $latitude) && ($latitude >= 64)) return "W"; + else if (( 64 > $latitude) && ($latitude >= 56)) return "V"; + else if (( 56 > $latitude) && ($latitude >= 48)) return "U"; + else if (( 48 > $latitude) && ($latitude >= 40)) return "T"; + else if (( 40 > $latitude) && ($latitude >= 32)) return "S"; + else if (( 32 > $latitude) && ($latitude >= 24)) return "R"; + else if (( 24 > $latitude) && ($latitude >= 16)) return "Q"; + else if (( 16 > $latitude) && ($latitude >= 8)) return "P"; + else if (( 8 > $latitude) && ($latitude >= 0)) return "N"; + else if (( 0 > $latitude) && ($latitude >= -8)) return "M"; + else if (( -8 > $latitude) && ($latitude >= -16)) return "L"; + else if ((-16 > $latitude) && ($latitude >= -24)) return "K"; + else if ((-24 > $latitude) && ($latitude >= -32)) return "J"; + else if ((-32 > $latitude) && ($latitude >= -40)) return "H"; + else if ((-40 > $latitude) && ($latitude >= -48)) return "G"; + else if ((-48 > $latitude) && ($latitude >= -56)) return "F"; + else if ((-56 > $latitude) && ($latitude >= -64)) return "E"; + else if ((-64 > $latitude) && ($latitude >= -72)) return "D"; + else if ((-72 > $latitude) && ($latitude >= -80)) return "C"; + else return 'Z'; + } + +?> diff --git a/includes/phpcoord/readme-2.3.txt b/includes/phpcoord/readme-2.3.txt new file mode 100644 index 0000000..ac3b253 --- /dev/null +++ b/includes/phpcoord/readme-2.3.txt @@ -0,0 +1,52 @@ +-------------------------------------------------------------------------- PHPcoord + readme.txt + (c) 2005 Jonathan Stott Created on 11-Aug-2005 + 2.3 - 24 Aug 2006 + - Changed OSRef->toSixFigureString() so that the eastings and northings + are rounded rather than floored. + 2.2 - 11 Feb 2006 + - Used different algorithm for calculating distance between latitudes + and longitudes - fixes a number of problems with distance calculations + 2.1 - 22 Dec 2005 + - Added getOSRefFromSixFigureReference function + 2.0 - 21 Dec 2005 + - Completely different object design - conversion functions now through + objects rather than static functions + - Updated comments and documentation + 1.1 - 11 Sep 2005 + - Added WGS84/OSGB36 conversions 1.0 - 11 Aug 2005 - Initial version -------------------------------------------------------------------------- + +PHPcoord is a PHP script that provides functions for handling various +co-ordinate systems and converting between them. Currently, OSGB (Ordnance +Survey of Great Britain) grid references, UTM (Universal Transverse +Mercator) references and latitude/longitude are supported. A function is +also provided to find the surface distance between two points of latitude +and longitude. + +When using the OSGB conversions, the majority of applications use the +WGS84 datum rather than the OSGB36 datum. Conversions between the two +data were added in v1.1 - the conversions should be accurate to within +5m or so. If accuracy is not important (i.e. to within 200m or so), +then it isn't necessary to perform the conversions. + +Examples of how to use the functions provided in phpcoord.php can be +found in the test.php script. + +See http://www.jstott.me.uk/phpcoord/ for latest releases and information. + + +DISCLAIMER + +Accuracy of the co-ordinate conversions contained within the PHPcoord +package is not guaranteed. Use of the conversions is entirely at your +own risk and I cannot be held responsible for any consequences of +errors created by the conversions. I do not recommend using the package +for mission-critical applications. + + +LICENSING + +This software product is available under the GNU General Public License +(GPL). Terms of the GPL can be read at http://www.jstott.me.uk/gpl/. +Any commercial use requires the purchase of a license - contact me at +phpcoord@jstott.me.uk for details. \ No newline at end of file diff --git a/includes/phpcoord/test-2.3.php b/includes/phpcoord/test-2.3.php new file mode 100644 index 0000000..8c90261 --- /dev/null +++ b/includes/phpcoord/test-2.3.php @@ -0,0 +1,211 @@ +toSixFigureString() so that the eastings and northings + // are rounded rather than floored. + // 2.2 - 11 Feb 2006 + // - Used different algorithm for calculating distance between latitudes + // and longitudes - fixes a number of problems with distance calculations + // 2.1 - 22 Dec 2005 + // - Added getOSRefFromSixFigureReference function + // 2.0 - 21 Dec 2005 + // - Completely different object design - conversion functions now through + // objects rather than static functions + // - Updated comments and documentation + // 1.1 - 11 Sep 2005 + // - Added OSGB36/WGS84 data conversions + // 1.0 - 11 Aug 2005 + // - Initial version + //-------------------------------------------------------------------------- + * + * @package kernel + * @subpackage functions + */ + +/** + * Load library + */ + require_once("phpcoord-2.3.php"); +?> + + + + + phpcoord Test Script + + + + +

    phpcoord Test Script

    + +

    Calculate Surface Distance between two Latitudes/Longitudes

    + +

    + The LatLngDistance function takes two latitudes/longitudes and calculates + the surface distance between the two in kilometres: +

    + +

    +

    $lld1 = new LatLng(40.718119, -73.995667); // New York
    +echo "New York Lat/Long: " . $lld1->toString() . "<br />";
    +$lld2 = new LatLng(51.499981, -0.125313);  // London
    +$d = $lld1->distance($lld2);
    +echo "Surface Distance between New York and London: " . $d . "km";
    + + toString() . "
    "; + $lld2 = new LatLng(51.499981, -0.125313); // London + echo "London Lat/Long: " . $lld2->toString() . "
    "; + $d = $lld1->distance($lld2); + echo "Surface Distance between New York and London: " . $d . "km"; + ?> +

    + +

    Convert OS Grid Reference to Latitude/Longitude

    + +

    + Note that the OSGB-Latitude/Longitude conversions use the OSGB36 datum by default. The + majority of applications use the WGS84 datum, for which the appropriate conversions + need to be added. See the examples below to see the difference between the two data. +

    + +

    + Using OSGB36 (convert an OSGB grid reference to a latitude and longitude using the OSGB36 datum): + +

    $os1 = new OSRef(651409.903, 313177.270);
    +echo "OS Grid Reference: " . $os1->toString() . " - " . $os1->toSixFigureString() . "<br />";
    +$ll1 = $os1->toLatLng();
    +echo "Converted to Lat/Long: " . $ll1->toString();
    + + toString() . " - " . $os1->toSixFigureString() . "
    "; + $ll1 = $os1->toLatLng(); + echo "Converted to Lat/Long: " . $ll1->toString(); + ?> +

    + +

    + Using WGS84 (convert an OSGB grid reference to a latitude and longitude using the WGS84 datum): + +

    $os1w = new OSRef(651409.903, 313177.270);
    +echo "OS Grid Reference: " . $os1w->toString() . " - " . $os1w->toSixFigureString() . "<br />";
    +$l1w = $os1w->toLatLng();
    +$l1w->OSGB36ToWGS84();
    +echo "Converted to Lat/Long: " . $ll1w->toString();
    + + toString() . " - " . $os1w->toSixFigureString() . "
    "; + $ll1w = $os1w->toLatLng(); + $ll1w->OSGB36ToWGS84(); + echo "Converted to Lat/Long: " . $ll1w->toString(); + ?> +

    + +

    Convert Latitude/Longitude to OS Grid Reference

    + +

    + Note that the OSGB-Latitude/Longitude conversions use the OSGB36 datum by default. The + majority of applications use the WGS84 datum, for which the appropriate conversions + need to be added. See the examples below to see the difference between the two data. +

    + +

    + Using OSGB36 (convert a latitude and longitude using the OSGB36 datum to an OSGB grid reference): + +

    $ll2 = new LatLng(52.657570301933, 1.7179215806451);
    +echo "Latitude/Longitude: " . $ll2->toString() . "<br />";
    +$os2 = $ll2->toOSRef();
    +echo "Converted to OS Grid Ref: " . $os2->toString() . " - " . $os2->toSixFigureString();
    + + toString() . "
    "; + $os2 = $ll2->toOSRef(); + echo "Converted to OS Grid Ref: " . $os2->toString() . " - " . $os2->toSixFigureString(); + ?> +

    + +

    + Using WGS84 (convert a latitude and longitude using the WGS84 datum to an OSGB grid reference): + +

    $ll2w = new LatLng(52.657570301933, 1.7179215806451);
    +echo "Latitude/Longitude: " . $ll2->toString() . "<br />";
    +$ll2w->WGS84ToOSGB36();
    +$os2w = $ll2w->toOSRef();
    +echo "Converted to OS Grid Ref: " . $os2w->toString() . " - " . $os2w->toSixFigureString();
    + + toString() . "
    "; + $ll2w->WGS84ToOSGB36(); + $os2w = $ll2w->toOSRef(); + echo "Converted to OS Grid Ref: " . $os2w->toString() . " - " . $os2w->toSixFigureString(); + ?> +

    + +

    Convert Six-Figure OS Grid Reference String to an OSRef Object

    + +

    + To convert a string representing a six-figure OSGB grid reference: + +

    $os6 = "TG514131";
    +echo "Six figure string: " . $os6 . "<br />";
    +$os6x = getOSRefFromSixFigureReference($os6);
    +echo "Converted to OS Grid Ref: " . $os6x->toString() . " - " . $os6x->toSixFigureString();
    + + "; + $os6x = getOSRefFromSixFigureReference($os6); + echo "Converted to OS Grid Ref: " . $os6x->toString() . " - " . $os6x->toSixFigureString(); + ?> +

    + +

    Convert UTM Reference to Latitude/Longitude

    + +

    +

    $utm1 = new UTMRef(456463.99, 3335334.05, "E", 12);
    +echo "UTM Reference: " . $utm1->toString() . "<br />";
    +$ll3 = $utm1->toLatLng();
    +echo "Converted to Lat/Long: " . $ll3->toString();
    + + toString() . "
    "; + $ll3 = $utm1->toLatLng(); + echo "Converted to Lat/Long: " . $ll3->toString(); + ?> +

    + +

    Convert Latitude/Longitude to UTM Reference

    + +

    +

    $ll4 = new LatLng(-60.1167, -111.7833);
    +echo "Latitude/Longitude: " . $ll4->toString() . "<br />";
    +$utm2 = $ll4->toUTMRef();
    +echo "Converted to UTM Ref: " . $utm2->toString() ;
    + + toString() . "
    "; + $utm2 = $ll4->toUTMRef(); + echo "Converted to UTM Ref: " . $utm2->toString() ; + ?> +

    + +

    + (c) 2005, Jonathan Stott +

    + + + diff --git a/includes/phplot.php b/includes/phplot.php new file mode 100644 index 0000000..44286fe --- /dev/null +++ b/includes/phplot.php @@ -0,0 +1,6688 @@ + + * + * Maintainer (2006-present) + * + * + * Requires PHP 5.2.x or later. (PHP 4 is unsupported as of Jan 2008) + */ + +class PHPlot +{ + const version = '5.4.0'; + + /* Declare class variables which are initialized to static values. Many more class variables + * are used, defined as needed, but are unset by default. + * All these are declared as public. While it is tempting to make them private or protected, this + * is avoided for two reasons. First, it will break existing code, since all member variables + * were public in PHP4 and who knows what internal variables people used. Second, it makes + * testing harder and less effective. Nevertheless, your code should not modify these. + */ + + public $is_inline = FALSE; // FALSE = Sends headers, TRUE = sends just raw image data + public $browser_cache = FALSE; // FALSE = Sends headers for browser to not cache the image, + // (only if is_inline = FALSE also) + public $print_image = TRUE; // DrawGraph calls PrintImage. See SetPrintImage + + public $safe_margin = 5; // Extra margin used in several places, in pixels + + public $x_axis_position = ''; // X axis position in Y world coordinates, blank for default. + public $y_axis_position = ''; // Y axis position in X world coordinates, blank for default. + + public $xscale_type = 'linear'; // linear, log + public $yscale_type = 'linear'; + +//Fonts + public $use_ttf = FALSE; // Use True Type Fonts by default? + public $ttf_path = '.'; // Default path to look in for TT Fonts. + // public $default_ttfont; // Initialized in GetDefaultTTFont + public $line_spacing = 4; // Controls line spacing of multi-line labels + + // Label angles: 0 or 90 degrees for fixed fonts, any for TTF + public $x_label_angle = 0; // For X tick labels + // public $x_data_label_angle; // For X data labels; defaults to x_label_angle - see CheckLabels() + public $y_label_angle = 0; // For Y tick labels + public $y_data_label_angle = 0; // For Y data labels + +//Formats + public $file_format = 'png'; + public $output_file = ''; // For output to a file instead of stdout + +//Data + public $data_type = 'text-data'; // Structure of the data array + public $plot_type = 'linepoints'; // See $plots[] below + + public $label_scale_position = 0.5; // Shifts data labels in pie charts. 1 = top, 0 = bottom + public $group_frac_width = 0.7; // Bars use this fraction (0 to 1) of a group's space + public $bar_extra_space = 0.5; // Number of extra bar's worth of space in a group + public $bar_width_adjust = 1; // 1 = bars of normal width, must be > 0 + +// Titles + public $title_txt = ''; + + public $x_title_txt = ''; + public $x_title_pos = 'none'; // plotdown, plotup, both, none + + public $y_title_txt = ''; + public $y_title_pos = 'none'; // plotleft, plotright, both, none + +//Labels + // There are two types of labels in PHPlot: + // Tick labels: Follow the grid, next to ticks in axis. + // Are drawn at grid drawing time, by DrawXTicks() and DrawYTicks() + // Data labels: Follow the data points, and can be placed on the axis or the plot (x/y) + // Are drawn at graph plotting time, by Draw*DataLabel(), called by DrawLines(), etc. + // DrawXDataLabel() also draws vertical lines to data points, depending on + // draw_x_data_label_lines. + // Tick Labels + // Tick and Data label positions are not initialized, because PHPlot needs to tell if they + // defaulted or are set by the user. See CheckLabels() for details. The variables and + // effective defaults are shown here in comments (but CheckLabels adjusts the defaults). + // public $x_tick_label_pos = 'plotdown'; // X tick label position + // public $y_tick_label_pos = 'plotleft'; // Y tick label position + // public $x_data_label_pos = 'plotdown'; // X data label position + // public $y_data_label_pos = 'none'; // Y data label position + + public $draw_x_data_label_lines = FALSE; // Draw a line from the data point to the axis? + + // Label format controls: (for tick, data and plot labels) + // Unset by default, these array members are used as needed for 'x' (x tick labels), 'xd' (x data + // labels), 'y' (y tick labels), and 'yd' (y data labels). + // type, precision, prefix, suffix, time_format, printf_format, custom_callback, custom_arg. + // These replace the former: x_label_type, x_time_format, x_precision (similar for y), data_units_text. + public $label_format = array('x' => array(), 'xd' => array(), 'y' => array(), 'yd' => array()); + // data_units_text is retained for backward compatibility, because there was never a function + // to set it. Use the 'suffix' argument to Set[XY]LabelType instead. + public $data_units_text = ''; // Units text for 'data' labels (i.e: '¤', '$', etc.) + +// Legend + public $legend = ''; // An array with legend titles + // Other legend_* variables are set as needed, unset for default values. + +//Ticks + public $x_tick_length = 5; // tick length in pixels for upper/lower axis + public $y_tick_length = 5; // tick length in pixels for left/right axis + + public $x_tick_cross = 3; // ticks cross x axis this many pixels + public $y_tick_cross = 3; // ticks cross y axis this many pixels + + public $x_tick_pos = 'plotdown'; // plotdown, plotup, both, xaxis, none + public $y_tick_pos = 'plotleft'; // plotright, plotleft, both, yaxis, none + + public $num_x_ticks = ''; + public $num_y_ticks = ''; + + public $x_tick_inc = ''; // Set num_x_ticks or x_tick_inc, not both. + public $y_tick_inc = ''; // Set num_y_ticks or y_tick_inc, not both. + + public $skip_top_tick = FALSE; + public $skip_bottom_tick = FALSE; + public $skip_left_tick = FALSE; + public $skip_right_tick = FALSE; + +//Grid Formatting + // public $draw_x_grid = FALSE; // Default is False except for swapped data type + // public $draw_y_grid = TRUE; // Default is True except for swapped data type + + public $dashed_grid = TRUE; + public $grid_at_foreground = FALSE; // Chooses whether to draw the grid below or above the graph + +//Colors and styles (all colors can be array (R,G,B) or named color) + public $color_array = 'small'; // 'small', 'large' or array (define your own colors) + // See rgb.inc.php and SetRGBArray() + public $default_colors = array( // The default colors for data and error bars + 'SkyBlue', 'green', 'orange', 'blue', 'red', 'DarkGreen', 'purple', 'peru', + 'cyan', 'salmon', 'SlateBlue', 'YellowGreen', 'magenta', 'aquamarine1', 'gold', 'violet'); + + // See SetDefaultStyles() for default colors for PHPlot elements. + + public $line_widths = 1; // single value or array + public $line_styles = array('solid', 'solid', 'dashed'); // single value or array + public $dashed_style = '2-4'; // colored dots-transparent dots + + public $point_sizes = array(6); // Array of sizes for points. See CheckPointParams() + public $point_shapes = array( // Array of point shapes. See SetPointShapes() and DrawDot() + 'diamond', 'dot', 'delta', 'home', 'yield', 'box', 'circle', 'up', 'down', 'cross' + ); + + public $error_bar_size = 5; // right and left size of tee + public $error_bar_shape = 'tee'; // 'tee' or 'line' + public $error_bar_line_width = 1; // single value (or array TODO) + + public $plot_border_type = 'sides'; // left, right, top, bottom, sides, none, full; or array + public $image_border_type = 'none'; // 'raised', 'plain', 'none' + // public $image_border_width; // NULL, 0, or unset for default. Default depends on type. + + public $shading = 5; // 0 for no shading, > 0 is size of shadows in pixels + + public $draw_plot_area_background = FALSE; + public $draw_broken_lines = FALSE; // Tells not to draw lines for missing Y data. + +//Miscellaneous + public $callbacks = array( // Valid callback reasons (see SetCallBack) + 'draw_setup' => NULL, + 'draw_image_background' => NULL, + 'draw_plotarea_background' => NULL, + 'draw_titles' => NULL, + 'draw_axes' => NULL, + 'draw_graph' => NULL, + 'draw_border' => NULL, + 'draw_legend' => NULL, + 'draw_all' => NULL, + 'data_color' => NULL, + 'debug_textbox' => NULL, // For testing/debugging text box alignment + 'debug_scale' => NULL, // For testing/debugging scale setup + ); + + // Defined plot types static array: + // Array key is the plot type. (Upper case letters are not allowed due to CheckOption) + // Value is an array with these keys: + // draw_method (required) : Class method to call to draw the plot. + // draw_arg : Optional array of arguments to pass to draw_method. + // draw_axes : If FALSE, do not draw X/Y axis lines, labels, ticks, grid, titles. + // abs_vals, sum_vals : Data array processing flags. See FindDataLimits(). + static protected $plots = array( + 'area' => array( + 'draw_method' => 'DrawArea', + 'abs_vals' => TRUE, + ), + 'bars' => array( + 'draw_method' => 'DrawBars', + ), + 'candlesticks' => array( + 'draw_method' => 'DrawOHLC', + 'draw_arg' => array(TRUE, FALSE), // Draw candlesticks, only fill if "closed down" + ), + 'candlesticks2' => array( + 'draw_method' => 'DrawOHLC', + 'draw_arg' => array(TRUE, TRUE), // Draw candlesticks, fill always + ), + 'linepoints' => array( + 'draw_method' => 'DrawLinePoints', + ), + 'lines' => array( + 'draw_method' => 'DrawLines', + ), + 'ohlc' => array( + 'draw_method' => 'DrawOHLC', + 'draw_arg' => array(FALSE), // Don't draw candlesticks + ), + 'pie' => array( + 'draw_method' => 'DrawPieChart', + 'draw_axes' => FALSE, + 'abs_vals' => TRUE, + ), + 'points' => array( + 'draw_method' => 'DrawDots', + ), + 'squared' => array( + 'draw_method' => 'DrawSquared', + ), + 'stackedarea' => array( + 'draw_method' => 'DrawArea', + 'draw_arg' => array(TRUE), // Tells DrawArea to draw stacked area plot + 'sum_vals' => TRUE, + 'abs_vals' => TRUE, + ), + 'stackedbars' => array( + 'draw_method' => 'DrawStackedBars', + 'sum_vals' => TRUE, + ), + 'thinbarline' => array( + 'draw_method' => 'DrawThinBarLines', + ), + ); + +////////////////////////////////////////////////////// +//BEGIN CODE +////////////////////////////////////////////////////// + + /* + * Constructor: Setup img resource, colors and size of the image, and font sizes. + * + * $which_width : Image width in pixels. + * $which_height : Image height in pixels. + * $which_output_file : Filename for output. + * $which_input_file : Path to a file to be used as background. + */ + function PHPlot($which_width=600, $which_height=400, $which_output_file=NULL, $which_input_file=NULL) + { + $this->SetRGBArray($this->color_array); + + if ($which_output_file) + $this->SetOutputFile($which_output_file); + + if ($which_input_file) { + $this->SetInputFile($which_input_file); + } else { + $this->image_width = $which_width; + $this->image_height = $which_height; + + $this->img = ImageCreate($this->image_width, $this->image_height); + if (! $this->img) + return $this->PrintError('PHPlot(): Could not create image resource.'); + } + + $this->SetDefaultStyles(); + $this->SetDefaultFonts(); + } + + /* + * Reads an image file. Stores width and height, and returns the image + * resource. On error, calls PrintError and returns False. + * This is used by the constructor via SetInputFile, and by tile_img(). + */ + protected function GetImage($image_filename, &$width, &$height) + { + $error = ''; + $size = getimagesize($image_filename); + if (!$size) { + $error = "Unable to query image file $image_filename"; + } else { + $image_type = $size[2]; + switch ($image_type) { + case IMAGETYPE_GIF: + $img = @ ImageCreateFromGIF ($image_filename); + break; + case IMAGETYPE_PNG: + $img = @ ImageCreateFromPNG ($image_filename); + break; + case IMAGETYPE_JPEG: + $img = @ ImageCreateFromJPEG ($image_filename); + break; + default: + $error = "Unknown image type ($image_type) for image file $image_filename"; + break; + } + } + if (empty($error) && !$img) { + // getimagesize is OK, but GD won't read it. Maybe unsupported format. + $error = "Failed to read image file $image_filename"; + } + if (!empty($error)) { + return $this->PrintError("GetImage(): $error"); + } + $width = $size[0]; + $height = $size[1]; + return $img; + } + + /* + * Selects an input file to be used as background for the whole graph. + * This resets the graph size to the image's size. + * Note: This is used by the constructor. It is deprecated for direct use. + */ + function SetInputFile($which_input_file) + { + $im = $this->GetImage($which_input_file, $this->image_width, $this->image_height); + if (!$im) + return FALSE; // GetImage already produced an error message. + + // Deallocate any resources previously allocated + if (isset($this->img)) + imagedestroy($this->img); + + $this->img = $im; + + // Do not overwrite the input file with the background color. + $this->done['background'] = TRUE; + + return TRUE; + } + +///////////////////////////////////////////// +////////////// COLORS +///////////////////////////////////////////// + + /* + * Allocate a GD color index for a color specified by a 4 component array. + * When a color is requested, it is parsed and checked by SetRGBColor, and then saved as an array + * of (R,G,B,A) components. At graph drawing time, this function is used to allocate the color. + * $color : The color specification as a 4 component array: R, G, B, A. + * Returns: A GD color index that can be used when drawing. + */ + protected function GetColorIndex($color) + { + list($r, $g, $b, $a) = $color; + return imagecolorresolvealpha($this->img, $r, $g, $b, $a); + } + + /* + * Allocate an array of GD color indexes for an array of color specifications. + * This is used for the data_colors array, for example. + * $color_array : Array of color specifications, each an array of R,G,B,A components. + * This must use 0-based sequential integer indexes. + * $max_colors : Limit color allocation to no more than this. + * Returns an array of GD color indexes. + */ + protected function GetColorIndexArray($color_array, $max_colors) + { + $n = min(count($color_array), $max_colors); + $result = array(); + for ($i = 0; $i < $n; $i++) + $result[] = $this->GetColorIndex($color_array[$i]); + return $result; + } + + /* + * Allocate an array of GD color indexes for darker shades of an array of color specifications. + * $color_array : Array of color specifications, each an array of R,G,B,A components. + * $max_colors : Limit color allocation to this many colors from the array. + * Returns an array of GD color indexes. + */ + protected function GetDarkColorIndexArray($color_array, $max_colors) + { + $n = min(count($color_array), $max_colors); + $result = array(); + for ($i = 0; $i < $n; $i++) + $result[] = $this->GetDarkColorIndex($color_array[$i]); + return $result; + } + + /* + * Allocate a GD color index for a darker shade of a color specified by a 4 component array. + * See notes for GetColorIndex() above. + * $color : The color specification as a 4 component array: R, G, B, A. + * Returns: A GD color index that can be used when drawing. + */ + protected function GetDarkColorIndex($color) + { + list ($r, $g, $b, $a) = $color; + $r = max(0, $r - 0x30); + $g = max(0, $g - 0x30); + $b = max(0, $b - 0x30); + return imagecolorresolvealpha($this->img, $r, $g, $b, $a); + } + + /* + * Sets/reverts all colors and styles to their defaults. + */ + protected function SetDefaultStyles() + { + $this->SetDefaultDashedStyle($this->dashed_style); + $this->SetImageBorderColor(array(194, 194, 194)); + $this->SetPlotBgColor('white'); + $this->SetBackgroundColor('white'); + $this->SetTextColor('black'); + $this->SetGridColor('black'); + $this->SetLightGridColor('gray'); + $this->SetTickColor('black'); + $this->SetTitleColor('black'); + // These functions set up the default colors when called without parameters + $this->SetDataColors(); + $this->SetErrorBarColors(); + $this->SetDataBorderColors(); + return TRUE; + } + + /* + * Set the image background color to $which_color. + */ + function SetBackgroundColor($which_color) + { + return (bool)($this->bg_color = $this->SetRGBColor($which_color)); + } + + /* + * Set the plot area background color (if enabled) to $which_color. + */ + function SetPlotBgColor($which_color) + { + return (bool)($this->plot_bg_color = $this->SetRGBColor($which_color)); + } + + /* + * Set the color of the titles (main, X, and Y) to $which_color. + * See also SetXTitleColor and SetYTitleColor. + */ + function SetTitleColor($which_color) + { + return (bool)($this->title_color = $this->SetRGBColor($which_color)); + } + + /* + * Set the color of the X title to $which_color. + * This overrides the color set with SetTitleColor. + */ + function SetXTitleColor($which_color) + { + return (bool)($this->x_title_color = $this->SetRGBColor($which_color)); + } + + /* + * Set the color of the Y title to $which_color. + * This overrides the color set with SetTitleColor. + */ + function SetYTitleColor($which_color) + { + return (bool)($this->y_title_color = $this->SetRGBColor($which_color)); + } + + /* + * Set the color of the axis tick marks to $which_color. + */ + function SetTickColor($which_color) + { + return (bool)($this->tick_color = $this->SetRGBColor($which_color)); + } + + /* + * Deprecated. Use SetTitleColor() + */ + function SetLabelColor($which_color) + { + return $this->SetTitleColor($which_color); + } + + /* + * Set the general text color (tick and data labels, legend, etc) to $which_color. + */ + function SetTextColor($which_color) + { + return (bool)($this->text_color = $this->SetRGBColor($which_color)); + } + + /* + * Set the X and Y grid colors to $which_color. Also sets the data label line color. + */ + function SetLightGridColor($which_color) + { + return (bool)($this->light_grid_color = $this->SetRGBColor($which_color)); + } + + /* + * Set the color used for the X and Y axis, plot border, legend border to $which_color. + * Note: This has nothing to do with the grid, and we don't recall where this name came from. + */ + function SetGridColor($which_color) + { + return (bool)($this->grid_color = $this->SetRGBColor($which_color)); + } + + /* + * Set the color used for the image border to $which_color. + */ + function SetImageBorderColor($which_color) + { + return (bool)($this->i_border = $this->SetRGBColor($which_color)); + } + + /* + * Designate color $which_color to be transparent, if supported by the image format. + */ + function SetTransparentColor($which_color) + { + return (bool)($this->transparent_color = $this->SetRGBColor($which_color)); + } + + /* + * Sets the array of colors to be used. It can be user defined, a small predefined one + * or a large one included from 'rgb.inc.php'. + * + * $which_color_array : A color array, or 'small' or 'large'. + * Color arrays map color names into arrays of R, G, B and optionally A values. + */ + function SetRGBArray($which_color_array) + { + if (is_array($which_color_array)) { // User defined array + $this->rgb_array = $which_color_array; + } elseif ($which_color_array == 'small') { // Small predefined color array + $this->rgb_array = array( + 'white' => array(255, 255, 255), + 'snow' => array(255, 250, 250), + 'PeachPuff' => array(255, 218, 185), + 'ivory' => array(255, 255, 240), + 'lavender' => array(230, 230, 250), + 'black' => array( 0, 0, 0), + 'DimGrey' => array(105, 105, 105), + 'gray' => array(190, 190, 190), + 'grey' => array(190, 190, 190), + 'navy' => array( 0, 0, 128), + 'SlateBlue' => array(106, 90, 205), + 'blue' => array( 0, 0, 255), + 'SkyBlue' => array(135, 206, 235), + 'cyan' => array( 0, 255, 255), + 'DarkGreen' => array( 0, 100, 0), + 'green' => array( 0, 255, 0), + 'YellowGreen' => array(154, 205, 50), + 'yellow' => array(255, 255, 0), + 'orange' => array(255, 165, 0), + 'gold' => array(255, 215, 0), + 'peru' => array(205, 133, 63), + 'beige' => array(245, 245, 220), + 'wheat' => array(245, 222, 179), + 'tan' => array(210, 180, 140), + 'brown' => array(165, 42, 42), + 'salmon' => array(250, 128, 114), + 'red' => array(255, 0, 0), + 'pink' => array(255, 192, 203), + 'maroon' => array(176, 48, 96), + 'magenta' => array(255, 0, 255), + 'violet' => array(238, 130, 238), + 'plum' => array(221, 160, 221), + 'orchid' => array(218, 112, 214), + 'purple' => array(160, 32, 240), + 'azure1' => array(240, 255, 255), + 'aquamarine1' => array(127, 255, 212) + ); + } elseif ($which_color_array == 'large') { // Large color array + if (!@include('rgb.inc.php')) { + return $this->PrintError("SetRGBArray(): Large color map could not be loaded\n" + . "from 'rgb.inc.php'."); + } + $this->rgb_array = $ColorArray; + } else { // Default to black and white only. + $this->rgb_array = array('white' => array(255, 255, 255), 'black' => array(0, 0, 0)); + } + + return TRUE; + } + + /* + * Parse a color description and return the color component values. + * Arguments: + * $color_asked : The desired color description, in one of these forms: + * Component notation: array(R, G, B) or array(R, G, B, A) with each + * in the range described below for the return value. + * Examples: (255,255,0) (204,0,0,30) + * Hex notation: "#RRGGBB" or "#RRGGBBAA" where each pair is a 2 digit hex number. + * Examples: #FF00FF (magenta) #0000FF40 (Blue with alpha=64/127) + * Named color in the current colormap, with optional suffix ":alpha" for alpha value. + * Examples: blue red:60 yellow:20 + * $alpha : optional default alpha value. This is applied to the color if it doesn't + * already have an alpha value. If not supplied, colors are opaque (alpha=0) by default. + * + * Returns an array describing a color as (R, G, B, Alpha). + * R, G, and B are integers 0-255, and Alpha is 0 (opaque) to 127 (transparent). + * Note: This function should be considered 'protected', and is not documented for public use. + */ + function SetRGBColor($color_asked, $alpha = 0) + { + if (empty($color_asked)) { + $ret_val = array(0, 0, 0); + + } elseif (is_array($color_asked) && (($n = count($color_asked)) == 3 || $n == 4) ) { + // Already an array of 3 or 4 elements: + $ret_val = $color_asked; + + } elseif (preg_match('/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})?$/i', + $color_asked, $ss)) { + // #RRGGBB or #RRGGBBAA notation: + $ret_val = array(hexdec($ss[1]), hexdec($ss[2]), hexdec($ss[3])); + if (isset($ss[4])) $ret_val[] = hexdec($ss[4]); + + } elseif (isset($this->rgb_array[$color_asked])) { + // Color by name: + $ret_val = $this->rgb_array[$color_asked]; + + } elseif (preg_match('/(.+):([\d]+)$/', $color_asked, $ss) + && isset($this->rgb_array[$ss[1]])) { + // Color by name with ":alpha" suffix, alpha is a decimal number: + $ret_val = $this->rgb_array[$ss[1]]; + $ret_val[3] = (int)$ss[2]; + + } else { + return $this->PrintError("SetRGBColor(): Color '$color_asked' is not valid."); + } + + // Append alpha if not already provided for: + if (count($ret_val) == 3) + $ret_val[] = $alpha; + return $ret_val; + } + + /* + * Sets the colors for the data, with optional default alpha value + * Cases are: + * SetDataColors(array(...)) : Use the supplied array as the color map. + * SetDataColors(colorname) : Use an array of just colorname as the color map. + * SetDataColors() or SetDataColors(NULL) : Load default color map if no color map is already set. + * SetDataColors('') or SetDataColors(False) : Load default color map (even if one is already set). + * $which_border is passed to SetDataBorderColors, for backward compatibility. + * $alpha is a default Alpha to apply to all data colors that do not have alpha. + * The default for this is NULL, not 0, so we can tell if it was defaulted. But the effective + * default value is 0 (opaque). + */ + function SetDataColors($which_data = NULL, $which_border = NULL, $alpha = NULL) + { + if (is_array($which_data)) { + $colors = $which_data; // Use supplied array + } elseif (!empty($which_data)) { + $colors = array($which_data); // Use supplied single color + } elseif (empty($this->data_colors) || !is_null($which_data)) { + $colors = $this->default_colors; // Use default color array + } else { + // which_data is NULL or missing and a color array is already set. + // The existing color array is left alone, except that if $alpha is + // given this will replace the alpha value of each existing color. + // This makes SetDataColors(NULL, NULL, $alpha) work. + if (isset($alpha)) { + $n_colors = count($this->data_colors); + for ($i = 0; $i < $n_colors; $i++) { + $this->data_colors[$i][3] = $alpha; // Component 3 = alpha value + } + } + // No need to reparse the colors or anything else. + return TRUE; + } + + if (!isset($alpha)) + $alpha = 0; // Actual default is opaque colors. + + // Check each color and convert to array (r,g,b,a) form. + // Use the $alpha argument as a default for the alpha value of each color. + $this->data_colors = array(); + foreach ($colors as $color) { + $color_array = $this->SetRGBColor($color, $alpha); + if (!$color_array) return FALSE; // SetRGBColor already did an error message. + $this->data_colors[] = $color_array; + } + + // For past compatibility: + return $this->SetDataBorderColors($which_border); + } + + /* + * Set the colors for the bars and stacked bars outlines. + * Argument usage is similar to SetDataColors(), except the default is just black. + */ + function SetDataBorderColors($which_br = NULL) + { + if (is_array($which_br)) { + $colors = $which_br; // Use supplied array + } elseif (!empty($which_br)) { + $colors = array($which_br); // Use supplied single color + } elseif (empty($this->data_border_colors) || !is_null($which_br)) { + $colors = array('black'); // Use default + } else { + return TRUE; // Do nothing: which_br is NULL or missing and a color array is already set. + } + + // Check each color and convert to array (r,g,b,a) form. + $this->data_border_colors = array(); + foreach ($colors as $color) { + $color_array = $this->SetRGBColor($color); + if (!$color_array) return FALSE; // SetRGBColor already did an error message. + $this->data_border_colors[] = $color_array; + } + return TRUE; + } + + /* + * Sets the colors for the data error bars. + * Argument usage is the same as SetDataColors(). + */ + function SetErrorBarColors($which_err = NULL) + { + if (is_array($which_err)) { + $colors = $which_err; // Use supplied array + } elseif (!empty($which_err)) { + $colors = array($which_err); // Use supplied single color + } elseif (empty($this->error_bar_colors) || !is_null($which_err)) { + $colors = $this->default_colors; // Use default color array + } else { + return TRUE; // Do nothing: which_err is NULL or missing and a color array is already set. + } + + // Check each color and convert to array (r,g,b,a) form. + $this->error_bar_colors = array(); + foreach ($colors as $color) { + $color_array = $this->SetRGBColor($color); + if (!$color_array) return FALSE; // SetRGBColor already did an error message. + $this->error_bar_colors[] = $color_array; + } + return TRUE; + } + + /* + * Sets the default dashed line style. + * $which_style : A string specifying the dashed line style, as alternating numbers + * of the length (in pixels) of lines and spaces, separated by dashes. + * For example: '2-3-1-2' means 2 dots of color, 3 transparent, 1 color, then 2 transparent. + * This builds a string which will evaluate to an array of integers. Each colored dot + * is '$which_ndxcol' and each transparent dot is 'IMG_COLOR_TRANSPARENT'. When SetDashedStyle() + * eval's this with $which_ndxcol set, the result is a GD line style array. + */ + function SetDefaultDashedStyle($which_style) + { + // Explode "numcol-numtrans-numcol-numtrans..." into segment counts: + $asked = explode('-', $which_style); + + if (count($asked) < 2) { + return $this->PrintError("SetDefaultDashedStyle(): Wrong parameter '$which_style'."); + } + + // Build the string to be evaluated later by SetDashedStyle() with $which_ndxcolor set. + $result = ''; + $vals = array('$which_ndxcol,', 'IMG_COLOR_TRANSPARENT,'); + $index = 0; + foreach ($asked as $n) { + $result .= str_repeat($vals[$index], $n); + $index = 1 - $index; + } + $this->default_dashed_style = "array($result)"; + + return TRUE; + } + + /* + * Sets the style before drawing a dashed line. Defaults to $this->default_dashed_style + * $which_ndxcol : Color index to be used. + */ + protected function SetDashedStyle($which_ndxcol) + { + // See SetDefaultDashedStyle() to understand this. + eval ("\$style = $this->default_dashed_style;"); + return imagesetstyle($this->img, $style); + } + + /* + * Set line widths for each data set. + * $which_lw : Array of line widths in pixels, or a single value to use for all data sets. + */ + function SetLineWidths($which_lw=NULL) + { + if (is_array($which_lw)) { + $this->line_widths = $which_lw; // Use provided array + } elseif (!is_null($which_lw)) { + $this->line_widths = array($which_lw); // Convert value to array + } + return TRUE; + } + + /* + * Set line style ('solid' or 'dashed') for each data set. + * $which_ls : Array of keywords, or a single keyword to use for all data sets. + */ + function SetLineStyles($which_ls=NULL) + { + if (is_array($which_ls)) { + $this->line_styles = $which_ls; // Use provided array + } elseif (!is_null($which_ls)) { + $this->line_styles = ($which_ls) ? array($which_ls) : array('solid'); + } + return TRUE; + } + +///////////////////////////////////////////// +////////////// TEXT and FONTS +///////////////////////////////////////////// + + /* + * Controls the line spacing of multi-line labels. + * $which_spc : Line spacing factor for text + * For GD text, this is the number of pixels between lines. + * For TTF text, it controls line spacing in proportion to the normal + * spacing defined by the font. + */ + function SetLineSpacing($which_spc) + { + $this->line_spacing = $which_spc; + return TRUE; + } + + /* + * Select the default font type to use. + * $which_ttf : True to default to TrueType, False to default to GD (fixed) fonts. + * This also resets all font settings to the defaults. + */ + function SetUseTTF($which_ttf) + { + $this->use_ttf = $which_ttf; + return $this->SetDefaultFonts(); + } + + /* + * Sets the directory name to look into for TrueType fonts. + */ + function SetTTFPath($which_path) + { + if (!is_dir($which_path) || !is_readable($which_path)) { + return $this->PrintError("SetTTFPath(): $which_path is not a valid path."); + } + $this->ttf_path = $which_path; + return TRUE; + } + + /* + * Sets the default TrueType font and updates all fonts to that. + * The default font might be a full path, or relative to the TTFPath, + * so let SetFont check that it exists. + * Side effects: Enables use of TrueType fonts as the default font type, + * and resets all font settings. + */ + function SetDefaultTTFont($which_font) + { + $this->default_ttfont = $which_font; + return $this->SetUseTTF(TRUE); + } + + /* + * Return the default TrueType font name. If no default has been set, + * this tries some likely candidates for a font which can be loaded. + * If it finds one that works, that becomes the default TT font. + * If there is no default and it cannot find a working font, it falls + * back to the original PHPlot default (which will not likely work either). + */ + protected function GetDefaultTTFont() + { + if (!isset($this->default_ttfont)) { + // No default font yet. Try some common sans-serif fonts. + $fonts = array('LiberationSans-Regular.ttf', // For Linux with a correct GD font search path + 'Verdana.ttf', 'Arial.ttf', 'Helvetica.ttf', // For Windows, maybe others + 'ttf-liberation/LiberationSans-Regular.ttf', // For Debian, Ubuntu, and friends + 'benjamingothic.ttf', // Original PHPlot default + ); + foreach ($fonts as $font) { + // First try the font name alone, to see if GD can find and load it. + if (@imagettfbbox(10, 0, $font, "1") !== False) + break; + // If the font wasn't found, try it with the default TTF path in front. + $font_with_path = $this->ttf_path . DIRECTORY_SEPARATOR . $font; + if (@imagettfbbox(10, 0, $font_with_path, "1") !== False) { + $font = $font_with_path; + break; + } + } + // We either have a working font, or are using the last one regardless. + $this->default_ttfont = $font; + } + return $this->default_ttfont; + } + + /* + * Sets fonts to their defaults + */ + protected function SetDefaultFonts() + { + // TTF: + if ($this->use_ttf) { + return $this->SetFont('generic', '', 8) + && $this->SetFont('title', '', 14) + && $this->SetFont('legend', '', 8) + && $this->SetFont('x_label', '', 6) + && $this->SetFont('y_label', '', 6) + && $this->SetFont('x_title', '', 10) + && $this->SetFont('y_title', '', 10); + } + // Fixed GD Fonts: + return $this->SetFont('generic', 2) + && $this->SetFont('title', 5) + && $this->SetFont('legend', 2) + && $this->SetFont('x_label', 1) + && $this->SetFont('y_label', 1) + && $this->SetFont('x_title', 3) + && $this->SetFont('y_title', 3); + } + + /* + * Select a fixed (GD) font for an element. + * This allows using a fixed font, even with SetUseTTF(True). + * $which_elem : The element whose font is to be changed. + * One of: title legend generic x_label y_label x_title y_title + * $which_font : A GD font number 1-5 + * $which_spacing (optional) : Line spacing factor + */ + function SetFontGD($which_elem, $which_font, $which_spacing = NULL) + { + if ($which_font < 1 || 5 < $which_font) { + return $this->PrintError(__FUNCTION__ . ': Font size must be 1, 2, 3, 4 or 5'); + } + if (!$this->CheckOption($which_elem, + 'generic, title, legend, x_label, y_label, x_title, y_title', + __FUNCTION__)) { + return FALSE; + } + + // Store the font parameters: name/size, char cell height and width. + $this->fonts[$which_elem] = array('ttf' => FALSE, + 'font' => $which_font, + 'height' => ImageFontHeight($which_font), + 'width' => ImageFontWidth($which_font), + 'line_spacing' => $which_spacing); + return TRUE; + } + + /* + * Select a TrueType font for an element. + * This allows using a TrueType font, even with SetUseTTF(False). + * $which_elem : The element whose font is to be changed. + * One of: title legend generic x_label y_label x_title y_title + * $which_font : A TrueType font filename or pathname. + * $which_size : Font point size. + * $which_spacing (optional) : Line spacing factor + */ + function SetFontTTF($which_elem, $which_font, $which_size = 12, $which_spacing = NULL) + { + if (!$this->CheckOption($which_elem, + 'generic, title, legend, x_label, y_label, x_title, y_title', + __FUNCTION__)) { + return FALSE; + } + + // Empty font name means use the default font. + if (empty($which_font)) + $which_font = $this->GetDefaultTTFont(); + $path = $which_font; + + // First try the font name directly, if not then try with path. + // Use GD imagettfbbox() to determine if this is a valid font. + // The return $bbox is used below, if valid. + if (($bbox = @imagettfbbox($which_size, 0, $path, "E")) === False) { + $path = $this->ttf_path . DIRECTORY_SEPARATOR . $which_font; + if (($bbox = @imagettfbbox($which_size, 0, $path, "E")) === False) { + return $this->PrintError(__FUNCTION__ . ": Can't find TrueType font $which_font"); + } + } + + // Calculate the font height and inherent line spacing. TrueType fonts have this information + // internally, but PHP/GD has no way to directly access it. So get the bounding box size of + // an upper-case character without descenders, and the baseline-to-baseline height. + // Note: In practice, $which_size = $height, maybe +/-1 . But which_size is in points, + // and height is in pixels, and someday GD may be able to tell the difference. + // The character width is saved too, but not used by the normal text drawing routines - it + // isn't necessarily a fixed-space font. It is used in DrawLegend. + $height = $bbox[1] - $bbox[5]; + $width = $bbox[2] - $bbox[0]; + $bbox = ImageTTFBBox($which_size, 0, $path, "E\nE"); + $spacing = $bbox[1] - $bbox[5] - 2 * $height; + + // Store the font parameters: + $this->fonts[$which_elem] = array('ttf' => TRUE, + 'font' => $path, + 'size' => $which_size, + 'height' => $height, + 'width' => $width, + 'spacing' => $spacing, + 'line_spacing' => $which_spacing); + return TRUE; + } + + /* + * Select Fixed/TrueType font for an element. Which type of font is + * selected depends on the $use_ttf class variable (see SetUseTTF()). + * Before PHPlot supported mixing font types, only this function and + * SetUseTTF were available to select an overall font type, but now + * SetFontGD() and SetFontTTF() can be used for mixing font types. + * $which_elem : The element whose font is to be changed. + * One of: title legend generic x_label y_label x_title y_title + * $which_font : A number 1-5 for fixed fonts, or a TrueType font. + * $which_size : Ignored for Fixed fonts, point size for TrueType. + * $which_spacing (optional) : Line spacing factor + */ + function SetFont($which_elem, $which_font, $which_size = 12, $line_spacing = NULL) + { + if ($this->use_ttf) + return $this->SetFontTTF($which_elem, $which_font, $which_size, $line_spacing); + return $this->SetFontGD($which_elem, $which_font, $line_spacing); + } + + /* + * Return the inter-line spacing for a font. + * This is an internal function, used by ProcessText* and DrawLegend. + * $font : A font array variable. + * Returns: Spacing, in pixels, between text lines. + */ + protected function GetLineSpacing($font) + { + // Use the per-font line spacing preference, if set, else the global value: + if (isset($font['line_spacing'])) + $line_spacing = $font['line_spacing']; + else + $line_spacing = $this->line_spacing; + + // For GD fonts, that is the spacing in pixels. + // For TTF, adjust based on the 'natural' font spacing (see SetFontTTF): + if ($font['ttf']) { + $line_spacing = (int)($line_spacing * $font['spacing'] / 6.0); + } + return $line_spacing; + } + + /* + * Text drawing and sizing functions: + * ProcessText is meant for use only by DrawText and SizeText. + * ProcessText(True, ...) - Draw a block of text + * ProcessText(False, ...) - Just return ($width, $height) of + * the orthogonal bounding box containing the text. + * ProcessText is further split into separate functions for GD and TTF + * text, due to the size of the code. + * + * Horizontal and vertical alignment are relative to the drawing. That is: + * vertical text (90 deg) gets centered along Y position with + * v_align = 'center', and adjusted to the right of X position with + * h_align = 'right'. Another way to look at this is to say + * that text rotation happens first, then alignment. + * + * Original multiple lines code submitted by Remi Ricard. + * Original vertical code submitted by Marlin Viss. + * + * Text routines rewritten by ljb to fix alignment and position problems. + * Here is my explanation and notes. More information and pictures will be + * placed in the PHPlot Reference Manual. + * + * + Process TTF text one line at a time, not as a block. (See below) + * + Flipped top vs bottom vertical alignment. The usual interpretation + * is: bottom align means bottom of the text is at the specified Y + * coordinate. For some reason, PHPlot did left/right the correct way, + * but had top/bottom reversed. I fixed it, and left the default valign + * argument as bottom, but the meaning of the default value changed. + * + * For GD font text, only single-line text is handled by GD, and the + * basepoint is the upper left corner of each text line. + * For TTF text, multi-line text could be handled by GD, with the text + * basepoint at the lower left corner of the first line of text. + * (Behavior of TTF drawing routines on multi-line text is not documented.) + * But you cannot do left/center/right alignment on each line that way, + * or proper line spacing. + * Therefore, for either text type, we have to break up the text into + * lines and position each line independently. + * + * There are 9 alignment modes: Horizontal = left, center, or right, and + * Vertical = top, center, or bottom. Alignment is interpreted relative to + * the image, not as the text is read. This makes sense when you consider + * for example X axis labels. They need to be centered below the marks + * (center, top alignment) regardless of the text angle. + * 'Bottom' alignment really means baseline alignment. + * + * GD font text is supported (by libgd) at 0 degrees and 90 degrees only. + * Multi-line or single line text works with any of the 9 alignment modes. + * + * TTF text can be at any angle. The 9 alignment modes work for all angles, + * but the results might not be what you expect for multi-line text. See + * the PHPlot Reference Manual for pictures and details. In short, alignment + * applies to the orthogonal (aligned with X and Y axes) bounding box that + * contains the text, and to each line in the multi-line text box. Since + * alignment is relative to the image, 45 degree multi-line text aligns + * differently from 46 degree text. + * + * Note that PHPlot allows multi-line text for the 3 titles, and they + * are only drawn at 0 degrees (main and X titles) or 90 degrees (Y title). + * Data labels can also be multi-line, and they can be drawn at any angle. + * -ljb 2007-11-03 + * + */ + + /* + * ProcessTextGD() - Draw or size GD fixed-font text. + * This is intended for use only by ProcessText(). + * $draw_it : True to draw the text, False to just return the orthogonal width and height. + * $font : PHPlot font array (with 'ttf' = False) - see SetFontGD() + * $angle : Text angle in degrees. GD only supports 0 and 90. We treat >= 45 as 90, else 0. + * $x, $y : Reference point for the text (ignored if !$draw_it) + * $color : GD color index to use for drawing the text (ignored if !$draw_it) + * $text : The text to draw or size. Put a newline between lines. + * $h_factor : Horizontal alignment factor: 0(left), .5(center), or 1(right) (ignored if !$draw_it) + * $v_factor : Vertical alignment factor: 0(top), .5(center), or 1(bottom) (ignored if !$draw_it) + * Returns: True, if drawing text, or an array of ($width, $height) if not. + */ + protected function ProcessTextGD($draw_it, $font, $angle, $x, $y, $color, $text, $h_factor, $v_factor) + { + // Extract font parameters: + $font_number = $font['font']; + $font_width = $font['width']; + $font_height = $font['height']; + $line_spacing = $this->GetLineSpacing($font); + + // Break up the text into lines, trim whitespace, find longest line. + // Save the lines and length for drawing below. + $longest = 0; + foreach (explode("\n", $text) as $each_line) { + $lines[] = $line = trim($each_line); + $line_lens[] = $line_len = strlen($line); + if ($line_len > $longest) $longest = $line_len; + } + $n_lines = count($lines); + + // Width, height are based on font size and longest line, line count respectively. + // These are relative to the text angle. + $total_width = $longest * $font_width; + $total_height = $n_lines * $font_height + ($n_lines - 1) * $line_spacing; + + if (!$draw_it) { + if ($angle < 45) return array($total_width, $total_height); + return array($total_height, $total_width); + } + + $interline_step = $font_height + $line_spacing; // Line-to-line step + + if ($angle >= 45) { + // Vertical text (90 degrees): + // (Remember the alignment convention with vertical text) + // For 90 degree text, alignment factors change like this: + $temp = $v_factor; + $v_factor = $h_factor; + $h_factor = 1 - $temp; + + $draw_func = 'ImageStringUp'; + + // Rotation matrix "R" for 90 degrees (with Y pointing down): + $r00 = 0; $r01 = 1; + $r10 = -1; $r11 = 0; + + } else { + // Horizontal text (0 degrees): + $draw_func = 'ImageString'; + + // Rotation matrix "R" for 0 degrees: + $r00 = 1; $r01 = 0; + $r10 = 0; $r11 = 1; + } + + // Adjust for vertical alignment (horizontal text) or horizontal alignment (vertical text): + $factor = (int)($total_height * $v_factor); + $xpos = $x - $r01 * $factor; + $ypos = $y - $r11 * $factor; + + // Debug callback provides the bounding box: + if ($this->GetCallback('debug_textbox')) { + if ($angle >= 45) { + $bbox_width = $total_height; + $bbox_height = $total_width; + $px = $xpos; + $py = $ypos - (1 - $h_factor) * $total_width; + } else { + $bbox_width = $total_width; + $bbox_height = $total_height; + $px = $xpos - $h_factor * $total_width; + $py = $ypos; + } + $this->DoCallback('debug_textbox', $px, $py, $bbox_width, $bbox_height); + } + + for ($i = 0; $i < $n_lines; $i++) { + + // Adjust for alignment of this line within the text block: + $factor = (int)($line_lens[$i] * $font_width * $h_factor); + $x = $xpos - $r00 * $factor; + $y = $ypos - $r10 * $factor; + + // Call ImageString or ImageStringUp: + $draw_func($this->img, $font_number, $x, $y, $lines[$i], $color); + + // Step to the next line of text. This is a rotation of (x=0, y=interline_spacing) + $xpos += $r01 * $interline_step; + $ypos += $r11 * $interline_step; + } + return TRUE; + } + + /* + * ProcessTextTTF() - Draw or size TTF text. + * This is intended for use only by ProcessText(). + * $draw_it : True to draw the text, False to just return the orthogonal width and height. + * $font : PHPlot font array (with 'ttf' = True) - see SetFontTTF() + * $angle : Text angle in degrees. + * $x, $y : Reference point for the text (ignored if !$draw_it) + * $color : GD color index to use for drawing the text (ignored if !$draw_it) + * $text : The text to draw or size. Put a newline between lines. + * $h_factor : Horizontal alignment factor: 0(left), .5(center), or 1(right) (ignored if !$draw_it) + * $v_factor : Vertical alignment factor: 0(top), .5(center), or 1(bottom) (ignored if !$draw_it) + * Returns: True, if drawing text, or an array of ($width, $height) if not. + */ + protected function ProcessTextTTF($draw_it, $font, $angle, $x, $y, $color, $text, $h_factor, $v_factor) + { + // Extract font parameters (see SetFontTTF): + $font_file = $font['font']; + $font_size = $font['size']; + $font_height = $font['height']; + $line_spacing = $this->GetLineSpacing($font); + + // Break up the text into lines, trim whitespace. + // Calculate the total width and height of the text box at 0 degrees. + // Save the trimmed lines and their widths for later when drawing. + // To get uniform spacing, don't use the actual line heights. + // Total height = Font-specific line heights plus inter-line spacing. + // Total width = width of widest line. + // Last Line Descent is the offset from the bottom to the text baseline. + // Note: For some reason, ImageTTFBBox uses (-1,-1) as the reference point. + // So 1+bbox[1] is the baseline to bottom distance. + $total_width = 0; + $lastline_descent = 0; + foreach (explode("\n", $text) as $each_line) { + $lines[] = $line = trim($each_line); + $bbox = ImageTTFBBox($font_size, 0, $font_file, $line); + $line_widths[] = $width = $bbox[2] - $bbox[0]; + if ($width > $total_width) $total_width = $width; + $lastline_descent = 1 + $bbox[1]; + } + $n_lines = count($lines); + $total_height = $n_lines * $font_height + ($n_lines - 1) * $line_spacing; + + // Calculate the rotation matrix for the text's angle. Remember that GD points Y down, + // so the sin() terms change sign. + $theta = deg2rad($angle); + $cos_t = cos($theta); + $sin_t = sin($theta); + $r00 = $cos_t; $r01 = $sin_t; + $r10 = -$sin_t; $r11 = $cos_t; + + // Make a bounding box of the right size, with upper left corner at (0,0). + // By convention, the point order is: LL, LR, UR, UL. + // Note this is still working with the text at 0 degrees. + // When sizing text (SizeText), use the overall size with descenders. + // This tells the caller how much room to leave for the text. + // When drawing text (DrawText), use the size without descenders - that + // is, down to the baseline. This is for accurate positioning. + $b[0] = 0; + if ($draw_it) { + $b[1] = $total_height; + } else { + $b[1] = $total_height + $lastline_descent; + } + $b[2] = $total_width; $b[3] = $b[1]; + $b[4] = $total_width; $b[5] = 0; + $b[6] = 0; $b[7] = 0; + + // Rotate the bounding box, then offset to the reference point: + for ($i = 0; $i < 8; $i += 2) { + $x_b = $b[$i]; + $y_b = $b[$i+1]; + $c[$i] = $x + $r00 * $x_b + $r01 * $y_b; + $c[$i+1] = $y + $r10 * $x_b + $r11 * $y_b; + } + + // Get an orthogonal (aligned with X and Y axes) bounding box around it, by + // finding the min and max X and Y: + $bbox_ref_x = $bbox_max_x = $c[0]; + $bbox_ref_y = $bbox_max_y = $c[1]; + for ($i = 2; $i < 8; $i += 2) { + $x_b = $c[$i]; + if ($x_b < $bbox_ref_x) $bbox_ref_x = $x_b; + elseif ($bbox_max_x < $x_b) $bbox_max_x = $x_b; + $y_b = $c[$i+1]; + if ($y_b < $bbox_ref_y) $bbox_ref_y = $y_b; + elseif ($bbox_max_y < $y_b) $bbox_max_y = $y_b; + } + $bbox_width = $bbox_max_x - $bbox_ref_x; + $bbox_height = $bbox_max_y - $bbox_ref_y; + + if (!$draw_it) { + // Return the bounding box, rounded up (so it always contains the text): + return array((int)ceil($bbox_width), (int)ceil($bbox_height)); + } + + $interline_step = $font_height + $line_spacing; // Line-to-line step + + // Calculate the offsets from the supplied reference point to the + // upper-left corner of the text. + // Start at the reference point at the upper left corner of the bounding + // box (bbox_ref_x, bbox_ref_y) then adjust it for the 9 point alignment. + // h,v_factor are 0,0 for top,left, .5,.5 for center,center, 1,1 for bottom,right. + // $off_x = $bbox_ref_x + $bbox_width * $h_factor - $x; + // $off_y = $bbox_ref_y + $bbox_height * $v_factor - $y; + // Then use that offset to calculate back to the supplied reference point x, y + // to get the text base point. + // $qx = $x - $off_x; + // $qy = $y - $off_y; + // Reduces to: + $qx = 2 * $x - $bbox_ref_x - $bbox_width * $h_factor; + $qy = 2 * $y - $bbox_ref_y - $bbox_height * $v_factor; + + // Check for debug callback. Don't calculate bounding box unless it is wanted. + if ($this->GetCallback('debug_textbox')) { + // Calculate the orthogonal bounding box coordinates for debug testing. + + // qx, qy is upper left corner relative to the text. + // Calculate px,py: upper left corner (absolute) of the bounding box. + // There are 4 equation sets for this, depending on the quadrant: + if ($sin_t > 0) { + if ($cos_t > 0) { + // Quadrant: 0d - 90d: + $px = $qx; $py = $qy - $total_width * $sin_t; + } else { + // Quadrant: 90d - 180d: + $px = $qx + $total_width * $cos_t; $py = $qy - $bbox_height; + } + } else { + if ($cos_t < 0) { + // Quadrant: 180d - 270d: + $px = $qx - $bbox_width; $py = $qy + $total_height * $cos_t; + } else { + // Quadrant: 270d - 360d: + $px = $qx + $total_height * $sin_t; $py = $qy; + } + } + $this->DoCallback('debug_textbox', $px, $py, $bbox_width, $bbox_height); + } + + // Since alignment is applied after rotation, which parameter is used + // to control alignment of each line within the text box varies with + // the angle. + // Angle (degrees): Line alignment controlled by: + // -45 < angle <= 45 h_align + // 45 < angle <= 135 reversed v_align + // 135 < angle <= 225 reversed h_align + // 225 < angle <= 315 v_align + if ($cos_t >= $sin_t) { + if ($cos_t >= -$sin_t) $line_align_factor = $h_factor; + else $line_align_factor = $v_factor; + } else { + if ($cos_t >= -$sin_t) $line_align_factor = 1-$v_factor; + else $line_align_factor = 1-$h_factor; + } + + // Now we have the start point, spacing and in-line alignment factor. + // We are finally ready to start drawing the text, line by line. + for ($i = 0; $i < $n_lines; $i++) { + + // For drawing TTF text, the reference point is the left edge of the + // text baseline (not the lower left corner of the bounding box). + // The following also adjusts for horizontal (relative to + // the text) alignment of the current line within the box. + // What is happening is rotation of this vector by the text angle: + // (x = (total_width - line_width) * factor, y = font_height) + + $width_factor = ($total_width - $line_widths[$i]) * $line_align_factor; + $rx = $qx + $r00 * $width_factor + $r01 * $font_height; + $ry = $qy + $r10 * $width_factor + $r11 * $font_height; + + // Finally, draw the text: + ImageTTFText($this->img, $font_size, $angle, $rx, $ry, $color, $font_file, $lines[$i]); + + // Step to position of next line. + // This is a rotation of (x=0,y=height+line_spacing) by $angle: + $qx += $r01 * $interline_step; + $qy += $r11 * $interline_step; + } + return TRUE; + } + + /* + * ProcessText() - Wrapper for ProcessTextTTF() and ProcessTextGD(). See notes above. + * This is intended for use from within PHPlot only, and only by DrawText() and SizeText(). + * $draw_it : True to draw the text, False to just return the orthogonal width and height. + * $font : PHPlot font array, or NULL or empty string to use 'generic' + * $angle : Text angle in degrees + * $x, $y : Reference point for the text (ignored if !$draw_it) + * $color : GD color index to use for drawing the text (ignored if !$draw_it) + * $text : The text to draw or size. Put a newline between lines. + * $halign : Horizontal alignment: left, center, or right (ignored if !$draw_it) + * $valign : Vertical alignment: top, center, or bottom (ignored if !$draw_it) + * Note: Alignment is relative to the image, not the text. + * Returns: True, if drawing text, or an array of ($width, $height) if not. + */ + protected function ProcessText($draw_it, $font, $angle, $x, $y, $color, $text, $halign, $valign) + { + // Empty text case: + if ($text === '') { + if ($draw_it) return TRUE; + return array(0, 0); + } + + // Calculate width and height offset factors using the alignment args: + if ($valign == 'top') $v_factor = 0; + elseif ($valign == 'center') $v_factor = 0.5; + else $v_factor = 1.0; // 'bottom' + if ($halign == 'left') $h_factor = 0; + elseif ($halign == 'center') $h_factor = 0.5; + else $h_factor = 1.0; // 'right' + + // Apply a default font. This is mostly for external (callback) users. + if (empty($font)) $font = $this->fonts['generic']; + + if ($font['ttf']) { + return $this->ProcessTextTTF($draw_it, $font, $angle, $x, $y, $color, $text, + $h_factor, $v_factor); + } + return $this->ProcessTextGD($draw_it, $font, $angle, $x, $y, $color, $text, $h_factor, $v_factor); + } + + /* + * Draws a block of text. See comments above before ProcessText(). + * $which_font : PHPlot font array, or NULL or empty string to use 'generic' + * $which_angle : Text angle in degrees + * $which_xpos, $which_ypos: Reference point for the text + * $which_color : GD color index to use for drawing the text + * $which_text : The text to draw, with newlines (\n) between lines. + * $which_halign : Horizontal (relative to the image) alignment: left, center, or right. + * $which_valign : Vertical (relative to the image) alignment: top, center, or bottom. + * Note: This function should be considered 'protected', and is not documented for public use. + */ + function DrawText($which_font, $which_angle, $which_xpos, $which_ypos, $which_color, $which_text, + $which_halign = 'left', $which_valign = 'bottom') + { + return $this->ProcessText(TRUE, + $which_font, $which_angle, $which_xpos, $which_ypos, + $which_color, $which_text, $which_halign, $which_valign); + } + + /* + * Returns the size of block of text. This is the orthogonal width and height of a bounding + * box aligned with the X and Y axes of the text. Only for angle=0 is this the actual + * width and height of the text block, but for any angle it is the amount of space needed + * to contain the text. + * $which_font : PHPlot font array, or NULL or empty string to use 'generic' + * $which_angle : Text angle in degrees + * $which_text : The text to draw, with newlines (\n) between lines. + * Returns a two element array with: $width, $height. + * This is just a wrapper for ProcessText() - see above. + * Note: This function should be considered 'protected', and is not documented for public use. + */ + function SizeText($which_font, $which_angle, $which_text) + { + // Color, position, and alignment are not used when calculating the size. + return $this->ProcessText(FALSE, + $which_font, $which_angle, 0, 0, 1, $which_text, '', ''); + } + +///////////////////////////////////////////// +/////////// INPUT / OUTPUT CONTROL +///////////////////////////////////////////// + + /* + * Sets output file format to $format (jpg, png, ...) + */ + function SetFileFormat($format) + { + $asked = $this->CheckOption($format, 'jpg, png, gif, wbmp', __FUNCTION__); + if (!$asked) return FALSE; + switch ($asked) { + case 'jpg': + $format_test = IMG_JPG; + break; + case 'png': + $format_test = IMG_PNG; + break; + case 'gif': + $format_test = IMG_GIF; + break; + case 'wbmp': + $format_test = IMG_WBMP; + break; + } + if (!(imagetypes() & $format_test)) { + return $this->PrintError("SetFileFormat(): File format '$format' not supported"); + } + $this->file_format = $asked; + return TRUE; + } + + /* + * Selects an input file to be used as graph background and scales or tiles this image + * to fit the sizes. + * $input_file : Path to the file to be used (jpeg, png and gif accepted) + * $mode : 'centeredtile', 'tile', or 'scale' (the image to the graph's size) + */ + function SetBgImage($input_file, $mode='centeredtile') + { + $this->bgmode = $this->CheckOption($mode, 'tile, centeredtile, scale', __FUNCTION__); + $this->bgimg = $input_file; + return (boolean)$this->bgmode; + } + + /* + * Selects an input file to be used as plot area background and scales or tiles this image + * to fit the sizes. + * $input_file : Path to the file to be used (jpeg, png and gif accepted) + * $mode : 'centeredtile', 'tile', or 'scale' (the image to the graph's size) + */ + function SetPlotAreaBgImage($input_file, $mode='tile') + { + $this->plotbgmode = $this->CheckOption($mode, 'tile, centeredtile, scale', __FUNCTION__); + $this->plotbgimg = $input_file; + return (boolean)$this->plotbgmode; + } + + /* + * Sets the name of the file to be used as output file. + */ + function SetOutputFile($which_output_file) + { + $this->output_file = $which_output_file; + return TRUE; + } + + /* + * Sets the output image as 'inline', that is: no Content-Type headers are sent + * to the browser. Needed if you want to embed the images. + */ + function SetIsInline($which_ii) + { + $this->is_inline = (bool)$which_ii; + return TRUE; + } + + /* + * Performs the actual outputting of the generated graph. + */ + function PrintImage() + { + // Browser cache stuff submitted by Thiemo Nagel + if ( (! $this->browser_cache) && (! $this->is_inline)) { + header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); + header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . 'GMT'); + header('Cache-Control: no-cache, must-revalidate'); + header('Pragma: no-cache'); + } + + switch ($this->file_format) { + case 'png': + $mime_type = 'image/png'; + $output_f = 'imagepng'; + break; + case 'jpg': + $mime_type = 'image/jpeg'; + $output_f = 'imagejpeg'; + break; + case 'gif': + $mime_type = 'image/gif'; + $output_f = 'imagegif'; + break; + case 'wbmp': + $mime_type = 'image/wbmp'; + $output_f = 'imagewbmp'; + break; + default: + return $this->PrintError('PrintImage(): Please select an image type!'); + } + if (!$this->is_inline) { + Header("Content-type: $mime_type"); + } + if ($this->is_inline && $this->output_file != '') { + $output_f($this->img, $this->output_file); + } else { + $output_f($this->img); + } + return TRUE; + } + + /* + * Error handling for 'fatal' errors: + * $error_message Text of the error message + * Standard output from PHPlot is expected to be an image file, such as + * when handling an tag browser request. So it is not permitted to + * output text to standard output. (You should have display_errors=off) + * Here is how PHPlot handles fatal errors: + * + Write the error message into an image, and output the image. + * + If no image can be output, write nothing and produce an HTTP + * error header. + * + Trigger a user-level error containing the error message. + * If no error handler was set up, the script will log the + * error and exit with non-zero status. + * + * PrintError() and DrawError() are now equivalent. Both are provided for + * compatibility. (In earlier releases, PrintError sent the message to + * stdout only, and DrawError sent it in an image only.) + * + * This function does not return, unless the calling script has set up + * an error handler which does not exit. In that case, PrintError will + * return False. But not all of PHPlot will handle this correctly, so + * it is probably a bad idea for an error handler to return. + */ + protected function PrintError($error_message) + { + // Be sure not to loop recursively, e.g. PrintError - PrintImage - PrintError. + if (isset($this->in_error)) return FALSE; + $this->in_error = TRUE; + + // Output an image containing the error message: + if (!empty($this->img)) { + $ypos = $this->image_height/2; + $xpos = $this->image_width/2; + $bgcolor = ImageColorResolve($this->img, 255, 255, 255); + $fgcolor = ImageColorResolve($this->img, 0, 0, 0); + ImageFilledRectangle($this->img, 0, 0, $this->image_width, $this->image_height, $bgcolor); + + // Switch to built-in fonts, in case of error with TrueType fonts: + $this->SetUseTTF(FALSE); + + $this->DrawText($this->fonts['generic'], 0, $xpos, $ypos, $fgcolor, + wordwrap($error_message), 'center', 'center'); + + $this->PrintImage(); + } elseif (! $this->is_inline) { + Header('HTTP/1.0 500 Internal Server Error'); + } + trigger_error($error_message, E_USER_ERROR); + unset($this->in_error); + return FALSE; // In case error handler returns, rather than doing exit(). + } + + /* + * Display an error message and exit. + * This is provided for backward compatibility only. Use PrintError() instead. + * $error_message Text of the error message + * $where_x, $where_y Ignored, provided for compatibility. + */ + protected function DrawError($error_message, $where_x = NULL, $where_y = NULL) + { + return $this->PrintError($error_message); + } + +///////////////////////////////////////////// +/////////// LABELS +///////////////////////////////////////////// + + /* + * Sets position for X data labels. + * For vertical plots, these are X axis data labels, showing label strings from the data array. + * Accepted positions are: plotdown, plotup, both, none. + * For horizontal plots (bar, stackedbar only), these are X data value labels, show the data values. + * Accepted positions are: plotin, plotstack, none. + */ + function SetXDataLabelPos($which_xdlp) + { + $which_xdlp = $this->CheckOption($which_xdlp, 'plotdown, plotup, both, none, plotin, plotstack', + __FUNCTION__); + if (!$which_xdlp) return FALSE; + $this->x_data_label_pos = $which_xdlp; + + return TRUE; + } + + /* + * Sets position for Y data labels. + * For vertical plots (where available), these are Y data value labels, showing the data values. + * Accepted positions are: plotin, plotstack, none. + * For horizontal plots, these are Y axis data labels, showing label strings from the data array. + * Accepted positions are: plotleft, plotright, both, none. + */ + function SetYDataLabelPos($which_ydlp) + { + $which_ydlp = $this->CheckOption($which_ydlp, 'plotleft, plotright, both, none, plotin, plotstack', + __FUNCTION__); + if (!$which_ydlp) return FALSE; + $this->y_data_label_pos = $which_ydlp; + + return TRUE; + } + + /* + * Set position for X tick labels. + */ + function SetXTickLabelPos($which_xtlp) + { + $which_xtlp = $this->CheckOption($which_xtlp, 'plotdown, plotup, both, xaxis, none', + __FUNCTION__); + if (!$which_xtlp) return FALSE; + $this->x_tick_label_pos = $which_xtlp; + + return TRUE; + } + + /* + * Set position for Y tick labels. + */ + function SetYTickLabelPos($which_ytlp) + { + $which_ytlp = $this->CheckOption($which_ytlp, 'plotleft, plotright, both, yaxis, none', + __FUNCTION__); + if (!$which_ytlp) return FALSE; + $this->y_tick_label_pos = $which_ytlp; + + return TRUE; + } + + /* + * Set formatting type for tick and data labels on X or Y axis. + * This implements the 4 functions Set[XY]LabelType() and Set[XY]DataLabelType(). + * $mode : 'x', 'y', 'xd', or 'yd' - which type of label to configure. + * 'x' and 'y' set the type for tick labels, and the default type for data labels + * if they are not separately configured. 'xd' and 'yd' set the type for data labels. + * $args : Variable arguments, passed as an array. + * [0] = $type (required) : Label type. 'data', 'time', 'printf', or 'custom'. + * For type 'data': + * [1] = $precision (optional). Numeric precision. Can also be set by SetPrecision[XY](). + * [2] = $prefix (optional) - prefix string for labels. + * [3] = $suffix (optional) - suffix string for labels. This replaces data_units_text. + * For type 'time': + * [1] = $format for strftime (optional). Can also be set by Set[XY]TimeFormat(). + * For type 'printf': + * [1] = $format (optional) for sprintf. + * For type 'custom': + * [1] = $callback (required) - Custom function or array of (instance,method) to call. + * [2] = $argument (optional) - Pass-through argument for the formatting function. + */ + protected function SetLabelType($mode, $args) + { + if (!$this->CheckOption($mode, 'x, y, xd, yd', __FUNCTION__)) + return FALSE; + + $type = isset($args[0]) ? $args[0] : ''; + $format =& $this->label_format[$mode]; // Shorthand reference to format storage variables + switch ($type) { + case 'data': + if (isset($args[1])) + $format['precision'] = $args[1]; + elseif (!isset($format['precision'])) + $format['precision'] = 1; + $format['prefix'] = isset($args[2]) ? $args[2] : ''; + $format['suffix'] = isset($args[3]) ? $args[3] : ''; + break; + + case 'time': + if (isset($args[1])) + $format['time_format'] = $args[1]; + elseif (!isset($format['time_format'])) + $format['time_format'] = '%H:%M:%S'; + break; + + case 'printf': + if (isset($args[1])) + $format['printf_format'] = $args[1]; + elseif (!isset($format['printf_format'])) + $format['printf_format'] = '%e'; + break; + + case 'custom': + if (isset($args[1])) { + $format['custom_callback'] = $args[1]; + $format['custom_arg'] = isset($args[2]) ? $args[2] : NULL; + } else { + $type = ''; // Error, 'custom' without a function, set to no-format mode. + } + break; + + case '': + case 'title': // Retained for backwards compatibility? + break; + + default: + $this->CheckOption($type, 'data, time, printf, custom', __FUNCTION__); + $type = ''; + } + $format['type'] = $type; + return (boolean)$type; + } + + /* + * Select label formating for X tick labels, and for X data labels + * (unless SetXDataLabelType was called). + * See SetLabelType() for details. + */ + function SetXLabelType() // Variable arguments: $type, ... + { + $args = func_get_args(); + return $this->SetLabelType('x', $args); + } + + /* + * Select label formatting for X data labels, overriding SetXLabelType. + */ + function SetXDataLabelType() // Variable arguments: $type, ... + { + $args = func_get_args(); + return $this->SetLabelType('xd', $args); + } + + /* + * Select label formating for Y tick labels, and for Y data labels + * (unless SetYDataLabelType was called). + * See SetLabelType() for details. + */ + function SetYLabelType() // Variable arguments: $type, ... + { + $args = func_get_args(); + return $this->SetLabelType('y', $args); + } + + /* + * Select label formatting for Y data labels, overriding SetYLabelType. + */ + function SetYDataLabelType() // Variable arguments: $type, ... + { + $args = func_get_args(); + return $this->SetLabelType('yd', $args); + } + + /* + * Set the date/time format code for X labels. + * Note: Use of SetXLabelType('time', $which_xtf) is preferred, because + * SetXTimeFormat does not also enable date/time formatting. + */ + function SetXTimeFormat($which_xtf) + { + $this->label_format['x']['time_format'] = $which_xtf; + return TRUE; + } + + /* + * Set the date/time format code for Y labels. + * Note: Use of SetYLabelType('time', $which_ytf) is preferred, because + * SetYTimeFormat does not also enable date/time formatting. + */ + function SetYTimeFormat($which_ytf) + { + $this->label_format['y']['time_format'] = $which_ytf; + return TRUE; + } + + /* + * Set number format parameters (decimal point and thousands separator) for + * 'data' mode label formatting, overriding the locale-defaults. + */ + function SetNumberFormat($decimal_point, $thousands_sep) + { + $this->decimal_point = $decimal_point; + $this->thousands_sep = $thousands_sep; + return TRUE; + } + + /* + * Set the text angle for X labels to $which_xla degrees. + */ + function SetXLabelAngle($which_xla) + { + $this->x_label_angle = $which_xla; + return TRUE; + } + + /* + * Set the text angle for Y labels to $which_xla degrees. + */ + function SetYLabelAngle($which_yla) + { + $this->y_label_angle = $which_yla; + return TRUE; + } + + /* + * Set the angle for X Data Labels to $which_xdla degrees. + * If not used, this defaults to the value set with SetXLabelAngle. + */ + function SetXDataLabelAngle($which_xdla) + { + $this->x_data_label_angle = $which_xdla; + return TRUE; + } + + /* + * Set the angle for Y Data Labels to $which_ydla degrees. + * If not used, this defaults to zero (unlike X data labels). + */ + function SetYDataLabelAngle($which_ydla) + { + $this->y_data_label_angle = $which_ydla; + return TRUE; + } + +///////////////////////////////////////////// +/////////// MISC +///////////////////////////////////////////// + + /* + * Checks the validity of an option. + * $which_opt String to check, such as the provided value of a function argument. + * $which_acc String of accepted choices. Must be lower-case, and separated + * by exactly ', ' (comma, space). + * $which_func Name of the calling function, for error messages. + * Returns the supplied option value, downcased and trimmed, if it is valid. + * Reports an error if the supplied option is not valid. + */ + protected function CheckOption($which_opt, $which_acc, $which_func) + { + $asked = strtolower(trim($which_opt)); + + // Look for the supplied value in a comma/space separated list. + if (strpos(", $which_acc,", ", $asked,") !== FALSE) + return $asked; + + $this->PrintError("$which_func(): '$which_opt' not in available choices: '$which_acc'."); + return NULL; + } + + /* + * Checks the validity of an array of options. + * $opt Array or string to check. + * $acc String of accepted choices. Must be lower-case, and separated + * by exactly ', ' (comma, space). + * $func Name of the calling function, for error messages. + * Returns a array option value(s), downcased and trimmed, if all entries in $opt are valid. + * Reports an error if any supplied option is not valid. Returns NULL if the error handler returns. + */ + protected function CheckOptionArray($opt, $acc, $func) + { + $opt_array = (array)$opt; + $result = array(); + foreach ($opt_array as $option) { + $choice = $this->CheckOption($option, $acc, $func); + if (is_null($choice)) return NULL; // In case CheckOption error handler returns + $result[] = $choice; + } + return $result; + } + + /* + * Check compatibility of a plot type and data type. + * This is called by the plot-type-specific drawing functions. + * $valid_types String of supported data types. Multiple values must be + * separated by exactly ', ' (comma, space). + * Returns True if the type is valid for this plot. + * Reports an error if the data type is not value. If the error is handled and + * the handler returns, this returns False. + */ + protected function CheckDataType($valid_types) + { + if (strpos(", $valid_types,", ", $this->data_type,") !== FALSE) + return TRUE; + + $this->PrintError("Data type '$this->data_type' is not valid for '$this->plot_type' plots." + . " Supported data type(s): '$valid_types'"); + return FALSE; + } + + /* + * Decode the data type into variables used to determine how to process a data array. + * The goal is minimize which functions understand the actual data type values. + * This sets the datatype_* variables for use by other member functions. + * datatype_implied : Implicit independent variable (e.g. text-data vs data-data) + * datatype_swapped_xy : Swapped X/Y (horizontal plot) + * datatype_error_bars : Data array has error bar data + * datatype_pie_single : Data array is for a pie chart with one row per slice + */ + protected function DecodeDataType() + { + $dt = $this->data_type; + + $this->datatype_implied = ($dt == 'text-data' || $dt == 'text-data-single' + || $dt == 'text-data-yx'); + $this->datatype_swapped_xy = ($dt == 'text-data-yx' || $dt == 'data-data-yx'); + $this->datatype_error_bars = ($dt == 'data-data-error'); + $this->datatype_pie_single = ($dt == 'text-data-single'); + } + + /* + * Make sure the data array is populated, and calculate the number of columns. + * This is called from DrawGraph. Calculates data_columns, which is the + * maximum number of dependent variable values (usually Y) in the data array rows. + * (For pie charts, this is the number of slices.) + * This depends on the data_type, unlike records_per_group (which was + * previously used to pad style arrays, but is not accurate). + * Returns True if the data array is OK, else reports an error (and may return False). + * Note error messages refer to the caller, the public DrawGraph(). + */ + protected function CheckDataArray() + { + // Test for missing image, which really should never happen. + if (!$this->img) { + return $this->PrintError('DrawGraph(): No image resource allocated'); + } + + // Test for missing or empty data array: + if (empty($this->data) || !is_array($this->data)) { + return $this->PrintError("DrawGraph(): No data array"); + } + if ($this->total_records == 0) { + return $this->PrintError('DrawGraph(): Empty data set'); + } + + // Decode the data type into functional flags. + $this->DecodeDataType(); + + // Calculate the maximum number of dependent values per independent value + // (e.g. Y for each X), or the number of pie slices. + if ($this->datatype_pie_single) { + $this->data_columns = $this->num_data_rows; // Special case for 1 type of pie chart. + } else { + $skip = $this->datatype_implied ? 1 : 2; // Skip data label and independent variable if used + $this->data_columns = $this->records_per_group - $skip; + if ($this->datatype_error_bars) // Each Y has +err and -err along with it + $this->data_columns = (int)($this->data_columns / 3); + } + return TRUE; + } + + /* + * Control headers for browser-side image caching. + * $which_browser_cache : True to allow browsers to cache the image. + */ + function SetBrowserCache($which_browser_cache) + { + $this->browser_cache = $which_browser_cache; + return TRUE; + } + + /* + * Set whether DrawGraph automatically outputs the image too. + * $which_pi : True to have DrawGraph call PrintImage at the end. + */ + function SetPrintImage($which_pi) + { + $this->print_image = $which_pi; + return TRUE; + } + + /* + * Set border for the plot area. + * Accepted values are: left, right, top, bottom, sides, none, full or an array of those. + */ + function SetPlotBorderType($pbt) + { + $this->plot_border_type = $this->CheckOptionArray($pbt, 'left, right, top, bottom, sides, none, full', + __FUNCTION__); + return !empty($this->plot_border_type); + } + + /* + * Set border style for the image. + * Accepted values are: raised, plain, solid, none + * 'solid' is the same as 'plain' except it fixes the color (see DrawImageBorder) + */ + function SetImageBorderType($sibt) + { + $this->image_border_type = $this->CheckOption($sibt, 'raised, plain, solid, none', __FUNCTION__); + return (boolean)$this->image_border_type; + } + + /* + * Set border width for the image to $width in pixels. + */ + function SetImageBorderWidth($width) + { + $this->image_border_width = $width; + return TRUE; + } + + /* + * Enable or disable drawing of the plot area background color. + */ + function SetDrawPlotAreaBackground($dpab) + { + $this->draw_plot_area_background = (bool)$dpab; + return TRUE; + } + + /* + * Enable or disable drawing of the X grid lines. + */ + function SetDrawXGrid($dxg) + { + $this->draw_x_grid = (bool)$dxg; + return TRUE; + } + + /* + * Enable or disable drawing of the Y grid lines. + */ + function SetDrawYGrid($dyg) + { + $this->draw_y_grid = (bool)$dyg; + return TRUE; + } + + /* + * Select dashed or solid grid lines. + * $ddg : True for dashed grid lines, false for solid grid lines. + */ + function SetDrawDashedGrid($ddg) + { + $this->dashed_grid = (bool)$ddg; + return TRUE; + } + + /* + * Enable or disable drawing of X Data Label Lines. + */ + function SetDrawXDataLabelLines($dxdl) + { + $this->draw_x_data_label_lines = (bool)$dxdl; + return TRUE; + } + + /* + * Set the main title text for the plot. + */ + function SetTitle($which_title) + { + $this->title_txt = $which_title; + return TRUE; + } + + /* + * Set the X axis title and position. + */ + function SetXTitle($which_xtitle, $which_xpos = 'plotdown') + { + if ($which_xtitle == '') + $which_xpos = 'none'; + + $this->x_title_pos = $this->CheckOption($which_xpos, 'plotdown, plotup, both, none', __FUNCTION__); + if (!$this->x_title_pos) return FALSE; + $this->x_title_txt = $which_xtitle; + return TRUE; + } + + /* + * Set the Y axis title and position. + */ + function SetYTitle($which_ytitle, $which_ypos = 'plotleft') + { + if ($which_ytitle == '') + $which_ypos = 'none'; + + $this->y_title_pos = $this->CheckOption($which_ypos, 'plotleft, plotright, both, none', __FUNCTION__); + if (!$this->y_title_pos) return FALSE; + $this->y_title_txt = $which_ytitle; + return TRUE; + } + + /* + * Set the size of the drop shadow for bar and pie charts. + * $which_s : Size of the drop shadow in pixels. + */ + function SetShading($which_s) + { + $this->shading = (int)$which_s; + return TRUE; + } + + /* + * Set the plot type (bars, points, ...) + */ + function SetPlotType($which_pt) + { + $avail_plot_types = implode(', ', array_keys(PHPlot::$plots)); // List of known plot types + $this->plot_type = $this->CheckOption($which_pt, $avail_plot_types, __FUNCTION__); + return (boolean)$this->plot_type; + } + + /* + * Set the position of the X axis. + * $pos : Axis position in world coordinates (as an integer). + */ + function SetXAxisPosition($pos='') + { + $this->x_axis_position = ($pos === '') ? $pos : (int)$pos; + return TRUE; + } + + /* + * Set the position of the Y axis. + * $pos : Axis position in world coordinates (as an integer). + */ + function SetYAxisPosition($pos='') + { + $this->y_axis_position = ($pos === '') ? $pos : (int)$pos; + return TRUE; + } + + /* + * Enable or disable drawing of the X axis line. + * $draw : True to draw the axis (default if not called), False to suppress it. + * This controls drawing of the axis line only, and not the ticks, labels, or grid. + */ + function SetDrawXAxis($draw) + { + $this->suppress_x_axis = !$draw; // See DrawXAxis() + return TRUE; + } + + /* + * Enable or disable drawing of the Y axis line. + * $draw : True to draw the axis (default if not called), False to suppress it. + * This controls drawing of the axis line only, and not the ticks, labels, or grid. + */ + function SetDrawYAxis($draw) + { + $this->suppress_y_axis = !$draw; // See DrawYAxis() + return TRUE; + } + + /* + * Select linear or log scale for the X axis. + */ + function SetXScaleType($which_xst) + { + $this->xscale_type = $this->CheckOption($which_xst, 'linear, log', __FUNCTION__); + return (boolean)$this->xscale_type; + } + + /* + * Select linear or log scale for the Y axis. + */ + function SetYScaleType($which_yst) + { + $this->yscale_type = $this->CheckOption($which_yst, 'linear, log', __FUNCTION__); + return (boolean)$this->yscale_type; + } + + /* + * Set the precision for numerically formatted X labels. + * $which_prec : Number of digits to display. + * Note: This is equivalent to: SetXLabelType('data', $which_prec) + */ + function SetPrecisionX($which_prec) + { + return $this->SetXLabelType('data', $which_prec); + } + + /* + * Set the precision for numerically formatted Y labels. + * $which_prec : Number of digits to display. + * Note: This is equivalent to: SetYLabelType('data', $which_prec) + */ + function SetPrecisionY($which_prec) + { + return $this->SetYLabelType('data', $which_prec); + } + + /* + * Set the line width (in pixels) for error bars. + */ + function SetErrorBarLineWidth($which_seblw) + { + $this->error_bar_line_width = $which_seblw; + return TRUE; + } + + /* + * Set the position for pie chart percentage labels. + * $which_blb : Real number between 0 and 1. + * Smaller values move the labels in towards the center. + */ + function SetLabelScalePosition($which_blp) + { + $this->label_scale_position = $which_blp; + return TRUE; + } + + /* + * Set the size (in pixels) of the "T" in error bars. + */ + function SetErrorBarSize($which_ebs) + { + $this->error_bar_size = $which_ebs; + return TRUE; + } + + /* + * Set the shape of the in error bars. + * $which_ebs : Error bar shape, 'tee' or 'line'. + */ + function SetErrorBarShape($which_ebs) + { + $this->error_bar_shape = $this->CheckOption($which_ebs, 'tee, line', __FUNCTION__); + return (boolean)$this->error_bar_shape; + } + + /* + * Synchronize the point shape and point size arrays. + * This is called just before drawing any plot that needs 'points'. + */ + protected function CheckPointParams() + { + // Make both point_shapes and point_sizes the same size, by padding the smaller. + $ps = count($this->point_sizes); + $pt = count($this->point_shapes); + + if ($ps < $pt) { + $this->pad_array($this->point_sizes, $pt); + $this->point_counts = $pt; + } elseif ($ps > $pt) { + $this->pad_array($this->point_shapes, $ps); + $this->point_counts = $ps; + } else { + $this->point_counts = $ps; + } + + // Note: PHPlot used to check and adjust point_sizes to be an even number here, + // for all 'diamond' and 'triangle' shapes. The reason for this having been + // lost, and the current maintainer seeing no sense it doing this for only + // some shapes, the code has been removed. But see what DrawDot() does. + } + + /* + * Set the point shape for each data set. + * $which_pt : Array (or single value) of valid point shapes. See also DrawDot() for valid shapes. + * The point shape and point sizes arrays are synchronized before drawing a graph + * that uses points. See CheckPointParams() + */ + function SetPointShapes($which_pt) + { + $this->point_shapes = $this->CheckOptionArray($which_pt, 'halfline, line, plus, cross, rect,' + . ' circle, dot, diamond, triangle, trianglemid, delta, yield, star, hourglass,' + . ' bowtie, target, box, home, up, down, none', __FUNCTION__); + return !empty($this->point_shapes); + } + + /* + * Set the point size for point plots. + * $which_ps : Array (or single value) of point sizes in pixels. + * The point shape and point sizes arrays are synchronized before drawing a graph + * that uses points. See CheckPointParams() + */ + function SetPointSizes($which_ps) + { + if (is_array($which_ps)) { + // Use provided array: + $this->point_sizes = $which_ps; + } elseif (!is_null($which_ps)) { + // Make the single value into an array: + $this->point_sizes = array($which_ps); + } + return TRUE; + } + + /* + * Sets whether lines should be broken at missing data. + * $bl : True to break the lines, false to connect around missing data. + * This only works with 'lines' and 'squared' plots. + */ + function SetDrawBrokenLines($bl) + { + $this->draw_broken_lines = (bool)$bl; + return TRUE; + } + + /* + * Set the data type, which defines the structure of the data array + * text-data: ('label', y1, y2, y3, ...) + * text-data-single: ('label', data), for some pie charts. + * data-data: ('label', x, y1, y2, y3, ...) + * data-data-error: ('label', x1, y1, e1+, e2-, y2, e2+, e2-, y3, e3+, e3-, ...) + * data-data-yx: ('label', y, x1, x2, x3, ..) + * text-data-yx: ('label', x1, x2, x3, ...) + */ + function SetDataType($which_dt) + { + //The next four lines are for past compatibility. + if ($which_dt == 'text-linear') $which_dt = 'text-data'; + elseif ($which_dt == 'linear-linear') $which_dt = 'data-data'; + elseif ($which_dt == 'linear-linear-error') $which_dt = 'data-data-error'; + elseif ($which_dt == 'text-data-pie') $which_dt = 'text-data-single'; + + $this->data_type = $this->CheckOption($which_dt, 'text-data, text-data-single, '. + 'data-data, data-data-error, '. + 'data-data-yx, text-data-yx', + __FUNCTION__); + return (boolean)$this->data_type; + } + + /* + * Copy the array passed as data values. We convert to numerical indexes, for its + * use for (or while) loops, which sometimes are faster. Performance improvements + * vary from 28% in DrawLines() to 49% in DrawArea() for plot drawing functions. + */ + function SetDataValues($which_dv) + { + $this->num_data_rows = count($which_dv); + $this->total_records = 0; + $this->data = array(); + $this->num_recs = array(); + for ($i = 0; $i < $this->num_data_rows; $i++) { + $this->data[$i] = array_values($which_dv[$i]); // convert to numerical indices. + + // Count size of each row, and total for the array. + $recs = count($this->data[$i]); + $this->total_records += $recs; + $this->num_recs[$i] = $recs; + } + // This is the size of the widest row in the data array + // Note records_per_group isn't used much anymore. See data_columns in CheckDataArray() + $this->records_per_group = max($this->num_recs); + return TRUE; + } + + /* + * Pad styles arrays for later use by plot drawing functions: + * This removes the need for $max_data_colors, etc. and $color_index = $color_index % $max_data_colors + * in DrawBars(), DrawLines(), etc. + * The arrays are padded to data_columns which is the maximum number of data sets. + * See CheckDataArray() for the calculation. + */ + protected function PadArrays() + { + $this->pad_array($this->line_widths, $this->data_columns); + $this->pad_array($this->line_styles, $this->data_columns); + $this->pad_array($this->ndx_data_colors, $this->data_columns); + $this->pad_array($this->ndx_data_border_colors, $this->data_columns); + // Other data color arrays are handled in the Need*Colors() functions. + + return TRUE; + } + + /* + * Pads an array with itself. This only works on 0-based sequential integer indexed arrays. + * $arr : The array (or scalar) to pad. This argument is modified. + * $size : Minimum size of the resulting array. + * If $arr is a scalar, it will be converted first to a single element array. + * If $arr has at least $size elements, it is unchanged. + * Otherwise, append elements of $arr to itself until it reaches $size elements. + */ + protected function pad_array(&$arr, $size) + { + if (! is_array($arr)) { + $arr = array($arr); + } + $n = count($arr); + $base = 0; + while ($n < $size) $arr[$n++] = $arr[$base++]; + } + + /* + * Format a floating-point number. + * $number : A floating point number to format + * $decimals : Number of decimal places in the result + * Returns the formatted result. + * This is like PHP's number_format, but uses class variables for separators. + * The separators will default to locale-specific values, if available. + */ + protected function number_format($number, $decimals=0) + { + if (!isset($this->decimal_point) || !isset($this->thousands_sep)) { + // Load locale-specific values from environment, unless disabled: + if (empty($this->locale_override)) + @setlocale(LC_ALL, ''); + // Fetch locale settings: + $locale = @localeconv(); + if (isset($locale['decimal_point']) && isset($locale['thousands_sep'])) { + $this->decimal_point = $locale['decimal_point']; + $this->thousands_sep = $locale['thousands_sep']; + } else { + // Locale information not available. + $this->decimal_point = '.'; + $this->thousands_sep = ','; + } + } + return number_format($number, $decimals, $this->decimal_point, $this->thousands_sep); + } + + /* + * Register a callback (hook) function + * $reason : A pre-defined name where a callback can be defined. + * $function : The name of a function to register for callback, or an instance/method + * pair in an array (see 'callbacks' in the PHP reference manual). + * $arg : Optional argument to supply to the callback function when it is triggered. + * (Often called "clientData") + * Returns True if the callback reason is valid, else False. + */ + function SetCallback($reason, $function, $arg = NULL) + { + // Use array_key_exists because valid reason keys have NULL as value. + if (!array_key_exists($reason, $this->callbacks)) + return FALSE; + $this->callbacks[$reason] = array($function, $arg); + return TRUE; + } + + /* + * Return the name of a function registered for callback. See SetCallBack. + * $reason - A pre-defined name where a callback can be defined. + * Returns the current callback function (name or array) for the given reason, + * or False if there was no active callback or the reason is not valid. + * Note you can safely test the return value with a simple 'if', as + * no valid function name evaluates to false. + */ + function GetCallback($reason) + { + if (isset($this->callbacks[$reason])) + return $this->callbacks[$reason][0]; + return FALSE; + } + + /* + * Un-register (remove) a function registered for callback. + * $reason - A pre-defined name where a callback can be defined. + * Returns: True if it was a valid callback reason, else False. + * Note: Returns True whether or not there was a callback registered. + */ + function RemoveCallback($reason) + { + if (!array_key_exists($reason, $this->callbacks)) + return FALSE; + $this->callbacks[$reason] = NULL; + return TRUE; + } + + /* + * Invoke a callback, if one is registered. + * Accepts a variable number of arguments >= 1: + * $reason : A string naming the callback. + * ... : Zero or more additional arguments to be passed to the + * callback function, after the passthru argument: + * callback_function($image, $passthru, ...) + * Returns: whatever value (if any) was returned by the callback. + */ + protected function DoCallback() // Note: Variable arguments + { + $args = func_get_args(); + $reason = $args[0]; + if (!isset($this->callbacks[$reason])) + return; + list($function, $args[0]) = $this->callbacks[$reason]; + array_unshift($args, $this->img); + // Now args[] looks like: img, passthru, extra args... + return call_user_func_array($function, $args); + } + + /* + * Allocate colors for the plot. + * This is called by DrawGraph to allocate the colors needed for the plot. Each selectable + * color has already been validated, parsed into an array (r,g,b,a), and stored into a member + * variable. Now the GD color indexes are assigned and stored into the ndx_*_color variables. + * This is deferred here to avoid allocating unneeded colors and to avoid order dependencies, + * especially with the transparent color. + * + * For drawing data elements, only the main data colors and border colors are allocated here. + * Dark colors and error bar colors are allocated by Need*Color() functions. + * (Data border colors default to just black, so there is no cost to always allocating.) + * + * Data color allocation works as follows. If there is a data_color callback, then allocate all + * defined data colors (because the callback can use them however it wants). Otherwise, only allocate + * the number of colors that will be used. This is the larger of the number of data sets and the + * number of legend lines. + */ + protected function SetColorIndexes() + { + $this->ndx_bg_color = $this->GetColorIndex($this->bg_color); // Background first + $this->ndx_plot_bg_color = $this->GetColorIndex($this->plot_bg_color); + if ($this->image_border_type != 'none') { + $this->ndx_i_border = $this->GetColorIndex($this->i_border); + $this->ndx_i_border_dark = $this->GetDarkColorIndex($this->i_border); + } + + // Handle defaults for X and Y title colors. + $this->ndx_title_color = $this->GetColorIndex($this->title_color); + if (empty($this->x_title_color)) { + $this->ndx_x_title_color = $this->ndx_title_color; + } else { + $this->ndx_x_title_color = $this->GetColorIndex($this->x_title_color); + } + if (empty($this->y_title_color)) { + $this->ndx_y_title_color = $this->ndx_title_color; + } else { + $this->ndx_y_title_color = $this->GetColorIndex($this->y_title_color); + } + + $this->ndx_text_color = $this->GetColorIndex($this->text_color); + $this->ndx_grid_color = $this->GetColorIndex($this->grid_color); + $this->ndx_light_grid_color = $this->GetColorIndex($this->light_grid_color); + $this->ndx_tick_color = $this->GetColorIndex($this->tick_color); + + // Maximum number of data & border colors to allocate: + if ($this->GetCallback('data_color')) { + $n_data = count($this->data_colors); // Need all of them + $n_border = count($this->data_border_colors); + } else { + $n_data = max($this->data_columns, empty($this->legend) ? 0 : count($this->legend)); + $n_border = $n_data; // One border color per data color + } + + // Allocate main data colors. For other colors used for data, see the functions which follow. + $this->ndx_data_colors = $this->GetColorIndexArray($this->data_colors, $n_data); + $this->ndx_data_border_colors = $this->GetColorIndexArray($this->data_border_colors, $n_border); + + // Set up a color as transparent, if SetTransparentColor was used. + if (!empty($this->transparent_color)) { + imagecolortransparent($this->img, $this->GetColorIndex($this->transparent_color)); + } + } + + /* + * Allocate dark-shade data colors. Called if needed by graph drawing functions. + */ + protected function NeedDataDarkColors() + { + // This duplicates the calculation in SetColorIndexes() for number of data colors to allocate. + if ($this->GetCallback('data_color')) { + $n_data = count($this->data_colors); + } else { + $n_data = max($this->data_columns, empty($this->legend) ? 0 : count($this->legend)); + } + $this->ndx_data_dark_colors = $this->GetDarkColorIndexArray($this->data_colors, $n_data); + $this->pad_array($this->ndx_data_dark_colors, $this->data_columns); + } + + /* + * Allocate error bar colors. Called if needed by graph drawing functions. + */ + protected function NeedErrorBarColors() + { + // This is similar to the calculation in SetColorIndexes() for number of data colors to allocate. + if ($this->GetCallback('data_color')) { + $n_err = count($this->error_bar_colors); + } else { + $n_err = max($this->data_columns, empty($this->legend) ? 0 : count($this->legend)); + } + $this->ndx_error_bar_colors = $this->GetColorIndexArray($this->error_bar_colors, $n_err); + $this->pad_array($this->ndx_error_bar_colors, $this->data_columns); + } + + /* + * Determine if, and where, to draw Data Value Labels. + * $label_control : Label position control. Either x_data_label_pos or y_data_label_pos. + * &$x_adj, &$y_adj : Returns X,Y adjustments (offset in pixels) to the text position. + * &$h_align, &$v_align : Returns horizontal and vertical alignment for the label. + * The above 4 argument values should be passed to DrawDataValueLabel() + * Returns True if data value labels should be drawn (based on $label_control), else False. + * This is used for plot types other than bars/stackedbars (which have their own way of doing it). + * It uses two member variables (unset by default): data_value_label_angle and data_value_label_distance + * to define the vector to the label. Default is 90 degrees at 5 pixels. + */ + protected function CheckDataValueLabels($label_control, &$x_adj, &$y_adj, &$h_align, &$v_align) + { + if ($label_control != 'plotin') + return FALSE; // No data value labels + $angle = deg2rad(isset($this->data_value_label_angle) ? $this->data_value_label_angle : 90); + $radius = isset($this->data_value_label_distance) ? $this->data_value_label_distance : 5; + $cos = cos($angle); + $sin = sin($angle); + $x_adj = (int)($radius * $cos); + $y_adj = -(int)($radius * $sin); // Y is reversed in device coordinates + + // Choose from 8 (not 9, center/center can't happen) text alignments based on angle: + if ($sin >= 0.383) $v_align = 'bottom'; // 0.383 = sin(360deg / 16) + elseif ($sin >= -0.383) $v_align = 'center'; + else $v_align = 'top'; + if ($cos >= 0.383) $h_align = 'left'; + elseif ($cos >= -0.383) $h_align = 'center'; + else $h_align = 'right'; + return TRUE; + } + +////////////////////////////////////////////////////////// +/////////// DATA ANALYSIS, SCALING AND TRANSLATION +////////////////////////////////////////////////////////// + + /* + * Analyzes the data array and calculates the minimum and maximum values. + * In this function, IV refers to the independent variable, and DV the dependent variable. + * For most plots, IV is X and DV is Y. For swapped X/Y plots, IV is Y and DV is X. + * At the end of the function, IV and DV ranges get assigned into X or Y. + * + * The data type mostly determines the data array structure, but some plot types do special + * things such as sum the values in a row. This information is in the plots[] array. + * + * This calculates min_x, max_x, min_y, and max_y. It also calculates two arrays + * data_min[] and data_max[] with per-row min and max values. These are used for + * data label lines. For normal (unswapped) data, these are the Y range for each X. + * For swapped X/Y data, they are the X range for each Y. + */ + protected function FindDataLimits() + { + // Does this plot type need special processing of the data values? + $sum_vals = !empty(PHPlot::$plots[$this->plot_type]['sum_vals']); // Add up values in each row + $abs_vals = !empty(PHPlot::$plots[$this->plot_type]['abs_vals']); // Take absolute values + + // These need to be initialized in case there are multiple plots and missing data points. + $this->data_min = array(); + $this->data_max = array(); + + // Independent values are in the data array or assumed? + if ($this->datatype_implied) { + $all_iv = array(0, $this->num_data_rows - 1); + } else { + $all_iv = array(); + } + + // Process all rows of data: + for ($i = 0; $i < $this->num_data_rows; $i++) { + $n_vals = $this->num_recs[$i]; + $j = 1; // Skips label at [0] + + if (!$this->datatype_implied) { + $all_iv[] = (double)$this->data[$i][$j++]; + } + + if ($sum_vals) { + $all_dv = array(0, 0); // One limit is 0, other calculated below + } else { + $all_dv = array(); + } + while ($j < $n_vals) { + if (is_numeric($this->data[$i][$j])) { + $val = (double)$this->data[$i][$j++]; + + if ($this->datatype_error_bars) { + $all_dv[] = $val + (double)$this->data[$i][$j++]; + $all_dv[] = $val - (double)$this->data[$i][$j++]; + } else { + if ($abs_vals) { + $val = abs($val); // Use absolute values + } + if ($sum_vals) { + $all_dv[1] += $val; // Sum of values + } else { + $all_dv[] = $val; // List of all values + } + } + } else { // Missing DV value + $j++; + if ($this->datatype_error_bars) $j += 2; + } + } + if (!empty($all_dv)) { + $this->data_min[$i] = min($all_dv); // Store per-row DV range + $this->data_max[$i] = max($all_dv); + } + } + + if ($this->datatype_swapped_xy) { + // Assign min and max for swapped X/Y plots: IV=Y and DV=X + $this->min_y = min($all_iv); + $this->max_y = max($all_iv); + if (empty($this->data_min)) { // Guard against regressive case: No X at all + $this->min_x = 0; + $this->max_x = 0; + } else { + $this->min_x = min($this->data_min); // Store global X range + $this->max_x = max($this->data_max); + } + } else { + // Assign min and max for normal plots: IV=X and DV=Y + $this->min_x = min($all_iv); + $this->max_x = max($all_iv); + if (empty($this->data_min)) { // Guard against regressive case: No Y at all + $this->min_y = 0; + $this->max_y = 0; + } else { + $this->min_y = min($this->data_min); // Store global Y range + $this->max_y = max($this->data_max); + } + } + + if ($this->GetCallback('debug_scale')) { + $this->DoCallback('debug_scale', __FUNCTION__, array( + 'min_x' => $this->min_x, 'min_y' => $this->min_y, + 'max_x' => $this->max_x, 'max_y' => $this->max_y)); + } + return TRUE; + } + + /* + * Calculates image margins on the fly from title positions and sizes, + * and tick labels positions and sizes. + * + * A picture of the locations of elements and spacing can be found in the + * PHPlot Reference Manual. + * + * Calculates the following (class variables unless noted): + * + * Plot area margins (see note below): + * y_top_margin + * y_bot_margin + * x_left_margin + * x_right_margin + * + * Title sizes (these are now local, not class variables, since they are not used elsewhere): + * title_height : Height of main title + * x_title_height : Height of X axis title, 0 if no X title + * y_title_width : Width of Y axis title, 0 if no Y title + * + * Tick/Data label offsets, relative to plot_area: + * x_label_top_offset, x_label_bot_offset, x_label_axis_offset + * y_label_left_offset, y_label_right_offset, y_label_axis_offset + * + * Title offsets, relative to plot area: + * x_title_top_offset, x_title_bot_offset + * y_title_left_offset, y_title_left_offset + * title_offset (for main title, relative to image edge) + * + * Note: The margins are calculated, but not stored, if margins or plot area were + * set by the user with SetPlotAreaPixels or SetMarginsPixels. The margin + * calculation is mixed in with the offset variables, so it doesn't seem worth the + * trouble to separate them. + * + * If the $maximize argument is true, we use the full image size, minus safe_margin + * and main title, for the plot. This is for pie charts which have no axes or X/Y titles. + */ + protected function CalcMargins($maximize) + { + // This is the line-to-line or line-to-text spacing: + $gap = $this->safe_margin; + // Initial margin on each side takes into account a possible image border. + // For compatibility, if border is 1 or 2, don't increase the margins. + $base_margin = max($gap, $this->GetImageBorderWidth() + 3); + $this->title_offset = $base_margin; // For use in DrawTitle + + // Minimum margin on each side. This reduces the chance that the + // right-most tick label (for example) will run off the image edge + // if there are no titles on that side. + $min_margin = 2 * $gap + $base_margin; + + // Calculate the title sizes: + list($unused, $title_height) = $this->SizeText($this->fonts['title'], 0, $this->title_txt); + list($unused, $x_title_height) = $this->SizeText($this->fonts['x_title'], 0, $this->x_title_txt); + list($y_title_width, $unused) = $this->SizeText($this->fonts['y_title'], 90, $this->y_title_txt); + + // Special case for maximum area usage with no X/Y titles or labels, only main title: + if ($maximize) { + if (!isset($this->x_left_margin)) + $this->x_left_margin = $base_margin; + if (!isset($this->x_right_margin)) + $this->x_right_margin = $base_margin; + if (!isset($this->y_top_margin)) { + $this->y_top_margin = $base_margin; + if ($title_height > 0) + $this->y_top_margin += $title_height + $gap; + } + if (!isset($this->y_bot_margin)) + $this->y_bot_margin = $base_margin; + + return TRUE; + } + + // Make local variables for these. (They get used a lot and I'm tired of this, this, this.) + $x_tick_label_pos = $this->x_tick_label_pos; + $x_data_label_pos = $this->x_data_label_pos; + $x_tick_pos = $this->x_tick_pos; + $x_tick_len = $this->x_tick_length; + $y_tick_label_pos = $this->y_tick_label_pos; + $y_tick_pos = $this->y_tick_pos; + $y_tick_len = $this->y_tick_length; + $y_data_label_pos = $this->y_data_label_pos; + + // For X/Y tick and label position of 'xaxis' or 'yaxis', determine if the axis happens to be + // on an edge of a plot. If it is, we need to account for the margins there. + if ($this->x_axis_position <= $this->plot_min_y) + $x_axis_pos = 'bottom'; + elseif ($this->x_axis_position >= $this->plot_max_y) + $x_axis_pos = 'top'; + else + $x_axis_pos = 'none'; + if ($this->y_axis_position <= $this->plot_min_x) + $y_axis_pos = 'left'; + elseif ($this->y_axis_position >= $this->plot_max_x) + $y_axis_pos = 'right'; + else + $y_axis_pos = 'none'; + + // Calculate the heights for X tick and data labels, and the max (used if they are overlaid): + $x_data_label_height = ($x_data_label_pos == 'none') ? 0 : $this->CalcMaxDataLabelSize('x'); + $x_tick_label_height = ($x_tick_label_pos == 'none') ? 0 : $this->CalcMaxTickLabelSize('x'); + $x_max_label_height = max($x_data_label_height, $x_tick_label_height); + + // Calculate the space needed above and below the plot for X tick and X data labels: + + // Above the plot: + $tick_labels_above = ($x_tick_label_pos == 'plotup' || $x_tick_label_pos == 'both' + || ($x_tick_label_pos == 'xaxis' && $x_axis_pos == 'top')); + $data_labels_above = ($x_data_label_pos == 'plotup' || $x_data_label_pos == 'both'); + if ($tick_labels_above) { + if ($data_labels_above) { + $label_height_above = $x_max_label_height; + } else { + $label_height_above = $x_tick_label_height; + } + } elseif ($data_labels_above) { + $label_height_above = $x_data_label_height; + } else { + $label_height_above = 0; + } + + // Below the plot: + $tick_labels_below = ($x_tick_label_pos == 'plotdown' || $x_tick_label_pos == 'both' + || ($x_tick_label_pos == 'xaxis' && $x_axis_pos == 'bottom')); + $data_labels_below = ($x_data_label_pos == 'plotdown' || $x_data_label_pos == 'both'); + if ($tick_labels_below) { + if ($data_labels_below) { + $label_height_below = $x_max_label_height; + } else { + $label_height_below = $x_tick_label_height; + } + } elseif ($data_labels_below) { + $label_height_below = $x_data_label_height; + } else { + $label_height_below = 0; + } + + // Calculate the width for Y tick and data labels, if on, and the max: + // Note CalcMaxDataLabelSize('y') returns 0 except for swapped X/Y plots. + $y_data_label_width = ($y_data_label_pos == 'none') ? 0 : $this->CalcMaxDataLabelSize('y'); + $y_tick_label_width = ($y_tick_label_pos == 'none') ? 0 : $this->CalcMaxTickLabelSize('y'); + $y_max_label_width = max($y_data_label_width, $y_tick_label_width); + + // Calculate the space needed left and right of the plot for Y tick and Y data labels: + // (Y data labels here are for swapped X/Y plots such has horizontal bars) + + // Left of the plot: + $tick_labels_left = ($y_tick_label_pos == 'plotleft' || $y_tick_label_pos == 'both' + || ($y_tick_label_pos == 'yaxis' && $y_axis_pos == 'left')); + $data_labels_left = ($y_data_label_pos == 'plotleft' || $y_data_label_pos == 'both'); + if ($tick_labels_left) { + if ($data_labels_left) { + $label_width_left = $y_max_label_width; + } else { + $label_width_left = $y_tick_label_width; + } + } elseif ($data_labels_left) { + $label_width_left = $y_data_label_width; + } else { + $label_width_left = 0; + } + + // Right of the plot: + $tick_labels_right = ($y_tick_label_pos == 'plotright' || $y_tick_label_pos == 'both' + || ($y_tick_label_pos == 'yaxis' && $y_axis_pos == 'right')); + $data_labels_right = ($y_data_label_pos == 'plotright' || $y_data_label_pos == 'both'); + if ($tick_labels_right) { + if ($data_labels_right) { + $label_width_right = $y_max_label_width; + } else { + $label_width_right = $y_tick_label_width; + } + } elseif ($data_labels_right) { + $label_width_right = $y_data_label_width; + } else { + $label_width_right = 0; + } + + ///////// Calculate margins: + + // Calculating Top and Bottom margins: + // y_top_margin: Main title, Upper X title, X ticks and tick labels, and X data labels: + // y_bot_margin: Lower title, ticks and tick labels, and data labels: + $top_margin = $base_margin; + $bot_margin = $base_margin; + $this->x_title_top_offset = $gap; + $this->x_title_bot_offset = $gap; + + // Space for main title? + if ($title_height > 0) + $top_margin += $title_height + $gap; + + // Space for X Title? + if ($x_title_height > 0) { + $pos = $this->x_title_pos; + if ($pos == 'plotup' || $pos == 'both') + $top_margin += $x_title_height + $gap; + if ($pos == 'plotdown' || $pos == 'both') + $bot_margin += $x_title_height + $gap; + } + + // Space for X Labels above the plot? + if ($label_height_above > 0) { + $top_margin += $label_height_above + $gap; + $this->x_title_top_offset += $label_height_above + $gap; + } + + // Space for X Labels below the plot? + if ($label_height_below > 0) { + $bot_margin += $label_height_below + $gap; + $this->x_title_bot_offset += $label_height_below + $gap; + } + + // Space for X Ticks above the plot? + if ($x_tick_pos == 'plotup' || $x_tick_pos == 'both' + || ($x_tick_pos == 'xaxis' && $x_axis_pos == 'top')) { + $top_margin += $x_tick_len; + $this->x_label_top_offset = $x_tick_len + $gap; + $this->x_title_top_offset += $x_tick_len; + } else { + // No X Ticks above the plot: + $this->x_label_top_offset = $gap; + } + + // Space for X Ticks below the plot? + if ($x_tick_pos == 'plotdown' || $x_tick_pos == 'both' + || ($x_tick_pos == 'xaxis' && $x_axis_pos == 'bottom')) { + $bot_margin += $x_tick_len; + $this->x_label_bot_offset = $x_tick_len + $gap; + $this->x_title_bot_offset += $x_tick_len; + } else { + // No X Ticks below the plot: + $this->x_label_bot_offset = $gap; + } + // Label offsets for on-axis ticks: + if ($x_tick_pos == 'xaxis') { + $this->x_label_axis_offset = $x_tick_len + $gap; + } else { + $this->x_label_axis_offset = $gap; + } + + // Calculating Left and Right margins: + // x_left_margin: Left Y title, Y ticks and tick labels: + // x_right_margin: Right Y title, Y ticks and tick labels: + $left_margin = $base_margin; + $right_margin = $base_margin; + $this->y_title_left_offset = $gap; + $this->y_title_right_offset = $gap; + + // Space for Y Title? + if ($y_title_width > 0) { + $pos = $this->y_title_pos; + if ($pos == 'plotleft' || $pos == 'both') + $left_margin += $y_title_width + $gap; + if ($pos == 'plotright' || $pos == 'both') + $right_margin += $y_title_width + $gap; + } + + // Space for Y Labels left of the plot? + if ($label_width_left > 0) { + $left_margin += $label_width_left + $gap; + $this->y_title_left_offset += $label_width_left + $gap; + } + + // Space for Y Labels right of the plot? + if ($label_width_right > 0) { + $right_margin += $label_width_right + $gap; + $this->y_title_right_offset += $label_width_right + $gap; + } + + // Space for Y Ticks left of plot? + if ($y_tick_pos == 'plotleft' || $y_tick_pos == 'both' + || ($y_tick_pos == 'yaxis' && $y_axis_pos == 'left')) { + $left_margin += $y_tick_len; + $this->y_label_left_offset = $y_tick_len + $gap; + $this->y_title_left_offset += $y_tick_len; + } else { + // No Y Ticks left of plot: + $this->y_label_left_offset = $gap; + } + + // Space for Y Ticks right of plot? + if ($y_tick_pos == 'plotright' || $y_tick_pos == 'both' + || ($y_tick_pos == 'yaxis' && $y_axis_pos == 'right')) { + $right_margin += $y_tick_len; + $this->y_label_right_offset = $y_tick_len + $gap; + $this->y_title_right_offset += $y_tick_len; + } else { + // No Y Ticks right of plot: + $this->y_label_right_offset = $gap; + } + + // Label offsets for on-axis ticks: + if ($x_tick_pos == 'yaxis') { + $this->y_label_axis_offset = $y_tick_len + $gap; + } else { + $this->y_label_axis_offset = $gap; + } + + // Apply the minimum margins and store in the object. + // Do not set margins which were user-defined (see note at top of function). + if (!isset($this->y_top_margin)) + $this->y_top_margin = max($min_margin, $top_margin); + if (!isset($this->y_bot_margin)) + $this->y_bot_margin = max($min_margin, $bot_margin); + if (!isset($this->x_left_margin)) + $this->x_left_margin = max($min_margin, $left_margin); + if (!isset($this->x_right_margin)) + $this->x_right_margin = max($min_margin, $right_margin); + + if ($this->GetCallback('debug_scale')) { + // (Too bad compact() doesn't work on class member variables...) + $this->DoCallback('debug_scale', __FUNCTION__, array( + 'label_height_above' => $label_height_above, + 'label_height_below' => $label_height_below, + 'label_width_left' => $label_width_left, + 'label_width_right' => $label_width_right, + 'x_tick_len' => $x_tick_len, + 'y_tick_len' => $y_tick_len, + 'x_left_margin' => $this->x_left_margin, + 'x_right_margin' => $this->x_right_margin, + 'y_top_margin' => $this->y_top_margin, + 'y_bot_margin' => $this->y_bot_margin, + 'x_label_top_offset' => $this->x_label_top_offset, + 'x_label_bot_offset' => $this->x_label_bot_offset, + 'y_label_left_offset' => $this->y_label_left_offset, + 'y_label_right_offset' => $this->y_label_right_offset, + 'x_title_top_offset' => $this->x_title_top_offset, + 'x_title_bot_offset' => $this->x_title_bot_offset, + 'y_title_left_offset' => $this->y_title_left_offset, + 'y_title_right_offset' => $this->y_title_right_offset)); + } + + return TRUE; + } + + /* + * Calculate the plot area (device coordinates) from the margins. + * (This used to be part of SetPlotAreaPixels.) + * The margins might come from SetMarginsPixels, SetPlotAreaPixels, + * or CalcMargins. + */ + protected function CalcPlotAreaPixels() + { + $this->plot_area = array($this->x_left_margin, $this->y_top_margin, + $this->image_width - $this->x_right_margin, + $this->image_height - $this->y_bot_margin); + $this->plot_area_width = $this->plot_area[2] - $this->plot_area[0]; + $this->plot_area_height = $this->plot_area[3] - $this->plot_area[1]; + + $this->DoCallback('debug_scale', __FUNCTION__, $this->plot_area); + return TRUE; + } + + /* + * Set the margins in pixels (left, right, top, bottom) + * This determines the plot area, equivalent to SetPlotAreaPixels(). + * Deferred calculations now occur in CalcPlotAreaPixels(). + */ + function SetMarginsPixels($which_lm = NULL, $which_rm = NULL, $which_tm = NULL, $which_bm = NULL) + { + $this->x_left_margin = $which_lm; + $this->x_right_margin = $which_rm; + $this->y_top_margin = $which_tm; + $this->y_bot_margin = $which_bm; + + return TRUE; + } + + /* + * Sets the limits for the plot area. + * This stores the margins, not the area. That may seem odd, but + * the idea is to make SetPlotAreaPixels and SetMarginsPixels two + * ways to accomplish the same thing, and the deferred calculations + * in CalcMargins and CalcPlotAreaPixels don't need to know which + * was used. + * (x1, y1) - Upper left corner of the plot area + * (x2, y2) - Lower right corner of the plot area + */ + function SetPlotAreaPixels($x1 = NULL, $y1 = NULL, $x2 = NULL, $y2 = NULL) + { + $this->x_left_margin = $x1; + if (isset($x2)) $this->x_right_margin = $this->image_width - $x2; + else unset($this->x_right_margin); + $this->y_top_margin = $y1; + if (isset($y2)) $this->y_bot_margin = $this->image_height - $y2; + else unset($this->y_bot_margin); + + return TRUE; + } + + /* + * Calculate the World Coordinate limits of the plot area. + * This goes with SetPlotAreaWorld, but the calculations are + * deferred until the graph is being drawn. + * Uses and sets: plot_min_x, plot_max_x, plot_min_y, plot_max_y + * These can be user-supplied or NULL to auto-calculate. + * Pre-requisites: FindDataLimits() calculates min_x, max_x, min_y, max_y + * which are the limits of the data to be plotted. + * + * The general method is this: + * If any part of the range is user-defined (via SetPlotAreaWorld), + * use the user-defined value. + * Else, if this is an implicitly-defined independent variable, + * use the fixed range of 0 to (max+1). + * Else, if this is an explicitly-defined independent variable, + * use the exact data range (min to max). + * Else, this is the dependent variable, so define a range which + * includes and exceeds the data range by a bit. + */ + protected function CalcPlotAreaWorld() + { + // Data array omits X or Y? + $implied_x = $this->datatype_implied && !$this->datatype_swapped_xy; + $implied_y = $this->datatype_implied && $this->datatype_swapped_xy; + + if (isset($this->plot_min_x) && $this->plot_min_x !== '') + $xmin = $this->plot_min_x; // Use user-provided value + elseif ($implied_x) + $xmin = 0; // Implied X starts at zero + elseif ($this->datatype_swapped_xy) + // If X is the dependent variable, leave some room below. + $xmin = floor($this->min_x - abs($this->min_x) * 0.1); + else + $xmin = $this->min_x; // Otherwise just start at the min data X + + if (isset($this->plot_max_x) && $this->plot_max_x !== '') + $xmax = $this->plot_max_x; // Use user-provided value + elseif ($implied_x) + $xmax = $this->max_x + 1; // Implied X ends after last value + elseif ($this->datatype_swapped_xy) + // If X is the dependent variable, leave some room above. + $xmax = ceil($this->max_x + abs($this->max_x) * 0.1); + else + $xmax = $this->max_x; // Otherwise just end at the max data X + + if (isset($this->plot_min_y) && $this->plot_min_y !== '') + $ymin = $this->plot_min_y; // Use user-provided value + elseif ($implied_y) + $ymin = 0; // Implied Y starts at zero + elseif ($this->datatype_swapped_xy) + $ymin = $this->min_y; // Start at min data Y + else + // If Y is the dependent variable, leave some room below. + $ymin = floor($this->min_y - abs($this->min_y) * 0.1); + + if (isset($this->plot_max_y) && $this->plot_max_y !== '') + $ymax = $this->plot_max_y; // Use user-provided value + elseif ($implied_y) + $ymax = $this->max_y + 1; // Implied Y ends after last value + elseif ($this->datatype_swapped_xy) + $ymax = $this->max_y; // End at max data Y + else + // If Y is the dependent variable, leave some room above. + $ymax = ceil($this->max_y + abs($this->max_y) * 0.1); + + // Error checking + + if ($ymin == $ymax) + $ymax++; + if ($xmin == $xmax) + $xmax++; + + if ($this->yscale_type == 'log') { + if ($ymin <= 0) { + $ymin = 1; + } + if ($ymax <= 0) { + // Note: Error messages reference the user function, not this function. + return $this->PrintError('SetPlotAreaWorld(): Log plots need data greater than 0'); + } + } + + if ($ymax <= $ymin) { + return $this->PrintError('SetPlotAreaWorld(): Error in data - max not greater than min'); + } + + $this->plot_min_x = $xmin; + $this->plot_max_x = $xmax; + $this->plot_min_y = $ymin; + $this->plot_max_y = $ymax; + if ($this->GetCallback('debug_scale')) { + $this->DoCallback('debug_scale', __FUNCTION__, array( + 'plot_min_x' => $this->plot_min_x, 'plot_min_y' => $this->plot_min_y, + 'plot_max_x' => $this->plot_max_x, 'plot_max_y' => $this->plot_max_y)); + } + return TRUE; + } + + /* + * Stores the desired World Coordinate range of the plot. + * The user calls this to force one or more of the range limits to + * specific values. Anything not set will be calculated in CalcPlotAreaWorld(). + */ + function SetPlotAreaWorld($xmin=NULL, $ymin=NULL, $xmax=NULL, $ymax=NULL) + { + $this->plot_min_x = $xmin; + $this->plot_max_x = $xmax; + $this->plot_min_y = $ymin; + $this->plot_max_y = $ymax; + return TRUE; + } + + /* + * Calculate the width (or height) of bars for bar plots. + * $stacked : If true, this is a stacked bar plot (1 bar per group). + * $verticals : If false, this is a horizontal bar plot. + * This calculates: + * record_bar_width : Allocated width for each bar (including gaps) + * actual_bar_width : Actual drawn width of each bar + * bar_adjust_gap : Gap on each side of each bar (0 if they touch) + * For the case $verticals=False, horizontal bars are being drawn, + * but the same variable names are used. Think of "bar_width" as being + * the width if you are standing on the Y axis looking towards positive X. + */ + protected function CalcBarWidths($stacked, $verticals) + { + // group_width is the width of a group, including padding + if ($verticals) { + $group_width = $this->plot_area_width / $this->num_data_rows; + } else { + $group_width = $this->plot_area_height / $this->num_data_rows; + } + + // Actual number of bar spaces in the group. This includes the drawn bars, and + // 'bar_extra_space'-worth of extra bars. + if ($stacked) { + $num_spots = 1 + $this->bar_extra_space; + } else { + $num_spots = $this->data_columns + $this->bar_extra_space; + } + + // record_bar_width is the width of each bar's allocated area. + // If bar_width_adjust=1 this is the width of the bar, otherwise + // the bar is centered inside record_bar_width. + // The equation is: + // group_frac_width * group_width = record_bar_width * num_spots + $this->record_bar_width = $this->group_frac_width * $group_width / $num_spots; + + // Note that the extra space due to group_frac_width and bar_extra_space will be + // evenly divided on each side of the group: the drawn bars are centered in the group. + + // Within each bar's allocated space, if bar_width_adjust=1 the bar fills the + // space, otherwise it is centered. + // This is the actual drawn bar width: + $this->actual_bar_width = $this->record_bar_width * $this->bar_width_adjust; + // This is the gap on each side of the bar (0 if bar_width_adjust=1): + $this->bar_adjust_gap = ($this->record_bar_width - $this->actual_bar_width) / 2; + + if ($this->GetCallback('debug_scale')) { + $this->DoCallback('debug_scale', __FUNCTION__, array( + 'record_bar_width' => $this->record_bar_width, + 'actual_bar_width' => $this->actual_bar_width, + 'bar_adjust_gap' => $this->bar_adjust_gap)); + } + return TRUE; + } + + /* + * Calculate X and Y Axis Positions, world coordinates. + * This needs the min/max x/y range set by CalcPlotAreaWorld. + * It adjusts or sets x_axis_position and y_axis_position per the data. + * Empty string means the values need to be calculated; otherwise they + * are supplied but need to be validated against the World area. + * + * Note: This used to be in CalcTranslation, but CalcMargins needs it too. + * This does not calculate the pixel values of the axes. That happens in + * CalcTranslation, after scaling is set up (which has to happen after + * margins are set up). + * + * For vertical plots, the X axis defaults to Y=0 if that is inside the plot range, else whichever + * of the top or bottom that has the smallest absolute value (that is, the value closest to 0). + * The Y axis defaults to the left edge. For horizontal plots, the axis roles and defaults are switched. + */ + protected function CalcAxisPositions() + { + // Validate user-provided X axis position, or calculate a default if not provided: + if ($this->x_axis_position !== '') { + // Force user-provided X axis position to be within the plot range: + $this->x_axis_position = min(max($this->plot_min_y, $this->x_axis_position), $this->plot_max_y); + } elseif ($this->yscale_type == 'log') { + // Always use 1 for X axis position on log scale plots. + $this->x_axis_position = 1; + } elseif ($this->datatype_swapped_xy || $this->plot_min_y > 0) { + // Horizontal plot, or Vertical Plot with all Y > 0: Place X axis on the bottom. + $this->x_axis_position = $this->plot_min_y; + } elseif ($this->plot_max_y < 0) { + // Vertical plot with all Y < 0, so place the X axis at the top. + $this->x_axis_position = $this->plot_max_y; + } else { + // Vertical plot range includes Y=0, so place X axis at 0. + $this->x_axis_position = 0; + } + + // Validate user-provided Y axis position, or calculate a default if not provided: + if ($this->y_axis_position !== '') { + // Force user-provided Y axis position to be within the plot range: + $this->y_axis_position = min(max($this->plot_min_x, $this->y_axis_position), $this->plot_max_x); + } elseif ($this->xscale_type == 'log') { + // Always use 1 for Y axis position on log scale plots. + $this->y_axis_position = 1; + } elseif (!$this->datatype_swapped_xy || $this->plot_min_x > 0) { + // Vertical plot, or Horizontal Plot with all X > 0: Place Y axis on left side. + $this->y_axis_position = $this->plot_min_x; + } elseif ($this->plot_max_x < 0) { + // Horizontal plot with all X < 0, so place the Y axis on the right side. + $this->y_axis_position = $this->plot_max_x; + } else { + // Horizontal plot range includes X=0: place Y axis at 0. + $this->y_axis_position = 0; + } + + if ($this->GetCallback('debug_scale')) { + $this->DoCallback('debug_scale', __FUNCTION__, array( + 'x_axis_position' => $this->x_axis_position, + 'y_axis_position' => $this->y_axis_position)); + } + + return TRUE; + } + + /* + * Calculates scaling stuff... + */ + protected function CalcTranslation() + { + if ($this->plot_max_x - $this->plot_min_x == 0) { // Check for div by 0 + $this->xscale = 0; + } else { + if ($this->xscale_type == 'log') { + $this->xscale = $this->plot_area_width / + (log10($this->plot_max_x) - log10($this->plot_min_x)); + } else { + $this->xscale = $this->plot_area_width / ($this->plot_max_x - $this->plot_min_x); + } + } + + if ($this->plot_max_y - $this->plot_min_y == 0) { // Check for div by 0 + $this->yscale = 0; + } else { + if ($this->yscale_type == 'log') { + $this->yscale = $this->plot_area_height / + (log10($this->plot_max_y) - log10($this->plot_min_y)); + } else { + $this->yscale = $this->plot_area_height / ($this->plot_max_y - $this->plot_min_y); + } + } + // GD defines x = 0 at left and y = 0 at TOP so -/+ respectively + if ($this->xscale_type == 'log') { + $this->plot_origin_x = $this->plot_area[0] - ($this->xscale * log10($this->plot_min_x) ); + } else { + $this->plot_origin_x = $this->plot_area[0] - ($this->xscale * $this->plot_min_x); + } + if ($this->yscale_type == 'log') { + $this->plot_origin_y = $this->plot_area[3] + ($this->yscale * log10($this->plot_min_y)); + } else { + $this->plot_origin_y = $this->plot_area[3] + ($this->yscale * $this->plot_min_y); + } + + // Convert axis positions to device coordinates: + $this->y_axis_x_pixels = $this->xtr($this->y_axis_position); + $this->x_axis_y_pixels = $this->ytr($this->x_axis_position); + + if ($this->GetCallback('debug_scale')) { + $this->DoCallback('debug_scale', __FUNCTION__, array( + 'xscale' => $this->xscale, 'yscale' => $this->yscale, + 'plot_origin_x' => $this->plot_origin_x, 'plot_origin_y' => $this->plot_origin_y, + 'y_axis_x_pixels' => $this->y_axis_x_pixels, + 'x_axis_y_pixels' => $this->x_axis_y_pixels)); + } + + return TRUE; + } + + /* + * Translate X world coordinate into pixel coordinate + * See CalcTranslation() for calculation of xscale. + * Note: This function should be 'protected', but is left public for historical reasons. + * See GetDeviceXY() for a preferred public method. + */ + function xtr($x_world) + { + if ($this->xscale_type == 'log') { + $x_pixels = $this->plot_origin_x + log10($x_world) * $this->xscale ; + } else { + $x_pixels = $this->plot_origin_x + $x_world * $this->xscale ; + } + return round($x_pixels); + } + + /* + * Translate Y world coordinate into pixel coordinate. + * See CalcTranslation() for calculation of yscale. + * Note: This function should be 'protected', but is left public for historical reasons. + * See GetDeviceXY() for a preferred public method. + */ + function ytr($y_world) + { + if ($this->yscale_type == 'log') { + //minus because GD defines y = 0 at top. doh! + $y_pixels = $this->plot_origin_y - log10($y_world) * $this->yscale ; + } else { + $y_pixels = $this->plot_origin_y - $y_world * $this->yscale ; + } + return round($y_pixels); + } + + /* A public interface to xtr and ytr. Translates (x,y) in world coordinates + * to (x,y) in device coordinates and returns them as an array. + * Usage is: list($x_pixel, $y_pixel) = $plot->GetDeviceXY($x_world, $y_world) + */ + function GetDeviceXY($x_world, $y_world) + { + if (!isset($this->xscale)) { + return $this->PrintError("GetDeviceXY() was called before translation factors were calculated"); + } + return array($this->xtr($x_world), $this->ytr($y_world)); + } + + /* + * Calculate tick parameters: Start, end, and delta values. This is used + * by both DrawXTicks() and DrawYTicks(). + * This currently uses the same simplistic method previously used by + * PHPlot (basically just range/10), but splitting this out into its + * own function is the first step in replacing the method. + * This is also used by CalcMaxTickSize() for CalcMargins(). + * + * $which : 'x' or 'y' : Which tick parameters to calculate + * + * Returns an array of 3 elements: tick_start, tick_end, tick_step + */ + protected function CalcTicks($which) + { + if ($which == 'x') { + $num_ticks = $this->num_x_ticks; + $tick_inc = $this->x_tick_inc; + $data_max = $this->plot_max_x; + $data_min = $this->plot_min_x; + $skip_lo = $this->skip_left_tick; + $skip_hi = $this->skip_right_tick; + $anchor = &$this->x_tick_anchor; // Use reference because this might not be set + } elseif ($which == 'y') { + $num_ticks = $this->num_y_ticks; + $tick_inc = $this->y_tick_inc; + $data_max = $this->plot_max_y; + $data_min = $this->plot_min_y; + $skip_lo = $this->skip_bottom_tick; + $skip_hi = $this->skip_top_tick; + $anchor = &$this->y_tick_anchor; // Use reference because this might not be set + } else { + return $this->PrintError("CalcTicks: Invalid usage ($which)"); + } + + if (!empty($tick_inc)) { + $tick_step = $tick_inc; + } elseif (!empty($num_ticks)) { + $tick_step = ($data_max - $data_min) / $num_ticks; + } else { + $tick_step = ($data_max - $data_min) / 10; + } + + // NOTE: When working with floats, because of approximations when adding $tick_step, + // the value may not quite reach the end, or may exceed it very slightly. + // So apply a "fudge" factor. + $tick_start = (double)$data_min; + $tick_end = (double)$data_max + ($data_max - $data_min) / 10000.0; + + // If a tick anchor was given, adjust the start of the range so the anchor falls + // at an exact tick mark (or would, if it was within range). + if (isset($anchor)) { + $tick_start = $anchor - $tick_step * floor(($anchor - $tick_start) / $tick_step); + } + + // Lastly, adjust for option to skip left/bottom or right/top tick marks: + if ($skip_lo) + $tick_start += $tick_step; + if ($skip_hi) + $tick_end -= $tick_step; + + return array($tick_start, $tick_end, $tick_step); + } + + /* + * Calculate the size of the biggest tick label. This is used by CalcMargins(). + * For 'x' ticks, it returns the height . For 'y' ticks, it returns the width. + * This means height along Y, or width along X - not relative to the text angle. + * That is what we need to calculate the needed margin space. + * (Previous versions of PHPlot estimated this, using the maximum X or Y value, + * or maybe the longest string. That doesn't work. -10 is longer than 9, etc. + * So this gets the actual size of each label, slow as that may be. + */ + protected function CalcMaxTickLabelSize($which) + { + list($tick_start, $tick_end, $tick_step) = $this->CalcTicks($which); + + if ($which == 'x') { + $font = $this->fonts['x_label']; + $angle = $this->x_label_angle; + } elseif ($which == 'y') { + $font = $this->fonts['y_label']; + $angle = $this->y_label_angle; + } else { + return $this->PrintError("CalcMaxTickLabelSize: Invalid usage ($which)"); + } + + $max_width = 0; + $max_height = 0; + + // Loop over ticks, same as DrawXTicks and DrawYTicks: + // Avoid cumulative round-off errors from $val += $delta + $n = 0; + $tick_val = $tick_start; + while ($tick_val <= $tick_end) { + $tick_label = $this->FormatLabel($which, $tick_val); + list($width, $height) = $this->SizeText($font, $angle, $tick_label); + if ($width > $max_width) $max_width = $width; + if ($height > $max_height) $max_height = $height; + $tick_val = $tick_start + ++$n * $tick_step; + } + if ($this->GetCallback('debug_scale')) { + $this->DoCallback('debug_scale', __FUNCTION__, array( + 'which' => $which, 'height' => $max_height, 'width' => $max_width)); + } + + if ($which == 'x') + return $max_height; + return $max_width; + } + + /* + * Calculate the size of the biggest data label. This is used by CalcMargins(). + * For $which='x', it returns the height of labels along the top or bottom. + * For $which='y', it returns the width of labels along the left or right sides. + * There is only one set of data labels (the first position in each data record). + * They normally go along the top or bottom (or both). If the data type indicates + * X/Y swapping (which is used for horizontal bar charts), the data labels go + * along the sides instead. So CalcMaxDataLabelSize('x') returns 0 if the + * data is X/Y swapped, and CalcMaxDataLabelSize('y') returns 0 if the data is + * is not X/Y swapped. + */ + protected function CalcMaxDataLabelSize($which = 'x') + { + if ($which == 'x') { + if ($this->datatype_swapped_xy) + return 0; // Shortcut: labels aren't on top/bottom. + $font = $this->fonts['x_label']; + $angle = $this->x_data_label_angle; + $format_code = 'xd'; + } elseif ($which == 'y') { + if (!$this->datatype_swapped_xy) + return 0; // Shortcut: labels aren't on left/right. + $font = $this->fonts['y_label']; + $angle = $this->y_data_label_angle; + $format_code = 'yd'; + } else { + return $this->PrintError("CalcMaxDataLabelSize: Invalid usage ($which)"); + } + $max_width = 0; + $max_height = 0; + + // Loop over all data labels and find the biggest: + for ($i = 0; $i < $this->num_data_rows; $i++) { + $label = $this->FormatLabel($format_code, $this->data[$i][0]); + list($width, $height) = $this->SizeText($font, $angle, $label); + if ($width > $max_width) $max_width = $width; + if ($height > $max_height) $max_height = $height; + } + if ($this->GetCallback('debug_scale')) { + $this->DoCallback('debug_scale', __FUNCTION__, array( + 'height' => $max_height, 'width' => $max_width)); + } + + if ($this->datatype_swapped_xy) + return $max_width; + return $max_height; + } + + /* + * Set grid control defaults. + * X grid defaults off, Y grid defaults on, except the reverse is true + * with swapped graphs such as horizontal bars. + */ + protected function CalcGridSettings() + { + if (!isset($this->draw_x_grid)) + $this->draw_x_grid = $this->datatype_swapped_xy; + if (!isset($this->draw_y_grid)) + $this->draw_y_grid = !$this->datatype_swapped_xy; + } + + /* + * Helper for CheckLabels() - determine if there are any non-empty labels. + * Returns True if all data labels are empty, else False. + */ + protected function CheckLabelsAllEmpty() + { + for ($i = 0; $i < $this->num_data_rows; $i++) + if ($this->data[$i][0] !== '') return FALSE; + return TRUE; + } + + /* + * Check and set label parameters. This handles deferred processing for label + * positioning and other label-related parameters. + * Copy label_format from 'x' to 'xd', and 'y' to 'yd', if not already set. + * Set x_data_label_angle from x_label_angle, if not already set. + * Apply defaults to X and Y tick and data label positions. + * Note: the label strings in the data array are used as X data labels in + * the normal case, but as Y data labels in the swapped X/Y case. + */ + protected function CheckLabels() + { + // The X and Y data labels are formatted the same as X and Y tick labels, + // unless overridden. Check and apply defaults for FormatLabel here: + if (empty($this->label_format['xd']) && !empty($this->label_format['x'])) + $this->label_format['xd'] = $this->label_format['x']; + if (empty($this->label_format['yd']) && !empty($this->label_format['y'])) + $this->label_format['yd'] = $this->label_format['y']; + + // The X tick label angle setting controls X data label angles too, + // unless overridden. Check and apply the default here: + if (!isset($this->x_data_label_angle)) + $this->x_data_label_angle = $this->x_label_angle; + // Note: Y data label angle defaults to zero, unlike X, + // for compatibility with older releases. + + // X Label position fixups, for x_data_label_pos and x_tick_label_pos: + if ($this->datatype_swapped_xy) { + // Just apply defaults - there is no position conflict for X labels. + if (!isset($this->x_tick_label_pos)) + $this->x_tick_label_pos = 'plotdown'; + if (!isset($this->x_data_label_pos)) + $this->x_data_label_pos = 'none'; + } else { + // Apply defaults but do not allow conflict between tick and data labels. + if (isset($this->x_data_label_pos)) { + if (!isset($this->x_tick_label_pos)) { + // Case: data_label_pos is set, tick_label_pos needs a default: + if ($this->x_data_label_pos == 'none') + $this->x_tick_label_pos = 'plotdown'; + else + $this->x_tick_label_pos = 'none'; + } + } elseif (isset($this->x_tick_label_pos)) { + // Case: tick_label_pos is set, data_label_pos needs a default: + if ($this->x_tick_label_pos == 'none') + $this->x_data_label_pos = 'plotdown'; + else + $this->x_data_label_pos = 'none'; + } else { + // Case: Neither tick_label_pos nor data_label_pos is set. + // We do not want them to be both on (as PHPlot used to do in this case). + // Turn on data labels if any were supplied, else tick labels. + if ($this->CheckLabelsAllEmpty()) { + $this->x_data_label_pos = 'none'; + $this->x_tick_label_pos = 'plotdown'; + } else { + $this->x_data_label_pos = 'plotdown'; + $this->x_tick_label_pos = 'none'; + } + } + } + + // Y Label position fixups, for y_data_label_pos and y_tick_label_pos: + if (!$this->datatype_swapped_xy) { + // Just apply defaults - there is no position conflict. + if (!isset($this->y_tick_label_pos)) + $this->y_tick_label_pos = 'plotleft'; + if (!isset($this->y_data_label_pos)) + $this->y_data_label_pos = 'none'; + } else { + // Apply defaults but do not allow conflict between tick and data labels. + if (isset($this->y_data_label_pos)) { + if (!isset($this->y_tick_label_pos)) { + // Case: data_label_pos is set, tick_label_pos needs a default: + if ($this->y_data_label_pos == 'none') + $this->y_tick_label_pos = 'plotleft'; + else + $this->y_tick_label_pos = 'none'; + } + } elseif (isset($this->y_tick_label_pos)) { + // Case: tick_label_pos is set, data_label_pos needs a default: + if ($this->y_tick_label_pos == 'none') + $this->y_data_label_pos = 'plotleft'; + else + $this->y_data_label_pos = 'none'; + } else { + // Case: Neither tick_label_pos nor data_label_pos is set. + // Turn on data labels if any were supplied, else tick labels. + if ($this->CheckLabelsAllEmpty()) { + $this->y_data_label_pos = 'none'; + $this->y_tick_label_pos = 'plotleft'; + } else { + $this->y_data_label_pos = 'plotleft'; + $this->y_tick_label_pos = 'none'; + } + } + } + return TRUE; + } + + /* + * Formats a tick or data label. + * which_pos - 'x', 'xd', 'y', or 'yd', selects formatting controls. + * x, y are for tick labels; xd, yd are for data labels. + * which_lab - String to format as a label. + * Credits: Time formatting suggested by Marlin Viss + * Custom formatting suggested by zer0x333 + * Notes: + * Type 'title' is obsolete and retained for compatibility. + * Class variable 'data_units_text' is retained as a suffix for 'data' type formatting for + * backward compatibility. Since there was never a function/method to set it, there + * could be somebody out there who sets it directly in the object. + */ + protected function FormatLabel($which_pos, $which_lab) + { + // Assign a reference shortcut to the label format controls. + // Note CheckLabels() made sure the 'xd' and 'yd' arrays are set. + $format =& $this->label_format[$which_pos]; + + // Don't format empty strings (especially as time or numbers), or if no type was set. + if ($which_lab !== '' && !empty($format['type'])) { + switch ($format['type']) { + case 'title': // Note: This is obsolete + $which_lab = @ $this->data[$which_lab][0]; + break; + case 'data': + $which_lab = $format['prefix'] + . $this->number_format($which_lab, $format['precision']) + . $this->data_units_text // Obsolete + . $format['suffix']; + break; + case 'time': + $which_lab = strftime($format['time_format'], $which_lab); + break; + case 'printf': + $which_lab = sprintf($format['printf_format'], $which_lab); + break; + case 'custom': + $which_lab = call_user_func($format['custom_callback'], $which_lab, $format['custom_arg']); + break; + + } + } + return $which_lab; + } + +///////////////////////////////////////////// +/////////////// TICKS +///////////////////////////////////////////// + + /* + * Set the step (interval) between X ticks. + * Use either this or SetNumXTicks(), not both, to control the X tick marks. + */ + function SetXTickIncrement($which_ti='') + { + $this->x_tick_inc = $which_ti; + if (!empty($which_ti)) { + $this->num_x_ticks = ''; + } + return TRUE; + } + + /* + * Set the step (interval) between Y ticks. + * Use either this or SetNumYTicks(), not both, to control the Y tick marks. + */ + function SetYTickIncrement($which_ti='') + { + $this->y_tick_inc = $which_ti; + if (!empty($which_ti)) { + $this->num_y_ticks = ''; + } + return TRUE; + } + + /* + * Set the number of X tick marks. + * Use either this or SetXTickIncrement(), not both, to control the X tick marks. + */ + function SetNumXTicks($which_nt='') + { + $this->num_x_ticks = $which_nt; + if (!empty($which_nt)) { + $this->x_tick_inc = ''; + } + return TRUE; + } + + /* + * Set the number of Y tick marks. + * Use either this or SetYTickIncrement(), not both, to control the Y tick marks. + */ + function SetNumYTicks($which_nt='') + { + $this->num_y_ticks = $which_nt; + if (!empty($which_nt)) { + $this->y_tick_inc = ''; //either use num_y_ticks or y_tick_inc, not both + } + return TRUE; + } + + /* + * Set the position for the X tick marks. + * These can be above the plot, below, both positions, at the X axis, or suppressed. + */ + function SetXTickPos($which_tp) + { + $this->x_tick_pos = $this->CheckOption($which_tp, 'plotdown, plotup, both, xaxis, none', + __FUNCTION__); + return (boolean)$this->x_tick_pos; + } + + /* + * Set the position for the Y tick marks. + * These can be left of the plot, right, both positions, at the Y axis, or suppressed. + */ + function SetYTickPos($which_tp) + { + $this->y_tick_pos = $this->CheckOption($which_tp, 'plotleft, plotright, both, yaxis, none', + __FUNCTION__); + return (boolean)$this->y_tick_pos; + } + + /* + * Skip the top-most Y axis tick mark and label if $skip is true. + */ + function SetSkipTopTick($skip) + { + $this->skip_top_tick = (bool)$skip; + return TRUE; + } + + /* + * Skip the bottom-most Y axis tick mark and label if $skip is true. + */ + function SetSkipBottomTick($skip) + { + $this->skip_bottom_tick = (bool)$skip; + return TRUE; + } + + /* + * Skip the left-most X axis tick mark and label if $skip is true. + */ + function SetSkipLeftTick($skip) + { + $this->skip_left_tick = (bool)$skip; + return TRUE; + } + + /* + * Skip the right-most X axis tick mark and label if $skip is true. + */ + function SetSkipRightTick($skip) + { + $this->skip_right_tick = (bool)$skip; + return TRUE; + } + + /* + * Set the outer length of X tick marks to $which_xln pixels. + * This is the part of the tick mark that sticks out from the plot area. + */ + function SetXTickLength($which_xln) + { + $this->x_tick_length = $which_xln; + return TRUE; + } + + /* + * Set the outer length of Y tick marks to $which_yln pixels. + * This is the part of the tick mark that sticks out from the plot area. + */ + function SetYTickLength($which_yln) + { + $this->y_tick_length = $which_yln; + return TRUE; + } + + /* + * Set the crossing length of X tick marks to $which_xc pixels. + * This is the part of the tick mark that sticks into the plot area. + */ + function SetXTickCrossing($which_xc) + { + $this->x_tick_cross = $which_xc; + return TRUE; + } + + /* + * Set the crossing length of Y tick marks to $which_yc pixels. + * This is the part of the tick mark that sticks into the plot area. + */ + function SetYTickCrossing($which_yc) + { + $this->y_tick_cross = $which_yc; + return TRUE; + } + + /* + * Set an anchor point for X tick marks. There will be an X tick mark at + * this exact value (if the data range were extended to include it). + */ + function SetXTickAnchor($xta = NULL) + { + $this->x_tick_anchor = $xta; + return TRUE; + } + + /* + * Set an anchor point for Y tick marks. There will be a Y tick mark at + * this exact value (if the data range were extended to include it). + */ + function SetYTickAnchor($yta = NULL) + { + $this->y_tick_anchor = $yta; + return TRUE; + } + +///////////////////////////////////////////// +//////////////////// GENERIC DRAWING +///////////////////////////////////////////// + + /* + * Fill the image background, with a tiled image file or solid color. + */ + protected function DrawBackground() + { + // Don't draw this twice if drawing two plots on one image + if (empty($this->done['background'])) { + if (isset($this->bgimg)) { // If bgimg is defined, use it + $this->tile_img($this->bgimg, 0, 0, $this->image_width, $this->image_height, $this->bgmode); + } else { // Else use solid color + ImageFilledRectangle($this->img, 0, 0, $this->image_width, $this->image_height, + $this->ndx_bg_color); + } + $this->done['background'] = TRUE; + } + return TRUE; + } + + /* + * Fill the plot area background, with a tiled image file or solid color. + */ + protected function DrawPlotAreaBackground() + { + if (isset($this->plotbgimg)) { + $this->tile_img($this->plotbgimg, $this->plot_area[0], $this->plot_area[1], + $this->plot_area_width, $this->plot_area_height, $this->plotbgmode); + } elseif ($this->draw_plot_area_background) { + ImageFilledRectangle($this->img, $this->plot_area[0], $this->plot_area[1], + $this->plot_area[2], $this->plot_area[3], $this->ndx_plot_bg_color); + } + return TRUE; + } + + /* + * Tiles an image at some given coordinates. + * $file : Filename of the picture to be used as tile. + * $xorig : X device coordinate of where the tile is to begin. + * $yorig : Y device coordinate of where the tile is to begin. + * $width : Width of the area to be tiled. + * $height : Height of the area to be tiled. + * $mode : Tiling mode. One of 'centeredtile', 'tile', 'scale'. + */ + protected function tile_img($file, $xorig, $yorig, $width, $height, $mode) + { + $im = $this->GetImage($file, $tile_width, $tile_height); + if (!$im) + return FALSE; // GetImage already produced an error message. + + if ($mode == 'scale') { + imagecopyresampled($this->img, $im, $xorig, $yorig, 0, 0, $width, $height, + $tile_width, $tile_height); + return TRUE; + } + + if ($mode == 'centeredtile') { + $x0 = - floor($tile_width/2); // Make the tile look better + $y0 = - floor($tile_height/2); + } else { // Accept anything else as $mode == 'tile' + $x0 = 0; + $y0 = 0; + } + + // Draw the tile onto a temporary image first. + $tmp = imagecreate($width, $height); + if (! $tmp) + return $this->PrintError('tile_img(): Could not create image resource.'); + + for ($x = $x0; $x < $width; $x += $tile_width) + for ($y = $y0; $y < $height; $y += $tile_height) + imagecopy($tmp, $im, $x, $y, 0, 0, $tile_width, $tile_height); + + // Copy the temporary image onto the final one. + imagecopy($this->img, $tmp, $xorig, $yorig, 0,0, $width, $height); + + // Free resources + imagedestroy($tmp); + imagedestroy($im); + + return TRUE; + } + + /* + * Return the image border width. + * This is used by CalcMargins() and DrawImageBorder(). + */ + protected function GetImageBorderWidth() + { + if ($this->image_border_type == 'none') + return 0; // No border + if (!empty($this->image_border_width)) + return $this->image_border_width; // Specified border width + if ($this->image_border_type == 'raised') + return 2; // Default for raised border is 2 pixels. + return 1; // Default for other border types is 1 pixel. + } + + /* + * Draws a border around the final image. + * Note: 'plain' draws a flat border using the dark shade of the border color. + * This probably should have been written to use the actual border color, but + * it is too late to fix it without changing plot appearances. Therefore a + * new type 'solid' was added to use the SetImageBorderColor color. + */ + protected function DrawImageBorder() + { + // Do nothing if already drawn, or if no border has been set. + if ($this->image_border_type == 'none' || !empty($this->done['border'])) + return TRUE; + $width = $this->GetImageBorderWidth(); + $color1 = $this->ndx_i_border; + $color2 = $this->ndx_i_border_dark; + $ex = $this->image_width - 1; + $ey = $this->image_height - 1; + switch ($this->image_border_type) { + case 'raised': + // Top and left lines use border color, right and bottom use the darker shade. + // Drawing order matters in the upper right and lower left corners. + for ($i = 0; $i < $width; $i++, $ex--, $ey--) { + imageline($this->img, $i, $i, $ex, $i, $color1); // Top + imageline($this->img, $ex, $i, $ex, $ey, $color2); // Right + imageline($this->img, $i, $i, $i, $ey, $color1); // Left + imageline($this->img, $i, $ey, $ex, $ey, $color2); // Bottom + } + break; + case 'plain': // See note above re colors + $color1 = $color2; + // Fall through + case 'solid': + for ($i = 0; $i < $width; $i++, $ex--, $ey--) { + imagerectangle($this->img, $i, $i, $ex, $ey, $color1); + } + break; + default: + return $this->PrintError( + "DrawImageBorder(): unknown image_border_type: '$this->image_border_type'"); + } + $this->done['border'] = TRUE; // Border should only be drawn once per image. + return TRUE; + } + + /* + * Draws the main title on the graph. + * The title must not be drawn more than once (in the case of multiple plots + * on the image), because TTF text antialiasing makes it look bad. + */ + protected function DrawTitle() + { + if (!empty($this->done['title']) || $this->title_txt === '') + return TRUE; + + // Center of the image: + $xpos = $this->image_width / 2; + + // Place it at almost at the top + $ypos = $this->title_offset; + + $this->DrawText($this->fonts['title'], 0, $xpos, $ypos, + $this->ndx_title_color, $this->title_txt, 'center', 'top'); + + $this->done['title'] = TRUE; + return TRUE; + } + + /* + * Draws the X-Axis Title + */ + protected function DrawXTitle() + { + if ($this->x_title_pos == 'none') + return TRUE; + + // Center of the plot + $xpos = ($this->plot_area[2] + $this->plot_area[0]) / 2; + + // Upper title + if ($this->x_title_pos == 'plotup' || $this->x_title_pos == 'both') { + $ypos = $this->plot_area[1] - $this->x_title_top_offset; + $this->DrawText($this->fonts['x_title'], 0, $xpos, $ypos, $this->ndx_x_title_color, + $this->x_title_txt, 'center', 'bottom'); + } + // Lower title + if ($this->x_title_pos == 'plotdown' || $this->x_title_pos == 'both') { + $ypos = $this->plot_area[3] + $this->x_title_bot_offset; + $this->DrawText($this->fonts['x_title'], 0, $xpos, $ypos, $this->ndx_x_title_color, + $this->x_title_txt, 'center', 'top'); + } + return TRUE; + } + + /* + * Draws the Y-Axis Title + */ + protected function DrawYTitle() + { + if ($this->y_title_pos == 'none') + return TRUE; + + // Center the title vertically to the plot area + $ypos = ($this->plot_area[3] + $this->plot_area[1]) / 2; + + if ($this->y_title_pos == 'plotleft' || $this->y_title_pos == 'both') { + $xpos = $this->plot_area[0] - $this->y_title_left_offset; + $this->DrawText($this->fonts['y_title'], 90, $xpos, $ypos, $this->ndx_y_title_color, + $this->y_title_txt, 'right', 'center'); + } + if ($this->y_title_pos == 'plotright' || $this->y_title_pos == 'both') { + $xpos = $this->plot_area[2] + $this->y_title_right_offset; + $this->DrawText($this->fonts['y_title'], 90, $xpos, $ypos, $this->ndx_y_title_color, + $this->y_title_txt, 'left', 'center'); + } + + return TRUE; + } + + /* + * Draw the X axis, including ticks and labels, and X (vertical) grid lines. + */ + protected function DrawXAxis() + { + // Draw ticks, labels and grid + $this->DrawXTicks(); + + //Draw X Axis at Y = x_axis_y_pixels, unless suppressed (See SetXAxisPosition) + if (empty($this->suppress_x_axis)) { + ImageLine($this->img, $this->plot_area[0]+1, $this->x_axis_y_pixels, + $this->plot_area[2]-1, $this->x_axis_y_pixels, $this->ndx_grid_color); + } + return TRUE; + } + + /* + * Draw the Y axis, including ticks and labels, and Y (horizontal) grid lines. + * Horizontal grid lines overwrite horizontal axis with y=0, so call this first, then DrawXAxis() + */ + protected function DrawYAxis() + { + // Draw ticks, labels and grid + $this->DrawYTicks(); + + // Draw Y axis at X = y_axis_x_pixels, unless suppressed (See SetYAxisPosition) + if (empty($this->suppress_y_axis)) { + ImageLine($this->img, $this->y_axis_x_pixels, $this->plot_area[1], + $this->y_axis_x_pixels, $this->plot_area[3], $this->ndx_grid_color); + } + return TRUE; + } + + /* + * Draw one X tick mark and its tick label. + * $which_xlab : Formatted X value for the label. + * $which_xpix : X device coordinate for this tick mark. + */ + protected function DrawXTick($which_xlab, $which_xpix) + { + // Ticks on X axis + if ($this->x_tick_pos == 'xaxis') { + ImageLine($this->img, $which_xpix, $this->x_axis_y_pixels - $this->x_tick_cross, + $which_xpix, $this->x_axis_y_pixels + $this->x_tick_length, $this->ndx_tick_color); + } + + // Ticks on top of the Plot Area + if ($this->x_tick_pos == 'plotup' || $this->x_tick_pos == 'both') { + ImageLine($this->img, $which_xpix, $this->plot_area[1] - $this->x_tick_length, + $which_xpix, $this->plot_area[1] + $this->x_tick_cross, $this->ndx_tick_color); + } + + // Ticks on bottom of Plot Area + if ($this->x_tick_pos == 'plotdown' || $this->x_tick_pos == 'both') { + ImageLine($this->img, $which_xpix, $this->plot_area[3] + $this->x_tick_length, + $which_xpix, $this->plot_area[3] - $this->x_tick_cross, $this->ndx_tick_color); + } + + // Label on X axis + if ($this->x_tick_label_pos == 'xaxis') { + $this->DrawText($this->fonts['x_label'], $this->x_label_angle, + $which_xpix, $this->x_axis_y_pixels + $this->x_label_axis_offset, + $this->ndx_text_color, $which_xlab, 'center', 'top'); + } + + // Label on top of the Plot Area + if ($this->x_tick_label_pos == 'plotup' || $this->x_tick_label_pos == 'both') { + $this->DrawText($this->fonts['x_label'], $this->x_label_angle, + $which_xpix, $this->plot_area[1] - $this->x_label_top_offset, + $this->ndx_text_color, $which_xlab, 'center', 'bottom'); + } + + // Label on bottom of the Plot Area + if ($this->x_tick_label_pos == 'plotdown' || $this->x_tick_label_pos == 'both') { + $this->DrawText($this->fonts['x_label'], $this->x_label_angle, + $which_xpix, $this->plot_area[3] + $this->x_label_bot_offset, + $this->ndx_text_color, $which_xlab, 'center', 'top'); + } + return TRUE; + } + + /* + * Draw one Y tick mark and its tick label. Called from DrawYTicks() and DrawXAxis() + * $which_ylab : Formatted Y value for the label. + * $which_ypix : Y device coordinate for this tick mark. + */ + protected function DrawYTick($which_ylab, $which_ypix) + { + // Ticks on Y axis + if ($this->y_tick_pos == 'yaxis') { + ImageLine($this->img, $this->y_axis_x_pixels - $this->y_tick_length, $which_ypix, + $this->y_axis_x_pixels + $this->y_tick_cross, $which_ypix, $this->ndx_tick_color); + } + + // Ticks to the left of the Plot Area + if (($this->y_tick_pos == 'plotleft') || ($this->y_tick_pos == 'both') ) { + ImageLine($this->img, $this->plot_area[0] - $this->y_tick_length, $which_ypix, + $this->plot_area[0] + $this->y_tick_cross, $which_ypix, $this->ndx_tick_color); + } + + // Ticks to the right of the Plot Area + if (($this->y_tick_pos == 'plotright') || ($this->y_tick_pos == 'both') ) { + ImageLine($this->img, $this->plot_area[2] + $this->y_tick_length, $which_ypix, + $this->plot_area[2] - $this->y_tick_cross, $which_ypix, $this->ndx_tick_color); + } + + // Labels on Y axis + if ($this->y_tick_label_pos == 'yaxis') { + $this->DrawText($this->fonts['y_label'], $this->y_label_angle, + $this->y_axis_x_pixels - $this->y_label_axis_offset, $which_ypix, + $this->ndx_text_color, $which_ylab, 'right', 'center'); + } + + // Labels to the left of the plot area + if ($this->y_tick_label_pos == 'plotleft' || $this->y_tick_label_pos == 'both') { + $this->DrawText($this->fonts['y_label'], $this->y_label_angle, + $this->plot_area[0] - $this->y_label_left_offset, $which_ypix, + $this->ndx_text_color, $which_ylab, 'right', 'center'); + } + // Labels to the right of the plot area + if ($this->y_tick_label_pos == 'plotright' || $this->y_tick_label_pos == 'both') { + $this->DrawText($this->fonts['y_label'], $this->y_label_angle, + $this->plot_area[2] + $this->y_label_right_offset, $which_ypix, + $this->ndx_text_color, $which_ylab, 'left', 'center'); + } + return TRUE; + } + + /* + * Draws Grid, Ticks and Tick Labels along X-Axis + * Ticks and tick labels can be down of plot only, up of plot only, + * both on up and down of plot, or crossing a user defined X-axis + * + * Original vertical code submitted by Marlin Viss + */ + protected function DrawXTicks() + { + // Sets the line style for IMG_COLOR_STYLED lines (grid) + if ($this->dashed_grid) { + $this->SetDashedStyle($this->ndx_light_grid_color); + $style = IMG_COLOR_STYLED; + } else { + $style = $this->ndx_light_grid_color; + } + + // Calculate the tick start, end, and step: + list($x_start, $x_end, $delta_x) = $this->CalcTicks('x'); + + // Loop, avoiding cumulative round-off errors from $x_tmp += $delta_x + $n = 0; + $x_tmp = $x_start; + while ($x_tmp <= $x_end) { + $xlab = $this->FormatLabel('x', $x_tmp); + $x_pixels = $this->xtr($x_tmp); + + // Vertical grid lines + if ($this->draw_x_grid) { + ImageLine($this->img, $x_pixels, $this->plot_area[1], $x_pixels, $this->plot_area[3], $style); + } + + // Draw tick mark(s) + $this->DrawXTick($xlab, $x_pixels); + + // Step to next X, without accumulating error + $x_tmp = $x_start + ++$n * $delta_x; + } + return TRUE; + } + + /* + * Draw the grid, ticks, and tick labels along the Y axis. + * Ticks and tick labels can be left of plot only, right of plot only, + * both on the left and right of plot, or crossing a user defined Y-axis + */ + protected function DrawYTicks() + { + // Sets the line style for IMG_COLOR_STYLED lines (grid) + if ($this->dashed_grid) { + $this->SetDashedStyle($this->ndx_light_grid_color); + $style = IMG_COLOR_STYLED; + } else { + $style = $this->ndx_light_grid_color; + } + + // Calculate the tick start, end, and step: + list($y_start, $y_end, $delta_y) = $this->CalcTicks('y'); + + // Loop, avoiding cumulative round-off errors from $y_tmp += $delta_y + $n = 0; + $y_tmp = $y_start; + while ($y_tmp <= $y_end) { + $ylab = $this->FormatLabel('y', $y_tmp); + $y_pixels = $this->ytr($y_tmp); + + // Horizontal grid line + if ($this->draw_y_grid) { + ImageLine($this->img, $this->plot_area[0]+1, $y_pixels, $this->plot_area[2]-1, + $y_pixels, $style); + } + + // Draw tick mark(s) + $this->DrawYTick($ylab, $y_pixels); + + // Step to next Y, without accumulating error + $y_tmp = $y_start + ++$n * $delta_y; + } + return TRUE; + } + + /* + * Draw a border around the plot area. See SetPlotBorderType. + * Note: SetPlotBorderType sets plot_border_type to an array, but + * it won't be an array if it defaults or is set directly (backward compatibility). + */ + protected function DrawPlotBorder() + { + $pbt = (array)$this->plot_border_type; + $sides = 0; // Bitmap: 1=left 2=top 4=right 8=bottom + $map = array('left' => 1, 'plotleft' => 1, 'right' => 4, 'plotright' => 4, 'top' => 2, + 'bottom' => 8, 'both' => 5, 'sides' => 5, 'full' => 15, 'none' => 0); + foreach ($pbt as $option) $sides |= $map[$option]; + if ($sides == 15) { // Border on all 4 sides + imagerectangle($this->img, $this->plot_area[0], $this->plot_area[1], + $this->plot_area[2], $this->plot_area[3], $this->ndx_grid_color); + } else { + if ($sides & 1) // Left + imageline($this->img, $this->plot_area[0], $this->plot_area[1], + $this->plot_area[0], $this->plot_area[3], $this->ndx_grid_color); + if ($sides & 2) // Top + imageline($this->img, $this->plot_area[0], $this->plot_area[1], + $this->plot_area[2], $this->plot_area[1], $this->ndx_grid_color); + if ($sides & 4) // Right + imageline($this->img, $this->plot_area[2], $this->plot_area[1], + $this->plot_area[2], $this->plot_area[3], $this->ndx_grid_color); + if ($sides & 8) // Bottom + imageline($this->img, $this->plot_area[0], $this->plot_area[3], + $this->plot_area[2], $this->plot_area[3], $this->ndx_grid_color); + } + return TRUE; + } + + /* + * Draw the data value label associated with a point in the plot. + * These are labels that show the value (dependent variable, usually Y) of the data point, + * and are drawn within the plot area (not to be confused with axis data labels). + * + * $x_or_y : Specify 'x' or 'y' labels. This selects font, angle, and formatting. + * $x_world, $y_world : World coordinates of the text (see also x/y_adjustment). + * $text : The text to draw, after formatting with FormatLabel(). + * $halign, $valign : Selects from 9-point text alignment. + * $x_adjustment, $y_adjustment : Text position offsets, in device coordinates. + * $min_width, $min_height : If supplied, suppress the text if it will not fit. + * Returns True, if the text was drawn, or False, if it will not fit. + */ + protected function DrawDataValueLabel($x_or_y, $x_world, $y_world, $text, $halign, $valign, + $x_adjustment=0, $y_adjustment=0, $min_width=NULL, $min_height=NULL) + { + if ($x_or_y == 'x') { + $angle = $this->x_data_label_angle; + $font = $this->fonts['x_label']; + $formatted_text = $this->FormatLabel('xd', $text); + } else { // Assumed 'y' + $angle = $this->y_data_label_angle; + $font = $this->fonts['y_label']; + $formatted_text = $this->FormatLabel('yd', $text); + } + $color = $this->ndx_title_color; // Currently this is the same for X and Y labels + + // Check to see if the text fits in the available space, if requested. + if (isset($min_width) || isset($min_height)) { + list($width, $height) = $this->SizeText($font, $angle, $formatted_text); + if ((isset($min_width) && ($min_width - $width) < 2) + || (isset($min_height) && ($min_height - $height) < 2)) + return FALSE; + } + + $this->DrawText($font, $angle, $this->xtr($x_world) + $x_adjustment, + $this->ytr($y_world) + $y_adjustment, + $color, $formatted_text, $halign, $valign); + return TRUE; + } + + /* + * Draws the axis data label associated with a point in the plot. + * This is different from x_labels drawn by DrawXTicks() and care + * should be taken not to draw both, as they'd probably overlap. + * Calling of this function in DrawLines(), etc is decided after x_data_label_pos value. + * Leave the last parameter out, to avoid the drawing of vertical lines, no matter + * what the setting is (for plots that need it, like DrawSquared()) + */ + protected function DrawXDataLabel($xlab, $xpos, $row=FALSE) + { + $xlab = $this->FormatLabel('xd', $xlab); + + // Labels below the plot area + if ($this->x_data_label_pos == 'plotdown' || $this->x_data_label_pos == 'both') + $this->DrawText($this->fonts['x_label'], $this->x_data_label_angle, + $xpos, $this->plot_area[3] + $this->x_label_bot_offset, + $this->ndx_text_color, $xlab, 'center', 'top'); + + // Labels above the plot area + if ($this->x_data_label_pos == 'plotup' || $this->x_data_label_pos == 'both') + $this->DrawText($this->fonts['x_label'], $this->x_data_label_angle, + $xpos, $this->plot_area[1] - $this->x_label_top_offset, + $this->ndx_text_color, $xlab, 'center', 'bottom'); + + // $row=0 means this is the first row. $row=FALSE means don't do any rows. + if ($row !== FALSE && $this->draw_x_data_label_lines) + $this->DrawXDataLine($xpos, $row); + return TRUE; + } + + /* + * Draw a data label along the Y axis or side. + * This is used by horizontal plots. + */ + protected function DrawYDataLabel($ylab, $ypos) + { + $ylab = $this->FormatLabel('yd', $ylab); + + // Labels left of the plot area + if ($this->y_data_label_pos == 'plotleft' || $this->y_data_label_pos == 'both') + $this->DrawText($this->fonts['y_label'], $this->y_data_label_angle, + $this->plot_area[0] - $this->y_label_left_offset, $ypos, + $this->ndx_text_color, $ylab, 'right', 'center'); + + // Labels right of the plot area + if ($this->y_data_label_pos == 'plotright' || $this->y_data_label_pos == 'both') + $this->DrawText($this->fonts['y_label'], $this->y_data_label_angle, + $this->plot_area[2] + $this->y_label_right_offset, $ypos, + $this->ndx_text_color, $ylab, 'left', 'center'); + return TRUE; + } + + /* + * Draws Vertical lines from data points up and down. + * Which lines are drawn depends on the value of x_data_label_pos, + * and whether this is at all done or not, on draw_x_data_label_lines + * + * $xpos : position in pixels of the line. + * $row : index of the data row being drawn. + */ + protected function DrawXDataLine($xpos, $row) + { + // Sets the line style for IMG_COLOR_STYLED lines (grid) + if ($this->dashed_grid) { + $this->SetDashedStyle($this->ndx_light_grid_color); + $style = IMG_COLOR_STYLED; + } else { + $style = $this->ndx_light_grid_color; + } + + if ($this->x_data_label_pos == 'both') { + // Lines from the bottom up + ImageLine($this->img, $xpos, $this->plot_area[3], $xpos, $this->plot_area[1], $style); + } elseif ($this->x_data_label_pos == 'plotdown' && isset($this->data_max[$row])) { + // Lines from the bottom of the plot up to the max Y value at this X: + $ypos = $this->ytr($this->data_max[$row]); + ImageLine($this->img, $xpos, $ypos, $xpos, $this->plot_area[3], $style); + } elseif ($this->x_data_label_pos == 'plotup' && isset($this->data_min[$row])) { + // Lines from the top of the plot down to the min Y value at this X: + $ypos = $this->ytr($this->data_min[$row]); + ImageLine($this->img, $xpos, $this->plot_area[1], $xpos, $ypos, $style); + } + return TRUE; + } + +///////////////////////////////////////////// +/////////////// LEGEND +///////////////////////////////////////////// + + /* + * Set text to display in the graph's legend. + * $which_leg : Array of strings for the complete legend, or a single string + * to be appended to the legend. + * Or NULL (or an empty array) to cancel the legend. + */ + function SetLegend($which_leg) + { + if (is_array($which_leg)) { // use array (or cancel, if empty array) + $this->legend = $which_leg; + } elseif (!is_null($which_leg)) { // append string + $this->legend[] = $which_leg; + } else { + $this->legend = ''; // Reinitialize to empty, meaning no legend. + } + return TRUE; + } + + /* + * Specifies the position of the legend's upper/leftmost corner, in pixel (device) coordinates. + * Both X and Y must be provided, or both omitted (or use NULL) to restore auto-positioning. + */ + function SetLegendPixels($which_x=NULL, $which_y=NULL) + { + return $this->SetLegendPosition(0, 0, 'image', 0, 0, $which_x, $which_y); + } + + /* + * Specifies the position of the legend's upper/leftmost corner, in world (data space) coordinates. + */ + function SetLegendWorld($which_x, $which_y) + { + return $this->SetLegendPosition(0, 0, 'world', $which_x, $which_y); + } + + /* + * Specifies the position of the legend. This includes SetLegendWorld(), SetLegendPixels(), and + * additional choices using relative coordinates, with optional pixel offset. + * $x, $y : Relative coordinates of a point on the legend box. (See below) + * $relative_to : What to position the legend relative to: 'image', 'plot', 'world', or 'title'. + * $x_base, $y_base : Base point for positioning. + * If $relative_to is 'world', then this is a world coordinate position. + * Otherwise, this is a relative coordinate position on the $relative_to element. + * $x_offset, $y_offset : Additional legend box offset in device coordinates (pixels). + * The legend is positioned so that point ($x,$y) is at ($x_base, $y_base). + * 'Relative coordinates' means: (0,0) is the upper left corner, and (1,1) is the lower right corner + * of the element (legend, image, plot, or title area), regardless of its size. These are floating + * point values, each usually in the range [0,1], but they can be negative or greater than 1. + * If any of x, y, x_offset, or y_offset are NULL, default legend positioning is restored. + */ + function SetLegendPosition($x, $y, $relative_to, $x_base, $y_base, $x_offset = 0, $y_offset = 0) + { + // Special case: NULL means restore the default positioning. + if (!isset($x, $y, $x_offset, $y_offset)) { + unset($this->legend_pos); + } else { + $mode = $this->CheckOption($relative_to, 'image, plot, title, world', __FUNCTION__); + if (empty($mode)) + return FALSE; + // Save all values for use by GetLegendPosition() + $this->legend_pos = compact('x', 'y', 'mode', 'x_base', 'y_base', 'x_offset', 'y_offset'); + } + return TRUE; + } + + /* + * Set legend text alignment, color box alignment, and style options. + * $text_align : Alignment of the text, 'left' or 'right'. + * $colorbox_align : Alignment of the color boxes, 'left', 'right', 'none', or missing/empty. + * If missing or empty, the same alignment as $text_align is used. Color box is positioned first. + */ + function SetLegendStyle($text_align, $colorbox_align = '') + { + $this->legend_text_align = $this->CheckOption($text_align, 'left, right', __FUNCTION__); + if (empty($colorbox_align)) + $this->legend_colorbox_align = $this->legend_text_align; + else + $this->legend_colorbox_align = $this->CheckOption($colorbox_align, 'left, right, none', + __FUNCTION__); + return ((boolean)$this->legend_text_align && (boolean)$this->legend_colorbox_align); + } + + /* + * Use color boxes or point shapes (for points and linepoints plots only) in the legend. + * $use_shapes : True to use point shapes, false to use color boxes. + */ + function SetLegendUseShapes($use_shapes) + { + $this->legend_use_shapes = (bool)$use_shapes; + return TRUE; + } + + /* + * Get legend sizing parameters. + * This is used internally by DrawLegend(), and also by the public GetLegendSize(). + * It returns information based on any SetLegend*() calls already made. It does not use + * legend position or data scaling, so it can be called before data scaling is set up. + * Returns an associative array with these entries describing legend sizing: + * 'width', 'height' : Overall legend box size in pixels. + * 'char_w', 'char_h' : Width and height of 'E' in legend text font. (Used to size color boxes) + * 'v_margin' : Inside margin for legend + * 'text_align', 'colorbox_align' : Same as the class variables, with default applied. + * 'draw_colorbox' : True if color boxes will be drawn. + * 'dot_height' : Height of color boxes (even if not drawn), for line spacing. + * 'colorbox_width' : Width of color boxes. + */ + protected function GetLegendSizeParams() + { + $font = &$this->fonts['legend']; // Shortcut to font info array + + // Find maximum legend label line width. + $max_width = 0; + foreach ($this->legend as $line) { + list($width, $unused) = $this->SizeText($font, 0, $line); + if ($width > $max_width) $max_width = $width; + } + + // Font parameters are used to size the color boxes: + $char_w = $font['width']; + $char_h = $font['height']; + $line_spacing = $this->GetLineSpacing($font); + + // Apply defaults to text alignment and colorbox alignment variables: + $text_align = isset($this->legend_text_align) ? $this->legend_text_align : 'right'; + $colorbox_align = isset($this->legend_colorbox_align) ? $this->legend_colorbox_align : 'right'; + $draw_colorbox = ($colorbox_align != 'none'); + + // Sizing parameters: + $v_margin = $char_h / 2; // Between vertical borders and labels + $dot_height = $char_h + $line_spacing; // Height of the color boxes (even if not drawn) + $colorbox_width = $char_w; // Base color box width + if (isset($this->legend_colorbox_width)) + $colorbox_width *= $this->legend_colorbox_width; // Adjustment to color box width + + // Calculate overall legend box width and height. + // Width is e.g.: "| space colorbox space text space |" where each space adds $char_w, + // and colorbox (if drawn) adds $char_w * its width adjustment. + if ($draw_colorbox) { + $width = $max_width + 3 * $char_w + $colorbox_width; + } else { + $width = $max_width + 2 * $char_w; + } + $height = $dot_height * count($this->legend) + 2 * $v_margin; + + return compact('width', 'height', 'char_w', 'char_h', 'v_margin', + 'text_align', 'colorbox_align', 'draw_colorbox', 'dot_height', 'colorbox_width'); + } + + /* + * Get legend box size. This can be used to adjust the plot margins, for example. + * Returns: Array of ($width, $height) of the legend box in pixels. + */ + function GetLegendSize() + { + $params = $this->GetLegendSizeParams(); + return array($params['width'], $params['height']); + } + + /* + * Get legend location in device coordinates. This is a helper for DrawLegend, and is only + * called if there is a legend. See SetLegendWorld(), SetLegendPixels(), SetLegendPosition(). + * $width, $height : Width and height of the legend box. + * Returns: coordinates of the upper left corner of the legend box as an array ($x, $y) + */ + protected function GetLegendPosition($width, $height) + { + // Extract variables set by SetLegend*(): $mode, $x, $y, $x_base, $y_base, $x_offset, $y_offset + if (isset($this->legend_pos['mode'])) + extract($this->legend_pos); + else + $mode = ''; // Default legend position mode. + + switch ($mode) { + + case 'plot': // SetLegendPosition with mode='plot', relative coordinates over plot area. + return array((int)($x_base * $this->plot_area_width - $x * $width) + + $this->plot_area[0] + $x_offset, + (int)($y_base * $this->plot_area_height - $y * $height) + + $this->plot_area[1] + $y_offset); + + case 'world': // User-defined position in world-coordinates (SetLegendWorld), using x_base, y_base + return array($this->xtr($x_base) + $x_offset - (int)($x * $width), + $this->ytr($y_base) + $y_offset - (int)($y * $height)); + + case 'image': // SetLegendPosition with mode='image', relative coordinates over image area. + // SetLegendPixels() uses this too, with x=y=0. + return array((int)($x_base * $this->image_width - $x * $width) + $x_offset, + (int)($y_base * $this->image_height - $y * $height) + $y_offset); + + case 'title': // SetLegendPosition with mode='title', relative to main title. + // Recalculate main title position/size, since CalcMargins does not save it. See DrawTitle() + list($title_width, $title_height) = $this->SizeText($this->fonts['title'], 0, $this->title_txt); + $title_x = (int)(($this->image_width - $title_width) / 2); + return array((int)($x_base * $title_width - $x * $width) + $title_x + $x_offset, + (int)($y_base * $title_height - $y * $height) + $this->title_offset + $y_offset); + + default: // If mode is unset (or invalid), use default position. + return array ($this->plot_area[2] - $width - $this->safe_margin, + $this->plot_area[1] + $this->safe_margin); + } + } + + /* + * Draws the graph legend + * This is called by DrawGraph only if $this->legend is not empty. + * Base code submitted by Marlin Viss + */ + protected function DrawLegend() + { + $font = &$this->fonts['legend']; // Shortcut to font info array + + // Calculate legend box sizing parameters: + // See GetLegendSizeParams() to see what variables are set by this. + extract($this->GetLegendSizeParams()); + + // Get legend box position: + list($box_start_x, $box_start_y) = $this->GetLegendPosition($width, $height); + $box_end_y = $box_start_y + $height; + $box_end_x = $box_start_x + $width; + + // Draw outer box + ImageFilledRectangle($this->img, $box_start_x, $box_start_y, $box_end_x, $box_end_y, + $this->ndx_bg_color); + ImageRectangle($this->img, $box_start_x, $box_start_y, $box_end_x, $box_end_y, + $this->ndx_grid_color); + + $color_index = 0; + $max_color_index = count($this->ndx_data_colors) - 1; + + // Calculate color box and text horizontal positions. + if (!$draw_colorbox) { + if ($text_align == 'left') + $x_pos = $box_start_x + $char_w; + else + $x_pos = $box_end_x - $char_w; + $dot_left_x = 0; // Not used directly if color boxes/shapes are off, but referenced below. + } elseif ($colorbox_align == 'left') { + $dot_left_x = $box_start_x + $char_w; + $dot_right_x = $dot_left_x + $colorbox_width; + if ($text_align == 'left') + $x_pos = $dot_right_x + $char_w; + else + $x_pos = $box_end_x - $char_w; + } else { // $colorbox_align == 'right' + $dot_right_x = $box_end_x - $char_w; + $dot_left_x = $dot_right_x - $colorbox_width; + if ($text_align == 'left') + $x_pos = $box_start_x + $char_w; + else + $x_pos = $dot_left_x - $char_w; + } + + // $y_pos is the bottom of each color box. $yc is the vertical center of the color box or + // the point shape (if drawn). The text is centered vertically on $yc. + $y_pos = $box_start_y + $v_margin + $dot_height; + $yc = (int)($y_pos - $dot_height / 2); + $xc = (int)($dot_left_x + $colorbox_width / 2); // Horizontal center for point shape if drawn + $shape_index = 0; // Shape number index, if drawing point shapes + + // Option to use point shapes rather than solid boxes. Disallow this if the shapes array + // has not been initialized (see CheckPointParams). Only works with 'points' or 'linepoints' plots. + $use_shapes = !empty($this->legend_use_shapes) && !empty($this->point_counts); + + foreach ($this->legend as $leg) { + // Draw text with requested alignment: + $this->DrawText($font, 0, $x_pos, $yc, $this->ndx_text_color, $leg, $text_align, 'center'); + if ($draw_colorbox) { + $y1 = $y_pos - $dot_height + 1; + $y2 = $y_pos - 1; + if ($use_shapes) { + // Draw a point shape in the data color + // If plot area background is on, use that as the shape background: + if ($this->draw_plot_area_background) { + ImageFilledRectangle($this->img, $dot_left_x, $y1, $dot_right_x, $y2, + $this->ndx_plot_bg_color); + } + // Draw the shape. DrawShape() takes shape_index modulo number of defined shapes. + $this->DrawShape($xc, $yc, $shape_index++, $this->ndx_data_colors[$color_index]); + } else { + // Draw color boxes: + ImageFilledRectangle($this->img, $dot_left_x, $y1, $dot_right_x, $y2, + $this->ndx_data_colors[$color_index]); + // Draw a rectangle around the box + ImageRectangle($this->img, $dot_left_x, $y1, $dot_right_x, $y2, $this->ndx_text_color); + } + } + $y_pos += $dot_height; + $yc += $dot_height; + if (++$color_index > $max_color_index) + $color_index = 0; + } + return TRUE; + } + +///////////////////////////////////////////// +//////////////////// PLOT DRAWING HELPERS +///////////////////////////////////////////// + + /* + * Get data color to use for plotting. + * $row, $idx : Index arguments for the current data point. + * &$vars : Variable storage. Caller makes an empty array, and this function uses it. + * &$data_color : Returned result - Color index for the data point. + * $extra : Extra info flag passed through to data color callback. + */ + protected function GetDataColor($row, $idx, &$vars, &$data_color, $extra = 0) + { + // Initialize or extract variables: + if (empty($vars)) { + $custom_color = (bool)$this->GetCallback('data_color'); + $num_data_colors = count($this->ndx_data_colors); + $vars = compact('custom_color', 'num_data_colors'); + } else { + extract($vars); + } + + // Select the colors. + if ($custom_color) { + $col_i = $this->DoCallback('data_color', $row, $idx, $extra); // Custom color index + $data_color = $this->ndx_data_colors[$col_i % $num_data_colors]; + } else { + $data_color = $this->ndx_data_colors[$idx]; + } + } + + /* + * Get data color and error bar color to use for plotting. + * $row, $idx : Index arguments for the current bar. + * &$vars : Variable storage. Caller makes an empty array, and this function uses it. + * &$data_color : Returned result - Color index for the data (bar fill) + * &$error_color : Returned result - Color index for the error bars + * $extra : Extra info flag passed through to data color callback. + */ + protected function GetDataErrorColors($row, $idx, &$vars, &$data_color, &$error_color, $extra = 0) + { + // Initialize or extract variables: + if (empty($vars)) { + $this->NeedErrorBarColors(); // This plot needs error bar colors. + $custom_color = (bool)$this->GetCallback('data_color'); + $num_data_colors = count($this->ndx_data_colors); + $num_error_colors = count($this->ndx_error_bar_colors); + $vars = compact('custom_color', 'num_data_colors', 'num_error_colors'); + } else { + extract($vars); + } + + // Select the colors. + if ($custom_color) { + $col_i = $this->DoCallback('data_color', $row, $idx, $extra); // Custom color index + $data_color = $this->ndx_data_colors[$col_i % $num_data_colors]; + $error_color = $this->ndx_error_bar_colors[$col_i % $num_error_colors]; + } else { + $data_color = $this->ndx_data_colors[$idx]; + $error_color = $this->ndx_error_bar_colors[$idx]; + } + } + + /* + * Get colors to use for a bar chart. There is a data color, and either a border color + * or a shading color (data dark color). + * $row, $idx : Index arguments for the current bar. + * &$vars : Variable storage. Caller makes an empty array, and this function uses it. + * &$data_color : Returned result - Color index for the data (bar fill). + * &$alt_color : Returned result - Color index for the shading or outline. + */ + protected function GetBarColors($row, $idx, &$vars, &$data_color, &$alt_color) + { + // Initialize or extract variables: + if (empty($vars)) { + if ($this->shading > 0) // This plot needs dark colors if shading is on. + $this->NeedDataDarkColors(); + $custom_color = (bool)$this->GetCallback('data_color'); + $num_data_colors = count($this->ndx_data_colors); + $num_border_colors = count($this->ndx_data_border_colors); + $vars = compact('custom_color', 'num_data_colors', 'num_border_colors'); + } else { + extract($vars); + } + + // Select the colors. + if ($custom_color) { + $col_i = $this->DoCallback('data_color', $row, $idx); // Custom color index + $i_data = $col_i % $num_data_colors; // Index for data colors and dark colors + $i_border = $col_i % $num_border_colors; // Index for data borders (if used) + } else { + $i_data = $i_border = $idx; + } + $data_color = $this->ndx_data_colors[$i_data]; + if ($this->shading > 0) { + $alt_color = $this->ndx_data_dark_colors[$i_data]; + } else { + $alt_color = $this->ndx_data_border_colors[$i_border]; + } + } + + /* + * Draw a shape (dot, point). This is the bottom half of DrawDot, and is also + * used by legend drawing. Unlike DrawDot this takes device coordinates. + * The list of supported shapes can also be found in SetPointShapes(). + * $x, $y - Device coordinates of the center of the shape + * $record - Index into point_shapes[] and point_sizes[]. This is taken modulo the array sizes. + * $color - Shape color to use. + */ + protected function DrawShape($x, $y, $record, $color) + { + $index = $record % $this->point_counts; + $point_size = $this->point_sizes[$index]; + $half_point = (int)($point_size / 2); + + $x1 = $x - $half_point; + $x2 = $x + $half_point; + $y1 = $y - $half_point; + $y2 = $y + $half_point; + + switch ($this->point_shapes[$index]) { + case 'halfline': + ImageLine($this->img, $x1, $y, $x, $y, $color); + break; + case 'line': + ImageLine($this->img, $x1, $y, $x2, $y, $color); + break; + case 'plus': + ImageLine($this->img, $x1, $y, $x2, $y, $color); + ImageLine($this->img, $x, $y1, $x, $y2, $color); + break; + case 'cross': + ImageLine($this->img, $x1, $y1, $x2, $y2, $color); + ImageLine($this->img, $x1, $y2, $x2, $y1, $color); + break; + case 'circle': + ImageArc($this->img, $x, $y, $point_size, $point_size, 0, 360, $color); + break; + case 'dot': + ImageFilledEllipse($this->img, $x, $y, $point_size, $point_size, $color); + break; + case 'diamond': + $arrpoints = array($x1, $y, $x, $y1, $x2, $y, $x, $y2); + ImageFilledPolygon($this->img, $arrpoints, 4, $color); + break; + case 'triangle': + $arrpoints = array($x1, $y, $x2, $y, $x, $y2); + ImageFilledPolygon($this->img, $arrpoints, 3, $color); + break; + case 'trianglemid': + $arrpoints = array($x1, $y1, $x2, $y1, $x, $y); + ImageFilledPolygon($this->img, $arrpoints, 3, $color); + break; + case 'yield': + $arrpoints = array($x1, $y1, $x2, $y1, $x, $y2); + ImageFilledPolygon($this->img, $arrpoints, 3, $color); + break; + case 'delta': + $arrpoints = array($x1, $y2, $x2, $y2, $x, $y1); + ImageFilledPolygon($this->img, $arrpoints, 3, $color); + break; + case 'star': + ImageLine($this->img, $x1, $y, $x2, $y, $color); + ImageLine($this->img, $x, $y1, $x, $y2, $color); + ImageLine($this->img, $x1, $y1, $x2, $y2, $color); + ImageLine($this->img, $x1, $y2, $x2, $y1, $color); + break; + case 'hourglass': + $arrpoints = array($x1, $y1, $x2, $y1, $x1, $y2, $x2, $y2); + ImageFilledPolygon($this->img, $arrpoints, 4, $color); + break; + case 'bowtie': + $arrpoints = array($x1, $y1, $x1, $y2, $x2, $y1, $x2, $y2); + ImageFilledPolygon($this->img, $arrpoints, 4, $color); + break; + case 'target': + ImageFilledRectangle($this->img, $x1, $y1, $x, $y, $color); + ImageFilledRectangle($this->img, $x, $y, $x2, $y2, $color); + ImageRectangle($this->img, $x1, $y1, $x2, $y2, $color); + break; + case 'box': + ImageRectangle($this->img, $x1, $y1, $x2, $y2, $color); + break; + case 'home': /* As in: "home plate" (baseball), also looks sort of like a house. */ + $arrpoints = array($x1, $y2, $x2, $y2, $x2, $y, $x, $y1, $x1, $y); + ImageFilledPolygon($this->img, $arrpoints, 5, $color); + break; + case 'up': + ImagePolygon($this->img, array($x, $y1, $x2, $y2, $x1, $y2), 3, $color); + break; + case 'down': + ImagePolygon($this->img, array($x, $y2, $x1, $y1, $x2, $y1), 3, $color); + break; + case 'none': /* Special case, no point shape here */ + break; + default: /* Also 'rect' */ + ImageFilledRectangle($this->img, $x1, $y1, $x2, $y2, $color); + break; + } + return TRUE; + } + + /* + * Draws a styled dot. Uses world coordinates. + * Note: DrawShape() does all the work. + */ + protected function DrawDot($x_world, $y_world, $record, $color) + { + return $this->DrawShape($this->xtr($x_world), $this->ytr($y_world), $record, $color); + } + + /* + * Draw a bar (or segment of a bar), with optional shading or border. + * This is used by the bar and stackedbar plots, vertical and horizontal. + * $x1, $y1 : One corner of the bar. + * $x2, $y2 : Other corner of the bar. + * $data_color : Color index to use for the bar fill. + * $alt_color : Color index to use for the shading (if shading is on), else for the border. + * Note the same color is NOT used for shading and border - just the same argument. + * See GetBarColors() for where these arguments come from. + * $shade_top : Shade the top? (Suppressed for downward stack segments except first.) + * $shade_side : Shade the right side? (Suppressed for leftward stack segments except first.) + * Only one of $shade_top or $shade_side can be FALSE. Both default to TRUE. + */ + protected function DrawBar($x1, $y1, $x2, $y2, $data_color, $alt_color, + $shade_top = TRUE, $shade_side = TRUE) + { + // Sort the points so x1,y1 is upper left and x2,y2 is lower right. This + // is needed in order to get the shading right, and imagerectangle may require it. + if ($x1 > $x2) { + $t = $x1; $x1 = $x2; $x2 = $t; + } + if ($y1 > $y2) { + $t = $y1; $y1 = $y2; $y2 = $t; + } + + // Draw the bar + ImageFilledRectangle($this->img, $x1, $y1, $x2, $y2, $data_color); + + // Draw a shade, or a border. + if (($shade = $this->shading) > 0) { + if ($shade_top && $shade_side) { + $npts = 6; + $pts = array($x1, $y1, $x1 + $shade, $y1 - $shade, $x2 + $shade, $y1 - $shade, + $x2 + $shade, $y2 - $shade, $x2, $y2, $x2, $y1); + } else { + $npts = 4; + if ($shade_top) { // Suppress side shading + $pts = array($x1, $y1, $x1 + $shade, $y1 - $shade, $x2 + $shade, $y1 - $shade, $x2, $y1); + } else { // Suppress top shading + $pts = array($x2, $y2, $x2, $y1, $x2 + $shade, $y1 - $shade, $x2 + $shade, $y2 - $shade); + } + } + ImageFilledPolygon($this->img, $pts, $npts, $alt_color); + } else { + ImageRectangle($this->img, $x1, $y1, $x2,$y2, $alt_color); + } + } + + /* + * Draw an Error Bar set. Used by DrawDotsError and DrawLinesError + */ + protected function DrawYErrorBar($x_world, $y_world, $error_height, $error_bar_type, $color) + { + $x1 = $this->xtr($x_world); + $y1 = $this->ytr($y_world); + $y2 = $this->ytr($y_world+$error_height) ; + + ImageSetThickness($this->img, $this->error_bar_line_width); + ImageLine($this->img, $x1, $y1 , $x1, $y2, $color); + if ($error_bar_type == 'tee') { + ImageLine($this->img, $x1-$this->error_bar_size, $y2, $x1+$this->error_bar_size, $y2, $color); + } + ImageSetThickness($this->img, 1); + return TRUE; + } + +///////////////////////////////////////////// +//////////////////// PLOT DRAWING +///////////////////////////////////////////// + + /* + * Draws a pie chart. Data is 'text-data', 'data-data', or 'text-data-single'. + * + * For text-data-single, the data array contains records with an ignored label, + * and one Y value. Each record defines a sector of the pie, as a portion of + * the sum of all Y values. + * + * For text-data and data-data, the data array contains records with an ignored label, + * an ignored X value (for data-data only), and N (N>=1) Y values per record. + * The pie chart will be produced with N segments. The relative size of the first + * sector of the pie is the sum of the first Y data value in each record, etc. + * + * Note: With text-data-single, the data labels could be used, but are not currently. + * + * If there are no valid data points > 0 at all, just draw nothing. It may seem more correct to + * raise an error, but all of the other plot types handle it this way implicitly. DrawGraph + * checks for an empty data array, but this is different: a non-empty data array with no Y values, + * or all Y=0. + */ + protected function DrawPieChart() + { + if (!$this->CheckDataType('text-data, text-data-single, data-data')) + return FALSE; + + // Allocate dark colors only if they will be used for shading. + if ($this->shading > 0) + $this->NeedDataDarkColors(); + + $xpos = $this->plot_area[0] + $this->plot_area_width/2; + $ypos = $this->plot_area[1] + $this->plot_area_height/2; + $diameter = min($this->plot_area_width, $this->plot_area_height); + $radius = $diameter/2; + + $num_slices = $this->data_columns; // See CheckDataArray which calculates this for us. + if ($num_slices < 1) return TRUE; // Give up early if there is no data at all. + $sumarr = array_fill(0, $num_slices, 0); + + if ($this->datatype_pie_single) { + // text-data-single: One data column per row, one pie slice per row. + for ($i = 0; $i < $num_slices; $i++) { + // $legend[$i] = $this->data[$i][0]; // Note: Labels are not used yet + if (is_numeric($this->data[$i][1])) + $sumarr[$i] = abs($this->data[$i][1]); + } + } else { + // text-data: Sum each column (skipping label), one pie slice per column. + // data-data: Sum each column (skipping X value and label), one pie slice per column. + $skip = ($this->datatype_implied) ? 1 : 2; // Leading values to skip in each row. + for ($i = 0; $i < $this->num_data_rows; $i++) { + for ($j = $skip; $j < $this->num_recs[$i]; $j++) { + if (is_numeric($this->data[$i][$j])) + $sumarr[$j-$skip] += abs($this->data[$i][$j]); + } + } + } + + $total = array_sum($sumarr); + + if ($total == 0) { + // There are either no valid data points, or all are 0. + // See top comment about why not to make this an error. + return TRUE; + } + + if ($this->shading) { + $diam2 = $diameter / 2; + } else { + $diam2 = $diameter; + } + $max_data_colors = count($this->ndx_data_colors); + + // Use the Y label format precision, with default value: + if (isset($this->label_format['y']['precision'])) + $precision = $this->label_format['y']['precision']; + else + $precision = 1; + + for ($h = $this->shading; $h >= 0; $h--) { + $color_index = 0; + $start_angle = 0; + $end_angle = 0; + for ($j = 0; $j < $num_slices; $j++) { + $val = $sumarr[$j]; + + // For shaded pies: the last one (at the top of the "stack") has a brighter color: + if ($h == 0) + $slicecol = $this->ndx_data_colors[$color_index]; + else + $slicecol = $this->ndx_data_dark_colors[$color_index]; + + $label_txt = $this->number_format(($val / $total * 100), $precision) . '%'; + $val = 360 * ($val / $total); + + // NOTE that imagefilledarc measures angles CLOCKWISE (go figure why), + // so the pie chart would start clockwise from 3 o'clock, would it not be + // for the reversal of start and end angles in imagefilledarc() + // Also note ImageFilledArc only takes angles in integer degrees, and if the + // the start and end angles match then you get a full circle not a zero-width + // pie. This is bad. So skip any zero-size wedge. On the other hand, we cannot + // let cumulative error from rounding to integer result in missing wedges. So + // keep the running total as a float, and round the angles. It should not + // be necessary to check that the last wedge ends at 360 degrees. + $start_angle = $end_angle; + $end_angle += $val; + // This method of conversion to integer - truncate after reversing it - was + // chosen to match the implicit method of PHPlot<=5.0.4 to get the same slices. + $arc_start_angle = (int)(360 - $start_angle); + $arc_end_angle = (int)(360 - $end_angle); + + if ($arc_start_angle > $arc_end_angle) { + $mid_angle = deg2rad($end_angle - ($val / 2)); + + // Draw the slice + ImageFilledArc($this->img, $xpos, $ypos+$h, $diameter, $diam2, + $arc_end_angle, $arc_start_angle, + $slicecol, IMG_ARC_PIE); + + // Draw the labels only once + if ($h == 0) { + // Draw the outline + if (! $this->shading) + ImageFilledArc($this->img, $xpos, $ypos+$h, $diameter, $diam2, + $arc_end_angle, $arc_start_angle, $this->ndx_grid_color, + IMG_ARC_PIE | IMG_ARC_EDGED |IMG_ARC_NOFILL); + + // The '* 1.2' trick is to get labels out of the pie chart so there are more + // chances they can be seen in small sectors. + $label_x = $xpos + ($diameter * 1.2 * cos($mid_angle)) * $this->label_scale_position; + $label_y = $ypos+$h - ($diam2 * 1.2 * sin($mid_angle)) * $this->label_scale_position; + + $this->DrawText($this->fonts['generic'], 0, $label_x, $label_y, $this->ndx_grid_color, + $label_txt, 'center', 'center'); + } + } + if (++$color_index >= $max_data_colors) + $color_index = 0; + } // end for + } // end for + return TRUE; + } + + /* + * Draw the points and errors bars for an error plot of types points and linepoints + * Supports only data-data-error format, with each row of the form + * array("title", x, y1, error1+, error1-, y2, error2+, error2-, ...) + * This is called from DrawDots, with data type already checked. + * $paired is true for linepoints error plots, to make sure elements are + * only drawn once. If true, data labels are drawn by DrawLinesError, and error + * bars are drawn by DrawDotsError. (This choice is for backwards compatibility.) + */ + protected function DrawDotsError($paired = FALSE) + { + // Adjust the point shapes and point sizes arrays: + $this->CheckPointParams(); + + $gcvars = array(); // For GetDataErrorColors, which initializes and uses this. + // Special flag for data color callback to indicate the 'points' part of 'linepoints': + $alt_flag = $paired ? 1 : 0; + + for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) { + $record = 1; // Skip record #0 (title) + + $x_now = $this->data[$row][$record++]; // Read it, advance record index + + $x_now_pixels = $this->xtr($x_now); // Absolute coordinates. + + // Draw X Data labels? + if ($this->x_data_label_pos != 'none' && !$paired) + $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels, $row); + + // Now go for Y, E+, E- + for ($idx = 0; $record < $this->num_recs[$row]; $idx++) { + if (is_numeric($this->data[$row][$record])) { // Allow for missing Y data + + // Select the colors: + $this->GetDataErrorColors($row, $idx, $gcvars, $data_color, $error_color, $alt_flag); + + // Y: + $y_now = $this->data[$row][$record++]; + $this->DrawDot($x_now, $y_now, $idx, $data_color); + + // Error + + $val = $this->data[$row][$record++]; + $this->DrawYErrorBar($x_now, $y_now, $val, $this->error_bar_shape, $error_color); + // Error - + $val = $this->data[$row][$record++]; + $this->DrawYErrorBar($x_now, $y_now, -$val, $this->error_bar_shape, $error_color); + } else { + $record += 3; // Skip over missing Y and its error values + } + } + } + return TRUE; + } + + /* + * Draw a points plot, or the points for a linepoints plot + * Data format can be text-data (label, y1, y2, ...) or data-data (label, x, y1, y2, ...) + * Points plot with error bars (data-data-error format) is redirected to DrawDotsError. + * $paired is true for linepoints plots, to make sure elements are only drawn once. + */ + protected function DrawDots($paired = FALSE) + { + if (!$this->CheckDataType('text-data, data-data, data-data-error')) + return FALSE; + if ($this->datatype_error_bars) + return $this->DrawDotsError($paired); // Redirect for points+errorbars plot + + // Adjust the point shapes and point sizes arrays: + $this->CheckPointParams(); + + $gcvars = array(); // For GetDataColor, which initializes and uses this. + // Special flag for data color callback to indicate the 'points' part of 'linepoints': + $alt_flag = $paired ? 1 : 0; + + // Data Value Labels? (Skip if doing the points from a linepoints plot) + $do_dvls = !$paired && $this->CheckDataValueLabels($this->y_data_label_pos, + $dvl_x_off, $dvl_y_off, $dvl_h_align, $dvl_v_align); + + for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) { + $rec = 1; // Skip record #0 (data label) + + if ($this->datatype_implied) // Implied X values? + $x_now = 0.5 + $cnt++; // Place text-data at X = 0.5, 1.5, 2.5, etc... + else + $x_now = $this->data[$row][$rec++]; // Read it, advance record index + + $x_now_pixels = $this->xtr($x_now); + + // Draw X Data labels? + if (!$paired && $this->x_data_label_pos != 'none') + $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels, $row); + + // Proceed with Y values + for ($idx = 0;$rec < $this->num_recs[$row]; $rec++, $idx++) { + if (is_numeric($this->data[$row][$rec])) { // Allow for missing Y data + $y_now = (double)$this->data[$row][$rec]; + + // Select the color: + $this->GetDataColor($row, $idx, $gcvars, $data_color, $alt_flag); + // Draw the marker: + $this->DrawDot($x_now, $y_now, $idx, $data_color); + + // Draw data value labels? + if ($do_dvls) { + $this->DrawDataValueLabel('y', $x_now, $y_now, $y_now, $dvl_h_align, $dvl_v_align, + $dvl_x_off, $dvl_y_off); + } + } + } + } + return TRUE; + } + + /* + * Draw a Thin Bar Line plot, also known as an Impulse plot. + * A clean, fast routine for when you just want charts like stock volume charts. + * Supports data-data and text-data formats for vertical plots, + * and data-data-yx and text-data-yx for horizontal plots. + * Note that although this plot type supports multiple data sets, it rarely makes + * sense to have more than 1, because the lines will overlay. + * This one function does both vertical and horizontal plots. "iv" is used for the + * independent variable (X for vertical plots, Y for horizontal) and "dv" is used + * for the dependent variable (Y for vertical plots, X for horizontal). + */ + protected function DrawThinBarLines() + { + if (!$this->CheckDataType('text-data, data-data, text-data-yx, data-data-yx')) + return FALSE; + + $gcvars = array(); // For GetDataColor, which initializes and uses this. + + for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) { + $rec = 1; // Skip record #0 (data label) + + if ($this->datatype_implied) // Implied independent variable values? + $iv_now = 0.5 + $cnt++; // Place text-data at 0.5, 1.5, 2.5, etc... + else + $iv_now = $this->data[$row][$rec++]; // Read it, advance record index + + if ($this->datatype_swapped_xy) { + $y_now_pixels = $this->ytr($iv_now); + // Draw Y Data labels? + if ($this->y_data_label_pos != 'none') + $this->DrawYDataLabel($this->data[$row][0], $y_now_pixels); + } else { + $x_now_pixels = $this->xtr($iv_now); + // Draw X Data labels? + if ($this->x_data_label_pos != 'none') + $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels); + } + + // Proceed with dependent values + for ($idx = 0; $rec < $this->num_recs[$row]; $rec++, $idx++) { + if (is_numeric($this->data[$row][$rec])) { // Allow for missing data + $dv = $this->data[$row][$rec]; + ImageSetThickness($this->img, $this->line_widths[$idx]); + + // Select the color: + $this->GetDataColor($row, $idx, $gcvars, $data_color); + + if ($this->datatype_swapped_xy) { + // Draw a line from user defined y axis position right (or left) to xtr($dv) + ImageLine($this->img, $this->y_axis_x_pixels, $y_now_pixels, + $this->xtr($dv), $y_now_pixels, $data_color); + } else { + // Draw a line from user defined x axis position up (or down) to ytr($dv) + ImageLine($this->img, $x_now_pixels, $this->x_axis_y_pixels, + $x_now_pixels, $this->ytr($dv), $data_color); + } + } + } + } + + ImageSetThickness($this->img, 1); + return TRUE; + } + + /* + * Draw an 'area' or 'stacked area' plot. + * Both of these fill the area between lines, but in the stacked area graph the Y values + * are accumulated for each X, same as stacked bars. In the regular area graph, the areas + * are filled in order from the X axis up to each Y (so the Y values for each X need to be + * in decreasing order in this case). + * Data format can be text-data (label, y1, y2, ...) or data-data (label, x, y1, y2, ...) + * Notes: + * All Y values must be >= 0. (If any Y<0 the absolute value is used.) + * Missing data points are NOT handled. (They are counted as 0.) + * All rows must have the same number of Y points, or an error image will be produced. + */ + protected function DrawArea($do_stacked = FALSE) + { + if (!$this->CheckDataType('text-data, data-data')) + return FALSE; + + $n = $this->num_data_rows; // Number of X values + + // These arrays store the device X and Y coordinates for all lines: + $xd = array(); + $yd = array(); + + // Make sure each row has the same number of values. Note records_per_group is max(num_recs). + if ($this->records_per_group != min($this->num_recs)) { + return $this->PrintError("DrawArea(): Data array must contain the same number" + . " of Y values for each X"); + } + + // Calculate the Y value for each X, and store the device + // coordinates into the xd and yd arrays. + // For stacked area plots, the Y values accumulate. + for ($row = 0; $row < $n; $row++) { + $rec = 1; // Skip record #0 (data label) + + if ($this->datatype_implied) // Implied X values? + $x_now = 0.5 + $row; // Place text-data at X = 0.5, 1.5, 2.5, etc... + else + $x_now = $this->data[$row][$rec++]; // Read it, advance record index + + $x_now_pixels = $this->xtr($x_now); + + if ($this->x_data_label_pos != 'none') // Draw X Data labels? + $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels); + + // Store the X value. + // There is an artificial Y value at the axis. For 'area' it goes + // at the end; for stackedarea it goes before the start. + $xd[$row] = $x_now_pixels; + $yd[$row] = array(); + if ($do_stacked) + $yd[$row][] = $this->x_axis_y_pixels; + + // Store the Y values for this X. + // All Y values are clipped to the x axis which should be zero but can be moved. + $y = 0; + while ($rec < $this->records_per_group) { + if (is_numeric($this->data[$row][$rec])) { // Treat missing values as 0. + $y += abs($this->data[$row][$rec]); + } + $yd[$row][] = $this->ytr(max($this->x_axis_position, $y)); + if (!$do_stacked) $y = 0; + $rec++; + } + + if (!$do_stacked) + $yd[$row][] = $this->x_axis_y_pixels; + } + + // Now draw the filled polygons. + // Note data_columns is the number of Y points (columns excluding label and X), and the + // number of entries in the yd[] arrays is data_columns+1. + $prev_row = 0; + for ($row = 1; $row <= $this->data_columns; $row++) { // 1 extra for X axis artificial row + $pts = array(); + // Previous data set forms top (for area) or bottom (for stackedarea): + for ($j = 0; $j < $n; $j++) { + $pts[] = $xd[$j]; + $pts[] = $yd[$j][$prev_row]; + } + // Current data set forms bottom (for area) or top (for stackedarea): + for ($j = $n- 1; $j >= 0; $j--) { + $pts[] = $xd[$j]; + $pts[] = $yd[$j][$row]; + } + // Draw it: + ImageFilledPolygon($this->img, $pts, $n * 2, $this->ndx_data_colors[$prev_row]); + + $prev_row = $row; + } + return TRUE; + } + + /* + * Draw a line plot, or the lines part of a linepoints plot + * Data format can be text-data (label, y1, y2, ...) or data-data (label, x, y1, y2, ...) + * Line plot with error bars (data-data-error format) is redirected to DrawLinesError. + * $paired is true for linepoints plots, to make sure elements are only drawn once. + */ + protected function DrawLines($paired = FALSE) + { + if (!$this->CheckDataType('text-data, data-data, data-data-error')) + return FALSE; + if ($this->datatype_error_bars) + return $this->DrawLinesError($paired); // Redirect for lines+errorbar plot + + // Flag array telling if the current point is valid, one element per plot line. + // If start_lines[i] is true, then (lastx[i], lasty[i]) is the previous point. + $start_lines = array_fill(0, $this->data_columns, FALSE); + + $gcvars = array(); // For GetDataColor, which initializes and uses this. + + // Data Value Labels? + $do_dvls = $this->CheckDataValueLabels($this->y_data_label_pos, + $dvl_x_off, $dvl_y_off, $dvl_h_align, $dvl_v_align); + + for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) { + $record = 1; // Skip record #0 (data label) + + if ($this->datatype_implied) // Implied X values? + $x_now = 0.5 + $cnt++; // Place text-data at X = 0.5, 1.5, 2.5, etc... + else + $x_now = $this->data[$row][$record++]; // Read it, advance record index + + $x_now_pixels = $this->xtr($x_now); // Absolute coordinates + + if ($this->x_data_label_pos != 'none') // Draw X Data labels? + $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels, $row); + + for ($idx = 0; $record < $this->num_recs[$row]; $record++, $idx++) { + if (($line_style = $this->line_styles[$idx]) == 'none') + continue; //Allow suppressing entire line, useful with linepoints + if (is_numeric($this->data[$row][$record])) { //Allow for missing Y data + $y_now = (double)$this->data[$row][$record]; + $y_now_pixels = $this->ytr($y_now); + + if ($start_lines[$idx]) { + // Set line width, revert it to normal at the end + ImageSetThickness($this->img, $this->line_widths[$idx]); + + // Select the color: + $this->GetDataColor($row, $idx, $gcvars, $data_color); + + if ($line_style == 'dashed') { + $this->SetDashedStyle($data_color); + $data_color = IMG_COLOR_STYLED; + } + ImageLine($this->img, $x_now_pixels, $y_now_pixels, + $lastx[$idx], $lasty[$idx], $data_color); + } + + // Draw data value labels? + if ($do_dvls) { + $this->DrawDataValueLabel('y', $x_now, $y_now, $y_now, $dvl_h_align, $dvl_v_align, + $dvl_x_off, $dvl_y_off); + } + + $lasty[$idx] = $y_now_pixels; + $lastx[$idx] = $x_now_pixels; + $start_lines[$idx] = TRUE; + } elseif ($this->draw_broken_lines) { // Y data missing, leave a gap. + $start_lines[$idx] = FALSE; + } + } // end for + } // end for + + ImageSetThickness($this->img, 1); // Revert to original state for lines to be drawn later. + return TRUE; + } + + /* + * Draw lines with error bars for an error plot of types lines and linepoints + * Supports only data-data-error format, with each row of the form + * array("title", x, y1, error1+, error1-, y2, error2+, error2-, ...) + * This is called from DrawLines, with data type already checked. + * $paired is true for linepoints error plots, to make sure elements are + * only drawn once. If true, data labels are drawn by DrawLinesError, and error + * bars are drawn by DrawDotsError. (This choice is for backwards compatibility.) + */ + protected function DrawLinesError($paired = FALSE) + { + $start_lines = array_fill(0, $this->data_columns, FALSE); + + $gcvars = array(); // For GetDataErrorColors, which initializes and uses this. + + for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) { + $record = 1; // Skip record #0 (data label) + + $x_now = $this->data[$row][$record++]; // Read X value, advance record index + + $x_now_pixels = $this->xtr($x_now); // Absolute coordinates. + + if ($this->x_data_label_pos != 'none') // Draw X Data labels? + $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels, $row); + + // Now go for Y, E+, E- + for ($idx = 0; $record < $this->num_recs[$row]; $idx++) { + if (($line_style = $this->line_styles[$idx]) == 'none') + continue; //Allow suppressing entire line, useful with linepoints + if (is_numeric($this->data[$row][$record])) { // Allow for missing Y data + + // Select the colors: + $this->GetDataErrorColors($row, $idx, $gcvars, $data_color, $error_color); + + // Y + $y_now = $this->data[$row][$record++]; + $y_now_pixels = $this->ytr($y_now); + + if ($start_lines[$idx]) { + ImageSetThickness($this->img, $this->line_widths[$idx]); + + if ($line_style == 'dashed') { + $this->SetDashedStyle($data_color); + $data_color = IMG_COLOR_STYLED; + } + ImageLine($this->img, $x_now_pixels, $y_now_pixels, + $lastx[$idx], $lasty[$idx], $data_color); + } + + if ($paired) { + $record += 2; // Skip error bars - done in the 'points' part of 'linepoints'. + } else { + // Error+ + $val = $this->data[$row][$record++]; + $this->DrawYErrorBar($x_now, $y_now, $val, $this->error_bar_shape, $error_color); + + // Error- + $val = $this->data[$row][$record++]; + $this->DrawYErrorBar($x_now, $y_now, -$val, $this->error_bar_shape, $error_color); + } + + // Update indexes: + $start_lines[$idx] = TRUE; // Tells us if we already drew the first column of points, + // thus having $lastx and $lasty ready for the next column. + $lastx[$idx] = $x_now_pixels; + $lasty[$idx] = $y_now_pixels; + + } else { + $record += 3; // Skip over missing Y and its error values + if ($this->draw_broken_lines) { + $start_lines[$idx] = FALSE; + } + } + } // end for + } // end for + + ImageSetThickness($this->img, 1); // Revert to original state for lines to be drawn later. + return TRUE; + } + + /* + * Draw a Lines+Points plot (linepoints). + * This just uses DrawLines and DrawDots. They handle the error-bar case themselves. + */ + protected function DrawLinePoints() + { + // This check is redundant, as DrawLines and DrawDots do it, but left here as insurance. + if (!$this->CheckDataType('text-data, data-data, data-data-error')) + return FALSE; + $this->DrawLines(TRUE); + $this->DrawDots(TRUE); + return TRUE; + } + + /* + * Draw a Squared Line plot. + * Data format can be text-data (label, y1, y2, ...) or data-data (label, x, y1, y2, ...) + * This is based on DrawLines(), with one more line drawn for each point. + */ + protected function DrawSquared() + { + if (!$this->CheckDataType('text-data, data-data')) + return FALSE; + + // Flag array telling if the current point is valid, one element per plot line. + // If start_lines[i] is true, then (lastx[i], lasty[i]) is the previous point. + $start_lines = array_fill(0, $this->data_columns, FALSE); + + $gcvars = array(); // For GetDataColor, which initializes and uses this. + + // Data Value Labels? + $do_dvls = $this->CheckDataValueLabels($this->y_data_label_pos, + $dvl_x_off, $dvl_y_off, $dvl_h_align, $dvl_v_align); + + for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) { + $record = 1; // Skip record #0 (data label) + + if ($this->datatype_implied) // Implied X values? + $x_now = 0.5 + $cnt++; // Place text-data at X = 0.5, 1.5, 2.5, etc... + else + $x_now = $this->data[$row][$record++]; // Read it, advance record index + + $x_now_pixels = $this->xtr($x_now); // Absolute coordinates + + if ($this->x_data_label_pos != 'none') // Draw X Data labels? + $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels); // notice there is no last param. + + // Draw Lines + for ($idx = 0; $record < $this->num_recs[$row]; $record++, $idx++) { + if (is_numeric($this->data[$row][$record])) { // Allow for missing Y data + $y_now = (double)$this->data[$row][$record]; + $y_now_pixels = $this->ytr($y_now); + + if ($start_lines[$idx]) { + // Set line width, revert it to normal at the end + ImageSetThickness($this->img, $this->line_widths[$idx]); + + // Select the color: + $this->GetDataColor($row, $idx, $gcvars, $data_color); + + if ($this->line_styles[$idx] == 'dashed') { + $this->SetDashedStyle($data_color); + $data_color = IMG_COLOR_STYLED; + } + ImageLine($this->img, $lastx[$idx], $lasty[$idx], + $x_now_pixels, $lasty[$idx], $data_color); + ImageLine($this->img, $x_now_pixels, $lasty[$idx], + $x_now_pixels, $y_now_pixels, $data_color); + } + + // Draw data value labels? + if ($do_dvls) { + $this->DrawDataValueLabel('y', $x_now, $y_now, $y_now, $dvl_h_align, $dvl_v_align, + $dvl_x_off, $dvl_y_off); + } + + $lastx[$idx] = $x_now_pixels; + $lasty[$idx] = $y_now_pixels; + $start_lines[$idx] = TRUE; + } elseif ($this->draw_broken_lines) { // Y data missing, leave a gap. + $start_lines[$idx] = FALSE; + } + } + } // end while + + ImageSetThickness($this->img, 1); + return TRUE; + } + + /* + * Draw a Bar chart + * Supports text-data format, with each row in the form array(label, y1, y2, y3, ...) + * Horizontal bars (text-data-yx format) are sent to DrawHorizBars() instead. + */ + protected function DrawBars() + { + if (!$this->CheckDataType('text-data, text-data-yx')) + return FALSE; + if ($this->datatype_swapped_xy) + return $this->DrawHorizBars(); + $this->CalcBarWidths(FALSE, TRUE); // Calculate bar widths for unstacked, vertical + + // This is the X offset from the bar group's label center point to the left side of the first bar + // in the group. See also CalcBarWidths above. + $x_first_bar = ($this->data_columns * $this->record_bar_width) / 2 - $this->bar_adjust_gap; + + $gcvars = array(); // For GetBarColors, which initializes and uses this. + + for ($row = 0; $row < $this->num_data_rows; $row++) { + $record = 1; // Skip record #0 (data label) + + $x_now_pixels = $this->xtr(0.5 + $row); // Place text-data at X = 0.5, 1.5, 2.5, etc... + + if ($this->x_data_label_pos != 'none') // Draw X Data labels? + $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels); + + // Lower left X of first bar in the group: + $x1 = $x_now_pixels - $x_first_bar; + + // Draw the bars in the group: + for ($idx = 0; $record < $this->num_recs[$row]; $record++, $idx++) { + if (is_numeric($this->data[$row][$record])) { // Allow for missing Y data + $y = $this->data[$row][$record]; + $x2 = $x1 + $this->actual_bar_width; + + if (($upgoing_bar = $y >= $this->x_axis_position)) { + $y1 = $this->ytr($y); + $y2 = $this->x_axis_y_pixels; + } else { + $y1 = $this->x_axis_y_pixels; + $y2 = $this->ytr($y); + } + + // Select the colors: + $this->GetBarColors($row, $idx, $gcvars, $data_color, $alt_color); + + // Draw the bar, and the shade or border: + $this->DrawBar($x1, $y1, $x2, $y2, $data_color, $alt_color); + + // Draw optional data labels above the bars (or below, for negative values). + if ( $this->y_data_label_pos == 'plotin') { + if ($upgoing_bar) { + $v_align = 'bottom'; + $y_offset = -5 - $this->shading; + } else { + $v_align = 'top'; + $y_offset = 2; + } + $this->DrawDataValueLabel('y', $row+0.5, $y, $y, 'center', $v_align, + ($idx + 0.5) * $this->record_bar_width - $x_first_bar, $y_offset); + } + } + // Step to next bar in group: + $x1 += $this->record_bar_width; + } // end for + } // end for + return TRUE; + } + + /* + * Draw a Horizontal Bar chart + * Supports only text-data-yx format, with each row in the form array(label, x1, x2, x3, ...) + * Note that the data values are X not Y, and the bars are drawn horizontally. + * This is called from DrawBars, which has already checked the data type. + */ + protected function DrawHorizBars() + { + $this->CalcBarWidths(FALSE, FALSE); // Calculate bar widths for unstacked, vertical + + // This is the Y offset from the bar group's label center point to the bottom of the first bar + // in the group. See also CalcBarWidths above. + $y_first_bar = ($this->data_columns * $this->record_bar_width) / 2 - $this->bar_adjust_gap; + + $gcvars = array(); // For GetBarColors, which initializes and uses this. + + for ($row = 0; $row < $this->num_data_rows; $row++) { + $record = 1; // Skip record #0 (data label) + + $y_now_pixels = $this->ytr(0.5 + $row); // Place bars at Y=0.5, 1.5, 2.5, etc... + + if ($this->y_data_label_pos != 'none') // Draw Y Data Labels? + $this->DrawYDataLabel($this->data[$row][0], $y_now_pixels); + + // Lower left Y of first bar in the group: + $y1 = $y_now_pixels + $y_first_bar; + + // Draw the bars in the group: + for ($idx = 0; $record < $this->num_recs[$row]; $record++, $idx++) { + if (is_numeric($this->data[$row][$record])) { // Allow for missing X data + $x = $this->data[$row][$record]; + $y2 = $y1 - $this->actual_bar_width; + + if (($rightwards_bar = $x >= $this->y_axis_position)) { + $x1 = $this->xtr($x); + $x2 = $this->y_axis_x_pixels; + } else { + $x1 = $this->y_axis_x_pixels; + $x2 = $this->xtr($x); + } + + // Select the colors: + $this->GetBarColors($row, $idx, $gcvars, $data_color, $alt_color); + + // Draw the bar, and the shade or border: + $this->DrawBar($x1, $y1, $x2, $y2, $data_color, $alt_color); + + // Draw optional data labels to the right of the bars (or left, if the bar + // goes left of the Y axis line). + if ($this->x_data_label_pos == 'plotin') { + if ($rightwards_bar) { + $h_align = 'left'; + $x_offset = 5 + $this->shading; + } else { + $h_align = 'right'; + $x_offset = -2; + } + $this->DrawDataValueLabel('x', $x, $row+0.5, $x, $h_align, 'center', + $x_offset, $y_first_bar - ($idx + 0.5) * $this->record_bar_width); + } + + } + // Step to next bar in group: + $y1 -= $this->record_bar_width; + } // end for + } // end for + + return TRUE; + } + + /* + * Draw a Stacked Bar chart + * Supports text-data format, with each row in the form array(label, y1, y2, y3, ...) + * Horizontal stacked bars (text-data-yx format) are sent to DrawHorizStackedBars() instead. + * Original stacked bars idea by Laurent Kruk < lolok at users.sourceforge.net > + */ + protected function DrawStackedBars() + { + if (!$this->CheckDataType('text-data, text-data-yx')) + return FALSE; + if ($this->datatype_swapped_xy) + return $this->DrawHorizStackedBars(); + $this->CalcBarWidths(TRUE, TRUE); // Calculate bar widths for stacked, vertical + + // This is the X offset from the bar's label center point to the left side of the bar. + $x_first_bar = $this->record_bar_width / 2 - $this->bar_adjust_gap; + + $gcvars = array(); // For GetBarColors, which initializes and uses this. + + // Determine if any data labels are on: + $data_labels_within = ($this->y_data_label_pos == 'plotstack'); + $data_labels_end = $data_labels_within || ($this->y_data_label_pos == 'plotin'); + $data_label_y_offset = -5 - $this->shading; // For upward labels only. + + for ($row = 0; $row < $this->num_data_rows; $row++) { + $record = 1; // Skip record #0 (data label) + + $x_now_pixels = $this->xtr(0.5 + $row); // Place text-data at X = 0.5, 1.5, 2.5, etc... + + if ($this->x_data_label_pos != 'none') // Draw X Data labels? + $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels); + + // Determine bar direction based on 1st non-zero value. Note the bar direction is + // based on zero, not the axis value. + $n_recs = $this->num_recs[$row]; + $upward = TRUE; // Initialize this for the case of all segments = 0 + for ($i = $record; $i < $n_recs; $i++) { + if (is_numeric($this_y = $this->data[$row][$i]) && $this_y != 0) { + $upward = ($this_y > 0); + break; + } + } + + $x1 = $x_now_pixels - $x_first_bar; // Left X of bars in this stack + $x2 = $x1 + $this->actual_bar_width; // Right X of bars in this stack + $wy1 = 0; // World coordinates Y1, current sum of values + $wy2 = $this->x_axis_position; // World coordinates Y2, last drawn value + + // Draw bar segments and labels in this stack. + $first = TRUE; + for ($idx = 0; $record < $n_recs; $record++, $idx++) { + + // Skip missing Y values. Process Y=0 values due to special case of moved axis. + if (is_numeric($this_y = $this->data[$row][$record])) { + + $wy1 += $this_y; // Keep the running total for this bar stack + + // Draw nothing if this segment would not increase the bar height. + // Upward bars: draw if wy1>wy2. Downward bars: Draw if wy1ytr($wy1); // Convert to device coordinates. $y1 is outermost value. + $y2 = $this->ytr($wy2); // $y2 is innermost (closest to axis). + + // Select the colors: + $this->GetBarColors($row, $idx, $gcvars, $data_color, $alt_color); + + // Draw the bar, and the shade or border: + $this->DrawBar($x1, $y1, $x2, $y2, $data_color, $alt_color, + // Only shade the top for upward bars, or the first segment of downward bars: + $upward || $first, TRUE); + + // Draw optional data label for this bar segment just inside the end. + // Text value is the current Y, but position is the cumulative Y. + // The label is only drawn if it fits in the segment height |y2-y1|. + if ($data_labels_within) { + $this->DrawDataValueLabel('y', $row+0.5, $wy1, $this_y, + 'center', $upward ? 'top' : 'bottom', + 0, $upward ? 3 : -3, NULL, abs($y1 - $y2)); + } + // Mark the new end of the bar, conditional on segment height > 0. + $wy2 = $wy1; + $first = FALSE; + } + } + } // end for + + // Draw optional data label above the bar with the total value. + // Value is wy1 (total value), but position is wy2 (end of the bar stack). + // These differ only with wrong-direction segments, or a stack completely clipped by the axis. + if ($data_labels_end) { + $this->DrawDataValueLabel('y', $row+0.5, $wy2, $wy1, 'center', $upward ? 'bottom' : 'top', + 0, $upward ? $data_label_y_offset : 5); + } + } // end for + return TRUE; + } + + /* + * Draw a Horizontal Stacked Bar chart + * Supports only text-data-yx format, with each row in the form array(label, x1, x2, x3, ...) + * Note that the data values are X not Y, and the bars are drawn horizontally. + * This is called from DrawStackedBars, which has already checked the data type. + */ + protected function DrawHorizStackedBars() + { + $this->CalcBarWidths(TRUE, FALSE); // Calculate bar widths for stacked, horizontal + + // This is the Y offset from the bar's label center point to the bottom of the bar + $y_first_bar = $this->record_bar_width / 2 - $this->bar_adjust_gap; + + $gcvars = array(); // For GetBarColors, which initializes and uses this. + + // Determine if any data labels are on: + $data_labels_within = ($this->x_data_label_pos == 'plotstack'); + $data_labels_end = $data_labels_within || ($this->x_data_label_pos == 'plotin'); + $data_label_x_offset = 5 + $this->shading; // For rightward labels only + + for ($row = 0; $row < $this->num_data_rows; $row++) { + $record = 1; // Skip record #0 (data label) + + $y_now_pixels = $this->ytr(0.5 + $row); // Place bars at Y=0.5, 1.5, 2.5, etc... + + if ($this->y_data_label_pos != 'none') // Draw Y Data labels? + $this->DrawYDataLabel($this->data[$row][0], $y_now_pixels); + + // Determine bar direction based on 1st non-zero value. Note the bar direction is + // based on zero, not the axis value. + $n_recs = $this->num_recs[$row]; + $rightward = TRUE; // Initialize this for the case of all segments = 0 + for ($i = $record; $i < $n_recs; $i++) { + if (is_numeric($this_x = $this->data[$row][$i]) && $this_x != 0) { + $rightward = ($this_x > 0); + break; + } + } + + // Lower left and upper left Y of the bars in this stack: + $y1 = $y_now_pixels + $y_first_bar; // Lower Y of bars in this stack + $y2 = $y1 - $this->actual_bar_width; // Upper Y of bars in this stack + $wx1 = 0; // World coordinates X1, current sum of values + $wx2 = $this->y_axis_position; // World coordinates X2, last drawn value + + // Draw bar segments and labels in this stack. + $first = TRUE; + for ($idx = 0; $record < $this->num_recs[$row]; $record++, $idx++) { + + // Skip missing X values. Process Y=0 values due to special case of moved axis. + if (is_numeric($this_x = $this->data[$row][$record])) { + + $wx1 += $this_x; // Keep the running total for this bar stack + + // Draw nothing if this segment would not increase the bar length. + // Rightward bars: draw if wx1>wx2. Leftward bars: Draw if wx1xtr($wx1); // Convert to device coordinates. $x1 is outermost value. + $x2 = $this->xtr($wx2); // $x2 is innermost (closest to axis). + + // Select the colors: + $this->GetBarColors($row, $idx, $gcvars, $data_color, $alt_color); + + // Draw the bar, and the shade or border: + $this->DrawBar($x1, $y1, $x2, $y2, $data_color, $alt_color, + // Only shade the side for rightward bars, or the first segment of leftward bars: + TRUE, $rightward || $first); + // Draw optional data label for this bar segment just inside the end. + // Text value is the current X, but position is the cumulative X. + // The label is only drawn if it fits in the segment width |x2-x1|. + if ($data_labels_within) { + $this->DrawDataValueLabel('x', $wx1, $row+0.5, $this_x, + $rightward ? 'right' : 'left', 'center', + $rightward ? -3 : 3, 0, abs($x1 - $x2), NULL); + } + // Mark the new end of the bar, conditional on segment width > 0. + $wx2 = $wx1; + $first = FALSE; + } + } + } // end for + + // Draw optional data label right of the bar with the total value. + // Value is wx1 (total value), but position is wx2 (end of the bar stack). + // These differ only with wrong-direction segments, or a stack completely clipped by the axis. + if ($data_labels_end) { + $this->DrawDataValueLabel('x', $wx2, $row+0.5, $wx1, $rightward ? 'left' : 'right', 'center', + $rightward ? $data_label_x_offset : -5, 0); + } + } // end for + return TRUE; + } + + /* + * Draw a financial "Open/High/Low/Close" (OHLC) plot, including candlestick plots. + * Data format can be text-data (label, Yo, Yh, Yl, Yc) or data-data (label, X, Yo, Yh, Yl, Yc). + * Yo="Opening price", Yc="Closing price", Yl="Low price", Yh="High price". + * Each row must have exactly 4 Y values. No multiple data sets, no missing values. + * There are 3 subtypes, selected by $draw_candles and $always_fill. + * $draw_candles $always_fill Description: + * FALSE N/A A basic OHLC chart with a vertical line for price range, horizontal + * tick marks on left for opening price and right for closing price. + * TRUE FALSE A candlestick plot with filled body indicating close down, outline + * for closing up, and vertical wicks for low and high prices. + * TRUE TRUE A candlestick plot where the candle bodies are always filled. + * These map to 3 plot types per the $plots[] array. + * + * Data color usage: If closes down: If closes up or unchanged: + * Candlestick body, ohlc range line: color 0 color 1 + * Candlestick wicks, ohlc tick marks: color 2 color 3 + * There are three member variables that control the width (candlestick body or tick marks): + * ohlc_max_width, ohlc_min_width, ohlc_frac_width + * (There is no API to change them at this time.) + */ + protected function DrawOHLC($draw_candles, $always_fill = FALSE) + { + if (!$this->CheckDataType('text-data, data-data')) + return FALSE; + + // Assign name of GD function to draw candlestick bodies for stocks that close up. + $draw_body_close_up = $always_fill ? 'imagefilledrectangle' : 'imagerectangle'; + + // These 3 variables control the calculation of the half-width of the candle body, or length of + // the tick marks. This is scaled based on the plot density, but within tight limits. + $min_width = isset($this->ohlc_min_width) ? $this->ohlc_min_width : 2; + $max_width = isset($this->ohlc_max_width) ? $this->ohlc_max_width : 8; + $width_factor = isset($this->ohlc_frac_width) ? $this->ohlc_frac_width : 0.3; + $dw = max($min_width, min($max_width, + (int)($width_factor * $this->plot_area_width / $this->num_data_rows))); + + // Get line widths to use: index 0 for body/stroke, 1 for wick/tick. + list($body_thickness, $wick_thickness) = $this->line_widths; + + $gcvars = array(); // For GetDataColor, which initializes and uses this. + + for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) { + $record = 1; // Skip record #0 (data label) + + if ($this->datatype_implied) // Implied X values? + $x_now = 0.5 + $cnt++; // Place text-data at X = 0.5, 1.5, 2.5, etc... + else + $x_now = $this->data[$row][$record++]; // Read it, advance record index + + $x_now_pixels = $this->xtr($x_now); // Convert X to device coordinates + $x_left = $x_now_pixels - $dw; + $x_right = $x_now_pixels + $dw; + + if ($this->x_data_label_pos != 'none') // Draw X Data labels? + $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels, $row); + + // Require and use 4 numeric values in each row. + if ($this->num_recs[$row] - $record != 4 + || !is_numeric($yo = $this->data[$row][$record++]) + || !is_numeric($yh = $this->data[$row][$record++]) + || !is_numeric($yl = $this->data[$row][$record++]) + || !is_numeric($yc = $this->data[$row][$record++])) { + return $this->PrintError("DrawOHLC: row $row must have 4 values."); + } + + // Set device coordinates for each value and direction flag: + $yh_pixels = $this->ytr($yh); + $yl_pixels = $this->ytr($yl); + $yc_pixels = $this->ytr($yc); + $yo_pixels = $this->ytr($yo); + $closed_up = $yc >= $yo; + + // Get data colors and line thicknesses: + if ($closed_up) { + $this->GetDataColor($row, 1, $gcvars, $body_color); // Color 1 for body, closing up + $this->GetDataColor($row, 3, $gcvars, $ext_color); // Color 3 for wicks/ticks + } else { + $this->GetDataColor($row, 0, $gcvars, $body_color); // Color 0 for body, closing down + $this->GetDataColor($row, 2, $gcvars, $ext_color); // Color 2 for wicks/ticks + } + imagesetthickness($this->img, $body_thickness); + + if ($draw_candles) { + // Note: Unlike ImageFilledRectangle, ImageRectangle 'requires' its arguments in + // order with upper left corner first. + if ($closed_up) { + $yb1_pixels = $yc_pixels; // Upper body Y + $yb2_pixels = $yo_pixels; // Lower body Y + $draw_body = $draw_body_close_up; + // Avoid a PHP/GD bug resulting in "T"-shaped ends to zero height unfilled rectangle: + if ($yb1_pixels == $yb2_pixels) + $draw_body = 'imagefilledrectangle'; + } else { + $yb1_pixels = $yo_pixels; + $yb2_pixels = $yc_pixels; + $draw_body = 'imagefilledrectangle'; + } + + // Draw candle body + $draw_body($this->img, $x_left, $yb1_pixels, $x_right, $yb2_pixels, $body_color); + + // Draw upper and lower wicks, if they have height. (In device coords, that's dY<0) + imagesetthickness($this->img, $wick_thickness); + if ($yh_pixels < $yb1_pixels) { + imageline($this->img, $x_now_pixels, $yb1_pixels, $x_now_pixels, $yh_pixels, $ext_color); + } + if ($yl_pixels > $yb2_pixels) { + imageline($this->img, $x_now_pixels, $yb2_pixels, $x_now_pixels, $yl_pixels, $ext_color); + } + } else { + // Basic OHLC + imageline($this->img, $x_now_pixels, $yl_pixels, $x_now_pixels, $yh_pixels, $body_color); + imagesetthickness($this->img, $wick_thickness); + imageline($this->img, $x_left, $yo_pixels, $x_now_pixels, $yo_pixels, $ext_color); + imageline($this->img, $x_right, $yc_pixels, $x_now_pixels, $yc_pixels, $ext_color); + } + imagesetthickness($this->img, 1); + } + return TRUE; + } + + /* + * Draw the graph. + * This is the function that performs the actual drawing, after all + * the parameters and data are set up. + * It also outputs the finished image, unless told not to. + * Note: It is possible for this to be called multiple times. + */ + function DrawGraph() + { + // Test for missing image, missing data, empty data: + if (!$this->CheckDataArray()) + return FALSE; // Error message already reported. + + // Set defaults then import plot type configuration: + $draw_axes = TRUE; + $draw_arg = array(); // Default is: no arguments to the drawing function + extract(PHPlot::$plots[$this->plot_type]); + + // Allocate colors for the plot: + $this->SetColorIndexes(); + + // Get maxima and minima for scaling: + if (!$this->FindDataLimits()) + return FALSE; + + // Set plot area world values (plot_max_x, etc.): + if (!$this->CalcPlotAreaWorld()) + return FALSE; + + // Calculate X and Y axis positions in World Coordinates: + $this->CalcAxisPositions(); + + // Process label-related parameters: + $this->CheckLabels(); + + // Apply grid defaults: + $this->CalcGridSettings(); + + // Calculate the plot margins, if needed. + // For pie charts, set the $maximize argument to maximize space usage. + $this->CalcMargins(!$draw_axes); + + // Calculate the actual plot area in device coordinates: + $this->CalcPlotAreaPixels(); + + // Calculate the mapping between world and device coordinates: + $this->CalcTranslation(); + + // Pad color and style arrays to fit records per group: + $this->PadArrays(); + $this->DoCallback('draw_setup'); + + $this->DrawBackground(); + $this->DrawImageBorder(); + $this->DoCallback('draw_image_background'); + + $this->DrawPlotAreaBackground(); + $this->DoCallback('draw_plotarea_background', $this->plot_area); + + $this->DrawTitle(); + if ($draw_axes) { // If no axes (pie chart), no axis titles either + $this->DrawXTitle(); + $this->DrawYTitle(); + } + $this->DoCallback('draw_titles'); + + if ($draw_axes && ! $this->grid_at_foreground) { // Usually one wants grids to go back, but... + $this->DrawYAxis(); // Y axis must be drawn before X axis (see DrawYAxis()) + $this->DrawXAxis(); + $this->DoCallback('draw_axes'); + } + + // Call the plot-type drawing method: + call_user_func_array(array($this, $draw_method), $draw_arg); + $this->DoCallback('draw_graph', $this->plot_area); + + if ($draw_axes && $this->grid_at_foreground) { // Usually one wants grids to go back, but... + $this->DrawYAxis(); // Y axis must be drawn before X axis (see DrawYAxis()) + $this->DrawXAxis(); + $this->DoCallback('draw_axes'); + } + + if ($draw_axes) { + $this->DrawPlotBorder(); + $this->DoCallback('draw_border'); + } + + if ($this->legend) { + $this->DrawLegend(); + $this->DoCallback('draw_legend'); + } + $this->DoCallback('draw_all', $this->plot_area); + + if ($this->print_image && !$this->PrintImage()) + return FALSE; + + return TRUE; + } + +///////////////////////////////////////////// +////////////////// DEPRECATED METHODS +///////////////////////////////////////////// + + /* + * Note on deprecated methods - as these pre-date the PHPlot Reference + * Manual, and there is minimal documentation about them, I have neither + * removed them nor changed them. They are not tested or documented, and + * should not be used. + */ + + /* + * Deprecated, use SetYTickPos() + */ + function SetDrawVertTicks($which_dvt) + { + if ($which_dvt != 1) + $this->SetYTickPos('none'); + return TRUE; + } + + /* + * Deprecated, use SetXTickPos() + */ + function SetDrawHorizTicks($which_dht) + { + if ($which_dht != 1) + $this->SetXTickPos('none'); + return TRUE; + } + + /* + * Deprecated - use SetNumXTicks() + */ + function SetNumHorizTicks($n) + { + return $this->SetNumXTicks($n); + } + + /* + * Deprecated - use SetNumYTicks() + */ + function SetNumVertTicks($n) + { + return $this->SetNumYTicks($n); + } + + /* + * Deprecated - use SetXTickIncrement() + */ + function SetHorizTickIncrement($inc) + { + return $this->SetXTickIncrement($inc); + } + + /* + * Deprecated - use SetYTickIncrement() + */ + function SetVertTickIncrement($inc) + { + return $this->SetYTickIncrement($inc); + } + + /* + * Deprecated - use SetYTickPos() + */ + function SetVertTickPosition($which_tp) + { + return $this->SetYTickPos($which_tp); + } + + /* + * Deprecated - use SetXTickPos() + */ + function SetHorizTickPosition($which_tp) + { + return $this->SetXTickPos($which_tp); + } + + /* + * Deprecated - use SetFont() + */ + function SetTitleFontSize($which_size) + { + return $this->SetFont('title', $which_size); + } + + /* + * Deprecated - use SetFont() + */ + function SetAxisFontSize($which_size) + { + $this->SetFont('x_label', $which_size); + $this->SetFont('y_label', $which_size); + } + + /* + * Deprecated - use SetFont() + */ + function SetSmallFontSize($which_size) + { + return $this->SetFont('generic', $which_size); + } + + /* + * Deprecated - use SetFont() + */ + function SetXLabelFontSize($which_size) + { + return $this->SetFont('x_title', $which_size); + } + + /* + * Deprecated - use SetFont() + */ + function SetYLabelFontSize($which_size) + { + return $this->SetFont('y_title', $which_size); + } + + /* + * Deprecated - use SetXTitle() + */ + function SetXLabel($which_xlab) + { + return $this->SetXTitle($which_xlab); + } + + /* + * Deprecated - use SetYTitle() + */ + function SetYLabel($which_ylab) + { + return $this->SetYTitle($which_ylab); + } + + /* + * Deprecated - use SetXTickLength() and SetYTickLength() instead. + */ + function SetTickLength($which_tl) + { + $this->SetXTickLength($which_tl); + $this->SetYTickLength($which_tl); + return TRUE; + } + + /* + * Deprecated - use SetYLabelType() + */ + function SetYGridLabelType($which_yglt) + { + return $this->SetYLabelType($which_yglt); + } + + /* + * Deprecated - use SetXLabelType() + */ + function SetXGridLabelType($which_xglt) + { + return $this->SetXLabelType($which_xglt); + } + /* + * Deprecated - use SetYTickLabelPos() + */ + function SetYGridLabelPos($which_yglp) + { + return $this->SetYTickLabelPos($which_yglp); + } + /* + * Deprecated - use SetXTickLabelPos() + */ + function SetXGridLabelPos($which_xglp) + { + return $this->SetXTickLabelPos($which_xglp); + } + + /* + * Deprecated - use SetXtitle() + */ + function SetXTitlePos($xpos) + { + $this->x_title_pos = $xpos; + return TRUE; + } + + /* + * Deprecated - use SetYTitle() + */ + function SetYTitlePos($xpos) + { + $this->y_title_pos = $xpos; + return TRUE; + } + + /* + * Deprecated - use SetXDataLabelPos() + */ + function SetDrawXDataLabels($which_dxdl) + { + if ($which_dxdl == '1' ) + $this->SetXDataLabelPos('plotdown'); + else + $this->SetXDataLabelPos('none'); + } + + /* + * Deprecated - use SetPlotAreaPixels() + */ + function SetNewPlotAreaPixels($x1, $y1, $x2, $y2) + { + return $this->SetPlotAreaPixels($x1, $y1, $x2, $y2); + } + + /* + * Deprecated - use SetLineWidths(). + */ + function SetLineWidth($which_lw) + { + + $this->SetLineWidths($which_lw); + + if (!$this->error_bar_line_width) { + $this->SetErrorBarLineWidth($which_lw); + } + return TRUE; + } + + /* + * Deprecated - use SetPointShapes(). + */ + function SetPointShape($which_pt) + { + $this->SetPointShapes($which_pt); + return TRUE; + } + + /* + * Deprecated - use SetPointSizes(). + */ + function SetPointSize($which_ps) + { + $this->SetPointSizes($which_ps); + return TRUE; + } +} + +/* + * The PHPlot_truecolor class extends PHPlot to use GD truecolor images. + */ + +class PHPlot_truecolor extends PHPlot +{ + /* + * PHPlot Truecolor variation constructor: Create a PHPlot_truecolor object and initialize it. + * Note this does NOT call the parent (PHPlot) constructor. It duplicates the code here. + * Everything is the same as the PHPlot constructor except for imagecreatetruecolor. + * + * Parameters are the same as PHPlot: + * $which_width : Image width in pixels. + * $which_height : Image height in pixels. + * $which_output_file : Filename for output. + * $which_input_file : Path to a file to be used as background. + */ + function __construct($which_width=600, $which_height=400, $which_output_file=NULL, $which_input_file=NULL) + { + $this->SetRGBArray($this->color_array); + + if ($which_output_file) + $this->SetOutputFile($which_output_file); + + if ($which_input_file) { + $this->SetInputFile($which_input_file); + } else { + $this->image_width = $which_width; + $this->image_height = $which_height; + + $this->img = imagecreatetruecolor($this->image_width, $this->image_height); + if (! $this->img) + return $this->PrintError('PHPlot_truecolor(): Could not create image resource.'); + } + + $this->SetDefaultStyles(); + $this->SetDefaultFonts(); + } +} diff --git a/includes/phpmailer/README b/includes/phpmailer/README new file mode 100644 index 0000000..de5876f --- /dev/null +++ b/includes/phpmailer/README @@ -0,0 +1,102 @@ +PHPMailer +Full Featured Email Transfer Class for PHP +========================================== + +http://phpmailer.sourceforge.net/ + +This software is licenced under the LGPL. Please read LICENSE for information on the +software availability and distribution. + +Class Features: +- Send emails with multiple TOs, CCs, BCCs and REPLY-TOs +- Redundant SMTP servers +- Multipart/alternative emails for mail clients that do not read HTML email +- Support for 8bit, base64, binary, and quoted-printable encoding +- Uses the same methods as the very popular AspEmail active server (COM) component +- SMTP authentication +- Native language support +- Word wrap, and more! + +Why you might need it: + +Many PHP developers utilize email in their code. The only PHP function +that supports this is the mail() function. However, it does not expose +any of the popular features that many email clients use nowadays like +HTML-based emails and attachments. There are two proprietary +development tools out there that have all the functionality built into +easy to use classes: AspEmail(tm) and AspMail. Both of these +programs are COM components only available on Windows. They are also a +little pricey for smaller projects. + +Since I do Linux development I’ve missed these tools for my PHP coding. +So I built a version myself that implements the same methods (object +calls) that the Windows-based components do. It is open source and the +LGPL license allows you to place the class in your proprietary PHP +projects. + + +Installation: + +Copy class.phpmailer.php into your php.ini include_path. If you are +using the SMTP mailer then place class.smtp.php in your path as well. +In the language directory you will find several files like +phpmailer.lang-en.php. If you look right before the .php extension +that there are two letters. These represent the language type of the +translation file. For instance "en" is the English file and "br" is +the Portuguese file. Chose the file that best fits with your language +and place it in the PHP include path. If your language is English +then you have nothing more to do. If it is a different language then +you must point PHPMailer to the correct translation. To do this, call +the PHPMailer SetLanguage method like so: + +// To load the Portuguese version +$mail->SetLanguage("br", "/optional/path/to/language/directory/"); + +That's it. You should now be ready to use PHPMailer! + + +A Simple Example: + +IsSMTP(); // set mailer to use SMTP +$mail->Host = "smtp1.example.com;smtp2.example.com"; // specify main and backup server +$mail->SMTPAuth = true; // turn on SMTP authentication +$mail->Username = "jswan"; // SMTP username +$mail->Password = "secret"; // SMTP password + +$mail->From = "from@example.com"; +$mail->FromName = "Mailer"; +$mail->AddAddress("josh@example.net", "Josh Adams"); +$mail->AddAddress("ellen@example.com"); // name is optional +$mail->AddReplyTo("info@example.com", "Information"); + +$mail->WordWrap = 50; // set word wrap to 50 characters +$mail->AddAttachment("/var/tmp/file.tar.gz"); // add attachments +$mail->AddAttachment("/tmp/image.jpg", "new.jpg"); // optional name +$mail->IsHTML(true); // set email format to HTML + +$mail->Subject = "Here is the subject"; +$mail->Body = "This is the HTML message body in bold!"; +$mail->AltBody = "This is the body in plain text for non-HTML mail clients"; + +if(!$mail->Send()) +{ + echo "Message could not be sent.

    "; + echo "Mailer Error: " . $mail->ErrorInfo; + exit; +} + +echo "Message has been sent"; +?> + +CHANGELOG + +See ChangeLog.txt + +Download: http://sourceforge.net/project/showfiles.php?group_id=26031 + +Brent R. Matzelle diff --git a/includes/phpmailer/class.phpmailer.php b/includes/phpmailer/class.phpmailer.php new file mode 100644 index 0000000..014a8d3 --- /dev/null +++ b/includes/phpmailer/class.phpmailer.php @@ -0,0 +1,1721 @@ +ContentType = 'text/html'; + } else { + $this->ContentType = 'text/plain'; + } + } + + /** + * Sets Mailer to send message using SMTP. + * @return void + */ + function IsSMTP() { + $this->Mailer = 'smtp'; + } + + /** + * Sets Mailer to send message using PHP mail() function. + * @return void + */ + function IsMail() { + $this->Mailer = 'mail'; + } + + /** + * Sets Mailer to send message using the $Sendmail program. + * @return void + */ + function IsSendmail() { + $this->Mailer = 'sendmail'; + } + + /** + * Sets Mailer to send message using the qmail MTA. + * @return void + */ + function IsQmail() { + $this->Sendmail = '/var/qmail/bin/sendmail'; + $this->Mailer = 'sendmail'; + } + + ///////////////////////////////////////////////// + // METHODS, RECIPIENTS + ///////////////////////////////////////////////// + + /** + * Adds a "To" address. + * @param string $address + * @param string $name + * @return void + */ + function AddAddress($address, $name = '') { + $cur = count($this->to); + $this->to[$cur][0] = trim($address); + $this->to[$cur][1] = $name; + } + + /** + * Adds a "Cc" address. Note: this function works + * with the SMTP mailer on win32, not with the "mail" + * mailer. + * @param string $address + * @param string $name + * @return void + */ + function AddCC($address, $name = '') { + $cur = count($this->cc); + $this->cc[$cur][0] = trim($address); + $this->cc[$cur][1] = $name; + } + + /** + * Adds a "Bcc" address. Note: this function works + * with the SMTP mailer on win32, not with the "mail" + * mailer. + * @param string $address + * @param string $name + * @return void + */ + function AddBCC($address, $name = '') { + $cur = count($this->bcc); + $this->bcc[$cur][0] = trim($address); + $this->bcc[$cur][1] = $name; + } + + /** + * Adds a "Reply-To" address. + * @param string $address + * @param string $name + * @return void + */ + function AddReplyTo($address, $name = '') { + $cur = count($this->ReplyTo); + $this->ReplyTo[$cur][0] = trim($address); + $this->ReplyTo[$cur][1] = $name; + } + + ///////////////////////////////////////////////// + // METHODS, MAIL SENDING + ///////////////////////////////////////////////// + + /** + * Creates message and assigns Mailer. If the message is + * not sent successfully then it returns false. Use the ErrorInfo + * variable to view description of the error. + * @return bool + */ + function Send() { + $header = ''; + $body = ''; + $result = true; + + if((count($this->to) + count($this->cc) + count($this->bcc)) < 1) { + $this->SetError($this->Lang('provide_address')); + return false; + } + + /* Set whether the message is multipart/alternative */ + if(!empty($this->AltBody)) { + $this->ContentType = 'multipart/alternative'; + } + + $this->error_count = 0; // reset errors + $this->SetMessageType(); + $header .= $this->CreateHeader(); + $body = $this->CreateBody(); + + if($body == '') { + return false; + } + + /* Choose the mailer */ + switch($this->Mailer) { + case 'sendmail': + $result = $this->SendmailSend($header, $body); + break; + case 'smtp': + $result = $this->SmtpSend($header, $body); + break; + case 'mail': + $result = $this->MailSend($header, $body); + break; + default: + $result = $this->MailSend($header, $body); + break; + //$this->SetError($this->Mailer . $this->Lang('mailer_not_supported')); + //$result = false; + //break; + } + + return $result; + } + + /** + * Sends mail using the $Sendmail program. + * @access private + * @return bool + */ + function SendmailSend($header, $body) { + if ($this->Sender != '') { + $sendmail = sprintf("%s -oi -f %s -t", escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender)); + } else { + $sendmail = sprintf("%s -oi -t", escapeshellcmd($this->Sendmail)); + } + + if(!@$mail = popen($sendmail, 'w')) { + $this->SetError($this->Lang('execute') . $this->Sendmail); + return false; + } + + fputs($mail, $header); + fputs($mail, $body); + + $result = pclose($mail) >> 8 & 0xFF; + if($result != 0) { + $this->SetError($this->Lang('execute') . $this->Sendmail); + return false; + } + + return true; + } + + /** + * Sends mail using the PHP mail() function. + * @access private + * @return bool + */ + function MailSend($header, $body) { + + $to = ''; + for($i = 0; $i < count($this->to); $i++) { + if($i != 0) { $to .= ', '; } + $to .= $this->AddrFormat($this->to[$i]); + } + + $toArr = split(',', $to); + + if ($this->Sender != '' && strlen(ini_get('safe_mode'))< 1) { + $old_from = ini_get('sendmail_from'); + ini_set('sendmail_from', $this->Sender); + $params = sprintf("-oi -f %s", $this->Sender); + if ($this->SingleTo === true && count($toArr) > 1) { + foreach ($toArr as $key => $val) { + $rt = @mail($val, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params); + } + } else { + $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params); + } + } else { + if ($this->SingleTo === true && count($toArr) > 1) { + foreach ($toArr as $key => $val) { + $rt = @mail($val, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params); + } + } else { + $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header); + } + } + + if (isset($old_from)) { + ini_set('sendmail_from', $old_from); + } + + if(!$rt) { + $this->SetError($this->Lang('instantiate')); + return false; + } + + return true; + } + + /** + * Sends mail via SMTP using PhpSMTP (Author: + * Chris Ryan). Returns bool. Returns false if there is a + * bad MAIL FROM, RCPT, or DATA input. + * @access private + * @return bool + */ + function SmtpSend($header, $body) { + include_once($this->PluginDir . 'class.smtp.php'); + $error = ''; + $bad_rcpt = array(); + + if(!$this->SmtpConnect()) { + return false; + } + + $smtp_from = ($this->Sender == '') ? $this->From : $this->Sender; + if(!$this->smtp->Mail($smtp_from)) { + $error = $this->Lang('from_failed') . $smtp_from; + $this->SetError($error); + $this->smtp->Reset(); + return false; + } + + /* Attempt to send attach all recipients */ + for($i = 0; $i < count($this->to); $i++) { + if(!$this->smtp->Recipient($this->to[$i][0])) { + $bad_rcpt[] = $this->to[$i][0]; + } + } + for($i = 0; $i < count($this->cc); $i++) { + if(!$this->smtp->Recipient($this->cc[$i][0])) { + $bad_rcpt[] = $this->cc[$i][0]; + } + } + for($i = 0; $i < count($this->bcc); $i++) { + if(!$this->smtp->Recipient($this->bcc[$i][0])) { + $bad_rcpt[] = $this->bcc[$i][0]; + } + } + + if(count($bad_rcpt) > 0) { // Create error message + for($i = 0; $i < count($bad_rcpt); $i++) { + if($i != 0) { + $error .= ', '; + } + $error .= $bad_rcpt[$i]; + } + $error = $this->Lang('recipients_failed') . $error; + $this->SetError($error); + $this->smtp->Reset(); + return false; + } + + if(!$this->smtp->Data($header . $body)) { + $this->SetError($this->Lang('data_not_accepted')); + $this->smtp->Reset(); + return false; + } + if($this->SMTPKeepAlive == true) { + $this->smtp->Reset(); + } else { + $this->SmtpClose(); + } + + return true; + } + + /** + * Initiates a connection to an SMTP server. Returns false if the + * operation failed. + * @access private + * @return bool + */ + function SmtpConnect() { + if($this->smtp == NULL) { + $this->smtp = new SMTP(); + } + + $this->smtp->do_debug = $this->SMTPDebug; + $hosts = explode(';', $this->Host); + $index = 0; + $connection = ($this->smtp->Connected()); + + /* Retry while there is no connection */ + while($index < count($hosts) && $connection == false) { + $hostinfo = array(); + if(preg_match('/^(.+):([0-9]+)$/i', $hosts[$index], $hostinfo)) { + $host = $hostinfo[1]; + $port = $hostinfo[2]; + } else { + $host = $hosts[$index]; + $port = $this->Port; + } + + if($this->smtp->Connect(((!empty($this->SMTPSecure))?$this->SMTPSecure.'://':'').$host, $port, $this->Timeout)) { + if ($this->Helo != '') { + $this->smtp->Hello($this->Helo); + } else { + $this->smtp->Hello($this->ServerHostname()); + } + + $connection = true; + if($this->SMTPAuth) { + if(!$this->smtp->Authenticate($this->Username, $this->Password)) { + $this->SetError($this->Lang('authenticate')); + $this->smtp->Reset(); + $connection = false; + } + } + } + $index++; + } + if(!$connection) { + $this->SetError($this->Lang('connect_host').$host); + } + + return $connection; + } + + /** + * Closes the active SMTP session if one exists. + * @return void + */ + function SmtpClose() { + if($this->smtp != NULL) { + if($this->smtp->Connected()) { + $this->smtp->Quit(); + $this->smtp->Close(); + } + } + } + + /** + * Sets the language for all class error messages. Returns false + * if it cannot load the language file. The default language type + * is English. + * @param string $lang_type Type of language (e.g. Portuguese: "br") + * @param string $lang_path Path to the language file directory + * @access public + * @return bool + */ + function SetLanguage($lang_type, $lang_path = 'language/') { + if(file_exists($lang_path.'phpmailer.lang-'.$lang_type.'.php')) { + include($lang_path.'phpmailer.lang-'.$lang_type.'.php'); + } elseif (file_exists($lang_path.'phpmailer.lang-en.php')) { + include($lang_path.'phpmailer.lang-en.php'); + } else { + $this->SetError('Could not load language file'); + return false; + } + $this->language = $PHPMAILER_LANG; + + return true; + } + + ///////////////////////////////////////////////// + // METHODS, MESSAGE CREATION + ///////////////////////////////////////////////// + + /** + * Creates recipient headers. + * @access private + * @return string + */ + function AddrAppend($type, $addr) { + $addr_str = $type . ': '; + $addr_str .= $this->AddrFormat($addr[0]); + if(count($addr) > 1) { + for($i = 1; $i < count($addr); $i++) { + $addr_str .= ', ' . $this->AddrFormat($addr[$i]); + } + } + $addr_str .= $this->LE; + + return $addr_str; + } + + /** + * Formats an address correctly. + * @access private + * @return string + */ + function AddrFormat($addr) { + if(empty($addr[1])) { + $formatted = $this->SecureHeader($addr[0]); + } else { + $formatted = $this->EncodeHeader($this->SecureHeader($addr[1]), 'phrase') . " <" . $this->SecureHeader($addr[0]) . ">"; + } + + return $formatted; + } + + /** + * Wraps message for use with mailers that do not + * automatically perform wrapping and for quoted-printable. + * Original written by philippe. + * @access private + * @return string + */ + function WrapText($message, $length, $qp_mode = false) { + $soft_break = ($qp_mode) ? sprintf(" =%s", $this->LE) : $this->LE; + + $message = $this->FixEOL($message); + if (substr($message, -1) == $this->LE) { + $message = substr($message, 0, -1); + } + + $line = explode($this->LE, $message); + $message = ''; + for ($i=0 ;$i < count($line); $i++) { + $line_part = explode(' ', $line[$i]); + $buf = ''; + for ($e = 0; $e $length)) { + $space_left = $length - strlen($buf) - 1; + if ($e != 0) { + if ($space_left > 20) { + $len = $space_left; + if (substr($word, $len - 1, 1) == '=') { + $len--; + } elseif (substr($word, $len - 2, 1) == '=') { + $len -= 2; + } + $part = substr($word, 0, $len); + $word = substr($word, $len); + $buf .= ' ' . $part; + $message .= $buf . sprintf("=%s", $this->LE); + } else { + $message .= $buf . $soft_break; + } + $buf = ''; + } + while (strlen($word) > 0) { + $len = $length; + if (substr($word, $len - 1, 1) == '=') { + $len--; + } elseif (substr($word, $len - 2, 1) == '=') { + $len -= 2; + } + $part = substr($word, 0, $len); + $word = substr($word, $len); + + if (strlen($word) > 0) { + $message .= $part . sprintf("=%s", $this->LE); + } else { + $buf = $part; + } + } + } else { + $buf_o = $buf; + $buf .= ($e == 0) ? $word : (' ' . $word); + + if (strlen($buf) > $length and $buf_o != '') { + $message .= $buf_o . $soft_break; + $buf = $word; + } + } + } + $message .= $buf . $this->LE; + } + + return $message; + } + + /** + * Set the body wrapping. + * @access private + * @return void + */ + function SetWordWrap() { + if($this->WordWrap < 1) { + return; + } + + switch($this->message_type) { + case 'alt': + /* fall through */ + case 'alt_attachments': + $this->AltBody = $this->WrapText($this->AltBody, $this->WordWrap); + break; + default: + $this->Body = $this->WrapText($this->Body, $this->WordWrap); + break; + } + } + + /** + * Assembles message header. + * @access private + * @return string + */ + function CreateHeader() { + $result = ''; + + /* Set the boundaries */ + $uniq_id = md5(uniqid(time())); + $this->boundary[1] = 'b1_' . $uniq_id; + $this->boundary[2] = 'b2_' . $uniq_id; + + $result .= $this->HeaderLine('Date', $this->RFCDate()); + if($this->Sender == '') { + $result .= $this->HeaderLine('Return-Path', trim($this->From)); + } else { + $result .= $this->HeaderLine('Return-Path', trim($this->Sender)); + } + + /* To be created automatically by mail() */ + if($this->Mailer != 'mail') { + if(count($this->to) > 0) { + $result .= $this->AddrAppend('To', $this->to); + } elseif (count($this->cc) == 0) { + $result .= $this->HeaderLine('To', 'undisclosed-recipients:;'); + } + if(count($this->cc) > 0) { + $result .= $this->AddrAppend('Cc', $this->cc); + } + } + + $from = array(); + $from[0][0] = trim($this->From); + $from[0][1] = $this->FromName; + $result .= $this->AddrAppend('From', $from); + + /* sendmail and mail() extract Cc from the header before sending */ + if((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (count($this->cc) > 0)) { + $result .= $this->AddrAppend('Cc', $this->cc); + } + + /* sendmail and mail() extract Bcc from the header before sending */ + if((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (count($this->bcc) > 0)) { + $result .= $this->AddrAppend('Bcc', $this->bcc); + } + + if(count($this->ReplyTo) > 0) { + $result .= $this->AddrAppend('Reply-To', $this->ReplyTo); + } + + /* mail() sets the subject itself */ + if($this->Mailer != 'mail') { + $result .= $this->HeaderLine('Subject', $this->EncodeHeader($this->SecureHeader($this->Subject))); + } + +// {{{ BIT_MOD + $this->MessageID = '<'.$uniq_id.'@'.$this->ServerHostname().'>'; + $result .= sprintf("Message-ID: %s%s", $this->MessageID, $this->LE); +// }}} BIT_MOD + $result .= $this->HeaderLine('X-Priority', $this->Priority); + $result .= $this->HeaderLine('X-Mailer', 'PHPMailer (phpmailer.sourceforge.net) [version ' . $this->Version . ']'); + + if($this->ConfirmReadingTo != '') { + $result .= $this->HeaderLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>'); + } + + // Add custom headers + for($index = 0; $index < count($this->CustomHeader); $index++) { + $result .= $this->HeaderLine(trim($this->CustomHeader[$index][0]), $this->EncodeHeader(trim($this->CustomHeader[$index][1]))); + } + $result .= $this->HeaderLine('MIME-Version', '1.0'); + + switch($this->message_type) { + case 'plain': + $result .= $this->HeaderLine('Content-Transfer-Encoding', $this->Encoding); + $result .= sprintf("Content-Type: %s; charset=\"%s\"", $this->ContentType, $this->CharSet); + break; + case 'attachments': + /* fall through */ + case 'alt_attachments': + if($this->InlineImageExists()){ + $result .= sprintf("Content-Type: %s;%s\ttype=\"text/html\";%s\tboundary=\"%s\"%s", 'multipart/related', $this->LE, $this->LE, $this->boundary[1], $this->LE); + } else { + $result .= $this->HeaderLine('Content-Type', 'multipart/mixed;'); + $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"'); + } + break; + case 'alt': + $result .= $this->HeaderLine('Content-Type', 'multipart/alternative;'); + $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"'); + break; + } + + if($this->Mailer != 'mail') { + $result .= $this->LE.$this->LE; + } + + return $result; + } + + /** + * Assembles the message body. Returns an empty string on failure. + * @access private + * @return string + */ + function CreateBody() { + $result = ''; + + $this->SetWordWrap(); + + switch($this->message_type) { + case 'alt': + $result .= $this->GetBoundary($this->boundary[1], '', 'text/plain', ''); + $result .= $this->EncodeString($this->AltBody, $this->Encoding); + $result .= $this->LE.$this->LE; + $result .= $this->GetBoundary($this->boundary[1], '', 'text/html', ''); + $result .= $this->EncodeString($this->Body, $this->Encoding); + $result .= $this->LE.$this->LE; + $result .= $this->EndBoundary($this->boundary[1]); + break; + case 'plain': + $result .= $this->EncodeString($this->Body, $this->Encoding); + break; + case 'attachments': + $result .= $this->GetBoundary($this->boundary[1], '', '', ''); + $result .= $this->EncodeString($this->Body, $this->Encoding); + $result .= $this->LE; + $result .= $this->AttachAll(); + break; + case 'alt_attachments': + $result .= sprintf("--%s%s", $this->boundary[1], $this->LE); + $result .= sprintf("Content-Type: %s;%s" . "\tboundary=\"%s\"%s", 'multipart/alternative', $this->LE, $this->boundary[2], $this->LE.$this->LE); + $result .= $this->GetBoundary($this->boundary[2], '', 'text/plain', '') . $this->LE; // Create text body + $result .= $this->EncodeString($this->AltBody, $this->Encoding); + $result .= $this->LE.$this->LE; + $result .= $this->GetBoundary($this->boundary[2], '', 'text/html', '') . $this->LE; // Create the HTML body + $result .= $this->EncodeString($this->Body, $this->Encoding); + $result .= $this->LE.$this->LE; + $result .= $this->EndBoundary($this->boundary[2]); + $result .= $this->AttachAll(); + break; + } + if($this->IsError()) { + $result = ''; + } + + return $result; + } + + /** + * Returns the start of a message boundary. + * @access private + */ + function GetBoundary($boundary, $charSet, $contentType, $encoding) { + $result = ''; + if($charSet == '') { + $charSet = $this->CharSet; + } + if($contentType == '') { + $contentType = $this->ContentType; + } + if($encoding == '') { + $encoding = $this->Encoding; + } + $result .= $this->TextLine('--' . $boundary); + $result .= sprintf("Content-Type: %s; charset = \"%s\"", $contentType, $charSet); + $result .= $this->LE; + $result .= $this->HeaderLine('Content-Transfer-Encoding', $encoding); + $result .= $this->LE; + + return $result; + } + + /** + * Returns the end of a message boundary. + * @access private + */ + function EndBoundary($boundary) { + return $this->LE . '--' . $boundary . '--' . $this->LE; + } + + /** + * Sets the message type. + * @access private + * @return void + */ + function SetMessageType() { + if(count($this->attachment) < 1 && strlen($this->AltBody) < 1) { + $this->message_type = 'plain'; + } else { + if(count($this->attachment) > 0) { + $this->message_type = 'attachments'; + } + if(strlen($this->AltBody) > 0 && count($this->attachment) < 1) { + $this->message_type = 'alt'; + } + if(strlen($this->AltBody) > 0 && count($this->attachment) > 0) { + $this->message_type = 'alt_attachments'; + } + } + } + + /* Returns a formatted header line. + * @access private + * @return string + */ + function HeaderLine($name, $value) { + return $name . ': ' . $value . $this->LE; + } + + /** + * Returns a formatted mail line. + * @access private + * @return string + */ + function TextLine($value) { + return $value . $this->LE; + } + + ///////////////////////////////////////////////// + // CLASS METHODS, ATTACHMENTS + ///////////////////////////////////////////////// + + /** + * Adds an attachment from a path on the filesystem. + * Returns false if the file could not be found + * or accessed. + * @param string $path Path to the attachment. + * @param string $name Overrides the attachment name. + * @param string $encoding File encoding (see $Encoding). + * @param string $type File extension (MIME) type. + * @return bool + */ + function AddAttachment($path, $name = '', $encoding = 'base64', $type = 'application/octet-stream') { + if(!@is_file($path)) { + $this->SetError($this->Lang('file_access') . $path); + return false; + } + + $filename = basename($path); + if($name == '') { + $name = $filename; + } + + $cur = count($this->attachment); + $this->attachment[$cur][0] = $path; + $this->attachment[$cur][1] = $filename; + $this->attachment[$cur][2] = $name; + $this->attachment[$cur][3] = $encoding; + $this->attachment[$cur][4] = $type; + $this->attachment[$cur][5] = false; // isStringAttachment + $this->attachment[$cur][6] = 'attachment'; + $this->attachment[$cur][7] = 0; + + return true; + } + + /** + * Attaches all fs, string, and binary attachments to the message. + * Returns an empty string on failure. + * @access private + * @return string + */ + function AttachAll() { + /* Return text of body */ + $mime = array(); + + /* Add all attachments */ + for($i = 0; $i < count($this->attachment); $i++) { + /* Check for string attachment */ + $bString = $this->attachment[$i][5]; + if ($bString) { + $string = $this->attachment[$i][0]; + } else { + $path = $this->attachment[$i][0]; + } + + $filename = $this->attachment[$i][1]; + $name = $this->attachment[$i][2]; + $encoding = $this->attachment[$i][3]; + $type = $this->attachment[$i][4]; + $disposition = $this->attachment[$i][6]; + $cid = $this->attachment[$i][7]; + + $mime[] = sprintf("--%s%s", $this->boundary[1], $this->LE); + $mime[] = sprintf("Content-Type: %s; name=\"%s\"%s", $type, $name, $this->LE); + $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE); + + if($disposition == 'inline') { + $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE); + } + + $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", $disposition, $name, $this->LE.$this->LE); + + /* Encode as string attachment */ + if($bString) { + $mime[] = $this->EncodeString($string, $encoding); + if($this->IsError()) { + return ''; + } + $mime[] = $this->LE.$this->LE; + } else { + $mime[] = $this->EncodeFile($path, $encoding); + if($this->IsError()) { + return ''; + } + $mime[] = $this->LE.$this->LE; + } + } + + $mime[] = sprintf("--%s--%s", $this->boundary[1], $this->LE); + + return join('', $mime); + } + + /** + * Encodes attachment in requested format. Returns an + * empty string on failure. + * @access private + * @return string + */ + function EncodeFile ($path, $encoding = 'base64') { + if(!@$fd = fopen($path, 'rb')) { + $this->SetError($this->Lang('file_open') . $path); + return ''; + } + $magic_quotes = get_magic_quotes_runtime(); + set_magic_quotes_runtime(0); + $file_buffer = fread($fd, filesize($path)); + $file_buffer = $this->EncodeString($file_buffer, $encoding); + fclose($fd); + set_magic_quotes_runtime($magic_quotes); + + return $file_buffer; + } + + /** + * Encodes string to requested format. Returns an + * empty string on failure. + * @access private + * @return string + */ + function EncodeString ($str, $encoding = 'base64') { + $encoded = ''; + switch(strtolower($encoding)) { + case 'base64': + /* chunk_split is found in PHP >= 3.0.6 */ + $encoded = chunk_split(base64_encode($str), 76, $this->LE); + break; + case '7bit': + case '8bit': + $encoded = $this->FixEOL($str); + if (substr($encoded, -(strlen($this->LE))) != $this->LE) + $encoded .= $this->LE; + break; + case 'binary': + $encoded = $str; + break; + case 'quoted-printable': + $encoded = $this->EncodeQP($str); + break; + default: + $this->SetError($this->Lang('encoding') . $encoding); + break; + } + return $encoded; + } + + /** + * Encode a header string to best of Q, B, quoted or none. + * @access private + * @return string + */ + function EncodeHeader ($str, $position = 'text') { + $x = 0; + + switch (strtolower($position)) { + case 'phrase': + if (!preg_match('/[\200-\377]/', $str)) { + /* Can't use addslashes as we don't know what value has magic_quotes_sybase. */ + $encoded = addcslashes($str, "\0..\37\177\\\""); + if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) { + return ($encoded); + } else { + return ("\"$encoded\""); + } + } + $x = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches); + break; + case 'comment': + $x = preg_match_all('/[()"]/', $str, $matches); + /* Fall-through */ + case 'text': + default: + $x += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches); + break; + } + + if ($x == 0) { + return ($str); + } + + $maxlen = 75 - 7 - strlen($this->CharSet); + /* Try to select the encoding which should produce the shortest output */ + if (strlen($str)/3 < $x) { + $encoding = 'B'; + $encoded = base64_encode($str); + $maxlen -= $maxlen % 4; + $encoded = trim(chunk_split($encoded, $maxlen, "\n")); + } else { + $encoding = 'Q'; + $encoded = $this->EncodeQ($str, $position); + $encoded = $this->WrapText($encoded, $maxlen, true); + $encoded = str_replace('='.$this->LE, "\n", trim($encoded)); + } + + $encoded = preg_replace('/^(.*)$/m', " =?".$this->CharSet."?$encoding?\\1?=", $encoded); + $encoded = trim(str_replace("\n", $this->LE, $encoded)); + + return $encoded; + } + + /** + * Encode string to quoted-printable. + * @access private + * @return string + */ + function EncodeQP( $input = '', $line_max = 76, $space_conv = false ) { + $hex = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'); + $lines = preg_split('/(?:\r\n|\r|\n)/', $input); + $eol = "\r\n"; + $escape = '='; + $output = ''; + while( list(, $line) = each($lines) ) { + $linlen = strlen($line); + $newline = ''; + for($i = 0; $i < $linlen; $i++) { + $c = substr( $line, $i, 1 ); + $dec = ord( $c ); + if ( ( $i == 0 ) && ( $dec == 46 ) ) { // convert first point in the line into =2E + $c = '=2E'; + } + if ( $dec == 32 ) { + if ( $i == ( $linlen - 1 ) ) { // convert space at eol only + $c = '=20'; + } else if ( $space_conv ) { + $c = '=20'; + } + } elseif ( ($dec == 61) || ($dec < 32 ) || ($dec > 126) ) { // always encode "\t", which is *not* required + $h2 = floor($dec/16); + $h1 = floor($dec%16); + $c = $escape.$hex[$h2].$hex[$h1]; + } + if ( (strlen($newline) + strlen($c)) >= $line_max ) { // CRLF is not counted + $output .= $newline.$escape.$eol; // soft line break; " =\r\n" is okay + $newline = ''; + // check if newline first character will be point or not + if ( $dec == 46 ) { + $c = '=2E'; + } + } + $newline .= $c; + } // end of for + $output .= $newline.$eol; + } // end of while + return trim($output); + } + + /** + * Encode string to q encoding. + * @access private + * @return string + */ + function EncodeQ ($str, $position = 'text') { + /* There should not be any EOL in the string */ + $encoded = preg_replace("[\r\n]", '', $str); + + switch (strtolower($position)) { + case 'phrase': + $encoded = preg_replace("/([^A-Za-z0-9!*+\/ -])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded); + break; + case 'comment': + $encoded = preg_replace("/([\(\)\"])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded); + case 'text': + default: + /* Replace every high ascii, control =, ? and _ characters */ + $encoded = preg_replace('/([\000-\011\013\014\016-\037\075\077\137\177-\377])/e', + "'='.sprintf('%02X', ord('\\1'))", $encoded); + break; + } + + /* Replace every spaces to _ (more readable than =20) */ + $encoded = str_replace(' ', '_', $encoded); + + return $encoded; + } + + /** + * Adds a string or binary attachment (non-filesystem) to the list. + * This method can be used to attach ascii or binary data, + * such as a BLOB record from a database. + * @param string $string String attachment data. + * @param string $filename Name of the attachment. + * @param string $encoding File encoding (see $Encoding). + * @param string $type File extension (MIME) type. + * @return void + */ + function AddStringAttachment($string, $filename, $encoding = 'base64', $type = 'application/octet-stream') { + /* Append to $attachment array */ + $cur = count($this->attachment); + $this->attachment[$cur][0] = $string; + $this->attachment[$cur][1] = $filename; + $this->attachment[$cur][2] = $filename; + $this->attachment[$cur][3] = $encoding; + $this->attachment[$cur][4] = $type; + $this->attachment[$cur][5] = true; // isString + $this->attachment[$cur][6] = 'attachment'; + $this->attachment[$cur][7] = 0; + } + + /** + * Adds an embedded attachment. This can include images, sounds, and + * just about any other document. Make sure to set the $type to an + * image type. For JPEG images use "image/jpeg" and for GIF images + * use "image/gif". + * @param string $path Path to the attachment. + * @param string $cid Content ID of the attachment. Use this to identify + * the Id for accessing the image in an HTML form. + * @param string $name Overrides the attachment name. + * @param string $encoding File encoding (see $Encoding). + * @param string $type File extension (MIME) type. + * @return bool + */ + function AddEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = 'application/octet-stream') { + + if(!@is_file($path)) { + $this->SetError($this->Lang('file_access') . $path); + return false; + } + + $filename = basename($path); + if($name == '') { + $name = $filename; + } + + /* Append to $attachment array */ + $cur = count($this->attachment); + $this->attachment[$cur][0] = $path; + $this->attachment[$cur][1] = $filename; + $this->attachment[$cur][2] = $name; + $this->attachment[$cur][3] = $encoding; + $this->attachment[$cur][4] = $type; + $this->attachment[$cur][5] = false; + $this->attachment[$cur][6] = 'inline'; + $this->attachment[$cur][7] = $cid; + + return true; + } + + /** + * Returns true if an inline attachment is present. + * @access private + * @return bool + */ + function InlineImageExists() { + $result = false; + for($i = 0; $i < count($this->attachment); $i++) { + if($this->attachment[$i][6] == 'inline') { + $result = true; + break; + } + } + + return $result; + } + + ///////////////////////////////////////////////// + // CLASS METHODS, MESSAGE RESET + ///////////////////////////////////////////////// + + /** + * Clears all recipients assigned in the TO array. Returns void. + * @return void + */ + function ClearAddresses() { + $this->to = array(); + } + + /** + * Clears all recipients assigned in the CC array. Returns void. + * @return void + */ + function ClearCCs() { + $this->cc = array(); + } + + /** + * Clears all recipients assigned in the BCC array. Returns void. + * @return void + */ + function ClearBCCs() { + $this->bcc = array(); + } + + /** + * Clears all recipients assigned in the ReplyTo array. Returns void. + * @return void + */ + function ClearReplyTos() { + $this->ReplyTo = array(); + } + + /** + * Clears all recipients assigned in the TO, CC and BCC + * array. Returns void. + * @return void + */ + function ClearAllRecipients() { + $this->to = array(); + $this->cc = array(); + $this->bcc = array(); + } + + /** + * Clears all previously set filesystem, string, and binary + * attachments. Returns void. + * @return void + */ + function ClearAttachments() { + $this->attachment = array(); + } + + /** + * Clears all custom headers. Returns void. + * @return void + */ + function ClearCustomHeaders() { + $this->CustomHeader = array(); + } + + ///////////////////////////////////////////////// + // CLASS METHODS, MISCELLANEOUS + ///////////////////////////////////////////////// + + /** + * Adds the error message to the error container. + * Returns void. + * @access private + * @return void + */ + function SetError($msg) { + $this->error_count++; + $this->ErrorInfo = $msg; + } + + /** + * Returns the proper RFC 822 formatted date. + * @access private + * @return string + */ + function RFCDate() { + $tz = date('Z'); + $tzs = ($tz < 0) ? '-' : '+'; + $tz = abs($tz); + $tz = (int)($tz/3600)*100 + ($tz%3600)/60; + $result = sprintf("%s %s%04d", date('D, j M Y H:i:s'), $tzs, $tz); + + return $result; + } + + /** + * Returns the appropriate server variable. Should work with both + * PHP 4.1.0+ as well as older versions. Returns an empty string + * if nothing is found. + * @access private + * @return mixed + */ + function ServerVar($varName) { + global $HTTP_SERVER_VARS; + global $HTTP_ENV_VARS; + + if(!isset($_SERVER)) { + $_SERVER = $HTTP_SERVER_VARS; + if(!isset($_SERVER['REMOTE_ADDR'])) { + $_SERVER = $HTTP_ENV_VARS; // must be Apache + } + } + + if(isset($_SERVER[$varName])) { + return $_SERVER[$varName]; + } else { + return ''; + } + } + + /** + * Returns the server hostname or 'localhost.localdomain' if unknown. + * @access private + * @return string + */ + function ServerHostname() { + if ($this->Hostname != '') { + $result = $this->Hostname; + } elseif ($this->ServerVar('SERVER_NAME') != '') { + $result = $this->ServerVar('SERVER_NAME'); + } else { + $result = 'localhost.localdomain'; + } + + return $result; + } + + /** + * Returns a message in the appropriate language. + * @access private + * @return string + */ + function Lang($key) { + if(count($this->language) < 1) { + $this->SetLanguage('en'); // set the default language + } + + if(isset($this->language[$key])) { + return $this->language[$key]; + } else { + return 'Language string failed to load: ' . $key; + } + } + + /** + * Returns true if an error occurred. + * @return bool + */ + function IsError() { + return ($this->error_count > 0); + } + + /** + * Changes every end of line from CR or LF to CRLF. + * @access private + * @return string + */ + function FixEOL($str) { + $str = str_replace("\r\n", "\n", $str); + $str = str_replace("\r", "\n", $str); + $str = str_replace("\n", $this->LE, $str); + return $str; + } + + /** + * Adds a custom header. + * @return void + */ + function AddCustomHeader($custom_header) { + $this->CustomHeader[] = explode(':', $custom_header, 2); + } + + /** + * Evaluates the message and returns modifications for inline images and backgrounds + * @access public + * @return $message + */ + function MsgHTML($message) { + preg_match_all("/(src|background)=\"(.*)\"/Ui", $message, $images); + if(isset($images[2])) { + foreach($images[2] as $i => $url) { + $filename = basename($url); + $directory = dirname($url); + $cid = 'cid:' . md5($filename); + $fileParts = split("\.", $filename); + $ext = $fileParts[1]; + $mimeType = $this->_mime_types($ext); + $message = preg_replace("/".$images[1][$i]."=\"".preg_quote($url, '/')."\"/Ui", $images[1][$i]."=\"".$cid."\"", $message); + $this->AddEmbeddedImage($url, md5($filename), $filename, 'base64', $mimeType); + } + } + $this->IsHTML(true); + $this->Body = $message; + $textMsg = trim(strip_tags($message)); + if ( !empty($textMsg) && empty($this->AltBody) ) { + $this->AltBody = $textMsg; + } + if ( empty($this->AltBody) ) { + $this->AltBody = 'To view this email message, open the email in with HTML compatibility!' . "\n\n"; + } + } + + /** + * Gets the mime type of the embedded or inline image + * @access private + * @return mime type of ext + */ + function _mime_types($ext = '') { + $mimes = array( + 'hqx' => 'application/mac-binhex40', + 'cpt' => 'application/mac-compactpro', + 'doc' => 'application/msword', + 'bin' => 'application/macbinary', + 'dms' => 'application/octet-stream', + 'lha' => 'application/octet-stream', + 'lzh' => 'application/octet-stream', + 'exe' => 'application/octet-stream', + 'class' => 'application/octet-stream', + 'psd' => 'application/octet-stream', + 'so' => 'application/octet-stream', + 'sea' => 'application/octet-stream', + 'dll' => 'application/octet-stream', + 'oda' => 'application/oda', + 'pdf' => 'application/pdf', + 'ai' => 'application/postscript', + 'eps' => 'application/postscript', + 'ps' => 'application/postscript', + 'smi' => 'application/smil', + 'smil' => 'application/smil', + 'mif' => 'application/vnd.mif', + 'xls' => 'application/vnd.ms-excel', + 'ppt' => 'application/vnd.ms-powerpoint', + 'wbxml' => 'application/vnd.wap.wbxml', + 'wmlc' => 'application/vnd.wap.wmlc', + 'dcr' => 'application/x-director', + 'dir' => 'application/x-director', + 'dxr' => 'application/x-director', + 'dvi' => 'application/x-dvi', + 'gtar' => 'application/x-gtar', + 'php' => 'application/x-httpd-php', + 'php4' => 'application/x-httpd-php', + 'php3' => 'application/x-httpd-php', + 'phtml' => 'application/x-httpd-php', + 'phps' => 'application/x-httpd-php-source', + 'js' => 'application/x-javascript', + 'swf' => 'application/x-shockwave-flash', + 'sit' => 'application/x-stuffit', + 'tar' => 'application/x-tar', + 'tgz' => 'application/x-tar', + 'xhtml' => 'application/xhtml+xml', + 'xht' => 'application/xhtml+xml', + 'zip' => 'application/zip', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mpga' => 'audio/mpeg', + 'mp2' => 'audio/mpeg', + 'mp3' => 'audio/mpeg', + 'aif' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'aifc' => 'audio/x-aiff', + 'ram' => 'audio/x-pn-realaudio', + 'rm' => 'audio/x-pn-realaudio', + 'rpm' => 'audio/x-pn-realaudio-plugin', + 'ra' => 'audio/x-realaudio', + 'rv' => 'video/vnd.rn-realvideo', + 'wav' => 'audio/x-wav', + 'bmp' => 'image/bmp', + 'gif' => 'image/gif', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'jpe' => 'image/jpeg', + 'png' => 'image/png', + 'tiff' => 'image/tiff', + 'tif' => 'image/tiff', + 'css' => 'text/css', + 'html' => 'text/html', + 'htm' => 'text/html', + 'shtml' => 'text/html', + 'txt' => 'text/plain', + 'text' => 'text/plain', + 'log' => 'text/plain', + 'rtx' => 'text/richtext', + 'rtf' => 'text/rtf', + 'xml' => 'text/xml', + 'xsl' => 'text/xml', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpe' => 'video/mpeg', + 'qt' => 'video/quicktime', + 'mov' => 'video/quicktime', + 'avi' => 'video/x-msvideo', + 'movie' => 'video/x-sgi-movie', + 'doc' => 'application/msword', + 'word' => 'application/msword', + 'xl' => 'application/excel', + 'eml' => 'message/rfc822' + ); + return ( ! isset($mimes[strtolower($ext)])) ? 'application/x-unknown-content-type' : $mimes[strtolower($ext)]; + } + + /** + * Set (or reset) Class Objects (variables) + * + * Usage Example: + * $page->set('X-Priority', '3'); + * + * @access public + * @param string $name Parameter Name + * @param mixed $value Parameter Value + * NOTE: will not work with arrays, there are no arrays to set/reset + */ + function set ( $name, $value = '' ) { + if ( isset($this->$name) ) { + $this->$name = $value; + } else { + $this->SetError('Cannot set or reset variable ' . $name); + return false; + } + } + + /** + * Read a file from a supplied filename and return it. + * + * @access public + * @param string $filename Parameter File Name + */ + function getFile($filename) { + $return = ''; + if ($fp = fopen($filename, 'rb')) { + while (!feof($fp)) { + $return .= fread($fp, 1024); + } + fclose($fp); + return $return; + } else { + return false; + } + } + + /** + * Strips newlines to prevent header injection. + * @access private + * @param string $str String + * @return string + */ + function SecureHeader($str) { + $str = trim($str); + $str = str_replace("\r", "", $str); + $str = str_replace("\n", "", $str); + return $str; + } + +} + +?> diff --git a/includes/phpmailer/class.pop3.php b/includes/phpmailer/class.pop3.php new file mode 100644 index 0000000..af1e63b --- /dev/null +++ b/includes/phpmailer/class.pop3.php @@ -0,0 +1,437 @@ +pop_conn = 0; + $this->connected = false; + $this->error = null; + } + + /** + * Combination of public events - connect, login, disconnect + * + * @param string $host + * @param integer $port + * @param integer $tval + * @param string $username + * @param string $password + */ + function Authorise ($host, $port = false, $tval = false, $username, $password, $debug_level = 0) + { + $this->host = $host; + + // If no port value is passed, retrieve it + if ($port == false) + { + $this->port = $this->POP3_PORT; + } + else + { + $this->port = $port; + } + + // If no port value is passed, retrieve it + if ($tval == false) + { + $this->tval = $this->POP3_TIMEOUT; + } + else + { + $this->tval = $tval; + } + + $this->do_debug = $debug_level; + $this->username = $username; + $this->password = $password; + + // Refresh the error log + $this->error = null; + + // Connect + $result = $this->Connect($this->host, $this->port, $this->tval); + + if ($result) + { + $login_result = $this->Login($this->username, $this->password); + + if ($login_result) + { + $this->Disconnect(); + + return true; + } + + } + + // We need to disconnect regardless if the login succeeded + $this->Disconnect(); + + return false; + } + + /** + * Connect to the POP3 server + * + * @param string $host + * @param integer $port + * @param integer $tval + * @return boolean + */ + function Connect ($host, $port = false, $tval = 30) + { + // Are we already connected? + if ($this->connected) + { + return true; + } + + /* + On Windows this will raise a PHP Warning error if the hostname doesn't exist. + Rather than supress it with @fsockopen, let's capture it cleanly instead + */ + + set_error_handler(array(&$this, 'catchWarning')); + + // Connect to the POP3 server + $this->pop_conn = fsockopen($host, // POP3 Host + $port, // Port # + $errno, // Error Number + $errstr, // Error Message + $tval); // Timeout (seconds) + + // Restore the error handler + restore_error_handler(); + + // Does the Error Log now contain anything? + if ($this->error && $this->do_debug >= 1) + { + $this->displayErrors(); + } + + // Did we connect? + if ($this->pop_conn == false) + { + // It would appear not... + $this->error = array( + 'error' => "Failed to connect to server $host on port $port", + 'errno' => $errno, + 'errstr' => $errstr + ); + + if ($this->do_debug >= 1) + { + $this->displayErrors(); + } + + return false; + } + + // Increase the stream time-out + + // Check for PHP 4.3.0 or later + if (version_compare(phpversion(), '4.3.0', 'ge')) + { + stream_set_timeout($this->pop_conn, $tval, 0); + } + else + { + // Does not work on Windows + if (substr(PHP_OS, 0, 3) !== 'WIN') + { + socket_set_timeout($this->pop_conn, $tval, 0); + } + } + + // Get the POP3 server response + $pop3_response = $this->getResponse(); + + // Check for the +OK + if ($this->checkResponse($pop3_response)) + { + // The connection is established and the POP3 server is talking + $this->connected = true; + return true; + } + + } + + /** + * Login to the POP3 server (does not support APOP yet) + * + * @param string $username + * @param string $password + * @return boolean + */ + function Login ($username = '', $password = '') + { + if ($this->connected == false) + { + $this->error = 'Not connected to POP3 server'; + + if ($this->do_debug >= 1) + { + $this->displayErrors(); + } + } + + if (empty($username)) + { + $username = $this->username; + } + + if (empty($password)) + { + $password = $this->password; + } + + $pop_username = "USER $username" . $this->CRLF; + $pop_password = "PASS $password" . $this->CRLF; + + // Send the Username + $this->sendString($pop_username); + $pop3_response = $this->getResponse(); + + if ($this->checkResponse($pop3_response)) + { + // Send the Password + $this->sendString($pop_password); + $pop3_response = $this->getResponse(); + + if ($this->checkResponse($pop3_response)) + { + return true; + } + else + { + return false; + } + } + else + { + return false; + } + } + + /** + * Disconnect from the POP3 server + */ + function Disconnect () + { + $this->sendString('QUIT'); + + fclose($this->pop_conn); + } + + /* + --------------- + Private Methods + --------------- + */ + + /** + * Get the socket response back. + * $size is the maximum number of bytes to retrieve + * + * @param integer $size + * @return string + */ + function getResponse ($size = 128) + { + $pop3_response = fgets($this->pop_conn, $size); + + return $pop3_response; + } + + /** + * Send a string down the open socket connection to the POP3 server + * + * @param string $string + * @return integer + */ + function sendString ($string) + { + $bytes_sent = fwrite($this->pop_conn, $string, strlen($string)); + + return $bytes_sent; + + } + + /** + * Checks the POP3 server response for +OK or -ERR + * + * @param string $string + * @return boolean + */ + function checkResponse ($string) + { + if (substr($string, 0, 3) !== '+OK') + { + $this->error = array( + 'error' => "Server reported an error: $string", + 'errno' => 0, + 'errstr' => '' + ); + + if ($this->do_debug >= 1) + { + $this->displayErrors(); + } + + return false; + } + else + { + return true; + } + + } + + /** + * If debug is enabled, display the error message array + * + */ + function displayErrors () + { + echo '

    ';
    +
    +      foreach ($this->error as $single_error)
    +    {
    +        print_r($single_error);
    +    }
    +
    +      echo '
    '; + } + + /** + * Takes over from PHP for the socket warning handler + * + * @param integer $errno + * @param string $errstr + * @param string $errfile + * @param integer $errline + */ + function catchWarning ($errno, $errstr, $errfile, $errline) + { + $this->error[] = array( + 'error' => "Connecting to the POP3 server raised a PHP warning: ", + 'errno' => $errno, + 'errstr' => $errstr + ); + } + + // End of class +} +?> diff --git a/includes/phpmailer/class.smtp.php b/includes/phpmailer/class.smtp.php new file mode 100644 index 0000000..57088b5 --- /dev/null +++ b/includes/phpmailer/class.smtp.php @@ -0,0 +1,1062 @@ +smtp_conn = 0; + $this->error = null; + $this->helo_rply = null; + + $this->do_debug = 0; + } + + /************************************************************* + * CONNECTION FUNCTIONS * + ***********************************************************/ + + /** + * Connect to the server specified on the port specified. + * If the port is not specified use the default SMTP_PORT. + * If tval is specified then a connection will try and be + * established with the server for that number of seconds. + * If tval is not specified the default is 30 seconds to + * try on the connection. + * + * SMTP CODE SUCCESS: 220 + * SMTP CODE FAILURE: 421 + * @access public + * @return bool + */ + function Connect($host,$port=0,$tval=30) { + # set the error val to null so there is no confusion + $this->error = null; + + # make sure we are __not__ connected + if($this->connected()) { + # ok we are connected! what should we do? + # for now we will just give an error saying we + # are already connected + $this->error = array("error" => "Already connected to a server"); + return false; + } + + if(empty($port)) { + $port = $this->SMTP_PORT; + } + + #connect to the smtp server + $this->smtp_conn = fsockopen($host, # the host of the server + $port, # the port to use + $errno, # error number if any + $errstr, # error message if any + $tval); # give up after ? secs + # verify we connected properly + if(empty($this->smtp_conn)) { + $this->error = array("error" => "Failed to connect to server", + "errno" => $errno, + "errstr" => $errstr); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": $errstr ($errno)" . $this->CRLF; + } + return false; + } + + # sometimes the SMTP server takes a little longer to respond + # so we will give it a longer timeout for the first read + // Windows still does not have support for this timeout function + if(substr(PHP_OS, 0, 3) != "WIN") + socket_set_timeout($this->smtp_conn, $tval, 0); + + # get any announcement stuff + $announce = $this->get_lines(); + + # set the timeout of any socket functions at 1/10 of a second + //if(function_exists("socket_set_timeout")) + // socket_set_timeout($this->smtp_conn, 0, 100000); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $announce; + } + + return true; + } + + /** + * Performs SMTP authentication. Must be run after running the + * Hello() method. Returns true if successfully authenticated. + * @access public + * @return bool + */ + function Authenticate($username, $password) { + // Start authentication + fputs($this->smtp_conn,"AUTH LOGIN" . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($code != 334) { + $this->error = + array("error" => "AUTH not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + + // Send encoded username + fputs($this->smtp_conn, base64_encode($username) . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($code != 334) { + $this->error = + array("error" => "Username not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + + // Send encoded password + fputs($this->smtp_conn, base64_encode($password) . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($code != 235) { + $this->error = + array("error" => "Password not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + + return true; + } + + /** + * Returns true if connected to a server otherwise false + * @access private + * @return bool + */ + function Connected() { + if(!empty($this->smtp_conn)) { + $sock_status = socket_get_status($this->smtp_conn); + if($sock_status["eof"]) { + # hmm this is an odd situation... the socket is + # valid but we are not connected anymore + if($this->do_debug >= 1) { + echo "SMTP -> NOTICE:" . $this->CRLF . + "EOF caught while checking if connected"; + } + $this->Close(); + return false; + } + return true; # everything looks good + } + return false; + } + + /** + * Closes the socket and cleans up the state of the class. + * It is not considered good to use this function without + * first trying to use QUIT. + * @access public + * @return void + */ + function Close() { + $this->error = null; # so there is no confusion + $this->helo_rply = null; + if(!empty($this->smtp_conn)) { + # close the connection and cleanup + fclose($this->smtp_conn); + $this->smtp_conn = 0; + } + } + + /*************************************************************** + * SMTP COMMANDS * + *************************************************************/ + + /** + * Issues a data command and sends the msg_data to the server + * finializing the mail transaction. $msg_data is the message + * that is to be send with the headers. Each header needs to be + * on a single line followed by a with the message headers + * and the message body being seperated by and additional . + * + * Implements rfc 821: DATA + * + * SMTP CODE INTERMEDIATE: 354 + * [data] + * . + * SMTP CODE SUCCESS: 250 + * SMTP CODE FAILURE: 552,554,451,452 + * SMTP CODE FAILURE: 451,554 + * SMTP CODE ERROR : 500,501,503,421 + * @access public + * @return bool + */ + function Data($msg_data) { + $this->error = null; # so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Data() without being connected"); + return false; + } + + fputs($this->smtp_conn,"DATA" . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + } + + if($code != 354) { + $this->error = + array("error" => "DATA command not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + + # the server is ready to accept data! + # according to rfc 821 we should not send more than 1000 + # including the CRLF + # characters on a single line so we will break the data up + # into lines by \r and/or \n then if needed we will break + # each of those into smaller lines to fit within the limit. + # in addition we will be looking for lines that start with + # a period '.' and append and additional period '.' to that + # line. NOTE: this does not count towards are limit. + + # normalize the line breaks so we know the explode works + $msg_data = str_replace("\r\n","\n",$msg_data); + $msg_data = str_replace("\r","\n",$msg_data); + $lines = explode("\n",$msg_data); + + # we need to find a good way to determine is headers are + # in the msg_data or if it is a straight msg body + # currently I am assuming rfc 822 definitions of msg headers + # and if the first field of the first line (':' sperated) + # does not contain a space then it _should_ be a header + # and we can process all lines before a blank "" line as + # headers. + $field = substr($lines[0],0,strpos($lines[0],":")); + $in_headers = false; + if(!empty($field) && !strstr($field," ")) { + $in_headers = true; + } + + $max_line_length = 998; # used below; set here for ease in change + + while(list(,$line) = @each($lines)) { + $lines_out = null; + if($line == "" && $in_headers) { + $in_headers = false; + } + # ok we need to break this line up into several + # smaller lines + while(strlen($line) > $max_line_length) { + $pos = strrpos(substr($line,0,$max_line_length)," "); + + # Patch to fix DOS attack + if(!$pos) { + $pos = $max_line_length - 1; + } + + $lines_out[] = substr($line,0,$pos); + $line = substr($line,$pos + 1); + # if we are processing headers we need to + # add a LWSP-char to the front of the new line + # rfc 822 on long msg headers + if($in_headers) { + $line = "\t" . $line; + } + } + $lines_out[] = $line; + + # now send the lines to the server + while(list(,$line_out) = @each($lines_out)) { + if(strlen($line_out) > 0) + { + if(substr($line_out, 0, 1) == ".") { + $line_out = "." . $line_out; + } + } + fputs($this->smtp_conn,$line_out . $this->CRLF); + } + } + + # ok all the message data has been sent so lets get this + # over with aleady + fputs($this->smtp_conn, $this->CRLF . "." . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + } + + if($code != 250) { + $this->error = + array("error" => "DATA not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + return true; + } + + /** + * Expand takes the name and asks the server to list all the + * people who are members of the _list_. Expand will return + * back and array of the result or false if an error occurs. + * Each value in the array returned has the format of: + * [ ] + * The definition of is defined in rfc 821 + * + * Implements rfc 821: EXPN + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE FAILURE: 550 + * SMTP CODE ERROR : 500,501,502,504,421 + * @access public + * @return string array + */ + function Expand($name) { + $this->error = null; # so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Expand() without being connected"); + return false; + } + + fputs($this->smtp_conn,"EXPN " . $name . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + } + + if($code != 250) { + $this->error = + array("error" => "EXPN not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + + # parse the reply and place in our array to return to user + $entries = explode($this->CRLF,$rply); + while(list(,$l) = @each($entries)) { + $list[] = substr($l,4); + } + + return $list; + } + + /** + * Sends the HELO command to the smtp server. + * This makes sure that we and the server are in + * the same known state. + * + * Implements from rfc 821: HELO + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE ERROR : 500, 501, 504, 421 + * @access public + * @return bool + */ + function Hello($host="") { + $this->error = null; # so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Hello() without being connected"); + return false; + } + + # if a hostname for the HELO was not specified determine + # a suitable one to send + if(empty($host)) { + # we need to determine some sort of appopiate default + # to send to the server + $host = "localhost"; + } + + // Send extended hello first (RFC 2821) + if(!$this->SendHello("EHLO", $host)) + { + if(!$this->SendHello("HELO", $host)) + return false; + } + + return true; + } + + /** + * Sends a HELO/EHLO command. + * @access private + * @return bool + */ + function SendHello($hello, $host) { + fputs($this->smtp_conn, $hello . " " . $host . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER: " . $this->CRLF . $rply; + } + + if($code != 250) { + $this->error = + array("error" => $hello . " not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + + $this->helo_rply = $rply; + + return true; + } + + /** + * Gets help information on the keyword specified. If the keyword + * is not specified then returns generic help, ussually contianing + * A list of keywords that help is available on. This function + * returns the results back to the user. It is up to the user to + * handle the returned data. If an error occurs then false is + * returned with $this->error set appropiately. + * + * Implements rfc 821: HELP [ ] + * + * SMTP CODE SUCCESS: 211,214 + * SMTP CODE ERROR : 500,501,502,504,421 + * @access public + * @return string + */ + function Help($keyword="") { + $this->error = null; # to avoid confusion + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Help() without being connected"); + return false; + } + + $extra = ""; + if(!empty($keyword)) { + $extra = " " . $keyword; + } + + fputs($this->smtp_conn,"HELP" . $extra . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + } + + if($code != 211 && $code != 214) { + $this->error = + array("error" => "HELP not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + + return $rply; + } + + /** + * Starts a mail transaction from the email address specified in + * $from. Returns true if successful or false otherwise. If True + * the mail transaction is started and then one or more Recipient + * commands may be called followed by a Data command. + * + * Implements rfc 821: MAIL FROM: + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE SUCCESS: 552,451,452 + * SMTP CODE SUCCESS: 500,501,421 + * @access public + * @return bool + */ + function Mail($from) { + $this->error = null; # so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Mail() without being connected"); + return false; + } + + $useVerp = ($this->do_verp ? "XVERP" : ""); + fputs($this->smtp_conn,"MAIL FROM:<" . $from . ">" . $useVerp . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + } + + if($code != 250) { + $this->error = + array("error" => "MAIL not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + return true; + } + + /** + * Sends the command NOOP to the SMTP server. + * + * Implements from rfc 821: NOOP + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE ERROR : 500, 421 + * @access public + * @return bool + */ + function Noop() { + $this->error = null; # so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Noop() without being connected"); + return false; + } + + fputs($this->smtp_conn,"NOOP" . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + } + + if($code != 250) { + $this->error = + array("error" => "NOOP not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + return true; + } + + /** + * Sends the quit command to the server and then closes the socket + * if there is no error or the $close_on_error argument is true. + * + * Implements from rfc 821: QUIT + * + * SMTP CODE SUCCESS: 221 + * SMTP CODE ERROR : 500 + * @access public + * @return bool + */ + function Quit($close_on_error=true) { + $this->error = null; # so there is no confusion + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Quit() without being connected"); + return false; + } + + # send the quit command to the server + fputs($this->smtp_conn,"quit" . $this->CRLF); + + # get any good-bye messages + $byemsg = $this->get_lines(); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $byemsg; + } + + $rval = true; + $e = null; + + $code = substr($byemsg,0,3); + if($code != 221) { + # use e as a tmp var cause Close will overwrite $this->error + $e = array("error" => "SMTP server rejected quit command", + "smtp_code" => $code, + "smtp_rply" => substr($byemsg,4)); + $rval = false; + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $e["error"] . ": " . + $byemsg . $this->CRLF; + } + } + + if(empty($e) || $close_on_error) { + $this->Close(); + } + + return $rval; + } + + /** + * Sends the command RCPT to the SMTP server with the TO: argument of $to. + * Returns true if the recipient was accepted false if it was rejected. + * + * Implements from rfc 821: RCPT TO: + * + * SMTP CODE SUCCESS: 250,251 + * SMTP CODE FAILURE: 550,551,552,553,450,451,452 + * SMTP CODE ERROR : 500,501,503,421 + * @access public + * @return bool + */ + function Recipient($to) { + $this->error = null; # so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Recipient() without being connected"); + return false; + } + + fputs($this->smtp_conn,"RCPT TO:<" . $to . ">" . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + } + + if($code != 250 && $code != 251) { + $this->error = + array("error" => "RCPT not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + return true; + } + + /** + * Sends the RSET command to abort and transaction that is + * currently in progress. Returns true if successful false + * otherwise. + * + * Implements rfc 821: RSET + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE ERROR : 500,501,504,421 + * @access public + * @return bool + */ + function Reset() { + $this->error = null; # so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Reset() without being connected"); + return false; + } + + fputs($this->smtp_conn,"RSET" . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + } + + if($code != 250) { + $this->error = + array("error" => "RSET failed", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + + return true; + } + + /** + * Starts a mail transaction from the email address specified in + * $from. Returns true if successful or false otherwise. If True + * the mail transaction is started and then one or more Recipient + * commands may be called followed by a Data command. This command + * will send the message to the users terminal if they are logged + * in. + * + * Implements rfc 821: SEND FROM: + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE SUCCESS: 552,451,452 + * SMTP CODE SUCCESS: 500,501,502,421 + * @access public + * @return bool + */ + function Send($from) { + $this->error = null; # so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Send() without being connected"); + return false; + } + + fputs($this->smtp_conn,"SEND FROM:" . $from . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + } + + if($code != 250) { + $this->error = + array("error" => "SEND not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + return true; + } + + /** + * Starts a mail transaction from the email address specified in + * $from. Returns true if successful or false otherwise. If True + * the mail transaction is started and then one or more Recipient + * commands may be called followed by a Data command. This command + * will send the message to the users terminal if they are logged + * in and send them an email. + * + * Implements rfc 821: SAML FROM: + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE SUCCESS: 552,451,452 + * SMTP CODE SUCCESS: 500,501,502,421 + * @access public + * @return bool + */ + function SendAndMail($from) { + $this->error = null; # so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called SendAndMail() without being connected"); + return false; + } + + fputs($this->smtp_conn,"SAML FROM:" . $from . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + } + + if($code != 250) { + $this->error = + array("error" => "SAML not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + return true; + } + + /** + * Starts a mail transaction from the email address specified in + * $from. Returns true if successful or false otherwise. If True + * the mail transaction is started and then one or more Recipient + * commands may be called followed by a Data command. This command + * will send the message to the users terminal if they are logged + * in or mail it to them if they are not. + * + * Implements rfc 821: SOML FROM: + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE SUCCESS: 552,451,452 + * SMTP CODE SUCCESS: 500,501,502,421 + * @access public + * @return bool + */ + function SendOrMail($from) { + $this->error = null; # so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called SendOrMail() without being connected"); + return false; + } + + fputs($this->smtp_conn,"SOML FROM:" . $from . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + } + + if($code != 250) { + $this->error = + array("error" => "SOML not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + return true; + } + + /** + * This is an optional command for SMTP that this class does not + * support. This method is here to make the RFC821 Definition + * complete for this class and __may__ be implimented in the future + * + * Implements from rfc 821: TURN + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE FAILURE: 502 + * SMTP CODE ERROR : 500, 503 + * @access public + * @return bool + */ + function Turn() { + $this->error = array("error" => "This method, TURN, of the SMTP ". + "is not implemented"); + if($this->do_debug >= 1) { + echo "SMTP -> NOTICE: " . $this->error["error"] . $this->CRLF; + } + return false; + } + + /** + * Verifies that the name is recognized by the server. + * Returns false if the name could not be verified otherwise + * the response from the server is returned. + * + * Implements rfc 821: VRFY + * + * SMTP CODE SUCCESS: 250,251 + * SMTP CODE FAILURE: 550,551,553 + * SMTP CODE ERROR : 500,501,502,421 + * @access public + * @return int + */ + function Verify($name) { + $this->error = null; # so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Verify() without being connected"); + return false; + } + + fputs($this->smtp_conn,"VRFY " . $name . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + } + + if($code != 250 && $code != 251) { + $this->error = + array("error" => "VRFY failed on name '$name'", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + return $rply; + } + + /******************************************************************* + * INTERNAL FUNCTIONS * + ******************************************************************/ + + /** + * Read in as many lines as possible + * either before eof or socket timeout occurs on the operation. + * With SMTP we can tell if we have more lines to read if the + * 4th character is '-' symbol. If it is a space then we don't + * need to read anything else. + * @access private + * @return string + */ + function get_lines() { + $data = ""; + while($str = @fgets($this->smtp_conn,515)) { + if($this->do_debug >= 4) { + echo "SMTP -> get_lines(): \$data was \"$data\"" . + $this->CRLF; + echo "SMTP -> get_lines(): \$str is \"$str\"" . + $this->CRLF; + } + $data .= $str; + if($this->do_debug >= 4) { + echo "SMTP -> get_lines(): \$data is \"$data\"" . $this->CRLF; + } + # if the 4th character is a space then we are done reading + # so just break the loop + if(substr($str,3,1) == " ") { break; } + } + return $data; + } + +} + + + ?> diff --git a/includes/phpmailer/language/phpmailer.lang-br.php b/includes/phpmailer/language/phpmailer.lang-br.php new file mode 100644 index 0000000..681914c --- /dev/null +++ b/includes/phpmailer/language/phpmailer.lang-br.php @@ -0,0 +1,23 @@ + diff --git a/includes/phpmailer/language/phpmailer.lang-ca.php b/includes/phpmailer/language/phpmailer.lang-ca.php new file mode 100644 index 0000000..3a9615a --- /dev/null +++ b/includes/phpmailer/language/phpmailer.lang-ca.php @@ -0,0 +1,24 @@ + \ No newline at end of file diff --git a/includes/phpmailer/language/phpmailer.lang-cz.php b/includes/phpmailer/language/phpmailer.lang-cz.php new file mode 100644 index 0000000..11568ee --- /dev/null +++ b/includes/phpmailer/language/phpmailer.lang-cz.php @@ -0,0 +1,26 @@ + \ No newline at end of file diff --git a/includes/phpmailer/language/phpmailer.lang-de.php b/includes/phpmailer/language/phpmailer.lang-de.php new file mode 100644 index 0000000..d5917ee --- /dev/null +++ b/includes/phpmailer/language/phpmailer.lang-de.php @@ -0,0 +1,25 @@ + diff --git a/includes/phpmailer/language/phpmailer.lang-dk.php b/includes/phpmailer/language/phpmailer.lang-dk.php new file mode 100644 index 0000000..ca05f50 --- /dev/null +++ b/includes/phpmailer/language/phpmailer.lang-dk.php @@ -0,0 +1,26 @@ + + */ + +$PHPMAILER_LANG = array(); + +$PHPMAILER_LANG["provide_address"] = 'Du skal indtaste mindst en ' . + 'modtagers emailadresse.'; +$PHPMAILER_LANG["mailer_not_supported"] = ' mailer understøttes ikke.'; +$PHPMAILER_LANG["execute"] = 'Kunne ikke køre: '; +$PHPMAILER_LANG["instantiate"] = 'Kunne ikke initialisere email funktionen.'; +$PHPMAILER_LANG["authenticate"] = 'SMTP fejl: Kunne ikke logge på.'; +$PHPMAILER_LANG["from_failed"] = 'Følgende afsenderadresse er forkert: '; +$PHPMAILER_LANG["recipients_failed"] = 'SMTP fejl: Følgende' . + 'modtagere er forkerte: '; +$PHPMAILER_LANG["data_not_accepted"] = 'SMTP fejl: Data kunne ikke accepteres.'; +$PHPMAILER_LANG["connect_host"] = 'SMTP fejl: Kunne ikke tilslutte SMTP serveren.'; +$PHPMAILER_LANG["file_access"] = 'Ingen adgang til fil: '; +$PHPMAILER_LANG["file_open"] = 'Fil fejl: Kunne ikke åbne filen: '; +$PHPMAILER_LANG["encoding"] = 'Ukendt encode-format: '; +?> \ No newline at end of file diff --git a/includes/phpmailer/language/phpmailer.lang-en.php b/includes/phpmailer/language/phpmailer.lang-en.php new file mode 100644 index 0000000..3422134 --- /dev/null +++ b/includes/phpmailer/language/phpmailer.lang-en.php @@ -0,0 +1,25 @@ + diff --git a/includes/phpmailer/language/phpmailer.lang-es.php b/includes/phpmailer/language/phpmailer.lang-es.php new file mode 100644 index 0000000..47f64ed --- /dev/null +++ b/includes/phpmailer/language/phpmailer.lang-es.php @@ -0,0 +1,25 @@ + \ No newline at end of file diff --git a/includes/phpmailer/language/phpmailer.lang-fi.php b/includes/phpmailer/language/phpmailer.lang-fi.php new file mode 100644 index 0000000..02ba6b7 --- /dev/null +++ b/includes/phpmailer/language/phpmailer.lang-fi.php @@ -0,0 +1,25 @@ + \ No newline at end of file diff --git a/includes/phpmailer/language/phpmailer.lang-fo.php b/includes/phpmailer/language/phpmailer.lang-fo.php new file mode 100644 index 0000000..bc06b76 --- /dev/null +++ b/includes/phpmailer/language/phpmailer.lang-fo.php @@ -0,0 +1,27 @@ + diff --git a/includes/phpmailer/language/phpmailer.lang-fr.php b/includes/phpmailer/language/phpmailer.lang-fr.php new file mode 100644 index 0000000..ccea966 --- /dev/null +++ b/includes/phpmailer/language/phpmailer.lang-fr.php @@ -0,0 +1,26 @@ + diff --git a/includes/phpmailer/language/phpmailer.lang-hu.php b/includes/phpmailer/language/phpmailer.lang-hu.php new file mode 100644 index 0000000..7ba57c4 --- /dev/null +++ b/includes/phpmailer/language/phpmailer.lang-hu.php @@ -0,0 +1,25 @@ + \ No newline at end of file diff --git a/includes/phpmailer/language/phpmailer.lang-it.php b/includes/phpmailer/language/phpmailer.lang-it.php new file mode 100644 index 0000000..45e82df --- /dev/null +++ b/includes/phpmailer/language/phpmailer.lang-it.php @@ -0,0 +1,29 @@ + + */ + +$PHPMAILER_LANG = array(); + +$PHPMAILER_LANG["provide_address"] = 'Deve essere fornito almeno un'. + ' indirizzo ricevente'; +$PHPMAILER_LANG["mailer_not_supported"] = 'Mailer non supportato'; +$PHPMAILER_LANG["execute"] = "Impossibile eseguire l'operazione: "; +$PHPMAILER_LANG["instantiate"] = 'Impossibile istanziare la funzione mail'; +$PHPMAILER_LANG["authenticate"] = 'SMTP Error: Impossibile autenticarsi.'; +$PHPMAILER_LANG["from_failed"] = 'I seguenti indirizzi mittenti hanno'. + ' generato errore: '; +$PHPMAILER_LANG["recipients_failed"] = 'SMTP Error: I seguenti indirizzi'. + 'destinatari hanno generato errore: '; +$PHPMAILER_LANG["data_not_accepted"] = 'SMTP Error: Data non accettati dal'. + 'server.'; +$PHPMAILER_LANG["connect_host"] = 'SMTP Error: Impossibile connettersi'. + ' all\'host SMTP.'; +$PHPMAILER_LANG["file_access"] = 'Impossibile accedere al file: '; +$PHPMAILER_LANG["file_open"] = 'File Error: Impossibile aprire il file: '; +$PHPMAILER_LANG["encoding"] = 'Encoding set dei caratteri sconosciuto: '; +?> diff --git a/includes/phpmailer/language/phpmailer.lang-ja.php b/includes/phpmailer/language/phpmailer.lang-ja.php new file mode 100644 index 0000000..142f1bf --- /dev/null +++ b/includes/phpmailer/language/phpmailer.lang-ja.php @@ -0,0 +1,27 @@ + \ No newline at end of file diff --git a/includes/phpmailer/language/phpmailer.lang-nl.php b/includes/phpmailer/language/phpmailer.lang-nl.php new file mode 100644 index 0000000..aa07b40 --- /dev/null +++ b/includes/phpmailer/language/phpmailer.lang-nl.php @@ -0,0 +1,25 @@ + \ No newline at end of file diff --git a/includes/phpmailer/language/phpmailer.lang-no.php b/includes/phpmailer/language/phpmailer.lang-no.php new file mode 100644 index 0000000..5d8db67 --- /dev/null +++ b/includes/phpmailer/language/phpmailer.lang-no.php @@ -0,0 +1,25 @@ + diff --git a/includes/phpmailer/language/phpmailer.lang-pl.php b/includes/phpmailer/language/phpmailer.lang-pl.php new file mode 100644 index 0000000..982494d --- /dev/null +++ b/includes/phpmailer/language/phpmailer.lang-pl.php @@ -0,0 +1,26 @@ + \ No newline at end of file diff --git a/includes/phpmailer/language/phpmailer.lang-ro.php b/includes/phpmailer/language/phpmailer.lang-ro.php new file mode 100644 index 0000000..f72ec78 --- /dev/null +++ b/includes/phpmailer/language/phpmailer.lang-ro.php @@ -0,0 +1,24 @@ + + */ + +$PHPMAILER_LANG = array(); + +$PHPMAILER_LANG["provide_address"] = 'Trebuie sa adaugati cel putin un recipient (adresa de mail).'; +$PHPMAILER_LANG["mailer_not_supported"] = ' mailer nu este suportat.'; +$PHPMAILER_LANG["execute"] = 'Nu pot executa: '; +$PHPMAILER_LANG["instantiate"] = 'Nu am putut instantia functia mail.'; +$PHPMAILER_LANG["authenticate"] = 'Eroare SMTP: Nu a functionat autentificarea.'; +$PHPMAILER_LANG["from_failed"] = 'Urmatoarele adrese From au dat eroare: '; +$PHPMAILER_LANG["recipients_failed"] = 'Eroare SMTP: Urmatoarele adrese de mail au dat eroare: '; +$PHPMAILER_LANG["data_not_accepted"] = 'Eroare SMTP: Continutul mailului nu a fost acceptat.'; +$PHPMAILER_LANG["connect_host"] = 'Eroare SMTP: Nu m-am putut conecta la adresa SMTP.'; +$PHPMAILER_LANG["file_access"] = 'Nu pot accesa fisierul: '; +$PHPMAILER_LANG["file_open"] = 'Eroare de fisier: Nu pot deschide fisierul: '; +$PHPMAILER_LANG["encoding"] = 'Encodare necunoscuta: '; +?> diff --git a/includes/phpmailer/language/phpmailer.lang-ru.php b/includes/phpmailer/language/phpmailer.lang-ru.php new file mode 100644 index 0000000..27579c3 --- /dev/null +++ b/includes/phpmailer/language/phpmailer.lang-ru.php @@ -0,0 +1,25 @@ + diff --git a/includes/phpmailer/language/phpmailer.lang-se.php b/includes/phpmailer/language/phpmailer.lang-se.php new file mode 100644 index 0000000..39f2c3b --- /dev/null +++ b/includes/phpmailer/language/phpmailer.lang-se.php @@ -0,0 +1,26 @@ + + */ + +$PHPMAILER_LANG = array(); + +$PHPMAILER_LANG["provide_address"] = 'Du måste ange minst en ' . + 'mottagares e-postadress.'; +$PHPMAILER_LANG["mailer_not_supported"] = ' mailer stöds inte.'; +$PHPMAILER_LANG["execute"] = 'Kunde inte köra: '; +$PHPMAILER_LANG["instantiate"] = 'Kunde inte initiera e-postfunktion.'; +$PHPMAILER_LANG["authenticate"] = 'SMTP fel: Kunde inte autentisera.'; +$PHPMAILER_LANG["from_failed"] = 'Följande avsändaradress är felaktig: '; +$PHPMAILER_LANG["recipients_failed"] = 'SMTP fel: Följande ' . + 'mottagare är felaktig: '; +$PHPMAILER_LANG["data_not_accepted"] = 'SMTP fel: Data accepterades inte.'; +$PHPMAILER_LANG["connect_host"] = 'SMTP fel: Kunde inte ansluta till SMTP-server.'; +$PHPMAILER_LANG["file_access"] = 'Ingen åtkomst till fil: '; +$PHPMAILER_LANG["file_open"] = 'Fil fel: Kunde inte öppna fil: '; +$PHPMAILER_LANG["encoding"] = 'Okänt encode-format: '; +?> \ No newline at end of file diff --git a/includes/phpmailer/language/phpmailer.lang-tr.php b/includes/phpmailer/language/phpmailer.lang-tr.php new file mode 100644 index 0000000..d46a670 --- /dev/null +++ b/includes/phpmailer/language/phpmailer.lang-tr.php @@ -0,0 +1,27 @@ + \ No newline at end of file diff --git a/includes/phpsniff/index.php b/includes/phpsniff/index.php new file mode 100644 index 0000000..3e305fe --- /dev/null +++ b/includes/phpsniff/index.php @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/includes/phpsniff/phpSniff.class.php b/includes/phpsniff/phpSniff.class.php new file mode 100644 index 0000000..7d8a9c5 --- /dev/null +++ b/includes/phpsniff/phpSniff.class.php @@ -0,0 +1,907 @@ + + * @version $Id: phpSniff.class.php,v 1.22 2004/04/27 00:55:49 epsilon7 Exp $ + * @copyright Copyright © 2002-2004 Roger Raymond + * @package phpSniff + * @license http://opensource.org/licenses/lgpl-license.php GNU Lesser General Public License + * @filesource + */ +/** + * PHP Sniffer Class + * + * Used to determine the browser and other associated properies + * using nothing other than the HTTP_USER_AGENT value supplied by a + * user's web browser. + * + * @package phpSniff + * @access public + * @author Roger Raymond + */ +class phpSniff +{ + + /** + * @access private + * @var string + */ + var $_version = '2.1.4'; + + /** + * $_temp_file_path + * default : /tmp/ + * desc : directory writable by the server to store cookie check files. + * : trailing slash is needed. only used if you use the check cookie routine + * + * @access public + * @var string + */ + var $_temp_file_path = '/tmp/'; // with trailing slash + + /** + * $_check_cookies + * default : null + * desc : Allow for the script to redirect the browser in order + * : to check for cookies. In order for this to work, this + * : class must be instantiated before any headers are sent. + * + * @access public + * @var string + */ + var $_check_cookies = NULL; + + /** + * $_default_language + * default : en-us + * desc : language to report as if no languages are found + * @access public + * @var string + */ + var $_default_language = 'en-us'; + + /** + * default : null + * Allow for browser to Masquerade as another. (ie: Opera identifies as MSIE 5.0) + * + * @access public + * @var string + */ + var $_allow_masquerading = NULL; + + /** + * @access private + * @var string + */ + var $_php_version = ''; + + + /** + * 2D Array of browsers we wish to search for in key => value pairs. + *
    +     *  key   = browser to search for [as in HTTP_USER_AGENT]
    +     *  value = value to return as 'browser' property
    +     *  
    + * + * @access public + * @var array + */ + var $_browsers = array( + 'microsoft internet explorer' => 'IE', + 'msie' => 'IE', + 'netscape6' => 'NS', + 'netscape' => 'NS', + 'galeon' => 'GA', + 'phoenix' => 'PX', + 'mozilla firebird' => 'FB', + 'firebird' => 'FB', + 'firefox' => 'FX', + 'chimera' => 'CH', + 'camino' => 'CA', + 'epiphany' => 'EP', + 'safari' => 'SF', + 'k-meleon' => 'KM', + 'mozilla' => 'MZ', + 'opera' => 'OP', + 'konqueror' => 'KQ', + 'icab' => 'IC', + 'lynx' => 'LX', + 'links' => 'LI', + 'ncsa mosaic' => 'MO', + 'amaya' => 'AM', + 'omniweb' => 'OW', + 'hotjava' => 'HJ', + 'browsex' => 'BX', + 'amigavoyager' => 'AV', + 'amiga-aweb' => 'AW', + 'ibrowse' => 'IB' + ); + + /** + * $_javascript_versions + * desc : 2D Array of javascript version supported by which browser + * : in key => value pairs. + * : key = javascript version + * : value = search parameter for browsers that support the + * : javascript version listed in the key (comma delimited) + * : note: the search parameters rely on the values + * : set in the $_browsers array + * @access public + * @var array + */ + var $_javascript_versions = array( + '1.5' => 'NS5+,MZ,PX,FB,FX,GA,CH,CA,SF,KQ3+,KM,EP', // browsers that support JavaScript 1.5 + '1.4' => '', + '1.3' => 'NS4.05+,OP5+,IE5+', + '1.2' => 'NS4+,IE4+', + '1.1' => 'NS3+,OP,KQ', + '1.0' => 'NS2+,IE3+', + '0' => 'LI,LX,HJ' + ); + + /** + * $_browser_features + * desc : 2D Array of browser features supported by which browser + * : in key => value pairs. + * : key = feature + * : value = search parameter for browsers that support the + * : feature listed in the key (comma delimited) + * : note: the search parameters rely on the values + * : set in the $_browsers array + * @access public + * @var string + */ + var $_browser_features = array( + /** + * the following are true by default + * (see phpSniff.core.php $_feature_set array) + * browsers listed here will be set to false + **/ + 'html' => '', + 'images' => 'LI,LX', + 'frames' => 'LX', + 'tables' => '', + 'java' => 'OP3,LI,LX,NS1,MO,IE1,IE2', + 'plugins' => 'IE1,IE2,LI,LX', + /** + * the following are false by default + * (see phpSniff.core.php $_feature_set array) + * browsers listed here will be set to true + **/ + 'css2' => 'NS5+,IE5+,MZ,PX,FB,FX,CH,CA,SF,GA,KQ3+,OP7+,KM,EP', + 'css1' => 'NS4+,IE4+,MZ,PX,FB,FX,CH,CA,SF,GA,KQ,OP7+,KM,EP', + 'iframes' => 'LI,IE3+,NS5+,MZ,PX,FB,FX,CH,CA,SF,GA,KQ,OP7+,KM,EP', + 'xml' => 'IE5+,NS5+,MZ,PX,FB,FX,CH,CA,SF,GA,KQ,OP7+,KM,EP', + 'dom' => 'IE5+,NS5+,MZ,PX,FB,FX,CH,CA,SF,GA,KQ,OP7+,KM,EP', + 'hdml' => '', + 'wml' => '' + ); + + /** + * + * $_browser_quirks + * desc : 2D Array of browser quirks present in which browser + * : in key => value pairs. + * : key = quirk + * : value = search parameter for browsers that feature the + * : quirk listed in the key (comma delimited) + * : note: the search parameters rely on the values + * : set in the $_browsers array + * @access public + * @var string + */ + var $_browser_quirks = array( + 'must_cache_forms' => 'NS,MZ,FB,PX,FX', + 'avoid_popup_windows' => 'IE3,LI,LX', + 'cache_ssl_downloads' => 'IE', + 'break_disposition_header' => 'IE5.5', + 'empty_file_input_value' => 'KQ', + 'scrollbar_in_way' => 'IE6' + ); + + /** + * @access private + * @var array + */ + var $_browser_info = array( + 'ua' => '', + 'browser' => 'Unknown', + 'version' => 0, + 'maj_ver' => 0, + 'min_ver' => 0, + 'letter_ver' => '', + 'javascript' => '0.0', + 'platform' => 'Unknown', + 'os' => 'Unknown', + 'ip' => 'Unknown', + 'cookies' => 'Unknown', // remains for backwards compatability + 'ss_cookies' => 'Unknown', + 'st_cookies' => 'Unknown', + 'language' => '', + 'long_name' => '', + 'gecko' => '', + 'gecko_ver' => '' + ); + + /** + * @access private + * @var array + */ + var $_feature_set = array( + 'html' => true, + 'images' => true, + 'frames' => true, + 'tables' => true, + 'java' => true, + 'plugins' => true, + 'iframes' => false, + 'css2' => false, + 'css1' => false, + 'xml' => false, + 'dom' => false, + 'wml' => false, + 'hdml' => false + ); + + /** + * @access private + * @var array + */ + var $_quirks = array( + 'must_cache_forms' => false, + 'avoid_popup_windows' => false, + 'cache_ssl_downloads' => false, + 'break_disposition_header' => false, + 'empty_file_input_value' => false, + 'scrollbar_in_way' => false + ); + + /** + * @access private + * @var boolean + */ + var $_get_languages_ran_once = false; + /** + * @access private + * @var string + */ + var $_browser_search_regex = '([a-z]+)([0-9]*)([0-9.]*)(up|dn|\+|\-)?'; + /** + * @access private + * @var string + */ + var $_language_search_regex = '([a-z-]{2,})'; + /** + * @access private + * @var string + */ + var $_browser_regex; + + /** + * Performs some basic initialization and returns and object + * @param string User Agent to parse + * @param mixed array of settings + * [check_cookies, default_language, allow_masqeurading] + * + * @return object phpSniff object + */ + + function phpSniff($UA='',$settings = true) + { // populate the HTTP_USER_AGENT string + // 20020425 :: rraymond + // routine for easier configuration of the client at runtime + if(is_array($settings)) { + $run = true; + extract($settings); + $this->_check_cookies = $check_cookies; + $this->_default_language = $default_language; + $this->_allow_masquerading = $allow_masquerading; + } else { + // for backwards compatibility with 2.0.x series + $run = (bool) $settings; + } + + // if the user agent is empty, see if it exists somewhere + if(empty($UA)) { + if(isset($HTTP_SERVER_VARS['HTTP_USER_AGENT'])) { + $UA = $HTTP_SERVER_VARS['HTTP_USER_AGENT']; + } elseif(isset($_SERVER['HTTP_USER_AGENT'])) { + $UA = $_SERVER['HTTP_USER_AGENT']; + } else { + // try to use the getenv function as a last resort + $UA = getenv('HTTP_USER_AGENT'); + } + } + + // if it's still empty, just return false as there is nothing to do + if(empty($UA)) return false; + + $this->_set_browser('ua',$UA); + if($run) $this->init(); + } + + function init () + { + // collect the ip + $this->_get_ip(); + // run the cookie check routine first + // [note: method only runs if allowed] + $this->_test_cookies(); + // rip the user agent to pieces + $this->_get_browser_info(); + // gecko build + $this->_get_gecko(); + // look for other languages + $this->_get_languages(); + // establish the operating platform + $this->_get_os_info(); + // determine javascript version + $this->_get_javascript(); + // determine current feature set + $this->_get_features(); + // point out any quirks + $this->_get_quirks(); + } + + /** + * turn the cookie check routine on or off + * @param bool true or false + */ + function check_cookies($yn) + { + $this->_check_cookies = (bool) $yn; + } + + /** + * allow browser masquerading + * @param bool true or false + */ + function allow_masquerading($yn) + { + $this->_allow_masquerading = (bool) $yn; + } + + /** + * set the default browser language + * @param string valid language (ex: en-us) + */ + function default_language($language) + { + $this->_default_language = $language; + } + + /** + * property + * @param string property to return . optional (null returns entire array) + * @return mixed array/string entire array or value of property + **/ + function property ($p=null) + { if($p==null) + { return $this->_browser_info; + } + else + { return $this->_browser_info[strtolower($p)]; + } + } + + /** + * get_property is an alias for property + * @param string property to return . optional (null returns entire array) + * @return mixed array/string entire array or value of property + **/ + function get_property ($p) + { return $this->property($p); + } + + /** + * is + * @param string search phrase format = l:lang;b:browser + * @return bool true on success + * ex: $client->is('b:OP5Up'); + **/ + function is ($s) + { // perform language search + if(preg_match('/l:'.$this->_language_search_regex.'/i',$s,$match)) + { if($match) return $this->_perform_language_search($match); + } + // perform browser search + elseif(preg_match('/b:'.$this->_browser_search_regex.'/i',$s,$match)) + { if($match) return $this->_perform_browser_search($match); + } + return false; + } + + /** + * browser_is + * @param string search phrase for browser + * @return bool true on success + * ex: $client->browser_is('OP5Up'); + **/ + function browser_is ($s) + { preg_match('/'.$this->_browser_search_regex.'/i',$s,$match); + if($match) return $this->_perform_browser_search($match); + } + + /** + * language_is + * @param string search phrase for language + * @return bool true on success + * ex: $client->language_is('en-US'); + **/ + function language_is ($s) + { preg_match('/'.$this->_language_search_regex.'/i',$s,$match); + if($match) return $this->_perform_language_search($match); + } + + /** + * checks to see if the browser supports any of the following features: + *
      + *
    • html + *
    • images + *
    • frames + *
    • tables + *
    • plugins + *
    • iframes + *
    • css2 + *
    • css1 + *
    • xml + *
    • dom + *
    • wml + *
    • hdml + *
    + * ex: $client->has_feature('html'); + * @param string feature we're checking on + * @return bool true on success + + **/ + function has_feature ($s) + { return $this->_feature_set[$s]; + } + + /** + * checks to see if the browser has any of the following quirks: + *
      + *
    • must_cache_forms + *
    • avoid_popup_windows + *
    • cache_ssl_downloads + *
    • break_disposition_header + *
    • empty_file_input_value + *
    • scrollbar_in_way + *
    + * ex: $client->has_quirk('avoid_popup_windows'); + * @param string quirk we're looking for + * @return bool true on success + **/ + function has_quirk ($s) + { return $this->_quirks[$s]; + } + + /** + * _perform_browser_search + * @param string what we're searching for + * @return bool true on success + * @access private + **/ + function _perform_browser_search ($data) + { $search = array(); + $search['phrase'] = isset($data[0]) ? $data[0] : ''; + $search['name'] = isset($data[1]) ? strtolower($data[1]) : ''; + $search['maj_ver'] = isset($data[2]) ? $data[2] : ''; + $search['min_ver'] = isset($data[3]) ? $data[3] : ''; + $search['direction'] = isset($data[4]) ? strtolower($data[4]) : ''; + + $looking_for = $search['maj_ver'].$search['min_ver']; + if($search['name'] == 'aol' || $search['name'] == 'webtv') { + return stristr($this->_browser_info['ua'],$search['name']); + } elseif($this->_browser_info['browser'] == $search['name'] || $search['name'] == 'gecko') { + if(strtolower($search['name']) == 'gecko') { + $what_we_are =& $this->_browser_info['gecko_ver']; + } else { + $majv = $search['maj_ver'] ? $this->_browser_info['maj_ver'] : ''; + $minv = $search['min_ver'] ? $this->_browser_info['min_ver'] : ''; + $what_we_are = $majv.$minv; + } + if(($search['direction'] == 'up' || $search['direction'] == '+') + && ($what_we_are >= $looking_for)) + { return true; + } + elseif(($search['direction'] == 'dn' || $search['direction'] == '-') + && ($what_we_are <= $looking_for)) + { return true; + } + elseif($what_we_are == $looking_for) + { return true; + } + } + return false; + } + + /** + * @access private + */ + function _perform_language_search ($data) + { // if we've not grabbed the languages, then do so. + $this->_get_languages(); + return stristr($this->_browser_info['language'],$data[1]); + } + + /** + * @access private + */ + function _get_languages () + { // capture available languages and insert into container + if(!$this->_get_languages_ran_once) + { if($languages = getenv('HTTP_ACCEPT_LANGUAGE')) + { $languages = preg_replace('/(;q=[0-9]+.[0-9]+)/i','',$languages); + } + else + { $languages = $this->_default_language; + } + $this->_set_browser('language',$languages); + $this->_get_languages_ran_once = true; + } + } + + /** + * @access private + */ + function _get_os_info () + { // regexes to use + $regex_windows = '/([^dar]win[dows]*)[\s]?([0-9a-z]*)[\w\s]?([a-z0-9.]*)/i'; + $regex_mac = '/(68[k0]{1,3})|(ppc|intel) (mac os x)|([p\S]{1,5}pc)|(darwin)/i'; + $regex_os2 = '/os\/2|ibm-webexplorer/i'; + $regex_sunos = '/(sun|i86)[os\s]*([0-9]*)/i'; + $regex_irix = '/(irix)[\s]*([0-9]*)/i'; + $regex_hpux = '/(hp-ux)[\s]*([0-9]*)/i'; + $regex_aix = '/aix([0-9]*)/i'; + $regex_dec = '/dec|osfl|alphaserver|ultrix|alphastation/i'; + $regex_vms = '/vax|openvms/i'; + $regex_sco = '/sco|unix_sv/i'; + $regex_linux = '/x11|inux/i'; + $regex_bsd = '/(free)?(bsd)/i'; + $regex_amiga = '/amiga[os]?/i'; + + // look for Windows Box + if(preg_match_all($regex_windows,$this->_browser_info['ua'],$match)) + { /** Windows has some of the most ridiculous HTTP_USER_AGENT strings */ + //$match[1][count($match[0])-1]; + $v = $match[2][count($match[0])-1]; + $v2 = $match[3][count($match[0])-1]; + // Establish NT 5.1 as Windows XP + if(stristr($v,'NT') && $v2 == 5.1) $v = 'xp'; + // Establish NT 5.0 and Windows 2000 as win2k + elseif($v == '2000') $v = '2k'; + elseif(stristr($v,'NT') && $v2 == 5.0) $v = '2k'; + // Establish 9x 4.90 as Windows 98 + elseif(stristr($v,'9x') && $v2 == 4.9) $v = '98'; + // See if we're running windows 3.1 + elseif($v.$v2 == '16bit') $v = '31'; + // otherwise display as is (31,95,98,NT,ME,XP) + else $v .= $v2; + // update browser info container array + if(empty($v)) $v = 'win'; + $this->_set_browser('os',strtolower($v)); + $this->_set_browser('platform','win'); + } + // look for mac + // sets: platform = mac ; os = 68k or ppc + elseif( preg_match($regex_mac,$this->_browser_info['ua'],$match) ) + { $this->_set_browser('platform','mac'); + $os = ''; + if( $match[3] == 'mac os x' ) { $os = 'osx'; } + elseif( !empty($match[1]) ) { $os = '68k'; } + $this->_set_browser('os',$os); + } + // look for *nix boxes + // linux sets: platform = *nix ; os = linux + elseif(preg_match($regex_linux,$this->_browser_info['ua'],$match)) + { $this->_set_browser('platform','*nix'); + $this->_set_browser('os','linux'); + } + // sunos sets: platform = *nix ; os = sun|sun4|sun5|suni86 + elseif(preg_match($regex_sunos,$this->_browser_info['ua'],$match)) + { $this->_set_browser('platform','*nix'); + if(!stristr('sun',$match[1])) $match[1] = 'sun'.$match[1]; + $this->_set_browser('os',$match[1].$match[2]); + } + // irix sets: platform = *nix ; os = irix|irix5|irix6|... + elseif(preg_match($regex_irix,$this->_browser_info['ua'],$match)) + { $this->_set_browser('platform','*nix'); + $this->_set_browser('os',$match[1].$match[2]); + } + // hp-ux sets: platform = *nix ; os = hpux9|hpux10|... + elseif(preg_match($regex_hpux,$this->_browser_info['ua'],$match)) + { $this->_set_browser('platform','*nix'); + $match[1] = str_replace('-','',$match[1]); + $match[2] = (int) $match[2]; + $this->_set_browser('os',$match[1].$match[2]); + } + // aix sets: platform = *nix ; os = aix|aix1|aix2|aix3|... + elseif(preg_match($regex_aix,$this->_browser_info['ua'],$match)) + { $this->_set_browser('platform','*nix'); + $this->_set_browser('os','aix'.$match[1]); + } + // dec sets: platform = *nix ; os = dec + elseif(preg_match($regex_dec,$this->_browser_info['ua'],$match)) + { $this->_set_browser('platform','*nix'); + $this->_set_browser('os','dec'); + } + // vms sets: platform = *nix ; os = vms + elseif(preg_match($regex_vms,$this->_browser_info['ua'],$match)) + { $this->_set_browser('platform','*nix'); + $this->_set_browser('os','vms'); + } + // sco sets: platform = *nix ; os = sco + elseif(preg_match($regex_sco,$this->_browser_info['ua'],$match)) + { $this->_set_browser('platform','*nix'); + $this->_set_browser('os','sco'); + } + // unixware sets: platform = *nix ; os = unixware + elseif(stristr($this->_browser_info['ua'],'unix_system_v')) + { $this->_set_browser('platform','*nix'); + $this->_set_browser('os','unixware'); + } + // mpras sets: platform = *nix ; os = mpras + elseif(stristr($this->_browser_info['ua'],'ncr')) + { $this->_set_browser('platform','*nix'); + $this->_set_browser('os','mpras'); + } + // reliant sets: platform = *nix ; os = reliant + elseif(stristr($this->_browser_info['ua'],'reliantunix')) + { $this->_set_browser('platform','*nix'); + $this->_set_browser('os','reliant'); + } + // sinix sets: platform = *nix ; os = sinix + elseif(stristr($this->_browser_info['ua'],'sinix')) + { $this->_set_browser('platform','*nix'); + $this->_set_browser('os','sinix'); + } + // bsd sets: platform = *nix ; os = bsd|freebsd + elseif(preg_match($regex_bsd,$this->_browser_info['ua'],$match)) + { $this->_set_browser('platform','*nix'); + $this->_set_browser('os',$match[1].$match[2]); + } + // last one to look for + // look for amiga OS + elseif(preg_match($regex_amiga,$this->_browser_info['ua'],$match)) + { $this->_set_browser('platform','amiga'); + if(stristr($this->_browser_info['ua'],'morphos')) { + // checking for MorphOS + $this->_set_browser('os','morphos'); + } elseif(stristr($this->_browser_info['ua'],'mc680x0')) { + // checking for MC680x0 + $this->_set_browser('os','mc680x0'); + } elseif(stristr($this->_browser_info['ua'],'ppc')) { + // checking for PPC + $this->_set_browser('os','ppc'); + } elseif(preg_match('/(AmigaOS [\.1-9]?)/i',$this->_browser_info['ua'],$match)) { + // checking for AmigaOS version string + $this->_set_browser('os',$match[1]); + } + } + // look for OS2 + elseif( preg_match($regex_os2,$this->_browser_info['ua'])) + { $this->_set_browser('os','os2'); + $this->_set_browser('platform','os2'); + } + } + + /** + * @access private + */ + function _get_browser_info () + { $this->_build_regex(); + if(preg_match_all($this->_browser_regex,$this->_browser_info['ua'],$results)) + { // get the position of the last browser found + $count = count($results[0])-1; + // if we're allowing masquerading, revert to the next to last browser found + // if possible, otherwise stay put + if($this->_allow_masquerading && $count > 0) $count--; + // insert findings into the container + $this->_set_browser('browser',$this->_get_short_name($results[1][$count])); + $this->_set_browser('long_name',$results[1][$count]); + $this->_set_browser('maj_ver',$results[2][$count]); + // parse the minor version string and look for alpha chars + preg_match('/([.\0-9]+)?([\.a-z0-9]+)?/i',$results[3][$count],$match); + if(isset($match[1])) { + $this->_set_browser('min_ver',$match[1]); + } else { + $this->_set_browser('min_ver','.0'); + } + if(isset($match[2])) $this->_set_browser('letter_ver',$match[2]); + // insert findings into container + $this->_set_browser('version',$this->_browser_info['maj_ver'].$this->property('min_ver')); + } + } + + /** + * @access private + */ + function _get_ip () + { if(getenv('HTTP_CLIENT_IP')) + { $ip = getenv('HTTP_CLIENT_IP'); + } + else + { $ip = getenv('REMOTE_ADDR'); + } + $this->_set_browser('ip',$ip); + } + + /** + * @access private + */ + function _build_regex () + { $browsers = ''; + while(list($k,) = each($this->_browsers)) + { if(!empty($browsers)) $browsers .= "|"; + $browsers .= $k; + } + $version_string = "[\/\sa-z(]*([0-9]+)([\.0-9a-z]+)?"; + $this->_browser_regex = "/($browsers)$version_string/i"; + } + + /** + * @access private + */ + function _get_short_name ($long_name) + { return $this->_browsers[strtolower($long_name)]; + } + + /** + * @access private + */ + // medianes :: new test cookie routine + function _test_cookies() + { global $HTTP_COOKIE_VARS; + $cookies = array(); + if(isset($_COOKIE)) { + $cookies = $_COOKIE; + } elseif(isset($HTTP_COOKIE_VARS)) { + $cookies = $HTTP_COOKIE_VARS; + } + if($this->_check_cookies) + { $fp = @fopen($this->_temp_file_path.$this->property('ip'),'r'); + if(!$fp) + { $fp = @fopen($this->_temp_file_path.$this->property('ip'),'a'); + // make sure we have a valid file pointer + if($fp) { + fclose($fp); + setcookie('phpSniff_session','ss',0,'/'); + setcookie('phpSniff_stored','st',time()+3600*24*365,'/'); + $QS=getenv('QUERY_STRING'); + $script_path=getenv('PATH_INFO')?getenv('PATH_INFO'):getenv('SCRIPT_NAME'); + if(is_integer($pos=strpos(strrev($script_path),"php.xedni/"))&&!$pos) { + $script_path=strrev(substr(strrev($script_path),9)); + } + } + $location='http://'.getenv('SERVER_NAME').$script_path.($QS==''?'':'?'.$QS); + header("Location: $location"); + exit; + } elseif($fp) { + // we only want to proceed if we have a file pointer + unlink($this->_temp_file_path.$this->property('ip')); + fclose($fp); + $this->_set_browser('ss_cookies',isset($cookies['phpSniff_session'])?'true':'false'); + $this->_set_browser('st_cookies',isset($cookies['phpSniff_stored'])?'true':'false'); + // delete the old cookies + setcookie('phpSniff_session','',0,'/'); + setcookie('phpSniff_stored','',0,'/'); + } + } + } + + /** + * @access private + */ + function _get_javascript() + { $set=false; + // see if we have any matches + while(list($version,$browser) = each($this->_javascript_versions)) + { $browser = explode(',',$browser); + while(list(,$search) = each($browser)) + { if($this->is('b:'.$search)) + { $this->_set_browser('javascript',$version); + $set = true; + break; + } + } + if($set) break; + } + } + + /** + * @access private + */ + function _get_features () + { while(list($feature,$browser) = each($this->_browser_features)) + { $browser = explode(',',$browser); + while(list(,$search) = each($browser)) + { if($this->browser_is($search)) + { $this->_set_feature($feature); + break; + } + } + } + } + + /** + * @access private + */ + function _get_quirks () + { while(list($quirk,$browser) = each($this->_browser_quirks)) + { $browser = explode(',',$browser); + while(list(,$search) = each($browser)) + { if($this->browser_is($search)) + { $this->_set_quirk($quirk); + break; + } + } + } + } + + /** + * @access private + */ + function _get_gecko () + { if(preg_match('/gecko\/([0-9]+)/i',$this->property('ua'),$match)) + { $this->_set_browser('gecko',$match[1]); + if (preg_match('/rv[: ]?([0-9a-z.+]+)/i',$this->property('ua'),$mozv)) { + // mozilla release + $this->_set_browser('gecko_ver',$mozv[1]); + } elseif (preg_match('/(m[0-9]+)/i',$this->property('ua'),$mozv)) { + // mozilla milestone version + $this->_set_browser('gecko_ver',$mozv[1]); + } + // if this is a mozilla browser, get the rv: information + if($this->browser_is($this->_get_short_name('mozilla'))) { + if( !empty( $mozv[1] ) && preg_match('/([0-9]+)([\.0-9]+)([a-z0-9+]?)/i',$mozv[1],$match) ) { + $this->_set_browser('version',$mozv[1]); + $this->_set_browser('maj_ver',$match[1]); + $this->_set_browser('min_ver',$match[2]); + $this->_set_browser('letter_ver',$match[3]); + } + } + } elseif($this->is('b:'.$this->_get_short_name('mozilla'))) { + // this is probably a netscape browser or compatible + $this->_set_browser('long_name','netscape'); + $this->_set_browser('browser',$this->_get_short_name('netscape')); + } + } + + /** + * @access private + */ + function _set_browser ($k,$v) + { $this->_browser_info[strtolower($k)] = strtolower($v); + } + + /** + * @access private + */ + function _set_feature ($k) + { $this->_feature_set[strtolower($k)] = !$this->_feature_set[strtolower($k)]; + } + + /** + * @access private + */ + function _set_quirk ($k) + { $this->_quirks[strtolower($k)] = true; + } +} +?> diff --git a/includes/phpsniff/phpSniff.core.php b/includes/phpsniff/phpSniff.core.php new file mode 100644 index 0000000..4d09242 --- /dev/null +++ b/includes/phpsniff/phpSniff.core.php @@ -0,0 +1,547 @@ + '', + 'browser' => 'Unknown', + 'version' => 0, + 'maj_ver' => 0, + 'min_ver' => 0, + 'letter_ver' => '', + 'javascript' => '0.0', + 'platform' => 'Unknown', + 'os' => 'Unknown', + 'ip' => 'Unknown', + 'cookies' => 'Unknown', // remains for backwards compatability + 'ss_cookies' => 'Unknown', + 'st_cookies' => 'Unknown', + 'language' => '', + 'long_name' => '', + 'gecko' => '', + 'gecko_ver' => '' + ); + + var $_feature_set = array( + 'html' => true, + 'images' => true, + 'frames' => true, + 'tables' => true, + 'java' => true, + 'plugins' => true, + 'iframes' => false, + 'css2' => false, + 'css1' => false, + 'xml' => false, + 'dom' => false, + 'wml' => false, + 'hdml' => false + ); + + var $_quirks = array( + 'must_cache_forms' => false, + 'avoid_popup_windows' => false, + 'cache_ssl_downloads' => false, + 'break_disposition_header' => false, + 'empty_file_input_value' => false, + 'scrollbar_in_way' => false + ); + + var $_get_languages_ran_once = false; + var $_browser_search_regex = '([a-z]+)([0-9]*)([0-9.]*)(up|dn|\+|\-)?'; + var $_language_search_regex = '([a-z-]{2,})'; + + /** + * init + * this method starts the madness + **/ + function init () + { + // collect the ip + $this->_get_ip(); + // run the cookie check routine first + // [note: method only runs if allowed] + $this->_test_cookies(); + // rip the user agent to pieces + $this->_get_browser_info(); + // gecko build + $this->_get_gecko(); + // look for other languages + $this->_get_languages(); + // establish the operating platform + $this->_get_os_info(); + // determine javascript version + $this->_get_javascript(); + // determine current feature set + $this->_get_features(); + // point out any quirks + $this->_get_quirks(); + } + + /** + * property + * @param $p property to return . optional (null returns entire array) + * @return array/string entire array or value of property + **/ + function property ($p=null) + { if($p==null) + { return $this->_browser_info; + } + else + { return $this->_browser_info[strtolower($p)]; + } + } + + /** + * get_property + * alias for property + **/ + function get_property ($p) + { return $this->property($p); + } + + /** + * is + * @param $s string search phrase format = l:lang;b:browser + * @return bool true on success + * ex: $client->is('b:OP5Up'); + **/ + function is ($s) + { // perform language search + if(preg_match('/l:'.$this->_language_search_regex.'/i',$s,$match)) + { if($match) return $this->_perform_language_search($match); + } + // perform browser search + elseif(preg_match('/b:'.$this->_browser_search_regex.'/i',$s,$match)) + { if($match) return $this->_perform_browser_search($match); + } + return false; + } + + /** + * browser_is + * @param $s string search phrase for browser + * @return bool true on success + * ex: $client->browser_is('OP5Up'); + **/ + function browser_is ($s) + { preg_match('/'.$this->_browser_search_regex.'/i',$s,$match); + if($match) return $this->_perform_browser_search($match); + } + + /** + * language_is + * @param $s string search phrase for language + * @return bool true on success + * ex: $client->language_is('en-US'); + **/ + function language_is ($s) + { preg_match('/'.$this->_language_search_regex.'/i',$s,$match); + if($match) return $this->_perform_language_search($match); + } + + /** + * has_feature + * @param $s string feature we're checking on + * @return bool true on success + * ex: $client->has_feature('html'); + **/ + function has_feature ($s) + { return $this->_feature_set[$s]; + } + + /** + * has_quirk + * @param $s string quirk we're looking for + * @return bool true on success + * ex: $client->has_quirk('avoid_popup_windows'); + **/ + function has_quirk ($s) + { return $this->_quirks[$s]; + } + + /** + * _perform_browser_search + * @param $data string what we're searching for + * @return bool true on success + * @private + **/ + function _perform_browser_search ($data) + { $search = array(); + $search['phrase'] = isset($data[0]) ? $data[0] : ''; + $search['name'] = isset($data[1]) ? strtolower($data[1]) : ''; + $search['maj_ver'] = isset($data[2]) ? $data[2] : ''; + $search['min_ver'] = isset($data[3]) ? $data[3] : ''; + $search['direction'] = isset($data[4]) ? strtolower($data[4]) : ''; + + $looking_for = $search['maj_ver'].$search['min_ver']; + if($search['name'] == 'aol' || $search['name'] == 'webtv') + { return stristr($this->_browser_info['ua'],$search['name']); + } + elseif($this->_browser_info['browser'] == $search['name']) + { $majv = $search['maj_ver'] ? $this->_browser_info['maj_ver'] : ''; + $minv = $search['min_ver'] ? $this->_browser_info['min_ver'] : ''; + $what_we_are = $majv.$minv; + if(($search['direction'] == 'up' || $search['direction'] == '+') + && ($what_we_are >= $looking_for)) + { return true; + } + elseif(($search['direction'] == 'dn' || $search['direction'] == '-') + && ($what_we_are <= $looking_for)) + { return true; + } + elseif($what_we_are == $looking_for) + { return true; + } + } + return false; + } + + function _perform_language_search ($data) + { // if we've not grabbed the languages, then do so. + $this->_get_languages(); + return stristr($this->_browser_info['language'],$data[1]); + } + + function _get_languages () + { // capture available languages and insert into container + if(!$this->_get_languages_ran_once) + { if($languages = getenv('HTTP_ACCEPT_LANGUAGE')) + { $languages = preg_replace('/(;q=[0-9]+.[0-9]+)/i','',$languages); + } + else + { $languages = $this->_default_language; + } + $this->_set_browser('language',$languages); + $this->_get_languages_ran_once = true; + } + } + + function _get_os_info () + { // regexes to use + $regex_windows = '/([^dar]win[dows]*)[\s]?([0-9a-z]*)[\w\s]?([a-z0-9.]*)/i'; + $regex_mac = '/(68[k0]{1,3})|(ppc mac os x)|([p\S]{1,5}pc)|(darwin)/i'; + $regex_os2 = '/os\/2|ibm-webexplorer/i'; + $regex_sunos = '/(sun|i86)[os\s]*([0-9]*)/i'; + $regex_irix = '/(irix)[\s]*([0-9]*)/i'; + $regex_hpux = '/(hp-ux)[\s]*([0-9]*)/i'; + $regex_aix = '/aix([0-9]*)/i'; + $regex_dec = '/dec|osfl|alphaserver|ultrix|alphastation/i'; + $regex_vms = '/vax|openvms/i'; + $regex_sco = '/sco|unix_sv/i'; + $regex_linux = '/x11|inux/i'; + $regex_bsd = '/(free)?(bsd)/i'; + $regex_amiga = '/amiga[os]?/i'; + + // look for Windows Box + if(preg_match_all($regex_windows,$this->_browser_info['ua'],$match)) + { /** Windows has some of the most ridiculous HTTP_USER_AGENT strings */ + //$match[1][count($match[0])-1]; + $v = $match[2][count($match[0])-1]; + $v2 = $match[3][count($match[0])-1]; + // Establish NT 5.1 as Windows XP + if(stristr($v,'NT') && $v2 == 5.1) $v = 'xp'; + // Establish NT 5.0 and Windows 2000 as win2k + elseif($v == '2000') $v = '2k'; + elseif(stristr($v,'NT') && $v2 == 5.0) $v = '2k'; + // Establish 9x 4.90 as Windows 98 + elseif(stristr($v,'9x') && $v2 == 4.9) $v = '98'; + // See if we're running windows 3.1 + elseif($v.$v2 == '16bit') $v = '31'; + // otherwise display as is (31,95,98,NT,ME,XP) + else $v .= $v2; + // update browser info container array + if(empty($v)) $v = 'win'; + $this->_set_browser('os',strtolower($v)); + $this->_set_browser('platform','win'); + } + // look for amiga OS + elseif(preg_match($regex_amiga,$this->_browser_info['ua'],$match)) + { $this->_set_browser('platform','amiga'); + if(stristr($this->_browser_info['ua'],'morphos')) { + // checking for MorphOS + $this->_set_browser('os','morphos'); + } elseif(stristr($this->_browser_info['ua'],'mc680x0')) { + // checking for MC680x0 + $this->_set_browser('os','mc680x0'); + } elseif(stristr($this->_browser_info['ua'],'ppc')) { + // checking for PPC + $this->_set_browser('os','ppc'); + } elseif(preg_match('/(AmigaOS [\.1-9]?)/i',$this->_browser_info['ua'],$match)) { + // checking for AmigaOS version string + $this->_set_browser('os',$match[1]); + } + } + // look for OS2 + elseif( preg_match($regex_os2,$this->_browser_info['ua'])) + { $this->_set_browser('os','os2'); + $this->_set_browser('platform','os2'); + } + // look for mac + // sets: platform = mac ; os = 68k or ppc + elseif( preg_match($regex_mac,$this->_browser_info['ua'],$match) ) + { $this->_set_browser('platform','mac'); + $os = !empty($match[1]) ? '68k' : ''; + $os = !empty($match[2]) ? 'osx' : $os; + $os = !empty($match[3]) ? 'ppc' : $os; + $os = !empty($match[4]) ? 'osx' : $os; + $this->_set_browser('os',$os); + } + // look for *nix boxes + // sunos sets: platform = *nix ; os = sun|sun4|sun5|suni86 + elseif(preg_match($regex_sunos,$this->_browser_info['ua'],$match)) + { $this->_set_browser('platform','*nix'); + if(!stristr('sun',$match[1])) $match[1] = 'sun'.$match[1]; + $this->_set_browser('os',$match[1].$match[2]); + } + // irix sets: platform = *nix ; os = irix|irix5|irix6|... + elseif(preg_match($regex_irix,$this->_browser_info['ua'],$match)) + { $this->_set_browser('platform','*nix'); + $this->_set_browser('os',$match[1].$match[2]); + } + // hp-ux sets: platform = *nix ; os = hpux9|hpux10|... + elseif(preg_match($regex_hpux,$this->_browser_info['ua'],$match)) + { $this->_set_browser('platform','*nix'); + $match[1] = str_replace('-','',$match[1]); + $match[2] = (int) $match[2]; + $this->_set_browser('os',$match[1].$match[2]); + } + // aix sets: platform = *nix ; os = aix|aix1|aix2|aix3|... + elseif(preg_match($regex_aix,$this->_browser_info['ua'],$match)) + { $this->_set_browser('platform','*nix'); + $this->_set_browser('os','aix'.$match[1]); + } + // dec sets: platform = *nix ; os = dec + elseif(preg_match($regex_dec,$this->_browser_info['ua'],$match)) + { $this->_set_browser('platform','*nix'); + $this->_set_browser('os','dec'); + } + // vms sets: platform = *nix ; os = vms + elseif(preg_match($regex_vms,$this->_browser_info['ua'],$match)) + { $this->_set_browser('platform','*nix'); + $this->_set_browser('os','vms'); + } + // sco sets: platform = *nix ; os = sco + elseif(preg_match($regex_sco,$this->_browser_info['ua'],$match)) + { $this->_set_browser('platform','*nix'); + $this->_set_browser('os','sco'); + } + // unixware sets: platform = *nix ; os = unixware + elseif(stristr($this->_browser_info['ua'],'unix_system_v')) + { $this->_set_browser('platform','*nix'); + $this->_set_browser('os','unixware'); + } + // mpras sets: platform = *nix ; os = mpras + elseif(stristr($this->_browser_info['ua'],'ncr')) + { $this->_set_browser('platform','*nix'); + $this->_set_browser('os','mpras'); + } + // reliant sets: platform = *nix ; os = reliant + elseif(stristr($this->_browser_info['ua'],'reliantunix')) + { $this->_set_browser('platform','*nix'); + $this->_set_browser('os','reliant'); + } + // sinix sets: platform = *nix ; os = sinix + elseif(stristr($this->_browser_info['ua'],'sinix')) + { $this->_set_browser('platform','*nix'); + $this->_set_browser('os','sinix'); + } + // bsd sets: platform = *nix ; os = bsd|freebsd + elseif(preg_match($regex_bsd,$this->_browser_info['ua'],$match)) + { $this->_set_browser('platform','*nix'); + $this->_set_browser('os',$match[1].$match[2]); + } + // last one to look for + // linux sets: platform = *nix ; os = linux + elseif(preg_match($regex_linux,$this->_browser_info['ua'],$match)) + { $this->_set_browser('platform','*nix'); + $this->_set_browser('os','linux'); + } + } + + function _get_browser_info () + { $this->_build_regex(); + if(preg_match_all($this->_browser_regex,$this->_browser_info['ua'],$results)) + { // get the position of the last browser found + $count = count($results[0])-1; + // if we're allowing masquerading, revert to the next to last browser found + // if possible, otherwise stay put + if($this->_allow_masquerading && $count > 0) $count--; + // insert findings into the container + $this->_set_browser('browser',$this->_get_short_name($results[1][$count])); + $this->_set_browser('long_name',$results[1][$count]); + $this->_set_browser('maj_ver',$results[2][$count]); + // parse the minor version string and look for alpha chars + preg_match('/([.\0-9]+)?([\.a-z0-9]+)?/i',$results[3][$count],$match); + if(isset($match[1])) { + $this->_set_browser('min_ver',$match[1]); + } else { + $this->_set_browser('min_ver','.0'); + } + if(isset($match[2])) $this->_set_browser('letter_ver',$match[2]); + // insert findings into container + $this->_set_browser('version',$this->_browser_info['maj_ver'].$this->property('min_ver')); + } + } + + function _get_ip () + { if(getenv('HTTP_CLIENT_IP')) + { $ip = getenv('HTTP_CLIENT_IP'); + } + else + { $ip = getenv('REMOTE_ADDR'); + } + $this->_set_browser('ip',$ip); + } + + function _build_regex () + { $browsers = ''; + while(list($k,) = each($this->_browsers)) + { if(!empty($browsers)) $browsers .= "|"; + $browsers .= $k; + } + $version_string = "[\/\sa-z(]*([0-9]+)([\.0-9a-z]+)?"; + $this->_browser_regex = "/($browsers)$version_string/i"; + } + + function _get_short_name ($long_name) + { return $this->_browsers[strtolower($long_name)]; + } + + // medianes :: new test cookie routine + function _test_cookies() + { global $HTTP_COOKIE_VARS; + $cookies = array(); + if(isset($_COOKIE)) { + $cookies = $_COOKIE; + } elseif(isset($HTTP_COOKIE_VARS)) { + $cookies = $HTTP_COOKIE_VARS; + } + if($this->_check_cookies) + { $fp = @fopen($this->_temp_file_path.$this->property('ip'),'r'); + if(!$fp) + { $fp = @fopen($this->_temp_file_path.$this->property('ip'),'a'); + fclose($fp); + setcookie('phpSniff_session','ss',0,'/'); + setcookie('phpSniff_stored','st',time()+3600*24*365,'/'); + $QS=getenv('QUERY_STRING'); + $script_path=getenv('PATH_INFO')?getenv('PATH_INFO'):getenv('SCRIPT_NAME'); + if(is_integer($pos=strpos(strrev($script_path),"php.xedni/"))&&!$pos) { + $script_path=strrev(substr(strrev($script_path),9)); + } + $location='http://'.getenv('SERVER_NAME').$script_path.($QS==''?'':'?'.$QS); + header("Location: $location"); + exit; + } + else + { unlink($this->_temp_file_path.$this->property('ip')); + fclose($fp); + $this->_set_browser('ss_cookies',isset($cookies['phpSniff_session'])?'true':'false'); + $this->_set_browser('st_cookies',isset($cookies['phpSniff_stored'])?'true':'false'); + // delete the old cookies + setcookie('phpSniff_session','',0,'/'); + setcookie('phpSniff_stored','',0,'/'); + + } + } + } + + function _get_javascript() + { $set=false; + // see if we have any matches + while(list($version,$browser) = each($this->_javascript_versions)) + { $browser = explode(',',$browser); + while(list(,$search) = each($browser)) + { if($this->is('b:'.$search)) + { $this->_set_browser('javascript',$version); + $set = true; + break; + } + } + if($set) break; + } + } + + function _get_features () + { while(list($feature,$browser) = each($this->_browser_features)) + { $browser = explode(',',$browser); + while(list(,$search) = each($browser)) + { if($this->browser_is($search)) + { $this->_set_feature($feature); + break; + } + } + } + } + + function _get_quirks () + { while(list($quirk,$browser) = each($this->_browser_quirks)) + { $browser = explode(',',$browser); + while(list(,$search) = each($browser)) + { if($this->browser_is($search)) + { $this->_set_quirk($quirk); + break; + } + } + } + } + + function _get_gecko () + { if(preg_match('/gecko\/([0-9]+)/i',$this->property('ua'),$match)) + { $this->_set_browser('gecko',$match[1]); + if (preg_match('/rv[: ]?([0-9a-z.+]+)/i',$this->property('ua'),$mozv)) { + // mozilla release + $this->_set_browser('gecko_ver',$mozv[1]); + } elseif (preg_match('/(m[0-9]+)/i',$this->property('ua'),$mozv)) { + // mozilla milestone version + $this->_set_browser('gecko_ver',$mozv[1]); + } + // if this is a mozilla browser, get the rv: information + if($this->browser_is($this->_get_short_name('mozilla'))) { + if( !empty( $mozv[1] ) && preg_match('/([0-9]+)([\.0-9]+)([a-z0-9+]?)/i',$mozv[1],$match)) { + $this->_set_browser('version',$mozv[1]); + $this->_set_browser('maj_ver',$match[1]); + $this->_set_browser('min_ver',$match[2]); + $this->_set_browser('letter_ver',$match[3]); + } + } + } elseif($this->is('b:'.$this->_get_short_name('mozilla'))) { + // this is probably a netscape browser or compatible + $this->_set_browser('long_name','netscape'); + $this->_set_browser('browser',$this->_get_short_name('netscape')); + } + } + + function _set_browser ($k,$v) + { $this->_browser_info[strtolower($k)] = strtolower($v); + } + + function _set_feature ($k) + { $this->_feature_set[strtolower($k)] = !$this->_feature_set[strtolower($k)]; + } + + function _set_quirk ($k) + { $this->_quirks[strtolower($k)] = true; + } +} +?> diff --git a/includes/simplepie/idn/idna_convert.class.php b/includes/simplepie/idn/idna_convert.class.php new file mode 100644 index 0000000..ed2bae2 --- /dev/null +++ b/includes/simplepie/idn/idna_convert.class.php @@ -0,0 +1,969 @@ + + * @copyright 2004-2007 phlyLabs Berlin, http://phlylabs.de + * @version 0.5.1 + * + */ +class idna_convert +{ + /** + * Holds all relevant mapping tables, loaded from a seperate file on construct + * See RFC3454 for details + * + * @var array + * @access private + */ + var $NP = array(); + + // Internal settings, do not mess with them + var $_punycode_prefix = 'xn--'; + var $_invalid_ucs = 0x80000000; + var $_max_ucs = 0x10FFFF; + var $_base = 36; + var $_tmin = 1; + var $_tmax = 26; + var $_skew = 38; + var $_damp = 700; + var $_initial_bias = 72; + var $_initial_n = 0x80; + var $_sbase = 0xAC00; + var $_lbase = 0x1100; + var $_vbase = 0x1161; + var $_tbase = 0x11A7; + var $_lcount = 19; + var $_vcount = 21; + var $_tcount = 28; + var $_ncount = 588; // _vcount * _tcount + var $_scount = 11172; // _lcount * _tcount * _vcount + var $_error = false; + + // See {@link set_paramter()} for details of how to change the following + // settings from within your script / application + var $_api_encoding = 'utf8'; // Default input charset is UTF-8 + var $_allow_overlong = false; // Overlong UTF-8 encodings are forbidden + var $_strict_mode = false; // Behave strict or not + + // The constructor + function idna_convert($options = false) + { + $this->slast = $this->_sbase + $this->_lcount * $this->_vcount * $this->_tcount; + if (function_exists('file_get_contents')) { + $this->NP = unserialize(file_get_contents(dirname(__FILE__).'/npdata.ser')); + } else { + $this->NP = unserialize(join('', file(dirname(__FILE__).'/npdata.ser'))); + } + // If parameters are given, pass these to the respective method + if (is_array($options)) { + return $this->set_parameter($options); + } + return true; + } + + /** + * Sets a new option value. Available options and values: + * [encoding - Use either UTF-8, UCS4 as array or UCS4 as string as input ('utf8' for UTF-8, + * 'ucs4_string' and 'ucs4_array' respectively for UCS4); The output is always UTF-8] + * [overlong - Unicode does not allow unnecessarily long encodings of chars, + * to allow this, set this parameter to true, else to false; + * default is false.] + * [strict - true: strict mode, good for registration purposes - Causes errors + * on failures; false: loose mode, ideal for "wildlife" applications + * by silently ignoring errors and returning the original input instead + * + * @param mixed Parameter to set (string: single parameter; array of Parameter => Value pairs) + * @param string Value to use (if parameter 1 is a string) + * @return boolean true on success, false otherwise + * @access public + */ + function set_parameter($option, $value = false) + { + if (!is_array($option)) { + $option = array($option => $value); + } + foreach ($option as $k => $v) { + switch ($k) { + case 'encoding': + switch ($v) { + case 'utf8': + case 'ucs4_string': + case 'ucs4_array': + $this->_api_encoding = $v; + break; + default: + $this->_error('Set Parameter: Unknown parameter '.$v.' for option '.$k); + return false; + } + break; + case 'overlong': + $this->_allow_overlong = ($v) ? true : false; + break; + case 'strict': + $this->_strict_mode = ($v) ? true : false; + break; + default: + $this->_error('Set Parameter: Unknown option '.$k); + return false; + } + } + return true; + } + + /** + * Decode a given ACE domain name + * @param string Domain name (ACE string) + * [@param string Desired output encoding, see {@link set_parameter}] + * @return string Decoded Domain name (UTF-8 or UCS-4) + * @access public + */ + function decode($input, $one_time_encoding = false) + { + // Optionally set + if ($one_time_encoding) { + switch ($one_time_encoding) { + case 'utf8': + case 'ucs4_string': + case 'ucs4_array': + break; + default: + $this->_error('Unknown encoding '.$one_time_encoding); + return false; + } + } + // Make sure to drop any newline characters around + $input = trim($input); + + // Negotiate input and try to determine, whether it is a plain string, + // an email address or something like a complete URL + if (strpos($input, '@')) { // Maybe it is an email address + // No no in strict mode + if ($this->_strict_mode) { + $this->_error('Only simple domain name parts can be handled in strict mode'); + return false; + } + list ($email_pref, $input) = explode('@', $input, 2); + $arr = explode('.', $input); + foreach ($arr as $k => $v) { + if (preg_match('!^'.preg_quote($this->_punycode_prefix, '!').'!', $v)) { + $conv = $this->_decode($v); + if ($conv) $arr[$k] = $conv; + } + } + $input = join('.', $arr); + $arr = explode('.', $email_pref); + foreach ($arr as $k => $v) { + if (preg_match('!^'.preg_quote($this->_punycode_prefix, '!').'!', $v)) { + $conv = $this->_decode($v); + if ($conv) $arr[$k] = $conv; + } + } + $email_pref = join('.', $arr); + $return = $email_pref . '@' . $input; + } elseif (preg_match('![:\./]!', $input)) { // Or a complete domain name (with or without paths / parameters) + // No no in strict mode + if ($this->_strict_mode) { + $this->_error('Only simple domain name parts can be handled in strict mode'); + return false; + } + $parsed = parse_url($input); + if (isset($parsed['host'])) { + $arr = explode('.', $parsed['host']); + foreach ($arr as $k => $v) { + $conv = $this->_decode($v); + if ($conv) $arr[$k] = $conv; + } + $parsed['host'] = join('.', $arr); + $return = + (empty($parsed['scheme']) ? '' : $parsed['scheme'].(strtolower($parsed['scheme']) == 'mailto' ? ':' : '://')) + .(empty($parsed['user']) ? '' : $parsed['user'].(empty($parsed['pass']) ? '' : ':'.$parsed['pass']).'@') + .$parsed['host'] + .(empty($parsed['port']) ? '' : ':'.$parsed['port']) + .(empty($parsed['path']) ? '' : $parsed['path']) + .(empty($parsed['query']) ? '' : '?'.$parsed['query']) + .(empty($parsed['fragment']) ? '' : '#'.$parsed['fragment']); + } else { // parse_url seems to have failed, try without it + $arr = explode('.', $input); + foreach ($arr as $k => $v) { + $conv = $this->_decode($v); + $arr[$k] = ($conv) ? $conv : $v; + } + $return = join('.', $arr); + } + } else { // Otherwise we consider it being a pure domain name string + $return = $this->_decode($input); + if (!$return) $return = $input; + } + // The output is UTF-8 by default, other output formats need conversion here + // If one time encoding is given, use this, else the objects property + switch (($one_time_encoding) ? $one_time_encoding : $this->_api_encoding) { + case 'utf8': + return $return; + break; + case 'ucs4_string': + return $this->_ucs4_to_ucs4_string($this->_utf8_to_ucs4($return)); + break; + case 'ucs4_array': + return $this->_utf8_to_ucs4($return); + break; + default: + $this->_error('Unsupported output format'); + return false; + } + } + + /** + * Encode a given UTF-8 domain name + * @param string Domain name (UTF-8 or UCS-4) + * [@param string Desired input encoding, see {@link set_parameter}] + * @return string Encoded Domain name (ACE string) + * @access public + */ + function encode($decoded, $one_time_encoding = false) + { + // Forcing conversion of input to UCS4 array + // If one time encoding is given, use this, else the objects property + switch ($one_time_encoding ? $one_time_encoding : $this->_api_encoding) { + case 'utf8': + $decoded = $this->_utf8_to_ucs4($decoded); + break; + case 'ucs4_string': + $decoded = $this->_ucs4_string_to_ucs4($decoded); + case 'ucs4_array': + break; + default: + $this->_error('Unsupported input format: '.($one_time_encoding ? $one_time_encoding : $this->_api_encoding)); + return false; + } + + // No input, no output, what else did you expect? + if (empty($decoded)) return ''; + + // Anchors for iteration + $last_begin = 0; + // Output string + $output = ''; + foreach ($decoded as $k => $v) { + // Make sure to use just the plain dot + switch($v) { + case 0x3002: + case 0xFF0E: + case 0xFF61: + $decoded[$k] = 0x2E; + // Right, no break here, the above are converted to dots anyway + // Stumbling across an anchoring character + case 0x2E: + case 0x2F: + case 0x3A: + case 0x3F: + case 0x40: + // Neither email addresses nor URLs allowed in strict mode + if ($this->_strict_mode) { + $this->_error('Neither email addresses nor URLs are allowed in strict mode.'); + return false; + } else { + // Skip first char + if ($k) { + $encoded = ''; + $encoded = $this->_encode(array_slice($decoded, $last_begin, (($k)-$last_begin))); + if ($encoded) { + $output .= $encoded; + } else { + $output .= $this->_ucs4_to_utf8(array_slice($decoded, $last_begin, (($k)-$last_begin))); + } + $output .= chr($decoded[$k]); + } + $last_begin = $k + 1; + } + } + } + // Catch the rest of the string + if ($last_begin) { + $inp_len = sizeof($decoded); + $encoded = ''; + $encoded = $this->_encode(array_slice($decoded, $last_begin, (($inp_len)-$last_begin))); + if ($encoded) { + $output .= $encoded; + } else { + $output .= $this->_ucs4_to_utf8(array_slice($decoded, $last_begin, (($inp_len)-$last_begin))); + } + return $output; + } else { + if ($output = $this->_encode($decoded)) { + return $output; + } else { + return $this->_ucs4_to_utf8($decoded); + } + } + } + + /** + * Use this method to get the last error ocurred + * @param void + * @return string The last error, that occured + * @access public + */ + function get_last_error() + { + return $this->_error; + } + + /** + * The actual decoding algorithm + * @access private + */ + function _decode($encoded) + { + // We do need to find the Punycode prefix + if (!preg_match('!^'.preg_quote($this->_punycode_prefix, '!').'!', $encoded)) { + $this->_error('This is not a punycode string'); + return false; + } + $encode_test = preg_replace('!^'.preg_quote($this->_punycode_prefix, '!').'!', '', $encoded); + // If nothing left after removing the prefix, it is hopeless + if (!$encode_test) { + $this->_error('The given encoded string was empty'); + return false; + } + // Find last occurence of the delimiter + $delim_pos = strrpos($encoded, '-'); + if ($delim_pos > strlen($this->_punycode_prefix)) { + for ($k = strlen($this->_punycode_prefix); $k < $delim_pos; ++$k) { + $decoded[] = ord($encoded{$k}); + } + } else { + $decoded = array(); + } + $deco_len = count($decoded); + $enco_len = strlen($encoded); + + // Wandering through the strings; init + $is_first = true; + $bias = $this->_initial_bias; + $idx = 0; + $char = $this->_initial_n; + + for ($enco_idx = ($delim_pos) ? ($delim_pos + 1) : 0; $enco_idx < $enco_len; ++$deco_len) { + for ($old_idx = $idx, $w = 1, $k = $this->_base; 1 ; $k += $this->_base) { + $digit = $this->_decode_digit($encoded{$enco_idx++}); + $idx += $digit * $w; + $t = ($k <= $bias) ? $this->_tmin : + (($k >= $bias + $this->_tmax) ? $this->_tmax : ($k - $bias)); + if ($digit < $t) break; + $w = (int) ($w * ($this->_base - $t)); + } + $bias = $this->_adapt($idx - $old_idx, $deco_len + 1, $is_first); + $is_first = false; + $char += (int) ($idx / ($deco_len + 1)); + $idx %= ($deco_len + 1); + if ($deco_len > 0) { + // Make room for the decoded char + for ($i = $deco_len; $i > $idx; $i--) { + $decoded[$i] = $decoded[($i - 1)]; + } + } + $decoded[$idx++] = $char; + } + return $this->_ucs4_to_utf8($decoded); + } + + /** + * The actual encoding algorithm + * @access private + */ + function _encode($decoded) + { + // We cannot encode a domain name containing the Punycode prefix + $extract = strlen($this->_punycode_prefix); + $check_pref = $this->_utf8_to_ucs4($this->_punycode_prefix); + $check_deco = array_slice($decoded, 0, $extract); + + if ($check_pref == $check_deco) { + $this->_error('This is already a punycode string'); + return false; + } + // We will not try to encode strings consisting of basic code points only + $encodable = false; + foreach ($decoded as $k => $v) { + if ($v > 0x7a) { + $encodable = true; + break; + } + } + if (!$encodable) { + $this->_error('The given string does not contain encodable chars'); + return false; + } + + // Do NAMEPREP + $decoded = $this->_nameprep($decoded); + if (!$decoded || !is_array($decoded)) return false; // NAMEPREP failed + + $deco_len = count($decoded); + if (!$deco_len) return false; // Empty array + + $codecount = 0; // How many chars have been consumed + + $encoded = ''; + // Copy all basic code points to output + for ($i = 0; $i < $deco_len; ++$i) { + $test = $decoded[$i]; + // Will match [-0-9a-zA-Z] + if ((0x2F < $test && $test < 0x40) || (0x40 < $test && $test < 0x5B) + || (0x60 < $test && $test <= 0x7B) || (0x2D == $test)) { + $encoded .= chr($decoded[$i]); + $codecount++; + } + } + if ($codecount == $deco_len) return $encoded; // All codepoints were basic ones + + // Start with the prefix; copy it to output + $encoded = $this->_punycode_prefix.$encoded; + + // If we have basic code points in output, add an hyphen to the end + if ($codecount) $encoded .= '-'; + + // Now find and encode all non-basic code points + $is_first = true; + $cur_code = $this->_initial_n; + $bias = $this->_initial_bias; + $delta = 0; + while ($codecount < $deco_len) { + // Find the smallest code point >= the current code point and + // remember the last ouccrence of it in the input + for ($i = 0, $next_code = $this->_max_ucs; $i < $deco_len; $i++) { + if ($decoded[$i] >= $cur_code && $decoded[$i] <= $next_code) { + $next_code = $decoded[$i]; + } + } + + $delta += ($next_code - $cur_code) * ($codecount + 1); + $cur_code = $next_code; + + // Scan input again and encode all characters whose code point is $cur_code + for ($i = 0; $i < $deco_len; $i++) { + if ($decoded[$i] < $cur_code) { + $delta++; + } elseif ($decoded[$i] == $cur_code) { + for ($q = $delta, $k = $this->_base; 1; $k += $this->_base) { + $t = ($k <= $bias) ? $this->_tmin : + (($k >= $bias + $this->_tmax) ? $this->_tmax : $k - $bias); + if ($q < $t) break; + $encoded .= $this->_encode_digit(intval($t + (($q - $t) % ($this->_base - $t)))); //v0.4.5 Changed from ceil() to intval() + $q = (int) (($q - $t) / ($this->_base - $t)); + } + $encoded .= $this->_encode_digit($q); + $bias = $this->_adapt($delta, $codecount+1, $is_first); + $codecount++; + $delta = 0; + $is_first = false; + } + } + $delta++; + $cur_code++; + } + return $encoded; + } + + /** + * Adapt the bias according to the current code point and position + * @access private + */ + function _adapt($delta, $npoints, $is_first) + { + $delta = intval($is_first ? ($delta / $this->_damp) : ($delta / 2)); + $delta += intval($delta / $npoints); + for ($k = 0; $delta > (($this->_base - $this->_tmin) * $this->_tmax) / 2; $k += $this->_base) { + $delta = intval($delta / ($this->_base - $this->_tmin)); + } + return intval($k + ($this->_base - $this->_tmin + 1) * $delta / ($delta + $this->_skew)); + } + + /** + * Encoding a certain digit + * @access private + */ + function _encode_digit($d) + { + return chr($d + 22 + 75 * ($d < 26)); + } + + /** + * Decode a certain digit + * @access private + */ + function _decode_digit($cp) + { + $cp = ord($cp); + return ($cp - 48 < 10) ? $cp - 22 : (($cp - 65 < 26) ? $cp - 65 : (($cp - 97 < 26) ? $cp - 97 : $this->_base)); + } + + /** + * Internal error handling method + * @access private + */ + function _error($error = '') + { + $this->_error = $error; + } + + /** + * Do Nameprep according to RFC3491 and RFC3454 + * @param array Unicode Characters + * @return string Unicode Characters, Nameprep'd + * @access private + */ + function _nameprep($input) + { + $output = array(); + $error = false; + // + // Mapping + // Walking through the input array, performing the required steps on each of + // the input chars and putting the result into the output array + // While mapping required chars we apply the cannonical ordering + foreach ($input as $v) { + // Map to nothing == skip that code point + if (in_array($v, $this->NP['map_nothing'])) continue; + + // Try to find prohibited input + if (in_array($v, $this->NP['prohibit']) || in_array($v, $this->NP['general_prohibited'])) { + $this->_error('NAMEPREP: Prohibited input U+'.sprintf('%08X', $v)); + return false; + } + foreach ($this->NP['prohibit_ranges'] as $range) { + if ($range[0] <= $v && $v <= $range[1]) { + $this->_error('NAMEPREP: Prohibited input U+'.sprintf('%08X', $v)); + return false; + } + } + // + // Hangul syllable decomposition + if (0xAC00 <= $v && $v <= 0xD7AF) { + foreach ($this->_hangul_decompose($v) as $out) { + $output[] = (int) $out; + } + // There's a decomposition mapping for that code point + } elseif (isset($this->NP['replacemaps'][$v])) { + foreach ($this->_apply_cannonical_ordering($this->NP['replacemaps'][$v]) as $out) { + $output[] = (int) $out; + } + } else { + $output[] = (int) $v; + } + } + // Before applying any Combining, try to rearrange any Hangul syllables + $output = $this->_hangul_compose($output); + // + // Combine code points + // + $last_class = 0; + $last_starter = 0; + $out_len = count($output); + for ($i = 0; $i < $out_len; ++$i) { + $class = $this->_get_combining_class($output[$i]); + if ((!$last_class || $last_class > $class) && $class) { + // Try to match + $seq_len = $i - $last_starter; + $out = $this->_combine(array_slice($output, $last_starter, $seq_len)); + // On match: Replace the last starter with the composed character and remove + // the now redundant non-starter(s) + if ($out) { + $output[$last_starter] = $out; + if (count($out) != $seq_len) { + for ($j = $i+1; $j < $out_len; ++$j) { + $output[$j-1] = $output[$j]; + } + unset($output[$out_len]); + } + // Rewind the for loop by one, since there can be more possible compositions + $i--; + $out_len--; + $last_class = ($i == $last_starter) ? 0 : $this->_get_combining_class($output[$i-1]); + continue; + } + } + // The current class is 0 + if (!$class) $last_starter = $i; + $last_class = $class; + } + return $output; + } + + /** + * Decomposes a Hangul syllable + * (see http://www.unicode.org/unicode/reports/tr15/#Hangul + * @param integer 32bit UCS4 code point + * @return array Either Hangul Syllable decomposed or original 32bit value as one value array + * @access private + */ + function _hangul_decompose($char) + { + $sindex = (int) $char - $this->_sbase; + if ($sindex < 0 || $sindex >= $this->_scount) { + return array($char); + } + $result = array(); + $result[] = (int) $this->_lbase + $sindex / $this->_ncount; + $result[] = (int) $this->_vbase + ($sindex % $this->_ncount) / $this->_tcount; + $T = intval($this->_tbase + $sindex % $this->_tcount); + if ($T != $this->_tbase) $result[] = $T; + return $result; + } + /** + * Ccomposes a Hangul syllable + * (see http://www.unicode.org/unicode/reports/tr15/#Hangul + * @param array Decomposed UCS4 sequence + * @return array UCS4 sequence with syllables composed + * @access private + */ + function _hangul_compose($input) + { + $inp_len = count($input); + if (!$inp_len) return array(); + $result = array(); + $last = (int) $input[0]; + $result[] = $last; // copy first char from input to output + + for ($i = 1; $i < $inp_len; ++$i) { + $char = (int) $input[$i]; + $sindex = $last - $this->_sbase; + $lindex = $last - $this->_lbase; + $vindex = $char - $this->_vbase; + $tindex = $char - $this->_tbase; + // Find out, whether two current characters are LV and T + if (0 <= $sindex && $sindex < $this->_scount && ($sindex % $this->_tcount == 0) + && 0 <= $tindex && $tindex <= $this->_tcount) { + // create syllable of form LVT + $last += $tindex; + $result[(count($result) - 1)] = $last; // reset last + continue; // discard char + } + // Find out, whether two current characters form L and V + if (0 <= $lindex && $lindex < $this->_lcount && 0 <= $vindex && $vindex < $this->_vcount) { + // create syllable of form LV + $last = (int) $this->_sbase + ($lindex * $this->_vcount + $vindex) * $this->_tcount; + $result[(count($result) - 1)] = $last; // reset last + continue; // discard char + } + // if neither case was true, just add the character + $last = $char; + $result[] = $char; + } + return $result; + } + + /** + * Returns the combining class of a certain wide char + * @param integer Wide char to check (32bit integer) + * @return integer Combining class if found, else 0 + * @access private + */ + function _get_combining_class($char) + { + return isset($this->NP['norm_combcls'][$char]) ? $this->NP['norm_combcls'][$char] : 0; + } + + /** + * Apllies the cannonical ordering of a decomposed UCS4 sequence + * @param array Decomposed UCS4 sequence + * @return array Ordered USC4 sequence + * @access private + */ + function _apply_cannonical_ordering($input) + { + $swap = true; + $size = count($input); + while ($swap) { + $swap = false; + $last = $this->_get_combining_class(intval($input[0])); + for ($i = 0; $i < $size-1; ++$i) { + $next = $this->_get_combining_class(intval($input[$i+1])); + if ($next != 0 && $last > $next) { + // Move item leftward until it fits + for ($j = $i + 1; $j > 0; --$j) { + if ($this->_get_combining_class(intval($input[$j-1])) <= $next) break; + $t = intval($input[$j]); + $input[$j] = intval($input[$j-1]); + $input[$j-1] = $t; + $swap = true; + } + // Reentering the loop looking at the old character again + $next = $last; + } + $last = $next; + } + } + return $input; + } + + /** + * Do composition of a sequence of starter and non-starter + * @param array UCS4 Decomposed sequence + * @return array Ordered USC4 sequence + * @access private + */ + function _combine($input) + { + $inp_len = count($input); + foreach ($this->NP['replacemaps'] as $np_src => $np_target) { + if ($np_target[0] != $input[0]) continue; + if (count($np_target) != $inp_len) continue; + $hit = false; + foreach ($input as $k2 => $v2) { + if ($v2 == $np_target[$k2]) { + $hit = true; + } else { + $hit = false; + break; + } + } + if ($hit) return $np_src; + } + return false; + } + + /** + * This converts an UTF-8 encoded string to its UCS-4 representation + * By talking about UCS-4 "strings" we mean arrays of 32bit integers representing + * each of the "chars". This is due to PHP not being able to handle strings with + * bit depth different from 8. This apllies to the reverse method _ucs4_to_utf8(), too. + * The following UTF-8 encodings are supported: + * bytes bits representation + * 1 7 0xxxxxxx + * 2 11 110xxxxx 10xxxxxx + * 3 16 1110xxxx 10xxxxxx 10xxxxxx + * 4 21 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + * 5 26 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + * 6 31 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + * Each x represents a bit that can be used to store character data. + * The five and six byte sequences are part of Annex D of ISO/IEC 10646-1:2000 + * @access private + */ + function _utf8_to_ucs4($input) + { + $output = array(); + $out_len = 0; + $inp_len = strlen($input); + $mode = 'next'; + $test = 'none'; + for ($k = 0; $k < $inp_len; ++$k) { + $v = ord($input{$k}); // Extract byte from input string + + if ($v < 128) { // We found an ASCII char - put into stirng as is + $output[$out_len] = $v; + ++$out_len; + if ('add' == $mode) { + $this->_error('Conversion from UTF-8 to UCS-4 failed: malformed input at byte '.$k); + return false; + } + continue; + } + if ('next' == $mode) { // Try to find the next start byte; determine the width of the Unicode char + $start_byte = $v; + $mode = 'add'; + $test = 'range'; + if ($v >> 5 == 6) { // &110xxxxx 10xxxxx + $next_byte = 0; // Tells, how many times subsequent bitmasks must rotate 6bits to the left + $v = ($v - 192) << 6; + } elseif ($v >> 4 == 14) { // &1110xxxx 10xxxxxx 10xxxxxx + $next_byte = 1; + $v = ($v - 224) << 12; + } elseif ($v >> 3 == 30) { // &11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + $next_byte = 2; + $v = ($v - 240) << 18; + } elseif ($v >> 2 == 62) { // &111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + $next_byte = 3; + $v = ($v - 248) << 24; + } elseif ($v >> 1 == 126) { // &1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + $next_byte = 4; + $v = ($v - 252) << 30; + } else { + $this->_error('This might be UTF-8, but I don\'t understand it at byte '.$k); + return false; + } + if ('add' == $mode) { + $output[$out_len] = (int) $v; + ++$out_len; + continue; + } + } + if ('add' == $mode) { + if (!$this->_allow_overlong && $test == 'range') { + $test = 'none'; + if (($v < 0xA0 && $start_byte == 0xE0) || ($v < 0x90 && $start_byte == 0xF0) || ($v > 0x8F && $start_byte == 0xF4)) { + $this->_error('Bogus UTF-8 character detected (out of legal range) at byte '.$k); + return false; + } + } + if ($v >> 6 == 2) { // Bit mask must be 10xxxxxx + $v = ($v - 128) << ($next_byte * 6); + $output[($out_len - 1)] += $v; + --$next_byte; + } else { + $this->_error('Conversion from UTF-8 to UCS-4 failed: malformed input at byte '.$k); + return false; + } + if ($next_byte < 0) { + $mode = 'next'; + } + } + } // for + return $output; + } + + /** + * Convert UCS-4 string into UTF-8 string + * See _utf8_to_ucs4() for details + * @access private + */ + function _ucs4_to_utf8($input) + { + $output = ''; + $k = 0; + foreach ($input as $v) { + ++$k; + // $v = ord($v); + if ($v < 128) { // 7bit are transferred literally + $output .= chr($v); + } elseif ($v < (1 << 11)) { // 2 bytes + $output .= chr(192 + ($v >> 6)) . chr(128 + ($v & 63)); + } elseif ($v < (1 << 16)) { // 3 bytes + $output .= chr(224 + ($v >> 12)) . chr(128 + (($v >> 6) & 63)) . chr(128 + ($v & 63)); + } elseif ($v < (1 << 21)) { // 4 bytes + $output .= chr(240 + ($v >> 18)) . chr(128 + (($v >> 12) & 63)) + . chr(128 + (($v >> 6) & 63)) . chr(128 + ($v & 63)); + } elseif ($v < (1 << 26)) { // 5 bytes + $output .= chr(248 + ($v >> 24)) . chr(128 + (($v >> 18) & 63)) + . chr(128 + (($v >> 12) & 63)) . chr(128 + (($v >> 6) & 63)) + . chr(128 + ($v & 63)); + } elseif ($v < (1 << 31)) { // 6 bytes + $output .= chr(252 + ($v >> 30)) . chr(128 + (($v >> 24) & 63)) + . chr(128 + (($v >> 18) & 63)) . chr(128 + (($v >> 12) & 63)) + . chr(128 + (($v >> 6) & 63)) . chr(128 + ($v & 63)); + } else { + $this->_error('Conversion from UCS-4 to UTF-8 failed: malformed input at byte '.$k); + return false; + } + } + return $output; + } + + /** + * Convert UCS-4 array into UCS-4 string + * + * @access private + */ + function _ucs4_to_ucs4_string($input) + { + $output = ''; + // Take array values and split output to 4 bytes per value + // The bit mask is 255, which reads &11111111 + foreach ($input as $v) { + $output .= chr(($v >> 24) & 255).chr(($v >> 16) & 255).chr(($v >> 8) & 255).chr($v & 255); + } + return $output; + } + + /** + * Convert UCS-4 strin into UCS-4 garray + * + * @access private + */ + function _ucs4_string_to_ucs4($input) + { + $output = array(); + $inp_len = strlen($input); + // Input length must be dividable by 4 + if ($inp_len % 4) { + $this->_error('Input UCS4 string is broken'); + return false; + } + // Empty input - return empty output + if (!$inp_len) return $output; + for ($i = 0, $out_len = -1; $i < $inp_len; ++$i) { + // Increment output position every 4 input bytes + if (!($i % 4)) { + $out_len++; + $output[$out_len] = 0; + } + $output[$out_len] += ord($input{$i}) << (8 * (3 - ($i % 4) ) ); + } + return $output; + } +} + +/** +* Adapter class for aligning the API of idna_convert with that of Net_IDNA +* @author Matthias Sommerfeld +*/ +class Net_IDNA_php4 extends idna_convert +{ + /** + * Sets a new option value. Available options and values: + * [encoding - Use either UTF-8, UCS4 as array or UCS4 as string as input ('utf8' for UTF-8, + * 'ucs4_string' and 'ucs4_array' respectively for UCS4); The output is always UTF-8] + * [overlong - Unicode does not allow unnecessarily long encodings of chars, + * to allow this, set this parameter to true, else to false; + * default is false.] + * [strict - true: strict mode, good for registration purposes - Causes errors + * on failures; false: loose mode, ideal for "wildlife" applications + * by silently ignoring errors and returning the original input instead + * + * @param mixed Parameter to set (string: single parameter; array of Parameter => Value pairs) + * @param string Value to use (if parameter 1 is a string) + * @return boolean true on success, false otherwise + * @access public + */ + function setParams($option, $param = false) + { + return $this->IC->set_parameters($option, $param); + } +} + +?> \ No newline at end of file diff --git a/includes/simplepie/idn/npdata.ser b/includes/simplepie/idn/npdata.ser new file mode 100644 index 0000000..d7ce6d0 --- /dev/null +++ b/includes/simplepie/idn/npdata.ser @@ -0,0 +1 @@ +a:6:{s:11:"map_nothing";a:27:{i:0;i:173;i:1;i:847;i:2;i:6150;i:3;i:6155;i:4;i:6156;i:5;i:6157;i:6;i:8203;i:7;i:8204;i:8;i:8205;i:9;i:8288;i:10;i:65024;i:11;i:65025;i:12;i:65026;i:13;i:65027;i:14;i:65028;i:15;i:65029;i:16;i:65030;i:17;i:65031;i:18;i:65032;i:19;i:65033;i:20;i:65034;i:21;i:65035;i:22;i:65036;i:23;i:65037;i:24;i:65038;i:25;i:65039;i:26;i:65279;}s:18:"general_prohibited";a:64:{i:0;i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:7;i:7;i:8;i:8;i:9;i:9;i:10;i:10;i:11;i:11;i:12;i:12;i:13;i:13;i:14;i:14;i:15;i:15;i:16;i:16;i:17;i:17;i:18;i:18;i:19;i:19;i:20;i:20;i:21;i:21;i:22;i:22;i:23;i:23;i:24;i:24;i:25;i:25;i:26;i:26;i:27;i:27;i:28;i:28;i:29;i:29;i:30;i:30;i:31;i:31;i:32;i:32;i:33;i:33;i:34;i:34;i:35;i:35;i:36;i:36;i:37;i:37;i:38;i:38;i:39;i:39;i:40;i:40;i:41;i:41;i:42;i:42;i:43;i:43;i:44;i:44;i:45;i:47;i:46;i:59;i:47;i:60;i:48;i:61;i:49;i:62;i:50;i:63;i:51;i:64;i:52;i:91;i:53;i:92;i:54;i:93;i:55;i:94;i:56;i:95;i:57;i:96;i:58;i:123;i:59;i:124;i:60;i:125;i:61;i:126;i:62;i:127;i:63;i:12290;}s:8:"prohibit";a:84:{i:0;i:160;i:1;i:5760;i:2;i:8192;i:3;i:8193;i:4;i:8194;i:5;i:8195;i:6;i:8196;i:7;i:8197;i:8;i:8198;i:9;i:8199;i:10;i:8200;i:11;i:8201;i:12;i:8202;i:13;i:8203;i:14;i:8239;i:15;i:8287;i:16;i:12288;i:17;i:1757;i:18;i:1807;i:19;i:6158;i:20;i:8204;i:21;i:8205;i:22;i:8232;i:23;i:8233;i:24;i:65279;i:25;i:65529;i:26;i:65530;i:27;i:65531;i:28;i:65532;i:29;i:65534;i:30;i:65535;i:31;i:131070;i:32;i:131071;i:33;i:196606;i:34;i:196607;i:35;i:262142;i:36;i:262143;i:37;i:327678;i:38;i:327679;i:39;i:393214;i:40;i:393215;i:41;i:458750;i:42;i:458751;i:43;i:524286;i:44;i:524287;i:45;i:589822;i:46;i:589823;i:47;i:655358;i:48;i:655359;i:49;i:720894;i:50;i:720895;i:51;i:786430;i:52;i:786431;i:53;i:851966;i:54;i:851967;i:55;i:917502;i:56;i:917503;i:57;i:983038;i:58;i:983039;i:59;i:1048574;i:60;i:1048575;i:61;i:1114110;i:62;i:1114111;i:63;i:65529;i:64;i:65530;i:65;i:65531;i:66;i:65532;i:67;i:65533;i:68;i:832;i:69;i:833;i:70;i:8206;i:71;i:8207;i:72;i:8234;i:73;i:8235;i:74;i:8236;i:75;i:8237;i:76;i:8238;i:77;i:8298;i:78;i:8299;i:79;i:8300;i:80;i:8301;i:81;i:8302;i:82;i:8303;i:83;i:917505;}s:15:"prohibit_ranges";a:10:{i:0;a:2:{i:0;i:128;i:1;i:159;}i:1;a:2:{i:0;i:8288;i:1;i:8303;}i:2;a:2:{i:0;i:119155;i:1;i:119162;}i:3;a:2:{i:0;i:57344;i:1;i:63743;}i:4;a:2:{i:0;i:983040;i:1;i:1048573;}i:5;a:2:{i:0;i:1048576;i:1;i:1114109;}i:6;a:2:{i:0;i:64976;i:1;i:65007;}i:7;a:2:{i:0;i:55296;i:1;i:57343;}i:8;a:2:{i:0;i:12272;i:1;i:12283;}i:9;a:2:{i:0;i:917536;i:1;i:917631;}}s:11:"replacemaps";a:1401:{i:65;a:1:{i:0;i:97;}i:66;a:1:{i:0;i:98;}i:67;a:1:{i:0;i:99;}i:68;a:1:{i:0;i:100;}i:69;a:1:{i:0;i:101;}i:70;a:1:{i:0;i:102;}i:71;a:1:{i:0;i:103;}i:72;a:1:{i:0;i:104;}i:73;a:1:{i:0;i:105;}i:74;a:1:{i:0;i:106;}i:75;a:1:{i:0;i:107;}i:76;a:1:{i:0;i:108;}i:77;a:1:{i:0;i:109;}i:78;a:1:{i:0;i:110;}i:79;a:1:{i:0;i:111;}i:80;a:1:{i:0;i:112;}i:81;a:1:{i:0;i:113;}i:82;a:1:{i:0;i:114;}i:83;a:1:{i:0;i:115;}i:84;a:1:{i:0;i:116;}i:85;a:1:{i:0;i:117;}i:86;a:1:{i:0;i:118;}i:87;a:1:{i:0;i:119;}i:88;a:1:{i:0;i:120;}i:89;a:1:{i:0;i:121;}i:90;a:1:{i:0;i:122;}i:181;a:1:{i:0;i:956;}i:192;a:1:{i:0;i:224;}i:193;a:1:{i:0;i:225;}i:194;a:1:{i:0;i:226;}i:195;a:1:{i:0;i:227;}i:196;a:1:{i:0;i:228;}i:197;a:1:{i:0;i:229;}i:198;a:1:{i:0;i:230;}i:199;a:1:{i:0;i:231;}i:200;a:1:{i:0;i:232;}i:201;a:1:{i:0;i:233;}i:202;a:1:{i:0;i:234;}i:203;a:1:{i:0;i:235;}i:204;a:1:{i:0;i:236;}i:205;a:1:{i:0;i:237;}i:206;a:1:{i:0;i:238;}i:207;a:1:{i:0;i:239;}i:208;a:1:{i:0;i:240;}i:209;a:1:{i:0;i:241;}i:210;a:1:{i:0;i:242;}i:211;a:1:{i:0;i:243;}i:212;a:1:{i:0;i:244;}i:213;a:1:{i:0;i:245;}i:214;a:1:{i:0;i:246;}i:216;a:1:{i:0;i:248;}i:217;a:1:{i:0;i:249;}i:218;a:1:{i:0;i:250;}i:219;a:1:{i:0;i:251;}i:220;a:1:{i:0;i:252;}i:221;a:1:{i:0;i:253;}i:222;a:1:{i:0;i:254;}i:223;a:2:{i:0;i:115;i:1;i:115;}i:256;a:1:{i:0;i:257;}i:258;a:1:{i:0;i:259;}i:260;a:1:{i:0;i:261;}i:262;a:1:{i:0;i:263;}i:264;a:1:{i:0;i:265;}i:266;a:1:{i:0;i:267;}i:268;a:1:{i:0;i:269;}i:270;a:1:{i:0;i:271;}i:272;a:1:{i:0;i:273;}i:274;a:1:{i:0;i:275;}i:276;a:1:{i:0;i:277;}i:278;a:1:{i:0;i:279;}i:280;a:1:{i:0;i:281;}i:282;a:1:{i:0;i:283;}i:284;a:1:{i:0;i:285;}i:286;a:1:{i:0;i:287;}i:288;a:1:{i:0;i:289;}i:290;a:1:{i:0;i:291;}i:292;a:1:{i:0;i:293;}i:294;a:1:{i:0;i:295;}i:296;a:1:{i:0;i:297;}i:298;a:1:{i:0;i:299;}i:300;a:1:{i:0;i:301;}i:302;a:1:{i:0;i:303;}i:304;a:2:{i:0;i:105;i:1;i:775;}i:306;a:1:{i:0;i:307;}i:308;a:1:{i:0;i:309;}i:310;a:1:{i:0;i:311;}i:313;a:1:{i:0;i:314;}i:315;a:1:{i:0;i:316;}i:317;a:1:{i:0;i:318;}i:319;a:1:{i:0;i:320;}i:321;a:1:{i:0;i:322;}i:323;a:1:{i:0;i:324;}i:325;a:1:{i:0;i:326;}i:327;a:1:{i:0;i:328;}i:329;a:2:{i:0;i:700;i:1;i:110;}i:330;a:1:{i:0;i:331;}i:332;a:1:{i:0;i:333;}i:334;a:1:{i:0;i:335;}i:336;a:1:{i:0;i:337;}i:338;a:1:{i:0;i:339;}i:340;a:1:{i:0;i:341;}i:342;a:1:{i:0;i:343;}i:344;a:1:{i:0;i:345;}i:346;a:1:{i:0;i:347;}i:348;a:1:{i:0;i:349;}i:350;a:1:{i:0;i:351;}i:352;a:1:{i:0;i:353;}i:354;a:1:{i:0;i:355;}i:356;a:1:{i:0;i:357;}i:358;a:1:{i:0;i:359;}i:360;a:1:{i:0;i:361;}i:362;a:1:{i:0;i:363;}i:364;a:1:{i:0;i:365;}i:366;a:1:{i:0;i:367;}i:368;a:1:{i:0;i:369;}i:370;a:1:{i:0;i:371;}i:372;a:1:{i:0;i:373;}i:374;a:1:{i:0;i:375;}i:376;a:1:{i:0;i:255;}i:377;a:1:{i:0;i:378;}i:379;a:1:{i:0;i:380;}i:381;a:1:{i:0;i:382;}i:383;a:1:{i:0;i:115;}i:385;a:1:{i:0;i:595;}i:386;a:1:{i:0;i:387;}i:388;a:1:{i:0;i:389;}i:390;a:1:{i:0;i:596;}i:391;a:1:{i:0;i:392;}i:393;a:1:{i:0;i:598;}i:394;a:1:{i:0;i:599;}i:395;a:1:{i:0;i:396;}i:398;a:1:{i:0;i:477;}i:399;a:1:{i:0;i:601;}i:400;a:1:{i:0;i:603;}i:401;a:1:{i:0;i:402;}i:403;a:1:{i:0;i:608;}i:404;a:1:{i:0;i:611;}i:406;a:1:{i:0;i:617;}i:407;a:1:{i:0;i:616;}i:408;a:1:{i:0;i:409;}i:412;a:1:{i:0;i:623;}i:413;a:1:{i:0;i:626;}i:415;a:1:{i:0;i:629;}i:416;a:1:{i:0;i:417;}i:418;a:1:{i:0;i:419;}i:420;a:1:{i:0;i:421;}i:422;a:1:{i:0;i:640;}i:423;a:1:{i:0;i:424;}i:425;a:1:{i:0;i:643;}i:428;a:1:{i:0;i:429;}i:430;a:1:{i:0;i:648;}i:431;a:1:{i:0;i:432;}i:433;a:1:{i:0;i:650;}i:434;a:1:{i:0;i:651;}i:435;a:1:{i:0;i:436;}i:437;a:1:{i:0;i:438;}i:439;a:1:{i:0;i:658;}i:440;a:1:{i:0;i:441;}i:444;a:1:{i:0;i:445;}i:452;a:1:{i:0;i:454;}i:453;a:1:{i:0;i:454;}i:455;a:1:{i:0;i:457;}i:456;a:1:{i:0;i:457;}i:458;a:1:{i:0;i:460;}i:459;a:1:{i:0;i:460;}i:461;a:1:{i:0;i:462;}i:463;a:1:{i:0;i:464;}i:465;a:1:{i:0;i:466;}i:467;a:1:{i:0;i:468;}i:469;a:1:{i:0;i:470;}i:471;a:1:{i:0;i:472;}i:473;a:1:{i:0;i:474;}i:475;a:1:{i:0;i:476;}i:478;a:1:{i:0;i:479;}i:480;a:1:{i:0;i:481;}i:482;a:1:{i:0;i:483;}i:484;a:1:{i:0;i:485;}i:486;a:1:{i:0;i:487;}i:488;a:1:{i:0;i:489;}i:490;a:1:{i:0;i:491;}i:492;a:1:{i:0;i:493;}i:494;a:1:{i:0;i:495;}i:496;a:2:{i:0;i:106;i:1;i:780;}i:497;a:1:{i:0;i:499;}i:498;a:1:{i:0;i:499;}i:500;a:1:{i:0;i:501;}i:502;a:1:{i:0;i:405;}i:503;a:1:{i:0;i:447;}i:504;a:1:{i:0;i:505;}i:506;a:1:{i:0;i:507;}i:508;a:1:{i:0;i:509;}i:510;a:1:{i:0;i:511;}i:512;a:1:{i:0;i:513;}i:514;a:1:{i:0;i:515;}i:516;a:1:{i:0;i:517;}i:518;a:1:{i:0;i:519;}i:520;a:1:{i:0;i:521;}i:522;a:1:{i:0;i:523;}i:524;a:1:{i:0;i:525;}i:526;a:1:{i:0;i:527;}i:528;a:1:{i:0;i:529;}i:530;a:1:{i:0;i:531;}i:532;a:1:{i:0;i:533;}i:534;a:1:{i:0;i:535;}i:536;a:1:{i:0;i:537;}i:538;a:1:{i:0;i:539;}i:540;a:1:{i:0;i:541;}i:542;a:1:{i:0;i:543;}i:544;a:1:{i:0;i:414;}i:546;a:1:{i:0;i:547;}i:548;a:1:{i:0;i:549;}i:550;a:1:{i:0;i:551;}i:552;a:1:{i:0;i:553;}i:554;a:1:{i:0;i:555;}i:556;a:1:{i:0;i:557;}i:558;a:1:{i:0;i:559;}i:560;a:1:{i:0;i:561;}i:562;a:1:{i:0;i:563;}i:837;a:1:{i:0;i:953;}i:890;a:2:{i:0;i:32;i:1;i:953;}i:902;a:1:{i:0;i:940;}i:904;a:1:{i:0;i:941;}i:905;a:1:{i:0;i:942;}i:906;a:1:{i:0;i:943;}i:908;a:1:{i:0;i:972;}i:910;a:1:{i:0;i:973;}i:911;a:1:{i:0;i:974;}i:912;a:3:{i:0;i:953;i:1;i:776;i:2;i:769;}i:913;a:1:{i:0;i:945;}i:914;a:1:{i:0;i:946;}i:915;a:1:{i:0;i:947;}i:916;a:1:{i:0;i:948;}i:917;a:1:{i:0;i:949;}i:918;a:1:{i:0;i:950;}i:919;a:1:{i:0;i:951;}i:920;a:1:{i:0;i:952;}i:921;a:1:{i:0;i:953;}i:922;a:1:{i:0;i:954;}i:923;a:1:{i:0;i:955;}i:924;a:1:{i:0;i:956;}i:925;a:1:{i:0;i:957;}i:926;a:1:{i:0;i:958;}i:927;a:1:{i:0;i:959;}i:928;a:1:{i:0;i:960;}i:929;a:1:{i:0;i:961;}i:931;a:1:{i:0;i:963;}i:932;a:1:{i:0;i:964;}i:933;a:1:{i:0;i:965;}i:934;a:1:{i:0;i:966;}i:935;a:1:{i:0;i:967;}i:936;a:1:{i:0;i:968;}i:937;a:1:{i:0;i:969;}i:938;a:1:{i:0;i:970;}i:939;a:1:{i:0;i:971;}i:944;a:3:{i:0;i:965;i:1;i:776;i:2;i:769;}i:962;a:1:{i:0;i:963;}i:976;a:1:{i:0;i:946;}i:977;a:1:{i:0;i:952;}i:978;a:1:{i:0;i:965;}i:979;a:1:{i:0;i:973;}i:980;a:1:{i:0;i:971;}i:981;a:1:{i:0;i:966;}i:982;a:1:{i:0;i:960;}i:984;a:1:{i:0;i:985;}i:986;a:1:{i:0;i:987;}i:988;a:1:{i:0;i:989;}i:990;a:1:{i:0;i:991;}i:992;a:1:{i:0;i:993;}i:994;a:1:{i:0;i:995;}i:996;a:1:{i:0;i:997;}i:998;a:1:{i:0;i:999;}i:1000;a:1:{i:0;i:1001;}i:1002;a:1:{i:0;i:1003;}i:1004;a:1:{i:0;i:1005;}i:1006;a:1:{i:0;i:1007;}i:1008;a:1:{i:0;i:954;}i:1009;a:1:{i:0;i:961;}i:1010;a:1:{i:0;i:963;}i:1012;a:1:{i:0;i:952;}i:1013;a:1:{i:0;i:949;}i:1024;a:1:{i:0;i:1104;}i:1025;a:1:{i:0;i:1105;}i:1026;a:1:{i:0;i:1106;}i:1027;a:1:{i:0;i:1107;}i:1028;a:1:{i:0;i:1108;}i:1029;a:1:{i:0;i:1109;}i:1030;a:1:{i:0;i:1110;}i:1031;a:1:{i:0;i:1111;}i:1032;a:1:{i:0;i:1112;}i:1033;a:1:{i:0;i:1113;}i:1034;a:1:{i:0;i:1114;}i:1035;a:1:{i:0;i:1115;}i:1036;a:1:{i:0;i:1116;}i:1037;a:1:{i:0;i:1117;}i:1038;a:1:{i:0;i:1118;}i:1039;a:1:{i:0;i:1119;}i:1040;a:1:{i:0;i:1072;}i:1041;a:1:{i:0;i:1073;}i:1042;a:1:{i:0;i:1074;}i:1043;a:1:{i:0;i:1075;}i:1044;a:1:{i:0;i:1076;}i:1045;a:1:{i:0;i:1077;}i:1046;a:1:{i:0;i:1078;}i:1047;a:1:{i:0;i:1079;}i:1048;a:1:{i:0;i:1080;}i:1049;a:1:{i:0;i:1081;}i:1050;a:1:{i:0;i:1082;}i:1051;a:1:{i:0;i:1083;}i:1052;a:1:{i:0;i:1084;}i:1053;a:1:{i:0;i:1085;}i:1054;a:1:{i:0;i:1086;}i:1055;a:1:{i:0;i:1087;}i:1056;a:1:{i:0;i:1088;}i:1057;a:1:{i:0;i:1089;}i:1058;a:1:{i:0;i:1090;}i:1059;a:1:{i:0;i:1091;}i:1060;a:1:{i:0;i:1092;}i:1061;a:1:{i:0;i:1093;}i:1062;a:1:{i:0;i:1094;}i:1063;a:1:{i:0;i:1095;}i:1064;a:1:{i:0;i:1096;}i:1065;a:1:{i:0;i:1097;}i:1066;a:1:{i:0;i:1098;}i:1067;a:1:{i:0;i:1099;}i:1068;a:1:{i:0;i:1100;}i:1069;a:1:{i:0;i:1101;}i:1070;a:1:{i:0;i:1102;}i:1071;a:1:{i:0;i:1103;}i:1120;a:1:{i:0;i:1121;}i:1122;a:1:{i:0;i:1123;}i:1124;a:1:{i:0;i:1125;}i:1126;a:1:{i:0;i:1127;}i:1128;a:1:{i:0;i:1129;}i:1130;a:1:{i:0;i:1131;}i:1132;a:1:{i:0;i:1133;}i:1134;a:1:{i:0;i:1135;}i:1136;a:1:{i:0;i:1137;}i:1138;a:1:{i:0;i:1139;}i:1140;a:1:{i:0;i:1141;}i:1142;a:1:{i:0;i:1143;}i:1144;a:1:{i:0;i:1145;}i:1146;a:1:{i:0;i:1147;}i:1148;a:1:{i:0;i:1149;}i:1150;a:1:{i:0;i:1151;}i:1152;a:1:{i:0;i:1153;}i:1162;a:1:{i:0;i:1163;}i:1164;a:1:{i:0;i:1165;}i:1166;a:1:{i:0;i:1167;}i:1168;a:1:{i:0;i:1169;}i:1170;a:1:{i:0;i:1171;}i:1172;a:1:{i:0;i:1173;}i:1174;a:1:{i:0;i:1175;}i:1176;a:1:{i:0;i:1177;}i:1178;a:1:{i:0;i:1179;}i:1180;a:1:{i:0;i:1181;}i:1182;a:1:{i:0;i:1183;}i:1184;a:1:{i:0;i:1185;}i:1186;a:1:{i:0;i:1187;}i:1188;a:1:{i:0;i:1189;}i:1190;a:1:{i:0;i:1191;}i:1192;a:1:{i:0;i:1193;}i:1194;a:1:{i:0;i:1195;}i:1196;a:1:{i:0;i:1197;}i:1198;a:1:{i:0;i:1199;}i:1200;a:1:{i:0;i:1201;}i:1202;a:1:{i:0;i:1203;}i:1204;a:1:{i:0;i:1205;}i:1206;a:1:{i:0;i:1207;}i:1208;a:1:{i:0;i:1209;}i:1210;a:1:{i:0;i:1211;}i:1212;a:1:{i:0;i:1213;}i:1214;a:1:{i:0;i:1215;}i:1217;a:1:{i:0;i:1218;}i:1219;a:1:{i:0;i:1220;}i:1221;a:1:{i:0;i:1222;}i:1223;a:1:{i:0;i:1224;}i:1225;a:1:{i:0;i:1226;}i:1227;a:1:{i:0;i:1228;}i:1229;a:1:{i:0;i:1230;}i:1232;a:1:{i:0;i:1233;}i:1234;a:1:{i:0;i:1235;}i:1236;a:1:{i:0;i:1237;}i:1238;a:1:{i:0;i:1239;}i:1240;a:1:{i:0;i:1241;}i:1242;a:1:{i:0;i:1243;}i:1244;a:1:{i:0;i:1245;}i:1246;a:1:{i:0;i:1247;}i:1248;a:1:{i:0;i:1249;}i:1250;a:1:{i:0;i:1251;}i:1252;a:1:{i:0;i:1253;}i:1254;a:1:{i:0;i:1255;}i:1256;a:1:{i:0;i:1257;}i:1258;a:1:{i:0;i:1259;}i:1260;a:1:{i:0;i:1261;}i:1262;a:1:{i:0;i:1263;}i:1264;a:1:{i:0;i:1265;}i:1266;a:1:{i:0;i:1267;}i:1268;a:1:{i:0;i:1269;}i:1272;a:1:{i:0;i:1273;}i:1280;a:1:{i:0;i:1281;}i:1282;a:1:{i:0;i:1283;}i:1284;a:1:{i:0;i:1285;}i:1286;a:1:{i:0;i:1287;}i:1288;a:1:{i:0;i:1289;}i:1290;a:1:{i:0;i:1291;}i:1292;a:1:{i:0;i:1293;}i:1294;a:1:{i:0;i:1295;}i:1329;a:1:{i:0;i:1377;}i:1330;a:1:{i:0;i:1378;}i:1331;a:1:{i:0;i:1379;}i:1332;a:1:{i:0;i:1380;}i:1333;a:1:{i:0;i:1381;}i:1334;a:1:{i:0;i:1382;}i:1335;a:1:{i:0;i:1383;}i:1336;a:1:{i:0;i:1384;}i:1337;a:1:{i:0;i:1385;}i:1338;a:1:{i:0;i:1386;}i:1339;a:1:{i:0;i:1387;}i:1340;a:1:{i:0;i:1388;}i:1341;a:1:{i:0;i:1389;}i:1342;a:1:{i:0;i:1390;}i:1343;a:1:{i:0;i:1391;}i:1344;a:1:{i:0;i:1392;}i:1345;a:1:{i:0;i:1393;}i:1346;a:1:{i:0;i:1394;}i:1347;a:1:{i:0;i:1395;}i:1348;a:1:{i:0;i:1396;}i:1349;a:1:{i:0;i:1397;}i:1350;a:1:{i:0;i:1398;}i:1351;a:1:{i:0;i:1399;}i:1352;a:1:{i:0;i:1400;}i:1353;a:1:{i:0;i:1401;}i:1354;a:1:{i:0;i:1402;}i:1355;a:1:{i:0;i:1403;}i:1356;a:1:{i:0;i:1404;}i:1357;a:1:{i:0;i:1405;}i:1358;a:1:{i:0;i:1406;}i:1359;a:1:{i:0;i:1407;}i:1360;a:1:{i:0;i:1408;}i:1361;a:1:{i:0;i:1409;}i:1362;a:1:{i:0;i:1410;}i:1363;a:1:{i:0;i:1411;}i:1364;a:1:{i:0;i:1412;}i:1365;a:1:{i:0;i:1413;}i:1366;a:1:{i:0;i:1414;}i:1415;a:2:{i:0;i:1381;i:1;i:1410;}i:7680;a:1:{i:0;i:7681;}i:7682;a:1:{i:0;i:7683;}i:7684;a:1:{i:0;i:7685;}i:7686;a:1:{i:0;i:7687;}i:7688;a:1:{i:0;i:7689;}i:7690;a:1:{i:0;i:7691;}i:7692;a:1:{i:0;i:7693;}i:7694;a:1:{i:0;i:7695;}i:7696;a:1:{i:0;i:7697;}i:7698;a:1:{i:0;i:7699;}i:7700;a:1:{i:0;i:7701;}i:7702;a:1:{i:0;i:7703;}i:7704;a:1:{i:0;i:7705;}i:7706;a:1:{i:0;i:7707;}i:7708;a:1:{i:0;i:7709;}i:7710;a:1:{i:0;i:7711;}i:7712;a:1:{i:0;i:7713;}i:7714;a:1:{i:0;i:7715;}i:7716;a:1:{i:0;i:7717;}i:7718;a:1:{i:0;i:7719;}i:7720;a:1:{i:0;i:7721;}i:7722;a:1:{i:0;i:7723;}i:7724;a:1:{i:0;i:7725;}i:7726;a:1:{i:0;i:7727;}i:7728;a:1:{i:0;i:7729;}i:7730;a:1:{i:0;i:7731;}i:7732;a:1:{i:0;i:7733;}i:7734;a:1:{i:0;i:7735;}i:7736;a:1:{i:0;i:7737;}i:7738;a:1:{i:0;i:7739;}i:7740;a:1:{i:0;i:7741;}i:7742;a:1:{i:0;i:7743;}i:7744;a:1:{i:0;i:7745;}i:7746;a:1:{i:0;i:7747;}i:7748;a:1:{i:0;i:7749;}i:7750;a:1:{i:0;i:7751;}i:7752;a:1:{i:0;i:7753;}i:7754;a:1:{i:0;i:7755;}i:7756;a:1:{i:0;i:7757;}i:7758;a:1:{i:0;i:7759;}i:7760;a:1:{i:0;i:7761;}i:7762;a:1:{i:0;i:7763;}i:7764;a:1:{i:0;i:7765;}i:7766;a:1:{i:0;i:7767;}i:7768;a:1:{i:0;i:7769;}i:7770;a:1:{i:0;i:7771;}i:7772;a:1:{i:0;i:7773;}i:7774;a:1:{i:0;i:7775;}i:7776;a:1:{i:0;i:7777;}i:7778;a:1:{i:0;i:7779;}i:7780;a:1:{i:0;i:7781;}i:7782;a:1:{i:0;i:7783;}i:7784;a:1:{i:0;i:7785;}i:7786;a:1:{i:0;i:7787;}i:7788;a:1:{i:0;i:7789;}i:7790;a:1:{i:0;i:7791;}i:7792;a:1:{i:0;i:7793;}i:7794;a:1:{i:0;i:7795;}i:7796;a:1:{i:0;i:7797;}i:7798;a:1:{i:0;i:7799;}i:7800;a:1:{i:0;i:7801;}i:7802;a:1:{i:0;i:7803;}i:7804;a:1:{i:0;i:7805;}i:7806;a:1:{i:0;i:7807;}i:7808;a:1:{i:0;i:7809;}i:7810;a:1:{i:0;i:7811;}i:7812;a:1:{i:0;i:7813;}i:7814;a:1:{i:0;i:7815;}i:7816;a:1:{i:0;i:7817;}i:7818;a:1:{i:0;i:7819;}i:7820;a:1:{i:0;i:7821;}i:7822;a:1:{i:0;i:7823;}i:7824;a:1:{i:0;i:7825;}i:7826;a:1:{i:0;i:7827;}i:7828;a:1:{i:0;i:7829;}i:7830;a:2:{i:0;i:104;i:1;i:817;}i:7831;a:2:{i:0;i:116;i:1;i:776;}i:7832;a:2:{i:0;i:119;i:1;i:778;}i:7833;a:2:{i:0;i:121;i:1;i:778;}i:7834;a:2:{i:0;i:97;i:1;i:702;}i:7835;a:1:{i:0;i:7777;}i:7840;a:1:{i:0;i:7841;}i:7842;a:1:{i:0;i:7843;}i:7844;a:1:{i:0;i:7845;}i:7846;a:1:{i:0;i:7847;}i:7848;a:1:{i:0;i:7849;}i:7850;a:1:{i:0;i:7851;}i:7852;a:1:{i:0;i:7853;}i:7854;a:1:{i:0;i:7855;}i:7856;a:1:{i:0;i:7857;}i:7858;a:1:{i:0;i:7859;}i:7860;a:1:{i:0;i:7861;}i:7862;a:1:{i:0;i:7863;}i:7864;a:1:{i:0;i:7865;}i:7866;a:1:{i:0;i:7867;}i:7868;a:1:{i:0;i:7869;}i:7870;a:1:{i:0;i:7871;}i:7872;a:1:{i:0;i:7873;}i:7874;a:1:{i:0;i:7875;}i:7876;a:1:{i:0;i:7877;}i:7878;a:1:{i:0;i:7879;}i:7880;a:1:{i:0;i:7881;}i:7882;a:1:{i:0;i:7883;}i:7884;a:1:{i:0;i:7885;}i:7886;a:1:{i:0;i:7887;}i:7888;a:1:{i:0;i:7889;}i:7890;a:1:{i:0;i:7891;}i:7892;a:1:{i:0;i:7893;}i:7894;a:1:{i:0;i:7895;}i:7896;a:1:{i:0;i:7897;}i:7898;a:1:{i:0;i:7899;}i:7900;a:1:{i:0;i:7901;}i:7902;a:1:{i:0;i:7903;}i:7904;a:1:{i:0;i:7905;}i:7906;a:1:{i:0;i:7907;}i:7908;a:1:{i:0;i:7909;}i:7910;a:1:{i:0;i:7911;}i:7912;a:1:{i:0;i:7913;}i:7914;a:1:{i:0;i:7915;}i:7916;a:1:{i:0;i:7917;}i:7918;a:1:{i:0;i:7919;}i:7920;a:1:{i:0;i:7921;}i:7922;a:1:{i:0;i:7923;}i:7924;a:1:{i:0;i:7925;}i:7926;a:1:{i:0;i:7927;}i:7928;a:1:{i:0;i:7929;}i:7944;a:1:{i:0;i:7936;}i:7945;a:1:{i:0;i:7937;}i:7946;a:1:{i:0;i:7938;}i:7947;a:1:{i:0;i:7939;}i:7948;a:1:{i:0;i:7940;}i:7949;a:1:{i:0;i:7941;}i:7950;a:1:{i:0;i:7942;}i:7951;a:1:{i:0;i:7943;}i:7960;a:1:{i:0;i:7952;}i:7961;a:1:{i:0;i:7953;}i:7962;a:1:{i:0;i:7954;}i:7963;a:1:{i:0;i:7955;}i:7964;a:1:{i:0;i:7956;}i:7965;a:1:{i:0;i:7957;}i:7976;a:1:{i:0;i:7968;}i:7977;a:1:{i:0;i:7969;}i:7978;a:1:{i:0;i:7970;}i:7979;a:1:{i:0;i:7971;}i:7980;a:1:{i:0;i:7972;}i:7981;a:1:{i:0;i:7973;}i:7982;a:1:{i:0;i:7974;}i:7983;a:1:{i:0;i:7975;}i:7992;a:1:{i:0;i:7984;}i:7993;a:1:{i:0;i:7985;}i:7994;a:1:{i:0;i:7986;}i:7995;a:1:{i:0;i:7987;}i:7996;a:1:{i:0;i:7988;}i:7997;a:1:{i:0;i:7989;}i:7998;a:1:{i:0;i:7990;}i:7999;a:1:{i:0;i:7991;}i:8008;a:1:{i:0;i:8000;}i:8009;a:1:{i:0;i:8001;}i:8010;a:1:{i:0;i:8002;}i:8011;a:1:{i:0;i:8003;}i:8012;a:1:{i:0;i:8004;}i:8013;a:1:{i:0;i:8005;}i:8016;a:2:{i:0;i:965;i:1;i:787;}i:8018;a:3:{i:0;i:965;i:1;i:787;i:2;i:768;}i:8020;a:3:{i:0;i:965;i:1;i:787;i:2;i:769;}i:8022;a:3:{i:0;i:965;i:1;i:787;i:2;i:834;}i:8025;a:1:{i:0;i:8017;}i:8027;a:1:{i:0;i:8019;}i:8029;a:1:{i:0;i:8021;}i:8031;a:1:{i:0;i:8023;}i:8040;a:1:{i:0;i:8032;}i:8041;a:1:{i:0;i:8033;}i:8042;a:1:{i:0;i:8034;}i:8043;a:1:{i:0;i:8035;}i:8044;a:1:{i:0;i:8036;}i:8045;a:1:{i:0;i:8037;}i:8046;a:1:{i:0;i:8038;}i:8047;a:1:{i:0;i:8039;}i:8064;a:2:{i:0;i:7936;i:1;i:953;}i:8065;a:2:{i:0;i:7937;i:1;i:953;}i:8066;a:2:{i:0;i:7938;i:1;i:953;}i:8067;a:2:{i:0;i:7939;i:1;i:953;}i:8068;a:2:{i:0;i:7940;i:1;i:953;}i:8069;a:2:{i:0;i:7941;i:1;i:953;}i:8070;a:2:{i:0;i:7942;i:1;i:953;}i:8071;a:2:{i:0;i:7943;i:1;i:953;}i:8072;a:2:{i:0;i:7936;i:1;i:953;}i:8073;a:2:{i:0;i:7937;i:1;i:953;}i:8074;a:2:{i:0;i:7938;i:1;i:953;}i:8075;a:2:{i:0;i:7939;i:1;i:953;}i:8076;a:2:{i:0;i:7940;i:1;i:953;}i:8077;a:2:{i:0;i:7941;i:1;i:953;}i:8078;a:2:{i:0;i:7942;i:1;i:953;}i:8079;a:2:{i:0;i:7943;i:1;i:953;}i:8080;a:2:{i:0;i:7968;i:1;i:953;}i:8081;a:2:{i:0;i:7969;i:1;i:953;}i:8082;a:2:{i:0;i:7970;i:1;i:953;}i:8083;a:2:{i:0;i:7971;i:1;i:953;}i:8084;a:2:{i:0;i:7972;i:1;i:953;}i:8085;a:2:{i:0;i:7973;i:1;i:953;}i:8086;a:2:{i:0;i:7974;i:1;i:953;}i:8087;a:2:{i:0;i:7975;i:1;i:953;}i:8088;a:2:{i:0;i:7968;i:1;i:953;}i:8089;a:2:{i:0;i:7969;i:1;i:953;}i:8090;a:2:{i:0;i:7970;i:1;i:953;}i:8091;a:2:{i:0;i:7971;i:1;i:953;}i:8092;a:2:{i:0;i:7972;i:1;i:953;}i:8093;a:2:{i:0;i:7973;i:1;i:953;}i:8094;a:2:{i:0;i:7974;i:1;i:953;}i:8095;a:2:{i:0;i:7975;i:1;i:953;}i:8096;a:2:{i:0;i:8032;i:1;i:953;}i:8097;a:2:{i:0;i:8033;i:1;i:953;}i:8098;a:2:{i:0;i:8034;i:1;i:953;}i:8099;a:2:{i:0;i:8035;i:1;i:953;}i:8100;a:2:{i:0;i:8036;i:1;i:953;}i:8101;a:2:{i:0;i:8037;i:1;i:953;}i:8102;a:2:{i:0;i:8038;i:1;i:953;}i:8103;a:2:{i:0;i:8039;i:1;i:953;}i:8104;a:2:{i:0;i:8032;i:1;i:953;}i:8105;a:2:{i:0;i:8033;i:1;i:953;}i:8106;a:2:{i:0;i:8034;i:1;i:953;}i:8107;a:2:{i:0;i:8035;i:1;i:953;}i:8108;a:2:{i:0;i:8036;i:1;i:953;}i:8109;a:2:{i:0;i:8037;i:1;i:953;}i:8110;a:2:{i:0;i:8038;i:1;i:953;}i:8111;a:2:{i:0;i:8039;i:1;i:953;}i:8114;a:2:{i:0;i:8048;i:1;i:953;}i:8115;a:2:{i:0;i:945;i:1;i:953;}i:8116;a:2:{i:0;i:940;i:1;i:953;}i:8118;a:2:{i:0;i:945;i:1;i:834;}i:8119;a:3:{i:0;i:945;i:1;i:834;i:2;i:953;}i:8120;a:1:{i:0;i:8112;}i:8121;a:1:{i:0;i:8113;}i:8122;a:1:{i:0;i:8048;}i:8123;a:1:{i:0;i:8049;}i:8124;a:2:{i:0;i:945;i:1;i:953;}i:8126;a:1:{i:0;i:953;}i:8130;a:2:{i:0;i:8052;i:1;i:953;}i:8131;a:2:{i:0;i:951;i:1;i:953;}i:8132;a:2:{i:0;i:942;i:1;i:953;}i:8134;a:2:{i:0;i:951;i:1;i:834;}i:8135;a:3:{i:0;i:951;i:1;i:834;i:2;i:953;}i:8136;a:1:{i:0;i:8050;}i:8137;a:1:{i:0;i:8051;}i:8138;a:1:{i:0;i:8052;}i:8139;a:1:{i:0;i:8053;}i:8140;a:2:{i:0;i:951;i:1;i:953;}i:8146;a:3:{i:0;i:953;i:1;i:776;i:2;i:768;}i:8147;a:3:{i:0;i:953;i:1;i:776;i:2;i:769;}i:8150;a:2:{i:0;i:953;i:1;i:834;}i:8151;a:3:{i:0;i:953;i:1;i:776;i:2;i:834;}i:8152;a:1:{i:0;i:8144;}i:8153;a:1:{i:0;i:8145;}i:8154;a:1:{i:0;i:8054;}i:8155;a:1:{i:0;i:8055;}i:8162;a:3:{i:0;i:965;i:1;i:776;i:2;i:768;}i:8163;a:3:{i:0;i:965;i:1;i:776;i:2;i:769;}i:8164;a:2:{i:0;i:961;i:1;i:787;}i:8166;a:2:{i:0;i:965;i:1;i:834;}i:8167;a:3:{i:0;i:965;i:1;i:776;i:2;i:834;}i:8168;a:1:{i:0;i:8160;}i:8169;a:1:{i:0;i:8161;}i:8170;a:1:{i:0;i:8058;}i:8171;a:1:{i:0;i:8059;}i:8172;a:1:{i:0;i:8165;}i:8178;a:2:{i:0;i:8060;i:1;i:953;}i:8179;a:2:{i:0;i:969;i:1;i:953;}i:8180;a:2:{i:0;i:974;i:1;i:953;}i:8182;a:2:{i:0;i:969;i:1;i:834;}i:8183;a:3:{i:0;i:969;i:1;i:834;i:2;i:953;}i:8184;a:1:{i:0;i:8056;}i:8185;a:1:{i:0;i:8057;}i:8186;a:1:{i:0;i:8060;}i:8187;a:1:{i:0;i:8061;}i:8188;a:2:{i:0;i:969;i:1;i:953;}i:8360;a:2:{i:0;i:114;i:1;i:115;}i:8450;a:1:{i:0;i:99;}i:8451;a:2:{i:0;i:176;i:1;i:99;}i:8455;a:1:{i:0;i:603;}i:8457;a:2:{i:0;i:176;i:1;i:102;}i:8459;a:1:{i:0;i:104;}i:8460;a:1:{i:0;i:104;}i:8461;a:1:{i:0;i:104;}i:8464;a:1:{i:0;i:105;}i:8465;a:1:{i:0;i:105;}i:8466;a:1:{i:0;i:108;}i:8469;a:1:{i:0;i:110;}i:8470;a:2:{i:0;i:110;i:1;i:111;}i:8473;a:1:{i:0;i:112;}i:8474;a:1:{i:0;i:113;}i:8475;a:1:{i:0;i:114;}i:8476;a:1:{i:0;i:114;}i:8477;a:1:{i:0;i:114;}i:8480;a:2:{i:0;i:115;i:1;i:109;}i:8481;a:3:{i:0;i:116;i:1;i:101;i:2;i:108;}i:8482;a:2:{i:0;i:116;i:1;i:109;}i:8484;a:1:{i:0;i:122;}i:8486;a:1:{i:0;i:969;}i:8488;a:1:{i:0;i:122;}i:8490;a:1:{i:0;i:107;}i:8491;a:1:{i:0;i:229;}i:8492;a:1:{i:0;i:98;}i:8493;a:1:{i:0;i:99;}i:8496;a:1:{i:0;i:101;}i:8497;a:1:{i:0;i:102;}i:8499;a:1:{i:0;i:109;}i:8510;a:1:{i:0;i:947;}i:8511;a:1:{i:0;i:960;}i:8517;a:1:{i:0;i:100;}i:8544;a:1:{i:0;i:8560;}i:8545;a:1:{i:0;i:8561;}i:8546;a:1:{i:0;i:8562;}i:8547;a:1:{i:0;i:8563;}i:8548;a:1:{i:0;i:8564;}i:8549;a:1:{i:0;i:8565;}i:8550;a:1:{i:0;i:8566;}i:8551;a:1:{i:0;i:8567;}i:8552;a:1:{i:0;i:8568;}i:8553;a:1:{i:0;i:8569;}i:8554;a:1:{i:0;i:8570;}i:8555;a:1:{i:0;i:8571;}i:8556;a:1:{i:0;i:8572;}i:8557;a:1:{i:0;i:8573;}i:8558;a:1:{i:0;i:8574;}i:8559;a:1:{i:0;i:8575;}i:9398;a:1:{i:0;i:9424;}i:9399;a:1:{i:0;i:9425;}i:9400;a:1:{i:0;i:9426;}i:9401;a:1:{i:0;i:9427;}i:9402;a:1:{i:0;i:9428;}i:9403;a:1:{i:0;i:9429;}i:9404;a:1:{i:0;i:9430;}i:9405;a:1:{i:0;i:9431;}i:9406;a:1:{i:0;i:9432;}i:9407;a:1:{i:0;i:9433;}i:9408;a:1:{i:0;i:9434;}i:9409;a:1:{i:0;i:9435;}i:9410;a:1:{i:0;i:9436;}i:9411;a:1:{i:0;i:9437;}i:9412;a:1:{i:0;i:9438;}i:9413;a:1:{i:0;i:9439;}i:9414;a:1:{i:0;i:9440;}i:9415;a:1:{i:0;i:9441;}i:9416;a:1:{i:0;i:9442;}i:9417;a:1:{i:0;i:9443;}i:9418;a:1:{i:0;i:9444;}i:9419;a:1:{i:0;i:9445;}i:9420;a:1:{i:0;i:9446;}i:9421;a:1:{i:0;i:9447;}i:9422;a:1:{i:0;i:9448;}i:9423;a:1:{i:0;i:9449;}i:13169;a:3:{i:0;i:104;i:1;i:112;i:2;i:97;}i:13171;a:2:{i:0;i:97;i:1;i:117;}i:13173;a:2:{i:0;i:111;i:1;i:118;}i:13184;a:2:{i:0;i:112;i:1;i:97;}i:13185;a:2:{i:0;i:110;i:1;i:97;}i:13186;a:2:{i:0;i:956;i:1;i:97;}i:13187;a:2:{i:0;i:109;i:1;i:97;}i:13188;a:2:{i:0;i:107;i:1;i:97;}i:13189;a:2:{i:0;i:107;i:1;i:98;}i:13190;a:2:{i:0;i:109;i:1;i:98;}i:13191;a:2:{i:0;i:103;i:1;i:98;}i:13194;a:2:{i:0;i:112;i:1;i:102;}i:13195;a:2:{i:0;i:110;i:1;i:102;}i:13196;a:2:{i:0;i:956;i:1;i:102;}i:13200;a:2:{i:0;i:104;i:1;i:122;}i:13201;a:3:{i:0;i:107;i:1;i:104;i:2;i:122;}i:13202;a:3:{i:0;i:109;i:1;i:104;i:2;i:122;}i:13203;a:3:{i:0;i:103;i:1;i:104;i:2;i:122;}i:13204;a:3:{i:0;i:116;i:1;i:104;i:2;i:122;}i:13225;a:2:{i:0;i:112;i:1;i:97;}i:13226;a:3:{i:0;i:107;i:1;i:112;i:2;i:97;}i:13227;a:3:{i:0;i:109;i:1;i:112;i:2;i:97;}i:13228;a:3:{i:0;i:103;i:1;i:112;i:2;i:97;}i:13236;a:2:{i:0;i:112;i:1;i:118;}i:13237;a:2:{i:0;i:110;i:1;i:118;}i:13238;a:2:{i:0;i:956;i:1;i:118;}i:13239;a:2:{i:0;i:109;i:1;i:118;}i:13240;a:2:{i:0;i:107;i:1;i:118;}i:13241;a:2:{i:0;i:109;i:1;i:118;}i:13242;a:2:{i:0;i:112;i:1;i:119;}i:13243;a:2:{i:0;i:110;i:1;i:119;}i:13244;a:2:{i:0;i:956;i:1;i:119;}i:13245;a:2:{i:0;i:109;i:1;i:119;}i:13246;a:2:{i:0;i:107;i:1;i:119;}i:13247;a:2:{i:0;i:109;i:1;i:119;}i:13248;a:2:{i:0;i:107;i:1;i:969;}i:13249;a:2:{i:0;i:109;i:1;i:969;}i:13251;a:2:{i:0;i:98;i:1;i:113;}i:13254;a:4:{i:0;i:99;i:1;i:8725;i:2;i:107;i:3;i:103;}i:13255;a:3:{i:0;i:99;i:1;i:111;i:2;i:46;}i:13256;a:2:{i:0;i:100;i:1;i:98;}i:13257;a:2:{i:0;i:103;i:1;i:121;}i:13259;a:2:{i:0;i:104;i:1;i:112;}i:13261;a:2:{i:0;i:107;i:1;i:107;}i:13262;a:2:{i:0;i:107;i:1;i:109;}i:13271;a:2:{i:0;i:112;i:1;i:104;}i:13273;a:3:{i:0;i:112;i:1;i:112;i:2;i:109;}i:13274;a:2:{i:0;i:112;i:1;i:114;}i:13276;a:2:{i:0;i:115;i:1;i:118;}i:13277;a:2:{i:0;i:119;i:1;i:98;}i:64256;a:2:{i:0;i:102;i:1;i:102;}i:64257;a:2:{i:0;i:102;i:1;i:105;}i:64258;a:2:{i:0;i:102;i:1;i:108;}i:64259;a:3:{i:0;i:102;i:1;i:102;i:2;i:105;}i:64260;a:3:{i:0;i:102;i:1;i:102;i:2;i:108;}i:64261;a:2:{i:0;i:115;i:1;i:116;}i:64262;a:2:{i:0;i:115;i:1;i:116;}i:64275;a:2:{i:0;i:1396;i:1;i:1398;}i:64276;a:2:{i:0;i:1396;i:1;i:1381;}i:64277;a:2:{i:0;i:1396;i:1;i:1387;}i:64278;a:2:{i:0;i:1406;i:1;i:1398;}i:64279;a:2:{i:0;i:1396;i:1;i:1389;}i:65313;a:1:{i:0;i:65345;}i:65314;a:1:{i:0;i:65346;}i:65315;a:1:{i:0;i:65347;}i:65316;a:1:{i:0;i:65348;}i:65317;a:1:{i:0;i:65349;}i:65318;a:1:{i:0;i:65350;}i:65319;a:1:{i:0;i:65351;}i:65320;a:1:{i:0;i:65352;}i:65321;a:1:{i:0;i:65353;}i:65322;a:1:{i:0;i:65354;}i:65323;a:1:{i:0;i:65355;}i:65324;a:1:{i:0;i:65356;}i:65325;a:1:{i:0;i:65357;}i:65326;a:1:{i:0;i:65358;}i:65327;a:1:{i:0;i:65359;}i:65328;a:1:{i:0;i:65360;}i:65329;a:1:{i:0;i:65361;}i:65330;a:1:{i:0;i:65362;}i:65331;a:1:{i:0;i:65363;}i:65332;a:1:{i:0;i:65364;}i:65333;a:1:{i:0;i:65365;}i:65334;a:1:{i:0;i:65366;}i:65335;a:1:{i:0;i:65367;}i:65336;a:1:{i:0;i:65368;}i:65337;a:1:{i:0;i:65369;}i:65338;a:1:{i:0;i:65370;}i:66560;a:1:{i:0;i:66600;}i:66561;a:1:{i:0;i:66601;}i:66562;a:1:{i:0;i:66602;}i:66563;a:1:{i:0;i:66603;}i:66564;a:1:{i:0;i:66604;}i:66565;a:1:{i:0;i:66605;}i:66566;a:1:{i:0;i:66606;}i:66567;a:1:{i:0;i:66607;}i:66568;a:1:{i:0;i:66608;}i:66569;a:1:{i:0;i:66609;}i:66570;a:1:{i:0;i:66610;}i:66571;a:1:{i:0;i:66611;}i:66572;a:1:{i:0;i:66612;}i:66573;a:1:{i:0;i:66613;}i:66574;a:1:{i:0;i:66614;}i:66575;a:1:{i:0;i:66615;}i:66576;a:1:{i:0;i:66616;}i:66577;a:1:{i:0;i:66617;}i:66578;a:1:{i:0;i:66618;}i:66579;a:1:{i:0;i:66619;}i:66580;a:1:{i:0;i:66620;}i:66581;a:1:{i:0;i:66621;}i:66582;a:1:{i:0;i:66622;}i:66583;a:1:{i:0;i:66623;}i:66584;a:1:{i:0;i:66624;}i:66585;a:1:{i:0;i:66625;}i:66586;a:1:{i:0;i:66626;}i:66587;a:1:{i:0;i:66627;}i:66588;a:1:{i:0;i:66628;}i:66589;a:1:{i:0;i:66629;}i:66590;a:1:{i:0;i:66630;}i:66591;a:1:{i:0;i:66631;}i:66592;a:1:{i:0;i:66632;}i:66593;a:1:{i:0;i:66633;}i:66594;a:1:{i:0;i:66634;}i:66595;a:1:{i:0;i:66635;}i:66596;a:1:{i:0;i:66636;}i:66597;a:1:{i:0;i:66637;}i:119808;a:1:{i:0;i:97;}i:119809;a:1:{i:0;i:98;}i:119810;a:1:{i:0;i:99;}i:119811;a:1:{i:0;i:100;}i:119812;a:1:{i:0;i:101;}i:119813;a:1:{i:0;i:102;}i:119814;a:1:{i:0;i:103;}i:119815;a:1:{i:0;i:104;}i:119816;a:1:{i:0;i:105;}i:119817;a:1:{i:0;i:106;}i:119818;a:1:{i:0;i:107;}i:119819;a:1:{i:0;i:108;}i:119820;a:1:{i:0;i:109;}i:119821;a:1:{i:0;i:110;}i:119822;a:1:{i:0;i:111;}i:119823;a:1:{i:0;i:112;}i:119824;a:1:{i:0;i:113;}i:119825;a:1:{i:0;i:114;}i:119826;a:1:{i:0;i:115;}i:119827;a:1:{i:0;i:116;}i:119828;a:1:{i:0;i:117;}i:119829;a:1:{i:0;i:118;}i:119830;a:1:{i:0;i:119;}i:119831;a:1:{i:0;i:120;}i:119832;a:1:{i:0;i:121;}i:119833;a:1:{i:0;i:122;}i:119860;a:1:{i:0;i:97;}i:119861;a:1:{i:0;i:98;}i:119862;a:1:{i:0;i:99;}i:119863;a:1:{i:0;i:100;}i:119864;a:1:{i:0;i:101;}i:119865;a:1:{i:0;i:102;}i:119866;a:1:{i:0;i:103;}i:119867;a:1:{i:0;i:104;}i:119868;a:1:{i:0;i:105;}i:119869;a:1:{i:0;i:106;}i:119870;a:1:{i:0;i:107;}i:119871;a:1:{i:0;i:108;}i:119872;a:1:{i:0;i:109;}i:119873;a:1:{i:0;i:110;}i:119874;a:1:{i:0;i:111;}i:119875;a:1:{i:0;i:112;}i:119876;a:1:{i:0;i:113;}i:119877;a:1:{i:0;i:114;}i:119878;a:1:{i:0;i:115;}i:119879;a:1:{i:0;i:116;}i:119880;a:1:{i:0;i:117;}i:119881;a:1:{i:0;i:118;}i:119882;a:1:{i:0;i:119;}i:119883;a:1:{i:0;i:120;}i:119884;a:1:{i:0;i:121;}i:119885;a:1:{i:0;i:122;}i:119912;a:1:{i:0;i:97;}i:119913;a:1:{i:0;i:98;}i:119914;a:1:{i:0;i:99;}i:119915;a:1:{i:0;i:100;}i:119916;a:1:{i:0;i:101;}i:119917;a:1:{i:0;i:102;}i:119918;a:1:{i:0;i:103;}i:119919;a:1:{i:0;i:104;}i:119920;a:1:{i:0;i:105;}i:119921;a:1:{i:0;i:106;}i:119922;a:1:{i:0;i:107;}i:119923;a:1:{i:0;i:108;}i:119924;a:1:{i:0;i:109;}i:119925;a:1:{i:0;i:110;}i:119926;a:1:{i:0;i:111;}i:119927;a:1:{i:0;i:112;}i:119928;a:1:{i:0;i:113;}i:119929;a:1:{i:0;i:114;}i:119930;a:1:{i:0;i:115;}i:119931;a:1:{i:0;i:116;}i:119932;a:1:{i:0;i:117;}i:119933;a:1:{i:0;i:118;}i:119934;a:1:{i:0;i:119;}i:119935;a:1:{i:0;i:120;}i:119936;a:1:{i:0;i:121;}i:119937;a:1:{i:0;i:122;}i:119964;a:1:{i:0;i:97;}i:119966;a:1:{i:0;i:99;}i:119967;a:1:{i:0;i:100;}i:119970;a:1:{i:0;i:103;}i:119973;a:1:{i:0;i:106;}i:119974;a:1:{i:0;i:107;}i:119977;a:1:{i:0;i:110;}i:119978;a:1:{i:0;i:111;}i:119979;a:1:{i:0;i:112;}i:119980;a:1:{i:0;i:113;}i:119982;a:1:{i:0;i:115;}i:119983;a:1:{i:0;i:116;}i:119984;a:1:{i:0;i:117;}i:119985;a:1:{i:0;i:118;}i:119986;a:1:{i:0;i:119;}i:119987;a:1:{i:0;i:120;}i:119988;a:1:{i:0;i:121;}i:119989;a:1:{i:0;i:122;}i:120016;a:1:{i:0;i:97;}i:120017;a:1:{i:0;i:98;}i:120018;a:1:{i:0;i:99;}i:120019;a:1:{i:0;i:100;}i:120020;a:1:{i:0;i:101;}i:120021;a:1:{i:0;i:102;}i:120022;a:1:{i:0;i:103;}i:120023;a:1:{i:0;i:104;}i:120024;a:1:{i:0;i:105;}i:120025;a:1:{i:0;i:106;}i:120026;a:1:{i:0;i:107;}i:120027;a:1:{i:0;i:108;}i:120028;a:1:{i:0;i:109;}i:120029;a:1:{i:0;i:110;}i:120030;a:1:{i:0;i:111;}i:120031;a:1:{i:0;i:112;}i:120032;a:1:{i:0;i:113;}i:120033;a:1:{i:0;i:114;}i:120034;a:1:{i:0;i:115;}i:120035;a:1:{i:0;i:116;}i:120036;a:1:{i:0;i:117;}i:120037;a:1:{i:0;i:118;}i:120038;a:1:{i:0;i:119;}i:120039;a:1:{i:0;i:120;}i:120040;a:1:{i:0;i:121;}i:120041;a:1:{i:0;i:122;}i:120068;a:1:{i:0;i:97;}i:120069;a:1:{i:0;i:98;}i:120071;a:1:{i:0;i:100;}i:120072;a:1:{i:0;i:101;}i:120073;a:1:{i:0;i:102;}i:120074;a:1:{i:0;i:103;}i:120077;a:1:{i:0;i:106;}i:120078;a:1:{i:0;i:107;}i:120079;a:1:{i:0;i:108;}i:120080;a:1:{i:0;i:109;}i:120081;a:1:{i:0;i:110;}i:120082;a:1:{i:0;i:111;}i:120083;a:1:{i:0;i:112;}i:120084;a:1:{i:0;i:113;}i:120086;a:1:{i:0;i:115;}i:120087;a:1:{i:0;i:116;}i:120088;a:1:{i:0;i:117;}i:120089;a:1:{i:0;i:118;}i:120090;a:1:{i:0;i:119;}i:120091;a:1:{i:0;i:120;}i:120092;a:1:{i:0;i:121;}i:120120;a:1:{i:0;i:97;}i:120121;a:1:{i:0;i:98;}i:120123;a:1:{i:0;i:100;}i:120124;a:1:{i:0;i:101;}i:120125;a:1:{i:0;i:102;}i:120126;a:1:{i:0;i:103;}i:120128;a:1:{i:0;i:105;}i:120129;a:1:{i:0;i:106;}i:120130;a:1:{i:0;i:107;}i:120131;a:1:{i:0;i:108;}i:120132;a:1:{i:0;i:109;}i:120134;a:1:{i:0;i:111;}i:120138;a:1:{i:0;i:115;}i:120139;a:1:{i:0;i:116;}i:120140;a:1:{i:0;i:117;}i:120141;a:1:{i:0;i:118;}i:120142;a:1:{i:0;i:119;}i:120143;a:1:{i:0;i:120;}i:120144;a:1:{i:0;i:121;}i:120172;a:1:{i:0;i:97;}i:120173;a:1:{i:0;i:98;}i:120174;a:1:{i:0;i:99;}i:120175;a:1:{i:0;i:100;}i:120176;a:1:{i:0;i:101;}i:120177;a:1:{i:0;i:102;}i:120178;a:1:{i:0;i:103;}i:120179;a:1:{i:0;i:104;}i:120180;a:1:{i:0;i:105;}i:120181;a:1:{i:0;i:106;}i:120182;a:1:{i:0;i:107;}i:120183;a:1:{i:0;i:108;}i:120184;a:1:{i:0;i:109;}i:120185;a:1:{i:0;i:110;}i:120186;a:1:{i:0;i:111;}i:120187;a:1:{i:0;i:112;}i:120188;a:1:{i:0;i:113;}i:120189;a:1:{i:0;i:114;}i:120190;a:1:{i:0;i:115;}i:120191;a:1:{i:0;i:116;}i:120192;a:1:{i:0;i:117;}i:120193;a:1:{i:0;i:118;}i:120194;a:1:{i:0;i:119;}i:120195;a:1:{i:0;i:120;}i:120196;a:1:{i:0;i:121;}i:120197;a:1:{i:0;i:122;}i:120224;a:1:{i:0;i:97;}i:120225;a:1:{i:0;i:98;}i:120226;a:1:{i:0;i:99;}i:120227;a:1:{i:0;i:100;}i:120228;a:1:{i:0;i:101;}i:120229;a:1:{i:0;i:102;}i:120230;a:1:{i:0;i:103;}i:120231;a:1:{i:0;i:104;}i:120232;a:1:{i:0;i:105;}i:120233;a:1:{i:0;i:106;}i:120234;a:1:{i:0;i:107;}i:120235;a:1:{i:0;i:108;}i:120236;a:1:{i:0;i:109;}i:120237;a:1:{i:0;i:110;}i:120238;a:1:{i:0;i:111;}i:120239;a:1:{i:0;i:112;}i:120240;a:1:{i:0;i:113;}i:120241;a:1:{i:0;i:114;}i:120242;a:1:{i:0;i:115;}i:120243;a:1:{i:0;i:116;}i:120244;a:1:{i:0;i:117;}i:120245;a:1:{i:0;i:118;}i:120246;a:1:{i:0;i:119;}i:120247;a:1:{i:0;i:120;}i:120248;a:1:{i:0;i:121;}i:120249;a:1:{i:0;i:122;}i:120276;a:1:{i:0;i:97;}i:120277;a:1:{i:0;i:98;}i:120278;a:1:{i:0;i:99;}i:120279;a:1:{i:0;i:100;}i:120280;a:1:{i:0;i:101;}i:120281;a:1:{i:0;i:102;}i:120282;a:1:{i:0;i:103;}i:120283;a:1:{i:0;i:104;}i:120284;a:1:{i:0;i:105;}i:120285;a:1:{i:0;i:106;}i:120286;a:1:{i:0;i:107;}i:120287;a:1:{i:0;i:108;}i:120288;a:1:{i:0;i:109;}i:120289;a:1:{i:0;i:110;}i:120290;a:1:{i:0;i:111;}i:120291;a:1:{i:0;i:112;}i:120292;a:1:{i:0;i:113;}i:120293;a:1:{i:0;i:114;}i:120294;a:1:{i:0;i:115;}i:120295;a:1:{i:0;i:116;}i:120296;a:1:{i:0;i:117;}i:120297;a:1:{i:0;i:118;}i:120298;a:1:{i:0;i:119;}i:120299;a:1:{i:0;i:120;}i:120300;a:1:{i:0;i:121;}i:120301;a:1:{i:0;i:122;}i:120328;a:1:{i:0;i:97;}i:120329;a:1:{i:0;i:98;}i:120330;a:1:{i:0;i:99;}i:120331;a:1:{i:0;i:100;}i:120332;a:1:{i:0;i:101;}i:120333;a:1:{i:0;i:102;}i:120334;a:1:{i:0;i:103;}i:120335;a:1:{i:0;i:104;}i:120336;a:1:{i:0;i:105;}i:120337;a:1:{i:0;i:106;}i:120338;a:1:{i:0;i:107;}i:120339;a:1:{i:0;i:108;}i:120340;a:1:{i:0;i:109;}i:120341;a:1:{i:0;i:110;}i:120342;a:1:{i:0;i:111;}i:120343;a:1:{i:0;i:112;}i:120344;a:1:{i:0;i:113;}i:120345;a:1:{i:0;i:114;}i:120346;a:1:{i:0;i:115;}i:120347;a:1:{i:0;i:116;}i:120348;a:1:{i:0;i:117;}i:120349;a:1:{i:0;i:118;}i:120350;a:1:{i:0;i:119;}i:120351;a:1:{i:0;i:120;}i:120352;a:1:{i:0;i:121;}i:120353;a:1:{i:0;i:122;}i:120380;a:1:{i:0;i:97;}i:120381;a:1:{i:0;i:98;}i:120382;a:1:{i:0;i:99;}i:120383;a:1:{i:0;i:100;}i:120384;a:1:{i:0;i:101;}i:120385;a:1:{i:0;i:102;}i:120386;a:1:{i:0;i:103;}i:120387;a:1:{i:0;i:104;}i:120388;a:1:{i:0;i:105;}i:120389;a:1:{i:0;i:106;}i:120390;a:1:{i:0;i:107;}i:120391;a:1:{i:0;i:108;}i:120392;a:1:{i:0;i:109;}i:120393;a:1:{i:0;i:110;}i:120394;a:1:{i:0;i:111;}i:120395;a:1:{i:0;i:112;}i:120396;a:1:{i:0;i:113;}i:120397;a:1:{i:0;i:114;}i:120398;a:1:{i:0;i:115;}i:120399;a:1:{i:0;i:116;}i:120400;a:1:{i:0;i:117;}i:120401;a:1:{i:0;i:118;}i:120402;a:1:{i:0;i:119;}i:120403;a:1:{i:0;i:120;}i:120404;a:1:{i:0;i:121;}i:120405;a:1:{i:0;i:122;}i:120432;a:1:{i:0;i:97;}i:120433;a:1:{i:0;i:98;}i:120434;a:1:{i:0;i:99;}i:120435;a:1:{i:0;i:100;}i:120436;a:1:{i:0;i:101;}i:120437;a:1:{i:0;i:102;}i:120438;a:1:{i:0;i:103;}i:120439;a:1:{i:0;i:104;}i:120440;a:1:{i:0;i:105;}i:120441;a:1:{i:0;i:106;}i:120442;a:1:{i:0;i:107;}i:120443;a:1:{i:0;i:108;}i:120444;a:1:{i:0;i:109;}i:120445;a:1:{i:0;i:110;}i:120446;a:1:{i:0;i:111;}i:120447;a:1:{i:0;i:112;}i:120448;a:1:{i:0;i:113;}i:120449;a:1:{i:0;i:114;}i:120450;a:1:{i:0;i:115;}i:120451;a:1:{i:0;i:116;}i:120452;a:1:{i:0;i:117;}i:120453;a:1:{i:0;i:118;}i:120454;a:1:{i:0;i:119;}i:120455;a:1:{i:0;i:120;}i:120456;a:1:{i:0;i:121;}i:120457;a:1:{i:0;i:122;}i:120488;a:1:{i:0;i:945;}i:120489;a:1:{i:0;i:946;}i:120490;a:1:{i:0;i:947;}i:120491;a:1:{i:0;i:948;}i:120492;a:1:{i:0;i:949;}i:120493;a:1:{i:0;i:950;}i:120494;a:1:{i:0;i:951;}i:120495;a:1:{i:0;i:952;}i:120496;a:1:{i:0;i:953;}i:120497;a:1:{i:0;i:954;}i:120498;a:1:{i:0;i:955;}i:120499;a:1:{i:0;i:956;}i:120500;a:1:{i:0;i:957;}i:120501;a:1:{i:0;i:958;}i:120502;a:1:{i:0;i:959;}i:120503;a:1:{i:0;i:960;}i:120504;a:1:{i:0;i:961;}i:120505;a:1:{i:0;i:952;}i:120506;a:1:{i:0;i:963;}i:120507;a:1:{i:0;i:964;}i:120508;a:1:{i:0;i:965;}i:120509;a:1:{i:0;i:966;}i:120510;a:1:{i:0;i:967;}i:120511;a:1:{i:0;i:968;}i:120512;a:1:{i:0;i:969;}i:120531;a:1:{i:0;i:963;}i:120546;a:1:{i:0;i:945;}i:120547;a:1:{i:0;i:946;}i:120548;a:1:{i:0;i:947;}i:120549;a:1:{i:0;i:948;}i:120550;a:1:{i:0;i:949;}i:120551;a:1:{i:0;i:950;}i:120552;a:1:{i:0;i:951;}i:120553;a:1:{i:0;i:952;}i:120554;a:1:{i:0;i:953;}i:120555;a:1:{i:0;i:954;}i:120556;a:1:{i:0;i:955;}i:120557;a:1:{i:0;i:956;}i:120558;a:1:{i:0;i:957;}i:120559;a:1:{i:0;i:958;}i:120560;a:1:{i:0;i:959;}i:120561;a:1:{i:0;i:960;}i:120562;a:1:{i:0;i:961;}i:120563;a:1:{i:0;i:952;}i:120564;a:1:{i:0;i:963;}i:120565;a:1:{i:0;i:964;}i:120566;a:1:{i:0;i:965;}i:120567;a:1:{i:0;i:966;}i:120568;a:1:{i:0;i:967;}i:120569;a:1:{i:0;i:968;}i:120570;a:1:{i:0;i:969;}i:120589;a:1:{i:0;i:963;}i:120604;a:1:{i:0;i:945;}i:120605;a:1:{i:0;i:946;}i:120606;a:1:{i:0;i:947;}i:120607;a:1:{i:0;i:948;}i:120608;a:1:{i:0;i:949;}i:120609;a:1:{i:0;i:950;}i:120610;a:1:{i:0;i:951;}i:120611;a:1:{i:0;i:952;}i:120612;a:1:{i:0;i:953;}i:120613;a:1:{i:0;i:954;}i:120614;a:1:{i:0;i:955;}i:120615;a:1:{i:0;i:956;}i:120616;a:1:{i:0;i:957;}i:120617;a:1:{i:0;i:958;}i:120618;a:1:{i:0;i:959;}i:120619;a:1:{i:0;i:960;}i:120620;a:1:{i:0;i:961;}i:120621;a:1:{i:0;i:952;}i:120622;a:1:{i:0;i:963;}i:120623;a:1:{i:0;i:964;}i:120624;a:1:{i:0;i:965;}i:120625;a:1:{i:0;i:966;}i:120626;a:1:{i:0;i:967;}i:120627;a:1:{i:0;i:968;}i:120628;a:1:{i:0;i:969;}i:120647;a:1:{i:0;i:963;}i:120662;a:1:{i:0;i:945;}i:120663;a:1:{i:0;i:946;}i:120664;a:1:{i:0;i:947;}i:120665;a:1:{i:0;i:948;}i:120666;a:1:{i:0;i:949;}i:120667;a:1:{i:0;i:950;}i:120668;a:1:{i:0;i:951;}i:120669;a:1:{i:0;i:952;}i:120670;a:1:{i:0;i:953;}i:120671;a:1:{i:0;i:954;}i:120672;a:1:{i:0;i:955;}i:120673;a:1:{i:0;i:956;}i:120674;a:1:{i:0;i:957;}i:120675;a:1:{i:0;i:958;}i:120676;a:1:{i:0;i:959;}i:120677;a:1:{i:0;i:960;}i:120678;a:1:{i:0;i:961;}i:120679;a:1:{i:0;i:952;}i:120680;a:1:{i:0;i:963;}i:120681;a:1:{i:0;i:964;}i:120682;a:1:{i:0;i:965;}i:120683;a:1:{i:0;i:966;}i:120684;a:1:{i:0;i:967;}i:120685;a:1:{i:0;i:968;}i:120686;a:1:{i:0;i:969;}i:120705;a:1:{i:0;i:963;}i:120720;a:1:{i:0;i:945;}i:120721;a:1:{i:0;i:946;}i:120722;a:1:{i:0;i:947;}i:120723;a:1:{i:0;i:948;}i:120724;a:1:{i:0;i:949;}i:120725;a:1:{i:0;i:950;}i:120726;a:1:{i:0;i:951;}i:120727;a:1:{i:0;i:952;}i:120728;a:1:{i:0;i:953;}i:120729;a:1:{i:0;i:954;}i:120730;a:1:{i:0;i:955;}i:120731;a:1:{i:0;i:956;}i:120732;a:1:{i:0;i:957;}i:120733;a:1:{i:0;i:958;}i:120734;a:1:{i:0;i:959;}i:120735;a:1:{i:0;i:960;}i:120736;a:1:{i:0;i:961;}i:120737;a:1:{i:0;i:952;}i:120738;a:1:{i:0;i:963;}i:120739;a:1:{i:0;i:964;}i:120740;a:1:{i:0;i:965;}i:120741;a:1:{i:0;i:966;}i:120742;a:1:{i:0;i:967;}i:120743;a:1:{i:0;i:968;}i:120744;a:1:{i:0;i:969;}i:120763;a:1:{i:0;i:963;}i:1017;a:1:{i:0;i:963;}i:7468;a:1:{i:0;i:97;}i:7469;a:1:{i:0;i:230;}i:7470;a:1:{i:0;i:98;}i:7472;a:1:{i:0;i:100;}i:7473;a:1:{i:0;i:101;}i:7474;a:1:{i:0;i:477;}i:7475;a:1:{i:0;i:103;}i:7476;a:1:{i:0;i:104;}i:7477;a:1:{i:0;i:105;}i:7478;a:1:{i:0;i:106;}i:7479;a:1:{i:0;i:107;}i:7480;a:1:{i:0;i:108;}i:7481;a:1:{i:0;i:109;}i:7482;a:1:{i:0;i:110;}i:7484;a:1:{i:0;i:111;}i:7485;a:1:{i:0;i:547;}i:7486;a:1:{i:0;i:112;}i:7487;a:1:{i:0;i:114;}i:7488;a:1:{i:0;i:116;}i:7489;a:1:{i:0;i:117;}i:7490;a:1:{i:0;i:119;}i:8507;a:3:{i:0;i:102;i:1;i:97;i:2;i:120;}i:12880;a:3:{i:0;i:112;i:1;i:116;i:2;i:101;}i:13004;a:2:{i:0;i:104;i:1;i:103;}i:13006;a:2:{i:0;i:101;i:1;i:118;}i:13007;a:3:{i:0;i:108;i:1;i:116;i:2;i:100;}i:13178;a:2:{i:0;i:105;i:1;i:117;}i:13278;a:3:{i:0;i:118;i:1;i:8725;i:2;i:109;}i:13279;a:3:{i:0;i:97;i:1;i:8725;i:2;i:109;}}s:12:"norm_combcls";a:341:{i:820;i:1;i:821;i:1;i:822;i:1;i:823;i:1;i:824;i:1;i:2364;i:7;i:2492;i:7;i:2620;i:7;i:2748;i:7;i:2876;i:7;i:3260;i:7;i:4151;i:7;i:12441;i:8;i:12442;i:8;i:2381;i:9;i:2509;i:9;i:2637;i:9;i:2765;i:9;i:2893;i:9;i:3021;i:9;i:3149;i:9;i:3277;i:9;i:3405;i:9;i:3530;i:9;i:3642;i:9;i:3972;i:9;i:4153;i:9;i:5908;i:9;i:5940;i:9;i:6098;i:9;i:1456;i:10;i:1457;i:11;i:1458;i:12;i:1459;i:13;i:1460;i:14;i:1461;i:15;i:1462;i:16;i:1463;i:17;i:1464;i:18;i:1465;i:19;i:1467;i:20;i:1468;i:21;i:1469;i:22;i:1471;i:23;i:1473;i:24;i:1474;i:25;i:64286;i:26;i:1611;i:27;i:1612;i:28;i:1613;i:29;i:1614;i:30;i:1615;i:31;i:1616;i:32;i:1617;i:33;i:1618;i:34;i:1648;i:35;i:1809;i:36;i:3157;i:84;i:3158;i:91;i:3640;i:103;i:3641;i:103;i:3656;i:107;i:3657;i:107;i:3658;i:107;i:3659;i:107;i:3768;i:118;i:3769;i:118;i:3784;i:122;i:3785;i:122;i:3786;i:122;i:3787;i:122;i:3953;i:129;i:3954;i:130;i:3962;i:130;i:3963;i:130;i:3964;i:130;i:3965;i:130;i:3968;i:130;i:3956;i:132;i:801;i:202;i:802;i:202;i:807;i:202;i:808;i:202;i:795;i:216;i:3897;i:216;i:119141;i:216;i:119142;i:216;i:119150;i:216;i:119151;i:216;i:119152;i:216;i:119153;i:216;i:119154;i:216;i:12330;i:218;i:790;i:220;i:791;i:220;i:792;i:220;i:793;i:220;i:796;i:220;i:797;i:220;i:798;i:220;i:799;i:220;i:800;i:220;i:803;i:220;i:804;i:220;i:805;i:220;i:806;i:220;i:809;i:220;i:810;i:220;i:811;i:220;i:812;i:220;i:813;i:220;i:814;i:220;i:815;i:220;i:816;i:220;i:817;i:220;i:818;i:220;i:819;i:220;i:825;i:220;i:826;i:220;i:827;i:220;i:828;i:220;i:839;i:220;i:840;i:220;i:841;i:220;i:845;i:220;i:846;i:220;i:851;i:220;i:852;i:220;i:853;i:220;i:854;i:220;i:1425;i:220;i:1430;i:220;i:1435;i:220;i:1443;i:220;i:1444;i:220;i:1445;i:220;i:1446;i:220;i:1447;i:220;i:1450;i:220;i:1621;i:220;i:1622;i:220;i:1763;i:220;i:1770;i:220;i:1773;i:220;i:1841;i:220;i:1844;i:220;i:1847;i:220;i:1848;i:220;i:1849;i:220;i:1851;i:220;i:1852;i:220;i:1854;i:220;i:1858;i:220;i:1860;i:220;i:1862;i:220;i:1864;i:220;i:2386;i:220;i:3864;i:220;i:3865;i:220;i:3893;i:220;i:3895;i:220;i:4038;i:220;i:6459;i:220;i:8424;i:220;i:119163;i:220;i:119164;i:220;i:119165;i:220;i:119166;i:220;i:119167;i:220;i:119168;i:220;i:119169;i:220;i:119170;i:220;i:119178;i:220;i:119179;i:220;i:1434;i:222;i:1453;i:222;i:6441;i:222;i:12333;i:222;i:12334;i:224;i:12335;i:224;i:119149;i:226;i:1454;i:228;i:6313;i:228;i:12331;i:228;i:768;i:230;i:769;i:230;i:770;i:230;i:771;i:230;i:772;i:230;i:773;i:230;i:774;i:230;i:775;i:230;i:776;i:230;i:777;i:230;i:778;i:230;i:779;i:230;i:780;i:230;i:781;i:230;i:782;i:230;i:783;i:230;i:784;i:230;i:785;i:230;i:786;i:230;i:787;i:230;i:788;i:230;i:829;i:230;i:830;i:230;i:831;i:230;i:832;i:230;i:833;i:230;i:834;i:230;i:835;i:230;i:836;i:230;i:838;i:230;i:842;i:230;i:843;i:230;i:844;i:230;i:848;i:230;i:849;i:230;i:850;i:230;i:855;i:230;i:867;i:230;i:868;i:230;i:869;i:230;i:870;i:230;i:871;i:230;i:872;i:230;i:873;i:230;i:874;i:230;i:875;i:230;i:876;i:230;i:877;i:230;i:878;i:230;i:879;i:230;i:1155;i:230;i:1156;i:230;i:1157;i:230;i:1158;i:230;i:1426;i:230;i:1427;i:230;i:1428;i:230;i:1429;i:230;i:1431;i:230;i:1432;i:230;i:1433;i:230;i:1436;i:230;i:1437;i:230;i:1438;i:230;i:1439;i:230;i:1440;i:230;i:1441;i:230;i:1448;i:230;i:1449;i:230;i:1451;i:230;i:1452;i:230;i:1455;i:230;i:1476;i:230;i:1552;i:230;i:1553;i:230;i:1554;i:230;i:1555;i:230;i:1556;i:230;i:1557;i:230;i:1619;i:230;i:1620;i:230;i:1623;i:230;i:1624;i:230;i:1750;i:230;i:1751;i:230;i:1752;i:230;i:1753;i:230;i:1754;i:230;i:1755;i:230;i:1756;i:230;i:1759;i:230;i:1760;i:230;i:1761;i:230;i:1762;i:230;i:1764;i:230;i:1767;i:230;i:1768;i:230;i:1771;i:230;i:1772;i:230;i:1840;i:230;i:1842;i:230;i:1843;i:230;i:1845;i:230;i:1846;i:230;i:1850;i:230;i:1853;i:230;i:1855;i:230;i:1856;i:230;i:1857;i:230;i:1859;i:230;i:1861;i:230;i:1863;i:230;i:1865;i:230;i:1866;i:230;i:2385;i:230;i:2387;i:230;i:2388;i:230;i:3970;i:230;i:3971;i:230;i:3974;i:230;i:3975;i:230;i:5901;i:230;i:6458;i:230;i:8400;i:230;i:8401;i:230;i:8404;i:230;i:8405;i:230;i:8406;i:230;i:8407;i:230;i:8411;i:230;i:8412;i:230;i:8417;i:230;i:8423;i:230;i:8425;i:230;i:65056;i:230;i:65057;i:230;i:65058;i:230;i:65059;i:230;i:119173;i:230;i:119174;i:230;i:119175;i:230;i:119177;i:230;i:119176;i:230;i:119210;i:230;i:119211;i:230;i:119212;i:230;i:119213;i:230;i:789;i:232;i:794;i:232;i:12332;i:232;i:863;i:233;i:866;i:233;i:861;i:234;i:862;i:234;i:864;i:234;i:865;i:234;i:837;i:240;}} \ No newline at end of file diff --git a/includes/simplepie/simplepie.inc b/includes/simplepie/simplepie.inc new file mode 100644 index 0000000..ee0ba2c --- /dev/null +++ b/includes/simplepie/simplepie.inc @@ -0,0 +1,13316 @@ +' . SIMPLEPIE_NAME . ''); + +/** + * No Autodiscovery + * @see SimplePie::set_autodiscovery_level() + */ +define('SIMPLEPIE_LOCATOR_NONE', 0); + +/** + * Feed Link Element Autodiscovery + * @see SimplePie::set_autodiscovery_level() + */ +define('SIMPLEPIE_LOCATOR_AUTODISCOVERY', 1); + +/** + * Local Feed Extension Autodiscovery + * @see SimplePie::set_autodiscovery_level() + */ +define('SIMPLEPIE_LOCATOR_LOCAL_EXTENSION', 2); + +/** + * Local Feed Body Autodiscovery + * @see SimplePie::set_autodiscovery_level() + */ +define('SIMPLEPIE_LOCATOR_LOCAL_BODY', 4); + +/** + * Remote Feed Extension Autodiscovery + * @see SimplePie::set_autodiscovery_level() + */ +define('SIMPLEPIE_LOCATOR_REMOTE_EXTENSION', 8); + +/** + * Remote Feed Body Autodiscovery + * @see SimplePie::set_autodiscovery_level() + */ +define('SIMPLEPIE_LOCATOR_REMOTE_BODY', 16); + +/** + * All Feed Autodiscovery + * @see SimplePie::set_autodiscovery_level() + */ +define('SIMPLEPIE_LOCATOR_ALL', 31); + +/** + * No known feed type + */ +define('SIMPLEPIE_TYPE_NONE', 0); + +/** + * RSS 0.90 + */ +define('SIMPLEPIE_TYPE_RSS_090', 1); + +/** + * RSS 0.91 (Netscape) + */ +define('SIMPLEPIE_TYPE_RSS_091_NETSCAPE', 2); + +/** + * RSS 0.91 (Userland) + */ +define('SIMPLEPIE_TYPE_RSS_091_USERLAND', 4); + +/** + * RSS 0.91 (both Netscape and Userland) + */ +define('SIMPLEPIE_TYPE_RSS_091', 6); + +/** + * RSS 0.92 + */ +define('SIMPLEPIE_TYPE_RSS_092', 8); + +/** + * RSS 0.93 + */ +define('SIMPLEPIE_TYPE_RSS_093', 16); + +/** + * RSS 0.94 + */ +define('SIMPLEPIE_TYPE_RSS_094', 32); + +/** + * RSS 1.0 + */ +define('SIMPLEPIE_TYPE_RSS_10', 64); + +/** + * RSS 2.0 + */ +define('SIMPLEPIE_TYPE_RSS_20', 128); + +/** + * RDF-based RSS + */ +define('SIMPLEPIE_TYPE_RSS_RDF', 65); + +/** + * Non-RDF-based RSS (truly intended as syndication format) + */ +define('SIMPLEPIE_TYPE_RSS_SYNDICATION', 190); + +/** + * All RSS + */ +define('SIMPLEPIE_TYPE_RSS_ALL', 255); + +/** + * Atom 0.3 + */ +define('SIMPLEPIE_TYPE_ATOM_03', 256); + +/** + * Atom 1.0 + */ +define('SIMPLEPIE_TYPE_ATOM_10', 512); + +/** + * All Atom + */ +define('SIMPLEPIE_TYPE_ATOM_ALL', 768); + +/** + * All feed types + */ +define('SIMPLEPIE_TYPE_ALL', 1023); + +/** + * No construct + */ +define('SIMPLEPIE_CONSTRUCT_NONE', 0); + +/** + * Text construct + */ +define('SIMPLEPIE_CONSTRUCT_TEXT', 1); + +/** + * HTML construct + */ +define('SIMPLEPIE_CONSTRUCT_HTML', 2); + +/** + * XHTML construct + */ +define('SIMPLEPIE_CONSTRUCT_XHTML', 4); + +/** + * base64-encoded construct + */ +define('SIMPLEPIE_CONSTRUCT_BASE64', 8); + +/** + * IRI construct + */ +define('SIMPLEPIE_CONSTRUCT_IRI', 16); + +/** + * A construct that might be HTML + */ +define('SIMPLEPIE_CONSTRUCT_MAYBE_HTML', 32); + +/** + * All constructs + */ +define('SIMPLEPIE_CONSTRUCT_ALL', 63); + +/** + * PCRE for HTML attributes + */ +define('SIMPLEPIE_PCRE_HTML_ATTRIBUTE', '((?:[\x09\x0A\x0B\x0C\x0D\x20]+[^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3D\x3E]*(?:[\x09\x0A\x0B\x0C\x0D\x20]*=[\x09\x0A\x0B\x0C\x0D\x20]*(?:"(?:[^"]*)"|\'(?:[^\']*)\'|(?:[^\x09\x0A\x0B\x0C\x0D\x20\x22\x27\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x3E]*)?))?)*)[\x09\x0A\x0B\x0C\x0D\x20]*'); + +/** + * PCRE for XML attributes + */ +define('SIMPLEPIE_PCRE_XML_ATTRIBUTE', '((?:\s+(?:(?:[^\s:]+:)?[^\s:]+)\s*=\s*(?:"(?:[^"]*)"|\'(?:[^\']*)\'))*)\s*'); + +/** + * XML Namespace + */ +define('SIMPLEPIE_NAMESPACE_XML', 'http://www.w3.org/XML/1998/namespace'); + +/** + * Atom 1.0 Namespace + */ +define('SIMPLEPIE_NAMESPACE_ATOM_10', 'http://www.w3.org/2005/Atom'); + +/** + * Atom 0.3 Namespace + */ +define('SIMPLEPIE_NAMESPACE_ATOM_03', 'http://purl.org/atom/ns#'); + +/** + * RDF Namespace + */ +define('SIMPLEPIE_NAMESPACE_RDF', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'); + +/** + * RSS 0.90 Namespace + */ +define('SIMPLEPIE_NAMESPACE_RSS_090', 'http://my.netscape.com/rdf/simple/0.9/'); + +/** + * RSS 1.0 Namespace + */ +define('SIMPLEPIE_NAMESPACE_RSS_10', 'http://purl.org/rss/1.0/'); + +/** + * RSS 1.0 Content Module Namespace + */ +define('SIMPLEPIE_NAMESPACE_RSS_10_MODULES_CONTENT', 'http://purl.org/rss/1.0/modules/content/'); + +/** + * DC 1.0 Namespace + */ +define('SIMPLEPIE_NAMESPACE_DC_10', 'http://purl.org/dc/elements/1.0/'); + +/** + * DC 1.1 Namespace + */ +define('SIMPLEPIE_NAMESPACE_DC_11', 'http://purl.org/dc/elements/1.1/'); + +/** + * W3C Basic Geo (WGS84 lat/long) Vocabulary Namespace + */ +define('SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO', 'http://www.w3.org/2003/01/geo/wgs84_pos#'); + +/** + * GeoRSS Namespace + */ +define('SIMPLEPIE_NAMESPACE_GEORSS', 'http://www.georss.org/georss'); + +/** + * Media RSS Namespace + */ +define('SIMPLEPIE_NAMESPACE_MEDIARSS', 'http://search.yahoo.com/mrss/'); + +/** + * Wrong Media RSS Namespace + */ +define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG', 'http://search.yahoo.com/mrss'); + +/** + * iTunes RSS Namespace + */ +define('SIMPLEPIE_NAMESPACE_ITUNES', 'http://www.itunes.com/dtds/podcast-1.0.dtd'); + +/** + * XHTML Namespace + */ +define('SIMPLEPIE_NAMESPACE_XHTML', 'http://www.w3.org/1999/xhtml'); + +/** + * IANA Link Relations Registry + */ +define('SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY', 'http://www.iana.org/assignments/relation/'); + +/** + * Whether we're running on PHP5 + */ +define('SIMPLEPIE_PHP5', version_compare(PHP_VERSION, '5.0.0', '>=')); + +/** + * No file source + */ +define('SIMPLEPIE_FILE_SOURCE_NONE', 0); + +/** + * Remote file source + */ +define('SIMPLEPIE_FILE_SOURCE_REMOTE', 1); + +/** + * Local file source + */ +define('SIMPLEPIE_FILE_SOURCE_LOCAL', 2); + +/** + * fsockopen() file source + */ +define('SIMPLEPIE_FILE_SOURCE_FSOCKOPEN', 4); + +/** + * cURL file source + */ +define('SIMPLEPIE_FILE_SOURCE_CURL', 8); + +/** + * file_get_contents() file source + */ +define('SIMPLEPIE_FILE_SOURCE_FILE_GET_CONTENTS', 16); + +/** + * SimplePie + * + * @package SimplePie + * @version "Razzleberry" + * @copyright 2004-2007 Ryan Parman, Geoffrey Sneddon + * @author Ryan Parman + * @author Geoffrey Sneddon + * @todo Option for type of fetching (cache, not modified header, fetch, etc.) + */ +class SimplePie +{ + /** + * @var array Raw data + * @access private + */ + var $data = array(); + + /** + * @var mixed Error string + * @access private + */ + var $error; + + /** + * @var object Instance of SimplePie_Sanitize (or other class) + * @see SimplePie::set_sanitize_class() + * @access private + */ + var $sanitize; + + /** + * @var string SimplePie Useragent + * @see SimplePie::set_useragent() + * @access private + */ + var $useragent = SIMPLEPIE_USERAGENT; + + /** + * @var string Feed URL + * @see SimplePie::set_feed_url() + * @access private + */ + var $feed_url; + + /** + * @var object Instance of SimplePie_File to use as a feed + * @see SimplePie::set_file() + * @access private + */ + var $file; + + /** + * @var string Raw feed data + * @see SimplePie::set_raw_data() + * @access private + */ + var $raw_data; + + /** + * @var int Timeout for fetching remote files + * @see SimplePie::set_timeout() + * @access private + */ + var $timeout = 10; + + /** + * @var bool Forces fsockopen() to be used for remote files instead + * of cURL, even if a new enough version is installed + * @see SimplePie::force_fsockopen() + * @access private + */ + var $force_fsockopen = false; + + /** + * @var bool Force the given data/URL to be treated as a feed no matter what + * it appears like + * @see SimplePie::force_feed() + * @access private + */ + var $force_feed = false; + + /** + * @var bool Enable/Disable XML dump + * @see SimplePie::enable_xml_dump() + * @access private + */ + var $xml_dump = false; + + /** + * @var bool Enable/Disable Caching + * @see SimplePie::enable_cache() + * @access private + */ + var $cache = true; + + /** + * @var int Cache duration (in seconds) + * @see SimplePie::set_cache_duration() + * @access private + */ + var $cache_duration = 3600; + + /** + * @var int Auto-discovery cache duration (in seconds) + * @see SimplePie::set_autodiscovery_cache_duration() + * @access private + */ + var $autodiscovery_cache_duration = 604800; // 7 Days. + + /** + * @var string Cache location (relative to executing script) + * @see SimplePie::set_cache_location() + * @access private + */ + var $cache_location = './cache'; + + /** + * @var string Function that creates the cache filename + * @see SimplePie::set_cache_name_function() + * @access private + */ + var $cache_name_function = 'md5'; + + /** + * @var bool Reorder feed by date descending + * @see SimplePie::enable_order_by_date() + * @access private + */ + var $order_by_date = true; + + /** + * @var mixed Force input encoding to be set to the follow value + * (false, or anything type-cast to false, disables this feature) + * @see SimplePie::set_input_encoding() + * @access private + */ + var $input_encoding = false; + + /** + * @var int Feed Autodiscovery Level + * @see SimplePie::set_autodiscovery_level() + * @access private + */ + var $autodiscovery = SIMPLEPIE_LOCATOR_ALL; + + /** + * @var string Class used for caching feeds + * @see SimplePie::set_cache_class() + * @access private + */ + var $cache_class = 'SimplePie_Cache'; + + /** + * @var string Class used for locating feeds + * @see SimplePie::set_locator_class() + * @access private + */ + var $locator_class = 'SimplePie_Locator'; + + /** + * @var string Class used for parsing feeds + * @see SimplePie::set_parser_class() + * @access private + */ + var $parser_class = 'SimplePie_Parser'; + + /** + * @var string Class used for fetching feeds + * @see SimplePie::set_file_class() + * @access private + */ + var $file_class = 'SimplePie_File'; + + /** + * @var string Class used for items + * @see SimplePie::set_item_class() + * @access private + */ + var $item_class = 'SimplePie_Item'; + + /** + * @var string Class used for authors + * @see SimplePie::set_author_class() + * @access private + */ + var $author_class = 'SimplePie_Author'; + + /** + * @var string Class used for categories + * @see SimplePie::set_category_class() + * @access private + */ + var $category_class = 'SimplePie_Category'; + + /** + * @var string Class used for enclosures + * @see SimplePie::set_enclosures_class() + * @access private + */ + var $enclosure_class = 'SimplePie_Enclosure'; + + /** + * @var string Class used for Media RSS captions + * @see SimplePie::set_caption_class() + * @access private + */ + var $caption_class = 'SimplePie_Caption'; + + /** + * @var string Class used for Media RSS + * @see SimplePie::set_copyright_class() + * @access private + */ + var $copyright_class = 'SimplePie_Copyright'; + + /** + * @var string Class used for Media RSS + * @see SimplePie::set_credit_class() + * @access private + */ + var $credit_class = 'SimplePie_Credit'; + + /** + * @var string Class used for Media RSS + * @see SimplePie::set_rating_class() + * @access private + */ + var $rating_class = 'SimplePie_Rating'; + + /** + * @var string Class used for Media RSS + * @see SimplePie::set_restriction_class() + * @access private + */ + var $restriction_class = 'SimplePie_Restriction'; + + /** + * @var string Class used for content-type sniffing + * @see SimplePie::set_content_type_sniffer_class() + * @access private + */ + var $content_type_sniffer_class = 'SimplePie_Content_Type_Sniffer'; + + /** + * @var string Class used for item sources. + * @see SimplePie::set_source_class() + * @access private + */ + var $source_class = 'SimplePie_Source'; + + /** + * @var mixed Set javascript query string parameter (false, or + * anything type-cast to false, disables this feature) + * @see SimplePie::set_javascript() + * @access private + */ + var $javascript = 'js'; + + /** + * @var int Maximum number of feeds to check with autodiscovery + * @see SimplePie::set_max_checked_feeds() + * @access private + */ + var $max_checked_feeds = 10; + + /** + * @var string Web-accessible path to the handler_favicon.php file. + * @see SimplePie::set_favicon_handler() + * @access private + */ + var $favicon_handler = ''; + + /** + * @var string Web-accessible path to the handler_image.php file. + * @see SimplePie::set_image_handler() + * @access private + */ + var $image_handler = ''; + + /** + * @var array Stores the URLs when multiple feeds are being initialized. + * @see SimplePie::set_feed_url() + * @access private + */ + var $multifeed_url = array(); + + /** + * @var array Stores SimplePie objects when multiple feeds initialized. + * @access private + */ + var $multifeed_objects = array(); + + /** + * @var array Stores the get_object_vars() array for use with multifeeds. + * @see SimplePie::set_feed_url() + * @access private + */ + var $config_settings = null; + + /** + * @var integer Stores the number of items to return per-feed with multifeeds. + * @see SimplePie::set_item_limit() + * @access private + */ + var $item_limit = 0; + + /** + * @var array Stores the default attributes to be stripped by strip_attributes(). + * @see SimplePie::strip_attributes() + * @access private + */ + var $strip_attributes = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc'); + + /** + * @var array Stores the default tags to be stripped by strip_htmltags(). + * @see SimplePie::strip_htmltags() + * @access private + */ + var $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style'); + + /** + * The SimplePie class contains feed level data and options + * + * There are two ways that you can create a new SimplePie object. The first + * is by passing a feed URL as a parameter to the SimplePie constructor + * (as well as optionally setting the cache location and cache expiry). This + * will initialise the whole feed with all of the default settings, and you + * can begin accessing methods and properties immediately. + * + * The second way is to create the SimplePie object with no parameters + * at all. This will enable you to set configuration options. After setting + * them, you must initialise the feed using $feed->init(). At that point the + * object's methods and properties will be available to you. This format is + * what is used throughout this documentation. + * + * @access public + * @since 1.0 Preview Release + * @param string $feed_url This is the URL you want to parse. + * @param string $cache_location This is where you want the cache to be stored. + * @param int $cache_duration This is the number of seconds that you want to store the cache file for. + */ + function SimplePie($feed_url = null, $cache_location = null, $cache_duration = null) + { + // Other objects, instances created here so we can set options on them + $this->sanitize =& new SimplePie_Sanitize; + + // Set options if they're passed to the constructor + if ($cache_location !== null) + { + $this->set_cache_location($cache_location); + } + + if ($cache_duration !== null) + { + $this->set_cache_duration($cache_duration); + } + + // Only init the script if we're passed a feed URL + if ($feed_url !== null) + { + $this->set_feed_url($feed_url); + $this->init(); + } + } + + /** + * Used for converting object to a string + */ + function __toString() + { + return md5(serialize($this->data)); + } + + /** + * Remove items that link back to this before destroying this object + */ + function __destruct() + { + if (!empty($this->data['items'])) + { + foreach ($this->data['items'] as $item) + { + $item->__destruct(); + } + unset($this->data['items']); + } + if (!empty($this->data['ordered_items'])) + { + foreach ($this->data['ordered_items'] as $item) + { + $item->__destruct(); + } + unset($this->data['ordered_items']); + } + } + + /** + * Force the given data/URL to be treated as a feed no matter what it + * appears like + * + * @access public + * @since 1.1 + * @param bool $enable Force the given data/URL to be treated as a feed + */ + function force_feed($enable = false) + { + $this->force_feed = (bool) $enable; + } + + /** + * This is the URL of the feed you want to parse. + * + * This allows you to enter the URL of the feed you want to parse, or the + * website you want to try to use auto-discovery on. This takes priority + * over any set raw data. + * + * You can set multiple feeds to mash together by passing an array instead + * of a string for the $url. Remember that with each additional feed comes + * additional processing and resources. + * + * @access public + * @since 1.0 Preview Release + * @param mixed $url This is the URL (or array of URLs) that you want to parse. + * @see SimplePie::set_raw_data() + */ + function set_feed_url($url) + { + if (is_array($url)) + { + $this->multifeed_url = array(); + foreach ($url as $value) + { + $this->multifeed_url[] = SimplePie_Misc::fix_protocol($value, 1); + } + } + else + { + $this->feed_url = SimplePie_Misc::fix_protocol($url, 1); + } + } + + /** + * Provides an instance of SimplePie_File to use as a feed + * + * @access public + * @param object &$file Instance of SimplePie_File (or subclass) + * @return bool True on success, false on failure + */ + function set_file(&$file) + { + if (is_a($file, 'SimplePie_File')) + { + $this->feed_url = $file->url; + $this->file =& $file; + return true; + } + return false; + } + + /** + * Allows you to use a string of RSS/Atom data instead of a remote feed. + * + * If you have a feed available as a string in PHP, you can tell SimplePie + * to parse that data string instead of a remote feed. Any set feed URL + * takes precedence. + * + * @access public + * @since 1.0 Beta 3 + * @param string $data RSS or Atom data as a string. + * @see SimplePie::set_feed_url() + */ + function set_raw_data($data) + { + $this->raw_data = $data; + } + + /** + * Allows you to override the default timeout for fetching remote feeds. + * + * This allows you to change the maximum time the feed's server to respond + * and send the feed back. + * + * @access public + * @since 1.0 Beta 3 + * @param int $timeout The maximum number of seconds to spend waiting to retrieve a feed. + */ + function set_timeout($timeout = 10) + { + $this->timeout = (int) $timeout; + } + + /** + * Forces SimplePie to use fsockopen() instead of the preferred cURL + * functions. + * + * @access public + * @since 1.0 Beta 3 + * @param bool $enable Force fsockopen() to be used + */ + function force_fsockopen($enable = false) + { + $this->force_fsockopen = (bool) $enable; + } + + /** + * Outputs the raw XML content of the feed, after it has gone through + * SimplePie's filters. + * + * Used only for debugging, this function will output the XML content as + * text/xml. When SimplePie reads in a feed, it does a bit of cleaning up + * before trying to parse it. Many parts of the feed are re-written in + * memory, and in the end, you have a parsable feed. XML dump shows you the + * actual XML that SimplePie tries to parse, which may or may not be very + * different from the original feed. + * + * @access public + * @since 1.0 Preview Release + * @param bool $enable Enable XML dump + */ + function enable_xml_dump($enable = false) + { + $this->xml_dump = (bool) $enable; + } + + /** + * Enables/disables caching in SimplePie. + * + * This option allows you to disable caching all-together in SimplePie. + * However, disabling the cache can lead to longer load times. + * + * @access public + * @since 1.0 Preview Release + * @param bool $enable Enable caching + */ + function enable_cache($enable = true) + { + $this->cache = (bool) $enable; + } + + /** + * Set the length of time (in seconds) that the contents of a feed + * will be cached. + * + * @access public + * @param int $seconds The feed content cache duration. + */ + function set_cache_duration($seconds = 3600) + { + $this->cache_duration = (int) $seconds; + } + + /** + * Set the length of time (in seconds) that the autodiscovered feed + * URL will be cached. + * + * @access public + * @param int $seconds The autodiscovered feed URL cache duration. + */ + function set_autodiscovery_cache_duration($seconds = 604800) + { + $this->autodiscovery_cache_duration = (int) $seconds; + } + + /** + * Set the file system location where the cached files should be stored. + * + * @access public + * @param string $location The file system location. + */ + function set_cache_location($location = './cache') + { + $this->cache_location = (string) $location; + } + + /** + * Determines whether feed items should be sorted into reverse chronological order. + * + * @access public + * @param bool $enable Sort as reverse chronological order. + */ + function enable_order_by_date($enable = true) + { + $this->order_by_date = (bool) $enable; + } + + /** + * Allows you to override the character encoding reported by the feed. + * + * @access public + * @param string $encoding Character encoding. + */ + function set_input_encoding($encoding = false) + { + if ($encoding) + { + $this->input_encoding = (string) $encoding; + } + else + { + $this->input_encoding = false; + } + } + + /** + * Set how much feed autodiscovery to do + * + * @access public + * @see SIMPLEPIE_LOCATOR_NONE + * @see SIMPLEPIE_LOCATOR_AUTODISCOVERY + * @see SIMPLEPIE_LOCATOR_LOCAL_EXTENSION + * @see SIMPLEPIE_LOCATOR_LOCAL_BODY + * @see SIMPLEPIE_LOCATOR_REMOTE_EXTENSION + * @see SIMPLEPIE_LOCATOR_REMOTE_BODY + * @see SIMPLEPIE_LOCATOR_ALL + * @param int $level Feed Autodiscovery Level (level can be a + * combination of the above constants, see bitwise OR operator) + */ + function set_autodiscovery_level($level = SIMPLEPIE_LOCATOR_ALL) + { + $this->autodiscovery = (int) $level; + } + + /** + * Allows you to change which class SimplePie uses for caching. + * Useful when you are overloading or extending SimplePie's default classes. + * + * @access public + * @param string $class Name of custom class. + * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation + * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + */ + function set_cache_class($class = 'SimplePie_Cache') + { + if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Cache')) + { + $this->cache_class = $class; + return true; + } + return false; + } + + /** + * Allows you to change which class SimplePie uses for auto-discovery. + * Useful when you are overloading or extending SimplePie's default classes. + * + * @access public + * @param string $class Name of custom class. + * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation + * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + */ + function set_locator_class($class = 'SimplePie_Locator') + { + if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Locator')) + { + $this->locator_class = $class; + return true; + } + return false; + } + + /** + * Allows you to change which class SimplePie uses for XML parsing. + * Useful when you are overloading or extending SimplePie's default classes. + * + * @access public + * @param string $class Name of custom class. + * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation + * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + */ + function set_parser_class($class = 'SimplePie_Parser') + { + if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Parser')) + { + $this->parser_class = $class; + return true; + } + return false; + } + + /** + * Allows you to change which class SimplePie uses for remote file fetching. + * Useful when you are overloading or extending SimplePie's default classes. + * + * @access public + * @param string $class Name of custom class. + * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation + * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + */ + function set_file_class($class = 'SimplePie_File') + { + if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_File')) + { + $this->file_class = $class; + return true; + } + return false; + } + + /** + * Allows you to change which class SimplePie uses for data sanitization. + * Useful when you are overloading or extending SimplePie's default classes. + * + * @access public + * @param string $class Name of custom class. + * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation + * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + */ + function set_sanitize_class($class = 'SimplePie_Sanitize') + { + if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Sanitize')) + { + $this->sanitize =& new $class; + return true; + } + return false; + } + + /** + * Allows you to change which class SimplePie uses for handling feed items. + * Useful when you are overloading or extending SimplePie's default classes. + * + * @access public + * @param string $class Name of custom class. + * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation + * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + */ + function set_item_class($class = 'SimplePie_Item') + { + if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Item')) + { + $this->item_class = $class; + return true; + } + return false; + } + + /** + * Allows you to change which class SimplePie uses for handling author data. + * Useful when you are overloading or extending SimplePie's default classes. + * + * @access public + * @param string $class Name of custom class. + * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation + * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + */ + function set_author_class($class = 'SimplePie_Author') + { + if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Author')) + { + $this->author_class = $class; + return true; + } + return false; + } + + /** + * Allows you to change which class SimplePie uses for handling category data. + * Useful when you are overloading or extending SimplePie's default classes. + * + * @access public + * @param string $class Name of custom class. + * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation + * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + */ + function set_category_class($class = 'SimplePie_Category') + { + if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Category')) + { + $this->category_class = $class; + return true; + } + return false; + } + + /** + * Allows you to change which class SimplePie uses for feed enclosures. + * Useful when you are overloading or extending SimplePie's default classes. + * + * @access public + * @param string $class Name of custom class. + * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation + * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + */ + function set_enclosure_class($class = 'SimplePie_Enclosure') + { + if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Enclosure')) + { + $this->enclosure_class = $class; + return true; + } + return false; + } + + /** + * Allows you to change which class SimplePie uses for captions + * Useful when you are overloading or extending SimplePie's default classes. + * + * @access public + * @param string $class Name of custom class. + * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation + * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + */ + function set_caption_class($class = 'SimplePie_Caption') + { + if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Caption')) + { + $this->caption_class = $class; + return true; + } + return false; + } + + /** + * Allows you to change which class SimplePie uses for + * Useful when you are overloading or extending SimplePie's default classes. + * + * @access public + * @param string $class Name of custom class. + * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation + * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + */ + function set_copyright_class($class = 'SimplePie_Copyright') + { + if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Copyright')) + { + $this->copyright_class = $class; + return true; + } + return false; + } + + /** + * Allows you to change which class SimplePie uses for + * Useful when you are overloading or extending SimplePie's default classes. + * + * @access public + * @param string $class Name of custom class. + * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation + * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + */ + function set_credit_class($class = 'SimplePie_Credit') + { + if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Credit')) + { + $this->credit_class = $class; + return true; + } + return false; + } + + /** + * Allows you to change which class SimplePie uses for + * Useful when you are overloading or extending SimplePie's default classes. + * + * @access public + * @param string $class Name of custom class. + * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation + * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + */ + function set_rating_class($class = 'SimplePie_Rating') + { + if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Rating')) + { + $this->rating_class = $class; + return true; + } + return false; + } + + /** + * Allows you to change which class SimplePie uses for + * Useful when you are overloading or extending SimplePie's default classes. + * + * @access public + * @param string $class Name of custom class. + * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation + * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + */ + function set_restriction_class($class = 'SimplePie_Restriction') + { + if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Restriction')) + { + $this->restriction_class = $class; + return true; + } + return false; + } + + /** + * Allows you to change which class SimplePie uses for content-type sniffing. + * Useful when you are overloading or extending SimplePie's default classes. + * + * @access public + * @param string $class Name of custom class. + * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation + * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + */ + function set_content_type_sniffer_class($class = 'SimplePie_Content_Type_Sniffer') + { + if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Content_Type_Sniffer')) + { + $this->content_type_sniffer_class = $class; + return true; + } + return false; + } + + /** + * Allows you to change which class SimplePie uses item sources. + * Useful when you are overloading or extending SimplePie's default classes. + * + * @access public + * @param string $class Name of custom class. + * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation + * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + */ + function set_source_class($class = 'SimplePie_Source') + { + if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Source')) + { + $this->source_class = $class; + return true; + } + return false; + } + + /** + * Allows you to override the default user agent string. + * + * @access public + * @param string $ua New user agent string. + */ + function set_useragent($ua = SIMPLEPIE_USERAGENT) + { + $this->useragent = (string) $ua; + } + + /** + * Set callback function to create cache filename with + * + * @access public + * @param mixed $function Callback function + */ + function set_cache_name_function($function = 'md5') + { + if (is_callable($function)) + { + $this->cache_name_function = $function; + } + } + + /** + * Set javascript query string parameter + * + * @access public + * @param mixed $get Javascript query string parameter + */ + function set_javascript($get = 'js') + { + if ($get) + { + $this->javascript = (string) $get; + } + else + { + $this->javascript = false; + } + } + + /** + * Set options to make SP as fast as possible. Forgoes a + * substantial amount of data sanitization in favor of speed. + * + * @access public + * @param bool $set Whether to set them or not + */ + function set_stupidly_fast($set = false) + { + if ($set) + { + $this->enable_order_by_date(false); + $this->remove_div(false); + $this->strip_comments(false); + $this->strip_htmltags(false); + $this->strip_attributes(false); + $this->set_image_handler(false); + } + } + + /** + * Set maximum number of feeds to check with autodiscovery + * + * @access public + * @param int $max Maximum number of feeds to check + */ + function set_max_checked_feeds($max = 10) + { + $this->max_checked_feeds = (int) $max; + } + + function remove_div($enable = true) + { + $this->sanitize->remove_div($enable); + } + + function strip_htmltags($tags = '', $encode = null) + { + if ($tags === '') + { + $tags = $this->strip_htmltags; + } + $this->sanitize->strip_htmltags($tags); + if ($encode !== null) + { + $this->sanitize->encode_instead_of_strip($tags); + } + } + + function encode_instead_of_strip($enable = true) + { + $this->sanitize->encode_instead_of_strip($enable); + } + + function strip_attributes($attribs = '') + { + if ($attribs === '') + { + $attribs = $this->strip_attributes; + } + $this->sanitize->strip_attributes($attribs); + } + + function set_output_encoding($encoding = 'UTF-8') + { + $this->sanitize->set_output_encoding($encoding); + } + + function strip_comments($strip = false) + { + $this->sanitize->strip_comments($strip); + } + + /** + * Set element/attribute key/value pairs of HTML attributes + * containing URLs that need to be resolved relative to the feed + * + * @access public + * @since 1.0 + * @param array $element_attribute Element/attribute key/value pairs + */ + function set_url_replacements($element_attribute = array('a' => 'href', 'area' => 'href', 'blockquote' => 'cite', 'del' => 'cite', 'form' => 'action', 'img' => array('longdesc', 'src'), 'input' => 'src', 'ins' => 'cite', 'q' => 'cite')) + { + $this->sanitize->set_url_replacements($element_attribute); + } + + /** + * Set the handler to enable the display of cached favicons. + * + * @access public + * @param str $page Web-accessible path to the handler_favicon.php file. + * @param str $qs The query string that the value should be passed to. + */ + function set_favicon_handler($page = false, $qs = 'i') + { + if ($page != false) + { + $this->favicon_handler = $page . '?' . $qs . '='; + } + else + { + $this->favicon_handler = ''; + } + } + + /** + * Set the handler to enable the display of cached images. + * + * @access public + * @param str $page Web-accessible path to the handler_image.php file. + * @param str $qs The query string that the value should be passed to. + */ + function set_image_handler($page = false, $qs = 'i') + { + if ($page != false) + { + $this->sanitize->set_image_handler($page . '?' . $qs . '='); + } + else + { + $this->image_handler = ''; + } + } + + /** + * Set the limit for items returned per-feed with multifeeds. + * + * @access public + * @param integer $limit The maximum number of items to return. + */ + function set_item_limit($limit = 0) + { + $this->item_limit = (int) $limit; + } + + function init() + { + if ((function_exists('version_compare') && version_compare(PHP_VERSION, '4.3.0', '<')) || !extension_loaded('xml') || !extension_loaded('pcre')) + { + return false; + } + if (isset($_GET[$this->javascript])) + { + if (function_exists('ob_gzhandler')) + { + ob_start('ob_gzhandler'); + } + header('Content-type: text/javascript; charset: UTF-8'); + header('Cache-Control: must-revalidate'); + header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 604800) . ' GMT'); // 7 days + ?> +function embed_odeo(link) { + document.writeln(''); +} + +function embed_quicktime(type, bgcolor, width, height, link, placeholder, loop) { + if (placeholder != '') { + document.writeln(''); + } + else { + document.writeln(''); + } +} + +function embed_flash(bgcolor, width, height, link, loop, type) { + document.writeln(''); +} + +function embed_flv(width, height, link, placeholder, loop, player) { + document.writeln(''); +} + +function embed_wmedia(width, height, link) { + document.writeln(''); +} + sanitize->pass_cache_data($this->cache, $this->cache_location, $this->cache_name_function, $this->cache_class); + $this->sanitize->pass_file_data($this->file_class, $this->timeout, $this->useragent, $this->force_fsockopen); + + if ($this->feed_url !== null || $this->raw_data !== null) + { + $this->data = array(); + $this->multifeed_objects = array(); + $cache = false; + + if ($this->feed_url !== null) + { + $parsed_feed_url = SimplePie_Misc::parse_url($this->feed_url); + // Decide whether to enable caching + if ($this->cache && $parsed_feed_url['scheme'] !== '') + { + $cache = call_user_func(array($this->cache_class, 'create'), $this->cache_location, call_user_func($this->cache_name_function, $this->feed_url), 'spc'); + } + // If it's enabled and we don't want an XML dump, use the cache + if ($cache && !$this->xml_dump) + { + // Load the Cache + $this->data = $cache->load(); + if (!empty($this->data)) + { + // If the cache is for an outdated build of SimplePie + if (!isset($this->data['build']) || $this->data['build'] != SIMPLEPIE_BUILD) + { + $cache->unlink(); + $this->data = array(); + } + // If we've hit a collision just rerun it with caching disabled + elseif (isset($this->data['url']) && $this->data['url'] != $this->feed_url) + { + $cache = false; + $this->data = array(); + } + // If we've got a non feed_url stored (if the page isn't actually a feed, or is a redirect) use that URL. + elseif (isset($this->data['feed_url'])) + { + // If the autodiscovery cache is still valid use it. + if ($cache->mtime() + $this->autodiscovery_cache_duration > time()) + { + // Do not need to do feed autodiscovery yet. + if ($this->data['feed_url'] == $this->data['url']) + { + $cache->unlink(); + $this->data = array(); + } + else + { + $this->set_feed_url($this->data['feed_url']); + return $this->init(); + } + } + } + // Check if the cache has been updated + elseif ($cache->mtime() + $this->cache_duration < time()) + { + // If we have last-modified and/or etag set + if (isset($this->data['headers']['last-modified']) || isset($this->data['headers']['etag'])) + { + $headers = array(); + if (isset($this->data['headers']['last-modified'])) + { + $headers['if-modified-since'] = $this->data['headers']['last-modified']; + } + if (isset($this->data['headers']['etag'])) + { + $headers['if-none-match'] = '"' . $this->data['headers']['etag'] . '"'; + } + $file =& new $this->file_class($this->feed_url, $this->timeout/10, 5, $headers, $this->useragent, $this->force_fsockopen); + if ($file->success) + { + if ($file->status_code == 304) + { + $cache->touch(); + return true; + } + else + { + $headers = $file->headers; + } + } + else + { + unset($file); + } + } + } + // If the cache is still valid, just return true + else + { + return true; + } + } + // If the cache is empty, delete it + else + { + $cache->unlink(); + $this->data = array(); + } + } + // If we don't already have the file (it'll only exist if we've opened it to check if the cache has been modified), open it. + if (!isset($file)) + { + if (is_a($this->file, 'SimplePie_File') && $this->file->url == $this->feed_url) + { + $file =& $this->file; + } + else + { + $file =& new $this->file_class($this->feed_url, $this->timeout, 5, null, $this->useragent, $this->force_fsockopen); + } + } + // If the file connection has an error, set SimplePie::error to that and quit + if (!$file->success) + { + $this->error = $file->error; + if (!empty($this->data)) + { + return true; + } + else + { + return false; + } + } + + if (!$this->force_feed) + { + // Check if the supplied URL is a feed, if it isn't, look for it. + $locate =& new $this->locator_class($file, $this->timeout, $this->useragent, $this->file_class, $this->max_checked_feeds, $this->content_type_sniffer_class); + if (!$locate->is_feed($file)) + { + // We need to unset this so that if SimplePie::set_file() has been called that object is untouched + unset($file); + if ($file = $locate->find($this->autodiscovery)) + { + if ($cache) + { + $this->data = array('url' => $this->feed_url, 'feed_url' => $file->url, 'build' => SIMPLEPIE_BUILD); + if (!$cache->save($this)) + { + trigger_error("$cache->name is not writeable", E_USER_WARNING); + } + $cache = call_user_func(array($this->cache_class, 'create'), $this->cache_location, call_user_func($this->cache_name_function, $file->url), 'spc'); + } + $this->feed_url = $file->url; + } + else + { + $this->error = "A feed could not be found at $this->feed_url"; + SimplePie_Misc::error($this->error, E_USER_NOTICE, __FILE__, __LINE__); + return false; + } + } + $locate = null; + } + + $headers = $file->headers; + $data = $file->body; + $sniffer = new $this->content_type_sniffer_class($file); + $sniffed = $sniffer->get_type(); + } + else + { + $data = $this->raw_data; + } + + // Set up array of possible encodings + $encodings = array(); + + // First check to see if input has been overridden. + if ($this->input_encoding !== false) + { + $encodings[] = $this->input_encoding; + } + + $application_types = array('application/xml', 'application/xml-dtd', 'application/xml-external-parsed-entity'); + $text_types = array('text/xml', 'text/xml-external-parsed-entity'); + + // RFC 3023 (only applies to sniffed content) + if (isset($sniffed)) + { + if (in_array($sniffed, $application_types) || substr($sniffed, 0, 12) === 'application/' && substr($sniffed, -4) === '+xml') + { + if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset)) + { + $encodings[] = strtoupper($charset[1]); + } + $encodings = array_merge($encodings, SimplePie_Misc::xml_encoding($data)); + $encodings[] = 'UTF-8'; + } + elseif (in_array($sniffed, $text_types) || substr($sniffed, 0, 5) === 'text/' && substr($sniffed, -4) === '+xml') + { + if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset)) + { + $encodings[] = $charset[1]; + } + $encodings[] = 'US-ASCII'; + } + // Text MIME-type default + elseif (substr($sniffed, 0, 5) === 'text/') + { + $encodings[] = 'US-ASCII'; + } + } + + // Fallback to XML 1.0 Appendix F.1/UTF-8/ISO-8859-1 + $encodings = array_merge($encodings, SimplePie_Misc::xml_encoding($data)); + $encodings[] = 'UTF-8'; + $encodings[] = 'ISO-8859-1'; + + // There's no point in trying an encoding twice + $encodings = array_unique($encodings); + + // If we want the XML, just output that with the most likely encoding and quit + if ($this->xml_dump) + { + header('Content-type: text/xml; charset=' . $encodings[0]); + echo $data; + exit; + } + + // Loop through each possible encoding, till we return something, or run out of possibilities + foreach ($encodings as $encoding) + { + // Change the encoding to UTF-8 (as we always use UTF-8 internally) + $utf8_data = SimplePie_Misc::change_encoding($data, $encoding, 'UTF-8'); + + // Create new parser + $parser =& new $this->parser_class(); + + // If it's parsed fine + if ($parser->parse($utf8_data, 'UTF-8')) + { + $this->data = $parser->get_data(); + if (isset($this->data['child'])) + { + if (isset($headers)) + { + $this->data['headers'] = $headers; + } + $this->data['build'] = SIMPLEPIE_BUILD; + + // Cache the file if caching is enabled + if ($cache && !$cache->save($this)) + { + trigger_error("$cache->name is not writeable", E_USER_WARNING); + } + return true; + } + else + { + $this->error = "A feed could not be found at $this->feed_url"; + SimplePie_Misc::error($this->error, E_USER_NOTICE, __FILE__, __LINE__); + return false; + } + } + } + // We have an error, just set SimplePie::error to it and quit + $this->error = sprintf('XML error: %s at line %d, column %d', $parser->get_error_string(), $parser->get_current_line(), $parser->get_current_column()); + SimplePie_Misc::error($this->error, E_USER_NOTICE, __FILE__, __LINE__); + return false; + } + elseif (!empty($this->multifeed_url)) + { + $i = 0; + $success = 0; + $this->multifeed_objects = array(); + foreach ($this->multifeed_url as $url) + { + if (SIMPLEPIE_PHP5) + { + // This keyword needs to defy coding standards for PHP4 compatibility + $this->multifeed_objects[$i] = clone($this); + } + else + { + $this->multifeed_objects[$i] = $this; + } + $this->multifeed_objects[$i]->set_feed_url($url); + $success |= $this->multifeed_objects[$i]->init(); + $i++; + } + return (bool) $success; + } + else + { + return false; + } + } + + /** + * Return the error message for the occured error + * + * @access public + * @return string Error message + */ + function error() + { + return $this->error; + } + + function get_encoding() + { + return $this->sanitize->output_encoding; + } + + function handle_content_type($mime = 'text/html') + { + if (!headers_sent()) + { + $header = "Content-type: $mime;"; + if ($this->get_encoding()) + { + $header .= ' charset=' . $this->get_encoding(); + } + else + { + $header .= ' charset=UTF-8'; + } + header($header); + } + } + + function get_type() + { + if (!isset($this->data['type'])) + { + $this->data['type'] = SIMPLEPIE_TYPE_ALL; + if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'])) + { + $this->data['type'] &= SIMPLEPIE_TYPE_ATOM_10; + } + elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'])) + { + $this->data['type'] &= SIMPLEPIE_TYPE_ATOM_03; + } + elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'])) + { + if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['channel']) + || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['image']) + || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item']) + || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['textinput'])) + { + $this->data['type'] &= SIMPLEPIE_TYPE_RSS_10; + } + if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['channel']) + || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['image']) + || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item']) + || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['textinput'])) + { + $this->data['type'] &= SIMPLEPIE_TYPE_RSS_090; + } + } + elseif (isset($this->data['child']['']['rss'])) + { + $this->data['type'] &= SIMPLEPIE_TYPE_RSS_ALL; + if (isset($this->data['child']['']['rss'][0]['attribs']['']['version'])) + { + switch (trim($this->data['child']['']['rss'][0]['attribs']['']['version'])) + { + case '0.91': + $this->data['type'] &= SIMPLEPIE_TYPE_RSS_091; + if (isset($this->data['child']['']['rss'][0]['child']['']['skiphours']['hour'][0]['data'])) + { + switch (trim($this->data['child']['']['rss'][0]['child']['']['skiphours']['hour'][0]['data'])) + { + case '0': + $this->data['type'] &= SIMPLEPIE_TYPE_RSS_091_NETSCAPE; + break; + + case '24': + $this->data['type'] &= SIMPLEPIE_TYPE_RSS_091_USERLAND; + break; + } + } + break; + + case '0.92': + $this->data['type'] &= SIMPLEPIE_TYPE_RSS_092; + break; + + case '0.93': + $this->data['type'] &= SIMPLEPIE_TYPE_RSS_093; + break; + + case '0.94': + $this->data['type'] &= SIMPLEPIE_TYPE_RSS_094; + break; + + case '2.0': + $this->data['type'] &= SIMPLEPIE_TYPE_RSS_20; + break; + } + } + } + else + { + $this->data['type'] = SIMPLEPIE_TYPE_NONE; + } + } + return $this->data['type']; + } + + /** + * Returns the URL for the favicon of the feed's website. + * + * @todo Cache atom:icon + * @access public + * @since 1.0 + */ + function get_favicon() + { + if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'icon')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); + } + elseif (($url = $this->get_link()) !== null && preg_match('/^http(s)?:\/\//i', $url)) + { + $favicon = SimplePie_Misc::absolutize_url('/favicon.ico', $url); + + if ($this->cache && $this->favicon_handler) + { + $favicon_filename = call_user_func($this->cache_name_function, $favicon); + $cache = call_user_func(array($this->cache_class, 'create'), $this->cache_location, $favicon_filename, 'spi'); + + if ($cache->load()) + { + return $this->sanitize($this->favicon_handler . $favicon_filename, SIMPLEPIE_CONSTRUCT_IRI); + } + else + { + $file =& new $this->file_class($favicon, $this->timeout / 10, 5, array('X-FORWARDED-FOR' => $_SERVER['REMOTE_ADDR']), $this->useragent, $this->force_fsockopen); + + if ($file->success && ($file->status_code == 200 || ($file->status_code > 206 && $file->status_code < 300)) && strlen($file->body) > 0) + { + $sniffer = new $this->content_type_sniffer_class($file); + if (substr($sniffer->get_type(), 0, 6) === 'image/') + { + if ($cache->save(array('headers' => $file->headers, 'body' => $file->body))) + { + return $this->sanitize($this->favicon_handler . $favicon_filename, SIMPLEPIE_CONSTRUCT_IRI); + } + else + { + trigger_error("$cache->name is not writeable", E_USER_WARNING); + return $this->sanitize($favicon, SIMPLEPIE_CONSTRUCT_IRI); + } + } + } + } + } + else + { + return $this->sanitize($favicon, SIMPLEPIE_CONSTRUCT_IRI); + } + } + return false; + } + + /** + * @todo If we have a perm redirect we should return the new URL + * @todo When we make the above change, let's support as well + * @todo Also, |atom:link|@rel=self + */ + function subscribe_url() + { + if ($this->feed_url !== null) + { + return $this->sanitize($this->feed_url, SIMPLEPIE_CONSTRUCT_IRI); + } + else + { + return null; + } + } + + function subscribe_feed() + { + if ($this->feed_url !== null) + { + return $this->sanitize(SimplePie_Misc::fix_protocol($this->feed_url, 2), SIMPLEPIE_CONSTRUCT_IRI); + } + else + { + return null; + } + } + + function subscribe_outlook() + { + if ($this->feed_url !== null) + { + return 'outlook' . $this->sanitize(SimplePie_Misc::fix_protocol($this->feed_url, 2), SIMPLEPIE_CONSTRUCT_IRI); + } + else + { + return null; + } + } + + function subscribe_podcast() + { + if ($this->feed_url !== null) + { + return $this->sanitize(SimplePie_Misc::fix_protocol($this->feed_url, 3), SIMPLEPIE_CONSTRUCT_IRI); + } + else + { + return null; + } + } + + function subscribe_itunes() + { + if ($this->feed_url !== null) + { + return $this->sanitize(SimplePie_Misc::fix_protocol($this->feed_url, 4), SIMPLEPIE_CONSTRUCT_IRI); + } + else + { + return null; + } + } + + /** + * Creates the subscribe_* methods' return data + * + * @access private + * @param string $feed_url String to prefix to the feed URL + * @param string $site_url String to prefix to the site URL (and + * suffix to the feed URL) + * @return mixed URL if feed exists, false otherwise + */ + function subscribe_service($feed_url, $site_url = null) + { + if ($this->subscribe_url()) + { + $return = $this->sanitize($feed_url, SIMPLEPIE_CONSTRUCT_IRI) . rawurlencode($this->feed_url); + if ($site_url !== null && $this->get_link() !== null) + { + $return .= $this->sanitize($site_url, SIMPLEPIE_CONSTRUCT_IRI) . rawurlencode($this->get_link()); + } + return $return; + } + else + { + return null; + } + } + + function subscribe_aol() + { + return $this->subscribe_service('http://feeds.my.aol.com/add.jsp?url='); + } + + function subscribe_bloglines() + { + return urldecode($this->subscribe_service('http://www.bloglines.com/sub/')); + } + + function subscribe_eskobo() + { + return $this->subscribe_service('http://www.eskobo.com/?AddToMyPage='); + } + + function subscribe_feedfeeds() + { + return $this->subscribe_service('http://www.feedfeeds.com/add?feed='); + } + + function subscribe_feedster() + { + return $this->subscribe_service('http://www.feedster.com/myfeedster.php?action=addrss&confirm=no&rssurl='); + } + + function subscribe_google() + { + return $this->subscribe_service('http://fusion.google.com/add?feedurl='); + } + + function subscribe_gritwire() + { + return $this->subscribe_service('http://my.gritwire.com/feeds/addExternalFeed.aspx?FeedUrl='); + } + + function subscribe_msn() + { + return $this->subscribe_service('http://my.msn.com/addtomymsn.armx?id=rss&ut=', '&ru='); + } + + function subscribe_netvibes() + { + return $this->subscribe_service('http://www.netvibes.com/subscribe.php?url='); + } + + function subscribe_newsburst() + { + return $this->subscribe_service('http://www.newsburst.com/Source/?add='); + } + + function subscribe_newsgator() + { + return $this->subscribe_service('http://www.newsgator.com/ngs/subscriber/subext.aspx?url='); + } + + function subscribe_odeo() + { + return $this->subscribe_service('http://www.odeo.com/listen/subscribe?feed='); + } + + function subscribe_podnova() + { + return $this->subscribe_service('http://www.podnova.com/index_your_podcasts.srf?action=add&url='); + } + + function subscribe_rojo() + { + return $this->subscribe_service('http://www.rojo.com/add-subscription?resource='); + } + + function subscribe_yahoo() + { + return $this->subscribe_service('http://add.my.yahoo.com/rss?url='); + } + + function get_feed_tags($namespace, $tag) + { + $type = $this->get_type(); + if ($type & SIMPLEPIE_TYPE_ATOM_10) + { + if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['child'][$namespace][$tag])) + { + return $this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['child'][$namespace][$tag]; + } + } + if ($type & SIMPLEPIE_TYPE_ATOM_03) + { + if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['child'][$namespace][$tag])) + { + return $this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['child'][$namespace][$tag]; + } + } + if ($type & SIMPLEPIE_TYPE_RSS_RDF) + { + if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][$namespace][$tag])) + { + return $this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][$namespace][$tag]; + } + } + if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION) + { + if (isset($this->data['child']['']['rss'][0]['child'][$namespace][$tag])) + { + return $this->data['child']['']['rss'][0]['child'][$namespace][$tag]; + } + } + return null; + } + + function get_channel_tags($namespace, $tag) + { + $type = $this->get_type(); + if ($type & SIMPLEPIE_TYPE_ATOM_ALL) + { + if ($return = $this->get_feed_tags($namespace, $tag)) + { + return $return; + } + } + if ($type & SIMPLEPIE_TYPE_RSS_10) + { + if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'channel')) + { + if (isset($channel[0]['child'][$namespace][$tag])) + { + return $channel[0]['child'][$namespace][$tag]; + } + } + } + if ($type & SIMPLEPIE_TYPE_RSS_090) + { + if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'channel')) + { + if (isset($channel[0]['child'][$namespace][$tag])) + { + return $channel[0]['child'][$namespace][$tag]; + } + } + } + if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION) + { + if ($channel = $this->get_feed_tags('', 'channel')) + { + if (isset($channel[0]['child'][$namespace][$tag])) + { + return $channel[0]['child'][$namespace][$tag]; + } + } + } + return null; + } + + function get_image_tags($namespace, $tag) + { + $type = $this->get_type(); + if ($type & SIMPLEPIE_TYPE_RSS_10) + { + if ($image = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'image')) + { + if (isset($image[0]['child'][$namespace][$tag])) + { + return $image[0]['child'][$namespace][$tag]; + } + } + } + if ($type & SIMPLEPIE_TYPE_RSS_090) + { + if ($image = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'image')) + { + if (isset($image[0]['child'][$namespace][$tag])) + { + return $image[0]['child'][$namespace][$tag]; + } + } + } + if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION) + { + if ($image = $this->get_channel_tags('', 'image')) + { + if (isset($image[0]['child'][$namespace][$tag])) + { + return $image[0]['child'][$namespace][$tag]; + } + } + } + return null; + } + + function get_base($element = array()) + { + if (!($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION) && !empty($element['xml_base_explicit']) && isset($element['xml_base'])) + { + return $element['xml_base']; + } + elseif ($this->get_link() !== null) + { + return $this->get_link(); + } + else + { + return $this->subscribe_url(); + } + } + + function sanitize($data, $type, $base = '') + { + return $this->sanitize->sanitize($data, $type, $base); + } + + function get_title() + { + if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title')) + { + return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title')) + { + return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags('', 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + return null; + } + } + + function get_category($key = 0) + { + $categories = $this->get_categories(); + if (isset($categories[$key])) + { + return $categories[$key]; + } + else + { + return null; + } + } + + function get_categories() + { + $categories = array(); + + foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category) + { + $term = null; + $scheme = null; + $label = null; + if (isset($category['attribs']['']['term'])) + { + $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($category['attribs']['']['scheme'])) + { + $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($category['attribs']['']['label'])) + { + $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $categories[] =& new $this->category_class($term, $scheme, $label); + } + foreach ((array) $this->get_channel_tags('', 'category') as $category) + { + $categories[] =& new $this->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + } + foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category) + { + $categories[] =& new $this->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + } + foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category) + { + $categories[] =& new $this->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + } + + if (!empty($categories)) + { + return SimplePie_Misc::array_unique($categories); + } + else + { + return null; + } + } + + function get_author($key = 0) + { + $authors = $this->get_authors(); + if (isset($authors[$key])) + { + return $authors[$key]; + } + else + { + return null; + } + } + + function get_authors() + { + $authors = array(); + foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author) + { + $name = null; + $uri = null; + $email = null; + if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) + { + $name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) + { + $uri = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); + } + if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) + { + $email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $uri !== null) + { + $authors[] =& new $this->author_class($name, $uri, $email); + } + } + if ($author = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author')) + { + $name = null; + $url = null; + $email = null; + if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) + { + $name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) + { + $url = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); + } + if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) + { + $email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $url !== null) + { + $authors[] =& new $this->author_class($name, $url, $email); + } + } + foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author) + { + $authors[] =& new $this->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + } + foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author) + { + $authors[] =& new $this->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + } + foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author) + { + $authors[] =& new $this->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + } + + if (!empty($authors)) + { + return SimplePie_Misc::array_unique($authors); + } + else + { + return null; + } + } + + function get_contributor($key = 0) + { + $contributors = $this->get_contributors(); + if (isset($contributors[$key])) + { + return $contributors[$key]; + } + else + { + return null; + } + } + + function get_contributors() + { + $contributors = array(); + foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor) + { + $name = null; + $uri = null; + $email = null; + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) + { + $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) + { + $uri = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) + { + $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $uri !== null) + { + $contributors[] =& new $this->author_class($name, $uri, $email); + } + } + foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor) + { + $name = null; + $url = null; + $email = null; + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) + { + $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) + { + $url = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) + { + $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $url !== null) + { + $contributors[] =& new $this->author_class($name, $url, $email); + } + } + + if (!empty($contributors)) + { + return SimplePie_Misc::array_unique($contributors); + } + else + { + return null; + } + } + + function get_link($key = 0, $rel = 'alternate') + { + $links = $this->get_links($rel); + if (isset($links[$key])) + { + return $links[$key]; + } + else + { + return null; + } + } + + /** + * Added for parity between the parent-level and the item/entry-level. + */ + function get_permalink() + { + return $this->get_link(0); + } + + function get_links($rel = 'alternate') + { + if (!isset($this->data['links'])) + { + $this->data['links'] = array(); + if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link')) + { + foreach ($links as $link) + { + if (isset($link['attribs']['']['href'])) + { + $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; + $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); + } + } + } + if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link')) + { + foreach ($links as $link) + { + if (isset($link['attribs']['']['href'])) + { + $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; + $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); + + } + } + } + if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link')) + { + $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + } + if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link')) + { + $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + } + if ($links = $this->get_channel_tags('', 'link')) + { + $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + } + + $keys = array_keys($this->data['links']); + foreach ($keys as $key) + { + if (SimplePie_Misc::is_isegment_nz_nc($key)) + { + if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key])) + { + $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]); + $this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]; + } + else + { + $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key]; + } + } + elseif (substr($key, 0, 41) == SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY) + { + $this->data['links'][substr($key, 41)] =& $this->data['links'][$key]; + } + $this->data['links'][$key] = array_unique($this->data['links'][$key]); + } + } + + if (isset($this->data['links'][$rel])) + { + return $this->data['links'][$rel]; + } + else + { + return null; + } + } + + function get_description() + { + if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'subtitle')) + { + return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'tagline')) + { + return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags('', 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); + } + else + { + return null; + } + } + + function get_copyright() + { + if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights')) + { + return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'copyright')) + { + return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags('', 'copyright')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + return null; + } + } + + function get_language() + { + if ($return = $this->get_channel_tags('', 'language')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'language')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'language')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['xml_lang'])) + { + return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['xml_lang'])) + { + return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['xml_lang'])) + { + return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif (isset($this->data['headers']['content-language'])) + { + return $this->sanitize($this->data['headers']['content-language'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + return null; + } + } + + function get_latitude() + { + if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat')) + { + return (float) $return[0]['data']; + } + elseif (($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', $return[0]['data'], $match)) + { + return (float) $match[1]; + } + else + { + return null; + } + } + + function get_longitude() + { + if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long')) + { + return (float) $return[0]['data']; + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon')) + { + return (float) $return[0]['data']; + } + elseif (($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', $return[0]['data'], $match)) + { + return (float) $match[2]; + } + else + { + return null; + } + } + + function get_image_title() + { + if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_image_tags('', 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + return null; + } + } + + function get_image_url() + { + if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'image')) + { + return $this->sanitize($return[0]['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'logo')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'icon')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); + } + elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'url')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); + } + elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'url')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); + } + elseif ($return = $this->get_image_tags('', 'url')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); + } + else + { + return null; + } + } + + function get_image_link() + { + if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); + } + elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); + } + elseif ($return = $this->get_image_tags('', 'link')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); + } + else + { + return null; + } + } + + function get_image_width() + { + if ($return = $this->get_image_tags('', 'width')) + { + return round($return[0]['data']); + } + elseif ($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION && $this->get_image_tags('', 'url')) + { + return 88.0; + } + else + { + return null; + } + } + + function get_image_height() + { + if ($return = $this->get_image_tags('', 'height')) + { + return round($return[0]['data']); + } + elseif ($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION && $this->get_image_tags('', 'url')) + { + return 31.0; + } + else + { + return null; + } + } + + function get_item_quantity($max = 0) + { + $qty = count($this->get_items()); + if ($max == 0) + { + return $qty; + } + else + { + return ($qty > $max) ? $max : $qty; + } + } + + function get_item($key = 0) + { + $items = $this->get_items(); + if (isset($items[$key])) + { + return $items[$key]; + } + else + { + return null; + } + } + + function get_items($start = 0, $end = 0) + { + if (!empty($this->multifeed_objects)) + { + return SimplePie::merge_items($this->multifeed_objects, $start, $end, $this->item_limit); + } + elseif (!isset($this->data['items'])) + { + if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'entry')) + { + $keys = array_keys($items); + foreach ($keys as $key) + { + $this->data['items'][] =& new $this->item_class($this, $items[$key]); + } + } + if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'entry')) + { + $keys = array_keys($items); + foreach ($keys as $key) + { + $this->data['items'][] =& new $this->item_class($this, $items[$key]); + } + } + if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'item')) + { + $keys = array_keys($items); + foreach ($keys as $key) + { + $this->data['items'][] =& new $this->item_class($this, $items[$key]); + } + } + if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'item')) + { + $keys = array_keys($items); + foreach ($keys as $key) + { + $this->data['items'][] =& new $this->item_class($this, $items[$key]); + } + } + if ($items = $this->get_channel_tags('', 'item')) + { + $keys = array_keys($items); + foreach ($keys as $key) + { + $this->data['items'][] =& new $this->item_class($this, $items[$key]); + } + } + } + + if (!empty($this->data['items'])) + { + // If we want to order it by date, check if all items have a date, and then sort it + if ($this->order_by_date) + { + if (!isset($this->data['ordered_items'])) + { + $do_sort = true; + foreach ($this->data['items'] as $item) + { + if (!$item->get_date('U')) + { + $do_sort = false; + break; + } + } + $item = null; + $this->data['ordered_items'] = $this->data['items']; + if ($do_sort) + { + usort($this->data['ordered_items'], array(&$this, 'sort_items')); + } + } + $items = $this->data['ordered_items']; + } + else + { + $items = $this->data['items']; + } + + // Slice the data as desired + if ($end == 0) + { + return array_slice($items, $start); + } + else + { + return array_slice($items, $start, $end); + } + } + else + { + return array(); + } + } + + function sort_items($a, $b) + { + return $a->get_date('U') <= $b->get_date('U'); + } + + function merge_items($urls, $start = 0, $end = 0, $limit = 0) + { + if (is_array($urls) && sizeof($urls) > 0) + { + $items = array(); + foreach ($urls as $arg) + { + if (is_a($arg, 'SimplePie')) + { + $items = array_merge($items, $arg->get_items(0, $limit)); + } + else + { + trigger_error('Arguments must be SimplePie objects', E_USER_WARNING); + } + } + + $do_sort = true; + foreach ($items as $item) + { + if (!$item->get_date('U')) + { + $do_sort = false; + break; + } + } + $item = null; + if ($do_sort) + { + usort($items, array('SimplePie', 'sort_items')); + } + + if ($end == 0) + { + return array_slice($items, $start); + } + else + { + return array_slice($items, $start, $end); + } + } + else + { + trigger_error('Cannot merge zero SimplePie objects', E_USER_WARNING); + return array(); + } + } +} + +class SimplePie_Item +{ + var $feed; + var $data = array(); + + function SimplePie_Item($feed, $data) + { + $this->feed = $feed; + $this->data = $data; + } + + function __toString() + { + return md5(serialize($this->data)); + } + + /** + * Remove items that link back to this before destroying this object + */ + function __destruct() + { + unset($this->feed); + } + + function get_item_tags($namespace, $tag) + { + if (isset($this->data['child'][$namespace][$tag])) + { + return $this->data['child'][$namespace][$tag]; + } + else + { + return null; + } + } + + function get_base($element = array()) + { + return $this->feed->get_base($element); + } + + function sanitize($data, $type, $base = '') + { + return $this->feed->sanitize($data, $type, $base); + } + + function get_feed() + { + return $this->feed; + } + + function get_id($hash = false) + { + if (!$hash) + { + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'id')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'id')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_item_tags('', 'guid')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'identifier')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'identifier')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif (($return = $this->get_permalink()) !== null) + { + return $return; + } + elseif (($return = $this->get_title()) !== null) + { + return $return; + } + } + if ($this->get_permalink() !== null || $this->get_title() !== null) + { + return md5($this->get_permalink() . $this->get_title()); + } + else + { + return md5(serialize($this->data)); + } + } + + function get_title() + { + if (!isset($this->data['title'])) + { + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title')) + { + $this->data['title'] = $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title')) + { + $this->data['title'] = $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title')) + { + $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title')) + { + $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags('', 'title')) + { + $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title')) + { + $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title')) + { + $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $this->data['title'] = null; + } + } + return $this->data['title']; + } + + function get_description($description_only = false) + { + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'summary')) + { + return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'summary')) + { + return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags('', 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif (!$description_only) + { + return $this->get_content(true); + } + else + { + return null; + } + } + + function get_content($content_only = false) + { + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'content')) + { + return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_content_construct_type($return[0]['attribs']), $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'content')) + { + return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10_MODULES_CONTENT, 'encoded')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); + } + elseif (!$content_only) + { + return $this->get_description(true); + } + else + { + return null; + } + } + + function get_category($key = 0) + { + $categories = $this->get_categories(); + if (isset($categories[$key])) + { + return $categories[$key]; + } + else + { + return null; + } + } + + function get_categories() + { + $categories = array(); + + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category) + { + $term = null; + $scheme = null; + $label = null; + if (isset($category['attribs']['']['term'])) + { + $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($category['attribs']['']['scheme'])) + { + $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($category['attribs']['']['label'])) + { + $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $categories[] =& new $this->feed->category_class($term, $scheme, $label); + } + foreach ((array) $this->get_item_tags('', 'category') as $category) + { + $categories[] =& new $this->feed->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + } + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category) + { + $categories[] =& new $this->feed->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + } + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category) + { + $categories[] =& new $this->feed->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + } + + if (!empty($categories)) + { + return SimplePie_Misc::array_unique($categories); + } + else + { + return null; + } + } + + function get_author($key = 0) + { + $authors = $this->get_authors(); + if (isset($authors[$key])) + { + return $authors[$key]; + } + else + { + return null; + } + } + + function get_contributor($key = 0) + { + $contributors = $this->get_contributors(); + if (isset($contributors[$key])) + { + return $contributors[$key]; + } + else + { + return null; + } + } + + function get_contributors() + { + $contributors = array(); + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor) + { + $name = null; + $uri = null; + $email = null; + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) + { + $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) + { + $uri = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) + { + $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $uri !== null) + { + $contributors[] =& new $this->feed->author_class($name, $uri, $email); + } + } + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor) + { + $name = null; + $url = null; + $email = null; + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) + { + $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) + { + $url = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) + { + $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $url !== null) + { + $contributors[] =& new $this->feed->author_class($name, $url, $email); + } + } + + if (!empty($contributors)) + { + return SimplePie_Misc::array_unique($contributors); + } + else + { + return null; + } + } + + /** + * @todo Atom inheritance (item author, source author, feed author) + */ + function get_authors() + { + $authors = array(); + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author) + { + $name = null; + $uri = null; + $email = null; + if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) + { + $name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) + { + $uri = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); + } + if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) + { + $email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $uri !== null) + { + $authors[] =& new $this->feed->author_class($name, $uri, $email); + } + } + if ($author = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author')) + { + $name = null; + $url = null; + $email = null; + if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) + { + $name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) + { + $url = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); + } + if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) + { + $email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $url !== null) + { + $authors[] =& new $this->feed->author_class($name, $url, $email); + } + } + if ($author = $this->get_item_tags('', 'author')) + { + $authors[] =& new $this->feed->author_class(null, null, $this->sanitize($author[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); + } + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author) + { + $authors[] =& new $this->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + } + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author) + { + $authors[] =& new $this->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + } + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author) + { + $authors[] =& new $this->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + } + + if (!empty($authors)) + { + return SimplePie_Misc::array_unique($authors); + } + elseif (($source = $this->get_source()) && ($authors = $source->get_authors())) + { + return $authors; + } + elseif ($authors = $this->feed->get_authors()) + { + return $authors; + } + else + { + return null; + } + } + + function get_copyright() + { + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights')) + { + return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + return null; + } + } + + function get_date($date_format = 'j F Y, g:i a') + { + if (!isset($this->data['date'])) + { + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'published')) + { + $this->data['date']['raw'] = $return[0]['data']; + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'updated')) + { + $this->data['date']['raw'] = $return[0]['data']; + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'issued')) + { + $this->data['date']['raw'] = $return[0]['data']; + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'created')) + { + $this->data['date']['raw'] = $return[0]['data']; + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'modified')) + { + $this->data['date']['raw'] = $return[0]['data']; + } + elseif ($return = $this->get_item_tags('', 'pubDate')) + { + $this->data['date']['raw'] = $return[0]['data']; + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'date')) + { + $this->data['date']['raw'] = $return[0]['data']; + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'date')) + { + $this->data['date']['raw'] = $return[0]['data']; + } + + if (!empty($this->data['date']['raw'])) + { + $parser = SimplePie_Parse_Date::get(); + $this->data['date']['parsed'] = $parser->parse($this->data['date']['raw']); + } + else + { + $this->data['date'] = null; + } + } + if ($this->data['date']) + { + $date_format = (string) $date_format; + switch ($date_format) + { + case '': + return $this->sanitize($this->data['date']['raw'], SIMPLEPIE_CONSTRUCT_TEXT); + + case 'U': + return $this->data['date']['parsed']; + + default: + return date($date_format, $this->data['date']['parsed']); + } + } + else + { + return null; + } + } + + function get_local_date($date_format = '%c') + { + if (!$date_format) + { + return $this->sanitize($this->get_date(''), SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif (($date = $this->get_date('U')) !== null) + { + return strftime($date_format, $date); + } + else + { + return null; + } + } + + function get_permalink() + { + $link = $this->get_link(); + $enclosure = $this->get_enclosure(0); + if ($link !== null) + { + return $link; + } + elseif ($enclosure !== null) + { + return $enclosure->get_link(); + } + else + { + return null; + } + } + + function get_link($key = 0, $rel = 'alternate') + { + $links = $this->get_links($rel); + if ($links[$key] !== null) + { + return $links[$key]; + } + else + { + return null; + } + } + + function get_links($rel = 'alternate') + { + if (!isset($this->data['links'])) + { + $this->data['links'] = array(); + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link') as $link) + { + if (isset($link['attribs']['']['href'])) + { + $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; + $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); + + } + } + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link') as $link) + { + if (isset($link['attribs']['']['href'])) + { + $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; + $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); + } + } + if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link')) + { + $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + } + if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link')) + { + $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + } + if ($links = $this->get_item_tags('', 'link')) + { + $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + } + if ($links = $this->get_item_tags('', 'guid')) + { + if (!isset($links[0]['attribs']['']['isPermaLink']) || strtolower(trim($links[0]['attribs']['']['isPermaLink'])) == 'true') + { + $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + } + } + + $keys = array_keys($this->data['links']); + foreach ($keys as $key) + { + if (SimplePie_Misc::is_isegment_nz_nc($key)) + { + if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key])) + { + $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]); + $this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]; + } + else + { + $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key]; + } + } + elseif (substr($key, 0, 41) == SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY) + { + $this->data['links'][substr($key, 41)] =& $this->data['links'][$key]; + } + $this->data['links'][$key] = array_unique($this->data['links'][$key]); + } + } + if (isset($this->data['links'][$rel])) + { + return $this->data['links'][$rel]; + } + else + { + return null; + } + } + + /** + * @todo Add ability to prefer one type of content over another (in a media group). + */ + function get_enclosure($key = 0, $prefer = null) + { + $enclosures = $this->get_enclosures(); + if (isset($enclosures[$key])) + { + return $enclosures[$key]; + } + else + { + return null; + } + } + + /** + * Grabs all available enclosures (podcasts, etc.) + * + * Supports the RSS tag, as well as Media RSS and iTunes RSS. + * + * At this point, we're pretty much assuming that all enclosures for an item are the same content. Anything else is too complicated to properly support. + * + * @todo Add support for end-user defined sorting of enclosures by type/handler (so we can prefer the faster-loading FLV over MP4). + * @todo If an element exists at a level, but it's value is empty, we should fall back to the value from the parent (if it exists). + */ + function get_enclosures() + { + if (!isset($this->data['enclosures'])) + { + $this->data['enclosures'] = array(); + + // Elements + $captions_parent = null; + $categories_parent = null; + $copyrights_parent = null; + $credits_parent = null; + $description_parent = null; + $duration_parent = null; + $hashes_parent = null; + $keywords_parent = null; + $player_parent = null; + $ratings_parent = null; + $restrictions_parent = null; + $thumbnails_parent = null; + $title_parent = null; + + // Let's do the channel and item-level ones first, and just re-use them if we need to. + $parent = $this->get_feed(); + + // CAPTIONS + if ($captions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'text')) + { + foreach ($captions as $caption) + { + $caption_type = null; + $caption_lang = null; + $caption_startTime = null; + $caption_endTime = null; + $caption_text = null; + if (isset($caption['attribs']['']['type'])) + { + $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['lang'])) + { + $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['start'])) + { + $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['end'])) + { + $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['data'])) + { + $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $captions_parent[] =& new $this->feed->caption_class($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text); + } + } + elseif ($captions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'text')) + { + foreach ($captions as $caption) + { + $caption_type = null; + $caption_lang = null; + $caption_startTime = null; + $caption_endTime = null; + $caption_text = null; + if (isset($caption['attribs']['']['type'])) + { + $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['lang'])) + { + $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['start'])) + { + $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['end'])) + { + $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['data'])) + { + $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $captions_parent[] =& new $this->feed->caption_class($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text); + } + } + if (is_array($captions_parent)) + { + $captions_parent = array_values(SimplePie_Misc::array_unique($captions_parent)); + } + + // CATEGORIES + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'category') as $category) + { + $term = null; + $scheme = null; + $label = null; + if (isset($category['data'])) + { + $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($category['attribs']['']['scheme'])) + { + $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $scheme = 'http://search.yahoo.com/mrss/category_schema'; + } + if (isset($category['attribs']['']['label'])) + { + $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $categories_parent[] =& new $this->feed->category_class($term, $scheme, $label); + } + foreach ((array) $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'category') as $category) + { + $term = null; + $scheme = null; + $label = null; + if (isset($category['data'])) + { + $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($category['attribs']['']['scheme'])) + { + $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $scheme = 'http://search.yahoo.com/mrss/category_schema'; + } + if (isset($category['attribs']['']['label'])) + { + $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $categories_parent[] =& new $this->feed->category_class($term, $scheme, $label); + } + foreach ((array) $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'category') as $category) + { + $term = null; + $scheme = 'http://www.itunes.com/dtds/podcast-1.0.dtd'; + $label = null; + if (isset($category['attribs']['']['text'])) + { + $label = $this->sanitize($category['attribs']['']['text'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $categories_parent[] =& new $this->feed->category_class($term, $scheme, $label); + + if (isset($category['child'][SIMPLEPIE_NAMESPACE_ITUNES]['category'])) + { + foreach ((array) $category['child'][SIMPLEPIE_NAMESPACE_ITUNES]['category'] as $subcategory) + { + if (isset($subcategory['attribs']['']['text'])) + { + $label = $this->sanitize($subcategory['attribs']['']['text'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $categories_parent[] =& new $this->feed->category_class($term, $scheme, $label); + } + } + } + if (is_array($categories_parent)) + { + $categories_parent = array_values(SimplePie_Misc::array_unique($categories_parent)); + } + + // COPYRIGHT + if ($copyright = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'copyright')) + { + $copyright_url = null; + $copyright_label = null; + if (isset($copyright[0]['attribs']['']['url'])) + { + $copyright_url = $this->sanitize($copyright[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($copyright[0]['data'])) + { + $copyright_label = $this->sanitize($copyright[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $copyrights_parent =& new $this->feed->copyright_class($copyright_url, $copyright_label); + } + elseif ($copyright = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'copyright')) + { + $copyright_url = null; + $copyright_label = null; + if (isset($copyright[0]['attribs']['']['url'])) + { + $copyright_url = $this->sanitize($copyright[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($copyright[0]['data'])) + { + $copyright_label = $this->sanitize($copyright[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $copyrights_parent =& new $this->feed->copyright_class($copyright_url, $copyright_label); + } + + // CREDITS + if ($credits = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'credit')) + { + foreach ($credits as $credit) + { + $credit_role = null; + $credit_scheme = null; + $credit_name = null; + if (isset($credit['attribs']['']['role'])) + { + $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($credit['attribs']['']['scheme'])) + { + $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $credit_scheme = 'urn:ebu'; + } + if (isset($credit['data'])) + { + $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $credits_parent[] =& new $this->feed->credit_class($credit_role, $credit_scheme, $credit_name); + } + } + elseif ($credits = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'credit')) + { + foreach ($credits as $credit) + { + $credit_role = null; + $credit_scheme = null; + $credit_name = null; + if (isset($credit['attribs']['']['role'])) + { + $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($credit['attribs']['']['scheme'])) + { + $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $credit_scheme = 'urn:ebu'; + } + if (isset($credit['data'])) + { + $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $credits_parent[] =& new $this->feed->credit_class($credit_role, $credit_scheme, $credit_name); + } + } + if (is_array($credits_parent)) + { + $credits_parent = array_values(SimplePie_Misc::array_unique($credits_parent)); + } + + // DESCRIPTION + if ($description_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'description')) + { + if (isset($description_parent[0]['data'])) + { + $description_parent = $this->sanitize($description_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + } + elseif ($description_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'description')) + { + if (isset($description_parent[0]['data'])) + { + $description_parent = $this->sanitize($description_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + } + + // DURATION + if ($duration_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'duration')) + { + $seconds = null; + $minutes = null; + $hours = null; + if (isset($duration_parent[0]['data'])) + { + $temp = explode(':', $this->sanitize($duration_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); + if (sizeof($temp) > 0) + { + (int) $seconds = array_pop($temp); + } + if (sizeof($temp) > 0) + { + (int) $minutes = array_pop($temp); + $seconds += $minutes * 60; + } + if (sizeof($temp) > 0) + { + (int) $hours = array_pop($temp); + $seconds += $hours * 3600; + } + unset($temp); + $duration_parent = $seconds; + } + } + + // HASHES + if ($hashes_iterator = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'hash')) + { + foreach ($hashes_iterator as $hash) + { + $value = null; + $algo = null; + if (isset($hash['data'])) + { + $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($hash['attribs']['']['algo'])) + { + $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $algo = 'md5'; + } + $hashes_parent[] = $algo.':'.$value; + } + } + elseif ($hashes_iterator = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'hash')) + { + foreach ($hashes_iterator as $hash) + { + $value = null; + $algo = null; + if (isset($hash['data'])) + { + $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($hash['attribs']['']['algo'])) + { + $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $algo = 'md5'; + } + $hashes_parent[] = $algo.':'.$value; + } + } + if (is_array($hashes_parent)) + { + $hashes_parent = array_values(SimplePie_Misc::array_unique($hashes_parent)); + } + + // KEYWORDS + if ($keywords = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'keywords')) + { + if (isset($keywords[0]['data'])) + { + $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); + foreach ($temp as $word) + { + $keywords_parent[] = trim($word); + } + } + unset($temp); + } + elseif ($keywords = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'keywords')) + { + if (isset($keywords[0]['data'])) + { + $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); + foreach ($temp as $word) + { + $keywords_parent[] = trim($word); + } + } + unset($temp); + } + elseif ($keywords = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'keywords')) + { + if (isset($keywords[0]['data'])) + { + $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); + foreach ($temp as $word) + { + $keywords_parent[] = trim($word); + } + } + unset($temp); + } + elseif ($keywords = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'keywords')) + { + if (isset($keywords[0]['data'])) + { + $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); + foreach ($temp as $word) + { + $keywords_parent[] = trim($word); + } + } + unset($temp); + } + if (is_array($keywords_parent)) + { + $keywords_parent = array_values(SimplePie_Misc::array_unique($keywords_parent)); + } + + // PLAYER + if ($player_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'player')) + { + if (isset($player_parent[0]['attribs']['']['url'])) + { + $player_parent = $this->sanitize($player_parent[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + } + elseif ($player_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'player')) + { + if (isset($player_parent[0]['attribs']['']['url'])) + { + $player_parent = $this->sanitize($player_parent[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + } + + // RATINGS + if ($ratings = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'rating')) + { + foreach ($ratings as $rating) + { + $rating_scheme = null; + $rating_value = null; + if (isset($rating['attribs']['']['scheme'])) + { + $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $rating_scheme = 'urn:simple'; + } + if (isset($rating['data'])) + { + $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $ratings_parent[] =& new $this->feed->rating_class($rating_scheme, $rating_value); + } + } + elseif ($ratings = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'explicit')) + { + foreach ($ratings as $rating) + { + $rating_scheme = 'urn:itunes'; + $rating_value = null; + if (isset($rating['data'])) + { + $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $ratings_parent[] =& new $this->feed->rating_class($rating_scheme, $rating_value); + } + } + elseif ($ratings = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'rating')) + { + foreach ($ratings as $rating) + { + $rating_scheme = null; + $rating_value = null; + if (isset($rating['attribs']['']['scheme'])) + { + $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $rating_scheme = 'urn:simple'; + } + if (isset($rating['data'])) + { + $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $ratings_parent[] =& new $this->feed->rating_class($rating_scheme, $rating_value); + } + } + elseif ($ratings = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'explicit')) + { + foreach ($ratings as $rating) + { + $rating_scheme = 'urn:itunes'; + $rating_value = null; + if (isset($rating['data'])) + { + $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $ratings_parent[] =& new $this->feed->rating_class($rating_scheme, $rating_value); + } + } + if (is_array($ratings_parent)) + { + $ratings_parent = array_values(SimplePie_Misc::array_unique($ratings_parent)); + } + + // RESTRICTIONS + if ($restrictions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'restriction')) + { + foreach ($restrictions as $restriction) + { + $restriction_relationship = null; + $restriction_type = null; + $restriction_value = null; + if (isset($restriction['attribs']['']['relationship'])) + { + $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['attribs']['']['type'])) + { + $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['data'])) + { + $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $restrictions_parent[] =& new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value); + } + } + elseif ($restrictions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'block')) + { + foreach ($restrictions as $restriction) + { + $restriction_relationship = 'allow'; + $restriction_type = null; + $restriction_value = 'itunes'; + if (isset($restriction['data']) && strtolower($restriction['data']) == 'yes') + { + $restriction_relationship = 'deny'; + } + $restrictions_parent[] =& new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value); + } + } + elseif ($restrictions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'restriction')) + { + foreach ($restrictions as $restriction) + { + $restriction_relationship = null; + $restriction_type = null; + $restriction_value = null; + if (isset($restriction['attribs']['']['relationship'])) + { + $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['attribs']['']['type'])) + { + $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['data'])) + { + $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $restrictions_parent[] =& new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value); + } + } + elseif ($restrictions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'block')) + { + foreach ($restrictions as $restriction) + { + $restriction_relationship = 'allow'; + $restriction_type = null; + $restriction_value = 'itunes'; + if (isset($restriction['data']) && strtolower($restriction['data']) == 'yes') + { + $restriction_relationship = 'deny'; + } + $restrictions_parent[] =& new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value); + } + } + if (is_array($restrictions_parent)) + { + $restrictions_parent = array_values(SimplePie_Misc::array_unique($restrictions_parent)); + } + + // THUMBNAILS + if ($thumbnails = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'thumbnail')) + { + foreach ($thumbnails as $thumbnail) + { + if (isset($thumbnail['attribs']['']['url'])) + { + $thumbnails_parent[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + } + } + elseif ($thumbnails = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'thumbnail')) + { + foreach ($thumbnails as $thumbnail) + { + if (isset($thumbnail['attribs']['']['url'])) + { + $thumbnails_parent[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + } + } + + // TITLES + if ($title_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'title')) + { + if (isset($title_parent[0]['data'])) + { + $title_parent = $this->sanitize($title_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + } + elseif ($title_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'title')) + { + if (isset($title_parent[0]['data'])) + { + $title_parent = $this->sanitize($title_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + } + + // Clear the memory + unset($parent); + + // Attributes + $bitrate = null; + $channels = null; + $duration = null; + $expression = null; + $framerate = null; + $height = null; + $javascript = null; + $lang = null; + $length = null; + $medium = null; + $samplingrate = null; + $type = null; + $url = null; + $width = null; + + // Elements + $captions = null; + $categories = null; + $copyrights = null; + $credits = null; + $description = null; + $hashes = null; + $keywords = null; + $player = null; + $ratings = null; + $restrictions = null; + $thumbnails = null; + $title = null; + + // If we have media:group tags, loop through them. + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'group') as $group) + { + // If we have media:content tags, loop through them. + foreach ((array) $group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'] as $content) + { + if (isset($content['attribs']['']['url'])) + { + // Attributes + $bitrate = null; + $channels = null; + $duration = null; + $expression = null; + $framerate = null; + $height = null; + $javascript = null; + $lang = null; + $length = null; + $medium = null; + $samplingrate = null; + $type = null; + $url = null; + $width = null; + + // Elements + $captions = null; + $categories = null; + $copyrights = null; + $credits = null; + $description = null; + $hashes = null; + $keywords = null; + $player = null; + $ratings = null; + $restrictions = null; + $thumbnails = null; + $title = null; + + // Start checking the attributes of media:content + if (isset($content['attribs']['']['bitrate'])) + { + $bitrate = $this->sanitize($content['attribs']['']['bitrate'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['channels'])) + { + $channels = $this->sanitize($content['attribs']['']['channels'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['duration'])) + { + $duration = $this->sanitize($content['attribs']['']['duration'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $duration = $duration_parent; + } + if (isset($content['attribs']['']['expression'])) + { + $expression = $this->sanitize($content['attribs']['']['expression'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['framerate'])) + { + $framerate = $this->sanitize($content['attribs']['']['framerate'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['height'])) + { + $height = $this->sanitize($content['attribs']['']['height'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['lang'])) + { + $lang = $this->sanitize($content['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['fileSize'])) + { + $length = ceil($content['attribs']['']['fileSize']); + } + if (isset($content['attribs']['']['medium'])) + { + $medium = $this->sanitize($content['attribs']['']['medium'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['samplingrate'])) + { + $samplingrate = $this->sanitize($content['attribs']['']['samplingrate'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['type'])) + { + $type = $this->sanitize($content['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['width'])) + { + $width = $this->sanitize($content['attribs']['']['width'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $url = $this->sanitize($content['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + + // Checking the other optional media: elements. Priority: media:content, media:group, item, channel + + // CAPTIONS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption) + { + $caption_type = null; + $caption_lang = null; + $caption_startTime = null; + $caption_endTime = null; + $caption_text = null; + if (isset($caption['attribs']['']['type'])) + { + $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['lang'])) + { + $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['start'])) + { + $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['end'])) + { + $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['data'])) + { + $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $captions[] =& new $this->feed->caption_class($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text); + } + if (is_array($captions)) + { + $captions = array_values(SimplePie_Misc::array_unique($captions)); + } + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'])) + { + foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption) + { + $caption_type = null; + $caption_lang = null; + $caption_startTime = null; + $caption_endTime = null; + $caption_text = null; + if (isset($caption['attribs']['']['type'])) + { + $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['lang'])) + { + $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['start'])) + { + $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['end'])) + { + $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['data'])) + { + $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $captions[] =& new $this->feed->caption_class($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text); + } + if (is_array($captions)) + { + $captions = array_values(SimplePie_Misc::array_unique($captions)); + } + } + else + { + $captions = $captions_parent; + } + + // CATEGORIES + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'])) + { + foreach ((array) $content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category) + { + $term = null; + $scheme = null; + $label = null; + if (isset($category['data'])) + { + $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($category['attribs']['']['scheme'])) + { + $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $scheme = 'http://search.yahoo.com/mrss/category_schema'; + } + if (isset($category['attribs']['']['label'])) + { + $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $categories[] =& new $this->feed->category_class($term, $scheme, $label); + } + } + if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'])) + { + foreach ((array) $group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category) + { + $term = null; + $scheme = null; + $label = null; + if (isset($category['data'])) + { + $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($category['attribs']['']['scheme'])) + { + $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $scheme = 'http://search.yahoo.com/mrss/category_schema'; + } + if (isset($category['attribs']['']['label'])) + { + $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $categories[] =& new $this->feed->category_class($term, $scheme, $label); + } + } + if (is_array($categories) && is_array($categories_parent)) + { + $categories = array_values(SimplePie_Misc::array_unique(array_merge($categories, $categories_parent))); + } + elseif (is_array($categories)) + { + $categories = array_values(SimplePie_Misc::array_unique($categories)); + } + elseif (is_array($categories_parent)) + { + $categories = array_values(SimplePie_Misc::array_unique($categories_parent)); + } + + // COPYRIGHTS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'])) + { + $copyright_url = null; + $copyright_label = null; + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'])) + { + $copyright_url = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'])) + { + $copyright_label = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $copyrights =& new $this->feed->copyright_class($copyright_url, $copyright_label); + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'])) + { + $copyright_url = null; + $copyright_label = null; + if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'])) + { + $copyright_url = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'])) + { + $copyright_label = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $copyrights =& new $this->feed->copyright_class($copyright_url, $copyright_label); + } + else + { + $copyrights = $copyrights_parent; + } + + // CREDITS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit) + { + $credit_role = null; + $credit_scheme = null; + $credit_name = null; + if (isset($credit['attribs']['']['role'])) + { + $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($credit['attribs']['']['scheme'])) + { + $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $credit_scheme = 'urn:ebu'; + } + if (isset($credit['data'])) + { + $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $credits[] =& new $this->feed->credit_class($credit_role, $credit_scheme, $credit_name); + } + if (is_array($credits)) + { + $credits = array_values(SimplePie_Misc::array_unique($credits)); + } + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'])) + { + foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit) + { + $credit_role = null; + $credit_scheme = null; + $credit_name = null; + if (isset($credit['attribs']['']['role'])) + { + $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($credit['attribs']['']['scheme'])) + { + $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $credit_scheme = 'urn:ebu'; + } + if (isset($credit['data'])) + { + $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $credits[] =& new $this->feed->credit_class($credit_role, $credit_scheme, $credit_name); + } + if (is_array($credits)) + { + $credits = array_values(SimplePie_Misc::array_unique($credits)); + } + } + else + { + $credits = $credits_parent; + } + + // DESCRIPTION + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'])) + { + $description = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'])) + { + $description = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $description = $description_parent; + } + + // HASHES + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash) + { + $value = null; + $algo = null; + if (isset($hash['data'])) + { + $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($hash['attribs']['']['algo'])) + { + $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $algo = 'md5'; + } + $hashes[] = $algo.':'.$value; + } + if (is_array($hashes)) + { + $hashes = array_values(SimplePie_Misc::array_unique($hashes)); + } + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'])) + { + foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash) + { + $value = null; + $algo = null; + if (isset($hash['data'])) + { + $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($hash['attribs']['']['algo'])) + { + $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $algo = 'md5'; + } + $hashes[] = $algo.':'.$value; + } + if (is_array($hashes)) + { + $hashes = array_values(SimplePie_Misc::array_unique($hashes)); + } + } + else + { + $hashes = $hashes_parent; + } + + // KEYWORDS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'])) + { + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'])) + { + $temp = explode(',', $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); + foreach ($temp as $word) + { + $keywords[] = trim($word); + } + unset($temp); + } + if (is_array($keywords)) + { + $keywords = array_values(SimplePie_Misc::array_unique($keywords)); + } + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'])) + { + if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'])) + { + $temp = explode(',', $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); + foreach ($temp as $word) + { + $keywords[] = trim($word); + } + unset($temp); + } + if (is_array($keywords)) + { + $keywords = array_values(SimplePie_Misc::array_unique($keywords)); + } + } + else + { + $keywords = $keywords_parent; + } + + // PLAYER + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'])) + { + $player = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'])) + { + $player = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + else + { + $player = $player_parent; + } + + // RATINGS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating) + { + $rating_scheme = null; + $rating_value = null; + if (isset($rating['attribs']['']['scheme'])) + { + $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $rating_scheme = 'urn:simple'; + } + if (isset($rating['data'])) + { + $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $ratings[] =& new $this->feed->rating_class($rating_scheme, $rating_value); + } + if (is_array($ratings)) + { + $ratings = array_values(SimplePie_Misc::array_unique($ratings)); + } + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'])) + { + foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating) + { + $rating_scheme = null; + $rating_value = null; + if (isset($rating['attribs']['']['scheme'])) + { + $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $rating_scheme = 'urn:simple'; + } + if (isset($rating['data'])) + { + $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $ratings[] =& new $this->feed->rating_class($rating_scheme, $rating_value); + } + if (is_array($ratings)) + { + $ratings = array_values(SimplePie_Misc::array_unique($ratings)); + } + } + else + { + $ratings = $ratings_parent; + } + + // RESTRICTIONS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction) + { + $restriction_relationship = null; + $restriction_type = null; + $restriction_value = null; + if (isset($restriction['attribs']['']['relationship'])) + { + $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['attribs']['']['type'])) + { + $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['data'])) + { + $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $restrictions[] =& new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value); + } + if (is_array($restrictions)) + { + $restrictions = array_values(SimplePie_Misc::array_unique($restrictions)); + } + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'])) + { + foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction) + { + $restriction_relationship = null; + $restriction_type = null; + $restriction_value = null; + if (isset($restriction['attribs']['']['relationship'])) + { + $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['attribs']['']['type'])) + { + $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['data'])) + { + $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $restrictions[] =& new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value); + } + if (is_array($restrictions)) + { + $restrictions = array_values(SimplePie_Misc::array_unique($restrictions)); + } + } + else + { + $restrictions = $restrictions_parent; + } + + // THUMBNAILS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail) + { + $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + if (is_array($thumbnails)) + { + $thumbnails = array_values(SimplePie_Misc::array_unique($thumbnails)); + } + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'])) + { + foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail) + { + $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + if (is_array($thumbnails)) + { + $thumbnails = array_values(SimplePie_Misc::array_unique($thumbnails)); + } + } + else + { + $thumbnails = $thumbnails_parent; + } + + // TITLES + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'])) + { + $title = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'])) + { + $title = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $title = $title_parent; + } + + $this->data['enclosures'][] =& new $this->feed->enclosure_class($url, $type, $length, $this->feed->javascript, $bitrate, $captions, $categories, $channels, $copyrights, $credits, $description, $duration, $expression, $framerate, $hashes, $height, $keywords, $lang, $medium, $player, $ratings, $restrictions, $samplingrate, $thumbnails, $title, $width); + } + } + } + + // If we have standalone media:content tags, loop through them. + if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'])) + { + foreach ((array) $this->data['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'] as $content) + { + if (isset($content['attribs']['']['url'])) + { + // Attributes + $bitrate = null; + $channels = null; + $duration = null; + $expression = null; + $framerate = null; + $height = null; + $javascript = null; + $lang = null; + $length = null; + $medium = null; + $samplingrate = null; + $type = null; + $url = null; + $width = null; + + // Elements + $captions = null; + $categories = null; + $copyrights = null; + $credits = null; + $description = null; + $hashes = null; + $keywords = null; + $player = null; + $ratings = null; + $restrictions = null; + $thumbnails = null; + $title = null; + + // Start checking the attributes of media:content + if (isset($content['attribs']['']['bitrate'])) + { + $bitrate = $this->sanitize($content['attribs']['']['bitrate'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['channels'])) + { + $channels = $this->sanitize($content['attribs']['']['channels'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['duration'])) + { + $duration = $this->sanitize($content['attribs']['']['duration'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $duration = $duration_parent; + } + if (isset($content['attribs']['']['expression'])) + { + $expression = $this->sanitize($content['attribs']['']['expression'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['framerate'])) + { + $framerate = $this->sanitize($content['attribs']['']['framerate'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['height'])) + { + $height = $this->sanitize($content['attribs']['']['height'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['lang'])) + { + $lang = $this->sanitize($content['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['fileSize'])) + { + $length = ceil($content['attribs']['']['fileSize']); + } + if (isset($content['attribs']['']['medium'])) + { + $medium = $this->sanitize($content['attribs']['']['medium'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['samplingrate'])) + { + $samplingrate = $this->sanitize($content['attribs']['']['samplingrate'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['type'])) + { + $type = $this->sanitize($content['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['width'])) + { + $width = $this->sanitize($content['attribs']['']['width'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $url = $this->sanitize($content['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + + // Checking the other optional media: elements. Priority: media:content, media:group, item, channel + + // CAPTIONS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption) + { + $caption_type = null; + $caption_lang = null; + $caption_startTime = null; + $caption_endTime = null; + $caption_text = null; + if (isset($caption['attribs']['']['type'])) + { + $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['lang'])) + { + $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['start'])) + { + $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['end'])) + { + $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['data'])) + { + $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $captions[] =& new $this->feed->caption_class($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text); + } + if (is_array($captions)) + { + $captions = array_values(SimplePie_Misc::array_unique($captions)); + } + } + else + { + $captions = $captions_parent; + } + + // CATEGORIES + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'])) + { + foreach ((array) $content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category) + { + $term = null; + $scheme = null; + $label = null; + if (isset($category['data'])) + { + $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($category['attribs']['']['scheme'])) + { + $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $scheme = 'http://search.yahoo.com/mrss/category_schema'; + } + if (isset($category['attribs']['']['label'])) + { + $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $categories[] =& new $this->feed->category_class($term, $scheme, $label); + } + } + if (is_array($categories) && is_array($categories_parent)) + { + $categories = array_values(SimplePie_Misc::array_unique(array_merge($categories, $categories_parent))); + } + elseif (is_array($categories)) + { + $categories = array_values(SimplePie_Misc::array_unique($categories)); + } + elseif (is_array($categories_parent)) + { + $categories = array_values(SimplePie_Misc::array_unique($categories_parent)); + } + else + { + $categories = null; + } + + // COPYRIGHTS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'])) + { + $copyright_url = null; + $copyright_label = null; + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'])) + { + $copyright_url = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'])) + { + $copyright_label = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $copyrights =& new $this->feed->copyright_class($copyright_url, $copyright_label); + } + else + { + $copyrights = $copyrights_parent; + } + + // CREDITS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit) + { + $credit_role = null; + $credit_scheme = null; + $credit_name = null; + if (isset($credit['attribs']['']['role'])) + { + $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($credit['attribs']['']['scheme'])) + { + $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $credit_scheme = 'urn:ebu'; + } + if (isset($credit['data'])) + { + $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $credits[] =& new $this->feed->credit_class($credit_role, $credit_scheme, $credit_name); + } + if (is_array($credits)) + { + $credits = array_values(SimplePie_Misc::array_unique($credits)); + } + } + else + { + $credits = $credits_parent; + } + + // DESCRIPTION + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'])) + { + $description = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $description = $description_parent; + } + + // HASHES + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash) + { + $value = null; + $algo = null; + if (isset($hash['data'])) + { + $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($hash['attribs']['']['algo'])) + { + $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $algo = 'md5'; + } + $hashes[] = $algo.':'.$value; + } + if (is_array($hashes)) + { + $hashes = array_values(SimplePie_Misc::array_unique($hashes)); + } + } + else + { + $hashes = $hashes_parent; + } + + // KEYWORDS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'])) + { + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'])) + { + $temp = explode(',', $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); + foreach ($temp as $word) + { + $keywords[] = trim($word); + } + unset($temp); + } + if (is_array($keywords)) + { + $keywords = array_values(SimplePie_Misc::array_unique($keywords)); + } + } + else + { + $keywords = $keywords_parent; + } + + // PLAYER + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'])) + { + $player = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + else + { + $player = $player_parent; + } + + // RATINGS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating) + { + $rating_scheme = null; + $rating_value = null; + if (isset($rating['attribs']['']['scheme'])) + { + $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $rating_scheme = 'urn:simple'; + } + if (isset($rating['data'])) + { + $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $ratings[] =& new $this->feed->rating_class($rating_scheme, $rating_value); + } + if (is_array($ratings)) + { + $ratings = array_values(SimplePie_Misc::array_unique($ratings)); + } + } + else + { + $ratings = $ratings_parent; + } + + // RESTRICTIONS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction) + { + $restriction_relationship = null; + $restriction_type = null; + $restriction_value = null; + if (isset($restriction['attribs']['']['relationship'])) + { + $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['attribs']['']['type'])) + { + $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['data'])) + { + $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $restrictions[] =& new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value); + } + if (is_array($restrictions)) + { + $restrictions = array_values(SimplePie_Misc::array_unique($restrictions)); + } + } + else + { + $restrictions = $restrictions_parent; + } + + // THUMBNAILS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail) + { + $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + if (is_array($thumbnails)) + { + $thumbnails = array_values(SimplePie_Misc::array_unique($thumbnails)); + } + } + else + { + $thumbnails = $thumbnails_parent; + } + + // TITLES + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'])) + { + $title = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $title = $title_parent; + } + + $this->data['enclosures'][] =& new $this->feed->enclosure_class($url, $type, $length, $this->feed->javascript, $bitrate, $captions, $categories, $channels, $copyrights, $credits, $description, $duration, $expression, $framerate, $hashes, $height, $keywords, $lang, $medium, $player, $ratings, $restrictions, $samplingrate, $thumbnails, $title, $width); + } + } + } + + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link') as $link) + { + if (isset($link['attribs']['']['href']) && !empty($link['attribs']['']['rel']) && $link['attribs']['']['rel'] == 'enclosure') + { + // Attributes + $bitrate = null; + $channels = null; + $duration = null; + $expression = null; + $framerate = null; + $height = null; + $javascript = null; + $lang = null; + $length = null; + $medium = null; + $samplingrate = null; + $type = null; + $url = null; + $width = null; + + $url = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); + if (isset($link['attribs']['']['type'])) + { + $type = $this->sanitize($link['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($link['attribs']['']['length'])) + { + $length = ceil($link['attribs']['']['length']); + } + + // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor + $this->data['enclosures'][] =& new $this->feed->enclosure_class($url, $type, $length, $this->feed->javascript, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width); + } + } + + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link') as $link) + { + if (isset($link['attribs']['']['href']) && !empty($link['attribs']['']['rel']) && $link['attribs']['']['rel'] == 'enclosure') + { + // Attributes + $bitrate = null; + $channels = null; + $duration = null; + $expression = null; + $framerate = null; + $height = null; + $javascript = null; + $lang = null; + $length = null; + $medium = null; + $samplingrate = null; + $type = null; + $url = null; + $width = null; + + $url = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); + if (isset($link['attribs']['']['type'])) + { + $type = $this->sanitize($link['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($link['attribs']['']['length'])) + { + $length = ceil($link['attribs']['']['length']); + } + + // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor + $this->data['enclosures'][] =& new $this->feed->enclosure_class($url, $type, $length, $this->feed->javascript, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width); + } + } + + if ($enclosure = $this->get_item_tags('', 'enclosure')) + { + if (isset($enclosure[0]['attribs']['']['url'])) + { + // Attributes + $bitrate = null; + $channels = null; + $duration = null; + $expression = null; + $framerate = null; + $height = null; + $javascript = null; + $lang = null; + $length = null; + $medium = null; + $samplingrate = null; + $type = null; + $url = null; + $width = null; + + $url = $this->sanitize($enclosure[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($enclosure[0])); + if (isset($enclosure[0]['attribs']['']['type'])) + { + $type = $this->sanitize($enclosure[0]['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($enclosure[0]['attribs']['']['length'])) + { + $length = ceil($enclosure[0]['attribs']['']['length']); + } + + // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor + $this->data['enclosures'][] =& new $this->feed->enclosure_class($url, $type, $length, $this->feed->javascript, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width); + } + } + + if (sizeof($this->data['enclosures']) == 0 && ($url || $type || $length || $bitrate || $captions_parent || $categories_parent || $channels || $copyrights_parent || $credits_parent || $description_parent || $duration_parent || $expression || $framerate || $hashes_parent || $height || $keywords_parent || $lang || $medium || $player_parent || $ratings_parent || $restrictions_parent || $samplingrate || $thumbnails_parent || $title_parent || $width)) + { + // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor + $this->data['enclosures'][] =& new $this->feed->enclosure_class($url, $type, $length, $this->feed->javascript, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width); + } + + $this->data['enclosures'] = array_values(SimplePie_Misc::array_unique($this->data['enclosures'])); + } + if (!empty($this->data['enclosures'])) + { + return $this->data['enclosures']; + } + else + { + return null; + } + } + + function get_latitude() + { + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat')) + { + return (float) $return[0]['data']; + } + elseif (($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', $return[0]['data'], $match)) + { + return (float) $match[1]; + } + else + { + return null; + } + } + + function get_longitude() + { + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long')) + { + return (float) $return[0]['data']; + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon')) + { + return (float) $return[0]['data']; + } + elseif (($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', $return[0]['data'], $match)) + { + return (float) $match[2]; + } + else + { + return null; + } + } + + function get_source() + { + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'source')) + { + return new $this->feed->source_class($this, $return[0]); + } + else + { + return null; + } + } + + /** + * Creates the add_to_* methods' return data + * + * @access private + * @param string $item_url String to prefix to the item permalink + * @param string $title_url String to prefix to the item title + * (and suffix to the item permalink) + * @return mixed URL if feed exists, false otherwise + */ + function add_to_service($item_url, $title_url = null, $summary_url = null) + { + if ($this->get_permalink() !== null) + { + $return = $this->sanitize($item_url, SIMPLEPIE_CONSTRUCT_IRI) . rawurlencode($this->get_permalink()); + if ($title_url !== null && $this->get_title() !== null) + { + $return .= $this->sanitize($title_url, SIMPLEPIE_CONSTRUCT_IRI) . rawurlencode($this->get_title()); + } + if ($summary_url !== null && $this->get_description() !== null) + { + $return .= $this->sanitize($summary_url, SIMPLEPIE_CONSTRUCT_IRI) . rawurlencode($this->get_description()); + } + return $return; + } + else + { + return null; + } + } + + function add_to_blinklist() + { + return $this->add_to_service('http://www.blinklist.com/index.php?Action=Blink/addblink.php&Description=&Url=', '&Title='); + } + + function add_to_blogmarks() + { + return $this->add_to_service('http://blogmarks.net/my/new.php?mini=1&simple=1&url=', '&title='); + } + + function add_to_delicious() + { + return $this->add_to_service('http://del.icio.us/post/?v=4&url=', '&title='); + } + + function add_to_digg() + { + return $this->add_to_service('http://digg.com/submit?url=', '&title=', '&bodytext='); + } + + function add_to_furl() + { + return $this->add_to_service('http://www.furl.net/storeIt.jsp?u=', '&t='); + } + + function add_to_magnolia() + { + return $this->add_to_service('http://ma.gnolia.com/bookmarklet/add?url=', '&title='); + } + + function add_to_myweb20() + { + return $this->add_to_service('http://myweb2.search.yahoo.com/myresults/bookmarklet?u=', '&t='); + } + + function add_to_newsvine() + { + return $this->add_to_service('http://www.newsvine.com/_wine/save?u=', '&h='); + } + + function add_to_reddit() + { + return $this->add_to_service('http://reddit.com/submit?url=', '&title='); + } + + function add_to_segnalo() + { + return $this->add_to_service('http://segnalo.com/post.html.php?url=', '&title='); + } + + function add_to_simpy() + { + return $this->add_to_service('http://www.simpy.com/simpy/LinkAdd.do?href=', '&title='); + } + + function add_to_spurl() + { + return $this->add_to_service('http://www.spurl.net/spurl.php?v=3&url=', '&title='); + } + + function add_to_wists() + { + return $this->add_to_service('http://wists.com/r.php?c=&r=', '&title='); + } + + function search_technorati() + { + return $this->add_to_service('http://www.technorati.com/search/'); + } +} + +class SimplePie_Source +{ + var $item; + var $data = array(); + + function SimplePie_Source($item, $data) + { + $this->item = $item; + $this->data = $data; + } + + function __toString() + { + return md5(serialize($this->data)); + } + + /** + * Remove items that link back to this before destroying this object + */ + function __destruct() + { + unset($this->item); + } + + function get_source_tags($namespace, $tag) + { + if (isset($this->data['child'][$namespace][$tag])) + { + return $this->data['child'][$namespace][$tag]; + } + else + { + return null; + } + } + + function get_base($element = array()) + { + return $this->item->get_base($element); + } + + function sanitize($data, $type, $base = '') + { + return $this->item->sanitize($data, $type, $base); + } + + function get_item() + { + return $this->item; + } + + function get_title() + { + if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title')) + { + return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title')) + { + return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags('', 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + return null; + } + } + + function get_category($key = 0) + { + $categories = $this->get_categories(); + if (isset($categories[$key])) + { + return $categories[$key]; + } + else + { + return null; + } + } + + function get_categories() + { + $categories = array(); + + foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category) + { + $term = null; + $scheme = null; + $label = null; + if (isset($category['attribs']['']['term'])) + { + $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($category['attribs']['']['scheme'])) + { + $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($category['attribs']['']['label'])) + { + $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $categories[] =& new $this->item->feed->category_class($term, $scheme, $label); + } + foreach ((array) $this->get_source_tags('', 'category') as $category) + { + $categories[] =& new $this->item->feed->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + } + foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category) + { + $categories[] =& new $this->item->feed->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + } + foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category) + { + $categories[] =& new $this->item->feed->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + } + + if (!empty($categories)) + { + return SimplePie_Misc::array_unique($categories); + } + else + { + return null; + } + } + + function get_author($key = 0) + { + $authors = $this->get_authors(); + if (isset($authors[$key])) + { + return $authors[$key]; + } + else + { + return null; + } + } + + function get_authors() + { + $authors = array(); + foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author) + { + $name = null; + $uri = null; + $email = null; + if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) + { + $name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) + { + $uri = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); + } + if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) + { + $email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $uri !== null) + { + $authors[] =& new $this->item->feed->author_class($name, $uri, $email); + } + } + if ($author = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author')) + { + $name = null; + $url = null; + $email = null; + if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) + { + $name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) + { + $url = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); + } + if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) + { + $email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $url !== null) + { + $authors[] =& new $this->item->feed->author_class($name, $url, $email); + } + } + foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author) + { + $authors[] =& new $this->item->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + } + foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author) + { + $authors[] =& new $this->item->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + } + foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author) + { + $authors[] =& new $this->item->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); + } + + if (!empty($authors)) + { + return SimplePie_Misc::array_unique($authors); + } + else + { + return null; + } + } + + function get_contributor($key = 0) + { + $contributors = $this->get_contributors(); + if (isset($contributors[$key])) + { + return $contributors[$key]; + } + else + { + return null; + } + } + + function get_contributors() + { + $contributors = array(); + foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor) + { + $name = null; + $uri = null; + $email = null; + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) + { + $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) + { + $uri = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) + { + $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $uri !== null) + { + $contributors[] =& new $this->item->feed->author_class($name, $uri, $email); + } + } + foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor) + { + $name = null; + $url = null; + $email = null; + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) + { + $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) + { + $url = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) + { + $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $url !== null) + { + $contributors[] =& new $this->item->feed->author_class($name, $url, $email); + } + } + + if (!empty($contributors)) + { + return SimplePie_Misc::array_unique($contributors); + } + else + { + return null; + } + } + + function get_link($key = 0, $rel = 'alternate') + { + $links = $this->get_links($rel); + if (isset($links[$key])) + { + return $links[$key]; + } + else + { + return null; + } + } + + /** + * Added for parity between the parent-level and the item/entry-level. + */ + function get_permalink() + { + return $this->get_link(0); + } + + function get_links($rel = 'alternate') + { + if (!isset($this->data['links'])) + { + $this->data['links'] = array(); + if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link')) + { + foreach ($links as $link) + { + if (isset($link['attribs']['']['href'])) + { + $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; + $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); + } + } + } + if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link')) + { + foreach ($links as $link) + { + if (isset($link['attribs']['']['href'])) + { + $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; + $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); + + } + } + } + if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link')) + { + $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + } + if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link')) + { + $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + } + if ($links = $this->get_source_tags('', 'link')) + { + $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + } + + $keys = array_keys($this->data['links']); + foreach ($keys as $key) + { + if (SimplePie_Misc::is_isegment_nz_nc($key)) + { + if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key])) + { + $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]); + $this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]; + } + else + { + $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key]; + } + } + elseif (substr($key, 0, 41) == SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY) + { + $this->data['links'][substr($key, 41)] =& $this->data['links'][$key]; + } + $this->data['links'][$key] = array_unique($this->data['links'][$key]); + } + } + + if (isset($this->data['links'][$rel])) + { + return $this->data['links'][$rel]; + } + else + { + return null; + } + } + + function get_description() + { + if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'subtitle')) + { + return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'tagline')) + { + return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags('', 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); + } + else + { + return null; + } + } + + function get_copyright() + { + if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights')) + { + return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'copyright')) + { + return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags('', 'copyright')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + return null; + } + } + + function get_language() + { + if ($return = $this->get_source_tags('', 'language')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'language')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'language')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif (isset($this->data['xml_lang'])) + { + return $this->sanitize($this->data['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + return null; + } + } + + function get_latitude() + { + if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat')) + { + return (float) $return[0]['data']; + } + elseif (($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', $return[0]['data'], $match)) + { + return (float) $match[1]; + } + else + { + return null; + } + } + + function get_longitude() + { + if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long')) + { + return (float) $return[0]['data']; + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon')) + { + return (float) $return[0]['data']; + } + elseif (($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', $return[0]['data'], $match)) + { + return (float) $match[2]; + } + else + { + return null; + } + } + + function get_image_url() + { + if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'image')) + { + return $this->sanitize($return[0]['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'logo')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'icon')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); + } + else + { + return null; + } + } +} + +class SimplePie_Author +{ + var $name; + var $link; + var $email; + + // Constructor, used to input the data + function SimplePie_Author($name = null, $link = null, $email = null) + { + $this->name = $name; + $this->link = $link; + $this->email = $email; + } + + function __toString() + { + // There is no $this->data here + return md5(serialize($this)); + } + + function get_name() + { + if ($this->name !== null) + { + return $this->name; + } + else + { + return null; + } + } + + function get_link() + { + if ($this->link !== null) + { + return $this->link; + } + else + { + return null; + } + } + + function get_email() + { + if ($this->email !== null) + { + return $this->email; + } + else + { + return null; + } + } +} + +class SimplePie_Category +{ + var $term; + var $scheme; + var $label; + + // Constructor, used to input the data + function SimplePie_Category($term = null, $scheme = null, $label = null) + { + $this->term = $term; + $this->scheme = $scheme; + $this->label = $label; + } + + function __toString() + { + // There is no $this->data here + return md5(serialize($this)); + } + + function get_term() + { + if ($this->term !== null) + { + return $this->term; + } + else + { + return null; + } + } + + function get_scheme() + { + if ($this->scheme !== null) + { + return $this->scheme; + } + else + { + return null; + } + } + + function get_label() + { + if ($this->label !== null) + { + return $this->label; + } + else + { + return $this->get_term(); + } + } +} + +class SimplePie_Enclosure +{ + var $bitrate; + var $captions; + var $categories; + var $channels; + var $copyright; + var $credits; + var $description; + var $duration; + var $expression; + var $framerate; + var $handler; + var $hashes; + var $height; + var $javascript; + var $keywords; + var $lang; + var $length; + var $link; + var $medium; + var $player; + var $ratings; + var $restrictions; + var $samplingrate; + var $thumbnails; + var $title; + var $type; + var $width; + + // Constructor, used to input the data + function SimplePie_Enclosure($link = null, $type = null, $length = null, $javascript = null, $bitrate = null, $captions = null, $categories = null, $channels = null, $copyright = null, $credits = null, $description = null, $duration = null, $expression = null, $framerate = null, $hashes = null, $height = null, $keywords = null, $lang = null, $medium = null, $player = null, $ratings = null, $restrictions = null, $samplingrate = null, $thumbnails = null, $title = null, $width = null) + { + $this->bitrate = $bitrate; + $this->captions = $captions; + $this->categories = $categories; + $this->channels = $channels; + $this->copyright = $copyright; + $this->credits = $credits; + $this->description = $description; + $this->duration = $duration; + $this->expression = $expression; + $this->framerate = $framerate; + $this->hashes = $hashes; + $this->height = $height; + $this->javascript = $javascript; + $this->keywords = $keywords; + $this->lang = $lang; + $this->length = $length; + $this->link = $link; + $this->medium = $medium; + $this->player = $player; + $this->ratings = $ratings; + $this->restrictions = $restrictions; + $this->samplingrate = $samplingrate; + $this->thumbnails = $thumbnails; + $this->title = $title; + $this->type = $type; + $this->width = $width; + if (class_exists('idna_convert')) + { + $idn =& new idna_convert; + $parsed = SimplePie_Misc::parse_url($link); + $this->link = SimplePie_Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], $parsed['fragment']); + } + $this->handler = $this->get_handler(); // Needs to load last + } + + function __toString() + { + // There is no $this->data here + return md5(serialize($this)); + } + + function get_bitrate() + { + if ($this->bitrate !== null) + { + return $this->bitrate; + } + else + { + return null; + } + } + + function get_caption($key = 0) + { + $captions = $this->get_captions(); + if (isset($captions[$key])) + { + return $captions[$key]; + } + else + { + return null; + } + } + + function get_captions() + { + if ($this->captions !== null) + { + return $this->captions; + } + else + { + return null; + } + } + + function get_category($key = 0) + { + $categories = $this->get_categories(); + if (isset($categories[$key])) + { + return $categories[$key]; + } + else + { + return null; + } + } + + function get_categories() + { + if ($this->categories !== null) + { + return $this->categories; + } + else + { + return null; + } + } + + function get_channels() + { + if ($this->channels !== null) + { + return $this->channels; + } + else + { + return null; + } + } + + function get_copyright() + { + if ($this->copyright !== null) + { + return $this->copyright; + } + else + { + return null; + } + } + + function get_credit($key = 0) + { + $credits = $this->get_credits(); + if (isset($credits[$key])) + { + return $credits[$key]; + } + else + { + return null; + } + } + + function get_credits() + { + if ($this->credits !== null) + { + return $this->credits; + } + else + { + return null; + } + } + + function get_description() + { + if ($this->description !== null) + { + return $this->description; + } + else + { + return null; + } + } + + function get_duration($convert = false) + { + if ($this->duration !== null) + { + if ($convert) + { + $time = SimplePie_Misc::time_hms($this->duration); + return $time; + } + else + { + return $this->duration; + } + } + else + { + return null; + } + } + + function get_expression() + { + if ($this->expression !== null) + { + return $this->expression; + } + else + { + return 'full'; + } + } + + function get_extension() + { + if ($this->link !== null) + { + $url = SimplePie_Misc::parse_url($this->link); + if ($url['path'] !== '') + { + return pathinfo($url['path'], PATHINFO_EXTENSION); + } + } + return null; + } + + function get_framerate() + { + if ($this->framerate !== null) + { + return $this->framerate; + } + else + { + return null; + } + } + + function get_handler() + { + return $this->get_real_type(true); + } + + function get_hash($key = 0) + { + $hashes = $this->get_hashes(); + if (isset($hashes[$key])) + { + return $hashes[$key]; + } + else + { + return null; + } + } + + function get_hashes() + { + if ($this->hashes !== null) + { + return $this->hashes; + } + else + { + return null; + } + } + + function get_height() + { + if ($this->height !== null) + { + return $this->height; + } + else + { + return null; + } + } + + function get_language() + { + if ($this->lang !== null) + { + return $this->lang; + } + else + { + return null; + } + } + + function get_keyword($key = 0) + { + $keywords = $this->get_keywords(); + if (isset($keywords[$key])) + { + return $keywords[$key]; + } + else + { + return null; + } + } + + function get_keywords() + { + if ($this->keywords !== null) + { + return $this->keywords; + } + else + { + return null; + } + } + + function get_length() + { + if ($this->length !== null) + { + return $this->length; + } + else + { + return null; + } + } + + function get_link() + { + if ($this->link !== null) + { + return urldecode($this->link); + } + else + { + return null; + } + } + + function get_medium() + { + if ($this->medium !== null) + { + return $this->medium; + } + else + { + return null; + } + } + + function get_player() + { + if ($this->player !== null) + { + return $this->player; + } + else + { + return null; + } + } + + function get_rating($key = 0) + { + $ratings = $this->get_ratings(); + if (isset($ratings[$key])) + { + return $ratings[$key]; + } + else + { + return null; + } + } + + function get_ratings() + { + if ($this->ratings !== null) + { + return $this->ratings; + } + else + { + return null; + } + } + + function get_restriction($key = 0) + { + $restrictions = $this->get_restrictions(); + if (isset($restrictions[$key])) + { + return $restrictions[$key]; + } + else + { + return null; + } + } + + function get_restrictions() + { + if ($this->restrictions !== null) + { + return $this->restrictions; + } + else + { + return null; + } + } + + function get_sampling_rate() + { + if ($this->samplingrate !== null) + { + return $this->samplingrate; + } + else + { + return null; + } + } + + function get_size() + { + $length = $this->get_length(); + if ($length !== null) + { + return round($length/1048576, 2); + } + else + { + return null; + } + } + + function get_thumbnail($key = 0) + { + $thumbnails = $this->get_thumbnails(); + if (isset($thumbnails[$key])) + { + return $thumbnails[$key]; + } + else + { + return null; + } + } + + function get_thumbnails() + { + if ($this->thumbnails !== null) + { + return $this->thumbnails; + } + else + { + return null; + } + } + + function get_title() + { + if ($this->title !== null) + { + return $this->title; + } + else + { + return null; + } + } + + function get_type() + { + if ($this->type !== null) + { + return $this->type; + } + else + { + return null; + } + } + + function get_width() + { + if ($this->width !== null) + { + return $this->width; + } + else + { + return null; + } + } + + function native_embed($options='') + { + return $this->embed($options, true); + } + + /** + * @todo If the dimensions for media:content are defined, use them when width/height are set to 'auto'. + */ + function embed($options = '', $native = false) + { + // Set up defaults + $audio = ''; + $video = ''; + $alt = ''; + $altclass = ''; + $loop = 'false'; + $width = 'auto'; + $height = 'auto'; + $bgcolor = '#ffffff'; + $mediaplayer = ''; + $widescreen = false; + $handler = $this->get_handler(); + $type = $this->get_real_type(); + + // Process options and reassign values as necessary + if (is_array($options)) + { + extract($options); + } + else + { + $options = explode(',', $options); + foreach($options as $option) + { + $opt = explode(':', $option, 2); + if (isset($opt[0], $opt[1])) + { + $opt[0] = trim($opt[0]); + $opt[1] = trim($opt[1]); + switch ($opt[0]) + { + case 'audio': + $audio = $opt[1]; + break; + + case 'video': + $video = $opt[1]; + break; + + case 'alt': + $alt = $opt[1]; + break; + + case 'altclass': + $altclass = $opt[1]; + break; + + case 'loop': + $loop = $opt[1]; + break; + + case 'width': + $width = $opt[1]; + break; + + case 'height': + $height = $opt[1]; + break; + + case 'bgcolor': + $bgcolor = $opt[1]; + break; + + case 'mediaplayer': + $mediaplayer = $opt[1]; + break; + + case 'widescreen': + $widescreen = $opt[1]; + break; + } + } + } + } + + $mime = explode('/', $type, 2); + $mime = $mime[0]; + + // Process values for 'auto' + if ($width == 'auto') + { + if ($mime == 'video') + { + if ($height == 'auto') + { + $width = 480; + } + elseif ($widescreen) + { + $width = round((intval($height)/9)*16); + } + else + { + $width = round((intval($height)/3)*4); + } + } + else + { + $width = '100%'; + } + } + + if ($height == 'auto') + { + if ($mime == 'audio') + { + $height = 0; + } + elseif ($mime == 'video') + { + if ($width == 'auto') + { + if ($widescreen) + { + $height = 270; + } + else + { + $height = 360; + } + } + elseif ($widescreen) + { + $height = round((intval($width)/16)*9); + } + else + { + $height = round((intval($width)/4)*3); + } + } + else + { + $height = 376; + } + } + elseif ($mime == 'audio') + { + $height = 0; + } + + // Set proper placeholder value + if ($mime == 'audio') + { + $placeholder = $audio; + } + elseif ($mime == 'video') + { + $placeholder = $video; + } + + $embed = ''; + + // Make sure the JS library is included + if (!$native) + { + static $javascript_outputted = null; + if (!$javascript_outputted && $this->javascript) + { + $embed .= ''; + $javascript_outputted = true; + } + } + + // Odeo Feed MP3's + if ($handler == 'odeo') + { + if ($native) + { + $embed .= ''; + } + else + { + $embed .= ''; + } + } + + // Flash + elseif ($handler == 'flash') + { + if ($native) + { + $embed .= "get_link() . "\" pluginspage=\"http://adobe.com/go/getflashplayer\" type=\"$type\" quality=\"high\" width=\"$width\" height=\"$height\" bgcolor=\"$bgcolor\" loop=\"$loop\">"; + } + else + { + $embed .= ""; + } + } + + // Flash Media Player file types. + // Preferred handler for MP3 file types. + elseif ($handler == 'fmedia' || ($handler == 'mp3' && $mediaplayer != '')) + { + $height += 20; + if ($native) + { + $embed .= "get_link().'?file_extension=.'.$this->get_extension()) . "&autostart=false&repeat=$loop&showdigits=true&showfsbutton=false\">"; + } + else + { + $embed .= ""; + } + } + + // QuickTime 7 file types. Need to test with QuickTime 6. + // Only handle MP3's if the Flash Media Player is not present. + elseif ($handler == 'quicktime' || ($handler == 'mp3' && $mediaplayer == '')) + { + $height += 16; + if ($native) + { + if ($placeholder != ""){ + $embed .= "get_link() . "\" src=\"$placeholder\" width=\"$width\" height=\"$height\" autoplay=\"false\" target=\"myself\" controller=\"false\" loop=\"$loop\" scale=\"aspect\" bgcolor=\"$bgcolor\" pluginspage=\"http://apple.com/quicktime/download/\">"; + } + else { + $embed .= "get_link() . "\" width=\"$width\" height=\"$height\" autoplay=\"false\" target=\"myself\" controller=\"true\" loop=\"$loop\" scale=\"aspect\" bgcolor=\"$bgcolor\" pluginspage=\"http://apple.com/quicktime/download/\">"; + } + } + else + { + $embed .= ""; + } + } + + // Windows Media + elseif ($handler == 'wmedia') + { + $height += 45; + if ($native) + { + $embed .= "get_link() . "\" autosize=\"1\" width=\"$width\" height=\"$height\" showcontrols=\"1\" showstatusbar=\"0\" showdisplay=\"0\" autostart=\"0\">"; + } + else + { + $embed .= ""; + } + } + + // Everything else + else $embed .= '' . $alt . ''; + + return $embed; + } + + function get_real_type($find_handler = false) + { + // If it's Odeo, let's get it out of the way. + if (substr(strtolower($this->get_link()), 0, 15) == 'http://odeo.com') + { + return 'odeo'; + } + + // Mime-types by handler. + $types_flash = array('application/x-shockwave-flash', 'application/futuresplash'); // Flash + $types_fmedia = array('video/flv', 'video/x-flv'); // Flash Media Player + $types_quicktime = array('audio/3gpp', 'audio/3gpp2', 'audio/aac', 'audio/x-aac', 'audio/aiff', 'audio/x-aiff', 'audio/mid', 'audio/midi', 'audio/x-midi', 'audio/mp4', 'audio/m4a', 'audio/x-m4a', 'audio/wav', 'audio/x-wav', 'video/3gpp', 'video/3gpp2', 'video/m4v', 'video/x-m4v', 'video/mp4', 'video/mpeg', 'video/x-mpeg', 'video/quicktime', 'video/sd-video'); // QuickTime + $types_wmedia = array('application/asx', 'application/x-mplayer2', 'audio/x-ms-wma', 'audio/x-ms-wax', 'video/x-ms-asf-plugin', 'video/x-ms-asf', 'video/x-ms-wm', 'video/x-ms-wmv', 'video/x-ms-wvx'); // Windows Media + $types_mp3 = array('audio/mp3', 'audio/x-mp3', 'audio/mpeg', 'audio/x-mpeg'); // MP3 + + if ($this->get_type() !== null) + { + $type = strtolower($this->type); + } + else + { + $type = null; + } + + // If we encounter an unsupported mime-type, check the file extension and guess intelligently. + if (!in_array($type, array_merge($types_flash, $types_fmedia, $types_quicktime, $types_wmedia, $types_mp3))) + { + switch (strtolower($this->get_extension())) + { + // Audio mime-types + case 'aac': + case 'adts': + $type = 'audio/acc'; + break; + + case 'aif': + case 'aifc': + case 'aiff': + case 'cdda': + $type = 'audio/aiff'; + break; + + case 'bwf': + $type = 'audio/wav'; + break; + + case 'kar': + case 'mid': + case 'midi': + case 'smf': + $type = 'audio/midi'; + break; + + case 'm4a': + $type = 'audio/x-m4a'; + break; + + case 'mp3': + case 'swa': + $type = 'audio/mp3'; + break; + + case 'wav': + $type = 'audio/wav'; + break; + + case 'wax': + $type = 'audio/x-ms-wax'; + break; + + case 'wma': + $type = 'audio/x-ms-wma'; + break; + + // Video mime-types + case '3gp': + case '3gpp': + $type = 'video/3gpp'; + break; + + case '3g2': + case '3gp2': + $type = 'video/3gpp2'; + break; + + case 'asf': + $type = 'video/x-ms-asf'; + break; + + case 'flv': + $type = 'video/x-flv'; + break; + + case 'm1a': + case 'm1s': + case 'm1v': + case 'm15': + case 'm75': + case 'mp2': + case 'mpa': + case 'mpeg': + case 'mpg': + case 'mpm': + case 'mpv': + $type = 'video/mpeg'; + break; + + case 'm4v': + $type = 'video/x-m4v'; + break; + + case 'mov': + case 'qt': + $type = 'video/quicktime'; + break; + + case 'mp4': + case 'mpg4': + $type = 'video/mp4'; + break; + + case 'sdv': + $type = 'video/sd-video'; + break; + + case 'wm': + $type = 'video/x-ms-wm'; + break; + + case 'wmv': + $type = 'video/x-ms-wmv'; + break; + + case 'wvx': + $type = 'video/x-ms-wvx'; + break; + + // Flash mime-types + case 'spl': + $type = 'application/futuresplash'; + break; + + case 'swf': + $type = 'application/x-shockwave-flash'; + break; + } + } + + if ($find_handler) + { + if (in_array($type, $types_flash)) + { + return 'flash'; + } + elseif (in_array($type, $types_fmedia)) + { + return 'fmedia'; + } + elseif (in_array($type, $types_quicktime)) + { + return 'quicktime'; + } + elseif (in_array($type, $types_wmedia)) + { + return 'wmedia'; + } + elseif (in_array($type, $types_mp3)) + { + return 'mp3'; + } + else + { + return null; + } + } + else + { + return $type; + } + } +} + +class SimplePie_Caption +{ + var $type; + var $lang; + var $startTime; + var $endTime; + var $text; + + // Constructor, used to input the data + function SimplePie_Caption($type = null, $lang = null, $startTime = null, $endTime = null, $text = null) + { + $this->type = $type; + $this->lang = $lang; + $this->startTime = $startTime; + $this->endTime = $endTime; + $this->text = $text; + } + + function __toString() + { + // There is no $this->data here + return md5(serialize($this)); + } + + function get_endtime() + { + if ($this->endTime !== null) + { + return $this->endTime; + } + else + { + return null; + } + } + + function get_language() + { + if ($this->lang !== null) + { + return $this->lang; + } + else + { + return null; + } + } + + function get_starttime() + { + if ($this->startTime !== null) + { + return $this->startTime; + } + else + { + return null; + } + } + + function get_text() + { + if ($this->text !== null) + { + return $this->text; + } + else + { + return null; + } + } + + function get_type() + { + if ($this->type !== null) + { + return $this->type; + } + else + { + return null; + } + } +} + +class SimplePie_Credit +{ + var $role; + var $scheme; + var $name; + + // Constructor, used to input the data + function SimplePie_Credit($role = null, $scheme = null, $name = null) + { + $this->role = $role; + $this->scheme = $scheme; + $this->name = $name; + } + + function __toString() + { + // There is no $this->data here + return md5(serialize($this)); + } + + function get_role() + { + if ($this->role !== null) + { + return $this->role; + } + else + { + return null; + } + } + + function get_scheme() + { + if ($this->scheme !== null) + { + return $this->scheme; + } + else + { + return null; + } + } + + function get_name() + { + if ($this->name !== null) + { + return $this->name; + } + else + { + return null; + } + } +} + +class SimplePie_Copyright +{ + var $url; + var $label; + + // Constructor, used to input the data + function SimplePie_Copyright($url = null, $label = null) + { + $this->url = $url; + $this->label = $label; + } + + function __toString() + { + // There is no $this->data here + return md5(serialize($this)); + } + + function get_url() + { + if ($this->url !== null) + { + return $this->url; + } + else + { + return null; + } + } + + function get_attribution() + { + if ($this->label !== null) + { + return $this->label; + } + else + { + return null; + } + } +} + +class SimplePie_Rating +{ + var $scheme; + var $value; + + // Constructor, used to input the data + function SimplePie_Rating($scheme = null, $value = null) + { + $this->scheme = $scheme; + $this->value = $value; + } + + function __toString() + { + // There is no $this->data here + return md5(serialize($this)); + } + + function get_scheme() + { + if ($this->scheme !== null) + { + return $this->scheme; + } + else + { + return null; + } + } + + function get_value() + { + if ($this->value !== null) + { + return $this->value; + } + else + { + return null; + } + } +} + +class SimplePie_Restriction +{ + var $relationship; + var $type; + var $value; + + // Constructor, used to input the data + function SimplePie_Restriction($relationship = null, $type = null, $value = null) + { + $this->relationship = $relationship; + $this->type = $type; + $this->value = $value; + } + + function __toString() + { + // There is no $this->data here + return md5(serialize($this)); + } + + function get_relationship() + { + if ($this->relationship !== null) + { + return $this->relationship; + } + else + { + return null; + } + } + + function get_type() + { + if ($this->type !== null) + { + return $this->type; + } + else + { + return null; + } + } + + function get_value() + { + if ($this->value !== null) + { + return $this->value; + } + else + { + return null; + } + } +} + +/** + * @todo Move to properly supporting RFC2616 (HTTP/1.1) + */ +class SimplePie_File +{ + var $url; + var $useragent; + var $success = true; + var $headers = array(); + var $body; + var $status_code; + var $redirects = 0; + var $error; + var $method = SIMPLEPIE_FILE_SOURCE_NONE; + + function SimplePie_File($url, $timeout = 10, $redirects = 5, $headers = null, $useragent = null, $force_fsockopen = false) + { + if (class_exists('idna_convert')) + { + $idn =& new idna_convert; + $parsed = SimplePie_Misc::parse_url($url); + $url = SimplePie_Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], $parsed['fragment']); + } + $this->url = $url; + $this->useragent = $useragent; + if (preg_match('/^http(s)?:\/\//i', $url)) + { + if ($useragent === null) + { + $useragent = ini_get('user_agent'); + $this->useragent = $useragent; + } + if (!is_array($headers)) + { + $headers = array(); + } + if (!$force_fsockopen && function_exists('curl_exec')) + { + $this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_CURL; + $fp = curl_init(); + $headers2 = array(); + foreach ($headers as $key => $value) + { + $headers2[] = "$key: $value"; + } + if (version_compare(SimplePie_Misc::get_curl_version(), '7.10.5', '>=')) + { + curl_setopt($fp, CURLOPT_ENCODING, ''); + } + curl_setopt($fp, CURLOPT_URL, $url); + curl_setopt($fp, CURLOPT_HEADER, 1); + curl_setopt($fp, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($fp, CURLOPT_TIMEOUT, $timeout); + curl_setopt($fp, CURLOPT_CONNECTTIMEOUT, $timeout); + curl_setopt($fp, CURLOPT_REFERER, $url); + curl_setopt($fp, CURLOPT_USERAGENT, $useragent); + curl_setopt($fp, CURLOPT_HTTPHEADER, $headers2); + if (!ini_get('open_basedir') && !ini_get('safe_mode') && version_compare(SimplePie_Misc::get_curl_version(), '7.15.2', '>=')) + { + curl_setopt($fp, CURLOPT_FOLLOWLOCATION, 1); + curl_setopt($fp, CURLOPT_MAXREDIRS, $redirects); + } + + $this->headers = curl_exec($fp); + if (curl_errno($fp) == 23 || curl_errno($fp) == 61) + { + curl_setopt($fp, CURLOPT_ENCODING, 'none'); + $this->headers = curl_exec($fp); + } + if (curl_errno($fp)) + { + $this->error = 'cURL error ' . curl_errno($fp) . ': ' . curl_error($fp); + $this->success = false; + } + else + { + $info = curl_getinfo($fp); + curl_close($fp); + $this->headers = explode("\r\n\r\n", $this->headers, $info['redirect_count'] + 1); + $this->headers = array_pop($this->headers); + $parser =& new SimplePie_HTTP_Parser($this->headers); + if ($parser->parse()) + { + $this->headers = $parser->headers; + $this->body = $parser->body; + $this->status_code = $parser->status_code; + if (($this->status_code == 300 || $this->status_code == 301 || $this->status_code == 302 || $this->status_code == 303 || $this->status_code == 307 || $this->status_code > 307 && $this->status_code < 400) && isset($this->headers['location']) && $this->redirects < $redirects) + { + $this->redirects++; + $location = SimplePie_Misc::absolutize_url($this->headers['location'], $url); + return $this->SimplePie_File($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen); + } + } + } + } + else + { + $this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_FSOCKOPEN; + $url_parts = parse_url($url); + if (isset($url_parts['scheme']) && strtolower($url_parts['scheme']) == 'https') + { + $url_parts['host'] = "ssl://$url_parts[host]"; + $url_parts['port'] = 443; + } + if (!isset($url_parts['port'])) + { + $url_parts['port'] = 80; + } + $fp = @fsockopen($url_parts['host'], $url_parts['port'], $errno, $errstr, $timeout); + if (!$fp) + { + $this->error = 'fsockopen error: ' . $errstr; + $this->success = false; + } + else + { + stream_set_timeout($fp, $timeout); + if (isset($url_parts['path'])) + { + if (isset($url_parts['query'])) + { + $get = "$url_parts[path]?$url_parts[query]"; + } + else + { + $get = $url_parts['path']; + } + } + else + { + $get = '/'; + } + $out = "GET $get HTTP/1.0\r\n"; + $out .= "Host: $url_parts[host]\r\n"; + $out .= "User-Agent: $useragent\r\n"; + if (function_exists('gzinflate')) + { + $out .= "Accept-Encoding: gzip,deflate\r\n"; + } + + if (isset($url_parts['user']) && isset($url_parts['pass'])) + { + $out .= "Authorization: Basic " . base64_encode("$url_parts[user]:$url_parts[pass]") . "\r\n"; + } + foreach ($headers as $key => $value) + { + $out .= "$key: $value\r\n"; + } + $out .= "Connection: Close\r\n\r\n"; + fwrite($fp, $out); + + $info = stream_get_meta_data($fp); + + $this->headers = ''; + while (!$info['eof'] && !$info['timed_out']) + { + $this->headers .= fread($fp, 1160); + $info = stream_get_meta_data($fp); + } + if (!$info['timed_out']) + { + $parser =& new SimplePie_HTTP_Parser($this->headers); + if ($parser->parse()) + { + $this->headers = $parser->headers; + $this->body = $parser->body; + $this->status_code = $parser->status_code; + if (($this->status_code == 300 || $this->status_code == 301 || $this->status_code == 302 || $this->status_code == 303 || $this->status_code == 307 || $this->status_code > 307 && $this->status_code < 400) && isset($this->headers['location']) && $this->redirects < $redirects) + { + $this->redirects++; + $location = SimplePie_Misc::absolutize_url($this->headers['location'], $url); + return $this->SimplePie_File($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen); + } + if (isset($this->headers['content-encoding']) && ($this->headers['content-encoding'] == 'gzip' || $this->headers['content-encoding'] == 'deflate')) + { + if (substr($this->body, 0, 8) == "\x1f\x8b\x08\x00\x00\x00\x00\x00") + { + $this->body = substr($this->body, 10); + } + $this->body = gzinflate($this->body); + } + } + } + else + { + $this->error = 'fsocket timed out'; + $this->success = false; + } + fclose($fp); + } + } + } + else + { + $this->method = SIMPLEPIE_FILE_SOURCE_LOCAL | SIMPLEPIE_FILE_SOURCE_FILE_GET_CONTENTS; + if (!$this->body = file_get_contents($url)) + { + $this->error = 'file_get_contents could not read the file'; + $this->success = false; + } + } + } +} + +/** + * HTTP Response Parser + * + * @package SimplePie + */ +class SimplePie_HTTP_Parser +{ + /** + * HTTP Version + * + * @access public + * @var float + */ + var $http_version = 0.0; + + /** + * Status code + * + * @access public + * @var int + */ + var $status_code = 0; + + /** + * Reason phrase + * + * @access public + * @var string + */ + var $reason = ''; + + /** + * Key/value pairs of the headers + * + * @access public + * @var array + */ + var $headers = array(); + + /** + * Body of the response + * + * @access public + * @var string + */ + var $body = ''; + + /** + * Current state of the state machine + * + * @access private + * @var string + */ + var $state = 'http_version'; + + /** + * Input data + * + * @access private + * @var string + */ + var $data = ''; + + /** + * Input data length (to avoid calling strlen() everytime this is needed) + * + * @access private + * @var int + */ + var $data_length = 0; + + /** + * Current position of the pointer + * + * @var int + * @access private + */ + var $position = 0; + + /** + * Name of the hedaer currently being parsed + * + * @access private + * @var string + */ + var $name = ''; + + /** + * Value of the hedaer currently being parsed + * + * @access private + * @var string + */ + var $value = ''; + + /** + * Create an instance of the class with the input data + * + * @access public + * @param string $data Input data + */ + function SimplePie_HTTP_Parser($data) + { + $this->data = $data; + $this->data_length = strlen($this->data); + } + + /** + * Parse the input data + * + * @access public + * @return bool true on success, false on failure + */ + function parse() + { + while ($this->state && $this->state !== 'emit' && $this->has_data()) + { + $state = $this->state; + $this->$state(); + } + $this->data = ''; + if ($this->state === 'emit' || $this->state === 'body') + { + return true; + } + else + { + $this->http_version = ''; + $this->status_code = ''; + $this->reason = ''; + $this->headers = array(); + $this->body = ''; + return false; + } + } + + /** + * Check whether there is data beyond the pointer + * + * @access private + * @return bool true if there is further data, false if not + */ + function has_data() + { + return (bool) ($this->position < $this->data_length); + } + + /** + * See if the next character is LWS + * + * @access private + * @return bool true if the next character is LWS, false if not + */ + function is_linear_whitespace() + { + return (bool) ($this->data[$this->position] === "\x09" + || $this->data[$this->position] === "\x20" + || ($this->data[$this->position] === "\x0A" + && isset($this->data[$this->position + 1]) + && ($this->data[$this->position + 1] === "\x09" || $this->data[$this->position + 1] === "\x20"))); + } + + /** + * Parse the HTTP version + * + * @access private + */ + function http_version() + { + if (strpos($this->data, "\x0A") !== false && strtoupper(substr($this->data, 0, 5)) === 'HTTP/') + { + $len = strspn($this->data, '0123456789.', 5); + $this->http_version = substr($this->data, 5, $len); + $this->position += 5 + $len; + if (substr_count($this->http_version, '.') <= 1) + { + $this->http_version = (float) $this->http_version; + $this->position += strspn($this->data, "\x09\x20", $this->position); + $this->state = 'status'; + } + else + { + $this->state = false; + } + } + else + { + $this->state = false; + } + } + + /** + * Parse the status code + * + * @access private + */ + function status() + { + if ($len = strspn($this->data, '0123456789', $this->position)) + { + $this->status_code = (int) substr($this->data, $this->position, $len); + $this->position += $len; + $this->state = 'reason'; + } + else + { + $this->state = false; + } + } + + /** + * Parse the reason phrase + * + * @access private + */ + function reason() + { + $len = strcspn($this->data, "\x0A", $this->position); + $this->reason = trim(substr($this->data, $this->position, $len), "\x09\x0D\x20"); + $this->position += $len + 1; + $this->state = 'new_line'; + } + + /** + * Deal with a new line, shifting data around as needed + * + * @access private + */ + function new_line() + { + $this->value = trim($this->value, "\x0D\x20"); + if ($this->name !== '' && $this->value !== '') + { + $this->name = strtolower($this->name); + if (isset($this->headers[$this->name])) + { + $this->headers[$this->name] .= ', ' . $this->value; + } + else + { + $this->headers[$this->name] = $this->value; + } + } + $this->name = ''; + $this->value = ''; + if (substr($this->data[$this->position], 0, 2) === "\x0D\x0A") + { + $this->position += 2; + $this->state = 'body'; + } + elseif ($this->data[$this->position] === "\x0A") + { + $this->position++; + $this->state = 'body'; + } + else + { + $this->state = 'name'; + } + } + + /** + * Parse a header name + * + * @access private + */ + function name() + { + $len = strcspn($this->data, "\x0A:", $this->position); + if (isset($this->data[$this->position + $len])) + { + if ($this->data[$this->position + $len] === "\x0A") + { + $this->position += $len; + $this->state = 'new_line'; + } + else + { + $this->name = substr($this->data, $this->position, $len); + $this->position += $len + 1; + $this->state = 'value'; + } + } + else + { + $this->state = false; + } + } + + /** + * Parse LWS, replacing consecutive LWS characters with a single space + * + * @access private + */ + function linear_whitespace() + { + do + { + if (substr($this->data, $this->position, 2) === "\x0D\x0A") + { + $this->position += 2; + } + elseif ($this->data[$this->position] === "\x0A") + { + $this->position++; + } + $this->position += strspn($this->data, "\x09\x20", $this->position); + } while ($this->has_data() && $this->is_linear_whitespace()); + $this->value .= "\x20"; + } + + /** + * See what state to move to while within non-quoted header values + * + * @access private + */ + function value() + { + if ($this->is_linear_whitespace()) + { + $this->linear_whitespace(); + } + else + { + switch ($this->data[$this->position]) + { + case '"': + $this->position++; + $this->state = 'quote'; + break; + + case "\x0A": + $this->position++; + $this->state = 'new_line'; + break; + + default: + $this->state = 'value_char'; + break; + } + } + } + + /** + * Parse a header value while outside quotes + * + * @access private + */ + function value_char() + { + $len = strcspn($this->data, "\x09\x20\x0A\"", $this->position); + $this->value .= substr($this->data, $this->position, $len); + $this->position += $len; + $this->state = 'value'; + } + + /** + * See what state to move to while within quoted header values + * + * @access private + */ + function quote() + { + if ($this->is_linear_whitespace()) + { + $this->linear_whitespace(); + } + else + { + switch ($this->data[$this->position]) + { + case '"': + $this->position++; + $this->state = 'value'; + break; + + case "\x0A": + $this->position++; + $this->state = 'new_line'; + break; + + case '\\': + $this->position++; + $this->state = 'quote_escaped'; + break; + + default: + $this->state = 'quote_char'; + break; + } + } + } + + /** + * Parse a header value while within quotes + * + * @access private + */ + function quote_char() + { + $len = strcspn($this->data, "\x09\x20\x0A\"\\", $this->position); + $this->value .= substr($this->data, $this->position, $len); + $this->position += $len; + $this->state = 'value'; + } + + /** + * Parse an escaped character within quotes + * + * @access private + */ + function quote_escaped() + { + $this->value .= $this->data[$this->position]; + $this->position++; + $this->state = 'quote'; + } + + /** + * Parse the body + * + * @access private + */ + function body() + { + $this->body = substr($this->data, $this->position); + $this->state = 'emit'; + } +} + +class SimplePie_Cache +{ + /** + * Don't call the constructor. Please. + * + * @access private + */ + function SimplePie_Cache() + { + trigger_error('Please call SimplePie_Cache::create() instead of the constructor', E_USER_ERROR); + } + + /** + * Create a new SimplePie_Cache object + * + * @static + * @access public + */ + function create($location, $filename, $extension) + { + return new SimplePie_Cache_File($location, $filename, $extension); + } +} + +class SimplePie_Cache_File +{ + var $location; + var $filename; + var $extension; + var $name; + + function SimplePie_Cache_File($location, $filename, $extension) + { + $this->location = $location; + $this->filename = rawurlencode($filename); + $this->extension = rawurlencode($extension); + $this->name = "$location/$this->filename.$this->extension"; + } + + function save($data) + { + if (file_exists($this->name) && is_writeable($this->name) || file_exists($this->location) && is_writeable($this->location)) + { + if (is_a($data, 'SimplePie')) + { + $data = $data->data; + } + + $data = serialize($data); + + if (function_exists('file_put_contents')) + { + return (bool) file_put_contents($this->name, $data); + } + else + { + $fp = fopen($this->name, 'wb'); + if ($fp) + { + fwrite($fp, $data); + fclose($fp); + return true; + } + } + } + return false; + } + + function load() + { + if (file_exists($this->name) && is_readable($this->name)) + { + return unserialize(file_get_contents($this->name)); + } + return false; + } + + function mtime() + { + if (file_exists($this->name)) + { + return filemtime($this->name); + } + return false; + } + + function touch() + { + if (file_exists($this->name)) + { + return touch($this->name); + } + return false; + } + + function unlink() + { + if (file_exists($this->name)) + { + return unlink($this->name); + } + return false; + } +} + +class SimplePie_Misc +{ + function time_hms($seconds) + { + $time = ''; + + $hours = floor($seconds / 3600); + $remainder = $seconds % 3600; + if ($hours > 0) + { + $time .= $hours.':'; + } + + $minutes = floor($remainder / 60); + $seconds = $remainder % 60; + if ($minutes < 10 && $hours > 0) + { + $minutes = '0' . $minutes; + } + if ($seconds < 10) + { + $seconds = '0' . $seconds; + } + + $time .= $minutes.':'; + $time .= $seconds; + + return $time; + } + + function absolutize_url($relative, $base) + { + if ($relative !== '') + { + $relative = SimplePie_Misc::parse_url($relative); + if ($relative['scheme'] !== '') + { + $target = $relative; + } + elseif ($base !== '') + { + $base = SimplePie_Misc::parse_url($base); + $target = SimplePie_Misc::parse_url(''); + if ($relative['authority'] !== '') + { + $target = $relative; + $target['scheme'] = $base['scheme']; + } + else + { + $target['scheme'] = $base['scheme']; + $target['authority'] = $base['authority']; + if ($relative['path'] !== '') + { + if (strpos($relative['path'], '/') === 0) + { + $target['path'] = $relative['path']; + } + elseif ($base['authority'] !== '' && $base['path'] === '') + { + $target['path'] = '/' . $relative['path']; + } + elseif (($last_segment = strrpos($base['path'], '/')) !== false) + { + $target['path'] = substr($base['path'], 0, $last_segment + 1) . $relative['path']; + } + else + { + $target['path'] = $relative['path']; + } + $target['query'] = $relative['query']; + } + else + { + $target['path'] = $base['path']; + if ($relative['query'] !== '') + { + $target['query'] = $relative['query']; + } + elseif ($base['query'] !== '') + { + $target['query'] = $base['query']; + } + } + } + $target['fragment'] = $relative['fragment']; + } + else + { + // No base URL, just return the relative URL + $target = $relative; + } + $return = SimplePie_Misc::compress_parse_url($target['scheme'], $target['authority'], $target['path'], $target['query'], $target['fragment']); + } + else + { + $return = $base; + } + $return = SimplePie_Misc::normalize_url($return); + return $return; + } + + function remove_dot_segments($input) + { + $output = ''; + while (strpos($input, './') !== false || strpos($input, '/.') !== false || $input == '.' || $input == '..') + { + // A: If the input buffer begins with a prefix of "../" or "./", then remove that prefix from the input buffer; otherwise, + if (strpos($input, '../') === 0) + { + $input = substr($input, 3); + } + elseif (strpos($input, './') === 0) + { + $input = substr($input, 2); + } + // B: if the input buffer begins with a prefix of "/./" or "/.", where "." is a complete path segment, then replace that prefix with "/" in the input buffer; otherwise, + elseif (strpos($input, '/./') === 0) + { + $input = substr_replace($input, '/', 0, 3); + } + elseif ($input == '/.') + { + $input = '/'; + } + // C: if the input buffer begins with a prefix of "/../" or "/..", where ".." is a complete path segment, then replace that prefix with "/" in the input buffer and remove the last segment and its preceding "/" (if any) from the output buffer; otherwise, + elseif (strpos($input, '/../') === 0) + { + $input = substr_replace($input, '/', 0, 4); + $output = substr_replace($output, '', strrpos($output, '/')); + } + elseif ($input == '/..') + { + $input = '/'; + $output = substr_replace($output, '', strrpos($output, '/')); + } + // D: if the input buffer consists only of "." or "..", then remove that from the input buffer; otherwise, + elseif ($input == '.' || $input == '..') + { + $input = ''; + } + // E: move the first path segment in the input buffer to the end of the output buffer, including the initial "/" character (if any) and any subsequent characters up to, but not including, the next "/" character or the end of the input buffer + elseif (($pos = strpos($input, '/', 1)) !== false) + { + $output .= substr($input, 0, $pos); + $input = substr_replace($input, '', 0, $pos); + } + else + { + $output .= $input; + $input = ''; + } + } + return $output . $input; + } + + function get_element($realname, $string) + { + $return = array(); + $name = preg_quote($realname, '/'); + if (preg_match_all("/<($name)" . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . "(>(.*)<\/$name>|(\/)?>)/siU", $string, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) + { + for ($i = 0, $total_matches = count($matches); $i < $total_matches; $i++) + { + $return[$i]['tag'] = $realname; + $return[$i]['full'] = $matches[$i][0][0]; + $return[$i]['offset'] = $matches[$i][0][1]; + if (strlen($matches[$i][3][0]) <= 2) + { + $return[$i]['self_closing'] = true; + } + else + { + $return[$i]['self_closing'] = false; + $return[$i]['content'] = $matches[$i][4][0]; + } + $return[$i]['attribs'] = array(); + if (isset($matches[$i][2][0]) && preg_match_all('/[\x09\x0A\x0B\x0C\x0D\x20]+([^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3D\x3E]*)(?:[\x09\x0A\x0B\x0C\x0D\x20]*=[\x09\x0A\x0B\x0C\x0D\x20]*(?:"([^"]*)"|\'([^\']*)\'|([^\x09\x0A\x0B\x0C\x0D\x20\x22\x27\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x3E]*)?))?/', ' ' . $matches[$i][2][0] . ' ', $attribs, PREG_SET_ORDER)) + { + for ($j = 0, $total_attribs = count($attribs); $j < $total_attribs; $j++) + { + if (count($attribs[$j]) == 2) + { + $attribs[$j][2] = $attribs[$j][1]; + } + $return[$i]['attribs'][strtolower($attribs[$j][1])]['data'] = SimplePie_Misc::entities_decode(end($attribs[$j]), 'UTF-8'); + } + } + } + } + return $return; + } + + function element_implode($element) + { + $full = "<$element[tag]"; + foreach ($element['attribs'] as $key => $value) + { + $key = strtolower($key); + $full .= " $key=\"" . htmlspecialchars($value['data']) . '"'; + } + if ($element['self_closing']) + { + $full .= ' />'; + } + else + { + $full .= ">$element[content]"; + } + return $full; + } + + function error($message, $level, $file, $line) + { + switch ($level) + { + case E_USER_ERROR: + $note = 'PHP Error'; + break; + case E_USER_WARNING: + $note = 'PHP Warning'; + break; + case E_USER_NOTICE: + $note = 'PHP Notice'; + break; + default: + $note = 'Unknown Error'; + break; + } + error_log("$note: $message in $file on line $line", 0); + return $message; + } + + /** + * If a file has been cached, retrieve and display it. + * + * This is most useful for caching images (get_favicon(), etc.), + * however it works for all cached files. This WILL NOT display ANY + * file/image/page/whatever, but rather only display what has already + * been cached by SimplePie. + * + * @access public + * @see SimplePie::get_favicon() + * @param str $identifier_url URL that is used to identify the content. + * This may or may not be the actual URL of the live content. + * @param str $cache_location Location of SimplePie's cache. Defaults + * to './cache'. + * @param str $cache_extension The file extension that the file was + * cached with. Defaults to 'spc'. + * @param str $cache_class Name of the cache-handling class being used + * in SimplePie. Defaults to 'SimplePie_Cache', and should be left + * as-is unless you've overloaded the class. + * @param str $cache_name_function Obsolete. Exists for backwards + * compatibility reasons only. + */ + function display_cached_file($identifier_url, $cache_location = './cache', $cache_extension = 'spc', $cache_class = 'SimplePie_Cache', $cache_name_function = 'md5') + { + $cache = call_user_func(array($cache_class, 'create'), $cache_location, $identifier_url, $cache_extension); + + if ($file = $cache->load()) + { + if (isset($file['headers']['content-type'])) + { + header('Content-type:' . $file['headers']['content-type']); + } + else + { + header('Content-type: application/octet-stream'); + } + header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 604800) . ' GMT'); // 7 days + echo $file['body']; + exit; + } + + die('Cached file for ' . $identifier_url . ' cannot be found.'); + } + + function fix_protocol($url, $http = 1) + { + $url = SimplePie_Misc::normalize_url($url); + $parsed = SimplePie_Misc::parse_url($url); + if ($parsed['scheme'] !== '' && $parsed['scheme'] != 'http' && $parsed['scheme'] != 'https') + { + return SimplePie_Misc::fix_protocol(SimplePie_Misc::compress_parse_url('http', $parsed['authority'], $parsed['path'], $parsed['query'], $parsed['fragment']), $http); + } + + if ($parsed['scheme'] === '' && $parsed['authority'] === '' && !file_exists($url)) + { + return SimplePie_Misc::fix_protocol(SimplePie_Misc::compress_parse_url('http', $parsed['path'], '', $parsed['query'], $parsed['fragment']), $http); + } + + if ($http == 2 && $parsed['scheme'] !== '') + { + return "feed:$url"; + } + elseif ($http == 3 && strtolower($parsed['scheme']) == 'http') + { + return substr_replace($url, 'podcast', 0, 4); + } + elseif ($http == 4 && strtolower($parsed['scheme']) == 'http') + { + return substr_replace($url, 'itpc', 0, 4); + } + else + { + return $url; + } + } + + function parse_url($url) + { + static $cache = array(); + if (isset($cache[$url])) + { + return $cache[$url]; + } + elseif (preg_match('/^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/', $url, $match)) + { + for ($i = count($match); $i <= 9; $i++) + { + $match[$i] = ''; + } + return $cache[$url] = array('scheme' => $match[2], 'authority' => $match[4], 'path' => $match[5], 'query' => $match[7], 'fragment' => $match[9]); + } + else + { + return $cache[$url] = array('scheme' => '', 'authority' => '', 'path' => '', 'query' => '', 'fragment' => ''); + } + } + + function compress_parse_url($scheme = '', $authority = '', $path = '', $query = '', $fragment = '') + { + $return = ''; + if ($scheme !== '') + { + $return .= "$scheme:"; + } + if ($authority !== '') + { + $return .= "//$authority"; + } + if ($path !== '') + { + $return .= $path; + } + if ($query !== '') + { + $return .= "?$query"; + } + if ($fragment !== '') + { + $return .= "#$fragment"; + } + return $return; + } + + function normalize_url($url) + { + $url = preg_replace_callback('/%([0-9A-Fa-f]{2})/', array('SimplePie_Misc', 'percent_encoding_normalization'), $url); + $url = SimplePie_Misc::parse_url($url); + $url['scheme'] = strtolower($url['scheme']); + if ($url['authority'] !== '') + { + $url['authority'] = strtolower($url['authority']); + $url['path'] = SimplePie_Misc::remove_dot_segments($url['path']); + } + return SimplePie_Misc::compress_parse_url($url['scheme'], $url['authority'], $url['path'], $url['query'], $url['fragment']); + } + + function percent_encoding_normalization($match) + { + $integer = hexdec($match[1]); + if ($integer >= 0x41 && $integer <= 0x5A || $integer >= 0x61 && $integer <= 0x7A || $integer >= 0x30 && $integer <= 0x39 || $integer == 0x2D || $integer == 0x2E || $integer == 0x5F || $integer == 0x7E) + { + return chr($integer); + } + else + { + return strtoupper($match[0]); + } + } + + /** + * Remove bad UTF-8 bytes + * + * PCRE Pattern to locate bad bytes in a UTF-8 string comes from W3C + * FAQ: Multilingual Forms (modified to include full ASCII range) + * + * @author Geoffrey Sneddon + * @see http://www.w3.org/International/questions/qa-forms-utf-8 + * @param string $str String to remove bad UTF-8 bytes from + * @return string UTF-8 string + */ + function utf8_bad_replace($str) + { + if (function_exists('iconv') && ($return = @iconv('UTF-8', 'UTF-8//IGNORE', $str))) + { + return $return; + } + elseif (function_exists('mb_convert_encoding') && ($return = @mb_convert_encoding($str, 'UTF-8', 'UTF-8'))) + { + return $return; + } + elseif (preg_match_all('/(?:[\x00-\x7F]|[\xC2-\xDF][\x80-\xBF]|\xE0[\xA0-\xBF][\x80-\xBF]|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}|\xED[\x80-\x9F][\x80-\xBF]|\xF0[\x90-\xBF][\x80-\xBF]{2}|[\xF1-\xF3][\x80-\xBF]{3}|\xF4[\x80-\x8F][\x80-\xBF]{2})+/', $str, $matches)) + { + return implode("\xEF\xBF\xBD", $matches[0]); + } + elseif ($str !== '') + { + return "\xEF\xBF\xBD"; + } + else + { + return ''; + } + } + + /** + * Converts a Windows-1252 encoded string to a UTF-8 encoded string + * + * @static + * @access public + * @param string $string Windows-1252 encoded string + * @return string UTF-8 encoded string + */ + function windows_1252_to_utf8($string) + { + static $convert_table = array("\x80" => "\xE2\x82\xAC", "\x81" => "\xEF\xBF\xBD", "\x82" => "\xE2\x80\x9A", "\x83" => "\xC6\x92", "\x84" => "\xE2\x80\x9E", "\x85" => "\xE2\x80\xA6", "\x86" => "\xE2\x80\xA0", "\x87" => "\xE2\x80\xA1", "\x88" => "\xCB\x86", "\x89" => "\xE2\x80\xB0", "\x8A" => "\xC5\xA0", "\x8B" => "\xE2\x80\xB9", "\x8C" => "\xC5\x92", "\x8D" => "\xEF\xBF\xBD", "\x8E" => "\xC5\xBD", "\x8F" => "\xEF\xBF\xBD", "\x90" => "\xEF\xBF\xBD", "\x91" => "\xE2\x80\x98", "\x92" => "\xE2\x80\x99", "\x93" => "\xE2\x80\x9C", "\x94" => "\xE2\x80\x9D", "\x95" => "\xE2\x80\xA2", "\x96" => "\xE2\x80\x93", "\x97" => "\xE2\x80\x94", "\x98" => "\xCB\x9C", "\x99" => "\xE2\x84\xA2", "\x9A" => "\xC5\xA1", "\x9B" => "\xE2\x80\xBA", "\x9C" => "\xC5\x93", "\x9D" => "\xEF\xBF\xBD", "\x9E" => "\xC5\xBE", "\x9F" => "\xC5\xB8", "\xA0" => "\xC2\xA0", "\xA1" => "\xC2\xA1", "\xA2" => "\xC2\xA2", "\xA3" => "\xC2\xA3", "\xA4" => "\xC2\xA4", "\xA5" => "\xC2\xA5", "\xA6" => "\xC2\xA6", "\xA7" => "\xC2\xA7", "\xA8" => "\xC2\xA8", "\xA9" => "\xC2\xA9", "\xAA" => "\xC2\xAA", "\xAB" => "\xC2\xAB", "\xAC" => "\xC2\xAC", "\xAD" => "\xC2\xAD", "\xAE" => "\xC2\xAE", "\xAF" => "\xC2\xAF", "\xB0" => "\xC2\xB0", "\xB1" => "\xC2\xB1", "\xB2" => "\xC2\xB2", "\xB3" => "\xC2\xB3", "\xB4" => "\xC2\xB4", "\xB5" => "\xC2\xB5", "\xB6" => "\xC2\xB6", "\xB7" => "\xC2\xB7", "\xB8" => "\xC2\xB8", "\xB9" => "\xC2\xB9", "\xBA" => "\xC2\xBA", "\xBB" => "\xC2\xBB", "\xBC" => "\xC2\xBC", "\xBD" => "\xC2\xBD", "\xBE" => "\xC2\xBE", "\xBF" => "\xC2\xBF", "\xC0" => "\xC3\x80", "\xC1" => "\xC3\x81", "\xC2" => "\xC3\x82", "\xC3" => "\xC3\x83", "\xC4" => "\xC3\x84", "\xC5" => "\xC3\x85", "\xC6" => "\xC3\x86", "\xC7" => "\xC3\x87", "\xC8" => "\xC3\x88", "\xC9" => "\xC3\x89", "\xCA" => "\xC3\x8A", "\xCB" => "\xC3\x8B", "\xCC" => "\xC3\x8C", "\xCD" => "\xC3\x8D", "\xCE" => "\xC3\x8E", "\xCF" => "\xC3\x8F", "\xD0" => "\xC3\x90", "\xD1" => "\xC3\x91", "\xD2" => "\xC3\x92", "\xD3" => "\xC3\x93", "\xD4" => "\xC3\x94", "\xD5" => "\xC3\x95", "\xD6" => "\xC3\x96", "\xD7" => "\xC3\x97", "\xD8" => "\xC3\x98", "\xD9" => "\xC3\x99", "\xDA" => "\xC3\x9A", "\xDB" => "\xC3\x9B", "\xDC" => "\xC3\x9C", "\xDD" => "\xC3\x9D", "\xDE" => "\xC3\x9E", "\xDF" => "\xC3\x9F", "\xE0" => "\xC3\xA0", "\xE1" => "\xC3\xA1", "\xE2" => "\xC3\xA2", "\xE3" => "\xC3\xA3", "\xE4" => "\xC3\xA4", "\xE5" => "\xC3\xA5", "\xE6" => "\xC3\xA6", "\xE7" => "\xC3\xA7", "\xE8" => "\xC3\xA8", "\xE9" => "\xC3\xA9", "\xEA" => "\xC3\xAA", "\xEB" => "\xC3\xAB", "\xEC" => "\xC3\xAC", "\xED" => "\xC3\xAD", "\xEE" => "\xC3\xAE", "\xEF" => "\xC3\xAF", "\xF0" => "\xC3\xB0", "\xF1" => "\xC3\xB1", "\xF2" => "\xC3\xB2", "\xF3" => "\xC3\xB3", "\xF4" => "\xC3\xB4", "\xF5" => "\xC3\xB5", "\xF6" => "\xC3\xB6", "\xF7" => "\xC3\xB7", "\xF8" => "\xC3\xB8", "\xF9" => "\xC3\xB9", "\xFA" => "\xC3\xBA", "\xFB" => "\xC3\xBB", "\xFC" => "\xC3\xBC", "\xFD" => "\xC3\xBD", "\xFE" => "\xC3\xBE", "\xFF" => "\xC3\xBF"); + + return strtr($string, $convert_table); + } + + function change_encoding($data, $input, $output) + { + $input = SimplePie_Misc::encoding($input); + $output = SimplePie_Misc::encoding($output); + + // We fail to fail on non US-ASCII bytes + if ($input === 'US-ASCII') + { + static $non_ascii_octects = ''; + if (!$non_ascii_octects) + { + for ($i = 0x80; $i <= 0xFF; $i++) + { + $non_ascii_octects .= chr($i); + } + } + $data = substr($data, 0, strcspn($data, $non_ascii_octects)); + } + + if (function_exists('iconv') && ($return = @iconv($input, $output, $data))) + { + return $return; + } + elseif (function_exists('mb_convert_encoding') && ($return = @mb_convert_encoding($data, $output, $input))) + { + return $return; + } + elseif ($input == 'windows-1252' && $output == 'UTF-8') + { + return SimplePie_Misc::windows_1252_to_utf8($data); + } + elseif ($input == 'UTF-8' && $output == 'windows-1252') + { + return utf8_decode($data); + } + return $data; + } + + function encoding($encoding) + { + // Character sets are case-insensitive (though we'll return them in the form given in their registration) + switch (strtoupper($encoding)) + { + case 'ANSI_X3.110-1983': + case 'CSA_T500-1983': + case 'CSISO99NAPLPS': + case 'ISO-IR-99': + case 'NAPLPS': + return 'ANSI_X3.110-1983'; + + case 'ARABIC7': + case 'ASMO_449': + case 'CSISO89ASMO449': + case 'ISO-IR-89': + case 'ISO_9036': + return 'ASMO_449'; + + case 'ADOBE-STANDARD-ENCODING': + case 'CSADOBESTANDARDENCODING': + return 'Adobe-Standard-Encoding'; + + case 'ADOBE-SYMBOL-ENCODING': + case 'CSHPPSMATH': + return 'Adobe-Symbol-Encoding'; + + case 'AMI-1251': + case 'AMI1251': + case 'AMIGA-1251': + case 'AMIGA1251': + return 'Amiga-1251'; + + case 'BOCU-1': + case 'CSBOCU-1': + return 'BOCU-1'; + + case 'BRF': + case 'CSBRF': + return 'BRF'; + + case 'BS_4730': + case 'CSISO4UNITEDKINGDOM': + case 'GB': + case 'ISO-IR-4': + case 'ISO646-GB': + case 'UK': + return 'BS_4730'; + + case 'BS_VIEWDATA': + case 'CSISO47BSVIEWDATA': + case 'ISO-IR-47': + return 'BS_viewdata'; + + case 'BIG5': + case 'CSBIG5': + return 'Big5'; + + case 'BIG5-HKSCS': + return 'Big5-HKSCS'; + + case 'CESU-8': + case 'CSCESU-8': + return 'CESU-8'; + + case 'CA': + case 'CSA7-1': + case 'CSA_Z243.4-1985-1': + case 'CSISO121CANADIAN1': + case 'ISO-IR-121': + case 'ISO646-CA': + return 'CSA_Z243.4-1985-1'; + + case 'CSA7-2': + case 'CSA_Z243.4-1985-2': + case 'CSISO122CANADIAN2': + case 'ISO-IR-122': + case 'ISO646-CA2': + return 'CSA_Z243.4-1985-2'; + + case 'CSA_Z243.4-1985-GR': + case 'CSISO123CSAZ24341985GR': + case 'ISO-IR-123': + return 'CSA_Z243.4-1985-gr'; + + case 'CSISO139CSN369103': + case 'CSN_369103': + case 'ISO-IR-139': + return 'CSN_369103'; + + case 'CSDECMCS': + case 'DEC': + case 'DEC-MCS': + return 'DEC-MCS'; + + case 'CSISO21GERMAN': + case 'DE': + case 'DIN_66003': + case 'ISO-IR-21': + case 'ISO646-DE': + return 'DIN_66003'; + + case 'CSISO646DANISH': + case 'DK': + case 'DS2089': + case 'DS_2089': + case 'ISO646-DK': + return 'DS_2089'; + + case 'CSIBMEBCDICATDE': + case 'EBCDIC-AT-DE': + return 'EBCDIC-AT-DE'; + + case 'CSEBCDICATDEA': + case 'EBCDIC-AT-DE-A': + return 'EBCDIC-AT-DE-A'; + + case 'CSEBCDICCAFR': + case 'EBCDIC-CA-FR': + return 'EBCDIC-CA-FR'; + + case 'CSEBCDICDKNO': + case 'EBCDIC-DK-NO': + return 'EBCDIC-DK-NO'; + + case 'CSEBCDICDKNOA': + case 'EBCDIC-DK-NO-A': + return 'EBCDIC-DK-NO-A'; + + case 'CSEBCDICES': + case 'EBCDIC-ES': + return 'EBCDIC-ES'; + + case 'CSEBCDICESA': + case 'EBCDIC-ES-A': + return 'EBCDIC-ES-A'; + + case 'CSEBCDICESS': + case 'EBCDIC-ES-S': + return 'EBCDIC-ES-S'; + + case 'CSEBCDICFISE': + case 'EBCDIC-FI-SE': + return 'EBCDIC-FI-SE'; + + case 'CSEBCDICFISEA': + case 'EBCDIC-FI-SE-A': + return 'EBCDIC-FI-SE-A'; + + case 'CSEBCDICFR': + case 'EBCDIC-FR': + return 'EBCDIC-FR'; + + case 'CSEBCDICIT': + case 'EBCDIC-IT': + return 'EBCDIC-IT'; + + case 'CSEBCDICPT': + case 'EBCDIC-PT': + return 'EBCDIC-PT'; + + case 'CSEBCDICUK': + case 'EBCDIC-UK': + return 'EBCDIC-UK'; + + case 'CSEBCDICUS': + case 'EBCDIC-US': + return 'EBCDIC-US'; + + case 'CSISO111ECMACYRILLIC': + case 'ECMA-CYRILLIC': + case 'ISO-IR-111': + case 'KOI8-E': + return 'ECMA-cyrillic'; + + case 'CSISO17SPANISH': + case 'ES': + case 'ISO-IR-17': + case 'ISO646-ES': + return 'ES'; + + case 'CSISO85SPANISH2': + case 'ES2': + case 'ISO-IR-85': + case 'ISO646-ES2': + return 'ES2'; + + case 'CSEUCPKDFMTJAPANESE': + case 'EUC-JP': + case 'EXTENDED_UNIX_CODE_PACKED_FORMAT_FOR_JAPANESE': + return 'EUC-JP'; + + case 'CSEUCKR': + case 'EUC-KR': + return 'EUC-KR'; + + case 'CSEUCFIXWIDJAPANESE': + case 'EXTENDED_UNIX_CODE_FIXED_WIDTH_FOR_JAPANESE': + return 'Extended_UNIX_Code_Fixed_Width_for_Japanese'; + + case 'GB18030': + return 'GB18030'; + + case 'CSGB2312': + case 'GB2312': + return 'GB2312'; + + case 'CP936': + case 'GBK': + case 'MS936': + case 'WINDOWS-936': + return 'GBK'; + + case 'CN': + case 'CSISO57GB1988': + case 'GB_1988-80': + case 'ISO-IR-57': + case 'ISO646-CN': + return 'GB_1988-80'; + + case 'CHINESE': + case 'CSISO58GB231280': + case 'GB_2312-80': + case 'ISO-IR-58': + return 'GB_2312-80'; + + case 'CSISO153GOST1976874': + case 'GOST_19768-74': + case 'ISO-IR-153': + case 'ST_SEV_358-88': + return 'GOST_19768-74'; + + case 'CSHPDESKTOP': + case 'HP-DESKTOP': + return 'HP-DeskTop'; + + case 'CSHPLEGAL': + case 'HP-LEGAL': + return 'HP-Legal'; + + case 'CSHPMATH8': + case 'HP-MATH8': + return 'HP-Math8'; + + case 'CSHPPIFONT': + case 'HP-PI-FONT': + return 'HP-Pi-font'; + + case 'HZ-GB-2312': + return 'HZ-GB-2312'; + + case 'CSIBMSYMBOLS': + case 'IBM-SYMBOLS': + return 'IBM-Symbols'; + + case 'CSIBMTHAI': + case 'IBM-THAI': + return 'IBM-Thai'; + + case 'CCSID00858': + case 'CP00858': + case 'IBM00858': + case 'PC-MULTILINGUAL-850+EURO': + return 'IBM00858'; + + case 'CCSID00924': + case 'CP00924': + case 'EBCDIC-LATIN9--EURO': + case 'IBM00924': + return 'IBM00924'; + + case 'CCSID01140': + case 'CP01140': + case 'EBCDIC-US-37+EURO': + case 'IBM01140': + return 'IBM01140'; + + case 'CCSID01141': + case 'CP01141': + case 'EBCDIC-DE-273+EURO': + case 'IBM01141': + return 'IBM01141'; + + case 'CCSID01142': + case 'CP01142': + case 'EBCDIC-DK-277+EURO': + case 'EBCDIC-NO-277+EURO': + case 'IBM01142': + return 'IBM01142'; + + case 'CCSID01143': + case 'CP01143': + case 'EBCDIC-FI-278+EURO': + case 'EBCDIC-SE-278+EURO': + case 'IBM01143': + return 'IBM01143'; + + case 'CCSID01144': + case 'CP01144': + case 'EBCDIC-IT-280+EURO': + case 'IBM01144': + return 'IBM01144'; + + case 'CCSID01145': + case 'CP01145': + case 'EBCDIC-ES-284+EURO': + case 'IBM01145': + return 'IBM01145'; + + case 'CCSID01146': + case 'CP01146': + case 'EBCDIC-GB-285+EURO': + case 'IBM01146': + return 'IBM01146'; + + case 'CCSID01147': + case 'CP01147': + case 'EBCDIC-FR-297+EURO': + case 'IBM01147': + return 'IBM01147'; + + case 'CCSID01148': + case 'CP01148': + case 'EBCDIC-INTERNATIONAL-500+EURO': + case 'IBM01148': + return 'IBM01148'; + + case 'CCSID01149': + case 'CP01149': + case 'EBCDIC-IS-871+EURO': + case 'IBM01149': + return 'IBM01149'; + + case 'CP037': + case 'CSIBM037': + case 'EBCDIC-CP-CA': + case 'EBCDIC-CP-NL': + case 'EBCDIC-CP-US': + case 'EBCDIC-CP-WT': + case 'IBM037': + return 'IBM037'; + + case 'CP038': + case 'CSIBM038': + case 'EBCDIC-INT': + case 'IBM038': + return 'IBM038'; + + case 'CP1026': + case 'CSIBM1026': + case 'IBM1026': + return 'IBM1026'; + + case 'IBM-1047': + case 'IBM1047': + return 'IBM1047'; + + case 'CP273': + case 'CSIBM273': + case 'IBM273': + return 'IBM273'; + + case 'CP274': + case 'CSIBM274': + case 'EBCDIC-BE': + case 'IBM274': + return 'IBM274'; + + case 'CP275': + case 'CSIBM275': + case 'EBCDIC-BR': + case 'IBM275': + return 'IBM275'; + + case 'CSIBM277': + case 'EBCDIC-CP-DK': + case 'EBCDIC-CP-NO': + case 'IBM277': + return 'IBM277'; + + case 'CP278': + case 'CSIBM278': + case 'EBCDIC-CP-FI': + case 'EBCDIC-CP-SE': + case 'IBM278': + return 'IBM278'; + + case 'CP280': + case 'CSIBM280': + case 'EBCDIC-CP-IT': + case 'IBM280': + return 'IBM280'; + + case 'CP281': + case 'CSIBM281': + case 'EBCDIC-JP-E': + case 'IBM281': + return 'IBM281'; + + case 'CP284': + case 'CSIBM284': + case 'EBCDIC-CP-ES': + case 'IBM284': + return 'IBM284'; + + case 'CP285': + case 'CSIBM285': + case 'EBCDIC-CP-GB': + case 'IBM285': + return 'IBM285'; + + case 'CP290': + case 'CSIBM290': + case 'EBCDIC-JP-KANA': + case 'IBM290': + return 'IBM290'; + + case 'CP297': + case 'CSIBM297': + case 'EBCDIC-CP-FR': + case 'IBM297': + return 'IBM297'; + + case 'CP420': + case 'CSIBM420': + case 'EBCDIC-CP-AR1': + case 'IBM420': + return 'IBM420'; + + case 'CP423': + case 'CSIBM423': + case 'EBCDIC-CP-GR': + case 'IBM423': + return 'IBM423'; + + case 'CP424': + case 'CSIBM424': + case 'EBCDIC-CP-HE': + case 'IBM424': + return 'IBM424'; + + case '437': + case 'CP437': + case 'CSPC8CODEPAGE437': + case 'IBM437': + return 'IBM437'; + + case 'CP500': + case 'CSIBM500': + case 'EBCDIC-CP-BE': + case 'EBCDIC-CP-CH': + case 'IBM500': + return 'IBM500'; + + case 'CP775': + case 'CSPC775BALTIC': + case 'IBM775': + return 'IBM775'; + + case '850': + case 'CP850': + case 'CSPC850MULTILINGUAL': + case 'IBM850': + return 'IBM850'; + + case '851': + case 'CP851': + case 'CSIBM851': + case 'IBM851': + return 'IBM851'; + + case '852': + case 'CP852': + case 'CSPCP852': + case 'IBM852': + return 'IBM852'; + + case '855': + case 'CP855': + case 'CSIBM855': + case 'IBM855': + return 'IBM855'; + + case '857': + case 'CP857': + case 'CSIBM857': + case 'IBM857': + return 'IBM857'; + + case '860': + case 'CP860': + case 'CSIBM860': + case 'IBM860': + return 'IBM860'; + + case '861': + case 'CP-IS': + case 'CP861': + case 'CSIBM861': + case 'IBM861': + return 'IBM861'; + + case '862': + case 'CP862': + case 'CSPC862LATINHEBREW': + case 'IBM862': + return 'IBM862'; + + case '863': + case 'CP863': + case 'CSIBM863': + case 'IBM863': + return 'IBM863'; + + case 'CP864': + case 'CSIBM864': + case 'IBM864': + return 'IBM864'; + + case '865': + case 'CP865': + case 'CSIBM865': + case 'IBM865': + return 'IBM865'; + + case '866': + case 'CP866': + case 'CSIBM866': + case 'IBM866': + return 'IBM866'; + + case 'CP-AR': + case 'CP868': + case 'CSIBM868': + case 'IBM868': + return 'IBM868'; + + case '869': + case 'CP-GR': + case 'CP869': + case 'CSIBM869': + case 'IBM869': + return 'IBM869'; + + case 'CP870': + case 'CSIBM870': + case 'EBCDIC-CP-ROECE': + case 'EBCDIC-CP-YU': + case 'IBM870': + return 'IBM870'; + + case 'CP871': + case 'CSIBM871': + case 'EBCDIC-CP-IS': + case 'IBM871': + return 'IBM871'; + + case 'CP880': + case 'CSIBM880': + case 'EBCDIC-CYRILLIC': + case 'IBM880': + return 'IBM880'; + + case 'CP891': + case 'CSIBM891': + case 'IBM891': + return 'IBM891'; + + case 'CP903': + case 'CSIBM903': + case 'IBM903': + return 'IBM903'; + + case '904': + case 'CP904': + case 'CSIBBM904': + case 'IBM904': + return 'IBM904'; + + case 'CP905': + case 'CSIBM905': + case 'EBCDIC-CP-TR': + case 'IBM905': + return 'IBM905'; + + case 'CP918': + case 'CSIBM918': + case 'EBCDIC-CP-AR2': + case 'IBM918': + return 'IBM918'; + + case 'CSISO143IECP271': + case 'IEC_P27-1': + case 'ISO-IR-143': + return 'IEC_P27-1'; + + case 'CSISO49INIS': + case 'INIS': + case 'ISO-IR-49': + return 'INIS'; + + case 'CSISO50INIS8': + case 'INIS-8': + case 'ISO-IR-50': + return 'INIS-8'; + + case 'CSISO51INISCYRILLIC': + case 'INIS-CYRILLIC': + case 'ISO-IR-51': + return 'INIS-cyrillic'; + + case 'CSINVARIANT': + case 'INVARIANT': + return 'INVARIANT'; + + case 'ISO-10646-J-1': + return 'ISO-10646-J-1'; + + case 'CSUNICODE': + case 'ISO-10646-UCS-2': + return 'ISO-10646-UCS-2'; + + case 'CSUCS4': + case 'ISO-10646-UCS-4': + return 'ISO-10646-UCS-4'; + + case 'CSUNICODEASCII': + case 'ISO-10646-UCS-BASIC': + return 'ISO-10646-UCS-Basic'; + + case 'CSISO10646UTF1': + case 'ISO-10646-UTF-1': + return 'ISO-10646-UTF-1'; + + case 'CSUNICODELATIN1': + case 'ISO-10646': + case 'ISO-10646-UNICODE-LATIN1': + return 'ISO-10646-Unicode-Latin1'; + + case 'CSISO115481': + case 'ISO-11548-1': + case 'ISO_11548-1': + case 'ISO_TR_11548-1': + return 'ISO-11548-1'; + + case 'ISO-2022-CN': + return 'ISO-2022-CN'; + + case 'ISO-2022-CN-EXT': + return 'ISO-2022-CN-EXT'; + + case 'CSISO2022JP': + case 'ISO-2022-JP': + return 'ISO-2022-JP'; + + case 'CSISO2022JP2': + case 'ISO-2022-JP-2': + return 'ISO-2022-JP-2'; + + case 'CSISO2022KR': + case 'ISO-2022-KR': + return 'ISO-2022-KR'; + + case 'CSWINDOWS30LATIN1': + case 'ISO-8859-1-WINDOWS-3.0-LATIN-1': + return 'ISO-8859-1-Windows-3.0-Latin-1'; + + case 'CSWINDOWS31LATIN1': + case 'ISO-8859-1-WINDOWS-3.1-LATIN-1': + return 'ISO-8859-1-Windows-3.1-Latin-1'; + + case 'CSISOLATIN6': + case 'ISO-8859-10': + case 'ISO-IR-157': + case 'ISO_8859-10:1992': + case 'L6': + case 'LATIN6': + return 'ISO-8859-10'; + + case 'ISO-8859-13': + return 'ISO-8859-13'; + + case 'ISO-8859-14': + case 'ISO-CELTIC': + case 'ISO-IR-199': + case 'ISO_8859-14': + case 'ISO_8859-14:1998': + case 'L8': + case 'LATIN8': + return 'ISO-8859-14'; + + case 'ISO-8859-15': + case 'ISO_8859-15': + case 'LATIN-9': + return 'ISO-8859-15'; + + case 'ISO-8859-16': + case 'ISO-IR-226': + case 'ISO_8859-16': + case 'ISO_8859-16:2001': + case 'L10': + case 'LATIN10': + return 'ISO-8859-16'; + + case 'CSISOLATIN2': + case 'ISO-8859-2': + case 'ISO-IR-101': + case 'ISO_8859-2': + case 'ISO_8859-2:1987': + case 'L2': + case 'LATIN2': + return 'ISO-8859-2'; + + case 'CSWINDOWS31LATIN2': + case 'ISO-8859-2-WINDOWS-LATIN-2': + return 'ISO-8859-2-Windows-Latin-2'; + + case 'CSISOLATIN3': + case 'ISO-8859-3': + case 'ISO-IR-109': + case 'ISO_8859-3': + case 'ISO_8859-3:1988': + case 'L3': + case 'LATIN3': + return 'ISO-8859-3'; + + case 'CSISOLATIN4': + case 'ISO-8859-4': + case 'ISO-IR-110': + case 'ISO_8859-4': + case 'ISO_8859-4:1988': + case 'L4': + case 'LATIN4': + return 'ISO-8859-4'; + + case 'CSISOLATINCYRILLIC': + case 'CYRILLIC': + case 'ISO-8859-5': + case 'ISO-IR-144': + case 'ISO_8859-5': + case 'ISO_8859-5:1988': + return 'ISO-8859-5'; + + case 'ARABIC': + case 'ASMO-708': + case 'CSISOLATINARABIC': + case 'ECMA-114': + case 'ISO-8859-6': + case 'ISO-IR-127': + case 'ISO_8859-6': + case 'ISO_8859-6:1987': + return 'ISO-8859-6'; + + case 'CSISO88596E': + case 'ISO-8859-6-E': + case 'ISO_8859-6-E': + return 'ISO-8859-6-E'; + + case 'CSISO88596I': + case 'ISO-8859-6-I': + case 'ISO_8859-6-I': + return 'ISO-8859-6-I'; + + case 'CSISOLATINGREEK': + case 'ECMA-118': + case 'ELOT_928': + case 'GREEK': + case 'GREEK8': + case 'ISO-8859-7': + case 'ISO-IR-126': + case 'ISO_8859-7': + case 'ISO_8859-7:1987': + return 'ISO-8859-7'; + + case 'CSISOLATINHEBREW': + case 'HEBREW': + case 'ISO-8859-8': + case 'ISO-IR-138': + case 'ISO_8859-8': + case 'ISO_8859-8:1988': + return 'ISO-8859-8'; + + case 'CSISO88598E': + case 'ISO-8859-8-E': + case 'ISO_8859-8-E': + return 'ISO-8859-8-E'; + + case 'CSISO88598I': + case 'ISO-8859-8-I': + case 'ISO_8859-8-I': + return 'ISO-8859-8-I'; + + case 'CSISOLATIN5': + case 'ISO-8859-9': + case 'ISO-IR-148': + case 'ISO_8859-9': + case 'ISO_8859-9:1989': + case 'L5': + case 'LATIN5': + return 'ISO-8859-9'; + + case 'CSWINDOWS31LATIN5': + case 'ISO-8859-9-WINDOWS-LATIN-5': + return 'ISO-8859-9-Windows-Latin-5'; + + case 'CSUNICODEIBM1261': + case 'ISO-UNICODE-IBM-1261': + return 'ISO-Unicode-IBM-1261'; + + case 'CSUNICODEIBM1264': + case 'ISO-UNICODE-IBM-1264': + return 'ISO-Unicode-IBM-1264'; + + case 'CSUNICODEIBM1265': + case 'ISO-UNICODE-IBM-1265': + return 'ISO-Unicode-IBM-1265'; + + case 'CSUNICODEIBM1268': + case 'ISO-UNICODE-IBM-1268': + return 'ISO-Unicode-IBM-1268'; + + case 'CSUNICODEIBM1276': + case 'ISO-UNICODE-IBM-1276': + return 'ISO-Unicode-IBM-1276'; + + case 'CSISO10367BOX': + case 'ISO-IR-155': + case 'ISO_10367-BOX': + return 'ISO_10367-box'; + + case 'CSISO2033': + case 'E13B': + case 'ISO-IR-98': + case 'ISO_2033-1983': + return 'ISO_2033-1983'; + + case 'CSISO5427CYRILLIC': + case 'ISO-IR-37': + case 'ISO_5427': + return 'ISO_5427'; + + case 'ISO-IR-54': + case 'ISO5427CYRILLIC1981': + case 'ISO_5427:1981': + return 'ISO_5427:1981'; + + case 'CSISO5428GREEK': + case 'ISO-IR-55': + case 'ISO_5428:1980': + return 'ISO_5428:1980'; + + case 'CSISO646BASIC1983': + case 'ISO_646.BASIC:1983': + case 'REF': + return 'ISO_646.basic:1983'; + + case 'CSISO2INTLREFVERSION': + case 'IRV': + case 'ISO-IR-2': + case 'ISO_646.IRV:1983': + return 'ISO_646.irv:1983'; + + case 'CSISO6937ADD': + case 'ISO-IR-152': + case 'ISO_6937-2-25': + return 'ISO_6937-2-25'; + + case 'CSISOTEXTCOMM': + case 'ISO-IR-142': + case 'ISO_6937-2-ADD': + return 'ISO_6937-2-add'; + + case 'CSISO8859SUPP': + case 'ISO-IR-154': + case 'ISO_8859-SUPP': + case 'LATIN1-2-5': + return 'ISO_8859-supp'; + + case 'CSISO15ITALIAN': + case 'ISO-IR-15': + case 'ISO646-IT': + case 'IT': + return 'IT'; + + case 'CSISO13JISC6220JP': + case 'ISO-IR-13': + case 'JIS_C6220-1969': + case 'JIS_C6220-1969-JP': + case 'KATAKANA': + case 'X0201-7': + return 'JIS_C6220-1969-jp'; + + case 'CSISO14JISC6220RO': + case 'ISO-IR-14': + case 'ISO646-JP': + case 'JIS_C6220-1969-RO': + case 'JP': + return 'JIS_C6220-1969-ro'; + + case 'CSISO42JISC62261978': + case 'ISO-IR-42': + case 'JIS_C6226-1978': + return 'JIS_C6226-1978'; + + case 'CSISO87JISX0208': + case 'ISO-IR-87': + case 'JIS_C6226-1983': + case 'JIS_X0208-1983': + case 'X0208': + return 'JIS_C6226-1983'; + + case 'CSISO91JISC62291984A': + case 'ISO-IR-91': + case 'JIS_C6229-1984-A': + case 'JP-OCR-A': + return 'JIS_C6229-1984-a'; + + case 'CSISO92JISC62991984B': + case 'ISO-IR-92': + case 'ISO646-JP-OCR-B': + case 'JIS_C6229-1984-B': + case 'JP-OCR-B': + return 'JIS_C6229-1984-b'; + + case 'CSISO93JIS62291984BADD': + case 'ISO-IR-93': + case 'JIS_C6229-1984-B-ADD': + case 'JP-OCR-B-ADD': + return 'JIS_C6229-1984-b-add'; + + case 'CSISO94JIS62291984HAND': + case 'ISO-IR-94': + case 'JIS_C6229-1984-HAND': + case 'JP-OCR-HAND': + return 'JIS_C6229-1984-hand'; + + case 'CSISO95JIS62291984HANDADD': + case 'ISO-IR-95': + case 'JIS_C6229-1984-HAND-ADD': + case 'JP-OCR-HAND-ADD': + return 'JIS_C6229-1984-hand-add'; + + case 'CSISO96JISC62291984KANA': + case 'ISO-IR-96': + case 'JIS_C6229-1984-KANA': + return 'JIS_C6229-1984-kana'; + + case 'CSJISENCODING': + case 'JIS_ENCODING': + return 'JIS_Encoding'; + + case 'CSHALFWIDTHKATAKANA': + case 'JIS_X0201': + case 'X0201': + return 'JIS_X0201'; + + case 'CSISO159JISX02121990': + case 'ISO-IR-159': + case 'JIS_X0212-1990': + case 'X0212': + return 'JIS_X0212-1990'; + + case 'CSISO141JUSIB1002': + case 'ISO-IR-141': + case 'ISO646-YU': + case 'JS': + case 'JUS_I.B1.002': + case 'YU': + return 'JUS_I.B1.002'; + + case 'CSISO147MACEDONIAN': + case 'ISO-IR-147': + case 'JUS_I.B1.003-MAC': + case 'MACEDONIAN': + return 'JUS_I.B1.003-mac'; + + case 'CSISO146SERBIAN': + case 'ISO-IR-146': + case 'JUS_I.B1.003-SERB': + case 'SERBIAN': + return 'JUS_I.B1.003-serb'; + + case 'KOI7-SWITCHED': + return 'KOI7-switched'; + + case 'CSKOI8R': + case 'KOI8-R': + return 'KOI8-R'; + + case 'KOI8-U': + return 'KOI8-U'; + + case 'CSKSC5636': + case 'ISO646-KR': + case 'KSC5636': + return 'KSC5636'; + + case 'CSKSC56011987': + case 'ISO-IR-149': + case 'KOREAN': + case 'KSC_5601': + case 'KS_C_5601-1987': + case 'KS_C_5601-1989': + return 'KS_C_5601-1987'; + + case 'CSKZ1048': + case 'KZ-1048': + case 'RK1048': + case 'STRK1048-2002': + return 'KZ-1048'; + + case 'CSISO27LATINGREEK1': + case 'ISO-IR-27': + case 'LATIN-GREEK-1': + return 'Latin-greek-1'; + + case 'CSMNEM': + case 'MNEM': + return 'MNEM'; + + case 'CSMNEMONIC': + case 'MNEMONIC': + return 'MNEMONIC'; + + case 'CSISO86HUNGARIAN': + case 'HU': + case 'ISO-IR-86': + case 'ISO646-HU': + case 'MSZ_7795.3': + return 'MSZ_7795.3'; + + case 'CSMICROSOFTPUBLISHING': + case 'MICROSOFT-PUBLISHING': + return 'Microsoft-Publishing'; + + case 'CSNATSDANO': + case 'ISO-IR-9-1': + case 'NATS-DANO': + return 'NATS-DANO'; + + case 'CSNATSDANOADD': + case 'ISO-IR-9-2': + case 'NATS-DANO-ADD': + return 'NATS-DANO-ADD'; + + case 'CSNATSSEFI': + case 'ISO-IR-8-1': + case 'NATS-SEFI': + return 'NATS-SEFI'; + + case 'CSNATSSEFIADD': + case 'ISO-IR-8-2': + case 'NATS-SEFI-ADD': + return 'NATS-SEFI-ADD'; + + case 'CSISO151CUBA': + case 'CUBA': + case 'ISO-IR-151': + case 'ISO646-CU': + case 'NC_NC00-10:81': + return 'NC_NC00-10:81'; + + case 'CSISO69FRENCH': + case 'FR': + case 'ISO-IR-69': + case 'ISO646-FR': + case 'NF_Z_62-010': + return 'NF_Z_62-010'; + + case 'CSISO25FRENCH': + case 'ISO-IR-25': + case 'ISO646-FR1': + case 'NF_Z_62-010_(1973)': + return 'NF_Z_62-010_(1973)'; + + case 'CSISO60DANISHNORWEGIAN': + case 'CSISO60NORWEGIAN1': + case 'ISO-IR-60': + case 'ISO646-NO': + case 'NO': + case 'NS_4551-1': + return 'NS_4551-1'; + + case 'CSISO61NORWEGIAN2': + case 'ISO-IR-61': + case 'ISO646-NO2': + case 'NO2': + case 'NS_4551-2': + return 'NS_4551-2'; + + case 'OSD_EBCDIC_DF03_IRV': + return 'OSD_EBCDIC_DF03_IRV'; + + case 'OSD_EBCDIC_DF04_1': + return 'OSD_EBCDIC_DF04_1'; + + case 'OSD_EBCDIC_DF04_15': + return 'OSD_EBCDIC_DF04_15'; + + case 'CSPC8DANISHNORWEGIAN': + case 'PC8-DANISH-NORWEGIAN': + return 'PC8-Danish-Norwegian'; + + case 'CSPC8TURKISH': + case 'PC8-TURKISH': + return 'PC8-Turkish'; + + case 'CSISO16PORTUGUESE': + case 'ISO-IR-16': + case 'ISO646-PT': + case 'PT': + return 'PT'; + + case 'CSISO84PORTUGUESE2': + case 'ISO-IR-84': + case 'ISO646-PT2': + case 'PT2': + return 'PT2'; + + case 'CP154': + case 'CSPTCP154': + case 'CYRILLIC-ASIAN': + case 'PT154': + case 'PTCP154': + return 'PTCP154'; + + case 'SCSU': + return 'SCSU'; + + case 'CSISO10SWEDISH': + case 'FI': + case 'ISO-IR-10': + case 'ISO646-FI': + case 'ISO646-SE': + case 'SE': + case 'SEN_850200_B': + return 'SEN_850200_B'; + + case 'CSISO11SWEDISHFORNAMES': + case 'ISO-IR-11': + case 'ISO646-SE2': + case 'SE2': + case 'SEN_850200_C': + return 'SEN_850200_C'; + + case 'CSSHIFTJIS': + case 'MS_KANJI': + case 'SHIFT_JIS': + return 'Shift_JIS'; + + case 'CSISO128T101G2': + case 'ISO-IR-128': + case 'T.101-G2': + return 'T.101-G2'; + + case 'CSISO102T617BIT': + case 'ISO-IR-102': + case 'T.61-7BIT': + return 'T.61-7bit'; + + case 'CSISO103T618BIT': + case 'ISO-IR-103': + case 'T.61': + case 'T.61-8BIT': + return 'T.61-8bit'; + + case 'CSTSCII': + case 'TSCII': + return 'TSCII'; + + case 'CSUNICODE11': + case 'UNICODE-1-1': + return 'UNICODE-1-1'; + + case 'CSUNICODE11UTF7': + case 'UNICODE-1-1-UTF-7': + return 'UNICODE-1-1-UTF-7'; + + case 'CSUNKNOWN8BIT': + case 'UNKNOWN-8BIT': + return 'UNKNOWN-8BIT'; + + case 'ANSI': + case 'ANSI_X3.4-1968': + case 'ANSI_X3.4-1986': + case 'ASCII': + case 'CP367': + case 'CSASCII': + case 'IBM367': + case 'ISO-IR-6': + case 'ISO646-US': + case 'ISO_646.IRV:1991': + case 'US': + case 'US-ASCII': + return 'US-ASCII'; + + case 'UTF-16': + return 'UTF-16'; + + case 'UTF-16BE': + return 'UTF-16BE'; + + case 'UTF-16LE': + return 'UTF-16LE'; + + case 'UTF-32': + return 'UTF-32'; + + case 'UTF-32BE': + return 'UTF-32BE'; + + case 'UTF-32LE': + return 'UTF-32LE'; + + case 'UTF-7': + return 'UTF-7'; + + case 'UTF-8': + return 'UTF-8'; + + case 'CSVIQR': + case 'VIQR': + return 'VIQR'; + + case 'CSVISCII': + case 'VISCII': + return 'VISCII'; + + case 'CSVENTURAINTERNATIONAL': + case 'VENTURA-INTERNATIONAL': + return 'Ventura-International'; + + case 'CSVENTURAMATH': + case 'VENTURA-MATH': + return 'Ventura-Math'; + + case 'CSVENTURAUS': + case 'VENTURA-US': + return 'Ventura-US'; + + case 'CSWINDOWS31J': + case 'WINDOWS-31J': + return 'Windows-31J'; + + case 'CSDKUS': + case 'DK-US': + return 'dk-us'; + + case 'CSISO150': + case 'CSISO150GREEKCCITT': + case 'GREEK-CCITT': + case 'ISO-IR-150': + return 'greek-ccitt'; + + case 'CSISO88GREEK7': + case 'GREEK7': + case 'ISO-IR-88': + return 'greek7'; + + case 'CSISO18GREEK7OLD': + case 'GREEK7-OLD': + case 'ISO-IR-18': + return 'greek7-old'; + + case 'CSHPROMAN8': + case 'HP-ROMAN8': + case 'R8': + case 'ROMAN8': + return 'hp-roman8'; + + case 'CSISO90': + case 'ISO-IR-90': + return 'iso-ir-90'; + + case 'CSISO19LATINGREEK': + case 'ISO-IR-19': + case 'LATIN-GREEK': + return 'latin-greek'; + + case 'CSISO158LAP': + case 'ISO-IR-158': + case 'LAP': + case 'LATIN-LAP': + return 'latin-lap'; + + case 'CSMACINTOSH': + case 'MAC': + case 'MACINTOSH': + return 'macintosh'; + + case 'CSUSDK': + case 'US-DK': + return 'us-dk'; + + case 'CSISO70VIDEOTEXSUPP1': + case 'ISO-IR-70': + case 'VIDEOTEX-SUPPL': + return 'videotex-suppl'; + + case 'WINDOWS-1250': + return 'windows-1250'; + + case 'WINDOWS-1251': + return 'windows-1251'; + + case 'CP819': + case 'CSISOLATIN1': + case 'IBM819': + case 'ISO-8859-1': + case 'ISO-IR-100': + case 'ISO_8859-1': + case 'ISO_8859-1:1987': + case 'L1': + case 'LATIN1': + case 'WINDOWS-1252': + return 'windows-1252'; + + case 'WINDOWS-1253': + return 'windows-1253'; + + case 'WINDOWS-1254': + return 'windows-1254'; + + case 'WINDOWS-1255': + return 'windows-1255'; + + case 'WINDOWS-1256': + return 'windows-1256'; + + case 'WINDOWS-1257': + return 'windows-1257'; + + case 'WINDOWS-1258': + return 'windows-1258'; + + default: + return $encoding; + } + } + + function get_curl_version() + { + if (is_array($curl = curl_version())) + { + $curl = $curl['version']; + } + elseif (substr($curl, 0, 5) == 'curl/') + { + $curl = substr($curl, 5, strcspn($curl, "\x09\x0A\x0B\x0C\x0D", 5)); + } + elseif (substr($curl, 0, 8) == 'libcurl/') + { + $curl = substr($curl, 8, strcspn($curl, "\x09\x0A\x0B\x0C\x0D", 8)); + } + else + { + $curl = 0; + } + return $curl; + } + + function is_subclass_of($class1, $class2) + { + if (func_num_args() != 2) + { + trigger_error('Wrong parameter count for SimplePie_Misc::is_subclass_of()', E_USER_WARNING); + } + elseif (version_compare(PHP_VERSION, '5.0.3', '>=') || is_object($class1)) + { + return is_subclass_of($class1, $class2); + } + elseif (is_string($class1) && is_string($class2)) + { + if (class_exists($class1)) + { + if (class_exists($class2)) + { + $class2 = strtolower($class2); + while ($class1 = strtolower(get_parent_class($class1))) + { + if ($class1 == $class2) + { + return true; + } + } + } + } + else + { + trigger_error('Unknown class passed as parameter', E_USER_WARNNG); + } + } + return false; + } + + /** + * Strip HTML comments + * + * @access public + * @param string $data Data to strip comments from + * @return string Comment stripped string + */ + function strip_comments($data) + { + $output = ''; + while (($start = strpos($data, '', $start)) !== false) + { + $data = substr_replace($data, '', 0, $end + 3); + } + else + { + $data = ''; + } + } + return $output . $data; + } + + function parse_date($dt) + { + $parser = SimplePie_Parse_Date::get(); + return $parser->parse($dt); + } + + /** + * Decode HTML entities + * + * @static + * @access public + * @param string $data Input data + * @return string Output data + */ + function entities_decode($data) + { + $decoder = new SimplePie_Decode_HTML_Entities($data); + return $decoder->parse(); + } + + /** + * Remove RFC822 comments + * + * @access public + * @param string $data Data to strip comments from + * @return string Comment stripped string + */ + function uncomment_rfc822($string) + { + $string = (string) $string; + $position = 0; + $length = strlen($string); + $depth = 0; + + $output = ''; + + while ($position < $length && ($pos = strpos($string, '(', $position)) !== false) + { + $output .= substr($string, $position, $pos - $position); + $position = $pos + 1; + if ($string[$pos - 1] !== '\\') + { + $depth++; + while ($depth && $position < $length) + { + $position += strcspn($string, '()', $position); + if ($string[$position - 1] === '\\') + { + $position++; + continue; + } + elseif (isset($string[$position])) + { + switch ($string[$position]) + { + case '(': + $depth++; + break; + + case ')': + $depth--; + break; + } + $position++; + } + else + { + break; + } + } + } + else + { + $output .= '('; + } + } + $output .= substr($string, $position); + + return $output; + } + + function parse_mime($mime) + { + if (($pos = strpos($mime, ';')) === false) + { + return trim($mime); + } + else + { + return trim(substr($mime, 0, $pos)); + } + } + + function htmlspecialchars_decode($string, $quote_style) + { + if (function_exists('htmlspecialchars_decode')) + { + return htmlspecialchars_decode($string, $quote_style); + } + else + { + return strtr($string, array_flip(get_html_translation_table(HTML_SPECIALCHARS, $quote_style))); + } + } + + function atom_03_construct_type($attribs) + { + if (isset($attribs['']['mode']) && strtolower(trim($attribs['']['mode']) == 'base64')) + { + $mode = SIMPLEPIE_CONSTRUCT_BASE64; + } + else + { + $mode = SIMPLEPIE_CONSTRUCT_NONE; + } + if (isset($attribs['']['type'])) + { + switch (strtolower(trim($attribs['']['type']))) + { + case 'text': + case 'text/plain': + return SIMPLEPIE_CONSTRUCT_TEXT | $mode; + + case 'html': + case 'text/html': + return SIMPLEPIE_CONSTRUCT_HTML | $mode; + + case 'xhtml': + case 'application/xhtml+xml': + return SIMPLEPIE_CONSTRUCT_XHTML | $mode; + + default: + return SIMPLEPIE_CONSTRUCT_NONE | $mode; + } + } + else + { + return SIMPLEPIE_CONSTRUCT_TEXT | $mode; + } + } + + function atom_10_construct_type($attribs) + { + if (isset($attribs['']['type'])) + { + switch (strtolower(trim($attribs['']['type']))) + { + case 'text': + return SIMPLEPIE_CONSTRUCT_TEXT; + + case 'html': + return SIMPLEPIE_CONSTRUCT_HTML; + + case 'xhtml': + return SIMPLEPIE_CONSTRUCT_XHTML; + + default: + return SIMPLEPIE_CONSTRUCT_NONE; + } + } + return SIMPLEPIE_CONSTRUCT_TEXT; + } + + function atom_10_content_construct_type($attribs) + { + if (isset($attribs['']['type'])) + { + $type = strtolower(trim($attribs['']['type'])); + switch ($type) + { + case 'text': + return SIMPLEPIE_CONSTRUCT_TEXT; + + case 'html': + return SIMPLEPIE_CONSTRUCT_HTML; + + case 'xhtml': + return SIMPLEPIE_CONSTRUCT_XHTML; + } + if (in_array(substr($type, -4), array('+xml', '/xml')) || substr($type, 0, 5) == 'text/') + { + return SIMPLEPIE_CONSTRUCT_NONE; + } + else + { + return SIMPLEPIE_CONSTRUCT_BASE64; + } + } + else + { + return SIMPLEPIE_CONSTRUCT_TEXT; + } + } + + function is_isegment_nz_nc($string) + { + return (bool) preg_match('/^([A-Za-z0-9\-._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!$&\'()*+,;=@]|(%[0-9ABCDEF]{2}))+$/u', $string); + } + + function space_seperated_tokens($string) + { + $space_characters = "\x20\x09\x0A\x0B\x0C\x0D"; + $string_length = strlen($string); + + $position = strspn($string, $space_characters); + $tokens = array(); + + while ($position < $string_length) + { + $len = strcspn($string, $space_characters, $position); + $tokens[] = substr($string, $position, $len); + $position += $len; + $position += strspn($string, $space_characters, $position); + } + + return $tokens; + } + + function array_unique($array) + { + if (version_compare(PHP_VERSION, '5.2', '>=')) + { + return array_unique($array); + } + else + { + $array = (array) $array; + $new_array = array(); + $new_array_strings = array(); + foreach ($array as $key => $value) + { + if (is_object($value)) + { + if (method_exists($value, '__toString')) + { + $cmp = $value->__toString(); + } + else + { + trigger_error('Object of class ' . get_class($value) . ' could not be converted to string', E_USER_ERROR); + } + } + elseif (is_array($value)) + { + $cmp = (string) reset($value); + } + else + { + $cmp = (string) $value; + } + if (!in_array($cmp, $new_array_strings)) + { + $new_array[$key] = $value; + $new_array_strings[] = $cmp; + } + } + return $new_array; + } + } + + /** + * Converts a unicode codepoint to a UTF-8 character + * + * @static + * @access public + * @param int $codepoint Unicode codepoint + * @return string UTF-8 character + */ + function codepoint_to_utf8($codepoint) + { + static $cache = array(); + $codepoint = (int) $codepoint; + if (isset($cache[$codepoint])) + { + return $cache[$codepoint]; + } + elseif ($codepoint < 0) + { + return $cache[$codepoint] = false; + } + else if ($codepoint <= 0x7f) + { + return $cache[$codepoint] = chr($codepoint); + } + else if ($codepoint <= 0x7ff) + { + return $cache[$codepoint] = chr(0xc0 | ($codepoint >> 6)) . chr(0x80 | ($codepoint & 0x3f)); + } + else if ($codepoint <= 0xffff) + { + return $cache[$codepoint] = chr(0xe0 | ($codepoint >> 12)) . chr(0x80 | (($codepoint >> 6) & 0x3f)) . chr(0x80 | ($codepoint & 0x3f)); + } + else if ($codepoint <= 0x10ffff) + { + return $cache[$codepoint] = chr(0xf0 | ($codepoint >> 18)) . chr(0x80 | (($codepoint >> 12) & 0x3f)) . chr(0x80 | (($codepoint >> 6) & 0x3f)) . chr(0x80 | ($codepoint & 0x3f)); + } + else + { + // U+FFFD REPLACEMENT CHARACTER + return $cache[$codepoint] = "\xEF\xBF\xBD"; + } + } + + /** + * Re-implementation of PHP 5's stripos() + * + * Returns the numeric position of the first occurrence of needle in the + * haystack string. + * + * @static + * @access string + * @param object $haystack + * @param string $needle Note that the needle may be a string of one or more + * characters. If needle is not a string, it is converted to an integer + * and applied as the ordinal value of a character. + * @param int $offset The optional offset parameter allows you to specify which + * character in haystack to start searching. The position returned is still + * relative to the beginning of haystack. + * @return bool If needle is not found, stripos() will return boolean false. + */ + function stripos($haystack, $needle, $offset = 0) + { + if (function_exists('stripos')) + { + return stripos($haystack, $needle, $offset); + } + else + { + if (is_string($needle)) + { + $needle = strtolower($needle); + } + elseif (is_int($needle) || is_bool($needle) || is_double($needle)) + { + $needle = strtolower(chr($needle)); + } + else + { + trigger_error('needle is not a string or an integer', E_USER_WARNING); + return false; + } + + return strpos(strtolower($haystack), $needle, $offset); + } + } + + /** + * Similar to parse_str() + * + * Returns an associative array of name/value pairs, where the value is an + * array of values that have used the same name + * + * @static + * @access string + * @param string $str The input string. + * @return array + */ + function parse_str($str) + { + $return = array(); + $str = explode('&', $str); + + foreach ($str as $section) + { + if (strpos($section, '=') !== false) + { + list($name, $value) = explode('=', $section, 2); + $return[urldecode($name)][] = urldecode($value); + } + else + { + $return[urldecode($section)][] = null; + } + } + + return $return; + } + + /** + * Detect XML encoding, as per XML 1.0 Appendix F.1 + * + * @todo Add support for EBCDIC + * @param string $data XML data + * @return array Possible encodings + */ + function xml_encoding($data) + { + // UTF-32 Big Endian BOM + if (substr($data, 0, 4) === "\x00\x00\xFE\xFF") + { + $encoding[] = 'UTF-32BE'; + } + // UTF-32 Little Endian BOM + elseif (substr($data, 0, 4) === "\xFF\xFE\x00\x00") + { + $encoding[] = 'UTF-32LE'; + } + // UTF-16 Big Endian BOM + elseif (substr($data, 0, 2) === "\xFE\xFF") + { + $encoding[] = 'UTF-16BE'; + } + // UTF-16 Little Endian BOM + elseif (substr($data, 0, 2) === "\xFF\xFE") + { + $encoding[] = 'UTF-16LE'; + } + // UTF-8 BOM + elseif (substr($data, 0, 3) === "\xEF\xBB\xBF") + { + $encoding[] = 'UTF-8'; + } + // UTF-32 Big Endian Without BOM + elseif (substr($data, 0, 20) === "\x00\x00\x00\x3C\x00\x00\x00\x3F\x00\x00\x00\x78\x00\x00\x00\x6D\x00\x00\x00\x6C") + { + if ($pos = strpos($data, "\x00\x00\x00\x3F\x00\x00\x00\x3E")) + { + $parser = new SimplePie_XML_Declaration_Parser(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 20), 'UTF-32BE', 'UTF-8')); + if ($parser->parse()) + { + $encoding[] = $parser->encoding; + } + } + $encoding[] = 'UTF-32BE'; + } + // UTF-32 Little Endian Without BOM + elseif (substr($data, 0, 20) === "\x3C\x00\x00\x00\x3F\x00\x00\x00\x78\x00\x00\x00\x6D\x00\x00\x00\x6C\x00\x00\x00") + { + if ($pos = strpos($data, "\x3F\x00\x00\x00\x3E\x00\x00\x00")) + { + $parser = new SimplePie_XML_Declaration_Parser(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 20), 'UTF-32LE', 'UTF-8')); + if ($parser->parse()) + { + $encoding[] = $parser->encoding; + } + } + $encoding[] = 'UTF-32LE'; + } + // UTF-16 Big Endian Without BOM + elseif (substr($data, 0, 10) === "\x00\x3C\x00\x3F\x00\x78\x00\x6D\x00\x6C") + { + if ($pos = strpos($data, "\x00\x3F\x00\x3E")) + { + $parser = new SimplePie_XML_Declaration_Parser(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 10), 'UTF-16BE', 'UTF-8')); + if ($parser->parse()) + { + $encoding[] = $parser->encoding; + } + } + $encoding[] = 'UTF-16BE'; + } + // UTF-16 Little Endian Without BOM + elseif (substr($data, 0, 10) === "\x3C\x00\x3F\x00\x78\x00\x6D\x00\x6C\x00") + { + if ($pos = strpos($data, "\x3F\x00\x3E\x00")) + { + $parser = new SimplePie_XML_Declaration_Parser(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 10), 'UTF-16LE', 'UTF-8')); + if ($parser->parse()) + { + $encoding[] = $parser->encoding; + } + } + $encoding[] = 'UTF-16LE'; + } + // US-ASCII (or superset) + elseif (substr($data, 0, 5) === "\x3C\x3F\x78\x6D\x6C") + { + if ($pos = strpos($data, "\x3F\x3E")) + { + $parser = new SimplePie_XML_Declaration_Parser(substr($data, 5, $pos - 5)); + if ($parser->parse()) + { + $encoding[] = $parser->encoding; + } + } + $encoding[] = 'UTF-8'; + } + // Fallback to UTF-8 + else + { + $encoding[] = 'UTF-8'; + } + return $encoding; + } +} + +/** + * Decode HTML Entities + * + * This implements HTML5 as of revision 967 (2007-06-28) + * + * @package SimplePie + */ +class SimplePie_Decode_HTML_Entities +{ + /** + * Data to be parsed + * + * @access private + * @var string + */ + var $data = ''; + + /** + * Currently consumed bytes + * + * @access private + * @var string + */ + var $consumed = ''; + + /** + * Position of the current byte being parsed + * + * @access private + * @var int + */ + var $position = 0; + + /** + * Create an instance of the class with the input data + * + * @access public + * @param string $data Input data + */ + function SimplePie_Decode_HTML_Entities($data) + { + $this->data = $data; + } + + /** + * Parse the input data + * + * @access public + * @return string Output data + */ + function parse() + { + while (($this->position = strpos($this->data, '&', $this->position)) !== false) + { + $this->consume(); + $this->entity(); + $this->consumed = ''; + } + return $this->data; + } + + /** + * Consume the next byte + * + * @access private + * @return mixed The next byte, or false, if there is no more data + */ + function consume() + { + if (isset($this->data[$this->position])) + { + $this->consumed .= $this->data[$this->position]; + return $this->data[$this->position++]; + } + else + { + $this->consumed = false; + return false; + } + } + + /** + * Consume a range of characters + * + * @access private + * @param string $chars Characters to consume + * @return mixed A series of characters that match the range, or false + */ + function consume_range($chars) + { + if ($len = strspn($this->data, $chars, $this->position)) + { + $data = substr($this->data, $this->position, $len); + $this->consumed .= $data; + $this->position += $len; + return $data; + } + else + { + $this->consumed = false; + return false; + } + } + + /** + * Unconsume one byte + * + * @access private + */ + function unconsume() + { + $this->consumed = substr($this->consumed, 0, -1); + $this->position--; + } + + /** + * Decode an entity + * + * @access private + */ + function entity() + { + switch ($this->consume()) + { + case "\x09": + case "\x0A": + case "\x0B": + case "\x0B": + case "\x0C": + case "\x20": + case "\x3C": + case "\x26": + case false: + break; + + case "\x23": + switch ($this->consume()) + { + case "\x78": + case "\x58": + $range = '0123456789ABCDEFabcdef'; + $hex = true; + break; + + default: + $range = '0123456789'; + $hex = false; + $this->unconsume(); + break; + } + + if ($codepoint = $this->consume_range($range)) + { + static $windows_1252_specials = array(0x0D => "\x0A", 0x80 => "\xE2\x82\xAC", 0x81 => "\xEF\xBF\xBD", 0x82 => "\xE2\x80\x9A", 0x83 => "\xC6\x92", 0x84 => "\xE2\x80\x9E", 0x85 => "\xE2\x80\xA6", 0x86 => "\xE2\x80\xA0", 0x87 => "\xE2\x80\xA1", 0x88 => "\xCB\x86", 0x89 => "\xE2\x80\xB0", 0x8A => "\xC5\xA0", 0x8B => "\xE2\x80\xB9", 0x8C => "\xC5\x92", 0x8D => "\xEF\xBF\xBD", 0x8E => "\xC5\xBD", 0x8F => "\xEF\xBF\xBD", 0x90 => "\xEF\xBF\xBD", 0x91 => "\xE2\x80\x98", 0x92 => "\xE2\x80\x99", 0x93 => "\xE2\x80\x9C", 0x94 => "\xE2\x80\x9D", 0x95 => "\xE2\x80\xA2", 0x96 => "\xE2\x80\x93", 0x97 => "\xE2\x80\x94", 0x98 => "\xCB\x9C", 0x99 => "\xE2\x84\xA2", 0x9A => "\xC5\xA1", 0x9B => "\xE2\x80\xBA", 0x9C => "\xC5\x93", 0x9D => "\xEF\xBF\xBD", 0x9E => "\xC5\xBE", 0x9F => "\xC5\xB8"); + + if ($hex) + { + $codepoint = hexdec($codepoint); + } + else + { + $codepoint = intval($codepoint); + } + + if (isset($windows_1252_specials[$codepoint])) + { + $replacement = $windows_1252_specials[$codepoint]; + } + else + { + $replacement = SimplePie_Misc::codepoint_to_utf8($codepoint); + } + + if ($this->consume() != ';') + { + $this->unconsume(); + } + + $consumed_length = strlen($this->consumed); + $this->data = substr_replace($this->data, $replacement, $this->position - $consumed_length, $consumed_length); + $this->position += strlen($replacement) - $consumed_length; + } + break; + + default: + static $entities = array('Aacute' => "\xC3\x81", 'aacute' => "\xC3\xA1", 'Aacute;' => "\xC3\x81", 'aacute;' => "\xC3\xA1", 'Acirc' => "\xC3\x82", 'acirc' => "\xC3\xA2", 'Acirc;' => "\xC3\x82", 'acirc;' => "\xC3\xA2", 'acute' => "\xC2\xB4", 'acute;' => "\xC2\xB4", 'AElig' => "\xC3\x86", 'aelig' => "\xC3\xA6", 'AElig;' => "\xC3\x86", 'aelig;' => "\xC3\xA6", 'Agrave' => "\xC3\x80", 'agrave' => "\xC3\xA0", 'Agrave;' => "\xC3\x80", 'agrave;' => "\xC3\xA0", 'alefsym;' => "\xE2\x84\xB5", 'Alpha;' => "\xCE\x91", 'alpha;' => "\xCE\xB1", 'AMP' => "\x26", 'amp' => "\x26", 'AMP;' => "\x26", 'amp;' => "\x26", 'and;' => "\xE2\x88\xA7", 'ang;' => "\xE2\x88\xA0", 'apos;' => "\x27", 'Aring' => "\xC3\x85", 'aring' => "\xC3\xA5", 'Aring;' => "\xC3\x85", 'aring;' => "\xC3\xA5", 'asymp;' => "\xE2\x89\x88", 'Atilde' => "\xC3\x83", 'atilde' => "\xC3\xA3", 'Atilde;' => "\xC3\x83", 'atilde;' => "\xC3\xA3", 'Auml' => "\xC3\x84", 'auml' => "\xC3\xA4", 'Auml;' => "\xC3\x84", 'auml;' => "\xC3\xA4", 'bdquo;' => "\xE2\x80\x9E", 'Beta;' => "\xCE\x92", 'beta;' => "\xCE\xB2", 'brvbar' => "\xC2\xA6", 'brvbar;' => "\xC2\xA6", 'bull;' => "\xE2\x80\xA2", 'cap;' => "\xE2\x88\xA9", 'Ccedil' => "\xC3\x87", 'ccedil' => "\xC3\xA7", 'Ccedil;' => "\xC3\x87", 'ccedil;' => "\xC3\xA7", 'cedil' => "\xC2\xB8", 'cedil;' => "\xC2\xB8", 'cent' => "\xC2\xA2", 'cent;' => "\xC2\xA2", 'Chi;' => "\xCE\xA7", 'chi;' => "\xCF\x87", 'circ;' => "\xCB\x86", 'clubs;' => "\xE2\x99\xA3", 'cong;' => "\xE2\x89\x85", 'COPY' => "\xC2\xA9", 'copy' => "\xC2\xA9", 'COPY;' => "\xC2\xA9", 'copy;' => "\xC2\xA9", 'crarr;' => "\xE2\x86\xB5", 'cup;' => "\xE2\x88\xAA", 'curren' => "\xC2\xA4", 'curren;' => "\xC2\xA4", 'Dagger;' => "\xE2\x80\xA1", 'dagger;' => "\xE2\x80\xA0", 'dArr;' => "\xE2\x87\x93", 'darr;' => "\xE2\x86\x93", 'deg' => "\xC2\xB0", 'deg;' => "\xC2\xB0", 'Delta;' => "\xCE\x94", 'delta;' => "\xCE\xB4", 'diams;' => "\xE2\x99\xA6", 'divide' => "\xC3\xB7", 'divide;' => "\xC3\xB7", 'Eacute' => "\xC3\x89", 'eacute' => "\xC3\xA9", 'Eacute;' => "\xC3\x89", 'eacute;' => "\xC3\xA9", 'Ecirc' => "\xC3\x8A", 'ecirc' => "\xC3\xAA", 'Ecirc;' => "\xC3\x8A", 'ecirc;' => "\xC3\xAA", 'Egrave' => "\xC3\x88", 'egrave' => "\xC3\xA8", 'Egrave;' => "\xC3\x88", 'egrave;' => "\xC3\xA8", 'empty;' => "\xE2\x88\x85", 'emsp;' => "\xE2\x80\x83", 'ensp;' => "\xE2\x80\x82", 'Epsilon;' => "\xCE\x95", 'epsilon;' => "\xCE\xB5", 'equiv;' => "\xE2\x89\xA1", 'Eta;' => "\xCE\x97", 'eta;' => "\xCE\xB7", 'ETH' => "\xC3\x90", 'eth' => "\xC3\xB0", 'ETH;' => "\xC3\x90", 'eth;' => "\xC3\xB0", 'Euml' => "\xC3\x8B", 'euml' => "\xC3\xAB", 'Euml;' => "\xC3\x8B", 'euml;' => "\xC3\xAB", 'euro;' => "\xE2\x82\xAC", 'exist;' => "\xE2\x88\x83", 'fnof;' => "\xC6\x92", 'forall;' => "\xE2\x88\x80", 'frac12' => "\xC2\xBD", 'frac12;' => "\xC2\xBD", 'frac14' => "\xC2\xBC", 'frac14;' => "\xC2\xBC", 'frac34' => "\xC2\xBE", 'frac34;' => "\xC2\xBE", 'frasl;' => "\xE2\x81\x84", 'Gamma;' => "\xCE\x93", 'gamma;' => "\xCE\xB3", 'ge;' => "\xE2\x89\xA5", 'GT' => "\x3E", 'gt' => "\x3E", 'GT;' => "\x3E", 'gt;' => "\x3E", 'hArr;' => "\xE2\x87\x94", 'harr;' => "\xE2\x86\x94", 'hearts;' => "\xE2\x99\xA5", 'hellip;' => "\xE2\x80\xA6", 'Iacute' => "\xC3\x8D", 'iacute' => "\xC3\xAD", 'Iacute;' => "\xC3\x8D", 'iacute;' => "\xC3\xAD", 'Icirc' => "\xC3\x8E", 'icirc' => "\xC3\xAE", 'Icirc;' => "\xC3\x8E", 'icirc;' => "\xC3\xAE", 'iexcl' => "\xC2\xA1", 'iexcl;' => "\xC2\xA1", 'Igrave' => "\xC3\x8C", 'igrave' => "\xC3\xAC", 'Igrave;' => "\xC3\x8C", 'igrave;' => "\xC3\xAC", 'image;' => "\xE2\x84\x91", 'infin;' => "\xE2\x88\x9E", 'int;' => "\xE2\x88\xAB", 'Iota;' => "\xCE\x99", 'iota;' => "\xCE\xB9", 'iquest' => "\xC2\xBF", 'iquest;' => "\xC2\xBF", 'isin;' => "\xE2\x88\x88", 'Iuml' => "\xC3\x8F", 'iuml' => "\xC3\xAF", 'Iuml;' => "\xC3\x8F", 'iuml;' => "\xC3\xAF", 'Kappa;' => "\xCE\x9A", 'kappa;' => "\xCE\xBA", 'Lambda;' => "\xCE\x9B", 'lambda;' => "\xCE\xBB", 'lang;' => "\xE3\x80\x88", 'laquo' => "\xC2\xAB", 'laquo;' => "\xC2\xAB", 'lArr;' => "\xE2\x87\x90", 'larr;' => "\xE2\x86\x90", 'lceil;' => "\xE2\x8C\x88", 'ldquo;' => "\xE2\x80\x9C", 'le;' => "\xE2\x89\xA4", 'lfloor;' => "\xE2\x8C\x8A", 'lowast;' => "\xE2\x88\x97", 'loz;' => "\xE2\x97\x8A", 'lrm;' => "\xE2\x80\x8E", 'lsaquo;' => "\xE2\x80\xB9", 'lsquo;' => "\xE2\x80\x98", 'LT' => "\x3C", 'lt' => "\x3C", 'LT;' => "\x3C", 'lt;' => "\x3C", 'macr' => "\xC2\xAF", 'macr;' => "\xC2\xAF", 'mdash;' => "\xE2\x80\x94", 'micro' => "\xC2\xB5", 'micro;' => "\xC2\xB5", 'middot' => "\xC2\xB7", 'middot;' => "\xC2\xB7", 'minus;' => "\xE2\x88\x92", 'Mu;' => "\xCE\x9C", 'mu;' => "\xCE\xBC", 'nabla;' => "\xE2\x88\x87", 'nbsp' => "\xC2\xA0", 'nbsp;' => "\xC2\xA0", 'ndash;' => "\xE2\x80\x93", 'ne;' => "\xE2\x89\xA0", 'ni;' => "\xE2\x88\x8B", 'not' => "\xC2\xAC", 'not;' => "\xC2\xAC", 'notin;' => "\xE2\x88\x89", 'nsub;' => "\xE2\x8A\x84", 'Ntilde' => "\xC3\x91", 'ntilde' => "\xC3\xB1", 'Ntilde;' => "\xC3\x91", 'ntilde;' => "\xC3\xB1", 'Nu;' => "\xCE\x9D", 'nu;' => "\xCE\xBD", 'Oacute' => "\xC3\x93", 'oacute' => "\xC3\xB3", 'Oacute;' => "\xC3\x93", 'oacute;' => "\xC3\xB3", 'Ocirc' => "\xC3\x94", 'ocirc' => "\xC3\xB4", 'Ocirc;' => "\xC3\x94", 'ocirc;' => "\xC3\xB4", 'OElig;' => "\xC5\x92", 'oelig;' => "\xC5\x93", 'Ograve' => "\xC3\x92", 'ograve' => "\xC3\xB2", 'Ograve;' => "\xC3\x92", 'ograve;' => "\xC3\xB2", 'oline;' => "\xE2\x80\xBE", 'Omega;' => "\xCE\xA9", 'omega;' => "\xCF\x89", 'Omicron;' => "\xCE\x9F", 'omicron;' => "\xCE\xBF", 'oplus;' => "\xE2\x8A\x95", 'or;' => "\xE2\x88\xA8", 'ordf' => "\xC2\xAA", 'ordf;' => "\xC2\xAA", 'ordm' => "\xC2\xBA", 'ordm;' => "\xC2\xBA", 'Oslash' => "\xC3\x98", 'oslash' => "\xC3\xB8", 'Oslash;' => "\xC3\x98", 'oslash;' => "\xC3\xB8", 'Otilde' => "\xC3\x95", 'otilde' => "\xC3\xB5", 'Otilde;' => "\xC3\x95", 'otilde;' => "\xC3\xB5", 'otimes;' => "\xE2\x8A\x97", 'Ouml' => "\xC3\x96", 'ouml' => "\xC3\xB6", 'Ouml;' => "\xC3\x96", 'ouml;' => "\xC3\xB6", 'para' => "\xC2\xB6", 'para;' => "\xC2\xB6", 'part;' => "\xE2\x88\x82", 'permil;' => "\xE2\x80\xB0", 'perp;' => "\xE2\x8A\xA5", 'Phi;' => "\xCE\xA6", 'phi;' => "\xCF\x86", 'Pi;' => "\xCE\xA0", 'pi;' => "\xCF\x80", 'piv;' => "\xCF\x96", 'plusmn' => "\xC2\xB1", 'plusmn;' => "\xC2\xB1", 'pound' => "\xC2\xA3", 'pound;' => "\xC2\xA3", 'Prime;' => "\xE2\x80\xB3", 'prime;' => "\xE2\x80\xB2", 'prod;' => "\xE2\x88\x8F", 'prop;' => "\xE2\x88\x9D", 'Psi;' => "\xCE\xA8", 'psi;' => "\xCF\x88", 'QUOT' => "\x22", 'quot' => "\x22", 'QUOT;' => "\x22", 'quot;' => "\x22", 'radic;' => "\xE2\x88\x9A", 'rang;' => "\xE3\x80\x89", 'raquo' => "\xC2\xBB", 'raquo;' => "\xC2\xBB", 'rArr;' => "\xE2\x87\x92", 'rarr;' => "\xE2\x86\x92", 'rceil;' => "\xE2\x8C\x89", 'rdquo;' => "\xE2\x80\x9D", 'real;' => "\xE2\x84\x9C", 'REG' => "\xC2\xAE", 'reg' => "\xC2\xAE", 'REG;' => "\xC2\xAE", 'reg;' => "\xC2\xAE", 'rfloor;' => "\xE2\x8C\x8B", 'Rho;' => "\xCE\xA1", 'rho;' => "\xCF\x81", 'rlm;' => "\xE2\x80\x8F", 'rsaquo;' => "\xE2\x80\xBA", 'rsquo;' => "\xE2\x80\x99", 'sbquo;' => "\xE2\x80\x9A", 'Scaron;' => "\xC5\xA0", 'scaron;' => "\xC5\xA1", 'sdot;' => "\xE2\x8B\x85", 'sect' => "\xC2\xA7", 'sect;' => "\xC2\xA7", 'shy' => "\xC2\xAD", 'shy;' => "\xC2\xAD", 'Sigma;' => "\xCE\xA3", 'sigma;' => "\xCF\x83", 'sigmaf;' => "\xCF\x82", 'sim;' => "\xE2\x88\xBC", 'spades;' => "\xE2\x99\xA0", 'sub;' => "\xE2\x8A\x82", 'sube;' => "\xE2\x8A\x86", 'sum;' => "\xE2\x88\x91", 'sup;' => "\xE2\x8A\x83", 'sup1' => "\xC2\xB9", 'sup1;' => "\xC2\xB9", 'sup2' => "\xC2\xB2", 'sup2;' => "\xC2\xB2", 'sup3' => "\xC2\xB3", 'sup3;' => "\xC2\xB3", 'supe;' => "\xE2\x8A\x87", 'szlig' => "\xC3\x9F", 'szlig;' => "\xC3\x9F", 'Tau;' => "\xCE\xA4", 'tau;' => "\xCF\x84", 'there4;' => "\xE2\x88\xB4", 'Theta;' => "\xCE\x98", 'theta;' => "\xCE\xB8", 'thetasym;' => "\xCF\x91", 'thinsp;' => "\xE2\x80\x89", 'THORN' => "\xC3\x9E", 'thorn' => "\xC3\xBE", 'THORN;' => "\xC3\x9E", 'thorn;' => "\xC3\xBE", 'tilde;' => "\xCB\x9C", 'times' => "\xC3\x97", 'times;' => "\xC3\x97", 'TRADE;' => "\xE2\x84\xA2", 'trade;' => "\xE2\x84\xA2", 'Uacute' => "\xC3\x9A", 'uacute' => "\xC3\xBA", 'Uacute;' => "\xC3\x9A", 'uacute;' => "\xC3\xBA", 'uArr;' => "\xE2\x87\x91", 'uarr;' => "\xE2\x86\x91", 'Ucirc' => "\xC3\x9B", 'ucirc' => "\xC3\xBB", 'Ucirc;' => "\xC3\x9B", 'ucirc;' => "\xC3\xBB", 'Ugrave' => "\xC3\x99", 'ugrave' => "\xC3\xB9", 'Ugrave;' => "\xC3\x99", 'ugrave;' => "\xC3\xB9", 'uml' => "\xC2\xA8", 'uml;' => "\xC2\xA8", 'upsih;' => "\xCF\x92", 'Upsilon;' => "\xCE\xA5", 'upsilon;' => "\xCF\x85", 'Uuml' => "\xC3\x9C", 'uuml' => "\xC3\xBC", 'Uuml;' => "\xC3\x9C", 'uuml;' => "\xC3\xBC", 'weierp;' => "\xE2\x84\x98", 'Xi;' => "\xCE\x9E", 'xi;' => "\xCE\xBE", 'Yacute' => "\xC3\x9D", 'yacute' => "\xC3\xBD", 'Yacute;' => "\xC3\x9D", 'yacute;' => "\xC3\xBD", 'yen' => "\xC2\xA5", 'yen;' => "\xC2\xA5", 'yuml' => "\xC3\xBF", 'Yuml;' => "\xC5\xB8", 'yuml;' => "\xC3\xBF", 'Zeta;' => "\xCE\x96", 'zeta;' => "\xCE\xB6", 'zwj;' => "\xE2\x80\x8D", 'zwnj;' => "\xE2\x80\x8C"); + + for ($i = 0, $match = null; $i < 9 && $this->consume() !== false; $i++) + { + $consumed = substr($this->consumed, 1); + if (isset($entities[$consumed])) + { + $match = $consumed; + } + } + + if ($match !== null) + { + $this->data = substr_replace($this->data, $entities[$match], $this->position - strlen($consumed) - 1, strlen($match) + 1); + $this->position += strlen($entities[$match]) - strlen($consumed) - 1; + } + break; + } + } +} + +/** + * Date Parser + * + * @package SimplePie + */ +class SimplePie_Parse_Date +{ + /** + * Input data + * + * @access protected + * @var string + */ + var $date; + + /** + * List of days, calendar day name => ordinal day number in the week + * + * @access protected + * @var array + */ + var $day = array( + // English + 'mon' => 1, + 'monday' => 1, + 'tue' => 2, + 'tuesday' => 2, + 'wed' => 3, + 'wednesday' => 3, + 'thu' => 4, + 'thursday' => 4, + 'fri' => 5, + 'friday' => 5, + 'sat' => 6, + 'saturday' => 6, + 'sun' => 7, + 'sunday' => 7, + // Dutch + 'maandag' => 1, + 'dinsdag' => 2, + 'woensdag' => 3, + 'donderdag' => 4, + 'vrijdag' => 5, + 'zaterdag' => 6, + 'zondag' => 7, + // French + 'lundi' => 1, + 'mardi' => 2, + 'mercredi' => 3, + 'jeudi' => 4, + 'vendredi' => 5, + 'samedi' => 6, + 'dimanche' => 7, + // German + 'montag' => 1, + 'dienstag' => 2, + 'mittwoch' => 3, + 'donnerstag' => 4, + 'freitag' => 5, + 'samstag' => 6, + 'sonnabend' => 6, + 'sonntag' => 7, + // Italian + 'lunedì' => 1, + 'martedì' => 2, + 'mercoledì' => 3, + 'giovedì' => 4, + 'venerdì' => 5, + 'sabato' => 6, + 'domenica' => 7, + // Spanish + 'lunes' => 1, + 'martes' => 2, + 'miércoles' => 3, + 'jueves' => 4, + 'viernes' => 5, + 'sábado' => 6, + 'domingo' => 7, + // Finnish + 'maanantai' => 1, + 'tiistai' => 2, + 'keskiviikko' => 3, + 'torstai' => 4, + 'perjantai' => 5, + 'lauantai' => 6, + 'sunnuntai' => 7, + // Hungarian + 'hétfÅ‘' => 1, + 'kedd' => 2, + 'szerda' => 3, + 'csütörtok' => 4, + 'péntek' => 5, + 'szombat' => 6, + 'vasárnap' => 7, + // Greek + 'Δευ' => 1, + 'ΤÏι' => 2, + 'Τετ' => 3, + 'Πεμ' => 4, + 'ΠαÏ' => 5, + 'Σαβ' => 6, + 'ΚυÏ' => 7, + ); + + /** + * List of months, calendar month name => calendar month number + * + * @access protected + * @var array + */ + var $month = array( + // English + 'jan' => 1, + 'january' => 1, + 'feb' => 2, + 'february' => 2, + 'mar' => 3, + 'march' => 3, + 'apr' => 4, + 'april' => 4, + 'may' => 5, + // No long form of May + 'jun' => 6, + 'june' => 6, + 'jul' => 7, + 'july' => 7, + 'aug' => 8, + 'august' => 8, + 'sep' => 9, + 'september' => 8, + 'oct' => 10, + 'october' => 10, + 'nov' => 11, + 'november' => 11, + 'dec' => 12, + 'december' => 12, + // Dutch + 'januari' => 1, + 'februari' => 2, + 'maart' => 3, + 'april' => 4, + 'mei' => 5, + 'juni' => 6, + 'juli' => 7, + 'augustus' => 8, + 'september' => 9, + 'oktober' => 10, + 'november' => 11, + 'december' => 12, + // French + 'janvier' => 1, + 'février' => 2, + 'mars' => 3, + 'avril' => 4, + 'mai' => 5, + 'juin' => 6, + 'juillet' => 7, + 'août' => 8, + 'septembre' => 9, + 'octobre' => 10, + 'novembre' => 11, + 'décembre' => 12, + // German + 'januar' => 1, + 'februar' => 2, + 'märz' => 3, + 'april' => 4, + 'mai' => 5, + 'juni' => 6, + 'juli' => 7, + 'august' => 8, + 'september' => 9, + 'oktober' => 10, + 'november' => 11, + 'dezember' => 12, + // Italian + 'gennaio' => 1, + 'febbraio' => 2, + 'marzo' => 3, + 'aprile' => 4, + 'maggio' => 5, + 'giugno' => 6, + 'luglio' => 7, + 'agosto' => 8, + 'settembre' => 9, + 'ottobre' => 10, + 'novembre' => 11, + 'dicembre' => 12, + // Spanish + 'enero' => 1, + 'febrero' => 2, + 'marzo' => 3, + 'abril' => 4, + 'mayo' => 5, + 'junio' => 6, + 'julio' => 7, + 'agosto' => 8, + 'septiembre' => 9, + 'setiembre' => 9, + 'octubre' => 10, + 'noviembre' => 11, + 'diciembre' => 12, + // Finnish + 'tammikuu' => 1, + 'helmikuu' => 2, + 'maaliskuu' => 3, + 'huhtikuu' => 4, + 'toukokuu' => 5, + 'kesäkuu' => 6, + 'heinäkuu' => 7, + 'elokuu' => 8, + 'suuskuu' => 9, + 'lokakuu' => 10, + 'marras' => 11, + 'joulukuu' => 12, + // Hungarian + 'január' => 1, + 'február' => 2, + 'március' => 3, + 'április' => 4, + 'május' => 5, + 'június' => 6, + 'július' => 7, + 'augusztus' => 8, + 'szeptember' => 9, + 'október' => 10, + 'november' => 11, + 'december' => 12, + // Greek + 'Ιαν' => 1, + 'Φεβ' => 2, + 'Μάώ' => 3, + 'Μαώ' => 3, + 'ΑπÏ' => 4, + 'Μάι' => 5, + 'Μαϊ' => 5, + 'Μαι' => 5, + 'ΙοÏν' => 6, + 'Ιον' => 6, + 'ΙοÏλ' => 7, + 'Ιολ' => 7, + 'ΑÏγ' => 8, + 'Αυγ' => 8, + 'Σεπ' => 9, + 'Οκτ' => 10, + 'Îοέ' => 11, + 'Δεκ' => 12, + ); + + /** + * List of timezones, abbreviation => offset from UTC + * + * @access protected + * @var array + */ + var $timezone = array( + 'ACDT' => 37800, + 'ACIT' => 28800, + 'ACST' => 34200, + 'ACT' => -18000, + 'ACWDT' => 35100, + 'ACWST' => 31500, + 'AEDT' => 39600, + 'AEST' => 36000, + 'AFT' => 16200, + 'AKDT' => -28800, + 'AKST' => -32400, + 'AMDT' => 18000, + 'AMT' => -14400, + 'ANAST' => 46800, + 'ANAT' => 43200, + 'ART' => -10800, + 'AZOST' => -3600, + 'AZST' => 18000, + 'AZT' => 14400, + 'BIOT' => 21600, + 'BIT' => -43200, + 'BOT' => -14400, + 'BRST' => -7200, + 'BRT' => -10800, + 'BST' => 3600, + 'BTT' => 21600, + 'CAST' => 18000, + 'CAT' => 7200, + 'CCT' => 23400, + 'CDT' => -18000, + 'CEDT' => 7200, + 'CET' => 3600, + 'CGST' => -7200, + 'CGT' => -10800, + 'CHADT' => 49500, + 'CHAST' => 45900, + 'CIST' => -28800, + 'CKT' => -36000, + 'CLDT' => -10800, + 'CLST' => -14400, + 'COT' => -18000, + 'CST' => -21600, + 'CVT' => -3600, + 'CXT' => 25200, + 'DAVT' => 25200, + 'DTAT' => 36000, + 'EADT' => -18000, + 'EAST' => -21600, + 'EAT' => 10800, + 'ECT' => -18000, + 'EDT' => -14400, + 'EEST' => 10800, + 'EET' => 7200, + 'EGT' => -3600, + 'EKST' => 21600, + 'EST' => -18000, + 'FJT' => 43200, + 'FKDT' => -10800, + 'FKST' => -14400, + 'FNT' => -7200, + 'GALT' => -21600, + 'GEDT' => 14400, + 'GEST' => 10800, + 'GFT' => -10800, + 'GILT' => 43200, + 'GIT' => -32400, + 'GST' => 14400, + 'GST' => -7200, + 'GYT' => -14400, + 'HAA' => -10800, + 'HAC' => -18000, + 'HADT' => -32400, + 'HAE' => -14400, + 'HAP' => -25200, + 'HAR' => -21600, + 'HAST' => -36000, + 'HAT' => -9000, + 'HAY' => -28800, + 'HKST' => 28800, + 'HMT' => 18000, + 'HNA' => -14400, + 'HNC' => -21600, + 'HNE' => -18000, + 'HNP' => -28800, + 'HNR' => -25200, + 'HNT' => -12600, + 'HNY' => -32400, + 'IRDT' => 16200, + 'IRKST' => 32400, + 'IRKT' => 28800, + 'IRST' => 12600, + 'JFDT' => -10800, + 'JFST' => -14400, + 'JST' => 32400, + 'KGST' => 21600, + 'KGT' => 18000, + 'KOST' => 39600, + 'KOVST' => 28800, + 'KOVT' => 25200, + 'KRAST' => 28800, + 'KRAT' => 25200, + 'KST' => 32400, + 'LHDT' => 39600, + 'LHST' => 37800, + 'LINT' => 50400, + 'LKT' => 21600, + 'MAGST' => 43200, + 'MAGT' => 39600, + 'MAWT' => 21600, + 'MDT' => -21600, + 'MESZ' => 7200, + 'MEZ' => 3600, + 'MHT' => 43200, + 'MIT' => -34200, + 'MNST' => 32400, + 'MSDT' => 14400, + 'MSST' => 10800, + 'MST' => -25200, + 'MUT' => 14400, + 'MVT' => 18000, + 'MYT' => 28800, + 'NCT' => 39600, + 'NDT' => -9000, + 'NFT' => 41400, + 'NMIT' => 36000, + 'NOVST' => 25200, + 'NOVT' => 21600, + 'NPT' => 20700, + 'NRT' => 43200, + 'NST' => -12600, + 'NUT' => -39600, + 'NZDT' => 46800, + 'NZST' => 43200, + 'OMSST' => 25200, + 'OMST' => 21600, + 'PDT' => -25200, + 'PET' => -18000, + 'PETST' => 46800, + 'PETT' => 43200, + 'PGT' => 36000, + 'PHOT' => 46800, + 'PHT' => 28800, + 'PKT' => 18000, + 'PMDT' => -7200, + 'PMST' => -10800, + 'PONT' => 39600, + 'PST' => -28800, + 'PWT' => 32400, + 'PYST' => -10800, + 'PYT' => -14400, + 'RET' => 14400, + 'ROTT' => -10800, + 'SAMST' => 18000, + 'SAMT' => 14400, + 'SAST' => 7200, + 'SBT' => 39600, + 'SCDT' => 46800, + 'SCST' => 43200, + 'SCT' => 14400, + 'SEST' => 3600, + 'SGT' => 28800, + 'SIT' => 28800, + 'SRT' => -10800, + 'SST' => -39600, + 'SYST' => 10800, + 'SYT' => 7200, + 'TFT' => 18000, + 'THAT' => -36000, + 'TJT' => 18000, + 'TKT' => -36000, + 'TMT' => 18000, + 'TOT' => 46800, + 'TPT' => 32400, + 'TRUT' => 36000, + 'TVT' => 43200, + 'TWT' => 28800, + 'UYST' => -7200, + 'UYT' => -10800, + 'UZT' => 18000, + 'VET' => -14400, + 'VLAST' => 39600, + 'VLAT' => 36000, + 'VOST' => 21600, + 'VUT' => 39600, + 'WAST' => 7200, + 'WAT' => 3600, + 'WDT' => 32400, + 'WEST' => 3600, + 'WFT' => 43200, + 'WIB' => 25200, + 'WIT' => 32400, + 'WITA' => 28800, + 'WKST' => 18000, + 'WST' => 28800, + 'YAKST' => 36000, + 'YAKT' => 32400, + 'YAPT' => 36000, + 'YEKST' => 21600, + 'YEKT' => 18000, + ); + + /** + * Cached PCRE for SimplePie_Parse_Date::$day + * + * @access protected + * @var string + */ + var $day_pcre; + + /** + * Cached PCRE for SimplePie_Parse_Date::$month + * + * @access protected + * @var string + */ + var $month_pcre; + + /** + * Array of user-added callback methods + * + * @access private + * @var array + */ + var $built_in = array(); + + /** + * Array of user-added callback methods + * + * @access private + * @var array + */ + var $user = array(); + + /** + * Create new SimplePie_Parse_Date object, and set self::day_pcre, + * self::month_pcre, and self::built_in + * + * @access private + */ + function SimplePie_Parse_Date() + { + $this->day_pcre = '(' . implode(array_keys($this->day), '|') . ')'; + $this->month_pcre = '(' . implode(array_keys($this->month), '|') . ')'; + + static $cache; + if (!isset($cache[get_class($this)])) + { + if (extension_loaded('Reflection')) + { + $class = new ReflectionClass(get_class($this)); + $methods = $class->getMethods(); + $all_methods = array(); + foreach ($methods as $method) + { + $all_methods[] = $method->getName(); + } + } + else + { + $all_methods = get_class_methods($this); + } + + foreach ($all_methods as $method) + { + if (strtolower(substr($method, 0, 5)) === 'date_') + { + $cache[get_class($this)][] = $method; + } + } + } + + foreach ($cache[get_class($this)] as $method) + { + $this->built_in[] = $method; + } + } + + /** + * Get the object + * + * @access public + */ + function get() + { + static $object; + if (!$object) + { + $object = new SimplePie_Parse_Date; + } + return $object; + } + + /** + * Parse a date + * + * @final + * @access public + * @param string $date Date to parse + * @return int Timestamp corresponding to date string, or false on failure + */ + function parse($date) + { + foreach ($this->user as $method) + { + if (($returned = call_user_func($method, $date)) !== false) + { + return $returned; + } + } + + foreach ($this->built_in as $method) + { + if (($returned = call_user_func(array(&$this, $method), $date)) !== false) + { + return $returned; + } + } + + return false; + } + + /** + * Add a callback method to parse a date + * + * @final + * @access public + * @param callback $callback + */ + function add_callback($callback) + { + if (is_callable($callback)) + { + $this->user[] = $callback; + } + else + { + trigger_error('User-supplied function must be a valid callback', E_USER_WARNING); + } + } + + /** + * Parse a superset of W3C-DTF (allows hyphens and colons to be omitted, as + * well as allowing any of upper or lower case "T", horizontal tabs, or + * spaces to be used as the time seperator (including more than one)) + * + * @access protected + * @return int Timestamp + */ + function date_w3cdtf($date) + { + static $pcre; + if (!$pcre) + { + $year = '([0-9]{4})'; + $month = $day = $hour = $minute = $second = '([0-9]{2})'; + $decimal = '([0-9]*)'; + $zone = '(?:(Z)|([+\-])([0-9]{1,2}):?([0-9]{1,2}))'; + $pcre = '/^' . $year . '(?:-?' . $month . '(?:-?' . $day . '(?:[Tt\x09\x20]+' . $hour . '(?::?' . $minute . '(?::?' . $second . '(?:.' . $decimal . ')?)?)?' . $zone . ')?)?)?$/'; + } + if (preg_match($pcre, $date, $match)) + { + /* + Capturing subpatterns: + 1: Year + 2: Month + 3: Day + 4: Hour + 5: Minute + 6: Second + 7: Decimal fraction of a second + 8: Zulu + 9: Timezone ± + 10: Timezone hours + 11: Timezone minutes + */ + + // Fill in empty matches + for ($i = count($match); $i <= 3; $i++) + { + $match[$i] = '1'; + } + + for ($i = count($match); $i <= 7; $i++) + { + $match[$i] = '0'; + } + + // Numeric timezone + if (isset($match[9]) && $match[9] !== '') + { + $timezone = $match[10] * 3600; + $timezone += $match[11] * 60; + if ($match[9] === '-') + { + $timezone = 0 - $timezone; + } + } + else + { + $timezone = 0; + } + + // Convert the number of seconds to an integer, taking decimals into account + $second = round($match[6] + $match[7] / pow(10, strlen($match[7]))); + + return gmmktime($match[4], $match[5], $second, $match[2], $match[3], $match[1]) - $timezone; + } + else + { + return false; + } + } + + /** + * Remove RFC822 comments + * + * @access protected + * @param string $data Data to strip comments from + * @return string Comment stripped string + */ + function remove_rfc2822_comments($string) + { + $string = (string) $string; + $position = 0; + $length = strlen($string); + $depth = 0; + + $output = ''; + + while ($position < $length && ($pos = strpos($string, '(', $position)) !== false) + { + $output .= substr($string, $position, $pos - $position); + $position = $pos + 1; + if ($string[$pos - 1] !== '\\') + { + $depth++; + while ($depth && $position < $length) + { + $position += strcspn($string, '()', $position); + if ($string[$position - 1] === '\\') + { + $position++; + continue; + } + elseif (isset($string[$position])) + { + switch ($string[$position]) + { + case '(': + $depth++; + break; + + case ')': + $depth--; + break; + } + $position++; + } + else + { + break; + } + } + } + else + { + $output .= '('; + } + } + $output .= substr($string, $position); + + return $output; + } + + /** + * Parse RFC2822's date format + * + * @access protected + * @return int Timestamp + */ + function date_rfc2822($date) + { + static $pcre; + if (!$pcre) + { + $wsp = '[\x09\x20]'; + $fws = '(?:' . $wsp . '+|' . $wsp . '*(?:\x0D\x0A' . $wsp . '+)+)'; + $optional_fws = $fws . '?'; + $day_name = $this->day_pcre; + $month = $this->month_pcre; + $day = '([0-9]{1,2})'; + $hour = $minute = $second = '([0-9]{2})'; + $year = '([0-9]{2,4})'; + $num_zone = '([+\-])([0-9]{2})([0-9]{2})'; + $character_zone = '([A-Z]{1,5})'; + $zone = '(?:' . $num_zone . '|' . $character_zone . ')'; + $pcre = '/(?:' . $optional_fws . $day_name . $optional_fws . ',)?' . $optional_fws . $day . $fws . $month . $fws . $year . $fws . $hour . $optional_fws . ':' . $optional_fws . $minute . '(?:' . $optional_fws . ':' . $optional_fws . $second . ')?' . $fws . $zone . '/i'; + } + if (preg_match($pcre, $this->remove_rfc2822_comments($date), $match)) + { + /* + Capturing subpatterns: + 1: Day name + 2: Day + 3: Month + 4: Year + 5: Hour + 6: Minute + 7: Second + 8: Timezone ± + 9: Timezone hours + 10: Timezone minutes + 11: Alphabetic timezone + */ + + // Find the month number + $month = $this->month[strtolower($match[3])]; + + // Numeric timezone + if ($match[8] !== '') + { + $timezone = $match[9] * 3600; + $timezone += $match[10] * 60; + if ($match[8] === '-') + { + $timezone = 0 - $timezone; + } + } + // Character timezone + elseif (isset($this->timezone[strtoupper($match[11])])) + { + $timezone = $this->timezone[strtoupper($match[11])]; + } + // Assume everything else to be -0000 + else + { + $timezone = 0; + } + + // Deal with 2/3 digit years + if ($match[4] < 50) + { + $match[4] += 2000; + } + elseif ($match[4] < 1000) + { + $match[4] += 1900; + } + + // Second is optional, if it is empty set it to zero + if ($match[7] !== '') + { + $second = $match[7]; + } + else + { + $second = 0; + } + + return gmmktime($match[5], $match[6], $second, $month, $match[2], $match[4]) - $timezone; + } + else + { + return false; + } + } + + /** + * Parse RFC850's date format + * + * @access protected + * @return int Timestamp + */ + function date_rfc850($date) + { + static $pcre; + if (!$pcre) + { + $space = '[\x09\x20]+'; + $day_name = $this->day_pcre; + $month = $this->month_pcre; + $day = '([0-9]{1,2})'; + $year = $hour = $minute = $second = '([0-9]{2})'; + $zone = '([A-Z]{1,5})'; + $pcre = '/^' . $day_name . ',' . $space . $day . '-' . $month . '-' . $year . $space . $hour . ':' . $minute . ':' . $second . $space . $zone . '$/i'; + } + if (preg_match($pcre, $date, $match)) + { + /* + Capturing subpatterns: + 1: Day name + 2: Day + 3: Month + 4: Year + 5: Hour + 6: Minute + 7: Second + 8: Timezone + */ + + // Month + $month = $this->month[strtolower($match[3])]; + + // Character timezone + if (isset($this->timezone[strtoupper($match[8])])) + { + $timezone = $this->timezone[strtoupper($match[8])]; + } + // Assume everything else to be -0000 + else + { + $timezone = 0; + } + + // Deal with 2 digit year + if ($match[4] < 50) + { + $match[4] += 2000; + } + else + { + $match[4] += 1900; + } + + return gmmktime($match[5], $match[6], $match[7], $month, $match[2], $match[4]) - $timezone; + } + else + { + return false; + } + } + + /** + * Parse C99's asctime()'s date format + * + * @access protected + * @return int Timestamp + */ + function date_asctime($date) + { + static $pcre; + if (!$pcre) + { + $space = '[\x09\x20]+'; + $wday_name = $this->day_pcre; + $mon_name = $this->month_pcre; + $day = '([0-9]{1,2})'; + $hour = $sec = $min = '([0-9]{2})'; + $year = '([0-9]{4})'; + $terminator = '\x0A?\x00?'; + $pcre = '/^' . $wday_name . $space . $mon_name . $space . $day . $space . $hour . ':' . $min . ':' . $sec . $space . $year . $terminator . '$/i'; + } + if (preg_match($pcre, $date, $match)) + { + /* + Capturing subpatterns: + 1: Day name + 2: Month + 3: Day + 4: Hour + 5: Minute + 6: Second + 7: Year + */ + + $month = $this->month[strtolower($match[2])]; + return gmmktime($match[4], $match[5], $match[6], $month, $match[3], $match[7]); + } + else + { + return false; + } + } + + /** + * Parse dates using strtotime() + * + * @access protected + * @return int Timestamp + */ + function date_strtotime($date) + { + $strtotime = strtotime($date); + if ($strtotime === -1 || $strtotime === false) + { + return false; + } + else + { + return $strtotime; + } + } +} + +/** + * Content-type sniffing + * + * @package SimplePie + */ +class SimplePie_Content_Type_Sniffer +{ + /** + * File object + * + * @var SimplePie_File + * @access private + */ + var $file; + + /** + * Create an instance of the class with the input file + * + * @access public + * @param SimplePie_Content_Type_Sniffer $file Input file + */ + function SimplePie_Content_Type_Sniffer($file) + { + $this->file = $file; + } + + /** + * Get the Content-Type of the specified file + * + * @access public + * @return string Actual Content-Type + */ + function get_type() + { + if (isset($this->file->headers['content-type'])) + { + if (!isset($this->file->headers['content-encoding']) + && ($this->file->headers['content-type'] === 'text/plain' + || $this->file->headers['content-type'] === 'text/plain; charset=ISO-8859-1' + || $this->file->headers['content-type'] === 'text/plain; charset=iso-8859-1')) + { + return $this->text_or_binary(); + } + + if (($pos = strpos($this->file->headers['content-type'], ';')) !== false) + { + $official = substr($this->file->headers['content-type'], 0, $pos); + } + else + { + $official = $this->file->headers['content-type']; + } + $official = strtolower($official); + + if ($official === 'unknown/unknown' + || $official === 'application/unknown') + { + return $this->unknown(); + } + elseif (substr($official, -4) === '+xml' + || $official === 'text/xml' + || $official === 'application/xml') + { + return $official; + } + elseif (substr($official, 0, 6) === 'image/') + { + if ($return = $this->image()) + { + return $return; + } + else + { + return $official; + } + } + elseif ($official === 'text/html') + { + return $this->feed_or_html(); + } + else + { + return $official; + } + } + else + { + return $this->unknown(); + } + } + + /** + * Sniff text or binary + * + * @access private + * @return string Actual Content-Type + */ + function text_or_binary() + { + if (substr($this->file->body, 0, 2) === "\xFE\xFF" + || substr($this->file->body, 0, 2) === "\xFF\xFE" + || substr($this->file->body, 0, 4) === "\x00\x00\xFE\xFF" + || substr($this->file->body, 0, 3) === "\xEF\xBB\xBF") + { + return 'text/plain'; + } + elseif (preg_match('/[\x00-\x08\x0E-\x1A\x1C-\x1F]/', $this->file->body)) + { + return 'application/octect-stream'; + } + else + { + return 'text/plain'; + } + } + + /** + * Sniff unknown + * + * @access private + * @return string Actual Content-Type + */ + function unknown() + { + $ws = strspn($this->file->body, "\x09\x0A\x0B\x0C\x0D\x20"); + if (strtolower(substr($this->file->body, $ws, 14)) === 'file->body, $ws, 5)) === 'file->body, $ws, 7)) === 'file->body, 0, 5) === '%PDF-') + { + return 'application/pdf'; + } + elseif (substr($this->file->body, 0, 11) === '%!PS-Adobe-') + { + return 'application/postscript'; + } + elseif (substr($this->file->body, 0, 6) === 'GIF87a' + || substr($this->file->body, 0, 6) === 'GIF89a') + { + return 'image/gif'; + } + elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A") + { + return 'image/png'; + } + elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF") + { + return 'image/jpeg'; + } + elseif (substr($this->file->body, 0, 2) === "\x42\x4D") + { + return 'image/bmp'; + } + else + { + return $this->text_or_binary(); + } + } + + /** + * Sniff images + * + * @access private + * @return string Actual Content-Type + */ + function image() + { + if (substr($this->file->body, 0, 6) === 'GIF87a' + || substr($this->file->body, 0, 6) === 'GIF89a') + { + return 'image/gif'; + } + elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A") + { + return 'image/png'; + } + elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF") + { + return 'image/jpeg'; + } + elseif (substr($this->file->body, 0, 2) === "\x42\x4D") + { + return 'image/bmp'; + } + else + { + return false; + } + } + + /** + * Sniff HTML + * + * @access private + * @return string Actual Content-Type + */ + function feed_or_html() + { + $len = strlen($this->file->body); + $pos = strspn($this->file->body, "\x09\x0A\x0D\x20"); + + while ($pos < $len) + { + switch ($this->file->body[$pos]) + { + case "\x09": + case "\x0A": + case "\x0D": + case "\x20": + $pos += strspn($this->file->body, "\x09\x0A\x0D\x20", $pos); + continue 2; + + case '<': + $pos++; + break; + + default: + return 'text/html'; + } + + if (substr($this->file->body, $pos, 3) === '!--') + { + $pos += 3; + if ($pos < $len && ($pos = strpos($this->file->body, '-->', $pos)) !== false) + { + $pos += 3; + } + else + { + return 'text/html'; + } + } + elseif (substr($this->file->body, $pos, 1) === '!') + { + if ($pos < $len && ($pos = strpos($this->file->body, '>', $pos)) !== false) + { + $pos++; + } + else + { + return 'text/html'; + } + } + elseif (substr($this->file->body, $pos, 1) === '?') + { + if ($pos < $len && ($pos = strpos($this->file->body, '?>', $pos)) !== false) + { + $pos += 2; + } + else + { + return 'text/html'; + } + } + elseif (substr($this->file->body, $pos, 3) === 'rss' + || substr($this->file->body, $pos, 7) === 'rdf:RDF') + { + return 'application/rss+xml'; + } + elseif (substr($this->file->body, $pos, 4) === 'feed') + { + return 'application/atom+xml'; + } + else + { + return 'text/html'; + } + } + + return 'text/html'; + } +} + +/** + * Parses the XML Declaration + * + * @package SimplePie + */ +class SimplePie_XML_Declaration_Parser +{ + /** + * XML Version + * + * @access public + * @var string + */ + var $version = '1.0'; + + /** + * Encoding + * + * @access public + * @var string + */ + var $encoding = 'UTF-8'; + + /** + * Standalone + * + * @access public + * @var bool + */ + var $standalone = false; + + /** + * Current state of the state machine + * + * @access private + * @var string + */ + var $state = 'before_version_name'; + + /** + * Input data + * + * @access private + * @var string + */ + var $data = ''; + + /** + * Input data length (to avoid calling strlen() everytime this is needed) + * + * @access private + * @var int + */ + var $data_length = 0; + + /** + * Current position of the pointer + * + * @var int + * @access private + */ + var $position = 0; + + /** + * Create an instance of the class with the input data + * + * @access public + * @param string $data Input data + */ + function SimplePie_XML_Declaration_Parser($data) + { + $this->data = $data; + $this->data_length = strlen($this->data); + } + + /** + * Parse the input data + * + * @access public + * @return bool true on success, false on failure + */ + function parse() + { + while ($this->state && $this->state !== 'emit' && $this->has_data()) + { + $state = $this->state; + $this->$state(); + } + $this->data = ''; + if ($this->state === 'emit') + { + return true; + } + else + { + $this->version = ''; + $this->encoding = ''; + $this->standalone = ''; + return false; + } + } + + /** + * Check whether there is data beyond the pointer + * + * @access private + * @return bool true if there is further data, false if not + */ + function has_data() + { + return (bool) ($this->position < $this->data_length); + } + + /** + * Advance past any whitespace + * + * @return int Number of whitespace characters passed + */ + function skip_whitespace() + { + $whitespace = strspn($this->data, "\x09\x0A\x0D\x20", $this->position); + $this->position += $whitespace; + return $whitespace; + } + + /** + * Read value + */ + function get_value() + { + $quote = substr($this->data, $this->position, 1); + if ($quote === '"' || $quote === "'") + { + $this->position++; + $len = strcspn($this->data, $quote, $this->position); + if ($this->has_data()) + { + $value = substr($this->data, $this->position, $len); + $this->position += $len + 1; + return $value; + } + } + return false; + } + + function before_version_name() + { + if ($this->skip_whitespace()) + { + $this->state = 'version_name'; + } + else + { + $this->state = false; + } + } + + function version_name() + { + if (substr($this->data, $this->position, 7) === 'version') + { + $this->position += 7; + $this->skip_whitespace(); + $this->state = 'version_equals'; + } + else + { + $this->state = false; + } + } + + function version_equals() + { + if (substr($this->data, $this->position, 1) === '=') + { + $this->position++; + $this->skip_whitespace(); + $this->state = 'version_value'; + } + else + { + $this->state = false; + } + } + + function version_value() + { + if ($this->version = $this->get_value()) + { + $this->skip_whitespace(); + if ($this->has_data()) + { + $this->state = 'encoding_name'; + } + else + { + $this->state = 'emit'; + } + } + else + { + $this->state = 'standalone_name'; + } + } + + function encoding_name() + { + if (substr($this->data, $this->position, 8) === 'encoding') + { + $this->position += 8; + $this->skip_whitespace(); + $this->state = 'encoding_equals'; + } + else + { + $this->state = false; + } + } + + function encoding_equals() + { + if (substr($this->data, $this->position, 1) === '=') + { + $this->position++; + $this->skip_whitespace(); + $this->state = 'encoding_value'; + } + else + { + $this->state = false; + } + } + + function encoding_value() + { + if ($this->encoding = $this->get_value()) + { + $this->skip_whitespace(); + if ($this->has_data()) + { + $this->state = 'standalone_name'; + } + else + { + $this->state = 'emit'; + } + } + else + { + $this->state = false; + } + } + + function standalone_name() + { + if (substr($this->data, $this->position, 10) === 'standalone') + { + $this->position += 10; + $this->skip_whitespace(); + $this->state = 'standalone_equals'; + } + else + { + $this->state = false; + } + } + + function standalone_equals() + { + if (substr($this->data, $this->position, 1) === '=') + { + $this->position++; + $this->skip_whitespace(); + $this->state = 'standalone_value'; + } + else + { + $this->state = false; + } + } + + function standalone_value() + { + if ($standalone = $this->get_value()) + { + switch ($standalone) + { + case 'yes': + $this->standalone = true; + break; + + case 'no': + $this->standalone = false; + break; + + default: + $this->state = false; + return; + } + + $this->skip_whitespace(); + if ($this->has_data()) + { + $this->state = false; + } + else + { + $this->state = 'emit'; + } + } + else + { + $this->state = false; + } + } +} + +class SimplePie_Locator +{ + var $useragent; + var $timeout; + var $file; + var $local = array(); + var $elsewhere = array(); + var $file_class = 'SimplePie_File'; + var $cached_entities = array(); + var $http_base; + var $base; + var $base_location = 0; + var $checked_feeds = 0; + var $max_checked_feeds = 10; + var $content_type_sniffer_class = 'SimplePie_Content_Type_Sniffer'; + + function SimplePie_Locator(&$file, $timeout = 10, $useragent = null, $file_class = 'SimplePie_File', $max_checked_feeds = 10, $content_type_sniffer_class = 'SimplePie_Content_Type_Sniffer') + { + $this->file =& $file; + $this->file_class = $file_class; + $this->useragent = $useragent; + $this->timeout = $timeout; + $this->max_checked_feeds = $max_checked_feeds; + $this->content_type_sniffer_class = $content_type_sniffer_class; + } + + function find($type = SIMPLEPIE_LOCATOR_ALL) + { + if ($this->is_feed($this->file)) + { + return $this->file; + } + + if ($this->file->method & SIMPLEPIE_FILE_SOURCE_REMOTE) + { + $sniffer = new $this->content_type_sniffer_class($this->file); + if ($sniffer->get_type() !== 'text/html') + { + return null; + } + } + + if ($type & ~SIMPLEPIE_LOCATOR_NONE) + { + $this->get_base(); + } + + if ($type & SIMPLEPIE_LOCATOR_AUTODISCOVERY && $working = $this->autodiscovery()) + { + return $working; + } + + if ($type & (SIMPLEPIE_LOCATOR_LOCAL_EXTENSION | SIMPLEPIE_LOCATOR_LOCAL_BODY | SIMPLEPIE_LOCATOR_REMOTE_EXTENSION | SIMPLEPIE_LOCATOR_REMOTE_BODY) && $this->get_links()) + { + if ($type & SIMPLEPIE_LOCATOR_LOCAL_EXTENSION && $working = $this->extension($this->local)) + { + return $working; + } + + if ($type & SIMPLEPIE_LOCATOR_LOCAL_BODY && $working = $this->body($this->local)) + { + return $working; + } + + if ($type & SIMPLEPIE_LOCATOR_REMOTE_EXTENSION && $working = $this->extension($this->elsewhere)) + { + return $working; + } + + if ($type & SIMPLEPIE_LOCATOR_REMOTE_BODY && $working = $this->body($this->elsewhere)) + { + return $working; + } + } + return null; + } + + function is_feed(&$file) + { + if ($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE) + { + $sniffer = new $this->content_type_sniffer_class($file); + $sniffed = $sniffer->get_type(); + if (in_array($sniffed, array('application/rss+xml', 'application/rdf+xml', 'application/atom+xml', 'text/xml', 'application/xml', 'text/plain'))) + { + return true; + } + else + { + return false; + } + } + elseif ($file->method & SIMPLEPIE_FILE_SOURCE_LOCAL) + { + return true; + } + else + { + return false; + } + } + + function get_base() + { + $this->http_base = $this->file->url; + $this->base = $this->http_base; + $elements = SimplePie_Misc::get_element('base', $this->file->body); + foreach ($elements as $element) + { + if ($element['attribs']['href']['data'] !== '') + { + $this->base = SimplePie_Misc::absolutize_url(trim($element['attribs']['href']['data']), $this->http_base); + $this->base_location = $element['offset']; + break; + } + } + } + + function autodiscovery() + { + $links = array_merge(SimplePie_Misc::get_element('link', $this->file->body), SimplePie_Misc::get_element('a', $this->file->body), SimplePie_Misc::get_element('area', $this->file->body)); + $done = array(); + foreach ($links as $link) + { + if ($this->checked_feeds == $this->max_checked_feeds) + { + break; + } + if (isset($link['attribs']['href']['data']) && isset($link['attribs']['rel']['data'])) + { + $rel = array_unique(SimplePie_Misc::space_seperated_tokens(strtolower($link['attribs']['rel']['data']))); + + if ($this->base_location < $link['offset']) + { + $href = SimplePie_Misc::absolutize_url(trim($link['attribs']['href']['data']), $this->base); + } + else + { + $href = SimplePie_Misc::absolutize_url(trim($link['attribs']['href']['data']), $this->http_base); + } + + if (!in_array($href, $done) && in_array('feed', $rel) || (in_array('alternate', $rel) && !empty($link['attribs']['type']['data']) && in_array(strtolower(SimplePie_Misc::parse_mime($link['attribs']['type']['data'])), array('application/rss+xml', 'application/atom+xml')))) + { + $this->checked_feeds++; + $feed =& new $this->file_class($href, $this->timeout, 5, null, $this->useragent); + if ($this->is_feed($feed)) + { + return $feed; + } + } + $done[] = $href; + } + } + return null; + } + + function get_links() + { + $links = SimplePie_Misc::get_element('a', $this->file->body); + foreach ($links as $link) + { + if (isset($link['attribs']['href']['data'])) + { + $href = trim($link['attribs']['href']['data']); + $parsed = SimplePie_Misc::parse_url($href); + if ($parsed['scheme'] === '' || preg_match('/^(http(s)|feed)?$/i', $parsed['scheme'])) + { + if ($this->base_location < $link['offset']) + { + $href = SimplePie_Misc::absolutize_url(trim($link['attribs']['href']['data']), $this->base); + } + else + { + $href = SimplePie_Misc::absolutize_url(trim($link['attribs']['href']['data']), $this->http_base); + } + + $current = SimplePie_Misc::parse_url($this->file->url); + + if ($parsed['authority'] === '' || $parsed['authority'] == $current['authority']) + { + $this->local[] = $href; + } + else + { + $this->elsewhere[] = $href; + } + } + } + } + $this->local = array_unique($this->local); + $this->elsewhere = array_unique($this->elsewhere); + if (!empty($this->local) || !empty($this->elsewhere)) + { + return true; + } + return null; + } + + function extension(&$array) + { + foreach ($array as $key => $value) + { + if ($this->checked_feeds == $this->max_checked_feeds) + { + break; + } + if (in_array(strtolower(strrchr($value, '.')), array('.rss', '.rdf', '.atom', '.xml'))) + { + $this->checked_feeds++; + $feed =& new $this->file_class($value, $this->timeout, 5, null, $this->useragent); + if ($this->is_feed($feed)) + { + return $feed; + } + else + { + unset($array[$key]); + } + } + } + return null; + } + + function body(&$array) + { + foreach ($array as $key => $value) + { + if ($this->checked_feeds == $this->max_checked_feeds) + { + break; + } + if (preg_match('/(rss|rdf|atom|xml)/i', $value)) + { + $this->checked_feeds++; + $feed =& new $this->file_class($value, $this->timeout, 5, null, $this->useragent); + if ($this->is_feed($feed)) + { + return $feed; + } + else + { + unset($array[$key]); + } + } + } + return null; + } +} + +class SimplePie_Parser +{ + var $error_code; + var $error_string; + var $current_line; + var $current_column; + var $current_byte; + var $separator = ' '; + var $feed = false; + var $namespace = array(''); + var $element = array(''); + var $xml_base = array(''); + var $xml_base_explicit = array(false); + var $xml_lang = array(''); + var $data = array(); + var $datas = array(array()); + var $current_xhtml_construct = -1; + var $encoding; + + function parse(&$data, $encoding) + { + // Use UTF-8 if we get passed US-ASCII, as every US-ASCII character is a UTF-8 character + if (strtoupper($encoding) == 'US-ASCII') + { + $this->encoding = 'UTF-8'; + } + else + { + $this->encoding = $encoding; + } + + // Strip BOM: + // UTF-32 Big Endian BOM + if (substr($data, 0, 4) === "\x00\x00\xFE\xFF") + { + $data = substr($data, 4); + } + // UTF-32 Little Endian BOM + elseif (substr($data, 0, 4) === "\xFF\xFE\x00\x00") + { + $data = substr($data, 4); + } + // UTF-16 Big Endian BOM + elseif (substr($data, 0, 2) === "\xFE\xFF") + { + $data = substr($data, 2); + } + // UTF-16 Little Endian BOM + elseif (substr($data, 0, 2) === "\xFF\xFE") + { + $data = substr($data, 2); + } + // UTF-8 BOM + elseif (substr($data, 0, 3) === "\xEF\xBB\xBF") + { + $data = substr($data, 3); + } + + if (substr($data, 0, 5) === '')) !== false) + { + $declaration = new SimplePie_XML_Declaration_Parser(substr($data, 5, $pos - 5)); + if ($declaration->parse()) + { + $data = substr($data, $pos + 2); + $data = 'version . '" encoding="' . $encoding . '" standalone="' . (($declaration->standalone) ? 'yes' : 'no') . '"?>' . $data; + } + else + { + $this->error_string = 'SimplePie bug! Please report this!'; + return false; + } + } + + $return = true; + + // Create the parser + $xml = xml_parser_create_ns($this->encoding, $this->separator); + xml_parser_set_option($xml, XML_OPTION_SKIP_WHITE, 1); + xml_parser_set_option($xml, XML_OPTION_CASE_FOLDING, 0); + xml_set_object($xml, $this); + xml_set_character_data_handler($xml, 'cdata'); + xml_set_element_handler($xml, 'tag_open', 'tag_close'); + + // Parse! + if (!xml_parse($xml, $data, true)) + { + $this->error_code = xml_get_error_code($xml); + $this->error_string = xml_error_string($this->error_code); + $return = false; + } + $this->current_line = xml_get_current_line_number($xml); + $this->current_column = xml_get_current_column_number($xml); + $this->current_byte = xml_get_current_byte_index($xml); + xml_parser_free($xml); + return $return; + } + + function get_error_code() + { + return $this->error_code; + } + + function get_error_string() + { + return $this->error_string; + } + + function get_current_line() + { + return $this->current_line; + } + + function get_current_column() + { + return $this->current_column; + } + + function get_current_byte() + { + return $this->current_byte; + } + + function get_data() + { + return $this->data; + } + + function tag_open($parser, $tag, $attributes) + { + if ($this->feed === 0) + { + return; + } + elseif ($this->feed == false) + { + if (in_array($tag, array( + SIMPLEPIE_NAMESPACE_ATOM_10 . $this->separator . 'feed', + SIMPLEPIE_NAMESPACE_ATOM_03 . $this->separator . 'feed', + 'rss', + SIMPLEPIE_NAMESPACE_RDF . $this->separator . 'RDF' + ))) + { + $this->feed = 1; + } + } + else + { + $this->feed++; + } + + list($this->namespace[], $this->element[]) = $this->split_ns($tag); + + $attribs = array(); + foreach ($attributes as $name => $value) + { + list($attrib_namespace, $attribute) = $this->split_ns($name); + $attribs[$attrib_namespace][$attribute] = $value; + } + + if (isset($attribs[SIMPLEPIE_NAMESPACE_XML]['base'])) + { + $this->xml_base[] = SimplePie_Misc::absolutize_url($attribs[SIMPLEPIE_NAMESPACE_XML]['base'], end($this->xml_base)); + $this->xml_base_explicit[] = true; + } + else + { + $this->xml_base[] = end($this->xml_base); + $this->xml_base_explicit[] = end($this->xml_base_explicit); + } + + if (isset($attribs[SIMPLEPIE_NAMESPACE_XML]['lang'])) + { + $this->xml_lang[] = $attribs[SIMPLEPIE_NAMESPACE_XML]['lang']; + } + else + { + $this->xml_lang[] = end($this->xml_lang); + } + + if ($this->current_xhtml_construct >= 0) + { + $this->current_xhtml_construct++; + if (end($this->namespace) == SIMPLEPIE_NAMESPACE_XHTML) + { + $this->data['data'] .= '<' . end($this->element); + if (isset($attribs[''])) + { + foreach ($attribs[''] as $name => $value) + { + $this->data['data'] .= ' ' . $name . '="' . htmlspecialchars($value, ENT_COMPAT, $this->encoding) . '"'; + } + } + $this->data['data'] .= '>'; + } + } + else + { + $this->datas[] =& $this->data; + $this->data =& $this->data['child'][end($this->namespace)][end($this->element)][]; + $this->data = array('data' => '', 'attribs' => $attribs, 'xml_base' => end($this->xml_base), 'xml_base_explicit' => end($this->xml_base_explicit), 'xml_lang' => end($this->xml_lang)); + if ((end($this->namespace) == SIMPLEPIE_NAMESPACE_ATOM_03 && in_array(end($this->element), array('title', 'tagline', 'copyright', 'info', 'summary', 'content')) && isset($attribs['']['mode']) && $attribs['']['mode'] == 'xml') + || (end($this->namespace) == SIMPLEPIE_NAMESPACE_ATOM_10 && in_array(end($this->element), array('rights', 'subtitle', 'summary', 'info', 'title', 'content')) && isset($attribs['']['type']) && $attribs['']['type'] == 'xhtml')) + { + $this->current_xhtml_construct = 0; + } + } + } + + function cdata($parser, $cdata) + { + if ($this->current_xhtml_construct >= 0) + { + $this->data['data'] .= htmlspecialchars($cdata, ENT_QUOTES, $this->encoding); + } + elseif ($this->feed > 1) + { + $this->data['data'] .= $cdata; + } + } + + function tag_close($parser, $tag) + { + if (!$this->feed) + { + return; + } + + if ($this->current_xhtml_construct >= 0) + { + $this->current_xhtml_construct--; + if (end($this->namespace) == SIMPLEPIE_NAMESPACE_XHTML && !in_array(end($this->element), array('area', 'base', 'basefont', 'br', 'col', 'frame', 'hr', 'img', 'input', 'isindex', 'link', 'meta', 'param'))) + { + $this->data['data'] .= 'element) . '>'; + } + } + if ($this->current_xhtml_construct == -1) + { + $this->data =& $this->datas[$this->feed]; + array_pop($this->datas); + } + + array_pop($this->element); + array_pop($this->namespace); + array_pop($this->xml_base); + array_pop($this->xml_base_explicit); + array_pop($this->xml_lang); + $this->feed--; + } + + function split_ns($string) + { + static $cache = array(); + if (!isset($cache[$string])) + { + if ($pos = strpos($string, $this->separator)) + { + static $separator_length; + if (!$separator_length) + { + $separator_length = strlen($this->separator); + } + $namespace = substr($string, 0, $pos); + $local_name = substr($string, $pos + $separator_length); + if (strtolower($namespace) === SIMPLEPIE_NAMESPACE_ITUNES) + { + $namespace = SIMPLEPIE_NAMESPACE_ITUNES; + } + + // Normalize the Media RSS namespaces + if ($namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG) + { + $namespace = SIMPLEPIE_NAMESPACE_MEDIARSS; + } + $cache[$string] = array($namespace, $local_name); + } + else + { + $cache[$string] = array('', $string); + } + } + return $cache[$string]; + } +} + +/** + * @todo Move to using an actual HTML parser (this will allow tags to be properly stripped, and to switch between HTML and XHTML), this will also make it easier to shorten a string while preserving HTML tags + */ +class SimplePie_Sanitize +{ + // Private vars + var $base; + + // Options + var $remove_div = true; + var $image_handler = ''; + var $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style'); + var $encode_instead_of_strip = false; + var $strip_attributes = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc'); + var $strip_comments = false; + var $output_encoding = 'UTF-8'; + var $enable_cache = true; + var $cache_location = './cache'; + var $cache_name_function = 'md5'; + var $cache_class = 'SimplePie_Cache'; + var $file_class = 'SimplePie_File'; + var $timeout = 10; + var $useragent = ''; + var $force_fsockopen = false; + + var $replace_url_attributes = array( + 'a' => 'href', + 'area' => 'href', + 'blockquote' => 'cite', + 'del' => 'cite', + 'form' => 'action', + 'img' => array('longdesc', 'src'), + 'input' => 'src', + 'ins' => 'cite', + 'q' => 'cite' + ); + + function remove_div($enable = true) + { + $this->remove_div = (bool) $enable; + } + + function set_image_handler($page = false) + { + if ($page) + { + $this->image_handler = (string) $page; + } + else + { + $this->image_handler = false; + } + } + + function pass_cache_data($enable_cache = true, $cache_location = './cache', $cache_name_function = 'md5', $cache_class = 'SimplePie_Cache') + { + if (isset($enable_cache)) + { + $this->enable_cache = (bool) $enable_cache; + } + + if ($cache_location) + { + $this->cache_location = (string) $cache_location; + } + + if ($cache_name_function) + { + $this->cache_name_function = (string) $cache_name_function; + } + + if ($cache_class) + { + $this->cache_class = (string) $cache_class; + } + } + + function pass_file_data($file_class = 'SimplePie_File', $timeout = 10, $useragent = '', $force_fsockopen = false) + { + if ($file_class) + { + $this->file_class = (string) $file_class; + } + + if ($timeout) + { + $this->timeout = (string) $timeout; + } + + if ($useragent) + { + $this->useragent = (string) $useragent; + } + + if ($force_fsockopen) + { + $this->force_fsockopen = (string) $force_fsockopen; + } + } + + function strip_htmltags($tags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style')) + { + if ($tags) + { + if (is_array($tags)) + { + $this->strip_htmltags = $tags; + } + else + { + $this->strip_htmltags = explode(',', $tags); + } + } + else + { + $this->strip_htmltags = false; + } + } + + function encode_instead_of_strip($encode = false) + { + $this->encode_instead_of_strip = (bool) $encode; + } + + function strip_attributes($attribs = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc')) + { + if ($attribs) + { + if (is_array($attribs)) + { + $this->strip_attributes = $attribs; + } + else + { + $this->strip_attributes = explode(',', $attribs); + } + } + else + { + $this->strip_attributes = false; + } + } + + function strip_comments($strip = false) + { + $this->strip_comments = (bool) $strip; + } + + function set_output_encoding($encoding = 'UTF-8') + { + $this->output_encoding = (string) $encoding; + } + + /** + * Set element/attribute key/value pairs of HTML attributes + * containing URLs that need to be resolved relative to the feed + * + * @access public + * @since 1.0 + * @param array $element_attribute Element/attribute key/value pairs + */ + function set_url_replacements($element_attribute = array('a' => 'href', 'area' => 'href', 'blockquote' => 'cite', 'del' => 'cite', 'form' => 'action', 'img' => array('longdesc', 'src'), 'input' => 'src', 'ins' => 'cite', 'q' => 'cite')) + { + $this->replace_url_attributes = (array) $element_attribute; + } + + function sanitize($data, $type, $base = '') + { + $data = trim($data); + if ($data !== '' || $type & SIMPLEPIE_CONSTRUCT_IRI) + { + if ($type & SIMPLEPIE_CONSTRUCT_MAYBE_HTML) + { + if (preg_match('/(&(#(x[0-9a-fA-F]+|[0-9]+)|[a-zA-Z0-9]+)|<\/[A-Za-z][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E]*' . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . '>)/', $data)) + { + $type |= SIMPLEPIE_CONSTRUCT_HTML; + } + else + { + $type |= SIMPLEPIE_CONSTRUCT_TEXT; + } + } + + if ($type & SIMPLEPIE_CONSTRUCT_BASE64) + { + $data = base64_decode($data); + } + + if ($type & SIMPLEPIE_CONSTRUCT_XHTML) + { + if ($this->remove_div) + { + $data = preg_replace('/^/', '', $data); + $data = preg_replace('/<\/div>$/', '', $data); + } + else + { + $data = preg_replace('/^/', '
    ', $data); + } + } + + if ($type & (SIMPLEPIE_CONSTRUCT_HTML | SIMPLEPIE_CONSTRUCT_XHTML)) + { + // Strip comments + if ($this->strip_comments) + { + $data = SimplePie_Misc::strip_comments($data); + } + + // Strip out HTML tags and attributes that might cause various security problems. + // Based on recommendations by Mark Pilgrim at: + // http://diveintomark.org/archives/2003/06/12/how_to_consume_rss_safely + if ($this->strip_htmltags) + { + foreach ($this->strip_htmltags as $tag) + { + $pcre = "/<($tag)" . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . "(>(.*)<\/$tag" . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . '>|(\/)?>)/siU'; + while (preg_match($pcre, $data)) + { + $data = preg_replace_callback($pcre, array(&$this, 'do_strip_htmltags'), $data); + } + } + } + + if ($this->strip_attributes) + { + foreach ($this->strip_attributes as $attrib) + { + $data = preg_replace('/(<[A-Za-z][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E]*)' . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . trim($attrib) . '(?:\s*=\s*(?:"(?:[^"]*)"|\'(?:[^\']*)\'|(?:[^\x09\x0A\x0B\x0C\x0D\x20\x22\x27\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x3E]*)?))?' . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . '>/', '\1\2\3>', $data); + } + } + + // Replace relative URLs + $this->base = $base; + foreach ($this->replace_url_attributes as $element => $attributes) + { + $data = $this->replace_urls($data, $element, $attributes); + } + + // If image handling (caching, etc.) is enabled, cache and rewrite all the image tags. + if (isset($this->image_handler) && ((string) $this->image_handler) !== '' && $this->enable_cache) + { + $images = SimplePie_Misc::get_element('img', $data); + foreach ($images as $img) + { + if (isset($img['attribs']['src']['data'])) + { + $image_url = call_user_func($this->cache_name_function, $img['attribs']['src']['data']); + $cache = call_user_func(array($this->cache_class, 'create'), $this->cache_location, $image_url, 'spi'); + + if ($cache->load()) + { + $img['attribs']['src']['data'] = $this->image_handler . $image_url; + $data = str_replace($img['full'], SimplePie_Misc::element_implode($img), $data); + } + else + { + $file =& new $this->file_class($img['attribs']['src']['data'], $this->timeout, 5, array('X-FORWARDED-FOR' => $_SERVER['REMOTE_ADDR']), $this->useragent, $this->force_fsockopen); + $headers = $file->headers; + + if ($file->success && ($file->status_code == 200 || ($file->status_code > 206 && $file->status_code < 300))) + { + if ($cache->save(array('headers' => $file->headers, 'body' => $file->body))) + { + $img['attribs']['src']['data'] = $this->image_handler . $image_url; + $data = str_replace($img['full'], SimplePie_Misc::element_implode($img), $data); + } + else + { + trigger_error("$cache->name is not writeable", E_USER_WARNING); + } + } + } + } + } + } + + // Having (possibly) taken stuff out, there may now be whitespace at the beginning/end of the data + $data = trim($data); + } + + if ($type & SIMPLEPIE_CONSTRUCT_IRI) + { + $data = SimplePie_Misc::absolutize_url($data, $base); + } + + if ($type & (SIMPLEPIE_CONSTRUCT_TEXT | SIMPLEPIE_CONSTRUCT_IRI)) + { + $data = htmlspecialchars($data, ENT_COMPAT, 'UTF-8'); + } + + if ($this->output_encoding != 'UTF-8') + { + $data = SimplePie_Misc::change_encoding($data, 'UTF-8', $this->output_encoding); + } + } + return $data; + } + + function replace_urls($data, $tag, $attributes) + { + if (!is_array($this->strip_htmltags) || !in_array($tag, $this->strip_htmltags)) + { + $elements = SimplePie_Misc::get_element($tag, $data); + foreach ($elements as $element) + { + if (is_array($attributes)) + { + foreach ($attributes as $attribute) + { + if (isset($element['attribs'][$attribute]['data'])) + { + $element['attribs'][$attribute]['data'] = SimplePie_Misc::absolutize_url($element['attribs'][$attribute]['data'], $this->base); + $new_element = SimplePie_Misc::element_implode($element); + $data = str_replace($element['full'], $new_element, $data); + $element['full'] = $new_element; + } + } + } + elseif (isset($element['attribs'][$attributes]['data'])) + { + $element['attribs'][$attributes]['data'] = SimplePie_Misc::absolutize_url($element['attribs'][$attributes]['data'], $this->base); + $data = str_replace($element['full'], SimplePie_Misc::element_implode($element), $data); + } + } + } + return $data; + } + + function do_strip_htmltags($match) + { + if ($this->encode_instead_of_strip) + { + if (isset($match[4]) && !in_array(strtolower($match[1]), array('script', 'style'))) + { + $match[1] = htmlspecialchars($match[1], ENT_COMPAT, 'UTF-8'); + $match[2] = htmlspecialchars($match[2], ENT_COMPAT, 'UTF-8'); + return "<$match[1]$match[2]>$match[3]</$match[1]>"; + } + else + { + return htmlspecialchars($match[0], ENT_COMPAT, 'UTF-8'); + } + } + elseif (isset($match[4]) && !in_array(strtolower($match[1]), array('script', 'style'))) + { + return $match[4]; + } + else + { + return ''; + } + } +} + +?> \ No newline at end of file diff --git a/includes/spyc/README b/includes/spyc/README new file mode 100644 index 0000000..a8539bd --- /dev/null +++ b/includes/spyc/README @@ -0,0 +1,159 @@ +# +# S P Y C +# a simple php yaml class +# +# Load this README! +# >> $readme = Spyc::YAMLLoad('README'); +# +--- %YAML:1.1 +title: Spyc -- a Simple PHP YAML Class +version: 0.4.5 +authors: [chris wanstrath (chris@ozmm.org), vlad andersen (vlad.andersen@gmail.com)] +websites: [http://www.yaml.org, http://spyc.sourceforge.net] +license: [MIT License, http://www.opensource.org/licenses/mit-license.php] +copyright: "(c) 2005-2006 Chris Wanstrath, 2006-2009 Vlad Andersen" +tested on: [php 5.2.x] + +installation: > + Copy spyc.php to a directory you can + access with your YAML-ready PHP script. + + That's it! + +about: > + From www.yaml.org: + + "YAML(tm) (rhymes with 'camel') is a human-friendly, cross language, + Unicode based data serialization language designed around the common + native data structures of agile programming languages. It is broadly + useful for programming needs ranging from configuration files to + Internet messaging to object persistence to data auditing. Together + with the Unicode standard for characters, the YAML specification provides + all the information necessary to understand YAML Version 1.1 and to + creating programs that process YAML information. + + YAML(tm) is a balance of the following design goals: + - YAML documents are very readable by humans. + - YAML interacts well with scripting languages. + - YAML uses host languages' native data structures. + - YAML has a consistent information model. + - YAML enables stream-based processing. + - YAML is expressive and extensible. + - YAML is easy to implement." + + YAML makes a lot of sense. It's easy to use, easy to learn, and cool. + As the lucky stiff named why once said, "YAML is a beacon of light." + + If you're new to YAML, may we suggest YAML In Five Minutes: + - http://yaml.kwiki.org/?YamlInFiveMinutes + + If you don't have five minutes, realize that this README is a completely + valid YAML document. Dig in, load this or any YAML file into an array + with Spyc and see how easy it is to translate friendly text into usable + data. + + The purpose of Spyc is to provide a pure PHP alternative to Syck, a + simple API for loading and dumping YAML documents, a YAML loader which + understands a usable subset of the YAML spec, and to further spread + the glory of YAML to the PHP masses. + + If you're at all hesitant ("usable subset of YAML?!"), navigate + http://yaml.org/start.html. Spyc completely understands the YAML + document shown there, a document which has features way beyond the + scope of what normal config files might require. Try it for yourself, + and then start enjoying the peace of mind YAML brings to your life. + +meat and a few potatoes: + - concept: Loading a YAML document into PHP + brief: > + $yaml will become an array of all the data in wicked.yaml + code: | + + include('spyc.php'); + + $yaml = Spyc::YAMLLoad('wicked.yaml'); + + - concept: Loading a YAML string into PHP + brief: > + $array will look like this: + array('A YAML','document in a','string') + code: | + + include('spyc.php'); + + $yaml = '- A YAML\n- document in a\n- string.'; + $array = Spyc::YAMLLoad($yaml); + + - concept: Dumping a PHP array to YAML + brief: > + $yaml will become a string of a YAML document created from + $array. + code: | + + include('spyc.php'); + + $array['name'] = 'chris'; + $array['sport'] = 'curbing'; + + $yaml = Spyc::YAMLDump($array); + +prior art: + - who: [Brian Ingerson, Clark Evans, Oren Ben-Kiki] + why?: > + The YAML spec is really a piece of work, and these guys + did a great job on it. A simple and elegant language like + YAML was a long time coming and it's refreshing to know + such able minded individuals took the task to heart and + executed it with cunning and strength. In addition to + their various noteworthy contributions to YAML parsers + and related projects, YAML.pm's README is a treasure trove + of information for knowledge seekers. Thanks, guys. + + - who: why the lucky stiff + why?: > + As the author of Syck, the code used in Ruby for the language's + YAML class and methods, why is indirectly (directly?) responsible + for my first exposure to YAML (as a config file in a Ruby web-app) + and the countless hours I spent playing with this sheik new data + format afterwards. Syck's README is a YAML file and thus the + inspiration for this file and, even, this very piece of software. + + - who: Steve Howell + why?: > + Python's YAML implementation. PyYAML's README file is also YAML, + so it too inspired the YAML format of this README file. + + - who: [Rasmus Lerdorf, Zeev Suraski, Andi Gutmans, et al] + why?: > + PHP is great at what it does best. It's also paid a lot of my bills. + Thanks. + +bugs: + report: > + Please see Spyc's Sourceforge project page for information on reporting bugs. + speed: > + This implementation was not designed for speed. Rather, it + was designed for those who need a pure PHP implementation of + a YAML parser and who are not overly concerned with performance. + If you want speed, check out Syck. + depth: > + This parser is by no means a comprehensive YAML parser. For supported + features and future plans, check the website. + unicode: > + YAML is supposed to be unicode, but for now we're just using ASCII. + PHP has crappy unicode support but who knows what the future holds. + +resources: + - http://www.yaml.org + - http://www.yaml.org/spec/ + - http://yaml.kwiki.org/?YamlInFiveMinutes + - http://www.whytheluckystiff.net/syck/ + - http://yaml4r.sourceforge.net/cookbook/ + +thanks: + - Adam Wood + - Daniel Ferreira + - Aaron Jensen + - Mike Thornton + - Fabien Potencier + - Mustafa Kumas \ No newline at end of file diff --git a/includes/spyc/examples/yaml-dump.php b/includes/spyc/examples/yaml-dump.php new file mode 100644 index 0000000..05a8a2f --- /dev/null +++ b/includes/spyc/examples/yaml-dump.php @@ -0,0 +1,25 @@ + 'A sequence','second' => 'of mapped values'); +$array['Mapped'] = array('A sequence','which is mapped'); +$array['A Note'] = 'What if your text is too long?'; +$array['Another Note'] = 'If that is the case, the dumper will probably fold your text by using a block. Kinda like this.'; +$array['The trick?'] = 'The trick is that we overrode the default indent, 2, to 4 and the default wordwrap, 40, to 60.'; +$array['Old Dog'] = "And if you want\n to preserve line breaks, \ngo ahead!"; +$array['key:withcolon'] = "Should support this to"; + +$yaml = Spyc::YAMLDump($array,4,60); \ No newline at end of file diff --git a/includes/spyc/examples/yaml-load.php b/includes/spyc/examples/yaml-load.php new file mode 100644 index 0000000..3e1a4a7 --- /dev/null +++ b/includes/spyc/examples/yaml-load.php @@ -0,0 +1,25 @@ +spyc.yaml loaded into PHP:
    '; +print_r($array); +echo ''; + + +echo '
    YAML Data dumped back:
    '; +echo Spyc::YAMLDump($array); +echo '
    '; diff --git a/includes/spyc/php4/5to4.php b/includes/spyc/php4/5to4.php new file mode 100644 index 0000000..2789620 --- /dev/null +++ b/includes/spyc/php4/5to4.php @@ -0,0 +1,16 @@ +', $code); + $f = fopen ($dest, 'w'); + fwrite($f, $code); + fclose ($f); + print "Written to $dest.\n"; +} \ No newline at end of file diff --git a/includes/spyc/php4/spyc.php4 b/includes/spyc/php4/spyc.php4 new file mode 100644 index 0000000..040ff74 --- /dev/null +++ b/includes/spyc/php4/spyc.php4 @@ -0,0 +1,1023 @@ + + * @author Chris Wanstrath + * @link http://code.google.com/p/spyc/ + * @copyright Copyright 2005-2006 Chris Wanstrath, 2006-2009 Vlad Andersen + * @license http://www.opensource.org/licenses/mit-license.php MIT License + * @package Spyc + */ + +if (!function_exists('spyc_load')) { + /** + * Parses YAML to array. + * @param string $string YAML string. + * @return array + */ + function spyc_load ($string) { + return Spyc::YAMLLoadString($string); + } +} + +if (!function_exists('spyc_load_file')) { + /** + * Parses YAML to array. + * @param string $file Path to YAML file. + * @return array + */ + function spyc_load_file ($file) { + return Spyc::YAMLLoad($file); + } +} + +/** + * The Simple PHP YAML Class. + * + * This class can be used to read a YAML file and convert its contents + * into a PHP array. It currently supports a very limited subsection of + * the YAML spec. + * + * Usage: + * + * $Spyc = new Spyc; + * $array = $Spyc->load($file); + * + * or: + * + * $array = Spyc::YAMLLoad($file); + * + * or: + * + * $array = spyc_load_file($file); + * + * @package Spyc + */ +class Spyc { + + // SETTINGS + + /** + * Setting this to true will force YAMLDump to enclose any string value in + * quotes. False by default. + * + * @var bool + */ + var $setting_dump_force_quotes = false; + + /** + * Setting this to true will forse YAMLLoad to use syck_load function when + * possible. False by default. + * @var bool + */ + var $setting_use_syck_is_possible = false; + + + + /**#@+ + * @access private + * @var mixed + */ + var $_dumpIndent; + var $_dumpWordWrap; + var $_containsGroupAnchor = false; + var $_containsGroupAlias = false; + var $path; + var $result; + var $LiteralPlaceHolder = '___YAML_Literal_Block___'; + var $SavedGroups = array(); + var $indent; + /** + * Path modifier that should be applied after adding current element. + * @var array + */ + var $delayedPath = array(); + + /**#@+ + * @access public + * @var mixed + */ + var $_nodeId; + +/** + * Load a valid YAML string to Spyc. + * @param string $input + * @return array + */ + function load ($input) { + return $this->__loadString($input); + } + + /** + * Load a valid YAML file to Spyc. + * @param string $file + * @return array + */ + function loadFile ($file) { + return $this->__load($file); + } + + /** + * Load YAML into a PHP array statically + * + * The load method, when supplied with a YAML stream (string or file), + * will do its best to convert YAML in a file into a PHP array. Pretty + * simple. + * Usage: + * + * $array = Spyc::YAMLLoad('lucky.yaml'); + * print_r($array); + * + * @access public + * @return array + * @param string $input Path of YAML file or string containing YAML + */ + function YAMLLoad($input) { + $Spyc = new Spyc; + return $Spyc->__load($input); + } + + /** + * Load a string of YAML into a PHP array statically + * + * The load method, when supplied with a YAML string, will do its best + * to convert YAML in a string into a PHP array. Pretty simple. + * + * Note: use this function if you don't want files from the file system + * loaded and processed as YAML. This is of interest to people concerned + * about security whose input is from a string. + * + * Usage: + * + * $array = Spyc::YAMLLoadString("---\n0: hello world\n"); + * print_r($array); + * + * @access public + * @return array + * @param string $input String containing YAML + */ + function YAMLLoadString($input) { + $Spyc = new Spyc; + return $Spyc->__loadString($input); + } + + /** + * Dump YAML from PHP array statically + * + * The dump method, when supplied with an array, will do its best + * to convert the array into friendly YAML. Pretty simple. Feel free to + * save the returned string as nothing.yaml and pass it around. + * + * Oh, and you can decide how big the indent is and what the wordwrap + * for folding is. Pretty cool -- just pass in 'false' for either if + * you want to use the default. + * + * Indent's default is 2 spaces, wordwrap's default is 40 characters. And + * you can turn off wordwrap by passing in 0. + * + * @access public + * @return string + * @param array $array PHP array + * @param int $indent Pass in false to use the default, which is 2 + * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40) + */ + function YAMLDump($array,$indent = false,$wordwrap = false) { + $spyc = new Spyc; + return $spyc->dump($array,$indent,$wordwrap); + } + + + /** + * Dump PHP array to YAML + * + * The dump method, when supplied with an array, will do its best + * to convert the array into friendly YAML. Pretty simple. Feel free to + * save the returned string as tasteful.yaml and pass it around. + * + * Oh, and you can decide how big the indent is and what the wordwrap + * for folding is. Pretty cool -- just pass in 'false' for either if + * you want to use the default. + * + * Indent's default is 2 spaces, wordwrap's default is 40 characters. And + * you can turn off wordwrap by passing in 0. + * + * @access public + * @return string + * @param array $array PHP array + * @param int $indent Pass in false to use the default, which is 2 + * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40) + */ + function dump($array,$indent = false,$wordwrap = false) { + // Dumps to some very clean YAML. We'll have to add some more features + // and options soon. And better support for folding. + + // New features and options. + if ($indent === false or !is_numeric($indent)) { + $this->_dumpIndent = 2; + } else { + $this->_dumpIndent = $indent; + } + + if ($wordwrap === false or !is_numeric($wordwrap)) { + $this->_dumpWordWrap = 40; + } else { + $this->_dumpWordWrap = $wordwrap; + } + + // New YAML document + $string = "---\n"; + + // Start at the base of the array and move through it. + if ($array) { + $array = (array)$array; + $first_key = key($array); + + $previous_key = -1; + foreach ($array as $key => $value) { + $string .= $this->_yamlize($key,$value,0,$previous_key, $first_key); + $previous_key = $key; + } + } + return $string; + } + + /** + * Attempts to convert a key / value array item to YAML + * @access private + * @return string + * @param $key The name of the key + * @param $value The value of the item + * @param $indent The indent of the current node + */ + function _yamlize($key,$value,$indent, $previous_key = -1, $first_key = 0) { + if (is_array($value)) { + if (empty ($value)) + return $this->_dumpNode($key, array(), $indent, $previous_key, $first_key); + // It has children. What to do? + // Make it the right kind of item + $string = $this->_dumpNode($key, NULL, $indent, $previous_key, $first_key); + // Add the indent + $indent += $this->_dumpIndent; + // Yamlize the array + $string .= $this->_yamlizeArray($value,$indent); + } elseif (!is_array($value)) { + // It doesn't have children. Yip. + $string = $this->_dumpNode($key, $value, $indent, $previous_key, $first_key); + } + return $string; + } + + /** + * Attempts to convert an array to YAML + * @access private + * @return string + * @param $array The array you want to convert + * @param $indent The indent of the current level + */ + function _yamlizeArray($array,$indent) { + if (is_array($array)) { + $string = ''; + $previous_key = -1; + $first_key = key($array); + foreach ($array as $key => $value) { + $string .= $this->_yamlize($key, $value, $indent, $previous_key, $first_key); + $previous_key = $key; + } + return $string; + } else { + return false; + } + } + + /** + * Returns YAML from a key and a value + * @access private + * @return string + * @param $key The name of the key + * @param $value The value of the item + * @param $indent The indent of the current node + */ + function _dumpNode($key, $value, $indent, $previous_key = -1, $first_key = 0) { + // do some folding here, for blocks + if (is_string ($value) && ((strpos($value,"\n") !== false || strpos($value,": ") !== false || strpos($value,"- ") !== false || + strpos($value,"*") !== false || strpos($value,"#") !== false || strpos($value,"<") !== false || strpos($value,">") !== false || + strpos($value,"[") !== false || strpos($value,"]") !== false || strpos($value,"{") !== false || strpos($value,"}") !== false) || substr ($value, -1, 1) == ':')) { + $value = $this->_doLiteralBlock($value,$indent); + } else { + $value = $this->_doFolding($value,$indent); + if (is_bool($value)) { + $value = ($value) ? "true" : "false"; + } + } + + if ($value === array()) $value = '[ ]'; + + $spaces = str_repeat(' ',$indent); + + if (is_int($key) && $key - 1 == $previous_key && $first_key===0) { + // It's a sequence + $string = $spaces.'- '.$value."\n"; + } else { + if ($first_key===0) throw new Exception('Keys are all screwy. The first one was zero, now it\'s "'. $key .'"'); + // It's mapped + if (strpos($key, ":") !== false) { $key = '"' . $key . '"'; } + $string = $spaces.$key.': '.$value."\n"; + } + return $string; + } + + /** + * Creates a literal block for dumping + * @access private + * @return string + * @param $value + * @param $indent int The value of the indent + */ + function _doLiteralBlock($value,$indent) { + if (strpos($value, "\n") === false && strpos($value, "'") === false) { + return sprintf ("'%s'", $value); + } + if (strpos($value, "\n") === false && strpos($value, '"') === false) { + return sprintf ('"%s"', $value); + } + $exploded = explode("\n",$value); + $newValue = '|'; + $indent += $this->_dumpIndent; + $spaces = str_repeat(' ',$indent); + foreach ($exploded as $line) { + $newValue .= "\n" . $spaces . trim($line); + } + return $newValue; + } + + /** + * Folds a string of text, if necessary + * @access private + * @return string + * @param $value The string you wish to fold + */ + function _doFolding($value,$indent) { + // Don't do anything if wordwrap is set to 0 + + if ($this->_dumpWordWrap !== 0 && is_string ($value) && strlen($value) > $this->_dumpWordWrap) { + $indent += $this->_dumpIndent; + $indent = str_repeat(' ',$indent); + $wrapped = wordwrap($value,$this->_dumpWordWrap,"\n$indent"); + $value = ">\n".$indent.$wrapped; + } else { + if ($this->setting_dump_force_quotes && is_string ($value)) + $value = '"' . $value . '"'; + } + + + return $value; + } + +// LOADING FUNCTIONS + + function __load($input) { + $Source = $this->loadFromSource($input); + return $this->loadWithSource($Source); + } + + function __loadString($input) { + $Source = $this->loadFromString($input); + return $this->loadWithSource($Source); + } + + function loadWithSource($Source) { + if (empty ($Source)) return array(); + if ($this->setting_use_syck_is_possible && function_exists ('syck_load')) { + $array = syck_load (implode ('', $Source)); + return is_array($array) ? $array : array(); + } + + $this->path = array(); + $this->result = array(); + + $cnt = count($Source); + for ($i = 0; $i < $cnt; $i++) { + $line = $Source[$i]; + + $this->indent = strlen($line) - strlen(ltrim($line)); + $tempPath = $this->getParentPathByIndent($this->indent); + $line = $this->stripIndent($line, $this->indent); + if ($this->isComment($line)) continue; + if ($this->isEmpty($line)) continue; + $this->path = $tempPath; + + $literalBlockStyle = $this->startsLiteralBlock($line); + if ($literalBlockStyle) { + $line = rtrim ($line, $literalBlockStyle . " \n"); + $literalBlock = ''; + $line .= $this->LiteralPlaceHolder; + + while (++$i < $cnt && $this->literalBlockContinues($Source[$i], $this->indent)) { + $literalBlock = $this->addLiteralLine($literalBlock, $Source[$i], $literalBlockStyle); + } + $i--; + } + + while (++$i < $cnt && $this->greedilyNeedNextLine($line)) { + $line = rtrim ($line, " \n\t\r") . ' ' . ltrim ($Source[$i], " \t"); + } + $i--; + + + + if (strpos ($line, '#')) { + if (strpos ($line, '"') === false && strpos ($line, "'") === false) + $line = preg_replace('/\s+#(.+)$/','',$line); + } + + $lineArray = $this->_parseLine($line); + + if ($literalBlockStyle) + $lineArray = $this->revertLiteralPlaceHolder ($lineArray, $literalBlock); + + $this->addArray($lineArray, $this->indent); + + foreach ($this->delayedPath as $indent => $delayedPath) + $this->path[$indent] = $delayedPath; + + $this->delayedPath = array(); + + } + return $this->result; + } + + function loadFromSource ($input) { + if (!empty($input) && strpos($input, "\n") === false && file_exists($input)) + return file($input); + + return $this->loadFromString($input); + } + + function loadFromString ($input) { + $lines = explode("\n",$input); + foreach ($lines as $k => $_) { + $lines[$k] = rtrim ($_, "\r"); + } + return $lines; + } + + /** + * Parses YAML code and returns an array for a node + * @access private + * @return array + * @param string $line A line from the YAML file + */ + function _parseLine($line) { + if (!$line) return array(); + $line = trim($line); + + if (!$line) return array(); + $array = array(); + + $group = $this->nodeContainsGroup($line); + if ($group) { + $this->addGroup($line, $group); + $line = $this->stripGroup ($line, $group); + } + + if ($this->startsMappedSequence($line)) + return $this->returnMappedSequence($line); + + if ($this->startsMappedValue($line)) + return $this->returnMappedValue($line); + + if ($this->isArrayElement($line)) + return $this->returnArrayElement($line); + + if ($this->isPlainArray($line)) + return $this->returnPlainArray($line); + + + return $this->returnKeyValuePair($line); + + } + + /** + * Finds the type of the passed value, returns the value as the new type. + * @access private + * @param string $value + * @return mixed + */ + function _toType($value) { + if ($value === '') return null; + $first_character = $value[0]; + $last_character = substr($value, -1, 1); + + $is_quoted = false; + do { + if (!$value) break; + if ($first_character != '"' && $first_character != "'") break; + if ($last_character != '"' && $last_character != "'") break; + $is_quoted = true; + } while (0); + + if ($is_quoted) + return strtr(substr ($value, 1, -1), array ('\\"' => '"', '\'\'' => '\'', '\\\'' => '\'')); + + if (strpos($value, ' #') !== false) + $value = preg_replace('/\s+#(.+)$/','',$value); + + if ($first_character == '[' && $last_character == ']') { + // Take out strings sequences and mappings + $innerValue = trim(substr ($value, 1, -1)); + if ($innerValue === '') return array(); + $explode = $this->_inlineEscape($innerValue); + // Propagate value array + $value = array(); + foreach ($explode as $v) { + $value[] = $this->_toType($v); + } + return $value; + } + + if (strpos($value,': ')!==false && $first_character != '{') { + $array = explode(': ',$value); + $key = trim($array[0]); + array_shift($array); + $value = trim(implode(': ',$array)); + $value = $this->_toType($value); + return array($key => $value); + } + + if ($first_character == '{' && $last_character == '}') { + $innerValue = trim(substr ($value, 1, -1)); + if ($innerValue === '') return array(); + // Inline Mapping + // Take out strings sequences and mappings + $explode = $this->_inlineEscape($innerValue); + // Propagate value array + $array = array(); + foreach ($explode as $v) { + $SubArr = $this->_toType($v); + if (empty($SubArr)) continue; + if (is_array ($SubArr)) { + $array[key($SubArr)] = $SubArr[key($SubArr)]; continue; + } + $array[] = $SubArr; + } + return $array; + } + + if ($value == 'null' || $value == 'NULL' || $value == 'Null' || $value == '' || $value == '~') { + return null; + } + + if (intval($first_character) > 0 && preg_match ('/^[1-9]+[0-9]*$/', $value)) { + $intvalue = (int)$value; + if ($intvalue != PHP_INT_MAX) + $value = $intvalue; + return $value; + } + + if (in_array($value, + array('true', 'on', '+', 'yes', 'y', 'True', 'TRUE', 'On', 'ON', 'YES', 'Yes', 'Y'))) { + return true; + } + + if (in_array(strtolower($value), + array('false', 'off', '-', 'no', 'n'))) { + return false; + } + + if (is_numeric($value)) { + if ($value === '0') return 0; + if (trim ($value, 0) === $value) + $value = (float)$value; + return $value; + } + + return $value; + } + + /** + * Used in inlines to check for more inlines or quoted strings + * @access private + * @return array + */ + function _inlineEscape($inline) { + // There's gotta be a cleaner way to do this... + // While pure sequences seem to be nesting just fine, + // pure mappings and mappings with sequences inside can't go very + // deep. This needs to be fixed. + + $seqs = array(); + $maps = array(); + $saved_strings = array(); + + // Check for strings + $regex = '/(?:(")|(?:\'))((?(1)[^"]+|[^\']+))(?(1)"|\')/'; + if (preg_match_all($regex,$inline,$strings)) { + $saved_strings = $strings[0]; + $inline = preg_replace($regex,'YAMLString',$inline); + } + unset($regex); + + $i = 0; + do { + + // Check for sequences + while (preg_match('/\[([^{}\[\]]+)\]/U',$inline,$matchseqs)) { + $seqs[] = $matchseqs[0]; + $inline = preg_replace('/\[([^{}\[\]]+)\]/U', ('YAMLSeq' . (count($seqs) - 1) . 's'), $inline, 1); + } + + // Check for mappings + while (preg_match('/{([^\[\]{}]+)}/U',$inline,$matchmaps)) { + $maps[] = $matchmaps[0]; + $inline = preg_replace('/{([^\[\]{}]+)}/U', ('YAMLMap' . (count($maps) - 1) . 's'), $inline, 1); + } + + if ($i++ >= 10) break; + + } while (strpos ($inline, '[') !== false || strpos ($inline, '{') !== false); + + $explode = explode(', ',$inline); + $stringi = 0; $i = 0; + + while (1) { + + // Re-add the sequences + if (!empty($seqs)) { + foreach ($explode as $key => $value) { + if (strpos($value,'YAMLSeq') !== false) { + foreach ($seqs as $seqk => $seq) { + $explode[$key] = str_replace(('YAMLSeq'.$seqk.'s'),$seq,$value); + $value = $explode[$key]; + } + } + } + } + + // Re-add the mappings + if (!empty($maps)) { + foreach ($explode as $key => $value) { + if (strpos($value,'YAMLMap') !== false) { + foreach ($maps as $mapk => $map) { + $explode[$key] = str_replace(('YAMLMap'.$mapk.'s'), $map, $value); + $value = $explode[$key]; + } + } + } + } + + + // Re-add the strings + if (!empty($saved_strings)) { + foreach ($explode as $key => $value) { + while (strpos($value,'YAMLString') !== false) { + $explode[$key] = preg_replace('/YAMLString/',$saved_strings[$stringi],$value, 1); + unset($saved_strings[$stringi]); + ++$stringi; + $value = $explode[$key]; + } + } + } + + $finished = true; + foreach ($explode as $key => $value) { + if (strpos($value,'YAMLSeq') !== false) { + $finished = false; break; + } + if (strpos($value,'YAMLMap') !== false) { + $finished = false; break; + } + if (strpos($value,'YAMLString') !== false) { + $finished = false; break; + } + } + if ($finished) break; + + $i++; + if ($i > 10) + break; // Prevent infinite loops. + } + + return $explode; + } + + function literalBlockContinues ($line, $lineIndent) { + if (!trim($line)) return true; + if (strlen($line) - strlen(ltrim($line)) > $lineIndent) return true; + return false; + } + + function referenceContentsByAlias ($alias) { + do { + if (!isset($this->SavedGroups[$alias])) { echo "Bad group name: $alias."; break; } + $groupPath = $this->SavedGroups[$alias]; + $value = $this->result; + foreach ($groupPath as $k) { + $value = $value[$k]; + } + } while (false); + return $value; + } + + function addArrayInline ($array, $indent) { + $CommonGroupPath = $this->path; + if (empty ($array)) return false; + + foreach ($array as $k => $_) { + $this->addArray(array($k => $_), $indent); + $this->path = $CommonGroupPath; + } + return true; + } + + function addArray ($incoming_data, $incoming_indent) { + + // print_r ($incoming_data); + + if (count ($incoming_data) > 1) + return $this->addArrayInline ($incoming_data, $incoming_indent); + + $key = key ($incoming_data); + $value = isset($incoming_data[$key]) ? $incoming_data[$key] : null; + if ($key === '__!YAMLZero') $key = '0'; + + if ($incoming_indent == 0 && !$this->_containsGroupAlias && !$this->_containsGroupAnchor) { // Shortcut for root-level values. + if ($key || $key === '' || $key === '0') { + $this->result[$key] = $value; + } else { + $this->result[] = $value; end ($this->result); $key = key ($this->result); + } + $this->path[$incoming_indent] = $key; + return; + } + + + + $history = array(); + // Unfolding inner array tree. + $history[] = $_arr = $this->result; + foreach ($this->path as $k) { + $history[] = $_arr = $_arr[$k]; + } + + if ($this->_containsGroupAlias) { + $value = $this->referenceContentsByAlias($this->_containsGroupAlias); + $this->_containsGroupAlias = false; + } + + + // Adding string or numeric key to the innermost level or $this->arr. + if (is_string($key) && $key == '<<') { + if (!is_array ($_arr)) { $_arr = array (); } + $_arr = array_merge ($_arr, $value); + } else if ($key || $key === '' || $key === '0') { + $_arr[$key] = $value; + } else { + if (!is_array ($_arr)) { $_arr = array ($value); $key = 0; } + else { $_arr[] = $value; end ($_arr); $key = key ($_arr); } + } + + $reverse_path = array_reverse($this->path); + $reverse_history = array_reverse ($history); + $reverse_history[0] = $_arr; + $cnt = count($reverse_history) - 1; + for ($i = 0; $i < $cnt; $i++) { + $reverse_history[$i+1][$reverse_path[$i]] = $reverse_history[$i]; + } + $this->result = $reverse_history[$cnt]; + + $this->path[$incoming_indent] = $key; + + if ($this->_containsGroupAnchor) { + $this->SavedGroups[$this->_containsGroupAnchor] = $this->path; + if (is_array ($value)) { + $k = key ($value); + if (!is_int ($k)) { + $this->SavedGroups[$this->_containsGroupAnchor][$incoming_indent + 2] = $k; + } + } + $this->_containsGroupAnchor = false; + } + + } + + function startsLiteralBlock ($line) { + $lastChar = substr (trim($line), -1); + if ($lastChar != '>' && $lastChar != '|') return false; + if ($lastChar == '|') return $lastChar; + // HTML tags should not be counted as literal blocks. + if (preg_match ('#<.*?>$#', $line)) return false; + return $lastChar; + } + + function greedilyNeedNextLine($line) { + $line = trim ($line); + if (!strlen($line)) return false; + if (substr ($line, -1, 1) == ']') return false; + if ($line[0] == '[') return true; + if (preg_match ('#^[^:]+?:\s*\[#', $line)) return true; + return false; + } + + function addLiteralLine ($literalBlock, $line, $literalBlockStyle) { + $line = $this->stripIndent($line); + $line = rtrim ($line, "\r\n\t ") . "\n"; + if ($literalBlockStyle == '|') { + return $literalBlock . $line; + } + if (strlen($line) == 0) + return rtrim($literalBlock, ' ') . "\n"; + if ($line == "\n" && $literalBlockStyle == '>') { + return rtrim ($literalBlock, " \t") . "\n"; + } + if ($line != "\n") + $line = trim ($line, "\r\n ") . " "; + return $literalBlock . $line; + } + + function revertLiteralPlaceHolder ($lineArray, $literalBlock) { + foreach ($lineArray as $k => $_) { + if (is_array($_)) + $lineArray[$k] = $this->revertLiteralPlaceHolder ($_, $literalBlock); + else if (substr($_, -1 * strlen ($this->LiteralPlaceHolder)) == $this->LiteralPlaceHolder) + $lineArray[$k] = rtrim ($literalBlock, " \r\n"); + } + return $lineArray; + } + + function stripIndent ($line, $indent = -1) { + if ($indent == -1) $indent = strlen($line) - strlen(ltrim($line)); + return substr ($line, $indent); + } + + function getParentPathByIndent ($indent) { + if ($indent == 0) return array(); + $linePath = $this->path; + do { + end($linePath); $lastIndentInParentPath = key($linePath); + if ($indent <= $lastIndentInParentPath) array_pop ($linePath); + } while ($indent <= $lastIndentInParentPath); + return $linePath; + } + + + function clearBiggerPathValues ($indent) { + + + if ($indent == 0) $this->path = array(); + if (empty ($this->path)) return true; + + foreach ($this->path as $k => $_) { + if ($k > $indent) unset ($this->path[$k]); + } + + return true; + } + + + function isComment ($line) { + if (!$line) return false; + if ($line[0] == '#') return true; + if (trim($line, " \r\n\t") == '---') return true; + return false; + } + + function isEmpty ($line) { + return (trim ($line) === ''); + } + + + function isArrayElement ($line) { + if (!$line) return false; + if ($line[0] != '-') return false; + if (strlen ($line) > 3) + if (substr($line,0,3) == '---') return false; + + return true; + } + + function isHashElement ($line) { + return strpos($line, ':'); + } + + function isLiteral ($line) { + if ($this->isArrayElement($line)) return false; + if ($this->isHashElement($line)) return false; + return true; + } + + + function unquote ($value) { + if (!$value) return $value; + if (!is_string($value)) return $value; + if ($value[0] == '\'') return trim ($value, '\''); + if ($value[0] == '"') return trim ($value, '"'); + return $value; + } + + function startsMappedSequence ($line) { + return ($line[0] == '-' && substr ($line, -1, 1) == ':'); + } + + function returnMappedSequence ($line) { + $array = array(); + $key = $this->unquote(trim(substr($line,1,-1))); + $array[$key] = array(); + $this->delayedPath = array(strpos ($line, $key) + $this->indent => $key); + return array($array); + } + + function returnMappedValue ($line) { + $array = array(); + $key = $this->unquote (trim(substr($line,0,-1))); + $array[$key] = ''; + return $array; + } + + function startsMappedValue ($line) { + return (substr ($line, -1, 1) == ':'); + } + + function isPlainArray ($line) { + return ($line[0] == '[' && substr ($line, -1, 1) == ']'); + } + + function returnPlainArray ($line) { + return $this->_toType($line); + } + + function returnKeyValuePair ($line) { + $array = array(); + $key = ''; + if (strpos ($line, ':')) { + // It's a key/value pair most likely + // If the key is in double quotes pull it out + if (($line[0] == '"' || $line[0] == "'") && preg_match('/^(["\'](.*)["\'](\s)*:)/',$line,$matches)) { + $value = trim(str_replace($matches[1],'',$line)); + $key = $matches[2]; + } else { + // Do some guesswork as to the key and the value + $explode = explode(':',$line); + $key = trim($explode[0]); + array_shift($explode); + $value = trim(implode(':',$explode)); + } + // Set the type of the value. Int, string, etc + $value = $this->_toType($value); + if ($key === '0') $key = '__!YAMLZero'; + $array[$key] = $value; + } else { + $array = array ($line); + } + return $array; + + } + + + function returnArrayElement ($line) { + if (strlen($line) <= 1) return array(array()); // Weird %) + $array = array(); + $value = trim(substr($line,1)); + $value = $this->_toType($value); + $array[] = $value; + return $array; + } + + + function nodeContainsGroup ($line) { + $symbolsForReference = 'A-z0-9_\-'; + if (strpos($line, '&') === false && strpos($line, '*') === false) return false; // Please die fast ;-) + if ($line[0] == '&' && preg_match('/^(&['.$symbolsForReference.']+)/', $line, $matches)) return $matches[1]; + if ($line[0] == '*' && preg_match('/^(\*['.$symbolsForReference.']+)/', $line, $matches)) return $matches[1]; + if (preg_match('/(&['.$symbolsForReference.']+)$/', $line, $matches)) return $matches[1]; + if (preg_match('/(\*['.$symbolsForReference.']+$)/', $line, $matches)) return $matches[1]; + if (preg_match ('#^\s*<<\s*:\s*(\*[^\s]+).*$#', $line, $matches)) return $matches[1]; + return false; + + } + + function addGroup ($line, $group) { + if ($group[0] == '&') $this->_containsGroupAnchor = substr ($group, 1); + if ($group[0] == '*') $this->_containsGroupAlias = substr ($group, 1); + //print_r ($this->path); + } + + function stripGroup ($line, $group) { + $line = trim(str_replace($group, '', $line)); + return $line; + } +} + +// Enable use of Spyc from command line +// The syntax is the following: php spyc.php spyc.yaml + +define ('SPYC_FROM_COMMAND_LINE', false); + +do { + if (!SPYC_FROM_COMMAND_LINE) break; + if (empty ($_SERVER['argc']) || $_SERVER['argc'] < 2) break; + if (empty ($_SERVER['SCRIPT_NAME']) || $_SERVER['SCRIPT_NAME'] != 'spyc.php') break; + $file = $argv[1]; + printf ("Spyc loading file: %s\n", $file); + print_r (spyc_load_file ($file)); +} while (0); \ No newline at end of file diff --git a/includes/spyc/php4/test.php4 b/includes/spyc/php4/test.php4 new file mode 100644 index 0000000..315f501 --- /dev/null +++ b/includes/spyc/php4/test.php4 @@ -0,0 +1,162 @@ + "1.5ghz", "ram" => "1 gig", + "os" => "os x 10.4.1")) + die('Sequence 4 failed'); + +# Mapped sequence +if ($yaml['domains'] != array("yaml.org", "php.net")) + die("Key: 'domains' failed"); + +# A sequence like this. +if ($yaml[5] != array("program" => "Adium", "platform" => "OS X", + "type" => "Chat Client")) + die('Sequence 5 failed'); + +# A folded block as a mapped value +if ($yaml['no time'] != "There isn't any time for your tricks!\nDo you understand?") + die("Key: 'no time' failed"); + +# A literal block as a mapped value +if ($yaml['some time'] != "There is nothing but time\nfor your tricks.") + die("Key: 'some time' failed"); + +# Crazy combinations +if ($yaml['databases'] != array( array("name" => "spartan", "notes" => + array( "Needs to be backed up", + "Needs to be normalized" ), + "type" => "mysql" ))) + die("Key: 'databases' failed"); + +# You can be a bit tricky +if ($yaml["if: you'd"] != "like") + die("Key: 'if: you\'d' failed"); + +# Inline sequences +if ($yaml[6] != array("One", "Two", "Three", "Four")) + die("Sequence 6 failed"); + +# Nested Inline Sequences +if ($yaml[7] != array("One", array("Two", "And", "Three"), "Four", "Five")) + die("Sequence 7 failed"); + +# Nested Nested Inline Sequences +if ($yaml[8] != array( "This", array("Is", "Getting", array("Ridiculous", "Guys")), + "Seriously", array("Show", "Mercy"))) + die("Sequence 8 failed"); + +# Inline mappings +if ($yaml[9] != array("name" => "chris", "age" => "young", "brand" => "lucky strike")) + die("Sequence 9 failed"); + +# Nested inline mappings +if ($yaml[10] != array("name" => "mark", "age" => "older than chris", + "brand" => array("marlboro", "lucky strike"))) + die("Sequence 10 failed"); + +# References -- they're shaky, but functional +if ($yaml['dynamic languages'] != array('Perl', 'Python', 'PHP', 'Ruby')) + die("Key: 'dynamic languages' failed"); + +if ($yaml['compiled languages'] != array('C/C++', 'Java')) + die("Key: 'compiled languages' failed"); + +if ($yaml['all languages'] != array( + array('Perl', 'Python', 'PHP', 'Ruby'), + array('C/C++', 'Java') + )) + die("Key: 'all languages' failed"); + +# Added in .2.2: Escaped quotes +if ($yaml[11] != "you know, this shouldn't work. but it does.") + die("Sequence 11 failed."); + +if ($yaml[12] != "that's my value.") + die("Sequence 12 failed."); + +if ($yaml[13] != "again, that's my value.") + die("Sequence 13 failed."); + +if ($yaml[14] != "here's to \"quotes\", boss.") + die("Sequence 14 failed."); + +if ($yaml[15] != array( 'name' => "Foo, Bar's", 'age' => 20)) + die("Sequence 15 failed."); + +if ($yaml[16] != array( 0 => "a", 1 => array (0 => 1, 1 => 2), 2 => "b")) + die("Sequence 16 failed."); + +if ($yaml['endloop'] != "Does this line in the end indeed make Spyc go to an infinite loop?") + die("[endloop] failed."); + + +print "spyc.yaml parsed correctly\n"; + +?> \ No newline at end of file diff --git a/includes/spyc/spyc.php b/includes/spyc/spyc.php new file mode 100644 index 0000000..6ff8f1c --- /dev/null +++ b/includes/spyc/spyc.php @@ -0,0 +1,1024 @@ + + * @author Chris Wanstrath + * @link http://code.google.com/p/spyc/ + * @copyright Copyright 2005-2006 Chris Wanstrath, 2006-2009 Vlad Andersen + * @license http://www.opensource.org/licenses/mit-license.php MIT License + * @package Spyc + */ + +if (!function_exists('spyc_load')) { + /** + * Parses YAML to array. + * @param string $string YAML string. + * @return array + */ + function spyc_load ($string) { + return Spyc::YAMLLoadString($string); + } +} + +if (!function_exists('spyc_load_file')) { + /** + * Parses YAML to array. + * @param string $file Path to YAML file. + * @return array + */ + function spyc_load_file ($file) { + return Spyc::YAMLLoad($file); + } +} + +/** + * The Simple PHP YAML Class. + * + * This class can be used to read a YAML file and convert its contents + * into a PHP array. It currently supports a very limited subsection of + * the YAML spec. + * + * Usage: + * + * $Spyc = new Spyc; + * $array = $Spyc->load($file); + * + * or: + * + * $array = Spyc::YAMLLoad($file); + * + * or: + * + * $array = spyc_load_file($file); + * + * @package Spyc + */ +class Spyc { + + // SETTINGS + + /** + * Setting this to true will force YAMLDump to enclose any string value in + * quotes. False by default. + * + * @var bool + */ + public $setting_dump_force_quotes = false; + + /** + * Setting this to true will forse YAMLLoad to use syck_load function when + * possible. False by default. + * @var bool + */ + public $setting_use_syck_is_possible = false; + + + + /**#@+ + * @access private + * @var mixed + */ + private $_dumpIndent; + private $_dumpWordWrap; + private $_containsGroupAnchor = false; + private $_containsGroupAlias = false; + private $path; + private $result; + private $LiteralPlaceHolder = '___YAML_Literal_Block___'; + private $SavedGroups = array(); + private $indent; + /** + * Path modifier that should be applied after adding current element. + * @var array + */ + private $delayedPath = array(); + + /**#@+ + * @access public + * @var mixed + */ + public $_nodeId; + +/** + * Load a valid YAML string to Spyc. + * @param string $input + * @return array + */ + public function load ($input) { + return $this->__loadString($input); + } + + /** + * Load a valid YAML file to Spyc. + * @param string $file + * @return array + */ + public function loadFile ($file) { + return $this->__load($file); + } + + /** + * Load YAML into a PHP array statically + * + * The load method, when supplied with a YAML stream (string or file), + * will do its best to convert YAML in a file into a PHP array. Pretty + * simple. + * Usage: + * + * $array = Spyc::YAMLLoad('lucky.yaml'); + * print_r($array); + * + * @access public + * @return array + * @param string $input Path of YAML file or string containing YAML + */ + public static function YAMLLoad($input) { + $Spyc = new Spyc; + return $Spyc->__load($input); + } + + /** + * Load a string of YAML into a PHP array statically + * + * The load method, when supplied with a YAML string, will do its best + * to convert YAML in a string into a PHP array. Pretty simple. + * + * Note: use this function if you don't want files from the file system + * loaded and processed as YAML. This is of interest to people concerned + * about security whose input is from a string. + * + * Usage: + * + * $array = Spyc::YAMLLoadString("---\n0: hello world\n"); + * print_r($array); + * + * @access public + * @return array + * @param string $input String containing YAML + */ + public static function YAMLLoadString($input) { + $Spyc = new Spyc; + return $Spyc->__loadString($input); + } + + /** + * Dump YAML from PHP array statically + * + * The dump method, when supplied with an array, will do its best + * to convert the array into friendly YAML. Pretty simple. Feel free to + * save the returned string as nothing.yaml and pass it around. + * + * Oh, and you can decide how big the indent is and what the wordwrap + * for folding is. Pretty cool -- just pass in 'false' for either if + * you want to use the default. + * + * Indent's default is 2 spaces, wordwrap's default is 40 characters. And + * you can turn off wordwrap by passing in 0. + * + * @access public + * @return string + * @param array $array PHP array + * @param int $indent Pass in false to use the default, which is 2 + * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40) + */ + public static function YAMLDump($array,$indent = false,$wordwrap = false) { + $spyc = new Spyc; + return $spyc->dump($array,$indent,$wordwrap); + } + + + /** + * Dump PHP array to YAML + * + * The dump method, when supplied with an array, will do its best + * to convert the array into friendly YAML. Pretty simple. Feel free to + * save the returned string as tasteful.yaml and pass it around. + * + * Oh, and you can decide how big the indent is and what the wordwrap + * for folding is. Pretty cool -- just pass in 'false' for either if + * you want to use the default. + * + * Indent's default is 2 spaces, wordwrap's default is 40 characters. And + * you can turn off wordwrap by passing in 0. + * + * @access public + * @return string + * @param array $array PHP array + * @param int $indent Pass in false to use the default, which is 2 + * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40) + */ + public function dump($array,$indent = false,$wordwrap = false) { + // Dumps to some very clean YAML. We'll have to add some more features + // and options soon. And better support for folding. + + // New features and options. + if ($indent === false or !is_numeric($indent)) { + $this->_dumpIndent = 2; + } else { + $this->_dumpIndent = $indent; + } + + if ($wordwrap === false or !is_numeric($wordwrap)) { + $this->_dumpWordWrap = 40; + } else { + $this->_dumpWordWrap = $wordwrap; + } + + // New YAML document + $string = "---\n"; + + // Start at the base of the array and move through it. + if ($array) { + $array = (array)$array; + $first_key = key($array); + + $previous_key = -1; + foreach ($array as $key => $value) { + $string .= $this->_yamlize($key,$value,0,$previous_key, $first_key); + $previous_key = $key; + } + } + return $string; + } + + /** + * Attempts to convert a key / value array item to YAML + * @access private + * @return string + * @param $key The name of the key + * @param $value The value of the item + * @param $indent The indent of the current node + */ + private function _yamlize($key,$value,$indent, $previous_key = -1, $first_key = 0) { + if (is_array($value)) { + if (empty ($value)) + return $this->_dumpNode($key, array(), $indent, $previous_key, $first_key); + // It has children. What to do? + // Make it the right kind of item + $string = $this->_dumpNode($key, NULL, $indent, $previous_key, $first_key); + // Add the indent + $indent += $this->_dumpIndent; + // Yamlize the array + $string .= $this->_yamlizeArray($value,$indent); + } elseif (!is_array($value)) { + // It doesn't have children. Yip. + $string = $this->_dumpNode($key, $value, $indent, $previous_key, $first_key); + } + return $string; + } + + /** + * Attempts to convert an array to YAML + * @access private + * @return string + * @param $array The array you want to convert + * @param $indent The indent of the current level + */ + private function _yamlizeArray($array,$indent) { + if (is_array($array)) { + $string = ''; + $previous_key = -1; + $first_key = key($array); + foreach ($array as $key => $value) { + $string .= $this->_yamlize($key, $value, $indent, $previous_key, $first_key); + $previous_key = $key; + } + return $string; + } else { + return false; + } + } + + /** + * Returns YAML from a key and a value + * @access private + * @return string + * @param $key The name of the key + * @param $value The value of the item + * @param $indent The indent of the current node + */ + private function _dumpNode($key, $value, $indent, $previous_key = -1, $first_key = 0) { + // do some folding here, for blocks + if (is_string ($value) && ((strpos($value,"\n") !== false || strpos($value,": ") !== false || strpos($value,"- ") !== false || + strpos($value,"*") !== false || strpos($value,"#") !== false || strpos($value,"<") !== false || strpos($value,">") !== false || + strpos($value,"[") !== false || strpos($value,"]") !== false || strpos($value,"{") !== false || strpos($value,"}") !== false) || substr ($value, -1, 1) == ':')) { + $value = $this->_doLiteralBlock($value,$indent); + } else { + $value = $this->_doFolding($value,$indent); + if (is_bool($value)) { + $value = ($value) ? "true" : "false"; + } + } + + if ($value === array()) $value = '[ ]'; + + $spaces = str_repeat(' ',$indent); + + if (is_int($key) && $key - 1 == $previous_key && $first_key===0) { + // It's a sequence + $string = $spaces.'- '.$value."\n"; + } else { + if ($first_key===0) throw new Exception('Keys are all screwy. The first one was zero, now it\'s "'. $key .'"'); + // It's mapped + if (strpos($key, ":") !== false) { $key = '"' . $key . '"'; } + $string = $spaces.$key.': '.$value."\n"; + } + return $string; + } + + /** + * Creates a literal block for dumping + * @access private + * @return string + * @param $value + * @param $indent int The value of the indent + */ + private function _doLiteralBlock($value,$indent) { + if (strpos($value, "\n") === false && strpos($value, "'") === false) { + return sprintf ("'%s'", $value); + } + if (strpos($value, "\n") === false && strpos($value, '"') === false) { + return sprintf ('"%s"', $value); + } + $exploded = explode("\n",$value); + $newValue = '|'; + $indent += $this->_dumpIndent; + $spaces = str_repeat(' ',$indent); + foreach ($exploded as $line) { + $newValue .= "\n" . $spaces . trim($line); + } + return $newValue; + } + + /** + * Folds a string of text, if necessary + * @access private + * @return string + * @param $value The string you wish to fold + */ + private function _doFolding($value,$indent) { + // Don't do anything if wordwrap is set to 0 + + if ($this->_dumpWordWrap !== 0 && is_string ($value) && strlen($value) > $this->_dumpWordWrap) { + $indent += $this->_dumpIndent; + $indent = str_repeat(' ',$indent); + $wrapped = wordwrap($value,$this->_dumpWordWrap,"\n$indent"); + $value = ">\n".$indent.$wrapped; + } else { + if ($this->setting_dump_force_quotes && is_string ($value)) + $value = '"' . $value . '"'; + } + + + return $value; + } + +// LOADING FUNCTIONS + + private function __load($input) { + $Source = $this->loadFromSource($input); + return $this->loadWithSource($Source); + } + + private function __loadString($input) { + $Source = $this->loadFromString($input); + return $this->loadWithSource($Source); + } + + private function loadWithSource($Source) { + if (empty ($Source)) return array(); + if ($this->setting_use_syck_is_possible && function_exists ('syck_load')) { + $array = syck_load (implode ('', $Source)); + return is_array($array) ? $array : array(); + } + + $this->path = array(); + $this->result = array(); + + $cnt = count($Source); + for ($i = 0; $i < $cnt; $i++) { + $line = $Source[$i]; + + $this->indent = strlen($line) - strlen(ltrim($line)); + $tempPath = $this->getParentPathByIndent($this->indent); + $line = self::stripIndent($line, $this->indent); + if (self::isComment($line)) continue; + if (self::isEmpty($line)) continue; + $this->path = $tempPath; + + $literalBlockStyle = self::startsLiteralBlock($line); + if ($literalBlockStyle) { + $line = rtrim ($line, $literalBlockStyle . " \n"); + $literalBlock = ''; + $line .= $this->LiteralPlaceHolder; + + while (++$i < $cnt && $this->literalBlockContinues($Source[$i], $this->indent)) { + $literalBlock = $this->addLiteralLine($literalBlock, $Source[$i], $literalBlockStyle); + } + $i--; + } + + while (++$i < $cnt && self::greedilyNeedNextLine($line)) { + $line = rtrim ($line, " \n\t\r") . ' ' . ltrim ($Source[$i], " \t"); + } + $i--; + + + + if (strpos ($line, '#')) { + if (strpos ($line, '"') === false && strpos ($line, "'") === false) + $line = preg_replace('/\s+#(.+)$/','',$line); + } + + $lineArray = $this->_parseLine($line); + + if ($literalBlockStyle) + $lineArray = $this->revertLiteralPlaceHolder ($lineArray, $literalBlock); + + $this->addArray($lineArray, $this->indent); + + foreach ($this->delayedPath as $indent => $delayedPath) + $this->path[$indent] = $delayedPath; + + $this->delayedPath = array(); + + } + return $this->result; + } + + private function loadFromSource ($input) { + if (!empty($input) && strpos($input, "\n") === false && file_exists($input)) + return file($input); + + return $this->loadFromString($input); + } + + private function loadFromString ($input) { + $lines = explode("\n",$input); + foreach ($lines as $k => $_) { + $lines[$k] = rtrim ($_, "\r"); + } + return $lines; + } + + /** + * Parses YAML code and returns an array for a node + * @access private + * @return array + * @param string $line A line from the YAML file + */ + private function _parseLine($line) { + if (!$line) return array(); + $line = trim($line); + + if (!$line) return array(); + $array = array(); + + $group = $this->nodeContainsGroup($line); + if ($group) { + $this->addGroup($line, $group); + $line = $this->stripGroup ($line, $group); + } + + if ($this->startsMappedSequence($line)) + return $this->returnMappedSequence($line); + + if ($this->startsMappedValue($line)) + return $this->returnMappedValue($line); + + if ($this->isArrayElement($line)) + return $this->returnArrayElement($line); + + if ($this->isPlainArray($line)) + return $this->returnPlainArray($line); + + + return $this->returnKeyValuePair($line); + + } + + /** + * Finds the type of the passed value, returns the value as the new type. + * @access private + * @param string $value + * @return mixed + */ + private function _toType($value) { + if ($value === '') return null; + $first_character = $value[0]; + $last_character = substr($value, -1, 1); + + $is_quoted = false; + do { + if (!$value) break; + if ($first_character != '"' && $first_character != "'") break; + if ($last_character != '"' && $last_character != "'") break; + $is_quoted = true; + } while (0); + + if ($is_quoted) + return strtr(substr ($value, 1, -1), array ('\\"' => '"', '\'\'' => '\'', '\\\'' => '\'')); + + if (strpos($value, ' #') !== false) + $value = preg_replace('/\s+#(.+)$/','',$value); + + if ($first_character == '[' && $last_character == ']') { + // Take out strings sequences and mappings + $innerValue = trim(substr ($value, 1, -1)); + if ($innerValue === '') return array(); + $explode = $this->_inlineEscape($innerValue); + // Propagate value array + $value = array(); + foreach ($explode as $v) { + $value[] = $this->_toType($v); + } + return $value; + } + + if (strpos($value,': ')!==false && $first_character != '{') { + $array = explode(': ',$value); + $key = trim($array[0]); + array_shift($array); + $value = trim(implode(': ',$array)); + $value = $this->_toType($value); + return array($key => $value); + } + + if ($first_character == '{' && $last_character == '}') { + $innerValue = trim(substr ($value, 1, -1)); + if ($innerValue === '') return array(); + // Inline Mapping + // Take out strings sequences and mappings + $explode = $this->_inlineEscape($innerValue); + // Propagate value array + $array = array(); + foreach ($explode as $v) { + $SubArr = $this->_toType($v); + if (empty($SubArr)) continue; + if (is_array ($SubArr)) { + $array[key($SubArr)] = $SubArr[key($SubArr)]; continue; + } + $array[] = $SubArr; + } + return $array; + } + + if ($value == 'null' || $value == 'NULL' || $value == 'Null' || $value == '' || $value == '~') { + return null; + } + + if (intval($first_character) > 0 && preg_match ('/^[1-9]+[0-9]*$/', $value)) { + $intvalue = (int)$value; + if ($intvalue != PHP_INT_MAX) + $value = $intvalue; + return $value; + } + + if (in_array($value, + array('true', 'on', '+', 'yes', 'y', 'True', 'TRUE', 'On', 'ON', 'YES', 'Yes', 'Y'))) { + return true; + } + + if (in_array(strtolower($value), + array('false', 'off', '-', 'no', 'n'))) { + return false; + } + + if (is_numeric($value)) { + if ($value === '0') return 0; + if (trim ($value, 0) === $value) + $value = (float)$value; + return $value; + } + + return $value; + } + + /** + * Used in inlines to check for more inlines or quoted strings + * @access private + * @return array + */ + private function _inlineEscape($inline) { + // There's gotta be a cleaner way to do this... + // While pure sequences seem to be nesting just fine, + // pure mappings and mappings with sequences inside can't go very + // deep. This needs to be fixed. + + $seqs = array(); + $maps = array(); + $saved_strings = array(); + + // Check for strings + $regex = '/(?:(")|(?:\'))((?(1)[^"]+|[^\']+))(?(1)"|\')/'; + if (preg_match_all($regex,$inline,$strings)) { + $saved_strings = $strings[0]; + $inline = preg_replace($regex,'YAMLString',$inline); + } + unset($regex); + + $i = 0; + do { + + // Check for sequences + while (preg_match('/\[([^{}\[\]]+)\]/U',$inline,$matchseqs)) { + $seqs[] = $matchseqs[0]; + $inline = preg_replace('/\[([^{}\[\]]+)\]/U', ('YAMLSeq' . (count($seqs) - 1) . 's'), $inline, 1); + } + + // Check for mappings + while (preg_match('/{([^\[\]{}]+)}/U',$inline,$matchmaps)) { + $maps[] = $matchmaps[0]; + $inline = preg_replace('/{([^\[\]{}]+)}/U', ('YAMLMap' . (count($maps) - 1) . 's'), $inline, 1); + } + + if ($i++ >= 10) break; + + } while (strpos ($inline, '[') !== false || strpos ($inline, '{') !== false); + + $explode = explode(', ',$inline); + $stringi = 0; $i = 0; + + while (1) { + + // Re-add the sequences + if (!empty($seqs)) { + foreach ($explode as $key => $value) { + if (strpos($value,'YAMLSeq') !== false) { + foreach ($seqs as $seqk => $seq) { + $explode[$key] = str_replace(('YAMLSeq'.$seqk.'s'),$seq,$value); + $value = $explode[$key]; + } + } + } + } + + // Re-add the mappings + if (!empty($maps)) { + foreach ($explode as $key => $value) { + if (strpos($value,'YAMLMap') !== false) { + foreach ($maps as $mapk => $map) { + $explode[$key] = str_replace(('YAMLMap'.$mapk.'s'), $map, $value); + $value = $explode[$key]; + } + } + } + } + + + // Re-add the strings + if (!empty($saved_strings)) { + foreach ($explode as $key => $value) { + while (strpos($value,'YAMLString') !== false) { + $explode[$key] = preg_replace('/YAMLString/',$saved_strings[$stringi],$value, 1); + unset($saved_strings[$stringi]); + ++$stringi; + $value = $explode[$key]; + } + } + } + + $finished = true; + foreach ($explode as $key => $value) { + if (strpos($value,'YAMLSeq') !== false) { + $finished = false; break; + } + if (strpos($value,'YAMLMap') !== false) { + $finished = false; break; + } + if (strpos($value,'YAMLString') !== false) { + $finished = false; break; + } + } + if ($finished) break; + + $i++; + if ($i > 10) + break; // Prevent infinite loops. + } + + return $explode; + } + + private function literalBlockContinues ($line, $lineIndent) { + if (!trim($line)) return true; + if (strlen($line) - strlen(ltrim($line)) > $lineIndent) return true; + return false; + } + + private function referenceContentsByAlias ($alias) { + do { + if (!isset($this->SavedGroups[$alias])) { echo "Bad group name: $alias."; break; } + $groupPath = $this->SavedGroups[$alias]; + $value = $this->result; + foreach ($groupPath as $k) { + $value = $value[$k]; + } + } while (false); + return $value; + } + + private function addArrayInline ($array, $indent) { + $CommonGroupPath = $this->path; + if (empty ($array)) return false; + + foreach ($array as $k => $_) { + $this->addArray(array($k => $_), $indent); + $this->path = $CommonGroupPath; + } + return true; + } + + private function addArray ($incoming_data, $incoming_indent) { + + // print_r ($incoming_data); + + if (count ($incoming_data) > 1) + return $this->addArrayInline ($incoming_data, $incoming_indent); + + $key = key ($incoming_data); + $value = isset($incoming_data[$key]) ? $incoming_data[$key] : null; + if ($key === '__!YAMLZero') $key = '0'; + + if ($incoming_indent == 0 && !$this->_containsGroupAlias && !$this->_containsGroupAnchor) { // Shortcut for root-level values. + if ($key || $key === '' || $key === '0') { + $this->result[$key] = $value; + } else { + $this->result[] = $value; end ($this->result); $key = key ($this->result); + } + $this->path[$incoming_indent] = $key; + return; + } + + + + $history = array(); + // Unfolding inner array tree. + $history[] = $_arr = $this->result; + foreach ($this->path as $k) { + $history[] = $_arr = $_arr[$k]; + } + + if ($this->_containsGroupAlias) { + $value = $this->referenceContentsByAlias($this->_containsGroupAlias); + $this->_containsGroupAlias = false; + } + + + // Adding string or numeric key to the innermost level or $this->arr. + if (is_string($key) && $key == '<<') { + if (!is_array ($_arr)) { $_arr = array (); } + + $_arr = array_merge ($_arr, $value); + } else if ($key || $key === '' || $key === '0') { + $_arr[$key] = $value; + } else { + if (!is_array ($_arr)) { $_arr = array ($value); $key = 0; } + else { $_arr[] = $value; end ($_arr); $key = key ($_arr); } + } + + $reverse_path = array_reverse($this->path); + $reverse_history = array_reverse ($history); + $reverse_history[0] = $_arr; + $cnt = count($reverse_history) - 1; + for ($i = 0; $i < $cnt; $i++) { + $reverse_history[$i+1][$reverse_path[$i]] = $reverse_history[$i]; + } + $this->result = $reverse_history[$cnt]; + + $this->path[$incoming_indent] = $key; + + if ($this->_containsGroupAnchor) { + $this->SavedGroups[$this->_containsGroupAnchor] = $this->path; + if (is_array ($value)) { + $k = key ($value); + if (!is_int ($k)) { + $this->SavedGroups[$this->_containsGroupAnchor][$incoming_indent + 2] = $k; + } + } + $this->_containsGroupAnchor = false; + } + + } + + private static function startsLiteralBlock ($line) { + $lastChar = substr (trim($line), -1); + if ($lastChar != '>' && $lastChar != '|') return false; + if ($lastChar == '|') return $lastChar; + // HTML tags should not be counted as literal blocks. + if (preg_match ('#<.*?>$#', $line)) return false; + return $lastChar; + } + + private static function greedilyNeedNextLine($line) { + $line = trim ($line); + if (!strlen($line)) return false; + if (substr ($line, -1, 1) == ']') return false; + if ($line[0] == '[') return true; + if (preg_match ('#^[^:]+?:\s*\[#', $line)) return true; + return false; + } + + private function addLiteralLine ($literalBlock, $line, $literalBlockStyle) { + $line = self::stripIndent($line); + $line = rtrim ($line, "\r\n\t ") . "\n"; + if ($literalBlockStyle == '|') { + return $literalBlock . $line; + } + if (strlen($line) == 0) + return rtrim($literalBlock, ' ') . "\n"; + if ($line == "\n" && $literalBlockStyle == '>') { + return rtrim ($literalBlock, " \t") . "\n"; + } + if ($line != "\n") + $line = trim ($line, "\r\n ") . " "; + return $literalBlock . $line; + } + + function revertLiteralPlaceHolder ($lineArray, $literalBlock) { + foreach ($lineArray as $k => $_) { + if (is_array($_)) + $lineArray[$k] = $this->revertLiteralPlaceHolder ($_, $literalBlock); + else if (substr($_, -1 * strlen ($this->LiteralPlaceHolder)) == $this->LiteralPlaceHolder) + $lineArray[$k] = rtrim ($literalBlock, " \r\n"); + } + return $lineArray; + } + + private static function stripIndent ($line, $indent = -1) { + if ($indent == -1) $indent = strlen($line) - strlen(ltrim($line)); + return substr ($line, $indent); + } + + private function getParentPathByIndent ($indent) { + if ($indent == 0) return array(); + $linePath = $this->path; + do { + end($linePath); $lastIndentInParentPath = key($linePath); + if ($indent <= $lastIndentInParentPath) array_pop ($linePath); + } while ($indent <= $lastIndentInParentPath); + return $linePath; + } + + + private function clearBiggerPathValues ($indent) { + + + if ($indent == 0) $this->path = array(); + if (empty ($this->path)) return true; + + foreach ($this->path as $k => $_) { + if ($k > $indent) unset ($this->path[$k]); + } + + return true; + } + + + private static function isComment ($line) { + if (!$line) return false; + if ($line[0] == '#') return true; + if (trim($line, " \r\n\t") == '---') return true; + return false; + } + + private static function isEmpty ($line) { + return (trim ($line) === ''); + } + + + private function isArrayElement ($line) { + if (!$line) return false; + if ($line[0] != '-') return false; + if (strlen ($line) > 3) + if (substr($line,0,3) == '---') return false; + + return true; + } + + private function isHashElement ($line) { + return strpos($line, ':'); + } + + private function isLiteral ($line) { + if ($this->isArrayElement($line)) return false; + if ($this->isHashElement($line)) return false; + return true; + } + + + private static function unquote ($value) { + if (!$value) return $value; + if (!is_string($value)) return $value; + if ($value[0] == '\'') return trim ($value, '\''); + if ($value[0] == '"') return trim ($value, '"'); + return $value; + } + + private function startsMappedSequence ($line) { + return ($line[0] == '-' && substr ($line, -1, 1) == ':'); + } + + private function returnMappedSequence ($line) { + $array = array(); + $key = self::unquote(trim(substr($line,1,-1))); + $array[$key] = array(); + $this->delayedPath = array(strpos ($line, $key) + $this->indent => $key); + return array($array); + } + + private function returnMappedValue ($line) { + $array = array(); + $key = self::unquote (trim(substr($line,0,-1))); + $array[$key] = ''; + return $array; + } + + private function startsMappedValue ($line) { + return (substr ($line, -1, 1) == ':'); + } + + private function isPlainArray ($line) { + return ($line[0] == '[' && substr ($line, -1, 1) == ']'); + } + + private function returnPlainArray ($line) { + return $this->_toType($line); + } + + private function returnKeyValuePair ($line) { + $array = array(); + $key = ''; + if (strpos ($line, ':')) { + // It's a key/value pair most likely + // If the key is in double quotes pull it out + if (($line[0] == '"' || $line[0] == "'") && preg_match('/^(["\'](.*)["\'](\s)*:)/',$line,$matches)) { + $value = trim(str_replace($matches[1],'',$line)); + $key = $matches[2]; + } else { + // Do some guesswork as to the key and the value + $explode = explode(':',$line); + $key = trim($explode[0]); + array_shift($explode); + $value = trim(implode(':',$explode)); + } + // Set the type of the value. Int, string, etc + $value = $this->_toType($value); + if ($key === '0') $key = '__!YAMLZero'; + $array[$key] = $value; + } else { + $array = array ($line); + } + return $array; + + } + + + private function returnArrayElement ($line) { + if (strlen($line) <= 1) return array(array()); // Weird %) + $array = array(); + $value = trim(substr($line,1)); + $value = $this->_toType($value); + $array[] = $value; + return $array; + } + + + private function nodeContainsGroup ($line) { + $symbolsForReference = 'A-z0-9_\-'; + if (strpos($line, '&') === false && strpos($line, '*') === false) return false; // Please die fast ;-) + if ($line[0] == '&' && preg_match('/^(&['.$symbolsForReference.']+)/', $line, $matches)) return $matches[1]; + if ($line[0] == '*' && preg_match('/^(\*['.$symbolsForReference.']+)/', $line, $matches)) return $matches[1]; + if (preg_match('/(&['.$symbolsForReference.']+)$/', $line, $matches)) return $matches[1]; + if (preg_match('/(\*['.$symbolsForReference.']+$)/', $line, $matches)) return $matches[1]; + if (preg_match ('#^\s*<<\s*:\s*(\*[^\s]+).*$#', $line, $matches)) return $matches[1]; + return false; + + } + + private function addGroup ($line, $group) { + if ($group[0] == '&') $this->_containsGroupAnchor = substr ($group, 1); + if ($group[0] == '*') $this->_containsGroupAlias = substr ($group, 1); + //print_r ($this->path); + } + + private function stripGroup ($line, $group) { + $line = trim(str_replace($group, '', $line)); + return $line; + } +} + +// Enable use of Spyc from command line +// The syntax is the following: php spyc.php spyc.yaml + +define ('SPYC_FROM_COMMAND_LINE', false); + +do { + if (!SPYC_FROM_COMMAND_LINE) break; + if (empty ($_SERVER['argc']) || $_SERVER['argc'] < 2) break; + if (empty ($_SERVER['SCRIPT_NAME']) || $_SERVER['SCRIPT_NAME'] != 'spyc.php') break; + $file = $argv[1]; + printf ("Spyc loading file: %s\n", $file); + print_r (spyc_load_file ($file)); +} while (0); \ No newline at end of file diff --git a/includes/spyc/spyc.yaml b/includes/spyc/spyc.yaml new file mode 100644 index 0000000..2e38e82 --- /dev/null +++ b/includes/spyc/spyc.yaml @@ -0,0 +1,196 @@ +# +# S P Y C +# a simple php yaml class +# +# authors: [vlad andersen (vlad.andersen@gmail.com), chris wanstrath (chris@ozmm.org)] +# websites: [http://www.yaml.org, http://spyc.sourceforge.net/] +# license: [MIT License, http://www.opensource.org/licenses/mit-license.php] +# copyright: (c) 2005-2006 Chris Wanstrath, 2006-2009 Vlad Andersen +# +# spyc.yml - A file containing the YAML that Spyc understands. + +--- + +# Mappings - with proper types +String: Anyone's name, really. +Int: 13 +True: true +False: false +Zero: 0 +Null: NULL +Float: 5.34 + +# A sequence +- PHP Class +- Basic YAML Loader +- Very Basic YAML Dumper + +# A sequence of a sequence +- + - YAML is so easy to learn. + - Your config files will never be the same. + +# Sequence of mappings +- + cpu: 1.5ghz + ram: 1 gig + os : os x 10.4.1 + +# Mapped sequence +domains: + - yaml.org + - php.net + +# A sequence like this. +- program: Adium + platform: OS X + type: Chat Client + +# A folded block as a mapped value +no time: > + There isn't any time + for your tricks! + + Do you understand? + +# A literal block as a mapped value +some time: | + There is nothing but time + for your tricks. + +# Crazy combinations +databases: + - name: spartan + notes: + - Needs to be backed up + - Needs to be normalized + type: mysql + +# You can be a bit tricky +"if: you'd": like + +# Inline sequences +- [One, Two, Three, Four] + +# Nested Inline Sequences +- [One, [Two, And, Three], Four, Five] + +# Nested Nested Inline Sequences +- [This, [Is, Getting, [Ridiculous, Guys]], Seriously, [Show, Mercy]] + +# Inline mappings +- {name: chris, age: young, brand: lucky strike} + +# Nested inline mappings +- {name: mark, age: older than chris, brand: [marlboro, lucky strike]} + +# References -- they're shaky, but functional +dynamic languages: &DLANGS + - Perl + - Python + - PHP + - Ruby +compiled languages: &CLANGS + - C/C++ + - Java +all languages: + - *DLANGS + - *CLANGS + +# Added in .2.2: Escaped quotes +- you know, this shouldn't work. but it does. +- 'that''s my value.' +- 'again, that\'s my value.' +- "here's to \"quotes\", boss." + +# added in .2.3 +- {name: "Foo, Bar's", age: 20} + +# Added in .2.4: bug [ 1418193 ] Quote Values in Nested Arrays +- [a, ['1', "2"], b] + +# Added in .2.4: malformed YAML +all + javascripts: [dom1.js, dom.js] + +# Added in .2 +1040: Ooo, a numeric key! # And working comments? Wow! Colons in comments: a menace (0.3). + +hash_1: Hash #and a comment +hash_2: "Hash #and a comment" +"hash#3": "Hash (#) can appear in key too" + +float_test: 1.0 +float_test_with_quotes: '1.0' +float_inverse_test: 001 + +a_really_large_number: 115792089237316195423570985008687907853269984665640564039457584007913129639936 # 2^256 + +int array: [ 1, 2, 3 ] + +array on several lines: + [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 ] + +morelesskey: "" + +array_of_zero: [0] +sophisticated_array_of_zero: {rx: {tx: [0]} } + +switches: + - { row: 0, col: 0, func: {tx: [0, 1]} } + +empty_sequence: [ ] +empty_hash: { } + +special_characters: "[{]]{{]]" + +asterisks: "*" + +empty_key: + : + key: value + +trailing_colon: "foo:" + +multiline_items: + - type: SomeItem + values: [blah, blah, blah, + blah] + ints: [2, 54, 12, + 2143] + +many_lines: | + A quick + fox + + + jumped + over + + + + + + a lazy + + + + dog + + +werte: + 1: nummer 1 + 0: Stunde 0 + +noindent_records: +- record1: value1 +- record2: value2 + +"a:1": [1000] +"a:2": + - 2000 + +# [Endloop] +endloop: | + Does this line in the end indeed make Spyc go to an infinite loop? \ No newline at end of file diff --git a/includes/spyc/tests/DumpTest.php b/includes/spyc/tests/DumpTest.php new file mode 100644 index 0000000..0529d67 --- /dev/null +++ b/includes/spyc/tests/DumpTest.php @@ -0,0 +1,58 @@ +files_to_test = array ('../spyc.yaml', 'failing1.yaml', 'indent_1.yaml', 'quotes.yaml'); + } + + public function testDump() { + foreach ($this->files_to_test as $file) { + $yaml = spyc_load(file_get_contents($file)); + $dump = Spyc::YAMLDump ($yaml); + $yaml_after_dump = Spyc::YAMLLoad ($dump); + $this->assertEquals ($yaml, $yaml_after_dump); + } + } + + public function testDumpWithQuotes() { + $Spyc = new Spyc(); + $Spyc->setting_dump_force_quotes = true; + foreach ($this->files_to_test as $file) { + $yaml = $Spyc->load(file_get_contents($file)); + $dump = $Spyc->dump ($yaml); + $yaml_after_dump = Spyc::YAMLLoad ($dump); + $this->assertEquals ($yaml, $yaml_after_dump); + } + } + + public function testDumpArrays() { + $dump = Spyc::YAMLDump(array ('item1', 'item2', 'item3')); + $awaiting = "---\n- item1\n- item2\n- item3\n"; + $this->assertEquals ($awaiting, $dump); + } + + public function testDumpNumerics() { + $dump = Spyc::YAMLDump(array ('404', '405', '500')); + $awaiting = "---\n- 404\n- 405\n- 500\n"; + $this->assertEquals ($awaiting, $dump); + } + + public function testDumpAsterisks() { + $dump = Spyc::YAMLDump(array ('*')); + $awaiting = "---\n- '*'\n"; + $this->assertEquals ($awaiting, $dump); + } + + + public function testEmpty() { + $dump = Spyc::YAMLDump(array("foo" => array())); + $awaiting = "---\nfoo: [ ]\n"; + $this->assertEquals ($awaiting, $dump); + } + +} \ No newline at end of file diff --git a/includes/spyc/tests/IndentTest.php b/includes/spyc/tests/IndentTest.php new file mode 100644 index 0000000..482d8e4 --- /dev/null +++ b/includes/spyc/tests/IndentTest.php @@ -0,0 +1,57 @@ +Y = Spyc::YAMLLoad("indent_1.yaml"); + } + + public function testIndent_1() { + $this->assertEquals (array ('child_1' => 2, 'child_2' => 0, 'child_3' => 1), $this->Y['root']); + } + + public function testIndent_2() { + $this->assertEquals (array ('child_1' => 1, 'child_2' => 2), $this->Y['root2']); + } + + public function testIndent_3() { + $this->assertEquals (array (array ('resolutions' => array (1024 => 768, 1920 => 1200), 'producer' => 'Nec')), $this->Y['display']); + } + + public function testIndent_4() { + $this->assertEquals (array ( + array ('resolutions' => array (1024 => 768)), + array ('resolutions' => array (1920 => 1200)), + ), $this->Y['displays']); + } + + public function testIndent_5() { + $this->assertEquals (array (array ( + 'row' => 0, + 'col' => 0, + 'headsets_affected' => array ( + array ( + 'ports' => array (0), + 'side' => 'left', + ) + ), + 'switch_function' => array ( + 'ics_ptt' => true + ) + )), $this->Y['nested_hashes_and_seqs']); + } + + public function testIndent_6() { + $this->assertEquals (array ( + 'h' => array ( + array ('a' => 'b', 'a1' => 'b1'), + array ('c' => 'd') + ) + ), $this->Y['easier_nest']); + } + +} \ No newline at end of file diff --git a/includes/spyc/tests/ParseTest.php b/includes/spyc/tests/ParseTest.php new file mode 100644 index 0000000..2eecf54 --- /dev/null +++ b/includes/spyc/tests/ParseTest.php @@ -0,0 +1,305 @@ +yaml = spyc_load_file('../spyc.yaml'); + } + + public function testMergeHashKeys() { + $Expected = array ( + array ('step' => array('instrument' => 'Lasik 2000', 'pulseEnergy' => 5.4, 'pulseDuration' => 12, 'repetition' => 1000, 'spotSize' => '1mm')), + array ('step' => array('instrument' => 'Lasik 2000', 'pulseEnergy' => 5.4, 'pulseDuration' => 12, 'repetition' => 1000, 'spotSize' => '2mm')), + ); + $Actual = spyc_load_file ('indent_1.yaml'); + $this->assertEquals ($Expected, $Actual['steps']); + } + + public function testDeathMasks() { + $Expected = array ('sad' => 2, 'magnificent' => 4); + $Actual = spyc_load_file ('indent_1.yaml'); + $this->assertEquals ($Expected, $Actual['death masks are']); + } + + public function testDevDb() { + $Expected = array ('adapter' => 'mysql', 'host' => 'localhost', 'database' => 'rails_dev'); + $Actual = spyc_load_file ('indent_1.yaml'); + $this->assertEquals ($Expected, $Actual['development']); + } + + public function testNumericKey() { + $this->assertEquals ("Ooo, a numeric key!", $this->yaml[1040]); + } + + public function testMappingsString() { + $this->assertEquals ("Anyone's name, really.", $this->yaml['String']); + } + + public function testMappingsInt() { + $this->assertSame (13, $this->yaml['Int']); + } + + public function testMappingsBooleanTrue() { + $this->assertSame (true, $this->yaml['True']); + } + + public function testMappingsBooleanFalse() { + $this->assertSame (false, $this->yaml['False']); + } + + public function testMappingsZero() { + $this->assertSame (0, $this->yaml['Zero']); + } + + public function testMappingsNull() { + $this->assertSame (null, $this->yaml['Null']); + } + + public function testMappingsFloat() { + $this->assertSame (5.34, $this->yaml['Float']); + } + + public function testSeq0() { + $this->assertEquals ("PHP Class", $this->yaml[0]); + } + + public function testSeq1() { + $this->assertEquals ("Basic YAML Loader", $this->yaml[1]); + } + + public function testSeq2() { + $this->assertEquals ("Very Basic YAML Dumper", $this->yaml[2]); + } + + public function testSeq3() { + $this->assertEquals (array("YAML is so easy to learn.", + "Your config files will never be the same."), $this->yaml[3]); + } + + public function testSeqMap() { + $this->assertEquals (array("cpu" => "1.5ghz", "ram" => "1 gig", + "os" => "os x 10.4.1"), $this->yaml[4]); + } + + public function testMappedSequence() { + $this->assertEquals (array("yaml.org", "php.net"), $this->yaml['domains']); + } + + public function testAnotherSequence() { + $this->assertEquals (array("program" => "Adium", "platform" => "OS X", + "type" => "Chat Client"), $this->yaml[5]); + } + + public function testFoldedBlock() { + $this->assertEquals ("There isn't any time for your tricks!\nDo you understand?", $this->yaml['no time']); + } + + public function testLiteralAsMapped() { + $this->assertEquals ("There is nothing but time\nfor your tricks.", $this->yaml['some time']); + } + + public function testCrazy() { + $this->assertEquals (array( array("name" => "spartan", "notes" => + array( "Needs to be backed up", + "Needs to be normalized" ), + "type" => "mysql" )), $this->yaml['databases']); + } + + public function testColons() { + $this->assertEquals ("like", $this->yaml["if: you'd"]); + } + + public function testInline() { + $this->assertEquals (array("One", "Two", "Three", "Four"), $this->yaml[6]); + } + + public function testNestedInline() { + $this->assertEquals (array("One", array("Two", "And", "Three"), "Four", "Five"), $this->yaml[7]); + } + + public function testNestedNestedInline() { + $this->assertEquals (array( "This", array("Is", "Getting", array("Ridiculous", "Guys")), + "Seriously", array("Show", "Mercy")), $this->yaml[8]); + } + + public function testInlineMappings() { + $this->assertEquals (array("name" => "chris", "age" => "young", "brand" => "lucky strike"), $this->yaml[9]); + } + + public function testNestedInlineMappings() { + $this->assertEquals (array("name" => "mark", "age" => "older than chris", + "brand" => array("marlboro", "lucky strike")), $this->yaml[10]); + } + + public function testReferences() { + $this->assertEquals (array('Perl', 'Python', 'PHP', 'Ruby'), $this->yaml['dynamic languages']); + } + + public function testReferences2() { + $this->assertEquals (array('C/C++', 'Java'), $this->yaml['compiled languages']); + } + + public function testReferences3() { + $this->assertEquals (array( + array('Perl', 'Python', 'PHP', 'Ruby'), + array('C/C++', 'Java') + ), $this->yaml['all languages']); + } + + public function testEscapedQuotes() { + $this->assertEquals ("you know, this shouldn't work. but it does.", $this->yaml[11]); + } + + public function testEscapedQuotes_2() { + $this->assertEquals ( "that's my value.", $this->yaml[12]); + } + + public function testEscapedQuotes_3() { + $this->assertEquals ("again, that's my value.", $this->yaml[13]); + } + + public function testQuotes() { + $this->assertEquals ("here's to \"quotes\", boss.", $this->yaml[14]); + } + + public function testQuoteSequence() { + $this->assertEquals ( array( 'name' => "Foo, Bar's", 'age' => 20), $this->yaml[15]); + } + + public function testShortSequence() { + $this->assertEquals (array( 0 => "a", 1 => array (0 => 1, 1 => 2), 2 => "b"), $this->yaml[16]); + } + + public function testHash_1() { + $this->assertEquals ("Hash", $this->yaml['hash_1']); + } + + public function testHash_2() { + $this->assertEquals ('Hash #and a comment', $this->yaml['hash_2']); + } + + public function testHash_3() { + $this->assertEquals ('Hash (#) can appear in key too', $this->yaml['hash#3']); + } + + public function testEndloop() { + $this->assertEquals ("Does this line in the end indeed make Spyc go to an infinite loop?", $this->yaml['endloop']); + } + + public function testReallyLargeNumber() { + $this->assertEquals ('115792089237316195423570985008687907853269984665640564039457584007913129639936', $this->yaml['a_really_large_number']); + } + + public function testFloatWithZeros() { + $this->assertSame ('1.0', $this->yaml['float_test']); + } + + public function testFloatWithQuotes() { + $this->assertSame ('1.0', $this->yaml['float_test_with_quotes']); + } + + public function testFloatInverse() { + $this->assertEquals ('001', $this->yaml['float_inverse_test']); + } + + public function testIntArray() { + $this->assertEquals (array (1, 2, 3), $this->yaml['int array']); + } + + public function testArrayOnSeveralLines() { + $this->assertEquals (array (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19), $this->yaml['array on several lines']); + } + + public function testmoreLessKey() { + $this->assertEquals ('', $this->yaml['morelesskey']); + } + + public function testArrayOfZero() { + $this->assertSame (array(0), $this->yaml['array_of_zero']); + } + + public function testSophisticatedArrayOfZero() { + $this->assertSame (array('rx' => array ('tx' => array (0))), $this->yaml['sophisticated_array_of_zero']); + } + + public function testSwitches() { + $this->assertEquals (array (array ('row' => 0, 'col' => 0, 'func' => array ('tx' => array(0, 1)))), $this->yaml['switches']); + } + + public function testEmptySequence() { + $this->assertSame (array(), $this->yaml['empty_sequence']); + } + + public function testEmptyHash() { + $this->assertSame (array(), $this->yaml['empty_hash']); + } + + public function testEmptykey() { + $this->assertSame (array('' => array ('key' => 'value')), $this->yaml['empty_key']); + } + + public function testMultilines() { + $this->assertSame (array(array('type' => 'SomeItem', 'values' => array ('blah', 'blah', 'blah', 'blah'), 'ints' => array(2, 54, 12, 2143))), $this->yaml['multiline_items']); + } + + public function testManyNewlines() { + $this->assertSame ('A quick +fox + + +jumped +over + + + + + +a lazy + + + +dog', $this->yaml['many_lines']); + } + + public function testWerte() { + $this->assertSame (array ('1' => 'nummer 1', '0' => 'Stunde 0'), $this->yaml['werte']); + } + + /* public function testNoIndent() { + $this->assertSame (array( + array ('record1'=>'value1'), + array ('record2'=>'value2') + ) + , $this->yaml['noindent_records']); + } */ + + public function testColonsInKeys() { + $this->assertSame (array (1000), $this->yaml['a:1']); + } + + public function testColonsInKeys2() { + $this->assertSame (array (2000), $this->yaml['a:2']); + } + + public function testSpecialCharacters() { + $this->assertSame ('[{]]{{]]', $this->yaml['special_characters']); + } + + public function testAngleQuotes() { + $Quotes = Spyc::YAMLLoad('quotes.yaml'); + $this->assertEquals (array ('html_tags' => array ('
    ', '

    '), 'html_content' => array ('

    hello world

    ', 'hello
    world'), 'text_content' => array ('hello world')), + $Quotes); + } + + public function testFailingColons() { + $Failing = Spyc::YAMLLoad('failing1.yaml'); + $this->assertSame (array ('MyObject' => array ('Prop1' => array ('key1:val1'))), + $Failing); + } + +} \ No newline at end of file diff --git a/includes/spyc/tests/failing1.yaml b/includes/spyc/tests/failing1.yaml new file mode 100644 index 0000000..6906a51 --- /dev/null +++ b/includes/spyc/tests/failing1.yaml @@ -0,0 +1,2 @@ +MyObject: + Prop1: {key1:val1} \ No newline at end of file diff --git a/includes/spyc/tests/indent_1.yaml b/includes/spyc/tests/indent_1.yaml new file mode 100644 index 0000000..5a55434 --- /dev/null +++ b/includes/spyc/tests/indent_1.yaml @@ -0,0 +1,53 @@ +root: + child_1: 2 + + child_2: 0 + child_3: 1 + +root2: + child_1: 1 +# A comment + child_2: 2 + +displays: + - resolutions: + 1024: 768 + - resolutions: + 1920: 1200 + +display: + - resolutions: + 1024: 768 + 1920: 1200 + producer: "Nec" + +nested_hashes_and_seqs: + - { row: 0, col: 0, headsets_affected: [{ports: [0], side: left}], switch_function: {ics_ptt: true} } + +easier_nest: { h: [{a: b, a1: b1}, {c: d}] } + +steps: + - step: &id001 + instrument: Lasik 2000 + pulseEnergy: 5.4 + pulseDuration: 12 + repetition: 1000 + spotSize: 1mm + - step: + <<: *id001 + spotSize: 2mm + +death masks are: + sad: 2 + <<: {magnificent: 4} + +login: &login + adapter: mysql + host: localhost + +development: + database: rails_dev + <<: *login + +"key": "value:" +colon_only: ":" \ No newline at end of file diff --git a/includes/spyc/tests/quotes.yaml b/includes/spyc/tests/quotes.yaml new file mode 100644 index 0000000..2ceea86 --- /dev/null +++ b/includes/spyc/tests/quotes.yaml @@ -0,0 +1,8 @@ +html_tags: + -
    + -

    +html_content: + -

    hello world

    + - hello
    world +text_content: + - hello world \ No newline at end of file diff --git a/includes/tar.class.php b/includes/tar.class.php new file mode 100644 index 0000000..c8070b6 --- /dev/null +++ b/includes/tar.class.php @@ -0,0 +1,467 @@ + +Description: + This class reads and writes Tape-Archive (TAR) Files and Gzip + compressed TAR files, which are mainly used on UNIX systems. + This class works on both windows AND unix systems, and does + NOT rely on external applications!! Woohoo! +Usage: + Copyright (C) 2002 Josh Barger + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details at: + http://www.gnu.org/copyleft/lesser.html + If you use this script in your application/website, please + send me an e-mail letting me know about it :) +Bugs: + Please report any bugs you might find to my e-mail address + at joshb@npt.com. If you have already created a fix/patch + for the bug, please do send it to me so I can incorporate it into my release. +Version History: + 1.0 04/10/2002 - InitialRelease + 2.0 04/11/2002 - Merged both tarReader and tarWriter + classes into one + - Added support for gzipped tar files + Remember to name for .tar.gz or .tgz + if you use gzip compression! + :: THIS REQUIRES ZLIB EXTENSION :: + - Added additional comments to + functions to help users + - Added ability to remove files and + directories from archive + 2.1 04/12/2002 - Fixed serious bug in generating tar + - Created another example file + - Added check to make sure ZLIB is + installed before running GZIP + compression on TAR + 2.2 05/07/2002 - Added automatic detection of Gzipped + tar files (Thanks go to Jrgen Falch + for the idea) + - Changed "private" functions to have + special function names beginning with + two underscores +======================================================================= +*/ +class tar { + // Unprocessed Archive Information + public $filename; + public $isGzipped; + public $tar_file; + // Processed Archive Information + public $files; + public $directories; + public $numFiles = 0; + public $numDirectories = 0; + // Class Constructor -- Does nothing... + function tar() { + return true; + } + // Computes the unsigned Checksum of a file's header + // to try to ensure valid file + // PRIVATE ACCESS FUNCTION + function __computeUnsignedChecksum($bytestring) { + $unsigned_chksum=0; + for($i=0; $i<512; $i++) + $unsigned_chksum += ord($bytestring[$i]); + for($i=0; $i<8; $i++) + $unsigned_chksum -= ord($bytestring[148 + $i]); + $unsigned_chksum += ord(" ") * 8; + return $unsigned_chksum; + } + // Converts a NULL padded string to a non-NULL padded string + // PRIVATE ACCESS FUNCTION + function __parseNullPaddedString($string) { + $position = strpos($string,chr(0)); + return substr($string,0,$position); + } + // This function parses the current TAR file + // PRIVATE ACCESS FUNCTION + function __parseTar() { + // Read Files from archive + $tar_length = strlen($this->tar_file); + $main_offset = 0; + while($main_offset < $tar_length) { + // If we read a block of 512 nulls, we are at the end of the archive + if(substr($this->tar_file,$main_offset,512) == str_repeat(chr(0),512)) + break; + // Parse file name + $file_name = $this->__parseNullPaddedString(substr($this->tar_file,$main_offset,100)); + // Parse the file mode + $file_mode = substr($this->tar_file,$main_offset + 100,8); + // Parse the file user ID + $file_uid = octdec(substr($this->tar_file,$main_offset + 108,8)); + // Parse the file group ID + $file_gid = octdec(substr($this->tar_file,$main_offset + 116,8)); + // Parse the file size + $file_size = octdec(substr($this->tar_file,$main_offset + 124,12)); + // Parse the file update time - unix timestamp format + $file_time = octdec(substr($this->tar_file,$main_offset + 136,12)); + // Parse Checksum + $file_chksum = octdec(substr($this->tar_file,$main_offset + 148,6)); + // Parse user name + $file_uname = $this->__parseNullPaddedString(substr($this->tar_file,$main_offset + 265,32)); + // Parse Group name + $file_gname = $this->__parseNullPaddedString(substr($this->tar_file,$main_offset + 297,32)); + // Make sure our file is valid + if($this->__computeUnsignedChecksum(substr($this->tar_file,$main_offset,512)) != $file_chksum) + return false; + // Parse File Contents + $file_contents = substr($this->tar_file,$main_offset + 512,$file_size); + /* ### Unused Header Information ### + $activeFile["typeflag"] = substr($this->tar_file,$main_offset + 156,1); + $activeFile["linkname"] = substr($this->tar_file,$main_offset + 157,100); + $activeFile["magic"] = substr($this->tar_file,$main_offset + 257,6); + $activeFile["version"] = substr($this->tar_file,$main_offset + 263,2); + $activeFile["devmajor"] = substr($this->tar_file,$main_offset + 329,8); + $activeFile["devminor"] = substr($this->tar_file,$main_offset + 337,8); + $activeFile["prefix"] = substr($this->tar_file,$main_offset + 345,155); + $activeFile["endheader"] = substr($this->tar_file,$main_offset + 500,12); + */ + if($file_size > 0) { + // Increment number of files + $this->numFiles++; + // Create us a new file in our array + $activeFile = &$this->files[]; + // Asign Values + $activeFile["name"] = $file_name; + $activeFile["mode"] = $file_mode; + $activeFile["size"] = $file_size; + $activeFile["time"] = $file_time; + $activeFile["user_id"] = $file_uid; + $activeFile["group_id"] = $file_gid; + $activeFile["user_name"] = $file_uname; + $activeFile["group_name"] = $file_gname; + $activeFile["checksum"] = $file_chksum; + $activeFile["file"] = $file_contents; + } else { + // Increment number of directories + $this->numDirectories++; + // Create a new directory in our array + $activeDir = &$this->directories[]; + // Assign values + $activeDir["name"] = $file_name; + $activeDir["mode"] = $file_mode; + $activeDir["time"] = $file_time; + $activeDir["user_id"] = $file_uid; + $activeDir["group_id"] = $file_gid; + $activeDir["user_name"] = $file_uname; + $activeDir["group_name"] = $file_gname; + $activeDir["checksum"] = $file_chksum; + } + // Move our offset the number of blocks we have processed + $main_offset += 512 + (ceil($file_size / 512) * 512); + } + return true; + } + // Read a non gzipped tar file in for processing + // PRIVATE ACCESS FUNCTION + function __readTar($filename='') { + // Set the filename to load + if(!$filename) + $filename = $this->filename; + // Read in the TAR file + $fp = fopen($filename,"rb"); + $this->tar_file = fread($fp,filesize($filename)); + fclose($fp); + if($this->tar_file[0] == chr(31) && $this->tar_file[1] == chr(139) && $this->tar_file[2] == chr(8)) { + if(!function_exists("gzinflate")) + return false; + $this->isGzipped = TRUE; + $this->tar_file = gzinflate(substr($this->tar_file,10,-4)); + } + // Parse the TAR file + $this->__parseTar(); + return true; + } + // Generates a TAR file from the processed data + // PRIVATE ACCESS FUNCTION + function __generateTAR() { + // Clear any data currently in $this->tar_file + unset($this->tar_file); + $this->tar_file=''; + // Generate Records for each directory, if we have directories + if($this->numDirectories > 0) { + foreach($this->directories as $key => $information) { + unset($header); + // Generate tar header for this directory + // Filename, Permissions, UID, GID, size, Time, checksum, typeflag, linkname, magic, version, user name, group name, devmajor, devminor, prefix, end + $header .= str_pad($information["name"],100,chr(0)); + $header .= str_pad(decoct($information["mode"]),7,"0",STR_PAD_LEFT) . chr(0); + $header .= str_pad(decoct($information["user_id"]),7,"0",STR_PAD_LEFT) . chr(0); + $header .= str_pad(decoct($information["group_id"]),7,"0",STR_PAD_LEFT) . chr(0); + $header .= str_pad(decoct(0),11,"0",STR_PAD_LEFT) . chr(0); + $header .= str_pad(decoct($information["time"]),11,"0",STR_PAD_LEFT) . chr(0); + $header .= str_repeat(" ",8); + $header .= "5"; + $header .= str_repeat(chr(0),100); + $header .= str_pad("ustar",6,chr(32)); + $header .= chr(32) . chr(0); + $header .= str_pad("",32,chr(0)); + $header .= str_pad("",32,chr(0)); + $header .= str_repeat(chr(0),8); + $header .= str_repeat(chr(0),8); + $header .= str_repeat(chr(0),155); + $header .= str_repeat(chr(0),12); + // Compute header checksum + $checksum = str_pad(decoct($this->__computeUnsignedChecksum($header)),6,"0",STR_PAD_LEFT); + for($i=0; $i<6; $i++) { + $header[(148 + $i)] = substr($checksum,$i,1); + } + $header[154] = chr(0); + $header[155] = chr(32); + // Add new tar formatted data to tar file contents + $this->tar_file .= $header; + } + } + // Generate Records for each file, if we have files (We should...) + if($this->numFiles > 0) { + foreach($this->files as $key => $information) { + unset($header); + $header=''; + // Generate the TAR header for this file + // Filename, Permissions, UID, GID, size, Time, checksum, typeflag, linkname, magic, version, user name, group name, devmajor, devminor, prefix, end + $header .= str_pad($information["name"],100,chr(0)); + $header .= str_pad(decoct($information["mode"]),7,"0",STR_PAD_LEFT) . chr(0); + $header .= str_pad(decoct($information["user_id"]),7,"0",STR_PAD_LEFT) . chr(0); + $header .= str_pad(decoct($information["group_id"]),7,"0",STR_PAD_LEFT) . chr(0); + $header .= str_pad(decoct($information["size"]),11,"0",STR_PAD_LEFT) . chr(0); + $header .= str_pad(decoct($information["time"]),11,"0",STR_PAD_LEFT) . chr(0); + $header .= str_repeat(" ",8); + $header .= "0"; + $header .= str_repeat(chr(0),100); + $header .= str_pad("ustar",6,chr(32)); + $header .= chr(32) . chr(0); + $header .= str_pad($information["user_name"],32,chr(0)); // How do I get a file's user name from PHP? + $header .= str_pad($information["group_name"],32,chr(0)); // How do I get a file's group name from PHP? + $header .= str_repeat(chr(0),8); + $header .= str_repeat(chr(0),8); + $header .= str_repeat(chr(0),155); + $header .= str_repeat(chr(0),12); + // Compute header checksum + $checksum = str_pad(decoct($this->__computeUnsignedChecksum($header)),6,"0",STR_PAD_LEFT); + for($i=0; $i<6; $i++) { + $header[(148 + $i)] = substr($checksum,$i,1); + } + $header[154] = chr(0); + $header[155] = chr(32); + // Pad file contents to byte count divisible by 512 + $file_contents = str_pad($information["file"],(ceil($information["size"] / 512) * 512),chr(0)); + // Add new tar formatted data to tar file contents + $this->tar_file .= $header . $file_contents; + } + } + // Add 512 bytes of NULLs to designate EOF + $this->tar_file .= str_repeat(chr(0),512); + return true; + } + // Open a TAR file + function openTAR($filename) { + // Clear any values from previous tar archives + //unset($this->filename); + $this->filename=''; + //unset($this->isGzipped); + $this->isGzipped=0; + //unset($this->tar_file); + $this->tar_file=0; + //unset($this->files); + $this->files=''; + //unset($this->directories); + //unset($this->numFiles); + //unset($this->numDirectories); + $this->directories=''; + $this->numFiles=0; + $this->numDirectories=0; + // If the tar file doesn't exist... + if(!file_exists($filename)) + return false; + $this->filename = $filename; + // Parse this file + $this->__readTar(); + return true; + } + // Appends a tar file to the end of the currently opened tar file + function appendTar($filename) { + // If the tar file doesn't exist... + if(!file_exists($filename)) + return false; + $this->__readTar($filename); + return true; + } + // Retrieves information about a file in the current tar archive + function getFile($filename) { + if($this->numFiles > 0) { + foreach($this->files as $key => $information) { + if($information["name"] == $filename) + return $information; + } + } + return false; + } + // Retrieves information about a directory in the current tar archive + function getDirectory($dirname) { + if($this->numDirectories > 0) { + foreach($this->directories as $key => $information) { + if($information["name"] == $dirname) + return $information; + } + } + return false; + } + // Check if this tar archive contains a specific file + function containsFile($filename) { + if($this->numFiles > 0) { + foreach($this->files as $key => $information) { + if($information["name"] == $filename) + return true; + } + } + return false; + } + // Check if this tar archive contains a specific directory + function containsDirectory($dirname) { + if($this->numDirectories > 0) { + foreach($this->directories as $key => $information) { + if($information["name"] == $dirname) + return true; + } + } + return false; + } + // Add a directory to this tar archive + function addDirectory($dirname) { + if(!file_exists($dirname)) + return false; + // Get directory information + $file_information = stat($dirname); + // Add directory to processed data + $this->numDirectories++; + $activeDir = &$this->directories[]; + $activeDir["name"] = $dirname; + $activeDir["mode"] = $file_information["mode"]; + $activeDir["time"] = $file_information["time"]; + $activeDir["user_id"] = $file_information["uid"]; + $activeDir["group_id"] = $file_information["gid"]; + $activeDir["checksum"] = $checksum; + return true; + } + // Add a file to the tar archive + function addFile($filename) { + // Make sure the file we are adding exists! + if(!file_exists($filename)) + return false; + // Make sure there are no other files in the archive that have this same filename + if($this->containsFile($filename)) + return false; + // Get file information + $file_information = stat($filename); + // Read in the file's contents + $fp = fopen($filename,"rb"); + $file_contents = fread($fp,filesize($filename)); + fclose($fp); + // Add file to processed data + $this->numFiles++; + $activeFile = &$this->files[]; + $activeFile["name"] = $filename; + $activeFile["mode"] = $file_information["mode"]; + $activeFile["user_id"] = $file_information["uid"]; + $activeFile["group_id"] = $file_information["gid"]; + $activeFile["size"] = $file_information["size"]; + $activeFile["time"] = $file_information["mtime"]; + $activeFile["checksum"] = $checksum; + $activeFile["user_name"] = ""; + $activeFile["group_name"] = ""; + $activeFile["file"] = $file_contents; + return true; + } + // Add a file to the tar archive + function addData($filename,$data,$time=0) { + global $gBitSystem; + // Make sure there are no other files in the archive that have this same filename + if($this->containsFile($filename)) + return false; + if (!$time) $time=$gBitSystem->getUTCTime(); + // Read in the file's contents + $file_contents = $data; + // Add file to processed data + $this->numFiles++; + $activeFile = &$this->files[]; + $activeFile["name"] = $filename; + $activeFile["mode"] = octdec("666"); + $activeFile["user_id"] = ""; + $activeFile["group_id"] = ""; + $activeFile["size"] = strlen($data); + $activeFile["time"] = $time; + if(!isset($checksum)) $checksum=0; + $activeFile["checksum"] = $checksum; + $activeFile["user_name"] = ""; + $activeFile["group_name"] = ""; + $activeFile["file"] = $file_contents; + return true; + } + // Remove a file from the tar archive + function removeFile($filename) { + if($this->numFiles > 0) { + foreach($this->files as $key => $information) { + if($information["name"] == $filename) { + $this->numFiles--; + unset($this->files[$key]); + return true; + } + } + } + return false; + } + // Remove a directory from the tar archive + function removeDirectory($dirname) { + if($this->numDirectories > 0) { + foreach($this->directories as $key => $information) { + if($information["name"] == $dirname) { + $this->numDirectories--; + unset($this->directories[$key]); + return true; + } + } + } + return false; + } + // Write the currently loaded tar archive to disk + function saveTar() { + if(!$this->filename) + return false; + // Write tar to current file using specified gzip compression + $this->toTar($this->filename,$this->isGzipped); + return true; + } + // Saves tar archive to a different file than the current file + function toTar($filename,$useGzip) { + if(!$filename) + return false; + // Encode processed files into TAR file format + $this->__generateTar(); + // GZ Compress the data if we need to + if($useGzip) { + // Make sure we have gzip support + if(!function_exists("gzencode")) + return false; + $file = gzencode($this->tar_file); + } else { + $file = $this->tar_file; + } + // Write the TAR file + $fp = fopen($filename,"wb"); + fwrite($fp,$file); + fclose($fp); + return true; + } +} +?> diff --git a/includes/template_biticon_updater.sh b/includes/template_biticon_updater.sh new file mode 100644 index 0000000..e7cf3b5 --- /dev/null +++ b/includes/template_biticon_updater.sh @@ -0,0 +1,340 @@ +#!/bin/bash +echo "This script will update all your custom templates to use the new set of +icons used in bitweaver R2. To run this script please copy it to your theme +directory and execute it with: sh $0" + +if [[ ( $1 == '--help' ) || ( $1 == '-h' ) || ( $1 == '?' ) ]] +then + exit +fi + +# Check to see if we've already made a backup +if [ -f theme_backup.tar.gz ] +then + echo "I have found a css backup file. Please rename or remove the file +theme_backup.tar.gz before executing this script again." + exit +fi + +echo "Creating backup of theme." +tar -czf theme_backup.tar.gz * +echo +echo "The script will continue in 5 seconds - hit to abort." +echo 5; sleep 1 +echo 4; sleep 1 +echo 3; sleep 1 +echo 2; sleep 1 +echo 1; sleep 1 +echo +echo "====== Executing substitutions ======" +echo +echo "------ Doing Biticons Now ------" +echo + +# we should make sure that ipackage comes before iname - this is true in bitweaver but who knows in custom templates... +#find . -name "*.tpl" -exec perl -i -wpe 's/{biticon([^}]*?)iname="?(\w+)"?([^}]*?)ipackage="?(\w+)"?/{biticon$1ipackage="$4"$3iname="$2" /g' {} \; + +echo substituting liberty biticons +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\baccept"?\s/{biticon$1ipackage="icons"$2iname="dialog-ok" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\badministration"?\s/{biticon$1ipackage="icons"$2iname="preferences-system" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bassign"?\s/{biticon$1ipackage="icons"$2iname="mail-attachment" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bback"?\s/{biticon$1ipackage="icons"$2iname="go-previous" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bbithelp"?\s/{biticon$1ipackage="icons"$2iname="help-browser" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bcancel"?\s/{biticon$1ipackage="icons"$2iname="dialog-cancel" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bclose"?\s/{biticon$1ipackage="icons"$2iname="window-close" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bconfig"?\s/{biticon$1ipackage="icons"$2iname="document-properties" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bcopy"?\s/{biticon$1ipackage="icons"$2iname="edit-copy" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bcopy_folder"?\s/{biticon$1ipackage="icons"$2iname="edit-copy" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bcurrent"?\s/{biticon$1ipackage="icons"$2iname="emblem-default" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bedit"?\s/{biticon$1ipackage="icons"$2iname="accessories-text-editor" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bedit_small"?\s/{biticon$1ipackage="icons"$2iname="accessories-text-editor" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bdelete"?\s/{biticon$1ipackage="icons"$2iname="edit-delete" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bdelete_small"?\s/{biticon$1ipackage="icons"$2iname="edit-delete" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bdownload"?\s/{biticon$1ipackage="icons"$2iname="emblem-downloads" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\berror"?\s/{biticon$1ipackage="icons"$2iname="dialog-error" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bexport"?\s/{biticon$1ipackage="icons"$2iname="document-save-as" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bfind"?\s/{biticon$1ipackage="icons"$2iname="edit-find" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bdirectory"?\s/{biticon$1ipackage="icons"$2iname="folder" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bfolder"?\s/{biticon$1ipackage="icons"$2iname="folder" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bfolder_open"?\s/{biticon$1ipackage="icons"$2iname="folder-open" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bhelp"?\s/{biticon$1ipackage="icons"$2iname="help-contents" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bhistory"?\s/{biticon$1ipackage="icons"$2iname="appointment-new" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bhome"?\s/{biticon$1ipackage="icons"$2iname="go-home" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bimport"?\s/{biticon$1ipackage="icons"$2iname="document-open" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\binfo"?\s/{biticon$1ipackage="icons"$2iname="dialog-information" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\binsert"?\s/{biticon$1ipackage="icons"$2iname="insert-object" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bmail_send"?\s/{biticon$1ipackage="icons"$2iname="mail-forward" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bnote"?\s/{biticon$1ipackage="icons"$2iname="x-office-document" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bnav_up"?\s/{biticon$1ipackage="icons"$2iname="go-up" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bnav_down"?\s/{biticon$1ipackage="icons"$2iname="go-down" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bnav_next"?\s/{biticon$1ipackage="icons"$2iname="go-next" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bnav_prev"?\s/{biticon$1ipackage="icons"$2iname="go-previous" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bnav_first"?\s/{biticon$1ipackage="icons"$2iname="go-first" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bnav_last"?\s/{biticon$1ipackage="icons"$2iname="go-last" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bnew"?\s/{biticon$1ipackage="icons"$2iname="document-new" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bpermissions"?\s/{biticon$1ipackage="icons"$2iname="emblem-shared" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bpermissions_set"?\s/{biticon$1ipackage="icons"$2iname="emblem-readonly" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bplugin"?\s/{biticon$1ipackage="icons"$2iname="applications-accessories" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bprint"?\s/{biticon$1ipackage="icons"$2iname="document-print" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\brefresh"?\s/{biticon$1ipackage="icons"$2iname="view-refresh" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\breply"?\s/{biticon$1ipackage="icons"$2iname="mail-reply-sender" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\breply_quote"?\s/{biticon$1ipackage="icons"$2iname="mail-reply-all" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bsave"?\s/{biticon$1ipackage="icons"$2iname="document-save" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bsettings"?\s/{biticon$1ipackage="icons"$2iname="emblem-system" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bsort_asc"?\s/{biticon$1ipackage="icons"$2iname="view-sort-ascending" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bsort_desc"?\s/{biticon$1ipackage="icons"$2iname="view-sort-descending" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bsuccess"?\s/{biticon$1ipackage="icons"$2iname="dialog-ok" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bupload"?\s/{biticon$1ipackage="icons"$2iname="applications-internet" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bview"?\s/{biticon$1ipackage="icons"$2iname="document-open" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bwarning"?\s/{biticon$1ipackage="icons"$2iname="dialog-warning" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\berror_large"?\s/{biticon$1ipackage="icons"$2iname="dialog-error" ipath="large" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bwarning_large"?\s/{biticon$1ipackage="icons"$2iname="dialog-warning" ipath="large" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\blist"?\s/{biticon$1ipackage="icons"$2iname="format-justify-fill" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bcollapsed"?\s/{biticon$1ipackage="icons"$2iname="list-add" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bexpanded"?\s/{biticon$1ipackage="icons"$2iname="list-remove" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bpost"?\s/{biticon$1ipackage="icons"$2iname="mail-forward" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bsecurity"?\s/{biticon$1ipackage="icons"$2iname="emblem-readonly" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bsort"?\s/{biticon$1ipackage="icons"$2iname="emblem-symbolic-link" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\btree"?\s/{biticon$1ipackage="icons"$2iname="folder-remote" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bactive"?\s/{biticon$1ipackage="icons"$2iname="face-smile" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\binactive"?\s/{biticon$1ipackage="icons"$2iname="face-sad" /g' {} \; + +echo substituting users biticons +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\busers\b"?([^}]*?)iname="?\bbatch-assign"?\s/{biticon$1ipackage="icons"$2iname="mail-attachment" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\busers\b"?([^}]*?)iname="?\btasks"?\s/{biticon$1ipackage="icons"$2iname="task-due" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\busers\b"?([^}]*?)iname="?\busers"?\s/{biticon$1ipackage="icons"$2iname="system-users" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\busers\b"?([^}]*?)iname="?\bunknown_user"?\s/{biticon$1ipackage="icons"$2iname="user-offline" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\busers\b"?([^}]*?)iname="?\bfavorite"?\s/{biticon$1ipackage="icons"$2iname="emblem-favorite" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\busers\b"?([^}]*?)iname="?\bsend_msg_small"?\s/{biticon$1ipackage="icons"$2iname="mail-forward style="width:8px;height:8px;"" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\busers\b"?([^}]*?)iname="?\btasks"?\s/{biticon$1ipackage="icons"$2iname="x-office-calendar" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\busers\b"?([^}]*?)iname="?\bhome"?\s/{biticon$1ipackage="icons"$2iname="go-home" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\busers\b"?([^}]*?)iname="?\bbookmarks"?\s/{biticon$1ipackage="icons"$2iname="system-file-manager" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\busers\b"?([^}]*?)iname="?\bgroups"?\s/{biticon$1ipackage="icons"$2iname="preferences-desktop" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\busers\b"?([^}]*?)iname="?\bwatch"?\s/{biticon$1ipackage="icons"$2iname="weather-clear" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\busers\b"?([^}]*?)iname="?\bunwatch"?\s/{biticon$1ipackage="icons"$2iname="weather-clear-night" /g' {} \; + +echo substituting bitboards biticons +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bbitboard\b"?([^}]*?)iname="?\bedit_add"?\s/{biticon$1ipackage="icons"$2iname="list-add" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bbitboard\b"?([^}]*?)iname="?\bedit_remove"?\s/{biticon$1ipackage="icons"$2iname="list-remove" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bbitboard\b"?([^}]*?)iname="?\bsticky"?\s/{biticon$1ipackage="icons"$2iname="emblem-important" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bbitboard\b"?([^}]*?)iname="?\bmake_sticky"?\s/{biticon$1ipackage="icons"$2iname="go-top" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bbitboard\b"?([^}]*?)iname="?\bmove"?\s/{biticon$1ipackage="icons"$2iname="go-jump" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bbitboard\b"?([^}]*?)iname="?\bnew_topic"?\s/{biticon$1ipackage="icons"$2iname="mail-message-new" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bbitboard\b"?([^}]*?)iname="?\bpost_reply"?\s/{biticon$1ipackage="icons"$2iname="mail-reply-sender" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bbitboard\b"?([^}]*?)iname="?\bnotify_off"?\s/{biticon$1ipackage="icons"$2iname="media-stop" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bbitboard\b"?([^}]*?)iname="?\bnotify_on"?\s/{biticon$1ipackage="icons"$2iname="media-record" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bbitboard\b"?([^}]*?)iname="?\btrack_new"?\s/{biticon$1ipackage="icons"$2iname="media-record" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bbitboard\b"?([^}]*?)iname="?\btrack_new_large"?\s/{biticon$1ipackage="icons"$2iname="media-record" ipath="large" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bbitboard\b"?([^}]*?)iname="?\btrack_old"?\s/{biticon$1ipackage="icons"$2iname="media-playback-pause" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bbitboard\b"?([^}]*?)iname="?\btrack_old_large"?\s/{biticon$1ipackage="icons"$2iname="media-playback-pause" ipath="large" /g' {} \; + +# move locked and unlocked to liberty +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bbitboards\b"?([^}]*?)iname="?\blocked"?\s/{biticon$1ipackage="icons"$2iname="emblem-readonly" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bbitboards\b"?([^}]*?)iname="?\bunlocked"?\s/{biticon$1ipackage="icons"$2iname="emblem-default" /g' {} \; + +echo substituting fisheye biticons +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bfisheye\b"?([^}]*?)iname="?\blocked"?\s/{biticon$1ipackage="icons"$2iname="emblem-readonly" /g' {} \; + +echo substituting gatekeeper biticons +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bgatekeeper\b"?([^}]*?)iname="?\bsecurity"?\s/{biticon$1ipackage="icons"$2iname="emblem-readonly" /g' {} \; + +echo substituting messages biticons +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bmessages\b"?([^}]*?)iname="?\bflagged"?\s/{biticon$1ipackage="icons"$2iname="mail-mark-important" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bmessages\b"?([^}]*?)iname="?\bmail"?\s/{biticon$1ipackage="icons"$2iname="mail-message-new" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bmessages\b"?([^}]*?)iname="?\brecieve_mail"?\s/{biticon$1ipackage="icons"$2iname="mail-send-receive" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bmessages\b"?([^}]*?)iname="?\bsend_mail"?\s/{biticon$1ipackage="icons"$2iname="mail-send-receive" /g' {} \; + +echo substituting nexus biticons +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bnexus\b"?([^}]*?)iname="?\bmenu"?\s/{biticon$1ipackage="icons"$2iname="folder-remote" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bnexus\b"?([^}]*?)iname="?\borganise"?\s/{biticon$1ipackage="icons"$2iname="view-refresh" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bnexus\b"?([^}]*?)iname="?\bremove_dead"?\s/{biticon$1ipackage="icons"$2iname="mail-mark-junk" /g' {} \; + +echo substituting pigeonholes biticons +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bpigeonholes\b"?([^}]*?)iname="?\borganise"?\s/{biticon$1ipackage="icons"$2iname="view-refresh" /g' {} \; + +echo substituting quota biticons +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bquota\b"?([^}]*?)iname="?\bquota"?\s/{biticon$1ipackage="icons"$2iname="drive-harddisk" /g' {} \; + +echo substituting rss biticons +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\brss\b"?([^}]*?)iname="?\brss"?\s/{biticon$1ipackage="icons"$2iname="network-wireless" /g' {} \; + +echo substituting wiki biticons +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bwiki\b"?([^}]*?)iname="?\blocked"?\s/{biticon$1ipackage="icons"$2iname="emblem-readonly" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bwiki\b"?([^}]*?)iname="?\bunlocked"?\s/{biticon$1ipackage="icons"$2iname="emblem-default" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bwiki\b"?([^}]*?)iname="?\bs5"?\s/{biticon$1ipackage="icons"$2iname="x-office-presentation" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bwiki\b"?([^}]*?)iname="?\bslides"?\s/{biticon$1ipackage="icons"$2iname="x-office-presentation" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bwiki\b"?([^}]*?)iname="?\btree"?\s/{biticon$1ipackage="icons"$2iname="already dealt with" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bwiki\b"?([^}]*?)iname="?\bwebhelp"?\s/{biticon$1ipackage="icons"$2iname="help-browser" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bwiki\b"?([^}]*?)iname="?\bwebhelp_toc"?\s/{biticon$1ipackage="icons"$2iname="help-contents" /g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bwiki\b"?([^}]*?)iname="?\bbook"?\s/{biticon$1ipackage="icons"$2iname="x-office-address-book" /g' {} \; + +echo substituting gigaupload biticons +find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bgigaupload\b"?([^}]*?)iname="?\bbusy"?\s/{biticon$1ipackage="icons"$2iname="busy" /g' {} \; + +echo;echo;echo +echo "------ Doing Smartlinks Now ------" +echo +echo substituting liberty smartlinks +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/accept"#{smartlink$1ibiticon="icons/dialog-ok"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/administration"#{smartlink$1ibiticon="icons/preferences-system"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/assign"#{smartlink$1ibiticon="icons/mail-attachment"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/back"#{smartlink$1ibiticon="icons/go-previous"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/bithelp"#{smartlink$1ibiticon="icons/help-browser"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/cancel"#{smartlink$1ibiticon="icons/dialog-cancel"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/close"#{smartlink$1ibiticon="icons/window-close"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/config"#{smartlink$1ibiticon="icons/document-properties"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/copy"#{smartlink$1ibiticon="icons/edit-copy"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/copy_folder"#{smartlink$1ibiticon="icons/edit-copy"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/current"#{smartlink$1ibiticon="icons/emblem-default"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/edit"#{smartlink$1ibiticon="icons/accessories-text-editor"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/edit_small"#{smartlink$1ibiticon="icons/accessories-text-editor"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/delete"#{smartlink$1ibiticon="icons/edit-delete"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/delete_small"#{smartlink$1ibiticon="icons/edit-delete"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/download"#{smartlink$1ibiticon="icons/emblem-downloads"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/error"#{smartlink$1ibiticon="icons/dialog-error"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/export"#{smartlink$1ibiticon="icons/document-save-as"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/find"#{smartlink$1ibiticon="icons/edit-find"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/directory"#{smartlink$1ibiticon="icons/folder"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/folder"#{smartlink$1ibiticon="icons/folder"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/folder_open"#{smartlink$1ibiticon="icons/folder-open"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/help"#{smartlink$1ibiticon="icons/help-contents"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/history"#{smartlink$1ibiticon="icons/appointment-new"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/home"#{smartlink$1ibiticon="icons/go-home"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/import"#{smartlink$1ibiticon="icons/document-open"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/info"#{smartlink$1ibiticon="icons/dialog-information"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/insert"#{smartlink$1ibiticon="icons/insert-object"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/mail_send"#{smartlink$1ibiticon="icons/mail-forward"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/note"#{smartlink$1ibiticon="icons/x-office-document"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/nav_up"#{smartlink$1ibiticon="icons/go-up"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/nav_down"#{smartlink$1ibiticon="icons/go-down"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/nav_next"#{smartlink$1ibiticon="icons/go-next"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/nav_prev"#{smartlink$1ibiticon="icons/go-previous"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/nav_first"#{smartlink$1ibiticon="icons/go-first"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/nav_last"#{smartlink$1ibiticon="icons/go-last"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/new"#{smartlink$1ibiticon="icons/document-new"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/permissions"#{smartlink$1ibiticon="icons/emblem-shared"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/permissions_set"#{smartlink$1ibiticon="icons/emblem-readonly"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/plugin"#{smartlink$1ibiticon="icons/applications-accessories"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/print"#{smartlink$1ibiticon="icons/document-print"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/refresh"#{smartlink$1ibiticon="icons/view-refresh"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/reply"#{smartlink$1ibiticon="icons/mail-reply-sender"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/reply_quote"#{smartlink$1ibiticon="icons/mail-reply-all"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/save"#{smartlink$1ibiticon="icons/document-save"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/settings"#{smartlink$1ibiticon="icons/emblem-system"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/sort_asc"#{smartlink$1ibiticon="icons/view-sort-ascending"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/sort_desc"#{smartlink$1ibiticon="icons/view-sort-descending"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/success"#{smartlink$1ibiticon="icons/dialog-ok"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/upload"#{smartlink$1ibiticon="icons/applications-internet"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/view"#{smartlink$1ibiticon="icons/document-open"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/warning"#{smartlink$1ibiticon="icons/dialog-warning"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/error_large"#{smartlink$1ibiticon="liberty/large/dialog-error"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/warning_large"#{smartlink$1ibiticon="liberty/large/dialog-warning"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/list"#{smartlink$1ibiticon="icons/format-justify-fill"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/collapsed"#{smartlink$1ibiticon="icons/list-add"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/expanded"#{smartlink$1ibiticon="icons/list-remove"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/post"#{smartlink$1ibiticon="icons/mail-forward"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/security"#{smartlink$1ibiticon="icons/emblem-readonly"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/sort"#{smartlink$1ibiticon="icons/emblem-symbolic-link"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/tree"#{smartlink$1ibiticon="icons/folder-remote"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/active"#{smartlink$1ibiticon="icons/face-smile"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/inactive"#{smartlink$1ibiticon="icons/face-sad"#g' {} \; + +echo substituting users smartlinks +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="users/batch-assign"#{smartlink$1ibiticon="icons/mail-attachment"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="users/tasks"#{smartlink$1ibiticon="icons/task-due"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="users/users"#{smartlink$1ibiticon="icons/system-users"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="users/unknown_user"#{smartlink$1ibiticon="icons/user-offline"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="users/favorite"#{smartlink$1ibiticon="icons/emblem-favorite"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="users/send_msg_small"#{smartlink$1ibiticon="icons/mail-forward style="#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="users/tasks"#{smartlink$1ibiticon="icons/x-office-calendar"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="users/home"#{smartlink$1ibiticon="icons/go-home"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="users/bookmarks"#{smartlink$1ibiticon="icons/system-file-manager"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="users/groups"#{smartlink$1ibiticon="icons/preferences-desktop"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="users/watch"#{smartlink$1ibiticon="icons/weather-clear"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="users/unwatch"#{smartlink$1ibiticon="icons/weather-clear-night"#g' {} \; + +echo substituting bitboards smartlinks +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="bitboard/edit_add"#{smartlink$1ibiticon="icons/list-add"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="bitboard/edit_remove"#{smartlink$1ibiticon="icons/list-remove"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="bitboard/sticky"#{smartlink$1ibiticon="icons/emblem-important"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="bitboard/make_sticky"#{smartlink$1ibiticon="icons/go-top"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="bitboard/move"#{smartlink$1ibiticon="icons/go-jump"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="bitboard/new_topic"#{smartlink$1ibiticon="icons/mail-message-new"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="bitboard/post_reply"#{smartlink$1ibiticon="icons/mail-reply-sender"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="bitboard/notify_off"#{smartlink$1ibiticon="icons/media-stop"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="bitboard/notify_on"#{smartlink$1ibiticon="icons/media-record"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="bitboard/track_new"#{smartlink$1ibiticon="icons/mail-mark-read"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="bitboard/track_new_large"#{smartlink$1ibiticon="icons/large/mail-mark-read"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="bitboard/track_old"#{smartlink$1ibiticon="icons/mail-mark-unread"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="bitboard/track_old_large"#{smartlink$1ibiticon="icons/large/mail-mark-unread"#g' {} \; + +# move locked and unlocked to liberty +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="bitboards/locked"#{smartlink$1ibiticon="icons/emblem-readonly"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="bitboards/unlocked"#{smartlink$1ibiticon="icons/emblem-default"#g' {} \; + +echo substituting fisheye smartlinks +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="fisheye/locked"#{smartlink$1ibiticon="icons/emblem-readonly"#g' {} \; + +echo substituting gatekeeper smartlinks +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="gatekeeper/security"#{smartlink$1ibiticon="icons/emblem-readonly"#g' {} \; + +echo substituting messages smartlinks +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="messages/flagged"#{smartlink$1ibiticon="icons/mail-mark-important"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="messages/mail"#{smartlink$1ibiticon="icons/mail-message-new"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="messages/recieve_mail"#{smartlink$1ibiticon="icons/mail-send-receive"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="messages/send_mail"#{smartlink$1ibiticon="icons/mail-send-receive"#g' {} \; + +echo substituting nexus smartlinks +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="nexus/menu"#{smartlink$1ibiticon="icons/folder-remote"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="nexus/organise"#{smartlink$1ibiticon="icons/view-refresh"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="nexus/remove_dead"#{smartlink$1ibiticon="icons/mail-mark-junk"#g' {} \; + +echo substituting pigeonholes smartlinks +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="pigeonholes/organise"#{smartlink$1ibiticon="icons/view-refresh"#g' {} \; + +echo substituting quota smartlinks +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="quota/quota"#{smartlink$1ibiticon="icons/drive-harddisk"#g' {} \; + +echo substituting rss smartlinks +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="rss/rss"#{smartlink$1ibiticon="icons/network-wireless"#g' {} \; + +echo substituting wiki smartlinks +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="wiki/locked"#{smartlink$1ibiticon="icons/emblem-readonly"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="wiki/unlocked"#{smartlink$1ibiticon="icons/emblem-default"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="wiki/s5"#{smartlink$1ibiticon="icons/x-office-presentation"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="wiki/slides"#{smartlink$1ibiticon="icons/x-office-presentation"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="wiki/tree"#{smartlink$1ibiticon="icons/already dealt with"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="wiki/webhelp"#{smartlink$1ibiticon="icons/help-browser"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="wiki/webhelp_toc"#{smartlink$1ibiticon="icons/help-contents"#g' {} \; +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="wiki/book"#{smartlink$1ibiticon="icons/x-office-address-book"#g' {} \; + +echo substituting gigaupload smartlinks +find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="gigaupload/busy"#{smartlink$1ibiticon="icons/busy"#g' {} \; + + +# original testing line +# find . -name "*.tpl" -exec perl -i -wpe 's/{smartlink\b([^}]*?)ibiticon=""/{smartlink ibiticon=""$1/g' {} \; + + +# - keep original: +# busy +# move_up +# move_down +# move_left +# move_right +# move_left_right +# generating_thumbnail + + +# - remove: +# enlargeH +# reduceH +# lulu +# imagick_logo +# gd_logo +# spy +# liberty/pdf + + +# todo +# boards/move diff --git a/includes/tree.php b/includes/tree.php new file mode 100644 index 0000000..48153d0 --- /dev/null +++ b/includes/tree.php @@ -0,0 +1,135 @@ + number of ID of current node + * parent => number of ID of parant node + * data => user provided data to be placed as node text + * + * @package TreeMaker + */ +class TreeMaker { + /// Unique prefix for cookies generated for this tree + public $prefix; + /// Constructor + function TreeMaker($prefix) { + $this->prefix = $prefix; + } + /// Generate HTML code for tree + function make_tree($rootid, $ar) { + return $this->make_tree_r($rootid, $ar); + } + /// Recursive make (do not call directly) + function make_tree_r($rootid, &$ar) { + //global $debugger; + //$debugger->msg("TreeMaker::make_tree_r: Root ID=" . $rootid); + $result = ''; + if (count($ar) > 0) { + $cli = array(); + $tmp = array(); + foreach ($ar as $i) + if ($rootid == $i["parent"]) + $cli[] = $i; + else + $tmp[] = $i; + // + foreach ($cli as $i) { + $child_result = $this->make_tree_r($i["id"], $tmp); + $have_childs = (strlen($child_result) > 0); + // + // NOTE: The main rule is to call all methods in + // stricty defined order!! + // + $nsc = $this->node_start_code($i); + $flipper = ''; + if ($have_childs) + $flipper = $this->node_flipper_code($i); + $ndsc = $this->node_data_start_code($i); + $ndec = $this->node_data_end_code($i); + $ncsc = ''; + $ncec = ''; + if ($have_childs) { + $ncsc = $this->node_child_start_code($i); + $ncec = $this->node_child_end_code($i); + } + $nec = $this->node_end_code($i); + // Form result + $result .= $nsc . $flipper . $ndsc . $i["data"] . $ndec . $ncsc . $child_result . $ncec . $nec; + } + } + return $result; + } + /** + * To change behavior (look and feel :) of generated tree + * it is enough to redefine follwing methods.. + * (thanx that PHP have implicit vurtual functions :) + * + * General layout of generated tree code looks like this: + * + * [node start code] + * [node flipper code] (1) + * [node data start code] + * [node data end code] + * [node childs start code] (1) + * [node childs end code] (1) + * [node end code] + * + * (1) -- this code will be generated if node have childs + * + * NOTE: Methods called exactly in that order. This fact can be + * (and actualy do) used by child classes to define + * and use some variables depends on pervious call... + * + * NOTE: This is abstract base class... it doing nothig + * except defining algirithm... + * So to make smth other use inheritance and redifine + * corresponding function :) + */ + function node_start_code($nodeinfo) { + return ''; + } + // + function node_flipper_code($nodeinfo) { + return ''; + } + // + function node_data_start_code($nodeinfo) { + return ''; + } + // + function node_data_end_code($nodeinfo) { + return ''; + } + // + function node_child_start_code($nodeinfo) { + return ''; + } + // + function node_child_end_code($nodeinfo) { + return ''; + } + // + function node_end_code($nodeinfo) { + return ''; + } +} +?> diff --git a/includes/zip_lib.php b/includes/zip_lib.php new file mode 100644 index 0000000..e6dbc15 --- /dev/null +++ b/includes/zip_lib.php @@ -0,0 +1,805 @@ +> 8) & 0xffffff)); + } + return ~$crc; +} +define('GZIP_MAGIC', "\037\213"); +define('GZIP_DEFLATE', 010); +function zip_deflate($content) { + // Compress content, and suck information from gzip header. + $z = gzip_compress($content); + // Suck OS type byte from gzip header. FIXME: this smells bad. + extract (unpack("a2magic/Ccomp_type/Cflags/@9/Cos_type", $z)); + if ($magic != GZIP_MAGIC) + trigger_error(sprintf("Bad %s", "gzip magic"), E_USER_ERROR); + if ($comp_type != GZIP_DEFLATE) + trigger_error(sprintf("Bad %s", "gzip comp type"), E_USER_ERROR); + if (($flags & 0x3e) != 0) + trigger_error(sprintf("Bad %s", sprintf("flags (0x%02x)", $flags)), E_USER_ERROR); + $gz_header_len = 10; + $gz_data_len = strlen($z) - $gz_header_len - 8; + if ($gz_data_len < 0) + trigger_error("not enough gzip output?", E_USER_ERROR); + extract (unpack("Vcrc32", substr($z, $gz_header_len + $gz_data_len))); + return array( + substr($z, $gz_header_len, $gz_data_len), // gzipped data + $crc32, // crc + $os_type // OS type + ); +} +function zip_inflate($data, $crc32, $uncomp_size) { + if (!function_exists('gzopen')) { + global $request; + $request->finish(_("Can't inflate data: zlib support not enabled in this PHP")); + } + // Reconstruct gzip header and ungzip the data. + $mtime = time(); //(Bogus mtime) + return gzip_uncompress(pack("a2CxV@10", GZIP_MAGIC, GZIP_DEFLATE, $mtime). $data . pack("VV", $crc32, $uncomp_size)); +} +function unixtime2dostime($unix_time) { + if ($unix_time % 1) + $unix_time++; // Round up to even seconds. + list($year, $month, $mday, $hour, $min, $sec) = explode(" ", date("Y n j G i s", $unix_time)); + if ($year < 1980) + list($year, $month, $mday, $hour, $min, $sec) = array( + 1980, + 1, + 1, + 0, + 0, + 0 + ); + $dosdate = (($year - 1980) << 9) | ($month << 5) | $mday; + $dostime = ($hour << 11) | ($min << 5) | ($sec >> 1); + return array( + $dosdate, + $dostime + ); +} +function dostime2unixtime($dosdate, $dostime) { + $mday = $dosdate & 0x1f; + $month = ($dosdate >> 5) & 0x0f; + $year = 1980 + (($dosdate >> 9) & 0x7f); + $sec = ($dostime & 0x1f) * 2; + $min = ($dostime >> 5) & 0x3f; + $hour = ($dostime >> 11) & 0x1f; + return mktime($hour, $min, $sec, $month, $mday, $year); +} +/** + * Class for zipfile creation. + */ +define('ZIP_DEFLATE', GZIP_DEFLATE); +define('ZIP_STORE', 0); +define('ZIP_CENTHEAD_MAGIC', "PK\001\002"); +define('ZIP_LOCHEAD_MAGIC', "PK\003\004"); +define('ZIP_ENDDIR_MAGIC', "PK\005\006"); +class ZipWriter { + function ZipWriter($comment = "", $zipname = "archive.zip") { + $this->comment = $comment; + $this->nfiles = 0; + $this->dir = ""; // "Central directory block" + $this->offset = 0; // Current file position. + $zipname = addslashes($zipname); + header ("Content-Type: application/zip; name=\"$zipname\""); + header ("Content-Disposition: attachment; filename=\"$zipname\""); + //header( "Content-Disposition: inline; filename=$zipname" ); + } + function addRegularFile($filename, $content, $attrib = false) { + if (!$attrib) + $attrib = array(); + $size = strlen($content); + if (function_exists('gzopen')) { + list($data, $crc32, $os_type) = zip_deflate($content); + if (strlen($data) < $size) { + $content = $data; // Use compressed data. + $comp_type = ZIP_DEFLATE; + } else + unset ($crc32); // force plain store. + } + if (!isset($crc32)) { + $comp_type = ZIP_STORE; + $crc32 = zip_crc32($content); + } + if (!empty($attrib['write_protected'])) + $atx = (0100444 << 16) | 1; // S_IFREG + read permissions to + // everybody. + else + $atx = (0100644 << 16); // Add owner write perms. + $ati = $attrib['is_ascii'] ? 1 : 0; + if (empty($attrib['mtime'])) + $attrib['mtime'] = time(); + list($mod_date, $mod_time) = unixtime2dostime($attrib['mtime']); + // Construct parts common to "Local file header" and "Central + // directory file header." + if (!isset($attrib['extra_field'])) + $attrib['extra_field'] = ''; + if (!isset($attrib['file_comment'])) + $attrib['file_comment'] = ''; + $head = pack("vvvvvVVVvv", 20, // Version needed to extract (FIXME: is this right?) + 0, // Gen purp bit flag + $comp_type, $mod_time, $mod_date, $crc32, strlen($content), $size, strlen($filename), strlen($attrib['extra_field'])); + // Construct the "Local file header" + $lheader = ZIP_LOCHEAD_MAGIC . $head . $filename . $attrib['extra_field']; + // Construct the "central directory file header" + $this->dir .= pack("a4CC", ZIP_CENTHEAD_MAGIC, 23, // Version made by (FIXME: is this right?) + $os_type); + $this->dir .= $head; + $this->dir .= pack("vvvVV", strlen($attrib['file_comment']), 0, // Disk number start + $ati, // Internal file attributes + $atx, // External file attributes + $this->offset); // Relative offset of local header + $this->dir .= $filename . $attrib['extra_field'] . $attrib['file_comment']; + // Output the "Local file header" and file contents. + echo $lheader; + echo $content; + $this->offset += strlen($lheader) + strlen($content); + $this->nfiles++; + } + function finish() { + // Output the central directory + echo $this->dir; + // Construct the "End of central directory record" + echo ZIP_ENDDIR_MAGIC; + echo pack("vvvvVVv", 0, // Number of this disk. + 0, // Number of disk with start of c dir + $this->nfiles, // Number entries on this disk + $this->nfiles, // Number entries + strlen($this->dir), // Size of central directory + $this->offset, // Offset of central directory + strlen($this->comment)); + echo $this->comment; + } +} +/** + * Class for reading zip files. + * + * BUGS: + * + * Many of the ExitWiki()'s should probably be warn()'s (eg. CRC mismatch). + * + * Only a subset of zip formats is recognized. (I think that + * unsupported formats will be recognized as such rather than silently + * munged.) + * + * We don't read the central directory. This means we don't see the + * file attributes (text? read-only?), or file comments. + * + * Right now we ignore the file mod date and time, since we don't need it. + */ +class ZipReader { + function ZipReader($zipfile) { + if (!is_string($zipfile)) + $this->fp = $zipfile; // File already open + else if (!($this->fp = fopen($zipfile, "rb"))) + trigger_error(sprintf(_("Can't open zip file '%s' for reading"), $zipfile), E_USER_ERROR); + } + function _read($nbytes) { + $chunk = fread($this->fp, $nbytes); + if (strlen($chunk) != $nbytes) + trigger_error(_("Unexpected EOF in zip file"), E_USER_ERROR); + return $chunk; + } + function done() { + fclose ($this->fp); + return false; + } + function readFile() { + $head = $this->_read(30); + extract (unpack("a4magic/vreq_version/vflags/vcomp_type" . "/vmod_time/vmod_date" . "/Vcrc32/Vcomp_size/Vuncomp_size" . "/vfilename_len/vextrafld_len", $head)); + //FIXME: we should probably check $req_version. + $attrib['mtime'] = dostime2unixtime($mod_date, $mod_time); + if ($magic != ZIP_LOCHEAD_MAGIC) { + if ($magic != ZIP_CENTHEAD_MAGIC) + // FIXME: better message? + ExitWiki (sprintf("Bad header type: %s", $magic)); + return $this->done(); + } + if (($flags & 0x21) != 0) + ExitWiki ("Encryption and/or zip patches not supported."); + if (($flags & 0x08) != 0) + // FIXME: better message? + ExitWiki ("Postponed CRC not yet supported."); + $filename = $this->_read($filename_len); + if ($extrafld_len != 0) + $attrib['extra_field'] = $this->_read($extrafld_len); + $data = $this->_read($comp_size); + if ($comp_type == ZIP_DEFLATE) { + $data = zip_inflate($data, $crc32, $uncomp_size); + } else if ($comp_type == ZIP_STORE) { + $crc = zip_crc32($data); + if ($crc32 != $crc) + ExitWiki (sprintf("CRC mismatch %x != %x", $crc, $crc32)); + } else + ExitWiki (sprintf("Compression method %s unsupported", $comp_method)); + if (strlen($data) != $uncomp_size) + ExitWiki (sprintf("Uncompressed size mismatch %d != %d", strlen($data), $uncomp_size)); + return array( + $filename, + $data, + $attrib + ); + } +} +/** + * Routines for Mime mailification of pages. + */ +//FIXME: these should go elsewhere (libmime?). +/** + * Routines for quoted-printable en/decoding. + */ +function QuotedPrintableEncode($string) { + // Quote special characters in line. + $quoted = ""; + while ($string) { + // The complicated regexp is to force quoting of trailing spaces. + preg_match('/^([ !-<>-~]*)(?:([!-<>-~]$)|(.))/s', $string, $match); + $quoted .= $match[1] . $match[2]; + if (!empty($match[3])) + $quoted .= sprintf("=%02X", ord($match[3])); + $string = substr($string, strlen($match[0])); + } + // Split line. + // This splits the line (preferably after white-space) into lines + // which are no longer than 76 chars (after adding trailing '=' for + // soft line break, but before adding \r\n.) + return preg_replace('/(?=.{77})(.{10,74}[ \t]|.{71,73}[^=][^=])/s', "\\1=\r\n", $quoted); +} +function QuotedPrintableDecode($string) { + // Eliminate soft line-breaks. + $string = preg_replace('/=[ \t\r]*\n/', '', $string); + return quoted_printable_decode($string); +} +define('MIME_TOKEN_REGEXP', "[-!#-'*+.0-9A-Z^-~]+"); +function MimeContentTypeHeader($type, $subtype, $params) { + $header = "Content-Type: $type/$subtype"; + reset ($params); + while (list($key, $val) = each($params)) { + //FIXME: what about non-ascii printables in $val? + if (!preg_match('/^' . MIME_TOKEN_REGEXP . '$/', $val)) + $val = '"' . addslashes($val). '"'; + $header .= ";\r\n $key=$val"; + } + return "$header\r\n"; +} +function MimeMultipart($parts) { + global $mime_multipart_count; + // The string "=_" can not occur in quoted-printable encoded data. + $boundary = "=_multipart_boundary_" . ++$mime_multipart_count; + $head = MimeContentTypeHeader('multipart', 'mixed', array('boundary' => $boundary)); + $sep = "\r\n--$boundary\r\n"; + return $head . $sep . implode($sep, $parts). "\r\n--${boundary}--\r\n"; +} +/** + * For reference see: + * http://www.nacs.uci.edu/indiv/ehood/MIME/2045/rfc2045.html + * http://www.faqs.org/rfcs/rfc2045.html + * (RFC 1521 has been superceeded by RFC 2045 & others). + * + * Also see http://www.faqs.org/rfcs/rfc2822.html + * + * + * Notes on content-transfer-encoding. + * + * "7bit" means short lines of US-ASCII. + * "8bit" means short lines of octets with (possibly) the high-order bit set. + * "binary" means lines are not necessarily short enough for SMTP + * transport, and non-ASCII characters may be present. + * + * Only "7bit", "quoted-printable", and "base64" are universally safe + * for transport via e-mail. (Though many MTAs can/will be configured to + * automatically convert encodings to a safe type if they receive + * mail encoded in '8bit' and/or 'binary' encodings. + */ +function MimeifyPageRevision($page) { + //$page = $revision->getPage(); + // FIXME: add 'hits' to $params + $params = array( + 'title' => $page["title"], + 'flags' => "", + 'author' => !empty( $page["creator_user"] ) ? $page["creator_user"] : NULL, + 'version' => $page["version"], + 'lastmodified' => $page["last_modified"] + ); + $params["author_id"] = $page["ip"]; + $params["summary"] = $page["comment"]; + if (isset($page["hits"])) + $params["hits"] = $page["hits"]; + $params["description"] = $page["description"]; + $params['charset'] = 'iso-8859-1'; + // Non-US-ASCII is not allowed in Mime headers (at least not without + // special handling) --- so we urlencode all parameter values. + foreach ($params as $key => $val) + $params[$key] = rawurlencode($val); + $out = MimeContentTypeHeader('application', 'x-tikiwiki', $params); + $out .= sprintf("Content-Transfer-Encoding: %s\r\n", 'binary'); + $out .= "\r\n"; + $lines = split("\n", $page["data"]); + foreach ($lines as $line) { + // This is a dirty hack to allow saving binary text files. See above. + $line = rtrim($line); + $out .= "$line\r\n"; + } + return $out; +} +/** + * Routines for parsing Mime-ified phpwiki pages. + */ +function ParseRFC822Headers(&$string) { + if (preg_match("/^From (.*)\r?\n/", $string, $match)) { + $headers['from '] = preg_replace('/^\s+|\s+$/', '', $match[1]); + $string = substr($string, strlen($match[0])); + } + while (preg_match('/^([!-9;-~]+) [ \t]* : [ \t]* ' . '( .* \r?\n (?: [ \t] .* \r?\n)* )/x', $string, $match)) { + $headers[strtolower($match[1])] = preg_replace('/^\s+|\s+$/', '', $match[2]); + $string = substr($string, strlen($match[0])); + } + if (empty($headers)) + return false; + if (!preg_match("/^\r?\n/", $string, $match)) { + // No blank line after headers. + return false; + } + $string = substr($string, strlen($match[0])); + return $headers; +} +function ParseMimeContentType($string) { + // FIXME: Remove (RFC822 style comments). + // Get type/subtype + if (!preg_match(':^\s*(' . MIME_TOKEN_REGEXP . ')\s*' . '/' . '\s*(' . MIME_TOKEN_REGEXP . ')\s*:x', $string, $match)) + ExitWiki (sprintf("Bad %s", 'MIME content-type')); + $type = strtolower($match[1]); + $subtype = strtolower($match[2]); + $string = substr($string, strlen($match[0])); + $param = array(); + while (preg_match('/^;\s*(' . MIME_TOKEN_REGEXP . ')\s*=\s*' . '(?:(' . MIME_TOKEN_REGEXP . ')|"((?:[^"\\\\]|\\.)*)") \s*/sx', $string, $match)) { + //" <--kludge for brain-dead syntax coloring + if (strlen($match[2])) + $val = $match[2]; + else + $val = preg_replace('/[\\\\](.)/s', '\\1', $match[3]); + $param[strtolower($match[1])] = $val; + $string = substr($string, strlen($match[0])); + } + return array( + $type, + $subtype, + $param + ); +} +function ParseMimeMultipart($data, $boundary) { + if (!$boundary) + ExitWiki ("No boundary?"); + $boundary = preg_quote($boundary); + while (preg_match("/^(|.*?\n)--$boundary((?:--)?)[^\n]*\n/s", $data, $match)) { + $data = substr($data, strlen($match[0])); + if (!isset($parts)) + $parts = array(); // First time through: discard leading chaff + else { + if ($content = ParseMimeifiedPages($match[1])) + for (reset($content); $p = current($content); next($content)) + $parts[] = $p; + } + if ($match[2]) + return $parts; // End boundary found. + } + ExitWiki ("No end boundary?"); +} +function GenerateFootnotesFromRefs($params) { + $footnotes = array(); + reset ($params); + while (list($p, $reference) = each($params)) { + if (preg_match('/^ref([1-9][0-9]*)$/', $p, $m)) + $footnotes[$m[1]] = sprintf(_("[%d] See [%s]"), $m[1], rawurldecode($reference)); + } + if (sizeof($footnotes) > 0) { + ksort ($footnotes); + return "-----\n" . "!" . _("References"). "\n" . join("\n%%%\n", $footnotes). "\n"; + } else + return ""; +} +// Convert references in meta-data to footnotes. +// Only zip archives generated by phpwiki 1.2.x or earlier should have +// references. +function ParseMimeifiedPages($data) { + if (!($headers = ParseRFC822Headers($data)) || empty($headers['content-type'])) { + //trigger_error( sprintf(_("Can't find %s"),'content-type header'), + // E_USER_WARNING ); + return false; + } + $typeheader = $headers['content-type']; + if (!(list($type, $subtype, $params) = ParseMimeContentType($typeheader))) { + trigger_error(sprintf("Can't parse %s: (%s)", 'content-type', $typeheader), E_USER_WARNING); + return false; + } + if ("$type/$subtype" == 'multipart/mixed') { + return ParseMimeMultipart($data, $params['boundary']); + } else if ("$type/$subtype" != 'application/x-phpwiki') { + trigger_error(sprintf("Bad %s", "content-type: $type/$subtype"), E_USER_WARNING); + return false; + } + // FIXME: more sanity checking? + $page = array(); + $pagedata = array(); + $versiondata = array(); + foreach ($params as $key => $value) { + if (empty($value)) + continue; + $value = rawurldecode($value); + switch ($key) { + case 'pagename': + case 'version': + $page[$key] = $value; + break; + case 'flags': + if (preg_match('/PAGE_LOCKED/', $value)) + $pagedata['locked'] = 'yes'; + break; + case 'created': + case 'hits': + $pagedata[$key] = $value; + break; + case 'lastmodified': + $versiondata['mtime'] = $value; + break; + case 'author': + case 'author_id': + case 'summary': + case 'markup': + $versiondata[$key] = $value; + break; + } + } + // FIXME: do we need to try harder to find a pagename if we + // haven't got one yet? + if (!isset($versiondata['author'])) { + global $request; + $user = $request->getUser(); + $versiondata['author'] = $user->getId(); //FIXME:? + } + $encoding = strtolower($headers['content-transfer-encoding']); + if ($encoding == 'quoted-printable') + $data = QuotedPrintableDecode($data); + else if ($encoding && $encoding != 'binary') + ExitWiki (sprintf("Unknown %s", 'encoding type: $encoding')); + $data .= GenerateFootnotesFromRefs($params); + $page['content'] = preg_replace('/[ \t\r]*\n/', "\n", chop($data)); + $page['pagedata'] = $pagedata; + $page['versiondata'] = $versiondata; + return array($page); +} +// Local Variables: +// mode: php +// tab-width: 8 +// c-basic-offset: 4 +// c-hanging-comment-ender-p: nil +// indent-tabs-mode: nil +// End: +?> diff --git a/is_email.php b/is_email.php deleted file mode 100644 index 3a29fbb..0000000 --- a/is_email.php +++ /dev/null @@ -1,529 +0,0 @@ - - * Test schema documentation Copyright © 2010, Daniel Marschall
    - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * - Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - Neither the name of Dominic Sayers nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @package is_email - * @author Dominic Sayers - * @copyright 2008-2010 Dominic Sayers - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @link http://www.dominicsayers.com/isemail - * @version 2.10.1 - Amended DNS lookup logic. Also, in is_email_statustext.php, changed $type to integer to allow for additional types (starting with SMTP codes) - */ - -// The quality of this code has been improved greatly by using PHPLint -// Copyright (c) 2010 Umberto Salsi -// This is free software; see the license for copying conditions. -// More info: http://www.icosaedro.it/phplint/ -/*. - require_module 'standard'; - require_module 'pcre'; -.*/ -/** - * Check that an email address conforms to RFCs 5321, 5322 and others - * - * @param string $email The email address to check - * @param boolean $checkDNS If true then a DNS check for A and MX records will be made - * @param mixed $errorlevel If true then return an integer error or warning number rather than true or false - */ -/*.mixed.*/ function is_email ($email, $checkDNS = false, $errorlevel = false) { - // Check that $email is a valid address. Read the following RFCs to understand the constraints: - // (http://tools.ietf.org/html/rfc5321) - // (http://tools.ietf.org/html/rfc5322) - // (http://tools.ietf.org/html/rfc4291#section-2.2) - // (http://tools.ietf.org/html/rfc1123#section-2.1) - // (http://tools.ietf.org/html/rfc3696) (guidance only) - - // $errorlevel Behaviour - // --------------- --------------------------------------------------------------------------- - // E_ERROR Return validation failures only. For technically valid addresses return - // ISEMAIL_VALID - // E_WARNING Return warnings for unlikely but technically valid addresses. This includes - // addresses at TLDs (e.g. johndoe@com), addresses with FWS and comments, - // addresses that are quoted and addresses that contain no alphabetic or - // numeric characters. - // true Same as E_ERROR - // false Return true for valid addresses, false for invalid ones. No warnings. - // - // Errors can be distinguished from warnings if ($return_value > ISEMAIL_ERROR) -// version 2.0: Enhance $diagnose parameter to $errorlevel -// revision 2.5: some syntax changes to make it more PHPLint-friendly. Should be functionally identical. - - if (!defined('ISEMAIL_VALID')) { - // No errors - define('ISEMAIL_VALID' , 0); - // Warnings (valid address but unlikely in the real world) - define('ISEMAIL_WARNING' , 64); - define('ISEMAIL_TLD' , 65); - define('ISEMAIL_TLDNUMERIC' , 66); - define('ISEMAIL_QUOTEDSTRING' , 67); - define('ISEMAIL_COMMENTS' , 68); - define('ISEMAIL_FWS' , 69); - define('ISEMAIL_ADDRESSLITERAL' , 70); - define('ISEMAIL_UNLIKELYINITIAL' , 71); - define('ISEMAIL_SINGLEGROUPELISION' , 72); - define('ISEMAIL_DOMAINNOTFOUND' , 73); - define('ISEMAIL_MXNOTFOUND' , 74); - // Errors (invalid address) - define('ISEMAIL_ERROR' , 128); - define('ISEMAIL_TOOLONG' , 129); - define('ISEMAIL_NOAT' , 130); - define('ISEMAIL_NOLOCALPART' , 131); - define('ISEMAIL_NODOMAIN' , 132); - define('ISEMAIL_ZEROLENGTHELEMENT' , 133); - define('ISEMAIL_BADCOMMENT_START' , 134); - define('ISEMAIL_BADCOMMENT_END' , 135); - define('ISEMAIL_UNESCAPEDDELIM' , 136); - define('ISEMAIL_EMPTYELEMENT' , 137); - define('ISEMAIL_UNESCAPEDSPECIAL' , 138); - define('ISEMAIL_LOCALTOOLONG' , 139); -// define('ISEMAIL_IPV4BADPREFIX' , 140); - define('ISEMAIL_IPV6BADPREFIXMIXED' , 141); - define('ISEMAIL_IPV6BADPREFIX' , 142); - define('ISEMAIL_IPV6GROUPCOUNT' , 143); - define('ISEMAIL_IPV6DOUBLEDOUBLECOLON' , 144); - define('ISEMAIL_IPV6BADCHAR' , 145); - define('ISEMAIL_IPV6TOOMANYGROUPS' , 146); - define('ISEMAIL_DOMAINEMPTYELEMENT' , 147); - define('ISEMAIL_DOMAINELEMENTTOOLONG' , 148); - define('ISEMAIL_DOMAINBADCHAR' , 149); - define('ISEMAIL_DOMAINTOOLONG' , 150); - define('ISEMAIL_IPV6SINGLECOLONSTART' , 151); - define('ISEMAIL_IPV6SINGLECOLONEND' , 152); - // Unexpected errors -// define('ISEMAIL_BADPARAMETER' , 190); -// define('ISEMAIL_NOTDEFINED' , 191); -// revision 2.1: Redefined unexpected error constants so they don't clash with the ISEMAIL_WARNING bit -// revision 2.5: Undefined unused constants - } - - if (is_bool($errorlevel)) { - if ((bool) $errorlevel) { - $diagnose = true; - $warn = false; - } else { - $diagnose = false; - $warn = false; - } - } else { - switch ((int) $errorlevel) { - case E_WARNING: - $diagnose = true; - $warn = true; - break; - case E_ERROR: - $diagnose = true; - $warn = false; - break; - default: - $diagnose = false; - $warn = false; - } - } - - if ($diagnose) /*.mixed.*/ $return_status = ISEMAIL_VALID; else $return_status = true; -// version 2.0: Enhance $diagnose parameter to $errorlevel - - // the upper limit on address lengths should normally be considered to be 254 - // (http://www.rfc-editor.org/errata_search.php?rfc=3696) - // NB My erratum has now been verified by the IETF so the correct answer is 254 - // - // The maximum total length of a reverse-path or forward-path is 256 - // characters (including the punctuation and element separators) - // (http://tools.ietf.org/html/rfc5321#section-4.5.3.1.3) - // NB There is a mandatory 2-character wrapper round the actual address - $emailLength = strlen($email); -// revision 1.17: Max length reduced to 254 (see above) - if ($emailLength > 254) if ($diagnose) return ISEMAIL_TOOLONG; else return false; // Too long - - // Contemporary email addresses consist of a "local part" separated from - // a "domain part" (a fully-qualified domain name) by an at-sign ("@"). - // (http://tools.ietf.org/html/rfc3696#section-3) - $atIndex = strrpos($email,'@'); - - if ($atIndex === false) if ($diagnose) return ISEMAIL_NOAT; else return false; // No at-sign - if ($atIndex === 0) if ($diagnose) return ISEMAIL_NOLOCALPART; else return false; // No local part - if ($atIndex === $emailLength - 1) if ($diagnose) return ISEMAIL_NODOMAIN; else return false; // No domain part -// revision 1.14: Length test bug suggested by Andrew Campbell of Gloucester, MA - - // Sanitize comments - // - remove nested comments, quotes and dots in comments - // - remove parentheses and dots from quoted strings - $braceDepth = 0; - $inQuote = false; - $escapeThisChar = false; - - for ($i = 0; $i < $emailLength; ++$i) { - $char = $email[$i]; - $replaceChar = false; - - if ($char === '\\') $escapeThisChar = !$escapeThisChar; // Escape the next character? - else { - switch ($char) { - case '(': - if ($escapeThisChar) $replaceChar = true; - else if ($inQuote) $replaceChar = true; - else if ($braceDepth++ > 0) $replaceChar = true; // Increment brace depth - - break; - case ')': - if ($escapeThisChar) $replaceChar = true; - else if ($inQuote) $replaceChar = true; - else { - if (--$braceDepth > 0) $replaceChar = true; // Decrement brace depth - if ($braceDepth < 0) $braceDepth = 0; - } - - break; - case '"': - if ($escapeThisChar) $replaceChar = true; - else if ($braceDepth === 0) $inQuote = !$inQuote; // Are we inside a quoted string? - else $replaceChar = true; - - break; - case '.': - if ($escapeThisChar) $replaceChar = true; // Dots don't help us either - else if ($braceDepth > 0) $replaceChar = true; - - break; - default: - } - - $escapeThisChar = false; -// if ($replaceChar) $email[$i] = 'x'; // Replace the offending character with something harmless -// revision 1.12: Line above replaced because PHPLint doesn't like that syntax - if ($replaceChar) $email = (string) substr_replace($email, 'x', $i, 1); // Replace the offending character with something harmless - } - } - - $localPart = substr($email, 0, $atIndex); - $domain = substr($email, $atIndex + 1); - $FWS = "(?:(?:(?:[ \\t]*(?:\\r\\n))?[ \\t]+)|(?:[ \\t]+(?:(?:\\r\\n)[ \\t]+)*))"; // Folding white space - $dotArray = /*. (array[]) .*/ array(); - - // Let's check the local part for RFC compliance... - // - // local-part = dot-atom / quoted-string / obs-local-part - // obs-local-part = word *("." word) - // (http://tools.ietf.org/html/rfc5322#section-3.4.1) - // - // Problem: need to distinguish between "first.last" and "first"."last" - // (i.e. one element or two). And I suck at regular expressions. - $dotArray = preg_split('/\\.(?=(?:[^\\"]*\\"[^\\"]*\\")*(?![^\\"]*\\"))/m', $localPart); - $partLength = 0; - - foreach ($dotArray as $arrayMember) { - $element = (string) $arrayMember; - // Remove any leading or trailing FWS - $new_element = preg_replace("/^$FWS|$FWS\$/", '', $element); - if ($warn && ($element !== $new_element)) $return_status = ISEMAIL_FWS; // FWS is unlikely in the real world - $element = $new_element; -// version 2.3: Warning condition added - $elementLength = strlen($element); - - if ($elementLength === 0) if ($diagnose) return ISEMAIL_ZEROLENGTHELEMENT; else return false; // Can't have empty element (consecutive dots or dots at the start or end) -// revision 1.15: Speed up the test and get rid of "uninitialized string offset" notices from PHP - - // We need to remove any valid comments (i.e. those at the start or end of the element) - if ($element[0] === '(') { - if ($warn) $return_status = ISEMAIL_COMMENTS; // Comments are unlikely in the real world -// version 2.0: Warning condition added - $indexBrace = strpos($element, ')'); - if ($indexBrace !== false) { - if (preg_match('/(? 0) - if ($diagnose) return ISEMAIL_BADCOMMENT_START; else return false; // Illegal characters in comment - $element = substr($element, $indexBrace + 1, $elementLength - $indexBrace - 1); - $elementLength = strlen($element); - } - } - - if ($element[$elementLength - 1] === ')') { - if ($warn) $return_status = ISEMAIL_COMMENTS; // Comments are unlikely in the real world -// version 2.0: Warning condition added - $indexBrace = strrpos($element, '('); - if ($indexBrace !== false) { - if (preg_match('/(? 0) - if ($diagnose) return ISEMAIL_BADCOMMENT_END; else return false; // Illegal characters in comment - $element = substr($element, 0, $indexBrace); - $elementLength = strlen($element); - } - } - - // Remove any remaining leading or trailing FWS around the element (having removed any comments) - $new_element = preg_replace("/^$FWS|$FWS\$/", '', $element); - if ($warn && ($element !== $new_element)) $return_status = ISEMAIL_FWS; // FWS is unlikely in the real world - $element = $new_element; -// version 2.0: Warning condition added - - // What's left counts towards the maximum length for this part - if ($partLength > 0) $partLength++; // for the dot - $partLength += strlen($element); - - // Each dot-delimited component can be an atom or a quoted string - // (because of the obs-local-part provision) - if (preg_match('/^"(?:.)*"$/s', $element) > 0) { - // Quoted-string tests: - if ($warn) $return_status = ISEMAIL_QUOTEDSTRING; // Quoted string is unlikely in the real world -// version 2.0: Warning condition added - // Remove any FWS - $element = preg_replace("/(? 0) if ($diagnose) return ISEMAIL_UNESCAPEDDELIM; else return false; // ", CR, LF and NUL must be escaped -// version 2.0: allow ""@example.com because it's technically valid - } else { - // Unquoted string tests: - // - // Period (".") may...appear, but may not be used to start or end the - // local part, nor may two or more consecutive periods appear. - // (http://tools.ietf.org/html/rfc3696#section-3) - // - // A zero-length element implies a period at the beginning or end of the - // local part, or two periods together. Either way it's not allowed. - if ($element === '') if ($diagnose) return ISEMAIL_EMPTYELEMENT; else return false; // Dots in wrong place - - // Any ASCII graphic (printing) character other than the - // at-sign ("@"), backslash, double quote, comma, or square brackets may - // appear without quoting. If any of that list of excluded characters - // are to appear, they must be quoted - // (http://tools.ietf.org/html/rfc3696#section-3) - // - // Any excluded characters? i.e. 0x00-0x20, (, ), <, >, [, ], :, ;, @, \, comma, period, " - if (preg_match('/[\\x00-\\x20\\(\\)<>\\[\\]:;@\\\\,\\."]/', $element) > 0) if ($diagnose) return ISEMAIL_UNESCAPEDSPECIAL; else return false; // These characters must be in a quoted string - if ($warn && (preg_match('/^\\w+/', $element) === 0)) $return_status = ISEMAIL_UNLIKELYINITIAL; // First character is an odd one - } - } - - if ($partLength > 64) if ($diagnose) return ISEMAIL_LOCALTOOLONG; else return false; // Local part must be 64 characters or less - - // Now let's check the domain part... - - // The domain name can also be replaced by an IP address in square brackets - // (http://tools.ietf.org/html/rfc3696#section-3) - // (http://tools.ietf.org/html/rfc5321#section-4.1.3) - // (http://tools.ietf.org/html/rfc4291#section-2.2) - if (preg_match('/^\\[(.)+]$/', $domain) === 1) { - // It's an address-literal - if ($warn) $return_status = ISEMAIL_ADDRESSLITERAL; // Quoted string is unlikely in the real world -// version 2.0: Warning condition added - $addressLiteral = substr($domain, 1, strlen($domain) - 2); - $groupMax = 8; -// revision 2.1: new IPv6 testing strategy - $matchesIP = array(); - $colon = ':'; // Revision 2.7: Daniel Marschall's new IPv6 testing strategy - $double_colon = '::'; - - // Extract IPv4 part from the end of the address-literal (if there is one) - if (preg_match('/\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/', $addressLiteral, $matchesIP) > 0) { - $index = strrpos($addressLiteral, $matchesIP[0]); - - if ($index === 0) { - // Nothing there except a valid IPv4 address, so... - if ($diagnose) return $return_status; else return true; -// version 2.0: return warning if one is set - } else { -//- // Assume it's an attempt at a mixed address (IPv6 + IPv4) -//- if ($addressLiteral[$index - 1] !== $colon) if ($diagnose) return ISEMAIL_IPV4BADPREFIX; else return false; // Character preceding IPv4 address must be ':' -// revision 2.1: new IPv6 testing strategy - if (substr($addressLiteral, 0, 5) !== 'IPv6:') if ($diagnose) return ISEMAIL_IPV6BADPREFIXMIXED; else return false; // RFC5321 section 4.1.3 -//- -//- $IPv6 = substr($addressLiteral, 5, ($index === 7) ? 2 : $index - 6); -//- $groupMax = 6; -// revision 2.1: new IPv6 testing strategy - $IPv6 = substr($addressLiteral, 5, $index - 5) . '0000:0000'; // Convert IPv4 part to IPv6 format - } - } else { - // It must be an attempt at pure IPv6 - if (substr($addressLiteral, 0, 5) !== 'IPv6:') if ($diagnose) return ISEMAIL_IPV6BADPREFIX; else return false; // RFC5321 section 4.1.3 - $IPv6 = substr($addressLiteral, 5); -//- $groupMax = 8; -// revision 2.1: new IPv6 testing strategy - } - - $matchesIP = explode($colon, $IPv6); // Revision 2.7: Daniel Marschall's new IPv6 testing strategy - $groupCount = count($matchesIP); - $index = strpos($IPv6,$double_colon); - - if ($index === false) { - // We need exactly the right number of groups - if ($groupCount !== $groupMax) if ($diagnose) return ISEMAIL_IPV6GROUPCOUNT; else return false; // RFC5321 section 4.1.3 - } else { - if ($index !== strrpos($IPv6,$double_colon)) if ($diagnose) return ISEMAIL_IPV6DOUBLEDOUBLECOLON; else return false; // More than one '::' - if ($index === 0 || $index === (strlen($IPv6) - 2)) $groupMax++; // RFC 4291 allows :: at the start or end of an address with 7 other groups in addition - if ($groupCount > $groupMax) if ($diagnose) return ISEMAIL_IPV6TOOMANYGROUPS; else return false; // Too many IPv6 groups in address - if ($groupCount === $groupMax) $return_status = ISEMAIL_SINGLEGROUPELISION; // Eliding a single group with :: is deprecated by RFCs 5321 & 5952 - } - - // Check for single : at start and end of address - // Revision 2.7: Daniel Marschall's new IPv6 testing strategy - if ((substr($IPv6, 0, 1) === $colon) && (substr($IPv6, 1, 1) !== $colon)) if ($diagnose) return ISEMAIL_IPV6SINGLECOLONSTART; else return false; // Address starts with a single colon - if ((substr($IPv6, -1) === $colon) && (substr($IPv6, -2, 1) !== $colon)) if ($diagnose) return ISEMAIL_IPV6SINGLECOLONEND; else return false; // Address ends with a single colon - - // Check for unmatched characters - if (count(preg_grep('/^[0-9A-Fa-f]{0,4}$/', $matchesIP, PREG_GREP_INVERT)) !== 0) if ($diagnose) return ISEMAIL_IPV6BADCHAR; else return false; // Illegal characters in address - // It's a valid IPv6 address, so... - if ($diagnose) return $return_status; else return true; -// revision 2.1: bug fix: now correctly return warning status - } else { - // It's a domain name... - - // The syntax of a legal Internet host name was specified in RFC-952 - // One aspect of host name syntax is hereby changed: the - // restriction on the first character is relaxed to allow either a - // letter or a digit. - // (http://tools.ietf.org/html/rfc1123#section-2.1) - // - // NB RFC 1123 updates RFC 1035, but this is not currently apparent from reading RFC 1035. - // - // Most common applications, including email and the Web, will generally not - // permit...escaped strings - // (http://tools.ietf.org/html/rfc3696#section-2) - // - // the better strategy has now become to make the "at least one period" test, - // to verify LDH conformance (including verification that the apparent TLD name - // is not all-numeric) - // (http://tools.ietf.org/html/rfc3696#section-2) - // - // Characters outside the set of alphabetic characters, digits, and hyphen MUST NOT appear in domain name - // labels for SMTP clients or servers - // (http://tools.ietf.org/html/rfc5321#section-4.1.2) - // - // RFC5321 precludes the use of a trailing dot in a domain name for SMTP purposes - // (http://tools.ietf.org/html/rfc5321#section-4.1.2) - $dotArray = preg_split('/\\.(?=(?:[^\\"]*\\"[^\\"]*\\")*(?![^\\"]*\\"))/m', $domain); - $partLength = 0; - $element = ''; // Since we use $element after the foreach loop let's make sure it has a value -// revision 1.13: Line above added because PHPLint now checks for Definitely Assigned Variables - - if ($warn && (count($dotArray) === 1)) $return_status = ISEMAIL_TLD; // The mail host probably isn't a TLD -// version 2.0: downgraded to a warning - - foreach ($dotArray as $arrayMember) { - $element = (string) $arrayMember; - // Remove any leading or trailing FWS - $new_element = preg_replace("/^$FWS|$FWS\$/", '', $element); - if ($warn && ($element !== $new_element)) $return_status = ISEMAIL_FWS; // FWS is unlikely in the real world - $element = $new_element; -// version 2.0: Warning condition added - $elementLength = strlen($element); - - // Each dot-delimited component must be of type atext - // A zero-length element implies a period at the beginning or end of the - // local part, or two periods together. Either way it's not allowed. - if ($elementLength === 0) if ($diagnose) return ISEMAIL_DOMAINEMPTYELEMENT; else return false; // Dots in wrong place -// revision 1.15: Speed up the test and get rid of "uninitialized string offset" notices from PHP - - // Then we need to remove all valid comments (i.e. those at the start or end of the element - if ($element[0] === '(') { - if ($warn) $return_status = ISEMAIL_COMMENTS; // Comments are unlikely in the real world -// version 2.0: Warning condition added - $indexBrace = strpos($element, ')'); - if ($indexBrace !== false) { - if (preg_match('/(? 0) - if ($diagnose) return ISEMAIL_BADCOMMENT_START; else return false; // Illegal characters in comment -// revision 1.17: Fixed name of constant (also spotted by turboflash - thanks!) - $element = substr($element, $indexBrace + 1, $elementLength - $indexBrace - 1); - $elementLength = strlen($element); - } - } - - if ($element[$elementLength - 1] === ')') { - if ($warn) $return_status = ISEMAIL_COMMENTS; // Comments are unlikely in the real world -// version 2.0: Warning condition added - $indexBrace = strrpos($element, '('); - if ($indexBrace !== false) { - if (preg_match('/(? 0) - if ($diagnose) return ISEMAIL_BADCOMMENT_END; else return false; // Illegal characters in comment -// revision 1.17: Fixed name of constant (also spotted by turboflash - thanks!) - $element = substr($element, 0, $indexBrace); - $elementLength = strlen($element); - } - } - - // Remove any leading or trailing FWS around the element (inside any comments) - $new_element = preg_replace("/^$FWS|$FWS\$/", '', $element); - if ($warn && ($element !== $new_element)) $return_status = ISEMAIL_FWS; // FWS is unlikely in the real world - $element = $new_element; -// version 2.0: Warning condition added - - // What's left counts towards the maximum length for this part - if ($partLength > 0) $partLength++; // for the dot - $partLength += strlen($element); - - // The DNS defines domain name syntax very generally -- a - // string of labels each containing up to 63 8-bit octets, - // separated by dots, and with a maximum total of 255 - // octets. - // (http://tools.ietf.org/html/rfc1123#section-6.1.3.5) - if ($elementLength > 63) if ($diagnose) return ISEMAIL_DOMAINELEMENTTOOLONG; else return false; // Label must be 63 characters or less - - // Any ASCII graphic (printing) character other than the - // at-sign ("@"), backslash, double quote, comma, or square brackets may - // appear without quoting. If any of that list of excluded characters - // are to appear, they must be quoted - // (http://tools.ietf.org/html/rfc3696#section-3) - // - // If the hyphen is used, it is not permitted to appear at - // either the beginning or end of a label. - // (http://tools.ietf.org/html/rfc3696#section-2) - // - // Any excluded characters? i.e. 0x00-0x20, (, ), <, >, [, ], :, ;, @, \, comma, period, " - if (preg_match('/[\\x00-\\x20\\(\\)<>\\[\\]:;@\\\\,\\."]|^-|-$/', $element) > 0) if ($diagnose) return ISEMAIL_DOMAINBADCHAR; else return false; // Illegal character in domain name - } - - if ($partLength > 255) if ($diagnose) return ISEMAIL_DOMAINTOOLONG; else return false; // Domain part must be 255 characters or less (http://tools.ietf.org/html/rfc1123#section-6.1.3.5) - - if ($warn && (preg_match('/^[0-9]+$/', $element) > 0)) $return_status = ISEMAIL_TLDNUMERIC; // TLD probably isn't all-numeric (http://www.apps.ietf.org/rfc/rfc3696.html#sec-2) -// version 2.0: Downgraded to a warning - - // Check DNS? - if ($diagnose && ($return_status === ISEMAIL_VALID) && $checkDNS && function_exists('checkdnsrr')) { - // Revision 2.10: Amended DNS logic - // An A-record is not required unless there are no MX-records - // for a domain. Obvious when you think about it. - // (http://tools.ietf.org/html/rfc5321#section-5) - // (http://tools.ietf.org/html/rfc2181#section-10.3) - // (http://tools.ietf.org/html/rfc1035) - if (!(checkdnsrr($domain, 'MX'))) { - $result = @dns_get_record($domain, DNS_A); - - if ((is_bool($result) && !(bool) $result)) - $return_status = ISEMAIL_DOMAINNOTFOUND; // Neither MX- nor A-record for domain can be found - else $return_status = ISEMAIL_MXNOTFOUND; // MX-record for domain can't be found - } - } - } - - // Eliminate all other factors, and the one which remains must be the truth. - // (Sherlock Holmes, The Sign of Four) - if ($diagnose) return $return_status; else return true; -// version 2.0: return warning if one is set -} -?> diff --git a/jpeg_metadata_tk/COPYING.txt b/jpeg_metadata_tk/COPYING.txt deleted file mode 100644 index 5b6e7c6..0000000 --- a/jpeg_metadata_tk/COPYING.txt +++ /dev/null @@ -1,340 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General -Public License instead of this License. diff --git a/jpeg_metadata_tk/EXIF.php b/jpeg_metadata_tk/EXIF.php deleted file mode 100644 index 8751dcd..0000000 --- a/jpeg_metadata_tk/EXIF.php +++ /dev/null @@ -1,2764 +0,0 @@ - 1.10 : added function get_EXIF_TIFF to allow extracting EXIF from a TIFF file -* 1.10 -> 1.11 : added functionality to allow decoding of XMP and Photoshop IRB information -* embedded within the EXIF data -* added checks for http and ftp wrappers, as these are not supported -* changed interpret_IFD to allow thumbnail links to work when -* toolkit is portable across directories -* -* -* URL: http://electronics.ozhiker.com -* -* Copyright: Copyright Evan Hunter 2004 -* -* License: This file is part of the PHP JPEG Metadata Toolkit. -* -* The PHP JPEG Metadata Toolkit is free software; you can -* redistribute it and/or modify it under the terms of the -* GNU General Public License as published by the Free Software -* Foundation; either version 2 of the License, or (at your -* option) any later version. -* -* The PHP JPEG Metadata Toolkit is distributed in the hope -* that it will be useful, but WITHOUT ANY WARRANTY; without -* even the implied warranty of MERCHANTABILITY or FITNESS -* FOR A PARTICULAR PURPOSE. See the GNU General Public License -* for more details. -* -* You should have received a copy of the GNU General Public -* License along with the PHP JPEG Metadata Toolkit; if not, -* write to the Free Software Foundation, Inc., 59 Temple -* Place, Suite 330, Boston, MA 02111-1307 USA -* -* If you require a different license for commercial or other -* purposes, please contact the author: evan@ozhiker.com -* -******************************************************************************/ - - -// TODO : Thoroughly test the functions for writing EXIF segments -// TODO : Figure out a way to allow EXIF to function normally with HTTP and FTP wrappers -// TODO : Implement EXIF decoding of Device Setting Description field -// TODO : Implement EXIF decoding of SpatialFrequencyResponse field -// TODO : Implement EXIF decoding of OECF field -// TODO : Implement EXIF decoding of SubjectArea field -// TODO : Add a put_EXIF_TIFF function - -/****************************************************************************** -* -* Initialisation -* -******************************************************************************/ - - -if ( !isset( $GLOBALS['HIDE_UNKNOWN_TAGS'] ) ) $GLOBALS['HIDE_UNKNOWN_TAGS']= FALSE; -if ( !isset( $GLOBALS['SHOW_BINARY_DATA_HEX'] ) ) $GLOBALS['SHOW_BINARY_DATA_HEX'] = FALSE; -if ( !isset( $GLOBALS['SHOW_BINARY_DATA_TEXT'] ) ) $GLOBALS['SHOW_BINARY_DATA_TEXT'] = FALSE; - - -include_once 'EXIF_Tags.php'; -include_once 'EXIF_Makernote.php'; -include_once 'PIM.php'; -include_once 'Unicode.php'; -include_once 'JPEG.php'; -include_once 'IPTC.php'; -include_once 'Photoshop_IRB.php'; // Change: as of version 1.11 - Required for TIFF with embedded IRB -include_once 'XMP.php'; // Change: as of version 1.11 - Required for TIFF with embedded XMP -include_once 'pjmt_utils.php'; // Change: as of version 1.11 - Required for directory portability - - - - - - - - -/****************************************************************************** -* -* Function: get_EXIF_JPEG -* -* Description: Retrieves information from a Exchangeable Image File Format (EXIF) -* APP1 segment and returns it in an array. -* -* Parameters: filename - the filename of the JPEG image to process -* -* Returns: OutputArray - Array of EXIF records -* FALSE - If an error occured in decoding -* -******************************************************************************/ - -function get_EXIF_JPEG( $filename ) -{ - // Change: Added as of version 1.11 - // Check if a wrapper is being used - these are not currently supported (see notes at top of file) - if ( ( stristr ( $filename, "http://" ) != FALSE ) || ( stristr ( $filename, "ftp://" ) != FALSE ) ) - { - // A HTTP or FTP wrapper is being used - show a warning and abort - error_log( "HTTP and FTP wrappers are currently not supported with EXIF - See EXIF functionality documentation - a local file must be specified
    - To work on an internet file, copy it locally to start with:

    \n - $newfilename = tempnam ( \$dir, \"tmpexif\" );
    \n - copy ( \"http://whatever.com\", \$newfilename );

    \n" ); - return FALSE; - } - - // get the JPEG headers - $jpeg_header_data = get_jpeg_header_data( $filename ); - - - // Flag that an EXIF segment has not been found yet - $EXIF_Location = -1; - - //Cycle through the header segments - for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) - { - // If we find an APP1 header, - if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP1" ) == 0 ) - { - // And if it has the EXIF label, - if ( ( strncmp ( $jpeg_header_data[$i]['SegData'], "Exif\x00\x00", 6) == 0 ) || - ( strncmp ( $jpeg_header_data[$i]['SegData'], "Exif\x00\xFF", 6) == 0 ) ) // For some reason, some files have a faulty EXIF name which has a 0xFF in it - { - // Save the location of the EXIF segment - $EXIF_Location = $i; - } - } - - } - - // Check if an EXIF segment was found - if ( $EXIF_Location == -1 ) - { - // Couldn't find any EXIF block to decode - return FALSE; - } - - $filehnd = @fopen($filename, 'rb'); - - // Check if the file opened successfully - if ( ! $filehnd ) - { - // Could't open the file - exit - error_log( "Could not open file $filename" ); - return FALSE; - } - - fseek( $filehnd, $jpeg_header_data[$EXIF_Location]['SegDataStart'] + 6 ); - - // Decode the Exif segment into an array and return it - $exif_data = process_TIFF_Header( $filehnd, "TIFF" ); - - - - // Close File - fclose($filehnd); - return $exif_data; -} - -/****************************************************************************** -* End of Function: get_EXIF_JPEG -******************************************************************************/ - - - -/****************************************************************************** -* -* Function: put_EXIF_JPEG -* -* Description: Stores information into a Exchangeable Image File Format (EXIF) -* APP1 segment from an EXIF array. -* -* WARNING: Because the EXIF standard allows pointers to data -* outside the APP1 segment, if there are any such pointers in -* a makernote, this function will DAMAGE them since it will not -* be aware that there is an external pointer. This will often -* happen with Makernotes that include an embedded thumbnail. -* This damage could be prevented where makernotes can be decoded, -* but currently this is not implemented. -* -* -* Parameters: exif_data - The array of EXIF data to insert into the JPEG header -* jpeg_header_data - The JPEG header into which the EXIF data -* should be stored, as from get_jpeg_header_data -* -* Returns: jpeg_header_data - JPEG header array with the EXIF segment inserted -* FALSE - If an error occured -* -******************************************************************************/ - -function put_EXIF_JPEG( $exif_data, $jpeg_header_data ) -{ - // pack the EXIF data into its proper format for a JPEG file - $packed_data = get_TIFF_Packed_Data( $exif_data ); - if ( $packed_data === FALSE ) - { - return $jpeg_header_data; - } - - $packed_data = "Exif\x00\x00$packed_data"; - - //Cycle through the header segments - for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) - { - // If we find an APP1 header, - if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP1" ) == 0 ) - { - // And if it has the EXIF label, - if ( ( strncmp ( $jpeg_header_data[$i]['SegData'], "Exif\x00\x00", 6) == 0 ) || - ( strncmp ( $jpeg_header_data[$i]['SegData'], "Exif\x00\xFF", 6) == 0 ) ) // For some reason, some files have a faulty EXIF name which has a 0xFF in it - { - // Found a preexisting EXIF block - Replace it with the new one and return. - $jpeg_header_data[$i]['SegData'] = $packed_data; - return $jpeg_header_data; - } - } - } - - // No preexisting segment segment found, insert a new one at the start of the header data. - - // Determine highest position of an APP segment at or below APP3, so we can put the - // new APP3 at this position - - - $highest_APP = -1; - - //Cycle through the header segments - for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) - { - // Check if we have found an APP segment at or below APP3, - if ( ( $jpeg_header_data[$i]['SegType'] >= 0xE0 ) && ( $jpeg_header_data[$i]['SegType'] <= 0xE3 ) ) - { - // Found an APP segment at or below APP12 - $highest_APP = $i; - } - } - - // No preexisting EXIF block found, insert a new one at the start of the header data. - array_splice($jpeg_header_data, $highest_APP + 1 , 0, array( array( "SegType" => 0xE1, - "SegName" => "APP1", - "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE1 ], - "SegData" => $packed_data ) ) ); - return $jpeg_header_data; - -} - -/****************************************************************************** -* End of Function: put_EXIF_JPEG -******************************************************************************/ - - - - -/****************************************************************************** -* -* Function: get_Meta_JPEG -* -* Description: Retrieves information from a Meta APP3 segment and returns it -* in an array. Uses information supplied by the -* get_jpeg_header_data function. -* The Meta segment has the same format as an EXIF segment, but -* uses different tags -* -* Parameters: filename - the filename of the JPEG image to process -* -* Returns: OutputArray - Array of Meta records -* FALSE - If an error occured in decoding -* -******************************************************************************/ - -function get_Meta_JPEG( $filename ) -{ - // Change: Added as of version 1.11 - // Check if a wrapper is being used - these are not currently supported (see notes at top of file) - if ( ( stristr ( $filename, "http://" ) != FALSE ) || ( stristr ( $filename, "ftp://" ) != FALSE ) ) - { - // A HTTP or FTP wrapper is being used - show a warning and abort - echo "HTTP and FTP wrappers are currently not supported with Meta - See EXIF/Meta functionality documentation - a local file must be specified
    "; - echo "To work on an internet file, copy it locally to start with:

    \n"; - echo "\$newfilename = tempnam ( \$dir, \"tmpmeta\" );
    \n"; - echo "copy ( \"http://whatever.com\", \$newfilename );

    \n"; - return FALSE; - } - - // get the JPEG headers - $jpeg_header_data = get_jpeg_header_data( $filename ); - - - // Flag that an Meta segment has not been found yet - $Meta_Location = -1; - - //Cycle through the header segments - for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) - { - // If we find an APP3 header, - if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP3" ) == 0 ) - { - // And if it has the Meta label, - if ( ( strncmp ( $jpeg_header_data[$i]['SegData'], "Meta\x00\x00", 6) == 0 ) || - ( strncmp ( $jpeg_header_data[$i]['SegData'], "META\x00\x00", 6) == 0 ) ) - { - // Save the location of the Meta segment - $Meta_Location = $i; - } - } - } - - // Check if an EXIF segment was found - if ( $Meta_Location == -1 ) - { - // Couldn't find any Meta block to decode - return FALSE; - } - - - $filehnd = @fopen($filename, 'rb'); - - // Check if the file opened successfully - if ( ! $filehnd ) - { - // Could't open the file - exit - error_log( "Could not open file $filename" ); - return FALSE; - } - - fseek( $filehnd, $jpeg_header_data[$Meta_Location]['SegDataStart'] + 6 ); - - // Decode the Meta segment into an array and return it - $meta = process_TIFF_Header( $filehnd, "Meta" ); - - // Close File - fclose($filehnd); - - return $meta; -} - -/****************************************************************************** -* End of Function: get_Meta -******************************************************************************/ - - - - - - - -/****************************************************************************** -* -* Function: put_Meta_JPEG -* -* Description: Stores information into a Meta APP3 segment from a Meta array. -* -* -* WARNING: Because the Meta (EXIF) standard allows pointers to data -* outside the APP1 segment, if there are any such pointers in -* a makernote, this function will DAMAGE them since it will not -* be aware that there is an external pointer. This will often -* happen with Makernotes that include an embedded thumbnail. -* This damage could be prevented where makernotes can be decoded, -* but currently this is not implemented. -* -* -* Parameters: meta_data - The array of Meta data to insert into the JPEG header -* jpeg_header_data - The JPEG header into which the Meta data -* should be stored, as from get_jpeg_header_data -* -* Returns: jpeg_header_data - JPEG header array with the Meta segment inserted -* FALSE - If an error occured -* -******************************************************************************/ - -function put_Meta_JPEG( $meta_data, $jpeg_header_data ) -{ - // pack the Meta data into its proper format for a JPEG file - $packed_data = get_TIFF_Packed_Data( $meta_data ); - if ( $packed_data === FALSE ) - { - return $jpeg_header_data; - } - - $packed_data = "Meta\x00\x00$packed_data"; - - //Cycle through the header segments - for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) - { - // If we find an APP1 header, - if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP3" ) == 0 ) - { - // And if it has the Meta label, - if ( ( strncmp ( $jpeg_header_data[$i]['SegData'], "Meta\x00\x00", 6) == 0 ) || - ( strncmp ( $jpeg_header_data[$i]['SegData'], "META\x00\x00", 6) == 0 ) ) - { - // Found a preexisting Meta block - Replace it with the new one and return. - $jpeg_header_data[$i]['SegData'] = $packed_data; - return $jpeg_header_data; - } - } - } - // No preexisting segment segment found, insert a new one at the start of the header data. - - // Determine highest position of an APP segment at or below APP3, so we can put the - // new APP3 at this position - - - $highest_APP = -1; - - //Cycle through the header segments - for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) - { - // Check if we have found an APP segment at or below APP3, - if ( ( $jpeg_header_data[$i]['SegType'] >= 0xE0 ) && ( $jpeg_header_data[$i]['SegType'] <= 0xE3 ) ) - { - // Found an APP segment at or below APP12 - $highest_APP = $i; - } - } - - // No preexisting Meta block found, insert a new one at the start of the header data. - array_splice($jpeg_header_data, $highest_APP + 1 , 0, array( array( "SegType" => 0xE3, - "SegName" => "APP3", - "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE1 ], - "SegData" => $packed_data ) ) ); - return $jpeg_header_data; - -} - -/****************************************************************************** -* End of Function: put_Meta_JPEG -******************************************************************************/ - - - -/****************************************************************************** -* -* Function: get_EXIF_TIFF -* -* Description: Retrieves information from a Exchangeable Image File Format (EXIF) -* within a TIFF file and returns it in an array. -* -* Parameters: filename - the filename of the TIFF image to process -* -* Returns: OutputArray - Array of EXIF records -* FALSE - If an error occured in decoding -* -******************************************************************************/ - -function get_EXIF_TIFF( $filename ) -{ - // Change: Added as of version 1.11 - // Check if a wrapper is being used - these are not currently supported (see notes at top of file) - if ( ( stristr ( $filename, "http://" ) != FALSE ) || ( stristr ( $filename, "ftp://" ) != FALSE ) ) - { - // A HTTP or FTP wrapper is being used - show a warning and abort - echo "HTTP and FTP wrappers are currently not supported with TIFF - See EXIF/TIFF functionality documentation - a local file must be specified
    "; - echo "To work on an internet file, copy it locally to start with:

    \n"; - echo "\$newfilename = tempnam ( \$dir, \"tmptiff\" );
    \n"; - echo "copy ( \"http://whatever.com\", \$newfilename );

    \n"; - return FALSE; - } - - - $filehnd = @fopen($filename, 'rb'); - - // Check if the file opened successfully - if ( ! $filehnd ) - { - // Could't open the file - exit - error_log( "Could not open file $filename" ); - return FALSE; - } - - // Decode the Exif segment into an array and return it - $exif_data = process_TIFF_Header( $filehnd, "TIFF" ); - - // Close File - fclose($filehnd); - return $exif_data; -} - -/****************************************************************************** -* End of Function: get_EXIF_TIFF -******************************************************************************/ - - - - -/****************************************************************************** -* -* Function: Interpret_EXIF_to_HTML -* -* Description: Generates html detailing the contents an APP1 EXIF array -* which was retrieved with a get_EXIF_.... function. -* Can also be used for APP3 Meta arrays. -* -* Parameters: Exif_array - the EXIF array,as read from get_EXIF_.... -* filename - the name of the Image file being processed ( used -* by scripts which displays EXIF thumbnails) -* -* Returns: output_str - A string containing the HTML -* -******************************************************************************/ - -function Interpret_EXIF_to_HTML( $Exif_array, $filename ) -{ - // Create the string to receive the html output - $output_str = ""; - - // Check if the array to process is valid - if ( $Exif_array === FALSE ) - { - // Exif Array is not valid - abort processing - return $output_str; - } - - // Ouput the heading according to what type of tags were used in processing - if ( $Exif_array[ 'Tags Name' ] == "TIFF" ) - { - $output_str .= "

    Contains Exchangeable Image File Format (EXIF) Information

    \n"; - } - else if ( $Exif_array[ 'Tags Name' ] == "Meta" ) - { - $output_str .= "

    Contains META Information (APP3)

    \n"; - } - else - { - $output_str .= "

    Contains " . $Exif_array[ 'Tags Name' ] . " Information

    \n"; - } - - - // Check that there are actually items to process in the array - if ( count( $Exif_array ) < 1 ) - { - // No items to process in array - abort processing - return $output_str; - } - - // Output secondary heading - $output_str .= "

    Main Image Information

    \n"; - - // Interpret the zeroth IFD to html - $output_str .= interpret_IFD( $Exif_array[0], $filename, $Exif_array['Byte_Align'] ); - - // Check if there is a first IFD to process - if ( array_key_exists( 1, $Exif_array ) ) - { - // There is a first IFD for a thumbnail - // Add a heading for it to the output - $output_str .= "

    Thumbnail Information

    \n"; - - // Interpret the IFD to html and add it to the output - $output_str .= interpret_IFD( $Exif_array[1], $filename, $Exif_array['Byte_Align'] ); - } - - // Cycle through any other IFD's - $i = 2; - while ( array_key_exists( $i, $Exif_array ) ) - { - // Add a heading for the IFD - $output_str .= "

    Image File Directory (IFD) $i Information

    \n"; - - // Interpret the IFD to html and add it to the output - $output_str .= interpret_IFD( $Exif_array[$i], $filename, $Exif_array['Byte_Align'] ); - $i++; - } - - // Return the resulting HTML - return $output_str; -} - -/****************************************************************************** -* End of Function: Interpret_EXIF_to_HTML -******************************************************************************/ - - - - - - - - - - - - - - - - -/****************************************************************************** -* -* INTERNAL FUNCTIONS -* -******************************************************************************/ - - - - - - - - - - - -/****************************************************************************** -* -* Internal Function: get_TIFF_Packed_Data -* -* Description: Packs TIFF IFD data from EXIF or Meta into a form ready for -* either a JPEG EXIF/Meta segment or a TIFF file -* This function attempts to protect the contents of an EXIF makernote, -* by ensuring that it remains in the same position relative to the -* TIFF header -* -* Parameters: tiff_data - the EXIF array,as read from get_EXIF_JPEG or get_Meta_JPEG -* -* Returns: packed_data - A string containing packed segment -* -******************************************************************************/ - -function get_TIFF_Packed_Data( $tiff_data ) -{ - // Check that the segment is valid - if ( $tiff_data === FALSE ) - { - return FALSE; - } - - // Get the byte alignment - $Byte_Align = $tiff_data['Byte_Align']; - - // Add the Byte Alignment to the Packed data - $packed_data = $Byte_Align; - - // Add the TIFF ID to the Packed Data - $packed_data .= put_IFD_Data_Type( 42, 3, $Byte_Align ); - - // Create a string for the makernote - $makernote = ""; - - // Check if the makernote exists - if ( $tiff_data[ 'Makernote_Tag' ] !== FALSE ) - { - // A makernote exists - We need to ensure that it stays in the same position as it was - // Put the Makernote before any of the IFD's by padding zeros to the correct offset - $makernote .= str_repeat("\x00",( $tiff_data[ 'Makernote_Tag' ][ 'Offset' ] - 8 ) ); - $makernote .= $tiff_data[ 'Makernote_Tag' ]['Data']; - } - - // Calculage where the zeroth ifd will be - $ifd_offset = strlen( $makernote ) + 8; - - // Add the Zeroth IFD pointer to the packed data - $packed_data .= put_IFD_Data_Type( $ifd_offset, 4, $Byte_Align ); - - // Add the makernote to the packed data (if there was one) - $packed_data .= $makernote; - - //Add the IFD's to the packed data - $packed_data .= get_IFD_Array_Packed_Data( $tiff_data, $ifd_offset, $Byte_Align ); - - // Return the result - return $packed_data; -} - -/****************************************************************************** -* End of Function: get_TIFF_Packed_Data -******************************************************************************/ - - - - -/****************************************************************************** -* -* Internal Function: get_IFD_Array_Packed_Data -* -* Description: Packs a chain of IFD's from EXIF or Meta segments into a form -* ready for either a JPEG EXIF/Meta segment or a TIFF file -* -* Parameters: ifd_data - the IFD chain array, as read from get_EXIF_JPEG or get_Meta_JPEG -* Zero_IFD_offset - The offset to the first IFD from the start of the TIFF header -* Byte_Align - the Byte alignment to use - "MM" or "II" -* -* Returns: packed_data - A string containing packed IFD's -* -******************************************************************************/ - -function get_IFD_Array_Packed_Data( $ifd_data, $Zero_IFD_offset, $Byte_Align ) -{ - // Create a string to receive the packed output - $packed_data = ""; - - // Count the IFDs - $ifd_count = 0; - foreach( $ifd_data as $key => $IFD ) - { - // Make sure we only count the IFD's, not other information keys - if ( is_numeric( $key ) ) - { - $ifd_count++; - } - } - - - // Cycle through each IFD, - for ( $ifdno = 0; $ifdno < $ifd_count; $ifdno++ ) - { - // Check if this IFD is the last one - if ( $ifdno == $ifd_count - 1 ) - { - // This IFD is the last one, get it's packed data - $packed_data .= get_IFD_Packed_Data( $ifd_data[ $ifdno ], $Zero_IFD_offset +strlen($packed_data), $Byte_Align, FALSE ); - } - else - { - // This IFD is NOT the last one, get it's packed data - $packed_data .= get_IFD_Packed_Data( $ifd_data[ $ifdno ], $Zero_IFD_offset +strlen($packed_data), $Byte_Align, TRUE ); - } - - } - - // Return the packed output - return $packed_data; -} - -/****************************************************************************** -* End of Function: get_IFD_Array_Packed_Data -******************************************************************************/ - - - -/****************************************************************************** -* -* Internal Function: get_IFD_Packed_Data -* -* Description: Packs an IFD from EXIF or Meta segments into a form -* ready for either a JPEG EXIF/Meta segment or a TIFF file -* -* Parameters: ifd_data - the IFD chain array, as read from get_EXIF_JPEG or get_Meta_JPEG -* IFD_offset - The offset to the IFD from the start of the TIFF header -* Byte_Align - the Byte alignment to use - "MM" or "II" -* Another_IFD - boolean - false if this is the last IFD in the chain -* - true if it is not the last -* -* Returns: packed_data - A string containing packed IFD's -* -******************************************************************************/ - -function get_IFD_Packed_Data( $ifd_data, $IFD_offset, $Byte_Align, $Another_IFD ) -{ - - $ifd_body_str = ""; - $ifd_data_str = ""; - - $Tag_Definitions_Name = $ifd_data[ 'Tags Name' ]; - - - // Count the Tags in this IFD - $tag_count = 0; - foreach( $ifd_data as $key => $tag ) - { - // Make sure we only count the Tags, not other information keys - if ( is_numeric( $key ) ) - { - $tag_count++; - } - } - - // Add the Tag count to the packed data - $packed_data = put_IFD_Data_Type( $tag_count, 3, $Byte_Align ); - - // Calculate the total length of the IFD (without the offset data) - $IFD_len = 2 + $tag_count * 12 + 4; - - - // Cycle through each tag - foreach( $ifd_data as $key => $tag ) - { - // Make sure this is a tag, not another information key - if ( is_numeric( $key ) ) - { - - // Add the tag number to the packed data - $ifd_body_str .= put_IFD_Data_Type( $tag[ 'Tag Number' ], 3, $Byte_Align ); - - // Add the Data type to the packed data - $ifd_body_str .= put_IFD_Data_Type( $tag['Data Type'], 3, $Byte_Align ); - - // Check if this is a Print Image Matching entry - if ( $tag['Type'] == "PIM" ) - { - // This is a Print Image Matching entry, - // encode it - $data = Encode_PIM( $tag, $Byte_Align ); - } - // Check if this is a IPTC/NAA Record within the EXIF IFD - else if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) && - ( $tag[ 'Tag Number' ] == 33723 ) ) - { - // This is a IPTC/NAA Record, encode it - $data = put_IPTC( $tag['Data'] ); - } - // Change: Check for embedded XMP as of version 1.11 - // Check if this is a XMP Record within the EXIF IFD - else if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) && - ( $tag[ 'Tag Number' ] == 700 ) ) - { - // This is a XMP Record, encode it - $data = write_XMP_array_to_text( $tag['Data'] ); - } - // Change: Check for embedded IRB as of version 1.11 - // Check if this is a Photoshop IRB Record within the EXIF IFD - else if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) && - ( $tag[ 'Tag Number' ] == 34377 ) ) - { - // This is a Photoshop IRB Record, encode it - $data = pack_Photoshop_IRB_Data( $tag['Data'] ); - } - // Exif Thumbnail Offset - else if ( ( $tag[ 'Tag Number' ] == 513 ) && ( $Tag_Definitions_Name == "TIFF" ) ) - { - // The Exif Thumbnail Offset is a pointer but of type Long, not Unknown - // Hence we need to put the data into the packed string separately - // Calculate the thumbnail offset - $data_offset = $IFD_offset + $IFD_len + strlen($ifd_data_str); - - // Create the Offset for the IFD - $data = put_IFD_Data_Type( $data_offset, 4, $Byte_Align ); - - // Store the thumbnail - $ifd_data_str .= $tag['Data']; - } - // Exif Thumbnail Length - else if ( ( $tag[ 'Tag Number' ] == 514 ) && ( $Tag_Definitions_Name == "TIFF" ) ) - { - // Encode the Thumbnail Length - $data = put_IFD_Data_Type( strlen($ifd_data[513]['Data']), 4, $Byte_Align ); - } - // Sub-IFD - else if ( $tag['Type'] == "SubIFD" ) - { - // This is a Sub-IFD - // Calculate the offset to the start of the Sub-IFD - $data_offset = $IFD_offset + $IFD_len + strlen($ifd_data_str); - // Get the packed data for the IFD chain as the data for this tag - $data = get_IFD_Array_Packed_Data( $tag['Data'], $data_offset, $Byte_Align ); - } - else - { - // Not a special tag - - // Create a string to receive the data - $data = ""; - - // Check if this is a type Unknown tag - if ( $tag['Data Type'] != 7 ) - { - // NOT type Unknown - // Cycle through each data value and add it to the data string - foreach( $tag[ 'Data' ] as $data_val ) - { - $data .= put_IFD_Data_Type( $data_val, $tag['Data Type'], $Byte_Align ); - } - } - else - { - // This is a type Unknown - just add the data as is to the data string - $data .= $tag[ 'Data' ]; - } - } - - // Pad the data string out to at least 4 bytes - $data = str_pad ( $data, 4, "\x00" ); - - - // Check if the data type is an ASCII String or type Unknown - if ( ( $tag['Data Type'] == 2 ) || ( $tag['Data Type'] == 7 ) ) - { - // This is an ASCII String or type Unknown - // Add the Length of the string to the packed data as the Count - $ifd_body_str .= put_IFD_Data_Type( strlen($data), 4, $Byte_Align ); - } - else - { - // Add the array count to the packed data as the Count - $ifd_body_str .= put_IFD_Data_Type( count($tag[ 'Data' ]), 4, $Byte_Align ); - } - - - // Check if the data is over 4 bytes long - if ( strlen( $data ) > 4 ) - { - // Data is longer than 4 bytes - it needs to be offset - // Check if this entry is the Maker Note - if ( ( $Tag_Definitions_Name == "EXIF" ) && ( $tag[ 'Tag Number' ] == 37500 ) ) - { - // This is the makernote - It will have already been stored - // at its original offset to help preserve it - // all we need to do is add the Offset to the IFD packed data - $data_offset = $tag[ 'Offset' ]; - - $ifd_body_str .= put_IFD_Data_Type( $data_offset, 4, $Byte_Align ); - } - else - { - // This is NOT the makernote - // Calculate the data offset - $data_offset = $IFD_offset + $IFD_len + strlen($ifd_data_str); - - // Add the offset to the IFD packed data - $ifd_body_str .= put_IFD_Data_Type( $data_offset, 4, $Byte_Align ); - - // Add the data to the offset packed data - $ifd_data_str .= $data; - } - } - else - { - // Data is less than or equal to 4 bytes - Add it to the packed IFD data as is - $ifd_body_str .= $data; - } - - } - } - - // Assemble the IFD body onto the packed data - $packed_data .= $ifd_body_str; - - // Check if there is another IFD after this one - if( $Another_IFD === TRUE ) - { - // There is another IFD after this - // Calculate the Next-IFD offset so that it goes immediately after this IFD - $next_ifd_offset = $IFD_offset + $IFD_len + strlen($ifd_data_str); - } - else - { - // There is NO IFD after this - indicate with offset=0 - $next_ifd_offset = 0; - } - - // Add the Next-IFD offset to the packed data - $packed_data .= put_IFD_Data_Type( $next_ifd_offset, 4, $Byte_Align ); - - // Add the offset data to the packed data - $packed_data .= $ifd_data_str; - - // Return the resulting packed data - return $packed_data; -} - -/****************************************************************************** -* End of Function: get_IFD_Packed_Data -******************************************************************************/ - - - - - -/****************************************************************************** -* -* Internal Function: process_TIFF_Header -* -* Description: Decodes the information stored in a TIFF header and it's -* Image File Directories (IFD's). This information is returned -* in an array -* -* Parameters: filehnd - The handle of a open image file, positioned at the -* start of the TIFF header -* Tag_Definitions_Name - The name of the Tag Definitions group -* within the global array IFD_Tag_Definitions -* -* -* Returns: OutputArray - Array of IFD records -* FALSE - If an error occured in decoding -* -******************************************************************************/ - -function process_TIFF_Header( $filehnd, $Tag_Definitions_Name ) -{ - - - // Save the file position where the TIFF header starts, as offsets are relative to this position - $Tiff_start_pos = ftell( $filehnd ); - - - - // Read the eight bytes of the TIFF header - $DataStr = network_safe_fread( $filehnd, 8 ); - - // Check that we did get all eight bytes - if ( strlen( $DataStr ) != 8 ) - { - return FALSE; // Couldn't read the TIFF header properly - } - - $pos = 0; - // First two bytes indicate the byte alignment - should be 'II' or 'MM' - // II = Intel (LSB first, MSB last - Little Endian) - // MM = Motorola (MSB first, LSB last - Big Endian) - $Byte_Align = substr( $DataStr, $pos, 2 ); - - - - // Check the Byte Align Characters for validity - if ( ( $Byte_Align != "II" ) && ( $Byte_Align != "MM" ) ) - { - // Byte align field is invalid - we won't be able to decode file - return FALSE; - } - - // Skip over the Byte Align field which was just read - $pos += 2; - - // Next two bytes are TIFF ID - should be value 42 with the appropriate byte alignment - $TIFF_ID = substr( $DataStr, $pos, 2 ); - - if ( get_IFD_Data_Type( $TIFF_ID, 3, $Byte_Align ) != 42 ) - { - // TIFF header ID not found - return FALSE; - } - - // Skip over the TIFF ID field which was just read - $pos += 2; - - - // Next four bytes are the offset to the first IFD - $offset_str = substr( $DataStr, $pos, 4 ); - $offset = get_IFD_Data_Type( $offset_str, 4, $Byte_Align ); - - // Done reading TIFF Header - - - // Move to first IFD - - if ( fseek( $filehnd, $Tiff_start_pos + $offset ) !== 0 ) - { - // Error seeking to position of first IFD - return FALSE; - } - - - - // Flag that a makernote has not been found yet - $GLOBALS[ "Maker_Note_Tag" ] = FALSE; - - // Read the IFD chain into an array - $Output_Array = read_Multiple_IFDs( $filehnd, $Tiff_start_pos, $Byte_Align, $Tag_Definitions_Name ); - - // Check if a makernote was found - if ( $GLOBALS[ "Maker_Note_Tag" ] != FALSE ) - { - // Makernote was found - Process it - // The makernote needs to be processed after all other - // tags as it may require some of the other tags in order - // to be processed properly - $GLOBALS[ "Maker_Note_Tag" ] = Read_Makernote_Tag( $GLOBALS[ "Maker_Note_Tag" ], $Output_Array, $filehnd ); - - } - - $Output_Array[ 'Makernote_Tag' ] = $GLOBALS[ "Maker_Note_Tag" ]; - - // Save the Name of the Tags used in the output array - $Output_Array[ 'Tags Name' ] = $Tag_Definitions_Name; - - - - // Save the Byte alignment - $Output_Array['Byte_Align'] = $Byte_Align; - - - // Return the output array - return $Output_Array ; -} - -/****************************************************************************** -* End of Function: process_TIFF_Header -******************************************************************************/ - - - - - - -/****************************************************************************** -* -* Internal Function: read_Multiple_IFDs -* -* Description: Reads and interprets a chain of standard Image File Directories (IFD's), -* and returns the entries in an array. This chain is made up from IFD's -* which have a pointer to the next IFD. IFD's are read until the next -* pointer indicates there are no more -* -* Parameters: filehnd - a handle for the image file being read, positioned at the -* start of the IFD chain -* Tiff_offset - The offset of the TIFF header from the start of the file -* Byte_Align - either "MM" or "II" indicating Motorola or Intel Byte alignment -* Tag_Definitions_Name - The name of the Tag Definitions group within the global array IFD_Tag_Definitions -* local_offsets - True indicates that offset data should be interpreted as being relative to the start of the currrent entry -* False (normal) indicates offests are relative to start of Tiff header as per IFD standard -* read_next_ptr - True (normal) indicates that a pointer to the next IFD should be read at the end of the IFD -* False indicates that no pointer follows the IFD -* -* -* Returns: OutputArray - Array of IFD entries -* -******************************************************************************/ - -function read_Multiple_IFDs( $filehnd, $Tiff_offset, $Byte_Align, $Tag_Definitions_Name, $local_offsets = FALSE, $read_next_ptr = TRUE ) -{ - // Start at the offset of the first IFD - $Next_Offset = 0; - - do - { - // Read an IFD - list($IFD_Array , $Next_Offset) = read_IFD_universal( $filehnd, $Tiff_offset, $Byte_Align, $Tag_Definitions_Name, $local_offsets, $read_next_ptr ); - - // Move to the position of the next IFD - if ( fseek( $filehnd, $Tiff_offset + $Next_Offset ) !== 0 ) - { - // Error seeking to position of next IFD - error_log( "Error: Corrupted EXIF" ); - return FALSE; - } - - $Output_Array[] = $IFD_Array; - - - } while ( $Next_Offset != 0 ); // Until the Next IFD Offset is zero - - - // return resulting array - - return $Output_Array ; -} - -/****************************************************************************** -* End of Function: read_Multiple_IFDs -******************************************************************************/ - - - - - - - -/****************************************************************************** -* -* Internal Function: read_IFD_universal -* -* Description: Reads and interprets a standard or Non-standard Image File -* Directory (IFD), and returns the entries in an array -* -* Parameters: filehnd - a handle for the image file being read, positioned at the start -* of the IFD -* Tiff_offset - The offset of the TIFF header from the start of the file -* Byte_Align - either "MM" or "II" indicating Motorola or Intel Byte alignment -* Tag_Definitions_Name - The name of the Tag Definitions group within the global array IFD_Tag_Definitions -* local_offsets - True indicates that offset data should be interpreted as being relative to the start of the currrent entry -* False (normal) indicates offests are relative to start of Tiff header as per IFD standard -* read_next_ptr - True (normal) indicates that a pointer to the next IFD should be read at the end of the IFD -* False indicates that no pointer follows the IFD -* -* Returns: OutputArray - Array of IFD entries -* Next_Offset - Offset to next IFD (zero = no next IFD) -* -******************************************************************************/ - -function read_IFD_universal( $filehnd, $Tiff_offset, $Byte_Align, $Tag_Definitions_Name, $local_offsets = FALSE, $read_next_ptr = TRUE ) -{ - if ( ( $filehnd == NULL ) || ( feof( $filehnd ) ) ) - { - return array (FALSE , 0); - } - - // Record the Name of the Tag Group used for this IFD in the output array - $OutputArray[ 'Tags Name' ] = $Tag_Definitions_Name; - - // Record the offset of the TIFF header in the output array - $OutputArray[ 'Tiff Offset' ] = $Tiff_offset; - - // First 2 bytes of IFD are number of entries in the IFD - $No_Entries_str = network_safe_fread( $filehnd, 2 ); - $No_Entries = get_IFD_Data_Type( $No_Entries_str, 3, $Byte_Align ); - - - // If the data is corrupt, the number of entries may be huge, which will cause errors - // This is often caused by a lack of a Next-IFD pointer - if ( $No_Entries> 10000 ) - { - // Huge number of entries - abort - // error_log( "Error: huge number of EXIF entries - EXIF is probably Corrupted" ); - - return array ( FALSE , 0); - } - - // If the data is corrupt or just stupid, the number of entries may zero, - // Indicate this by returning false - if ( $No_Entries === 0 ) - { - // No entries - abort - return array ( FALSE , 0); - } - - // Save the file position where first IFD record starts as non-standard offsets - // need to know this to calculate an absolute offset - $IFD_first_rec_pos = ftell( $filehnd ); - - - // Read in the IFD structure - $IFD_Data = network_safe_fread( $filehnd, 12 * $No_Entries ); - - // Check if the entire IFD was able to be read - if ( strlen( $IFD_Data ) != (12 * $No_Entries) ) - { - // Couldn't read the IFD Data properly, Some Casio files have no Next IFD pointer, hence cause this error - error_log( "Error: EXIF Corrupted" ); - return array(FALSE, 0); - } - - - // Last 4 bytes of a standard IFD are the offset to the next IFD - // Some NON-Standard IFD implementations do not have this, hence causing problems if it is read - - // If the Next IFD pointer has been requested to be read, - if ( $read_next_ptr ) - { - // Read the pointer to the next IFD - - $Next_Offset_str = network_safe_fread( $filehnd, 4 ); - $Next_Offset = get_IFD_Data_Type( $Next_Offset_str, 4, $Byte_Align ); - } - else - { - // Otherwise set the pointer to zero ( no next IFD ) - $Next_Offset = 0; - } - - - - // Initialise current position to the start - $pos = 0; - - - // Loop for reading IFD entries - - for ( $i = 0; $i < $No_Entries; $i++ ) - { - // First 2 bytes of IFD entry are the tag number ( Unsigned Short ) - $Tag_No_str = substr( $IFD_Data, $pos, 2 ); - $Tag_No = get_IFD_Data_Type( $Tag_No_str, 3, $Byte_Align ); - $pos += 2; - - // Next 2 bytes of IFD entry are the data format ( Unsigned Short ) - $Data_Type_str = substr( $IFD_Data, $pos, 2 ); - $Data_Type = get_IFD_Data_Type( $Data_Type_str, 3, $Byte_Align ); - $pos += 2; - - // If Datatype is not between 1 and 12, then skip this entry, it is probably corrupted or custom - if (( $Data_Type > 12 ) || ( $Data_Type < 1 ) ) - { - $pos += 8; - continue 1; // Stop trying to process the tag any further and skip to the next one - } - - // Next 4 bytes of IFD entry are the data count ( Unsigned Long ) - $Data_Count_str = substr( $IFD_Data, $pos, 4 ); - $Data_Count = get_IFD_Data_Type( $Data_Count_str, 4, $Byte_Align ); - $pos += 4; - - if ( $Data_Count > 100000 ) - { - // error_log( "Error: huge EXIF data count - EXIF is probably Corrupted" ); - - // Some Casio files have no Next IFD pointer, hence cause errors - - return array ( FALSE , 0); - } - - // Total Data size is the Data Count multiplied by the size of the Data Type - $Total_Data_Size = $GLOBALS['IFD_Data_Sizes'][ $Data_Type ] * $Data_Count; - - $Data_Start_pos = -1; - - // If the total data size is larger than 4 bytes, then the data part is the offset to the real data - if ( $Total_Data_Size > 4 ) - { - // Not enough room for data - offset provided instead - $Data_Offset_str = substr( $IFD_Data, $pos, 4 ); - $Data_Start_pos = get_IFD_Data_Type( $Data_Offset_str, 4, $Byte_Align ); - - - // In some NON-STANDARD makernotes, the offset is relative to the start of the current IFD entry - if ( $local_offsets ) - { - // This is a NON-Standard IFD, seek relative to the start of the current tag - fseek( $filehnd, $IFD_first_rec_pos + $pos - 8 + $Data_Start_pos ); - } - else - { - // This is a normal IFD, seek relative to the start of the TIFF header - fseek( $filehnd, $Tiff_offset + $Data_Start_pos ); - } - - // Read the data block from the offset position - $DataStr = network_safe_fread( $filehnd, $Total_Data_Size ); - } - else - { - // The data block is less than 4 bytes, and is provided in the IFD entry, so read it - $DataStr = substr( $IFD_Data, $pos, $Total_Data_Size ); - } - - // Increment the position past the data - $pos += 4; - - - // Now create the entry for output array - - $Data_Array = array( ); - - - // Read the data items from the data block - - if ( ( $Data_Type != 2 ) && ( $Data_Type != 7 ) ) - { - // The data type is Numerical, Read the data items from the data block - for ( $j = 0; $j < $Data_Count; $j++ ) - { - $Part_Data_Str = substr( $DataStr, $j * $GLOBALS['IFD_Data_Sizes'][ $Data_Type ], $GLOBALS['IFD_Data_Sizes'][ $Data_Type ] ); - $Data_Array[] = get_IFD_Data_Type( $Part_Data_Str, $Data_Type, $Byte_Align ); - } - } - elseif ( $Data_Type == 2 ) - { - // The data type is String(s) (type 2) - - // Strip the last terminating Null - $DataStr = substr( $DataStr, 0, strlen($DataStr)-1 ); - - // Split the data block into multiple strings whereever there is a Null - $Data_Array = explode( "\x00", $DataStr ); - } - else - { - // The data type is Unknown (type 7) - // Do nothing to data - $Data_Array = $DataStr; - } - - - // If this is a Sub-IFD entry, - if ( ( array_key_exists( $Tag_No, $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name] ) ) && - ( "SubIFD" == $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ]['Type'] ) ) - { - // This is a Sub-IFD entry, go and process the data forming Sub-IFD and use its output array as the new data for this entry - fseek( $filehnd, $Tiff_offset + $Data_Array[0] ); - $Data_Array = read_Multiple_IFDs( $filehnd, $Tiff_offset, $Byte_Align, $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ]['Tags Name'] ); - } - - $desc = ""; - $units = ""; - - // Check if this tag exists in the list of tag definitions, - - if ( array_key_exists ( $Tag_No, $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name]) ) - { - - if ( array_key_exists ( 'Description', $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ] ) ) - { - $desc = $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ]['Description']; - } - - if ( array_key_exists ( 'Units', $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ] ) ) - { - $units = $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ]['Units']; - } - - // Tag exists in definitions, append details to output array - $OutputArray[ $Tag_No ] = array ( "Tag Number" => $Tag_No, - "Tag Name" => $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ]['Name'], - "Tag Description" => $desc, - "Data Type" => $Data_Type, - "Type" => $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ]['Type'], - "Units" => $units, - "Data" => $Data_Array ); - - } - else - { - // Tag doesnt exist in definitions, append unknown details to output array - - $OutputArray[ $Tag_No ] = array ( "Tag Number" => $Tag_No, - "Tag Name" => "Unknown Tag #" . $Tag_No, - "Tag Description" => "", - "Data Type" => $Data_Type, - "Type" => "Unknown", - "Units" => "", - "Data" => $Data_Array ); - } - - - - // Some information of type "Unknown" (type 7) might require information about - // how it's position and byte alignment in order to be decoded - if ( $Data_Type == 7 ) - { - $OutputArray[ $Tag_No ]['Offset'] = $Data_Start_pos; - $OutputArray[ $Tag_No ]['Byte Align'] = $Byte_Align; - } - - - //////////////////////////////////////////////////////////////////////// - // Special Data handling - //////////////////////////////////////////////////////////////////////// - - - // Check if this is a Print Image Matching entry - if ( $OutputArray[ $Tag_No ]['Type'] == "PIM" ) - { - // This is a Print Image Matching entry, decode it. - $OutputArray[ $Tag_No ] = Decode_PIM( $OutputArray[ $Tag_No ], $Tag_Definitions_Name ); - } - - - // Interpret the entry into a text string using a custom interpreter - $text_val = get_Tag_Text_Value( $OutputArray[ $Tag_No ], $Tag_Definitions_Name ); - - // Check if a text string was generated - if ( $text_val !== FALSE ) - { - // A string was generated, append it to the output array entry - $OutputArray[ $Tag_No ]['Text Value'] = $text_val; - $OutputArray[ $Tag_No ]['Decoded'] = TRUE; - } - else - { - // A string was NOT generated, append a generic string to the output array entry - $OutputArray[ $Tag_No ]['Text Value'] = get_IFD_value_as_text( $OutputArray[ $Tag_No ] ) . " " . $units; - $OutputArray[ $Tag_No ]['Decoded'] = FALSE; - } - - - - - // Check if this entry is the Maker Note - if ( ( $Tag_Definitions_Name == "EXIF" ) && ( $Tag_No == 37500 ) ) - { - - // Save some extra information which will allow Makernote Decoding with the output array entry - $OutputArray[ $Tag_No ]['Offset'] = $Data_Start_pos; - $OutputArray[ $Tag_No ][ 'Tiff Offset' ] = $Tiff_offset; - $OutputArray[ $Tag_No ]['ByteAlign'] = $Byte_Align; - - // Save a pointer to this entry for Maker note processing later - $GLOBALS[ "Maker_Note_Tag" ] = & $OutputArray[ $Tag_No ]; - } - - - // Check if this is a IPTC/NAA Record within the EXIF IFD - if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) && - ( $Tag_No == 33723 ) ) - { - // This is a IPTC/NAA Record, interpret it and put result in the data for this entry - $OutputArray[ $Tag_No ]['Data'] = get_IPTC( $DataStr ); - $OutputArray[ $Tag_No ]['Decoded'] = TRUE; - } - // Change: Check for embedded XMP as of version 1.11 - // Check if this is a XMP Record within the EXIF IFD - if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) && - ( $Tag_No == 700 ) ) - { - // This is a XMP Record, interpret it and put result in the data for this entry - $OutputArray[ $Tag_No ]['Data'] = read_XMP_array_from_text( $DataStr ); - $OutputArray[ $Tag_No ]['Decoded'] = TRUE; - } - - // Change: Check for embedded IRB as of version 1.11 - // Check if this is a Photoshop IRB Record within the EXIF IFD - if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) && - ( $Tag_No == 34377 ) ) - { - // This is a Photoshop IRB Record, interpret it and put result in the data for this entry - $OutputArray[ $Tag_No ]['Data'] = unpack_Photoshop_IRB_Data( $DataStr ); - $OutputArray[ $Tag_No ]['Decoded'] = TRUE; - } - - // Exif Thumbnail - // Check that both the thumbnail length and offset entries have been processed, - // and that this is one of them - if ( ( ( ( $Tag_No == 513 ) && ( array_key_exists( 514, $OutputArray ) ) ) || - ( ( $Tag_No == 514 ) && ( array_key_exists( 513, $OutputArray ) ) ) ) && - ( $Tag_Definitions_Name == "TIFF" ) ) - { - // Seek to the start of the thumbnail using the offset entry - fseek( $filehnd, $Tiff_offset + $OutputArray[513]['Data'][0] ); - - // Read the thumbnail data, and replace the offset data with the thumbnail - $OutputArray[513]['Data'] = network_safe_fread( $filehnd, $OutputArray[514]['Data'][0] ); - } - - - // Casio Thumbnail - // Check that both the thumbnail length and offset entries have been processed, - // and that this is one of them - if ( ( ( ( $Tag_No == 0x0004 ) && ( array_key_exists( 0x0003, $OutputArray ) ) ) || - ( ( $Tag_No == 0x0003 ) && ( array_key_exists( 0x0004, $OutputArray ) ) ) ) && - ( $Tag_Definitions_Name == "Casio Type 2" ) ) - { - // Seek to the start of the thumbnail using the offset entry - fseek( $filehnd, $Tiff_offset + $OutputArray[0x0004]['Data'][0] ); - - // Read the thumbnail data, and replace the offset data with the thumbnail - $OutputArray[0x0004]['Data'] = network_safe_fread( $filehnd, $OutputArray[0x0003]['Data'][0] ); - } - - // Minolta Thumbnail - // Check that both the thumbnail length and offset entries have been processed, - // and that this is one of them - if ( ( ( ( $Tag_No == 0x0088 ) && ( array_key_exists( 0x0089, $OutputArray ) ) ) || - ( ( $Tag_No == 0x0089 ) && ( array_key_exists( 0x0088, $OutputArray ) ) ) ) && - ( $Tag_Definitions_Name == "Olympus" ) ) - { - - // Seek to the start of the thumbnail using the offset entry - fseek( $filehnd, $Tiff_offset + $OutputArray[0x0088]['Data'][0] ); - - // Read the thumbnail data, and replace the offset data with the thumbnail - $OutputArray[0x0088]['Data'] = network_safe_fread( $filehnd, $OutputArray[0x0089]['Data'][0] ); - - // Sometimes the minolta thumbnail data is empty (or the offset is corrupt, which results in the same thing) - - // Check if the thumbnail data exists - if ( $OutputArray[0x0088]['Data'] != "" ) - { - // Thumbnail exists - - // Minolta Thumbnails are missing their first 0xFF for some reason, - // which is replaced with some weird character, so fix this - $OutputArray[0x0088]['Data']{0} = "\xFF"; - } - else - { - // Thumbnail doesnt exist - make it obvious - $OutputArray[0x0088]['Data'] = FALSE; - } - } - - } - - - - - - - - // Return the array of IFD entries and the offset to the next IFD - - return array ($OutputArray , $Next_Offset); -} - - - -/****************************************************************************** -* End of Function: read_IFD_universal -******************************************************************************/ - - - - - - - - - - - - -/****************************************************************************** -* -* Internal Function: get_Tag_Text_Value -* -* Description: Attempts to interpret an IFD entry into a text string using the -* information in the IFD_Tag_Definitions global array. -* -* Parameters: Tag - The IFD entry to process -* Tag_Definitions_Name - The name of the tag definitions to use from within the IFD_Tag_Definitions global array -* -* Returns: String - if the tag was successfully decoded into a text string -* FALSE - if the tag could not be decoded using the information -* in the IFD_Tag_Definitions global array -* -******************************************************************************/ - -function get_Tag_Text_Value( $Tag, $Tag_Definitions_Name ) -{ - // Check what format the entry is specified as - - if ( $Tag['Type'] == "String" ) - { - // Format is Text String - - // If "Unknown" (type 7) data type, - if ( $Tag['Data Type'] == 7 ) - { - // Return data as is. - return $Tag['Data']; - } - else - { - // Otherwise return the default string value of the datatype - return get_IFD_value_as_text( $Tag ); - } - } - else if ( $Tag['Type'] == "Character Coded String" ) - { - // Format is Character Coded String (First 8 characters indicate coding scheme) - - // Convert Data to a string - if ( $Tag['Data Type'] == 7 ) - { - // If it is type "Unknown" (type 7) use data as is - $data = $Tag['Data']; - } - else - { - // Otherwise use the default string value of the datatype - $data = get_IFD_value_as_text( $Tag ); - } - - // Some implementations allow completely data with no Coding Scheme Name, - // so we need to handle this to avoid errors - if ( trim( $data ) == "" ) - { - return ""; - } - - // Extract the Coding Scheme Name from the first 8 characters - $char_code = substr( $data, 0, 8 ); - - // Extract the Data part from after the first 8 characters - $characters = substr( $data, 8 ); - - // Check coding scheme and interpret as neccessary - - if ( $char_code === "ASCII\x00\x00\x00" ) - { - // ASCII coding - return data as is. - return $characters; - } - elseif ( ( $char_code === "UNICODE\x00" ) || - ( $char_code === "Unicode\x00" ) ) // Note lowercase is non standard - { - // Unicode coding - interpret and return result. - return xml_UTF16_clean( $characters, TRUE ); - } - else - { - // Unknown coding - return string indicating this - return "Unsupported character coding : \"$char_code\"\n\"" . trim($characters) . "\""; - } - break; - } - else if ( $Tag['Type'] == "Numeric" ) - { - // Format is numeric - return default text value with any required units text appended - if ( array_key_exists ( 'Units', $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag["Tag Number"] ] ) ) - { - $units = $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag["Tag Number"] ]['Units']; - } - else - { - $units = ""; - } - return get_IFD_value_as_text( $Tag ) . " " . $units; - } - else if ( $Tag['Type'] == "Lookup" ) - { - // Format is a Lookup Table - - // Get a numeric value to use in lookup - - if ( is_array( $Tag['Data'] ) ) - { - // If data is an array, use first element - $first_val = $Tag['Data'][0]; - } - else if ( is_string( $Tag['Data'] ) ) - { - // If data is a string, use the first character - $first_val = ord($Tag['Data']{0}); - } - else - { - // Otherwise use the data as is - $first_val = $Tag['Data']; - } - - // Check if the data value exists in the lookup table for this IFD entry - if ( array_key_exists( $first_val, $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag["Tag Number"] ] ) ) - { - // Data value exists in lookup table - return the matching string - return $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag["Tag Number"] ][ $first_val ]; - } - else - { - // Data value doesnt exist in lookup table - return explanation string - return "Unknown Reserved value $first_val "; - } - } - else if ( $Tag['Type'] == "Special" ) - { - // Format is special - interpret to text with special handlers - return get_Special_Tag_Text_Value( $Tag, $Tag_Definitions_Name ); - } - else if ( $Tag['Type'] == "PIM" ) - { - // Format is Print Image Matching info - interpret with custom handler - return get_PIM_Text_Value( $Tag, $Tag_Definitions_Name ); - } - else if ( $Tag['Type'] == "SubIFD" ) - { - // Format is a Sub-IFD - this has no text value - return ""; - } - else - { - // Unknown Format - Couldn't interpret using the IFD_Tag_Definitions global array information - return FALSE; - } -} - -/****************************************************************************** -* End of Function: get_Tag_Text_Value -******************************************************************************/ - - - - - - -/****************************************************************************** -* -* Internal Function: get_Special_Tag_Text_Value -* -* Description: Interprets an IFD entry marked as "Special" in the IFD_Tag_Definitions -* global array into a text string using custom handlers -* -* Parameters: Tag - The IFD entry to process -* Tag_Definitions_Name - The name of the tag definitions to use from within the IFD_Tag_Definitions global array -* -* Returns: String - if the tag was successfully decoded into a text string -* FALSE - if the tag could not be decoded -* -******************************************************************************/ - -function get_Special_Tag_Text_Value( $Tag, $Tag_Definitions_Name ) -{ - // Check what type of IFD is being decoded - - if ( $Tag_Definitions_Name == "TIFF" ) - { - // This is a TIFF IFD (bottom level) - - // Check what tag number the IFD entry has. - switch ( $Tag['Tag Number'] ) - { - case 530: // YCbCr Sub Sampling Entry - - // Data contains two numerical values - - if ( ( $Tag['Data'][0] == 2 ) && ( $Tag['Data'][1] == 1 ) ) - { - // Values are 2,1 - hence YCbCr 4:2:2 - return "YCbCr 4:2:2 ratio of chrominance components to the luminance components"; - } - elseif ( ( $Tag['Data'][0] == 2 ) && ( $Tag['Data'][1] == 2 ) ) - { - // Values are 2,2 - hence YCbCr 4:2:0 - return "YCbCr 4:2:0 ratio of chrominance components to the luminance components"; - } - else - { - // Other values are unknown - return "Unknown Reserved value (" . $Tag['Data'][0] . ")"; - } - break; - - default: - return FALSE; - } - } - else if ( $Tag_Definitions_Name == "EXIF" ) - { - // This is an EXIF IFD - - // Check what tag number the IFD entry has. - switch ( $Tag['Tag Number'] ) - { - - case 37121: // Components configuration - - // Data contains 4 numerical values indicating component type - - $output_str = ""; - - // Cycle through each component - for ( $Num = 0; $Num < 4; $Num++ ) - { - // Construct first part of text string - $output_str .= "Component " . ( $Num + 1 ) . ": "; - - // Construct second part of text string via - // lookup using numerical value - - $value = ord( $Tag['Data']{$Num} ); - switch( $value ) - { - case 0: - $output_str .= "Does not exist\n"; - break; - case 1: - $output_str .= "Y (Luminance)\n"; - break; - case 2: - $output_str .= "Cb (Chroma minus Blue)\n"; - break; - case 3: - $output_str .= "Cr (Chroma minus Red)\n"; - break; - case 4: - $output_str .= "Red\n"; - break; - case 5: - $output_str .= "Green\n"; - break; - case 6: - $output_str .= "Blue\n"; - break; - default: - $output_str .= "Unknown value $value\n"; - }; - } - - // Return the completed string - - return $output_str; - break; - - - - case 41730: // Colour Filter Array Pattern - - // The first two characters are a SHORT for Horizontal repeat pixel unit - - $n_max = get_IFD_Data_Type( substr( $Tag['Data'], 0, 2 ), 3, $Tag['Byte Align'] ); - - // The next two characters are a SHORT for Vertical repeat pixel unit - - $m_max = get_IFD_Data_Type( substr( $Tag['Data'], 2, 2 ), 3, $Tag['Byte Align'] ); - - - // At least one camera type appears to have byte reversed values for N_Max and M_Max - // Check if they need reversing - if ( $n_max > 256 ) - { - $n_max = $n_max/256 + 256*($n_max%256); - } - - if ( $m_max > 256 ) - { - $m_max = $m_max/256 + 256*($m_max%256); - } - - - $output_str = ""; - - - // Cycle through all the elements in the resulting 2 dimensional array, - for( $m = 1; $m <= $m_max; $m++ ) - { - for( $n = 1; $n <= $n_max; $n++ ) - { - - // Append text from a lookup table according to - // the value read for this element - if( isset( $Tag['Data']{($n_max*($m-1)+$n+3)} ) ) { - switch ( ord($Tag['Data']{($n_max*($m-1)+$n+3)}) ) - { - case 0: - $output_str .= "RED "; - break; - case 1: - $output_str .= "GREEN "; - break; - case 2: - $output_str .= "BLUE "; - break; - case 3: - $output_str .= "CYAN "; - break; - case 4: - $output_str .= "MAGENTA "; - break; - case 5: - $output_str .= "YELLOW "; - break; - case 6: - $output_str .= "WHITE "; - break; - default: - $output_str .= "Unknown "; - break; - }; - } - }; - $output_str .= "\n"; - }; - - // Return the resulting string - return $output_str; - break; - - default: - return FALSE; - } - } - else - { - // Unknown IFD type, see if it is part of a makernote - return get_Makernote_Text_Value( $Tag, $Tag_Definitions_Name ); - } - - -} - -/****************************************************************************** -* End of Function: get_Tag_Text_Value -******************************************************************************/ - - - - - - - - -/****************************************************************************** -* -* Function: interpret_IFD -* -* Description: Generates html detailing the contents a single IFD. -* -* Parameters: IFD_array - the array containing an IFD -* filename - the name of the Image file being processed ( used -* by scripts which displays EXIF thumbnails) -* -* Returns: output_str - A string containing the HTML -* -******************************************************************************/ - -function interpret_IFD( $IFD_array, $filename ) -{ - // Create the output string with the table tag - $output_str = "\n"; - - // Create an extra output string to receive any supplementary html - // which cannot go inside the table - $extra_IFD_str = ""; - - // Check that the IFD array is valid - if ( ( $IFD_array === FALSE ) || ( $IFD_array === NULL ) ) - { - // the IFD array is NOT valid - exit - return ""; - } - - // Check if this is an EXIF IFD and if there is a makernote present - if ( ( $IFD_array['Tags Name'] === "EXIF" ) && - ( ! array_key_exists( 37500, $IFD_array ) ) ) - { - - // This is an EXIF IFD but NO makernote is present - Add a message to the output - $extra_IFD_str .= "

    No Makernote Present

    "; - } - - // Cycle through each tag in the IFD - - foreach( $IFD_array as $Tag_ID => $Exif_Tag ) - { - - // Ignore the non numeric elements - they aren't tags - if ( ! is_numeric ( $Tag_ID ) ) - { - // Skip Tags Name - } - // Check if the Tag has been decoded successfully - else if ( $Exif_Tag['Decoded'] == TRUE ) - { - // This tag has been successfully decoded - - // Table cells won't get drawn with nothing in them - - // Ensure that at least a non breaking space exists in them - - if ( trim($Exif_Tag['Text Value']) == "" ) - { - $Exif_Tag['Text Value'] = " "; - } - - // Check if the tag is a sub-IFD - if ( $Exif_Tag['Type'] == "SubIFD" ) - { - // This is a sub-IFD tag - // Add a sub-heading for the sub-IFD - $extra_IFD_str .= "

    " . $Exif_Tag['Tag Name'] . " contents

    "; - - // Cycle through each sub-IFD in the chain - foreach ( $Exif_Tag['Data'] as $subIFD ) - { - // Interpret this sub-IFD and add the html to the secondary output - $extra_IFD_str .= interpret_IFD( $subIFD, $filename ); - } - } - // Check if the tag is a makernote - else if ( $Exif_Tag['Type'] == "Maker Note" ) - { - // This is a Makernote Tag - // Add a sub-heading for the Makernote - $extra_IFD_str .= "

    Maker Note Contents

    "; - - // Interpret the Makernote and add the html to the secondary output - $extra_IFD_str .= Interpret_Makernote_to_HTML( $Exif_Tag, $filename ); - } - // Check if this is a IPTC/NAA Record within the EXIF IFD - else if ( $Exif_Tag['Type'] == "IPTC" ) - { - // This is a IPTC/NAA Record, interpret it and output to the secondary html - $extra_IFD_str .= "

    Contains IPTC/NAA Embedded in EXIF

    "; - $extra_IFD_str .=Interpret_IPTC_to_HTML( $Exif_Tag['Data'] ); - } - // Change: Check for embedded XMP as of version 1.11 - // Check if this is a XMP Record within the EXIF IFD - else if ( $Exif_Tag['Type'] == "XMP" ) - { - // This is a XMP Record, interpret it and output to the secondary html - $extra_IFD_str .= "

    Contains XMP Embedded in EXIF

    "; - $extra_IFD_str .= Interpret_XMP_to_HTML( $Exif_Tag['Data'] ); - } - // Change: Check for embedded IRB as of version 1.11 - // Check if this is a Photoshop IRB Record within the EXIF IFD - else if ( $Exif_Tag['Type'] == "IRB" ) - { - // This is a Photoshop IRB Record, interpret it and output to the secondary html - $extra_IFD_str .= "

    Contains Photoshop IRB Embedded in EXIF

    "; - $extra_IFD_str .= Interpret_IRB_to_HTML( $Exif_Tag['Data'], $filename ); - } - // Check if the tag is Numeric - else if ( $Exif_Tag['Type'] == "Numeric" ) - { - // Numeric Tag - Output text value as is. - $output_str .= "\n"; - } - else - { - // Other tag - Output text as preformatted - $output_str .= "\n"; - } - - } - else - { - // Tag has NOT been decoded successfully - // Hence it is either an unknown tag, or one which - // requires processing at the time of html construction - - // Table cells won't get drawn with nothing in them - - // Ensure that at least a non breaking space exists in them - - if ( trim($Exif_Tag['Text Value']) == "" ) - { - $Exif_Tag['Text Value'] = " "; - } - - // Check if this tag is the first IFD Thumbnail - if ( ( $IFD_array['Tags Name'] == "TIFF" ) && - ( $Tag_ID == 513 ) ) - { - // This is the first IFD thumbnail - Add html to the output - - // Change: as of version 1.11 - Changed to make thumbnail link portable across directories - // Build the path of the thumbnail script and its filename parameter to put in a url - $link_str = get_relative_path( dirname(__FILE__) . "/get_exif_thumb.php" , getcwd ( ) ); - $link_str .= "?filename="; - $link_str .= get_relative_path( $filename, dirname(__FILE__) ); - - // Add thumbnail link to html - $output_str .= "\n"; - } - // Check if this is the Makernote - else if ( $Exif_Tag['Type'] == "Maker Note" ) - { - // This is the makernote, but has not been decoded - // Add a message to the secondary output - $extra_IFD_str .= "

    Makernote Coding Unknown

    \n"; - } - else - { - // This is an Unknown Tag - - // Check if the user wants to hide unknown tags - if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] === FALSE ) - { - // User wants to display unknown tags - - // Check if the Data is an ascii string - if ( $Exif_Tag['Data Type'] == 2 ) - { - // This is a Ascii String field - add it preformatted to the output - $output_str .= "\n"; - } - else - { - // Not an ASCII string - add it as is to the output - $output_str .= "\n"; - } - } - } - } - } - - // Close the table in the output - $output_str .= "
    " . $Exif_Tag['Tag Name'] . "" . $Exif_Tag['Text Value'] . "
    " . $Exif_Tag['Tag Name'] . "
    " . trim( $Exif_Tag['Text Value']) . "
    " . $Exif_Tag['Tag Name'] . "
    " . $Exif_Tag['Tag Name'] . "
    " . trim( $Exif_Tag['Text Value'] ) . "
    " . $Exif_Tag['Tag Name'] . "" . trim( $Exif_Tag['Text Value'] ) . "
    \n"; - - // Add the secondary output at the end of the main output - $output_str .= "$extra_IFD_str\n"; - - // Return the resulting html - return $output_str; -} - -/****************************************************************************** -* End of Function: interpret_IFD -******************************************************************************/ - - - - - - - - - - - - - - - -/****************************************************************************** -* -* Function: get_IFD_Data_Type -* -* Description: Decodes an IFD field value from a binary data string, using -* information supplied about the data type and byte alignment of -* the stored data. -* This function should be used for all datatypes except ASCII strings -* -* Parameters: input_data - a binary data string containing the IFD value, -* must be exact length of the value -* data_type - a number representing the IFD datatype as per the -* TIFF 6.0 specification: -* 1 = Unsigned 8-bit Byte -* 2 = ASCII String -* 3 = Unsigned 16-bit Short -* 4 = Unsigned 32-bit Long -* 5 = Unsigned 2x32-bit Rational -* 6 = Signed 8-bit Byte -* 7 = Undefined -* 8 = Signed 16-bit Short -* 9 = Signed 32-bit Long -* 10 = Signed 2x32-bit Rational -* 11 = 32-bit Float -* 12 = 64-bit Double -* Byte_Align - Indicates the byte alignment of the data. -* MM = Motorola, MSB first, Big Endian -* II = Intel, LSB first, Little Endian -* -* Returns: output - the value of the data (string or numeric) -* -******************************************************************************/ - -function get_IFD_Data_Type( $input_data, $data_type, $Byte_Align ) -{ - $value = NULL; - if( !empty( $input_data ) ) { - // Check if this is a Unsigned Byte, Unsigned Short or Unsigned Long - if (( $data_type == 1 ) || ( $data_type == 3 ) || ( $data_type == 4 )) - { - // This is a Unsigned Byte, Unsigned Short or Unsigned Long - - // Check the byte alignment to see if the bytes need tp be reversed - if ( $Byte_Align == "II" ) - { - // This is in Intel format, reverse it - $input_data = strrev ( $input_data ); - } - - // Convert the binary string to a number and return it - return hexdec( bin2hex( $input_data ) ); - } - // Check if this is a ASCII string type - elseif ( $data_type == 2 ) - { - // Null terminated ASCII string(s) - // The input data may represent multiple strings, as the - // 'count' field represents the total bytes, not the number of strings - // Hence this should not be processed here, as it would have - // to return multiple values instead of a single value - - error_log( "Error - ASCII Strings should not be processed in get_IFD_Data_Type" ); - return "Error Should never get here"; //explode( "\x00", $input_data ); - } - // Check if this is a Unsigned rational type - elseif ( $data_type == 5 ) - { - // This is a Unsigned rational type - - // Check the byte alignment to see if the bytes need to be reversed - if ( $Byte_Align == "MM" ) - { - // Motorola MSB first byte aligment - // Unpack the Numerator and denominator and return them - return @unpack( 'NNumerator/NDenominator', $input_data ); - } - else - { - // Intel LSB first byte aligment - // Unpack the Numerator and denominator and return them - return @unpack( 'VNumerator/VDenominator', $input_data ); - } - } - // Check if this is a Signed Byte, Signed Short or Signed Long - elseif ( ( $data_type == 6 ) || ( $data_type == 8 ) || ( $data_type == 9 ) ) - { - // This is a Signed Byte, Signed Short or Signed Long - - // Check the byte alignment to see if the bytes need to be reversed - if ( $Byte_Align == "II" ) - { - //Intel format, reverse the bytes - $input_data = strrev ( $input_data ); - } - - // Convert the binary string to an Unsigned number - $value = hexdec( bin2hex( $input_data ) ); - - // Convert to signed number - - // Check if it is a Byte above 128 (i.e. a negative number) - if ( ( $data_type == 6 ) && ( $value > 128 ) ) - { - // number should be negative - make it negative - return $value - 256; - } - - // Check if it is a Short above 32767 (i.e. a negative number) - if ( ( $data_type == 8 ) && ( $value > 32767 ) ) - { - // number should be negative - make it negative - return $value - 65536; - } - - // Check if it is a Long above 2147483648 (i.e. a negative number) - if ( ( $data_type == 9 ) && ( $value > 2147483648 ) ) - { - // number should be negative - make it negative - return $value - 4294967296; - } - - // Return the signed number - return $value; - } - // Check if this is Undefined type - elseif ( $data_type == 7 ) - { - // Custom Data - Do nothing - return $input_data; - } - // Check if this is a Signed Rational type - elseif ( $data_type == 10 ) - { - // This is a Signed Rational type - - // Signed Long not available with endian in unpack , use unsigned and convert - - // Check the byte alignment to see if the bytes need to be reversed - if ( $Byte_Align == "MM" ) - { - // Motorola MSB first byte aligment - // Unpack the Numerator and denominator - $value = @unpack( 'NNumerator/NDenominator', $input_data ); - } - else - { - // Intel LSB first byte aligment - // Unpack the Numerator and denominator - $value = @unpack( 'VNumerator/VDenominator', $input_data ); - } - - // Convert the numerator to a signed number - // Check if it is above 2147483648 (i.e. a negative number) - if ( $value['Numerator'] > 2147483648 ) - { - // number is negative - $value['Numerator'] -= 4294967296; - } - - // Convert the denominator to a signed number - // Check if it is above 2147483648 (i.e. a negative number) - if ( $value['Denominator'] > 2147483648 ) - { - // number is negative - $value['Denominator'] -= 4294967296; - } - - // Return the Signed Rational value - return $value; - } - // Check if this is a Float type - elseif ( $data_type == 11 ) - { - // IEEE 754 Float - // TODO - EXIF - IFD datatype Float not implemented yet - return "FLOAT NOT IMPLEMENTED YET"; - } - // Check if this is a Double type - elseif ( $data_type == 12 ) - { - // IEEE 754 Double - // TODO - EXIF - IFD datatype Double not implemented yet - return "DOUBLE NOT IMPLEMENTED YET"; - } - else - { - // Error - Invalid Datatype - return "Invalid Datatype $data_type"; - - } - } else { - return "Invalid Datatype $data_type"; - } - -} - -/****************************************************************************** -* End of Function: get_IFD_Data_Type -******************************************************************************/ - - - - - - -/****************************************************************************** -* -* Function: put_IFD_Data_Type -* -* Description: Encodes an IFD field from a value to a binary data string, using -* information supplied about the data type and byte alignment of -* the stored data. -* -* Parameters: input_data - an IFD data value, numeric or string -* data_type - a number representing the IFD datatype as per the -* TIFF 6.0 specification: -* 1 = Unsigned 8-bit Byte -* 2 = ASCII String -* 3 = Unsigned 16-bit Short -* 4 = Unsigned 32-bit Long -* 5 = Unsigned 2x32-bit Rational -* 6 = Signed 8-bit Byte -* 7 = Undefined -* 8 = Signed 16-bit Short -* 9 = Signed 32-bit Long -* 10 = Signed 2x32-bit Rational -* 11 = 32-bit Float -* 12 = 64-bit Double -* Byte_Align - Indicates the byte alignment of the data. -* MM = Motorola, MSB first, Big Endian -* II = Intel, LSB first, Little Endian -* -* Returns: output - the packed binary string of the data -* -******************************************************************************/ - -function put_IFD_Data_Type( $input_data, $data_type, $Byte_Align ) -{ - // Process according to the datatype - switch ( $data_type ) - { - case 1: // Unsigned Byte - return character as is - return chr($input_data); - break; - - case 2: // ASCII String - // Return the string with terminating null - return $input_data . "\x00"; - break; - - case 3: // Unsigned Short - // Check byte alignment - if ( $Byte_Align == "II" ) - { - // Intel/Little Endian - pack the short and return - return pack( "v", $input_data ); - } - else - { - // Motorola/Big Endian - pack the short and return - return pack( "n", $input_data ); - } - break; - - case 4: // Unsigned Long - // Check byte alignment - if ( $Byte_Align == "II" ) - { - // Intel/Little Endian - pack the long and return - return pack( "V", $input_data ); - } - else - { - // Motorola/Big Endian - pack the long and return - return pack( "N", $input_data ); - } - break; - - case 5: // Unsigned Rational - // Check byte alignment - if ( $Byte_Align == "II" ) - { - // Intel/Little Endian - pack the two longs and return - return pack( "VV", $input_data['Numerator'], $input_data['Denominator'] ); - } - else - { - // Motorola/Big Endian - pack the two longs and return - return pack( "NN", $input_data['Numerator'], $input_data['Denominator'] ); - } - break; - - case 6: // Signed Byte - // Check if number is negative - if ( $input_data < 0 ) - { - // Number is negative - return signed character - return chr( $input_data + 256 ); - } - else - { - // Number is positive - return character - return chr( $input_data ); - } - break; - - case 7: // Unknown - return as is - return $input_data; - break; - - case 8: // Signed Short - // Check if number is negative - if ( $input_data < 0 ) - { - // Number is negative - make signed value - $input_data = $input_data + 65536; - } - // Check byte alignment - if ( $Byte_Align == "II" ) - { - // Intel/Little Endian - pack the short and return - return pack( "v", $input_data ); - } - else - { - // Motorola/Big Endian - pack the short and return - return pack( "n", $input_data ); - } - break; - - case 9: // Signed Long - // Check if number is negative - if ( $input_data < 0 ) - { - // Number is negative - make signed value - $input_data = $input_data + 4294967296; - } - // Check byte alignment - if ( $Byte_Align == "II" ) - { - // Intel/Little Endian - pack the long and return - return pack( "v", $input_data ); - } - else - { - // Motorola/Big Endian - pack the long and return - return pack( "n", $input_data ); - } - break; - - case 10: // Signed Rational - // Check if numerator is negative - if ( $input_data['Numerator'] < 0 ) - { - // Number is numerator - make signed value - $input_data['Numerator'] = $input_data['Numerator'] + 4294967296; - } - // Check if denominator is negative - if ( $input_data['Denominator'] < 0 ) - { - // Number is denominator - make signed value - $input_data['Denominator'] = $input_data['Denominator'] + 4294967296; - } - // Check byte alignment - if ( $Byte_Align == "II" ) - { - // Intel/Little Endian - pack the two longs and return - return pack( "VV", $input_data['Numerator'], $input_data['Denominator'] ); - } - else - { - // Motorola/Big Endian - pack the two longs and return - return pack( "NN", $input_data['Numerator'], $input_data['Denominator'] ); - } - break; - - case 11: // Float - // IEEE 754 Float - // TODO - EXIF - IFD datatype Float not implemented yet - return "FLOAT NOT IMPLEMENTED YET"; - break; - - case 12: // Double - // IEEE 754 Double - // TODO - EXIF - IFD datatype Double not implemented yet - return "DOUBLE NOT IMPLEMENTED YET"; - break; - - default: - // Error - Invalid Datatype - return "Invalid Datatype $data_type"; - break; - - } - - // Shouldn't get here - return FALSE; -} - -/****************************************************************************** -* End of Function: put_IFD_Data_Type -******************************************************************************/ - - - - - -/****************************************************************************** -* -* Function: get_IFD_value_as_text -* -* Description: Decodes an IFD field value from a binary data string, using -* information supplied about the data type and byte alignment of -* the stored data. -* This function should be used for all datatypes except ASCII strings -* -* Parameters: input_data - a binary data string containing the IFD value, -* must be exact length of the value -* data_type - a number representing the IFD datatype as per the -* TIFF 6.0 specification: -* 1 = Unsigned 8-bit Byte -* 2 = ASCII String -* 3 = Unsigned 16-bit Short -* 4 = Unsigned 32-bit Long -* 5 = Unsigned 2x32-bit Rational -* 6 = Signed 8-bit Byte -* 7 = Undefined -* 8 = Signed 16-bit Short -* 9 = Signed 32-bit Long -* 10 = Signed 2x32-bit Rational -* 11 = 32-bit Float -* 12 = 64-bit Double -* Byte_Align - Indicates the byte alignment of the data. -* MM = Motorola, MSB first, Big Endian -* II = Intel, LSB first, Little Endian -* -* Returns: output - the value of the data (string or numeric) -* -******************************************************************************/ - -function get_IFD_value_as_text( $Exif_Tag ) -{ - // Create a string to receive the output text - $output_str = ""; - -if( !empty( $Exif_Tag['Data Type'] ) ) { - // Select Processing method according to the datatype - switch ($Exif_Tag['Data Type']) - { - case 1 : // Unsigned Byte - case 3 : // Unsigned Short - case 4 : // Unsigned Long - case 6 : // Signed Byte - case 8 : // Signed Short - case 9 : // Signed Long - - // Cycle through each of the values for this tag - foreach ( $Exif_Tag['Data'] as $val ) - { - // Check that this isn't the first value, - if ( $output_str != "" ) - { - // This isn't the first value, Add a Comma and Newline to the output - $output_str .= ",\n"; - } - // Add the Value to the output - $output_str .= $val; - } - break; - - case 2 : // ASCII - // Append all the strings together, separated by Newlines - $output_str .= implode ( "\n", $Exif_Tag['Data']); - break; - - case 5 : // Unsigned Rational - case 10: // Signed Rational - - // Cycle through each of the values for this tag - foreach ( $Exif_Tag['Data'] as $val ) - { - // Check that this isn't the first value, - if ( $output_str != "" ) - { - // This isn't the first value, Add a Comma and Newline to the output - $output_str .= ",\n"; - } - - // Add the Full Value to the output - $output_str .= $val['Numerator'] ."/" . $val['Denominator']; - - // Check if division by zero might be a problem - if ( $val['Denominator'] != 0 ) - { - // Denominator is not zero, Add the Decimal Value to the output text - $output_str .= " (" . ($val['Numerator'] / $val['Denominator']) . ")"; - } - } - break; - - case 11: // Float - case 12: // Double - // TODO - EXIF - IFD datatype Double and Float not implemented yet - $output_str .= "Float and Double not implemented yet"; - break; - - case 7 : // Undefined - // Unless the User has asked to see the raw binary data, this - // type should not be displayed - - // Check if the user has requested to see the binary data in hex - if ( $GLOBALS['SHOW_BINARY_DATA_HEX'] == TRUE) - { - // User has requested to see the binary data in hex - // Add the value in hex - $output_str .= "( " . strlen( $Exif_Tag['Data'] ) . " bytes of binary data ): " . bin2hex( $Exif_Tag['Data'] ) ; - } - // Check if the user has requested to see the binary data as is - else if ( $GLOBALS['SHOW_BINARY_DATA_TEXT'] == TRUE) - { - // User has requested to see the binary data as is - // Add the value as is - $output_str .= "( " . strlen( $Exif_Tag['Data'] ) . " bytes of binary data ): " . $Exif_Tag['Data'] ; - } - else - { - // User has NOT requested to see binary data, - // Add a message indicating the number of bytes to the output - $output_str .= "( " . strlen( $Exif_Tag['Data'] ) . " bytes of binary data ) " ; - } - break; - - default : - // Error - Unknown IFD datatype - $output_str .= "Error - Exif tag data type (" . $Exif_Tag['Data Type'] .") is invalid"; - break; - } -} - - // Return the resulting text string - return $output_str; -} - -/****************************************************************************** -* End of Function: get_IFD_value_as_text -******************************************************************************/ - - - - -/****************************************************************************** -* Global Variable: IFD_Data_Sizes -* -* Contents: The sizes (in bytes) of each EXIF IFD Datatype, indexed by -* their datatype number -* -******************************************************************************/ - -$GLOBALS['IFD_Data_Sizes'] = array( 1 => 1, // Unsigned Byte - 2 => 1, // ASCII String - 3 => 2, // Unsigned Short - 4 => 4, // Unsigned Long - 5 => 8, // Unsigned Rational - 6 => 1, // Signed Byte - 7 => 1, // Undefined - 8 => 2, // Signed Short - 9 => 4, // Signed Long - 10 => 8, // Signed Rational - 11 => 4, // Float - 12 => 8 ); // Double - -/****************************************************************************** -* End of Global Variable: IFD_Data_Sizes -******************************************************************************/ - - - - - - -?> diff --git a/jpeg_metadata_tk/EXIF_Makernote.php b/jpeg_metadata_tk/EXIF_Makernote.php deleted file mode 100644 index ceb82f4..0000000 --- a/jpeg_metadata_tk/EXIF_Makernote.php +++ /dev/null @@ -1,334 +0,0 @@ - 1.11 : changed makernotes directory definition to allow -* the toolkit to be portable across directories -* -* URL: http://electronics.ozhiker.com -* -* Copyright: Copyright Evan Hunter 2004 -* -* License: This file is part of the PHP JPEG Metadata Toolkit. -* -* The PHP JPEG Metadata Toolkit is free software; you can -* redistribute it and/or modify it under the terms of the -* GNU General Public License as published by the Free Software -* Foundation; either version 2 of the License, or (at your -* option) any later version. -* -* The PHP JPEG Metadata Toolkit is distributed in the hope -* that it will be useful, but WITHOUT ANY WARRANTY; without -* even the implied warranty of MERCHANTABILITY or FITNESS -* FOR A PARTICULAR PURPOSE. See the GNU General Public License -* for more details. -* -* You should have received a copy of the GNU General Public -* License along with the PHP JPEG Metadata Toolkit; if not, -* write to the Free Software Foundation, Inc., 59 Temple -* Place, Suite 330, Boston, MA 02111-1307 USA -* -* If you require a different license for commercial or other -* purposes, please contact the author: evan@ozhiker.com -* -******************************************************************************/ - - -// Create the Makernote Parser and Interpreter Function Array - -$GLOBALS['Makernote_Function_Array'] = array( "Read_Makernote_Tag" => array( ), - "get_Makernote_Text_Value" => array( ), - "Interpret_Makernote_to_HTML" => array( ) ); - - -// Include the Main TIFF and EXIF Tags array - -include_once 'EXIF.php'; - - - -/****************************************************************************** -* -* Include the Makernote Scripts -* -******************************************************************************/ - -// Set the Makernotes Directory - -$dir = dirname(__FILE__) . "/Makernotes/"; // Change: as of version 1.11 - to allow directory portability - -// Open the directory -$dir_hnd = @opendir ( $dir ); - -// Cycle through each of the files in the Makernotes directory - -while ( ( $file = readdir( $dir_hnd ) ) !== false ) -{ - // Check if the current item is a file - if ( is_file ( $dir . $file ) ) - { - // Item is a file, break it into it's parts - $path_parts = pathinfo( $dir . $file ); - - // Check if the extension is php - if ( $path_parts["extension"] == "php" ) - { - // This is a php script - include it - include_once ($dir . $file) ; - } - } -} -// close the directory -closedir( $dir_hnd ); - - - - - - - - - - -/****************************************************************************** -* -* Function: Read_Makernote_Tag -* -* Description: Attempts to decodes the Makernote tag supplied, returning the -* new tag with the decoded information attached. -* -* Parameters: Makernote_Tag - the element of an EXIF array containing the -* makernote, as returned from get_EXIF_JPEG -* EXIF_Array - the entire EXIF array containing the -* makernote, as returned from get_EXIF_JPEG, in -* case more information is required for decoding -* filehnd - an open file handle for the file containing the -* makernote - does not have to be positioned at the -* start of the makernote -* -* -* Returns: Makernote_Tag - the Makernote_Tag from the parameters, but -* modified to contain the decoded information -* -******************************************************************************/ - -function Read_Makernote_Tag( $Makernote_Tag, $EXIF_Array, $filehnd ) -{ - - // Check if the Makernote is present but empty - this sometimes happens - if ( ( strlen( $Makernote_Tag['Data'] ) === 0 ) || - ( is_string( $Makernote_Tag['Data'] ) && $Makernote_Tag['Data'] === str_repeat ( "\x00", strlen( $Makernote_Tag['Data'] )) ) ) - { - // Modify the makernote to display that it is empty - $Makernote_Tag['Decoded Data'] = "Empty"; - $Makernote_Tag['Makernote Type'] = "Empty"; - $Makernote_Tag['Makernote Tags'] = "Empty"; - $Makernote_Tag['Decoded'] = TRUE; - - // Return the new makernote - return $Makernote_Tag; - } - - // Check if the Make Field exists in the TIFF IFD - if ( array_key_exists ( 271, $EXIF_Array[0] ) ) - { - // A Make tag exists in IFD0, collapse multiple strings (if any), and save result - $Make_Field = implode ( "\n", $EXIF_Array[0][271]['Data']); - } - else - { - // No Make field found - $Make_Field = ""; - } - - // Cycle through each of the "Read_Makernote_Tag" functions - - foreach( $GLOBALS['Makernote_Function_Array']['Read_Makernote_Tag'] as $func ) - { - // Run the current function, and save the result - $New_Makernote_Tag = $func( $Makernote_Tag, $EXIF_Array, $filehnd, $Make_Field ); - - // Check if a valid result was returned - if ( $New_Makernote_Tag !== FALSE ) - { - // A valid result was returned - stop cycling - break; - } - } - - // Check if a valid result was returned - if ( $New_Makernote_Tag === false ) - { - // A valid result was NOT returned - construct a makernote tag representing this - $New_Makernote_Tag = $Makernote_Tag; - $New_Makernote_Tag['Decoded'] = FALSE; - $New_Makernote_Tag['Makernote Type'] = "Unknown Makernote"; - } - - // Return the new makernote tag - return $New_Makernote_Tag; - -} - -/****************************************************************************** -* End of Function: Read_Makernote_Tag -******************************************************************************/ - - - - - - - - - -/****************************************************************************** -* -* Function: get_Makernote_Text_Value -* -* Description: Attempts to provide a text value for any makernote tag marked -* as type special. Returns false no handler could be found to -* process the tag -* -* Parameters: Exif_Tag - the element of an the Makernote array containing the -* tag in question, as returned from Read_Makernote_Tag -* Tag_Definitions_Name - The name of the Tag Definitions group -* within the global array IFD_Tag_Definitions -* -* -* Returns: output - the text value for the tag -* FALSE - If no handler could be found to process this tag, or if -* an error occured in decoding -* -******************************************************************************/ - -function get_Makernote_Text_Value( $Tag, $Tag_Definitions_Name ) -{ - - // Cycle through each of the "get_Makernote_Text_Value" functions - - foreach( $GLOBALS['Makernote_Function_Array']['get_Makernote_Text_Value'] as $func ) - { - // Run the current function, and save the result - $Text_Val = $func( $Tag, $Tag_Definitions_Name ); - - // Check if a valid result was returned - if ( $Text_Val !== FALSE ) - { - // valid result - return it - return $Text_Val; - } - } - - // No Special tag handler found for this tag - return false - return FALSE; - -} - - -/****************************************************************************** -* End of Function: get_Makernote_Text_Value -******************************************************************************/ - - - - - - - - -/****************************************************************************** -* -* Function: Interpret_Makernote_to_HTML -* -* Description: Attempts to interpret a makernote into html. -* -* Parameters: Makernote_Tag - the element of an EXIF array containing the -* makernote, as returned from get_EXIF_JPEG -* filename - the name of the JPEG file being processed ( used -* by scripts which display embedded thumbnails) -* -* -* Returns: output - the html representing the makernote -* -******************************************************************************/ - -function Interpret_Makernote_to_HTML( $Makernote_tag, $filename ) -{ - - // Create a string to receive the HTML - $output_str = ""; - - // Check if the makernote tag is valid - if ( $Makernote_tag === FALSE ) - { - // No makernote info - return - return $output_str; - } - - - // Check if the makernote has been marked as unknown - if ( $Makernote_tag['Makernote Type'] == "Unknown Makernote" ) - { - // Makernote is unknown - return message - $output_str .= "

    Unknown Makernote Coding

    \n"; - return $output_str; - } - else - { - // Makernote is known - add a heading to the output - $output_str .= "

    Makernote Coding: " . $Makernote_tag['Makernote Type'] . "

    \n"; - } - - // Check if this is an empty makernote - if ( $Makernote_tag['Makernote Type'] == "Empty" ) - { - // It is empty - don't try to interpret - return $output_str; - } - - // Cycle through each of the "Interpret_Makernote_to_HTML" functions - - foreach( $GLOBALS['Makernote_Function_Array']['Interpret_Makernote_to_HTML'] as $func ) - { - // Run the current function, and save the result - $html_text = $func( $Makernote_tag, $filename ); - - // Check if a valid result was returned - if ( $html_text !== FALSE ) - { - // valid result - return it - return $output_str . $html_text; - } - } - - // No Interpreter function handled the makernote - return a message - - $output_str .= "

    Could not Decode Makernote, it may be corrupted or empty

    \n"; - - return $output_str; - - -} - -/****************************************************************************** -* End of Function: Interpret_Makernote_to_HTML -******************************************************************************/ - - - -?> diff --git a/jpeg_metadata_tk/EXIF_Tags.php b/jpeg_metadata_tk/EXIF_Tags.php deleted file mode 100644 index cb36f18..0000000 --- a/jpeg_metadata_tk/EXIF_Tags.php +++ /dev/null @@ -1,915 +0,0 @@ - 1.11 : Added TIFF compression types ZIP, LZW and JPEG -* Added embedded XMP tag -* Added embedded Photoshop IRB tag -* Fixed GPS tags after testing -* -* URL: http://electronics.ozhiker.com -* -* Copyright: Copyright Evan Hunter 2004 -* -* License: This file is part of the PHP JPEG Metadata Toolkit. -* -* The PHP JPEG Metadata Toolkit is free software; you can -* redistribute it and/or modify it under the terms of the -* GNU General Public License as published by the Free Software -* Foundation; either version 2 of the License, or (at your -* option) any later version. -* -* The PHP JPEG Metadata Toolkit is distributed in the hope -* that it will be useful, but WITHOUT ANY WARRANTY; without -* even the implied warranty of MERCHANTABILITY or FITNESS -* FOR A PARTICULAR PURPOSE. See the GNU General Public License -* for more details. -* -* You should have received a copy of the GNU General Public -* License along with the PHP JPEG Metadata Toolkit; if not, -* write to the Free Software Foundation, Inc., 59 Temple -* Place, Suite 330, Boston, MA 02111-1307 USA -* -* If you require a different license for commercial or other -* purposes, please contact the author: evan@ozhiker.com -* -******************************************************************************/ - - - - - - -/****************************************************************************** -* Global Variable: IFD_Tag_Definitions -* -* Contents: This array defines the fields for the TIFF, EXIF, Interoperability, -* GPS, Meta, Kodak Special Effects and Kodak Borders IFD's. -* It is indexed by the IFD Type, then the Tag number -* -******************************************************************************/ - -$GLOBALS[ "IFD_Tag_Definitions" ] = array( - - -/*****************************************************************************/ -/* */ -/* TIFF Tags */ -/* */ -/*****************************************************************************/ - - -"TIFF" => array( - - -256 => array( 'Name' => "Image Width", - 'Description' => "Width of image in pixels (number of columns)", - 'Type' => "Numeric", - 'Units' => "pixels" ), - -257 => array( 'Name' => "Image Length", - 'Description' => "Height of image in pixels (number of rows)", - 'Type' => "Numeric", - 'Units' => "pixels" ), - -258 => array( 'Name' => "Bits Per Sample", - 'Description' => "Number of bits recorded per sample (a sample is usually one colour (Red, Green or Blue) of one pixel)", - 'Type' => "Numeric", - 'Units' => "bits ( for each colour component )" ), - - -259 => array( 'Name' => "Compression", - 'Description' => "Specifies what type of compression is used 1 = uncompressed, 6 = JPEG compression (thumbnails only), Other = reserved", - 'Type' => "Lookup", - 1 => "Uncompressed", - 5 => "LZW Compression", - 6 => "Thumbnail compressed with JPEG compression", - 7 => "JPEG Compression", - 8 => "ZIP Compression" ), // Change: Added TIFF compression types as of version 1.11 - -262 => array( 'Name' => "Photometric Interpretation", - 'Description' => "Specifies Pixel Composition - 0 or 1 = monochrome, 2 = RGB, 3 = Palatte Colour, 4 = Transparency Mask, 6 = YCbCr", - 'Type' => "Lookup", - 2 => "RGB (Red Green Blue)", - 6 => "YCbCr (Luminance, Chroma minus Blue, and Chroma minus Red)" ), - -274 => array( 'Name' => "Orientation", - 'Description' => "Specifies the orientation of the image.\n -1 = Row 0 top, column 0 left\n -2 = Row 0 top, column 0 right\n -3 = Row 0 bottom, column 0 right\n -4 = Row 0 bottom, column 0 left\n -5 = Row 0 left, column 0 top\n -6 = Row 0 right, column 0 top\n -7 = Row 0 right, column 0 bottom\n -8 = Row 0 left, column 0 bottom", - 'Type' => "Lookup", - 1 => "No Rotation, No Flip \n(Row 0 is at the visual top of the image,\n and column 0 is the visual left-hand side)", - 2 => "No Rotation, Flipped Horizontally \n(Row 0 is at the visual top of the image,\n and column 0 is the visual right-hand side)", - 3 => "Rotated 180 degrees, No Flip \n(Row 0 is at the visual bottom of the image,\n and column 0 is the visual right-hand side)", - 4 => "No Rotation, Flipped Vertically \n(Row 0 is at the visual bottom of the image,\n and column 0 is the visual left-hand side)", - 5 => "Flipped Horizontally, Rotated 90 degrees counter clockwise \n(Row 0 is at the visual left-hand side of of the image,\n and column 0 is the visual top)", - 6 => "No Flip, Rotated 90 degrees clockwise \n(Row 0 is at the visual right-hand side of of the image,\n and column 0 is the visual top)", - 7 => "Flipped Horizontally, Rotated 90 degrees clockwise \n(Row 0 is at the visual right-hand side of of the image,\n and column 0 is the visual bottom)", - 8 => "No Flip, Rotated 90 degrees counter clockwise \n(Row 0 is at the visual left-hand side of of the image,\n and column 0 is the visual bottom)" ), -277 => array( 'Name' => "Samples Per Pixel", - 'Description' => "Number of recorded samples (colours) per pixel - usually 1 for B&W, grayscale, and palette-colour, usually 3 for RGB and YCbCr", - 'Type' => "Numeric", - 'Units' => "Components (colours)" ), - -284 => array( 'Name' => "Planar Configuration", - 'Description' => "Specifies whether pixel components are recorded in chunky or planar format - 1 = Chunky, 2 = Planar", - 'Type' => "Lookup", - 1 => "Chunky Format", - 2 => "Planar Format" ), - -530 => array( 'Name' => "YCbCr Sub-Sampling", - 'Description' => "Specifies ratio of chrominance to luminance components - [2, 1] = YCbCr4:2:2, [2, 2] = YCbCr4:2:0", - 'Type' => "Special" ), - - -531 => array( 'Name' => "YCbCr Positioning", - 'Description' => "Specifies location of chrominance and luminance components - 1 = centered, 2 = co-sited", - 'Type' => "Lookup", - 1 => "Chrominance components Centred in relation to luminance components", - 2 => "Chrominance and luminance components Co-Sited" ), - - -282 => array( 'Name' => "X Resolution", - 'Description' => "Number of columns (pixels) per \'ResolutionUnit\'", - 'Type' => "Numeric", - 'Units'=> "pixels per 'Resolution Unit' " ), - -283 => array( 'Name' => "Y Resolution", - 'Description' => "Number of rows (pixels) per \'ResolutionUnit\'", - 'Type' => "Numeric", - 'Units'=> "pixels per 'Resolution Unit' " ), - -296 => array( 'Name' => "Resolution Unit", - 'Description' => "Units for measuring XResolution and YResolution - 1 = No units, 2 = Inches, 3 = Centimetres", - 'Type' => "Lookup", - 2 => "Inches", - 3 => "Centimetres" ), - -273 => array( 'Name' => "Strip Offsets", - 'Type' => "Numeric", - 'Units'=> "bytes offset" ), - -278 => array( 'Name' => "Rows Per Strip", - 'Type' => "Numeric", - 'Units'=> "rows" ), - -279 => array( 'Name' => "Strip Byte Counts", - 'Type' => "Numeric", - 'Units'=> "bytes" ), - -513 => array( 'Name' => "Exif Thumbnail (JPEG Interchange Format)", - 'Type' => "Special" ), - -514 => array( 'Name' => "Exif Thumbnail Length (JPEG Interchange Format Length)", - 'Type' => "Numeric", - 'Units'=> "bytes" ), - -301 => array( 'Name' => "Transfer Function", - 'Type' => "Numeric", - 'Units'=> "" ), - -318 => array( 'Name' => "White Point Chromaticity", - 'Type' => "Numeric", - 'Units'=> "(x,y coordinates on a 1931 CIE xy chromaticity diagram)" ), - -319 => array( 'Name' => "Primary Chromaticities", - 'Type' => "Numeric", - 'Units'=> "(Red x,y, Green x,y, Blue x,y coordinates on a 1931 CIE xy chromaticity diagram)" ), - -529 => array( 'Name' => "YCbCr Coefficients", - 'Description' => "Transform Coefficients for transformation from RGB to YCbCr", - 'Type' => "Numeric", - 'Units'=> "(LumaRed, LumaGreen, LumaBlue [proportions of red, green, and blue in luminance])" ), - -532 => array( 'Name' => "Reference Black point and White point", - 'Type' => "Numeric", - 'Units'=> "(R or Y White Headroom, R or Y Black Footroom, G or Cb White Headroom, G or Cb Black Footroom, B or Cr White Headroom, B or Cr Black Footroom)" ), - -306 => array( 'Name' => "Date and Time", - 'Type' => "Numeric", - 'Units'=> " (Format: YYYY:MM:DD HH:mm:SS)" ), - -270 => array( 'Name' => "Image Description", - 'Type' => "String" ), - -271 => array( 'Name' => "Make (Manufacturer)", - 'Type' => "String" ), - -272 => array( 'Name' => "Model", - 'Type' => "String" ), - -305 => array( 'Name' => "Software or Firmware", - 'Type' => "String" ), - -315 => array( 'Name' => "Artist Name", - 'Type' => "String" ), - -700 => array( 'Name' => "Embedded XMP Block", // Change: Added embedded XMP as of version 1.11 - 'Type' => "XMP" ), - -33432 => array( 'Name' => "Copyright Information", - 'Type' => "String" ), - -34665 => array( 'Name' => "EXIF Image File Directory (IFD)", - 'Type' => "SubIFD", - 'Tags Name' => "EXIF" ), - -33723 => array( 'Name' => "IPTC Records", - 'Type' => "IPTC" ), - -34377 => array( 'Name' => "Embedded Photoshop IRB", // Change: Added embedded IRB as of version 1.11 - 'Type' => "IRB" ), - -34853 => array( 'Name' => "GPS Info Image File Directory (IFD)", // Change: Moved GPS IFD tag to correct location as of version 1.11 - 'Type' => "SubIFD", - 'Tags Name' => "GPS" ), - -50341 => array( 'Name' => "Print Image Matching Info", - 'Type' => "PIM" ), - -), - - -/*****************************************************************************/ -/* */ -/* EXIF Tags */ -/* */ -/*****************************************************************************/ - - -'EXIF' => array ( - -// Exif IFD -36864 => array( 'Name' => "Exif Version", - 'Type' => "String" ), - -40965 => array( 'Name' => "Interoperability Image File Directory (IFD)", - 'Type' => "SubIFD", - 'Tags Name' => "Interoperability" ), - -// Change: removed GPS IFD tag from here as it was incorrect location - as of version 1.11 - -40960 => array( 'Name' => "FlashPix Version", - 'Type' => "String" ), - -40961 => array( 'Name' => "Colour Space", - 'Type' => "Lookup", - 1 => "sRGB", - 0xFFFF => "Uncalibrated" ), - -40962 => array( 'Name' => "Pixel X Dimension", - 'Type' => "Numeric", - 'Units'=> "pixels" ), - -40963 => array( 'Name' => "Pixel Y Dimension", - 'Type' => "Numeric", - 'Units' => "pixels" ), - -37121 => array( 'Name' => "Components Configuration", - 'Type' => "Special" ), - -37122 => array( 'Name' => "Compressed Bits Per Pixel", - 'Type' => "Numeric", - 'Units' => "bits" ), - -37500 => array( 'Name' => "Maker Note", - 'Type' => "Maker Note" ), - -37510 => array( 'Name' => "User Comment", - 'Type' => "Character Coded String" ), - -40964 => array( 'Name' => "Related Sound File", - 'Type' => "String" ), - -36867 => array( 'Name' => "Date and Time of Original", - 'Type' => "String", - 'Units' => " (Format: YYYY:MM:DD HH:mm:SS)" ), - -36868 => array( 'Name' => "Date and Time when Digitized", - 'Type' => "String", - 'Units' => " (Format: YYYY:MM:DD HH:mm:SS)" ), - -37520 => array( 'Name' => "Sub Second Time", - 'Type' => "String" ), - -37521 => array( 'Name' => "Sub Second Time of Original", - 'Type' => "String" ), - -37522 => array( 'Name' => "Sub Second Time when Digitized", - 'Type' => "String" ), - -33434 => array( 'Name' => "Exposure Time", - 'Type' => "Numeric", - 'Units' => "seconds" ), - -37377 => array( 'Name' => "APEX Shutter Speed Value (Tv)", - 'Type' => "Numeric" ), - -37378 => array( 'Name' => "APEX Aperture Value (Av)", - 'Type' => "Numeric" ), - -37379 => array( 'Name' => "APEX Brightness Value (Bv)", - 'Type' => "Numeric" ), - -37380 => array( 'Name' => "APEX Exposure Bias Value (Exposure Compensation)", - 'Type' => "Numeric", - 'Units' => "EV" ), - -42240 => array( 'Name' => "Gamma Compensation for Playback", - 'Type' => "Numeric" ), - - -37381 => array( 'Name' => "APEX Maximum Aperture Value", - 'Type' => "Numeric" ), - -37382 => array( 'Name' => "Subject Distance", - 'Type' => "Numeric", - 'Units' => "metres" ), - -37383 => array( 'Name' => "Metering Mode", - 'Type' => "Lookup", - 0 => "Unknown", - 1 => "Average", - 2 => "Center Weighted Average", - 3 => "Spot", - 4 => "Multi Spot", - 5 => "Pattern", - 6 => "Partial", - 255 => "Other" ), - -37384 => array( 'Name' => "Light Source", - 'Type' => "Lookup", - 0 => "Unknown", - 1 => "Daylight", - 2 => "Fluorescent", - 3 => "Tungsten (incandescent light)", - 4 => "Flash", - 9 => "Fine weather", - 10 => "Cloudy weather", - 11 => "Shade", - 12 => "Daylight fluorescent (D 5700 – 7100K)", - 13 => "Day white fluorescent (N 4600 – 5400K)", - 14 => "Cool white fluorescent (W 3900 – 4500K)", - 15 => "White fluorescent (WW 3200 – 3700K)", - 17 => "Standard light A", - 18 => "Standard light B", - 19 => "Standard light C", - 20 => "D55", - 21 => "D65", - 22 => "D75", - 23 => "D50", - 24 => "ISO studio tungsten", - 255 => "Other" ), - -37385 => array( 'Name' => "Flash", - 'Type' => "Lookup", - 0 => "Flash did not fire", - 1 => "Flash fired", - 5 => "Strobe return light not detected", - 7 => "Strobe return light detected", - 9 => "Flash fired, compulsory flash mode", - 13 => "Flash fired, compulsory flash mode, return light not detected", - 15 => "Flash fired, compulsory flash mode, return light detected", - 16 => "Flash did not fire, compulsory flash suppression mode", - 24 => "Flash did not fire, auto mode", - 25 => "Flash fired, auto mode", - 29 => "Flash fired, auto mode, return light not detected", - 31 => "Flash fired, auto mode, return light detected", - 32 => "No flash function", - 65 => "Flash fired, red-eye reduction mode", - 69 => "Flash fired, red-eye reduction mode, return light not detected", - 71 => "Flash fired, red-eye reduction mode, return light detected", - 73 => "Flash fired, compulsory flash mode, red-eye reduction mode", - 77 => "Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected", - 79 => "Flash fired, compulsory flash mode, red-eye reduction mode, return light detected", - 89 => "Flash fired, auto mode, red-eye reduction mode", - 93 => "Flash fired, auto mode, return light not detected, red-eye reduction mode", - 95 => "Flash fired, auto mode, return light detected, red-eye reduction mode" ), - -37386 => array( 'Name' => "FocalLength", - 'Type' => "Numeric", - 'Units' => "mm" ), - -37396 => array( 'Name' => "Subject Area", - 'Type' => "Numeric", - 'Units' => "( Two Values: x,y coordinates, Three Values: x,y coordinates, diameter, Four Values: center x,y coordinates, width, height)" ), - -33437 => array( 'Name' => "Aperture F Number", - 'Type' => "Numeric" ), - -34850 => array( 'Name' => "Exposure Program", - 'Type' => "Lookup", - 0 => "Not defined", - 1 => "Manual", - 2 => "Normal program", - 3 => "Aperture priority", - 4 => "Shutter priority", - 5 => "Creative program (biased toward depth of field)", - 6 => "Action program (biased toward fast shutter speed)", - 7 => "Portrait mode (for closeup photos with the background out of focus)", - 8 => "Landscape mode (for landscape photos with the background in focus)" ), - -34852 => array( 'Name' => "Spectral Sensitivity", - 'Type' => "String" ), - -34855 => array( 'Name' => "ISO Speed Ratings", - 'Type' => "Numeric" ), - -34856 => array( 'Name' => "Opto-Electronic Conversion Function", - 'Type' => "Unknown" ), - -41483 => array( 'Name' => "Flash Energy", - 'Type' => "Numeric", - 'Units' => "Beam Candle Power Seconds (BCPS)" ), - -41484 => array( 'Name' => "Spatial Frequency Response", - 'Type' => "Unknown" ), - -41486 => array( 'Name' => "Focal Plane X Resolution", - 'Type' => "Numeric", - 'Units' => "pixels per 'Focal Plane Resolution Unit'" ), - -41487 => array( 'Name' => "Focal Plane Y Resolution", - 'Type' => "Numeric", - 'Units' => "pixels per 'Focal Plane Resolution Unit'" ), - -41488 => array( 'Name' => "Focal Plane Resolution Unit", - 'Type' => "Lookup", - 2 => "Inches", - 3 => "Centimetres" ), - -41492 => array( 'Name' => "Subject Location", - 'Type' => "Numeric", - 'Units' => "(x,y pixel coordinates of subject)" ), - -41493 => array( 'Name' => "Exposure Index", - 'Type' => "Numeric" ), - -41495 => array( 'Name' => "Sensing Method", - 'Type' => "Lookup", - 1 => "Not defined", - 2 => "One-chip colour area sensor", - 3 => "Two-chip colour area sensor", - 4 => "Three-chip colour area sensor", - 5 => "Colour sequential area sensor", - 7 => "Trilinear sensor", - 8 => "Colour sequential linear sensor" ), - -41728 => array( 'Name' => "File Source", - 'Type' => "Lookup", - 3 => "Digital Still Camera" ), - -41729 => array( 'Name' => "Scene Type", - 'Type' => "Lookup", - 1 => "A directly photographed image" ), - -41730 => array( 'Name' => "Colour Filter Array Pattern", - 'Type' => "Special" ), - -41985 => array( 'Name' => "Special Processing (Custom Rendered)", - 'Type' => "Lookup", - 0 => "Normal process", - 1 => "Custom process" ), - -41986 => array( 'Name' => "Exposure Mode", - 'Type' => "Lookup", - 0 => "Auto exposure", - 1 => "Manual exposure", - 2 => "Auto bracket" ), - -41987 => array( 'Name' => "White Balance", - 'Type' => "Lookup", - 0 => "Auto white balance", - 1 => "Manual white balance" ), - -41988 => array( 'Name' => "Digital Zoom Ratio", - 'Type' => "Numeric", - 'Units' => " ( Zero = Digital Zoom Not Used )" ), - -41989 => array( 'Name' => "Equivalent Focal Length In 35mm Film", - 'Type' => "Numeric", - 'Units' => "mm" ), - -41990 => array( 'Name' => "Scene Capture Type", - 'Type' => "Lookup", - 0 => "Standard", - 1 => "Landscape", - 2 => "Portrait", - 3 => "Night scene" ), - -41991 => array( 'Name' => "Gain Control", - 'Type' => "Lookup", - 0 => "None", - 1 => "Low gain up", - 2 => "High gain up", - 3 => "Low gain down", - 4 => "High gain down" ), - -41992 => array( 'Name' => "Contrast", - 'Type' => "Lookup", - 0 => "Normal", - 1 => "Soft", - 2 => "Hard" ), - -41993 => array( 'Name' => "Saturation", - 'Type' => "Lookup", - 0 => "Normal", - 1 => "Low saturation", - 2 => "High saturation" ), - -41994 => array( 'Name' => "Sharpness", - 'Type' => "Lookup", - 0 => "Normal", - 1 => "Soft", - 2 => "Hard" ), - -41995 => array( 'Name' => "Device Setting Description", - 'Type' => "Unknown" ), - -41996 => array( 'Name' => "Subject Distance Range", - 'Type' => "Lookup", - 0 => "Unknown", - 1 => "Macro", - 2 => "Close view", - 3 => "Distant view" ), - -42016 => array( 'Name' => "Image Unique ID", - 'Type' => "String" ), - - - -// 11 => "ACDComment", -// 255 => "NewSubfileType" - - -), - - - - -/*****************************************************************************/ -/* */ -/* Interoperability Tags */ -/* */ -/*****************************************************************************/ - -"Interoperability" => array( - -1 => array( 'Name' => "Interoperability Index", - 'Type' => "String" ), - -2 => array( 'Name' => "Interoperability Version", - 'Type' => "String" ), - -4096 => array( 'Name' => "Related Image File Format", - 'Type' => "String" ), - -4097 => array( 'Name' => "Related Image File Width", - 'Type' => "Numeric", - 'Units' => "pixels" ), - -4098 => array( 'Name' => "Related Image File Length", - 'Type' => "Numeric", - 'Units' => "pixels " ) - -), - - -/*****************************************************************************/ -/* */ -/* GPS Tags */ -/* */ -/*****************************************************************************/ - -"GPS" => array( - -0 => array( 'Name' => "GPS Tag Version", - 'Type' => "Numeric", - 'Units' => "(e.g.: 2.2.0.0 = Version 2.2 )" ), - -1 => array( 'Name' => "North or South Latitude", - 'Type' => "String" ), - -2 => array( 'Name' => "Latitude", - 'Type' => "Numeric", - 'Units' => "(Degrees Minutes Seconds North or South)" ), - -3 => array( 'Name' => "East or West Longitude", - 'Type' => "String" ), - -4 => array( 'Name' => "Longitude", - 'Type' => "Numeric", - 'Units' => "(Degrees Minutes Seconds East or West)" ), - -5 => array( 'Name' => "Altitude Reference", - 'Type' => "Lookup", - 0 => "Sea Level", - 1 => "Sea level reference (negative value)" ), - -6 => array( 'Name' => "Altitude", - 'Type' => "Numeric", - 'Units' => "Metres with respect to Altitude Reference" ), - -7 => array( 'Name' => "GPS Time (atomic clock)", - 'Type' => "Numeric", - 'Units' => "(Hours Minutes Seconds)" ), - -8 => array( 'Name' => "GPS Satellites used for Measurement", - 'Type' => "String" ), - -9 => array( 'Name' => "GPS Receiver Status", - 'Type' => "Lookup", - 'A' => "Measurement in progress", // Change: Fixed tag values as of version 1.11 - 'V' => "Measurement Interoperability" ), - -10 => array( 'Name' => "GPS Measurement Mode", - 'Type' => "Lookup", - 2 => "2-dimensional measurement", // Change: Fixed tag values as of version 1.11 - 3 => "3-dimensional measurement" ), - -11 => array( 'Name' => "Measurement Precision", - 'Type' => "Numeric", - 'Units' => "(Data Degree of Precision, Horizontal for 2D, Position for 3D)" ), - -12 => array( 'Name' => "Speed Unit", - 'Type' => "Lookup", - 'K' => "Kilometers per Hour", // Change: Fixed tag values as of version 1.11 - 'M' => "Miles per Hour", - 'N' => "Knots" ), - -13 => array( 'Name' => "Speed of GPS receiver", - 'Type' => "Numeric", - 'Units' => "Speed Units" ), - -14 => array( 'Name' => "Reference for direction of Movement", - 'Type' => "Lookup", // Change: Fixed tag values as of version 1.11 - 'T' => "True North", - 'M' => "Magnetic North" ), - -15 => array( 'Name' => "Direction of Movement", - 'Type' => "Numeric", - 'Units' => "Degrees relative to Movement Direction Reference" ), - -16 => array( 'Name' => "Reference for Direction of Image", - 'Type' => "Lookup", - 'T' => "True North", // Change: Fixed tag values as of version 1.11 - 'M' => "Magnetic North" ), - -17 => array( 'Name' => "Direction of Image", - 'Type' => "Numeric", - 'Units' => "Degrees relative to Image Direction Reference" ), - -18 => array( 'Name' => "Geodetic Survey Datum Used", - 'Type' => "String" ), - -19 => array( 'Name' => "Destination - North or South Latitude", - 'Type' => "String" ), - -20 => array( 'Name' => "Latitude of Destination", - 'Type' => "Numeric", - 'Units' => "(Degrees Minutes Seconds North or South)" ), - -21 => array( 'Name' => "Destination - East or West Longitude", - 'Type' => "String" ), - -22 => array( 'Name' => "Longitude of Destination", - 'Type' => "Numeric", - 'Units' => "(Degrees Minutes Seconds East or West)" ), - -23 => array( 'Name' => "Reference for Bearing of Destination", - 'Type' => "Lookup", - 'T' => "True North", // Change: Fixed tag values as of version 1.11 - 'M' => "Magnetic North" ), - -24 => array( 'Name' => "Bearing of Destination", - 'Type' => "Numeric", - 'Units' => "Degrees relative to Destination Bearing Reference" ), - -25 => array( 'Name' => "Units for Distance to Destination", - 'Type' => "Lookup", - 'K' => "Kilometres", // Change: Fixed tag values as of version 1.11 - 'M' => "Miles", - 'N' => "Nautical Miles" ), - -26 => array( 'Name' => "Distance to Destination", - 'Type' => "Numeric", - 'Units' => "Destination Distance Units" ), - -27 => array( 'Name' => "Name of GPS Processing Method", - 'Type' => "Character Coded String" ), - -28 => array( 'Name' => "Name of GPS Area", - 'Type' => "Character Coded String" ), - -29 => array( 'Name' => "GPS Date", - 'Type' => "Numeric", - 'Units'=> " (Format: YYYY:MM:DD HH:mm:SS)" ), - -30 => array( 'Name' => "GPS Differential Correction", - 'Type' => "Lookup", - 0 => "Measurement without differential correction", - 1 => "Differential correction applied" ), - -), - - - - - - - - - -/*****************************************************************************/ -/* */ -/* META (App3) Tags */ -/* */ -/*****************************************************************************/ - -"Meta" => array( - - -50000 => array( 'Name' => "CaptureDevice.FilmProductCode", - 'Type' => "Unknown" ), - -50001 => array( 'Name' => "DigitalProcess.ImageSourceEK", - 'Type' => "Unknown" ), - -50002 => array( 'Name' => "CaptureConditions.PAR", - 'Type' => "Unknown" ), - -50003 => array( 'Name' => "CaptureDevice.CameraOwner.EK", - 'Type' => "Character Coded String" ), - -50004 => array( 'Name' => "CaptureDevice.SerialNumber.Camera", - 'Type' => "Unknown" ), - -50005 => array( 'Name' => "SceneContent.GroupCaption.UserSelectGroupTitle", - 'Type' => "Unknown" ), - -50006 => array( 'Name' => "OutputOrder.Information.DealerIDNumber", - 'Type' => "Unknown" ), - -50007 => array( 'Name' => "CaptureDevice.FID", - 'Type' => "Unknown" ), - -50008 => array( 'Name' => "OutputOrder.Information.EnvelopeNumber", - 'Type' => "Unknown" ), - -50009 => array( 'Name' => "OutputOrder.SimpleRenderInst.FrameNumber", - 'Type' => "Unknown" ), - -50010 => array( 'Name' => "CaptureDevice.FilmCategory", - 'Type' => "Unknown" ), - -50011 => array( 'Name' => "CaptureDevice.FilmGencode", - 'Type' => "Unknown" ), - -50012 => array( 'Name' => "CaptureDevice.Scanner.ModelAndVersion", - 'Type' => "Unknown" ), - -50013 => array( 'Name' => "CaptureDevice.FilmSize", - 'Type' => "Unknown" ), - -50014 => array( 'Name' => "DigitalProcess.History.SBARGBShifts", - 'Type' => "Unknown" ), - -50015 => array( 'Name' => "DigitalProcess.History.SBAInputImageColourspace", - 'Type' => "Unknown" ), - -50016 => array( 'Name' => "DigitalProcess.History.SBAInputImageBitDepth", - 'Type' => "Unknown" ), - -50017 => array( 'Name' => "DigitalProcess.History.SBAExposureRecord", - 'Type' => "Unknown" ), - -50018 => array( 'Name' => "DigitalProcess.History.UserAdjSBARGBShifts", - 'Type' => "Unknown" ), - -50019 => array( 'Name' => "DigitalProcess.ImageRotationStatus", - 'Type' => "Unknown" ), - -50020 => array( 'Name' => "DigitalProcess.RollGuid.Elements", - 'Type' => "Unknown" ), - -50021 => array( 'Name' => "ImageContainer.MetadataNumber", - 'Type' => "String" ), - -50022 => array( 'Name' => "DigitalProcess.History.EditTagArray", - 'Type' => "Unknown" ), - -50023 => array( 'Name' => "CaptureConditions.Magnification", - 'Type' => "Unknown" ), - -50028 => array( 'Name' => "CaptureDevice.NativePhysicalXResolution", - 'Type' => "Unknown" ), - -50029 => array( 'Name' => "CaptureDevice.NativePhysicalYResolution", - 'Type' => "Unknown" ), - -50030 => array( 'Name' => "Kodak Special Effects IFD", - 'Type' => "SubIFD", - 'Tags Name' => "KodakSpecialEffects" ), - -50031 => array( 'Name' => "Kodak Borders IFD", - 'Type' => "SubIFD", - 'Tags Name' => "KodakBorders" ), - -50042 => array( 'Name' => "CaptureDevice.NativePhysicalResolutionUnit", - 'Type' => "Unknown" ), - -50200 => array( 'Name' => "ImageContainer.SourceImageDirectory", - 'Type' => "Unknown" ), - -50201 => array( 'Name' => "ImageContainer.SourceImageFileName", - 'Type' => "Unknown" ), - -50202 => array( 'Name' => "ImageContainer.SourceImageVolumeName", - 'Type' => "Unknown" ), - -50284 => array( 'Name' => "CaptureConditions.PrintQuantity", - 'Type' => "Unknown" ), - -50286 => array( 'Name' => "DigitalProcess.ImagePrintStatus", - 'Type' => "Unknown" ) - -), - - - -/*****************************************************************************/ -/* */ -/* Kodak Special Effects IFD Tags */ -/* */ -/*****************************************************************************/ - -"KodakSpecialEffects" => array( - -0 => array( 'Name' => "Digital Effects Version", - 'Type' => "Numeric" ), - -1 => array( 'Name' => "Digital Effects Name", - 'Type' => "Character Coded String" ), - -2 => array( 'Name' => "Digital Effects Type", - 'Type' => "Lookup", - 0 => "None Applied" ) - -), - -/*****************************************************************************/ -/* */ -/* Kodak Borders IFD Tags */ -/* */ -/*****************************************************************************/ - -"KodakBorders" => array( - -0 => array( 'Name' => "Borders Version", - 'Type' => "Numeric" ), - -1 => array( 'Name' => "Border Name", - 'Type' => "Character Coded String" ), - -2 => array( 'Name' => "Border ID", - 'Type' => "Numeric" ), - -3 => array( 'Name' => "Border Location", - 'Type' => "Lookup" ), - -4 => array( 'Name' => "Border Type", - 'Type' => "Lookup", - 0 => "None" ), - -8 => array( 'Name' => "Watermark Type", - 'Type' => "Lookup", - 0 => "None" ) - -), - -); - -/****************************************************************************** -* End of Global Variable: IFD_Tag_Definitions -******************************************************************************/ - -?> \ No newline at end of file diff --git a/jpeg_metadata_tk/Edit_File_Info.php b/jpeg_metadata_tk/Edit_File_Info.php deleted file mode 100644 index 7b19e70..0000000 --- a/jpeg_metadata_tk/Edit_File_Info.php +++ /dev/null @@ -1,575 +0,0 @@ - 1.11 : Changed displayed toolkit version numbers to reference Toolkit_Version.php -* -* URL: http://electronics.ozhiker.com -* -* Copyright: Copyright Evan Hunter 2004 -* -* License: This file is part of the PHP JPEG Metadata Toolkit. -* -* The PHP JPEG Metadata Toolkit is free software; you can -* redistribute it and/or modify it under the terms of the -* GNU General Public License as published by the Free Software -* Foundation; either version 2 of the License, or (at your -* option) any later version. -* -* The PHP JPEG Metadata Toolkit is distributed in the hope -* that it will be useful, but WITHOUT ANY WARRANTY; without -* even the implied warranty of MERCHANTABILITY or FITNESS -* FOR A PARTICULAR PURPOSE. See the GNU General Public License -* for more details. -* -* You should have received a copy of the GNU General Public -* License along with the PHP JPEG Metadata Toolkit; if not, -* write to the Free Software Foundation, Inc., 59 Temple -* Place, Suite 330, Boston, MA 02111-1307 USA -* -* If you require a different license for commercial or other -* purposes, please contact the author: evan@ozhiker.com -* -***************************************************************************/ - - - - require_once 'Toolkit_Version.php'; // Change: added as of version 1.11 - - // Check for operation modes 2 or 3 - // i.e. $filename is defined, and $new_ps_file_info_array is not - if ( ( ! isset( $new_ps_file_info_array ) ) && - ( isset( $filename ) ) && - ( is_string( $filename ) ) ) - { - // Hide any unknown EXIF tags - $GLOBALS['HIDE_UNKNOWN_TAGS'] = TRUE; - - // Accessing the existing file info for the specified file requires these includes - require_once 'JPEG.php'; - require_once 'XMP.php'; - require_once 'Photoshop_IRB.php'; - require_once 'EXIF.php'; - require_once 'Photoshop_File_Info.php'; - - // Retrieve the header information from the JPEG file - $jpeg_header_data = get_jpeg_header_data( $filename ); - - // Retrieve EXIF information from the JPEG file - $Exif_array = get_EXIF_JPEG( $filename ); - - // Retrieve XMP information from the JPEG file - $XMP_array = read_XMP_array_from_text( get_XMP_text( $jpeg_header_data ) ); - - // Retrieve Photoshop IRB information from the JPEG file - $IRB_array = get_Photoshop_IRB( $jpeg_header_data ); - - // Retrieve Photoshop File Info from the three previous arrays - $new_ps_file_info_array = get_photoshop_file_info( $Exif_array, $XMP_array, $IRB_array ); - - - - // Check if there is an array of defaults available - if ( ( isset( $default_ps_file_info_array) ) && - ( is_array( $default_ps_file_info_array) ) ) - { - // There are defaults defined - - // Check if there is a default for the date defined - if ( ( ! array_key_exists( 'date', $default_ps_file_info_array ) ) || - ( ( array_key_exists( 'date', $default_ps_file_info_array ) ) && - ( $default_ps_file_info_array['date'] == '' ) ) ) - { - // No default for the date defined - // figure out a default from the file - - // Check if there is a EXIF Tag 36867 "Date and Time of Original" - if ( ( $Exif_array != FALSE ) && - ( array_key_exists( 0, $Exif_array ) ) && - ( array_key_exists( 34665, $Exif_array[0] ) ) && - ( array_key_exists( 0, $Exif_array[0][34665] ) ) && - ( array_key_exists( 36867, $Exif_array[0][34665][0] ) ) ) - { - // Tag "Date and Time of Original" found - use it for the default date - $default_ps_file_info_array['date'] = $Exif_array[0][34665][0][36867]['Data'][0]; - $default_ps_file_info_array['date'] = preg_replace( "/(\d\d\d\d):(\d\d):(\d\d)( \d\d:\d\d:\d\d)/", "$1-$2-$3", $default_ps_file_info_array['date'] ); - } - // Check if there is a EXIF Tag 36868 "Date and Time when Digitized" - else if ( ( $Exif_array != FALSE ) && - ( array_key_exists( 0, $Exif_array ) ) && - ( array_key_exists( 34665, $Exif_array[0] ) ) && - ( array_key_exists( 0, $Exif_array[0][34665] ) ) && - ( array_key_exists( 36868, $Exif_array[0][34665][0] ) ) ) - { - // Tag "Date and Time when Digitized" found - use it for the default date - $default_ps_file_info_array['date'] = $Exif_array[0][34665][0][36868]['Data'][0]; - $default_ps_file_info_array['date'] = preg_replace( "/(\d\d\d\d):(\d\d):(\d\d)( \d\d:\d\d:\d\d)/", "$1-$2-$3", $default_ps_file_info_array['date'] ); - } - // Check if there is a EXIF Tag 306 "Date and Time" - else if ( ( $Exif_array != FALSE ) && - ( array_key_exists( 0, $Exif_array ) ) && - ( array_key_exists( 306, $Exif_array[0] ) ) ) - { - // Tag "Date and Time" found - use it for the default date - $default_ps_file_info_array['date'] = $Exif_array[0][306]['Data'][0]; - $default_ps_file_info_array['date'] = preg_replace( "/(\d\d\d\d):(\d\d):(\d\d)( \d\d:\d\d:\d\d)/", "$1-$2-$3", $default_ps_file_info_array['date'] ); - } - else - { - // Couldn't find an EXIF date in the image - // Set default date as creation date of file - $default_ps_file_info_array['date'] = date ("Y-m-d", filectime( $filename )); - } - } - - // Cycle through all the elements of the default values array - foreach( $default_ps_file_info_array as $def_key =>$default_item ) - { - // Check if the current element is Keywords or - // Supplemental Categories as these are arrays - // and need to be treated differently - if ( ( strcasecmp( $def_key, "keywords" ) == 0 ) || - ( strcasecmp( $def_key, "supplementalcategories" ) == 0 ) ) - { - // Keywords or Supplemental Categories found - // Check if the File Info from the file is empty for this element - // and if there are default values in this array element - if ( ( count( $new_ps_file_info_array[ $def_key ] ) == 0 ) && - ( is_array( $default_item ) ) && - ( count( $default_item ) >= 0 ) ) - { - // The existing file info is empty, and there are - // defaults - add them - $new_ps_file_info_array[ $def_key ] = $default_item; - } - } - // Otherwise, this is not an array element, just check if it is blank in the existing file info - else if ( trim( $new_ps_file_info_array[ $def_key ] ) == "" ) - { - // The existing file info is blank, add the default value - $new_ps_file_info_array[ $def_key ] = $default_item; - } - - } - } - } - // Check for operation mode 4 - $new_ps_file_info_array and $filename are not defined, - else if ( ( ( !isset($new_ps_file_info_array) ) || ( ! is_array($new_ps_file_info_array) ) ) && - ( ( !isset($filename) ) || ( ! is_string( $filename ) ) ) ) - { - // No filename or new_ps_file_info_array defined, create a blank file info array to display - $new_ps_file_info_array = array( - "title" => "", - "author" => "", - "authorsposition" => "", - "caption" => "", - "captionwriter" => "", - "jobname" => "", - "copyrightstatus" => "", - "copyrightnotice" => "", - "ownerurl" => "", - "keywords" => array(), - "category" => "", - "supplementalcategories" => array(), - "date" => "", - "city" => "", - "state" => "", - "country" => "", - "credit" => "", - "source" => "", - "headline" => "", - "instructions" => "", - "transmissionreference" => "", - "urgency" => "" ); - } - - - -/*************************************************************************** -* -* Now output the actual HTML form -* -***************************************************************************/ - -?> - - - - -
    - - - "; ?> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - Title - - "; - ?> -
    - Author - - "; - ?> -
    - Authors Position - - - Note: not used in Photoshop 7 or higher"; - ?> -
    - Description - - -
    - Description Writer - - "; - ?> -
    - Keywords - - -
    - Copyright Status - - -
    - Copyright Notice - - -
    - Copyright Info URL - - \n"; - if ($new_ps_file_info_array[ 'ownerurl' ] != "" ) - { - echo " (". $new_ps_file_info_array[ 'ownerurl' ] .")\n"; - } - ?> - -
    - Category - - \n"; - ?> - -
    - Supplemental Categories - - -
    - Date Created - - "; - ?> - (Note date must be YYYY-MM-DD format) -
    - City - - "; - ?> -
    - State - - "; - ?> -
    - Country - - "; - ?> -
    - Credit - - "; - ?> -
    - Source - - "; - ?> -
    - Headline - - -
    - Instructions - - -
    - Transmission Reference - - -
    - Job Name - - - Note: not used in Photoshop CS"; - ?> -
    - Urgency - - -
    -
    - - - -
    - -
    -
    -

    Powered by: PHP JPEG Metadata Toolkit version , Copyright (C) 2004 Evan Hunter

    -
    -
    diff --git a/jpeg_metadata_tk/IPTC.php b/jpeg_metadata_tk/IPTC.php deleted file mode 100644 index 4499c67..0000000 --- a/jpeg_metadata_tk/IPTC.php +++ /dev/null @@ -1,691 +0,0 @@ - 1.01 : changed get_IPTC to return partial data when error occurs -* 1.01 -> 1.10 : changed put_IPTC to check if the incoming IPTC block is valid -* changed Interpret_IPTC_to_HTML, adding nl2br functions for each text field, -* so that multiline text displays properly -* -* URL: http://electronics.ozhiker.com -* -* Copyright: Copyright Evan Hunter 2004 -* -* License: This file is part of the PHP JPEG Metadata Toolkit. -* -* The PHP JPEG Metadata Toolkit is free software; you can -* redistribute it and/or modify it under the terms of the -* GNU General Public License as published by the Free Software -* Foundation; either version 2 of the License, or (at your -* option) any later version. -* -* The PHP JPEG Metadata Toolkit is distributed in the hope -* that it will be useful, but WITHOUT ANY WARRANTY; without -* even the implied warranty of MERCHANTABILITY or FITNESS -* FOR A PARTICULAR PURPOSE. See the GNU General Public License -* for more details. -* -* You should have received a copy of the GNU General Public -* License along with the PHP JPEG Metadata Toolkit; if not, -* write to the Free Software Foundation, Inc., 59 Temple -* Place, Suite 330, Boston, MA 02111-1307 USA -* -* If you require a different license for commercial or other -* purposes, please contact the author: evan@ozhiker.com -* -******************************************************************************/ - - -// TODO: Not all of the IPTC fields have been tested properly - -/****************************************************************************** -* -* Function: get_IPTC -* -* Description: Extracts IPTC-NAA IIM data from the string provided, and returns -* the information as an array -* -* Parameters: Data_Str - the string containing the IPTC-NAA IIM records. Must -* be exact length of the IPTC-NAA IIM data. -* -* Returns: OutputArray - Array of IPTC-NAA IIM records -* FALSE - If an error occured in decoding -* -******************************************************************************/ - -function get_IPTC( $Data_Str ) -{ - - // Initialise the start position - $pos = 0; - // Create the array to receive the data - $OutputArray = array( ); - - // Cycle through the IPTC records, decoding and storing them - while( $pos < strlen($Data_Str) ) - { - // TODO - Extended Dataset record not supported - - // Check if there is sufficient data for reading the record - if ( strlen( substr($Data_Str,$pos) ) < 5 ) - { - // Not enough data left for a record - Probably corrupt data - ERROR - // Change: changed to return partial data as of revision 1.01 - return $OutputArray; - } - - // Unpack data from IPTC record: - // First byte - IPTC Tag Marker - always 28 - // Second byte - IPTC Record Number - // Third byte - IPTC Dataset Number - // Fourth and fifth bytes - two byte size value - $iptc_raw = unpack( "CIPTC_Tag_Marker/CIPTC_Record_No/CIPTC_Dataset_No/nIPTC_Size", substr($Data_Str,$pos) ); - - // Skip position over the unpacked data - $pos += 5; - - // Construct the IPTC type string eg 2:105 - $iptctype = sprintf( "%01d:%02d", $iptc_raw['IPTC_Record_No'], $iptc_raw['IPTC_Dataset_No']); - - // Check if there is sufficient data for reading the record contents - if ( strlen( substr( $Data_Str, $pos, $iptc_raw['IPTC_Size'] ) ) !== $iptc_raw['IPTC_Size'] ) - { - // Not enough data left for the record content - Probably corrupt data - ERROR - // Change: changed to return partial data as of revision 1.01 - return $OutputArray; - } - - // Add the IPTC record to the output array - $OutputArray[] = array( "IPTC_Type" => $iptctype , - "RecName" => (isset( $GLOBALS["IPTC_Entry_Names"][$iptctype] ) ? $GLOBALS["IPTC_Entry_Names"][$iptctype] : ''), - "RecDesc" => (isset( $GLOBALS["IPTC_Entry_Descriptions"][$iptctype] ) ? $GLOBALS[ "IPTC_Entry_Descriptions" ][ $iptctype ] : ''), - "RecData" => substr( $Data_Str, $pos, $iptc_raw['IPTC_Size'] ) ); - - // Skip over the IPTC record data - $pos += $iptc_raw['IPTC_Size']; - } - return $OutputArray; - -} - - -/****************************************************************************** -* End of Function: get_IPTC -******************************************************************************/ - - - - -/****************************************************************************** -* -* Function: put_IPTC -* -* Description: Encodes an array of IPTC-NAA records into a string encoded -* as IPTC-NAA IIM. (The reverse of get_IPTC) -* -* Parameters: new_IPTC_block - the IPTC-NAA array to be encoded. Should be -* the same format as that received from get_IPTC -* -* Returns: iptc_packed_data - IPTC-NAA IIM encoded string -* -******************************************************************************/ - - -function put_IPTC( $new_IPTC_block ) -{ - // Check if the incoming IPTC block is valid - if ( $new_IPTC_block == FALSE ) - { - // Invalid IPTC block - abort - return FALSE; - } - // Initialise the packed output data string - $iptc_packed_data = ""; - - // Cycle through each record in the new IPTC block - foreach ($new_IPTC_block as $record) - { - // Extract the Record Number and Dataset Number from the IPTC_Type field - list($IPTC_Record, $IPTC_Dataset) = sscanf( $record['IPTC_Type'], "%d:%d"); - - // Write the IPTC-NAA IIM Tag Marker, Record Number, Dataset Number and Data Size to the packed output data string - $iptc_packed_data .= pack( "CCCn", 28, $IPTC_Record, $IPTC_Dataset, strlen($record['RecData']) ); - - // Write the IPTC-NAA IIM Data to the packed output data string - $iptc_packed_data .= $record['RecData']; - } - - // Return the IPTC-NAA IIM data - return $iptc_packed_data; -} - -/****************************************************************************** -* End of Function: put_IPTC -******************************************************************************/ - - - -/****************************************************************************** -* -* Function: Interpret_IPTC_to_HTML -* -* Description: Generates html detailing the contents a IPTC-NAA IIM array -* which was retrieved with the get_IPTC function -* -* Parameters: IPTC_info - the IPTC-NAA IIM array,as read from get_IPTC -* -* Returns: OutputStr - A string containing the HTML -* -******************************************************************************/ - -function Interpret_IPTC_to_HTML( $IPTC_info ) -{ - // Create a string to receive the HTML - $output_str =""; - - // Check if the IPTC - if ( $IPTC_info !== FALSE ) - { - - - // Add Heading to HTML - $output_str .= "

    IPTC-NAA Record

    \n"; - - // Add Table to HTML - $output_str .= "\n\n"; - - // Cycle through each of the IPTC-NAA IIM records - foreach( $IPTC_info as $IPTC_Record ) - { - // Check if the record is a known IPTC field - $Record_Name = $IPTC_Record['RecName']; - if ( $Record_Name == "" ) - { - // Record is an unknown field - add message to HTML - $output_str .= "\n"; - } - else - { - // Record is a recognised IPTC field - Process it accordingly - - switch ( $IPTC_Record['IPTC_Type'] ) - { - case "1:00": // Envelope Record:Model Version - case "1:22": // Envelope Record:File Format Version - $output_str .= "\n"; - break; - - case "1:90": // Envelope Record:Coded Character Set - $output_str .= "\n"; - break; - // TODO: Implement decoding of IPTC record 1:90 - - case "1:20": // Envelope Record:File Format - - $formatno = hexdec( bin2hex( $IPTC_Record['RecData'] ) ); - - // Lookup file format from lookup-table - if ( array_key_exists( $formatno, $GLOBALS[ "IPTC_File Formats" ] ) ) - { - // Entry was found in lookup table - add it to HTML - $output_str .= "\n"; - } - else - { - // No matching entry was found in lookup table - add message to html - $output_str .= "\n"; - } - break; - - - case "2:00": // Application Record:Record Version - $output_str .= "\n"; - break; - - case "2:42": // Application Record: Action Advised - - // Looup Action - if ( $IPTC_Record['RecData'] == "01" ) - { - $output_str .= "\n"; - } - elseif ( $IPTC_Record['RecData'] == "02" ) - { - $output_str .= "\n"; - } - elseif ( $IPTC_Record['RecData'] == "03" ) - { - $output_str .= "\n"; - } - elseif ( $IPTC_Record['RecData'] == "04" ) - { - $output_str .= "\n"; - } - else - { - // Unknown Action - $output_str .= "\n"; - } - break; - - case "2:08": // Application Record:Editorial Update - if ( $IPTC_Record['RecData'] == "01" ) - { - // Additional Language - $output_str .= "\n"; - } - else - { - // Unknown Value - $output_str .= "\n"; - } - break; - - case "2:30": // Application Record:Release Date - case "2:37": // Application Record:Expiration Date - case "2:47": // Application Record:Reference Date - case "2:55": // Application Record:Date Created - case "2:62": // Application Record:Digital Creation Date - case "1:70": // Envelope Record:Date Sent - $date_array = unpack( "a4Year/a2Month/A2Day", $IPTC_Record['RecData'] ); - $output_str .= "\n"; - break; - - case "2:35": // Application Record:Release Time - case "2:38": // Application Record:Expiration Time - case "2:60": // Application Record:Time Created - case "2:63": // Application Record:Digital Creation Time - case "1:80": // Envelope Record:Time Sent - $time_array = unpack( "a2Hour/a2Minute/A2Second/APlusMinus/A4Timezone", $IPTC_Record['RecData'] ); - $output_str .= "\n"; - break; - - case "2:75": // Application Record:Object Cycle - // Lookup Value - if ( $IPTC_Record['RecData'] == "a" ) - { - $output_str .= "\n"; - } - elseif ( $IPTC_Record['RecData'] == "p" ) - { - $output_str .= "\n"; - } - elseif ( $IPTC_Record['RecData'] == "b" ) - { - $output_str .= "\n"; - } - else - { - // Unknown Value - $output_str .= "\n"; - } - break; - - case "2:125": // Application Record:Rasterised Caption - $output_str .= "\n"; - break; - // TODO: Display Rasterised Caption for IPTC record 2:125 - - case "2:130": // Application Record:Image Type - // Lookup Number of Components - if ( $IPTC_Record['RecData']{0} == "0" ) - { - $output_str .= "\n"; - break; - - case "2:131": // Application Record:Image Orientation - // Lookup value - if ( $IPTC_Record['RecData'] == "L" ) - { - $output_str .= "\n"; - } - elseif ( $IPTC_Record['RecData'] == "P" ) - { - $output_str .= "\n"; - } - elseif ( $IPTC_Record['RecData'] == "S" ) - { - $output_str .= "\n"; - } - else - { - // Unknown Orientation Value - $output_str .= "\n"; - } - break; - - default: // All other records - $output_str .= "\n"; - break; - } - } - } - - // Add Table End to HTML - $output_str .= "
    Unknown IPTC field '". htmlentities( $IPTC_Record['IPTC_Type'] ). "' :" . nl2br( HTML_UTF8_Escape( $IPTC_Record['RecData'] ) ) ."
    $Record_Name" . hexdec( bin2hex( $IPTC_Record['RecData'] ) ) ."
    $Record_NameDecoding not yet implemented
    \n (Hex Data: " . bin2hex( $IPTC_Record['RecData'] ) .")
    File Format". $GLOBALS[ "IPTC_File Formats" ][$formatno] . "
    File FormatUnknown File Format ($formatno)
    IPTC Version" . hexdec( bin2hex( $IPTC_Record['RecData'] ) ) ."
    $Record_NameKill
    $Record_NameReplace
    $Record_NameAppend
    $Record_NameReference
    $Record_NameUnknown : " . nl2br( HTML_UTF8_Escape( $IPTC_Record['RecData'] ) ) ."
    $Record_NameAdditional language
    $Record_NameUnknown : " . nl2br( HTML_UTF8_Escape( $IPTC_Record['RecData'] ) ) ."
    $Record_Name" . nl2br( HTML_UTF8_Escape( $date_array['Day'] . "/" . $date_array['Month'] . "/" . $date_array['Year'] ) ) ."
    $Record_Name" . nl2br( HTML_UTF8_Escape( $time_array['Hour'] . ":" . $time_array['Minute'] . ":" . $time_array['Second'] . " ". $time_array['PlusMinus'] . $time_array['Timezone'] ) ) ."
    $Record_NameMorning
    $Record_NameEvening
    $Record_NameBoth Morning and Evening
    $Record_NameUnknown : " . nl2br( HTML_UTF8_Escape( $IPTC_Record['RecData'] ) ) ."
    $Record_Name460x128 pixel black and white caption image
    $Record_NameNo Objectdata"; - } - elseif ( $IPTC_Record['RecData']{0} == "9" ) - { - $output_str .= "
    $Record_NameSupplemental objects related to other objectdata"; - } - else - { - $output_str .= "
    $Record_NameNumber of Colour Components : " . nl2br( HTML_UTF8_Escape( $IPTC_Record['RecData']{0} ) ); - } - - // Lookup current objectdata colour - if ( $GLOBALS['ImageType_Names'][ $IPTC_Record['RecData']{1} ] == "" ) - { - $output_str .= ", Unknown : " . nl2br( HTML_UTF8_Escape( $IPTC_Record['RecData']{1} ) ); - } - else - { - $output_str .= ", " . nl2br( HTML_UTF8_Escape( $GLOBALS['ImageType_Names'][ $IPTC_Record['RecData']{1} ] ) ); - } - $output_str .= "
    $Record_NameLandscape
    $Record_NamePortrait
    $Record_NameSquare
    $Record_NameUnknown : " . nl2br( HTML_UTF8_Escape( $IPTC_Record['RecData'] ) ) ."
    $Record_Name" .nl2br( HTML_UTF8_Escape( $IPTC_Record['RecData'] ) ) ."

    \n"; - } - - // Return HTML - return $output_str; -} - - -/****************************************************************************** -* End of Function: Interpret_IPTC_to_HTML -******************************************************************************/ - - - -/****************************************************************************** -* Global Variable: IPTC_Entry_Names -* -* Contents: The names of the IPTC-NAA IIM fields -* -******************************************************************************/ - -$GLOBALS[ "IPTC_Entry_Names" ] = array( -// Envelope Record -"1:00" => "Model Version", -"1:05" => "Destination", -"1:20" => "File Format", -"1:22" => "File Format Version", -"1:30" => "Service Identifier", -"1:40" => "Envelope Number", -"1:50" => "Product ID", -"1:60" => "Envelope Priority", -"1:70" => "Date Sent", -"1:80" => "Time Sent", -"1:90" => "Coded Character Set", -"1:100" => "UNO (Unique Name of Object)", -"1:120" => "ARM Identifier", -"1:122" => "ARM Version", - -// Application Record -"2:00" => "Record Version", -"2:03" => "Object Type Reference", -"2:05" => "Object Name (Title)", -"2:07" => "Edit Status", -"2:08" => "Editorial Update", -"2:10" => "Urgency", -"2:12" => "Subject Reference", -"2:15" => "Category", -"2:20" => "Supplemental Category", -"2:22" => "Fixture Identifier", -"2:25" => "Keywords", -"2:26" => "Content Location Code", -"2:27" => "Content Location Name", -"2:30" => "Release Date", -"2:35" => "Release Time", -"2:37" => "Expiration Date", -"2:35" => "Expiration Time", -"2:40" => "Special Instructions", -"2:42" => "Action Advised", -"2:45" => "Reference Service", -"2:47" => "Reference Date", -"2:50" => "Reference Number", -"2:55" => "Date Created", -"2:60" => "Time Created", -"2:62" => "Digital Creation Date", -"2:63" => "Digital Creation Time", -"2:65" => "Originating Program", -"2:70" => "Program Version", -"2:75" => "Object Cycle", -"2:80" => "By-Line (Author)", -"2:85" => "By-Line Title (Author Position) [Not used in Photoshop 7]", -"2:90" => "City", -"2:92" => "Sub-Location", -"2:95" => "Province/State", -"2:100" => "Country/Primary Location Code", -"2:101" => "Country/Primary Location Name", -"2:103" => "Original Transmission Reference", -"2:105" => "Headline", -"2:110" => "Credit", -"2:115" => "Source", -"2:116" => "Copyright Notice", -"2:118" => "Contact", -"2:120" => "Caption/Abstract", -"2:122" => "Caption Writer/Editor", -"2:125" => "Rasterized Caption", -"2:130" => "Image Type", -"2:131" => "Image Orientation", -"2:135" => "Language Identifier", -"2:150" => "Audio Type", -"2:151" => "Audio Sampling Rate", -"2:152" => "Audio Sampling Resolution", -"2:153" => "Audio Duration", -"2:154" => "Audio Outcue", -"2:200" => "ObjectData Preview File Format", -"2:201" => "ObjectData Preview File Format Version", -"2:202" => "ObjectData Preview Data", - -// Pre-ObjectData Descriptor Record -"7:10" => "Size Mode", -"7:20" => "Max Subfile Size", -"7:90" => "ObjectData Size Announced", -"7:95" => "Maximum ObjectData Size", - -// ObjectData Record -"8:10" => "Subfile", - -// Post ObjectData Descriptor Record -"9:10" => "Confirmed ObjectData Size" - -); - -/****************************************************************************** -* End of Global Variable: IPTC_Entry_Names -******************************************************************************/ - - - - - -/****************************************************************************** -* Global Variable: IPTC_Entry_Descriptions -* -* Contents: The Descriptions of the IPTC-NAA IIM fields -* -******************************************************************************/ - -$GLOBALS[ "IPTC_Entry_Descriptions" ] = array( -// Envelope Record -"1:00" => "2 byte binary version number", -"1:05" => "Max 1024 characters of Destination", -"1:20" => "2 byte binary file format number, see IPTC-NAA V4 Appendix A", -"1:22" => "Binary version number of file format", -"1:30" => "Max 10 characters of Service Identifier", -"1:40" => "8 Character Envelope Number", -"1:50" => "Product ID - Max 32 characters", -"1:60" => "Envelope Priority - 1 numeric characters", -"1:70" => "Date Sent - 8 numeric characters CCYYMMDD", -"1:80" => "Time Sent - 11 characters HHMMSS±HHMM", -"1:90" => "Coded Character Set - Max 32 characters", -"1:100" => "UNO (Unique Name of Object) - 14 to 80 characters", -"1:120" => "ARM Identifier - 2 byte binary number", -"1:122" => "ARM Version - 2 byte binary number", - -// Application Record -"2:00" => "Record Version - 2 byte binary number", -"2:03" => "Object Type Reference - 3 plus 0 to 64 Characters", -"2:05" => "Object Name (Title) - Max 64 characters", -"2:07" => "Edit Status - Max 64 characters", -"2:08" => "Editorial Update - 2 numeric characters", -"2:10" => "Urgency - 1 numeric character", -"2:12" => "Subject Reference - 13 to 236 characters", -"2:15" => "Category - Max 3 characters", -"2:20" => "Supplemental Category - Max 32 characters", -"2:22" => "Fixture Identifier - Max 32 characters", -"2:25" => "Keywords - Max 64 characters", -"2:26" => "Content Location Code - 3 characters", -"2:27" => "Content Location Name - Max 64 characters", -"2:30" => "Release Date - 8 numeric characters CCYYMMDD", -"2:35" => "Release Time - 11 characters HHMMSS±HHMM", -"2:37" => "Expiration Date - 8 numeric characters CCYYMMDD", -"2:35" => "Expiration Time - 11 characters HHMMSS±HHMM", -"2:40" => "Special Instructions - Max 256 Characters", -"2:42" => "Action Advised - 2 numeric characters", -"2:45" => "Reference Service - Max 10 characters", -"2:47" => "Reference Date - 8 numeric characters CCYYMMDD", -"2:50" => "Reference Number - 8 characters", -"2:55" => "Date Created - 8 numeric characters CCYYMMDD", -"2:60" => "Time Created - 11 characters HHMMSS±HHMM", -"2:62" => "Digital Creation Date - 8 numeric characters CCYYMMDD", -"2:63" => "Digital Creation Time - 11 characters HHMMSS±HHMM", -"2:65" => "Originating Program - Max 32 characters", -"2:70" => "Program Version - Max 10 characters", -"2:75" => "Object Cycle - 1 character", -"2:80" => "By-Line (Author) - Max 32 Characters", -"2:85" => "By-Line Title (Author Position) - Max 32 characters", -"2:90" => "City - Max 32 Characters", -"2:92" => "Sub-Location - Max 32 characters", -"2:95" => "Province/State - Max 32 Characters", -"2:100" => "Country/Primary Location Code - 3 alphabetic characters", -"2:101" => "Country/Primary Location Name - Max 64 characters", -"2:103" => "Original Transmission Reference - Max 32 characters", -"2:105" => "Headline - Max 256 Characters", -"2:110" => "Credit - Max 32 Characters", -"2:115" => "Source - Max 32 Characters", -"2:116" => "Copyright Notice - Max 128 Characters", -"2:118" => "Contact - Max 128 characters", -"2:120" => "Caption/Abstract - Max 2000 Characters", -"2:122" => "Caption Writer/Editor - Max 32 Characters", -"2:125" => "Rasterized Caption - 7360 bytes, 1 bit per pixel, 460x128pixel image", -"2:130" => "Image Type - 2 characters", -"2:131" => "Image Orientation - 1 alphabetic character", -"2:135" => "Language Identifier - 2 or 3 aphabetic characters", -"2:150" => "Audio Type - 2 characters", -"2:151" => "Audio Sampling Rate - 6 numeric characters", -"2:152" => "Audio Sampling Resolution - 2 numeric characters", -"2:153" => "Audio Duration - 6 numeric characters", -"2:154" => "Audio Outcue - Max 64 characters", -"2:200" => "ObjectData Preview File Format - 2 byte binary number", -"2:201" => "ObjectData Preview File Format Version - 2 byte binary number", -"2:202" => "ObjectData Preview Data - Max 256000 binary bytes", - -// Pre-ObjectData Descriptor Record -"7:10" => "Size Mode - 1 numeric character", -"7:20" => "Max Subfile Size", -"7:90" => "ObjectData Size Announced", -"7:95" => "Maximum ObjectData Size", - -// ObjectData Record -"8:10" => "Subfile", - -// Post ObjectData Descriptor Record -"9:10" => "Confirmed ObjectData Size" - -); - -/****************************************************************************** -* End of Global Variable: IPTC_Entry_Descriptions -******************************************************************************/ - - - - -/****************************************************************************** -* Global Variable: IPTC_File Formats -* -* Contents: The names of the IPTC-NAA IIM File Formats for field 1:20 -* -******************************************************************************/ - -$GLOBALS[ "IPTC_File Formats" ] = array( -00 => "No ObjectData", -01 => "IPTC-NAA Digital Newsphoto Parameter Record", -02 => "IPTC7901 Recommended Message Format", -03 => "Tagged Image File Format (Adobe/Aldus Image data)", -04 => "Illustrator (Adobe Graphics data)", -05 => "AppleSingle (Apple Computer Inc)", -06 => "NAA 89-3 (ANPA 1312)", -07 => "MacBinary II", -08 => "IPTC Unstructured Character Oriented File Format (UCOFF)", -09 => "United Press International ANPA 1312 variant", -10 => "United Press International Down-Load Message", -11 => "JPEG File Interchange (JFIF)", -12 => "Photo-CD Image-Pac (Eastman Kodak)", -13 => "Microsoft Bit Mapped Graphics File [*.BMP]", -14 => "Digital Audio File [*.WAV] (Microsoft & Creative Labs)", -15 => "Audio plus Moving Video [*.AVI] (Microsoft)", -16 => "PC DOS/Windows Executable Files [*.COM][*.EXE]", -17 => "Compressed Binary File [*.ZIP] (PKWare Inc)", -18 => "Audio Interchange File Format AIFF (Apple Computer Inc)", -19 => "RIFF Wave (Microsoft Corporation)", -20 => "Freehand (Macromedia/Aldus)", -21 => "Hypertext Markup Language - HTML (The Internet Society)", -22 => "MPEG 2 Audio Layer 2 (Musicom), ISO/IEC", -23 => "MPEG 2 Audio Layer 3, ISO/IEC", -24 => "Portable Document File (*.PDF) Adobe", -25 => "News Industry Text Format (NITF)", -26 => "Tape Archive (*.TAR)", -27 => "Tidningarnas Telegrambyrå NITF version (TTNITF DTD)", -28 => "Ritzaus Bureau NITF version (RBNITF DTD)", -29 => "Corel Draw [*.CDR]" -); - - -/****************************************************************************** -* End of Global Variable: IPTC_File Formats -******************************************************************************/ - -/****************************************************************************** -* Global Variable: ImageType_Names -* -* Contents: The names of the colour components for IPTC-NAA IIM field 2:130 -* -******************************************************************************/ - -$GLOBALS['ImageType_Names'] = array( "M" => "Monochrome", - "Y" => "Yellow Component", - "M" => "Magenta Component", - "C" => "Cyan Component", - "K" => "Black Component", - "R" => "Red Component", - "G" => "Green Component", - "B" => "Blue Component", - "T" => "Text Only", - "F" => "Full colour composite, frame sequential", - "L" => "Full colour composite, line sequential", - "P" => "Full colour composite, pixel sequential", - "S" => "Full colour composite, special interleaving" ); - - - -/****************************************************************************** -* End of Global Variable: ImageType_Names -******************************************************************************/ - -?> diff --git a/jpeg_metadata_tk/JFIF.php b/jpeg_metadata_tk/JFIF.php deleted file mode 100644 index 90f314f..0000000 --- a/jpeg_metadata_tk/JFIF.php +++ /dev/null @@ -1,438 +0,0 @@ - 1.11 : changed Interpret_JFXX_to_HTML to allow thumbnail links to work when -* toolkit is portable across directories -* -* URL: http://electronics.ozhiker.com -* -* Copyright: Copyright Evan Hunter 2004 -* -* License: This file is part of the PHP JPEG Metadata Toolkit. -* -* The PHP JPEG Metadata Toolkit is free software; you can -* redistribute it and/or modify it under the terms of the -* GNU General Public License as published by the Free Software -* Foundation; either version 2 of the License, or (at your -* option) any later version. -* -* The PHP JPEG Metadata Toolkit is distributed in the hope -* that it will be useful, but WITHOUT ANY WARRANTY; without -* even the implied warranty of MERCHANTABILITY or FITNESS -* FOR A PARTICULAR PURPOSE. See the GNU General Public License -* for more details. -* -* You should have received a copy of the GNU General Public -* License along with the PHP JPEG Metadata Toolkit; if not, -* write to the Free Software Foundation, Inc., 59 Temple -* Place, Suite 330, Boston, MA 02111-1307 USA -* -* If you require a different license for commercial or other -* purposes, please contact the author: evan@ozhiker.com -* -******************************************************************************/ - -include_once 'pjmt_utils.php'; // Change: as of version 1.11 - added to allow directory portability - -/****************************************************************************** -* -* Function: get_JFIF -* -* Description: Retrieves information from a JPEG File Interchange Format (JFIF) -* segment and returns it in an array. Uses information supplied by -* the get_jpeg_header_data function -* -* Parameters: jpeg_header_data - a JPEG header data array in the same format -* as from get_jpeg_header_data -* -* Returns: JFIF_data - an array of JFIF data -* FALSE - if a JFIF segment could not be found -* -******************************************************************************/ - -function get_JFIF( $jpeg_header_data ) -{ - //Cycle through the header segments - for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) - { - // If we find an APP0 header, - if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP0" ) == 0 ) - { - // And if it has the JFIF label, - if( strncmp ( $jpeg_header_data[$i]['SegData'], "JFIF\x00", 5) == 0 ) - { - // Found a JPEG File Interchange Format (JFIF) Block - - // unpack the JFIF data from the incoming string - // First is the JFIF label string - // Then a two byte version number - // Then a byte, units identifier, ( 0 = aspect ration, 1 = dpi, 2 = dpcm) - // Then a two byte int X-Axis pixel Density (resolution) - // Then a two byte int Y-Axis pixel Density (resolution) - // Then a byte X-Axis JFIF thumbnail size - // Then a byte Y-Axis JFIF thumbnail size - // Then the uncompressed RGB JFIF thumbnail data - - $JFIF_data = unpack( 'a5JFIF/C2Version/CUnits/nXDensity/nYDensity/CThumbX/CThumbY/a*ThumbData', $jpeg_header_data[$i]['SegData'] ); - - return $JFIF_data; - } - } - } - return FALSE; -} - -/****************************************************************************** -* End of Function: get_JFIF -******************************************************************************/ - - - - -/****************************************************************************** -* -* Function: put_JFIF -* -* Description: Creates a new JFIF segment from an array of JFIF data in the -* same format as would be retrieved from get_JFIF, and inserts -* this segment into the supplied JPEG header array -* -* Parameters: jpeg_header_data - a JPEG header data array in the same format -* as from get_jpeg_header_data, into which the -* new JFIF segment will be put -* new_JFIF_array - a JFIF information array in the same format as -* from get_JFIF, to create the new segment -* -* Returns: jpeg_header_data - the JPEG header data array with the new -* JFIF segment added -* -******************************************************************************/ - -function put_JFIF( $jpeg_header_data, $new_JFIF_array ) -{ - // pack the JFIF data into its proper format for a JPEG file - $packed_data = pack( 'a5CCCnnCCa*',"JFIF\x00", $new_JFIF_array['Version1'], $new_JFIF_array['Version2'], $new_JFIF_array['Units'], $new_JFIF_array['XDensity'], $new_JFIF_array['YDensity'], $new_JFIF_array['ThumbX'], $new_JFIF_array['ThumbY'], $new_JFIF_array['ThumbData'] ); - - //Cycle through the header segments - for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) - { - // If we find an APP0 header, - if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP0" ) == 0 ) - { - // And if it has the JFIF label, - if( strncmp ( $jpeg_header_data[$i]['SegData'], "JFIF\x00", 5) == 0 ) - { - // Found a preexisting JFIF block - Replace it with the new one and return. - $jpeg_header_data[$i]['SegData'] = $packed_data; - return $jpeg_header_data; - } - } - } - - // No preexisting JFIF block found, insert a new one at the start of the header data. - array_splice($jpeg_header_data, 0 , 0, array( array( "SegType" => 0xE0, - "SegName" => "APP0", - "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE0 ], - "SegData" => $packed_data ) ) ); - return $jpeg_header_data; -} - -/****************************************************************************** -* End of Function: put_JFIF -******************************************************************************/ - - - - - - - - -/****************************************************************************** -* -* Function: Interpret_JFIF_to_HTML -* -* Description: Generates html showing the JFIF information contained in -* a JFIF data array, as retrieved with get_JFIF -* -* Parameters: JFIF_array - a JFIF data array, as from get_JFIF -* filename - the name of the JPEG file being processed ( used -* by the script which displays the JFIF thumbnail) -* -* -* Returns: output - the HTML string -* -******************************************************************************/ - -function Interpret_JFIF_to_HTML( $JFIF_array, $filename ) -{ - $output = ""; - if ( $JFIF_array !== FALSE ) - { - $output .= "

    Contains JPEG File Interchange Format (JFIF) Information

    \n"; - $output .= "\n\n"; - $output .= "\n"; - if ( $JFIF_array['Units'] == 0 ) - { - $output .= "\n"; - } - elseif ( $JFIF_array['Units'] == 1 ) - { - $output .= "\n"; - } - elseif ( $JFIF_array['Units'] == 2 ) - { - $output .= "\n"; - } - - $output .= "\n"; - // TODO Implement JFIF Thumbnail display - } - else - { - $output .= "None\n"; - } - - $output .= "
    JFIF version: ". sprintf( "%d.%02d", $JFIF_array['Version1'], $JFIF_array['Version2'] ) . "
    Pixel Aspect Ratio: " . $JFIF_array['XDensity'] ." x " . $JFIF_array['YDensity'] . "
    Resolution: " . $JFIF_array['XDensity'] ." x " . $JFIF_array['YDensity'] . " pixels per inch
    Resolution: " . $JFIF_array['XDensity'] ." x " . $JFIF_array['YDensity'] . " pixels per cm
    JFIF (uncompressed) thumbnail: "; - if ( ( $JFIF_array['ThumbX'] != 0 ) && ( $JFIF_array['ThumbY'] != 0 ) ) - { - $output .= $JFIF_array['ThumbX'] ." x " . $JFIF_array['ThumbY'] . " pixels, Thumbnail Display Not Yet Implemented

    \n"; - } - - return $output; - -} - - -/****************************************************************************** -* End of Function: Interpret_JFIF_to_HTML -******************************************************************************/ - - - - - - - - - - -/****************************************************************************** -* -* Function: get_JFXX -* -* Description: Retrieves information from a JPEG File Interchange Format Extension (JFXX) -* segment and returns it in an array. Uses information supplied by -* the get_jpeg_header_data function -* -* Parameters: jpeg_header_data - a JPEG header data array in the same format -* as from get_jpeg_header_data -* -* Returns: JFXX_data - an array of JFXX data -* FALSE - if a JFXX segment could not be found -* -******************************************************************************/ - -function get_JFXX( $jpeg_header_data ) -{ - //Cycle through the header segments - for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) - { - // If we find an APP0 header, - if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP0" ) == 0 ) - { - // And if it has the JFIF label, - if( strncmp ( $jpeg_header_data[$i]['SegData'], "JFXX\x00", 5) == 0 ) - { - // Found a JPEG File Interchange Format Extension (JFXX) Block - - // unpack the JFXX data from the incoming string - // First is the 5 byte JFXX label string - // Then a 1 byte Extension code, indicating Thumbnail Format - // Then the thumbnail data - - $JFXX_data = unpack( 'a5JFXX/CExtension_Code/a*ThumbData', $jpeg_header_data[$i]['SegData'] ); - return $JFXX_data; - } - } - } - return FALSE; -} - -/****************************************************************************** -* End of Function: get_JFXX -******************************************************************************/ - - - - -/****************************************************************************** -* -* Function: put_JFXX -* -* Description: Creates a new JFXX segment from an array of JFXX data in the -* same format as would be retrieved from get_JFXX, and inserts -* this segment into the supplied JPEG header array -* -* Parameters: jpeg_header_data - a JPEG header data array in the same format -* as from get_jpeg_header_data, into which the -* new JFXX segment will be put -* new_JFXX_array - a JFXX information array in the same format as -* from get_JFXX, to create the new segment -* -* Returns: jpeg_header_data - the JPEG header data array with the new -* JFXX segment added -* -******************************************************************************/ - -function put_JFXX( $jpeg_header_data, $new_JFXX_array ) -{ - // pack the JFXX data into its proper format for a JPEG file - $packed_data = pack( 'a5Ca*',"JFXX\x00", $new_JFXX_array['Extension_Code'], $new_JFXX_array['ThumbData'] ); - - $JFIF_pos = -1; - - //Cycle through the header segments - for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) - { - // If we find an APP0 header, - if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP0" ) == 0 ) - { - // And if it has the JFXX label, - if( strncmp ( $jpeg_header_data[$i]['SegData'], "JFXX\x00", 5) == 0 ) - { - // Found a preexisting JFXX block - Replace it with the new one and return. - $jpeg_header_data[$i]['SegData'] = $packed_data; - return $jpeg_header_data; - } - - // if it has the JFIF label, - if( strncmp ( $jpeg_header_data[$i][SegData], "JFIF\x00", 5) == 0 ) - { - // Found a preexisting JFIF block - Mark it in case we need to insert the JFXX after it - $JFIF_pos = $i; - } - } - } - - - // No preexisting JFXX block found - - // Check if a JFIF segment was found, - if ( $JFIF_pos !== -1 ) - { - // A pre-existing JFIF segment was found, - // insert the new JFXX segment after it. - array_splice($jpeg_header_data, $JFIF_pos +1 , 0, array ( array( "SegType" => 0xE0, - "SegName" => "APP0", - "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE0 ], - "SegData" => $packed_data ) ) ); - - } - else - { - // No pre-existing JFIF segment was found, - // insert a new JFIF and the new JFXX segment at the start of the array. - - // Insert new JFXX segment - array_splice($jpeg_header_data, 0 , 0, array( array( "SegType" => 0xE0, - "SegName" => "APP0", - "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE0 ], - "SegData" => $packed_data ) ) ); - - // Create a new JFIF to be inserted at the start of - // the array, with generic values - $packed_data = pack( 'a5CCCnnCCa*',"JFIF\x00", 1, 2, 1, 72, 72, 0, 0, "" ); - - array_splice($jpeg_header_data, 0 , 0, array( array( "SegType" => 0xE0, - "SegName" => "APP0", - "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE0 ], - "SegData" => $packed_data ) ) ); - } - - - return $jpeg_header_data; -} - -/****************************************************************************** -* End of Function: put_JFIF -******************************************************************************/ - - - -/****************************************************************************** -* -* Function: Interpret_JFXX_to_HTML -* -* Description: Generates html showing the JFXX thumbnail contained in -* a JFXX data array, as retrieved with get_JFXX -* -* Parameters: JFXX_array - a JFXX information array in the same format as -* from get_JFXX, to create the new segment -* filename - the name of the JPEG file being processed ( used -* by the script which displays the JFXX thumbnail) -* -* Returns: output - the Html string -* -******************************************************************************/ - -function Interpret_JFXX_to_HTML( $JFXX_array, $filename ) -{ - $output = ""; - if ( $JFXX_array !== FALSE ) - { - $output .= "

    Contains JPEG File Interchange Extension Format (JFXX) Thumbnail

    \n"; - switch ( $JFXX_array['Extension_Code'] ) - { - case 0x10 : $output .= "

    JFXX Thumbnail is JPEG Encoded

    \n"; - - // Change: as of version 1.11 - Changed to make thumbnail link portable across directories - // Build the path of the thumbnail script and its filename parameter to put in a url - $link_str = get_relative_path( dirname(__FILE__) . "/get_JFXX_thumb.php" , getcwd ( ) ); - $link_str .= "?filename="; - $link_str .= get_relative_path( $filename, dirname(__FILE__) ); - - // Add thumbnail link to html - $output .= "\n"; - break; - case 0x11 : $output .= "

    JFXX Thumbnail is Encoded 1 byte/pixel

    \n"; - $output .= "

    Thumbnail Display Not Implemented Yet

    \n"; - break; - case 0x13 : $output .= "

    JFXX Thumbnail is Encoded 3 bytes/pixel

    \n"; - $output .= "

    Thumbnail Display Not Implemented Yet

    \n"; - break; - default : $output .= "

    JFXX Thumbnail is Encoded with Unknown format

    \n"; - break; - - // TODO: Implement JFXX one and three bytes per pixel thumbnail decoding - } - - } - - return $output; - -} - -/****************************************************************************** -* End of Function: Interpret_JFXX_to_HTML -******************************************************************************/ - - - - -?> \ No newline at end of file diff --git a/jpeg_metadata_tk/JPEG.php b/jpeg_metadata_tk/JPEG.php deleted file mode 100644 index 2004192..0000000 --- a/jpeg_metadata_tk/JPEG.php +++ /dev/null @@ -1,973 +0,0 @@ - 1.10 : changed put_jpeg_header_data to check if the data -* being written exists -* -* URL: http://electronics.ozhiker.com -* -* Copyright: Copyright Evan Hunter 2004 -* -* License: This file is part of the PHP JPEG Metadata Toolkit. -* -* The PHP JPEG Metadata Toolkit is free software; you can -* redistribute it and/or modify it under the terms of the -* GNU General Public License as published by the Free Software -* Foundation; either version 2 of the License, or (at your -* option) any later version. -* -* The PHP JPEG Metadata Toolkit is distributed in the hope -* that it will be useful, but WITHOUT ANY WARRANTY; without -* even the implied warranty of MERCHANTABILITY or FITNESS -* FOR A PARTICULAR PURPOSE. See the GNU General Public License -* for more details. -* -* You should have received a copy of the GNU General Public -* License along with the PHP JPEG Metadata Toolkit; if not, -* write to the Free Software Foundation, Inc., 59 Temple -* Place, Suite 330, Boston, MA 02111-1307 USA -* -* If you require a different license for commercial or other -* purposes, please contact the author: evan@ozhiker.com -* -******************************************************************************/ - - - - -/****************************************************************************** -* -* Function: get_jpeg_header_data -* -* Description: Reads all the JPEG header segments from an JPEG image file into an -* array -* -* Parameters: filename - the filename of the file to JPEG file to read -* -* Returns: headerdata - Array of JPEG header segments -* FALSE - if headers could not be read -* -******************************************************************************/ - -function get_jpeg_header_data( $filename ) -{ - - // prevent refresh from aborting file operations and hosing file - ignore_user_abort(true); - - - // Attempt to open the jpeg file - the at symbol supresses the error message about - // not being able to open files. The file_exists would have been used, but it - // does not work with files fetched over http or ftp. - $filehnd = @fopen($filename, 'rb'); - - // Check if the file opened successfully - if ( ! $filehnd ) - { - // Could't open the file - exit - error_log( "Could not open file $filename" ); - return FALSE; - } - - - // Read the first two characters - $data = network_safe_fread( $filehnd, 2 ); - - // Check that the first two characters are 0xFF 0xDA (SOI - Start of image) - if ( $data != "\xFF\xD8" ) - { - // No SOI (FF D8) at start of file - This probably isn't a JPEG file - close file and return; - error_log( "This probably is not a JPEG file" ); - fclose($filehnd); - return FALSE; - } - - - // Read the third character - $data = network_safe_fread( $filehnd, 2 ); - - // Check that the third character is 0xFF (Start of first segment header) - if ( $data{0} != "\xFF" ) - { - // NO FF found - close file and return - JPEG is probably corrupted - fclose($filehnd); - return FALSE; - } - - // Flag that we havent yet hit the compressed image data - $hit_compressed_image_data = FALSE; - - - // Cycle through the file until, one of: 1) an EOI (End of image) marker is hit, - // 2) we have hit the compressed image data (no more headers are allowed after data) - // 3) or end of file is hit - - while ( ( $data{1} != "\xD9" ) && (! $hit_compressed_image_data) && ( ! feof( $filehnd ) )) - { - // Found a segment to look at. - // Check that the segment marker is not a Restart marker - restart markers don't have size or data after them - if ( ( ord($data{1}) < 0xD0 ) || ( ord($data{1}) > 0xD7 ) ) - { - // Segment isn't a Restart marker - // Read the next two bytes (size) - $sizestr = network_safe_fread( $filehnd, 2 ); - - // convert the size bytes to an integer - $decodedsize = unpack ("nsize", $sizestr); - - // Save the start position of the data - $segdatastart = ftell( $filehnd ); - - // Read the segment data with length indicated by the previously read size - $segdata = network_safe_fread( $filehnd, $decodedsize['size'] - 2 ); - - - // Store the segment information in the output array - $headerdata[] = array( "SegType" => ord($data{1}), - "SegName" => $GLOBALS[ "JPEG_Segment_Names" ][ ord($data{1}) ], - "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ ord($data{1}) ], - "SegDataStart" => $segdatastart, - "SegData" => $segdata ); - } - - // If this is a SOS (Start Of Scan) segment, then there is no more header data - the compressed image data follows - if ( $data{1} == "\xDA" ) - { - // Flag that we have hit the compressed image data - exit loop as no more headers available. - $hit_compressed_image_data = TRUE; - } - else - { - // Not an SOS - Read the next two bytes - should be the segment marker for the next segment - $data = network_safe_fread( $filehnd, 2 ); - - // Check that the first byte of the two is 0xFF as it should be for a marker - if ( $data{0} != "\xFF" ) - { - // NO FF found - close file and return - JPEG is probably corrupted - fclose($filehnd); - return FALSE; - } - } - } - - // Close File - fclose($filehnd); - // Alow the user to abort from now on - ignore_user_abort(false); - - // Return the header data retrieved - return $headerdata; -} - - -/****************************************************************************** -* End of Function: get_jpeg_header_data -******************************************************************************/ - - - - -/****************************************************************************** -* -* Function: put_jpeg_header_data -* -* Description: Writes JPEG header data into a JPEG file. Takes an array in the -* same format as from get_jpeg_header_data, and combines it with -* the image data of an existing JPEG file, to create a new JPEG file -* WARNING: As this function will replace all JPEG headers, -* including SOF etc, it is best to read the jpeg headers -* from a file, alter them, then put them back on the same -* file. If a SOF segment wer to be transfered from one -* file to another, the image could become unreadable unless -* the images were idenical size and configuration -* -* -* Parameters: old_filename - the JPEG file from which the image data will be retrieved -* new_filename - the name of the new JPEG to create (can be same as old_filename) -* jpeg_header_data - a JPEG header data array in the same format -* as from get_jpeg_header_data -* -* Returns: TRUE - on Success -* FALSE - on Failure -* -******************************************************************************/ - -function put_jpeg_header_data( $old_filename, $new_filename, $jpeg_header_data ) -{ - - // Change: added check to ensure data exists, as of revision 1.10 - // Check if the data to be written exists - if ( $jpeg_header_data == FALSE ) - { - // Data to be written not valid - abort - return FALSE; - } - - // extract the compressed image data from the old file - $compressed_image_data = get_jpeg_image_data( $old_filename ); - - // Check if the extraction worked - if ( ( $compressed_image_data === FALSE ) || ( $compressed_image_data === NULL ) ) - { - // Couldn't get image data from old file - return FALSE; - } - - - // Cycle through new headers - foreach ($jpeg_header_data as $segno => $segment) - { - // Check that this header is smaller than the maximum size - if ( strlen($segment['SegData']) > 0xfffd ) - { - // Could't open the file - exit - error_log( "A Header is too large to fit in JPEG segment" ); - return FALSE; - } - } - - ignore_user_abort(true); ## prevent refresh from aborting file operations and hosing file - - - // Attempt to open the new jpeg file - $newfilehnd = @fopen($new_filename, 'wb'); - // Check if the file opened successfully - if ( ! $newfilehnd ) - { - // Could't open the file - exit - error_log( "Could not open file $new_filename" ); - return FALSE; - } - - // Write SOI - fwrite( $newfilehnd, "\xFF\xD8" ); - - // Cycle through new headers, writing them to the new file - foreach ($jpeg_header_data as $segno => $segment) - { - - // Write segment marker - fwrite( $newfilehnd, sprintf( "\xFF%c", $segment['SegType'] ) ); - - // Write segment size - fwrite( $newfilehnd, pack( "n", strlen($segment['SegData']) + 2 ) ); - - // Write segment data - fwrite( $newfilehnd, $segment['SegData'] ); - } - - // Write the compressed image data - fwrite( $newfilehnd, $compressed_image_data ); - - // Write EOI - fwrite( $newfilehnd, "\xFF\xD9" ); - - // Close File - fclose($newfilehnd); - - // Alow the user to abort from now on - ignore_user_abort(false); - - - return TRUE; - -} - -/****************************************************************************** -* End of Function: put_jpeg_header_data -******************************************************************************/ - - - -/****************************************************************************** -* -* Function: get_jpeg_Comment -* -* Description: Retreives the contents of the JPEG Comment (COM = 0xFFFE) segment if one -* exists -* -* Parameters: jpeg_header_data - the JPEG header data, as retrieved -* from the get_jpeg_header_data function -* -* Returns: string - Contents of the Comment segement -* FALSE - if the comment segment couldnt be found -* -******************************************************************************/ - -function get_jpeg_Comment( $jpeg_header_data ) -{ - //Cycle through the header segments until COM is found or we run out of segments - $i = 0; - while ( ( $i < count( $jpeg_header_data) ) && ( $jpeg_header_data[$i]['SegName'] != "COM" ) ) - { - $i++; - } - - // Check if a COM segment has been found - if ( $i < count( $jpeg_header_data) ) - { - // A COM segment was found, return it's contents - return $jpeg_header_data[$i]['SegData']; - } - else - { - // No COM segment found - return FALSE; - } -} - -/****************************************************************************** -* End of Function: get_jpeg_Comment -******************************************************************************/ - - -/****************************************************************************** -* -* Function: put_jpeg_Comment -* -* Description: Creates a new JPEG Comment segment from a string, and inserts -* this segment into the supplied JPEG header array -* -* Parameters: jpeg_header_data - a JPEG header data array in the same format -* as from get_jpeg_header_data, into which the -* new Comment segment will be put -* $new_Comment - a string containing the new Comment -* -* Returns: jpeg_header_data - the JPEG header data array with the new -* JPEG Comment segment added -* -******************************************************************************/ - -function put_jpeg_Comment( $jpeg_header_data, $new_Comment ) -{ - //Cycle through the header segments - for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) - { - // If we find an COM header, - if ( strcmp ( $jpeg_header_data[$i]['SegName'], "COM" ) == 0 ) - { - // Found a preexisting Comment block - Replace it with the new one and return. - $jpeg_header_data[$i]['SegData'] = $new_Comment; - return $jpeg_header_data; - } - } - - - - // No preexisting Comment block found, find where to put it by searching for the highest app segment - $i = 0; - while ( ( $i < count( $jpeg_header_data ) ) && ( $jpeg_header_data[$i]["SegType"] >= 0xE0 ) ) - { - $i++; - } - - - // insert a Comment segment new at the position found of the header data. - array_splice($jpeg_header_data, $i , 0, array( array( "SegType" => 0xFE, - "SegName" => $GLOBALS[ "JPEG_Segment_Names" ][ 0xFE ], - "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xFE ], - "SegData" => $new_Comment ) ) ); - return $jpeg_header_data; -} - -/****************************************************************************** -* End of Function: put_jpeg_Comment -******************************************************************************/ - - - - -/****************************************************************************** -* -* Function: Interpret_Comment_to_HTML -* -* Description: Generates html showing the contents of any JPEG Comment segment -* -* Parameters: jpeg_header_data - the JPEG header data, as retrieved -* from the get_jpeg_header_data function -* -* Returns: output - the HTML -* -******************************************************************************/ - -function Interpret_Comment_to_HTML( $jpeg_header_data ) -{ - // Create a string to receive the output - $output = ""; - - // read the comment segment - $comment = get_jpeg_Comment( $jpeg_header_data ); - - // Check if the comment segment was valid - if ( $comment !== FALSE ) - { - // Comment exists - add it to the output - $output .= "

    JPEG Comment

    \n"; - $output .= "

    $comment

    \n"; - } - - // Return the result - return $output; -} - -/****************************************************************************** -* End of Function: Interpret_Comment_to_HTML -******************************************************************************/ - - - - -/****************************************************************************** -* -* Function: get_jpeg_intrinsic_values -* -* Description: Retreives information about the intrinsic characteristics of the -* jpeg image, such as Bits per Component, Height and Width. -* -* Parameters: jpeg_header_data - the JPEG header data, as retrieved -* from the get_jpeg_header_data function -* -* Returns: array - An array containing the intrinsic JPEG values -* FALSE - if the comment segment couldnt be found -* -******************************************************************************/ - -function get_jpeg_intrinsic_values( $jpeg_header_data ) -{ - // Create a blank array for the output - $Outputarray = array( ); - - //Cycle through the header segments until Start Of Frame (SOF) is found or we run out of segments - $i = 0; - while ( ( $i < count( $jpeg_header_data) ) && ( substr( $jpeg_header_data[$i]['SegName'], 0, 3 ) != "SOF" ) ) - { - $i++; - } - - // Check if a SOF segment has been found - if ( substr( $jpeg_header_data[$i]['SegName'], 0, 3 ) == "SOF" ) - { - // SOF segment was found, extract the information - - $data = $jpeg_header_data[$i]['SegData']; - - // First byte is Bits per component - $Outputarray['Bits per Component'] = ord( $data{0} ); - - // Second and third bytes are Image Height - $Outputarray['Image Height'] = ord( $data{ 1 } ) * 256 + ord( $data{ 2 } ); - - // Forth and fifth bytes are Image Width - $Outputarray['Image Width'] = ord( $data{ 3 } ) * 256 + ord( $data{ 4 } ); - - // Sixth byte is number of components - $numcomponents = ord( $data{ 5 } ); - - // Following this is a table containing information about the components - for( $i = 0; $i < $numcomponents; $i++ ) - { - $Outputarray['Components'][] = array ( 'Component Identifier' => ord( $data{ 6 + $i * 3 } ), - 'Horizontal Sampling Factor' => ( ord( $data{ 7 + $i * 3 } ) & 0xF0 ) / 16, - 'Vertical Sampling Factor' => ( ord( $data{ 7 + $i * 3 } ) & 0x0F ), - 'Quantization table destination selector' => ord( $data{ 8 + $i * 3 } ) ); - } - } - else - { - // Couldn't find Start Of Frame segment, hence can't retrieve info - return FALSE; - } - - return $Outputarray; -} - - -/****************************************************************************** -* End of Function: get_jpeg_intrinsic_values -******************************************************************************/ - - - - - -/****************************************************************************** -* -* Function: Interpret_intrinsic_values_to_HTML -* -* Description: Generates html showing some of the intrinsic JPEG values which -* were retrieved with the get_jpeg_intrinsic_values function -* -* Parameters: values - the JPEG intrinsic values, as read from get_jpeg_intrinsic_values -* -* Returns: OutputStr - A string containing the HTML -* -******************************************************************************/ - -function Interpret_intrinsic_values_to_HTML( $values ) -{ - // Check values are valid - if ( $values != FALSE ) - { - // Write Heading - $OutputStr = "

    Intrinsic JPEG Information

    \n"; - - // Create Table - $OutputStr .= "\n"; - - // Put image height and width into table - $OutputStr .= "\n"; - $OutputStr .= "\n"; - - // Put colour depth into table - if ( count( $values['Components'] ) == 1 ) - { - $OutputStr .= "\n"; - } - else - { - $OutputStr .= "\n"; - } - - // Close Table - $OutputStr .= "
    Image Height" . $values['Image Height'] . " pixels
    Image Width" . $values['Image Width'] . " pixels
    Colour Depth" . $values['Bits per Component'] . " bit Monochrome
    Colour Depth" . ($values['Bits per Component'] * count( $values['Components'] ) ) . " bit
    \n"; - - // Return html - return $OutputStr; - } -} - -/****************************************************************************** -* End of Function: Interpret_intrinsic_values_to_HTML -******************************************************************************/ - - - - - - - -/****************************************************************************** -* -* Function: get_jpeg_image_data -* -* Description: Retrieves the compressed image data part of the JPEG file -* -* Parameters: filename - the filename of the JPEG file to read -* -* Returns: compressed_data - A string containing the compressed data -* FALSE - if retrieval failed -* -******************************************************************************/ - -function get_jpeg_image_data( $filename ) -{ - - // prevent refresh from aborting file operations and hosing file - ignore_user_abort(true); - - // Attempt to open the jpeg file - $filehnd = @fopen($filename, 'rb'); - - // Check if the file opened successfully - if ( ! $filehnd ) - { - // Could't open the file - exit - return FALSE; - } - - - // Read the first two characters - $data = network_safe_fread( $filehnd, 2 ); - - // Check that the first two characters are 0xFF 0xDA (SOI - Start of image) - if ( $data != "\xFF\xD8" ) - { - // No SOI (FF D8) at start of file - close file and return; - fclose($filehnd); - return FALSE; - } - - - - // Read the third character - $data = network_safe_fread( $filehnd, 2 ); - - // Check that the third character is 0xFF (Start of first segment header) - if ( $data{0} != "\xFF" ) - { - // NO FF found - close file and return - fclose($filehnd); - return; - } - - // Flag that we havent yet hit the compressed image data - $hit_compressed_image_data = FALSE; - - - // Cycle through the file until, one of: 1) an EOI (End of image) marker is hit, - // 2) we have hit the compressed image data (no more headers are allowed after data) - // 3) or end of file is hit - - while ( ( $data{1} != "\xD9" ) && (! $hit_compressed_image_data) && ( ! feof( $filehnd ) )) - { - // Found a segment to look at. - // Check that the segment marker is not a Restart marker - restart markers don't have size or data after them - if ( ( ord($data{1}) < 0xD0 ) || ( ord($data{1}) > 0xD7 ) ) - { - // Segment isn't a Restart marker - // Read the next two bytes (size) - $sizestr = network_safe_fread( $filehnd, 2 ); - - // convert the size bytes to an integer - $decodedsize = unpack ("nsize", $sizestr); - - // Read the segment data with length indicated by the previously read size - $segdata = network_safe_fread( $filehnd, $decodedsize['size'] - 2 ); - } - - // If this is a SOS (Start Of Scan) segment, then there is no more header data - the compressed image data follows - if ( $data{1} == "\xDA" ) - { - // Flag that we have hit the compressed image data - exit loop after reading the data - $hit_compressed_image_data = TRUE; - - // read the rest of the file in - // Can't use the filesize function to work out - // how much to read, as it won't work for files being read by http or ftp - // So instead read 1Mb at a time till EOF - - $compressed_data = ""; - do - { - $compressed_data .= network_safe_fread( $filehnd, 1048576 ); - } while( ! feof( $filehnd ) ); - - // Strip off EOI and anything after - $EOI_pos = strpos( $compressed_data, "\xFF\xD9" ); - $compressed_data = substr( $compressed_data, 0, $EOI_pos ); - } - else - { - // Not an SOS - Read the next two bytes - should be the segment marker for the next segment - $data = network_safe_fread( $filehnd, 2 ); - - // Check that the first byte of the two is 0xFF as it should be for a marker - if ( $data{0} != "\xFF" ) - { - // Problem - NO FF foundclose file and return"; - fclose($filehnd); - return; - } - } - } - - // Close File - fclose($filehnd); - - // Alow the user to abort from now on - ignore_user_abort(false); - - - // Return the compressed data if it was found - if ( $hit_compressed_image_data ) - { - return $compressed_data; - } - else - { - return FALSE; - } -} - - -/****************************************************************************** -* End of Function: get_jpeg_image_data -******************************************************************************/ - - - - - - - -/****************************************************************************** -* -* Function: Generate_JPEG_APP_Segment_HTML -* -* Description: Generates html showing information about the Application (APP) -* segments which are present in the JPEG file -* -* Parameters: jpeg_header_data - the JPEG header data, as retrieved -* from the get_jpeg_header_data function -* -* Returns: output - A string containing the HTML -* -******************************************************************************/ - -function Generate_JPEG_APP_Segment_HTML( $jpeg_header_data ) -{ - if ( $jpeg_header_data == FALSE ) - { - return ""; - } - - - // Write Heading - $output = "

    Application Metadata Segments

    \n"; - - // Create table - $output .= "\n"; - - - // Cycle through each segment in the array - - foreach( $jpeg_header_data as $jpeg_header ) - { - - // Check if the segment is a APP segment - - if ( ( $jpeg_header['SegType'] >= 0xE0 ) && ( $jpeg_header['SegType'] <= 0xEF ) ) - { - // This is an APP segment - - // Read APP Segment Name - a Null terminated string at the start of the segment - $seg_name = strtok($jpeg_header['SegData'], "\x00"); - - // Some Segment names are either too long or not meaningfull, so - // we should clean them up - - if ( $seg_name == "http://ns.adobe.com/xap/1.0/" ) - { - $seg_name = "XAP/RDF (\"http://ns.adobe.com/xap/1.0/\")"; - } - elseif ( $seg_name == "Photoshop 3.0" ) - { - $seg_name = "Photoshop IRB (\"Photoshop 3.0\")"; - } - elseif ( ( strncmp ( $seg_name, "[picture info]", 14) == 0 ) || - ( strncmp ( $seg_name, "\x0a\x09\x09\x09\x09[picture info]", 19) == 0 ) ) - { - $seg_name = "[picture info]"; - } - elseif ( strncmp ( $seg_name, "Type=", 5) == 0 ) - { - $seg_name = "Epson Info"; - } - elseif ( ( strncmp ( $seg_name, "HHHHHHHHHHHHHHH", 15) == 0 ) || - ( strncmp ( $seg_name, "@s33", 5) == 0 ) ) - { - $seg_name = "HP segment full of \"HHHHH\""; - } - - - // Clean the segment name so it doesn't cause problems with HTML - $seg_name = htmlentities( $seg_name ); - - // Output a Table row containing this APP segment - $output .= "\n"; - } - } - - // Close the table - $output .= "
    $seg_name" . $jpeg_header['SegName'] . "" . strlen( $jpeg_header['SegData']). " bytes
    \n"; - - // Return the HTML - return $output; -} - - -/****************************************************************************** -* End of Function: Generate_JPEG_APP_Segment_HTML -******************************************************************************/ - - - - -/****************************************************************************** -* -* Function: network_safe_fread -* -* Description: Retrieves data from a file. This function is required since -* the fread function will not always return the requested number -* of characters when reading from a network stream or pipe -* -* Parameters: file_handle - the handle of a file to read from -* length - the number of bytes requested -* -* Returns: data - the data read from the file. may be less than the number -* requested if EOF was hit -* -******************************************************************************/ - -function network_safe_fread( $file_handle, $length ) -{ - // Create blank string to receive data - $data = ""; - - // Keep reading data from the file until either EOF occurs or we have - // retrieved the requested number of bytes - - while ( ( !feof( $file_handle ) ) && ( strlen($data) < $length ) ) - { - $data .= fread( $file_handle, $length-strlen($data) ); - } - - // return the data read - return $data; -} - -/****************************************************************************** -* End of Function: network_safe_fread -******************************************************************************/ - - - - -/****************************************************************************** -* Global Variable: JPEG_Segment_Names -* -* Contents: The names of the JPEG segment markers, indexed by their marker number -* -******************************************************************************/ - -$GLOBALS[ "JPEG_Segment_Names" ] = array( - -0xC0 => "SOF0", 0xC1 => "SOF1", 0xC2 => "SOF2", 0xC3 => "SOF4", -0xC5 => "SOF5", 0xC6 => "SOF6", 0xC7 => "SOF7", 0xC8 => "JPG", -0xC9 => "SOF9", 0xCA => "SOF10", 0xCB => "SOF11", 0xCD => "SOF13", -0xCE => "SOF14", 0xCF => "SOF15", -0xC4 => "DHT", 0xCC => "DAC", - -0xD0 => "RST0", 0xD1 => "RST1", 0xD2 => "RST2", 0xD3 => "RST3", -0xD4 => "RST4", 0xD5 => "RST5", 0xD6 => "RST6", 0xD7 => "RST7", - -0xD8 => "SOI", 0xD9 => "EOI", 0xDA => "SOS", 0xDB => "DQT", -0xDC => "DNL", 0xDD => "DRI", 0xDE => "DHP", 0xDF => "EXP", - -0xE0 => "APP0", 0xE1 => "APP1", 0xE2 => "APP2", 0xE3 => "APP3", -0xE4 => "APP4", 0xE5 => "APP5", 0xE6 => "APP6", 0xE7 => "APP7", -0xE8 => "APP8", 0xE9 => "APP9", 0xEA => "APP10", 0xEB => "APP11", -0xEC => "APP12", 0xED => "APP13", 0xEE => "APP14", 0xEF => "APP15", - - -0xF0 => "JPG0", 0xF1 => "JPG1", 0xF2 => "JPG2", 0xF3 => "JPG3", -0xF4 => "JPG4", 0xF5 => "JPG5", 0xF6 => "JPG6", 0xF7 => "JPG7", -0xF8 => "JPG8", 0xF9 => "JPG9", 0xFA => "JPG10", 0xFB => "JPG11", -0xFC => "JPG12", 0xFD => "JPG13", - -0xFE => "COM", 0x01 => "TEM", 0x02 => "RES", - -); - -/****************************************************************************** -* End of Global Variable: JPEG_Segment_Names -******************************************************************************/ - - -/****************************************************************************** -* Global Variable: JPEG_Segment_Descriptions -* -* Contents: The descriptions of the JPEG segment markers, indexed by their marker number -* -******************************************************************************/ - -$GLOBALS[ "JPEG_Segment_Descriptions" ] = array( - -/* JIF Marker byte pairs in JPEG Interchange Format sequence */ -0xC0 => "Start Of Frame (SOF) Huffman - Baseline DCT", -0xC1 => "Start Of Frame (SOF) Huffman - Extended sequential DCT", -0xC2 => "Start Of Frame Huffman - Progressive DCT (SOF2)", -0xC3 => "Start Of Frame Huffman - Spatial (sequential) lossless (SOF3)", -0xC5 => "Start Of Frame Huffman - Differential sequential DCT (SOF5)", -0xC6 => "Start Of Frame Huffman - Differential progressive DCT (SOF6)", -0xC7 => "Start Of Frame Huffman - Differential spatial (SOF7)", -0xC8 => "Start Of Frame Arithmetic - Reserved for JPEG extensions (JPG)", -0xC9 => "Start Of Frame Arithmetic - Extended sequential DCT (SOF9)", -0xCA => "Start Of Frame Arithmetic - Progressive DCT (SOF10)", -0xCB => "Start Of Frame Arithmetic - Spatial (sequential) lossless (SOF11)", -0xCD => "Start Of Frame Arithmetic - Differential sequential DCT (SOF13)", -0xCE => "Start Of Frame Arithmetic - Differential progressive DCT (SOF14)", -0xCF => "Start Of Frame Arithmetic - Differential spatial (SOF15)", -0xC4 => "Define Huffman Table(s) (DHT)", -0xCC => "Define Arithmetic coding conditioning(s) (DAC)", - -0xD0 => "Restart with modulo 8 count 0 (RST0)", -0xD1 => "Restart with modulo 8 count 1 (RST1)", -0xD2 => "Restart with modulo 8 count 2 (RST2)", -0xD3 => "Restart with modulo 8 count 3 (RST3)", -0xD4 => "Restart with modulo 8 count 4 (RST4)", -0xD5 => "Restart with modulo 8 count 5 (RST5)", -0xD6 => "Restart with modulo 8 count 6 (RST6)", -0xD7 => "Restart with modulo 8 count 7 (RST7)", - -0xD8 => "Start of Image (SOI)", -0xD9 => "End of Image (EOI)", -0xDA => "Start of Scan (SOS)", -0xDB => "Define quantization Table(s) (DQT)", -0xDC => "Define Number of Lines (DNL)", -0xDD => "Define Restart Interval (DRI)", -0xDE => "Define Hierarchical progression (DHP)", -0xDF => "Expand Reference Component(s) (EXP)", - -0xE0 => "Application Field 0 (APP0) - usually JFIF or JFXX", -0xE1 => "Application Field 1 (APP1) - usually EXIF or XMP/RDF", -0xE2 => "Application Field 2 (APP2) - usually Flashpix", -0xE3 => "Application Field 3 (APP3)", -0xE4 => "Application Field 4 (APP4)", -0xE5 => "Application Field 5 (APP5)", -0xE6 => "Application Field 6 (APP6)", -0xE7 => "Application Field 7 (APP7)", - -0xE8 => "Application Field 8 (APP8)", -0xE9 => "Application Field 9 (APP9)", -0xEA => "Application Field 10 (APP10)", -0xEB => "Application Field 11 (APP11)", -0xEC => "Application Field 12 (APP12) - usually [picture info]", -0xED => "Application Field 13 (APP13) - usually photoshop IRB / IPTC", -0xEE => "Application Field 14 (APP14)", -0xEF => "Application Field 15 (APP15)", - - -0xF0 => "Reserved for JPEG extensions (JPG0)", -0xF1 => "Reserved for JPEG extensions (JPG1)", -0xF2 => "Reserved for JPEG extensions (JPG2)", -0xF3 => "Reserved for JPEG extensions (JPG3)", -0xF4 => "Reserved for JPEG extensions (JPG4)", -0xF5 => "Reserved for JPEG extensions (JPG5)", -0xF6 => "Reserved for JPEG extensions (JPG6)", -0xF7 => "Reserved for JPEG extensions (JPG7)", -0xF8 => "Reserved for JPEG extensions (JPG8)", -0xF9 => "Reserved for JPEG extensions (JPG9)", -0xFA => "Reserved for JPEG extensions (JPG10)", -0xFB => "Reserved for JPEG extensions (JPG11)", -0xFC => "Reserved for JPEG extensions (JPG12)", -0xFD => "Reserved for JPEG extensions (JPG13)", - -0xFE => "Comment (COM)", -0x01 => "For temp private use arith code (TEM)", -0x02 => "Reserved (RES)", - -); - -/****************************************************************************** -* End of Global Variable: JPEG_Segment_Descriptions -******************************************************************************/ - - - -?> diff --git a/jpeg_metadata_tk/Makernotes/Pentax.php b/jpeg_metadata_tk/Makernotes/Pentax.php deleted file mode 100644 index 10194a6..0000000 --- a/jpeg_metadata_tk/Makernotes/Pentax.php +++ /dev/null @@ -1,353 +0,0 @@ - array( 'Name' => "Capture Mode", - 'Type' => "Lookup", - 0 => "Auto", - 1 => "Night-scene", - 2 => "Manual", - 4 => "Multiple" ), - -0x0002 => array( 'Name' => "Quality Level", - 'Type' => "Lookup", - 0 => "Good", - 1 => "Better", - 2 => "Best" ), - -0x0003 => array( 'Name' => "Focus Mode", - 'Type' => "Lookup", - 2 => "Custom", - 3 => "Auto" ), - -0x0004 => array( 'Name' => "Flash Mode", - 'Type' => "Lookup", - 1 => "Auto", - 2 => "Flash on", - 4 => "Flash off", - 6 => "Red-eye Reduction" ), - -0x0007 => array( 'Name' => "White Balance", - 'Type' => "Lookup", - 0 => "Auto", - 1 => "Daylight", - 2 => "Shade", - 3 => "Tungsten", - 4 => "Fluorescent", - 5 => "Manual" ), - - -0x000a => array( 'Name' => "Digital Zoom", - 'Type' => "Numeric", - 'Units' => " (0 = Off)" ), - -0x000b => array( 'Name' => "Sharpness", - 'Type' => "Lookup", - 0 => "Normal", - 1 => "Soft", - 2 => "Hard" ), - -0x000c => array( 'Name' => "Contrast", - 'Type' => "Lookup", - 0 => "Normal", - 1 => "Low", - 2 => "High" ), - -0x000d => array( 'Name' => "Saturation", - 'Type' => "Lookup", - 0 => "Normal", - 1 => "Low", - 2 => "High" ), - -0x0014 => array( 'Name' => "ISO Speed", - 'Type' => "Lookup", - 10 => "100", - 16 => "200", - 100 => "100", - 200 => "200" ), - -0x0017 => array( 'Name' => "Colour", - 'Type' => "Lookup", - 1 => "Normal", - 2 => "Black & White", - 3 => "Sepia" ), - -0x0e00 => array( 'Name' => "Print Image Matching Info", - 'Type' => "PIM" ), - -0x1000 => array( 'Name' => "Time Zone", - 'Type' => "String" ), - -0x1001 => array( 'Name' => "Daylight Savings", - 'Type' => "String" ), - - - - - -); - -/****************************************************************************** -* End of Global Variable: IFD_Tag_Definitions, Pentax -******************************************************************************/ - - - -?> diff --git a/jpeg_metadata_tk/Makernotes/agfa.php b/jpeg_metadata_tk/Makernotes/agfa.php deleted file mode 100644 index 214c70d..0000000 --- a/jpeg_metadata_tk/Makernotes/agfa.php +++ /dev/null @@ -1,117 +0,0 @@ - diff --git a/jpeg_metadata_tk/Makernotes/canon.php b/jpeg_metadata_tk/Makernotes/canon.php deleted file mode 100644 index a261506..0000000 --- a/jpeg_metadata_tk/Makernotes/canon.php +++ /dev/null @@ -1,748 +0,0 @@ - $value ) - { - // Check that the value exists - if ( $value !== NULL ) - { - // Process the settings according to their offset - if ( $offset == 0 ) - { - // Do Not Show this Field ( Number of Bytes in Tag ) - } - else if ( $offset == 2 ) - { - if ( $value == 0 ) - { - $output_str .= "Self timer not used\n"; - } - else - { - $output_str .= "Self timer length : ". ($value/10) . " seconds\n"; - } - } - else if ( ( $offset == 23 ) && ( $Exif_Tag['Data'][25] != 0 )) - { - $output_str .= "Maximum Focal Length of Lens: " . ($value / $Exif_Tag['Data'][25]) . "mm\n"; - } - else if ( ( $offset == 24 ) && ( $Exif_Tag['Data'][25] != 0 )) - { - $output_str .= "Minimum Focal Length of Lens: " . ($value / $Exif_Tag['Data'][25]) . "mm\n"; - } - else if ( $offset == 25 ) - { - // Do Not Show this Field ( Focal Length units per mm ) - } - else if ( $offset == 29 ) - { - if ( $value & 0x4000 == 0x4000 ) - { - $output_str .= "External E-TTL Flash\n"; - } - if ( $value & 0x2000 == 0x2000 ) - { - $output_str .= "Internal Flash\n"; - } - if ( $value & 0x0800 == 0x0800 ) - { - $output_str .= "Flash FP sync used\n"; - } - if ( $value & 0x0080 == 0x0080 ) - { - $output_str .= "Second (Rear) curtain flash sync used\n"; - } - if ( $value & 0x0008 == 0x0008 ) - { - $output_str .= "Flash FP sync enabled\n"; - } - - } - else if ( array_key_exists( $offset, $GLOBALS[ "Canon_Camera_Settings_1_Tag_Values" ] ) ) - { - if ( array_key_exists( $value, $GLOBALS[ "Canon_Camera_Settings_1_Tag_Values" ][$offset] ) ) - { - $output_str .= $GLOBALS[ "Canon_Camera_Settings_1_Tag_Values" ][$offset]['Name'] . ": " . $GLOBALS[ "Canon_Camera_Settings_1_Tag_Values" ][$offset][$value] . "\n"; - } - else - { - if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == FALSE ) - { - $output_str .= $GLOBALS[ "Canon_Camera_Settings_1_Tag_Values" ][$offset]['Name'] . ": Unknown Value ($value)\n"; - } - } - } - else - { - if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == FALSE ) - { - // Unknown Canon camera setting - $output_str .= " Unknown Setting ($offset), value: $value\n"; - } - } - } - - } - // Return the text string - return $output_str; - break; - - - // CAMERA SETTINGS 2 - case 4: - // Create an output string - $output_str = ""; - - // Cycle through each of the camera settings Values - foreach( $Exif_Tag['Data'] as $offset => $value ) - { - // Check that the value exists - if ( $value !== NULL ) - { - // Process the settings according to their offset - if ( $offset == 0 ) - { - // Do Not Show this Field ( Number of Bytes in Tag ) - } - else if ( $offset == 9 ) - { - $output_str .= "Sequence Number in a continuous burst : $value\n"; - } - else if ( $offset == 14 ) - { - $output_str .= "Number of Focus Points Available: ". ( ( $value & 0xF000 ) / 0x1000 ) . "\n"; - - if ( $value & 0x0004 == 0x0004 ) - { - $output_str .= "Left Focus Point Used\n"; - } - if ( $value & 0x0002 == 0x0002 ) - { - $output_str .= "Centre Focus Point Used\n"; - } - if ( $value & 0x0001 == 0x0001 ) - { - $output_str .= "Right Focus Point Used\n"; - } - } - else if ( $offset == 19 ) - { - $output_str .= "Subject distance: $value (units either mm or cm)\n"; - } - else if ( array_key_exists( $offset, $GLOBALS[ "Canon_Camera_Settings_2_Tag_Values" ] ) ) - { - if ( array_key_exists( $value, $GLOBALS[ "Canon_Camera_Settings_2_Tag_Values" ][$offset] ) ) - { - $output_str .= $GLOBALS[ "Canon_Camera_Settings_2_Tag_Values" ][$offset]['Name'] . ": " . $GLOBALS[ "Canon_Camera_Settings_2_Tag_Values" ][$offset][$value] . "\n"; - } - else - { - if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == FALSE ) - { - $output_str .= $GLOBALS[ "Canon_Camera_Settings_2_Tag_Values" ][$offset]['Name'] . ": Unknown Value ($value)\n"; - } - } - } - else - { - if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == FALSE ) - { - $output_str .= " Unknown Setting ($offset), value: $value\n"; - } - } - } - - } - // Return the text string - return $output_str; - break; - - - // Serial Number - case 12: - $output_str = sprintf ( "%04X%05d", (($Exif_Tag['Data'][0] & 0xFF00)/256), ($Exif_Tag['Data'][0] & 0x00FF) ); - break; - - - // Custom Functions - case 15: - // Create an output string - $output_str = ""; - - // The size element is the first of the value array - // get rid of it - $tmparray = $Exif_Tag['Data']; - array_shift ( $tmparray ); - - // Cycle through each of the custom functions - foreach( $tmparray as $valorder => $value ) - { - // Figure out the function number and value - $funcno = ( $value & 0xFF00 ) / 256; - $funcval = $value & 0x00FF; - - // Check if the function exists in the lookup table of custom functions - if ( array_key_exists( $funcno, $GLOBALS[ "Canon_Custom_Functions_Tag_Values" ] ) ) - { - // Function Exists in lookup table, - // Check if value exists for this function in the lookup table - if ( array_key_exists( $funcval, $GLOBALS[ "Canon_Custom_Functions_Tag_Values" ][$funcno] ) ) - { - // Value exists - Add it to the output text - $output_str .= $GLOBALS[ "Canon_Custom_Functions_Tag_Values" ][$funcno]['Name'] . ": " . $GLOBALS[ "Canon_Custom_Functions_Tag_Values" ][$funcno][$funcval] . "\n"; - } - else - { - if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == FALSE ) - { - // Value doesn't exist - Add a message to the output text - $output_str .= $GLOBALS[ "Canon_Custom_Functions_Tag_Values" ][$funcno]['Name'] . ": Unknown Value ($value)\n"; - } - } - } - else - { - if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == FALSE ) - { - // Function doesn't exist in lookup table - add a message to the output text - $output_str .= "Unknown Custom Function ($funcno), value: $funcval\n"; - } - } - } - // Return the resulting string - return $output_str; - break; - - default : - return FALSE; - } - - return FALSE; -} - -/****************************************************************************** -* End of Function: get_Canon_Text_Value -******************************************************************************/ - - - - - - - - - - - - - - - - -/****************************************************************************** -* Global Variable: IFD_Tag_Definitions, Canon -* -* Contents: This global variable provides definitions of the known Canon -* Makernote tags, indexed by their tag number. -* -******************************************************************************/ - -$GLOBALS[ "IFD_Tag_Definitions" ]['Canon'] = array( - -1 => array( 'Name' => "Camera Settings 1", - 'Type' => "Special" ), - -4 => array( 'Name' => "Camera Settings 2", - 'Type' => "Special" ), - -6 => array( 'Name' => "Image Type", - 'Type' => "String" ), - -7 => array( 'Name' => "Firmware Version", - 'Type' => "String" ), - -8 => array( 'Name' => "Image Number", - 'Type' => "Numeric" ), - -9 => array( 'Name' => "Owner Name", - 'Type' => "String" ), - -12 => array( 'Name' => "Camera Serial Number", - 'Type' => "Special" ), - -15 => array( 'Name' => "Custom Functions", - 'Type' => "Special" ) - -); - - -/****************************************************************************** -* End of Global Variable: IFD_Tag_Definitions, Canon -******************************************************************************/ - - - - - - - - - - - - - - - - - - -/****************************************************************************** -* Global Variable: Canon_Camera_Settings_1_Tag_Values -* -* Contents: This global variable provides definitions for the Canon Camera -* Settings 1 Makernote tag, indexed by their offset. -* -******************************************************************************/ - -$GLOBALS[ "Canon_Camera_Settings_1_Tag_Values" ] = array( - -1 => array( 'Name' => "Macro Mode", - 1 => "Macro", - 2 => "Normal ( Not Macro )" ), - -3 => array( 'Name' => "Quality", - 2 => "Normal", - 3 => "Fine", - 5 => "Superfine" ), - -4 => array( 'Name' => "Flash Mode", - 0 => "Flash Not Fired", - 1 => "Auto", - 2 => "On", - 3 => "Red Eye Reduction", - 4 => "Slow Synchro", - 5 => "Auto + Red Eye Reduction", - 6 => "On + Red Eye Reduction", - 16 => "External Flash" ), - -5 => array( 'Name' => "Continuous drive mode", - 0 => "Single Frame or Timer Mode", - 1 => "Continuous" ), - -7 => array( 'Name' => "Focus Mode", - 0 => "One-Shot", - 1 => "AI Servo", - 2 => "AI Focus", - 3 => "Manual Focus", - 4 => "Single", - 5 => "Continuous", - 6 => "Manual Focus" ), - -10 => array( 'Name' => "Image Size", - 0 => "Large", - 1 => "Medium", - 2 => "Small" ), - -11 => array( 'Name' => "Easy shooting Mode", - 0 => "Full Auto", - 1 => "Manual", - 2 => "Landscape", - 3 => "Fast Shutter", - 4 => "Slow Shutter", - 5 => "Night", - 6 => "Black & White", - 7 => "Sepia", - 8 => "Portrait", - 9 => "Sports", - 10 => "Macro / Close-Up", - 11 => "Pan Focus" ), - - -12 => array( 'Name' => "Digital Zoom", - 0 => "No Digital Zoom", - 1 => "2x", - 2 => "4x" ), - -13 => array( 'Name' => "Contrast", - 0 => "Normal", - 1 => "High", - 65535 => "Low" ), - -14 => array( 'Name' => "Saturation", - 0 => "Normal", - 1 => "High", - 65535 => "Low" ), - -15 => array( 'Name' => "Sharpness", - 0 => "Normal", - 1 => "High", - 65535 => "Low" ), - -16 => array( 'Name' => "ISO Speed", - 0 => "Check ISOSpeedRatings EXIF tag for ISO Speed", - 15 => "Auto ISO", - 16 => "ISO 50", - 17 => "ISO 100", - 18 => "ISO 200", - 19 => "ISO 400" ), - -17 => array( 'Name' => "Metering Mode", - 3 => "Evaluative", - 4 => "Partial", - 5 => "Centre Weighted" ), - -18 => array( 'Name' => "Focus Type", - 0 => "Manual", - 1 => "Auto", - 3 => "Close-up (Macro)", - 8 => "Locked (Pan Mode)" ), - -19 => array( 'Name' => "Auto Focus Point Selected", - 12288 => "None (Manual Focus)", - 12289 => "Auto Selected", - 12290 => "Right", - 12291 => "Centre", - 12292 => "Left" ), - -20 => array( 'Name' => "Exposure Mode", - 0 => "Easy Shooting (See Easy Shooting Mode)", - 1 => "Program", - 2 => "Tv-Priority", - 3 => "Av-Priority", - 4 => "Manual", - 5 => "A-DEP" ), - -28 => array( 'Name' => "Flash Activity", - 0 => "Flash Did Not Fire", - 1 => "Flash Fired" ), - -32 => array( 'Name' => "Focus Mode", - 0 => "Focus Mode: Single", - 1 => "Focus Mode: Continuous" ) - -); - -/****************************************************************************** -* End of Global Variable: Canon_Camera_Settings_1_Tag_Values -******************************************************************************/ - - - - -/****************************************************************************** -* Global Variable: Canon_Camera_Settings_2_Tag_Values -* -* Contents: This global variable provides definitions for the Canon Camera -* Settings 2 Makernote tag, indexed by their offset. -* -******************************************************************************/ - -$GLOBALS[ "Canon_Camera_Settings_2_Tag_Values" ] = array( - -7 => array ( 'Name' => "White Balance", - 0 => "Auto", - 1 => "Sunny", - 2 => "Cloudy", - 3 => "Tungsten", - 4 => "Flourescent", - 5 => "Flash", - 6 => "Custom" ), - -15 => array( 'Name' => "Flash Bias", - 0xffc0 => "-2 EV", - 0xffcc => "-1.67 EV", - 0xffd0 => "-1.5 EV", - 0xffd4 => "-1.33 EV", - 0xffe0 => "-1 EV", - 0xffec => "-0.67 EV", - 0xfff0 => "-0.5 EV", - 0xfff4 => "-0.33 EV", - 0x0000 => "0 EV", - 0x000c => "0.33 EV", - 0x0010 => "0.5 EV", - 0x0014 => "0.67 EV", - 0x0020 => "1 EV", - 0x002c => "1.33 EV", - 0x0030 => "1.5 EV", - 0x0034 => "1.67 EV", - 0x0040 => "2 EV" ), -); - -/****************************************************************************** -* End of Global Variable: Canon_Camera_Settings_2_Tag_Values -******************************************************************************/ - - - - - - -/****************************************************************************** -* Global Variable: Canon_Custom_Functions_Tag_Values -* -* Contents: This global variable provides definitions for the Canon Custom -* Functions Makernote tag, indexed by their offset. -* -******************************************************************************/ - -$GLOBALS[ "Canon_Custom_Functions_Tag_Values" ] = array( - -1 => array ( 'Name' => "Long Exposure Noise Reduction", - 0 => "Off", - 1 => "On" ), - -2 => array ( 'Name' => "Shutter/Auto Exposure-lock buttons", - 0 => "AF/AE lock", - 1 => "AE lock/AF", - 2 => "AF/AF lock", - 3 => "AE+release/AE+AF" ), - -3 => array ( 'Name' => "Mirror lockup", - 0 => "Disable", - 1 => "Enable" ), - -4 => array ( 'Name' => "Tv/Av and exposure level", - 0 => "1/2 stop", - 1 => "1/3 stop" ), - -5 => array ( 'Name' => "AF-assist light", - 0 => "On (Auto)", - 1 => "Off" ), - -6 => array ( 'Name' => "Shutter speed in Av mode", - 0 => "Automatic", - 1 => "1/200 (fixed)" ), - -7 => array ( 'Name' => "Auto-Exposure Bracketting sequence/auto cancellation", - 0 => "0,-,+ / Enabled", - 1 => "0,-,+ / Disabled", - 2 => "-,0,+ / Enabled", - 3 => "-,0,+ / Disabled" ), - -8 => array ( 'Name' => "Shutter Curtain Sync", - 0 => "1st Curtain Sync", - 1 => "2nd Curtain Sync" ), - -9 => array ( 'Name' => "Lens Auto-Focus stop button Function Switch", - 0 => "AF stop", - 1 => "Operate AF", - 2 => "Lock AE and start timer" ), - -10 => array ( 'Name' => "Auto reduction of fill flash", - 0 => "Enable", - 1 => "Disable" ), - -11 => array ( 'Name' => "Menu button return position", - 0 => "Top", - 1 => "Previous (volatile)", - 2 => "Previous" ), - -12 => array ( 'Name' => "SET button function when shooting", - 0 => "Not Assigned", - 1 => "Change Quality", - 2 => "Change ISO Speed", - 3 => "Select Parameters" ), - -13 => array ( 'Name' => "Sensor cleaning", - 0 => "Disable", - 1 => "Enable" ) - - -); - -/****************************************************************************** -* End of Global Variable: Canon_Custom_Functions_Tag_Values -******************************************************************************/ - - -?> diff --git a/jpeg_metadata_tk/Makernotes/casio.php b/jpeg_metadata_tk/Makernotes/casio.php deleted file mode 100644 index d1cd74e..0000000 --- a/jpeg_metadata_tk/Makernotes/casio.php +++ /dev/null @@ -1,575 +0,0 @@ - 1.11 : changed get_Casio_Makernote_Html to allow thumbnail links to work when -* toolkit is portable across directories -* -* URL: http://electronics.ozhiker.com -* -* Copyright: Copyright Evan Hunter 2004 -* This file may be used freely for non-commercial purposes.For -* commercial uses please contact the author: evan@ozhiker.com -* -******************************************************************************/ - - -// Add the parser and interpreter functions to the list of Makernote parsers and interpreters. - -$GLOBALS['Makernote_Function_Array']['Read_Makernote_Tag'][] = "get_Casio_Makernote"; -$GLOBALS['Makernote_Function_Array']['get_Makernote_Text_Value'][] = "get_Casio_Text_Value"; -$GLOBALS['Makernote_Function_Array']['Interpret_Makernote_to_HTML'][] = "get_Casio_Makernote_Html"; - -include_once dirname(__FILE__) .'/../pjmt_utils.php'; // Change: as of version 1.11 - added to allow directory portability - - -/****************************************************************************** -* -* Function: get_Casio_Makernote -* -* Description: Decodes the Makernote tag and returns the new tag with the decoded -* information attached. Returns false if this is not a makernote -* that can be processed with this script -* -* Parameters: Makernote_Tag - the element of an EXIF array containing the -* makernote, as returned from get_EXIF_JPEG -* EXIF_Array - the entire EXIF array containing the -* makernote, as returned from get_EXIF_JPEG, in -* case more information is required for decoding -* filehnd - an open file handle for the file containing the -* makernote - does not have to be positioned at the -* start of the makernote -* Make_Field - The contents of the EXIF Make field, to aid -* determining whether this script can decode -* the makernote -* -* -* Returns: Makernote_Tag - the Makernote_Tag from the parameters, but -* modified to contain the decoded information -* FALSE - If this script could not decode the makernote, or if -* an error occured in decoding -* -******************************************************************************/ - -function get_Casio_Makernote( $Makernote_Tag, $EXIF_Array, $filehnd, $Make_Field ) -{ - - // Check if the Make Field contains the word Casio - if ( stristr( $Make_Field, "Casio" ) === FALSE ) - { - return FALSE; - } - - - if ( substr( $Makernote_Tag['Data'],0 , 6 ) == "QVC\x00\x00\x00" ) - { - - // Seek to the start of the IFD - fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] + 6 ); - - $Makernote_Tag['ByteAlign'] = "MM"; - - // Read the IFD(s) into an array - $Makernote_Tag['Decoded Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'], $Makernote_Tag['ByteAlign'], "Casio Type 2" ); - - // Save some information into the Tag element to aid interpretation - $Makernote_Tag['Decoded'] = TRUE; - $Makernote_Tag['Makernote Type'] = "Casio Type 2"; - $Makernote_Tag['Makernote Tags'] = "Casio Type 2"; - - // Return the new tag - return $Makernote_Tag; - } - else - { - // Seek to the start of the IFD - fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] + 0 ); - - $Makernote_Tag['ByteAlign'] = "MM"; - - // Read the IFD(s) into an array - $Makernote_Tag['Decoded Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'], $Makernote_Tag['ByteAlign'], "Casio Type 1" ); - - // Save some information into the Tag element to aid interpretation - $Makernote_Tag['Decoded'] = TRUE; - $Makernote_Tag['Makernote Type'] = "Casio Type 1"; - $Makernote_Tag['Makernote Tags'] = "Casio Type 1"; - - // Return the new tag - return $Makernote_Tag; - } -} - -/****************************************************************************** -* End of Function: get_Casio_Makernote -******************************************************************************/ - - - - -/****************************************************************************** -* -* Function: get_Casio_Text_Value -* -* Description: Provides a text value for any tag marked as special for makernotes -* that this script can decode. Returns false if this is not a makernote -* that can be processed with this script -* -* Parameters: Exif_Tag - the element of an the Makernote array containing the -* tag in question, as returned from get_Casio_Makernote -* Tag_Definitions_Name - The name of the Tag Definitions group -* within the global array IFD_Tag_Definitions -* -* -* Returns: output - the text value for the tag -* FALSE - If this script could not decode the makernote, or if -* an error occured in decoding -* -******************************************************************************/ - -function get_Casio_Text_Value( $Exif_Tag, $Tag_Definitions_Name ) -{ - - // Check that this tag uses the Casio tag definitions, otherwise it can't be decoded here - if ( $Tag_Definitions_Name == "Casio Type 2" ) - { - // Tag Uses Casio Type 2 Tag definitions - // Process the tag according to it's tag number - if ( $Exif_Tag['Tag Number'] == 0x001D ) - { - return $Exif_Tag['Data'][0]/10 . $Exif_Tag['Units']; - } - else - { - return FALSE; - } - } - else if ( $Tag_Definitions_Name == "Casio Type 1" ) - { - // Tag Uses Casio Type 1 Tags - return FALSE; - } - else - { - // Tag does NOT use Casio Tag definitions - return FALSE; - } - - // Shouldn't get here - return FALSE; - -} - -/****************************************************************************** -* End of Function: get_Casio_Text_Value -******************************************************************************/ - - - - - - - -/****************************************************************************** -* -* Function: get_Casio_Makernote_Html -* -* Description: Attempts to interpret a makernote into html. Returns false if -* it is not a makernote that can be processed with this script -* -* Parameters: Makernote_Tag - the element of an EXIF array containing the -* makernote, as returned from get_EXIF_JPEG -* filename - the name of the JPEG file being processed ( used -* by scripts which display embedded thumbnails) -* -* -* Returns: output - the html representing the makernote -* FALSE - If this script could not interpret the makernote, or if -* an error occured in decoding -* -******************************************************************************/ - -function get_Casio_Makernote_Html( $Makernote_tag, $filename ) -{ - // Check that this tag uses the Casio tags, otherwise it can't be interpreted here - if ( ( $Makernote_tag['Makernote Type'] != "Casio Type 1" ) && - ( $Makernote_tag['Makernote Type'] != "Casio Type 2" ) ) - { - // Not Casio tags - can't interpret with this function - return FALSE; - } - - // Casio Thumbnail (Tag 4) - if ( ( array_key_exists( 4, $Makernote_tag['Decoded Data'][0] ) ) && - ( $Makernote_tag['Makernote Tags'] == "Casio Type 2" ) ) - { - // Change: as of version 1.11 - Changed to make thumbnail link portable across directories - // Build the path of the thumbnail script and its filename parameter to put in a url - $link_str = get_relative_path( dirname(__FILE__) . "/get_casio_thumb.php" , getcwd ( ) ); - $link_str .= "?filename="; - $link_str .= get_relative_path( $filename, dirname(__FILE__) ); - - // Add thumbnail link to html - $Makernote_tag['Decoded Data'][0][4]['Text Value'] = "\n"; - $Makernote_tag['Decoded Data'][0][4]['Type'] = "String"; - } - - - // Casio Thumbnail (Tag 8192) - if ( ( array_key_exists( 8192, $Makernote_tag['Decoded Data'][0] ) ) && - ( $Makernote_tag['Makernote Tags'] == "Casio Type 2" ) ) - { - // Change: as of version 1.11 - Changed to make thumbnail link portable across directories - // Build the path of the thumbnail script and its filename parameter to put in a url - $link_str = get_relative_path( dirname(__FILE__) . "/.." . "/get_casio_thumb.php" , getcwd ( ) ); - $link_str .= "?filename="; - $link_str .= get_relative_path( $filename, dirname(__FILE__) . "/.." ); - - // Add thumbnail link to html - $Makernote_tag['Decoded Data'][0][8192]['Text Value'] = "\n"; - $Makernote_tag['Decoded Data'][0][8192]['Type'] = "String"; - } - - - // Check if there are two thumbnail offset tags - if ( ( array_key_exists( 4, $Makernote_tag['Decoded Data'][0] ) ) && - ( array_key_exists( 8192, $Makernote_tag['Decoded Data'][0] ) ) ) - { - // There are two copies of the thumbnail offset - Remove one - array_splice( $Makernote_tag['Decoded Data'][0], 4, 1); - } - - - // Interpret the IFD and return the html - return interpret_IFD( $Makernote_tag['Decoded Data'][0], $filename ); - -} - -/****************************************************************************** -* End of Function: get_Casio_Makernote_Html -******************************************************************************/ - - - - - - -/****************************************************************************** -* Global Variable: IFD_Tag_Definitions, Casio Type 1 -* -* Contents: This global variable provides definitions of the known Casio Type 1 -* Makernote tags, indexed by their tag number. -* -******************************************************************************/ - - -$GLOBALS[ "IFD_Tag_Definitions" ]["Casio Type 1"] = array( - -1 => array( 'Name' => "Recording Mode", - 'Type' => "Lookup", - 1 => "Single Shutter", - 2 => "Panorama", - 3 => "Night Scene", - 4 => "Portrait", - 5 => "Landscape" ), - -2 => array( 'Name' => "Quality", - 'Type' => "Lookup", - 1 => "Economy", - 2 => "Normal", - 3 => "Fine" ), - -3 => array( 'Name' => "Focusing Mode", - 'Type' => "Lookup", - 2 => "Macro", - 3 => "Auto Focus", - 4 => "Manual Focus", - 5 => "Infinity" ), - -4 => array( 'Name' => "Flash Mode", - 'Type' => "Lookup", - 1 => "Auto", - 2 => "On", - 3 => "Off", - 4 => "Off" ), - -5 => array( 'Name' => "Flash Intensity", - 'Type' => "Lookup", - 11 => "Weak", - 13 => "Normal", - 15 => "Strong" ), - -6 => array( 'Name' => "Object Distance", - 'Type' => "Numeric", - 'Units' => "mm" ), - -7 => array( 'Name' => "White Balance", - 'Type' => "Lookup", - 1 => "Auto", - 2 => "Tungsten", - 3 => "Daylight", - 4 => "Flourescent", - 5 => "Shade", - 129 => "Manual" ), - -10 => array( 'Name' => "Digital Zoom", - 'Type' => "Lookup", - 0x10000 => "Off", - 0x10001 => "2x Digital Zoom", - 0x20000 => "2x Digital Zoom", - 0x40000 => "4x Digital Zoom" ), - -11 => array( 'Name' => "Sharpness", - 'Type' => "Lookup", - 0 => "Normal", - 1 => "Soft", - 2 => "Hard" ), - -12 => array( 'Name' => "Contrast", - 'Type' => "Lookup", - 0 => "Normal", - 1 => "Low", - 2 => "High" ), - -13 => array( 'Name' => "Saturation", - 'Type' => "Lookup", - 0 => "Normal", - 1 => "Low", - 2 => "High" ), - -20 => array( 'Name' => "CCD Sensitivity", - 'Type' => "Lookup", - 64 => "Normal", - 125 => "+1.0", - 250 => "+2.0", - 244 => "+3.0", - 80 => "Normal (ISO 80 equivalent)", - 100 => "High" ), - -); - -/****************************************************************************** -* End of Global Variable: IFD_Tag_Definitions, Casio Type 1 -******************************************************************************/ - - - - - - -/****************************************************************************** -* Global Variable: IFD_Tag_Definitions, Casio Type 2 -* -* Contents: This global variable provides definitions of the known Casio Type 2 -* Makernote tags, indexed by their tag number. -* -******************************************************************************/ - -$GLOBALS[ "IFD_Tag_Definitions" ]["Casio Type 2"] = array( - -0x0002 => array( 'Name' => "Preview Thumbnail Dimensions", - 'Type' => "Numeric", - 'Units' => "(x,y pixels)" ), - -0x0003 => array( 'Name' => "Preview Thumbnail Size", - 'Type' => "Numeric", - 'Units' => "bytes" ), - -0x0004 => array( 'Name' => "Preview Thumbnail", // thumbnail offset - 'Type' => "Numeric" ), - - -0x0008 => array( 'Name' => "Quality Mode", - 'Type' => "Lookup", - 1 => "Fine", - 2 => "Super Fine" ), - -0x0009 => array( 'Name' => "Image Size", - 'Type' => "Lookup", - 20 => "2288 x 1712 pixels", - 36 => "3008 x 2008 pixels", - 5 => "2048 x 1536 pixels", - 4 => "1600 x 1200 pixels", - 21 => "2592 x 1944 pixels", - 0 => "640 x 480 pixels", - 22 => "2304 x 1728 pixels" ), - -0x000D => array( 'Name' => "Focus Mode", - 'Type' => "Lookup", - 0 => "Normal", - 1 => "Macro" ), - - -0x0014 => array( 'Name' => "Iso Sensitivity", - 'Type' => "Lookup", - 3 => "50", - 4 => "64", - 6 => "100", - 9 => "200" ), - - -0x0019 => array( 'Name' => "White Balance", - 'Type' => "Lookup", - 0 => "Auto", - 1 => "Daylight", - 2 => "Shade", - 3 => "Tungsten", - 4 => "Fluorescent", - 5 => "Manual" ), - -0x001D => array( 'Name' => "Focal Length", - 'Type' => "Special", - 'Units' => "mm" ), - -0x001F => array( 'Name' => "Saturation", - 'Type' => "Lookup", - 0 => "-1", - 1 => "Normal", - 2 => "+1", ), - -0x0020 => array( 'Name' => "Contrast", - 'Type' => "Lookup", - 0 => "-1", - 1 => "Normal", - 2 => "+1", ), - -0x0021 => array( 'Name' => "Sharpness", - 'Type' => "Lookup", - 0 => "-1", - 1 => "Normal", - 2 => "+1", ), - - -0x0e00 => array( 'Name' => "Print Image Matching Info", - 'Type' => "PIM" ), - - -0x2000 => array( 'Name' => "Casio Preview Thumbnail", // thumbnail offset - 'Type' => "String" ), - - - -0x2011 => array( 'Name' => "White Balance Bias", - 'Type' => "Numeric" ), - - -0x2012 => array( 'Name' => "White Balance", - 'Type' => "Lookup", - 12 => "Flash", - 0 => "Manual", - 1 => "Auto?", - 4 => "Flash?", ), - -0x2022 => array( 'Name' => "Object Distance", - 'Type' => "Numeric", - 'Units' => "mm" ), - - -0x2034 => array( 'Name' => "Flash Distance", - 'Type' => "Numeric", - 'Units' => " (0=Off)" ), - -0x3000 => array( 'Name' => "Record Mode", - 'Type' => "Lookup", - 2 => "Normal Mode" ), - -0x3001 => array( 'Name' => "Self Timer?", - 'Type' => "Lookup", - 1 => "Off?" ), - - -0x3002 => array( 'Name' => "Quality", - 'Type' => "Lookup", - 3 => "Fine" ), - -0x3003 => array( 'Name' => "Focus Mode", - 'Type' => "Lookup", - 6 => "Multi-Area Auto Focus", - 1 => "Fixation" ), - - -0x3006 => array( 'Name' => "Time Zone", - 'Type' => "String" ), - - -0x3007 => array( 'Name' => "Bestshot Mode", - 'Type' => "Lookup", - 0 => "Off", - 1 => "On?" ), - - -0x3014 => array( 'Name' => "CCD ISO Sensitivity", - 'Type' => "Numeric" ), - - - -0x3015 => array( 'Name' => "Colour Mode", - 'Type' => "Lookup", - 0 => "Off" ), - - -0x3016 => array( 'Name' => "Enhancement", - 'Type' => "Lookup", - 0 => "Off" ), - -0x3017 => array( 'Name' => "Filter", - 'Type' => "Lookup", - 0 => "Off" ), - - -); - -/****************************************************************************** -* End of Global Variable: IFD_Tag_Definitions, Casio Type 2 -******************************************************************************/ - - - - - - - - - - - - - - - - - - -?> \ No newline at end of file diff --git a/jpeg_metadata_tk/Makernotes/epson.php b/jpeg_metadata_tk/Makernotes/epson.php deleted file mode 100644 index 16057bb..0000000 --- a/jpeg_metadata_tk/Makernotes/epson.php +++ /dev/null @@ -1,119 +0,0 @@ - diff --git a/jpeg_metadata_tk/Makernotes/fujifilm.php b/jpeg_metadata_tk/Makernotes/fujifilm.php deleted file mode 100644 index 5d135cd..0000000 --- a/jpeg_metadata_tk/Makernotes/fujifilm.php +++ /dev/null @@ -1,344 +0,0 @@ - array( 'Name' => "Version", - 'Type' => "String" ), - -4096 => array( 'Name' => "Quality", - 'Type' => "String" ), - -4097 => array( 'Name' => "Sharpness", - 'Type' => "Lookup", - 1 => "Softest", - 2 => "Soft", - 3 => "Normal", - 4 => "Hard", - 5 => "Hardest" ), - -4098 => array( 'Name' => "White Balance", - 'Type' => "Lookup", - 0 => "Auto", - 256 => "Daylight", - 512 => "Cloudy", - 768 => "DaylightColour-fluorescence", - 769 => "DaywhiteColour-fluorescence", - 770 => "White-fluorescence", - 1024 => "Incandenscense", - 3840 => "Custom white balance" ), - -4099 => array( 'Name' => "Colour Saturation", - 'Type' => "Lookup", - 0 => "Normal", - 256 => "High", - 512 => "Low" ), - -4100 => array( 'Name' => "Tone (Contrast)", - 'Type' => "Lookup", - 0 => "Normal", - 256 => "High", - 512 => "Low" ), - -4112 => array( 'Name' => "Flash Mode", - 'Type' => "Lookup", - 0 => "Auto", - 1 => "On", - 2 => "Off", - 3 => "Red-eye Reduction" ), - -4113 => array( 'Name' => "Flash Strength", - 'Type' => "Numeric", - 'Units' => "EV" ), - -4128 => array( 'Name' => "Macro", - 'Type' => "Lookup", - 0 => "Off", - 1 => "On" ), - -4129 => array( 'Name' => "Focus Mode", - 'Type' => "Lookup", - 0 => "Auto Focus", - 1 => "Manual Focus" ), - -4144 => array( 'Name' => "Slow Sync", - 'Type' => "Lookup", - 0 => "Off", - 1 => "On" ), - -4145 => array( 'Name' => "Picture Mode", - 'Type' => "Lookup", - 0 => "Auto", - 1 => "Portrait Scene", - 2 => "Landscape Scene", - 4 => "Sports Scene", - 5 => "Night Scene", - 6 => "Program AE", - 256 => "Aperture priority AE", - 512 => "Shutter priority AE", - 768 => "Manual Exposure" ), - -4352 => array( 'Name' => "Continuous taking or auto bracketing mode", - 'Type' => "Lookup", - 0 => "Off", - 1 => "On" ), - -4864 => array( 'Name' => "Blur Warning", - 'Type' => "Lookup", - 0 => "No Blur Warning", - 1 => "Blur Warning" ), - -4865 => array( 'Name' => "Focus warning", - 'Type' => "Lookup", - 0 => "Auto Focus Good", - 1 => "Out of Focus" ), - -4866 => array( 'Name' => "Auto Exposure Warning", - 'Type' => "Lookup", - 0 => "Auto Exposure Good", - 1 => "Over exposure (>1/1000s,F11)" ) - -); - -/****************************************************************************** -* End of Global Variable: IFD_Tag_Definitions, Fujifilm -******************************************************************************/ - - - - - -?> diff --git a/jpeg_metadata_tk/Makernotes/konica_minolta.php b/jpeg_metadata_tk/Makernotes/konica_minolta.php deleted file mode 100644 index 17bbfbd..0000000 --- a/jpeg_metadata_tk/Makernotes/konica_minolta.php +++ /dev/null @@ -1,745 +0,0 @@ - array ( 'Name' => "Exposure Mode", - 'Type' => "Lookup", - 0 => "P", - 1 => "A", - 2 => "S", - 3 => "M" ), - -3 => array ( 'Name' => "Flash Mode", - 'Type' => "Lookup", - 0 => "Normal", - 1 => "Red-eye reduction", - 2 => "Rear flash sync", - 3 => "Wireless" ), - -4 => array ( 'Name' => "White Balance", - 'Type' => "Lookup", - 0 => "Auto", - 1 => "Daylight", - 2 => "Cloudy", - 3 => "Tungsten", - 5 => "Custom", - 7 => "Fluorescent", - 8 => "Fluorescent 2", - 11 => "Custom 2", - 12 => "Custom 3" ), - -5 => array ( 'Name' => "Image Size", - 'Type' => "Lookup", - 0 => "2560 x 1920 (2048x1536 - DiMAGE 5 only)", - 1 => "1600 x 1200", - 2 => "1280 x 960", - 3 => "640 x 480" ), - - -6 => array ( 'Name' => "Image Quality", - 'Type' => "Lookup", - 0 => "Raw", - 1 => "Super Fine", - 2 => "Fine", - 3 => "Standard", - 4 => "Economy", - 5 => "Extra Fine" ), - -7 => array ( 'Name' => "Shooting Mode", - 'Type' => "Lookup", - 0 => "Single", - 1 => "Continuous", - 2 => "Self-timer", - 4 => "Bracketing", - 5 => "Interval", - 6 => "UHS Continuous", - 7 => "HS Continuous" ), - - -8 => array ( 'Name' => "Metering Mode", - 'Type' => "Lookup", - 0 => "Multi-Segment", - 1 => "Centre Weighted", - 2 => "Spot" ), - - -9 => array ( 'Name' => "Apex Film Speed Value", - 'Type' => "Special" ), - -// 09 FilmSpeed , APEX Film Speed Value , Speed value = x/8-1 , ISO= (2^(x/8-1))*3.125 - - -10 => array ( 'Name' => "Apex Shutter Speed Time Value", - 'Type' => "Special", - 'Units' => "Seconds?" ), - -// APEX Time Value , Time value = x/8-6 , ShutterSpeed = 2^( (48-x)/8 ), ! Due to rounding error x=8 should be displayed as 30 sec. - -11 => array ( 'Name' => "Apex Aperture Value", - 'Type' => "Special" ), - -// APEX Aperture Value ApertureValue = x/8-1 , Aperture = 2^( x/16-0.5 ) - - -12 => array ( 'Name' => "Macro Mode", - 'Type' => "Lookup", - 0 => "Off", - 1 => "On" ), - -13 => array ( 'Name' => "Digital Zoom", - 'Type' => "Lookup", - 0 => "Off", - 1 => "Electronic magnification was used", - 2 => "Digital zoom 2x" ), - - -14 => array ( 'Name' => "Exposure Compensation", - 'Type' => "Special", - 'Units' => "EV" ), - -// EV = x/3 -2 Exposure compensation in EV - - -15 => array ( 'Name' => "Bracket Step", - 'Type' => "Lookup", - 0 => "1/3 EV", - 1 => "2/3 EV", - 2 => "1 EV" ), - - -17 => array ( 'Name' => "Interval Length", - 'Type' => "Special", - 'Units' => "Min" ), - -// interval is x+1 min (used with interval mode) - - -18 => array ( 'Name' => "Interval Number", - 'Type' => "Numeric", - 'Units' => "frames" ), - -19 => array ( 'Name' => "Focal Length", - 'Type' => "Special", - 'Units' => "mm" ), - -// x / 256 is real focal length in mm , x / 256 * 3.9333 is 35-mm equivalent - - -20 => array ( 'Name' => "Focus Distance", - 'Type' => "Numeric", - 'Units' => "mm ( 0 = Infinity)" ), - - -21 => array ( 'Name' => "Flash Fired", - 'Type' => "Lookup", - 0 => "No", - 1 => "Yes" ), - -22 => array ( 'Name' => "Date", - 'Type' => "Special" ), - -// yyyymmdd , year = x/65536 , month = x/256-x/65536*256 , day = x%256 - -23 => array ( 'Name' => "Time", - 'Type' => "Special" ), - -// hhhhmmss , hour = x/65536 , minute = x/256-x/65536*256 , second = x%256 - - -24 => array ( 'Name' => "Max Aperture at this focal length", - 'Type' => "Special" ), - -// Fno = 2^(x/16-0.5) - - -27 => array ( 'Name' => "File Number Memory", - 'Type' => "Lookup", - 0 => "Off", - 1 => "On" ), - -28 => array ( 'Name' => "Last File Number", - 'Type' => "Numeric", - 'Units' => " ( 0 = File Number Memory is Off)" ), - - -29 => array ( 'Name' => "White Balance Red", - 'Type' => "Special" ), - -// x/256 - red white balance coefficient used for this picture - - -30 => array ( 'Name' => "White Balance Green", - 'Type' => "Special" ), - -// x/256 - green white balance coefficient used for this picture - -31 => array ( 'Name' => "White Balance Blue", - 'Type' => "Special" ), - -// x/256 - blue white balance coefficient used for this picture - - -32 => array ( 'Name' => "Saturation", - 'Type' => "Special" ), - -// x-3 = saturation - - -33 => array ( 'Name' => "Contrast", - 'Type' => "Special" ), - -// x-3 - contrast - - -34 => array ( 'Name' => "Sharpness", - 'Type' => "Lookup", - 0 => "Hard", - 1 => "Normal", - 2 => "Soft" ), - - -35 => array ( 'Name' => "Subject Program", - 'Type' => "Lookup", - 0 => "none", - 1 => "portrait", - 2 => "text", - 3 => "night portrait", - 4 => "sunset", - 5 => "sports action" ), - - -36 => array ( 'Name' => "Flash Compensation", - 'Type' => "Special", - 'Units' => "EV" ), - -// (x-6)/3 = flash compensation in EV - - -37 => array ( 'Name' => "ISO Setting", - 'Type' => "Lookup", - 0 => "100", - 1 => "200", - 2 => "400", - 3 => "800", - 4 => "auto", - 5 => "64" ), - - -38 => array ( 'Name' => "Camera Model", - 'Type' => "Lookup", - 0 => "DiMAGE 7", - 1 => "DiMAGE 5", - 2 => "DiMAGE S304", - 3 => "DiMAGE S404", - 4 => "DiMAGE 7i", - 5 => "DiMAGE 7Hi", - 6 => "DiMAGE A1", - 7 => "DiMAGE S414" ), - - -39 => array ( 'Name' => "Interval Mode", - 'Type' => "Lookup", - 0 => "Still Image", - 1 => "Time-lapse Movie" ), - - -40 => array ( 'Name' => "Folder Name", - 'Type' => "Lookup", - 0 => "Standard Form", - 1 => "Data Form" ), - - -41 => array ( 'Name' => "Color Mode", - 'Type' => "Lookup", - 0 => "Natural Color", - 1 => "Black & White", - 2 => "Vivid Color", - 3 => "Solarization", - 4 => "Adobe RGB" ), - - -42 => array ( 'Name' => "Color Filter", - 'Type' => "Special" ), - -// x-3 = color filter - - -43 => array ( 'Name' => "Black & White Filter", - 'Type' => "Numeric" ), - - - -44 => array ( 'Name' => "Internal Flash", - 'Type' => "Lookup", - 0 => "Not Fired", - 1 => "Fired" ), - - - -45 => array ( 'Name' => "Apex Brightness Value", - 'Type' => "Special" ), - -// Brightness Value = x/8-6 - - - - -46 => array ( 'Name' => "Spot Focus Point X Coordinate", - 'Type' => "Numeric" ), - - - -47 => array ( 'Name' => "Spot Focus Point Y Coordinate", - 'Type' => "Numeric" ), - - - -48 => array ( 'Name' => "Wide Focus Zone", - 'Type' => "Lookup", - 0 => "No Zone or AF Failed", - 1 => "Center Zone (Horizontal Orientation)", - 2 => "Center Zone (Vertical Orientation)", - 3 => "Left Zone", - 4 => "Right Zone" ), - - -49 => array ( 'Name' => "Focus Mode", - 'Type' => "Lookup", - 0 => "Auto Focus", - 1 => "Manual Focus" ), - - -50 => array ( 'Name' => "Focus Area", - 'Type' => "Lookup", - 0 => "Wide Focus (normal)", - 1 => "Spot Focus" ), - - -51 => array ( 'Name' => "DEC Switch Position", - 'Type' => "Lookup", - 0 => "Exposure", - 1 => "Contrast", - 2 => "Saturation", - 3 => "Filter" ), - - - -); - -/****************************************************************************** -* End of Global Variable: Minolta_Camera_Setting_Definitions -******************************************************************************/ - - - - -?> diff --git a/jpeg_metadata_tk/Makernotes/kyocera.php b/jpeg_metadata_tk/Makernotes/kyocera.php deleted file mode 100644 index bb5f17e..0000000 --- a/jpeg_metadata_tk/Makernotes/kyocera.php +++ /dev/null @@ -1,241 +0,0 @@ - array( 'Name' => "Kyocera Proprietory Format Thumbnail", - 'Type' => "Unknown" ), - -0x0E00 => array( 'Name' => "Print Image Matching Info", - 'Type' => "PIM" ), - -); - -/****************************************************************************** -* End of Global Variable: IFD_Tag_Definitions, Kyocera -******************************************************************************/ - - - - - - - - -?> diff --git a/jpeg_metadata_tk/Makernotes/nikon.php b/jpeg_metadata_tk/Makernotes/nikon.php deleted file mode 100644 index b860d51..0000000 --- a/jpeg_metadata_tk/Makernotes/nikon.php +++ /dev/null @@ -1,731 +0,0 @@ - array( 'Name' => "Quality", - 'Description' => "1: VGA Basic, 2: VGA Normal, 3: VGA Fine, 4: SXGA Basic, 5: SXGA Normal, 6: SXGA Fine", - 'Type' => "Lookup", - 1 => "VGA (640x480) Basic", - 2 => "VGA (640x480) Normal", - 3 => "VGA (640x480) Fine", - 4 => "SXGA (1280x960) Basic", - 5 => "SXGA (1280x960) Normal", - 6 => "SXGA (1280x960) Fine", - 7 => "Unknown, Possibly XGA (1024x768) Basic", - 8 => "Unknown, Possibly XGA (1024x768) Basic", - 9 => "Unknown, Possibly XGA (1024x768) Basic", - 10 => "UXGA (1600x1200) Basic", - 11 => "UXGA (1600x1200) Normal", - 12 => "UXGA (1600x1200) Fine" ), - -4 => array( 'Name' => "Colour Mode", - 'Description' => "1: Colour, 2: Monochrome.", - 'Type' => "Lookup", - 1 => "Colour", - 2 => "Monochrome" ), - -5 => array( 'Name' => "Image Adjustment", - 'Description' => "0: Normal, 1: Bright+, 2: Bright-, 3: Contrast+, 4: Contrast-.", - 'Type' => "Lookup", - 0 => "Normal", - 1 => "Bright+", - 2 => "Bright-", - 3 => "Contrast+", - 4 => "Contrast-" ), - -6 => array( 'Name' => "CCD Sensitivity", - 'Description' => "0: ISO80, 2: ISO160, 4: ISO320, 5: ISO100", - 'Type' => "Lookup", - 0 => "ISO 80", - 2 => "ISO 160", - 4 => "ISO 320", - 5 => "ISO 100" ), - -7 => array( 'Name' => "White Balance", - 'Description' => "0: Auto, 1: Preset, 2: Daylight, 3: Incandescense, 4: Fluorescence, 5: Cloudy, 6: SpeedLight", - 'Type' => "Lookup", - 0 => "Auto", - 1 => "Preset", - 2 => "Daylight", - 3 => "Incandescense", - 4 => "Flourescence", - 5 => "Cloudy", - 6 => "Speedlight" ), - -8 => array( 'Name' => "Focus", - 'Description' => "If infinite focus, value is '1/0'.", - 'Type' => "Numeric" ), - -10 => array( 'Name' => "Digital Zoom", - 'Description' => "'160/100' means 1.6x digital zoom, '0/100' means no digital zoom (optical zoom only).", - 'Type' => "Numeric" ), - -11 => array( 'Name' => "Converter", - 'Description' => "If Fisheye Converter is used, value is 1", - 'Type' => "Lookup", - 0 => "No Converter Used", - 1 => "Fish-eye Converter Used" ) - -); - -/****************************************************************************** -* End of Global Variable: IFD_Tag_Definitions, Nikon Type 1 -******************************************************************************/ - - - - - -/****************************************************************************** -* Global Variable: IFD_Tag_Definitions, Nikon Type 3 -* -* Contents: This global variable provides definitions of the known Nikon Type 3 -* Makernote tags, indexed by their tag number. -* -******************************************************************************/ - -$GLOBALS[ "IFD_Tag_Definitions" ]["Nikon Type 3"] = array( - - -1 => array( 'Name' => "Nikon Makernote Version", - 'Type' => "Special" ), - -2 => array( 'Name' => "ISO Speed Used", - 'Type' => "Special" ), - -3 => array( 'Name' => "Colour Mode", - 'Type' => "String" ), - -4 => array( 'Name' => "Quality", - 'Type' => "String" ), - -5 => array( 'Name' => "White Balance", - 'Type' => "String" ), - -6 => array( 'Name' => "Sharpening", - 'Type' => "String" ), - -7 => array( 'Name' => "Focus Mode", - 'Type' => "String" ), - -8 => array( 'Name' => "Flash Setting", - 'Type' => "String" ), - -9 => array( 'Name' => "Auto Flash Mode", - 'Type' => "String" ), - -11 => array( 'Name' => "White Balance Bias Value", - 'Type' => "Numeric", - 'Units' => "(Units Approx: 100 Mired per increment)" ), - -12 => array( 'Name' => "White Balance Red, Blue Coefficients?", - 'Type' => "Numeric" ), - -15 => array( 'Name' => "ISO Selection?", - 'Type' => "String" ), - - -18 => array( 'Name' => "Flash Compensation", - 'Type' => "Lookup", - 0x06 => "+1.0 EV", - 0x04 => "+0.7 EV", - 0x03 => "+0.5 EV", - 0x02 => "+0.3 EV", - 0x00 => "0.0 EV", - 0xfe => "-0.3 EV", - 0xfd => "-0.5 EV", - 0xfc => "-0.7 EV", - 0xfa => "-1.0 EV", - 0xf8 => "-1.3 EV", - 0xf7 => "-1.5 EV", - 0xf6 => "-1.7 EV", - 0xf4 => "-2.0 EV", - 0xf2 => "-2.3 EV", - 0xf1 => "-2.5 EV", - 0xf0 => "-2.7 EV", - 0xee => "-3.0 EV" ), - -19 => array( 'Name' => "ISO Speed Requested", - 'Type' => "Special", - 'Units' => "(May be different to Speed Used when Auto ISO is on)" ), - - -22 => array( 'Name' => "Photo corner coordinates", - 'Type' => "Numeric", - 'Units' => "Pixels" ), - -24 => array( 'Name' => "Flash Bracket Compensation Applied", - 'Type' => "Lookup", - 0x06 => "+1.0 EV", - 0x04 => "+0.7 EV", - 0x03 => "+0.5 EV", - 0x02 => "+0.3 EV", - 0x00 => "0.0 EV", - 0xfe => "-0.3 EV", - 0xfd => "-0.5 EV", - 0xfc => "-0.7 EV", - 0xfa => "-1.0 EV", - 0xf8 => "-1.3 EV", - 0xf7 => "-1.5 EV", - 0xf6 => "-1.7 EV", - 0xf4 => "-2.0 EV", - 0xf2 => "-2.3 EV", - 0xf1 => "-2.5 EV", - 0xf0 => "-2.7 EV", - 0xee => "-3.0 EV" ), - -25 => array( 'Name' => "AE Bracket Compensation Applied", - 'Type' => "Numeric", - 'Units' => "EV" ), - -128 => array( 'Name' => "Image Adjustment?", - 'Type' => "String" ), - -129 => array( 'Name' => "Tone Compensation (Contrast)", - 'Type' => "String" ), - -130 => array( 'Name' => "Auxiliary Lens (Adapter)", - 'Type' => "String" ), - -131 => array( 'Name' => "Lens Type?", - 'Type' => "Lookup", - 6 => "Nikon D series Lens", - 14 => "Nikon G series Lens" ), - -132 => array( 'Name' => "Lens Min/Max Focal Length, Min/Max Aperture", - 'Type' => "Numeric", - 'Units' => " mm, mm, F#, F#" ), - -133 => array( 'Name' => "Manual Focus Distance?", - 'Type' => "Numeric"), - -134 => array( 'Name' => "Digital Zoom Factor?", - 'Type' => "Numeric" ), - -135 => array( 'Name' => "Flash Used", - 'Type' => "Lookup", - 0 => "Flash Not Used", - 9 => "Flash Fired" ), - -136 => array( 'Name' => "Auto Focus Area", - 'Description' => "byte 1 : AF Mode: 00 = single area, 01 = Dynamic Area, 02 = Closest Subject\n - byte 2 : AF Area Selected : 00 = Centre, 01 = Top, 02 = Bottom, 03 = Left, 04 = Right\n - byte 3 : Unknown, always zero\n - byte 4 : Properly focused Area(s) : bit 0 = Centre, bit 1 = Top, bit 2 = Bottom, bit 3 = Left, bit 4 = Right", - 'Type' => "Special" ), - -137 => array( 'Name' => "Bracketing & Shooting Mode", - 'Description' => "bit 0&1 (0 = single frame, 1 = continuous,2=timer, 3=remote timer? 4 = remote?\n - bit 4, Bracketing on or off\n - bit 6, white Balance Bracketing on", - 'Type' => "Special" ), - -141 => array( 'Name' => "Colour Mode", - 'Description' =>"1a = Portrait sRGB, 2 = Adobe RGB, 3a = Landscape sRGB", - 'Type' => "String" ), - -143 => array( 'Name' => "Scene Mode?", - 'Type' => "Numeric" ), - -144 => array( 'Name' => "Lighting Type", - 'Type' => "String" ), - -146 => array( 'Name' => "Hue Adjustment", - 'Type' => "Numeric", - 'Units' => "Degrees" ), - -148 => array( 'Name' => "Saturation?", - 'Type' => "Lookup", - -3 => "Black and White", - -2 => "-2", - -1 => "-1", - 0 => "Normal", - 1 => "+1", - 2 => "+2" ), - -149 => array( 'Name' => "Noise Reduction", - 'Type' => "String" ), - -167 => array( 'Name' => "Total Number of Shutter Releases for Camera", - 'Type' => "Numeric", - 'Units' => "Shutter Releases" ), - -169 => array( 'Name' => "Image optimisation", - 'Type' => "String" ), - -170 => array( 'Name' => "Saturation", - 'Type' => "String" ), - -171 => array( 'Name' => "Digital Vari-Program", - 'Type' => "String" ) - - -// Tags that exist but are unknown: 10, 13, 14, 16, 17, 23, 24, 138, 139, 145, -// 151, 152, 160, 162 163, 165, 166, 168 - - -); - -/****************************************************************************** -* End of Global Variable: IFD_Tag_Definitions, Nikon Type 3 -******************************************************************************/ - - - -?> diff --git a/jpeg_metadata_tk/Makernotes/olympus.php b/jpeg_metadata_tk/Makernotes/olympus.php deleted file mode 100644 index a9717b0..0000000 --- a/jpeg_metadata_tk/Makernotes/olympus.php +++ /dev/null @@ -1,486 +0,0 @@ - 1.11 : changed get_Olympus_Makernote_Html to allow thumbnail links to work when -* toolkit is portable across directories -* -* URL: http://electronics.ozhiker.com -* -* Copyright: Copyright Evan Hunter 2004 -* This file may be used freely for non-commercial purposes.For -* commercial uses please contact the author: evan@ozhiker.com -* -******************************************************************************/ - - - -// Add the parser and interpreter functions to the list of Makernote parsers and interpreters. - -$GLOBALS['Makernote_Function_Array']['Read_Makernote_Tag'][] = "get_Olympus_Makernote"; -$GLOBALS['Makernote_Function_Array']['get_Makernote_Text_Value'][] = "get_Olympus_Text_Value"; -$GLOBALS['Makernote_Function_Array']['Interpret_Makernote_to_HTML'][] = "get_Olympus_Makernote_Html"; - - - - -include_once dirname(__FILE__) .'/../pjmt_utils.php'; // Change: as of version 1.11 - added to allow directory portability - - - - -/****************************************************************************** -* -* Function: get_Olympus_Makernote -* -* Description: Decodes the Makernote tag and returns the new tag with the decoded -* information attached. Returns false if this is not a makernote -* that can be processed with this script -* -* Parameters: Makernote_Tag - the element of an EXIF array containing the -* makernote, as returned from get_EXIF_JPEG -* EXIF_Array - the entire EXIF array containing the -* makernote, as returned from get_EXIF_JPEG, in -* case more information is required for decoding -* filehnd - an open file handle for the file containing the -* makernote - does not have to be positioned at the -* start of the makernote -* Make_Field - The contents of the EXIF Make field, to aid -* determining whether this script can decode -* the makernote -* -* -* Returns: Makernote_Tag - the Makernote_Tag from the parameters, but -* modified to contain the decoded information -* FALSE - If this script could not decode the makernote, or if -* an error occured in decoding -* -******************************************************************************/ - -function get_Olympus_Makernote( $Makernote_Tag, $EXIF_Array, $filehnd, $Make_Field ) -{ - - // Check if the Make Field contains the word Olympus - if ( stristr( $Make_Field, "Olympus" ) === FALSE ) - { - return FALSE; - } - - // Check if the header exists at the start of the Makernote - if ( ( substr( $Makernote_Tag['Data'], 0, 7 ) != "OLYMP\x00\x01" ) && - ( substr( $Makernote_Tag['Data'], 0, 7 ) != "OLYMP\x00\x02" ) ) - { - // This isn't a Olympus Makernote, abort - return FALSE ; - } - - - - // Seek to the start of the IFD - fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] + 8 ); - - // Read the IFD(s) into an array - $Makernote_Tag['Decoded Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'], $Makernote_Tag['ByteAlign'], "Olympus" ); - - // Save some information into the Tag element to aid interpretation - $Makernote_Tag['Decoded'] = TRUE; - $Makernote_Tag['Makernote Type'] = "Olympus"; - $Makernote_Tag['Makernote Tags'] = "Olympus"; - - - // Return the new tag - return $Makernote_Tag; -} - -/****************************************************************************** -* End of Function: get_Olympus_Makernote -******************************************************************************/ - - - - - - - -/****************************************************************************** -* -* Function: get_Olympus_Text_Value -* -* Description: Provides a text value for any tag marked as special for makernotes -* that this script can decode. Returns false if this is not a makernote -* that can be processed with this script -* -* Parameters: Exif_Tag - the element of an the Makernote array containing the -* tag in question, as returned from get_Olympus_Makernote -* Tag_Definitions_Name - The name of the Tag Definitions group -* within the global array IFD_Tag_Definitions -* -* -* Returns: output - the text value for the tag -* FALSE - If this script could not decode the makernote, or if -* an error occured in decoding -* -******************************************************************************/ - -function get_Olympus_Text_Value( $Exif_Tag, $Tag_Definitions_Name ) -{ - // Check that this tag uses the Olympus tags, otherwise it can't be decoded here - if ( $Tag_Definitions_Name !== "Olympus" ) - { - // Not an Olympus tag - can't decode it - return FALSE; - } - - - // Process the tag acording to it's tag number, to produce a text value - if ( $Exif_Tag['Tag Number'] == 0x200 ) - { - // Special Mode Tag - - // Add info from the first value to the output string - switch ( $Exif_Tag['Data'][0] ) - { - case 0: $outputstr = "Normal\n"; - break; - case 2: $outputstr = "Fast\n"; - break; - case 3: $outputstr = "Panorama\n"; - break; - default: $outputstr = "Unknown Mode ( " . $Exif_Tag['Data'][0] . " )\n"; - break; - } - - // Add info from the second value to the output string - $outputstr .= "Sequence Number: " . $Exif_Tag['Data'][1] . "\n"; - - // Add info from the third value to the output string - switch ( $Exif_Tag['Data'][2] ) - { - case 0: // Do nothing - break; - case 1: $outputstr .= "Panorama Direction: Left to Right\n"; - break; - case 2: $outputstr .= "Panorama Direction: Right to Left\n"; - break; - case 3: $outputstr .= "Panorama Direction: Bottom to Top\n"; - break; - case 4: $outputstr .= "Panorama Direction: Top to Bottom\n"; - break; - default: $outputstr .= "Unknown Panorama Direction\n"; - break; - } - - // Return the output string - return $outputstr; - } - else - { - // Unknown special tag - can't process it here - return FALSE; - } - - // Unknown special tag - can't process it here - return FALSE; - -} - -/****************************************************************************** -* End of Function: get_Olympus_Text_Value -******************************************************************************/ - - - - -/****************************************************************************** -* -* Function: get_Olympus_Makernote_Html -* -* Description: Attempts to interpret a makernote into html. Returns false if -* it is not a makernote that can be processed with this script -* -* Parameters: Makernote_Tag - the element of an EXIF array containing the -* makernote, as returned from get_EXIF_JPEG -* filename - the name of the JPEG file being processed ( used -* by scripts which display embedded thumbnails) -* -* -* Returns: output - the html representing the makernote -* FALSE - If this script could not interpret the makernote, or if -* an error occured in decoding -* -******************************************************************************/ - -function get_Olympus_Makernote_Html( $Makernote_tag, $filename ) -{ - - // Check that this tag uses the Olympus tags, otherwise it can't be interpreted here - if ( $Makernote_tag['Makernote Tags'] != "Olympus" ) - { - // Not Olympus tags - can't interpret with this function - return FALSE; - } - - // Check if the Decoded data is valid - if ( $Makernote_tag['Decoded Data'][0] === FALSE ) - { - // Decoded data is not valid - can't interpret with this function - return FALSE; - } - - // Minolta Thumbnail 1 - if ( ( array_key_exists( 0x0088, $Makernote_tag['Decoded Data'][0] ) ) && - ( $Makernote_tag['Makernote Tags'] == "Olympus" ) ) - { - // Change: as of version 1.11 - Changed to make thumbnail link portable across directories - // Build the path of the thumbnail script and its filename parameter to put in a url - $link_str = get_relative_path( dirname(__FILE__) . "/../get_minolta_thumb.php" , getcwd ( ) ); - $link_str .= "?filename="; - $link_str .= get_relative_path( $filename, dirname(__FILE__) ."/.." ); - - // Add thumbnail link to html - $Makernote_tag['Decoded Data'][0][0x0088]['Text Value'] = ""; - - $Makernote_tag['Decoded Data'][0][0x0088]['Type'] = "String"; - } - // Minolta Thumbnail 2 - if ( ( array_key_exists( 0x0081, $Makernote_tag['Decoded Data'][0] ) ) && - ( $Makernote_tag['Makernote Tags'] == "Olympus" ) ) - { - // Change: as of version 1.11 - Changed to make thumbnail link portable across directories - // Build the path of the thumbnail script and its filename parameter to put in a url - $link_str = get_relative_path( dirname(__FILE__) . " /../get_minolta_thumb.php" , getcwd ( ) ); - $link_str .= "?filename="; - $link_str .= get_relative_path( $filename, dirname(__FILE__) ."/.." ); - - // Add thumbnail link to html - $Makernote_tag['Decoded Data'][0][0x0081]['Text Value'] = ""; - $Makernote_tag['Decoded Data'][0][0x0081]['Type'] = "String"; - } - - // Interpret the IFD and return the HTML - return interpret_IFD( $Makernote_tag['Decoded Data'][0], $filename ); - -} - -/****************************************************************************** -* End of Function: get_Olympus_Makernote_Html -******************************************************************************/ - - - - - - - - - - - - -/****************************************************************************** -* Global Variable: IFD_Tag_Definitions, Olympus -* -* Contents: This global variable provides definitions of the known Olympus -* Makernote tags, indexed by their tag number. -* It also includes Minolta and Agfa tags, as they use many of the -* same tags -* -******************************************************************************/ - -$GLOBALS[ "IFD_Tag_Definitions" ]["Olympus"] = array( - -0x0000 => array( 'Name' => "Makernote Version", // Minolta - 'Type' => "String" ), - -0x0001 => array( 'Name' => "Camera Settings", // Minolta - 'Type' => "Special" ), - -0x0003 => array( 'Name' => "Camera Settings", // Minolta - 'Type' => "Special" ), - -0x0040 => array( 'Name' => "Compressed Image Size", // Minolta - 'Type' => "Numeric", - 'Units' => "Bytes" ), - -0x0081 => array( 'Name' => "Minolta Thumbnail", // Minolta - 'Type' => "Special" ), - -0x0088 => array( 'Name' => "Minolta Thumbnail", // Minolta - 'Type' => "Special" ), - -0x0089 => array( 'Name' => "Minolta Thumbnail Length", // Minolta - 'Type' => "Numeric", - 'Units' => "bytes" ), - -0x0101 => array( 'Name' => "Colour Mode", // Minolta - 'Type' => "Lookup", - 0 => "Natural Colour", - 1 => "Black & White", - 2 => "Vivid colour", - 3 => "Solarization", - 4 => "AdobeRGB" ), - -0x0102 => array( 'Name' => "Image Quality", // Minolta - 'Type' => "Lookup", - 0 => "Raw", - 1 => "Super Fine", - 2 => "Fine", - 3 => "Standard", - 4 => "Extra Fine" ), - -0x0103 => array( 'Name' => "Image Quality?", // Minolta - 'Type' => "Lookup", - 0 => "Raw", - 1 => "Super Fine", - 2 => "Fine", - 3 => "Standard", - 4 => "Extra Fine" ), - - - -0x0200 => array( 'Name' => "Special Mode", - 'Type' => "Special" ), - - -0x0201 => array( 'Name' => "JPEG Quality", - 'Type' => "Lookup", - 1 => "Standard Quality", - 2 => "High Quality", - 3 => "Super High Quality" ), - -0x0202 => array( 'Name' => "Macro", - 'Type' => "Lookup", - 0 => "Normal (Not Macro)", - 1 => "Macro" ), - -0x0204 => array( 'Name' => "Digital Zoom", - 'Type' => "Numeric", - 'Units' => " x Digital Zoom, (0 or 1 = normal)" ), - -0x0207 => array( 'Name' => "Firmware Version", - 'Type' => "String" ), - - -0x0208 => array( 'Name' => "Picture Info Data", - 'Type' => "String" ), - -0x0209 => array( 'Name' => "Camera ID", - 'Type' => "String" ), - - -0x020B => array( 'Name' => "Image Width", // Epson Tag - 'Type' => "Pixels" ), - -0x020C => array( 'Name' => "Image Height", // Epson Tag - 'Type' => "Pixels" ), - -0x020D => array( 'Name' => "Original Manufacturer Model?", // Epson Tag - 'Type' => "String" ), - -0x0E00 => array( 'Name'=> "Print Image Matching Info", // Minolta Tag - 'Type' => "PIM" ), - -0x1004 => array( 'Name' => "Flash Mode", - 'Type' => "Numeric" ), - -0x1006 => array( 'Name' => "Bracket", - 'Type' => "Numeric" ), - -0x100B => array( 'Name' => "Focus Mode", - 'Type' => "Numeric" ), - -0x100C => array( 'Name' => "Focus Distance", - 'Type' => "Numeric" ), - -0x100D => array( 'Name' => "Zoom", - 'Type' => "Numeric" ), - -0x100E => array( 'Name' => "Macro Focus", - 'Type' => "Numeric" ), - -0x100F => array( 'Name' => "Sharpness", - 'Type' => "Numeric" ), - -0x1011 => array( 'Name' => "Colour Matrix", - 'Type' => "Numeric" ), - -0x1012 => array( 'Name' => "Black Level", - 'Type' => "Numeric" ), - -0x1015 => array( 'Name' => "White Balance", - 'Type' => "Numeric" ), - -0x1017 => array( 'Name' => "Red Bias", - 'Type' => "Numeric" ), - -0x1018 => array( 'Name' => "Blue Bias", - 'Type' => "Numeric" ), - -0x101A => array( 'Name' => "Serial Number", - 'Type' => "Numeric" ), - -0x1023 => array( 'Name' => "Flash Bias", - 'Type' => "Numeric" ), - -0x1029 => array( 'Name' => "Contrast", - 'Type' => "Numeric" ), - -0x102A => array( 'Name' => "Sharpness Factor", - 'Type' => "Numeric" ), - -0x102B => array( 'Name' => "Colour Control", - 'Type' => "Numeric" ), - -0x102C => array( 'Name' => "Valid Bits", - 'Type' => "Numeric" ), - -0x102D => array( 'Name' => "Coring Filter", - 'Type' => "Numeric" ), - -0x102E => array( 'Name' => "Final Width", - 'Type' => "Numeric" ), - -0x102F => array( 'Name' => "Final Height", - 'Type' => "Numeric" ), - -0x1034 => array( 'Name' => "Compression Ratio", - 'Type' => "Numeric" ), - - - -); - - -/****************************************************************************** -* End of Global Variable: IFD_Tag_Definitions, Olympus -******************************************************************************/ - - - - - -?> \ No newline at end of file diff --git a/jpeg_metadata_tk/Makernotes/panasonic.php b/jpeg_metadata_tk/Makernotes/panasonic.php deleted file mode 100644 index 2da827f..0000000 --- a/jpeg_metadata_tk/Makernotes/panasonic.php +++ /dev/null @@ -1,292 +0,0 @@ - array( 'Name' => "Quality Mode", - 'Type' => "Numeric" ), - -0x02 => array( 'Name' => "Version", - 'Type' => "String" ), - -0x1c => array( 'Name' => "Macro Mode", - 'Type' => "Lookup", - 1 => "On", - 2 => "Off" ), - -0x1f => array( 'Name' => "Record Mode", - 'Type' => "Lookup", - 1 => "Normal", - 2 => "Portrait", - 9 => "Macro" ), - -0xE00 => array( 'Name' => "Print Image Matching Info", - 'Type' => "PIM" ), - -); - -/****************************************************************************** -* End of Global Variable: IFD_Tag_Definitions, Panasonic -******************************************************************************/ - - - - - -?> diff --git a/jpeg_metadata_tk/Makernotes/ricoh.php b/jpeg_metadata_tk/Makernotes/ricoh.php deleted file mode 100644 index f858bbc..0000000 --- a/jpeg_metadata_tk/Makernotes/ricoh.php +++ /dev/null @@ -1,415 +0,0 @@ -"; - - // Replace the semicolon dividers with line break html tags - $output_str .= str_replace ( ";", "
    \n", $Makernote_tag['Data'] ); - - // Close the html - $output_str .= ""; - - // Return the html - return $output_str; - } - // Check if this makernote is a Ricoh Empty makernote - else if ( $Makernote_tag['Makernote Type'] == "Ricoh Empty Makernote" ) - { - // Do Nothing - return ""; - } - else - { - // Don't recognise the Makernote type - not a Ricoh makernote - return FALSE; - } - - // shouldn't get here - return FALSE; -} - -/****************************************************************************** -* End of Function: get_Ricoh_Makernote_Html -******************************************************************************/ - - - - - - - - -/****************************************************************************** -* -* Function: get_Ricoh_Text_Value -* -* Description: Provides a text value for any tag marked as special for makernotes -* that this script can decode. Returns false if this is not a makernote -* that can be processed with this script -* -* Parameters: Exif_Tag - the element of an the Makernote array containing the -* tag in question, as returned from get_Ricoh_Makernote -* Tag_Definitions_Name - The name of the Tag Definitions group -* within the global array IFD_Tag_Definitions -* -* -* Returns: output - the text value for the tag -* FALSE - If this script could not decode the makernote, or if -* an error occured in decoding -* -******************************************************************************/ - -function get_Ricoh_Text_Value( $Exif_Tag, $Tag_Definitions_Name ) -{ - - // Check that this tag uses the Ricoh tags, otherwise it can't be decoded here - if ( $Tag_Definitions_Name == "Ricoh" ) - { - - // Process the tag acording to it's tag number, to produce a text value - if ( $Exif_Tag['Tag Number'] == 0x0002 ) // Version tag - { - $tmp = implode ( "\x00", $Exif_Tag['Data']); - return "\"" .HTML_UTF8_Escape( $tmp ) . "\" (" . bin2hex( $tmp ) . " hex)"; - } - - } - - // Unknown tag or tag definitions - return FALSE; - -} - -/****************************************************************************** -* End of Function: get_Ricoh_Text_Value -******************************************************************************/ - - - - - - - - - - - -/****************************************************************************** -* Global Variable: IFD_Tag_Definitions, Ricoh -* -* Contents: This global variable provides definitions of the known Ricoh -* Makernote tags, indexed by their tag number. -* -******************************************************************************/ - -$GLOBALS[ "IFD_Tag_Definitions" ]["Ricoh"] = array( - - -0x0001 => array( 'Name' => "Makernote Data Type", - 'Type' => "String" ), - -0x0002 => array( 'Name' => "Version", - 'Type' => "Special" ), - -0x0e00 => array( 'Name' => "Print Image Matching Info", - 'Type' => "PIM" ), - -0x2001 => array( 'Name' => "Ricoh Camera Info Makernote Sub-IFD", - 'Type' => "Special" ), - -); - -/****************************************************************************** -* End of Global Variable: IFD_Tag_Definitions, Ricoh -******************************************************************************/ - - - - - - - - -/****************************************************************************** -* Global Variable: IFD_Tag_Definitions, RicohSubIFD -* -* Contents: This global variable provides definitions of the known Ricoh -* Camera Info Sub-IFD Makernote tags, indexed by their tag number. -* -******************************************************************************/ - -$GLOBALS[ "IFD_Tag_Definitions" ]["RicohSubIFD"] = array( - -); - -/****************************************************************************** -* End of Global Variable: IFD_Tag_Definitions, RicohSubIFD -******************************************************************************/ - - - - - - -?> \ No newline at end of file diff --git a/jpeg_metadata_tk/Makernotes/sony.php b/jpeg_metadata_tk/Makernotes/sony.php deleted file mode 100644 index 2690d81..0000000 --- a/jpeg_metadata_tk/Makernotes/sony.php +++ /dev/null @@ -1,244 +0,0 @@ - array( 'Name' => "Print Image Matching Info", - 'Type' => "PIM" ), - -); - -/****************************************************************************** -* End of Global Variable: IFD_Tag_Definitions, Sony -******************************************************************************/ - - - - - - - - - - - -?> diff --git a/jpeg_metadata_tk/PIM.php b/jpeg_metadata_tk/PIM.php deleted file mode 100644 index 70be02d..0000000 --- a/jpeg_metadata_tk/PIM.php +++ /dev/null @@ -1,279 +0,0 @@ - $PI_tag, 'Data' => substr($data_part, $a*6+2, 4) , 'Decoded' => False ); - } - } - - } - - // Return the updated tag - return $newtag; - -} - -/****************************************************************************** -* End of Function: Decode_PIM -******************************************************************************/ - - - - -/****************************************************************************** -* -* Function: Encode_PIM -* -* Description: Encodes the contents of a EXIF tag containing Print Image -* Matching information, and returns the contents as a packed binary string -* -* Parameters: tag - An EXIF tag containing Print Image Matching information -* as from get_EXIF_JPEG -* Byte_Align - the Byte alignment to use - "MM" or "II" -* -* Returns: packed_data - The packed binary string representing the PIM data -* -******************************************************************************/ - -function Encode_PIM( $tag, $Byte_Align) -{ - - // Create a string to receive the packed data - $packed_data = ""; - - // Check that this tag is for Print Image Matching Info - if ( $tag['Type'] == "PIM" ) - { - // Check that the tag has been decoded - otherwise we don't need to do anything - if ( ( is_array( $tag['Data'] ) ) && - ( count ( $tag['Data'] ) > 0 ) ) - { - // Add the header to the packed data - $packed_data .= "PrintIM\x00"; - - // Add the version to the packed data - $packed_data .= $tag['Data']['Version'] . "\x00"; - - // Create a string to receive the tag data - $tag_data_str = ""; - - // Cycle through each tag - $tag_count = 0; - foreach( $tag['Data'] as $key => $curr_tag ) - { - // Make sure this is a tag and not supplementary info - if ( is_numeric( $key ) ) - { - // Count how many tags are created - $tag_count++; - - // Add the tag number to the packed tag data - $tag_data_str .= put_IFD_Data_Type( $curr_tag['Tag Number'], 3, $Byte_Align ); - - // Add the tag data to the packed tag data - $tag_data_str .= $curr_tag['Data']; - } - } - - // Add the tag count to the packed data - $packed_data .= put_IFD_Data_Type( $tag_count, 3, $Byte_Align ); - - // Add the packed tag data to the packed data - $packed_data .= $tag_data_str; - } - } - - // Return the resulting packed data - return $packed_data; - -} - -/****************************************************************************** -* End of Function: Encode_PIM -******************************************************************************/ - - - - - - - - - - -/****************************************************************************** -* -* Function: get_PIM_Text_Value -* -* Description: Interprets the contents of a EXIF tag containing Print Image -* Matching information, and returns content as as a text string -* -* Parameters: tag - An EXIF tag containing Print Image Matching information -* as from get_EXIF_JPEG -* Tag_Definitions_Name - The name of the Tag Definitions group -* within the global array IFD_Tag_Definitions -* -* Returns: output_str - The text string representing the PIM info -* -******************************************************************************/ - -function get_PIM_Text_Value( $Tag, $Tag_Definitions_Name ) -{ - - // Create a string to receive the output - $output_str = ""; - - // Check if the PIM tag has been decoded - if ( ( is_array( $Tag['Data'] ) ) && - ( count ( $Tag['Data'] ) > 0 ) ) - { - // The tag has been decoded - - // Add the Version to the output - $output_str = "Version: " . $Tag['Data']['Version'] . "\n"; - - // Check if the user wants to hide unknown tags - if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == FALSE ) - { - // The user wants to see unknown tags - // Cycle through each tag - foreach ( $Tag['Data'] as $PIM_tag_Key => $PIM_tag ) - { - // Check that the tag is not the version array element - if ( $PIM_tag_Key !== 'Version' ) - { - // Add the tag to the output - $output_str .= "Unknown Tag " . $PIM_tag['Tag Number'] . ": (" . strlen( $PIM_tag['Data'] ) . " bytes of data)\n"; - } - } - } - } - - // Return the output text - return $output_str; -} - -/****************************************************************************** -* End of Function: get_PIM_Text_Value -******************************************************************************/ - - - -?> diff --git a/jpeg_metadata_tk/Photoshop_File_Info.php b/jpeg_metadata_tk/Photoshop_File_Info.php deleted file mode 100644 index 1da7a8c..0000000 --- a/jpeg_metadata_tk/Photoshop_File_Info.php +++ /dev/null @@ -1,2498 +0,0 @@ - 1.11 : Changed displayed toolkit version numbers to reference Toolkit_Version.php -* -* URL: http://electronics.ozhiker.com -* -* License: This file is part of the PHP JPEG Metadata Toolkit. -* -* The PHP JPEG Metadata Toolkit is free software; you can -* redistribute it and/or modify it under the terms of the -* GNU General Public License as published by the Free Software -* Foundation; either version 2 of the License, or (at your -* option) any later version. -* -* The PHP JPEG Metadata Toolkit is distributed in the hope -* that it will be useful, but WITHOUT ANY WARRANTY; without -* even the implied warranty of MERCHANTABILITY or FITNESS -* FOR A PARTICULAR PURPOSE. See the GNU General Public License -* for more details. -* -* You should have received a copy of the GNU General Public -* License along with the PHP JPEG Metadata Toolkit; if not, -* write to the Free Software Foundation, Inc., 59 Temple -* Place, Suite 330, Boston, MA 02111-1307 USA -* -* If you require a different license for commercial or other -* purposes, please contact the author: evan@ozhiker.com -* -******************************************************************************/ - -// TODO: XMP sections: XAPMM, TIFF, EXIF - - -require_once 'Toolkit_Version.php'; // Change: added as of version 1.11 - - -/****************************************************************************** -* Global Variable: Software Name -* -* Contents: The string that is appended to fields which store the name of -* the software editor. -* -******************************************************************************/ - -$GLOBALS[ "Software Name" ] = "(PHP JPEG Metadata Toolkit v" . $GLOBALS['Toolkit_Version'] . ")"; // Change: Changed version numbers to reference Toolkit_Version.php - as of version 1.11 - -/****************************************************************************** -* End of Global Variable: Software Name -******************************************************************************/ - - - - - - -/****************************************************************************** -* -* Function: get_photoshop_file_info -* -* Description: Retrieves Photoshop 'File Info' metadata in the same way that Photoshop -* does. The results are returned in an array as below: -* -* $file_info_array = array( -* "title" => "", -* "author" => "", -* "authorsposition" => "", // Note: Not used in Photoshop 7 or higher -* "caption" => "", -* "captionwriter" => "", -* "jobname" => "", // Note: Not used in Photoshop CS -* "copyrightstatus" => "", -* "copyrightnotice" => "", -* "ownerurl" => "", -* "keywords" => array( 0 => "", 1 => "", ... ), -* "category" => "", // Note: Max 3 characters -* "supplementalcategories" => array( 0 => "", 1 => "", ... ), -* "date" => "", // Note: DATE MUST BE IN YYYY-MM-DD format -* "city" => "", -* "state" => "", -* "country" => "", -* "credit" => "", -* "source" => "", -* "headline" => "", -* "instructions" => "", -* "transmissionreference" => "", -* "urgency" => "" ); -* -* Parameters: Exif_array - an array containing the EXIF information to be -* searched, as retrieved by get_EXIF_JPEG. (saves having to parse the EXIF again) -* XMP_array - an array containing the XMP information to be -* searched, as retrieved by read_XMP_array_from_text. (saves having to parse the XMP again) -* IRB_array - an array containing the Photoshop IRB information -* to be searched, as retrieved by get_Photoshop_IRB. (saves having to parse the IRB again) -* -* Returns: outputarray - an array as above, containing the Photoshop File Info data -* -******************************************************************************/ - -function get_photoshop_file_info( $Exif_array, $XMP_array, $IRB_array ) -{ - - // Create a blank array to receive the output - $outputarray = array( - "title" => "", - "author" => "", - "authorsposition" => "", - "caption" => "", - "captionwriter" => "", - "jobname" => "", - "copyrightstatus" => "", - "copyrightnotice" => "", - "ownerurl" => "", - "keywords" => array(), - "category" => "", - "supplementalcategories" => array(), - "date" => "", - "city" => "", - "state" => "", - "country" => "", - "credit" => "", - "source" => "", - "headline" => "", - "instructions" => "", - "transmissionreference" => "", - "urgency" => "" ); - - - /***************************************/ - - // XMP Processing - - - // Retrieve the dublin core section from the XMP header - - // Extract the Dublin Core section from the XMP - $dublincore_block = find_XMP_block( $XMP_array, "dc" ); - - // Check that the Dublin Core section exists - if ( $dublincore_block != FALSE ) - { - // Dublin Core Description Field contains caption - // Extract Description - $Item = find_XMP_item( $dublincore_block, "dc:description" ); - - // Check if Description Tag existed - if ( $Item != FALSE ) - { - // Ensure that the Description value exists and save it. - if ( ( array_key_exists( 'children', $Item ) ) && - ( $Item['children'][0]['tag'] == "rdf:Alt" ) && - ( array_key_exists( 'value', $Item['children'][0]['children'][0] ) ) ) - { - $outputarray = add_to_field( $outputarray, 'caption' , HTML_UTF8_Escape( $Item['children'][0]['children'][0]['value'] ), "\n" ); - } - } - - /***************************************/ - - // Dublin Core Creator Field contains author - // Extract Description - $Item = find_XMP_item( $dublincore_block, "dc:creator" ); - - // Check if Creator Tag existed - if ( $Item != FALSE ) - { - // Ensure that the Creator value exists and save it. - if ( ( !empty( $Item['children'][0] ) ) && - ( $Item['children'][0]['tag'] =="rdf:Seq" ) && - ( !empty( $Item['children'][0]['children'][0]['empty'] ) ) ) - { - $outputarray = add_to_field( $outputarray, 'author' , HTML_UTF8_Escape( $Item['children'][0]['children'][0]['value'] ), "\n" ); - } - } - - /***************************************/ - - // Dublin Core Title Field contains title - // Extract Title - $Item = find_XMP_item( $dublincore_block, "dc:title" ); - - // Check if Title Tag existed - if ( $Item != FALSE ) - { - // Ensure that the Title value exists and save it. - if ( ( array_key_exists( 'children', $Item ) ) && - ( $Item['children'][0]['tag'] =="rdf:Alt" ) && - ( array_key_exists( 'value', $Item['children'][0]['children'][0] ) ) ) - { - - $outputarray = add_to_field( $outputarray, 'title' , HTML_UTF8_Escape( $Item['children'][0]['children'][0]['value'] ), "," ); - } - } - - /***************************************/ - - // Dublin Core Rights Field contains copyrightnotice - // Extract Rights - $Item = find_XMP_item( $dublincore_block, "dc:rights" ); - - // Check if Rights Tag existed - if ( $Item != FALSE ) - { - // Ensure that the Rights value exists and save it. - if ( ( array_key_exists( 'children', $Item ) ) && - ( $Item['children'][0]['tag'] =="rdf:Alt" ) && - ( array_key_exists( 'value', $Item['children'][0]['children'][0] ) ) ) - { - - $outputarray = add_to_field( $outputarray, 'copyrightnotice' , HTML_UTF8_Escape( $Item['children'][0]['children'][0]['value'] ), "," ); - } - } - - /***************************************/ - - // Dublin Core Subject Field contains keywords - // Extract Subject - $Item = find_XMP_item( $dublincore_block, "dc:subject" ); - - // Check if Subject Tag existed - if ( $Item != FALSE ) - { - // Ensure that the Subject values exist - if ( ( array_key_exists( 'children', $Item ) ) && ( $Item['children'][0]['tag'] =="rdf:Bag" ) ) - { - // Cycle through each Subject value and save them - foreach ( $Item['children'][0]['children'] as $keywords ) - { - if ( ! in_array ( HTML_UTF8_Escape( $keywords['value'] ), $outputarray['keywords'])) - { - if ( array_key_exists( 'value', $keywords ) ) - { - $outputarray['keywords'][] = HTML_UTF8_Escape( $keywords['value'] ); - } - } - } - } - } - - - } - - /***************************************/ - - // Find the Photoshop Information within the XMP block - $photoshop_block = find_XMP_block( $XMP_array, "photoshop" ); - - // Check that the Photoshop Information exists - if ( $photoshop_block != FALSE ) - { - // The Photoshop CaptionWriter tag contains captionwriter - Find it - $Item = find_XMP_item( $photoshop_block, "photoshop:CaptionWriter" ); - - // Check that the CaptionWriter Field exists and save the value - if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) - { - $outputarray = add_to_field( $outputarray, 'captionwriter' , HTML_UTF8_Escape( $Item['value'] ), "," ); - } - - /***************************************/ - - // The Photoshop Headline tag contains headline - Find it - $Item = find_XMP_item( $photoshop_block, "photoshop:Headline" ); - - // Check that the Headline Field exists and save the value - if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) - { - $outputarray = add_to_field( $outputarray, 'headline' , HTML_UTF8_Escape( $Item['value'] ), "," ); - } - - /***************************************/ - - // The Photoshop Instructions tag contains instructions - Find it - $Item = find_XMP_item( $photoshop_block, "photoshop:Instructions" ); - - // Check that the Instructions Field exists and save the value - if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) - { - $outputarray = add_to_field( $outputarray, 'instructions' , HTML_UTF8_Escape( $Item['value'] ), "\n" ); - } - - /***************************************/ - - // The Photoshop AuthorsPosition tag contains authorsposition - Find it - $Item = find_XMP_item( $photoshop_block, "photoshop:AuthorsPosition" ); - - // Check that the AuthorsPosition Field exists and save the value - if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) - { - $outputarray = add_to_field( $outputarray, 'authorsposition' , HTML_UTF8_Escape( $Item['value'] ), "," ); - } - - /***************************************/ - - // The Photoshop Credit tag contains credit - Find it - $Item = find_XMP_item( $photoshop_block, "photoshop:Credit" ); - - // Check that the Credit Field exists and save the value - if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) - { - $outputarray = add_to_field( $outputarray, 'credit' , HTML_UTF8_Escape( $Item['value'] ), "," ); - } - - /***************************************/ - - // The Photoshop Source tag contains source - Find it - $Item = find_XMP_item( $photoshop_block, "photoshop:Source" ); - - // Check that the Credit Field exists and save the value - if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) - { - $outputarray = add_to_field( $outputarray, 'source' , HTML_UTF8_Escape( $Item['value'] ), "," ); - } - - /***************************************/ - - // The Photoshop City tag contains city - Find it - $Item = find_XMP_item( $photoshop_block, "photoshop:City" ); - - // Check that the City Field exists and save the value - if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) - { - $outputarray = add_to_field( $outputarray, 'city' , HTML_UTF8_Escape( $Item['value'] ), "," ); - } - - /***************************************/ - - // The Photoshop State tag contains state - Find it - $Item = find_XMP_item( $photoshop_block, "photoshop:State" ); - - // Check that the State Field exists and save the value - if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) - { - $outputarray = add_to_field( $outputarray, 'state' , HTML_UTF8_Escape( $Item['value'] ), "," ); - } - - /***************************************/ - - // The Photoshop Country tag contains country - Find it - $Item = find_XMP_item( $photoshop_block, "photoshop:Country" ); - - // Check that the Country Field exists and save the value - if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) - { - $outputarray = add_to_field( $outputarray, 'country' , HTML_UTF8_Escape( $Item['value'] ), "," ); - } - - /***************************************/ - - // The Photoshop TransmissionReference tag contains transmissionreference - Find it - $Item = find_XMP_item( $photoshop_block, "photoshop:TransmissionReference" ); - - // Check that the TransmissionReference Field exists and save the value - if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) - { - $outputarray = add_to_field( $outputarray, 'transmissionreference' , HTML_UTF8_Escape( $Item['value'] ), "," ); - } - - /***************************************/ - - // The Photoshop Category tag contains category - Find it - $Item = find_XMP_item( $photoshop_block, "photoshop:Category" ); - - // Check that the TransmissionReference Field exists and save the value - if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) - { - $outputarray = add_to_field( $outputarray, 'category' , HTML_UTF8_Escape( $Item['value'] ), "," ); - } - - /***************************************/ - - // The Photoshop DateCreated tag contains date - Find it - $Item = find_XMP_item( $photoshop_block, "photoshop:DateCreated" ); - - // Check that the DateCreated Field exists and save the value - if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) - { - $outputarray = add_to_field( $outputarray, 'date' , HTML_UTF8_Escape( $Item['value'] ), "," ); - } - - /***************************************/ - - // The Photoshop Urgency tag contains urgency - Find it - $Item = find_XMP_item( $photoshop_block, "photoshop:Urgency" ); - - // Check that the Urgency Field exists and save the value - if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) - { - $outputarray = add_to_field( $outputarray, 'urgency' , HTML_UTF8_Escape( $Item['value'] ), "," ); - } - - /***************************************/ - - // The Photoshop SupplementalCategories tag contains supplementalcategories - Find it - $Item = find_XMP_item( $photoshop_block, "photoshop:SupplementalCategories" ); - - // Check that the SupplementalCategories Field exists - if ( $Item != FALSE ) - { - // Check that the values exist - if ( ( array_key_exists( 'children', $Item ) ) && ( $Item['children'][0]['tag'] =="rdf:Bag" ) ) - { - // Cycle through the values and save them - foreach ( $Item['children'][0]['children'] as $sup_category ) - { - if ( ( array_key_exists( 'value', $sup_category ) ) && - ( ! in_array ( HTML_UTF8_Escape( $sup_category['value'] ), $outputarray['supplementalcategories'])) ) - { - if ( array_key_exists( 'value', $sup_category ) ) - { - $outputarray['supplementalcategories'][] = HTML_UTF8_Escape( $sup_category['value'] ); - } - } - } - } - } - - } - - /***************************************/ - - // Find the Job Reference Information within the XMP block - $job_block = find_XMP_block( $XMP_array, "xapBJ" ); - - // Check that the Job Reference Information exists - if ( $job_block != FALSE ) - { - // The JobRef Field contains jobname - Find it - $Item = find_XMP_item( $job_block, "xapBJ:JobRef" ); - - // Check that the JobRef Field exists - if ( $Item != FALSE ) - { - // Check that the value exists and save it - if ( ( array_key_exists( 'children', $Item ) ) && - ( $Item['children'][0]['tag'] =="rdf:Bag" ) && - ( array_key_exists( 'children', $Item['children'][0] ) ) && - ( $Item['children'][0]['children'][0]['tag'] =="rdf:li" ) && - ( array_key_exists( 'children', $Item['children'][0]['children'][0] ) ) && - ( $Item['children'][0]['children'][0]['children'][0]['tag'] =="stJob:name" ) && - ( array_key_exists( 'value', $Item['children'][0]['children'][0]['children'][0] ) ) ) - { - $outputarray = add_to_field( $outputarray, 'jobname' , HTML_UTF8_Escape( $Item['children'][0]['children'][0]['children'][0]['value'] ), "," ); - } - } - } - - - /***************************************/ - - // Find the Rights Information within the XMP block - $rights_block = find_XMP_block( $XMP_array, "xapRights" ); - - // Check that the Rights Information exists - if ( $rights_block != FALSE ) - { - // The WebStatement Field contains ownerurl - Find it - $Item = find_XMP_item( $rights_block, "xapRights:WebStatement" ); - - // Check that the WebStatement Field exists and save the value - if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) - { - $outputarray = add_to_field( $outputarray, 'ownerurl' , HTML_UTF8_Escape( $Item['value'] ), "\n" ); - } - - /***************************************/ - - // The Marked Field contains copyrightstatus - Find it - $Item = find_XMP_item( $rights_block, "xapRights:Marked" ); - - // Check that the Marked Field exists and save the value - if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) - { - if ( $Item['value'] == "True" ) - { - $outputarray = add_to_field( $outputarray, 'copyrightstatus' , "Copyrighted Work", "," ); - } - else - { - $outputarray = add_to_field( $outputarray, 'copyrightstatus' , "Public Domain", "," ); - } - } - - } - - - - - - /***************************************/ - - // Photoshop IRB Processing - - // Check that the Photoshop IRB exists - if ( $IRB_array != FALSE ) - { - // Create a translation table to convert carriage returns to linefeeds - $irbtrans = array("\x0d" => "\x0a"); - - // The Photoshop IRB Copyright flag (0x040A) contains copyrightstatus - find it - $IRB_copyright_flag = find_Photoshop_IRB_Resource( $IRB_array, 0x040A ); - - // Check if the Copyright flag Field exists, and save the value - if( $IRB_copyright_flag != FALSE ) - { - // Check the value of the copyright flag - if ( hexdec( bin2hex( $IRB_copyright_flag['ResData'] ) ) == 1 ) - { - // Save the value - $outputarray = add_to_field( $outputarray, 'copyrightstatus' , "Copyrighted Work", "," ); - } - else - { - // Do nothing - copyrightstatus will be set to unmarked if still blank at end - } - } - - /***************************************/ - - // The Photoshop IRB URL (0x040B) contains ownerurl - find it - $IRB_url = find_Photoshop_IRB_Resource( $IRB_array, 0x040B ); - - // Check if the URL Field exists and save the value - if( $IRB_url != FALSE ) - { - $outputarray = add_to_field( $outputarray, 'ownerurl' , strtr( $IRB_url['ResData'], $irbtrans ), "\n" ); - } - - /***************************************/ - - // Extract any IPTC block from the Photoshop IRB information - $IPTC_array = get_Photoshop_IPTC( $IRB_array ); - - // Check if the IPTC block exits - if ( ( $IPTC_array != FALSE ) && ( count( $IPTC_array ) != 0 ) ) - { - // The IPTC Caption/Abstract Field contains caption - find it - $record = find_IPTC_Resource( $IPTC_array, "2:120" ); - - // Check if the Caption/Abstract Field exists and save the value - if ( $record != FALSE ) - { - $outputarray = add_to_field( $outputarray, 'caption' , strtr( $record['RecData'], $irbtrans ), "\n" ); - } - - /***************************************/ - - // The IPTC Caption Writer/Editor Field contains captionwriter - find it - $record = find_IPTC_Resource( $IPTC_array, "2:122" ); - - // Check if the Caption Writer/Editor Field exists and save the value - if ( $record != FALSE ) - { - $outputarray = add_to_field( $outputarray, 'captionwriter' , strtr( $record['RecData'], $irbtrans ), "\n" ); - } - - /***************************************/ - - // The IPTC Headline Field contains headline - find it - $record = find_IPTC_Resource( $IPTC_array, "2:105" ); - - // Check if the Headline Field exists and save the value - if ( $record != FALSE ) - { - $outputarray = add_to_field( $outputarray, 'headline' , strtr( $record['RecData'], $irbtrans ), "\n" ); - } - - /***************************************/ - - // The IPTC Special Instructions Field contains instructions - find it - $record = find_IPTC_Resource( $IPTC_array, "2:40" ); - - // Check if the Special Instructions Field exists and save the value - if ( $record != FALSE ) - { - $outputarray = add_to_field( $outputarray, 'instructions' , strtr( $record['RecData'], $irbtrans ), "\n" ); - } - - /***************************************/ - - // The IPTC By-Line Field contains author - find it - $record = find_IPTC_Resource( $IPTC_array, "2:80" ); - - // Check if the By-Line Field exists and save the value - if ( $record != FALSE ) - { - $outputarray = add_to_field( $outputarray, 'author' , strtr( $record['RecData'], $irbtrans ), "\n" ); - } - - /***************************************/ - - // The IPTC By-Line Title Field contains authorsposition - find it - $record = find_IPTC_Resource( $IPTC_array, "2:85" ); - - // Check if the By-Line Title Field exists and save the value - if ( $record != FALSE ) - { - $outputarray = add_to_field( $outputarray, 'authorsposition' , strtr( $record['RecData'], $irbtrans ), "\n" ); - } - - /***************************************/ - - // The IPTC Credit Field contains credit - find it - $record = find_IPTC_Resource( $IPTC_array, "2:110" ); - - // Check if the Credit Field exists and save the value - if ( $record != FALSE ) - { - $outputarray = add_to_field( $outputarray, 'credit' , strtr( $record['RecData'], $irbtrans ), "\n" ); - } - - /***************************************/ - - // The IPTC Source Field contains source - find it - $record = find_IPTC_Resource( $IPTC_array, "2:115" ); - - // Check if the Source Field exists and save the value - if ( $record != FALSE ) - { - $outputarray = add_to_field( $outputarray, 'source' , strtr( $record['RecData'], $irbtrans ), "\n" ); - } - - /***************************************/ - - // The IPTC Object Name Field contains title - find it - $record = find_IPTC_Resource( $IPTC_array, "2:05" ); - - // Check if the Object Name Field exists and save the value - if ( $record != FALSE ) - { - $outputarray = add_to_field( $outputarray, 'title' , strtr( $record['RecData'], $irbtrans ), "\n" ); - } - - /***************************************/ - - // The IPTC Date Created Field contains date - find it - $record = find_IPTC_Resource( $IPTC_array, "2:55" ); - - // Check if the Date Created Field exists and save the value - if ( $record != FALSE ) - { - $date_array = unpack( "a4Year/a2Month/A2Day", $record['RecData'] ); - $tmpdate = $date_array['Year'] . "-" . $date_array['Month'] . "-" . $date_array['Day']; - $outputarray = add_to_field( $outputarray, 'date' , strtr( $tmpdate, $irbtrans ), "," ); - - } - - /***************************************/ - - // The IPTC City Field contains city - find it - $record = find_IPTC_Resource( $IPTC_array, "2:90" ); - - // Check if the City Field exists and save the value - if ( $record != FALSE ) - { - $outputarray = add_to_field( $outputarray, 'city' , strtr( $record['RecData'], $irbtrans ), "\n" ); - } - - /***************************************/ - - // The IPTC Province/State Field contains state - find it - $record = find_IPTC_Resource( $IPTC_array, "2:95" ); - - // Check if the Province/State Field exists and save the value - if ( $record != FALSE ) - { - $outputarray = add_to_field( $outputarray, 'state' , strtr( $record['RecData'], $irbtrans ), "\n" ); - } - - /***************************************/ - - // The IPTC Country/Primary Location Name Field contains country - find it - $record = find_IPTC_Resource( $IPTC_array, "2:101" ); - - // Check if the Country/Primary Location Name Field exists and save the value - if ( $record != FALSE ) - { - $outputarray = add_to_field( $outputarray, 'country' , strtr( $record['RecData'], $irbtrans ), "\n" ); - } - - /***************************************/ - - // The IPTC Original Transmission Reference Field contains transmissionreference - find it - $record = find_IPTC_Resource( $IPTC_array, "2:103" ); - - // Check if the Original Transmission Reference Field exists and save the value - if ( $record != FALSE ) - { - $outputarray = add_to_field( $outputarray, 'transmissionreference' , strtr( $record['RecData'], $irbtrans ), "\n" ); - } - - /***************************************/ - - // The IPTC Category Field contains category - find it - $record = find_IPTC_Resource( $IPTC_array, "2:15" ); - - // Check if the Category Field exists and save the value - if ( $record != FALSE ) - { - $outputarray = add_to_field( $outputarray, 'category' , strtr( $record['RecData'], $irbtrans ), "\n" ); - } - - - /***************************************/ - - // Cycle through the IPTC records looking for Supplemental Category records - foreach ($IPTC_array as $record) - { - // Check if a Supplemental Category record has been found - if ( $record['IPTC_Type'] == "2:20" ) - { - // A Supplemental Category record has been found, save it's value if the value doesn't already exist - if ( ! in_array ( $record['RecData'], $outputarray['supplementalcategories'])) - { - $outputarray['supplementalcategories'][] = strtr( $record['RecData'], array("\x0a" => "", "\x0d" => " ") ) ; - } - } - } - - - /***************************************/ - - // The IPTC Urgency Field contains urgency - find it - $record = find_IPTC_Resource( $IPTC_array, "2:10" ); - - // Check if the Urgency Field exists and save the value - if ( $record != FALSE ) - { - $outputarray = add_to_field( $outputarray, 'urgency' , strtr( $record['RecData'], $irbtrans ), "\n" ); - } - - - - /***************************************/ - - // Cycle through the IPTC records looking for Keywords records - foreach ($IPTC_array as $record) - { - // Check if a Keywords record has been found - if ( $record['IPTC_Type'] == "2:25" ) - { - // A Keywords record has been found, save it's value if the value doesn't already exist - if ( ! in_array ( $record['RecData'], $outputarray['keywords'])) - { - $outputarray['keywords'][] = strtr( $record['RecData'], array("\x0a" => "", "\x0d" => " ") ) ; - } - } - } - - - /***************************************/ - - // The IPTC Copyright Notice Field contains copyrightnotice - find it - $record = find_IPTC_Resource( $IPTC_array, "2:116" ); - - // Check if the Copyright Field exists and save the value - if ( $record != FALSE ) - { - $outputarray = add_to_field( $outputarray, 'copyrightnotice' , strtr( $record['RecData'], $irbtrans ), "\n" ); - } - - } - } - - - - - /***************************************/ - - // EXIF Processing - - - // Retreive Information from the EXIF data if it exists - - if ( ( $Exif_array != FALSE ) || ( count( $Exif_array ) == 0 ) ) - { - // Check the Image Description Tag - it can contain the caption - if ( array_key_exists( 270, $Exif_array[0] ) ) - { - $outputarray = add_to_field( $outputarray, 'caption' , $Exif_array[0][270]['Data'][0], "\n" ); - } - - /***************************************/ - - // Check the Copyright Information Tag - it contains the copyrightnotice - if ( array_key_exists( 33432, $Exif_array[0] ) ) - { - $outputarray = add_to_field( $outputarray, 'copyrightnotice' , HTML_UTF8_UnEscape( $Exif_array[0][33432]['Data'][0] ), "\n" ); - } - - /***************************************/ - - // Check the Artist Name Tag - it contains the author - if ( array_key_exists( 315, $Exif_array[0] ) ) - { - $outputarray = add_to_field( $outputarray, 'author' , HTML_UTF8_UnEscape( $Exif_array[0][315]['Data'][0] ), "\n" ); - } - - } - - - /***************************/ - - // FINISHED RETRIEVING INFORMATION - - // Perform final processing - - - // Check if any urgency information was found - if ( $outputarray["urgency"] == "" ) - { - // No urgency information was found - set it to default (None) - $outputarray["urgency"] = "none"; - } - - // Check if any copyrightstatus information was found - if ( $outputarray["copyrightstatus"] == "" ) - { - // No copyrightstatus information was found - set it to default (Unmarked) - $outputarray["copyrightstatus"] = "unmarked"; - } - - // Return the resulting Photoshop File Info Array - return $outputarray; - -} - -/****************************************************************************** -* End of Function: get_photoshop_file_info -******************************************************************************/ - - - - - - -/****************************************************************************** -* -* Function: put_photoshop_file_info -* -* Description: Stores Photoshop 'File Info' metadata in the same way that Photoshop -* does. The 'File Info' metadata must be in an array similar to that -* returned by get_photoshop_file_info, as follows: -* -* $file_info_array = array( -* "title" => "", -* "author" => "", -* "authorsposition" => "", // Note: Not used in Photoshop 7 or higher -* "caption" => "", -* "captionwriter" => "", -* "jobname" => "", // Note: Not used in Photoshop CS -* "copyrightstatus" => "", -* "copyrightnotice" => "", -* "ownerurl" => "", -* "keywords" => array( 0 => "", 1 => "", ... ), -* "category" => "", // Note: Max 3 characters -* "supplementalcategories" => array( 0 => "", 1 => "", ... ), -* "date" => "", // Note: DATE MUST BE IN YYYY-MM-DD format -* "city" => "", -* "state" => "", -* "country" => "", -* "credit" => "", -* "source" => "", -* "headline" => "", -* "instructions" => "", -* "transmissionreference" => "", -* "urgency" => "" ); -* -* Parameters: jpeg_header_data - a JPEG header data array in the same format -* as from get_jpeg_header_data. This contains the -* header information which is to be updated. -* new_ps_file_info_array - An array as above, which contains the -* 'File Info' metadata information to be -* written. -* Old_Exif_array - an array containing the EXIF information to be -* updated, as retrieved by get_EXIF_JPEG. (saves having to parse the EXIF again) -* Old_XMP_array - an array containing the XMP information to be -* updated, as retrieved by read_XMP_array_from_text. (saves having to parse the XMP again) -* Old_IRB_array - an array containing the Photoshop IRB information -* to be updated, as retrieved by get_Photoshop_IRB. (saves having to parse the IRB again) -* -* Returns: jpeg_header_data - a JPEG header data array in the same format -* as from get_jpeg_header_data, containing the -* Photshop 'File Info' metadata. This can then -* be written to a file using put_jpeg_header_data. -* -******************************************************************************/ - -function put_photoshop_file_info( $jpeg_header_data, $new_ps_file_info_array, $Old_Exif_array, $Old_XMP_array, $Old_IRB_array ) -{ - /*******************************************/ - // PREPROCESSING - - // Check that the date is in the correct format (YYYY-MM-DD) - - // Explode the date into pieces using the - symbol - $date_pieces = explode( "-", $new_ps_file_info_array[ 'date' ] ); - - // If there are not 3 pieces to the date, it is invalid - if ( count( $date_pieces ) != 3 ) - { - // INVALID DATE - echo "Invalid Date - must be YYYY-MM-DD format
    "; - return FALSE; - } - - // Cycle through each piece of the date - foreach( $date_pieces as $piece ) - { - // If the piece is not numeric, then the date is invalid. - if ( ! is_numeric( $piece ) ) - { - // INVALID DATE - echo "Invalid Date - must be YYYY-MM-DD format
    "; - return FALSE; - } - } - - // Make a unix timestamp at midnight on the date specified - $date_stamp = mktime( 0,0,0, $date_pieces[1], $date_pieces[2], $date_pieces[0] ); - - - - - // Create a translation table to remove carriage return characters - $trans = array( "\x0d" => "" ); - - // Cycle through each of the File Info elements - foreach( $new_ps_file_info_array as $valkey => $val ) - { - // If the element is 'Keywords' or 'Supplemental Categories', then - // it is an array, and needs to be treated as one - if ( ( $valkey != 'supplementalcategories' ) && ( $valkey != 'keywords' ) ) - { - // Not Keywords or Supplemental Categories - // Convert escaped HTML characters to UTF8 and remove carriage returns - $new_ps_file_info_array[ $valkey ] = strtr( HTML_UTF8_UnEscape( $val ), $trans ); - } - else - { - // Either Keywords or Supplemental Categories - // Cycle through the array, - foreach( $val as $subvalkey => $subval ) - { - // Convert escaped HTML characters to UTF8 and remove carriage returns - $new_ps_file_info_array[ $valkey ][ $subvalkey ] = strtr( HTML_UTF8_UnEscape( $subval ), $trans ); - } - } - } - - - - - - /*******************************************/ - - // EXIF Processing - - - // Check if the EXIF array exists - if( $Old_Exif_array == FALSE ) - { - // EXIF Array doesn't exist - create a new one - $new_Exif_array = array ( 'Byte_Align' => "MM", - 'Makernote_Tag' => false, - 'Tags Name' => "TIFF", - 0 => array( "Tags Name" => "TIFF" ) ); - } - else - { - // EXIF Array Does Exist - use it - $new_Exif_array = $Old_Exif_array; - } - - - - // Update the EXIF Image Description Tag with the new value - $new_Exif_array[0][270] = array ( "Tag Name" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 270 ]['Name'], - "Tag Number" => 270, - "Data Type" => 2, - "Type" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 270 ]['Type'], - "Data" => array( HTML_UTF8_Escape( $new_ps_file_info_array[ 'caption' ]) )); - - // Update the EXIF Artist Name Tag with the new value - $new_Exif_array[0][315] = array ( "Tag Name" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 315 ]['Name'], - "Tag Number" => 315, - "Data Type" => 2, - "Type" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 315 ]['Type'], - "Data" => array( HTML_UTF8_Escape( $new_ps_file_info_array[ 'author' ] ) ) ); - - // Update the EXIF Copyright Information Tag with the new value - $new_Exif_array[0][33432] = array ( "Tag Name" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 33432 ]['Name'], - "Tag Number" => 33432, - "Data Type" => 2, - "Type" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 33432 ]['Type'], - "Data" => array( HTML_UTF8_Escape( $new_ps_file_info_array[ 'copyrightnotice' ]) ) ); - - - // Photoshop checks if the "Date and Time of Original" and "Date and Time when Digitized" tags exist - // If they don't exist, it means that the EXIF date may be wiped out if it is changed, so Photoshop - // copies the EXIF date to these two tags - - if ( ( array_key_exists( 306, $new_Exif_array[0] ) )&& - ( array_key_exists( 34665, $new_Exif_array[0] ) ) && - ( array_key_exists( 0, $new_Exif_array[0][34665] ) ) ) - { - // Replace "Date and Time of Original" if it doesn't exist - if ( ! array_key_exists( 36867, $new_Exif_array[0][34665][0] ) ) - { - $new_Exif_array[0][34665][0][36867] = array ( "Tag Name" => $GLOBALS[ "IFD_Tag_Definitions" ]['EXIF'][ 36867 ]['Name'], - "Tag Number" => 36867, - "Data Type" => 2, - "Type" => $GLOBALS[ "IFD_Tag_Definitions" ]['EXIF'][ 36867 ]['Type'], - "Data" => $new_Exif_array[0][306]['Data'] ); - } - - // Replace "Date and Time when Digitized" if it doesn't exist - if ( ! array_key_exists( 36868, $new_Exif_array[0][34665][0] ) ) - { - $new_Exif_array[0][34665][0][36868] = array ( "Tag Name" => $GLOBALS[ "IFD_Tag_Definitions" ]['EXIF'][ 36868 ]['Name'], - "Tag Number" => 36868, - "Data Type" => 2, - "Type" => $GLOBALS[ "IFD_Tag_Definitions" ]['EXIF'][ 36868 ]['Type'], - "Data" => $new_Exif_array[0][306]['Data'] ); - } - } - - - // Photoshop changes the EXIF date Tag (306) to the current date, not the date that was entered in File Info - $exif_date = date ( "Y:m:d H:i:s" ); - - // Update the EXIF Date and Time Tag with the new value - $new_Exif_array[0][306] = array ( "Tag Name" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 306 ]['Name'], - "Tag Number" => 306, - "Data Type" => 2, - "Type" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 306 ]['Type'], - "Data" => array( $exif_date ) ); - - - - // Photoshop replaces the EXIF Software or Firmware Tag with "Adobe Photoshop ..." - // This toolkit instead preserves existing value and appends the toolkit name to the end of it - - // Check if the EXIF Software or Firmware Tag exists - if ( array_key_exists( 305, $new_Exif_array[0] ) ) - { - // An existing EXIF Software or Firmware Tag was found - // Check if the existing Software or Firmware Tag already contains the Toolkit's name - if ( stristr ( $new_Exif_array[0][305]['Data'][0], $GLOBALS[ "Software Name" ]) == FALSE ) - { - // Toolkit Name string not found in the existing Software/Firmware string - append it. - $firmware_str = $new_Exif_array[0][305]['Data'][0] . " " . $GLOBALS[ "Software Name" ]; - } - else - { - // Toolkit name already exists in Software/Firmware string - don't put another copy in the string - $firmware_str = $new_Exif_array[0][305]['Data'][0]; - } - } - else - { - // No Software/Firmware string exists - create one - $firmware_str = $GLOBALS[ "Software Name" ]; - } - - // Update the EXIF Software/Firmware Tag with the new value - $new_Exif_array[0][305] = array( "Tag Name" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 305 ]['Name'], - "Tag Number" => 305, - "Data Type" => 2, - "Type" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 305 ]['Type'], - "Data" => array( HTML_UTF8_Escape( $firmware_str ) ) ); - - - - - - /*******************************************/ - - // Photoshop IRB Processing - - - // Check if there is an existing Photoshop IRB array - if ($Old_IRB_array == FALSE ) - { - // No existing IRB array - create one - $new_IRB_array = array(); - } - else - { - // There is an existing Photoshop IRB array - use it - $new_IRB_array = $Old_IRB_array; - } - - // Remove any existing Copyright Flag, URL, or IPTC resources - these will be re-written - foreach( $new_IRB_array as $resno => $res ) - { - if ( ( $res[ 'ResID' ] == 0x040A ) || - ( $res[ 'ResID' ] == 0x040B ) || - ( $res[ 'ResID' ] == 0x0404 ) ) - { - array_splice( $new_IRB_array, $resno, 1 ); - } - } - - - // Add a new Copyright Flag resource - if ( $new_ps_file_info_array[ 'copyrightstatus' ] == "Copyrighted Work" ) - { - $PS_copyright_flag = "\x01"; // Copyrighted - } - else - { - $PS_copyright_flag = "\x00"; // Public domain or Unmarked - } - $new_IRB_array[] = array( 'ResID' => 0x040A, - 'ResName' => $GLOBALS[ "Photoshop_ID_Names" ][0x040A], - 'ResDesc' => $GLOBALS[ "Photoshop_ID_Descriptions" ][0x040A], - 'ResEmbeddedName' => "", - 'ResData' => $PS_copyright_flag ); - - - - // Add a new URL resource - $new_IRB_array[] = array( 'ResID' => 0x040B, - 'ResName' => $GLOBALS[ "Photoshop_ID_Names" ][0x040B], - 'ResDesc' => $GLOBALS[ "Photoshop_ID_Descriptions" ][0x040B], - 'ResEmbeddedName' => "", - 'ResData' => $new_ps_file_info_array[ 'ownerurl' ] ); - - - - // Create IPTC resource - - // IPTC requires date to be in the following format YYYYMMDD - $iptc_date = date( "Ymd", $date_stamp ); - - // Create the new IPTC array - $new_IPTC_array = array ( - 0 => - array ( - 'IPTC_Type' => '2:00', - 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:00'], - 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:00'], - 'RecData' => "\x00\x02", - ), - 1 => - array ( - 'IPTC_Type' => '2:120', - 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:120'], - 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:120'], - 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'caption' ] ), 0 , 2000 ), - ), - 2 => - array ( - 'IPTC_Type' => '2:122', - 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:122'], - 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:122'], - 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'captionwriter' ] ), 0 , 32 ), - ), - 3 => - array ( - 'IPTC_Type' => '2:105', - 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:105'], - 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:105'], - 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'headline' ] ), 0 , 256 ), - ), - 4 => - array ( - 'IPTC_Type' => '2:40', - 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:40'], - 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:40'], - 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'instructions' ] ), 0, 256 ), - ), - 5 => - array ( - 'IPTC_Type' => '2:80', - 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:80'], - 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:80'], - 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'author' ] ), 0, 32 ), - ), - 6 => - array ( - 'IPTC_Type' => '2:85', - 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:85'], - 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:85'], - 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'authorsposition' ] ), 0, 32 ), - ), - 7 => - array ( - 'IPTC_Type' => '2:110', - 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:110'], - 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:110'], - 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'credit' ] ), 0, 32 ), - ), - 8 => - array ( - 'IPTC_Type' => '2:115', - 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:115'], - 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:115'], - 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'source' ] ), 0, 32 ), - ), - 9 => - array ( - 'IPTC_Type' => '2:05', - 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:05'], - 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:05'], - 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'title' ] ), 0, 64 ), - ), - 10 => - array ( - 'IPTC_Type' => '2:55', - 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:55'], - 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:55'], - 'RecData' => "$iptc_date", - ), - 11 => - array ( - 'IPTC_Type' => '2:90', - 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:90'], - 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:90'], - 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'city' ] ), 0, 32 ), - ), - 12 => - array ( - 'IPTC_Type' => '2:95', - 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:95'], - 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:95'], - 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'state' ] ), 0, 32 ), - ), - 13 => - array ( - 'IPTC_Type' => '2:101', - 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:101'], - 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:101'], - 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'country' ] ), 0, 64 ), - ), - 14 => - array ( - 'IPTC_Type' => '2:103', - 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:103'], - 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:103'], - 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'transmissionreference' ] ), 0, 32 ), - ), - 15 => - array ( - 'IPTC_Type' => '2:15', - 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:15'], - 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:15'], - 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'category' ] ), 0, 3 ), - ), - 21 => - array ( - 'IPTC_Type' => '2:116', - 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:10'], - 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:10'], - 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'copyrightnotice' ] ), 0, 128 ), - ), - ); - - // Check the value of urgency is valid - if ( ( $new_ps_file_info_array[ 'urgency' ] > 0 ) && ( $new_ps_file_info_array[ 'urgency' ] < 9 ) ) - { - // Add the Urgency item to the IPTC array - $new_IPTC_array[] = array ( - 'IPTC_Type' => '2:10', - 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:10'], - 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:10'], - 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'urgency' ] ), 0, 1 ), - ); - } - - // Cycle through the Supplemental Categories, - foreach( $new_ps_file_info_array[ 'supplementalcategories' ] as $supcat ) - { - // Add this Supplemental Category to the IPTC array - $new_IPTC_array[] = array ( - 'IPTC_Type' => '2:20', - 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:20'], - 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:20'], - 'RecData' => HTML_UTF8_Escape( $supcat ), - ); - } - - - // Cycle through the Keywords, - foreach( $new_ps_file_info_array[ 'keywords' ] as $keyword ) - { - // Add this Keyword to the IPTC array - $new_IPTC_array[] = array ( - 'IPTC_Type' => '2:25', - 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:25'], - 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:25'], - 'RecData' => $keyword, - ); - } - - - /***********************************/ - - // XMP Processing - - // Check if XMP existed previously - if ($Old_XMP_array == FALSE ) - { - // XMP didn't exist - create a new one based on a blank structure - $new_XMP_array = XMP_Check( $GLOBALS[ 'Blank XMP Structure' ], array( ) ); - } - else - { - // XMP does exist - // Some old XMP processors used x:xapmeta, check for this - if ( $Old_XMP_array[0]['tag'] == 'x:xapmeta' ) - { - // x:xapmeta found - change it to x:xmpmeta - $Old_XMP_array[0]['tag'] = 'x:xmpmeta'; - } - - // Ensure that the existing XMP has all required fields, and add any that are missing - $new_XMP_array = XMP_Check( $GLOBALS[ 'Blank XMP Structure' ], $Old_XMP_array ); - } - - - // Process the XMP Photoshop block - - // Find the Photoshop Information within the XMP block - $photoshop_block = & find_XMP_block( $new_XMP_array, "photoshop" ); - - // The Photoshop CaptionWriter tag contains captionwriter - Find it and Update the value - $Item = & find_XMP_item( $photoshop_block, "photoshop:CaptionWriter" ); - $Item[ 'value' ] = $new_ps_file_info_array[ 'captionwriter' ]; - - // The Photoshop Category tag contains category - Find it and Update the value - $Item = & find_XMP_item( $photoshop_block, "photoshop:Category" ); - $Item[ 'value' ] = $new_ps_file_info_array[ 'category' ]; - - // The Photoshop DateCreated tag contains date - Find it and Update the value - $Item = & find_XMP_item( $photoshop_block, "photoshop:DateCreated" ); - $Item[ 'value' ] = $new_ps_file_info_array[ 'date' ]; - - // The Photoshop City tag contains city - Find it and Update the value - $Item = & find_XMP_item( $photoshop_block, "photoshop:City" ); - $Item[ 'value' ] = $new_ps_file_info_array[ 'city' ]; - - // The Photoshop State tag contains state - Find it and Update the value - $Item = & find_XMP_item( $photoshop_block, "photoshop:State" ); - $Item[ 'value' ] = $new_ps_file_info_array[ 'state' ]; - - // The Photoshop Country tag contains country - Find it and Update the value - $Item = & find_XMP_item( $photoshop_block, "photoshop:Country" ); - $Item[ 'value' ] = $new_ps_file_info_array[ 'country' ]; - - // The Photoshop AuthorsPosition tag contains authorsposition - Find it and Update the value - $Item = & find_XMP_item( $photoshop_block, "photoshop:AuthorsPosition" ); - $Item[ 'value' ] = $new_ps_file_info_array[ 'authorsposition' ]; - - // The Photoshop Credit tag contains credit - Find it and Update the value - $Item = & find_XMP_item( $photoshop_block, "photoshop:Credit" ); - $Item[ 'value' ] = $new_ps_file_info_array[ 'credit' ]; - - // The Photoshop Source tag contains source - Find it and Update the value - $Item = & find_XMP_item( $photoshop_block, "photoshop:Source" ); - $Item[ 'value' ] = $new_ps_file_info_array[ 'source' ]; - - // The Photoshop Headline tag contains headline - Find it and Update the value - $Item = & find_XMP_item( $photoshop_block, "photoshop:Headline" ); - $Item[ 'value' ] = $new_ps_file_info_array[ 'headline' ]; - - // The Photoshop Instructions tag contains instructions - Find it and Update the value - $Item = & find_XMP_item( $photoshop_block, "photoshop:Instructions" ); - $Item[ 'value' ] = $new_ps_file_info_array[ 'instructions' ]; - - // The Photoshop TransmissionReference tag contains transmissionreference - Find it and Update the value - $Item = & find_XMP_item( $photoshop_block, "photoshop:TransmissionReference" ); - $Item[ 'value' ] = $new_ps_file_info_array[ 'transmissionreference' ]; - - // The Photoshop Urgency tag contains urgency - Find it and Update the value - $Item = & find_XMP_item( $photoshop_block, "photoshop:Urgency" ); - $Item[ 'value' ] = $new_ps_file_info_array[ 'urgency' ]; - - // The Photoshop SupplementalCategories tag contains supplementalcategories - Find it - $Item = & find_XMP_item( $photoshop_block, "photoshop:SupplementalCategories" ); - - // Create an array to receive the XML list items for the Supplemental Categories - $new_supcat_array = array( ); - - // Cycle through the Supplemental Categories - foreach ( $new_ps_file_info_array[ 'supplementalcategories' ] as $sup_category ) - { - // Add a new list item for this Supplemental Category - $new_supcat_array[] = array( 'tag' => 'rdf:li', 'value' => $sup_category ); - } - - // Add the array of Supplemental Category List Items to the Photoshop SupplementalCategories tag - $Item[ 'children' ][ 0 ][ 'children' ] = $new_supcat_array; - - - - // Process the XMP XAP block - - // Find the XAP Information within the XMP block - $XAP_block = & find_XMP_block( $new_XMP_array, "xap" ); - - // The XAP CreateDate tag contains date XMP was first created - Find it and Update the value - $Item = & find_XMP_item( $XAP_block, "xap:CreateDate" ); - - // Check if the CreateDate is blank - if ( $Item[ 'value' ] == "" ) - { - // CreateDate is blank - we must have just added it - set it to the current date - $Item[ 'value' ] = date( "Y-m-d\TH:i:s" ); - $Item[ 'value' ] .= get_Local_Timezone_Offset( ); - } - - - // The XAP ModifyDate tag contains last resource change date - Find it and Update the value to the current date - $Item = & find_XMP_item( $XAP_block, "xap:ModifyDate" ); - $Item[ 'value' ] = date( "Y-m-d\TH:i:s" ); - $Item[ 'value' ] .= get_Local_Timezone_Offset( ); - - // The XAP ModifyDate tag contains last XMP change date - Find it and Update the value to the current date - $Item = & find_XMP_item( $XAP_block, "xap:MetadataDate" ); - $Item[ 'value' ] = date( "Y-m-d\TH:i:s" ); - $Item[ 'value' ] .= get_Local_Timezone_Offset( ); - - - - // The XAP CreatorTool tag contains name of the software editor - Find it - $Item = & find_XMP_item( $XAP_block, "xap:CreatorTool" ); - - // Photoshop replaces the CreatorTool with "Adobe Photoshop ..." - // This toolkit instead preserves existing value and appends the toolkit name to the end of it - - // Check if a CreatorTool already exists - if ( $Item[ 'value' ] != "" ) - { - // An existing CreatorTool was found - // Check if the existing CreatorTool already contains the Toolkit's name - if ( stristr ( $Item[ 'value' ], $GLOBALS[ "Software Name" ]) == FALSE ) - { - // Toolkit Name string not found in the existing CreatorTool string - append it. - $Item[ 'value' ] = $Item[ 'value' ] . " " . $GLOBALS[ "Software Name" ]; - } - else - { - // Toolkit name already exists in CreatorTool string - leave as is - } - } - else - { - // No CreatorTool string exists - create one - $Item[ 'value' ] = $GLOBALS[ "Software Name" ]; - } - - - - - // Process the XMP Basic Job Information block - - // Find the XAP Basic Job Information within the XMP block - $XAPBJ_block = & find_XMP_block( $new_XMP_array, "xapBJ" ); - - // The XAP Basic Job JobRef tag contains urgency - Find it and Update the value - $Item = & find_XMP_item( $XAPBJ_block, "xapBJ:JobRef" ); - $Item[ 'children' ][ 0 ][ 'children' ] = - array( array ( 'tag' => 'rdf:li', - 'attributes' => array ( 'rdf:parseType' => 'Resource' ), - 'children' => array ( 0 => array ( 'tag' => 'stJob:name', - 'value' => $new_ps_file_info_array[ 'jobname' ] ), - ), - ), - ); - - - - - // Process the XMP XAP Rights Information block - - // Find the XAP Rights Information within the XMP block - $XAPRights_block = & find_XMP_block( $new_XMP_array, "xapRights" ); - - - - // The XAP Rights Marked tag should only be present if copyrightstatus is 'Copyrighted Work' or 'Public Domain' - // If copyrightstatus 'Unmarked' or anything else, the XAP Rights Marked tag should be missing - - - // Remove any existing XAP Rights Marked tags - they will be replaced - foreach( $XAPRights_block as $tagno => $tag ) - { - if ( $tag[ 'tag' ] == "xapRights:Marked" ) - { - array_splice( $XAPRights_block, $tagno, 1 ); - } - } - - // Check the value of the copyrightstatus flag - if ( $new_ps_file_info_array[ 'copyrightstatus' ] == "Copyrighted Work" ) - { - // Copyrighted - add the tag - $XAPRights_block[] = array ( 'tag' => 'xapRights:Marked', 'value' => 'True' ); - } - else if ( $new_ps_file_info_array[ 'copyrightstatus' ] == "Public Domain" ) - { - // Public domain - add the tag - $XAPRights_block[] = array ( 'tag' => 'xapRights:Marked', 'value' => 'False' ); - } - else - { - // Unmarked or Other - Do nothing - don't add a Marked tag - } - - - // The XAP Rights WebStatement tag contains ownerurl - Find it and Update the value - $Item = & find_XMP_item( $XAPRights_block, "xapRights:WebStatement" ); - $Item[ 'value' ] = $new_ps_file_info_array[ 'ownerurl' ]; - - - - - // Process the XMP Dublin Core block - - // Find the Dublin Core Information within the XMP block - $DC_block = & find_XMP_block( $new_XMP_array, "dc" ); - - - // The Dublin Core description tag contains caption - Find it and Update the value - $Item = & find_XMP_item( $DC_block, "dc:description" ); - $Item[ 'children' ][ 0 ][ 'children' ] = array( array( 'tag' => "rdf:li", - 'value' => $new_ps_file_info_array[ 'caption' ], - 'attributes' => array( 'xml:lang' => "x-default" ) ) ); - - - // The Dublin Core title tag contains title - Find it and Update the value - $Item = & find_XMP_item( $DC_block, "dc:title" ); - $Item[ 'children' ][ 0 ][ 'children' ] = array( array( 'tag' => "rdf:li", - 'value' => $new_ps_file_info_array[ 'title' ], - 'attributes' => array( 'xml:lang' => "x-default" ) ) ); - - - // The Dublin Core rights tag contains copyrightnotice - Find it and Update the value - $Item = & find_XMP_item( $DC_block, "dc:rights" ); - $Item[ 'children' ][ 0 ][ 'children' ] = array( array( 'tag' => "rdf:li", - 'value' => $new_ps_file_info_array[ 'copyrightnotice' ], - 'attributes' => array( 'xml:lang' => "x-default" ) ) ); - - // The Dublin Core creator tag contains author - Find it and Update the value - $Item = & find_XMP_item( $DC_block, "dc:creator" ); - $Item[ 'children' ][ 0 ][ 'children' ] = array( array( 'tag' => "rdf:li", - 'value' => $new_ps_file_info_array[ 'author' ]) ); - - // The Dublin Core subject tag contains keywords - Find it - $Item = & find_XMP_item( $DC_block, "dc:subject" ); - - // Create an array to receive the Keywords List Items - $new_keywords_array = array( ); - - // Cycle through each keyword - foreach( $new_ps_file_info_array[ 'keywords' ] as $keyword ) - { - // Add a List item for this keyword - $new_keywords_array[] = array( 'tag' => "rdf:li", 'value' => $keyword ); - } - // Add the Keywords List Items array to the Dublin Core subject tag - $Item[ 'children' ][ 0 ][ 'children' ] = $new_keywords_array; - - - - /***************************************/ - - // FINISHED UPDATING VALUES - - // Insert the new IPTC array into the Photoshop IRB array - $new_IRB_array = put_Photoshop_IPTC( $new_IRB_array, $new_IPTC_array ); - - // Write the EXIF array to the JPEG header - $jpeg_header_data = put_EXIF_JPEG( $new_Exif_array, $jpeg_header_data ); - - // Convert the XMP array to XMP text - $xmp_text = write_XMP_array_to_text( $new_XMP_array ); - - // Write the XMP text to the JPEG Header - $jpeg_header_data = put_XMP_text( $jpeg_header_data, $xmp_text ); - - // Write the Photoshop IRB array to the JPEG header - $jpeg_header_data = put_Photoshop_IRB( $jpeg_header_data, $new_IRB_array ); - - return $jpeg_header_data; - -} - -/****************************************************************************** -* End of Function: put_photoshop_file_info -******************************************************************************/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -/****************************************************************************** -* -* INTERNAL FUNCTIONS -* -******************************************************************************/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -/****************************************************************************** -* -* Function: get_Local_Timezone_Offset -* -* Description: Returns a string indicating the time difference between the local -* timezone and GMT in hours and minutes, e.g. +10:00 or -06:30 -* -* Parameters: None -* -* Returns: $tz_str - a string containing the timezone offset -* -******************************************************************************/ - -function get_Local_Timezone_Offset( ) -{ - // Retrieve the Timezone offset in seconds - $tz_seconds = date( "Z" ); - - // Check if the offset is less than zero - if ( $tz_seconds < 0 ) - { - // Offset is less than zero - add a Minus sign to the output - $tz_str = "-"; - } - else - { - // Offset is greater than or equal to zero - add a Plus sign to the output - $tz_str = "+"; - } - - // Add the absolute offset to the output, formatted as HH:MM - $tz_str .= gmdate( "H:i", abs($tz_seconds) ); - - // Return the result - return $tz_str; -} - -/****************************************************************************** -* End of Function: get_Local_Timezone_Offset -******************************************************************************/ - - - -/****************************************************************************** -* -* Function: XMP_Check -* -* Description: Checks a given XMP array against a reference array, and adds any -* missing blocks and tags -* -* NOTE: This is a recursive function -* -* Parameters: reference_array - The standard XMP array which contains all required tags -* check_array - The XMP array to check -* -* Returns: output - a string containing the timezone offset -* -******************************************************************************/ - -function XMP_Check( $reference_array, $check_array) -{ - // Cycle through each of the elements of the reference XMP array - foreach( $reference_array as $valkey => $val ) - { - - // Search for the current reference tag within the XMP array to be checked - $tagpos = find_XMP_Tag( $check_array, $val ); - - // Check if the tag was found - if ( $tagpos === FALSE ) - { - // Tag not found - Add tag to array being checked - $tagpos = count( $check_array ); - $check_array[ $tagpos ] = $val; - } - - // Check if the reference tag has children - if ( array_key_exists( 'children', $val ) ) - { - // Reference tag has children - these need to be checked too - - // Determine if the array being checked has children for this tag - if ( ! array_key_exists( 'children', $check_array[ $tagpos ] ) ) - { - // Array being checked has no children - add a blank children array - $check_array[ $tagpos ][ 'children' ] = array( ); - } - - // Recurse, checking the children tags against the reference children - $check_array[ $tagpos ][ 'children' ] = XMP_Check( $val[ 'children' ] , $check_array[ $tagpos ][ 'children' ] ); - } - else - { - // No children - don't need to check anything else - } - } - - // Return the checked XMP array - return $check_array; -} - - -/****************************************************************************** -* End of Function: XMP_Check -******************************************************************************/ - - - - -/****************************************************************************** -* -* Function: find_XMP_Tag -* -* Description: Searches one level of an XMP array for a specific tag, and -* returns the tag position. Does not descend the XMP tree. -* -* Parameters: XMP_array - The XMP array which should be searched -* tag - The XMP tag to search for (in same format as would be found in XMP array) -* -* Returns: output - a string containing the timezone offset -* -******************************************************************************/ - -function find_XMP_Tag( $XMP_array, $tag ) -{ - $namespacestr = ""; - - // Some tags have a namespace attribute which defines them (i.e. rdf:Description tags) - - // Check if the tag being searched for has attributs - if ( array_key_exists( 'attributes', $tag ) ) - { - // Tag has attributes - cycle through them - foreach( $tag['attributes'] as $key => $val ) - { - // Check if the current attribute is the namespace attribute - i.e. starts with xmlns: - if ( strcasecmp( substr($key,0,6), "xmlns:" ) == 0 ) - { - // Found a namespace attribute - save it for later. - $namespacestr = $key; - } - } - } - - - - // Cycle through the elements of the XMP array to be searched. - foreach( $XMP_array as $valkey => $val ) - { - - // Check if the current element is a rdf:Description tag - if ( strcasecmp ( $tag[ 'tag' ], 'rdf:Description' ) == 0 ) - { - // Current element is a rdf:Description tag - // Check if the namespace attribute is the same as in the tag that is being searched for - if ( array_key_exists( $namespacestr, $val['attributes'] ) ) - { - // Namespace is the same - this is the correct tag - return it's position - return $valkey; - } - } - // Otherwise check if the current element has the same name as the tag in question - else if ( strcasecmp ( $val[ 'tag' ], $tag[ 'tag' ] ) == 0 ) - { - // Tags have same name - this is the correct tag - return it's position - return $valkey; - } - } - - // Cycled through all tags without finding the correct one - return error value - return FALSE; -} - -/****************************************************************************** -* End of Function: find_XMP_Tag -******************************************************************************/ - - - - -/****************************************************************************** -* -* Function: create_GUID -* -* Description: Creates a Globally Unique IDentifier, in the format that is used -* by XMP (and Windows). This value is not guaranteed to be 100% unique, -* but it is ridiculously unlikely that two identical values will be produced -* -* Parameters: none -* -* Returns: output - a string containing the timezone offset -* -******************************************************************************/ - -function create_GUID( ) -{ - // Create a md5 sum of a random number - this is a 32 character hex string - $raw_GUID = md5( uniqid( getmypid() . rand( ) . (double)microtime()*1000000, TRUE ) ); - - // Format the string into 8-4-4-4-12 (numbers are the number of characters in each block) - return substr($raw_GUID,0,8) . "-" . substr($raw_GUID,8,4) . "-" . substr($raw_GUID,12,4) . "-" . substr($raw_GUID,16,4) . "-" . substr($raw_GUID,20,12); -} - -/****************************************************************************** -* End of Function: create_GUID -******************************************************************************/ - - - - - -/****************************************************************************** -* -* Function: add_to_field -* -* Description: Adds a value to a particular field in a Photoshop File Info array, -* first checking whether the value is already there. If the value is -* already in the array, it is not changed, otherwise the value is appended -* to whatever is already in that field of the array -* -* Parameters: field_array - The Photoshop File Info array to receive the new value -* field - The File Info field which the value is for -* value - The value to be written into the File Info -* separator - The string to place between values when having to append the value -* -* Returns: output - the Photoshop File Info array with the value added -* -******************************************************************************/ - -function add_to_field( $field_array, $field, $value, $separator ) -{ - // Check if the value is blank - if ( $value == "" ) - { - // Value is blank - return File Info array unchanged - return $field_array; - } - - // Check if the value can be found anywhere within the existing value for this field - if ( stristr ( $field_array[ $field ], $value ) == FALSE) - { - // Value could not be found - // Check if the existing value for the field is blank - if ( $field_array[$field] != "" ) - { - // Existing value for field is not blank - append a separator - $field_array[$field] .= $separator; - } - // Append the value to the field - $field_array[$field] .= $value; - } - - // Return the File Info Array - return $field_array; -} - -/****************************************************************************** -* End of Function: add_to_field -******************************************************************************/ - - - -/****************************************************************************** -* -* Function: find_IPTC_Resource -* -* Description: Searches an IPTC array for a particular record, and returns it if found -* -* Parameters: IPTC_array - The IPTC array to search -* record_type - The IPTC record number to search for (e.g. 2:151 ) -* -* Returns: output - the contents of the record if found -* FALSE - otherwise -* -******************************************************************************/ - -function find_IPTC_Resource( $IPTC_array, $record_type ) -{ - // Cycle through the ITPC records - foreach ($IPTC_array as $record) - { - // Check the IPTC type against the required type - if ( $record['IPTC_Type'] == $record_type ) - { - // IPTC type matches - return this record - return $record; - } - } - - // No matching record found - return error code - return FALSE; -} - -/****************************************************************************** -* End of Function: find_IPTC_Resource -******************************************************************************/ - - - - -/****************************************************************************** -* -* Function: find_Photoshop_IRB_Resource -* -* Description: Searches a Photoshop IRB array for a particular resource, and returns it if found -* -* Parameters: IRB_array - The IRB array to search -* resource_ID - The IRB resource number to search for (e.g. 0x03F9 ) -* -* Returns: output - the contents of the resource if found -* FALSE - otherwise -* -******************************************************************************/ - -function find_Photoshop_IRB_Resource( $IRB_array, $resource_ID ) -{ - // Cycle through the IRB resources - foreach( $IRB_array as $IRB_Resource ) - { - // Check the IRB resource ID against the required ID - if ( $resource_ID == $IRB_Resource['ResID'] ) - { - // Resource ID matches - return this resource - return $IRB_Resource; - } - } - - // No matching resource found - return error code - return FALSE; -} - -/****************************************************************************** -* End of Function: find_Photoshop_IRB_Resource -******************************************************************************/ - - - - - - - - -/****************************************************************************** -* -* Function: find_XMP_item -* -* Description: Searches a one level of a XMP array for a particular item by name, and returns it if found. -* Does not descend through the XMP array -* -* Parameters: Item_Array - The XMP array to search -* item_name - The name of the tag to serch for (e.g. photoshop:CaptionWriter ) -* -* Returns: output - the contents of the tag if found -* FALSE - otherwise -* -******************************************************************************/ - -function find_XMP_item( & $Item_Array, $item_name ) -{ - // Cycle through the top level of the XMP array - foreach( $Item_Array as $Item_Key => $Item ) - { - // Check this tag name against the required tag name - if( $Item['tag'] == $item_name ) - { - // The tag names match - return the item - return $Item_Array[ $Item_Key ]; - } - } - - // No matching tag found - return error code - return FALSE; -} - -/****************************************************************************** -* End of Function: find_XMP_item -******************************************************************************/ - - - - - -/****************************************************************************** -* -* Function: find_XMP_block -* -* Description: Searches a for a particular rdf:Description block within a XMP array, and returns its children if found. -* -* Parameters: XMP_array - The XMP array to search as returned by read_XMP_array_from_text -* block_name - The namespace of the XMP block to be found (e.g. photoshop or xapRights ) -* -* Returns: output - the children of the tag if found -* FALSE - otherwise -* -******************************************************************************/ - -function find_XMP_block( & $XMP_array, $block_name ) -{ - // Check that the rdf:RDF section can be found (which contains the rdf:Description tags - if ( ( $XMP_array !== FALSE ) && - ( ( $XMP_array[0]['tag'] == "x:xapmeta" ) || - ( $XMP_array[0]['tag'] == "x:xmpmeta" ) ) && - ( $XMP_array[0]['children'][0]['tag'] == "rdf:RDF" ) ) - { - // Found rdf:RDF - // Make it's children easily accessible - $RDF_Contents = $XMP_array[0]['children'][0]['children']; - - // Cycle through the children (rdf:Description tags) - foreach ($RDF_Contents as $RDF_Key => $RDF_Item) - { - // Check if this is a rdf:description tag that has children - if ( ( $RDF_Item['tag'] == "rdf:Description" ) && - ( array_key_exists( 'children', $RDF_Item ) ) ) - { - // RDF Description tag has children, - // Cycle through it's attributes - foreach( $RDF_Item['attributes'] as $key => $val ) - { - // Check if this attribute matches the namespace block name required - if ( $key == "xmlns:$block_name" ) - { - // Namespace matches required block name - return it's children - return $XMP_array[0]['children'][0]['children'][ $RDF_Key ]['children']; - } - } - } - } - } - - // No matching rdf:Description block found - return FALSE; -} - -/****************************************************************************** -* End of Function: find_XMP_block -******************************************************************************/ - - - - - - - - - -/****************************************************************************** -* Global Variable: Blank XMP Structure -* -* Contents: A template XMP array which can be used to create a new XMP segment -* -******************************************************************************/ - -// Create a GUID to be used in this template array -$new_GUID = create_GUID( ); - -$GLOBALS[ 'Blank XMP Structure' ] = -array ( - 0 => - array ( - 'tag' => 'x:xmpmeta', - 'attributes' => - array ( - 'xmlns:x' => 'adobe:ns:meta/', - 'x:xmptk' => 'XMP toolkit 3.0-28, framework 1.6', - ), - 'children' => - array ( - 0 => - array ( - 'tag' => 'rdf:RDF', - 'attributes' => - array ( - 'xmlns:rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', - 'xmlns:iX' => 'http://ns.adobe.com/iX/1.0/', - ), - 'children' => - array ( - 1 => - array ( - 'tag' => 'rdf:Description', - 'attributes' => - array ( - 'rdf:about' => "uuid:$new_GUID", - 'xmlns:pdf' => 'http://ns.adobe.com/pdf/1.3/', - ), - ), - 2 => - array ( - 'tag' => 'rdf:Description', - 'attributes' => - array ( - 'rdf:about' => "uuid:$new_GUID", - 'xmlns:photoshop' => 'http://ns.adobe.com/photoshop/1.0/', - ), - 'children' => - array ( - 0 => - array ( - 'tag' => 'photoshop:CaptionWriter', - 'value' => '', - ), - 1 => - array ( - 'tag' => 'photoshop:Category', - 'value' => '', - ), - 2 => - array ( - 'tag' => 'photoshop:DateCreated', - 'value' => '', - ), - 3 => - array ( - 'tag' => 'photoshop:City', - 'value' => '', - ), - 4 => - array ( - 'tag' => 'photoshop:State', - 'value' => '', - ), - 5 => - array ( - 'tag' => 'photoshop:Country', - 'value' => '', - ), - 6 => - array ( - 'tag' => 'photoshop:Credit', - 'value' => '', - ), - 7 => - array ( - 'tag' => 'photoshop:Source', - 'value' => '', - ), - 8 => - array ( - 'tag' => 'photoshop:Headline', - 'value' => '', - ), - 9 => - array ( - 'tag' => 'photoshop:Instructions', - 'value' => '', - ), - 10 => - array ( - 'tag' => 'photoshop:TransmissionReference', - 'value' => '', - ), - 11 => - array ( - 'tag' => 'photoshop:Urgency', - 'value' => '', - ), - 12 => - array ( - 'tag' => 'photoshop:SupplementalCategories', - 'children' => - array ( - 0 => - array ( - 'tag' => 'rdf:Bag', - ), - ), - ), - 13 => - array ( - 'tag' => 'photoshop:AuthorsPosition', - 'value' => '', - ), - ), - ), - 4 => - array ( - 'tag' => 'rdf:Description', - 'attributes' => - array ( - 'rdf:about' => "uuid:$new_GUID", - 'xmlns:xap' => 'http://ns.adobe.com/xap/1.0/', - ), - 'children' => - array ( - 0 => - array ( - 'tag' => 'xap:CreateDate', - 'value' => '', - ), - 1 => - array ( - 'tag' => 'xap:ModifyDate', - 'value' => '', - ), - 2 => - array ( - 'tag' => 'xap:MetadataDate', - 'value' => '', - ), - 3 => - array ( - 'tag' => 'xap:CreatorTool', - 'value' => '', - ), - ), - ), - 5 => - array ( - 'tag' => 'rdf:Description', - 'attributes' => - array ( - 'about' => "uuid:$new_GUID", - 'xmlns:stJob' => 'http://ns.adobe.com/xap/1.0/sType/Job#', - 'xmlns:xapBJ' => 'http://ns.adobe.com/xap/1.0/bj/', - ), - 'children' => - array ( - 0 => - array ( - 'tag' => 'xapBJ:JobRef', - 'children' => - array ( - 0 => - array ( - 'tag' => 'rdf:Bag', - 'children' => - array ( - ), - ), - ), - ), - ), - ), - 6 => - array ( - 'tag' => 'rdf:Description', - 'attributes' => - array ( - 'rdf:about' => "uuid:$new_GUID", - 'xmlns:xapRights' => 'http://ns.adobe.com/xap/1.0/rights/', - ), - 'children' => - array ( - 1 => - array ( - 'tag' => 'xapRights:WebStatement', - 'value' => '', - ), - ), - ), - 7 => - array ( - 'tag' => 'rdf:Description', - 'attributes' => - array ( - 'rdf:about' => "uuid:$new_GUID", - 'xmlns:dc' => 'http://purl.org/dc/elements/1.1/', - ), - 'children' => - array ( - 0 => - array ( - 'tag' => 'dc:format', - 'value' => 'image/jpeg', - ), - 1 => - array ( - 'tag' => 'dc:title', - 'children' => - array ( - 0 => - array ( - 'tag' => 'rdf:Alt', - ), - ), - ), - 2 => - array ( - 'tag' => 'dc:description', - 'children' => - array ( - 0 => - array ( - 'tag' => 'rdf:Alt', - ), - ), - ), - 3 => - array ( - 'tag' => 'dc:rights', - 'children' => - array ( - 0 => - array ( - 'tag' => 'rdf:Alt', - ), - ), - ), - 4 => - array ( - 'tag' => 'dc:creator', - 'children' => - array ( - 0 => - array ( - 'tag' => 'rdf:Seq', - ), - ), - ), - 5 => - array ( - 'tag' => 'dc:subject', - 'children' => - array ( - 0 => - array ( - 'tag' => 'rdf:Bag', - ), - ), - ), - ), - ), - -/* 0 => - array ( - 'tag' => 'rdf:Description', - 'attributes' => - array ( - 'rdf:about' => "uuid:$new_GUID", - 'xmlns:exif' => 'http://ns.adobe.com/exif/1.0/', - ), - 'children' => - array ( - -//EXIF DATA GOES HERE - Not Implemented yet - ), - ), -*/ -/* - 2 => - array ( - 'tag' => 'rdf:Description', - 'attributes' => - array ( - 'rdf:about' => "uuid:$new_GUID", - 'xmlns:tiff' => 'http://ns.adobe.com/tiff/1.0/', - ), - 'children' => - array ( -// TIFF DATA GOES HERE - Not Implemented yet - 0 => - array ( - 'tag' => 'tiff:Make', - 'value' => 'NIKON CORPORATION', - ), - ), - ), -*/ -/* - 3 => - array ( - 'tag' => 'rdf:Description', - 'attributes' => - array ( - 'rdf:about' => "uuid:$new_GUID", - 'xmlns:stRef' => 'http://ns.adobe.com/xap/1.0/sType/ResourceRef#', - 'xmlns:xapMM' => 'http://ns.adobe.com/xap/1.0/mm/', - ), - 'children' => - array ( -// XAPMM DATA GOES HERE - Not Implemented yet - 0 => - array ( - 'tag' => 'xapMM:DocumentID', - 'value' => 'adobe:docid:photoshop:dceba4c2-e699-11d8-94b2-b6ec48319f2d', - ), - 1 => - array ( - 'tag' => 'xapMM:DerivedFrom', - 'attributes' => - array ( - 'rdf:parseType' => 'Resource', - ), - 'children' => - array ( - 0 => - array ( - 'tag' => 'stRef:documentID', - 'value' => 'adobe:docid:photoshop:5144475b-e698-11d8-94b2-b6ec48319f2d', - ), - 1 => - array ( - 'tag' => 'stRef:instanceID', - 'value' => "uuid:$new_GUID", - ), - ), - ), - ), - ), -*/ - - ), - ), - ), - ), -); - - - -/****************************************************************************** -* End of Global Variable: Blank XMP Structure -******************************************************************************/ - - - - - -?> diff --git a/jpeg_metadata_tk/Photoshop_IRB.php b/jpeg_metadata_tk/Photoshop_IRB.php deleted file mode 100644 index fa09b49..0000000 --- a/jpeg_metadata_tk/Photoshop_IRB.php +++ /dev/null @@ -1,1514 +0,0 @@ - 1.02 : changed get_Photoshop_IRB to work with corrupted -* resource names which Photoshop can still read -* 1.02 -> 1.03 : Fixed put_Photoshop_IRB to output "Photoshop 3.0\x00" -* string with every APP13 segment, not just the first one -* 1.03 -> 1.10 : changed get_Photoshop_IRB to fix processing of embedded resource names, -* after discovering that Photoshop does not process -* resource names according to the standard : -* "Adobe Photoshop 6.0 File Formats Specification, Version 6.0, Release 2, November 2000" -* This is an update to the change 1.00 -> 1.02, which was not fully correct -* changed put_Photoshop_IRB to fix the writing of embedded resource name, -* to avoid creating blank resources, and to fix a problem -* causing the IRB block to be incorrectly positioned if no APP segments existed. -* changed get_Photoshop_IPTC to initialise the output array correctly. -* 1.10 -> 1.11 : Moved code out of get_Photoshop_IRB into new function unpack_Photoshop_IRB_Data -* to allow reading of IRB blocks embedded within EXIF (for TIFF Files) -* Moved code out of put_Photoshop_IRB into new function pack_Photoshop_IRB_Data -* to allow writing of IRB blocks embedded within EXIF (for TIFF Files) -* Enabled the usage of $GLOBALS['HIDE_UNKNOWN_TAGS'] to hide unknown resources -* changed Interpret_IRB_to_HTML to allow thumbnail links to work when -* toolkit is portable across directories -* -* -* URL: http://electronics.ozhiker.com -* -* Copyright: Copyright Evan Hunter 2004 -* -* License: This file is part of the PHP JPEG Metadata Toolkit. -* -* The PHP JPEG Metadata Toolkit is free software; you can -* redistribute it and/or modify it under the terms of the -* GNU General Public License as published by the Free Software -* Foundation; either version 2 of the License, or (at your -* option) any later version. -* -* The PHP JPEG Metadata Toolkit is distributed in the hope -* that it will be useful, but WITHOUT ANY WARRANTY; without -* even the implied warranty of MERCHANTABILITY or FITNESS -* FOR A PARTICULAR PURPOSE. See the GNU General Public License -* for more details. -* -* You should have received a copy of the GNU General Public -* License along with the PHP JPEG Metadata Toolkit; if not, -* write to the Free Software Foundation, Inc., 59 Temple -* Place, Suite 330, Boston, MA 02111-1307 USA -* -* If you require a different license for commercial or other -* purposes, please contact the author: evan@ozhiker.com -* -******************************************************************************/ - -// Change: as of version 1.11 - added to ensure the HIDE_UNKNOWN_TAGS variable is set even if EXIF.php is not included -if ( !isset( $GLOBALS['HIDE_UNKNOWN_TAGS'] ) ) $GLOBALS['HIDE_UNKNOWN_TAGS']= FALSE; - -include_once 'IPTC.php'; -include_once 'Unicode.php'; - - - -// TODO: Many Photoshop IRB resources not interpeted -// TODO: Obtain a copy of the Photoshop CS File Format Specification -// TODO: Find out what Photoshop IRB resources 1061, 1062 & 1064 are -// TODO: Test get_Photoshop_IRB and put_Photoshop_IRB with multiple APP13 segments - -/****************************************************************************** -* -* Function: get_Photoshop_IRB -* -* Description: Retrieves the Photoshop Information Resource Block (IRB) information -* from an App13 JPEG segment and returns it as an array. This may -* include IPTC-NAA IIM Information. Uses information -* supplied by the get_jpeg_header_data function -* -* Parameters: jpeg_header_data - a JPEG header data array in the same format -* as from get_jpeg_header_data -* -* Returns: IRBdata - The array of Photoshop IRB records -* FALSE - if an APP 13 Photoshop IRB segment could not be found, -* or if an error occured -* -******************************************************************************/ - -function get_Photoshop_IRB( $jpeg_header_data ) -{ - // Photoshop Image Resource blocks can span several JPEG APP13 segments, so we need to join them up if there are more than one - $joined_IRB = ""; - - - //Cycle through the header segments - for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) - { - // If we find an APP13 header, - if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP13" ) == 0 ) - { - // And if it has the photoshop label, - if( strncmp ( $jpeg_header_data[$i]['SegData'], "Photoshop 3.0\x00", 14) == 0 ) - { - // join it to the other previous IRB data - $joined_IRB .= substr ( $jpeg_header_data[$i]['SegData'], 14 ); - } - } - } - - // If there was some Photoshop IRB information found, - if ( $joined_IRB != "" ) - { - // Found a Photoshop Image Resource Block - extract it. - // Change: Moved code into unpack_Photoshop_IRB_Data to allow TIFF reading as of 1.11 - return unpack_Photoshop_IRB_Data( $joined_IRB ); - - } - else - { - // No Photoshop IRB found - return FALSE; - } - -} - -/****************************************************************************** -* End of Function: get_Photoshop_IRB -******************************************************************************/ - - - - - - - - - - -/****************************************************************************** -* -* Function: put_Photoshop_IRB -* -* Description: Adds or modifies the Photoshop Information Resource Block (IRB) -* information from an App13 JPEG segment. If a Photoshop IRB already -* exists, it is replaced, otherwise a new one is inserted, using the -* supplied data. Uses information supplied by the get_jpeg_header_data -* function -* -* Parameters: jpeg_header_data - a JPEG header data array in the same format -* as from get_jpeg_header_data -* new_IRB_data - an array of the data to be stored in the Photoshop -* IRB segment. Should be in the same format as received -* from get_Photoshop_IRB -* -* Returns: jpeg_header_data - the JPEG header data array with the -* Photoshop IRB added. -* FALSE - if an error occured -* -******************************************************************************/ - -function put_Photoshop_IRB( $jpeg_header_data, $new_IRB_data ) -{ - // Delete all existing Photoshop IRB blocks - the new one will replace them - - //Cycle through the header segments - for( $i = 0; $i < count( $jpeg_header_data ) ; $i++ ) - { - // If we find an APP13 header, - if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP13" ) == 0 ) - { - // And if it has the photoshop label, - if( strncmp ( $jpeg_header_data[$i]['SegData'], "Photoshop 3.0\x00", 14) == 0 ) - { - // Delete the block information - it needs to be rebuilt - array_splice( $jpeg_header_data, $i, 1 ); - } - } - } - - - // Now we have deleted the pre-existing blocks - - - // Retrieve the Packed Photoshop IRB Data - // Change: Moved code into pack_Photoshop_IRB_Data to allow TIFF writing as of 1.11 - $packed_IRB_data = pack_Photoshop_IRB_Data( $new_IRB_data ); - - // Change : This section changed to fix incorrect positioning of IRB segment, as of revision 1.10 - // when there are no APP segments present - - //Cycle through the header segments in reverse order (to find where to put the APP13 block - after any APP0 to APP12 blocks) - $i = count( $jpeg_header_data ) - 1; - while (( $i >= 0 ) && ( ( $jpeg_header_data[$i]['SegType'] > 0xED ) || ( $jpeg_header_data[$i]['SegType'] < 0xE0 ) ) ) - { - $i--; - } - - - - // Cycle through the packed output data until it's size is less than 32000 bytes, outputting each 32000 byte block to an APP13 segment - while ( strlen( $packed_IRB_data ) > 32000 ) - { - // Change: Fixed put_Photoshop_IRB to output "Photoshop 3.0\x00" string with every APP13 segment, not just the first one, as of 1.03 - - // Write a 32000 byte APP13 segment - array_splice($jpeg_header_data, $i +1 , 0, array( "SegType" => 0xED, - "SegName" => "APP13", - "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xED ], - "SegData" => "Photoshop 3.0\x00" . substr( $packed_IRB_data,0,32000) ) ); - - // Delete the 32000 bytes from the packed output data, that were just output - $packed_IRB_data = substr_replace($packed_IRB_data, '', 0, 32000); - $i++; - } - - // Write the last block of packed output data to an APP13 segment - Note array_splice doesn't work with multidimensional arrays, hence inserting a blank string - array_splice($jpeg_header_data, $i + 1 , 0, "" ); - $jpeg_header_data[$i + 1] = array( "SegType" => 0xED, - "SegName" => "APP13", - "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xED ], - "SegData" => "Photoshop 3.0\x00" . $packed_IRB_data ); - - return $jpeg_header_data; -} - -/****************************************************************************** -* End of Function: put_Photoshop_IRB -******************************************************************************/ - - - - - - - - -/****************************************************************************** -* -* Function: get_Photoshop_IPTC -* -* Description: Retrieves IPTC-NAA IIM information from within a Photoshop -* IRB (if it is present) and returns it in an array. Uses -* information supplied by the get_jpeg_header_data function -* -* Parameters: Photoshop_IRB_data - an array of Photoshop IRB records, as -* returned from get_Photoshop_IRB -* -* Returns: IPTC_Data_Out - The array of IPTC-NAA IIM records -* FALSE - if an IPTC-NAA IIM record could not be found, or if -* an error occured -* -******************************************************************************/ - -function get_Photoshop_IPTC( $Photoshop_IRB_data ) -{ - - // Change: Initialise array correctly, as of revision 1.10 - $IPTC_Data_Out = array(); - - //Cycle through the Photoshop 8BIM records looking for the IPTC-NAA record - for( $i = 0; $i < count( $Photoshop_IRB_data ); $i++ ) - { - // Check if each record is a IPTC record (which has id 0x0404) - if ( $Photoshop_IRB_data[$i]['ResID'] == 0x0404 ) - { - // We've found an IPTC block - Decode it - $IPTC_Data_Out = get_IPTC( $Photoshop_IRB_data[$i]['ResData'] ); - } - } - - // If there was no records put into the output array, - if ( count( $IPTC_Data_Out ) == 0 ) - { - // Then return false - return FALSE; - } - else - { - // Otherwise return the array - return $IPTC_Data_Out; - } - -} -/****************************************************************************** -* End of Function: get_Photoshop_IPTC -******************************************************************************/ - - - - - - -/****************************************************************************** -* -* Function: put_Photoshop_IPTC -* -* Description: Inserts a new IPTC-NAA IIM resource into a Photoshop -* IRB, or replaces an the existing resource if one is present. -* Uses information supplied by the get_Photoshop_IRB function -* -* Parameters: Photoshop_IRB_data - an array of Photoshop IRB records, as -* returned from get_Photoshop_IRB, into -* which the IPTC-NAA IIM record will be inserted -* new_IPTC_block - an array of IPTC-NAA records in the same format -* as those returned by get_Photoshop_IPTC -* -* Returns: Photoshop_IRB_data - The Photoshop IRB array with the -* IPTC-NAA IIM resource inserted -* -******************************************************************************/ - -function put_Photoshop_IPTC( $Photoshop_IRB_data, $new_IPTC_block ) -{ - $iptc_block_pos = -1; - - //Cycle through the 8BIM records looking for the IPTC-NAA record - for( $i = 0; $i < count( $Photoshop_IRB_data ); $i++ ) - { - // Check if each record is a IPTC record (which has id 0x0404) - if ( $Photoshop_IRB_data[$i]['ResID'] == 0x0404 ) - { - // We've found an IPTC block - save the position - $iptc_block_pos = $i; - } - } - - // If no IPTC block was found, create a new one - if ( $iptc_block_pos == -1 ) - { - // New block position will be at the end of the array - $iptc_block_pos = count( $Photoshop_IRB_data ); - } - - - // Write the new IRB resource to the Photoshop IRB array with no data - $Photoshop_IRB_data[$iptc_block_pos] = array( "ResID" => 0x0404, - "ResName" => $GLOBALS['Photoshop_ID_Names'][ 0x0404 ], - "ResDesc" => $GLOBALS[ "Photoshop_ID_Descriptions" ][ 0x0404 ], - "ResEmbeddedName" => "\x00\x00", - "ResData" => put_IPTC( $new_IPTC_block ) ); - - - // Return the modified IRB - return $Photoshop_IRB_data; -} - -/****************************************************************************** -* End of Function: put_Photoshop_IPTC -******************************************************************************/ - - - - - - - - -/****************************************************************************** -* -* Function: Interpret_IRB_to_HTML -* -* Description: Generates html showing the information contained in a Photoshop -* IRB data array, as retrieved with get_Photoshop_IRB, including -* any IPTC-NAA IIM records found. -* -* Please note that the following resource numbers are not currently -* decoded: ( Many of these do not apply to JPEG images) -* 0x03E9, 0x03EE, 0x03EF, 0x03F0, 0x03F1, 0x03F2, 0x03F6, 0x03F9, -* 0x03FA, 0x03FB, 0x03FD, 0x03FE, 0x0400, 0x0401, 0x0402, 0x0405, -* 0x040E, 0x040F, 0x0410, 0x0412, 0x0413, 0x0415, 0x0416, 0x0417, -* 0x041B, 0x041C, 0x041D, 0x0BB7 -* -* ( Also these Obsolete resource numbers) -* 0x03E8, 0x03EB, 0x03FC, 0x03FF, 0x0403 -* -* -* Parameters: IRB_array - a Photoshop IRB data array as from get_Photoshop_IRB -* filename - the name of the JPEG file being processed ( used -* by the script which displays the Photoshop thumbnail) -* -* -* Returns: output_str - the HTML string -* -******************************************************************************/ - -function Interpret_IRB_to_HTML( $IRB_array, $filename ) -{ - // Create a string to receive the HTML - $output_str = ""; - - // Check if the Photoshop IRB array is valid - if ( $IRB_array !== FALSE ) - { - - // Create another string to receive secondary HTML to be appended at the end - $secondary_output_str = ""; - - // Add the Heading to the HTML - $output_str .= "

    Contains Photoshop Information Resource Block (IRB)

    "; - - // Add Table to the HTML - $output_str .= "\n"; - - // Cycle through each of the Photoshop IRB records, creating HTML for each - foreach( $IRB_array as $IRB_Resource ) - { - // Check if the entry is a known Photoshop IRB resource - - // Get the Name of the Resource - if ( array_key_exists( $IRB_Resource['ResID'], $GLOBALS[ "Photoshop_ID_Names" ] ) ) - { - $Resource_Name = $GLOBALS['Photoshop_ID_Names'][ $IRB_Resource['ResID'] ]; - } - else - { - // Change: Added check for $GLOBALS['HIDE_UNKNOWN_TAGS'] to allow hiding of unknown resources as of 1.11 - if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == TRUE ) - { - continue; - } - else - { - // Unknown Resource - Make appropriate name - $Resource_Name = "Unknown Resource (". $IRB_Resource['ResID'] .")"; - } - } - - // Add HTML for the resource as appropriate - switch ( $IRB_Resource['ResID'] ) - { - - case 0x0404 : // IPTC-NAA IIM Record - $secondary_output_str .= Interpret_IPTC_to_HTML( get_IPTC( $IRB_Resource['ResData'] ) ); - break; - - case 0x040B : // URL - $output_str .= "\n"; - break; - - case 0x040A : // Copyright Marked - if ( hexdec( bin2hex( $IRB_Resource['ResData'] ) ) == 1 ) - { - $output_str .= "\n"; - } - else - { - $output_str .= "\n"; - } - break; - - case 0x040D : // Global Lighting Angle - $output_str .= "\n"; - break; - - case 0x0419 : // Global Altitude - $output_str .= "\n"; - break; - - case 0x0421 : // Version Info - $output_str .= "\n"; - break; - - case 0x0411 : // ICC Untagged - if ( $IRB_Resource['ResData'] == "\x01" ) - { - $output_str .= "\n"; - } - else - { - $output_str .= "\n"; - } - break; - - case 0x041A : // Slices - $output_str .= "\n"; - - break; - - - case 0x0408 : // Grid and Guides information - $output_str .= "\n"; - - case 0x0406 : // JPEG Quality - $Qual_Info = unpack("nQuality/nFormat/nScans/Cconst", $IRB_Resource['ResData'] ); - $output_str .= "\n"; - break; - - case 0x0409 : // Thumbnail Resource - case 0x040C : // Thumbnail Resource - $thumb_data = unpack("NFormat/NWidth/NHeight/NWidthBytes/NSize/NCompressedSize/nBitsPixel/nPlanes", $IRB_Resource['ResData'] ); - $output_str .= "\n"; - break; - - case 0x0414 : // Document Specific ID's - $output_str .= "\n"; - break; - - case 0x041E : // URL List - $URL_count = hexdec( bin2hex( substr( $IRB_Resource['ResData'], 0, 4 ) ) ); - $output_str .= "\n"; - break; - case 0x03F4 : // Grayscale and multichannel halftoning information. - $output_str .= "\n"; - break; - case 0x03F5 : // Color halftoning information - $output_str .= "\n"; - break; - - case 0x03F7 : // Grayscale and multichannel transfer function. - $output_str .= "\n"; - break; - - case 0x03F8 : // Color transfer functions - $output_str .= "\n"; - break; - - case 0x03F3 : // Print Flags - $output_str .= "\n"; - break; - - case 0x2710 : // Print Flags Information - $PrintFlags = unpack( "nVersion/CCentCrop/Cjunk/NBleedWidth/nBleedWidthScale", $IRB_Resource['ResData'] ); - $output_str .= "\n"; - break; - - case 0x03ED : // Resolution Info - $ResInfo = unpack( "nhRes_int/nhResdec/nhResUnit/nwidthUnit/nvRes_int/nvResdec/nvResUnit/nheightUnit", $IRB_Resource['ResData'] ); - $output_str .= "\n"; - break; - - default : // All other records - $output_str .= "\n"; - - } - - } - - // Add the table end to the HTML - $output_str .= "
    $Resource_Name" . htmlentities( $IRB_Resource['ResData'] ) ."
    $Resource_Name
    Image is Copyrighted Material
    $Resource_Name
    Image is Not Copyrighted Material
    $Resource_Name
    Global lighting angle for effects layer = " . hexdec( bin2hex( $IRB_Resource['ResData'] ) ) . " degrees
    $Resource_Name
    Global Altitude = " . hexdec( bin2hex( $IRB_Resource['ResData'] ) ) . "
    $Resource_Name
    \n";
    -                                        $output_str .= "Version = " . hexdec( bin2hex( substr( $IRB_Resource['ResData'], 0, 4 ) ) ) . "\n";
    -                                        $output_str .= "Has Real Merged Data = " . ord( $IRB_Resource['ResData']{4} ) . "\n";
    -                                        $writer_size = hexdec( bin2hex( substr( $IRB_Resource['ResData'], 5, 4 ) ) ) * 2;
    -
    -                                        $output_str .= "Writer Name = " . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], 9, $writer_size ), TRUE ) . "\n";
    -                                        $reader_size = hexdec( bin2hex( substr( $IRB_Resource['ResData'], 9 + $writer_size , 4 ) ) ) * 2;
    -                                        $output_str .= "Reader Name = " . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], 13 + $writer_size, $reader_size ), TRUE ) . "\n";
    -                                        $output_str .= "File Version = " . hexdec( bin2hex( substr( $IRB_Resource['ResData'], 13 + $writer_size + $reader_size, 4 ) ) ) . "\n";
    -                                        $output_str .=  "
    $Resource_Name
    Intentionally untagged - any assumed ICC profile handling disabled
    $Resource_Name
    Unknown value (0x" .bin2hex( $IRB_Resource['ResData'] ). ")
    $Resource_Name"; - - // Unpack the first 24 bytes - $Slices_Info = unpack("NVersion/NBound_top/NBound_left/NBound_bottom/NBound_right/NStringlen", $IRB_Resource['ResData'] ); - $output_str .= "Version = " . $Slices_Info['Version'] . "
    \n"; - $output_str .= "Bounding Rectangle = Top:" . $Slices_Info['Bound_top'] . ", Left:" . $Slices_Info['Bound_left'] . ", Bottom:" . $Slices_Info['Bound_bottom'] . ", Right:" . $Slices_Info['Bound_right'] . " (Pixels)
    \n"; - $Slicepos = 24; - - // Extract a Unicode String - $output_str .= "Text = '" . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], 24, $Slices_Info['Stringlen']*2), TRUE ) . "'
    \n"; - $Slicepos += $Slices_Info['Stringlen'] * 2; - - // Unpack the number of Slices - $Num_Slices = hexdec( bin2hex( substr( $IRB_Resource['ResData'], $Slicepos, 4 ) ) ); - $output_str .= "Number of Slices = " . $Num_Slices . "\n"; - $Slicepos += 4; - - // Cycle through the slices - for( $i = 1; $i <= $Num_Slices; $i++ ) - { - $output_str .= "

    Slice $i:
    \n"; - - // Unpack the first 16 bytes of the slice - $SliceA = unpack("NID/NGroupID/NOrigin/NStringlen", substr($IRB_Resource['ResData'], $Slicepos ) ); - $Slicepos += 16; - $output_str .= "ID = " . $SliceA['ID'] . "
    \n"; - $output_str .= "Group ID = " . $SliceA['GroupID'] . "
    \n"; - $output_str .= "Origin = " . $SliceA['Origin'] . "
    \n"; - - // Extract a Unicode String - $output_str .= "Text = '" . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], $Slicepos, $SliceA['Stringlen']*2), TRUE ) . "'
    \n"; - $Slicepos += $SliceA['Stringlen'] * 2; - - // Unpack the next 24 bytes of the slice - $SliceB = unpack("NType/NLeftPos/NTopPos/NRightPos/NBottomPos/NURLlen", substr($IRB_Resource['ResData'], $Slicepos ) ); - $Slicepos += 24; - $output_str .= "Type = " . $SliceB['Type'] . "
    \n"; - $output_str .= "Position = Top:" . $SliceB['TopPos'] . ", Left:" . $SliceB['LeftPos'] . ", Bottom:" . $SliceB['BottomPos'] . ", Right:" . $SliceB['RightPos'] . " (Pixels)
    \n"; - - // Extract a Unicode String - $output_str .= "URL = " . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], $Slicepos, $SliceB['URLlen']*2), TRUE ) . "
    \n"; - $Slicepos += $SliceB['URLlen'] * 2; - - // Unpack the length of a Unicode String - $Targetlen = hexdec( bin2hex( substr( $IRB_Resource['ResData'], $Slicepos, 4 ) ) ); - $Slicepos += 4; - // Extract a Unicode String - $output_str .= "Target = '" . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], $Slicepos, $Targetlen*2), TRUE ) . "'
    \n"; - $Slicepos += $Targetlen * 2; - - // Unpack the length of a Unicode String - $Messagelen = hexdec( bin2hex( substr( $IRB_Resource['ResData'], $Slicepos, 4 ) ) ); - $Slicepos += 4; - // Extract a Unicode String - $output_str .= "Message = '" . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], $Slicepos, $Messagelen*2), TRUE ) . "'
    \n"; - $Slicepos += $Messagelen * 2; - - // Unpack the length of a Unicode String - $AltTaglen = hexdec( bin2hex( substr( $IRB_Resource['ResData'], $Slicepos, 4 ) ) ); - $Slicepos += 4; - // Extract a Unicode String - $output_str .= "Alt Tag = '" . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], $Slicepos, $AltTaglen*2), TRUE ) . "'
    \n"; - $Slicepos += $AltTaglen * 2; - - // Unpack the HTML flag - if ( ord( $IRB_Resource['ResData']{ $Slicepos } ) === 0x01 ) - { - $output_str .= "Cell Text is HTML
    \n"; - } - else - { - $output_str .= "Cell Text is NOT HTML
    \n"; - } - $Slicepos++; - - // Unpack the length of a Unicode String - $CellTextlen = hexdec( bin2hex( substr( $IRB_Resource['ResData'], $Slicepos, 4 ) ) ); - $Slicepos += 4; - // Extract a Unicode String - $output_str .= "Cell Text = '" . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], $Slicepos, $CellTextlen*2), TRUE ) . "'
    \n"; - $Slicepos += $CellTextlen * 2; - - - // Unpack the last 12 bytes of the slice - $SliceC = unpack("NAlignH/NAlignV/CAlpha/CRed/CGreen/CBlue", substr($IRB_Resource['ResData'], $Slicepos ) ); - $Slicepos += 12; - $output_str .= "Alignment = Horizontal:" . $SliceC['AlignH'] . ", Vertical:" . $SliceC['AlignV'] . "
    \n"; - $output_str .= "Alpha Colour = " . $SliceC['Alpha'] . "
    \n"; - $output_str .= "Red = " . $SliceC['Red'] . "
    \n"; - $output_str .= "Green = " . $SliceC['Green'] . "
    \n"; - $output_str .= "Blue = " . $SliceC['Blue'] . "\n"; - } - - $output_str .= "
    $Resource_Name"; - - // Unpack the Grids info - $Grid_Info = unpack("NVersion/NGridCycleH/NGridCycleV/NGuideCount", $IRB_Resource['ResData'] ); - $output_str .= "Version = " . $Grid_Info['Version'] . "
    \n"; - $output_str .= "Grid Cycle = " . $Grid_Info['GridCycleH']/32 . " Pixel(s) x " . $Grid_Info['GridCycleV']/32 . " Pixel(s)
    \n"; - $output_str .= "Number of Guides = " . $Grid_Info['GuideCount'] . "\n"; - - // Cycle through the Guides - for( $i = 0; $i < $Grid_Info['GuideCount']; $i++ ) - { - // Unpack the info for this guide - $Guide_Info = unpack("NLocation/CDirection", substr($IRB_Resource['ResData'],16+$i*5,5) ); - $output_str .= "
    Guide $i : Location = " . $Guide_Info['Location']/32 . " Pixel(s) from edge"; - if ( $Guide_Info['Direction'] === 0 ) - { - $output_str .= ", Vertical\n"; - } - else - { - $output_str .= ", Horizontal\n"; - } - } - break; - $output_str .= "
    $Resource_Name"; - switch ( $Qual_Info['Quality'] ) - { - case 0xFFFD: - $output_str .= "Quality 1 (Low)
    \n"; - break; - case 0xFFFE: - $output_str .= "Quality 2 (Low)
    \n"; - break; - case 0xFFFF: - $output_str .= "Quality 3 (Low)
    \n"; - break; - case 0x0000: - $output_str .= "Quality 4 (Low)
    \n"; - break; - case 0x0001: - $output_str .= "Quality 5 (Medium)
    \n"; - break; - case 0x0002: - $output_str .= "Quality 6 (Medium)
    \n"; - break; - case 0x0003: - $output_str .= "Quality 7 (Medium)
    \n"; - break; - case 0x0004: - $output_str .= "Quality 8 (High)
    \n"; - break; - case 0x0005: - $output_str .= "Quality 9 (High)
    \n"; - break; - case 0x0006: - $output_str .= "Quality 10 (Maximum)
    \n"; - break; - case 0x0007: - $output_str .= "Quality 11 (Maximum)
    \n"; - break; - case 0x0008: - $output_str .= "Quality 12 (Maximum)
    \n"; - break; - default: - $output_str .= "Unknown Quality (" . $Qual_Info['Quality'] . ")
    \n"; - break; - } - - switch ( $Qual_Info['Format'] ) - { - case 0x0000: - $output_str .= "Standard Format\n"; - break; - case 0x0001: - $output_str .= "Optimised Format\n"; - break; - case 0x0101: - $output_str .= "Progressive Format
    \n"; - break; - default: - $output_str .= "Unknown Format (" . $Qual_Info['Format'] .")\n"; - break; - } - if ( $Qual_Info['Format'] == 0x0101 ) - { - switch ( $Qual_Info['Scans'] ) - { - case 0x0001: - $output_str .= "3 Scans\n"; - break; - case 0x0002: - $output_str .= "4 Scans\n"; - break; - case 0x0003: - $output_str .= "5 Scans\n"; - break; - default: - $output_str .= "Unknown number of scans (" . $Qual_Info['Scans'] .")\n"; - break; - } - } - $output_str .= "
    $Resource_Name
    \n";
    -                                        $output_str .= "Format = " . (( $thumb_data['Format'] == 1 ) ? "JPEG RGB\n" :  "Raw RGB\n");
    -                                        $output_str .= "Width = " . $thumb_data['Width'] . "\n";
    -                                        $output_str .= "Height = " . $thumb_data['Height'] . "\n";
    -                                        $output_str .= "Padded Row Bytes = " . $thumb_data['WidthBytes'] . " bytes\n";
    -                                        $output_str .= "Total Size = " . $thumb_data['Size'] . " bytes\n";
    -                                        $output_str .= "Compressed Size = " . $thumb_data['CompressedSize'] . " bytes\n";
    -                                        $output_str .= "Bits per Pixel = " . $thumb_data['BitsPixel'] . " bits\n";
    -                                        $output_str .= "Number of planes = " . $thumb_data['Planes'] . " bytes\n";
    -
    -                                        // Change: as of version 1.11 - Changed to make thumbnail link portable across directories
    -                                        // Build the path of the thumbnail script and its filename parameter to put in a url
    -                                        $link_str = get_relative_path( dirname(__FILE__) . "/get_ps_thumb.php" , getcwd ( ) );
    -                                        $link_str .= "?filename=";
    -                                        $link_str .= get_relative_path( $filename, dirname(__FILE__) );
    -
    -                                        // Add thumbnail link to html
    -                                        $output_str .= "Thumbnail Data:
    \n"; - - $output_str .= "
    $Resource_Name
    " . hexdec( bin2hex( $IRB_Resource['ResData'] ) ) . "
    $Resource_Name\n"; - $output_str .= "$URL_count URL's in list
    \n"; - $urlstr = substr( $IRB_Resource['ResData'], 4 ); - // TODO: Check if URL List in Photoshop IRB works - for( $i = 0; $i < $URL_count; $i++ ) - { - $url_data = unpack( "NLong/NID/NURLSize", $urlstr ); - $output_str .= "URL $i info: long = " . $url_data['Long'] .", "; - $output_str .= "ID = " . $url_data['ID'] . ", "; - $urlstr = substr( $urlstr, 12 ); - $url = substr( $urlstr, 0, $url_data['URLSize'] ); - $output_str .= "URL = " . HTML_UTF16_Escape( $url, TRUE ) . "
    \n"; - } - $output_str .= "
    $Resource_Name
    \n";
    -                                        $output_str .= Interpret_Halftone( $IRB_Resource['ResData'] );
    -                                        $output_str .= "
    $Resource_Name
    \n";
    -                                        $output_str .= "Cyan Halftoning Info:\n" . Interpret_Halftone( substr( $IRB_Resource['ResData'], 0, 18 ) ) . "\n\n";
    -                                        $output_str .= "Magenta Halftoning Info:\n" . Interpret_Halftone( substr( $IRB_Resource['ResData'], 18, 18 ) ) . "\n\n";
    -                                        $output_str .= "Yellow Halftoning Info:\n" . Interpret_Halftone( substr( $IRB_Resource['ResData'], 36, 18 ) ) . "\n";
    -                                        $output_str .= "Black Halftoning Info:\n" . Interpret_Halftone( substr( $IRB_Resource['ResData'], 54, 18 ) ) . "\n";
    -                                        $output_str .= "
    $Resource_Name
    \n";
    -                                        $output_str .= Interpret_Transfer_Function( substr( $IRB_Resource['ResData'], 0, 28 ) ) ;
    -                                        $output_str .= "
    $Resource_Name
    \n";
    -                                        $output_str .= "Red Transfer Function:   \n" . Interpret_Transfer_Function( substr( $IRB_Resource['ResData'], 0, 28 ) ) . "\n\n";
    -                                        $output_str .= "Green Transfer Function: \n" . Interpret_Transfer_Function( substr( $IRB_Resource['ResData'], 28, 28 ) ) . "\n\n";
    -                                        $output_str .= "Blue Transfer Function:  \n" . Interpret_Transfer_Function( substr( $IRB_Resource['ResData'], 56, 28 ) ) . "\n";
    -                                        $output_str .= "
    $Resource_Name
    \n";
    -                                        if ( $IRB_Resource['ResData']{0} == "\x01" )
    -                                        {
    -                                                $output_str .= "Labels Selected\n";
    -                                        }
    -                                        else
    -                                        {
    -                                                $output_str .= "Labels Not Selected\n";
    -                                        }
    -                                        if ( $IRB_Resource['ResData']{1} == "\x01" )
    -                                        {
    -                                                $output_str .= "Crop Marks Selected\n";
    -                                        }
    -                                        else
    -                                        {
    -                                                $output_str .= "Crop Marks Not Selected\n";
    -                                        }
    -                                        if ( $IRB_Resource['ResData']{2} == "\x01" )
    -                                        {
    -                                                $output_str .= "Color Bars Selected\n";
    -                                        }
    -                                        else
    -                                        {
    -                                                $output_str .= "Color Bars Not Selected\n";
    -                                        }
    -                                        if ( $IRB_Resource['ResData']{3} == "\x01" )
    -                                        {
    -                                                $output_str .= "Registration Marks Selected\n";
    -                                        }
    -                                        else
    -                                        {
    -                                                $output_str .= "Registration Marks Not Selected\n";
    -                                        }
    -                                        if ( $IRB_Resource['ResData']{4} == "\x01" )
    -                                        {
    -                                                $output_str .= "Negative Selected\n";
    -                                        }
    -                                        else
    -                                        {
    -                                                $output_str .= "Negative Not Selected\n";
    -                                        }
    -                                        if ( $IRB_Resource['ResData']{5} == "\x01" )
    -                                        {
    -                                                $output_str .= "Flip Selected\n";
    -                                        }
    -                                        else
    -                                        {
    -                                                $output_str .= "Flip Not Selected\n";
    -                                        }
    -                                        if ( $IRB_Resource['ResData']{6} == "\x01" )
    -                                        {
    -                                                $output_str .= "Interpolate Selected\n";
    -                                        }
    -                                        else
    -                                        {
    -                                                $output_str .= "Interpolate Not Selected\n";
    -                                        }
    -                                        if ( $IRB_Resource['ResData']{7} == "\x01" )
    -                                        {
    -                                                $output_str .= "Caption Selected";
    -                                        }
    -                                        else
    -                                        {
    -                                                $output_str .= "Caption Not Selected";
    -                                        }
    -                                        $output_str .= "
    $Resource_Name
    \n";
    -                                        $output_str .= "Version = " . $PrintFlags['Version'] . "\n";
    -                                        $output_str .= "Centre Crop Marks = " . $PrintFlags['CentCrop'] . "\n";
    -                                        $output_str .= "Bleed Width = " . $PrintFlags['BleedWidth'] . "\n";
    -                                        $output_str .= "Bleed Width Scale = " . $PrintFlags['BleedWidthScale'];
    -                                        $output_str .= "
    $Resource_Name
    \n";
    -                                        $output_str .= "Horizontal Resolution = " . ($ResInfo['hRes_int'] + $ResInfo['hResdec']/65536) . " pixels per Inch\n";
    -                                        $output_str .= "Vertical Resolution = " . ($ResInfo['vRes_int'] + $ResInfo['vResdec']/65536) . " pixels per Inch\n";
    -                                        if ( $ResInfo['hResUnit'] == 1 )
    -                                        {
    -                                                $output_str .= "Display units for Horizontal Resolution = Pixels per Inch\n";
    -                                        }
    -                                        elseif ( $ResInfo['hResUnit'] == 2 )
    -                                        {
    -                                                $output_str .= "Display units for Horizontal Resolution = Pixels per Centimetre\n";
    -                                        }
    -                                        else
    -                                        {
    -                                                $output_str .= "Display units for Horizontal Resolution = Unknown Value (". $ResInfo['hResUnit'] .")\n";
    -                                        }
    -
    -                                        if ( $ResInfo['vResUnit'] == 1 )
    -                                        {
    -                                                $output_str .= "Display units for Vertical Resolution = Pixels per Inch\n";
    -                                        }
    -                                        elseif ( $ResInfo['vResUnit'] == 2 )
    -                                        {
    -                                                $output_str .= "Display units for Vertical Resolution = Pixels per Centimetre\n";
    -                                        }
    -                                        else
    -                                        {
    -                                                $output_str .= "Display units for Vertical Resolution = Unknown Value (". $ResInfo['vResUnit'] .")\n";
    -                                        }
    -
    -                                        if ( $ResInfo['widthUnit'] == 1 )
    -                                        {
    -                                                $output_str .= "Display units for Image Width = Inches\n";
    -                                        }
    -                                        elseif ( $ResInfo['widthUnit'] == 2 )
    -                                        {
    -                                                $output_str .= "Display units for Image Width = Centimetres\n";
    -                                        }
    -                                        elseif ( $ResInfo['widthUnit'] == 3 )
    -                                        {
    -                                                $output_str .= "Display units for Image Width = Points\n";
    -                                        }
    -                                        elseif ( $ResInfo['widthUnit'] == 4 )
    -                                        {
    -                                                $output_str .= "Display units for Image Width = Picas\n";
    -                                        }
    -                                        elseif ( $ResInfo['widthUnit'] == 5 )
    -                                        {
    -                                                $output_str .= "Display units for Image Width = Columns\n";
    -                                        }
    -                                        else
    -                                        {
    -                                                $output_str .= "Display units for Image Width = Unknown Value (". $ResInfo['widthUnit'] .")\n";
    -                                        }
    -
    -                                        if ( $ResInfo['heightUnit'] == 1 )
    -                                        {
    -                                                $output_str .= "Display units for Image Height = Inches";
    -                                        }
    -                                        elseif ( $ResInfo['heightUnit'] == 2 )
    -                                        {
    -                                                $output_str .= "Display units for Image Height = Centimetres";
    -                                        }
    -                                        elseif ( $ResInfo['heightUnit'] == 3 )
    -                                        {
    -                                                $output_str .= "Display units for Image Height = Points";
    -                                        }
    -                                        elseif ( $ResInfo['heightUnit'] == 4 )
    -                                        {
    -                                                $output_str .= "Display units for Image Height = Picas";
    -                                        }
    -                                        elseif ( $ResInfo['heightUnit'] == 5 )
    -                                        {
    -                                                $output_str .= "Display units for Image Height = Columns";
    -                                        }
    -                                        else
    -                                        {
    -                                                $output_str .= "Display units for Image Height = Unknown Value (". $ResInfo['heightUnit'] .")";
    -                                        }
    -                                        $output_str .= "
    $Resource_NameRESOURCE DECODING NOT IMPLEMENTED YET
    " . strlen( $IRB_Resource['ResData'] ) . " bytes
    \n"; - - // Add any secondary output to the HTML - $output_str .= $secondary_output_str; - - } - - // Return the HTML - return $output_str; -} - -/****************************************************************************** -* End of Function: Interpret_IRB_to_HTML -******************************************************************************/ - - - - - - -/****************************************************************************** -* -* INTERNAL FUNCTIONS -* -******************************************************************************/ - - - - - - - -/****************************************************************************** -* -* Function: unpack_Photoshop_IRB_Data -* -* Description: Extracts Photoshop Information Resource Block (IRB) information -* from a binary string containing the IRB, as read from a file -* -* Parameters: IRB_Data - The binary string containing the IRB -* -* Returns: IRBdata - The array of Photoshop IRB records -* -******************************************************************************/ - -function unpack_Photoshop_IRB_Data( $IRB_Data ) -{ - $pos = 0; - - // Cycle through the IRB and extract its records - Records are started with 8BIM, so cycle until no more instances of 8BIM can be found - while ( ( $pos < strlen( $IRB_Data ) ) && ( ($pos = strpos( $IRB_Data, "8BIM", $pos) ) !== FALSE ) ) - { - // Skip the position over the 8BIM characters - $pos += 4; - - // Next two characters are the record ID - denoting what type of record it is. - $ID = ord( $IRB_Data{ $pos } ) * 256 + ord( $IRB_Data{ $pos +1 } ); - - // Skip the positionover the two record ID characters - $pos += 2; - - // Next comes a Record Name - usually not used, but it should be a null terminated string, padded with 0x00 to be an even length - $namestartpos = $pos; - - // Change: Fixed processing of embedded resource names, as of revision 1.10 - - // NOTE: Photoshop does not process resource names according to the standard : - // "Adobe Photoshop 6.0 File Formats Specification, Version 6.0, Release 2, November 2000" - // - // The resource name is actually formatted as follows: - // One byte name length, followed by the null terminated ascii name string. - // The field is then padded with a Null character if required, to ensure that the - // total length of the name length and name is even. - - // Name - process it - // Get the length - $namelen = ord ( $IRB_Data{ $namestartpos } ); - - // Total length of name and length info must be even, hence name length must be odd - // Check if the name length is even, - if ( $namelen % 2 == 0 ) - { - // add one to length to make it odd - $namelen ++; - } - // Extract the name - $resembeddedname = trim( substr ( $IRB_Data, $namestartpos+1, $namelen) ); - $pos += $namelen + 1; - - - // Next is a four byte size field indicating the size in bytes of the record's data - MSB first - $datasize = ord( $IRB_Data{ $pos } ) * 16777216 + ord( $IRB_Data{ $pos + 1 } ) * 65536 + - ord( $IRB_Data{ $pos + 2 } ) * 256 + ord( $IRB_Data{ $pos + 3 } ); - $pos += 4; - - // The record is stored padded with 0x00 characters to make the size even, so we need to calculate the stored size - $storedsize = $datasize + ($datasize % 2); - - $resdata = substr ( $IRB_Data, $pos, $datasize ); - - // Get the description for this resource - // Check if this is a Path information Resource, since they have a range of ID's - if ( ( $ID >= 0x07D0 ) && ( $ID <= 0x0BB6 ) ) - { - $ResDesc = "ID Info : Path Information (saved paths)."; - } - else - { - if ( array_key_exists( $ID, $GLOBALS[ "Photoshop_ID_Descriptions" ] ) ) - { - $ResDesc = $GLOBALS[ "Photoshop_ID_Descriptions" ][ $ID ]; - } - else - { - $ResDesc = ""; - } - } - - // Get the Name of the Resource - if ( array_key_exists( $ID, $GLOBALS[ "Photoshop_ID_Names" ] ) ) - { - $ResName = $GLOBALS['Photoshop_ID_Names'][ $ID ]; - } - else - { - $ResName = ""; - } - - - // Store the Resource in the array to be returned - - $IRB_Array[] = array( "ResID" => $ID, - "ResName" => $ResName, - "ResDesc" => $ResDesc, - "ResEmbeddedName" => $resembeddedname, - "ResData" => $resdata ); - - // Jump over the data to the next record - $pos += $storedsize; - } - - // Return the array created - return $IRB_Array; -} - -/****************************************************************************** -* End of Function: unpack_Photoshop_IRB_Data -******************************************************************************/ - - - - - - - - - - - -/****************************************************************************** -* -* Function: pack_Photoshop_IRB_Data -* -* Description: Packs a Photoshop Information Resource Block (IRB) array into it's -* binary form, which can be written to a file -* -* Parameters: IRB_data - an Photoshop IRB array to be converted. Should be in -* the same format as received from get_Photoshop_IRB -* -* Returns: packed_IRB_data - the binary string of packed IRB data -* -******************************************************************************/ - -function pack_Photoshop_IRB_Data( $IRB_data ) -{ - $packed_IRB_data = ""; - - // Cycle through each resource in the IRB, - foreach ($IRB_data as $resource) - { - - // Change: Fix to avoid creating blank resources, as of revision 1.10 - - // Check if there is actually any data for this resource - if( strlen( $resource['ResData'] ) == 0 ) - { - // No data for resource - skip it - continue; - } - - // Append the 8BIM tag, and resource ID to the packed output data - $packed_IRB_data .= pack("a4n", "8BIM", $resource['ResID'] ); - - - // Change: Fixed processing of embedded resource names, as of revision 1.10 - - // NOTE: Photoshop does not process resource names according to the standard : - // "Adobe Photoshop 6.0 File Formats Specification, Version 6.0, Release 2, November 2000" - // - // The resource name is actually formatted as follows: - // One byte name length, followed by the null terminated ascii name string. - // The field is then padded with a Null character if required, to ensure that the - // total length of the name length and name is even. - - // Append Name Size - $packed_IRB_data .= pack( "c", strlen(trim($resource['ResEmbeddedName']))); - - // Append the Resource Name to the packed output data - $packed_IRB_data .= trim($resource['ResEmbeddedName']); - - // If the resource name is even length, then with the addition of - // the size it becomes odd and needs to be padded to an even number - if ( strlen( trim($resource['ResEmbeddedName']) ) % 2 == 0 ) - { - // then it needs to be evened up by appending another null - $packed_IRB_data .= "\x00"; - } - - // Append the resource data size to the packed output data - $packed_IRB_data .= pack("N", strlen( $resource['ResData'] ) ); - - // Append the resource data to the packed output data - $packed_IRB_data .= $resource['ResData']; - - // If the resource data is odd length, - if ( strlen( $resource['ResData'] ) % 2 == 1 ) - { - // then it needs to be evened up by appending another null - $packed_IRB_data .= "\x00"; - } - } - - // Return the packed data string - return $packed_IRB_data; -} - -/****************************************************************************** -* End of Function: pack_Photoshop_IRB_Data -******************************************************************************/ - - - - - - - - -/****************************************************************************** -* -* Internal Function: Interpret_Transfer_Function -* -* Description: Used by Interpret_IRB_to_HTML to interpret Color transfer functions -* for Photoshop IRB resource 0x03F8. Converts the transfer function -* information to a human readable version. -* -* Parameters: Transfer_Function_Binary - a 28 byte Ink curves structure string -* -* Returns: output_str - the text string containing the transfer function -* information -* -******************************************************************************/ - -function Interpret_Transfer_Function( $Transfer_Function_Binary ) -{ - // Unpack the Transfer function information - $Trans_vals = unpack ( "n13Curve/nOverride", $Transfer_Function_Binary ); - - $output_str = "Transfer Function Points: "; - - // Cycle through each of the Transfer function array values - foreach ( $Trans_vals as $Key => $val ) - { - // Check if the value should be negative - if ($val > 32768 ) - { - // Value should be negative - make it so - $val = $val - 65536; - } - // Check that the Override item is not getting in this list, and - // that the value is not -1, which means ignored - if ( ( $Key != "Override" ) && ( $val != -1 ) ) - { - // This is a valid transfer function point, output it - $output_str .= $val/10 . "%, "; - } - } - - // Output the override info - if ( $Trans_vals['Override'] == 0 ) - { - $output_str .= "\nOverride: Let printer supply curve"; - } - else - { - $output_str .= "\nOverride: Override printer’s default transfer curve"; - } - - // Return the result - return $output_str; -} - -/****************************************************************************** -* End of Function: Interpret_Transfer_Function -******************************************************************************/ - - - - - -/****************************************************************************** -* -* Internal Function: Interpret_Halftone -* -* Description: Used by Interpret_IRB_to_HTML to interpret Color halftoning information -* for Photoshop IRB resource 0x03F5. Converts the halftoning info -* to a human readable version. -* -* Parameters: Transfer_Function_Binary - a 18 byte Halftone screen parameter -& structure string -* -* Returns: output_str - the text string containing the transfer function -* information -* -******************************************************************************/ - -function Interpret_Halftone( $Halftone_Binary ) -{ - // Create a string to receive the output - $output_str = ""; - - // Unpack the binary data into an array - $HalftoneInfo = unpack( "nFreqVal_int/nFreqVal_dec/nFreqScale/nAngle_int/nAngle_dec/nShapeCode/NMisc/CAccurate/CDefault", $Halftone_Binary ); - - // Interpret Ink Screen Frequency - $output_str .= "Ink Screen Frequency = " . ($HalftoneInfo['FreqVal_int'] + $HalftoneInfo['FreqVal_dec']/65536) . " lines per Inch\n"; - if ( $HalftoneInfo['FreqScale'] == 1 ) - { - $output_str .= "Display units for Ink Screen Frequency = Inches\n"; - } - else - { - $output_str .= "Display units for Ink Screen Frequency = Centimetres\n"; - } - - // Interpret Angle for screen - $output_str .= "Angle for screen = " . ($HalftoneInfo['Angle_int'] + $HalftoneInfo['Angle_dec']/65536) . " degrees\n"; - - // Interpret Shape of Halftone Dots - if ($HalftoneInfo['ShapeCode'] > 32768 ) - { - $HalftoneInfo['ShapeCode'] = $HalftoneInfo['ShapeCode'] - 65536; - } - if ( $HalftoneInfo['ShapeCode'] == 0 ) - { - $output_str .= "Shape of Halftone Dots = Round\n"; - } - elseif ( $HalftoneInfo['ShapeCode'] == 1 ) - { - $output_str .= "Shape of Halftone Dots = Ellipse\n"; - } - elseif ( $HalftoneInfo['ShapeCode'] == 2 ) - { - $output_str .= "Shape of Halftone Dots = Line\n"; - } - elseif ( $HalftoneInfo['ShapeCode'] == 3 ) - { - $output_str .= "Shape of Halftone Dots = Square\n"; - } - elseif ( $HalftoneInfo['ShapeCode'] == 4 ) - { - $output_str .= "Shape of Halftone Dots = Cross\n"; - } - elseif ( $HalftoneInfo['ShapeCode'] == 6 ) - { - $output_str .= "Shape of Halftone Dots = Diamond\n"; - } - else - { - $output_str .= "Shape of Halftone Dots = Unknown shape (" . $HalftoneInfo['ShapeCode'] . ")\n"; - } - - // Interpret Accurate Screens - if ( $HalftoneInfo['Accurate'] == 1 ) - { - $output_str .= "Use Accurate Screens Selected\n"; - } - else - { - $output_str .= "Use Other (not Accurate) Screens Selected\n"; - } - - // Interpret Printer Default Screens - if ( $HalftoneInfo['Default'] == 1 ) - { - $output_str .= "Use printer’s default screens\n"; - } - else - { - $output_str .= "Use Other (not Printer Default) Screens Selected\n"; - } - - // Return Text - return $output_str; - -} - -/****************************************************************************** -* End of Global Variable: Interpret_Halftone -******************************************************************************/ - - - - - - - - - - - - -/****************************************************************************** -* Global Variable: Photoshop_ID_Names -* -* Contents: The Names of the Photoshop IRB resources, indexed by their -* resource number -* -******************************************************************************/ - -$GLOBALS[ "Photoshop_ID_Names" ] = array( -0x03E8 => "Number of channels, rows, columns, depth, and mode. (Obsolete)", -0x03E9 => "Macintosh print manager info ", -0x03EB => "Indexed color table (Obsolete)", -0x03ED => "Resolution Info", -0x03EE => "Alpha Channel Names", -0x03EF => "Display Info", -0x03F0 => "Caption String", -0x03F1 => "Border information", -0x03F2 => "Background color", -0x03F3 => "Print flags", -0x03F4 => "Grayscale and multichannel halftoning information", -0x03F5 => "Color halftoning information", -0x03F6 => "Duotone halftoning information", -0x03F7 => "Grayscale and multichannel transfer function", -0x03F8 => "Color transfer functions", -0x03F9 => "Duotone transfer functions", -0x03FA => "Duotone image information", -0x03FB => "Black and white values", -0x03FC => "Obsolete Resource.", -0x03FD => "EPS options", -0x03FE => "Quick Mask information", -0x03FF => "Obsolete Resource", -0x0400 => "Layer state information", -0x0401 => "Working path (not saved)", -0x0402 => "Layers group information", -0x0403 => "Obsolete Resource", -0x0404 => "IPTC-NAA record", -0x0405 => "Raw Format Image mode", -0x0406 => "JPEG quality", -0x0408 => "Grid and guides information", -0x0409 => "Thumbnail resource", -0x040A => "Copyright flag", -0x040B => "URL", -0x040C => "Thumbnail resource", -0x040D => "Global Angle", -0x040E => "Color samplers resource", -0x040F => "ICC Profile", -0x0410 => "Watermark", -0x0411 => "ICC Untagged", -0x0412 => "Effects visible", -0x0413 => "Spot Halftone", -0x0414 => "Document Specific IDs", -0x0415 => "Unicode Alpha Names", -0x0416 => "Indexed Color Table Count", -0x0417 => "Tansparent Index. Index of transparent color, if any.", -0x0419 => "Global Altitude", -0x041A => "Slices", -0x041B => "Workflow URL", -0x041C => "Jump To XPEP", -0x041D => "Alpha Identifiers", -0x041E => "URL List", -0x0421 => "Version Info", -0x0BB7 => "Name of clipping path.", -0x2710 => "Print flags information" -); - -/****************************************************************************** -* End of Global Variable: Photoshop_ID_Names -******************************************************************************/ - - - - - -/****************************************************************************** -* Global Variable: Photoshop_ID_Descriptions -* -* Contents: The Descriptions of the Photoshop IRB resources, indexed by their -* resource number -* -******************************************************************************/ - -$GLOBALS[ "Photoshop_ID_Descriptions" ] = array( -0x03E8 => "Obsolete—Photoshop 2.0 only. number of channels, rows, columns, depth, and mode.", -0x03E9 => "Optional. Macintosh print manager print info record.", -0x03EB => "Obsolete—Photoshop 2.0 only. Contains the indexed color table.", -0x03ED => "ResolutionInfo structure. See Appendix A in Photoshop SDK Guide.pdf", -0x03EE => "Names of the alpha channels as a series of Pascal strings.", -0x03EF => "DisplayInfo structure. See Appendix A in Photoshop SDK Guide.pdf", -0x03F0 => "Optional. The caption as a Pascal string.", -0x03F1 => "Border information. border width, border units", -0x03F2 => "Background color.", -0x03F3 => "Print flags. labels, crop marks, color bars, registration marks, negative, flip, interpolate, caption.", -0x03F4 => "Grayscale and multichannel halftoning information.", -0x03F5 => "Color halftoning information.", -0x03F6 => "Duotone halftoning information.", -0x03F7 => "Grayscale and multichannel transfer function.", -0x03F8 => "Color transfer functions.", -0x03F9 => "Duotone transfer functions.", -0x03FA => "Duotone image information.", -0x03FB => "Effective black and white values for the dot range.", -0x03FC => "Obsolete Resource.", -0x03FD => "EPS options.", -0x03FE => "Quick Mask information. Quick Mask channel ID, Mask initially empty.", -0x03FF => "Obsolete Resource.", -0x0400 => "Layer state information. Index of target layer.", -0x0401 => "Working path (not saved).", -0x0402 => "Layers group information. Group ID for the dragging groups. Layers in a group have the same group ID.", -0x0403 => "Obsolete Resource.", -0x0404 => "IPTC-NAA record. This contains the File Info... information. See the IIMV4.pdf document.", -0x0405 => "Image mode for raw format files.", -0x0406 => "JPEG quality. Private.", -0x0408 => "Grid and guides information.", -0x0409 => "Thumbnail resource.", -0x040A => "Copyright flag. Boolean indicating whether image is copyrighted. Can be set via Property suite or by user in File Info...", -0x040B => "URL. Handle of a text string with uniform resource locator. Can be set via Property suite or by user in File Info...", -0x040C => "Thumbnail resource.", -0x040D => "Global Angle. Global lighting angle for effects layer.", -0x040E => "Color samplers resource.", -0x040F => "ICC Profile. The raw bytes of an ICC format profile, see the ICC34.pdf and ICC34.h files from the Internation Color Consortium.", -0x0410 => "Watermark.", -0x0411 => "ICC Untagged. Disables any assumed profile handling when opening the file. 1 = intentionally untagged.", -0x0412 => "Effects visible. Show/hide all the effects layer.", -0x0413 => "Spot Halftone. Version, length, variable length data.", -0x0414 => "Document specific IDs for layer identification", -0x0415 => "Unicode Alpha Names. Length and the string", -0x0416 => "Indexed Color Table Count. Number of colors in table that are actually defined", -0x0417 => "Transparent Index. Index of transparent color, if any.", -0x0419 => "Global Altitude.", -0x041A => "Slices.", -0x041B => "Workflow URL. Length, string.", -0x041C => "Jump To XPEP. Major version, Minor version, Count. Table which can include: Dirty flag, Mod date.", -0x041D => "Alpha Identifiers.", -0x041E => "URL List. Count of URLs, IDs, and strings", -0x0421 => "Version Info. Version, HasRealMergedData, string of writer name, string of reader name, file version.", -0x0BB7 => "Name of clipping path.", -0x2710 => "Print flags information. Version, Center crop marks, Bleed width value, Bleed width scale." -); - -/****************************************************************************** -* End of Global Variable: Photoshop_ID_Descriptions -******************************************************************************/ - - - - - - -?> \ No newline at end of file diff --git a/jpeg_metadata_tk/PictureInfo.php b/jpeg_metadata_tk/PictureInfo.php deleted file mode 100644 index 05edd00..0000000 --- a/jpeg_metadata_tk/PictureInfo.php +++ /dev/null @@ -1,284 +0,0 @@ - $App12_PI_Head, "Picture Info" => substr( $App12_PI_Text, 0, $pos + 5 ) ); - } - else - { - return array( "Header" => $App12_PI_Head, "Picture Info" => $App12_PI_Text ); - } - } - - // No Picture Info Segment Found - Return False - return array( FALSE, FALSE ); -} - -/****************************************************************************** -* End of Function: get_jpeg_header_data -******************************************************************************/ - - - - - -/****************************************************************************** -* -* Function: put_jpeg_App12_Pic_Info -* -* Description: Writes Picture Info text into an App12 JPEG segment. Uses information -* supplied by the get_jpeg_header_data function. If no App12 exists -* already a new one is created, otherwise it replaces the old one -* -* Parameters: jpeg_header_data - a JPEG header data array in the same format -* as from get_jpeg_header_data -* new_Pic_Info_Text - The Picture Info Text, including any header -* that is required -* -* Returns: jpeg_header_data - the JPEG header array with the new Picture -* info segment inserted -* FALSE - if an error occured -* -******************************************************************************/ - -function put_jpeg_App12_Pic_Info( $jpeg_header_data, $new_Pic_Info_Text ) -{ - - //Cycle through the header segments - for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) - { - // Check if we have found an APP12 header, - if ( strcmp ( $jpeg_header_data[$i][SegName], "APP12" ) == 0 ) - { - // Found an APP12 segment - // Check if the APP12 has one of the correct labels (headers) - // for a picture info segment - if ( ( strncmp ( $jpeg_header_data[$i]['SegData'], "[picture info]", 14) == 0 ) || - ( strncmp ( $jpeg_header_data[$i]['SegData'], "\x0a\x09\x09\x09\x09[picture info]", 19) == 0 ) || - ( strncmp ( $jpeg_header_data[$i]['SegData'], "SEIKO EPSON CORP. \x00", 20) == 0 ) || - ( strncmp ( $jpeg_header_data[$i]['SegData'], "Agfa Gevaert \x00", 16) == 0 ) || - ( strncmp ( $jpeg_header_data[$i]['SegData'], "SanyoElectricDSC\x00", 17) == 0 ) || - ( strncmp ( $jpeg_header_data[$i]['SegData'], "Type=", 5) == 0 ) || - ( strncmp ( $jpeg_header_data[$i]['SegData'], "OLYMPUS OPTICAL CO.,LTD.", 24) == 0 ) ) - { - // Found a preexisting Picture Info segment - Replace it with the new one and return. - $jpeg_header_data[$i][SegData] = $new_Pic_Info_Text; - return $jpeg_header_data; - } - } - } - - // No preexisting Picture Info segment found, insert a new one at the start of the header data. - - // Determine highest position of an APP segment at or below APP12, so we can put the - // new APP12 at this position - - - $highest_APP = -1; - - //Cycle through the header segments - for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) - { - // Check if we have found an APP segment at or below APP12, - if ( ( $jpeg_header_data[$i]['SegType'] >= 0xE0 ) && ( $jpeg_header_data[$i]['SegType'] <= 0xEC ) ) - { - // Found an APP segment at or below APP12 - $highest_APP = $i; - } - } - - // Insert the new Picture Info segment - array_splice($jpeg_header_data, $highest_APP + 1 , 0, array( array( "SegType" => 0xEC, - "SegName" => "APP12", - "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xEC ], - "SegData" => $new_Pic_Info_Text ) ) ); - - return $jpeg_header_data; - - -} - -/****************************************************************************** -* End of Function: put_jpeg_header_data -******************************************************************************/ - - - -/****************************************************************************** -* -* Function: Interpret_App12_Pic_Info_to_HTML -* -* Description: Generates html showing the contents of any JPEG App12 Picture -* Info segment -* -* Parameters: jpeg_header_data - the JPEG header data, as retrieved -* from the get_jpeg_header_data function -* -* Returns: output - the HTML -* -******************************************************************************/ - -function Interpret_App12_Pic_Info_to_HTML( $jpeg_header_data ) -{ - // Create a string to receive the output - $output = ""; - - // read the App12 Picture Info segment - $PI = get_jpeg_App12_Pic_Info( $jpeg_header_data ); - - // Check if the Picture Info segment was valid - if ( $PI !== array(FALSE, FALSE) ) - { - // Picture Info exists - add it to the output - $output .= "

    Picture Info Text

    \n"; - $output .= "

    Header: " . HTML_UTF8_Escape( $PI['Header'] ) . "

    \n"; - $output .= "

    Picture Info Text:

    " . HTML_UTF8_Escape( $PI['Picture Info'] ) . "
    \n"; - } - - // Return the result - return $output; -} - -/****************************************************************************** -* End of Function: Interpret_App12_Pic_Info_to_HTML -******************************************************************************/ - - - -?> diff --git a/jpeg_metadata_tk/Toolkit_Version.php b/jpeg_metadata_tk/Toolkit_Version.php deleted file mode 100644 index 99be1f3..0000000 --- a/jpeg_metadata_tk/Toolkit_Version.php +++ /dev/null @@ -1,50 +0,0 @@ - \ No newline at end of file diff --git a/jpeg_metadata_tk/Unicode.php b/jpeg_metadata_tk/Unicode.php deleted file mode 100644 index 19cbc5b..0000000 --- a/jpeg_metadata_tk/Unicode.php +++ /dev/null @@ -1,1227 +0,0 @@ - 1.10 : Added the following functions: -* smart_HTML_Entities -* smart_htmlspecialchars -* HTML_UTF16_UnEscape -* HTML_UTF8_UnEscape -* changed HTML_UTF8_Escape and HTML_UTF16_Escape to -* use smart_htmlspecialchars, so that characters which -* were already escaped would remain intact -* -* -* URL: http://electronics.ozhiker.com -* -* License: This file is part of the PHP JPEG Metadata Toolkit. -* -* The PHP JPEG Metadata Toolkit is free software; you can -* redistribute it and/or modify it under the terms of the -* GNU General Public License as published by the Free Software -* Foundation; either version 2 of the License, or (at your -* option) any later version. -* -* The PHP JPEG Metadata Toolkit is distributed in the hope -* that it will be useful, but WITHOUT ANY WARRANTY; without -* even the implied warranty of MERCHANTABILITY or FITNESS -* FOR A PARTICULAR PURPOSE. See the GNU General Public License -* for more details. -* -* You should have received a copy of the GNU General Public -* License along with the PHP JPEG Metadata Toolkit; if not, -* write to the Free Software Foundation, Inc., 59 Temple -* Place, Suite 330, Boston, MA 02111-1307 USA -* -* If you require a different license for commercial or other -* purposes, please contact the author: evan@ozhiker.com -* -******************************************************************************/ - - -// TODO: UTF-16 functions have not been tested fully - - - -/****************************************************************************** -* -* Unicode UTF-8 Encoding Functions -* -* Description: UTF-8 is a Unicode encoding system in which extended characters -* use only the upper half (128 values) of the byte range, thus it -* allows the use of normal 7-bit ASCII text. -* 7-Bit ASCII will pass straight through UTF-8 encoding/decoding without change -* -* -* The encoding is as follows: -* Unicode Value : Binary representation (x=data bit) -*-------------------------------------------------------------------------------- -* U-00000000 - U-0000007F: 0xxxxxxx <- This is 7-bit ASCII -* U-00000080 - U-000007FF: 110xxxxx 10xxxxxx -* U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx -* U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx -* U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx -* U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx -*-------------------------------------------------------------------------------- -* -******************************************************************************/ - - - - -/****************************************************************************** -* -* Unicode UTF-16 Encoding Functions -* -* Description: UTF-16 is a Unicode encoding system uses 16 bit values for representing -* characters. -* It also has an extended set of characters available by the use -* of surrogate pairs, which are a pair of 16 bit values, giving a -* total data length of 20 useful bits. -* -* -* The encoding is as follows: -* Unicode Value : Binary representation (x=data bit) -*-------------------------------------------------------------------------------- -* U-000000 - U-00D7FF: xxxxxxxx xxxxxxxx -* U-00D800 - U-00DBFF: Not available - used for high surrogate pairs -* U-00DC00 - U-00DFFF: Not available - used for low surrogate pairs - U-00E000 - U-00FFFF: xxxxxxxx xxxxxxxx -* U-010000 - U-10FFFF: 110110ww wwxxxxxx 110111xx xxxxxxxx ( wwww = (uni-0x10000)/0x10000 ) -*-------------------------------------------------------------------------------- -* -* Surrogate pair Calculations -* -* $hi = ($uni - 0x10000) / 0x400 + 0xD800; -* $lo = ($uni - 0x10000) % 0x400 + 0xDC00; -* -* -* $uni = 0x10000 + ($hi - 0xD800) * 0x400 + ($lo - 0xDC00); -* -* -******************************************************************************/ - - - - - - -/****************************************************************************** -* -* Function: UTF8_fix -* -* Description: Checks a string for badly formed Unicode UTF-8 coding and -* returns the same string containing only the parts which -* were properly formed UTF-8 data. -* -* Parameters: utf8_text - a string with possibly badly formed UTF-8 data -* -* Returns: output - the well formed UTF-8 version of the string -* -******************************************************************************/ - -function UTF8_fix( $utf8_text ) -{ - // Initialise the current position in the string - $pos = 0; - - // Create a string to accept the well formed output - $output = "" ; - - // Cycle through each group of bytes, ensuring the coding is correct - while ( $pos < strlen( $utf8_text ) ) - { - // Retreive the current numerical character value - $chval = ord($utf8_text{$pos}); - - // Check what the first character is - it will tell us how many bytes the - // Unicode value covers - - if ( ( $chval >= 0x00 ) && ( $chval <= 0x7F ) ) - { - // 1 Byte UTF-8 Unicode (7-Bit ASCII) Character - $bytes = 1; - } - else if ( ( $chval >= 0xC0 ) && ( $chval <= 0xDF ) ) - { - // 2 Byte UTF-8 Unicode Character - $bytes = 2; - } - else if ( ( $chval >= 0xE0 ) && ( $chval <= 0xEF ) ) - { - // 3 Byte UTF-8 Unicode Character - $bytes = 3; - } - else if ( ( $chval >= 0xF0 ) && ( $chval <= 0xF7 ) ) - { - // 4 Byte UTF-8 Unicode Character - $bytes = 4; - } - else if ( ( $chval >= 0xF8 ) && ( $chval <= 0xFB ) ) - { - // 5 Byte UTF-8 Unicode Character - $bytes = 5; - } - else if ( ( $chval >= 0xFC ) && ( $chval <= 0xFD ) ) - { - // 6 Byte UTF-8 Unicode Character - $bytes = 6; - } - else - { - // Invalid Code - skip character and do nothing - $bytes = 0; - $pos++; - } - - - // check that there is enough data remaining to read - if (($pos + $bytes - 1) < strlen( $utf8_text ) ) - { - // Cycle through the number of bytes specified, - // copying them to the output string - while ( $bytes > 0 ) - { - $output .= $utf8_text{$pos}; - $pos++; - $bytes--; - } - } - else - { - break; - } - } - - // Return the result - return $output; -} - -/****************************************************************************** -* End of Function: UTF8_fix -******************************************************************************/ - - - - - - - - - -/****************************************************************************** -* -* Function: UTF16_fix -* -* Description: Checks a string for badly formed Unicode UTF-16 coding and -* returns the same string containing only the parts which -* were properly formed UTF-16 data. -* -* Parameters: utf16_text - a string with possibly badly formed UTF-16 data -* MSB_first - True will cause processing as Big Endian UTF-16 (Motorola, MSB first) -* False will cause processing as Little Endian UTF-16 (Intel, LSB first) -* -* Returns: output - the well formed UTF-16 version of the string -* -******************************************************************************/ - -function UTF16_fix( $utf16_text, $MSB_first ) -{ - // Initialise the current position in the string - $pos = 0; - - // Create a string to accept the well formed output - $output = "" ; - - // Cycle through each group of bytes, ensuring the coding is correct - while ( $pos < strlen( $utf16_text ) ) - { - // Retreive the current numerical character value - $chval1 = ord($utf16_text{$pos}); - - // Skip over character just read - $pos++; - - // Check if there is another character available - if ( $pos < strlen( $utf16_text ) ) - { - // Another character is available - get it for the second half of the UTF-16 value - $chval2 = ord( $utf16_text{$pos} ); - } - else - { - // Error - no second byte to this UTF-16 value - end processing - continue 1; - } - - // Skip over character just read - $pos++; - - // Calculate the 16 bit unicode value - if ( $MSB_first ) - { - // Big Endian - $UTF16_val = $chval1 * 0x100 + $chval2; - } - else - { - // Little Endian - $UTF16_val = $chval2 * 0x100 + $chval1; - } - - - - if ( ( ( $UTF16_val >= 0x0000 ) && ( $UTF16_val <= 0xD7FF ) ) || - ( ( $UTF16_val >= 0xE000 ) && ( $UTF16_val <= 0xFFFF ) ) ) - { - // Normal Character (Non Surrogate pair) - // Add it to the output - $output .= chr( $chval1 ) . chr ( $chval2 ); - } - else if ( ( $UTF16_val >= 0xD800 ) && ( $UTF16_val <= 0xDBFF ) ) - { - // High surrogate of a surrogate pair - // Now we need to read the low surrogate - // Check if there is another 2 characters available - if ( ( $pos + 3 ) < strlen( $utf16_text ) ) - { - // Another 2 characters are available - get them - $chval3 = ord( $utf16_text{$pos} ); - $chval4 = ord( $utf16_text{$pos+1} ); - - // Calculate the second 16 bit unicode value - if ( $MSB_first ) - { - // Big Endian - $UTF16_val2 = $chval3 * 0x100 + $chval4; - } - else - { - // Little Endian - $UTF16_val2 = $chval4 * 0x100 + $chval3; - } - - // Check that this is a low surrogate - if ( ( $UTF16_val2 >= 0xDC00 ) && ( $UTF16_val2 <= 0xDFFF ) ) - { - // Low surrogate found following high surrogate - // Add both to the output - $output .= chr( $chval1 ) . chr ( $chval2 ) . chr( $chval3 ) . chr ( $chval4 ); - - // Skip over the low surrogate - $pos += 2; - } - else - { - // Low surrogate not found after high surrogate - // Don't add either to the output - // Only the High surrogate is skipped and processing continues after it - } - - } - else - { - // Error - not enough data for low surrogate - end processing - continue 1; - } - - } - else - { - // Low surrogate of a surrogate pair - // This should not happen - it means this is a lone low surrogate - // Dont add it to the output - } - - } - - // Return the result - return $output; -} - -/****************************************************************************** -* End of Function: UTF16_fix -******************************************************************************/ - - - - - -/****************************************************************************** -* -* Function: UTF8_to_unicode_array -* -* Description: Converts a string encoded with Unicode UTF-8, to an array of -* numbers which represent unicode character numbers -* -* Parameters: utf8_text - a string containing the UTF-8 data -* -* Returns: output - the array containing the unicode character numbers -* -******************************************************************************/ - -function UTF8_to_unicode_array( $utf8_text ) -{ - // Create an array to receive the unicode character numbers output - $output = array( ); - - // Cycle through the characters in the UTF-8 string - for ( $pos = 0; $pos < strlen( $utf8_text ); $pos++ ) - { - // Retreive the current numerical character value - $chval = ord($utf8_text{$pos}); - - // Check what the first character is - it will tell us how many bytes the - // Unicode value covers - - if ( ( $chval >= 0x00 ) && ( $chval <= 0x7F ) ) - { - // 1 Byte UTF-8 Unicode (7-Bit ASCII) Character - $bytes = 1; - $outputval = $chval; // Since 7-bit ASCII is unaffected, the output equals the input - } - else if ( ( $chval >= 0xC0 ) && ( $chval <= 0xDF ) ) - { - // 2 Byte UTF-8 Unicode - $bytes = 2; - $outputval = $chval & 0x1F; // The first byte is bitwise ANDed with 0x1F to remove the leading 110b - } - else if ( ( $chval >= 0xE0 ) && ( $chval <= 0xEF ) ) - { - // 3 Byte UTF-8 Unicode - $bytes = 3; - $outputval = $chval & 0x0F; // The first byte is bitwise ANDed with 0x0F to remove the leading 1110b - } - else if ( ( $chval >= 0xF0 ) && ( $chval <= 0xF7 ) ) - { - // 4 Byte UTF-8 Unicode - $bytes = 4; - $outputval = $chval & 0x07; // The first byte is bitwise ANDed with 0x07 to remove the leading 11110b - } - else if ( ( $chval >= 0xF8 ) && ( $chval <= 0xFB ) ) - { - // 5 Byte UTF-8 Unicode - $bytes = 5; - $outputval = $chval & 0x03; // The first byte is bitwise ANDed with 0x03 to remove the leading 111110b - } - else if ( ( $chval >= 0xFC ) && ( $chval <= 0xFD ) ) - { - // 6 Byte UTF-8 Unicode - $bytes = 6; - $outputval = $chval & 0x01; // The first byte is bitwise ANDed with 0x01 to remove the leading 1111110b - } - else - { - // Invalid Code - do nothing - $bytes = 0; - } - - // Check if the byte was valid - if ( $bytes !== 0 ) - { - // The byte was valid - - // Check if there is enough data left in the UTF-8 string to allow the - // retrieval of the remainder of this unicode character - if ( $pos + $bytes - 1 < strlen( $utf8_text ) ) - { - // The UTF-8 string is long enough - - // Cycle through the number of bytes required, - // minus the first one which has already been done - while ( $bytes > 1 ) - { - $pos++; - $bytes--; - - // Each remaining byte is coded with 6 bits of data and 10b on the high - // order bits. Hence we need to shift left by 6 bits (0x40) then add the - // current characer after it has been bitwise ANDed with 0x3F to remove the - // highest two bits. - $outputval = $outputval*0x40 + ( (ord($utf8_text{$pos})) & 0x3F ); - } - - // Add the calculated Unicode number to the output array - $output[] = $outputval; - } - } - - } - - // Return the resulting array - return $output; -} - -/****************************************************************************** -* End of Function: UTF8_to_unicode_array -******************************************************************************/ - - - - - -/****************************************************************************** -* -* Function: UTF16_to_unicode_array -* -* Description: Converts a string encoded with Unicode UTF-16, to an array of -* numbers which represent unicode character numbers -* -* Parameters: utf16_text - a string containing the UTF-16 data -* MSB_first - True will cause processing as Big Endian UTF-16 (Motorola, MSB first) -* False will cause processing as Little Endian UTF-16 (Intel, LSB first) -* -* Returns: output - the array containing the unicode character numbers -* -******************************************************************************/ - -function UTF16_to_unicode_array( $utf16_text, $MSB_first ) -{ - // Create an array to receive the unicode character numbers output - $output = array( ); - - - // Initialise the current position in the string - $pos = 0; - - // Cycle through each group of bytes, ensuring the coding is correct - while ( $pos < strlen( $utf16_text ) ) - { - // Retreive the current numerical character value - $chval1 = ord($utf16_text{$pos}); - - // Skip over character just read - $pos++; - - // Check if there is another character available - if ( $pos < strlen( $utf16_text ) ) - { - // Another character is available - get it for the second half of the UTF-16 value - $chval2 = ord( $utf16_text{$pos} ); - } - else - { - // Error - no second byte to this UTF-16 value - end processing - continue 1; - } - - // Skip over character just read - $pos++; - - // Calculate the 16 bit unicode value - if ( $MSB_first ) - { - // Big Endian - $UTF16_val = $chval1 * 0x100 + $chval2; - } - else - { - // Little Endian - $UTF16_val = $chval2 * 0x100 + $chval1; - } - - - if ( ( ( $UTF16_val >= 0x0000 ) && ( $UTF16_val <= 0xD7FF ) ) || - ( ( $UTF16_val >= 0xE000 ) && ( $UTF16_val <= 0xFFFF ) ) ) - { - // Normal Character (Non Surrogate pair) - // Add it to the output - $output[] = $UTF16_val; - } - else if ( ( $UTF16_val >= 0xD800 ) && ( $UTF16_val <= 0xDBFF ) ) - { - // High surrogate of a surrogate pair - // Now we need to read the low surrogate - // Check if there is another 2 characters available - if ( ( $pos + 3 ) < strlen( $utf16_text ) ) - { - // Another 2 characters are available - get them - $chval3 = ord( $utf16_text{$pos} ); - $chval4 = ord( $utf16_text{$pos+1} ); - - // Calculate the second 16 bit unicode value - if ( $MSB_first ) - { - // Big Endian - $UTF16_val2 = $chval3 * 0x100 + $chval4; - } - else - { - // Little Endian - $UTF16_val2 = $chval4 * 0x100 + $chval3; - } - - // Check that this is a low surrogate - if ( ( $UTF16_val2 >= 0xDC00 ) && ( $UTF16_val2 <= 0xDFFF ) ) - { - // Low surrogate found following high surrogate - // Add both to the output - $output[] = 0x10000 + ( ( $UTF16_val - 0xD800 ) * 0x400 ) + ( $UTF16_val2 - 0xDC00 ); - - // Skip over the low surrogate - $pos += 2; - } - else - { - // Low surrogate not found after high surrogate - // Don't add either to the output - // The high surrogate is skipped and processing continued - } - - } - else - { - // Error - not enough data for low surrogate - end processing - continue 1; - } - - } - else - { - // Low surrogate of a surrogate pair - // This should not happen - it means this is a lone low surrogate - // Don't add it to the output - } - - } - - // Return the result - return $output; - - -} - -/****************************************************************************** -* End of Function: UTF16_to_unicode_array -******************************************************************************/ - - - - - - - -/****************************************************************************** -* -* Function: unicode_array_to_UTF8 -* -* Description: Converts an array of unicode character numbers to a string -* encoded by UTF-8 -* -* Parameters: unicode_array - the array containing unicode character numbers -* -* Returns: output - the UTF-8 encoded string representing the data -* -******************************************************************************/ - -function unicode_array_to_UTF8( $unicode_array ) -{ - - // Create a string to receive the UTF-8 output - $output = ""; - - // Cycle through each Unicode character number - foreach( $unicode_array as $unicode_char ) - { - // Check which range the current unicode character lies in - if ( ( $unicode_char >= 0x00 ) && ( $unicode_char <= 0x7F ) ) - { - // 1 Byte UTF-8 Unicode (7-Bit ASCII) Character - - $output .= chr($unicode_char); // Output is equal to input for 7-bit ASCII - } - else if ( ( $unicode_char >= 0x80 ) && ( $unicode_char <= 0x7FF ) ) - { - // 2 Byte UTF-8 Unicode - binary encode data as : 110xxxxx 10xxxxxx - - $output .= chr(0xC0 + ($unicode_char/0x40)); - $output .= chr(0x80 + ($unicode_char & 0x3F)); - } - else if ( ( $unicode_char >= 0x800 ) && ( $unicode_char <= 0xFFFF ) ) - { - // 3 Byte UTF-8 Unicode - binary encode data as : 1110xxxx 10xxxxxx 10xxxxxx - - $output .= chr(0xE0 + ($unicode_char/0x1000)); - $output .= chr(0x80 + (($unicode_char/0x40) & 0x3F)); - $output .= chr(0x80 + ($unicode_char & 0x3F)); - } - else if ( ( $unicode_char >= 0x10000 ) && ( $unicode_char <= 0x1FFFFF ) ) - { - // 4 Byte UTF-8 Unicode - binary encode data as : 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - - $output .= chr(0xF0 + ($unicode_char/0x40000)); - $output .= chr(0x80 + (($unicode_char/0x1000) & 0x3F)); - $output .= chr(0x80 + (($unicode_char/0x40) & 0x3F)); - $output .= chr(0x80 + ($unicode_char & 0x3F)); - } - else if ( ( $unicode_char >= 0x200000 ) && ( $unicode_char <= 0x3FFFFFF ) ) - { - // 5 Byte UTF-8 Unicode - binary encode data as : 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx - - $output .= chr(0xF8 + ($unicode_char/0x1000000)); - $output .= chr(0x80 + (($unicode_char/0x40000) & 0x3F)); - $output .= chr(0x80 + (($unicode_char/0x1000) & 0x3F)); - $output .= chr(0x80 + (($unicode_char/0x40) & 0x3F)); - $output .= chr(0x80 + ($unicode_char & 0x3F)); - } - else if ( ( $unicode_char >= 0x4000000 ) && ( $unicode_char <= 0x7FFFFFFF ) ) - { - // 6 Byte UTF-8 Unicode - binary encode data as : 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx - - $output .= chr(0xFC + ($unicode_char/0x40000000)); - $output .= chr(0x80 + (($unicode_char/0x1000000) & 0x3F)); - $output .= chr(0x80 + (($unicode_char/0x40000) & 0x3F)); - $output .= chr(0x80 + (($unicode_char/0x1000) & 0x3F)); - $output .= chr(0x80 + (($unicode_char/0x40) & 0x3F)); - $output .= chr(0x80 + ($unicode_char & 0x3F)); - } - else - { - // Invalid Code - do nothing - } - - } - - // Return resulting UTF-8 String - return $output; -} - -/****************************************************************************** -* End of Function: unicode_array_to_UTF8 -******************************************************************************/ - - - - - - - - - -/****************************************************************************** -* -* Function: unicode_array_to_UTF16 -* -* Description: Converts an array of unicode character numbers to a string -* encoded by UTF-16 -* -* Parameters: unicode_array - the array containing unicode character numbers -* MSB_first - True will cause processing as Big Endian UTF-16 (Motorola, MSB first) -* False will cause processing as Little Endian UTF-16 (Intel, LSB first) -* -* Returns: output - the UTF-16 encoded string representing the data -* -******************************************************************************/ - -function unicode_array_to_UTF16( $unicode_array, $MSB_first ) -{ - - // Create a string to receive the UTF-16 output - $output = ""; - - // Cycle through each Unicode character number - foreach( $unicode_array as $unicode_char ) - { - // Check which range the current unicode character lies in - if ( ( ( $unicode_char >= 0x0000 ) && ( $unicode_char <= 0xD7FF ) ) || - ( ( $unicode_char >= 0xE000 ) && ( $unicode_char <= 0xFFFF ) ) ) - { - // Normal 16 Bit Character (Not a Surrogate Pair) - - // Check what byte order should be used - if ( $MSB_first ) - { - // Big Endian - $output .= chr( $unicode_char / 0x100 ) . chr( $unicode_char % 0x100 ) ; - } - else - { - // Little Endian - $output .= chr( $unicode_char % 0x100 ) . chr( $unicode_char / 0x100 ) ; - } - - } - else if ( ( $unicode_char >= 0x10000 ) && ( $unicode_char <= 0x10FFFF ) ) - { - // Surrogate Pair required - - // Calculate Surrogates - $High_Surrogate = ( ( $unicode_char - 0x10000 ) / 0x400 ) + 0xD800; - $Low_Surrogate = ( ( $unicode_char - 0x10000 ) % 0x400 ) + 0xDC00; - - // Check what byte order should be used - if ( $MSB_first ) - { - // Big Endian - $output .= chr( $High_Surrogate / 0x100 ) . chr( $High_Surrogate % 0x100 ); - $output .= chr( $Low_Surrogate / 0x100 ) . chr( $Low_Surrogate % 0x100 ); - } - else - { - // Little Endian - $output .= chr( $High_Surrogate % 0x100 ) . chr( $High_Surrogate / 0x100 ); - $output .= chr( $Low_Surrogate % 0x100 ) . chr( $Low_Surrogate / 0x100 ); - } - } - else - { - // Invalid UTF-16 codepoint - // Unicode value should never be between 0xD800 and 0xDFFF - // Do not output this point - there is no way to encode it in UTF-16 - } - - } - - // Return resulting UTF-16 String - return $output; -} - -/****************************************************************************** -* End of Function: unicode_array_to_UTF16 -******************************************************************************/ - - - - - -/****************************************************************************** -* -* Function: xml_UTF8_clean -* -* Description: XML has specific requirements about the characters that are -* allowed, and characters that must be escaped. -* This function ensures that all characters in the given string -* are valid, and that characters such as Quotes, Greater than, -* Less than and Ampersand are properly escaped. Newlines and Tabs -* are also escaped. -* Note - Do not use this on constructed XML which includes tags, -* as it will escape the tags. It is designed to be used -* on the tag and attribute names, attribute values, and text. -* -* Parameters: utf8_text - a string containing the UTF-8 data -* -* Returns: output - the array containing the unicode character numbers -* -******************************************************************************/ - -function xml_UTF8_clean( $UTF8_text ) -{ - // Ensure that the Unicode UTF8 encoding is valid. - - $UTF8_text = UTF8_fix( $UTF8_text ); - - - // XML only allows characters in the following unicode ranges - // #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] - // Hence we need to delete any characters that dont fit this - - // Convert the UTF-8 string to an array of unicode character numbers - $unicode_array = UTF8_to_unicode_array( $UTF8_text ); - - // Create a new array to receive the valid unicode character numbers - $new_unicode_array = array( ); - - // Cycle through the unicode character numbers - foreach( $unicode_array as $unichar ) - { - // Check if the unicode character number is valid for XML - if ( ( $unichar == 0x09 ) || - ( $unichar == 0x0A ) || - ( $unichar == 0x0D ) || - ( ( $unichar >= 0x20 ) && ( $unichar <= 0xD7FF ) ) || - ( ( $unichar >= 0xE000 ) && ( $unichar <= 0xFFFD ) ) || - ( ( $unichar >= 0x10000 ) && ( $unichar <= 0x10FFFF ) ) ) - { - // Unicode character is valid for XML - add it to the valid characters array - $new_unicode_array[] = $unichar; - } - - } - - // Convert the array of valid unicode character numbers back to UTF-8 encoded text - $UTF8_text = unicode_array_to_UTF8( $new_unicode_array ); - - // Escape any special HTML characters present - $UTF8_text = htmlspecialchars ( $UTF8_text, ENT_QUOTES ); - - // Escape CR, LF and TAB characters, so that they are kept and not treated as expendable white space - $trans = array( "\x09" => " ", "\x0A" => " ", "\x0D" => " " ); - $UTF8_text = strtr( $UTF8_text, $trans ); - - // Return the resulting XML valid string - return $UTF8_text; -} - -/****************************************************************************** -* End of Function: xml_UTF8_clean -******************************************************************************/ - - - - - - - - - -/****************************************************************************** -* -* Function: xml_UTF16_clean -* -* Description: XML has specific requirements about the characters that are -* allowed, and characters that must be escaped. -* This function ensures that all characters in the given string -* are valid, and that characters such as Quotes, Greater than, -* Less than and Ampersand are properly escaped. Newlines and Tabs -* are also escaped. -* Note - Do not use this on constructed XML which includes tags, -* as it will escape the tags. It is designed to be used -* on the tag and attribute names, attribute values, and text. -* -* Parameters: utf16_text - a string containing the UTF-16 data -* MSB_first - True will cause processing as Big Endian UTF-16 (Motorola, MSB first) -* False will cause processing as Little Endian UTF-16 (Intel, LSB first) -* -* Returns: output - the array containing the unicode character numbers -* -******************************************************************************/ - -function xml_UTF16_clean( $UTF16_text, $MSB_first ) -{ - // Ensure that the Unicode UTF16 encoding is valid. - - $UTF16_text = UTF16_fix( $UTF16_text, $MSB_first ); - - - // XML only allows characters in the following unicode ranges - // #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] - // Hence we need to delete any characters that dont fit this - - // Convert the UTF-16 string to an array of unicode character numbers - $unicode_array = UTF16_to_unicode_array( $UTF16_text, $MSB_first ); - - // Create a new array to receive the valid unicode character numbers - $new_unicode_array = array( ); - - // Cycle through the unicode character numbers - foreach( $unicode_array as $unichar ) - { - // Check if the unicode character number is valid for XML - if ( ( $unichar == 0x09 ) || - ( $unichar == 0x0A ) || - ( $unichar == 0x0D ) || - ( ( $unichar >= 0x20 ) && ( $unichar <= 0xD7FF ) ) || - ( ( $unichar >= 0xE000 ) && ( $unichar <= 0xFFFD ) ) || - ( ( $unichar >= 0x10000 ) && ( $unichar <= 0x10FFFF ) ) ) - { - // Unicode character is valid for XML - add it to the valid characters array - $new_unicode_array[] = $unichar; - } - - } - - // Convert the array of valid unicode character numbers back to UTF-16 encoded text - $UTF16_text = unicode_array_to_UTF16( $new_unicode_array, $MSB_first ); - - // Escape any special HTML characters present - $UTF16_text = htmlspecialchars ( $UTF16_text, ENT_QUOTES ); - - // Escape CR, LF and TAB characters, so that they are kept and not treated as expendable white space - $trans = array( "\x09" => " ", "\x0A" => " ", "\x0D" => " " ); - $UTF16_text = strtr( $UTF16_text, $trans ); - - // Return the resulting XML valid string - return $UTF16_text; -} - -/****************************************************************************** -* End of Function: xml_UTF16_clean -******************************************************************************/ - - - - - - -/****************************************************************************** -* -* Function: HTML_UTF8_Escape -* -* Description: A HTML page can display UTF-8 data properly if it has a -* META http-equiv="Content-Type" tag with the content attribute -* including the value: "charset=utf-8". -* Otherwise the ISO-8859-1 character set is usually assumed, and -* Unicode values above 0x7F must be escaped. -* This function takes a UTF-8 encoded string and escapes the -* characters above 0x7F as well as reserved HTML characters such -* as Quotes, Greater than, Less than and Ampersand. -* -* Parameters: utf8_text - a string containing the UTF-8 data -* -* Returns: htmloutput - a string containing the HTML equivalent -* -******************************************************************************/ - -function HTML_UTF8_Escape( $UTF8_text ) -{ - - // Ensure that the Unicode UTF8 encoding is valid. - $UTF8_text = UTF8_fix( $UTF8_text ); - - // Change: changed to use smart_htmlspecialchars, so that characters which were already escaped would remain intact, as of revision 1.10 - // Escape any special HTML characters present - $UTF8_text = smart_htmlspecialchars( $UTF8_text, ENT_QUOTES ); - - // Convert the UTF-8 string to an array of unicode character numbers - $unicode_array = UTF8_to_unicode_array( $UTF8_text ); - - // Create a string to receive the escaped HTML - $htmloutput = ""; - - // Cycle through the unicode character numbers - foreach( $unicode_array as $unichar ) - { - // Check if the character needs to be escaped - if ( ( $unichar >= 0x00 ) && ( $unichar <= 0x7F ) ) - { - // Character is less than 0x7F - add it to the html as is - $htmloutput .= chr( $unichar ); - } - else - { - // Character is greater than 0x7F - escape it and add it to the html - $htmloutput .= "&#x" . dechex($unichar) . ";"; - } - } - - // Return the resulting escaped HTML - return $htmloutput; -} - -/****************************************************************************** -* End of Function: HTML_UTF8_Escape -******************************************************************************/ - - - -/****************************************************************************** -* -* Function: HTML_UTF8_UnEscape -* -* Description: Converts HTML which contains escaped decimal or hex characters -* into UTF-8 text -* -* Parameters: HTML_text - a string containing the HTML text to convert -* -* Returns: utfoutput - a string containing the UTF-8 equivalent -* -******************************************************************************/ - -function HTML_UTF8_UnEscape( $HTML_text ) -{ - preg_match_all( "/\&\#(\d+);/", $HTML_text, $matches); - preg_match_all( "/\&\#[x|X]([A|B|C|D|E|F|a|b|c|d|e|f|0-9]+);/", $HTML_text, $hexmatches); - foreach( $hexmatches[1] as $index => $match ) - { - $matches[0][] = $hexmatches[0][$index]; - $matches[1][] = hexdec( $match ); - } - - for ( $i = 0; $i < count( $matches[ 0 ] ); $i++ ) - { - $trans = array( $matches[0][$i] => unicode_array_to_UTF8( array( $matches[1][$i] ) ) ); - - $HTML_text = strtr( $HTML_text , $trans ); - } - return $HTML_text; -} - -/****************************************************************************** -* End of Function: HTML_UTF8_UnEscape -******************************************************************************/ - - - - - - -/****************************************************************************** -* -* Function: HTML_UTF16_Escape -* -* Description: A HTML page can display UTF-16 data properly if it has a -* META http-equiv="Content-Type" tag with the content attribute -* including the value: "charset=utf-16". -* Otherwise the ISO-8859-1 character set is usually assumed, and -* Unicode values above 0x7F must be escaped. -* This function takes a UTF-16 encoded string and escapes the -* characters above 0x7F as well as reserved HTML characters such -* as Quotes, Greater than, Less than and Ampersand. -* -* Parameters: utf16_text - a string containing the UTF-16 data -* MSB_first - True will cause processing as Big Endian UTF-16 (Motorola, MSB first) -* False will cause processing as Little Endian UTF-16 (Intel, LSB first) -* -* Returns: htmloutput - a string containing the HTML equivalent -* -******************************************************************************/ - -function HTML_UTF16_Escape( $UTF16_text, $MSB_first ) -{ - - // Ensure that the Unicode UTF16 encoding is valid. - $UTF16_text = UTF16_fix( $UTF16_text, $MSB_first ); - - // Change: changed to use smart_htmlspecialchars, so that characters which were already escaped would remain intact, as of revision 1.10 - // Escape any special HTML characters present - $UTF16_text = smart_htmlspecialchars( $UTF16_text ); - - // Convert the UTF-16 string to an array of unicode character numbers - $unicode_array = UTF16_to_unicode_array( $UTF16_text, $MSB_first ); - - // Create a string to receive the escaped HTML - $htmloutput = ""; - - // Cycle through the unicode character numbers - foreach( $unicode_array as $unichar ) - { - // Check if the character needs to be escaped - if ( ( $unichar >= 0x00 ) && ( $unichar <= 0x7F ) ) - { - // Character is less than 0x7F - add it to the html as is - $htmloutput .= chr( $unichar ); - } - else - { - // Character is greater than 0x7F - escape it and add it to the html - $htmloutput .= "&#x" . dechex($unichar) . ";"; - } - } - - // Return the resulting escaped HTML - return $htmloutput; -} - -/****************************************************************************** -* End of Function: HTML_UTF16_Escape -******************************************************************************/ - - -/****************************************************************************** -* -* Function: HTML_UTF16_UnEscape -* -* Description: Converts HTML which contains escaped decimal or hex characters -* into UTF-16 text -* -* Parameters: HTML_text - a string containing the HTML text to be converted -* MSB_first - True will cause processing as Big Endian UTF-16 (Motorola, MSB first) -* False will cause processing as Little Endian UTF-16 (Intel, LSB first) -* -* Returns: utfoutput - a string containing the UTF-16 equivalent -* -******************************************************************************/ - -function HTML_UTF16_UnEscape( $HTML_text, $MSB_first ) -{ - $utf8_text = HTML_UTF8_UnEscape( $HTML_text ); - - return unicode_array_to_UTF16( UTF8_to_unicode_array( $utf8_text ), $MSB_first ); -} - -/****************************************************************************** -* End of Function: HTML_UTF16_UnEscape -******************************************************************************/ - - - - -/****************************************************************************** -* -* Function: smart_HTML_Entities -* -* Description: Performs the same function as HTML_Entities, but leaves entities -* that are already escaped intact. -* -* Parameters: HTML_text - a string containing the HTML text to be escaped -* -* Returns: HTML_text_out - a string containing the escaped HTML text -* -******************************************************************************/ - -function smart_HTML_Entities( $HTML_text ) -{ - // Get a table containing the HTML entities translations - $translation_table = get_html_translation_table( HTML_ENTITIES ); - - // Change the ampersand to translate to itself, to avoid getting & - $translation_table[ chr(38) ] = '&'; - - // Perform replacements - // Regular expression says: find an ampersand, check the text after it, - // if the text after it is not one of the following, then replace the ampersand - // with & - // a) any combination of up to 4 letters (upper or lower case) with at least 2 or 3 non whitespace characters, then a semicolon - // b) a hash symbol, then between 2 and 7 digits - // c) a hash symbol, an 'x' character, then between 2 and 7 digits - // d) a hash symbol, an 'X' character, then between 2 and 7 digits - return preg_replace( "/&(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,7}|#x[0-9]{2,7}|#X[0-9]{2,7};)/","&" , strtr( $HTML_text, $translation_table ) ); -} - -/****************************************************************************** -* End of Function: smart_HTML_Entities -******************************************************************************/ - - - -/****************************************************************************** -* -* Function: smart_htmlspecialchars -* -* Description: Performs the same function as htmlspecialchars, but leaves characters -* that are already escaped intact. -* -* Parameters: HTML_text - a string containing the HTML text to be escaped -* -* Returns: HTML_text_out - a string containing the escaped HTML text -* -******************************************************************************/ - -function smart_htmlspecialchars( $HTML_text ) -{ - // Get a table containing the HTML special characters translations - $translation_table=get_html_translation_table (HTML_SPECIALCHARS); - - // Change the ampersand to translate to itself, to avoid getting & - $translation_table[ chr(38) ] = '&'; - - // Perform replacements - // Regular expression says: find an ampersand, check the text after it, - // if the text after it is not one of the following, then replace the ampersand - // with & - // a) any combination of up to 4 letters (upper or lower case) with at least 2 or 3 non whitespace characters, then a semicolon - // b) a hash symbol, then between 2 and 7 digits - // c) a hash symbol, an 'x' character, then between 2 and 7 digits - // d) a hash symbol, an 'X' character, then between 2 and 7 digits - return preg_replace( "/&(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,7}|#x[0-9]{2,7}|#X[0-9]{2,7};)/","&" , strtr( $HTML_text, $translation_table ) ); -} - -/****************************************************************************** -* End of Function: smart_htmlspecialchars -******************************************************************************/ - - -?> \ No newline at end of file diff --git a/jpeg_metadata_tk/Write_File_Info.php b/jpeg_metadata_tk/Write_File_Info.php deleted file mode 100644 index efa402a..0000000 --- a/jpeg_metadata_tk/Write_File_Info.php +++ /dev/null @@ -1,196 +0,0 @@ - - - - - - - - - - Writing Photoshop File Info Metadata - - - - -

    Powered by: PHP JPEG Metadata Toolkit version , Copyright (C) 2004 Evan Hunter

    -
    -
    - - $var_val ) - { - $new_ps_file_info_array[ $var_key ] = stripslashes( $var_val ); - } - - // Keywords should be an array - explode it on newline boundarys - $new_ps_file_info_array[ 'keywords' ] = explode( "\n", trim( $new_ps_file_info_array[ 'keywords' ] ) ); - - // Supplemental Categories should be an array - explode it on newline boundarys - $new_ps_file_info_array[ 'supplementalcategories' ] = explode( "\n", trim( $new_ps_file_info_array[ 'supplementalcategories' ] ) ); - - // Make the filename easier to access - $filename = $new_ps_file_info_array[ 'filename' ]; - - // Protect against hackers editing other files - $path_parts = pathinfo( $filename ); - if ( strcasecmp( $path_parts["extension"], "jpg" ) != 0 ) - { - echo "Incorrect File Type - JPEG Only\n"; - exit( ); - } - // Change: removed limitation on file being in current directory - as of version 1.11 - - // Retrieve the header information - $jpeg_header_data = get_jpeg_header_data( $filename ); - - // Retreive the EXIF, XMP and Photoshop IRB information from - // the existing file, so that it can be updated - $Exif_array = get_EXIF_JPEG( $filename ); - $XMP_array = read_XMP_array_from_text( get_XMP_text( $jpeg_header_data ) ); - $IRB_array = get_Photoshop_IRB( $jpeg_header_data ); - - // Update the JPEG header information with the new Photoshop File Info - $jpeg_header_data = put_photoshop_file_info( $jpeg_header_data, $new_ps_file_info_array, $Exif_array, $XMP_array, $IRB_array ); - - // Check if the Update worked - if ( $jpeg_header_data == FALSE ) - { - // Update of file info didn't work - output error message - echo "Error - Failure update Photoshop File Info : $filename
    \n"; - - // Output HTML with the form and data which was - // sent, to allow the user to fix it - - $outputfilename = $filename; - require_once "Edit_File_info.php"; - echo "\n"; - echo "\n"; - - // Abort processing - exit( ); - } - - // Attempt to write the new JPEG file - if ( FALSE == put_jpeg_header_data( $filename, $filename, $jpeg_header_data ) ) - { - // Writing of the new file didn't work - output error message - echo "Error - Failure to write new JPEG : $filename
    \n"; - - // Output HTML with the form and data which was - // sent, to allow the user to fix it - - $outputfilename = $filename; - require_once "Edit_File_info.php"; - echo "\n"; - echo "\n"; - - // Abort processing - exit( ); - } - - - // Writing of new JPEG succeeded - - // Output information about new file - - echo "

    DONE! - $filename updated

    \n"; - echo "

    View Full Metatdata Information

    \n"; - echo "

    Re-Edit Photoshop File Info

    \n"; - echo "

    \n"; - echo "

    Below is the updated image, you can save it and look at the changed metadata in your favorite image editor

    \n"; - echo "

    \n"; - - - ?> - -
    -
    -
    -
    - - -

    Powered by: PHP JPEG Metadata Toolkit version , Copyright (C) 2004 Evan Hunter

    - -
    -
    - - - - diff --git a/jpeg_metadata_tk/XML.php b/jpeg_metadata_tk/XML.php deleted file mode 100644 index 2ce4668..0000000 --- a/jpeg_metadata_tk/XML.php +++ /dev/null @@ -1,396 +0,0 @@ - 1.10 : Changed read_xml_array_from_text to fix problem that -* caused the whitespace (especially newlines) to be -* destroyed when converting xml text to an xml array -* -* URL: http://electronics.ozhiker.com -* -* License: This file is part of the PHP JPEG Metadata Toolkit. -* -* The PHP JPEG Metadata Toolkit is free software; you can -* redistribute it and/or modify it under the terms of the -* GNU General Public License as published by the Free Software -* Foundation; either version 2 of the License, or (at your -* option) any later version. -* -* The PHP JPEG Metadata Toolkit is distributed in the hope -* that it will be useful, but WITHOUT ANY WARRANTY; without -* even the implied warranty of MERCHANTABILITY or FITNESS -* FOR A PARTICULAR PURPOSE. See the GNU General Public License -* for more details. -* -* You should have received a copy of the GNU General Public -* License along with the PHP JPEG Metadata Toolkit; if not, -* write to the Free Software Foundation, Inc., 59 Temple -* Place, Suite 330, Boston, MA 02111-1307 USA -* -* If you require a different license for commercial or other -* purposes, please contact the author: evan@ozhiker.com -* -******************************************************************************/ - -include_once 'Unicode.php'; // Unicode is required as XML is always Unicode encoded - - -/****************************************************************************** -* -* Function: read_xml_array_from_text -* -* Description: Parses a string containing XML, and returns the resulting -* tree structure array, which contains all the XML information. -* Note: White space and comments in the XML are ignored -* Note: All text information contained in the tree structure -* is encoded as Unicode UTF-8. Hence text will appear as -* normal ASCII except where there is an extended character. -* -* Parameters: xmltext - a string containing the XML to be parsed -* -* Returns: output - the tree structure array containing the XML information -* FALSE - if an error occured -* -******************************************************************************/ - -function read_xml_array_from_text( $xmltext ) -{ - // Check if there actually is any text to parse - if ( trim( $xmltext ) == "" ) - { - return FALSE; - } - - // Create an instance of a xml parser to parse the XML text - $xml_parser = xml_parser_create( "UTF-8" ); - - - // Change: Fixed problem that caused the whitespace (especially newlines) to be destroyed when converting xml text to an xml array, as of revision 1.10 - - // We would like to remove unneccessary white space, but this will also - // remove things like newlines ( ) in the XML values, so white space - // will have to be removed later - if ( xml_parser_set_option($xml_parser,XML_OPTION_SKIP_WHITE,0) == FALSE ) - { - // Error setting case folding - destroy the parser and return - xml_parser_free($xml_parser); - return FALSE; - } - - // to use XML code correctly we have to turn case folding - // (uppercasing) off. XML is case sensitive and upper - // casing is in reality XML standards violation - if ( xml_parser_set_option($xml_parser,XML_OPTION_CASE_FOLDING,0) == FALSE ) - { - // Error setting case folding - destroy the parser and return - xml_parser_free($xml_parser); - return FALSE; - } - - // Parse the XML text into a array structure - if ( xml_parse_into_struct($xml_parser, $xmltext, $vals, $index) == 0 ) - { - // Error Parsing XML - destroy the parser and return - xml_parser_free($xml_parser); - return FALSE; - } - - // Destroy the xml parser - xml_parser_free($xml_parser); - - - // Change: Fixed problem that caused the whitespace (especially newlines) to be destroyed when converting xml text to an xml array, as of revision 1.10 - - // Since the xml was processed with whitespace enabled, it will have many values which are - // only whitespace. These need to be removed to make a sensible array. - - $newvals = array( ); - - // Cycle through each of the items - foreach( $vals as $valno => $val ) - { - // If the item has a whitespace only value, remove it - if ( ( array_key_exists( 'value', $val ) ) && (trim( $val[ 'value' ] ) == "" ) ) - { - unset( $val[ 'value' ] ); - } - // If the item has a value (which will be non blank now) or is of type other than cdata, add it to the new array - if ( ( $val[ 'type' ] != 'cdata' ) || ( array_key_exists( 'value', $val ) ) ) - { - $newvals[] = $val; - } - - } - - // The xml_parse_into_struct function returns a flat version - // of the XML data, where each tag has a level number attached. - // This is very difficult to work with, so it needs to be - // converted to a tree structure before being returned - return xml_get_children($newvals, $i=0); - -} - -/****************************************************************************** -* End of Function: read_xml_array_from_text -******************************************************************************/ - - - - - -/****************************************************************************** -* -* Function: write_xml_array_to_text -* -* Description: Takes a tree structure array (in the same format as returned -* by read_xml_array_from_text, and constructs a string containing -* the equivalent XML. This function is recursive, and produces -* XML which has correct indents. -* Note: All text information contained in the tree structure -* can be either 7-bit ASCII or encoded as Unicode UTF-8, -* since UTF-8 passes 7-bit ASCII text unchanged. -* -* Parameters: xmlarray - the tree structure array containing the information to -* be converted to XML -* indentlevel - the indent level of the top level tags (usually zero) -* -* Returns: output - the string containing the equivalent XML -* FALSE - if an error occured -* -******************************************************************************/ - -function write_xml_array_to_text( $xmlarray, $indentlevel ) -{ - // Create a string to receive the XML - $output_xml_text = ""; - - - // Cycle through each xml element at this level - foreach ($xmlarray as $xml_elem) - { - - // Add the indent, then the cleaned tag name to the output - $output_xml_text .= str_repeat ( " ", $indentlevel ) . "<" . xml_UTF8_clean( $xml_elem['tag'] ); - - // Check if there are any attributes for this tag - if (array_key_exists('attributes',$xml_elem)) - { - // There are attributes - // Cycle through each attribute for this tag - foreach ($xml_elem['attributes'] as $xml_attr_name => $xml_attr_val) - { - // Add the cleaned attribute name, and cleaned attribute value to the output - $output_xml_text .= " ". xml_UTF8_clean( $xml_attr_name ) ." ='" . xml_UTF8_clean( $xml_attr_val ) ."'"; - } - } - - // Add the 'greater-than' to close this tag to the output - $output_xml_text .= ">"; - - // Check if this element has any text inside it. - if (array_key_exists('value',$xml_elem) ) - { - // There is text for this element - clean it and add it to the output - $output_xml_text .= xml_UTF8_clean( $xml_elem['value'] ); - } - - // Check if there are any lower levels contained by this element - if (array_key_exists('children',$xml_elem) ) - { - // There are sub-elements for this element - - // Add a newline to the output, so the sub-elements start on a fresh line - $output_xml_text .= "\n"; - - // Recursively call this function to output the sub-elements, and add the result to the output - $output_xml_text .= write_xml_array_to_text( $xml_elem['children'], $indentlevel + 1 ); - - // Add an indent to the output for the closing tag, since we are on a new line due to the sub-elements - $output_xml_text .= str_repeat ( " ", $indentlevel ); - } - - // Add the cleaned closing tag to the output - $output_xml_text .= "\n"; - } - - // Return the XML text - return $output_xml_text; -} - -/****************************************************************************** -* End of Function: write_xml_array_to_text -******************************************************************************/ - - - - - - - - - - - - - - - - - - - - - - - -/****************************************************************************** -* -* INTERNAL FUNCTIONS -* -******************************************************************************/ - - - - - - -/****************************************************************************** -* -* Internal Function: xml_get_children -* -* Description: Used by the read_xml_array_from_text function. -* This function recursively converts the values retrieved from -* the xml_parse_into_struct function into a tree structure array, -* which is much more useful and easier to use. -* -* Parameters: input_xml_array - the flat array of XML elements retrieved -* from xml_parse_into_struct -* $item_num - the number of the element at which the conversion -* should start (usually zero when called from another -* function, this is used for recursion) -* -* Returns: children - the tree structure array containing XML elements -* FALSE - if an error occured -* -******************************************************************************/ - -function xml_get_children( &$input_xml_array, &$item_num ) -{ - - // Make an array to receive the output XML tree structure - $children = array(); - - - // Cycle through all the elements of the input XML array - while ( $item_num < count( $input_xml_array ) ) - { - // Retrieve the current array element - $v = &$input_xml_array[ $item_num++ ]; - - // Check what type of XML array element this is, and process accordingly - - switch ( $v['type'] ) - { - case 'cdata': // This is a non parsed Character Data tag - case 'complete': // This is a pair of XML matching tags possibly with text (but no tags) inside - $children[] = xml_get_child( $v ); - break; - - case 'open': // This is a single opening tag - // Recursively get the children for this opening tag - $children[] = xml_get_child( $v, xml_get_children( $input_xml_array, $item_num ) ); - break; // This is a single opening tag - - case 'close': // This is a single closing tag - break 2; // leave "while" loop (and the function) - } - } - - // Return the results - return $children; -} - -/****************************************************************************** -* End of Function: xml_get_children -******************************************************************************/ - - -/****************************************************************************** -* -* Internal Function: xml_get_child -* -* Description: Used by the xml_get_children function. -* Takes an element from an array provided by xml_parse_into_struct -* and returns an element for a tree structure array -* -* Parameters: input_xml_item - the item from the array provided by xml_parse_into_struct -* children - an array of sub-elements to be added to the tree -* structure array. Null or missing value indicate no -* sub-elements are to be added. -* -* Returns: child - the element for a tree structure array -* FALSE - if an error occured -* -******************************************************************************/ - -function xml_get_child( &$input_xml_item, $children = NULL ) -{ - // Create an array to receive the child structure - $child = array(); - - // If the input item has the 'tag' element set, copy it to the child - if ( isset( $input_xml_item['tag'] ) ) - { - $child['tag'] = $input_xml_item['tag'] ; - } - - // If the input item has the 'value' element set, copy it to the child - if ( isset( $input_xml_item['value'] ) ) - { - $child['value'] = $input_xml_item['value'] ; - } - - // If the input item has the 'attributes' element set, copy it to the child - if ( isset( $input_xml_item['attributes'] ) ) - { - $child['attributes'] = $input_xml_item['attributes']; - } - - // If children have been specified, add them to the child - if ( is_array( $children ) ) - { - $child['children'] = $children; - } - - // Return the child structure - return $child; -} - -/****************************************************************************** -* End of Function: xml_get_children -******************************************************************************/ - - - - - - - - - - - - -?> \ No newline at end of file diff --git a/jpeg_metadata_tk/XMP.php b/jpeg_metadata_tk/XMP.php deleted file mode 100644 index 0948be1..0000000 --- a/jpeg_metadata_tk/XMP.php +++ /dev/null @@ -1,1063 +0,0 @@ - 1.04 : changed put_IPTC to fix a bug preventing the correct -* insertion of a XMP block where none existed previously -* -* 1.04 -> 1.10 : changed put_XMP_text to fix some array indexes which were missing qoutes -* -* URL: http://electronics.ozhiker.com -* -* Copyright: Copyright Evan Hunter 2004 -* -* License: This file is part of the PHP JPEG Metadata Toolkit. -* -* The PHP JPEG Metadata Toolkit is free software; you can -* redistribute it and/or modify it under the terms of the -* GNU General Public License as published by the Free Software -* Foundation; either version 2 of the License, or (at your -* option) any later version. -* -* The PHP JPEG Metadata Toolkit is distributed in the hope -* that it will be useful, but WITHOUT ANY WARRANTY; without -* even the implied warranty of MERCHANTABILITY or FITNESS -* FOR A PARTICULAR PURPOSE. See the GNU General Public License -* for more details. -* -* You should have received a copy of the GNU General Public -* License along with the PHP JPEG Metadata Toolkit; if not, -* write to the Free Software Foundation, Inc., 59 Temple -* Place, Suite 330, Boston, MA 02111-1307 USA -* -* If you require a different license for commercial or other -* purposes, please contact the author: evan@ozhiker.com -* -******************************************************************************/ - -include_once 'XML.php'; - - - - - -/****************************************************************************** -* -* Function: get_XMP_text -* -* Description: Retrieves the Extensible Metadata Platform (XMP) information -* from an App1 JPEG segment and returns the raw XML text as a -* string. This includes the Resource Description Framework (RDF) -* information and may include Dublin Core Metadata Initiative (DCMI) -* information. Uses information supplied by the get_jpeg_header_data -* function -* -* Parameters: jpeg_header_data - a JPEG header data array in the same format -* as from get_jpeg_header_data -* -* Returns: xmp_data - the string of raw XML text -* FALSE - if an APP 1 XMP segment could not be found, -* or if an error occured -* -******************************************************************************/ - -function get_XMP_text( $jpeg_header_data ) -{ - //Cycle through the header segments - for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) - { - // If we find an APP1 header, - if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP1" ) == 0 ) - { - // And if it has the Adobe XMP/RDF label (http://ns.adobe.com/xap/1.0/\x00) , - if( strncmp ( $jpeg_header_data[$i]['SegData'], "http://ns.adobe.com/xap/1.0/\x00", 29) == 0 ) - { - // Found a XMP/RDF block - // Return the XMP text - $xmp_data = substr ( $jpeg_header_data[$i]['SegData'], 29 ); - - return $xmp_data; - } - } - } - return FALSE; -} - -/****************************************************************************** -* End of Function: get_XMP_text -******************************************************************************/ - - - - - - -/****************************************************************************** -* -* Function: put_XMP_text -* -* Description: Adds or modifies the Extensible Metadata Platform (XMP) information -* in an App1 JPEG segment. If a XMP segment already exists, it is -* replaced, otherwise a new one is inserted, using the supplied data. -* Uses information supplied by the get_jpeg_header_data function -* -* Parameters: jpeg_header_data - a JPEG header data array in the same format -* as from get_jpeg_header_data -* newXMP - a string containing the XMP text to be stored in the XMP -* segment. Should be constructed using the write_XMP_array_to_text -* function -* -* Returns: jpeg_header_data - the JPEG header data array with the -* XMP segment added. -* FALSE - if an error occured -* -******************************************************************************/ - -function put_XMP_text( $jpeg_header_data, $newXMP ) -{ - //Cycle through the header segments - for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) - { - // If we find an APP1 header, - if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP1" ) == 0 ) - { - // And if it has the Adobe XMP/RDF label (http://ns.adobe.com/xap/1.0/\x00) , - if( strncmp ( $jpeg_header_data[$i]['SegData'], "http://ns.adobe.com/xap/1.0/\x00", 29) == 0 ) - { - // Found a preexisting XMP/RDF block - Replace it with the new one and return. - $jpeg_header_data[$i]['SegData'] = "http://ns.adobe.com/xap/1.0/\x00" . $newXMP; - return $jpeg_header_data; - } - } - } - - // No pre-existing XMP/RDF found - insert a new one after any pre-existing APP0 or APP1 blocks - // Change: changed to initialise $i properly as of revision 1.04 - $i = 0; - // Loop until a block is found that isn't an APP0 or APP1 - while ( ( $jpeg_header_data[$i]['SegName'] == "APP0" ) || ( $jpeg_header_data[$i]['SegName'] == "APP1" ) ) - { - $i++; - } - - - - // Insert a new XMP/RDF APP1 segment at the specified point. - // Change: changed to properly construct array element as of revision 1.04 - requires two array statements not one, requires insertion at $i, not $i - 1 - array_splice($jpeg_header_data, $i, 0, array( array( "SegType" => 0xE1, - "SegName" => "APP1", - "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE1 ], - "SegData" => "http://ns.adobe.com/xap/1.0/\x00" . $newXMP ) ) ); - - // Return the headers with the new segment inserted - return $jpeg_header_data; -} - -/****************************************************************************** -* End of Function: put_XMP_text -******************************************************************************/ - - - - - - -/****************************************************************************** -* -* Function: read_XMP_array_from_text -* -* Description: An alias for read_xml_array_from_text. -* Parses a string containing XMP data (XML), and returns the resulting -* tree structure array, which contains all the XMP (XML) information. -* Note: White space and comments in the XMP data (XML) are ignored -* Note: All text information contained in the tree structure -* is encoded as Unicode UTF-8. Hence text will appear as -* normal ASCII except where there is an extended character. -* -* Parameters: xmptext - a string containing the XMP data (XML) to be parsed -* -* Returns: output - the tree structure array containing the XMP (XML) information -* FALSE - if an error occured -* -******************************************************************************/ - -function read_XMP_array_from_text( $xmptext ) -{ - return read_xml_array_from_text( $xmptext ); -} - -/****************************************************************************** -* End of Function: read_XMP_array_from_text -******************************************************************************/ - - - - - -/****************************************************************************** -* -* Function: write_XMP_array_to_text -* -* Description: Takes a tree structure array containing XMP (in the same format -* as returned by read_XMP_array_from_text, and constructs a string -* containing the equivalent XMP, including the XMP Packet header -* and trailer. Produces XMP text which has correct indents, encoded -* using UTF-8. -* Note: All text information contained in the tree structure -* can be either 7-bit ASCII or encoded as Unicode UTF-8, -* since UTF-8 passes 7-bit ASCII text unchanged. -* -* Parameters: xmparray - the tree structure array containing the information to -* be converted to XMP text -* -* Returns: output_XMP_text - the string containing the equivalent XMP text -* -******************************************************************************/ - -function write_XMP_array_to_text( $xmparray ) -{ - // Add the XMP packet header - // The sequence 0xEFBBBF is the UTF-8 encoded version of the Unicode “zero - // width non-breaking space character” (U+FEFF), which is used for detecting - // whether UTF-16 or UTF-8 is being used. - $output_XMP_text = "\n"; - - // Photoshop Seems to add this, but there doesn't appear to be - // any information on what it means - // TODO : XMP, Find out what the adobe-xap-filters tag means - $output_XMP_text .= "\n"; - - // Add the XML text - $output_XMP_text .= write_xml_array_to_text( $xmparray, 0 ); - - - // The XMP standard recommends adding 2-4k of white space at the - // end for in place editing, so we will add it to the XML now - $output_XMP_text .= str_repeat(" \n", 30); - - // Add the XMP packet trailer - $output_XMP_text .= ""; - - // Return the resulting XMP text - return $output_XMP_text; -} - -/****************************************************************************** -* End of Function: write_XMP_array_to_text -******************************************************************************/ - - - - - - -/****************************************************************************** -* -* Function: Interpret_XMP_to_HTML -* -* Description: Generates html showing the information contained in an Extensible -* Metadata Platform (XMP) tree structure array, as retrieved -* with read_XMP_array_from_text -* -* Parameters: XMP_array - a XMP tree structure array as from read_XMP_array_from_text -* -* Returns: output - the HTML string -* -******************************************************************************/ - -function Interpret_XMP_to_HTML( $XMP_array ) -{ - // Create a string to receive the output html - $output =""; - - // Check if the XMP tree structure array is valid - if ( $XMP_array !== FALSE ) - { - // Check if there is a rdf:RDF tag at either the first or second level - if ( ( $XMP_array[0]['tag'] == "x:xapmeta" ) && ( $XMP_array[0]['children'][0]['tag'] == "rdf:RDF" ) ) - { - // RDF found at second level - Save it's position - $RDF_Contents = &$XMP_array[0]['children'][0]['children']; - } - else if ( ( $XMP_array[0]['tag'] == "x:xmpmeta" ) && ( $XMP_array[0]['children'][0]['tag'] == "rdf:RDF" ) ) - { - // RDF found at second level - Save it's position - $RDF_Contents = &$XMP_array[0]['children'][0]['children']; - } - else if ( $XMP_array[0]['tag'] == "rdf:RDF" ) - { - // RDF found at first level - Save it's position - $RDF_Contents = &$XMP_array[0]['children']; - } - else - { - // RDF section not found - abort - return ""; - } - - // Add heading to html output - $output .= "

    Contains Extensible Metadata Platform (XMP) / Resource Description Framework (RDF) Information

    \n"; - - // Cycle through each of the items in the RDF tree array, and process them - foreach ($RDF_Contents as $RDF_Item) - { - // Check if the item is a rdf:Description tag - these are the only ones that can be processed - - if ( ( $RDF_Item['tag'] == "rdf:Description" ) && ( array_key_exists( 'children', $RDF_Item ) ) ) - { - // Item is a rdf:Description tag. - - // Cycle through each of the attributes for this tag, looking - // for a xmlns: attribute, which tells us what Namespace the - // sub-items will be in. - foreach( $RDF_Item['attributes'] as $key => $val ) - { - // Check for the xmlns: namespace attribute - if ( substr( $key,0,6) == "xmlns:" ) - { - // Found a xmlns: attribute - // Extract the namespace string - // Add heading to the HTML according to which Namespace the RDF items have - switch ( substr( $key,6) ) - { - case "photoshop": - $output .= "

    Photoshop RDF Segment

    \n"; - break; - case "xapBJ": - $output .= "

    Basic Job Ticket RDF Segment

    \n"; - break; - case "xapMM": - $output .= "

    Media Management RDF Segment

    \n"; - break; - case "xapRights": - $output .= "

    Rights Management RDF Segment

    \n"; - break; - case "dc": - $output .= "

    Dublin Core Metadata Initiative RDF Segment

    \n"; - break; - case "xmp": - case "xap": - $output .= "

    XMP Basic Segment

    \n"; - break; - case "xmpTPg": - $output .= "

    XMP Paged-Text Segment

    \n"; - break; - case "xmpTPg": - $output .= "

    Adobe PDF Segment

    \n"; - break; - case "tiff": - $output .= "

    XMP - embedded TIFF Segment

    \n"; - break; - case "exif": - $output .= "

    XMP - embedded EXIF Segment

    \n"; - break; - case "xapGImg": // Sub Category - Do nothing - break; - case "stDim": // Sub Category - Do nothing - break; - case "stEvt": // Sub Category - Do nothing - break; - case "stRef": // Sub Category - Do nothing - break; - case "stVer": // Sub Category - Do nothing - break; - case "stJob": // Sub Category - Do nothing - break; - - default: - $output .= "

    Unknown RDF Segment '" . substr( $key,6) . "'

    \n"; - break; - } - - - } - - } - - // Add the start of the table to the HTML output - $output .= "\n\n"; - - - // Check if this element has sub-items - - if ( array_key_exists( 'children', $RDF_Item ) ) - { - - // Cycle through each of the sub-items - foreach( $RDF_Item['children'] as $child_item ) - { - // Get an interpretation of the sub-item's caption and value - list($tag_caption, $value_str) = Interpret_RDF_Item( $child_item ); - - // Escape the text of the caption for html - $tag_caption = HTML_UTF8_Escape( $tag_caption ); - // Escape the text of the value for html and turn newlines to
    - $value_str = nl2br( HTML_UTF8_Escape( $value_str ) ); - - // Check if the value is empty - if it is, put a no-break-space in - // to ensure the table cell gets drawn - if ( $value_str == "" ) - { - $value_str = " "; - } - // Add the table row to the output html - $output .= "\n"; - } - } - - // Add the end of the table to the html - $output .= "\n
    " . $tag_caption . ":" . $value_str . "
    \n"; - - - } - else - { - // Don't know how to process tags other than rdf:Description - do nothing - } - } - - - - } - // Return the resulting HTML - return $output; -} - -/****************************************************************************** -* End of Function: Interpret_XMP_to_HTML -******************************************************************************/ - - - - - - - - - - - - - - - - - - -/****************************************************************************** -* -* INTERNAL FUNCTIONS -* -******************************************************************************/ - - - - - - - - - - - - -/****************************************************************************** -* -* Internal Function: Interpret_RDF_Item -* -* Description: Used by Interpret_XMP_to_HTML -* Used by get_RDF_field_html_value -* Used by interpret_RDF_collection -* Generates a caption and text representation of the value of a -* particular RDF item. -* -* Parameters: Item - The RDF item to evaluate -* -* Returns: tag_caption - the caption of the tag -* value_str - the text representation of the value -* -******************************************************************************/ - -function Interpret_RDF_Item( $Item ) -{ - - // TODO: Many RDF items have not been tested - only photoshop 7.0 and CS items - - // Create a string to receive the HTML output - $value_str = ""; - - // Check if the item has is in the lookup table of tag captions - if ( array_key_exists( $Item['tag'], $GLOBALS[ 'XMP_tag_captions' ] ) ) - { - // Item is in list of captions, get the caption - $tag_caption = $GLOBALS[ 'XMP_tag_captions' ][ $Item['tag'] ]; - } - else - { - // Item has no caption - make one - $tag_caption = "Unknown field " . $Item['tag']; - } - - - // Process specially the item according to it's tag - switch ( $Item['tag'] ) - { - - case "photoshop:DateCreated": // This is in year month day order - // Extract the year,month and day - list( $year, $month, $day ) = sscanf( $Item['value'], "%d-%d-%d" ); - // Make a new date string with Day, Month, Year - $value_str = "$day/$month/$year"; - break; - - default : - $value_str = get_RDF_field_html_value( $Item ); - break; - } - - - - - // Return the captiona and value - return array($tag_caption, $value_str); -} - - -/****************************************************************************** -* End of Function: Interpret_RDF_Item -******************************************************************************/ - - - - - -/****************************************************************************** -* -* Internal Function: get_RDF_field_html_value -* -* Description: Attempts to build a text representation of the value of an RDF -* item. This includes handling any collections or sub-resources. -* -* Parameters: rdf_item - The RDF item to evaluate -* -* Returns: output_str - the text representation of the field value -* -******************************************************************************/ - -function get_RDF_field_html_value( $rdf_item ) -{ - // Create a string to receive the output text - $output_str = ""; - - // Check if the item has a value - if ( array_key_exists( 'value', $rdf_item ) ) - { - // The item does have a value - add it to the text - $output_str .= $rdf_item['value']; - } - - // Check if the item has any attributes - if ( array_key_exists( 'attributes', $rdf_item ) ) - { - // Cycle through each of the attributes - foreach( $rdf_item['attributes'] as $key => $val ) - { - // Check if this attribute is rdf:parseType = 'Resource' i.e. a sub-resource indicator - if ( ( $key == "rdf:parseType" ) && ( $val == "Resource" ) ) - { - // This item has a attribute indicating sub-resources - // Check that the item has sub items - if ( array_key_exists( 'children', $rdf_item ) ) - { - // The item does have sub-items, - // Cycle through each, Interpreting them and adding the result to the output text - foreach( $rdf_item['children'] as $child ) - { - list($tag_caption, $value_str) = Interpret_RDF_Item( $child ); - $output_str .= "$tag_caption = $value_str\n"; - } - // The output text will have an extra \n on it - remove it - $output_str = rtrim( $output_str ); - } - } - } - } - // If the item did not have sub-resources, it may still have sub-items - check for this - else if ( array_key_exists( 'children', $rdf_item ) ) - { - // Non-resource Sub-items found, Cycle through each - foreach( $rdf_item['children'] as $child_item ) - { - // Check if this sub-item has a tag - if ( array_key_exists( 'tag', $child_item ) ) - { - // Sub item has a tag, Process it according to the tag - switch ( $child_item[ 'tag' ] ) - { - // Collections - case "rdf:Alt": - $output_str .= "List of Alternates:\n"; - $output_str .= interpret_RDF_collection( $child_item ); - break; - - case "rdf:Bag": - $output_str .= "Unordered List:\n"; - $output_str .= interpret_RDF_collection( $child_item ); - break; - - case "rdf:Seq": - $output_str .= "Ordered List:\n"; - $output_str .= interpret_RDF_collection( $child_item ); - break; - - // Sub-Resource - case "rdf:Description": - // Check that the item has sub items - if ( array_key_exists( 'children', $child_item ) ) - { - // The item does have sub-items, - // Cycle through each, Interpreting them and adding the result to the output text - foreach( $child_item['children'] as $child ) - { - list($tag_caption, $value_str) = Interpret_RDF_Item( $child ); - $output_str .= "$tag_caption = $value_str\n"; - } - // The output text will have an extra \n on it - remove it - $output_str = rtrim( $output_str ); - } - break; - - // Other - default: - $output_str .= "Unknown Sub Item type:". $child_item[ 'tag' ]. "\n"; - break; - } - } // sub-item Has no tags, look for a value - else if ( array_key_exists( 'value', $child_item ) ) - { - $output_str .= $rdf_item['value'] . "\n"; - } - else - { - // no info - do nothing - } - - } - } - - // return the resulting value string - return $output_str; -} - -/****************************************************************************** -* End of Function: get_RDF_field_html_value -******************************************************************************/ - - - - - - - - -/****************************************************************************** -* -* Internal Function: interpret_RDF_collection -* -* Description: Attempts to build a text representation of the value of an RDF -* collection item. This includes handling any sub-collections or -* sub-resources. -* -* Parameters: rdf_item - The RDF collection item to evaluate -* -* Returns: output_str - the text representation of the collection value -* -******************************************************************************/ - -function interpret_RDF_collection( $item ) -{ - // Create a string to receive the output - $output_str = ""; - - // Check if the collection item has sub-items - if ( array_key_exists( 'children', $item ) ) - { - - // Cycle through each of the sub-items - foreach( $item['children'] as $list_item ) - { - // Check that the sub item has a tag, and don't process it if it doesn't - if ( ! array_key_exists( 'tag', $list_item ) ) - { - continue 1; - } - - // Check that the sub-item tag is either rdf:li or rdf:_1 .... - // This signifies it is a list item of the collection - if ( ( $list_item['tag'] == "rdf:li" ) || - ( preg_match ( "rdf:_\d+", $list_item['tag'] ) == 1 ) ) - { - // A List item has been found - // Check if there are sub-resources, - // starting by checking if there are attributes - if ( array_key_exists( 'attributes', $list_item ) ) - { - // Cycle through each of the attributes - foreach( $list_item['attributes'] as $key => $val ) - { - // Check if this attribute is rdf:parseType = 'Resource' i.e. a sub-resource indicator - if ( ( $key == "rdf:parseType" ) && ( $val == "Resource" ) ) - { - // This item has a attribute indicating sub-resources - // Check that the item has sub items - if ( array_key_exists( 'children', $list_item ) ) - { - // The item does have sub-items, - // Cycle through each, Interpreting them and adding the result to the output text - foreach( $list_item['children'] as $child ) - { - list($tag_caption, $value_str) = Interpret_RDF_Item( $child ); - $output_str .= "$tag_caption = $value_str\n"; - } - // The output text will have an extra \n on it - remove it - $output_str = rtrim( $output_str ); - } - } - } - } - - // Check if the list item has a value - if ( array_key_exists( 'value', $list_item ) ) - { - // Value found, add it to the output - $output_str .= get_RDF_field_html_value( $list_item ) . "\n"; - } - - } - } - // The list of sub-items formed will have a trailing \n, remove it. - $output_str = rtrim( $output_str ); - - } - else - { - // No sub-items in collection - can't do anything - } - - // Return the output value - return $output_str; - -} - -/****************************************************************************** -* End of Function: interpret_RDF_collection -******************************************************************************/ - - - - - - - - - - - - - - - -/****************************************************************************** -* Global Variable: XMP_tag_captions -* -* Contents: The Captions of the known XMP fields, indexed by their field name -* -******************************************************************************/ - -$GLOBALS[ 'XMP_tag_captions' ] = array ( - -"dc:contributor" => "Other Contributor(s)", -"dc:coverage" => "Coverage (scope)", -"dc:creator" => "Creator(s) (Authors)", -"dc:date" => "Date", -"dc:description" => "Description (Caption)", -"dc:format" => "MIME Data Format", -"dc:identifier" => "Unique Resource Identifer", -"dc:language" => "Language(s)", -"dc:publisher" => "Publisher(s)", -"dc:relation" => "Relations to other documents", -"dc:rights" => "Rights Statement", -"dc:source" => "Source (from which this Resource is derived)", -"dc:subject" => "Subject and Keywords", -"dc:title" => "Title", -"dc:type" => "Resource Type", - -"xmp:Advisory" => "Externally Editied Properties", -"xmp:BaseURL" => "Base URL for relative URL's", -"xmp:CreateDate" => "Original Creation Date", -"xmp:CreatorTool" => "Creator Tool", -"xmp:Identifier" => "Identifier(s)", -"xmp:MetadataDate" => "Metadata Last Modify Date", -"xmp:ModifyDate" => "Resource Last Modify Date", -"xmp:Nickname" => "Nickname", -"xmp:Thumbnails" => "Thumbnails", - -"xmpidq:Scheme" => "Identification Scheme", - -// These are not in spec but Photoshop CS seems to use them -"xap:Advisory" => "Externally Editied Properties", -"xap:BaseURL" => "Base URL for relative URL's", -"xap:CreateDate" => "Original Creation Date", -"xap:CreatorTool" => "Creator Tool", -"xap:Identifier" => "Identifier(s)", -"xap:MetadataDate" => "Metadata Last Modify Date", -"xap:ModifyDate" => "Resource Last Modify Date", -"xap:Nickname" => "Nickname", -"xap:Thumbnails" => "Thumbnails", -"xapidq:Scheme" => "Identification Scheme", - - -"xapRights:Certificate" => "Certificate", -"xapRights:Copyright" => "Copyright", -"xapRights:Marked" => "Marked", -"xapRights:Owner" => "Owner", -"xapRights:UsageTerms" => "Legal Terms of Usage", -"xapRights:WebStatement" => "Web Page describing rights statement (Owner URL)", - -"xapMM:ContainedResources" => "Contained Resources", -"xapMM:ContributorResources" => "Contributor Resources", -"xapMM:DerivedFrom" => "Derived From", -"xapMM:DocumentID" => "Document ID", -"xapMM:History" => "History", -"xapMM:LastURL" => "Last Written URL", -"xapMM:ManagedFrom" => "Managed From", -"xapMM:Manager" => "Asset Management System", -"xapMM:ManageTo" => "Manage To", -"xapMM:xmpMM:ManageUI" => "Managed Resource URI", -"xapMM:ManagerVariant" => "Particular Variant of Asset Management System", -"xapMM:RenditionClass" => "Rendition Class", -"xapMM:RenditionParams" => "Rendition Parameters", -"xapMM:RenditionOf" => "Rendition Of", -"xapMM:SaveID" => "Save ID", -"xapMM:VersionID" => "Version ID", -"xapMM:Versions" => "Versions", - -"xapBJ:JobRef" => "Job Reference", - -"xmpTPg:MaxPageSize" => "Largest Page Size", -"xmpTPg:NPages" => "Number of pages", - -"pdf:Keywords" => "Keywords", -"pdf:PDFVersion" => "PDF file version", -"pdf:Producer" => "PDF Creation Tool", - -"photoshop:AuthorsPosition" => "Authors Position", -"photoshop:CaptionWriter" => "Caption Writer", -"photoshop:Category" => "Category", -"photoshop:City" => "City", -"photoshop:Country" => "Country", -"photoshop:Credit" => "Credit", -"photoshop:DateCreated" => "Creation Date", -"photoshop:Headline" => "Headline", -"photoshop:History" => "History", // Not in XMP spec -"photoshop:Instructions" => "Instructions", -"photoshop:Source" => "Source", -"photoshop:State" => "State", -"photoshop:SupplementalCategories" => "Supplemental Categories", -"photoshop:TransmissionReference" => "Technical (Transmission) Reference", -"photoshop:Urgency" => "Urgency", - - -"tiff:ImageWidth" => "Image Width", -"tiff:ImageLength" => "Image Length", -"tiff:BitsPerSample" => "Bits Per Sample", -"tiff:Compression" => "Compression", -"tiff:PhotometricInterpretation" => "Photometric Interpretation", -"tiff:Orientation" => "Orientation", -"tiff:SamplesPerPixel" => "Samples Per Pixel", -"tiff:PlanarConfiguration" => "Planar Configuration", -"tiff:YCbCrSubSampling" => "YCbCr Sub-Sampling", -"tiff:YCbCrPositioning" => "YCbCr Positioning", -"tiff:XResolution" => "X Resolution", -"tiff:YResolution" => "Y Resolution", -"tiff:ResolutionUnit" => "Resolution Unit", -"tiff:TransferFunction" => "Transfer Function", -"tiff:WhitePoint" => "White Point", -"tiff:PrimaryChromaticities" => "Primary Chromaticities", -"tiff:YCbCrCoefficients" => "YCbCr Coefficients", -"tiff:ReferenceBlackWhite" => "Black & White Reference", -"tiff:DateTime" => "Date & Time", -"tiff:ImageDescription" => "Image Description", -"tiff:Make" => "Make", -"tiff:Model" => "Model", -"tiff:Software" => "Software", -"tiff:Artist" => "Artist", -"tiff:Copyright" => "Copyright", - - -"exif:ExifVersion" => "Exif Version", -"exif:FlashpixVersion" => "Flash pix Version", -"exif:ColorSpace" => "Color Space", -"exif:ComponentsConfiguration" => "Components Configuration", -"exif:CompressedBitsPerPixel" => "Compressed Bits Per Pixel", -"exif:PixelXDimension" => "Pixel X Dimension", -"exif:PixelYDimension" => "Pixel Y Dimension", -"exif:MakerNote" => "Maker Note", -"exif:UserComment" => "User Comment", -"exif:RelatedSoundFile" => "Related Sound File", -"exif:DateTimeOriginal" => "Date & Time of Original", -"exif:DateTimeDigitized" => "Date & Time Digitized", -"exif:ExposureTime" => "Exposure Time", -"exif:FNumber" => "F Number", -"exif:ExposureProgram" => "Exposure Program", -"exif:SpectralSensitivity" => "Spectral Sensitivity", -"exif:ISOSpeedRatings" => "ISO Speed Ratings", -"exif:OECF" => "Opto-Electronic Conversion Function", -"exif:ShutterSpeedValue" => "Shutter Speed Value", -"exif:ApertureValue" => "Aperture Value", -"exif:BrightnessValue" => "Brightness Value", -"exif:ExposureBiasValue" => "Exposure Bias Value", -"exif:MaxApertureValue" => "Max Aperture Value", -"exif:SubjectDistance" => "Subject Distance", -"exif:MeteringMode" => "Metering Mode", -"exif:LightSource" => "Light Source", -"exif:Flash" => "Flash", -"exif:FocalLength" => "Focal Length", -"exif:SubjectArea" => "Subject Area", -"exif:FlashEnergy" => "Flash Energy", -"exif:SpatialFrequencyResponse" => "Spatial Frequency Response", -"exif:FocalPlaneXResolution" => "Focal Plane X Resolution", -"exif:FocalPlaneYResolution" => "Focal Plane Y Resolution", -"exif:FocalPlaneResolutionUnit" => "Focal Plane Resolution Unit", -"exif:SubjectLocation" => "Subject Location", -"exif:SensingMethod" => "Sensing Method", -"exif:FileSource" => "File Source", -"exif:SceneType" => "Scene Type", -"exif:CFAPattern" => "Colour Filter Array Pattern", -"exif:CustomRendered" => "Custom Rendered", -"exif:ExposureMode" => "Exposure Mode", -"exif:WhiteBalance" => "White Balance", -"exif:DigitalZoomRatio" => "Digital Zoom Ratio", -"exif:FocalLengthIn35mmFilm" => "Focal Length In 35mm Film", -"exif:SceneCaptureType" => "Scene Capture Type", -"exif:GainControl" => "Gain Control", -"exif:Contrast" => "Contrast", -"exif:Saturation" => "Saturation", -"exif:Sharpness" => "Sharpness", -"exif:DeviceSettingDescription" => "Device Setting Description", -"exif:SubjectDistanceRange" => "Subject Distance Range", -"exif:ImageUniqueID" => "Image Unique ID", -"exif:GPSVersionID" => "GPS Version ID", -"exif:GPSLatitude" => "GPS Latitude", -"exif:GPSLongitude" => "GPS Longitude", -"exif:GPSAltitudeRef" => "GPS Altitude Reference", -"exif:GPSAltitude" => "GPS Altitude", -"exif:GPSTimeStamp" => "GPS Time Stamp", -"exif:GPSSatellites" => "GPS Satellites", -"exif:GPSStatus" => "GPS Status", -"exif:GPSMeasureMode" => "GPS Measure Mode", -"exif:GPSDOP" => "GPS Degree Of Precision", -"exif:GPSSpeedRef" => "GPS Speed Reference", -"exif:GPSSpeed" => "GPS Speed", -"exif:GPSTrackRef" => "GPS Track Reference", -"exif:GPSTrack" => "GPS Track", -"exif:GPSImgDirectionRef" => "GPS Image Direction Reference", -"exif:GPSImgDirection" => "GPS Image Direction", -"exif:GPSMapDatum" => "GPS Map Datum", -"exif:GPSDestLatitude" => "GPS Destination Latitude", -"exif:GPSDestLongitude" => "GPS Destnation Longitude", -"exif:GPSDestBearingRef" => "GPS Destination Bearing Reference", -"exif:GPSDestBearing" => "GPS Destination Bearing", -"exif:GPSDestDistanceRef" => "GPS Destination Distance Reference", -"exif:GPSDestDistance" => "GPS Destination Distance", -"exif:GPSProcessingMethod" => "GPS Processing Method", -"exif:GPSAreaInformation" => "GPS Area Information", -"exif:GPSDifferential" => "GPS Differential", - -"stDim:w" => "Width", -"stDim:h" => "Height", -"stDim:unit" => "Units", - -"xapGImg:height" => "Height", -"xapGImg:width" => "Width", -"xapGImg:format" => "Format", -"xapGImg:image" => "Image", - -"stEvt:action" => "Action", -"stEvt:instanceID" => "Instance ID", -"stEvt:parameters" => "Parameters", -"stEvt:softwareAgent" => "Software Agent", -"stEvt:when" => "When", - -"stRef:instanceID" => "Instance ID", -"stRef:documentID" => "Document ID", -"stRef:versionID" => "Version ID", -"stRef:renditionClass" => "Rendition Class", -"stRef:renditionParams" => "Rendition Parameters", -"stRef:manager" => "Asset Management System", -"stRef:managerVariant" => "Particular Variant of Asset Management System", -"stRef:manageTo" => "Manage To", -"stRef:manageUI" => "Managed Resource URI", - -"stVer:comments" => "", -"stVer:event" => "", -"stVer:modifyDate" => "", -"stVer:modifier" => "", -"stVer:version" => "", - - - -"stJob:name" => "Job Name", -"stJob:id" => "Unique Job ID", -"stJob:url" => "URL for External Job Management File", - -// Exif Flash -"exif:Fired" => "Fired", -"exif:Return" => "Return", -"exif:Mode" => "Mode", -"exif:Function" => "Function", -"exif:RedEyeMode" => "Red Eye Mode", - -// Exif OECF/SFR -"exif:Columns" => "Columns", -"exif:Rows" => "Rows", -"exif:Names" => "Names", -"exif:Values" => "Values", - -// Exif CFAPattern -"exif:Columns" => "Columns", -"exif:Rows" => "Rows", -"exif:Values" => "Values", - - -// Exif DeviceSettings -"exif:Columns" => "Columns", -"exif:Rows" => "Rows", -"exif:Settings" => "Settings", - - - -); - -/****************************************************************************** -* End of Global Variable: XMP_tag_captions -******************************************************************************/ - - -?> \ No newline at end of file diff --git a/jpeg_metadata_tk/documentation/Camera_List_1.0.pdf b/jpeg_metadata_tk/documentation/Camera_List_1.0.pdf deleted file mode 100644 index 711e2a7..0000000 Binary files a/jpeg_metadata_tk/documentation/Camera_List_1.0.pdf and /dev/null differ diff --git a/jpeg_metadata_tk/documentation/Edit_File_Info_Example.php b/jpeg_metadata_tk/documentation/Edit_File_Info_Example.php deleted file mode 100644 index b8a9f24..0000000 --- a/jpeg_metadata_tk/documentation/Edit_File_Info_Example.php +++ /dev/null @@ -1,248 +0,0 @@ - - - - - - - - - - - No image filename defined\n"; - echo "\n"; - echo "\n"; - echo "

    No image filename defined - use GET method with field: jpeg_fname

    \n"; - echo "

    PHP JPEG Metadata Toolkit version " . $GLOBALS['Toolkit_Version'] . ", Copyright (C) 2004 Evan Hunter

    \n"; // Change: displayed toolkit version numbers to reference Toolkit_Version.php - as of version 1.11 - echo "\n"; - exit( ); - - } - else - { - $filename = $GLOBALS['HTTP_GET_VARS']['jpeg_fname']; - } - ?> - - - Edit Photoshop File Info details for <?php $filename ?> - - - -

    Powered by: PHP JPEG Metadata Toolkit version , Copyright (C) 2004 Evan Hunter

    -
    -
    - - - Edit Photoshop File Info details for $filename"; - - // Output a link to display the full metadata - echo "

    View Full Metatdata Information

    \n"; - - - // Display a small copy of the image - echo "

    "; - - // Define defaults for the fields - These are only used where the image has blank fields - $default_ps_file_info_array = array ( - 'title' => "", - 'author' => "Evan Hunter", - 'authorsposition' => "", - 'caption' => "", - 'captionwriter' => "Evan Hunter", - 'jobname' => "", - 'copyrightstatus' => "Copyrighted Work", - 'copyrightnotice' => "Copyright (c) Evan Hunter 2004", - 'ownerurl' => "http://www.ozhiker.com", - 'keywords' => array(), - 'category' => "", - 'supplementalcategories'=> array(), - 'date' => "", - 'city' => "", - 'state' => "Tasmania", - 'country' => "Australia", - 'credit' => "Evan Hunter", - 'source' => "Evan Hunter", - 'headline' => "", - 'instructions' => "", - 'transmissionreference' => "", - 'urgency' => "" - ); - - // outputfilename must always be defined, as it specifies the - // file which will be changed - - // These two lines create a temporary copy of the file - // which will be the one that is edited, keeping - // the original intact. - This would not be required if you wanted - // to change the original - in that case just set $outputfilename = $filename - $outputfilename = get_next_filename( ); - copy( $filename, $outputfilename ); - - - - - // Include the File Info Editor. - - include "Edit_File_Info.php"; - - ?> - - - - - - - - - - - - - - - - - - - - - - - - 'z' ) ) - { - $filename = 'a'; - } - - - // Check if the names are up to 'z' - if( $filename == 'z' ) - { - // Name is at z - the next one should be 'a' - $new_filename = 'a'; - } - else - { - // The name is not 'z' add one to it to get the next value - $new_filename = chr( ord( $filename ) + 1 ); - } - - // Write the next temp file letter back into the file - $Fhnd = fopen ("next_temp_file.dat", "w"); - fwrite ($Fhnd, $new_filename); - fclose ($Fhnd); - - // return the filename - return "temp_$filename.jpg"; -} - -/****************************************************************************** -* End of Function: get_next_filename -******************************************************************************/ - - - -?> \ No newline at end of file diff --git a/jpeg_metadata_tk/documentation/Example.php b/jpeg_metadata_tk/documentation/Example.php deleted file mode 100644 index 86ac497..0000000 --- a/jpeg_metadata_tk/documentation/Example.php +++ /dev/null @@ -1,218 +0,0 @@ - - - - - - - - - - - No image filename defined\n"; - echo "\n"; - echo "\n"; - echo "

    No image filename defined - use GET method with field: jpeg_fname

    \n"; - echo "

    PHP JPEG Metadata Toolkit version " . $GLOBALS['Toolkit_Version'] . ", Copyright (C) 2004 Evan Hunter

    \n"; // Change: displayed toolkit version numbers to reference Toolkit_Version.php - as of version 1.11 - echo "\n"; - exit( ); - } - else - { - $filename = $GLOBALS['HTTP_GET_VARS']['jpeg_fname']; - } - - - // Output the title - echo "Metadata details for $filename"; - - // Retrieve the header information - $jpeg_header_data = get_jpeg_header_data( $filename ); - - ?> - - - - - -

    Interpreted using: PHP JPEG Metadata Toolkit version , Copyright (C) 2004 Evan Hunter

    -
    -
    - -

    Metadata for ""

    -
    - - - -

    " >Click here to edit the Photoshop File Info for this file

    -
    - - - - - - -
    -
    -
    - - - - -
    -
    -
    - - - - -
    -
    -
    - - - - -
    -
    -
    - - - - -
    -
    -
    - - - - -
    -
    -
    - - - - -
    -
    -
    - - - - -
    -
    -
    - - - - -
    -
    -
    - - - - -
    -
    -
    - - - -

    Original Image

    - "; ?> - - -
    -
    -
    -

    Interpreted using:

    -

    PHP JPEG Metadata Toolkit version , Copyright (C) 2004 Evan Hunter

    - - - - \ No newline at end of file diff --git a/jpeg_metadata_tk/documentation/TIFFExample.php b/jpeg_metadata_tk/documentation/TIFFExample.php deleted file mode 100644 index 873d18f..0000000 --- a/jpeg_metadata_tk/documentation/TIFFExample.php +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - - - - - No image filename defined\n"; - echo "\n"; - echo "\n"; - echo "

    No image filename defined - use GET method with field: tiff_fname

    \n"; - echo "

    PHP JPEG Metadata Toolkit version " . $GLOBALS['Toolkit_Version'] . ", Copyright (C) 2004 Evan Hunter

    \n"; // Change: displayed toolkit version numbers to reference Toolkit_Version.php - as of version 1.11 - echo "\n"; - exit( ); - } - else - { - $filename = $GLOBALS['HTTP_GET_VARS']['tiff_fname']; - } - - - // Output the title - echo "Metadata details for $filename"; - - - - ?> - - - - - -

    Interpreted using: PHP JPEG Metadata Toolkit version , Copyright (C) 2004 Evan Hunter

    -
    - -

    Metadata for ""

    - - - - - -
    -
    -
    -

    Interpreted using:

    -

    PHP JPEG Metadata Toolkit version , Copyright (C) 2004 Evan Hunter

    - - - - \ No newline at end of file diff --git a/jpeg_metadata_tk/documentation/changes.html b/jpeg_metadata_tk/documentation/changes.html deleted file mode 100644 index e85f5b7..0000000 --- a/jpeg_metadata_tk/documentation/changes.html +++ /dev/null @@ -1,257 +0,0 @@ - - - - - - - The PHP JPEG Metadata Toolkit - Documentation - - - -
    -

    The PHP JPEG Metadata Toolkit - Documentation

    -
    - - Go to Documentation - Home - -
    - -

    Changes List

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    VersionsFileChanges
    1.00 -> 1.01IPTC.phpChanged get_IPTC to return partial data when error occurs
    1.00 -> 1.02Photoshop_IRB.phpChanged get_Photoshop_IRB to work with corrupted resource names which Photoshop can still read
    1.02 -> 1.03Photoshop_IRB.phpFixed put_Photoshop_IRB to output "Photoshop 3.0\x00" string with every APP13 segment, not just the first one
    1.03 -> 1.04XMP.phpChanged put_IPTC to fix a bug preventing the correct insertion of a XMP block where none existed previously
    1.04 -> 1.10XMP.phpChanged put_XMP_text to fix some array indexes which were missing qoutes
    IPTC.php - Changed put_IPTC to check if the incoming IPTC block is valid
    - Changed Interpret_IPTC_to_HTML, adding nl2br functions for each text field, - so that multiline text displays properly -
    XML.php - Changed read_xml_array_from_text to fix problem that caused the whitespace (especially newlines) to be - destroyed when converting xml text to an xml array -
    Photoshop_IRB.php - Changed get_Photoshop_IRB to fix processing of embedded resource names, - after discovering that Photoshop does not process - resource names according to the standard :
    - "Adobe Photoshop 6.0 File Formats Specification, Version 6.0, Release 2, November 2000"
    - This is an update to the change 1.00 -> 1.02, which was not fully correct

    - Changed put_Photoshop_IRB to fix the writing of embedded resource name, - to avoid creating blank resources, and to fix a problem - causing the IRB block to be incorrectly positioned if no APP segments existed.

    - Changed get_Photoshop_IPTC to initialise the output array correctly. -
    Unicode.php - Added the following functions:
    - smart_HTML_Entities
    - smart_htmlspecialchars
    - HTML_UTF16_UnEscape
    - HTML_UTF8_UnEscape

    - Changed HTML_UTF8_Escape and HTML_UTF16_Escape to - use smart_htmlspecialchars, so that characters which - were already escaped would remain intact -
    JPEG.php - changed put_jpeg_header_data to check if the data being written exists -
    Example.php - Changed name of GET parameter from "filename" to "jpeg_fname" - to stop script-kiddies using the google command "allinurl:*.php?filename=*" - to find servers to attack -
    EXIF.php - added function get_EXIF_TIFF to allow extracting EXIF from a TIFF file -
    Photoshop_File_Info.php - New File - see documentation for more information -
    Edit_File_Info.php - New File - see documentation for more information -
    Edit_File_Info_Example.php - New File - see documentation for more information -
    Write_File_Info.php - New File - see documentation for more information -
    1.10 -> 1.11EXIF.php - Added functionality to allow decoding of XMP and Photoshop IRB information embedded within the EXIF data
    - Added checks for http and ftp wrappers, as these are not supported
    - Changed interpret_IFD to allow thumbnail links to work when toolkit is portable across directories -
    Makernotes/casio.phpChanged get_Casio_Makernote_Html to allow thumbnail links to work when toolkit is portable across directories
    Makernotes/olympus.phpChanged get_Olympus_Makernote_Html to allow thumbnail links to work when toolkit is portable across directories
    JFIF.phpChanged Interpret_JFXX_to_HTML to allow thumbnail links to work when toolkit is portable across directories
    Photoshop_IRB.php - Moved code out of get_Photoshop_IRB into new function unpack_Photoshop_IRB_Data to allow reading of IRB blocks embedded within EXIF (for TIFF Files)
    - Moved code out of put_Photoshop_IRB into new function pack_Photoshop_IRB_Data to allow writing of IRB blocks embedded within EXIF (for TIFF Files)
    - Enabled the usage of $GLOBALS['HIDE_UNKNOWN_TAGS'] to hide unknown resources
    - Changed Interpret_IRB_to_HTML to allow thumbnail links to work when toolkit is portable across directories -
    EXIF_Makernote.php - Changed makernotes directory definition to allow the toolkit to be portable across directories -
    EXIF_Tags.php - Added TIFF compression types ZIP, LZW and JPEG
    - Added embedded XMP tag
    - Added embedded Photoshop IRB tag
    - Fixed GPS tags after testing -
    Example.php - Changed displayed toolkit version numbers to reference Toolkit_Version.php - Changed this example file to be easily relocatable -
    TIFFExample.php - Changed displayed toolkit version numbers to reference Toolkit_Version.php -
    Write_File_Info.php - Changed displayed toolkit version numbers to reference Toolkit_Version.php
    - Changed error reporting to no errors
    - Removed limitation on file being in current directory -
    Edit_File_Info_Example.php - Changed displayed toolkit version numbers to reference Toolkit_Version.php -
    Photoshop_File_Info.php - Changed displayed toolkit version numbers to reference Toolkit_Version.php -
    Edit_File_Info.php - Changed displayed toolkit version numbers to reference Toolkit_Version.php -
    get_ps_thumb.php - Added support for Photoshop IRB thumbnails which are embedded within EXIF information (used in TIFF files) -
    Toolkit_Version.php - New File - Added file to provide a single storage place for current version number -
    pjmt_utils.php - New File - Added file to provide utility functions for the toolkit -
    - -
    - -
    - -
    - - - \ No newline at end of file diff --git a/jpeg_metadata_tk/documentation/css_terms.html b/jpeg_metadata_tk/documentation/css_terms.html deleted file mode 100644 index 648f6cd..0000000 --- a/jpeg_metadata_tk/documentation/css_terms.html +++ /dev/null @@ -1,171 +0,0 @@ - - - - - - - The PHP Metadata Toolkit - Documentation - - - -
    -

    The PHP JPEG Metadata Toolkit - Documentation

    -
    - - Go to Documentation - Home - -
    - -

    Customising the HTML look via style sheets

    -

    - Below is a list of style classes which have been used in the HTML for the PHP JPEG Metadata Toolkit. - These allow the look of the output HTML to be changed via the use of cascading style sheets. - -

    - -

    There are two ways to do this:

    -
      -
    1. Embedded Style sheets
    2. -
    3. External Style sheets
    4. -
    - -

    Examples

    - -

    Both of the following two sections would cause the following visual effects:

    -

      -
    • The page will have a dark grey background colour (#505050)
    • -
    • The default text colour for the page will be off-white (#F0F0F0)
    • -
    • Links (Anchors) will be orange
    • -
    • Main headings for the EXIF section will be red
    • -
    • Secondary headings for the EXIF section will be orange
    • -
    • The outer border of the Tables for the EXIF section will be a thin dark yellow (#909000) solid line
    • -
    -
    -
    -
    - -

    Embedded Style Sheets

    - -

    Include the following in the <HEAD> section of the HTML page:

    -
    -        <META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css">
    -        <STYLE TYPE="text/css" MEDIA="screen, print, projection">
    -        <!--
    -                BODY { background-color:#505050; color:#F0F0F0 }
    -                a  { color:orange  }
    -                .EXIF_Main_Heading { color:red }
    -                .EXIF_Secondary_Heading{ color: orange}
    -                .EXIF_Table {  border-collapse: collapse ; border: 1px solid #909000}
    -        -->
    -        </STYLE>
    -                
    - - -

    External Style Sheets

    -

    Include the following in the <HEAD> section of the HTML page:

    -
    -        <link rel=StyleSheet href="style.css" type="text/css">
    -                
    -

    Create an external file called "style.css" containing the following:

    -
    -        BODY { background-color:#505050; color:#F0F0F0 }
    -        a  { color:orange  }
    -        .EXIF_Main_Heading { color:red }
    -        .EXIF_Secondary_Heading{ color: orange}
    -        .EXIF_Table {  border-collapse: collapse ; border: 1px solid #909000}
    -                
    - - - -
    -
    - -

    Cascading Style Sheets Class List

    -

    Any of the following classes may be used to customise the look of the html output from the PHP JPEG Metadata Toolkit

    -
      -
    • JPEG_Intrinsic_Main_Heading
    • -
    • JPEG_Intrinsic_Table
    • -
    • JPEG_Intrinsic_Table_Row
    • -
    • JPEG_Intrinsic_Caption_Cell
    • -
    • JPEG_Intrinsic_Value_Cell
    • -
    -
      -
    • JPEG_APP_Segments_Main_Heading
    • -
    • JPEG_APP_Segments_Table
    • -
    • JPEG_APP_Segments_Table_Row
    • -
    • JPEG_APP_Segments_Caption_Cell
    • -
    • JPEG_APP_Segments_Type_Cell
    • -
    • JPEG_APP_Segments_Size_Cell
    • -
    -
      -
    • XMP_Main_Heading
    • -
    • XMP_Secondary_Heading
    • -
    • XMP_Table
    • -
    • XMP_Table_Row
    • -
    • XMP_Caption_Cell
    • -
    • XMP_Value_Cell
    • -
    -
      -
    • JFIF_Main_Heading
    • -
    • JFIF_Table
    • -
    • JFIF_Table_Row
    • -
    • JFIF_Caption_Cell
    • -
    • JFIF_Value_Cell
    • -
    • JFIF_Thumbnail
    • -
    -
      -
    • JFXX_Main_Heading
    • -
    • JFXX_Text
    • -
    • JFXX_Thumbnail
    • -
    • JFXX_Thumbnail_Link
    • -
    -
      -
    • JPEG_Comment_Main_Heading
    • -
    • JPEG_Comment_Text
    • -
    -
      -
    • Picture_Info_Main_Heading
    • -
    • Picture_Info_Caption_Text
    • -
    • Picture_Info_Value_Text
    • -
    -
      -
    • Photoshop_Main_Heading
    • -
    • Photoshop_Table
    • -
    • Photoshop_Table_Row
    • -
    • Photoshop_Caption_Cell
    • -
    • Photoshop_Value_Cell
    • -
    • Photoshop_Thumbnail
    • -
    • Photoshop_Thumbnail_Link
    • -
    -
      -
    • IPTC_Main_Heading
    • -
    • IPTC_Table
    • -
    • IPTC_Table_Row
    • -
    • IPTC_Caption_Cell
    • -
    • IPTC_Value_Cell
    • -
    -
      -
    • EXIF_Main_Heading
    • -
    • EXIF_Secondary_Heading
    • -
    • EXIF_Table
    • -
    • EXIF_Table_Row
    • -
    • EXIF_Caption_Cell
    • -
    • EXIF_Value_Cell
    • -
    • EXIF_First_IFD_Thumb
    • -
    • EXIF_First_IFD_Thumb_Link
    • -
    • EXIF_Minolta_Thumb
    • -
    • EXIF_Minolta_Thumb_Link
    • -
    • EXIF_Casio_Thumb
    • -
    • EXIF_Casio_Thumb_Link
    • -
    • EXIF_Makernote_Small_Heading
    • -
    • EXIF_Makernote_Text
    • -
    - - -
    -
    - -
    - - - diff --git a/jpeg_metadata_tk/documentation/edit_write_file_info.html b/jpeg_metadata_tk/documentation/edit_write_file_info.html deleted file mode 100644 index d9ab787..0000000 --- a/jpeg_metadata_tk/documentation/edit_write_file_info.html +++ /dev/null @@ -1,272 +0,0 @@ - - - - - - - The PHP JPEG Metadata Toolkit - Documentation - - - -
    -

    The PHP JPEG Metadata Toolkit - Documentation

    -
    - - Go to Documentation - Examples - -
    - -

    Example Photoshop "File Info" Editor Scripts

    - -

    - These two example scripts areincluded with the PHP JPEG Metadata Toolkit. - They show how the library can be used to edit metadata over the internet - in exactly the same format as Photoshop"s "File Info" dialog box. - This is an explanation of some of the commands used in the script. -

    - - - Click here to see the Edit_File_Info_Example.php -

    - - Click here to see the Write_File_Info.php -
    - -
    -
    -
    - -

    Edit_File_Info_Example.php

    - -

    Overview

    -

    - This script utilises another script called Edit_File_Info.php.
    - Edit_File_Info.php outputs the HTML required to display a HTML form which emulates - the Photoshop "File Info" dialog box. -

    -

    - Edit_File_Info.php has four modes of operation: -

    -
      -
    1. - If $new_ps_file_info_array is defined then it's data will be used - to fill the fields. -
    2. -
    3. - If $new_ps_file_info_array is not defined but $filename is - defined, then the file info fields will be filled from the - metadata in the file specified -
    4. -
    5. - If $new_ps_file_info_array is not defined but $filename - and $default_ps_file_info_array are defined, then the - file info fields will be filled from the metadata in the - file specified, but where fields are blank, they will be - filled from $default_ps_file_info_array -
    6. -
    7. - Otherwise the fields will be blank -
    8. -
    - - - - -
    -
    - -

    Forced Field Values

    - -

    - If the variable $new_ps_file_info_array before Edit_File_Info.php is included, then it - forces the values of the fields to be loaded from $new_ps_file_info_array -

    - -

    - NOTE: -

      -
    • date must be in YYYY-MM-DD format
    • -
    • coyrightstatus must be either "Copyrighted Work", "Public Domain", or "Unknown"
    • -
    • urgency must be a single digit between 1 and 8 inclusive
    • -
    • category must be 3 characters or less
    • -
    • authors position is not used in Photoshop 7 or higher
    • -
    • jobname is not used in Photoshop CS or higher
    • -
    -

    - -

    Here is some example code for defining $new_ps_file_info_array

    -
    -                        $new_ps_file_info_array = array (
    -                                                                'title'                 => "Frenchmans Cap",
    -                                                                'author'                => "Evan Hunter",
    -                                                                'authorsposition'       => "",
    -                                                                'caption'               => "A view of Frenchmans cap from a bus on the Lyell Highway",
    -                                                                'captionwriter'         => "Evan Hunter",
    -                                                                'jobname'               => "",
    -                                                                'copyrightstatus'       => "Copyrighted Work",
    -                                                                'copyrightnotice'       => "Copyright (c) Evan Hunter 2004",
    -                                                                'ownerurl'              => "http://www.ozhiker.com",
    -                                                                'keywords'              => array( "Frenchmans Cap", "Franklin Gordon National Park", "Tasmania" ),
    -                                                                'category'              => "abc",
    -                                                                'supplementalcategories'=> array( "Supp Category1", "Supp Category2" ),
    -                                                                'date'                  => "2004-05-11",
    -                                                                'city'                  => "Franklin Gordon National Park",
    -                                                                'state'                 => "Tasmania",
    -                                                                'country'               => "Australia",
    -                                                                'credit'                => "Evan Hunter",
    -                                                                'source'                => "Evan Hunter",
    -                                                                'headline'              => "Frenchmans Cap from Lyell Highway",
    -                                                                'instructions'          => "No Instructions",
    -                                                                'transmissionreference' => "12345",
    -                                                                'urgency'               => "3"
    -                                                                );
    -                
    - -
    -
    - - -

    Default Field Values

    - -

    - The variable $default_ps_file_info_array sets the default values of the File Info fields. - These are only loaded into the fields where File Info has been loaded from a file, but - the fields in question are still blank. It has similar definition requirements to $new_ps_file_info_array. -

    - -
    -                        // Define defaults for the fields - These are only used where the image has blank fields
    -                        $default_ps_file_info_array = array (
    -                                                                'title'                 => "",
    -                                                                'author'                => "Evan Hunter",
    -                                                                'authorsposition'       => "",
    -                                                                'caption'               => "",
    -                                                                'captionwriter'         => "Evan Hunter",
    -                                                                'jobname'               => "",
    -                                                                'copyrightstatus'       => "Copyrighted Work",
    -                                                                'copyrightnotice'       => "Copyright (c) Evan Hunter 2004",
    -                                                                'ownerurl'              => "http://www.ozhiker.com",
    -                                                                'keywords'              => array(),
    -                                                                'category'              => "",
    -                                                                'supplementalcategories'=> array(),
    -                                                                'date'                  => "",
    -                                                                'city'                  => "",
    -                                                                'state'                 => "Tasmania",
    -                                                                'country'               => "Australia",
    -                                                                'credit'                => "Evan Hunter",
    -                                                                'source'                => "Evan Hunter",
    -                                                                'headline'              => "",
    -                                                                'instructions'          => "",
    -                                                                'transmissionreference' => "",
    -                                                                'urgency'               => ""
    -                                                                );
    -                
    - -
    -
    - -

    Input Filename

    -

    - When the $filename variable is defined before the inclusion of Edit_File_Info.php, it defines - which JPEG file should be searched to fill the File Info fields -

    -
    -                        $filename = "test.jpg";
    -                
    - -
    -
    - -

    Output Filename

    -

    - The $outputfilename variable must always be defined before the inclusion of Edit_File_Info.php. - It defines the target image which will be modified with the information that the user enters. - It can be the same as $filename. -

    -
    -                        $outputfilename = "test.jpg";
    -                
    - -

    Including Edit_File_Info.php

    -

    - After $outputfilename has been defined, and any of the other variables have been defined as required, - Edit_File_Info.php should be included. It outputs the HTML for the File Info Editor. -

    -
    -                        include "Edit_File_Info.php";
    -                
    - -
    -
    - - -

    Write_File_Info.php

    -

    Overview

    -

    - This script receives the File Info data from the user via the HTML POST method.
    -

    - - -

    Retrieving the POSTed variables

    -

    - The field data is retrieved as follows: -

    -
    -                        $new_ps_file_info_array = $GLOBALS['HTTP_POST_VARS'];
    -                
    - -
    -
    - -

    Preparing the field data

    -

    - The POSTed field data has some characters escaped with backslashes, which need to be removed. - Two of the fields should also be arrays which and need to be split. -

    -
    -                        // Some characters are escaped with backslashes in HTML Posted variable
    -                        // Cycle through each of the HTML Posted variables, and strip out the slashes
    -                        foreach( $new_ps_file_info_array as $var_key => $var_val )
    -                        {
    -                                $new_ps_file_info_array[ $var_key ] = stripslashes( $var_val );
    -                        }
    -
    -                        // Keywords should be an array - explode it on newline boundarys
    -                        $new_ps_file_info_array[ 'keywords' ] = explode( "\n", trim( $new_ps_file_info_array[ 'keywords' ] ) );
    -
    -                        // Supplemental Categories should be an array - explode it on newline boundarys
    -                        $new_ps_file_info_array[ 'supplementalcategories' ] = explode( "\n", trim( $new_ps_file_info_array[ 'supplementalcategories' ] ) );
    -                
    - -
    -
    - -

    Writing the File Info

    -

    - Now the File Info data can be written to a file. The following code results in - the $jpeg_header_data being updated with the new Photoshop File Info, and it can - then be written to a JPEG file as normal using put_jpeg_header_data: -

    -
    -                        // Retrieve the header information
    -                        $jpeg_header_data = get_jpeg_header_data( $filename );
    -
    -                        // Retreive the EXIF, XMP and Photoshop IRB information from
    -                        // the existing file, so that it can be updated
    -                        $Exif_array = get_EXIF_JPEG( $filename );
    -                        $XMP_array = read_XMP_array_from_text( get_XMP_text( $jpeg_header_data ) );
    -                        $IRB_array = get_Photoshop_IRB( $jpeg_header_data );
    -
    -                        // Update the JPEG header information with the new Photoshop File Info
    -                        $jpeg_header_data = put_photoshop_file_info( $jpeg_header_data, $new_ps_file_info_array, $Exif_array, $XMP_array, $IRB_array );
    -                
    - -
    -
    - - - -
    - - - \ No newline at end of file diff --git a/jpeg_metadata_tk/documentation/example.html b/jpeg_metadata_tk/documentation/example.html deleted file mode 100644 index 7f922de..0000000 --- a/jpeg_metadata_tk/documentation/example.html +++ /dev/null @@ -1,131 +0,0 @@ - - - - - - - The PHP JPEG Metadata Toolkit - Documentation - - - -
    -

    The PHP JPEG Metadata Toolkit - Documentation

    -
    - - Go to Documentation - Examples - -
    - -

    Example Metadata Display Script

    - -

    - This example script is included with the PHP JPEG Metadata Toolkit. It shows how the library - can be used to display detailed metadata from a JPEG image file. This is an explanation of some of the - commands used in the script. -

    - - - Click here to see the script - -
    - -
    -
    -
    - -

    Error Reporting

    -

    - Although this library has been tested to be error free on a large number of images, - it is good programming to turn error reporting off, to ensure that users don't see - any error messages. This is not just cosmetic, it helps hide the details of the program - from hackers. -

    -
    -        // Turn off Error Reporting
    -        error_reporting ( 0 );
    -                
    - -

    Includes

    -

    Ensure that the correct files are included:

    - - - - - - - - - - - - - - - - - - - - - - - - - - -
    JPEG.phpFor accessing basic JPEG functions - most programs will need this
    EXIF.phpFor accessing Exchangeable Image File Format (EXIF) APP1 segments
    JFIF.phpFor accessing JPEG File Interchange Format and JFIF Extension segments
    Photoshop_IRB.phpFor accessing Photoshop IRB & IPTC-NAA IIM segments
    PictureInfo.phpFor accessing APP12 Picture Info segments
    XMP.phpFor accessing APP1 XMP / RDF / Dublin Core segments
    - -
    -
    - -

    Displaying Metadata Information

    - -

    - Displaying metadata in HTML format from a PHP script is very simple. For example, - the following will display any EXIF information in a JPEG image: -

    - -
    -        echo Interpret_EXIF_to_HTML( get_EXIF_JPEG( $filename ), $filename );
    -                
    - -
    -
    - -

    To display the Photoshop IRB and IPTC-NAA information, use:

    -
    -        echo Interpret_IRB_to_HTML( get_Photoshop_IRB( get_jpeg_header_data( $filename ) ), $filename );
    -                
    - -
    -
    - -

    - Note: The HTML returned from these functions is just a fragment, and requires - <HTML>, <HEAD> and <BODY> tags to be provided. -

    - -
    -
    - -

    Writing Metadata Information

    -

    - Although this is not shown in the example script, writing metadata is also very simple. For example, the following will allow - the EXIF data in an image to be modified, and written to a file called "output.jpg" : -

    -
    -        $exif_data = get_EXIF_JPEG( $filename );
    -
    -        // Modify the $exif_data array here
    -
    -        $jpeg_header_data = put_EXIF_JPEG( $exif_data, $jpeg_header_data );
    -        put_jpeg_header_data( $filename, "output.jpg", $jpeg_header_data );
    -                
    - -
    -
    - -
    - - - \ No newline at end of file diff --git a/jpeg_metadata_tk/documentation/examples.html b/jpeg_metadata_tk/documentation/examples.html deleted file mode 100644 index 172b123..0000000 --- a/jpeg_metadata_tk/documentation/examples.html +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - The PHP JPEG Metadata Toolkit - Documentation - - - -
    -

    The PHP JPEG Metadata Toolkit - Documentation

    -
    - - Go to Documentation - Home - -
    - -

    Example Scripts

    - -

    - Included with the PHP JPEG Metadata Toolkit are several example scripts, which show how the library - can be used to retrieve and store the metadata in a JPEG image file. Below are explanations of some of the - commands used in the scripts. -

    - - - - - - - - - - - - - - - - - -
    -
    - - - - - \ No newline at end of file diff --git a/jpeg_metadata_tk/documentation/exif.html b/jpeg_metadata_tk/documentation/exif.html deleted file mode 100644 index f67c1fd..0000000 --- a/jpeg_metadata_tk/documentation/exif.html +++ /dev/null @@ -1,284 +0,0 @@ - - - - - - - The PHP JPEG Metadata Toolkit - Documentation - - - -
    -

    The PHP JPEG Metadata Toolkit - Documentation

    -
    - - Go to Documentation - Home - -
    - -

    EXIF and Kodak "Meta" Function Reference

    - -
    - Example.php - - Displays detailed information about the metadata in a JPEG file. -
    - TIFFExample.php - - Displays detailed information about the metadata in a JPEG file. -
    - Edit_File_Info_Example.php,
    Write_File_Info.php
    -
    - Allows editing of metadata over the internet in exactly the same format as - Photoshop"s "File Info" dialog box. -
    - - - - - - - - - - - - - - - - - - - - - - -
    Function:get_EXIF_JPEG
    Description: - Retrieves information from a Exchangeable Image File Format (EXIF) - APP1 segment and returns it in an array. Uses information - supplied by the get_jpeg_header_data function -
    Parameters:filenamethe filename of the JPEG image to process
    Returns:OutputArrayArray of EXIF records
    FALSEIf an error occured in decoding
    - - -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Function:put_EXIF_JPEG
    Description: - Stores information into a Exchangeable Image File Format (EXIF) - APP1 segment from an EXIF array.
    -
    - WARNING: Because the EXIF standard allows pointers to data - outside the APP1 segment, if there are any such pointers in - a makernote, this function will DAMAGE them since it will not - be aware that there is an external pointer. This will often - happen with Makernotes that include an embedded thumbnail. - This damage could be prevented where makernotes can be decoded, - but currently this is not implemented. -
    Parameters:exif_dataThe array of EXIF data to insert into the JPEG header
    jpeg_header_dataThe JPEG header into which the EXIF data should be stored, as from get_jpeg_header_data
    Returns:jpeg_header_dataJPEG header array with the EXIF segment inserted
    FALSEIf an error occured
    - - -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - -
    Function:get_Meta_JPEG
    Description: -

    - Retrieves information from a Meta APP3 segment and returns it - in an array. Uses information supplied by the - get_jpeg_header_data function. -

    -

    - The Meta segment has the same format as an EXIF segment, but - uses different tags -

    -
    Parameters:filenamethe filename of the JPEG image to process
    Returns:OutputArrayArray of Meta records
    FALSEIf an error occured in decoding
    - - -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Function:put_Meta_JPEG
    Description: - Stores information into a Meta APP3 segment from a Meta array.
    -
    - WARNING: Because the Meta (EXIF) standard allows pointers to data - outside the APP1 segment, if there are any such pointers in - a makernote, this function will DAMAGE them since it will not - be aware that there is an external pointer. -
    Parameters:meta_dataThe array of Meta data to insert into the JPEG header
    jpeg_header_dataThe JPEG header into which the Meta data should be stored, as from get_jpeg_header_data
    Returns:jpeg_header_dataJPEG header array with the Meta segment inserted
    FALSEIf an error occured
    - -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - -
    Function:get_EXIF_TIFF
    Description: -

    - Retrieves information from a Exchangeable Image File Format (EXIF) - within a TIFF file and returns it in an array. -

    -
    Parameters:filenamethe filename of the TIFF image to process
    Returns:OutputArrayArray of EXIF records
    FALSEIf an error occured in decoding
    - -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - -
    Function:Interpret_EXIF_to_HTML
    Description: -

    - Generates html detailing the contents an APP1 EXIF array - which was retrieved with a get_EXIF_JPEG function. - Can also be used for APP3 Meta arrays. -

    -
    Parameters:Exif_arraythe EXIF array,as read from get_EXIF_JPEG
    filenamethe name of the Image file being processed (used by scripts which displays EXIF thumbnails)
    Returns:output_str A string containing the HTML
    - - -

    Notes:

    -

    - Unfortunately, because EXIF data may be distributed anywhere - throughout an image file, rather than just being in one block, - it is impossible to pass just a string containing only the EXIF - information. Hence it is neccessary to be able to seek to - any point in the file. This causes the HTTP and FTP wrappers - not to work - i.e. the EXIF functions will only work with local - files. -

    - To work on an internet file, copy it locally to start with: -

    -
    - $newfilename = tempnam ( $dir, "tmpexif" );
    - copy ( "http://whatever.com", $newfilename ); -
    - -

    - There are three global variables which can be set to alter the look of any HTML - output from Interpret_EXIF_to_HTML: -

    -
    - $GLOBALS['HIDE_UNKNOWN_TAGS'];
    - $GLOBALS['SHOW_BINARY_DATA_HEX'];
    - $GLOBALS['SHOW_BINARY_DATA_TEXT']; -
    -

    These variables are all boolean and all default to FALSE

    - - -
    - - - \ No newline at end of file diff --git a/jpeg_metadata_tk/documentation/index.html b/jpeg_metadata_tk/documentation/index.html deleted file mode 100644 index 8d21c64..0000000 --- a/jpeg_metadata_tk/documentation/index.html +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - The PHP JPEG Metadata Toolkit - Documentation - - - -
    -

    The PHP JPEG Metadata Toolkit - Documentation

    -
    - - - - \ No newline at end of file diff --git a/jpeg_metadata_tk/documentation/intro.html b/jpeg_metadata_tk/documentation/intro.html deleted file mode 100644 index b40cfe0..0000000 --- a/jpeg_metadata_tk/documentation/intro.html +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - The PHP JPEG Metadata Toolkit - Documentation - - - -
    -

    The PHP JPEG Metadata Toolkit - Documentation

    -
    - - Go to Documentation - Home - -
    - -

    Introduction

    - -

    - The PHP JPEG Metadata Toolkit is a library of functions which allows manipulation of many types of metadata - that reside in a JPEG image file. -

    -
    - -

    The metadata that can be accessed by this library include:

    - - - - - - - - - - - - - - - - - - - - - - - - - - -
    EXIF, DCF & TIFF/EP - including makernotesContains Digital Camera Settings. Can contain a thumbnail.
    XMP, RDF & Dublin Core, including multiple language supportCan contain Digital camera settings and text headings/captions.
    Photoshop IRB & IPTC-NAA IIMContains Photoshop settings and can contain text headings/captions. Can contain a thumbnail.
    Picture InfoContains Digital Camera Settings for older cameras
    JFIF & JFIF ExtensionContains limited info about the image and can contain a thumbnail
    Intrinsic JPEG ValuesContains limited info configuration of the image
    -
    -
    - -

    Other features

    -

      -
    • Has been tested with over 450 popular digital cameras
    • -
    • Provides access to lots of metadata for which php has no built in support
    • -
    • Works with many files that have corrupted metadata.
    • -
    • Customisable look of the HTML output via style sheets
    • -
    - -
    -

    Requirements

    -

    The toolkit requires PHP 4 or higher - Has been tested with PHP 4.3.7 and 4.3.8

    -
    -
    - -
    - - - \ No newline at end of file diff --git a/jpeg_metadata_tk/documentation/jfif.html b/jpeg_metadata_tk/documentation/jfif.html deleted file mode 100644 index f00cb45..0000000 --- a/jpeg_metadata_tk/documentation/jfif.html +++ /dev/null @@ -1,237 +0,0 @@ - - - - - - - The PHP JPEG Metadata Toolkit - Documentation - - - -
    -

    The PHP JPEG Metadata Toolkit - Documentation

    -
    - - Go to Documentation - Home - -
    - -

    JFIF & JFIF Extension Function Reference

    -

    - The "App 0" segment is often used by the JPEG File Interchange Format (JFIF) information. - Sometimes, there is an additional "App 0" segment for the JFIF Extension (JFXX) information. -

    - -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - -
    Function:get_JFIF
    Description: - Retrieves information from a JPEG File Interchange Format (JFIF) - segment and returns it in an array. Uses information supplied by - the get_jpeg_header_data function -
    Parameters:jpeg_header_dataa JPEG header data array in the same format as from get_jpeg_header_data
    Returns:JFIF_dataan array of JFIF data
    FALSEif a JFIF segment could not be found
    - - -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - -
    Function:put_JFIF
    Description: - Creates a new JFIF segment from an array of JFIF data in the - same format as would be retrieved from get_JFIF, and inserts - this segment into the supplied JPEG header array -
    Parameters:jpeg_header_dataa JPEG header data array in the same format as from get_jpeg_header_data, into which the new JFIF segment will be put
    new_JFIF_arraya JFIF information array in the same format as from get_JFIF, to create the new segment
    Returns:jpeg_header_datathe JPEG header data array with the new JFIF segment added
    - - -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - -
    Function:Interpret_JFIF_to_HTML
    Description: - Generates html showing the JFIF information contained in a JFIF data array, as retrieved with get_JFIF -
    Parameters:JFIF_arraya JFIF data array, as from get_JFIF
    filenamethe name of the JPEG file being processed (used by the script which displays the JFIF thumbnail)
    Returns:outputthe HTML string
    - - -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - -
    Function:get_JFXX
    Description: - Retrieves information from a JPEG File Interchange Format Extension (JFXX) - segment and returns it in an array. Uses information supplied by - the get_jpeg_header_data function -
    Parameters:jpeg_header_dataa JPEG header data array in the same format as from get_jpeg_header_data
    Returns:JFXX_dataan array of JFXX data
    FALSEif a JFXX segment could not be found
    - - -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - -
    Function:put_JFXX
    Description: - Creates a new JFXX segment from an array of JFXX data in the - same format as would be retrieved from get_JFXX, and inserts - this segment into the supplied JPEG header array -
    Parameters:jpeg_header_dataa JPEG header data array in the same format as from get_jpeg_header_data, into which the new JFXX segment will be put
    new_JFXX_arraya JFXX information array in the same format as from get_JFXX, to create the new segment
    Returns:jpeg_header_datathe JPEG header data array with the new JFXX segment added
    - - -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - -
    Function:Interpret_JFXX_to_HTML
    Description: - Generates html showing the JFXX thumbnail contained in a JFXX data array, as retrieved with get_JFXX -
    Parameters:JFXX_arraya JFXX information array in the same format as from get_JFXX, to create the new segment
    filenamethe name of the JPEG file being processed (used by the script which displays the JFXX thumbnail)
    Returns:outputthe Html string
    - - -
    -
    - -
    - - - \ No newline at end of file diff --git a/jpeg_metadata_tk/documentation/jpeg.html b/jpeg_metadata_tk/documentation/jpeg.html deleted file mode 100644 index 36a59a7..0000000 --- a/jpeg_metadata_tk/documentation/jpeg.html +++ /dev/null @@ -1,303 +0,0 @@ - - - - - - - The PHP JPEG Metadata Toolkit - Documentation - - - -
    -

    The PHP JPEG Metadata Toolkit - Documentation

    -
    - - Go to Documentation - Home - -
    - -

    JPEG & Intrinsic JPEG Values Function Reference

    -

    - Functions for performing basic operations on a JPEG file -

    - -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - -
    Function:get_jpeg_header_data
    Description: - Reads all the JPEG header segments from an JPEG image file into an array -
    Parameters:filenamethe filename of the file to JPEG file to read
    Returns:headerdataArray of JPEG header segments
    FALSEif headers could not be read
    - - -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Function:put_jpeg_header_data
    Description: - Writes JPEG header data into a JPEG file. Takes an array in the - same format as from get_jpeg_header_data, and combines it with - the image data of an existing JPEG file, to create a new JPEG file
    -
    - WARNING: As this function will replace all JPEG headers, - including SOF etc, it is best to read the jpeg headers - from a file, alter them, then put them back on the same - file. If a SOF segment wer to be transfered from one - file to another, the image could become unreadable unless - the images were idenical size and configuration -
    Parameters:old_filenamethe JPEG file from which the image data will be retrieved
    new_filenamethe name of the new JPEG to create (can be same as old_filename)
    jpeg_header_dataa JPEG header data array in the same format as from get_jpeg_header_data
    Returns:TRUEon Success
    FALSEon Failure
    - - -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - -
    Function:get_jpeg_Comment
    Description: - Retreives the contents of the JPEG Comment (COM = 0xFFFE) segment if one exists -
    Parameters:jpeg_header_datathe JPEG header data, as retrieved from the get_jpeg_header_data function
    Returns:stringContents of the Comment segement
    FALSEif the comment segment couldnt be found
    - - -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - -
    Function:Interpret_Comment_to_HTML
    Description: - Generates html showing the contents of any JPEG Comment segment -
    Parameters:jpeg_header_datathe JPEG header data, as retrieved from the get_jpeg_header_data function
    Returns:outputthe HTML
    - - -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - -
    Function:get_jpeg_intrinsic_values
    Description: - Retreives information about the intrinsic characteristics of the jpeg image, such as Bits per Component, Height and Width. -
    Parameters:jpeg_header_datathe JPEG header data, as retrieved from the get_jpeg_header_data function
    Returns:arrayAn array containing the intrinsic JPEG values
    FALSEif the comment segment couldnt be found
    - - -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - -
    Function:Interpret_intrinsic_values_to_HTML
    Description: - Generates html showing some of the intrinsic JPEG values which were retrieved with the get_jpeg_intrinsic_values function -
    Parameters:valuesthe JPEG intrinsic values, as read from get_jpeg_intrinsic_values
    Returns:OutputStrA string containing the HTML
    - - -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - -
    Function:get_jpeg_image_data
    Description: - Retrieves the compressed image data part of the JPEG file -
    Parameters:filenamethe filename of the JPEG file to read
    Returns:compressed_dataA string containing the compressed data
    FALSEif retrieval failed
    - - -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - -
    Function:Generate_JPEG_APP_Segment_HTML
    Description: - Generates html showing information about the Application (APP) segments which are present in the JPEG file -
    Parameters:jpeg_header_datathe JPEG header data, as retrieved from the get_jpeg_header_data function
    Returns:outputA string containing the HTML
    - - -
    -
    - -
    - - - \ No newline at end of file diff --git a/jpeg_metadata_tk/documentation/photoshop.html b/jpeg_metadata_tk/documentation/photoshop.html deleted file mode 100644 index c8c8009..0000000 --- a/jpeg_metadata_tk/documentation/photoshop.html +++ /dev/null @@ -1,262 +0,0 @@ - - - - - - - The PHP JPEG Metadata Toolkit - Documentation - - - -
    -

    The PHP JPEG Metadata Toolkit - Documentation

    -
    - - Go to Documentation - Home - -
    - -

    Photoshop IRB & IPTC-NAA IIM Function Reference

    -

    - The Photoshop Image Resource Block (IRB) resides in - the "App 13" segment of a JPEG image. - It contains photoshop information but can also contain the - IPTC-NAA IIM record. (International Press Telecommunications Council, Newspaper Association of America, Information Interchange Model) -

    - -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - -
    Function:get_Photoshop_IRB
    Description: - Retrieves the Photoshop Information Resource Block (IRB) information - from an App13 JPEG segment and returns it as an array. This may - include IPTC-NAA IIM Information. Uses information - supplied by the get_jpeg_header_data function -
    Parameters:jpeg_header_dataa JPEG header data array in the same format as from get_jpeg_header_data
    Returns:IRBdataThe array of Photoshop IRB records
    FALSEif an APP 13 Photoshop IRB segment could not be found, or if an error occured
    - - -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Function:put_Photoshop_IRB
    Description: - Adds or modifies the Photoshop Information Resource Block (IRB) - information from an App13 JPEG segment. If a Photoshop IRB already - exists, it is replaced, otherwise a new one is inserted, using the - supplied data. Uses information supplied by the get_jpeg_header_data - function -
    Parameters:jpeg_header_dataa JPEG header data array in the same format as from get_jpeg_header_data
    new_IRB_data - an array of the data to be stored in the Photoshop - IRB segment. Should be in the same format as received - from get_Photoshop_IRB -
    Returns:jpeg_header_datathe JPEG header data array with the Photoshop IRB added.
    FALSEif an error occured
    - - -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - -
    Function:get_Photoshop_IPTC
    Description: - Retrieves IPTC-NAA IIM information from within a Photoshop - IRB (if it is present) and returns it in an array. Uses - information supplied by the get_jpeg_header_data function -
    Parameters:Photoshop_IRB_dataan array of Photoshop IRB records, as returned from get_Photoshop_IRB
    Returns:IPTC_Data_OutThe array of IPTC-NAA IIM records
    FALSEif an IPTC-NAA IIM record could not be found, or if an error occured
    - - -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - -
    Function:put_Photoshop_IPTC
    Description: - Inserts a new IPTC-NAA IIM resource into a Photoshop - IRB, or replaces an the existing resource if one is present. - Uses information supplied by the get_Photoshop_IRB function -
    Parameters:Photoshop_IRB_dataan array of Photoshop IRB records, as returned from get_Photoshop_IRB, into which the IPTC-NAA IIM record will be inserted
    new_IPTC_blockan array of IPTC-NAA records in the same format as those returned by get_Photoshop_IPTC
    Returns:Photoshop_IRB_dataThe Photoshop IRB array with the IPTC-NAA IIM resource inserted
    - - -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Function:Interpret_IRB_to_HTML
    Description: - Generates html showing the information contained in a Photoshop - IRB data array, as retrieved with get_Photoshop_IRB, including - any IPTC-NAA IIM records found.
    -
    - Note: The following resource numbers are not currently - decoded: ( Many of these do not apply to JPEG images)
    - 0x03E9, 0x03EE, 0x03EF, 0x03F0, 0x03F1, 0x03F2, 0x03F6, 0x03F9, - 0x03FA, 0x03FB, 0x03FD, 0x03FE, 0x0400, 0x0401, 0x0402, 0x0405, - 0x040E, 0x040F, 0x0410, 0x0412, 0x0413, 0x0415, 0x0416, 0x0417, - 0x041B, 0x041C, 0x041D, 0x0BB7
    -
    - ( Also these Obsolete resource numbers)
    - 0x03E8, 0x03EB, 0x03FC, 0x03FF, 0x0403 -
    Parameters:IRB_arraya Photoshop IRB data array as from get_Photoshop_IRB
    filenamethe name of the JPEG file being processed (used by the script which displays the Photoshop thumbnail)
    Returns:output_strthe HTML string
    - - -
    -
    - - - - - - - - - - - - - - - - - - - - - -
    Function:Interpret_IPTC_to_HTML
    Description: - Generates html detailing the contents a IPTC-NAA IIM array which was retrieved with the get_Photoshop_IPTC function -
    Parameters:IPTC_infothe IPTC-NAA IIM array,as read from get_IPTC
    Returns:OutputStrA string containing the HTML
    - - -
    -
    - - -
    - - - \ No newline at end of file diff --git a/jpeg_metadata_tk/documentation/photoshop_file_info.html b/jpeg_metadata_tk/documentation/photoshop_file_info.html deleted file mode 100644 index 35e5f5a..0000000 --- a/jpeg_metadata_tk/documentation/photoshop_file_info.html +++ /dev/null @@ -1,162 +0,0 @@ - - - - - - - The PHP JPEG Metadata Toolkit - Documentation - - - -
    -

    The PHP JPEG Metadata Toolkit - Documentation

    -
    - - Go to Documentation - Home - -
    - -

    Photoshop File Info Function Reference

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Function:get_photoshop_file_info
    Description: - Retrieves Photoshop 'File Info' metadata in the same way that Photoshop - does. The results are returned in an array as below:
    -
    -                $file_info_array = array(
    -                        "title"                  => "",
    -                        "author"                 => "",
    -                        "authorsposition"        => "",      // Note: Not used in Photoshop 7 or higher
    -                        "caption"                => "",
    -                        "captionwriter"          => "",
    -                        "jobname"                => "",      // Note: Not used in Photoshop CS
    -                        "copyrightstatus"        => "",
    -                        "copyrightnotice"        => "",
    -                        "ownerurl"               => "",
    -                        "keywords"               => array( 0 => "", 1 => "", ... ),
    -                        "category"               => "",     // Note: Max 3 characters
    -                        "supplementalcategories" => array( 0 => "", 1 => "", ... ),
    -                        "date"                   => "",     // Note: DATE MUST BE IN YYYY-MM-DD format
    -                        "city"                   => "",
    -                        "state"                  => "",
    -                        "country"                => "",
    -                        "credit"                 => "",
    -                        "source"                 => "",
    -                        "headline"               => "",
    -                        "instructions"           => "",
    -                        "transmissionreference"  => "",
    -                        "urgency"                => "" );
    -                                        
    -
    Parameters:Exif_arrayan array containing the EXIF information to be searched, as retrieved by get_EXIF_JPEG.
    XMP_arrayan array containing the XMP information to be searched, as retrieved by read_XMP_array_from_text.
    IRB_arrayan array containing the Photoshop IRB information to be searched, as retrieved by get_Photoshop_IRB.
    Returns:outputarrayan array as above, containing the Photoshop File Info data
    - - -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Function:put_photoshop_file_info
    Description: - Stores Photoshop "File Info" metadata in the same way that Photoshop - does. The "File Info" metadata must be in an array similar to that - returned by get_photoshop_file_info, as follows:
    -
    -                $file_info_array = array(
    -                        "title"                  => "",
    -                        "author"                 => "",
    -                        "authorsposition"        => "",      // Note: Not used in Photoshop 7 or higher
    -                        "caption"                => "",
    -                        "captionwriter"          => "",
    -                        "jobname"                => "",      // Note: Not used in Photoshop CS
    -                        "copyrightstatus"        => "",
    -                        "copyrightnotice"        => "",
    -                        "ownerurl"               => "",
    -                        "keywords"               => array( 0 => "", 1 => "", ... ),
    -                        "category"               => "",     // Note: Max 3 characters
    -                        "supplementalcategories" => array( 0 => "", 1 => "", ... ),
    -                        "date"                   => "",     // Note: DATE MUST BE IN YYYY-MM-DD format
    -                        "city"                   => "",
    -                        "state"                  => "",
    -                        "country"                => "",
    -                        "credit"                 => "",
    -                        "source"                 => "",
    -                        "headline"               => "",
    -                        "instructions"           => "",
    -                        "transmissionreference"  => "",
    -                        "urgency"                => "" );
    -                                        
    -
    Parameters:jpeg_header_dataa JPEG header data array in the same format as from get_jpeg_header_data. - This contains the header information which is to be updated.
    new_ps_file_info_arrayAn array as above, which contains the "File Info" metadata information to be written.
    Old_Exif_arrayan array containing the EXIF information to be updated, as retrieved by get_EXIF_JPEG.
    Old_XMP_arrayan array containing the XMP information to be updated, as retrieved by read_XMP_array_from_text.
    Old_IRB_arrayan array containing the Photoshop IRB information to be updated, as retrieved by get_Photoshop_IRB.
    Returns:jpeg_header_dataa JPEG header data array in the same format as from get_jpeg_header_data, - containing the Photshop "File Info" metadata. - This can then be written to a file using put_jpeg_header_data.
    FALSEIf an error occured
    - -
    -
    - - \ No newline at end of file diff --git a/jpeg_metadata_tk/documentation/picture_info.html b/jpeg_metadata_tk/documentation/picture_info.html deleted file mode 100644 index 44ab6aa..0000000 --- a/jpeg_metadata_tk/documentation/picture_info.html +++ /dev/null @@ -1,133 +0,0 @@ - - - - - - - The PHP JPEG Metadata Toolkit - Documentation - - - -
    -

    The PHP JPEG Metadata Toolkit - Documentation

    -
    - - Go to Documentation - Home - -
    - -

    Picture Info Function Reference

    -

    - The "App 12" segment is used by many older digital cameras, and contains - text called "Picture Info" -

    - -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - -
    Function:get_jpeg_App12_Pic_Info
    Description: - Retrieves the Picture Info text information from an App12 - JPEG segment and returns it as a string. Uses information - supplied by the get_jpeg_header_data function -
    Parameters:jpeg_header_dataa JPEG header data array in the same format as from get_jpeg_header_data
    Returns:App12_Head, App12_TextThe text preceeding the Picture Info (often the camera manufacturer's name), plus the Picture Info Text
    FALSE, FALSEif an APP 12 Picture Info segment could not be found
    - - -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Function:put_jpeg_App12_Pic_Info
    Description: - Writes Picture Info text into an App12 JPEG segment. Uses information - supplied by the get_jpeg_header_data function. If no App12 exists - already a new one is created, otherwise it replaces the old one -
    Parameters:jpeg_header_dataa JPEG header data array in the same format as from get_jpeg_header_data
    new_Pic_Info_TextThe Picture Info Text, including any header that is required
    Returns:jpeg_header_datathe JPEG header array with the new Picture info segment inserted
    FALSEif an error occured
    - - -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - -
    Function:Interpret_App12_Pic_Info_to_HTML
    Description: - Generates html showing the contents of any JPEG App12 Picture Info segment -
    Parameters:jpeg_header_datathe JPEG header data, as retrieved from the get_jpeg_header_data function
    Returns:outputthe HTML
    - - -
    -
    - - -
    - - - \ No newline at end of file diff --git a/jpeg_metadata_tk/documentation/style.css b/jpeg_metadata_tk/documentation/style.css deleted file mode 100644 index ae5ccff..0000000 --- a/jpeg_metadata_tk/documentation/style.css +++ /dev/null @@ -1,29 +0,0 @@ -body { - margin:0 0 0 0; - background: #A4C7A0; -} - -.headerbar { background:#505060; - color:#C0C070; - } -.maintext { margin-left:10%; - margin-right:10%; - } - - -a:link { - color: #00f; - background: transparent; -} - -a:visited { - color: #800080; - background: transparent; -} - -a:active { - color: green; - background: #FFD700 -} - - diff --git a/jpeg_metadata_tk/documentation/tiffexample.html b/jpeg_metadata_tk/documentation/tiffexample.html deleted file mode 100644 index 5e47aaf..0000000 --- a/jpeg_metadata_tk/documentation/tiffexample.html +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - The PHP JPEG Metadata Toolkit - Documentation - - - -
    -

    The PHP JPEG Metadata Toolkit - Documentation

    -
    - - Go to Documentation - Examples - -
    - -

    Example TIFF Metadata Display Script

    - -

    - This example script is included with the PHP JPEG Metadata Toolkit. It shows how the library - can be used to display detailed metadata from a TIFF image file. This is an explanation of some of the - commands used in the script. -

    - -
    -
    - - Click here to see this script - -
    -
    -
    -

    This script is essentially a subset of Example.php

    -
    -
    -
    - -

    TIFF EXIF

    -

    - The only metadata that can be stored in a TIFF file is TIFF/EXIF information. - Displaying this information metadata in HTML format from a PHP script is very simple: -

    -
    -
    -                echo Interpret_EXIF_to_HTML( get_EXIF_TIFF( $filename ), $filename );
    -
    -                
    - -
    -
    - -
    - - - \ No newline at end of file diff --git a/jpeg_metadata_tk/documentation/todo.html b/jpeg_metadata_tk/documentation/todo.html deleted file mode 100644 index 8d4e5d7..0000000 --- a/jpeg_metadata_tk/documentation/todo.html +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - The PHP JPEG Metadata Toolkit - Documentation - - - -
    -

    The PHP JPEG Metadata Toolkit - Documentation

    -
    - - Go to Documentation - Home - -
    - -

    Further Work Required

    - -

    - If you would like to contribute, and extend the toolkit, here are some - things that are yet to be done -

    - -
      -
    • Obtain format specifications for more Makernotes
    • -
    • Find out definitions of Print Image Matching Info tags
    • -
    • Obtain a copy of the Photoshop CS File Format Specification
    • -
    • Find out what the adobe-xap-filters tag means in XMP
    • -
    • Fully Test Functions to write EXIF data
    • -
    • Test the use of EXIF functions on TIFF Files - only JPEG's tested so far
    • -
    • Figure out a way to allow EXIF to function normally with HTTP and FTP wrappers
    • -
    • Implement decoding of Adobe segment
    • -
    • Find definition of Ducky segment and implement decoding
    • -
    • Implement decoding of Apple plist segment
    • -
    • Implement decoding of ICC Profiles
    • -
    • Implement EXIF decoding of Device Setting Description field
    • -
    • Implement EXIF decoding of SpatialFrequencyResponse field
    • -
    • Implement EXIF decoding of User comment
    • -
    • Implement EXIF decoding of OECF field
    • -
    • Implement EXIF decoding of SubjectArea field
    • -
    • Implement EXIF decoding of IFD datatype Float
    • -
    • Implement EXIF decoding of IFD datatype Double
    • -
    • Test those IPTC fields which are not used by Photoshop
    • -
    • Implement support for IPTC Extended Dataset record
    • -
    • Implement decoding of IPTC record 1:90 ( Coded Character Set )
    • -
    • Implement Display of Rasterised Caption for IPTC record 2:125
    • -
    • Implement JFIF Thumbnail display
    • -
    • Implement JFXX one and three bytes per pixel thumbnail decoding
    • -
    • Implement decoding for the many Photoshop IRB resources which are currently not supported
    • -
    • Find out what Photoshop IRB resources 1061, 1062 & 1064 are
    • -
    • Test whether the URL List field in Photoshop IRB works - No sample files available
    • -
    • Test get_Photoshop_IRB and put_Photoshop_IRB with multiple APP13 segments
    • -
    • Test the Unicode UTF-16 functions that have not been tested fully
    • -
    • Test the many RDF items that have not yet been tested - only those used by photoshop 7.0 and CS have been tested
    • -
    • Implement decoding of One Byte Per Pixel encoded JFXX Thumbnail
    • -
    • Implement decoding of Three Bytes Per Pixel encoded JFXX Thumbnail
    • -
    - -
    -
    -
    - - \ No newline at end of file diff --git a/jpeg_metadata_tk/documentation/xmp.html b/jpeg_metadata_tk/documentation/xmp.html deleted file mode 100644 index 894546f..0000000 --- a/jpeg_metadata_tk/documentation/xmp.html +++ /dev/null @@ -1,228 +0,0 @@ - - - - - - - The PHP JPEG Metadata Toolkit - Documentation - - - -
    -

    The PHP JPEG Metadata Toolkit - Documentation

    -
    - - Go to Documentation - Home - -
    - -

    XMP / RDF / Dublin Core Function Reference

    -

    - The Extensible Metadata Platform (XMP) information resides in - the "App 1" segment of a JPEG image. - An XMP segment is XML based and contains - Resource Description Framework (RDF) data, which itself can - contain the Dublin Core Metadata Initiative (DCMI) information. -

    - -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - -
    Function:get_XMP_text
    Description: - Retrieves the Extensible Metadata Platform (XMP) information - from an App1 JPEG segment and returns the raw XML text as a - string. This includes the Resource Description Framework (RDF) - information and may include Dublin Core Metadata Initiative (DCMI) - information. Uses information supplied by the get_jpeg_header_data - function -
    Parameters:jpeg_header_dataa JPEG header data array in the same format as from get_jpeg_header_data
    Returns:xmp_datathe string of raw XML text
    FALSEif an APP 1 XMP segment could not be found, or if an error occured
    - - -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Function:put_XMP_text
    Description: - Adds or modifies the Extensible Metadata Platform (XMP) information - in an App1 JPEG segment. If a XMP segment already exists, it is - replaced, otherwise a new one is inserted, using the supplied data. - Uses information supplied by the get_jpeg_header_data function -
    Parameters:jpeg_header_dataa JPEG header data array in the same format as from get_jpeg_header_data
    newXMPa string containing the XMP text to be stored in the XMP segment. Should be constructed using the write_XMP_array_to_text function
    Returns:jpeg_header_datathe JPEG header data array with the XMP segment added.
    FALSEif an error occured
    - - -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - -
    Function:read_XMP_array_from_text
    Description: -

    - An alias for read_xml_array_from_text.
    - Parses a string containing XMP data (XML), and returns the resulting - tree structure array, which contains all the XMP (XML) information.
    - Note: White space and comments in the XMP data (XML) are ignored
    - Note: All text information contained in the tree structure
    - is encoded as Unicode UTF-8. Hence text will appear as - normal ASCII except where there is an extended character. -

    -
    Parameters:xmptexta string containing the XMP data (XML) to be parsed
    Returns:outputthe tree structure array containing the XMP (XML) information
    FALSEif an error occured
    - - -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - -
    Function:write_XMP_array_to_text
    Description: -

    - Takes a tree structure array containing XMP (in the same format - as returned by read_XMP_array_from_text, and constructs a string - containing the equivalent XMP, including the XMP Packet header - and trailer. Produces XMP text which has correct indents, encoded - using UTF-8.
    - Note: All text information contained in the tree structure - can be either 7-bit ASCII or encoded as Unicode UTF-8, - since UTF-8 passes 7-bit ASCII text unchanged. -

    -
    Parameters:xmparraythe tree structure array containing the information to be converted to XMP text
    Returns:output_XMP_textthe string containing the equivalent XMP text
    - - -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - -
    Function:Interpret_XMP_to_HTML
    Description: -

    - Generates html showing the information contained in an Extensible - Metadata Platform (XMP) tree structure array, as retrieved - with read_XMP_array_from_text -

    -
    Parameters:XMP_arraya XMP tree structure array as from read_XMP_array_from_text
    Returns:outputthe HTML string
    - - -
    -
    -
    -
    - - -
    - - - \ No newline at end of file diff --git a/jpeg_metadata_tk/get_JFXX_thumb.php b/jpeg_metadata_tk/get_JFXX_thumb.php deleted file mode 100644 index 801417d..0000000 --- a/jpeg_metadata_tk/get_JFXX_thumb.php +++ /dev/null @@ -1,109 +0,0 @@ - -* -* Author: Evan Hunter -* -* Date: 23/7/2004 -* -* Project: PHP JPEG Metadata Toolkit -* -* Revision: 1.00 -* -* URL: http://electronics.ozhiker.com -* -* Copyright: Copyright Evan Hunter 2004 -* -* License: This file is part of the PHP JPEG Metadata Toolkit. -* -* The PHP JPEG Metadata Toolkit is free software; you can -* redistribute it and/or modify it under the terms of the -* GNU General Public License as published by the Free Software -* Foundation; either version 2 of the License, or (at your -* option) any later version. -* -* The PHP JPEG Metadata Toolkit is distributed in the hope -* that it will be useful, but WITHOUT ANY WARRANTY; without -* even the implied warranty of MERCHANTABILITY or FITNESS -* FOR A PARTICULAR PURPOSE. See the GNU General Public License -* for more details. -* -* You should have received a copy of the GNU General Public -* License along with the PHP JPEG Metadata Toolkit; if not, -* write to the Free Software Foundation, Inc., 59 Temple -* Place, Suite 330, Boston, MA 02111-1307 USA -* -* If you require a different license for commercial or other -* purposes, please contact the author: evan@ozhiker.com -* -******************************************************************************/ - - - // Ensure that nothing can write to the standard io, before we get the header out - ob_start( ); - - - require_once 'JPEG.php'; - require_once 'JFIF.php'; - - - // retrieve the filename from the URL parameters - - $filename = $GLOBALS['HTTP_GET_VARS']['filename']; - - // Retrieve the JPEG header Data - - $jpeg_header_data = get_jpeg_header_data( $filename ); - - // Retrieve any JFXX data in the file - - $JFXX_array = get_JFXX( $jpeg_header_data ); - - // Check if JFXX data was retrieved - - if ( $JFXX_array === FALSE ) - { - // No JFXX data could be retrieved - abort - ob_end_clean ( ); - echo "

    JFXX Data could not be retrieved

    \n"; - return; - } - - // Check the JFXX extension code which indicates what format - // the thumbnail is encoded with - - if ( $JFXX_array['Extension_Code'] == 0x10 ) // JPEG Encoding - { - // JPEG Encoding - Output JPEG Data - ob_end_clean ( ); - header("Content-type: image/jpeg"); - print $JFXX_array['ThumbData']; - return; - } - else if ( $JFXX_array['Extension_Code'] == 0x11 ) // One Byte Per Pixel Encoding - { - // TODO: Implement decoding of One Byte Per Pixel encoded JFXX Thumbnail - return; - } - else if ( $JFXX_array['Extension_Code'] == 0x13 ) // Three Bytes Per Pixel Encoding - { - // TODO: Implement decoding of Three Bytes Per Pixel encoded JFXX Thumbnail - return; - } - else - { - // Invalid Extension Value - abort - return; - } - - - -?> diff --git a/jpeg_metadata_tk/get_casio_thumb.php b/jpeg_metadata_tk/get_casio_thumb.php deleted file mode 100644 index f75b558..0000000 --- a/jpeg_metadata_tk/get_casio_thumb.php +++ /dev/null @@ -1,126 +0,0 @@ - -* -* Author: Evan Hunter -* -* Date: 23/7/2004 -* -* Project: PHP JPEG Metadata Toolkit -* -* Revision: 1.00 -* -* URL: http://electronics.ozhiker.com -* -* Copyright: Copyright Evan Hunter 2004 -* -* License: This file is part of the PHP JPEG Metadata Toolkit. -* -* The PHP JPEG Metadata Toolkit is free software; you can -* redistribute it and/or modify it under the terms of the -* GNU General Public License as published by the Free Software -* Foundation; either version 2 of the License, or (at your -* option) any later version. -* -* The PHP JPEG Metadata Toolkit is distributed in the hope -* that it will be useful, but WITHOUT ANY WARRANTY; without -* even the implied warranty of MERCHANTABILITY or FITNESS -* FOR A PARTICULAR PURPOSE. See the GNU General Public License -* for more details. -* -* You should have received a copy of the GNU General Public -* License along with the PHP JPEG Metadata Toolkit; if not, -* write to the Free Software Foundation, Inc., 59 Temple -* Place, Suite 330, Boston, MA 02111-1307 USA -* -* If you require a different license for commercial or other -* purposes, please contact the author: evan@ozhiker.com -* -******************************************************************************/ - - // Ensure that nothing can write to the standard io, before we get the header out - ob_start( ); - - - require_once 'EXIF.php'; - - - // retrieve the filename from the URL parameters - - $filename = $GLOBALS['HTTP_GET_VARS']['filename']; - - - // Retrieve any EXIF data in the file - - $Exif_array = get_EXIF_JPEG( $filename ); - - // Check if any EXIF data was retrieved - if ( $Exif_array === FALSE ) - { - // No EXIF data was found - abort - ob_end_clean ( ); - echo "

    Error getting EXIF Information

    \n"; - return; - } - - - // Check that there is at least the Zeroth IFD in the array - if ( count( $Exif_array ) < 1 ) - { - // Couldn't find the zeroth IFD - return; - } - - - // Check that the EXIF IFD exists - if ( array_key_exists( 34665, $Exif_array[0] ) ) - { - // Found the EXIF IFD, - - // Check that the makernote tag exists - if ( array_key_exists( 37500, $Exif_array[0][34665]['Data'][0] ) ) - { - // Makernote Exists - - // Check that the Makernote is Casio Type 2 - if ( $Exif_array[0][34665]['Data'][0][37500]['Makernote Tags'] == "Casio Type 2" ) - { - // Makernote is Casio Type 2 - // Check if an IFD exists for the makernote - if ( array_key_exists( 0, $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'] ) ) - { - // Check if the Thumbnail offset tag 8192 exists - if ( array_key_exists( 8192, $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'][0] ) ) - { - // Found Thumbnail - Display it - ob_end_clean ( ); - header("Content-type: image/jpeg"); - print $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'][0][8192]['Data']; - } - // Check if the Thumbnail offset tag 4 exists - else if ( array_key_exists( 4, $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'][0] ) ) - { - // Found Thumbnail - Display it - ob_end_clean ( ); - header("Content-type: image/jpeg"); - print $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'][0][4]['Data']; - } - } - - } - } - } - else - { - } - - return; - -?> diff --git a/jpeg_metadata_tk/get_exif_thumb.php b/jpeg_metadata_tk/get_exif_thumb.php deleted file mode 100644 index 0f40090..0000000 --- a/jpeg_metadata_tk/get_exif_thumb.php +++ /dev/null @@ -1,98 +0,0 @@ - -* -* Author: Evan Hunter -* -* Date: 23/7/2004 -* -* Project: PHP JPEG Metadata Toolkit -* -* Revision: 1.00 -* -* URL: http://electronics.ozhiker.com -* -* Copyright: Copyright Evan Hunter 2004 -* -* License: This file is part of the PHP JPEG Metadata Toolkit. -* -* The PHP JPEG Metadata Toolkit is free software; you can -* redistribute it and/or modify it under the terms of the -* GNU General Public License as published by the Free Software -* Foundation; either version 2 of the License, or (at your -* option) any later version. -* -* The PHP JPEG Metadata Toolkit is distributed in the hope -* that it will be useful, but WITHOUT ANY WARRANTY; without -* even the implied warranty of MERCHANTABILITY or FITNESS -* FOR A PARTICULAR PURPOSE. See the GNU General Public License -* for more details. -* -* You should have received a copy of the GNU General Public -* License along with the PHP JPEG Metadata Toolkit; if not, -* write to the Free Software Foundation, Inc., 59 Temple -* Place, Suite 330, Boston, MA 02111-1307 USA -* -* If you require a different license for commercial or other -* purposes, please contact the author: evan@ozhiker.com -* -******************************************************************************/ - - // Ensure that nothing can write to the standard io, before we get the header out - ob_start( ); - - - require_once 'JPEG.php'; - require_once 'EXIF.php'; - - - // retrieve the filename from the URL parameters - - $filename = $GLOBALS['HTTP_GET_VARS']['filename']; - - // Retrieve any EXIF data in the file - - $Exif_array = get_EXIF_JPEG( $filename ); - - // Check if EXIF data was retrieved - - if ( $Exif_array === FALSE ) - { - // No EXIF data could be retrieved - abort - ob_end_clean ( ); - echo "

    EXIF segment could not be retrieved

    \n"; - return; - } - - - // Check if the First IFD exists ( The First IFD is actually the second, since there is also the zeroth IFD) - if ( count( $Exif_array ) < 2 ) - { - ob_end_clean ( ); - echo "

    Couldn't find Thumbnail IFD

    \n"; - return; - } - - // Check if the First IFD contains the Thumbnail tag - if ( array_key_exists( 513, $Exif_array[1] ) ) - { - // Output the thumbnail - ob_end_clean ( ); - header("Content-type: image/jpeg"); - print $Exif_array[1][513]['Data']; - } - else - { - ob_end_clean ( ); - echo "

    Couldn't find Thumbnail Tag

    \n"; - return; - } - -?> diff --git a/jpeg_metadata_tk/get_minolta_thumb.php b/jpeg_metadata_tk/get_minolta_thumb.php deleted file mode 100644 index eecea8a..0000000 --- a/jpeg_metadata_tk/get_minolta_thumb.php +++ /dev/null @@ -1,195 +0,0 @@ - -* -* Author: Evan Hunter -* -* Date: 23/7/2004 -* -* Project: PHP JPEG Metadata Toolkit -* -* Revision: 1.00 -* -* URL: http://electronics.ozhiker.com -* -* Copyright: Copyright Evan Hunter 2004 -* -* License: This file is part of the PHP JPEG Metadata Toolkit. -* -* The PHP JPEG Metadata Toolkit is free software; you can -* redistribute it and/or modify it under the terms of the -* GNU General Public License as published by the Free Software -* Foundation; either version 2 of the License, or (at your -* option) any later version. -* -* The PHP JPEG Metadata Toolkit is distributed in the hope -* that it will be useful, but WITHOUT ANY WARRANTY; without -* even the implied warranty of MERCHANTABILITY or FITNESS -* FOR A PARTICULAR PURPOSE. See the GNU General Public License -* for more details. -* -* You should have received a copy of the GNU General Public -* License along with the PHP JPEG Metadata Toolkit; if not, -* write to the Free Software Foundation, Inc., 59 Temple -* Place, Suite 330, Boston, MA 02111-1307 USA -* -* If you require a different license for commercial or other -* purposes, please contact the author: evan@ozhiker.com -* -******************************************************************************/ - - - // Ensure that nothing can write to the standard io, before we get the header out - ob_start( ); - - - require_once 'EXIF.php'; - - - // retrieve the filename from the URL parameters - - $filename = $GLOBALS['HTTP_GET_VARS']['filename']; - - // Retrieve any EXIF data in the file - - $Exif_array = get_EXIF_JPEG( $filename ); - - - // Check if any EXIF data was retrieved - if ( $Exif_array === FALSE ) - { - // No EXIF data was found - abort - ob_end_clean ( ); - echo "

    Error getting EXIF Information

    \n"; - return; - } - - - // Check that there is at least the Zeroth IFD in the array - if ( count( $Exif_array ) < 1 ) - { - ob_end_clean ( ); - echo "

    Couldn't find TIFF IFD 0

    \n"; - return; - } - - - - // Check that the EXIF IFD exists - if ( array_key_exists( 34665, $Exif_array[0] ) ) - { - // Found the EXIF IFD, - - // Check that the makernote tag exists - if ( array_key_exists( 37500, $Exif_array[0][34665]['Data'][0] ) ) - { - // Makernote Exists - - // Check that the Makernote is Olympus - if ( $Exif_array[0][34665]['Data'][0][37500]['Makernote Tags'] == "Olympus" ) - { - // Makernote is Olympus - // Check if an IFD exists for the makernote - if ( array_key_exists( 0, $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'] ) ) - { - // Check if the Thumbnail tag 0x0088 exists - if ( array_key_exists( 0x0088, $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'][0] ) ) - { - // Found a Thumbnail - // Get the data - $data = $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'][0][0x0088]['Data']; - - // Sometimes the Minolta thumbnails are corrupt as there is no data - // Check that the data is OK - if ( $data !== FALSE ) - { - // Minolta thumbnails seem to have the first byte incorrect - this could possibly be a counter in case the thumbnail needs to span more than one tag - // Restore the first byte of the jpeg thumbnail - $data{0} = "\xff"; - - // Display the thumbnail - ob_end_clean ( ); - header("Content-type: image/jpeg"); - print $data; - } - else - { - // Thumbnail data is missing - display message - ob_end_clean ( ); - echo "

    Thumbnail missing

    \n"; - } - } - // Check if the Thumbnail tag 0x0081 exists - else if ( array_key_exists( 0x0081, $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'][0] ) ) - { - // Found a Thumbnail - // Get the data - $data = $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'][0][0x0081]['Data']; - - // Sometimes the Minolta thumbnails are corrupt as there is no data - // Check that the data is OK - if ( $data !== FALSE ) - { - // Minolta thumbnails seem to have the first byte incorrect - this could possibly be a counter in case the thumbnail needs to span more than one tag - // Restore the first byte of the jpeg thumbnail - $data{0} = "\xff"; - - // Display the thumbnail - ob_end_clean ( ); - header("Content-type: image/jpeg"); - print $data ; - } - else - { - // Thumbnail data is missing - display message - ob_end_clean ( ); - echo "

    Thumbnail missing

    \n"; - } - } - else - { - // Couldn't find a Minolta thumbnail tag - display message - ob_end_clean ( ); - echo "

    Couldn't find Minolta Thumbnail Tag

    \n"; - } - } - else - { - // Couldn't find an IFD in the Makernote tag - display message - ob_end_clean ( ); - echo "

    Makernote Doesn't contain IFD 0

    \n"; - } - - } - else - { - // Makernote does not use Olympus tags - display message - ob_end_clean ( ); - echo "

    Not an Olympus Makernote

    \n"; - } - } - else - { - // Couldn't find Makernote tag - display message - ob_end_clean ( ); - echo "

    Couldn't find Makernote

    \n"; - } - } - else - { - // Couldn't find the EXIF IFD - display message - ob_end_clean ( ); - echo "

    Couldn't find Exif IFD

    \n"; - } - - - return; - -?> diff --git a/jpeg_metadata_tk/get_ps_thumb.php b/jpeg_metadata_tk/get_ps_thumb.php deleted file mode 100644 index 966c328..0000000 --- a/jpeg_metadata_tk/get_ps_thumb.php +++ /dev/null @@ -1,176 +0,0 @@ - -* -* Author: Evan Hunter -* -* Date: 23/7/2004 -* -* Project: PHP JPEG Metadata Toolkit -* -* Revision: 1.11 -* -* 1.00 -> 1.11 : Added support for Photoshop IRB thumbnails which are -* embedded within EXIF information (used in TIFF files) -* -* URL: http://electronics.ozhiker.com -* -* Copyright: Copyright Evan Hunter 2004 -* -* License: This file is part of the PHP JPEG Metadata Toolkit. -* -* The PHP JPEG Metadata Toolkit is free software; you can -* redistribute it and/or modify it under the terms of the -* GNU General Public License as published by the Free Software -* Foundation; either version 2 of the License, or (at your -* option) any later version. -* -* The PHP JPEG Metadata Toolkit is distributed in the hope -* that it will be useful, but WITHOUT ANY WARRANTY; without -* even the implied warranty of MERCHANTABILITY or FITNESS -* FOR A PARTICULAR PURPOSE. See the GNU General Public License -* for more details. -* -* You should have received a copy of the GNU General Public -* License along with the PHP JPEG Metadata Toolkit; if not, -* write to the Free Software Foundation, Inc., 59 Temple -* Place, Suite 330, Boston, MA 02111-1307 USA -* -* If you require a different license for commercial or other -* purposes, please contact the author: evan@ozhiker.com -* -******************************************************************************/ - - // Ensure that nothing can write to the standard io, before we get the header out - ob_start( ); - - - // retrieve the filename from the URL parameters - - $filename = $GLOBALS['HTTP_GET_VARS']['filename']; - - // Change: Check for file extension rather than assuming JPEG as of 1.11 - // Retrieve the Filename Extension - $path_parts = pathinfo( $filename ); - - // Check if the Extension is JPEG - if ( ( strcasecmp( $path_parts["extension"], "jpg" ) == 0 ) || - ( strcasecmp( $path_parts["extension"], "jpeg" ) == 0 ) ) - { - // JPEG Extension - - require_once 'JPEG.php'; - require_once 'Photoshop_IRB.php'; - - // Retrieve the JPEG header Data - - $jpeg_header_data = get_jpeg_header_data( $filename ); - - // Retrieve any Photoshop IRB data in the file - - $IRB_array = get_Photoshop_IRB( $jpeg_header_data ); - - // Check if Photoshop IRB data was retrieved - - if ( $IRB_array === FALSE ) - { - // No Photoshop IRB data could be retrieved - abort - ob_end_clean ( ); - echo "

    Photoshop IRB could not be retrieved from the JPEG file

    \n"; - return; - } - - // Cycle through the resources in the Photoshop IRB - // Until either a thumbnail resource is found or - // there are no more resources - $i = 0; - while ( ( $i < count( $IRB_array ) ) && - ( $IRB_array[$i]['ResID'] != 0x0409 ) && - ( $IRB_array[$i]['ResID'] != 0x040C ) ) - { - $i++; - } - - - // Check if a thumbnail was found - if ( $i < count( $IRB_array ) ) - { - // A thumbnail was found, Display it - ob_end_clean ( ); - header("Content-type: image/jpeg"); - print substr( $IRB_array[$i]['ResData'] , 28 ); - } - } - // Change: Add support for TIFF Photoshop IRB thumbnails as of 1.11 - // Check if file has TIFF extension - else if ( ( strcasecmp( $path_parts["extension"], "tif" ) == 0 ) || - ( strcasecmp( $path_parts["extension"], "tiff" ) == 0 ) ) - { - // TIFF Extension - - require_once 'EXIF.php'; - - // Retrieve the EXIF info - $exif_array = get_EXIF_TIFF( $filename ); - - // Retrieve any Photoshop IRB data in the EXIF - if ( ( array_key_exists( 0, $exif_array ) ) && - ( array_key_exists( 34377, $exif_array[0] ) ) && - ( array_key_exists( 'Data', $exif_array[0][34377] ) ) ) - { - $IRB_array = $exif_array[0][34377]['Data']; - - // Check if Photoshop IRB data was retrieved - - if ( $IRB_array === FALSE ) - { - // No Photoshop IRB data could be retrieved - abort - ob_end_clean ( ); - echo "

    Photoshop IRB could not be retrieved from the TIFF file

    \n"; - return; - } - - // Cycle through the resources in the Photoshop IRB - // Until either a thumbnail resource is found or - // there are no more resources - $i = 0; - while ( ( $i < count( $IRB_array ) ) && - ( $IRB_array[$i]['ResID'] != 0x0409 ) && - ( $IRB_array[$i]['ResID'] != 0x040C ) ) - { - $i++; - } - - - // Check if a thumbnail was found - if ( $i < count( $IRB_array ) ) - { - // A thumbnail was found, Display it - ob_end_clean ( ); - header("Content-type: image/jpeg"); - print substr( $IRB_array[$i]['ResData'] , 28 ); - } - } - else - { - // Embedded Photoshop IRB block not found - ob_end_clean ( ); - echo "No Photoshop IRB found within EXIF"; - } - } - else - { - // Unknown extension - ob_end_clean ( ); - echo "Unknown file Type"; - } - - -?> diff --git a/jpeg_metadata_tk/pjmt_utils.php b/jpeg_metadata_tk/pjmt_utils.php deleted file mode 100644 index 07579b3..0000000 --- a/jpeg_metadata_tk/pjmt_utils.php +++ /dev/null @@ -1,150 +0,0 @@ - \ No newline at end of file diff --git a/mailman.cfg b/mailman.cfg deleted file mode 100644 index 4a087e3..0000000 --- a/mailman.cfg +++ /dev/null @@ -1,15 +0,0 @@ -private_roster = 2 -send_reminders = False -max_message_size = 0 -convert_html_to_plaintext = False -default_member_moderation = True -generic_nonmember_action = 3 -msg_footer = """_______________________________________________ -%(real_name)s mailing list -%(real_name)s@%(host_name)s -""" -digest_footer = """_______________________________________________ -%(real_name)s mailing list -%(real_name)s@%(host_name)s -""" -mlist.Save() diff --git a/mailman_lib.php b/mailman_lib.php deleted file mode 100644 index e654ebf..0000000 --- a/mailman_lib.php +++ /dev/null @@ -1,366 +0,0 @@ - spider -*/ - -function mailman_verify_list( $pListName ) { - $error = NULL; - if( $matches = preg_match( '/[^A-Za-z0-9\-\_]/', $pListName ) ) { - $error = tra( 'Invalid mailing list name' ).': '.tra( 'List names can only contain letters and numbers' ); - } else { - $lists = mailman_list_lists(); - if( !empty( $lists[strtolower($pListName)] ) ) { - $error = tra( 'Invalid mailing list name' ).': '.tra( 'List already exists' ); - } - } - return $error; -} - -function mailman_list_lists() { - $ret = array(); - if( $ret_code = mailman_command( 'list_lists', $output) ) { - mailman_fatal(tra('Unable to list lists.'), $ret_code); - } - else { - foreach( $output as $o ) { - if( strpos( $o, '-' ) ) { - list( $name, $desc ) = split( '-', $o ); - $ret[strtolower( trim( $name ) )] = trim( $desc ); - } - } - } - return( $ret ); -} - - -function mailman_list_members( $pListName ) { - $ret = array(); - $options = escapeshellarg( $pListName ); - if( $ret = mailman_command( 'list_members', $output, $options ) ) { - // mailman_fatal(tra('Unable to get members for list: ').$pListName, $ret); - } - return( $output ); -} - -// pParamHash follows naming convention off config_list --help usage instructions -function mailman_config_list( $pParamHash ){ - $options = ' -i '.escapeshellarg(UTIL_PKG_PATH.'mailman.cfg'); - $options .= ' '.escapeshellarg( $pParamHash['listname'] ); - if( $ret = mailman_command( 'config_list', $output, $options) ) { - return (tra('Unable to configure list: ').$pParamHash['listname'].":".$ret); - } -} - -// pParamHash follows naming convention off newlist --help usage instructions -function mailman_newlist( $pParamHash ) { - $error = NULL; - if( !($error = mailman_verify_list( $pParamHash['listname'] )) ) { - $options = ""; - if( !empty( $pParamHash['listhost'] ) ) { - $options .= ' -e '.escapeshellarg( $pParamHash['listhost'] ); - } - $options .= ' -q '.escapeshellarg( $pParamHash['listname'] ); - $options .= ' '.escapeshellarg( $pParamHash['listadmin-addr'] ).' '; - $options .= ' '.escapeshellarg( $pParamHash['admin-password'] ).' '; - - if( $ret = mailman_command( 'newlist', $output, $options ) ) { - return (tra('Unable to create list: ').$pParamHash['listname'].":".$ret); - } - - $options = ' -i '.escapeshellarg(UTIL_PKG_PATH.'mailman.cfg'); - $options .= ' '.escapeshellarg( $pParamHash['listname'] ); - if( $ret = mailman_command( 'config_list', $output, $options) ) { - // @TODO if this fails we should roll back the newlist created - return (tra('Unable to configure list: ').$pParamHash['listname'].":".$ret); - } - - $newList = $pParamHash['listname']; - $mailman = mailman_get_mailman_command(); - $newAliases = " -## $newList mailing list -$newList: \"|$mailman post $newList\" -$newList-admin: \"|$mailman admin $newList\" -$newList-bounces: \"|$mailman bounces $newList\" -$newList-confirm: \"|$mailman confirm $newList\" -$newList-join: \"|$mailman join $newList\" -$newList-leave: \"|$mailman leave $newList\" -$newList-owner: \"|$mailman owner $newList\" -$newList-request: \"|$mailman request $newList\" -$newList-subscribe: \"|$mailman subscribe $newList\" -$newList-unsubscribe: \"|$mailman unsubscribe $newList\""; - - // Make sure we unlock the semaphore - ignore_user_abort(true); - // Get a lock. flock is not reliable (NFS, FAT, etc) - // so we use a semaphore instead. - $sem_key = ftok(mailman_get_aliases_file(), 'm'); - if( $sem_key != -1 ) { - // Get the semaphore - $sem = sem_get($sem_key); - if( $sem ) { - if( sem_acquire($sem) ) { - if( $fh = fopen( mailman_get_aliases_file(), 'a' ) ) { - fwrite( $fh, $newAliases ); - fclose( $fh ); - mailman_newalias(); - } else { - $error = "Could not open /etc/aliases for appending."; - } - if( !sem_release($sem) ) { - $error = "Error releasing a sempahore."; - } - } else { - $error = "Unable to aquire a semaphore."; - } - } else { - $error = "Unable to get a semaphore."; - } - } else { - $error = "Couldn't create semaphore key."; - } - // Let the user cancel again - ignore_user_abort(false); - } - return $error; -} - -function mailman_remove_member( $pListName, $pEmail ) { - $ret = ''; - if( $fullCommand = mailman_get_command( 'remove_members' ) ) { - $cmd = "echo ".escapeshellarg( $pEmail )." | $fullCommand -f - ".escapeshellarg( $pListName ); - exec( $cmd, $ret ); - } else { - bit_error_log( 'Groups mailman command failed (remove_members) File not found: '.$fullCommand ); - } -} - -function mailman_setmoderated( $pListName, $pModerated = TRUE ) { - $ret = FALSE; - if( $fullCommand = mailman_get_command( 'withlist' ) ) { - $cmd = $fullCommand." -q -l -r mailman_lib.setDefaultModerationFlag ".escapeshellarg( $pListName )." ".( $pModerated ? 1 : 0 ); - $cmd = "/bin/sh -c \"PYTHONPATH=".UTIL_PKG_PATH." $cmd\""; - exec( $cmd, $ret ); - } else { - bit_error_log( 'Groups mailman command failed (withlist) File not found: '.$fullCommand ); - } - return $ret; -} - -function mailman_setmoderator( $pListName, $pEmail ) { - $ret = ''; - if( $fullCommand = mailman_get_command( 'withlist' ) ) { - $cmd = $fullCommand." -q -l -r mailman_lib.setMemberModeratedFlag ".escapeshellarg( $pListName )." ".escapeshellarg( $pEmail ); - $cmd = "/bin/sh -c \"PYTHONPATH=".UTIL_PKG_PATH." $cmd\""; - exec( $cmd, $ret ); - } else { - bit_error_log( 'Groups mailman command failed (withlist) File not found: '.$fullCommand ); - } - return $ret; -} - -function mailman_addmember( $pListName, $pEmail, $pType = NULL ) { - $ret = ''; - if( $fullCommand = mailman_get_command( 'add_members' ) ) { - switch( $pType){ - case "digest": - $cmd = "echo ".escapeshellarg( $pEmail )." | $fullCommand -d - ".escapeshellarg( $pListName ); - break; - case "email": - default: - $cmd = "echo ".escapeshellarg( $pEmail )." | $fullCommand -r - ".escapeshellarg( $pListName ); - break; - } - exec( $cmd, $ret ); - - /** - * its not enough to rely on the add_members call - * add_members is ignored by mailman if the user is already a member - * bw relies on mailman_addmember to toggle if a user wants to receive a digest or not - * to keep things simple in bw we conveniently handle the toggle here - **/ - if( !empty( $pType ) ){ - mailman_setsubscriptiontype( $pListName, $pEmail, $pType ); - } - } else { - bit_error_log( 'Groups mailman command failed (add_members) File not found: '.$fullCommand ); - } -} - -function mailman_findmember( $pListName, $pEmail ) { - $options = ' -l '.escapeshellarg( $pListName ).' '.escapeshellarg( $pEmail ); - if( $ret = mailman_command( 'find_member', $output, $options ) ) { - mailman_fatal(tra('Unable to find member in list: ').$pListName, $ret); - } - return $output; -} - -function mailman_setsubscriptiontype( $pListName, $pEmail, $pType ) { - if( $fullCommand = mailman_get_command( 'withlist' ) ) { - $cmd = $fullCommand." -q -l -r mailman_lib.setSubscriptionType ".escapeshellarg( $pListName )." ".escapeshellarg( $pEmail )." ".( $pType == 'digest' ? 1 : 0 ); - $cmd = "/bin/sh -c \"PYTHONPATH=".UTIL_PKG_PATH." $cmd\""; - exec( $cmd, $ret ); - }else{ - bit_error_log( 'Groups mailman command failed (withlist) File not found: '.$fullCommand ); - } - return $ret; -} - -function mailman_getsubscriptiontype( $pListName, $pEmail ){ - // even though setSubscriptionType returns a string or false we return an array because thats what exec returns - if( $fullCommand = mailman_get_command( 'withlist' ) ) { - $cmd = $fullCommand." -l -r mailman_lib.getSubscriptionType ".escapeshellarg( $pListName )." ".escapeshellarg( $pEmail ); - $cmd = "/bin/sh -c \"PYTHONPATH=".UTIL_PKG_PATH." $cmd\""; - exec( $cmd, $ret ); - }else{ - bit_error_log( 'Groups mailman command failed (withlist) File not found: '.$fullCommand ); - } - return $ret; -} - -function mailman_rmlist( $pListName ) { - $error = NULL; - if( mailman_verify_list( $pListName ) ) { - $options = ' -a '.escapeshellarg( $pListName ); - if( $ret = mailman_command( 'rmlist', $output, $options ) ) { - mailman_fatal(tra('Unable to remove list: ').$pListName, $ret); - } - - $newList = $pListName; - - $mailman = mailman_get_mailman_command(); - - $aliasesLines = array( - "## $newList mailing list", - "$newList", - "$newList-admin", - "$newList-bounces", - "$newList-confirm", - "$newList-join", - "$newList-leave", - "$newList-owner", - "$newList-request", - "$newList-subscribe", - "$newList-unsubscribe" - ); - // Make sure we unlock the semaphore - ignore_user_abort(true); - // Get a lock. flock is not reliable (NFS, FAT, etc) - // so we use a semaphore instead. - $sem_key = ftok(mailman_get_aliases_file(), 'm'); - if( $sem_key != -1 ) { - // Get the semaphore - $sem = sem_get($sem_key); - if( $sem ) { - if( sem_acquire($sem) ) { - if( $fh = fopen( mailman_get_aliases_file(), 'r+' ) ) { - // cull out all aliase lines for the mailing list and rewrite the file - $newContents = ''; - while( $line = fgets( $fh ) ) { - @list( $alias, $value ) = split( ':', $line ); - $alias = trim( $alias ); - if( !in_array($alias, $aliasesLines) ) { - $newContents .= $line; - } - } - - // Truncate the file - if( ftruncate($fh, 0) != 1) { - $error = "Unable to truncate /etc/aliases"; - } else { - if( !rewind($fh) ) { - $error = "Unable to seek /etc/aliases"; - } - else { - if( empty( $newContents ) ) { - $error = "Empty aliases for /etc/aliases"; - } elseif( !fwrite( $fh, $newContents ) ) { - $error = "Could not write new /etc/aliases"; - } - } - } - - fclose( $fh ); - mailman_newalias(); - } else { - $error = "Could not open /etc/aliases for appending."; - } - - if( !sem_release($sem) ) { - $error = "Error releasing a sempahore."; - } - } else { - $error = "Unable to aquire a semaphore."; - } - } else { - $error = "Unable to get a semaphore."; - } - } else { - $error = "Couldn't create semaphore key."; - } - // Let the user cancel again - ignore_user_abort(false); - } - return $error; -} - -function mailman_newalias() { - global $gBitSystem; - exec( $gBitSystem->getConfig('server_newaliases_cmd', '/usr/bin/newaliases' )); -} - -function mailman_get_aliases_file() { - global $gBitSystem; - return $gBitSystem->getConfig('server_aliases_file', '/etc/aliases'); -} - -function mailman_command( $pCommand, &$output, $pOptions=NULL ) { - $ret_code = NULL; - if( $fullCommand = mailman_get_command( $pCommand ) ) { - $cmd = $fullCommand.' '.$pOptions; - if( !defined( 'IS_LIVE' ) || !IS_LIVE ) { - bit_error_log( 'mailman LOG: '.$cmd ); - } - exec( $cmd, $output, $ret_code ); - if( $ret_code ) { - bit_error_log('Error running command: '. $cmd . ' Command returned: '.$ret_code); - } - } else { - bit_error_log( 'Groups mailman command failed ('.$pCommand.'): File not found: '.$fullCommand ); - } - return $ret_code; -} - -function mailman_fatal($pMessage, $pRetCode) { - global $gBitSystem; - $gBitSystem->fatalError($pMessage.tra(' Command returned: ').$pRetCode); - die; -} - -function mailman_get_mailman_command() { - global $gBitSystem; - $mailman = $gBitSystem->getConfig( 'server_mailman_cmd', '/usr/lib/mailman/mail/mailman' ); - return $mailman; -} - -function mailman_get_command( $pCommand ) { - global $gBitSystem; - $ret = NULL; - // Support for legacy configurations - $fullCommand = $gBitSystem->getConfig( 'server_mailman_bin' ) .'/'.$pCommand; - $fullCommand = str_replace( '//', '/', $fullCommand ); - if( file_exists( $fullCommand ) ) { - $ret = $fullCommand; - } - return $ret; -} - -?> diff --git a/mailman_lib.py b/mailman_lib.py deleted file mode 100644 index 8301873..0000000 --- a/mailman_lib.py +++ /dev/null @@ -1,27 +0,0 @@ -from Mailman import mm_cfg -from Mailman.Errors import NotAMemberError -from Mailman.mm_cfg import Digests - -def setMemberModeratedFlag (mlist, addr): - mlist.moderator.append(addr) - mlist.Save() - -def setDefaultModerationFlag(mlist, val): - mlist.default_member_moderation = int(val); - for member in mlist.getMembers(): - mlist.setMemberOption(member, mm_cfg.Moderate, int(val)) - mlist.Save() - -def getSubscriptionType(mlist, addr): - try: - if mlist.getMemberOption(addr, Digests): - print "digest" - else: - print "email" - except NotAMemberError: - print 0 - -def setSubscriptionType(mlist, addr, val): - mlist.setMemberOption(addr, mm_cfg.Digests, int(val)) - mlist.Save() - diff --git a/markdown.php b/markdown.php deleted file mode 100644 index 10e7954..0000000 --- a/markdown.php +++ /dev/null @@ -1,1732 +0,0 @@ - -# -# Original Markdown -# Copyright (c) 2004-2006 John Gruber -# -# - - -define( 'MARKDOWN_VERSION', "1.0.1n" ); # Sat 10 Oct 2009 - - -# -# Global default settings: -# - -# Change to ">" for HTML output -@define( 'MARKDOWN_EMPTY_ELEMENT_SUFFIX', " />"); - -# Define the width of a tab for code blocks. -@define( 'MARKDOWN_TAB_WIDTH', 4 ); - - -# -# WordPress settings: -# - -# Change to false to remove Markdown from posts and/or comments. -@define( 'MARKDOWN_WP_POSTS', true ); -@define( 'MARKDOWN_WP_COMMENTS', true ); - - - -### Standard Function Interface ### - -@define( 'MARKDOWN_PARSER_CLASS', 'Markdown_Parser' ); - -function Markdown($text) { -# -# Initialize the parser and return the result of its transform method. -# - # Setup static parser variable. - static $parser; - if (!isset($parser)) { - $parser_class = MARKDOWN_PARSER_CLASS; - $parser = new $parser_class; - } - - # Transform text using parser. - return $parser->transform($text); -} - - -### WordPress Plugin Interface ### - -/* -Plugin Name: Markdown -Plugin URI: http://michelf.com/projects/php-markdown/ -Description: Markdown syntax allows you to write using an easy-to-read, easy-to-write plain text format. Based on the original Perl version by John Gruber. More... -Version: 1.0.1n -Author: Michel Fortin -Author URI: http://michelf.com/ -*/ - -if (isset($wp_version)) { - # More details about how it works here: - # - - # Post content and excerpts - # - Remove WordPress paragraph generator. - # - Run Markdown on excerpt, then remove all tags. - # - Add paragraph tag around the excerpt, but remove it for the excerpt rss. - if (MARKDOWN_WP_POSTS) { - remove_filter('the_content', 'wpautop'); - remove_filter('the_content_rss', 'wpautop'); - remove_filter('the_excerpt', 'wpautop'); - add_filter('the_content', 'Markdown', 6); - add_filter('the_content_rss', 'Markdown', 6); - add_filter('get_the_excerpt', 'Markdown', 6); - add_filter('get_the_excerpt', 'trim', 7); - add_filter('the_excerpt', 'mdwp_add_p'); - add_filter('the_excerpt_rss', 'mdwp_strip_p'); - - remove_filter('content_save_pre', 'balanceTags', 50); - remove_filter('excerpt_save_pre', 'balanceTags', 50); - add_filter('the_content', 'balanceTags', 50); - add_filter('get_the_excerpt', 'balanceTags', 9); - } - - # Comments - # - Remove WordPress paragraph generator. - # - Remove WordPress auto-link generator. - # - Scramble important tags before passing them to the kses filter. - # - Run Markdown on excerpt then remove paragraph tags. - if (MARKDOWN_WP_COMMENTS) { - remove_filter('comment_text', 'wpautop', 30); - remove_filter('comment_text', 'make_clickable'); - add_filter('pre_comment_content', 'Markdown', 6); - add_filter('pre_comment_content', 'mdwp_hide_tags', 8); - add_filter('pre_comment_content', 'mdwp_show_tags', 12); - add_filter('get_comment_text', 'Markdown', 6); - add_filter('get_comment_excerpt', 'Markdown', 6); - add_filter('get_comment_excerpt', 'mdwp_strip_p', 7); - - global $mdwp_hidden_tags, $mdwp_placeholders; - $mdwp_hidden_tags = explode(' ', - '

     
  • '); - $mdwp_placeholders = explode(' ', str_rot13( - 'pEj07ZbbBZ U1kqgh4w4p pre2zmeN6K QTi31t9pre ol0MP1jzJR '. - 'ML5IjmbRol ulANi1NsGY J7zRLJqPul liA8ctl16T K9nhooUHli')); - } - - function mdwp_add_p($text) { - if (!preg_match('{^$|^<(p|ul|ol|dl|pre|blockquote)>}i', $text)) { - $text = '

    '.$text.'

    '; - $text = preg_replace('{\n{2,}}', "

    \n\n

    ", $text); - } - return $text; - } - - function mdwp_strip_p($t) { return preg_replace('{}i', '', $t); } - - function mdwp_hide_tags($text) { - global $mdwp_hidden_tags, $mdwp_placeholders; - return str_replace($mdwp_hidden_tags, $mdwp_placeholders, $text); - } - function mdwp_show_tags($text) { - global $mdwp_hidden_tags, $mdwp_placeholders; - return str_replace($mdwp_placeholders, $mdwp_hidden_tags, $text); - } -} - - -### bBlog Plugin Info ### - -function identify_modifier_markdown() { - return array( - 'name' => 'markdown', - 'type' => 'modifier', - 'nicename' => 'Markdown', - 'description' => 'A text-to-HTML conversion tool for web writers', - 'authors' => 'Michel Fortin and John Gruber', - 'licence' => 'BSD-like', - 'version' => MARKDOWN_VERSION, - 'help' => 'Markdown syntax allows you to write using an easy-to-read, easy-to-write plain text format. Based on the original Perl version by John Gruber. More...' - ); -} - - -### Smarty Modifier Interface ### - -function smarty_modifier_markdown($text) { - return Markdown($text); -} - - -### Textile Compatibility Mode ### - -# Rename this file to "classTextile.php" and it can replace Textile everywhere. - -if (strcasecmp(substr(__FILE__, -16), "classTextile.php") == 0) { - # Try to include PHP SmartyPants. Should be in the same directory. - @include_once 'smartypants.php'; - # Fake Textile class. It calls Markdown instead. - class Textile { - function TextileThis($text, $lite='', $encode='') { - if ($lite == '' && $encode == '') $text = Markdown($text); - if (function_exists('SmartyPants')) $text = SmartyPants($text); - return $text; - } - # Fake restricted version: restrictions are not supported for now. - function TextileRestricted($text, $lite='', $noimage='') { - return $this->TextileThis($text, $lite); - } - # Workaround to ensure compatibility with TextPattern 4.0.3. - function blockLite($text) { return $text; } - } -} - - - -# -# Markdown Parser Class -# - -class Markdown_Parser { - - # Regex to match balanced [brackets]. - # Needed to insert a maximum bracked depth while converting to PHP. - public $nested_brackets_depth = 6; - public $nested_brackets_re; - - public $nested_url_parenthesis_depth = 4; - public $nested_url_parenthesis_re; - - # Table of hash values for escaped characters: - public $escape_chars = '\`*_{}[]()>#+-.!'; - public $escape_chars_re; - - # Change to ">" for HTML output. - public $empty_element_suffix = MARKDOWN_EMPTY_ELEMENT_SUFFIX; - public $tab_width = MARKDOWN_TAB_WIDTH; - - # Change to `true` to disallow markup or entities. - public $no_markup = false; - public $no_entities = false; - - # Predefined urls and titles for reference links and images. - public $predef_urls = array(); - public $predef_titles = array(); - - - function Markdown_Parser() { - # - # Constructor function. Initialize appropriate member variables. - # - $this->_initDetab(); - $this->prepareItalicsAndBold(); - - $this->nested_brackets_re = - str_repeat('(?>[^\[\]]+|\[', $this->nested_brackets_depth). - str_repeat('\])*', $this->nested_brackets_depth); - - $this->nested_url_parenthesis_re = - str_repeat('(?>[^()\s]+|\(', $this->nested_url_parenthesis_depth). - str_repeat('(?>\)))*', $this->nested_url_parenthesis_depth); - - $this->escape_chars_re = '['.preg_quote($this->escape_chars).']'; - - # Sort document, block, and span gamut in ascendent priority order. - asort($this->document_gamut); - asort($this->block_gamut); - asort($this->span_gamut); - } - - - # Internal hashes used during transformation. - public $urls = array(); - public $titles = array(); - public $html_hashes = array(); - - # Status flag to avoid invalid nesting. - public $in_anchor = false; - - - function setup() { - # - # Called before the transformation process starts to setup parser - # states. - # - # Clear global hashes. - $this->urls = $this->predef_urls; - $this->titles = $this->predef_titles; - $this->html_hashes = array(); - - $in_anchor = false; - } - - function teardown() { - # - # Called after the transformation process to clear any variable - # which may be taking up memory unnecessarly. - # - $this->urls = array(); - $this->titles = array(); - $this->html_hashes = array(); - } - - - function transform($text) { - # - # Main function. Performs some preprocessing on the input text - # and pass it through the document gamut. - # - $this->setup(); - - # Remove UTF-8 BOM and marker character in input, if present. - $text = preg_replace('{^\xEF\xBB\xBF|\x1A}', '', $text); - - # Standardize line endings: - # DOS to Unix and Mac to Unix - $text = preg_replace('{\r\n?}', "\n", $text); - - # Make sure $text ends with a couple of newlines: - $text .= "\n\n"; - - # Convert all tabs to spaces. - $text = $this->detab($text); - - # Turn block-level HTML blocks into hash entries - $text = $this->hashHTMLBlocks($text); - - # Strip any lines consisting only of spaces and tabs. - # This makes subsequent regexen easier to write, because we can - # match consecutive blank lines with /\n+/ instead of something - # contorted like /[ ]*\n+/ . - $text = preg_replace('/^[ ]+$/m', '', $text); - - # Run document gamut methods. - foreach ($this->document_gamut as $method => $priority) { - $text = $this->$method($text); - } - - $this->teardown(); - - return $text . "\n"; - } - - public $document_gamut = array( - # Strip link definitions, store in hashes. - "stripLinkDefinitions" => 20, - - "runBasicBlockGamut" => 30, - ); - - - function stripLinkDefinitions($text) { - # - # Strips link definitions from text, stores the URLs and titles in - # hash references. - # - $less_than_tab = $this->tab_width - 1; - - # Link defs are in the form: ^[id]: url "optional title" - $text = preg_replace_callback('{ - ^[ ]{0,'.$less_than_tab.'}\[(.+)\][ ]?: # id = $1 - [ ]* - \n? # maybe *one* newline - [ ]* - (?: - <(.+?)> # url = $2 - | - (\S+?) # url = $3 - ) - [ ]* - \n? # maybe one newline - [ ]* - (?: - (?<=\s) # lookbehind for whitespace - ["(] - (.*?) # title = $4 - [")] - [ ]* - )? # title is optional - (?:\n+|\Z) - }xm', - array(&$this, '_stripLinkDefinitions_callback'), - $text); - return $text; - } - function _stripLinkDefinitions_callback($matches) { - $link_id = strtolower($matches[1]); - $url = $matches[2] == '' ? $matches[3] : $matches[2]; - $this->urls[$link_id] = $url; - $this->titles[$link_id] =& $matches[4]; - return ''; # String that will replace the block - } - - - function hashHTMLBlocks($text) { - if ($this->no_markup) return $text; - - $less_than_tab = $this->tab_width - 1; - - # Hashify HTML blocks: - # We only want to do this for block-level HTML tags, such as headers, - # lists, and tables. That's because we still want to wrap

    s around - # "paragraphs" that are wrapped in non-block-level tags, such as anchors, - # phrase emphasis, and spans. The list of tags we're looking for is - # hard-coded: - # - # * List "a" is made of tags which can be both inline or block-level. - # These will be treated block-level when the start tag is alone on - # its line, otherwise they're not matched here and will be taken as - # inline later. - # * List "b" is made of tags which are always block-level; - # - $block_tags_a_re = 'ins|del'; - $block_tags_b_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|'. - 'script|noscript|form|fieldset|iframe|math'; - - # Regular expression for the content of a block tag. - $nested_tags_level = 4; - $attr = ' - (?> # optional tag attributes - \s # starts with whitespace - (?> - [^>"/]+ # text outside quotes - | - /+(?!>) # slash not followed by ">" - | - "[^"]*" # text inside double quotes (tolerate ">") - | - \'[^\']*\' # text inside single quotes (tolerate ">") - )* - )? - '; - $content = - str_repeat(' - (?> - [^<]+ # content without tag - | - <\2 # nested opening tag - '.$attr.' # attributes - (?> - /> - | - >', $nested_tags_level). # end of opening tag - '.*?'. # last level nested tag content - str_repeat(' - # closing nested tag - ) - | - <(?!/\2\s*> # other tags with a different name - ) - )*', - $nested_tags_level); - $content2 = str_replace('\2', '\3', $content); - - # First, look for nested blocks, e.g.: - #

    - #
    - # tags for inner block must be indented. - #
    - #
    - # - # The outermost tags must start at the left margin for this to match, and - # the inner nested divs must be indented. - # We need to do this before the next, more liberal match, because the next - # match will start at the first `
    ` and stop at the first `
    `. - $text = preg_replace_callback('{(?> - (?> - (?<=\n\n) # Starting after a blank line - | # or - \A\n? # the beginning of the doc - ) - ( # save in $1 - - # Match from `\n` to `\n`, handling nested tags - # in between. - - [ ]{0,'.$less_than_tab.'} - <('.$block_tags_b_re.')# start tag = $2 - '.$attr.'> # attributes followed by > and \n - '.$content.' # content, support nesting - # the matching end tag - [ ]* # trailing spaces/tabs - (?=\n+|\Z) # followed by a newline or end of document - - | # Special version for tags of group a. - - [ ]{0,'.$less_than_tab.'} - <('.$block_tags_a_re.')# start tag = $3 - '.$attr.'>[ ]*\n # attributes followed by > - '.$content2.' # content, support nesting - # the matching end tag - [ ]* # trailing spaces/tabs - (?=\n+|\Z) # followed by a newline or end of document - - | # Special case just for
    . It was easier to make a special - # case than to make the other regex more complicated. - - [ ]{0,'.$less_than_tab.'} - <(hr) # start tag = $2 - '.$attr.' # attributes - /?> # the matching end tag - [ ]* - (?=\n{2,}|\Z) # followed by a blank line or end of document - - | # Special case for standalone HTML comments: - - [ ]{0,'.$less_than_tab.'} - (?s: - - ) - [ ]* - (?=\n{2,}|\Z) # followed by a blank line or end of document - - | # PHP and ASP-style processor instructions ( - ) - [ ]* - (?=\n{2,}|\Z) # followed by a blank line or end of document - - ) - )}Sxmi', - array(&$this, '_hashHTMLBlocks_callback'), - $text); - - return $text; - } - function _hashHTMLBlocks_callback($matches) { - $text = $matches[1]; - $key = $this->hashBlock($text); - return "\n\n$key\n\n"; - } - - - function hashPart($text, $boundary = 'X') { - # - # Called whenever a tag must be hashed when a function insert an atomic - # element in the text stream. Passing $text to through this function gives - # a unique text-token which will be reverted back when calling unhash. - # - # The $boundary argument specify what character should be used to surround - # the token. By convension, "B" is used for block elements that needs not - # to be wrapped into paragraph tags at the end, ":" is used for elements - # that are word separators and "X" is used in the general case. - # - # Swap back any tag hash found in $text so we do not have to `unhash` - # multiple times at the end. - $text = $this->unhash($text); - - # Then hash the block. - static $i = 0; - $key = "$boundary\x1A" . ++$i . $boundary; - $this->html_hashes[$key] = $text; - return $key; # String that will replace the tag. - } - - - function hashBlock($text) { - # - # Shortcut function for hashPart with block-level boundaries. - # - return $this->hashPart($text, 'B'); - } - - - public $block_gamut = array( - # - # These are all the transformations that form block-level - # tags like paragraphs, headers, and list items. - # - "doHeaders" => 10, - "doHorizontalRules" => 20, - - "doLists" => 40, - "doCodeBlocks" => 50, - "doBlockQuotes" => 60, - ); - - function runBlockGamut($text) { - # - # Run block gamut tranformations. - # - # We need to escape raw HTML in Markdown source before doing anything - # else. This need to be done for each block, and not only at the - # begining in the Markdown function since hashed blocks can be part of - # list items and could have been indented. Indented blocks would have - # been seen as a code block in a previous pass of hashHTMLBlocks. - $text = $this->hashHTMLBlocks($text); - - return $this->runBasicBlockGamut($text); - } - - function runBasicBlockGamut($text) { - # - # Run block gamut tranformations, without hashing HTML blocks. This is - # useful when HTML blocks are known to be already hashed, like in the first - # whole-document pass. - # - foreach ($this->block_gamut as $method => $priority) { - $text = $this->$method($text); - } - - # Finally form paragraph and restore hashed blocks. - $text = $this->formParagraphs($text); - - return $text; - } - - - function doHorizontalRules($text) { - # Do Horizontal Rules: - return preg_replace( - '{ - ^[ ]{0,3} # Leading space - ([-*_]) # $1: First marker - (?> # Repeated marker group - [ ]{0,2} # Zero, one, or two spaces. - \1 # Marker character - ){2,} # Group repeated at least twice - [ ]* # Tailing spaces - $ # End of line. - }mx', - "\n".$this->hashBlock("empty_element_suffix")."\n", - $text); - } - - - public $span_gamut = array( - # - # These are all the transformations that occur *within* block-level - # tags like paragraphs, headers, and list items. - # - # Process character escapes, code spans, and inline HTML - # in one shot. - "parseSpan" => -30, - - # Process anchor and image tags. Images must come first, - # because ![foo][f] looks like an anchor. - "doImages" => 10, - "doAnchors" => 20, - - # Make links out of things like `` - # Must come after doAnchors, because you can use < and > - # delimiters in inline links like [this](). - "doAutoLinks" => 30, - "encodeAmpsAndAngles" => 40, - - "doItalicsAndBold" => 50, - "doHardBreaks" => 60, - ); - - function runSpanGamut($text) { - # - # Run span gamut tranformations. - # - foreach ($this->span_gamut as $method => $priority) { - $text = $this->$method($text); - } - - return $text; - } - - - function doHardBreaks($text) { - # Do hard breaks: - return preg_replace_callback('/ {2,}\n/', - array(&$this, '_doHardBreaks_callback'), $text); - } - function _doHardBreaks_callback($matches) { - return $this->hashPart("empty_element_suffix\n"); - } - - - function doAnchors($text) { - # - # Turn Markdown link shortcuts into XHTML tags. - # - if ($this->in_anchor) return $text; - $this->in_anchor = true; - - # - # First, handle reference-style links: [link text] [id] - # - $text = preg_replace_callback('{ - ( # wrap whole match in $1 - \[ - ('.$this->nested_brackets_re.') # link text = $2 - \] - - [ ]? # one optional space - (?:\n[ ]*)? # one optional newline followed by spaces - - \[ - (.*?) # id = $3 - \] - ) - }xs', - array(&$this, '_doAnchors_reference_callback'), $text); - - # - # Next, inline-style links: [link text](url "optional title") - # - $text = preg_replace_callback('{ - ( # wrap whole match in $1 - \[ - ('.$this->nested_brackets_re.') # link text = $2 - \] - \( # literal paren - [ \n]* - (?: - <(.+?)> # href = $3 - | - ('.$this->nested_url_parenthesis_re.') # href = $4 - ) - [ \n]* - ( # $5 - ([\'"]) # quote char = $6 - (.*?) # Title = $7 - \6 # matching quote - [ \n]* # ignore any spaces/tabs between closing quote and ) - )? # title is optional - \) - ) - }xs', - array(&$this, '_doAnchors_inline_callback'), $text); - - # - # Last, handle reference-style shortcuts: [link text] - # These must come last in case you've also got [link text][1] - # or [link text](/foo) - # - $text = preg_replace_callback('{ - ( # wrap whole match in $1 - \[ - ([^\[\]]+) # link text = $2; can\'t contain [ or ] - \] - ) - }xs', - array(&$this, '_doAnchors_reference_callback'), $text); - - $this->in_anchor = false; - return $text; - } - function _doAnchors_reference_callback($matches) { - $whole_match = $matches[1]; - $link_text = $matches[2]; - $link_id =& $matches[3]; - - if ($link_id == "") { - # for shortcut links like [this][] or [this]. - $link_id = $link_text; - } - - # lower-case and turn embedded newlines into spaces - $link_id = strtolower($link_id); - $link_id = preg_replace('{[ ]?\n}', ' ', $link_id); - - if (isset($this->urls[$link_id])) { - $url = $this->urls[$link_id]; - $url = $this->encodeAttribute($url); - - $result = "titles[$link_id] ) ) { - $title = $this->titles[$link_id]; - $title = $this->encodeAttribute($title); - $result .= " title=\"$title\""; - } - - $link_text = $this->runSpanGamut($link_text); - $result .= ">$link_text"; - $result = $this->hashPart($result); - } - else { - $result = $whole_match; - } - return $result; - } - function _doAnchors_inline_callback($matches) { - $whole_match = $matches[1]; - $link_text = $this->runSpanGamut($matches[2]); - $url = $matches[3] == '' ? $matches[4] : $matches[3]; - $title =& $matches[7]; - - $url = $this->encodeAttribute($url); - - $result = "encodeAttribute($title); - $result .= " title=\"$title\""; - } - - $link_text = $this->runSpanGamut($link_text); - $result .= ">$link_text"; - - return $this->hashPart($result); - } - - - function doImages($text) { - # - # Turn Markdown image shortcuts into tags. - # - # - # First, handle reference-style labeled images: ![alt text][id] - # - $text = preg_replace_callback('{ - ( # wrap whole match in $1 - !\[ - ('.$this->nested_brackets_re.') # alt text = $2 - \] - - [ ]? # one optional space - (?:\n[ ]*)? # one optional newline followed by spaces - - \[ - (.*?) # id = $3 - \] - - ) - }xs', - array(&$this, '_doImages_reference_callback'), $text); - - # - # Next, handle inline images: ![alt text](url "optional title") - # Don't forget: encode * and _ - # - $text = preg_replace_callback('{ - ( # wrap whole match in $1 - !\[ - ('.$this->nested_brackets_re.') # alt text = $2 - \] - \s? # One optional whitespace character - \( # literal paren - [ \n]* - (?: - <(\S*)> # src url = $3 - | - ('.$this->nested_url_parenthesis_re.') # src url = $4 - ) - [ \n]* - ( # $5 - ([\'"]) # quote char = $6 - (.*?) # title = $7 - \6 # matching quote - [ \n]* - )? # title is optional - \) - ) - }xs', - array(&$this, '_doImages_inline_callback'), $text); - - return $text; - } - function _doImages_reference_callback($matches) { - $whole_match = $matches[1]; - $alt_text = $matches[2]; - $link_id = strtolower($matches[3]); - - if ($link_id == "") { - $link_id = strtolower($alt_text); # for shortcut links like ![this][]. - } - - $alt_text = $this->encodeAttribute($alt_text); - if (isset($this->urls[$link_id])) { - $url = $this->encodeAttribute($this->urls[$link_id]); - $result = "\"$alt_text\"";titles[$link_id])) { - $title = $this->titles[$link_id]; - $title = $this->encodeAttribute($title); - $result .= " title=\"$title\""; - } - $result .= $this->empty_element_suffix; - $result = $this->hashPart($result); - } - else { - # If there's no such link ID, leave intact: - $result = $whole_match; - } - - return $result; - } - function _doImages_inline_callback($matches) { - $whole_match = $matches[1]; - $alt_text = $matches[2]; - $url = $matches[3] == '' ? $matches[4] : $matches[3]; - $title =& $matches[7]; - - $alt_text = $this->encodeAttribute($alt_text); - $url = $this->encodeAttribute($url); - $result = "\"$alt_text\"";encodeAttribute($title); - $result .= " title=\"$title\""; # $title already quoted - } - $result .= $this->empty_element_suffix; - - return $this->hashPart($result); - } - - - function doHeaders($text) { - # Setext-style headers: - # Header 1 - # ======== - # - # Header 2 - # -------- - # - $text = preg_replace_callback('{ ^(.+?)[ ]*\n(=+|-+)[ ]*\n+ }mx', - array(&$this, '_doHeaders_callback_setext'), $text); - - # atx-style headers: - # # Header 1 - # ## Header 2 - # ## Header 2 with closing hashes ## - # ... - # ###### Header 6 - # - $text = preg_replace_callback('{ - ^(\#{1,6}) # $1 = string of #\'s - [ ]* - (.+?) # $2 = Header text - [ ]* - \#* # optional closing #\'s (not counted) - \n+ - }xm', - array(&$this, '_doHeaders_callback_atx'), $text); - - return $text; - } - function _doHeaders_callback_setext($matches) { - # Terrible hack to check we haven't found an empty list item. - if ($matches[2] == '-' && preg_match('{^-(?: |$)}', $matches[1])) - return $matches[0]; - - $level = $matches[2]{0} == '=' ? 1 : 2; - $block = "".$this->runSpanGamut($matches[1]).""; - return "\n" . $this->hashBlock($block) . "\n\n"; - } - function _doHeaders_callback_atx($matches) { - $level = strlen($matches[1]); - $block = "".$this->runSpanGamut($matches[2]).""; - return "\n" . $this->hashBlock($block) . "\n\n"; - } - - - function doLists($text) { - # - # Form HTML ordered (numbered) and unordered (bulleted) lists. - # - $less_than_tab = $this->tab_width - 1; - - # Re-usable patterns to match list item bullets and number markers: - $marker_ul_re = '[*+-]'; - $marker_ol_re = '\d+[.]'; - $marker_any_re = "(?:$marker_ul_re|$marker_ol_re)"; - - $markers_relist = array( - $marker_ul_re => $marker_ol_re, - $marker_ol_re => $marker_ul_re, - ); - - foreach ($markers_relist as $marker_re => $other_marker_re) { - # Re-usable pattern to match any entirel ul or ol list: - $whole_list_re = ' - ( # $1 = whole list - ( # $2 - ([ ]{0,'.$less_than_tab.'}) # $3 = number of spaces - ('.$marker_re.') # $4 = first list item marker - [ ]+ - ) - (?s:.+?) - ( # $5 - \z - | - \n{2,} - (?=\S) - (?! # Negative lookahead for another list item marker - [ ]* - '.$marker_re.'[ ]+ - ) - | - (?= # Lookahead for another kind of list - \n - \3 # Must have the same indentation - '.$other_marker_re.'[ ]+ - ) - ) - ) - '; // mx - - # We use a different prefix before nested lists than top-level lists. - # See extended comment in _ProcessListItems(). - - if ($this->list_level) { - $text = preg_replace_callback('{ - ^ - '.$whole_list_re.' - }mx', - array(&$this, '_doLists_callback'), $text); - } - else { - $text = preg_replace_callback('{ - (?:(?<=\n)\n|\A\n?) # Must eat the newline - '.$whole_list_re.' - }mx', - array(&$this, '_doLists_callback'), $text); - } - } - - return $text; - } - function _doLists_callback($matches) { - # Re-usable patterns to match list item bullets and number markers: - $marker_ul_re = '[*+-]'; - $marker_ol_re = '\d+[.]'; - $marker_any_re = "(?:$marker_ul_re|$marker_ol_re)"; - - $list = $matches[1]; - $list_type = preg_match("/$marker_ul_re/", $matches[4]) ? "ul" : "ol"; - - $marker_any_re = ( $list_type == "ul" ? $marker_ul_re : $marker_ol_re ); - - $list .= "\n"; - $result = $this->processListItems($list, $marker_any_re); - - $result = $this->hashBlock("<$list_type>\n" . $result . ""); - return "\n". $result ."\n\n"; - } - - public $list_level = 0; - - function processListItems($list_str, $marker_any_re) { - # - # Process the contents of a single ordered or unordered list, splitting it - # into individual list items. - # - # The $this->list_level global keeps track of when we're inside a list. - # Each time we enter a list, we increment it; when we leave a list, - # we decrement. If it's zero, we're not in a list anymore. - # - # We do this because when we're not inside a list, we want to treat - # something like this: - # - # I recommend upgrading to version - # 8. Oops, now this line is treated - # as a sub-list. - # - # As a single paragraph, despite the fact that the second line starts - # with a digit-period-space sequence. - # - # Whereas when we're inside a list (or sub-list), that line will be - # treated as the start of a sub-list. What a kludge, huh? This is - # an aspect of Markdown's syntax that's hard to parse perfectly - # without resorting to mind-reading. Perhaps the solution is to - # change the syntax rules such that sub-lists must start with a - # starting cardinal number; e.g. "1." or "a.". - - $this->list_level++; - - # trim trailing blank lines: - $list_str = preg_replace("/\n{2,}\\z/", "\n", $list_str); - - $list_str = preg_replace_callback('{ - (\n)? # leading line = $1 - (^[ ]*) # leading whitespace = $2 - ('.$marker_any_re.' # list marker and space = $3 - (?:[ ]+|(?=\n)) # space only required if item is not empty - ) - ((?s:.*?)) # list item text = $4 - (?:(\n+(?=\n))|\n) # tailing blank line = $5 - (?= \n* (\z | \2 ('.$marker_any_re.') (?:[ ]+|(?=\n)))) - }xm', - array(&$this, '_processListItems_callback'), $list_str); - - $this->list_level--; - return $list_str; - } - function _processListItems_callback($matches) { - $item = $matches[4]; - $leading_line =& $matches[1]; - $leading_space =& $matches[2]; - $marker_space = $matches[3]; - $tailing_blank_line =& $matches[5]; - - if ($leading_line || $tailing_blank_line || - preg_match('/\n{2,}/', $item)) - { - # Replace marker with the appropriate whitespace indentation - $item = $leading_space . str_repeat(' ', strlen($marker_space)) . $item; - $item = $this->runBlockGamut($this->outdent($item)."\n"); - } - else { - # Recursion for sub-lists: - $item = $this->doLists($this->outdent($item)); - $item = preg_replace('/\n+$/', '', $item); - $item = $this->runSpanGamut($item); - } - - return "
  • " . $item . "
  • \n"; - } - - - function doCodeBlocks($text) { - # - # Process Markdown `
    ` blocks.
    -	#
    -		$text = preg_replace_callback('{
    -				(?:\n\n|\A\n?)
    -				(	            # $1 = the code block -- one or more lines, starting with a space/tab
    -				  (?>
    -					[ ]{'.$this->tab_width.'}  # Lines must start with a tab or a tab-width of spaces
    -					.*\n+
    -				  )+
    -				)
    -				((?=^[ ]{0,'.$this->tab_width.'}\S)|\Z)	# Lookahead for non-space at line-start, or end of doc
    -			}xm',
    -			array(&$this, '_doCodeBlocks_callback'), $text);
    -
    -		return $text;
    -	}
    -	function _doCodeBlocks_callback($matches) {
    -		$codeblock = $matches[1];
    -
    -		$codeblock = $this->outdent($codeblock);
    -		$codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES);
    -
    -		# trim leading newlines and trailing newlines
    -		$codeblock = preg_replace('/\A\n+|\n+\z/', '', $codeblock);
    -
    -		$codeblock = "
    $codeblock\n
    "; - return "\n\n".$this->hashBlock($codeblock)."\n\n"; - } - - - function makeCodeSpan($code) { - # - # Create a code span markup for $code. Called from handleSpanToken. - # - $code = htmlspecialchars(trim($code), ENT_NOQUOTES); - return $this->hashPart("$code"); - } - - - public $em_relist = array( - '' => '(?:(? '(?<=\S|^)(? '(?<=\S|^)(? '(?:(? '(?<=\S|^)(? '(?<=\S|^)(? '(?:(? '(?<=\S|^)(? '(?<=\S|^)(?em_relist as $em => $em_re) { - foreach ($this->strong_relist as $strong => $strong_re) { - # Construct list of allowed token expressions. - $token_relist = array(); - if (isset($this->em_strong_relist["$em$strong"])) { - $token_relist[] = $this->em_strong_relist["$em$strong"]; - } - $token_relist[] = $em_re; - $token_relist[] = $strong_re; - - # Construct master expression from list. - $token_re = '{('. implode('|', $token_relist) .')}'; - $this->em_strong_prepared_relist["$em$strong"] = $token_re; - } - } - } - - function doItalicsAndBold($text) { - $token_stack = array(''); - $text_stack = array(''); - $em = ''; - $strong = ''; - $tree_char_em = false; - - while (1) { - # - # Get prepared regular expression for seraching emphasis tokens - # in current context. - # - $token_re = $this->em_strong_prepared_relist["$em$strong"]; - - # - # Each loop iteration search for the next emphasis token. - # Each token is then passed to handleSpanToken. - # - $parts = preg_split($token_re, $text, 2, PREG_SPLIT_DELIM_CAPTURE); - $text_stack[0] .= $parts[0]; - $token =& $parts[1]; - $text =& $parts[2]; - - if (empty($token)) { - # Reached end of text span: empty stack without emitting. - # any more emphasis. - while ($token_stack[0]) { - $text_stack[1] .= array_shift($token_stack); - $text_stack[0] .= array_shift($text_stack); - } - break; - } - - $token_len = strlen($token); - if ($tree_char_em) { - # Reached closing marker while inside a three-char emphasis. - if ($token_len == 3) { - # Three-char closing marker, close em and strong. - array_shift($token_stack); - $span = array_shift($text_stack); - $span = $this->runSpanGamut($span); - $span = "$span"; - $text_stack[0] .= $this->hashPart($span); - $em = ''; - $strong = ''; - } else { - # Other closing marker: close one em or strong and - # change current token state to match the other - $token_stack[0] = str_repeat($token{0}, 3-$token_len); - $tag = $token_len == 2 ? "strong" : "em"; - $span = $text_stack[0]; - $span = $this->runSpanGamut($span); - $span = "<$tag>$span"; - $text_stack[0] = $this->hashPart($span); - $$tag = ''; # $$tag stands for $em or $strong - } - $tree_char_em = false; - } else if ($token_len == 3) { - if ($em) { - # Reached closing marker for both em and strong. - # Closing strong marker: - for ($i = 0; $i < 2; ++$i) { - $shifted_token = array_shift($token_stack); - $tag = strlen($shifted_token) == 2 ? "strong" : "em"; - $span = array_shift($text_stack); - $span = $this->runSpanGamut($span); - $span = "<$tag>$span"; - $text_stack[0] .= $this->hashPart($span); - $$tag = ''; # $$tag stands for $em or $strong - } - } else { - # Reached opening three-char emphasis marker. Push on token - # stack; will be handled by the special condition above. - $em = $token{0}; - $strong = "$em$em"; - array_unshift($token_stack, $token); - array_unshift($text_stack, ''); - $tree_char_em = true; - } - } else if ($token_len == 2) { - if ($strong) { - # Unwind any dangling emphasis marker: - if (strlen($token_stack[0]) == 1) { - $text_stack[1] .= array_shift($token_stack); - $text_stack[0] .= array_shift($text_stack); - } - # Closing strong marker: - array_shift($token_stack); - $span = array_shift($text_stack); - $span = $this->runSpanGamut($span); - $span = "$span"; - $text_stack[0] .= $this->hashPart($span); - $strong = ''; - } else { - array_unshift($token_stack, $token); - array_unshift($text_stack, ''); - $strong = $token; - } - } else { - # Here $token_len == 1 - if ($em) { - if (strlen($token_stack[0]) == 1) { - # Closing emphasis marker: - array_shift($token_stack); - $span = array_shift($text_stack); - $span = $this->runSpanGamut($span); - $span = "$span"; - $text_stack[0] .= $this->hashPart($span); - $em = ''; - } else { - $text_stack[0] .= $token; - } - } else { - array_unshift($token_stack, $token); - array_unshift($text_stack, ''); - $em = $token; - } - } - } - return $text_stack[0]; - } - - - function doBlockQuotes($text) { - $text = preg_replace_callback('/ - ( # Wrap whole match in $1 - (?> - ^[ ]*>[ ]? # ">" at the start of a line - .+\n # rest of the first line - (.+\n)* # subsequent consecutive lines - \n* # blanks - )+ - ) - /xm', - array(&$this, '_doBlockQuotes_callback'), $text); - - return $text; - } - function _doBlockQuotes_callback($matches) { - $bq = $matches[1]; - # trim one level of quoting - trim whitespace-only lines - $bq = preg_replace('/^[ ]*>[ ]?|^[ ]+$/m', '', $bq); - $bq = $this->runBlockGamut($bq); # recurse - - $bq = preg_replace('/^/m', " ", $bq); - # These leading spaces cause problem with
     content, 
    -		# so we need to fix that:
    -		$bq = preg_replace_callback('{(\s*
    .+?
    )}sx', - array(&$this, '_doBlockQuotes_callback2'), $bq); - - return "\n". $this->hashBlock("
    \n$bq\n
    ")."\n\n"; - } - function _doBlockQuotes_callback2($matches) { - $pre = $matches[1]; - $pre = preg_replace('/^ /m', '', $pre); - return $pre; - } - - - function formParagraphs($text) { - # - # Params: - # $text - string to process with html

    tags - # - # Strip leading and trailing lines: - $text = preg_replace('/\A\n+|\n+\z/', '', $text); - - $grafs = preg_split('/\n{2,}/', $text, -1, PREG_SPLIT_NO_EMPTY); - - # - # Wrap

    tags and unhashify HTML blocks - # - foreach ($grafs as $key => $value) { - if (!preg_match('/^B\x1A[0-9]+B$/', $value)) { - # Is a paragraph. - $value = $this->runSpanGamut($value); - $value = preg_replace('/^([ ]*)/', "

    ", $value); - $value .= "

    "; - $grafs[$key] = $this->unhash($value); - } - else { - # Is a block. - # Modify elements of @grafs in-place... - $graf = $value; - $block = $this->html_hashes[$graf]; - $graf = $block; -// if (preg_match('{ -// \A -// ( # $1 =
    tag -//
    ]* -// \b -// markdown\s*=\s* ([\'"]) # $2 = attr quote char -// 1 -// \2 -// [^>]* -// > -// ) -// ( # $3 = contents -// .* -// ) -// (
    ) # $4 = closing tag -// \z -// }xs', $block, $matches)) -// { -// list(, $div_open, , $div_content, $div_close) = $matches; -// -// # We can't call Markdown(), because that resets the hash; -// # that initialization code should be pulled into its own sub, though. -// $div_content = $this->hashHTMLBlocks($div_content); -// -// # Run document gamut methods on the content. -// foreach ($this->document_gamut as $method => $priority) { -// $div_content = $this->$method($div_content); -// } -// -// $div_open = preg_replace( -// '{\smarkdown\s*=\s*([\'"]).+?\1}', '', $div_open); -// -// $graf = $div_open . "\n" . $div_content . "\n" . $div_close; -// } - $grafs[$key] = $graf; - } - } - - return implode("\n\n", $grafs); - } - - - function encodeAttribute($text) { - # - # Encode text for a double-quoted HTML attribute. This function - # is *not* suitable for attributes enclosed in single quotes. - # - $text = $this->encodeAmpsAndAngles($text); - $text = str_replace('"', '"', $text); - return $text; - } - - - function encodeAmpsAndAngles($text) { - # - # Smart processing for ampersands and angle brackets that need to - # be encoded. Valid character entities are left alone unless the - # no-entities mode is set. - # - if ($this->no_entities) { - $text = str_replace('&', '&', $text); - } else { - # Ampersand-encoding based entirely on Nat Irons's Amputator - # MT plugin: - $text = preg_replace('/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/', - '&', $text);; - } - # Encode remaining <'s - $text = str_replace('<', '<', $text); - - return $text; - } - - - function doAutoLinks($text) { - $text = preg_replace_callback('{<((https?|ftp|dict):[^\'">\s]+)>}i', - array(&$this, '_doAutoLinks_url_callback'), $text); - - # Email addresses: - $text = preg_replace_callback('{ - < - (?:mailto:)? - ( - (?: - [-!#$%&\'*+/=?^_`.{|}~\w\x80-\xFF]+ - | - ".*?" - ) - \@ - (?: - [-a-z0-9\x80-\xFF]+(\.[-a-z0-9\x80-\xFF]+)*\.[a-z]+ - | - \[[\d.a-fA-F:]+\] # IPv4 & IPv6 - ) - ) - > - }xi', - array(&$this, '_doAutoLinks_email_callback'), $text); - - return $text; - } - function _doAutoLinks_url_callback($matches) { - $url = $this->encodeAttribute($matches[1]); - $link = "$url"; - return $this->hashPart($link); - } - function _doAutoLinks_email_callback($matches) { - $address = $matches[1]; - $link = $this->encodeEmailAddress($address); - return $this->hashPart($link); - } - - - function encodeEmailAddress($addr) { - # - # Input: an email address, e.g. "foo@example.com" - # - # Output: the email address as a mailto link, with each character - # of the address encoded as either a decimal or hex entity, in - # the hopes of foiling most address harvesting spam bots. E.g.: - # - #

    foo@exampl - # e.com

    - # - # Based by a filter by Matthew Wickline, posted to BBEdit-Talk. - # With some optimizations by Milian Wolff. - # - $addr = "mailto:" . $addr; - $chars = preg_split('/(? $char) { - $ord = ord($char); - # Ignore non-ascii chars. - if ($ord < 128) { - $r = ($seed * (1 + $key)) % 100; # Pseudo-random function. - # roughly 10% raw, 45% hex, 45% dec - # '@' *must* be encoded. I insist. - if ($r > 90 && $char != '@') /* do nothing */; - else if ($r < 45) $chars[$key] = '&#x'.dechex($ord).';'; - else $chars[$key] = '&#'.$ord.';'; - } - } - - $addr = implode('', $chars); - $text = implode('', array_slice($chars, 7)); # text without `mailto:` - $addr = "$text"; - - return $addr; - } - - - function parseSpan($str) { - # - # Take the string $str and parse it into tokens, hashing embeded HTML, - # escaped characters and handling code spans. - # - $output = ''; - - $span_re = '{ - ( - \\\\'.$this->escape_chars_re.' - | - (?no_markup ? '' : ' - | - # comment - | - <\?.*?\?> | <%.*?%> # processing instruction - | - <[/!$]?[-a-zA-Z0-9:_]+ # regular tags - (?> - \s - (?>[^"\'>]+|"[^"]*"|\'[^\']*\')* - )? - > - ').' - ) - }xs'; - - while (1) { - # - # Each loop iteration seach for either the next tag, the next - # openning code span marker, or the next escaped character. - # Each token is then passed to handleSpanToken. - # - $parts = preg_split($span_re, $str, 2, PREG_SPLIT_DELIM_CAPTURE); - - # Create token from text preceding tag. - if ($parts[0] != "") { - $output .= $parts[0]; - } - - # Check if we reach the end. - if (isset($parts[1])) { - $output .= $this->handleSpanToken($parts[1], $parts[2]); - $str = $parts[2]; - } - else { - break; - } - } - - return $output; - } - - - function handleSpanToken($token, &$str) { - # - # Handle $token provided by parseSpan by determining its nature and - # returning the corresponding value that should replace it. - # - switch ($token{0}) { - case "\\": - return $this->hashPart("&#". ord($token{1}). ";"); - case "`": - # Search for end marker in remaining text. - if (preg_match('/^(.*?[^`])'.preg_quote($token).'(?!`)(.*)$/sm', - $str, $matches)) - { - $str = $matches[2]; - $codespan = $this->makeCodeSpan($matches[1]); - return $this->hashPart($codespan); - } - return $token; // return as text since no ending marker found. - default: - return $this->hashPart($token); - } - } - - - function outdent($text) { - # - # Remove one level of line-leading tabs or spaces - # - return preg_replace('/^(\t|[ ]{1,'.$this->tab_width.'})/m', '', $text); - } - - - # String length function for detab. `_initDetab` will create a function to - # hanlde UTF-8 if the default function does not exist. - public $utf8_strlen = 'mb_strlen'; - - function detab($text) { - # - # Replace tabs with the appropriate amount of space. - # - # For each line we separate the line in blocks delemited by - # tab characters. Then we reconstruct every line by adding the - # appropriate number of space between each blocks. - - $text = preg_replace_callback('/^.*\t.*$/m', - array(&$this, '_detab_callback'), $text); - - return $text; - } - function _detab_callback($matches) { - $line = $matches[0]; - $strlen = $this->utf8_strlen; # strlen function for UTF-8. - - # Split in blocks. - $blocks = explode("\t", $line); - # Add each blocks to the line. - $line = $blocks[0]; - unset($blocks[0]); # Do not add first block twice. - foreach ($blocks as $block) { - # Calculate amount of space, insert spaces, insert block. - $amount = $this->tab_width - - $strlen($line, 'UTF-8') % $this->tab_width; - $line .= str_repeat(" ", $amount) . $block; - } - return $line; - } - function _initDetab() { - # - # Check for the availability of the function in the `utf8_strlen` property - # (initially `mb_strlen`). If the function is not available, create a - # function that will loosely count the number of UTF-8 characters with a - # regular expression. - # - if (function_exists($this->utf8_strlen)) return; - $this->utf8_strlen = create_function('$text', 'return preg_match_all( - "/[\\\\x00-\\\\xBF]|[\\\\xC0-\\\\xFF][\\\\x80-\\\\xBF]*/", - $text, $m);'); - } - - - function unhash($text) { - # - # Swap back in all the tags hashed by _HashHTMLBlocks. - # - return preg_replace_callback('/(.)\x1A[0-9]+\1/', - array(&$this, '_unhash_callback'), $text); - } - function _unhash_callback($matches) { - return $this->html_hashes[$matches[0]]; - } - -} - -/* - -PHP Markdown -============ - -Description ------------ - -This is a PHP translation of the original Markdown formatter written in -Perl by John Gruber. - -Markdown is a text-to-HTML filter; it translates an easy-to-read / -easy-to-write structured text format into HTML. Markdown's text format -is most similar to that of plain text email, and supports features such -as headers, *emphasis*, code blocks, blockquotes, and links. - -Markdown's syntax is designed not as a generic markup language, but -specifically to serve as a front-end to (X)HTML. You can use span-level -HTML tags anywhere in a Markdown document, and you can use block level -HTML tags (like
    and as well). - -For more information about Markdown's syntax, see: - - - - -Bugs ----- - -To file bug reports please send email to: - - - -Please include with your report: (1) the example input; (2) the output you -expected; (3) the output Markdown actually produced. - - -Version History ---------------- - -See the readme file for detailed release notes for this version. - - -Copyright and License ---------------------- - -PHP Markdown -Copyright (c) 2004-2009 Michel Fortin - -All rights reserved. - -Based on Markdown -Copyright (c) 2003-2006 John Gruber - -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - -* Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -* Neither the name "Markdown" nor the names of its contributors may - be used to endorse or promote products derived from this software - without specific prior written permission. - -This software is provided by the copyright holders and contributors "as -is" and any express or implied warranties, including, but not limited -to, the implied warranties of merchantability and fitness for a -particular purpose are disclaimed. In no event shall the copyright owner -or contributors be liable for any direct, indirect, incidental, special, -exemplary, or consequential damages (including, but not limited to, -procurement of substitute goods or services; loss of use, data, or -profits; or business interruption) however caused and on any theory of -liability, whether in contract, strict liability, or tort (including -negligence or otherwise) arising in any way out of the use of this -software, even if advised of the possibility of such damage. - -*/ -?> diff --git a/mime_lib.php b/mime_lib.php deleted file mode 100644 index 6757fe0..0000000 --- a/mime_lib.php +++ /dev/null @@ -1,32 +0,0 @@ - diff --git a/mimetypes.php b/mimetypes.php deleted file mode 100644 index e3b3dd4..0000000 --- a/mimetypes.php +++ /dev/null @@ -1,133 +0,0 @@ - \"" $1 "\","}}' >> mimetypes.php.new -// done -// echo ");" -static $mimetypes = array( - "ez" => "application/andrew-inset", - "hqx" => "application/mac-binhex40", - "cpt" => "application/mac-compactpro", - "doc" => "application/msword", - "bin" => "application/octet-stream", - "oda" => "application/oda", - "pdf" => "application/pdf", - "ai" => "application/postscript", - "rtf" => "application/rtf", - "smi" => "application/smil", - "mif" => "application/vnd.mif", - "ppt" => "application/vnd.ms-powerpoint", - "xls" => "application/vnd.ms-excel", - "bcpio" => "application/x-bcpio", - "vcd" => "application/x-cdlink", - "pgn" => "application/x-chess-pgn", - "cpio" => "application/x-cpio", - "csh" => "application/x-csh", - "dcr" => "application/x-director", - "dvi" => "application/x-dvi", - "spl" => "application/x-futuresplash", - "gtar" => "application/x-gtar", - "hdf" => "application/x-hdf", - "js" => "application/x-javascript", - "skp" => "application/x-koan", - "latex" => "application/x-latex", - "nc" => "application/x-netcdf", - "sh" => "application/x-sh", - "shar" => "application/x-shar", - "swf" => "application/x-shockwave-flash", - "sit" => "application/x-stuffit", - "sv4cpio" => "application/x-sv4cpio", - "sv4crc" => "application/x-sv4crc", - "tar" => "application/x-tar", - "tcl" => "application/x-tcl", - "tex" => "application/x-tex", - "texinfo" => "application/x-texinfo", - "t" => "application/x-troff", - "man" => "application/x-troff-man", - "me" => "application/x-troff-me", - "ms" => "application/x-troff-ms", - "ustar" => "application/x-ustar", - "src" => "application/x-wais-source", - "zip" => "application/zip", - "au" => "audio/basic", - "mid" => "audio/midi", - "mpga" => "audio/mpeg", - "aif" => "audio/x-aiff", - "ram" => "audio/x-pn-realaudio", - "rpm" => "audio/x-pn-realaudio-plugin", - "ra" => "audio/x-realaudio", - "wav" => "audio/x-wav", - "pdb" => "chemical/x-pdb", - "gif" => "image/gif", - "ief" => "image/ief", - "jpeg" => "image/jpeg", - "png" => "image/png", - "tiff" => "image/tiff", - "ras" => "image/x-cmu-raster", - "pnm" => "image/x-portable-anymap", - "pbm" => "image/x-portable-bitmap", - "pgm" => "image/x-portable-graymap", - "ppm" => "image/x-portable-pixmap", - "rgb" => "image/x-rgb", - "xbm" => "image/x-xbitmap", - "xpm" => "image/x-xpixmap", - "xwd" => "image/x-xwindowdump", - "igs" => "model/iges", - "msh" => "model/mesh", - "wrl" => "model/vrml", - "css" => "text/css", - "html" => "text/html", - "asc" => "text/plain", - "rtx" => "text/richtext", - "rtf" => "text/rtf", - "sgml" => "text/sgml", - "tsv" => "text/tab-separated-values", - "etx" => "text/x-setext", - "xml" => "text/xml", - "mpeg" => "video/mpeg", - "qt" => "video/quicktime", - "avi" => "video/x-msvideo", - "movie" => "video/x-sgi-movie", - "ice" => "x-conference/x-cooltalk", - "dms" => "application/octet-stream", - "eps" => "application/postscript", - "smil" => "application/smil", - "dir" => "application/x-director", - "skd" => "application/x-koan", - "cdf" => "application/x-netcdf", - "texi" => "application/x-texinfo", - "tr" => "application/x-troff", - "snd" => "audio/basic", - "midi" => "audio/midi", - "mp2" => "audio/mpeg", - "aiff" => "audio/x-aiff", - "rm" => "audio/x-pn-realaudio", - "xyz" => "chemical/x-pdb", - "jpg" => "image/jpeg", - "tif" => "image/tiff", - "iges" => "model/iges", - "mesh" => "model/mesh", - "vrml" => "model/vrml", - "htm" => "text/html", - "txt" => "text/plain", - "sgm" => "text/sgml", - "mpg" => "video/mpeg", - "mov" => "video/quicktime", - "lha" => "application/octet-stream", - "ps" => "application/postscript", - "dxr" => "application/x-director", - "skt" => "application/x-koan", - "roff" => "application/x-troff", - "kar" => "audio/midi", - "mp3" => "audio/mpeg", - "aifc" => "audio/x-aiff", - "jpe" => "image/jpeg", - "silo" => "model/mesh", - "mpe" => "video/mpeg", - "lzh" => "application/octet-stream", - "skm" => "application/x-koan", - "exe" => "application/octet-stream", - "class" => "application/octet-stream" -); -?> diff --git a/pclzip_lib.php b/pclzip_lib.php deleted file mode 100644 index e62a292..0000000 --- a/pclzip_lib.php +++ /dev/null @@ -1,4018 +0,0 @@ -zipname = $p_zipname; - $this->zip_fd = 0; - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 1); - return; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : - // create($p_filelist, $p_add_dir="", $p_remove_dir="") - // create($p_filelist, $p_option, $p_option_value, ...) - // Description : - // This method supports two different synopsis. The first one is historical. - // This method creates a Zip Archive. The Zip file is created in the - // filesystem. The files and directories indicated in $p_filelist - // are added in the archive. See the parameters description for the - // supported format of $p_filelist. - // When a directory is in the list, the directory and its content is added - // in the archive. - // In this synopsis, the function takes an optional variable list of - // options. See bellow the supported options. - // Parameters : - // $p_filelist : An array containing file or directory names, or - // a string containing one filename or one directory name, or - // a string containing a list of filenames and/or directory - // names separated by spaces. - // $p_add_dir : A path to add before the real path of the archived file, - // in order to have it memorized in the archive. - // $p_remove_dir : A path to remove from the real path of the file to archive, - // in order to have a shorter path memorized in the archive. - // When $p_add_dir and $p_remove_dir are set, $p_remove_dir - // is removed first, before $p_add_dir is added. - // Options : - // PCLZIP_OPT_ADD_PATH : - // PCLZIP_OPT_REMOVE_PATH : - // PCLZIP_OPT_REMOVE_ALL_PATH : - // PCLZIP_CB_PRE_ADD : - // PCLZIP_CB_POST_ADD : - // Return Values : - // 0 on failure, - // The list of the added files, with a status of the add action. - // (see PclZip::listContent() for list entry format) - // -------------------------------------------------------------------------------- -// function create($p_filelist, $p_add_dir="", $p_remove_dir="") - function create($p_filelist /*, options */) - { - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, 'PclZip::create', "filelist='$p_filelist', ..."); - $v_result=1; - // ----- Reset the error handler - $this->privErrorReset(); - // ----- Set default values - $v_options = array(); - $v_add_path = ""; - $v_remove_path = ""; - $v_remove_all_path = false; - $v_options[PCLZIP_OPT_NO_COMPRESSION] = FALSE; - // ----- Look for variable options arguments - $v_size = func_num_args(); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "$v_size arguments passed to the method"); - // ----- Look for arguments - if ($v_size > 1) { - // ----- Get the arguments - $v_arg_list = &func_get_args(); - // ----- Remove form the options list the first argument - array_shift($v_arg_list); - $v_size--; - // ----- Look for first arg - if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Variable list of options detected"); - // ----- Parse the options - $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, - array (PCLZIP_OPT_REMOVE_PATH => 'optional', - PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', - PCLZIP_OPT_ADD_PATH => 'optional', - PCLZIP_CB_PRE_ADD => 'optional', - PCLZIP_CB_POST_ADD => 'optional', - PCLZIP_OPT_NO_COMPRESSION => 'optional' )); - if ($v_result != 1) { - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0); - return 0; - } - // ----- Set the arguments - if (isset($v_options[PCLZIP_OPT_ADD_PATH])) { - $v_add_path = $v_options[PCLZIP_OPT_ADD_PATH]; - } - if (isset($v_options[PCLZIP_OPT_REMOVE_PATH])) { - $v_remove_path = $v_options[PCLZIP_OPT_REMOVE_PATH]; - } - if (isset($v_options[PCLZIP_OPT_REMOVE_ALL_PATH])) { - $v_remove_all_path = $v_options[PCLZIP_OPT_REMOVE_ALL_PATH]; - } - } - // ----- Look for 2 args - // Here we need to support the first historic synopsis of the - // method. - else { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Static synopsis"); - // ----- Get the first argument - $v_add_path = $v_arg_list[0]; - // ----- Look for the optional second argument - if ($v_size == 2) { - $v_remove_path = $v_arg_list[1]; - } - else if ($v_size > 2) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments"); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return 0; - } - } - } - // ----- Trace - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "add_path='$v_add_path', remove_path='$v_remove_path', remove_all_path='".($v_remove_all_path?'true':'false')."'"); - // ----- Look if the $p_filelist is really an array - $p_result_list = array(); - if (is_array($p_filelist)) - { - // ----- Call the create fct - $v_result = $this->privCreate($p_filelist, $p_result_list, $v_add_path, $v_remove_path, $v_remove_all_path, $v_options); - } - // ----- Look if the $p_filelist is a string - else if (is_string($p_filelist)) - { - // ----- Create a list with the elements from the string - $v_list = explode(PCLZIP_SEPARATOR, $p_filelist); - // ----- Call the create fct - $v_result = $this->privCreate($v_list, $p_result_list, $v_add_path, $v_remove_path, $v_remove_all_path, $v_options); - } - // ----- Invalid variable - else - { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_filelist"); - $v_result = PCLZIP_ERR_INVALID_PARAMETER; - } - if ($v_result != 1) - { - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0); - return 0; - } - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $p_result_list); - return $p_result_list; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : - // add($p_filelist, $p_add_dir="", $p_remove_dir="") - // add($p_filelist, $p_option, $p_option_value, ...) - // Description : - // This method supports two synopsis. The first one is historical. - // This methods add the list of files in an existing archive. - // If a file with the same name already exists, it is added at the end of the - // archive, the first one is still present. - // If the archive does not exist, it is created. - // Parameters : - // $p_filelist : An array containing file or directory names, or - // a string containing one filename or one directory name, or - // a string containing a list of filenames and/or directory - // names separated by spaces. - // $p_add_dir : A path to add before the real path of the archived file, - // in order to have it memorized in the archive. - // $p_remove_dir : A path to remove from the real path of the file to archive, - // in order to have a shorter path memorized in the archive. - // When $p_add_dir and $p_remove_dir are set, $p_remove_dir - // is removed first, before $p_add_dir is added. - // Options : - // PCLZIP_OPT_ADD_PATH : - // PCLZIP_OPT_REMOVE_PATH : - // PCLZIP_OPT_REMOVE_ALL_PATH : - // PCLZIP_CB_PRE_ADD : - // PCLZIP_CB_POST_ADD : - // Return Values : - // 0 on failure, - // The list of the added files, with a status of the add action. - // (see PclZip::listContent() for list entry format) - // -------------------------------------------------------------------------------- -// function add($p_filelist, $p_add_dir="", $p_remove_dir="") - function add($p_filelist /* options */) - { - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, 'PclZip::add', "filelist='$p_filelist', ..."); - $v_result=1; - // ----- Reset the error handler - $this->privErrorReset(); - // ----- Set default values - $v_options = array(); - $v_add_path = ""; - $v_remove_path = ""; - $v_remove_all_path = false; - $v_options[PCLZIP_OPT_NO_COMPRESSION] = FALSE; - // ----- Look for variable options arguments - $v_size = func_num_args(); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "$v_size arguments passed to the method"); - // ----- Look for arguments - if ($v_size > 1) { - // ----- Get the arguments - $v_arg_list = &func_get_args(); - // ----- Remove form the options list the first argument - array_shift($v_arg_list); - $v_size--; - // ----- Look for first arg - if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Variable list of options detected"); - // ----- Parse the options - $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, - array (PCLZIP_OPT_REMOVE_PATH => 'optional', - PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', - PCLZIP_OPT_ADD_PATH => 'optional', - PCLZIP_CB_PRE_ADD => 'optional', - PCLZIP_CB_POST_ADD => 'optional', - PCLZIP_OPT_NO_COMPRESSION => 'optional' )); - if ($v_result != 1) { - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0); - return 0; - } - // ----- Set the arguments - if (isset($v_options[PCLZIP_OPT_ADD_PATH])) { - $v_add_path = $v_options[PCLZIP_OPT_ADD_PATH]; - } - if (isset($v_options[PCLZIP_OPT_REMOVE_PATH])) { - $v_remove_path = $v_options[PCLZIP_OPT_REMOVE_PATH]; - } - if (isset($v_options[PCLZIP_OPT_REMOVE_ALL_PATH])) { - $v_remove_all_path = $v_options[PCLZIP_OPT_REMOVE_ALL_PATH]; - } - } - // ----- Look for 2 args - // Here we need to support the first historic synopsis of the - // method. - else { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Static synopsis"); - // ----- Get the first argument - $v_add_path = $v_arg_list[0]; - // ----- Look for the optional second argument - if ($v_size == 2) { - $v_remove_path = $v_arg_list[1]; - } - else if ($v_size > 2) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments"); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return 0; - } - } - } - // ----- Trace - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "add_path='$v_add_path', remove_path='$v_remove_path', remove_all_path='".($v_remove_all_path?'true':'false')."'"); - // ----- Look if the $p_filelist is really an array - $p_result_list = array(); - if (is_array($p_filelist)) - { - // ----- Call the create fct - $v_result = $this->privAdd($p_filelist, $p_result_list, $v_add_path, $v_remove_path, $v_remove_all_path, $v_options); - } - // ----- Look if the $p_filelist is a string - else if (is_string($p_filelist)) - { - // ----- Create a list with the elements from the string - $v_list = explode(PCLZIP_SEPARATOR, $p_filelist); - // ----- Call the create fct - $v_result = $this->privAdd($v_list, $p_result_list, $v_add_path, $v_remove_path, $v_remove_all_path, $v_options); - } - // ----- Invalid variable - else - { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_filelist"); - $v_result = PCLZIP_ERR_INVALID_PARAMETER; - } - if ($v_result != 1) - { - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0); - return 0; - } - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $p_result_list); - return $p_result_list; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : listContent() - // Description : - // This public method, gives the list of the files and directories, with their - // properties. - // The properties of each entries in the list are (used also in other functions) : - // filename : Name of the file. For a create or add action it is the filename - // given by the user. For an extract function it is the filename - // of the extracted file. - // stored_filename : Name of the file / directory stored in the archive. - // size : Size of the stored file. - // compressed_size : Size of the file's data compressed in the archive - // (without the headers overhead) - // mtime : Last known modification date of the file (UNIX timestamp) - // comment : Comment associated with the file - // folder : true | false - // index : index of the file in the archive - // status : status of the action (depending of the action) : - // Values are : - // ok : OK ! - // filtered : the file / dir is not extracted (filtered by user) - // already_a_directory : the file can not be extracted because a - // directory with the same name already exists - // write_protected : the file can not be extracted because a file - // with the same name already exists and is - // write protected - // newer_exist : the file was not extracted because a newer file exists - // path_creation_fail : the file is not extracted because the folder - // does not exists and can not be created - // write_error : the file was not extracted because there was a - // error while writing the file - // read_error : the file was not extracted because there was a error - // while reading the file - // invalid_header : the file was not extracted because of an archive - // format error (bad file header) - // Note that each time a method can continue operating when there - // is an action error on a file, the error is only logged in the file status. - // Return Values : - // 0 on an unrecoverable failure, - // The list of the files in the archive. - // -------------------------------------------------------------------------------- - function listContent() - { - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, 'PclZip::listContent', ""); - $v_result=1; - // ----- Reset the error handler - $this->privErrorReset(); - // ----- Check archive - if (!$this->privCheckFormat()) { - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0); - return(0); - } - // ----- Call the extracting fct - $p_list = array(); - if (($v_result = $this->privList($p_list)) != 1) - { - unset($p_list); - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0, PclZip::errorInfo()); - return(0); - } - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $p_list); - return $p_list; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : - // extract($p_path="./", $p_remove_path="") - // extract([$p_option, $p_option_value, ...]) - // Description : - // This method supports two synopsis. The first one is historical. - // This method extract all the files / directories from the archive to the - // folder indicated in $p_path. - // If you want to ignore the 'root' part of path of the memorized files - // you can indicate this in the optional $p_remove_path parameter. - // By default, if a newer file with the same name already exists, the - // file is not extracted. - // - // If both PCLZIP_OPT_PATH and PCLZIP_OPT_ADD_PATH aoptions - // are used, the path indicated in PCLZIP_OPT_ADD_PATH is append - // at the end of the path value of PCLZIP_OPT_PATH. - // Parameters : - // $p_path : Path where the files and directories are to be extracted - // $p_remove_path : First part ('root' part) of the memorized path - // (if any similar) to remove while extracting. - // Options : - // PCLZIP_OPT_PATH : - // PCLZIP_OPT_ADD_PATH : - // PCLZIP_OPT_REMOVE_PATH : - // PCLZIP_OPT_REMOVE_ALL_PATH : - // PCLZIP_CB_PRE_EXTRACT : - // PCLZIP_CB_POST_EXTRACT : - // Return Values : - // 0 or a negative value on failure, - // The list of the extracted files, with a status of the action. - // (see PclZip::listContent() for list entry format) - // -------------------------------------------------------------------------------- - //function extract($p_path="./", $p_remove_path="") - function extract(/* options */) - { - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::extract", ""); - $v_result=1; - // ----- Reset the error handler - $this->privErrorReset(); - // ----- Check archive - if (!$this->privCheckFormat()) { - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0); - return(0); - } - // ----- Set default values - $v_options = array(); - $v_path = "./"; - $v_remove_path = ""; - $v_remove_all_path = false; - // ----- Look for variable options arguments - $v_size = func_num_args(); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "$v_size arguments passed to the method"); - // ----- Default values for option - $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE; - // ----- Look for arguments - if ($v_size > 0) { - // ----- Get the arguments - $v_arg_list = &func_get_args(); - // ----- Look for first arg - if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Variable list of options"); - // ----- Parse the options - $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, - array (PCLZIP_OPT_PATH => 'optional', - PCLZIP_OPT_REMOVE_PATH => 'optional', - PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', - PCLZIP_OPT_ADD_PATH => 'optional', - PCLZIP_CB_PRE_EXTRACT => 'optional', - PCLZIP_CB_POST_EXTRACT => 'optional', - PCLZIP_OPT_SET_CHMOD => 'optional', - PCLZIP_OPT_BY_NAME => 'optional', - PCLZIP_OPT_BY_EREG => 'optional', - PCLZIP_OPT_BY_PREG => 'optional', - PCLZIP_OPT_BY_INDEX => 'optional', - PCLZIP_OPT_EXTRACT_AS_STRING => 'optional' )); - if ($v_result != 1) { - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0); - return 0; - } - // ----- Set the arguments - if (isset($v_options[PCLZIP_OPT_PATH])) { - $v_path = $v_options[PCLZIP_OPT_PATH]; - } - if (isset($v_options[PCLZIP_OPT_REMOVE_PATH])) { - $v_remove_path = $v_options[PCLZIP_OPT_REMOVE_PATH]; - } - if (isset($v_options[PCLZIP_OPT_REMOVE_ALL_PATH])) { - $v_remove_all_path = $v_options[PCLZIP_OPT_REMOVE_ALL_PATH]; - } - if (isset($v_options[PCLZIP_OPT_ADD_PATH])) { - // ----- Check for '/' in last path char - if ((strlen($v_path) > 0) && (substr($v_path, -1) != '/')) { - $v_path .= '/'; - } - $v_path .= $v_options[PCLZIP_OPT_ADD_PATH]; - } - } - // ----- Look for 2 args - // Here we need to support the first historic synopsis of the - // method. - else { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Static synopsis"); - // ----- Get the first argument - $v_path = $v_arg_list[0]; - // ----- Look for the optional second argument - if ($v_size == 2) { - $v_remove_path = $v_arg_list[1]; - } - else if ($v_size > 2) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments"); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0, PclZip::errorInfo()); - return 0; - } - } - } - // ----- Trace - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "path='$v_path', remove_path='$v_remove_path', remove_all_path='".($v_remove_path?'true':'false')."'"); - // ----- Call the extracting fct - $p_list = array(); - if (($v_result = $this->privExtractByRule($p_list, $v_path, $v_remove_path, $v_remove_all_path, $v_options)) != 1) - { - unset($p_list); - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0, PclZip::errorInfo()); - return(0); - } - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $p_list); - return $p_list; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : - // extractByIndex($p_index, $p_path="./", $p_remove_path="") - // extractByIndex($p_index, [$p_option, $p_option_value, ...]) - // Description : - // This method supports two synopsis. The first one is historical. - // This method is doing a partial extract of the archive. - // The extracted files or folders are identified by their index in the - // archive (from 0 to n). - // Note that if the index identify a folder, only the folder entry is - // extracted, not all the files included in the archive. - // Parameters : - // $p_index : A single index (integer) or a string of indexes of files to - // extract. The form of the string is "0,4-6,8-12" with only numbers - // and '-' for range or ',' to separate ranges. No spaces or ';' - // are allowed. - // $p_path : Path where the files and directories are to be extracted - // $p_remove_path : First part ('root' part) of the memorized path - // (if any similar) to remove while extracting. - // Options : - // PCLZIP_OPT_PATH : - // PCLZIP_OPT_ADD_PATH : - // PCLZIP_OPT_REMOVE_PATH : - // PCLZIP_OPT_REMOVE_ALL_PATH : - // PCLZIP_OPT_EXTRACT_AS_STRING : The files are extracted as strings and - // not as files. - // The resulting content is in a new field 'content' in the file - // structure. - // This option must be used alone (any other options are ignored). - // PCLZIP_CB_PRE_EXTRACT : - // PCLZIP_CB_POST_EXTRACT : - // Return Values : - // 0 on failure, - // The list of the extracted files, with a status of the action. - // (see PclZip::listContent() for list entry format) - // -------------------------------------------------------------------------------- - function extractByIndex($p_index /* $options */) - { - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::extractByIndex", "index='$p_index', ..."); - $v_result=1; - // ----- Reset the error handler - $this->privErrorReset(); - // ----- Check archive - if (!$this->privCheckFormat()) { - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0); - return(0); - } - // ----- Set default values - $v_options = array(); - $v_path = "./"; - $v_remove_path = ""; - $v_remove_all_path = false; - // ----- Look for variable options arguments - $v_size = func_num_args(); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "$v_size arguments passed to the method"); - // ----- Default values for option - $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE; - // ----- Look for arguments - if ($v_size > 1) { - // ----- Get the arguments - $v_arg_list = &func_get_args(); - // ----- Remove form the options list the first argument - array_shift($v_arg_list); - $v_size--; - // ----- Look for first arg - if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Variable list of options"); - // ----- Parse the options - $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, - array (PCLZIP_OPT_PATH => 'optional', - PCLZIP_OPT_REMOVE_PATH => 'optional', - PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', - PCLZIP_OPT_EXTRACT_AS_STRING => 'optional', - PCLZIP_OPT_ADD_PATH => 'optional', - PCLZIP_CB_PRE_EXTRACT => 'optional', - PCLZIP_CB_POST_EXTRACT => 'optional', - PCLZIP_OPT_SET_CHMOD => 'optional' )); - if ($v_result != 1) { - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0); - return 0; - } - // ----- Set the arguments - if (isset($v_options[PCLZIP_OPT_PATH])) { - $v_path = $v_options[PCLZIP_OPT_PATH]; - } - if (isset($v_options[PCLZIP_OPT_REMOVE_PATH])) { - $v_remove_path = $v_options[PCLZIP_OPT_REMOVE_PATH]; - } - if (isset($v_options[PCLZIP_OPT_REMOVE_ALL_PATH])) { - $v_remove_all_path = $v_options[PCLZIP_OPT_REMOVE_ALL_PATH]; - } - if (isset($v_options[PCLZIP_OPT_ADD_PATH])) { - // ----- Check for '/' in last path char - if ((strlen($v_path) > 0) && (substr($v_path, -1) != '/')) { - $v_path .= '/'; - } - $v_path .= $v_options[PCLZIP_OPT_ADD_PATH]; - } - if (!isset($v_options[PCLZIP_OPT_EXTRACT_AS_STRING])) { - $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE; - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Option PCLZIP_OPT_EXTRACT_AS_STRING not set."); - } - else { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Option PCLZIP_OPT_EXTRACT_AS_STRING set."); - } - } - // ----- Look for 2 args - // Here we need to support the first historic synopsis of the - // method. - else { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Static synopsis"); - // ----- Get the first argument - $v_path = $v_arg_list[0]; - // ----- Look for the optional second argument - if ($v_size == 2) { - $v_remove_path = $v_arg_list[1]; - } - else if ($v_size > 2) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments"); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return 0; - } - } - } - // ----- Trace - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "index='$p_index', path='$v_path', remove_path='$v_remove_path', remove_all_path='".($v_remove_path?'true':'false')."'"); - // ----- Trick - // Here I want to reuse extractByRule(), so I need to parse the $p_index - // with privParseOptions() - $v_arg_trick = array (PCLZIP_OPT_BY_INDEX, $p_index); - $v_options_trick = array(); - $v_result = $this->privParseOptions($v_arg_trick, sizeof($v_arg_trick), $v_options_trick, - array (PCLZIP_OPT_BY_INDEX => 'optional' )); - if ($v_result != 1) { - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0); - return 0; - } - $v_options[PCLZIP_OPT_BY_INDEX] = $v_options_trick[PCLZIP_OPT_BY_INDEX]; - // ----- Call the extracting fct - if (($v_result = $this->privExtractByRule($p_list, $v_path, $v_remove_path, $v_remove_all_path, $v_options)) != 1) { - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0, PclZip::errorInfo()); - return(0); - } - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $p_list); - return $p_list; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : - // delete([$p_option, $p_option_value, ...]) - // Description : - // Parameters : - // None - // Options : - // PCLZIP_OPT_BY_INDEX : - // Return Values : - // 0 on failure, - // The list of the files which are still present in the archive. - // (see PclZip::listContent() for list entry format) - // -------------------------------------------------------------------------------- - function delete(/* options */) - { - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::delete", ""); - $v_result=1; - // ----- Reset the error handler - $this->privErrorReset(); - // ----- Check archive - if (!$this->privCheckFormat()) { - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0); - return(0); - } - // ----- Set default values - $v_options = array(); - // ----- Look for variable options arguments - $v_size = func_num_args(); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "$v_size arguments passed to the method"); - // ----- Look for no arguments - if ($v_size <= 0) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Missing arguments"); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0, PclZip::errorInfo()); - return 0; - } - // ----- Get the arguments - $v_arg_list = &func_get_args(); - // ----- Parse the options - $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, - array (PCLZIP_OPT_BY_NAME => 'optional', - PCLZIP_OPT_BY_EREG => 'optional', - PCLZIP_OPT_BY_PREG => 'optional', - PCLZIP_OPT_BY_INDEX => 'optional' )); - if ($v_result != 1) { - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0); - return 0; - } - // ----- Check that at least one rule is set - if ( (!isset($v_options[PCLZIP_OPT_BY_NAME])) - && (!isset($v_options[PCLZIP_OPT_BY_EREG])) - && (!isset($v_options[PCLZIP_OPT_BY_PREG])) - && (!isset($v_options[PCLZIP_OPT_BY_INDEX]))) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "At least one filtering rule must be set"); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0, PclZip::errorInfo()); - return 0; - } - // ----- Call the delete fct - $v_list = array(); - if (($v_result = $this->privDeleteByRule($v_list, $v_options)) != 1) - { - unset($v_list); - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0, PclZip::errorInfo()); - return(0); - } - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_list); - return $v_list; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : deleteByIndex() - // Description : - // ***** Deprecated ***** - // delete(PCLZIP_OPT_BY_INDEX, $p_index) should be prefered. - // -------------------------------------------------------------------------------- - function deleteByIndex($p_index) - { - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::deleteByIndex", "index='$p_index'"); - $p_list = $this->delete(PCLZIP_OPT_BY_INDEX, $p_index); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $p_list); - return $p_list; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : properties() - // Description : - // This method gives the properties of the archive. - // The properties are : - // nb : Number of files in the archive - // comment : Comment associated with the archive file - // status : not_exist, ok - // Parameters : - // None - // Return Values : - // 0 on failure, - // An array with the archive properties. - // -------------------------------------------------------------------------------- - function properties() - { - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::properties", ""); - // ----- Reset the error handler - $this->privErrorReset(); - // ----- Check archive - if (!$this->privCheckFormat()) { - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0); - return(0); - } - // ----- Default properties - $v_prop = array(); - $v_prop['comment'] = ''; - $v_prop['nb'] = 0; - $v_prop['status'] = 'not_exist'; - // ----- Look if file exists - if (@is_file($this->zipname)) - { - // ----- Open the zip file - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Open file in binary read mode"); - if (($this->zip_fd = @fopen($this->zipname, 'rb')) == 0) - { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in binary read mode'); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), 0); - return 0; - } - // ----- Read the central directory informations - $v_central_dir = array(); - if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) - { - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0); - return 0; - } - // ----- Close the zip file - $this->privCloseFd(); - // ----- Set the user attributes - $v_prop['comment'] = $v_central_dir['comment']; - $v_prop['nb'] = $v_central_dir['entries']; - $v_prop['status'] = 'ok'; - } - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_prop); - return $v_prop; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : duplicate() - // Description : - // This method creates an archive by copying the content of an other one. If - // the archive already exist, it is replaced by the new one without any warning. - // Parameters : - // $p_archive : The filename of a valid archive, or - // a valid PclZip object. - // Return Values : - // 1 on success. - // 0 or a negative value on error (error code). - // -------------------------------------------------------------------------------- - function duplicate($p_archive) - { - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::duplicate", ""); - $v_result = 1; - // ----- Reset the error handler - $this->privErrorReset(); - // ----- Look if the $p_archive is a PclZip object - if ((is_object($p_archive)) && (get_class($p_archive) == 'pclzip')) - { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "The parameter is valid PclZip object '".$p_archive->zipname."'"); - // ----- Duplicate the archive - $v_result = $this->privDuplicate($p_archive->zipname); - } - // ----- Look if the $p_archive is a string (so a filename) - else if (is_string($p_archive)) - { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "The parameter is a filename '$p_archive'"); - // ----- Check that $p_archive is a valid zip file - // TBC : Should also check the archive format - if (!is_file($p_archive)) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_MISSING_FILE, "No file with filename '".$p_archive."'"); - $v_result = PCLZIP_ERR_MISSING_FILE; - } - else { - // ----- Duplicate the archive - $v_result = $this->privDuplicate($p_archive); - } - } - // ----- Invalid variable - else - { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_archive_to_add"); - $v_result = PCLZIP_ERR_INVALID_PARAMETER; - } - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : merge() - // Description : - // This method merge the $p_archive_to_add archive at the end of the current - // one ($this). - // If the archive ($this) does not exist, the merge becomes a duplicate. - // If the $p_archive_to_add archive does not exist, the merge is a success. - // Parameters : - // $p_archive_to_add : It can be directly the filename of a valid zip archive, - // or a PclZip object archive. - // Return Values : - // 1 on success, - // 0 or negative values on error (see below). - // -------------------------------------------------------------------------------- - function merge($p_archive_to_add) - { - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::merge", ""); - $v_result = 1; - // ----- Reset the error handler - $this->privErrorReset(); - // ----- Check archive - if (!$this->privCheckFormat()) { - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, 0); - return(0); - } - // ----- Look if the $p_archive_to_add is a PclZip object - if ((is_object($p_archive_to_add)) && (get_class($p_archive_to_add) == 'pclzip')) - { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "The parameter is valid PclZip object"); - // ----- Merge the archive - $v_result = $this->privMerge($p_archive_to_add); - } - // ----- Look if the $p_archive_to_add is a string (so a filename) - else if (is_string($p_archive_to_add)) - { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "The parameter is a filename"); - // ----- Create a temporary archive - $v_object_archive = new PclZip($p_archive_to_add); - // ----- Merge the archive - $v_result = $this->privMerge($v_object_archive); - } - // ----- Invalid variable - else - { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_archive_to_add"); - $v_result = PCLZIP_ERR_INVALID_PARAMETER; - } - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : errorCode() - // Description : - // Parameters : - // -------------------------------------------------------------------------------- - function errorCode() - { - if (PCLZIP_ERROR_EXTERNAL == 1) { - return(PclErrorCode()); - } - else { - return($this->error_code); - } - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : errorName() - // Description : - // Parameters : - // -------------------------------------------------------------------------------- - function errorName($p_with_code=false) - { - $v_name = array ( PCLZIP_ERR_NO_ERROR => 'PCLZIP_ERR_NO_ERROR', - PCLZIP_ERR_WRITE_OPEN_FAIL => 'PCLZIP_ERR_WRITE_OPEN_FAIL', - PCLZIP_ERR_READ_OPEN_FAIL => 'PCLZIP_ERR_READ_OPEN_FAIL', - PCLZIP_ERR_INVALID_PARAMETER => 'PCLZIP_ERR_INVALID_PARAMETER', - PCLZIP_ERR_MISSING_FILE => 'PCLZIP_ERR_MISSING_FILE', - PCLZIP_ERR_FILENAME_TOO_LONG => 'PCLZIP_ERR_FILENAME_TOO_LONG', - PCLZIP_ERR_INVALID_ZIP => 'PCLZIP_ERR_INVALID_ZIP', - PCLZIP_ERR_BAD_EXTRACTED_FILE => 'PCLZIP_ERR_BAD_EXTRACTED_FILE', - PCLZIP_ERR_DIR_CREATE_FAIL => 'PCLZIP_ERR_DIR_CREATE_FAIL', - PCLZIP_ERR_BAD_EXTENSION => 'PCLZIP_ERR_BAD_EXTENSION', - PCLZIP_ERR_BAD_FORMAT => 'PCLZIP_ERR_BAD_FORMAT', - PCLZIP_ERR_DELETE_FILE_FAIL => 'PCLZIP_ERR_DELETE_FILE_FAIL', - PCLZIP_ERR_RENAME_FILE_FAIL => 'PCLZIP_ERR_RENAME_FILE_FAIL', - PCLZIP_ERR_BAD_CHECKSUM => 'PCLZIP_ERR_BAD_CHECKSUM', - PCLZIP_ERR_INVALID_ARCHIVE_ZIP => 'PCLZIP_ERR_INVALID_ARCHIVE_ZIP', - PCLZIP_ERR_MISSING_OPTION_VALUE => 'PCLZIP_ERR_MISSING_OPTION_VALUE', - PCLZIP_ERR_INVALID_OPTION_VALUE => 'PCLZIP_ERR_INVALID_OPTION_VALUE' ); - if (isset($v_name[$this->error_code])) { - $v_value = $v_name[$this->error_code]; - } - else { - $v_value = 'NoName'; - } - if ($p_with_code) { - return($v_value.' ('.$this->error_code.')'); - } - else { - return($v_value); - } - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : errorInfo() - // Description : - // Parameters : - // -------------------------------------------------------------------------------- - function errorInfo($p_full=false) - { - if (PCLZIP_ERROR_EXTERNAL == 1) { - return(PclErrorString()); - } - else { - if ($p_full) { - return($this->errorName(true)." : ".$this->error_string); - } - else { - return($this->error_string." [code ".$this->error_code."]"); - } - } - } - // -------------------------------------------------------------------------------- -// -------------------------------------------------------------------------------- -// ***** UNDER THIS LINE ARE DEFINED PRIVATE INTERNAL FUNCTIONS ***** -// ***** ***** -// ***** THESES FUNCTIONS MUST NOT BE USED DIRECTLY ***** -// -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : privCheckFormat() - // Description : - // This method check that the archive exists and is a valid zip archive. - // Several level of check exists. (futur) - // Parameters : - // $p_level : Level of check. Default 0. - // 0 : Check the first bytes (magic codes) (default value)) - // 1 : 0 + Check the central directory (futur) - // 2 : 1 + Check each file header (futur) - // Return Values : - // true on success, - // false on error, the error code is set. - // -------------------------------------------------------------------------------- - function privCheckFormat($p_level=0) - { - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privCheckFormat", ""); - $v_result = true; - // ----- Reset the error handler - $this->privErrorReset(); - // ----- Look if the file exits - if (!is_file($this->zipname)) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_MISSING_FILE, "Missing archive file '".$this->zipname."'"); - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, false, PclZip::errorInfo()); - return(false); - } - // ----- Check that the file is readeable - if (!is_readable($this->zipname)) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to read archive '".$this->zipname."'"); - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, false, PclZip::errorInfo()); - return(false); - } - // ----- Check the magic code - // TBC - // ----- Check the central header - // TBC - // ----- Check each file header - // TBC - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : privParseOptions() - // Description : - // This internal methods reads the variable list of arguments ($p_options_list, - // $p_size) and generate an array with the options and values ($v_result_list). - // $v_requested_options contains the options that can be present and those that - // must be present. - // $v_requested_options is an array, with the option value as key, and 'optional', - // or 'mandatory' as value. - // Parameters : - // See above. - // Return Values : - // 1 on success. - // 0 on failure. - // -------------------------------------------------------------------------------- - function privParseOptions(&$p_options_list, $p_size, &$v_result_list, $v_requested_options) - { - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privParseOptions", ""); - $v_result=1; - // ----- Read the options - $i=0; - while ($i<$p_size) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Looking for table index $i, option = '".PclZipUtilOptionText($p_options_list[$i])."(".$p_options_list[$i].")'"); - // ----- Check if the option is requested - if (!isset($v_requested_options[$p_options_list[$i]])) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid optional parameter '".$p_options_list[$i]."' for this method"); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - // ----- Look for next option - switch ($p_options_list[$i]) { - // ----- Look for options that request a path value - case PCLZIP_OPT_PATH : - case PCLZIP_OPT_REMOVE_PATH : - case PCLZIP_OPT_ADD_PATH : - // ----- Check the number of parameters - if (($i+1) >= $p_size) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - // ----- Get the value - $v_result_list[$p_options_list[$i]] = PclZipUtilTranslateWinPath($p_options_list[$i+1], false); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "".PclZipUtilOptionText($p_options_list[$i])." = '".$v_result_list[$p_options_list[$i]]."'"); - $i++; - break; - // ----- Look for options that request an array of string for value - case PCLZIP_OPT_BY_NAME : - // ----- Check the number of parameters - if (($i+1) >= $p_size) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - // ----- Get the value - if (is_string($p_options_list[$i+1])) { - $v_result_list[$p_options_list[$i]][0] = $p_options_list[$i+1]; - } - else if (is_array($p_options_list[$i+1])) { - $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1]; - } - else { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Wrong parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - ////--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "".PclZipUtilOptionText($p_options_list[$i])." = '".$v_result_list[$p_options_list[$i]]."'"); - $i++; - break; - // ----- Look for options that request an EREG or PREG expression - case PCLZIP_OPT_BY_EREG : - case PCLZIP_OPT_BY_PREG : - // ----- Check the number of parameters - if (($i+1) >= $p_size) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - // ----- Get the value - if (is_string($p_options_list[$i+1])) { - $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1]; - } - else { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Wrong parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "".PclZipUtilOptionText($p_options_list[$i])." = '".$v_result_list[$p_options_list[$i]]."'"); - $i++; - break; - // ----- Look for options that request an array of index - case PCLZIP_OPT_BY_INDEX : - // ----- Check the number of parameters - if (($i+1) >= $p_size) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - // ----- Get the value - $v_work_list = array(); - if (is_string($p_options_list[$i+1])) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Index value is a string '".$p_options_list[$i+1]."'"); - // ----- Remove spaces - $p_options_list[$i+1] = strtr($p_options_list[$i+1], ' ', ''); - // ----- Parse items - $v_work_list = explode(",", $p_options_list[$i+1]); - } - else if (is_integer($p_options_list[$i+1])) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Index value is an integer '".$p_options_list[$i+1]."'"); - $v_work_list[0] = $p_options_list[$i+1].'-'.$p_options_list[$i+1]; - } - else if (is_array($p_options_list[$i+1])) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Index value is an array"); - $v_work_list = $p_options_list[$i+1]; - } - else { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Value must be integer, string or array for option '".PclZipUtilOptionText($p_options_list[$i])."'"); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - // ----- Reduce the index list - // each index item in the list must be a couple with a start and - // an end value : [0,3], [5-5], [8-10], ... - // ----- Check the format of each item - $v_sort_flag=false; - $v_sort_value=0; - for ($j=0; $j= $p_size) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - // ----- Get the value - $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1]; - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "".PclZipUtilOptionText($p_options_list[$i])." = '".$v_result_list[$p_options_list[$i]]."'"); - $i++; - break; - // ----- Look for options that request a call-back - case PCLZIP_CB_PRE_EXTRACT : - case PCLZIP_CB_POST_EXTRACT : - case PCLZIP_CB_PRE_ADD : - case PCLZIP_CB_POST_ADD : - /* for futur use - case PCLZIP_CB_PRE_DELETE : - case PCLZIP_CB_POST_DELETE : - case PCLZIP_CB_PRE_LIST : - case PCLZIP_CB_POST_LIST : - */ - // ----- Check the number of parameters - if (($i+1) >= $p_size) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - // ----- Get the value - $v_function_name = $p_options_list[$i+1]; - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "call-back ".PclZipUtilOptionText($p_options_list[$i])." = '".$v_function_name."'"); - // ----- Check that the value is a valid existing function - if (!function_exists($v_function_name)) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Function '".$v_function_name."()' is not an existing function for option '".PclZipUtilOptionText($p_options_list[$i])."'"); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - // ----- Set the attribute - $v_result_list[$p_options_list[$i]] = $v_function_name; - $i++; - break; - default : - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Unknown optional parameter '".$p_options_list[$i]."'"); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - // ----- Next options - $i++; - } - // ----- Look for mandatory options - for ($key=reset($v_requested_options); $key=key($v_requested_options); $key=next($v_requested_options)) { - // ----- Look for mandatory option - if ($v_requested_options[$key] == 'mandatory') { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Detect a mandatory option : ".PclZipUtilOptionText($key)."(".$key.")"); - // ----- Look if present - if (!isset($v_result_list[$key])) { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Missing mandatory parameter ".PclZipUtilOptionText($key)."(".$key.")"); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - } - } - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : privCreate() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privCreate($p_list, &$p_result_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, &$p_options) - { - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privCreate", "list, result_list, add_dir='$p_add_dir', remove_dir='$p_remove_dir'"); - $v_result=1; - $v_list_detail = array(); - // ----- Open the file in write mode - if (($v_result = $this->privOpenFd('wb')) != 1) - { - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // ----- Add the list of files - $v_result = $this->privAddList($p_list, $p_result_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, $p_options); - // ----- Close - $this->privCloseFd(); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : privAdd() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privAdd($p_list, &$p_result_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, &$p_options) - { - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privAdd", "list, result_list, add_dir='$p_add_dir', remove_dir='$p_remove_dir'"); - $v_result=1; - $v_list_detail = array(); - // ----- Look if the archive exists or is empty - if ((!is_file($this->zipname)) || (filesize($this->zipname) == 0)) - { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Archive does not exist, or is empty, create it."); - // ----- Do a create - $v_result = $this->privCreate($p_list, $p_result_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, $p_options); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // ----- Open the zip file - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Open file in binary read mode"); - if (($v_result=$this->privOpenFd('rb')) != 1) - { - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // ----- Read the central directory informations - $v_central_dir = array(); - if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) - { - $this->privCloseFd(); - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // ----- Go to beginning of File - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position in file : ".ftell($this->zip_fd)."'"); - @rewind($this->zip_fd); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position in file : ".ftell($this->zip_fd)."'"); - // ----- Creates a temporay file - $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp'; - // ----- Open the temporary file in write mode - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Open file in binary read mode"); - if (($v_zip_temp_fd = @fopen($v_zip_temp_name, 'wb')) == 0) - { - $this->privCloseFd(); - PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_zip_temp_name.'\' in binary write mode'); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - // ----- Copy the files from the archive to the temporary file - // TBC : Here I should better append the file and go back to erase the central dir - $v_size = $v_central_dir['offset']; - while ($v_size != 0) - { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Read $v_read_size bytes"); - $v_buffer = fread($this->zip_fd, $v_read_size); - @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - // ----- Swap the file descriptor - // Here is a trick : I swap the temporary fd with the zip fd, in order to use - // the following methods on the temporary fil and not the real archive - $v_swap = $this->zip_fd; - $this->zip_fd = $v_zip_temp_fd; - $v_zip_temp_fd = $v_swap; - // ----- Add the files - $v_header_list = array(); - if (($v_result = $this->privAddFileList($p_list, $v_header_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, $p_options)) != 1) - { - fclose($v_zip_temp_fd); - $this->privCloseFd(); - @unlink($v_zip_temp_name); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // ----- Store the offset of the central dir - $v_offset = @ftell($this->zip_fd); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "New offset of central dir : $v_offset"); - // ----- Copy the block of file headers from the old archive - $v_size = $v_central_dir['size']; - while ($v_size != 0) - { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Read $v_read_size bytes"); - $v_buffer = @fread($v_zip_temp_fd, $v_read_size); - @fwrite($this->zip_fd, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - // ----- Create the Central Dir files header - for ($i=0, $v_count=0; $iprivWriteCentralFileHeader($v_header_list[$i])) != 1) { - fclose($v_zip_temp_fd); - $this->privCloseFd(); - @unlink($v_zip_temp_name); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - $v_count++; - } - // ----- Transform the header to a 'usable' info - $this->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]); - } - // ----- Zip file comment - $v_comment = ''; - // ----- Calculate the size of the central header - $v_size = @ftell($this->zip_fd)-$v_offset; - // ----- Create the central dir footer - if (($v_result = $this->privWriteCentralHeader($v_count+$v_central_dir['entries'], $v_size, $v_offset, $v_comment)) != 1) - { - // ----- Reset the file list - unset($v_header_list); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // ----- Swap back the file descriptor - $v_swap = $this->zip_fd; - $this->zip_fd = $v_zip_temp_fd; - $v_zip_temp_fd = $v_swap; - // ----- Close - $this->privCloseFd(); - // ----- Close the temporary file - @fclose($v_zip_temp_fd); - // ----- Delete the zip file - // TBC : I should test the result ... - @unlink($this->zipname); - // ----- Rename the temporary file - // TBC : I should test the result ... - //@rename($v_zip_temp_name, $this->zipname); - PclZipUtilRename($v_zip_temp_name, $this->zipname); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : privOpenFd() - // Description : - // Parameters : - // -------------------------------------------------------------------------------- - function privOpenFd($p_mode) - { - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privOpenFd", 'mode='.$p_mode); - $v_result=1; - // ----- Look if already open - if ($this->zip_fd != 0) - { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Zip file \''.$this->zipname.'\' already open'); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - // ----- Open the zip file - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Open file in '.$p_mode.' mode'); - if (($this->zip_fd = @fopen($this->zipname, $p_mode)) == 0) - { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in '.$p_mode.' mode'); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : privCloseFd() - // Description : - // Parameters : - // -------------------------------------------------------------------------------- - function privCloseFd() - { - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privCloseFd", ""); - $v_result=1; - if ($this->zip_fd != 0) - @fclose($this->zip_fd); - $this->zip_fd = 0; - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : privAddList() - // Description : - // $p_add_dir and $p_remove_dir will give the ability to memorize a path which is - // different from the real path of the file. This is usefull if you want to have PclTar - // running in any directory, and memorize relative path from an other directory. - // Parameters : - // $p_list : An array containing the file or directory names to add in the tar - // $p_result_list : list of added files with their properties (specially the status field) - // $p_add_dir : Path to add in the filename path archived - // $p_remove_dir : Path to remove in the filename path archived - // Return Values : - // -------------------------------------------------------------------------------- - function privAddList($p_list, &$p_result_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, &$p_options) - { - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privAddList", "list, add_dir='$p_add_dir', remove_dir='$p_remove_dir'"); - $v_result=1; - // ----- Add the files - $v_header_list = array(); - if (($v_result = $this->privAddFileList($p_list, $v_header_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, $p_options)) != 1) - { - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // ----- Store the offset of the central dir - $v_offset = @ftell($this->zip_fd); - // ----- Create the Central Dir files header - for ($i=0,$v_count=0; $iprivWriteCentralFileHeader($v_header_list[$i])) != 1) { - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - $v_count++; - } - // ----- Transform the header to a 'usable' info - $this->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]); - } - // ----- Zip file comment - $v_comment = ''; - // ----- Calculate the size of the central header - $v_size = @ftell($this->zip_fd)-$v_offset; - // ----- Create the central dir footer - if (($v_result = $this->privWriteCentralHeader($v_count, $v_size, $v_offset, $v_comment)) != 1) - { - // ----- Reset the file list - unset($v_header_list); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : privAddFileList() - // Description : - // $p_add_dir and $p_remove_dir will give the ability to memorize a path which is - // different from the real path of the file. This is usefull if you want to - // run the lib in any directory, and memorize relative path from an other directory. - // Parameters : - // $p_list : An array containing the file or directory names to add in the tar - // $p_result_list : list of added files with their properties (specially the status field) - // $p_add_dir : Path to add in the filename path archived - // $p_remove_dir : Path to remove in the filename path archived - // Return Values : - // -------------------------------------------------------------------------------- - function privAddFileList($p_list, &$p_result_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, &$p_options) - { - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privAddFileList", "list, add_dir='$p_add_dir', remove_dir='$p_remove_dir'"); - $v_result=1; - $v_header = array(); - // ----- Recuperate the current number of elt in list - $v_nb = sizeof($p_result_list); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Before add, list have $v_nb elements"); - // ----- Loop on the files - for ($j=0; ($j 0xFF) - { - // ----- Error log - PclZip::privErrorLog(-5, "File name is too long (max. 255) : '$p_filename'"); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - */ - // ----- Look if it is a file or a dir with no all pathnre move - if ((is_file($p_filename)) || ((is_dir($p_filename)) && !$p_remove_all_dir)) { - // ----- Add the file - if (($v_result = $this->privAddFile($p_filename, $v_header, $p_add_dir, $p_remove_dir, $p_remove_all_dir, $p_options)) != 1) - { - // ----- Return status - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // ----- Store the file infos - $p_result_list[$v_nb++] = $v_header; - } - // ----- Look for directory - if (is_dir($p_filename)) - { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "$p_filename is a directory"); - // ----- Look for path - if ($p_filename != ".") - $v_path = $p_filename."/"; - else - $v_path = ""; - // ----- Read the directory for files and sub-directories - $p_hdir = opendir($p_filename); - $p_hitem = readdir($p_hdir); // '.' directory - $p_hitem = readdir($p_hdir); // '..' directory - while ($p_hitem = readdir($p_hdir)) - { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Looking for $p_hitem in the directory"); - // ----- Look for a file - if (is_file($v_path.$p_hitem)) - { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Add the file '".$v_path.$p_hitem."'"); - // ----- Add the file - if (($v_result = $this->privAddFile($v_path.$p_hitem, $v_header, $p_add_dir, $p_remove_dir, $p_remove_all_dir, $p_options)) != 1) - { - // ----- Return status - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // ----- Store the file infos - $p_result_list[$v_nb++] = $v_header; - } - // ----- Recursive call to privAddFileList() - else - { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Add the directory '".$v_path.$p_hitem."'"); - // ----- Need an array as parameter - $p_temp_list[0] = $v_path.$p_hitem; - $v_result = $this->privAddFileList($p_temp_list, $p_result_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, $p_options); - // ----- Update the number of elements of the list - $v_nb = sizeof($p_result_list); - } - } - // ----- Free memory for the recursive loop - unset($p_temp_list); - unset($p_hdir); - unset($p_hitem); - } - } - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "After add, list have $v_nb elements"); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : privAddFile() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privAddFile($p_filename, &$p_header, $p_add_dir, $p_remove_dir, $p_remove_all_dir, &$p_options) - { - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privAddFile", "filename='$p_filename', add_dir='$p_add_dir', remove_dir='$p_remove_dir'"); - $v_result=1; - if ($p_filename == "") - { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid file list parameter (invalid or empty list)"); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - // ----- Calculate the stored filename - $v_stored_filename = $p_filename; - // ----- Look for all path to remove - if ($p_remove_all_dir) { - $v_stored_filename = basename($p_filename); - } - // ----- Look for partial path remove - else if ($p_remove_dir != "") - { - if (substr($p_remove_dir, -1) != '/') - $p_remove_dir .= "/"; - if ((substr($p_filename, 0, 2) == "./") || (substr($p_remove_dir, 0, 2) == "./")) - { - if ((substr($p_filename, 0, 2) == "./") && (substr($p_remove_dir, 0, 2) != "./")) - $p_remove_dir = "./".$p_remove_dir; - if ((substr($p_filename, 0, 2) != "./") && (substr($p_remove_dir, 0, 2) == "./")) - $p_remove_dir = substr($p_remove_dir, 2); - } - $v_compare = PclZipUtilPathInclusion($p_remove_dir, $p_filename); - if ($v_compare > 0) -// if (substr($p_filename, 0, strlen($p_remove_dir)) == $p_remove_dir) - { - if ($v_compare == 2) { - $v_stored_filename = ""; - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Path to remove is the current folder"); - } - else { - $v_stored_filename = substr($p_filename, strlen($p_remove_dir)); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Remove path '$p_remove_dir' in file '$p_filename' = '$v_stored_filename'"); - } - } - } - // ----- Look for path to add - if ($p_add_dir != "") - { - if (substr($p_add_dir, -1) == "/") - $v_stored_filename = $p_add_dir.$v_stored_filename; - else - $v_stored_filename = $p_add_dir."/".$v_stored_filename; - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Add path '$p_add_dir' in file '$p_filename' = '$v_stored_filename'"); - } - // ----- Filename (reduce the path of stored name) - $v_stored_filename = PclZipUtilPathReduction($v_stored_filename); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Filename (reduced) '$v_stored_filename', strlen ".strlen($v_stored_filename)); - /* filename length moved after call-back in release 1.3 - // ----- Check the path length - if (strlen($v_stored_filename) > 0xFF) - { - // ----- Error log - PclZip::privErrorLog(-5, "Stored file name is too long (max. 255) : '$v_stored_filename'"); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - */ - // ----- Set the file properties - clearstatcache(); - $p_header['version'] = 20; - $p_header['version_extracted'] = 10; - $p_header['flag'] = 0; - $p_header['compression'] = 0; - $p_header['mtime'] = filemtime($p_filename); - $p_header['crc'] = 0; - $p_header['compressed_size'] = 0; - $p_header['size'] = filesize($p_filename); - $p_header['filename_len'] = strlen($p_filename); - $p_header['extra_len'] = 0; - $p_header['comment_len'] = 0; - $p_header['disk'] = 0; - $p_header['internal'] = 0; - $p_header['external'] = (is_file($p_filename)?0xFE49FFE0:0x41FF0010); - $p_header['offset'] = 0; - $p_header['filename'] = $p_filename; - $p_header['stored_filename'] = $v_stored_filename; - $p_header['extra'] = ''; - $p_header['comment'] = ''; - $p_header['status'] = 'ok'; - $p_header['index'] = -1; - // ----- Look for pre-add callback - if (isset($p_options[PCLZIP_CB_PRE_ADD])) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "A pre-callback '".$p_options[PCLZIP_CB_PRE_ADD]."()') is defined for the extraction"); - // ----- Generate a local information - $v_local_header = array(); - $this->privConvertHeader2FileInfo($p_header, $v_local_header); - // ----- Call the callback - // Here I do not use call_user_func() because I need to send a reference to the - // header. - eval('$v_result = '.$p_options[PCLZIP_CB_PRE_ADD].'(PCLZIP_CB_PRE_ADD, $v_local_header);'); - if ($v_result == 0) { - // ----- Change the file status - $p_header['status'] = "skipped"; - $v_result = 1; - } - // ----- Update the informations - // Only some fields can be modified - if ($p_header['stored_filename'] != $v_local_header['stored_filename']) { - $p_header['stored_filename'] = PclZipUtilPathReduction($v_local_header['stored_filename']); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "New stored filename is '".$p_header['stored_filename']."'"); - } - } - // ----- Look for empty stored filename - if ($p_header['stored_filename'] == "") { - $p_header['status'] = "filtered"; - } - // ----- Check the path length - if (strlen($p_header['stored_filename']) > 0xFF) { - $p_header['status'] = 'filename_too_long'; - } - // ----- Look if no error, or file not skipped - if ($p_header['status'] == 'ok') { - // ----- Look for a file - if (is_file($p_filename)) - { - // ----- Open the source file - if (($v_file = @fopen($p_filename, "rb")) == 0) { - PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to open file '$p_filename' in binary read mode"); - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - if ($p_options[PCLZIP_OPT_NO_COMPRESSION]) { - // ----- Read the file content - $v_content_compressed = @fread($v_file, $p_header['size']); - // ----- Calculate the CRC - $p_header['crc'] = crc32($v_content_compressed); - } - else { - // ----- Read the file content - $v_content = @fread($v_file, $p_header['size']); - // ----- Calculate the CRC - $p_header['crc'] = crc32($v_content); - // ----- Compress the file - $v_content_compressed = gzdeflate($v_content); - } - // ----- Set header parameters - $p_header['compressed_size'] = strlen($v_content_compressed); - $p_header['compression'] = 8; - // ----- Call the header generation - if (($v_result = $this->privWriteFileHeader($p_header)) != 1) { - @fclose($v_file); - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // ----- Write the compressed content - $v_binary_data = pack('a'.$p_header['compressed_size'], $v_content_compressed); - @fwrite($this->zip_fd, $v_binary_data, $p_header['compressed_size']); - // ----- Close the file - @fclose($v_file); - } - // ----- Look for a directory - else - { - // ----- Set the file properties - $p_header['filename'] .= '/'; - $p_header['filename_len']++; - $p_header['size'] = 0; - $p_header['external'] = 0x41FF0010; // Value for a folder : to be checked - // ----- Call the header generation - if (($v_result = $this->privWriteFileHeader($p_header)) != 1) - { - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - } - } - // ----- Look for pre-add callback - if (isset($p_options[PCLZIP_CB_POST_ADD])) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "A post-callback '".$p_options[PCLZIP_CB_POST_ADD]."()') is defined for the extraction"); - // ----- Generate a local information - $v_local_header = array(); - $this->privConvertHeader2FileInfo($p_header, $v_local_header); - // ----- Call the callback - // Here I do not use call_user_func() because I need to send a reference to the - // header. - eval('$v_result = '.$p_options[PCLZIP_CB_POST_ADD].'(PCLZIP_CB_POST_ADD, $v_local_header);'); - if ($v_result == 0) { - // ----- Ignored - $v_result = 1; - } - // ----- Update the informations - // Nothing can be modified - } - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : privWriteFileHeader() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privWriteFileHeader(&$p_header) - { - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privWriteFileHeader", 'file="'.$p_header['filename'].'", stored as "'.$p_header['stored_filename'].'"'); - $v_result=1; - // TBC - //for(reset($p_header); $key = key($p_header); next($p_header)) { - // //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "header[$key] = ".$p_header[$key]); - //} - // ----- Store the offset position of the file - $p_header['offset'] = ftell($this->zip_fd); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, 'File offset of the header :'.$p_header['offset']); - // ----- Transform UNIX mtime to DOS format mdate/mtime - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Date : \''.date("d/m/y H:i:s", $p_header['mtime']).'\''); - $v_date = getdate($p_header['mtime']); - $v_mtime = ($v_date['hours']<<11) + ($v_date['minutes']<<5) + $v_date['seconds']/2; - $v_mdate = (($v_date['year']-1980)<<9) + ($v_date['mon']<<5) + $v_date['mday']; - // ----- Packed data - $v_binary_data = pack("VvvvvvVVVvv", 0x04034b50, $p_header['version'], $p_header['flag'], - $p_header['compression'], $v_mtime, $v_mdate, - $p_header['crc'], $p_header['compressed_size'], $p_header['size'], - strlen($p_header['stored_filename']), $p_header['extra_len']); - // ----- Write the first 148 bytes of the header in the archive - fputs($this->zip_fd, $v_binary_data, 30); - // ----- Write the variable fields - if (strlen($p_header['stored_filename']) != 0) - { - fputs($this->zip_fd, $p_header['stored_filename'], strlen($p_header['stored_filename'])); - } - if ($p_header['extra_len'] != 0) - { - fputs($this->zip_fd, $p_header['extra'], $p_header['extra_len']); - } - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : privWriteCentralFileHeader() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privWriteCentralFileHeader(&$p_header) - { - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privWriteCentralFileHeader", 'file="'.$p_header['filename'].'", stored as "'.$p_header['stored_filename'].'"'); - $v_result=1; - // TBC - //for(reset($p_header); $key = key($p_header); next($p_header)) { - // //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "header[$key] = ".$p_header[$key]); - //} - // ----- Transform UNIX mtime to DOS format mdate/mtime - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Date : \''.date("d/m/y H:i:s", $p_header['mtime']).'\''); - $v_date = getdate($p_header['mtime']); - $v_mtime = ($v_date['hours']<<11) + ($v_date['minutes']<<5) + $v_date['seconds']/2; - $v_mdate = (($v_date['year']-1980)<<9) + ($v_date['mon']<<5) + $v_date['mday']; - // ----- Packed data - $v_binary_data = pack("VvvvvvvVVVvvvvvVV", 0x02014b50, $p_header['version'], $p_header['version_extracted'], - $p_header['flag'], $p_header['compression'], $v_mtime, $v_mdate, $p_header['crc'], - $p_header['compressed_size'], $p_header['size'], - strlen($p_header['stored_filename']), $p_header['extra_len'], $p_header['comment_len'], - $p_header['disk'], $p_header['internal'], $p_header['external'], $p_header['offset']); - // ----- Write the 42 bytes of the header in the zip file - fputs($this->zip_fd, $v_binary_data, 46); - // ----- Write the variable fields - if (strlen($p_header['stored_filename']) != 0) - { - fputs($this->zip_fd, $p_header['stored_filename'], strlen($p_header['stored_filename'])); - } - if ($p_header['extra_len'] != 0) - { - fputs($this->zip_fd, $p_header['extra'], $p_header['extra_len']); - } - if ($p_header['comment_len'] != 0) - { - fputs($this->zip_fd, $p_header['comment'], $p_header['comment_len']); - } - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : privWriteCentralHeader() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privWriteCentralHeader($p_nb_entries, $p_size, $p_offset, $p_comment) - { - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privWriteCentralHeader", 'nb_entries='.$p_nb_entries.', size='.$p_size.', offset='.$p_offset.', comment="'.$p_comment.'"'); - $v_result=1; - // ----- Packed data - $v_binary_data = pack("VvvvvVVv", 0x06054b50, 0, 0, $p_nb_entries, $p_nb_entries, $p_size, $p_offset, strlen($p_comment)); - // ----- Write the 22 bytes of the header in the zip file - fputs($this->zip_fd, $v_binary_data, 22); - // ----- Write the variable fields - if (strlen($p_comment) != 0) - { - fputs($this->zip_fd, $p_comment, strlen($p_comment)); - } - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : privList() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privList(&$p_list) - { - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privList", "list"); - $v_result=1; - // ----- Open the zip file - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Open file in binary read mode"); - if (($this->zip_fd = @fopen($this->zipname, 'rb')) == 0) - { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in binary read mode'); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - // ----- Read the central directory informations - $v_central_dir = array(); - if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) - { - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // ----- Go to beginning of Central Dir - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Offset : ".$v_central_dir['offset']."'"); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Position in file : ".ftell($this->zip_fd)."'"); - @rewind($this->zip_fd); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Position in file : ".ftell($this->zip_fd)."'"); - if (@fseek($this->zip_fd, $v_central_dir['offset'])) - { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Position in file : ".ftell($this->zip_fd)."'"); - // ----- Read each entry - for ($i=0; $i<$v_central_dir['entries']; $i++) - { - // ----- Read the file header - if (($v_result = $this->privReadCentralFileHeader($v_header)) != 1) - { - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - $v_header['index'] = $i; - // ----- Get the only interesting attributes - $this->privConvertHeader2FileInfo($v_header, $p_list[$i]); - unset($v_header); - } - // ----- Close the zip file - $this->privCloseFd(); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : privConvertHeader2FileInfo() - // Description : - // This function takes the file informations from the central directory - // entries and extract the interesting parameters that will be given back. - // The resulting file infos are set in the array $p_info - // $p_info['filename'] : Filename with full path. Given by user (add), - // extracted in the filesystem (extract). - // $p_info['stored_filename'] : Stored filename in the archive. - // $p_info['size'] = Size of the file. - // $p_info['compressed_size'] = Compressed size of the file. - // $p_info['mtime'] = Last modification date of the file. - // $p_info['comment'] = Comment associated with the file. - // $p_info['folder'] = true/false : indicates if the entry is a folder or not. - // $p_info['status'] = status of the action on the file. - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privConvertHeader2FileInfo($p_header, &$p_info) - { - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privConvertHeader2FileInfo", "Filename='".$p_header['filename']."'"); - $v_result=1; - // ----- Get the interesting attributes - $p_info['filename'] = $p_header['filename']; - $p_info['stored_filename'] = $p_header['stored_filename']; - $p_info['size'] = $p_header['size']; - $p_info['compressed_size'] = $p_header['compressed_size']; - $p_info['mtime'] = $p_header['mtime']; - $p_info['comment'] = $p_header['comment']; - $p_info['folder'] = (($p_header['external']&0x00000010)==0x00000010); - $p_info['index'] = $p_header['index']; - $p_info['status'] = $p_header['status']; - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : privExtractByRule() - // Description : - // Extract a file or directory depending of rules (by index, by name, ...) - // Parameters : - // $p_file_list : An array where will be placed the properties of each - // extracted file - // $p_path : Path to add while writing the extracted files - // $p_remove_path : Path to remove (from the file memorized path) while writing the - // extracted files. If the path does not match the file path, - // the file is extracted with its memorized path. - // $p_remove_path does not apply to 'list' mode. - // $p_path and $p_remove_path are commulative. - // Return Values : - // 1 on success,0 or less on error (see error code list) - // -------------------------------------------------------------------------------- - function privExtractByRule(&$p_file_list, $p_path, $p_remove_path, $p_remove_all_path, &$p_options) - { - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privExtractByRule", "path='$p_path', remove_path='$p_remove_path', remove_all_path='".($p_remove_all_path?'true':'false')."'"); - $v_result=1; - // ----- Check the path - if (($p_path == "") || ((substr($p_path, 0, 1) != "/") && (substr($p_path, 0, 3) != "../") && (substr($p_path,1,2)!=":/"))) - $p_path = "./".$p_path; - // ----- Reduce the path last (and duplicated) '/' - if (($p_path != "./") && ($p_path != "/")) - { - // ----- Look for the path end '/' - while (substr($p_path, -1) == "/") - { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Destination path [$p_path] ends by '/'"); - $p_path = substr($p_path, 0, strlen($p_path)-1); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Modified to [$p_path]"); - } - } - // ----- Look for path to remove format (should end by /) - if (($p_remove_path != "") && (substr($p_remove_path, -1) != '/')) - { - $p_remove_path .= '/'; - } - $p_remove_path_size = strlen($p_remove_path); - // ----- Open the zip file - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Open file in binary read mode"); - if (($v_result = $this->privOpenFd('rb')) != 1) - { - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // ----- Read the central directory informations - $v_central_dir = array(); - if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) - { - // ----- Close the zip file - $this->privCloseFd(); - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // ----- Start at beginning of Central Dir - $v_pos_entry = $v_central_dir['offset']; - // ----- Read each entry - $j_start = 0; - for ($i=0, $v_nb_extracted=0; $i<$v_central_dir['entries']; $i++) - { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Read next file header entry : '$i'"); - // ----- Read next Central dir entry - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Position before rewind : ".ftell($this->zip_fd)."'"); - @rewind($this->zip_fd); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Position after rewind : ".ftell($this->zip_fd)."'"); - if (@fseek($this->zip_fd, $v_pos_entry)) - { - // ----- Close the zip file - $this->privCloseFd(); - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Position after fseek : ".ftell($this->zip_fd)."'"); - // ----- Read the file header - $v_header = array(); - if (($v_result = $this->privReadCentralFileHeader($v_header)) != 1) - { - // ----- Close the zip file - $this->privCloseFd(); - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // ----- Store the index - $v_header['index'] = $i; - // ----- Store the file position - $v_pos_entry = ftell($this->zip_fd); - // ----- Look for the specific extract rules - $v_extract = false; - // ----- Look for extract by name rule - if ( (isset($p_options[PCLZIP_OPT_BY_NAME])) - && ($p_options[PCLZIP_OPT_BY_NAME] != 0)) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Extract with rule 'ByName'"); - // ----- Look if the filename is in the list - for ($j=0; ($j strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) - && (substr($v_header['stored_filename'], 0, strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) == $p_options[PCLZIP_OPT_BY_NAME][$j])) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "The directory is in the file path"); - $v_extract = true; - } - } - // ----- Look for a filename - elseif ($v_header['stored_filename'] == $p_options[PCLZIP_OPT_BY_NAME][$j]) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "The file is the right one."); - $v_extract = true; - } - } - } - // ----- Look for extract by ereg rule - else if ( (isset($p_options[PCLZIP_OPT_BY_EREG])) - && ($p_options[PCLZIP_OPT_BY_EREG] != "")) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Extract by ereg '".$p_options[PCLZIP_OPT_BY_EREG]."'"); - if (ereg($p_options[PCLZIP_OPT_BY_EREG], $v_header['stored_filename'])) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Filename match the regular expression"); - $v_extract = true; - } - } - // ----- Look for extract by preg rule - else if ( (isset($p_options[PCLZIP_OPT_BY_PREG])) - && ($p_options[PCLZIP_OPT_BY_PREG] != "")) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Extract with rule 'ByEreg'"); - if (preg_match($p_options[PCLZIP_OPT_BY_PREG], $v_header['stored_filename'])) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Filename match the regular expression"); - $v_extract = true; - } - } - // ----- Look for extract by index rule - else if ( (isset($p_options[PCLZIP_OPT_BY_INDEX])) - && ($p_options[PCLZIP_OPT_BY_INDEX] != 0)) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Extract with rule 'ByIndex'"); - // ----- Look if the index is in the list - for ($j=$j_start; ($j=$p_options[PCLZIP_OPT_BY_INDEX][$j]['start']) && ($i<=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end'])) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Found as part of an index range"); - $v_extract = true; - } - if ($i>=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end']) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Do not look this index range for next loop"); - $j_start = $j+1; - } - if ($p_options[PCLZIP_OPT_BY_INDEX][$j]['start']>$i) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Index range is greater than index, stop loop"); - break; - } - } - } - // ----- Look for no rule, which means extract all the archive - else { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Extract with no rule (extract all)"); - $v_extract = true; - } - // ----- Look for real extraction - if ($v_extract) - { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Extracting file '".$v_header['filename']."', index '$i'"); - // ----- Go to the file position - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position before rewind : ".ftell($this->zip_fd)."'"); - @rewind($this->zip_fd); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position after rewind : ".ftell($this->zip_fd)."'"); - if (@fseek($this->zip_fd, $v_header['offset'])) - { - // ----- Close the zip file - $this->privCloseFd(); - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position after fseek : ".ftell($this->zip_fd)."'"); - // ----- Look for extraction as string - if ($p_options[PCLZIP_OPT_EXTRACT_AS_STRING]) { - // ----- Extracting the file - if (($v_result = $this->privExtractFileAsString($v_header, $v_string)) != 1) - { - // ----- Close the zip file - $this->privCloseFd(); - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // ----- Get the only interesting attributes - if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted])) != 1) - { - // ----- Close the zip file - $this->privCloseFd(); - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // ----- Set the file content - $p_file_list[$v_nb_extracted]['content'] = $v_string; - // ----- Next extracted file - $v_nb_extracted++; - } - else { - // ----- Extracting the file - if (($v_result = $this->privExtractFile($v_header, $p_path, $p_remove_path, $p_remove_all_path, $p_options)) != 1) - { - // ----- Close the zip file - $this->privCloseFd(); - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // ----- Get the only interesting attributes - if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted++])) != 1) - { - // ----- Close the zip file - $this->privCloseFd(); - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - } - } - } - // ----- Close the zip file - $this->privCloseFd(); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : privExtractFile() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privExtractFile(&$p_entry, $p_path, $p_remove_path, $p_remove_all_path, &$p_options) - { - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, 'PclZip::privExtractFile', "path='$p_path', remove_path='$p_remove_path', remove_all_path='".($p_remove_all_path?'true':'false')."'"); - $v_result=1; - // ----- Read the file header - if (($v_result = $this->privReadFileHeader($v_header)) != 1) - { - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Found file '".$v_header['filename']."', size '".$v_header['size']."'"); - // ----- Check that the file header is coherent with $p_entry info - // TBC - // ----- Look for all path to remove - if ($p_remove_all_path == true) { - // ----- Get the basename of the path - $p_entry['filename'] = basename($p_entry['filename']); - } - // ----- Look for path to remove - else if ($p_remove_path != "") - { - //if (strcmp($p_remove_path, $p_entry['filename'])==0) - if (PclZipUtilPathInclusion($p_remove_path, $p_entry['filename']) == 2) - { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "The folder is the same as the removed path '".$p_entry['filename']."'"); - // ----- Change the file status - $p_entry['status'] = "filtered"; - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - $p_remove_path_size = strlen($p_remove_path); - if (substr($p_entry['filename'], 0, $p_remove_path_size) == $p_remove_path) - { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Found path '$p_remove_path' to remove in file '".$p_entry['filename']."'"); - // ----- Remove the path - $p_entry['filename'] = substr($p_entry['filename'], $p_remove_path_size); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Resulting file is '".$p_entry['filename']."'"); - } - } - // ----- Add the path - if ($p_path != '') - { - $p_entry['filename'] = $p_path."/".$p_entry['filename']; - } - // ----- Look for pre-extract callback - if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "A pre-callback '".$p_options[PCLZIP_CB_PRE_EXTRACT]."()') is defined for the extraction"); - // ----- Generate a local information - $v_local_header = array(); - $this->privConvertHeader2FileInfo($p_entry, $v_local_header); - // ----- Call the callback - // Here I do not use call_user_func() because I need to send a reference to the - // header. - eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'(PCLZIP_CB_PRE_EXTRACT, $v_local_header);'); - if ($v_result == 0) { - // ----- Change the file status - $p_entry['status'] = "skipped"; - $v_result = 1; - } - // ----- Update the informations - // Only some fields can be modified - $p_entry['filename'] = $v_local_header['filename']; - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "New filename is '".$p_entry['filename']."'"); - } - // ----- Trace - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Extracting file (with path) '".$p_entry['filename']."', size '$v_header[size]'"); - // ----- Look if extraction should be done - if ($p_entry['status'] == 'ok') { - // ----- Look for specific actions while the file exist - if (file_exists($p_entry['filename'])) - { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "File '".$p_entry['filename']."' already exists"); - // ----- Look if file is a directory - if (is_dir($p_entry['filename'])) - { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Existing file '".$p_entry['filename']."' is a directory"); - // ----- Change the file status - $p_entry['status'] = "already_a_directory"; - // ----- Return - ////--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - //return $v_result; - } - // ----- Look if file is write protected - else if (!is_writeable($p_entry['filename'])) - { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Existing file '".$p_entry['filename']."' is write protected"); - // ----- Change the file status - $p_entry['status'] = "write_protected"; - // ----- Return - ////--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - //return $v_result; - } - // ----- Look if the extracted file is older - else if (filemtime($p_entry['filename']) > $p_entry['mtime']) - { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Existing file '".$p_entry['filename']."' is newer (".date("l dS of F Y h:i:s A", filemtime($p_entry['filename'])).") than the extracted file (".date("l dS of F Y h:i:s A", $p_entry['mtime']).")"); - // ----- Change the file status - $p_entry['status'] = "newer_exist"; - // ----- Return - ////--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - //return $v_result; - } - } - // ----- Check the directory availability and create it if necessary - else { - if ((($p_entry['external']&0x00000010)==0x00000010) || (substr($p_entry['filename'], -1) == '/')) - $v_dir_to_check = $p_entry['filename']; - else if (!strstr($p_entry['filename'], "/")) - $v_dir_to_check = ""; - else - $v_dir_to_check = dirname($p_entry['filename']); - if (($v_result = $this->privDirCheck($v_dir_to_check, (($p_entry['external']&0x00000010)==0x00000010))) != 1) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Unable to create path for '".$p_entry['filename']."'"); - // ----- Change the file status - $p_entry['status'] = "path_creation_fail"; - // ----- Return - ////--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - //return $v_result; - $v_result = 1; - } - } - } - // ----- Look if extraction should be done - if ($p_entry['status'] == 'ok') { - // ----- Do the extraction (if not a folder) - if (!(($p_entry['external']&0x00000010)==0x00000010)) - { - // ----- Look for not compressed file - if ($p_entry['compressed_size'] == $p_entry['size']) - { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Extracting an un-compressed file"); - // ----- Opening destination file - if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) - { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Error while opening '".$p_entry['filename']."' in write binary mode"); - // ----- Change the file status - $p_entry['status'] = "write_error"; - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Reading '".$p_entry['size']."' bytes"); - // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks - $v_size = $p_entry['compressed_size']; - while ($v_size != 0) - { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Read $v_read_size bytes"); - $v_buffer = fread($this->zip_fd, $v_read_size); - $v_binary_data = pack('a'.$v_read_size, $v_buffer); - @fwrite($v_dest_file, $v_binary_data, $v_read_size); - $v_size -= $v_read_size; - } - // ----- Closing the destination file - fclose($v_dest_file); - // ----- Change the file mtime - touch($p_entry['filename'], $p_entry['mtime']); - } - else - { - // ----- Trace - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Extracting a compressed file"); - // ----- Opening destination file - if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Error while opening '".$p_entry['filename']."' in write binary mode"); - // ----- Change the file status - $p_entry['status'] = "write_error"; - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Reading '".$p_entry['size']."' bytes"); - // ----- Read the compressed file in a buffer (one shot) - $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']); - // ----- Decompress the file - $v_file_content = gzinflate($v_buffer); - unset($v_buffer); - // ----- Write the uncompressed data - @fwrite($v_dest_file, $v_file_content, $p_entry['size']); - unset($v_file_content); - // ----- Closing the destination file - @fclose($v_dest_file); - // ----- Change the file mtime - touch($p_entry['filename'], $p_entry['mtime']); - } - // ----- Look for chmod option - if (isset($p_options[PCLZIP_OPT_SET_CHMOD])) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "chmod option activated '".$p_options[PCLZIP_OPT_SET_CHMOD]."'"); - // ----- Change the mode of the file - chmod($p_entry['filename'], $p_options[PCLZIP_OPT_SET_CHMOD]); - } - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Extraction done"); - } - } - // ----- Look for post-extract callback - if (isset($p_options[PCLZIP_CB_POST_EXTRACT])) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "A post-callback '".$p_options[PCLZIP_CB_POST_EXTRACT]."()') is defined for the extraction"); - // ----- Generate a local information - $v_local_header = array(); - $this->privConvertHeader2FileInfo($p_entry, $v_local_header); - // ----- Call the callback - // Here I do not use call_user_func() because I need to send a reference to the - // header. - eval('$v_result = '.$p_options[PCLZIP_CB_POST_EXTRACT].'(PCLZIP_CB_POST_EXTRACT, $v_local_header);'); - } - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : privExtractFileAsString() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privExtractFileAsString(&$p_entry, &$p_string) - { - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, 'PclZip::privExtractFileAsString', "p_entry['filename']='".$p_entry['filename']."'"); - $v_result=1; - // ----- Read the file header - $v_header = array(); - if (($v_result = $this->privReadFileHeader($v_header)) != 1) - { - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Found file '".$v_header['filename']."', size '".$v_header['size']."'"); - // ----- Check that the file header is coherent with $p_entry info - // TBC - // ----- Trace - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Extracting file in string (with path) '".$p_entry['filename']."', size '$v_header[size]'"); - // ----- Do the extraction (if not a folder) - if (!(($p_entry['external']&0x00000010)==0x00000010)) - { - // ----- Look for not compressed file - if ($p_entry['compressed_size'] == $p_entry['size']) - { - // ----- Trace - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Extracting an un-compressed file"); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Reading '".$p_entry['size']."' bytes"); - // ----- Reading the file - $p_string = fread($this->zip_fd, $p_entry['compressed_size']); - } - else - { - // ----- Trace - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Extracting a compressed file"); - // ----- Reading the file - $v_data = fread($this->zip_fd, $p_entry['compressed_size']); - // ----- Decompress the file - $p_string = gzinflate($v_data); - } - // ----- Trace - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Extraction done"); - } - else { - // TBC : error : can not extract a folder in a string - } - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : privReadFileHeader() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privReadFileHeader(&$p_header) - { - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privReadFileHeader", ""); - $v_result=1; - // ----- Read the 4 bytes signature - $v_binary_data = @fread($this->zip_fd, 4); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Binary data is : '".sprintf("%08x", $v_binary_data)."'"); - $v_data = unpack('Vid', $v_binary_data); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Binary signature is : '".sprintf("0x%08x", $v_data['id'])."'"); - // ----- Check signature - if ($v_data['id'] != 0x04034b50) - { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Invalid File header"); - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Invalid archive structure'); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - // ----- Read the first 42 bytes of the header - $v_binary_data = fread($this->zip_fd, 26); - // ----- Look for invalid block size - if (strlen($v_binary_data) != 26) - { - $p_header['filename'] = ""; - $p_header['status'] = "invalid_header"; - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Invalid block size : ".strlen($v_binary_data)); - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid block size : ".strlen($v_binary_data)); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - // ----- Extract the values - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Header : '".$v_binary_data."'"); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Header (Hex) : '".bin2hex($v_binary_data)."'"); - $v_data = unpack('vversion/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len', $v_binary_data); - // ----- Get filename - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "File name length : ".$v_data['filename_len']); - $p_header['filename'] = fread($this->zip_fd, $v_data['filename_len']); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Filename : \''.$p_header['filename'].'\''); - // ----- Get extra_fields - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Extra field length : ".$v_data['extra_len']); - if ($v_data['extra_len'] != 0) { - $p_header['extra'] = fread($this->zip_fd, $v_data['extra_len']); - } - else { - $p_header['extra'] = ''; - } - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Extra field : \''.bin2hex($p_header['extra']).'\''); - // ----- Extract properties - $p_header['compression'] = $v_data['compression']; - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Compression method : \''.bin2hex($p_header['compression']).'\''); - $p_header['size'] = $v_data['size']; - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Size : \''.$p_header['size'].'\''); - $p_header['compressed_size'] = $v_data['compressed_size']; - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Compressed Size : \''.$p_header['compressed_size'].'\''); - $p_header['crc'] = $v_data['crc']; - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'CRC : \''.$p_header['crc'].'\''); - $p_header['flag'] = $v_data['flag']; - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Flag : \''.$p_header['flag'].'\''); - // ----- Recuperate date in UNIX format - $p_header['mdate'] = $v_data['mdate']; - $p_header['mtime'] = $v_data['mtime']; - if ($p_header['mdate'] && $p_header['mtime']) - { - // ----- Extract time - $v_hour = ($p_header['mtime'] & 0xF800) >> 11; - $v_minute = ($p_header['mtime'] & 0x07E0) >> 5; - $v_seconde = ($p_header['mtime'] & 0x001F)*2; - // ----- Extract date - $v_year = (($p_header['mdate'] & 0xFE00) >> 9) + 1980; - $v_month = ($p_header['mdate'] & 0x01E0) >> 5; - $v_day = $p_header['mdate'] & 0x001F; - // ----- Get UNIX date format - $p_header['mtime'] = mktime($v_hour, $v_minute, $v_seconde, $v_month, $v_day, $v_year); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Date : \''.date("d/m/y H:i:s", $p_header['mtime']).'\''); - } - else - { - $p_header['mtime'] = time(); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Date is actual : \''.date("d/m/y H:i:s", $p_header['mtime']).'\''); - } - // ----- Other informations - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Compression type : ".$v_data['compression']); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Version : ".$v_data['version']); - // TBC - //for(reset($v_data); $key = key($v_data); next($v_data)) { - // //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Attribut[$key] = ".$v_data[$key]); - //} - // ----- Set the stored filename - $p_header['stored_filename'] = $p_header['filename']; - // ----- Set the status field - $p_header['status'] = "ok"; - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : privReadCentralFileHeader() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privReadCentralFileHeader(&$p_header) - { - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privReadCentralFileHeader", ""); - $v_result=1; - // ----- Read the 4 bytes signature - $v_binary_data = @fread($this->zip_fd, 4); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Binary data is : '".sprintf("%08x", $v_binary_data)."'"); - $v_data = unpack('Vid', $v_binary_data); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Binary signature is : '".sprintf("0x%08x", $v_data['id'])."'"); - // ----- Check signature - if ($v_data['id'] != 0x02014b50) - { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Invalid Central Dir File signature"); - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Invalid archive structure'); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - // ----- Read the first 42 bytes of the header - $v_binary_data = fread($this->zip_fd, 42); - // ----- Look for invalid block size - if (strlen($v_binary_data) != 42) - { - $p_header['filename'] = ""; - $p_header['status'] = "invalid_header"; - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Invalid block size : ".strlen($v_binary_data)); - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid block size : ".strlen($v_binary_data)); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - // ----- Extract the values - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Header : '".$v_binary_data."'"); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Header (Hex) : '".bin2hex($v_binary_data)."'"); - $p_header = unpack('vversion/vversion_extracted/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len/vcomment_len/vdisk/vinternal/Vexternal/Voffset', $v_binary_data); - // ----- Get filename - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "File name length : ".$p_header['filename_len']); - if ($p_header['filename_len'] != 0) - $p_header['filename'] = fread($this->zip_fd, $p_header['filename_len']); - else - $p_header['filename'] = ''; - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Filename : \''.$p_header['filename'].'\''); - // ----- Get extra - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Extra length : ".$p_header['extra_len']); - if ($p_header['extra_len'] != 0) - $p_header['extra'] = fread($this->zip_fd, $p_header['extra_len']); - else - $p_header['extra'] = ''; - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Extra : \''.$p_header['extra'].'\''); - // ----- Get comment - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Comment length : ".$p_header['comment_len']); - if ($p_header['comment_len'] != 0) - $p_header['comment'] = fread($this->zip_fd, $p_header['comment_len']); - else - $p_header['comment'] = ''; - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Comment : \''.$p_header['comment'].'\''); - // ----- Extract properties - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Version : \''.($p_header['version']/10).'.'.($p_header['version']%10).'\''); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Version need to extract : \''.($p_header['version_extracted']/10).'.'.($p_header['version_extracted']%10).'\''); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Size : \''.$p_header['size'].'\''); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Compressed Size : \''.$p_header['compressed_size'].'\''); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'CRC : \''.$p_header['crc'].'\''); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Flag : \''.$p_header['flag'].'\''); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Offset : \''.$p_header['offset'].'\''); - // ----- Recuperate date in UNIX format - if ($p_header['mdate'] && $p_header['mtime']) - { - // ----- Extract time - $v_hour = ($p_header['mtime'] & 0xF800) >> 11; - $v_minute = ($p_header['mtime'] & 0x07E0) >> 5; - $v_seconde = ($p_header['mtime'] & 0x001F)*2; - // ----- Extract date - $v_year = (($p_header['mdate'] & 0xFE00) >> 9) + 1980; - $v_month = ($p_header['mdate'] & 0x01E0) >> 5; - $v_day = $p_header['mdate'] & 0x001F; - // ----- Get UNIX date format - $p_header['mtime'] = mktime($v_hour, $v_minute, $v_seconde, $v_month, $v_day, $v_year); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Date : \''.date("d/m/y H:i:s", $p_header['mtime']).'\''); - } - else - { - $p_header['mtime'] = time(); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Date is actual : \''.date("d/m/y H:i:s", $p_header['mtime']).'\''); - } - // ----- Set the stored filename - $p_header['stored_filename'] = $p_header['filename']; - // ----- Set default status to ok - $p_header['status'] = 'ok'; - // ----- Look if it is a directory - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Internal (Hex) : '".sprintf("Ox%04X", $p_header['internal'])."'"); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "External (Hex) : '".sprintf("Ox%04X", $p_header['external'])."' (".(($p_header['external']&0x00000010)==0x00000010?'is a folder':'is a file').')'); - if (substr($p_header['filename'], -1) == '/') - { - $p_header['external'] = 0x41FF0010; - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Force folder external : \''.$p_header['external'].'\''); - } - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Header of filename : \''.$p_header['filename'].'\''); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : privReadEndCentralDir() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privReadEndCentralDir(&$p_central_dir) - { - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privReadEndCentralDir", ""); - $v_result=1; - // ----- Go to the end of the zip file - $v_size = filesize($this->zipname); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Size of the file :$v_size"); - @fseek($this->zip_fd, $v_size); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Position at end of zip file : \''.ftell($this->zip_fd).'\''); - if (@ftell($this->zip_fd) != $v_size) - { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to go to the end of the archive \''.$this->zipname.'\''); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - // ----- First try : look if this is an archive with no commentaries (most of the time) - // in this case the end of central dir is at 22 bytes of the file end - $v_found = 0; - if ($v_size > 26) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Look for central dir with no comment'); - @fseek($this->zip_fd, $v_size-22); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Position after min central position : \''.ftell($this->zip_fd).'\''); - if (($v_pos = @ftell($this->zip_fd)) != ($v_size-22)) - { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to seek back to the middle of the archive \''.$this->zipname.'\''); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - // ----- Read for bytes - $v_binary_data = @fread($this->zip_fd, 4); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Binary data is : '".sprintf("%08x", $v_binary_data)."'"); - $v_data = unpack('Vid', $v_binary_data); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Binary signature is : '".sprintf("0x%08x", $v_data['id'])."'"); - // ----- Check signature - if ($v_data['id'] == 0x06054b50) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Found central dir at the default position."); - $v_found = 1; - } - $v_pos = ftell($this->zip_fd); - } - // ----- Go back to the maximum possible size of the Central Dir End Record - if (!$v_found) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Start extended search of end central dir'); - $v_maximum_size = 65557; // 0xFFFF + 22; - if ($v_maximum_size > $v_size) - $v_maximum_size = $v_size; - @fseek($this->zip_fd, $v_size-$v_maximum_size); - if (@ftell($this->zip_fd) != ($v_size-$v_maximum_size)) - { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to seek back to the middle of the archive \''.$this->zipname.'\''); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Position after max central position : \''.ftell($this->zip_fd).'\''); - // ----- Read byte per byte in order to find the signature - $v_pos = ftell($this->zip_fd); - $v_bytes = 0x00000000; - while ($v_pos < $v_size) - { - // ----- Read a byte - $v_byte = @fread($this->zip_fd, 1); - // ----- Add the byte - $v_bytes = ($v_bytes << 8) | Ord($v_byte); - // ----- Compare the bytes - if ($v_bytes == 0x504b0506) - { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, 'Found End Central Dir signature at position : \''.ftell($this->zip_fd).'\''); - $v_pos++; - break; - } - $v_pos++; - } - // ----- Look if not found end of central dir - if ($v_pos == $v_size) - { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Unable to find End of Central Dir Record signature"); - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Unable to find End of Central Dir Record signature"); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - } - // ----- Read the first 18 bytes of the header - $v_binary_data = fread($this->zip_fd, 18); - // ----- Look for invalid block size - if (strlen($v_binary_data) != 18) - { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Invalid End of Central Dir Record size : ".strlen($v_binary_data)); - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid End of Central Dir Record size : ".strlen($v_binary_data)); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - // ----- Extract the values - ////--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Central Dir Record : '".$v_binary_data."'"); - ////--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Central Dir Record (Hex) : '".bin2hex($v_binary_data)."'"); - $v_data = unpack('vdisk/vdisk_start/vdisk_entries/ventries/Vsize/Voffset/vcomment_size', $v_binary_data); - // ----- Check the global size - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Comment length : ".$v_data['comment_size']); - if (($v_pos + $v_data['comment_size'] + 18) != $v_size) - { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Fail to find the right signature"); - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Fail to find the right signature"); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - // ----- Get comment - if ($v_data['comment_size'] != 0) - $p_central_dir['comment'] = fread($this->zip_fd, $v_data['comment_size']); - else - $p_central_dir['comment'] = ''; - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Comment : \''.$p_central_dir['comment'].'\''); - $p_central_dir['entries'] = $v_data['entries']; - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Nb of entries : \''.$p_central_dir['entries'].'\''); - $p_central_dir['disk_entries'] = $v_data['disk_entries']; - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Nb of entries for this disk : \''.$p_central_dir['disk_entries'].'\''); - $p_central_dir['offset'] = $v_data['offset']; - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Offset of Central Dir : \''.$p_central_dir['offset'].'\''); - $p_central_dir['size'] = $v_data['size']; - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Size of Central Dir : \''.$p_central_dir['size'].'\''); - $p_central_dir['disk'] = $v_data['disk']; - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Disk number : \''.$p_central_dir['disk'].'\''); - $p_central_dir['disk_start'] = $v_data['disk_start']; - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, 'Start disk number : \''.$p_central_dir['disk_start'].'\''); - // TBC - //for(reset($p_central_dir); $key = key($p_central_dir); next($p_central_dir)) { - // //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "central_dir[$key] = ".$p_central_dir[$key]); - //} - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : privDeleteByRule() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privDeleteByRule(&$p_result_list, &$p_options) - { - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privDeleteByRule", ""); - $v_result=1; - $v_list_detail = array(); - // ----- Open the zip file - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Open file in binary read mode"); - if (($v_result=$this->privOpenFd('rb')) != 1) - { - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // ----- Read the central directory informations - $v_central_dir = array(); - if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) - { - $this->privCloseFd(); - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // ----- Go to beginning of File - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position in file : ".ftell($this->zip_fd)."'"); - @rewind($this->zip_fd); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position in file : ".ftell($this->zip_fd)."'"); - // ----- Scan all the files - // ----- Start at beginning of Central Dir - $v_pos_entry = $v_central_dir['offset']; - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position before rewind : ".ftell($this->zip_fd)."'"); - @rewind($this->zip_fd); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position after rewind : ".ftell($this->zip_fd)."'"); - if (@fseek($this->zip_fd, $v_pos_entry)) - { - // ----- Close the zip file - $this->privCloseFd(); - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position after fseek : ".ftell($this->zip_fd)."'"); - // ----- Read each entry - $v_header_list = array(); - $j_start = 0; - for ($i=0, $v_nb_extracted=0; $i<$v_central_dir['entries']; $i++) - { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Read next file header entry (index '$i')"); - // ----- Read the file header - $v_header_list[$v_nb_extracted] = array(); - if (($v_result = $this->privReadCentralFileHeader($v_header_list[$v_nb_extracted])) != 1) - { - // ----- Close the zip file - $this->privCloseFd(); - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Filename (index '$i') : '".$v_header_list[$v_nb_extracted]['stored_filename']."'"); - // ----- Store the index - $v_header_list[$v_nb_extracted]['index'] = $i; - // ----- Look for the specific extract rules - $v_found = false; - // ----- Look for extract by name rule - if ( (isset($p_options[PCLZIP_OPT_BY_NAME])) - && ($p_options[PCLZIP_OPT_BY_NAME] != 0)) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Extract with rule 'ByName'"); - // ----- Look if the filename is in the list - for ($j=0; ($j strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) - && (substr($v_header_list[$v_nb_extracted]['stored_filename'], 0, strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) == $p_options[PCLZIP_OPT_BY_NAME][$j])) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "The directory is in the file path"); - $v_found = true; - } - elseif ( (($v_header_list[$v_nb_extracted]['external']&0x00000010)==0x00000010) /* Indicates a folder */ - && ($v_header_list[$v_nb_extracted]['stored_filename'].'/' == $p_options[PCLZIP_OPT_BY_NAME][$j])) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "The entry is the searched directory"); - $v_found = true; - } - } - // ----- Look for a filename - elseif ($v_header_list[$v_nb_extracted]['stored_filename'] == $p_options[PCLZIP_OPT_BY_NAME][$j]) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "The file is the right one."); - $v_found = true; - } - } - } - // ----- Look for extract by ereg rule - else if ( (isset($p_options[PCLZIP_OPT_BY_EREG])) - && ($p_options[PCLZIP_OPT_BY_EREG] != "")) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Extract by ereg '".$p_options[PCLZIP_OPT_BY_EREG]."'"); - if (ereg($p_options[PCLZIP_OPT_BY_EREG], $v_header_list[$v_nb_extracted]['stored_filename'])) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Filename match the regular expression"); - $v_found = true; - } - } - // ----- Look for extract by preg rule - else if ( (isset($p_options[PCLZIP_OPT_BY_PREG])) - && ($p_options[PCLZIP_OPT_BY_PREG] != "")) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Extract with rule 'ByEreg'"); - if (preg_match($p_options[PCLZIP_OPT_BY_PREG], $v_header_list[$v_nb_extracted]['stored_filename'])) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Filename match the regular expression"); - $v_found = true; - } - } - // ----- Look for extract by index rule - else if ( (isset($p_options[PCLZIP_OPT_BY_INDEX])) - && ($p_options[PCLZIP_OPT_BY_INDEX] != 0)) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Extract with rule 'ByIndex'"); - // ----- Look if the index is in the list - for ($j=$j_start; ($j=$p_options[PCLZIP_OPT_BY_INDEX][$j]['start']) && ($i<=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end'])) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Found as part of an index range"); - $v_found = true; - } - if ($i>=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end']) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Do not look this index range for next loop"); - $j_start = $j+1; - } - if ($p_options[PCLZIP_OPT_BY_INDEX][$j]['start']>$i) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Index range is greater than index, stop loop"); - break; - } - } - } - // ----- Look for deletion - if ($v_found) - { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "File '".$v_header_list[$v_nb_extracted]['stored_filename']."', index '$i' need to be deleted"); - unset($v_header_list[$v_nb_extracted]); - } - else - { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "File '".$v_header_list[$v_nb_extracted]['stored_filename']."', index '$i' will not be deleted"); - $v_nb_extracted++; - } - } - // ----- Look if something need to be deleted - if ($v_nb_extracted > 0) { - // ----- Creates a temporay file - $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp'; - // ----- Creates a temporary zip archive - $v_temp_zip = new PclZip($v_zip_temp_name); - // ----- Open the temporary zip file in write mode - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Open file in binary write mode"); - if (($v_result = $v_temp_zip->privOpenFd('wb')) != 1) { - $this->privCloseFd(); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // ----- Look which file need to be kept - for ($i=0; $izip_fd)."'"); - @rewind($this->zip_fd); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position after rewind : ".ftell($this->zip_fd)."'"); - if (@fseek($this->zip_fd, $v_header_list[$i]['offset'])) { - // ----- Close the zip file - $this->privCloseFd(); - $v_temp_zip->privCloseFd(); - @unlink($v_zip_temp_name); - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position after fseek : ".ftell($this->zip_fd)."'"); - // ----- Read the file header - if (($v_result = $this->privReadFileHeader($v_header_list[$i])) != 1) { - // ----- Close the zip file - $this->privCloseFd(); - $v_temp_zip->privCloseFd(); - @unlink($v_zip_temp_name); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // ----- Write the file header - if (($v_result = $v_temp_zip->privWriteFileHeader($v_header_list[$i])) != 1) { - // ----- Close the zip file - $this->privCloseFd(); - $v_temp_zip->privCloseFd(); - @unlink($v_zip_temp_name); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Offset for this file is '".$v_header_list[$i]['offset']."'"); - // ----- Read/write the data block - if (($v_result = PclZipUtilCopyBlock($this->zip_fd, $v_temp_zip->zip_fd, $v_header_list[$i]['compressed_size'])) != 1) { - // ----- Close the zip file - $this->privCloseFd(); - $v_temp_zip->privCloseFd(); - @unlink($v_zip_temp_name); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - } - // ----- Store the offset of the central dir - $v_offset = @ftell($v_temp_zip->zip_fd); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "New offset of central dir : $v_offset"); - // ----- Re-Create the Central Dir files header - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Creates the new central directory"); - for ($i=0; $iprivWriteCentralFileHeader($v_header_list[$i])) != 1) { - $v_temp_zip->privCloseFd(); - $this->privCloseFd(); - @unlink($v_zip_temp_name); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // ----- Transform the header to a 'usable' info - $v_temp_zip->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]); - } - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Creates the central directory footer"); - // ----- Zip file comment - $v_comment = ''; - // ----- Calculate the size of the central header - $v_size = @ftell($v_temp_zip->zip_fd)-$v_offset; - // ----- Create the central dir footer - if (($v_result = $v_temp_zip->privWriteCentralHeader(sizeof($v_header_list), $v_size, $v_offset, $v_comment)) != 1) { - // ----- Reset the file list - unset($v_header_list); - $v_temp_zip->privCloseFd(); - $this->privCloseFd(); - @unlink($v_zip_temp_name); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // ----- Close - $v_temp_zip->privCloseFd(); - $this->privCloseFd(); - // ----- Delete the zip file - // TBC : I should test the result ... - @unlink($this->zipname); - // ----- Rename the temporary file - // TBC : I should test the result ... - //@rename($v_zip_temp_name, $this->zipname); - PclZipUtilRename($v_zip_temp_name, $this->zipname); - // ----- Destroy the temporary archive - unset($v_temp_zip); - } - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : privDirCheck() - // Description : - // Check if a directory exists, if not it creates it and all the parents directory - // which may be useful. - // Parameters : - // $p_dir : Directory path to check. - // Return Values : - // 1 : OK - // -1 : Unable to create directory - // -------------------------------------------------------------------------------- - function privDirCheck($p_dir, $p_is_dir=false) - { - $v_result = 1; - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privDirCheck", "entry='$p_dir', is_dir='".($p_is_dir?"true":"false")."'"); - // ----- Remove the final '/' - if (($p_is_dir) && (substr($p_dir, -1)=='/')) - { - $p_dir = substr($p_dir, 0, strlen($p_dir)-1); - } - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Looking for entry '$p_dir'"); - // ----- Check the directory availability - if ((is_dir($p_dir)) || ($p_dir == "")) - { - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, "'$p_dir' is a directory"); - return 1; - } - // ----- Extract parent directory - $p_parent_dir = dirname($p_dir); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Parent directory is '$p_parent_dir'"); - // ----- Just a check - if ($p_parent_dir != $p_dir) - { - // ----- Look for parent directory - if ($p_parent_dir != "") - { - if (($v_result = $this->privDirCheck($p_parent_dir)) != 1) - { - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - } - } - // ----- Create the directory - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Create directory '$p_dir'"); - if (!@mkdir($p_dir, 0777)) - { - // ----- Error log - PclZip::privErrorLog(PCLZIP_ERR_DIR_CREATE_FAIL, "Unable to create directory '$p_dir'"); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result, "Directory '$p_dir' created"); - return $v_result; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : privMerge() - // Description : - // If $p_archive_to_add does not exist, the function exit with a success result. - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privMerge(&$p_archive_to_add) - { - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privMerge", "archive='".$p_archive_to_add->zipname."'"); - $v_result=1; - // ----- Look if the archive_to_add exists - if (!is_file($p_archive_to_add->zipname)) - { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Archive to add does not exist. End of merge."); - // ----- Nothing to merge, so merge is a success - $v_result = 1; - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // ----- Look if the archive exists - if (!is_file($this->zipname)) - { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Archive does not exist, duplicate the archive_to_add."); - // ----- Do a duplicate - $v_result = $this->privDuplicate($p_archive_to_add->zipname); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // ----- Open the zip file - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Open file in binary read mode"); - if (($v_result=$this->privOpenFd('rb')) != 1) - { - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // ----- Read the central directory informations - $v_central_dir = array(); - if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) - { - $this->privCloseFd(); - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // ----- Go to beginning of File - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position in zip : ".ftell($this->zip_fd)."'"); - @rewind($this->zip_fd); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position in zip : ".ftell($this->zip_fd)."'"); - // ----- Open the archive_to_add file - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Open archive_to_add in binary read mode"); - if (($v_result=$p_archive_to_add->privOpenFd('rb')) != 1) - { - $this->privCloseFd(); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // ----- Read the central directory informations - $v_central_dir_to_add = array(); - if (($v_result = $p_archive_to_add->privReadEndCentralDir($v_central_dir_to_add)) != 1) - { - $this->privCloseFd(); - $p_archive_to_add->privCloseFd(); - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // ----- Go to beginning of File - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position in archive_to_add : ".ftell($p_archive_to_add->zip_fd)."'"); - @rewind($p_archive_to_add->zip_fd); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Position in archive_to_add : ".ftell($p_archive_to_add->zip_fd)."'"); - // ----- Creates a temporay file - $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp'; - // ----- Open the temporary file in write mode - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Open file in binary read mode"); - if (($v_zip_temp_fd = @fopen($v_zip_temp_name, 'wb')) == 0) - { - $this->privCloseFd(); - $p_archive_to_add->privCloseFd(); - PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_zip_temp_name.'\' in binary write mode'); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - // ----- Copy the files from the archive to the temporary file - // TBC : Here I should better append the file and go back to erase the central dir - $v_size = $v_central_dir['offset']; - while ($v_size != 0) - { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Read $v_read_size bytes"); - $v_buffer = fread($this->zip_fd, $v_read_size); - @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - // ----- Copy the files from the archive_to_add into the temporary file - $v_size = $v_central_dir_to_add['offset']; - while ($v_size != 0) - { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Read $v_read_size bytes"); - $v_buffer = fread($p_archive_to_add->zip_fd, $v_read_size); - @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - // ----- Store the offset of the central dir - $v_offset = @ftell($v_zip_temp_fd); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "New offset of central dir : $v_offset"); - // ----- Copy the block of file headers from the old archive - $v_size = $v_central_dir['size']; - while ($v_size != 0) - { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Read $v_read_size bytes"); - $v_buffer = @fread($this->zip_fd, $v_read_size); - @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - // ----- Copy the block of file headers from the archive_to_add - $v_size = $v_central_dir_to_add['size']; - while ($v_size != 0) - { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Read $v_read_size bytes"); - $v_buffer = @fread($p_archive_to_add->zip_fd, $v_read_size); - @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - // ----- Zip file comment - // TBC : I should merge the two comments - $v_comment = ''; - // ----- Calculate the size of the (new) central header - $v_size = @ftell($v_zip_temp_fd)-$v_offset; - // ----- Swap the file descriptor - // Here is a trick : I swap the temporary fd with the zip fd, in order to use - // the following methods on the temporary fil and not the real archive fd - $v_swap = $this->zip_fd; - $this->zip_fd = $v_zip_temp_fd; - $v_zip_temp_fd = $v_swap; - // ----- Create the central dir footer - if (($v_result = $this->privWriteCentralHeader($v_central_dir['entries']+$v_central_dir_to_add['entries'], $v_size, $v_offset, $v_comment)) != 1) - { - $this->privCloseFd(); - $p_archive_to_add->privCloseFd(); - @fclose($v_zip_temp_fd); - $this->zip_fd = null; - // ----- Reset the file list - unset($v_header_list); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // ----- Swap back the file descriptor - $v_swap = $this->zip_fd; - $this->zip_fd = $v_zip_temp_fd; - $v_zip_temp_fd = $v_swap; - // ----- Close - $this->privCloseFd(); - $p_archive_to_add->privCloseFd(); - // ----- Close the temporary file - @fclose($v_zip_temp_fd); - // ----- Delete the zip file - // TBC : I should test the result ... - @unlink($this->zipname); - // ----- Rename the temporary file - // TBC : I should test the result ... - //@rename($v_zip_temp_name, $this->zipname); - PclZipUtilRename($v_zip_temp_name, $this->zipname); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : privDuplicate() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function privDuplicate($p_archive_filename) - { - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZip::privDuplicate", "archive_filename='$p_archive_filename'"); - $v_result=1; - // ----- Look if the $p_archive_filename exists - if (!is_file($p_archive_filename)) - { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Archive to duplicate does not exist. End of duplicate."); - // ----- Nothing to duplicate, so duplicate is a success. - $v_result = 1; - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // ----- Open the zip file - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Open file in binary read mode"); - if (($v_result=$this->privOpenFd('wb')) != 1) - { - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // ----- Open the temporary file in write mode - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Open file in binary read mode"); - if (($v_zip_temp_fd = @fopen($p_archive_filename, 'rb')) == 0) - { - $this->privCloseFd(); - PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive file \''.$p_archive_filename.'\' in binary write mode'); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo()); - return PclZip::errorCode(); - } - // ----- Copy the files from the archive to the temporary file - // TBC : Here I should better append the file and go back to erase the central dir - $v_size = filesize($p_archive_filename); - while ($v_size != 0) - { - $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Read $v_read_size bytes"); - $v_buffer = fread($v_zip_temp_fd, $v_read_size); - @fwrite($this->zip_fd, $v_buffer, $v_read_size); - $v_size -= $v_read_size; - } - // ----- Close - $this->privCloseFd(); - // ----- Close the temporary file - @fclose($v_zip_temp_fd); - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : privErrorLog() - // Description : - // Parameters : - // -------------------------------------------------------------------------------- - function privErrorLog($p_error_code=0, $p_error_string='') - { - if (PCLZIP_ERROR_EXTERNAL == 1) { - PclError($p_error_code, $p_error_string); - } - else { - $this->error_code = $p_error_code; - $this->error_string = $p_error_string; - } - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : privErrorReset() - // Description : - // Parameters : - // -------------------------------------------------------------------------------- - function privErrorReset() - { - if (PCLZIP_ERROR_EXTERNAL == 1) { - PclErrorReset(); - } - else { - $this->error_code = 1; - $this->error_string = ''; - } - } - // -------------------------------------------------------------------------------- - } - // End of class - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : PclZipUtilPathReduction() - // Description : - // Parameters : - // Return Values : - // -------------------------------------------------------------------------------- - function PclZipUtilPathReduction($p_dir) - { - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZipUtilPathReduction", "dir='$p_dir'"); - $v_result = ""; - // ----- Look for not empty path - if ($p_dir != "") - { - // ----- Explode path by directory names - $v_list = explode("/", $p_dir); - // ----- Study directories from last to first - for ($i=sizeof($v_list)-1; $i>=0; $i--) - { - // ----- Look for current path - if ($v_list[$i] == ".") - { - // ----- Ignore this directory - // Should be the first $i=0, but no check is done - } - else if ($v_list[$i] == "..") - { - // ----- Ignore it and ignore the $i-1 - $i--; - } - else if (($v_list[$i] == "") && ($i!=(sizeof($v_list)-1)) && ($i!=0)) - { - // ----- Ignore only the double '//' in path, - // but not the first and last '/' - } - else - { - $v_result = $v_list[$i].($i!=(sizeof($v_list)-1)?"/".$v_result:""); - } - } - } - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : PclZipUtilPathInclusion() - // Description : - // This function indicates if the path $p_path is under the $p_dir tree. Or, - // said in an other way, if the file or sub-dir $p_path is inside the dir - // $p_dir. - // The function indicates also if the path is exactly the same as the dir. - // This function supports path with duplicated '/' like '//', but does not - // support '.' or '..' statements. - // Parameters : - // Return Values : - // 0 if $p_path is not inside directory $p_dir - // 1 if $p_path is inside directory $p_dir - // 2 if $p_path is exactly the same as $p_dir - // -------------------------------------------------------------------------------- - function PclZipUtilPathInclusion($p_dir, $p_path) - { - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZipUtilPathInclusion", "dir='$p_dir', path='$p_path'"); - $v_result = 1; - // ----- Explode dir and path by directory separator - $v_list_dir = explode("/", $p_dir); - $v_list_dir_size = sizeof($v_list_dir); - $v_list_path = explode("/", $p_path); - $v_list_path_size = sizeof($v_list_path); - // ----- Study directories paths - $i = 0; - $j = 0; - while (($i < $v_list_dir_size) && ($j < $v_list_path_size) && ($v_result)) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Working on dir($i)='".$v_list_dir[$i]."' and path($j)='".$v_list_path[$j]."'"); - // ----- Look for empty dir (path reduction) - if ($v_list_dir[$i] == '') { - $i++; - continue; - } - if ($v_list_path[$j] == '') { - $j++; - continue; - } - // ----- Compare the items - if (($v_list_dir[$i] != $v_list_path[$j]) && ($v_list_dir[$i] != '') && ( $v_list_path[$j] != '')) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Items ($i,$j) are different"); - $v_result = 0; - } - // ----- Next items - $i++; - $j++; - } - // ----- Look if everything seems to be the same - if ($v_result) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Look for tie break"); - // ----- Skip all the empty items - while (($j < $v_list_path_size) && ($v_list_path[$j] == '')) $j++; - while (($i < $v_list_dir_size) && ($v_list_dir[$i] == '')) $i++; - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Looking on dir($i)='".($i < $v_list_dir_size?$v_list_dir[$i]:'')."' and path($j)='".($j < $v_list_path_size?$v_list_path[$j]:'')."'"); - if (($i >= $v_list_dir_size) && ($j >= $v_list_path_size)) { - // ----- There are exactly the same - $v_result = 2; - } - else if ($i < $v_list_dir_size) { - // ----- The path is shorter than the dir - $v_result = 0; - } - } - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : PclZipUtilCopyBlock() - // Description : - // Parameters : - // $p_mode : read/write compression mode - // 0 : src & dest normal - // 1 : src gzip, dest normal - // 2 : src normal, dest gzip - // 3 : src & dest gzip - // Return Values : - // -------------------------------------------------------------------------------- - function PclZipUtilCopyBlock($p_src, $p_dest, $p_size, $p_mode=0) - { - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZipUtilCopyBlock", "size=$p_size, mode=$p_mode"); - $v_result = 1; - if ($p_mode==0) - { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Src offset before read :".(@ftell($p_src))); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Dest offset before write :".(@ftell($p_dest))); - while ($p_size != 0) - { - $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Read $v_read_size bytes"); - $v_buffer = @fread($p_src, $v_read_size); - @fwrite($p_dest, $v_buffer, $v_read_size); - $p_size -= $v_read_size; - } - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Src offset after read :".(@ftell($p_src))); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Dest offset after write :".(@ftell($p_dest))); - } - else if ($p_mode==1) - { - while ($p_size != 0) - { - $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Read $v_read_size bytes"); - $v_buffer = @gzread($p_src, $v_read_size); - @fwrite($p_dest, $v_buffer, $v_read_size); - $p_size -= $v_read_size; - } - } - else if ($p_mode==2) - { - while ($p_size != 0) - { - $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Read $v_read_size bytes"); - $v_buffer = @fread($p_src, $v_read_size); - @gzwrite($p_dest, $v_buffer, $v_read_size); - $p_size -= $v_read_size; - } - } - else if ($p_mode==3) - { - while ($p_size != 0) - { - $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 4, "Read $v_read_size bytes"); - $v_buffer = @gzread($p_src, $v_read_size); - @gzwrite($p_dest, $v_buffer, $v_read_size); - $p_size -= $v_read_size; - } - } - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : PclZipUtilRename() - // Description : - // This function tries to do a simple rename() function. If it fails, it - // tries to copy the $p_src file in a new $p_dest file and then unlink the - // first one. - // Parameters : - // $p_src : Old filename - // $p_dest : New filename - // Return Values : - // 1 on success, 0 on failure. - // -------------------------------------------------------------------------------- - function PclZipUtilRename($p_src, $p_dest) - { - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZipUtilRename", "source=$p_src, destination=$p_dest"); - $v_result = 1; - // ----- Try to rename the files - if (!@rename($p_src, $p_dest)) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Fail to rename file, try copy+unlink"); - // ----- Try to copy & unlink the src - if (!@copy($p_src, $p_dest)) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Fail to copy file"); - $v_result = 0; - } - else if (!@unlink($p_src)) { - //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Fail to unlink old filename"); - $v_result = 0; - } - } - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : PclZipUtilOptionText() - // Description : - // Translate option value in text. Mainly for debug purpose. - // Parameters : - // $p_option : the option value. - // Return Values : - // The option text value. - // -------------------------------------------------------------------------------- - function PclZipUtilOptionText($p_option) - { - //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, "PclZipUtilOptionText", "option='".$p_option."'"); - switch ($p_option) { - case PCLZIP_OPT_PATH : - $v_result = 'PCLZIP_OPT_PATH'; - break; - case PCLZIP_OPT_ADD_PATH : - $v_result = 'PCLZIP_OPT_ADD_PATH'; - break; - case PCLZIP_OPT_REMOVE_PATH : - $v_result = 'PCLZIP_OPT_REMOVE_PATH'; - break; - case PCLZIP_OPT_REMOVE_ALL_PATH : - $v_result = 'PCLZIP_OPT_REMOVE_ALL_PATH'; - break; - case PCLZIP_OPT_EXTRACT_AS_STRING : - $v_result = 'PCLZIP_OPT_EXTRACT_AS_STRING'; - break; - case PCLZIP_OPT_SET_CHMOD : - $v_result = 'PCLZIP_OPT_SET_CHMOD'; - break; - case PCLZIP_OPT_BY_NAME : - $v_result = 'PCLZIP_OPT_BY_NAME'; - break; - case PCLZIP_OPT_BY_INDEX : - $v_result = 'PCLZIP_OPT_BY_INDEX'; - break; - case PCLZIP_OPT_BY_EREG : - $v_result = 'PCLZIP_OPT_BY_EREG'; - break; - case PCLZIP_OPT_BY_PREG : - $v_result = 'PCLZIP_OPT_BY_PREG'; - break; - case PCLZIP_CB_PRE_EXTRACT : - $v_result = 'PCLZIP_CB_PRE_EXTRACT'; - break; - case PCLZIP_CB_POST_EXTRACT : - $v_result = 'PCLZIP_CB_POST_EXTRACT'; - break; - case PCLZIP_CB_PRE_ADD : - $v_result = 'PCLZIP_CB_PRE_ADD'; - break; - case PCLZIP_CB_POST_ADD : - $v_result = 'PCLZIP_CB_POST_ADD'; - break; - default : - $v_result = 'Unknown'; - } - // ----- Return - //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result); - return $v_result; - } - // -------------------------------------------------------------------------------- - // -------------------------------------------------------------------------------- - // Function : PclZipUtilTranslateWinPath() - // Description : - // Translate windows path by replacing '\' by '/' and optionally removing - // drive letter. - // Parameters : - // $p_path : path to translate. - // $p_remove_disk_letter : true | false - // Return Values : - // The path translated. - // -------------------------------------------------------------------------------- - function PclZipUtilTranslateWinPath($p_path, $p_remove_disk_letter=true) - { - if (stristr(php_uname(), 'windows')) { - // ----- Look for potential disk letter - if (($p_remove_disk_letter) && (($v_position = strpos($p_path, ':')) != false)) { - $p_path = substr($p_path, $v_position+1); - } - // ----- Change potential windows directory separator - if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) { - $p_path = strtr($p_path, '\\', '/'); - } - } - return $p_path; - } - // -------------------------------------------------------------------------------- -?> diff --git a/pear/Archive/Tar.php b/pear/Archive/Tar.php deleted file mode 100644 index 61eff98..0000000 --- a/pear/Archive/Tar.php +++ /dev/null @@ -1,1909 +0,0 @@ - - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * - * @category File_Formats - * @package Archive_Tar - * @author Vincent Blavet - * @copyright 1997-2008 The Authors - * @license http://www.opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id: Tar.php 295988 2010-03-09 08:39:37Z mrook $ - * @link http://pear.php.net/package/Archive_Tar - */ - -require_once 'PEAR.php'; - - -define ('ARCHIVE_TAR_ATT_SEPARATOR', 90001); -define ('ARCHIVE_TAR_END_BLOCK', pack("a512", '')); - -/** -* Creates a (compressed) Tar archive -* -* @author Vincent Blavet -* @version $Revision: 295988 $ -* @license http://www.opensource.org/licenses/bsd-license.php New BSD License -* @package Archive_Tar -*/ -class Archive_Tar extends PEAR -{ - /** - * @var string Name of the Tar - */ - var $_tarname=''; - - /** - * @var boolean if true, the Tar file will be gzipped - */ - var $_compress=false; - - /** - * @var string Type of compression : 'none', 'gz' or 'bz2' - */ - var $_compress_type='none'; - - /** - * @var string Explode separator - */ - var $_separator=' '; - - /** - * @var file descriptor - */ - var $_file=0; - - /** - * @var string Local Tar name of a remote Tar (http:// or ftp://) - */ - var $_temp_tarname=''; - - /** - * @var string regular expression for ignoring files or directories - */ - var $_ignore_regexp=''; - - // {{{ constructor - /** - * Archive_Tar Class constructor. This flavour of the constructor only - * declare a new Archive_Tar object, identifying it by the name of the - * tar file. - * If the compress argument is set the tar will be read or created as a - * gzip or bz2 compressed TAR file. - * - * @param string $p_tarname The name of the tar archive to create - * @param string $p_compress can be null, 'gz' or 'bz2'. This - * parameter indicates if gzip or bz2 compression - * is required. For compatibility reason the - * boolean value 'true' means 'gz'. - * @access public - */ - function Archive_Tar($p_tarname, $p_compress = null) - { - $this->PEAR(); - $this->_compress = false; - $this->_compress_type = 'none'; - if (($p_compress === null) || ($p_compress == '')) { - if (@file_exists($p_tarname)) { - if ($fp = @fopen($p_tarname, "rb")) { - // look for gzip magic cookie - $data = fread($fp, 2); - fclose($fp); - if ($data == "\37\213") { - $this->_compress = true; - $this->_compress_type = 'gz'; - // No sure it's enought for a magic code .... - } elseif ($data == "BZ") { - $this->_compress = true; - $this->_compress_type = 'bz2'; - } - } - } else { - // probably a remote file or some file accessible - // through a stream interface - if (substr($p_tarname, -2) == 'gz') { - $this->_compress = true; - $this->_compress_type = 'gz'; - } elseif ((substr($p_tarname, -3) == 'bz2') || - (substr($p_tarname, -2) == 'bz')) { - $this->_compress = true; - $this->_compress_type = 'bz2'; - } - } - } else { - if (($p_compress === true) || ($p_compress == 'gz')) { - $this->_compress = true; - $this->_compress_type = 'gz'; - } else if ($p_compress == 'bz2') { - $this->_compress = true; - $this->_compress_type = 'bz2'; - } else { - $this->_error("Unsupported compression type '$p_compress'\n". - "Supported types are 'gz' and 'bz2'.\n"); - return false; - } - } - $this->_tarname = $p_tarname; - if ($this->_compress) { // assert zlib or bz2 extension support - if ($this->_compress_type == 'gz') - $extname = 'zlib'; - else if ($this->_compress_type == 'bz2') - $extname = 'bz2'; - - if (!extension_loaded($extname)) { - PEAR::loadExtension($extname); - } - if (!extension_loaded($extname)) { - $this->_error("The extension '$extname' couldn't be found.\n". - "Please make sure your version of PHP was built ". - "with '$extname' support.\n"); - return false; - } - } - } - // }}} - - // {{{ destructor - function _Archive_Tar() - { - $this->_close(); - // ----- Look for a local copy to delete - if ($this->_temp_tarname != '') - @unlink($this->_temp_tarname); - $this->_PEAR(); - } - // }}} - - // {{{ create() - /** - * This method creates the archive file and add the files / directories - * that are listed in $p_filelist. - * If a file with the same name exist and is writable, it is replaced - * by the new tar. - * The method return false and a PEAR error text. - * The $p_filelist parameter can be an array of string, each string - * representing a filename or a directory name with their path if - * needed. It can also be a single string with names separated by a - * single blank. - * For each directory added in the archive, the files and - * sub-directories are also added. - * See also createModify() method for more details. - * - * @param array $p_filelist An array of filenames and directory names, or a - * single string with names separated by a single - * blank space. - * @return true on success, false on error. - * @see createModify() - * @access public - */ - function create($p_filelist) - { - return $this->createModify($p_filelist, '', ''); - } - // }}} - - // {{{ add() - /** - * This method add the files / directories that are listed in $p_filelist in - * the archive. If the archive does not exist it is created. - * The method return false and a PEAR error text. - * The files and directories listed are only added at the end of the archive, - * even if a file with the same name is already archived. - * See also createModify() method for more details. - * - * @param array $p_filelist An array of filenames and directory names, or a - * single string with names separated by a single - * blank space. - * @return true on success, false on error. - * @see createModify() - * @access public - */ - function add($p_filelist) - { - return $this->addModify($p_filelist, '', ''); - } - // }}} - - // {{{ extract() - function extract($p_path='') - { - return $this->extractModify($p_path, ''); - } - // }}} - - // {{{ listContent() - function listContent() - { - $v_list_detail = array(); - - if ($this->_openRead()) { - if (!$this->_extractList('', $v_list_detail, "list", '', '')) { - unset($v_list_detail); - $v_list_detail = 0; - } - $this->_close(); - } - - return $v_list_detail; - } - // }}} - - // {{{ createModify() - /** - * This method creates the archive file and add the files / directories - * that are listed in $p_filelist. - * If the file already exists and is writable, it is replaced by the - * new tar. It is a create and not an add. If the file exists and is - * read-only or is a directory it is not replaced. The method return - * false and a PEAR error text. - * The $p_filelist parameter can be an array of string, each string - * representing a filename or a directory name with their path if - * needed. It can also be a single string with names separated by a - * single blank. - * The path indicated in $p_remove_dir will be removed from the - * memorized path of each file / directory listed when this path - * exists. By default nothing is removed (empty path '') - * The path indicated in $p_add_dir will be added at the beginning of - * the memorized path of each file / directory listed. However it can - * be set to empty ''. The adding of a path is done after the removing - * of path. - * The path add/remove ability enables the user to prepare an archive - * for extraction in a different path than the origin files are. - * See also addModify() method for file adding properties. - * - * @param array $p_filelist An array of filenames and directory names, - * or a single string with names separated by - * a single blank space. - * @param string $p_add_dir A string which contains a path to be added - * to the memorized path of each element in - * the list. - * @param string $p_remove_dir A string which contains a path to be - * removed from the memorized path of each - * element in the list, when relevant. - * @return boolean true on success, false on error. - * @access public - * @see addModify() - */ - function createModify($p_filelist, $p_add_dir, $p_remove_dir='') - { - $v_result = true; - - if (!$this->_openWrite()) - return false; - - if ($p_filelist != '') { - if (is_array($p_filelist)) - $v_list = $p_filelist; - elseif (is_string($p_filelist)) - $v_list = explode($this->_separator, $p_filelist); - else { - $this->_cleanFile(); - $this->_error('Invalid file list'); - return false; - } - - $v_result = $this->_addList($v_list, $p_add_dir, $p_remove_dir); - } - - if ($v_result) { - $this->_writeFooter(); - $this->_close(); - } else - $this->_cleanFile(); - - return $v_result; - } - // }}} - - // {{{ addModify() - /** - * This method add the files / directories listed in $p_filelist at the - * end of the existing archive. If the archive does not yet exists it - * is created. - * The $p_filelist parameter can be an array of string, each string - * representing a filename or a directory name with their path if - * needed. It can also be a single string with names separated by a - * single blank. - * The path indicated in $p_remove_dir will be removed from the - * memorized path of each file / directory listed when this path - * exists. By default nothing is removed (empty path '') - * The path indicated in $p_add_dir will be added at the beginning of - * the memorized path of each file / directory listed. However it can - * be set to empty ''. The adding of a path is done after the removing - * of path. - * The path add/remove ability enables the user to prepare an archive - * for extraction in a different path than the origin files are. - * If a file/dir is already in the archive it will only be added at the - * end of the archive. There is no update of the existing archived - * file/dir. However while extracting the archive, the last file will - * replace the first one. This results in a none optimization of the - * archive size. - * If a file/dir does not exist the file/dir is ignored. However an - * error text is send to PEAR error. - * If a file/dir is not readable the file/dir is ignored. However an - * error text is send to PEAR error. - * - * @param array $p_filelist An array of filenames and directory - * names, or a single string with names - * separated by a single blank space. - * @param string $p_add_dir A string which contains a path to be - * added to the memorized path of each - * element in the list. - * @param string $p_remove_dir A string which contains a path to be - * removed from the memorized path of - * each element in the list, when - * relevant. - * @return true on success, false on error. - * @access public - */ - function addModify($p_filelist, $p_add_dir, $p_remove_dir='') - { - $v_result = true; - - if (!$this->_isArchive()) - $v_result = $this->createModify($p_filelist, $p_add_dir, - $p_remove_dir); - else { - if (is_array($p_filelist)) - $v_list = $p_filelist; - elseif (is_string($p_filelist)) - $v_list = explode($this->_separator, $p_filelist); - else { - $this->_error('Invalid file list'); - return false; - } - - $v_result = $this->_append($v_list, $p_add_dir, $p_remove_dir); - } - - return $v_result; - } - // }}} - - // {{{ addString() - /** - * This method add a single string as a file at the - * end of the existing archive. If the archive does not yet exists it - * is created. - * - * @param string $p_filename A string which contains the full - * filename path that will be associated - * with the string. - * @param string $p_string The content of the file added in - * the archive. - * @return true on success, false on error. - * @access public - */ - function addString($p_filename, $p_string) - { - $v_result = true; - - if (!$this->_isArchive()) { - if (!$this->_openWrite()) { - return false; - } - $this->_close(); - } - - if (!$this->_openAppend()) - return false; - - // Need to check the get back to the temporary file ? .... - $v_result = $this->_addString($p_filename, $p_string); - - $this->_writeFooter(); - - $this->_close(); - - return $v_result; - } - // }}} - - // {{{ extractModify() - /** - * This method extract all the content of the archive in the directory - * indicated by $p_path. When relevant the memorized path of the - * files/dir can be modified by removing the $p_remove_path path at the - * beginning of the file/dir path. - * While extracting a file, if the directory path does not exists it is - * created. - * While extracting a file, if the file already exists it is replaced - * without looking for last modification date. - * While extracting a file, if the file already exists and is write - * protected, the extraction is aborted. - * While extracting a file, if a directory with the same name already - * exists, the extraction is aborted. - * While extracting a directory, if a file with the same name already - * exists, the extraction is aborted. - * While extracting a file/directory if the destination directory exist - * and is write protected, or does not exist but can not be created, - * the extraction is aborted. - * If after extraction an extracted file does not show the correct - * stored file size, the extraction is aborted. - * When the extraction is aborted, a PEAR error text is set and false - * is returned. However the result can be a partial extraction that may - * need to be manually cleaned. - * - * @param string $p_path The path of the directory where the - * files/dir need to by extracted. - * @param string $p_remove_path Part of the memorized path that can be - * removed if present at the beginning of - * the file/dir path. - * @return boolean true on success, false on error. - * @access public - * @see extractList() - */ - function extractModify($p_path, $p_remove_path) - { - $v_result = true; - $v_list_detail = array(); - - if ($v_result = $this->_openRead()) { - $v_result = $this->_extractList($p_path, $v_list_detail, - "complete", 0, $p_remove_path); - $this->_close(); - } - - return $v_result; - } - // }}} - - // {{{ extractInString() - /** - * This method extract from the archive one file identified by $p_filename. - * The return value is a string with the file content, or NULL on error. - * @param string $p_filename The path of the file to extract in a string. - * @return a string with the file content or NULL. - * @access public - */ - function extractInString($p_filename) - { - if ($this->_openRead()) { - $v_result = $this->_extractInString($p_filename); - $this->_close(); - } else { - $v_result = NULL; - } - - return $v_result; - } - // }}} - - // {{{ extractList() - /** - * This method extract from the archive only the files indicated in the - * $p_filelist. These files are extracted in the current directory or - * in the directory indicated by the optional $p_path parameter. - * If indicated the $p_remove_path can be used in the same way as it is - * used in extractModify() method. - * @param array $p_filelist An array of filenames and directory names, - * or a single string with names separated - * by a single blank space. - * @param string $p_path The path of the directory where the - * files/dir need to by extracted. - * @param string $p_remove_path Part of the memorized path that can be - * removed if present at the beginning of - * the file/dir path. - * @return true on success, false on error. - * @access public - * @see extractModify() - */ - function extractList($p_filelist, $p_path='', $p_remove_path='') - { - $v_result = true; - $v_list_detail = array(); - - if (is_array($p_filelist)) - $v_list = $p_filelist; - elseif (is_string($p_filelist)) - $v_list = explode($this->_separator, $p_filelist); - else { - $this->_error('Invalid string list'); - return false; - } - - if ($v_result = $this->_openRead()) { - $v_result = $this->_extractList($p_path, $v_list_detail, "partial", - $v_list, $p_remove_path); - $this->_close(); - } - - return $v_result; - } - // }}} - - // {{{ setAttribute() - /** - * This method set specific attributes of the archive. It uses a variable - * list of parameters, in the format attribute code + attribute values : - * $arch->setAttribute(ARCHIVE_TAR_ATT_SEPARATOR, ','); - * @param mixed $argv variable list of attributes and values - * @return true on success, false on error. - * @access public - */ - function setAttribute() - { - $v_result = true; - - // ----- Get the number of variable list of arguments - if (($v_size = func_num_args()) == 0) { - return true; - } - - // ----- Get the arguments - $v_att_list = &func_get_args(); - - // ----- Read the attributes - $i=0; - while ($i<$v_size) { - - // ----- Look for next option - switch ($v_att_list[$i]) { - // ----- Look for options that request a string value - case ARCHIVE_TAR_ATT_SEPARATOR : - // ----- Check the number of parameters - if (($i+1) >= $v_size) { - $this->_error('Invalid number of parameters for ' - .'attribute ARCHIVE_TAR_ATT_SEPARATOR'); - return false; - } - - // ----- Get the value - $this->_separator = $v_att_list[$i+1]; - $i++; - break; - - default : - $this->_error('Unknow attribute code '.$v_att_list[$i].''); - return false; - } - - // ----- Next attribute - $i++; - } - - return $v_result; - } - // }}} - - // {{{ setIgnoreRegexp() - /** - * This method sets the regular expression for ignoring files and directories - * at import, for example: - * $arch->setIgnoreRegexp("#CVS|\.svn#"); - * @param string $regexp regular expression defining which files or directories to ignore - * @access public - */ - function setIgnoreRegexp($regexp) - { - $this->_ignore_regexp = $regexp; - } - // }}} - - // {{{ setIgnoreList() - /** - * This method sets the regular expression for ignoring all files and directories - * matching the filenames in the array list at import, for example: - * $arch->setIgnoreList(array('CVS', '.svn', 'bin/tool')); - * @param array $list a list of file or directory names to ignore - * @access public - */ - function setIgnoreList($list) - { - $regexp = str_replace(array('#', '.', '^', '$'), array('\#', '\.', '\^', '\$'), $list); - $regexp = '#/'.join('$|/', $list).'#'; - $this->setIgnoreRegexp($regexp); - } - // }}} - - // {{{ _error() - function _error($p_message) - { - // ----- To be completed - $this->raiseError($p_message); - } - // }}} - - // {{{ _warning() - function _warning($p_message) - { - // ----- To be completed - $this->raiseError($p_message); - } - // }}} - - // {{{ _isArchive() - function _isArchive($p_filename=NULL) - { - if ($p_filename == NULL) { - $p_filename = $this->_tarname; - } - clearstatcache(); - return @is_file($p_filename) && !@is_link($p_filename); - } - // }}} - - // {{{ _openWrite() - function _openWrite() - { - if ($this->_compress_type == 'gz') - $this->_file = @gzopen($this->_tarname, "wb9"); - else if ($this->_compress_type == 'bz2') - $this->_file = @bzopen($this->_tarname, "w"); - else if ($this->_compress_type == 'none') - $this->_file = @fopen($this->_tarname, "wb"); - else - $this->_error('Unknown or missing compression type (' - .$this->_compress_type.')'); - - if ($this->_file == 0) { - $this->_error('Unable to open in write mode \'' - .$this->_tarname.'\''); - return false; - } - - return true; - } - // }}} - - // {{{ _openRead() - function _openRead() - { - if (strtolower(substr($this->_tarname, 0, 7)) == 'http://') { - - // ----- Look if a local copy need to be done - if ($this->_temp_tarname == '') { - $this->_temp_tarname = uniqid('tar').'.tmp'; - if (!$v_file_from = @fopen($this->_tarname, 'rb')) { - $this->_error('Unable to open in read mode \'' - .$this->_tarname.'\''); - $this->_temp_tarname = ''; - return false; - } - if (!$v_file_to = @fopen($this->_temp_tarname, 'wb')) { - $this->_error('Unable to open in write mode \'' - .$this->_temp_tarname.'\''); - $this->_temp_tarname = ''; - return false; - } - while ($v_data = @fread($v_file_from, 1024)) - @fwrite($v_file_to, $v_data); - @fclose($v_file_from); - @fclose($v_file_to); - } - - // ----- File to open if the local copy - $v_filename = $this->_temp_tarname; - - } else - // ----- File to open if the normal Tar file - $v_filename = $this->_tarname; - - if ($this->_compress_type == 'gz') - $this->_file = @gzopen($v_filename, "rb"); - else if ($this->_compress_type == 'bz2') - $this->_file = @bzopen($v_filename, "r"); - else if ($this->_compress_type == 'none') - $this->_file = @fopen($v_filename, "rb"); - else - $this->_error('Unknown or missing compression type (' - .$this->_compress_type.')'); - - if ($this->_file == 0) { - $this->_error('Unable to open in read mode \''.$v_filename.'\''); - return false; - } - - return true; - } - // }}} - - // {{{ _openReadWrite() - function _openReadWrite() - { - if ($this->_compress_type == 'gz') - $this->_file = @gzopen($this->_tarname, "r+b"); - else if ($this->_compress_type == 'bz2') { - $this->_error('Unable to open bz2 in read/write mode \'' - .$this->_tarname.'\' (limitation of bz2 extension)'); - return false; - } else if ($this->_compress_type == 'none') - $this->_file = @fopen($this->_tarname, "r+b"); - else - $this->_error('Unknown or missing compression type (' - .$this->_compress_type.')'); - - if ($this->_file == 0) { - $this->_error('Unable to open in read/write mode \'' - .$this->_tarname.'\''); - return false; - } - - return true; - } - // }}} - - // {{{ _close() - function _close() - { - //if (isset($this->_file)) { - if (is_resource($this->_file)) { - if ($this->_compress_type == 'gz') - @gzclose($this->_file); - else if ($this->_compress_type == 'bz2') - @bzclose($this->_file); - else if ($this->_compress_type == 'none') - @fclose($this->_file); - else - $this->_error('Unknown or missing compression type (' - .$this->_compress_type.')'); - - $this->_file = 0; - } - - // ----- Look if a local copy need to be erase - // Note that it might be interesting to keep the url for a time : ToDo - if ($this->_temp_tarname != '') { - @unlink($this->_temp_tarname); - $this->_temp_tarname = ''; - } - - return true; - } - // }}} - - // {{{ _cleanFile() - function _cleanFile() - { - $this->_close(); - - // ----- Look for a local copy - if ($this->_temp_tarname != '') { - // ----- Remove the local copy but not the remote tarname - @unlink($this->_temp_tarname); - $this->_temp_tarname = ''; - } else { - // ----- Remove the local tarname file - @unlink($this->_tarname); - } - $this->_tarname = ''; - - return true; - } - // }}} - - // {{{ _writeBlock() - function _writeBlock($p_binary_data, $p_len=null) - { - if (is_resource($this->_file)) { - if ($p_len === null) { - if ($this->_compress_type == 'gz') - @gzputs($this->_file, $p_binary_data); - else if ($this->_compress_type == 'bz2') - @bzwrite($this->_file, $p_binary_data); - else if ($this->_compress_type == 'none') - @fputs($this->_file, $p_binary_data); - else - $this->_error('Unknown or missing compression type (' - .$this->_compress_type.')'); - } else { - if ($this->_compress_type == 'gz') - @gzputs($this->_file, $p_binary_data, $p_len); - else if ($this->_compress_type == 'bz2') - @bzwrite($this->_file, $p_binary_data, $p_len); - else if ($this->_compress_type == 'none') - @fputs($this->_file, $p_binary_data, $p_len); - else - $this->_error('Unknown or missing compression type (' - .$this->_compress_type.')'); - - } - } - return true; - } - // }}} - - // {{{ _readBlock() - function _readBlock() - { - $v_block = null; - if (is_resource($this->_file)) { - if ($this->_compress_type == 'gz') - $v_block = @gzread($this->_file, 512); - else if ($this->_compress_type == 'bz2') - $v_block = @bzread($this->_file, 512); - else if ($this->_compress_type == 'none') - $v_block = @fread($this->_file, 512); - else - $this->_error('Unknown or missing compression type (' - .$this->_compress_type.')'); - } - return $v_block; - } - // }}} - - // {{{ _jumpBlock() - function _jumpBlock($p_len=null) - { - if (is_resource($this->_file)) { - if ($p_len === null) - $p_len = 1; - - if ($this->_compress_type == 'gz') { - @gzseek($this->_file, gztell($this->_file)+($p_len*512)); - } - else if ($this->_compress_type == 'bz2') { - // ----- Replace missing bztell() and bzseek() - for ($i=0; $i<$p_len; $i++) - $this->_readBlock(); - } else if ($this->_compress_type == 'none') - @fseek($this->_file, $p_len*512, SEEK_CUR); - else - $this->_error('Unknown or missing compression type (' - .$this->_compress_type.')'); - - } - return true; - } - // }}} - - // {{{ _writeFooter() - function _writeFooter() - { - if (is_resource($this->_file)) { - // ----- Write the last 0 filled block for end of archive - $v_binary_data = pack('a1024', ''); - $this->_writeBlock($v_binary_data); - } - return true; - } - // }}} - - // {{{ _addList() - function _addList($p_list, $p_add_dir, $p_remove_dir) - { - $v_result=true; - $v_header = array(); - - // ----- Remove potential windows directory separator - $p_add_dir = $this->_translateWinPath($p_add_dir); - $p_remove_dir = $this->_translateWinPath($p_remove_dir, false); - - if (!$this->_file) { - $this->_error('Invalid file descriptor'); - return false; - } - - if (sizeof($p_list) == 0) - return true; - - foreach ($p_list as $v_filename) { - if (!$v_result) { - break; - } - - // ----- Skip the current tar name - if ($v_filename == $this->_tarname) - continue; - - if ($v_filename == '') - continue; - - // ----- ignore files and directories matching the ignore regular expression - if ($this->_ignore_regexp && preg_match($this->_ignore_regexp, '/'.$v_filename)) { - $this->_warning("File '$v_filename' ignored"); - continue; - } - - if (!file_exists($v_filename)) { - $this->_warning("File '$v_filename' does not exist"); - continue; - } - - // ----- Add the file or directory header - if (!$this->_addFile($v_filename, $v_header, $p_add_dir, $p_remove_dir)) - return false; - - if (@is_dir($v_filename) && !@is_link($v_filename)) { - if (!($p_hdir = opendir($v_filename))) { - $this->_warning("Directory '$v_filename' can not be read"); - continue; - } - while (false !== ($p_hitem = readdir($p_hdir))) { - if (($p_hitem != '.') && ($p_hitem != '..')) { - if ($v_filename != ".") - $p_temp_list[0] = $v_filename.'/'.$p_hitem; - else - $p_temp_list[0] = $p_hitem; - - $v_result = $this->_addList($p_temp_list, - $p_add_dir, - $p_remove_dir); - } - } - - unset($p_temp_list); - unset($p_hdir); - unset($p_hitem); - } - } - - return $v_result; - } - // }}} - - // {{{ _addFile() - function _addFile($p_filename, &$p_header, $p_add_dir, $p_remove_dir) - { - if (!$this->_file) { - $this->_error('Invalid file descriptor'); - return false; - } - - if ($p_filename == '') { - $this->_error('Invalid file name'); - return false; - } - - // ----- Calculate the stored filename - $p_filename = $this->_translateWinPath($p_filename, false);; - $v_stored_filename = $p_filename; - if (strcmp($p_filename, $p_remove_dir) == 0) { - return true; - } - if ($p_remove_dir != '') { - if (substr($p_remove_dir, -1) != '/') - $p_remove_dir .= '/'; - - if (substr($p_filename, 0, strlen($p_remove_dir)) == $p_remove_dir) - $v_stored_filename = substr($p_filename, strlen($p_remove_dir)); - } - $v_stored_filename = $this->_translateWinPath($v_stored_filename); - if ($p_add_dir != '') { - if (substr($p_add_dir, -1) == '/') - $v_stored_filename = $p_add_dir.$v_stored_filename; - else - $v_stored_filename = $p_add_dir.'/'.$v_stored_filename; - } - - $v_stored_filename = $this->_pathReduction($v_stored_filename); - - if ($this->_isArchive($p_filename)) { - if (($v_file = @fopen($p_filename, "rb")) == 0) { - $this->_warning("Unable to open file '".$p_filename - ."' in binary read mode"); - return true; - } - - if (!$this->_writeHeader($p_filename, $v_stored_filename)) - return false; - - while (($v_buffer = fread($v_file, 512)) != '') { - $v_binary_data = pack("a512", "$v_buffer"); - $this->_writeBlock($v_binary_data); - } - - fclose($v_file); - - } else { - // ----- Only header for dir - if (!$this->_writeHeader($p_filename, $v_stored_filename)) - return false; - } - - return true; - } - // }}} - - // {{{ _addString() - function _addString($p_filename, $p_string) - { - if (!$this->_file) { - $this->_error('Invalid file descriptor'); - return false; - } - - if ($p_filename == '') { - $this->_error('Invalid file name'); - return false; - } - - // ----- Calculate the stored filename - $p_filename = $this->_translateWinPath($p_filename, false);; - - if (!$this->_writeHeaderBlock($p_filename, strlen($p_string), - time(), 384, "", 0, 0)) - return false; - - $i=0; - while (($v_buffer = substr($p_string, (($i++)*512), 512)) != '') { - $v_binary_data = pack("a512", $v_buffer); - $this->_writeBlock($v_binary_data); - } - - return true; - } - // }}} - - // {{{ _writeHeader() - function _writeHeader($p_filename, $p_stored_filename) - { - if ($p_stored_filename == '') - $p_stored_filename = $p_filename; - $v_reduce_filename = $this->_pathReduction($p_stored_filename); - - if (strlen($v_reduce_filename) > 99) { - if (!$this->_writeLongHeader($v_reduce_filename)) - return false; - } - - $v_info = lstat($p_filename); - $v_uid = sprintf("%07s", DecOct($v_info[4])); - $v_gid = sprintf("%07s", DecOct($v_info[5])); - $v_perms = sprintf("%07s", DecOct($v_info['mode'] & 000777)); - - $v_mtime = sprintf("%011s", DecOct($v_info['mtime'])); - - $v_linkname = ''; - - if (@is_link($p_filename)) { - $v_typeflag = '2'; - $v_linkname = readlink($p_filename); - $v_size = sprintf("%011s", DecOct(0)); - } elseif (@is_dir($p_filename)) { - $v_typeflag = "5"; - $v_size = sprintf("%011s", DecOct(0)); - } else { - $v_typeflag = '0'; - clearstatcache(); - $v_size = sprintf("%011s", DecOct($v_info['size'])); - } - - $v_magic = 'ustar '; - - $v_version = ' '; - - if (function_exists('posix_getpwuid')) - { - $userinfo = posix_getpwuid($v_info[4]); - $groupinfo = posix_getgrgid($v_info[5]); - - $v_uname = $userinfo['name']; - $v_gname = $groupinfo['name']; - } - else - { - $v_uname = ''; - $v_gname = ''; - } - - $v_devmajor = ''; - - $v_devminor = ''; - - $v_prefix = ''; - - $v_binary_data_first = pack("a100a8a8a8a12a12", - $v_reduce_filename, $v_perms, $v_uid, - $v_gid, $v_size, $v_mtime); - $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12", - $v_typeflag, $v_linkname, $v_magic, - $v_version, $v_uname, $v_gname, - $v_devmajor, $v_devminor, $v_prefix, ''); - - // ----- Calculate the checksum - $v_checksum = 0; - // ..... First part of the header - for ($i=0; $i<148; $i++) - $v_checksum += ord(substr($v_binary_data_first,$i,1)); - // ..... Ignore the checksum value and replace it by ' ' (space) - for ($i=148; $i<156; $i++) - $v_checksum += ord(' '); - // ..... Last part of the header - for ($i=156, $j=0; $i<512; $i++, $j++) - $v_checksum += ord(substr($v_binary_data_last,$j,1)); - - // ----- Write the first 148 bytes of the header in the archive - $this->_writeBlock($v_binary_data_first, 148); - - // ----- Write the calculated checksum - $v_checksum = sprintf("%06s ", DecOct($v_checksum)); - $v_binary_data = pack("a8", $v_checksum); - $this->_writeBlock($v_binary_data, 8); - - // ----- Write the last 356 bytes of the header in the archive - $this->_writeBlock($v_binary_data_last, 356); - - return true; - } - // }}} - - // {{{ _writeHeaderBlock() - function _writeHeaderBlock($p_filename, $p_size, $p_mtime=0, $p_perms=0, - $p_type='', $p_uid=0, $p_gid=0) - { - $p_filename = $this->_pathReduction($p_filename); - - if (strlen($p_filename) > 99) { - if (!$this->_writeLongHeader($p_filename)) - return false; - } - - if ($p_type == "5") { - $v_size = sprintf("%011s", DecOct(0)); - } else { - $v_size = sprintf("%011s", DecOct($p_size)); - } - - $v_uid = sprintf("%07s", DecOct($p_uid)); - $v_gid = sprintf("%07s", DecOct($p_gid)); - $v_perms = sprintf("%07s", DecOct($p_perms & 000777)); - - $v_mtime = sprintf("%11s", DecOct($p_mtime)); - - $v_linkname = ''; - - $v_magic = 'ustar '; - - $v_version = ' '; - - if (function_exists('posix_getpwuid')) - { - $userinfo = posix_getpwuid($p_uid); - $groupinfo = posix_getgrgid($p_gid); - - $v_uname = $userinfo['name']; - $v_gname = $groupinfo['name']; - } - else - { - $v_uname = ''; - $v_gname = ''; - } - - $v_devmajor = ''; - - $v_devminor = ''; - - $v_prefix = ''; - - $v_binary_data_first = pack("a100a8a8a8a12A12", - $p_filename, $v_perms, $v_uid, $v_gid, - $v_size, $v_mtime); - $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12", - $p_type, $v_linkname, $v_magic, - $v_version, $v_uname, $v_gname, - $v_devmajor, $v_devminor, $v_prefix, ''); - - // ----- Calculate the checksum - $v_checksum = 0; - // ..... First part of the header - for ($i=0; $i<148; $i++) - $v_checksum += ord(substr($v_binary_data_first,$i,1)); - // ..... Ignore the checksum value and replace it by ' ' (space) - for ($i=148; $i<156; $i++) - $v_checksum += ord(' '); - // ..... Last part of the header - for ($i=156, $j=0; $i<512; $i++, $j++) - $v_checksum += ord(substr($v_binary_data_last,$j,1)); - - // ----- Write the first 148 bytes of the header in the archive - $this->_writeBlock($v_binary_data_first, 148); - - // ----- Write the calculated checksum - $v_checksum = sprintf("%06s ", DecOct($v_checksum)); - $v_binary_data = pack("a8", $v_checksum); - $this->_writeBlock($v_binary_data, 8); - - // ----- Write the last 356 bytes of the header in the archive - $this->_writeBlock($v_binary_data_last, 356); - - return true; - } - // }}} - - // {{{ _writeLongHeader() - function _writeLongHeader($p_filename) - { - $v_size = sprintf("%11s ", DecOct(strlen($p_filename))); - - $v_typeflag = 'L'; - - $v_linkname = ''; - - $v_magic = ''; - - $v_version = ''; - - $v_uname = ''; - - $v_gname = ''; - - $v_devmajor = ''; - - $v_devminor = ''; - - $v_prefix = ''; - - $v_binary_data_first = pack("a100a8a8a8a12a12", - '././@LongLink', 0, 0, 0, $v_size, 0); - $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12", - $v_typeflag, $v_linkname, $v_magic, - $v_version, $v_uname, $v_gname, - $v_devmajor, $v_devminor, $v_prefix, ''); - - // ----- Calculate the checksum - $v_checksum = 0; - // ..... First part of the header - for ($i=0; $i<148; $i++) - $v_checksum += ord(substr($v_binary_data_first,$i,1)); - // ..... Ignore the checksum value and replace it by ' ' (space) - for ($i=148; $i<156; $i++) - $v_checksum += ord(' '); - // ..... Last part of the header - for ($i=156, $j=0; $i<512; $i++, $j++) - $v_checksum += ord(substr($v_binary_data_last,$j,1)); - - // ----- Write the first 148 bytes of the header in the archive - $this->_writeBlock($v_binary_data_first, 148); - - // ----- Write the calculated checksum - $v_checksum = sprintf("%06s ", DecOct($v_checksum)); - $v_binary_data = pack("a8", $v_checksum); - $this->_writeBlock($v_binary_data, 8); - - // ----- Write the last 356 bytes of the header in the archive - $this->_writeBlock($v_binary_data_last, 356); - - // ----- Write the filename as content of the block - $i=0; - while (($v_buffer = substr($p_filename, (($i++)*512), 512)) != '') { - $v_binary_data = pack("a512", "$v_buffer"); - $this->_writeBlock($v_binary_data); - } - - return true; - } - // }}} - - // {{{ _readHeader() - function _readHeader($v_binary_data, &$v_header) - { - if (strlen($v_binary_data)==0) { - $v_header['filename'] = ''; - return true; - } - - if (strlen($v_binary_data) != 512) { - $v_header['filename'] = ''; - $this->_error('Invalid block size : '.strlen($v_binary_data)); - return false; - } - - if (!is_array($v_header)) { - $v_header = array(); - } - // ----- Calculate the checksum - $v_checksum = 0; - // ..... First part of the header - for ($i=0; $i<148; $i++) - $v_checksum+=ord(substr($v_binary_data,$i,1)); - // ..... Ignore the checksum value and replace it by ' ' (space) - for ($i=148; $i<156; $i++) - $v_checksum += ord(' '); - // ..... Last part of the header - for ($i=156; $i<512; $i++) - $v_checksum+=ord(substr($v_binary_data,$i,1)); - - $v_data = unpack("a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/" - ."a8checksum/a1typeflag/a100link/a6magic/a2version/" - ."a32uname/a32gname/a8devmajor/a8devminor", - $v_binary_data); - - // ----- Extract the checksum - $v_header['checksum'] = OctDec(trim($v_data['checksum'])); - if ($v_header['checksum'] != $v_checksum) { - $v_header['filename'] = ''; - - // ----- Look for last block (empty block) - if (($v_checksum == 256) && ($v_header['checksum'] == 0)) - return true; - - $this->_error('Invalid checksum for file "'.$v_data['filename'] - .'" : '.$v_checksum.' calculated, ' - .$v_header['checksum'].' expected'); - return false; - } - - // ----- Extract the properties - $v_header['filename'] = $v_data['filename']; - if ($this->_maliciousFilename($v_header['filename'])) { - $this->_error('Malicious .tar detected, file "' . $v_header['filename'] . - '" will not install in desired directory tree'); - return false; - } - $v_header['mode'] = OctDec(trim($v_data['mode'])); - $v_header['uid'] = OctDec(trim($v_data['uid'])); - $v_header['gid'] = OctDec(trim($v_data['gid'])); - $v_header['size'] = OctDec(trim($v_data['size'])); - $v_header['mtime'] = OctDec(trim($v_data['mtime'])); - if (($v_header['typeflag'] = $v_data['typeflag']) == "5") { - $v_header['size'] = 0; - } - $v_header['link'] = trim($v_data['link']); - /* ----- All these fields are removed form the header because - they do not carry interesting info - $v_header[magic] = trim($v_data[magic]); - $v_header[version] = trim($v_data[version]); - $v_header[uname] = trim($v_data[uname]); - $v_header[gname] = trim($v_data[gname]); - $v_header[devmajor] = trim($v_data[devmajor]); - $v_header[devminor] = trim($v_data[devminor]); - */ - - return true; - } - // }}} - - // {{{ _maliciousFilename() - /** - * Detect and report a malicious file name - * - * @param string $file - * @return bool - * @access private - */ - function _maliciousFilename($file) - { - if (strpos($file, '/../') !== false) { - return true; - } - if (strpos($file, '../') === 0) { - return true; - } - return false; - } - // }}} - - // {{{ _readLongHeader() - function _readLongHeader(&$v_header) - { - $v_filename = ''; - $n = floor($v_header['size']/512); - for ($i=0; $i<$n; $i++) { - $v_content = $this->_readBlock(); - $v_filename .= $v_content; - } - if (($v_header['size'] % 512) != 0) { - $v_content = $this->_readBlock(); - $v_filename .= trim($v_content); - } - - // ----- Read the next header - $v_binary_data = $this->_readBlock(); - - if (!$this->_readHeader($v_binary_data, $v_header)) - return false; - - $v_filename = trim($v_filename); - $v_header['filename'] = $v_filename; - if ($this->_maliciousFilename($v_filename)) { - $this->_error('Malicious .tar detected, file "' . $v_filename . - '" will not install in desired directory tree'); - return false; - } - - return true; - } - // }}} - - // {{{ _extractInString() - /** - * This method extract from the archive one file identified by $p_filename. - * The return value is a string with the file content, or NULL on error. - * @param string $p_filename The path of the file to extract in a string. - * @return a string with the file content or NULL. - * @access private - */ - function _extractInString($p_filename) - { - $v_result_str = ""; - - While (strlen($v_binary_data = $this->_readBlock()) != 0) - { - if (!$this->_readHeader($v_binary_data, $v_header)) - return NULL; - - if ($v_header['filename'] == '') - continue; - - // ----- Look for long filename - if ($v_header['typeflag'] == 'L') { - if (!$this->_readLongHeader($v_header)) - return NULL; - } - - if ($v_header['filename'] == $p_filename) { - if ($v_header['typeflag'] == "5") { - $this->_error('Unable to extract in string a directory ' - .'entry {'.$v_header['filename'].'}'); - return NULL; - } else { - $n = floor($v_header['size']/512); - for ($i=0; $i<$n; $i++) { - $v_result_str .= $this->_readBlock(); - } - if (($v_header['size'] % 512) != 0) { - $v_content = $this->_readBlock(); - $v_result_str .= substr($v_content, 0, - ($v_header['size'] % 512)); - } - return $v_result_str; - } - } else { - $this->_jumpBlock(ceil(($v_header['size']/512))); - } - } - - return NULL; - } - // }}} - - // {{{ _extractList() - function _extractList($p_path, &$p_list_detail, $p_mode, - $p_file_list, $p_remove_path) - { - $v_result=true; - $v_nb = 0; - $v_extract_all = true; - $v_listing = false; - - $p_path = $this->_translateWinPath($p_path, false); - if ($p_path == '' || (substr($p_path, 0, 1) != '/' - && substr($p_path, 0, 3) != "../" && !strpos($p_path, ':'))) { - $p_path = "./".$p_path; - } - $p_remove_path = $this->_translateWinPath($p_remove_path); - - // ----- Look for path to remove format (should end by /) - if (($p_remove_path != '') && (substr($p_remove_path, -1) != '/')) - $p_remove_path .= '/'; - $p_remove_path_size = strlen($p_remove_path); - - switch ($p_mode) { - case "complete" : - $v_extract_all = TRUE; - $v_listing = FALSE; - break; - case "partial" : - $v_extract_all = FALSE; - $v_listing = FALSE; - break; - case "list" : - $v_extract_all = FALSE; - $v_listing = TRUE; - break; - default : - $this->_error('Invalid extract mode ('.$p_mode.')'); - return false; - } - - clearstatcache(); - - while (strlen($v_binary_data = $this->_readBlock()) != 0) - { - $v_extract_file = FALSE; - $v_extraction_stopped = 0; - - if (!$this->_readHeader($v_binary_data, $v_header)) - return false; - - if ($v_header['filename'] == '') { - continue; - } - - // ----- Look for long filename - if ($v_header['typeflag'] == 'L') { - if (!$this->_readLongHeader($v_header)) - return false; - } - - if ((!$v_extract_all) && (is_array($p_file_list))) { - // ----- By default no unzip if the file is not found - $v_extract_file = false; - - for ($i=0; $i strlen($p_file_list[$i])) - && (substr($v_header['filename'], 0, strlen($p_file_list[$i])) - == $p_file_list[$i])) { - $v_extract_file = TRUE; - break; - } - } - - // ----- It is a file, so compare the file names - elseif ($p_file_list[$i] == $v_header['filename']) { - $v_extract_file = TRUE; - break; - } - } - } else { - $v_extract_file = TRUE; - } - - // ----- Look if this file need to be extracted - if (($v_extract_file) && (!$v_listing)) - { - if (($p_remove_path != '') - && (substr($v_header['filename'], 0, $p_remove_path_size) - == $p_remove_path)) - $v_header['filename'] = substr($v_header['filename'], - $p_remove_path_size); - if (($p_path != './') && ($p_path != '/')) { - while (substr($p_path, -1) == '/') - $p_path = substr($p_path, 0, strlen($p_path)-1); - - if (substr($v_header['filename'], 0, 1) == '/') - $v_header['filename'] = $p_path.$v_header['filename']; - else - $v_header['filename'] = $p_path.'/'.$v_header['filename']; - } - if (file_exists($v_header['filename'])) { - if ( (@is_dir($v_header['filename'])) - && ($v_header['typeflag'] == '')) { - $this->_error('File '.$v_header['filename'] - .' already exists as a directory'); - return false; - } - if ( ($this->_isArchive($v_header['filename'])) - && ($v_header['typeflag'] == "5")) { - $this->_error('Directory '.$v_header['filename'] - .' already exists as a file'); - return false; - } - if (!is_writeable($v_header['filename'])) { - $this->_error('File '.$v_header['filename'] - .' already exists and is write protected'); - return false; - } - if (filemtime($v_header['filename']) > $v_header['mtime']) { - // To be completed : An error or silent no replace ? - } - } - - // ----- Check the directory availability and create it if necessary - elseif (($v_result - = $this->_dirCheck(($v_header['typeflag'] == "5" - ?$v_header['filename'] - :dirname($v_header['filename'])))) != 1) { - $this->_error('Unable to create path for '.$v_header['filename']); - return false; - } - - if ($v_extract_file) { - if ($v_header['typeflag'] == "5") { - if (!@file_exists($v_header['filename'])) { - if (!@mkdir($v_header['filename'], 0777)) { - $this->_error('Unable to create directory {' - .$v_header['filename'].'}'); - return false; - } - } - } elseif ($v_header['typeflag'] == "2") { - if (@file_exists($v_header['filename'])) { - @unlink($v_header['filename']); - } - if (!@symlink($v_header['link'], $v_header['filename'])) { - $this->_error('Unable to extract symbolic link {' - .$v_header['filename'].'}'); - return false; - } - } else { - if (($v_dest_file = @fopen($v_header['filename'], "wb")) == 0) { - $this->_error('Error while opening {'.$v_header['filename'] - .'} in write binary mode'); - return false; - } else { - $n = floor($v_header['size']/512); - for ($i=0; $i<$n; $i++) { - $v_content = $this->_readBlock(); - fwrite($v_dest_file, $v_content, 512); - } - if (($v_header['size'] % 512) != 0) { - $v_content = $this->_readBlock(); - fwrite($v_dest_file, $v_content, ($v_header['size'] % 512)); - } - - @fclose($v_dest_file); - - // ----- Change the file mode, mtime - @touch($v_header['filename'], $v_header['mtime']); - if ($v_header['mode'] & 0111) { - // make file executable, obey umask - $mode = fileperms($v_header['filename']) | (~umask() & 0111); - @chmod($v_header['filename'], $mode); - } - } - - // ----- Check the file size - clearstatcache(); - if (filesize($v_header['filename']) != $v_header['size']) { - $this->_error('Extracted file '.$v_header['filename'] - .' does not have the correct file size \'' - .filesize($v_header['filename']) - .'\' ('.$v_header['size'] - .' expected). Archive may be corrupted.'); - return false; - } - } - } else { - $this->_jumpBlock(ceil(($v_header['size']/512))); - } - } else { - $this->_jumpBlock(ceil(($v_header['size']/512))); - } - - /* TBC : Seems to be unused ... - if ($this->_compress) - $v_end_of_file = @gzeof($this->_file); - else - $v_end_of_file = @feof($this->_file); - */ - - if ($v_listing || $v_extract_file || $v_extraction_stopped) { - // ----- Log extracted files - if (($v_file_dir = dirname($v_header['filename'])) - == $v_header['filename']) - $v_file_dir = ''; - if ((substr($v_header['filename'], 0, 1) == '/') && ($v_file_dir == '')) - $v_file_dir = '/'; - - $p_list_detail[$v_nb++] = $v_header; - if (is_array($p_file_list) && (count($p_list_detail) == count($p_file_list))) { - return true; - } - } - } - - return true; - } - // }}} - - // {{{ _openAppend() - function _openAppend() - { - if (filesize($this->_tarname) == 0) - return $this->_openWrite(); - - if ($this->_compress) { - $this->_close(); - - if (!@rename($this->_tarname, $this->_tarname.".tmp")) { - $this->_error('Error while renaming \''.$this->_tarname - .'\' to temporary file \''.$this->_tarname - .'.tmp\''); - return false; - } - - if ($this->_compress_type == 'gz') - $v_temp_tar = @gzopen($this->_tarname.".tmp", "rb"); - elseif ($this->_compress_type == 'bz2') - $v_temp_tar = @bzopen($this->_tarname.".tmp", "r"); - - if ($v_temp_tar == 0) { - $this->_error('Unable to open file \''.$this->_tarname - .'.tmp\' in binary read mode'); - @rename($this->_tarname.".tmp", $this->_tarname); - return false; - } - - if (!$this->_openWrite()) { - @rename($this->_tarname.".tmp", $this->_tarname); - return false; - } - - if ($this->_compress_type == 'gz') { - while (!@gzeof($v_temp_tar)) { - $v_buffer = @gzread($v_temp_tar, 512); - if ($v_buffer == ARCHIVE_TAR_END_BLOCK) { - // do not copy end blocks, we will re-make them - // after appending - continue; - } - $v_binary_data = pack("a512", $v_buffer); - $this->_writeBlock($v_binary_data); - } - - @gzclose($v_temp_tar); - } - elseif ($this->_compress_type == 'bz2') { - while (strlen($v_buffer = @bzread($v_temp_tar, 512)) > 0) { - if ($v_buffer == ARCHIVE_TAR_END_BLOCK) { - continue; - } - $v_binary_data = pack("a512", $v_buffer); - $this->_writeBlock($v_binary_data); - } - - @bzclose($v_temp_tar); - } - - if (!@unlink($this->_tarname.".tmp")) { - $this->_error('Error while deleting temporary file \'' - .$this->_tarname.'.tmp\''); - } - - } else { - // ----- For not compressed tar, just add files before the last - // one or two 512 bytes block - if (!$this->_openReadWrite()) - return false; - - clearstatcache(); - $v_size = filesize($this->_tarname); - - // We might have zero, one or two end blocks. - // The standard is two, but we should try to handle - // other cases. - fseek($this->_file, $v_size - 1024); - if (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) { - fseek($this->_file, $v_size - 1024); - } - elseif (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) { - fseek($this->_file, $v_size - 512); - } - } - - return true; - } - // }}} - - // {{{ _append() - function _append($p_filelist, $p_add_dir='', $p_remove_dir='') - { - if (!$this->_openAppend()) - return false; - - if ($this->_addList($p_filelist, $p_add_dir, $p_remove_dir)) - $this->_writeFooter(); - - $this->_close(); - - return true; - } - // }}} - - // {{{ _dirCheck() - - /** - * Check if a directory exists and create it (including parent - * dirs) if not. - * - * @param string $p_dir directory to check - * - * @return bool TRUE if the directory exists or was created - */ - function _dirCheck($p_dir) - { - clearstatcache(); - if ((@is_dir($p_dir)) || ($p_dir == '')) - return true; - - $p_parent_dir = dirname($p_dir); - - if (($p_parent_dir != $p_dir) && - ($p_parent_dir != '') && - (!$this->_dirCheck($p_parent_dir))) - return false; - - if (!@mkdir($p_dir, 0777)) { - $this->_error("Unable to create directory '$p_dir'"); - return false; - } - - return true; - } - - // }}} - - // {{{ _pathReduction() - - /** - * Compress path by changing for example "/dir/foo/../bar" to "/dir/bar", - * rand emove double slashes. - * - * @param string $p_dir path to reduce - * - * @return string reduced path - * - * @access private - * - */ - function _pathReduction($p_dir) - { - $v_result = ''; - - // ----- Look for not empty path - if ($p_dir != '') { - // ----- Explode path by directory names - $v_list = explode('/', $p_dir); - - // ----- Study directories from last to first - for ($i=sizeof($v_list)-1; $i>=0; $i--) { - // ----- Look for current path - if ($v_list[$i] == ".") { - // ----- Ignore this directory - // Should be the first $i=0, but no check is done - } - else if ($v_list[$i] == "..") { - // ----- Ignore it and ignore the $i-1 - $i--; - } - else if ( ($v_list[$i] == '') - && ($i!=(sizeof($v_list)-1)) - && ($i!=0)) { - // ----- Ignore only the double '//' in path, - // but not the first and last / - } else { - $v_result = $v_list[$i].($i!=(sizeof($v_list)-1)?'/' - .$v_result:''); - } - } - } - $v_result = strtr($v_result, '\\', '/'); - return $v_result; - } - - // }}} - - // {{{ _translateWinPath() - function _translateWinPath($p_path, $p_remove_disk_letter=true) - { - if (defined('OS_WINDOWS') && OS_WINDOWS) { - // ----- Look for potential disk letter - if ( ($p_remove_disk_letter) - && (($v_position = strpos($p_path, ':')) != false)) { - $p_path = substr($p_path, $v_position+1); - } - // ----- Change potential windows directory separator - if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) { - $p_path = strtr($p_path, '\\', '/'); - } - } - return $p_path; - } - // }}} - -} -?> diff --git a/pear/Auth.php b/pear/Auth.php deleted file mode 100644 index f34b8f6..0000000 --- a/pear/Auth.php +++ /dev/null @@ -1,1330 +0,0 @@ - - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version CVS: $Id$ - * @link http://pear.php.net/package/Auth - */ - -/** - * Returned if session exceeds idle time - */ -define('AUTH_IDLED', -1); -/** - * Returned if session has expired - */ -define('AUTH_EXPIRED', -2); -/** - * Returned if container is unable to authenticate user/password pair - */ -define('AUTH_WRONG_LOGIN', -3); -/** - * Returned if a container method is not supported. - */ -define('AUTH_METHOD_NOT_SUPPORTED', -4); -/** - * Returned if new Advanced security system detects a breach - */ -define('AUTH_SECURITY_BREACH', -5); -/** - * Returned if checkAuthCallback says session should not continue. - */ -define('AUTH_CALLBACK_ABORT', -6); - -/** - * Auth Log level - INFO - */ -define('AUTH_LOG_INFO', 6); -/** - * Auth Log level - DEBUG - */ -define('AUTH_LOG_DEBUG', 7); - -/** - * Auth Advanced Security - IP Checks - */ -define('AUTH_ADV_IPCHECK', 1); -/** - * Auth Advanced Security - User Agent Checks - */ -define('AUTH_ADV_USERAGENT', 2); -/** - * Auth Advanced Security - Challenge Response - */ -define('AUTH_ADV_CHALLENGE', 3); - - -/** - * PEAR::Auth - * - * The PEAR::Auth class provides methods for creating an - * authentication system using PHP. - * - * @category Authentication - * @package Auth - * @author Martin Jansen - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version Release: @package_version@ File: $Revision$ - * @link http://pear.php.net/package/Auth - */ -class Auth { - - // {{{ properties - - /** - * Auth lifetime in seconds - * - * If this variable is set to 0, auth never expires - * - * @var integer - * @see setExpire(), checkAuth() - */ - private $expire = 0; - - /** - * Has the auth session expired? - * - * @var bool - * @see checkAuth() - */ - private $expired = false; - - /** - * Maximum idletime in seconds - * - * The difference to $expire is, that the idletime gets - * refreshed each time checkAuth() is called. If this - * variable is set to 0, idletime is never checked. - * - * @var integer - * @see setIdle(), checkAuth() - */ - private $idle = 0; - - /** - * Is the maximum idletime over? - * - * @var boolean - * @see checkAuth() - */ - private $idled = false; - - /** - * Storage object - * - * @var object - * @see Auth(), validateLogin() - */ - private $storage = ''; - - /** - * User-defined function that creates the login screen - * - * @var string - */ - private $loginFunction = ''; - - /** - * Should the login form be displayed - * - * @var bool - * @see setShowlogin() - */ - private $showLogin = true; - - /** - * Is Login Allowed from this page - * - * @var bool - * @see setAllowLogin - */ - private $allowLogin = true; - - /** - * Current authentication status - * - * @var string - */ - private $status = ''; - - /** - * Username - * - * @var string - */ - private $username = ''; - - /** - * Password - * - * @var string - */ - private $password = ''; - - /** - * checkAuth callback function name - * - * @var string - * @see setCheckAuthCallback() - */ - private $checkAuthCallback = ''; - - /** - * Login callback function name - * - * @var string - * @see setLoginCallback() - */ - private $loginCallback = ''; - - /** - * Failed Login callback function name - * - * @var string - * @see setFailedLoginCallback() - */ - private $loginFailedCallback = ''; - - /** - * Logout callback function name - * - * @var string - * @see setLogoutCallback() - */ - private $logoutCallback = ''; - - /** - * Auth session-array name - * - * @var string - */ - private $_sessionName = '_authsession'; - - /** - * Package Version - * - * @var string - */ - private $version = "@version@"; - - /** - * Flag to use advanced security - * When set extra checks will be made to see if the - * user's IP or useragent have changed across requests. - * Turned off by default to preserve BC. - * - * @var mixed Boolean to turn all advanced security options on or off - * Array containing named values turning specific advanced - * security features on or off individually - * array( - * AUTH_ADV_IPCHECK => true, - * AUTH_ADV_USERAGENT => true, - * AUTH_ADV_CHALLENGE => true, - * ); - */ - private $advancedsecurity = false; - - /** - * Username key in POST array - * - * @var string - */ - private $_postUsername = 'username'; - - /** - * Password key in POST array - * - * @var string - */ - private $_postPassword = 'password'; - - /** - * Holds a reference to the session auth variable - * @var array - */ - private $session; - - /** - * Holds a reference to the global server variable - * @var array - */ - private $server; - - /** - * Holds a reference to the global post variable - * @var array - */ - private $post; - - /** - * Holds a reference to the global cookie variable - * @var array - */ - private $cookie; - - /** - * A hash to hold various superglobals as reference - * @var array - */ - private $authdata; - - /** - * How many times has checkAuth been called - * @var int - */ - private $authChecks = 0; - - /** - * PEAR::Log object - * - * @var object Log - */ - private $logger = null; - - /** - * Whether to enable logging of behaviour - * - * @var boolean - */ - private $enableLogging = false; - - /** - * Whether to regenerate session id everytime start is called - * - * @var boolean - */ - private $regenerateSessionId = false; - - // }}} - // {{{ Auth() [constructor] - - /** - * Constructor - * - * Set up the storage driver. - * - * @param string Type of the storage driver - * @param mixed Additional options for the storage driver - * (example: if you are using DB as the storage - * driver, you have to pass the dsn string here) - * - * @param string Name of the function that creates the login form - * @param boolean Should the login form be displayed if necessary? - * @return void - */ - function Auth($storageDriver, $options = '', $loginFunction = '', $showLogin = true) - { - $this->applyAuthOptions($options); - - // Start the session suppress error if already started - if(!session_id()){ - @session_start(); - if(!session_id()) { - // Throw error - include_once 'PEAR.php'; - PEAR::throwError('Session could not be started by Auth, ' - .'possibly headers are already sent, try putting ' - .'ob_start in the beginning of your script'); - } - } - - // Make Sure Auth session variable is there - if(!isset($_SESSION[$this->_sessionName])) { - $_SESSION[$this->_sessionName] = array(); - } - - // Assign Some globals to internal references, this will replace _importGlobalVariable - $this->session =& $_SESSION[$this->_sessionName]; - $this->server =& $_SERVER; - $this->post =& $_POST; - $this->cookie =& $_COOKIE; - - if ($loginFunction != '' && is_callable($loginFunction)) { - $this->loginFunction = $loginFunction; - } - - if (is_bool($showLogin)) { - $this->showLogin = $showLogin; - } - - if (is_object($storageDriver)) { - $this->storage =& $storageDriver; - // Pass a reference to auth to the container, ugly but works - // this is used by the DB container to use method setAuthData not staticaly. - $this->storage->_auth_obj =& $this; - } else { - // $this->storage = $this->_factory($storageDriver, $options); - // - $this->storage_driver = $storageDriver; - $this->storage_options =& $options; - } - } - - // }}} - // {{{ applyAuthOptions() - - /** - * Set the Auth options - * - * Some options which are Auth specific will be applied - * the rest will be left for usage by the container - * - * @param array An array of Auth options - * @return array The options which were not applied - */ - private function &applyAuthOptions(&$options) - { - if(is_array($options)){ - if (!empty($options['sessionName'])) { - $this->_sessionName = $options['sessionName']; - unset($options['sessionName']); - } - if (isset($options['allowLogin'])) { - $this->allowLogin = $options['allowLogin']; - unset($options['allowLogin']); - } - if (!empty($options['postUsername'])) { - $this->_postUsername = $options['postUsername']; - unset($options['postUsername']); - } - if (!empty($options['postPassword'])) { - $this->_postPassword = $options['postPassword']; - unset($options['postPassword']); - } - if (isset($options['advancedsecurity'])) { - $this->advancedsecurity = $options['advancedsecurity']; - unset($options['advancedsecurity']); - } - if (isset($options['enableLogging'])) { - $this->enableLogging = $options['enableLogging']; - unset($options['enableLogging']); - } - if (isset($options['regenerateSessionId']) && is_bool($options['regenerateSessionId'])) { - $this->regenerateSessionId = $options['regenerateSessionId']; - } - } - return($options); - } - - // }}} - // {{{ _loadStorage() - - /** - * Load Storage Driver if not already loaded - * - * Suspend storage instantiation to make Auth lighter to use - * for calls which do not require login - * - * @return bool True if the conainer is loaded, false if the container - * is already loaded - */ - private function _loadStorage() - { - if(!is_object($this->storage)) { - $this->storage =& $this->_factory($this->storage_driver, - $this->storage_options); - $this->storage->_auth_obj =& $this; - $this->log('Loaded storage container ('.$this->storage_driver.')', AUTH_LOG_DEBUG); - return(true); - } - return(false); - } - - // }}} - // {{{ _factory() - - /** - * Return a storage driver based on $driver and $options - * - * @static - * @param string $driver Type of storage class to return - * @param string $options Optional parameters for the storage class - * @return object Object Storage object - */ - private function &_factory($driver, $options = '') - { - $storage_class = 'Auth_Container_' . $driver; - include_once 'Auth/Container/' . $driver . '.php'; - $obj = new $storage_class($options); - return $obj; - } - - // }}} - // {{{ assignData() - - /** - * Assign data from login form to internal values - * - * This function takes the values for username and password - * from $HTTP_POST_VARS/$_POST and assigns them to internal variables. - * If you wish to use another source apart from $HTTP_POST_VARS/$_POST, - * you have to derive this function. - * - * @global $HTTP_POST_VARS, $_POST - * @see Auth - * @return void - */ - private function assignData() - { - $this->log('Auth::assignData() called.', AUTH_LOG_DEBUG); - - if ( isset($this->post[$this->_postUsername]) - && $this->post[$this->_postUsername] != '') { - $this->username = (get_magic_quotes_gpc() == 1 - ? stripslashes($this->post[$this->_postUsername]) - : $this->post[$this->_postUsername]); - } - if ( isset($this->post[$this->_postPassword]) - && $this->post[$this->_postPassword] != '') { - $this->password = (get_magic_quotes_gpc() == 1 - ? stripslashes($this->post[$this->_postPassword]) - : $this->post[$this->_postPassword] ); - } - } - - // }}} - // {{{ start() - - /** - * Start new auth session - * - * @return void - */ - public function start() - { - $this->log('Auth::start() called.', AUTH_LOG_DEBUG); - - // #10729 - Regenerate session id here if we are generating it on every - // page load. - if ($this->regenerateSessionId) { - session_regenerate_id(true); - } - - $this->assignData(); - if (!$this->checkAuth() && $this->allowLogin) { - $this->login(); - } - } - - // }}} - // {{{ login() - - /** - * Login function - * - * @return void - */ - private function login() - { - $this->log('Auth::login() called.', AUTH_LOG_DEBUG); - - $login_ok = false; - $this->_loadStorage(); - - // Check if using challenge response - (isset($this->post['authsecret']) && $this->post['authsecret'] == 1) - ? $usingChap = true - : $usingChap = false; - - - // When the user has already entered a username, we have to validate it. - if (!empty($this->username)) { - if (true === $this->storage->fetchData($this->username, $this->password, $usingChap)) { - $this->session['challengekey'] = md5($this->username.$this->password); - $login_ok = true; - $this->log('Successful login.', AUTH_LOG_INFO); - } - } - - if (!empty($this->username) && $login_ok) { - $this->setAuth($this->username); - if (is_callable($this->loginCallback)) { - $this->log('Calling loginCallback ('.$this->loginCallback.').', AUTH_LOG_DEBUG); - call_user_func_array($this->loginCallback, array($this->username, &$this)); - } - } - - // If the login failed or the user entered no username, - // output the login screen again. - if (!empty($this->username) && !$login_ok) { - $this->log('Incorrect login.', AUTH_LOG_INFO); - $this->status = AUTH_WRONG_LOGIN; - if (is_callable($this->loginFailedCallback)) { - $this->log('Calling loginFailedCallback ('.$this->loginFailedCallback.').', AUTH_LOG_DEBUG); - call_user_func_array($this->loginFailedCallback, array($this->username, &$this)); - } - } - - if ((empty($this->username) || !$login_ok) && $this->showLogin) { - $this->log('Rendering Login Form.', AUTH_LOG_INFO); - if (is_callable($this->loginFunction)) { - $this->log('Calling loginFunction ('.$this->loginFunction.').', AUTH_LOG_DEBUG); - call_user_func_array($this->loginFunction, array($this->username, $this->status, &$this)); - } else { - // BC fix Auth used to use drawLogin for this - // call is sub classes implement this - if (is_callable(array($this, 'drawLogin'))) { - $this->log('Calling Auth::drawLogin()', AUTH_LOG_DEBUG); - return $this->drawLogin($this->username, $this); - } - - $this->log('Using default Auth_Frontend_Html', AUTH_LOG_DEBUG); - - // New Login form - include_once 'Auth/Frontend/Html.php'; - return Auth_Frontend_Html::render($this, $this->username); - } - } else { - return; - } - } - - // }}} - // {{{ setExpire() - - /** - * Set the maximum expire time - * - * @param integer time in seconds - * @param bool add time to current expire time or not - * @return void - */ - public function setExpire($time, $add = false) - { - $add ? $this->expire += $time : $this->expire = $time; - } - - // }}} - // {{{ setIdle() - - /** - * Set the maximum idle time - * - * @param integer time in seconds - * @param bool add time to current maximum idle time or not - * @return void - */ - public function setIdle($time, $add = false) - { - $add ? $this->idle += $time : $this->idle = $time; - } - - // }}} - // {{{ setSessionName() - - /** - * Set name of the session to a customized value. - * - * If you are using multiple instances of PEAR::Auth - * on the same domain, you can change the name of - * session per application via this function. - * This will chnage the name of the session variable - * auth uses to store it's data in the session - * - * @param string New name for the session - * @return void - */ - public function setSessionName($name = 'session') - { - $this->_sessionName = '_auth_'.$name; - // Make Sure Auth session variable is there - if(!isset($_SESSION[$this->_sessionName])) { - $_SESSION[$this->_sessionName] = array(); - } - $this->session =& $_SESSION[$this->_sessionName]; - } - - // }}} - // {{{ setShowLogin() - - /** - * Should the login form be displayed if necessary? - * - * @param bool show login form or not - * @return void - */ - public function setShowLogin($showLogin = true) - { - $this->showLogin = $showLogin; - } - - // }}} - // {{{ setAllowLogin() - - /** - * Is Login Allowed from this page? - * - * @param bool allow login from this page or not - * @return void - */ - public function setAllowLogin($allowLogin = true) - { - $this->allowLogin = $allowLogin; - } - - // }}} - // {{{ setCheckAuthCallback() - - /** - * Register a callback function to be called whenever the validity of the login is checked - * The function will receive two parameters, the username and a reference to the auth object. - * - * @param string callback function name - * @return void - * @since Method available since Release 1.4.3 - */ - public function setCheckAuthCallback($checkAuthCallback) - { - $this->checkAuthCallback = $checkAuthCallback; - } - - // }}} - // {{{ setLoginCallback() - - /** - * Register a callback function to be called on user login. - * The function will receive two parameters, the username and a reference to the auth object. - * - * @param string callback function name - * @return void - * @see setLogoutCallback() - */ - public function setLoginCallback($loginCallback) - { - $this->loginCallback = $loginCallback; - } - - // }}} - // {{{ setFailedLoginCallback() - - /** - * Register a callback function to be called on failed user login. - * The function will receive two parameters, the username and a reference to the auth object. - * - * @param string callback function name - * @return void - */ - public function setFailedLoginCallback($loginFailedCallback) - { - $this->loginFailedCallback = $loginFailedCallback; - } - - // }}} - // {{{ setLogoutCallback() - - /** - * Register a callback function to be called on user logout. - * The function will receive three parameters, the username and a reference to the auth object. - * - * @param string callback function name - * @return void - * @see setLoginCallback() - */ - public function setLogoutCallback($logoutCallback) - { - $this->logoutCallback = $logoutCallback; - } - - // }}} - // {{{ setAuthData() - - /** - * Register additional information that is to be stored - * in the session. - * - * @param string Name of the data field - * @param mixed Value of the data field - * @param boolean Should existing data be overwritten? (default - * is true) - * @return void - */ - public function setAuthData($name, $value, $overwrite = true) - { - if (!empty($this->session['data'][$name]) && $overwrite == false) { - return; - } - $this->session['data'][$name] = $value; - } - - // }}} - // {{{ getAuthData() - - /** - * Get additional information that is stored in the session. - * - * If no value for the first parameter is passed, the method will - * return all data that is currently stored. - * - * @param string Name of the data field - * @return mixed Value of the data field. - */ - public function getAuthData($name = null) - { - if (!isset($this->session['data'])) { - return null; - } - if(!isset($name)) { - return $this->session['data']; - } - if (isset($name) && isset($this->session['data'][$name])) { - return $this->session['data'][$name]; - } - return null; - } - - // }}} - // {{{ setAuth() - - /** - * Register variable in a session telling that the user - * has logged in successfully - * - * @param string Username - * @return void - */ - public function setAuth($username) - { - $this->log('Auth::setAuth() called.', AUTH_LOG_DEBUG); - - // #10729 - Regenerate session id here only if generating at login only - // Don't do it if we are regenerating on every request so we don't - // regenerate it twice in one request. - if (!$this->regenerateSessionId) { - // #2021 - Change the session id to avoid session fixation attacks php 4.3.3 > - session_regenerate_id(true); - } - - if (!isset($this->session) || !is_array($this->session)) { - $this->session = array(); - } - - if (!isset($this->session['data'])) { - $this->session['data'] = array(); - } - - $this->session['sessionip'] = isset($this->server['REMOTE_ADDR']) - ? $this->server['REMOTE_ADDR'] - : ''; - $this->session['sessionuseragent'] = isset($this->server['HTTP_USER_AGENT']) - ? $this->server['HTTP_USER_AGENT'] - : ''; - $this->session['sessionforwardedfor'] = isset($this->server['HTTP_X_FORWARDED_FOR']) - ? $this->server['HTTP_X_FORWARDED_FOR'] - : ''; - - // This should be set by the container to something more safe - // Like md5(passwd.microtime) - if(empty($this->session['challengekey'])) { - $this->session['challengekey'] = md5($username.microtime()); - } - - $this->session['challengecookie'] = md5($this->session['challengekey'].microtime()); - setcookie('authchallenge', $this->session['challengecookie'], 0, '/'); - - $this->session['registered'] = true; - $this->session['username'] = $username; - $this->session['timestamp'] = time(); - $this->session['idle'] = time(); - } - - // }}} - // {{{ setAdvancedSecurity() - - /** - * Enables advanced security checks - * - * Currently only ip change and useragent change - * are detected - * @todo Add challenge cookies - Create a cookie which changes every time - * and contains some challenge key which the server can verify with - * a session var cookie might need to be crypted (user pass) - * @param bool Enable or disable - * @return void - */ - public function setAdvancedSecurity($flag=true) - { - $this->advancedsecurity = $flag; - } - - // }}} - // {{{ checkAuth() - - /** - * Checks if there is a session with valid auth information. - * - * @return boolean Whether or not the user is authenticated. - */ - public function checkAuth() - { - $this->log('Auth::checkAuth() called.', AUTH_LOG_DEBUG); - $this->authChecks++; - if (isset($this->session)) { - // Check if authentication session is expired - if ( $this->expire > 0 - && isset($this->session['timestamp']) - && ($this->session['timestamp'] + $this->expire) < time()) { - $this->log('Session Expired', AUTH_LOG_INFO); - $this->expired = true; - $this->status = AUTH_EXPIRED; - $this->logout(); - return false; - } - - // Check if maximum idle time is reached - if ( $this->idle > 0 - && isset($this->session['idle']) - && ($this->session['idle'] + $this->idle) < time()) { - $this->log('Session Idle Time Reached', AUTH_LOG_INFO); - $this->idled = true; - $this->status = AUTH_IDLED; - $this->logout(); - return false; - } - - if ( isset($this->session['registered']) - && isset($this->session['username']) - && $this->session['registered'] == true - && $this->session['username'] != '') { - Auth::updateIdle(); - - if ($this->_isAdvancedSecurityEnabled()) { - $this->log('Advanced Security Mode Enabled.', AUTH_LOG_DEBUG); - - // Only Generate the challenge once - if ( $this->authChecks == 1 - && $this->_isAdvancedSecurityEnabled(AUTH_ADV_CHALLENGE)) { - $this->log('Generating new Challenge Cookie.', AUTH_LOG_DEBUG); - $this->session['challengecookieold'] = $this->session['challengecookie']; - $this->session['challengecookie'] = md5($this->session['challengekey'].microtime()); - setcookie('authchallenge', $this->session['challengecookie'], 0, '/'); - } - - // Check for ip change - if ( $this->_isAdvancedSecurityEnabled(AUTH_ADV_IPCHECK) - && isset($this->server['REMOTE_ADDR']) - && $this->session['sessionip'] != $this->server['REMOTE_ADDR']) { - $this->log('Security Breach. Remote IP Address changed.', AUTH_LOG_INFO); - // Check if the IP of the user has changed, if so we - // assume a man in the middle attack and log him out - $this->expired = true; - $this->status = AUTH_SECURITY_BREACH; - $this->logout(); - return false; - } - - // Check for ip change (if connected via proxy) - if ( $this->_isAdvancedSecurityEnabled(AUTH_ADV_IPCHECK) - && isset($this->server['HTTP_X_FORWARDED_FOR']) - && $this->session['sessionforwardedfor'] != $this->server['HTTP_X_FORWARDED_FOR']) { - $this->log('Security Breach. Forwarded For IP Address changed.', AUTH_LOG_INFO); - // Check if the IP of the user connecting via proxy has - // changed, if so we assume a man in the middle attack - // and log him out. - $this->expired = true; - $this->status = AUTH_SECURITY_BREACH; - $this->logout(); - return false; - } - - // Check for useragent change - if ( $this->_isAdvancedSecurityEnabled(AUTH_ADV_USERAGENT) - && isset($this->server['HTTP_USER_AGENT']) - && $this->session['sessionuseragent'] != $this->server['HTTP_USER_AGENT']) { - $this->log('Security Breach. User Agent changed.', AUTH_LOG_INFO); - // Check if the User-Agent of the user has changed, if - // so we assume a man in the middle attack and log him out - $this->expired = true; - $this->status = AUTH_SECURITY_BREACH; - $this->logout(); - return false; - } - - // Check challenge cookie here, if challengecookieold is not set - // this is the first time and check is skipped - // TODO when user open two pages similtaneuly (open in new window,open - // in tab) auth breach is caused find out a way around that if possible - if ( $this->_isAdvancedSecurityEnabled(AUTH_ADV_CHALLENGE) - && isset($this->session['challengecookieold']) - && $this->session['challengecookieold'] != $this->cookie['authchallenge']) { - $this->log('Security Breach. Challenge Cookie mismatch.', AUTH_LOG_INFO); - $this->expired = true; - $this->status = AUTH_SECURITY_BREACH; - $this->logout(); - $this->login(); - return false; - } - } - - if (is_callable($this->checkAuthCallback)) { - $this->log('Calling checkAuthCallback ('.$this->checkAuthCallback.').', AUTH_LOG_DEBUG); - $checkCallback = call_user_func_array($this->checkAuthCallback, array($this->username, &$this)); - if ($checkCallback == false) { - $this->log('checkAuthCallback failed.', AUTH_LOG_INFO); - $this->expired = true; - $this->status = AUTH_CALLBACK_ABORT; - $this->logout(); - return false; - } - } - - $this->log('Session OK.', AUTH_LOG_INFO); - return true; - } - } else { - $this->log('Unable to locate session storage.', AUTH_LOG_DEBUG); - return false; - } - $this->log('No login session.', AUTH_LOG_DEBUG); - return false; - } - - // }}} - // {{{ staticCheckAuth() [static] - - /** - * Statically checks if there is a session with valid auth information. - * - * @access public - * @see checkAuth - * @return boolean Whether or not the user is authenticated. - */ - public static function staticCheckAuth($options = null) - { - static $staticAuth; - if(!isset($staticAuth)) { - $staticAuth = new Auth('null', $options); - } - $staticAuth->log('Auth::staticCheckAuth() called', AUTH_LOG_DEBUG); - return $staticAuth->checkAuth(); - } - - // }}} - // {{{ getAuth() - - /** - * Has the user been authenticated? - * - * Is there a valid login session. Previously this was different from - * checkAuth() but now it is just an alias. - * - * @return bool True if the user is logged in, otherwise false. - */ - public function getAuth() - { - $this->log('Auth::getAuth() called.', AUTH_LOG_DEBUG); - return $this->checkAuth(); - } - - // }}} - // {{{ logout() - - /** - * Logout function - * - * This function clears any auth tokens in the currently - * active session and executes the logout callback function, - * if any - * - * @return void - */ - public function logout() - { - $this->log('Auth::logout() called.', AUTH_LOG_DEBUG); - - if (is_callable($this->logoutCallback) && isset($this->session['username'])) { - $this->log('Calling logoutCallback ('.$this->logoutCallback.').', AUTH_LOG_DEBUG); - call_user_func_array($this->logoutCallback, array($this->session['username'], &$this)); - } - - $this->username = ''; - $this->password = ''; - - $this->session = null; - } - - // }}} - // {{{ updateIdle() - - /** - * Update the idletime - * - * @return void - */ - public function updateIdle() - { - $this->session['idle'] = time(); - } - - // }}} - // {{{ getUsername() - - /** - * Get the username - * - * @return string - */ - public function getUsername() - { - if (isset($this->session['username'])) { - return($this->session['username']); - } - return(''); - } - - // }}} - // {{{ getStatus() - - /** - * Get the current status - * - * @return string - */ - public function getStatus() - { - return $this->status; - } - - // }}} - // {{{ getPostUsernameField() - - /** - * Gets the post varible used for the username - * - * @return string - */ - public function getPostUsernameField() - { - return($this->_postUsername); - } - - // }}} - // {{{ getPostPasswordField() - - /** - * Gets the post varible used for the username - * - * @return string - */ - public function getPostPasswordField() - { - return($this->_postPassword); - } - - // }}} - // {{{ sessionValidThru() - - /** - * Returns the time up to the session is valid - * - * @return integer - */ - public function sessionValidThru() - { - if (!isset($this->session['idle'])) { - return 0; - } - if ($this->idle == 0) { - return 0; - } - return ($this->session['idle'] + $this->idle); - } - - // }}} - // {{{ listUsers() - - /** - * List all users that are currently available in the storage - * container - * - * @return array - */ - public function listUsers() - { - $this->log('Auth::listUsers() called.', AUTH_LOG_DEBUG); - $this->_loadStorage(); - return $this->storage->listUsers(); - } - - // }}} - // {{{ addUser() - - /** - * Add user to the storage container - * - * @param string Username - * @param string Password - * @param mixed Additional parameters - * @return mixed True on success, PEAR error object on error - * and AUTH_METHOD_NOT_SUPPORTED otherwise. - */ - public function addUser($username, $password, $additional = '') - { - $this->log('Auth::addUser() called.', AUTH_LOG_DEBUG); - $this->_loadStorage(); - return $this->storage->addUser($username, $password, $additional); - } - - // }}} - // {{{ removeUser() - - /** - * Remove user from the storage container - * - * @param string Username - * @return mixed True on success, PEAR error object on error - * and AUTH_METHOD_NOT_SUPPORTED otherwise. - */ - public function removeUser($username) - { - $this->log('Auth::removeUser() called.', AUTH_LOG_DEBUG); - $this->_loadStorage(); - return $this->storage->removeUser($username); - } - - // }}} - // {{{ changePassword() - - /** - * Change password for user in the storage container - * - * @param string Username - * @param string The new password - * @return mixed True on success, PEAR error object on error - * and AUTH_METHOD_NOT_SUPPORTED otherwise. - */ - public function changePassword($username, $password) - { - $this->log('Auth::changePassword() called', AUTH_LOG_DEBUG); - $this->_loadStorage(); - return $this->storage->changePassword($username, $password); - } - - // }}} - // {{{ log() - - /** - * Log a message from the Auth system - * - * @param string The message to log - * @param string The log level to log the message under. See the Log documentation for more info. - * @return boolean - */ - public function log($message, $level = AUTH_LOG_DEBUG) - { - if (!$this->enableLogging) return false; - - $this->_loadLogger(); - - $this->logger->log('AUTH: '.$message, $level); - } - - // }}} - // {{{ _loadLogger() - - /** - * Load Log object if not already loaded - * - * Suspend logger instantiation to make Auth lighter to use - * for calls which do not require logging - * - * @return bool True if the logger is loaded, false if the logger - * is already loaded - */ - private function _loadLogger() - { - if(is_null($this->logger)) { - if (!class_exists('Log')) { - include_once 'Log.php'; - } - $this->logger =& Log::singleton('null', - null, - 'auth['.getmypid().']', - array(), - AUTH_LOG_DEBUG); - return(true); - } - return(false); - } - - // }}} - // {{{ attachLogObserver() - - /** - * Attach an Observer to the Auth Log Source - * - * @param object Log_Observer A Log Observer instance - * @return boolean - */ - public function attachLogObserver(&$observer) { - - $this->_loadLogger(); - - return $this->logger->attach($observer); - - } - - // }}} - // {{{ _isAdvancedSecurityEnabled() - - /** - * Is advanced security enabled? - * - * Pass one of the Advanced Security constants as the first parameter - * to check if that advanced security check is enabled. - * - * @param integer - * @return boolean - */ - private function _isAdvancedSecurityEnabled($feature = null) { - - if (is_null($feature)) { - - if ($this->advancedsecurity === true) - return true; - - if ( is_array($this->advancedsecurity) - && in_array(true, $this->advancedsecurity, true)) - return true; - - return false; - - } else { - - if (is_array($this->advancedsecurity)) { - - if ( isset($this->advancedsecurity[$feature]) - && $this->advancedsecurity[$feature] == true) - return true; - - return false; - - } - - return (bool)$this->advancedsecurity; - - } - - } - - // }}} - -} -?> diff --git a/pear/Auth/Anonymous.php b/pear/Auth/Anonymous.php deleted file mode 100644 index ec29bd6..0000000 --- a/pear/Auth/Anonymous.php +++ /dev/null @@ -1,138 +0,0 @@ - - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version CVS: $Id: Anonymous.php 289651 2009-10-15 04:39:07Z aashley $ - * @link http://pear.php.net/package/Auth - * @since File available since Release 1.3.0 - */ - -/** - * Include Auth package - */ -require_once 'Auth.php'; - -/** - * Anonymous Authentication - * - * This class provides anonymous authentication if username and password - * were not supplied - * - * @category Authentication - * @package Auth - * @author Yavor Shahpasov - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version Release: @package_version@ File: $Revision: 289651 $ - * @link http://pear.php.net/package/Auth - * @since Class available since Release 1.3.0 - */ -class Auth_Anonymous extends Auth -{ - - // {{{ properties - - /** - * Whether to allow anonymous authentication - * - * @var boolean - */ - var $allow_anonymous = true; - - /** - * Username to use for anonymous user - * - * @var string - */ - var $anonymous_username = 'anonymous'; - - // }}} - // {{{ Auth_Anonymous() [constructor] - - /** - * Pass all parameters to Parent Auth class - * - * Set up the storage driver. - * - * @param string Type of the storage driver - * @param mixed Additional options for the storage driver - * (example: if you are using DB as the storage - * driver, you have to pass the dsn string here) - * - * @param string Name of the function that creates the login form - * @param boolean Should the login form be displayed if necessary? - * @return void - * @see Auth::Auth() - */ - function Auth_Anonymous($storageDriver, $options = '', $loginFunction = '', $showLogin = true) { - parent::Auth($storageDriver, $options, $loginFunction, $showLogin); - } - - // }}} - // {{{ login() - - /** - * Login function - * - * If no username & password is passed then login as the username - * provided in $this->anonymous_username else call standard login() - * function. - * - * @return void - * @access private - * @see Auth::login() - */ - function login() { - if ( $this->allow_anonymous - && empty($this->username) - && empty($this->password) ) { - $this->setAuth($this->anonymous_username); - if (is_callable($this->loginCallback)) { - call_user_func_array($this->loginCallback, array($this->username, $this) ); - } - } else { - // Call normal login system - parent::login(); - } - } - - // }}} - // {{{ forceLogin() - - /** - * Force the user to login - * - * Calling this function forces the user to provide a real username and - * password before continuing. - * - * @return void - */ - function forceLogin() { - $this->allow_anonymous = false; - if( !empty($this->session['username']) && $this->session['username'] == $this->anonymous_username ) { - $this->logout(); - } - } - - // }}} - -} - -?> diff --git a/pear/Auth/Auth.php b/pear/Auth/Auth.php deleted file mode 100644 index 61ffe5c..0000000 --- a/pear/Auth/Auth.php +++ /dev/null @@ -1,30 +0,0 @@ - - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version CVS: $Id: Auth.php 208437 2006-03-02 06:53:08Z aashley $ - * @link http://pear.php.net/package/Auth - * @deprecated File deprecated since Release 1.2.0 - */ - -/** - * Include Auth package - */ -require_once 'Auth.php'; - -?> diff --git a/pear/Auth/Container.php b/pear/Auth/Container.php deleted file mode 100644 index 5724d7d..0000000 --- a/pear/Auth/Container.php +++ /dev/null @@ -1,262 +0,0 @@ - - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version CVS: $Id: Container.php 294935 2010-02-12 00:05:45Z clockwerx $ - * @link http://pear.php.net/package/Auth - */ - -/** - * Storage class for fetching login data - * - * @category Authentication - * @package Auth - * @author Martin Jansen - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version Release: @package_version@ File: $Revision: 294935 $ - * @link http://pear.php.net/package/Auth - */ -class Auth_Container -{ - - // {{{ properties - - /** - * User that is currently selected from the storage container. - * - * @access public - */ - var $activeUser = ""; - - /** - * The Auth object this container is attached to. - * - * @access public - */ - var $_auth_obj = null; - - // }}} - // {{{ Auth_Container() [constructor] - - /** - * Constructor - * - * Has to be overwritten by each storage class - * - * @access public - */ - function Auth_Container() - { - } - - // }}} - // {{{ fetchData() - - /** - * Fetch data from storage container - * - * Has to be overwritten by each storage class - * - * @access public - */ - function fetchData($username, $password, $isChallengeResponse=false) - { - $this->log('Auth_Container::fetchData() called.', AUTH_LOG_DEBUG); - } - - // }}} - // {{{ verifyPassword() - - /** - * Crypt and verfiy the entered password - * - * @param string Entered password - * @param string Password from the data container (usually this password - * is already encrypted. - * @param string Type of algorithm with which the password from - * the container has been crypted. (md5, crypt etc.) - * Defaults to "md5". - * @return bool True, if the passwords match - */ - function verifyPassword($password1, $password2, $cryptType = "md5") - { - $this->log('Auth_Container::verifyPassword() called.', AUTH_LOG_DEBUG); - switch ($cryptType) { - case "crypt" : - return ((string)crypt($password1, $password2) === (string)$password2); - break; - case "none" : - case "" : - return ((string)$password1 === (string)$password2); - break; - case "md5" : - return ((string)md5($password1) === (string)$password2); - break; - default : - if (function_exists($cryptType)) { - return ((string)$cryptType($password1) === (string)$password2); - } elseif (method_exists($this,$cryptType)) { - return ((string)$this->$cryptType($password1) === (string)$password2); - } else { - return false; - } - break; - } - } - - // }}} - // {{{ supportsChallengeResponse() - - /** - * Returns true if the container supports Challenge Response - * password authentication - */ - function supportsChallengeResponse() - { - return(false); - } - - // }}} - // {{{ getCryptType() - - /** - * Returns the crypt current crypt type of the container - * - * @return string - */ - function getCryptType() - { - return(''); - } - - // }}} - // {{{ listUsers() - - /** - * List all users that are available from the storage container - */ - function listUsers() - { - $this->log('Auth_Container::listUsers() called.', AUTH_LOG_DEBUG); - return AUTH_METHOD_NOT_SUPPORTED; - } - - // }}} - // {{{ getUser() - - /** - * Returns a user assoc array - * - * Containers which want should overide this - * - * @param string The username - */ - function getUser($username) - { - $this->log('Auth_Container::getUser() called.', AUTH_LOG_DEBUG); - $users = $this->listUsers(); - if ($users === AUTH_METHOD_NOT_SUPPORTED) { - return AUTH_METHOD_NOT_SUPPORTED; - } - for ($i=0; $c = count($users), $i<$c; $i++) { - if ($users[$i]['username'] == $username) { - return $users[$i]; - } - } - return false; - } - - // }}} - // {{{ addUser() - - /** - * Add a new user to the storage container - * - * @param string Username - * @param string Password - * @param array Additional information - * - * @return boolean - */ - function addUser($username, $password, $additional=null) - { - $this->log('Auth_Container::addUser() called.', AUTH_LOG_DEBUG); - return AUTH_METHOD_NOT_SUPPORTED; - } - - // }}} - // {{{ removeUser() - - /** - * Remove user from the storage container - * - * @param string Username - */ - function removeUser($username) - { - $this->log('Auth_Container::removeUser() called.', AUTH_LOG_DEBUG); - return AUTH_METHOD_NOT_SUPPORTED; - } - - // }}} - // {{{ changePassword() - - /** - * Change password for user in the storage container - * - * @param string Username - * @param string The new password - */ - function changePassword($username, $password) - { - $this->log('Auth_Container::changePassword() called.', AUTH_LOG_DEBUG); - return AUTH_METHOD_NOT_SUPPORTED; - } - - // }}} - // {{{ log() - - /** - * Log a message to the Auth log - * - * @param string The message - * @param int - * @return boolean - */ - function log($message, $level = AUTH_LOG_DEBUG) { - - if (is_null($this->_auth_obj)) { - - return false; - - } else { - - return $this->_auth_obj->log($message, $level); - - } - - } - - // }}} - -} - -?> diff --git a/pear/Auth/Container/Array.php b/pear/Auth/Container/Array.php deleted file mode 100644 index 334080c..0000000 --- a/pear/Auth/Container/Array.php +++ /dev/null @@ -1,161 +0,0 @@ - - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version CVS: $Id: Array.php 237449 2007-06-12 03:11:27Z aashley $ - * @since File available since Release 1.4.0 - */ - -/** - * Include Auth_Container base class - */ -require_once "Auth/Container.php"; -/** - * Include PEAR package for error handling - */ -require_once "PEAR.php"; - -/** - * Storage driver for fetching authentication data from a PHP Array - * - * This container takes two options when configuring: - * - * cryptType: The crypt used to store the password. Currently recognised - * are: none, md5 and crypt. default: none - * users: A named array of usernames and passwords. - * Ex: - * array( - * 'guest' => '084e0343a0486ff05530df6c705c8bb4', // password guest - * 'georg' => 'fc77dba827fcc88e0243404572c51325' // password georg - * ) - * - * Usage Example: - * array( - * 'guest' => '084e0343a0486ff05530df6c705c8bb4', // password guest - * 'georg' => 'fc77dba827fcc88e0243404572c51325' // password georg - * ), - * 'cryptType'=>'md5', - * ); - * - * $auth = new Auth("Array", $AuthOptions); - * ?> - * - * @category Authentication - * @package Auth - * @author georg_1 at have2 dot com - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version Release: @package_version@ File: $Revision: 237449 $ - * @since File available since Release 1.4.0 - */ - -class Auth_Container_Array extends Auth_Container { - - // {{{ properties - - /** - * The users and their password to authenticate against - * - * @var array $users - */ - var $users; - - /** - * The cryptType used on the passwords - * - * @var string $cryptType - */ - var $cryptType = 'none'; - - // }}} - // {{{ Auth_Container_Array() - - /** - * Constructor for Array Container - * - * @param array $data Options for the container - * @return void - */ - function Auth_Container_Array($data) - { - if (!is_array($data)) { - PEAR::raiseError('The options for Auth_Container_Array must be an array'); - } - if (isset($data['users']) && is_array($data['users'])) { - $this->users = $data['users']; - } else { - $this->users = array(); - PEAR::raiseError('Auth_Container_Array: no user data found in options array'); - } - if (isset($data['cryptType'])) { - $this->cryptType = $data['cryptType']; - } - } - - // }}} - // {{{ fetchData() - - /** - * Get user information from array - * - * This function uses the given username to fetch the corresponding - * login data from the array. If an account that matches the passed - * username and password is found, the function returns true. - * Otherwise it returns false. - * - * @param string Username - * @param string Password - * @return boolean|PEAR_Error Error object or boolean - */ - function fetchData($user, $pass) - { - $this->log('Auth_Container_Array::fetchData() called.', AUTH_LOG_DEBUG); - if ( isset($this->users[$user]) - && $this->verifyPassword($pass, $this->users[$user], $this->cryptType)) { - return true; - } - return false; - } - - // }}} - // {{{ listUsers() - - /** - * Returns a list of users available within the container - * - * @return array - */ - function listUsers() - { - $this->log('Auth_Container_Array::listUsers() called.', AUTH_LOG_DEBUG); - $ret = array(); - foreach ($this->users as $username => $password) { - $ret[]['username'] = $username; - } - return $ret; - } - - // }}} - -} - -?> diff --git a/pear/Auth/Container/DB.php b/pear/Auth/Container/DB.php deleted file mode 100644 index 9137d39..0000000 --- a/pear/Auth/Container/DB.php +++ /dev/null @@ -1,639 +0,0 @@ - - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version CVS: $Id: DB.php 256753 2008-04-04 07:57:02Z aashley $ - * @link http://pear.php.net/package/Auth - */ - -/** - * Include Auth_Container base class - */ -require_once 'Auth/Container.php'; -/** - * Include PEAR DB - */ -require_once 'DB.php'; - -/** - * Storage driver for fetching login data from a database - * - * This storage driver can use all databases which are supported - * by the PEAR DB abstraction layer to fetch login data. - * - * @category Authentication - * @package Auth - * @author Martin Jansen - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version Release: @package_version@ File: $Revision: 256753 $ - * @link http://pear.php.net/package/Auth - */ -class Auth_Container_DB extends Auth_Container -{ - - // {{{ properties - - /** - * Additional options for the storage container - * @var array - */ - var $options = array(); - - /** - * DB object - * @var object - */ - var $db = null; - var $dsn = ''; - - /** - * User that is currently selected from the DB. - * @var string - */ - var $activeUser = ''; - - // }}} - // {{{ Auth_Container_DB [constructor] - - /** - * Constructor of the container class - * - * Save the initial options passed to the container. Initiation of the DB - * connection is no longer performed here and is only done when needed. - * - * @param string Connection data or DB object - * @return object Returns an error object if something went wrong - */ - function Auth_Container_DB($dsn) - { - $this->_setDefaults(); - - if (is_array($dsn)) { - $this->_parseOptions($dsn); - - if (empty($this->options['dsn'])) { - PEAR::raiseError('No connection parameters specified!'); - } - } else { - $this->options['dsn'] = $dsn; - } - } - - // }}} - // {{{ _connect() - - /** - * Connect to database by using the given DSN string - * - * @access private - * @param string DSN string - * @return mixed Object on error, otherwise bool - */ - function _connect($dsn) - { - $this->log('Auth_Container_DB::_connect() called.', AUTH_LOG_DEBUG); - - if (is_string($dsn) || is_array($dsn)) { - $this->db = DB::Connect($dsn, $this->options['db_options']); - } elseif (is_subclass_of($dsn, 'db_common')) { - $this->db = $dsn; - } elseif (DB::isError($dsn)) { - return PEAR::raiseError($dsn->getMessage(), $dsn->getCode()); - } else { - return PEAR::raiseError('The given dsn was not valid in file ' . __FILE__ . ' at line ' . __LINE__, - 41, - PEAR_ERROR_RETURN, - null, - null - ); - } - - if (DB::isError($this->db) || PEAR::isError($this->db)) { - return PEAR::raiseError($this->db->getMessage(), $this->db->getCode()); - } else { - return true; - } - } - - // }}} - // {{{ _prepare() - - /** - * Prepare database connection - * - * This function checks if we have already opened a connection to - * the database. If that's not the case, a new connection is opened. - * - * @access private - * @return mixed True or a DB error object. - */ - function _prepare() - { - if (!DB::isConnection($this->db)) { - $res = $this->_connect($this->options['dsn']); - if (DB::isError($res) || PEAR::isError($res)) { - return $res; - } - } - if ($this->options['auto_quote'] && $this->db->dsn['phptype'] != 'sqlite') { - if (strpos('.', $this->options['table']) === false) { - $this->options['final_table'] = $this->db->quoteIdentifier($this->options['table']); - } else { - $t = explode('.', $this->options['table']); - for ($i = 0, $count = count($t); $i < $count; $i++) - $t[$i] = $this->db->quoteIdentifier($t[$i]); - $this->options['final_table'] = implode('.', $t); - } - $this->options['final_usernamecol'] = $this->db->quoteIdentifier($this->options['usernamecol']); - $this->options['final_passwordcol'] = $this->db->quoteIdentifier($this->options['passwordcol']); - } else { - $this->options['final_table'] = $this->options['table']; - $this->options['final_usernamecol'] = $this->options['usernamecol']; - $this->options['final_passwordcol'] = $this->options['passwordcol']; - } - return true; - } - - // }}} - // {{{ query() - - /** - * Prepare query to the database - * - * This function checks if we have already opened a connection to - * the database. If that's not the case, a new connection is opened. - * After that the query is passed to the database. - * - * @access public - * @param string Query string - * @return mixed a DB_result object or DB_OK on success, a DB - * or PEAR error on failure - */ - function query($query) - { - $err = $this->_prepare(); - if ($err !== true) { - return $err; - } - return $this->db->query($query); - } - - // }}} - // {{{ _setDefaults() - - /** - * Set some default options - * - * @access private - * @return void - */ - function _setDefaults() - { - $this->options['table'] = 'auth'; - $this->options['usernamecol'] = 'username'; - $this->options['passwordcol'] = 'password'; - $this->options['dsn'] = ''; - $this->options['db_fields'] = ''; - $this->options['cryptType'] = 'md5'; - $this->options['db_options'] = array(); - $this->options['db_where'] = ''; - $this->options['auto_quote'] = true; - } - - // }}} - // {{{ _parseOptions() - - /** - * Parse options passed to the container class - * - * @access private - * @param array - */ - function _parseOptions($array) - { - foreach ($array as $key => $value) { - if (isset($this->options[$key])) { - $this->options[$key] = $value; - } - } - } - - // }}} - // {{{ _quoteDBFields() - - /** - * Quote the db_fields option to avoid the possibility of SQL injection. - * - * @access private - * @return string A properly quoted string that can be concatenated into a - * SELECT clause. - */ - function _quoteDBFields() - { - if (isset($this->options['db_fields'])) { - if (is_array($this->options['db_fields'])) { - if ($this->options['auto_quote']) { - $fields = array(); - foreach ($this->options['db_fields'] as $field) { - $fields[] = $this->db->quoteIdentifier($field); - } - return implode(', ', $fields); - } else { - return implode(', ', $this->options['db_fields']); - } - } else { - if (strlen($this->options['db_fields']) > 0) { - if ($this->options['auto_quote']) { - return $this->db->quoteIdentifier($this->options['db_fields']); - } else { - return $this->options['db_fields']; - } - } - } - } - - return ''; - } - - // }}} - // {{{ fetchData() - - /** - * Get user information from database - * - * This function uses the given username to fetch - * the corresponding login data from the database - * table. If an account that matches the passed username - * and password is found, the function returns true. - * Otherwise it returns false. - * - * @param string Username - * @param string Password - * @param boolean If true password is secured using a md5 hash - * the frontend and auth are responsible for making sure the container supports - * challenge response password authentication - * @return mixed Error object or boolean - */ - function fetchData($username, $password, $isChallengeResponse=false) - { - $this->log('Auth_Container_DB::fetchData() called.', AUTH_LOG_DEBUG); - // Prepare for a database query - $err = $this->_prepare(); - if ($err !== true) { - return PEAR::raiseError($err->getMessage(), $err->getCode()); - } - - // Find if db_fields contains a *, if so assume all columns are selected - if (is_string($this->options['db_fields']) - && strstr($this->options['db_fields'], '*')) { - $sql_from = "*"; - } else { - $sql_from = $this->options['final_usernamecol']. - ", ".$this->options['final_passwordcol']; - - if (strlen($fields = $this->_quoteDBFields()) > 0) { - $sql_from .= ', '.$fields; - } - } - - $query = "SELECT ".$sql_from. - " FROM ".$this->options['final_table']. - " WHERE ".$this->options['final_usernamecol']." = ".$this->db->quoteSmart($username); - - // check if there is an optional parameter db_where - if ($this->options['db_where'] != '') { - // there is one, so add it to the query - $query .= " AND ".$this->options['db_where']; - } - - $this->log('Running SQL against DB: '.$query, AUTH_LOG_DEBUG); - - $res = $this->db->getRow($query, null, DB_FETCHMODE_ASSOC); - - if (DB::isError($res)) { - return PEAR::raiseError($res->getMessage(), $res->getCode()); - } - - if (!is_array($res)) { - $this->activeUser = ''; - return false; - } - - // Perform trimming here before the hashihg - $password = trim($password, "\r\n"); - $res[$this->options['passwordcol']] = trim($res[$this->options['passwordcol']], "\r\n"); - - // If using Challenge Response md5 the pass with the secret - if ($isChallengeResponse) { - $res[$this->options['passwordcol']] = md5($res[$this->options['passwordcol']] - .$this->_auth_obj->session['loginchallenege']); - - // UGLY cannot avoid without modifying verifyPassword - if ($this->options['cryptType'] == 'md5') { - $res[$this->options['passwordcol']] = md5($res[$this->options['passwordcol']]); - } - - //print " Hashed Password [{$res[$this->options['passwordcol']]}]
    \n"; - } - - if ($this->verifyPassword($password, - $res[$this->options['passwordcol']], - $this->options['cryptType'])) { - // Store additional field values in the session - foreach ($res as $key => $value) { - if ($key == $this->options['passwordcol'] || - $key == $this->options['usernamecol']) { - continue; - } - - $this->log('Storing additional field: '.$key, AUTH_LOG_DEBUG); - - // Use reference to the auth object if exists - // This is because the auth session variable can change so a - // static call to setAuthData does not make sence - $this->_auth_obj->setAuthData($key, $value); - } - return true; - } - $this->activeUser = $res[$this->options['usernamecol']]; - return false; - } - - // }}} - // {{{ listUsers() - - /** - * Returns a list of users from the container - * - * @return mixed - * @access public - */ - function listUsers() - { - $this->log('Auth_Container_DB::listUsers() called.', AUTH_LOG_DEBUG); - $err = $this->_prepare(); - if ($err !== true) { - return PEAR::raiseError($err->getMessage(), $err->getCode()); - } - - $retVal = array(); - - // Find if db_fields contains a *, if so assume all col are selected - if ( is_string($this->options['db_fields']) - && strstr($this->options['db_fields'], '*')) { - $sql_from = "*"; - } else { - $sql_from = $this->options['final_usernamecol']. - ", ".$this->options['final_passwordcol']; - - if (strlen($fields = $this->_quoteDBFields()) > 0) { - $sql_from .= ', '.$fields; - } - } - - $query = sprintf("SELECT %s FROM %s", - $sql_from, - $this->options['final_table'] - ); - - // check if there is an optional parameter db_where - if ($this->options['db_where'] != '') { - // there is one, so add it to the query - $query .= " WHERE ".$this->options['db_where']; - } - - $this->log('Running SQL against DB: '.$query, AUTH_LOG_DEBUG); - - $res = $this->db->getAll($query, null, DB_FETCHMODE_ASSOC); - - if (DB::isError($res)) { - return PEAR::raiseError($res->getMessage(), $res->getCode()); - } else { - foreach ($res as $user) { - $user['username'] = $user[$this->options['usernamecol']]; - $retVal[] = $user; - } - } - $this->log('Found '.count($retVal).' users.', AUTH_LOG_DEBUG); - return $retVal; - } - - // }}} - // {{{ addUser() - - /** - * Add user to the storage container - * - * @access public - * @param string Username - * @param string Password - * @param mixed Additional information that are stored in the DB - * - * @return mixed True on success, otherwise error object - */ - function addUser($username, $password, $additional = "") - { - $this->log('Auth_Container_DB::addUser() called.', AUTH_LOG_DEBUG); - $err = $this->_prepare(); - if ($err !== true) { - return PEAR::raiseError($err->getMessage(), $err->getCode()); - } - - if ( isset($this->options['cryptType']) - && $this->options['cryptType'] == 'none') { - $cryptFunction = 'strval'; - } elseif ( isset($this->options['cryptType']) - && function_exists($this->options['cryptType'])) { - $cryptFunction = $this->options['cryptType']; - } else { - $cryptFunction = 'md5'; - } - - $password = $cryptFunction($password); - - $additional_key = ''; - $additional_value = ''; - - if (is_array($additional)) { - foreach ($additional as $key => $value) { - if ($this->options['auto_quote']) { - $additional_key .= ', ' . $this->db->quoteIdentifier($key); - } else { - $additional_key .= ', ' . $key; - } - $additional_value .= ", " . $this->db->quoteSmart($value); - } - } - - $query = sprintf("INSERT INTO %s (%s, %s%s) VALUES (%s, %s%s)", - $this->options['final_table'], - $this->options['final_usernamecol'], - $this->options['final_passwordcol'], - $additional_key, - $this->db->quoteSmart($username), - $this->db->quoteSmart($password), - $additional_value - ); - - $this->log('Running SQL against DB: '.$query, AUTH_LOG_DEBUG); - - $res = $this->query($query); - - if (DB::isError($res)) { - return PEAR::raiseError($res->getMessage(), $res->getCode()); - } else { - return true; - } - } - - // }}} - // {{{ removeUser() - - /** - * Remove user from the storage container - * - * @access public - * @param string Username - * - * @return mixed True on success, otherwise error object - */ - function removeUser($username) - { - $this->log('Auth_Container_DB::removeUser() called.', AUTH_LOG_DEBUG); - - $err = $this->_prepare(); - if ($err !== true) { - return PEAR::raiseError($err->getMessage(), $err->getCode()); - } - - // check if there is an optional parameter db_where - if ($this->options['db_where'] != '') { - // there is one, so add it to the query - $where = " AND ".$this->options['db_where']; - } else { - $where = ''; - } - - $query = sprintf("DELETE FROM %s WHERE %s = %s %s", - $this->options['final_table'], - $this->options['final_usernamecol'], - $this->db->quoteSmart($username), - $where - ); - - $this->log('Running SQL against DB: '.$query, AUTH_LOG_DEBUG); - - $res = $this->query($query); - - if (DB::isError($res)) { - return PEAR::raiseError($res->getMessage(), $res->getCode()); - } else { - return true; - } - } - - // }}} - // {{{ changePassword() - - /** - * Change password for user in the storage container - * - * @param string Username - * @param string The new password (plain text) - */ - function changePassword($username, $password) - { - $this->log('Auth_Container_DB::changePassword() called.', AUTH_LOG_DEBUG); - $err = $this->_prepare(); - if ($err !== true) { - return PEAR::raiseError($err->getMessage(), $err->getCode()); - } - - if ( isset($this->options['cryptType']) - && $this->options['cryptType'] == 'none') { - $cryptFunction = 'strval'; - } elseif ( isset($this->options['cryptType']) - && function_exists($this->options['cryptType'])) { - $cryptFunction = $this->options['cryptType']; - } else { - $cryptFunction = 'md5'; - } - - $password = $cryptFunction($password); - - // check if there is an optional parameter db_where - if ($this->options['db_where'] != '') { - // there is one, so add it to the query - $where = " AND ".$this->options['db_where']; - } else { - $where = ''; - } - - $query = sprintf("UPDATE %s SET %s = %s WHERE %s = %s %s", - $this->options['final_table'], - $this->options['final_passwordcol'], - $this->db->quoteSmart($password), - $this->options['final_usernamecol'], - $this->db->quoteSmart($username), - $where - ); - - $this->log('Running SQL against DB: '.$query, AUTH_LOG_DEBUG); - - $res = $this->query($query); - - if (DB::isError($res)) { - return PEAR::raiseError($res->getMessage(), $res->getCode()); - } else { - return true; - } - } - - // }}} - // {{{ supportsChallengeResponse() - - /** - * Determine if this container supports - * password authentication with challenge response - * - * @return bool - * @access public - */ - function supportsChallengeResponse() - { - return in_array($this->options['cryptType'], array('md5', 'none', '')); - } - - // }}} - // {{{ getCryptType() - - /** - * Returns the selected crypt type for this container - */ - function getCryptType() - { - return($this->options['cryptType']); - } - - // }}} - -} -?> diff --git a/pear/Auth/Container/DBLite.php b/pear/Auth/Container/DBLite.php deleted file mode 100644 index 2996108..0000000 --- a/pear/Auth/Container/DBLite.php +++ /dev/null @@ -1,320 +0,0 @@ - - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version CVS: $Id: DBLite.php 256753 2008-04-04 07:57:02Z aashley $ - * @link http://pear.php.net/package/Auth - * @since File available since Release 1.3.0 - */ - -/** - * Include Auth_Container base class - */ -require_once 'Auth/Container.php'; -/** - * Include PEAR DB package - */ -require_once 'DB.php'; - -/** - * A lighter storage driver for fetching login data from a database - * - * This driver is derived from the DB storage container but - * with the user manipulation function removed for smaller file size - * by the PEAR DB abstraction layer to fetch login data. - * - * @category Authentication - * @package Auth - * @author Martin Jansen - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version Release: @package_version@ File: $Revision: 256753 $ - * @link http://pear.php.net/package/Auth - * @since Class available since Release 1.3.0 - */ -class Auth_Container_DBLite extends Auth_Container -{ - - // {{{ properties - - /** - * Additional options for the storage container - * @var array - */ - var $options = array(); - - /** - * DB object - * @var object - */ - var $db = null; - var $dsn = ''; - - /** - * User that is currently selected from the DB. - * @var string - */ - var $activeUser = ''; - - // }}} - // {{{ Auth_Container_DBLite() [constructor] - - /** - * Constructor of the container class - * - * Initate connection to the database via PEAR::DB - * - * @param string Connection data or DB object - * @return object Returns an error object if something went wrong - */ - function Auth_Container_DBLite($dsn) - { - $this->options['table'] = 'auth'; - $this->options['usernamecol'] = 'username'; - $this->options['passwordcol'] = 'password'; - $this->options['dsn'] = ''; - $this->options['db_fields'] = ''; - $this->options['cryptType'] = 'md5'; - $this->options['db_options'] = array(); - $this->options['db_where'] = ''; - $this->options['auto_quote'] = true; - - if (is_array($dsn)) { - $this->_parseOptions($dsn); - if (empty($this->options['dsn'])) { - PEAR::raiseError('No connection parameters specified!'); - } - } else { - $this->options['dsn'] = $dsn; - } - } - - // }}} - // {{{ _connect() - - /** - * Connect to database by using the given DSN string - * - * @access private - * @param string DSN string - * @return mixed Object on error, otherwise bool - */ - function _connect(&$dsn) - { - $this->log('Auth_Container_DBLite::_connect() called.', AUTH_LOG_DEBUG); - if (is_string($dsn) || is_array($dsn)) { - $this->db =& DB::connect($dsn, $this->options['db_options']); - } elseif (is_subclass_of($dsn, "db_common")) { - $this->db =& $dsn; - } else { - return PEAR::raiseError("Invalid dsn or db object given"); - } - - if (DB::isError($this->db) || PEAR::isError($this->db)) { - return PEAR::raiseError($this->db->getMessage(), $this->db->getCode()); - } else { - return true; - } - } - - // }}} - // {{{ _prepare() - - /** - * Prepare database connection - * - * This function checks if we have already opened a connection to - * the database. If that's not the case, a new connection is opened. - * - * @access private - * @return mixed True or a DB error object. - */ - function _prepare() - { - if (!DB::isConnection($this->db)) { - $res = $this->_connect($this->options['dsn']); - if (DB::isError($res) || PEAR::isError($res)) { - return $res; - } - } - if ($this->options['auto_quote'] && $this->db->dsn['phptype'] != 'sqlite') { - if (strpos('.', $this->options['table']) === false) { - $this->options['final_table'] = $this->db->quoteIdentifier($this->options['table']); - } else { - $t = explode('.', $this->options['table']); - for ($i = 0, $count = count($t); $i < $count; $i++) - $t[$i] = $this->db->quoteIdentifier($t[$i]); - $this->options['final_table'] = implode('.', $t); - } - $this->options['final_usernamecol'] = $this->db->quoteIdentifier($this->options['usernamecol']); - $this->options['final_passwordcol'] = $this->db->quoteIdentifier($this->options['passwordcol']); - } else { - $this->options['final_table'] = $this->options['table']; - $this->options['final_usernamecol'] = $this->options['usernamecol']; - $this->options['final_passwordcol'] = $this->options['passwordcol']; - } - return true; - } - - // }}} - // {{{ _parseOptions() - - /** - * Parse options passed to the container class - * - * @access private - * @param array - */ - function _parseOptions($array) - { - foreach ($array as $key => $value) { - if (isset($this->options[$key])) { - $this->options[$key] = $value; - } - } - } - - // }}} - // {{{ _quoteDBFields() - - /** - * Quote the db_fields option to avoid the possibility of SQL injection. - * - * @access private - * @return string A properly quoted string that can be concatenated into a - * SELECT clause. - */ - function _quoteDBFields() - { - if (isset($this->options['db_fields'])) { - if (is_array($this->options['db_fields'])) { - if ($this->options['auto_quote']) { - $fields = array(); - foreach ($this->options['db_fields'] as $field) { - $fields[] = $this->db->quoteIdentifier($field); - } - return implode(', ', $fields); - } else { - return implode(', ', $this->options['db_fields']); - } - } else { - if (strlen($this->options['db_fields']) > 0) { - if ($this->options['auto_quote']) { - return $this->db->quoteIdentifier($this->options['db_fields']); - } else { - $this->options['db_fields']; - } - } - } - } - - return ''; - } - - // }}} - // {{{ fetchData() - - /** - * Get user information from database - * - * This function uses the given username to fetch - * the corresponding login data from the database - * table. If an account that matches the passed username - * and password is found, the function returns true. - * Otherwise it returns false. - * - * @param string Username - * @param string Password - * @return mixed Error object or boolean - */ - function fetchData($username, $password) - { - $this->log('Auth_Container_DBLite::fetchData() called.', AUTH_LOG_DEBUG); - // Prepare for a database query - $err = $this->_prepare(); - if ($err !== true) { - return PEAR::raiseError($err->getMessage(), $err->getCode()); - } - - // Find if db_fields contains a *, if so assume all col are selected - if (is_string($this->options['db_fields']) - && strstr($this->options['db_fields'], '*')) { - $sql_from = "*"; - } else { - $sql_from = $this->options['final_usernamecol']. - ", ".$this->options['final_passwordcol']; - - if (strlen($fields = $this->_quoteDBFields()) > 0) { - $sql_from .= ', '.$fields; - } - } - - $query = "SELECT ".$sql_from. - " FROM ".$this->options['final_table']. - " WHERE ".$this->options['final_usernamecol']." = ".$this->db->quoteSmart($username); - - // check if there is an optional parameter db_where - if ($this->options['db_where'] != '') { - // there is one, so add it to the query - $query .= " AND ".$this->options['db_where']; - } - - $this->log('Running SQL against DB: '.$query, AUTH_LOG_DEBUG); - - $res = $this->db->getRow($query, null, DB_FETCHMODE_ASSOC); - - if (DB::isError($res)) { - return PEAR::raiseError($res->getMessage(), $res->getCode()); - } - if (!is_array($res)) { - $this->activeUser = ''; - return false; - } - if ($this->verifyPassword(trim($password, "\r\n"), - trim($res[$this->options['passwordcol']], "\r\n"), - $this->options['cryptType'])) { - // Store additional field values in the session - foreach ($res as $key => $value) { - if ($key == $this->options['passwordcol'] || - $key == $this->options['usernamecol']) { - continue; - } - - $this->log('Storing additional field: '.$key, AUTH_LOG_DEBUG); - - // Use reference to the auth object if exists - // This is because the auth session variable can change so a static call to setAuthData does not make sence - if (is_object($this->_auth_obj)) { - $this->_auth_obj->setAuthData($key, $value); - } else { - Auth::setAuthData($key, $value); - } - } - $this->activeUser = $res[$this->options['usernamecol']]; - return true; - } - $this->activeUser = $res[$this->options['usernamecol']]; - return false; - } - - // }}} - -} -?> diff --git a/pear/Auth/Container/File.php b/pear/Auth/Container/File.php deleted file mode 100644 index 2b8274c..0000000 --- a/pear/Auth/Container/File.php +++ /dev/null @@ -1,314 +0,0 @@ - - * @author Martin Jansen - * @author Mika Tuupola - * @author Michael Wallner - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version CVS: $Id: File.php 237449 2007-06-12 03:11:27Z aashley $ - * @link http://pear.php.net/package/Auth - */ - -/** - * Include PEAR File_Passwd package - */ -require_once "File/Passwd.php"; -/** - * Include Auth_Container base class - */ -require_once "Auth/Container.php"; -/** - * Include PEAR package for error handling - */ -require_once "PEAR.php"; - -/** - * Storage driver for fetching login data from an encrypted password file. - * - * This storage container can handle CVS pserver style passwd files. - * - * @category Authentication - * @package Auth - * @author Stefan Ekman - * @author Martin Jansen - * @author Mika Tuupola - * @author Michael Wallner - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version Release: @package_version@ File: $Revision: 237449 $ - * @link http://pear.php.net/package/Auth - */ -class Auth_Container_File extends Auth_Container -{ - - // {{{ properties - - /** - * Path to passwd file - * - * @var string - */ - var $pwfile = ''; - - /** - * Options for container - * - * @var array - */ - var $options = array(); - - // }}} - // {{{ Auth_Container_File() [constructor] - - /** - * Constructor of the container class - * - * @param string $filename path to passwd file - * @return object Auth_Container_File new Auth_Container_File object - */ - function Auth_Container_File($filename) { - $this->_setDefaults(); - - // Only file is a valid option here - if(is_array($filename)) { - $this->pwfile = $filename['file']; - $this->_parseOptions($filename); - } else { - $this->pwfile = $filename; - } - } - - // }}} - // {{{ fetchData() - - /** - * Authenticate an user - * - * @param string username - * @param string password - * @return mixed boolean|PEAR_Error - */ - function fetchData($user, $pass) - { - $this->log('Auth_Container_File::fetchData() called.', AUTH_LOG_DEBUG); - return File_Passwd::staticAuth($this->options['type'], $this->pwfile, $user, $pass); - } - - // }}} - // {{{ listUsers() - - /** - * List all available users - * - * @return array - */ - function listUsers() - { - $this->log('Auth_Container_File::listUsers() called.', AUTH_LOG_DEBUG); - - $pw_obj = &$this->_load(); - if (PEAR::isError($pw_obj)) { - return array(); - } - - $users = $pw_obj->listUser(); - if (!is_array($users)) { - return array(); - } - - foreach ($users as $key => $value) { - $retVal[] = array("username" => $key, - "password" => $value['passwd'], - "cvsuser" => $value['system']); - } - - $this->log('Found '.count($retVal).' users.', AUTH_LOG_DEBUG); - - return $retVal; - } - - // }}} - // {{{ addUser() - - /** - * Add a new user to the storage container - * - * @param string username - * @param string password - * @param mixed Additional parameters to File_Password_*::addUser() - * - * @return boolean - */ - function addUser($user, $pass, $additional='') - { - $this->log('Auth_Container_File::addUser() called.', AUTH_LOG_DEBUG); - $params = array($user, $pass); - if (is_array($additional)) { - foreach ($additional as $item) { - $params[] = $item; - } - } else { - $params[] = $additional; - } - - $pw_obj = &$this->_load(); - if (PEAR::isError($pw_obj)) { - return false; - } - - $res = call_user_func_array(array(&$pw_obj, 'addUser'), $params); - if (PEAR::isError($res)) { - return false; - } - - $res = $pw_obj->save(); - if (PEAR::isError($res)) { - return false; - } - - return true; - } - - // }}} - // {{{ removeUser() - - /** - * Remove user from the storage container - * - * @param string Username - * @return boolean - */ - function removeUser($user) - { - $this->log('Auth_Container_File::removeUser() called.', AUTH_LOG_DEBUG); - $pw_obj = &$this->_load(); - if (PEAR::isError($pw_obj)) { - return false; - } - - $res = $pw_obj->delUser($user); - if (PEAR::isError($res)) { - return false; - } - - $res = $pw_obj->save(); - if (PEAR::isError($res)) { - return false; - } - - return true; - } - - // }}} - // {{{ changePassword() - - /** - * Change password for user in the storage container - * - * @param string Username - * @param string The new password - */ - function changePassword($username, $password) - { - $this->log('Auth_Container_File::changePassword() called.', AUTH_LOG_DEBUG); - $pw_obj = &$this->_load(); - if (PEAR::isError($pw_obj)) { - return false; - } - - $res = $pw_obj->changePasswd($username, $password); - if (PEAR::isError($res)) { - return false; - } - - $res = $pw_obj->save(); - if (PEAR::isError($res)) { - return false; - } - - return true; - } - - // }}} - // {{{ _load() - - /** - * Load and initialize the File_Passwd object - * - * @return object File_Passwd_Cvs|PEAR_Error - */ - function &_load() - { - static $pw_obj; - - if (!isset($pw_obj)) { - $this->log('Instanciating File_Password object of type '.$this->options['type'], AUTH_LOG_DEBUG); - $pw_obj = File_Passwd::factory($this->options['type']); - if (PEAR::isError($pw_obj)) { - return $pw_obj; - } - - $pw_obj->setFile($this->pwfile); - - $res = $pw_obj->load(); - if (PEAR::isError($res)) { - return $res; - } - } - - return $pw_obj; - } - - // }}} - // {{{ _setDefaults() - - /** - * Set some default options - * - * @access private - * @return void - */ - function _setDefaults() - { - $this->options['type'] = 'Cvs'; - } - - // }}} - // {{{ _parseOptions() - - /** - * Parse options passed to the container class - * - * @access private - * @param array - */ - function _parseOptions($array) - { - foreach ($array as $key => $value) { - if (isset($this->options[$key])) { - $this->options[$key] = $value; - } - } - } - - // }}} - -} -?> diff --git a/pear/Auth/Container/IMAP.php b/pear/Auth/Container/IMAP.php deleted file mode 100644 index 2768483..0000000 --- a/pear/Auth/Container/IMAP.php +++ /dev/null @@ -1,210 +0,0 @@ - - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version CVS: $Id: IMAP.php 237449 2007-06-12 03:11:27Z aashley $ - * @link http://pear.php.net/package/Auth - * @since File available since Release 1.2.0 - */ - -/** - * Include Auth_Container base class - */ -require_once "Auth/Container.php"; - -/** - * Include PEAR class for error handling - */ -require_once "PEAR.php"; - -/** - * Storage driver for fetching login data from an IMAP server - * - * This class is based on LDAP containers, but it very simple. - * By default it connects to localhost:143 - * The constructor will first check if the host:port combination is - * actually reachable. This behaviour can be disabled. - * It then tries to create an IMAP stream (without opening a mailbox) - * If you wish to pass extended options to the connections, you may - * do so by specifying protocol options. - * - * To use this storage containers, you have to use the - * following syntax: - * - * 'mail.example.com', - * 'port' => 143, - * ); - * $myAuth = new Auth('IMAP', $params); - * ... - * - * By default we connect without any protocol options set. However, some - * servers require you to connect with the notls or norsh options set. - * To do this you need to add the following value to the params array: - * 'baseDSN' => '/imap/notls/norsh' - * - * To connect to an SSL IMAP server: - * 'baseDSN' => '/imap/ssl' - * - * To connect to an SSL IMAP server with a self-signed certificate: - * 'baseDSN' => '/imap/ssl/novalidate-cert' - * - * Further options may be available and can be found on the php site at - * http://www.php.net/manual/function.imap-open.php - * - * @category Authentication - * @package Auth - * @author Jeroen Houben - * @author Cipriano Groenendal - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version Release: @package_version@ File: $Revision: 237449 $ - * @link http://pear.php.net/package/Auth - * @since Class available since Release 1.2.0 - */ -class Auth_Container_IMAP extends Auth_Container -{ - - // {{{ properties - - /** - * Options for the class - * @var array - */ - var $options = array(); - - // }}} - // {{{ Auth_Container_IMAP() [constructor] - - /** - * Constructor of the container class - * - * @param $params associative array with host, port, baseDSN, checkServer - * and userattr key - * @return object Returns an error object if something went wrong - * @todo Use PEAR Net_IMAP if IMAP extension not loaded - */ - function Auth_Container_IMAP($params) - { - if (!extension_loaded('imap')) { - return PEAR::raiseError('Cannot use IMAP authentication, ' - .'IMAP extension not loaded!', 41, PEAR_ERROR_DIE); - } - $this->_setDefaults(); - - // set parameters (if any) - if (is_array($params)) { - $this->_parseOptions($params); - } - - if ($this->options['checkServer']) { - $this->_checkServer($this->options['timeout']); - } - return true; - } - - // }}} - // {{{ _setDefaults() - - /** - * Set some default options - * - * @access private - */ - function _setDefaults() - { - $this->options['host'] = 'localhost'; - $this->options['port'] = 143; - $this->options['baseDSN'] = ''; - $this->options['checkServer'] = true; - $this->options['timeout'] = 20; - } - - // }}} - // {{{ _checkServer() - - /** - * Check if the given server and port are reachable - * - * @access private - */ - function _checkServer() { - $this->log('Auth_Container_IMAP::_checkServer() called.', AUTH_LOG_DEBUG); - $fp = @fsockopen ($this->options['host'], $this->options['port'], - $errno, $errstr, $this->options['timeout']); - if (is_resource($fp)) { - @fclose($fp); - } else { - $message = "Error connecting to IMAP server " - . $this->options['host'] - . ":" . $this->options['port']; - return PEAR::raiseError($message, 41); - } - } - - // }}} - // {{{ _parseOptions() - - /** - * Parse options passed to the container class - * - * @access private - * @param array - */ - function _parseOptions($array) - { - foreach ($array as $key => $value) { - $this->options[$key] = $value; - } - } - - // }}} - // {{{ fetchData() - - /** - * Try to open a IMAP stream using $username / $password - * - * @param string Username - * @param string Password - * @return boolean - */ - function fetchData($username, $password) - { - $this->log('Auth_Container_IMAP::fetchData() called.', AUTH_LOG_DEBUG); - $dsn = '{'.$this->options['host'].':'.$this->options['port'].$this->options['baseDSN'].'}'; - $conn = @imap_open ($dsn, $username, $password, OP_HALFOPEN); - if (is_resource($conn)) { - $this->log('Successfully connected to IMAP server.', AUTH_LOG_DEBUG); - $this->activeUser = $username; - @imap_close($conn); - return true; - } else { - $this->log('Connection to IMAP server failed.', AUTH_LOG_DEBUG); - $this->activeUser = ''; - return false; - } - } - - // }}} - -} -?> diff --git a/pear/Auth/Container/KADM5.php b/pear/Auth/Container/KADM5.php deleted file mode 100644 index 2853580..0000000 --- a/pear/Auth/Container/KADM5.php +++ /dev/null @@ -1,171 +0,0 @@ - - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version CVS: $Id: KADM5.php 237449 2007-06-12 03:11:27Z aashley $ - * @link http://pear.php.net/package/Auth - * @since File available since Release 1.4.0 - */ - -/** - * Include Auth_Container base class - */ -require_once 'Auth/Container.php'; -/** - * Include PEAR for error handling - */ -require_once 'PEAR.php'; - -/** - * Storage driver for Authentication on a Kerberos V server. - * - * Available options: - * hostname: The hostname of the kerberos server - * realm: The Kerberos V realm - * timeout: The timeout for checking the server - * checkServer: Set to true to check if the server is running when - * constructing the object - * - * @category Authentication - * @package Auth - * @author Andrew Teixeira - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version Release: @package_version@ File: $Revision: 237449 $ - * @link http://pear.php.net/package/Auth - * @since Class available since Release 1.4.0 - */ -class Auth_Container_KADM5 extends Auth_Container { - - // {{{ properties - - /** - * Options for the class - * @var string - */ - var $options = array(); - - // }}} - // {{{ Auth_Container_KADM5() - - /** - * Constructor of the container class - * - * $options can have these keys: - * 'hostname' The hostname of the kerberos server - * 'realm' The Kerberos V realm - * 'timeout' The timeout for checking the server - * 'checkServer' Set to true to check if the server is running when - * constructing the object - * - * @param $options associative array - * @return object Returns an error object if something went wrong - */ - function Auth_Container_KADM5($options) { - if (!extension_loaded('kadm5')) { - return PEAR::raiseError("Cannot use Kerberos V authentication, KADM5 extension not loaded!", 41, PEAR_ERROR_DIE); - } - - $this->_setDefaults(); - - if (isset($options['hostname'])) { - $this->options['hostname'] = $options['hostname']; - } - if (isset($options['realm'])) { - $this->options['realm'] = $options['realm']; - } - if (isset($options['timeout'])) { - $this->options['timeout'] = $options['timeout']; - } - if (isset($options['checkServer'])) { - $this->options['checkServer'] = $options['checkServer']; - } - - if ($this->options['checkServer']) { - $this->_checkServer(); - } - } - - // }}} - // {{{ fetchData() - - /** - * Try to login to the KADM5 server - * - * @param string Username - * @param string Password - * @return boolean - */ - function fetchData($username, $password) { - $this->log('Auth_Container_KADM5::fetchData() called.', AUTH_LOG_DEBUG); - if ( ($username == NULL) || ($password == NULL) ) { - return false; - } - - $server = $this->options['hostname']; - $realm = $this->options['realm']; - $check = @kadm5_init_with_password($server, $realm, $username, $password); - - if ($check == false) { - return false; - } else { - return true; - } - } - - // }}} - // {{{ _setDefaults() - - /** - * Set some default options - * - * @access private - */ - function _setDefaults() { - $this->options['hostname'] = 'localhost'; - $this->options['realm'] = NULL; - $this->options['timeout'] = 10; - $this->options['checkServer'] = false; - } - - // }}} - // {{{ _checkServer() - - /** - * Check if the given server and port are reachable - * - * @access private - */ - function _checkServer() { - $fp = @fsockopen ($this->options['hostname'], 88, $errno, $errstr, $this->options['timeout']); - if (is_resource($fp)) { - @fclose($fp); - } else { - $message = "Error connecting to Kerberos V server " - .$this->options['hostname'].":".$this->options['port']; - return PEAR::raiseError($message, 41, PEAR_ERROR_DIE); - } - } - - // }}} - -} - -?> diff --git a/pear/Auth/Container/LDAP.php b/pear/Auth/Container/LDAP.php deleted file mode 100644 index eaf35cf..0000000 --- a/pear/Auth/Container/LDAP.php +++ /dev/null @@ -1,766 +0,0 @@ - - * @author Adam Ashley - * @author Hugues Peeters - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version CVS: $Id: LDAP.php 237449 2007-06-12 03:11:27Z aashley $ - * @link http://pear.php.net/package/Auth - */ - -/** - * Include Auth_Container base class - */ -require_once "Auth/Container.php"; -/** - * Include PEAR package for error handling - */ -require_once "PEAR.php"; - -/** - * Storage driver for fetching login data from LDAP - * - * This class is heavily based on the DB and File containers. By default it - * connects to localhost:389 and searches for uid=$username with the scope - * "sub". If no search base is specified, it will try to determine it via - * the namingContexts attribute. It takes its parameters in a hash, connects - * to the ldap server, binds anonymously, searches for the user, and tries - * to bind as the user with the supplied password. When a group was set, it - * will look for group membership of the authenticated user. If all goes - * well the authentication was successful. - * - * Parameters: - * - * host: localhost (default), ldap.netsols.de or 127.0.0.1 - * port: 389 (default) or 636 or whereever your server runs - * url: ldap://localhost:389/ - * useful for ldaps://, works only with openldap2 ? - * it will be preferred over host and port - * version: LDAP version to use, ususally 2 (default) or 3, - * must be an integer! - * referrals: If set, determines whether the LDAP library automatically - * follows referrals returned by LDAP servers or not. Possible - * values are true (default) or false. - * binddn: If set, searching for user will be done after binding - * as this user, if not set the bind will be anonymous. - * This is reported to make the container work with MS - * Active Directory, but should work with any server that - * is configured this way. - * This has to be a complete dn for now (basedn and - * userdn will not be appended). - * bindpw: The password to use for binding with binddn - * basedn: the base dn of your server - * userdn: gets prepended to basedn when searching for user - * userscope: Scope for user searching: one, sub (default), or base - * userattr: the user attribute to search for (default: uid) - * userfilter: filter that will be added to the search filter - * this way: (&(userattr=username)(userfilter)) - * default: (objectClass=posixAccount) - * attributes: array of additional attributes to fetch from entry. - * these will added to auth data and can be retrieved via - * Auth::getAuthData(). An empty array will fetch all attributes, - * array('') will fetch no attributes at all (default) - * If you add 'dn' as a value to this array, the users DN that was - * used for binding will be added to auth data as well. - * attrformat: The returned format of the additional data defined in the - * 'attributes' option. Two formats are available. - * LDAP returns data formatted in a - * multidimensional array where each array starts with a - * 'count' element providing the number of attributes in the - * entry, or the number of values for attributes. When set - * to this format, the only way to retrieve data from the - * Auth object is by calling getAuthData('attributes'). - * AUTH returns data formatted in a - * structure more compliant with other Auth Containers, - * where each attribute element can be directly called by - * getAuthData() method from Auth. - * For compatibily with previous LDAP container versions, - * the default format is LDAP. - * groupdn: gets prepended to basedn when searching for group - * groupattr: the group attribute to search for (default: cn) - * groupfilter: filter that will be added to the search filter when - * searching for a group: - * (&(groupattr=group)(memberattr=username)(groupfilter)) - * default: (objectClass=groupOfUniqueNames) - * memberattr : the attribute of the group object where the user dn - * may be found (default: uniqueMember) - * memberisdn: whether the memberattr is the dn of the user (default) - * or the value of userattr (usually uid) - * group: the name of group to search for - * groupscope: Scope for group searching: one, sub (default), or base - * start_tls: enable/disable the use of START_TLS encrypted connection - * (default: false) - * debug: Enable/Disable debugging output (default: false) - * try_all: Whether to try all user accounts returned from the search - * or just the first one. (default: false) - * - * To use this storage container, you have to use the following syntax: - * - * 'localhost', - * 'port' => '389', - * 'version' => 3, - * 'basedn' => 'o=netsols,c=de', - * 'userattr' => 'uid' - * 'binddn' => 'cn=admin,o=netsols,c=de', - * 'bindpw' => 'password')); - * - * $a2 = new Auth('LDAP', array( - * 'url' => 'ldaps://ldap.netsols.de', - * 'basedn' => 'o=netsols,c=de', - * 'userscope' => 'one', - * 'userdn' => 'ou=People', - * 'groupdn' => 'ou=Groups', - * 'groupfilter' => '(objectClass=posixGroup)', - * 'memberattr' => 'memberUid', - * 'memberisdn' => false, - * 'group' => 'admin' - * )); - * - * $a3 = new Auth('LDAP', array( - * 'host' => 'ldap.netsols.de', - * 'port' => 389, - * 'version' => 3, - * 'referrals' => false, - * 'basedn' => 'dc=netsols,dc=de', - * 'binddn' => 'cn=Jan Wagner,cn=Users,dc=netsols,dc=de', - * 'bindpw' => 'password', - * 'userattr' => 'samAccountName', - * 'userfilter' => '(objectClass=user)', - * 'attributes' => array(''), - * 'group' => 'testing', - * 'groupattr' => 'samAccountName', - * 'groupfilter' => '(objectClass=group)', - * 'memberattr' => 'member', - * 'memberisdn' => true, - * 'groupdn' => 'cn=Users', - * 'groupscope' => 'one', - * 'debug' => true); - * - * The parameter values have to correspond - * to the ones for your LDAP server of course. - * - * When talking to a Microsoft ActiveDirectory server you have to - * use 'samaccountname' as the 'userattr' and follow special rules - * to translate the ActiveDirectory directory names into 'basedn'. - * The 'basedn' for the default 'Users' folder on an ActiveDirectory - * server for the ActiveDirectory Domain (which is not related to - * its DNS name) "win2000.example.org" would be: - * "CN=Users, DC=win2000, DC=example, DC=org' - * where every component of the domain name becomes a DC attribute - * of its own. If you want to use a custom users folder you have to - * replace "CN=Users" with a sequence of "OU" attributes that specify - * the path to your custom folder in reverse order. - * So the ActiveDirectory folder - * "win2000.example.org\Custom\Accounts" - * would become - * "OU=Accounts, OU=Custom, DC=win2000, DC=example, DC=org' - * - * It seems that binding anonymously to an Active Directory - * is not allowed, so you have to set binddn and bindpw for - * user searching. - * - * LDAP Referrals need to be set to false for AD to work sometimes. - * - * Example a3 shows a full blown and tested example for connection to - * Windows 2000 Active Directory with group mebership checking - * - * Note also that if you want an encrypted connection to an MS LDAP - * server, then, on your webserver, you must specify - * TLS_REQCERT never - * in /etc/ldap/ldap.conf or in the webserver user's ~/.ldaprc (which - * may or may not be read depending on your configuration). - * - * - * @category Authentication - * @package Auth - * @author Jan Wagner - * @author Adam Ashley - * @author Hugues Peeters - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version Release: @package_version@ File: $Revision: 237449 $ - * @link http://pear.php.net/package/Auth - */ -class Auth_Container_LDAP extends Auth_Container -{ - - // {{{ properties - - /** - * Options for the class - * @var array - */ - var $options = array(); - - /** - * Connection ID of LDAP Link - * @var string - */ - var $conn_id = false; - - // }}} - - // {{{ Auth_Container_LDAP() [constructor] - - /** - * Constructor of the container class - * - * @param $params, associative hash with host,port,basedn and userattr key - * @return object Returns an error object if something went wrong - */ - function Auth_Container_LDAP($params) - { - if (false === extension_loaded('ldap')) { - return PEAR::raiseError('Auth_Container_LDAP: LDAP Extension not loaded', - 41, PEAR_ERROR_DIE); - } - - $this->_setDefaults(); - - if (is_array($params)) { - $this->_parseOptions($params); - } - } - - // }}} - // {{{ _prepare() - - /** - * Prepare LDAP connection - * - * This function checks if we have already opened a connection to - * the LDAP server. If that's not the case, a new connection is opened. - * - * @access private - * @return mixed True or a PEAR error object. - */ - function _prepare() - { - if (!$this->_isValidLink()) { - $res = $this->_connect(); - if (PEAR::isError($res)) { - return $res; - } - } - return true; - } - - // }}} - // {{{ _connect() - - /** - * Connect to the LDAP server using the global options - * - * @access private - * @return object Returns a PEAR error object if an error occurs. - */ - function _connect() - { - $this->log('Auth_Container_LDAP::_connect() called.', AUTH_LOG_DEBUG); - // connect - if (isset($this->options['url']) && $this->options['url'] != '') { - $this->log('Connecting with URL', AUTH_LOG_DEBUG); - $conn_params = array($this->options['url']); - } else { - $this->log('Connecting with host:port', AUTH_LOG_DEBUG); - $conn_params = array($this->options['host'], $this->options['port']); - } - - if (($this->conn_id = @call_user_func_array('ldap_connect', $conn_params)) === false) { - $this->log('Connection to server failed.', AUTH_LOG_DEBUG); - $this->log('LDAP ERROR: '.ldap_errno($this->conn_id).': '.ldap_error($this->conn_id), AUTH_LOG_DEBUG); - return PEAR::raiseError('Auth_Container_LDAP: Could not connect to server.', 41); - } - $this->log('Successfully connected to server', AUTH_LOG_DEBUG); - - // switch LDAP version - if (is_numeric($this->options['version']) && $this->options['version'] > 2) { - $this->log("Switching to LDAP version {$this->options['version']}", AUTH_LOG_DEBUG); - @ldap_set_option($this->conn_id, LDAP_OPT_PROTOCOL_VERSION, $this->options['version']); - - // start TLS if available - if (isset($this->options['start_tls']) && $this->options['start_tls']) { - $this->log("Starting TLS session", AUTH_LOG_DEBUG); - if (@ldap_start_tls($this->conn_id) === false) { - $this->log('Could not start TLS session', AUTH_LOG_DEBUG); - $this->log('LDAP ERROR: '.ldap_errno($this->conn_id).': '.ldap_error($this->conn_id), AUTH_LOG_DEBUG); - return PEAR::raiseError('Auth_Container_LDAP: Could not start tls.', 41); - } - } - } - - // switch LDAP referrals - if (is_bool($this->options['referrals'])) { - $this->log("Switching LDAP referrals to " . (($this->options['referrals']) ? 'true' : 'false'), AUTH_LOG_DEBUG); - if (@ldap_set_option($this->conn_id, LDAP_OPT_REFERRALS, $this->options['referrals']) === false) { - $this->log('Could not change LDAP referrals options', AUTH_LOG_DEBUG); - $this->log('LDAP ERROR: '.ldap_errno($this->conn_id).': '.ldap_error($this->conn_id), AUTH_LOG_DEBUG); - } - } - - // bind with credentials or anonymously - if (strlen($this->options['binddn']) && strlen($this->options['bindpw'])) { - $this->log('Binding with credentials', AUTH_LOG_DEBUG); - $bind_params = array($this->conn_id, $this->options['binddn'], $this->options['bindpw']); - } else { - $this->log('Binding anonymously', AUTH_LOG_DEBUG); - $bind_params = array($this->conn_id); - } - - // bind for searching - if ((@call_user_func_array('ldap_bind', $bind_params)) === false) { - $this->log('Bind failed', AUTH_LOG_DEBUG); - $this->log('LDAP ERROR: '.ldap_errno($this->conn_id).': '.ldap_error($this->conn_id), AUTH_LOG_DEBUG); - $this->_disconnect(); - return PEAR::raiseError("Auth_Container_LDAP: Could not bind to LDAP server.", 41); - } - $this->log('Binding was successful', AUTH_LOG_DEBUG); - - return true; - } - - // }}} - // {{{ _disconnect() - - /** - * Disconnects (unbinds) from ldap server - * - * @access private - */ - function _disconnect() - { - $this->log('Auth_Container_LDAP::_disconnect() called.', AUTH_LOG_DEBUG); - if ($this->_isValidLink()) { - $this->log('disconnecting from server'); - @ldap_unbind($this->conn_id); - } - } - - // }}} - // {{{ _getBaseDN() - - /** - * Tries to find Basedn via namingContext Attribute - * - * @access private - */ - function _getBaseDN() - { - $this->log('Auth_Container_LDAP::_getBaseDN() called.', AUTH_LOG_DEBUG); - $err = $this->_prepare(); - if ($err !== true) { - return PEAR::raiseError($err->getMessage(), $err->getCode()); - } - - if ($this->options['basedn'] == "" && $this->_isValidLink()) { - $this->log("basedn not set, searching via namingContexts.", AUTH_LOG_DEBUG); - - $result_id = @ldap_read($this->conn_id, "", "(objectclass=*)", array("namingContexts")); - - if (@ldap_count_entries($this->conn_id, $result_id) == 1) { - - $this->log("got result for namingContexts", AUTH_LOG_DEBUG); - - $entry_id = @ldap_first_entry($this->conn_id, $result_id); - $attrs = @ldap_get_attributes($this->conn_id, $entry_id); - $basedn = $attrs['namingContexts'][0]; - - if ($basedn != "") { - $this->log("result for namingContexts was $basedn", AUTH_LOG_DEBUG); - $this->options['basedn'] = $basedn; - } - } - @ldap_free_result($result_id); - } - - // if base ist still not set, raise error - if ($this->options['basedn'] == "") { - return PEAR::raiseError("Auth_Container_LDAP: LDAP search base not specified!", 41); - } - return true; - } - - // }}} - // {{{ _isValidLink() - - /** - * determines whether there is a valid ldap conenction or not - * - * @accessd private - * @return boolean - */ - function _isValidLink() - { - if (is_resource($this->conn_id)) { - if (get_resource_type($this->conn_id) == 'ldap link') { - return true; - } - } - return false; - } - - // }}} - // {{{ _setDefaults() - - /** - * Set some default options - * - * @access private - */ - function _setDefaults() - { - $this->options['url'] = ''; - $this->options['host'] = 'localhost'; - $this->options['port'] = '389'; - $this->options['version'] = 2; - $this->options['referrals'] = true; - $this->options['binddn'] = ''; - $this->options['bindpw'] = ''; - $this->options['basedn'] = ''; - $this->options['userdn'] = ''; - $this->options['userscope'] = 'sub'; - $this->options['userattr'] = 'uid'; - $this->options['userfilter'] = '(objectClass=posixAccount)'; - $this->options['attributes'] = array(''); // no attributes - $this->options['attrformat'] = 'AUTH'; // returns attribute like other Auth containers - $this->options['group'] = ''; - $this->options['groupdn'] = ''; - $this->options['groupscope'] = 'sub'; - $this->options['groupattr'] = 'cn'; - $this->options['groupfilter'] = '(objectClass=groupOfUniqueNames)'; - $this->options['memberattr'] = 'uniqueMember'; - $this->options['memberisdn'] = true; - $this->options['start_tls'] = false; - $this->options['debug'] = false; - $this->options['try_all'] = false; // Try all user ids returned not just the first one - } - - // }}} - // {{{ _parseOptions() - - /** - * Parse options passed to the container class - * - * @access private - * @param array - */ - function _parseOptions($array) - { - $array = $this->_setV12OptionsToV13($array); - - foreach ($array as $key => $value) { - if (array_key_exists($key, $this->options)) { - if ($key == 'attributes') { - if (is_array($value)) { - $this->options[$key] = $value; - } else { - $this->options[$key] = explode(',', $value); - } - } else { - $this->options[$key] = $value; - } - } - } - } - - // }}} - // {{{ _setV12OptionsToV13() - - /** - * Adapt deprecated options from Auth 1.2 LDAP to Auth 1.3 LDAP - * - * @author Hugues Peeters - * @access private - * @param array - * @return array - */ - function _setV12OptionsToV13($array) - { - if (isset($array['useroc'])) - $array['userfilter'] = "(objectClass=".$array['useroc'].")"; - if (isset($array['groupoc'])) - $array['groupfilter'] = "(objectClass=".$array['groupoc'].")"; - if (isset($array['scope'])) - $array['userscope'] = $array['scope']; - - return $array; - } - - // }}} - // {{{ _scope2function() - - /** - * Get search function for scope - * - * @param string scope - * @return string ldap search function - */ - function _scope2function($scope) - { - switch($scope) { - case 'one': - $function = 'ldap_list'; - break; - case 'base': - $function = 'ldap_read'; - break; - default: - $function = 'ldap_search'; - break; - } - return $function; - } - - // }}} - // {{{ fetchData() - - /** - * Fetch data from LDAP server - * - * Searches the LDAP server for the given username/password - * combination. Escapes all LDAP meta characters in username - * before performing the query. - * - * @param string Username - * @param string Password - * @return boolean - */ - function fetchData($username, $password) - { - $this->log('Auth_Container_LDAP::fetchData() called.', AUTH_LOG_DEBUG); - $err = $this->_prepare(); - if ($err !== true) { - return PEAR::raiseError($err->getMessage(), $err->getCode()); - } - - $err = $this->_getBaseDN(); - if ($err !== true) { - return PEAR::raiseError($err->getMessage(), $err->getCode()); - } - - // UTF8 Encode username for LDAPv3 - if (@ldap_get_option($this->conn_id, LDAP_OPT_PROTOCOL_VERSION, $ver) && $ver == 3) { - $this->log('UTF8 encoding username for LDAPv3', AUTH_LOG_DEBUG); - $username = utf8_encode($username); - } - - // make search filter - $filter = sprintf('(&(%s=%s)%s)', - $this->options['userattr'], - $this->_quoteFilterString($username), - $this->options['userfilter']); - - // make search base dn - $search_basedn = $this->options['userdn']; - if ($search_basedn != '' && substr($search_basedn, -1) != ',') { - $search_basedn .= ','; - } - $search_basedn .= $this->options['basedn']; - - // attributes - $searchAttributes = $this->options['attributes']; - - // make functions params array - $func_params = array($this->conn_id, $search_basedn, $filter, $searchAttributes); - - // search function to use - $func_name = $this->_scope2function($this->options['userscope']); - - $this->log("Searching with $func_name and filter $filter in $search_basedn", AUTH_LOG_DEBUG); - - // search - if (($result_id = @call_user_func_array($func_name, $func_params)) === false) { - $this->log('User not found', AUTH_LOG_DEBUG); - } elseif (@ldap_count_entries($this->conn_id, $result_id) >= 1) { // did we get some possible results? - - $this->log('User(s) found', AUTH_LOG_DEBUG); - - $first = true; - $entry_id = null; - - do { - - // then get the user dn - if ($first) { - $entry_id = @ldap_first_entry($this->conn_id, $result_id); - $first = false; - } else { - $entry_id = @ldap_next_entry($this->conn_id, $entry_id); - if ($entry_id === false) - break; - } - $user_dn = @ldap_get_dn($this->conn_id, $entry_id); - - // as the dn is not fetched as an attribute, we save it anyway - if (is_array($searchAttributes) && in_array('dn', $searchAttributes)) { - $this->log('Saving DN to AuthData', AUTH_LOG_DEBUG); - $this->_auth_obj->setAuthData('dn', $user_dn); - } - - // fetch attributes - if ($attributes = @ldap_get_attributes($this->conn_id, $entry_id)) { - - if (is_array($attributes) && isset($attributes['count']) && - $attributes['count'] > 0) { - - // ldap_get_attributes() returns a specific multi dimensional array - // format containing all the attributes and where each array starts - // with a 'count' element providing the number of attributes in the - // entry, or the number of values for attribute. For compatibility - // reasons, it remains the default format returned by LDAP container - // setAuthData(). - // The code below optionally returns attributes in another format, - // more compliant with other Auth containers, where each attribute - // element are directly set in the 'authData' list. This option is - // enabled by setting 'attrformat' to - // 'AUTH' in the 'options' array. - // eg. $this->options['attrformat'] = 'AUTH' - - if ( strtoupper($this->options['attrformat']) == 'AUTH' ) { - $this->log('Saving attributes to Auth data in AUTH format', AUTH_LOG_DEBUG); - unset ($attributes['count']); - foreach ($attributes as $attributeName => $attributeValue ) { - if (is_int($attributeName)) continue; - if (is_array($attributeValue) && isset($attributeValue['count'])) { - unset ($attributeValue['count']); - } - if (count($attributeValue)<=1) $attributeValue = $attributeValue[0]; - $this->log('Storing additional field: '.$attributeName, AUTH_LOG_DEBUG); - $this->_auth_obj->setAuthData($attributeName, $attributeValue); - } - } - else - { - $this->log('Saving attributes to Auth data in LDAP format', AUTH_LOG_DEBUG); - $this->_auth_obj->setAuthData('attributes', $attributes); - } - } - } - @ldap_free_result($result_id); - - // need to catch an empty password as openldap seems to return TRUE - // if anonymous binding is allowed - if ($password != "") { - $this->log("Bind as $user_dn", AUTH_LOG_DEBUG); - - // try binding as this user with the supplied password - if (@ldap_bind($this->conn_id, $user_dn, $password)) { - $this->log('Bind successful', AUTH_LOG_DEBUG); - - // check group if appropiate - if (strlen($this->options['group'])) { - // decide whether memberattr value is a dn or the username - $this->log('Checking group membership', AUTH_LOG_DEBUG); - $return = $this->checkGroup(($this->options['memberisdn']) ? $user_dn : $username); - $this->_disconnect(); - return $return; - } else { - $this->log('Authenticated', AUTH_LOG_DEBUG); - $this->_disconnect(); - return true; // user authenticated - } // checkGroup - } // bind - } // non-empty password - } while ($this->options['try_all'] == true); // interate through entries - } // get results - // default - $this->log('NOT authenticated!', AUTH_LOG_DEBUG); - $this->_disconnect(); - return false; - } - - // }}} - // {{{ checkGroup() - - /** - * Validate group membership - * - * Searches the LDAP server for group membership of the - * supplied username. Quotes all LDAP filter meta characters in - * the user name before querying the LDAP server. - * - * @param string Distinguished Name of the authenticated User - * @return boolean - */ - function checkGroup($user) - { - $this->log('Auth_Container_LDAP::checkGroup() called.', AUTH_LOG_DEBUG); - $err = $this->_prepare(); - if ($err !== true) { - return PEAR::raiseError($err->getMessage(), $err->getCode()); - } - - // make filter - $filter = sprintf('(&(%s=%s)(%s=%s)%s)', - $this->options['groupattr'], - $this->options['group'], - $this->options['memberattr'], - $this->_quoteFilterString($user), - $this->options['groupfilter']); - - // make search base dn - $search_basedn = $this->options['groupdn']; - if ($search_basedn != '' && substr($search_basedn, -1) != ',') { - $search_basedn .= ','; - } - $search_basedn .= $this->options['basedn']; - - $func_params = array($this->conn_id, $search_basedn, $filter, - array($this->options['memberattr'])); - $func_name = $this->_scope2function($this->options['groupscope']); - - $this->log("Searching with $func_name and filter $filter in $search_basedn", AUTH_LOG_DEBUG); - - // search - if (($result_id = @call_user_func_array($func_name, $func_params)) != false) { - if (@ldap_count_entries($this->conn_id, $result_id) == 1) { - @ldap_free_result($result_id); - $this->log('User is member of group', AUTH_LOG_DEBUG); - return true; - } - } - // default - $this->log('User is NOT member of group', AUTH_LOG_DEBUG); - return false; - } - - // }}} - // {{{ _quoteFilterString() - - /** - * Escapes LDAP filter special characters as defined in RFC 2254. - * - * @access private - * @param string Filter String - */ - function _quoteFilterString($filter_str) - { - $metas = array( '\\', '*', '(', ')', "\x00"); - $quoted_metas = array('\\\\', '\*', '\(', '\)', "\\\x00"); - return str_replace($metas, $quoted_metas, $filter_str); - } - - // }}} - -} - -?> diff --git a/pear/Auth/Container/MDB.php b/pear/Auth/Container/MDB.php deleted file mode 100644 index 019a122..0000000 --- a/pear/Auth/Container/MDB.php +++ /dev/null @@ -1,625 +0,0 @@ - - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version CVS: $Id: MDB.php 256753 2008-04-04 07:57:02Z aashley $ - * @link http://pear.php.net/package/Auth - * @since File available since Release 1.2.3 - */ - -/** - * Include Auth_Container base class - */ -require_once 'Auth/Container.php'; -/** - * Include PEAR MDB package - */ -require_once 'MDB.php'; - -/** - * Storage driver for fetching login data from a database - * - * This storage driver can use all databases which are supported - * by the PEAR MDB abstraction layer to fetch login data. - * - * @category Authentication - * @package Auth - * @author Lorenzo Alberton - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version Release: @package_version@ File: $Revision: 256753 $ - * @link http://pear.php.net/package/Auth - * @since Class available since Release 1.2.3 - */ -class Auth_Container_MDB extends Auth_Container -{ - - // {{{ properties - - /** - * Additional options for the storage container - * @var array - */ - var $options = array(); - - /** - * MDB object - * @var object - */ - var $db = null; - var $dsn = ''; - - /** - * User that is currently selected from the DB. - * @var string - */ - var $activeUser = ''; - - // }}} - // {{{ Auth_Container_MDB() [constructor] - - /** - * Constructor of the container class - * - * Initate connection to the database via PEAR::MDB - * - * @param string Connection data or MDB object - * @return object Returns an error object if something went wrong - */ - function Auth_Container_MDB($dsn) - { - $this->_setDefaults(); - - if (is_array($dsn)) { - $this->_parseOptions($dsn); - if (empty($this->options['dsn'])) { - PEAR::raiseError('No connection parameters specified!'); - } - } else { - $this->options['dsn'] = $dsn; - } - } - - // }}} - // {{{ _connect() - - /** - * Connect to database by using the given DSN string - * - * @access private - * @param mixed DSN string | array | mdb object - * @return mixed Object on error, otherwise bool - */ - function _connect($dsn) - { - $this->log('Auth_Container_MDB::_connect() called.', AUTH_LOG_DEBUG); - if (is_string($dsn) || is_array($dsn)) { - $this->db =& MDB::connect($dsn, $this->options['db_options']); - } elseif (is_subclass_of($dsn, 'mdb_common')) { - $this->db = $dsn; - } elseif (is_object($dsn) && MDB::isError($dsn)) { - return PEAR::raiseError($dsn->getMessage(), $dsn->code); - } else { - return PEAR::raiseError('The given dsn was not valid in file ' . __FILE__ . ' at line ' . __LINE__, - 41, - PEAR_ERROR_RETURN, - null, - null - ); - - } - - if (MDB::isError($this->db) || PEAR::isError($this->db)) { - return PEAR::raiseError($this->db->getMessage(), $this->db->code); - } - - if ($this->options['auto_quote']) { - if (strpos('.', $this->options['table']) === false) { - $this->options['final_table'] = $this->db->quoteIdentifier($this->options['table']); - } else { - $t = explode('.', $this->options['table']); - for ($i = 0, $count = count($t); $i < $count; $i++) - $t[$i] = $this->db->quoteIdentifier($t[$i]); - $this->options['final_table'] = implode('.', $t); - } - $this->options['final_usernamecol'] = $this->db->quoteIdentifier($this->options['usernamecol']); - $this->options['final_passwordcol'] = $this->db->quoteIdentifier($this->options['passwordcol']); - } else { - $this->options['final_table'] = $this->options['table']; - $this->options['final_usernamecol'] = $this->options['usernamecol']; - $this->options['final_passwordcol'] = $this->options['passwordcol']; - } - - return true; - } - - // }}} - // {{{ _prepare() - - /** - * Prepare database connection - * - * This function checks if we have already opened a connection to - * the database. If that's not the case, a new connection is opened. - * - * @access private - * @return mixed True or a MDB error object. - */ - function _prepare() - { - if (is_subclass_of($this->db, 'mdb_common')) { - return true; - } - return $this->_connect($this->options['dsn']); - } - - // }}} - // {{{ query() - - /** - * Prepare query to the database - * - * This function checks if we have already opened a connection to - * the database. If that's not the case, a new connection is opened. - * After that the query is passed to the database. - * - * @access public - * @param string Query string - * @return mixed a MDB_result object or MDB_OK on success, a MDB - * or PEAR error on failure - */ - function query($query) - { - $this->log('Auth_Container_MDB::query() called.', AUTH_LOG_DEBUG); - $err = $this->_prepare(); - if ($err !== true) { - return $err; - } - return $this->db->query($query); - } - - // }}} - // {{{ _setDefaults() - - /** - * Set some default options - * - * @access private - * @return void - */ - function _setDefaults() - { - $this->options['table'] = 'auth'; - $this->options['usernamecol'] = 'username'; - $this->options['passwordcol'] = 'password'; - $this->options['dsn'] = ''; - $this->options['db_fields'] = ''; - $this->options['cryptType'] = 'md5'; - $this->options['db_options'] = array(); - $this->options['db_where'] = ''; - $this->options['auto_quote'] = true; - } - - // }}} - // {{{ _parseOptions() - - /** - * Parse options passed to the container class - * - * @access private - * @param array - */ - function _parseOptions($array) - { - foreach ($array as $key => $value) { - if (isset($this->options[$key])) { - $this->options[$key] = $value; - } - } - } - - // }}} - // {{{ _quoteDBFields() - - /** - * Quote the db_fields option to avoid the possibility of SQL injection. - * - * @access private - * @return string A properly quoted string that can be concatenated into a - * SELECT clause. - */ - function _quoteDBFields() - { - if (isset($this->options['db_fields'])) { - if (is_array($this->options['db_fields'])) { - if ($this->options['auto_quote']) { - $fields = array(); - foreach ($this->options['db_fields'] as $field) { - $fields[] = $this->db->quoteIdentifier($field); - } - return implode(', ', $fields); - } else { - return implode(', ', $this->options['db_fields']); - } - } else { - if (strlen($this->options['db_fields']) > 0) { - if ($this->options['auto_quote']) { - return $this->db->quoteIdentifier($this->options['db_fields']); - } else { - return $this->options['db_fields']; - } - } - } - } - - return ''; - } - - // }}} - // {{{ fetchData() - - /** - * Get user information from database - * - * This function uses the given username to fetch - * the corresponding login data from the database - * table. If an account that matches the passed username - * and password is found, the function returns true. - * Otherwise it returns false. - * - * @param string Username - * @param string Password - * @param boolean If true password is secured using a md5 hash - * the frontend and auth are responsible for making sure the container supports - * challenge response password authentication - * @return mixed Error object or boolean - */ - function fetchData($username, $password, $isChallengeResponse=false) - { - $this->log('Auth_Container_MDB::fetchData() called.', AUTH_LOG_DEBUG); - // Prepare for a database query - $err = $this->_prepare(); - if ($err !== true) { - return PEAR::raiseError($err->getMessage(), $err->getCode()); - } - - //Check if db_fields contains a *, if so assume all columns are selected - if (is_string($this->options['db_fields']) - && strstr($this->options['db_fields'], '*')) { - $sql_from = '*'; - } else { - $sql_from = $this->options['final_usernamecol']. - ", ".$this->options['final_passwordcol']; - - if (strlen($fields = $this->_quoteDBFields()) > 0) { - $sql_from .= ', '.$fields; - } - } - - $query = sprintf("SELECT %s FROM %s WHERE %s = %s", - $sql_from, - $this->options['final_table'], - $this->options['final_usernamecol'], - $this->db->getTextValue($username) - ); - - // check if there is an optional parameter db_where - if ($this->options['db_where'] != '') { - // there is one, so add it to the query - $query .= " AND ".$this->options['db_where']; - } - - $this->log('Running SQL against MDB: '.$query, AUTH_LOG_DEBUG); - - $res = $this->db->getRow($query, null, null, null, MDB_FETCHMODE_ASSOC); - - if (MDB::isError($res) || PEAR::isError($res)) { - return PEAR::raiseError($res->getMessage(), $res->getCode()); - } - if (!is_array($res)) { - $this->activeUser = ''; - return false; - } - - // Perform trimming here before the hashing - $password = trim($password, "\r\n"); - $res[$this->options['passwordcol']] = trim($res[$this->options['passwordcol']], "\r\n"); - - // If using Challenge Response md5 the pass with the secret - if ($isChallengeResponse) { - $res[$this->options['passwordcol']] = - md5($res[$this->options['passwordcol']].$this->_auth_obj->session['loginchallenege']); - // UGLY cannot avoid without modifying verifyPassword - if ($this->options['cryptType'] == 'md5') { - $res[$this->options['passwordcol']] = md5($res[$this->options['passwordcol']]); - } - } - - if ($this->verifyPassword($password, - $res[$this->options['passwordcol']], - $this->options['cryptType'])) { - // Store additional field values in the session - foreach ($res as $key => $value) { - if ($key == $this->options['passwordcol'] || - $key == $this->options['usernamecol']) { - continue; - } - - $this->log('Storing additional field: '.$key, AUTH_LOG_DEBUG); - // Use reference to the auth object if exists - // This is because the auth session variable can change so a static - // call to setAuthData does not make sense - $this->_auth_obj->setAuthData($key, $value); - } - return true; - } - - $this->activeUser = $res[$this->options['usernamecol']]; - return false; - } - - // }}} - // {{{ listUsers() - - /** - * Returns a list of users from the container - * - * @return mixed array|PEAR_Error - * @access public - */ - function listUsers() - { - $this->log('Auth_Container_MDB::listUsers() called.', AUTH_LOG_DEBUG); - $err = $this->_prepare(); - if ($err !== true) { - return PEAR::raiseError($err->getMessage(), $err->getCode()); - } - - $retVal = array(); - - //Check if db_fields contains a *, if so assume all columns are selected - if ( is_string($this->options['db_fields']) - && strstr($this->options['db_fields'], '*')) { - $sql_from = '*'; - } else { - $sql_from = $this->options['final_usernamecol'] - .', '.$this->options['final_passwordcol']; - - if (strlen($fields = $this->_quoteDBFields()) > 0) { - $sql_from .= ', '.$fields; - } - } - - $query = sprintf('SELECT %s FROM %s', - $sql_from, - $this->options['final_table'] - ); - - // check if there is an optional parameter db_where - if ($this->options['db_where'] != '') { - // there is one, so add it to the query - $query .= " WHERE ".$this->options['db_where']; - } - - $this->log('Running SQL against MDB: '.$query, AUTH_LOG_DEBUG); - - $res = $this->db->getAll($query, null, null, null, MDB_FETCHMODE_ASSOC); - - if (MDB::isError($res)) { - return PEAR::raiseError($res->getMessage(), $res->getCode()); - } else { - foreach ($res as $user) { - $user['username'] = $user[$this->options['usernamecol']]; - $retVal[] = $user; - } - } - $this->log('Found '.count($retVal).' users.', AUTH_LOG_DEBUG); - return $retVal; - } - - // }}} - // {{{ addUser() - - /** - * Add user to the storage container - * - * @access public - * @param string Username - * @param string Password - * @param mixed Additional information that are stored in the DB - * - * @return mixed True on success, otherwise error object - */ - function addUser($username, $password, $additional = "") - { - $this->log('Auth_Container_MDB::addUser() called.', AUTH_LOG_DEBUG); - $err = $this->_prepare(); - if ($err !== true) { - return PEAR::raiseError($err->getMessage(), $err->getCode()); - } - - if (isset($this->options['cryptType']) && $this->options['cryptType'] == 'none') { - $cryptFunction = 'strval'; - } elseif (isset($this->options['cryptType']) && function_exists($this->options['cryptType'])) { - $cryptFunction = $this->options['cryptType']; - } else { - $cryptFunction = 'md5'; - } - - $password = $cryptFunction($password); - - $additional_key = ''; - $additional_value = ''; - - if (is_array($additional)) { - foreach ($additional as $key => $value) { - if ($this->options['auto_quote']) { - $additional_key .= ', ' . $this->db->quoteIdentifier($key); - } else { - $additional_key .= ', ' . $key; - } - $additional_value .= ', ' . $this->db->getTextValue($value); - } - } - - $query = sprintf("INSERT INTO %s (%s, %s%s) VALUES (%s, %s%s)", - $this->options['final_table'], - $this->options['final_usernamecol'], - $this->options['final_passwordcol'], - $additional_key, - $this->db->getTextValue($username), - $this->db->getTextValue($password), - $additional_value - ); - - $this->log('Running SQL against MDB: '.$query, AUTH_LOG_DEBUG); - - $res = $this->query($query); - - if (MDB::isError($res)) { - return PEAR::raiseError($res->getMessage(), $res->code); - } - return true; - } - - // }}} - // {{{ removeUser() - - /** - * Remove user from the storage container - * - * @access public - * @param string Username - * - * @return mixed True on success, otherwise error object - */ - function removeUser($username) - { - $this->log('Auth_Container_MDB::removeUser() called.', AUTH_LOG_DEBUG); - $err = $this->_prepare(); - if ($err !== true) { - return PEAR::raiseError($err->getMessage(), $err->getCode()); - } - - $query = sprintf("DELETE FROM %s WHERE %s = %s", - $this->options['final_table'], - $this->options['final_usernamecol'], - $this->db->getTextValue($username) - ); - - // check if there is an optional parameter db_where - if ($this->options['db_where'] != '') { - // there is one, so add it to the query - $query .= " AND ".$this->options['db_where']; - } - - $this->log('Running SQL against MDB: '.$query, AUTH_LOG_DEBUG); - - $res = $this->query($query); - - if (MDB::isError($res)) { - return PEAR::raiseError($res->getMessage(), $res->code); - } - return true; - } - - // }}} - // {{{ changePassword() - - /** - * Change password for user in the storage container - * - * @param string Username - * @param string The new password (plain text) - */ - function changePassword($username, $password) - { - $this->log('Auth_Container_MDB::changePassword() called.', AUTH_LOG_DEBUG); - $err = $this->_prepare(); - if ($err !== true) { - return PEAR::raiseError($err->getMessage(), $err->getCode()); - } - - if (isset($this->options['cryptType']) && $this->options['cryptType'] == 'none') { - $cryptFunction = 'strval'; - } elseif (isset($this->options['cryptType']) && function_exists($this->options['cryptType'])) { - $cryptFunction = $this->options['cryptType']; - } else { - $cryptFunction = 'md5'; - } - - $password = $cryptFunction($password); - - $query = sprintf("UPDATE %s SET %s = %s WHERE %s = %s", - $this->options['final_table'], - $this->options['final_passwordcol'], - $this->db->getTextValue($password), - $this->options['final_usernamecol'], - $this->db->getTextValue($username) - ); - - // check if there is an optional parameter db_where - if ($this->options['db_where'] != '') { - // there is one, so add it to the query - $query .= " AND ".$this->options['db_where']; - } - - $this->log('Running SQL against MDB: '.$query, AUTH_LOG_DEBUG); - - $res = $this->query($query); - - if (MDB::isError($res)) { - return PEAR::raiseError($res->getMessage(), $res->code); - } - return true; - } - - // }}} - // {{{ supportsChallengeResponse() - - /** - * Determine if this container supports - * password authentication with challenge response - * - * @return bool - * @access public - */ - function supportsChallengeResponse() - { - return in_array($this->options['cryptType'], array('md5', 'none', '')); - } - - // }}} - // {{{ getCryptType() - - /** - * Returns the selected crypt type for this container - * - * @return string Function used to crypt the password - */ - function getCryptType() - { - return $this->options['cryptType']; - } - - // }}} - -} -?> diff --git a/pear/Auth/Container/MDB2.php b/pear/Auth/Container/MDB2.php deleted file mode 100644 index a3c26b7..0000000 --- a/pear/Auth/Container/MDB2.php +++ /dev/null @@ -1,624 +0,0 @@ - - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version CVS: $Id: MDB2.php 256753 2008-04-04 07:57:02Z aashley $ - * @link http://pear.php.net/package/Auth - * @since File available since Release 1.3.0 - */ - -/** - * Include Auth_Container base class - */ -require_once 'Auth/Container.php'; -/** - * Include PEAR MDB2 package - */ -require_once 'MDB2.php'; - -/** - * Storage driver for fetching login data from a database - * - * This storage driver can use all databases which are supported - * by the PEAR MDB2 abstraction layer to fetch login data. - * - * @category Authentication - * @package Auth - * @author Lorenzo Alberton - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version Release: @package_version@ File: $Revision: 256753 $ - * @link http://pear.php.net/package/Auth - * @since Class available since Release 1.3.0 - */ -class Auth_Container_MDB2 extends Auth_Container -{ - - // {{{ properties - - /** - * Additional options for the storage container - * @var array - */ - var $options = array(); - - /** - * MDB object - * @var object - */ - var $db = null; - var $dsn = ''; - - /** - * User that is currently selected from the DB. - * @var string - */ - var $activeUser = ''; - - // }}} - // {{{ Auth_Container_MDB2() [constructor] - - /** - * Constructor of the container class - * - * Initate connection to the database via PEAR::MDB2 - * - * @param string Connection data or MDB2 object - * @return object Returns an error object if something went wrong - */ - function Auth_Container_MDB2($dsn) - { - $this->_setDefaults(); - - if (is_array($dsn)) { - $this->_parseOptions($dsn); - if (empty($this->options['dsn'])) { - PEAR::raiseError('No connection parameters specified!'); - } - } else { - $this->options['dsn'] = $dsn; - } - } - - // }}} - // {{{ _connect() - - /** - * Connect to database by using the given DSN string - * - * @access private - * @param mixed DSN string | array | mdb object - * @return mixed Object on error, otherwise bool - */ - function _connect($dsn) - { - $this->log('Auth_Container_MDB2::_connect() called.', AUTH_LOG_DEBUG); - if (is_string($dsn) || is_array($dsn)) { - $this->db =& MDB2::connect($dsn, $this->options['db_options']); - } elseif (is_subclass_of($dsn, 'MDB2_Driver_Common')) { - $this->db = $dsn; - } elseif (is_object($dsn) && MDB2::isError($dsn)) { - return PEAR::raiseError($dsn->getMessage(), $dsn->code); - } else { - return PEAR::raiseError('The given dsn was not valid in file ' . __FILE__ . ' at line ' . __LINE__, - 41, - PEAR_ERROR_RETURN, - null, - null - ); - - } - - if (MDB2::isError($this->db) || PEAR::isError($this->db)) { - return PEAR::raiseError($this->db->getMessage(), $this->db->code); - } - - if ($this->options['auto_quote']) { - if (strpos('.', $this->options['table']) === false) { - $this->options['final_table'] = $this->db->quoteIdentifier($this->options['table'], true); - } else { - $t = explode('.', $this->options['table']); - for ($i = 0, $count = count($t); $i < $count; $i++) - $t[$i] = $this->db->quoteIdentifier($t[$i], true); - $this->options['final_table'] = implode('.', $t); - } - $this->options['final_usernamecol'] = $this->db->quoteIdentifier($this->options['usernamecol'], true); - $this->options['final_passwordcol'] = $this->db->quoteIdentifier($this->options['passwordcol'], true); - } else { - $this->options['final_table'] = $this->options['table']; - $this->options['final_usernamecol'] = $this->options['usernamecol']; - $this->options['final_passwordcol'] = $this->options['passwordcol']; - } - - return true; - } - - // }}} - // {{{ _prepare() - - /** - * Prepare database connection - * - * This function checks if we have already opened a connection to - * the database. If that's not the case, a new connection is opened. - * - * @access private - * @return mixed True or a MDB error object. - */ - function _prepare() - { - if (is_subclass_of($this->db, 'MDB2_Driver_Common')) { - return true; - } - return $this->_connect($this->options['dsn']); - } - - // }}} - // {{{ query() - - /** - * Prepare query to the database - * - * This function checks if we have already opened a connection to - * the database. If that's not the case, a new connection is opened. - * After that the query is passed to the database. - * - * @access public - * @param string Query string - * @return mixed a MDB_result object or MDB_OK on success, a MDB - * or PEAR error on failure - */ - function query($query) - { - $this->log('Auth_Container_MDB2::query() called.', AUTH_LOG_DEBUG); - $err = $this->_prepare(); - if ($err !== true) { - return $err; - } - return $this->db->exec($query); - } - - // }}} - // {{{ _setDefaults() - - /** - * Set some default options - * - * @access private - * @return void - */ - function _setDefaults() - { - $this->options['table'] = 'auth'; - $this->options['usernamecol'] = 'username'; - $this->options['passwordcol'] = 'password'; - $this->options['dsn'] = ''; - $this->options['db_fields'] = ''; - $this->options['cryptType'] = 'md5'; - $this->options['db_options'] = array(); - $this->options['db_where'] = ''; - $this->options['auto_quote'] = true; - } - - // }}} - // {{{ _parseOptions() - - /** - * Parse options passed to the container class - * - * @access private - * @param array - */ - function _parseOptions($array) - { - foreach ($array as $key => $value) { - if (isset($this->options[$key])) { - $this->options[$key] = $value; - } - } - } - - // }}} - // {{{ _quoteDBFields() - - /** - * Quote the db_fields option to avoid the possibility of SQL injection. - * - * @access private - * @return string A properly quoted string that can be concatenated into a - * SELECT clause. - */ - function _quoteDBFields() - { - if (isset($this->options['db_fields'])) { - if (is_array($this->options['db_fields'])) { - if ($this->options['auto_quote']) { - $fields = array(); - foreach ($this->options['db_fields'] as $field) { - $fields[] = $this->db->quoteIdentifier($field, true); - } - return implode(', ', $fields); - } else { - return implode(', ', $this->options['db_fields']); - } - } else { - if (strlen($this->options['db_fields']) > 0) { - if ($this->options['auto_quote']) { - return $this->db->quoteIdentifier($this->options['db_fields'], true); - } else { - return $this->options['db_fields']; - } - } - } - } - - return ''; - } - - // }}} - // {{{ fetchData() - - /** - * Get user information from database - * - * This function uses the given username to fetch - * the corresponding login data from the database - * table. If an account that matches the passed username - * and password is found, the function returns true. - * Otherwise it returns false. - * - * @param string Username - * @param string Password - * @param boolean If true password is secured using a md5 hash - * the frontend and auth are responsible for making sure the container supports - * challenge response password authentication - * @return mixed Error object or boolean - */ - function fetchData($username, $password, $isChallengeResponse=false) - { - $this->log('Auth_Container_MDB2::fetchData() called.', AUTH_LOG_DEBUG); - // Prepare for a database query - $err = $this->_prepare(); - if ($err !== true) { - return PEAR::raiseError($err->getMessage(), $err->getCode()); - } - - //Check if db_fields contains a *, if so assume all columns are selected - if (is_string($this->options['db_fields']) - && strstr($this->options['db_fields'], '*')) { - $sql_from = '*'; - } else { - $sql_from = $this->options['final_usernamecol']. - ", ".$this->options['final_passwordcol']; - - if (strlen($fields = $this->_quoteDBFields()) > 0) { - $sql_from .= ', '.$fields; - } - } - $query = sprintf("SELECT %s FROM %s WHERE %s = %s", - $sql_from, - $this->options['final_table'], - $this->options['final_usernamecol'], - $this->db->quote($username, 'text') - ); - - // check if there is an optional parameter db_where - if ($this->options['db_where'] != '') { - // there is one, so add it to the query - $query .= " AND ".$this->options['db_where']; - } - - $this->log('Running SQL against MDB2: '.$query, AUTH_LOG_DEBUG); - - $res = $this->db->queryRow($query, null, MDB2_FETCHMODE_ASSOC); - if (MDB2::isError($res) || PEAR::isError($res)) { - return PEAR::raiseError($res->getMessage(), $res->getCode()); - } - if (!is_array($res)) { - $this->activeUser = ''; - return false; - } - - // Perform trimming here before the hashing - $password = trim($password, "\r\n"); - $res[$this->options['passwordcol']] = trim($res[$this->options['passwordcol']], "\r\n"); - // If using Challenge Response md5 the pass with the secret - if ($isChallengeResponse) { - $res[$this->options['passwordcol']] = - md5($res[$this->options['passwordcol']].$this->_auth_obj->session['loginchallenege']); - // UGLY cannot avoid without modifying verifyPassword - if ($this->options['cryptType'] == 'md5') { - $res[$this->options['passwordcol']] = md5($res[$this->options['passwordcol']]); - } - } - if ($this->verifyPassword($password, - $res[$this->options['passwordcol']], - $this->options['cryptType'])) { - // Store additional field values in the session - foreach ($res as $key => $value) { - if ($key == $this->options['passwordcol'] || - $key == $this->options['usernamecol']) { - continue; - } - - $this->log('Storing additional field: '.$key, AUTH_LOG_DEBUG); - - // Use reference to the auth object if exists - // This is because the auth session variable can change so a static call to setAuthData does not make sense - $this->_auth_obj->setAuthData($key, $value); - } - return true; - } - - $this->activeUser = $res[$this->options['usernamecol']]; - return false; - } - - // }}} - // {{{ listUsers() - - /** - * Returns a list of users from the container - * - * @return mixed array|PEAR_Error - * @access public - */ - function listUsers() - { - $this->log('Auth_Container_MDB2::listUsers() called.', AUTH_LOG_DEBUG); - $err = $this->_prepare(); - if ($err !== true) { - return PEAR::raiseError($err->getMessage(), $err->getCode()); - } - - $retVal = array(); - - //Check if db_fields contains a *, if so assume all columns are selected - if ( is_string($this->options['db_fields']) - && strstr($this->options['db_fields'], '*')) { - $sql_from = '*'; - } else { - $sql_from = $this->options['final_usernamecol']. - ", ".$this->options['final_passwordcol']; - - if (strlen($fields = $this->_quoteDBFields()) > 0) { - $sql_from .= ', '.$fields; - } - } - - $query = sprintf('SELECT %s FROM %s', - $sql_from, - $this->options['final_table'] - ); - - // check if there is an optional parameter db_where - if ($this->options['db_where'] != '') { - // there is one, so add it to the query - $query .= " WHERE ".$this->options['db_where']; - } - - $this->log('Running SQL against MDB2: '.$query, AUTH_LOG_DEBUG); - - $res = $this->db->queryAll($query, null, MDB2_FETCHMODE_ASSOC); - if (MDB2::isError($res)) { - return PEAR::raiseError($res->getMessage(), $res->getCode()); - } else { - foreach ($res as $user) { - $user['username'] = $user[$this->options['usernamecol']]; - $retVal[] = $user; - } - } - $this->log('Found '.count($retVal).' users.', AUTH_LOG_DEBUG); - return $retVal; - } - - // }}} - // {{{ addUser() - - /** - * Add user to the storage container - * - * @access public - * @param string Username - * @param string Password - * @param mixed Additional information that are stored in the DB - * - * @return mixed True on success, otherwise error object - */ - function addUser($username, $password, $additional = "") - { - $this->log('Auth_Container_MDB2::addUser() called.', AUTH_LOG_DEBUG); - - // Prepare for a database query - $err = $this->_prepare(); - if ($err !== true) { - return PEAR::raiseError($err->getMessage(), $err->getCode()); - } - - if (isset($this->options['cryptType']) && $this->options['cryptType'] == 'none') { - $cryptFunction = 'strval'; - } elseif (isset($this->options['cryptType']) && function_exists($this->options['cryptType'])) { - $cryptFunction = $this->options['cryptType']; - } else { - $cryptFunction = 'md5'; - } - - $password = $cryptFunction($password); - - $additional_key = ''; - $additional_value = ''; - - if (is_array($additional)) { - foreach ($additional as $key => $value) { - if ($this->options['auto_quote']) { - $additional_key .= ', ' . $this->db->quoteIdentifier($key, true); - } else { - $additional_key .= ', ' . $key; - } - $additional_value .= ', ' . $this->db->quote($value, 'text'); - } - } - - $query = sprintf("INSERT INTO %s (%s, %s%s) VALUES (%s, %s%s)", - $this->options['final_table'], - $this->options['final_usernamecol'], - $this->options['final_passwordcol'], - $additional_key, - $this->db->quote($username, 'text'), - $this->db->quote($password, 'text'), - $additional_value - ); - - $this->log('Running SQL against MDB2: '.$query, AUTH_LOG_DEBUG); - - $res = $this->query($query); - - if (MDB2::isError($res)) { - return PEAR::raiseError($res->getMessage(), $res->code); - } - return true; - } - - // }}} - // {{{ removeUser() - - /** - * Remove user from the storage container - * - * @access public - * @param string Username - * - * @return mixed True on success, otherwise error object - */ - function removeUser($username) - { - $this->log('Auth_Container_MDB2::removeUser() called.', AUTH_LOG_DEBUG); - // Prepare for a database query - $err = $this->_prepare(); - if ($err !== true) { - return PEAR::raiseError($err->getMessage(), $err->getCode()); - } - - $query = sprintf("DELETE FROM %s WHERE %s = %s", - $this->options['final_table'], - $this->options['final_usernamecol'], - $this->db->quote($username, 'text') - ); - - // check if there is an optional parameter db_where - if ($this->options['db_where'] != '') { - // there is one, so add it to the query - $query .= " AND ".$this->options['db_where']; - } - - $this->log('Running SQL against MDB2: '.$query, AUTH_LOG_DEBUG); - - $res = $this->query($query); - - if (MDB2::isError($res)) { - return PEAR::raiseError($res->getMessage(), $res->code); - } - return true; - } - - // }}} - // {{{ changePassword() - - /** - * Change password for user in the storage container - * - * @param string Username - * @param string The new password (plain text) - */ - function changePassword($username, $password) - { - $this->log('Auth_Container_MDB2::changePassword() called.', AUTH_LOG_DEBUG); - // Prepare for a database query - $err = $this->_prepare(); - if ($err !== true) { - return PEAR::raiseError($err->getMessage(), $err->getCode()); - } - - if (isset($this->options['cryptType']) && $this->options['cryptType'] == 'none') { - $cryptFunction = 'strval'; - } elseif (isset($this->options['cryptType']) && function_exists($this->options['cryptType'])) { - $cryptFunction = $this->options['cryptType']; - } else { - $cryptFunction = 'md5'; - } - - $password = $cryptFunction($password); - - $query = sprintf("UPDATE %s SET %s = %s WHERE %s = %s", - $this->options['final_table'], - $this->options['final_passwordcol'], - $this->db->quote($password, 'text'), - $this->options['final_usernamecol'], - $this->db->quote($username, 'text') - ); - - // check if there is an optional parameter db_where - if ($this->options['db_where'] != '') { - // there is one, so add it to the query - $query .= " AND ".$this->options['db_where']; - } - - $this->log('Running SQL against MDB2: '.$query, AUTH_LOG_DEBUG); - - $res = $this->query($query); - - if (MDB2::isError($res)) { - return PEAR::raiseError($res->getMessage(), $res->code); - } - return true; - } - - // }}} - // {{{ supportsChallengeResponse() - - /** - * Determine if this container supports - * password authentication with challenge response - * - * @return bool - * @access public - */ - function supportsChallengeResponse() - { - return in_array($this->options['cryptType'], array('md5', 'none', '')); - } - - // }}} - // {{{ getCryptType() - - /** - * Returns the selected crypt type for this container - * - * @return string Function used to crypt the password - */ - function getCryptType() - { - return $this->options['cryptType']; - } - - // }}} - -} -?> diff --git a/pear/Auth/Container/Multiple.php b/pear/Auth/Container/Multiple.php deleted file mode 100644 index 08f131d..0000000 --- a/pear/Auth/Container/Multiple.php +++ /dev/null @@ -1,188 +0,0 @@ - - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version CVS: $Id: Multiple.php 289653 2009-10-15 04:50:43Z aashley $ - * @since File available since Release 1.5.0 - */ - -/** - * Include Auth_Container base class - */ -require_once "Auth/Container.php"; -/** - * Include PEAR package for error handling - */ -require_once "PEAR.php"; - -/** - * Storage driver for using multiple storage drivers in a fall through fashion - * - * This storage driver provides a mechanism for working through multiple - * storage drivers until either one allows successful login or the list is - * exhausted. - * - * This container takes an array of options of the following form: - * - * array( - * array( - * 'type' => , - * 'options' => , - * ), - * ); - * - * Full example: - * - * $options = array( - * array( - * 'type' => 'DB', - * 'options' => array( - * 'dsn' => "mysql://user:password@localhost/database", - * ), - * ), - * array( - * 'type' => 'Array', - * 'options' => array( - * 'cryptType' => 'md5', - * 'users' => array( - * 'admin' => md5('password'), - * ), - * ), - * ), - * ); - * - * $auth = new Auth('Multiple', $options); - * - * @category Authentication - * @package Auth - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version Release: @package_version@ File: $Revision: 289653 $ - * @since File available since Release 1.5.0 - */ - -class Auth_Container_Multiple extends Auth_Container { - - // {{{ properties - - /** - * The options for each container - * - * @var array $options - */ - var $options = array(); - - /** - * The instanciated containers - * - * @var array $containers - */ - var $containers = array(); - - // }}} - // {{{ Auth_Container_Multiple() - - /** - * Constructor for Array Container - * - * @param array $data Options for the container - * @return void - */ - function Auth_Container_Multiple($options) - { - if (!is_array($options)) { - PEAR::raiseError('The options for Auth_Container_Multiple must be an array'); - } - if (count($options) < 1) { - PEAR::raiseError('You must define at least one sub container to use in Auth_Container_Multiple'); - } - foreach ($options as $option) { - if (!isset($option['type'])) { - PEAR::raiseError('No type defined for sub container'); - } - } - $this->options = $options; - } - - // }}} - // {{{ fetchData() - - /** - * Get user information from array - * - * This function uses the given username to fetch the corresponding - * login data from the array. If an account that matches the passed - * username and password is found, the function returns true. - * Otherwise it returns false. - * - * @param string Username - * @param string Password - * @return boolean|PEAR_Error Error object or boolean - */ - function fetchData($user, $pass) - { - $this->log('Auth_Container_Multiple::fetchData() called.', AUTH_LOG_DEBUG); - - foreach ($this->options as $key => $options) { - - $this->log('Using Container '.$key.' of type '.$options['type'].'.', AUTH_LOG_DEBUG); - - if (isset($this->containers[$key]) && is_a($this->containers[$key], 'Auth_Container')) { - - $container = &$this->containers[$key]; - - } else { - - $this->containers[$key] = &$this->_auth_obj->_factory($options['type'], $options['options']); - $this->containers[$key]->_auth_obj = &$this->_auth_obj; - $container = &$this->containers[$key]; - - } - - $result = $container->fetchData($user, $pass); - - if (PEAR::isError($result)) { - - $this->log('Container '.$key.': '.$result->getMessage(), AUTH_LOG_DEBUG); - return $result; - - } elseif ($result == true) { - - $this->log('Container '.$key.': Authentication successful.', AUTH_LOG_DEBUG); - return true; - - } else { - - $this->log('Container '.$key.': Authentication failed.', AUTH_LOG_DEBUG); - - } - - } - - $this->log('Auth_Container_Multiple: All containers rejected user credentials.', AUTH_LOG_DEBUG); - - return false; - - } - - // }}} - -} - -?> diff --git a/pear/Auth/Container/NetVPOPMaild.php b/pear/Auth/Container/NetVPOPMaild.php deleted file mode 100644 index 634fab5..0000000 --- a/pear/Auth/Container/NetVPOPMaild.php +++ /dev/null @@ -1,129 +0,0 @@ - - * @author Stefan Ekman - * @author Martin Jansen - * @author Mika Tuupola - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @link http://pear.php.net/package/Auth - * @since File available since Release 1.2.0 - */ - -/** - * Include Auth_Container base class - */ -require_once 'Auth/Container.php'; -/** - * Include PEAR package for error handling - */ -require_once 'PEAR.php'; -/** - * Include PEAR Net_Vpopmaild package - */ -require_once 'Net/Vpopmaild.php'; - -/** - * Storage driver for Authentication on a Vpopmaild server. - * - * @category Authentication - * @package Auth - * @author Martin Jansen - * @author Mika Tuupola - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version Release: 1.5.4 File: $Revision: 256741 $ - * @link http://pear.php.net/package/Auth - * @since Class available since Release 1.6.0 - */ -class Auth_Container_Vpopmaild extends Auth_Container -{ - - /** - * Vpopmaild Server - * @var string - */ - var $server = 'localhost'; - - /** - * Vpopmaild Server port - * @var string - */ - var $port = 89; - - /** - * Constructor of the container class - * - * @param $server string server or server:port combination - * @return object Returns an error object if something went wrong - */ - function Auth_Container_Vpopmaild($server=null) - { - if (isset($server) && !is_null($server)) { - if (is_array($server)) { - if (isset($server['host'])) { - $this->server = $server['host']; - } - if (isset($server['port'])) { - $this->port = $server['port']; - } - } else { - if (strstr($server, ':')) { - $serverparts = explode(':', trim($server)); - $this->server = $serverparts[0]; - $this->port = $serverparts[1]; - } else { - $this->server = $server; - } - } - } - } - - /** - * fetchData() - * - * Try to login to the Vpopmaild server - * - * @param string username - * @param string password - * - * @return boolean - */ - function fetchData($username, $password) - { - $this->log('Auth_Container_Vpopmaild::fetchData() called.', AUTH_LOG_DEBUG); - $vpopmaild =& new Net_Vpopmaild(); - // Connect - try { - $res = $vpopmaild->connect($this->server, $this->port, $this->method); - } catch (Net_Vpopmaild_FatalException $e) { - $this->log('Connection to Vpopmaild server failed.', AUTH_LOG_DEBUG); - return PEAR::raiseError($e->getMessage(), $e->getCode()); - } - // Authenticate - try { - $result = $vpopmaild->clogin($username, $password); - $vpopmaild->quit(); - } catch (Net_Vpopmaild_Exception $e) { - return PEAR::raiseError($e->getMessage(), $e->getCode()); - } - return $result; - } -} -?> diff --git a/pear/Auth/Container/PEAR.php b/pear/Auth/Container/PEAR.php deleted file mode 100644 index 1de9b82..0000000 --- a/pear/Auth/Container/PEAR.php +++ /dev/null @@ -1,165 +0,0 @@ - - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version CVS: $Id: PEAR.php 289652 2009-10-15 04:42:18Z aashley $ - * @link http://pear.php.net/package/Auth - * @since File available since Release 1.3.0 - */ - -/** - * Include PEAR HTTP_Client. - */ -require_once 'HTTP/Client.php'; -/** - * Include Auth_Container base class - */ -require_once 'Auth/Container.php'; - -/** - * Storage driver for authenticating against PEAR website - * - * This driver provides a method for authenticating against the pear.php.net - * authentication system. - * - * Supports two options: - * - "url": The base URL with schema to authenticate against - * - "karma": An array of karma levels which the user needs one of. - * When empty, no karma level is required. - * - * @category Authentication - * @package Auth - * @author Yavor Shahpasov - * @author Adam Ashley - * @author Adam Harvey - * @copyright 2001-2007 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version Release: @package_version@ File: $Revision: 289652 $ - * @link http://pear.php.net/package/Auth - * @since Class available since Release 1.3.0 - */ -class Auth_Container_Pear extends Auth_Container -{ - // {{{ properties - - /** - * URL to connect to, with schema - * - * @var string - */ - var $url = 'https://pear.php.net/rest-login.php/'; - - /** - * Array of karma levels the user can have. - * A user needs only one of the levels to succeed login. - * No levels mean that only username and password need to match - * - * @var array - */ - var $karma = array(); - - // }}} - // {{{ Auth_Container_Pear() [constructor] - - /** - * Constructor - * - * Accepts options "url" and "karma", see class docs. - * - * @param array $data Array of options - * - * @return void - */ - function Auth_Container_Pear($data = null) - { - if (!is_array($data)) { - PEAR::raiseError('The options for Auth_Container_Pear must be an array'); - } - if (isset($data['karma'])) { - if (is_array($data['karma'])) { - $this->karma = $data['karma']; - } else { - $this->karma = array($data['karma']); - } - } - - if (isset($data['url'])) { - $this->url = $data['url']; - } - } - - // }}} - // {{{ fetchData() - - /** - * Get user information from pear.php.net - * - * This function uses the given username and password to authenticate - * against the pear.php.net website - * - * @param string Username - * @param string Password - * @return mixed Error object or boolean - */ - function fetchData($username, $password) - { - $this->log('Auth_Container_PEAR::fetchData() called.', AUTH_LOG_DEBUG); - - $client = new HTTP_Client; - - $this->log('Auth_Container_PEAR::fetchData() getting salt.', AUTH_LOG_DEBUG); - $code = $client->get($this->url . '/getsalt'); - if ($code instanceof PEAR_Error) { - return $code; - } - if ($code != 200) { - return PEAR::raiseError('Bad response to salt request.', $code); - } - $resp = $client->currentResponse(); - $salt = $resp['body']; - - $this->log('Auth_Container_PEAR::fetchData() calling validate.', AUTH_LOG_DEBUG); - $postOptions = array( - 'username' => $username, - 'password' => md5($salt . md5($password)) - ); - if (is_array($this->karma) && count($this->karma) > 0) { - $postOptions['karma'] = implode(',', $this->karma); - } - - $code = $client->post($this->url . '/validate', $postOptions); - if ($code instanceof PEAR_Error) { - return $code; - } - if ($code != 200) { - return PEAR::raiseError('Bad response to validate request.', $code); - } - $resp = $client->currentResponse(); - - list($code, $message) = explode(' ', $resp['body'], 2); - if ($code != 8) { - return PEAR::raiseError($message, $code); - } - return true; - } - - // }}} - -} -?> diff --git a/pear/Auth/Container/POP3.php b/pear/Auth/Container/POP3.php deleted file mode 100644 index 0bfab1b..0000000 --- a/pear/Auth/Container/POP3.php +++ /dev/null @@ -1,145 +0,0 @@ - - * @author Martin Jansen - * @author Mika Tuupola - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version CVS: $Id: POP3.php 237449 2007-06-12 03:11:27Z aashley $ - * @link http://pear.php.net/package/Auth - * @since File available since Release 1.2.0 - */ - -/** - * Include Auth_Container base class - */ -require_once 'Auth/Container.php'; -/** - * Include PEAR package for error handling - */ -require_once 'PEAR.php'; -/** - * Include PEAR Net_POP3 package - */ -require_once 'Net/POP3.php'; - -/** - * Storage driver for Authentication on a POP3 server. - * - * @category Authentication - * @package Auth - * @author Martin Jansen - * @author Mika Tuupola - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version Release: @package_version@ File: $Revision: 237449 $ - * @link http://pear.php.net/package/Auth - * @since Class available since Release 1.2.0 - */ -class Auth_Container_POP3 extends Auth_Container -{ - - // {{{ properties - - /** - * POP3 Server - * @var string - */ - var $server='localhost'; - - /** - * POP3 Server port - * @var string - */ - var $port='110'; - - /** - * POP3 Authentication method - * - * Prefered POP3 authentication method. Acceptable values: - * Boolean TRUE - Use Net_POP3's autodetection - * String 'DIGEST-MD5','CRAM-MD5','LOGIN','PLAIN','APOP','USER' - * - Attempt this authentication style first - * then fallback to autodetection. - * @var mixed - */ - var $method=true; - - // }}} - // {{{ Auth_Container_POP3() [constructor] - - /** - * Constructor of the container class - * - * @param $server string server or server:port combination - * @return object Returns an error object if something went wrong - */ - function Auth_Container_POP3($server=null) - { - if (isset($server) && !is_null($server)) { - if (is_array($server)) { - if (isset($server['host'])) { - $this->server = $server['host']; - } - if (isset($server['port'])) { - $this->port = $server['port']; - } - if (isset($server['method'])) { - $this->method = $server['method']; - } - } else { - if (strstr($server, ':')) { - $serverparts = explode(':', trim($server)); - $this->server = $serverparts[0]; - $this->port = $serverparts[1]; - } else { - $this->server = $server; - } - } - } - } - - // }}} - // {{{ fetchData() - - /** - * Try to login to the POP3 server - * - * @param string Username - * @param string Password - * @return boolean - */ - function fetchData($username, $password) - { - $this->log('Auth_Container_POP3::fetchData() called.', AUTH_LOG_DEBUG); - $pop3 =& new Net_POP3(); - $res = $pop3->connect($this->server, $this->port, $this->method); - if (!$res) { - $this->log('Connection to POP3 server failed.', AUTH_LOG_DEBUG); - return $res; - } - $result = $pop3->login($username, $password); - $pop3->disconnect(); - return $result; - } - - // }}} - -} -?> diff --git a/pear/Auth/Container/RADIUS.php b/pear/Auth/Container/RADIUS.php deleted file mode 100644 index 585b641..0000000 --- a/pear/Auth/Container/RADIUS.php +++ /dev/null @@ -1,182 +0,0 @@ - - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version CVS: $Id: RADIUS.php 237449 2007-06-12 03:11:27Z aashley $ - * @link http://pear.php.net/package/Auth - * @since File available since Release 1.2.0 - */ - -/** - * Include Auth_Container base class - */ -require_once "Auth/Container.php"; -/** - * Include PEAR Auth_RADIUS package - */ -require_once "Auth/RADIUS.php"; - -/** - * Storage driver for authenticating users against RADIUS servers. - * - * @category Authentication - * @package Auth - * @author Michael Bretterklieber - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version Release: @package_version@ File: $Revision: 237449 $ - * @link http://pear.php.net/package/Auth - * @since Class available since Release 1.2.0 - */ -class Auth_Container_RADIUS extends Auth_Container -{ - - // {{{ properties - - /** - * Contains a RADIUS object - * @var object - */ - var $radius; - - /** - * Contains the authentication type - * @var string - */ - var $authtype; - - // }}} - // {{{ Auth_Container_RADIUS() [constructor] - - /** - * Constructor of the container class. - * - * $options can have these keys: - * 'servers' an array containing an array: servername, port, - * sharedsecret, timeout, maxtries - * 'configfile' The filename of the configuration file - * 'authtype' The type of authentication, one of: PAP, CHAP_MD5, - * MSCHAPv1, MSCHAPv2, default is PAP - * - * @param $options associative array - * @return object Returns an error object if something went wrong - */ - function Auth_Container_RADIUS($options) - { - $this->authtype = 'PAP'; - if (isset($options['authtype'])) { - $this->authtype = $options['authtype']; - } - $classname = 'Auth_RADIUS_' . $this->authtype; - if (!class_exists($classname)) { - PEAR::raiseError("Unknown Authtype, please use one of: " - ."PAP, CHAP_MD5, MSCHAPv1, MSCHAPv2!", 41, PEAR_ERROR_DIE); - } - - $this->radius = new $classname; - - if (isset($options['configfile'])) { - $this->radius->setConfigfile($options['configfile']); - } - - $servers = $options['servers']; - if (is_array($servers)) { - foreach ($servers as $server) { - $servername = $server[0]; - $port = isset($server[1]) ? $server[1] : 0; - $sharedsecret = isset($server[2]) ? $server[2] : 'testing123'; - $timeout = isset($server[3]) ? $server[3] : 3; - $maxtries = isset($server[4]) ? $server[4] : 3; - $this->radius->addServer($servername, $port, $sharedsecret, $timeout, $maxtries); - } - } - - if (!$this->radius->start()) { - PEAR::raiseError($this->radius->getError(), 41, PEAR_ERROR_DIE); - } - } - - // }}} - // {{{ fetchData() - - /** - * Authenticate - * - * @param string Username - * @param string Password - * @return bool true on success, false on reject - */ - function fetchData($username, $password, $challenge = null) - { - $this->log('Auth_Container_RADIUS::fetchData() called.', AUTH_LOG_DEBUG); - - switch($this->authtype) { - case 'CHAP_MD5': - case 'MSCHAPv1': - if (isset($challenge)) { - $this->radius->challenge = $challenge; - $this->radius->chapid = 1; - $this->radius->response = pack('H*', $password); - } else { - require_once 'Crypt/CHAP.php'; - $classname = 'Crypt_' . $this->authtype; - $crpt = new $classname; - $crpt->password = $password; - $this->radius->challenge = $crpt->challenge; - $this->radius->chapid = $crpt->chapid; - $this->radius->response = $crpt->challengeResponse(); - } - break; - - case 'MSCHAPv2': - require_once 'Crypt/CHAP.php'; - $crpt = new Crypt_MSCHAPv2; - $crpt->username = $username; - $crpt->password = $password; - $this->radius->challenge = $crpt->authChallenge; - $this->radius->peerChallenge = $crpt->peerChallenge; - $this->radius->chapid = $crpt->chapid; - $this->radius->response = $crpt->challengeResponse(); - break; - - default: - $this->radius->password = $password; - break; - } - - $this->radius->username = $username; - - $this->radius->putAuthAttributes(); - $result = $this->radius->send(); - if (PEAR::isError($result)) { - return false; - } - - $this->radius->getAttributes(); -// just for debugging -// $this->radius->dumpAttributes(); - - return $result; - } - - // }}} - -} -?> diff --git a/pear/Auth/Container/SAP.php b/pear/Auth/Container/SAP.php deleted file mode 100644 index 58ce168..0000000 --- a/pear/Auth/Container/SAP.php +++ /dev/null @@ -1,179 +0,0 @@ - - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version CVS: $Id: SAP.php 302205 2010-08-14 14:08:08Z clockwerx $ - * @link http://pear.php.net/package/Auth - * @since File available since Release 1.4.0 - */ - -/** - * Include Auth_Container base class - */ -require_once 'Auth/Container.php'; -/** - * Include PEAR for error handling - */ -require_once 'PEAR.php'; - -/** - * Performs authentication against a SAP system using the SAPRFC PHP extension. - * - * When the option GETSSO2 is TRUE (default) - * the Single Sign-On (SSO) ticket is retrieved - * and stored as an Auth attribute called 'sap' - * in order to be reused for consecutive connections. - * - * @category Authentication - * @package Auth - * @author Stoyan Stefanov - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version Release: @package_version@ File: $Revision: 302205 $ - * @since Class available since Release 1.4.0 - */ -class Auth_Container_SAP extends Auth_Container { - - // {{{ properties - - /** - * @var array Default options - */ - var $options = array( - 'CLIENT' => '000', - 'LANG' => 'EN', - 'GETSSO2' => true, - ); - - // }}} - // {{{ Auth_Container_SAP() - - /** - * Class constructor. Checks that required options - * are present and that the SAPRFC extension is loaded - * - * Options that can be passed and their defaults: - *
    -     * array(
    -     *   'ASHOST' => "",
    -     *   'SYSNR'  => "",
    -     *   'CLIENT' => "000",
    -     *   'GWHOST' =>"",
    -     *   'GWSERV' =>"",
    -     *   'MSHOST' =>"",
    -     *   'R3NAME' =>"",
    -     *   'GROUP'  =>"",
    -     *   'LANG'   =>"EN",
    -     *   'TRACE'  =>"",
    -     *   'GETSSO2'=> true
    -     * )
    -     * 
    - * - * @param array array of options. - * @return void - */ - function Auth_Container_SAP($options) - { - $saprfc_loaded = PEAR::loadExtension('saprfc'); - if (!$saprfc_loaded) { - return PEAR::raiseError('Cannot use SAP authentication, ' - .'SAPRFC extension not loaded!'); - } - if (empty($options['R3NAME']) && empty($options['ASHOST'])) { - return PEAR::raiseError('R3NAME or ASHOST required for authentication'); - } - $this->options = array_merge($this->options, $options); - } - - // }}} - // {{{ fetchData() - - /** - * Performs username and password check - * - * @param string Username - * @param string Password - * @return boolean TRUE on success (valid user), FALSE otherwise - */ - function fetchData($username, $password) - { - $this->log('Auth_Container_SAP::fetchData() called.', AUTH_LOG_DEBUG); - $connection_options = $this->options; - $connection_options['USER'] = $username; - $connection_options['PASSWD'] = $password; - $rfc = saprfc_open($connection_options); - if (!$rfc) { - $message = "Couldn't connect to the SAP system."; - $error = $this->getError(); - if ($error['message']) { - $message .= ': ' . $error['message']; - } - PEAR::raiseError($message, null, null, null, @$error['all']); - return false; - } else { - if (!empty($this->options['GETSSO2'])) { - $this->log('Attempting to retrieve SSO2 ticket.', AUTH_LOG_DEBUG); - if ($ticket = @saprfc_get_ticket($rfc)) { - $this->options['MYSAPSSO2'] = $ticket; - unset($this->options['GETSSO2']); - $this->_auth_obj->setAuthData('sap', $this->options); - } else { - PEAR::raiseError("SSO ticket retrieval failed"); - } - } - @saprfc_close($rfc); - return true; - } - - } - - // }}} - // {{{ getError() - - /** - * Retrieves the last error from the SAP connection - * and returns it as an array. - * - * @return array Array of error information - */ - function getError() - { - - $error = array(); - $sap_error = saprfc_error(); - if (empty($err)) { - return $error; - } - $err = explode("n", $sap_error); - foreach ($err AS $line) { - $item = explode(':', $line); - $error[strtolower(trim($item[0]))] = trim($item[1]); - } - $error['all'] = $sap_error; - return $error; - } - - // }}} - -} - -?> diff --git a/pear/Auth/Container/SMBPasswd.php b/pear/Auth/Container/SMBPasswd.php deleted file mode 100644 index 15c3e75..0000000 --- a/pear/Auth/Container/SMBPasswd.php +++ /dev/null @@ -1,182 +0,0 @@ - - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version CVS: $Id: SMBPasswd.php 237449 2007-06-12 03:11:27Z aashley $ - * @link http://pear.php.net/package/Auth - * @since File available since Release 1.2.3 - */ - -/** - * Include PEAR File_SMBPasswd - */ -require_once "File/SMBPasswd.php"; -/** - * Include Auth_Container Base file - */ -require_once "Auth/Container.php"; -/** - * Include PEAR class for error handling - */ -require_once "PEAR.php"; - -/** - * Storage driver for fetching login data from an SAMBA smbpasswd file. - * - * This storage container can handle SAMBA smbpasswd files. - * - * Example: - * $a = new Auth("SMBPasswd", '/usr/local/private/smbpasswd'); - * $a->start(); - * if ($a->getAuth()) { - * printf ("AUTH OK
    \n"); - * $a->logout(); - * } - * - * @category Authentication - * @package Auth - * @author Michael Bretterklieber - * @author Adam Ashley - * @package Auth - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version Release: @package_version@ File: $Revision: 237449 $ - * @link http://pear.php.net/package/Auth - * @since Class available since Release 1.2.3 - */ -class Auth_Container_SMBPasswd extends Auth_Container -{ - - // {{{ properties - - /** - * File_SMBPasswd object - * @var object - */ - var $pwfile; - - // }}} - - // {{{ Auth_Container_SMBPasswd() [constructor] - - /** - * Constructor of the container class - * - * @param $filename string filename for a passwd type file - * @return object Returns an error object if something went wrong - */ - function Auth_Container_SMBPasswd($filename) - { - $this->pwfile = new File_SMBPasswd($filename,0); - - if (!$this->pwfile->load()) { - PEAR::raiseError("Error while reading file contents.", 41, PEAR_ERROR_DIE); - return; - } - - } - - // }}} - // {{{ fetchData() - - /** - * Get user information from pwfile - * - * @param string Username - * @param string Password - * @return boolean - */ - function fetchData($username, $password) - { - $this->log('Auth_Container_SMBPasswd::fetchData() called.', AUTH_LOG_DEBUG); - return $this->pwfile->verifyAccount($username, $password); - } - - // }}} - // {{{ listUsers() - - function listUsers() - { - $this->log('Auth_Container_SMBPasswd::fetchData() called.', AUTH_LOG_DEBUG); - return $this->pwfile->getAccounts(); - } - - // }}} - // {{{ addUser() - - /** - * Add a new user to the storage container - * - * @param string Username - * @param string Password - * @param array Additional information - * - * @return boolean - */ - function addUser($username, $password, $additional = '') - { - $this->log('Auth_Container_SMBPasswd::addUser() called.', AUTH_LOG_DEBUG); - $res = $this->pwfile->addUser($user, $additional['userid'], $pass); - if ($res === true) { - return $this->pwfile->save(); - } - return $res; - } - - // }}} - // {{{ removeUser() - - /** - * Remove user from the storage container - * - * @param string Username - */ - function removeUser($username) - { - $this->log('Auth_Container_SMBPasswd::removeUser() called.', AUTH_LOG_DEBUG); - $res = $this->pwfile->delUser($username); - if ($res === true) { - return $this->pwfile->save(); - } - return $res; - } - - // }}} - // {{{ changePassword() - - /** - * Change password for user in the storage container - * - * @param string Username - * @param string The new password - */ - function changePassword($username, $password) - { - $this->log('Auth_Container_SMBPasswd::changePassword() called.', AUTH_LOG_DEBUG); - $res = $this->pwfile->modUser($username, '', $password); - if ($res === true) { - return $this->pwfile->save(); - } - return $res; - } - - // }}} - -} -?> diff --git a/pear/Auth/Container/SOAP.php b/pear/Auth/Container/SOAP.php deleted file mode 100644 index 2e11e6b..0000000 --- a/pear/Auth/Container/SOAP.php +++ /dev/null @@ -1,229 +0,0 @@ - - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version CVS: $Id: SOAP.php 237449 2007-06-12 03:11:27Z aashley $ - * @link http://pear.php.net/package/Auth - * @since File available since Release 1.2.0 - */ - -/** - * Include Auth_Container base class - */ -require_once "Auth/Container.php"; -/** - * Include PEAR package for error handling - */ -require_once "PEAR.php"; -/** - * Include PEAR SOAP_Client - */ -require_once 'SOAP/Client.php'; - -/** - * Storage driver for fetching login data from SOAP - * - * This class takes one parameter (options), where - * you specify the following fields: endpoint, namespace, - * method, encoding, usernamefield and passwordfield. - * - * You can use specify features of your SOAP service - * by providing its parameters in an associative manner by - * using the '_features' array through the options parameter. - * - * The 'matchpassword' option should be set to false if your - * webservice doesn't return (username,password) pairs, but - * instead returns error when the login is invalid. - * - * Example usage: - * - * 'http://your.soap.service/endpoint', - * 'namespace' => 'urn:/Your/Namespace', - * 'method' => 'get', - * 'encoding' => 'UTF-8', - * 'usernamefield' => 'login', - * 'passwordfield' => 'password', - * 'matchpasswords' => false, - * '_features' => array ( - * 'example_feature' => 'example_value', - * 'another_example' => '' - * ) - * ); - * $auth = new Auth('SOAP', $options, 'loginFunction'); - * $auth->start(); - * - * ... - * - * ?> - * - * @category Authentication - * @package Auth - * @author Bruno Pedro - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version Release: @package_version@ File: $Revision: 237449 $ - * @link http://pear.php.net/package/Auth - * @since Class available since Release 1.2.0 - */ -class Auth_Container_SOAP extends Auth_Container -{ - - // {{{ properties - - /** - * Required options for the class - * @var array - * @access private - */ - var $_requiredOptions = array( - 'endpoint', - 'namespace', - 'method', - 'encoding', - 'usernamefield', - 'passwordfield', - ); - - /** - * Options for the class - * @var array - * @access private - */ - var $_options = array(); - - /** - * Optional SOAP features - * @var array - * @access private - */ - var $_features = array(); - - /** - * The SOAP response - * @var array - * @access public - */ - var $soapResponse = array(); - - /** - * The SOAP client - * @var mixed - * @access public - */ - var $soapClient = null; - - // }}} - // {{{ Auth_Container_SOAP() [constructor] - - /** - * Constructor of the container class - * - * @param $options, associative array with endpoint, namespace, method, - * usernamefield, passwordfield and optional features - */ - function Auth_Container_SOAP($options) - { - $this->_options = $options; - if (!isset($this->_options['matchpasswords'])) { - $this->_options['matchpasswords'] = true; - } - if (!empty($this->_options['_features'])) { - $this->_features = $this->_options['_features']; - unset($this->_options['_features']); - } - } - - // }}} - // {{{ fetchData() - - /** - * Fetch data from SOAP service - * - * Requests the SOAP service for the given username/password - * combination. - * - * @param string Username - * @param string Password - * @return mixed Returns the SOAP response or false if something went wrong - */ - function fetchData($username, $password) - { - $this->log('Auth_Container_SOAP::fetchData() called.', AUTH_LOG_DEBUG); - // check if all required options are set - if (array_intersect($this->_requiredOptions, array_keys($this->_options)) != $this->_requiredOptions) { - return false; - } else { - // create a SOAP client and set encoding - $this->soapClient = new SOAP_Client($this->_options['endpoint']); - $this->soapClient->setEncoding($this->_options['encoding']); - } - - // set the trace option if requested - if (isset($this->_options['trace'])) { - $this->soapClient->__options['trace'] = true; - } - - // set the timeout option if requested - if (isset($this->_options['timeout'])) { - $this->soapClient->__options['timeout'] = $this->_options['timeout']; - } - - // assign username and password fields - $usernameField = new SOAP_Value($this->_options['usernamefield'],'string', $username); - $passwordField = new SOAP_Value($this->_options['passwordfield'],'string', $password); - $SOAPParams = array($usernameField, $passwordField); - - // assign optional features - foreach ($this->_features as $fieldName => $fieldValue) { - $SOAPParams[] = new SOAP_Value($fieldName, 'string', $fieldValue); - } - - // make SOAP call - $this->soapResponse = $this->soapClient->call( - $this->_options['method'], - $SOAPParams, - array('namespace' => $this->_options['namespace']) - ); - - if (!PEAR::isError($this->soapResponse)) { - if ($this->_options['matchpasswords']) { - // check if passwords match - if ($password == $this->soapResponse->{$this->_options['passwordfield']}) { - return true; - } else { - return false; - } - } else { - return true; - } - } else { - return false; - } - } - - // }}} - -} -?> diff --git a/pear/Auth/Container/SOAP5.php b/pear/Auth/Container/SOAP5.php deleted file mode 100644 index 698c685..0000000 --- a/pear/Auth/Container/SOAP5.php +++ /dev/null @@ -1,268 +0,0 @@ - - * @author Marcel Oelke - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version CVS: $Id: SOAP5.php 238999 2007-07-02 08:25:41Z aashley $ - * @since File available since Release 1.4.0 - */ - -/** - * Include Auth_Container base class - */ -require_once "Auth/Container.php"; -/** - * Include PEAR package for error handling - */ -require_once "PEAR.php"; - -/** - * Storage driver for fetching login data from SOAP using the PHP5 Builtin SOAP - * functions. This is a modification of the SOAP Storage driver from Bruno Pedro - * thats using the PEAR SOAP Package. - * - * This class takes one parameter (options), where - * you specify the following fields: - * * location and uri, or wsdl file - * * method to call on the SOAP service - * * usernamefield, the name of the parameter where the username is supplied - * * passwordfield, the name of the parameter where the password is supplied - * * matchpassword, whether to look for the password in the response from - * the function call or assume that no errors means user - * authenticated. - * - * See http://www.php.net/manual/en/ref.soap.php for further details - * on options for the PHP5 SoapClient which are passed through. - * - * Example usage without WSDL: - * - * NULL, - * 'location' => 'http://your.soap.service/endpoint', - * 'uri' => 'urn:/Your/Namespace', - * 'method' => 'checkAuth', - * 'usernamefield' => 'username', - * 'passwordfield' => 'password', - * 'matchpasswords' => false, - * '_features' => array ( - * 'extra_parameter' => 'example_value', - * 'another_parameter' => 'foobar' - * ) - * ); - * - * $auth = new Auth('SOAP5', $options); - * $auth->start(); - * - * ?> - * - * Example usage with WSDL: - * - * 'http://your.soap.service/wsdl', - * 'method' => 'checkAuth', - * 'usernamefield' => 'username', - * 'passwordfield' => 'password', - * 'matchpasswords' => false, - * '_features' => array ( - * 'extra_parameter' => 'example_value', - * 'another_parameter' => 'foobar' - * ) - * ); - * - * $auth = new Auth('SOAP5', $options); - * $auth->start(); - * - * ?> - * - * @category Authentication - * @package Auth - * @author Based upon Auth_Container_SOAP by Bruno Pedro - * @author Marcel Oelke - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version Release: @package_version@ File: $Revision: 238999 $ - * @since Class available since Release 1.4.0 - */ -class Auth_Container_SOAP5 extends Auth_Container -{ - - // {{{ properties - - /** - * Required options for the class - * @var array - * @access private - */ - var $_requiredOptions = array( - 'location', - 'uri', - 'method', - 'usernamefield', - 'passwordfield', - 'wsdl', - ); - - /** - * Options for the class - * @var array - * @access private - */ - var $_options = array(); - - /** - * Optional SOAP features - * @var array - * @access private - */ - var $_features = array(); - - /** - * The SOAP response - * @var array - * @access public - */ - var $soapResponse = array(); - - // }}} - // {{{ Auth_Container_SOAP5() - - /** - * Constructor of the container class - * - * @param $options, associative array with endpoint, namespace, method, - * usernamefield, passwordfield and optional features - */ - function Auth_Container_SOAP5($options) - { - $this->_setDefaults(); - - foreach ($options as $name => $value) { - $this->_options[$name] = $value; - } - - if (!empty($this->_options['_features'])) { - $this->_features = $this->_options['_features']; - unset($this->_options['_features']); - } - } - - // }}} - // {{{ fetchData() - - /** - * Fetch data from SOAP service - * - * Requests the SOAP service for the given username/password - * combination. - * - * @param string Username - * @param string Password - * @return mixed Returns the SOAP response or false if something went wrong - */ - function fetchData($username, $password) - { - $this->log('Auth_Container_SOAP5::fetchData() called.', AUTH_LOG_DEBUG); - $result = $this->_validateOptions(); - if (PEAR::isError($result)) - return $result; - - // create a SOAP client - $soapClient = new SoapClient($this->_options["wsdl"], $this->_options); - - $params = array(); - // first, assign the optional features - foreach ($this->_features as $fieldName => $fieldValue) { - $params[$fieldName] = $fieldValue; - } - // assign username and password ... - $params[$this->_options['usernamefield']] = $username; - $params[$this->_options['passwordfield']] = $password; - - try { - $this->soapResponse = $soapClient->__soapCall($this->_options['method'], $params); - - if ($this->_options['matchpasswords']) { - // check if passwords match - if ($password == $this->soapResponse[$this->_options['passwordfield']]) { - return true; - } else { - return false; - } - } else { - return true; - } - } catch (SoapFault $e) { - return PEAR::raiseError("Error retrieving authentication data. Received SOAP Fault: ".$e->faultstring, $e->faultcode); - } - } - - // }}} - // {{{ _validateOptions() - - /** - * Validate that the options passed to the container class are enough for us to proceed - * - * @access private - * @param array - */ - function _validateOptions() - { - if ( ( is_null($this->_options['wsdl']) - && is_null($this->_options['location']) - && is_null($this->_options['uri'])) - || ( is_null($this->_options['wsdl']) - && ( is_null($this->_options['location']) - || is_null($this->_options['uri'])))) { - return PEAR::raiseError('Either a WSDL file or a location/uri pair must be specified.'); - } - if (is_null($this->_options['method'])) { - return PEAR::raiseError('A method to call on the soap service must be specified.'); - } - return true; - } - - // }}} - // {{{ _setDefaults() - - /** - * Set some default options - * - * @access private - * @return void - */ - function _setDefaults() - { - $this->_options['wsdl'] = null; - $this->_options['location'] = null; - $this->_options['uri'] = null; - $this->_options['method'] = null; - $this->_options['usernamefield'] = 'username'; - $this->_options['passwordfield'] = 'password'; - $this->_options['matchpasswords'] = true; - } - - // }}} - -} -?> diff --git a/pear/Auth/Container/vpopmail.php b/pear/Auth/Container/vpopmail.php deleted file mode 100644 index 2350ca7..0000000 --- a/pear/Auth/Container/vpopmail.php +++ /dev/null @@ -1,88 +0,0 @@ - - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version CVS: $Id: vpopmail.php 237449 2007-06-12 03:11:27Z aashley $ - * @link http://pear.php.net/package/Auth - * @since File available since Release 1.2.0 - */ - -/** - * Include Auth_Container base class - */ -require_once "Auth/Container.php"; -/** - * Include PEAR package for error handling - */ -require_once "PEAR.php"; - -/** - * Storage driver for fetching login data from vpopmail - * - * @category Authentication - * @package Auth - * @author Stanislav Grozev - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version Release: @package_version@ File: $Revision: 237449 $ - * @link http://pear.php.net/package/Auth - * @since Class available since Release 1.2.0 - */ -class Auth_Container_vpopmail extends Auth_Container { - - // {{{ Constructor - - /** - * Constructor of the container class - * - * @return void - */ - function Auth_Container_vpopmail() - { - if (!extension_loaded('vpopmail')) { - return PEAR::raiseError('Cannot use VPOPMail authentication, ' - .'VPOPMail extension not loaded!', 41, PEAR_ERROR_DIE); - } - } - - // }}} - // {{{ fetchData() - - /** - * Get user information from vpopmail - * - * @param string Username - has to be valid email address - * @param string Password - * @return boolean - */ - function fetchData($username, $password) - { - $this->log('Auth_Container_vpopmail::fetchData() called.', AUTH_LOG_DEBUG); - $userdata = array(); - $userdata = preg_split("/@/", $username, 2); - $result = @vpopmail_auth_user($userdata[0], $userdata[1], $password); - - return $result; - } - - // }}} - -} -?> diff --git a/pear/Auth/Controller.php b/pear/Auth/Controller.php deleted file mode 100644 index 92d9f80..0000000 --- a/pear/Auth/Controller.php +++ /dev/null @@ -1,302 +0,0 @@ - - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version CVS: $Id: Controller.php 237449 2007-06-12 03:11:27Z aashley $ - * @link http://pear.php.net/package/Auth - * @since File available since Release 1.3.0 - */ - -/** - * Controlls access to a group of php access - * and redirects to a predefined login page as - * needed - * - * In all pages - * - * include_once('Auth.php'); - * include_once('Auth/Controller.php'); - * $_auth = new Auth('File', 'passwd'); - * $authController = new Auth_Controller($_auth, 'login.php', 'index.php'); - * $authController->start(); - * - * - * In login.php - * - * include_once('Auth.php'); - * include_once('Auth/Controller.php'); - * $_auth = new Auth('File', 'passwd'); - * $authController = new Auth_Controller($_auth, 'login.php', 'index.php'); - * $authController->start(); - * if( $authController->isAuthorised() ){ - * $authController->redirectBack(); - * } - * - * - * @category Authentication - * @author Yavor Shahpasov - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version Release: @package_version@ File: $Revision: 237449 $ - * @link http://pear.php.net/package/Auth - * @since Class available since Release 1.3.0 - */ -class Auth_Controller -{ - - // {{{ properties - - /** - * The Auth instance this controller is managing - * - * @var object Auth - */ - var $auth = null; - - /** - * The login URL - * @var string - * */ - var $login = null; - - /** - * The default index page to use when the caller page is not set - * - * @var string - */ - var $default = null; - - /** - * If this is set to true after a succesfull login the - * Auth_Controller::redirectBack() is invoked automatically - * - * @var boolean - */ - var $autoRedirectBack = false; - - // }}} - // {{{ Auth_Controller() [constructor] - - /** - * Constructor - * - * @param Auth An auth instance - * @param string The login page - * @param string The default page to go to if return page is not set - * @param array Some rules about which urls need to be sent to the login page - * @return void - * @todo Add a list of urls which need redirection - */ - function Auth_Controller(&$auth_obj, $login='login.php', $default='index.php', $accessList=array()) - { - $this->auth =& $auth_obj; - $this->_loginPage = $login; - $this->_defaultPage = $default; - @session_start(); - if (!empty($_GET['return']) && $_GET['return'] && !strstr($_GET['return'], $this->_loginPage)) { - $this->auth->setAuthData('returnUrl', $_GET['return']); - } - - if(!empty($_GET['authstatus']) && $this->auth->status == '') { - $this->auth->status = $_GET['authstatus']; - } - } - - // }}} - // {{{ setAutoRedirectBack() - - /** - * Enables auto redirection when login is done - * - * @param bool Sets the autoRedirectBack flag to this - * @see Auth_Controller::autoRedirectBack - * @return void - */ - function setAutoRedirectBack($flag = true) - { - $this->autoRedirectBack = $flag; - } - - // }}} - // {{{ redirectBack() - - /** - * Redirects Back to the calling page - * - * @return void - */ - function redirectBack() - { - // If redirectback go there - // else go to the default page - - $returnUrl = $this->auth->getAuthData('returnUrl'); - if(!$returnUrl) { - $returnUrl = $this->_defaultPage; - } - - // Add some entropy to the return to make it unique - // avoind problems with cached pages and proxies - if(strpos($returnUrl, '?') === false) { - $returnUrl .= '?'; - } - $returnUrl .= uniqid(''); - - // Track the auth status - if($this->auth->status != '') { - $url .= '&authstatus='.$this->auth->status; - } - header('Location:'.$returnUrl); - print("You could not be redirected to $returnUrl"); - } - - // }}} - // {{{ redirectLogin() - - /** - * Redirects to the login Page if not authorised - * - * put return page on the query or in auth - * - * @return void - */ - function redirectLogin() - { - // Go to the login Page - - // For Auth, put some check to avoid infinite redirects, this should at least exclude - // the login page - - $url = $this->_loginPage; - if(strpos($url, '?') === false) { - $url .= '?'; - } - - if(!strstr($_SERVER['PHP_SELF'], $this->_loginPage)) { - $url .= 'return='.urlencode($_SERVER['PHP_SELF']); - } - - // Track the auth status - if($this->auth->status != '') { - $url .= '&authstatus='.$this->auth->status; - } - - header('Location:'.$url); - print("You could not be redirected to $url"); - } - - // }}} - // {{{ start() - - /** - * Starts the Auth Procedure - * - * If the page requires login the user is redirected to the login page - * otherwise the Auth::start is called to initialize Auth - * - * @return void - * @todo Implement an access list which specifies which urls/pages need login and which do not - */ - function start() - { - // Check the accessList here - // ACL should be a list of urls with allow/deny - // If allow set allowLogin to false - // Some wild card matching should be implemented ?,* - if(!strstr($_SERVER['PHP_SELF'], $this->_loginPage) && !$this->auth->checkAuth()) { - $this->redirectLogin(); - } else { - $this->auth->start(); - // Logged on and on login page - if(strstr($_SERVER['PHP_SELF'], $this->_loginPage) && $this->auth->checkAuth()){ - $this->autoRedirectBack ? - $this->redirectBack() : - null ; - } - } - - - } - - // }}} - // {{{ isAuthorised() - - /** - * Checks is the user is logged on - * @see Auth::checkAuth() - */ - function isAuthorised() - { - return($this->auth->checkAuth()); - } - - // }}} - // {{{ checkAuth() - - /** - * Proxy call to auth - * @see Auth::checkAuth() - */ - function checkAuth() - { - return($this->auth->checkAuth()); - } - - // }}} - // {{{ logout() - - /** - * Proxy call to auth - * @see Auth::logout() - */ - function logout() - { - return($this->auth->logout()); - } - - // }}} - // {{{ getUsername() - - /** - * Proxy call to auth - * @see Auth::getUsername() - */ - function getUsername() - { - return($this->auth->getUsername()); - } - - // }}} - // {{{ getStatus() - - /** - * Proxy call to auth - * @see Auth::getStatus() - */ - function getStatus() - { - return($this->auth->getStatus()); - } - - // }}} - -} - -?> diff --git a/pear/Auth/Frontend/Html.php b/pear/Auth/Frontend/Html.php deleted file mode 100644 index 6719e92..0000000 --- a/pear/Auth/Frontend/Html.php +++ /dev/null @@ -1,142 +0,0 @@ - - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version CVS: $Id: Html.php 237449 2007-06-12 03:11:27Z aashley $ - * @link http://pear.php.net/package/Auth - * @since File available since Release 1.3.0 - */ - -/** - * Standard Html Login form - * - * @category Authentication - * @package Auth - * @author Yavor Shahpasov - * @author Adam Ashley - * @copyright 2001-2006 The PHP Group - * @license http://www.php.net/license/3_01.txt PHP License 3.01 - * @version Release: @package_version@ File: $Revision: 237449 $ - * @link http://pear.php.net/package/Auth - * @since Class available since Release 1.3.0 - */ -class Auth_Frontend_Html { - - // {{{ render() - - /** - * Displays the login form - * - * @param object The calling auth instance - * @param string The previously used username - * @return void - */ - function render(&$caller, $username = '') { - $loginOnClick = 'return true;'; - - // Try To Use Challene response - // TODO javascript might need some improvement for work on other browsers - if($caller->advancedsecurity && $caller->storage->supportsChallengeResponse() ) { - - // Init the secret cookie - $caller->session['loginchallenege'] = md5(microtime()); - - print "\n"; - print ''."\n";; - print "\n"; - - $loginOnClick = ' return securePassword(); '; - } - - print '
    '."\n"; - - $status = ''; - if (!empty($caller->status) && $caller->status == AUTH_EXPIRED) { - $status = 'Your session has expired. Please login again!'."\n"; - } else if (!empty($caller->status) && $caller->status == AUTH_IDLED) { - $status = 'You have been idle for too long. Please login again!'."\n"; - } else if (!empty ($caller->status) && $caller->status == AUTH_WRONG_LOGIN) { - $status = 'Wrong login data!'."\n"; - } else if (!empty ($caller->status) && $caller->status == AUTH_SECURITY_BREACH) { - $status = 'Security problem detected. '."\n"; - } - - print '
    '."\n"; - print '
    '."\n"; - print ''."\n"; - print ' '."\n"; - print ''."\n"; - print ''."\n"; - print ' '."\n"; - print ' '."\n"; - print ''."\n"; - print ''."\n"; - print ' '."\n"; - print ' '."\n"; - print ''."\n"; - print ''."\n"; - - //onClick=" '.$loginOnClick.' " - print ' '."\n"; - print ''."\n"; - print '
    Login ' - .$status.'
    Username:
    Password:
    '."\n"; - - // Might be a good idea to make the variable name variable - print ''; - print ''."\n"; - print ''."\n"; - } - - // }}} - -} - -?> diff --git a/pear/Console/Getopt.php b/pear/Console/Getopt.php deleted file mode 100644 index eb65df2..0000000 --- a/pear/Console/Getopt.php +++ /dev/null @@ -1,360 +0,0 @@ - - * @license http://www.php.net/license/3_0.txt PHP 3.0 - * @version CVS: $Id: Getopt.php 306067 2010-12-08 00:13:31Z dufuz $ - * @link http://pear.php.net/package/Console_Getopt - */ - -require_once 'PEAR.php'; - -/** - * Command-line options parsing class. - * - * @category Console - * @package Console_Getopt - * @author Andrei Zmievski - * @license http://www.php.net/license/3_0.txt PHP 3.0 - * @link http://pear.php.net/package/Console_Getopt - */ -class Console_Getopt -{ - - /** - * Parses the command-line options. - * - * The first parameter to this function should be the list of command-line - * arguments without the leading reference to the running program. - * - * The second parameter is a string of allowed short options. Each of the - * option letters can be followed by a colon ':' to specify that the option - * requires an argument, or a double colon '::' to specify that the option - * takes an optional argument. - * - * The third argument is an optional array of allowed long options. The - * leading '--' should not be included in the option name. Options that - * require an argument should be followed by '=', and options that take an - * option argument should be followed by '=='. - * - * The return value is an array of two elements: the list of parsed - * options and the list of non-option command-line arguments. Each entry in - * the list of parsed options is a pair of elements - the first one - * specifies the option, and the second one specifies the option argument, - * if there was one. - * - * Long and short options can be mixed. - * - * Most of the semantics of this function are based on GNU getopt_long(). - * - * @param array $args an array of command-line arguments - * @param string $short_options specifies the list of allowed short options - * @param array $long_options specifies the list of allowed long options - * @param boolean $skip_unknown suppresses Console_Getopt: unrecognized option - * - * @return array two-element array containing the list of parsed options and - * the non-option arguments - */ - public static function getopt2($args, $short_options, $long_options = null, $skip_unknown = false) - { - return Console_Getopt::doGetopt(2, $args, $short_options, $long_options, $skip_unknown); - } - - /** - * This function expects $args to start with the script name (POSIX-style). - * Preserved for backwards compatibility. - * - * @param array $args an array of command-line arguments - * @param string $short_options specifies the list of allowed short options - * @param array $long_options specifies the list of allowed long options - * - * @see getopt2() - * @return array two-element array containing the list of parsed options and - * the non-option arguments - */ - public static function getopt($args, $short_options, $long_options = null, $skip_unknown = false) - { - return Console_Getopt::doGetopt(1, $args, $short_options, $long_options, $skip_unknown); - } - - /** - * The actual implementation of the argument parsing code. - * - * @param int $version Version to use - * @param array $args an array of command-line arguments - * @param string $short_options specifies the list of allowed short options - * @param array $long_options specifies the list of allowed long options - * @param boolean $skip_unknown suppresses Console_Getopt: unrecognized option - * - * @return array - */ - public static function doGetopt($version, $args, $short_options, $long_options = null, $skip_unknown = false) - { - // in case you pass directly readPHPArgv() as the first arg - if (PEAR::isError($args)) { - return $args; - } - - if (empty($args)) { - return array(array(), array()); - } - - $non_opts = $opts = array(); - - settype($args, 'array'); - - if ($long_options) { - sort($long_options); - } - - /* - * Preserve backwards compatibility with callers that relied on - * erroneous POSIX fix. - */ - if ($version < 2) { - if (isset($args[0]{0}) && $args[0]{0} != '-') { - array_shift($args); - } - } - - reset($args); - while (list($i, $arg) = each($args)) { - /* The special element '--' means explicit end of - options. Treat the rest of the arguments as non-options - and end the loop. */ - if ($arg == '--') { - $non_opts = array_merge($non_opts, array_slice($args, $i + 1)); - break; - } - - if ($arg{0} != '-' || (strlen($arg) > 1 && $arg{1} == '-' && !$long_options)) { - $non_opts = array_merge($non_opts, array_slice($args, $i)); - break; - } elseif (strlen($arg) > 1 && $arg{1} == '-') { - $error = Console_Getopt::_parseLongOption(substr($arg, 2), - $long_options, - $opts, - $args, - $skip_unknown); - if (PEAR::isError($error)) { - return $error; - } - } elseif ($arg == '-') { - // - is stdin - $non_opts = array_merge($non_opts, array_slice($args, $i)); - break; - } else { - $error = Console_Getopt::_parseShortOption(substr($arg, 1), - $short_options, - $opts, - $args, - $skip_unknown); - if (PEAR::isError($error)) { - return $error; - } - } - } - - return array($opts, $non_opts); - } - - /** - * Parse short option - * - * @param string $arg Argument - * @param string[] $short_options Available short options - * @param string[][] &$opts - * @param string[] &$args - * @param boolean $skip_unknown suppresses Console_Getopt: unrecognized option - * - * @return void - */ - public static function _parseShortOption($arg, $short_options, &$opts, &$args, $skip_unknown) - { - for ($i = 0; $i < strlen($arg); $i++) { - $opt = $arg{$i}; - $opt_arg = null; - - /* Try to find the short option in the specifier string. */ - if (($spec = strstr($short_options, $opt)) === false || $arg{$i} == ':') { - if ($skip_unknown === true) { - break; - } - - $msg = "Console_Getopt: unrecognized option -- $opt"; - return PEAR::raiseError($msg); - } - - if (strlen($spec) > 1 && $spec{1} == ':') { - if (strlen($spec) > 2 && $spec{2} == ':') { - if ($i + 1 < strlen($arg)) { - /* Option takes an optional argument. Use the remainder of - the arg string if there is anything left. */ - $opts[] = array($opt, substr($arg, $i + 1)); - break; - } - } else { - /* Option requires an argument. Use the remainder of the arg - string if there is anything left. */ - if ($i + 1 < strlen($arg)) { - $opts[] = array($opt, substr($arg, $i + 1)); - break; - } else if (list(, $opt_arg) = each($args)) { - /* Else use the next argument. */; - if (Console_Getopt::_isShortOpt($opt_arg) - || Console_Getopt::_isLongOpt($opt_arg)) { - $msg = "option requires an argument --$opt"; - return PEAR::raiseError("Console_Getopt:" . $msg); - } - } else { - $msg = "option requires an argument --$opt"; - return PEAR::raiseError("Console_Getopt:" . $msg); - } - } - } - - $opts[] = array($opt, $opt_arg); - } - } - - /** - * Checks if an argument is a short option - * - * @param string $arg Argument to check - * - * @return bool - */ - private static function _isShortOpt($arg) - { - return strlen($arg) == 2 && $arg[0] == '-' - && preg_match('/[a-zA-Z]/', $arg[1]); - } - - /** - * Checks if an argument is a long option - * - * @param string $arg Argument to check - * - * @return bool - */ - private static function _isLongOpt($arg) - { - return strlen($arg) > 2 && $arg[0] == '-' && $arg[1] == '-' && - preg_match('/[a-zA-Z]+$/', substr($arg, 2)); - } - - /** - * Parse long option - * - * @param string $arg Argument - * @param string[] $long_options Available long options - * @param string[][] &$opts - * @param string[] &$args - * - * @return void|PEAR_Error - */ - private static function _parseLongOption($arg, $long_options, &$opts, &$args, $skip_unknown) - { - @list($opt, $opt_arg) = explode('=', $arg, 2); - - $opt_len = strlen($opt); - - for ($i = 0; $i < count($long_options); $i++) { - $long_opt = $long_options[$i]; - $opt_start = substr($long_opt, 0, $opt_len); - - $long_opt_name = str_replace('=', '', $long_opt); - - /* Option doesn't match. Go on to the next one. */ - if ($long_opt_name != $opt) { - continue; - } - - $opt_rest = substr($long_opt, $opt_len); - - /* Check that the options uniquely matches one of the allowed - options. */ - if ($i + 1 < count($long_options)) { - $next_option_rest = substr($long_options[$i + 1], $opt_len); - } else { - $next_option_rest = ''; - } - - if ($opt_rest != '' && $opt{0} != '=' && - $i + 1 < count($long_options) && - $opt == substr($long_options[$i+1], 0, $opt_len) && - $next_option_rest != '' && - $next_option_rest{0} != '=') { - - $msg = "Console_Getopt: option --$opt is ambiguous"; - return PEAR::raiseError($msg); - } - - if (substr($long_opt, -1) == '=') { - if (substr($long_opt, -2) != '==') { - /* Long option requires an argument. - Take the next argument if one wasn't specified. */; - if (!strlen($opt_arg) && !(list(, $opt_arg) = each($args))) { - $msg = "Console_Getopt: option requires an argument --$opt"; - return PEAR::raiseError($msg); - } - - if (Console_Getopt::_isShortOpt($opt_arg) - || Console_Getopt::_isLongOpt($opt_arg)) { - $msg = "Console_Getopt: option requires an argument --$opt"; - return PEAR::raiseError($msg); - } - } - } else if ($opt_arg) { - $msg = "Console_Getopt: option --$opt doesn't allow an argument"; - return PEAR::raiseError($msg); - } - - $opts[] = array('--' . $opt, $opt_arg); - return; - } - - if ($skip_unknown === true) { - return; - } - - return PEAR::raiseError("Console_Getopt: unrecognized option --$opt"); - } - - /** - * Safely read the $argv PHP array across different PHP configurations. - * Will take care on register_globals and register_argc_argv ini directives - * - * @return mixed the $argv PHP array or PEAR error if not registered - */ - public static function readPHPArgv() - { - global $argv; - if (!is_array($argv)) { - if (!@is_array($_SERVER['argv'])) { - if (!@is_array($GLOBALS['HTTP_SERVER_VARS']['argv'])) { - $msg = "Could not read cmd args (register_argc_argv=Off?)"; - return PEAR::raiseError("Console_Getopt: " . $msg); - } - return $GLOBALS['HTTP_SERVER_VARS']['argv']; - } - return $_SERVER['argv']; - } - return $argv; - } - -} diff --git a/pear/HTTP.php b/pear/HTTP.php deleted file mode 100644 index 6ebd6db..0000000 --- a/pear/HTTP.php +++ /dev/null @@ -1,548 +0,0 @@ - - * @author Sterling Hughes - * @author Tomas V.V.Cox - * @author Richard Heyes - * @author Philippe Jausions - * @author Michael Wallner - * @copyright 2002-2008 The Authors - * @license http://www.opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id: HTTP.php,v 1.56 2008/08/31 20:15:43 jausions Exp $ - * @link http://pear.php.net/package/HTTP - */ - -/** - * Miscellaneous HTTP Utilities - * - * PEAR::HTTP provides static shorthand methods for generating HTTP dates, - * issueing HTTP HEAD requests, building absolute URIs, firing redirects and - * negotiating user preferred language. - * - * @category HTTP - * @package HTTP - * @author Stig Bakken - * @author Sterling Hughes - * @author Tomas V.V.Cox - * @author Richard Heyes - * @author Philippe Jausions - * @author Michael Wallner - * @license http://www.opensource.org/licenses/bsd-license.php New BSD License - * @abstract - * @version Release: $Revision: 1.56 $ - * @link http://pear.php.net/package/HTTP - */ -class HTTP -{ - /** - * Formats a RFC compliant GMT date HTTP header. This function honors the - * "y2k_compliance" php.ini directive and formats the GMT date corresponding - * to either RFC850 or RFC822. - * - * @param mixed $time unix timestamp or date (default = current time) - * - * @return mixed GMT date string, or false for an invalid $time parameter - * @access public - * @static - */ - function Date($time = null) - { - if (!isset($time)) { - $time = time(); - } elseif (!is_numeric($time) && (-1 === $time = strtotime($time))) { - return false; - } - - // RFC822 or RFC850 - $format = ini_get('y2k_compliance') ? 'D, d M Y' : 'l, d-M-y'; - - return gmdate($format .' H:i:s \G\M\T', $time); - } - - /** - * Negotiates language with the user's browser through the Accept-Language - * HTTP header or the user's host address. Language codes are generally in - * the form "ll" for a language spoken in only one country, or "ll-CC" for a - * language spoken in a particular country. For example, U.S. English is - * "en-US", while British English is "en-UK". Portugese as spoken in - * Portugal is "pt-PT", while Brazilian Portugese is "pt-BR". - * - * Quality factors in the Accept-Language: header are supported, e.g.: - * Accept-Language: en-UK;q=0.7, en-US;q=0.6, no, dk;q=0.8 - * - * - * require_once 'HTTP.php'; - * $langs = array( - * 'en' => 'locales/en', - * 'en-US' => 'locales/en', - * 'en-UK' => 'locales/en', - * 'de' => 'locales/de', - * 'de-DE' => 'locales/de', - * 'de-AT' => 'locales/de', - * ); - * $neg = HTTP::negotiateLanguage($langs); - * $dir = $langs[$neg]; - * - * - * @param array $supported An associative array of supported languages, - * whose values must evaluate to true. - * @param string $default The default language to use if none is found. - * - * @return string The negotiated language result or the supplied default. - * @static - * @access public - */ - function negotiateLanguage($supported, $default = 'en-US') - { - $supp = array(); - foreach ($supported as $lang => $isSupported) { - if ($isSupported) { - $supp[strtolower($lang)] = $lang; - } - } - - if (!count($supp)) { - return $default; - } - - if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { - $match = HTTP::_matchAccept($_SERVER['HTTP_ACCEPT_LANGUAGE'], - $supp); - if (!is_null($match)) { - return $match; - } - } - - if (isset($_SERVER['REMOTE_HOST'])) { - $lang = strtolower(end($h = explode('.', $_SERVER['REMOTE_HOST']))); - if (isset($supp[$lang])) { - return $supp[$lang]; - } - } - - return $default; - } - - /** - * Negotiates charset with the user's browser through the Accept-Charset - * HTTP header. - * - * Quality factors in the Accept-Charset: header are supported, e.g.: - * Accept-Language: en-UK;q=0.7, en-US;q=0.6, no, dk;q=0.8 - * - * - * require_once 'HTTP.php'; - * $charsets = array( - * 'UTF-8', - * 'ISO-8859-1', - * ); - * $charset = HTTP::negotiateCharset($charsets); - * - * - * @param array $supported An array of supported charsets - * @param string $default The default charset to use if none is found. - * - * @return string The negotiated language result or the supplied default. - * @static - * @author Philippe Jausions - * @access public - * @since 1.4.1 - */ - function negotiateCharset($supported, $default = 'ISO-8859-1') - { - $supp = array(); - foreach ($supported as $charset) { - $supp[strtolower($charset)] = $charset; - } - - if (!count($supp)) { - return $default; - } - - if (isset($_SERVER['HTTP_ACCEPT_CHARSET'])) { - $match = HTTP::_matchAccept($_SERVER['HTTP_ACCEPT_CHARSET'], - $supp); - if (!is_null($match)) { - return $match; - } - } - - return $default; - } - - /** - * Negotiates content type with the user's browser through the Accept - * HTTP header. - * - * Quality factors in the Accept: header are supported, e.g.: - * Accept: application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8 - * - * - * require_once 'HTTP.php'; - * $contentType = array( - * 'application/xhtml+xml', - * 'application/xml', - * 'text/html', - * 'text/plain', - * ); - * $mime = HTTP::negotiateContentType($contentType); - * - * - * @param array $supported An associative array of supported MIME types. - * @param string $default The default type to use if none match. - * - * @return string The negotiated MIME type result or the supplied default. - * @static - * @author Philippe Jausions - * @access public - * @since 1.4.1 - */ - function negotiateMimeType($supported, $default) - { - $supp = array(); - foreach ($supported as $type) { - $supp[strtolower($type)] = $type; - } - - if (!count($supp)) { - return $default; - } - - if (isset($_SERVER['HTTP_ACCEPT'])) { - $accepts = HTTP::_sortAccept($_SERVER['HTTP_ACCEPT']); - - foreach ($accepts as $type => $q) { - if (substr($type, -2) != '/*') { - if (isset($supp[$type])) { - return $supp[$type]; - } - continue; - } - if ($type == '*/*') { - return array_shift($supp); - } - list($general, $specific) = explode('/', $type); - $general .= '/'; - $len = strlen($general); - foreach ($supp as $mime => $t) { - if (strncasecmp($general, $mime, $len) == 0) { - return $t; - } - } - } - } - - return $default; - } - - /** - * Parses a weighed "Accept" HTTP header and matches it against a list - * of supported options - * - * @param string $header The HTTP "Accept" header to parse - * @param array $supported A list of supported values - * - * @return string|NULL a matched option, or NULL if no match - * @access private - * @static - */ - function _matchAccept($header, $supported) - { - $matches = HTTP::_sortAccept($header); - foreach ($matches as $key => $q) { - if (isset($supported[$key])) { - return $supported[$key]; - } - } - // If any (i.e. "*") is acceptable, return the first supported format - if (isset($matches['*'])) { - return array_shift($supported); - } - return null; - } - - /** - * Parses and sorts a weighed "Accept" HTTP header - * - * @param string $header The HTTP "Accept" header to parse - * - * @return array a sorted list of "accept" options - * @access private - * @static - */ - function _sortAccept($header) - { - $matches = array(); - foreach (explode(',', $header) as $option) { - $option = array_map('trim', explode(';', $option)); - - $l = strtolower($option[0]); - if (isset($option[1])) { - $q = (float) str_replace('q=', '', $option[1]); - } else { - $q = null; - // Assign default low weight for generic values - if ($l == '*/*') { - $q = 0.01; - } elseif (substr($l, -1) == '*') { - $q = 0.02; - } - } - // Unweighted values, get high weight by their position in the - // list - $matches[$l] = isset($q) ? $q : 1000 - count($matches); - } - arsort($matches, SORT_NUMERIC); - return $matches; - } - - /** - * Sends a "HEAD" HTTP command to a server and returns the headers - * as an associative array. - * - * Example output could be: - * - * Array - * ( - * [response_code] => 200 // The HTTP response code - * [response] => HTTP/1.1 200 OK // The full HTTP response string - * [Date] => Fri, 11 Jan 2002 01:41:44 GMT - * [Server] => Apache/1.3.20 (Unix) PHP/4.1.1 - * [X-Powered-By] => PHP/4.1.1 - * [Connection] => close - * [Content-Type] => text/html - * ) - * - * - * @param string $url A valid URL, e.g.: http://pear.php.net/credits.php - * @param integer $timeout Timeout in seconds (default = 10) - * - * @return array Returns associative array of response headers on success - * or PEAR error on failure. - * @static - * @access public - * @see HTTP_Client::head() - * @see HTTP_Request - */ - function head($url, $timeout = 10) - { - $p = parse_url($url); - if (!isset($p['scheme'])) { - $p = parse_url(HTTP::absoluteURI($url)); - } elseif ($p['scheme'] != 'http') { - return HTTP::raiseError('Unsupported protocol: '. $p['scheme']); - } - - $port = isset($p['port']) ? $p['port'] : 80; - - if (!$fp = @fsockopen($p['host'], $port, $eno, $estr, $timeout)) { - return HTTP::raiseError("Connection error: $estr ($eno)"); - } - - $path = !empty($p['path']) ? $p['path'] : '/'; - $path .= !empty($p['query']) ? '?' . $p['query'] : ''; - - fputs($fp, "HEAD $path HTTP/1.0\r\n"); - fputs($fp, 'Host: ' . $p['host'] . ':' . $port . "\r\n"); - fputs($fp, "Connection: close\r\n\r\n"); - - $response = rtrim(fgets($fp, 4096)); - if (preg_match("|^HTTP/[^\s]*\s(.*?)\s|", $response, $status)) { - $headers['response_code'] = $status[1]; - } - $headers['response'] = $response; - - while ($line = fgets($fp, 4096)) { - if (!trim($line)) { - break; - } - if (($pos = strpos($line, ':')) !== false) { - $header = substr($line, 0, $pos); - $value = trim(substr($line, $pos + 1)); - - $headers[$header] = $value; - } - } - fclose($fp); - return $headers; - } - - /** - * This function redirects the client. This is done by issuing - * a "Location" header and exiting if wanted. If you set $rfc2616 to true - * HTTP will output a hypertext note with the location of the redirect. - * - * @param string $url URL where the redirect should go to. - * @param bool $exit Whether to exit immediately after redirection. - * @param bool $rfc2616 Wheter to output a hypertext note where we're - * redirecting to (Redirecting to - * ....) - * - * @return boolean Returns TRUE on succes (or exits) or FALSE if headers - * have already been sent. - * @static - * @access public - */ - function redirect($url, $exit = true, $rfc2616 = false) - { - if (headers_sent()) { - return false; - } - - $url = HTTP::absoluteURI($url); - header('Location: '. $url); - - if ($rfc2616 && isset($_SERVER['REQUEST_METHOD']) - && $_SERVER['REQUEST_METHOD'] != 'HEAD') { - echo ' -

    Redirecting to: ' - .htmlspecialchars($url).'.

    -'; - } - if ($exit) { - exit; - } - return true; - } - - /** - * This function returns the absolute URI for the partial URL passed. - * The current scheme (HTTP/HTTPS), host server, port, current script - * location are used if necessary to resolve any relative URLs. - * - * Offsets potentially created by PATH_INFO are taken care of to resolve - * relative URLs to the current script. - * - * You can choose a new protocol while resolving the URI. This is - * particularly useful when redirecting a web browser using relative URIs - * and to switch from HTTP to HTTPS, or vice-versa, at the same time. - * - * @param string $url Absolute or relative URI the redirect should - * go to. - * @param string $protocol Protocol to use when redirecting URIs. - * @param integer $port A new port number. - * - * @return string The absolute URI. - * @author Philippe Jausions - * @static - * @access public - */ - function absoluteURI($url = null, $protocol = null, $port = null) - { - // filter CR/LF - $url = str_replace(array("\r", "\n"), ' ', $url); - - // Mess around protocol and port with already absolute URIs - if (preg_match('!^([a-z0-9]+)://!i', $url)) { - if (empty($protocol) && empty($port)) { - return $url; - } - if (!empty($protocol)) { - $url = $protocol .':'. end($array = explode(':', $url, 2)); - } - if (!empty($port)) { - $url = preg_replace('!^(([a-z0-9]+)://[^/:]+)(:[\d]+)?!i', - '\1:'. $port, $url); - } - return $url; - } - - $host = 'localhost'; - if (!empty($_SERVER['HTTP_HOST'])) { - list($host) = explode(':', $_SERVER['HTTP_HOST']); - } elseif (!empty($_SERVER['SERVER_NAME'])) { - list($host) = explode(':', $_SERVER['SERVER_NAME']); - } - - if (empty($protocol)) { - if (isset($_SERVER['HTTPS']) && !strcasecmp($_SERVER['HTTPS'], 'on')) { - $protocol = 'https'; - } else { - $protocol = 'http'; - } - if (!isset($port) || $port != intval($port)) { - $port = isset($_SERVER['SERVER_PORT']) ? $_SERVER['SERVER_PORT'] : 80; - } - } - - if ($protocol == 'http' && $port == 80) { - unset($port); - } - if ($protocol == 'https' && $port == 443) { - unset($port); - } - - $server = $protocol.'://'.$host.(isset($port) ? ':'.$port : ''); - - $uriAll = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] - : $_SERVER['PHP_SELF']; - if (false !== ($q = strpos($uriAll, '?'))) { - $uriBase = substr($uriAll, 0, $q); - } else { - $uriBase = $uriAll; - } - if (!strlen($url) || $url{0} == '#') { - $url = $uriAll.$url; - } elseif ($url{0} == '?') { - $url = $uriBase.$url; - } - if ($url{0} == '/') { - return $server . $url; - } - - // Adjust for PATH_INFO if needed - if (isset($_SERVER['PATH_INFO']) && strlen($_SERVER['PATH_INFO'])) { - $path = dirname(substr($uriBase, 0, - -strlen($_SERVER['PATH_INFO']))); - } else { - /** - * Fixes bug #12672 PHP_SELF ending on / causes incorrect redirects - * - * @link http://pear.php.net/bugs/12672 - */ - $path = dirname($uriBase.'-'); - } - - if (substr($path = strtr($path, '\\', '/'), -1) != '/') { - $path .= '/'; - } - - return $server . $path . $url; - } - - /** - * Raise Error - * - * Lazy raising of PEAR_Errors. - * - * @param mixed $error Error - * @param integer $code Error code - * - * @return object PEAR_Error - * @static - * @access protected - */ - function raiseError($error = null, $code = null) - { - include_once 'PEAR.php'; - return PEAR::raiseError($error, $code); - } -} - -?> \ No newline at end of file diff --git a/pear/HTTP/Download.php b/pear/HTTP/Download.php deleted file mode 100644 index 8cfc348..0000000 --- a/pear/HTTP/Download.php +++ /dev/null @@ -1,1243 +0,0 @@ - - * @copyright 2003-2005 Michael Wallner - * @license BSD, revised - * @version CVS: $Id: Download.php 304423 2010-10-15 13:36:46Z clockwerx $ - * @link http://pear.php.net/package/HTTP_Download - */ - -// {{{ includes -/** - * Requires PEAR - */ -require_once 'PEAR.php'; - -/** - * Requires HTTP_Header - */ -require_once 'HTTP/Header.php'; -// }}} - -// {{{ constants -/**#@+ Use with HTTP_Download::setContentDisposition() **/ -/** - * Send data as attachment - */ -define('HTTP_DOWNLOAD_ATTACHMENT', 'attachment'); -/** - * Send data inline - */ -define('HTTP_DOWNLOAD_INLINE', 'inline'); -/**#@-**/ - -/**#@+ Use with HTTP_Download::sendArchive() **/ -/** - * Send as uncompressed tar archive - */ -define('HTTP_DOWNLOAD_TAR', 'TAR'); -/** - * Send as gzipped tar archive - */ -define('HTTP_DOWNLOAD_TGZ', 'TGZ'); -/** - * Send as bzip2 compressed tar archive - */ -define('HTTP_DOWNLOAD_BZ2', 'BZ2'); -/** - * Send as zip archive - */ -define('HTTP_DOWNLOAD_ZIP', 'ZIP'); -/**#@-**/ - -/**#@+ - * Error constants - */ -define('HTTP_DOWNLOAD_E_HEADERS_SENT', -1); -define('HTTP_DOWNLOAD_E_NO_EXT_ZLIB', -2); -define('HTTP_DOWNLOAD_E_NO_EXT_MMAGIC', -3); -define('HTTP_DOWNLOAD_E_INVALID_FILE', -4); -define('HTTP_DOWNLOAD_E_INVALID_PARAM', -5); -define('HTTP_DOWNLOAD_E_INVALID_RESOURCE', -6); -define('HTTP_DOWNLOAD_E_INVALID_REQUEST', -7); -define('HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE', -8); -define('HTTP_DOWNLOAD_E_INVALID_ARCHIVE_TYPE', -9); -/**#@-**/ -// }}} - -/** - * Send HTTP Downloads/Responses. - * - * With this package you can handle (hidden) downloads. - * It supports partial downloads, resuming and sending - * raw data ie. from database BLOBs. - * - * ATTENTION: - * You shouldn't use this package together with ob_gzhandler or - * zlib.output_compression enabled in your php.ini, especially - * if you want to send already gzipped data! - * - * @access public - * @version $Revision: 304423 $ - */ -class HTTP_Download -{ - // {{{ protected member variables - /** - * Path to file for download - * - * @see HTTP_Download::setFile() - * @access protected - * @var string - */ - var $file = ''; - - /** - * Data for download - * - * @see HTTP_Download::setData() - * @access protected - * @var string - */ - var $data = null; - - /** - * Resource handle for download - * - * @see HTTP_Download::setResource() - * @access protected - * @var int - */ - var $handle = null; - - /** - * Whether to gzip the download - * - * @access protected - * @var bool - */ - var $gzip = false; - - /** - * Whether to allow caching of the download on the clients side - * - * @access protected - * @var bool - */ - var $cache = true; - - /** - * Size of download - * - * @access protected - * @var int - */ - var $size = 0; - - /** - * Last modified - * - * @access protected - * @var int - */ - var $lastModified = 0; - - /** - * HTTP headers - * - * @access protected - * @var array - */ - var $headers = array( - 'Content-Type' => 'application/x-octetstream', - 'Pragma' => 'cache', - 'Cache-Control' => 'public, must-revalidate, max-age=0', - 'Accept-Ranges' => 'bytes', - 'X-Sent-By' => 'PEAR::HTTP::Download' - ); - - /** - * HTTP_Header - * - * @access protected - * @var object - */ - var $HTTP = null; - - /** - * ETag - * - * @access protected - * @var string - */ - var $etag = ''; - - /** - * Buffer Size - * - * @access protected - * @var int - */ - var $bufferSize = 2097152; - - /** - * Throttle Delay - * - * @access protected - * @var float - */ - var $throttleDelay = 0; - - /** - * Sent Bytes - * - * @access public - * @var int - */ - var $sentBytes = 0; - - /** - * Startup error - * - * @var PEAR_Error - * @access protected - */ - var $_error = null; - // }}} - - // {{{ constructor - /** - * Constructor - * - * Set supplied parameters. - * - * @access public - * @param array $params associative array of parameters - * one of: - *
      - *
    • 'file' => path to file for download
    • - *
    • 'data' => raw data for download
    • - *
    • 'resource' => resource handle for download
    • - *
    - * and any of: - *
      - *
    • 'cache' => whether to allow cs caching
    • - *
    • 'gzip' => whether to gzip the download
    • - *
    • 'lastmodified' => unix timestamp
    • - *
    • 'contenttype' => content type of download
    • - *
    • 'contentdisposition' => content disposition
    • - *
    • 'buffersize' => amount of bytes to buffer
    • - *
    • 'throttledelay' => amount of secs to sleep
    • - *
    • 'cachecontrol' => cache privacy and validity
    • - *
    - * - * 'Content-Disposition' is not HTTP compliant, but most browsers - * follow this header, so it was borrowed from MIME standard. - * - * It looks like this: - * "Content-Disposition: attachment; filename=example.tgz". - * - * @see HTTP_Download::setContentDisposition() - */ - function HTTP_Download($params = array()) - { - $this->HTTP = &new HTTP_Header; - $this->_error = $this->setParams($params); - } - // }}} - - // {{{ public methods - /** - * Set parameters - * - * Set supplied parameters through its accessor methods. - * - * @access public - * @return mixed Returns true on success or PEAR_Error on failure. - * @param array $params associative array of parameters - * - * @see HTTP_Download::HTTP_Download() - */ - function setParams($params) - { - $error = $this->_getError(); - if ($error !== null) { - return $error; - } - foreach((array) $params as $param => $value){ - $method = 'set'. $param; - - if (!method_exists($this, $method)) { - return PEAR::raiseError( - "Method '$method' doesn't exist.", - HTTP_DOWNLOAD_E_INVALID_PARAM - ); - } - - $e = call_user_func_array(array(&$this, $method), (array) $value); - - if (PEAR::isError($e)) { - return $e; - } - } - return true; - } - - /** - * Set path to file for download - * - * The Last-Modified header will be set to files filemtime(), actually. - * Returns PEAR_Error (HTTP_DOWNLOAD_E_INVALID_FILE) if file doesn't exist. - * Sends HTTP 404 or 403 status if $send_error is set to true. - * - * @access public - * @return mixed Returns true on success or PEAR_Error on failure. - * @param string $file path to file for download - * @param bool $send_error whether to send HTTP/404 or 403 if - * the file wasn't found or is not readable - */ - function setFile($file, $send_error = true) - { - $error = $this->_getError(); - if ($error !== null) { - return $error; - } - $file = realpath($file); - if (!is_file($file)) { - if ($send_error) { - $this->HTTP->sendStatusCode(404); - } - return PEAR::raiseError( - "File '$file' not found.", - HTTP_DOWNLOAD_E_INVALID_FILE - ); - } - if (!is_readable($file)) { - if ($send_error) { - $this->HTTP->sendStatusCode(403); - } - return PEAR::raiseError( - "Cannot read file '$file'.", - HTTP_DOWNLOAD_E_INVALID_FILE - ); - } - $this->setLastModified(filemtime($file)); - $this->file = $file; - $this->size = filesize($file); - return true; - } - - /** - * Set data for download - * - * Set $data to null if you want to unset this. - * - * @access public - * @return void - * @param $data raw data to send - */ - function setData($data = null) - { - $this->data = $data; - $this->size = strlen($data); - } - - /** - * Set resource for download - * - * The resource handle supplied will be closed after sending the download. - * Returns a PEAR_Error (HTTP_DOWNLOAD_E_INVALID_RESOURCE) if $handle - * is no valid resource. Set $handle to null if you want to unset this. - * - * @access public - * @return mixed Returns true on success or PEAR_Error on failure. - * @param int $handle resource handle - */ - function setResource($handle = null) - { - $error = $this->_getError(); - if ($error !== null) { - return $error; - } - if (!isset($handle)) { - $this->handle = null; - $this->size = 0; - return true; - } - - if (is_resource($handle)) { - $this->handle = $handle; - $filestats = fstat($handle); - $this->size = isset($filestats['size']) ? $filestats['size'] - : -1; - return true; - } - - return PEAR::raiseError( - "Handle '$handle' is no valid resource.", - HTTP_DOWNLOAD_E_INVALID_RESOURCE - ); - } - - /** - * Whether to gzip the download - * - * Returns a PEAR_Error (HTTP_DOWNLOAD_E_NO_EXT_ZLIB) - * if ext/zlib is not available/loadable. - * - * @access public - * @return mixed Returns true on success or PEAR_Error on failure. - * @param bool $gzip whether to gzip the download - */ - function setGzip($gzip = false) - { - $error = $this->_getError(); - if ($error !== null) { - return $error; - } - if ($gzip && !PEAR::loadExtension('zlib')){ - return PEAR::raiseError( - 'GZIP compression (ext/zlib) not available.', - HTTP_DOWNLOAD_E_NO_EXT_ZLIB - ); - } - $this->gzip = (bool) $gzip; - return true; - } - - /** - * Whether to allow caching - * - * If set to true (default) we'll send some headers that are commonly - * used for caching purposes like ETag, Cache-Control and Last-Modified. - * - * If caching is disabled, we'll send the download no matter if it - * would actually be cached at the client side. - * - * @access public - * @return void - * @param bool $cache whether to allow caching - */ - function setCache($cache = true) - { - $this->cache = (bool) $cache; - } - - /** - * Whether to allow proxies to cache - * - * If set to 'private' proxies shouldn't cache the response. - * This setting defaults to 'public' and affects only cached responses. - * - * @access public - * @return bool - * @param string $cache private or public - * @param int $maxage maximum age of the client cache entry - */ - function setCacheControl($cache = 'public', $maxage = 0) - { - switch ($cache = strToLower($cache)) - { - case 'private': - case 'public': - $this->headers['Cache-Control'] = - $cache .', must-revalidate, max-age='. abs($maxage); - return true; - break; - } - return false; - } - - /** - * Set ETag - * - * Sets a user-defined ETag for cache-validation. The ETag is usually - * generated by HTTP_Download through its payload information. - * - * @access public - * @return void - * @param string $etag Entity tag used for strong cache validation. - */ - function setETag($etag = null) - { - $this->etag = (string) $etag; - } - - /** - * Set Size of Buffer - * - * The amount of bytes specified as buffer size is the maximum amount - * of data read at once from resources or files. The default size is 2M - * (2097152 bytes). Be aware that if you enable gzip compression and - * you set a very low buffer size that the actual file size may grow - * due to added gzip headers for each sent chunk of the specified size. - * - * Returns PEAR_Error (HTTP_DOWNLOAD_E_INVALID_PARAM) if $size is not - * greater than 0 bytes. - * - * @access public - * @return mixed Returns true on success or PEAR_Error on failure. - * @param int $bytes Amount of bytes to use as buffer. - */ - function setBufferSize($bytes = 2097152) - { - $error = $this->_getError(); - if ($error !== null) { - return $error; - } - if (0 >= $bytes) { - return PEAR::raiseError( - 'Buffer size must be greater than 0 bytes ('. $bytes .' given)', - HTTP_DOWNLOAD_E_INVALID_PARAM); - } - $this->bufferSize = abs($bytes); - return true; - } - - /** - * Set Throttle Delay - * - * Set the amount of seconds to sleep after each chunck that has been - * sent. One can implement some sort of throttle through adjusting the - * buffer size and the throttle delay. With the following settings - * HTTP_Download will sleep a second after each 25 K of data sent. - * - * - * Array( - * 'throttledelay' => 1, - * 'buffersize' => 1024 * 25, - * ) - * - * - * Just be aware that if gzipp'ing is enabled, decreasing the chunk size - * too much leads to proportionally increased network traffic due to added - * gzip header and bottom bytes around each chunk. - * - * @access public - * @return void - * @param float $seconds Amount of seconds to sleep after each - * chunk that has been sent. - */ - function setThrottleDelay($seconds = 0) - { - $this->throttleDelay = abs($seconds) * 1000; - } - - /** - * Set "Last-Modified" - * - * This is usually determined by filemtime() in HTTP_Download::setFile() - * If you set raw data for download with HTTP_Download::setData() and you - * want do send an appropiate "Last-Modified" header, you should call this - * method. - * - * @access public - * @return void - * @param int unix timestamp - */ - function setLastModified($last_modified) - { - $this->lastModified = $this->headers['Last-Modified'] = (int) $last_modified; - } - - /** - * Set Content-Disposition header - * - * @see HTTP_Download::HTTP_Download - * - * @access public - * @return void - * @param string $disposition whether to send the download - * inline or as attachment - * @param string $file_name the filename to display in - * the browser's download window - * - * Example: - * - * $HTTP_Download->setContentDisposition( - * HTTP_DOWNLOAD_ATTACHMENT, - * 'download.tgz' - * ); - * - */ - function setContentDisposition( $disposition = HTTP_DOWNLOAD_ATTACHMENT, - $file_name = null) - { - $cd = $disposition; - if (isset($file_name)) { - $cd .= '; filename="' . $file_name . '"'; - } elseif ($this->file) { - $cd .= '; filename="' . basename($this->file) . '"'; - } - $this->headers['Content-Disposition'] = $cd; - } - - /** - * Set content type of the download - * - * Default content type of the download will be 'application/x-octetstream'. - * Returns PEAR_Error (HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE) if - * $content_type doesn't seem to be valid. - * - * @access public - * @return mixed Returns true on success or PEAR_Error on failure. - * @param string $content_type content type of file for download - */ - function setContentType($content_type = 'application/x-octetstream') - { - $error = $this->_getError(); - if ($error !== null) { - return $error; - } - if (!preg_match('/^[a-z]+\w*\/[a-z]+[\w.;= -]*$/', $content_type)) { - return PEAR::raiseError( - "Invalid content type '$content_type' supplied.", - HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE - ); - } - $this->headers['Content-Type'] = $content_type; - return true; - } - - /** - * Guess content type of file - * - * First we try to use PEAR::MIME_Type, if installed, to detect the content - * type, else we check if ext/mime_magic is loaded and properly configured. - * - * Returns PEAR_Error if: - * o if PEAR::MIME_Type failed to detect a proper content type - * (HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE) - * o ext/magic.mime is not installed, or not properly configured - * (HTTP_DOWNLOAD_E_NO_EXT_MMAGIC) - * o mime_content_type() couldn't guess content type or returned - * a content type considered to be bogus by setContentType() - * (HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE) - * - * @access public - * @return mixed Returns true on success or PEAR_Error on failure. - */ - function guessContentType() - { - $error = $this->_getError(); - if ($error !== null) { - return $error; - } - if (class_exists('MIME_Type') || @include_once 'MIME/Type.php') { - if (PEAR::isError($mime_type = MIME_Type::autoDetect($this->file))) { - return PEAR::raiseError($mime_type->getMessage(), - HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE); - } - return $this->setContentType($mime_type); - } - if (!function_exists('mime_content_type')) { - return PEAR::raiseError( - 'This feature requires ext/mime_magic!', - HTTP_DOWNLOAD_E_NO_EXT_MMAGIC - ); - } - if (!is_file(ini_get('mime_magic.magicfile'))) { - return PEAR::raiseError( - 'ext/mime_magic is loaded but not properly configured!', - HTTP_DOWNLOAD_E_NO_EXT_MMAGIC - ); - } - if (!$content_type = @mime_content_type($this->file)) { - return PEAR::raiseError( - 'Couldn\'t guess content type with mime_content_type().', - HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE - ); - } - return $this->setContentType($content_type); - } - - /** - * Send - * - * Returns PEAR_Error if: - * o HTTP headers were already sent (HTTP_DOWNLOAD_E_HEADERS_SENT) - * o HTTP Range was invalid (HTTP_DOWNLOAD_E_INVALID_REQUEST) - * - * @access public - * @return mixed Returns true on success or PEAR_Error on failure. - * @param bool $autoSetContentDisposition Whether to set the - * Content-Disposition header if it isn't already. - */ - function send($autoSetContentDisposition = true) - { - $error = $this->_getError(); - if ($error !== null) { - return $error; - } - if (headers_sent()) { - return PEAR::raiseError( - 'Headers already sent.', - HTTP_DOWNLOAD_E_HEADERS_SENT - ); - } - - if (!ini_get('safe_mode')) { - @set_time_limit(0); - } - - if ($autoSetContentDisposition && - !isset($this->headers['Content-Disposition'])) { - $this->setContentDisposition(); - } - - if ($this->cache) { - $this->headers['ETag'] = $this->generateETag(); - if ($this->isCached()) { - $this->HTTP->sendStatusCode(304); - $this->sendHeaders(); - return true; - } - } else { - unset($this->headers['Last-Modified']); - } - - if (ob_get_level()) { - while (@ob_end_clean()); - } - - if ($this->gzip) { - @ob_start('ob_gzhandler'); - } else { - ob_start(); - } - - $this->sentBytes = 0; - - // Known content length? - $end = ($this->size >= 0) ? max($this->size - 1, 0) : '*'; - - if ($end != '*' && $this->isRangeRequest()) { - $chunks = $this->getChunks(); - if (empty($chunks)) { - $this->HTTP->sendStatusCode(200); - $chunks = array(array(0, $end)); - - } elseif (PEAR::isError($chunks)) { - ob_end_clean(); - $this->HTTP->sendStatusCode(416); - return $chunks; - - } else { - $this->HTTP->sendStatusCode(206); - } - } else { - $this->HTTP->sendStatusCode(200); - $chunks = array(array(0, $end)); - if (!$this->gzip && count(ob_list_handlers()) < 2 && $end != '*') { - $this->headers['Content-Length'] = $this->size; - } - } - - $this->sendChunks($chunks); - - ob_end_flush(); - flush(); - return true; - } - - /** - * Static send - * - * @see HTTP_Download::HTTP_Download() - * @see HTTP_Download::send() - * - * @static - * @access public - * @return mixed Returns true on success or PEAR_Error on failure. - * @param array $params associative array of parameters - * @param bool $guess whether HTTP_Download::guessContentType() - * should be called - */ - function staticSend($params, $guess = false) - { - $d = &new HTTP_Download(); - $e = $d->setParams($params); - if (PEAR::isError($e)) { - return $e; - } - if ($guess) { - $e = $d->guessContentType(); - if (PEAR::isError($e)) { - return $e; - } - } - return $d->send(); - } - - /** - * Send a bunch of files or directories as an archive - * - * Example: - * - * require_once 'HTTP/Download.php'; - * HTTP_Download::sendArchive( - * 'myArchive.tgz', - * '/var/ftp/pub/mike', - * HTTP_DOWNLOAD_TGZ, - * '', - * '/var/ftp/pub' - * ); - * - * - * @see Archive_Tar::createModify() - * @deprecated use HTTP_Download_Archive::send() - * @static - * @access public - * @return mixed Returns true on success or PEAR_Error on failure. - * @param string $name name the sent archive should have - * @param mixed $files files/directories - * @param string $type archive type - * @param string $add_path path that should be prepended to the files - * @param string $strip_path path that should be stripped from the files - */ - function sendArchive( $name, - $files, - $type = HTTP_DOWNLOAD_TGZ, - $add_path = '', - $strip_path = '') - { - require_once 'HTTP/Download/Archive.php'; - return HTTP_Download_Archive::send($name, $files, $type, - $add_path, $strip_path); - } - // }}} - - // {{{ protected methods - /** - * Generate ETag - * - * @access protected - * @return string - */ - function generateETag() - { - if (!$this->etag) { - if ($this->data) { - $md5 = md5($this->data); - } else { - $mtime = time(); - $ino = 0; - $size = mt_rand(); - extract(is_resource($this->handle) ? fstat($this->handle) - : stat($this->file)); - $md5 = md5($mtime .'='. $ino .'='. $size); - } - $this->etag = '"' . $md5 . '-' . crc32($md5) . '"'; - } - return $this->etag; - } - - /** - * Send multiple chunks - * - * @access protected - * @return mixed Returns true on success or PEAR_Error on failure. - * @param array $chunks - */ - function sendChunks($chunks) - { - if (count($chunks) == 1) { - return $this->sendChunk(current($chunks)); - } - - $bound = uniqid('HTTP_DOWNLOAD-', true); - $cType = $this->headers['Content-Type']; - $this->headers['Content-Type'] = - 'multipart/byteranges; boundary=' . $bound; - $this->sendHeaders(); - foreach ($chunks as $chunk){ - $this->sendChunk($chunk, $cType, $bound); - } - #echo "\r\n--$bound--\r\n"; - return true; - } - - /** - * Send chunk of data - * - * @access protected - * @return mixed Returns true on success or PEAR_Error on failure. - * @param array $chunk start and end offset of the chunk to send - * @param string $cType actual content type - * @param string $bound boundary for multipart/byteranges - */ - function sendChunk($chunk, $cType = null, $bound = null) - { - list($offset, $lastbyte) = $chunk; - $length = ($lastbyte - $offset) + 1; - - $range = $offset . '-' . $lastbyte . '/' - . (($this->size >= 0) ? $this->size : '*'); - - if (isset($cType, $bound)) { - echo "\r\n--$bound\r\n", - "Content-Type: $cType\r\n", - "Content-Range: bytes $range\r\n\r\n"; - } else { - if ($lastbyte != '*' && $this->isRangeRequest()) { - $this->headers['Content-Length'] = $length; - $this->headers['Content-Range'] = 'bytes '. $range; - } - $this->sendHeaders(); - } - - if ($this->data) { - while (($length -= $this->bufferSize) > 0) { - $this->flush(substr($this->data, $offset, $this->bufferSize)); - $this->throttleDelay and $this->sleep(); - $offset += $this->bufferSize; - } - if ($length) { - $this->flush(substr($this->data, $offset, $this->bufferSize + $length)); - } - } else { - if (!is_resource($this->handle)) { - $this->handle = fopen($this->file, 'rb'); - } - fseek($this->handle, $offset); - if ($lastbyte == '*') { - while (!feof($this->handle)) { - $this->flush(fread($this->handle, $this->bufferSize)); - $this->throttleDelay and $this->sleep(); - } - } else { - while (($length -= $this->bufferSize) > 0) { - $this->flush(fread($this->handle, $this->bufferSize)); - $this->throttleDelay and $this->sleep(); - } - if ($length) { - $this->flush(fread($this->handle, $this->bufferSize + $length)); - } - } - } - return true; - } - - /** - * Get chunks to send - * - * @access protected - * @return array Chunk list or PEAR_Error on invalid range request - * @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35 - */ - function getChunks() - { - $end = ($this->size >= 0) ? max($this->size - 1, 0) : '*'; - - // Trying to handle ranges on content with unknown length is too - // big of a mess (impossible to determine if a range is valid) - if ($end == '*') { - return array(); - } - - $ranges = $this->getRanges(); - if (empty($ranges)) { - return array(); - } - - $parts = array(); - $satisfiable = false; - foreach (explode(',', $ranges) as $chunk){ - list($o, $e) = explode('-', trim($chunk)); - - // If the last-byte-pos value is present, it MUST be greater than - // or equal to the first-byte-pos in that byte-range-spec, or the - // byte- range-spec is syntactically invalid. The recipient of a - // byte-range- set that includes one or more syntactically invalid - // byte-range-spec values MUST ignore the header field that - // includes that byte-range- set. - if ($e !== '' && $o !== '' && $e < $o) { - return array(); - } - - // If the last-byte-pos value is absent, or if the value is - // greater than or equal to the current length of the entity-body, - // last-byte-pos is taken to be equal to one less than the current - // length of the entity- body in bytes. - if ($e === '' || $e > $end) { - $e = $end; - } - - // A suffix-byte-range-spec is used to specify the suffix of the - // entity-body, of a length given by the suffix-length value. (That - // is, this form specifies the last N bytes of an entity-body.) If - // the entity is shorter than the specified suffix-length, the - // entire entity-body is used. - if ($o === '') { - // If a syntactically valid byte-range-set includes at least - // one suffix-byte-range-spec with a non-zero suffix-length, - // then the byte-range-set is satisfiable. - $satisfiable |= ($e != 0); - - $o = max($this->size - $e, 0); - $e = $end; - - } elseif ($o <= $end) { - // If a syntactically valid byte-range-set includes at least - // one byte- range-spec whose first-byte-pos is less than the - // current length of the entity-body, then the byte-range-set - // is satisfiable. - $satisfiable = true; - } else { - continue; - } - - $parts[] = array($o, $e); - } - - // If the byte-range-set is unsatisfiable, the server SHOULD return a - // response with a status of 416 (Requested range not satisfiable). - if (!$satisfiable) { - $error = PEAR::raiseError('Error processing range request', - HTTP_DOWNLOAD_E_INVALID_REQUEST); - return $error; - } - //$this->sortChunks($parts); - return $this->mergeChunks($parts); - } - - /** - * Sorts the ranges to be in ascending order - * - * @param array &$chunks ranges to sort - * - * @return void - * @access protected - * @static - * @author Philippe Jausions - */ - function sortChunks(&$chunks) - { - $sortFunc = create_function('$a,$b', - 'if ($a[0] == $b[0]) { - if ($a[1] == $b[1]) { - return 0; - } - return (($a[1] != "*" && $a[1] < $b[1]) - || $b[1] == "*") ? -1 : 1; - } - - return ($a[0] < $b[0]) ? -1 : 1;'); - - usort($chunks, $sortFunc); - } - - /** - * Merges consecutive chunks to avoid overlaps - * - * @param array $chunks Ranges to merge - * - * @return array merged ranges - * @access protected - * @static - * @author Philippe Jausions - */ - function mergeChunks($chunks) - { - do { - $count = count($chunks); - $merged = array(current($chunks)); - $j = 0; - for ($i = 1; $i < count($chunks); ++$i) { - list($o, $e) = $chunks[$i]; - if ($merged[$j][1] == '*') { - if ($merged[$j][0] <= $o) { - continue; - } elseif ($e == '*' || $merged[$j][0] <= $e) { - $merged[$j][0] = min($merged[$j][0], $o); - } else { - $merged[++$j] = $chunks[$i]; - } - } elseif ($merged[$j][0] <= $o && $o <= $merged[$j][1]) { - $merged[$j][1] = ($e == '*') ? '*' : max($e, $merged[$j][1]); - } elseif ($merged[$j][0] <= $e && $e <= $merged[$j][1]) { - $merged[$j][0] = min($o, $merged[$j][0]); - } else { - $merged[++$j] = $chunks[$i]; - } - } - if ($count == count($merged)) { - break; - } - $chunks = $merged; - } while (true); - return $merged; - } - - /** - * Check if range is requested - * - * @access protected - * @return bool - */ - function isRangeRequest() - { - if (!isset($_SERVER['HTTP_RANGE']) || !count($this->getRanges())) { - return false; - } - return $this->isValidRange(); - } - - /** - * Get range request - * - * @access protected - * @return array - */ - function getRanges() - { - return preg_match('/^bytes=((\d+-|\d+-\d+|-\d+)(, ?(\d+-|\d+-\d+|-\d+))*)$/', - @$_SERVER['HTTP_RANGE'], $matches) ? $matches[1] : array(); - } - - /** - * Check if entity is cached - * - * @access protected - * @return bool - */ - function isCached() - { - return ( - (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && - $this->lastModified == strtotime(current($a = explode( - ';', $_SERVER['HTTP_IF_MODIFIED_SINCE'])))) || - (isset($_SERVER['HTTP_IF_NONE_MATCH']) && - $this->compareAsterisk('HTTP_IF_NONE_MATCH', $this->etag)) - ); - } - - /** - * Check if entity hasn't changed - * - * @access protected - * @return bool - */ - function isValidRange() - { - if (isset($_SERVER['HTTP_IF_MATCH']) && - !$this->compareAsterisk('HTTP_IF_MATCH', $this->etag)) { - return false; - } - if (isset($_SERVER['HTTP_IF_RANGE']) && - $_SERVER['HTTP_IF_RANGE'] !== $this->etag && - strtotime($_SERVER['HTTP_IF_RANGE']) !== $this->lastModified) { - return false; - } - if (isset($_SERVER['HTTP_IF_UNMODIFIED_SINCE'])) { - $lm = current($a = explode(';', $_SERVER['HTTP_IF_UNMODIFIED_SINCE'])); - if (strtotime($lm) !== $this->lastModified) { - return false; - } - } - if (isset($_SERVER['HTTP_UNLESS_MODIFIED_SINCE'])) { - $lm = current($a = explode(';', $_SERVER['HTTP_UNLESS_MODIFIED_SINCE'])); - if (strtotime($lm) !== $this->lastModified) { - return false; - } - } - return true; - } - - /** - * Compare against an asterisk or check for equality - * - * @access protected - * @return bool - * @param string key for the $_SERVER array - * @param string string to compare - */ - function compareAsterisk($svar, $compare) - { - foreach (array_map('trim', explode(',', $_SERVER[$svar])) as $request) { - if ($request === '*' || $request === $compare) { - return true; - } - } - return false; - } - - /** - * Send HTTP headers - * - * @access protected - * @return void - */ - function sendHeaders() - { - foreach ($this->headers as $header => $value) { - $this->HTTP->setHeader($header, $value); - } - $this->HTTP->sendHeaders(); - /* NSAPI won't output anything if we did this */ - if (strncasecmp(PHP_SAPI, 'nsapi', 5)) { - if (ob_get_level()) { - ob_flush(); - } - flush(); - } - } - - /** - * Flush - * - * @access protected - * @return void - * @param string $data - */ - function flush($data = '') - { - if ($dlen = strlen($data)) { - $this->sentBytes += $dlen; - echo $data; - } - ob_flush(); - flush(); - } - - /** - * Sleep - * - * @access protected - * @return void - */ - function sleep() - { - if (OS_WINDOWS) { - com_message_pump($this->throttleDelay); - } else { - usleep($this->throttleDelay * 1000); - } - } - - /** - * Returns and clears startup error - * - * @return NULL|PEAR_Error startup error if one exists - * @access protected - */ - function _getError() - { - $error = null; - if (PEAR::isError($this->_error)) { - $error = $this->_error; - $this->_error = null; - } - return $error; - } - // }}} -} -?> diff --git a/pear/HTTP/Download/Archive.php b/pear/HTTP/Download/Archive.php deleted file mode 100644 index 4eadbd7..0000000 --- a/pear/HTTP/Download/Archive.php +++ /dev/null @@ -1,122 +0,0 @@ - - * @copyright 2003-2005 Michael Wallner - * @license BSD, revisewd - * @version CVS: $Id: Archive.php 304423 2010-10-15 13:36:46Z clockwerx $ - * @link http://pear.php.net/package/HTTP_Download - */ - -/** - * Requires HTTP_Download - */ -require_once 'HTTP/Download.php'; - -/** - * Requires System - */ -require_once 'System.php'; - -/** - * HTTP_Download_Archive - * - * Helper class for sending Archives. - * - * @access public - * @version $Revision: 304423 $ - */ -class HTTP_Download_Archive -{ - /** - * Send a bunch of files or directories as an archive - * - * Example: - * - * require_once 'HTTP/Download/Archive.php'; - * HTTP_Download_Archive::send( - * 'myArchive.tgz', - * '/var/ftp/pub/mike', - * HTTP_DOWNLOAD_BZ2, - * '', - * '/var/ftp/pub' - * ); - * - * - * @see Archive_Tar::createModify() - * @static - * @access public - * @return mixed Returns true on success or PEAR_Error on failure. - * @param string $name name the sent archive should have - * @param mixed $files files/directories - * @param string $type archive type - * @param string $add_path path that should be prepended to the files - * @param string $strip_path path that should be stripped from the files - */ - function send($name, $files, $type = HTTP_DOWNLOAD_TGZ, $add_path = '', $strip_path = '') - { - $tmp = System::mktemp(); - - switch ($type = strToUpper($type)) - { - case HTTP_DOWNLOAD_TAR: - include_once 'Archive/Tar.php'; - $arc = &new Archive_Tar($tmp); - $content_type = 'x-tar'; - break; - - case HTTP_DOWNLOAD_TGZ: - include_once 'Archive/Tar.php'; - $arc = &new Archive_Tar($tmp, 'gz'); - $content_type = 'x-gzip'; - break; - - case HTTP_DOWNLOAD_BZ2: - include_once 'Archive/Tar.php'; - $arc = &new Archive_Tar($tmp, 'bz2'); - $content_type = 'x-bzip2'; - break; - - case HTTP_DOWNLOAD_ZIP: - include_once 'Archive/Zip.php'; - $arc = &new Archive_Zip($tmp); - $content_type = 'x-zip'; - break; - - default: - return PEAR::raiseError( - 'Archive type not supported: ' . $type, - HTTP_DOWNLOAD_E_INVALID_ARCHIVE_TYPE - ); - } - - if ($type == HTTP_DOWNLOAD_ZIP) { - $options = array( 'add_path' => $add_path, - 'remove_path' => $strip_path); - if (!$arc->create($files, $options)) { - return PEAR::raiseError('Archive creation failed.'); - } - } else { - if (!$e = $arc->createModify($files, $add_path, $strip_path)) { - return PEAR::raiseError('Archive creation failed.'); - } - if (PEAR::isError($e)) { - return $e; - } - } - unset($arc); - - $dl = &new HTTP_Download(array('file' => $tmp)); - $dl->setContentType('application/' . $content_type); - $dl->setContentDisposition(HTTP_DOWNLOAD_ATTACHMENT, $name); - return $dl->send(); - } -} -?> diff --git a/pear/HTTP/Download/PgLOB.php b/pear/HTTP/Download/PgLOB.php deleted file mode 100644 index d1c989e..0000000 --- a/pear/HTTP/Download/PgLOB.php +++ /dev/null @@ -1,177 +0,0 @@ - - * @copyright 2003-2005 Michael Wallner - * @license BSD, revised - * @version CVS: $Id: PgLOB.php 304423 2010-10-15 13:36:46Z clockwerx $ - * @link http://pear.php.net/package/HTTP_Download - */ - -$GLOBALS['_HTTP_Download_PgLOB_Connection'] = null; -stream_register_wrapper('pglob', 'HTTP_Download_PgLOB'); - -/** - * PgSQL large object stream interface for HTTP_Download - * - * Usage: - * - * require_once 'HTTP/Download.php'; - * require_once 'HTTP/Download/PgLOB.php'; - * $db = &DB::connect('pgsql://user:pass@host/db'); - * // or $db = pg_connect(...); - * $lo = HTTP_Download_PgLOB::open($db, 12345); - * $dl = &new HTTP_Download; - * $dl->setResource($lo); - * $dl->send() - * - * - * @access public - * @version $Revision: 304423 $ - */ -class HTTP_Download_PgLOB -{ - /** - * Set Connection - * - * @static - * @access public - * @return bool - * @param mixed $conn - */ - function setConnection($conn) - { - if (is_a($conn, 'DB_Common')) { - $conn = $conn->dbh; - } elseif ( is_a($conn, 'MDB_Common') || - is_a($conn, 'MDB2_Driver_Common')) { - $conn = $conn->connection; - } - if ($isResource = is_resource($conn)) { - $GLOBALS['_HTTP_Download_PgLOB_Connection'] = $conn; - } - return $isResource; - } - - /** - * Get Connection - * - * @static - * @access public - * @return resource - */ - function getConnection() - { - if (is_resource($GLOBALS['_HTTP_Download_PgLOB_Connection'])) { - return $GLOBALS['_HTTP_Download_PgLOB_Connection']; - } - return null; - } - - /** - * Open - * - * @static - * @access public - * @return resource - * @param mixed $conn - * @param int $loid - * @param string $mode - */ - function open($conn, $loid, $mode = 'rb') - { - HTTP_Download_PgLOB::setConnection($conn); - return fopen('pglob:///'. $loid, $mode); - } - - /**#@+ - * Stream Interface Implementation - * @internal - */ - var $ID = 0; - var $size = 0; - var $conn = null; - var $handle = null; - - function stream_open($path, $mode) - { - if (!$this->conn = HTTP_Download_PgLOB::getConnection()) { - return false; - } - if (!preg_match('/(\d+)/', $path, $matches)) { - return false; - } - $this->ID = $matches[1]; - - if (!pg_query($this->conn, 'BEGIN')) { - return false; - } - - $this->handle = pg_lo_open($this->conn, $this->ID, $mode); - if (!is_resource($this->handle)) { - return false; - } - - // fetch size of lob - pg_lo_seek($this->handle, 0, PGSQL_SEEK_END); - $this->size = (int) pg_lo_tell($this->handle); - pg_lo_seek($this->handle, 0, PGSQL_SEEK_SET); - - return true; - } - - function stream_read($length) - { - return pg_lo_read($this->handle, $length); - } - - function stream_seek($offset, $whence = SEEK_SET) - { - return pg_lo_seek($this->handle, $offset, $whence); - } - - function stream_tell() - { - return pg_lo_tell($this->handle); - } - - function stream_eof() - { - return pg_lo_tell($this->handle) >= $this->size; - } - - function stream_flush() - { - return true; - } - - function stream_stat() - { - return array('size' => $this->size, 'ino' => $this->ID); - } - - function stream_write($data) - { - return pg_lo_write($this->handle, $data); - } - - function stream_close() - { - if (pg_lo_close($this->handle)) { - return pg_query($this->conn, 'COMMIT'); - } else { - pg_query($this->conn ,'ROLLBACK'); - return false; - } - } - /**#@-*/ -} - -?> diff --git a/pear/HTTP/Header.php b/pear/HTTP/Header.php deleted file mode 100644 index 14026ad..0000000 --- a/pear/HTTP/Header.php +++ /dev/null @@ -1,537 +0,0 @@ - - * @author Davey Shafik - * @author Michael Wallner - * @copyright 2003-2005 The Authors - * @license BSD, revised - * @version CVS: $Id: Header.php 304418 2010-10-15 13:18:02Z clockwerx $ - * @link http://pear.php.net/package/HTTP_Header - */ - -/** - * Requires HTTP - */ -require_once 'HTTP.php'; - -/**#@+ - * Information Codes - */ -define('HTTP_HEADER_STATUS_100', '100 Continue'); -define('HTTP_HEADER_STATUS_101', '101 Switching Protocols'); -define('HTTP_HEADER_STATUS_102', '102 Processing'); -define('HTTP_HEADER_STATUS_INFORMATIONAL',1); -/**#@-*/ - -/**#+ - * Success Codes - */ -define('HTTP_HEADER_STATUS_200', '200 OK'); -define('HTTP_HEADER_STATUS_201', '201 Created'); -define('HTTP_HEADER_STATUS_202', '202 Accepted'); -define('HTTP_HEADER_STATUS_203', '203 Non-Authoritative Information'); -define('HTTP_HEADER_STATUS_204', '204 No Content'); -define('HTTP_HEADER_STATUS_205', '205 Reset Content'); -define('HTTP_HEADER_STATUS_206', '206 Partial Content'); -define('HTTP_HEADER_STATUS_207', '207 Multi-Status'); -define('HTTP_HEADER_STATUS_SUCCESSFUL',2); -/**#@-*/ - -/**#@+ - * Redirection Codes - */ -define('HTTP_HEADER_STATUS_300', '300 Multiple Choices'); -define('HTTP_HEADER_STATUS_301', '301 Moved Permanently'); -define('HTTP_HEADER_STATUS_302', '302 Found'); -define('HTTP_HEADER_STATUS_303', '303 See Other'); -define('HTTP_HEADER_STATUS_304', '304 Not Modified'); -define('HTTP_HEADER_STATUS_305', '305 Use Proxy'); -define('HTTP_HEADER_STATUS_306', '306 (Unused)'); -define('HTTP_HEADER_STATUS_307', '307 Temporary Redirect'); -define('HTTP_HEADER_STATUS_REDIRECT',3); -/**#@-*/ - -/**#@+ - * Error Codes - */ -define('HTTP_HEADER_STATUS_400', '400 Bad Request'); -define('HTTP_HEADER_STATUS_401', '401 Unauthorized'); -define('HTTP_HEADER_STATUS_402', '402 Payment Granted'); -define('HTTP_HEADER_STATUS_403', '403 Forbidden'); -define('HTTP_HEADER_STATUS_404', '404 File Not Found'); -define('HTTP_HEADER_STATUS_405', '405 Method Not Allowed'); -define('HTTP_HEADER_STATUS_406', '406 Not Acceptable'); -define('HTTP_HEADER_STATUS_407', '407 Proxy Authentication Required'); -define('HTTP_HEADER_STATUS_408', '408 Request Time-out'); -define('HTTP_HEADER_STATUS_409', '409 Conflict'); -define('HTTP_HEADER_STATUS_410', '410 Gone'); -define('HTTP_HEADER_STATUS_411', '411 Length Required'); -define('HTTP_HEADER_STATUS_412', '412 Precondition Failed'); -define('HTTP_HEADER_STATUS_413', '413 Request Entity Too Large'); -define('HTTP_HEADER_STATUS_414', '414 Request-URI Too Large'); -define('HTTP_HEADER_STATUS_415', '415 Unsupported Media Type'); -define('HTTP_HEADER_STATUS_416', '416 Requested range not satisfiable'); -define('HTTP_HEADER_STATUS_417', '417 Expectation Failed'); -define('HTTP_HEADER_STATUS_422', '422 Unprocessable Entity'); -define('HTTP_HEADER_STATUS_423', '423 Locked'); -define('HTTP_HEADER_STATUS_424', '424 Failed Dependency'); -define('HTTP_HEADER_STATUS_CLIENT_ERROR',4); -/**#@-*/ - -/**#@+ - * Server Errors - */ -define('HTTP_HEADER_STATUS_500', '500 Internal Server Error'); -define('HTTP_HEADER_STATUS_501', '501 Not Implemented'); -define('HTTP_HEADER_STATUS_502', '502 Bad Gateway'); -define('HTTP_HEADER_STATUS_503', '503 Service Unavailable'); -define('HTTP_HEADER_STATUS_504', '504 Gateway Time-out'); -define('HTTP_HEADER_STATUS_505', '505 HTTP Version not supported'); -define('HTTP_HEADER_STATUS_507', '507 Insufficient Storage'); -define('HTTP_HEADER_STATUS_SERVER_ERROR',5); -/**#@-*/ - -/** - * HTTP_Header - * - * @package HTTP_Header - * @category HTTP - * @access public - * @version $Revision: 304418 $ - */ -class HTTP_Header extends HTTP -{ - /** - * Default Headers - * - * The values that are set as default, are the same as PHP sends by default. - * - * @var array - * @access private - */ - var $_headers = array( - 'content-type' => 'text/html', - 'pragma' => 'no-cache', - 'cache-control' => 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0' - ); - - /** - * HTTP version - * - * @var string - * @access private - */ - var $_httpVersion = '1.0'; - - /** - * @var bool - * @access public - */ - var $prettify = false; - - /** - * Constructor - * - * Sets HTTP version. - * - * @access public - * @return object HTTP_Header - */ - function HTTP_Header() - { - if (isset($_SERVER['SERVER_PROTOCOL'])) { - $this->setHttpVersion(substr($_SERVER['SERVER_PROTOCOL'], -3)); - } - } - - /** - * Set HTTP version - * - * @access public - * @return bool Returns true on success or false if version doesn't - * match 1.0 or 1.1 (note: 1 will result in 1.0) - * @param mixed $version HTTP version, either 1.0 or 1.1 - */ - function setHttpVersion($version) - { - $version = round((float) $version, 1); - if ($version < 1.0 || $version > 1.1) { - return false; - } - $this->_httpVersion = sprintf('%0.1f', $version); - return true; - } - - /** - * Get HTTP version - * - * @access public - * @return string - */ - function getHttpVersion() - { - return $this->_httpVersion; - } - - /** - * Set Header - * - * The default value for the Last-Modified header will be current - * date and atime if $value is omitted. - * - * @access public - * @return bool Returns true on success or false if $key was empty or - * $value was not of an scalar type. - * @param string $key The name of the header. - * @param string $value The value of the header. (NULL to unset header) - */ - function setHeader($key, $value = null) - { - if (empty($key) || (isset($value) && !is_scalar($value))) { - return false; - } - - $key = strToLower($key); - if ($key == 'last-modified') { - if (!isset($value)) { - $value = HTTP::Date(time()); - } elseif (is_numeric($value)) { - $value = HTTP::Date($value); - } - } - - if (isset($value)) { - $this->_headers[$key] = $value; - } else { - unset($this->_headers[$key]); - } - - return true; - } - - /** - * Get Header - * - * If $key is omitted, all stored headers will be returned. - * - * @access public - * @return mixed Returns string value of the requested header, - * array values of all headers or false if header $key - * is not set. - * @param string $key The name of the header to fetch. - */ - function getHeader($key = null) - { - if (!isset($key)) { - return $this->_headers; - } - - $key = strToLower($key); - - if (!isset($this->_headers[$key])) { - return false; - } - - return $this->_headers[$key]; - } - - /** - * Send Headers - * - * Send out the header that you set via setHeader(). - * - * @access public - * @return bool Returns true on success or false if headers are already - * sent. - * @param array $keys Headers to (not) send, see $include. - * @param array $include If true only $keys matching headers will be - * sent, if false only header not matching $keys will be - * sent. - */ - function sendHeaders($keys = array(), $include = true) - { - if (headers_sent()) { - return false; - } - - if (count($keys)) { - array_change_key_case($keys, CASE_LOWER); - foreach ($this->_headers as $key => $value) { - if ($include ? in_array($key, $keys) : !in_array($key, $keys)) { - header(($this->prettify ? uctitle($key) : $key) .': '. $value); - } - } - } else { - foreach ($this->_headers as $header => $value) { - header(($this->prettify ? uctitle($header) : $header) .': '. $value); - } - } - return true; - } - - /** - * Send Satus Code - * - * Send out the given HTTP-Status code. Use this for example when you - * want to tell the client this page is cached, then you would call - * sendStatusCode(304). - * - * @see HTTP_Header_Cache::exitIfCached() - * - * @access public - * @return bool Returns true on success or false if headers are already - * sent. - * @param int $code The status code to send, i.e. 404, 304, 200, etc. - */ - function sendStatusCode($code) - { - if (headers_sent()) { - return false; - } - - if ($code == (int) $code && defined('HTTP_HEADER_STATUS_'. $code)) { - $code = constant('HTTP_HEADER_STATUS_'. $code); - } - - if (strncasecmp(PHP_SAPI, 'cgi', 3)) { - header('HTTP/'. $this->_httpVersion .' '. $code); - } else { - header('Status: '. $code); - } - return true; - } - - /** - * Date to Timestamp - * - * Converts dates like - * Mon, 31 Mar 2003 15:26:34 GMT - * Tue, 15 Nov 1994 12:45:26 GMT - * into a timestamp, strtotime() didn't do it in older versions. - * - * @deprecated Use PHPs strtotime() instead. - * @access public - * @return mixed Returns int unix timestamp or false if the date doesn't - * seem to be a valid GMT date. - * @param string $date The GMT date. - */ - function dateToTimestamp($date) - { - static $months = array( - null => 0, 'Jan' => 1, 'Feb' => 2, 'Mar' => 3, 'Apr' => 4, - 'May' => 5, 'Jun' => 6, 'Jul' => 7, 'Aug' => 8, 'Sep' => 9, - 'Oct' => 10, 'Nov' => 11, 'Dec' => 12 - ); - - if (-1 < $timestamp = strToTime($date)) { - return $timestamp; - } - - if (!preg_match('~[^,]*,\s(\d+)\s(\w+)\s(\d+)\s(\d+):(\d+):(\d+).*~', - $date, $m)) { - return false; - } - - // [0] => Mon, 31 Mar 2003 15:42:55 GMT - // [1] => 31 [2] => Mar [3] => 2003 [4] => 15 [5] => 42 [6] => 55 - return mktime($m[4], $m[5], $m[6], $months[$m[2]], $m[1], $m[3]); - } - - /** - * Redirect - * - * This function redirects the client. This is done by issuing a Location - * header and exiting. Additionally to HTTP::redirect() you can also add - * parameters to the url. - * - * If you dont need parameters to be added, simply use HTTP::redirect() - * otherwise use HTTP_Header::redirect(). - * - * @see HTTP::redirect() - * @author Wolfram Kriesing - * @access public - * @return void - * @param string $url The URL to redirect to, if none is given it - * redirects to the current page. - * @param array $param Array of query string parameters to add; usually - * a set of key => value pairs; if an array entry consists - * only of an value it is used as key and the respective - * value is fetched from $GLOBALS[$value] - * @param bool $session Whether the session name/id should be added - */ - function redirect($url = null, $param = array(), $session = false) - { - if (!isset($url)) { - $url = $_SERVER['PHP_SELF']; - } - - $qs = array(); - - if ($session) { - $qs[] = session_name() .'='. session_id(); - } - - if (is_array($param) && count($param)) { - if (count($param)) { - foreach ($param as $key => $val) { - if (is_string($key)) { - $qs[] = urlencode($key) .'='. urlencode($val); - } else { - $qs[] = urlencode($val) .'='. urlencode(@$GLOBALS[$val]); - } - } - } - } - - if ($qstr = implode('&', $qs)) { - $purl = parse_url($url); - $url .= (isset($purl['query']) ? '&' : '?') . $qstr; - } - - parent::redirect($url); - } - - /**#@+ - * @author Davey Shafik - * @param int $http_code HTTP Code to check - * @access public - */ - - /** - * Return HTTP Status Code Type - * - * @return int|false - */ - function getStatusType($http_code) - { - if(is_int($http_code) && defined('HTTP_HEADER_STATUS_' .$http_code) || defined($http_code)) { - $type = substr($http_code,0,1); - switch ($type) { - case HTTP_HEADER_STATUS_INFORMATIONAL: - case HTTP_HEADER_STATUS_SUCCESSFUL: - case HTTP_HEADER_STATUS_REDIRECT: - case HTTP_HEADER_STATUS_CLIENT_ERROR: - case HTTP_HEADER_STATUS_SERVER_ERROR: - return $type; - break; - default: - return false; - break; - } - } else { - return false; - } - } - - /** - * Return Status Code Message - * - * @return string|false - */ - function getStatusText($http_code) - { - if ($this->getStatusType($http_code)) { - if (is_int($http_code) && defined('HTTP_HEADER_STATUS_' .$http_code)) { - return substr(constant('HTTP_HEADER_STATUS_' .$http_code),4); - } else { - return substr($http_code,4); - } - } else { - return false; - } - } - - /** - * Checks if HTTP Status code is Information (1xx) - * - * @return boolean - */ - function isInformational($http_code) - { - if ($status_type = $this->getStatusType($http_code)) { - return $status_type{0} == HTTP_HEADER_STATUS_INFORMATIONAL; - } else { - return false; - } - } - - /** - * Checks if HTTP Status code is Successful (2xx) - * - * @return boolean - */ - function isSuccessful($http_code) - { - if ($status_type = $this->getStatusType($http_code)) { - return $status_type{0} == HTTP_HEADER_STATUS_SUCCESSFUL; - } else { - return false; - } - } - - /** - * Checks if HTTP Status code is a Redirect (3xx) - * - * @return boolean - */ - function isRedirect($http_code) - { - if ($status_type = $this->getStatusType($http_code)) { - return $status_type{0} == HTTP_HEADER_STATUS_REDIRECT; - } else { - return false; - } - } - - /** - * Checks if HTTP Status code is a Client Error (4xx) - * - * @return boolean - */ - function isClientError($http_code) - { - if ($status_type = $this->getStatusType($http_code)) { - return $status_type{0} == HTTP_HEADER_STATUS_CLIENT_ERROR; - } else { - return false; - } - } - - /** - * Checks if HTTP Status code is Server Error (5xx) - * - * @return boolean - */ - function isServerError($http_code) - { - if ($status_type = $this->getStatusType($http_code)) { - return $status_type{0} == HTTP_HEADER_STATUS_SERVER_ERROR; - } else { - return false; - } - } - - /** - * Checks if HTTP Status code is Server OR Client Error (4xx or 5xx) - * - * @return boolean - */ - function isError($http_code) - { - if ($status_type = $this->getStatusType($http_code)) { - return (($status_type == HTTP_HEADER_STATUS_CLIENT_ERROR) || ($status_type == HTTP_HEADER_STATUS_SERVER_ERROR)) ? true : false; - } else { - return false; - } - } - /**#@-*/ -} -?> diff --git a/pear/HTTP/Header/Cache.php b/pear/HTTP/Header/Cache.php deleted file mode 100644 index f6ae867..0000000 --- a/pear/HTTP/Header/Cache.php +++ /dev/null @@ -1,238 +0,0 @@ - - * @author Michael Wallner - * @copyright 2003-2005 The Authors - * @license BSD, revised - * @version CVS: $Id: Cache.php 304418 2010-10-15 13:18:02Z clockwerx $ - * @link http://pear.php.net/package/HTTP_Header - */ - -/** - * Requires HTTP_Header - */ -require_once 'HTTP/Header.php'; - -/** - * HTTP_Header_Cache - * - * This package provides methods to easier handle caching of HTTP pages. That - * means that the pages can be cached at the client (user agent or browser) and - * your application only needs to send "hey client you already have the pages". - * - * Which is done by sending the HTTP-Status "304 Not Modified", so that your - * application load and the network traffic can be reduced, since you only need - * to send the complete page once. This is really an advantage e.g. for - * generated style sheets, or simply pages that do only change rarely. - * - * Usage: - * - * require_once 'HTTP/Header/Cache.php'; - * $httpCache = new HTTP_Header_Cache(4, 'weeks'); - * $httpCache->sendHeaders(); - * // your code goes here - * - * - * @package HTTP_Header - * @category HTTP - * @access public - * @version $Revision: 304418 $ - */ -class HTTP_Header_Cache extends HTTP_Header -{ - /** - * Constructor - * - * Set the amount of time to cache. - * - * @access public - * @return object HTTP_Header_Cache - * @param int $expires - * @param string $unit - */ - function HTTP_Header_Cache($expires = 0, $unit = 'seconds') - { - parent::HTTP_Header(); - $this->setHeader('Pragma', 'cache'); - $this->setHeader('Last-Modified', $this->getCacheStart()); - $this->setHeader('Cache-Control', 'private, must-revalidate, max-age=0'); - - if ($expires) { - if (!$this->isOlderThan($expires, $unit)) { - $this->exitCached(); - } - $this->setHeader('Last-Modified', time()); - } - } - - /** - * Get Cache Start - * - * Returns the unix timestamp of the If-Modified-Since HTTP header or the - * current time if the header was not sent by the client. - * - * @access public - * @return int unix timestamp - */ - function getCacheStart() - { - if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && !$this->isPost()) { - return strtotime(current($array = explode(';', - $_SERVER['HTTP_IF_MODIFIED_SINCE']))); - } - return time(); - } - - /** - * Is Older Than - * - * You can call it like this: - * - * $httpCache->isOlderThan(1, 'day'); - * $httpCache->isOlderThan(47, 'days'); - * - * $httpCache->isOlderThan(1, 'week'); - * $httpCache->isOlderThan(3, 'weeks'); - * - * $httpCache->isOlderThan(1, 'hour'); - * $httpCache->isOlderThan(5, 'hours'); - * - * $httpCache->isOlderThan(1, 'minute'); - * $httpCache->isOlderThan(15, 'minutes'); - * - * $httpCache->isOlderThan(1, 'second'); - * $httpCache->isOlderThan(15); - * - * - * If you specify something greater than "weeks" as time untit, it just - * works approximatly, because a month is taken to consist of 4.3 weeks. - * - * @access public - * @return bool Returns true if requested page is older than specified. - * @param int $time The amount of time. - * @param string $unit The unit of the time amount - (year[s], month[s], - * week[s], day[s], hour[s], minute[s], second[s]). - */ - function isOlderThan($time = 0, $unit = 'seconds') - { - if (!isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) || $this->isPost()) { - return true; - } - if (!$time) { - return false; - } - - switch (strtolower($unit)) - { - case 'year': - case 'years': - $time *= 12; - case 'month': - case 'months': - $time *= 4.3; - case 'week': - case 'weeks': - $time *= 7; - case 'day': - case 'days': - $time *= 24; - case 'hour': - case 'hours': - $time *= 60; - case 'minute': - case 'minutes': - $time *= 60; - } - - return (time() - $this->getCacheStart()) > $time; - } - - /** - * Is Cached - * - * Check whether we can consider to be cached on the client side. - * - * @access public - * @return bool Whether the page/resource is considered to be cached. - * @param int $lastModified Unix timestamp of last modification. - */ - function isCached($lastModified = 0) - { - if ($this->isPost()) { - return false; - } - if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && !$lastModified) { - return true; - } - if (!$seconds = time() - $lastModified) { - return false; - } - return !$this->isOlderThan($seconds); - } - - /** - * Is Post - * - * Check if request method is "POST". - * - * @access public - * @return bool - */ - function isPost() - { - return isset($_SERVER['REQUEST_METHOD']) and - 'POST' == $_SERVER['REQUEST_METHOD']; - } - - /** - * Exit If Cached - * - * Exit with "HTTP 304 Not Modified" if we consider to be cached. - * - * @access public - * @return void - * @param int $lastModified Unix timestamp of last modification. - */ - function exitIfCached($lastModified = 0) - { - if ($this->isCached($lastModified)) { - $this->exitCached(); - } - } - - /** - * Exit Cached - * - * Exit with "HTTP 304 Not Modified". - * - * @access public - * @return void - */ - function exitCached() - { - $this->sendHeaders(); - $this->sendStatusCode(304); - exit; - } - - /** - * Set Last Modified - * - * @access public - * @return void - * @param int $lastModified The unix timestamp of last modification. - */ - function setLastModified($lastModified = null) - { - $this->setHeader('Last-Modified', $lastModified); - } -} -?> diff --git a/pear/Image/GraphViz.php b/pear/Image/GraphViz.php deleted file mode 100644 index d7de7c5..0000000 --- a/pear/Image/GraphViz.php +++ /dev/null @@ -1,1005 +0,0 @@ - and - * Sebastian Bergmann . All rights reserved. - * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * - * @category Image - * @package Image_GraphViz - * @author Dr. Volker Göbbels - * @author Sebastian Bergmann - * @author Karsten Dambekalns - * @author Michael Lively Jr. - * @author Philippe Jausions - * @copyright 2001-2007 Dr. Volker Göbbels and Sebastian Bergmann - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: GraphViz.php 304688 2010-10-24 05:21:17Z clockwerx $ - * @link http://pear.php.net/package/Image_GraphViz - * @link http://www.graphviz.org/ - * @since File available since Release 0.1.0 - */ - -/** - * Required PEAR classes - */ -require_once 'System.php'; - -/** - * Interface to AT&T's GraphViz tools. - * - * The GraphViz class allows for the creation of and to work with directed - * and undirected graphs and their visualization with AT&T's GraphViz tools. - * - * - * addNode( - * 'Node1', - * array( - * 'URL' => 'http://link1', - * 'label' => 'This is a label', - * 'shape' => 'box' - * ) - * ); - * - * $graph->addNode( - * 'Node2', - * array( - * 'URL' => 'http://link2', - * 'fontsize' => '14' - * ) - * ); - * - * $graph->addNode( - * 'Node3', - * array( - * 'URL' => 'http://link3', - * 'fontsize' => '20' - * ) - * ); - * - * $graph->addEdge( - * array( - * 'Node1' => 'Node2' - * ), - * array( - * 'label' => 'Edge Label' - * ) - * ); - * - * $graph->addEdge( - * array( - * 'Node1' => 'Node2' - * ), - * array( - * 'color' => 'red' - * ) - * ); - * - * $graph->image(); - * ?> - * - * - * @category Image - * @package Image_GraphViz - * @author Sebastian Bergmann - * @author Dr. Volker Göbbels - * @author Karsten Dambekalns - * @author Michael Lively Jr. - * @author Philippe Jausions - * @copyright 2001-2007 Dr. Volker Göbbels and Sebastian Bergmann - * @license http://www.php.net/license/3_0.txt The PHP License, Version 3.0 - * @version Release: @package_version@ - * @link http://pear.php.net/package/Image_GraphViz - * @link http://www.graphviz.org/ - * @since Class available since Release 0.1 - */ -class Image_GraphViz -{ - /** - * Base path to GraphViz commands - * - * @var string - */ - var $binPath = ''; - - /** - * Path to GraphViz/dot command - * - * @var string - */ - var $dotCommand = 'dot'; - - /** - * Path to GraphViz/neato command - * - * @var string - */ - var $neatoCommand = 'neato'; - - /** - * Representation of the graph - * - * @var array - */ - var $graph = array('edgesFrom' => array(), - 'nodes' => array(), - 'attributes' => array(), - 'directed' => true, - 'clusters' => array(), - 'subgraphs' => array(), - 'name' => 'G', - 'strict' => true, - ); - - /** - * Whether to return PEAR_Error instance on failures instead of FALSE - * - * @var boolean - */ - protected $_returnFalseOnError = true; - - /** - * Constructor. - * - * Setting the name of the Graph is useful for including multiple image - * maps on one page. If not set, the graph will be named 'G'. - * - * @param boolean $directed Directed (TRUE) or undirected (FALSE) graph. - * Note: You MUST pass a boolean, and not just - * an expression that evaluates to TRUE or - * FALSE (i.e. NULL, empty string, 0 will NOT - * work) - * @param array $attributes Attributes of the graph - * @param string $name Name of the Graph - * @param boolean $strict Whether to collapse multiple edges between - * same nodes - * @param boolean $returnError Set to TRUE to return PEAR_Error instances - * on failures instead of FALSE - */ - public function Image_GraphViz($directed = true, $attributes = array(), - $name = 'G', $strict = true, $returnError = false) - { - $this->setDirected($directed); - $this->setAttributes($attributes); - $this->graph['name'] = $name; - $this->graph['strict'] = (boolean)$strict; - - $this->_returnFalseOnError = !$returnError; - } - - /** - * Outputs image of the graph in a given format - * - * This methods send HTTP headers - * - * @param string $format Format of the output image. This may be one - * of the formats supported by GraphViz. - * @param string $command "dot" or "neato" - * - * @return boolean TRUE on success, FALSE or PEAR_Error otherwise - */ - public function image($format = 'svg', $command = null) - { - $file = $this->saveParsedGraph(); - if (!$file || PEAR::isError($file)) { - return $file; - } - - $outputfile = $file . '.' . $format; - - $rendered = $this->renderDotFile($file, $outputfile, $format, - $command); - if ($rendered !== true) { - return $rendered; - } - - $sendContentLengthHeader = true; - - switch (strtolower($format)) { - case 'gif': - case 'png': - case 'bmp': - case 'jpeg': - case 'tiff': - header('Content-Type: image/' . $format); - break; - - case 'tif': - header('Content-Type: image/tiff'); - break; - - case 'jpg': - header('Content-Type: image/jpeg'); - break; - - case 'ico': - header('Content-Type: image/x-icon'); - break; - - case 'wbmp': - header('Content-Type: image/vnd.wap.wbmp'); - break; - - case 'pdf': - header('Content-Type: application/pdf'); - break; - - case 'mif': - header('Content-Type: application/vnd.mif'); - break; - - case 'vrml': - header('Content-Type: application/x-vrml'); - break; - - case 'svg': - header('Content-Type: image/svg+xml'); - break; - - case 'plain': - case 'plain-ext': - header('Content-Type: text/plain'); - break; - - default: - header('Content-Type: application/octet-stream'); - $sendContentLengthHeader = false; - } - - if ($sendContentLengthHeader) { - header('Content-Length: ' . filesize($outputfile)); - } - - $return = true; - if (readfile($outputfile) === false) { - $return = false; - } - @unlink($outputfile); - - return $return; - } - - /** - * Returns image (data) of the graph in a given format. - * - * @param string $format Format of the output image. This may be one - * of the formats supported by GraphViz. - * @param string $command "dot" or "neato" - * - * @return string The image (data) created by GraphViz, FALSE or PEAR_Error - * on error - * @since Method available since Release 1.1.0 - */ - public function fetch($format = 'svg', $command = null) - { - $file = $this->saveParsedGraph(); - if (!$file || PEAR::isError($file)) { - return $file; - } - - $outputfile = $file . '.' . $format; - - $rendered = $this->renderDotFile($file, $outputfile, $format, - $command); - if ($rendered !== true) { - return $rendered; - } - - @unlink($file); - - $fp = fopen($outputfile, 'rb'); - - if (!$fp) { - if ($this->_returnFalseOnError) { - return false; - } - $error = PEAR::raiseError('Could not read rendered file'); - return $error; - } - - $data = fread($fp, filesize($outputfile)); - fclose($fp); - @unlink($outputfile); - - return $data; - } - - /** - * Renders a given dot file into a given format. - * - * @param string $dotfile The absolute path of the dot file to use. - * @param string $outputfile The absolute path of the file to save to. - * @param string $format Format of the output image. This may be one - * of the formats supported by GraphViz. - * @param string $command "dot" or "neato" - * - * @return boolean TRUE if the file was saved, FALSE or PEAR_Error - * otherwise. - */ - public function renderDotFile($dotfile, $outputfile, $format = 'svg', - $command = null) - { - if (!file_exists($dotfile)) { - if ($this->_returnFalseOnError) { - return false; - } - $error = PEAR::raiseError('Could not find dot file'); - return $error; - } - - $oldmtime = file_exists($outputfile) ? filemtime($outputfile) : 0; - - switch ($command) { - case 'dot': - case 'neato': - break; - default: - $command = $this->graph['directed'] ? 'dot' : 'neato'; - } - $command_orig = $command; - - $command = $this->binPath.(($command == 'dot') ? $this->dotCommand - : $this->neatoCommand); - - $command .= ' -T'.escapeshellarg($format) - .' -o'.escapeshellarg($outputfile) - .' '.escapeshellarg($dotfile) - .' 2>&1'; - exec($command, $msg, $return_val); - - clearstatcache(); - if (file_exists($outputfile) && filemtime($outputfile) > $oldmtime - && $return_val == 0) { - return true; - } elseif ($this->_returnFalseOnError) { - return false; - } - $error = PEAR::raiseError($command_orig.' command failed: ' - .implode("\n", $msg)); - return $error; - } - - /** - * Adds a cluster to the graph. - * - * A cluster is a subgraph with a rectangle around it. - * - * @param string $id ID. - * @param array $title Title. - * @param array $attributes Attributes of the cluster. - * @param string $group ID of group to nest cluster into - * - * @return void - * @see addSubgraph() - */ - public function addCluster($id, $title, $attributes = array(), $group = 'default') - { - $this->graph['clusters'][$id]['title'] = $title; - $this->graph['clusters'][$id]['attributes'] = $attributes; - $this->graph['clusters'][$id]['embedIn'] = $group; - } - - /** - * Adds a subgraph to the graph. - * - * @param string $id ID. - * @param array $title Title. - * @param array $attributes Attributes of the cluster. - * @param string $group ID of group to nest subgraph into - * - * @return void - */ - public function addSubgraph($id, $title, $attributes = array(), $group = 'default') - { - $this->graph['subgraphs'][$id]['title'] = $title; - $this->graph['subgraphs'][$id]['attributes'] = $attributes; - $this->graph['subgraphs'][$id]['embedIn'] = $group; - } - - /** - * Adds a note to the graph. - * - * @param string $name Name of the node. - * @param array $attributes Attributes of the node. - * @param string $group Group of the node. - * - * @return void - */ - public function addNode($name, $attributes = array(), $group = 'default') - { - $this->graph['nodes'][$group][$name] = $attributes; - } - - /** - * Removes a node from the graph. - * - * This method doesn't remove edges associated with the node. - * - * @param string $name Name of the node to be removed. - * @param string $group Group of the node. - * - * @return void - */ - public function removeNode($name, $group = 'default') - { - if (isset($this->graph['nodes'][$group][$name])) { - unset($this->graph['nodes'][$group][$name]); - } - } - - /** - * Adds an edge to the graph. - * - * Examples: - * - * $g->addEdge(array('node1' => 'node2')); - * $attr = array( - * 'label' => '+1', - * 'style' => 'dashed', - * ); - * $g->addEdge(array('node3' => 'node4'), $attr); - * - * // With port specification - * $g->addEdge(array('node5' => 'node6'), $attr, array('node6' => 'portA')); - * $g->addEdge(array('node7' => 'node8'), null, array('node7' => 'portC', - * 'node8' => 'portD')); - * - * - * @param array $edge Start => End node of the edge. - * @param array $attributes Attributes of the edge. - * @param array $ports Start node => port, End node => port - * - * @return integer an edge ID that can be used with {@link removeEdge()} - */ - public function addEdge($edge, $attributes = array(), $ports = array()) - { - if (!is_array($edge)) { - return; - } - - $from = key($edge); - $to = $edge[$from]; - $info = array(); - - if (is_array($ports)) { - if (array_key_exists($from, $ports)) { - $info['portFrom'] = $ports[$from]; - } - - if (array_key_exists($to, $ports)) { - $info['portTo'] = $ports[$to]; - } - } - - if (is_array($attributes)) { - $info['attributes'] = $attributes; - } - - if (!empty($this->graph['strict'])) { - if (!isset($this->graph['edgesFrom'][$from][$to][0])) { - $this->graph['edgesFrom'][$from][$to][0] = $info; - } else { - $this->graph['edgesFrom'][$from][$to][0] = array_merge($this->graph['edgesFrom'][$from][$to][0], $info); - } - } else { - $this->graph['edgesFrom'][$from][$to][] = $info; - } - - return count($this->graph['edgesFrom'][$from][$to]) - 1; - } - - /** - * Removes an edge from the graph. - * - * @param array $edge Start and End node of the edge to be removed. - * @param integer $id specific edge ID (only usefull when multiple edges - * exist between the same 2 nodes) - * - * @return void - */ - public function removeEdge($edge, $id = null) - { - if (!is_array($edge)) { - return; - } - - $from = key($edge); - $to = $edge[$from]; - - if (!is_null($id)) { - if (isset($this->graph['edgesFrom'][$from][$to][$id])) { - unset($this->graph['edgesFrom'][$from][$to][$id]); - - if (count($this->graph['edgesFrom'][$from][$to]) == 0) { - unset($this->graph['edgesFrom'][$from][$to]); - } - } - } elseif (isset($this->graph['edgesFrom'][$from][$to])) { - unset($this->graph['edgesFrom'][$from][$to]); - } - } - - /** - * Adds attributes to the graph. - * - * @param array $attributes Attributes to be added to the graph. - * - * @return void - */ - public function addAttributes($attributes) - { - if (is_array($attributes)) { - $this->graph['attributes'] = array_merge($this->graph['attributes'], $attributes); - } - } - - /** - * Sets attributes of the graph. - * - * @param array $attributes Attributes to be set for the graph. - * - * @return void - */ - public function setAttributes($attributes) - { - if (is_array($attributes)) { - $this->graph['attributes'] = $attributes; - } - } - - /** - * Escapes an (attribute) array - * - * Detects if an attribute is , contains double-quotes, etc... - * - * @param array $input input to escape - * - * @return array input escaped - */ - protected function _escapeArray($input) - { - $output = array(); - - foreach ((array)$input as $k => $v) { - switch ($k) { - case 'label': - case 'headlabel': - case 'taillabel': - $v = $this->_escape($v, true); - break; - default: - $v = $this->_escape($v); - $k = $this->_escape($k); - } - - $output[$k] = $v; - } - - return $output; - } - - /** - * Returns a safe "ID" in DOT syntax - * - * @param string $input string to use as "ID" - * @param boolean $html whether to attempt detecting HTML-like content - * - * @return string - */ - protected function _escape($input, $html = false) - { - switch (strtolower($input)) { - case 'node': - case 'edge': - case 'graph': - case 'digraph': - case 'subgraph': - case 'strict': - return '"'.$input.'"'; - } - - if (is_bool($input)) { - return ($input) ? 'true' : 'false'; - } - - if ($html && (strpos($input, '') !== false)) { - return '<'.$input.'>'; - } - - if (preg_match('/^([a-z_][a-z_0-9]*|-?(\.[0-9]+|[0-9]+(\.[0-9]*)?))$/i', - $input)) { - return $input; - } - - return '"'.str_replace(array("\r\n", "\n", "\r", '"'), - array('\n', '\n', '\n', '\"'), $input).'"'; - } - - /** - * Sets directed/undirected flag for the graph. - * - * Note: You MUST pass a boolean, and not just an expression that evaluates - * to TRUE or FALSE (i.e. NULL, empty string, 0 will not work) - * - * @param boolean $directed Directed (TRUE) or undirected (FALSE) graph. - * - * @return void - */ - public function setDirected($directed) - { - if (is_bool($directed)) { - $this->graph['directed'] = $directed; - } - } - - /** - * Loads a graph from a file in Image_GraphViz format - * - * @param string $file File to load graph from. - * - * @return void - */ - public function load($file) - { - if ($serializedGraph = implode('', @file($file))) { - $g = unserialize($serializedGraph); - - if (!is_array($g)) { - return; - } - - // Convert old storage format to new one - $defaults = array('edgesFrom' => array(), - 'nodes' => array(), - 'attributes' => array(), - 'directed' => true, - 'clusters' => array(), - 'subgraphs' => array(), - 'name' => 'G', - 'strict' => true, - ); - - $this->graph = array_merge($defaults, $g); - - if (isset($this->graph['edges'])) { - foreach ($this->graph['edges'] as $id => $nodes) { - $attr = (isset($this->graph['edgeAttributes'][$id])) - ? $this->graph['edgeAttributes'][$id] - : array(); - - $this->addEdge($nodes, $attr); - } - - unset($this->graph['edges']); - unset($this->graph['edgeAttributes']); - } - } - } - - /** - * Save graph to file in Image_GraphViz format - * - * This saves the serialized version of the instance, not the - * rendered graph. - * - * @param string $file File to save the graph to. - * - * @return string File the graph was saved to, FALSE or PEAR_Error on - * failure. - */ - public function save($file = '') - { - $serializedGraph = serialize($this->graph); - - if (empty($file)) { - $file = System::mktemp('graph_'); - } - - if ($fp = @fopen($file, 'wb')) { - @fputs($fp, $serializedGraph); - @fclose($fp); - - return $file; - } - - if ($this->_returnFalseOnError) { - return false; - } - $error = PEAR::raiseError('Could not save serialized graph instance'); - return $error; - } - - /** - * Returns a list of sub-groups for a given parent group - * - * @param string $parent Group ID - * - * @return array list of group IDs - */ - protected function _getSubgraphs($parent) - { - $subgraphs = array(); - foreach ($this->graph['clusters'] as $id => $info) { - if ($info['embedIn'] === $parent) { - $subgraphs[] = $id; - } - } - foreach ($this->graph['subgraphs'] as $id => $info) { - if ($info['embedIn'] === $parent) { - $subgraphs[] = $id; - } - } - return $subgraphs; - } - - /** - * Returns a list of cluster/subgraph IDs - * - * @return array - */ - protected function _getGroups() - { - $groups = array_merge(array_keys($this->graph['clusters']), - array_keys($this->graph['subgraphs'])); - return array_unique($groups); - } - - /** - * Returns a list of top groups - * - * @return array - */ - protected function _getTopGraphs() - { - $top = array(); - $groups = $this->_getGroups(); - - foreach ($groups as $id) { - $isTop = ($id === 'default'); - if (isset($this->graph['clusters'][$id]) - && $this->graph['clusters'][$id]['embedIn'] === 'default') { - $isTop = true; - } - if (isset($this->graph['subgraphs'][$id]) - && $this->graph['subgraphs'][$id]['embedIn'] === 'default') { - $isTop = true; - } - if ($isTop) { - $top[] = $id; - } - } - - return array_unique($top); - } - - /** - * Parses the graph into GraphViz markup. - * - * @return string GraphViz markup - */ - public function parse() - { - $parsedGraph = (empty($this->graph['strict'])) ? '' : 'strict '; - $parsedGraph .= (empty($this->graph['directed'])) ? 'graph ' : 'digraph '; - $parsedGraph .= $this->_escape($this->graph['name'])." {\n"; - - $indent = ' '; - - $attr = $this->_escapeArray($this->graph['attributes']); - - foreach ($attr as $key => $value) { - $parsedGraph .= $indent.$key.'='.$value.";\n"; - } - - $groups = $this->_getGroups(); - foreach ($this->graph['nodes'] as $group => $nodes) { - if (!in_array($group, $groups)) { - $parsedGraph .= $this->_nodes($nodes, $indent); - } - } - $tops = $this->_getTopGraphs(); - foreach ($tops as $group) { - $parsedGraph .= $this->_subgraph($group, $indent); - } - - if (!empty($this->graph['directed'])) { - $separator = ' -> '; - } else { - $separator = ' -- '; - } - - foreach ($this->graph['edgesFrom'] as $from => $toNodes) { - $from = $this->_escape($from); - - foreach ($toNodes as $to => $edges) { - $to = $this->_escape($to); - - foreach ($edges as $info) { - $f = $from; - $t = $to; - - if (array_key_exists('portFrom', $info)) { - $f .= ':'.$this->_escape($info['portFrom']); - } - - if (array_key_exists('portTo', $info)) { - $t .= ':'.$this->_escape($info['portTo']); - } - - $parsedGraph .= $indent.$f.$separator.$t; - - if (!empty($info['attributes'])) { - $attributeList = array(); - - foreach ($this->_escapeArray($info['attributes']) as $key => $value) { - switch ($key) { - case 'lhead': - case 'ltail': - if (strncasecmp($value, 'cluster', 7)) { - $value = 'cluster_'.$value; - } - break; - } - $attributeList[] = $key.'='.$value; - } - - $parsedGraph .= ' [ '.implode(',', $attributeList).' ]'; - } - - $parsedGraph .= ";\n"; - } - } - } - - return $parsedGraph . "}\n"; - } - - /** - * Output nodes - * - * @param array $nodes nodes list - * @param string $indent space indentation - * - * @return string output - */ - protected function _nodes($nodes, $indent) - { - $parsedGraph = ''; - foreach ($nodes as $node => $attributes) { - $parsedGraph .= $indent.$this->_escape($node); - - $attributeList = array(); - - foreach ($this->_escapeArray($attributes) as $key => $value) { - $attributeList[] = $key.'='.$value; - } - - if (!empty($attributeList)) { - $parsedGraph .= ' [ '.implode(',', $attributeList).' ]'; - } - - $parsedGraph .= ";\n"; - } - return $parsedGraph; - } - - /** - * Generates output for a group - * - * @return string output - */ - protected function _subgraph($group, &$indent) - { - $parsedGraph = ''; - $nodes = $this->graph['nodes'][$group]; - - if ($group !== 'default') { - $type = null; - $_group = $this->_escape($group); - - if (isset($this->graph['clusters'][$group])) { - $type = 'clusters'; - if (strncasecmp($group, 'cluster', 7)) { - $_group = $this->_escape('cluster_'.$group); - } - } elseif (isset($this->graph['subgraphs'][$group])) { - $type = 'subgraphs'; - } - $parsedGraph .= $indent.'subgraph '.$_group." {\n"; - - $indent .= ' '; - - if ($type !== null && isset($this->graph[$type][$group])) { - $cluster = $this->graph[$type][$group]; - $_attr = $this->_escapeArray($cluster['attributes']); - - $attr = array(); - foreach ($_attr as $key => $value) { - $attr[] = $key.'='.$value; - } - - if (strlen($cluster['title'])) { - $attr[] = 'label=' - .$this->_escape($cluster['title'], true); - } - - if ($attr) { - $parsedGraph .= $indent.'graph [ '.implode(',', $attr) - ." ];\n"; - } - } - } - - $parsedGraph .= $this->_nodes($nodes, $indent); - - foreach ($this->_getSubgraphs($group) as $_group) { - $parsedGraph .= $this->_subgraph($_group, $indent); - } - - if ($group !== 'default') { - $indent = substr($indent, 0, -4); - - $parsedGraph .= $indent."}\n"; - } - - return $parsedGraph; - } - - /** - * Saves GraphViz markup to file (in DOT language) - * - * @param string $file File to write the GraphViz markup to. - * - * @return string File to which the GraphViz markup was written, FALSE or - * or PEAR_Error on failure. - */ - public function saveParsedGraph($file = '') - { - $parsedGraph = $this->parse(); - - if (!empty($parsedGraph)) { - if (empty($file)) { - $file = System::mktemp('graph_'); - } - - if ($fp = @fopen($file, 'wb')) { - @fputs($fp, $parsedGraph); - @fclose($fp); - - return $file; - } - } - - if ($this->_returnFalseOnError) { - return false; - } - $error = PEAR::raiseError('Could not save graph'); - return $error; - } -} - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * c-hanging-comment-ender-p: nil - * End: - */ -?> diff --git a/pear/MIME/Type.php b/pear/MIME/Type.php deleted file mode 100644 index 77491d2..0000000 --- a/pear/MIME/Type.php +++ /dev/null @@ -1,598 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html LGPL - * @link http://pear.php.net/package/MIME_Type - */ - -require_once 'PEAR.php'; - -$_fileCmd = &PEAR::getStaticProperty('MIME_Type', 'fileCmd'); -$_fileCmd = 'file'; - -/** - * Class for working with MIME types - * - * @category MIME - * @package MIME_Type - * @author Ian Eure - * @license http://www.gnu.org/copyleft/lesser.html LGPL - * @version Release: @version@ - * @link http://pear.php.net/package/MIME_Type - */ -class MIME_Type -{ - /** - * The MIME media type - * - * @var string - */ - var $media = ''; - - /** - * The MIME media sub-type - * - * @var string - */ - var $subType = ''; - - /** - * Optional MIME parameters - * - * @var array - */ - var $parameters = array(); - - /** - * List of valid media types. - * A media type is the string in front of the slash. - * The media type of "text/xml" would be "text". - * - * @var array - */ - var $validMediaTypes = array( - 'text', - 'image', - 'audio', - 'video', - 'application', - 'multipart', - 'message' - ); - - /** - * If the finfo functions shall be used when they are available - * - * @var boolean - */ - var $useFinfo = true; - - /** - * If mime_content_type shall be used when available - * - * @var boolean - */ - var $useMimeContentType = true; - - /** - * If the file command shall be used when available - * - * @var boolean - */ - var $useFileCmd = true; - - /** - * If the in-built file extension detection shall be used - * - * @var boolean - */ - var $useExtension = true; - - /** - * Path to the "magic" file database. - * If NULL, the default one is used - * - * @var string - */ - var $magicFile = null; - - - /** - * Constructor. - * - * If $type is set, if will be parsed and the appropriate class vars set. - * If not, you get an empty class. - * This is useful, but not quite as useful as parsing a type. - * - * @param string $type MIME type - * - * @return void - */ - function MIME_Type($type = false) - { - if ($type) { - $this->parse($type); - } - } - - - /** - * Parse a mime-type and set the class variables. - * - * @param string $type MIME type to parse - * - * @return boolean True if the type has been parsed, false if not - */ - function parse($type) - { - if ($type instanceof PEAR_Error) { - return false; - } - - $this->media = $this->getMedia($type); - $this->subType = $this->getSubType($type); - $this->parameters = array(); - if (MIME_Type::hasParameters($type)) { - include_once 'MIME/Type/Parameter.php'; - foreach (MIME_Type::getParameters($type) as $param) { - $param = new MIME_Type_Parameter($param); - $this->parameters[$param->name] = $param; - } - } - - return true; - } - - - /** - * Does this type have any parameters? - * - * @param string $type MIME type to check - * - * @return boolean true if $type has parameters, false otherwise - * @static - */ - function hasParameters($type) - { - if (strstr($type, ';')) { - return true; - } - return false; - } - - - /** - * Get a MIME type's parameters - * - * @param string $type MIME type to get parameters of - * - * @return array $type's parameters - * @static - */ - function getParameters($type) - { - $params = array(); - $tmp = explode(';', $type); - for ($i = 1; $i < count($tmp); $i++) { - $params[] = trim($tmp[$i]); - } - return $params; - } - - - /** - * Strip parameters from a MIME type string. - * - * @param string $type MIME type string - * - * @return string MIME type with parameters removed - * @static - */ - function stripParameters($type) - { - if (strstr($type, ';')) { - return substr($type, 0, strpos($type, ';')); - } - return $type; - } - - - /** - * Removes comments from a media type, subtype or parameter. - * - * @param string $string String to strip comments from - * @param string &$comment Comment is stored in there. - * Do not set it to NULL if you want the comment. - * - * @return string String without comments - * @static - */ - function stripComments($string, &$comment) - { - if (strpos($string, '(') === false) { - return $string; - } - - $inquote = false; - $escaped = false; - $incomment = 0; - $newstring = ''; - - for ($n = 0; $n < strlen($string); $n++) { - if ($escaped) { - if ($incomment == 0) { - $newstring .= $string[$n]; - } else if ($comment !== null) { - $comment .= $string[$n]; - } - $escaped = false; - } else if ($string[$n] == '\\') { - $escaped = true; - } else if (!$inquote && $incomment > 0 && $string[$n] == ')') { - $incomment--; - if ($incomment == 0 && $comment !== null) { - $comment .= ' '; - } - } else if (!$inquote && $string[$n] == '(') { - $incomment++; - } else if ($string[$n] == '"') { - if ($inquote) { - $inquote = false; - } else { - $inquote = true; - } - } else if ($incomment == 0) { - $newstring .= $string[$n]; - } else if ($comment !== null) { - $comment .= $string[$n]; - } - } - - if ($comment !== null) { - $comment = trim($comment); - } - - return $newstring; - } - - - /** - * Get a MIME type's media - * Note: 'media' refers to the portion before the first slash - * - * @param string $type MIME type to get media of - * - * @return string $type's media - * @static - */ - function getMedia($type) - { - $tmp = explode('/', $type); - return strtolower(trim(MIME_Type::stripComments($tmp[0], $null))); - } - - - /** - * Get a MIME type's subtype - * - * @param string $type MIME type to get subtype of - * - * @return string $type's subtype, null if invalid mime type - * @static - */ - function getSubType($type) - { - $tmp = explode('/', $type); - if (!isset($tmp[1])) { - return null; - } - $tmp = explode(';', $tmp[1]); - return strtolower(trim(MIME_Type::stripComments($tmp[0], $null))); - } - - - /** - * Create a textual MIME type from object values - * - * This function performs the opposite function of parse(). - * - * @return string MIME type string - */ - function get() - { - $type = strtolower($this->media . '/' . $this->subType); - if (count($this->parameters)) { - foreach ($this->parameters as $key => $null) { - $type .= '; ' . $this->parameters[$key]->get(); - } - } - return $type; - } - - - /** - * Is this type experimental? - * - * Note: Experimental types are denoted by a leading 'x-' in the media or - * subtype, e.g. text/x-vcard or x-world/x-vrml. - * - * @param string $type MIME type to check - * - * @return boolean true if $type is experimental, false otherwise - * @static - */ - function isExperimental($type) - { - if (substr(MIME_Type::getMedia($type), 0, 2) == 'x-' - || substr(MIME_Type::getSubType($type), 0, 2) == 'x-' - ) { - return true; - } - return false; - } - - - /** - * Is this a vendor MIME type? - * - * Note: Vendor types are denoted with a leading 'vnd. in the subtype. - * - * @param string $type MIME type to check - * - * @return boolean true if $type is a vendor type, false otherwise - * @static - */ - function isVendor($type) - { - if (substr(MIME_Type::getSubType($type), 0, 4) == 'vnd.') { - return true; - } - return false; - } - - - /** - * Is this a wildcard type? - * - * @param string $type MIME type to check - * - * @return boolean true if $type is a wildcard, false otherwise - * @static - */ - function isWildcard($type) - { - if ($type == '*/*' || MIME_Type::getSubtype($type) == '*') { - return true; - } - return false; - } - - - /** - * Perform a wildcard match on a MIME type - * - * Example: - * MIME_Type::wildcardMatch('image/*', 'image/png') - * - * @param string $card Wildcard to check against - * @param string $type MIME type to check - * - * @return boolean true if there was a match, false otherwise - * @static - */ - function wildcardMatch($card, $type) - { - if (!MIME_Type::isWildcard($card)) { - return false; - } - - if ($card == '*/*') { - return true; - } - - if (MIME_Type::getMedia($card) == MIME_Type::getMedia($type)) { - return true; - } - - return false; - } - - - /** - * Add a parameter to this type - * - * @param string $name Attribute name - * @param string $value Attribute value - * @param string $comment Comment for this parameter - * - * @return void - */ - function addParameter($name, $value, $comment = false) - { - $tmp = new MIME_Type_Parameter(); - - $tmp->name = $name; - $tmp->value = $value; - $tmp->comment = $comment; - $this->parameters[$name] = $tmp; - } - - - /** - * Remove a parameter from this type - * - * @param string $name Parameter name - * - * @return void - */ - function removeParameter($name) - { - unset($this->parameters[$name]); - } - - - /** - * Autodetect a file's MIME-type - * - * This function may be called staticly. - * - * @param string $file Path to the file to get the type of - * @param bool $params Append MIME parameters if true - * - * @return string $file's MIME-type on success, PEAR_Error otherwise - * - * @since 1.0.0beta1 - * @static - */ - function autoDetect($file, $params = false) - { - $isStatic = !(isset($this) && get_class($this) == __CLASS__); - if ($isStatic) { - $t = new MIME_Type(); - return $t->_autoDetect($file, $params); - } else { - $type = $this->_autoDetect($file, $params); - if (!$type instanceof PEAR_Error) { - $this->parse($type); - } - return $type; - } - } - - /** - * Autodetect a file's MIME-type - * - * @param string $file Path to the file to get the type of - * @param bool $params Append MIME parameters if true - * - * @return string $file's MIME-type on success, PEAR_Error otherwise - * - * @since 1.3.0 - * - * @internal Tries to use fileinfo extension at first. If that - * does not work, mime_magic is used. If this is also not available - * or does not succeed, "file" command is tried to be executed with - * System_Command. When that fails, too, then we use our in-built - * extension-to-mimetype-mapping list. - */ - function _autoDetect($file, $params = false) - { - // Sanity checks - if (!file_exists($file)) { - return PEAR::raiseError("File \"$file\" doesn't exist"); - } - - if (!is_readable($file)) { - return PEAR::raiseError("File \"$file\" is not readable"); - } - - if ($this->useFinfo && function_exists('finfo_file')) { - $finfo = finfo_open(FILEINFO_MIME, $this->magicFile); - if ($finfo) { - $type = finfo_file($finfo, $file); - finfo_close($finfo); - if ($type !== false && $type !== '') { - return MIME_Type::_handleDetection($type, $params); - } - } - } - - if ($this->useMimeContentType && function_exists('mime_content_type')) { - $type = mime_content_type($file); - if ($type !== false && $type !== '') { - return MIME_Type::_handleDetection($type, $params); - } - } - - if ($this->useFileCmd) { - @include_once 'System/Command.php'; - if (class_exists('System_Command')) { - $type = MIME_Type::_fileAutoDetect($file); - if ($type !== false && $type !== '') { - return MIME_Type::_handleDetection($type, $params); - } - } - } - - if ($this->useExtension) { - include_once 'MIME/Type/Extension.php'; - $mte = new MIME_Type_Extension(); - return $mte->getMIMEType($file); - } - - return PEAR::raiseError("Sorry, couldn't determine file type."); - } - - - /** - * Handles a detected MIME type and modifies it if necessary. - * - * @param string $type MIME Type of a file - * @param bool $params Append MIME parameters if true - * - * @return string $file's MIME-type on success, PEAR_Error otherwise - * @static - */ - function _handleDetection($type, $params) - { - // _fileAutoDetect() may have returned an error. - if (PEAR::isError($type)) { - return $type; - } - - // Don't return an empty string - if (!$type || !strlen($type)) { - return PEAR::raiseError("Sorry, couldn't determine file type."); - } - - // Strip parameters if present & requested - if (MIME_Type::hasParameters($type) && !$params) { - $type = MIME_Type::stripParameters($type); - } - - return $type; - } - - - /** - * Autodetect a file's MIME-type with 'file' and System_Command - * - * This function may be called staticly. - * - * @param string $file Path to the file to get the type of - * - * @return string $file's MIME-type - * - * @since 1.0.0beta1 - * @static - */ - function _fileAutoDetect($file) - { - $cmd = new System_Command(); - $magic = ''; - - // Make sure we have the 'file' command. - $fileCmd = PEAR::getStaticProperty('MIME_Type', 'fileCmd'); - if (!$cmd->which($fileCmd)) { - unset($cmd); - return PEAR::raiseError("Can't find file command \"{$fileCmd}\""); - } - if (strlen($this->magicFile)) { - $magic = '--magic-file ' . escapeshellarg($this->magicFile); - } - - $cmd->pushCommand($fileCmd, $magic, "-bi " . escapeshellarg($file)); - $res = $cmd->execute(); - unset($cmd); - - return $res; - } - -} \ No newline at end of file diff --git a/pear/MIME/Type/Extension.php b/pear/MIME/Type/Extension.php deleted file mode 100644 index 7f3da7a..0000000 --- a/pear/MIME/Type/Extension.php +++ /dev/null @@ -1,292 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html LGPL - * @link http://pear.php.net/package/MIME_Type - */ - -require_once 'PEAR.php'; - -/** - * Class for mapping file extensions to MIME types. - * - * @category MIME - * @package MIME_Type - * @author Christian Schmidt - * @license http://www.gnu.org/copyleft/lesser.html LGPL - * @version Release: @version@ - * @link http://pear.php.net/package/MIME_Type - */ -class MIME_Type_Extension -{ - /** - * Mapping between file extension and MIME type. - * - * @internal The array is sorted alphabetically by value and with primary - * extension first. Be careful about not adding duplicate keys - PHP - * silently ignores duplicates. The following command can be used for - * checking for duplicates: - * grep "=> '" Extension.php | cut -d\' -f2 | sort | uniq -d - * application/octet-stream is generally used as fallback when no other - * MIME-type can be found, but the array does not contain a lot of such - * unknown extension. One entry exists, though, to allow detection of - * file extension for this MIME-type. - * - * @var array - */ - var $extensionToType = array ( - 'ez' => 'application/andrew-inset', - 'atom' => 'application/atom+xml', - 'jar' => 'application/java-archive', - 'hqx' => 'application/mac-binhex40', - 'cpt' => 'application/mac-compactpro', - 'mathml' => 'application/mathml+xml', - 'doc' => 'application/msword', - 'dat' => 'application/octet-stream', - 'oda' => 'application/oda', - 'ogg' => 'application/ogg', - 'pdf' => 'application/pdf', - 'ai' => 'application/postscript', - 'eps' => 'application/postscript', - 'ps' => 'application/postscript', - 'rdf' => 'application/rdf+xml', - 'rss' => 'application/rss+xml', - 'smi' => 'application/smil', - 'smil' => 'application/smil', - 'gram' => 'application/srgs', - 'grxml' => 'application/srgs+xml', - 'kml' => 'application/vnd.google-earth.kml+xml', - 'kmz' => 'application/vnd.google-earth.kmz', - 'mif' => 'application/vnd.mif', - 'xul' => 'application/vnd.mozilla.xul+xml', - 'xls' => 'application/vnd.ms-excel', - 'xlb' => 'application/vnd.ms-excel', - 'xlt' => 'application/vnd.ms-excel', - 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12', - 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12', - 'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12', - 'xltm' => 'application/vnd.ms-excel.template.macroEnabled.12', - 'docm' => 'application/vnd.ms-word.document.macroEnabled.12', - 'dotm' => 'application/vnd.ms-word.template.macroEnabled.12', - 'ppam' => 'application/vnd.ms-powerpoint.addin.macroEnabled.12', - 'pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12', - 'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12', - 'potm' => 'application/vnd.ms-powerpoint.template.macroEnabled.12', - 'ppt' => 'application/vnd.ms-powerpoint', - 'pps' => 'application/vnd.ms-powerpoint', - 'odc' => 'application/vnd.oasis.opendocument.chart', - 'odb' => 'application/vnd.oasis.opendocument.database', - 'odf' => 'application/vnd.oasis.opendocument.formula', - 'odg' => 'application/vnd.oasis.opendocument.graphics', - 'otg' => 'application/vnd.oasis.opendocument.graphics-template', - 'odi' => 'application/vnd.oasis.opendocument.image', - 'odp' => 'application/vnd.oasis.opendocument.presentation', - 'otp' => 'application/vnd.oasis.opendocument.presentation-template', - 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', - 'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template', - 'odt' => 'application/vnd.oasis.opendocument.text', - 'odm' => 'application/vnd.oasis.opendocument.text-master', - 'ott' => 'application/vnd.oasis.opendocument.text-template', - 'oth' => 'application/vnd.oasis.opendocument.text-web', - 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', - 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', - 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', - 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', - 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', - 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', - 'vsd' => 'application/vnd.visio', - 'wbxml' => 'application/vnd.wap.wbxml', - 'wmlc' => 'application/vnd.wap.wmlc', - 'wmlsc' => 'application/vnd.wap.wmlscriptc', - 'vxml' => 'application/voicexml+xml', - 'bcpio' => 'application/x-bcpio', - 'vcd' => 'application/x-cdlink', - 'pgn' => 'application/x-chess-pgn', - 'cpio' => 'application/x-cpio', - 'csh' => 'application/x-csh', - 'dcr' => 'application/x-director', - 'dir' => 'application/x-director', - 'dxr' => 'application/x-director', - 'dvi' => 'application/x-dvi', - 'spl' => 'application/x-futuresplash', - 'tgz' => 'application/x-gtar', - 'gtar' => 'application/x-gtar', - 'hdf' => 'application/x-hdf', - 'js' => 'application/x-javascript', - 'skp' => 'application/x-koan', - 'skd' => 'application/x-koan', - 'skt' => 'application/x-koan', - 'skm' => 'application/x-koan', - 'latex' => 'application/x-latex', - 'nc' => 'application/x-netcdf', - 'cdf' => 'application/x-netcdf', - 'sh' => 'application/x-sh', - 'shar' => 'application/x-shar', - 'swf' => 'application/x-shockwave-flash', - 'sit' => 'application/x-stuffit', - 'sv4cpio' => 'application/x-sv4cpio', - 'sv4crc' => 'application/x-sv4crc', - 'tar' => 'application/x-tar', - 'tcl' => 'application/x-tcl', - 'tex' => 'application/x-tex', - 'texinfo' => 'application/x-texinfo', - 'texi' => 'application/x-texinfo', - 't' => 'application/x-troff', - 'tr' => 'application/x-troff', - 'roff' => 'application/x-troff', - 'man' => 'application/x-troff-man', - 'me' => 'application/x-troff-me', - 'ms' => 'application/x-troff-ms', - 'ustar' => 'application/x-ustar', - 'src' => 'application/x-wais-source', - 'xhtml' => 'application/xhtml+xml', - 'xht' => 'application/xhtml+xml', - 'xslt' => 'application/xslt+xml', - 'xml' => 'application/xml', - 'xsl' => 'application/xml', - 'dtd' => 'application/xml-dtd', - 'zip' => 'application/zip', - 'au' => 'audio/basic', - 'snd' => 'audio/basic', - 'mid' => 'audio/midi', - 'midi' => 'audio/midi', - 'kar' => 'audio/midi', - 'mpga' => 'audio/mpeg', - 'mp2' => 'audio/mpeg', - 'mp3' => 'audio/mpeg', - 'aif' => 'audio/x-aiff', - 'aiff' => 'audio/x-aiff', - 'aifc' => 'audio/x-aiff', - 'm3u' => 'audio/x-mpegurl', - 'wma' => 'audio/x-ms-wma', - 'wax' => 'audio/x-ms-wax', - 'ram' => 'audio/x-pn-realaudio', - 'ra' => 'audio/x-pn-realaudio', - 'rm' => 'application/vnd.rn-realmedia', - 'wav' => 'audio/x-wav', - 'pdb' => 'chemical/x-pdb', - 'xyz' => 'chemical/x-xyz', - 'bmp' => 'image/bmp', - 'cgm' => 'image/cgm', - 'gif' => 'image/gif', - 'ief' => 'image/ief', - 'jpeg' => 'image/jpeg', - 'jpg' => 'image/jpeg', - 'jpe' => 'image/jpeg', - 'png' => 'image/png', - 'svg' => 'image/svg+xml', - 'tiff' => 'image/tiff', - 'tif' => 'image/tiff', - 'djvu' => 'image/vnd.djvu', - 'djv' => 'image/vnd.djvu', - 'wbmp' => 'image/vnd.wap.wbmp', - 'ras' => 'image/x-cmu-raster', - 'ico' => 'image/x-icon', - 'pnm' => 'image/x-portable-anymap', - 'pbm' => 'image/x-portable-bitmap', - 'pgm' => 'image/x-portable-graymap', - 'ppm' => 'image/x-portable-pixmap', - 'rgb' => 'image/x-rgb', - 'xbm' => 'image/x-xbitmap', - 'psd' => 'image/x-photoshop', - 'xpm' => 'image/x-xpixmap', - 'xwd' => 'image/x-xwindowdump', - 'eml' => 'message/rfc822', - 'igs' => 'model/iges', - 'iges' => 'model/iges', - 'msh' => 'model/mesh', - 'mesh' => 'model/mesh', - 'silo' => 'model/mesh', - 'wrl' => 'model/vrml', - 'vrml' => 'model/vrml', - 'ics' => 'text/calendar', - 'ifb' => 'text/calendar', - 'css' => 'text/css', - 'csv' => 'text/csv', - 'html' => 'text/html', - 'htm' => 'text/html', - 'txt' => 'text/plain', - 'asc' => 'text/plain', - 'rtx' => 'text/richtext', - 'rtf' => 'text/rtf', - 'sgml' => 'text/sgml', - 'sgm' => 'text/sgml', - 'tsv' => 'text/tab-separated-values', - 'wml' => 'text/vnd.wap.wml', - 'wmls' => 'text/vnd.wap.wmlscript', - 'etx' => 'text/x-setext', - 'mpeg' => 'video/mpeg', - 'mpg' => 'video/mpeg', - 'mpe' => 'video/mpeg', - 'qt' => 'video/quicktime', - 'mov' => 'video/quicktime', - 'mxu' => 'video/vnd.mpegurl', - 'm4u' => 'video/vnd.mpegurl', - 'flv' => 'video/x-flv', - 'asf' => 'video/x-ms-asf', - 'asx' => 'video/x-ms-asf', - 'wmv' => 'video/x-ms-wmv', - 'wm' => 'video/x-ms-wm', - 'wmx' => 'video/x-ms-wmx', - 'avi' => 'video/x-msvideo', - 'ogv' => 'video/ogg', - 'movie' => 'video/x-sgi-movie', - 'ice' => 'x-conference/x-cooltalk', - ); - - - - /** - * Autodetect a file's MIME-type. - * - * @param string $file Path to the file to get the type of - * - * @return string $file's MIME-type on success, PEAR_Error otherwise - */ - function getMIMEType($file) - { - $extension = substr(strrchr($file, '.'), 1); - if ($extension === false) { - return PEAR::raiseError("File has no extension."); - } - - if (!isset($this->extensionToType[$extension])) { - return PEAR::raiseError("Sorry, couldn't determine file type."); - } - - return $this->extensionToType[$extension]; - } - - - - /** - * Return default MIME-type for the specified extension. - * - * @param string $type MIME-type - * - * @return string A file extension without leading period. - */ - function getExtension($type) - { - include_once 'MIME/Type.php'; - // Strip parameters and comments. - $type = MIME_Type::getMedia($type) . '/' . MIME_Type::getSubType($type); - - $extension = array_search($type, $this->extensionToType); - if ($extension === false) { - return PEAR::raiseError("Sorry, couldn't determine extension."); - } - return $extension; - } - -} - -?> \ No newline at end of file diff --git a/pear/MIME/Type/Parameter.php b/pear/MIME/Type/Parameter.php deleted file mode 100644 index 759fd58..0000000 --- a/pear/MIME/Type/Parameter.php +++ /dev/null @@ -1,170 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html LGPL - * @link http://pear.php.net/package/MIME_Type - */ - -/** - * Class for working with MIME type parameters - * - * @category File - * @package MIME_Type - * @author Ian Eure - * @license http://www.gnu.org/copyleft/lesser.html LGPL - * @version Release: @version@ - * @link http://pear.php.net/package/MIME_Type - */ -class MIME_Type_Parameter -{ - /** - * Parameter name - * - * @var string - */ - var $name; - - /** - * Parameter value - * - * @var string - */ - var $value; - - /** - * Parameter comment - * - * @var string - */ - var $comment; - - - /** - * Constructor. - * - * @param string $param MIME parameter to parse, if set. - * - * @return void - */ - function MIME_Type_Parameter($param = false) - { - if ($param) { - $this->parse($param); - } - } - - - /** - * Parse a MIME type parameter and set object fields - * - * @param string $param MIME type parameter to parse - * - * @return void - */ - function parse($param) - { - $comment = ''; - $param = MIME_Type::stripComments($param, $comment); - $this->name = $this->getAttribute($param); - $this->value = $this->getValue($param); - $this->comment = $comment; - } - - - /** - * Get a parameter attribute (e.g. name) - * - * @param string $param MIME type parameter - * - * @return string Attribute name - * @static - */ - function getAttribute($param) - { - $tmp = explode('=', $param); - return trim($tmp[0]); - } - - - /** - * Get a parameter value - * - * @param string $param MIME type parameter - * - * @return string Value - * @static - */ - function getValue($param) - { - $tmp = explode('=', $param, 2); - $value = $tmp[1]; - $value = trim($value); - if ($value[0] == '"' && $value[strlen($value)-1] == '"') { - $value = substr($value, 1, -1); - } - $value = str_replace('\\"', '"', $value); - return $value; - } - - - /** - * Get a parameter comment - * - * @param string $param MIME type parameter - * - * @return string Parameter comment - * @see hasComment() - * @static - */ - function getComment($param) - { - $cs = strpos($param, '('); - if ($cs === false) { - return null; - } - $comment = substr($param, $cs); - return trim($comment, '() '); - } - - - /** - * Does this parameter have a comment? - * - * @param string $param MIME type parameter - * - * @return boolean true if $param has a comment, false otherwise - * @static - */ - function hasComment($param) - { - if (strstr($param, '(')) { - return true; - } - return false; - } - - - /** - * Get a string representation of this parameter - * - * This function performs the oppsite of parse() - * - * @return string String representation of parameter - */ - function get() - { - $val = $this->name . '="' . str_replace('"', '\\"', $this->value) . '"'; - if ($this->comment) { - $val .= ' (' . $this->comment . ')'; - } - return $val; - } -} -?> \ No newline at end of file diff --git a/pear/OS/Guess.php b/pear/OS/Guess.php deleted file mode 100644 index 6f41f32..0000000 --- a/pear/OS/Guess.php +++ /dev/null @@ -1,338 +0,0 @@ - - * @author Gregory Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since PEAR 0.1 - */ - -// {{{ uname examples - -// php_uname() without args returns the same as 'uname -a', or a PHP-custom -// string for Windows. -// PHP versions prior to 4.3 return the uname of the host where PHP was built, -// as of 4.3 it returns the uname of the host running the PHP code. -// -// PC RedHat Linux 7.1: -// Linux host.example.com 2.4.2-2 #1 Sun Apr 8 20:41:30 EDT 2001 i686 unknown -// -// PC Debian Potato: -// Linux host 2.4.17 #2 SMP Tue Feb 12 15:10:04 CET 2002 i686 unknown -// -// PC FreeBSD 3.3: -// FreeBSD host.example.com 3.3-STABLE FreeBSD 3.3-STABLE #0: Mon Feb 21 00:42:31 CET 2000 root@example.com:/usr/src/sys/compile/CONFIG i386 -// -// PC FreeBSD 4.3: -// FreeBSD host.example.com 4.3-RELEASE FreeBSD 4.3-RELEASE #1: Mon Jun 25 11:19:43 EDT 2001 root@example.com:/usr/src/sys/compile/CONFIG i386 -// -// PC FreeBSD 4.5: -// FreeBSD host.example.com 4.5-STABLE FreeBSD 4.5-STABLE #0: Wed Feb 6 23:59:23 CET 2002 root@example.com:/usr/src/sys/compile/CONFIG i386 -// -// PC FreeBSD 4.5 w/uname from GNU shellutils: -// FreeBSD host.example.com 4.5-STABLE FreeBSD 4.5-STABLE #0: Wed Feb i386 unknown -// -// HP 9000/712 HP-UX 10: -// HP-UX iq B.10.10 A 9000/712 2008429113 two-user license -// -// HP 9000/712 HP-UX 10 w/uname from GNU shellutils: -// HP-UX host B.10.10 A 9000/712 unknown -// -// IBM RS6000/550 AIX 4.3: -// AIX host 3 4 000003531C00 -// -// AIX 4.3 w/uname from GNU shellutils: -// AIX host 3 4 000003531C00 unknown -// -// SGI Onyx IRIX 6.5 w/uname from GNU shellutils: -// IRIX64 host 6.5 01091820 IP19 mips -// -// SGI Onyx IRIX 6.5: -// IRIX64 host 6.5 01091820 IP19 -// -// SparcStation 20 Solaris 8 w/uname from GNU shellutils: -// SunOS host.example.com 5.8 Generic_108528-12 sun4m sparc -// -// SparcStation 20 Solaris 8: -// SunOS host.example.com 5.8 Generic_108528-12 sun4m sparc SUNW,SPARCstation-20 -// -// Mac OS X (Darwin) -// Darwin home-eden.local 7.5.0 Darwin Kernel Version 7.5.0: Thu Aug 5 19:26:16 PDT 2004; root:xnu/xnu-517.7.21.obj~3/RELEASE_PPC Power Macintosh -// -// Mac OS X early versions -// - -// }}} - -/* TODO: - * - define endianness, to allow matchSignature("bigend") etc. - */ - -/** - * Retrieves information about the current operating system - * - * This class uses php_uname() to grok information about the current OS - * - * @category pear - * @package PEAR - * @author Stig Bakken - * @author Gregory Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 0.1 - */ -class OS_Guess -{ - var $sysname; - var $nodename; - var $cpu; - var $release; - var $extra; - - function OS_Guess($uname = null) - { - list($this->sysname, - $this->release, - $this->cpu, - $this->extra, - $this->nodename) = $this->parseSignature($uname); - } - - function parseSignature($uname = null) - { - static $sysmap = array( - 'HP-UX' => 'hpux', - 'IRIX64' => 'irix', - ); - static $cpumap = array( - 'i586' => 'i386', - 'i686' => 'i386', - 'ppc' => 'powerpc', - ); - if ($uname === null) { - $uname = php_uname(); - } - $parts = preg_split('/\s+/', trim($uname)); - $n = count($parts); - - $release = $machine = $cpu = ''; - $sysname = $parts[0]; - $nodename = $parts[1]; - $cpu = $parts[$n-1]; - $extra = ''; - if ($cpu == 'unknown') { - $cpu = $parts[$n - 2]; - } - - switch ($sysname) { - case 'AIX' : - $release = "$parts[3].$parts[2]"; - break; - case 'Windows' : - switch ($parts[1]) { - case '95/98': - $release = '9x'; - break; - default: - $release = $parts[1]; - break; - } - $cpu = 'i386'; - break; - case 'Linux' : - $extra = $this->_detectGlibcVersion(); - // use only the first two digits from the kernel version - $release = preg_replace('/^([0-9]+\.[0-9]+).*/', '\1', $parts[2]); - break; - case 'Mac' : - $sysname = 'darwin'; - $nodename = $parts[2]; - $release = $parts[3]; - if ($cpu == 'Macintosh') { - if ($parts[$n - 2] == 'Power') { - $cpu = 'powerpc'; - } - } - break; - case 'Darwin' : - if ($cpu == 'Macintosh') { - if ($parts[$n - 2] == 'Power') { - $cpu = 'powerpc'; - } - } - $release = preg_replace('/^([0-9]+\.[0-9]+).*/', '\1', $parts[2]); - break; - default: - $release = preg_replace('/-.*/', '', $parts[2]); - break; - } - - if (isset($sysmap[$sysname])) { - $sysname = $sysmap[$sysname]; - } else { - $sysname = strtolower($sysname); - } - if (isset($cpumap[$cpu])) { - $cpu = $cpumap[$cpu]; - } - return array($sysname, $release, $cpu, $extra, $nodename); - } - - function _detectGlibcVersion() - { - static $glibc = false; - if ($glibc !== false) { - return $glibc; // no need to run this multiple times - } - $major = $minor = 0; - include_once "System.php"; - // Use glibc's header file to - // get major and minor version number: - if (@file_exists('/usr/include/features.h') && - @is_readable('/usr/include/features.h')) { - if (!@file_exists('/usr/bin/cpp') || !@is_executable('/usr/bin/cpp')) { - $features_file = fopen('/usr/include/features.h', 'rb'); - while (!feof($features_file)) { - $line = fgets($features_file, 8192); - if (!$line || (strpos($line, '#define') === false)) { - continue; - } - if (strpos($line, '__GLIBC__')) { - // major version number #define __GLIBC__ version - $line = preg_split('/\s+/', $line); - $glibc_major = trim($line[2]); - if (isset($glibc_minor)) { - break; - } - continue; - } - - if (strpos($line, '__GLIBC_MINOR__')) { - // got the minor version number - // #define __GLIBC_MINOR__ version - $line = preg_split('/\s+/', $line); - $glibc_minor = trim($line[2]); - if (isset($glibc_major)) { - break; - } - continue; - } - } - fclose($features_file); - if (!isset($glibc_major) || !isset($glibc_minor)) { - return $glibc = ''; - } - return $glibc = 'glibc' . trim($glibc_major) . "." . trim($glibc_minor) ; - } // no cpp - - $tmpfile = System::mktemp("glibctest"); - $fp = fopen($tmpfile, "w"); - fwrite($fp, "#include \n__GLIBC__ __GLIBC_MINOR__\n"); - fclose($fp); - $cpp = popen("/usr/bin/cpp $tmpfile", "r"); - while ($line = fgets($cpp, 1024)) { - if ($line{0} == '#' || trim($line) == '') { - continue; - } - - if (list($major, $minor) = explode(' ', trim($line))) { - break; - } - } - pclose($cpp); - unlink($tmpfile); - } // features.h - - if (!($major && $minor) && @is_link('/lib/libc.so.6')) { - // Let's try reading the libc.so.6 symlink - if (preg_match('/^libc-(.*)\.so$/', basename(readlink('/lib/libc.so.6')), $matches)) { - list($major, $minor) = explode('.', $matches[1]); - } - } - - if (!($major && $minor)) { - return $glibc = ''; - } - - return $glibc = "glibc{$major}.{$minor}"; - } - - function getSignature() - { - if (empty($this->extra)) { - return "{$this->sysname}-{$this->release}-{$this->cpu}"; - } - return "{$this->sysname}-{$this->release}-{$this->cpu}-{$this->extra}"; - } - - function getSysname() - { - return $this->sysname; - } - - function getNodename() - { - return $this->nodename; - } - - function getCpu() - { - return $this->cpu; - } - - function getRelease() - { - return $this->release; - } - - function getExtra() - { - return $this->extra; - } - - function matchSignature($match) - { - $fragments = is_array($match) ? $match : explode('-', $match); - $n = count($fragments); - $matches = 0; - if ($n > 0) { - $matches += $this->_matchFragment($fragments[0], $this->sysname); - } - if ($n > 1) { - $matches += $this->_matchFragment($fragments[1], $this->release); - } - if ($n > 2) { - $matches += $this->_matchFragment($fragments[2], $this->cpu); - } - if ($n > 3) { - $matches += $this->_matchFragment($fragments[3], $this->extra); - } - return ($matches == $n); - } - - function _matchFragment($fragment, $value) - { - if (strcspn($fragment, '*?') < strlen($fragment)) { - $reg = '/^' . str_replace(array('*', '?', '/'), array('.*', '.', '\\/'), $fragment) . '\\z/'; - return preg_match($reg, $value); - } - return ($fragment == '*' || !strcasecmp($fragment, $value)); - } - -} -/* - * Local Variables: - * indent-tabs-mode: nil - * c-basic-offset: 4 - * End: - */ \ No newline at end of file diff --git a/pear/PEAR.php b/pear/PEAR.php deleted file mode 100644 index 48830fd..0000000 --- a/pear/PEAR.php +++ /dev/null @@ -1,1040 +0,0 @@ - - * @author Stig Bakken - * @author Tomas V.V.Cox - * @author Greg Beaver - * @copyright 1997-2010 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 0.1 - */ - -/**#@+ - * ERROR constants - */ -define('PEAR_ERROR_RETURN', 1); -define('PEAR_ERROR_PRINT', 2); -define('PEAR_ERROR_TRIGGER', 4); -define('PEAR_ERROR_DIE', 8); -define('PEAR_ERROR_CALLBACK', 16); -/** - * WARNING: obsolete - * @deprecated - */ -define('PEAR_ERROR_EXCEPTION', 32); -/**#@-*/ -define('PEAR_ZE2', (function_exists('version_compare') && - version_compare(zend_version(), "2-dev", "ge"))); - -if (substr(PHP_OS, 0, 3) == 'WIN') { - define('OS_WINDOWS', true); - define('OS_UNIX', false); - define('PEAR_OS', 'Windows'); -} else { - define('OS_WINDOWS', false); - define('OS_UNIX', true); - define('PEAR_OS', 'Unix'); // blatant assumption -} - -$GLOBALS['_PEAR_default_error_mode'] = PEAR_ERROR_RETURN; -$GLOBALS['_PEAR_default_error_options'] = E_USER_NOTICE; -$GLOBALS['_PEAR_destructor_object_list'] = array(); -$GLOBALS['_PEAR_shutdown_funcs'] = array(); -$GLOBALS['_PEAR_error_handler_stack'] = array(); - -@ini_set('track_errors', true); - -/** - * Base class for other PEAR classes. Provides rudimentary - * emulation of destructors. - * - * If you want a destructor in your class, inherit PEAR and make a - * destructor method called _yourclassname (same name as the - * constructor, but with a "_" prefix). Also, in your constructor you - * have to call the PEAR constructor: $this->PEAR();. - * The destructor method will be called without parameters. Note that - * at in some SAPI implementations (such as Apache), any output during - * the request shutdown (in which destructors are called) seems to be - * discarded. If you need to get any debug information from your - * destructor, use error_log(), syslog() or something similar. - * - * IMPORTANT! To use the emulated destructors you need to create the - * objects by reference: $obj =& new PEAR_child; - * - * @category pear - * @package PEAR - * @author Stig Bakken - * @author Tomas V.V. Cox - * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @see PEAR_Error - * @since Class available since PHP 4.0.2 - * @link http://pear.php.net/manual/en/core.pear.php#core.pear.pear - */ -class PEAR -{ - /** - * Whether to enable internal debug messages. - * - * @var bool - */ - private $_debug = false; - - /** - * Default error mode for this object. - * - * @var int - */ - private $_default_error_mode = null; - - /** - * Default error options used for this object when error mode - * is PEAR_ERROR_TRIGGER. - * - * @var int - */ - private $_default_error_options = null; - - /** - * Default error handler (callback) for this object, if error mode is - * PEAR_ERROR_CALLBACK. - * - * @var string - */ - private $_default_error_handler = ''; - - /** - * Which class to use for error objects. - * - * @var string - */ - private $_error_class = 'PEAR_Error'; - - /** - * An array of expected errors. - * - * @var array - */ - private $_expected_errors = array(); - - /** - * Constructor. Registers this object in - * $_PEAR_destructor_object_list for destructor emulation if a - * destructor object exists. - * - * @param string $error_class (optional) which class to use for - * error objects, defaults to PEAR_Error. - * @return void - */ - public function PEAR($error_class = null) - { - $classname = strtolower(get_class($this)); - if ($this->_debug) { - print "PEAR constructor called, class=$classname\n"; - } - - if ($error_class !== null) { - $this->_error_class = $error_class; - } - - while ($classname && strcasecmp($classname, "pear")) { - $destructor = "_$classname"; - if (method_exists($this, $destructor)) { - global $_PEAR_destructor_object_list; - $_PEAR_destructor_object_list[] = &$this; - if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) { - register_shutdown_function("_PEAR_call_destructors"); - $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true; - } - break; - } else { - $classname = get_parent_class($classname); - } - } - } - - /** - * Destructor (the emulated type of...). Does nothing right now, - * but is included for forward compatibility, so subclass - * destructors should always call it. - * - * See the note in the class desciption about output from - * destructors. - * - * @return void - */ - public function _PEAR() { - if ($this->_debug) { - printf("PEAR destructor called, class=%s\n", strtolower(get_class($this))); - } - } - - /** - * If you have a class that's mostly/entirely static, and you need static - * properties, you can use this method to simulate them. Eg. in your method(s) - * do this: $myVar = &PEAR::getStaticProperty('myclass', 'myVar'); - * You MUST use a reference, or they will not persist! - * - * @access public - * @param string $class The calling classname, to prevent clashes - * @param string $var The variable to retrieve. - * @return mixed A reference to the variable. If not set it will be - * auto initialised to NULL. - */ - public static function &getStaticProperty($class, $var) - { - static $properties; - if (!isset($properties[$class])) { - $properties[$class] = array(); - } - - if (!array_key_exists($var, $properties[$class])) { - $properties[$class][$var] = null; - } - - return $properties[$class][$var]; - } - - /** - * Use this function to register a shutdown method for static - * classes. - * - * @access public - * @param mixed $func The function name (or array of class/method) to call - * @param mixed $args The arguments to pass to the function - * @return void - */ - public static function registerShutdownFunc($func, $args = array()) - { - // if we are called statically, there is a potential - // that no shutdown func is registered. Bug #6445 - if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) { - register_shutdown_function("_PEAR_call_destructors"); - $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true; - } - $GLOBALS['_PEAR_shutdown_funcs'][] = array($func, $args); - } - - /** - * Tell whether a value is a PEAR error. - * - * @param mixed $data the value to test - * @param int $code if $data is an error object, return true - * only if $code is a string and - * $obj->getMessage() == $code or - * $code is an integer and $obj->getCode() == $code - * @return bool true if parameter is an error - */ - public static function isError($data, $code = null) - { - if (!is_object($data)) { - return false; - } - if (!is_a($data, 'PEAR_Error')) { - return false; - } - - if (is_null($code)) { - return true; - } elseif (is_string($code)) { - return $data->getMessage() == $code; - } - - return $data->getCode() == $code; - } - - /** - * Sets how errors generated by this object should be handled. - * Can be invoked both in objects and statically. If called - * statically, setErrorHandling sets the default behaviour for all - * PEAR objects. If called in an object, setErrorHandling sets - * the default behaviour for that object. - * - * @param int $mode - * One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT, - * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE, - * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION. - * - * @param mixed $options - * When $mode is PEAR_ERROR_TRIGGER, this is the error level (one - * of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR). - * - * When $mode is PEAR_ERROR_CALLBACK, this parameter is expected - * to be the callback function or method. A callback - * function is a string with the name of the function, a - * callback method is an array of two elements: the element - * at index 0 is the object, and the element at index 1 is - * the name of the method to call in the object. - * - * When $mode is PEAR_ERROR_PRINT or PEAR_ERROR_DIE, this is - * a printf format string used when printing the error - * message. - * - * @return void - * @see PEAR_ERROR_RETURN - * @see PEAR_ERROR_PRINT - * @see PEAR_ERROR_TRIGGER - * @see PEAR_ERROR_DIE - * @see PEAR_ERROR_CALLBACK - * @see PEAR_ERROR_EXCEPTION - * - * @since PHP 4.0.5 - */ - public function setErrorHandling($mode = null, $options = null) - { - if (isset($this) && is_a($this, 'PEAR')) { - $setmode = &$this->_default_error_mode; - $setoptions = &$this->_default_error_options; - } else { - $setmode = &$GLOBALS['_PEAR_default_error_mode']; - $setoptions = &$GLOBALS['_PEAR_default_error_options']; - } - - switch ($mode) { - case PEAR_ERROR_EXCEPTION: - case PEAR_ERROR_RETURN: - case PEAR_ERROR_PRINT: - case PEAR_ERROR_TRIGGER: - case PEAR_ERROR_DIE: - case null: - $setmode = $mode; - $setoptions = $options; - break; - - case PEAR_ERROR_CALLBACK: - $setmode = $mode; - // class/object method callback - if (is_callable($options)) { - $setoptions = $options; - } else { - trigger_error("invalid error callback", E_USER_WARNING); - } - break; - - default: - trigger_error("invalid error mode", E_USER_WARNING); - break; - } - } - - /** - * This method is used to tell which errors you expect to get. - * Expected errors are always returned with error mode - * PEAR_ERROR_RETURN. Expected error codes are stored in a stack, - * and this method pushes a new element onto it. The list of - * expected errors are in effect until they are popped off the - * stack with the popExpect() method. - * - * Note that this method can not be called statically - * - * @param mixed $code a single error code or an array of error codes to expect - * - * @return int the new depth of the "expected errors" stack - */ - public function expectError($code = '*') - { - if (is_array($code)) { - array_push($this->_expected_errors, $code); - } else { - array_push($this->_expected_errors, array($code)); - } - return count($this->_expected_errors); - } - - /** - * This method pops one element off the expected error codes - * stack. - * - * @return array the list of error codes that were popped - */ - public function popExpect() - { - return array_pop($this->_expected_errors); - } - - /** - * This method checks unsets an error code if available - * - * @param mixed error code - * @return bool true if the error code was unset, false otherwise - * @since PHP 4.3.0 - */ - private function _checkDelExpect($error_code) - { - $deleted = false; - foreach ($this->_expected_errors as $key => $error_array) { - if (in_array($error_code, $error_array)) { - unset($this->_expected_errors[$key][array_search($error_code, $error_array)]); - $deleted = true; - } - - // clean up empty arrays - if (0 == count($this->_expected_errors[$key])) { - unset($this->_expected_errors[$key]); - } - } - - return $deleted; - } - - /** - * This method deletes all occurences of the specified element from - * the expected error codes stack. - * - * @param mixed $error_code error code that should be deleted - * @return mixed list of error codes that were deleted or error - * @since PHP 4.3.0 - */ - public function delExpect($error_code) - { - $deleted = false; - if ((is_array($error_code) && (0 != count($error_code)))) { - // $error_code is a non-empty array here; we walk through it trying - // to unset all values - foreach ($error_code as $key => $error) { - $deleted = $this->_checkDelExpect($error) ? true : false; - } - - return $deleted ? true : PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME - } elseif (!empty($error_code)) { - // $error_code comes alone, trying to unset it - if ($this->_checkDelExpect($error_code)) { - return true; - } - - return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME - } - - // $error_code is empty - return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME - } - - /** - * This method is a wrapper that returns an instance of the - * configured error class with this object's default error - * handling applied. If the $mode and $options parameters are not - * specified, the object's defaults are used. - * - * @param mixed $message a text error message or a PEAR error object - * - * @param int $code a numeric error code (it is up to your class - * to define these if you want to use codes) - * - * @param int $mode One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT, - * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE, - * PEAR_ERROR_CALLBACK, PEAR_ERROR_EXCEPTION. - * - * @param mixed $options If $mode is PEAR_ERROR_TRIGGER, this parameter - * specifies the PHP-internal error level (one of - * E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR). - * If $mode is PEAR_ERROR_CALLBACK, this - * parameter specifies the callback function or - * method. In other error modes this parameter - * is ignored. - * - * @param string $userinfo If you need to pass along for example debug - * information, this parameter is meant for that. - * - * @param string $error_class The returned error object will be - * instantiated from this class, if specified. - * - * @param bool $skipmsg If true, raiseError will only pass error codes, - * the error message parameter will be dropped. - * - * @return object a PEAR error object - * @see PEAR::setErrorHandling - * @since PHP 4.0.5 - */ - public function &raiseError($message = null, - $code = null, - $mode = null, - $options = null, - $userinfo = null, - $error_class = null, - $skipmsg = false) - { - // The error is yet a PEAR error object - if (is_object($message)) { - $code = $message->getCode(); - $userinfo = $message->getUserInfo(); - $error_class = $message->getType(); - $message->error_message_prefix = ''; - $message = $message->getMessage(); - } - - if ( - isset($this) && - isset($this->_expected_errors) && - count($this->_expected_errors) > 0 && - count($exp = end($this->_expected_errors)) - ) { - if ($exp[0] == "*" || - (is_int(reset($exp)) && in_array($code, $exp)) || - (is_string(reset($exp)) && in_array($message, $exp)) - ) { - $mode = PEAR_ERROR_RETURN; - } - } - - // No mode given, try global ones - if ($mode === null) { - // Class error handler - if (isset($this) && isset($this->_default_error_mode)) { - $mode = $this->_default_error_mode; - $options = $this->_default_error_options; - // Global error handler - } elseif (isset($GLOBALS['_PEAR_default_error_mode'])) { - $mode = $GLOBALS['_PEAR_default_error_mode']; - $options = $GLOBALS['_PEAR_default_error_options']; - } - } - - if ($error_class !== null) { - $ec = $error_class; - } elseif (isset($this) && isset($this->_error_class)) { - $ec = $this->_error_class; - } else { - $ec = 'PEAR_Error'; - } - - if (intval(PHP_VERSION) < 5) { - // little non-eval hack to fix bug #12147 - include 'PEAR/FixPHP5PEARWarnings.php'; - return $a; - } - - if ($skipmsg) { - $a = new $ec($code, $mode, $options, $userinfo); - } else { - $a = new $ec($message, $code, $mode, $options, $userinfo); - } - - return $a; - } - - /** - * Simpler form of raiseError with fewer options. In most cases - * message, code and userinfo are enough. - * - * @param mixed $message a text error message or a PEAR error object - * - * @param int $code a numeric error code (it is up to your class - * to define these if you want to use codes) - * - * @param string $userinfo If you need to pass along for example debug - * information, this parameter is meant for that. - * - * @return object a PEAR error object - * @see PEAR::raiseError - */ - public function &throwError($message = null, $code = null, $userinfo = null) - { - if (isset($this) && is_a($this, 'PEAR')) { - $a = &$this->raiseError($message, $code, null, null, $userinfo); - return $a; - } - - $a = &PEAR::raiseError($message, $code, null, null, $userinfo); - return $a; - } - - public static function staticPushErrorHandling($mode, $options = null) - { - $stack = &$GLOBALS['_PEAR_error_handler_stack']; - $def_mode = &$GLOBALS['_PEAR_default_error_mode']; - $def_options = &$GLOBALS['_PEAR_default_error_options']; - $stack[] = array($def_mode, $def_options); - switch ($mode) { - case PEAR_ERROR_EXCEPTION: - case PEAR_ERROR_RETURN: - case PEAR_ERROR_PRINT: - case PEAR_ERROR_TRIGGER: - case PEAR_ERROR_DIE: - case null: - $def_mode = $mode; - $def_options = $options; - break; - - case PEAR_ERROR_CALLBACK: - $def_mode = $mode; - // class/object method callback - if (is_callable($options)) { - $def_options = $options; - } else { - trigger_error("invalid error callback", E_USER_WARNING); - } - break; - - default: - trigger_error("invalid error mode", E_USER_WARNING); - break; - } - $stack[] = array($mode, $options); - return true; - } - - public static function staticPopErrorHandling() - { - $stack = &$GLOBALS['_PEAR_error_handler_stack']; - $setmode = &$GLOBALS['_PEAR_default_error_mode']; - $setoptions = &$GLOBALS['_PEAR_default_error_options']; - array_pop($stack); - list($mode, $options) = $stack[sizeof($stack) - 1]; - array_pop($stack); - switch ($mode) { - case PEAR_ERROR_EXCEPTION: - case PEAR_ERROR_RETURN: - case PEAR_ERROR_PRINT: - case PEAR_ERROR_TRIGGER: - case PEAR_ERROR_DIE: - case null: - $setmode = $mode; - $setoptions = $options; - break; - - case PEAR_ERROR_CALLBACK: - $setmode = $mode; - // class/object method callback - if (is_callable($options)) { - $setoptions = $options; - } else { - trigger_error("invalid error callback", E_USER_WARNING); - } - break; - - default: - trigger_error("invalid error mode", E_USER_WARNING); - break; - } - return true; - } - - /** - * Push a new error handler on top of the error handler options stack. With this - * you can easily override the actual error handler for some code and restore - * it later with popErrorHandling. - * - * @param mixed $mode (same as setErrorHandling) - * @param mixed $options (same as setErrorHandling) - * - * @return bool Always true - * - * @see PEAR::setErrorHandling - */ - public function pushErrorHandling($mode, $options = null) - { - $stack = &$GLOBALS['_PEAR_error_handler_stack']; - if (isset($this) && is_a($this, 'PEAR')) { - $def_mode = &$this->_default_error_mode; - $def_options = &$this->_default_error_options; - } else { - $def_mode = &$GLOBALS['_PEAR_default_error_mode']; - $def_options = &$GLOBALS['_PEAR_default_error_options']; - } - $stack[] = array($def_mode, $def_options); - - if (isset($this) && is_a($this, 'PEAR')) { - $this->setErrorHandling($mode, $options); - } else { - PEAR::setErrorHandling($mode, $options); - } - $stack[] = array($mode, $options); - return true; - } - - /** - * Pop the last error handler used - * - * @return bool Always true - * - * @see PEAR::pushErrorHandling - */ - public function popErrorHandling() - { - $stack = &$GLOBALS['_PEAR_error_handler_stack']; - array_pop($stack); - list($mode, $options) = $stack[sizeof($stack) - 1]; - array_pop($stack); - if (isset($this) && is_a($this, 'PEAR')) { - $this->setErrorHandling($mode, $options); - } else { - PEAR::setErrorHandling($mode, $options); - } - return true; - } - - /** - * OS independant PHP extension load. Remember to take care - * on the correct extension name for case sensitive OSes. - * - * @param string $ext The extension name - * @return bool Success or not on the dl() call - */ - public function loadExtension($ext) - { - if (extension_loaded($ext)) { - return true; - } - - // if either returns true dl() will produce a FATAL error, stop that - if ( - function_exists('dl') === false || - ini_get('enable_dl') != 1 || - ini_get('safe_mode') == 1 - ) { - return false; - } - - if (OS_WINDOWS) { - $suffix = '.dll'; - } elseif (PHP_OS == 'HP-UX') { - $suffix = '.sl'; - } elseif (PHP_OS == 'AIX') { - $suffix = '.a'; - } elseif (PHP_OS == 'OSX') { - $suffix = '.bundle'; - } else { - $suffix = '.so'; - } - - return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix); - } -} - -if (PEAR_ZE2) { - include_once 'PEAR5.php'; -} - -function _PEAR_call_destructors() -{ - global $_PEAR_destructor_object_list; - if (is_array($_PEAR_destructor_object_list) && - sizeof($_PEAR_destructor_object_list)) - { - reset($_PEAR_destructor_object_list); - if (PEAR_ZE2) { - $destructLifoExists = PEAR5::getStaticProperty('PEAR', 'destructlifo'); - } else { - $destructLifoExists = PEAR::getStaticProperty('PEAR', 'destructlifo'); - } - - if ($destructLifoExists) { - $_PEAR_destructor_object_list = array_reverse($_PEAR_destructor_object_list); - } - - while (list($k, $objref) = each($_PEAR_destructor_object_list)) { - $classname = get_class($objref); - while ($classname) { - $destructor = "_$classname"; - if (method_exists($objref, $destructor)) { - $objref->$destructor(); - break; - } else { - $classname = get_parent_class($classname); - } - } - } - // Empty the object list to ensure that destructors are - // not called more than once. - $_PEAR_destructor_object_list = array(); - } - - // Now call the shutdown functions - if ( - isset($GLOBALS['_PEAR_shutdown_funcs']) && - is_array($GLOBALS['_PEAR_shutdown_funcs']) && - !empty($GLOBALS['_PEAR_shutdown_funcs']) - ) { - foreach ($GLOBALS['_PEAR_shutdown_funcs'] as $value) { - call_user_func_array($value[0], $value[1]); - } - } -} - -/** - * Standard PEAR error class for PHP 4 - * - * This class is supserseded by {@link PEAR_Exception} in PHP 5 - * - * @category pear - * @package PEAR - * @author Stig Bakken - * @author Tomas V.V. Cox - * @author Gregory Beaver - * @copyright 1997-2006 The PHP Group - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/manual/en/core.pear.pear-error.php - * @see PEAR::raiseError(), PEAR::throwError() - * @since Class available since PHP 4.0.2 - */ -class PEAR_Error -{ - var $error_message_prefix = ''; - var $mode = PEAR_ERROR_RETURN; - var $level = E_USER_NOTICE; - var $code = -1; - var $message = ''; - var $userinfo = ''; - var $backtrace = null; - - /** - * PEAR_Error constructor - * - * @param string $message message - * - * @param int $code (optional) error code - * - * @param int $mode (optional) error mode, one of: PEAR_ERROR_RETURN, - * PEAR_ERROR_PRINT, PEAR_ERROR_DIE, PEAR_ERROR_TRIGGER, - * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION - * - * @param mixed $options (optional) error level, _OR_ in the case of - * PEAR_ERROR_CALLBACK, the callback function or object/method - * tuple. - * - * @param string $userinfo (optional) additional user/debug info - * - */ - public function PEAR_Error($message = 'unknown error', $code = null, - $mode = null, $options = null, $userinfo = null) - { - if ($mode === null) { - $mode = PEAR_ERROR_RETURN; - } - $this->message = $message; - $this->code = $code; - $this->mode = $mode; - $this->userinfo = $userinfo; - - if (PEAR_ZE2) { - $skiptrace = PEAR5::getStaticProperty('PEAR_Error', 'skiptrace'); - } else { - $skiptrace = PEAR::getStaticProperty('PEAR_Error', 'skiptrace'); - } - - if (!$skiptrace) { - $this->backtrace = debug_backtrace(); - if (isset($this->backtrace[0]) && isset($this->backtrace[0]['object'])) { - unset($this->backtrace[0]['object']); - } - } - - if ($mode & PEAR_ERROR_CALLBACK) { - $this->level = E_USER_NOTICE; - $this->callback = $options; - } else { - if ($options === null) { - $options = E_USER_NOTICE; - } - - $this->level = $options; - $this->callback = null; - } - - if ($this->mode & PEAR_ERROR_PRINT) { - if (is_null($options) || is_int($options)) { - $format = "%s"; - } else { - $format = $options; - } - - printf($format, $this->getMessage()); - } - - if ($this->mode & PEAR_ERROR_TRIGGER) { - trigger_error($this->getMessage(), $this->level); - } - - if ($this->mode & PEAR_ERROR_DIE) { - $msg = $this->getMessage(); - if (is_null($options) || is_int($options)) { - $format = "%s"; - if (substr($msg, -1) != "\n") { - $msg .= "\n"; - } - } else { - $format = $options; - } - die(sprintf($format, $msg)); - } - - if ($this->mode & PEAR_ERROR_CALLBACK && is_callable($this->callback)) { - call_user_func($this->callback, $this); - } - - if ($this->mode & PEAR_ERROR_EXCEPTION) { - trigger_error("PEAR_ERROR_EXCEPTION is obsolete, use class PEAR_Exception for exceptions", E_USER_WARNING); - eval('$e = new Exception($this->message, $this->code);throw($e);'); - } - } - - /** - * Get the error mode from an error object. - * - * @return int error mode - */ - public function getMode() - { - return $this->mode; - } - - /** - * Get the callback function/method from an error object. - * - * @return mixed callback function or object/method array - */ - public function getCallback() - { - return $this->callback; - } - - /** - * Get the error message from an error object. - * - * @return string full error message - */ - public function getMessage() - { - return ($this->error_message_prefix . $this->message); - } - - /** - * Get error code from an error object - * - * @return int error code - */ - public function getCode() - { - return $this->code; - } - - /** - * Get the name of this error/exception. - * - * @return string error/exception name (type) - */ - public function getType() - { - return get_class($this); - } - - /** - * Get additional user-supplied information. - * - * @return string user-supplied information - */ - public function getUserInfo() - { - return $this->userinfo; - } - - /** - * Get additional debug information supplied by the application. - * - * @return string debug information - */ - public function getDebugInfo() - { - return $this->getUserInfo(); - } - - /** - * Get the call backtrace from where the error was generated. - * Supported with PHP 4.3.0 or newer. - * - * @param int $frame (optional) what frame to fetch - * @return array Backtrace, or NULL if not available. - */ - public function getBacktrace($frame = null) - { - if (defined('PEAR_IGNORE_BACKTRACE')) { - return null; - } - if ($frame === null) { - return $this->backtrace; - } - return $this->backtrace[$frame]; - } - - public function addUserInfo($info) - { - if (empty($this->userinfo)) { - $this->userinfo = $info; - } else { - $this->userinfo .= " ** $info"; - } - } - - public function __toString() - { - return $this->getMessage(); - } - - /** - * Make a string representation of this object. - * - * @return string a string with an object summary - */ - public function toString() - { - $modes = array(); - $levels = array(E_USER_NOTICE => 'notice', - E_USER_WARNING => 'warning', - E_USER_ERROR => 'error'); - if ($this->mode & PEAR_ERROR_CALLBACK) { - if (is_array($this->callback)) { - $callback = (is_object($this->callback[0]) ? - strtolower(get_class($this->callback[0])) : - $this->callback[0]) . '::' . - $this->callback[1]; - } else { - $callback = $this->callback; - } - return sprintf('[%s: message="%s" code=%d mode=callback '. - 'callback=%s prefix="%s" info="%s"]', - strtolower(get_class($this)), $this->message, $this->code, - $callback, $this->error_message_prefix, - $this->userinfo); - } - if ($this->mode & PEAR_ERROR_PRINT) { - $modes[] = 'print'; - } - if ($this->mode & PEAR_ERROR_TRIGGER) { - $modes[] = 'trigger'; - } - if ($this->mode & PEAR_ERROR_DIE) { - $modes[] = 'die'; - } - if ($this->mode & PEAR_ERROR_RETURN) { - $modes[] = 'return'; - } - return sprintf('[%s: message="%s" code=%d mode=%s level=%s '. - 'prefix="%s" info="%s"]', - strtolower(get_class($this)), $this->message, $this->code, - implode("|", $modes), $levels[$this->level], - $this->error_message_prefix, - $this->userinfo); - } -} - -/* - * Local Variables: - * mode: php - * tab-width: 4 - * c-basic-offset: 4 - * End: - */ diff --git a/pear/PEAR/Autoloader.php b/pear/PEAR/Autoloader.php deleted file mode 100644 index b833dc6..0000000 --- a/pear/PEAR/Autoloader.php +++ /dev/null @@ -1,218 +0,0 @@ - - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/manual/en/core.ppm.php#core.ppm.pear-autoloader - * @since File available since Release 0.1 - * @deprecated File deprecated in Release 1.4.0a1 - */ - -// /* vim: set expandtab tabstop=4 shiftwidth=4: */ - -if (!extension_loaded("overload")) { - // die hard without ext/overload - die("Rebuild PHP with the `overload' extension to use PEAR_Autoloader"); -} - -/** - * Include for PEAR_Error and PEAR classes - */ -require_once "PEAR.php"; - -/** - * This class is for objects where you want to separate the code for - * some methods into separate classes. This is useful if you have a - * class with not-frequently-used methods that contain lots of code - * that you would like to avoid always parsing. - * - * The PEAR_Autoloader class provides autoloading and aggregation. - * The autoloading lets you set up in which classes the separated - * methods are found. Aggregation is the technique used to import new - * methods, an instance of each class providing separated methods is - * stored and called every time the aggregated method is called. - * - * @category pear - * @package PEAR - * @author Stig Bakken - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/manual/en/core.ppm.php#core.ppm.pear-autoloader - * @since File available since Release 0.1 - * @deprecated File deprecated in Release 1.4.0a1 - */ -class PEAR_Autoloader extends PEAR -{ - // {{{ properties - - /** - * Map of methods and classes where they are defined - * - * @var array - * - * @access private - */ - var $_autoload_map = array(); - - /** - * Map of methods and aggregate objects - * - * @var array - * - * @access private - */ - var $_method_map = array(); - - // }}} - // {{{ addAutoload() - - /** - * Add one or more autoload entries. - * - * @param string $method which method to autoload - * - * @param string $classname (optional) which class to find the method in. - * If the $method parameter is an array, this - * parameter may be omitted (and will be ignored - * if not), and the $method parameter will be - * treated as an associative array with method - * names as keys and class names as values. - * - * @return void - * - * @access public - */ - function addAutoload($method, $classname = null) - { - if (is_array($method)) { - array_walk($method, create_function('$a,&$b', '$b = strtolower($b);')); - $this->_autoload_map = array_merge($this->_autoload_map, $method); - } else { - $this->_autoload_map[strtolower($method)] = $classname; - } - } - - // }}} - // {{{ removeAutoload() - - /** - * Remove an autoload entry. - * - * @param string $method which method to remove the autoload entry for - * - * @return bool TRUE if an entry was removed, FALSE if not - * - * @access public - */ - function removeAutoload($method) - { - $method = strtolower($method); - $ok = isset($this->_autoload_map[$method]); - unset($this->_autoload_map[$method]); - return $ok; - } - - // }}} - // {{{ addAggregateObject() - - /** - * Add an aggregate object to this object. If the specified class - * is not defined, loading it will be attempted following PEAR's - * file naming scheme. All the methods in the class will be - * aggregated, except private ones (name starting with an - * underscore) and constructors. - * - * @param string $classname what class to instantiate for the object. - * - * @return void - * - * @access public - */ - function addAggregateObject($classname) - { - $classname = strtolower($classname); - if (!class_exists($classname)) { - $include_file = preg_replace('/[^a-z0-9]/i', '_', $classname); - include_once $include_file; - } - $obj =& new $classname; - $methods = get_class_methods($classname); - foreach ($methods as $method) { - // don't import priviate methods and constructors - if ($method{0} != '_' && $method != $classname) { - $this->_method_map[$method] = $obj; - } - } - } - - // }}} - // {{{ removeAggregateObject() - - /** - * Remove an aggregate object. - * - * @param string $classname the class of the object to remove - * - * @return bool TRUE if an object was removed, FALSE if not - * - * @access public - */ - function removeAggregateObject($classname) - { - $ok = false; - $classname = strtolower($classname); - reset($this->_method_map); - while (list($method, $obj) = each($this->_method_map)) { - if (is_a($obj, $classname)) { - unset($this->_method_map[$method]); - $ok = true; - } - } - return $ok; - } - - // }}} - // {{{ __call() - - /** - * Overloaded object call handler, called each time an - * undefined/aggregated method is invoked. This method repeats - * the call in the right aggregate object and passes on the return - * value. - * - * @param string $method which method that was called - * - * @param string $args An array of the parameters passed in the - * original call - * - * @return mixed The return value from the aggregated method, or a PEAR - * error if the called method was unknown. - */ - function __call($method, $args, &$retval) - { - $method = strtolower($method); - if (empty($this->_method_map[$method]) && isset($this->_autoload_map[$method])) { - $this->addAggregateObject($this->_autoload_map[$method]); - } - if (isset($this->_method_map[$method])) { - $retval = call_user_func_array(array($this->_method_map[$method], $method), $args); - return true; - } - return false; - } - - // }}} -} - -overload("PEAR_Autoloader"); - -?> diff --git a/pear/PEAR/Builder.php b/pear/PEAR/Builder.php deleted file mode 100644 index b4fb430..0000000 --- a/pear/PEAR/Builder.php +++ /dev/null @@ -1,518 +0,0 @@ - - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 0.1 - * - * TODO: log output parameters in PECL command line - * TODO: msdev path in configuration - */ - -/** - * Needed for extending PEAR_Builder - */ -require_once 'PEAR/Common.php'; -require_once 'PEAR/PackageFile.php'; - -/** - * Class to handle building (compiling) extensions. - * - * @category pear - * @package PEAR - * @author Stig Bakken - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @PEAR-VER@ - * @link http://pear.php.net/package/PEAR - * @since Class available since PHP 4.0.2 - * @see http://pear.php.net/manual/en/core.ppm.pear-builder.php - */ -class PEAR_Builder extends PEAR_Common -{ - var $php_api_version = 0; - var $zend_module_api_no = 0; - var $zend_extension_api_no = 0; - - var $extensions_built = array(); - - /** - * @var string Used for reporting when it is not possible to pass function - * via extra parameter, e.g. log, msdevCallback - */ - var $current_callback = null; - - // used for msdev builds - var $_lastline = null; - var $_firstline = null; - - /** - * PEAR_Builder constructor. - * - * @param object $ui user interface object (instance of PEAR_Frontend_*) - * - * @access public - */ - function PEAR_Builder(&$ui) - { - parent::PEAR_Common(); - $this->setFrontendObject($ui); - } - - /** - * Build an extension from source on windows. - * requires msdev - */ - function _build_win32($descfile, $callback = null) - { - if (is_object($descfile)) { - $pkg = $descfile; - $descfile = $pkg->getPackageFile(); - } else { - $pf = &new PEAR_PackageFile($this->config, $this->debug); - $pkg = &$pf->fromPackageFile($descfile, PEAR_VALIDATE_NORMAL); - if (PEAR::isError($pkg)) { - return $pkg; - } - } - $dir = dirname($descfile); - $old_cwd = getcwd(); - - if (!file_exists($dir) || !is_dir($dir) || !chdir($dir)) { - return $this->raiseError("could not chdir to $dir"); - } - - // packages that were in a .tar have the packagefile in this directory - $vdir = $pkg->getPackage() . '-' . $pkg->getVersion(); - if (file_exists($dir) && is_dir($vdir)) { - if (!chdir($vdir)) { - return $this->raiseError("could not chdir to " . realpath($vdir)); - } - - $dir = getcwd(); - } - - $this->log(2, "building in $dir"); - - $dsp = $pkg->getPackage().'.dsp'; - if (!file_exists("$dir/$dsp")) { - return $this->raiseError("The DSP $dsp does not exist."); - } - // XXX TODO: make release build type configurable - $command = 'msdev '.$dsp.' /MAKE "'.$pkg->getPackage(). ' - Release"'; - - $err = $this->_runCommand($command, array(&$this, 'msdevCallback')); - if (PEAR::isError($err)) { - return $err; - } - - // figure out the build platform and type - $platform = 'Win32'; - $buildtype = 'Release'; - if (preg_match('/.*?'.$pkg->getPackage().'\s-\s(\w+)\s(.*?)-+/i',$this->_firstline,$matches)) { - $platform = $matches[1]; - $buildtype = $matches[2]; - } - - if (preg_match('/(.*)?\s-\s(\d+).*?(\d+)/', $this->_lastline, $matches)) { - if ($matches[2]) { - // there were errors in the build - return $this->raiseError("There were errors during compilation."); - } - $out = $matches[1]; - } else { - return $this->raiseError("Did not understand the completion status returned from msdev.exe."); - } - - // msdev doesn't tell us the output directory :/ - // open the dsp, find /out and use that directory - $dsptext = join(file($dsp),''); - - // this regex depends on the build platform and type having been - // correctly identified above. - $regex ='/.*?!IF\s+"\$\(CFG\)"\s+==\s+("'. - $pkg->getPackage().'\s-\s'. - $platform.'\s'. - $buildtype.'").*?'. - '\/out:"(.*?)"/is'; - - if ($dsptext && preg_match($regex, $dsptext, $matches)) { - // what we get back is a relative path to the output file itself. - $outfile = realpath($matches[2]); - } else { - return $this->raiseError("Could not retrieve output information from $dsp."); - } - // realpath returns false if the file doesn't exist - if ($outfile && copy($outfile, "$dir/$out")) { - $outfile = "$dir/$out"; - } - - $built_files[] = array( - 'file' => "$outfile", - 'php_api' => $this->php_api_version, - 'zend_mod_api' => $this->zend_module_api_no, - 'zend_ext_api' => $this->zend_extension_api_no, - ); - - return $built_files; - } - // }}} - - // {{{ msdevCallback() - function msdevCallback($what, $data) - { - if (!$this->_firstline) - $this->_firstline = $data; - $this->_lastline = $data; - call_user_func($this->current_callback, $what, $data); - } - - /** - * @param string - * @param string - * @param array - * @access private - */ - function _harvestInstDir($dest_prefix, $dirname, &$built_files) - { - $d = opendir($dirname); - if (!$d) - return false; - - $ret = true; - while (($ent = readdir($d)) !== false) { - if ($ent{0} == '.') - continue; - - $full = $dirname . DIRECTORY_SEPARATOR . $ent; - if (is_dir($full)) { - if (!$this->_harvestInstDir( - $dest_prefix . DIRECTORY_SEPARATOR . $ent, - $full, $built_files)) { - $ret = false; - break; - } - } else { - $dest = $dest_prefix . DIRECTORY_SEPARATOR . $ent; - $built_files[] = array( - 'file' => $full, - 'dest' => $dest, - 'php_api' => $this->php_api_version, - 'zend_mod_api' => $this->zend_module_api_no, - 'zend_ext_api' => $this->zend_extension_api_no, - ); - } - } - closedir($d); - return $ret; - } - - /** - * Build an extension from source. Runs "phpize" in the source - * directory, but compiles in a temporary directory - * (TMPDIR/pear-build-USER/PACKAGE-VERSION). - * - * @param string|PEAR_PackageFile_v* $descfile path to XML package description file, or - * a PEAR_PackageFile object - * - * @param mixed $callback callback function used to report output, - * see PEAR_Builder::_runCommand for details - * - * @return array an array of associative arrays with built files, - * format: - * array( array( 'file' => '/path/to/ext.so', - * 'php_api' => YYYYMMDD, - * 'zend_mod_api' => YYYYMMDD, - * 'zend_ext_api' => YYYYMMDD ), - * ... ) - * - * @access public - * - * @see PEAR_Builder::_runCommand - */ - function build($descfile, $callback = null, $options = array()) - { - if (preg_match('/(\\/|\\\\|^)([^\\/\\\\]+)?php(.+)?$/', - $this->config->get('php_bin'), $matches)) { - if (isset($matches[2]) && strlen($matches[2]) && - trim($matches[2]) != trim($this->config->get('php_prefix'))) { - $this->log(0, 'WARNING: php_bin ' . $this->config->get('php_bin') . - ' appears to have a prefix ' . $matches[2] . ', but' . - ' config variable php_prefix does not match'); - } - - if (isset($matches[3]) && strlen($matches[3]) && - trim($matches[3]) != trim($this->config->get('php_suffix'))) { - $this->log(0, 'WARNING: php_bin ' . $this->config->get('php_bin') . - ' appears to have a suffix ' . $matches[3] . ', but' . - ' config variable php_suffix does not match'); - } - } - - $this->current_callback = $callback; - if (PEAR_OS == "Windows") { - return $this->_build_win32($descfile, $callback); - } - - if (PEAR_OS != 'Unix') { - return $this->raiseError("building extensions not supported on this platform"); - } - - if (is_object($descfile)) { - $pkg = $descfile; - $descfile = $pkg->getPackageFile(); - if (is_a($pkg, 'PEAR_PackageFile_v1')) { - $dir = dirname($descfile); - } else { - $dir = $pkg->_config->get('temp_dir') . '/' . $pkg->getName(); - // automatically delete at session end - $this->addTempFile($dir); - } - } else { - $pf = &new PEAR_PackageFile($this->config); - $pkg = &$pf->fromPackageFile($descfile, PEAR_VALIDATE_NORMAL); - if (PEAR::isError($pkg)) { - return $pkg; - } - $dir = dirname($descfile); - } - - // Find config. outside of normal path - e.g. config.m4 - foreach (array_keys($pkg->getInstallationFileList()) as $item) { - if (stristr(basename($item), 'config.m4') && dirname($item) != '.') { - $dir .= DIRECTORY_SEPARATOR . dirname($item); - break; - } - } - - $old_cwd = getcwd(); - if (!file_exists($dir) || !is_dir($dir) || !chdir($dir)) { - return $this->raiseError("could not chdir to $dir"); - } - - $vdir = $pkg->getPackage() . '-' . $pkg->getVersion(); - if (is_dir($vdir)) { - chdir($vdir); - } - - $dir = getcwd(); - $this->log(2, "building in $dir"); - putenv('PATH=' . $this->config->get('bin_dir') . ':' . getenv('PATH')); - $err = $this->_runCommand($this->config->get('php_prefix') - . "phpize" . - $this->config->get('php_suffix'), - array(&$this, 'phpizeCallback')); - if (PEAR::isError($err)) { - return $err; - } - - if (!$err) { - return $this->raiseError("`phpize' failed"); - } - - // Figure out what params have been passed in to us already - formatting fixing - $opts = array(); - if (!empty($options)) { - foreach ($options as $op) { - $op = str_replace('--', '', $op); - list($name, $value) = explode('=', $op); - $opts[] = $name; - } - } - - // {{{ start of interactive part - $configure_command = "$dir/configure"; - $configure_options = $pkg->getConfigureOptions(); - if ($configure_options) { - foreach ($configure_options as $o) { - // skip params that have been passed already - if (in_array($o['name'], $opts)) { - continue; - } - - $default = array_key_exists('default', $o) ? $o['default'] : null; - list($r) = $this->ui->userDialog('build', - array($o['prompt']), - array('text'), - array($default)); - if (substr($o['name'], 0, 5) == 'with-' && - ($r == 'yes' || $r == 'autodetect')) { - $configure_command .= " --$o[name]"; - } else { - $configure_command .= " --$o[name]=".trim($r); - } - } - } - // }}} end of interactive part - - // Set any options that were passed in. - if (!empty($options)) { - foreach ($options as $op) { - $configure_command .= ' ' . $op; - } - } - - // FIXME make configurable - if (!$user = getenv('USER')){ - $user = 'defaultuser'; - } - - $tmpdir = $this->config->get('temp_dir'); - $build_basedir = System::mktemp(' -t "' . $tmpdir . '" -d "pear-build-' . $user . '"'); - $build_dir = "$build_basedir/$vdir"; - $inst_dir = "$build_basedir/install-$vdir"; - $this->log(1, "building in $build_dir"); - if (is_dir($build_dir)) { - System::rm(array('-rf', $build_dir)); - } - - if (!System::mkDir(array('-p', $build_dir))) { - return $this->raiseError("could not create build dir: $build_dir"); - } - - $this->addTempFile($build_dir); - if (!System::mkDir(array('-p', $inst_dir))) { - return $this->raiseError("could not create temporary install dir: $inst_dir"); - } - $this->addTempFile($inst_dir); - - $make_command = getenv('MAKE') ? getenv('MAKE') : 'make'; - - $to_run = array( - $configure_command, - $make_command, - "$make_command INSTALL_ROOT=\"$inst_dir\" install", - "find \"$inst_dir\" | xargs ls -dils" - ); - if (!file_exists($build_dir) || !is_dir($build_dir) || !chdir($build_dir)) { - return $this->raiseError("could not chdir to $build_dir"); - } - - putenv('PHP_PEAR_VERSION=1.9.4'); - foreach ($to_run as $cmd) { - $err = $this->_runCommand($cmd, $callback); - if (PEAR::isError($err)) { - chdir($old_cwd); - return $err; - } - if (!$err) { - chdir($old_cwd); - return $this->raiseError("`$cmd' failed"); - } - } - if (!($dp = opendir("modules"))) { - chdir($old_cwd); - return $this->raiseError("no `modules' directory found"); - } - - $built_files = array(); - $prefix = exec($this->config->get('php_prefix') - . "php-config" . - $this->config->get('php_suffix') . " --prefix"); - $ext_dir = $this->config->get('ext_dir'); - if (!$ext_dir) { - $ext_dir = $prefix; - } - $this->_harvestInstDir($ext_dir, $inst_dir . DIRECTORY_SEPARATOR . $prefix, $built_files); - - chdir($old_cwd); - return $built_files; - } - - /** - * Message callback function used when running the "phpize" - * program. Extracts the API numbers used. Ignores other message - * types than "cmdoutput". - * - * @param string $what the type of message - * @param mixed $data the message - * - * @return void - * - * @access public - */ - function phpizeCallback($what, $data) - { - if ($what != 'cmdoutput') { - return; - } - $this->log(1, rtrim($data)); - if (preg_match('/You should update your .aclocal.m4/', $data)) { - return; - } - $matches = array(); - if (preg_match('/^\s+(\S[^:]+):\s+(\d{8})/', $data, $matches)) { - $member = preg_replace('/[^a-z]/', '_', strtolower($matches[1])); - $apino = (int)$matches[2]; - if (isset($this->$member)) { - $this->$member = $apino; - //$msg = sprintf("%-22s : %d", $matches[1], $apino); - //$this->log(1, $msg); - } - } - } - - /** - * Run an external command, using a message callback to report - * output. The command will be run through popen and output is - * reported for every line with a "cmdoutput" message with the - * line string, including newlines, as payload. - * - * @param string $command the command to run - * - * @param mixed $callback (optional) function to use as message - * callback - * - * @return bool whether the command was successful (exit code 0 - * means success, any other means failure) - * - * @access private - */ - function _runCommand($command, $callback = null) - { - $this->log(1, "running: $command"); - $pp = popen("$command 2>&1", "r"); - if (!$pp) { - return $this->raiseError("failed to run `$command'"); - } - if ($callback && $callback[0]->debug == 1) { - $olddbg = $callback[0]->debug; - $callback[0]->debug = 2; - } - - while ($line = fgets($pp, 1024)) { - if ($callback) { - call_user_func($callback, 'cmdoutput', $line); - } else { - $this->log(2, rtrim($line)); - } - } - if ($callback && isset($olddbg)) { - $callback[0]->debug = $olddbg; - } - - $exitcode = is_resource($pp) ? pclose($pp) : -1; - return ($exitcode == 0); - } - - function log($level, $msg) - { - if ($this->current_callback) { - if ($this->debug >= $level) { - call_user_func($this->current_callback, 'output', $msg); - } - return; - } - return PEAR_Common::log($level, $msg); - } -} \ No newline at end of file diff --git a/pear/PEAR/ChannelFile.php b/pear/PEAR/ChannelFile.php deleted file mode 100644 index 9b27f28..0000000 --- a/pear/PEAR/ChannelFile.php +++ /dev/null @@ -1,1559 +0,0 @@ - - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a1 - */ - -/** - * Needed for error handling - */ -require_once 'PEAR/ErrorStack.php'; -require_once 'PEAR/XMLParser.php'; -require_once 'PEAR/Common.php'; - -/** - * Error code if the channel.xml tag does not contain a valid version - */ -define('PEAR_CHANNELFILE_ERROR_NO_VERSION', 1); -/** - * Error code if the channel.xml tag version is not supported (version 1.0 is the only supported version, - * currently - */ -define('PEAR_CHANNELFILE_ERROR_INVALID_VERSION', 2); - -/** - * Error code if parsing is attempted with no xml extension - */ -define('PEAR_CHANNELFILE_ERROR_NO_XML_EXT', 3); - -/** - * Error code if creating the xml parser resource fails - */ -define('PEAR_CHANNELFILE_ERROR_CANT_MAKE_PARSER', 4); - -/** - * Error code used for all sax xml parsing errors - */ -define('PEAR_CHANNELFILE_ERROR_PARSER_ERROR', 5); - -/**#@+ - * Validation errors - */ -/** - * Error code when channel name is missing - */ -define('PEAR_CHANNELFILE_ERROR_NO_NAME', 6); -/** - * Error code when channel name is invalid - */ -define('PEAR_CHANNELFILE_ERROR_INVALID_NAME', 7); -/** - * Error code when channel summary is missing - */ -define('PEAR_CHANNELFILE_ERROR_NO_SUMMARY', 8); -/** - * Error code when channel summary is multi-line - */ -define('PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY', 9); -/** - * Error code when channel server is missing for protocol - */ -define('PEAR_CHANNELFILE_ERROR_NO_HOST', 10); -/** - * Error code when channel server is invalid for protocol - */ -define('PEAR_CHANNELFILE_ERROR_INVALID_HOST', 11); -/** - * Error code when a mirror name is invalid - */ -define('PEAR_CHANNELFILE_ERROR_INVALID_MIRROR', 21); -/** - * Error code when a mirror type is invalid - */ -define('PEAR_CHANNELFILE_ERROR_INVALID_MIRRORTYPE', 22); -/** - * Error code when an attempt is made to generate xml, but the parsed content is invalid - */ -define('PEAR_CHANNELFILE_ERROR_INVALID', 23); -/** - * Error code when an empty package name validate regex is passed in - */ -define('PEAR_CHANNELFILE_ERROR_EMPTY_REGEX', 24); -/** - * Error code when a tag has no version - */ -define('PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION', 25); -/** - * Error code when a tag has no name - */ -define('PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME', 26); -/** - * Error code when a tag has no name - */ -define('PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME', 27); -/** - * Error code when a tag has no version attribute - */ -define('PEAR_CHANNELFILE_ERROR_NOVALIDATE_VERSION', 28); -/** - * Error code when a mirror does not exist but is called for in one of the set* - * methods. - */ -define('PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND', 32); -/** - * Error code when a server port is not numeric - */ -define('PEAR_CHANNELFILE_ERROR_INVALID_PORT', 33); -/** - * Error code when contains no version attribute - */ -define('PEAR_CHANNELFILE_ERROR_NO_STATICVERSION', 34); -/** - * Error code when contains no type attribute in a protocol definition - */ -define('PEAR_CHANNELFILE_ERROR_NOBASEURLTYPE', 35); -/** - * Error code when a mirror is defined and the channel.xml represents the __uri pseudo-channel - */ -define('PEAR_CHANNELFILE_URI_CANT_MIRROR', 36); -/** - * Error code when ssl attribute is present and is not "yes" - */ -define('PEAR_CHANNELFILE_ERROR_INVALID_SSL', 37); -/**#@-*/ - -/** - * Mirror types allowed. Currently only internet servers are recognized. - */ -$GLOBALS['_PEAR_CHANNELS_MIRROR_TYPES'] = array('server'); - - -/** - * The Channel handling class - * - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a1 - */ -class PEAR_ChannelFile -{ - /** - * @access private - * @var PEAR_ErrorStack - * @access private - */ - var $_stack; - - /** - * Supported channel.xml versions, for parsing - * @var array - * @access private - */ - var $_supportedVersions = array('1.0'); - - /** - * Parsed channel information - * @var array - * @access private - */ - var $_channelInfo; - - /** - * index into the subchannels array, used for parsing xml - * @var int - * @access private - */ - var $_subchannelIndex; - - /** - * index into the mirrors array, used for parsing xml - * @var int - * @access private - */ - var $_mirrorIndex; - - /** - * Flag used to determine the validity of parsed content - * @var boolean - * @access private - */ - var $_isValid = false; - - function PEAR_ChannelFile() - { - $this->_stack = &new PEAR_ErrorStack('PEAR_ChannelFile'); - $this->_stack->setErrorMessageTemplate($this->_getErrorMessage()); - $this->_isValid = false; - } - - /** - * @return array - * @access protected - */ - function _getErrorMessage() - { - return - array( - PEAR_CHANNELFILE_ERROR_INVALID_VERSION => - 'While parsing channel.xml, an invalid version number "%version% was passed in, expecting one of %versions%', - PEAR_CHANNELFILE_ERROR_NO_VERSION => - 'No version number found in tag', - PEAR_CHANNELFILE_ERROR_NO_XML_EXT => - '%error%', - PEAR_CHANNELFILE_ERROR_CANT_MAKE_PARSER => - 'Unable to create XML parser', - PEAR_CHANNELFILE_ERROR_PARSER_ERROR => - '%error%', - PEAR_CHANNELFILE_ERROR_NO_NAME => - 'Missing channel name', - PEAR_CHANNELFILE_ERROR_INVALID_NAME => - 'Invalid channel %tag% "%name%"', - PEAR_CHANNELFILE_ERROR_NO_SUMMARY => - 'Missing channel summary', - PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY => - 'Channel summary should be on one line, but is multi-line', - PEAR_CHANNELFILE_ERROR_NO_HOST => - 'Missing channel server for %type% server', - PEAR_CHANNELFILE_ERROR_INVALID_HOST => - 'Server name "%server%" is invalid for %type% server', - PEAR_CHANNELFILE_ERROR_INVALID_MIRROR => - 'Invalid mirror name "%name%", mirror type %type%', - PEAR_CHANNELFILE_ERROR_INVALID_MIRRORTYPE => - 'Invalid mirror type "%type%"', - PEAR_CHANNELFILE_ERROR_INVALID => - 'Cannot generate xml, contents are invalid', - PEAR_CHANNELFILE_ERROR_EMPTY_REGEX => - 'packagenameregex cannot be empty', - PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION => - '%parent% %protocol% function has no version', - PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME => - '%parent% %protocol% function has no name', - PEAR_CHANNELFILE_ERROR_NOBASEURLTYPE => - '%parent% rest baseurl has no type', - PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME => - 'Validation package has no name in tag', - PEAR_CHANNELFILE_ERROR_NOVALIDATE_VERSION => - 'Validation package "%package%" has no version', - PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND => - 'Mirror "%mirror%" does not exist', - PEAR_CHANNELFILE_ERROR_INVALID_PORT => - 'Port "%port%" must be numeric', - PEAR_CHANNELFILE_ERROR_NO_STATICVERSION => - ' tag must contain version attribute', - PEAR_CHANNELFILE_URI_CANT_MIRROR => - 'The __uri pseudo-channel cannot have mirrors', - PEAR_CHANNELFILE_ERROR_INVALID_SSL => - '%server% has invalid ssl attribute "%ssl%" can only be yes or not present', - ); - } - - /** - * @param string contents of package.xml file - * @return bool success of parsing - */ - function fromXmlString($data) - { - if (preg_match('/_supportedVersions)) { - $this->_stack->push(PEAR_CHANNELFILE_ERROR_INVALID_VERSION, 'error', - array('version' => $channelversion[1])); - return false; - } - $parser = new PEAR_XMLParser; - $result = $parser->parse($data); - if ($result !== true) { - if ($result->getCode() == 1) { - $this->_stack->push(PEAR_CHANNELFILE_ERROR_NO_XML_EXT, 'error', - array('error' => $result->getMessage())); - } else { - $this->_stack->push(PEAR_CHANNELFILE_ERROR_CANT_MAKE_PARSER, 'error'); - } - return false; - } - $this->_channelInfo = $parser->getData(); - return true; - } else { - $this->_stack->push(PEAR_CHANNELFILE_ERROR_NO_VERSION, 'error', array('xml' => $data)); - return false; - } - } - - /** - * @return array - */ - function toArray() - { - if (!$this->_isValid && !$this->validate()) { - return false; - } - return $this->_channelInfo; - } - - /** - * @param array - * @static - * @return PEAR_ChannelFile|false false if invalid - */ - function &fromArray($data, $compatibility = false, $stackClass = 'PEAR_ErrorStack') - { - $a = new PEAR_ChannelFile($compatibility, $stackClass); - $a->_fromArray($data); - if (!$a->validate()) { - $a = false; - return $a; - } - return $a; - } - - /** - * Unlike {@link fromArray()} this does not do any validation - * @param array - * @static - * @return PEAR_ChannelFile - */ - function &fromArrayWithErrors($data, $compatibility = false, - $stackClass = 'PEAR_ErrorStack') - { - $a = new PEAR_ChannelFile($compatibility, $stackClass); - $a->_fromArray($data); - return $a; - } - - /** - * @param array - * @access private - */ - function _fromArray($data) - { - $this->_channelInfo = $data; - } - - /** - * Wrapper to {@link PEAR_ErrorStack::getErrors()} - * @param boolean determines whether to purge the error stack after retrieving - * @return array - */ - function getErrors($purge = false) - { - return $this->_stack->getErrors($purge); - } - - /** - * Unindent given string (?) - * - * @param string $str The string that has to be unindented. - * @return string - * @access private - */ - function _unIndent($str) - { - // remove leading newlines - $str = preg_replace('/^[\r\n]+/', '', $str); - // find whitespace at the beginning of the first line - $indent_len = strspn($str, " \t"); - $indent = substr($str, 0, $indent_len); - $data = ''; - // remove the same amount of whitespace from following lines - foreach (explode("\n", $str) as $line) { - if (substr($line, 0, $indent_len) == $indent) { - $data .= substr($line, $indent_len) . "\n"; - } - } - return $data; - } - - /** - * Parse a channel.xml file. Expects the name of - * a channel xml file as input. - * - * @param string $descfile name of channel xml file - * @return bool success of parsing - */ - function fromXmlFile($descfile) - { - if (!file_exists($descfile) || !is_file($descfile) || !is_readable($descfile) || - (!$fp = fopen($descfile, 'r'))) { - require_once 'PEAR.php'; - return PEAR::raiseError("Unable to open $descfile"); - } - - // read the whole thing so we only get one cdata callback - // for each block of cdata - fclose($fp); - $data = file_get_contents($descfile); - return $this->fromXmlString($data); - } - - /** - * Parse channel information from different sources - * - * This method is able to extract information about a channel - * from an .xml file or a string - * - * @access public - * @param string Filename of the source or the source itself - * @return bool - */ - function fromAny($info) - { - if (is_string($info) && file_exists($info) && strlen($info) < 255) { - $tmp = substr($info, -4); - if ($tmp == '.xml') { - $info = $this->fromXmlFile($info); - } else { - $fp = fopen($info, "r"); - $test = fread($fp, 5); - fclose($fp); - if ($test == "fromXmlFile($info); - } - } - if (PEAR::isError($info)) { - require_once 'PEAR.php'; - return PEAR::raiseError($info); - } - } - if (is_string($info)) { - $info = $this->fromXmlString($info); - } - return $info; - } - - /** - * Return an XML document based on previous parsing and modifications - * - * @return string XML data - * - * @access public - */ - function toXml() - { - if (!$this->_isValid && !$this->validate()) { - $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID); - return false; - } - if (!isset($this->_channelInfo['attribs']['version'])) { - $this->_channelInfo['attribs']['version'] = '1.0'; - } - $channelInfo = $this->_channelInfo; - $ret = "\n"; - $ret .= " - $channelInfo[name] - " . htmlspecialchars($channelInfo['summary'])." -"; - if (isset($channelInfo['suggestedalias'])) { - $ret .= ' ' . $channelInfo['suggestedalias'] . "\n"; - } - if (isset($channelInfo['validatepackage'])) { - $ret .= ' ' . - htmlspecialchars($channelInfo['validatepackage']['_content']) . - "\n"; - } - $ret .= " \n"; - $ret .= ' _makeRestXml($channelInfo['servers']['primary']['rest'], ' '); - } - $ret .= " \n"; - if (isset($channelInfo['servers']['mirror'])) { - $ret .= $this->_makeMirrorsXml($channelInfo); - } - $ret .= " \n"; - $ret .= ""; - return str_replace("\r", "\n", str_replace("\r\n", "\n", $ret)); - } - - /** - * Generate the tag - * @access private - */ - function _makeRestXml($info, $indent) - { - $ret = $indent . "\n"; - if (isset($info['baseurl']) && !isset($info['baseurl'][0])) { - $info['baseurl'] = array($info['baseurl']); - } - - if (isset($info['baseurl'])) { - foreach ($info['baseurl'] as $url) { - $ret .= "$indent \n"; - } - } - $ret .= $indent . "\n"; - return $ret; - } - - /** - * Generate the tag - * @access private - */ - function _makeMirrorsXml($channelInfo) - { - $ret = ""; - if (!isset($channelInfo['servers']['mirror'][0])) { - $channelInfo['servers']['mirror'] = array($channelInfo['servers']['mirror']); - } - foreach ($channelInfo['servers']['mirror'] as $mirror) { - $ret .= ' _makeRestXml($mirror['rest'], ' '); - } - $ret .= " \n"; - } else { - $ret .= "/>\n"; - } - } - return $ret; - } - - /** - * Generate the tag - * @access private - */ - function _makeFunctionsXml($functions, $indent, $rest = false) - { - $ret = ''; - if (!isset($functions[0])) { - $functions = array($functions); - } - foreach ($functions as $function) { - $ret .= "$indent\n"; - } - return $ret; - } - - /** - * Validation error. Also marks the object contents as invalid - * @param error code - * @param array error information - * @access private - */ - function _validateError($code, $params = array()) - { - $this->_stack->push($code, 'error', $params); - $this->_isValid = false; - } - - /** - * Validation warning. Does not mark the object contents invalid. - * @param error code - * @param array error information - * @access private - */ - function _validateWarning($code, $params = array()) - { - $this->_stack->push($code, 'warning', $params); - } - - /** - * Validate parsed file. - * - * @access public - * @return boolean - */ - function validate() - { - $this->_isValid = true; - $info = $this->_channelInfo; - if (empty($info['name'])) { - $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_NAME); - } elseif (!$this->validChannelServer($info['name'])) { - if ($info['name'] != '__uri') { - $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME, array('tag' => 'name', - 'name' => $info['name'])); - } - } - if (empty($info['summary'])) { - $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SUMMARY); - } elseif (strpos(trim($info['summary']), "\n") !== false) { - $this->_validateWarning(PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY, - array('summary' => $info['summary'])); - } - if (isset($info['suggestedalias'])) { - if (!$this->validChannelServer($info['suggestedalias'])) { - $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME, - array('tag' => 'suggestedalias', 'name' =>$info['suggestedalias'])); - } - } - if (isset($info['localalias'])) { - if (!$this->validChannelServer($info['localalias'])) { - $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME, - array('tag' => 'localalias', 'name' =>$info['localalias'])); - } - } - if (isset($info['validatepackage'])) { - if (!isset($info['validatepackage']['_content'])) { - $this->_validateError(PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME); - } - if (!isset($info['validatepackage']['attribs']['version'])) { - $content = isset($info['validatepackage']['_content']) ? - $info['validatepackage']['_content'] : - null; - $this->_validateError(PEAR_CHANNELFILE_ERROR_NOVALIDATE_VERSION, - array('package' => $content)); - } - } - - if (isset($info['servers']['primary']['attribs'], $info['servers']['primary']['attribs']['port']) && - !is_numeric($info['servers']['primary']['attribs']['port'])) { - $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_PORT, - array('port' => $info['servers']['primary']['attribs']['port'])); - } - - if (isset($info['servers']['primary']['attribs'], $info['servers']['primary']['attribs']['ssl']) && - $info['servers']['primary']['attribs']['ssl'] != 'yes') { - $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_SSL, - array('ssl' => $info['servers']['primary']['attribs']['ssl'], - 'server' => $info['name'])); - } - - if (isset($info['servers']['primary']['rest']) && - isset($info['servers']['primary']['rest']['baseurl'])) { - $this->_validateFunctions('rest', $info['servers']['primary']['rest']['baseurl']); - } - if (isset($info['servers']['mirror'])) { - if ($this->_channelInfo['name'] == '__uri') { - $this->_validateError(PEAR_CHANNELFILE_URI_CANT_MIRROR); - } - if (!isset($info['servers']['mirror'][0])) { - $info['servers']['mirror'] = array($info['servers']['mirror']); - } - foreach ($info['servers']['mirror'] as $mirror) { - if (!isset($mirror['attribs']['host'])) { - $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_HOST, - array('type' => 'mirror')); - } elseif (!$this->validChannelServer($mirror['attribs']['host'])) { - $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_HOST, - array('server' => $mirror['attribs']['host'], 'type' => 'mirror')); - } - if (isset($mirror['attribs']['ssl']) && $mirror['attribs']['ssl'] != 'yes') { - $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_SSL, - array('ssl' => $info['ssl'], 'server' => $mirror['attribs']['host'])); - } - if (isset($mirror['rest'])) { - $this->_validateFunctions('rest', $mirror['rest']['baseurl'], - $mirror['attribs']['host']); - } - } - } - return $this->_isValid; - } - - /** - * @param string rest - protocol name this function applies to - * @param array the functions - * @param string the name of the parent element (mirror name, for instance) - */ - function _validateFunctions($protocol, $functions, $parent = '') - { - if (!isset($functions[0])) { - $functions = array($functions); - } - - foreach ($functions as $function) { - if (!isset($function['_content']) || empty($function['_content'])) { - $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME, - array('parent' => $parent, 'protocol' => $protocol)); - } - - if ($protocol == 'rest') { - if (!isset($function['attribs']['type']) || - empty($function['attribs']['type'])) { - $this->_validateError(PEAR_CHANNELFILE_ERROR_NOBASEURLTYPE, - array('parent' => $parent, 'protocol' => $protocol)); - } - } else { - if (!isset($function['attribs']['version']) || - empty($function['attribs']['version'])) { - $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION, - array('parent' => $parent, 'protocol' => $protocol)); - } - } - } - } - - /** - * Test whether a string contains a valid channel server. - * @param string $ver the package version to test - * @return bool - */ - function validChannelServer($server) - { - if ($server == '__uri') { - return true; - } - return (bool) preg_match(PEAR_CHANNELS_SERVER_PREG, $server); - } - - /** - * @return string|false - */ - function getName() - { - if (isset($this->_channelInfo['name'])) { - return $this->_channelInfo['name']; - } - - return false; - } - - /** - * @return string|false - */ - function getServer() - { - if (isset($this->_channelInfo['name'])) { - return $this->_channelInfo['name']; - } - - return false; - } - - /** - * @return int|80 port number to connect to - */ - function getPort($mirror = false) - { - if ($mirror) { - if ($mir = $this->getMirror($mirror)) { - if (isset($mir['attribs']['port'])) { - return $mir['attribs']['port']; - } - - if ($this->getSSL($mirror)) { - return 443; - } - - return 80; - } - - return false; - } - - if (isset($this->_channelInfo['servers']['primary']['attribs']['port'])) { - return $this->_channelInfo['servers']['primary']['attribs']['port']; - } - - if ($this->getSSL()) { - return 443; - } - - return 80; - } - - /** - * @return bool Determines whether secure sockets layer (SSL) is used to connect to this channel - */ - function getSSL($mirror = false) - { - if ($mirror) { - if ($mir = $this->getMirror($mirror)) { - if (isset($mir['attribs']['ssl'])) { - return true; - } - - return false; - } - - return false; - } - - if (isset($this->_channelInfo['servers']['primary']['attribs']['ssl'])) { - return true; - } - - return false; - } - - /** - * @return string|false - */ - function getSummary() - { - if (isset($this->_channelInfo['summary'])) { - return $this->_channelInfo['summary']; - } - - return false; - } - - /** - * @param string protocol type - * @param string Mirror name - * @return array|false - */ - function getFunctions($protocol, $mirror = false) - { - if ($this->getName() == '__uri') { - return false; - } - - $function = $protocol == 'rest' ? 'baseurl' : 'function'; - if ($mirror) { - if ($mir = $this->getMirror($mirror)) { - if (isset($mir[$protocol][$function])) { - return $mir[$protocol][$function]; - } - } - - return false; - } - - if (isset($this->_channelInfo['servers']['primary'][$protocol][$function])) { - return $this->_channelInfo['servers']['primary'][$protocol][$function]; - } - - return false; - } - - /** - * @param string Protocol type - * @param string Function name (null to return the - * first protocol of the type requested) - * @param string Mirror name, if any - * @return array - */ - function getFunction($type, $name = null, $mirror = false) - { - $protocols = $this->getFunctions($type, $mirror); - if (!$protocols) { - return false; - } - - foreach ($protocols as $protocol) { - if ($name === null) { - return $protocol; - } - - if ($protocol['_content'] != $name) { - continue; - } - - return $protocol; - } - - return false; - } - - /** - * @param string protocol type - * @param string protocol name - * @param string version - * @param string mirror name - * @return boolean - */ - function supports($type, $name = null, $mirror = false, $version = '1.0') - { - $protocols = $this->getFunctions($type, $mirror); - if (!$protocols) { - return false; - } - - foreach ($protocols as $protocol) { - if ($protocol['attribs']['version'] != $version) { - continue; - } - - if ($name === null) { - return true; - } - - if ($protocol['_content'] != $name) { - continue; - } - - return true; - } - - return false; - } - - /** - * Determines whether a channel supports Representational State Transfer (REST) protocols - * for retrieving channel information - * @param string - * @return bool - */ - function supportsREST($mirror = false) - { - if ($mirror == $this->_channelInfo['name']) { - $mirror = false; - } - - if ($mirror) { - if ($mir = $this->getMirror($mirror)) { - return isset($mir['rest']); - } - - return false; - } - - return isset($this->_channelInfo['servers']['primary']['rest']); - } - - /** - * Get the URL to access a base resource. - * - * Hyperlinks in the returned xml will be used to retrieve the proper information - * needed. This allows extreme extensibility and flexibility in implementation - * @param string Resource Type to retrieve - */ - function getBaseURL($resourceType, $mirror = false) - { - if ($mirror == $this->_channelInfo['name']) { - $mirror = false; - } - - if ($mirror) { - $mir = $this->getMirror($mirror); - if (!$mir) { - return false; - } - - $rest = $mir['rest']; - } else { - $rest = $this->_channelInfo['servers']['primary']['rest']; - } - - if (!isset($rest['baseurl'][0])) { - $rest['baseurl'] = array($rest['baseurl']); - } - - foreach ($rest['baseurl'] as $baseurl) { - if (strtolower($baseurl['attribs']['type']) == strtolower($resourceType)) { - return $baseurl['_content']; - } - } - - return false; - } - - /** - * Since REST does not implement RPC, provide this as a logical wrapper around - * resetFunctions for REST - * @param string|false mirror name, if any - */ - function resetREST($mirror = false) - { - return $this->resetFunctions('rest', $mirror); - } - - /** - * Empty all protocol definitions - * @param string protocol type - * @param string|false mirror name, if any - */ - function resetFunctions($type, $mirror = false) - { - if ($mirror) { - if (isset($this->_channelInfo['servers']['mirror'])) { - $mirrors = $this->_channelInfo['servers']['mirror']; - if (!isset($mirrors[0])) { - $mirrors = array($mirrors); - } - - foreach ($mirrors as $i => $mir) { - if ($mir['attribs']['host'] == $mirror) { - if (isset($this->_channelInfo['servers']['mirror'][$i][$type])) { - unset($this->_channelInfo['servers']['mirror'][$i][$type]); - } - - return true; - } - } - - return false; - } - - return false; - } - - if (isset($this->_channelInfo['servers']['primary'][$type])) { - unset($this->_channelInfo['servers']['primary'][$type]); - } - - return true; - } - - /** - * Set a channel's protocols to the protocols supported by pearweb - */ - function setDefaultPEARProtocols($version = '1.0', $mirror = false) - { - switch ($version) { - case '1.0' : - $this->resetREST($mirror); - - if (!isset($this->_channelInfo['servers'])) { - $this->_channelInfo['servers'] = array('primary' => - array('rest' => array())); - } elseif (!isset($this->_channelInfo['servers']['primary'])) { - $this->_channelInfo['servers']['primary'] = array('rest' => array()); - } - - return true; - break; - default : - return false; - break; - } - } - - /** - * @return array - */ - function getMirrors() - { - if (isset($this->_channelInfo['servers']['mirror'])) { - $mirrors = $this->_channelInfo['servers']['mirror']; - if (!isset($mirrors[0])) { - $mirrors = array($mirrors); - } - - return $mirrors; - } - - return array(); - } - - /** - * Get the unserialized XML representing a mirror - * @return array|false - */ - function getMirror($server) - { - foreach ($this->getMirrors() as $mirror) { - if ($mirror['attribs']['host'] == $server) { - return $mirror; - } - } - - return false; - } - - /** - * @param string - * @return string|false - * @error PEAR_CHANNELFILE_ERROR_NO_NAME - * @error PEAR_CHANNELFILE_ERROR_INVALID_NAME - */ - function setName($name) - { - return $this->setServer($name); - } - - /** - * Set the socket number (port) that is used to connect to this channel - * @param integer - * @param string|false name of the mirror server, or false for the primary - */ - function setPort($port, $mirror = false) - { - if ($mirror) { - if (!isset($this->_channelInfo['servers']['mirror'])) { - $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND, - array('mirror' => $mirror)); - return false; - } - - if (isset($this->_channelInfo['servers']['mirror'][0])) { - foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) { - if ($mirror == $mir['attribs']['host']) { - $this->_channelInfo['servers']['mirror'][$i]['attribs']['port'] = $port; - return true; - } - } - - return false; - } elseif ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) { - $this->_channelInfo['servers']['mirror']['attribs']['port'] = $port; - $this->_isValid = false; - return true; - } - } - - $this->_channelInfo['servers']['primary']['attribs']['port'] = $port; - $this->_isValid = false; - return true; - } - - /** - * Set the socket number (port) that is used to connect to this channel - * @param bool Determines whether to turn on SSL support or turn it off - * @param string|false name of the mirror server, or false for the primary - */ - function setSSL($ssl = true, $mirror = false) - { - if ($mirror) { - if (!isset($this->_channelInfo['servers']['mirror'])) { - $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND, - array('mirror' => $mirror)); - return false; - } - - if (isset($this->_channelInfo['servers']['mirror'][0])) { - foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) { - if ($mirror == $mir['attribs']['host']) { - if (!$ssl) { - if (isset($this->_channelInfo['servers']['mirror'][$i] - ['attribs']['ssl'])) { - unset($this->_channelInfo['servers']['mirror'][$i]['attribs']['ssl']); - } - } else { - $this->_channelInfo['servers']['mirror'][$i]['attribs']['ssl'] = 'yes'; - } - - return true; - } - } - - return false; - } elseif ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) { - if (!$ssl) { - if (isset($this->_channelInfo['servers']['mirror']['attribs']['ssl'])) { - unset($this->_channelInfo['servers']['mirror']['attribs']['ssl']); - } - } else { - $this->_channelInfo['servers']['mirror']['attribs']['ssl'] = 'yes'; - } - - $this->_isValid = false; - return true; - } - } - - if ($ssl) { - $this->_channelInfo['servers']['primary']['attribs']['ssl'] = 'yes'; - } else { - if (isset($this->_channelInfo['servers']['primary']['attribs']['ssl'])) { - unset($this->_channelInfo['servers']['primary']['attribs']['ssl']); - } - } - - $this->_isValid = false; - return true; - } - - /** - * @param string - * @return string|false - * @error PEAR_CHANNELFILE_ERROR_NO_SERVER - * @error PEAR_CHANNELFILE_ERROR_INVALID_SERVER - */ - function setServer($server, $mirror = false) - { - if (empty($server)) { - $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SERVER); - return false; - } elseif (!$this->validChannelServer($server)) { - $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME, - array('tag' => 'name', 'name' => $server)); - return false; - } - - if ($mirror) { - $found = false; - foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) { - if ($mirror == $mir['attribs']['host']) { - $found = true; - break; - } - } - - if (!$found) { - $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND, - array('mirror' => $mirror)); - return false; - } - - $this->_channelInfo['mirror'][$i]['attribs']['host'] = $server; - return true; - } - - $this->_channelInfo['name'] = $server; - return true; - } - - /** - * @param string - * @return boolean success - * @error PEAR_CHANNELFILE_ERROR_NO_SUMMARY - * @warning PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY - */ - function setSummary($summary) - { - if (empty($summary)) { - $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SUMMARY); - return false; - } elseif (strpos(trim($summary), "\n") !== false) { - $this->_validateWarning(PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY, - array('summary' => $summary)); - } - - $this->_channelInfo['summary'] = $summary; - return true; - } - - /** - * @param string - * @param boolean determines whether the alias is in channel.xml or local - * @return boolean success - */ - function setAlias($alias, $local = false) - { - if (!$this->validChannelServer($alias)) { - $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME, - array('tag' => 'suggestedalias', 'name' => $alias)); - return false; - } - - if ($local) { - $this->_channelInfo['localalias'] = $alias; - } else { - $this->_channelInfo['suggestedalias'] = $alias; - } - - return true; - } - - /** - * @return string - */ - function getAlias() - { - if (isset($this->_channelInfo['localalias'])) { - return $this->_channelInfo['localalias']; - } - if (isset($this->_channelInfo['suggestedalias'])) { - return $this->_channelInfo['suggestedalias']; - } - if (isset($this->_channelInfo['name'])) { - return $this->_channelInfo['name']; - } - return ''; - } - - /** - * Set the package validation object if it differs from PEAR's default - * The class must be includeable via changing _ in the classname to path separator, - * but no checking of this is made. - * @param string|false pass in false to reset to the default packagename regex - * @return boolean success - */ - function setValidationPackage($validateclass, $version) - { - if (empty($validateclass)) { - unset($this->_channelInfo['validatepackage']); - } - $this->_channelInfo['validatepackage'] = array('_content' => $validateclass); - $this->_channelInfo['validatepackage']['attribs'] = array('version' => $version); - } - - /** - * Add a protocol to the provides section - * @param string protocol type - * @param string protocol version - * @param string protocol name, if any - * @param string mirror name, if this is a mirror's protocol - * @return bool - */ - function addFunction($type, $version, $name = '', $mirror = false) - { - if ($mirror) { - return $this->addMirrorFunction($mirror, $type, $version, $name); - } - - $set = array('attribs' => array('version' => $version), '_content' => $name); - if (!isset($this->_channelInfo['servers']['primary'][$type]['function'])) { - if (!isset($this->_channelInfo['servers'])) { - $this->_channelInfo['servers'] = array('primary' => - array($type => array())); - } elseif (!isset($this->_channelInfo['servers']['primary'])) { - $this->_channelInfo['servers']['primary'] = array($type => array()); - } - - $this->_channelInfo['servers']['primary'][$type]['function'] = $set; - $this->_isValid = false; - return true; - } elseif (!isset($this->_channelInfo['servers']['primary'][$type]['function'][0])) { - $this->_channelInfo['servers']['primary'][$type]['function'] = array( - $this->_channelInfo['servers']['primary'][$type]['function']); - } - - $this->_channelInfo['servers']['primary'][$type]['function'][] = $set; - return true; - } - /** - * Add a protocol to a mirror's provides section - * @param string mirror name (server) - * @param string protocol type - * @param string protocol version - * @param string protocol name, if any - */ - function addMirrorFunction($mirror, $type, $version, $name = '') - { - if (!isset($this->_channelInfo['servers']['mirror'])) { - $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND, - array('mirror' => $mirror)); - return false; - } - - $setmirror = false; - if (isset($this->_channelInfo['servers']['mirror'][0])) { - foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) { - if ($mirror == $mir['attribs']['host']) { - $setmirror = &$this->_channelInfo['servers']['mirror'][$i]; - break; - } - } - } else { - if ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) { - $setmirror = &$this->_channelInfo['servers']['mirror']; - } - } - - if (!$setmirror) { - $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND, - array('mirror' => $mirror)); - return false; - } - - $set = array('attribs' => array('version' => $version), '_content' => $name); - if (!isset($setmirror[$type]['function'])) { - $setmirror[$type]['function'] = $set; - $this->_isValid = false; - return true; - } elseif (!isset($setmirror[$type]['function'][0])) { - $setmirror[$type]['function'] = array($setmirror[$type]['function']); - } - - $setmirror[$type]['function'][] = $set; - $this->_isValid = false; - return true; - } - - /** - * @param string Resource Type this url links to - * @param string URL - * @param string|false mirror name, if this is not a primary server REST base URL - */ - function setBaseURL($resourceType, $url, $mirror = false) - { - if ($mirror) { - if (!isset($this->_channelInfo['servers']['mirror'])) { - $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND, - array('mirror' => $mirror)); - return false; - } - - $setmirror = false; - if (isset($this->_channelInfo['servers']['mirror'][0])) { - foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) { - if ($mirror == $mir['attribs']['host']) { - $setmirror = &$this->_channelInfo['servers']['mirror'][$i]; - break; - } - } - } else { - if ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) { - $setmirror = &$this->_channelInfo['servers']['mirror']; - } - } - } else { - $setmirror = &$this->_channelInfo['servers']['primary']; - } - - $set = array('attribs' => array('type' => $resourceType), '_content' => $url); - if (!isset($setmirror['rest'])) { - $setmirror['rest'] = array(); - } - - if (!isset($setmirror['rest']['baseurl'])) { - $setmirror['rest']['baseurl'] = $set; - $this->_isValid = false; - return true; - } elseif (!isset($setmirror['rest']['baseurl'][0])) { - $setmirror['rest']['baseurl'] = array($setmirror['rest']['baseurl']); - } - - foreach ($setmirror['rest']['baseurl'] as $i => $url) { - if ($url['attribs']['type'] == $resourceType) { - $this->_isValid = false; - $setmirror['rest']['baseurl'][$i] = $set; - return true; - } - } - - $setmirror['rest']['baseurl'][] = $set; - $this->_isValid = false; - return true; - } - - /** - * @param string mirror server - * @param int mirror http port - * @return boolean - */ - function addMirror($server, $port = null) - { - if ($this->_channelInfo['name'] == '__uri') { - return false; // the __uri channel cannot have mirrors by definition - } - - $set = array('attribs' => array('host' => $server)); - if (is_numeric($port)) { - $set['attribs']['port'] = $port; - } - - if (!isset($this->_channelInfo['servers']['mirror'])) { - $this->_channelInfo['servers']['mirror'] = $set; - return true; - } - - if (!isset($this->_channelInfo['servers']['mirror'][0])) { - $this->_channelInfo['servers']['mirror'] = - array($this->_channelInfo['servers']['mirror']); - } - - $this->_channelInfo['servers']['mirror'][] = $set; - return true; - } - - /** - * Retrieve the name of the validation package for this channel - * @return string|false - */ - function getValidationPackage() - { - if (!$this->_isValid && !$this->validate()) { - return false; - } - - if (!isset($this->_channelInfo['validatepackage'])) { - return array('attribs' => array('version' => 'default'), - '_content' => 'PEAR_Validate'); - } - - return $this->_channelInfo['validatepackage']; - } - - /** - * Retrieve the object that can be used for custom validation - * @param string|false the name of the package to validate. If the package is - * the channel validation package, PEAR_Validate is returned - * @return PEAR_Validate|false false is returned if the validation package - * cannot be located - */ - function &getValidationObject($package = false) - { - if (!class_exists('PEAR_Validate')) { - require_once 'PEAR/Validate.php'; - } - - if (!$this->_isValid) { - if (!$this->validate()) { - $a = false; - return $a; - } - } - - if (isset($this->_channelInfo['validatepackage'])) { - if ($package == $this->_channelInfo['validatepackage']) { - // channel validation packages are always validated by PEAR_Validate - $val = &new PEAR_Validate; - return $val; - } - - if (!class_exists(str_replace('.', '_', - $this->_channelInfo['validatepackage']['_content']))) { - if ($this->isIncludeable(str_replace('_', '/', - $this->_channelInfo['validatepackage']['_content']) . '.php')) { - include_once str_replace('_', '/', - $this->_channelInfo['validatepackage']['_content']) . '.php'; - $vclass = str_replace('.', '_', - $this->_channelInfo['validatepackage']['_content']); - $val = &new $vclass; - } else { - $a = false; - return $a; - } - } else { - $vclass = str_replace('.', '_', - $this->_channelInfo['validatepackage']['_content']); - $val = &new $vclass; - } - } else { - $val = &new PEAR_Validate; - } - - return $val; - } - - function isIncludeable($path) - { - $possibilities = explode(PATH_SEPARATOR, ini_get('include_path')); - foreach ($possibilities as $dir) { - if (file_exists($dir . DIRECTORY_SEPARATOR . $path) - && is_readable($dir . DIRECTORY_SEPARATOR . $path)) { - return true; - } - } - - return false; - } - - /** - * This function is used by the channel updater and retrieves a value set by - * the registry, or the current time if it has not been set - * @return string - */ - function lastModified() - { - if (isset($this->_channelInfo['_lastmodified'])) { - return $this->_channelInfo['_lastmodified']; - } - - return time(); - } -} \ No newline at end of file diff --git a/pear/PEAR/ChannelFile/Parser.php b/pear/PEAR/ChannelFile/Parser.php deleted file mode 100644 index 5416c3d..0000000 --- a/pear/PEAR/ChannelFile/Parser.php +++ /dev/null @@ -1,68 +0,0 @@ - - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a1 - */ - -/** - * base xml parser class - */ -require_once 'PEAR/XMLParser.php'; -require_once 'PEAR/ChannelFile.php'; -/** - * Parser for channel.xml - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a1 - */ -class PEAR_ChannelFile_Parser extends PEAR_XMLParser -{ - var $_config; - var $_logger; - var $_registry; - - function setConfig(&$c) - { - $this->_config = &$c; - $this->_registry = &$c->getRegistry(); - } - - function setLogger(&$l) - { - $this->_logger = &$l; - } - - function parse($data, $file) - { - if (PEAR::isError($err = parent::parse($data, $file))) { - return $err; - } - - $ret = new PEAR_ChannelFile; - $ret->setConfig($this->_config); - if (isset($this->_logger)) { - $ret->setLogger($this->_logger); - } - - $ret->fromArray($this->_unserializedData); - // make sure the filelist is in the easy to read format needed - $ret->flattenFilelist(); - $ret->setPackagefile($file, $archive); - return $ret; - } -} \ No newline at end of file diff --git a/pear/PEAR/Command.php b/pear/PEAR/Command.php deleted file mode 100644 index 5dd3cc5..0000000 --- a/pear/PEAR/Command.php +++ /dev/null @@ -1,414 +0,0 @@ - - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 0.1 - */ - -/** - * Needed for error handling - */ -require_once 'PEAR.php'; -require_once 'PEAR/Frontend.php'; -require_once 'PEAR/XMLParser.php'; - -/** - * List of commands and what classes they are implemented in. - * @var array command => implementing class - */ -$GLOBALS['_PEAR_Command_commandlist'] = array(); - -/** - * List of commands and their descriptions - * @var array command => description - */ -$GLOBALS['_PEAR_Command_commanddesc'] = array(); - -/** - * List of shortcuts to common commands. - * @var array shortcut => command - */ -$GLOBALS['_PEAR_Command_shortcuts'] = array(); - -/** - * Array of command objects - * @var array class => object - */ -$GLOBALS['_PEAR_Command_objects'] = array(); - -/** - * PEAR command class, a simple factory class for administrative - * commands. - * - * How to implement command classes: - * - * - The class must be called PEAR_Command_Nnn, installed in the - * "PEAR/Common" subdir, with a method called getCommands() that - * returns an array of the commands implemented by the class (see - * PEAR/Command/Install.php for an example). - * - * - The class must implement a run() function that is called with three - * params: - * - * (string) command name - * (array) assoc array with options, freely defined by each - * command, for example: - * array('force' => true) - * (array) list of the other parameters - * - * The run() function returns a PEAR_CommandResponse object. Use - * these methods to get information: - * - * int getStatus() Returns PEAR_COMMAND_(SUCCESS|FAILURE|PARTIAL) - * *_PARTIAL means that you need to issue at least - * one more command to complete the operation - * (used for example for validation steps). - * - * string getMessage() Returns a message for the user. Remember, - * no HTML or other interface-specific markup. - * - * If something unexpected happens, run() returns a PEAR error. - * - * - DON'T OUTPUT ANYTHING! Return text for output instead. - * - * - DON'T USE HTML! The text you return will be used from both Gtk, - * web and command-line interfaces, so for now, keep everything to - * plain text. - * - * - DON'T USE EXIT OR DIE! Always use pear errors. From static - * classes do PEAR::raiseError(), from other classes do - * $this->raiseError(). - * @category pear - * @package PEAR - * @author Stig Bakken - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 0.1 - */ -class PEAR_Command -{ - // {{{ factory() - - /** - * Get the right object for executing a command. - * - * @param string $command The name of the command - * @param object $config Instance of PEAR_Config object - * - * @return object the command object or a PEAR error - * - * @access public - * @static - */ - function &factory($command, &$config) - { - if (empty($GLOBALS['_PEAR_Command_commandlist'])) { - PEAR_Command::registerCommands(); - } - if (isset($GLOBALS['_PEAR_Command_shortcuts'][$command])) { - $command = $GLOBALS['_PEAR_Command_shortcuts'][$command]; - } - if (!isset($GLOBALS['_PEAR_Command_commandlist'][$command])) { - $a = PEAR::raiseError("unknown command `$command'"); - return $a; - } - $class = $GLOBALS['_PEAR_Command_commandlist'][$command]; - if (!class_exists($class)) { - require_once $GLOBALS['_PEAR_Command_objects'][$class]; - } - if (!class_exists($class)) { - $a = PEAR::raiseError("unknown command `$command'"); - return $a; - } - $ui =& PEAR_Command::getFrontendObject(); - $obj = &new $class($ui, $config); - return $obj; - } - - // }}} - // {{{ & getObject() - function &getObject($command) - { - $class = $GLOBALS['_PEAR_Command_commandlist'][$command]; - if (!class_exists($class)) { - require_once $GLOBALS['_PEAR_Command_objects'][$class]; - } - if (!class_exists($class)) { - return PEAR::raiseError("unknown command `$command'"); - } - $ui =& PEAR_Command::getFrontendObject(); - $config = &PEAR_Config::singleton(); - $obj = &new $class($ui, $config); - return $obj; - } - - // }}} - // {{{ & getFrontendObject() - - /** - * Get instance of frontend object. - * - * @return object|PEAR_Error - * @static - */ - function &getFrontendObject() - { - $a = &PEAR_Frontend::singleton(); - return $a; - } - - // }}} - // {{{ & setFrontendClass() - - /** - * Load current frontend class. - * - * @param string $uiclass Name of class implementing the frontend - * - * @return object the frontend object, or a PEAR error - * @static - */ - function &setFrontendClass($uiclass) - { - $a = &PEAR_Frontend::setFrontendClass($uiclass); - return $a; - } - - // }}} - // {{{ setFrontendType() - - /** - * Set current frontend. - * - * @param string $uitype Name of the frontend type (for example "CLI") - * - * @return object the frontend object, or a PEAR error - * @static - */ - function setFrontendType($uitype) - { - $uiclass = 'PEAR_Frontend_' . $uitype; - return PEAR_Command::setFrontendClass($uiclass); - } - - // }}} - // {{{ registerCommands() - - /** - * Scan through the Command directory looking for classes - * and see what commands they implement. - * - * @param bool (optional) if FALSE (default), the new list of - * commands should replace the current one. If TRUE, - * new entries will be merged with old. - * - * @param string (optional) where (what directory) to look for - * classes, defaults to the Command subdirectory of - * the directory from where this file (__FILE__) is - * included. - * - * @return bool TRUE on success, a PEAR error on failure - * - * @access public - * @static - */ - function registerCommands($merge = false, $dir = null) - { - $parser = new PEAR_XMLParser; - if ($dir === null) { - $dir = dirname(__FILE__) . '/Command'; - } - if (!is_dir($dir)) { - return PEAR::raiseError("registerCommands: opendir($dir) '$dir' does not exist or is not a directory"); - } - $dp = @opendir($dir); - if (empty($dp)) { - return PEAR::raiseError("registerCommands: opendir($dir) failed"); - } - if (!$merge) { - $GLOBALS['_PEAR_Command_commandlist'] = array(); - } - - while ($file = readdir($dp)) { - if ($file{0} == '.' || substr($file, -4) != '.xml') { - continue; - } - - $f = substr($file, 0, -4); - $class = "PEAR_Command_" . $f; - // List of commands - if (empty($GLOBALS['_PEAR_Command_objects'][$class])) { - $GLOBALS['_PEAR_Command_objects'][$class] = "$dir/" . $f . '.php'; - } - - $parser->parse(file_get_contents("$dir/$file")); - $implements = $parser->getData(); - foreach ($implements as $command => $desc) { - if ($command == 'attribs') { - continue; - } - - if (isset($GLOBALS['_PEAR_Command_commandlist'][$command])) { - return PEAR::raiseError('Command "' . $command . '" already registered in ' . - 'class "' . $GLOBALS['_PEAR_Command_commandlist'][$command] . '"'); - } - - $GLOBALS['_PEAR_Command_commandlist'][$command] = $class; - $GLOBALS['_PEAR_Command_commanddesc'][$command] = $desc['summary']; - if (isset($desc['shortcut'])) { - $shortcut = $desc['shortcut']; - if (isset($GLOBALS['_PEAR_Command_shortcuts'][$shortcut])) { - return PEAR::raiseError('Command shortcut "' . $shortcut . '" already ' . - 'registered to command "' . $command . '" in class "' . - $GLOBALS['_PEAR_Command_commandlist'][$command] . '"'); - } - $GLOBALS['_PEAR_Command_shortcuts'][$shortcut] = $command; - } - - if (isset($desc['options']) && $desc['options']) { - foreach ($desc['options'] as $oname => $option) { - if (isset($option['shortopt']) && strlen($option['shortopt']) > 1) { - return PEAR::raiseError('Option "' . $oname . '" short option "' . - $option['shortopt'] . '" must be ' . - 'only 1 character in Command "' . $command . '" in class "' . - $class . '"'); - } - } - } - } - } - - ksort($GLOBALS['_PEAR_Command_shortcuts']); - ksort($GLOBALS['_PEAR_Command_commandlist']); - @closedir($dp); - return true; - } - - // }}} - // {{{ getCommands() - - /** - * Get the list of currently supported commands, and what - * classes implement them. - * - * @return array command => implementing class - * - * @access public - * @static - */ - function getCommands() - { - if (empty($GLOBALS['_PEAR_Command_commandlist'])) { - PEAR_Command::registerCommands(); - } - return $GLOBALS['_PEAR_Command_commandlist']; - } - - // }}} - // {{{ getShortcuts() - - /** - * Get the list of command shortcuts. - * - * @return array shortcut => command - * - * @access public - * @static - */ - function getShortcuts() - { - if (empty($GLOBALS['_PEAR_Command_shortcuts'])) { - PEAR_Command::registerCommands(); - } - return $GLOBALS['_PEAR_Command_shortcuts']; - } - - // }}} - // {{{ getGetoptArgs() - - /** - * Compiles arguments for getopt. - * - * @param string $command command to get optstring for - * @param string $short_args (reference) short getopt format - * @param array $long_args (reference) long getopt format - * - * @return void - * - * @access public - * @static - */ - function getGetoptArgs($command, &$short_args, &$long_args) - { - if (empty($GLOBALS['_PEAR_Command_commandlist'])) { - PEAR_Command::registerCommands(); - } - if (isset($GLOBALS['_PEAR_Command_shortcuts'][$command])) { - $command = $GLOBALS['_PEAR_Command_shortcuts'][$command]; - } - if (!isset($GLOBALS['_PEAR_Command_commandlist'][$command])) { - return null; - } - $obj = &PEAR_Command::getObject($command); - return $obj->getGetoptArgs($command, $short_args, $long_args); - } - - // }}} - // {{{ getDescription() - - /** - * Get description for a command. - * - * @param string $command Name of the command - * - * @return string command description - * - * @access public - * @static - */ - function getDescription($command) - { - if (!isset($GLOBALS['_PEAR_Command_commanddesc'][$command])) { - return null; - } - return $GLOBALS['_PEAR_Command_commanddesc'][$command]; - } - - // }}} - // {{{ getHelp() - - /** - * Get help for command. - * - * @param string $command Name of the command to return help for - * - * @access public - * @static - */ - function getHelp($command) - { - $cmds = PEAR_Command::getCommands(); - if (isset($GLOBALS['_PEAR_Command_shortcuts'][$command])) { - $command = $GLOBALS['_PEAR_Command_shortcuts'][$command]; - } - if (isset($cmds[$command])) { - $obj = &PEAR_Command::getObject($command); - return $obj->getHelp($command); - } - return false; - } - // }}} -} \ No newline at end of file diff --git a/pear/PEAR/Command/Auth.php b/pear/PEAR/Command/Auth.php deleted file mode 100644 index 5376bc3..0000000 --- a/pear/PEAR/Command/Auth.php +++ /dev/null @@ -1,81 +0,0 @@ - - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 0.1 - * @deprecated since 1.8.0alpha1 - */ - -/** - * base class - */ -require_once 'PEAR/Command/Channels.php'; - -/** - * PEAR commands for login/logout - * - * @category pear - * @package PEAR - * @author Stig Bakken - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 0.1 - * @deprecated since 1.8.0alpha1 - */ -class PEAR_Command_Auth extends PEAR_Command_Channels -{ - var $commands = array( - 'login' => array( - 'summary' => 'Connects and authenticates to remote server [Deprecated in favor of channel-login]', - 'shortcut' => 'li', - 'function' => 'doLogin', - 'options' => array(), - 'doc' => ' -WARNING: This function is deprecated in favor of using channel-login - -Log in to a remote channel server. If is not supplied, -the default channel is used. To use remote functions in the installer -that require any kind of privileges, you need to log in first. The -username and password you enter here will be stored in your per-user -PEAR configuration (~/.pearrc on Unix-like systems). After logging -in, your username and password will be sent along in subsequent -operations on the remote server.', - ), - 'logout' => array( - 'summary' => 'Logs out from the remote server [Deprecated in favor of channel-logout]', - 'shortcut' => 'lo', - 'function' => 'doLogout', - 'options' => array(), - 'doc' => ' -WARNING: This function is deprecated in favor of using channel-logout - -Logs out from the remote server. This command does not actually -connect to the remote server, it only deletes the stored username and -password from your user configuration.', - ) - - ); - - /** - * PEAR_Command_Auth constructor. - * - * @access public - */ - function PEAR_Command_Auth(&$ui, &$config) - { - parent::PEAR_Command_Channels($ui, $config); - } -} \ No newline at end of file diff --git a/pear/PEAR/Command/Auth.xml b/pear/PEAR/Command/Auth.xml deleted file mode 100644 index 590193d..0000000 --- a/pear/PEAR/Command/Auth.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - Connects and authenticates to remote server [Deprecated in favor of channel-login] - doLogin - li - - <channel name> -WARNING: This function is deprecated in favor of using channel-login - -Log in to a remote channel server. If <channel name> is not supplied, -the default channel is used. To use remote functions in the installer -that require any kind of privileges, you need to log in first. The -username and password you enter here will be stored in your per-user -PEAR configuration (~/.pearrc on Unix-like systems). After logging -in, your username and password will be sent along in subsequent -operations on the remote server. - - - Logs out from the remote server [Deprecated in favor of channel-logout] - doLogout - lo - - -WARNING: This function is deprecated in favor of using channel-logout - -Logs out from the remote server. This command does not actually -connect to the remote server, it only deletes the stored username and -password from your user configuration. - - \ No newline at end of file diff --git a/pear/PEAR/Command/Build.php b/pear/PEAR/Command/Build.php deleted file mode 100644 index 67a61d3..0000000 --- a/pear/PEAR/Command/Build.php +++ /dev/null @@ -1,85 +0,0 @@ - - * @author Tomas V.V.Cox - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 0.1 - */ - -/** - * base class - */ -require_once 'PEAR/Command/Common.php'; - -/** - * PEAR commands for building extensions. - * - * @category pear - * @package PEAR - * @author Stig Bakken - * @author Tomas V.V.Cox - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 0.1 - */ -class PEAR_Command_Build extends PEAR_Command_Common -{ - var $commands = array( - 'build' => array( - 'summary' => 'Build an Extension From C Source', - 'function' => 'doBuild', - 'shortcut' => 'b', - 'options' => array(), - 'doc' => '[package.xml] -Builds one or more extensions contained in a package.' - ), - ); - - /** - * PEAR_Command_Build constructor. - * - * @access public - */ - function PEAR_Command_Build(&$ui, &$config) - { - parent::PEAR_Command_Common($ui, $config); - } - - function doBuild($command, $options, $params) - { - require_once 'PEAR/Builder.php'; - if (sizeof($params) < 1) { - $params[0] = 'package.xml'; - } - - $builder = &new PEAR_Builder($this->ui); - $this->debug = $this->config->get('verbose'); - $err = $builder->build($params[0], array(&$this, 'buildCallback')); - if (PEAR::isError($err)) { - return $err; - } - - return true; - } - - function buildCallback($what, $data) - { - if (($what == 'cmdoutput' && $this->debug > 1) || - ($what == 'output' && $this->debug > 0)) { - $this->ui->outputData(rtrim($data), 'build'); - } - } -} \ No newline at end of file diff --git a/pear/PEAR/Command/Build.xml b/pear/PEAR/Command/Build.xml deleted file mode 100644 index ec4e6f5..0000000 --- a/pear/PEAR/Command/Build.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - Build an Extension From C Source - doBuild - b - - [package.xml] -Builds one or more extensions contained in a package. - - \ No newline at end of file diff --git a/pear/PEAR/Command/Channels.php b/pear/PEAR/Command/Channels.php deleted file mode 100644 index 5ab8f42..0000000 --- a/pear/PEAR/Command/Channels.php +++ /dev/null @@ -1,883 +0,0 @@ - - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a1 - */ - -/** - * base class - */ -require_once 'PEAR/Command/Common.php'; - -define('PEAR_COMMAND_CHANNELS_CHANNEL_EXISTS', -500); - -/** - * PEAR commands for managing channels. - * - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a1 - */ -class PEAR_Command_Channels extends PEAR_Command_Common -{ - var $commands = array( - 'list-channels' => array( - 'summary' => 'List Available Channels', - 'function' => 'doList', - 'shortcut' => 'lc', - 'options' => array(), - 'doc' => ' -List all available channels for installation. -', - ), - 'update-channels' => array( - 'summary' => 'Update the Channel List', - 'function' => 'doUpdateAll', - 'shortcut' => 'uc', - 'options' => array(), - 'doc' => ' -List all installed packages in all channels. -' - ), - 'channel-delete' => array( - 'summary' => 'Remove a Channel From the List', - 'function' => 'doDelete', - 'shortcut' => 'cde', - 'options' => array(), - 'doc' => ' -Delete a channel from the registry. You may not -remove any channel that has installed packages. -' - ), - 'channel-add' => array( - 'summary' => 'Add a Channel', - 'function' => 'doAdd', - 'shortcut' => 'ca', - 'options' => array(), - 'doc' => ' -Add a private channel to the channel list. Note that all -public channels should be synced using "update-channels". -Parameter may be either a local file or remote URL to a -channel.xml. -' - ), - 'channel-update' => array( - 'summary' => 'Update an Existing Channel', - 'function' => 'doUpdate', - 'shortcut' => 'cu', - 'options' => array( - 'force' => array( - 'shortopt' => 'f', - 'doc' => 'will force download of new channel.xml if an existing channel name is used', - ), - 'channel' => array( - 'shortopt' => 'c', - 'arg' => 'CHANNEL', - 'doc' => 'will force download of new channel.xml if an existing channel name is used', - ), -), - 'doc' => '[|] -Update a channel in the channel list directly. Note that all -public channels can be synced using "update-channels". -Parameter may be a local or remote channel.xml, or the name of -an existing channel. -' - ), - 'channel-info' => array( - 'summary' => 'Retrieve Information on a Channel', - 'function' => 'doInfo', - 'shortcut' => 'ci', - 'options' => array(), - 'doc' => ' -List the files in an installed package. -' - ), - 'channel-alias' => array( - 'summary' => 'Specify an alias to a channel name', - 'function' => 'doAlias', - 'shortcut' => 'cha', - 'options' => array(), - 'doc' => ' -Specify a specific alias to use for a channel name. -The alias may not be an existing channel name or -alias. -' - ), - 'channel-discover' => array( - 'summary' => 'Initialize a Channel from its server', - 'function' => 'doDiscover', - 'shortcut' => 'di', - 'options' => array(), - 'doc' => '[|] -Initialize a channel from its server and create a local channel.xml. -If is in the format ":@" then - and will be set as the login username/password for -. Use caution when passing the username/password in this way, as -it may allow other users on your computer to briefly view your username/ -password via the system\'s process list. -' - ), - 'channel-login' => array( - 'summary' => 'Connects and authenticates to remote channel server', - 'shortcut' => 'cli', - 'function' => 'doLogin', - 'options' => array(), - 'doc' => ' -Log in to a remote channel server. If is not supplied, -the default channel is used. To use remote functions in the installer -that require any kind of privileges, you need to log in first. The -username and password you enter here will be stored in your per-user -PEAR configuration (~/.pearrc on Unix-like systems). After logging -in, your username and password will be sent along in subsequent -operations on the remote server.', - ), - 'channel-logout' => array( - 'summary' => 'Logs out from the remote channel server', - 'shortcut' => 'clo', - 'function' => 'doLogout', - 'options' => array(), - 'doc' => ' -Logs out from a remote channel server. If is not supplied, -the default channel is used. This command does not actually connect to the -remote server, it only deletes the stored username and password from your user -configuration.', - ), - ); - - /** - * PEAR_Command_Registry constructor. - * - * @access public - */ - function PEAR_Command_Channels(&$ui, &$config) - { - parent::PEAR_Command_Common($ui, $config); - } - - function _sortChannels($a, $b) - { - return strnatcasecmp($a->getName(), $b->getName()); - } - - function doList($command, $options, $params) - { - $reg = &$this->config->getRegistry(); - $registered = $reg->getChannels(); - usort($registered, array(&$this, '_sortchannels')); - $i = $j = 0; - $data = array( - 'caption' => 'Registered Channels:', - 'border' => true, - 'headline' => array('Channel', 'Alias', 'Summary') - ); - foreach ($registered as $channel) { - $data['data'][] = array($channel->getName(), - $channel->getAlias(), - $channel->getSummary()); - } - - if (count($registered) === 0) { - $data = '(no registered channels)'; - } - $this->ui->outputData($data, $command); - return true; - } - - function doUpdateAll($command, $options, $params) - { - $reg = &$this->config->getRegistry(); - $channels = $reg->getChannels(); - - $success = true; - foreach ($channels as $channel) { - if ($channel->getName() != '__uri') { - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $err = $this->doUpdate('channel-update', - $options, - array($channel->getName())); - if (PEAR::isError($err)) { - $this->ui->outputData($err->getMessage(), $command); - $success = false; - } else { - $success &= $err; - } - } - } - return $success; - } - - function doInfo($command, $options, $params) - { - if (count($params) !== 1) { - return $this->raiseError("No channel specified"); - } - - $reg = &$this->config->getRegistry(); - $channel = strtolower($params[0]); - if ($reg->channelExists($channel)) { - $chan = $reg->getChannel($channel); - if (PEAR::isError($chan)) { - return $this->raiseError($chan); - } - } else { - if (strpos($channel, '://')) { - $downloader = &$this->getDownloader(); - $tmpdir = $this->config->get('temp_dir'); - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $loc = $downloader->downloadHttp($channel, $this->ui, $tmpdir); - PEAR::staticPopErrorHandling(); - if (PEAR::isError($loc)) { - return $this->raiseError('Cannot open "' . $channel . - '" (' . $loc->getMessage() . ')'); - } else { - $contents = implode('', file($loc)); - } - } else { - if (!file_exists($params[0])) { - return $this->raiseError('Unknown channel "' . $channel . '"'); - } - - $fp = fopen($params[0], 'r'); - if (!$fp) { - return $this->raiseError('Cannot open "' . $params[0] . '"'); - } - - $contents = ''; - while (!feof($fp)) { - $contents .= fread($fp, 1024); - } - fclose($fp); - } - - if (!class_exists('PEAR_ChannelFile')) { - require_once 'PEAR/ChannelFile.php'; - } - - $chan = new PEAR_ChannelFile; - $chan->fromXmlString($contents); - $chan->validate(); - if ($errs = $chan->getErrors(true)) { - foreach ($errs as $err) { - $this->ui->outputData($err['level'] . ': ' . $err['message']); - } - return $this->raiseError('Channel file "' . $params[0] . '" is not valid'); - } - } - - if (!$chan) { - return $this->raiseError('Serious error: Channel "' . $params[0] . - '" has a corrupted registry entry'); - } - - $channel = $chan->getName(); - $caption = 'Channel ' . $channel . ' Information:'; - $data1 = array( - 'caption' => $caption, - 'border' => true); - $data1['data']['server'] = array('Name and Server', $chan->getName()); - if ($chan->getAlias() != $chan->getName()) { - $data1['data']['alias'] = array('Alias', $chan->getAlias()); - } - - $data1['data']['summary'] = array('Summary', $chan->getSummary()); - $validate = $chan->getValidationPackage(); - $data1['data']['vpackage'] = array('Validation Package Name', $validate['_content']); - $data1['data']['vpackageversion'] = - array('Validation Package Version', $validate['attribs']['version']); - $d = array(); - $d['main'] = $data1; - - $data['data'] = array(); - $data['caption'] = 'Server Capabilities'; - $data['headline'] = array('Type', 'Version/REST type', 'Function Name/REST base'); - if ($chan->supportsREST()) { - if ($chan->supportsREST()) { - $funcs = $chan->getFunctions('rest'); - if (!isset($funcs[0])) { - $funcs = array($funcs); - } - foreach ($funcs as $protocol) { - $data['data'][] = array('rest', $protocol['attribs']['type'], - $protocol['_content']); - } - } - } else { - $data['data'][] = array('No supported protocols'); - } - - $d['protocols'] = $data; - $data['data'] = array(); - $mirrors = $chan->getMirrors(); - if ($mirrors) { - $data['caption'] = 'Channel ' . $channel . ' Mirrors:'; - unset($data['headline']); - foreach ($mirrors as $mirror) { - $data['data'][] = array($mirror['attribs']['host']); - $d['mirrors'] = $data; - } - - foreach ($mirrors as $i => $mirror) { - $data['data'] = array(); - $data['caption'] = 'Mirror ' . $mirror['attribs']['host'] . ' Capabilities'; - $data['headline'] = array('Type', 'Version/REST type', 'Function Name/REST base'); - if ($chan->supportsREST($mirror['attribs']['host'])) { - if ($chan->supportsREST($mirror['attribs']['host'])) { - $funcs = $chan->getFunctions('rest', $mirror['attribs']['host']); - if (!isset($funcs[0])) { - $funcs = array($funcs); - } - - foreach ($funcs as $protocol) { - $data['data'][] = array('rest', $protocol['attribs']['type'], - $protocol['_content']); - } - } - } else { - $data['data'][] = array('No supported protocols'); - } - $d['mirrorprotocols' . $i] = $data; - } - } - $this->ui->outputData($d, 'channel-info'); - } - - // }}} - - function doDelete($command, $options, $params) - { - if (count($params) !== 1) { - return $this->raiseError('channel-delete: no channel specified'); - } - - $reg = &$this->config->getRegistry(); - if (!$reg->channelExists($params[0])) { - return $this->raiseError('channel-delete: channel "' . $params[0] . '" does not exist'); - } - - $channel = $reg->channelName($params[0]); - if ($channel == 'pear.php.net') { - return $this->raiseError('Cannot delete the pear.php.net channel'); - } - - if ($channel == 'pecl.php.net') { - return $this->raiseError('Cannot delete the pecl.php.net channel'); - } - - if ($channel == 'doc.php.net') { - return $this->raiseError('Cannot delete the doc.php.net channel'); - } - - if ($channel == '__uri') { - return $this->raiseError('Cannot delete the __uri pseudo-channel'); - } - - if (PEAR::isError($err = $reg->listPackages($channel))) { - return $err; - } - - if (count($err)) { - return $this->raiseError('Channel "' . $channel . - '" has installed packages, cannot delete'); - } - - if (!$reg->deleteChannel($channel)) { - return $this->raiseError('Channel "' . $channel . '" deletion failed'); - } else { - $this->config->deleteChannel($channel); - $this->ui->outputData('Channel "' . $channel . '" deleted', $command); - } - } - - function doAdd($command, $options, $params) - { - if (count($params) !== 1) { - return $this->raiseError('channel-add: no channel file specified'); - } - - if (strpos($params[0], '://')) { - $downloader = &$this->getDownloader(); - $tmpdir = $this->config->get('temp_dir'); - if (!file_exists($tmpdir)) { - require_once 'System.php'; - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $err = System::mkdir(array('-p', $tmpdir)); - PEAR::staticPopErrorHandling(); - if (PEAR::isError($err)) { - return $this->raiseError('channel-add: temp_dir does not exist: "' . - $tmpdir . - '" - You can change this location with "pear config-set temp_dir"'); - } - } - - if (!is_writable($tmpdir)) { - return $this->raiseError('channel-add: temp_dir is not writable: "' . - $tmpdir . - '" - You can change this location with "pear config-set temp_dir"'); - } - - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $loc = $downloader->downloadHttp($params[0], $this->ui, $tmpdir, null, false); - PEAR::staticPopErrorHandling(); - if (PEAR::isError($loc)) { - return $this->raiseError('channel-add: Cannot open "' . $params[0] . - '" (' . $loc->getMessage() . ')'); - } - - list($loc, $lastmodified) = $loc; - $contents = implode('', file($loc)); - } else { - $lastmodified = $fp = false; - if (file_exists($params[0])) { - $fp = fopen($params[0], 'r'); - } - - if (!$fp) { - return $this->raiseError('channel-add: cannot open "' . $params[0] . '"'); - } - - $contents = ''; - while (!feof($fp)) { - $contents .= fread($fp, 1024); - } - fclose($fp); - } - - if (!class_exists('PEAR_ChannelFile')) { - require_once 'PEAR/ChannelFile.php'; - } - - $channel = new PEAR_ChannelFile; - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $result = $channel->fromXmlString($contents); - PEAR::staticPopErrorHandling(); - if (!$result) { - $exit = false; - if (count($errors = $channel->getErrors(true))) { - foreach ($errors as $error) { - $this->ui->outputData(ucfirst($error['level'] . ': ' . $error['message'])); - if (!$exit) { - $exit = $error['level'] == 'error' ? true : false; - } - } - if ($exit) { - return $this->raiseError('channel-add: invalid channel.xml file'); - } - } - } - - $reg = &$this->config->getRegistry(); - if ($reg->channelExists($channel->getName())) { - return $this->raiseError('channel-add: Channel "' . $channel->getName() . - '" exists, use channel-update to update entry', PEAR_COMMAND_CHANNELS_CHANNEL_EXISTS); - } - - $ret = $reg->addChannel($channel, $lastmodified); - if (PEAR::isError($ret)) { - return $ret; - } - - if (!$ret) { - return $this->raiseError('channel-add: adding Channel "' . $channel->getName() . - '" to registry failed'); - } - - $this->config->setChannels($reg->listChannels()); - $this->config->writeConfigFile(); - $this->ui->outputData('Adding Channel "' . $channel->getName() . '" succeeded', $command); - } - - function doUpdate($command, $options, $params) - { - if (count($params) !== 1) { - return $this->raiseError("No channel file specified"); - } - - $tmpdir = $this->config->get('temp_dir'); - if (!file_exists($tmpdir)) { - require_once 'System.php'; - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $err = System::mkdir(array('-p', $tmpdir)); - PEAR::staticPopErrorHandling(); - if (PEAR::isError($err)) { - return $this->raiseError('channel-add: temp_dir does not exist: "' . - $tmpdir . - '" - You can change this location with "pear config-set temp_dir"'); - } - } - - if (!is_writable($tmpdir)) { - return $this->raiseError('channel-add: temp_dir is not writable: "' . - $tmpdir . - '" - You can change this location with "pear config-set temp_dir"'); - } - - $reg = &$this->config->getRegistry(); - $lastmodified = false; - if ((!file_exists($params[0]) || is_dir($params[0])) - && $reg->channelExists(strtolower($params[0]))) { - $c = $reg->getChannel(strtolower($params[0])); - if (PEAR::isError($c)) { - return $this->raiseError($c); - } - - $this->ui->outputData("Updating channel \"$params[0]\"", $command); - $dl = &$this->getDownloader(array()); - // if force is specified, use a timestamp of "1" to force retrieval - $lastmodified = isset($options['force']) ? false : $c->lastModified(); - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $contents = $dl->downloadHttp('http://' . $c->getName() . '/channel.xml', - $this->ui, $tmpdir, null, $lastmodified); - PEAR::staticPopErrorHandling(); - if (PEAR::isError($contents)) { - // Attempt to fall back to https - $this->ui->outputData("Channel \"$params[0]\" is not responding over http://, failed with message: " . $contents->getMessage()); - $this->ui->outputData("Trying channel \"$params[0]\" over https:// instead"); - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $contents = $dl->downloadHttp('https://' . $c->getName() . '/channel.xml', - $this->ui, $tmpdir, null, $lastmodified); - PEAR::staticPopErrorHandling(); - if (PEAR::isError($contents)) { - return $this->raiseError('Cannot retrieve channel.xml for channel "' . - $c->getName() . '" (' . $contents->getMessage() . ')'); - } - } - - list($contents, $lastmodified) = $contents; - if (!$contents) { - $this->ui->outputData("Channel \"$params[0]\" is up to date"); - return; - } - - $contents = implode('', file($contents)); - if (!class_exists('PEAR_ChannelFile')) { - require_once 'PEAR/ChannelFile.php'; - } - - $channel = new PEAR_ChannelFile; - $channel->fromXmlString($contents); - if (!$channel->getErrors()) { - // security check: is the downloaded file for the channel we got it from? - if (strtolower($channel->getName()) != strtolower($c->getName())) { - if (!isset($options['force'])) { - return $this->raiseError('ERROR: downloaded channel definition file' . - ' for channel "' . $channel->getName() . '" from channel "' . - strtolower($c->getName()) . '"'); - } - - $this->ui->log(0, 'WARNING: downloaded channel definition file' . - ' for channel "' . $channel->getName() . '" from channel "' . - strtolower($c->getName()) . '"'); - } - } - } else { - if (strpos($params[0], '://')) { - $dl = &$this->getDownloader(); - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $loc = $dl->downloadHttp($params[0], - $this->ui, $tmpdir, null, $lastmodified); - PEAR::staticPopErrorHandling(); - if (PEAR::isError($loc)) { - return $this->raiseError("Cannot open " . $params[0] . - ' (' . $loc->getMessage() . ')'); - } - - list($loc, $lastmodified) = $loc; - $contents = implode('', file($loc)); - } else { - $fp = false; - if (file_exists($params[0])) { - $fp = fopen($params[0], 'r'); - } - - if (!$fp) { - return $this->raiseError("Cannot open " . $params[0]); - } - - $contents = ''; - while (!feof($fp)) { - $contents .= fread($fp, 1024); - } - fclose($fp); - } - - if (!class_exists('PEAR_ChannelFile')) { - require_once 'PEAR/ChannelFile.php'; - } - - $channel = new PEAR_ChannelFile; - $channel->fromXmlString($contents); - } - - $exit = false; - if (count($errors = $channel->getErrors(true))) { - foreach ($errors as $error) { - $this->ui->outputData(ucfirst($error['level'] . ': ' . $error['message'])); - if (!$exit) { - $exit = $error['level'] == 'error' ? true : false; - } - } - if ($exit) { - return $this->raiseError('Invalid channel.xml file'); - } - } - - if (!$reg->channelExists($channel->getName())) { - return $this->raiseError('Error: Channel "' . $channel->getName() . - '" does not exist, use channel-add to add an entry'); - } - - $ret = $reg->updateChannel($channel, $lastmodified); - if (PEAR::isError($ret)) { - return $ret; - } - - if (!$ret) { - return $this->raiseError('Updating Channel "' . $channel->getName() . - '" in registry failed'); - } - - $this->config->setChannels($reg->listChannels()); - $this->config->writeConfigFile(); - $this->ui->outputData('Update of Channel "' . $channel->getName() . '" succeeded'); - } - - function &getDownloader() - { - if (!class_exists('PEAR_Downloader')) { - require_once 'PEAR/Downloader.php'; - } - $a = new PEAR_Downloader($this->ui, array(), $this->config); - return $a; - } - - function doAlias($command, $options, $params) - { - if (count($params) === 1) { - return $this->raiseError('No channel alias specified'); - } - - if (count($params) !== 2 || (!empty($params[1]) && $params[1]{0} == '-')) { - return $this->raiseError( - 'Invalid format, correct is: channel-alias channel alias'); - } - - $reg = &$this->config->getRegistry(); - if (!$reg->channelExists($params[0], true)) { - $extra = ''; - if ($reg->isAlias($params[0])) { - $extra = ' (use "channel-alias ' . $reg->channelName($params[0]) . ' ' . - strtolower($params[1]) . '")'; - } - - return $this->raiseError('"' . $params[0] . '" is not a valid channel' . $extra); - } - - if ($reg->isAlias($params[1])) { - return $this->raiseError('Channel "' . $reg->channelName($params[1]) . '" is ' . - 'already aliased to "' . strtolower($params[1]) . '", cannot re-alias'); - } - - $chan = &$reg->getChannel($params[0]); - if (PEAR::isError($chan)) { - return $this->raiseError('Corrupt registry? Error retrieving channel "' . $params[0] . - '" information (' . $chan->getMessage() . ')'); - } - - // make it a local alias - if (!$chan->setAlias(strtolower($params[1]), true)) { - return $this->raiseError('Alias "' . strtolower($params[1]) . - '" is not a valid channel alias'); - } - - $reg->updateChannel($chan); - $this->ui->outputData('Channel "' . $chan->getName() . '" aliased successfully to "' . - strtolower($params[1]) . '"'); - } - - /** - * The channel-discover command - * - * @param string $command command name - * @param array $options option_name => value - * @param array $params list of additional parameters. - * $params[0] should contain a string with either: - * - or - * - :@ - * @return null|PEAR_Error - */ - function doDiscover($command, $options, $params) - { - if (count($params) !== 1) { - return $this->raiseError("No channel server specified"); - } - - // Look for the possible input format ":@" - if (preg_match('/^(.+):(.+)@(.+)\\z/', $params[0], $matches)) { - $username = $matches[1]; - $password = $matches[2]; - $channel = $matches[3]; - } else { - $channel = $params[0]; - } - - $reg = &$this->config->getRegistry(); - if ($reg->channelExists($channel)) { - if (!$reg->isAlias($channel)) { - return $this->raiseError("Channel \"$channel\" is already initialized", PEAR_COMMAND_CHANNELS_CHANNEL_EXISTS); - } - - return $this->raiseError("A channel alias named \"$channel\" " . - 'already exists, aliasing channel "' . $reg->channelName($channel) - . '"'); - } - - $this->pushErrorHandling(PEAR_ERROR_RETURN); - $err = $this->doAdd($command, $options, array('http://' . $channel . '/channel.xml')); - $this->popErrorHandling(); - if (PEAR::isError($err)) { - if ($err->getCode() === PEAR_COMMAND_CHANNELS_CHANNEL_EXISTS) { - return $this->raiseError("Discovery of channel \"$channel\" failed (" . - $err->getMessage() . ')'); - } - // Attempt fetch via https - $this->ui->outputData("Discovering channel $channel over http:// failed with message: " . $err->getMessage()); - $this->ui->outputData("Trying to discover channel $channel over https:// instead"); - $this->pushErrorHandling(PEAR_ERROR_RETURN); - $err = $this->doAdd($command, $options, array('https://' . $channel . '/channel.xml')); - $this->popErrorHandling(); - if (PEAR::isError($err)) { - return $this->raiseError("Discovery of channel \"$channel\" failed (" . - $err->getMessage() . ')'); - } - } - - // Store username/password if they were given - // Arguably we should do a logintest on the channel here, but since - // that's awkward on a REST-based channel (even "pear login" doesn't - // do it for those), and XML-RPC is deprecated, it's fairly pointless. - if (isset($username)) { - $this->config->set('username', $username, 'user', $channel); - $this->config->set('password', $password, 'user', $channel); - $this->config->store(); - $this->ui->outputData("Stored login for channel \"$channel\" using username \"$username\"", $command); - } - - $this->ui->outputData("Discovery of channel \"$channel\" succeeded", $command); - } - - /** - * Execute the 'login' command. - * - * @param string $command command name - * @param array $options option_name => value - * @param array $params list of additional parameters - * - * @return bool TRUE on success or - * a PEAR error on failure - * - * @access public - */ - function doLogin($command, $options, $params) - { - $reg = &$this->config->getRegistry(); - - // If a parameter is supplied, use that as the channel to log in to - $channel = isset($params[0]) ? $params[0] : $this->config->get('default_channel'); - - $chan = $reg->getChannel($channel); - if (PEAR::isError($chan)) { - return $this->raiseError($chan); - } - - $server = $this->config->get('preferred_mirror', null, $channel); - $username = $this->config->get('username', null, $channel); - if (empty($username)) { - $username = isset($_ENV['USER']) ? $_ENV['USER'] : null; - } - $this->ui->outputData("Logging in to $server.", $command); - - list($username, $password) = $this->ui->userDialog( - $command, - array('Username', 'Password'), - array('text', 'password'), - array($username, '') - ); - $username = trim($username); - $password = trim($password); - - $ourfile = $this->config->getConfFile('user'); - if (!$ourfile) { - $ourfile = $this->config->getConfFile('system'); - } - - $this->config->set('username', $username, 'user', $channel); - $this->config->set('password', $password, 'user', $channel); - - if ($chan->supportsREST()) { - $ok = true; - } - - if ($ok !== true) { - return $this->raiseError('Login failed!'); - } - - $this->ui->outputData("Logged in.", $command); - // avoid changing any temporary settings changed with -d - $ourconfig = new PEAR_Config($ourfile, $ourfile); - $ourconfig->set('username', $username, 'user', $channel); - $ourconfig->set('password', $password, 'user', $channel); - $ourconfig->store(); - - return true; - } - - /** - * Execute the 'logout' command. - * - * @param string $command command name - * @param array $options option_name => value - * @param array $params list of additional parameters - * - * @return bool TRUE on success or - * a PEAR error on failure - * - * @access public - */ - function doLogout($command, $options, $params) - { - $reg = &$this->config->getRegistry(); - - // If a parameter is supplied, use that as the channel to log in to - $channel = isset($params[0]) ? $params[0] : $this->config->get('default_channel'); - - $chan = $reg->getChannel($channel); - if (PEAR::isError($chan)) { - return $this->raiseError($chan); - } - - $server = $this->config->get('preferred_mirror', null, $channel); - $this->ui->outputData("Logging out from $server.", $command); - $this->config->remove('username', 'user', $channel); - $this->config->remove('password', 'user', $channel); - $this->config->store(); - return true; - } -} \ No newline at end of file diff --git a/pear/PEAR/Command/Channels.xml b/pear/PEAR/Command/Channels.xml deleted file mode 100644 index 47b7206..0000000 --- a/pear/PEAR/Command/Channels.xml +++ /dev/null @@ -1,123 +0,0 @@ - - - List Available Channels - doList - lc - - -List all available channels for installation. - - - - Update the Channel List - doUpdateAll - uc - - -List all installed packages in all channels. - - - - Remove a Channel From the List - doDelete - cde - - <channel name> -Delete a channel from the registry. You may not -remove any channel that has installed packages. - - - - Add a Channel - doAdd - ca - - <channel.xml> -Add a private channel to the channel list. Note that all -public channels should be synced using "update-channels". -Parameter may be either a local file or remote URL to a -channel.xml. - - - - Update an Existing Channel - doUpdate - cu - - - f - will force download of new channel.xml if an existing channel name is used - - - c - will force download of new channel.xml if an existing channel name is used - CHANNEL - - - [<channel.xml>|<channel name>] -Update a channel in the channel list directly. Note that all -public channels can be synced using "update-channels". -Parameter may be a local or remote channel.xml, or the name of -an existing channel. - - - - Retrieve Information on a Channel - doInfo - ci - - <package> -List the files in an installed package. - - - - Specify an alias to a channel name - doAlias - cha - - <channel> <alias> -Specify a specific alias to use for a channel name. -The alias may not be an existing channel name or -alias. - - - - Initialize a Channel from its server - doDiscover - di - - [<channel.xml>|<channel name>] -Initialize a channel from its server and create a local channel.xml. -If <channel name> is in the format "<username>:<password>@<channel>" then -<username> and <password> will be set as the login username/password for -<channel>. Use caution when passing the username/password in this way, as -it may allow other users on your computer to briefly view your username/ -password via the system's process list. - - - - Connects and authenticates to remote channel server - doLogin - cli - - <channel name> -Log in to a remote channel server. If <channel name> is not supplied, -the default channel is used. To use remote functions in the installer -that require any kind of privileges, you need to log in first. The -username and password you enter here will be stored in your per-user -PEAR configuration (~/.pearrc on Unix-like systems). After logging -in, your username and password will be sent along in subsequent -operations on the remote server. - - - Logs out from the remote channel server - doLogout - clo - - <channel name> -Logs out from a remote channel server. If <channel name> is not supplied, -the default channel is used. This command does not actually connect to the -remote server, it only deletes the stored username and password from your user -configuration. - - \ No newline at end of file diff --git a/pear/PEAR/Command/Common.php b/pear/PEAR/Command/Common.php deleted file mode 100644 index 69b5cf2..0000000 --- a/pear/PEAR/Command/Common.php +++ /dev/null @@ -1,273 +0,0 @@ - - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 0.1 - */ - -/** - * base class - */ -require_once 'PEAR.php'; - -/** - * PEAR commands base class - * - * @category pear - * @package PEAR - * @author Stig Bakken - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 0.1 - */ -class PEAR_Command_Common extends PEAR -{ - /** - * PEAR_Config object used to pass user system and configuration - * on when executing commands - * - * @var PEAR_Config - */ - var $config; - /** - * @var PEAR_Registry - * @access protected - */ - var $_registry; - - /** - * User Interface object, for all interaction with the user. - * @var object - */ - var $ui; - - var $_deps_rel_trans = array( - 'lt' => '<', - 'le' => '<=', - 'eq' => '=', - 'ne' => '!=', - 'gt' => '>', - 'ge' => '>=', - 'has' => '==' - ); - - var $_deps_type_trans = array( - 'pkg' => 'package', - 'ext' => 'extension', - 'php' => 'PHP', - 'prog' => 'external program', - 'ldlib' => 'external library for linking', - 'rtlib' => 'external runtime library', - 'os' => 'operating system', - 'websrv' => 'web server', - 'sapi' => 'SAPI backend' - ); - - /** - * PEAR_Command_Common constructor. - * - * @access public - */ - function PEAR_Command_Common(&$ui, &$config) - { - parent::PEAR(); - $this->config = &$config; - $this->ui = &$ui; - } - - /** - * Return a list of all the commands defined by this class. - * @return array list of commands - * @access public - */ - function getCommands() - { - $ret = array(); - foreach (array_keys($this->commands) as $command) { - $ret[$command] = $this->commands[$command]['summary']; - } - - return $ret; - } - - /** - * Return a list of all the command shortcuts defined by this class. - * @return array shortcut => command - * @access public - */ - function getShortcuts() - { - $ret = array(); - foreach (array_keys($this->commands) as $command) { - if (isset($this->commands[$command]['shortcut'])) { - $ret[$this->commands[$command]['shortcut']] = $command; - } - } - - return $ret; - } - - function getOptions($command) - { - $shortcuts = $this->getShortcuts(); - if (isset($shortcuts[$command])) { - $command = $shortcuts[$command]; - } - - if (isset($this->commands[$command]) && - isset($this->commands[$command]['options'])) { - return $this->commands[$command]['options']; - } - - return null; - } - - function getGetoptArgs($command, &$short_args, &$long_args) - { - $short_args = ''; - $long_args = array(); - if (empty($this->commands[$command]) || empty($this->commands[$command]['options'])) { - return; - } - - reset($this->commands[$command]['options']); - while (list($option, $info) = each($this->commands[$command]['options'])) { - $larg = $sarg = ''; - if (isset($info['arg'])) { - if ($info['arg']{0} == '(') { - $larg = '=='; - $sarg = '::'; - $arg = substr($info['arg'], 1, -1); - } else { - $larg = '='; - $sarg = ':'; - $arg = $info['arg']; - } - } - - if (isset($info['shortopt'])) { - $short_args .= $info['shortopt'] . $sarg; - } - - $long_args[] = $option . $larg; - } - } - - /** - * Returns the help message for the given command - * - * @param string $command The command - * @return mixed A fail string if the command does not have help or - * a two elements array containing [0]=>help string, - * [1]=> help string for the accepted cmd args - */ - function getHelp($command) - { - $config = &PEAR_Config::singleton(); - if (!isset($this->commands[$command])) { - return "No such command \"$command\""; - } - - $help = null; - if (isset($this->commands[$command]['doc'])) { - $help = $this->commands[$command]['doc']; - } - - if (empty($help)) { - // XXX (cox) Fallback to summary if there is no doc (show both?) - if (!isset($this->commands[$command]['summary'])) { - return "No help for command \"$command\""; - } - $help = $this->commands[$command]['summary']; - } - - if (preg_match_all('/{config\s+([^\}]+)}/e', $help, $matches)) { - foreach($matches[0] as $k => $v) { - $help = preg_replace("/$v/", $config->get($matches[1][$k]), $help); - } - } - - return array($help, $this->getHelpArgs($command)); - } - - /** - * Returns the help for the accepted arguments of a command - * - * @param string $command - * @return string The help string - */ - function getHelpArgs($command) - { - if (isset($this->commands[$command]['options']) && - count($this->commands[$command]['options'])) - { - $help = "Options:\n"; - foreach ($this->commands[$command]['options'] as $k => $v) { - if (isset($v['arg'])) { - if ($v['arg'][0] == '(') { - $arg = substr($v['arg'], 1, -1); - $sapp = " [$arg]"; - $lapp = "[=$arg]"; - } else { - $sapp = " $v[arg]"; - $lapp = "=$v[arg]"; - } - } else { - $sapp = $lapp = ""; - } - - if (isset($v['shortopt'])) { - $s = $v['shortopt']; - $help .= " -$s$sapp, --$k$lapp\n"; - } else { - $help .= " --$k$lapp\n"; - } - - $p = " "; - $doc = rtrim(str_replace("\n", "\n$p", $v['doc'])); - $help .= " $doc\n"; - } - - return $help; - } - - return null; - } - - function run($command, $options, $params) - { - if (empty($this->commands[$command]['function'])) { - // look for shortcuts - foreach (array_keys($this->commands) as $cmd) { - if (isset($this->commands[$cmd]['shortcut']) && $this->commands[$cmd]['shortcut'] == $command) { - if (empty($this->commands[$cmd]['function'])) { - return $this->raiseError("unknown command `$command'"); - } else { - $func = $this->commands[$cmd]['function']; - } - $command = $cmd; - - //$command = $this->commands[$cmd]['function']; - break; - } - } - } else { - $func = $this->commands[$command]['function']; - } - - return $this->$func($command, $options, $params); - } -} \ No newline at end of file diff --git a/pear/PEAR/Command/Config.php b/pear/PEAR/Command/Config.php deleted file mode 100644 index 8561ee5..0000000 --- a/pear/PEAR/Command/Config.php +++ /dev/null @@ -1,414 +0,0 @@ - - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 0.1 - */ - -/** - * base class - */ -require_once 'PEAR/Command/Common.php'; - -/** - * PEAR commands for managing configuration data. - * - * @category pear - * @package PEAR - * @author Stig Bakken - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 0.1 - */ -class PEAR_Command_Config extends PEAR_Command_Common -{ - var $commands = array( - 'config-show' => array( - 'summary' => 'Show All Settings', - 'function' => 'doConfigShow', - 'shortcut' => 'csh', - 'options' => array( - 'channel' => array( - 'shortopt' => 'c', - 'doc' => 'show configuration variables for another channel', - 'arg' => 'CHAN', - ), -), - 'doc' => '[layer] -Displays all configuration values. An optional argument -may be used to tell which configuration layer to display. Valid -configuration layers are "user", "system" and "default". To display -configurations for different channels, set the default_channel -configuration variable and run config-show again. -', - ), - 'config-get' => array( - 'summary' => 'Show One Setting', - 'function' => 'doConfigGet', - 'shortcut' => 'cg', - 'options' => array( - 'channel' => array( - 'shortopt' => 'c', - 'doc' => 'show configuration variables for another channel', - 'arg' => 'CHAN', - ), -), - 'doc' => ' [layer] -Displays the value of one configuration parameter. The -first argument is the name of the parameter, an optional second argument -may be used to tell which configuration layer to look in. Valid configuration -layers are "user", "system" and "default". If no layer is specified, a value -will be picked from the first layer that defines the parameter, in the order -just specified. The configuration value will be retrieved for the channel -specified by the default_channel configuration variable. -', - ), - 'config-set' => array( - 'summary' => 'Change Setting', - 'function' => 'doConfigSet', - 'shortcut' => 'cs', - 'options' => array( - 'channel' => array( - 'shortopt' => 'c', - 'doc' => 'show configuration variables for another channel', - 'arg' => 'CHAN', - ), -), - 'doc' => ' [layer] -Sets the value of one configuration parameter. The first argument is -the name of the parameter, the second argument is the new value. Some -parameters are subject to validation, and the command will fail with -an error message if the new value does not make sense. An optional -third argument may be used to specify in which layer to set the -configuration parameter. The default layer is "user". The -configuration value will be set for the current channel, which -is controlled by the default_channel configuration variable. -', - ), - 'config-help' => array( - 'summary' => 'Show Information About Setting', - 'function' => 'doConfigHelp', - 'shortcut' => 'ch', - 'options' => array(), - 'doc' => '[parameter] -Displays help for a configuration parameter. Without arguments it -displays help for all configuration parameters. -', - ), - 'config-create' => array( - 'summary' => 'Create a Default configuration file', - 'function' => 'doConfigCreate', - 'shortcut' => 'coc', - 'options' => array( - 'windows' => array( - 'shortopt' => 'w', - 'doc' => 'create a config file for a windows install', - ), - ), - 'doc' => ' -Create a default configuration file with all directory configuration -variables set to subdirectories of , and save it as . -This is useful especially for creating a configuration file for a remote -PEAR installation (using the --remoteconfig option of install, upgrade, -and uninstall). -', - ), - ); - - /** - * PEAR_Command_Config constructor. - * - * @access public - */ - function PEAR_Command_Config(&$ui, &$config) - { - parent::PEAR_Command_Common($ui, $config); - } - - function doConfigShow($command, $options, $params) - { - $layer = null; - if (is_array($params)) { - $layer = isset($params[0]) ? $params[0] : null; - } - - // $params[0] -> the layer - if ($error = $this->_checkLayer($layer)) { - return $this->raiseError("config-show:$error"); - } - - $keys = $this->config->getKeys(); - sort($keys); - $channel = isset($options['channel']) ? $options['channel'] : - $this->config->get('default_channel'); - $reg = &$this->config->getRegistry(); - if (!$reg->channelExists($channel)) { - return $this->raiseError('Channel "' . $channel . '" does not exist'); - } - - $channel = $reg->channelName($channel); - $data = array('caption' => 'Configuration (channel ' . $channel . '):'); - foreach ($keys as $key) { - $type = $this->config->getType($key); - $value = $this->config->get($key, $layer, $channel); - if ($type == 'password' && $value) { - $value = '********'; - } - - if ($value === false) { - $value = 'false'; - } elseif ($value === true) { - $value = 'true'; - } - - $data['data'][$this->config->getGroup($key)][] = array($this->config->getPrompt($key) , $key, $value); - } - - foreach ($this->config->getLayers() as $layer) { - $data['data']['Config Files'][] = array(ucfirst($layer) . ' Configuration File', 'Filename' , $this->config->getConfFile($layer)); - } - - $this->ui->outputData($data, $command); - return true; - } - - function doConfigGet($command, $options, $params) - { - $args_cnt = is_array($params) ? count($params) : 0; - switch ($args_cnt) { - case 1: - $config_key = $params[0]; - $layer = null; - break; - case 2: - $config_key = $params[0]; - $layer = $params[1]; - if ($error = $this->_checkLayer($layer)) { - return $this->raiseError("config-get:$error"); - } - break; - case 0: - default: - return $this->raiseError("config-get expects 1 or 2 parameters"); - } - - $reg = &$this->config->getRegistry(); - $channel = isset($options['channel']) ? $options['channel'] : $this->config->get('default_channel'); - if (!$reg->channelExists($channel)) { - return $this->raiseError('Channel "' . $channel . '" does not exist'); - } - - $channel = $reg->channelName($channel); - $this->ui->outputData($this->config->get($config_key, $layer, $channel), $command); - return true; - } - - function doConfigSet($command, $options, $params) - { - // $param[0] -> a parameter to set - // $param[1] -> the value for the parameter - // $param[2] -> the layer - $failmsg = ''; - if (count($params) < 2 || count($params) > 3) { - $failmsg .= "config-set expects 2 or 3 parameters"; - return PEAR::raiseError($failmsg); - } - - if (isset($params[2]) && ($error = $this->_checkLayer($params[2]))) { - $failmsg .= $error; - return PEAR::raiseError("config-set:$failmsg"); - } - - $channel = isset($options['channel']) ? $options['channel'] : $this->config->get('default_channel'); - $reg = &$this->config->getRegistry(); - if (!$reg->channelExists($channel)) { - return $this->raiseError('Channel "' . $channel . '" does not exist'); - } - - $channel = $reg->channelName($channel); - if ($params[0] == 'default_channel' && !$reg->channelExists($params[1])) { - return $this->raiseError('Channel "' . $params[1] . '" does not exist'); - } - - if ($params[0] == 'preferred_mirror' - && ( - !$reg->mirrorExists($channel, $params[1]) && - (!$reg->channelExists($params[1]) || $channel != $params[1]) - ) - ) { - $msg = 'Channel Mirror "' . $params[1] . '" does not exist'; - $msg .= ' in your registry for channel "' . $channel . '".'; - $msg .= "\n" . 'Attempt to run "pear channel-update ' . $channel .'"'; - $msg .= ' if you believe this mirror should exist as you may'; - $msg .= ' have outdated channel information.'; - return $this->raiseError($msg); - } - - if (count($params) == 2) { - array_push($params, 'user'); - $layer = 'user'; - } else { - $layer = $params[2]; - } - - array_push($params, $channel); - if (!call_user_func_array(array(&$this->config, 'set'), $params)) { - array_pop($params); - $failmsg = "config-set (" . implode(", ", $params) . ") failed, channel $channel"; - } else { - $this->config->store($layer); - } - - if ($failmsg) { - return $this->raiseError($failmsg); - } - - $this->ui->outputData('config-set succeeded', $command); - return true; - } - - function doConfigHelp($command, $options, $params) - { - if (empty($params)) { - $params = $this->config->getKeys(); - } - - $data['caption'] = "Config help" . ((count($params) == 1) ? " for $params[0]" : ''); - $data['headline'] = array('Name', 'Type', 'Description'); - $data['border'] = true; - foreach ($params as $name) { - $type = $this->config->getType($name); - $docs = $this->config->getDocs($name); - if ($type == 'set') { - $docs = rtrim($docs) . "\nValid set: " . - implode(' ', $this->config->getSetValues($name)); - } - - $data['data'][] = array($name, $type, $docs); - } - - $this->ui->outputData($data, $command); - } - - function doConfigCreate($command, $options, $params) - { - if (count($params) != 2) { - return PEAR::raiseError('config-create: must have 2 parameters, root path and ' . - 'filename to save as'); - } - - $root = $params[0]; - // Clean up the DIRECTORY_SEPARATOR mess - $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR; - $root = preg_replace(array('!\\\\+!', '!/+!', "!$ds2+!"), - array('/', '/', '/'), - $root); - if ($root{0} != '/') { - if (!isset($options['windows'])) { - return PEAR::raiseError('Root directory must be an absolute path beginning ' . - 'with "/", was: "' . $root . '"'); - } - - if (!preg_match('/^[A-Za-z]:/', $root)) { - return PEAR::raiseError('Root directory must be an absolute path beginning ' . - 'with "\\" or "C:\\", was: "' . $root . '"'); - } - } - - $windows = isset($options['windows']); - if ($windows) { - $root = str_replace('/', '\\', $root); - } - - if (!file_exists($params[1]) && !@touch($params[1])) { - return PEAR::raiseError('Could not create "' . $params[1] . '"'); - } - - $params[1] = realpath($params[1]); - $config = &new PEAR_Config($params[1], '#no#system#config#', false, false); - if ($root{strlen($root) - 1} == '/') { - $root = substr($root, 0, strlen($root) - 1); - } - - $config->noRegistry(); - $config->set('php_dir', $windows ? "$root\\pear\\php" : "$root/pear/php", 'user'); - $config->set('data_dir', $windows ? "$root\\pear\\data" : "$root/pear/data"); - $config->set('www_dir', $windows ? "$root\\pear\\www" : "$root/pear/www"); - $config->set('cfg_dir', $windows ? "$root\\pear\\cfg" : "$root/pear/cfg"); - $config->set('ext_dir', $windows ? "$root\\pear\\ext" : "$root/pear/ext"); - $config->set('doc_dir', $windows ? "$root\\pear\\docs" : "$root/pear/docs"); - $config->set('test_dir', $windows ? "$root\\pear\\tests" : "$root/pear/tests"); - $config->set('cache_dir', $windows ? "$root\\pear\\cache" : "$root/pear/cache"); - $config->set('download_dir', $windows ? "$root\\pear\\download" : "$root/pear/download"); - $config->set('temp_dir', $windows ? "$root\\pear\\temp" : "$root/pear/temp"); - $config->set('bin_dir', $windows ? "$root\\pear" : "$root/pear"); - $config->writeConfigFile(); - $this->_showConfig($config); - $this->ui->outputData('Successfully created default configuration file "' . $params[1] . '"', - $command); - } - - function _showConfig(&$config) - { - $params = array('user'); - $keys = $config->getKeys(); - sort($keys); - $channel = 'pear.php.net'; - $data = array('caption' => 'Configuration (channel ' . $channel . '):'); - foreach ($keys as $key) { - $type = $config->getType($key); - $value = $config->get($key, 'user', $channel); - if ($type == 'password' && $value) { - $value = '********'; - } - - if ($value === false) { - $value = 'false'; - } elseif ($value === true) { - $value = 'true'; - } - $data['data'][$config->getGroup($key)][] = - array($config->getPrompt($key) , $key, $value); - } - - foreach ($config->getLayers() as $layer) { - $data['data']['Config Files'][] = - array(ucfirst($layer) . ' Configuration File', 'Filename' , - $config->getConfFile($layer)); - } - - $this->ui->outputData($data, 'config-show'); - return true; - } - - /** - * Checks if a layer is defined or not - * - * @param string $layer The layer to search for - * @return mixed False on no error or the error message - */ - function _checkLayer($layer = null) - { - if (!empty($layer) && $layer != 'default') { - $layers = $this->config->getLayers(); - if (!in_array($layer, $layers)) { - return " only the layers: \"" . implode('" or "', $layers) . "\" are supported"; - } - } - - return false; - } -} \ No newline at end of file diff --git a/pear/PEAR/Command/Config.xml b/pear/PEAR/Command/Config.xml deleted file mode 100644 index f64a925..0000000 --- a/pear/PEAR/Command/Config.xml +++ /dev/null @@ -1,92 +0,0 @@ - - - Show All Settings - doConfigShow - csh - - - c - show configuration variables for another channel - CHAN - - - [layer] -Displays all configuration values. An optional argument -may be used to tell which configuration layer to display. Valid -configuration layers are "user", "system" and "default". To display -configurations for different channels, set the default_channel -configuration variable and run config-show again. - - - - Show One Setting - doConfigGet - cg - - - c - show configuration variables for another channel - CHAN - - - <parameter> [layer] -Displays the value of one configuration parameter. The -first argument is the name of the parameter, an optional second argument -may be used to tell which configuration layer to look in. Valid configuration -layers are "user", "system" and "default". If no layer is specified, a value -will be picked from the first layer that defines the parameter, in the order -just specified. The configuration value will be retrieved for the channel -specified by the default_channel configuration variable. - - - - Change Setting - doConfigSet - cs - - - c - show configuration variables for another channel - CHAN - - - <parameter> <value> [layer] -Sets the value of one configuration parameter. The first argument is -the name of the parameter, the second argument is the new value. Some -parameters are subject to validation, and the command will fail with -an error message if the new value does not make sense. An optional -third argument may be used to specify in which layer to set the -configuration parameter. The default layer is "user". The -configuration value will be set for the current channel, which -is controlled by the default_channel configuration variable. - - - - Show Information About Setting - doConfigHelp - ch - - [parameter] -Displays help for a configuration parameter. Without arguments it -displays help for all configuration parameters. - - - - Create a Default configuration file - doConfigCreate - coc - - - w - create a config file for a windows install - - - <root path> <filename> -Create a default configuration file with all directory configuration -variables set to subdirectories of <root path>, and save it as <filename>. -This is useful especially for creating a configuration file for a remote -PEAR installation (using the --remoteconfig option of install, upgrade, -and uninstall). - - - \ No newline at end of file diff --git a/pear/PEAR/Command/Install.php b/pear/PEAR/Command/Install.php deleted file mode 100644 index 79e68db..0000000 --- a/pear/PEAR/Command/Install.php +++ /dev/null @@ -1,1268 +0,0 @@ - - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 0.1 - */ - -/** - * base class - */ -require_once 'PEAR/Command/Common.php'; - -/** - * PEAR commands for installation or deinstallation/upgrading of - * packages. - * - * @category pear - * @package PEAR - * @author Stig Bakken - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 0.1 - */ -class PEAR_Command_Install extends PEAR_Command_Common -{ - // {{{ properties - - var $commands = array( - 'install' => array( - 'summary' => 'Install Package', - 'function' => 'doInstall', - 'shortcut' => 'i', - 'options' => array( - 'force' => array( - 'shortopt' => 'f', - 'doc' => 'will overwrite newer installed packages', - ), - 'loose' => array( - 'shortopt' => 'l', - 'doc' => 'do not check for recommended dependency version', - ), - 'nodeps' => array( - 'shortopt' => 'n', - 'doc' => 'ignore dependencies, install anyway', - ), - 'register-only' => array( - 'shortopt' => 'r', - 'doc' => 'do not install files, only register the package as installed', - ), - 'soft' => array( - 'shortopt' => 's', - 'doc' => 'soft install, fail silently, or upgrade if already installed', - ), - 'nobuild' => array( - 'shortopt' => 'B', - 'doc' => 'don\'t build C extensions', - ), - 'nocompress' => array( - 'shortopt' => 'Z', - 'doc' => 'request uncompressed files when downloading', - ), - 'installroot' => array( - 'shortopt' => 'R', - 'arg' => 'DIR', - 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT), use packagingroot for RPM', - ), - 'packagingroot' => array( - 'shortopt' => 'P', - 'arg' => 'DIR', - 'doc' => 'root directory used when packaging files, like RPM packaging', - ), - 'ignore-errors' => array( - 'doc' => 'force install even if there were errors', - ), - 'alldeps' => array( - 'shortopt' => 'a', - 'doc' => 'install all required and optional dependencies', - ), - 'onlyreqdeps' => array( - 'shortopt' => 'o', - 'doc' => 'install all required dependencies', - ), - 'offline' => array( - 'shortopt' => 'O', - 'doc' => 'do not attempt to download any urls or contact channels', - ), - 'pretend' => array( - 'shortopt' => 'p', - 'doc' => 'Only list the packages that would be downloaded', - ), - ), - 'doc' => '[channel/] ... -Installs one or more PEAR packages. You can specify a package to -install in four ways: - -"Package-1.0.tgz" : installs from a local file - -"http://example.com/Package-1.0.tgz" : installs from -anywhere on the net. - -"package.xml" : installs the package described in -package.xml. Useful for testing, or for wrapping a PEAR package in -another package manager such as RPM. - -"Package[-version/state][.tar]" : queries your default channel\'s server -({config master_server}) and downloads the newest package with -the preferred quality/state ({config preferred_state}). - -To retrieve Package version 1.1, use "Package-1.1," to retrieve -Package state beta, use "Package-beta." To retrieve an uncompressed -file, append .tar (make sure there is no file by the same name first) - -To download a package from another channel, prefix with the channel name like -"channel/Package" - -More than one package may be specified at once. It is ok to mix these -four ways of specifying packages. -'), - 'upgrade' => array( - 'summary' => 'Upgrade Package', - 'function' => 'doInstall', - 'shortcut' => 'up', - 'options' => array( - 'channel' => array( - 'shortopt' => 'c', - 'doc' => 'upgrade packages from a specific channel', - 'arg' => 'CHAN', - ), - 'force' => array( - 'shortopt' => 'f', - 'doc' => 'overwrite newer installed packages', - ), - 'loose' => array( - 'shortopt' => 'l', - 'doc' => 'do not check for recommended dependency version', - ), - 'nodeps' => array( - 'shortopt' => 'n', - 'doc' => 'ignore dependencies, upgrade anyway', - ), - 'register-only' => array( - 'shortopt' => 'r', - 'doc' => 'do not install files, only register the package as upgraded', - ), - 'nobuild' => array( - 'shortopt' => 'B', - 'doc' => 'don\'t build C extensions', - ), - 'nocompress' => array( - 'shortopt' => 'Z', - 'doc' => 'request uncompressed files when downloading', - ), - 'installroot' => array( - 'shortopt' => 'R', - 'arg' => 'DIR', - 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)', - ), - 'ignore-errors' => array( - 'doc' => 'force install even if there were errors', - ), - 'alldeps' => array( - 'shortopt' => 'a', - 'doc' => 'install all required and optional dependencies', - ), - 'onlyreqdeps' => array( - 'shortopt' => 'o', - 'doc' => 'install all required dependencies', - ), - 'offline' => array( - 'shortopt' => 'O', - 'doc' => 'do not attempt to download any urls or contact channels', - ), - 'pretend' => array( - 'shortopt' => 'p', - 'doc' => 'Only list the packages that would be downloaded', - ), - ), - 'doc' => ' ... -Upgrades one or more PEAR packages. See documentation for the -"install" command for ways to specify a package. - -When upgrading, your package will be updated if the provided new -package has a higher version number (use the -f option if you need to -upgrade anyway). - -More than one package may be specified at once. -'), - 'upgrade-all' => array( - 'summary' => 'Upgrade All Packages [Deprecated in favor of calling upgrade with no parameters]', - 'function' => 'doUpgradeAll', - 'shortcut' => 'ua', - 'options' => array( - 'channel' => array( - 'shortopt' => 'c', - 'doc' => 'upgrade packages from a specific channel', - 'arg' => 'CHAN', - ), - 'nodeps' => array( - 'shortopt' => 'n', - 'doc' => 'ignore dependencies, upgrade anyway', - ), - 'register-only' => array( - 'shortopt' => 'r', - 'doc' => 'do not install files, only register the package as upgraded', - ), - 'nobuild' => array( - 'shortopt' => 'B', - 'doc' => 'don\'t build C extensions', - ), - 'nocompress' => array( - 'shortopt' => 'Z', - 'doc' => 'request uncompressed files when downloading', - ), - 'installroot' => array( - 'shortopt' => 'R', - 'arg' => 'DIR', - 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT), use packagingroot for RPM', - ), - 'ignore-errors' => array( - 'doc' => 'force install even if there were errors', - ), - 'loose' => array( - 'doc' => 'do not check for recommended dependency version', - ), - ), - 'doc' => ' -WARNING: This function is deprecated in favor of using the upgrade command with no params - -Upgrades all packages that have a newer release available. Upgrades are -done only if there is a release available of the state specified in -"preferred_state" (currently {config preferred_state}), or a state considered -more stable. -'), - 'uninstall' => array( - 'summary' => 'Un-install Package', - 'function' => 'doUninstall', - 'shortcut' => 'un', - 'options' => array( - 'nodeps' => array( - 'shortopt' => 'n', - 'doc' => 'ignore dependencies, uninstall anyway', - ), - 'register-only' => array( - 'shortopt' => 'r', - 'doc' => 'do not remove files, only register the packages as not installed', - ), - 'installroot' => array( - 'shortopt' => 'R', - 'arg' => 'DIR', - 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)', - ), - 'ignore-errors' => array( - 'doc' => 'force install even if there were errors', - ), - 'offline' => array( - 'shortopt' => 'O', - 'doc' => 'do not attempt to uninstall remotely', - ), - ), - 'doc' => '[channel/] ... -Uninstalls one or more PEAR packages. More than one package may be -specified at once. Prefix with channel name to uninstall from a -channel not in your default channel ({config default_channel}) -'), - 'bundle' => array( - 'summary' => 'Unpacks a Pecl Package', - 'function' => 'doBundle', - 'shortcut' => 'bun', - 'options' => array( - 'destination' => array( - 'shortopt' => 'd', - 'arg' => 'DIR', - 'doc' => 'Optional destination directory for unpacking (defaults to current path or "ext" if exists)', - ), - 'force' => array( - 'shortopt' => 'f', - 'doc' => 'Force the unpacking even if there were errors in the package', - ), - ), - 'doc' => ' -Unpacks a Pecl Package into the selected location. It will download the -package if needed. -'), - 'run-scripts' => array( - 'summary' => 'Run Post-Install Scripts bundled with a package', - 'function' => 'doRunScripts', - 'shortcut' => 'rs', - 'options' => array( - ), - 'doc' => ' -Run post-installation scripts in package , if any exist. -'), - ); - - // }}} - // {{{ constructor - - /** - * PEAR_Command_Install constructor. - * - * @access public - */ - function PEAR_Command_Install(&$ui, &$config) - { - parent::PEAR_Command_Common($ui, $config); - } - - // }}} - - /** - * For unit testing purposes - */ - function &getDownloader(&$ui, $options, &$config) - { - if (!class_exists('PEAR_Downloader')) { - require_once 'PEAR/Downloader.php'; - } - $a = &new PEAR_Downloader($ui, $options, $config); - return $a; - } - - /** - * For unit testing purposes - */ - function &getInstaller(&$ui) - { - if (!class_exists('PEAR_Installer')) { - require_once 'PEAR/Installer.php'; - } - $a = &new PEAR_Installer($ui); - return $a; - } - - function enableExtension($binaries, $type) - { - if (!($phpini = $this->config->get('php_ini', null, 'pear.php.net'))) { - return PEAR::raiseError('configuration option "php_ini" is not set to php.ini location'); - } - $ini = $this->_parseIni($phpini); - if (PEAR::isError($ini)) { - return $ini; - } - $line = 0; - if ($type == 'extsrc' || $type == 'extbin') { - $search = 'extensions'; - $enable = 'extension'; - } else { - $search = 'zend_extensions'; - ob_start(); - phpinfo(INFO_GENERAL); - $info = ob_get_contents(); - ob_end_clean(); - $debug = function_exists('leak') ? '_debug' : ''; - $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : ''; - $enable = 'zend_extension' . $debug . $ts; - } - foreach ($ini[$search] as $line => $extension) { - if (in_array($extension, $binaries, true) || in_array( - $ini['extension_dir'] . DIRECTORY_SEPARATOR . $extension, $binaries, true)) { - // already enabled - assume if one is, all are - return true; - } - } - if ($line) { - $newini = array_slice($ini['all'], 0, $line); - } else { - $newini = array(); - } - foreach ($binaries as $binary) { - if (!$this->config->get('ext_dir') && $ini['extension_dir']) { - $binary = basename($binary); - } - $newini[] = $enable . '="' . $binary . '"' . (OS_UNIX ? "\n" : "\r\n"); - } - $newini = array_merge($newini, array_slice($ini['all'], $line)); - $fp = @fopen($phpini, 'wb'); - if (!$fp) { - return PEAR::raiseError('cannot open php.ini "' . $phpini . '" for writing'); - } - foreach ($newini as $line) { - fwrite($fp, $line); - } - fclose($fp); - return true; - } - - function disableExtension($binaries, $type) - { - if (!($phpini = $this->config->get('php_ini', null, 'pear.php.net'))) { - return PEAR::raiseError('configuration option "php_ini" is not set to php.ini location'); - } - $ini = $this->_parseIni($phpini); - if (PEAR::isError($ini)) { - return $ini; - } - $line = 0; - if ($type == 'extsrc' || $type == 'extbin') { - $search = 'extensions'; - $enable = 'extension'; - } else { - $search = 'zend_extensions'; - ob_start(); - phpinfo(INFO_GENERAL); - $info = ob_get_contents(); - ob_end_clean(); - $debug = function_exists('leak') ? '_debug' : ''; - $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : ''; - $enable = 'zend_extension' . $debug . $ts; - } - $found = false; - foreach ($ini[$search] as $line => $extension) { - if (in_array($extension, $binaries, true) || in_array( - $ini['extension_dir'] . DIRECTORY_SEPARATOR . $extension, $binaries, true)) { - $found = true; - break; - } - } - if (!$found) { - // not enabled - return true; - } - $fp = @fopen($phpini, 'wb'); - if (!$fp) { - return PEAR::raiseError('cannot open php.ini "' . $phpini . '" for writing'); - } - if ($line) { - $newini = array_slice($ini['all'], 0, $line); - // delete the enable line - $newini = array_merge($newini, array_slice($ini['all'], $line + 1)); - } else { - $newini = array_slice($ini['all'], 1); - } - foreach ($newini as $line) { - fwrite($fp, $line); - } - fclose($fp); - return true; - } - - function _parseIni($filename) - { - if (!file_exists($filename)) { - return PEAR::raiseError('php.ini "' . $filename . '" does not exist'); - } - - if (filesize($filename) > 300000) { - return PEAR::raiseError('php.ini "' . $filename . '" is too large, aborting'); - } - - ob_start(); - phpinfo(INFO_GENERAL); - $info = ob_get_contents(); - ob_end_clean(); - $debug = function_exists('leak') ? '_debug' : ''; - $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : ''; - $zend_extension_line = 'zend_extension' . $debug . $ts; - $all = @file($filename); - if (!$all) { - return PEAR::raiseError('php.ini "' . $filename .'" could not be read'); - } - $zend_extensions = $extensions = array(); - // assume this is right, but pull from the php.ini if it is found - $extension_dir = ini_get('extension_dir'); - foreach ($all as $linenum => $line) { - $line = trim($line); - if (!$line) { - continue; - } - if ($line[0] == ';') { - continue; - } - if (strtolower(substr($line, 0, 13)) == 'extension_dir') { - $line = trim(substr($line, 13)); - if ($line[0] == '=') { - $x = trim(substr($line, 1)); - $x = explode(';', $x); - $extension_dir = str_replace('"', '', array_shift($x)); - continue; - } - } - if (strtolower(substr($line, 0, 9)) == 'extension') { - $line = trim(substr($line, 9)); - if ($line[0] == '=') { - $x = trim(substr($line, 1)); - $x = explode(';', $x); - $extensions[$linenum] = str_replace('"', '', array_shift($x)); - continue; - } - } - if (strtolower(substr($line, 0, strlen($zend_extension_line))) == - $zend_extension_line) { - $line = trim(substr($line, strlen($zend_extension_line))); - if ($line[0] == '=') { - $x = trim(substr($line, 1)); - $x = explode(';', $x); - $zend_extensions[$linenum] = str_replace('"', '', array_shift($x)); - continue; - } - } - } - return array( - 'extensions' => $extensions, - 'zend_extensions' => $zend_extensions, - 'extension_dir' => $extension_dir, - 'all' => $all, - ); - } - - // {{{ doInstall() - - function doInstall($command, $options, $params) - { - if (!class_exists('PEAR_PackageFile')) { - require_once 'PEAR/PackageFile.php'; - } - - if (isset($options['installroot']) && isset($options['packagingroot'])) { - return $this->raiseError('ERROR: cannot use both --installroot and --packagingroot'); - } - - $reg = &$this->config->getRegistry(); - $channel = isset($options['channel']) ? $options['channel'] : $this->config->get('default_channel'); - if (!$reg->channelExists($channel)) { - return $this->raiseError('Channel "' . $channel . '" does not exist'); - } - - if (empty($this->installer)) { - $this->installer = &$this->getInstaller($this->ui); - } - - if ($command == 'upgrade' || $command == 'upgrade-all') { - // If people run the upgrade command but pass nothing, emulate a upgrade-all - if ($command == 'upgrade' && empty($params)) { - return $this->doUpgradeAll($command, $options, $params); - } - $options['upgrade'] = true; - } else { - $packages = $params; - } - - $instreg = &$reg; // instreg used to check if package is installed - if (isset($options['packagingroot']) && !isset($options['upgrade'])) { - $packrootphp_dir = $this->installer->_prependPath( - $this->config->get('php_dir', null, 'pear.php.net'), - $options['packagingroot']); - $instreg = new PEAR_Registry($packrootphp_dir); // other instreg! - - if ($this->config->get('verbose') > 2) { - $this->ui->outputData('using package root: ' . $options['packagingroot']); - } - } - - $abstractpackages = $otherpackages = array(); - // parse params - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - - foreach ($params as $param) { - if (strpos($param, 'http://') === 0) { - $otherpackages[] = $param; - continue; - } - - if (strpos($param, 'channel://') === false && @file_exists($param)) { - if (isset($options['force'])) { - $otherpackages[] = $param; - continue; - } - - $pkg = new PEAR_PackageFile($this->config); - $pf = $pkg->fromAnyFile($param, PEAR_VALIDATE_DOWNLOADING); - if (PEAR::isError($pf)) { - $otherpackages[] = $param; - continue; - } - - $exists = $reg->packageExists($pf->getPackage(), $pf->getChannel()); - $pversion = $reg->packageInfo($pf->getPackage(), 'version', $pf->getChannel()); - $version_compare = version_compare($pf->getVersion(), $pversion, '<='); - if ($exists && $version_compare) { - if ($this->config->get('verbose')) { - $this->ui->outputData('Ignoring installed package ' . - $reg->parsedPackageNameToString( - array('package' => $pf->getPackage(), - 'channel' => $pf->getChannel()), true)); - } - continue; - } - $otherpackages[] = $param; - continue; - } - - $e = $reg->parsePackageName($param, $channel); - if (PEAR::isError($e)) { - $otherpackages[] = $param; - } else { - $abstractpackages[] = $e; - } - } - PEAR::staticPopErrorHandling(); - - // if there are any local package .tgz or remote static url, we can't - // filter. The filter only works for abstract packages - if (count($abstractpackages) && !isset($options['force'])) { - // when not being forced, only do necessary upgrades/installs - if (isset($options['upgrade'])) { - $abstractpackages = $this->_filterUptodatePackages($abstractpackages, $command); - } else { - $count = count($abstractpackages); - foreach ($abstractpackages as $i => $package) { - if (isset($package['group'])) { - // do not filter out install groups - continue; - } - - if ($instreg->packageExists($package['package'], $package['channel'])) { - if ($count > 1) { - if ($this->config->get('verbose')) { - $this->ui->outputData('Ignoring installed package ' . - $reg->parsedPackageNameToString($package, true)); - } - unset($abstractpackages[$i]); - } elseif ($count === 1) { - // Lets try to upgrade it since it's already installed - $options['upgrade'] = true; - } - } - } - } - $abstractpackages = - array_map(array($reg, 'parsedPackageNameToString'), $abstractpackages); - } elseif (count($abstractpackages)) { - $abstractpackages = - array_map(array($reg, 'parsedPackageNameToString'), $abstractpackages); - } - - $packages = array_merge($abstractpackages, $otherpackages); - if (!count($packages)) { - $c = ''; - if (isset($options['channel'])){ - $c .= ' in channel "' . $options['channel'] . '"'; - } - $this->ui->outputData('Nothing to ' . $command . $c); - return true; - } - - $this->downloader = &$this->getDownloader($this->ui, $options, $this->config); - $errors = $downloaded = $binaries = array(); - $downloaded = &$this->downloader->download($packages); - if (PEAR::isError($downloaded)) { - return $this->raiseError($downloaded); - } - - $errors = $this->downloader->getErrorMsgs(); - if (count($errors)) { - $err = array(); - $err['data'] = array(); - foreach ($errors as $error) { - if ($error !== null) { - $err['data'][] = array($error); - } - } - - if (!empty($err['data'])) { - $err['headline'] = 'Install Errors'; - $this->ui->outputData($err); - } - - if (!count($downloaded)) { - return $this->raiseError("$command failed"); - } - } - - $data = array( - 'headline' => 'Packages that would be Installed' - ); - - if (isset($options['pretend'])) { - foreach ($downloaded as $package) { - $data['data'][] = array($reg->parsedPackageNameToString($package->getParsedPackage())); - } - $this->ui->outputData($data, 'pretend'); - return true; - } - - $this->installer->setOptions($options); - $this->installer->sortPackagesForInstall($downloaded); - if (PEAR::isError($err = $this->installer->setDownloadedPackages($downloaded))) { - $this->raiseError($err->getMessage()); - return true; - } - - $binaries = $extrainfo = array(); - foreach ($downloaded as $param) { - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $info = $this->installer->install($param, $options); - PEAR::staticPopErrorHandling(); - if (PEAR::isError($info)) { - $oldinfo = $info; - $pkg = &$param->getPackageFile(); - if ($info->getCode() != PEAR_INSTALLER_NOBINARY) { - if (!($info = $pkg->installBinary($this->installer))) { - $this->ui->outputData('ERROR: ' .$oldinfo->getMessage()); - continue; - } - - // we just installed a different package than requested, - // let's change the param and info so that the rest of this works - $param = $info[0]; - $info = $info[1]; - } - } - - if (!is_array($info)) { - return $this->raiseError("$command failed"); - } - - if ($param->getPackageType() == 'extsrc' || - $param->getPackageType() == 'extbin' || - $param->getPackageType() == 'zendextsrc' || - $param->getPackageType() == 'zendextbin' - ) { - $pkg = &$param->getPackageFile(); - if ($instbin = $pkg->getInstalledBinary()) { - $instpkg = &$instreg->getPackage($instbin, $pkg->getChannel()); - } else { - $instpkg = &$instreg->getPackage($pkg->getPackage(), $pkg->getChannel()); - } - - foreach ($instpkg->getFilelist() as $name => $atts) { - $pinfo = pathinfo($atts['installed_as']); - if (!isset($pinfo['extension']) || - in_array($pinfo['extension'], array('c', 'h')) - ) { - continue; // make sure we don't match php_blah.h - } - - if ((strpos($pinfo['basename'], 'php_') === 0 && - $pinfo['extension'] == 'dll') || - // most unices - $pinfo['extension'] == 'so' || - // hp-ux - $pinfo['extension'] == 'sl') { - $binaries[] = array($atts['installed_as'], $pinfo); - break; - } - } - - if (count($binaries)) { - foreach ($binaries as $pinfo) { - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $ret = $this->enableExtension(array($pinfo[0]), $param->getPackageType()); - PEAR::staticPopErrorHandling(); - if (PEAR::isError($ret)) { - $extrainfo[] = $ret->getMessage(); - if ($param->getPackageType() == 'extsrc' || - $param->getPackageType() == 'extbin') { - $exttype = 'extension'; - } else { - ob_start(); - phpinfo(INFO_GENERAL); - $info = ob_get_contents(); - ob_end_clean(); - $debug = function_exists('leak') ? '_debug' : ''; - $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : ''; - $exttype = 'zend_extension' . $debug . $ts; - } - $extrainfo[] = 'You should add "' . $exttype . '=' . - $pinfo[1]['basename'] . '" to php.ini'; - } else { - $extrainfo[] = 'Extension ' . $instpkg->getProvidesExtension() . - ' enabled in php.ini'; - } - } - } - } - - if ($this->config->get('verbose') > 0) { - $chan = $param->getChannel(); - $label = $reg->parsedPackageNameToString( - array( - 'channel' => $chan, - 'package' => $param->getPackage(), - 'version' => $param->getVersion(), - )); - $out = array('data' => "$command ok: $label"); - if (isset($info['release_warnings'])) { - $out['release_warnings'] = $info['release_warnings']; - } - $this->ui->outputData($out, $command); - - if (!isset($options['register-only']) && !isset($options['offline'])) { - if ($this->config->isDefinedLayer('ftp')) { - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $info = $this->installer->ftpInstall($param); - PEAR::staticPopErrorHandling(); - if (PEAR::isError($info)) { - $this->ui->outputData($info->getMessage()); - $this->ui->outputData("remote install failed: $label"); - } else { - $this->ui->outputData("remote install ok: $label"); - } - } - } - } - - $deps = $param->getDeps(); - if ($deps) { - if (isset($deps['group'])) { - $groups = $deps['group']; - if (!isset($groups[0])) { - $groups = array($groups); - } - - foreach ($groups as $group) { - if ($group['attribs']['name'] == 'default') { - // default group is always installed, unless the user - // explicitly chooses to install another group - continue; - } - $extrainfo[] = $param->getPackage() . ': Optional feature ' . - $group['attribs']['name'] . ' available (' . - $group['attribs']['hint'] . ')'; - } - - $extrainfo[] = $param->getPackage() . - ': To install optional features use "pear install ' . - $reg->parsedPackageNameToString( - array('package' => $param->getPackage(), - 'channel' => $param->getChannel()), true) . - '#featurename"'; - } - } - - $pkg = &$instreg->getPackage($param->getPackage(), $param->getChannel()); - // $pkg may be NULL if install is a 'fake' install via --packagingroot - if (is_object($pkg)) { - $pkg->setConfig($this->config); - if ($list = $pkg->listPostinstallScripts()) { - $pn = $reg->parsedPackageNameToString(array('channel' => - $param->getChannel(), 'package' => $param->getPackage()), true); - $extrainfo[] = $pn . ' has post-install scripts:'; - foreach ($list as $file) { - $extrainfo[] = $file; - } - $extrainfo[] = $param->getPackage() . - ': Use "pear run-scripts ' . $pn . '" to finish setup.'; - $extrainfo[] = 'DO NOT RUN SCRIPTS FROM UNTRUSTED SOURCES'; - } - } - } - - if (count($extrainfo)) { - foreach ($extrainfo as $info) { - $this->ui->outputData($info); - } - } - - return true; - } - - // }}} - // {{{ doUpgradeAll() - - function doUpgradeAll($command, $options, $params) - { - $reg = &$this->config->getRegistry(); - $upgrade = array(); - - if (isset($options['channel'])) { - $channels = array($options['channel']); - } else { - $channels = $reg->listChannels(); - } - - foreach ($channels as $channel) { - if ($channel == '__uri') { - continue; - } - - // parse name with channel - foreach ($reg->listPackages($channel) as $name) { - $upgrade[] = $reg->parsedPackageNameToString(array( - 'channel' => $channel, - 'package' => $name - )); - } - } - - $err = $this->doInstall($command, $options, $upgrade); - if (PEAR::isError($err)) { - $this->ui->outputData($err->getMessage(), $command); - } - } - - // }}} - // {{{ doUninstall() - - function doUninstall($command, $options, $params) - { - if (count($params) < 1) { - return $this->raiseError("Please supply the package(s) you want to uninstall"); - } - - if (empty($this->installer)) { - $this->installer = &$this->getInstaller($this->ui); - } - - if (isset($options['remoteconfig'])) { - $e = $this->config->readFTPConfigFile($options['remoteconfig']); - if (!PEAR::isError($e)) { - $this->installer->setConfig($this->config); - } - } - - $reg = &$this->config->getRegistry(); - $newparams = array(); - $binaries = array(); - $badparams = array(); - foreach ($params as $pkg) { - $channel = $this->config->get('default_channel'); - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $parsed = $reg->parsePackageName($pkg, $channel); - PEAR::staticPopErrorHandling(); - if (!$parsed || PEAR::isError($parsed)) { - $badparams[] = $pkg; - continue; - } - $package = $parsed['package']; - $channel = $parsed['channel']; - $info = &$reg->getPackage($package, $channel); - if ($info === null && - ($channel == 'pear.php.net' || $channel == 'pecl.php.net')) { - // make sure this isn't a package that has flipped from pear to pecl but - // used a package.xml 1.0 - $testc = ($channel == 'pear.php.net') ? 'pecl.php.net' : 'pear.php.net'; - $info = &$reg->getPackage($package, $testc); - if ($info !== null) { - $channel = $testc; - } - } - if ($info === null) { - $badparams[] = $pkg; - } else { - $newparams[] = &$info; - // check for binary packages (this is an alias for those packages if so) - if ($installedbinary = $info->getInstalledBinary()) { - $this->ui->log('adding binary package ' . - $reg->parsedPackageNameToString(array('channel' => $channel, - 'package' => $installedbinary), true)); - $newparams[] = &$reg->getPackage($installedbinary, $channel); - } - // add the contents of a dependency group to the list of installed packages - if (isset($parsed['group'])) { - $group = $info->getDependencyGroup($parsed['group']); - if ($group) { - $installed = $reg->getInstalledGroup($group); - if ($installed) { - foreach ($installed as $i => $p) { - $newparams[] = &$installed[$i]; - } - } - } - } - } - } - $err = $this->installer->sortPackagesForUninstall($newparams); - if (PEAR::isError($err)) { - $this->ui->outputData($err->getMessage(), $command); - return true; - } - $params = $newparams; - // twist this to use it to check on whether dependent packages are also being uninstalled - // for circular dependencies like subpackages - $this->installer->setUninstallPackages($newparams); - $params = array_merge($params, $badparams); - $binaries = array(); - foreach ($params as $pkg) { - $this->installer->pushErrorHandling(PEAR_ERROR_RETURN); - if ($err = $this->installer->uninstall($pkg, $options)) { - $this->installer->popErrorHandling(); - if (PEAR::isError($err)) { - $this->ui->outputData($err->getMessage(), $command); - continue; - } - if ($pkg->getPackageType() == 'extsrc' || - $pkg->getPackageType() == 'extbin' || - $pkg->getPackageType() == 'zendextsrc' || - $pkg->getPackageType() == 'zendextbin') { - if ($instbin = $pkg->getInstalledBinary()) { - continue; // this will be uninstalled later - } - - foreach ($pkg->getFilelist() as $name => $atts) { - $pinfo = pathinfo($atts['installed_as']); - if (!isset($pinfo['extension']) || - in_array($pinfo['extension'], array('c', 'h'))) { - continue; // make sure we don't match php_blah.h - } - if ((strpos($pinfo['basename'], 'php_') === 0 && - $pinfo['extension'] == 'dll') || - // most unices - $pinfo['extension'] == 'so' || - // hp-ux - $pinfo['extension'] == 'sl') { - $binaries[] = array($atts['installed_as'], $pinfo); - break; - } - } - if (count($binaries)) { - foreach ($binaries as $pinfo) { - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $ret = $this->disableExtension(array($pinfo[0]), $pkg->getPackageType()); - PEAR::staticPopErrorHandling(); - if (PEAR::isError($ret)) { - $extrainfo[] = $ret->getMessage(); - if ($pkg->getPackageType() == 'extsrc' || - $pkg->getPackageType() == 'extbin') { - $exttype = 'extension'; - } else { - ob_start(); - phpinfo(INFO_GENERAL); - $info = ob_get_contents(); - ob_end_clean(); - $debug = function_exists('leak') ? '_debug' : ''; - $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : ''; - $exttype = 'zend_extension' . $debug . $ts; - } - $this->ui->outputData('Unable to remove "' . $exttype . '=' . - $pinfo[1]['basename'] . '" from php.ini', $command); - } else { - $this->ui->outputData('Extension ' . $pkg->getProvidesExtension() . - ' disabled in php.ini', $command); - } - } - } - } - $savepkg = $pkg; - if ($this->config->get('verbose') > 0) { - if (is_object($pkg)) { - $pkg = $reg->parsedPackageNameToString($pkg); - } - $this->ui->outputData("uninstall ok: $pkg", $command); - } - if (!isset($options['offline']) && is_object($savepkg) && - defined('PEAR_REMOTEINSTALL_OK')) { - if ($this->config->isDefinedLayer('ftp')) { - $this->installer->pushErrorHandling(PEAR_ERROR_RETURN); - $info = $this->installer->ftpUninstall($savepkg); - $this->installer->popErrorHandling(); - if (PEAR::isError($info)) { - $this->ui->outputData($info->getMessage()); - $this->ui->outputData("remote uninstall failed: $pkg"); - } else { - $this->ui->outputData("remote uninstall ok: $pkg"); - } - } - } - } else { - $this->installer->popErrorHandling(); - if (!is_object($pkg)) { - return $this->raiseError("uninstall failed: $pkg"); - } - $pkg = $reg->parsedPackageNameToString($pkg); - } - } - - return true; - } - - // }}} - - - // }}} - // {{{ doBundle() - /* - (cox) It just downloads and untars the package, does not do - any check that the PEAR_Installer::_installFile() does. - */ - - function doBundle($command, $options, $params) - { - $opts = array( - 'force' => true, - 'nodeps' => true, - 'soft' => true, - 'downloadonly' => true - ); - $downloader = &$this->getDownloader($this->ui, $opts, $this->config); - $reg = &$this->config->getRegistry(); - if (count($params) < 1) { - return $this->raiseError("Please supply the package you want to bundle"); - } - - if (isset($options['destination'])) { - if (!is_dir($options['destination'])) { - System::mkdir('-p ' . $options['destination']); - } - $dest = realpath($options['destination']); - } else { - $pwd = getcwd(); - $dir = $pwd . DIRECTORY_SEPARATOR . 'ext'; - $dest = is_dir($dir) ? $dir : $pwd; - } - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $err = $downloader->setDownloadDir($dest); - PEAR::staticPopErrorHandling(); - if (PEAR::isError($err)) { - return PEAR::raiseError('download directory "' . $dest . - '" is not writeable.'); - } - $result = &$downloader->download(array($params[0])); - if (PEAR::isError($result)) { - return $result; - } - if (!isset($result[0])) { - return $this->raiseError('unable to unpack ' . $params[0]); - } - $pkgfile = &$result[0]->getPackageFile(); - $pkgname = $pkgfile->getName(); - $pkgversion = $pkgfile->getVersion(); - - // Unpacking ------------------------------------------------- - $dest .= DIRECTORY_SEPARATOR . $pkgname; - $orig = $pkgname . '-' . $pkgversion; - - $tar = &new Archive_Tar($pkgfile->getArchiveFile()); - if (!$tar->extractModify($dest, $orig)) { - return $this->raiseError('unable to unpack ' . $pkgfile->getArchiveFile()); - } - $this->ui->outputData("Package ready at '$dest'"); - // }}} - } - - // }}} - - function doRunScripts($command, $options, $params) - { - if (!isset($params[0])) { - return $this->raiseError('run-scripts expects 1 parameter: a package name'); - } - - $reg = &$this->config->getRegistry(); - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $parsed = $reg->parsePackageName($params[0], $this->config->get('default_channel')); - PEAR::staticPopErrorHandling(); - if (PEAR::isError($parsed)) { - return $this->raiseError($parsed); - } - - $package = &$reg->getPackage($parsed['package'], $parsed['channel']); - if (!is_object($package)) { - return $this->raiseError('Could not retrieve package "' . $params[0] . '" from registry'); - } - - $package->setConfig($this->config); - $package->runPostinstallScripts(); - $this->ui->outputData('Install scripts complete', $command); - return true; - } - - /** - * Given a list of packages, filter out those ones that are already up to date - * - * @param $packages: packages, in parsed array format ! - * @return list of packages that can be upgraded - */ - function _filterUptodatePackages($packages, $command) - { - $reg = &$this->config->getRegistry(); - $latestReleases = array(); - - $ret = array(); - foreach ($packages as $package) { - if (isset($package['group'])) { - $ret[] = $package; - continue; - } - - $channel = $package['channel']; - $name = $package['package']; - if (!$reg->packageExists($name, $channel)) { - $ret[] = $package; - continue; - } - - if (!isset($latestReleases[$channel])) { - // fill in cache for this channel - $chan = &$reg->getChannel($channel); - if (PEAR::isError($chan)) { - return $this->raiseError($chan); - } - - $base2 = false; - $preferred_mirror = $this->config->get('preferred_mirror', null, $channel); - if ($chan->supportsREST($preferred_mirror) && - ( - //($base2 = $chan->getBaseURL('REST1.4', $preferred_mirror)) || - ($base = $chan->getBaseURL('REST1.0', $preferred_mirror)) - ) - ) { - $dorest = true; - } - - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - if (!isset($package['state'])) { - $state = $this->config->get('preferred_state', null, $channel); - } else { - $state = $package['state']; - } - - if ($dorest) { - if ($base2) { - $rest = &$this->config->getREST('1.4', array()); - $base = $base2; - } else { - $rest = &$this->config->getREST('1.0', array()); - } - - $installed = array_flip($reg->listPackages($channel)); - $latest = $rest->listLatestUpgrades($base, $state, $installed, $channel, $reg); - } - - PEAR::staticPopErrorHandling(); - if (PEAR::isError($latest)) { - $this->ui->outputData('Error getting channel info from ' . $channel . - ': ' . $latest->getMessage()); - continue; - } - - $latestReleases[$channel] = array_change_key_case($latest); - } - - // check package for latest release - $name_lower = strtolower($name); - if (isset($latestReleases[$channel][$name_lower])) { - // if not set, up to date - $inst_version = $reg->packageInfo($name, 'version', $channel); - $channel_version = $latestReleases[$channel][$name_lower]['version']; - if (version_compare($channel_version, $inst_version, 'le')) { - // installed version is up-to-date - continue; - } - - // maintain BC - if ($command == 'upgrade-all') { - $this->ui->outputData(array('data' => 'Will upgrade ' . - $reg->parsedPackageNameToString($package)), $command); - } - $ret[] = $package; - } - } - - return $ret; - } -} \ No newline at end of file diff --git a/pear/PEAR/Command/Install.xml b/pear/PEAR/Command/Install.xml deleted file mode 100644 index 1b1e933..0000000 --- a/pear/PEAR/Command/Install.xml +++ /dev/null @@ -1,276 +0,0 @@ - - - Install Package - doInstall - i - - - f - will overwrite newer installed packages - - - l - do not check for recommended dependency version - - - n - ignore dependencies, install anyway - - - r - do not install files, only register the package as installed - - - s - soft install, fail silently, or upgrade if already installed - - - B - don't build C extensions - - - Z - request uncompressed files when downloading - - - R - root directory used when installing files (ala PHP's INSTALL_ROOT), use packagingroot for RPM - DIR - - - P - root directory used when packaging files, like RPM packaging - DIR - - - - force install even if there were errors - - - a - install all required and optional dependencies - - - o - install all required dependencies - - - O - do not attempt to download any urls or contact channels - - - p - Only list the packages that would be downloaded - - - [channel/]<package> ... -Installs one or more PEAR packages. You can specify a package to -install in four ways: - -"Package-1.0.tgz" : installs from a local file - -"http://example.com/Package-1.0.tgz" : installs from -anywhere on the net. - -"package.xml" : installs the package described in -package.xml. Useful for testing, or for wrapping a PEAR package in -another package manager such as RPM. - -"Package[-version/state][.tar]" : queries your default channel's server -({config master_server}) and downloads the newest package with -the preferred quality/state ({config preferred_state}). - -To retrieve Package version 1.1, use "Package-1.1," to retrieve -Package state beta, use "Package-beta." To retrieve an uncompressed -file, append .tar (make sure there is no file by the same name first) - -To download a package from another channel, prefix with the channel name like -"channel/Package" - -More than one package may be specified at once. It is ok to mix these -four ways of specifying packages. - - - - Upgrade Package - doInstall - up - - - c - upgrade packages from a specific channel - CHAN - - - f - overwrite newer installed packages - - - l - do not check for recommended dependency version - - - n - ignore dependencies, upgrade anyway - - - r - do not install files, only register the package as upgraded - - - B - don't build C extensions - - - Z - request uncompressed files when downloading - - - R - root directory used when installing files (ala PHP's INSTALL_ROOT) - DIR - - - - force install even if there were errors - - - a - install all required and optional dependencies - - - o - install all required dependencies - - - O - do not attempt to download any urls or contact channels - - - p - Only list the packages that would be downloaded - - - <package> ... -Upgrades one or more PEAR packages. See documentation for the -"install" command for ways to specify a package. - -When upgrading, your package will be updated if the provided new -package has a higher version number (use the -f option if you need to -upgrade anyway). - -More than one package may be specified at once. - - - - Upgrade All Packages [Deprecated in favor of calling upgrade with no parameters] - doUpgradeAll - ua - - - c - upgrade packages from a specific channel - CHAN - - - n - ignore dependencies, upgrade anyway - - - r - do not install files, only register the package as upgraded - - - B - don't build C extensions - - - Z - request uncompressed files when downloading - - - R - root directory used when installing files (ala PHP's INSTALL_ROOT), use packagingroot for RPM - DIR - - - - force install even if there were errors - - - - do not check for recommended dependency version - - - -WARNING: This function is deprecated in favor of using the upgrade command with no params - -Upgrades all packages that have a newer release available. Upgrades are -done only if there is a release available of the state specified in -"preferred_state" (currently {config preferred_state}), or a state considered -more stable. - - - - Un-install Package - doUninstall - un - - - n - ignore dependencies, uninstall anyway - - - r - do not remove files, only register the packages as not installed - - - R - root directory used when installing files (ala PHP's INSTALL_ROOT) - DIR - - - - force install even if there were errors - - - O - do not attempt to uninstall remotely - - - [channel/]<package> ... -Uninstalls one or more PEAR packages. More than one package may be -specified at once. Prefix with channel name to uninstall from a -channel not in your default channel ({config default_channel}) - - - - Unpacks a Pecl Package - doBundle - bun - - - d - Optional destination directory for unpacking (defaults to current path or "ext" if exists) - DIR - - - f - Force the unpacking even if there were errors in the package - - - <package> -Unpacks a Pecl Package into the selected location. It will download the -package if needed. - - - - Run Post-Install Scripts bundled with a package - doRunScripts - rs - - <package> -Run post-installation scripts in package <package>, if any exist. - - - \ No newline at end of file diff --git a/pear/PEAR/Command/Mirror.php b/pear/PEAR/Command/Mirror.php deleted file mode 100644 index 4f646b2..0000000 --- a/pear/PEAR/Command/Mirror.php +++ /dev/null @@ -1,139 +0,0 @@ - - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.2.0 - */ - -/** - * base class - */ -require_once 'PEAR/Command/Common.php'; - -/** - * PEAR commands for providing file mirrors - * - * @category pear - * @package PEAR - * @author Alexander Merz - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.2.0 - */ -class PEAR_Command_Mirror extends PEAR_Command_Common -{ - var $commands = array( - 'download-all' => array( - 'summary' => 'Downloads each available package from the default channel', - 'function' => 'doDownloadAll', - 'shortcut' => 'da', - 'options' => array( - 'channel' => - array( - 'shortopt' => 'c', - 'doc' => 'specify a channel other than the default channel', - 'arg' => 'CHAN', - ), - ), - 'doc' => ' -Requests a list of available packages from the default channel ({config default_channel}) -and downloads them to current working directory. Note: only -packages within preferred_state ({config preferred_state}) will be downloaded' - ), - ); - - /** - * PEAR_Command_Mirror constructor. - * - * @access public - * @param object PEAR_Frontend a reference to an frontend - * @param object PEAR_Config a reference to the configuration data - */ - function PEAR_Command_Mirror(&$ui, &$config) - { - parent::PEAR_Command_Common($ui, $config); - } - - /** - * For unit-testing - */ - function &factory($a) - { - $a = &PEAR_Command::factory($a, $this->config); - return $a; - } - - /** - * retrieves a list of avaible Packages from master server - * and downloads them - * - * @access public - * @param string $command the command - * @param array $options the command options before the command - * @param array $params the stuff after the command name - * @return bool true if succesful - * @throw PEAR_Error - */ - function doDownloadAll($command, $options, $params) - { - $savechannel = $this->config->get('default_channel'); - $reg = &$this->config->getRegistry(); - $channel = isset($options['channel']) ? $options['channel'] : - $this->config->get('default_channel'); - if (!$reg->channelExists($channel)) { - $this->config->set('default_channel', $savechannel); - return $this->raiseError('Channel "' . $channel . '" does not exist'); - } - $this->config->set('default_channel', $channel); - - $this->ui->outputData('Using Channel ' . $this->config->get('default_channel')); - $chan = $reg->getChannel($channel); - if (PEAR::isError($chan)) { - return $this->raiseError($chan); - } - - if ($chan->supportsREST($this->config->get('preferred_mirror')) && - $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) { - $rest = &$this->config->getREST('1.0', array()); - $remoteInfo = array_flip($rest->listPackages($base, $channel)); - } - - if (PEAR::isError($remoteInfo)) { - return $remoteInfo; - } - - $cmd = &$this->factory("download"); - if (PEAR::isError($cmd)) { - return $cmd; - } - - $this->ui->outputData('Using Preferred State of ' . - $this->config->get('preferred_state')); - $this->ui->outputData('Gathering release information, please wait...'); - - /** - * Error handling not necessary, because already done by - * the download command - */ - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $err = $cmd->run('download', array('downloadonly' => true), array_keys($remoteInfo)); - PEAR::staticPopErrorHandling(); - $this->config->set('default_channel', $savechannel); - if (PEAR::isError($err)) { - $this->ui->outputData($err->getMessage()); - } - - return true; - } -} \ No newline at end of file diff --git a/pear/PEAR/Command/Mirror.xml b/pear/PEAR/Command/Mirror.xml deleted file mode 100644 index fe8be9d..0000000 --- a/pear/PEAR/Command/Mirror.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - Downloads each available package from the default channel - doDownloadAll - da - - - c - specify a channel other than the default channel - CHAN - - - -Requests a list of available packages from the default channel ({config default_channel}) -and downloads them to current working directory. Note: only -packages within preferred_state ({config preferred_state}) will be downloaded - - \ No newline at end of file diff --git a/pear/PEAR/Command/Package.php b/pear/PEAR/Command/Package.php deleted file mode 100644 index 44c647b..0000000 --- a/pear/PEAR/Command/Package.php +++ /dev/null @@ -1,1124 +0,0 @@ - - * @author Martin Jansen - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 0.1 - */ - -/** - * base class - */ -require_once 'PEAR/Command/Common.php'; - -/** - * PEAR commands for login/logout - * - * @category pear - * @package PEAR - * @author Stig Bakken - * @author Martin Jansen - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 0.1 - */ - -class PEAR_Command_Package extends PEAR_Command_Common -{ - var $commands = array( - 'package' => array( - 'summary' => 'Build Package', - 'function' => 'doPackage', - 'shortcut' => 'p', - 'options' => array( - 'nocompress' => array( - 'shortopt' => 'Z', - 'doc' => 'Do not gzip the package file' - ), - 'showname' => array( - 'shortopt' => 'n', - 'doc' => 'Print the name of the packaged file.', - ), - ), - 'doc' => '[descfile] [descfile2] -Creates a PEAR package from its description file (usually called -package.xml). If a second packagefile is passed in, then -the packager will check to make sure that one is a package.xml -version 1.0, and the other is a package.xml version 2.0. The -package.xml version 1.0 will be saved as "package.xml" in the archive, -and the other as "package2.xml" in the archive" -' - ), - 'package-validate' => array( - 'summary' => 'Validate Package Consistency', - 'function' => 'doPackageValidate', - 'shortcut' => 'pv', - 'options' => array(), - 'doc' => ' -', - ), - 'cvsdiff' => array( - 'summary' => 'Run a "cvs diff" for all files in a package', - 'function' => 'doCvsDiff', - 'shortcut' => 'cd', - 'options' => array( - 'quiet' => array( - 'shortopt' => 'q', - 'doc' => 'Be quiet', - ), - 'reallyquiet' => array( - 'shortopt' => 'Q', - 'doc' => 'Be really quiet', - ), - 'date' => array( - 'shortopt' => 'D', - 'doc' => 'Diff against revision of DATE', - 'arg' => 'DATE', - ), - 'release' => array( - 'shortopt' => 'R', - 'doc' => 'Diff against tag for package release REL', - 'arg' => 'REL', - ), - 'revision' => array( - 'shortopt' => 'r', - 'doc' => 'Diff against revision REV', - 'arg' => 'REV', - ), - 'context' => array( - 'shortopt' => 'c', - 'doc' => 'Generate context diff', - ), - 'unified' => array( - 'shortopt' => 'u', - 'doc' => 'Generate unified diff', - ), - 'ignore-case' => array( - 'shortopt' => 'i', - 'doc' => 'Ignore case, consider upper- and lower-case letters equivalent', - ), - 'ignore-whitespace' => array( - 'shortopt' => 'b', - 'doc' => 'Ignore changes in amount of white space', - ), - 'ignore-blank-lines' => array( - 'shortopt' => 'B', - 'doc' => 'Ignore changes that insert or delete blank lines', - ), - 'brief' => array( - 'doc' => 'Report only whether the files differ, no details', - ), - 'dry-run' => array( - 'shortopt' => 'n', - 'doc' => 'Don\'t do anything, just pretend', - ), - ), - 'doc' => ' -Compares all the files in a package. Without any options, this -command will compare the current code with the last checked-in code. -Using the -r or -R option you may compare the current code with that -of a specific release. -', - ), - 'svntag' => array( - 'summary' => 'Set SVN Release Tag', - 'function' => 'doSvnTag', - 'shortcut' => 'sv', - 'options' => array( - 'quiet' => array( - 'shortopt' => 'q', - 'doc' => 'Be quiet', - ), - 'slide' => array( - 'shortopt' => 'F', - 'doc' => 'Move (slide) tag if it exists', - ), - 'delete' => array( - 'shortopt' => 'd', - 'doc' => 'Remove tag', - ), - 'dry-run' => array( - 'shortopt' => 'n', - 'doc' => 'Don\'t do anything, just pretend', - ), - ), - 'doc' => ' [files...] - Sets a SVN tag on all files in a package. Use this command after you have - packaged a distribution tarball with the "package" command to tag what - revisions of what files were in that release. If need to fix something - after running svntag once, but before the tarball is released to the public, - use the "slide" option to move the release tag. - - to include files (such as a second package.xml, or tests not included in the - release), pass them as additional parameters. - ', - ), - 'cvstag' => array( - 'summary' => 'Set CVS Release Tag', - 'function' => 'doCvsTag', - 'shortcut' => 'ct', - 'options' => array( - 'quiet' => array( - 'shortopt' => 'q', - 'doc' => 'Be quiet', - ), - 'reallyquiet' => array( - 'shortopt' => 'Q', - 'doc' => 'Be really quiet', - ), - 'slide' => array( - 'shortopt' => 'F', - 'doc' => 'Move (slide) tag if it exists', - ), - 'delete' => array( - 'shortopt' => 'd', - 'doc' => 'Remove tag', - ), - 'dry-run' => array( - 'shortopt' => 'n', - 'doc' => 'Don\'t do anything, just pretend', - ), - ), - 'doc' => ' [files...] -Sets a CVS tag on all files in a package. Use this command after you have -packaged a distribution tarball with the "package" command to tag what -revisions of what files were in that release. If need to fix something -after running cvstag once, but before the tarball is released to the public, -use the "slide" option to move the release tag. - -to include files (such as a second package.xml, or tests not included in the -release), pass them as additional parameters. -', - ), - 'package-dependencies' => array( - 'summary' => 'Show package dependencies', - 'function' => 'doPackageDependencies', - 'shortcut' => 'pd', - 'options' => array(), - 'doc' => ' or or -List all dependencies the package has. -Can take a tgz / tar file, package.xml or a package name of an installed package.' - ), - 'sign' => array( - 'summary' => 'Sign a package distribution file', - 'function' => 'doSign', - 'shortcut' => 'si', - 'options' => array( - 'verbose' => array( - 'shortopt' => 'v', - 'doc' => 'Display GnuPG output', - ), - ), - 'doc' => ' -Signs a package distribution (.tar or .tgz) file with GnuPG.', - ), - 'makerpm' => array( - 'summary' => 'Builds an RPM spec file from a PEAR package', - 'function' => 'doMakeRPM', - 'shortcut' => 'rpm', - 'options' => array( - 'spec-template' => array( - 'shortopt' => 't', - 'arg' => 'FILE', - 'doc' => 'Use FILE as RPM spec file template' - ), - 'rpm-pkgname' => array( - 'shortopt' => 'p', - 'arg' => 'FORMAT', - 'doc' => 'Use FORMAT as format string for RPM package name, %s is replaced -by the PEAR package name, defaults to "PEAR::%s".', - ), - ), - 'doc' => ' - -Creates an RPM .spec file for wrapping a PEAR package inside an RPM -package. Intended to be used from the SPECS directory, with the PEAR -package tarball in the SOURCES directory: - -$ pear makerpm ../SOURCES/Net_Socket-1.0.tgz -Wrote RPM spec file PEAR::Net_Geo-1.0.spec -$ rpm -bb PEAR::Net_Socket-1.0.spec -... -Wrote: /usr/src/redhat/RPMS/i386/PEAR::Net_Socket-1.0-1.i386.rpm -', - ), - 'convert' => array( - 'summary' => 'Convert a package.xml 1.0 to package.xml 2.0 format', - 'function' => 'doConvert', - 'shortcut' => 'c2', - 'options' => array( - 'flat' => array( - 'shortopt' => 'f', - 'doc' => 'do not beautify the filelist.', - ), - ), - 'doc' => '[descfile] [descfile2] -Converts a package.xml in 1.0 format into a package.xml -in 2.0 format. The new file will be named package2.xml by default, -and package.xml will be used as the old file by default. -This is not the most intelligent conversion, and should only be -used for automated conversion or learning the format. -' - ), - ); - - var $output; - - /** - * PEAR_Command_Package constructor. - * - * @access public - */ - function PEAR_Command_Package(&$ui, &$config) - { - parent::PEAR_Command_Common($ui, $config); - } - - function _displayValidationResults($err, $warn, $strict = false) - { - foreach ($err as $e) { - $this->output .= "Error: $e\n"; - } - foreach ($warn as $w) { - $this->output .= "Warning: $w\n"; - } - $this->output .= sprintf('Validation: %d error(s), %d warning(s)'."\n", - sizeof($err), sizeof($warn)); - if ($strict && count($err) > 0) { - $this->output .= "Fix these errors and try again."; - return false; - } - return true; - } - - function &getPackager() - { - if (!class_exists('PEAR_Packager')) { - require_once 'PEAR/Packager.php'; - } - $a = &new PEAR_Packager; - return $a; - } - - function &getPackageFile($config, $debug = false) - { - if (!class_exists('PEAR_Common')) { - require_once 'PEAR/Common.php'; - } - if (!class_exists('PEAR_PackageFile')) { - require_once 'PEAR/PackageFile.php'; - } - $a = &new PEAR_PackageFile($config, $debug); - $common = new PEAR_Common; - $common->ui = $this->ui; - $a->setLogger($common); - return $a; - } - - function doPackage($command, $options, $params) - { - $this->output = ''; - $pkginfofile = isset($params[0]) ? $params[0] : 'package.xml'; - $pkg2 = isset($params[1]) ? $params[1] : null; - if (!$pkg2 && !isset($params[0]) && file_exists('package2.xml')) { - $pkg2 = 'package2.xml'; - } - - $packager = &$this->getPackager(); - $compress = empty($options['nocompress']) ? true : false; - $result = $packager->package($pkginfofile, $compress, $pkg2); - if (PEAR::isError($result)) { - return $this->raiseError($result); - } - - // Don't want output, only the package file name just created - if (isset($options['showname'])) { - $this->output = $result; - } - - if ($this->output) { - $this->ui->outputData($this->output, $command); - } - - return true; - } - - function doPackageValidate($command, $options, $params) - { - $this->output = ''; - if (count($params) < 1) { - $params[0] = 'package.xml'; - } - - $obj = &$this->getPackageFile($this->config, $this->_debug); - $obj->rawReturn(); - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $info = $obj->fromTgzFile($params[0], PEAR_VALIDATE_NORMAL); - if (PEAR::isError($info)) { - $info = $obj->fromPackageFile($params[0], PEAR_VALIDATE_NORMAL); - } else { - $archive = $info->getArchiveFile(); - $tar = &new Archive_Tar($archive); - $tar->extract(dirname($info->getPackageFile())); - $info->setPackageFile(dirname($info->getPackageFile()) . DIRECTORY_SEPARATOR . - $info->getPackage() . '-' . $info->getVersion() . DIRECTORY_SEPARATOR . - basename($info->getPackageFile())); - } - - PEAR::staticPopErrorHandling(); - if (PEAR::isError($info)) { - return $this->raiseError($info); - } - - $valid = false; - if ($info->getPackagexmlVersion() == '2.0') { - if ($valid = $info->validate(PEAR_VALIDATE_NORMAL)) { - $info->flattenFileList(); - $valid = $info->validate(PEAR_VALIDATE_PACKAGING); - } - } else { - $valid = $info->validate(PEAR_VALIDATE_PACKAGING); - } - - $err = $warn = array(); - if ($errors = $info->getValidationWarnings()) { - foreach ($errors as $error) { - if ($error['level'] == 'warning') { - $warn[] = $error['message']; - } else { - $err[] = $error['message']; - } - } - } - - $this->_displayValidationResults($err, $warn); - $this->ui->outputData($this->output, $command); - return true; - } - - function doSvnTag($command, $options, $params) - { - $this->output = ''; - $_cmd = $command; - if (count($params) < 1) { - $help = $this->getHelp($command); - return $this->raiseError("$command: missing parameter: $help[0]"); - } - - $packageFile = realpath($params[0]); - $dir = dirname($packageFile); - $dir = substr($dir, strrpos($dir, DIRECTORY_SEPARATOR) + 1); - $obj = &$this->getPackageFile($this->config, $this->_debug); - $info = $obj->fromAnyFile($packageFile, PEAR_VALIDATE_NORMAL); - if (PEAR::isError($info)) { - return $this->raiseError($info); - } - - $err = $warn = array(); - if (!$info->validate()) { - foreach ($info->getValidationWarnings() as $error) { - if ($error['level'] == 'warning') { - $warn[] = $error['message']; - } else { - $err[] = $error['message']; - } - } - } - - if (!$this->_displayValidationResults($err, $warn, true)) { - $this->ui->outputData($this->output, $command); - return $this->raiseError('SVN tag failed'); - } - - $version = $info->getVersion(); - $package = $info->getName(); - $svntag = "$package-$version"; - - if (isset($options['delete'])) { - return $this->_svnRemoveTag($version, $package, $svntag, $packageFile, $options); - } - - $path = $this->_svnFindPath($packageFile); - - // Check if there are any modified files - $fp = popen('svn st --xml ' . dirname($packageFile), "r"); - $out = ''; - while ($line = fgets($fp, 1024)) { - $out .= rtrim($line)."\n"; - } - pclose($fp); - - if (!isset($options['quiet']) && strpos($out, 'item="modified"')) { - $params = array(array( - 'name' => 'modified', - 'type' => 'yesno', - 'default' => 'no', - 'prompt' => 'You have files in your SVN checkout (' . $path['from'] . ') that have been modified but not commited, do you still want to tag ' . $version . '?', - )); - $answers = $this->ui->confirmDialog($params); - - if (!in_array($answers['modified'], array('y', 'yes', 'on', '1'))) { - return true; - } - } - - if (isset($options['slide'])) { - $this->_svnRemoveTag($version, $package, $svntag, $packageFile, $options); - } - - // Check if tag already exists - $releaseTag = $path['local']['base'] . 'tags' . DIRECTORY_SEPARATOR . $svntag; - $existsCommand = 'svn ls ' . $path['base'] . 'tags/'; - - $fp = popen($existsCommand, "r"); - $out = ''; - while ($line = fgets($fp, 1024)) { - $out .= rtrim($line)."\n"; - } - pclose($fp); - - if (in_array($svntag . DIRECTORY_SEPARATOR, explode("\n", $out))) { - $this->ui->outputData($this->output, $command); - return $this->raiseError('SVN tag ' . $svntag . ' for ' . $package . ' already exists.'); - } elseif (file_exists($path['local']['base'] . 'tags') === false) { - return $this->raiseError('Can not locate the tags directory at ' . $path['local']['base'] . 'tags'); - } elseif (is_writeable($path['local']['base'] . 'tags') === false) { - return $this->raiseError('Can not write to the tag directory at ' . $path['local']['base'] . 'tags'); - } else { - $makeCommand = 'svn mkdir ' . $releaseTag; - $this->output .= "+ $makeCommand\n"; - if (empty($options['dry-run'])) { - // We need to create the tag dir. - $fp = popen($makeCommand, "r"); - $out = ''; - while ($line = fgets($fp, 1024)) { - $out .= rtrim($line)."\n"; - } - pclose($fp); - $this->output .= "$out\n"; - } - } - - $command = 'svn'; - if (isset($options['quiet'])) { - $command .= ' -q'; - } - - $command .= ' copy --parents '; - - $dir = dirname($packageFile); - $dir = substr($dir, strrpos($dir, DIRECTORY_SEPARATOR) + 1); - $files = array_keys($info->getFilelist()); - if (!in_array(basename($packageFile), $files)) { - $files[] = basename($packageFile); - } - - array_shift($params); - if (count($params)) { - // add in additional files to be tagged (package files and such) - $files = array_merge($files, $params); - } - - $commands = array(); - foreach ($files as $file) { - if (!file_exists($file)) { - $file = $dir . DIRECTORY_SEPARATOR . $file; - } - $commands[] = $command . ' ' . escapeshellarg($file) . ' ' . - escapeshellarg($releaseTag . DIRECTORY_SEPARATOR . $file); - } - - $this->output .= implode("\n", $commands) . "\n"; - if (empty($options['dry-run'])) { - foreach ($commands as $command) { - $fp = popen($command, "r"); - while ($line = fgets($fp, 1024)) { - $this->output .= rtrim($line)."\n"; - } - pclose($fp); - } - } - - $command = 'svn ci -m "Tagging the ' . $version . ' release" ' . $releaseTag . "\n"; - $this->output .= "+ $command\n"; - if (empty($options['dry-run'])) { - $fp = popen($command, "r"); - while ($line = fgets($fp, 1024)) { - $this->output .= rtrim($line)."\n"; - } - pclose($fp); - } - - $this->ui->outputData($this->output, $_cmd); - return true; - } - - function _svnFindPath($file) - { - $xml = ''; - $command = "svn info --xml $file"; - $fp = popen($command, "r"); - while ($line = fgets($fp, 1024)) { - $xml .= rtrim($line)."\n"; - } - pclose($fp); - $url_tag = strpos($xml, ''); - $url = substr($xml, $url_tag + 5, strpos($xml, '', $url_tag + 5) - ($url_tag + 5)); - - $path = array(); - $path['from'] = substr($url, 0, strrpos($url, '/')); - $path['base'] = substr($path['from'], 0, strrpos($path['from'], '/') + 1); - - // Figure out the local paths - see http://pear.php.net/bugs/17463 - $pos = strpos($file, DIRECTORY_SEPARATOR . 'trunk' . DIRECTORY_SEPARATOR); - if ($pos === false) { - $pos = strpos($file, DIRECTORY_SEPARATOR . 'branches' . DIRECTORY_SEPARATOR); - } - $path['local']['base'] = substr($file, 0, $pos + 1); - - return $path; - } - - function _svnRemoveTag($version, $package, $tag, $packageFile, $options) - { - $command = 'svn'; - - if (isset($options['quiet'])) { - $command .= ' -q'; - } - - $command .= ' remove'; - $command .= ' -m "Removing tag for the ' . $version . ' release."'; - - $path = $this->_svnFindPath($packageFile); - $command .= ' ' . $path['base'] . 'tags/' . $tag; - - - if ($this->config->get('verbose') > 1) { - $this->output .= "+ $command\n"; - } - - $this->output .= "+ $command\n"; - if (empty($options['dry-run'])) { - $fp = popen($command, "r"); - while ($line = fgets($fp, 1024)) { - $this->output .= rtrim($line)."\n"; - } - pclose($fp); - } - - $this->ui->outputData($this->output, $command); - return true; - } - - function doCvsTag($command, $options, $params) - { - $this->output = ''; - $_cmd = $command; - if (count($params) < 1) { - $help = $this->getHelp($command); - return $this->raiseError("$command: missing parameter: $help[0]"); - } - - $packageFile = realpath($params[0]); - $obj = &$this->getPackageFile($this->config, $this->_debug); - $info = $obj->fromAnyFile($packageFile, PEAR_VALIDATE_NORMAL); - if (PEAR::isError($info)) { - return $this->raiseError($info); - } - - $err = $warn = array(); - if (!$info->validate()) { - foreach ($info->getValidationWarnings() as $error) { - if ($error['level'] == 'warning') { - $warn[] = $error['message']; - } else { - $err[] = $error['message']; - } - } - } - - if (!$this->_displayValidationResults($err, $warn, true)) { - $this->ui->outputData($this->output, $command); - return $this->raiseError('CVS tag failed'); - } - - $version = $info->getVersion(); - $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $version); - $cvstag = "RELEASE_$cvsversion"; - $files = array_keys($info->getFilelist()); - $command = 'cvs'; - if (isset($options['quiet'])) { - $command .= ' -q'; - } - - if (isset($options['reallyquiet'])) { - $command .= ' -Q'; - } - - $command .= ' tag'; - if (isset($options['slide'])) { - $command .= ' -F'; - } - - if (isset($options['delete'])) { - $command .= ' -d'; - } - - $command .= ' ' . $cvstag . ' ' . escapeshellarg($params[0]); - array_shift($params); - if (count($params)) { - // add in additional files to be tagged - $files = array_merge($files, $params); - } - - $dir = dirname($packageFile); - $dir = substr($dir, strrpos($dir, '/') + 1); - foreach ($files as $file) { - if (!file_exists($file)) { - $file = $dir . DIRECTORY_SEPARATOR . $file; - } - $command .= ' ' . escapeshellarg($file); - } - - if ($this->config->get('verbose') > 1) { - $this->output .= "+ $command\n"; - } - - $this->output .= "+ $command\n"; - if (empty($options['dry-run'])) { - $fp = popen($command, "r"); - while ($line = fgets($fp, 1024)) { - $this->output .= rtrim($line)."\n"; - } - pclose($fp); - } - - $this->ui->outputData($this->output, $_cmd); - return true; - } - - function doCvsDiff($command, $options, $params) - { - $this->output = ''; - if (sizeof($params) < 1) { - $help = $this->getHelp($command); - return $this->raiseError("$command: missing parameter: $help[0]"); - } - - $file = realpath($params[0]); - $obj = &$this->getPackageFile($this->config, $this->_debug); - $info = $obj->fromAnyFile($file, PEAR_VALIDATE_NORMAL); - if (PEAR::isError($info)) { - return $this->raiseError($info); - } - - $err = $warn = array(); - if (!$info->validate()) { - foreach ($info->getValidationWarnings() as $error) { - if ($error['level'] == 'warning') { - $warn[] = $error['message']; - } else { - $err[] = $error['message']; - } - } - } - - if (!$this->_displayValidationResults($err, $warn, true)) { - $this->ui->outputData($this->output, $command); - return $this->raiseError('CVS diff failed'); - } - - $info1 = $info->getFilelist(); - $files = $info1; - $cmd = "cvs"; - if (isset($options['quiet'])) { - $cmd .= ' -q'; - unset($options['quiet']); - } - - if (isset($options['reallyquiet'])) { - $cmd .= ' -Q'; - unset($options['reallyquiet']); - } - - if (isset($options['release'])) { - $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $options['release']); - $cvstag = "RELEASE_$cvsversion"; - $options['revision'] = $cvstag; - unset($options['release']); - } - - $execute = true; - if (isset($options['dry-run'])) { - $execute = false; - unset($options['dry-run']); - } - - $cmd .= ' diff'; - // the rest of the options are passed right on to "cvs diff" - foreach ($options as $option => $optarg) { - $arg = $short = false; - if (isset($this->commands[$command]['options'][$option])) { - $arg = $this->commands[$command]['options'][$option]['arg']; - $short = $this->commands[$command]['options'][$option]['shortopt']; - } - $cmd .= $short ? " -$short" : " --$option"; - if ($arg && $optarg) { - $cmd .= ($short ? '' : '=') . escapeshellarg($optarg); - } - } - - foreach ($files as $file) { - $cmd .= ' ' . escapeshellarg($file['name']); - } - - if ($this->config->get('verbose') > 1) { - $this->output .= "+ $cmd\n"; - } - - if ($execute) { - $fp = popen($cmd, "r"); - while ($line = fgets($fp, 1024)) { - $this->output .= rtrim($line)."\n"; - } - pclose($fp); - } - - $this->ui->outputData($this->output, $command); - return true; - } - - function doPackageDependencies($command, $options, $params) - { - // $params[0] -> the PEAR package to list its information - if (count($params) !== 1) { - return $this->raiseError("bad parameter(s), try \"help $command\""); - } - - $obj = &$this->getPackageFile($this->config, $this->_debug); - if (is_file($params[0]) || strpos($params[0], '.xml') > 0) { - $info = $obj->fromAnyFile($params[0], PEAR_VALIDATE_NORMAL); - } else { - $reg = $this->config->getRegistry(); - $info = $obj->fromArray($reg->packageInfo($params[0])); - } - - if (PEAR::isError($info)) { - return $this->raiseError($info); - } - - $deps = $info->getDeps(); - if (is_array($deps)) { - if ($info->getPackagexmlVersion() == '1.0') { - $data = array( - 'caption' => 'Dependencies for pear/' . $info->getPackage(), - 'border' => true, - 'headline' => array("Required?", "Type", "Name", "Relation", "Version"), - ); - - foreach ($deps as $d) { - if (isset($d['optional'])) { - if ($d['optional'] == 'yes') { - $req = 'No'; - } else { - $req = 'Yes'; - } - } else { - $req = 'Yes'; - } - - if (isset($this->_deps_rel_trans[$d['rel']])) { - $rel = $this->_deps_rel_trans[$d['rel']]; - } else { - $rel = $d['rel']; - } - - if (isset($this->_deps_type_trans[$d['type']])) { - $type = ucfirst($this->_deps_type_trans[$d['type']]); - } else { - $type = $d['type']; - } - - if (isset($d['name'])) { - $name = $d['name']; - } else { - $name = ''; - } - - if (isset($d['version'])) { - $version = $d['version']; - } else { - $version = ''; - } - - $data['data'][] = array($req, $type, $name, $rel, $version); - } - } else { // package.xml 2.0 dependencies display - require_once 'PEAR/Dependency2.php'; - $deps = $info->getDependencies(); - $reg = &$this->config->getRegistry(); - if (is_array($deps)) { - $d = new PEAR_Dependency2($this->config, array(), ''); - $data = array( - 'caption' => 'Dependencies for ' . $info->getPackage(), - 'border' => true, - 'headline' => array("Required?", "Type", "Name", 'Versioning', 'Group'), - ); - foreach ($deps as $type => $subd) { - $req = ($type == 'required') ? 'Yes' : 'No'; - if ($type == 'group') { - $group = $subd['attribs']['name']; - } else { - $group = ''; - } - - if (!isset($subd[0])) { - $subd = array($subd); - } - - foreach ($subd as $groupa) { - foreach ($groupa as $deptype => $depinfo) { - if ($deptype == 'attribs') { - continue; - } - - if ($deptype == 'pearinstaller') { - $deptype = 'pear Installer'; - } - - if (!isset($depinfo[0])) { - $depinfo = array($depinfo); - } - - foreach ($depinfo as $inf) { - $name = ''; - if (isset($inf['channel'])) { - $alias = $reg->channelAlias($inf['channel']); - if (!$alias) { - $alias = '(channel?) ' .$inf['channel']; - } - $name = $alias . '/'; - - } - if (isset($inf['name'])) { - $name .= $inf['name']; - } elseif (isset($inf['pattern'])) { - $name .= $inf['pattern']; - } else { - $name .= ''; - } - - if (isset($inf['uri'])) { - $name .= ' [' . $inf['uri'] . ']'; - } - - if (isset($inf['conflicts'])) { - $ver = 'conflicts'; - } else { - $ver = $d->_getExtraString($inf); - } - - $data['data'][] = array($req, ucfirst($deptype), $name, - $ver, $group); - } - } - } - } - } - } - - $this->ui->outputData($data, $command); - return true; - } - - // Fallback - $this->ui->outputData("This package does not have any dependencies.", $command); - } - - function doSign($command, $options, $params) - { - // should move most of this code into PEAR_Packager - // so it'll be easy to implement "pear package --sign" - if (count($params) !== 1) { - return $this->raiseError("bad parameter(s), try \"help $command\""); - } - - require_once 'System.php'; - require_once 'Archive/Tar.php'; - - if (!file_exists($params[0])) { - return $this->raiseError("file does not exist: $params[0]"); - } - - $obj = $this->getPackageFile($this->config, $this->_debug); - $info = $obj->fromTgzFile($params[0], PEAR_VALIDATE_NORMAL); - if (PEAR::isError($info)) { - return $this->raiseError($info); - } - - $tar = new Archive_Tar($params[0]); - - $tmpdir = $this->config->get('temp_dir'); - $tmpdir = System::mktemp(' -t "' . $tmpdir . '" -d pearsign'); - if (!$tar->extractList('package2.xml package.xml package.sig', $tmpdir)) { - return $this->raiseError("failed to extract tar file"); - } - - if (file_exists("$tmpdir/package.sig")) { - return $this->raiseError("package already signed"); - } - - $packagexml = 'package.xml'; - if (file_exists("$tmpdir/package2.xml")) { - $packagexml = 'package2.xml'; - } - - if (file_exists("$tmpdir/package.sig")) { - unlink("$tmpdir/package.sig"); - } - - if (!file_exists("$tmpdir/$packagexml")) { - return $this->raiseError("Extracted file $tmpdir/$packagexml not found."); - } - - $input = $this->ui->userDialog($command, - array('GnuPG Passphrase'), - array('password')); - if (!isset($input[0])) { - //use empty passphrase - $input[0] = ''; - } - - $devnull = (isset($options['verbose'])) ? '' : ' 2>/dev/null'; - $gpg = popen("gpg --batch --passphrase-fd 0 --armor --detach-sign --output $tmpdir/package.sig $tmpdir/$packagexml" . $devnull, "w"); - if (!$gpg) { - return $this->raiseError("gpg command failed"); - } - - fwrite($gpg, "$input[0]\n"); - if (pclose($gpg) || !file_exists("$tmpdir/package.sig")) { - return $this->raiseError("gpg sign failed"); - } - - if (!$tar->addModify("$tmpdir/package.sig", '', $tmpdir)) { - return $this->raiseError('failed adding signature to file'); - } - - $this->ui->outputData("Package signed.", $command); - return true; - } - - /** - * For unit testing purposes - */ - function &getInstaller(&$ui) - { - if (!class_exists('PEAR_Installer')) { - require_once 'PEAR/Installer.php'; - } - $a = &new PEAR_Installer($ui); - return $a; - } - - /** - * For unit testing purposes - */ - function &getCommandPackaging(&$ui, &$config) - { - if (!class_exists('PEAR_Command_Packaging')) { - if ($fp = @fopen('PEAR/Command/Packaging.php', 'r', true)) { - fclose($fp); - include_once 'PEAR/Command/Packaging.php'; - } - } - - if (class_exists('PEAR_Command_Packaging')) { - $a = &new PEAR_Command_Packaging($ui, $config); - } else { - $a = null; - } - - return $a; - } - - function doMakeRPM($command, $options, $params) - { - - // Check to see if PEAR_Command_Packaging is installed, and - // transparently switch to use the "make-rpm-spec" command from it - // instead, if it does. Otherwise, continue to use the old version - // of "makerpm" supplied with this package (PEAR). - $packaging_cmd = $this->getCommandPackaging($this->ui, $this->config); - if ($packaging_cmd !== null) { - $this->ui->outputData('PEAR_Command_Packaging is installed; using '. - 'newer "make-rpm-spec" command instead'); - return $packaging_cmd->run('make-rpm-spec', $options, $params); - } - - $this->ui->outputData('WARNING: "pear makerpm" is no longer available; an '. - 'improved version is available via "pear make-rpm-spec", which '. - 'is available by installing PEAR_Command_Packaging'); - return true; - } - - function doConvert($command, $options, $params) - { - $packagexml = isset($params[0]) ? $params[0] : 'package.xml'; - $newpackagexml = isset($params[1]) ? $params[1] : dirname($packagexml) . - DIRECTORY_SEPARATOR . 'package2.xml'; - $pkg = &$this->getPackageFile($this->config, $this->_debug); - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $pf = $pkg->fromPackageFile($packagexml, PEAR_VALIDATE_NORMAL); - PEAR::staticPopErrorHandling(); - if (PEAR::isError($pf)) { - if (is_array($pf->getUserInfo())) { - foreach ($pf->getUserInfo() as $warning) { - $this->ui->outputData($warning['message']); - } - } - return $this->raiseError($pf); - } - - if (is_a($pf, 'PEAR_PackageFile_v2')) { - $this->ui->outputData($packagexml . ' is already a package.xml version 2.0'); - return true; - } - - $gen = &$pf->getDefaultGenerator(); - $newpf = &$gen->toV2(); - $newpf->setPackagefile($newpackagexml); - $gen = &$newpf->getDefaultGenerator(); - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $state = (isset($options['flat']) ? PEAR_VALIDATE_PACKAGING : PEAR_VALIDATE_NORMAL); - $saved = $gen->toPackageFile(dirname($newpackagexml), $state, basename($newpackagexml)); - PEAR::staticPopErrorHandling(); - if (PEAR::isError($saved)) { - if (is_array($saved->getUserInfo())) { - foreach ($saved->getUserInfo() as $warning) { - $this->ui->outputData($warning['message']); - } - } - - $this->ui->outputData($saved->getMessage()); - return true; - } - - $this->ui->outputData('Wrote new version 2.0 package.xml to "' . $saved . '"'); - return true; - } -} \ No newline at end of file diff --git a/pear/PEAR/Command/Package.xml b/pear/PEAR/Command/Package.xml deleted file mode 100644 index d1aff9d..0000000 --- a/pear/PEAR/Command/Package.xml +++ /dev/null @@ -1,237 +0,0 @@ - - - Build Package - doPackage - p - - - Z - Do not gzip the package file - - - n - Print the name of the packaged file. - - - [descfile] [descfile2] -Creates a PEAR package from its description file (usually called -package.xml). If a second packagefile is passed in, then -the packager will check to make sure that one is a package.xml -version 1.0, and the other is a package.xml version 2.0. The -package.xml version 1.0 will be saved as "package.xml" in the archive, -and the other as "package2.xml" in the archive" - - - - Validate Package Consistency - doPackageValidate - pv - - - - - - Run a "cvs diff" for all files in a package - doCvsDiff - cd - - - q - Be quiet - - - Q - Be really quiet - - - D - Diff against revision of DATE - DATE - - - R - Diff against tag for package release REL - REL - - - r - Diff against revision REV - REV - - - c - Generate context diff - - - u - Generate unified diff - - - i - Ignore case, consider upper- and lower-case letters equivalent - - - b - Ignore changes in amount of white space - - - B - Ignore changes that insert or delete blank lines - - - - Report only whether the files differ, no details - - - n - Don't do anything, just pretend - - - <package.xml> -Compares all the files in a package. Without any options, this -command will compare the current code with the last checked-in code. -Using the -r or -R option you may compare the current code with that -of a specific release. - - - - Set SVN Release Tag - doSvnTag - sv - - - q - Be quiet - - - F - Move (slide) tag if it exists - - - d - Remove tag - - - n - Don't do anything, just pretend - - - <package.xml> [files...] - Sets a SVN tag on all files in a package. Use this command after you have - packaged a distribution tarball with the "package" command to tag what - revisions of what files were in that release. If need to fix something - after running svntag once, but before the tarball is released to the public, - use the "slide" option to move the release tag. - - to include files (such as a second package.xml, or tests not included in the - release), pass them as additional parameters. - - - - Set CVS Release Tag - doCvsTag - ct - - - q - Be quiet - - - Q - Be really quiet - - - F - Move (slide) tag if it exists - - - d - Remove tag - - - n - Don't do anything, just pretend - - - <package.xml> [files...] -Sets a CVS tag on all files in a package. Use this command after you have -packaged a distribution tarball with the "package" command to tag what -revisions of what files were in that release. If need to fix something -after running cvstag once, but before the tarball is released to the public, -use the "slide" option to move the release tag. - -to include files (such as a second package.xml, or tests not included in the -release), pass them as additional parameters. - - - - Show package dependencies - doPackageDependencies - pd - - <package-file> or <package.xml> or <install-package-name> -List all dependencies the package has. -Can take a tgz / tar file, package.xml or a package name of an installed package. - - - Sign a package distribution file - doSign - si - - - v - Display GnuPG output - - - <package-file> -Signs a package distribution (.tar or .tgz) file with GnuPG. - - - Builds an RPM spec file from a PEAR package - doMakeRPM - rpm - - - t - Use FILE as RPM spec file template - FILE - - - p - Use FORMAT as format string for RPM package name, %s is replaced -by the PEAR package name, defaults to "PEAR::%s". - FORMAT - - - <package-file> - -Creates an RPM .spec file for wrapping a PEAR package inside an RPM -package. Intended to be used from the SPECS directory, with the PEAR -package tarball in the SOURCES directory: - -$ pear makerpm ../SOURCES/Net_Socket-1.0.tgz -Wrote RPM spec file PEAR::Net_Geo-1.0.spec -$ rpm -bb PEAR::Net_Socket-1.0.spec -... -Wrote: /usr/src/redhat/RPMS/i386/PEAR::Net_Socket-1.0-1.i386.rpm - - - - Convert a package.xml 1.0 to package.xml 2.0 format - doConvert - c2 - - - f - do not beautify the filelist. - - - [descfile] [descfile2] -Converts a package.xml in 1.0 format into a package.xml -in 2.0 format. The new file will be named package2.xml by default, -and package.xml will be used as the old file by default. -This is not the most intelligent conversion, and should only be -used for automated conversion or learning the format. - - - \ No newline at end of file diff --git a/pear/PEAR/Command/Pickle.php b/pear/PEAR/Command/Pickle.php deleted file mode 100644 index 7fe8da7..0000000 --- a/pear/PEAR/Command/Pickle.php +++ /dev/null @@ -1,421 +0,0 @@ - - * @copyright 2005-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.1 - */ - -/** - * base class - */ -require_once 'PEAR/Command/Common.php'; - -/** - * PEAR commands for login/logout - * - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 2005-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.1 - */ - -class PEAR_Command_Pickle extends PEAR_Command_Common -{ - var $commands = array( - 'pickle' => array( - 'summary' => 'Build PECL Package', - 'function' => 'doPackage', - 'shortcut' => 'pi', - 'options' => array( - 'nocompress' => array( - 'shortopt' => 'Z', - 'doc' => 'Do not gzip the package file' - ), - 'showname' => array( - 'shortopt' => 'n', - 'doc' => 'Print the name of the packaged file.', - ), - ), - 'doc' => '[descfile] -Creates a PECL package from its package2.xml file. - -An automatic conversion will be made to a package.xml 1.0 and written out to -disk in the current directory as "package.xml". Note that -only simple package.xml 2.0 will be converted. package.xml 2.0 with: - - - dependency types other than required/optional PECL package/ext/php/pearinstaller - - more than one extsrcrelease or zendextsrcrelease - - zendextbinrelease, extbinrelease, phprelease, or bundle release type - - dependency groups - - ignore tags in release filelist - - tasks other than replace - - custom roles - -will cause pickle to fail, and output an error message. If your package2.xml -uses any of these features, you are best off using PEAR_PackageFileManager to -generate both package.xml. -' - ), - ); - - /** - * PEAR_Command_Package constructor. - * - * @access public - */ - function PEAR_Command_Pickle(&$ui, &$config) - { - parent::PEAR_Command_Common($ui, $config); - } - - /** - * For unit-testing ease - * - * @return PEAR_Packager - */ - function &getPackager() - { - if (!class_exists('PEAR_Packager')) { - require_once 'PEAR/Packager.php'; - } - - $a = &new PEAR_Packager; - return $a; - } - - /** - * For unit-testing ease - * - * @param PEAR_Config $config - * @param bool $debug - * @param string|null $tmpdir - * @return PEAR_PackageFile - */ - function &getPackageFile($config, $debug = false) - { - if (!class_exists('PEAR_Common')) { - require_once 'PEAR/Common.php'; - } - - if (!class_exists('PEAR_PackageFile')) { - require_once 'PEAR/PackageFile.php'; - } - - $a = &new PEAR_PackageFile($config, $debug); - $common = new PEAR_Common; - $common->ui = $this->ui; - $a->setLogger($common); - return $a; - } - - function doPackage($command, $options, $params) - { - $this->output = ''; - $pkginfofile = isset($params[0]) ? $params[0] : 'package2.xml'; - $packager = &$this->getPackager(); - if (PEAR::isError($err = $this->_convertPackage($pkginfofile))) { - return $err; - } - - $compress = empty($options['nocompress']) ? true : false; - $result = $packager->package($pkginfofile, $compress, 'package.xml'); - if (PEAR::isError($result)) { - return $this->raiseError($result); - } - - // Don't want output, only the package file name just created - if (isset($options['showname'])) { - $this->ui->outputData($result, $command); - } - - return true; - } - - function _convertPackage($packagexml) - { - $pkg = &$this->getPackageFile($this->config); - $pf2 = &$pkg->fromPackageFile($packagexml, PEAR_VALIDATE_NORMAL); - if (!is_a($pf2, 'PEAR_PackageFile_v2')) { - return $this->raiseError('Cannot process "' . - $packagexml . '", is not a package.xml 2.0'); - } - - require_once 'PEAR/PackageFile/v1.php'; - $pf = new PEAR_PackageFile_v1; - $pf->setConfig($this->config); - if ($pf2->getPackageType() != 'extsrc' && $pf2->getPackageType() != 'zendextsrc') { - return $this->raiseError('Cannot safely convert "' . $packagexml . - '", is not an extension source package. Using a PEAR_PackageFileManager-based ' . - 'script is an option'); - } - - if (is_array($pf2->getUsesRole())) { - return $this->raiseError('Cannot safely convert "' . $packagexml . - '", contains custom roles. Using a PEAR_PackageFileManager-based script or ' . - 'the convert command is an option'); - } - - if (is_array($pf2->getUsesTask())) { - return $this->raiseError('Cannot safely convert "' . $packagexml . - '", contains custom tasks. Using a PEAR_PackageFileManager-based script or ' . - 'the convert command is an option'); - } - - $deps = $pf2->getDependencies(); - if (isset($deps['group'])) { - return $this->raiseError('Cannot safely convert "' . $packagexml . - '", contains dependency groups. Using a PEAR_PackageFileManager-based script ' . - 'or the convert command is an option'); - } - - if (isset($deps['required']['subpackage']) || - isset($deps['optional']['subpackage'])) { - return $this->raiseError('Cannot safely convert "' . $packagexml . - '", contains subpackage dependencies. Using a PEAR_PackageFileManager-based '. - 'script is an option'); - } - - if (isset($deps['required']['os'])) { - return $this->raiseError('Cannot safely convert "' . $packagexml . - '", contains os dependencies. Using a PEAR_PackageFileManager-based '. - 'script is an option'); - } - - if (isset($deps['required']['arch'])) { - return $this->raiseError('Cannot safely convert "' . $packagexml . - '", contains arch dependencies. Using a PEAR_PackageFileManager-based '. - 'script is an option'); - } - - $pf->setPackage($pf2->getPackage()); - $pf->setSummary($pf2->getSummary()); - $pf->setDescription($pf2->getDescription()); - foreach ($pf2->getMaintainers() as $maintainer) { - $pf->addMaintainer($maintainer['role'], $maintainer['handle'], - $maintainer['name'], $maintainer['email']); - } - - $pf->setVersion($pf2->getVersion()); - $pf->setDate($pf2->getDate()); - $pf->setLicense($pf2->getLicense()); - $pf->setState($pf2->getState()); - $pf->setNotes($pf2->getNotes()); - $pf->addPhpDep($deps['required']['php']['min'], 'ge'); - if (isset($deps['required']['php']['max'])) { - $pf->addPhpDep($deps['required']['php']['max'], 'le'); - } - - if (isset($deps['required']['package'])) { - if (!isset($deps['required']['package'][0])) { - $deps['required']['package'] = array($deps['required']['package']); - } - - foreach ($deps['required']['package'] as $dep) { - if (!isset($dep['channel'])) { - return $this->raiseError('Cannot safely convert "' . $packagexml . '"' . - ' contains uri-based dependency on a package. Using a ' . - 'PEAR_PackageFileManager-based script is an option'); - } - - if ($dep['channel'] != 'pear.php.net' - && $dep['channel'] != 'pecl.php.net' - && $dep['channel'] != 'doc.php.net') { - return $this->raiseError('Cannot safely convert "' . $packagexml . '"' . - ' contains dependency on a non-standard channel package. Using a ' . - 'PEAR_PackageFileManager-based script is an option'); - } - - if (isset($dep['conflicts'])) { - return $this->raiseError('Cannot safely convert "' . $packagexml . '"' . - ' contains conflicts dependency. Using a ' . - 'PEAR_PackageFileManager-based script is an option'); - } - - if (isset($dep['exclude'])) { - $this->ui->outputData('WARNING: exclude tags are ignored in conversion'); - } - - if (isset($dep['min'])) { - $pf->addPackageDep($dep['name'], $dep['min'], 'ge'); - } - - if (isset($dep['max'])) { - $pf->addPackageDep($dep['name'], $dep['max'], 'le'); - } - } - } - - if (isset($deps['required']['extension'])) { - if (!isset($deps['required']['extension'][0])) { - $deps['required']['extension'] = array($deps['required']['extension']); - } - - foreach ($deps['required']['extension'] as $dep) { - if (isset($dep['conflicts'])) { - return $this->raiseError('Cannot safely convert "' . $packagexml . '"' . - ' contains conflicts dependency. Using a ' . - 'PEAR_PackageFileManager-based script is an option'); - } - - if (isset($dep['exclude'])) { - $this->ui->outputData('WARNING: exclude tags are ignored in conversion'); - } - - if (isset($dep['min'])) { - $pf->addExtensionDep($dep['name'], $dep['min'], 'ge'); - } - - if (isset($dep['max'])) { - $pf->addExtensionDep($dep['name'], $dep['max'], 'le'); - } - } - } - - if (isset($deps['optional']['package'])) { - if (!isset($deps['optional']['package'][0])) { - $deps['optional']['package'] = array($deps['optional']['package']); - } - - foreach ($deps['optional']['package'] as $dep) { - if (!isset($dep['channel'])) { - return $this->raiseError('Cannot safely convert "' . $packagexml . '"' . - ' contains uri-based dependency on a package. Using a ' . - 'PEAR_PackageFileManager-based script is an option'); - } - - if ($dep['channel'] != 'pear.php.net' - && $dep['channel'] != 'pecl.php.net' - && $dep['channel'] != 'doc.php.net') { - return $this->raiseError('Cannot safely convert "' . $packagexml . '"' . - ' contains dependency on a non-standard channel package. Using a ' . - 'PEAR_PackageFileManager-based script is an option'); - } - - if (isset($dep['exclude'])) { - $this->ui->outputData('WARNING: exclude tags are ignored in conversion'); - } - - if (isset($dep['min'])) { - $pf->addPackageDep($dep['name'], $dep['min'], 'ge', 'yes'); - } - - if (isset($dep['max'])) { - $pf->addPackageDep($dep['name'], $dep['max'], 'le', 'yes'); - } - } - } - - if (isset($deps['optional']['extension'])) { - if (!isset($deps['optional']['extension'][0])) { - $deps['optional']['extension'] = array($deps['optional']['extension']); - } - - foreach ($deps['optional']['extension'] as $dep) { - if (isset($dep['exclude'])) { - $this->ui->outputData('WARNING: exclude tags are ignored in conversion'); - } - - if (isset($dep['min'])) { - $pf->addExtensionDep($dep['name'], $dep['min'], 'ge', 'yes'); - } - - if (isset($dep['max'])) { - $pf->addExtensionDep($dep['name'], $dep['max'], 'le', 'yes'); - } - } - } - - $contents = $pf2->getContents(); - $release = $pf2->getReleases(); - if (isset($releases[0])) { - return $this->raiseError('Cannot safely process "' . $packagexml . '" contains ' - . 'multiple extsrcrelease/zendextsrcrelease tags. Using a PEAR_PackageFileManager-based script ' . - 'or the convert command is an option'); - } - - if ($configoptions = $pf2->getConfigureOptions()) { - foreach ($configoptions as $option) { - $default = isset($option['default']) ? $option['default'] : false; - $pf->addConfigureOption($option['name'], $option['prompt'], $default); - } - } - - if (isset($release['filelist']['ignore'])) { - return $this->raiseError('Cannot safely process "' . $packagexml . '" contains ' - . 'ignore tags. Using a PEAR_PackageFileManager-based script or the convert' . - ' command is an option'); - } - - if (isset($release['filelist']['install']) && - !isset($release['filelist']['install'][0])) { - $release['filelist']['install'] = array($release['filelist']['install']); - } - - if (isset($contents['dir']['attribs']['baseinstalldir'])) { - $baseinstalldir = $contents['dir']['attribs']['baseinstalldir']; - } else { - $baseinstalldir = false; - } - - if (!isset($contents['dir']['file'][0])) { - $contents['dir']['file'] = array($contents['dir']['file']); - } - - foreach ($contents['dir']['file'] as $file) { - if ($baseinstalldir && !isset($file['attribs']['baseinstalldir'])) { - $file['attribs']['baseinstalldir'] = $baseinstalldir; - } - - $processFile = $file; - unset($processFile['attribs']); - if (count($processFile)) { - foreach ($processFile as $name => $task) { - if ($name != $pf2->getTasksNs() . ':replace') { - return $this->raiseError('Cannot safely process "' . $packagexml . - '" contains tasks other than replace. Using a ' . - 'PEAR_PackageFileManager-based script is an option.'); - } - $file['attribs']['replace'][] = $task; - } - } - - if (!in_array($file['attribs']['role'], PEAR_Common::getFileRoles())) { - return $this->raiseError('Cannot safely convert "' . $packagexml . - '", contains custom roles. Using a PEAR_PackageFileManager-based script ' . - 'or the convert command is an option'); - } - - if (isset($release['filelist']['install'])) { - foreach ($release['filelist']['install'] as $installas) { - if ($installas['attribs']['name'] == $file['attribs']['name']) { - $file['attribs']['install-as'] = $installas['attribs']['as']; - } - } - } - - $pf->addFile('/', $file['attribs']['name'], $file['attribs']); - } - - if ($pf2->getChangeLog()) { - $this->ui->outputData('WARNING: changelog is not translated to package.xml ' . - '1.0, use PEAR_PackageFileManager-based script if you need changelog-' . - 'translation for package.xml 1.0'); - } - - $gen = &$pf->getDefaultGenerator(); - $gen->toPackageFile('.'); - } -} \ No newline at end of file diff --git a/pear/PEAR/Command/Pickle.xml b/pear/PEAR/Command/Pickle.xml deleted file mode 100644 index 721ecea..0000000 --- a/pear/PEAR/Command/Pickle.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - Build PECL Package - doPackage - pi - - - Z - Do not gzip the package file - - - n - Print the name of the packaged file. - - - [descfile] -Creates a PECL package from its package2.xml file. - -An automatic conversion will be made to a package.xml 1.0 and written out to -disk in the current directory as "package.xml". Note that -only simple package.xml 2.0 will be converted. package.xml 2.0 with: - - - dependency types other than required/optional PECL package/ext/php/pearinstaller - - more than one extsrcrelease or zendextsrcrelease - - zendextbinrelease, extbinrelease, phprelease, or bundle release type - - dependency groups - - ignore tags in release filelist - - tasks other than replace - - custom roles - -will cause pickle to fail, and output an error message. If your package2.xml -uses any of these features, you are best off using PEAR_PackageFileManager to -generate both package.xml. - - - \ No newline at end of file diff --git a/pear/PEAR/Command/Registry.php b/pear/PEAR/Command/Registry.php deleted file mode 100644 index 4110123..0000000 --- a/pear/PEAR/Command/Registry.php +++ /dev/null @@ -1,1145 +0,0 @@ - - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 0.1 - */ - -/** - * base class - */ -require_once 'PEAR/Command/Common.php'; - -/** - * PEAR commands for registry manipulation - * - * @category pear - * @package PEAR - * @author Stig Bakken - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 0.1 - */ -class PEAR_Command_Registry extends PEAR_Command_Common -{ - var $commands = array( - 'list' => array( - 'summary' => 'List Installed Packages In The Default Channel', - 'function' => 'doList', - 'shortcut' => 'l', - 'options' => array( - 'channel' => array( - 'shortopt' => 'c', - 'doc' => 'list installed packages from this channel', - 'arg' => 'CHAN', - ), - 'allchannels' => array( - 'shortopt' => 'a', - 'doc' => 'list installed packages from all channels', - ), - 'channelinfo' => array( - 'shortopt' => 'i', - 'doc' => 'output fully channel-aware data, even on failure', - ), - ), - 'doc' => ' -If invoked without parameters, this command lists the PEAR packages -installed in your php_dir ({config php_dir}). With a parameter, it -lists the files in a package. -', - ), - 'list-files' => array( - 'summary' => 'List Files In Installed Package', - 'function' => 'doFileList', - 'shortcut' => 'fl', - 'options' => array(), - 'doc' => ' -List the files in an installed package. -' - ), - 'shell-test' => array( - 'summary' => 'Shell Script Test', - 'function' => 'doShellTest', - 'shortcut' => 'st', - 'options' => array(), - 'doc' => ' [[relation] version] -Tests if a package is installed in the system. Will exit(1) if it is not. - The version comparison operator. One of: - <, lt, <=, le, >, gt, >=, ge, ==, =, eq, !=, <>, ne - The version to compare with -'), - 'info' => array( - 'summary' => 'Display information about a package', - 'function' => 'doInfo', - 'shortcut' => 'in', - 'options' => array(), - 'doc' => ' -Displays information about a package. The package argument may be a -local package file, an URL to a package file, or the name of an -installed package.' - ) - ); - - /** - * PEAR_Command_Registry constructor. - * - * @access public - */ - function PEAR_Command_Registry(&$ui, &$config) - { - parent::PEAR_Command_Common($ui, $config); - } - - function _sortinfo($a, $b) - { - $apackage = isset($a['package']) ? $a['package'] : $a['name']; - $bpackage = isset($b['package']) ? $b['package'] : $b['name']; - return strcmp($apackage, $bpackage); - } - - function doList($command, $options, $params) - { - $reg = &$this->config->getRegistry(); - $channelinfo = isset($options['channelinfo']); - if (isset($options['allchannels']) && !$channelinfo) { - return $this->doListAll($command, array(), $params); - } - - if (isset($options['allchannels']) && $channelinfo) { - // allchannels with $channelinfo - unset($options['allchannels']); - $channels = $reg->getChannels(); - $errors = array(); - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - foreach ($channels as $channel) { - $options['channel'] = $channel->getName(); - $ret = $this->doList($command, $options, $params); - - if (PEAR::isError($ret)) { - $errors[] = $ret; - } - } - - PEAR::staticPopErrorHandling(); - if (count($errors)) { - // for now, only give first error - return PEAR::raiseError($errors[0]); - } - - return true; - } - - if (count($params) === 1) { - return $this->doFileList($command, $options, $params); - } - - if (isset($options['channel'])) { - if (!$reg->channelExists($options['channel'])) { - return $this->raiseError('Channel "' . $options['channel'] .'" does not exist'); - } - - $channel = $reg->channelName($options['channel']); - } else { - $channel = $this->config->get('default_channel'); - } - - $installed = $reg->packageInfo(null, null, $channel); - usort($installed, array(&$this, '_sortinfo')); - - $data = array( - 'caption' => 'Installed packages, channel ' . - $channel . ':', - 'border' => true, - 'headline' => array('Package', 'Version', 'State'), - 'channel' => $channel, - ); - if ($channelinfo) { - $data['headline'] = array('Channel', 'Package', 'Version', 'State'); - } - - if (count($installed) && !isset($data['data'])) { - $data['data'] = array(); - } - - foreach ($installed as $package) { - $pobj = $reg->getPackage(isset($package['package']) ? - $package['package'] : $package['name'], $channel); - if ($channelinfo) { - $packageinfo = array($pobj->getChannel(), $pobj->getPackage(), $pobj->getVersion(), - $pobj->getState() ? $pobj->getState() : null); - } else { - $packageinfo = array($pobj->getPackage(), $pobj->getVersion(), - $pobj->getState() ? $pobj->getState() : null); - } - $data['data'][] = $packageinfo; - } - - if (count($installed) === 0) { - if (!$channelinfo) { - $data = '(no packages installed from channel ' . $channel . ')'; - } else { - $data = array( - 'caption' => 'Installed packages, channel ' . - $channel . ':', - 'border' => true, - 'channel' => $channel, - 'data' => array(array('(no packages installed)')), - ); - } - } - - $this->ui->outputData($data, $command); - return true; - } - - function doListAll($command, $options, $params) - { - // This duplicate code is deprecated over - // list --channelinfo, which gives identical - // output for list and list --allchannels. - $reg = &$this->config->getRegistry(); - $installed = $reg->packageInfo(null, null, null); - foreach ($installed as $channel => $packages) { - usort($packages, array($this, '_sortinfo')); - $data = array( - 'caption' => 'Installed packages, channel ' . $channel . ':', - 'border' => true, - 'headline' => array('Package', 'Version', 'State'), - 'channel' => $channel - ); - - foreach ($packages as $package) { - $p = isset($package['package']) ? $package['package'] : $package['name']; - $pobj = $reg->getPackage($p, $channel); - $data['data'][] = array($pobj->getPackage(), $pobj->getVersion(), - $pobj->getState() ? $pobj->getState() : null); - } - - // Adds a blank line after each section - $data['data'][] = array(); - - if (count($packages) === 0) { - $data = array( - 'caption' => 'Installed packages, channel ' . $channel . ':', - 'border' => true, - 'data' => array(array('(no packages installed)'), array()), - 'channel' => $channel - ); - } - $this->ui->outputData($data, $command); - } - return true; - } - - function doFileList($command, $options, $params) - { - if (count($params) !== 1) { - return $this->raiseError('list-files expects 1 parameter'); - } - - $reg = &$this->config->getRegistry(); - $fp = false; - if (!is_dir($params[0]) && (file_exists($params[0]) || $fp = @fopen($params[0], 'r'))) { - if ($fp) { - fclose($fp); - } - - if (!class_exists('PEAR_PackageFile')) { - require_once 'PEAR/PackageFile.php'; - } - - $pkg = &new PEAR_PackageFile($this->config, $this->_debug); - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $info = &$pkg->fromAnyFile($params[0], PEAR_VALIDATE_NORMAL); - PEAR::staticPopErrorHandling(); - $headings = array('Package File', 'Install Path'); - $installed = false; - } else { - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $parsed = $reg->parsePackageName($params[0], $this->config->get('default_channel')); - PEAR::staticPopErrorHandling(); - if (PEAR::isError($parsed)) { - return $this->raiseError($parsed); - } - - $info = &$reg->getPackage($parsed['package'], $parsed['channel']); - $headings = array('Type', 'Install Path'); - $installed = true; - } - - if (PEAR::isError($info)) { - return $this->raiseError($info); - } - - if ($info === null) { - return $this->raiseError("`$params[0]' not installed"); - } - - $list = ($info->getPackagexmlVersion() == '1.0' || $installed) ? - $info->getFilelist() : $info->getContents(); - if ($installed) { - $caption = 'Installed Files For ' . $params[0]; - } else { - $caption = 'Contents of ' . basename($params[0]); - } - - $data = array( - 'caption' => $caption, - 'border' => true, - 'headline' => $headings); - if ($info->getPackagexmlVersion() == '1.0' || $installed) { - foreach ($list as $file => $att) { - if ($installed) { - if (empty($att['installed_as'])) { - continue; - } - $data['data'][] = array($att['role'], $att['installed_as']); - } else { - if (isset($att['baseinstalldir']) && !in_array($att['role'], - array('test', 'data', 'doc'))) { - $dest = $att['baseinstalldir'] . DIRECTORY_SEPARATOR . - $file; - } else { - $dest = $file; - } - switch ($att['role']) { - case 'test': - case 'data': - case 'doc': - $role = $att['role']; - if ($role == 'test') { - $role .= 's'; - } - $dest = $this->config->get($role . '_dir') . DIRECTORY_SEPARATOR . - $info->getPackage() . DIRECTORY_SEPARATOR . $dest; - break; - case 'php': - default: - $dest = $this->config->get('php_dir') . DIRECTORY_SEPARATOR . - $dest; - } - $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR; - $dest = preg_replace(array('!\\\\+!', '!/!', "!$ds2+!"), - array(DIRECTORY_SEPARATOR, - DIRECTORY_SEPARATOR, - DIRECTORY_SEPARATOR), - $dest); - $file = preg_replace('!/+!', '/', $file); - $data['data'][] = array($file, $dest); - } - } - } else { // package.xml 2.0, not installed - if (!isset($list['dir']['file'][0])) { - $list['dir']['file'] = array($list['dir']['file']); - } - - foreach ($list['dir']['file'] as $att) { - $att = $att['attribs']; - $file = $att['name']; - $role = &PEAR_Installer_Role::factory($info, $att['role'], $this->config); - $role->setup($this, $info, $att, $file); - if (!$role->isInstallable()) { - $dest = '(not installable)'; - } else { - $dest = $role->processInstallation($info, $att, $file, ''); - if (PEAR::isError($dest)) { - $dest = '(Unknown role "' . $att['role'] . ')'; - } else { - list(,, $dest) = $dest; - } - } - $data['data'][] = array($file, $dest); - } - } - - $this->ui->outputData($data, $command); - return true; - } - - function doShellTest($command, $options, $params) - { - if (count($params) < 1) { - return PEAR::raiseError('ERROR, usage: pear shell-test packagename [[relation] version]'); - } - - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $reg = &$this->config->getRegistry(); - $info = $reg->parsePackageName($params[0], $this->config->get('default_channel')); - if (PEAR::isError($info)) { - exit(1); // invalid package name - } - - $package = $info['package']; - $channel = $info['channel']; - // "pear shell-test Foo" - if (!$reg->packageExists($package, $channel)) { - if ($channel == 'pecl.php.net') { - if ($reg->packageExists($package, 'pear.php.net')) { - $channel = 'pear.php.net'; // magically change channels for extensions - } - } - } - - if (count($params) === 1) { - if (!$reg->packageExists($package, $channel)) { - exit(1); - } - // "pear shell-test Foo 1.0" - } elseif (count($params) === 2) { - $v = $reg->packageInfo($package, 'version', $channel); - if (!$v || !version_compare("$v", "{$params[1]}", "ge")) { - exit(1); - } - // "pear shell-test Foo ge 1.0" - } elseif (count($params) === 3) { - $v = $reg->packageInfo($package, 'version', $channel); - if (!$v || !version_compare("$v", "{$params[2]}", $params[1])) { - exit(1); - } - } else { - PEAR::staticPopErrorHandling(); - $this->raiseError("$command: expects 1 to 3 parameters"); - exit(1); - } - } - - function doInfo($command, $options, $params) - { - if (count($params) !== 1) { - return $this->raiseError('pear info expects 1 parameter'); - } - - $info = $fp = false; - $reg = &$this->config->getRegistry(); - if (is_file($params[0]) && !is_dir($params[0]) && - (file_exists($params[0]) || $fp = @fopen($params[0], 'r')) - ) { - if ($fp) { - fclose($fp); - } - - if (!class_exists('PEAR_PackageFile')) { - require_once 'PEAR/PackageFile.php'; - } - - $pkg = &new PEAR_PackageFile($this->config, $this->_debug); - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $obj = &$pkg->fromAnyFile($params[0], PEAR_VALIDATE_NORMAL); - PEAR::staticPopErrorHandling(); - if (PEAR::isError($obj)) { - $uinfo = $obj->getUserInfo(); - if (is_array($uinfo)) { - foreach ($uinfo as $message) { - if (is_array($message)) { - $message = $message['message']; - } - $this->ui->outputData($message); - } - } - - return $this->raiseError($obj); - } - - if ($obj->getPackagexmlVersion() != '1.0') { - return $this->_doInfo2($command, $options, $params, $obj, false); - } - - $info = $obj->toArray(); - } else { - $parsed = $reg->parsePackageName($params[0], $this->config->get('default_channel')); - if (PEAR::isError($parsed)) { - return $this->raiseError($parsed); - } - - $package = $parsed['package']; - $channel = $parsed['channel']; - $info = $reg->packageInfo($package, null, $channel); - if (isset($info['old'])) { - $obj = $reg->getPackage($package, $channel); - return $this->_doInfo2($command, $options, $params, $obj, true); - } - } - - if (PEAR::isError($info)) { - return $info; - } - - if (empty($info)) { - $this->raiseError("No information found for `$params[0]'"); - return; - } - - unset($info['filelist']); - unset($info['dirtree']); - unset($info['changelog']); - if (isset($info['xsdversion'])) { - $info['package.xml version'] = $info['xsdversion']; - unset($info['xsdversion']); - } - - if (isset($info['packagerversion'])) { - $info['packaged with PEAR version'] = $info['packagerversion']; - unset($info['packagerversion']); - } - - $keys = array_keys($info); - $longtext = array('description', 'summary'); - foreach ($keys as $key) { - if (is_array($info[$key])) { - switch ($key) { - case 'maintainers': { - $i = 0; - $mstr = ''; - foreach ($info[$key] as $m) { - if ($i++ > 0) { - $mstr .= "\n"; - } - $mstr .= $m['name'] . " <"; - if (isset($m['email'])) { - $mstr .= $m['email']; - } else { - $mstr .= $m['handle'] . '@php.net'; - } - $mstr .= "> ($m[role])"; - } - $info[$key] = $mstr; - break; - } - case 'release_deps': { - $i = 0; - $dstr = ''; - foreach ($info[$key] as $d) { - if (isset($this->_deps_rel_trans[$d['rel']])) { - $rel = $this->_deps_rel_trans[$d['rel']]; - } else { - $rel = $d['rel']; - } - if (isset($this->_deps_type_trans[$d['type']])) { - $type = ucfirst($this->_deps_type_trans[$d['type']]); - } else { - $type = $d['type']; - } - if (isset($d['name'])) { - $name = $d['name'] . ' '; - } else { - $name = ''; - } - if (isset($d['version'])) { - $version = $d['version'] . ' '; - } else { - $version = ''; - } - if (isset($d['optional']) && $d['optional'] == 'yes') { - $optional = ' (optional)'; - } else { - $optional = ''; - } - $dstr .= "$type $name$rel $version$optional\n"; - } - $info[$key] = $dstr; - break; - } - case 'provides' : { - $debug = $this->config->get('verbose'); - if ($debug < 2) { - $pstr = 'Classes: '; - } else { - $pstr = ''; - } - $i = 0; - foreach ($info[$key] as $p) { - if ($debug < 2 && $p['type'] != "class") { - continue; - } - // Only print classes when verbosity mode is < 2 - if ($debug < 2) { - if ($i++ > 0) { - $pstr .= ", "; - } - $pstr .= $p['name']; - } else { - if ($i++ > 0) { - $pstr .= "\n"; - } - $pstr .= ucfirst($p['type']) . " " . $p['name']; - if (isset($p['explicit']) && $p['explicit'] == 1) { - $pstr .= " (explicit)"; - } - } - } - $info[$key] = $pstr; - break; - } - case 'configure_options' : { - foreach ($info[$key] as $i => $p) { - $info[$key][$i] = array_map(null, array_keys($p), array_values($p)); - $info[$key][$i] = array_map(create_function('$a', - 'return join(" = ",$a);'), $info[$key][$i]); - $info[$key][$i] = implode(', ', $info[$key][$i]); - } - $info[$key] = implode("\n", $info[$key]); - break; - } - default: { - $info[$key] = implode(", ", $info[$key]); - break; - } - } - } - - if ($key == '_lastmodified') { - $hdate = date('Y-m-d', $info[$key]); - unset($info[$key]); - $info['Last Modified'] = $hdate; - } elseif ($key == '_lastversion') { - $info['Previous Installed Version'] = $info[$key] ? $info[$key] : '- None -'; - unset($info[$key]); - } else { - $info[$key] = trim($info[$key]); - if (in_array($key, $longtext)) { - $info[$key] = preg_replace('/ +/', ' ', $info[$key]); - } - } - } - - $caption = 'About ' . $info['package'] . '-' . $info['version']; - $data = array( - 'caption' => $caption, - 'border' => true); - foreach ($info as $key => $value) { - $key = ucwords(trim(str_replace('_', ' ', $key))); - $data['data'][] = array($key, $value); - } - $data['raw'] = $info; - - $this->ui->outputData($data, 'package-info'); - } - - /** - * @access private - */ - function _doInfo2($command, $options, $params, &$obj, $installed) - { - $reg = &$this->config->getRegistry(); - $caption = 'About ' . $obj->getChannel() . '/' .$obj->getPackage() . '-' . - $obj->getVersion(); - $data = array( - 'caption' => $caption, - 'border' => true); - switch ($obj->getPackageType()) { - case 'php' : - $release = 'PEAR-style PHP-based Package'; - break; - case 'extsrc' : - $release = 'PECL-style PHP extension (source code)'; - break; - case 'zendextsrc' : - $release = 'PECL-style Zend extension (source code)'; - break; - case 'extbin' : - $release = 'PECL-style PHP extension (binary)'; - break; - case 'zendextbin' : - $release = 'PECL-style Zend extension (binary)'; - break; - case 'bundle' : - $release = 'Package bundle (collection of packages)'; - break; - } - $extends = $obj->getExtends(); - $extends = $extends ? - $obj->getPackage() . ' (extends ' . $extends . ')' : $obj->getPackage(); - if ($src = $obj->getSourcePackage()) { - $extends .= ' (source package ' . $src['channel'] . '/' . $src['package'] . ')'; - } - - $info = array( - 'Release Type' => $release, - 'Name' => $extends, - 'Channel' => $obj->getChannel(), - 'Summary' => preg_replace('/ +/', ' ', $obj->getSummary()), - 'Description' => preg_replace('/ +/', ' ', $obj->getDescription()), - ); - $info['Maintainers'] = ''; - foreach (array('lead', 'developer', 'contributor', 'helper') as $role) { - $leads = $obj->{"get{$role}s"}(); - if (!$leads) { - continue; - } - - if (isset($leads['active'])) { - $leads = array($leads); - } - - foreach ($leads as $lead) { - if (!empty($info['Maintainers'])) { - $info['Maintainers'] .= "\n"; - } - - $active = $lead['active'] == 'no' ? ', inactive' : ''; - $info['Maintainers'] .= $lead['name'] . ' <'; - $info['Maintainers'] .= $lead['email'] . "> ($role$active)"; - } - } - - $info['Release Date'] = $obj->getDate(); - if ($time = $obj->getTime()) { - $info['Release Date'] .= ' ' . $time; - } - - $info['Release Version'] = $obj->getVersion() . ' (' . $obj->getState() . ')'; - $info['API Version'] = $obj->getVersion('api') . ' (' . $obj->getState('api') . ')'; - $info['License'] = $obj->getLicense(); - $uri = $obj->getLicenseLocation(); - if ($uri) { - if (isset($uri['uri'])) { - $info['License'] .= ' (' . $uri['uri'] . ')'; - } else { - $extra = $obj->getInstalledLocation($info['filesource']); - if ($extra) { - $info['License'] .= ' (' . $uri['filesource'] . ')'; - } - } - } - - $info['Release Notes'] = $obj->getNotes(); - if ($compat = $obj->getCompatible()) { - if (!isset($compat[0])) { - $compat = array($compat); - } - - $info['Compatible with'] = ''; - foreach ($compat as $package) { - $info['Compatible with'] .= $package['channel'] . '/' . $package['name'] . - "\nVersions >= " . $package['min'] . ', <= ' . $package['max']; - if (isset($package['exclude'])) { - if (is_array($package['exclude'])) { - $package['exclude'] = implode(', ', $package['exclude']); - } - - if (!isset($info['Not Compatible with'])) { - $info['Not Compatible with'] = ''; - } else { - $info['Not Compatible with'] .= "\n"; - } - $info['Not Compatible with'] .= $package['channel'] . '/' . - $package['name'] . "\nVersions " . $package['exclude']; - } - } - } - - $usesrole = $obj->getUsesrole(); - if ($usesrole) { - if (!isset($usesrole[0])) { - $usesrole = array($usesrole); - } - - foreach ($usesrole as $roledata) { - if (isset($info['Uses Custom Roles'])) { - $info['Uses Custom Roles'] .= "\n"; - } else { - $info['Uses Custom Roles'] = ''; - } - - if (isset($roledata['package'])) { - $rolepackage = $reg->parsedPackageNameToString($roledata, true); - } else { - $rolepackage = $roledata['uri']; - } - $info['Uses Custom Roles'] .= $roledata['role'] . ' (' . $rolepackage . ')'; - } - } - - $usestask = $obj->getUsestask(); - if ($usestask) { - if (!isset($usestask[0])) { - $usestask = array($usestask); - } - - foreach ($usestask as $taskdata) { - if (isset($info['Uses Custom Tasks'])) { - $info['Uses Custom Tasks'] .= "\n"; - } else { - $info['Uses Custom Tasks'] = ''; - } - - if (isset($taskdata['package'])) { - $taskpackage = $reg->parsedPackageNameToString($taskdata, true); - } else { - $taskpackage = $taskdata['uri']; - } - $info['Uses Custom Tasks'] .= $taskdata['task'] . ' (' . $taskpackage . ')'; - } - } - - $deps = $obj->getDependencies(); - $info['Required Dependencies'] = 'PHP version ' . $deps['required']['php']['min']; - if (isset($deps['required']['php']['max'])) { - $info['Required Dependencies'] .= '-' . $deps['required']['php']['max'] . "\n"; - } else { - $info['Required Dependencies'] .= "\n"; - } - - if (isset($deps['required']['php']['exclude'])) { - if (!isset($info['Not Compatible with'])) { - $info['Not Compatible with'] = ''; - } else { - $info['Not Compatible with'] .= "\n"; - } - - if (is_array($deps['required']['php']['exclude'])) { - $deps['required']['php']['exclude'] = - implode(', ', $deps['required']['php']['exclude']); - } - $info['Not Compatible with'] .= "PHP versions\n " . - $deps['required']['php']['exclude']; - } - - $info['Required Dependencies'] .= 'PEAR installer version'; - if (isset($deps['required']['pearinstaller']['max'])) { - $info['Required Dependencies'] .= 's ' . - $deps['required']['pearinstaller']['min'] . '-' . - $deps['required']['pearinstaller']['max']; - } else { - $info['Required Dependencies'] .= ' ' . - $deps['required']['pearinstaller']['min'] . ' or newer'; - } - - if (isset($deps['required']['pearinstaller']['exclude'])) { - if (!isset($info['Not Compatible with'])) { - $info['Not Compatible with'] = ''; - } else { - $info['Not Compatible with'] .= "\n"; - } - - if (is_array($deps['required']['pearinstaller']['exclude'])) { - $deps['required']['pearinstaller']['exclude'] = - implode(', ', $deps['required']['pearinstaller']['exclude']); - } - $info['Not Compatible with'] .= "PEAR installer\n Versions " . - $deps['required']['pearinstaller']['exclude']; - } - - foreach (array('Package', 'Extension') as $type) { - $index = strtolower($type); - if (isset($deps['required'][$index])) { - if (isset($deps['required'][$index]['name'])) { - $deps['required'][$index] = array($deps['required'][$index]); - } - - foreach ($deps['required'][$index] as $package) { - if (isset($package['conflicts'])) { - $infoindex = 'Not Compatible with'; - if (!isset($info['Not Compatible with'])) { - $info['Not Compatible with'] = ''; - } else { - $info['Not Compatible with'] .= "\n"; - } - } else { - $infoindex = 'Required Dependencies'; - $info[$infoindex] .= "\n"; - } - - if ($index == 'extension') { - $name = $package['name']; - } else { - if (isset($package['channel'])) { - $name = $package['channel'] . '/' . $package['name']; - } else { - $name = '__uri/' . $package['name'] . ' (static URI)'; - } - } - - $info[$infoindex] .= "$type $name"; - if (isset($package['uri'])) { - $info[$infoindex] .= "\n Download URI: $package[uri]"; - continue; - } - - if (isset($package['max']) && isset($package['min'])) { - $info[$infoindex] .= " \n Versions " . - $package['min'] . '-' . $package['max']; - } elseif (isset($package['min'])) { - $info[$infoindex] .= " \n Version " . - $package['min'] . ' or newer'; - } elseif (isset($package['max'])) { - $info[$infoindex] .= " \n Version " . - $package['max'] . ' or older'; - } - - if (isset($package['recommended'])) { - $info[$infoindex] .= "\n Recommended version: $package[recommended]"; - } - - if (isset($package['exclude'])) { - if (!isset($info['Not Compatible with'])) { - $info['Not Compatible with'] = ''; - } else { - $info['Not Compatible with'] .= "\n"; - } - - if (is_array($package['exclude'])) { - $package['exclude'] = implode(', ', $package['exclude']); - } - - $package['package'] = $package['name']; // for parsedPackageNameToString - if (isset($package['conflicts'])) { - $info['Not Compatible with'] .= '=> except '; - } - $info['Not Compatible with'] .= 'Package ' . - $reg->parsedPackageNameToString($package, true); - $info['Not Compatible with'] .= "\n Versions " . $package['exclude']; - } - } - } - } - - if (isset($deps['required']['os'])) { - if (isset($deps['required']['os']['name'])) { - $dep['required']['os']['name'] = array($dep['required']['os']['name']); - } - - foreach ($dep['required']['os'] as $os) { - if (isset($os['conflicts']) && $os['conflicts'] == 'yes') { - if (!isset($info['Not Compatible with'])) { - $info['Not Compatible with'] = ''; - } else { - $info['Not Compatible with'] .= "\n"; - } - $info['Not Compatible with'] .= "$os[name] Operating System"; - } else { - $info['Required Dependencies'] .= "\n"; - $info['Required Dependencies'] .= "$os[name] Operating System"; - } - } - } - - if (isset($deps['required']['arch'])) { - if (isset($deps['required']['arch']['pattern'])) { - $dep['required']['arch']['pattern'] = array($dep['required']['os']['pattern']); - } - - foreach ($dep['required']['arch'] as $os) { - if (isset($os['conflicts']) && $os['conflicts'] == 'yes') { - if (!isset($info['Not Compatible with'])) { - $info['Not Compatible with'] = ''; - } else { - $info['Not Compatible with'] .= "\n"; - } - $info['Not Compatible with'] .= "OS/Arch matching pattern '/$os[pattern]/'"; - } else { - $info['Required Dependencies'] .= "\n"; - $info['Required Dependencies'] .= "OS/Arch matching pattern '/$os[pattern]/'"; - } - } - } - - if (isset($deps['optional'])) { - foreach (array('Package', 'Extension') as $type) { - $index = strtolower($type); - if (isset($deps['optional'][$index])) { - if (isset($deps['optional'][$index]['name'])) { - $deps['optional'][$index] = array($deps['optional'][$index]); - } - - foreach ($deps['optional'][$index] as $package) { - if (isset($package['conflicts']) && $package['conflicts'] == 'yes') { - $infoindex = 'Not Compatible with'; - if (!isset($info['Not Compatible with'])) { - $info['Not Compatible with'] = ''; - } else { - $info['Not Compatible with'] .= "\n"; - } - } else { - $infoindex = 'Optional Dependencies'; - if (!isset($info['Optional Dependencies'])) { - $info['Optional Dependencies'] = ''; - } else { - $info['Optional Dependencies'] .= "\n"; - } - } - - if ($index == 'extension') { - $name = $package['name']; - } else { - if (isset($package['channel'])) { - $name = $package['channel'] . '/' . $package['name']; - } else { - $name = '__uri/' . $package['name'] . ' (static URI)'; - } - } - - $info[$infoindex] .= "$type $name"; - if (isset($package['uri'])) { - $info[$infoindex] .= "\n Download URI: $package[uri]"; - continue; - } - - if ($infoindex == 'Not Compatible with') { - // conflicts is only used to say that all versions conflict - continue; - } - - if (isset($package['max']) && isset($package['min'])) { - $info[$infoindex] .= " \n Versions " . - $package['min'] . '-' . $package['max']; - } elseif (isset($package['min'])) { - $info[$infoindex] .= " \n Version " . - $package['min'] . ' or newer'; - } elseif (isset($package['max'])) { - $info[$infoindex] .= " \n Version " . - $package['min'] . ' or older'; - } - - if (isset($package['recommended'])) { - $info[$infoindex] .= "\n Recommended version: $package[recommended]"; - } - - if (isset($package['exclude'])) { - if (!isset($info['Not Compatible with'])) { - $info['Not Compatible with'] = ''; - } else { - $info['Not Compatible with'] .= "\n"; - } - - if (is_array($package['exclude'])) { - $package['exclude'] = implode(', ', $package['exclude']); - } - - $info['Not Compatible with'] .= "Package $package\n Versions " . - $package['exclude']; - } - } - } - } - } - - if (isset($deps['group'])) { - if (!isset($deps['group'][0])) { - $deps['group'] = array($deps['group']); - } - - foreach ($deps['group'] as $group) { - $info['Dependency Group ' . $group['attribs']['name']] = $group['attribs']['hint']; - $groupindex = $group['attribs']['name'] . ' Contents'; - $info[$groupindex] = ''; - foreach (array('Package', 'Extension') as $type) { - $index = strtolower($type); - if (isset($group[$index])) { - if (isset($group[$index]['name'])) { - $group[$index] = array($group[$index]); - } - - foreach ($group[$index] as $package) { - if (!empty($info[$groupindex])) { - $info[$groupindex] .= "\n"; - } - - if ($index == 'extension') { - $name = $package['name']; - } else { - if (isset($package['channel'])) { - $name = $package['channel'] . '/' . $package['name']; - } else { - $name = '__uri/' . $package['name'] . ' (static URI)'; - } - } - - if (isset($package['uri'])) { - if (isset($package['conflicts']) && $package['conflicts'] == 'yes') { - $info[$groupindex] .= "Not Compatible with $type $name"; - } else { - $info[$groupindex] .= "$type $name"; - } - - $info[$groupindex] .= "\n Download URI: $package[uri]"; - continue; - } - - if (isset($package['conflicts']) && $package['conflicts'] == 'yes') { - $info[$groupindex] .= "Not Compatible with $type $name"; - continue; - } - - $info[$groupindex] .= "$type $name"; - if (isset($package['max']) && isset($package['min'])) { - $info[$groupindex] .= " \n Versions " . - $package['min'] . '-' . $package['max']; - } elseif (isset($package['min'])) { - $info[$groupindex] .= " \n Version " . - $package['min'] . ' or newer'; - } elseif (isset($package['max'])) { - $info[$groupindex] .= " \n Version " . - $package['min'] . ' or older'; - } - - if (isset($package['recommended'])) { - $info[$groupindex] .= "\n Recommended version: $package[recommended]"; - } - - if (isset($package['exclude'])) { - if (!isset($info['Not Compatible with'])) { - $info['Not Compatible with'] = ''; - } else { - $info[$groupindex] .= "Not Compatible with\n"; - } - - if (is_array($package['exclude'])) { - $package['exclude'] = implode(', ', $package['exclude']); - } - $info[$groupindex] .= " Package $package\n Versions " . - $package['exclude']; - } - } - } - } - } - } - - if ($obj->getPackageType() == 'bundle') { - $info['Bundled Packages'] = ''; - foreach ($obj->getBundledPackages() as $package) { - if (!empty($info['Bundled Packages'])) { - $info['Bundled Packages'] .= "\n"; - } - - if (isset($package['uri'])) { - $info['Bundled Packages'] .= '__uri/' . $package['name']; - $info['Bundled Packages'] .= "\n (URI: $package[uri]"; - } else { - $info['Bundled Packages'] .= $package['channel'] . '/' . $package['name']; - } - } - } - - $info['package.xml version'] = '2.0'; - if ($installed) { - if ($obj->getLastModified()) { - $info['Last Modified'] = date('Y-m-d H:i', $obj->getLastModified()); - } - - $v = $obj->getLastInstalledVersion(); - $info['Previous Installed Version'] = $v ? $v : '- None -'; - } - - foreach ($info as $key => $value) { - $data['data'][] = array($key, $value); - } - - $data['raw'] = $obj->getArray(); // no validation needed - $this->ui->outputData($data, 'package-info'); - } -} \ No newline at end of file diff --git a/pear/PEAR/Command/Registry.xml b/pear/PEAR/Command/Registry.xml deleted file mode 100644 index 9f4e214..0000000 --- a/pear/PEAR/Command/Registry.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - List Installed Packages In The Default Channel - doList - l - - - c - list installed packages from this channel - CHAN - - - a - list installed packages from all channels - - - i - output fully channel-aware data, even on failure - - - <package> -If invoked without parameters, this command lists the PEAR packages -installed in your php_dir ({config php_dir}). With a parameter, it -lists the files in a package. - - - - List Files In Installed Package - doFileList - fl - - <package> -List the files in an installed package. - - - - Shell Script Test - doShellTest - st - - <package> [[relation] version] -Tests if a package is installed in the system. Will exit(1) if it is not. - <relation> The version comparison operator. One of: - <, lt, <=, le, >, gt, >=, ge, ==, =, eq, !=, <>, ne - <version> The version to compare with - - - - Display information about a package - doInfo - in - - <package> -Displays information about a package. The package argument may be a -local package file, an URL to a package file, or the name of an -installed package. - - \ No newline at end of file diff --git a/pear/PEAR/Command/Remote.php b/pear/PEAR/Command/Remote.php deleted file mode 100644 index 52d0aee..0000000 --- a/pear/PEAR/Command/Remote.php +++ /dev/null @@ -1,810 +0,0 @@ - - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 0.1 - */ - -/** - * base class - */ -require_once 'PEAR/Command/Common.php'; -require_once 'PEAR/REST.php'; - -/** - * PEAR commands for remote server querying - * - * @category pear - * @package PEAR - * @author Stig Bakken - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 0.1 - */ -class PEAR_Command_Remote extends PEAR_Command_Common -{ - var $commands = array( - 'remote-info' => array( - 'summary' => 'Information About Remote Packages', - 'function' => 'doRemoteInfo', - 'shortcut' => 'ri', - 'options' => array(), - 'doc' => ' -Get details on a package from the server.', - ), - 'list-upgrades' => array( - 'summary' => 'List Available Upgrades', - 'function' => 'doListUpgrades', - 'shortcut' => 'lu', - 'options' => array( - 'channelinfo' => array( - 'shortopt' => 'i', - 'doc' => 'output fully channel-aware data, even on failure', - ), - ), - 'doc' => '[preferred_state] -List releases on the server of packages you have installed where -a newer version is available with the same release state (stable etc.) -or the state passed as the second parameter.' - ), - 'remote-list' => array( - 'summary' => 'List Remote Packages', - 'function' => 'doRemoteList', - 'shortcut' => 'rl', - 'options' => array( - 'channel' => - array( - 'shortopt' => 'c', - 'doc' => 'specify a channel other than the default channel', - 'arg' => 'CHAN', - ) - ), - 'doc' => ' -Lists the packages available on the configured server along with the -latest stable release of each package.', - ), - 'search' => array( - 'summary' => 'Search remote package database', - 'function' => 'doSearch', - 'shortcut' => 'sp', - 'options' => array( - 'channel' => - array( - 'shortopt' => 'c', - 'doc' => 'specify a channel other than the default channel', - 'arg' => 'CHAN', - ), - 'allchannels' => array( - 'shortopt' => 'a', - 'doc' => 'search packages from all known channels', - ), - 'channelinfo' => array( - 'shortopt' => 'i', - 'doc' => 'output fully channel-aware data, even on failure', - ), - ), - 'doc' => '[packagename] [packageinfo] -Lists all packages which match the search parameters. The first -parameter is a fragment of a packagename. The default channel -will be used unless explicitly overridden. The second parameter -will be used to match any portion of the summary/description', - ), - 'list-all' => array( - 'summary' => 'List All Packages', - 'function' => 'doListAll', - 'shortcut' => 'la', - 'options' => array( - 'channel' => - array( - 'shortopt' => 'c', - 'doc' => 'specify a channel other than the default channel', - 'arg' => 'CHAN', - ), - 'channelinfo' => array( - 'shortopt' => 'i', - 'doc' => 'output fully channel-aware data, even on failure', - ), - ), - 'doc' => ' -Lists the packages available on the configured server along with the -latest stable release of each package.', - ), - 'download' => array( - 'summary' => 'Download Package', - 'function' => 'doDownload', - 'shortcut' => 'd', - 'options' => array( - 'nocompress' => array( - 'shortopt' => 'Z', - 'doc' => 'download an uncompressed (.tar) file', - ), - ), - 'doc' => '... -Download package tarballs. The files will be named as suggested by the -server, for example if you download the DB package and the latest stable -version of DB is 1.6.5, the downloaded file will be DB-1.6.5.tgz.', - ), - 'clear-cache' => array( - 'summary' => 'Clear Web Services Cache', - 'function' => 'doClearCache', - 'shortcut' => 'cc', - 'options' => array(), - 'doc' => ' -Clear the REST cache. See also the cache_ttl configuration -parameter. -', - ), - ); - - /** - * PEAR_Command_Remote constructor. - * - * @access public - */ - function PEAR_Command_Remote(&$ui, &$config) - { - parent::PEAR_Command_Common($ui, $config); - } - - function _checkChannelForStatus($channel, $chan) - { - if (PEAR::isError($chan)) { - $this->raiseError($chan); - } - if (!is_a($chan, 'PEAR_ChannelFile')) { - return $this->raiseError('Internal corruption error: invalid channel "' . - $channel . '"'); - } - $rest = new PEAR_REST($this->config); - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $mirror = $this->config->get('preferred_mirror', null, - $channel); - $a = $rest->downloadHttp('http://' . $channel . - '/channel.xml', $chan->lastModified()); - PEAR::staticPopErrorHandling(); - if (!PEAR::isError($a) && $a) { - $this->ui->outputData('WARNING: channel "' . $channel . '" has ' . - 'updated its protocols, use "' . PEAR_RUNTYPE . ' channel-update ' . $channel . - '" to update'); - } - } - - function doRemoteInfo($command, $options, $params) - { - if (sizeof($params) != 1) { - return $this->raiseError("$command expects one param: the remote package name"); - } - $savechannel = $channel = $this->config->get('default_channel'); - $reg = &$this->config->getRegistry(); - $package = $params[0]; - $parsed = $reg->parsePackageName($package, $channel); - if (PEAR::isError($parsed)) { - return $this->raiseError('Invalid package name "' . $package . '"'); - } - - $channel = $parsed['channel']; - $this->config->set('default_channel', $channel); - $chan = $reg->getChannel($channel); - if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) { - return $e; - } - - $mirror = $this->config->get('preferred_mirror'); - if ($chan->supportsREST($mirror) && $base = $chan->getBaseURL('REST1.0', $mirror)) { - $rest = &$this->config->getREST('1.0', array()); - $info = $rest->packageInfo($base, $parsed['package'], $channel); - } - - if (!isset($info)) { - return $this->raiseError('No supported protocol was found'); - } - - if (PEAR::isError($info)) { - $this->config->set('default_channel', $savechannel); - return $this->raiseError($info); - } - - if (!isset($info['name'])) { - return $this->raiseError('No remote package "' . $package . '" was found'); - } - - $installed = $reg->packageInfo($info['name'], null, $channel); - $info['installed'] = $installed['version'] ? $installed['version'] : '- no -'; - if (is_array($info['installed'])) { - $info['installed'] = $info['installed']['release']; - } - - $this->ui->outputData($info, $command); - $this->config->set('default_channel', $savechannel); - - return true; - } - - function doRemoteList($command, $options, $params) - { - $savechannel = $channel = $this->config->get('default_channel'); - $reg = &$this->config->getRegistry(); - if (isset($options['channel'])) { - $channel = $options['channel']; - if (!$reg->channelExists($channel)) { - return $this->raiseError('Channel "' . $channel . '" does not exist'); - } - - $this->config->set('default_channel', $channel); - } - - $chan = $reg->getChannel($channel); - if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) { - return $e; - } - - $list_options = false; - if ($this->config->get('preferred_state') == 'stable') { - $list_options = true; - } - - $available = array(); - if ($chan->supportsREST($this->config->get('preferred_mirror')) && - $base = $chan->getBaseURL('REST1.1', $this->config->get('preferred_mirror')) - ) { - // use faster list-all if available - $rest = &$this->config->getREST('1.1', array()); - $available = $rest->listAll($base, $list_options, true, false, false, $chan->getName()); - } elseif ($chan->supportsREST($this->config->get('preferred_mirror')) && - $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) { - $rest = &$this->config->getREST('1.0', array()); - $available = $rest->listAll($base, $list_options, true, false, false, $chan->getName()); - } - - if (PEAR::isError($available)) { - $this->config->set('default_channel', $savechannel); - return $this->raiseError($available); - } - - $i = $j = 0; - $data = array( - 'caption' => 'Channel ' . $channel . ' Available packages:', - 'border' => true, - 'headline' => array('Package', 'Version'), - 'channel' => $channel - ); - - if (count($available) == 0) { - $data = '(no packages available yet)'; - } else { - foreach ($available as $name => $info) { - $version = (isset($info['stable']) && $info['stable']) ? $info['stable'] : '-n/a-'; - $data['data'][] = array($name, $version); - } - } - $this->ui->outputData($data, $command); - $this->config->set('default_channel', $savechannel); - return true; - } - - function doListAll($command, $options, $params) - { - $savechannel = $channel = $this->config->get('default_channel'); - $reg = &$this->config->getRegistry(); - if (isset($options['channel'])) { - $channel = $options['channel']; - if (!$reg->channelExists($channel)) { - return $this->raiseError("Channel \"$channel\" does not exist"); - } - - $this->config->set('default_channel', $channel); - } - - $list_options = false; - if ($this->config->get('preferred_state') == 'stable') { - $list_options = true; - } - - $chan = $reg->getChannel($channel); - if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) { - return $e; - } - - if ($chan->supportsREST($this->config->get('preferred_mirror')) && - $base = $chan->getBaseURL('REST1.1', $this->config->get('preferred_mirror'))) { - // use faster list-all if available - $rest = &$this->config->getREST('1.1', array()); - $available = $rest->listAll($base, $list_options, false, false, false, $chan->getName()); - } elseif ($chan->supportsREST($this->config->get('preferred_mirror')) && - $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) { - $rest = &$this->config->getREST('1.0', array()); - $available = $rest->listAll($base, $list_options, false, false, false, $chan->getName()); - } - - if (PEAR::isError($available)) { - $this->config->set('default_channel', $savechannel); - return $this->raiseError('The package list could not be fetched from the remote server. Please try again. (Debug info: "' . $available->getMessage() . '")'); - } - - $data = array( - 'caption' => 'All packages [Channel ' . $channel . ']:', - 'border' => true, - 'headline' => array('Package', 'Latest', 'Local'), - 'channel' => $channel, - ); - - if (isset($options['channelinfo'])) { - // add full channelinfo - $data['caption'] = 'Channel ' . $channel . ' All packages:'; - $data['headline'] = array('Channel', 'Package', 'Latest', 'Local', - 'Description', 'Dependencies'); - } - $local_pkgs = $reg->listPackages($channel); - - foreach ($available as $name => $info) { - $installed = $reg->packageInfo($name, null, $channel); - if (is_array($installed['version'])) { - $installed['version'] = $installed['version']['release']; - } - $desc = $info['summary']; - if (isset($params[$name])) { - $desc .= "\n\n".$info['description']; - } - - if (isset($options['mode'])) { - if ($options['mode'] == 'installed' && !isset($installed['version'])) { - continue; - } - if ($options['mode'] == 'notinstalled' && isset($installed['version'])) { - continue; - } - if ($options['mode'] == 'upgrades' - && (!isset($installed['version']) || version_compare($installed['version'], - $info['stable'], '>='))) { - continue; - } - } - $pos = array_search(strtolower($name), $local_pkgs); - if ($pos !== false) { - unset($local_pkgs[$pos]); - } - - if (isset($info['stable']) && !$info['stable']) { - $info['stable'] = null; - } - - if (isset($options['channelinfo'])) { - // add full channelinfo - if ($info['stable'] === $info['unstable']) { - $state = $info['state']; - } else { - $state = 'stable'; - } - $latest = $info['stable'].' ('.$state.')'; - $local = ''; - if (isset($installed['version'])) { - $inst_state = $reg->packageInfo($name, 'release_state', $channel); - $local = $installed['version'].' ('.$inst_state.')'; - } - - $packageinfo = array( - $channel, - $name, - $latest, - $local, - isset($desc) ? $desc : null, - isset($info['deps']) ? $info['deps'] : null, - ); - } else { - $packageinfo = array( - $reg->channelAlias($channel) . '/' . $name, - isset($info['stable']) ? $info['stable'] : null, - isset($installed['version']) ? $installed['version'] : null, - isset($desc) ? $desc : null, - isset($info['deps']) ? $info['deps'] : null, - ); - } - $data['data'][$info['category']][] = $packageinfo; - } - - if (isset($options['mode']) && in_array($options['mode'], array('notinstalled', 'upgrades'))) { - $this->config->set('default_channel', $savechannel); - $this->ui->outputData($data, $command); - return true; - } - - foreach ($local_pkgs as $name) { - $info = &$reg->getPackage($name, $channel); - $data['data']['Local'][] = array( - $reg->channelAlias($channel) . '/' . $info->getPackage(), - '', - $info->getVersion(), - $info->getSummary(), - $info->getDeps() - ); - } - - $this->config->set('default_channel', $savechannel); - $this->ui->outputData($data, $command); - return true; - } - - function doSearch($command, $options, $params) - { - if ((!isset($params[0]) || empty($params[0])) - && (!isset($params[1]) || empty($params[1]))) - { - return $this->raiseError('no valid search string supplied'); - } - - $channelinfo = isset($options['channelinfo']); - $reg = &$this->config->getRegistry(); - if (isset($options['allchannels'])) { - // search all channels - unset($options['allchannels']); - $channels = $reg->getChannels(); - $errors = array(); - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - foreach ($channels as $channel) { - if ($channel->getName() != '__uri') { - $options['channel'] = $channel->getName(); - $ret = $this->doSearch($command, $options, $params); - if (PEAR::isError($ret)) { - $errors[] = $ret; - } - } - } - - PEAR::staticPopErrorHandling(); - if (count($errors) !== 0) { - // for now, only give first error - return PEAR::raiseError($errors[0]); - } - - return true; - } - - $savechannel = $channel = $this->config->get('default_channel'); - $package = strtolower($params[0]); - $summary = isset($params[1]) ? $params[1] : false; - if (isset($options['channel'])) { - $reg = &$this->config->getRegistry(); - $channel = $options['channel']; - if (!$reg->channelExists($channel)) { - return $this->raiseError('Channel "' . $channel . '" does not exist'); - } - - $this->config->set('default_channel', $channel); - } - - $chan = $reg->getChannel($channel); - if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) { - return $e; - } - - if ($chan->supportsREST($this->config->get('preferred_mirror')) && - $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) { - $rest = &$this->config->getREST('1.0', array()); - $available = $rest->listAll($base, false, false, $package, $summary, $chan->getName()); - } - - if (PEAR::isError($available)) { - $this->config->set('default_channel', $savechannel); - return $this->raiseError($available); - } - - if (!$available && !$channelinfo) { - // clean exit when not found, no error ! - $data = 'no packages found that match pattern "' . $package . '", for channel '.$channel.'.'; - $this->ui->outputData($data); - $this->config->set('default_channel', $channel); - return true; - } - - if ($channelinfo) { - $data = array( - 'caption' => 'Matched packages, channel ' . $channel . ':', - 'border' => true, - 'headline' => array('Channel', 'Package', 'Stable/(Latest)', 'Local'), - 'channel' => $channel - ); - } else { - $data = array( - 'caption' => 'Matched packages, channel ' . $channel . ':', - 'border' => true, - 'headline' => array('Package', 'Stable/(Latest)', 'Local'), - 'channel' => $channel - ); - } - - if (!$available && $channelinfo) { - unset($data['headline']); - $data['data'] = 'No packages found that match pattern "' . $package . '".'; - $available = array(); - } - - foreach ($available as $name => $info) { - $installed = $reg->packageInfo($name, null, $channel); - $desc = $info['summary']; - if (isset($params[$name])) - $desc .= "\n\n".$info['description']; - - if (!isset($info['stable']) || !$info['stable']) { - $version_remote = 'none'; - } else { - if ($info['unstable']) { - $version_remote = $info['unstable']; - } else { - $version_remote = $info['stable']; - } - $version_remote .= ' ('.$info['state'].')'; - } - $version = is_array($installed['version']) ? $installed['version']['release'] : - $installed['version']; - if ($channelinfo) { - $packageinfo = array( - $channel, - $name, - $version_remote, - $version, - $desc, - ); - } else { - $packageinfo = array( - $name, - $version_remote, - $version, - $desc, - ); - } - $data['data'][$info['category']][] = $packageinfo; - } - - $this->ui->outputData($data, $command); - $this->config->set('default_channel', $channel); - return true; - } - - function &getDownloader($options) - { - if (!class_exists('PEAR_Downloader')) { - require_once 'PEAR/Downloader.php'; - } - $a = &new PEAR_Downloader($this->ui, $options, $this->config); - return $a; - } - - function doDownload($command, $options, $params) - { - // make certain that dependencies are ignored - $options['downloadonly'] = 1; - - // eliminate error messages for preferred_state-related errors - /* TODO: Should be an option, but until now download does respect - prefered state */ - /* $options['ignorepreferred_state'] = 1; */ - // eliminate error messages for preferred_state-related errors - - $downloader = &$this->getDownloader($options); - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $e = $downloader->setDownloadDir(getcwd()); - PEAR::staticPopErrorHandling(); - if (PEAR::isError($e)) { - return $this->raiseError('Current directory is not writeable, cannot download'); - } - - $errors = array(); - $downloaded = array(); - $err = $downloader->download($params); - if (PEAR::isError($err)) { - return $err; - } - - $errors = $downloader->getErrorMsgs(); - if (count($errors)) { - foreach ($errors as $error) { - if ($error !== null) { - $this->ui->outputData($error); - } - } - - return $this->raiseError("$command failed"); - } - - $downloaded = $downloader->getDownloadedPackages(); - foreach ($downloaded as $pkg) { - $this->ui->outputData("File $pkg[file] downloaded", $command); - } - - return true; - } - - function downloadCallback($msg, $params = null) - { - if ($msg == 'done') { - $this->bytes_downloaded = $params; - } - } - - function doListUpgrades($command, $options, $params) - { - require_once 'PEAR/Common.php'; - if (isset($params[0]) && !is_array(PEAR_Common::betterStates($params[0]))) { - return $this->raiseError($params[0] . ' is not a valid state (stable/beta/alpha/devel/etc.) try "pear help list-upgrades"'); - } - - $savechannel = $channel = $this->config->get('default_channel'); - $reg = &$this->config->getRegistry(); - foreach ($reg->listChannels() as $channel) { - $inst = array_flip($reg->listPackages($channel)); - if (!count($inst)) { - continue; - } - - if ($channel == '__uri') { - continue; - } - - $this->config->set('default_channel', $channel); - $state = empty($params[0]) ? $this->config->get('preferred_state') : $params[0]; - - $caption = $channel . ' Available Upgrades'; - $chan = $reg->getChannel($channel); - if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) { - return $e; - } - - $latest = array(); - $base2 = false; - $preferred_mirror = $this->config->get('preferred_mirror'); - if ($chan->supportsREST($preferred_mirror) && - ( - //($base2 = $chan->getBaseURL('REST1.4', $preferred_mirror)) || - ($base = $chan->getBaseURL('REST1.0', $preferred_mirror)) - ) - - ) { - if ($base2) { - $rest = &$this->config->getREST('1.4', array()); - $base = $base2; - } else { - $rest = &$this->config->getREST('1.0', array()); - } - - if (empty($state) || $state == 'any') { - $state = false; - } else { - $caption .= ' (' . implode(', ', PEAR_Common::betterStates($state, true)) . ')'; - } - - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $latest = $rest->listLatestUpgrades($base, $state, $inst, $channel, $reg); - PEAR::staticPopErrorHandling(); - } - - if (PEAR::isError($latest)) { - $this->ui->outputData($latest->getMessage()); - continue; - } - - $caption .= ':'; - if (PEAR::isError($latest)) { - $this->config->set('default_channel', $savechannel); - return $latest; - } - - $data = array( - 'caption' => $caption, - 'border' => 1, - 'headline' => array('Channel', 'Package', 'Local', 'Remote', 'Size'), - 'channel' => $channel - ); - - foreach ((array)$latest as $pkg => $info) { - $package = strtolower($pkg); - if (!isset($inst[$package])) { - // skip packages we don't have installed - continue; - } - - extract($info); - $inst_version = $reg->packageInfo($package, 'version', $channel); - $inst_state = $reg->packageInfo($package, 'release_state', $channel); - if (version_compare("$version", "$inst_version", "le")) { - // installed version is up-to-date - continue; - } - - if ($filesize >= 20480) { - $filesize += 1024 - ($filesize % 1024); - $fs = sprintf("%dkB", $filesize / 1024); - } elseif ($filesize > 0) { - $filesize += 103 - ($filesize % 103); - $fs = sprintf("%.1fkB", $filesize / 1024.0); - } else { - $fs = " -"; // XXX center instead - } - - $data['data'][] = array($channel, $pkg, "$inst_version ($inst_state)", "$version ($state)", $fs); - } - - if (isset($options['channelinfo'])) { - if (empty($data['data'])) { - unset($data['headline']); - if (count($inst) == 0) { - $data['data'] = '(no packages installed)'; - } else { - $data['data'] = '(no upgrades available)'; - } - } - $this->ui->outputData($data, $command); - } else { - if (empty($data['data'])) { - $this->ui->outputData('Channel ' . $channel . ': No upgrades available'); - } else { - $this->ui->outputData($data, $command); - } - } - } - - $this->config->set('default_channel', $savechannel); - return true; - } - - function doClearCache($command, $options, $params) - { - $cache_dir = $this->config->get('cache_dir'); - $verbose = $this->config->get('verbose'); - $output = ''; - if (!file_exists($cache_dir) || !is_dir($cache_dir)) { - return $this->raiseError("$cache_dir does not exist or is not a directory"); - } - - if (!($dp = @opendir($cache_dir))) { - return $this->raiseError("opendir($cache_dir) failed: $php_errormsg"); - } - - if ($verbose >= 1) { - $output .= "reading directory $cache_dir\n"; - } - - $num = 0; - while ($ent = readdir($dp)) { - if (preg_match('/rest.cache(file|id)\\z/', $ent)) { - $path = $cache_dir . DIRECTORY_SEPARATOR . $ent; - if (file_exists($path)) { - $ok = @unlink($path); - } else { - $ok = false; - $php_errormsg = ''; - } - - if ($ok) { - if ($verbose >= 2) { - $output .= "deleted $path\n"; - } - $num++; - } elseif ($verbose >= 1) { - $output .= "failed to delete $path $php_errormsg\n"; - } - } - } - - closedir($dp); - if ($verbose >= 1) { - $output .= "$num cache entries cleared\n"; - } - - $this->ui->outputData(rtrim($output), $command); - return $num; - } -} \ No newline at end of file diff --git a/pear/PEAR/Command/Remote.xml b/pear/PEAR/Command/Remote.xml deleted file mode 100644 index b4f6100..0000000 --- a/pear/PEAR/Command/Remote.xml +++ /dev/null @@ -1,109 +0,0 @@ - - - Information About Remote Packages - doRemoteInfo - ri - - <package> -Get details on a package from the server. - - - List Available Upgrades - doListUpgrades - lu - - - i - output fully channel-aware data, even on failure - - - [preferred_state] -List releases on the server of packages you have installed where -a newer version is available with the same release state (stable etc.) -or the state passed as the second parameter. - - - List Remote Packages - doRemoteList - rl - - - c - specify a channel other than the default channel - CHAN - - - -Lists the packages available on the configured server along with the -latest stable release of each package. - - - Search remote package database - doSearch - sp - - - c - specify a channel other than the default channel - CHAN - - - a - search packages from all known channels - - - i - output fully channel-aware data, even on failure - - - [packagename] [packageinfo] -Lists all packages which match the search parameters. The first -parameter is a fragment of a packagename. The default channel -will be used unless explicitly overridden. The second parameter -will be used to match any portion of the summary/description - - - List All Packages - doListAll - la - - - c - specify a channel other than the default channel - CHAN - - - i - output fully channel-aware data, even on failure - - - -Lists the packages available on the configured server along with the -latest stable release of each package. - - - Download Package - doDownload - d - - - Z - download an uncompressed (.tar) file - - - <package>... -Download package tarballs. The files will be named as suggested by the -server, for example if you download the DB package and the latest stable -version of DB is 1.6.5, the downloaded file will be DB-1.6.5.tgz. - - - Clear Web Services Cache - doClearCache - cc - - -Clear the XML-RPC/REST cache. See also the cache_ttl configuration -parameter. - - - \ No newline at end of file diff --git a/pear/PEAR/Command/Test.php b/pear/PEAR/Command/Test.php deleted file mode 100644 index fcfa6f6..0000000 --- a/pear/PEAR/Command/Test.php +++ /dev/null @@ -1,337 +0,0 @@ - - * @author Martin Jansen - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 0.1 - */ - -/** - * base class - */ -require_once 'PEAR/Command/Common.php'; - -/** - * PEAR commands for login/logout - * - * @category pear - * @package PEAR - * @author Stig Bakken - * @author Martin Jansen - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 0.1 - */ - -class PEAR_Command_Test extends PEAR_Command_Common -{ - var $commands = array( - 'run-tests' => array( - 'summary' => 'Run Regression Tests', - 'function' => 'doRunTests', - 'shortcut' => 'rt', - 'options' => array( - 'recur' => array( - 'shortopt' => 'r', - 'doc' => 'Run tests in child directories, recursively. 4 dirs deep maximum', - ), - 'ini' => array( - 'shortopt' => 'i', - 'doc' => 'actual string of settings to pass to php in format " -d setting=blah"', - 'arg' => 'SETTINGS' - ), - 'realtimelog' => array( - 'shortopt' => 'l', - 'doc' => 'Log test runs/results as they are run', - ), - 'quiet' => array( - 'shortopt' => 'q', - 'doc' => 'Only display detail for failed tests', - ), - 'simple' => array( - 'shortopt' => 's', - 'doc' => 'Display simple output for all tests', - ), - 'package' => array( - 'shortopt' => 'p', - 'doc' => 'Treat parameters as installed packages from which to run tests', - ), - 'phpunit' => array( - 'shortopt' => 'u', - 'doc' => 'Search parameters for AllTests.php, and use that to run phpunit-based tests -If none is found, all .phpt tests will be tried instead.', - ), - 'tapoutput' => array( - 'shortopt' => 't', - 'doc' => 'Output run-tests.log in TAP-compliant format', - ), - 'cgi' => array( - 'shortopt' => 'c', - 'doc' => 'CGI php executable (needed for tests with POST/GET section)', - 'arg' => 'PHPCGI', - ), - 'coverage' => array( - 'shortopt' => 'x', - 'doc' => 'Generate a code coverage report (requires Xdebug 2.0.0+)', - ), - ), - 'doc' => '[testfile|dir ...] -Run regression tests with PHP\'s regression testing script (run-tests.php).', - ), - ); - - var $output; - - /** - * PEAR_Command_Test constructor. - * - * @access public - */ - function PEAR_Command_Test(&$ui, &$config) - { - parent::PEAR_Command_Common($ui, $config); - } - - function doRunTests($command, $options, $params) - { - if (isset($options['phpunit']) && isset($options['tapoutput'])) { - return $this->raiseError('ERROR: cannot use both --phpunit and --tapoutput at the same time'); - } - - require_once 'PEAR/Common.php'; - require_once 'System.php'; - $log = new PEAR_Common; - $log->ui = &$this->ui; // slightly hacky, but it will work - $tests = array(); - $depth = isset($options['recur']) ? 14 : 1; - - if (!count($params)) { - $params[] = '.'; - } - - if (isset($options['package'])) { - $oldparams = $params; - $params = array(); - $reg = &$this->config->getRegistry(); - foreach ($oldparams as $param) { - $pname = $reg->parsePackageName($param, $this->config->get('default_channel')); - if (PEAR::isError($pname)) { - return $this->raiseError($pname); - } - - $package = &$reg->getPackage($pname['package'], $pname['channel']); - if (!$package) { - return PEAR::raiseError('Unknown package "' . - $reg->parsedPackageNameToString($pname) . '"'); - } - - $filelist = $package->getFilelist(); - foreach ($filelist as $name => $atts) { - if (isset($atts['role']) && $atts['role'] != 'test') { - continue; - } - - if (isset($options['phpunit']) && preg_match('/AllTests\.php\\z/i', $name)) { - $params[] = $atts['installed_as']; - continue; - } elseif (!preg_match('/\.phpt\\z/', $name)) { - continue; - } - $params[] = $atts['installed_as']; - } - } - } - - foreach ($params as $p) { - if (is_dir($p)) { - if (isset($options['phpunit'])) { - $dir = System::find(array($p, '-type', 'f', - '-maxdepth', $depth, - '-name', 'AllTests.php')); - if (count($dir)) { - foreach ($dir as $p) { - $p = realpath($p); - if (!count($tests) || - (count($tests) && strlen($p) < strlen($tests[0]))) { - // this is in a higher-level directory, use this one instead. - $tests = array($p); - } - } - } - continue; - } - - $args = array($p, '-type', 'f', '-name', '*.phpt'); - } else { - if (isset($options['phpunit'])) { - if (preg_match('/AllTests\.php\\z/i', $p)) { - $p = realpath($p); - if (!count($tests) || - (count($tests) && strlen($p) < strlen($tests[0]))) { - // this is in a higher-level directory, use this one instead. - $tests = array($p); - } - } - continue; - } - - if (file_exists($p) && preg_match('/\.phpt$/', $p)) { - $tests[] = $p; - continue; - } - - if (!preg_match('/\.phpt\\z/', $p)) { - $p .= '.phpt'; - } - - $args = array(dirname($p), '-type', 'f', '-name', $p); - } - - if (!isset($options['recur'])) { - $args[] = '-maxdepth'; - $args[] = 1; - } - - $dir = System::find($args); - $tests = array_merge($tests, $dir); - } - - $ini_settings = ''; - if (isset($options['ini'])) { - $ini_settings .= $options['ini']; - } - - if (isset($_ENV['TEST_PHP_INCLUDE_PATH'])) { - $ini_settings .= " -d include_path={$_ENV['TEST_PHP_INCLUDE_PATH']}"; - } - - if ($ini_settings) { - $this->ui->outputData('Using INI settings: "' . $ini_settings . '"'); - } - - $skipped = $passed = $failed = array(); - $tests_count = count($tests); - $this->ui->outputData('Running ' . $tests_count . ' tests', $command); - $start = time(); - if (isset($options['realtimelog']) && file_exists('run-tests.log')) { - unlink('run-tests.log'); - } - - if (isset($options['tapoutput'])) { - $tap = '1..' . $tests_count . "\n"; - } - - require_once 'PEAR/RunTest.php'; - $run = new PEAR_RunTest($log, $options); - $run->tests_count = $tests_count; - - if (isset($options['coverage']) && extension_loaded('xdebug')){ - $run->xdebug_loaded = true; - } else { - $run->xdebug_loaded = false; - } - - $j = $i = 1; - foreach ($tests as $t) { - if (isset($options['realtimelog'])) { - $fp = @fopen('run-tests.log', 'a'); - if ($fp) { - fwrite($fp, "Running test [$i / $tests_count] $t..."); - fclose($fp); - } - } - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - if (isset($options['phpunit'])) { - $result = $run->runPHPUnit($t, $ini_settings); - } else { - $result = $run->run($t, $ini_settings, $j); - } - PEAR::staticPopErrorHandling(); - if (PEAR::isError($result)) { - $this->ui->log($result->getMessage()); - continue; - } - - if (isset($options['tapoutput'])) { - $tap .= $result[0] . ' ' . $i . $result[1] . "\n"; - continue; - } - - if (isset($options['realtimelog'])) { - $fp = @fopen('run-tests.log', 'a'); - if ($fp) { - fwrite($fp, "$result\n"); - fclose($fp); - } - } - - if ($result == 'FAILED') { - $failed[] = $t; - } - if ($result == 'PASSED') { - $passed[] = $t; - } - if ($result == 'SKIPPED') { - $skipped[] = $t; - } - - $j++; - } - - $total = date('i:s', time() - $start); - if (isset($options['tapoutput'])) { - $fp = @fopen('run-tests.log', 'w'); - if ($fp) { - fwrite($fp, $tap, strlen($tap)); - fclose($fp); - $this->ui->outputData('wrote TAP-format log to "' .realpath('run-tests.log') . - '"', $command); - } - } else { - if (count($failed)) { - $output = "TOTAL TIME: $total\n"; - $output .= count($passed) . " PASSED TESTS\n"; - $output .= count($skipped) . " SKIPPED TESTS\n"; - $output .= count($failed) . " FAILED TESTS:\n"; - foreach ($failed as $failure) { - $output .= $failure . "\n"; - } - - $mode = isset($options['realtimelog']) ? 'a' : 'w'; - $fp = @fopen('run-tests.log', $mode); - - if ($fp) { - fwrite($fp, $output, strlen($output)); - fclose($fp); - $this->ui->outputData('wrote log to "' . realpath('run-tests.log') . '"', $command); - } - } elseif (file_exists('run-tests.log') && !is_dir('run-tests.log')) { - @unlink('run-tests.log'); - } - } - $this->ui->outputData('TOTAL TIME: ' . $total); - $this->ui->outputData(count($passed) . ' PASSED TESTS', $command); - $this->ui->outputData(count($skipped) . ' SKIPPED TESTS', $command); - if (count($failed)) { - $this->ui->outputData(count($failed) . ' FAILED TESTS:', $command); - foreach ($failed as $failure) { - $this->ui->outputData($failure, $command); - } - } - - return true; - } -} \ No newline at end of file diff --git a/pear/PEAR/Command/Test.xml b/pear/PEAR/Command/Test.xml deleted file mode 100644 index bbe9fcc..0000000 --- a/pear/PEAR/Command/Test.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - Run Regression Tests - doRunTests - rt - - - r - Run tests in child directories, recursively. 4 dirs deep maximum - - - i - actual string of settings to pass to php in format " -d setting=blah" - SETTINGS - - - l - Log test runs/results as they are run - - - q - Only display detail for failed tests - - - s - Display simple output for all tests - - - p - Treat parameters as installed packages from which to run tests - - - u - Search parameters for AllTests.php, and use that to run phpunit-based tests -If none is found, all .phpt tests will be tried instead. - - - t - Output run-tests.log in TAP-compliant format - - - c - CGI php executable (needed for tests with POST/GET section) - PHPCGI - - - x - Generate a code coverage report (requires Xdebug 2.0.0+) - - - [testfile|dir ...] -Run regression tests with PHP's regression testing script (run-tests.php). - - \ No newline at end of file diff --git a/pear/PEAR/Common.php b/pear/PEAR/Common.php deleted file mode 100644 index f98bf2c..0000000 --- a/pear/PEAR/Common.php +++ /dev/null @@ -1,837 +0,0 @@ - - * @author Tomas V. V. Cox - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 0.1.0 - * @deprecated File deprecated since Release 1.4.0a1 - */ - -/** - * Include error handling - */ -require_once 'PEAR.php'; - -/** - * PEAR_Common error when an invalid PHP file is passed to PEAR_Common::analyzeSourceCode() - */ -define('PEAR_COMMON_ERROR_INVALIDPHP', 1); -define('_PEAR_COMMON_PACKAGE_NAME_PREG', '[A-Za-z][a-zA-Z0-9_]+'); -define('PEAR_COMMON_PACKAGE_NAME_PREG', '/^' . _PEAR_COMMON_PACKAGE_NAME_PREG . '\\z/'); - -// this should allow: 1, 1.0, 1.0RC1, 1.0dev, 1.0dev123234234234, 1.0a1, 1.0b1, 1.0pl1 -define('_PEAR_COMMON_PACKAGE_VERSION_PREG', '\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?'); -define('PEAR_COMMON_PACKAGE_VERSION_PREG', '/^' . _PEAR_COMMON_PACKAGE_VERSION_PREG . '\\z/i'); - -// XXX far from perfect :-) -define('_PEAR_COMMON_PACKAGE_DOWNLOAD_PREG', '(' . _PEAR_COMMON_PACKAGE_NAME_PREG . - ')(-([.0-9a-zA-Z]+))?'); -define('PEAR_COMMON_PACKAGE_DOWNLOAD_PREG', '/^' . _PEAR_COMMON_PACKAGE_DOWNLOAD_PREG . - '\\z/'); - -define('_PEAR_CHANNELS_NAME_PREG', '[A-Za-z][a-zA-Z0-9\.]+'); -define('PEAR_CHANNELS_NAME_PREG', '/^' . _PEAR_CHANNELS_NAME_PREG . '\\z/'); - -// this should allow any dns or IP address, plus a path - NO UNDERSCORES ALLOWED -define('_PEAR_CHANNELS_SERVER_PREG', '[a-zA-Z0-9\-]+(?:\.[a-zA-Z0-9\-]+)*(\/[a-zA-Z0-9\-]+)*'); -define('PEAR_CHANNELS_SERVER_PREG', '/^' . _PEAR_CHANNELS_SERVER_PREG . '\\z/i'); - -define('_PEAR_CHANNELS_PACKAGE_PREG', '(' ._PEAR_CHANNELS_SERVER_PREG . ')\/(' - . _PEAR_COMMON_PACKAGE_NAME_PREG . ')'); -define('PEAR_CHANNELS_PACKAGE_PREG', '/^' . _PEAR_CHANNELS_PACKAGE_PREG . '\\z/i'); - -define('_PEAR_COMMON_CHANNEL_DOWNLOAD_PREG', '(' . _PEAR_CHANNELS_NAME_PREG . ')::(' - . _PEAR_COMMON_PACKAGE_NAME_PREG . ')(-([.0-9a-zA-Z]+))?'); -define('PEAR_COMMON_CHANNEL_DOWNLOAD_PREG', '/^' . _PEAR_COMMON_CHANNEL_DOWNLOAD_PREG . '\\z/'); - -/** - * List of temporary files and directories registered by - * PEAR_Common::addTempFile(). - * @var array - */ -$GLOBALS['_PEAR_Common_tempfiles'] = array(); - -/** - * Valid maintainer roles - * @var array - */ -$GLOBALS['_PEAR_Common_maintainer_roles'] = array('lead','developer','contributor','helper'); - -/** - * Valid release states - * @var array - */ -$GLOBALS['_PEAR_Common_release_states'] = array('alpha','beta','stable','snapshot','devel'); - -/** - * Valid dependency types - * @var array - */ -$GLOBALS['_PEAR_Common_dependency_types'] = array('pkg','ext','php','prog','ldlib','rtlib','os','websrv','sapi'); - -/** - * Valid dependency relations - * @var array - */ -$GLOBALS['_PEAR_Common_dependency_relations'] = array('has','eq','lt','le','gt','ge','not', 'ne'); - -/** - * Valid file roles - * @var array - */ -$GLOBALS['_PEAR_Common_file_roles'] = array('php','ext','test','doc','data','src','script'); - -/** - * Valid replacement types - * @var array - */ -$GLOBALS['_PEAR_Common_replacement_types'] = array('php-const', 'pear-config', 'package-info'); - -/** - * Valid "provide" types - * @var array - */ -$GLOBALS['_PEAR_Common_provide_types'] = array('ext', 'prog', 'class', 'function', 'feature', 'api'); - -/** - * Valid "provide" types - * @var array - */ -$GLOBALS['_PEAR_Common_script_phases'] = array('pre-install', 'post-install', 'pre-uninstall', 'post-uninstall', 'pre-build', 'post-build', 'pre-configure', 'post-configure', 'pre-setup', 'post-setup'); - -/** - * Class providing common functionality for PEAR administration classes. - * @category pear - * @package PEAR - * @author Stig Bakken - * @author Tomas V. V. Cox - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a1 - * @deprecated This class will disappear, and its components will be spread - * into smaller classes, like the AT&T breakup, as of Release 1.4.0a1 - */ -class PEAR_Common extends PEAR -{ - /** - * User Interface object (PEAR_Frontend_* class). If null, - * the log() method uses print. - * @var object - */ - var $ui = null; - - /** - * Configuration object (PEAR_Config). - * @var PEAR_Config - */ - var $config = null; - - /** stack of elements, gives some sort of XML context */ - var $element_stack = array(); - - /** name of currently parsed XML element */ - var $current_element; - - /** array of attributes of the currently parsed XML element */ - var $current_attributes = array(); - - /** assoc with information about a package */ - var $pkginfo = array(); - - var $current_path = null; - - /** - * Flag variable used to mark a valid package file - * @var boolean - * @access private - */ - var $_validPackageFile; - - /** - * PEAR_Common constructor - * - * @access public - */ - function PEAR_Common() - { - parent::PEAR(); - $this->config = &PEAR_Config::singleton(); - $this->debug = $this->config->get('verbose'); - } - - /** - * PEAR_Common destructor - * - * @access private - */ - function _PEAR_Common() - { - // doesn't work due to bug #14744 - //$tempfiles = $this->_tempfiles; - $tempfiles =& $GLOBALS['_PEAR_Common_tempfiles']; - while ($file = array_shift($tempfiles)) { - if (@is_dir($file)) { - if (!class_exists('System')) { - require_once 'System.php'; - } - - System::rm(array('-rf', $file)); - } elseif (file_exists($file)) { - unlink($file); - } - } - } - - /** - * Register a temporary file or directory. When the destructor is - * executed, all registered temporary files and directories are - * removed. - * - * @param string $file name of file or directory - * - * @return void - * - * @access public - */ - function addTempFile($file) - { - if (!class_exists('PEAR_Frontend')) { - require_once 'PEAR/Frontend.php'; - } - PEAR_Frontend::addTempFile($file); - } - - /** - * Wrapper to System::mkDir(), creates a directory as well as - * any necessary parent directories. - * - * @param string $dir directory name - * - * @return bool TRUE on success, or a PEAR error - * - * @access public - */ - function mkDirHier($dir) - { - // Only used in Installer - move it there ? - $this->log(2, "+ create dir $dir"); - if (!class_exists('System')) { - require_once 'System.php'; - } - return System::mkDir(array('-p', $dir)); - } - - /** - * Logging method. - * - * @param int $level log level (0 is quiet, higher is noisier) - * @param string $msg message to write to the log - * - * @return void - * - * @access public - * @static - */ - function log($level, $msg, $append_crlf = true) - { - if ($this->debug >= $level) { - if (!class_exists('PEAR_Frontend')) { - require_once 'PEAR/Frontend.php'; - } - - $ui = &PEAR_Frontend::singleton(); - if (is_a($ui, 'PEAR_Frontend')) { - $ui->log($msg, $append_crlf); - } else { - print "$msg\n"; - } - } - } - - /** - * Create and register a temporary directory. - * - * @param string $tmpdir (optional) Directory to use as tmpdir. - * Will use system defaults (for example - * /tmp or c:\windows\temp) if not specified - * - * @return string name of created directory - * - * @access public - */ - function mkTempDir($tmpdir = '') - { - $topt = $tmpdir ? array('-t', $tmpdir) : array(); - $topt = array_merge($topt, array('-d', 'pear')); - if (!class_exists('System')) { - require_once 'System.php'; - } - - if (!$tmpdir = System::mktemp($topt)) { - return false; - } - - $this->addTempFile($tmpdir); - return $tmpdir; - } - - /** - * Set object that represents the frontend to be used. - * - * @param object Reference of the frontend object - * @return void - * @access public - */ - function setFrontendObject(&$ui) - { - $this->ui = &$ui; - } - - /** - * Return an array containing all of the states that are more stable than - * or equal to the passed in state - * - * @param string Release state - * @param boolean Determines whether to include $state in the list - * @return false|array False if $state is not a valid release state - */ - function betterStates($state, $include = false) - { - static $states = array('snapshot', 'devel', 'alpha', 'beta', 'stable'); - $i = array_search($state, $states); - if ($i === false) { - return false; - } - if ($include) { - $i--; - } - return array_slice($states, $i + 1); - } - - /** - * Get the valid roles for a PEAR package maintainer - * - * @return array - * @static - */ - function getUserRoles() - { - return $GLOBALS['_PEAR_Common_maintainer_roles']; - } - - /** - * Get the valid package release states of packages - * - * @return array - * @static - */ - function getReleaseStates() - { - return $GLOBALS['_PEAR_Common_release_states']; - } - - /** - * Get the implemented dependency types (php, ext, pkg etc.) - * - * @return array - * @static - */ - function getDependencyTypes() - { - return $GLOBALS['_PEAR_Common_dependency_types']; - } - - /** - * Get the implemented dependency relations (has, lt, ge etc.) - * - * @return array - * @static - */ - function getDependencyRelations() - { - return $GLOBALS['_PEAR_Common_dependency_relations']; - } - - /** - * Get the implemented file roles - * - * @return array - * @static - */ - function getFileRoles() - { - return $GLOBALS['_PEAR_Common_file_roles']; - } - - /** - * Get the implemented file replacement types in - * - * @return array - * @static - */ - function getReplacementTypes() - { - return $GLOBALS['_PEAR_Common_replacement_types']; - } - - /** - * Get the implemented file replacement types in - * - * @return array - * @static - */ - function getProvideTypes() - { - return $GLOBALS['_PEAR_Common_provide_types']; - } - - /** - * Get the implemented file replacement types in - * - * @return array - * @static - */ - function getScriptPhases() - { - return $GLOBALS['_PEAR_Common_script_phases']; - } - - /** - * Test whether a string contains a valid package name. - * - * @param string $name the package name to test - * - * @return bool - * - * @access public - */ - function validPackageName($name) - { - return (bool)preg_match(PEAR_COMMON_PACKAGE_NAME_PREG, $name); - } - - /** - * Test whether a string contains a valid package version. - * - * @param string $ver the package version to test - * - * @return bool - * - * @access public - */ - function validPackageVersion($ver) - { - return (bool)preg_match(PEAR_COMMON_PACKAGE_VERSION_PREG, $ver); - } - - /** - * @param string $path relative or absolute include path - * @return boolean - * @static - */ - function isIncludeable($path) - { - if (file_exists($path) && is_readable($path)) { - return true; - } - - $ipath = explode(PATH_SEPARATOR, ini_get('include_path')); - foreach ($ipath as $include) { - $test = realpath($include . DIRECTORY_SEPARATOR . $path); - if (file_exists($test) && is_readable($test)) { - return true; - } - } - - return false; - } - - function _postProcessChecks($pf) - { - if (!PEAR::isError($pf)) { - return $this->_postProcessValidPackagexml($pf); - } - - $errs = $pf->getUserinfo(); - if (is_array($errs)) { - foreach ($errs as $error) { - $e = $this->raiseError($error['message'], $error['code'], null, null, $error); - } - } - - return $pf; - } - - /** - * Returns information about a package file. Expects the name of - * a gzipped tar file as input. - * - * @param string $file name of .tgz file - * - * @return array array with package information - * - * @access public - * @deprecated use PEAR_PackageFile->fromTgzFile() instead - * - */ - function infoFromTgzFile($file) - { - $packagefile = &new PEAR_PackageFile($this->config); - $pf = &$packagefile->fromTgzFile($file, PEAR_VALIDATE_NORMAL); - return $this->_postProcessChecks($pf); - } - - /** - * Returns information about a package file. Expects the name of - * a package xml file as input. - * - * @param string $descfile name of package xml file - * - * @return array array with package information - * - * @access public - * @deprecated use PEAR_PackageFile->fromPackageFile() instead - * - */ - function infoFromDescriptionFile($descfile) - { - $packagefile = &new PEAR_PackageFile($this->config); - $pf = &$packagefile->fromPackageFile($descfile, PEAR_VALIDATE_NORMAL); - return $this->_postProcessChecks($pf); - } - - /** - * Returns information about a package file. Expects the contents - * of a package xml file as input. - * - * @param string $data contents of package.xml file - * - * @return array array with package information - * - * @access public - * @deprecated use PEAR_PackageFile->fromXmlstring() instead - * - */ - function infoFromString($data) - { - $packagefile = &new PEAR_PackageFile($this->config); - $pf = &$packagefile->fromXmlString($data, PEAR_VALIDATE_NORMAL, false); - return $this->_postProcessChecks($pf); - } - - /** - * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 - * @return array - */ - function _postProcessValidPackagexml(&$pf) - { - if (!is_a($pf, 'PEAR_PackageFile_v2')) { - $this->pkginfo = $pf->toArray(); - return $this->pkginfo; - } - - // sort of make this into a package.xml 1.0-style array - // changelog is not converted to old format. - $arr = $pf->toArray(true); - $arr = array_merge($arr, $arr['old']); - unset($arr['old'], $arr['xsdversion'], $arr['contents'], $arr['compatible'], - $arr['channel'], $arr['uri'], $arr['dependencies'], $arr['phprelease'], - $arr['extsrcrelease'], $arr['zendextsrcrelease'], $arr['extbinrelease'], - $arr['zendextbinrelease'], $arr['bundle'], $arr['lead'], $arr['developer'], - $arr['helper'], $arr['contributor']); - $arr['filelist'] = $pf->getFilelist(); - $this->pkginfo = $arr; - return $arr; - } - - /** - * Returns package information from different sources - * - * This method is able to extract information about a package - * from a .tgz archive or from a XML package definition file. - * - * @access public - * @param string Filename of the source ('package.xml', '.tgz') - * @return string - * @deprecated use PEAR_PackageFile->fromAnyFile() instead - */ - function infoFromAny($info) - { - if (is_string($info) && file_exists($info)) { - $packagefile = &new PEAR_PackageFile($this->config); - $pf = &$packagefile->fromAnyFile($info, PEAR_VALIDATE_NORMAL); - if (PEAR::isError($pf)) { - $errs = $pf->getUserinfo(); - if (is_array($errs)) { - foreach ($errs as $error) { - $e = $this->raiseError($error['message'], $error['code'], null, null, $error); - } - } - - return $pf; - } - - return $this->_postProcessValidPackagexml($pf); - } - - return $info; - } - - /** - * Return an XML document based on the package info (as returned - * by the PEAR_Common::infoFrom* methods). - * - * @param array $pkginfo package info - * - * @return string XML data - * - * @access public - * @deprecated use a PEAR_PackageFile_v* object's generator instead - */ - function xmlFromInfo($pkginfo) - { - $config = &PEAR_Config::singleton(); - $packagefile = &new PEAR_PackageFile($config); - $pf = &$packagefile->fromArray($pkginfo); - $gen = &$pf->getDefaultGenerator(); - return $gen->toXml(PEAR_VALIDATE_PACKAGING); - } - - /** - * Validate XML package definition file. - * - * @param string $info Filename of the package archive or of the - * package definition file - * @param array $errors Array that will contain the errors - * @param array $warnings Array that will contain the warnings - * @param string $dir_prefix (optional) directory where source files - * may be found, or empty if they are not available - * @access public - * @return boolean - * @deprecated use the validation of PEAR_PackageFile objects - */ - function validatePackageInfo($info, &$errors, &$warnings, $dir_prefix = '') - { - $config = &PEAR_Config::singleton(); - $packagefile = &new PEAR_PackageFile($config); - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - if (strpos($info, 'fromXmlString($info, PEAR_VALIDATE_NORMAL, ''); - } else { - $pf = &$packagefile->fromAnyFile($info, PEAR_VALIDATE_NORMAL); - } - - PEAR::staticPopErrorHandling(); - if (PEAR::isError($pf)) { - $errs = $pf->getUserinfo(); - if (is_array($errs)) { - foreach ($errs as $error) { - if ($error['level'] == 'error') { - $errors[] = $error['message']; - } else { - $warnings[] = $error['message']; - } - } - } - - return false; - } - - return true; - } - - /** - * Build a "provides" array from data returned by - * analyzeSourceCode(). The format of the built array is like - * this: - * - * array( - * 'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'), - * ... - * ) - * - * - * @param array $srcinfo array with information about a source file - * as returned by the analyzeSourceCode() method. - * - * @return void - * - * @access public - * - */ - function buildProvidesArray($srcinfo) - { - $file = basename($srcinfo['source_file']); - $pn = ''; - if (isset($this->_packageName)) { - $pn = $this->_packageName; - } - - $pnl = strlen($pn); - foreach ($srcinfo['declared_classes'] as $class) { - $key = "class;$class"; - if (isset($this->pkginfo['provides'][$key])) { - continue; - } - - $this->pkginfo['provides'][$key] = - array('file'=> $file, 'type' => 'class', 'name' => $class); - if (isset($srcinfo['inheritance'][$class])) { - $this->pkginfo['provides'][$key]['extends'] = - $srcinfo['inheritance'][$class]; - } - } - - foreach ($srcinfo['declared_methods'] as $class => $methods) { - foreach ($methods as $method) { - $function = "$class::$method"; - $key = "function;$function"; - if ($method{0} == '_' || !strcasecmp($method, $class) || - isset($this->pkginfo['provides'][$key])) { - continue; - } - - $this->pkginfo['provides'][$key] = - array('file'=> $file, 'type' => 'function', 'name' => $function); - } - } - - foreach ($srcinfo['declared_functions'] as $function) { - $key = "function;$function"; - if ($function{0} == '_' || isset($this->pkginfo['provides'][$key])) { - continue; - } - - if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) { - $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\""; - } - - $this->pkginfo['provides'][$key] = - array('file'=> $file, 'type' => 'function', 'name' => $function); - } - } - - /** - * Analyze the source code of the given PHP file - * - * @param string Filename of the PHP file - * @return mixed - * @access public - */ - function analyzeSourceCode($file) - { - if (!class_exists('PEAR_PackageFile_v2_Validator')) { - require_once 'PEAR/PackageFile/v2/Validator.php'; - } - - $a = new PEAR_PackageFile_v2_Validator; - return $a->analyzeSourceCode($file); - } - - function detectDependencies($any, $status_callback = null) - { - if (!function_exists("token_get_all")) { - return false; - } - - if (PEAR::isError($info = $this->infoFromAny($any))) { - return $this->raiseError($info); - } - - if (!is_array($info)) { - return false; - } - - $deps = array(); - $used_c = $decl_c = $decl_f = $decl_m = array(); - foreach ($info['filelist'] as $file => $fa) { - $tmp = $this->analyzeSourceCode($file); - $used_c = @array_merge($used_c, $tmp['used_classes']); - $decl_c = @array_merge($decl_c, $tmp['declared_classes']); - $decl_f = @array_merge($decl_f, $tmp['declared_functions']); - $decl_m = @array_merge($decl_m, $tmp['declared_methods']); - $inheri = @array_merge($inheri, $tmp['inheritance']); - } - - $used_c = array_unique($used_c); - $decl_c = array_unique($decl_c); - $undecl_c = array_diff($used_c, $decl_c); - - return array('used_classes' => $used_c, - 'declared_classes' => $decl_c, - 'declared_methods' => $decl_m, - 'declared_functions' => $decl_f, - 'undeclared_classes' => $undecl_c, - 'inheritance' => $inheri, - ); - } - - /** - * Download a file through HTTP. Considers suggested file name in - * Content-disposition: header and can run a callback function for - * different events. The callback will be called with two - * parameters: the callback type, and parameters. The implemented - * callback types are: - * - * 'setup' called at the very beginning, parameter is a UI object - * that should be used for all output - * 'message' the parameter is a string with an informational message - * 'saveas' may be used to save with a different file name, the - * parameter is the filename that is about to be used. - * If a 'saveas' callback returns a non-empty string, - * that file name will be used as the filename instead. - * Note that $save_dir will not be affected by this, only - * the basename of the file. - * 'start' download is starting, parameter is number of bytes - * that are expected, or -1 if unknown - * 'bytesread' parameter is the number of bytes read so far - * 'done' download is complete, parameter is the total number - * of bytes read - * 'connfailed' if the TCP connection fails, this callback is called - * with array(host,port,errno,errmsg) - * 'writefailed' if writing to disk fails, this callback is called - * with array(destfile,errmsg) - * - * If an HTTP proxy has been configured (http_proxy PEAR_Config - * setting), the proxy will be used. - * - * @param string $url the URL to download - * @param object $ui PEAR_Frontend_* instance - * @param object $config PEAR_Config instance - * @param string $save_dir (optional) directory to save file in - * @param mixed $callback (optional) function/method to call for status - * updates - * - * @return string Returns the full path of the downloaded file or a PEAR - * error on failure. If the error is caused by - * socket-related errors, the error object will - * have the fsockopen error code available through - * getCode(). - * - * @access public - * @deprecated in favor of PEAR_Downloader::downloadHttp() - */ - function downloadHttp($url, &$ui, $save_dir = '.', $callback = null) - { - if (!class_exists('PEAR_Downloader')) { - require_once 'PEAR/Downloader.php'; - } - return PEAR_Downloader::downloadHttp($url, $ui, $save_dir, $callback); - } -} - -require_once 'PEAR/Config.php'; -require_once 'PEAR/PackageFile.php'; \ No newline at end of file diff --git a/pear/PEAR/Config.php b/pear/PEAR/Config.php deleted file mode 100644 index f6fb9ee..0000000 --- a/pear/PEAR/Config.php +++ /dev/null @@ -1,2092 +0,0 @@ - - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 0.1 - */ - -/** - * Required for error handling - */ -require_once 'PEAR.php'; -require_once 'PEAR/Registry.php'; -require_once 'PEAR/Installer/Role.php'; -require_once 'System.php'; - -/** - * Last created PEAR_Config instance. - * @var object - */ -$GLOBALS['_PEAR_Config_instance'] = null; -if (!defined('PEAR_INSTALL_DIR') || !PEAR_INSTALL_DIR) { - $PEAR_INSTALL_DIR = PHP_LIBDIR . DIRECTORY_SEPARATOR . 'pear'; -} else { - $PEAR_INSTALL_DIR = PEAR_INSTALL_DIR; -} - -// Below we define constants with default values for all configuration -// parameters except username/password. All of them can have their -// defaults set through environment variables. The reason we use the -// PHP_ prefix is for some security, PHP protects environment -// variables starting with PHP_*. - -// default channel and preferred mirror is based on whether we are invoked through -// the "pear" or the "pecl" command -if (!defined('PEAR_RUNTYPE')) { - define('PEAR_RUNTYPE', 'pear'); -} - -if (PEAR_RUNTYPE == 'pear') { - define('PEAR_CONFIG_DEFAULT_CHANNEL', 'pear.php.net'); -} else { - define('PEAR_CONFIG_DEFAULT_CHANNEL', 'pecl.php.net'); -} - -if (getenv('PHP_PEAR_SYSCONF_DIR')) { - define('PEAR_CONFIG_SYSCONFDIR', getenv('PHP_PEAR_SYSCONF_DIR')); -} elseif (getenv('SystemRoot')) { - define('PEAR_CONFIG_SYSCONFDIR', getenv('SystemRoot')); -} else { - define('PEAR_CONFIG_SYSCONFDIR', PHP_SYSCONFDIR); -} - -// Default for master_server -if (getenv('PHP_PEAR_MASTER_SERVER')) { - define('PEAR_CONFIG_DEFAULT_MASTER_SERVER', getenv('PHP_PEAR_MASTER_SERVER')); -} else { - define('PEAR_CONFIG_DEFAULT_MASTER_SERVER', 'pear.php.net'); -} - -// Default for http_proxy -if (getenv('PHP_PEAR_HTTP_PROXY')) { - define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', getenv('PHP_PEAR_HTTP_PROXY')); -} elseif (getenv('http_proxy')) { - define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', getenv('http_proxy')); -} else { - define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', ''); -} - -// Default for php_dir -if (getenv('PHP_PEAR_INSTALL_DIR')) { - define('PEAR_CONFIG_DEFAULT_PHP_DIR', getenv('PHP_PEAR_INSTALL_DIR')); -} else { - if (@file_exists($PEAR_INSTALL_DIR) && is_dir($PEAR_INSTALL_DIR)) { - define('PEAR_CONFIG_DEFAULT_PHP_DIR', $PEAR_INSTALL_DIR); - } else { - define('PEAR_CONFIG_DEFAULT_PHP_DIR', $PEAR_INSTALL_DIR); - } -} - -// Default for ext_dir -if (getenv('PHP_PEAR_EXTENSION_DIR')) { - define('PEAR_CONFIG_DEFAULT_EXT_DIR', getenv('PHP_PEAR_EXTENSION_DIR')); -} else { - if (ini_get('extension_dir')) { - define('PEAR_CONFIG_DEFAULT_EXT_DIR', ini_get('extension_dir')); - } elseif (defined('PEAR_EXTENSION_DIR') && - file_exists(PEAR_EXTENSION_DIR) && is_dir(PEAR_EXTENSION_DIR)) { - define('PEAR_CONFIG_DEFAULT_EXT_DIR', PEAR_EXTENSION_DIR); - } elseif (defined('PHP_EXTENSION_DIR')) { - define('PEAR_CONFIG_DEFAULT_EXT_DIR', PHP_EXTENSION_DIR); - } else { - define('PEAR_CONFIG_DEFAULT_EXT_DIR', '.'); - } -} - -// Default for doc_dir -if (getenv('PHP_PEAR_DOC_DIR')) { - define('PEAR_CONFIG_DEFAULT_DOC_DIR', getenv('PHP_PEAR_DOC_DIR')); -} else { - define('PEAR_CONFIG_DEFAULT_DOC_DIR', - $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'docs'); -} - -// Default for bin_dir -if (getenv('PHP_PEAR_BIN_DIR')) { - define('PEAR_CONFIG_DEFAULT_BIN_DIR', getenv('PHP_PEAR_BIN_DIR')); -} else { - define('PEAR_CONFIG_DEFAULT_BIN_DIR', PHP_BINDIR); -} - -// Default for data_dir -if (getenv('PHP_PEAR_DATA_DIR')) { - define('PEAR_CONFIG_DEFAULT_DATA_DIR', getenv('PHP_PEAR_DATA_DIR')); -} else { - define('PEAR_CONFIG_DEFAULT_DATA_DIR', - $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'data'); -} - -// Default for cfg_dir -if (getenv('PHP_PEAR_CFG_DIR')) { - define('PEAR_CONFIG_DEFAULT_CFG_DIR', getenv('PHP_PEAR_CFG_DIR')); -} else { - define('PEAR_CONFIG_DEFAULT_CFG_DIR', - $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'cfg'); -} - -// Default for www_dir -if (getenv('PHP_PEAR_WWW_DIR')) { - define('PEAR_CONFIG_DEFAULT_WWW_DIR', getenv('PHP_PEAR_WWW_DIR')); -} else { - define('PEAR_CONFIG_DEFAULT_WWW_DIR', - $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'www'); -} - -// Default for test_dir -if (getenv('PHP_PEAR_TEST_DIR')) { - define('PEAR_CONFIG_DEFAULT_TEST_DIR', getenv('PHP_PEAR_TEST_DIR')); -} else { - define('PEAR_CONFIG_DEFAULT_TEST_DIR', - $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'tests'); -} - -// Default for temp_dir -if (getenv('PHP_PEAR_TEMP_DIR')) { - define('PEAR_CONFIG_DEFAULT_TEMP_DIR', getenv('PHP_PEAR_TEMP_DIR')); -} else { - define('PEAR_CONFIG_DEFAULT_TEMP_DIR', - System::tmpdir() . DIRECTORY_SEPARATOR . 'pear' . - DIRECTORY_SEPARATOR . 'temp'); -} - -// Default for cache_dir -if (getenv('PHP_PEAR_CACHE_DIR')) { - define('PEAR_CONFIG_DEFAULT_CACHE_DIR', getenv('PHP_PEAR_CACHE_DIR')); -} else { - define('PEAR_CONFIG_DEFAULT_CACHE_DIR', - System::tmpdir() . DIRECTORY_SEPARATOR . 'pear' . - DIRECTORY_SEPARATOR . 'cache'); -} - -// Default for download_dir -if (getenv('PHP_PEAR_DOWNLOAD_DIR')) { - define('PEAR_CONFIG_DEFAULT_DOWNLOAD_DIR', getenv('PHP_PEAR_DOWNLOAD_DIR')); -} else { - define('PEAR_CONFIG_DEFAULT_DOWNLOAD_DIR', - System::tmpdir() . DIRECTORY_SEPARATOR . 'pear' . - DIRECTORY_SEPARATOR . 'download'); -} - -// Default for php_bin -if (getenv('PHP_PEAR_PHP_BIN')) { - define('PEAR_CONFIG_DEFAULT_PHP_BIN', getenv('PHP_PEAR_PHP_BIN')); -} else { - define('PEAR_CONFIG_DEFAULT_PHP_BIN', PEAR_CONFIG_DEFAULT_BIN_DIR. - DIRECTORY_SEPARATOR.'php'.(OS_WINDOWS ? '.exe' : '')); -} - -// Default for verbose -if (getenv('PHP_PEAR_VERBOSE')) { - define('PEAR_CONFIG_DEFAULT_VERBOSE', getenv('PHP_PEAR_VERBOSE')); -} else { - define('PEAR_CONFIG_DEFAULT_VERBOSE', 1); -} - -// Default for preferred_state -if (getenv('PHP_PEAR_PREFERRED_STATE')) { - define('PEAR_CONFIG_DEFAULT_PREFERRED_STATE', getenv('PHP_PEAR_PREFERRED_STATE')); -} else { - define('PEAR_CONFIG_DEFAULT_PREFERRED_STATE', 'stable'); -} - -// Default for umask -if (getenv('PHP_PEAR_UMASK')) { - define('PEAR_CONFIG_DEFAULT_UMASK', getenv('PHP_PEAR_UMASK')); -} else { - define('PEAR_CONFIG_DEFAULT_UMASK', decoct(umask())); -} - -// Default for cache_ttl -if (getenv('PHP_PEAR_CACHE_TTL')) { - define('PEAR_CONFIG_DEFAULT_CACHE_TTL', getenv('PHP_PEAR_CACHE_TTL')); -} else { - define('PEAR_CONFIG_DEFAULT_CACHE_TTL', 3600); -} - -// Default for sig_type -if (getenv('PHP_PEAR_SIG_TYPE')) { - define('PEAR_CONFIG_DEFAULT_SIG_TYPE', getenv('PHP_PEAR_SIG_TYPE')); -} else { - define('PEAR_CONFIG_DEFAULT_SIG_TYPE', 'gpg'); -} - -// Default for sig_bin -if (getenv('PHP_PEAR_SIG_BIN')) { - define('PEAR_CONFIG_DEFAULT_SIG_BIN', getenv('PHP_PEAR_SIG_BIN')); -} else { - define('PEAR_CONFIG_DEFAULT_SIG_BIN', - System::which( - 'gpg', OS_WINDOWS ? 'c:\gnupg\gpg.exe' : '/usr/local/bin/gpg')); -} - -// Default for sig_keydir -if (getenv('PHP_PEAR_SIG_KEYDIR')) { - define('PEAR_CONFIG_DEFAULT_SIG_KEYDIR', getenv('PHP_PEAR_SIG_KEYDIR')); -} else { - define('PEAR_CONFIG_DEFAULT_SIG_KEYDIR', - PEAR_CONFIG_SYSCONFDIR . DIRECTORY_SEPARATOR . 'pearkeys'); -} - -/** - * This is a class for storing configuration data, keeping track of - * which are system-defined, user-defined or defaulted. - * @category pear - * @package PEAR - * @author Stig Bakken - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 0.1 - */ -class PEAR_Config extends PEAR -{ - /** - * Array of config files used. - * - * @var array layer => config file - */ - var $files = array( - 'system' => '', - 'user' => '', - ); - - var $layers = array(); - - /** - * Configuration data, two-dimensional array where the first - * dimension is the config layer ('user', 'system' and 'default'), - * and the second dimension is keyname => value. - * - * The order in the first dimension is important! Earlier - * layers will shadow later ones when a config value is - * requested (if a 'user' value exists, it will be returned first, - * then 'system' and finally 'default'). - * - * @var array layer => array(keyname => value, ...) - */ - var $configuration = array( - 'user' => array(), - 'system' => array(), - 'default' => array(), - ); - - /** - * Configuration values that can be set for a channel - * - * All other configuration values can only have a global value - * @var array - * @access private - */ - var $_channelConfigInfo = array( - 'php_dir', 'ext_dir', 'doc_dir', 'bin_dir', 'data_dir', 'cfg_dir', - 'test_dir', 'www_dir', 'php_bin', 'php_prefix', 'php_suffix', 'username', - 'password', 'verbose', 'preferred_state', 'umask', 'preferred_mirror', 'php_ini' - ); - - /** - * Channels that can be accessed - * @see setChannels() - * @var array - * @access private - */ - var $_channels = array('pear.php.net', 'pecl.php.net', '__uri'); - - /** - * This variable is used to control the directory values returned - * @see setInstallRoot(); - * @var string|false - * @access private - */ - var $_installRoot = false; - - /** - * If requested, this will always refer to the registry - * contained in php_dir - * @var PEAR_Registry - */ - var $_registry = array(); - - /** - * @var array - * @access private - */ - var $_regInitialized = array(); - - /** - * @var bool - * @access private - */ - var $_noRegistry = false; - - /** - * amount of errors found while parsing config - * @var integer - * @access private - */ - var $_errorsFound = 0; - var $_lastError = null; - - /** - * Information about the configuration data. Stores the type, - * default value and a documentation string for each configuration - * value. - * - * @var array layer => array(infotype => value, ...) - */ - var $configuration_info = array( - // Channels/Internet Access - 'default_channel' => array( - 'type' => 'string', - 'default' => PEAR_CONFIG_DEFAULT_CHANNEL, - 'doc' => 'the default channel to use for all non explicit commands', - 'prompt' => 'Default Channel', - 'group' => 'Internet Access', - ), - 'preferred_mirror' => array( - 'type' => 'string', - 'default' => PEAR_CONFIG_DEFAULT_CHANNEL, - 'doc' => 'the default server or mirror to use for channel actions', - 'prompt' => 'Default Channel Mirror', - 'group' => 'Internet Access', - ), - 'remote_config' => array( - 'type' => 'password', - 'default' => '', - 'doc' => 'ftp url of remote configuration file to use for synchronized install', - 'prompt' => 'Remote Configuration File', - 'group' => 'Internet Access', - ), - 'auto_discover' => array( - 'type' => 'integer', - 'default' => 1, - 'doc' => 'whether to automatically discover new channels', - 'prompt' => 'Auto-discover new Channels', - 'group' => 'Internet Access', - ), - // Internet Access - 'master_server' => array( - 'type' => 'string', - 'default' => 'pear.php.net', - 'doc' => 'name of the main PEAR server [NOT USED IN THIS VERSION]', - 'prompt' => 'PEAR server [DEPRECATED]', - 'group' => 'Internet Access', - ), - 'http_proxy' => array( - 'type' => 'string', - 'default' => PEAR_CONFIG_DEFAULT_HTTP_PROXY, - 'doc' => 'HTTP proxy (host:port) to use when downloading packages', - 'prompt' => 'HTTP Proxy Server Address', - 'group' => 'Internet Access', - ), - // File Locations - 'php_dir' => array( - 'type' => 'directory', - 'default' => PEAR_CONFIG_DEFAULT_PHP_DIR, - 'doc' => 'directory where .php files are installed', - 'prompt' => 'PEAR directory', - 'group' => 'File Locations', - ), - 'ext_dir' => array( - 'type' => 'directory', - 'default' => PEAR_CONFIG_DEFAULT_EXT_DIR, - 'doc' => 'directory where loadable extensions are installed', - 'prompt' => 'PHP extension directory', - 'group' => 'File Locations', - ), - 'doc_dir' => array( - 'type' => 'directory', - 'default' => PEAR_CONFIG_DEFAULT_DOC_DIR, - 'doc' => 'directory where documentation is installed', - 'prompt' => 'PEAR documentation directory', - 'group' => 'File Locations', - ), - 'bin_dir' => array( - 'type' => 'directory', - 'default' => PEAR_CONFIG_DEFAULT_BIN_DIR, - 'doc' => 'directory where executables are installed', - 'prompt' => 'PEAR executables directory', - 'group' => 'File Locations', - ), - 'data_dir' => array( - 'type' => 'directory', - 'default' => PEAR_CONFIG_DEFAULT_DATA_DIR, - 'doc' => 'directory where data files are installed', - 'prompt' => 'PEAR data directory', - 'group' => 'File Locations (Advanced)', - ), - 'cfg_dir' => array( - 'type' => 'directory', - 'default' => PEAR_CONFIG_DEFAULT_CFG_DIR, - 'doc' => 'directory where modifiable configuration files are installed', - 'prompt' => 'PEAR configuration file directory', - 'group' => 'File Locations (Advanced)', - ), - 'www_dir' => array( - 'type' => 'directory', - 'default' => PEAR_CONFIG_DEFAULT_WWW_DIR, - 'doc' => 'directory where www frontend files (html/js) are installed', - 'prompt' => 'PEAR www files directory', - 'group' => 'File Locations (Advanced)', - ), - 'test_dir' => array( - 'type' => 'directory', - 'default' => PEAR_CONFIG_DEFAULT_TEST_DIR, - 'doc' => 'directory where regression tests are installed', - 'prompt' => 'PEAR test directory', - 'group' => 'File Locations (Advanced)', - ), - 'cache_dir' => array( - 'type' => 'directory', - 'default' => PEAR_CONFIG_DEFAULT_CACHE_DIR, - 'doc' => 'directory which is used for web service cache', - 'prompt' => 'PEAR Installer cache directory', - 'group' => 'File Locations (Advanced)', - ), - 'temp_dir' => array( - 'type' => 'directory', - 'default' => PEAR_CONFIG_DEFAULT_TEMP_DIR, - 'doc' => 'directory which is used for all temp files', - 'prompt' => 'PEAR Installer temp directory', - 'group' => 'File Locations (Advanced)', - ), - 'download_dir' => array( - 'type' => 'directory', - 'default' => PEAR_CONFIG_DEFAULT_DOWNLOAD_DIR, - 'doc' => 'directory which is used for all downloaded files', - 'prompt' => 'PEAR Installer download directory', - 'group' => 'File Locations (Advanced)', - ), - 'php_bin' => array( - 'type' => 'file', - 'default' => PEAR_CONFIG_DEFAULT_PHP_BIN, - 'doc' => 'PHP CLI/CGI binary for executing scripts', - 'prompt' => 'PHP CLI/CGI binary', - 'group' => 'File Locations (Advanced)', - ), - 'php_prefix' => array( - 'type' => 'string', - 'default' => '', - 'doc' => '--program-prefix for php_bin\'s ./configure, used for pecl installs', - 'prompt' => '--program-prefix passed to PHP\'s ./configure', - 'group' => 'File Locations (Advanced)', - ), - 'php_suffix' => array( - 'type' => 'string', - 'default' => '', - 'doc' => '--program-suffix for php_bin\'s ./configure, used for pecl installs', - 'prompt' => '--program-suffix passed to PHP\'s ./configure', - 'group' => 'File Locations (Advanced)', - ), - 'php_ini' => array( - 'type' => 'file', - 'default' => '', - 'doc' => 'location of php.ini in which to enable PECL extensions on install', - 'prompt' => 'php.ini location', - 'group' => 'File Locations (Advanced)', - ), - // Maintainers - 'username' => array( - 'type' => 'string', - 'default' => '', - 'doc' => '(maintainers) your PEAR account name', - 'prompt' => 'PEAR username (for maintainers)', - 'group' => 'Maintainers', - ), - 'password' => array( - 'type' => 'password', - 'default' => '', - 'doc' => '(maintainers) your PEAR account password', - 'prompt' => 'PEAR password (for maintainers)', - 'group' => 'Maintainers', - ), - // Advanced - 'verbose' => array( - 'type' => 'integer', - 'default' => PEAR_CONFIG_DEFAULT_VERBOSE, - 'doc' => 'verbosity level -0: really quiet -1: somewhat quiet -2: verbose -3: debug', - 'prompt' => 'Debug Log Level', - 'group' => 'Advanced', - ), - 'preferred_state' => array( - 'type' => 'set', - 'default' => PEAR_CONFIG_DEFAULT_PREFERRED_STATE, - 'doc' => 'the installer will prefer releases with this state when installing packages without a version or state specified', - 'valid_set' => array( - 'stable', 'beta', 'alpha', 'devel', 'snapshot'), - 'prompt' => 'Preferred Package State', - 'group' => 'Advanced', - ), - 'umask' => array( - 'type' => 'mask', - 'default' => PEAR_CONFIG_DEFAULT_UMASK, - 'doc' => 'umask used when creating files (Unix-like systems only)', - 'prompt' => 'Unix file mask', - 'group' => 'Advanced', - ), - 'cache_ttl' => array( - 'type' => 'integer', - 'default' => PEAR_CONFIG_DEFAULT_CACHE_TTL, - 'doc' => 'amount of secs where the local cache is used and not updated', - 'prompt' => 'Cache TimeToLive', - 'group' => 'Advanced', - ), - 'sig_type' => array( - 'type' => 'set', - 'default' => PEAR_CONFIG_DEFAULT_SIG_TYPE, - 'doc' => 'which package signature mechanism to use', - 'valid_set' => array('gpg'), - 'prompt' => 'Package Signature Type', - 'group' => 'Maintainers', - ), - 'sig_bin' => array( - 'type' => 'string', - 'default' => PEAR_CONFIG_DEFAULT_SIG_BIN, - 'doc' => 'which package signature mechanism to use', - 'prompt' => 'Signature Handling Program', - 'group' => 'Maintainers', - ), - 'sig_keyid' => array( - 'type' => 'string', - 'default' => '', - 'doc' => 'which key to use for signing with', - 'prompt' => 'Signature Key Id', - 'group' => 'Maintainers', - ), - 'sig_keydir' => array( - 'type' => 'directory', - 'default' => PEAR_CONFIG_DEFAULT_SIG_KEYDIR, - 'doc' => 'directory where signature keys are located', - 'prompt' => 'Signature Key Directory', - 'group' => 'Maintainers', - ), - // __channels is reserved - used for channel-specific configuration - ); - - /** - * Constructor. - * - * @param string file to read user-defined options from - * @param string file to read system-wide defaults from - * @param bool determines whether a registry object "follows" - * the value of php_dir (is automatically created - * and moved when php_dir is changed) - * @param bool if true, fails if configuration files cannot be loaded - * - * @access public - * - * @see PEAR_Config::singleton - */ - function PEAR_Config($user_file = '', $system_file = '', $ftp_file = false, - $strict = true) - { - $this->PEAR(); - PEAR_Installer_Role::initializeConfig($this); - $sl = DIRECTORY_SEPARATOR; - if (empty($user_file)) { - if (OS_WINDOWS) { - $user_file = PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.ini'; - } else { - $user_file = getenv('HOME') . $sl . '.pearrc'; - } - } - - if (empty($system_file)) { - $system_file = PEAR_CONFIG_SYSCONFDIR . $sl; - if (OS_WINDOWS) { - $system_file .= 'pearsys.ini'; - } else { - $system_file .= 'pear.conf'; - } - } - - $this->layers = array_keys($this->configuration); - $this->files['user'] = $user_file; - $this->files['system'] = $system_file; - if ($user_file && file_exists($user_file)) { - $this->pushErrorHandling(PEAR_ERROR_RETURN); - $this->readConfigFile($user_file, 'user', $strict); - $this->popErrorHandling(); - if ($this->_errorsFound > 0) { - return; - } - } - - if ($system_file && @file_exists($system_file)) { - $this->mergeConfigFile($system_file, false, 'system', $strict); - if ($this->_errorsFound > 0) { - return; - } - - } - - if (!$ftp_file) { - $ftp_file = $this->get('remote_config'); - } - - if ($ftp_file && defined('PEAR_REMOTEINSTALL_OK')) { - $this->readFTPConfigFile($ftp_file); - } - - foreach ($this->configuration_info as $key => $info) { - $this->configuration['default'][$key] = $info['default']; - } - - $this->_registry['default'] = &new PEAR_Registry($this->configuration['default']['php_dir']); - $this->_registry['default']->setConfig($this, false); - $this->_regInitialized['default'] = false; - //$GLOBALS['_PEAR_Config_instance'] = &$this; - } - - /** - * Return the default locations of user and system configuration files - * @static - */ - function getDefaultConfigFiles() - { - $sl = DIRECTORY_SEPARATOR; - if (OS_WINDOWS) { - return array( - 'user' => PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.ini', - 'system' => PEAR_CONFIG_SYSCONFDIR . $sl . 'pearsys.ini' - ); - } - - return array( - 'user' => getenv('HOME') . $sl . '.pearrc', - 'system' => PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.conf' - ); - } - - /** - * Static singleton method. If you want to keep only one instance - * of this class in use, this method will give you a reference to - * the last created PEAR_Config object if one exists, or create a - * new object. - * - * @param string (optional) file to read user-defined options from - * @param string (optional) file to read system-wide defaults from - * - * @return object an existing or new PEAR_Config instance - * - * @access public - * - * @see PEAR_Config::PEAR_Config - */ - function &singleton($user_file = '', $system_file = '', $strict = true) - { - if (is_object($GLOBALS['_PEAR_Config_instance'])) { - return $GLOBALS['_PEAR_Config_instance']; - } - - $t_conf = &new PEAR_Config($user_file, $system_file, false, $strict); - if ($t_conf->_errorsFound > 0) { - return $t_conf->lastError; - } - - $GLOBALS['_PEAR_Config_instance'] = &$t_conf; - return $GLOBALS['_PEAR_Config_instance']; - } - - /** - * Determine whether any configuration files have been detected, and whether a - * registry object can be retrieved from this configuration. - * @return bool - * @since PEAR 1.4.0a1 - */ - function validConfiguration() - { - if ($this->isDefinedLayer('user') || $this->isDefinedLayer('system')) { - return true; - } - - return false; - } - - /** - * Reads configuration data from a file. All existing values in - * the config layer are discarded and replaced with data from the - * file. - * @param string file to read from, if NULL or not specified, the - * last-used file for the same layer (second param) is used - * @param string config layer to insert data into ('user' or 'system') - * @return bool TRUE on success or a PEAR error on failure - */ - function readConfigFile($file = null, $layer = 'user', $strict = true) - { - if (empty($this->files[$layer])) { - return $this->raiseError("unknown config layer `$layer'"); - } - - if ($file === null) { - $file = $this->files[$layer]; - } - - $data = $this->_readConfigDataFrom($file); - if (PEAR::isError($data)) { - if (!$strict) { - return true; - } - - $this->_errorsFound++; - $this->lastError = $data; - - return $data; - } - - $this->files[$layer] = $file; - $this->_decodeInput($data); - $this->configuration[$layer] = $data; - $this->_setupChannels(); - if (!$this->_noRegistry && ($phpdir = $this->get('php_dir', $layer, 'pear.php.net'))) { - $this->_registry[$layer] = &new PEAR_Registry($phpdir); - $this->_registry[$layer]->setConfig($this, false); - $this->_regInitialized[$layer] = false; - } else { - unset($this->_registry[$layer]); - } - return true; - } - - /** - * @param string url to the remote config file, like ftp://www.example.com/pear/config.ini - * @return true|PEAR_Error - */ - function readFTPConfigFile($path) - { - do { // poor man's try - if (!class_exists('PEAR_FTP')) { - if (!class_exists('PEAR_Common')) { - require_once 'PEAR/Common.php'; - } - if (PEAR_Common::isIncludeable('PEAR/FTP.php')) { - require_once 'PEAR/FTP.php'; - } - } - - if (!class_exists('PEAR_FTP')) { - return PEAR::raiseError('PEAR_RemoteInstaller must be installed to use remote config'); - } - - $this->_ftp = &new PEAR_FTP; - $this->_ftp->pushErrorHandling(PEAR_ERROR_RETURN); - $e = $this->_ftp->init($path); - if (PEAR::isError($e)) { - $this->_ftp->popErrorHandling(); - return $e; - } - - $tmp = System::mktemp('-d'); - PEAR_Common::addTempFile($tmp); - $e = $this->_ftp->get(basename($path), $tmp . DIRECTORY_SEPARATOR . - 'pear.ini', false, FTP_BINARY); - if (PEAR::isError($e)) { - $this->_ftp->popErrorHandling(); - return $e; - } - - PEAR_Common::addTempFile($tmp . DIRECTORY_SEPARATOR . 'pear.ini'); - $this->_ftp->disconnect(); - $this->_ftp->popErrorHandling(); - $this->files['ftp'] = $tmp . DIRECTORY_SEPARATOR . 'pear.ini'; - $e = $this->readConfigFile(null, 'ftp'); - if (PEAR::isError($e)) { - return $e; - } - - $fail = array(); - foreach ($this->configuration_info as $key => $val) { - if (in_array($this->getGroup($key), - array('File Locations', 'File Locations (Advanced)')) && - $this->getType($key) == 'directory') { - // any directory configs must be set for this to work - if (!isset($this->configuration['ftp'][$key])) { - $fail[] = $key; - } - } - } - - if (!count($fail)) { - return true; - } - - $fail = '"' . implode('", "', $fail) . '"'; - unset($this->files['ftp']); - unset($this->configuration['ftp']); - return PEAR::raiseError('ERROR: Ftp configuration file must set all ' . - 'directory configuration variables. These variables were not set: ' . - $fail); - } while (false); // poor man's catch - unset($this->files['ftp']); - return PEAR::raiseError('no remote host specified'); - } - - /** - * Reads the existing configurations and creates the _channels array from it - */ - function _setupChannels() - { - $set = array_flip(array_values($this->_channels)); - foreach ($this->configuration as $layer => $data) { - $i = 1000; - if (isset($data['__channels']) && is_array($data['__channels'])) { - foreach ($data['__channels'] as $channel => $info) { - $set[$channel] = $i++; - } - } - } - $this->_channels = array_values(array_flip($set)); - $this->setChannels($this->_channels); - } - - function deleteChannel($channel) - { - $ch = strtolower($channel); - foreach ($this->configuration as $layer => $data) { - if (isset($data['__channels']) && isset($data['__channels'][$ch])) { - unset($this->configuration[$layer]['__channels'][$ch]); - } - } - - $this->_channels = array_flip($this->_channels); - unset($this->_channels[$ch]); - $this->_channels = array_flip($this->_channels); - } - - /** - * Merges data into a config layer from a file. Does the same - * thing as readConfigFile, except it does not replace all - * existing values in the config layer. - * @param string file to read from - * @param bool whether to overwrite existing data (default TRUE) - * @param string config layer to insert data into ('user' or 'system') - * @param string if true, errors are returned if file opening fails - * @return bool TRUE on success or a PEAR error on failure - */ - function mergeConfigFile($file, $override = true, $layer = 'user', $strict = true) - { - if (empty($this->files[$layer])) { - return $this->raiseError("unknown config layer `$layer'"); - } - - if ($file === null) { - $file = $this->files[$layer]; - } - - $data = $this->_readConfigDataFrom($file); - if (PEAR::isError($data)) { - if (!$strict) { - return true; - } - - $this->_errorsFound++; - $this->lastError = $data; - - return $data; - } - - $this->_decodeInput($data); - if ($override) { - $this->configuration[$layer] = - PEAR_Config::arrayMergeRecursive($this->configuration[$layer], $data); - } else { - $this->configuration[$layer] = - PEAR_Config::arrayMergeRecursive($data, $this->configuration[$layer]); - } - - $this->_setupChannels(); - if (!$this->_noRegistry && ($phpdir = $this->get('php_dir', $layer, 'pear.php.net'))) { - $this->_registry[$layer] = &new PEAR_Registry($phpdir); - $this->_registry[$layer]->setConfig($this, false); - $this->_regInitialized[$layer] = false; - } else { - unset($this->_registry[$layer]); - } - return true; - } - - /** - * @param array - * @param array - * @return array - * @static - */ - function arrayMergeRecursive($arr2, $arr1) - { - $ret = array(); - foreach ($arr2 as $key => $data) { - if (!isset($arr1[$key])) { - $ret[$key] = $data; - unset($arr1[$key]); - continue; - } - if (is_array($data)) { - if (!is_array($arr1[$key])) { - $ret[$key] = $arr1[$key]; - unset($arr1[$key]); - continue; - } - $ret[$key] = PEAR_Config::arrayMergeRecursive($arr1[$key], $arr2[$key]); - unset($arr1[$key]); - } - } - - return array_merge($ret, $arr1); - } - - /** - * Writes data into a config layer from a file. - * - * @param string|null file to read from, or null for default - * @param string config layer to insert data into ('user' or - * 'system') - * @param string|null data to write to config file or null for internal data [DEPRECATED] - * @return bool TRUE on success or a PEAR error on failure - */ - function writeConfigFile($file = null, $layer = 'user', $data = null) - { - $this->_lazyChannelSetup($layer); - if ($layer == 'both' || $layer == 'all') { - foreach ($this->files as $type => $file) { - $err = $this->writeConfigFile($file, $type, $data); - if (PEAR::isError($err)) { - return $err; - } - } - return true; - } - - if (empty($this->files[$layer])) { - return $this->raiseError("unknown config file type `$layer'"); - } - - if ($file === null) { - $file = $this->files[$layer]; - } - - $data = ($data === null) ? $this->configuration[$layer] : $data; - $this->_encodeOutput($data); - $opt = array('-p', dirname($file)); - if (!@System::mkDir($opt)) { - return $this->raiseError("could not create directory: " . dirname($file)); - } - - if (file_exists($file) && is_file($file) && !is_writeable($file)) { - return $this->raiseError("no write access to $file!"); - } - - $fp = @fopen($file, "w"); - if (!$fp) { - return $this->raiseError("PEAR_Config::writeConfigFile fopen('$file','w') failed ($php_errormsg)"); - } - - $contents = "#PEAR_Config 0.9\n" . serialize($data); - if (!@fwrite($fp, $contents)) { - return $this->raiseError("PEAR_Config::writeConfigFile: fwrite failed ($php_errormsg)"); - } - return true; - } - - /** - * Reads configuration data from a file and returns the parsed data - * in an array. - * - * @param string file to read from - * @return array configuration data or a PEAR error on failure - */ - private function _readConfigDataFrom($file) - { - $fp = false; - if (file_exists($file)) { - $fp = @fopen($file, "r"); - } - - if (!$fp) { - return $this->raiseError("PEAR_Config::readConfigFile fopen('$file','r') failed"); - } - - $size = filesize($file); - fclose($fp); - $contents = file_get_contents($file); - if (empty($contents)) { - return $this->raiseError('Configuration file "' . $file . '" is empty'); - } - - $version = false; - if (preg_match('/^#PEAR_Config\s+(\S+)\s+/si', $contents, $matches)) { - $version = $matches[1]; - $contents = substr($contents, strlen($matches[0])); - } else { - // Museum config file - if (substr($contents,0,2) == 'a:') { - $version = '0.1'; - } - } - - if ($version && version_compare("$version", '1', '<')) { - // no '@', it is possible that unserialize - // raises a notice but it seems to block IO to - // STDOUT if a '@' is used and a notice is raise - $data = unserialize($contents); - - if (!is_array($data) && !$data) { - if ($contents == serialize(false)) { - $data = array(); - } else { - $err = $this->raiseError("PEAR_Config: bad data in $file"); - return $err; - } - } - if (!is_array($data)) { - if (strlen(trim($contents)) > 0) { - $error = "PEAR_Config: bad data in $file"; - $err = $this->raiseError($error); - return $err; - } - - $data = array(); - } - // add parsing of newer formats here... - } else { - $err = $this->raiseError("$file: unknown version `$version'"); - return $err; - } - - return $data; - } - - /** - * Gets the file used for storing the config for a layer - * - * @param string $layer 'user' or 'system' - */ - function getConfFile($layer) - { - return $this->files[$layer]; - } - - /** - * @param string Configuration class name, used for detecting duplicate calls - * @param array information on a role as parsed from its xml file - * @return true|PEAR_Error - * @access private - */ - function _addConfigVars($class, $vars) - { - static $called = array(); - if (isset($called[$class])) { - return; - } - - $called[$class] = 1; - if (count($vars) > 3) { - return $this->raiseError('Roles can only define 3 new config variables or less'); - } - - foreach ($vars as $name => $var) { - if (!is_array($var)) { - return $this->raiseError('Configuration information must be an array'); - } - - if (!isset($var['type'])) { - return $this->raiseError('Configuration information must contain a type'); - } elseif (!in_array($var['type'], - array('string', 'mask', 'password', 'directory', 'file', 'set'))) { - return $this->raiseError( - 'Configuration type must be one of directory, file, string, ' . - 'mask, set, or password'); - } - if (!isset($var['default'])) { - return $this->raiseError( - 'Configuration information must contain a default value ("default" index)'); - } - - if (is_array($var['default'])) { - $real_default = ''; - foreach ($var['default'] as $config_var => $val) { - if (strpos($config_var, 'text') === 0) { - $real_default .= $val; - } elseif (strpos($config_var, 'constant') === 0) { - if (!defined($val)) { - return $this->raiseError( - 'Unknown constant "' . $val . '" requested in ' . - 'default value for configuration variable "' . - $name . '"'); - } - - $real_default .= constant($val); - } elseif (isset($this->configuration_info[$config_var])) { - $real_default .= - $this->configuration_info[$config_var]['default']; - } else { - return $this->raiseError( - 'Unknown request for "' . $config_var . '" value in ' . - 'default value for configuration variable "' . - $name . '"'); - } - } - $var['default'] = $real_default; - } - - if ($var['type'] == 'integer') { - $var['default'] = (integer) $var['default']; - } - - if (!isset($var['doc'])) { - return $this->raiseError( - 'Configuration information must contain a summary ("doc" index)'); - } - - if (!isset($var['prompt'])) { - return $this->raiseError( - 'Configuration information must contain a simple prompt ("prompt" index)'); - } - - if (!isset($var['group'])) { - return $this->raiseError( - 'Configuration information must contain a simple group ("group" index)'); - } - - if (isset($this->configuration_info[$name])) { - return $this->raiseError('Configuration variable "' . $name . - '" already exists'); - } - - $this->configuration_info[$name] = $var; - // fix bug #7351: setting custom config variable in a channel fails - $this->_channelConfigInfo[] = $name; - } - - return true; - } - - /** - * Encodes/scrambles configuration data before writing to files. - * Currently, 'password' values will be base64-encoded as to avoid - * that people spot cleartext passwords by accident. - * - * @param array (reference) array to encode values in - * @return bool TRUE on success - * @access private - */ - function _encodeOutput(&$data) - { - foreach ($data as $key => $value) { - if ($key == '__channels') { - foreach ($data['__channels'] as $channel => $blah) { - $this->_encodeOutput($data['__channels'][$channel]); - } - } - - if (!isset($this->configuration_info[$key])) { - continue; - } - - $type = $this->configuration_info[$key]['type']; - switch ($type) { - // we base64-encode passwords so they are at least - // not shown in plain by accident - case 'password': { - $data[$key] = base64_encode($data[$key]); - break; - } - case 'mask': { - $data[$key] = octdec($data[$key]); - break; - } - } - } - - return true; - } - - /** - * Decodes/unscrambles configuration data after reading from files. - * - * @param array (reference) array to encode values in - * @return bool TRUE on success - * @access private - * - * @see PEAR_Config::_encodeOutput - */ - function _decodeInput(&$data) - { - if (!is_array($data)) { - return true; - } - - foreach ($data as $key => $value) { - if ($key == '__channels') { - foreach ($data['__channels'] as $channel => $blah) { - $this->_decodeInput($data['__channels'][$channel]); - } - } - - if (!isset($this->configuration_info[$key])) { - continue; - } - - $type = $this->configuration_info[$key]['type']; - switch ($type) { - case 'password': { - $data[$key] = base64_decode($data[$key]); - break; - } - case 'mask': { - $data[$key] = decoct($data[$key]); - break; - } - } - } - - return true; - } - - /** - * Retrieve the default channel. - * - * On startup, channels are not initialized, so if the default channel is not - * pear.php.net, then initialize the config. - * @param string registry layer - * @return string|false - */ - function getDefaultChannel($layer = null) - { - $ret = false; - if ($layer === null) { - foreach ($this->layers as $layer) { - if (isset($this->configuration[$layer]['default_channel'])) { - $ret = $this->configuration[$layer]['default_channel']; - break; - } - } - } elseif (isset($this->configuration[$layer]['default_channel'])) { - $ret = $this->configuration[$layer]['default_channel']; - } - - if ($ret == 'pear.php.net' && defined('PEAR_RUNTYPE') && PEAR_RUNTYPE == 'pecl') { - $ret = 'pecl.php.net'; - } - - if ($ret) { - if ($ret != 'pear.php.net') { - $this->_lazyChannelSetup(); - } - - return $ret; - } - - return PEAR_CONFIG_DEFAULT_CHANNEL; - } - - /** - * Returns a configuration value, prioritizing layers as per the - * layers property. - * - * @param string config key - * @return mixed the config value, or NULL if not found - * @access public - */ - function get($key, $layer = null, $channel = false) - { - if (!isset($this->configuration_info[$key])) { - return null; - } - - if ($key == '__channels') { - return null; - } - - if ($key == 'default_channel') { - return $this->getDefaultChannel($layer); - } - - if (!$channel) { - $channel = $this->getDefaultChannel(); - } elseif ($channel != 'pear.php.net') { - $this->_lazyChannelSetup(); - } - $channel = strtolower($channel); - - $test = (in_array($key, $this->_channelConfigInfo)) ? - $this->_getChannelValue($key, $layer, $channel) : - null; - if ($test !== null) { - if ($this->_installRoot) { - if (in_array($this->getGroup($key), - array('File Locations', 'File Locations (Advanced)')) && - $this->getType($key) == 'directory') { - return $this->_prependPath($test, $this->_installRoot); - } - } - return $test; - } - - if ($layer === null) { - foreach ($this->layers as $layer) { - if (isset($this->configuration[$layer][$key])) { - $test = $this->configuration[$layer][$key]; - if ($this->_installRoot) { - if (in_array($this->getGroup($key), - array('File Locations', 'File Locations (Advanced)')) && - $this->getType($key) == 'directory') { - return $this->_prependPath($test, $this->_installRoot); - } - } - - if ($key == 'preferred_mirror') { - $reg = &$this->getRegistry(); - if (is_object($reg)) { - $chan = &$reg->getChannel($channel); - if (PEAR::isError($chan)) { - return $channel; - } - - if (!$chan->getMirror($test) && $chan->getName() != $test) { - return $channel; // mirror does not exist - } - } - } - return $test; - } - } - } elseif (isset($this->configuration[$layer][$key])) { - $test = $this->configuration[$layer][$key]; - if ($this->_installRoot) { - if (in_array($this->getGroup($key), - array('File Locations', 'File Locations (Advanced)')) && - $this->getType($key) == 'directory') { - return $this->_prependPath($test, $this->_installRoot); - } - } - - if ($key == 'preferred_mirror') { - $reg = &$this->getRegistry(); - if (is_object($reg)) { - $chan = &$reg->getChannel($channel); - if (PEAR::isError($chan)) { - return $channel; - } - - if (!$chan->getMirror($test) && $chan->getName() != $test) { - return $channel; // mirror does not exist - } - } - } - - return $test; - } - - return null; - } - - /** - * Returns a channel-specific configuration value, prioritizing layers as per the - * layers property. - * - * @param string config key - * @return mixed the config value, or NULL if not found - * @access private - */ - function _getChannelValue($key, $layer, $channel) - { - if ($key == '__channels' || $channel == 'pear.php.net') { - return null; - } - - $ret = null; - if ($layer === null) { - foreach ($this->layers as $ilayer) { - if (isset($this->configuration[$ilayer]['__channels'][$channel][$key])) { - $ret = $this->configuration[$ilayer]['__channels'][$channel][$key]; - break; - } - } - } elseif (isset($this->configuration[$layer]['__channels'][$channel][$key])) { - $ret = $this->configuration[$layer]['__channels'][$channel][$key]; - } - - if ($key != 'preferred_mirror') { - return $ret; - } - - - if ($ret !== null) { - $reg = &$this->getRegistry($layer); - if (is_object($reg)) { - $chan = &$reg->getChannel($channel); - if (PEAR::isError($chan)) { - return $channel; - } - - if (!$chan->getMirror($ret) && $chan->getName() != $ret) { - return $channel; // mirror does not exist - } - } - - return $ret; - } - - if ($channel != $this->getDefaultChannel($layer)) { - return $channel; // we must use the channel name as the preferred mirror - // if the user has not chosen an alternate - } - - return $this->getDefaultChannel($layer); - } - - /** - * Set a config value in a specific layer (defaults to 'user'). - * Enforces the types defined in the configuration_info array. An - * integer config variable will be cast to int, and a set config - * variable will be validated against its legal values. - * - * @param string config key - * @param string config value - * @param string (optional) config layer - * @param string channel to set this value for, or null for global value - * @return bool TRUE on success, FALSE on failure - */ - function set($key, $value, $layer = 'user', $channel = false) - { - if ($key == '__channels') { - return false; - } - - if (!isset($this->configuration[$layer])) { - return false; - } - - if ($key == 'default_channel') { - // can only set this value globally - $channel = 'pear.php.net'; - if ($value != 'pear.php.net') { - $this->_lazyChannelSetup($layer); - } - } - - if ($key == 'preferred_mirror') { - if ($channel == '__uri') { - return false; // can't set the __uri pseudo-channel's mirror - } - - $reg = &$this->getRegistry($layer); - if (is_object($reg)) { - $chan = &$reg->getChannel($channel ? $channel : 'pear.php.net'); - if (PEAR::isError($chan)) { - return false; - } - - if (!$chan->getMirror($value) && $chan->getName() != $value) { - return false; // mirror does not exist - } - } - } - - if (!isset($this->configuration_info[$key])) { - return false; - } - - extract($this->configuration_info[$key]); - switch ($type) { - case 'integer': - $value = (int)$value; - break; - case 'set': { - // If a valid_set is specified, require the value to - // be in the set. If there is no valid_set, accept - // any value. - if ($valid_set) { - reset($valid_set); - if ((key($valid_set) === 0 && !in_array($value, $valid_set)) || - (key($valid_set) !== 0 && empty($valid_set[$value]))) - { - return false; - } - } - break; - } - } - - if (!$channel) { - $channel = $this->get('default_channel', null, 'pear.php.net'); - } - - if (!in_array($channel, $this->_channels)) { - $this->_lazyChannelSetup($layer); - $reg = &$this->getRegistry($layer); - if ($reg) { - $channel = $reg->channelName($channel); - } - - if (!in_array($channel, $this->_channels)) { - return false; - } - } - - if ($channel != 'pear.php.net') { - if (in_array($key, $this->_channelConfigInfo)) { - $this->configuration[$layer]['__channels'][$channel][$key] = $value; - return true; - } - - return false; - } - - if ($key == 'default_channel') { - if (!isset($reg)) { - $reg = &$this->getRegistry($layer); - if (!$reg) { - $reg = &$this->getRegistry(); - } - } - - if ($reg) { - $value = $reg->channelName($value); - } - - if (!$value) { - return false; - } - } - - $this->configuration[$layer][$key] = $value; - if ($key == 'php_dir' && !$this->_noRegistry) { - if (!isset($this->_registry[$layer]) || - $value != $this->_registry[$layer]->install_dir) { - $this->_registry[$layer] = &new PEAR_Registry($value); - $this->_regInitialized[$layer] = false; - $this->_registry[$layer]->setConfig($this, false); - } - } - - return true; - } - - function _lazyChannelSetup($uselayer = false) - { - if ($this->_noRegistry) { - return; - } - - $merge = false; - foreach ($this->_registry as $layer => $p) { - if ($uselayer && $uselayer != $layer) { - continue; - } - - if (!$this->_regInitialized[$layer]) { - if ($layer == 'default' && isset($this->_registry['user']) || - isset($this->_registry['system'])) { - // only use the default registry if there are no alternatives - continue; - } - - if (!is_object($this->_registry[$layer])) { - if ($phpdir = $this->get('php_dir', $layer, 'pear.php.net')) { - $this->_registry[$layer] = &new PEAR_Registry($phpdir); - $this->_registry[$layer]->setConfig($this, false); - $this->_regInitialized[$layer] = false; - } else { - unset($this->_registry[$layer]); - return; - } - } - - $this->setChannels($this->_registry[$layer]->listChannels(), $merge); - $this->_regInitialized[$layer] = true; - $merge = true; - } - } - } - - /** - * Set the list of channels. - * - * This should be set via a call to {@link PEAR_Registry::listChannels()} - * @param array - * @param bool - * @return bool success of operation - */ - function setChannels($channels, $merge = false) - { - if (!is_array($channels)) { - return false; - } - - if ($merge) { - $this->_channels = array_merge($this->_channels, $channels); - } else { - $this->_channels = $channels; - } - - foreach ($channels as $channel) { - $channel = strtolower($channel); - if ($channel == 'pear.php.net') { - continue; - } - - foreach ($this->layers as $layer) { - if (!isset($this->configuration[$layer]['__channels'])) { - $this->configuration[$layer]['__channels'] = array(); - } - if (!isset($this->configuration[$layer]['__channels'][$channel]) - || !is_array($this->configuration[$layer]['__channels'][$channel])) { - $this->configuration[$layer]['__channels'][$channel] = array(); - } - } - } - - return true; - } - - /** - * Get the type of a config value. - * - * @param string config key - * - * @return string type, one of "string", "integer", "file", - * "directory", "set" or "password". - * - * @access public - * - */ - function getType($key) - { - if (isset($this->configuration_info[$key])) { - return $this->configuration_info[$key]['type']; - } - return false; - } - - /** - * Get the documentation for a config value. - * - * @param string config key - * @return string documentation string - * - * @access public - * - */ - function getDocs($key) - { - if (isset($this->configuration_info[$key])) { - return $this->configuration_info[$key]['doc']; - } - - return false; - } - - /** - * Get the short documentation for a config value. - * - * @param string config key - * @return string short documentation string - * - * @access public - * - */ - function getPrompt($key) - { - if (isset($this->configuration_info[$key])) { - return $this->configuration_info[$key]['prompt']; - } - - return false; - } - - /** - * Get the parameter group for a config key. - * - * @param string config key - * @return string parameter group - * - * @access public - * - */ - function getGroup($key) - { - if (isset($this->configuration_info[$key])) { - return $this->configuration_info[$key]['group']; - } - - return false; - } - - /** - * Get the list of parameter groups. - * - * @return array list of parameter groups - * - * @access public - * - */ - function getGroups() - { - $tmp = array(); - foreach ($this->configuration_info as $key => $info) { - $tmp[$info['group']] = 1; - } - - return array_keys($tmp); - } - - /** - * Get the list of the parameters in a group. - * - * @param string $group parameter group - * @return array list of parameters in $group - * - * @access public - * - */ - function getGroupKeys($group) - { - $keys = array(); - foreach ($this->configuration_info as $key => $info) { - if ($info['group'] == $group) { - $keys[] = $key; - } - } - - return $keys; - } - - /** - * Get the list of allowed set values for a config value. Returns - * NULL for config values that are not sets. - * - * @param string config key - * @return array enumerated array of set values, or NULL if the - * config key is unknown or not a set - * - * @access public - * - */ - function getSetValues($key) - { - if (isset($this->configuration_info[$key]) && - isset($this->configuration_info[$key]['type']) && - $this->configuration_info[$key]['type'] == 'set') - { - $valid_set = $this->configuration_info[$key]['valid_set']; - reset($valid_set); - if (key($valid_set) === 0) { - return $valid_set; - } - - return array_keys($valid_set); - } - - return null; - } - - /** - * Get all the current config keys. - * - * @return array simple array of config keys - * - * @access public - */ - function getKeys() - { - $keys = array(); - foreach ($this->layers as $layer) { - $test = $this->configuration[$layer]; - if (isset($test['__channels'])) { - foreach ($test['__channels'] as $channel => $configs) { - $keys = array_merge($keys, $configs); - } - } - - unset($test['__channels']); - $keys = array_merge($keys, $test); - - } - return array_keys($keys); - } - - /** - * Remove the a config key from a specific config layer. - * - * @param string config key - * @param string (optional) config layer - * @param string (optional) channel (defaults to default channel) - * @return bool TRUE on success, FALSE on failure - * - * @access public - */ - function remove($key, $layer = 'user', $channel = null) - { - if ($channel === null) { - $channel = $this->getDefaultChannel(); - } - - if ($channel !== 'pear.php.net') { - if (isset($this->configuration[$layer]['__channels'][$channel][$key])) { - unset($this->configuration[$layer]['__channels'][$channel][$key]); - return true; - } - } - - if (isset($this->configuration[$layer][$key])) { - unset($this->configuration[$layer][$key]); - return true; - } - - return false; - } - - /** - * Temporarily remove an entire config layer. USE WITH CARE! - * - * @param string config key - * @param string (optional) config layer - * @return bool TRUE on success, FALSE on failure - * - * @access public - */ - function removeLayer($layer) - { - if (isset($this->configuration[$layer])) { - $this->configuration[$layer] = array(); - return true; - } - - return false; - } - - /** - * Stores configuration data in a layer. - * - * @param string config layer to store - * @return bool TRUE on success, or PEAR error on failure - * - * @access public - */ - function store($layer = 'user', $data = null) - { - return $this->writeConfigFile(null, $layer, $data); - } - - /** - * Tells what config layer that gets to define a key. - * - * @param string config key - * @param boolean return the defining channel - * - * @return string|array the config layer, or an empty string if not found. - * - * if $returnchannel, the return is an array array('layer' => layername, - * 'channel' => channelname), or an empty string if not found - * - * @access public - */ - function definedBy($key, $returnchannel = false) - { - foreach ($this->layers as $layer) { - $channel = $this->getDefaultChannel(); - if ($channel !== 'pear.php.net') { - if (isset($this->configuration[$layer]['__channels'][$channel][$key])) { - if ($returnchannel) { - return array('layer' => $layer, 'channel' => $channel); - } - return $layer; - } - } - - if (isset($this->configuration[$layer][$key])) { - if ($returnchannel) { - return array('layer' => $layer, 'channel' => 'pear.php.net'); - } - return $layer; - } - } - - return ''; - } - - /** - * Tells whether a given key exists as a config value. - * - * @param string config key - * @return bool whether exists in this object - * - * @access public - */ - function isDefined($key) - { - foreach ($this->layers as $layer) { - if (isset($this->configuration[$layer][$key])) { - return true; - } - } - - return false; - } - - /** - * Tells whether a given config layer exists. - * - * @param string config layer - * @return bool whether exists in this object - * - * @access public - */ - function isDefinedLayer($layer) - { - return isset($this->configuration[$layer]); - } - - /** - * Returns the layers defined (except the 'default' one) - * - * @return array of the defined layers - */ - function getLayers() - { - $cf = $this->configuration; - unset($cf['default']); - return array_keys($cf); - } - - function apiVersion() - { - return '1.1'; - } - - /** - * @return PEAR_Registry - */ - function &getRegistry($use = null) - { - $layer = $use === null ? 'user' : $use; - if (isset($this->_registry[$layer])) { - return $this->_registry[$layer]; - } elseif ($use === null && isset($this->_registry['system'])) { - return $this->_registry['system']; - } elseif ($use === null && isset($this->_registry['default'])) { - return $this->_registry['default']; - } elseif ($use) { - $a = false; - return $a; - } - - // only go here if null was passed in - echo "CRITICAL ERROR: Registry could not be initialized from any value"; - exit(1); - } - - /** - * This is to allow customization like the use of installroot - * @param PEAR_Registry - * @return bool - */ - function setRegistry(&$reg, $layer = 'user') - { - if ($this->_noRegistry) { - return false; - } - - if (!in_array($layer, array('user', 'system'))) { - return false; - } - - $this->_registry[$layer] = &$reg; - if (is_object($reg)) { - $this->_registry[$layer]->setConfig($this, false); - } - - return true; - } - - function noRegistry() - { - $this->_noRegistry = true; - } - - /** - * @return PEAR_REST - */ - function &getREST($version, $options = array()) - { - $version = str_replace('.', '', $version); - if (!class_exists($class = 'PEAR_REST_' . $version)) { - require_once 'PEAR/REST/' . $version . '.php'; - } - - $remote = &new $class($this, $options); - return $remote; - } - - /** - * The ftp server is set in {@link readFTPConfigFile()}. It exists only if a - * remote configuration file has been specified - * @return PEAR_FTP|false - */ - function &getFTP() - { - if (isset($this->_ftp)) { - return $this->_ftp; - } - - $a = false; - return $a; - } - - function _prependPath($path, $prepend) - { - if (strlen($prepend) > 0) { - if (OS_WINDOWS && preg_match('/^[a-z]:/i', $path)) { - if (preg_match('/^[a-z]:/i', $prepend)) { - $prepend = substr($prepend, 2); - } elseif ($prepend{0} != '\\') { - $prepend = "\\$prepend"; - } - $path = substr($path, 0, 2) . $prepend . substr($path, 2); - } else { - $path = $prepend . $path; - } - } - return $path; - } - - /** - * @param string|false installation directory to prepend to all _dir variables, or false to - * disable - */ - function setInstallRoot($root) - { - if (substr($root, -1) == DIRECTORY_SEPARATOR) { - $root = substr($root, 0, -1); - } - $old = $this->_installRoot; - $this->_installRoot = $root; - if (($old != $root) && !$this->_noRegistry) { - foreach (array_keys($this->_registry) as $layer) { - if ($layer == 'ftp' || !isset($this->_registry[$layer])) { - continue; - } - $this->_registry[$layer] = - &new PEAR_Registry($this->get('php_dir', $layer, 'pear.php.net')); - $this->_registry[$layer]->setConfig($this, false); - $this->_regInitialized[$layer] = false; - } - } - } -} diff --git a/pear/PEAR/Dependency2.php b/pear/PEAR/Dependency2.php deleted file mode 100644 index 6d78964..0000000 --- a/pear/PEAR/Dependency2.php +++ /dev/null @@ -1,1362 +0,0 @@ - - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a1 - */ - -/** - * Required for the PEAR_VALIDATE_* constants - */ -require_once 'PEAR/Validate.php'; - -/** - * Dependency check for PEAR packages - * - * This class handles both version 1.0 and 2.0 dependencies - * WARNING: *any* changes to this class must be duplicated in the - * test_PEAR_Dependency2 class found in tests/PEAR_Dependency2/setup.php.inc, - * or unit tests will not actually validate the changes - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @PEAR-VER@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a1 - */ -class PEAR_Dependency2 -{ - /** - * One of the PEAR_VALIDATE_* states - * @see PEAR_VALIDATE_NORMAL - * @var integer - */ - var $_state; - - /** - * Command-line options to install/upgrade/uninstall commands - * @param array - */ - var $_options; - - /** - * @var OS_Guess - */ - var $_os; - - /** - * @var PEAR_Registry - */ - var $_registry; - - /** - * @var PEAR_Config - */ - var $_config; - - /** - * @var PEAR_DependencyDB - */ - var $_dependencydb; - - /** - * Output of PEAR_Registry::parsedPackageName() - * @var array - */ - var $_currentPackage; - - /** - * @param PEAR_Config - * @param array installation options - * @param array format of PEAR_Registry::parsedPackageName() - * @param int installation state (one of PEAR_VALIDATE_*) - */ - function PEAR_Dependency2(&$config, $installoptions, $package, - $state = PEAR_VALIDATE_INSTALLING) - { - $this->_config = &$config; - if (!class_exists('PEAR_DependencyDB')) { - require_once 'PEAR/DependencyDB.php'; - } - - if (isset($installoptions['packagingroot'])) { - // make sure depdb is in the right location - $config->setInstallRoot($installoptions['packagingroot']); - } - - $this->_registry = &$config->getRegistry(); - $this->_dependencydb = &PEAR_DependencyDB::singleton($config); - if (isset($installoptions['packagingroot'])) { - $config->setInstallRoot(false); - } - - $this->_options = $installoptions; - $this->_state = $state; - if (!class_exists('OS_Guess')) { - require_once 'OS/Guess.php'; - } - - $this->_os = new OS_Guess; - $this->_currentPackage = $package; - } - - function _getExtraString($dep) - { - $extra = ' ('; - if (isset($dep['uri'])) { - return ''; - } - - if (isset($dep['recommended'])) { - $extra .= 'recommended version ' . $dep['recommended']; - } else { - if (isset($dep['min'])) { - $extra .= 'version >= ' . $dep['min']; - } - - if (isset($dep['max'])) { - if ($extra != ' (') { - $extra .= ', '; - } - $extra .= 'version <= ' . $dep['max']; - } - - if (isset($dep['exclude'])) { - if (!is_array($dep['exclude'])) { - $dep['exclude'] = array($dep['exclude']); - } - - if ($extra != ' (') { - $extra .= ', '; - } - - $extra .= 'excluded versions: '; - foreach ($dep['exclude'] as $i => $exclude) { - if ($i) { - $extra .= ', '; - } - $extra .= $exclude; - } - } - } - - $extra .= ')'; - if ($extra == ' ()') { - $extra = ''; - } - - return $extra; - } - - /** - * This makes unit-testing a heck of a lot easier - */ - function getPHP_OS() - { - return PHP_OS; - } - - /** - * This makes unit-testing a heck of a lot easier - */ - function getsysname() - { - return $this->_os->getSysname(); - } - - /** - * Specify a dependency on an OS. Use arch for detailed os/processor information - * - * There are two generic OS dependencies that will be the most common, unix and windows. - * Other options are linux, freebsd, darwin (OS X), sunos, irix, hpux, aix - */ - function validateOsDependency($dep) - { - if ($this->_state != PEAR_VALIDATE_INSTALLING && $this->_state != PEAR_VALIDATE_DOWNLOADING) { - return true; - } - - if ($dep['name'] == '*') { - return true; - } - - $not = isset($dep['conflicts']) ? true : false; - switch (strtolower($dep['name'])) { - case 'windows' : - if ($not) { - if (strtolower(substr($this->getPHP_OS(), 0, 3)) == 'win') { - if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { - return $this->raiseError("Cannot install %s on Windows"); - } - - return $this->warning("warning: Cannot install %s on Windows"); - } - } else { - if (strtolower(substr($this->getPHP_OS(), 0, 3)) != 'win') { - if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { - return $this->raiseError("Can only install %s on Windows"); - } - - return $this->warning("warning: Can only install %s on Windows"); - } - } - break; - case 'unix' : - $unices = array('linux', 'freebsd', 'darwin', 'sunos', 'irix', 'hpux', 'aix'); - if ($not) { - if (in_array($this->getSysname(), $unices)) { - if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { - return $this->raiseError("Cannot install %s on any Unix system"); - } - - return $this->warning( "warning: Cannot install %s on any Unix system"); - } - } else { - if (!in_array($this->getSysname(), $unices)) { - if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { - return $this->raiseError("Can only install %s on a Unix system"); - } - - return $this->warning("warning: Can only install %s on a Unix system"); - } - } - break; - default : - if ($not) { - if (strtolower($dep['name']) == strtolower($this->getSysname())) { - if (!isset($this->_options['nodeps']) && - !isset($this->_options['force'])) { - return $this->raiseError('Cannot install %s on ' . $dep['name'] . - ' operating system'); - } - - return $this->warning('warning: Cannot install %s on ' . - $dep['name'] . ' operating system'); - } - } else { - if (strtolower($dep['name']) != strtolower($this->getSysname())) { - if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { - return $this->raiseError('Cannot install %s on ' . - $this->getSysname() . - ' operating system, can only install on ' . $dep['name']); - } - - return $this->warning('warning: Cannot install %s on ' . - $this->getSysname() . - ' operating system, can only install on ' . $dep['name']); - } - } - } - return true; - } - - /** - * This makes unit-testing a heck of a lot easier - */ - function matchSignature($pattern) - { - return $this->_os->matchSignature($pattern); - } - - /** - * Specify a complex dependency on an OS/processor/kernel version, - * Use OS for simple operating system dependency. - * - * This is the only dependency that accepts an eregable pattern. The pattern - * will be matched against the php_uname() output parsed by OS_Guess - */ - function validateArchDependency($dep) - { - if ($this->_state != PEAR_VALIDATE_INSTALLING) { - return true; - } - - $not = isset($dep['conflicts']) ? true : false; - if (!$this->matchSignature($dep['pattern'])) { - if (!$not) { - if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { - return $this->raiseError('%s Architecture dependency failed, does not ' . - 'match "' . $dep['pattern'] . '"'); - } - - return $this->warning('warning: %s Architecture dependency failed, does ' . - 'not match "' . $dep['pattern'] . '"'); - } - - return true; - } - - if ($not) { - if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { - return $this->raiseError('%s Architecture dependency failed, required "' . - $dep['pattern'] . '"'); - } - - return $this->warning('warning: %s Architecture dependency failed, ' . - 'required "' . $dep['pattern'] . '"'); - } - - return true; - } - - /** - * This makes unit-testing a heck of a lot easier - */ - function extension_loaded($name) - { - return extension_loaded($name); - } - - /** - * This makes unit-testing a heck of a lot easier - */ - function phpversion($name = null) - { - if ($name !== null) { - return phpversion($name); - } - - return phpversion(); - } - - function validateExtensionDependency($dep, $required = true) - { - if ($this->_state != PEAR_VALIDATE_INSTALLING && - $this->_state != PEAR_VALIDATE_DOWNLOADING) { - return true; - } - - $loaded = $this->extension_loaded($dep['name']); - $extra = $this->_getExtraString($dep); - if (isset($dep['exclude'])) { - if (!is_array($dep['exclude'])) { - $dep['exclude'] = array($dep['exclude']); - } - } - - if (!isset($dep['min']) && !isset($dep['max']) && - !isset($dep['recommended']) && !isset($dep['exclude']) - ) { - if ($loaded) { - if (isset($dep['conflicts'])) { - if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { - return $this->raiseError('%s conflicts with PHP extension "' . - $dep['name'] . '"' . $extra); - } - - return $this->warning('warning: %s conflicts with PHP extension "' . - $dep['name'] . '"' . $extra); - } - - return true; - } - - if (isset($dep['conflicts'])) { - return true; - } - - if ($required) { - if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { - return $this->raiseError('%s requires PHP extension "' . - $dep['name'] . '"' . $extra); - } - - return $this->warning('warning: %s requires PHP extension "' . - $dep['name'] . '"' . $extra); - } - - return $this->warning('%s can optionally use PHP extension "' . - $dep['name'] . '"' . $extra); - } - - if (!$loaded) { - if (isset($dep['conflicts'])) { - return true; - } - - if (!$required) { - return $this->warning('%s can optionally use PHP extension "' . - $dep['name'] . '"' . $extra); - } - - if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { - return $this->raiseError('%s requires PHP extension "' . $dep['name'] . - '"' . $extra); - } - - return $this->warning('warning: %s requires PHP extension "' . $dep['name'] . - '"' . $extra); - } - - $version = (string) $this->phpversion($dep['name']); - if (empty($version)) { - $version = '0'; - } - - $fail = false; - if (isset($dep['min']) && !version_compare($version, $dep['min'], '>=')) { - $fail = true; - } - - if (isset($dep['max']) && !version_compare($version, $dep['max'], '<=')) { - $fail = true; - } - - if ($fail && !isset($dep['conflicts'])) { - if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { - return $this->raiseError('%s requires PHP extension "' . $dep['name'] . - '"' . $extra . ', installed version is ' . $version); - } - - return $this->warning('warning: %s requires PHP extension "' . $dep['name'] . - '"' . $extra . ', installed version is ' . $version); - } elseif ((isset($dep['min']) || isset($dep['max'])) && !$fail && isset($dep['conflicts'])) { - if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { - return $this->raiseError('%s conflicts with PHP extension "' . - $dep['name'] . '"' . $extra . ', installed version is ' . $version); - } - - return $this->warning('warning: %s conflicts with PHP extension "' . - $dep['name'] . '"' . $extra . ', installed version is ' . $version); - } - - if (isset($dep['exclude'])) { - foreach ($dep['exclude'] as $exclude) { - if (version_compare($version, $exclude, '==')) { - if (isset($dep['conflicts'])) { - continue; - } - - if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { - return $this->raiseError('%s is not compatible with PHP extension "' . - $dep['name'] . '" version ' . - $exclude); - } - - return $this->warning('warning: %s is not compatible with PHP extension "' . - $dep['name'] . '" version ' . - $exclude); - } elseif (version_compare($version, $exclude, '!=') && isset($dep['conflicts'])) { - if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { - return $this->raiseError('%s conflicts with PHP extension "' . - $dep['name'] . '"' . $extra . ', installed version is ' . $version); - } - - return $this->warning('warning: %s conflicts with PHP extension "' . - $dep['name'] . '"' . $extra . ', installed version is ' . $version); - } - } - } - - if (isset($dep['recommended'])) { - if (version_compare($version, $dep['recommended'], '==')) { - return true; - } - - if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { - return $this->raiseError('%s dependency: PHP extension ' . $dep['name'] . - ' version "' . $version . '"' . - ' is not the recommended version "' . $dep['recommended'] . - '", but may be compatible, use --force to install'); - } - - return $this->warning('warning: %s dependency: PHP extension ' . - $dep['name'] . ' version "' . $version . '"' . - ' is not the recommended version "' . $dep['recommended'].'"'); - } - - return true; - } - - function validatePhpDependency($dep) - { - if ($this->_state != PEAR_VALIDATE_INSTALLING && - $this->_state != PEAR_VALIDATE_DOWNLOADING) { - return true; - } - - $version = $this->phpversion(); - $extra = $this->_getExtraString($dep); - if (isset($dep['exclude'])) { - if (!is_array($dep['exclude'])) { - $dep['exclude'] = array($dep['exclude']); - } - } - - if (isset($dep['min'])) { - if (!version_compare($version, $dep['min'], '>=')) { - if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { - return $this->raiseError('%s requires PHP' . - $extra . ', installed version is ' . $version); - } - - return $this->warning('warning: %s requires PHP' . - $extra . ', installed version is ' . $version); - } - } - - if (isset($dep['max'])) { - if (!version_compare($version, $dep['max'], '<=')) { - if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { - return $this->raiseError('%s requires PHP' . - $extra . ', installed version is ' . $version); - } - - return $this->warning('warning: %s requires PHP' . - $extra . ', installed version is ' . $version); - } - } - - if (isset($dep['exclude'])) { - foreach ($dep['exclude'] as $exclude) { - if (version_compare($version, $exclude, '==')) { - if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { - return $this->raiseError('%s is not compatible with PHP version ' . - $exclude); - } - - return $this->warning( - 'warning: %s is not compatible with PHP version ' . - $exclude); - } - } - } - - return true; - } - - /** - * This makes unit-testing a heck of a lot easier - */ - function getPEARVersion() - { - return '1.9.4'; - } - - function validatePearinstallerDependency($dep) - { - $pearversion = $this->getPEARVersion(); - if (array_key_exists('PEAR_RUNTESTS_PEAR_VERSION_RETURN', $GLOBALS)) { - // A means for testing pear-core in local repositories. - return $GLOBALS['PEAR_RUNTESTS_PEAR_VERSION_RETURN']; - } - $extra = $this->_getExtraString($dep); - if (isset($dep['exclude'])) { - if (!is_array($dep['exclude'])) { - $dep['exclude'] = array($dep['exclude']); - } - } - - if (version_compare($pearversion, $dep['min'], '<')) { - if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { - return $this->raiseError('%s requires PEAR Installer' . $extra . - ', installed version is ' . $pearversion); - } - - return $this->warning('warning: %s requires PEAR Installer' . $extra . - ', installed version is ' . $pearversion); - } - - if (isset($dep['max'])) { - if (version_compare($pearversion, $dep['max'], '>')) { - if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { - return $this->raiseError('%s requires PEAR Installer' . $extra . - ', installed version is ' . $pearversion); - } - - return $this->warning('warning: %s requires PEAR Installer' . $extra . - ', installed version is ' . $pearversion); - } - } - - if (isset($dep['exclude'])) { - if (!isset($dep['exclude'][0])) { - $dep['exclude'] = array($dep['exclude']); - } - - foreach ($dep['exclude'] as $exclude) { - if (version_compare($exclude, $pearversion, '==')) { - if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { - return $this->raiseError('%s is not compatible with PEAR Installer ' . - 'version ' . $exclude); - } - - return $this->warning('warning: %s is not compatible with PEAR ' . - 'Installer version ' . $exclude); - } - } - } - - return true; - } - - function validateSubpackageDependency($dep, $required, $params) - { - return $this->validatePackageDependency($dep, $required, $params); - } - - /** - * @param array dependency information (2.0 format) - * @param boolean whether this is a required dependency - * @param array a list of downloaded packages to be installed, if any - * @param boolean if true, then deps on pear.php.net that fail will also check - * against pecl.php.net packages to accomodate extensions that have - * moved to pecl.php.net from pear.php.net - */ - function validatePackageDependency($dep, $required, $params, $depv1 = false) - { - if ($this->_state != PEAR_VALIDATE_INSTALLING && - $this->_state != PEAR_VALIDATE_DOWNLOADING) { - return true; - } - - if (isset($dep['providesextension'])) { - if ($this->extension_loaded($dep['providesextension'])) { - $save = $dep; - $subdep = $dep; - $subdep['name'] = $subdep['providesextension']; - PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - $ret = $this->validateExtensionDependency($subdep, $required); - PEAR::popErrorHandling(); - if (!PEAR::isError($ret)) { - return true; - } - } - } - - if ($this->_state == PEAR_VALIDATE_INSTALLING) { - return $this->_validatePackageInstall($dep, $required, $depv1); - } - - if ($this->_state == PEAR_VALIDATE_DOWNLOADING) { - return $this->_validatePackageDownload($dep, $required, $params, $depv1); - } - } - - function _validatePackageDownload($dep, $required, $params, $depv1 = false) - { - $dep['package'] = $dep['name']; - if (isset($dep['uri'])) { - $dep['channel'] = '__uri'; - } - - $depname = $this->_registry->parsedPackageNameToString($dep, true); - $found = false; - foreach ($params as $param) { - if ($param->isEqual( - array('package' => $dep['name'], - 'channel' => $dep['channel']))) { - $found = true; - break; - } - - if ($depv1 && $dep['channel'] == 'pear.php.net') { - if ($param->isEqual( - array('package' => $dep['name'], - 'channel' => 'pecl.php.net'))) { - $found = true; - break; - } - } - } - - if (!$found && isset($dep['providesextension'])) { - foreach ($params as $param) { - if ($param->isExtension($dep['providesextension'])) { - $found = true; - break; - } - } - } - - if ($found) { - $version = $param->getVersion(); - $installed = false; - $downloaded = true; - } else { - if ($this->_registry->packageExists($dep['name'], $dep['channel'])) { - $installed = true; - $downloaded = false; - $version = $this->_registry->packageinfo($dep['name'], 'version', - $dep['channel']); - } else { - if ($dep['channel'] == 'pecl.php.net' && $this->_registry->packageExists($dep['name'], - 'pear.php.net')) { - $installed = true; - $downloaded = false; - $version = $this->_registry->packageinfo($dep['name'], 'version', - 'pear.php.net'); - } else { - $version = 'not installed or downloaded'; - $installed = false; - $downloaded = false; - } - } - } - - $extra = $this->_getExtraString($dep); - if (isset($dep['exclude']) && !is_array($dep['exclude'])) { - $dep['exclude'] = array($dep['exclude']); - } - - if (!isset($dep['min']) && !isset($dep['max']) && - !isset($dep['recommended']) && !isset($dep['exclude']) - ) { - if ($installed || $downloaded) { - $installed = $installed ? 'installed' : 'downloaded'; - if (isset($dep['conflicts'])) { - $rest = ''; - if ($version) { - $rest = ", $installed version is " . $version; - } - - if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { - return $this->raiseError('%s conflicts with package "' . $depname . '"' . $extra . $rest); - } - - return $this->warning('warning: %s conflicts with package "' . $depname . '"' . $extra . $rest); - } - - return true; - } - - if (isset($dep['conflicts'])) { - return true; - } - - if ($required) { - if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { - return $this->raiseError('%s requires package "' . $depname . '"' . $extra); - } - - return $this->warning('warning: %s requires package "' . $depname . '"' . $extra); - } - - return $this->warning('%s can optionally use package "' . $depname . '"' . $extra); - } - - if (!$installed && !$downloaded) { - if (isset($dep['conflicts'])) { - return true; - } - - if ($required) { - if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { - return $this->raiseError('%s requires package "' . $depname . '"' . $extra); - } - - return $this->warning('warning: %s requires package "' . $depname . '"' . $extra); - } - - return $this->warning('%s can optionally use package "' . $depname . '"' . $extra); - } - - $fail = false; - if (isset($dep['min']) && version_compare($version, $dep['min'], '<')) { - $fail = true; - } - - if (isset($dep['max']) && version_compare($version, $dep['max'], '>')) { - $fail = true; - } - - if ($fail && !isset($dep['conflicts'])) { - $installed = $installed ? 'installed' : 'downloaded'; - $dep['package'] = $dep['name']; - $dep = $this->_registry->parsedPackageNameToString($dep, true); - if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { - return $this->raiseError('%s requires package "' . $depname . '"' . - $extra . ", $installed version is " . $version); - } - - return $this->warning('warning: %s requires package "' . $depname . '"' . - $extra . ", $installed version is " . $version); - } elseif ((isset($dep['min']) || isset($dep['max'])) && !$fail && - isset($dep['conflicts']) && !isset($dep['exclude'])) { - $installed = $installed ? 'installed' : 'downloaded'; - if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { - return $this->raiseError('%s conflicts with package "' . $depname . '"' . $extra . - ", $installed version is " . $version); - } - - return $this->warning('warning: %s conflicts with package "' . $depname . '"' . - $extra . ", $installed version is " . $version); - } - - if (isset($dep['exclude'])) { - $installed = $installed ? 'installed' : 'downloaded'; - foreach ($dep['exclude'] as $exclude) { - if (version_compare($version, $exclude, '==') && !isset($dep['conflicts'])) { - if (!isset($this->_options['nodeps']) && - !isset($this->_options['force']) - ) { - return $this->raiseError('%s is not compatible with ' . - $installed . ' package "' . - $depname . '" version ' . - $exclude); - } - - return $this->warning('warning: %s is not compatible with ' . - $installed . ' package "' . - $depname . '" version ' . - $exclude); - } elseif (version_compare($version, $exclude, '!=') && isset($dep['conflicts'])) { - $installed = $installed ? 'installed' : 'downloaded'; - if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { - return $this->raiseError('%s conflicts with package "' . $depname . '"' . - $extra . ", $installed version is " . $version); - } - - return $this->warning('warning: %s conflicts with package "' . $depname . '"' . - $extra . ", $installed version is " . $version); - } - } - } - - if (isset($dep['recommended'])) { - $installed = $installed ? 'installed' : 'downloaded'; - if (version_compare($version, $dep['recommended'], '==')) { - return true; - } - - if (!$found && $installed) { - $param = $this->_registry->getPackage($dep['name'], $dep['channel']); - } - - if ($param) { - $found = false; - foreach ($params as $parent) { - if ($parent->isEqual($this->_currentPackage)) { - $found = true; - break; - } - } - - if ($found) { - if ($param->isCompatible($parent)) { - return true; - } - } else { // this is for validPackage() calls - $parent = $this->_registry->getPackage($this->_currentPackage['package'], - $this->_currentPackage['channel']); - if ($parent !== null && $param->isCompatible($parent)) { - return true; - } - } - } - - if (!isset($this->_options['nodeps']) && !isset($this->_options['force']) && - !isset($this->_options['loose']) - ) { - return $this->raiseError('%s dependency package "' . $depname . - '" ' . $installed . ' version ' . $version . - ' is not the recommended version ' . $dep['recommended'] . - ', but may be compatible, use --force to install'); - } - - return $this->warning('warning: %s dependency package "' . $depname . - '" ' . $installed . ' version ' . $version . - ' is not the recommended version ' . $dep['recommended']); - } - - return true; - } - - function _validatePackageInstall($dep, $required, $depv1 = false) - { - return $this->_validatePackageDownload($dep, $required, array(), $depv1); - } - - /** - * Verify that uninstalling packages passed in to command line is OK. - * - * @param PEAR_Installer $dl - * @return PEAR_Error|true - */ - function validatePackageUninstall(&$dl) - { - if (PEAR::isError($this->_dependencydb)) { - return $this->_dependencydb; - } - - $params = array(); - // construct an array of "downloaded" packages to fool the package dependency checker - // into using these to validate uninstalls of circular dependencies - $downloaded = &$dl->getUninstallPackages(); - foreach ($downloaded as $i => $pf) { - if (!class_exists('PEAR_Downloader_Package')) { - require_once 'PEAR/Downloader/Package.php'; - } - $dp = &new PEAR_Downloader_Package($dl); - $dp->setPackageFile($downloaded[$i]); - $params[$i] = &$dp; - } - - // check cache - $memyselfandI = strtolower($this->_currentPackage['channel']) . '/' . - strtolower($this->_currentPackage['package']); - if (isset($dl->___uninstall_package_cache)) { - $badpackages = $dl->___uninstall_package_cache; - if (isset($badpackages[$memyselfandI]['warnings'])) { - foreach ($badpackages[$memyselfandI]['warnings'] as $warning) { - $dl->log(0, $warning[0]); - } - } - - if (isset($badpackages[$memyselfandI]['errors'])) { - foreach ($badpackages[$memyselfandI]['errors'] as $error) { - if (is_array($error)) { - $dl->log(0, $error[0]); - } else { - $dl->log(0, $error->getMessage()); - } - } - - if (isset($this->_options['nodeps']) || isset($this->_options['force'])) { - return $this->warning( - 'warning: %s should not be uninstalled, other installed packages depend ' . - 'on this package'); - } - - return $this->raiseError( - '%s cannot be uninstalled, other installed packages depend on this package'); - } - - return true; - } - - // first, list the immediate parents of each package to be uninstalled - $perpackagelist = array(); - $allparents = array(); - foreach ($params as $i => $param) { - $a = array( - 'channel' => strtolower($param->getChannel()), - 'package' => strtolower($param->getPackage()) - ); - - $deps = $this->_dependencydb->getDependentPackages($a); - if ($deps) { - foreach ($deps as $d) { - $pardeps = $this->_dependencydb->getDependencies($d); - foreach ($pardeps as $dep) { - if (strtolower($dep['dep']['channel']) == $a['channel'] && - strtolower($dep['dep']['name']) == $a['package']) { - if (!isset($perpackagelist[$a['channel'] . '/' . $a['package']])) { - $perpackagelist[$a['channel'] . '/' . $a['package']] = array(); - } - $perpackagelist[$a['channel'] . '/' . $a['package']][] - = array($d['channel'] . '/' . $d['package'], $dep); - if (!isset($allparents[$d['channel'] . '/' . $d['package']])) { - $allparents[$d['channel'] . '/' . $d['package']] = array(); - } - if (!isset($allparents[$d['channel'] . '/' . $d['package']][$a['channel'] . '/' . $a['package']])) { - $allparents[$d['channel'] . '/' . $d['package']][$a['channel'] . '/' . $a['package']] = array(); - } - $allparents[$d['channel'] . '/' . $d['package']] - [$a['channel'] . '/' . $a['package']][] - = array($d, $dep); - } - } - } - } - } - - // next, remove any packages from the parents list that are not installed - $remove = array(); - foreach ($allparents as $parent => $d1) { - foreach ($d1 as $d) { - if ($this->_registry->packageExists($d[0][0]['package'], $d[0][0]['channel'])) { - continue; - } - $remove[$parent] = true; - } - } - - // next remove any packages from the parents list that are not passed in for - // uninstallation - foreach ($allparents as $parent => $d1) { - foreach ($d1 as $d) { - foreach ($params as $param) { - if (strtolower($param->getChannel()) == $d[0][0]['channel'] && - strtolower($param->getPackage()) == $d[0][0]['package']) { - // found it - continue 3; - } - } - $remove[$parent] = true; - } - } - - // remove all packages whose dependencies fail - // save which ones failed for error reporting - $badchildren = array(); - do { - $fail = false; - foreach ($remove as $package => $unused) { - if (!isset($allparents[$package])) { - continue; - } - - foreach ($allparents[$package] as $kid => $d1) { - foreach ($d1 as $depinfo) { - if ($depinfo[1]['type'] != 'optional') { - if (isset($badchildren[$kid])) { - continue; - } - $badchildren[$kid] = true; - $remove[$kid] = true; - $fail = true; - continue 2; - } - } - } - if ($fail) { - // start over, we removed some children - continue 2; - } - } - } while ($fail); - - // next, construct the list of packages that can't be uninstalled - $badpackages = array(); - $save = $this->_currentPackage; - foreach ($perpackagelist as $package => $packagedeps) { - foreach ($packagedeps as $parent) { - if (!isset($remove[$parent[0]])) { - continue; - } - - $packagename = $this->_registry->parsePackageName($parent[0]); - $packagename['channel'] = $this->_registry->channelAlias($packagename['channel']); - $pa = $this->_registry->getPackage($packagename['package'], $packagename['channel']); - $packagename['package'] = $pa->getPackage(); - $this->_currentPackage = $packagename; - // parent is not present in uninstall list, make sure we can actually - // uninstall it (parent dep is optional) - $parentname['channel'] = $this->_registry->channelAlias($parent[1]['dep']['channel']); - $pa = $this->_registry->getPackage($parent[1]['dep']['name'], $parent[1]['dep']['channel']); - $parentname['package'] = $pa->getPackage(); - $parent[1]['dep']['package'] = $parentname['package']; - $parent[1]['dep']['channel'] = $parentname['channel']; - if ($parent[1]['type'] == 'optional') { - $test = $this->_validatePackageUninstall($parent[1]['dep'], false, $dl); - if ($test !== true) { - $badpackages[$package]['warnings'][] = $test; - } - } else { - $test = $this->_validatePackageUninstall($parent[1]['dep'], true, $dl); - if ($test !== true) { - $badpackages[$package]['errors'][] = $test; - } - } - } - } - - $this->_currentPackage = $save; - $dl->___uninstall_package_cache = $badpackages; - if (isset($badpackages[$memyselfandI])) { - if (isset($badpackages[$memyselfandI]['warnings'])) { - foreach ($badpackages[$memyselfandI]['warnings'] as $warning) { - $dl->log(0, $warning[0]); - } - } - - if (isset($badpackages[$memyselfandI]['errors'])) { - foreach ($badpackages[$memyselfandI]['errors'] as $error) { - if (is_array($error)) { - $dl->log(0, $error[0]); - } else { - $dl->log(0, $error->getMessage()); - } - } - - if (isset($this->_options['nodeps']) || isset($this->_options['force'])) { - return $this->warning( - 'warning: %s should not be uninstalled, other installed packages depend ' . - 'on this package'); - } - - return $this->raiseError( - '%s cannot be uninstalled, other installed packages depend on this package'); - } - } - - return true; - } - - function _validatePackageUninstall($dep, $required, $dl) - { - $depname = $this->_registry->parsedPackageNameToString($dep, true); - $version = $this->_registry->packageinfo($dep['package'], 'version', $dep['channel']); - if (!$version) { - return true; - } - - $extra = $this->_getExtraString($dep); - if (isset($dep['exclude']) && !is_array($dep['exclude'])) { - $dep['exclude'] = array($dep['exclude']); - } - - if (isset($dep['conflicts'])) { - return true; // uninstall OK - these packages conflict (probably installed with --force) - } - - if (!isset($dep['min']) && !isset($dep['max'])) { - if (!$required) { - return $this->warning('"' . $depname . '" can be optionally used by ' . - 'installed package %s' . $extra); - } - - if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { - return $this->raiseError('"' . $depname . '" is required by ' . - 'installed package %s' . $extra); - } - - return $this->warning('warning: "' . $depname . '" is required by ' . - 'installed package %s' . $extra); - } - - $fail = false; - if (isset($dep['min']) && version_compare($version, $dep['min'], '>=')) { - $fail = true; - } - - if (isset($dep['max']) && version_compare($version, $dep['max'], '<=')) { - $fail = true; - } - - // we re-use this variable, preserve the original value - $saverequired = $required; - if (!$required) { - return $this->warning($depname . $extra . ' can be optionally used by installed package' . - ' "%s"'); - } - - if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { - return $this->raiseError($depname . $extra . ' is required by installed package' . - ' "%s"'); - } - - return $this->raiseError('warning: ' . $depname . $extra . - ' is required by installed package "%s"'); - } - - /** - * validate a downloaded package against installed packages - * - * As of PEAR 1.4.3, this will only validate - * - * @param array|PEAR_Downloader_Package|PEAR_PackageFile_v1|PEAR_PackageFile_v2 - * $pkg package identifier (either - * array('package' => blah, 'channel' => blah) or an array with - * index 'info' referencing an object) - * @param PEAR_Downloader $dl - * @param array $params full list of packages to install - * @return true|PEAR_Error - */ - function validatePackage($pkg, &$dl, $params = array()) - { - if (is_array($pkg) && isset($pkg['info'])) { - $deps = $this->_dependencydb->getDependentPackageDependencies($pkg['info']); - } else { - $deps = $this->_dependencydb->getDependentPackageDependencies($pkg); - } - - $fail = false; - if ($deps) { - if (!class_exists('PEAR_Downloader_Package')) { - require_once 'PEAR/Downloader/Package.php'; - } - - $dp = &new PEAR_Downloader_Package($dl); - if (is_object($pkg)) { - $dp->setPackageFile($pkg); - } else { - $dp->setDownloadURL($pkg); - } - - PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - foreach ($deps as $channel => $info) { - foreach ($info as $package => $ds) { - foreach ($params as $packd) { - if (strtolower($packd->getPackage()) == strtolower($package) && - $packd->getChannel() == $channel) { - $dl->log(3, 'skipping installed package check of "' . - $this->_registry->parsedPackageNameToString( - array('channel' => $channel, 'package' => $package), - true) . - '", version "' . $packd->getVersion() . '" will be ' . - 'downloaded and installed'); - continue 2; // jump to next package - } - } - - foreach ($ds as $d) { - $checker = &new PEAR_Dependency2($this->_config, $this->_options, - array('channel' => $channel, 'package' => $package), $this->_state); - $dep = $d['dep']; - $required = $d['type'] == 'required'; - $ret = $checker->_validatePackageDownload($dep, $required, array(&$dp)); - if (is_array($ret)) { - $dl->log(0, $ret[0]); - } elseif (PEAR::isError($ret)) { - $dl->log(0, $ret->getMessage()); - $fail = true; - } - } - } - } - PEAR::popErrorHandling(); - } - - if ($fail) { - return $this->raiseError( - '%s cannot be installed, conflicts with installed packages'); - } - - return true; - } - - /** - * validate a package.xml 1.0 dependency - */ - function validateDependency1($dep, $params = array()) - { - if (!isset($dep['optional'])) { - $dep['optional'] = 'no'; - } - - list($newdep, $type) = $this->normalizeDep($dep); - if (!$newdep) { - return $this->raiseError("Invalid Dependency"); - } - - if (method_exists($this, "validate{$type}Dependency")) { - return $this->{"validate{$type}Dependency"}($newdep, $dep['optional'] == 'no', - $params, true); - } - } - - /** - * Convert a 1.0 dep into a 2.0 dep - */ - function normalizeDep($dep) - { - $types = array( - 'pkg' => 'Package', - 'ext' => 'Extension', - 'os' => 'Os', - 'php' => 'Php' - ); - - if (!isset($types[$dep['type']])) { - return array(false, false); - } - - $type = $types[$dep['type']]; - - $newdep = array(); - switch ($type) { - case 'Package' : - $newdep['channel'] = 'pear.php.net'; - case 'Extension' : - case 'Os' : - $newdep['name'] = $dep['name']; - break; - } - - $dep['rel'] = PEAR_Dependency2::signOperator($dep['rel']); - switch ($dep['rel']) { - case 'has' : - return array($newdep, $type); - break; - case 'not' : - $newdep['conflicts'] = true; - break; - case '>=' : - case '>' : - $newdep['min'] = $dep['version']; - if ($dep['rel'] == '>') { - $newdep['exclude'] = $dep['version']; - } - break; - case '<=' : - case '<' : - $newdep['max'] = $dep['version']; - if ($dep['rel'] == '<') { - $newdep['exclude'] = $dep['version']; - } - break; - case 'ne' : - case '!=' : - $newdep['min'] = '0'; - $newdep['max'] = '100000'; - $newdep['exclude'] = $dep['version']; - break; - case '==' : - $newdep['min'] = $dep['version']; - $newdep['max'] = $dep['version']; - break; - } - if ($type == 'Php') { - if (!isset($newdep['min'])) { - $newdep['min'] = '4.4.0'; - } - - if (!isset($newdep['max'])) { - $newdep['max'] = '6.0.0'; - } - } - return array($newdep, $type); - } - - /** - * Converts text comparing operators to them sign equivalents - * - * Example: 'ge' to '>=' - * - * @access public - * @param string Operator - * @return string Sign equivalent - */ - function signOperator($operator) - { - switch($operator) { - case 'lt': return '<'; - case 'le': return '<='; - case 'gt': return '>'; - case 'ge': return '>='; - case 'eq': return '=='; - case 'ne': return '!='; - default: - return $operator; - } - } - - function raiseError($msg) - { - if (isset($this->_options['ignore-errors'])) { - return $this->warning($msg); - } - - return PEAR::raiseError(sprintf($msg, $this->_registry->parsedPackageNameToString( - $this->_currentPackage, true))); - } - - function warning($msg) - { - return array(sprintf($msg, $this->_registry->parsedPackageNameToString( - $this->_currentPackage, true))); - } -} \ No newline at end of file diff --git a/pear/PEAR/DependencyDB.php b/pear/PEAR/DependencyDB.php deleted file mode 100644 index e3ad0ec..0000000 --- a/pear/PEAR/DependencyDB.php +++ /dev/null @@ -1,757 +0,0 @@ - - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a1 - */ - -/** - * Needed for error handling - */ -require_once 'PEAR.php'; -require_once 'PEAR/Config.php'; - -$GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'] = array(); -/** - * Track dependency relationships between installed packages - * @category pear - * @package PEAR - * @author Greg Beaver - * @author Tomas V.V.Cox - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a1 - */ -class PEAR_DependencyDB -{ - // {{{ properties - - /** - * This is initialized by {@link setConfig()} - * @var PEAR_Config - */ - private $_config; - /** - * This is initialized by {@link setConfig()} - * @var PEAR_Registry - */ - private $_registry; - /** - * Filename of the dependency DB (usually .depdb) - * @var string - */ - private $_depdb = false; - /** - * File name of the lockfile (usually .depdblock) - * @var string - */ - private $_lockfile = false; - /** - * Open file resource for locking the lockfile - * @var resource|false - */ - private $_lockFp = false; - /** - * API version of this class, used to validate a file on-disk - * @var string - */ - private $_version = '1.0'; - /** - * Cached dependency database file - * @var array|null - */ - private $_cache; - - // }}} - // {{{ & singleton() - - /** - * Get a raw dependency database. Calls setConfig() and assertDepsDB() - * @param PEAR_Config - * @param string|false full path to the dependency database, or false to use default - * @return PEAR_DependencyDB|PEAR_Error - */ - public static function &singleton(&$config, $depdb = false) - { - $phpdir = $config->get('php_dir', null, 'pear.php.net'); - if (!isset($GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'][$phpdir])) { - $a = new PEAR_DependencyDB; - $GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'][$phpdir] = &$a; - $a->setConfig($config, $depdb); - $e = $a->assertDepsDB(); - if (PEAR::isError($e)) { - return $e; - } - } - - return $GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'][$phpdir]; - } - - /** - * Set up the registry/location of dependency DB - * @param PEAR_Config|false - * @param string|false full path to the dependency database, or false to use default - */ - public function setConfig(&$config, $depdb = false) - { - if (!$config) { - $this->_config = &PEAR_Config::singleton(); - } else { - $this->_config = &$config; - } - - $this->_registry = &$this->_config->getRegistry(); - if (!$depdb) { - $this->_depdb = $this->_config->get('php_dir', null, 'pear.php.net') . - DIRECTORY_SEPARATOR . '.depdb'; - } else { - $this->_depdb = $depdb; - } - - $this->_lockfile = dirname($this->_depdb) . DIRECTORY_SEPARATOR . '.depdblock'; - } - // }}} - - public function hasWriteAccess() - { - if (!file_exists($this->_depdb)) { - $dir = $this->_depdb; - while ($dir && $dir != '.') { - $dir = dirname($dir); // cd .. - if ($dir != '.' && file_exists($dir)) { - if (is_writeable($dir)) { - return true; - } - - return false; - } - } - - return false; - } - - return is_writeable($this->_depdb); - } - - // {{{ assertDepsDB() - - /** - * Create the dependency database, if it doesn't exist. Error if the database is - * newer than the code reading it. - * @return void|PEAR_Error - */ - public function assertDepsDB() - { - if (!is_file($this->_depdb)) { - $this->rebuildDB(); - return; - } - - $depdb = $this->_getDepDB(); - // Datatype format has been changed, rebuild the Deps DB - if ($depdb['_version'] < $this->_version) { - $this->rebuildDB(); - } - - if ($depdb['_version']{0} > $this->_version{0}) { - return PEAR::raiseError('Dependency database is version ' . - $depdb['_version'] . ', and we are version ' . - $this->_version . ', cannot continue'); - } - } - - /** - * Get a list of installed packages that depend on this package - * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array - * @return array|false - */ - public function getDependentPackages(&$pkg) - { - $data = $this->_getDepDB(); - if (is_object($pkg)) { - $channel = strtolower($pkg->getChannel()); - $package = strtolower($pkg->getPackage()); - } else { - $channel = strtolower($pkg['channel']); - $package = strtolower($pkg['package']); - } - - if (isset($data['packages'][$channel][$package])) { - return $data['packages'][$channel][$package]; - } - - return false; - } - - /** - * Get a list of the actual dependencies of installed packages that depend on - * a package. - * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array - * @return array|false - */ - public function getDependentPackageDependencies(&$pkg) - { - $data = $this->_getDepDB(); - if (is_object($pkg)) { - $channel = strtolower($pkg->getChannel()); - $package = strtolower($pkg->getPackage()); - } else { - $channel = strtolower($pkg['channel']); - $package = strtolower($pkg['package']); - } - - $depend = $this->getDependentPackages($pkg); - if (!$depend) { - return false; - } - - $dependencies = array(); - foreach ($depend as $info) { - $temp = $this->getDependencies($info); - foreach ($temp as $dep) { - if ( - isset($dep['dep'], $dep['dep']['channel'], $dep['dep']['name']) && - strtolower($dep['dep']['channel']) == $channel && - strtolower($dep['dep']['name']) == $package - ) { - if (!isset($dependencies[$info['channel']])) { - $dependencies[$info['channel']] = array(); - } - - if (!isset($dependencies[$info['channel']][$info['package']])) { - $dependencies[$info['channel']][$info['package']] = array(); - } - $dependencies[$info['channel']][$info['package']][] = $dep; - } - } - } - - return $dependencies; - } - - /** - * Get a list of dependencies of this installed package - * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array - * @return array|false - */ - public function getDependencies(&$pkg) - { - if (is_object($pkg)) { - $channel = strtolower($pkg->getChannel()); - $package = strtolower($pkg->getPackage()); - } else { - $channel = strtolower($pkg['channel']); - $package = strtolower($pkg['package']); - } - - $data = $this->_getDepDB(); - if (isset($data['dependencies'][$channel][$package])) { - return $data['dependencies'][$channel][$package]; - } - - return false; - } - - /** - * Determine whether $parent depends on $child, near or deep - * @param array|PEAR_PackageFile_v2|PEAR_PackageFile_v2 - * @param array|PEAR_PackageFile_v2|PEAR_PackageFile_v2 - */ - public function dependsOn($parent, $child) - { - $c = array(); - $this->_getDepDB(); - return $this->_dependsOn($parent, $child, $c); - } - - private function _dependsOn($parent, $child, &$checked) - { - if (is_object($parent)) { - $channel = strtolower($parent->getChannel()); - $package = strtolower($parent->getPackage()); - } else { - $channel = strtolower($parent['channel']); - $package = strtolower($parent['package']); - } - - if (is_object($child)) { - $depchannel = strtolower($child->getChannel()); - $deppackage = strtolower($child->getPackage()); - } else { - $depchannel = strtolower($child['channel']); - $deppackage = strtolower($child['package']); - } - - if (isset($checked[$channel][$package][$depchannel][$deppackage])) { - return false; // avoid endless recursion - } - - $checked[$channel][$package][$depchannel][$deppackage] = true; - if (!isset($this->_cache['dependencies'][$channel][$package])) { - return false; - } - - foreach ($this->_cache['dependencies'][$channel][$package] as $info) { - if (isset($info['dep']['uri'])) { - if (is_object($child)) { - if ($info['dep']['uri'] == $child->getURI()) { - return true; - } - } elseif (isset($child['uri'])) { - if ($info['dep']['uri'] == $child['uri']) { - return true; - } - } - return false; - } - - if (strtolower($info['dep']['channel']) == $depchannel && - strtolower($info['dep']['name']) == $deppackage) { - return true; - } - } - - foreach ($this->_cache['dependencies'][$channel][$package] as $info) { - if (isset($info['dep']['uri'])) { - if ($this->_dependsOn(array( - 'uri' => $info['dep']['uri'], - 'package' => $info['dep']['name']), $child, $checked)) { - return true; - } - } else { - if ($this->_dependsOn(array( - 'channel' => $info['dep']['channel'], - 'package' => $info['dep']['name']), $child, $checked)) { - return true; - } - } - } - - return false; - } - - /** - * Register dependencies of a package that is being installed or upgraded - * @param PEAR_PackageFile_v2|PEAR_PackageFile_v2 - */ - public function installPackage(&$package) - { - $data = $this->_getDepDB(); - unset($this->_cache); - $this->_setPackageDeps($data, $package); - $this->_writeDepDB($data); - } - - /** - * Remove dependencies of a package that is being uninstalled, or upgraded. - * - * Upgraded packages first uninstall, then install - * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array If an array, then it must have - * indices 'channel' and 'package' - */ - public function uninstallPackage(&$pkg) - { - $data = $this->_getDepDB(); - unset($this->_cache); - if (is_object($pkg)) { - $channel = strtolower($pkg->getChannel()); - $package = strtolower($pkg->getPackage()); - } else { - $channel = strtolower($pkg['channel']); - $package = strtolower($pkg['package']); - } - - if (!isset($data['dependencies'][$channel][$package])) { - return true; - } - - foreach ($data['dependencies'][$channel][$package] as $dep) { - $found = false; - $depchannel = isset($dep['dep']['uri']) ? '__uri' : strtolower($dep['dep']['channel']); - $depname = strtolower($dep['dep']['name']); - if (isset($data['packages'][$depchannel][$depname])) { - foreach ($data['packages'][$depchannel][$depname] as $i => $info) { - if ($info['channel'] == $channel && $info['package'] == $package) { - $found = true; - break; - } - } - } - - if ($found) { - unset($data['packages'][$depchannel][$depname][$i]); - if (!count($data['packages'][$depchannel][$depname])) { - unset($data['packages'][$depchannel][$depname]); - if (!count($data['packages'][$depchannel])) { - unset($data['packages'][$depchannel]); - } - } else { - $data['packages'][$depchannel][$depname] = - array_values($data['packages'][$depchannel][$depname]); - } - } - } - - unset($data['dependencies'][$channel][$package]); - if (!count($data['dependencies'][$channel])) { - unset($data['dependencies'][$channel]); - } - - if (!count($data['dependencies'])) { - unset($data['dependencies']); - } - - if (!count($data['packages'])) { - unset($data['packages']); - } - - $this->_writeDepDB($data); - } - - /** - * Rebuild the dependency DB by reading registry entries. - * @return true|PEAR_Error - */ - public function rebuildDB() - { - $depdb = array('_version' => $this->_version); - if (!$this->hasWriteAccess()) { - // allow startup for read-only with older Registry - return $depdb; - } - - $packages = $this->_registry->listAllPackages(); - if (PEAR::isError($packages)) { - return $packages; - } - - foreach ($packages as $channel => $ps) { - foreach ($ps as $package) { - $package = $this->_registry->getPackage($package, $channel); - if (PEAR::isError($package)) { - return $package; - } - $this->_setPackageDeps($depdb, $package); - } - } - - $error = $this->_writeDepDB($depdb); - if (PEAR::isError($error)) { - return $error; - } - - $this->_cache = $depdb; - return true; - } - - /** - * Register usage of the dependency DB to prevent race conditions - * @param int one of the LOCK_* constants - * @return true|PEAR_Error - */ - private function _lock($mode = LOCK_EX) - { - if (stristr(php_uname(), 'Windows 9')) { - return true; - } - - if ($mode != LOCK_UN && is_resource($this->_lockFp)) { - // XXX does not check type of lock (LOCK_SH/LOCK_EX) - return true; - } - - $open_mode = 'w'; - // XXX People reported problems with LOCK_SH and 'w' - if ($mode === LOCK_SH) { - if (!file_exists($this->_lockfile)) { - touch($this->_lockfile); - } elseif (!is_file($this->_lockfile)) { - return PEAR::raiseError('could not create Dependency lock file, ' . - 'it exists and is not a regular file'); - } - $open_mode = 'r'; - } - - if (!is_resource($this->_lockFp)) { - $this->_lockFp = @fopen($this->_lockfile, $open_mode); - } - - if (!is_resource($this->_lockFp)) { - return PEAR::raiseError("could not create Dependency lock file" . - (isset($php_errormsg) ? ": " . $php_errormsg : "")); - } - - if (!(int)flock($this->_lockFp, $mode)) { - switch ($mode) { - case LOCK_SH: $str = 'shared'; break; - case LOCK_EX: $str = 'exclusive'; break; - case LOCK_UN: $str = 'unlock'; break; - default: $str = 'unknown'; break; - } - - return PEAR::raiseError("could not acquire $str lock ($this->_lockfile)"); - } - - return true; - } - - /** - * Release usage of dependency DB - * @return true|PEAR_Error - */ - private function _unlock() - { - $ret = $this->_lock(LOCK_UN); - if (is_resource($this->_lockFp)) { - fclose($this->_lockFp); - } - $this->_lockFp = null; - return $ret; - } - - /** - * Load the dependency database from disk, or return the cache - * @return array|PEAR_Error - */ - private function _getDepDB() - { - if (!$this->hasWriteAccess()) { - return array('_version' => $this->_version); - } - - if (isset($this->_cache)) { - return $this->_cache; - } - - if (!$fp = fopen($this->_depdb, 'r')) { - $err = PEAR::raiseError("Could not open dependencies file `".$this->_depdb."'"); - return $err; - } - - $rt = get_magic_quotes_runtime(); - set_magic_quotes_runtime(0); - clearstatcache(); - fclose($fp); - $data = unserialize(file_get_contents($this->_depdb)); - set_magic_quotes_runtime($rt); - $this->_cache = $data; - return $data; - } - - /** - * Write out the dependency database to disk - * @param array the database - * @return true|PEAR_Error - */ - private function _writeDepDB(&$deps) - { - if (PEAR::isError($e = $this->_lock(LOCK_EX))) { - return $e; - } - - if (!$fp = fopen($this->_depdb, 'wb')) { - $this->_unlock(); - return PEAR::raiseError("Could not open dependencies file `".$this->_depdb."' for writing"); - } - - $rt = get_magic_quotes_runtime(); - set_magic_quotes_runtime(0); - fwrite($fp, serialize($deps)); - set_magic_quotes_runtime($rt); - fclose($fp); - $this->_unlock(); - $this->_cache = $deps; - return true; - } - - /** - * Register all dependencies from a package in the dependencies database, in essence - * "installing" the package's dependency information - * @param array the database - * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 - */ - private function _setPackageDeps(&$data, &$pkg) - { - $pkg->setConfig($this->_config); - if ($pkg->getPackagexmlVersion() == '1.0') { - $gen = &$pkg->getDefaultGenerator(); - $deps = $gen->dependenciesToV2(); - } else { - $deps = $pkg->getDeps(true); - } - - if (!$deps) { - return; - } - - if (!is_array($data)) { - $data = array(); - } - - if (!isset($data['dependencies'])) { - $data['dependencies'] = array(); - } - - $channel = strtolower($pkg->getChannel()); - $package = strtolower($pkg->getPackage()); - - if (!isset($data['dependencies'][$channel])) { - $data['dependencies'][$channel] = array(); - } - - $data['dependencies'][$channel][$package] = array(); - if (isset($deps['required']['package'])) { - if (!isset($deps['required']['package'][0])) { - $deps['required']['package'] = array($deps['required']['package']); - } - - foreach ($deps['required']['package'] as $dep) { - $this->_registerDep($data, $pkg, $dep, 'required'); - } - } - - if (isset($deps['optional']['package'])) { - if (!isset($deps['optional']['package'][0])) { - $deps['optional']['package'] = array($deps['optional']['package']); - } - - foreach ($deps['optional']['package'] as $dep) { - $this->_registerDep($data, $pkg, $dep, 'optional'); - } - } - - if (isset($deps['required']['subpackage'])) { - if (!isset($deps['required']['subpackage'][0])) { - $deps['required']['subpackage'] = array($deps['required']['subpackage']); - } - - foreach ($deps['required']['subpackage'] as $dep) { - $this->_registerDep($data, $pkg, $dep, 'required'); - } - } - - if (isset($deps['optional']['subpackage'])) { - if (!isset($deps['optional']['subpackage'][0])) { - $deps['optional']['subpackage'] = array($deps['optional']['subpackage']); - } - - foreach ($deps['optional']['subpackage'] as $dep) { - $this->_registerDep($data, $pkg, $dep, 'optional'); - } - } - - if (isset($deps['group'])) { - if (!isset($deps['group'][0])) { - $deps['group'] = array($deps['group']); - } - - foreach ($deps['group'] as $group) { - if (isset($group['package'])) { - if (!isset($group['package'][0])) { - $group['package'] = array($group['package']); - } - - foreach ($group['package'] as $dep) { - $this->_registerDep($data, $pkg, $dep, 'optional', - $group['attribs']['name']); - } - } - - if (isset($group['subpackage'])) { - if (!isset($group['subpackage'][0])) { - $group['subpackage'] = array($group['subpackage']); - } - - foreach ($group['subpackage'] as $dep) { - $this->_registerDep($data, $pkg, $dep, 'optional', - $group['attribs']['name']); - } - } - } - } - - if ($data['dependencies'][$channel][$package] == array()) { - unset($data['dependencies'][$channel][$package]); - if (!count($data['dependencies'][$channel])) { - unset($data['dependencies'][$channel]); - } - } - } - - /** - * @param array the database - * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 - * @param array the specific dependency - * @param required|optional whether this is a required or an optional dep - * @param string|false dependency group this dependency is from, or false for ordinary dep - */ - private function _registerDep(&$data, &$pkg, $dep, $type, $group = false) - { - $info = array( - 'dep' => $dep, - 'type' => $type, - 'group' => $group - ); - - $dep = array_map('strtolower', $dep); - $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri'; - if (!isset($data['dependencies'])) { - $data['dependencies'] = array(); - } - - $channel = strtolower($pkg->getChannel()); - $package = strtolower($pkg->getPackage()); - - if (!isset($data['dependencies'][$channel])) { - $data['dependencies'][$channel] = array(); - } - - if (!isset($data['dependencies'][$channel][$package])) { - $data['dependencies'][$channel][$package] = array(); - } - - $data['dependencies'][$channel][$package][] = $info; - if (isset($data['packages'][$depchannel][$dep['name']])) { - $found = false; - foreach ($data['packages'][$depchannel][$dep['name']] as $i => $p) { - if ($p['channel'] == $channel && $p['package'] == $package) { - $found = true; - break; - } - } - } else { - if (!isset($data['packages'])) { - $data['packages'] = array(); - } - - if (!isset($data['packages'][$depchannel])) { - $data['packages'][$depchannel] = array(); - } - - if (!isset($data['packages'][$depchannel][$dep['name']])) { - $data['packages'][$depchannel][$dep['name']] = array(); - } - - $found = false; - } - - if (!$found) { - $data['packages'][$depchannel][$dep['name']][] = array( - 'channel' => $channel, - 'package' => $package - ); - } - } -} diff --git a/pear/PEAR/Downloader.php b/pear/PEAR/Downloader.php deleted file mode 100644 index b42a210..0000000 --- a/pear/PEAR/Downloader.php +++ /dev/null @@ -1,1766 +0,0 @@ - - * @author Stig Bakken - * @author Tomas V. V. Cox - * @author Martin Jansen - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.3.0 - */ - -/** - * Needed for constants, extending - */ -require_once 'PEAR/Common.php'; - -define('PEAR_INSTALLER_OK', 1); -define('PEAR_INSTALLER_FAILED', 0); -define('PEAR_INSTALLER_SKIPPED', -1); -define('PEAR_INSTALLER_ERROR_NO_PREF_STATE', 2); - -/** - * Administration class used to download anything from the internet (PEAR Packages, - * static URLs, xml files) - * - * @category pear - * @package PEAR - * @author Greg Beaver - * @author Stig Bakken - * @author Tomas V. V. Cox - * @author Martin Jansen - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.3.0 - */ -class PEAR_Downloader extends PEAR_Common -{ - /** - * @var PEAR_Registry - * @access private - */ - var $_registry; - - /** - * Preferred Installation State (snapshot, devel, alpha, beta, stable) - * @var string|null - * @access private - */ - var $_preferredState; - - /** - * Options from command-line passed to Install. - * - * Recognized options:
    - * - onlyreqdeps : install all required dependencies as well - * - alldeps : install all dependencies, including optional - * - installroot : base relative path to install files in - * - force : force a download even if warnings would prevent it - * - nocompress : download uncompressed tarballs - * @see PEAR_Command_Install - * @access private - * @var array - */ - var $_options; - - /** - * Downloaded Packages after a call to download(). - * - * Format of each entry: - * - * - * array('pkg' => 'package_name', 'file' => '/path/to/local/file', - * 'info' => array() // parsed package.xml - * ); - * - * @access private - * @var array - */ - var $_downloadedPackages = array(); - - /** - * Packages slated for download. - * - * This is used to prevent downloading a package more than once should it be a dependency - * for two packages to be installed. - * Format of each entry: - * - *
    -     * array('package_name1' => parsed package.xml, 'package_name2' => parsed package.xml,
    -     * );
    -     * 
    - * @access private - * @var array - */ - var $_toDownload = array(); - - /** - * Array of every package installed, with names lower-cased. - * - * Format: - * - * array('package1' => 0, 'package2' => 1, ); - * - * @var array - */ - var $_installed = array(); - - /** - * @var array - * @access private - */ - var $_errorStack = array(); - - /** - * @var boolean - * @access private - */ - var $_internalDownload = false; - - /** - * Temporary variable used in sorting packages by dependency in {@link sortPkgDeps()} - * @var array - * @access private - */ - var $_packageSortTree; - - /** - * Temporary directory, or configuration value where downloads will occur - * @var string - */ - var $_downloadDir; - - /** - * @param PEAR_Frontend_* - * @param array - * @param PEAR_Config - */ - function PEAR_Downloader(&$ui, $options, &$config) - { - parent::PEAR_Common(); - $this->_options = $options; - $this->config = &$config; - $this->_preferredState = $this->config->get('preferred_state'); - $this->ui = &$ui; - if (!$this->_preferredState) { - // don't inadvertantly use a non-set preferred_state - $this->_preferredState = null; - } - - if (isset($this->_options['installroot'])) { - $this->config->setInstallRoot($this->_options['installroot']); - } - $this->_registry = &$config->getRegistry(); - - if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) { - $this->_installed = $this->_registry->listAllPackages(); - foreach ($this->_installed as $key => $unused) { - if (!count($unused)) { - continue; - } - $strtolower = create_function('$a','return strtolower($a);'); - array_walk($this->_installed[$key], $strtolower); - } - } - } - - /** - * Attempt to discover a channel's remote capabilities from - * its server name - * @param string - * @return boolean - */ - function discover($channel) - { - $this->log(1, 'Attempting to discover channel "' . $channel . '"...'); - PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - $callback = $this->ui ? array(&$this, '_downloadCallback') : null; - if (!class_exists('System')) { - require_once 'System.php'; - } - - $tmpdir = $this->config->get('temp_dir'); - $tmp = System::mktemp('-d -t "' . $tmpdir . '"'); - $a = $this->downloadHttp('http://' . $channel . '/channel.xml', $this->ui, $tmp, $callback, false); - PEAR::popErrorHandling(); - if (PEAR::isError($a)) { - // Attempt to fallback to https automatically. - PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - $this->log(1, 'Attempting fallback to https instead of http on channel "' . $channel . '"...'); - $a = $this->downloadHttp('https://' . $channel . '/channel.xml', $this->ui, $tmp, $callback, false); - PEAR::popErrorHandling(); - if (PEAR::isError($a)) { - return false; - } - } - - list($a, $lastmodified) = $a; - if (!class_exists('PEAR_ChannelFile')) { - require_once 'PEAR/ChannelFile.php'; - } - - $b = new PEAR_ChannelFile; - if ($b->fromXmlFile($a)) { - unlink($a); - if ($this->config->get('auto_discover')) { - $this->_registry->addChannel($b, $lastmodified); - $alias = $b->getName(); - if ($b->getName() == $this->_registry->channelName($b->getAlias())) { - $alias = $b->getAlias(); - } - - $this->log(1, 'Auto-discovered channel "' . $channel . - '", alias "' . $alias . '", adding to registry'); - } - - return true; - } - - unlink($a); - return false; - } - - /** - * For simpler unit-testing - * @param PEAR_Downloader - * @return PEAR_Downloader_Package - */ - function &newDownloaderPackage(&$t) - { - if (!class_exists('PEAR_Downloader_Package')) { - require_once 'PEAR/Downloader/Package.php'; - } - $a = &new PEAR_Downloader_Package($t); - return $a; - } - - /** - * For simpler unit-testing - * @param PEAR_Config - * @param array - * @param array - * @param int - */ - function &getDependency2Object(&$c, $i, $p, $s) - { - if (!class_exists('PEAR_Dependency2')) { - require_once 'PEAR/Dependency2.php'; - } - $z = &new PEAR_Dependency2($c, $i, $p, $s); - return $z; - } - - function &download($params) - { - if (!count($params)) { - $a = array(); - return $a; - } - - if (!isset($this->_registry)) { - $this->_registry = &$this->config->getRegistry(); - } - - $channelschecked = array(); - // convert all parameters into PEAR_Downloader_Package objects - foreach ($params as $i => $param) { - $params[$i] = &$this->newDownloaderPackage($this); - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $err = $params[$i]->initialize($param); - PEAR::staticPopErrorHandling(); - if (!$err) { - // skip parameters that were missed by preferred_state - continue; - } - - if (PEAR::isError($err)) { - if (!isset($this->_options['soft']) && $err->getMessage() !== '') { - $this->log(0, $err->getMessage()); - } - - $params[$i] = false; - if (is_object($param)) { - $param = $param->getChannel() . '/' . $param->getPackage(); - } - - if (!isset($this->_options['soft'])) { - $this->log(2, 'Package "' . $param . '" is not valid'); - } - - // Message logged above in a specific verbose mode, passing null to not show up on CLI - $this->pushError(null, PEAR_INSTALLER_SKIPPED); - } else { - do { - if ($params[$i] && $params[$i]->getType() == 'local') { - // bug #7090 skip channel.xml check for local packages - break; - } - - if ($params[$i] && !isset($channelschecked[$params[$i]->getChannel()]) && - !isset($this->_options['offline']) - ) { - $channelschecked[$params[$i]->getChannel()] = true; - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - if (!class_exists('System')) { - require_once 'System.php'; - } - - $curchannel = &$this->_registry->getChannel($params[$i]->getChannel()); - if (PEAR::isError($curchannel)) { - PEAR::staticPopErrorHandling(); - return $this->raiseError($curchannel); - } - - if (PEAR::isError($dir = $this->getDownloadDir())) { - PEAR::staticPopErrorHandling(); - break; - } - - $mirror = $this->config->get('preferred_mirror', null, $params[$i]->getChannel()); - $url = 'http://' . $mirror . '/channel.xml'; - $a = $this->downloadHttp($url, $this->ui, $dir, null, $curchannel->lastModified()); - - PEAR::staticPopErrorHandling(); - if (PEAR::isError($a) || !$a) { - // Attempt fallback to https automatically - PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - $a = $this->downloadHttp('https://' . $mirror . - '/channel.xml', $this->ui, $dir, null, $curchannel->lastModified()); - - PEAR::staticPopErrorHandling(); - if (PEAR::isError($a) || !$a) { - break; - } - } - $this->log(0, 'WARNING: channel "' . $params[$i]->getChannel() . '" has ' . - 'updated its protocols, use "' . PEAR_RUNTYPE . ' channel-update ' . $params[$i]->getChannel() . - '" to update'); - } - } while (false); - - if ($params[$i] && !isset($this->_options['downloadonly'])) { - if (isset($this->_options['packagingroot'])) { - $checkdir = $this->_prependPath( - $this->config->get('php_dir', null, $params[$i]->getChannel()), - $this->_options['packagingroot']); - } else { - $checkdir = $this->config->get('php_dir', - null, $params[$i]->getChannel()); - } - - while ($checkdir && $checkdir != '/' && !file_exists($checkdir)) { - $checkdir = dirname($checkdir); - } - - if ($checkdir == '.') { - $checkdir = '/'; - } - - if (!is_writeable($checkdir)) { - return PEAR::raiseError('Cannot install, php_dir for channel "' . - $params[$i]->getChannel() . '" is not writeable by the current user'); - } - } - } - } - - unset($channelschecked); - PEAR_Downloader_Package::removeDuplicates($params); - if (!count($params)) { - $a = array(); - return $a; - } - - if (!isset($this->_options['nodeps']) && !isset($this->_options['offline'])) { - $reverify = true; - while ($reverify) { - $reverify = false; - foreach ($params as $i => $param) { - //PHP Bug 40768 / PEAR Bug #10944 - //Nested foreaches fail in PHP 5.2.1 - key($params); - $ret = $params[$i]->detectDependencies($params); - if (PEAR::isError($ret)) { - $reverify = true; - $params[$i] = false; - PEAR_Downloader_Package::removeDuplicates($params); - if (!isset($this->_options['soft'])) { - $this->log(0, $ret->getMessage()); - } - continue 2; - } - } - } - } - - if (isset($this->_options['offline'])) { - $this->log(3, 'Skipping dependency download check, --offline specified'); - } - - if (!count($params)) { - $a = array(); - return $a; - } - - while (PEAR_Downloader_Package::mergeDependencies($params)); - PEAR_Downloader_Package::removeDuplicates($params, true); - $errorparams = array(); - if (PEAR_Downloader_Package::detectStupidDuplicates($params, $errorparams)) { - if (count($errorparams)) { - foreach ($errorparams as $param) { - $name = $this->_registry->parsedPackageNameToString($param->getParsedPackage()); - $this->pushError('Duplicate package ' . $name . ' found', PEAR_INSTALLER_FAILED); - } - $a = array(); - return $a; - } - } - - PEAR_Downloader_Package::removeInstalled($params); - if (!count($params)) { - $this->pushError('No valid packages found', PEAR_INSTALLER_FAILED); - $a = array(); - return $a; - } - - PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - $err = $this->analyzeDependencies($params); - PEAR::popErrorHandling(); - if (!count($params)) { - $this->pushError('No valid packages found', PEAR_INSTALLER_FAILED); - $a = array(); - return $a; - } - - $ret = array(); - $newparams = array(); - if (isset($this->_options['pretend'])) { - return $params; - } - - $somefailed = false; - foreach ($params as $i => $package) { - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $pf = &$params[$i]->download(); - PEAR::staticPopErrorHandling(); - if (PEAR::isError($pf)) { - if (!isset($this->_options['soft'])) { - $this->log(1, $pf->getMessage()); - $this->log(0, 'Error: cannot download "' . - $this->_registry->parsedPackageNameToString($package->getParsedPackage(), - true) . - '"'); - } - $somefailed = true; - continue; - } - - $newparams[] = &$params[$i]; - $ret[] = array( - 'file' => $pf->getArchiveFile(), - 'info' => &$pf, - 'pkg' => $pf->getPackage() - ); - } - - if ($somefailed) { - // remove params that did not download successfully - PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - $err = $this->analyzeDependencies($newparams, true); - PEAR::popErrorHandling(); - if (!count($newparams)) { - $this->pushError('Download failed', PEAR_INSTALLER_FAILED); - $a = array(); - return $a; - } - } - - $this->_downloadedPackages = $ret; - return $newparams; - } - - /** - * @param array all packages to be installed - */ - function analyzeDependencies(&$params, $force = false) - { - if (isset($this->_options['downloadonly'])) { - return; - } - - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $redo = true; - $reset = $hasfailed = $failed = false; - while ($redo) { - $redo = false; - foreach ($params as $i => $param) { - $deps = $param->getDeps(); - if (!$deps) { - $depchecker = &$this->getDependency2Object($this->config, $this->getOptions(), - $param->getParsedPackage(), PEAR_VALIDATE_DOWNLOADING); - $send = $param->getPackageFile(); - - $installcheck = $depchecker->validatePackage($send, $this, $params); - if (PEAR::isError($installcheck)) { - if (!isset($this->_options['soft'])) { - $this->log(0, $installcheck->getMessage()); - } - $hasfailed = true; - $params[$i] = false; - $reset = true; - $redo = true; - $failed = false; - PEAR_Downloader_Package::removeDuplicates($params); - continue 2; - } - continue; - } - - if (!$reset && $param->alreadyValidated() && !$force) { - continue; - } - - if (count($deps)) { - $depchecker = &$this->getDependency2Object($this->config, $this->getOptions(), - $param->getParsedPackage(), PEAR_VALIDATE_DOWNLOADING); - $send = $param->getPackageFile(); - if ($send === null) { - $send = $param->getDownloadURL(); - } - - $installcheck = $depchecker->validatePackage($send, $this, $params); - if (PEAR::isError($installcheck)) { - if (!isset($this->_options['soft'])) { - $this->log(0, $installcheck->getMessage()); - } - $hasfailed = true; - $params[$i] = false; - $reset = true; - $redo = true; - $failed = false; - PEAR_Downloader_Package::removeDuplicates($params); - continue 2; - } - - $failed = false; - if (isset($deps['required']) && is_array($deps['required'])) { - foreach ($deps['required'] as $type => $dep) { - // note: Dependency2 will never return a PEAR_Error if ignore-errors - // is specified, so soft is needed to turn off logging - if (!isset($dep[0])) { - if (PEAR::isError($e = $depchecker->{"validate{$type}Dependency"}($dep, - true, $params))) { - $failed = true; - if (!isset($this->_options['soft'])) { - $this->log(0, $e->getMessage()); - } - } elseif (is_array($e) && !$param->alreadyValidated()) { - if (!isset($this->_options['soft'])) { - $this->log(0, $e[0]); - } - } - } else { - foreach ($dep as $d) { - if (PEAR::isError($e = - $depchecker->{"validate{$type}Dependency"}($d, - true, $params))) { - $failed = true; - if (!isset($this->_options['soft'])) { - $this->log(0, $e->getMessage()); - } - } elseif (is_array($e) && !$param->alreadyValidated()) { - if (!isset($this->_options['soft'])) { - $this->log(0, $e[0]); - } - } - } - } - } - - if (isset($deps['optional']) && is_array($deps['optional'])) { - foreach ($deps['optional'] as $type => $dep) { - if (!isset($dep[0])) { - if (PEAR::isError($e = - $depchecker->{"validate{$type}Dependency"}($dep, - false, $params))) { - $failed = true; - if (!isset($this->_options['soft'])) { - $this->log(0, $e->getMessage()); - } - } elseif (is_array($e) && !$param->alreadyValidated()) { - if (!isset($this->_options['soft'])) { - $this->log(0, $e[0]); - } - } - } else { - foreach ($dep as $d) { - if (PEAR::isError($e = - $depchecker->{"validate{$type}Dependency"}($d, - false, $params))) { - $failed = true; - if (!isset($this->_options['soft'])) { - $this->log(0, $e->getMessage()); - } - } elseif (is_array($e) && !$param->alreadyValidated()) { - if (!isset($this->_options['soft'])) { - $this->log(0, $e[0]); - } - } - } - } - } - } - - $groupname = $param->getGroup(); - if (isset($deps['group']) && $groupname) { - if (!isset($deps['group'][0])) { - $deps['group'] = array($deps['group']); - } - - $found = false; - foreach ($deps['group'] as $group) { - if ($group['attribs']['name'] == $groupname) { - $found = true; - break; - } - } - - if ($found) { - unset($group['attribs']); - foreach ($group as $type => $dep) { - if (!isset($dep[0])) { - if (PEAR::isError($e = - $depchecker->{"validate{$type}Dependency"}($dep, - false, $params))) { - $failed = true; - if (!isset($this->_options['soft'])) { - $this->log(0, $e->getMessage()); - } - } elseif (is_array($e) && !$param->alreadyValidated()) { - if (!isset($this->_options['soft'])) { - $this->log(0, $e[0]); - } - } - } else { - foreach ($dep as $d) { - if (PEAR::isError($e = - $depchecker->{"validate{$type}Dependency"}($d, - false, $params))) { - $failed = true; - if (!isset($this->_options['soft'])) { - $this->log(0, $e->getMessage()); - } - } elseif (is_array($e) && !$param->alreadyValidated()) { - if (!isset($this->_options['soft'])) { - $this->log(0, $e[0]); - } - } - } - } - } - } - } - } else { - foreach ($deps as $dep) { - if (PEAR::isError($e = $depchecker->validateDependency1($dep, $params))) { - $failed = true; - if (!isset($this->_options['soft'])) { - $this->log(0, $e->getMessage()); - } - } elseif (is_array($e) && !$param->alreadyValidated()) { - if (!isset($this->_options['soft'])) { - $this->log(0, $e[0]); - } - } - } - } - $params[$i]->setValidated(); - } - - if ($failed) { - $hasfailed = true; - $params[$i] = false; - $reset = true; - $redo = true; - $failed = false; - PEAR_Downloader_Package::removeDuplicates($params); - continue 2; - } - } - } - - PEAR::staticPopErrorHandling(); - if ($hasfailed && (isset($this->_options['ignore-errors']) || - isset($this->_options['nodeps']))) { - // this is probably not needed, but just in case - if (!isset($this->_options['soft'])) { - $this->log(0, 'WARNING: dependencies failed'); - } - } - } - - /** - * Retrieve the directory that downloads will happen in - * @access private - * @return string - */ - function getDownloadDir() - { - if (isset($this->_downloadDir)) { - return $this->_downloadDir; - } - - $downloaddir = $this->config->get('download_dir'); - if (empty($downloaddir) || (is_dir($downloaddir) && !is_writable($downloaddir))) { - if (is_dir($downloaddir) && !is_writable($downloaddir)) { - $this->log(0, 'WARNING: configuration download directory "' . $downloaddir . - '" is not writeable. Change download_dir config variable to ' . - 'a writeable dir to avoid this warning'); - } - - if (!class_exists('System')) { - require_once 'System.php'; - } - - if (PEAR::isError($downloaddir = System::mktemp('-d'))) { - return $downloaddir; - } - $this->log(3, '+ tmp dir created at ' . $downloaddir); - } - - if (!is_writable($downloaddir)) { - if (PEAR::isError(System::mkdir(array('-p', $downloaddir))) || - !is_writable($downloaddir)) { - return PEAR::raiseError('download directory "' . $downloaddir . - '" is not writeable. Change download_dir config variable to ' . - 'a writeable dir'); - } - } - - return $this->_downloadDir = $downloaddir; - } - - function setDownloadDir($dir) - { - if (!@is_writable($dir)) { - if (PEAR::isError(System::mkdir(array('-p', $dir)))) { - return PEAR::raiseError('download directory "' . $dir . - '" is not writeable. Change download_dir config variable to ' . - 'a writeable dir'); - } - } - $this->_downloadDir = $dir; - } - - function configSet($key, $value, $layer = 'user', $channel = false) - { - $this->config->set($key, $value, $layer, $channel); - $this->_preferredState = $this->config->get('preferred_state', null, $channel); - if (!$this->_preferredState) { - // don't inadvertantly use a non-set preferred_state - $this->_preferredState = null; - } - } - - function setOptions($options) - { - $this->_options = $options; - } - - function getOptions() - { - return $this->_options; - } - - - /** - * @param array output of {@link parsePackageName()} - * @access private - */ - function _getPackageDownloadUrl($parr) - { - $curchannel = $this->config->get('default_channel'); - $this->configSet('default_channel', $parr['channel']); - // getDownloadURL returns an array. On error, it only contains information - // on the latest release as array(version, info). On success it contains - // array(version, info, download url string) - $state = isset($parr['state']) ? $parr['state'] : $this->config->get('preferred_state'); - if (!$this->_registry->channelExists($parr['channel'])) { - do { - if ($this->config->get('auto_discover') && $this->discover($parr['channel'])) { - break; - } - - $this->configSet('default_channel', $curchannel); - return PEAR::raiseError('Unknown remote channel: ' . $parr['channel']); - } while (false); - } - - $chan = &$this->_registry->getChannel($parr['channel']); - if (PEAR::isError($chan)) { - return $chan; - } - - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $version = $this->_registry->packageInfo($parr['package'], 'version', $parr['channel']); - $stability = $this->_registry->packageInfo($parr['package'], 'stability', $parr['channel']); - // package is installed - use the installed release stability level - if (!isset($parr['state']) && $stability !== null) { - $state = $stability['release']; - } - PEAR::staticPopErrorHandling(); - $base2 = false; - - $preferred_mirror = $this->config->get('preferred_mirror'); - if (!$chan->supportsREST($preferred_mirror) || - ( - !($base2 = $chan->getBaseURL('REST1.3', $preferred_mirror)) - && - !($base = $chan->getBaseURL('REST1.0', $preferred_mirror)) - ) - ) { - return $this->raiseError($parr['channel'] . ' is using a unsupported protocol - This should never happen.'); - } - - if ($base2) { - $rest = &$this->config->getREST('1.3', $this->_options); - $base = $base2; - } else { - $rest = &$this->config->getREST('1.0', $this->_options); - } - - $downloadVersion = false; - if (!isset($parr['version']) && !isset($parr['state']) && $version - && !PEAR::isError($version) - && !isset($this->_options['downloadonly']) - ) { - $downloadVersion = $version; - } - - $url = $rest->getDownloadURL($base, $parr, $state, $downloadVersion, $chan->getName()); - if (PEAR::isError($url)) { - $this->configSet('default_channel', $curchannel); - return $url; - } - - if ($parr['channel'] != $curchannel) { - $this->configSet('default_channel', $curchannel); - } - - if (!is_array($url)) { - return $url; - } - - $url['raw'] = false; // no checking is necessary for REST - if (!is_array($url['info'])) { - return PEAR::raiseError('Invalid remote dependencies retrieved from REST - ' . - 'this should never happen'); - } - - if (!isset($this->_options['force']) && - !isset($this->_options['downloadonly']) && - $version && - !PEAR::isError($version) && - !isset($parr['group']) - ) { - if (version_compare($version, $url['version'], '=')) { - return PEAR::raiseError($this->_registry->parsedPackageNameToString( - $parr, true) . ' is already installed and is the same as the ' . - 'released version ' . $url['version'], -976); - } - - if (version_compare($version, $url['version'], '>')) { - return PEAR::raiseError($this->_registry->parsedPackageNameToString( - $parr, true) . ' is already installed and is newer than detected ' . - 'released version ' . $url['version'], -976); - } - } - - if (isset($url['info']['required']) || $url['compatible']) { - require_once 'PEAR/PackageFile/v2.php'; - $pf = new PEAR_PackageFile_v2; - $pf->setRawChannel($parr['channel']); - if ($url['compatible']) { - $pf->setRawCompatible($url['compatible']); - } - } else { - require_once 'PEAR/PackageFile/v1.php'; - $pf = new PEAR_PackageFile_v1; - } - - $pf->setRawPackage($url['package']); - $pf->setDeps($url['info']); - if ($url['compatible']) { - $pf->setCompatible($url['compatible']); - } - - $pf->setRawState($url['stability']); - $url['info'] = &$pf; - if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) { - $ext = '.tar'; - } else { - $ext = '.tgz'; - } - - if (is_array($url) && isset($url['url'])) { - $url['url'] .= $ext; - } - - return $url; - } - - /** - * @param array dependency array - * @access private - */ - function _getDepPackageDownloadUrl($dep, $parr) - { - $xsdversion = isset($dep['rel']) ? '1.0' : '2.0'; - $curchannel = $this->config->get('default_channel'); - if (isset($dep['uri'])) { - $xsdversion = '2.0'; - $chan = &$this->_registry->getChannel('__uri'); - if (PEAR::isError($chan)) { - return $chan; - } - - $version = $this->_registry->packageInfo($dep['name'], 'version', '__uri'); - $this->configSet('default_channel', '__uri'); - } else { - if (isset($dep['channel'])) { - $remotechannel = $dep['channel']; - } else { - $remotechannel = 'pear.php.net'; - } - - if (!$this->_registry->channelExists($remotechannel)) { - do { - if ($this->config->get('auto_discover')) { - if ($this->discover($remotechannel)) { - break; - } - } - return PEAR::raiseError('Unknown remote channel: ' . $remotechannel); - } while (false); - } - - $chan = &$this->_registry->getChannel($remotechannel); - if (PEAR::isError($chan)) { - return $chan; - } - - $version = $this->_registry->packageInfo($dep['name'], 'version', $remotechannel); - $this->configSet('default_channel', $remotechannel); - } - - $state = isset($parr['state']) ? $parr['state'] : $this->config->get('preferred_state'); - if (isset($parr['state']) && isset($parr['version'])) { - unset($parr['state']); - } - - if (isset($dep['uri'])) { - $info = &$this->newDownloaderPackage($this); - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $err = $info->initialize($dep); - PEAR::staticPopErrorHandling(); - if (!$err) { - // skip parameters that were missed by preferred_state - return PEAR::raiseError('Cannot initialize dependency'); - } - - if (PEAR::isError($err)) { - if (!isset($this->_options['soft'])) { - $this->log(0, $err->getMessage()); - } - - if (is_object($info)) { - $param = $info->getChannel() . '/' . $info->getPackage(); - } - return PEAR::raiseError('Package "' . $param . '" is not valid'); - } - return $info; - } elseif ($chan->supportsREST($this->config->get('preferred_mirror')) - && - ( - ($base2 = $chan->getBaseURL('REST1.3', $this->config->get('preferred_mirror'))) - || - ($base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) - ) - ) { - if ($base2) { - $base = $base2; - $rest = &$this->config->getREST('1.3', $this->_options); - } else { - $rest = &$this->config->getREST('1.0', $this->_options); - } - - $url = $rest->getDepDownloadURL($base, $xsdversion, $dep, $parr, - $state, $version, $chan->getName()); - if (PEAR::isError($url)) { - return $url; - } - - if ($parr['channel'] != $curchannel) { - $this->configSet('default_channel', $curchannel); - } - - if (!is_array($url)) { - return $url; - } - - $url['raw'] = false; // no checking is necessary for REST - if (!is_array($url['info'])) { - return PEAR::raiseError('Invalid remote dependencies retrieved from REST - ' . - 'this should never happen'); - } - - if (isset($url['info']['required'])) { - if (!class_exists('PEAR_PackageFile_v2')) { - require_once 'PEAR/PackageFile/v2.php'; - } - $pf = new PEAR_PackageFile_v2; - $pf->setRawChannel($remotechannel); - } else { - if (!class_exists('PEAR_PackageFile_v1')) { - require_once 'PEAR/PackageFile/v1.php'; - } - $pf = new PEAR_PackageFile_v1; - - } - $pf->setRawPackage($url['package']); - $pf->setDeps($url['info']); - if ($url['compatible']) { - $pf->setCompatible($url['compatible']); - } - - $pf->setRawState($url['stability']); - $url['info'] = &$pf; - if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) { - $ext = '.tar'; - } else { - $ext = '.tgz'; - } - - if (is_array($url) && isset($url['url'])) { - $url['url'] .= $ext; - } - - return $url; - } - - return $this->raiseError($parr['channel'] . ' is using a unsupported protocol - This should never happen.'); - } - - /** - * @deprecated in favor of _getPackageDownloadUrl - */ - function getPackageDownloadUrl($package, $version = null, $channel = false) - { - if ($version) { - $package .= "-$version"; - } - if ($this === null || $this->_registry === null) { - $package = "http://pear.php.net/get/$package"; - } else { - $chan = $this->_registry->getChannel($channel); - if (PEAR::isError($chan)) { - return ''; - } - $package = "http://" . $chan->getServer() . "/get/$package"; - } - if (!extension_loaded("zlib")) { - $package .= '?uncompress=yes'; - } - return $package; - } - - /** - * Retrieve a list of downloaded packages after a call to {@link download()}. - * - * Also resets the list of downloaded packages. - * @return array - */ - function getDownloadedPackages() - { - $ret = $this->_downloadedPackages; - $this->_downloadedPackages = array(); - $this->_toDownload = array(); - return $ret; - } - - function _downloadCallback($msg, $params = null) - { - switch ($msg) { - case 'saveas': - $this->log(1, "downloading $params ..."); - break; - case 'done': - $this->log(1, '...done: ' . number_format($params, 0, '', ',') . ' bytes'); - break; - case 'bytesread': - static $bytes; - if (empty($bytes)) { - $bytes = 0; - } - if (!($bytes % 10240)) { - $this->log(1, '.', false); - } - $bytes += $params; - break; - case 'start': - if($params[1] == -1) { - $length = "Unknown size"; - } else { - $length = number_format($params[1], 0, '', ',')." bytes"; - } - $this->log(1, "Starting to download {$params[0]} ($length)"); - break; - } - if (method_exists($this->ui, '_downloadCallback')) - $this->ui->_downloadCallback($msg, $params); - } - - function _prependPath($path, $prepend) - { - if (strlen($prepend) > 0) { - if (OS_WINDOWS && preg_match('/^[a-z]:/i', $path)) { - if (preg_match('/^[a-z]:/i', $prepend)) { - $prepend = substr($prepend, 2); - } elseif ($prepend{0} != '\\') { - $prepend = "\\$prepend"; - } - $path = substr($path, 0, 2) . $prepend . substr($path, 2); - } else { - $path = $prepend . $path; - } - } - return $path; - } - - /** - * @param string - * @param integer - */ - function pushError($errmsg, $code = -1) - { - array_push($this->_errorStack, array($errmsg, $code)); - } - - function getErrorMsgs() - { - $msgs = array(); - $errs = $this->_errorStack; - foreach ($errs as $err) { - $msgs[] = $err[0]; - } - $this->_errorStack = array(); - return $msgs; - } - - /** - * for BC - * - * @deprecated - */ - function sortPkgDeps(&$packages, $uninstall = false) - { - $uninstall ? - $this->sortPackagesForUninstall($packages) : - $this->sortPackagesForInstall($packages); - } - - /** - * Sort a list of arrays of array(downloaded packagefilename) by dependency. - * - * This uses the topological sort method from graph theory, and the - * Structures_Graph package to properly sort dependencies for installation. - * @param array an array of downloaded PEAR_Downloader_Packages - * @return array array of array(packagefilename, package.xml contents) - */ - function sortPackagesForInstall(&$packages) - { - require_once 'Structures/Graph.php'; - require_once 'Structures/Graph/Node.php'; - require_once 'Structures/Graph/Manipulator/TopologicalSorter.php'; - $depgraph = new Structures_Graph(true); - $nodes = array(); - $reg = &$this->config->getRegistry(); - foreach ($packages as $i => $package) { - $pname = $reg->parsedPackageNameToString( - array( - 'channel' => $package->getChannel(), - 'package' => strtolower($package->getPackage()), - )); - $nodes[$pname] = new Structures_Graph_Node; - $nodes[$pname]->setData($packages[$i]); - $depgraph->addNode($nodes[$pname]); - } - - $deplinks = array(); - foreach ($nodes as $package => $node) { - $pf = &$node->getData(); - $pdeps = $pf->getDeps(true); - if (!$pdeps) { - continue; - } - - if ($pf->getPackagexmlVersion() == '1.0') { - foreach ($pdeps as $dep) { - if ($dep['type'] != 'pkg' || - (isset($dep['optional']) && $dep['optional'] == 'yes')) { - continue; - } - - $dname = $reg->parsedPackageNameToString( - array( - 'channel' => 'pear.php.net', - 'package' => strtolower($dep['name']), - )); - - if (isset($nodes[$dname])) { - if (!isset($deplinks[$dname])) { - $deplinks[$dname] = array(); - } - - $deplinks[$dname][$package] = 1; - // dependency is in installed packages - continue; - } - - $dname = $reg->parsedPackageNameToString( - array( - 'channel' => 'pecl.php.net', - 'package' => strtolower($dep['name']), - )); - - if (isset($nodes[$dname])) { - if (!isset($deplinks[$dname])) { - $deplinks[$dname] = array(); - } - - $deplinks[$dname][$package] = 1; - // dependency is in installed packages - continue; - } - } - } else { - // the only ordering we care about is: - // 1) subpackages must be installed before packages that depend on them - // 2) required deps must be installed before packages that depend on them - if (isset($pdeps['required']['subpackage'])) { - $t = $pdeps['required']['subpackage']; - if (!isset($t[0])) { - $t = array($t); - } - - $this->_setupGraph($t, $reg, $deplinks, $nodes, $package); - } - - if (isset($pdeps['group'])) { - if (!isset($pdeps['group'][0])) { - $pdeps['group'] = array($pdeps['group']); - } - - foreach ($pdeps['group'] as $group) { - if (isset($group['subpackage'])) { - $t = $group['subpackage']; - if (!isset($t[0])) { - $t = array($t); - } - - $this->_setupGraph($t, $reg, $deplinks, $nodes, $package); - } - } - } - - if (isset($pdeps['optional']['subpackage'])) { - $t = $pdeps['optional']['subpackage']; - if (!isset($t[0])) { - $t = array($t); - } - - $this->_setupGraph($t, $reg, $deplinks, $nodes, $package); - } - - if (isset($pdeps['required']['package'])) { - $t = $pdeps['required']['package']; - if (!isset($t[0])) { - $t = array($t); - } - - $this->_setupGraph($t, $reg, $deplinks, $nodes, $package); - } - - if (isset($pdeps['group'])) { - if (!isset($pdeps['group'][0])) { - $pdeps['group'] = array($pdeps['group']); - } - - foreach ($pdeps['group'] as $group) { - if (isset($group['package'])) { - $t = $group['package']; - if (!isset($t[0])) { - $t = array($t); - } - - $this->_setupGraph($t, $reg, $deplinks, $nodes, $package); - } - } - } - } - } - - $this->_detectDepCycle($deplinks); - foreach ($deplinks as $dependent => $parents) { - foreach ($parents as $parent => $unused) { - $nodes[$dependent]->connectTo($nodes[$parent]); - } - } - - $installOrder = Structures_Graph_Manipulator_TopologicalSorter::sort($depgraph); - $ret = array(); - for ($i = 0, $count = count($installOrder); $i < $count; $i++) { - foreach ($installOrder[$i] as $index => $sortedpackage) { - $data = &$installOrder[$i][$index]->getData(); - $ret[] = &$nodes[$reg->parsedPackageNameToString( - array( - 'channel' => $data->getChannel(), - 'package' => strtolower($data->getPackage()), - ))]->getData(); - } - } - - $packages = $ret; - return; - } - - /** - * Detect recursive links between dependencies and break the cycles - * - * @param array - * @access private - */ - function _detectDepCycle(&$deplinks) - { - do { - $keepgoing = false; - foreach ($deplinks as $dep => $parents) { - foreach ($parents as $parent => $unused) { - // reset the parent cycle detector - $this->_testCycle(null, null, null); - if ($this->_testCycle($dep, $deplinks, $parent)) { - $keepgoing = true; - unset($deplinks[$dep][$parent]); - if (count($deplinks[$dep]) == 0) { - unset($deplinks[$dep]); - } - - continue 3; - } - } - } - } while ($keepgoing); - } - - function _testCycle($test, $deplinks, $dep) - { - static $visited = array(); - if ($test === null) { - $visited = array(); - return; - } - - // this happens when a parent has a dep cycle on another dependency - // but the child is not part of the cycle - if (isset($visited[$dep])) { - return false; - } - - $visited[$dep] = 1; - if ($test == $dep) { - return true; - } - - if (isset($deplinks[$dep])) { - if (in_array($test, array_keys($deplinks[$dep]), true)) { - return true; - } - - foreach ($deplinks[$dep] as $parent => $unused) { - if ($this->_testCycle($test, $deplinks, $parent)) { - return true; - } - } - } - - return false; - } - - /** - * Set up the dependency for installation parsing - * - * @param array $t dependency information - * @param PEAR_Registry $reg - * @param array $deplinks list of dependency links already established - * @param array $nodes all existing package nodes - * @param string $package parent package name - * @access private - */ - function _setupGraph($t, $reg, &$deplinks, &$nodes, $package) - { - foreach ($t as $dep) { - $depchannel = !isset($dep['channel']) ? '__uri': $dep['channel']; - $dname = $reg->parsedPackageNameToString( - array( - 'channel' => $depchannel, - 'package' => strtolower($dep['name']), - )); - - if (isset($nodes[$dname])) { - if (!isset($deplinks[$dname])) { - $deplinks[$dname] = array(); - } - $deplinks[$dname][$package] = 1; - } - } - } - - function _dependsOn($a, $b) - { - return $this->_checkDepTree(strtolower($a->getChannel()), strtolower($a->getPackage()), $b); - } - - function _checkDepTree($channel, $package, $b, $checked = array()) - { - $checked[$channel][$package] = true; - if (!isset($this->_depTree[$channel][$package])) { - return false; - } - - if (isset($this->_depTree[$channel][$package][strtolower($b->getChannel())] - [strtolower($b->getPackage())])) { - return true; - } - - foreach ($this->_depTree[$channel][$package] as $ch => $packages) { - foreach ($packages as $pa => $true) { - if ($this->_checkDepTree($ch, $pa, $b, $checked)) { - return true; - } - } - } - - return false; - } - - function _sortInstall($a, $b) - { - if (!$a->getDeps() && !$b->getDeps()) { - return 0; // neither package has dependencies, order is insignificant - } - if ($a->getDeps() && !$b->getDeps()) { - return 1; // $a must be installed after $b because $a has dependencies - } - if (!$a->getDeps() && $b->getDeps()) { - return -1; // $b must be installed after $a because $b has dependencies - } - // both packages have dependencies - if ($this->_dependsOn($a, $b)) { - return 1; - } - if ($this->_dependsOn($b, $a)) { - return -1; - } - return 0; - } - - /** - * Download a file through HTTP. Considers suggested file name in - * Content-disposition: header and can run a callback function for - * different events. The callback will be called with two - * parameters: the callback type, and parameters. The implemented - * callback types are: - * - * 'setup' called at the very beginning, parameter is a UI object - * that should be used for all output - * 'message' the parameter is a string with an informational message - * 'saveas' may be used to save with a different file name, the - * parameter is the filename that is about to be used. - * If a 'saveas' callback returns a non-empty string, - * that file name will be used as the filename instead. - * Note that $save_dir will not be affected by this, only - * the basename of the file. - * 'start' download is starting, parameter is number of bytes - * that are expected, or -1 if unknown - * 'bytesread' parameter is the number of bytes read so far - * 'done' download is complete, parameter is the total number - * of bytes read - * 'connfailed' if the TCP/SSL connection fails, this callback is called - * with array(host,port,errno,errmsg) - * 'writefailed' if writing to disk fails, this callback is called - * with array(destfile,errmsg) - * - * If an HTTP proxy has been configured (http_proxy PEAR_Config - * setting), the proxy will be used. - * - * @param string $url the URL to download - * @param object $ui PEAR_Frontend_* instance - * @param object $config PEAR_Config instance - * @param string $save_dir directory to save file in - * @param mixed $callback function/method to call for status - * updates - * @param false|string|array $lastmodified header values to check against for caching - * use false to return the header values from this download - * @param false|array $accept Accept headers to send - * @param false|string $channel Channel to use for retrieving authentication - * @return string|array Returns the full path of the downloaded file or a PEAR - * error on failure. If the error is caused by - * socket-related errors, the error object will - * have the fsockopen error code available through - * getCode(). If caching is requested, then return the header - * values. - * - * @access public - */ - function downloadHttp($url, &$ui, $save_dir = '.', $callback = null, $lastmodified = null, - $accept = false, $channel = false) - { - static $redirect = 0; - // always reset , so we are clean case of error - $wasredirect = $redirect; - $redirect = 0; - if ($callback) { - call_user_func($callback, 'setup', array(&$ui)); - } - - $info = parse_url($url); - if (!isset($info['scheme']) || !in_array($info['scheme'], array('http', 'https'))) { - return PEAR::raiseError('Cannot download non-http URL "' . $url . '"'); - } - - if (!isset($info['host'])) { - return PEAR::raiseError('Cannot download from non-URL "' . $url . '"'); - } - - $host = isset($info['host']) ? $info['host'] : null; - $port = isset($info['port']) ? $info['port'] : null; - $path = isset($info['path']) ? $info['path'] : null; - - if (isset($this)) { - $config = &$this->config; - } else { - $config = &PEAR_Config::singleton(); - } - - $proxy_host = $proxy_port = $proxy_user = $proxy_pass = ''; - if ($config->get('http_proxy') && - $proxy = parse_url($config->get('http_proxy'))) { - $proxy_host = isset($proxy['host']) ? $proxy['host'] : null; - if (isset($proxy['scheme']) && $proxy['scheme'] == 'https') { - $proxy_host = 'ssl://' . $proxy_host; - } - $proxy_port = isset($proxy['port']) ? $proxy['port'] : 8080; - $proxy_user = isset($proxy['user']) ? urldecode($proxy['user']) : null; - $proxy_pass = isset($proxy['pass']) ? urldecode($proxy['pass']) : null; - - if ($callback) { - call_user_func($callback, 'message', "Using HTTP proxy $host:$port"); - } - } - - if (empty($port)) { - $port = (isset($info['scheme']) && $info['scheme'] == 'https') ? 443 : 80; - } - - $scheme = (isset($info['scheme']) && $info['scheme'] == 'https') ? 'https' : 'http'; - - if ($proxy_host != '') { - $fp = @fsockopen($proxy_host, $proxy_port, $errno, $errstr); - if (!$fp) { - if ($callback) { - call_user_func($callback, 'connfailed', array($proxy_host, $proxy_port, - $errno, $errstr)); - } - return PEAR::raiseError("Connection to `$proxy_host:$proxy_port' failed: $errstr", $errno); - } - - if ($lastmodified === false || $lastmodified) { - $request = "GET $url HTTP/1.1\r\n"; - $request .= "Host: $host\r\n"; - } else { - $request = "GET $url HTTP/1.0\r\n"; - $request .= "Host: $host\r\n"; - } - } else { - $network_host = $host; - if (isset($info['scheme']) && $info['scheme'] == 'https') { - $network_host = 'ssl://' . $host; - } - - $fp = @fsockopen($network_host, $port, $errno, $errstr); - if (!$fp) { - if ($callback) { - call_user_func($callback, 'connfailed', array($host, $port, - $errno, $errstr)); - } - return PEAR::raiseError("Connection to `$host:$port' failed: $errstr", $errno); - } - - if ($lastmodified === false || $lastmodified) { - $request = "GET $path HTTP/1.1\r\n"; - $request .= "Host: $host\r\n"; - } else { - $request = "GET $path HTTP/1.0\r\n"; - $request .= "Host: $host\r\n"; - } - } - - $ifmodifiedsince = ''; - if (is_array($lastmodified)) { - if (isset($lastmodified['Last-Modified'])) { - $ifmodifiedsince = 'If-Modified-Since: ' . $lastmodified['Last-Modified'] . "\r\n"; - } - - if (isset($lastmodified['ETag'])) { - $ifmodifiedsince .= "If-None-Match: $lastmodified[ETag]\r\n"; - } - } else { - $ifmodifiedsince = ($lastmodified ? "If-Modified-Since: $lastmodified\r\n" : ''); - } - - $request .= $ifmodifiedsince . - "User-Agent: PEAR/1.9.4/PHP/" . PHP_VERSION . "\r\n"; - - if (isset($this)) { // only pass in authentication for non-static calls - $username = $config->get('username', null, $channel); - $password = $config->get('password', null, $channel); - if ($username && $password) { - $tmp = base64_encode("$username:$password"); - $request .= "Authorization: Basic $tmp\r\n"; - } - } - - if ($proxy_host != '' && $proxy_user != '') { - $request .= 'Proxy-Authorization: Basic ' . - base64_encode($proxy_user . ':' . $proxy_pass) . "\r\n"; - } - - if ($accept) { - $request .= 'Accept: ' . implode(', ', $accept) . "\r\n"; - } - - $request .= "Connection: close\r\n"; - $request .= "\r\n"; - fwrite($fp, $request); - $headers = array(); - $reply = 0; - while (trim($line = fgets($fp, 1024))) { - if (preg_match('/^([^:]+):\s+(.*)\s*\\z/', $line, $matches)) { - $headers[strtolower($matches[1])] = trim($matches[2]); - } elseif (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) { - $reply = (int)$matches[1]; - if ($reply == 304 && ($lastmodified || ($lastmodified === false))) { - return false; - } - - if (!in_array($reply, array(200, 301, 302, 303, 305, 307))) { - return PEAR::raiseError("File $scheme://$host:$port$path not valid (received: $line)"); - } - } - } - - if ($reply != 200) { - if (!isset($headers['location'])) { - return PEAR::raiseError("File $scheme://$host:$port$path not valid (redirected but no location)"); - } - - if ($wasredirect > 4) { - return PEAR::raiseError("File $scheme://$host:$port$path not valid (redirection looped more than 5 times)"); - } - - $redirect = $wasredirect + 1; - return $this->downloadHttp($headers['location'], - $ui, $save_dir, $callback, $lastmodified, $accept); - } - - if (isset($headers['content-disposition']) && - preg_match('/\sfilename=\"([^;]*\S)\"\s*(;|\\z)/', $headers['content-disposition'], $matches)) { - $save_as = basename($matches[1]); - } else { - $save_as = basename($url); - } - - if ($callback) { - $tmp = call_user_func($callback, 'saveas', $save_as); - if ($tmp) { - $save_as = $tmp; - } - } - - $dest_file = $save_dir . DIRECTORY_SEPARATOR . $save_as; - if (is_link($dest_file)) { - return PEAR::raiseError('SECURITY ERROR: Will not write to ' . $dest_file . ' as it is symlinked to ' . readlink($dest_file) . ' - Possible symlink attack'); - } - - if (!$wp = @fopen($dest_file, 'wb')) { - fclose($fp); - if ($callback) { - call_user_func($callback, 'writefailed', array($dest_file, $php_errormsg)); - } - return PEAR::raiseError("could not open $dest_file for writing"); - } - - $length = isset($headers['content-length']) ? $headers['content-length'] : -1; - - $bytes = 0; - if ($callback) { - call_user_func($callback, 'start', array(basename($dest_file), $length)); - } - - while ($data = fread($fp, 1024)) { - $bytes += strlen($data); - if ($callback) { - call_user_func($callback, 'bytesread', $bytes); - } - if (!@fwrite($wp, $data)) { - fclose($fp); - if ($callback) { - call_user_func($callback, 'writefailed', array($dest_file, $php_errormsg)); - } - return PEAR::raiseError("$dest_file: write failed ($php_errormsg)"); - } - } - - fclose($fp); - fclose($wp); - if ($callback) { - call_user_func($callback, 'done', $bytes); - } - - if ($lastmodified === false || $lastmodified) { - if (isset($headers['etag'])) { - $lastmodified = array('ETag' => $headers['etag']); - } - - if (isset($headers['last-modified'])) { - if (is_array($lastmodified)) { - $lastmodified['Last-Modified'] = $headers['last-modified']; - } else { - $lastmodified = $headers['last-modified']; - } - } - return array($dest_file, $lastmodified, $headers); - } - return $dest_file; - } -} \ No newline at end of file diff --git a/pear/PEAR/Downloader/Package.php b/pear/PEAR/Downloader/Package.php deleted file mode 100644 index 03a53ee..0000000 --- a/pear/PEAR/Downloader/Package.php +++ /dev/null @@ -1,1988 +0,0 @@ - - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a1 - */ - -/** - * Error code when parameter initialization fails because no releases - * exist within preferred_state, but releases do exist - */ -define('PEAR_DOWNLOADER_PACKAGE_STATE', -1003); -/** - * Error code when parameter initialization fails because no releases - * exist that will work with the existing PHP version - */ -define('PEAR_DOWNLOADER_PACKAGE_PHPVERSION', -1004); - -/** - * Coordinates download parameters and manages their dependencies - * prior to downloading them. - * - * Input can come from three sources: - * - * - local files (archives or package.xml) - * - remote files (downloadable urls) - * - abstract package names - * - * The first two elements are handled cleanly by PEAR_PackageFile, but the third requires - * accessing pearweb's xml-rpc interface to determine necessary dependencies, and the - * format returned of dependencies is slightly different from that used in package.xml. - * - * This class hides the differences between these elements, and makes automatic - * dependency resolution a piece of cake. It also manages conflicts when - * two classes depend on incompatible dependencies, or differing versions of the same - * package dependency. In addition, download will not be attempted if the php version is - * not supported, PEAR installer version is not supported, or non-PECL extensions are not - * installed. - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @PEAR-VER@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a1 - */ -class PEAR_Downloader_Package -{ - /** - * @var PEAR_Downloader - */ - var $_downloader; - /** - * @var PEAR_Config - */ - var $_config; - /** - * @var PEAR_Registry - */ - var $_registry; - /** - * Used to implement packagingroot properly - * @var PEAR_Registry - */ - var $_installRegistry; - /** - * @var PEAR_PackageFile_v1|PEAR_PackageFile|v2 - */ - var $_packagefile; - /** - * @var array - */ - var $_parsedname; - /** - * @var array - */ - var $_downloadURL; - /** - * @var array - */ - var $_downloadDeps = array(); - /** - * @var boolean - */ - var $_valid = false; - /** - * @var boolean - */ - var $_analyzed = false; - /** - * if this or a parent package was invoked with Package-state, this is set to the - * state variable. - * - * This allows temporary reassignment of preferred_state for a parent package and all of - * its dependencies. - * @var string|false - */ - var $_explicitState = false; - /** - * If this package is invoked with Package#group, this variable will be true - */ - var $_explicitGroup = false; - /** - * Package type local|url - * @var string - */ - var $_type; - /** - * Contents of package.xml, if downloaded from a remote channel - * @var string|false - * @access private - */ - var $_rawpackagefile; - /** - * @var boolean - * @access private - */ - var $_validated = false; - - /** - * @param PEAR_Downloader - */ - function PEAR_Downloader_Package(&$downloader) - { - $this->_downloader = &$downloader; - $this->_config = &$this->_downloader->config; - $this->_registry = &$this->_config->getRegistry(); - $options = $downloader->getOptions(); - if (isset($options['packagingroot'])) { - $this->_config->setInstallRoot($options['packagingroot']); - $this->_installRegistry = &$this->_config->getRegistry(); - $this->_config->setInstallRoot(false); - } else { - $this->_installRegistry = &$this->_registry; - } - $this->_valid = $this->_analyzed = false; - } - - /** - * Parse the input and determine whether this is a local file, a remote uri, or an - * abstract package name. - * - * This is the heart of the PEAR_Downloader_Package(), and is used in - * {@link PEAR_Downloader::download()} - * @param string - * @return bool|PEAR_Error - */ - function initialize($param) - { - $origErr = $this->_fromFile($param); - if ($this->_valid) { - return true; - } - - $options = $this->_downloader->getOptions(); - if (isset($options['offline'])) { - if (PEAR::isError($origErr) && !isset($options['soft'])) { - foreach ($origErr->getUserInfo() as $userInfo) { - if (isset($userInfo['message'])) { - $this->_downloader->log(0, $userInfo['message']); - } - } - - $this->_downloader->log(0, $origErr->getMessage()); - } - - return PEAR::raiseError('Cannot download non-local package "' . $param . '"'); - } - - $err = $this->_fromUrl($param); - if (PEAR::isError($err) || !$this->_valid) { - if ($this->_type == 'url') { - if (PEAR::isError($err) && !isset($options['soft'])) { - $this->_downloader->log(0, $err->getMessage()); - } - - return PEAR::raiseError("Invalid or missing remote package file"); - } - - $err = $this->_fromString($param); - if (PEAR::isError($err) || !$this->_valid) { - if (PEAR::isError($err) && $err->getCode() == PEAR_DOWNLOADER_PACKAGE_STATE) { - return false; // instruct the downloader to silently skip - } - - if (isset($this->_type) && $this->_type == 'local' && PEAR::isError($origErr)) { - if (is_array($origErr->getUserInfo())) { - foreach ($origErr->getUserInfo() as $err) { - if (is_array($err)) { - $err = $err['message']; - } - - if (!isset($options['soft'])) { - $this->_downloader->log(0, $err); - } - } - } - - if (!isset($options['soft'])) { - $this->_downloader->log(0, $origErr->getMessage()); - } - - if (is_array($param)) { - $param = $this->_registry->parsedPackageNameToString($param, true); - } - - if (!isset($options['soft'])) { - $this->_downloader->log(2, "Cannot initialize '$param', invalid or missing package file"); - } - - // Passing no message back - already logged above - return PEAR::raiseError(); - } - - if (PEAR::isError($err) && !isset($options['soft'])) { - $this->_downloader->log(0, $err->getMessage()); - } - - if (is_array($param)) { - $param = $this->_registry->parsedPackageNameToString($param, true); - } - - if (!isset($options['soft'])) { - $this->_downloader->log(2, "Cannot initialize '$param', invalid or missing package file"); - } - - // Passing no message back - already logged above - return PEAR::raiseError(); - } - } - - return true; - } - - /** - * Retrieve any non-local packages - * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|PEAR_Error - */ - function &download() - { - if (isset($this->_packagefile)) { - return $this->_packagefile; - } - - if (isset($this->_downloadURL['url'])) { - $this->_isvalid = false; - $info = $this->getParsedPackage(); - foreach ($info as $i => $p) { - $info[$i] = strtolower($p); - } - - $err = $this->_fromUrl($this->_downloadURL['url'], - $this->_registry->parsedPackageNameToString($this->_parsedname, true)); - $newinfo = $this->getParsedPackage(); - foreach ($newinfo as $i => $p) { - $newinfo[$i] = strtolower($p); - } - - if ($info != $newinfo) { - do { - if ($info['channel'] == 'pecl.php.net' && $newinfo['channel'] == 'pear.php.net') { - $info['channel'] = 'pear.php.net'; - if ($info == $newinfo) { - // skip the channel check if a pecl package says it's a PEAR package - break; - } - } - if ($info['channel'] == 'pear.php.net' && $newinfo['channel'] == 'pecl.php.net') { - $info['channel'] = 'pecl.php.net'; - if ($info == $newinfo) { - // skip the channel check if a pecl package says it's a PEAR package - break; - } - } - - return PEAR::raiseError('CRITICAL ERROR: We are ' . - $this->_registry->parsedPackageNameToString($info) . ', but the file ' . - 'downloaded claims to be ' . - $this->_registry->parsedPackageNameToString($this->getParsedPackage())); - } while (false); - } - - if (PEAR::isError($err) || !$this->_valid) { - return $err; - } - } - - $this->_type = 'local'; - return $this->_packagefile; - } - - function &getPackageFile() - { - return $this->_packagefile; - } - - function &getDownloader() - { - return $this->_downloader; - } - - function getType() - { - return $this->_type; - } - - /** - * Like {@link initialize()}, but operates on a dependency - */ - function fromDepURL($dep) - { - $this->_downloadURL = $dep; - if (isset($dep['uri'])) { - $options = $this->_downloader->getOptions(); - if (!extension_loaded("zlib") || isset($options['nocompress'])) { - $ext = '.tar'; - } else { - $ext = '.tgz'; - } - - PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - $err = $this->_fromUrl($dep['uri'] . $ext); - PEAR::popErrorHandling(); - if (PEAR::isError($err)) { - if (!isset($options['soft'])) { - $this->_downloader->log(0, $err->getMessage()); - } - - return PEAR::raiseError('Invalid uri dependency "' . $dep['uri'] . $ext . '", ' . - 'cannot download'); - } - } else { - $this->_parsedname = - array( - 'package' => $dep['info']->getPackage(), - 'channel' => $dep['info']->getChannel(), - 'version' => $dep['version'] - ); - if (!isset($dep['nodefault'])) { - $this->_parsedname['group'] = 'default'; // download the default dependency group - $this->_explicitGroup = false; - } - - $this->_rawpackagefile = $dep['raw']; - } - } - - function detectDependencies($params) - { - $options = $this->_downloader->getOptions(); - if (isset($options['downloadonly'])) { - return; - } - - if (isset($options['offline'])) { - $this->_downloader->log(3, 'Skipping dependency download check, --offline specified'); - return; - } - - $pname = $this->getParsedPackage(); - if (!$pname) { - return; - } - - $deps = $this->getDeps(); - if (!$deps) { - return; - } - - if (isset($deps['required'])) { // package.xml 2.0 - return $this->_detect2($deps, $pname, $options, $params); - } - - return $this->_detect1($deps, $pname, $options, $params); - } - - function setValidated() - { - $this->_validated = true; - } - - function alreadyValidated() - { - return $this->_validated; - } - - /** - * Remove packages to be downloaded that are already installed - * @param array of PEAR_Downloader_Package objects - * @static - */ - function removeInstalled(&$params) - { - if (!isset($params[0])) { - return; - } - - $options = $params[0]->_downloader->getOptions(); - if (!isset($options['downloadonly'])) { - foreach ($params as $i => $param) { - $package = $param->getPackage(); - $channel = $param->getChannel(); - // remove self if already installed with this version - // this does not need any pecl magic - we only remove exact matches - if ($param->_installRegistry->packageExists($package, $channel)) { - $packageVersion = $param->_installRegistry->packageInfo($package, 'version', $channel); - if (version_compare($packageVersion, $param->getVersion(), '==')) { - if (!isset($options['force'])) { - $info = $param->getParsedPackage(); - unset($info['version']); - unset($info['state']); - if (!isset($options['soft'])) { - $param->_downloader->log(1, 'Skipping package "' . - $param->getShortName() . - '", already installed as version ' . $packageVersion); - } - $params[$i] = false; - } - } elseif (!isset($options['force']) && !isset($options['upgrade']) && - !isset($options['soft'])) { - $info = $param->getParsedPackage(); - $param->_downloader->log(1, 'Skipping package "' . - $param->getShortName() . - '", already installed as version ' . $packageVersion); - $params[$i] = false; - } - } - } - } - - PEAR_Downloader_Package::removeDuplicates($params); - } - - function _detect2($deps, $pname, $options, $params) - { - $this->_downloadDeps = array(); - $groupnotfound = false; - foreach (array('package', 'subpackage') as $packagetype) { - // get required dependency group - if (isset($deps['required'][$packagetype])) { - if (isset($deps['required'][$packagetype][0])) { - foreach ($deps['required'][$packagetype] as $dep) { - if (isset($dep['conflicts'])) { - // skip any package that this package conflicts with - continue; - } - $ret = $this->_detect2Dep($dep, $pname, 'required', $params); - if (is_array($ret)) { - $this->_downloadDeps[] = $ret; - } elseif (PEAR::isError($ret) && !isset($options['soft'])) { - $this->_downloader->log(0, $ret->getMessage()); - } - } - } else { - $dep = $deps['required'][$packagetype]; - if (!isset($dep['conflicts'])) { - // skip any package that this package conflicts with - $ret = $this->_detect2Dep($dep, $pname, 'required', $params); - if (is_array($ret)) { - $this->_downloadDeps[] = $ret; - } elseif (PEAR::isError($ret) && !isset($options['soft'])) { - $this->_downloader->log(0, $ret->getMessage()); - } - } - } - } - - // get optional dependency group, if any - if (isset($deps['optional'][$packagetype])) { - $skipnames = array(); - if (!isset($deps['optional'][$packagetype][0])) { - $deps['optional'][$packagetype] = array($deps['optional'][$packagetype]); - } - - foreach ($deps['optional'][$packagetype] as $dep) { - $skip = false; - if (!isset($options['alldeps'])) { - $dep['package'] = $dep['name']; - if (!isset($options['soft'])) { - $this->_downloader->log(3, 'Notice: package "' . - $this->_registry->parsedPackageNameToString($this->getParsedPackage(), - true) . '" optional dependency "' . - $this->_registry->parsedPackageNameToString(array('package' => - $dep['name'], 'channel' => 'pear.php.net'), true) . - '" will not be automatically downloaded'); - } - $skipnames[] = $this->_registry->parsedPackageNameToString($dep, true); - $skip = true; - unset($dep['package']); - } - - $ret = $this->_detect2Dep($dep, $pname, 'optional', $params); - if (PEAR::isError($ret) && !isset($options['soft'])) { - $this->_downloader->log(0, $ret->getMessage()); - } - - if (!$ret) { - $dep['package'] = $dep['name']; - $skip = count($skipnames) ? - $skipnames[count($skipnames) - 1] : ''; - if ($skip == - $this->_registry->parsedPackageNameToString($dep, true)) { - array_pop($skipnames); - } - } - - if (!$skip && is_array($ret)) { - $this->_downloadDeps[] = $ret; - } - } - - if (count($skipnames)) { - if (!isset($options['soft'])) { - $this->_downloader->log(1, 'Did not download optional dependencies: ' . - implode(', ', $skipnames) . - ', use --alldeps to download automatically'); - } - } - } - - // get requested dependency group, if any - $groupname = $this->getGroup(); - $explicit = $this->_explicitGroup; - if (!$groupname) { - if (!$this->canDefault()) { - continue; - } - - $groupname = 'default'; // try the default dependency group - } - - if ($groupnotfound) { - continue; - } - - if (isset($deps['group'])) { - if (isset($deps['group']['attribs'])) { - if (strtolower($deps['group']['attribs']['name']) == strtolower($groupname)) { - $group = $deps['group']; - } elseif ($explicit) { - if (!isset($options['soft'])) { - $this->_downloader->log(0, 'Warning: package "' . - $this->_registry->parsedPackageNameToString($pname, true) . - '" has no dependency ' . 'group named "' . $groupname . '"'); - } - - $groupnotfound = true; - continue; - } - } else { - $found = false; - foreach ($deps['group'] as $group) { - if (strtolower($group['attribs']['name']) == strtolower($groupname)) { - $found = true; - break; - } - } - - if (!$found) { - if ($explicit) { - if (!isset($options['soft'])) { - $this->_downloader->log(0, 'Warning: package "' . - $this->_registry->parsedPackageNameToString($pname, true) . - '" has no dependency ' . 'group named "' . $groupname . '"'); - } - } - - $groupnotfound = true; - continue; - } - } - } - - if (isset($group) && isset($group[$packagetype])) { - if (isset($group[$packagetype][0])) { - foreach ($group[$packagetype] as $dep) { - $ret = $this->_detect2Dep($dep, $pname, 'dependency group "' . - $group['attribs']['name'] . '"', $params); - if (is_array($ret)) { - $this->_downloadDeps[] = $ret; - } elseif (PEAR::isError($ret) && !isset($options['soft'])) { - $this->_downloader->log(0, $ret->getMessage()); - } - } - } else { - $ret = $this->_detect2Dep($group[$packagetype], $pname, - 'dependency group "' . - $group['attribs']['name'] . '"', $params); - if (is_array($ret)) { - $this->_downloadDeps[] = $ret; - } elseif (PEAR::isError($ret) && !isset($options['soft'])) { - $this->_downloader->log(0, $ret->getMessage()); - } - } - } - } - } - - function _detect2Dep($dep, $pname, $group, $params) - { - if (isset($dep['conflicts'])) { - return true; - } - - $options = $this->_downloader->getOptions(); - if (isset($dep['uri'])) { - return array('uri' => $dep['uri'], 'dep' => $dep);; - } - - $testdep = $dep; - $testdep['package'] = $dep['name']; - if (PEAR_Downloader_Package::willDownload($testdep, $params)) { - $dep['package'] = $dep['name']; - if (!isset($options['soft'])) { - $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group . - ' dependency "' . - $this->_registry->parsedPackageNameToString($dep, true) . - '", will be installed'); - } - return false; - } - - $options = $this->_downloader->getOptions(); - PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - if ($this->_explicitState) { - $pname['state'] = $this->_explicitState; - } - - $url = $this->_downloader->_getDepPackageDownloadUrl($dep, $pname); - if (PEAR::isError($url)) { - PEAR::popErrorHandling(); - return $url; - } - - $dep['package'] = $dep['name']; - $ret = $this->_analyzeDownloadURL($url, 'dependency', $dep, $params, $group == 'optional' && - !isset($options['alldeps']), true); - PEAR::popErrorHandling(); - if (PEAR::isError($ret)) { - if (!isset($options['soft'])) { - $this->_downloader->log(0, $ret->getMessage()); - } - - return false; - } - - // check to see if a dep is already installed and is the same or newer - if (!isset($dep['min']) && !isset($dep['max']) && !isset($dep['recommended'])) { - $oper = 'has'; - } else { - $oper = 'gt'; - } - - // do not try to move this before getDepPackageDownloadURL - // we can't determine whether upgrade is necessary until we know what - // version would be downloaded - if (!isset($options['force']) && $this->isInstalled($ret, $oper)) { - $version = $this->_installRegistry->packageInfo($dep['name'], 'version', $dep['channel']); - $dep['package'] = $dep['name']; - if (!isset($options['soft'])) { - $this->_downloader->log(3, $this->getShortName() . ': Skipping ' . $group . - ' dependency "' . - $this->_registry->parsedPackageNameToString($dep, true) . - '" version ' . $url['version'] . ', already installed as version ' . - $version); - } - - return false; - } - - if (isset($dep['nodefault'])) { - $ret['nodefault'] = true; - } - - return $ret; - } - - function _detect1($deps, $pname, $options, $params) - { - $this->_downloadDeps = array(); - $skipnames = array(); - foreach ($deps as $dep) { - $nodownload = false; - if (isset ($dep['type']) && $dep['type'] === 'pkg') { - $dep['channel'] = 'pear.php.net'; - $dep['package'] = $dep['name']; - switch ($dep['rel']) { - case 'not' : - continue 2; - case 'ge' : - case 'eq' : - case 'gt' : - case 'has' : - $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ? - 'required' : - 'optional'; - if (PEAR_Downloader_Package::willDownload($dep, $params)) { - $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group - . ' dependency "' . - $this->_registry->parsedPackageNameToString($dep, true) . - '", will be installed'); - continue 2; - } - $fakedp = new PEAR_PackageFile_v1; - $fakedp->setPackage($dep['name']); - // skip internet check if we are not upgrading (bug #5810) - if (!isset($options['upgrade']) && $this->isInstalled( - $fakedp, $dep['rel'])) { - $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group - . ' dependency "' . - $this->_registry->parsedPackageNameToString($dep, true) . - '", is already installed'); - continue 2; - } - } - - PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - if ($this->_explicitState) { - $pname['state'] = $this->_explicitState; - } - - $url = $this->_downloader->_getDepPackageDownloadUrl($dep, $pname); - $chan = 'pear.php.net'; - if (PEAR::isError($url)) { - // check to see if this is a pecl package that has jumped - // from pear.php.net to pecl.php.net channel - if (!class_exists('PEAR_Dependency2')) { - require_once 'PEAR/Dependency2.php'; - } - - $newdep = PEAR_Dependency2::normalizeDep($dep); - $newdep = $newdep[0]; - $newdep['channel'] = 'pecl.php.net'; - $chan = 'pecl.php.net'; - $url = $this->_downloader->_getDepPackageDownloadUrl($newdep, $pname); - $obj = &$this->_installRegistry->getPackage($dep['name']); - if (PEAR::isError($url)) { - PEAR::popErrorHandling(); - if ($obj !== null && $this->isInstalled($obj, $dep['rel'])) { - $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ? - 'required' : - 'optional'; - $dep['package'] = $dep['name']; - if (!isset($options['soft'])) { - $this->_downloader->log(3, $this->getShortName() . - ': Skipping ' . $group . ' dependency "' . - $this->_registry->parsedPackageNameToString($dep, true) . - '", already installed as version ' . $obj->getVersion()); - } - $skip = count($skipnames) ? - $skipnames[count($skipnames) - 1] : ''; - if ($skip == - $this->_registry->parsedPackageNameToString($dep, true)) { - array_pop($skipnames); - } - continue; - } else { - if (isset($dep['optional']) && $dep['optional'] == 'yes') { - $this->_downloader->log(2, $this->getShortName() . - ': Skipping optional dependency "' . - $this->_registry->parsedPackageNameToString($dep, true) . - '", no releases exist'); - continue; - } else { - return $url; - } - } - } - } - - PEAR::popErrorHandling(); - if (!isset($options['alldeps'])) { - if (isset($dep['optional']) && $dep['optional'] == 'yes') { - if (!isset($options['soft'])) { - $this->_downloader->log(3, 'Notice: package "' . - $this->getShortName() . - '" optional dependency "' . - $this->_registry->parsedPackageNameToString( - array('channel' => $chan, 'package' => - $dep['name']), true) . - '" will not be automatically downloaded'); - } - $skipnames[] = $this->_registry->parsedPackageNameToString( - array('channel' => $chan, 'package' => - $dep['name']), true); - $nodownload = true; - } - } - - if (!isset($options['alldeps']) && !isset($options['onlyreqdeps'])) { - if (!isset($dep['optional']) || $dep['optional'] == 'no') { - if (!isset($options['soft'])) { - $this->_downloader->log(3, 'Notice: package "' . - $this->getShortName() . - '" required dependency "' . - $this->_registry->parsedPackageNameToString( - array('channel' => $chan, 'package' => - $dep['name']), true) . - '" will not be automatically downloaded'); - } - $skipnames[] = $this->_registry->parsedPackageNameToString( - array('channel' => $chan, 'package' => - $dep['name']), true); - $nodownload = true; - } - } - - // check to see if a dep is already installed - // do not try to move this before getDepPackageDownloadURL - // we can't determine whether upgrade is necessary until we know what - // version would be downloaded - if (!isset($options['force']) && $this->isInstalled( - $url, $dep['rel'])) { - $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ? - 'required' : - 'optional'; - $dep['package'] = $dep['name']; - if (isset($newdep)) { - $version = $this->_installRegistry->packageInfo($newdep['name'], 'version', $newdep['channel']); - } else { - $version = $this->_installRegistry->packageInfo($dep['name'], 'version'); - } - - $dep['version'] = $url['version']; - if (!isset($options['soft'])) { - $this->_downloader->log(3, $this->getShortName() . ': Skipping ' . $group . - ' dependency "' . - $this->_registry->parsedPackageNameToString($dep, true) . - '", already installed as version ' . $version); - } - - $skip = count($skipnames) ? - $skipnames[count($skipnames) - 1] : ''; - if ($skip == - $this->_registry->parsedPackageNameToString($dep, true)) { - array_pop($skipnames); - } - - continue; - } - - if ($nodownload) { - continue; - } - - PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - if (isset($newdep)) { - $dep = $newdep; - } - - $dep['package'] = $dep['name']; - $ret = $this->_analyzeDownloadURL($url, 'dependency', $dep, $params, - isset($dep['optional']) && $dep['optional'] == 'yes' && - !isset($options['alldeps']), true); - PEAR::popErrorHandling(); - if (PEAR::isError($ret)) { - if (!isset($options['soft'])) { - $this->_downloader->log(0, $ret->getMessage()); - } - continue; - } - - $this->_downloadDeps[] = $ret; - } - } - - if (count($skipnames)) { - if (!isset($options['soft'])) { - $this->_downloader->log(1, 'Did not download dependencies: ' . - implode(', ', $skipnames) . - ', use --alldeps or --onlyreqdeps to download automatically'); - } - } - } - - function setDownloadURL($pkg) - { - $this->_downloadURL = $pkg; - } - - /** - * Set the package.xml object for this downloaded package - * - * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 $pkg - */ - function setPackageFile(&$pkg) - { - $this->_packagefile = &$pkg; - } - - function getShortName() - { - return $this->_registry->parsedPackageNameToString(array('channel' => $this->getChannel(), - 'package' => $this->getPackage()), true); - } - - function getParsedPackage() - { - if (isset($this->_packagefile) || isset($this->_parsedname)) { - return array('channel' => $this->getChannel(), - 'package' => $this->getPackage(), - 'version' => $this->getVersion()); - } - - return false; - } - - function getDownloadURL() - { - return $this->_downloadURL; - } - - function canDefault() - { - if (isset($this->_downloadURL) && isset($this->_downloadURL['nodefault'])) { - return false; - } - - return true; - } - - function getPackage() - { - if (isset($this->_packagefile)) { - return $this->_packagefile->getPackage(); - } elseif (isset($this->_downloadURL['info'])) { - return $this->_downloadURL['info']->getPackage(); - } - - return false; - } - - /** - * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 - */ - function isSubpackage(&$pf) - { - if (isset($this->_packagefile)) { - return $this->_packagefile->isSubpackage($pf); - } elseif (isset($this->_downloadURL['info'])) { - return $this->_downloadURL['info']->isSubpackage($pf); - } - - return false; - } - - function getPackageType() - { - if (isset($this->_packagefile)) { - return $this->_packagefile->getPackageType(); - } elseif (isset($this->_downloadURL['info'])) { - return $this->_downloadURL['info']->getPackageType(); - } - - return false; - } - - function isBundle() - { - if (isset($this->_packagefile)) { - return $this->_packagefile->getPackageType() == 'bundle'; - } - - return false; - } - - function getPackageXmlVersion() - { - if (isset($this->_packagefile)) { - return $this->_packagefile->getPackagexmlVersion(); - } elseif (isset($this->_downloadURL['info'])) { - return $this->_downloadURL['info']->getPackagexmlVersion(); - } - - return '1.0'; - } - - function getChannel() - { - if (isset($this->_packagefile)) { - return $this->_packagefile->getChannel(); - } elseif (isset($this->_downloadURL['info'])) { - return $this->_downloadURL['info']->getChannel(); - } - - return false; - } - - function getURI() - { - if (isset($this->_packagefile)) { - return $this->_packagefile->getURI(); - } elseif (isset($this->_downloadURL['info'])) { - return $this->_downloadURL['info']->getURI(); - } - - return false; - } - - function getVersion() - { - if (isset($this->_packagefile)) { - return $this->_packagefile->getVersion(); - } elseif (isset($this->_downloadURL['version'])) { - return $this->_downloadURL['version']; - } - - return false; - } - - function isCompatible($pf) - { - if (isset($this->_packagefile)) { - return $this->_packagefile->isCompatible($pf); - } elseif (isset($this->_downloadURL['info'])) { - return $this->_downloadURL['info']->isCompatible($pf); - } - - return true; - } - - function setGroup($group) - { - $this->_parsedname['group'] = $group; - } - - function getGroup() - { - if (isset($this->_parsedname['group'])) { - return $this->_parsedname['group']; - } - - return ''; - } - - function isExtension($name) - { - if (isset($this->_packagefile)) { - return $this->_packagefile->isExtension($name); - } elseif (isset($this->_downloadURL['info'])) { - if ($this->_downloadURL['info']->getPackagexmlVersion() == '2.0') { - return $this->_downloadURL['info']->getProvidesExtension() == $name; - } - - return false; - } - - return false; - } - - function getDeps() - { - if (isset($this->_packagefile)) { - $ver = $this->_packagefile->getPackagexmlVersion(); - if (version_compare($ver, '2.0', '>=')) { - return $this->_packagefile->getDeps(true); - } - - return $this->_packagefile->getDeps(); - } elseif (isset($this->_downloadURL['info'])) { - $ver = $this->_downloadURL['info']->getPackagexmlVersion(); - if (version_compare($ver, '2.0', '>=')) { - return $this->_downloadURL['info']->getDeps(true); - } - - return $this->_downloadURL['info']->getDeps(); - } - - return array(); - } - - /** - * @param array Parsed array from {@link PEAR_Registry::parsePackageName()} or a dependency - * returned from getDepDownloadURL() - */ - function isEqual($param) - { - if (is_object($param)) { - $channel = $param->getChannel(); - $package = $param->getPackage(); - if ($param->getURI()) { - $param = array( - 'channel' => $param->getChannel(), - 'package' => $param->getPackage(), - 'version' => $param->getVersion(), - 'uri' => $param->getURI(), - ); - } else { - $param = array( - 'channel' => $param->getChannel(), - 'package' => $param->getPackage(), - 'version' => $param->getVersion(), - ); - } - } else { - if (isset($param['uri'])) { - if ($this->getChannel() != '__uri') { - return false; - } - return $param['uri'] == $this->getURI(); - } - - $package = isset($param['package']) ? $param['package'] : $param['info']->getPackage(); - $channel = isset($param['channel']) ? $param['channel'] : $param['info']->getChannel(); - if (isset($param['rel'])) { - if (!class_exists('PEAR_Dependency2')) { - require_once 'PEAR/Dependency2.php'; - } - - $newdep = PEAR_Dependency2::normalizeDep($param); - $newdep = $newdep[0]; - } elseif (isset($param['min'])) { - $newdep = $param; - } - } - - if (isset($newdep)) { - if (!isset($newdep['min'])) { - $newdep['min'] = '0'; - } - - if (!isset($newdep['max'])) { - $newdep['max'] = '100000000000000000000'; - } - - // use magic to support pecl packages suddenly jumping to the pecl channel - // we need to support both dependency possibilities - if ($channel == 'pear.php.net' && $this->getChannel() == 'pecl.php.net') { - if ($package == $this->getPackage()) { - $channel = 'pecl.php.net'; - } - } - if ($channel == 'pecl.php.net' && $this->getChannel() == 'pear.php.net') { - if ($package == $this->getPackage()) { - $channel = 'pear.php.net'; - } - } - - return (strtolower($package) == strtolower($this->getPackage()) && - $channel == $this->getChannel() && - version_compare($newdep['min'], $this->getVersion(), '<=') && - version_compare($newdep['max'], $this->getVersion(), '>=')); - } - - // use magic to support pecl packages suddenly jumping to the pecl channel - if ($channel == 'pecl.php.net' && $this->getChannel() == 'pear.php.net') { - if (strtolower($package) == strtolower($this->getPackage())) { - $channel = 'pear.php.net'; - } - } - - if (isset($param['version'])) { - return (strtolower($package) == strtolower($this->getPackage()) && - $channel == $this->getChannel() && - $param['version'] == $this->getVersion()); - } - - return strtolower($package) == strtolower($this->getPackage()) && - $channel == $this->getChannel(); - } - - function isInstalled($dep, $oper = '==') - { - if (!$dep) { - return false; - } - - if ($oper != 'ge' && $oper != 'gt' && $oper != 'has' && $oper != '==') { - return false; - } - - if (is_object($dep)) { - $package = $dep->getPackage(); - $channel = $dep->getChannel(); - if ($dep->getURI()) { - $dep = array( - 'uri' => $dep->getURI(), - 'version' => $dep->getVersion(), - ); - } else { - $dep = array( - 'version' => $dep->getVersion(), - ); - } - } else { - if (isset($dep['uri'])) { - $channel = '__uri'; - $package = $dep['dep']['name']; - } else { - $channel = $dep['info']->getChannel(); - $package = $dep['info']->getPackage(); - } - } - - $options = $this->_downloader->getOptions(); - $test = $this->_installRegistry->packageExists($package, $channel); - if (!$test && $channel == 'pecl.php.net') { - // do magic to allow upgrading from old pecl packages to new ones - $test = $this->_installRegistry->packageExists($package, 'pear.php.net'); - $channel = 'pear.php.net'; - } - - if ($test) { - if (isset($dep['uri'])) { - if ($this->_installRegistry->packageInfo($package, 'uri', '__uri') == $dep['uri']) { - return true; - } - } - - if (isset($options['upgrade'])) { - $packageVersion = $this->_installRegistry->packageInfo($package, 'version', $channel); - if (version_compare($packageVersion, $dep['version'], '>=')) { - return true; - } - - return false; - } - - return true; - } - - return false; - } - - /** - * Detect duplicate package names with differing versions - * - * If a user requests to install Date 1.4.6 and Date 1.4.7, - * for instance, this is a logic error. This method - * detects this situation. - * - * @param array $params array of PEAR_Downloader_Package objects - * @param array $errorparams empty array - * @return array array of stupid duplicated packages in PEAR_Downloader_Package obejcts - */ - function detectStupidDuplicates($params, &$errorparams) - { - $existing = array(); - foreach ($params as $i => $param) { - $package = $param->getPackage(); - $channel = $param->getChannel(); - $group = $param->getGroup(); - if (!isset($existing[$channel . '/' . $package])) { - $existing[$channel . '/' . $package] = array(); - } - - if (!isset($existing[$channel . '/' . $package][$group])) { - $existing[$channel . '/' . $package][$group] = array(); - } - - $existing[$channel . '/' . $package][$group][] = $i; - } - - $indices = array(); - foreach ($existing as $package => $groups) { - foreach ($groups as $group => $dupes) { - if (count($dupes) > 1) { - $indices = $indices + $dupes; - } - } - } - - $indices = array_unique($indices); - foreach ($indices as $index) { - $errorparams[] = $params[$index]; - } - - return count($errorparams); - } - - /** - * @param array - * @param bool ignore install groups - for final removal of dupe packages - * @static - */ - function removeDuplicates(&$params, $ignoreGroups = false) - { - $pnames = array(); - foreach ($params as $i => $param) { - if (!$param) { - continue; - } - - if ($param->getPackage()) { - $group = $ignoreGroups ? '' : $param->getGroup(); - $pnames[$i] = $param->getChannel() . '/' . - $param->getPackage() . '-' . $param->getVersion() . '#' . $group; - } - } - - $pnames = array_unique($pnames); - $unset = array_diff(array_keys($params), array_keys($pnames)); - $testp = array_flip($pnames); - foreach ($params as $i => $param) { - if (!$param) { - $unset[] = $i; - continue; - } - - if (!is_a($param, 'PEAR_Downloader_Package')) { - $unset[] = $i; - continue; - } - - $group = $ignoreGroups ? '' : $param->getGroup(); - if (!isset($testp[$param->getChannel() . '/' . $param->getPackage() . '-' . - $param->getVersion() . '#' . $group])) { - $unset[] = $i; - } - } - - foreach ($unset as $i) { - unset($params[$i]); - } - - $ret = array(); - foreach ($params as $i => $param) { - $ret[] = &$params[$i]; - } - - $params = array(); - foreach ($ret as $i => $param) { - $params[] = &$ret[$i]; - } - } - - function explicitState() - { - return $this->_explicitState; - } - - function setExplicitState($s) - { - $this->_explicitState = $s; - } - - /** - * @static - */ - function mergeDependencies(&$params) - { - $bundles = $newparams = array(); - foreach ($params as $i => $param) { - if (!$param->isBundle()) { - continue; - } - - $bundles[] = $i; - $pf = &$param->getPackageFile(); - $newdeps = array(); - $contents = $pf->getBundledPackages(); - if (!is_array($contents)) { - $contents = array($contents); - } - - foreach ($contents as $file) { - $filecontents = $pf->getFileContents($file); - $dl = &$param->getDownloader(); - $options = $dl->getOptions(); - if (PEAR::isError($dir = $dl->getDownloadDir())) { - return $dir; - } - - $fp = @fopen($dir . DIRECTORY_SEPARATOR . $file, 'wb'); - if (!$fp) { - continue; - } - - // FIXME do symlink check - - fwrite($fp, $filecontents, strlen($filecontents)); - fclose($fp); - if ($s = $params[$i]->explicitState()) { - $obj->setExplicitState($s); - } - - $obj = &new PEAR_Downloader_Package($params[$i]->getDownloader()); - PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - if (PEAR::isError($dir = $dl->getDownloadDir())) { - PEAR::popErrorHandling(); - return $dir; - } - - $e = $obj->_fromFile($a = $dir . DIRECTORY_SEPARATOR . $file); - PEAR::popErrorHandling(); - if (PEAR::isError($e)) { - if (!isset($options['soft'])) { - $dl->log(0, $e->getMessage()); - } - continue; - } - - $j = &$obj; - if (!PEAR_Downloader_Package::willDownload($j, - array_merge($params, $newparams)) && !$param->isInstalled($j)) { - $newparams[] = &$j; - } - } - } - - foreach ($bundles as $i) { - unset($params[$i]); // remove bundles - only their contents matter for installation - } - - PEAR_Downloader_Package::removeDuplicates($params); // strip any unset indices - if (count($newparams)) { // add in bundled packages for install - foreach ($newparams as $i => $unused) { - $params[] = &$newparams[$i]; - } - $newparams = array(); - } - - foreach ($params as $i => $param) { - $newdeps = array(); - foreach ($param->_downloadDeps as $dep) { - $merge = array_merge($params, $newparams); - if (!PEAR_Downloader_Package::willDownload($dep, $merge) - && !$param->isInstalled($dep) - ) { - $newdeps[] = $dep; - } else { - //var_dump($dep); - // detect versioning conflicts here - } - } - - // convert the dependencies into PEAR_Downloader_Package objects for the next time around - $params[$i]->_downloadDeps = array(); - foreach ($newdeps as $dep) { - $obj = &new PEAR_Downloader_Package($params[$i]->getDownloader()); - if ($s = $params[$i]->explicitState()) { - $obj->setExplicitState($s); - } - - PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - $e = $obj->fromDepURL($dep); - PEAR::popErrorHandling(); - if (PEAR::isError($e)) { - if (!isset($options['soft'])) { - $obj->_downloader->log(0, $e->getMessage()); - } - continue; - } - - $e = $obj->detectDependencies($params); - if (PEAR::isError($e)) { - if (!isset($options['soft'])) { - $obj->_downloader->log(0, $e->getMessage()); - } - } - - $j = &$obj; - $newparams[] = &$j; - } - } - - if (count($newparams)) { - foreach ($newparams as $i => $unused) { - $params[] = &$newparams[$i]; - } - return true; - } - - return false; - } - - - /** - * @static - */ - function willDownload($param, $params) - { - if (!is_array($params)) { - return false; - } - - foreach ($params as $obj) { - if ($obj->isEqual($param)) { - return true; - } - } - - return false; - } - - /** - * For simpler unit-testing - * @param PEAR_Config - * @param int - * @param string - */ - function &getPackagefileObject(&$c, $d) - { - $a = &new PEAR_PackageFile($c, $d); - return $a; - } - - /** - * This will retrieve from a local file if possible, and parse out - * a group name as well. The original parameter will be modified to reflect this. - * @param string|array can be a parsed package name as well - * @access private - */ - function _fromFile(&$param) - { - $saveparam = $param; - if (is_string($param)) { - if (!@file_exists($param)) { - $test = explode('#', $param); - $group = array_pop($test); - if (@file_exists(implode('#', $test))) { - $this->setGroup($group); - $param = implode('#', $test); - $this->_explicitGroup = true; - } - } - - if (@is_file($param)) { - $this->_type = 'local'; - $options = $this->_downloader->getOptions(); - $pkg = &$this->getPackagefileObject($this->_config, $this->_downloader->_debug); - PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - $pf = &$pkg->fromAnyFile($param, PEAR_VALIDATE_INSTALLING); - PEAR::popErrorHandling(); - if (PEAR::isError($pf)) { - $this->_valid = false; - $param = $saveparam; - return $pf; - } - $this->_packagefile = &$pf; - if (!$this->getGroup()) { - $this->setGroup('default'); // install the default dependency group - } - return $this->_valid = true; - } - } - $param = $saveparam; - return $this->_valid = false; - } - - function _fromUrl($param, $saveparam = '') - { - if (!is_array($param) && (preg_match('#^(http|https|ftp)://#', $param))) { - $options = $this->_downloader->getOptions(); - $this->_type = 'url'; - $callback = $this->_downloader->ui ? - array(&$this->_downloader, '_downloadCallback') : null; - $this->_downloader->pushErrorHandling(PEAR_ERROR_RETURN); - if (PEAR::isError($dir = $this->_downloader->getDownloadDir())) { - $this->_downloader->popErrorHandling(); - return $dir; - } - - $this->_downloader->log(3, 'Downloading "' . $param . '"'); - $file = $this->_downloader->downloadHttp($param, $this->_downloader->ui, - $dir, $callback, null, false, $this->getChannel()); - $this->_downloader->popErrorHandling(); - if (PEAR::isError($file)) { - if (!empty($saveparam)) { - $saveparam = ", cannot download \"$saveparam\""; - } - $err = PEAR::raiseError('Could not download from "' . $param . - '"' . $saveparam . ' (' . $file->getMessage() . ')'); - return $err; - } - - if ($this->_rawpackagefile) { - require_once 'Archive/Tar.php'; - $tar = &new Archive_Tar($file); - $packagexml = $tar->extractInString('package2.xml'); - if (!$packagexml) { - $packagexml = $tar->extractInString('package.xml'); - } - - if (str_replace(array("\n", "\r"), array('',''), $packagexml) != - str_replace(array("\n", "\r"), array('',''), $this->_rawpackagefile)) { - if ($this->getChannel() != 'pear.php.net') { - return PEAR::raiseError('CRITICAL ERROR: package.xml downloaded does ' . - 'not match value returned from xml-rpc'); - } - - // be more lax for the existing PEAR packages that have not-ok - // characters in their package.xml - $this->_downloader->log(0, 'CRITICAL WARNING: The "' . - $this->getPackage() . '" package has invalid characters in its ' . - 'package.xml. The next version of PEAR may not be able to install ' . - 'this package for security reasons. Please open a bug report at ' . - 'http://pear.php.net/package/' . $this->getPackage() . '/bugs'); - } - } - - // whew, download worked! - $pkg = &$this->getPackagefileObject($this->_config, $this->_downloader->debug); - - PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - $pf = &$pkg->fromAnyFile($file, PEAR_VALIDATE_INSTALLING); - PEAR::popErrorHandling(); - if (PEAR::isError($pf)) { - if (is_array($pf->getUserInfo())) { - foreach ($pf->getUserInfo() as $err) { - if (is_array($err)) { - $err = $err['message']; - } - - if (!isset($options['soft'])) { - $this->_downloader->log(0, "Validation Error: $err"); - } - } - } - - if (!isset($options['soft'])) { - $this->_downloader->log(0, $pf->getMessage()); - } - - ///FIXME need to pass back some error code that we can use to match with to cancel all further operations - /// At least stop all deps of this package from being installed - $out = $saveparam ? $saveparam : $param; - $err = PEAR::raiseError('Download of "' . $out . '" succeeded, but it is not a valid package archive'); - $this->_valid = false; - return $err; - } - - $this->_packagefile = &$pf; - $this->setGroup('default'); // install the default dependency group - return $this->_valid = true; - } - - return $this->_valid = false; - } - - /** - * - * @param string|array pass in an array of format - * array( - * 'package' => 'pname', - * ['channel' => 'channame',] - * ['version' => 'version',] - * ['state' => 'state',]) - * or a string of format [channame/]pname[-version|-state] - */ - function _fromString($param) - { - $options = $this->_downloader->getOptions(); - $channel = $this->_config->get('default_channel'); - PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - $pname = $this->_registry->parsePackageName($param, $channel); - PEAR::popErrorHandling(); - if (PEAR::isError($pname)) { - if ($pname->getCode() == 'invalid') { - $this->_valid = false; - return false; - } - - if ($pname->getCode() == 'channel') { - $parsed = $pname->getUserInfo(); - if ($this->_downloader->discover($parsed['channel'])) { - if ($this->_config->get('auto_discover')) { - PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - $pname = $this->_registry->parsePackageName($param, $channel); - PEAR::popErrorHandling(); - } else { - if (!isset($options['soft'])) { - $this->_downloader->log(0, 'Channel "' . $parsed['channel'] . - '" is not initialized, use ' . - '"pear channel-discover ' . $parsed['channel'] . '" to initialize ' . - 'or pear config-set auto_discover 1'); - } - } - } - - if (PEAR::isError($pname)) { - if (!isset($options['soft'])) { - $this->_downloader->log(0, $pname->getMessage()); - } - - if (is_array($param)) { - $param = $this->_registry->parsedPackageNameToString($param); - } - - $err = PEAR::raiseError('invalid package name/package file "' . $param . '"'); - $this->_valid = false; - return $err; - } - } else { - if (!isset($options['soft'])) { - $this->_downloader->log(0, $pname->getMessage()); - } - - $err = PEAR::raiseError('invalid package name/package file "' . $param . '"'); - $this->_valid = false; - return $err; - } - } - - if (!isset($this->_type)) { - $this->_type = 'rest'; - } - - $this->_parsedname = $pname; - $this->_explicitState = isset($pname['state']) ? $pname['state'] : false; - $this->_explicitGroup = isset($pname['group']) ? true : false; - - $info = $this->_downloader->_getPackageDownloadUrl($pname); - if (PEAR::isError($info)) { - if ($info->getCode() != -976 && $pname['channel'] == 'pear.php.net') { - // try pecl - $pname['channel'] = 'pecl.php.net'; - if ($test = $this->_downloader->_getPackageDownloadUrl($pname)) { - if (!PEAR::isError($test)) { - $info = PEAR::raiseError($info->getMessage() . ' - package ' . - $this->_registry->parsedPackageNameToString($pname, true) . - ' can be installed with "pecl install ' . $pname['package'] . - '"'); - } else { - $pname['channel'] = 'pear.php.net'; - } - } else { - $pname['channel'] = 'pear.php.net'; - } - } - - return $info; - } - - $this->_rawpackagefile = $info['raw']; - $ret = $this->_analyzeDownloadURL($info, $param, $pname); - if (PEAR::isError($ret)) { - return $ret; - } - - if ($ret) { - $this->_downloadURL = $ret; - return $this->_valid = (bool) $ret; - } - } - - /** - * @param array output of package.getDownloadURL - * @param string|array|object information for detecting packages to be downloaded, and - * for errors - * @param array name information of the package - * @param array|null packages to be downloaded - * @param bool is this an optional dependency? - * @param bool is this any kind of dependency? - * @access private - */ - function _analyzeDownloadURL($info, $param, $pname, $params = null, $optional = false, - $isdependency = false) - { - if (!is_string($param) && PEAR_Downloader_Package::willDownload($param, $params)) { - return false; - } - - if ($info === false) { - $saveparam = !is_string($param) ? ", cannot download \"$param\"" : ''; - - // no releases exist - return PEAR::raiseError('No releases for package "' . - $this->_registry->parsedPackageNameToString($pname, true) . '" exist' . $saveparam); - } - - if (strtolower($info['info']->getChannel()) != strtolower($pname['channel'])) { - $err = false; - if ($pname['channel'] == 'pecl.php.net') { - if ($info['info']->getChannel() != 'pear.php.net') { - $err = true; - } - } elseif ($info['info']->getChannel() == 'pecl.php.net') { - if ($pname['channel'] != 'pear.php.net') { - $err = true; - } - } else { - $err = true; - } - - if ($err) { - return PEAR::raiseError('SECURITY ERROR: package in channel "' . $pname['channel'] . - '" retrieved another channel\'s name for download! ("' . - $info['info']->getChannel() . '")'); - } - } - - $preferred_state = $this->_config->get('preferred_state'); - if (!isset($info['url'])) { - $package_version = $this->_registry->packageInfo($info['info']->getPackage(), - 'version', $info['info']->getChannel()); - if ($this->isInstalled($info)) { - if ($isdependency && version_compare($info['version'], $package_version, '<=')) { - // ignore bogus errors of "failed to download dependency" - // if it is already installed and the one that would be - // downloaded is older or the same version (Bug #7219) - return false; - } - } - - if ($info['version'] === $package_version) { - if (!isset($options['soft'])) { - $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] . - '/' . $pname['package'] . '-' . $package_version. ', additionally the suggested version' . - ' (' . $package_version . ') is the same as the locally installed one.'); - } - - return false; - } - - if (version_compare($info['version'], $package_version, '<=')) { - if (!isset($options['soft'])) { - $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] . - '/' . $pname['package'] . '-' . $package_version . ', additionally the suggested version' . - ' (' . $info['version'] . ') is a lower version than the locally installed one (' . $package_version . ').'); - } - - return false; - } - - $instead = ', will instead download version ' . $info['version'] . - ', stability "' . $info['info']->getState() . '"'; - // releases exist, but we failed to get any - if (isset($this->_downloader->_options['force'])) { - if (isset($pname['version'])) { - $vs = ', version "' . $pname['version'] . '"'; - } elseif (isset($pname['state'])) { - $vs = ', stability "' . $pname['state'] . '"'; - } elseif ($param == 'dependency') { - if (!class_exists('PEAR_Common')) { - require_once 'PEAR/Common.php'; - } - - if (!in_array($info['info']->getState(), - PEAR_Common::betterStates($preferred_state, true))) { - if ($optional) { - // don't spit out confusing error message - return $this->_downloader->_getPackageDownloadUrl( - array('package' => $pname['package'], - 'channel' => $pname['channel'], - 'version' => $info['version'])); - } - $vs = ' within preferred state "' . $preferred_state . - '"'; - } else { - if (!class_exists('PEAR_Dependency2')) { - require_once 'PEAR/Dependency2.php'; - } - - if ($optional) { - // don't spit out confusing error message - return $this->_downloader->_getPackageDownloadUrl( - array('package' => $pname['package'], - 'channel' => $pname['channel'], - 'version' => $info['version'])); - } - $vs = PEAR_Dependency2::_getExtraString($pname); - $instead = ''; - } - } else { - $vs = ' within preferred state "' . $preferred_state . '"'; - } - - if (!isset($options['soft'])) { - $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] . - '/' . $pname['package'] . $vs . $instead); - } - - // download the latest release - return $this->_downloader->_getPackageDownloadUrl( - array('package' => $pname['package'], - 'channel' => $pname['channel'], - 'version' => $info['version'])); - } else { - if (isset($info['php']) && $info['php']) { - $err = PEAR::raiseError('Failed to download ' . - $this->_registry->parsedPackageNameToString( - array('channel' => $pname['channel'], - 'package' => $pname['package']), - true) . - ', latest release is version ' . $info['php']['v'] . - ', but it requires PHP version "' . - $info['php']['m'] . '", use "' . - $this->_registry->parsedPackageNameToString( - array('channel' => $pname['channel'], 'package' => $pname['package'], - 'version' => $info['php']['v'])) . '" to install', - PEAR_DOWNLOADER_PACKAGE_PHPVERSION); - return $err; - } - - // construct helpful error message - if (isset($pname['version'])) { - $vs = ', version "' . $pname['version'] . '"'; - } elseif (isset($pname['state'])) { - $vs = ', stability "' . $pname['state'] . '"'; - } elseif ($param == 'dependency') { - if (!class_exists('PEAR_Common')) { - require_once 'PEAR/Common.php'; - } - - if (!in_array($info['info']->getState(), - PEAR_Common::betterStates($preferred_state, true))) { - if ($optional) { - // don't spit out confusing error message, and don't die on - // optional dep failure! - return $this->_downloader->_getPackageDownloadUrl( - array('package' => $pname['package'], - 'channel' => $pname['channel'], - 'version' => $info['version'])); - } - $vs = ' within preferred state "' . $preferred_state . '"'; - } else { - if (!class_exists('PEAR_Dependency2')) { - require_once 'PEAR/Dependency2.php'; - } - - if ($optional) { - // don't spit out confusing error message, and don't die on - // optional dep failure! - return $this->_downloader->_getPackageDownloadUrl( - array('package' => $pname['package'], - 'channel' => $pname['channel'], - 'version' => $info['version'])); - } - $vs = PEAR_Dependency2::_getExtraString($pname); - } - } else { - $vs = ' within preferred state "' . $this->_downloader->config->get('preferred_state') . '"'; - } - - $options = $this->_downloader->getOptions(); - // this is only set by the "download-all" command - if (isset($options['ignorepreferred_state'])) { - $err = PEAR::raiseError( - 'Failed to download ' . $this->_registry->parsedPackageNameToString( - array('channel' => $pname['channel'], 'package' => $pname['package']), - true) - . $vs . - ', latest release is version ' . $info['version'] . - ', stability "' . $info['info']->getState() . '", use "' . - $this->_registry->parsedPackageNameToString( - array('channel' => $pname['channel'], 'package' => $pname['package'], - 'version' => $info['version'])) . '" to install', - PEAR_DOWNLOADER_PACKAGE_STATE); - return $err; - } - - // Checks if the user has a package installed already and checks the release against - // the state against the installed package, this allows upgrades for packages - // with lower stability than the preferred_state - $stability = $this->_registry->packageInfo($pname['package'], 'stability', $pname['channel']); - if (!$this->isInstalled($info) - || !in_array($info['info']->getState(), PEAR_Common::betterStates($stability['release'], true)) - ) { - $err = PEAR::raiseError( - 'Failed to download ' . $this->_registry->parsedPackageNameToString( - array('channel' => $pname['channel'], 'package' => $pname['package']), - true) - . $vs . - ', latest release is version ' . $info['version'] . - ', stability "' . $info['info']->getState() . '", use "' . - $this->_registry->parsedPackageNameToString( - array('channel' => $pname['channel'], 'package' => $pname['package'], - 'version' => $info['version'])) . '" to install'); - return $err; - } - } - } - - if (isset($info['deprecated']) && $info['deprecated']) { - $this->_downloader->log(0, - 'WARNING: "' . - $this->_registry->parsedPackageNameToString( - array('channel' => $info['info']->getChannel(), - 'package' => $info['info']->getPackage()), true) . - '" is deprecated in favor of "' . - $this->_registry->parsedPackageNameToString($info['deprecated'], true) . - '"'); - } - - return $info; - } -} \ No newline at end of file diff --git a/pear/PEAR/ErrorStack.php b/pear/PEAR/ErrorStack.php deleted file mode 100644 index 2cdddfc..0000000 --- a/pear/PEAR/ErrorStack.php +++ /dev/null @@ -1,985 +0,0 @@ - - * @copyright 2004-2008 Greg Beaver - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR_ErrorStack - */ - -/** - * Singleton storage - * - * Format: - *
    - * array(
    - *  'package1' => PEAR_ErrorStack object,
    - *  'package2' => PEAR_ErrorStack object,
    - *  ...
    - * )
    - * 
    - * @access private - * @global array $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] - */ -$GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] = array(); - -/** - * Global error callback (default) - * - * This is only used if set to non-false. * is the default callback for - * all packages, whereas specific packages may set a default callback - * for all instances, regardless of whether they are a singleton or not. - * - * To exclude non-singletons, only set the local callback for the singleton - * @see PEAR_ErrorStack::setDefaultCallback() - * @access private - * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'] - */ -$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'] = array( - '*' => false, -); - -/** - * Global Log object (default) - * - * This is only used if set to non-false. Use to set a default log object for - * all stacks, regardless of instantiation order or location - * @see PEAR_ErrorStack::setDefaultLogger() - * @access private - * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] - */ -$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = false; - -/** - * Global Overriding Callback - * - * This callback will override any error callbacks that specific loggers have set. - * Use with EXTREME caution - * @see PEAR_ErrorStack::staticPushCallback() - * @access private - * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] - */ -$GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array(); - -/**#@+ - * One of four possible return values from the error Callback - * @see PEAR_ErrorStack::_errorCallback() - */ -/** - * If this is returned, then the error will be both pushed onto the stack - * and logged. - */ -define('PEAR_ERRORSTACK_PUSHANDLOG', 1); -/** - * If this is returned, then the error will only be pushed onto the stack, - * and not logged. - */ -define('PEAR_ERRORSTACK_PUSH', 2); -/** - * If this is returned, then the error will only be logged, but not pushed - * onto the error stack. - */ -define('PEAR_ERRORSTACK_LOG', 3); -/** - * If this is returned, then the error is completely ignored. - */ -define('PEAR_ERRORSTACK_IGNORE', 4); -/** - * If this is returned, then the error is logged and die() is called. - */ -define('PEAR_ERRORSTACK_DIE', 5); -/**#@-*/ - -/** - * Error code for an attempt to instantiate a non-class as a PEAR_ErrorStack in - * the singleton method. - */ -define('PEAR_ERRORSTACK_ERR_NONCLASS', 1); - -/** - * Error code for an attempt to pass an object into {@link PEAR_ErrorStack::getMessage()} - * that has no __toString() method - */ -define('PEAR_ERRORSTACK_ERR_OBJTOSTRING', 2); -/** - * Error Stack Implementation - * - * Usage: - * - * // global error stack - * $global_stack = &PEAR_ErrorStack::singleton('MyPackage'); - * // local error stack - * $local_stack = new PEAR_ErrorStack('MyPackage'); - * - * @author Greg Beaver - * @version @package_version@ - * @package PEAR_ErrorStack - * @category Debugging - * @copyright 2004-2008 Greg Beaver - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR_ErrorStack - */ -class PEAR_ErrorStack { - /** - * Errors are stored in the order that they are pushed on the stack. - * @since 0.4alpha Errors are no longer organized by error level. - * This renders pop() nearly unusable, and levels could be more easily - * handled in a callback anyway - * @var array - * @access private - */ - var $_errors = array(); - - /** - * Storage of errors by level. - * - * Allows easy retrieval and deletion of only errors from a particular level - * @since PEAR 1.4.0dev - * @var array - * @access private - */ - var $_errorsByLevel = array(); - - /** - * Package name this error stack represents - * @var string - * @access protected - */ - var $_package; - - /** - * Determines whether a PEAR_Error is thrown upon every error addition - * @var boolean - * @access private - */ - var $_compat = false; - - /** - * If set to a valid callback, this will be used to generate the error - * message from the error code, otherwise the message passed in will be - * used - * @var false|string|array - * @access private - */ - var $_msgCallback = false; - - /** - * If set to a valid callback, this will be used to generate the error - * context for an error. For PHP-related errors, this will be a file - * and line number as retrieved from debug_backtrace(), but can be - * customized for other purposes. The error might actually be in a separate - * configuration file, or in a database query. - * @var false|string|array - * @access protected - */ - var $_contextCallback = false; - - /** - * If set to a valid callback, this will be called every time an error - * is pushed onto the stack. The return value will be used to determine - * whether to allow an error to be pushed or logged. - * - * The return value must be one an PEAR_ERRORSTACK_* constant - * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG - * @var false|string|array - * @access protected - */ - var $_errorCallback = array(); - - /** - * PEAR::Log object for logging errors - * @var false|Log - * @access protected - */ - var $_logger = false; - - /** - * Error messages - designed to be overridden - * @var array - * @abstract - */ - var $_errorMsgs = array(); - - /** - * Set up a new error stack - * - * @param string $package name of the package this error stack represents - * @param callback $msgCallback callback used for error message generation - * @param callback $contextCallback callback used for context generation, - * defaults to {@link getFileLine()} - * @param boolean $throwPEAR_Error - */ - function PEAR_ErrorStack($package, $msgCallback = false, $contextCallback = false, - $throwPEAR_Error = false) - { - $this->_package = $package; - $this->setMessageCallback($msgCallback); - $this->setContextCallback($contextCallback); - $this->_compat = $throwPEAR_Error; - } - - /** - * Return a single error stack for this package. - * - * Note that all parameters are ignored if the stack for package $package - * has already been instantiated - * @param string $package name of the package this error stack represents - * @param callback $msgCallback callback used for error message generation - * @param callback $contextCallback callback used for context generation, - * defaults to {@link getFileLine()} - * @param boolean $throwPEAR_Error - * @param string $stackClass class to instantiate - * @static - * @return PEAR_ErrorStack - */ - function &singleton($package, $msgCallback = false, $contextCallback = false, - $throwPEAR_Error = false, $stackClass = 'PEAR_ErrorStack') - { - if (isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) { - return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]; - } - if (!class_exists($stackClass)) { - if (function_exists('debug_backtrace')) { - $trace = debug_backtrace(); - } - PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_NONCLASS, - 'exception', array('stackclass' => $stackClass), - 'stack class "%stackclass%" is not a valid class name (should be like PEAR_ErrorStack)', - false, $trace); - } - $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package] = - new $stackClass($package, $msgCallback, $contextCallback, $throwPEAR_Error); - - return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]; - } - - /** - * Internal error handler for PEAR_ErrorStack class - * - * Dies if the error is an exception (and would have died anyway) - * @access private - */ - function _handleError($err) - { - if ($err['level'] == 'exception') { - $message = $err['message']; - if (isset($_SERVER['REQUEST_URI'])) { - echo '
    '; - } else { - echo "\n"; - } - var_dump($err['context']); - die($message); - } - } - - /** - * Set up a PEAR::Log object for all error stacks that don't have one - * @param Log $log - * @static - */ - function setDefaultLogger(&$log) - { - if (is_object($log) && method_exists($log, 'log') ) { - $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log; - } elseif (is_callable($log)) { - $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log; - } - } - - /** - * Set up a PEAR::Log object for this error stack - * @param Log $log - */ - function setLogger(&$log) - { - if (is_object($log) && method_exists($log, 'log') ) { - $this->_logger = &$log; - } elseif (is_callable($log)) { - $this->_logger = &$log; - } - } - - /** - * Set an error code => error message mapping callback - * - * This method sets the callback that can be used to generate error - * messages for any instance - * @param array|string Callback function/method - */ - function setMessageCallback($msgCallback) - { - if (!$msgCallback) { - $this->_msgCallback = array(&$this, 'getErrorMessage'); - } else { - if (is_callable($msgCallback)) { - $this->_msgCallback = $msgCallback; - } - } - } - - /** - * Get an error code => error message mapping callback - * - * This method returns the current callback that can be used to generate error - * messages - * @return array|string|false Callback function/method or false if none - */ - function getMessageCallback() - { - return $this->_msgCallback; - } - - /** - * Sets a default callback to be used by all error stacks - * - * This method sets the callback that can be used to generate error - * messages for a singleton - * @param array|string Callback function/method - * @param string Package name, or false for all packages - * @static - */ - function setDefaultCallback($callback = false, $package = false) - { - if (!is_callable($callback)) { - $callback = false; - } - $package = $package ? $package : '*'; - $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$package] = $callback; - } - - /** - * Set a callback that generates context information (location of error) for an error stack - * - * This method sets the callback that can be used to generate context - * information for an error. Passing in NULL will disable context generation - * and remove the expensive call to debug_backtrace() - * @param array|string|null Callback function/method - */ - function setContextCallback($contextCallback) - { - if ($contextCallback === null) { - return $this->_contextCallback = false; - } - if (!$contextCallback) { - $this->_contextCallback = array(&$this, 'getFileLine'); - } else { - if (is_callable($contextCallback)) { - $this->_contextCallback = $contextCallback; - } - } - } - - /** - * Set an error Callback - * If set to a valid callback, this will be called every time an error - * is pushed onto the stack. The return value will be used to determine - * whether to allow an error to be pushed or logged. - * - * The return value must be one of the ERRORSTACK_* constants. - * - * This functionality can be used to emulate PEAR's pushErrorHandling, and - * the PEAR_ERROR_CALLBACK mode, without affecting the integrity of - * the error stack or logging - * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG - * @see popCallback() - * @param string|array $cb - */ - function pushCallback($cb) - { - array_push($this->_errorCallback, $cb); - } - - /** - * Remove a callback from the error callback stack - * @see pushCallback() - * @return array|string|false - */ - function popCallback() - { - if (!count($this->_errorCallback)) { - return false; - } - return array_pop($this->_errorCallback); - } - - /** - * Set a temporary overriding error callback for every package error stack - * - * Use this to temporarily disable all existing callbacks (can be used - * to emulate the @ operator, for instance) - * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG - * @see staticPopCallback(), pushCallback() - * @param string|array $cb - * @static - */ - function staticPushCallback($cb) - { - array_push($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'], $cb); - } - - /** - * Remove a temporary overriding error callback - * @see staticPushCallback() - * @return array|string|false - * @static - */ - function staticPopCallback() - { - $ret = array_pop($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK']); - if (!is_array($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'])) { - $GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array(); - } - return $ret; - } - - /** - * Add an error to the stack - * - * If the message generator exists, it is called with 2 parameters. - * - the current Error Stack object - * - an array that is in the same format as an error. Available indices - * are 'code', 'package', 'time', 'params', 'level', and 'context' - * - * Next, if the error should contain context information, this is - * handled by the context grabbing method. - * Finally, the error is pushed onto the proper error stack - * @param int $code Package-specific error code - * @param string $level Error level. This is NOT spell-checked - * @param array $params associative array of error parameters - * @param string $msg Error message, or a portion of it if the message - * is to be generated - * @param array $repackage If this error re-packages an error pushed by - * another package, place the array returned from - * {@link pop()} in this parameter - * @param array $backtrace Protected parameter: use this to pass in the - * {@link debug_backtrace()} that should be used - * to find error context - * @return PEAR_Error|array if compatibility mode is on, a PEAR_Error is also - * thrown. If a PEAR_Error is returned, the userinfo - * property is set to the following array: - * - * - * array( - * 'code' => $code, - * 'params' => $params, - * 'package' => $this->_package, - * 'level' => $level, - * 'time' => time(), - * 'context' => $context, - * 'message' => $msg, - * //['repackage' => $err] repackaged error array/Exception class - * ); - * - * - * Normally, the previous array is returned. - */ - function push($code, $level = 'error', $params = array(), $msg = false, - $repackage = false, $backtrace = false) - { - $context = false; - // grab error context - if ($this->_contextCallback) { - if (!$backtrace) { - $backtrace = debug_backtrace(); - } - $context = call_user_func($this->_contextCallback, $code, $params, $backtrace); - } - - // save error - $time = explode(' ', microtime()); - $time = $time[1] + $time[0]; - $err = array( - 'code' => $code, - 'params' => $params, - 'package' => $this->_package, - 'level' => $level, - 'time' => $time, - 'context' => $context, - 'message' => $msg, - ); - - if ($repackage) { - $err['repackage'] = $repackage; - } - - // set up the error message, if necessary - if ($this->_msgCallback) { - $msg = call_user_func_array($this->_msgCallback, - array(&$this, $err)); - $err['message'] = $msg; - } - $push = $log = true; - $die = false; - // try the overriding callback first - $callback = $this->staticPopCallback(); - if ($callback) { - $this->staticPushCallback($callback); - } - if (!is_callable($callback)) { - // try the local callback next - $callback = $this->popCallback(); - if (is_callable($callback)) { - $this->pushCallback($callback); - } else { - // try the default callback - $callback = isset($GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package]) ? - $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package] : - $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK']['*']; - } - } - if (is_callable($callback)) { - switch(call_user_func($callback, $err)){ - case PEAR_ERRORSTACK_IGNORE: - return $err; - break; - case PEAR_ERRORSTACK_PUSH: - $log = false; - break; - case PEAR_ERRORSTACK_LOG: - $push = false; - break; - case PEAR_ERRORSTACK_DIE: - $die = true; - break; - // anything else returned has the same effect as pushandlog - } - } - if ($push) { - array_unshift($this->_errors, $err); - if (!isset($this->_errorsByLevel[$err['level']])) { - $this->_errorsByLevel[$err['level']] = array(); - } - $this->_errorsByLevel[$err['level']][] = &$this->_errors[0]; - } - if ($log) { - if ($this->_logger || $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']) { - $this->_log($err); - } - } - if ($die) { - die(); - } - if ($this->_compat && $push) { - return $this->raiseError($msg, $code, null, null, $err); - } - return $err; - } - - /** - * Static version of {@link push()} - * - * @param string $package Package name this error belongs to - * @param int $code Package-specific error code - * @param string $level Error level. This is NOT spell-checked - * @param array $params associative array of error parameters - * @param string $msg Error message, or a portion of it if the message - * is to be generated - * @param array $repackage If this error re-packages an error pushed by - * another package, place the array returned from - * {@link pop()} in this parameter - * @param array $backtrace Protected parameter: use this to pass in the - * {@link debug_backtrace()} that should be used - * to find error context - * @return PEAR_Error|array if compatibility mode is on, a PEAR_Error is also - * thrown. see docs for {@link push()} - * @static - */ - function staticPush($package, $code, $level = 'error', $params = array(), - $msg = false, $repackage = false, $backtrace = false) - { - $s = &PEAR_ErrorStack::singleton($package); - if ($s->_contextCallback) { - if (!$backtrace) { - if (function_exists('debug_backtrace')) { - $backtrace = debug_backtrace(); - } - } - } - return $s->push($code, $level, $params, $msg, $repackage, $backtrace); - } - - /** - * Log an error using PEAR::Log - * @param array $err Error array - * @param array $levels Error level => Log constant map - * @access protected - */ - function _log($err) - { - if ($this->_logger) { - $logger = &$this->_logger; - } else { - $logger = &$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']; - } - if (is_a($logger, 'Log')) { - $levels = array( - 'exception' => PEAR_LOG_CRIT, - 'alert' => PEAR_LOG_ALERT, - 'critical' => PEAR_LOG_CRIT, - 'error' => PEAR_LOG_ERR, - 'warning' => PEAR_LOG_WARNING, - 'notice' => PEAR_LOG_NOTICE, - 'info' => PEAR_LOG_INFO, - 'debug' => PEAR_LOG_DEBUG); - if (isset($levels[$err['level']])) { - $level = $levels[$err['level']]; - } else { - $level = PEAR_LOG_INFO; - } - $logger->log($err['message'], $level, $err); - } else { // support non-standard logs - call_user_func($logger, $err); - } - } - - - /** - * Pop an error off of the error stack - * - * @return false|array - * @since 0.4alpha it is no longer possible to specify a specific error - * level to return - the last error pushed will be returned, instead - */ - function pop() - { - $err = @array_shift($this->_errors); - if (!is_null($err)) { - @array_pop($this->_errorsByLevel[$err['level']]); - if (!count($this->_errorsByLevel[$err['level']])) { - unset($this->_errorsByLevel[$err['level']]); - } - } - return $err; - } - - /** - * Pop an error off of the error stack, static method - * - * @param string package name - * @return boolean - * @since PEAR1.5.0a1 - */ - function staticPop($package) - { - if ($package) { - if (!isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) { - return false; - } - return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->pop(); - } - } - - /** - * Determine whether there are any errors on the stack - * @param string|array Level name. Use to determine if any errors - * of level (string), or levels (array) have been pushed - * @return boolean - */ - function hasErrors($level = false) - { - if ($level) { - return isset($this->_errorsByLevel[$level]); - } - return count($this->_errors); - } - - /** - * Retrieve all errors since last purge - * - * @param boolean set in order to empty the error stack - * @param string level name, to return only errors of a particular severity - * @return array - */ - function getErrors($purge = false, $level = false) - { - if (!$purge) { - if ($level) { - if (!isset($this->_errorsByLevel[$level])) { - return array(); - } else { - return $this->_errorsByLevel[$level]; - } - } else { - return $this->_errors; - } - } - if ($level) { - $ret = $this->_errorsByLevel[$level]; - foreach ($this->_errorsByLevel[$level] as $i => $unused) { - // entries are references to the $_errors array - $this->_errorsByLevel[$level][$i] = false; - } - // array_filter removes all entries === false - $this->_errors = array_filter($this->_errors); - unset($this->_errorsByLevel[$level]); - return $ret; - } - $ret = $this->_errors; - $this->_errors = array(); - $this->_errorsByLevel = array(); - return $ret; - } - - /** - * Determine whether there are any errors on a single error stack, or on any error stack - * - * The optional parameter can be used to test the existence of any errors without the need of - * singleton instantiation - * @param string|false Package name to check for errors - * @param string Level name to check for a particular severity - * @return boolean - * @static - */ - function staticHasErrors($package = false, $level = false) - { - if ($package) { - if (!isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) { - return false; - } - return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->hasErrors($level); - } - foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) { - if ($obj->hasErrors($level)) { - return true; - } - } - return false; - } - - /** - * Get a list of all errors since last purge, organized by package - * @since PEAR 1.4.0dev BC break! $level is now in the place $merge used to be - * @param boolean $purge Set to purge the error stack of existing errors - * @param string $level Set to a level name in order to retrieve only errors of a particular level - * @param boolean $merge Set to return a flat array, not organized by package - * @param array $sortfunc Function used to sort a merged array - default - * sorts by time, and should be good for most cases - * @static - * @return array - */ - function staticGetErrors($purge = false, $level = false, $merge = false, - $sortfunc = array('PEAR_ErrorStack', '_sortErrors')) - { - $ret = array(); - if (!is_callable($sortfunc)) { - $sortfunc = array('PEAR_ErrorStack', '_sortErrors'); - } - foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) { - $test = $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->getErrors($purge, $level); - if ($test) { - if ($merge) { - $ret = array_merge($ret, $test); - } else { - $ret[$package] = $test; - } - } - } - if ($merge) { - usort($ret, $sortfunc); - } - return $ret; - } - - /** - * Error sorting function, sorts by time - * @access private - */ - function _sortErrors($a, $b) - { - if ($a['time'] == $b['time']) { - return 0; - } - if ($a['time'] < $b['time']) { - return 1; - } - return -1; - } - - /** - * Standard file/line number/function/class context callback - * - * This function uses a backtrace generated from {@link debug_backtrace()} - * and so will not work at all in PHP < 4.3.0. The frame should - * reference the frame that contains the source of the error. - * @return array|false either array('file' => file, 'line' => line, - * 'function' => function name, 'class' => class name) or - * if this doesn't work, then false - * @param unused - * @param integer backtrace frame. - * @param array Results of debug_backtrace() - * @static - */ - function getFileLine($code, $params, $backtrace = null) - { - if ($backtrace === null) { - return false; - } - $frame = 0; - $functionframe = 1; - if (!isset($backtrace[1])) { - $functionframe = 0; - } else { - while (isset($backtrace[$functionframe]['function']) && - $backtrace[$functionframe]['function'] == 'eval' && - isset($backtrace[$functionframe + 1])) { - $functionframe++; - } - } - if (isset($backtrace[$frame])) { - if (!isset($backtrace[$frame]['file'])) { - $frame++; - } - $funcbacktrace = $backtrace[$functionframe]; - $filebacktrace = $backtrace[$frame]; - $ret = array('file' => $filebacktrace['file'], - 'line' => $filebacktrace['line']); - // rearrange for eval'd code or create function errors - if (strpos($filebacktrace['file'], '(') && - preg_match(';^(.*?)\((\d+)\) : (.*?)\\z;', $filebacktrace['file'], - $matches)) { - $ret['file'] = $matches[1]; - $ret['line'] = $matches[2] + 0; - } - if (isset($funcbacktrace['function']) && isset($backtrace[1])) { - if ($funcbacktrace['function'] != 'eval') { - if ($funcbacktrace['function'] == '__lambda_func') { - $ret['function'] = 'create_function() code'; - } else { - $ret['function'] = $funcbacktrace['function']; - } - } - } - if (isset($funcbacktrace['class']) && isset($backtrace[1])) { - $ret['class'] = $funcbacktrace['class']; - } - return $ret; - } - return false; - } - - /** - * Standard error message generation callback - * - * This method may also be called by a custom error message generator - * to fill in template values from the params array, simply - * set the third parameter to the error message template string to use - * - * The special variable %__msg% is reserved: use it only to specify - * where a message passed in by the user should be placed in the template, - * like so: - * - * Error message: %msg% - internal error - * - * If the message passed like so: - * - * - * $stack->push(ERROR_CODE, 'error', array(), 'server error 500'); - * - * - * The returned error message will be "Error message: server error 500 - - * internal error" - * @param PEAR_ErrorStack - * @param array - * @param string|false Pre-generated error message template - * @static - * @return string - */ - function getErrorMessage(&$stack, $err, $template = false) - { - if ($template) { - $mainmsg = $template; - } else { - $mainmsg = $stack->getErrorMessageTemplate($err['code']); - } - $mainmsg = str_replace('%__msg%', $err['message'], $mainmsg); - if (is_array($err['params']) && count($err['params'])) { - foreach ($err['params'] as $name => $val) { - if (is_array($val)) { - // @ is needed in case $val is a multi-dimensional array - $val = @implode(', ', $val); - } - if (is_object($val)) { - if (method_exists($val, '__toString')) { - $val = $val->__toString(); - } else { - PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_OBJTOSTRING, - 'warning', array('obj' => get_class($val)), - 'object %obj% passed into getErrorMessage, but has no __toString() method'); - $val = 'Object'; - } - } - $mainmsg = str_replace('%' . $name . '%', $val, $mainmsg); - } - } - return $mainmsg; - } - - /** - * Standard Error Message Template generator from code - * @return string - */ - function getErrorMessageTemplate($code) - { - if (!isset($this->_errorMsgs[$code])) { - return '%__msg%'; - } - return $this->_errorMsgs[$code]; - } - - /** - * Set the Error Message Template array - * - * The array format must be: - *
    -     * array(error code => 'message template',...)
    -     * 
    - * - * Error message parameters passed into {@link push()} will be used as input - * for the error message. If the template is 'message %foo% was %bar%', and the - * parameters are array('foo' => 'one', 'bar' => 'six'), the error message returned will - * be 'message one was six' - * @return string - */ - function setErrorMessageTemplate($template) - { - $this->_errorMsgs = $template; - } - - - /** - * emulate PEAR::raiseError() - * - * @return PEAR_Error - */ - function raiseError() - { - require_once 'PEAR.php'; - $args = func_get_args(); - return call_user_func_array(array('PEAR', 'raiseError'), $args); - } -} -$stack = &PEAR_ErrorStack::singleton('PEAR_ErrorStack'); -$stack->pushCallback(array('PEAR_ErrorStack', '_handleError')); -?> diff --git a/pear/PEAR/ErrorStack5.php b/pear/PEAR/ErrorStack5.php deleted file mode 100644 index 52714d5..0000000 --- a/pear/PEAR/ErrorStack5.php +++ /dev/null @@ -1,921 +0,0 @@ - - * @version 0.1alpha (E_STRICT) - * @package PEAR_ErrorStack - * @category Debugging - * @license http://opensource.org/licenses/bsd-license.php New BSD License - */ - -/** - * PEAR_Exception is used by default - */ -require_once 'PEAR/Exception.php'; - -class PEAR_ErrorStack_Exception extends PEAR_Exception{} - -/**#@+ - * One of four possible return values from the error Callback - * @see PEAR_ErrorStack::_errorCallback() - */ -/** - * If this is returned, then the error will be both pushed onto the stack - * and logged. - */ -define('PEAR_ERRORSTACK_PUSHANDLOG', 1); -/** - * If this is returned, then the error will only be pushed onto the stack, - * and not logged. - */ -define('PEAR_ERRORSTACK_PUSH', 2); -/** - * If this is returned, then the error will only be logged, but not pushed - * onto the error stack. - */ -define('PEAR_ERRORSTACK_LOG', 3); -/** - * If this is returned, then the error is completely ignored. - */ -define('PEAR_ERRORSTACK_IGNORE', 4); -/**#@-*/ - -/** - * Error code for an attempt to instantiate a non-class as a PEAR_ErrorStack in - * the singleton method. - */ -define('PEAR_ERRORSTACK_ERR_NONCLASS', 1); - -/** - * Error code for an attempt to pass an object into {@link PEAR_ErrorStack::getMessage()} - * that has no __toString() method - */ -define('PEAR_ERRORSTACK_ERR_OBJTOSTRING', 2); -/** - * Error Stack Implementation - * - * Usage: - * - * // global error stack - * $global_stack = &PEAR_ErrorStack::singleton('MyPackage'); - * // local error stack - * $local_stack = new PEAR_ErrorStack('MyPackage'); - * - * @copyright 2004 Gregory Beaver - * @package PEAR_ErrorStack - * @license http://opensource.org/licenses/bsd-license.php New BSD License - */ -class PEAR_ErrorStack { - /** - * Singleton storage - * - * Format: - *
    -     * array(
    -     *  'package1' => PEAR_ErrorStack object,
    -     *  'package2' => PEAR_ErrorStack object,
    -     *  ...
    -     * )
    -     * 
    - */ - static protected $singleton; - - /** - * Global error callback (default) - * - * This is only used if set to non-false. * is the default callback for - * all packages, whereas specific packages may set a default callback - * for all instances, regardless of whether they are a singleton or not. - * - * To exclude non-singletons, only set the local callback for the singleton - * @see PEAR_ErrorStack::setDefaultCallback() - */ - static protected $globalcallback = - array( - '*' => false, - ); - - /** - * Global Log object (default) - * - * This is only used if set to non-false. Use to set a default log object for - * all stacks, regardless of instantiation order or location - * @see PEAR_ErrorStack::setDefaultLogger() - */ - static protected $globallogger = false; - - /** - * PEAR_Warning callback - * - * This is only used if set to non-false. Use to set a default log object for - * all stacks, regardless of instantiation order or location - * @see PEAR_ErrorStack::setDefaultLogger() - */ - static protected $pearwarningcallback = false; - - /** - * Global Overriding Callback - * - * This callback will override any error callbacks that specific loggers have set. - * Use with EXTREME caution - * @see PEAR_ErrorStack::staticPushCallback() - * @access private - * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] - */ - static protected $overridecallback = array(); - - /** - * Errors are stored in the order that they are pushed on the stack. - * @since 0.4alpha Errors are no longer organized by error level. - * This renders pop() nearly unusable, and levels could be more easily - * handled in a callback anyway - * @var array - */ - private $_errors = array(); - - /** - * Storage of errors by level. - * - * Allows easy retrieval and deletion of only errors from a particular level - * @since PEAR 1.4.0dev - * @var array - */ - private $_errorsByLevel = array(); - - /** - * Package name this error stack represents - * @var string - */ - protected $_package; - - /** - * Determines whether a PEAR_Error is thrown upon every error addition - * @var boolean - */ - private $_compat = false; - - /** - * If set to a valid callback, this will be used to generate the error - * message from the error code, otherwise the message passed in will be - * used - * @var false|string|array - */ - private $_msgCallback = false; - - /** - * If set to a valid callback, this will be used to generate the error - * context for an error. For PHP-related errors, this will be a file - * and line number as retrieved from debug_backtrace(), but can be - * customized for other purposes. The error might actually be in a separate - * configuration file, or in a database query. - * @var false|string|array - */ - public $contextCallback = false; - - /** - * If set to a valid callback, this will be called every time an error - * is pushed onto the stack. The return value will be used to determine - * whether to allow an error to be pushed or logged. - * - * The return value must be one an PEAR_ERRORSTACK_* constant - * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG - * @var false|string|array - */ - protected $_errorCallback = array(); - - /** - * PEAR::Log object for logging errors - * @var false|Log - */ - protected $_logger = false; - - /** - * Error messages - designed to be overridden - * @var array - * @abstract - */ - protected $_errorMsgs = array(); - - /** - * Set up a new error stack - * - * @param string $package name of the package this error stack represents - * @param callback $msgCallback callback used for error message generation - * @param callback $contextCallback callback used for context generation, - * defaults to {@link getFileLine()} - * @param boolean $throwPEAR_Error (ignored) - */ - public function PEAR_ErrorStack($package, $msgCallback = false, $contextCallback = false, - $throwPEAR_Error = false) - { - $this->_package = $package; - $this->setMessageCallback($msgCallback); - $this->setContextCallback($contextCallback); - $this->_compat = false; - } - - /** - * Return a single error stack for this package. - * - * Note that all parameters are ignored if the stack for package $package - * has already been instantiated - * @param string $package name of the package this error stack represents - * @param callback $msgCallback callback used for error message generation - * @param callback $contextCallback callback used for context generation, - * defaults to {@link getFileLine()} - * @param boolean $throwPEAR_Error - * @param string $exceptionClass exception class to instantiate if - * in PHP 5 - * @param string $stackClass class to instantiate - * @static - * @return PEAR_ErrorStack - */ - static public function singleton($package, $msgCallback = false, $contextCallback = false, - $throwPEAR_Error = false, $stackClass = 'PEAR_ErrorStack') - { - if (isset(self::$singleton[$package])) { - return self::$singleton[$package]; - } - if (!class_exists($stackClass)) { - throw PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_NONCLASS, - 'exception', array('stackclass' => $stackClass), - 'stack class "%stackclass%" is not a valid class name (should be like PEAR_ErrorStack)', - false, $trace); - } - return self::$singleton[$package] = - new $stackClass($package, $msgCallback, $contextCallback, $throwPEAR_Error); - } - - - /** - * Set up a PEAR::Log object for all error stacks that don't have one - * @param Log $log - * @static - */ - static public function setDefaultLogger($log) - { - self::$globallogger = $log; - } - - /** - * Set up a PEAR::Log object for this error stack - * @param Log $log - */ - public function setLogger($log) - { - $this->_logger = $log; - } - - /** - * Set an error code => error message mapping callback - * - * This method sets the callback that can be used to generate error - * messages for any instance - * @param array|string Callback function/method - */ - public function setMessageCallback($msgCallback) - { - if (!$msgCallback) { - $this->_msgCallback = array($this, 'getErrorMessage'); - } else { - if (is_callable($msgCallback)) { - $this->_msgCallback = $msgCallback; - } - } - } - - /** - * Get an error code => error message mapping callback - * - * This method returns the current callback that can be used to generate error - * messages - * @return array|string|false Callback function/method or false if none - */ - public function getMessageCallback() - { - return $this->_msgCallback; - } - - /** - * Sets a default callback to be used by all error stacks - * - * This method sets the callback that can be used to generate error - * messages for a singleton - * @param array|string Callback function/method - * @param string Package name, or false for all packages - */ - static public function setDefaultCallback($callback = false, $package = false) - { - if (!is_callable($callback)) { - $callback = false; - } - $package = $package ? $package : '*'; - self::$globalcallback[$package] = $callback; - } - - /** - * Do not use - * @access private - */ - static public function setPEARWarningCallback($callback) - { - self::$pearwarningcallback = $callback; - } - - /** - * Set an error code => error message mapping callback - * - * This method sets the callback that can be used to generate context - * information for an error. Passing in NULL will disable context generation - * and remove the expensive call to debug_backtrace() - * @param array|string Callback function/method - */ - public function setContextCallback($contextCallback) - { - if ($contextCallback === null) { - return $this->contextCallback = false; - } - if (!$contextCallback) { - $this->contextCallback = array($this, 'getFileLine'); - } else { - if (is_callable($contextCallback)) { - $this->contextCallback = $contextCallback; - } - } - } - - /** - * Set an error Callback - * If set to a valid callback, this will be called every time an error - * is pushed onto the stack. The return value will be used to determine - * whether to allow an error to be pushed or logged. - * - * The return value must be one of the ERRORSTACK_* constants. - * - * This functionality can be used to emulate PEAR's pushErrorHandling, and - * the PEAR_ERROR_CALLBACK mode, without affecting the integrity of - * the error stack or logging - * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG - * @see popCallback() - * @param string|array $cb - */ - public function pushCallback($cb) - { - array_push($this->_errorCallback, $cb); - } - - /** - * Remove a callback from the error callback stack - * @see pushCallback() - * @return array|string|false - */ - public function popCallback() - { - if (!count($this->_errorCallback)) { - return false; - } - return array_pop($this->_errorCallback); - } - - /** - * Set a temporary overriding error callback for every package error stack - * - * Use this to temporarily disable all existing callbacks (can be used - * to emulate the @ operator, for instance) - * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG - * @see staticPopCallback(), pushCallback() - * @param string|array $cb - */ - static public function staticPushCallback($cb) - { - array_push(self::$overridecallback, $cb); - } - - /** - * Remove a temporary overriding error callback - * @see staticPushCallback() - * @return array|string|false - */ - static public function staticPopCallback() - { - $ret = array_pop(self::$overridecallback); - if (!is_array(self::$overridecallback)) { - self::$overridecallback = array(); - } - return $ret; - } - - /** - * demote an exception to a non-fatal error (default is warning) - * @param Exception - * @param string - */ - public function demoteException($e, $level = 'warning') - { - $this->push(get_class($e), $level, array(), - $e->getMessage(), $e, $e->getTrace()); - } - - /** - * promote a warning into a PEAR_Exception - * - * It is probably best to do this manually, to take advantage of the - * use of exception classnames to categorize errors - * @return PEAR_Exception - */ - public function promoteWarning($warning, $exceptionclass = 'PEAR_Exception') - { - return new $exceptionclass($warning['message'], - array($warning), $warning['code']); - } - - /** - * Add an error to the stack - * - * If the message generator exists, it is called with 2 parameters. - * - the current Error Stack object - * - an array that is in the same format as an error. Available indices - * are 'code', 'package', 'time', 'params', 'level', and 'context' - * - * Next, if the error should contain context information, this is - * handled by the context grabbing method. - * Finally, the error is pushed onto the proper error stack - * @param int $code Package-specific error code - * @param string $level Error level. This is NOT spell-checked - * @param array $params associative array of error parameters - * @param string $msg Error message, or a portion of it if the message - * is to be generated - * @param array $repackage If this error re-packages an error pushed by - * another package, place the array returned from - * {@link pop()} in this parameter - * @param array $backtrace Protected parameter: use this to pass in the - * {@link debug_backtrace()} that should be used - * to find error context - * @return PEAR_Error|array|Exception - * if compatibility mode is on, a PEAR_Error is also - * thrown. If the class Exception exists, then one - * is returned to allow code like: - * - * throw ($stack->push(MY_ERROR_CODE, 'error', array('username' => 'grob'))); - * - * - * The errorData property of the exception class will be set to the array - * that would normally be returned. If a PEAR_Error is returned, the userinfo - * property is set to the array - * - * Otherwise, an array is returned in this format: - * - * array( - * 'code' => $code, - * 'params' => $params, - * 'package' => $this->_package, - * 'level' => $level, - * 'time' => time(), - * 'context' => $context, - * 'message' => $msg, - * //['repackage' => $err] repackaged error array - * ); - * - */ - public function push($code, $level = 'error', $params = array(), $msg = false, - $repackage = false, $backtrace = false) - { - $context = false; - // grab error context - if ($this->contextCallback) { - if (!$backtrace) { - $backtrace = debug_backtrace(); - } - $context = call_user_func($this->contextCallback, $code, $params, $backtrace); - } - - // save error - $time = explode(' ', microtime()); - $time = $time[1] + $time[0]; - $err = array( - 'code' => $code, - 'params' => $params, - 'package' => $this->_package, - 'level' => $level, - 'time' => $time, - 'context' => $context, - 'message' => $msg, - ); - - // set up the error message, if necessary - if ($this->_msgCallback) { - $msg = call_user_func_array($this->_msgCallback, - array($this, $err)); - $err['message'] = $msg; - } - - - if ($repackage) { - $err['repackage'] = $repackage; - } - $push = $log = true; - // try the overriding callback first - $callback = $this->staticPopCallback(); - if ($callback) { - $this->staticPushCallback($callback); - } - if (!is_callable($callback)) { - // try the local callback next - $callback = $this->popCallback(); - if (is_callable($callback)) { - $this->pushCallback($callback); - } else { - // try the default callback - $callback = isset(self::$globalcallback[$this->_package]) ? - self::$globalcallback[$this->_package] : - self::$globalcallback['*']; - } - } - if (is_callable($callback)) { - switch(call_user_func($callback, $err)){ - case PEAR_ERRORSTACK_IGNORE: - return $err; - break; - case PEAR_ERRORSTACK_PUSH: - $log = false; - break; - case PEAR_ERRORSTACK_LOG: - $push = false; - break; - // anything else returned has the same effect as pushandlog - } - } - if ($push) { - if (is_callable(self::$pearwarningcallback)) { - call_user_func(self::$pearwarningcallback, $err); - } - array_unshift($this->_errors, $err); - $this->_errorsByLevel[$err['level']][] = &$this->_errors[0]; - } - if ($log) { - if ($this->_logger || self::$globallogger) { - $this->_log($err); - } - } - return $err; - } - - /** - * Static version of {@link push()} - * - * @param string $package Package name this error belongs to - * @param int $code Package-specific error code - * @param string $level Error level. This is NOT spell-checked - * @param array $params associative array of error parameters - * @param string $msg Error message, or a portion of it if the message - * is to be generated - * @param array $repackage If this error re-packages an error pushed by - * another package, place the array returned from - * {@link pop()} in this parameter - * @param array $backtrace Protected parameter: use this to pass in the - * {@link debug_backtrace()} that should be used - * to find error context - * @return PEAR_Error|null|Exception - * if compatibility mode is on, a PEAR_Error is also - * thrown. If the class Exception exists, then one - * is returned to allow code like: - * - * throw ($stack->push(MY_ERROR_CODE, 'error', array('username' => 'grob'))); - * - * @static - */ - static public function staticPush($package, $code, $level = 'error', $params = array(), - $msg = false, $repackage = false, $backtrace = false) - { - $s = PEAR_ErrorStack::singleton($package); - if ($s->contextCallback) { - if (!$backtrace) { - $backtrace = debug_backtrace(); - } - } - return $s->push($code, $level, $params, $msg, $repackage, $backtrace); - } - - /** - * Log an error using PEAR::Log - * @param array $err Error array - * @param array $levels Error level => Log constant map - * @access protected - */ - protected function _log($err, $levels = array( - 'exception' => PEAR_LOG_CRIT, - 'alert' => PEAR_LOG_ALERT, - 'critical' => PEAR_LOG_CRIT, - 'error' => PEAR_LOG_ERR, - 'warning' => PEAR_LOG_WARNING, - 'notice' => PEAR_LOG_NOTICE, - 'info' => PEAR_LOG_INFO, - 'debug' => PEAR_LOG_DEBUG)) - { - if (isset($levels[$err['level']])) { - $level = $levels[$err['level']]; - } else { - $level = PEAR_LOG_INFO; - } - if ($this->_logger) { - $this->_logger->log($err['message'], $level, $err); - } else { - self::$globallogger->log($err['message'], $level, $err); - } - } - - - /** - * Pop an error off of the error stack - * - * @return false|array - * @since 0.4alpha it is no longer possible to specify a specific error - * level to return - the last error pushed will be returned, instead - */ - public function pop() - { - return @array_shift($this->_errors); - } - - /** - * Determine whether there are any errors on the stack - * @param string|array Level name. Use to determine if any errors - * of level (string), or levels (array) have been pushed - * @return boolean - */ - public function hasErrors($level = false) - { - if ($level) { - return isset($this->_errorsByLevel[$level]); - } - return count($this->_errors); - } - - /** - * Retrieve all errors since last purge - * - * @param boolean set in order to empty the error stack - * @param string level name, to return only errors of a particular severity - * @return array - */ - public function getErrors($purge = false, $level = false) - { - if (!$purge) { - if ($level) { - if (!isset($this->_errorsByLevel[$level])) { - return array(); - } else { - return $this->_errorsByLevel[$level]; - } - } else { - return $this->_errors; - } - } - if ($level) { - $ret = $this->_errorsByLevel[$level]; - foreach ($this->_errorsByLevel[$level] as $i => $unused) { - // entries are references to the $_errors array - $this->_errorsByLevel[$level][$i] = false; - } - // array_filter removes all entries === false - $this->_errors = array_filter($this->_errors); - unset($this->_errorsByLevel[$level]); - return $ret; - } - $ret = $this->_errors; - $this->_errors = array(); - $this->_errorsByLevel = array(); - return $ret; - } - - /** - * Determine whether there are any errors on a single error stack, or on any error stack - * - * The optional parameter can be used to test the existence of any errors without the need of - * singleton instantiation - * @param string|false Package name to check for errors - * @param string Level name to check for a particular severity - * @return boolean - */ - static public function staticHasErrors($package = false, $level = false) - { - if ($package) { - if (!isset(self::$singleton[$package])) { - return false; - } - return self::$singleton[$package]->hasErrors($level); - } - foreach (self::$singleton as $package => $obj) { - if ($obj->hasErrors($level)) { - return true; - } - } - return false; - } - - /** - * Get a list of all errors since last purge, organized by package - * @since PEAR 1.4.0dev BC break! $level is now in the place $merge used to be - * @param boolean $clearStack Set to purge the error stack of existing errors - * @param string $level Set to a level name in order to retrieve only errors of a particular level - * @param boolean $merge Set to return a flat array, not organized by package - * @param array $sortfunc Function used to sort a merged array - default - * sorts by time, and should be good for most cases - * @return array - */ - static public function staticGetErrors($purge = false, $level = false, $merge = false, $sortfunc = array('PEAR_ErrorStack', '_sortErrors')) - { - $ret = array(); - if (!is_callable($sortfunc)) { - $sortfunc = array('PEAR_ErrorStack', '_sortErrors'); - } - foreach (self::$singleton as $package => $obj) { - $test = self::$singleton[$package]->getErrors($purge, $level); - if ($test) { - if ($merge) { - $ret = array_merge($ret, $test); - } else { - $ret[$package] = $test; - } - } - } - if ($merge) { - usort($ret, $sortfunc); - } - return $ret; - } - - /** - * Error sorting function, sorts by time - * @access private - */ - function _sortErrors($a, $b) - { - if ($a['time'] == $b['time']) { - return 0; - } - if ($a['time'] < $b['time']) { - return 1; - } - return -1; - } - - /** - * Standard file/line number/function/class context callback - * - * This function uses a backtrace generated from {@link debug_backtrace()} - * and so will not work at all in PHP < 4.3.0. The frame should - * reference the frame that contains the source of the error. - * @return array|false either array('file' => file, 'line' => line, - * 'function' => function name, 'class' => class name) or - * if this doesn't work, then false - * @param unused - * @param integer backtrace frame. - * @param array Results of debug_backtrace() - */ - static public function getFileLine($code, $params, $backtrace = null) - { - if ($backtrace === null) { - return false; - } - $frame = 0; - $functionframe = 1; - if (!isset($backtrace[1])) { - $functionframe = 0; - } else { - while (isset($backtrace[$functionframe]['function']) && - $backtrace[$functionframe]['function'] == 'eval' && - isset($backtrace[$functionframe + 1])) { - $functionframe++; - } - } - if (isset($backtrace[$frame])) { - if (!isset($backtrace[$frame]['file'])) { - $frame++; - } - $funcbacktrace = $backtrace[$functionframe]; - $filebacktrace = $backtrace[$frame]; - $ret = array('file' => $filebacktrace['file'], - 'line' => $filebacktrace['line']); - // rearrange for eval'd code or create function errors - if (strpos($filebacktrace['file'], '(') && - preg_match(';^(.*?)\((\d+)\) : (.*?)\\z;', $filebacktrace['file'], - $matches)) { - $ret['file'] = $matches[1]; - $ret['line'] = $matches[2] + 0; - } - if (isset($funcbacktrace['function']) && isset($backtrace[1])) { - if ($funcbacktrace['function'] != 'eval') { - if ($funcbacktrace['function'] == '__lambda_func') { - $ret['function'] = 'create_function() code'; - } else { - $ret['function'] = $funcbacktrace['function']; - } - } - } - if (isset($funcbacktrace['class']) && isset($backtrace[1])) { - $ret['class'] = $funcbacktrace['class']; - } - return $ret; - } - return false; - } - - /** - * Standard error message generation callback - * - * This method may also be called by a custom error message generator - * to fill in template values from the params array, simply - * set the third parameter to the error message template string to use - * - * The special variable %__msg% is reserved: use it only to specify - * where a message passed in by the user should be placed in the template, - * like so: - * - * Error message: %msg% - internal error - * - * If the message passed like so: - * - * - * $stack->push(ERROR_CODE, 'error', array(), 'server error 500'); - * - * - * The returned error message will be "Error message: server error 500 - - * internal error" - * @param PEAR_ErrorStack - * @param array - * @param string|false Pre-generated error message template - * @static - * @return string - */ - public function getErrorMessage(&$stack, $err, $template = false) - { - if ($template) { - $mainmsg = $template; - } else { - $mainmsg = $stack->getErrorMessageTemplate($err['code']); - } - $mainmsg = str_replace('%__msg%', $err['message'], $mainmsg); - if (count($err['params'])) { - foreach ($err['params'] as $name => $val) { - if (is_array($val)) { - // @ is needed in case $val is a multi-dimensional array - $val = @implode(', ', $val); - } - if (is_object($val)) { - if (method_exists($val, '__toString')) { - $val = $val->__toString(); - } else { - throw PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_OBJTOSTRING, - 'warning', array('obj' => get_class($val)), - 'object %obj% passed into getErrorMessage, but has no __toString() method'); - $val = 'Object'; - } - } - $mainmsg = str_replace('%' . $name . '%', $val, $mainmsg); - } - } - return $mainmsg; - } - - /** - * Standard Error Message Template generator from code - * @return string - */ - public function getErrorMessageTemplate($code) - { - if (!isset($this->_errorMsgs[$code])) { - return '%__msg%'; - } - return $this->_errorMsgs[$code]; - } - - /** - * Set the Error Message Template array - * - * The array format must be: - *
    -     * array(error code => 'message template',...)
    -     * 
    - * - * Error message parameters passed into {@link push()} will be used as input - * for the error message. If the template is 'message %foo% was %bar%', and the - * parameters are array('foo' => 'one', 'bar' => 'six'), the error message returned will - * be 'message one was six' - * @return string - */ - public function setErrorMessageTemplate($template) - { - $this->_errorMsgs = $template; - } -} -PEAR_ErrorStack::singleton('PEAR_ErrorStack', false, null, false, 'PEAR_ErrorStack_Exception'); -?> \ No newline at end of file diff --git a/pear/PEAR/Exception.php b/pear/PEAR/Exception.php deleted file mode 100644 index 85eaf2c..0000000 --- a/pear/PEAR/Exception.php +++ /dev/null @@ -1,389 +0,0 @@ - - * @author Hans Lellelid - * @author Bertrand Mansion - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR_Exception - * @since File available since Release 1.0.0 - */ - - -/** - * Base PEAR_Exception Class - * - * 1) Features: - * - * - Nestable exceptions (throw new PEAR_Exception($msg, $prev_exception)) - * - Definable triggers, shot when exceptions occur - * - Pretty and informative error messages - * - Added more context info available (like class, method or cause) - * - cause can be a PEAR_Exception or an array of mixed - * PEAR_Exceptions/PEAR_ErrorStack warnings - * - callbacks for specific exception classes and their children - * - * 2) Ideas: - * - * - Maybe a way to define a 'template' for the output - * - * 3) Inherited properties from PHP Exception Class: - * - * protected $message - * protected $code - * protected $line - * protected $file - * private $trace - * - * 4) Inherited methods from PHP Exception Class: - * - * __clone - * __construct - * getMessage - * getCode - * getFile - * getLine - * getTraceSafe - * getTraceSafeAsString - * __toString - * - * 5) Usage example - * - * - * require_once 'PEAR/Exception.php'; - * - * class Test { - * function foo() { - * throw new PEAR_Exception('Error Message', ERROR_CODE); - * } - * } - * - * function myLogger($pear_exception) { - * echo $pear_exception->getMessage(); - * } - * // each time a exception is thrown the 'myLogger' will be called - * // (its use is completely optional) - * PEAR_Exception::addObserver('myLogger'); - * $test = new Test; - * try { - * $test->foo(); - * } catch (PEAR_Exception $e) { - * print $e; - * } - * - * - * @category pear - * @package PEAR_Exception - * @author Tomas V.V.Cox - * @author Hans Lellelid - * @author Bertrand Mansion - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR_Exception - * @since Class available since Release 1.0.0 - * - */ -class PEAR_Exception extends Exception -{ - const OBSERVER_PRINT = -2; - const OBSERVER_TRIGGER = -4; - const OBSERVER_DIE = -8; - protected $cause; - private static $_observers = array(); - private static $_uniqueid = 0; - private $_trace; - - /** - * Supported signatures: - * - PEAR_Exception(string $message); - * - PEAR_Exception(string $message, int $code); - * - PEAR_Exception(string $message, Exception $cause); - * - PEAR_Exception(string $message, Exception $cause, int $code); - * - PEAR_Exception(string $message, PEAR_Error $cause); - * - PEAR_Exception(string $message, PEAR_Error $cause, int $code); - * - PEAR_Exception(string $message, array $causes); - * - PEAR_Exception(string $message, array $causes, int $code); - * @param string exception message - * @param int|Exception|PEAR_Error|array|null exception cause - * @param int|null exception code or null - */ - public function __construct($message, $p2 = null, $p3 = null) - { - if (is_int($p2)) { - $code = $p2; - $this->cause = null; - } elseif (is_object($p2) || is_array($p2)) { - // using is_object allows both Exception and PEAR_Error - if (is_object($p2) && !($p2 instanceof Exception)) { - if (!class_exists('PEAR_Error') || !($p2 instanceof PEAR_Error)) { - throw new PEAR_Exception('exception cause must be Exception, ' . - 'array, or PEAR_Error'); - } - } - $code = $p3; - if (is_array($p2) && isset($p2['message'])) { - // fix potential problem of passing in a single warning - $p2 = array($p2); - } - $this->cause = $p2; - } else { - $code = null; - $this->cause = null; - } - parent::__construct($message, $code); - $this->signal(); - } - - /** - * @param mixed $callback - A valid php callback, see php func is_callable() - * - A PEAR_Exception::OBSERVER_* constant - * - An array(const PEAR_Exception::OBSERVER_*, - * mixed $options) - * @param string $label The name of the observer. Use this if you want - * to remove it later with removeObserver() - */ - public static function addObserver($callback, $label = 'default') - { - self::$_observers[$label] = $callback; - } - - public static function removeObserver($label = 'default') - { - unset(self::$_observers[$label]); - } - - /** - * @return int unique identifier for an observer - */ - public static function getUniqueId() - { - return self::$_uniqueid++; - } - - private function signal() - { - foreach (self::$_observers as $func) { - if (is_callable($func)) { - call_user_func($func, $this); - continue; - } - settype($func, 'array'); - switch ($func[0]) { - case self::OBSERVER_PRINT : - $f = (isset($func[1])) ? $func[1] : '%s'; - printf($f, $this->getMessage()); - break; - case self::OBSERVER_TRIGGER : - $f = (isset($func[1])) ? $func[1] : E_USER_NOTICE; - trigger_error($this->getMessage(), $f); - break; - case self::OBSERVER_DIE : - $f = (isset($func[1])) ? $func[1] : '%s'; - die(printf($f, $this->getMessage())); - break; - default: - trigger_error('invalid observer type', E_USER_WARNING); - } - } - } - - /** - * Return specific error information that can be used for more detailed - * error messages or translation. - * - * This method may be overridden in child exception classes in order - * to add functionality not present in PEAR_Exception and is a placeholder - * to define API - * - * The returned array must be an associative array of parameter => value like so: - *
    -     * array('name' => $name, 'context' => array(...))
    -     * 
    - * @return array - */ - public function getErrorData() - { - return array(); - } - - /** - * Returns the exception that caused this exception to be thrown - * @access public - * @return Exception|array The context of the exception - */ - public function getCause() - { - return $this->cause; - } - - /** - * Function must be public to call on caused exceptions - * @param array - */ - public function getCauseMessage(&$causes) - { - $trace = $this->getTraceSafe(); - $cause = array('class' => get_class($this), - 'message' => $this->message, - 'file' => 'unknown', - 'line' => 'unknown'); - if (isset($trace[0])) { - if (isset($trace[0]['file'])) { - $cause['file'] = $trace[0]['file']; - $cause['line'] = $trace[0]['line']; - } - } - $causes[] = $cause; - if ($this->cause instanceof PEAR_Exception) { - $this->cause->getCauseMessage($causes); - } elseif ($this->cause instanceof Exception) { - $causes[] = array('class' => get_class($this->cause), - 'message' => $this->cause->getMessage(), - 'file' => $this->cause->getFile(), - 'line' => $this->cause->getLine()); - } elseif (class_exists('PEAR_Error') && $this->cause instanceof PEAR_Error) { - $causes[] = array('class' => get_class($this->cause), - 'message' => $this->cause->getMessage(), - 'file' => 'unknown', - 'line' => 'unknown'); - } elseif (is_array($this->cause)) { - foreach ($this->cause as $cause) { - if ($cause instanceof PEAR_Exception) { - $cause->getCauseMessage($causes); - } elseif ($cause instanceof Exception) { - $causes[] = array('class' => get_class($cause), - 'message' => $cause->getMessage(), - 'file' => $cause->getFile(), - 'line' => $cause->getLine()); - } elseif (class_exists('PEAR_Error') && $cause instanceof PEAR_Error) { - $causes[] = array('class' => get_class($cause), - 'message' => $cause->getMessage(), - 'file' => 'unknown', - 'line' => 'unknown'); - } elseif (is_array($cause) && isset($cause['message'])) { - // PEAR_ErrorStack warning - $causes[] = array( - 'class' => $cause['package'], - 'message' => $cause['message'], - 'file' => isset($cause['context']['file']) ? - $cause['context']['file'] : - 'unknown', - 'line' => isset($cause['context']['line']) ? - $cause['context']['line'] : - 'unknown', - ); - } - } - } - } - - public function getTraceSafe() - { - if (!isset($this->_trace)) { - $this->_trace = $this->getTrace(); - if (empty($this->_trace)) { - $backtrace = debug_backtrace(); - $this->_trace = array($backtrace[count($backtrace)-1]); - } - } - return $this->_trace; - } - - public function getErrorClass() - { - $trace = $this->getTraceSafe(); - return $trace[0]['class']; - } - - public function getErrorMethod() - { - $trace = $this->getTraceSafe(); - return $trace[0]['function']; - } - - public function __toString() - { - if (isset($_SERVER['REQUEST_URI'])) { - return $this->toHtml(); - } - return $this->toText(); - } - - public function toHtml() - { - $trace = $this->getTraceSafe(); - $causes = array(); - $this->getCauseMessage($causes); - $html = '' . "\n"; - foreach ($causes as $i => $cause) { - $html .= '\n"; - } - $html .= '' . "\n" - . '' - . '' - . '' . "\n"; - - foreach ($trace as $k => $v) { - $html .= '' - . '' - . '' . "\n"; - } - $html .= '' - . '' - . '' . "\n" - . '
    ' - . str_repeat('-', $i) . ' ' . $cause['class'] . ': ' - . htmlspecialchars($cause['message']) . ' in ' . $cause['file'] . ' ' - . 'on line ' . $cause['line'] . '' - . "
    Exception trace
    #FunctionLocation
    ' . $k . ''; - if (!empty($v['class'])) { - $html .= $v['class'] . $v['type']; - } - $html .= $v['function']; - $args = array(); - if (!empty($v['args'])) { - foreach ($v['args'] as $arg) { - if (is_null($arg)) $args[] = 'null'; - elseif (is_array($arg)) $args[] = 'Array'; - elseif (is_object($arg)) $args[] = 'Object('.get_class($arg).')'; - elseif (is_bool($arg)) $args[] = $arg ? 'true' : 'false'; - elseif (is_int($arg) || is_double($arg)) $args[] = $arg; - else { - $arg = (string)$arg; - $str = htmlspecialchars(substr($arg, 0, 16)); - if (strlen($arg) > 16) $str .= '…'; - $args[] = "'" . $str . "'"; - } - } - } - $html .= '(' . implode(', ',$args) . ')' - . '' . (isset($v['file']) ? $v['file'] : 'unknown') - . ':' . (isset($v['line']) ? $v['line'] : 'unknown') - . '
    ' . ($k+1) . '{main} 
    '; - return $html; - } - - public function toText() - { - $causes = array(); - $this->getCauseMessage($causes); - $causeMsg = ''; - foreach ($causes as $i => $cause) { - $causeMsg .= str_repeat(' ', $i) . $cause['class'] . ': ' - . $cause['message'] . ' in ' . $cause['file'] - . ' on line ' . $cause['line'] . "\n"; - } - return $causeMsg . $this->getTraceAsString(); - } -} \ No newline at end of file diff --git a/pear/PEAR/FixPHP5PEARWarnings.php b/pear/PEAR/FixPHP5PEARWarnings.php deleted file mode 100644 index be5dc3c..0000000 --- a/pear/PEAR/FixPHP5PEARWarnings.php +++ /dev/null @@ -1,7 +0,0 @@ - \ No newline at end of file diff --git a/pear/PEAR/Frontend.php b/pear/PEAR/Frontend.php deleted file mode 100644 index b7447b5..0000000 --- a/pear/PEAR/Frontend.php +++ /dev/null @@ -1,228 +0,0 @@ - - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a1 - */ - -/** - * Include error handling - */ -//require_once 'PEAR.php'; - -/** - * Which user interface class is being used. - * @var string class name - */ -$GLOBALS['_PEAR_FRONTEND_CLASS'] = 'PEAR_Frontend_CLI'; - -/** - * Instance of $_PEAR_Command_uiclass. - * @var object - */ -$GLOBALS['_PEAR_FRONTEND_SINGLETON'] = null; - -/** - * Singleton-based frontend for PEAR user input/output - * - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a1 - */ -class PEAR_Frontend extends PEAR -{ - /** - * Retrieve the frontend object - * @return PEAR_Frontend_CLI|PEAR_Frontend_Web|PEAR_Frontend_Gtk - * @static - */ - function &singleton($type = null) - { - if ($type === null) { - if (!isset($GLOBALS['_PEAR_FRONTEND_SINGLETON'])) { - $a = false; - return $a; - } - return $GLOBALS['_PEAR_FRONTEND_SINGLETON']; - } - - $a = PEAR_Frontend::setFrontendClass($type); - return $a; - } - - /** - * Set the frontend class that will be used by calls to {@link singleton()} - * - * Frontends are expected to conform to the PEAR naming standard of - * _ => DIRECTORY_SEPARATOR (PEAR_Frontend_CLI is in PEAR/Frontend/CLI.php) - * @param string $uiclass full class name - * @return PEAR_Frontend - * @static - */ - function &setFrontendClass($uiclass) - { - if (is_object($GLOBALS['_PEAR_FRONTEND_SINGLETON']) && - is_a($GLOBALS['_PEAR_FRONTEND_SINGLETON'], $uiclass)) { - return $GLOBALS['_PEAR_FRONTEND_SINGLETON']; - } - - if (!class_exists($uiclass)) { - $file = str_replace('_', '/', $uiclass) . '.php'; - if (PEAR_Frontend::isIncludeable($file)) { - include_once $file; - } - } - - if (class_exists($uiclass)) { - $obj = &new $uiclass; - // quick test to see if this class implements a few of the most - // important frontend methods - if (is_a($obj, 'PEAR_Frontend')) { - $GLOBALS['_PEAR_FRONTEND_SINGLETON'] = &$obj; - $GLOBALS['_PEAR_FRONTEND_CLASS'] = $uiclass; - return $obj; - } - - $err = PEAR::raiseError("not a frontend class: $uiclass"); - return $err; - } - - $err = PEAR::raiseError("no such class: $uiclass"); - return $err; - } - - /** - * Set the frontend class that will be used by calls to {@link singleton()} - * - * Frontends are expected to be a descendant of PEAR_Frontend - * @param PEAR_Frontend - * @return PEAR_Frontend - * @static - */ - function &setFrontendObject($uiobject) - { - if (is_object($GLOBALS['_PEAR_FRONTEND_SINGLETON']) && - is_a($GLOBALS['_PEAR_FRONTEND_SINGLETON'], get_class($uiobject))) { - return $GLOBALS['_PEAR_FRONTEND_SINGLETON']; - } - - if (!is_a($uiobject, 'PEAR_Frontend')) { - $err = PEAR::raiseError('not a valid frontend class: (' . - get_class($uiobject) . ')'); - return $err; - } - - $GLOBALS['_PEAR_FRONTEND_SINGLETON'] = &$uiobject; - $GLOBALS['_PEAR_FRONTEND_CLASS'] = get_class($uiobject); - return $uiobject; - } - - /** - * @param string $path relative or absolute include path - * @return boolean - * @static - */ - function isIncludeable($path) - { - if (file_exists($path) && is_readable($path)) { - return true; - } - - $fp = @fopen($path, 'r', true); - if ($fp) { - fclose($fp); - return true; - } - - return false; - } - - /** - * @param PEAR_Config - */ - function setConfig(&$config) - { - } - - /** - * This can be overridden to allow session-based temporary file management - * - * By default, all files are deleted at the end of a session. The web installer - * needs to be able to sustain a list over many sessions in order to support - * user interaction with install scripts - */ - function addTempFile($file) - { - $GLOBALS['_PEAR_Common_tempfiles'][] = $file; - } - - /** - * Log an action - * - * @param string $msg the message to log - * @param boolean $append_crlf - * @return boolean true - * @abstract - */ - function log($msg, $append_crlf = true) - { - } - - /** - * Run a post-installation script - * - * @param array $scripts array of post-install scripts - * @abstract - */ - function runPostinstallScripts(&$scripts) - { - } - - /** - * Display human-friendly output formatted depending on the - * $command parameter. - * - * This should be able to handle basic output data with no command - * @param mixed $data data structure containing the information to display - * @param string $command command from which this method was called - * @abstract - */ - function outputData($data, $command = '_default') - { - } - - /** - * Display a modal form dialog and return the given input - * - * A frontend that requires multiple requests to retrieve and process - * data must take these needs into account, and implement the request - * handling code. - * @param string $command command from which this method was called - * @param array $prompts associative array. keys are the input field names - * and values are the description - * @param array $types array of input field types (text, password, - * etc.) keys have to be the same like in $prompts - * @param array $defaults array of default values. again keys have - * to be the same like in $prompts. Do not depend - * on a default value being set. - * @return array input sent by the user - * @abstract - */ - function userDialog($command, $prompts, $types = array(), $defaults = array()) - { - } -} \ No newline at end of file diff --git a/pear/PEAR/Frontend/CLI.php b/pear/PEAR/Frontend/CLI.php deleted file mode 100644 index 8c52e4e..0000000 --- a/pear/PEAR/Frontend/CLI.php +++ /dev/null @@ -1,751 +0,0 @@ - - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 0.1 - */ -/** - * base class - */ -require_once 'PEAR/Frontend.php'; - -/** - * Command-line Frontend for the PEAR Installer - * @category pear - * @package PEAR - * @author Stig Bakken - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 0.1 - */ -class PEAR_Frontend_CLI extends PEAR_Frontend -{ - /** - * What type of user interface this frontend is for. - * @var string - * @access public - */ - var $type = 'CLI'; - var $lp = ''; // line prefix - - var $params = array(); - var $term = array( - 'bold' => '', - 'normal' => '', - ); - - function PEAR_Frontend_CLI() - { - parent::PEAR(); - $term = getenv('TERM'); //(cox) $_ENV is empty for me in 4.1.1 - if (function_exists('posix_isatty') && !posix_isatty(1)) { - // output is being redirected to a file or through a pipe - } elseif ($term) { - if (preg_match('/^(xterm|vt220|linux)/', $term)) { - $this->term['bold'] = sprintf("%c%c%c%c", 27, 91, 49, 109); - $this->term['normal'] = sprintf("%c%c%c", 27, 91, 109); - } elseif (preg_match('/^vt100/', $term)) { - $this->term['bold'] = sprintf("%c%c%c%c%c%c", 27, 91, 49, 109, 0, 0); - $this->term['normal'] = sprintf("%c%c%c%c%c", 27, 91, 109, 0, 0); - } - } elseif (OS_WINDOWS) { - // XXX add ANSI codes here - } - } - - /** - * @param object PEAR_Error object - */ - function displayError($e) - { - return $this->_displayLine($e->getMessage()); - } - - /** - * @param object PEAR_Error object - */ - function displayFatalError($eobj) - { - $this->displayError($eobj); - if (class_exists('PEAR_Config')) { - $config = &PEAR_Config::singleton(); - if ($config->get('verbose') > 5) { - if (function_exists('debug_print_backtrace')) { - debug_print_backtrace(); - exit(1); - } - - $raised = false; - foreach (debug_backtrace() as $i => $frame) { - if (!$raised) { - if (isset($frame['class']) - && strtolower($frame['class']) == 'pear' - && strtolower($frame['function']) == 'raiseerror' - ) { - $raised = true; - } else { - continue; - } - } - - $frame['class'] = !isset($frame['class']) ? '' : $frame['class']; - $frame['type'] = !isset($frame['type']) ? '' : $frame['type']; - $frame['function'] = !isset($frame['function']) ? '' : $frame['function']; - $frame['line'] = !isset($frame['line']) ? '' : $frame['line']; - $this->_displayLine("#$i: $frame[class]$frame[type]$frame[function] $frame[line]"); - } - } - } - - exit(1); - } - - /** - * Instruct the runInstallScript method to skip a paramgroup that matches the - * id value passed in. - * - * This method is useful for dynamically configuring which sections of a post-install script - * will be run based on the user's setup, which is very useful for making flexible - * post-install scripts without losing the cross-Frontend ability to retrieve user input - * @param string - */ - function skipParamgroup($id) - { - $this->_skipSections[$id] = true; - } - - function runPostinstallScripts(&$scripts) - { - foreach ($scripts as $i => $script) { - $this->runInstallScript($scripts[$i]->_params, $scripts[$i]->_obj); - } - } - - /** - * @param array $xml contents of postinstallscript tag - * @param object $script post-installation script - * @param string install|upgrade - */ - function runInstallScript($xml, &$script) - { - $this->_skipSections = array(); - if (!is_array($xml) || !isset($xml['paramgroup'])) { - $script->run(array(), '_default'); - return; - } - - $completedPhases = array(); - if (!isset($xml['paramgroup'][0])) { - $xml['paramgroup'] = array($xml['paramgroup']); - } - - foreach ($xml['paramgroup'] as $group) { - if (isset($this->_skipSections[$group['id']])) { - // the post-install script chose to skip this section dynamically - continue; - } - - if (isset($group['name'])) { - $paramname = explode('::', $group['name']); - if ($lastgroup['id'] != $paramname[0]) { - continue; - } - - $group['name'] = $paramname[1]; - if (!isset($answers)) { - return; - } - - if (isset($answers[$group['name']])) { - switch ($group['conditiontype']) { - case '=' : - if ($answers[$group['name']] != $group['value']) { - continue 2; - } - break; - case '!=' : - if ($answers[$group['name']] == $group['value']) { - continue 2; - } - break; - case 'preg_match' : - if (!@preg_match('/' . $group['value'] . '/', - $answers[$group['name']])) { - continue 2; - } - break; - default : - return; - } - } - } - - $lastgroup = $group; - if (isset($group['instructions'])) { - $this->_display($group['instructions']); - } - - if (!isset($group['param'][0])) { - $group['param'] = array($group['param']); - } - - if (isset($group['param'])) { - if (method_exists($script, 'postProcessPrompts')) { - $prompts = $script->postProcessPrompts($group['param'], $group['id']); - if (!is_array($prompts) || count($prompts) != count($group['param'])) { - $this->outputData('postinstall', 'Error: post-install script did not ' . - 'return proper post-processed prompts'); - $prompts = $group['param']; - } else { - foreach ($prompts as $i => $var) { - if (!is_array($var) || !isset($var['prompt']) || - !isset($var['name']) || - ($var['name'] != $group['param'][$i]['name']) || - ($var['type'] != $group['param'][$i]['type']) - ) { - $this->outputData('postinstall', 'Error: post-install script ' . - 'modified the variables or prompts, severe security risk. ' . - 'Will instead use the defaults from the package.xml'); - $prompts = $group['param']; - } - } - } - - $answers = $this->confirmDialog($prompts); - } else { - $answers = $this->confirmDialog($group['param']); - } - } - - if ((isset($answers) && $answers) || !isset($group['param'])) { - if (!isset($answers)) { - $answers = array(); - } - - array_unshift($completedPhases, $group['id']); - if (!$script->run($answers, $group['id'])) { - $script->run($completedPhases, '_undoOnError'); - return; - } - } else { - $script->run($completedPhases, '_undoOnError'); - return; - } - } - } - - /** - * Ask for user input, confirm the answers and continue until the user is satisfied - * @param array an array of arrays, format array('name' => 'paramname', 'prompt' => - * 'text to display', 'type' => 'string'[, default => 'default value']) - * @return array - */ - function confirmDialog($params) - { - $answers = $prompts = $types = array(); - foreach ($params as $param) { - $prompts[$param['name']] = $param['prompt']; - $types[$param['name']] = $param['type']; - $answers[$param['name']] = isset($param['default']) ? $param['default'] : ''; - } - - $tried = false; - do { - if ($tried) { - $i = 1; - foreach ($answers as $var => $value) { - if (!strlen($value)) { - echo $this->bold("* Enter an answer for #" . $i . ": ({$prompts[$var]})\n"); - } - $i++; - } - } - - $answers = $this->userDialog('', $prompts, $types, $answers); - $tried = true; - } while (is_array($answers) && count(array_filter($answers)) != count($prompts)); - - return $answers; - } - - function userDialog($command, $prompts, $types = array(), $defaults = array(), $screensize = 20) - { - if (!is_array($prompts)) { - return array(); - } - - $testprompts = array_keys($prompts); - $result = $defaults; - - reset($prompts); - if (count($prompts) === 1) { - foreach ($prompts as $key => $prompt) { - $type = $types[$key]; - $default = @$defaults[$key]; - print "$prompt "; - if ($default) { - print "[$default] "; - } - print ": "; - - $line = fgets(STDIN, 2048); - $result[$key] = ($default && trim($line) == '') ? $default : trim($line); - } - - return $result; - } - - $first_run = true; - while (true) { - $descLength = max(array_map('strlen', $prompts)); - $descFormat = "%-{$descLength}s"; - $last = count($prompts); - - $i = 0; - foreach ($prompts as $n => $var) { - $res = isset($result[$n]) ? $result[$n] : null; - printf("%2d. $descFormat : %s\n", ++$i, $prompts[$n], $res); - } - print "\n1-$last, 'all', 'abort', or Enter to continue: "; - - $tmp = trim(fgets(STDIN, 1024)); - if (empty($tmp)) { - break; - } - - if ($tmp == 'abort') { - return false; - } - - if (isset($testprompts[(int)$tmp - 1])) { - $var = $testprompts[(int)$tmp - 1]; - $desc = $prompts[$var]; - $current = @$result[$var]; - print "$desc [$current] : "; - $tmp = trim(fgets(STDIN, 1024)); - if ($tmp !== '') { - $result[$var] = $tmp; - } - } elseif ($tmp == 'all') { - foreach ($prompts as $var => $desc) { - $current = $result[$var]; - print "$desc [$current] : "; - $tmp = trim(fgets(STDIN, 1024)); - if (trim($tmp) !== '') { - $result[$var] = trim($tmp); - } - } - } - - $first_run = false; - } - - return $result; - } - - function userConfirm($prompt, $default = 'yes') - { - trigger_error("PEAR_Frontend_CLI::userConfirm not yet converted", E_USER_ERROR); - static $positives = array('y', 'yes', 'on', '1'); - static $negatives = array('n', 'no', 'off', '0'); - print "$this->lp$prompt [$default] : "; - $fp = fopen("php://stdin", "r"); - $line = fgets($fp, 2048); - fclose($fp); - $answer = strtolower(trim($line)); - if (empty($answer)) { - $answer = $default; - } - if (in_array($answer, $positives)) { - return true; - } - if (in_array($answer, $negatives)) { - return false; - } - if (in_array($default, $positives)) { - return true; - } - return false; - } - - function outputData($data, $command = '_default') - { - switch ($command) { - case 'channel-info': - foreach ($data as $type => $section) { - if ($type == 'main') { - $section['data'] = array_values($section['data']); - } - - $this->outputData($section); - } - break; - case 'install': - case 'upgrade': - case 'upgrade-all': - if (is_array($data) && isset($data['release_warnings'])) { - $this->_displayLine(''); - $this->_startTable(array( - 'border' => false, - 'caption' => 'Release Warnings' - )); - $this->_tableRow(array($data['release_warnings']), null, array(1 => array('wrap' => 55))); - $this->_endTable(); - $this->_displayLine(''); - } - - $this->_displayLine(is_array($data) ? $data['data'] : $data); - break; - case 'search': - $this->_startTable($data); - if (isset($data['headline']) && is_array($data['headline'])) { - $this->_tableRow($data['headline'], array('bold' => true), array(1 => array('wrap' => 55))); - } - - $packages = array(); - foreach($data['data'] as $category) { - foreach($category as $name => $pkg) { - $packages[$pkg[0]] = $pkg; - } - } - - $p = array_keys($packages); - natcasesort($p); - foreach ($p as $name) { - $this->_tableRow($packages[$name], null, array(1 => array('wrap' => 55))); - } - - $this->_endTable(); - break; - case 'list-all': - if (!isset($data['data'])) { - $this->_displayLine('No packages in channel'); - break; - } - - $this->_startTable($data); - if (isset($data['headline']) && is_array($data['headline'])) { - $this->_tableRow($data['headline'], array('bold' => true), array(1 => array('wrap' => 55))); - } - - $packages = array(); - foreach($data['data'] as $category) { - foreach($category as $name => $pkg) { - $packages[$pkg[0]] = $pkg; - } - } - - $p = array_keys($packages); - natcasesort($p); - foreach ($p as $name) { - $pkg = $packages[$name]; - unset($pkg[4], $pkg[5]); - $this->_tableRow($pkg, null, array(1 => array('wrap' => 55))); - } - - $this->_endTable(); - break; - case 'config-show': - $data['border'] = false; - $opts = array( - 0 => array('wrap' => 30), - 1 => array('wrap' => 20), - 2 => array('wrap' => 35) - ); - - $this->_startTable($data); - if (isset($data['headline']) && is_array($data['headline'])) { - $this->_tableRow($data['headline'], array('bold' => true), $opts); - } - - foreach ($data['data'] as $group) { - foreach ($group as $value) { - if ($value[2] == '') { - $value[2] = ""; - } - - $this->_tableRow($value, null, $opts); - } - } - - $this->_endTable(); - break; - case 'remote-info': - $d = $data; - $data = array( - 'caption' => 'Package details:', - 'border' => false, - 'data' => array( - array("Latest", $data['stable']), - array("Installed", $data['installed']), - array("Package", $data['name']), - array("License", $data['license']), - array("Category", $data['category']), - array("Summary", $data['summary']), - array("Description", $data['description']), - ), - ); - - if (isset($d['deprecated']) && $d['deprecated']) { - $conf = &PEAR_Config::singleton(); - $reg = $conf->getRegistry(); - $name = $reg->parsedPackageNameToString($d['deprecated'], true); - $data['data'][] = array('Deprecated! use', $name); - } - default: { - if (is_array($data)) { - $this->_startTable($data); - $count = count($data['data'][0]); - if ($count == 2) { - $opts = array(0 => array('wrap' => 25), - 1 => array('wrap' => 48) - ); - } elseif ($count == 3) { - $opts = array(0 => array('wrap' => 30), - 1 => array('wrap' => 20), - 2 => array('wrap' => 35) - ); - } else { - $opts = null; - } - if (isset($data['headline']) && is_array($data['headline'])) { - $this->_tableRow($data['headline'], - array('bold' => true), - $opts); - } - - if (is_array($data['data'])) { - foreach($data['data'] as $row) { - $this->_tableRow($row, null, $opts); - } - } else { - $this->_tableRow(array($data['data']), null, $opts); - } - $this->_endTable(); - } else { - $this->_displayLine($data); - } - } - } - } - - function log($text, $append_crlf = true) - { - if ($append_crlf) { - return $this->_displayLine($text); - } - - return $this->_display($text); - } - - function bold($text) - { - if (empty($this->term['bold'])) { - return strtoupper($text); - } - - return $this->term['bold'] . $text . $this->term['normal']; - } - - function _displayHeading($title) - { - print $this->lp.$this->bold($title)."\n"; - print $this->lp.str_repeat("=", strlen($title))."\n"; - } - - function _startTable($params = array()) - { - $params['table_data'] = array(); - $params['widest'] = array(); // indexed by column - $params['highest'] = array(); // indexed by row - $params['ncols'] = 0; - $this->params = $params; - } - - function _tableRow($columns, $rowparams = array(), $colparams = array()) - { - $highest = 1; - for ($i = 0; $i < count($columns); $i++) { - $col = &$columns[$i]; - if (isset($colparams[$i]) && !empty($colparams[$i]['wrap'])) { - $col = wordwrap($col, $colparams[$i]['wrap']); - } - - if (strpos($col, "\n") !== false) { - $multiline = explode("\n", $col); - $w = 0; - foreach ($multiline as $n => $line) { - $len = strlen($line); - if ($len > $w) { - $w = $len; - } - } - $lines = count($multiline); - } else { - $w = strlen($col); - } - - if (isset($this->params['widest'][$i])) { - if ($w > $this->params['widest'][$i]) { - $this->params['widest'][$i] = $w; - } - } else { - $this->params['widest'][$i] = $w; - } - - $tmp = count_chars($columns[$i], 1); - // handle unix, mac and windows formats - $lines = (isset($tmp[10]) ? $tmp[10] : (isset($tmp[13]) ? $tmp[13] : 0)) + 1; - if ($lines > $highest) { - $highest = $lines; - } - } - - if (count($columns) > $this->params['ncols']) { - $this->params['ncols'] = count($columns); - } - - $new_row = array( - 'data' => $columns, - 'height' => $highest, - 'rowparams' => $rowparams, - 'colparams' => $colparams, - ); - $this->params['table_data'][] = $new_row; - } - - function _endTable() - { - extract($this->params); - if (!empty($caption)) { - $this->_displayHeading($caption); - } - - if (count($table_data) === 0) { - return; - } - - if (!isset($width)) { - $width = $widest; - } else { - for ($i = 0; $i < $ncols; $i++) { - if (!isset($width[$i])) { - $width[$i] = $widest[$i]; - } - } - } - - $border = false; - if (empty($border)) { - $cellstart = ''; - $cellend = ' '; - $rowend = ''; - $padrowend = false; - $borderline = ''; - } else { - $cellstart = '| '; - $cellend = ' '; - $rowend = '|'; - $padrowend = true; - $borderline = '+'; - foreach ($width as $w) { - $borderline .= str_repeat('-', $w + strlen($cellstart) + strlen($cellend) - 1); - $borderline .= '+'; - } - } - - if ($borderline) { - $this->_displayLine($borderline); - } - - for ($i = 0; $i < count($table_data); $i++) { - extract($table_data[$i]); - if (!is_array($rowparams)) { - $rowparams = array(); - } - - if (!is_array($colparams)) { - $colparams = array(); - } - - $rowlines = array(); - if ($height > 1) { - for ($c = 0; $c < count($data); $c++) { - $rowlines[$c] = preg_split('/(\r?\n|\r)/', $data[$c]); - if (count($rowlines[$c]) < $height) { - $rowlines[$c] = array_pad($rowlines[$c], $height, ''); - } - } - } else { - for ($c = 0; $c < count($data); $c++) { - $rowlines[$c] = array($data[$c]); - } - } - - for ($r = 0; $r < $height; $r++) { - $rowtext = ''; - for ($c = 0; $c < count($data); $c++) { - if (isset($colparams[$c])) { - $attribs = array_merge($rowparams, $colparams); - } else { - $attribs = $rowparams; - } - - $w = isset($width[$c]) ? $width[$c] : 0; - //$cell = $data[$c]; - $cell = $rowlines[$c][$r]; - $l = strlen($cell); - if ($l > $w) { - $cell = substr($cell, 0, $w); - } - - if (isset($attribs['bold'])) { - $cell = $this->bold($cell); - } - - if ($l < $w) { - // not using str_pad here because we may - // add bold escape characters to $cell - $cell .= str_repeat(' ', $w - $l); - } - - $rowtext .= $cellstart . $cell . $cellend; - } - - if (!$border) { - $rowtext = rtrim($rowtext); - } - - $rowtext .= $rowend; - $this->_displayLine($rowtext); - } - } - - if ($borderline) { - $this->_displayLine($borderline); - } - } - - function _displayLine($text) - { - print "$this->lp$text\n"; - } - - function _display($text) - { - print $text; - } -} \ No newline at end of file diff --git a/pear/PEAR/Installer.php b/pear/PEAR/Installer.php deleted file mode 100644 index 867b9ed..0000000 --- a/pear/PEAR/Installer.php +++ /dev/null @@ -1,1753 +0,0 @@ - - * @author Tomas V.V. Cox - * @author Martin Jansen - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 0.1 - */ - -/** - * Used for installation groups in package.xml 2.0 and platform exceptions - */ -require_once 'OS/Guess.php'; -require_once 'PEAR/Downloader.php'; - -define('PEAR_INSTALLER_NOBINARY', -240); -/** - * Administration class used to install PEAR packages and maintain the - * installed package database. - * - * @category pear - * @package PEAR - * @author Stig Bakken - * @author Tomas V.V. Cox - * @author Martin Jansen - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 0.1 - */ -class PEAR_Installer extends PEAR_Downloader -{ - /** name of the package directory, for example Foo-1.0 - * @var string - */ - var $pkgdir; - - /** directory where PHP code files go - * @var string - */ - var $phpdir; - - /** directory where PHP extension files go - * @var string - */ - var $extdir; - - /** directory where documentation goes - * @var string - */ - var $docdir; - - /** installation root directory (ala PHP's INSTALL_ROOT or - * automake's DESTDIR - * @var string - */ - var $installroot = ''; - - /** debug level - * @var int - */ - var $debug = 1; - - /** temporary directory - * @var string - */ - var $tmpdir; - - /** - * PEAR_Registry object used by the installer - * @var PEAR_Registry - */ - var $registry; - - /** - * array of PEAR_Downloader_Packages - * @var array - */ - var $_downloadedPackages; - - /** List of file transactions queued for an install/upgrade/uninstall. - * - * Format: - * array( - * 0 => array("rename => array("from-file", "to-file")), - * 1 => array("delete" => array("file-to-delete")), - * ... - * ) - * - * @var array - */ - var $file_operations = array(); - - /** - * PEAR_Installer constructor. - * - * @param object $ui user interface object (instance of PEAR_Frontend_*) - * - * @access public - */ - function PEAR_Installer(&$ui) - { - parent::PEAR_Common(); - $this->setFrontendObject($ui); - $this->debug = $this->config->get('verbose'); - } - - function setOptions($options) - { - $this->_options = $options; - } - - function setConfig(&$config) - { - $this->config = &$config; - $this->_registry = &$config->getRegistry(); - } - - function _removeBackups($files) - { - foreach ($files as $path) { - $this->addFileOperation('removebackup', array($path)); - } - } - - /** - * Delete a package's installed files, does not remove empty directories. - * - * @param string package name - * @param string channel name - * @param bool if true, then files are backed up first - * @return bool TRUE on success, or a PEAR error on failure - * @access protected - */ - function _deletePackageFiles($package, $channel = false, $backup = false) - { - if (!$channel) { - $channel = 'pear.php.net'; - } - - if (!strlen($package)) { - return $this->raiseError("No package to uninstall given"); - } - - if (strtolower($package) == 'pear' && $channel == 'pear.php.net') { - // to avoid race conditions, include all possible needed files - require_once 'PEAR/Task/Common.php'; - require_once 'PEAR/Task/Replace.php'; - require_once 'PEAR/Task/Unixeol.php'; - require_once 'PEAR/Task/Windowseol.php'; - require_once 'PEAR/PackageFile/v1.php'; - require_once 'PEAR/PackageFile/v2.php'; - require_once 'PEAR/PackageFile/Generator/v1.php'; - require_once 'PEAR/PackageFile/Generator/v2.php'; - } - - $filelist = $this->_registry->packageInfo($package, 'filelist', $channel); - if ($filelist == null) { - return $this->raiseError("$channel/$package not installed"); - } - - $ret = array(); - foreach ($filelist as $file => $props) { - if (empty($props['installed_as'])) { - continue; - } - - $path = $props['installed_as']; - if ($backup) { - $this->addFileOperation('backup', array($path)); - $ret[] = $path; - } - - $this->addFileOperation('delete', array($path)); - } - - if ($backup) { - return $ret; - } - - return true; - } - - /** - * @param string filename - * @param array attributes from tag in package.xml - * @param string path to install the file in - * @param array options from command-line - * @access private - */ - function _installFile($file, $atts, $tmp_path, $options) - { - // return if this file is meant for another platform - static $os; - if (!isset($this->_registry)) { - $this->_registry = &$this->config->getRegistry(); - } - - if (isset($atts['platform'])) { - if (empty($os)) { - $os = new OS_Guess; - } - - if (strlen($atts['platform']) && $atts['platform']{0} == '!') { - $negate = true; - $platform = substr($atts['platform'], 1); - } else { - $negate = false; - $platform = $atts['platform']; - } - - if ((bool) $os->matchSignature($platform) === $negate) { - $this->log(3, "skipped $file (meant for $atts[platform], we are ".$os->getSignature().")"); - return PEAR_INSTALLER_SKIPPED; - } - } - - $channel = $this->pkginfo->getChannel(); - // assemble the destination paths - switch ($atts['role']) { - case 'src': - case 'extsrc': - $this->source_files++; - return; - case 'doc': - case 'data': - case 'test': - $dest_dir = $this->config->get($atts['role'] . '_dir', null, $channel) . - DIRECTORY_SEPARATOR . $this->pkginfo->getPackage(); - unset($atts['baseinstalldir']); - break; - case 'ext': - case 'php': - $dest_dir = $this->config->get($atts['role'] . '_dir', null, $channel); - break; - case 'script': - $dest_dir = $this->config->get('bin_dir', null, $channel); - break; - default: - return $this->raiseError("Invalid role `$atts[role]' for file $file"); - } - - $save_destdir = $dest_dir; - if (!empty($atts['baseinstalldir'])) { - $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir']; - } - - if (dirname($file) != '.' && empty($atts['install-as'])) { - $dest_dir .= DIRECTORY_SEPARATOR . dirname($file); - } - - if (empty($atts['install-as'])) { - $dest_file = $dest_dir . DIRECTORY_SEPARATOR . basename($file); - } else { - $dest_file = $dest_dir . DIRECTORY_SEPARATOR . $atts['install-as']; - } - $orig_file = $tmp_path . DIRECTORY_SEPARATOR . $file; - - // Clean up the DIRECTORY_SEPARATOR mess - $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR; - list($dest_file, $orig_file) = preg_replace(array('!\\\\+!', '!/!', "!$ds2+!"), - array(DIRECTORY_SEPARATOR, - DIRECTORY_SEPARATOR, - DIRECTORY_SEPARATOR), - array($dest_file, $orig_file)); - $final_dest_file = $installed_as = $dest_file; - if (isset($this->_options['packagingroot'])) { - $installedas_dest_dir = dirname($final_dest_file); - $installedas_dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file); - $final_dest_file = $this->_prependPath($final_dest_file, $this->_options['packagingroot']); - } else { - $installedas_dest_dir = dirname($final_dest_file); - $installedas_dest_file = $installedas_dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file); - } - - $dest_dir = dirname($final_dest_file); - $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file); - if (preg_match('~/\.\.(/|\\z)|^\.\./~', str_replace('\\', '/', $dest_file))) { - return $this->raiseError("SECURITY ERROR: file $file (installed to $dest_file) contains parent directory reference ..", PEAR_INSTALLER_FAILED); - } - - if ( - empty($this->_options['register-only']) && - (!file_exists($dest_dir) || !is_dir($dest_dir)) - ) { - if (!$this->mkDirHier($dest_dir)) { - return $this->raiseError("failed to mkdir $dest_dir", - PEAR_INSTALLER_FAILED); - } - $this->log(3, "+ mkdir $dest_dir"); - } - - // pretty much nothing happens if we are only registering the install - if (empty($this->_options['register-only'])) { - if (empty($atts['replacements'])) { - if (!file_exists($orig_file)) { - return $this->raiseError("file $orig_file does not exist", - PEAR_INSTALLER_FAILED); - } - - if (!@copy($orig_file, $dest_file)) { - return $this->raiseError("failed to write $dest_file: $php_errormsg", - PEAR_INSTALLER_FAILED); - } - - $this->log(3, "+ cp $orig_file $dest_file"); - if (isset($atts['md5sum'])) { - $md5sum = md5_file($dest_file); - } - } else { - // file with replacements - if (!file_exists($orig_file)) { - return $this->raiseError("file does not exist", - PEAR_INSTALLER_FAILED); - } - - $contents = file_get_contents($orig_file); - if ($contents === false) { - $contents = ''; - } - - if (isset($atts['md5sum'])) { - $md5sum = md5($contents); - } - - $subst_from = $subst_to = array(); - foreach ($atts['replacements'] as $a) { - $to = ''; - if ($a['type'] == 'php-const') { - if (preg_match('/^[a-z0-9_]+\\z/i', $a['to'])) { - eval("\$to = $a[to];"); - } else { - if (!isset($options['soft'])) { - $this->log(0, "invalid php-const replacement: $a[to]"); - } - continue; - } - } elseif ($a['type'] == 'pear-config') { - if ($a['to'] == 'master_server') { - $chan = $this->_registry->getChannel($channel); - if (!PEAR::isError($chan)) { - $to = $chan->getServer(); - } else { - $to = $this->config->get($a['to'], null, $channel); - } - } else { - $to = $this->config->get($a['to'], null, $channel); - } - - if (is_null($to)) { - if (!isset($options['soft'])) { - $this->log(0, "invalid pear-config replacement: $a[to]"); - } - - continue; - } - } elseif ($a['type'] == 'package-info') { - if ($t = $this->pkginfo->packageInfo($a['to'])) { - $to = $t; - } else { - if (!isset($options['soft'])) { - $this->log(0, "invalid package-info replacement: $a[to]"); - } - - continue; - } - } - - if (!is_null($to)) { - $subst_from[] = $a['from']; - $subst_to[] = $to; - } - } - - $this->log(3, "doing ".sizeof($subst_from)." substitution(s) for $final_dest_file"); - if (sizeof($subst_from)) { - $contents = str_replace($subst_from, $subst_to, $contents); - } - - $wp = @fopen($dest_file, "wb"); - if (!is_resource($wp)) { - return $this->raiseError("failed to create $dest_file: $php_errormsg", - PEAR_INSTALLER_FAILED); - } - - if (@fwrite($wp, $contents) === false) { - return $this->raiseError("failed writing to $dest_file: $php_errormsg", - PEAR_INSTALLER_FAILED); - } - - fclose($wp); - } - - // check the md5 - if (isset($md5sum)) { - if (strtolower($md5sum) === strtolower($atts['md5sum'])) { - $this->log(2, "md5sum ok: $final_dest_file"); - } else { - if (empty($options['force'])) { - // delete the file - if (file_exists($dest_file)) { - unlink($dest_file); - } - - if (!isset($options['ignore-errors'])) { - return $this->raiseError("bad md5sum for file $final_dest_file", - PEAR_INSTALLER_FAILED); - } - - if (!isset($options['soft'])) { - $this->log(0, "warning : bad md5sum for file $final_dest_file"); - } - } else { - if (!isset($options['soft'])) { - $this->log(0, "warning : bad md5sum for file $final_dest_file"); - } - } - } - } - - // set file permissions - if (!OS_WINDOWS) { - $umask = $this->config->get('umask'); - if ($atts['role'] == 'script') { - $mode = 0777 & ~(int)octdec($umask); - $this->log(3, "+ chmod +x $dest_file"); - } else { - $mode = 0666 & ~(int)octdec($umask); - } - - if ($atts['role'] != 'src') { - $this->addFileOperation("chmod", array($mode, $dest_file)); - if (!@chmod($dest_file, $mode)) { - if (!isset($options['soft'])) { - $this->log(0, "failed to change mode of $dest_file: $php_errormsg"); - } - } - } - } - - if ($atts['role'] == 'src') { - rename($dest_file, $final_dest_file); - $this->log(2, "renamed source file $dest_file to $final_dest_file"); - } else { - $this->addFileOperation("rename", array($dest_file, $final_dest_file, - $atts['role'] == 'ext')); - } - } - - // Store the full path where the file was installed for easy unistall - if ($atts['role'] != 'script') { - $loc = $this->config->get($atts['role'] . '_dir'); - } else { - $loc = $this->config->get('bin_dir'); - } - - if ($atts['role'] != 'src') { - $this->addFileOperation("installed_as", array($file, $installed_as, - $loc, - dirname(substr($installedas_dest_file, strlen($loc))))); - } - - //$this->log(2, "installed: $dest_file"); - return PEAR_INSTALLER_OK; - } - - /** - * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 - * @param string filename - * @param array attributes from tag in package.xml - * @param string path to install the file in - * @param array options from command-line - * @access private - */ - function _installFile2(&$pkg, $file, &$real_atts, $tmp_path, $options) - { - $atts = $real_atts; - if (!isset($this->_registry)) { - $this->_registry = &$this->config->getRegistry(); - } - - $channel = $pkg->getChannel(); - // assemble the destination paths - $roles = PEAR_Installer_Role::getValidRoles($pkg->getPackageType()); - if (!in_array($atts['attribs']['role'], $roles)) { - return $this->raiseError('Invalid role `' . $atts['attribs']['role'] . - "' for file $file"); - } - - $role = &PEAR_Installer_Role::factory($pkg, $atts['attribs']['role'], $this->config); - $err = $role->setup($this, $pkg, $atts['attribs'], $file); - if (PEAR::isError($err)) { - return $err; - } - - if (!$role->isInstallable()) { - return; - } - - $info = $role->processInstallation($pkg, $atts['attribs'], $file, $tmp_path); - if (PEAR::isError($info)) { - return $info; - } - - list($save_destdir, $dest_dir, $dest_file, $orig_file) = $info; - if (preg_match('~/\.\.(/|\\z)|^\.\./~', str_replace('\\', '/', $dest_file))) { - return $this->raiseError("SECURITY ERROR: file $file (installed to $dest_file) contains parent directory reference ..", PEAR_INSTALLER_FAILED); - } - - $final_dest_file = $installed_as = $dest_file; - if (isset($this->_options['packagingroot'])) { - $final_dest_file = $this->_prependPath($final_dest_file, $this->_options['packagingroot']); - } - - $dest_dir = dirname($final_dest_file); - $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file); - - if (empty($this->_options['register-only'])) { - if (!file_exists($dest_dir) || !is_dir($dest_dir)) { - if (!$this->mkDirHier($dest_dir)) { - return $this->raiseError("failed to mkdir $dest_dir", - PEAR_INSTALLER_FAILED); - } - $this->log(3, "+ mkdir $dest_dir"); - } - } - - $attribs = $atts['attribs']; - unset($atts['attribs']); - // pretty much nothing happens if we are only registering the install - if (empty($this->_options['register-only'])) { - if (!count($atts)) { // no tasks - if (!file_exists($orig_file)) { - return $this->raiseError("file $orig_file does not exist", - PEAR_INSTALLER_FAILED); - } - - if (!@copy($orig_file, $dest_file)) { - return $this->raiseError("failed to write $dest_file: $php_errormsg", - PEAR_INSTALLER_FAILED); - } - - $this->log(3, "+ cp $orig_file $dest_file"); - if (isset($attribs['md5sum'])) { - $md5sum = md5_file($dest_file); - } - } else { // file with tasks - if (!file_exists($orig_file)) { - return $this->raiseError("file $orig_file does not exist", - PEAR_INSTALLER_FAILED); - } - - $contents = file_get_contents($orig_file); - if ($contents === false) { - $contents = ''; - } - - if (isset($attribs['md5sum'])) { - $md5sum = md5($contents); - } - - foreach ($atts as $tag => $raw) { - $tag = str_replace(array($pkg->getTasksNs() . ':', '-'), array('', '_'), $tag); - $task = "PEAR_Task_$tag"; - $task = &new $task($this->config, $this, PEAR_TASK_INSTALL); - if (!$task->isScript()) { // scripts are only handled after installation - $task->init($raw, $attribs, $pkg->getLastInstalledVersion()); - $res = $task->startSession($pkg, $contents, $final_dest_file); - if ($res === false) { - continue; // skip this file - } - - if (PEAR::isError($res)) { - return $res; - } - - $contents = $res; // save changes - } - - $wp = @fopen($dest_file, "wb"); - if (!is_resource($wp)) { - return $this->raiseError("failed to create $dest_file: $php_errormsg", - PEAR_INSTALLER_FAILED); - } - - if (fwrite($wp, $contents) === false) { - return $this->raiseError("failed writing to $dest_file: $php_errormsg", - PEAR_INSTALLER_FAILED); - } - - fclose($wp); - } - } - - // check the md5 - if (isset($md5sum)) { - // Make sure the original md5 sum matches with expected - if (strtolower($md5sum) === strtolower($attribs['md5sum'])) { - $this->log(2, "md5sum ok: $final_dest_file"); - - if (isset($contents)) { - // set md5 sum based on $content in case any tasks were run. - $real_atts['attribs']['md5sum'] = md5($contents); - } - } else { - if (empty($options['force'])) { - // delete the file - if (file_exists($dest_file)) { - unlink($dest_file); - } - - if (!isset($options['ignore-errors'])) { - return $this->raiseError("bad md5sum for file $final_dest_file", - PEAR_INSTALLER_FAILED); - } - - if (!isset($options['soft'])) { - $this->log(0, "warning : bad md5sum for file $final_dest_file"); - } - } else { - if (!isset($options['soft'])) { - $this->log(0, "warning : bad md5sum for file $final_dest_file"); - } - } - } - } else { - $real_atts['attribs']['md5sum'] = md5_file($dest_file); - } - - //set file permissions - if (!OS_WINDOWS) { - if ($role->isExecutable()) { - $mode = 0777 & ~(int)octdec($this->config->get('umask')); - $this->log(3, "+ chmod +x $dest_file"); - } else { - $mode = 0666 & ~(int)octdec($this->config->get('umask')); - } - - if ($attribs['role'] != 'src') { - $this->addFileOperation("chmod", array($mode, $dest_file)); - if (!@chmod($dest_file, $mode)) { - if (!isset($options['soft'])) { - $this->log(0, "failed to change mode of $dest_file: $php_errormsg"); - } - } - } - } - - if ($attribs['role'] == 'src') { - rename($dest_file, $final_dest_file); - $this->log(2, "renamed source file $dest_file to $final_dest_file"); - } else { - $this->addFileOperation("rename", array($dest_file, $final_dest_file, $role->isExtension())); - } - } - - // Store the full path where the file was installed for easy uninstall - if ($attribs['role'] != 'src') { - $loc = $this->config->get($role->getLocationConfig(), null, $channel); - $this->addFileOperation('installed_as', array($file, $installed_as, - $loc, - dirname(substr($installed_as, strlen($loc))))); - } - - //$this->log(2, "installed: $dest_file"); - return PEAR_INSTALLER_OK; - } - - /** - * Add a file operation to the current file transaction. - * - * @see startFileTransaction() - * @param string $type This can be one of: - * - rename: rename a file ($data has 3 values) - * - backup: backup an existing file ($data has 1 value) - * - removebackup: clean up backups created during install ($data has 1 value) - * - chmod: change permissions on a file ($data has 2 values) - * - delete: delete a file ($data has 1 value) - * - rmdir: delete a directory if empty ($data has 1 value) - * - installed_as: mark a file as installed ($data has 4 values). - * @param array $data For all file operations, this array must contain the - * full path to the file or directory that is being operated on. For - * the rename command, the first parameter must be the file to rename, - * the second its new name, the third whether this is a PHP extension. - * - * The installed_as operation contains 4 elements in this order: - * 1. Filename as listed in the filelist element from package.xml - * 2. Full path to the installed file - * 3. Full path from the php_dir configuration variable used in this - * installation - * 4. Relative path from the php_dir that this file is installed in - */ - function addFileOperation($type, $data) - { - if (!is_array($data)) { - return $this->raiseError('Internal Error: $data in addFileOperation' - . ' must be an array, was ' . gettype($data)); - } - - if ($type == 'chmod') { - $octmode = decoct($data[0]); - $this->log(3, "adding to transaction: $type $octmode $data[1]"); - } else { - $this->log(3, "adding to transaction: $type " . implode(" ", $data)); - } - $this->file_operations[] = array($type, $data); - } - - function startFileTransaction($rollback_in_case = false) - { - if (count($this->file_operations) && $rollback_in_case) { - $this->rollbackFileTransaction(); - } - $this->file_operations = array(); - } - - function commitFileTransaction() - { - //first, check permissions and such manually - $errors = array(); - foreach ($this->file_operations as $key => $tr) { - list($type, $data) = $tr; - switch ($type) { - case 'rename': - if (!file_exists($data[0])) { - $errors[] = "cannot rename file $data[0], doesn't exist"; - } - - // check that dest dir. is writable - if (!is_writable(dirname($data[1]))) { - $errors[] = "permission denied ($type): $data[1]"; - } - break; - case 'chmod': - // check that file is writable - if (!is_writable($data[1])) { - $errors[] = "permission denied ($type): $data[1] " . decoct($data[0]); - } - break; - case 'delete': - if (!file_exists($data[0])) { - $this->log(2, "warning: file $data[0] doesn't exist, can't be deleted"); - } - - // check that directory is writable - if (file_exists($data[0])) { - if (!is_writable(dirname($data[0]))) { - $errors[] = "permission denied ($type): $data[0]"; - } else { - // make sure the file to be deleted can be opened for writing - $fp = false; - if (!is_dir($data[0]) && - (!is_writable($data[0]) || !($fp = @fopen($data[0], 'a')))) { - $errors[] = "permission denied ($type): $data[0]"; - } elseif ($fp) { - fclose($fp); - } - } - - /* Verify we are not deleting a file owned by another package - * This can happen when a file moves from package A to B in - * an upgrade ala http://pear.php.net/17986 - */ - $info = array( - 'package' => strtolower($this->pkginfo->getName()), - 'channel' => strtolower($this->pkginfo->getChannel()), - ); - $result = $this->_registry->checkFileMap($data[0], $info, '1.1'); - if (is_array($result)) { - $res = array_diff($result, $info); - if (!empty($res)) { - $new = $this->_registry->getPackage($result[1], $result[0]); - $this->file_operations[$key] = false; - $pkginfoName = $this->pkginfo->getName(); - $newChannel = $new->getChannel(); - $newPackage = $new->getName(); - $this->log(3, "file $data[0] was scheduled for removal from $pkginfoName but is owned by $newChannel/$newPackage, removal has been cancelled."); - } - } - } - break; - } - } - - $n = count($this->file_operations); - $this->log(2, "about to commit $n file operations for " . $this->pkginfo->getName()); - - $m = count($errors); - if ($m > 0) { - foreach ($errors as $error) { - if (!isset($this->_options['soft'])) { - $this->log(1, $error); - } - } - - if (!isset($this->_options['ignore-errors'])) { - return false; - } - } - - $this->_dirtree = array(); - // really commit the transaction - foreach ($this->file_operations as $i => $tr) { - if (!$tr) { - // support removal of non-existing backups - continue; - } - - list($type, $data) = $tr; - switch ($type) { - case 'backup': - if (!file_exists($data[0])) { - $this->file_operations[$i] = false; - break; - } - - if (!@copy($data[0], $data[0] . '.bak')) { - $this->log(1, 'Could not copy ' . $data[0] . ' to ' . $data[0] . - '.bak ' . $php_errormsg); - return false; - } - $this->log(3, "+ backup $data[0] to $data[0].bak"); - break; - case 'removebackup': - if (file_exists($data[0] . '.bak') && is_writable($data[0] . '.bak')) { - unlink($data[0] . '.bak'); - $this->log(3, "+ rm backup of $data[0] ($data[0].bak)"); - } - break; - case 'rename': - $test = file_exists($data[1]) ? @unlink($data[1]) : null; - if (!$test && file_exists($data[1])) { - if ($data[2]) { - $extra = ', this extension must be installed manually. Rename to "' . - basename($data[1]) . '"'; - } else { - $extra = ''; - } - - if (!isset($this->_options['soft'])) { - $this->log(1, 'Could not delete ' . $data[1] . ', cannot rename ' . - $data[0] . $extra); - } - - if (!isset($this->_options['ignore-errors'])) { - return false; - } - } - - // permissions issues with rename - copy() is far superior - $perms = @fileperms($data[0]); - if (!@copy($data[0], $data[1])) { - $this->log(1, 'Could not rename ' . $data[0] . ' to ' . $data[1] . - ' ' . $php_errormsg); - return false; - } - - // copy over permissions, otherwise they are lost - @chmod($data[1], $perms); - @unlink($data[0]); - $this->log(3, "+ mv $data[0] $data[1]"); - break; - case 'chmod': - if (!@chmod($data[1], $data[0])) { - $this->log(1, 'Could not chmod ' . $data[1] . ' to ' . - decoct($data[0]) . ' ' . $php_errormsg); - return false; - } - - $octmode = decoct($data[0]); - $this->log(3, "+ chmod $octmode $data[1]"); - break; - case 'delete': - if (file_exists($data[0])) { - if (!@unlink($data[0])) { - $this->log(1, 'Could not delete ' . $data[0] . ' ' . - $php_errormsg); - return false; - } - $this->log(3, "+ rm $data[0]"); - } - break; - case 'rmdir': - if (file_exists($data[0])) { - do { - $testme = opendir($data[0]); - while (false !== ($entry = readdir($testme))) { - if ($entry == '.' || $entry == '..') { - continue; - } - closedir($testme); - break 2; // this directory is not empty and can't be - // deleted - } - - closedir($testme); - if (!@rmdir($data[0])) { - $this->log(1, 'Could not rmdir ' . $data[0] . ' ' . - $php_errormsg); - return false; - } - $this->log(3, "+ rmdir $data[0]"); - } while (false); - } - break; - case 'installed_as': - $this->pkginfo->setInstalledAs($data[0], $data[1]); - if (!isset($this->_dirtree[dirname($data[1])])) { - $this->_dirtree[dirname($data[1])] = true; - $this->pkginfo->setDirtree(dirname($data[1])); - - while(!empty($data[3]) && dirname($data[3]) != $data[3] && - $data[3] != '/' && $data[3] != '\\') { - $this->pkginfo->setDirtree($pp = - $this->_prependPath($data[3], $data[2])); - $this->_dirtree[$pp] = true; - $data[3] = dirname($data[3]); - } - } - break; - } - } - - $this->log(2, "successfully committed $n file operations"); - $this->file_operations = array(); - return true; - } - - function rollbackFileTransaction() - { - $n = count($this->file_operations); - $this->log(2, "rolling back $n file operations"); - foreach ($this->file_operations as $tr) { - list($type, $data) = $tr; - switch ($type) { - case 'backup': - if (file_exists($data[0] . '.bak')) { - if (file_exists($data[0] && is_writable($data[0]))) { - unlink($data[0]); - } - @copy($data[0] . '.bak', $data[0]); - $this->log(3, "+ restore $data[0] from $data[0].bak"); - } - break; - case 'removebackup': - if (file_exists($data[0] . '.bak') && is_writable($data[0] . '.bak')) { - unlink($data[0] . '.bak'); - $this->log(3, "+ rm backup of $data[0] ($data[0].bak)"); - } - break; - case 'rename': - @unlink($data[0]); - $this->log(3, "+ rm $data[0]"); - break; - case 'mkdir': - @rmdir($data[0]); - $this->log(3, "+ rmdir $data[0]"); - break; - case 'chmod': - break; - case 'delete': - break; - case 'installed_as': - $this->pkginfo->setInstalledAs($data[0], false); - break; - } - } - $this->pkginfo->resetDirtree(); - $this->file_operations = array(); - } - - function mkDirHier($dir) - { - $this->addFileOperation('mkdir', array($dir)); - return parent::mkDirHier($dir); - } - - /** - * Download any files and their dependencies, if necessary - * - * @param array a mixed list of package names, local files, or package.xml - * @param PEAR_Config - * @param array options from the command line - * @param array this is the array that will be populated with packages to - * install. Format of each entry: - * - * - * array('pkg' => 'package_name', 'file' => '/path/to/local/file', - * 'info' => array() // parsed package.xml - * ); - * - * @param array this will be populated with any error messages - * @param false private recursion variable - * @param false private recursion variable - * @param false private recursion variable - * @deprecated in favor of PEAR_Downloader - */ - function download($packages, $options, &$config, &$installpackages, - &$errors, $installed = false, $willinstall = false, $state = false) - { - // trickiness: initialize here - parent::PEAR_Downloader($this->ui, $options, $config); - $ret = parent::download($packages); - $errors = $this->getErrorMsgs(); - $installpackages = $this->getDownloadedPackages(); - trigger_error("PEAR Warning: PEAR_Installer::download() is deprecated " . - "in favor of PEAR_Downloader class", E_USER_WARNING); - return $ret; - } - - function _parsePackageXml(&$descfile) - { - // Parse xml file - $pkg = new PEAR_PackageFile($this->config, $this->debug); - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $p = &$pkg->fromAnyFile($descfile, PEAR_VALIDATE_INSTALLING); - PEAR::staticPopErrorHandling(); - if (PEAR::isError($p)) { - if (is_array($p->getUserInfo())) { - foreach ($p->getUserInfo() as $err) { - $loglevel = $err['level'] == 'error' ? 0 : 1; - if (!isset($this->_options['soft'])) { - $this->log($loglevel, ucfirst($err['level']) . ': ' . $err['message']); - } - } - } - return $this->raiseError('Installation failed: invalid package file'); - } - - $descfile = $p->getPackageFile(); - return $p; - } - - /** - * Set the list of PEAR_Downloader_Package objects to allow more sane - * dependency validation - * @param array - */ - function setDownloadedPackages(&$pkgs) - { - PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - $err = $this->analyzeDependencies($pkgs); - PEAR::popErrorHandling(); - if (PEAR::isError($err)) { - return $err; - } - $this->_downloadedPackages = &$pkgs; - } - - /** - * Set the list of PEAR_Downloader_Package objects to allow more sane - * dependency validation - * @param array - */ - function setUninstallPackages(&$pkgs) - { - $this->_downloadedPackages = &$pkgs; - } - - function getInstallPackages() - { - return $this->_downloadedPackages; - } - - /** - * Installs the files within the package file specified. - * - * @param string|PEAR_Downloader_Package $pkgfile path to the package file, - * or a pre-initialized packagefile object - * @param array $options - * recognized options: - * - installroot : optional prefix directory for installation - * - force : force installation - * - register-only : update registry but don't install files - * - upgrade : upgrade existing install - * - soft : fail silently - * - nodeps : ignore dependency conflicts/missing dependencies - * - alldeps : install all dependencies - * - onlyreqdeps : install only required dependencies - * - * @return array|PEAR_Error package info if successful - */ - function install($pkgfile, $options = array()) - { - $this->_options = $options; - $this->_registry = &$this->config->getRegistry(); - if (is_object($pkgfile)) { - $dlpkg = &$pkgfile; - $pkg = $pkgfile->getPackageFile(); - $pkgfile = $pkg->getArchiveFile(); - $descfile = $pkg->getPackageFile(); - } else { - $descfile = $pkgfile; - $pkg = $this->_parsePackageXml($descfile); - if (PEAR::isError($pkg)) { - return $pkg; - } - } - - $tmpdir = dirname($descfile); - if (realpath($descfile) != realpath($pkgfile)) { - // Use the temp_dir since $descfile can contain the download dir path - $tmpdir = $this->config->get('temp_dir', null, 'pear.php.net'); - $tmpdir = System::mktemp('-d -t "' . $tmpdir . '"'); - - $tar = new Archive_Tar($pkgfile); - if (!$tar->extract($tmpdir)) { - return $this->raiseError("unable to unpack $pkgfile"); - } - } - - $name = $pkg->getName(); - $channel = $pkg->getChannel(); - if (isset($this->_options['packagingroot'])) { - $regdir = $this->_prependPath( - $this->config->get('php_dir', null, 'pear.php.net'), - $this->_options['packagingroot']); - - $packrootphp_dir = $this->_prependPath( - $this->config->get('php_dir', null, $channel), - $this->_options['packagingroot']); - } - - if (isset($options['installroot'])) { - $this->config->setInstallRoot($options['installroot']); - $this->_registry = &$this->config->getRegistry(); - $installregistry = &$this->_registry; - $this->installroot = ''; // all done automagically now - $php_dir = $this->config->get('php_dir', null, $channel); - } else { - $this->config->setInstallRoot(false); - $this->_registry = &$this->config->getRegistry(); - if (isset($this->_options['packagingroot'])) { - $installregistry = &new PEAR_Registry($regdir); - if (!$installregistry->channelExists($channel, true)) { - // we need to fake a channel-discover of this channel - $chanobj = $this->_registry->getChannel($channel, true); - $installregistry->addChannel($chanobj); - } - $php_dir = $packrootphp_dir; - } else { - $installregistry = &$this->_registry; - $php_dir = $this->config->get('php_dir', null, $channel); - } - $this->installroot = ''; - } - - // checks to do when not in "force" mode - if (empty($options['force']) && - (file_exists($this->config->get('php_dir')) && - is_dir($this->config->get('php_dir')))) { - $testp = $channel == 'pear.php.net' ? $name : array($channel, $name); - $instfilelist = $pkg->getInstallationFileList(true); - if (PEAR::isError($instfilelist)) { - return $instfilelist; - } - - // ensure we have the most accurate registry - $installregistry->flushFileMap(); - $test = $installregistry->checkFileMap($instfilelist, $testp, '1.1'); - if (PEAR::isError($test)) { - return $test; - } - - if (sizeof($test)) { - $pkgs = $this->getInstallPackages(); - $found = false; - foreach ($pkgs as $param) { - if ($pkg->isSubpackageOf($param)) { - $found = true; - break; - } - } - - if ($found) { - // subpackages can conflict with earlier versions of parent packages - $parentreg = $installregistry->packageInfo($param->getPackage(), null, $param->getChannel()); - $tmp = $test; - foreach ($tmp as $file => $info) { - if (is_array($info)) { - if (strtolower($info[1]) == strtolower($param->getPackage()) && - strtolower($info[0]) == strtolower($param->getChannel()) - ) { - if (isset($parentreg['filelist'][$file])) { - unset($parentreg['filelist'][$file]); - } else{ - $pos = strpos($file, '/'); - $basedir = substr($file, 0, $pos); - $file2 = substr($file, $pos + 1); - if (isset($parentreg['filelist'][$file2]['baseinstalldir']) - && $parentreg['filelist'][$file2]['baseinstalldir'] === $basedir - ) { - unset($parentreg['filelist'][$file2]); - } - } - - unset($test[$file]); - } - } else { - if (strtolower($param->getChannel()) != 'pear.php.net') { - continue; - } - - if (strtolower($info) == strtolower($param->getPackage())) { - if (isset($parentreg['filelist'][$file])) { - unset($parentreg['filelist'][$file]); - } else{ - $pos = strpos($file, '/'); - $basedir = substr($file, 0, $pos); - $file2 = substr($file, $pos + 1); - if (isset($parentreg['filelist'][$file2]['baseinstalldir']) - && $parentreg['filelist'][$file2]['baseinstalldir'] === $basedir - ) { - unset($parentreg['filelist'][$file2]); - } - } - - unset($test[$file]); - } - } - } - - $pfk = &new PEAR_PackageFile($this->config); - $parentpkg = &$pfk->fromArray($parentreg); - $installregistry->updatePackage2($parentpkg); - } - - if ($param->getChannel() == 'pecl.php.net' && isset($options['upgrade'])) { - $tmp = $test; - foreach ($tmp as $file => $info) { - if (is_string($info)) { - // pear.php.net packages are always stored as strings - if (strtolower($info) == strtolower($param->getPackage())) { - // upgrading existing package - unset($test[$file]); - } - } - } - } - - if (count($test)) { - $msg = "$channel/$name: conflicting files found:\n"; - $longest = max(array_map("strlen", array_keys($test))); - $fmt = "%${longest}s (%s)\n"; - foreach ($test as $file => $info) { - if (!is_array($info)) { - $info = array('pear.php.net', $info); - } - $info = $info[0] . '/' . $info[1]; - $msg .= sprintf($fmt, $file, $info); - } - - if (!isset($options['ignore-errors'])) { - return $this->raiseError($msg); - } - - if (!isset($options['soft'])) { - $this->log(0, "WARNING: $msg"); - } - } - } - } - - $this->startFileTransaction(); - - // Figure out what channel to use and if the package exists or not - $usechannel = $channel; - if ($channel == 'pecl.php.net') { - $test = $installregistry->packageExists($name, $channel); - if (!$test) { - $test = $installregistry->packageExists($name, 'pear.php.net'); - $usechannel = 'pear.php.net'; - } - } else { - $test = $installregistry->packageExists($name, $channel); - } - - // checks to do only when installing new packages - if (empty($options['upgrade']) && empty($options['soft'])) { - if (empty($options['force']) && $test) { - return $this->raiseError("$channel/$name is already installed"); - } - } else { - // Upgrade - if ($test) { - $v1 = $installregistry->packageInfo($name, 'version', $usechannel); - $v2 = $pkg->getVersion(); - $cmp = version_compare("$v1", "$v2", 'gt'); - if (empty($options['force']) && !version_compare("$v2", "$v1", 'gt')) { - return $this->raiseError("upgrade to a newer version ($v2 is not newer than $v1)"); - } - } - } - - // Do cleanups for upgrade and install, remove old release's files first - if ($test && empty($options['register-only'])) { - $err = $this->_deletePackageFiles($name, $usechannel, true); - if (PEAR::isError($err)) { - if (!isset($options['ignore-errors'])) { - return $this->raiseError($err); - } - - if (!isset($options['soft'])) { - $this->log(0, 'WARNING: ' . $err->getMessage()); - } - } else { - $backedup = $err; - } - } - - // Copy files to dest dir - - // info from the package it self we want to access from _installFile - $this->pkginfo = &$pkg; - // used to determine whether we should build any C code - $this->source_files = 0; - - $savechannel = $this->config->get('default_channel'); - if (empty($options['register-only']) && !is_dir($php_dir)) { - if (PEAR::isError(System::mkdir(array('-p'), $php_dir))) { - return $this->raiseError("no installation destination directory '$php_dir'\n"); - } - } - - if (substr($pkgfile, -4) != '.xml') { - $tmpdir .= DIRECTORY_SEPARATOR . $name . '-' . $pkg->getVersion(); - } - - $this->configSet('default_channel', $channel); - // install files - - $filelist = $pkg->getInstallationFilelist(); - if (PEAR::isError($filelist)) { - return $filelist; - } - - $p = &$installregistry->getPackage($name, $channel); - $dirtree = (empty($options['register-only']) && $p) ? $p->getDirTree() : false; - - $pkg->resetFilelist(); - $version = $installregistry->packageInfo($pkg->getPackage(), 'version', $pkg->getChannel()); - $pkg->setLastInstalledVersion($version); - foreach ($filelist as $file => $atts) { - $this->expectError(PEAR_INSTALLER_FAILED); - if ($pkg->getPackagexmlVersion() == '1.0') { - $res = $this->_installFile($file, $atts, $tmpdir, $options); - } else { - $res = $this->_installFile2($pkg, $file, $atts, $tmpdir, $options); - } - $this->popExpect(); - - if (PEAR::isError($res)) { - if (empty($options['ignore-errors'])) { - $this->rollbackFileTransaction(); - if ($res->getMessage() == "file does not exist") { - $this->raiseError("file $file in package.xml does not exist"); - } - - return $this->raiseError($res); - } - - if (!isset($options['soft'])) { - $this->log(0, "Warning: " . $res->getMessage()); - } - } - - $real = isset($atts['attribs']) ? $atts['attribs'] : $atts; - if ($res == PEAR_INSTALLER_OK && $real['role'] != 'src') { - // Register files that were installed - $pkg->installedFile($file, $atts); - } - } - - // compile and install source files - if ($this->source_files > 0 && empty($options['nobuild'])) { - if (!isset($options['__compile_configureoptions'])) { - $options['__compile_configureoptions'] = array(); - } - - if (PEAR::isError($err = - $this->_compileSourceFiles($savechannel, $pkg, $options['__compile_configureoptions']))) { - return $err; - } - } - - if (isset($backedup)) { - $this->_removeBackups($backedup); - } - - if (!$this->commitFileTransaction()) { - $this->rollbackFileTransaction(); - $this->configSet('default_channel', $savechannel); - return $this->raiseError("commit failed", PEAR_INSTALLER_FAILED); - } - - // See if package already exists - $usechannel = $channel; - if ($channel == 'pecl.php.net') { - $test = $installregistry->packageExists($name, $channel); - if (!$test) { - $test = $installregistry->packageExists($name, 'pear.php.net'); - $usechannel = 'pear.php.net'; - } - } else { - $test = $installregistry->packageExists($name, $channel); - } - - $ret = false; - $installphase = 'install'; - $oldversion = false; - // Register that the package is installed - if (empty($options['upgrade'])) { - // if 'force' is used, replace the info in registry - if (!empty($options['force']) && $test) { - $oldversion = $installregistry->packageInfo($name, 'version', $usechannel); - $installregistry->deletePackage($name, $usechannel); - } - $ret = $installregistry->addPackage2($pkg); - } else { - if ($dirtree) { - $this->startFileTransaction(); - // attempt to delete empty directories - uksort($dirtree, array($this, '_sortDirs')); - foreach($dirtree as $dir => $notused) { - $this->addFileOperation('rmdir', array($dir)); - } - $this->commitFileTransaction(); - } - - // new: upgrade installs a package if it isn't installed - if (!$test) { - $ret = $installregistry->addPackage2($pkg); - } else { - if ($usechannel != $channel) { - $installregistry->deletePackage($name, $usechannel); - $ret = $installregistry->addPackage2($pkg); - } else { - $ret = $installregistry->updatePackage2($pkg); - } - $installphase = 'upgrade'; - } - } - - if (!$ret) { - $this->configSet('default_channel', $savechannel); - return $this->raiseError("Adding package $channel/$name to registry failed"); - } - // }}} - - $this->configSet('default_channel', $savechannel); - if (class_exists('PEAR_Task_Common')) { // this is auto-included if any tasks exist - if (PEAR_Task_Common::hasPostinstallTasks()) { - PEAR_Task_Common::runPostinstallTasks($installphase); - } - } - - return $pkg->toArray(true); - } - - /** - * @param string - * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 - */ - function _compileSourceFiles($savechannel, &$filelist, $configure_options = array()) - { - require_once 'PEAR/Builder.php'; - $this->log(1, "$this->source_files source files, building"); - $bob = &new PEAR_Builder($this->ui); - $bob->debug = $this->debug; - $built = $bob->build($filelist, array(&$this, '_buildCallback'), $configure_options); - if (PEAR::isError($built)) { - $this->rollbackFileTransaction(); - $this->configSet('default_channel', $savechannel); - return $built; - } - - $this->log(1, "\nBuild process completed successfully"); - foreach ($built as $ext) { - $bn = basename($ext['file']); - list($_ext_name, $_ext_suff) = explode('.', $bn); - if ($_ext_suff == '.so' || $_ext_suff == '.dll') { - if (extension_loaded($_ext_name)) { - $this->raiseError("Extension '$_ext_name' already loaded. " . - 'Please unload it in your php.ini file ' . - 'prior to install or upgrade'); - } - $role = 'ext'; - } else { - $role = 'src'; - } - - $dest = $ext['dest']; - $packagingroot = ''; - if (isset($this->_options['packagingroot'])) { - $packagingroot = $this->_options['packagingroot']; - } - - $copyto = $this->_prependPath($dest, $packagingroot); - $extra = $copyto != $dest ? " as '$copyto'" : ''; - $this->log(1, "Installing '$dest'$extra"); - - $copydir = dirname($copyto); - // pretty much nothing happens if we are only registering the install - if (empty($this->_options['register-only'])) { - if (!file_exists($copydir) || !is_dir($copydir)) { - if (!$this->mkDirHier($copydir)) { - return $this->raiseError("failed to mkdir $copydir", - PEAR_INSTALLER_FAILED); - } - - $this->log(3, "+ mkdir $copydir"); - } - - if (!@copy($ext['file'], $copyto)) { - return $this->raiseError("failed to write $copyto ($php_errormsg)", PEAR_INSTALLER_FAILED); - } - - $this->log(3, "+ cp $ext[file] $copyto"); - $this->addFileOperation('rename', array($ext['file'], $copyto)); - if (!OS_WINDOWS) { - $mode = 0666 & ~(int)octdec($this->config->get('umask')); - $this->addFileOperation('chmod', array($mode, $copyto)); - if (!@chmod($copyto, $mode)) { - $this->log(0, "failed to change mode of $copyto ($php_errormsg)"); - } - } - } - - $data = array( - 'role' => $role, - 'name' => $bn, - 'installed_as' => $dest, - 'php_api' => $ext['php_api'], - 'zend_mod_api' => $ext['zend_mod_api'], - 'zend_ext_api' => $ext['zend_ext_api'], - ); - - if ($filelist->getPackageXmlVersion() == '1.0') { - $filelist->installedFile($bn, $data); - } else { - $filelist->installedFile($bn, array('attribs' => $data)); - } - } - } - - function &getUninstallPackages() - { - return $this->_downloadedPackages; - } - - /** - * Uninstall a package - * - * This method removes all files installed by the application, and then - * removes any empty directories. - * @param string package name - * @param array Command-line options. Possibilities include: - * - * - installroot: base installation dir, if not the default - * - register-only : update registry but don't remove files - * - nodeps: do not process dependencies of other packages to ensure - * uninstallation does not break things - */ - function uninstall($package, $options = array()) - { - $installRoot = isset($options['installroot']) ? $options['installroot'] : ''; - $this->config->setInstallRoot($installRoot); - - $this->installroot = ''; - $this->_registry = &$this->config->getRegistry(); - if (is_object($package)) { - $channel = $package->getChannel(); - $pkg = $package; - $package = $pkg->getPackage(); - } else { - $pkg = false; - $info = $this->_registry->parsePackageName($package, - $this->config->get('default_channel')); - $channel = $info['channel']; - $package = $info['package']; - } - - $savechannel = $this->config->get('default_channel'); - $this->configSet('default_channel', $channel); - if (!is_object($pkg)) { - $pkg = $this->_registry->getPackage($package, $channel); - } - - if (!$pkg) { - $this->configSet('default_channel', $savechannel); - return $this->raiseError($this->_registry->parsedPackageNameToString( - array( - 'channel' => $channel, - 'package' => $package - ), true) . ' not installed'); - } - - if ($pkg->getInstalledBinary()) { - // this is just an alias for a binary package - return $this->_registry->deletePackage($package, $channel); - } - - $filelist = $pkg->getFilelist(); - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - if (!class_exists('PEAR_Dependency2')) { - require_once 'PEAR/Dependency2.php'; - } - - $depchecker = &new PEAR_Dependency2($this->config, $options, - array('channel' => $channel, 'package' => $package), - PEAR_VALIDATE_UNINSTALLING); - $e = $depchecker->validatePackageUninstall($this); - PEAR::staticPopErrorHandling(); - if (PEAR::isError($e)) { - if (!isset($options['ignore-errors'])) { - return $this->raiseError($e); - } - - if (!isset($options['soft'])) { - $this->log(0, 'WARNING: ' . $e->getMessage()); - } - } elseif (is_array($e)) { - if (!isset($options['soft'])) { - $this->log(0, $e[0]); - } - } - - $this->pkginfo = &$pkg; - // pretty much nothing happens if we are only registering the uninstall - if (empty($options['register-only'])) { - // Delete the files - $this->startFileTransaction(); - PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - if (PEAR::isError($err = $this->_deletePackageFiles($package, $channel))) { - PEAR::popErrorHandling(); - $this->rollbackFileTransaction(); - $this->configSet('default_channel', $savechannel); - if (!isset($options['ignore-errors'])) { - return $this->raiseError($err); - } - - if (!isset($options['soft'])) { - $this->log(0, 'WARNING: ' . $err->getMessage()); - } - } else { - PEAR::popErrorHandling(); - } - - if (!$this->commitFileTransaction()) { - $this->rollbackFileTransaction(); - if (!isset($options['ignore-errors'])) { - return $this->raiseError("uninstall failed"); - } - - if (!isset($options['soft'])) { - $this->log(0, 'WARNING: uninstall failed'); - } - } else { - $this->startFileTransaction(); - $dirtree = $pkg->getDirTree(); - if ($dirtree === false) { - $this->configSet('default_channel', $savechannel); - return $this->_registry->deletePackage($package, $channel); - } - - // attempt to delete empty directories - uksort($dirtree, array($this, '_sortDirs')); - foreach($dirtree as $dir => $notused) { - $this->addFileOperation('rmdir', array($dir)); - } - - if (!$this->commitFileTransaction()) { - $this->rollbackFileTransaction(); - if (!isset($options['ignore-errors'])) { - return $this->raiseError("uninstall failed"); - } - - if (!isset($options['soft'])) { - $this->log(0, 'WARNING: uninstall failed'); - } - } - } - } - - $this->configSet('default_channel', $savechannel); - // Register that the package is no longer installed - return $this->_registry->deletePackage($package, $channel); - } - - /** - * Sort a list of arrays of array(downloaded packagefilename) by dependency. - * - * It also removes duplicate dependencies - * @param array an array of PEAR_PackageFile_v[1/2] objects - * @return array|PEAR_Error array of array(packagefilename, package.xml contents) - */ - function sortPackagesForUninstall(&$packages) - { - $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->config); - if (PEAR::isError($this->_dependencyDB)) { - return $this->_dependencyDB; - } - usort($packages, array(&$this, '_sortUninstall')); - } - - function _sortUninstall($a, $b) - { - if (!$a->getDeps() && !$b->getDeps()) { - return 0; // neither package has dependencies, order is insignificant - } - if ($a->getDeps() && !$b->getDeps()) { - return -1; // $a must be installed after $b because $a has dependencies - } - if (!$a->getDeps() && $b->getDeps()) { - return 1; // $b must be installed after $a because $b has dependencies - } - // both packages have dependencies - if ($this->_dependencyDB->dependsOn($a, $b)) { - return -1; - } - if ($this->_dependencyDB->dependsOn($b, $a)) { - return 1; - } - return 0; - } - - function _sortDirs($a, $b) - { - if (strnatcmp($a, $b) == -1) return 1; - if (strnatcmp($a, $b) == 1) return -1; - return 0; - } - - function _buildCallback($what, $data) - { - if (($what == 'cmdoutput' && $this->debug > 1) || - ($what == 'output' && $this->debug > 0)) { - $this->ui->outputData(rtrim($data), 'build'); - } - } -} diff --git a/pear/PEAR/Installer/Role.php b/pear/PEAR/Installer/Role.php deleted file mode 100644 index 2eba3da..0000000 --- a/pear/PEAR/Installer/Role.php +++ /dev/null @@ -1,267 +0,0 @@ - - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a1 - */ - -/** - * base class for installer roles - */ -require_once 'PEAR/Installer/Role/Common.php'; -require_once 'PEAR/XMLParser.php'; -/** - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a1 - */ -class PEAR_Installer_Role -{ - /** - * Set up any additional configuration variables that file roles require - * - * Never call this directly, it is called by the PEAR_Config constructor - * @param PEAR_Config - */ - public static function initializeConfig(&$config) - { - if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) { - PEAR_Installer_Role::registerRoles(); - } - - foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $class => $info) { - if (!$info['config_vars']) { - continue; - } - - $config->_addConfigVars($class, $info['config_vars']); - } - } - - /** - * @param PEAR_PackageFile_v2 - * @param string role name - * @param PEAR_Config - * @return PEAR_Installer_Role_Common - */ - public static function &factory($pkg, $role, &$config) - { - if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) { - PEAR_Installer_Role::registerRoles(); - } - - if (!in_array($role, PEAR_Installer_Role::getValidRoles($pkg->getPackageType()))) { - $a = false; - return $a; - } - - $a = 'PEAR_Installer_Role_' . ucfirst($role); - if (!class_exists($a)) { - require_once str_replace('_', '/', $a) . '.php'; - } - - $b = new $a($config); - return $b; - } - - /** - * Get a list of file roles that are valid for the particular release type. - * - * For instance, src files serve no purpose in regular php releases. - * @param string - * @param bool clear cache - * @return array - */ - public static function getValidRoles($release, $clear = false) - { - if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) { - PEAR_Installer_Role::registerRoles(); - } - - static $ret = array(); - if ($clear) { - $ret = array(); - } - - if (isset($ret[$release])) { - return $ret[$release]; - } - - $ret[$release] = array(); - foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) { - if (in_array($release, $okreleases['releasetypes'])) { - $ret[$release][] = strtolower(str_replace('PEAR_Installer_Role_', '', $role)); - } - } - - return $ret[$release]; - } - - /** - * Get a list of roles that require their files to be installed - * - * Most roles must be installed, but src and package roles, for instance - * are pseudo-roles. src files are compiled into a new extension. Package - * roles are actually fully bundled releases of a package - * @param bool clear cache - * @return array - */ - public static function getInstallableRoles($clear = false) - { - if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) { - PEAR_Installer_Role::registerRoles(); - } - - static $ret; - if ($clear) { - unset($ret); - } - - if (isset($ret)) { - return $ret; - } - - $ret = array(); - foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) { - if ($okreleases['installable']) { - $ret[] = strtolower(str_replace('PEAR_Installer_Role_', '', $role)); - } - } - - return $ret; - } - - /** - * Return an array of roles that are affected by the baseinstalldir attribute - * - * Most roles ignore this attribute, and instead install directly into: - * PackageName/filepath - * so a tests file tests/file.phpt is installed into PackageName/tests/filepath.php - * @param bool clear cache - * @return array - */ - public static function getBaseinstallRoles($clear = false) - { - if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) { - PEAR_Installer_Role::registerRoles(); - } - - static $ret; - if ($clear) { - unset($ret); - } - - if (isset($ret)) { - return $ret; - } - - $ret = array(); - foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) { - if ($okreleases['honorsbaseinstall']) { - $ret[] = strtolower(str_replace('PEAR_Installer_Role_', '', $role)); - } - } - - return $ret; - } - - /** - * Return an array of file roles that should be analyzed for PHP content at package time, - * like the "php" role. - * @param bool clear cache - * @return array - */ - public static function getPhpRoles($clear = false) - { - if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) { - PEAR_Installer_Role::registerRoles(); - } - - static $ret; - if ($clear) { - unset($ret); - } - - if (isset($ret)) { - return $ret; - } - - $ret = array(); - foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) { - if ($okreleases['phpfile']) { - $ret[] = strtolower(str_replace('PEAR_Installer_Role_', '', $role)); - } - } - - return $ret; - } - - /** - * Scan through the Command directory looking for classes - * and see what commands they implement. - * @param string which directory to look for classes, defaults to - * the Installer/Roles subdirectory of - * the directory from where this file (__FILE__) is - * included. - * - * @return bool TRUE on success, a PEAR error on failure - */ - public static function registerRoles($dir = null) - { - $GLOBALS['_PEAR_INSTALLER_ROLES'] = array(); - $parser = new PEAR_XMLParser; - if ($dir === null) { - $dir = dirname(__FILE__) . '/Role'; - } - - if (!file_exists($dir) || !is_dir($dir)) { - return PEAR::raiseError("registerRoles: opendir($dir) failed: does not exist/is not directory"); - } - - $dp = @opendir($dir); - if (empty($dp)) { - return PEAR::raiseError("registerRoles: opendir($dir) failed: $php_errmsg"); - } - - while ($entry = readdir($dp)) { - if ($entry{0} == '.' || substr($entry, -4) != '.xml') { - continue; - } - - $class = "PEAR_Installer_Role_".substr($entry, 0, -4); - // List of roles - if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'][$class])) { - $file = "$dir/$entry"; - $parser->parse(file_get_contents($file)); - $data = $parser->getData(); - if (!is_array($data['releasetypes'])) { - $data['releasetypes'] = array($data['releasetypes']); - } - - $GLOBALS['_PEAR_INSTALLER_ROLES'][$class] = $data; - } - } - - closedir($dp); - ksort($GLOBALS['_PEAR_INSTALLER_ROLES']); - PEAR_Installer_Role::getBaseinstallRoles(true); - PEAR_Installer_Role::getInstallableRoles(true); - PEAR_Installer_Role::getPhpRoles(true); - PEAR_Installer_Role::getValidRoles('****', true); - return true; - } -} diff --git a/pear/PEAR/Installer/Role/Cfg.php b/pear/PEAR/Installer/Role/Cfg.php deleted file mode 100644 index 23af39c..0000000 --- a/pear/PEAR/Installer/Role/Cfg.php +++ /dev/null @@ -1,106 +0,0 @@ - - * @copyright 2007-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.7.0 - */ - -/** - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 2007-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.7.0 - */ -class PEAR_Installer_Role_Cfg extends PEAR_Installer_Role_Common -{ - /** - * @var PEAR_Installer - */ - var $installer; - - /** - * the md5 of the original file - * - * @var unknown_type - */ - var $md5 = null; - - /** - * Do any unusual setup here - * @param PEAR_Installer - * @param PEAR_PackageFile_v2 - * @param array file attributes - * @param string file name - */ - function setup(&$installer, $pkg, $atts, $file) - { - $this->installer = &$installer; - $reg = &$this->installer->config->getRegistry(); - $package = $reg->getPackage($pkg->getPackage(), $pkg->getChannel()); - if ($package) { - $filelist = $package->getFilelist(); - if (isset($filelist[$file]) && isset($filelist[$file]['md5sum'])) { - $this->md5 = $filelist[$file]['md5sum']; - } - } - } - - function processInstallation($pkg, $atts, $file, $tmp_path, $layer = null) - { - $test = parent::processInstallation($pkg, $atts, $file, $tmp_path, $layer); - if (@file_exists($test[2]) && @file_exists($test[3])) { - $md5 = md5_file($test[2]); - // configuration has already been installed, check for mods - if ($md5 !== $this->md5 && $md5 !== md5_file($test[3])) { - // configuration has been modified, so save our version as - // configfile-version - $old = $test[2]; - $test[2] .= '.new-' . $pkg->getVersion(); - // backup original and re-install it - PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - $tmpcfg = $this->config->get('temp_dir'); - $newloc = System::mkdir(array('-p', $tmpcfg)); - if (!$newloc) { - // try temp_dir - $newloc = System::mktemp(array('-d')); - if (!$newloc || PEAR::isError($newloc)) { - PEAR::popErrorHandling(); - return PEAR::raiseError('Could not save existing configuration file '. - $old . ', unable to install. Please set temp_dir ' . - 'configuration variable to a writeable location and try again'); - } - } else { - $newloc = $tmpcfg; - } - - $temp_file = $newloc . DIRECTORY_SEPARATOR . uniqid('savefile'); - if (!@copy($old, $temp_file)) { - PEAR::popErrorHandling(); - return PEAR::raiseError('Could not save existing configuration file '. - $old . ', unable to install. Please set temp_dir ' . - 'configuration variable to a writeable location and try again'); - } - - PEAR::popErrorHandling(); - $this->installer->log(0, "WARNING: configuration file $old is being installed as $test[2], you should manually merge in changes to the existing configuration file"); - $this->installer->addFileOperation('rename', array($temp_file, $old, false)); - $this->installer->addFileOperation('delete', array($temp_file)); - } - } - - return $test; - } -} \ No newline at end of file diff --git a/pear/PEAR/Installer/Role/Cfg.xml b/pear/PEAR/Installer/Role/Cfg.xml deleted file mode 100644 index 7a415dc..0000000 --- a/pear/PEAR/Installer/Role/Cfg.xml +++ /dev/null @@ -1,15 +0,0 @@ - - php - extsrc - extbin - zendextsrc - zendextbin - 1 - cfg_dir - - 1 - - - - - \ No newline at end of file diff --git a/pear/PEAR/Installer/Role/Common.php b/pear/PEAR/Installer/Role/Common.php deleted file mode 100644 index b2b2a61..0000000 --- a/pear/PEAR/Installer/Role/Common.php +++ /dev/null @@ -1,189 +0,0 @@ - - * @copyright 1997-2006 The PHP Group - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a1 - */ -/** - * Base class for all installation roles. - * - * This class allows extensibility of file roles. Packages with complex - * customization can now provide custom file roles along with the possibility of - * adding configuration values to match. - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a1 - */ -class PEAR_Installer_Role_Common -{ - /** - * @var PEAR_Config - * @access protected - */ - var $config; - - /** - * @param PEAR_Config - */ - function PEAR_Installer_Role_Common(&$config) - { - $this->config = $config; - } - - /** - * Retrieve configuration information about a file role from its XML info - * - * @param string $role Role Classname, as in "PEAR_Installer_Role_Data" - * @return array - */ - function getInfo($role) - { - if (empty($GLOBALS['_PEAR_INSTALLER_ROLES'][$role])) { - return PEAR::raiseError('Unknown Role class: "' . $role . '"'); - } - return $GLOBALS['_PEAR_INSTALLER_ROLES'][$role]; - } - - /** - * This is called for each file to set up the directories and files - * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 - * @param array attributes from the tag - * @param string file name - * @return array an array consisting of: - * - * 1 the original, pre-baseinstalldir installation directory - * 2 the final installation directory - * 3 the full path to the final location of the file - * 4 the location of the pre-installation file - */ - function processInstallation($pkg, $atts, $file, $tmp_path, $layer = null) - { - $role = $this->getRoleFromClass(); - $info = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . $role); - if (PEAR::isError($info)) { - return $info; - } - - if (!$info['locationconfig']) { - return false; - } - - if ($info['honorsbaseinstall']) { - $dest_dir = $save_destdir = $this->config->get($info['locationconfig'], $layer, - $pkg->getChannel()); - if (!empty($atts['baseinstalldir'])) { - $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir']; - } - } elseif ($info['unusualbaseinstall']) { - $dest_dir = $save_destdir = $this->config->get($info['locationconfig'], - $layer, $pkg->getChannel()) . DIRECTORY_SEPARATOR . $pkg->getPackage(); - if (!empty($atts['baseinstalldir'])) { - $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir']; - } - } else { - $save_destdir = $dest_dir = $this->config->get($info['locationconfig'], - $layer, $pkg->getChannel()) . DIRECTORY_SEPARATOR . $pkg->getPackage(); - } - - if (dirname($file) != '.' && empty($atts['install-as'])) { - $dest_dir .= DIRECTORY_SEPARATOR . dirname($file); - } - - if (empty($atts['install-as'])) { - $dest_file = $dest_dir . DIRECTORY_SEPARATOR . basename($file); - } else { - $dest_file = $dest_dir . DIRECTORY_SEPARATOR . $atts['install-as']; - } - - $orig_file = $tmp_path . DIRECTORY_SEPARATOR . $file; - - // Clean up the DIRECTORY_SEPARATOR mess - $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR; - - list($dest_dir, $dest_file, $orig_file) = preg_replace(array('!\\\\+!', '!/!', "!$ds2+!"), - array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR, - DIRECTORY_SEPARATOR), - array($dest_dir, $dest_file, $orig_file)); - - return array($save_destdir, $dest_dir, $dest_file, $orig_file); - } - - /** - * Get the name of the configuration variable that specifies the location of this file - * @return string|false - */ - function getLocationConfig() - { - $role = ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this)))); - $info = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . $role); - if (PEAR::isError($info)) { - return $info; - } - - return $info['locationconfig']; - } - - /** - * Do any unusual setup here - * @param PEAR_Installer - * @param PEAR_PackageFile_v2 - * @param array file attributes - * @param string file name - */ - function setup(&$installer, $pkg, $atts, $file) - { - } - - function isExecutable() - { - $role = $this->getRoleFromClass(); - $info = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . $role); - if (PEAR::isError($info)) { - return $info; - } - - return $info['executable']; - } - - function isInstallable() - { - $role = $this->getRoleFromClass(); - $info = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . $role); - if (PEAR::isError($info)) { - return $info; - } - - return $info['installable']; - } - - function isExtension() - { - $role = $this->getRoleFromClass(); - $info = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' . $role); - if (PEAR::isError($info)) { - return $info; - } - - return $info['phpextension']; - } - - function getRoleFromClass() - { - $lower = strtolower(get_class($this)); - return ucfirst(str_replace('pear_installer_role_', '', $lower)); - } -} \ No newline at end of file diff --git a/pear/PEAR/Installer/Role/Data.php b/pear/PEAR/Installer/Role/Data.php deleted file mode 100644 index 27d05d4..0000000 --- a/pear/PEAR/Installer/Role/Data.php +++ /dev/null @@ -1,28 +0,0 @@ - - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a1 - */ - -/** - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a1 - */ -class PEAR_Installer_Role_Data extends PEAR_Installer_Role_Common {} -?> \ No newline at end of file diff --git a/pear/PEAR/Installer/Role/Data.xml b/pear/PEAR/Installer/Role/Data.xml deleted file mode 100644 index eae6372..0000000 --- a/pear/PEAR/Installer/Role/Data.xml +++ /dev/null @@ -1,15 +0,0 @@ - - php - extsrc - extbin - zendextsrc - zendextbin - 1 - data_dir - - - - - - - \ No newline at end of file diff --git a/pear/PEAR/Installer/Role/Doc.php b/pear/PEAR/Installer/Role/Doc.php deleted file mode 100644 index 5194e6e..0000000 --- a/pear/PEAR/Installer/Role/Doc.php +++ /dev/null @@ -1,28 +0,0 @@ - - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a1 - */ - -/** - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a1 - */ -class PEAR_Installer_Role_Doc extends PEAR_Installer_Role_Common {} -?> \ No newline at end of file diff --git a/pear/PEAR/Installer/Role/Doc.xml b/pear/PEAR/Installer/Role/Doc.xml deleted file mode 100644 index 173afba..0000000 --- a/pear/PEAR/Installer/Role/Doc.xml +++ /dev/null @@ -1,15 +0,0 @@ - - php - extsrc - extbin - zendextsrc - zendextbin - 1 - doc_dir - - - - - - - \ No newline at end of file diff --git a/pear/PEAR/Installer/Role/Ext.php b/pear/PEAR/Installer/Role/Ext.php deleted file mode 100644 index ad8988c..0000000 --- a/pear/PEAR/Installer/Role/Ext.php +++ /dev/null @@ -1,28 +0,0 @@ - - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a1 - */ - -/** - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a1 - */ -class PEAR_Installer_Role_Ext extends PEAR_Installer_Role_Common {} -?> \ No newline at end of file diff --git a/pear/PEAR/Installer/Role/Ext.xml b/pear/PEAR/Installer/Role/Ext.xml deleted file mode 100644 index e2940fe..0000000 --- a/pear/PEAR/Installer/Role/Ext.xml +++ /dev/null @@ -1,12 +0,0 @@ - - extbin - zendextbin - 1 - ext_dir - 1 - - - - 1 - - \ No newline at end of file diff --git a/pear/PEAR/Installer/Role/Php.php b/pear/PEAR/Installer/Role/Php.php deleted file mode 100644 index ee7f935..0000000 --- a/pear/PEAR/Installer/Role/Php.php +++ /dev/null @@ -1,28 +0,0 @@ - - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a1 - */ - -/** - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a1 - */ -class PEAR_Installer_Role_Php extends PEAR_Installer_Role_Common {} -?> \ No newline at end of file diff --git a/pear/PEAR/Installer/Role/Php.xml b/pear/PEAR/Installer/Role/Php.xml deleted file mode 100644 index 6b9a0e6..0000000 --- a/pear/PEAR/Installer/Role/Php.xml +++ /dev/null @@ -1,15 +0,0 @@ - - php - extsrc - extbin - zendextsrc - zendextbin - 1 - php_dir - 1 - - 1 - - - - \ No newline at end of file diff --git a/pear/PEAR/Installer/Role/Script.php b/pear/PEAR/Installer/Role/Script.php deleted file mode 100644 index 8259305..0000000 --- a/pear/PEAR/Installer/Role/Script.php +++ /dev/null @@ -1,28 +0,0 @@ - - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a1 - */ - -/** - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a1 - */ -class PEAR_Installer_Role_Script extends PEAR_Installer_Role_Common {} -?> \ No newline at end of file diff --git a/pear/PEAR/Installer/Role/Script.xml b/pear/PEAR/Installer/Role/Script.xml deleted file mode 100644 index e732cf2..0000000 --- a/pear/PEAR/Installer/Role/Script.xml +++ /dev/null @@ -1,15 +0,0 @@ - - php - extsrc - extbin - zendextsrc - zendextbin - 1 - bin_dir - 1 - - - 1 - - - \ No newline at end of file diff --git a/pear/PEAR/Installer/Role/Src.php b/pear/PEAR/Installer/Role/Src.php deleted file mode 100644 index 3d114d4..0000000 --- a/pear/PEAR/Installer/Role/Src.php +++ /dev/null @@ -1,34 +0,0 @@ - - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a1 - */ - -/** - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a1 - */ -class PEAR_Installer_Role_Src extends PEAR_Installer_Role_Common -{ - function setup(&$installer, $pkg, $atts, $file) - { - $installer->source_files++; - } -} -?> \ No newline at end of file diff --git a/pear/PEAR/Installer/Role/Src.xml b/pear/PEAR/Installer/Role/Src.xml deleted file mode 100644 index 1034834..0000000 --- a/pear/PEAR/Installer/Role/Src.xml +++ /dev/null @@ -1,12 +0,0 @@ - - extsrc - zendextsrc - 1 - temp_dir - - - - - - - \ No newline at end of file diff --git a/pear/PEAR/Installer/Role/Test.php b/pear/PEAR/Installer/Role/Test.php deleted file mode 100644 index 06747f7..0000000 --- a/pear/PEAR/Installer/Role/Test.php +++ /dev/null @@ -1,28 +0,0 @@ - - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a1 - */ - -/** - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a1 - */ -class PEAR_Installer_Role_Test extends PEAR_Installer_Role_Common {} -?> \ No newline at end of file diff --git a/pear/PEAR/Installer/Role/Test.xml b/pear/PEAR/Installer/Role/Test.xml deleted file mode 100644 index 51d5b89..0000000 --- a/pear/PEAR/Installer/Role/Test.xml +++ /dev/null @@ -1,15 +0,0 @@ - - php - extsrc - extbin - zendextsrc - zendextbin - 1 - test_dir - - - - - - - \ No newline at end of file diff --git a/pear/PEAR/Installer/Role/Www.php b/pear/PEAR/Installer/Role/Www.php deleted file mode 100644 index c463c8b..0000000 --- a/pear/PEAR/Installer/Role/Www.php +++ /dev/null @@ -1,28 +0,0 @@ - - * @copyright 2007-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.7.0 - */ - -/** - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 2007-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.7.0 - */ -class PEAR_Installer_Role_Www extends PEAR_Installer_Role_Common {} -?> \ No newline at end of file diff --git a/pear/PEAR/Installer/Role/Www.xml b/pear/PEAR/Installer/Role/Www.xml deleted file mode 100644 index 7598be3..0000000 --- a/pear/PEAR/Installer/Role/Www.xml +++ /dev/null @@ -1,15 +0,0 @@ - - php - extsrc - extbin - zendextsrc - zendextbin - 1 - www_dir - 1 - - - - - - \ No newline at end of file diff --git a/pear/PEAR/PackageFile.php b/pear/PEAR/PackageFile.php deleted file mode 100644 index 2a29145..0000000 --- a/pear/PEAR/PackageFile.php +++ /dev/null @@ -1,503 +0,0 @@ - - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a1 - */ - -/** - * needed for PEAR_VALIDATE_* constants - */ -require_once 'PEAR/Validate.php'; -/** - * Error code if the package.xml tag does not contain a valid version - */ -define('PEAR_PACKAGEFILE_ERROR_NO_PACKAGEVERSION', 1); -/** - * Error code if the package.xml tag version is not supported (version 1.0 and 1.1 are the only supported versions, - * currently - */ -define('PEAR_PACKAGEFILE_ERROR_INVALID_PACKAGEVERSION', 2); -/** - * Abstraction for the package.xml package description file - * - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @PEAR-VER@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a1 - */ -class PEAR_PackageFile -{ - /** - * @var PEAR_Config - */ - var $_config; - var $_debug; - - var $_logger = false; - /** - * @var boolean - */ - var $_rawReturn = false; - - /** - * helper for extracting Archive_Tar errors - * @var array - * @access private - */ - var $_extractErrors = array(); - - /** - * - * @param PEAR_Config $config - * @param ? $debug - * @param string @tmpdir Optional temporary directory for uncompressing - * files - */ - function PEAR_PackageFile(&$config, $debug = false) - { - $this->_config = $config; - $this->_debug = $debug; - } - - /** - * Turn off validation - return a parsed package.xml without checking it - * - * This is used by the package-validate command - */ - function rawReturn() - { - $this->_rawReturn = true; - } - - function setLogger(&$l) - { - $this->_logger = &$l; - } - - /** - * Create a PEAR_PackageFile_Parser_v* of a given version. - * @param int $version - * @return PEAR_PackageFile_Parser_v1|PEAR_PackageFile_Parser_v1 - */ - function &parserFactory($version) - { - if (!in_array($version{0}, array('1', '2'))) { - $a = false; - return $a; - } - - include_once 'PEAR/PackageFile/Parser/v' . $version{0} . '.php'; - $version = $version{0}; - $class = "PEAR_PackageFile_Parser_v$version"; - $a = new $class; - return $a; - } - - /** - * For simpler unit-testing - * @return string - */ - function getClassPrefix() - { - return 'PEAR_PackageFile_v'; - } - - /** - * Create a PEAR_PackageFile_v* of a given version. - * @param int $version - * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|false - */ - function &factory($version) - { - if (!in_array($version{0}, array('1', '2'))) { - $a = false; - return $a; - } - - include_once 'PEAR/PackageFile/v' . $version{0} . '.php'; - $version = $version{0}; - $class = $this->getClassPrefix() . $version; - $a = new $class; - return $a; - } - - /** - * Create a PEAR_PackageFile_v* from its toArray() method - * - * WARNING: no validation is performed, the array is assumed to be valid, - * always parse from xml if you want validation. - * @param array $arr - * @return PEAR_PackageFileManager_v1|PEAR_PackageFileManager_v2|PEAR_Error - * @uses factory() to construct the returned object. - */ - function &fromArray($arr) - { - if (isset($arr['xsdversion'])) { - $obj = &$this->factory($arr['xsdversion']); - if ($this->_logger) { - $obj->setLogger($this->_logger); - } - - $obj->setConfig($this->_config); - $obj->fromArray($arr); - return $obj; - } - - // The extensive tests are necessary due to changes in PHP 5.4. - if (is_array($arr) - && array_key_exists('package', $arr) - && is_array($arr['package']) - && array_key_exists('attribs', $arr['package']) - && is_array($arr['package']['attribs']) - && array_key_exists('version', $arr['package']['attribs']) - && !empty($arr['package']['attribs']['version'])) - { - $obj = &$this->factory($arr['package']['attribs']['version']); - } else { - $obj = &$this->factory('1.0'); - } - if (!$obj) { - return PEAR::raiseError('Invalid package version.'); - } - - if ($this->_logger) { - $obj->setLogger($this->_logger); - } - - $obj->setConfig($this->_config); - $obj->fromArray($arr); - return $obj; - } - - /** - * Create a PEAR_PackageFile_v* from an XML string. - * @access public - * @param string $data contents of package.xml file - * @param int $state package state (one of PEAR_VALIDATE_* constants) - * @param string $file full path to the package.xml file (and the files - * it references) - * @param string $archive optional name of the archive that the XML was - * extracted from, if any - * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2 - * @uses parserFactory() to construct a parser to load the package. - */ - function &fromXmlString($data, $state, $file, $archive = false) - { - if (preg_match('/]+version=[\'"]([0-9]+\.[0-9]+)[\'"]/', $data, $packageversion)) { - if (!in_array($packageversion[1], array('1.0', '2.0', '2.1'))) { - return PEAR::raiseError('package.xml version "' . $packageversion[1] . - '" is not supported, only 1.0, 2.0, and 2.1 are supported.'); - } - - $object = &$this->parserFactory($packageversion[1]); - if ($this->_logger) { - $object->setLogger($this->_logger); - } - - $object->setConfig($this->_config); - $pf = $object->parse($data, $file, $archive); - if (PEAR::isError($pf)) { - return $pf; - } - - if ($this->_rawReturn) { - return $pf; - } - - if (!$pf->validate($state)) {; - if ($this->_config->get('verbose') > 0 - && $this->_logger && $pf->getValidationWarnings(false) - ) { - foreach ($pf->getValidationWarnings(false) as $warning) { - $this->_logger->log(0, 'ERROR: ' . $warning['message']); - } - } - - $a = PEAR::raiseError('Parsing of package.xml from file "' . $file . '" failed', - 2, null, null, $pf->getValidationWarnings()); - return $a; - } - - if ($this->_logger && $pf->getValidationWarnings(false)) { - foreach ($pf->getValidationWarnings() as $warning) { - $this->_logger->log(0, 'WARNING: ' . $warning['message']); - } - } - - if (method_exists($pf, 'flattenFilelist')) { - $pf->flattenFilelist(); // for v2 - } - - return $pf; - } elseif (preg_match('/]+version=[\'"]([^"\']+)[\'"]/', $data, $packageversion)) { - $a = PEAR::raiseError('package.xml file "' . $file . - '" has unsupported package.xml version "' . $packageversion[1] . '"'); - return $a; - } else { - if (!class_exists('PEAR_ErrorStack')) { - require_once 'PEAR/ErrorStack.php'; - } - - PEAR_ErrorStack::staticPush('PEAR_PackageFile', - PEAR_PACKAGEFILE_ERROR_NO_PACKAGEVERSION, - 'warning', array('xml' => $data), 'package.xml "' . $file . - '" has no package.xml version'); - $object = &$this->parserFactory('1.0'); - $object->setConfig($this->_config); - $pf = $object->parse($data, $file, $archive); - if (PEAR::isError($pf)) { - return $pf; - } - - if ($this->_rawReturn) { - return $pf; - } - - if (!$pf->validate($state)) { - $a = PEAR::raiseError('Parsing of package.xml from file "' . $file . '" failed', - 2, null, null, $pf->getValidationWarnings()); - return $a; - } - - if ($this->_logger && $pf->getValidationWarnings(false)) { - foreach ($pf->getValidationWarnings() as $warning) { - $this->_logger->log(0, 'WARNING: ' . $warning['message']); - } - } - - if (method_exists($pf, 'flattenFilelist')) { - $pf->flattenFilelist(); // for v2 - } - - return $pf; - } - } - - /** - * Register a temporary file or directory. When the destructor is - * executed, all registered temporary files and directories are - * removed. - * - * @param string $file name of file or directory - * @return void - */ - function addTempFile($file) - { - $GLOBALS['_PEAR_Common_tempfiles'][] = $file; - } - - /** - * Create a PEAR_PackageFile_v* from a compresed Tar or Tgz file. - * @access public - * @param string contents of package.xml file - * @param int package state (one of PEAR_VALIDATE_* constants) - * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2 - * @using Archive_Tar to extract the files - * @using fromPackageFile() to load the package after the package.xml - * file is extracted. - */ - function &fromTgzFile($file, $state) - { - if (!class_exists('Archive_Tar')) { - require_once 'Archive/Tar.php'; - } - - $tar = new Archive_Tar($file); - if ($this->_debug <= 1) { - $tar->pushErrorHandling(PEAR_ERROR_RETURN); - } - - $content = $tar->listContent(); - if ($this->_debug <= 1) { - $tar->popErrorHandling(); - } - - if (!is_array($content)) { - if (is_string($file) && strlen($file < 255) && - (!file_exists($file) || !@is_file($file))) { - $ret = PEAR::raiseError("could not open file \"$file\""); - return $ret; - } - - $file = realpath($file); - $ret = PEAR::raiseError("Could not get contents of package \"$file\"". - '. Invalid tgz file.'); - return $ret; - } - - if (!count($content) && !@is_file($file)) { - $ret = PEAR::raiseError("could not open file \"$file\""); - return $ret; - } - - $xml = null; - $origfile = $file; - foreach ($content as $file) { - $name = $file['filename']; - if ($name == 'package2.xml') { // allow a .tgz to distribute both versions - $xml = $name; - break; - } - - if ($name == 'package.xml') { - $xml = $name; - break; - } elseif (preg_match('/package.xml$/', $name, $match)) { - $xml = $name; - break; - } - } - - $tmpdir = System::mktemp('-t "' . $this->_config->get('temp_dir') . '" -d pear'); - if ($tmpdir === false) { - $ret = PEAR::raiseError("there was a problem with getting the configured temp directory"); - return $ret; - } - - PEAR_PackageFile::addTempFile($tmpdir); - - $this->_extractErrors(); - PEAR::staticPushErrorHandling(PEAR_ERROR_CALLBACK, array($this, '_extractErrors')); - - if (!$xml || !$tar->extractList(array($xml), $tmpdir)) { - $extra = implode("\n", $this->_extractErrors()); - if ($extra) { - $extra = ' ' . $extra; - } - - PEAR::staticPopErrorHandling(); - $ret = PEAR::raiseError('could not extract the package.xml file from "' . - $origfile . '"' . $extra); - return $ret; - } - - PEAR::staticPopErrorHandling(); - $ret = &PEAR_PackageFile::fromPackageFile("$tmpdir/$xml", $state, $origfile); - return $ret; - } - - /** - * helper callback for extracting Archive_Tar errors - * - * @param PEAR_Error|null $err - * @return array - * @access private - */ - function _extractErrors($err = null) - { - static $errors = array(); - if ($err === null) { - $e = $errors; - $errors = array(); - return $e; - } - $errors[] = $err->getMessage(); - } - - /** - * Create a PEAR_PackageFile_v* from a package.xml file. - * - * @access public - * @param string $descfile name of package xml file - * @param int $state package state (one of PEAR_VALIDATE_* constants) - * @param string|false $archive name of the archive this package.xml came - * from, if any - * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2 - * @uses PEAR_PackageFile::fromXmlString to create the oject after the - * XML is loaded from the package.xml file. - */ - function &fromPackageFile($descfile, $state, $archive = false) - { - $fp = false; - if (is_string($descfile) && strlen($descfile) < 255 && - ( - !file_exists($descfile) || !is_file($descfile) || !is_readable($descfile) - || (!$fp = @fopen($descfile, 'r')) - ) - ) { - $a = PEAR::raiseError("Unable to open $descfile"); - return $a; - } - - // read the whole thing so we only get one cdata callback - // for each block of cdata - fclose($fp); - $data = file_get_contents($descfile); - $ret = &PEAR_PackageFile::fromXmlString($data, $state, $descfile, $archive); - return $ret; - } - - /** - * Create a PEAR_PackageFile_v* from a .tgz archive or package.xml file. - * - * This method is able to extract information about a package from a .tgz - * archive or from a XML package definition file. - * - * @access public - * @param string $info file name - * @param int $state package state (one of PEAR_VALIDATE_* constants) - * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2 - * @uses fromPackageFile() if the file appears to be XML - * @uses fromTgzFile() to load all non-XML files - */ - function &fromAnyFile($info, $state) - { - if (is_dir($info)) { - $dir_name = realpath($info); - if (file_exists($dir_name . '/package.xml')) { - $info = PEAR_PackageFile::fromPackageFile($dir_name . '/package.xml', $state); - } elseif (file_exists($dir_name . '/package2.xml')) { - $info = PEAR_PackageFile::fromPackageFile($dir_name . '/package2.xml', $state); - } else { - $info = PEAR::raiseError("No package definition found in '$info' directory"); - } - - return $info; - } - - $fp = false; - if (is_string($info) && strlen($info) < 255 && - (file_exists($info) || ($fp = @fopen($info, 'r'))) - ) { - - if ($fp) { - fclose($fp); - } - - $tmp = substr($info, -4); - if ($tmp == '.xml') { - $info = &PEAR_PackageFile::fromPackageFile($info, $state); - } elseif ($tmp == '.tar' || $tmp == '.tgz') { - $info = &PEAR_PackageFile::fromTgzFile($info, $state); - } else { - $fp = fopen($info, 'r'); - $test = fread($fp, 5); - fclose($fp); - if ($test == ' - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a1 - */ -/** - * needed for PEAR_VALIDATE_* constants - */ -require_once 'PEAR/Validate.php'; -require_once 'System.php'; -require_once 'PEAR/PackageFile/v2.php'; -/** - * This class converts a PEAR_PackageFile_v1 object into any output format. - * - * Supported output formats include array, XML string, and a PEAR_PackageFile_v2 - * object, for converting package.xml 1.0 into package.xml 2.0 with no sweat. - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @PEAR-VER@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a1 - */ -class PEAR_PackageFile_Generator_v1 -{ - /** - * @var PEAR_PackageFile_v1 - */ - var $_packagefile; - function PEAR_PackageFile_Generator_v1(&$packagefile) - { - $this->_packagefile = &$packagefile; - } - - function getPackagerVersion() - { - return '1.9.4'; - } - - /** - * @param PEAR_Packager - * @param bool if true, a .tgz is written, otherwise a .tar is written - * @param string|null directory in which to save the .tgz - * @return string|PEAR_Error location of package or error object - */ - function toTgz(&$packager, $compress = true, $where = null) - { - require_once 'Archive/Tar.php'; - if ($where === null) { - if (!($where = System::mktemp(array('-d')))) { - return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: mktemp failed'); - } - } elseif (!@System::mkDir(array('-p', $where))) { - return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: "' . $where . '" could' . - ' not be created'); - } - if (file_exists($where . DIRECTORY_SEPARATOR . 'package.xml') && - !is_file($where . DIRECTORY_SEPARATOR . 'package.xml')) { - return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: unable to save package.xml as' . - ' "' . $where . DIRECTORY_SEPARATOR . 'package.xml"'); - } - if (!$this->_packagefile->validate(PEAR_VALIDATE_PACKAGING)) { - return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: invalid package file'); - } - $pkginfo = $this->_packagefile->getArray(); - $ext = $compress ? '.tgz' : '.tar'; - $pkgver = $pkginfo['package'] . '-' . $pkginfo['version']; - $dest_package = getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext; - if (file_exists(getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext) && - !is_file(getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext)) { - return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: cannot create tgz file "' . - getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext . '"'); - } - if ($pkgfile = $this->_packagefile->getPackageFile()) { - $pkgdir = dirname(realpath($pkgfile)); - $pkgfile = basename($pkgfile); - } else { - return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: package file object must ' . - 'be created from a real file'); - } - // {{{ Create the package file list - $filelist = array(); - $i = 0; - - foreach ($this->_packagefile->getFilelist() as $fname => $atts) { - $file = $pkgdir . DIRECTORY_SEPARATOR . $fname; - if (!file_exists($file)) { - return PEAR::raiseError("File does not exist: $fname"); - } else { - $filelist[$i++] = $file; - if (!isset($atts['md5sum'])) { - $this->_packagefile->setFileAttribute($fname, 'md5sum', md5_file($file)); - } - $packager->log(2, "Adding file $fname"); - } - } - // }}} - $packagexml = $this->toPackageFile($where, PEAR_VALIDATE_PACKAGING, 'package.xml', true); - if ($packagexml) { - $tar =& new Archive_Tar($dest_package, $compress); - $tar->setErrorHandling(PEAR_ERROR_RETURN); // XXX Don't print errors - // ----- Creates with the package.xml file - $ok = $tar->createModify(array($packagexml), '', $where); - if (PEAR::isError($ok)) { - return $ok; - } elseif (!$ok) { - return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: tarball creation failed'); - } - // ----- Add the content of the package - if (!$tar->addModify($filelist, $pkgver, $pkgdir)) { - return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: tarball creation failed'); - } - return $dest_package; - } - } - - /** - * @param string|null directory to place the package.xml in, or null for a temporary dir - * @param int one of the PEAR_VALIDATE_* constants - * @param string name of the generated file - * @param bool if true, then no analysis will be performed on role="php" files - * @return string|PEAR_Error path to the created file on success - */ - function toPackageFile($where = null, $state = PEAR_VALIDATE_NORMAL, $name = 'package.xml', - $nofilechecking = false) - { - if (!$this->_packagefile->validate($state, $nofilechecking)) { - return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: invalid package.xml', - null, null, null, $this->_packagefile->getValidationWarnings()); - } - if ($where === null) { - if (!($where = System::mktemp(array('-d')))) { - return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: mktemp failed'); - } - } elseif (!@System::mkDir(array('-p', $where))) { - return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: "' . $where . '" could' . - ' not be created'); - } - $newpkgfile = $where . DIRECTORY_SEPARATOR . $name; - $np = @fopen($newpkgfile, 'wb'); - if (!$np) { - return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: unable to save ' . - "$name as $newpkgfile"); - } - fwrite($np, $this->toXml($state, true)); - fclose($np); - return $newpkgfile; - } - - /** - * fix both XML encoding to be UTF8, and replace standard XML entities < > " & ' - * - * @param string $string - * @return string - * @access private - */ - function _fixXmlEncoding($string) - { - if (version_compare(phpversion(), '5.0.0', 'lt')) { - $string = utf8_encode($string); - } - return strtr($string, array( - '&' => '&', - '>' => '>', - '<' => '<', - '"' => '"', - '\'' => ''' )); - } - - /** - * Return an XML document based on the package info (as returned - * by the PEAR_Common::infoFrom* methods). - * - * @return string XML data - */ - function toXml($state = PEAR_VALIDATE_NORMAL, $nofilevalidation = false) - { - $this->_packagefile->setDate(date('Y-m-d')); - if (!$this->_packagefile->validate($state, $nofilevalidation)) { - return false; - } - $pkginfo = $this->_packagefile->getArray(); - static $maint_map = array( - "handle" => "user", - "name" => "name", - "email" => "email", - "role" => "role", - ); - $ret = "\n"; - $ret .= "\n"; - $ret .= "\n" . -" $pkginfo[package]"; - if (isset($pkginfo['extends'])) { - $ret .= "\n$pkginfo[extends]"; - } - $ret .= - "\n ".$this->_fixXmlEncoding($pkginfo['summary'])."\n" . -" ".trim($this->_fixXmlEncoding($pkginfo['description']))."\n \n" . -" \n"; - foreach ($pkginfo['maintainers'] as $maint) { - $ret .= " \n"; - foreach ($maint_map as $idx => $elm) { - $ret .= " <$elm>"; - $ret .= $this->_fixXmlEncoding($maint[$idx]); - $ret .= "\n"; - } - $ret .= " \n"; - } - $ret .= " \n"; - $ret .= $this->_makeReleaseXml($pkginfo, false, $state); - if (isset($pkginfo['changelog']) && count($pkginfo['changelog']) > 0) { - $ret .= " \n"; - foreach ($pkginfo['changelog'] as $oldrelease) { - $ret .= $this->_makeReleaseXml($oldrelease, true); - } - $ret .= " \n"; - } - $ret .= "\n"; - return $ret; - } - - // }}} - // {{{ _makeReleaseXml() - - /** - * Generate part of an XML description with release information. - * - * @param array $pkginfo array with release information - * @param bool $changelog whether the result will be in a changelog element - * - * @return string XML data - * - * @access private - */ - function _makeReleaseXml($pkginfo, $changelog = false, $state = PEAR_VALIDATE_NORMAL) - { - // XXX QUOTE ENTITIES IN PCDATA, OR EMBED IN CDATA BLOCKS!! - $indent = $changelog ? " " : ""; - $ret = "$indent \n"; - if (!empty($pkginfo['version'])) { - $ret .= "$indent $pkginfo[version]\n"; - } - if (!empty($pkginfo['release_date'])) { - $ret .= "$indent $pkginfo[release_date]\n"; - } - if (!empty($pkginfo['release_license'])) { - $ret .= "$indent $pkginfo[release_license]\n"; - } - if (!empty($pkginfo['release_state'])) { - $ret .= "$indent $pkginfo[release_state]\n"; - } - if (!empty($pkginfo['release_notes'])) { - $ret .= "$indent ".trim($this->_fixXmlEncoding($pkginfo['release_notes'])) - ."\n$indent \n"; - } - if (!empty($pkginfo['release_warnings'])) { - $ret .= "$indent ".$this->_fixXmlEncoding($pkginfo['release_warnings'])."\n"; - } - if (isset($pkginfo['release_deps']) && sizeof($pkginfo['release_deps']) > 0) { - $ret .= "$indent \n"; - foreach ($pkginfo['release_deps'] as $dep) { - $ret .= "$indent _fixXmlEncoding($c['name']) . "\""; - if (isset($c['default'])) { - $ret .= " default=\"" . $this->_fixXmlEncoding($c['default']) . "\""; - } - $ret .= " prompt=\"" . $this->_fixXmlEncoding($c['prompt']) . "\""; - $ret .= "/>\n"; - } - $ret .= "$indent \n"; - } - if (isset($pkginfo['provides'])) { - foreach ($pkginfo['provides'] as $key => $what) { - $ret .= "$indent recursiveXmlFilelist($pkginfo['filelist']); - } else { - foreach ($pkginfo['filelist'] as $file => $fa) { - if (!isset($fa['role'])) { - $fa['role'] = ''; - } - $ret .= "$indent _fixXmlEncoding($fa['baseinstalldir']) . '"'; - } - if (isset($fa['md5sum'])) { - $ret .= " md5sum=\"$fa[md5sum]\""; - } - if (isset($fa['platform'])) { - $ret .= " platform=\"$fa[platform]\""; - } - if (!empty($fa['install-as'])) { - $ret .= ' install-as="' . - $this->_fixXmlEncoding($fa['install-as']) . '"'; - } - $ret .= ' name="' . $this->_fixXmlEncoding($file) . '"'; - if (empty($fa['replacements'])) { - $ret .= "/>\n"; - } else { - $ret .= ">\n"; - foreach ($fa['replacements'] as $r) { - $ret .= "$indent $v) { - $ret .= " $k=\"" . $this->_fixXmlEncoding($v) .'"'; - } - $ret .= "/>\n"; - } - $ret .= "$indent \n"; - } - } - } - $ret .= "$indent \n"; - } - $ret .= "$indent \n"; - return $ret; - } - - /** - * @param array - * @access protected - */ - function recursiveXmlFilelist($list) - { - $this->_dirs = array(); - foreach ($list as $file => $attributes) { - $this->_addDir($this->_dirs, explode('/', dirname($file)), $file, $attributes); - } - return $this->_formatDir($this->_dirs); - } - - /** - * @param array - * @param array - * @param string|null - * @param array|null - * @access private - */ - function _addDir(&$dirs, $dir, $file = null, $attributes = null) - { - if ($dir == array() || $dir == array('.')) { - $dirs['files'][basename($file)] = $attributes; - return; - } - $curdir = array_shift($dir); - if (!isset($dirs['dirs'][$curdir])) { - $dirs['dirs'][$curdir] = array(); - } - $this->_addDir($dirs['dirs'][$curdir], $dir, $file, $attributes); - } - - /** - * @param array - * @param string - * @param string - * @access private - */ - function _formatDir($dirs, $indent = '', $curdir = '') - { - $ret = ''; - if (!count($dirs)) { - return ''; - } - if (isset($dirs['dirs'])) { - uksort($dirs['dirs'], 'strnatcasecmp'); - foreach ($dirs['dirs'] as $dir => $contents) { - $usedir = "$curdir/$dir"; - $ret .= "$indent \n"; - $ret .= $this->_formatDir($contents, "$indent ", $usedir); - $ret .= "$indent \n"; - } - } - if (isset($dirs['files'])) { - uksort($dirs['files'], 'strnatcasecmp'); - foreach ($dirs['files'] as $file => $attribs) { - $ret .= $this->_formatFile($file, $attribs, $indent); - } - } - return $ret; - } - - /** - * @param string - * @param array - * @param string - * @access private - */ - function _formatFile($file, $attributes, $indent) - { - $ret = "$indent _fixXmlEncoding($attributes['baseinstalldir']) . '"'; - } - if (isset($attributes['md5sum'])) { - $ret .= " md5sum=\"$attributes[md5sum]\""; - } - if (isset($attributes['platform'])) { - $ret .= " platform=\"$attributes[platform]\""; - } - if (!empty($attributes['install-as'])) { - $ret .= ' install-as="' . - $this->_fixXmlEncoding($attributes['install-as']) . '"'; - } - $ret .= ' name="' . $this->_fixXmlEncoding($file) . '"'; - if (empty($attributes['replacements'])) { - $ret .= "/>\n"; - } else { - $ret .= ">\n"; - foreach ($attributes['replacements'] as $r) { - $ret .= "$indent $v) { - $ret .= " $k=\"" . $this->_fixXmlEncoding($v) .'"'; - } - $ret .= "/>\n"; - } - $ret .= "$indent \n"; - } - return $ret; - } - - // {{{ _unIndent() - - /** - * Unindent given string (?) - * - * @param string $str The string that has to be unindented. - * @return string - * @access private - */ - function _unIndent($str) - { - // remove leading newlines - $str = preg_replace('/^[\r\n]+/', '', $str); - // find whitespace at the beginning of the first line - $indent_len = strspn($str, " \t"); - $indent = substr($str, 0, $indent_len); - $data = ''; - // remove the same amount of whitespace from following lines - foreach (explode("\n", $str) as $line) { - if (substr($line, 0, $indent_len) == $indent) { - $data .= substr($line, $indent_len) . "\n"; - } - } - return $data; - } - - /** - * @return array - */ - function dependenciesToV2() - { - $arr = array(); - $this->_convertDependencies2_0($arr); - return $arr['dependencies']; - } - - /** - * Convert a package.xml version 1.0 into version 2.0 - * - * Note that this does a basic conversion, to allow more advanced - * features like bundles and multiple releases - * @param string the classname to instantiate and return. This must be - * PEAR_PackageFile_v2 or a descendant - * @param boolean if true, only valid, deterministic package.xml 1.0 as defined by the - * strictest parameters will be converted - * @return PEAR_PackageFile_v2|PEAR_Error - */ - function &toV2($class = 'PEAR_PackageFile_v2', $strict = false) - { - if ($strict) { - if (!$this->_packagefile->validate()) { - $a = PEAR::raiseError('invalid package.xml version 1.0 cannot be converted' . - ' to version 2.0', null, null, null, - $this->_packagefile->getValidationWarnings(true)); - return $a; - } - } - - $arr = array( - 'attribs' => array( - 'version' => '2.0', - 'xmlns' => 'http://pear.php.net/dtd/package-2.0', - 'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0', - 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', - 'xsi:schemaLocation' => "http://pear.php.net/dtd/tasks-1.0\n" . -"http://pear.php.net/dtd/tasks-1.0.xsd\n" . -"http://pear.php.net/dtd/package-2.0\n" . -'http://pear.php.net/dtd/package-2.0.xsd', - ), - 'name' => $this->_packagefile->getPackage(), - 'channel' => 'pear.php.net', - ); - $arr['summary'] = $this->_packagefile->getSummary(); - $arr['description'] = $this->_packagefile->getDescription(); - $maintainers = $this->_packagefile->getMaintainers(); - foreach ($maintainers as $maintainer) { - if ($maintainer['role'] != 'lead') { - continue; - } - $new = array( - 'name' => $maintainer['name'], - 'user' => $maintainer['handle'], - 'email' => $maintainer['email'], - 'active' => 'yes', - ); - $arr['lead'][] = $new; - } - - if (!isset($arr['lead'])) { // some people... you know? - $arr['lead'] = array( - 'name' => 'unknown', - 'user' => 'unknown', - 'email' => 'noleadmaintainer@example.com', - 'active' => 'no', - ); - } - - if (count($arr['lead']) == 1) { - $arr['lead'] = $arr['lead'][0]; - } - - foreach ($maintainers as $maintainer) { - if ($maintainer['role'] == 'lead') { - continue; - } - $new = array( - 'name' => $maintainer['name'], - 'user' => $maintainer['handle'], - 'email' => $maintainer['email'], - 'active' => 'yes', - ); - $arr[$maintainer['role']][] = $new; - } - - if (isset($arr['developer']) && count($arr['developer']) == 1) { - $arr['developer'] = $arr['developer'][0]; - } - - if (isset($arr['contributor']) && count($arr['contributor']) == 1) { - $arr['contributor'] = $arr['contributor'][0]; - } - - if (isset($arr['helper']) && count($arr['helper']) == 1) { - $arr['helper'] = $arr['helper'][0]; - } - - $arr['date'] = $this->_packagefile->getDate(); - $arr['version'] = - array( - 'release' => $this->_packagefile->getVersion(), - 'api' => $this->_packagefile->getVersion(), - ); - $arr['stability'] = - array( - 'release' => $this->_packagefile->getState(), - 'api' => $this->_packagefile->getState(), - ); - $licensemap = - array( - 'php' => 'http://www.php.net/license', - 'php license' => 'http://www.php.net/license', - 'lgpl' => 'http://www.gnu.org/copyleft/lesser.html', - 'bsd' => 'http://www.opensource.org/licenses/bsd-license.php', - 'bsd style' => 'http://www.opensource.org/licenses/bsd-license.php', - 'bsd-style' => 'http://www.opensource.org/licenses/bsd-license.php', - 'mit' => 'http://www.opensource.org/licenses/mit-license.php', - 'gpl' => 'http://www.gnu.org/copyleft/gpl.html', - 'apache' => 'http://www.opensource.org/licenses/apache2.0.php' - ); - - if (isset($licensemap[strtolower($this->_packagefile->getLicense())])) { - $arr['license'] = array( - 'attribs' => array('uri' => - $licensemap[strtolower($this->_packagefile->getLicense())]), - '_content' => $this->_packagefile->getLicense() - ); - } else { - // don't use bogus uri - $arr['license'] = $this->_packagefile->getLicense(); - } - - $arr['notes'] = $this->_packagefile->getNotes(); - $temp = array(); - $arr['contents'] = $this->_convertFilelist2_0($temp); - $this->_convertDependencies2_0($arr); - $release = ($this->_packagefile->getConfigureOptions() || $this->_isExtension) ? - 'extsrcrelease' : 'phprelease'; - if ($release == 'extsrcrelease') { - $arr['channel'] = 'pecl.php.net'; - $arr['providesextension'] = $arr['name']; // assumption - } - - $arr[$release] = array(); - if ($this->_packagefile->getConfigureOptions()) { - $arr[$release]['configureoption'] = $this->_packagefile->getConfigureOptions(); - foreach ($arr[$release]['configureoption'] as $i => $opt) { - $arr[$release]['configureoption'][$i] = array('attribs' => $opt); - } - if (count($arr[$release]['configureoption']) == 1) { - $arr[$release]['configureoption'] = $arr[$release]['configureoption'][0]; - } - } - - $this->_convertRelease2_0($arr[$release], $temp); - if ($release == 'extsrcrelease' && count($arr[$release]) > 1) { - // multiple extsrcrelease tags added in PEAR 1.4.1 - $arr['dependencies']['required']['pearinstaller']['min'] = '1.4.1'; - } - - if ($cl = $this->_packagefile->getChangelog()) { - foreach ($cl as $release) { - $rel = array(); - $rel['version'] = - array( - 'release' => $release['version'], - 'api' => $release['version'], - ); - if (!isset($release['release_state'])) { - $release['release_state'] = 'stable'; - } - - $rel['stability'] = - array( - 'release' => $release['release_state'], - 'api' => $release['release_state'], - ); - if (isset($release['release_date'])) { - $rel['date'] = $release['release_date']; - } else { - $rel['date'] = date('Y-m-d'); - } - - if (isset($release['release_license'])) { - if (isset($licensemap[strtolower($release['release_license'])])) { - $uri = $licensemap[strtolower($release['release_license'])]; - } else { - $uri = 'http://www.example.com'; - } - $rel['license'] = array( - 'attribs' => array('uri' => $uri), - '_content' => $release['release_license'] - ); - } else { - $rel['license'] = $arr['license']; - } - - if (!isset($release['release_notes'])) { - $release['release_notes'] = 'no release notes'; - } - - $rel['notes'] = $release['release_notes']; - $arr['changelog']['release'][] = $rel; - } - } - - $ret = new $class; - $ret->setConfig($this->_packagefile->_config); - if (isset($this->_packagefile->_logger) && is_object($this->_packagefile->_logger)) { - $ret->setLogger($this->_packagefile->_logger); - } - - $ret->fromArray($arr); - return $ret; - } - - /** - * @param array - * @param bool - * @access private - */ - function _convertDependencies2_0(&$release, $internal = false) - { - $peardep = array('pearinstaller' => - array('min' => '1.4.0b1')); // this is a lot safer - $required = $optional = array(); - $release['dependencies'] = array('required' => array()); - if ($this->_packagefile->hasDeps()) { - foreach ($this->_packagefile->getDeps() as $dep) { - if (!isset($dep['optional']) || $dep['optional'] == 'no') { - $required[] = $dep; - } else { - $optional[] = $dep; - } - } - foreach (array('required', 'optional') as $arr) { - $deps = array(); - foreach ($$arr as $dep) { - // organize deps by dependency type and name - if (!isset($deps[$dep['type']])) { - $deps[$dep['type']] = array(); - } - if (isset($dep['name'])) { - $deps[$dep['type']][$dep['name']][] = $dep; - } else { - $deps[$dep['type']][] = $dep; - } - } - do { - if (isset($deps['php'])) { - $php = array(); - if (count($deps['php']) > 1) { - $php = $this->_processPhpDeps($deps['php']); - } else { - if (!isset($deps['php'][0])) { - list($key, $blah) = each ($deps['php']); // stupid buggy versions - $deps['php'] = array($blah[0]); - } - $php = $this->_processDep($deps['php'][0]); - if (!$php) { - break; // poor mans throw - } - } - $release['dependencies'][$arr]['php'] = $php; - } - } while (false); - do { - if (isset($deps['pkg'])) { - $pkg = array(); - $pkg = $this->_processMultipleDepsName($deps['pkg']); - if (!$pkg) { - break; // poor mans throw - } - $release['dependencies'][$arr]['package'] = $pkg; - } - } while (false); - do { - if (isset($deps['ext'])) { - $pkg = array(); - $pkg = $this->_processMultipleDepsName($deps['ext']); - $release['dependencies'][$arr]['extension'] = $pkg; - } - } while (false); - // skip sapi - it's not supported so nobody will have used it - // skip os - it's not supported in 1.0 - } - } - if (isset($release['dependencies']['required'])) { - $release['dependencies']['required'] = - array_merge($peardep, $release['dependencies']['required']); - } else { - $release['dependencies']['required'] = $peardep; - } - if (!isset($release['dependencies']['required']['php'])) { - $release['dependencies']['required']['php'] = - array('min' => '4.0.0'); - } - $order = array(); - $bewm = $release['dependencies']['required']; - $order['php'] = $bewm['php']; - $order['pearinstaller'] = $bewm['pearinstaller']; - isset($bewm['package']) ? $order['package'] = $bewm['package'] :0; - isset($bewm['extension']) ? $order['extension'] = $bewm['extension'] :0; - $release['dependencies']['required'] = $order; - } - - /** - * @param array - * @access private - */ - function _convertFilelist2_0(&$package) - { - $ret = array('dir' => - array( - 'attribs' => array('name' => '/'), - 'file' => array() - ) - ); - $package['platform'] = - $package['install-as'] = array(); - $this->_isExtension = false; - foreach ($this->_packagefile->getFilelist() as $name => $file) { - $file['name'] = $name; - if (isset($file['role']) && $file['role'] == 'src') { - $this->_isExtension = true; - } - if (isset($file['replacements'])) { - $repl = $file['replacements']; - unset($file['replacements']); - } else { - unset($repl); - } - if (isset($file['install-as'])) { - $package['install-as'][$name] = $file['install-as']; - unset($file['install-as']); - } - if (isset($file['platform'])) { - $package['platform'][$name] = $file['platform']; - unset($file['platform']); - } - $file = array('attribs' => $file); - if (isset($repl)) { - foreach ($repl as $replace ) { - $file['tasks:replace'][] = array('attribs' => $replace); - } - if (count($repl) == 1) { - $file['tasks:replace'] = $file['tasks:replace'][0]; - } - } - $ret['dir']['file'][] = $file; - } - return $ret; - } - - /** - * Post-process special files with install-as/platform attributes and - * make the release tag. - * - * This complex method follows this work-flow to create the release tags: - * - *
    -     * - if any install-as/platform exist, create a generic release and fill it with
    -     *   o  tags for 
    -     *   o  tags for 
    -     *   o  tags for 
    -     *   o  tags for 
    -     * - create a release for each platform encountered and fill with
    -     *   o  tags for 
    -     *   o  tags for 
    -     *   o  tags for 
    -     *   o  tags for 
    -     *   o  tags for 
    -     *   o  tags for 
    -     *   o  tags for 
    -     * 
    - * - * It does this by accessing the $package parameter, which contains an array with - * indices: - * - * - platform: mapping of file => OS the file should be installed on - * - install-as: mapping of file => installed name - * - osmap: mapping of OS => list of files that should be installed - * on that OS - * - notosmap: mapping of OS => list of files that should not be - * installed on that OS - * - * @param array - * @param array - * @access private - */ - function _convertRelease2_0(&$release, $package) - { - //- if any install-as/platform exist, create a generic release and fill it with - if (count($package['platform']) || count($package['install-as'])) { - $generic = array(); - $genericIgnore = array(); - foreach ($package['install-as'] as $file => $as) { - //o tags for - if (!isset($package['platform'][$file])) { - $generic[] = $file; - continue; - } - //o tags for - if (isset($package['platform'][$file]) && - $package['platform'][$file]{0} == '!') { - $generic[] = $file; - continue; - } - //o tags for - if (isset($package['platform'][$file]) && - $package['platform'][$file]{0} != '!') { - $genericIgnore[] = $file; - continue; - } - } - foreach ($package['platform'] as $file => $platform) { - if (isset($package['install-as'][$file])) { - continue; - } - if ($platform{0} != '!') { - //o tags for - $genericIgnore[] = $file; - } - } - if (count($package['platform'])) { - $oses = $notplatform = $platform = array(); - foreach ($package['platform'] as $file => $os) { - // get a list of oses - if ($os{0} == '!') { - if (isset($oses[substr($os, 1)])) { - continue; - } - $oses[substr($os, 1)] = count($oses); - } else { - if (isset($oses[$os])) { - continue; - } - $oses[$os] = count($oses); - } - } - //- create a release for each platform encountered and fill with - foreach ($oses as $os => $releaseNum) { - $release[$releaseNum]['installconditions']['os']['name'] = $os; - $release[$releaseNum]['filelist'] = array('install' => array(), - 'ignore' => array()); - foreach ($package['install-as'] as $file => $as) { - //o tags for - if (!isset($package['platform'][$file])) { - $release[$releaseNum]['filelist']['install'][] = - array( - 'attribs' => array( - 'name' => $file, - 'as' => $as, - ), - ); - continue; - } - //o tags for - // - if (isset($package['platform'][$file]) && - $package['platform'][$file] == $os) { - $release[$releaseNum]['filelist']['install'][] = - array( - 'attribs' => array( - 'name' => $file, - 'as' => $as, - ), - ); - continue; - } - //o tags for - // - if (isset($package['platform'][$file]) && - $package['platform'][$file] != "!$os" && - $package['platform'][$file]{0} == '!') { - $release[$releaseNum]['filelist']['install'][] = - array( - 'attribs' => array( - 'name' => $file, - 'as' => $as, - ), - ); - continue; - } - //o tags for - // - if (isset($package['platform'][$file]) && - $package['platform'][$file] == "!$os") { - $release[$releaseNum]['filelist']['ignore'][] = - array( - 'attribs' => array( - 'name' => $file, - ), - ); - continue; - } - //o tags for - // - if (isset($package['platform'][$file]) && - $package['platform'][$file]{0} != '!' && - $package['platform'][$file] != $os) { - $release[$releaseNum]['filelist']['ignore'][] = - array( - 'attribs' => array( - 'name' => $file, - ), - ); - continue; - } - } - foreach ($package['platform'] as $file => $platform) { - if (isset($package['install-as'][$file])) { - continue; - } - //o tags for - if ($platform == "!$os") { - $release[$releaseNum]['filelist']['ignore'][] = - array( - 'attribs' => array( - 'name' => $file, - ), - ); - continue; - } - //o tags for - if ($platform{0} != '!' && $platform != $os) { - $release[$releaseNum]['filelist']['ignore'][] = - array( - 'attribs' => array( - 'name' => $file, - ), - ); - } - } - if (!count($release[$releaseNum]['filelist']['install'])) { - unset($release[$releaseNum]['filelist']['install']); - } - if (!count($release[$releaseNum]['filelist']['ignore'])) { - unset($release[$releaseNum]['filelist']['ignore']); - } - } - if (count($generic) || count($genericIgnore)) { - $release[count($oses)] = array(); - if (count($generic)) { - foreach ($generic as $file) { - if (isset($package['install-as'][$file])) { - $installas = $package['install-as'][$file]; - } else { - $installas = $file; - } - $release[count($oses)]['filelist']['install'][] = - array( - 'attribs' => array( - 'name' => $file, - 'as' => $installas, - ) - ); - } - } - if (count($genericIgnore)) { - foreach ($genericIgnore as $file) { - $release[count($oses)]['filelist']['ignore'][] = - array( - 'attribs' => array( - 'name' => $file, - ) - ); - } - } - } - // cleanup - foreach ($release as $i => $rel) { - if (isset($rel['filelist']['install']) && - count($rel['filelist']['install']) == 1) { - $release[$i]['filelist']['install'] = - $release[$i]['filelist']['install'][0]; - } - if (isset($rel['filelist']['ignore']) && - count($rel['filelist']['ignore']) == 1) { - $release[$i]['filelist']['ignore'] = - $release[$i]['filelist']['ignore'][0]; - } - } - if (count($release) == 1) { - $release = $release[0]; - } - } else { - // no platform atts, but some install-as atts - foreach ($package['install-as'] as $file => $value) { - $release['filelist']['install'][] = - array( - 'attribs' => array( - 'name' => $file, - 'as' => $value - ) - ); - } - if (count($release['filelist']['install']) == 1) { - $release['filelist']['install'] = $release['filelist']['install'][0]; - } - } - } - } - - /** - * @param array - * @return array - * @access private - */ - function _processDep($dep) - { - if ($dep['type'] == 'php') { - if ($dep['rel'] == 'has') { - // come on - everyone has php! - return false; - } - } - $php = array(); - if ($dep['type'] != 'php') { - $php['name'] = $dep['name']; - if ($dep['type'] == 'pkg') { - $php['channel'] = 'pear.php.net'; - } - } - switch ($dep['rel']) { - case 'gt' : - $php['min'] = $dep['version']; - $php['exclude'] = $dep['version']; - break; - case 'ge' : - if (!isset($dep['version'])) { - if ($dep['type'] == 'php') { - if (isset($dep['name'])) { - $dep['version'] = $dep['name']; - } - } - } - $php['min'] = $dep['version']; - break; - case 'lt' : - $php['max'] = $dep['version']; - $php['exclude'] = $dep['version']; - break; - case 'le' : - $php['max'] = $dep['version']; - break; - case 'eq' : - $php['min'] = $dep['version']; - $php['max'] = $dep['version']; - break; - case 'ne' : - $php['exclude'] = $dep['version']; - break; - case 'not' : - $php['conflicts'] = 'yes'; - break; - } - return $php; - } - - /** - * @param array - * @return array - */ - function _processPhpDeps($deps) - { - $test = array(); - foreach ($deps as $dep) { - $test[] = $this->_processDep($dep); - } - $min = array(); - $max = array(); - foreach ($test as $dep) { - if (!$dep) { - continue; - } - if (isset($dep['min'])) { - $min[$dep['min']] = count($min); - } - if (isset($dep['max'])) { - $max[$dep['max']] = count($max); - } - } - if (count($min) > 0) { - uksort($min, 'version_compare'); - } - if (count($max) > 0) { - uksort($max, 'version_compare'); - } - if (count($min)) { - // get the highest minimum - $min = array_pop($a = array_flip($min)); - } else { - $min = false; - } - if (count($max)) { - // get the lowest maximum - $max = array_shift($a = array_flip($max)); - } else { - $max = false; - } - if ($min) { - $php['min'] = $min; - } - if ($max) { - $php['max'] = $max; - } - $exclude = array(); - foreach ($test as $dep) { - if (!isset($dep['exclude'])) { - continue; - } - $exclude[] = $dep['exclude']; - } - if (count($exclude)) { - $php['exclude'] = $exclude; - } - return $php; - } - - /** - * process multiple dependencies that have a name, like package deps - * @param array - * @return array - * @access private - */ - function _processMultipleDepsName($deps) - { - $ret = $tests = array(); - foreach ($deps as $name => $dep) { - foreach ($dep as $d) { - $tests[$name][] = $this->_processDep($d); - } - } - - foreach ($tests as $name => $test) { - $max = $min = $php = array(); - $php['name'] = $name; - foreach ($test as $dep) { - if (!$dep) { - continue; - } - if (isset($dep['channel'])) { - $php['channel'] = 'pear.php.net'; - } - if (isset($dep['conflicts']) && $dep['conflicts'] == 'yes') { - $php['conflicts'] = 'yes'; - } - if (isset($dep['min'])) { - $min[$dep['min']] = count($min); - } - if (isset($dep['max'])) { - $max[$dep['max']] = count($max); - } - } - if (count($min) > 0) { - uksort($min, 'version_compare'); - } - if (count($max) > 0) { - uksort($max, 'version_compare'); - } - if (count($min)) { - // get the highest minimum - $min = array_pop($a = array_flip($min)); - } else { - $min = false; - } - if (count($max)) { - // get the lowest maximum - $max = array_shift($a = array_flip($max)); - } else { - $max = false; - } - if ($min) { - $php['min'] = $min; - } - if ($max) { - $php['max'] = $max; - } - $exclude = array(); - foreach ($test as $dep) { - if (!isset($dep['exclude'])) { - continue; - } - $exclude[] = $dep['exclude']; - } - if (count($exclude)) { - $php['exclude'] = $exclude; - } - $ret[] = $php; - } - return $ret; - } -} -?> \ No newline at end of file diff --git a/pear/PEAR/PackageFile/Generator/v2.php b/pear/PEAR/PackageFile/Generator/v2.php deleted file mode 100644 index 8ff90e2..0000000 --- a/pear/PEAR/PackageFile/Generator/v2.php +++ /dev/null @@ -1,893 +0,0 @@ - - * @author Stephan Schmidt (original XML_Serializer code) - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a1 - */ -/** - * file/dir manipulation routines - */ -require_once 'System.php'; -require_once 'XML/Util.php'; - -/** - * This class converts a PEAR_PackageFile_v2 object into any output format. - * - * Supported output formats include array, XML string (using S. Schmidt's - * XML_Serializer, slightly customized) - * @category pear - * @package PEAR - * @author Greg Beaver - * @author Stephan Schmidt (original XML_Serializer code) - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @PEAR-VER@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a1 - */ -class PEAR_PackageFile_Generator_v2 -{ - /** - * default options for the serialization - * @access private - * @var array $_defaultOptions - */ - var $_defaultOptions = array( - 'indent' => ' ', // string used for indentation - 'linebreak' => "\n", // string used for newlines - 'typeHints' => false, // automatically add type hin attributes - 'addDecl' => true, // add an XML declaration - 'defaultTagName' => 'XML_Serializer_Tag', // tag used for indexed arrays or invalid names - 'classAsTagName' => false, // use classname for objects in indexed arrays - 'keyAttribute' => '_originalKey', // attribute where original key is stored - 'typeAttribute' => '_type', // attribute for type (only if typeHints => true) - 'classAttribute' => '_class', // attribute for class of objects (only if typeHints => true) - 'scalarAsAttributes' => false, // scalar values (strings, ints,..) will be serialized as attribute - 'prependAttributes' => '', // prepend string for attributes - 'indentAttributes' => false, // indent the attributes, if set to '_auto', it will indent attributes so they all start at the same column - 'mode' => 'simplexml', // use 'simplexml' to use parent name as tagname if transforming an indexed array - 'addDoctype' => false, // add a doctype declaration - 'doctype' => null, // supply a string or an array with id and uri ({@see XML_Util::getDoctypeDeclaration()} - 'rootName' => 'package', // name of the root tag - 'rootAttributes' => array( - 'version' => '2.0', - 'xmlns' => 'http://pear.php.net/dtd/package-2.0', - 'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0', - 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', - 'xsi:schemaLocation' => 'http://pear.php.net/dtd/tasks-1.0 -http://pear.php.net/dtd/tasks-1.0.xsd -http://pear.php.net/dtd/package-2.0 -http://pear.php.net/dtd/package-2.0.xsd', - ), // attributes of the root tag - 'attributesArray' => 'attribs', // all values in this key will be treated as attributes - 'contentName' => '_content', // this value will be used directly as content, instead of creating a new tag, may only be used in conjuction with attributesArray - 'beautifyFilelist' => false, - 'encoding' => 'UTF-8', - ); - - /** - * options for the serialization - * @access private - * @var array $options - */ - var $options = array(); - - /** - * current tag depth - * @var integer $_tagDepth - */ - var $_tagDepth = 0; - - /** - * serilialized representation of the data - * @var string $_serializedData - */ - var $_serializedData = null; - /** - * @var PEAR_PackageFile_v2 - */ - var $_packagefile; - /** - * @param PEAR_PackageFile_v2 - */ - function PEAR_PackageFile_Generator_v2(&$packagefile) - { - $this->_packagefile = &$packagefile; - if (isset($this->_packagefile->encoding)) { - $this->_defaultOptions['encoding'] = $this->_packagefile->encoding; - } - } - - /** - * @return string - */ - function getPackagerVersion() - { - return '1.9.4'; - } - - /** - * @param PEAR_Packager - * @param bool generate a .tgz or a .tar - * @param string|null temporary directory to package in - */ - function toTgz(&$packager, $compress = true, $where = null) - { - $a = null; - return $this->toTgz2($packager, $a, $compress, $where); - } - - /** - * Package up both a package.xml and package2.xml for the same release - * @param PEAR_Packager - * @param PEAR_PackageFile_v1 - * @param bool generate a .tgz or a .tar - * @param string|null temporary directory to package in - */ - function toTgz2(&$packager, &$pf1, $compress = true, $where = null) - { - require_once 'Archive/Tar.php'; - if (!$this->_packagefile->isEquivalent($pf1)) { - return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: "' . - basename($pf1->getPackageFile()) . - '" is not equivalent to "' . basename($this->_packagefile->getPackageFile()) - . '"'); - } - - if ($where === null) { - if (!($where = System::mktemp(array('-d')))) { - return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: mktemp failed'); - } - } elseif (!@System::mkDir(array('-p', $where))) { - return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: "' . $where . '" could' . - ' not be created'); - } - - $file = $where . DIRECTORY_SEPARATOR . 'package.xml'; - if (file_exists($file) && !is_file($file)) { - return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: unable to save package.xml as' . - ' "' . $file .'"'); - } - - if (!$this->_packagefile->validate(PEAR_VALIDATE_PACKAGING)) { - return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: invalid package.xml'); - } - - $ext = $compress ? '.tgz' : '.tar'; - $pkgver = $this->_packagefile->getPackage() . '-' . $this->_packagefile->getVersion(); - $dest_package = getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext; - if (file_exists($dest_package) && !is_file($dest_package)) { - return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: cannot create tgz file "' . - $dest_package . '"'); - } - - $pkgfile = $this->_packagefile->getPackageFile(); - if (!$pkgfile) { - return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: package file object must ' . - 'be created from a real file'); - } - - $pkgdir = dirname(realpath($pkgfile)); - $pkgfile = basename($pkgfile); - - // {{{ Create the package file list - $filelist = array(); - $i = 0; - $this->_packagefile->flattenFilelist(); - $contents = $this->_packagefile->getContents(); - if (isset($contents['bundledpackage'])) { // bundles of packages - $contents = $contents['bundledpackage']; - if (!isset($contents[0])) { - $contents = array($contents); - } - - $packageDir = $where; - foreach ($contents as $i => $package) { - $fname = $package; - $file = $pkgdir . DIRECTORY_SEPARATOR . $fname; - if (!file_exists($file)) { - return $packager->raiseError("File does not exist: $fname"); - } - - $tfile = $packageDir . DIRECTORY_SEPARATOR . $fname; - System::mkdir(array('-p', dirname($tfile))); - copy($file, $tfile); - $filelist[$i++] = $tfile; - $packager->log(2, "Adding package $fname"); - } - } else { // normal packages - $contents = $contents['dir']['file']; - if (!isset($contents[0])) { - $contents = array($contents); - } - - $packageDir = $where; - foreach ($contents as $i => $file) { - $fname = $file['attribs']['name']; - $atts = $file['attribs']; - $orig = $file; - $file = $pkgdir . DIRECTORY_SEPARATOR . $fname; - if (!file_exists($file)) { - return $packager->raiseError("File does not exist: $fname"); - } - - $origperms = fileperms($file); - $tfile = $packageDir . DIRECTORY_SEPARATOR . $fname; - unset($orig['attribs']); - if (count($orig)) { // file with tasks - // run any package-time tasks - $contents = file_get_contents($file); - foreach ($orig as $tag => $raw) { - $tag = str_replace( - array($this->_packagefile->getTasksNs() . ':', '-'), - array('', '_'), $tag); - $task = "PEAR_Task_$tag"; - $task = &new $task($this->_packagefile->_config, - $this->_packagefile->_logger, - PEAR_TASK_PACKAGE); - $task->init($raw, $atts, null); - $res = $task->startSession($this->_packagefile, $contents, $tfile); - if (!$res) { - continue; // skip this task - } - - if (PEAR::isError($res)) { - return $res; - } - - $contents = $res; // save changes - System::mkdir(array('-p', dirname($tfile))); - $wp = fopen($tfile, "wb"); - fwrite($wp, $contents); - fclose($wp); - } - } - - if (!file_exists($tfile)) { - System::mkdir(array('-p', dirname($tfile))); - copy($file, $tfile); - } - - chmod($tfile, $origperms); - $filelist[$i++] = $tfile; - $this->_packagefile->setFileAttribute($fname, 'md5sum', md5_file($tfile), $i - 1); - $packager->log(2, "Adding file $fname"); - } - } - // }}} - - $name = $pf1 !== null ? 'package2.xml' : 'package.xml'; - $packagexml = $this->toPackageFile($where, PEAR_VALIDATE_PACKAGING, $name); - if ($packagexml) { - $tar =& new Archive_Tar($dest_package, $compress); - $tar->setErrorHandling(PEAR_ERROR_RETURN); // XXX Don't print errors - // ----- Creates with the package.xml file - $ok = $tar->createModify(array($packagexml), '', $where); - if (PEAR::isError($ok)) { - return $packager->raiseError($ok); - } elseif (!$ok) { - return $packager->raiseError('PEAR_Packagefile_v2::toTgz(): adding ' . $name . - ' failed'); - } - - // ----- Add the content of the package - if (!$tar->addModify($filelist, $pkgver, $where)) { - return $packager->raiseError( - 'PEAR_Packagefile_v2::toTgz(): tarball creation failed'); - } - - // add the package.xml version 1.0 - if ($pf1 !== null) { - $pfgen = &$pf1->getDefaultGenerator(); - $packagexml1 = $pfgen->toPackageFile($where, PEAR_VALIDATE_PACKAGING, 'package.xml', true); - if (!$tar->addModify(array($packagexml1), '', $where)) { - return $packager->raiseError( - 'PEAR_Packagefile_v2::toTgz(): adding package.xml failed'); - } - } - - return $dest_package; - } - } - - function toPackageFile($where = null, $state = PEAR_VALIDATE_NORMAL, $name = 'package.xml') - { - if (!$this->_packagefile->validate($state)) { - return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: invalid package.xml', - null, null, null, $this->_packagefile->getValidationWarnings()); - } - - if ($where === null) { - if (!($where = System::mktemp(array('-d')))) { - return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: mktemp failed'); - } - } elseif (!@System::mkDir(array('-p', $where))) { - return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: "' . $where . '" could' . - ' not be created'); - } - - $newpkgfile = $where . DIRECTORY_SEPARATOR . $name; - $np = @fopen($newpkgfile, 'wb'); - if (!$np) { - return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: unable to save ' . - "$name as $newpkgfile"); - } - fwrite($np, $this->toXml($state)); - fclose($np); - return $newpkgfile; - } - - function &toV2() - { - return $this->_packagefile; - } - - /** - * Return an XML document based on the package info (as returned - * by the PEAR_Common::infoFrom* methods). - * - * @return string XML data - */ - function toXml($state = PEAR_VALIDATE_NORMAL, $options = array()) - { - $this->_packagefile->setDate(date('Y-m-d')); - $this->_packagefile->setTime(date('H:i:s')); - if (!$this->_packagefile->validate($state)) { - return false; - } - - if (is_array($options)) { - $this->options = array_merge($this->_defaultOptions, $options); - } else { - $this->options = $this->_defaultOptions; - } - - $arr = $this->_packagefile->getArray(); - if (isset($arr['filelist'])) { - unset($arr['filelist']); - } - - if (isset($arr['_lastversion'])) { - unset($arr['_lastversion']); - } - - // Fix the notes a little bit - if (isset($arr['notes'])) { - // This trims out the indenting, needs fixing - $arr['notes'] = "\n" . trim($arr['notes']) . "\n"; - } - - if (isset($arr['changelog']) && !empty($arr['changelog'])) { - // Fix for inconsistency how the array is filled depending on the changelog release amount - if (!isset($arr['changelog']['release'][0])) { - $release = $arr['changelog']['release']; - unset($arr['changelog']['release']); - - $arr['changelog']['release'] = array(); - $arr['changelog']['release'][0] = $release; - } - - foreach (array_keys($arr['changelog']['release']) as $key) { - $c =& $arr['changelog']['release'][$key]; - if (isset($c['notes'])) { - // This trims out the indenting, needs fixing - $c['notes'] = "\n" . trim($c['notes']) . "\n"; - } - } - } - - if ($state ^ PEAR_VALIDATE_PACKAGING && !isset($arr['bundle'])) { - $use = $this->_recursiveXmlFilelist($arr['contents']['dir']['file']); - unset($arr['contents']['dir']['file']); - if (isset($use['dir'])) { - $arr['contents']['dir']['dir'] = $use['dir']; - } - if (isset($use['file'])) { - $arr['contents']['dir']['file'] = $use['file']; - } - $this->options['beautifyFilelist'] = true; - } - - $arr['attribs']['packagerversion'] = '1.9.4'; - if ($this->serialize($arr, $options)) { - return $this->_serializedData . "\n"; - } - - return false; - } - - - function _recursiveXmlFilelist($list) - { - $dirs = array(); - if (isset($list['attribs'])) { - $file = $list['attribs']['name']; - unset($list['attribs']['name']); - $attributes = $list['attribs']; - $this->_addDir($dirs, explode('/', dirname($file)), $file, $attributes); - } else { - foreach ($list as $a) { - $file = $a['attribs']['name']; - $attributes = $a['attribs']; - unset($a['attribs']); - $this->_addDir($dirs, explode('/', dirname($file)), $file, $attributes, $a); - } - } - $this->_formatDir($dirs); - $this->_deFormat($dirs); - return $dirs; - } - - function _addDir(&$dirs, $dir, $file = null, $attributes = null, $tasks = null) - { - if (!$tasks) { - $tasks = array(); - } - if ($dir == array() || $dir == array('.')) { - $dirs['file'][basename($file)] = $tasks; - $attributes['name'] = basename($file); - $dirs['file'][basename($file)]['attribs'] = $attributes; - return; - } - $curdir = array_shift($dir); - if (!isset($dirs['dir'][$curdir])) { - $dirs['dir'][$curdir] = array(); - } - $this->_addDir($dirs['dir'][$curdir], $dir, $file, $attributes, $tasks); - } - - function _formatDir(&$dirs) - { - if (!count($dirs)) { - return array(); - } - $newdirs = array(); - if (isset($dirs['dir'])) { - $newdirs['dir'] = $dirs['dir']; - } - if (isset($dirs['file'])) { - $newdirs['file'] = $dirs['file']; - } - $dirs = $newdirs; - if (isset($dirs['dir'])) { - uksort($dirs['dir'], 'strnatcasecmp'); - foreach ($dirs['dir'] as $dir => $contents) { - $this->_formatDir($dirs['dir'][$dir]); - } - } - if (isset($dirs['file'])) { - uksort($dirs['file'], 'strnatcasecmp'); - }; - } - - function _deFormat(&$dirs) - { - if (!count($dirs)) { - return array(); - } - $newdirs = array(); - if (isset($dirs['dir'])) { - foreach ($dirs['dir'] as $dir => $contents) { - $newdir = array(); - $newdir['attribs']['name'] = $dir; - $this->_deFormat($contents); - foreach ($contents as $tag => $val) { - $newdir[$tag] = $val; - } - $newdirs['dir'][] = $newdir; - } - if (count($newdirs['dir']) == 1) { - $newdirs['dir'] = $newdirs['dir'][0]; - } - } - if (isset($dirs['file'])) { - foreach ($dirs['file'] as $name => $file) { - $newdirs['file'][] = $file; - } - if (count($newdirs['file']) == 1) { - $newdirs['file'] = $newdirs['file'][0]; - } - } - $dirs = $newdirs; - } - - /** - * reset all options to default options - * - * @access public - * @see setOption(), XML_Unserializer() - */ - function resetOptions() - { - $this->options = $this->_defaultOptions; - } - - /** - * set an option - * - * You can use this method if you do not want to set all options in the constructor - * - * @access public - * @see resetOption(), XML_Serializer() - */ - function setOption($name, $value) - { - $this->options[$name] = $value; - } - - /** - * sets several options at once - * - * You can use this method if you do not want to set all options in the constructor - * - * @access public - * @see resetOption(), XML_Unserializer(), setOption() - */ - function setOptions($options) - { - $this->options = array_merge($this->options, $options); - } - - /** - * serialize data - * - * @access public - * @param mixed $data data to serialize - * @return boolean true on success, pear error on failure - */ - function serialize($data, $options = null) - { - // if options have been specified, use them instead - // of the previously defined ones - if (is_array($options)) { - $optionsBak = $this->options; - if (isset($options['overrideOptions']) && $options['overrideOptions'] == true) { - $this->options = array_merge($this->_defaultOptions, $options); - } else { - $this->options = array_merge($this->options, $options); - } - } else { - $optionsBak = null; - } - - // start depth is zero - $this->_tagDepth = 0; - $this->_serializedData = ''; - // serialize an array - if (is_array($data)) { - $tagName = isset($this->options['rootName']) ? $this->options['rootName'] : 'array'; - $this->_serializedData .= $this->_serializeArray($data, $tagName, $this->options['rootAttributes']); - } - - // add doctype declaration - if ($this->options['addDoctype'] === true) { - $this->_serializedData = XML_Util::getDoctypeDeclaration($tagName, $this->options['doctype']) - . $this->options['linebreak'] - . $this->_serializedData; - } - - // build xml declaration - if ($this->options['addDecl']) { - $atts = array(); - $encoding = isset($this->options['encoding']) ? $this->options['encoding'] : null; - $this->_serializedData = XML_Util::getXMLDeclaration('1.0', $encoding) - . $this->options['linebreak'] - . $this->_serializedData; - } - - - if ($optionsBak !== null) { - $this->options = $optionsBak; - } - - return true; - } - - /** - * get the result of the serialization - * - * @access public - * @return string serialized XML - */ - function getSerializedData() - { - if ($this->_serializedData === null) { - return $this->raiseError('No serialized data available. Use XML_Serializer::serialize() first.', XML_SERIALIZER_ERROR_NO_SERIALIZATION); - } - return $this->_serializedData; - } - - /** - * serialize any value - * - * This method checks for the type of the value and calls the appropriate method - * - * @access private - * @param mixed $value - * @param string $tagName - * @param array $attributes - * @return string - */ - function _serializeValue($value, $tagName = null, $attributes = array()) - { - if (is_array($value)) { - $xml = $this->_serializeArray($value, $tagName, $attributes); - } elseif (is_object($value)) { - $xml = $this->_serializeObject($value, $tagName); - } else { - $tag = array( - 'qname' => $tagName, - 'attributes' => $attributes, - 'content' => $value - ); - $xml = $this->_createXMLTag($tag); - } - return $xml; - } - - /** - * serialize an array - * - * @access private - * @param array $array array to serialize - * @param string $tagName name of the root tag - * @param array $attributes attributes for the root tag - * @return string $string serialized data - * @uses XML_Util::isValidName() to check, whether key has to be substituted - */ - function _serializeArray(&$array, $tagName = null, $attributes = array()) - { - $_content = null; - - /** - * check for special attributes - */ - if ($this->options['attributesArray'] !== null) { - if (isset($array[$this->options['attributesArray']])) { - $attributes = $array[$this->options['attributesArray']]; - unset($array[$this->options['attributesArray']]); - } - /** - * check for special content - */ - if ($this->options['contentName'] !== null) { - if (isset($array[$this->options['contentName']])) { - $_content = $array[$this->options['contentName']]; - unset($array[$this->options['contentName']]); - } - } - } - - /* - * if mode is set to simpleXML, check whether - * the array is associative or indexed - */ - if (is_array($array) && $this->options['mode'] == 'simplexml') { - $indexed = true; - if (!count($array)) { - $indexed = false; - } - foreach ($array as $key => $val) { - if (!is_int($key)) { - $indexed = false; - break; - } - } - - if ($indexed && $this->options['mode'] == 'simplexml') { - $string = ''; - foreach ($array as $key => $val) { - if ($this->options['beautifyFilelist'] && $tagName == 'dir') { - if (!isset($this->_curdir)) { - $this->_curdir = ''; - } - $savedir = $this->_curdir; - if (isset($val['attribs'])) { - if ($val['attribs']['name'] == '/') { - $this->_curdir = '/'; - } else { - if ($this->_curdir == '/') { - $this->_curdir = ''; - } - $this->_curdir .= '/' . $val['attribs']['name']; - } - } - } - $string .= $this->_serializeValue( $val, $tagName, $attributes); - if ($this->options['beautifyFilelist'] && $tagName == 'dir') { - $string .= ' '; - if (empty($savedir)) { - unset($this->_curdir); - } else { - $this->_curdir = $savedir; - } - } - - $string .= $this->options['linebreak']; - // do indentation - if ($this->options['indent'] !== null && $this->_tagDepth > 0) { - $string .= str_repeat($this->options['indent'], $this->_tagDepth); - } - } - return rtrim($string); - } - } - - if ($this->options['scalarAsAttributes'] === true) { - foreach ($array as $key => $value) { - if (is_scalar($value) && (XML_Util::isValidName($key) === true)) { - unset($array[$key]); - $attributes[$this->options['prependAttributes'].$key] = $value; - } - } - } - - // check for empty array => create empty tag - if (empty($array)) { - $tag = array( - 'qname' => $tagName, - 'content' => $_content, - 'attributes' => $attributes - ); - - } else { - $this->_tagDepth++; - $tmp = $this->options['linebreak']; - foreach ($array as $key => $value) { - // do indentation - if ($this->options['indent'] !== null && $this->_tagDepth > 0) { - $tmp .= str_repeat($this->options['indent'], $this->_tagDepth); - } - - // copy key - $origKey = $key; - // key cannot be used as tagname => use default tag - $valid = XML_Util::isValidName($key); - if (PEAR::isError($valid)) { - if ($this->options['classAsTagName'] && is_object($value)) { - $key = get_class($value); - } else { - $key = $this->options['defaultTagName']; - } - } - $atts = array(); - if ($this->options['typeHints'] === true) { - $atts[$this->options['typeAttribute']] = gettype($value); - if ($key !== $origKey) { - $atts[$this->options['keyAttribute']] = (string)$origKey; - } - - } - if ($this->options['beautifyFilelist'] && $key == 'dir') { - if (!isset($this->_curdir)) { - $this->_curdir = ''; - } - $savedir = $this->_curdir; - if (isset($value['attribs'])) { - if ($value['attribs']['name'] == '/') { - $this->_curdir = '/'; - } else { - $this->_curdir .= '/' . $value['attribs']['name']; - } - } - } - - if (is_string($value) && $value && ($value{strlen($value) - 1} == "\n")) { - $value .= str_repeat($this->options['indent'], $this->_tagDepth); - } - $tmp .= $this->_createXMLTag(array( - 'qname' => $key, - 'attributes' => $atts, - 'content' => $value ) - ); - if ($this->options['beautifyFilelist'] && $key == 'dir') { - if (isset($value['attribs'])) { - $tmp .= ' '; - if (empty($savedir)) { - unset($this->_curdir); - } else { - $this->_curdir = $savedir; - } - } - } - $tmp .= $this->options['linebreak']; - } - - $this->_tagDepth--; - if ($this->options['indent']!==null && $this->_tagDepth>0) { - $tmp .= str_repeat($this->options['indent'], $this->_tagDepth); - } - - if (trim($tmp) === '') { - $tmp = null; - } - - $tag = array( - 'qname' => $tagName, - 'content' => $tmp, - 'attributes' => $attributes - ); - } - if ($this->options['typeHints'] === true) { - if (!isset($tag['attributes'][$this->options['typeAttribute']])) { - $tag['attributes'][$this->options['typeAttribute']] = 'array'; - } - } - - $string = $this->_createXMLTag($tag, false); - return $string; - } - - /** - * create a tag from an array - * this method awaits an array in the following format - * array( - * 'qname' => $tagName, - * 'attributes' => array(), - * 'content' => $content, // optional - * 'namespace' => $namespace // optional - * 'namespaceUri' => $namespaceUri // optional - * ) - * - * @access private - * @param array $tag tag definition - * @param boolean $replaceEntities whether to replace XML entities in content or not - * @return string $string XML tag - */ - function _createXMLTag($tag, $replaceEntities = true) - { - if ($this->options['indentAttributes'] !== false) { - $multiline = true; - $indent = str_repeat($this->options['indent'], $this->_tagDepth); - - if ($this->options['indentAttributes'] == '_auto') { - $indent .= str_repeat(' ', (strlen($tag['qname'])+2)); - - } else { - $indent .= $this->options['indentAttributes']; - } - } else { - $indent = $multiline = false; - } - - if (is_array($tag['content'])) { - if (empty($tag['content'])) { - $tag['content'] = ''; - } - } elseif(is_scalar($tag['content']) && (string)$tag['content'] == '') { - $tag['content'] = ''; - } - - if (is_scalar($tag['content']) || is_null($tag['content'])) { - if ($this->options['encoding'] == 'UTF-8' && - version_compare(phpversion(), '5.0.0', 'lt') - ) { - $tag['content'] = utf8_encode($tag['content']); - } - - if ($replaceEntities === true) { - $replaceEntities = XML_UTIL_ENTITIES_XML; - } - - $tag = XML_Util::createTagFromArray($tag, $replaceEntities, $multiline, $indent, $this->options['linebreak']); - } elseif (is_array($tag['content'])) { - $tag = $this->_serializeArray($tag['content'], $tag['qname'], $tag['attributes']); - } elseif (is_object($tag['content'])) { - $tag = $this->_serializeObject($tag['content'], $tag['qname'], $tag['attributes']); - } elseif (is_resource($tag['content'])) { - settype($tag['content'], 'string'); - $tag = XML_Util::createTagFromArray($tag, $replaceEntities); - } - return $tag; - } -} \ No newline at end of file diff --git a/pear/PEAR/PackageFile/Parser/v1.php b/pear/PEAR/PackageFile/Parser/v1.php deleted file mode 100644 index bbf5989..0000000 --- a/pear/PEAR/PackageFile/Parser/v1.php +++ /dev/null @@ -1,459 +0,0 @@ - - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a1 - */ -/** - * package.xml abstraction class - */ -require_once 'PEAR/PackageFile/v1.php'; -/** - * Parser for package.xml version 1.0 - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @PEAR-VER@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a1 - */ -class PEAR_PackageFile_Parser_v1 -{ - var $_registry; - var $_config; - var $_logger; - /** - * BC hack to allow PEAR_Common::infoFromString() to sort of - * work with the version 2.0 format - there's no filelist though - * @param PEAR_PackageFile_v2 - */ - function fromV2($packagefile) - { - $info = $packagefile->getArray(true); - $ret = new PEAR_PackageFile_v1; - $ret->fromArray($info['old']); - } - - function setConfig(&$c) - { - $this->_config = &$c; - $this->_registry = &$c->getRegistry(); - } - - function setLogger(&$l) - { - $this->_logger = &$l; - } - - /** - * @param string contents of package.xml file, version 1.0 - * @return bool success of parsing - */ - function &parse($data, $file, $archive = false) - { - if (!extension_loaded('xml')) { - return PEAR::raiseError('Cannot create xml parser for parsing package.xml, no xml extension'); - } - $xp = xml_parser_create(); - if (!$xp) { - $a = &PEAR::raiseError('Cannot create xml parser for parsing package.xml'); - return $a; - } - xml_set_object($xp, $this); - xml_set_element_handler($xp, '_element_start_1_0', '_element_end_1_0'); - xml_set_character_data_handler($xp, '_pkginfo_cdata_1_0'); - xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, false); - - $this->element_stack = array(); - $this->_packageInfo = array('provides' => array()); - $this->current_element = false; - unset($this->dir_install); - $this->_packageInfo['filelist'] = array(); - $this->filelist =& $this->_packageInfo['filelist']; - $this->dir_names = array(); - $this->in_changelog = false; - $this->d_i = 0; - $this->cdata = ''; - $this->_isValid = true; - - if (!xml_parse($xp, $data, 1)) { - $code = xml_get_error_code($xp); - $line = xml_get_current_line_number($xp); - xml_parser_free($xp); - $a = &PEAR::raiseError(sprintf("XML error: %s at line %d", - $str = xml_error_string($code), $line), 2); - return $a; - } - - xml_parser_free($xp); - - $pf = new PEAR_PackageFile_v1; - $pf->setConfig($this->_config); - if (isset($this->_logger)) { - $pf->setLogger($this->_logger); - } - $pf->setPackagefile($file, $archive); - $pf->fromArray($this->_packageInfo); - return $pf; - } - // {{{ _unIndent() - - /** - * Unindent given string - * - * @param string $str The string that has to be unindented. - * @return string - * @access private - */ - function _unIndent($str) - { - // remove leading newlines - $str = preg_replace('/^[\r\n]+/', '', $str); - // find whitespace at the beginning of the first line - $indent_len = strspn($str, " \t"); - $indent = substr($str, 0, $indent_len); - $data = ''; - // remove the same amount of whitespace from following lines - foreach (explode("\n", $str) as $line) { - if (substr($line, 0, $indent_len) == $indent) { - $data .= substr($line, $indent_len) . "\n"; - } elseif (trim(substr($line, 0, $indent_len))) { - $data .= ltrim($line); - } - } - return $data; - } - - // Support for package DTD v1.0: - // {{{ _element_start_1_0() - - /** - * XML parser callback for ending elements. Used for version 1.0 - * packages. - * - * @param resource $xp XML parser resource - * @param string $name name of ending element - * - * @return void - * - * @access private - */ - function _element_start_1_0($xp, $name, $attribs) - { - array_push($this->element_stack, $name); - $this->current_element = $name; - $spos = sizeof($this->element_stack) - 2; - $this->prev_element = ($spos >= 0) ? $this->element_stack[$spos] : ''; - $this->current_attributes = $attribs; - $this->cdata = ''; - switch ($name) { - case 'dir': - if ($this->in_changelog) { - break; - } - if (array_key_exists('name', $attribs) && $attribs['name'] != '/') { - $attribs['name'] = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'), - $attribs['name']); - if (strrpos($attribs['name'], '/') === strlen($attribs['name']) - 1) { - $attribs['name'] = substr($attribs['name'], 0, - strlen($attribs['name']) - 1); - } - if (strpos($attribs['name'], '/') === 0) { - $attribs['name'] = substr($attribs['name'], 1); - } - $this->dir_names[] = $attribs['name']; - } - if (isset($attribs['baseinstalldir'])) { - $this->dir_install = $attribs['baseinstalldir']; - } - if (isset($attribs['role'])) { - $this->dir_role = $attribs['role']; - } - break; - case 'file': - if ($this->in_changelog) { - break; - } - if (isset($attribs['name'])) { - $path = ''; - if (count($this->dir_names)) { - foreach ($this->dir_names as $dir) { - $path .= $dir . '/'; - } - } - $path .= preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'), - $attribs['name']); - unset($attribs['name']); - $this->current_path = $path; - $this->filelist[$path] = $attribs; - // Set the baseinstalldir only if the file don't have this attrib - if (!isset($this->filelist[$path]['baseinstalldir']) && - isset($this->dir_install)) - { - $this->filelist[$path]['baseinstalldir'] = $this->dir_install; - } - // Set the Role - if (!isset($this->filelist[$path]['role']) && isset($this->dir_role)) { - $this->filelist[$path]['role'] = $this->dir_role; - } - } - break; - case 'replace': - if (!$this->in_changelog) { - $this->filelist[$this->current_path]['replacements'][] = $attribs; - } - break; - case 'maintainers': - $this->_packageInfo['maintainers'] = array(); - $this->m_i = 0; // maintainers array index - break; - case 'maintainer': - // compatibility check - if (!isset($this->_packageInfo['maintainers'])) { - $this->_packageInfo['maintainers'] = array(); - $this->m_i = 0; - } - $this->_packageInfo['maintainers'][$this->m_i] = array(); - $this->current_maintainer =& $this->_packageInfo['maintainers'][$this->m_i]; - break; - case 'changelog': - $this->_packageInfo['changelog'] = array(); - $this->c_i = 0; // changelog array index - $this->in_changelog = true; - break; - case 'release': - if ($this->in_changelog) { - $this->_packageInfo['changelog'][$this->c_i] = array(); - $this->current_release = &$this->_packageInfo['changelog'][$this->c_i]; - } else { - $this->current_release = &$this->_packageInfo; - } - break; - case 'deps': - if (!$this->in_changelog) { - $this->_packageInfo['release_deps'] = array(); - } - break; - case 'dep': - // dependencies array index - if (!$this->in_changelog) { - $this->d_i++; - isset($attribs['type']) ? ($attribs['type'] = strtolower($attribs['type'])) : false; - $this->_packageInfo['release_deps'][$this->d_i] = $attribs; - } - break; - case 'configureoptions': - if (!$this->in_changelog) { - $this->_packageInfo['configure_options'] = array(); - } - break; - case 'configureoption': - if (!$this->in_changelog) { - $this->_packageInfo['configure_options'][] = $attribs; - } - break; - case 'provides': - if (empty($attribs['type']) || empty($attribs['name'])) { - break; - } - $attribs['explicit'] = true; - $this->_packageInfo['provides']["$attribs[type];$attribs[name]"] = $attribs; - break; - case 'package' : - if (isset($attribs['version'])) { - $this->_packageInfo['xsdversion'] = trim($attribs['version']); - } else { - $this->_packageInfo['xsdversion'] = '1.0'; - } - if (isset($attribs['packagerversion'])) { - $this->_packageInfo['packagerversion'] = $attribs['packagerversion']; - } - break; - } - } - - // }}} - // {{{ _element_end_1_0() - - /** - * XML parser callback for ending elements. Used for version 1.0 - * packages. - * - * @param resource $xp XML parser resource - * @param string $name name of ending element - * - * @return void - * - * @access private - */ - function _element_end_1_0($xp, $name) - { - $data = trim($this->cdata); - switch ($name) { - case 'name': - switch ($this->prev_element) { - case 'package': - $this->_packageInfo['package'] = $data; - break; - case 'maintainer': - $this->current_maintainer['name'] = $data; - break; - } - break; - case 'extends' : - $this->_packageInfo['extends'] = $data; - break; - case 'summary': - $this->_packageInfo['summary'] = $data; - break; - case 'description': - $data = $this->_unIndent($this->cdata); - $this->_packageInfo['description'] = $data; - break; - case 'user': - $this->current_maintainer['handle'] = $data; - break; - case 'email': - $this->current_maintainer['email'] = $data; - break; - case 'role': - $this->current_maintainer['role'] = $data; - break; - case 'version': - if ($this->in_changelog) { - $this->current_release['version'] = $data; - } else { - $this->_packageInfo['version'] = $data; - } - break; - case 'date': - if ($this->in_changelog) { - $this->current_release['release_date'] = $data; - } else { - $this->_packageInfo['release_date'] = $data; - } - break; - case 'notes': - // try to "de-indent" release notes in case someone - // has been over-indenting their xml ;-) - // Trim only on the right side - $data = rtrim($this->_unIndent($this->cdata)); - if ($this->in_changelog) { - $this->current_release['release_notes'] = $data; - } else { - $this->_packageInfo['release_notes'] = $data; - } - break; - case 'warnings': - if ($this->in_changelog) { - $this->current_release['release_warnings'] = $data; - } else { - $this->_packageInfo['release_warnings'] = $data; - } - break; - case 'state': - if ($this->in_changelog) { - $this->current_release['release_state'] = $data; - } else { - $this->_packageInfo['release_state'] = $data; - } - break; - case 'license': - if ($this->in_changelog) { - $this->current_release['release_license'] = $data; - } else { - $this->_packageInfo['release_license'] = $data; - } - break; - case 'dep': - if ($data && !$this->in_changelog) { - $this->_packageInfo['release_deps'][$this->d_i]['name'] = $data; - } - break; - case 'dir': - if ($this->in_changelog) { - break; - } - array_pop($this->dir_names); - break; - case 'file': - if ($this->in_changelog) { - break; - } - if ($data) { - $path = ''; - if (count($this->dir_names)) { - foreach ($this->dir_names as $dir) { - $path .= $dir . '/'; - } - } - $path .= $data; - $this->filelist[$path] = $this->current_attributes; - // Set the baseinstalldir only if the file don't have this attrib - if (!isset($this->filelist[$path]['baseinstalldir']) && - isset($this->dir_install)) - { - $this->filelist[$path]['baseinstalldir'] = $this->dir_install; - } - // Set the Role - if (!isset($this->filelist[$path]['role']) && isset($this->dir_role)) { - $this->filelist[$path]['role'] = $this->dir_role; - } - } - break; - case 'maintainer': - if (empty($this->_packageInfo['maintainers'][$this->m_i]['role'])) { - $this->_packageInfo['maintainers'][$this->m_i]['role'] = 'lead'; - } - $this->m_i++; - break; - case 'release': - if ($this->in_changelog) { - $this->c_i++; - } - break; - case 'changelog': - $this->in_changelog = false; - break; - } - array_pop($this->element_stack); - $spos = sizeof($this->element_stack) - 1; - $this->current_element = ($spos > 0) ? $this->element_stack[$spos] : ''; - $this->cdata = ''; - } - - // }}} - // {{{ _pkginfo_cdata_1_0() - - /** - * XML parser callback for character data. Used for version 1.0 - * packages. - * - * @param resource $xp XML parser resource - * @param string $name character data - * - * @return void - * - * @access private - */ - function _pkginfo_cdata_1_0($xp, $data) - { - if (isset($this->cdata)) { - $this->cdata .= $data; - } - } - - // }}} -} -?> \ No newline at end of file diff --git a/pear/PEAR/PackageFile/Parser/v2.php b/pear/PEAR/PackageFile/Parser/v2.php deleted file mode 100644 index cb81041..0000000 --- a/pear/PEAR/PackageFile/Parser/v2.php +++ /dev/null @@ -1,113 +0,0 @@ - - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a1 - */ -/** - * base xml parser class - */ -require_once 'PEAR/XMLParser.php'; -require_once 'PEAR/PackageFile/v2.php'; -/** - * Parser for package.xml version 2.0 - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @PEAR-VER@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a1 - */ -class PEAR_PackageFile_Parser_v2 extends PEAR_XMLParser -{ - var $_config; - var $_logger; - var $_registry; - - function setConfig(&$c) - { - $this->_config = &$c; - $this->_registry = &$c->getRegistry(); - } - - function setLogger(&$l) - { - $this->_logger = &$l; - } - /** - * Unindent given string - * - * @param string $str The string that has to be unindented. - * @return string - * @access private - */ - function _unIndent($str) - { - // remove leading newlines - $str = preg_replace('/^[\r\n]+/', '', $str); - // find whitespace at the beginning of the first line - $indent_len = strspn($str, " \t"); - $indent = substr($str, 0, $indent_len); - $data = ''; - // remove the same amount of whitespace from following lines - foreach (explode("\n", $str) as $line) { - if (substr($line, 0, $indent_len) == $indent) { - $data .= substr($line, $indent_len) . "\n"; - } else { - $data .= $line . "\n"; - } - } - return $data; - } - - /** - * post-process data - * - * @param string $data - * @param string $element element name - */ - function postProcess($data, $element) - { - if ($element == 'notes') { - return trim($this->_unIndent($data)); - } - return trim($data); - } - - /** - * @param string - * @param string file name of the package.xml - * @param string|false name of the archive this package.xml came from, if any - * @param string class name to instantiate and return. This must be PEAR_PackageFile_v2 or - * a subclass - * @return PEAR_PackageFile_v2 - */ - function &parse($data, $file, $archive = false, $class = 'PEAR_PackageFile_v2') - { - if (PEAR::isError($err = parent::parse($data, $file))) { - return $err; - } - - $ret = new $class; - $ret->encoding = $this->encoding; - $ret->setConfig($this->_config); - if (isset($this->_logger)) { - $ret->setLogger($this->_logger); - } - - $ret->fromArray($this->_unserializedData); - $ret->setPackagefile($file, $archive); - return $ret; - } -} \ No newline at end of file diff --git a/pear/PEAR/PackageFile/v1.php b/pear/PEAR/PackageFile/v1.php deleted file mode 100644 index 2884cc4..0000000 --- a/pear/PEAR/PackageFile/v1.php +++ /dev/null @@ -1,1612 +0,0 @@ - - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a1 - */ -/** - * For error handling - */ -require_once 'PEAR/ErrorStack.php'; - -/** - * Error code if parsing is attempted with no xml extension - */ -define('PEAR_PACKAGEFILE_ERROR_NO_XML_EXT', 3); - -/** - * Error code if creating the xml parser resource fails - */ -define('PEAR_PACKAGEFILE_ERROR_CANT_MAKE_PARSER', 4); - -/** - * Error code used for all sax xml parsing errors - */ -define('PEAR_PACKAGEFILE_ERROR_PARSER_ERROR', 5); - -/** - * Error code used when there is no name - */ -define('PEAR_PACKAGEFILE_ERROR_NO_NAME', 6); - -/** - * Error code when a package name is not valid - */ -define('PEAR_PACKAGEFILE_ERROR_INVALID_NAME', 7); - -/** - * Error code used when no summary is parsed - */ -define('PEAR_PACKAGEFILE_ERROR_NO_SUMMARY', 8); - -/** - * Error code for summaries that are more than 1 line - */ -define('PEAR_PACKAGEFILE_ERROR_MULTILINE_SUMMARY', 9); - -/** - * Error code used when no description is present - */ -define('PEAR_PACKAGEFILE_ERROR_NO_DESCRIPTION', 10); - -/** - * Error code used when no license is present - */ -define('PEAR_PACKAGEFILE_ERROR_NO_LICENSE', 11); - -/** - * Error code used when a version number is not present - */ -define('PEAR_PACKAGEFILE_ERROR_NO_VERSION', 12); - -/** - * Error code used when a version number is invalid - */ -define('PEAR_PACKAGEFILE_ERROR_INVALID_VERSION', 13); - -/** - * Error code when release state is missing - */ -define('PEAR_PACKAGEFILE_ERROR_NO_STATE', 14); - -/** - * Error code when release state is invalid - */ -define('PEAR_PACKAGEFILE_ERROR_INVALID_STATE', 15); - -/** - * Error code when release state is missing - */ -define('PEAR_PACKAGEFILE_ERROR_NO_DATE', 16); - -/** - * Error code when release state is invalid - */ -define('PEAR_PACKAGEFILE_ERROR_INVALID_DATE', 17); - -/** - * Error code when no release notes are found - */ -define('PEAR_PACKAGEFILE_ERROR_NO_NOTES', 18); - -/** - * Error code when no maintainers are found - */ -define('PEAR_PACKAGEFILE_ERROR_NO_MAINTAINERS', 19); - -/** - * Error code when a maintainer has no handle - */ -define('PEAR_PACKAGEFILE_ERROR_NO_MAINTHANDLE', 20); - -/** - * Error code when a maintainer has no handle - */ -define('PEAR_PACKAGEFILE_ERROR_NO_MAINTROLE', 21); - -/** - * Error code when a maintainer has no name - */ -define('PEAR_PACKAGEFILE_ERROR_NO_MAINTNAME', 22); - -/** - * Error code when a maintainer has no email - */ -define('PEAR_PACKAGEFILE_ERROR_NO_MAINTEMAIL', 23); - -/** - * Error code when a maintainer has no handle - */ -define('PEAR_PACKAGEFILE_ERROR_INVALID_MAINTROLE', 24); - -/** - * Error code when a dependency is not a PHP dependency, but has no name - */ -define('PEAR_PACKAGEFILE_ERROR_NO_DEPNAME', 25); - -/** - * Error code when a dependency has no type (pkg, php, etc.) - */ -define('PEAR_PACKAGEFILE_ERROR_NO_DEPTYPE', 26); - -/** - * Error code when a dependency has no relation (lt, ge, has, etc.) - */ -define('PEAR_PACKAGEFILE_ERROR_NO_DEPREL', 27); - -/** - * Error code when a dependency is not a 'has' relation, but has no version - */ -define('PEAR_PACKAGEFILE_ERROR_NO_DEPVERSION', 28); - -/** - * Error code when a dependency has an invalid relation - */ -define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPREL', 29); - -/** - * Error code when a dependency has an invalid type - */ -define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPTYPE', 30); - -/** - * Error code when a dependency has an invalid optional option - */ -define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPOPTIONAL', 31); - -/** - * Error code when a dependency is a pkg dependency, and has an invalid package name - */ -define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPNAME', 32); - -/** - * Error code when a dependency has a channel="foo" attribute, and foo is not a registered channel - */ -define('PEAR_PACKAGEFILE_ERROR_UNKNOWN_DEPCHANNEL', 33); - -/** - * Error code when rel="has" and version attribute is present. - */ -define('PEAR_PACKAGEFILE_ERROR_DEPVERSION_IGNORED', 34); - -/** - * Error code when type="php" and dependency name is present - */ -define('PEAR_PACKAGEFILE_ERROR_DEPNAME_IGNORED', 35); - -/** - * Error code when a configure option has no name - */ -define('PEAR_PACKAGEFILE_ERROR_NO_CONFNAME', 36); - -/** - * Error code when a configure option has no name - */ -define('PEAR_PACKAGEFILE_ERROR_NO_CONFPROMPT', 37); - -/** - * Error code when a file in the filelist has an invalid role - */ -define('PEAR_PACKAGEFILE_ERROR_INVALID_FILEROLE', 38); - -/** - * Error code when a file in the filelist has no role - */ -define('PEAR_PACKAGEFILE_ERROR_NO_FILEROLE', 39); - -/** - * Error code when analyzing a php source file that has parse errors - */ -define('PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE', 40); - -/** - * Error code when analyzing a php source file reveals a source element - * without a package name prefix - */ -define('PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX', 41); - -/** - * Error code when an unknown channel is specified - */ -define('PEAR_PACKAGEFILE_ERROR_UNKNOWN_CHANNEL', 42); - -/** - * Error code when no files are found in the filelist - */ -define('PEAR_PACKAGEFILE_ERROR_NO_FILES', 43); - -/** - * Error code when a file is not valid php according to _analyzeSourceCode() - */ -define('PEAR_PACKAGEFILE_ERROR_INVALID_FILE', 44); - -/** - * Error code when the channel validator returns an error or warning - */ -define('PEAR_PACKAGEFILE_ERROR_CHANNELVAL', 45); - -/** - * Error code when a php5 package is packaged in php4 (analysis doesn't work) - */ -define('PEAR_PACKAGEFILE_ERROR_PHP5', 46); - -/** - * Error code when a file is listed in package.xml but does not exist - */ -define('PEAR_PACKAGEFILE_ERROR_FILE_NOTFOUND', 47); - -/** - * Error code when a - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a1 - */ -class PEAR_PackageFile_v1 -{ - /** - * @access private - * @var PEAR_ErrorStack - * @access private - */ - var $_stack; - - /** - * A registry object, used to access the package name validation regex for non-standard channels - * @var PEAR_Registry - * @access private - */ - var $_registry; - - /** - * An object that contains a log method that matches PEAR_Common::log's signature - * @var object - * @access private - */ - var $_logger; - - /** - * Parsed package information - * @var array - * @access private - */ - var $_packageInfo; - - /** - * path to package.xml - * @var string - * @access private - */ - var $_packageFile; - - /** - * path to package .tgz or false if this is a local/extracted package.xml - * @var string - * @access private - */ - var $_archiveFile; - - /** - * @var int - * @access private - */ - var $_isValid = 0; - - /** - * Determines whether this packagefile was initialized only with partial package info - * - * If this package file was constructed via parsing REST, it will only contain - * - * - package name - * - channel name - * - dependencies - * @var boolean - * @access private - */ - var $_incomplete = true; - - /** - * @param bool determines whether to return a PEAR_Error object, or use the PEAR_ErrorStack - * @param string Name of Error Stack class to use. - */ - function PEAR_PackageFile_v1() - { - $this->_stack = &new PEAR_ErrorStack('PEAR_PackageFile_v1'); - $this->_stack->setErrorMessageTemplate($this->_getErrorMessage()); - $this->_isValid = 0; - } - - function installBinary($installer) - { - return false; - } - - function isExtension($name) - { - return false; - } - - function setConfig(&$config) - { - $this->_config = &$config; - $this->_registry = &$config->getRegistry(); - } - - function setRequestedGroup() - { - // placeholder - } - - /** - * For saving in the registry. - * - * Set the last version that was installed - * @param string - */ - function setLastInstalledVersion($version) - { - $this->_packageInfo['_lastversion'] = $version; - } - - /** - * @return string|false - */ - function getLastInstalledVersion() - { - if (isset($this->_packageInfo['_lastversion'])) { - return $this->_packageInfo['_lastversion']; - } - return false; - } - - function getInstalledBinary() - { - return false; - } - - function listPostinstallScripts() - { - return false; - } - - function initPostinstallScripts() - { - return false; - } - - function setLogger(&$logger) - { - if ($logger && (!is_object($logger) || !method_exists($logger, 'log'))) { - return PEAR::raiseError('Logger must be compatible with PEAR_Common::log'); - } - $this->_logger = &$logger; - } - - function setPackagefile($file, $archive = false) - { - $this->_packageFile = $file; - $this->_archiveFile = $archive ? $archive : $file; - } - - function getPackageFile() - { - return isset($this->_packageFile) ? $this->_packageFile : false; - } - - function getPackageType() - { - return 'php'; - } - - function getArchiveFile() - { - return $this->_archiveFile; - } - - function packageInfo($field) - { - if (!is_string($field) || empty($field) || - !isset($this->_packageInfo[$field])) { - return false; - } - return $this->_packageInfo[$field]; - } - - function setDirtree($path) - { - if (!isset($this->_packageInfo['dirtree'])) { - $this->_packageInfo['dirtree'] = array(); - } - $this->_packageInfo['dirtree'][$path] = true; - } - - function getDirtree() - { - if (isset($this->_packageInfo['dirtree']) && count($this->_packageInfo['dirtree'])) { - return $this->_packageInfo['dirtree']; - } - return false; - } - - function resetDirtree() - { - unset($this->_packageInfo['dirtree']); - } - - function fromArray($pinfo) - { - $this->_incomplete = false; - $this->_packageInfo = $pinfo; - } - - function isIncomplete() - { - return $this->_incomplete; - } - - function getChannel() - { - return 'pear.php.net'; - } - - function getUri() - { - return false; - } - - function getTime() - { - return false; - } - - function getExtends() - { - if (isset($this->_packageInfo['extends'])) { - return $this->_packageInfo['extends']; - } - return false; - } - - /** - * @return array - */ - function toArray() - { - if (!$this->validate(PEAR_VALIDATE_NORMAL)) { - return false; - } - return $this->getArray(); - } - - function getArray() - { - return $this->_packageInfo; - } - - function getName() - { - return $this->getPackage(); - } - - function getPackage() - { - if (isset($this->_packageInfo['package'])) { - return $this->_packageInfo['package']; - } - return false; - } - - /** - * WARNING - don't use this unless you know what you are doing - */ - function setRawPackage($package) - { - $this->_packageInfo['package'] = $package; - } - - function setPackage($package) - { - $this->_packageInfo['package'] = $package; - $this->_isValid = false; - } - - function getVersion() - { - if (isset($this->_packageInfo['version'])) { - return $this->_packageInfo['version']; - } - return false; - } - - function setVersion($version) - { - $this->_packageInfo['version'] = $version; - $this->_isValid = false; - } - - function clearMaintainers() - { - unset($this->_packageInfo['maintainers']); - } - - function getMaintainers() - { - if (isset($this->_packageInfo['maintainers'])) { - return $this->_packageInfo['maintainers']; - } - return false; - } - - /** - * Adds a new maintainer - no checking of duplicates is performed, use - * updatemaintainer for that purpose. - */ - function addMaintainer($role, $handle, $name, $email) - { - $this->_packageInfo['maintainers'][] = - array('handle' => $handle, 'role' => $role, 'email' => $email, 'name' => $name); - $this->_isValid = false; - } - - function updateMaintainer($role, $handle, $name, $email) - { - $found = false; - if (!isset($this->_packageInfo['maintainers']) || - !is_array($this->_packageInfo['maintainers'])) { - return $this->addMaintainer($role, $handle, $name, $email); - } - foreach ($this->_packageInfo['maintainers'] as $i => $maintainer) { - if ($maintainer['handle'] == $handle) { - $found = $i; - break; - } - } - if ($found !== false) { - unset($this->_packageInfo['maintainers'][$found]); - $this->_packageInfo['maintainers'] = - array_values($this->_packageInfo['maintainers']); - } - $this->addMaintainer($role, $handle, $name, $email); - } - - function deleteMaintainer($handle) - { - $found = false; - foreach ($this->_packageInfo['maintainers'] as $i => $maintainer) { - if ($maintainer['handle'] == $handle) { - $found = $i; - break; - } - } - if ($found !== false) { - unset($this->_packageInfo['maintainers'][$found]); - $this->_packageInfo['maintainers'] = - array_values($this->_packageInfo['maintainers']); - return true; - } - return false; - } - - function getState() - { - if (isset($this->_packageInfo['release_state'])) { - return $this->_packageInfo['release_state']; - } - return false; - } - - function setRawState($state) - { - $this->_packageInfo['release_state'] = $state; - } - - function setState($state) - { - $this->_packageInfo['release_state'] = $state; - $this->_isValid = false; - } - - function getDate() - { - if (isset($this->_packageInfo['release_date'])) { - return $this->_packageInfo['release_date']; - } - return false; - } - - function setDate($date) - { - $this->_packageInfo['release_date'] = $date; - $this->_isValid = false; - } - - function getLicense() - { - if (isset($this->_packageInfo['release_license'])) { - return $this->_packageInfo['release_license']; - } - return false; - } - - function setLicense($date) - { - $this->_packageInfo['release_license'] = $date; - $this->_isValid = false; - } - - function getSummary() - { - if (isset($this->_packageInfo['summary'])) { - return $this->_packageInfo['summary']; - } - return false; - } - - function setSummary($summary) - { - $this->_packageInfo['summary'] = $summary; - $this->_isValid = false; - } - - function getDescription() - { - if (isset($this->_packageInfo['description'])) { - return $this->_packageInfo['description']; - } - return false; - } - - function setDescription($desc) - { - $this->_packageInfo['description'] = $desc; - $this->_isValid = false; - } - - function getNotes() - { - if (isset($this->_packageInfo['release_notes'])) { - return $this->_packageInfo['release_notes']; - } - return false; - } - - function setNotes($notes) - { - $this->_packageInfo['release_notes'] = $notes; - $this->_isValid = false; - } - - function getDeps() - { - if (isset($this->_packageInfo['release_deps'])) { - return $this->_packageInfo['release_deps']; - } - return false; - } - - /** - * Reset dependencies prior to adding new ones - */ - function clearDeps() - { - unset($this->_packageInfo['release_deps']); - } - - function addPhpDep($version, $rel) - { - $this->_isValid = false; - $this->_packageInfo['release_deps'][] = - array('type' => 'php', - 'rel' => $rel, - 'version' => $version); - } - - function addPackageDep($name, $version, $rel, $optional = 'no') - { - $this->_isValid = false; - $dep = - array('type' => 'pkg', - 'name' => $name, - 'rel' => $rel, - 'optional' => $optional); - if ($rel != 'has' && $rel != 'not') { - $dep['version'] = $version; - } - $this->_packageInfo['release_deps'][] = $dep; - } - - function addExtensionDep($name, $version, $rel, $optional = 'no') - { - $this->_isValid = false; - $this->_packageInfo['release_deps'][] = - array('type' => 'ext', - 'name' => $name, - 'rel' => $rel, - 'version' => $version, - 'optional' => $optional); - } - - /** - * WARNING - do not use this function directly unless you know what you're doing - */ - function setDeps($deps) - { - $this->_packageInfo['release_deps'] = $deps; - } - - function hasDeps() - { - return isset($this->_packageInfo['release_deps']) && - count($this->_packageInfo['release_deps']); - } - - function getDependencyGroup($group) - { - return false; - } - - function isCompatible($pf) - { - return false; - } - - function isSubpackageOf($p) - { - return $p->isSubpackage($this); - } - - function isSubpackage($p) - { - return false; - } - - function dependsOn($package, $channel) - { - if (strtolower($channel) != 'pear.php.net') { - return false; - } - if (!($deps = $this->getDeps())) { - return false; - } - foreach ($deps as $dep) { - if ($dep['type'] != 'pkg') { - continue; - } - if (strtolower($dep['name']) == strtolower($package)) { - return true; - } - } - return false; - } - - function getConfigureOptions() - { - if (isset($this->_packageInfo['configure_options'])) { - return $this->_packageInfo['configure_options']; - } - return false; - } - - function hasConfigureOptions() - { - return isset($this->_packageInfo['configure_options']) && - count($this->_packageInfo['configure_options']); - } - - function addConfigureOption($name, $prompt, $default = false) - { - $o = array('name' => $name, 'prompt' => $prompt); - if ($default !== false) { - $o['default'] = $default; - } - if (!isset($this->_packageInfo['configure_options'])) { - $this->_packageInfo['configure_options'] = array(); - } - $this->_packageInfo['configure_options'][] = $o; - } - - function clearConfigureOptions() - { - unset($this->_packageInfo['configure_options']); - } - - function getProvides() - { - if (isset($this->_packageInfo['provides'])) { - return $this->_packageInfo['provides']; - } - return false; - } - - function getProvidesExtension() - { - return false; - } - - function addFile($dir, $file, $attrs) - { - $dir = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'), $dir); - if ($dir == '/' || $dir == '') { - $dir = ''; - } else { - $dir .= '/'; - } - $file = $dir . $file; - $file = preg_replace('![\\/]+!', '/', $file); - $this->_packageInfo['filelist'][$file] = $attrs; - } - - function getInstallationFilelist() - { - return $this->getFilelist(); - } - - function getFilelist() - { - if (isset($this->_packageInfo['filelist'])) { - return $this->_packageInfo['filelist']; - } - return false; - } - - function setFileAttribute($file, $attr, $value) - { - $this->_packageInfo['filelist'][$file][$attr] = $value; - } - - function resetFilelist() - { - $this->_packageInfo['filelist'] = array(); - } - - function setInstalledAs($file, $path) - { - if ($path) { - return $this->_packageInfo['filelist'][$file]['installed_as'] = $path; - } - unset($this->_packageInfo['filelist'][$file]['installed_as']); - } - - function installedFile($file, $atts) - { - if (isset($this->_packageInfo['filelist'][$file])) { - $this->_packageInfo['filelist'][$file] = - array_merge($this->_packageInfo['filelist'][$file], $atts); - } else { - $this->_packageInfo['filelist'][$file] = $atts; - } - } - - function getChangelog() - { - if (isset($this->_packageInfo['changelog'])) { - return $this->_packageInfo['changelog']; - } - return false; - } - - function getPackagexmlVersion() - { - return '1.0'; - } - - /** - * Wrapper to {@link PEAR_ErrorStack::getErrors()} - * @param boolean determines whether to purge the error stack after retrieving - * @return array - */ - function getValidationWarnings($purge = true) - { - return $this->_stack->getErrors($purge); - } - - // }}} - /** - * Validation error. Also marks the object contents as invalid - * @param error code - * @param array error information - * @access private - */ - function _validateError($code, $params = array()) - { - $this->_stack->push($code, 'error', $params, false, false, debug_backtrace()); - $this->_isValid = false; - } - - /** - * Validation warning. Does not mark the object contents invalid. - * @param error code - * @param array error information - * @access private - */ - function _validateWarning($code, $params = array()) - { - $this->_stack->push($code, 'warning', $params, false, false, debug_backtrace()); - } - - /** - * @param integer error code - * @access protected - */ - function _getErrorMessage() - { - return array( - PEAR_PACKAGEFILE_ERROR_NO_NAME => - 'Missing Package Name', - PEAR_PACKAGEFILE_ERROR_NO_SUMMARY => - 'No summary found', - PEAR_PACKAGEFILE_ERROR_MULTILINE_SUMMARY => - 'Summary should be on one line', - PEAR_PACKAGEFILE_ERROR_NO_DESCRIPTION => - 'Missing description', - PEAR_PACKAGEFILE_ERROR_NO_LICENSE => - 'Missing license', - PEAR_PACKAGEFILE_ERROR_NO_VERSION => - 'No release version found', - PEAR_PACKAGEFILE_ERROR_NO_STATE => - 'No release state found', - PEAR_PACKAGEFILE_ERROR_NO_DATE => - 'No release date found', - PEAR_PACKAGEFILE_ERROR_NO_NOTES => - 'No release notes found', - PEAR_PACKAGEFILE_ERROR_NO_LEAD => - 'Package must have at least one lead maintainer', - PEAR_PACKAGEFILE_ERROR_NO_MAINTAINERS => - 'No maintainers found, at least one must be defined', - PEAR_PACKAGEFILE_ERROR_NO_MAINTHANDLE => - 'Maintainer %index% has no handle (user ID at channel server)', - PEAR_PACKAGEFILE_ERROR_NO_MAINTROLE => - 'Maintainer %index% has no role', - PEAR_PACKAGEFILE_ERROR_NO_MAINTNAME => - 'Maintainer %index% has no name', - PEAR_PACKAGEFILE_ERROR_NO_MAINTEMAIL => - 'Maintainer %index% has no email', - PEAR_PACKAGEFILE_ERROR_NO_DEPNAME => - 'Dependency %index% is not a php dependency, and has no name', - PEAR_PACKAGEFILE_ERROR_NO_DEPREL => - 'Dependency %index% has no relation (rel)', - PEAR_PACKAGEFILE_ERROR_NO_DEPTYPE => - 'Dependency %index% has no type', - PEAR_PACKAGEFILE_ERROR_DEPNAME_IGNORED => - 'PHP Dependency %index% has a name attribute of "%name%" which will be' . - ' ignored!', - PEAR_PACKAGEFILE_ERROR_NO_DEPVERSION => - 'Dependency %index% is not a rel="has" or rel="not" dependency, ' . - 'and has no version', - PEAR_PACKAGEFILE_ERROR_NO_DEPPHPVERSION => - 'Dependency %index% is a type="php" dependency, ' . - 'and has no version', - PEAR_PACKAGEFILE_ERROR_DEPVERSION_IGNORED => - 'Dependency %index% is a rel="%rel%" dependency, versioning is ignored', - PEAR_PACKAGEFILE_ERROR_INVALID_DEPOPTIONAL => - 'Dependency %index% has invalid optional value "%opt%", should be yes or no', - PEAR_PACKAGEFILE_PHP_NO_NOT => - 'Dependency %index%: php dependencies cannot use "not" rel, use "ne"' . - ' to exclude specific versions', - PEAR_PACKAGEFILE_ERROR_NO_CONFNAME => - 'Configure Option %index% has no name', - PEAR_PACKAGEFILE_ERROR_NO_CONFPROMPT => - 'Configure Option %index% has no prompt', - PEAR_PACKAGEFILE_ERROR_NO_FILES => - 'No files in section of package.xml', - PEAR_PACKAGEFILE_ERROR_NO_FILEROLE => - 'File "%file%" has no role, expecting one of "%roles%"', - PEAR_PACKAGEFILE_ERROR_INVALID_FILEROLE => - 'File "%file%" has invalid role "%role%", expecting one of "%roles%"', - PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME => - 'File "%file%" cannot start with ".", cannot package or install', - PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE => - 'Parser error: invalid PHP found in file "%file%"', - PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX => - 'in %file%: %type% "%name%" not prefixed with package name "%package%"', - PEAR_PACKAGEFILE_ERROR_INVALID_FILE => - 'Parser error: invalid PHP file "%file%"', - PEAR_PACKAGEFILE_ERROR_CHANNELVAL => - 'Channel validator error: field "%field%" - %reason%', - PEAR_PACKAGEFILE_ERROR_PHP5 => - 'Error, PHP5 token encountered in %file%, analysis should be in PHP5', - PEAR_PACKAGEFILE_ERROR_FILE_NOTFOUND => - 'File "%file%" in package.xml does not exist', - PEAR_PACKAGEFILE_ERROR_NON_ISO_CHARS => - 'Package.xml contains non-ISO-8859-1 characters, and may not validate', - ); - } - - /** - * Validate XML package definition file. - * - * @access public - * @return boolean - */ - function validate($state = PEAR_VALIDATE_NORMAL, $nofilechecking = false) - { - if (($this->_isValid & $state) == $state) { - return true; - } - $this->_isValid = true; - $info = $this->_packageInfo; - if (empty($info['package'])) { - $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_NAME); - $this->_packageName = $pn = 'unknown'; - } else { - $this->_packageName = $pn = $info['package']; - } - - if (empty($info['summary'])) { - $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_SUMMARY); - } elseif (strpos(trim($info['summary']), "\n") !== false) { - $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_MULTILINE_SUMMARY, - array('summary' => $info['summary'])); - } - if (empty($info['description'])) { - $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DESCRIPTION); - } - if (empty($info['release_license'])) { - $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_LICENSE); - } - if (empty($info['version'])) { - $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_VERSION); - } - if (empty($info['release_state'])) { - $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_STATE); - } - if (empty($info['release_date'])) { - $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DATE); - } - if (empty($info['release_notes'])) { - $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_NOTES); - } - if (empty($info['maintainers'])) { - $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTAINERS); - } else { - $haslead = false; - $i = 1; - foreach ($info['maintainers'] as $m) { - if (empty($m['handle'])) { - $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTHANDLE, - array('index' => $i)); - } - if (empty($m['role'])) { - $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTROLE, - array('index' => $i, 'roles' => PEAR_Common::getUserRoles())); - } elseif ($m['role'] == 'lead') { - $haslead = true; - } - if (empty($m['name'])) { - $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTNAME, - array('index' => $i)); - } - if (empty($m['email'])) { - $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTEMAIL, - array('index' => $i)); - } - $i++; - } - if (!$haslead) { - $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_LEAD); - } - } - if (!empty($info['release_deps'])) { - $i = 1; - foreach ($info['release_deps'] as $d) { - if (!isset($d['type']) || empty($d['type'])) { - $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPTYPE, - array('index' => $i, 'types' => PEAR_Common::getDependencyTypes())); - continue; - } - if (!isset($d['rel']) || empty($d['rel'])) { - $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPREL, - array('index' => $i, 'rels' => PEAR_Common::getDependencyRelations())); - continue; - } - if (!empty($d['optional'])) { - if (!in_array($d['optional'], array('yes', 'no'))) { - $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_DEPOPTIONAL, - array('index' => $i, 'opt' => $d['optional'])); - } - } - if ($d['rel'] != 'has' && $d['rel'] != 'not' && empty($d['version'])) { - $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPVERSION, - array('index' => $i)); - } elseif (($d['rel'] == 'has' || $d['rel'] == 'not') && !empty($d['version'])) { - $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_DEPVERSION_IGNORED, - array('index' => $i, 'rel' => $d['rel'])); - } - if ($d['type'] == 'php' && !empty($d['name'])) { - $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_DEPNAME_IGNORED, - array('index' => $i, 'name' => $d['name'])); - } elseif ($d['type'] != 'php' && empty($d['name'])) { - $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPNAME, - array('index' => $i)); - } - if ($d['type'] == 'php' && empty($d['version'])) { - $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPPHPVERSION, - array('index' => $i)); - } - if (($d['rel'] == 'not') && ($d['type'] == 'php')) { - $this->_validateError(PEAR_PACKAGEFILE_PHP_NO_NOT, - array('index' => $i)); - } - $i++; - } - } - if (!empty($info['configure_options'])) { - $i = 1; - foreach ($info['configure_options'] as $c) { - if (empty($c['name'])) { - $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_CONFNAME, - array('index' => $i)); - } - if (empty($c['prompt'])) { - $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_CONFPROMPT, - array('index' => $i)); - } - $i++; - } - } - if (empty($info['filelist'])) { - $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_FILES); - $errors[] = 'no files'; - } else { - foreach ($info['filelist'] as $file => $fa) { - if (empty($fa['role'])) { - $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_FILEROLE, - array('file' => $file, 'roles' => PEAR_Common::getFileRoles())); - continue; - } elseif (!in_array($fa['role'], PEAR_Common::getFileRoles())) { - $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILEROLE, - array('file' => $file, 'role' => $fa['role'], 'roles' => PEAR_Common::getFileRoles())); - } - if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~', str_replace('\\', '/', $file))) { - // file contains .. parent directory or . cur directory references - $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME, - array('file' => $file)); - } - if (isset($fa['install-as']) && - preg_match('~/\.\.?(/|\\z)|^\.\.?/~', - str_replace('\\', '/', $fa['install-as']))) { - // install-as contains .. parent directory or . cur directory references - $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME, - array('file' => $file . ' [installed as ' . $fa['install-as'] . ']')); - } - if (isset($fa['baseinstalldir']) && - preg_match('~/\.\.?(/|\\z)|^\.\.?/~', - str_replace('\\', '/', $fa['baseinstalldir']))) { - // install-as contains .. parent directory or . cur directory references - $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME, - array('file' => $file . ' [baseinstalldir ' . $fa['baseinstalldir'] . ']')); - } - } - } - if (isset($this->_registry) && $this->_isValid) { - $chan = $this->_registry->getChannel('pear.php.net'); - if (PEAR::isError($chan)) { - $this->_validateError(PEAR_PACKAGEFILE_ERROR_CHANNELVAL, $chan->getMessage()); - return $this->_isValid = 0; - } - $validator = $chan->getValidationObject(); - $validator->setPackageFile($this); - $validator->validate($state); - $failures = $validator->getFailures(); - foreach ($failures['errors'] as $error) { - $this->_validateError(PEAR_PACKAGEFILE_ERROR_CHANNELVAL, $error); - } - foreach ($failures['warnings'] as $warning) { - $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_CHANNELVAL, $warning); - } - } - if ($this->_isValid && $state == PEAR_VALIDATE_PACKAGING && !$nofilechecking) { - if ($this->_analyzePhpFiles()) { - $this->_isValid = true; - } - } - if ($this->_isValid) { - return $this->_isValid = $state; - } - return $this->_isValid = 0; - } - - function _analyzePhpFiles() - { - if (!$this->_isValid) { - return false; - } - if (!isset($this->_packageFile)) { - return false; - } - $dir_prefix = dirname($this->_packageFile); - $common = new PEAR_Common; - $log = isset($this->_logger) ? array(&$this->_logger, 'log') : - array($common, 'log'); - $info = $this->getFilelist(); - foreach ($info as $file => $fa) { - if (!file_exists($dir_prefix . DIRECTORY_SEPARATOR . $file)) { - $this->_validateError(PEAR_PACKAGEFILE_ERROR_FILE_NOTFOUND, - array('file' => realpath($dir_prefix) . DIRECTORY_SEPARATOR . $file)); - continue; - } - if ($fa['role'] == 'php' && $dir_prefix) { - call_user_func_array($log, array(1, "Analyzing $file")); - $srcinfo = $this->_analyzeSourceCode($dir_prefix . DIRECTORY_SEPARATOR . $file); - if ($srcinfo) { - $this->_buildProvidesArray($srcinfo); - } - } - } - $this->_packageName = $pn = $this->getPackage(); - $pnl = strlen($pn); - if (isset($this->_packageInfo['provides'])) { - foreach ((array) $this->_packageInfo['provides'] as $key => $what) { - if (isset($what['explicit'])) { - // skip conformance checks if the provides entry is - // specified in the package.xml file - continue; - } - extract($what); - if ($type == 'class') { - if (!strncasecmp($name, $pn, $pnl)) { - continue; - } - $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX, - array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn)); - } elseif ($type == 'function') { - if (strstr($name, '::') || !strncasecmp($name, $pn, $pnl)) { - continue; - } - $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX, - array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn)); - } - } - } - return $this->_isValid; - } - - /** - * Get the default xml generator object - * - * @return PEAR_PackageFile_Generator_v1 - */ - function &getDefaultGenerator() - { - if (!class_exists('PEAR_PackageFile_Generator_v1')) { - require_once 'PEAR/PackageFile/Generator/v1.php'; - } - $a = &new PEAR_PackageFile_Generator_v1($this); - return $a; - } - - /** - * Get the contents of a file listed within the package.xml - * @param string - * @return string - */ - function getFileContents($file) - { - if ($this->_archiveFile == $this->_packageFile) { // unpacked - $dir = dirname($this->_packageFile); - $file = $dir . DIRECTORY_SEPARATOR . $file; - $file = str_replace(array('/', '\\'), - array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR), $file); - if (file_exists($file) && is_readable($file)) { - return implode('', file($file)); - } - } else { // tgz - if (!class_exists('Archive_Tar')) { - require_once 'Archive/Tar.php'; - } - $tar = &new Archive_Tar($this->_archiveFile); - $tar->pushErrorHandling(PEAR_ERROR_RETURN); - if ($file != 'package.xml' && $file != 'package2.xml') { - $file = $this->getPackage() . '-' . $this->getVersion() . '/' . $file; - } - $file = $tar->extractInString($file); - $tar->popErrorHandling(); - if (PEAR::isError($file)) { - return PEAR::raiseError("Cannot locate file '$file' in archive"); - } - return $file; - } - } - - // {{{ analyzeSourceCode() - /** - * Analyze the source code of the given PHP file - * - * @param string Filename of the PHP file - * @return mixed - * @access private - */ - function _analyzeSourceCode($file) - { - if (!function_exists("token_get_all")) { - return false; - } - if (!defined('T_DOC_COMMENT')) { - define('T_DOC_COMMENT', T_COMMENT); - } - if (!defined('T_INTERFACE')) { - define('T_INTERFACE', -1); - } - if (!defined('T_IMPLEMENTS')) { - define('T_IMPLEMENTS', -1); - } - if (!$fp = @fopen($file, "r")) { - return false; - } - fclose($fp); - $contents = file_get_contents($file); - $tokens = token_get_all($contents); -/* - for ($i = 0; $i < sizeof($tokens); $i++) { - @list($token, $data) = $tokens[$i]; - if (is_string($token)) { - var_dump($token); - } else { - print token_name($token) . ' '; - var_dump(rtrim($data)); - } - } -*/ - $look_for = 0; - $paren_level = 0; - $bracket_level = 0; - $brace_level = 0; - $lastphpdoc = ''; - $current_class = ''; - $current_interface = ''; - $current_class_level = -1; - $current_function = ''; - $current_function_level = -1; - $declared_classes = array(); - $declared_interfaces = array(); - $declared_functions = array(); - $declared_methods = array(); - $used_classes = array(); - $used_functions = array(); - $extends = array(); - $implements = array(); - $nodeps = array(); - $inquote = false; - $interface = false; - for ($i = 0; $i < sizeof($tokens); $i++) { - if (is_array($tokens[$i])) { - list($token, $data) = $tokens[$i]; - } else { - $token = $tokens[$i]; - $data = ''; - } - if ($inquote) { - if ($token != '"' && $token != T_END_HEREDOC) { - continue; - } else { - $inquote = false; - continue; - } - } - switch ($token) { - case T_WHITESPACE : - continue; - case ';': - if ($interface) { - $current_function = ''; - $current_function_level = -1; - } - break; - case '"': - case T_START_HEREDOC: - $inquote = true; - break; - case T_CURLY_OPEN: - case T_DOLLAR_OPEN_CURLY_BRACES: - case '{': $brace_level++; continue 2; - case '}': - $brace_level--; - if ($current_class_level == $brace_level) { - $current_class = ''; - $current_class_level = -1; - } - if ($current_function_level == $brace_level) { - $current_function = ''; - $current_function_level = -1; - } - continue 2; - case '[': $bracket_level++; continue 2; - case ']': $bracket_level--; continue 2; - case '(': $paren_level++; continue 2; - case ')': $paren_level--; continue 2; - case T_INTERFACE: - $interface = true; - case T_CLASS: - if (($current_class_level != -1) || ($current_function_level != -1)) { - $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE, - array('file' => $file)); - return false; - } - case T_FUNCTION: - case T_NEW: - case T_EXTENDS: - case T_IMPLEMENTS: - $look_for = $token; - continue 2; - case T_STRING: - if (version_compare(zend_version(), '2.0', '<')) { - if (in_array(strtolower($data), - array('public', 'private', 'protected', 'abstract', - 'interface', 'implements', 'throw') - )) { - $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_PHP5, - array($file)); - } - } - if ($look_for == T_CLASS) { - $current_class = $data; - $current_class_level = $brace_level; - $declared_classes[] = $current_class; - } elseif ($look_for == T_INTERFACE) { - $current_interface = $data; - $current_class_level = $brace_level; - $declared_interfaces[] = $current_interface; - } elseif ($look_for == T_IMPLEMENTS) { - $implements[$current_class] = $data; - } elseif ($look_for == T_EXTENDS) { - $extends[$current_class] = $data; - } elseif ($look_for == T_FUNCTION) { - if ($current_class) { - $current_function = "$current_class::$data"; - $declared_methods[$current_class][] = $data; - } elseif ($current_interface) { - $current_function = "$current_interface::$data"; - $declared_methods[$current_interface][] = $data; - } else { - $current_function = $data; - $declared_functions[] = $current_function; - } - $current_function_level = $brace_level; - $m = array(); - } elseif ($look_for == T_NEW) { - $used_classes[$data] = true; - } - $look_for = 0; - continue 2; - case T_VARIABLE: - $look_for = 0; - continue 2; - case T_DOC_COMMENT: - case T_COMMENT: - if (preg_match('!^/\*\*\s!', $data)) { - $lastphpdoc = $data; - if (preg_match_all('/@nodep\s+(\S+)/', $lastphpdoc, $m)) { - $nodeps = array_merge($nodeps, $m[1]); - } - } - continue 2; - case T_DOUBLE_COLON: - if (!($tokens[$i - 1][0] == T_WHITESPACE || $tokens[$i - 1][0] == T_STRING)) { - $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE, - array('file' => $file)); - return false; - } - $class = $tokens[$i - 1][1]; - if (strtolower($class) != 'parent') { - $used_classes[$class] = true; - } - continue 2; - } - } - return array( - "source_file" => $file, - "declared_classes" => $declared_classes, - "declared_interfaces" => $declared_interfaces, - "declared_methods" => $declared_methods, - "declared_functions" => $declared_functions, - "used_classes" => array_diff(array_keys($used_classes), $nodeps), - "inheritance" => $extends, - "implements" => $implements, - ); - } - - /** - * Build a "provides" array from data returned by - * analyzeSourceCode(). The format of the built array is like - * this: - * - * array( - * 'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'), - * ... - * ) - * - * - * @param array $srcinfo array with information about a source file - * as returned by the analyzeSourceCode() method. - * - * @return void - * - * @access private - * - */ - function _buildProvidesArray($srcinfo) - { - if (!$this->_isValid) { - return false; - } - $file = basename($srcinfo['source_file']); - $pn = $this->getPackage(); - $pnl = strlen($pn); - foreach ($srcinfo['declared_classes'] as $class) { - $key = "class;$class"; - if (isset($this->_packageInfo['provides'][$key])) { - continue; - } - $this->_packageInfo['provides'][$key] = - array('file'=> $file, 'type' => 'class', 'name' => $class); - if (isset($srcinfo['inheritance'][$class])) { - $this->_packageInfo['provides'][$key]['extends'] = - $srcinfo['inheritance'][$class]; - } - } - foreach ($srcinfo['declared_methods'] as $class => $methods) { - foreach ($methods as $method) { - $function = "$class::$method"; - $key = "function;$function"; - if ($method{0} == '_' || !strcasecmp($method, $class) || - isset($this->_packageInfo['provides'][$key])) { - continue; - } - $this->_packageInfo['provides'][$key] = - array('file'=> $file, 'type' => 'function', 'name' => $function); - } - } - - foreach ($srcinfo['declared_functions'] as $function) { - $key = "function;$function"; - if ($function{0} == '_' || isset($this->_packageInfo['provides'][$key])) { - continue; - } - if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) { - $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\""; - } - $this->_packageInfo['provides'][$key] = - array('file'=> $file, 'type' => 'function', 'name' => $function); - } - } - - // }}} -} -?> diff --git a/pear/PEAR/PackageFile/v2.php b/pear/PEAR/PackageFile/v2.php deleted file mode 100644 index 27310d4..0000000 --- a/pear/PEAR/PackageFile/v2.php +++ /dev/null @@ -1,2049 +0,0 @@ - - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a1 - */ -/** - * For error handling - */ -require_once 'PEAR/ErrorStack.php'; -/** - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a1 - */ -class PEAR_PackageFile_v2 -{ - - /** - * Parsed package information - * @var array - * @access private - */ - var $_packageInfo = array(); - - /** - * path to package .tgz or false if this is a local/extracted package.xml - * @var string|false - * @access private - */ - var $_archiveFile; - - /** - * path to package .xml or false if this is an abstract parsed-from-string xml - * @var string|false - * @access private - */ - var $_packageFile; - - /** - * This is used by file analysis routines to log progress information - * @var PEAR_Common - * @access protected - */ - var $_logger; - - /** - * This is set to the highest validation level that has been validated - * - * If the package.xml is invalid or unknown, this is set to 0. If - * normal validation has occurred, this is set to PEAR_VALIDATE_NORMAL. If - * downloading/installation validation has occurred it is set to PEAR_VALIDATE_DOWNLOADING - * or INSTALLING, and so on up to PEAR_VALIDATE_PACKAGING. This allows validation - * "caching" to occur, which is particularly important for package validation, so - * that PHP files are not validated twice - * @var int - * @access private - */ - var $_isValid = 0; - - /** - * True if the filelist has been validated - * @param bool - */ - var $_filesValid = false; - - /** - * @var PEAR_Registry - * @access protected - */ - var $_registry; - - /** - * @var PEAR_Config - * @access protected - */ - var $_config; - - /** - * Optional Dependency group requested for installation - * @var string - * @access private - */ - var $_requestedGroup = false; - - /** - * @var PEAR_ErrorStack - * @access protected - */ - var $_stack; - - /** - * Namespace prefix used for tasks in this package.xml - use tasks: whenever possible - */ - var $_tasksNs; - - /** - * Determines whether this packagefile was initialized only with partial package info - * - * If this package file was constructed via parsing REST, it will only contain - * - * - package name - * - channel name - * - dependencies - * @var boolean - * @access private - */ - var $_incomplete = true; - - /** - * @var PEAR_PackageFile_v2_Validator - */ - var $_v2Validator; - - /** - * The constructor merely sets up the private error stack - */ - function PEAR_PackageFile_v2() - { - $this->_stack = new PEAR_ErrorStack('PEAR_PackageFile_v2', false, null); - $this->_isValid = false; - } - - /** - * To make unit-testing easier - * @param PEAR_Frontend_* - * @param array options - * @param PEAR_Config - * @return PEAR_Downloader - * @access protected - */ - function &getPEARDownloader(&$i, $o, &$c) - { - $z = &new PEAR_Downloader($i, $o, $c); - return $z; - } - - /** - * To make unit-testing easier - * @param PEAR_Config - * @param array options - * @param array package name as returned from {@link PEAR_Registry::parsePackageName()} - * @param int PEAR_VALIDATE_* constant - * @return PEAR_Dependency2 - * @access protected - */ - function &getPEARDependency2(&$c, $o, $p, $s = PEAR_VALIDATE_INSTALLING) - { - if (!class_exists('PEAR_Dependency2')) { - require_once 'PEAR/Dependency2.php'; - } - $z = &new PEAR_Dependency2($c, $o, $p, $s); - return $z; - } - - function getInstalledBinary() - { - return isset($this->_packageInfo['#binarypackage']) ? $this->_packageInfo['#binarypackage'] : - false; - } - - /** - * Installation of source package has failed, attempt to download and install the - * binary version of this package. - * @param PEAR_Installer - * @return array|false - */ - function installBinary(&$installer) - { - if (!OS_WINDOWS) { - $a = false; - return $a; - } - if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') { - $releasetype = $this->getPackageType() . 'release'; - if (!is_array($installer->getInstallPackages())) { - $a = false; - return $a; - } - foreach ($installer->getInstallPackages() as $p) { - if ($p->isExtension($this->_packageInfo['providesextension'])) { - if ($p->getPackageType() != 'extsrc' && $p->getPackageType() != 'zendextsrc') { - $a = false; - return $a; // the user probably downloaded it separately - } - } - } - if (isset($this->_packageInfo[$releasetype]['binarypackage'])) { - $installer->log(0, 'Attempting to download binary version of extension "' . - $this->_packageInfo['providesextension'] . '"'); - $params = $this->_packageInfo[$releasetype]['binarypackage']; - if (!is_array($params) || !isset($params[0])) { - $params = array($params); - } - if (isset($this->_packageInfo['channel'])) { - foreach ($params as $i => $param) { - $params[$i] = array('channel' => $this->_packageInfo['channel'], - 'package' => $param, 'version' => $this->getVersion()); - } - } - $dl = &$this->getPEARDownloader($installer->ui, $installer->getOptions(), - $installer->config); - $verbose = $dl->config->get('verbose'); - $dl->config->set('verbose', -1); - foreach ($params as $param) { - PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - $ret = $dl->download(array($param)); - PEAR::popErrorHandling(); - if (is_array($ret) && count($ret)) { - break; - } - } - $dl->config->set('verbose', $verbose); - if (is_array($ret)) { - if (count($ret) == 1) { - $pf = $ret[0]->getPackageFile(); - PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - $err = $installer->install($ret[0]); - PEAR::popErrorHandling(); - if (is_array($err)) { - $this->_packageInfo['#binarypackage'] = $ret[0]->getPackage(); - // "install" self, so all dependencies will work transparently - $this->_registry->addPackage2($this); - $installer->log(0, 'Download and install of binary extension "' . - $this->_registry->parsedPackageNameToString( - array('channel' => $pf->getChannel(), - 'package' => $pf->getPackage()), true) . '" successful'); - $a = array($ret[0], $err); - return $a; - } - $installer->log(0, 'Download and install of binary extension "' . - $this->_registry->parsedPackageNameToString( - array('channel' => $pf->getChannel(), - 'package' => $pf->getPackage()), true) . '" failed'); - } - } - } - } - $a = false; - return $a; - } - - /** - * @return string|false Extension name - */ - function getProvidesExtension() - { - if (in_array($this->getPackageType(), - array('extsrc', 'extbin', 'zendextsrc', 'zendextbin'))) { - if (isset($this->_packageInfo['providesextension'])) { - return $this->_packageInfo['providesextension']; - } - } - return false; - } - - /** - * @param string Extension name - * @return bool - */ - function isExtension($extension) - { - if (in_array($this->getPackageType(), - array('extsrc', 'extbin', 'zendextsrc', 'zendextbin'))) { - return $this->_packageInfo['providesextension'] == $extension; - } - return false; - } - - /** - * Tests whether every part of the package.xml 1.0 is represented in - * this package.xml 2.0 - * @param PEAR_PackageFile_v1 - * @return bool - */ - function isEquivalent($pf1) - { - if (!$pf1) { - return true; - } - if ($this->getPackageType() == 'bundle') { - return false; - } - $this->_stack->getErrors(true); - if (!$pf1->validate(PEAR_VALIDATE_NORMAL)) { - return false; - } - $pass = true; - if ($pf1->getPackage() != $this->getPackage()) { - $this->_differentPackage($pf1->getPackage()); - $pass = false; - } - if ($pf1->getVersion() != $this->getVersion()) { - $this->_differentVersion($pf1->getVersion()); - $pass = false; - } - if (trim($pf1->getSummary()) != $this->getSummary()) { - $this->_differentSummary($pf1->getSummary()); - $pass = false; - } - if (preg_replace('/\s+/', '', $pf1->getDescription()) != - preg_replace('/\s+/', '', $this->getDescription())) { - $this->_differentDescription($pf1->getDescription()); - $pass = false; - } - if ($pf1->getState() != $this->getState()) { - $this->_differentState($pf1->getState()); - $pass = false; - } - if (!strstr(preg_replace('/\s+/', '', $this->getNotes()), - preg_replace('/\s+/', '', $pf1->getNotes()))) { - $this->_differentNotes($pf1->getNotes()); - $pass = false; - } - $mymaintainers = $this->getMaintainers(); - $yourmaintainers = $pf1->getMaintainers(); - for ($i1 = 0; $i1 < count($yourmaintainers); $i1++) { - $reset = false; - for ($i2 = 0; $i2 < count($mymaintainers); $i2++) { - if ($mymaintainers[$i2]['handle'] == $yourmaintainers[$i1]['handle']) { - if ($mymaintainers[$i2]['role'] != $yourmaintainers[$i1]['role']) { - $this->_differentRole($mymaintainers[$i2]['handle'], - $yourmaintainers[$i1]['role'], $mymaintainers[$i2]['role']); - $pass = false; - } - if ($mymaintainers[$i2]['email'] != $yourmaintainers[$i1]['email']) { - $this->_differentEmail($mymaintainers[$i2]['handle'], - $yourmaintainers[$i1]['email'], $mymaintainers[$i2]['email']); - $pass = false; - } - if ($mymaintainers[$i2]['name'] != $yourmaintainers[$i1]['name']) { - $this->_differentName($mymaintainers[$i2]['handle'], - $yourmaintainers[$i1]['name'], $mymaintainers[$i2]['name']); - $pass = false; - } - unset($mymaintainers[$i2]); - $mymaintainers = array_values($mymaintainers); - unset($yourmaintainers[$i1]); - $yourmaintainers = array_values($yourmaintainers); - $reset = true; - break; - } - } - if ($reset) { - $i1 = -1; - } - } - $this->_unmatchedMaintainers($mymaintainers, $yourmaintainers); - $filelist = $this->getFilelist(); - foreach ($pf1->getFilelist() as $file => $atts) { - if (!isset($filelist[$file])) { - $this->_missingFile($file); - $pass = false; - } - } - return $pass; - } - - function _differentPackage($package) - { - $this->_stack->push(__FUNCTION__, 'error', array('package' => $package, - 'self' => $this->getPackage()), - 'package.xml 1.0 package "%package%" does not match "%self%"'); - } - - function _differentVersion($version) - { - $this->_stack->push(__FUNCTION__, 'error', array('version' => $version, - 'self' => $this->getVersion()), - 'package.xml 1.0 version "%version%" does not match "%self%"'); - } - - function _differentState($state) - { - $this->_stack->push(__FUNCTION__, 'error', array('state' => $state, - 'self' => $this->getState()), - 'package.xml 1.0 state "%state%" does not match "%self%"'); - } - - function _differentRole($handle, $role, $selfrole) - { - $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle, - 'role' => $role, 'self' => $selfrole), - 'package.xml 1.0 maintainer "%handle%" role "%role%" does not match "%self%"'); - } - - function _differentEmail($handle, $email, $selfemail) - { - $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle, - 'email' => $email, 'self' => $selfemail), - 'package.xml 1.0 maintainer "%handle%" email "%email%" does not match "%self%"'); - } - - function _differentName($handle, $name, $selfname) - { - $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle, - 'name' => $name, 'self' => $selfname), - 'package.xml 1.0 maintainer "%handle%" name "%name%" does not match "%self%"'); - } - - function _unmatchedMaintainers($my, $yours) - { - if ($my) { - array_walk($my, create_function('&$i, $k', '$i = $i["handle"];')); - $this->_stack->push(__FUNCTION__, 'error', array('handles' => $my), - 'package.xml 2.0 has unmatched extra maintainers "%handles%"'); - } - if ($yours) { - array_walk($yours, create_function('&$i, $k', '$i = $i["handle"];')); - $this->_stack->push(__FUNCTION__, 'error', array('handles' => $yours), - 'package.xml 1.0 has unmatched extra maintainers "%handles%"'); - } - } - - function _differentNotes($notes) - { - $truncnotes = strlen($notes) < 25 ? $notes : substr($notes, 0, 24) . '...'; - $truncmynotes = strlen($this->getNotes()) < 25 ? $this->getNotes() : - substr($this->getNotes(), 0, 24) . '...'; - $this->_stack->push(__FUNCTION__, 'error', array('notes' => $truncnotes, - 'self' => $truncmynotes), - 'package.xml 1.0 release notes "%notes%" do not match "%self%"'); - } - - function _differentSummary($summary) - { - $truncsummary = strlen($summary) < 25 ? $summary : substr($summary, 0, 24) . '...'; - $truncmysummary = strlen($this->getsummary()) < 25 ? $this->getSummary() : - substr($this->getsummary(), 0, 24) . '...'; - $this->_stack->push(__FUNCTION__, 'error', array('summary' => $truncsummary, - 'self' => $truncmysummary), - 'package.xml 1.0 summary "%summary%" does not match "%self%"'); - } - - function _differentDescription($description) - { - $truncdescription = trim(strlen($description) < 25 ? $description : substr($description, 0, 24) . '...'); - $truncmydescription = trim(strlen($this->getDescription()) < 25 ? $this->getDescription() : - substr($this->getdescription(), 0, 24) . '...'); - $this->_stack->push(__FUNCTION__, 'error', array('description' => $truncdescription, - 'self' => $truncmydescription), - 'package.xml 1.0 description "%description%" does not match "%self%"'); - } - - function _missingFile($file) - { - $this->_stack->push(__FUNCTION__, 'error', array('file' => $file), - 'package.xml 1.0 file "%file%" is not present in '); - } - - /** - * WARNING - do not use this function unless you know what you're doing - */ - function setRawState($state) - { - if (!isset($this->_packageInfo['stability'])) { - $this->_packageInfo['stability'] = array(); - } - $this->_packageInfo['stability']['release'] = $state; - } - - /** - * WARNING - do not use this function unless you know what you're doing - */ - function setRawCompatible($compatible) - { - $this->_packageInfo['compatible'] = $compatible; - } - - /** - * WARNING - do not use this function unless you know what you're doing - */ - function setRawPackage($package) - { - $this->_packageInfo['name'] = $package; - } - - /** - * WARNING - do not use this function unless you know what you're doing - */ - function setRawChannel($channel) - { - $this->_packageInfo['channel'] = $channel; - } - - function setRequestedGroup($group) - { - $this->_requestedGroup = $group; - } - - function getRequestedGroup() - { - if (isset($this->_requestedGroup)) { - return $this->_requestedGroup; - } - return false; - } - - /** - * For saving in the registry. - * - * Set the last version that was installed - * @param string - */ - function setLastInstalledVersion($version) - { - $this->_packageInfo['_lastversion'] = $version; - } - - /** - * @return string|false - */ - function getLastInstalledVersion() - { - if (isset($this->_packageInfo['_lastversion'])) { - return $this->_packageInfo['_lastversion']; - } - return false; - } - - /** - * Determines whether this package.xml has post-install scripts or not - * @return array|false - */ - function listPostinstallScripts() - { - $filelist = $this->getFilelist(); - $contents = $this->getContents(); - $contents = $contents['dir']['file']; - if (!is_array($contents) || !isset($contents[0])) { - $contents = array($contents); - } - $taskfiles = array(); - foreach ($contents as $file) { - $atts = $file['attribs']; - unset($file['attribs']); - if (count($file)) { - $taskfiles[$atts['name']] = $file; - } - } - $common = new PEAR_Common; - $common->debug = $this->_config->get('verbose'); - $this->_scripts = array(); - $ret = array(); - foreach ($taskfiles as $name => $tasks) { - if (!isset($filelist[$name])) { - // ignored files will not be in the filelist - continue; - } - $atts = $filelist[$name]; - foreach ($tasks as $tag => $raw) { - $task = $this->getTask($tag); - $task = &new $task($this->_config, $common, PEAR_TASK_INSTALL); - if ($task->isScript()) { - $ret[] = $filelist[$name]['installed_as']; - } - } - } - if (count($ret)) { - return $ret; - } - return false; - } - - /** - * Initialize post-install scripts for running - * - * This method can be used to detect post-install scripts, as the return value - * indicates whether any exist - * @return bool - */ - function initPostinstallScripts() - { - $filelist = $this->getFilelist(); - $contents = $this->getContents(); - $contents = $contents['dir']['file']; - if (!is_array($contents) || !isset($contents[0])) { - $contents = array($contents); - } - $taskfiles = array(); - foreach ($contents as $file) { - $atts = $file['attribs']; - unset($file['attribs']); - if (count($file)) { - $taskfiles[$atts['name']] = $file; - } - } - $common = new PEAR_Common; - $common->debug = $this->_config->get('verbose'); - $this->_scripts = array(); - foreach ($taskfiles as $name => $tasks) { - if (!isset($filelist[$name])) { - // file was not installed due to installconditions - continue; - } - $atts = $filelist[$name]; - foreach ($tasks as $tag => $raw) { - $taskname = $this->getTask($tag); - $task = &new $taskname($this->_config, $common, PEAR_TASK_INSTALL); - if (!$task->isScript()) { - continue; // scripts are only handled after installation - } - $lastversion = isset($this->_packageInfo['_lastversion']) ? - $this->_packageInfo['_lastversion'] : null; - $task->init($raw, $atts, $lastversion); - $res = $task->startSession($this, $atts['installed_as']); - if (!$res) { - continue; // skip this file - } - if (PEAR::isError($res)) { - return $res; - } - $assign = &$task; - $this->_scripts[] = &$assign; - } - } - if (count($this->_scripts)) { - return true; - } - return false; - } - - function runPostinstallScripts() - { - if ($this->initPostinstallScripts()) { - $ui = &PEAR_Frontend::singleton(); - if ($ui) { - $ui->runPostinstallScripts($this->_scripts, $this); - } - } - } - - - /** - * Convert a recursive set of
    and tags into a single tag with - * tags. - */ - function flattenFilelist() - { - if (isset($this->_packageInfo['bundle'])) { - return; - } - $filelist = array(); - if (isset($this->_packageInfo['contents']['dir']['dir'])) { - $this->_getFlattenedFilelist($filelist, $this->_packageInfo['contents']['dir']); - if (!isset($filelist[1])) { - $filelist = $filelist[0]; - } - $this->_packageInfo['contents']['dir']['file'] = $filelist; - unset($this->_packageInfo['contents']['dir']['dir']); - } else { - // else already flattened but check for baseinstalldir propagation - if (isset($this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'])) { - if (isset($this->_packageInfo['contents']['dir']['file'][0])) { - foreach ($this->_packageInfo['contents']['dir']['file'] as $i => $file) { - if (isset($file['attribs']['baseinstalldir'])) { - continue; - } - $this->_packageInfo['contents']['dir']['file'][$i]['attribs']['baseinstalldir'] - = $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir']; - } - } else { - if (!isset($this->_packageInfo['contents']['dir']['file']['attribs']['baseinstalldir'])) { - $this->_packageInfo['contents']['dir']['file']['attribs']['baseinstalldir'] - = $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir']; - } - } - } - } - } - - /** - * @param array the final flattened file list - * @param array the current directory being processed - * @param string|false any recursively inherited baeinstalldir attribute - * @param string private recursion variable - * @return array - * @access protected - */ - function _getFlattenedFilelist(&$files, $dir, $baseinstall = false, $path = '') - { - if (isset($dir['attribs']) && isset($dir['attribs']['baseinstalldir'])) { - $baseinstall = $dir['attribs']['baseinstalldir']; - } - if (isset($dir['dir'])) { - if (!isset($dir['dir'][0])) { - $dir['dir'] = array($dir['dir']); - } - foreach ($dir['dir'] as $subdir) { - if (!isset($subdir['attribs']) || !isset($subdir['attribs']['name'])) { - $name = '*unknown*'; - } else { - $name = $subdir['attribs']['name']; - } - $newpath = empty($path) ? $name : - $path . '/' . $name; - $this->_getFlattenedFilelist($files, $subdir, - $baseinstall, $newpath); - } - } - if (isset($dir['file'])) { - if (!isset($dir['file'][0])) { - $dir['file'] = array($dir['file']); - } - foreach ($dir['file'] as $file) { - $attrs = $file['attribs']; - $name = $attrs['name']; - if ($baseinstall && !isset($attrs['baseinstalldir'])) { - $attrs['baseinstalldir'] = $baseinstall; - } - $attrs['name'] = empty($path) ? $name : $path . '/' . $name; - $attrs['name'] = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'), - $attrs['name']); - $file['attribs'] = $attrs; - $files[] = $file; - } - } - } - - function setConfig(&$config) - { - $this->_config = &$config; - $this->_registry = &$config->getRegistry(); - } - - function setLogger(&$logger) - { - if (!is_object($logger) || !method_exists($logger, 'log')) { - return PEAR::raiseError('Logger must be compatible with PEAR_Common::log'); - } - $this->_logger = &$logger; - } - - /** - * WARNING - do not use this function directly unless you know what you're doing - */ - function setDeps($deps) - { - $this->_packageInfo['dependencies'] = $deps; - } - - /** - * WARNING - do not use this function directly unless you know what you're doing - */ - function setCompatible($compat) - { - $this->_packageInfo['compatible'] = $compat; - } - - function setPackagefile($file, $archive = false) - { - $this->_packageFile = $file; - $this->_archiveFile = $archive ? $archive : $file; - } - - /** - * Wrapper to {@link PEAR_ErrorStack::getErrors()} - * @param boolean determines whether to purge the error stack after retrieving - * @return array - */ - function getValidationWarnings($purge = true) - { - return $this->_stack->getErrors($purge); - } - - function getPackageFile() - { - return $this->_packageFile; - } - - function getArchiveFile() - { - return $this->_archiveFile; - } - - - /** - * Directly set the array that defines this packagefile - * - * WARNING: no validation. This should only be performed by internal methods - * inside PEAR or by inputting an array saved from an existing PEAR_PackageFile_v2 - * @param array - */ - function fromArray($pinfo) - { - unset($pinfo['old']); - unset($pinfo['xsdversion']); - // If the changelog isn't an array then it was passed in as an empty tag - if (isset($pinfo['changelog']) && !is_array($pinfo['changelog'])) { - unset($pinfo['changelog']); - } - $this->_incomplete = false; - $this->_packageInfo = $pinfo; - } - - function isIncomplete() - { - return $this->_incomplete; - } - - /** - * @return array - */ - function toArray($forreg = false) - { - if (!$this->validate(PEAR_VALIDATE_NORMAL)) { - return false; - } - return $this->getArray($forreg); - } - - function getArray($forReg = false) - { - if ($forReg) { - $arr = $this->_packageInfo; - $arr['old'] = array(); - $arr['old']['version'] = $this->getVersion(); - $arr['old']['release_date'] = $this->getDate(); - $arr['old']['release_state'] = $this->getState(); - $arr['old']['release_license'] = $this->getLicense(); - $arr['old']['release_notes'] = $this->getNotes(); - $arr['old']['release_deps'] = $this->getDeps(); - $arr['old']['maintainers'] = $this->getMaintainers(); - $arr['xsdversion'] = '2.0'; - return $arr; - } else { - $info = $this->_packageInfo; - unset($info['dirtree']); - if (isset($info['_lastversion'])) { - unset($info['_lastversion']); - } - if (isset($info['#binarypackage'])) { - unset($info['#binarypackage']); - } - return $info; - } - } - - function packageInfo($field) - { - $arr = $this->getArray(true); - if ($field == 'state') { - return $arr['stability']['release']; - } - if ($field == 'api-version') { - return $arr['version']['api']; - } - if ($field == 'api-state') { - return $arr['stability']['api']; - } - if (isset($arr['old'][$field])) { - if (!is_string($arr['old'][$field])) { - return null; - } - return $arr['old'][$field]; - } - if (isset($arr[$field])) { - if (!is_string($arr[$field])) { - return null; - } - return $arr[$field]; - } - return null; - } - - function getName() - { - return $this->getPackage(); - } - - function getPackage() - { - if (isset($this->_packageInfo['name'])) { - return $this->_packageInfo['name']; - } - return false; - } - - function getChannel() - { - if (isset($this->_packageInfo['uri'])) { - return '__uri'; - } - if (isset($this->_packageInfo['channel'])) { - return strtolower($this->_packageInfo['channel']); - } - return false; - } - - function getUri() - { - if (isset($this->_packageInfo['uri'])) { - return $this->_packageInfo['uri']; - } - return false; - } - - function getExtends() - { - if (isset($this->_packageInfo['extends'])) { - return $this->_packageInfo['extends']; - } - return false; - } - - function getSummary() - { - if (isset($this->_packageInfo['summary'])) { - return $this->_packageInfo['summary']; - } - return false; - } - - function getDescription() - { - if (isset($this->_packageInfo['description'])) { - return $this->_packageInfo['description']; - } - return false; - } - - function getMaintainers($raw = false) - { - if (!isset($this->_packageInfo['lead'])) { - return false; - } - if ($raw) { - $ret = array('lead' => $this->_packageInfo['lead']); - (isset($this->_packageInfo['developer'])) ? - $ret['developer'] = $this->_packageInfo['developer'] :null; - (isset($this->_packageInfo['contributor'])) ? - $ret['contributor'] = $this->_packageInfo['contributor'] :null; - (isset($this->_packageInfo['helper'])) ? - $ret['helper'] = $this->_packageInfo['helper'] :null; - return $ret; - } else { - $ret = array(); - $leads = isset($this->_packageInfo['lead'][0]) ? $this->_packageInfo['lead'] : - array($this->_packageInfo['lead']); - foreach ($leads as $lead) { - $s = $lead; - $s['handle'] = $s['user']; - unset($s['user']); - $s['role'] = 'lead'; - $ret[] = $s; - } - if (isset($this->_packageInfo['developer'])) { - $leads = isset($this->_packageInfo['developer'][0]) ? - $this->_packageInfo['developer'] : - array($this->_packageInfo['developer']); - foreach ($leads as $maintainer) { - $s = $maintainer; - $s['handle'] = $s['user']; - unset($s['user']); - $s['role'] = 'developer'; - $ret[] = $s; - } - } - if (isset($this->_packageInfo['contributor'])) { - $leads = isset($this->_packageInfo['contributor'][0]) ? - $this->_packageInfo['contributor'] : - array($this->_packageInfo['contributor']); - foreach ($leads as $maintainer) { - $s = $maintainer; - $s['handle'] = $s['user']; - unset($s['user']); - $s['role'] = 'contributor'; - $ret[] = $s; - } - } - if (isset($this->_packageInfo['helper'])) { - $leads = isset($this->_packageInfo['helper'][0]) ? - $this->_packageInfo['helper'] : - array($this->_packageInfo['helper']); - foreach ($leads as $maintainer) { - $s = $maintainer; - $s['handle'] = $s['user']; - unset($s['user']); - $s['role'] = 'helper'; - $ret[] = $s; - } - } - return $ret; - } - return false; - } - - function getLeads() - { - if (isset($this->_packageInfo['lead'])) { - return $this->_packageInfo['lead']; - } - return false; - } - - function getDevelopers() - { - if (isset($this->_packageInfo['developer'])) { - return $this->_packageInfo['developer']; - } - return false; - } - - function getContributors() - { - if (isset($this->_packageInfo['contributor'])) { - return $this->_packageInfo['contributor']; - } - return false; - } - - function getHelpers() - { - if (isset($this->_packageInfo['helper'])) { - return $this->_packageInfo['helper']; - } - return false; - } - - function setDate($date) - { - if (!isset($this->_packageInfo['date'])) { - // ensure that the extends tag is set up in the right location - $this->_packageInfo = $this->_insertBefore($this->_packageInfo, - array('time', 'version', - 'stability', 'license', 'notes', 'contents', 'compatible', - 'dependencies', 'providesextension', 'srcpackage', 'srcuri', - 'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', - 'zendextbinrelease', 'bundle', 'changelog'), array(), 'date'); - } - $this->_packageInfo['date'] = $date; - $this->_isValid = 0; - } - - function setTime($time) - { - $this->_isValid = 0; - if (!isset($this->_packageInfo['time'])) { - // ensure that the time tag is set up in the right location - $this->_packageInfo = $this->_insertBefore($this->_packageInfo, - array('version', - 'stability', 'license', 'notes', 'contents', 'compatible', - 'dependencies', 'providesextension', 'srcpackage', 'srcuri', - 'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', - 'zendextbinrelease', 'bundle', 'changelog'), $time, 'time'); - } - $this->_packageInfo['time'] = $time; - } - - function getDate() - { - if (isset($this->_packageInfo['date'])) { - return $this->_packageInfo['date']; - } - return false; - } - - function getTime() - { - if (isset($this->_packageInfo['time'])) { - return $this->_packageInfo['time']; - } - return false; - } - - /** - * @param package|api version category to return - */ - function getVersion($key = 'release') - { - if (isset($this->_packageInfo['version'][$key])) { - return $this->_packageInfo['version'][$key]; - } - return false; - } - - function getStability() - { - if (isset($this->_packageInfo['stability'])) { - return $this->_packageInfo['stability']; - } - return false; - } - - function getState($key = 'release') - { - if (isset($this->_packageInfo['stability'][$key])) { - return $this->_packageInfo['stability'][$key]; - } - return false; - } - - function getLicense($raw = false) - { - if (isset($this->_packageInfo['license'])) { - if ($raw) { - return $this->_packageInfo['license']; - } - if (is_array($this->_packageInfo['license'])) { - return $this->_packageInfo['license']['_content']; - } else { - return $this->_packageInfo['license']; - } - } - return false; - } - - function getLicenseLocation() - { - if (!isset($this->_packageInfo['license']) || !is_array($this->_packageInfo['license'])) { - return false; - } - return $this->_packageInfo['license']['attribs']; - } - - function getNotes() - { - if (isset($this->_packageInfo['notes'])) { - return $this->_packageInfo['notes']; - } - return false; - } - - /** - * Return the tag contents, if any - * @return array|false - */ - function getUsesrole() - { - if (isset($this->_packageInfo['usesrole'])) { - return $this->_packageInfo['usesrole']; - } - return false; - } - - /** - * Return the tag contents, if any - * @return array|false - */ - function getUsestask() - { - if (isset($this->_packageInfo['usestask'])) { - return $this->_packageInfo['usestask']; - } - return false; - } - - /** - * This should only be used to retrieve filenames and install attributes - */ - function getFilelist($preserve = false) - { - if (isset($this->_packageInfo['filelist']) && !$preserve) { - return $this->_packageInfo['filelist']; - } - $this->flattenFilelist(); - if ($contents = $this->getContents()) { - $ret = array(); - if (!isset($contents['dir'])) { - return false; - } - if (!isset($contents['dir']['file'][0])) { - $contents['dir']['file'] = array($contents['dir']['file']); - } - foreach ($contents['dir']['file'] as $file) { - $name = $file['attribs']['name']; - if (!$preserve) { - $file = $file['attribs']; - } - $ret[$name] = $file; - } - if (!$preserve) { - $this->_packageInfo['filelist'] = $ret; - } - return $ret; - } - return false; - } - - /** - * Return configure options array, if any - * - * @return array|false - */ - function getConfigureOptions() - { - if ($this->getPackageType() != 'extsrc' && $this->getPackageType() != 'zendextsrc') { - return false; - } - - $releases = $this->getReleases(); - if (isset($releases[0])) { - $releases = $releases[0]; - } - - if (isset($releases['configureoption'])) { - if (!isset($releases['configureoption'][0])) { - $releases['configureoption'] = array($releases['configureoption']); - } - - for ($i = 0; $i < count($releases['configureoption']); $i++) { - $releases['configureoption'][$i] = $releases['configureoption'][$i]['attribs']; - } - - return $releases['configureoption']; - } - - return false; - } - - /** - * This is only used at install-time, after all serialization - * is over. - */ - function resetFilelist() - { - $this->_packageInfo['filelist'] = array(); - } - - /** - * Retrieve a list of files that should be installed on this computer - * @return array - */ - function getInstallationFilelist($forfilecheck = false) - { - $contents = $this->getFilelist(true); - if (isset($contents['dir']['attribs']['baseinstalldir'])) { - $base = $contents['dir']['attribs']['baseinstalldir']; - } - if (isset($this->_packageInfo['bundle'])) { - return PEAR::raiseError( - 'Exception: bundles should be handled in download code only'); - } - $release = $this->getReleases(); - if ($release) { - if (!isset($release[0])) { - if (!isset($release['installconditions']) && !isset($release['filelist'])) { - if ($forfilecheck) { - return $this->getFilelist(); - } - return $contents; - } - $release = array($release); - } - $depchecker = &$this->getPEARDependency2($this->_config, array(), - array('channel' => $this->getChannel(), 'package' => $this->getPackage()), - PEAR_VALIDATE_INSTALLING); - foreach ($release as $instance) { - if (isset($instance['installconditions'])) { - $installconditions = $instance['installconditions']; - if (is_array($installconditions)) { - PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - foreach ($installconditions as $type => $conditions) { - if (!isset($conditions[0])) { - $conditions = array($conditions); - } - foreach ($conditions as $condition) { - $ret = $depchecker->{"validate{$type}Dependency"}($condition); - if (PEAR::isError($ret)) { - PEAR::popErrorHandling(); - continue 3; // skip this release - } - } - } - PEAR::popErrorHandling(); - } - } - // this is the release to use - if (isset($instance['filelist'])) { - // ignore files - if (isset($instance['filelist']['ignore'])) { - $ignore = isset($instance['filelist']['ignore'][0]) ? - $instance['filelist']['ignore'] : - array($instance['filelist']['ignore']); - foreach ($ignore as $ig) { - unset ($contents[$ig['attribs']['name']]); - } - } - // install files as this name - if (isset($instance['filelist']['install'])) { - $installas = isset($instance['filelist']['install'][0]) ? - $instance['filelist']['install'] : - array($instance['filelist']['install']); - foreach ($installas as $as) { - $contents[$as['attribs']['name']]['attribs']['install-as'] = - $as['attribs']['as']; - } - } - } - if ($forfilecheck) { - foreach ($contents as $file => $attrs) { - $contents[$file] = $attrs['attribs']; - } - } - return $contents; - } - } else { // simple release - no installconditions or install-as - if ($forfilecheck) { - return $this->getFilelist(); - } - return $contents; - } - // no releases matched - return PEAR::raiseError('No releases in package.xml matched the existing operating ' . - 'system, extensions installed, or architecture, cannot install'); - } - - /** - * This is only used at install-time, after all serialization - * is over. - * @param string file name - * @param string installed path - */ - function setInstalledAs($file, $path) - { - if ($path) { - return $this->_packageInfo['filelist'][$file]['installed_as'] = $path; - } - unset($this->_packageInfo['filelist'][$file]['installed_as']); - } - - function getInstalledLocation($file) - { - if (isset($this->_packageInfo['filelist'][$file]['installed_as'])) { - return $this->_packageInfo['filelist'][$file]['installed_as']; - } - return false; - } - - /** - * This is only used at install-time, after all serialization - * is over. - */ - function installedFile($file, $atts) - { - if (isset($this->_packageInfo['filelist'][$file])) { - $this->_packageInfo['filelist'][$file] = - array_merge($this->_packageInfo['filelist'][$file], $atts['attribs']); - } else { - $this->_packageInfo['filelist'][$file] = $atts['attribs']; - } - } - - /** - * Retrieve the contents tag - */ - function getContents() - { - if (isset($this->_packageInfo['contents'])) { - return $this->_packageInfo['contents']; - } - return false; - } - - /** - * @param string full path to file - * @param string attribute name - * @param string attribute value - * @param int risky but fast - use this to choose a file based on its position in the list - * of files. Index is zero-based like PHP arrays. - * @return bool success of operation - */ - function setFileAttribute($filename, $attr, $value, $index = false) - { - $this->_isValid = 0; - if (in_array($attr, array('role', 'name', 'baseinstalldir'))) { - $this->_filesValid = false; - } - if ($index !== false && - isset($this->_packageInfo['contents']['dir']['file'][$index]['attribs'])) { - $this->_packageInfo['contents']['dir']['file'][$index]['attribs'][$attr] = $value; - return true; - } - if (!isset($this->_packageInfo['contents']['dir']['file'])) { - return false; - } - $files = $this->_packageInfo['contents']['dir']['file']; - if (!isset($files[0])) { - $files = array($files); - $ind = false; - } else { - $ind = true; - } - foreach ($files as $i => $file) { - if (isset($file['attribs'])) { - if ($file['attribs']['name'] == $filename) { - if ($ind) { - $this->_packageInfo['contents']['dir']['file'][$i]['attribs'][$attr] = $value; - } else { - $this->_packageInfo['contents']['dir']['file']['attribs'][$attr] = $value; - } - return true; - } - } - } - return false; - } - - function setDirtree($path) - { - if (!isset($this->_packageInfo['dirtree'])) { - $this->_packageInfo['dirtree'] = array(); - } - $this->_packageInfo['dirtree'][$path] = true; - } - - function getDirtree() - { - if (isset($this->_packageInfo['dirtree']) && count($this->_packageInfo['dirtree'])) { - return $this->_packageInfo['dirtree']; - } - return false; - } - - function resetDirtree() - { - unset($this->_packageInfo['dirtree']); - } - - /** - * Determines whether this package claims it is compatible with the version of - * the package that has a recommended version dependency - * @param PEAR_PackageFile_v2|PEAR_PackageFile_v1|PEAR_Downloader_Package - * @return boolean - */ - function isCompatible($pf) - { - if (!isset($this->_packageInfo['compatible'])) { - return false; - } - if (!isset($this->_packageInfo['channel'])) { - return false; - } - $me = $pf->getVersion(); - $compatible = $this->_packageInfo['compatible']; - if (!isset($compatible[0])) { - $compatible = array($compatible); - } - $found = false; - foreach ($compatible as $info) { - if (strtolower($info['name']) == strtolower($pf->getPackage())) { - if (strtolower($info['channel']) == strtolower($pf->getChannel())) { - $found = true; - break; - } - } - } - if (!$found) { - return false; - } - if (isset($info['exclude'])) { - if (!isset($info['exclude'][0])) { - $info['exclude'] = array($info['exclude']); - } - foreach ($info['exclude'] as $exclude) { - if (version_compare($me, $exclude, '==')) { - return false; - } - } - } - if (version_compare($me, $info['min'], '>=') && version_compare($me, $info['max'], '<=')) { - return true; - } - return false; - } - - /** - * @return array|false - */ - function getCompatible() - { - if (isset($this->_packageInfo['compatible'])) { - return $this->_packageInfo['compatible']; - } - return false; - } - - function getDependencies() - { - if (isset($this->_packageInfo['dependencies'])) { - return $this->_packageInfo['dependencies']; - } - return false; - } - - function isSubpackageOf($p) - { - return $p->isSubpackage($this); - } - - /** - * Determines whether the passed in package is a subpackage of this package. - * - * No version checking is done, only name verification. - * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 - * @return bool - */ - function isSubpackage($p) - { - $sub = array(); - if (isset($this->_packageInfo['dependencies']['required']['subpackage'])) { - $sub = $this->_packageInfo['dependencies']['required']['subpackage']; - if (!isset($sub[0])) { - $sub = array($sub); - } - } - if (isset($this->_packageInfo['dependencies']['optional']['subpackage'])) { - $sub1 = $this->_packageInfo['dependencies']['optional']['subpackage']; - if (!isset($sub1[0])) { - $sub1 = array($sub1); - } - $sub = array_merge($sub, $sub1); - } - if (isset($this->_packageInfo['dependencies']['group'])) { - $group = $this->_packageInfo['dependencies']['group']; - if (!isset($group[0])) { - $group = array($group); - } - foreach ($group as $deps) { - if (isset($deps['subpackage'])) { - $sub2 = $deps['subpackage']; - if (!isset($sub2[0])) { - $sub2 = array($sub2); - } - $sub = array_merge($sub, $sub2); - } - } - } - foreach ($sub as $dep) { - if (strtolower($dep['name']) == strtolower($p->getPackage())) { - if (isset($dep['channel'])) { - if (strtolower($dep['channel']) == strtolower($p->getChannel())) { - return true; - } - } else { - if ($dep['uri'] == $p->getURI()) { - return true; - } - } - } - } - return false; - } - - function dependsOn($package, $channel) - { - if (!($deps = $this->getDependencies())) { - return false; - } - foreach (array('package', 'subpackage') as $type) { - foreach (array('required', 'optional') as $needed) { - if (isset($deps[$needed][$type])) { - if (!isset($deps[$needed][$type][0])) { - $deps[$needed][$type] = array($deps[$needed][$type]); - } - foreach ($deps[$needed][$type] as $dep) { - $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri'; - if (strtolower($dep['name']) == strtolower($package) && - $depchannel == $channel) { - return true; - } - } - } - } - if (isset($deps['group'])) { - if (!isset($deps['group'][0])) { - $dep['group'] = array($deps['group']); - } - foreach ($deps['group'] as $group) { - if (isset($group[$type])) { - if (!is_array($group[$type])) { - $group[$type] = array($group[$type]); - } - foreach ($group[$type] as $dep) { - $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri'; - if (strtolower($dep['name']) == strtolower($package) && - $depchannel == $channel) { - return true; - } - } - } - } - } - } - return false; - } - - /** - * Get the contents of a dependency group - * @param string - * @return array|false - */ - function getDependencyGroup($name) - { - $name = strtolower($name); - if (!isset($this->_packageInfo['dependencies']['group'])) { - return false; - } - $groups = $this->_packageInfo['dependencies']['group']; - if (!isset($groups[0])) { - $groups = array($groups); - } - foreach ($groups as $group) { - if (strtolower($group['attribs']['name']) == $name) { - return $group; - } - } - return false; - } - - /** - * Retrieve a partial package.xml 1.0 representation of dependencies - * - * a very limited representation of dependencies is returned by this method. - * The tag for excluding certain versions of a dependency is - * completely ignored. In addition, dependency groups are ignored, with the - * assumption that all dependencies in dependency groups are also listed in - * the optional group that work with all dependency groups - * @param boolean return package.xml 2.0 tag - * @return array|false - */ - function getDeps($raw = false, $nopearinstaller = false) - { - if (isset($this->_packageInfo['dependencies'])) { - if ($raw) { - return $this->_packageInfo['dependencies']; - } - $ret = array(); - $map = array( - 'php' => 'php', - 'package' => 'pkg', - 'subpackage' => 'pkg', - 'extension' => 'ext', - 'os' => 'os', - 'pearinstaller' => 'pkg', - ); - foreach (array('required', 'optional') as $type) { - $optional = ($type == 'optional') ? 'yes' : 'no'; - if (!isset($this->_packageInfo['dependencies'][$type]) - || empty($this->_packageInfo['dependencies'][$type])) { - continue; - } - foreach ($this->_packageInfo['dependencies'][$type] as $dtype => $deps) { - if ($dtype == 'pearinstaller' && $nopearinstaller) { - continue; - } - if (!isset($deps[0])) { - $deps = array($deps); - } - foreach ($deps as $dep) { - if (!isset($map[$dtype])) { - // no support for arch type - continue; - } - if ($dtype == 'pearinstaller') { - $dep['name'] = 'PEAR'; - $dep['channel'] = 'pear.php.net'; - } - $s = array('type' => $map[$dtype]); - if (isset($dep['channel'])) { - $s['channel'] = $dep['channel']; - } - if (isset($dep['uri'])) { - $s['uri'] = $dep['uri']; - } - if (isset($dep['name'])) { - $s['name'] = $dep['name']; - } - if (isset($dep['conflicts'])) { - $s['rel'] = 'not'; - } else { - if (!isset($dep['min']) && - !isset($dep['max'])) { - $s['rel'] = 'has'; - $s['optional'] = $optional; - } elseif (isset($dep['min']) && - isset($dep['max'])) { - $s['rel'] = 'ge'; - $s1 = $s; - $s1['rel'] = 'le'; - $s['version'] = $dep['min']; - $s1['version'] = $dep['max']; - if (isset($dep['channel'])) { - $s1['channel'] = $dep['channel']; - } - if ($dtype != 'php') { - $s['name'] = $dep['name']; - $s1['name'] = $dep['name']; - } - $s['optional'] = $optional; - $s1['optional'] = $optional; - $ret[] = $s1; - } elseif (isset($dep['min'])) { - if (isset($dep['exclude']) && - $dep['exclude'] == $dep['min']) { - $s['rel'] = 'gt'; - } else { - $s['rel'] = 'ge'; - } - $s['version'] = $dep['min']; - $s['optional'] = $optional; - if ($dtype != 'php') { - $s['name'] = $dep['name']; - } - } elseif (isset($dep['max'])) { - if (isset($dep['exclude']) && - $dep['exclude'] == $dep['max']) { - $s['rel'] = 'lt'; - } else { - $s['rel'] = 'le'; - } - $s['version'] = $dep['max']; - $s['optional'] = $optional; - if ($dtype != 'php') { - $s['name'] = $dep['name']; - } - } - } - $ret[] = $s; - } - } - } - if (count($ret)) { - return $ret; - } - } - return false; - } - - /** - * @return php|extsrc|extbin|zendextsrc|zendextbin|bundle|false - */ - function getPackageType() - { - if (isset($this->_packageInfo['phprelease'])) { - return 'php'; - } - if (isset($this->_packageInfo['extsrcrelease'])) { - return 'extsrc'; - } - if (isset($this->_packageInfo['extbinrelease'])) { - return 'extbin'; - } - if (isset($this->_packageInfo['zendextsrcrelease'])) { - return 'zendextsrc'; - } - if (isset($this->_packageInfo['zendextbinrelease'])) { - return 'zendextbin'; - } - if (isset($this->_packageInfo['bundle'])) { - return 'bundle'; - } - return false; - } - - /** - * @return array|false - */ - function getReleases() - { - $type = $this->getPackageType(); - if ($type != 'bundle') { - $type .= 'release'; - } - if ($this->getPackageType() && isset($this->_packageInfo[$type])) { - return $this->_packageInfo[$type]; - } - return false; - } - - /** - * @return array - */ - function getChangelog() - { - if (isset($this->_packageInfo['changelog'])) { - return $this->_packageInfo['changelog']; - } - return false; - } - - function hasDeps() - { - return isset($this->_packageInfo['dependencies']); - } - - function getPackagexmlVersion() - { - if (isset($this->_packageInfo['zendextsrcrelease'])) { - return '2.1'; - } - if (isset($this->_packageInfo['zendextbinrelease'])) { - return '2.1'; - } - return '2.0'; - } - - /** - * @return array|false - */ - function getSourcePackage() - { - if (isset($this->_packageInfo['extbinrelease']) || - isset($this->_packageInfo['zendextbinrelease'])) { - return array('channel' => $this->_packageInfo['srcchannel'], - 'package' => $this->_packageInfo['srcpackage']); - } - return false; - } - - function getBundledPackages() - { - if (isset($this->_packageInfo['bundle'])) { - return $this->_packageInfo['contents']['bundledpackage']; - } - return false; - } - - function getLastModified() - { - if (isset($this->_packageInfo['_lastmodified'])) { - return $this->_packageInfo['_lastmodified']; - } - return false; - } - - /** - * Get the contents of a file listed within the package.xml - * @param string - * @return string - */ - function getFileContents($file) - { - if ($this->_archiveFile == $this->_packageFile) { // unpacked - $dir = dirname($this->_packageFile); - $file = $dir . DIRECTORY_SEPARATOR . $file; - $file = str_replace(array('/', '\\'), - array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR), $file); - if (file_exists($file) && is_readable($file)) { - return implode('', file($file)); - } - } else { // tgz - $tar = &new Archive_Tar($this->_archiveFile); - $tar->pushErrorHandling(PEAR_ERROR_RETURN); - if ($file != 'package.xml' && $file != 'package2.xml') { - $file = $this->getPackage() . '-' . $this->getVersion() . '/' . $file; - } - $file = $tar->extractInString($file); - $tar->popErrorHandling(); - if (PEAR::isError($file)) { - return PEAR::raiseError("Cannot locate file '$file' in archive"); - } - return $file; - } - } - - function &getRW() - { - if (!class_exists('PEAR_PackageFile_v2_rw')) { - require_once 'PEAR/PackageFile/v2/rw.php'; - } - $a = new PEAR_PackageFile_v2_rw; - foreach (get_object_vars($this) as $name => $unused) { - if (!isset($this->$name)) { - continue; - } - if ($name == '_config' || $name == '_logger'|| $name == '_registry' || - $name == '_stack') { - $a->$name = &$this->$name; - } else { - $a->$name = $this->$name; - } - } - return $a; - } - - function &getDefaultGenerator() - { - if (!class_exists('PEAR_PackageFile_Generator_v2')) { - require_once 'PEAR/PackageFile/Generator/v2.php'; - } - $a = &new PEAR_PackageFile_Generator_v2($this); - return $a; - } - - function analyzeSourceCode($file, $string = false) - { - if (!isset($this->_v2Validator) || - !is_a($this->_v2Validator, 'PEAR_PackageFile_v2_Validator')) { - if (!class_exists('PEAR_PackageFile_v2_Validator')) { - require_once 'PEAR/PackageFile/v2/Validator.php'; - } - $this->_v2Validator = new PEAR_PackageFile_v2_Validator; - } - return $this->_v2Validator->analyzeSourceCode($file, $string); - } - - function validate($state = PEAR_VALIDATE_NORMAL) - { - if (!isset($this->_packageInfo) || !is_array($this->_packageInfo)) { - return false; - } - if (!isset($this->_v2Validator) || - !is_a($this->_v2Validator, 'PEAR_PackageFile_v2_Validator')) { - if (!class_exists('PEAR_PackageFile_v2_Validator')) { - require_once 'PEAR/PackageFile/v2/Validator.php'; - } - $this->_v2Validator = new PEAR_PackageFile_v2_Validator; - } - if (isset($this->_packageInfo['xsdversion'])) { - unset($this->_packageInfo['xsdversion']); - } - return $this->_v2Validator->validate($this, $state); - } - - function getTasksNs() - { - if (!isset($this->_tasksNs)) { - if (isset($this->_packageInfo['attribs'])) { - foreach ($this->_packageInfo['attribs'] as $name => $value) { - if ($value == 'http://pear.php.net/dtd/tasks-1.0') { - $this->_tasksNs = str_replace('xmlns:', '', $name); - break; - } - } - } - } - return $this->_tasksNs; - } - - /** - * Determine whether a task name is a valid task. Custom tasks may be defined - * using subdirectories by putting a "-" in the name, as in - * - * Note that this method will auto-load the task class file and test for the existence - * of the name with "-" replaced by "_" as in PEAR/Task/mycustom/task.php makes class - * PEAR_Task_mycustom_task - * @param string - * @return boolean - */ - function getTask($task) - { - $this->getTasksNs(); - // transform all '-' to '/' and 'tasks:' to '' so tasks:replace becomes replace - $task = str_replace(array($this->_tasksNs . ':', '-'), array('', ' '), $task); - $taskfile = str_replace(' ', '/', ucwords($task)); - $task = str_replace(array(' ', '/'), '_', ucwords($task)); - if (class_exists("PEAR_Task_$task")) { - return "PEAR_Task_$task"; - } - $fp = @fopen("PEAR/Task/$taskfile.php", 'r', true); - if ($fp) { - fclose($fp); - require_once "PEAR/Task/$taskfile.php"; - return "PEAR_Task_$task"; - } - return false; - } - - /** - * Key-friendly array_splice - * @param tagname to splice a value in before - * @param mixed the value to splice in - * @param string the new tag name - */ - function _ksplice($array, $key, $value, $newkey) - { - $offset = array_search($key, array_keys($array)); - $after = array_slice($array, $offset); - $before = array_slice($array, 0, $offset); - $before[$newkey] = $value; - return array_merge($before, $after); - } - - /** - * @param array a list of possible keys, in the order they may occur - * @param mixed contents of the new package.xml tag - * @param string tag name - * @access private - */ - function _insertBefore($array, $keys, $contents, $newkey) - { - foreach ($keys as $key) { - if (isset($array[$key])) { - return $array = $this->_ksplice($array, $key, $contents, $newkey); - } - } - $array[$newkey] = $contents; - return $array; - } - - /** - * @param subsection of {@link $_packageInfo} - * @param array|string tag contents - * @param array format: - *
    -     * array(
    -     *   tagname => array(list of tag names that follow this one),
    -     *   childtagname => array(list of child tag names that follow this one),
    -     * )
    -     * 
    - * - * This allows construction of nested tags - * @access private - */ - function _mergeTag($manip, $contents, $order) - { - if (count($order)) { - foreach ($order as $tag => $curorder) { - if (!isset($manip[$tag])) { - // ensure that the tag is set up - $manip = $this->_insertBefore($manip, $curorder, array(), $tag); - } - if (count($order) > 1) { - $manip[$tag] = $this->_mergeTag($manip[$tag], $contents, array_slice($order, 1)); - return $manip; - } - } - } else { - return $manip; - } - if (is_array($manip[$tag]) && !empty($manip[$tag]) && isset($manip[$tag][0])) { - $manip[$tag][] = $contents; - } else { - if (!count($manip[$tag])) { - $manip[$tag] = $contents; - } else { - $manip[$tag] = array($manip[$tag]); - $manip[$tag][] = $contents; - } - } - return $manip; - } -} -?> diff --git a/pear/PEAR/PackageFile/v2/Validator.php b/pear/PEAR/PackageFile/v2/Validator.php deleted file mode 100644 index 7ce2fb3..0000000 --- a/pear/PEAR/PackageFile/v2/Validator.php +++ /dev/null @@ -1,2154 +0,0 @@ - - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a8 - */ -/** - * Private validation class used by PEAR_PackageFile_v2 - do not use directly, its - * sole purpose is to split up the PEAR/PackageFile/v2.php file to make it smaller - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a8 - * @access private - */ -class PEAR_PackageFile_v2_Validator -{ - /** - * @var array - */ - var $_packageInfo; - /** - * @var PEAR_PackageFile_v2 - */ - var $_pf; - /** - * @var PEAR_ErrorStack - */ - var $_stack; - /** - * @var int - */ - var $_isValid = 0; - /** - * @var int - */ - var $_filesValid = 0; - /** - * @var int - */ - var $_curState = 0; - /** - * @param PEAR_PackageFile_v2 - * @param int - */ - function validate(&$pf, $state = PEAR_VALIDATE_NORMAL) - { - $this->_pf = &$pf; - $this->_curState = $state; - $this->_packageInfo = $this->_pf->getArray(); - $this->_isValid = $this->_pf->_isValid; - $this->_filesValid = $this->_pf->_filesValid; - $this->_stack = &$pf->_stack; - $this->_stack->getErrors(true); - if (($this->_isValid & $state) == $state) { - return true; - } - if (!isset($this->_packageInfo) || !is_array($this->_packageInfo)) { - return false; - } - if (!isset($this->_packageInfo['attribs']['version']) || - ($this->_packageInfo['attribs']['version'] != '2.0' && - $this->_packageInfo['attribs']['version'] != '2.1') - ) { - $this->_noPackageVersion(); - } - $structure = - array( - 'name', - 'channel|uri', - '*extends', // can't be multiple, but this works fine - 'summary', - 'description', - '+lead', // these all need content checks - '*developer', - '*contributor', - '*helper', - 'date', - '*time', - 'version', - 'stability', - 'license->?uri->?filesource', - 'notes', - 'contents', //special validation needed - '*compatible', - 'dependencies', //special validation needed - '*usesrole', - '*usestask', // reserve these for 1.4.0a1 to implement - // this will allow a package.xml to gracefully say it - // needs a certain package installed in order to implement a role or task - '*providesextension', - '*srcpackage|*srcuri', - '+phprelease|+extsrcrelease|+extbinrelease|' . - '+zendextsrcrelease|+zendextbinrelease|bundle', //special validation needed - '*changelog', - ); - $test = $this->_packageInfo; - if (isset($test['dependencies']) && - isset($test['dependencies']['required']) && - isset($test['dependencies']['required']['pearinstaller']) && - isset($test['dependencies']['required']['pearinstaller']['min']) && - version_compare('1.9.4', - $test['dependencies']['required']['pearinstaller']['min'], '<') - ) { - $this->_pearVersionTooLow($test['dependencies']['required']['pearinstaller']['min']); - return false; - } - // ignore post-installation array fields - if (array_key_exists('filelist', $test)) { - unset($test['filelist']); - } - if (array_key_exists('_lastmodified', $test)) { - unset($test['_lastmodified']); - } - if (array_key_exists('#binarypackage', $test)) { - unset($test['#binarypackage']); - } - if (array_key_exists('old', $test)) { - unset($test['old']); - } - if (array_key_exists('_lastversion', $test)) { - unset($test['_lastversion']); - } - if (!$this->_stupidSchemaValidate($structure, $test, '')) { - return false; - } - if (empty($this->_packageInfo['name'])) { - $this->_tagCannotBeEmpty('name'); - } - $test = isset($this->_packageInfo['uri']) ? 'uri' :'channel'; - if (empty($this->_packageInfo[$test])) { - $this->_tagCannotBeEmpty($test); - } - if (is_array($this->_packageInfo['license']) && - (!isset($this->_packageInfo['license']['_content']) || - empty($this->_packageInfo['license']['_content']))) { - $this->_tagCannotBeEmpty('license'); - } elseif (empty($this->_packageInfo['license'])) { - $this->_tagCannotBeEmpty('license'); - } - if (empty($this->_packageInfo['summary'])) { - $this->_tagCannotBeEmpty('summary'); - } - if (empty($this->_packageInfo['description'])) { - $this->_tagCannotBeEmpty('description'); - } - if (empty($this->_packageInfo['date'])) { - $this->_tagCannotBeEmpty('date'); - } - if (empty($this->_packageInfo['notes'])) { - $this->_tagCannotBeEmpty('notes'); - } - if (isset($this->_packageInfo['time']) && empty($this->_packageInfo['time'])) { - $this->_tagCannotBeEmpty('time'); - } - if (isset($this->_packageInfo['dependencies'])) { - $this->_validateDependencies(); - } - if (isset($this->_packageInfo['compatible'])) { - $this->_validateCompatible(); - } - if (!isset($this->_packageInfo['bundle'])) { - if (empty($this->_packageInfo['contents'])) { - $this->_tagCannotBeEmpty('contents'); - } - if (!isset($this->_packageInfo['contents']['dir'])) { - $this->_filelistMustContainDir('contents'); - return false; - } - if (isset($this->_packageInfo['contents']['file'])) { - $this->_filelistCannotContainFile('contents'); - return false; - } - } - $this->_validateMaintainers(); - $this->_validateStabilityVersion(); - $fail = false; - if (array_key_exists('usesrole', $this->_packageInfo)) { - $roles = $this->_packageInfo['usesrole']; - if (!is_array($roles) || !isset($roles[0])) { - $roles = array($roles); - } - foreach ($roles as $role) { - if (!isset($role['role'])) { - $this->_usesroletaskMustHaveRoleTask('usesrole', 'role'); - $fail = true; - } else { - if (!isset($role['channel'])) { - if (!isset($role['uri'])) { - $this->_usesroletaskMustHaveChannelOrUri($role['role'], 'usesrole'); - $fail = true; - } - } elseif (!isset($role['package'])) { - $this->_usesroletaskMustHavePackage($role['role'], 'usesrole'); - $fail = true; - } - } - } - } - if (array_key_exists('usestask', $this->_packageInfo)) { - $roles = $this->_packageInfo['usestask']; - if (!is_array($roles) || !isset($roles[0])) { - $roles = array($roles); - } - foreach ($roles as $role) { - if (!isset($role['task'])) { - $this->_usesroletaskMustHaveRoleTask('usestask', 'task'); - $fail = true; - } else { - if (!isset($role['channel'])) { - if (!isset($role['uri'])) { - $this->_usesroletaskMustHaveChannelOrUri($role['task'], 'usestask'); - $fail = true; - } - } elseif (!isset($role['package'])) { - $this->_usesroletaskMustHavePackage($role['task'], 'usestask'); - $fail = true; - } - } - } - } - - if ($fail) { - return false; - } - - $list = $this->_packageInfo['contents']; - if (isset($list['dir']) && is_array($list['dir']) && isset($list['dir'][0])) { - $this->_multipleToplevelDirNotAllowed(); - return $this->_isValid = 0; - } - - $this->_validateFilelist(); - $this->_validateRelease(); - if (!$this->_stack->hasErrors()) { - $chan = $this->_pf->_registry->getChannel($this->_pf->getChannel(), true); - if (PEAR::isError($chan)) { - $this->_unknownChannel($this->_pf->getChannel()); - } else { - $valpack = $chan->getValidationPackage(); - // for channel validator packages, always use the default PEAR validator. - // otherwise, they can't be installed or packaged - $validator = $chan->getValidationObject($this->_pf->getPackage()); - if (!$validator) { - $this->_stack->push(__FUNCTION__, 'error', - array('channel' => $chan->getName(), - 'package' => $this->_pf->getPackage(), - 'name' => $valpack['_content'], - 'version' => $valpack['attribs']['version']), - 'package "%channel%/%package%" cannot be properly validated without ' . - 'validation package "%channel%/%name%-%version%"'); - return $this->_isValid = 0; - } - $validator->setPackageFile($this->_pf); - $validator->validate($state); - $failures = $validator->getFailures(); - foreach ($failures['errors'] as $error) { - $this->_stack->push(__FUNCTION__, 'error', $error, - 'Channel validator error: field "%field%" - %reason%'); - } - foreach ($failures['warnings'] as $warning) { - $this->_stack->push(__FUNCTION__, 'warning', $warning, - 'Channel validator warning: field "%field%" - %reason%'); - } - } - } - - $this->_pf->_isValid = $this->_isValid = !$this->_stack->hasErrors('error'); - if ($this->_isValid && $state == PEAR_VALIDATE_PACKAGING && !$this->_filesValid) { - if ($this->_pf->getPackageType() == 'bundle') { - if ($this->_analyzeBundledPackages()) { - $this->_filesValid = $this->_pf->_filesValid = true; - } else { - $this->_pf->_isValid = $this->_isValid = 0; - } - } else { - if (!$this->_analyzePhpFiles()) { - $this->_pf->_isValid = $this->_isValid = 0; - } else { - $this->_filesValid = $this->_pf->_filesValid = true; - } - } - } - - if ($this->_isValid) { - return $this->_pf->_isValid = $this->_isValid = $state; - } - - return $this->_pf->_isValid = $this->_isValid = 0; - } - - function _stupidSchemaValidate($structure, $xml, $root) - { - if (!is_array($xml)) { - $xml = array(); - } - $keys = array_keys($xml); - reset($keys); - $key = current($keys); - while ($key == 'attribs' || $key == '_contents') { - $key = next($keys); - } - $unfoundtags = $optionaltags = array(); - $ret = true; - $mismatch = false; - foreach ($structure as $struc) { - if ($key) { - $tag = $xml[$key]; - } - $test = $this->_processStructure($struc); - if (isset($test['choices'])) { - $loose = true; - foreach ($test['choices'] as $choice) { - if ($key == $choice['tag']) { - $key = next($keys); - while ($key == 'attribs' || $key == '_contents') { - $key = next($keys); - } - $unfoundtags = $optionaltags = array(); - $mismatch = false; - if ($key && $key != $choice['tag'] && isset($choice['multiple'])) { - $unfoundtags[] = $choice['tag']; - $optionaltags[] = $choice['tag']; - if ($key) { - $mismatch = true; - } - } - $ret &= $this->_processAttribs($choice, $tag, $root); - continue 2; - } else { - $unfoundtags[] = $choice['tag']; - $mismatch = true; - } - if (!isset($choice['multiple']) || $choice['multiple'] != '*') { - $loose = false; - } else { - $optionaltags[] = $choice['tag']; - } - } - if (!$loose) { - $this->_invalidTagOrder($unfoundtags, $key, $root); - return false; - } - } else { - if ($key != $test['tag']) { - if (isset($test['multiple']) && $test['multiple'] != '*') { - $unfoundtags[] = $test['tag']; - $this->_invalidTagOrder($unfoundtags, $key, $root); - return false; - } else { - if ($key) { - $mismatch = true; - } - $unfoundtags[] = $test['tag']; - $optionaltags[] = $test['tag']; - } - if (!isset($test['multiple'])) { - $this->_invalidTagOrder($unfoundtags, $key, $root); - return false; - } - continue; - } else { - $unfoundtags = $optionaltags = array(); - $mismatch = false; - } - $key = next($keys); - while ($key == 'attribs' || $key == '_contents') { - $key = next($keys); - } - if ($key && $key != $test['tag'] && isset($test['multiple'])) { - $unfoundtags[] = $test['tag']; - $optionaltags[] = $test['tag']; - $mismatch = true; - } - $ret &= $this->_processAttribs($test, $tag, $root); - continue; - } - } - if (!$mismatch && count($optionaltags)) { - // don't error out on any optional tags - $unfoundtags = array_diff($unfoundtags, $optionaltags); - } - if (count($unfoundtags)) { - $this->_invalidTagOrder($unfoundtags, $key, $root); - } elseif ($key) { - // unknown tags - $this->_invalidTagOrder('*no tags allowed here*', $key, $root); - while ($key = next($keys)) { - $this->_invalidTagOrder('*no tags allowed here*', $key, $root); - } - } - return $ret; - } - - function _processAttribs($choice, $tag, $context) - { - if (isset($choice['attribs'])) { - if (!is_array($tag)) { - $tag = array($tag); - } - $tags = $tag; - if (!isset($tags[0])) { - $tags = array($tags); - } - $ret = true; - foreach ($tags as $i => $tag) { - if (!is_array($tag) || !isset($tag['attribs'])) { - foreach ($choice['attribs'] as $attrib) { - if ($attrib{0} != '?') { - $ret &= $this->_tagHasNoAttribs($choice['tag'], - $context); - continue 2; - } - } - } - foreach ($choice['attribs'] as $attrib) { - if ($attrib{0} != '?') { - if (!isset($tag['attribs'][$attrib])) { - $ret &= $this->_tagMissingAttribute($choice['tag'], - $attrib, $context); - } - } - } - } - return $ret; - } - return true; - } - - function _processStructure($key) - { - $ret = array(); - if (count($pieces = explode('|', $key)) > 1) { - $ret['choices'] = array(); - foreach ($pieces as $piece) { - $ret['choices'][] = $this->_processStructure($piece); - } - return $ret; - } - $multi = $key{0}; - if ($multi == '+' || $multi == '*') { - $ret['multiple'] = $key{0}; - $key = substr($key, 1); - } - if (count($attrs = explode('->', $key)) > 1) { - $ret['tag'] = array_shift($attrs); - $ret['attribs'] = $attrs; - } else { - $ret['tag'] = $key; - } - return $ret; - } - - function _validateStabilityVersion() - { - $structure = array('release', 'api'); - $a = $this->_stupidSchemaValidate($structure, $this->_packageInfo['version'], ''); - $a &= $this->_stupidSchemaValidate($structure, $this->_packageInfo['stability'], ''); - if ($a) { - if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/', - $this->_packageInfo['version']['release'])) { - $this->_invalidVersion('release', $this->_packageInfo['version']['release']); - } - if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/', - $this->_packageInfo['version']['api'])) { - $this->_invalidVersion('api', $this->_packageInfo['version']['api']); - } - if (!in_array($this->_packageInfo['stability']['release'], - array('snapshot', 'devel', 'alpha', 'beta', 'stable'))) { - $this->_invalidState('release', $this->_packageInfo['stability']['release']); - } - if (!in_array($this->_packageInfo['stability']['api'], - array('devel', 'alpha', 'beta', 'stable'))) { - $this->_invalidState('api', $this->_packageInfo['stability']['api']); - } - } - } - - function _validateMaintainers() - { - $structure = - array( - 'name', - 'user', - 'email', - 'active', - ); - foreach (array('lead', 'developer', 'contributor', 'helper') as $type) { - if (!isset($this->_packageInfo[$type])) { - continue; - } - if (isset($this->_packageInfo[$type][0])) { - foreach ($this->_packageInfo[$type] as $lead) { - $this->_stupidSchemaValidate($structure, $lead, '<' . $type . '>'); - } - } else { - $this->_stupidSchemaValidate($structure, $this->_packageInfo[$type], - '<' . $type . '>'); - } - } - } - - function _validatePhpDep($dep, $installcondition = false) - { - $structure = array( - 'min', - '*max', - '*exclude', - ); - $type = $installcondition ? '' : ''; - $this->_stupidSchemaValidate($structure, $dep, $type); - if (isset($dep['min'])) { - if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/', - $dep['min'])) { - $this->_invalidVersion($type . '', $dep['min']); - } - } - if (isset($dep['max'])) { - if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/', - $dep['max'])) { - $this->_invalidVersion($type . '', $dep['max']); - } - } - if (isset($dep['exclude'])) { - if (!is_array($dep['exclude'])) { - $dep['exclude'] = array($dep['exclude']); - } - foreach ($dep['exclude'] as $exclude) { - if (!preg_match( - '/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/', - $exclude)) { - $this->_invalidVersion($type . '', $exclude); - } - } - } - } - - function _validatePearinstallerDep($dep) - { - $structure = array( - 'min', - '*max', - '*recommended', - '*exclude', - ); - $this->_stupidSchemaValidate($structure, $dep, ''); - if (isset($dep['min'])) { - if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/', - $dep['min'])) { - $this->_invalidVersion('', - $dep['min']); - } - } - if (isset($dep['max'])) { - if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/', - $dep['max'])) { - $this->_invalidVersion('', - $dep['max']); - } - } - if (isset($dep['recommended'])) { - if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/', - $dep['recommended'])) { - $this->_invalidVersion('', - $dep['recommended']); - } - } - if (isset($dep['exclude'])) { - if (!is_array($dep['exclude'])) { - $dep['exclude'] = array($dep['exclude']); - } - foreach ($dep['exclude'] as $exclude) { - if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/', - $exclude)) { - $this->_invalidVersion('', - $exclude); - } - } - } - } - - function _validatePackageDep($dep, $group, $type = '') - { - if (isset($dep['uri'])) { - if (isset($dep['conflicts'])) { - $structure = array( - 'name', - 'uri', - 'conflicts', - '*providesextension', - ); - } else { - $structure = array( - 'name', - 'uri', - '*providesextension', - ); - } - } else { - if (isset($dep['conflicts'])) { - $structure = array( - 'name', - 'channel', - '*min', - '*max', - '*exclude', - 'conflicts', - '*providesextension', - ); - } else { - $structure = array( - 'name', - 'channel', - '*min', - '*max', - '*recommended', - '*exclude', - '*nodefault', - '*providesextension', - ); - } - } - if (isset($dep['name'])) { - $type .= '' . $dep['name'] . ''; - } - $this->_stupidSchemaValidate($structure, $dep, '' . $group . $type); - if (isset($dep['uri']) && (isset($dep['min']) || isset($dep['max']) || - isset($dep['recommended']) || isset($dep['exclude']))) { - $this->_uriDepsCannotHaveVersioning('' . $group . $type); - } - if (isset($dep['channel']) && strtolower($dep['channel']) == '__uri') { - $this->_DepchannelCannotBeUri('' . $group . $type); - } - if (isset($dep['min'])) { - if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/', - $dep['min'])) { - $this->_invalidVersion('' . $group . $type . '', $dep['min']); - } - } - if (isset($dep['max'])) { - if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/', - $dep['max'])) { - $this->_invalidVersion('' . $group . $type . '', $dep['max']); - } - } - if (isset($dep['recommended'])) { - if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/', - $dep['recommended'])) { - $this->_invalidVersion('' . $group . $type . '', - $dep['recommended']); - } - } - if (isset($dep['exclude'])) { - if (!is_array($dep['exclude'])) { - $dep['exclude'] = array($dep['exclude']); - } - foreach ($dep['exclude'] as $exclude) { - if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/', - $exclude)) { - $this->_invalidVersion('' . $group . $type . '', - $exclude); - } - } - } - } - - function _validateSubpackageDep($dep, $group) - { - $this->_validatePackageDep($dep, $group, ''); - if (isset($dep['providesextension'])) { - $this->_subpackageCannotProvideExtension(isset($dep['name']) ? $dep['name'] : ''); - } - if (isset($dep['conflicts'])) { - $this->_subpackagesCannotConflict(isset($dep['name']) ? $dep['name'] : ''); - } - } - - function _validateExtensionDep($dep, $group = false, $installcondition = false) - { - if (isset($dep['conflicts'])) { - $structure = array( - 'name', - '*min', - '*max', - '*exclude', - 'conflicts', - ); - } else { - $structure = array( - 'name', - '*min', - '*max', - '*recommended', - '*exclude', - ); - } - if ($installcondition) { - $type = ''; - } else { - $type = '' . $group . ''; - } - if (isset($dep['name'])) { - $type .= '' . $dep['name'] . ''; - } - $this->_stupidSchemaValidate($structure, $dep, $type); - if (isset($dep['min'])) { - if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/', - $dep['min'])) { - $this->_invalidVersion(substr($type, 1) . '_invalidVersion(substr($type, 1) . '_invalidVersion(substr($type, 1) . '_invalidVersion(substr($type, 1) . '' : ''; - if ($this->_stupidSchemaValidate($structure, $dep, $type)) { - if ($dep['name'] == '*') { - if (array_key_exists('conflicts', $dep)) { - $this->_cannotConflictWithAllOs($type); - } - } - } - } - - function _validateArchDep($dep, $installcondition = false) - { - $structure = array( - 'pattern', - '*conflicts', - ); - $type = $installcondition ? '' : ''; - $this->_stupidSchemaValidate($structure, $dep, $type); - } - - function _validateInstallConditions($cond, $release) - { - $structure = array( - '*php', - '*extension', - '*os', - '*arch', - ); - if (!$this->_stupidSchemaValidate($structure, - $cond, $release)) { - return false; - } - foreach (array('php', 'extension', 'os', 'arch') as $type) { - if (isset($cond[$type])) { - $iter = $cond[$type]; - if (!is_array($iter) || !isset($iter[0])) { - $iter = array($iter); - } - foreach ($iter as $package) { - if ($type == 'extension') { - $this->{"_validate{$type}Dep"}($package, false, true); - } else { - $this->{"_validate{$type}Dep"}($package, true); - } - } - } - } - } - - function _validateDependencies() - { - $structure = array( - 'required', - '*optional', - '*group->name->hint' - ); - if (!$this->_stupidSchemaValidate($structure, - $this->_packageInfo['dependencies'], '')) { - return false; - } - foreach (array('required', 'optional') as $simpledep) { - if (isset($this->_packageInfo['dependencies'][$simpledep])) { - if ($simpledep == 'optional') { - $structure = array( - '*package', - '*subpackage', - '*extension', - ); - } else { - $structure = array( - 'php', - 'pearinstaller', - '*package', - '*subpackage', - '*extension', - '*os', - '*arch', - ); - } - if ($this->_stupidSchemaValidate($structure, - $this->_packageInfo['dependencies'][$simpledep], - "<$simpledep>")) { - foreach (array('package', 'subpackage', 'extension') as $type) { - if (isset($this->_packageInfo['dependencies'][$simpledep][$type])) { - $iter = $this->_packageInfo['dependencies'][$simpledep][$type]; - if (!isset($iter[0])) { - $iter = array($iter); - } - foreach ($iter as $package) { - if ($type != 'extension') { - if (isset($package['uri'])) { - if (isset($package['channel'])) { - $this->_UrlOrChannel($type, - $package['name']); - } - } else { - if (!isset($package['channel'])) { - $this->_NoChannel($type, $package['name']); - } - } - } - $this->{"_validate{$type}Dep"}($package, "<$simpledep>"); - } - } - } - if ($simpledep == 'optional') { - continue; - } - foreach (array('php', 'pearinstaller', 'os', 'arch') as $type) { - if (isset($this->_packageInfo['dependencies'][$simpledep][$type])) { - $iter = $this->_packageInfo['dependencies'][$simpledep][$type]; - if (!isset($iter[0])) { - $iter = array($iter); - } - foreach ($iter as $package) { - $this->{"_validate{$type}Dep"}($package); - } - } - } - } - } - } - if (isset($this->_packageInfo['dependencies']['group'])) { - $groups = $this->_packageInfo['dependencies']['group']; - if (!isset($groups[0])) { - $groups = array($groups); - } - $structure = array( - '*package', - '*subpackage', - '*extension', - ); - foreach ($groups as $group) { - if ($this->_stupidSchemaValidate($structure, $group, '')) { - if (!PEAR_Validate::validGroupName($group['attribs']['name'])) { - $this->_invalidDepGroupName($group['attribs']['name']); - } - foreach (array('package', 'subpackage', 'extension') as $type) { - if (isset($group[$type])) { - $iter = $group[$type]; - if (!isset($iter[0])) { - $iter = array($iter); - } - foreach ($iter as $package) { - if ($type != 'extension') { - if (isset($package['uri'])) { - if (isset($package['channel'])) { - $this->_UrlOrChannelGroup($type, - $package['name'], - $group['name']); - } - } else { - if (!isset($package['channel'])) { - $this->_NoChannelGroup($type, - $package['name'], - $group['name']); - } - } - } - $this->{"_validate{$type}Dep"}($package, ''); - } - } - } - } - } - } - } - - function _validateCompatible() - { - $compat = $this->_packageInfo['compatible']; - if (!isset($compat[0])) { - $compat = array($compat); - } - $required = array('name', 'channel', 'min', 'max', '*exclude'); - foreach ($compat as $package) { - $type = ''; - if (is_array($package) && array_key_exists('name', $package)) { - $type .= '' . $package['name'] . ''; - } - $this->_stupidSchemaValidate($required, $package, $type); - if (is_array($package) && array_key_exists('min', $package)) { - if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/', - $package['min'])) { - $this->_invalidVersion(substr($type, 1) . '_invalidVersion(substr($type, 1) . '_invalidVersion(substr($type, 1) . '_NoBundledPackages(); - } - if (!is_array($list['bundledpackage']) || !isset($list['bundledpackage'][0])) { - return $this->_AtLeast2BundledPackages(); - } - foreach ($list['bundledpackage'] as $package) { - if (!is_string($package)) { - $this->_bundledPackagesMustBeFilename(); - } - } - } - - function _validateFilelist($list = false, $allowignore = false, $dirs = '') - { - $iscontents = false; - if (!$list) { - $iscontents = true; - $list = $this->_packageInfo['contents']; - if (isset($this->_packageInfo['bundle'])) { - return $this->_validateBundle($list); - } - } - if ($allowignore) { - $struc = array( - '*install->name->as', - '*ignore->name' - ); - } else { - $struc = array( - '*dir->name->?baseinstalldir', - '*file->name->role->?baseinstalldir->?md5sum' - ); - if (isset($list['dir']) && isset($list['file'])) { - // stave off validation errors without requiring a set order. - $_old = $list; - if (isset($list['attribs'])) { - $list = array('attribs' => $_old['attribs']); - } - $list['dir'] = $_old['dir']; - $list['file'] = $_old['file']; - } - } - if (!isset($list['attribs']) || !isset($list['attribs']['name'])) { - $unknown = $allowignore ? '' : '
    '; - $dirname = $iscontents ? '' : $unknown; - } else { - $dirname = ''; - if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~', - str_replace('\\', '/', $list['attribs']['name']))) { - // file contains .. parent directory or . cur directory - $this->_invalidDirName($list['attribs']['name']); - } - } - $res = $this->_stupidSchemaValidate($struc, $list, $dirname); - if ($allowignore && $res) { - $ignored_or_installed = array(); - $this->_pf->getFilelist(); - $fcontents = $this->_pf->getContents(); - $filelist = array(); - if (!isset($fcontents['dir']['file'][0])) { - $fcontents['dir']['file'] = array($fcontents['dir']['file']); - } - foreach ($fcontents['dir']['file'] as $file) { - $filelist[$file['attribs']['name']] = true; - } - if (isset($list['install'])) { - if (!isset($list['install'][0])) { - $list['install'] = array($list['install']); - } - foreach ($list['install'] as $file) { - if (!isset($filelist[$file['attribs']['name']])) { - $this->_notInContents($file['attribs']['name'], 'install'); - continue; - } - if (array_key_exists($file['attribs']['name'], $ignored_or_installed)) { - $this->_multipleInstallAs($file['attribs']['name']); - } - if (!isset($ignored_or_installed[$file['attribs']['name']])) { - $ignored_or_installed[$file['attribs']['name']] = array(); - } - $ignored_or_installed[$file['attribs']['name']][] = 1; - if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~', - str_replace('\\', '/', $file['attribs']['as']))) { - // file contains .. parent directory or . cur directory references - $this->_invalidFileInstallAs($file['attribs']['name'], - $file['attribs']['as']); - } - } - } - if (isset($list['ignore'])) { - if (!isset($list['ignore'][0])) { - $list['ignore'] = array($list['ignore']); - } - foreach ($list['ignore'] as $file) { - if (!isset($filelist[$file['attribs']['name']])) { - $this->_notInContents($file['attribs']['name'], 'ignore'); - continue; - } - if (array_key_exists($file['attribs']['name'], $ignored_or_installed)) { - $this->_ignoreAndInstallAs($file['attribs']['name']); - } - } - } - } - if (!$allowignore && isset($list['file'])) { - if (is_string($list['file'])) { - $this->_oldStyleFileNotAllowed(); - return false; - } - if (!isset($list['file'][0])) { - // single file - $list['file'] = array($list['file']); - } - foreach ($list['file'] as $i => $file) - { - if (isset($file['attribs']) && isset($file['attribs']['name'])) { - if ($file['attribs']['name']{0} == '.' && - $file['attribs']['name']{1} == '/') { - // name is something like "./doc/whatever.txt" - $this->_invalidFileName($file['attribs']['name'], $dirname); - } - if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~', - str_replace('\\', '/', $file['attribs']['name']))) { - // file contains .. parent directory or . cur directory - $this->_invalidFileName($file['attribs']['name'], $dirname); - } - } - if (isset($file['attribs']) && isset($file['attribs']['role'])) { - if (!$this->_validateRole($file['attribs']['role'])) { - if (isset($this->_packageInfo['usesrole'])) { - $roles = $this->_packageInfo['usesrole']; - if (!isset($roles[0])) { - $roles = array($roles); - } - foreach ($roles as $role) { - if ($role['role'] = $file['attribs']['role']) { - $msg = 'This package contains role "%role%" and requires ' . - 'package "%package%" to be used'; - if (isset($role['uri'])) { - $params = array('role' => $role['role'], - 'package' => $role['uri']); - } else { - $params = array('role' => $role['role'], - 'package' => $this->_pf->_registry-> - parsedPackageNameToString(array('package' => - $role['package'], 'channel' => $role['channel']), - true)); - } - $this->_stack->push('_mustInstallRole', 'error', $params, $msg); - } - } - } - $this->_invalidFileRole($file['attribs']['name'], - $dirname, $file['attribs']['role']); - } - } - if (!isset($file['attribs'])) { - continue; - } - $save = $file['attribs']; - if ($dirs) { - $save['name'] = $dirs . '/' . $save['name']; - } - unset($file['attribs']); - if (count($file) && $this->_curState != PEAR_VALIDATE_DOWNLOADING) { // has tasks - foreach ($file as $task => $value) { - if ($tagClass = $this->_pf->getTask($task)) { - if (!is_array($value) || !isset($value[0])) { - $value = array($value); - } - foreach ($value as $v) { - $ret = call_user_func(array($tagClass, 'validateXml'), - $this->_pf, $v, $this->_pf->_config, $save); - if (is_array($ret)) { - $this->_invalidTask($task, $ret, isset($save['name']) ? - $save['name'] : ''); - } - } - } else { - if (isset($this->_packageInfo['usestask'])) { - $roles = $this->_packageInfo['usestask']; - if (!isset($roles[0])) { - $roles = array($roles); - } - foreach ($roles as $role) { - if ($role['task'] = $task) { - $msg = 'This package contains task "%task%" and requires ' . - 'package "%package%" to be used'; - if (isset($role['uri'])) { - $params = array('task' => $role['task'], - 'package' => $role['uri']); - } else { - $params = array('task' => $role['task'], - 'package' => $this->_pf->_registry-> - parsedPackageNameToString(array('package' => - $role['package'], 'channel' => $role['channel']), - true)); - } - $this->_stack->push('_mustInstallTask', 'error', - $params, $msg); - } - } - } - $this->_unknownTask($task, $save['name']); - } - } - } - } - } - if (isset($list['ignore'])) { - if (!$allowignore) { - $this->_ignoreNotAllowed('ignore'); - } - } - if (isset($list['install'])) { - if (!$allowignore) { - $this->_ignoreNotAllowed('install'); - } - } - if (isset($list['file'])) { - if ($allowignore) { - $this->_fileNotAllowed('file'); - } - } - if (isset($list['dir'])) { - if ($allowignore) { - $this->_fileNotAllowed('dir'); - } else { - if (!isset($list['dir'][0])) { - $list['dir'] = array($list['dir']); - } - foreach ($list['dir'] as $dir) { - if (isset($dir['attribs']) && isset($dir['attribs']['name'])) { - if ($dir['attribs']['name'] == '/' || - !isset($this->_packageInfo['contents']['dir']['dir'])) { - // always use nothing if the filelist has already been flattened - $newdirs = ''; - } elseif ($dirs == '') { - $newdirs = $dir['attribs']['name']; - } else { - $newdirs = $dirs . '/' . $dir['attribs']['name']; - } - } else { - $newdirs = $dirs; - } - $this->_validateFilelist($dir, $allowignore, $newdirs); - } - } - } - } - - function _validateRelease() - { - if (isset($this->_packageInfo['phprelease'])) { - $release = 'phprelease'; - if (isset($this->_packageInfo['providesextension'])) { - $this->_cannotProvideExtension($release); - } - if (isset($this->_packageInfo['srcpackage']) || isset($this->_packageInfo['srcuri'])) { - $this->_cannotHaveSrcpackage($release); - } - $releases = $this->_packageInfo['phprelease']; - if (!is_array($releases)) { - return true; - } - if (!isset($releases[0])) { - $releases = array($releases); - } - foreach ($releases as $rel) { - $this->_stupidSchemaValidate(array( - '*installconditions', - '*filelist', - ), $rel, ''); - } - } - foreach (array('', 'zend') as $prefix) { - $releasetype = $prefix . 'extsrcrelease'; - if (isset($this->_packageInfo[$releasetype])) { - $release = $releasetype; - if (!isset($this->_packageInfo['providesextension'])) { - $this->_mustProvideExtension($release); - } - if (isset($this->_packageInfo['srcpackage']) || isset($this->_packageInfo['srcuri'])) { - $this->_cannotHaveSrcpackage($release); - } - $releases = $this->_packageInfo[$releasetype]; - if (!is_array($releases)) { - return true; - } - if (!isset($releases[0])) { - $releases = array($releases); - } - foreach ($releases as $rel) { - $this->_stupidSchemaValidate(array( - '*installconditions', - '*configureoption->name->prompt->?default', - '*binarypackage', - '*filelist', - ), $rel, '<' . $releasetype . '>'); - if (isset($rel['binarypackage'])) { - if (!is_array($rel['binarypackage']) || !isset($rel['binarypackage'][0])) { - $rel['binarypackage'] = array($rel['binarypackage']); - } - foreach ($rel['binarypackage'] as $bin) { - if (!is_string($bin)) { - $this->_binaryPackageMustBePackagename(); - } - } - } - } - } - $releasetype = 'extbinrelease'; - if (isset($this->_packageInfo[$releasetype])) { - $release = $releasetype; - if (!isset($this->_packageInfo['providesextension'])) { - $this->_mustProvideExtension($release); - } - if (isset($this->_packageInfo['channel']) && - !isset($this->_packageInfo['srcpackage'])) { - $this->_mustSrcPackage($release); - } - if (isset($this->_packageInfo['uri']) && !isset($this->_packageInfo['srcuri'])) { - $this->_mustSrcuri($release); - } - $releases = $this->_packageInfo[$releasetype]; - if (!is_array($releases)) { - return true; - } - if (!isset($releases[0])) { - $releases = array($releases); - } - foreach ($releases as $rel) { - $this->_stupidSchemaValidate(array( - '*installconditions', - '*filelist', - ), $rel, '<' . $releasetype . '>'); - } - } - } - if (isset($this->_packageInfo['bundle'])) { - $release = 'bundle'; - if (isset($this->_packageInfo['providesextension'])) { - $this->_cannotProvideExtension($release); - } - if (isset($this->_packageInfo['srcpackage']) || isset($this->_packageInfo['srcuri'])) { - $this->_cannotHaveSrcpackage($release); - } - $releases = $this->_packageInfo['bundle']; - if (!is_array($releases) || !isset($releases[0])) { - $releases = array($releases); - } - foreach ($releases as $rel) { - $this->_stupidSchemaValidate(array( - '*installconditions', - '*filelist', - ), $rel, ''); - } - } - foreach ($releases as $rel) { - if (is_array($rel) && array_key_exists('installconditions', $rel)) { - $this->_validateInstallConditions($rel['installconditions'], - "<$release>"); - } - if (is_array($rel) && array_key_exists('filelist', $rel)) { - if ($rel['filelist']) { - - $this->_validateFilelist($rel['filelist'], true); - } - } - } - } - - /** - * This is here to allow role extension through plugins - * @param string - */ - function _validateRole($role) - { - return in_array($role, PEAR_Installer_Role::getValidRoles($this->_pf->getPackageType())); - } - - function _pearVersionTooLow($version) - { - $this->_stack->push(__FUNCTION__, 'error', - array('version' => $version), - 'This package.xml requires PEAR version %version% to parse properly, we are ' . - 'version 1.9.4'); - } - - function _invalidTagOrder($oktags, $actual, $root) - { - $this->_stack->push(__FUNCTION__, 'error', - array('oktags' => $oktags, 'actual' => $actual, 'root' => $root), - 'Invalid tag order in %root%, found <%actual%> expected one of "%oktags%"'); - } - - function _ignoreNotAllowed($type) - { - $this->_stack->push(__FUNCTION__, 'error', array('type' => $type), - '<%type%> is not allowed inside global , only inside ' . - '//, use and only'); - } - - function _fileNotAllowed($type) - { - $this->_stack->push(__FUNCTION__, 'error', array('type' => $type), - '<%type%> is not allowed inside release , only inside ' . - ', use and only'); - } - - function _oldStyleFileNotAllowed() - { - $this->_stack->push(__FUNCTION__, 'error', array(), - 'Old-style name is not allowed. Use' . - ''); - } - - function _tagMissingAttribute($tag, $attr, $context) - { - $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag, - 'attribute' => $attr, 'context' => $context), - 'tag <%tag%> in context "%context%" has no attribute "%attribute%"'); - } - - function _tagHasNoAttribs($tag, $context) - { - $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag, - 'context' => $context), - 'tag <%tag%> has no attributes in context "%context%"'); - } - - function _invalidInternalStructure() - { - $this->_stack->push(__FUNCTION__, 'exception', array(), - 'internal array was not generated by compatible parser, or extreme parser error, cannot continue'); - } - - function _invalidFileRole($file, $dir, $role) - { - $this->_stack->push(__FUNCTION__, 'error', array( - 'file' => $file, 'dir' => $dir, 'role' => $role, - 'roles' => PEAR_Installer_Role::getValidRoles($this->_pf->getPackageType())), - 'File "%file%" in directory "%dir%" has invalid role "%role%", should be one of %roles%'); - } - - function _invalidFileName($file, $dir) - { - $this->_stack->push(__FUNCTION__, 'error', array( - 'file' => $file), - 'File "%file%" in directory "%dir%" cannot begin with "./" or contain ".."'); - } - - function _invalidFileInstallAs($file, $as) - { - $this->_stack->push(__FUNCTION__, 'error', array( - 'file' => $file, 'as' => $as), - 'File "%file%" cannot contain "./" or contain ".."'); - } - - function _invalidDirName($dir) - { - $this->_stack->push(__FUNCTION__, 'error', array( - 'dir' => $file), - 'Directory "%dir%" cannot begin with "./" or contain ".."'); - } - - function _filelistCannotContainFile($filelist) - { - $this->_stack->push(__FUNCTION__, 'error', array('tag' => $filelist), - '<%tag%> can only contain , contains . Use ' . - ' as the first dir element'); - } - - function _filelistMustContainDir($filelist) - { - $this->_stack->push(__FUNCTION__, 'error', array('tag' => $filelist), - '<%tag%> must contain . Use as the ' . - 'first dir element'); - } - - function _tagCannotBeEmpty($tag) - { - $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag), - '<%tag%> cannot be empty (<%tag%/>)'); - } - - function _UrlOrChannel($type, $name) - { - $this->_stack->push(__FUNCTION__, 'error', array('type' => $type, - 'name' => $name), - 'Required dependency <%type%> "%name%" can have either url OR ' . - 'channel attributes, and not both'); - } - - function _NoChannel($type, $name) - { - $this->_stack->push(__FUNCTION__, 'error', array('type' => $type, - 'name' => $name), - 'Required dependency <%type%> "%name%" must have either url OR ' . - 'channel attributes'); - } - - function _UrlOrChannelGroup($type, $name, $group) - { - $this->_stack->push(__FUNCTION__, 'error', array('type' => $type, - 'name' => $name, 'group' => $group), - 'Group "%group%" dependency <%type%> "%name%" can have either url OR ' . - 'channel attributes, and not both'); - } - - function _NoChannelGroup($type, $name, $group) - { - $this->_stack->push(__FUNCTION__, 'error', array('type' => $type, - 'name' => $name, 'group' => $group), - 'Group "%group%" dependency <%type%> "%name%" must have either url OR ' . - 'channel attributes'); - } - - function _unknownChannel($channel) - { - $this->_stack->push(__FUNCTION__, 'error', array('channel' => $channel), - 'Unknown channel "%channel%"'); - } - - function _noPackageVersion() - { - $this->_stack->push(__FUNCTION__, 'error', array(), - 'package.xml tag has no version attribute, or version is not 2.0'); - } - - function _NoBundledPackages() - { - $this->_stack->push(__FUNCTION__, 'error', array(), - 'No tag was found in , required for bundle packages'); - } - - function _AtLeast2BundledPackages() - { - $this->_stack->push(__FUNCTION__, 'error', array(), - 'At least 2 packages must be bundled in a bundle package'); - } - - function _ChannelOrUri($name) - { - $this->_stack->push(__FUNCTION__, 'error', array('name' => $name), - 'Bundled package "%name%" can have either a uri or a channel, not both'); - } - - function _noChildTag($child, $tag) - { - $this->_stack->push(__FUNCTION__, 'error', array('child' => $child, 'tag' => $tag), - 'Tag <%tag%> is missing child tag <%child%>'); - } - - function _invalidVersion($type, $value) - { - $this->_stack->push(__FUNCTION__, 'error', array('type' => $type, 'value' => $value), - 'Version type <%type%> is not a valid version (%value%)'); - } - - function _invalidState($type, $value) - { - $states = array('stable', 'beta', 'alpha', 'devel'); - if ($type != 'api') { - $states[] = 'snapshot'; - } - if (strtolower($value) == 'rc') { - $this->_stack->push(__FUNCTION__, 'error', - array('version' => $this->_packageInfo['version']['release']), - 'RC is not a state, it is a version postfix, try %version%RC1, stability beta'); - } - $this->_stack->push(__FUNCTION__, 'error', array('type' => $type, 'value' => $value, - 'types' => $states), - 'Stability type <%type%> is not a valid stability (%value%), must be one of ' . - '%types%'); - } - - function _invalidTask($task, $ret, $file) - { - switch ($ret[0]) { - case PEAR_TASK_ERROR_MISSING_ATTRIB : - $info = array('attrib' => $ret[1], 'task' => $task, 'file' => $file); - $msg = 'task <%task%> is missing attribute "%attrib%" in file %file%'; - break; - case PEAR_TASK_ERROR_NOATTRIBS : - $info = array('task' => $task, 'file' => $file); - $msg = 'task <%task%> has no attributes in file %file%'; - break; - case PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE : - $info = array('attrib' => $ret[1], 'values' => $ret[3], - 'was' => $ret[2], 'task' => $task, 'file' => $file); - $msg = 'task <%task%> attribute "%attrib%" has the wrong value "%was%" '. - 'in file %file%, expecting one of "%values%"'; - break; - case PEAR_TASK_ERROR_INVALID : - $info = array('reason' => $ret[1], 'task' => $task, 'file' => $file); - $msg = 'task <%task%> in file %file% is invalid because of "%reason%"'; - break; - } - $this->_stack->push(__FUNCTION__, 'error', $info, $msg); - } - - function _unknownTask($task, $file) - { - $this->_stack->push(__FUNCTION__, 'error', array('task' => $task, 'file' => $file), - 'Unknown task "%task%" passed in file '); - } - - function _subpackageCannotProvideExtension($name) - { - $this->_stack->push(__FUNCTION__, 'error', array('name' => $name), - 'Subpackage dependency "%name%" cannot use , ' . - 'only package dependencies can use this tag'); - } - - function _subpackagesCannotConflict($name) - { - $this->_stack->push(__FUNCTION__, 'error', array('name' => $name), - 'Subpackage dependency "%name%" cannot use , ' . - 'only package dependencies can use this tag'); - } - - function _cannotProvideExtension($release) - { - $this->_stack->push(__FUNCTION__, 'error', array('release' => $release), - '<%release%> packages cannot use , only extbinrelease, extsrcrelease, zendextsrcrelease, and zendextbinrelease can provide a PHP extension'); - } - - function _mustProvideExtension($release) - { - $this->_stack->push(__FUNCTION__, 'error', array('release' => $release), - '<%release%> packages must use to indicate which PHP extension is provided'); - } - - function _cannotHaveSrcpackage($release) - { - $this->_stack->push(__FUNCTION__, 'error', array('release' => $release), - '<%release%> packages cannot specify a source code package, only extension binaries may use the tag'); - } - - function _mustSrcPackage($release) - { - $this->_stack->push(__FUNCTION__, 'error', array('release' => $release), - '/ packages must specify a source code package with '); - } - - function _mustSrcuri($release) - { - $this->_stack->push(__FUNCTION__, 'error', array('release' => $release), - '/ packages must specify a source code package with '); - } - - function _uriDepsCannotHaveVersioning($type) - { - $this->_stack->push(__FUNCTION__, 'error', array('type' => $type), - '%type%: dependencies with a tag cannot have any versioning information'); - } - - function _conflictingDepsCannotHaveVersioning($type) - { - $this->_stack->push(__FUNCTION__, 'error', array('type' => $type), - '%type%: conflicting dependencies cannot have versioning info, use to ' . - 'exclude specific versions of a dependency'); - } - - function _DepchannelCannotBeUri($type) - { - $this->_stack->push(__FUNCTION__, 'error', array('type' => $type), - '%type%: channel cannot be __uri, this is a pseudo-channel reserved for uri ' . - 'dependencies only'); - } - - function _bundledPackagesMustBeFilename() - { - $this->_stack->push(__FUNCTION__, 'error', array(), - ' tags must contain only the filename of a package release ' . - 'in the bundle'); - } - - function _binaryPackageMustBePackagename() - { - $this->_stack->push(__FUNCTION__, 'error', array(), - ' tags must contain the name of a package that is ' . - 'a compiled version of this extsrc/zendextsrc package'); - } - - function _fileNotFound($file) - { - $this->_stack->push(__FUNCTION__, 'error', array('file' => $file), - 'File "%file%" in package.xml does not exist'); - } - - function _notInContents($file, $tag) - { - $this->_stack->push(__FUNCTION__, 'error', array('file' => $file, 'tag' => $tag), - '<%tag% name="%file%"> is invalid, file is not in '); - } - - function _cannotValidateNoPathSet() - { - $this->_stack->push(__FUNCTION__, 'error', array(), - 'Cannot validate files, no path to package file is set (use setPackageFile())'); - } - - function _usesroletaskMustHaveChannelOrUri($role, $tag) - { - $this->_stack->push(__FUNCTION__, 'error', array('role' => $role, 'tag' => $tag), - '<%tag%> for role "%role%" must contain either , or and '); - } - - function _usesroletaskMustHavePackage($role, $tag) - { - $this->_stack->push(__FUNCTION__, 'error', array('role' => $role, 'tag' => $tag), - '<%tag%> for role "%role%" must contain '); - } - - function _usesroletaskMustHaveRoleTask($tag, $type) - { - $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag, 'type' => $type), - '<%tag%> must contain <%type%> defining the %type% to be used'); - } - - function _cannotConflictWithAllOs($type) - { - $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag), - '%tag% cannot conflict with all OSes'); - } - - function _invalidDepGroupName($name) - { - $this->_stack->push(__FUNCTION__, 'error', array('name' => $name), - 'Invalid dependency group name "%name%"'); - } - - function _multipleToplevelDirNotAllowed() - { - $this->_stack->push(__FUNCTION__, 'error', array(), - 'Multiple top-level tags are not allowed. Enclose them ' . - 'in a '); - } - - function _multipleInstallAs($file) - { - $this->_stack->push(__FUNCTION__, 'error', array('file' => $file), - 'Only one tag is allowed for file "%file%"'); - } - - function _ignoreAndInstallAs($file) - { - $this->_stack->push(__FUNCTION__, 'error', array('file' => $file), - 'Cannot have both and tags for file "%file%"'); - } - - function _analyzeBundledPackages() - { - if (!$this->_isValid) { - return false; - } - if (!$this->_pf->getPackageType() == 'bundle') { - return false; - } - if (!isset($this->_pf->_packageFile)) { - return false; - } - $dir_prefix = dirname($this->_pf->_packageFile); - $common = new PEAR_Common; - $log = isset($this->_pf->_logger) ? array(&$this->_pf->_logger, 'log') : - array($common, 'log'); - $info = $this->_pf->getContents(); - $info = $info['bundledpackage']; - if (!is_array($info)) { - $info = array($info); - } - $pkg = &new PEAR_PackageFile($this->_pf->_config); - foreach ($info as $package) { - if (!file_exists($dir_prefix . DIRECTORY_SEPARATOR . $package)) { - $this->_fileNotFound($dir_prefix . DIRECTORY_SEPARATOR . $package); - $this->_isValid = 0; - continue; - } - call_user_func_array($log, array(1, "Analyzing bundled package $package")); - PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - $ret = $pkg->fromAnyFile($dir_prefix . DIRECTORY_SEPARATOR . $package, - PEAR_VALIDATE_NORMAL); - PEAR::popErrorHandling(); - if (PEAR::isError($ret)) { - call_user_func_array($log, array(0, "ERROR: package $package is not a valid " . - 'package')); - $inf = $ret->getUserInfo(); - if (is_array($inf)) { - foreach ($inf as $err) { - call_user_func_array($log, array(1, $err['message'])); - } - } - return false; - } - } - return true; - } - - function _analyzePhpFiles() - { - if (!$this->_isValid) { - return false; - } - if (!isset($this->_pf->_packageFile)) { - $this->_cannotValidateNoPathSet(); - return false; - } - $dir_prefix = dirname($this->_pf->_packageFile); - $common = new PEAR_Common; - $log = isset($this->_pf->_logger) ? array(&$this->_pf->_logger, 'log') : - array(&$common, 'log'); - $info = $this->_pf->getContents(); - if (!$info || !isset($info['dir']['file'])) { - $this->_tagCannotBeEmpty('contents>_fileNotFound($dir_prefix . DIRECTORY_SEPARATOR . $file); - $this->_isValid = 0; - continue; - } - if (in_array($fa['role'], PEAR_Installer_Role::getPhpRoles()) && $dir_prefix) { - call_user_func_array($log, array(1, "Analyzing $file")); - $srcinfo = $this->analyzeSourceCode($dir_prefix . DIRECTORY_SEPARATOR . $file); - if ($srcinfo) { - $provides = array_merge($provides, $this->_buildProvidesArray($srcinfo)); - } - } - } - $this->_packageName = $pn = $this->_pf->getPackage(); - $pnl = strlen($pn); - foreach ($provides as $key => $what) { - if (isset($what['explicit']) || !$what) { - // skip conformance checks if the provides entry is - // specified in the package.xml file - continue; - } - extract($what); - if ($type == 'class') { - if (!strncasecmp($name, $pn, $pnl)) { - continue; - } - $this->_stack->push(__FUNCTION__, 'warning', - array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn), - 'in %file%: %type% "%name%" not prefixed with package name "%package%"'); - } elseif ($type == 'function') { - if (strstr($name, '::') || !strncasecmp($name, $pn, $pnl)) { - continue; - } - $this->_stack->push(__FUNCTION__, 'warning', - array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn), - 'in %file%: %type% "%name%" not prefixed with package name "%package%"'); - } - } - return $this->_isValid; - } - - /** - * Analyze the source code of the given PHP file - * - * @param string Filename of the PHP file - * @param boolean whether to analyze $file as the file contents - * @return mixed - */ - function analyzeSourceCode($file, $string = false) - { - if (!function_exists("token_get_all")) { - $this->_stack->push(__FUNCTION__, 'error', array('file' => $file), - 'Parser error: token_get_all() function must exist to analyze source code, PHP may have been compiled with --disable-tokenizer'); - return false; - } - - if (!defined('T_DOC_COMMENT')) { - define('T_DOC_COMMENT', T_COMMENT); - } - - if (!defined('T_INTERFACE')) { - define('T_INTERFACE', -1); - } - - if (!defined('T_IMPLEMENTS')) { - define('T_IMPLEMENTS', -1); - } - - if ($string) { - $contents = $file; - } else { - if (!$fp = @fopen($file, "r")) { - return false; - } - fclose($fp); - $contents = file_get_contents($file); - } - - // Silence this function so we can catch PHP Warnings and show our own custom message - $tokens = @token_get_all($contents); - if (isset($php_errormsg)) { - if (isset($this->_stack)) { - $pn = $this->_pf->getPackage(); - $this->_stack->push(__FUNCTION__, 'warning', - array('file' => $file, 'package' => $pn), - 'in %file%: Could not process file for unkown reasons,' . - ' possibly a PHP parse error in %file% from %package%'); - } - } -/* - for ($i = 0; $i < sizeof($tokens); $i++) { - @list($token, $data) = $tokens[$i]; - if (is_string($token)) { - var_dump($token); - } else { - print token_name($token) . ' '; - var_dump(rtrim($data)); - } - } -*/ - $look_for = 0; - $paren_level = 0; - $bracket_level = 0; - $brace_level = 0; - $lastphpdoc = ''; - $current_class = ''; - $current_interface = ''; - $current_class_level = -1; - $current_function = ''; - $current_function_level = -1; - $declared_classes = array(); - $declared_interfaces = array(); - $declared_functions = array(); - $declared_methods = array(); - $used_classes = array(); - $used_functions = array(); - $extends = array(); - $implements = array(); - $nodeps = array(); - $inquote = false; - $interface = false; - for ($i = 0; $i < sizeof($tokens); $i++) { - if (is_array($tokens[$i])) { - list($token, $data) = $tokens[$i]; - } else { - $token = $tokens[$i]; - $data = ''; - } - - if ($inquote) { - if ($token != '"' && $token != T_END_HEREDOC) { - continue; - } else { - $inquote = false; - continue; - } - } - - switch ($token) { - case T_WHITESPACE : - continue; - case ';': - if ($interface) { - $current_function = ''; - $current_function_level = -1; - } - break; - case '"': - case T_START_HEREDOC: - $inquote = true; - break; - case T_CURLY_OPEN: - case T_DOLLAR_OPEN_CURLY_BRACES: - case '{': $brace_level++; continue 2; - case '}': - $brace_level--; - if ($current_class_level == $brace_level) { - $current_class = ''; - $current_class_level = -1; - } - if ($current_function_level == $brace_level) { - $current_function = ''; - $current_function_level = -1; - } - continue 2; - case '[': $bracket_level++; continue 2; - case ']': $bracket_level--; continue 2; - case '(': $paren_level++; continue 2; - case ')': $paren_level--; continue 2; - case T_INTERFACE: - $interface = true; - case T_CLASS: - if (($current_class_level != -1) || ($current_function_level != -1)) { - if (isset($this->_stack)) { - $this->_stack->push(__FUNCTION__, 'error', array('file' => $file), - 'Parser error: invalid PHP found in file "%file%"'); - } else { - PEAR::raiseError("Parser error: invalid PHP found in file \"$file\"", - PEAR_COMMON_ERROR_INVALIDPHP); - } - - return false; - } - case T_FUNCTION: - case T_NEW: - case T_EXTENDS: - case T_IMPLEMENTS: - $look_for = $token; - continue 2; - case T_STRING: - if (version_compare(zend_version(), '2.0', '<')) { - if (in_array(strtolower($data), - array('public', 'private', 'protected', 'abstract', - 'interface', 'implements', 'throw') - ) - ) { - if (isset($this->_stack)) { - $this->_stack->push(__FUNCTION__, 'warning', array( - 'file' => $file), - 'Error, PHP5 token encountered in %file%,' . - ' analysis should be in PHP5'); - } else { - PEAR::raiseError('Error: PHP5 token encountered in ' . $file . - 'packaging should be done in PHP 5'); - return false; - } - } - } - - if ($look_for == T_CLASS) { - $current_class = $data; - $current_class_level = $brace_level; - $declared_classes[] = $current_class; - } elseif ($look_for == T_INTERFACE) { - $current_interface = $data; - $current_class_level = $brace_level; - $declared_interfaces[] = $current_interface; - } elseif ($look_for == T_IMPLEMENTS) { - $implements[$current_class] = $data; - } elseif ($look_for == T_EXTENDS) { - $extends[$current_class] = $data; - } elseif ($look_for == T_FUNCTION) { - if ($current_class) { - $current_function = "$current_class::$data"; - $declared_methods[$current_class][] = $data; - } elseif ($current_interface) { - $current_function = "$current_interface::$data"; - $declared_methods[$current_interface][] = $data; - } else { - $current_function = $data; - $declared_functions[] = $current_function; - } - - $current_function_level = $brace_level; - $m = array(); - } elseif ($look_for == T_NEW) { - $used_classes[$data] = true; - } - - $look_for = 0; - continue 2; - case T_VARIABLE: - $look_for = 0; - continue 2; - case T_DOC_COMMENT: - case T_COMMENT: - if (preg_match('!^/\*\*\s!', $data)) { - $lastphpdoc = $data; - if (preg_match_all('/@nodep\s+(\S+)/', $lastphpdoc, $m)) { - $nodeps = array_merge($nodeps, $m[1]); - } - } - continue 2; - case T_DOUBLE_COLON: - $token = $tokens[$i - 1][0]; - if (!($token == T_WHITESPACE || $token == T_STRING || $token == T_STATIC)) { - if (isset($this->_stack)) { - $this->_stack->push(__FUNCTION__, 'warning', array('file' => $file), - 'Parser error: invalid PHP found in file "%file%"'); - } else { - PEAR::raiseError("Parser error: invalid PHP found in file \"$file\"", - PEAR_COMMON_ERROR_INVALIDPHP); - } - - return false; - } - - $class = $tokens[$i - 1][1]; - if (strtolower($class) != 'parent') { - $used_classes[$class] = true; - } - - continue 2; - } - } - - return array( - "source_file" => $file, - "declared_classes" => $declared_classes, - "declared_interfaces" => $declared_interfaces, - "declared_methods" => $declared_methods, - "declared_functions" => $declared_functions, - "used_classes" => array_diff(array_keys($used_classes), $nodeps), - "inheritance" => $extends, - "implements" => $implements, - ); - } - - /** - * Build a "provides" array from data returned by - * analyzeSourceCode(). The format of the built array is like - * this: - * - * array( - * 'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'), - * ... - * ) - * - * - * @param array $srcinfo array with information about a source file - * as returned by the analyzeSourceCode() method. - * - * @return void - * - * @access private - * - */ - function _buildProvidesArray($srcinfo) - { - if (!$this->_isValid) { - return array(); - } - - $providesret = array(); - $file = basename($srcinfo['source_file']); - $pn = isset($this->_pf) ? $this->_pf->getPackage() : ''; - $pnl = strlen($pn); - foreach ($srcinfo['declared_classes'] as $class) { - $key = "class;$class"; - if (isset($providesret[$key])) { - continue; - } - - $providesret[$key] = - array('file'=> $file, 'type' => 'class', 'name' => $class); - if (isset($srcinfo['inheritance'][$class])) { - $providesret[$key]['extends'] = - $srcinfo['inheritance'][$class]; - } - } - - foreach ($srcinfo['declared_methods'] as $class => $methods) { - foreach ($methods as $method) { - $function = "$class::$method"; - $key = "function;$function"; - if ($method{0} == '_' || !strcasecmp($method, $class) || - isset($providesret[$key])) { - continue; - } - - $providesret[$key] = - array('file'=> $file, 'type' => 'function', 'name' => $function); - } - } - - foreach ($srcinfo['declared_functions'] as $function) { - $key = "function;$function"; - if ($function{0} == '_' || isset($providesret[$key])) { - continue; - } - - if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) { - $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\""; - } - - $providesret[$key] = - array('file'=> $file, 'type' => 'function', 'name' => $function); - } - - return $providesret; - } -} \ No newline at end of file diff --git a/pear/PEAR/PackageFile/v2/rw.php b/pear/PEAR/PackageFile/v2/rw.php deleted file mode 100644 index 4320d33..0000000 --- a/pear/PEAR/PackageFile/v2/rw.php +++ /dev/null @@ -1,1607 +0,0 @@ - - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a8 - */ -/** - * For base class - */ -require_once 'PEAR/PackageFile/v2.php'; -/** - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a8 - */ -class PEAR_PackageFile_v2_rw extends PEAR_PackageFile_v2 -{ - /** - * @param string Extension name - * @return bool success of operation - */ - function setProvidesExtension($extension) - { - if (in_array($this->getPackageType(), - array('extsrc', 'extbin', 'zendextsrc', 'zendextbin'))) { - if (!isset($this->_packageInfo['providesextension'])) { - // ensure that the channel tag is set up in the right location - $this->_packageInfo = $this->_insertBefore($this->_packageInfo, - array('usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease', - 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', - 'bundle', 'changelog'), - $extension, 'providesextension'); - } - $this->_packageInfo['providesextension'] = $extension; - return true; - } - return false; - } - - function setPackage($package) - { - $this->_isValid = 0; - if (!isset($this->_packageInfo['attribs'])) { - $this->_packageInfo = array_merge(array('attribs' => array( - 'version' => '2.0', - 'xmlns' => 'http://pear.php.net/dtd/package-2.0', - 'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0', - 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', - 'xsi:schemaLocation' => 'http://pear.php.net/dtd/tasks-1.0 - http://pear.php.net/dtd/tasks-1.0.xsd - http://pear.php.net/dtd/package-2.0 - http://pear.php.net/dtd/package-2.0.xsd', - )), $this->_packageInfo); - } - if (!isset($this->_packageInfo['name'])) { - return $this->_packageInfo = array_merge(array('name' => $package), - $this->_packageInfo); - } - $this->_packageInfo['name'] = $package; - } - - /** - * set this as a package.xml version 2.1 - * @access private - */ - function _setPackageVersion2_1() - { - $info = array( - 'version' => '2.1', - 'xmlns' => 'http://pear.php.net/dtd/package-2.1', - 'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0', - 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', - 'xsi:schemaLocation' => 'http://pear.php.net/dtd/tasks-1.0 - http://pear.php.net/dtd/tasks-1.0.xsd - http://pear.php.net/dtd/package-2.1 - http://pear.php.net/dtd/package-2.1.xsd', - ); - if (!isset($this->_packageInfo['attribs'])) { - $this->_packageInfo = array_merge(array('attribs' => $info), $this->_packageInfo); - } else { - $this->_packageInfo['attribs'] = $info; - } - } - - function setUri($uri) - { - unset($this->_packageInfo['channel']); - $this->_isValid = 0; - if (!isset($this->_packageInfo['uri'])) { - // ensure that the uri tag is set up in the right location - $this->_packageInfo = $this->_insertBefore($this->_packageInfo, - array('extends', 'summary', 'description', 'lead', - 'developer', 'contributor', 'helper', 'date', 'time', 'version', - 'stability', 'license', 'notes', 'contents', 'compatible', - 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', - 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease', - 'extbinrelease', 'bundle', 'changelog'), $uri, 'uri'); - } - $this->_packageInfo['uri'] = $uri; - } - - function setChannel($channel) - { - unset($this->_packageInfo['uri']); - $this->_isValid = 0; - if (!isset($this->_packageInfo['channel'])) { - // ensure that the channel tag is set up in the right location - $this->_packageInfo = $this->_insertBefore($this->_packageInfo, - array('extends', 'summary', 'description', 'lead', - 'developer', 'contributor', 'helper', 'date', 'time', 'version', - 'stability', 'license', 'notes', 'contents', 'compatible', - 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', - 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease', - 'extbinrelease', 'bundle', 'changelog'), $channel, 'channel'); - } - $this->_packageInfo['channel'] = $channel; - } - - function setExtends($extends) - { - $this->_isValid = 0; - if (!isset($this->_packageInfo['extends'])) { - // ensure that the extends tag is set up in the right location - $this->_packageInfo = $this->_insertBefore($this->_packageInfo, - array('summary', 'description', 'lead', - 'developer', 'contributor', 'helper', 'date', 'time', 'version', - 'stability', 'license', 'notes', 'contents', 'compatible', - 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', - 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease', - 'extbinrelease', 'bundle', 'changelog'), $extends, 'extends'); - } - $this->_packageInfo['extends'] = $extends; - } - - function setSummary($summary) - { - $this->_isValid = 0; - if (!isset($this->_packageInfo['summary'])) { - // ensure that the summary tag is set up in the right location - $this->_packageInfo = $this->_insertBefore($this->_packageInfo, - array('description', 'lead', - 'developer', 'contributor', 'helper', 'date', 'time', 'version', - 'stability', 'license', 'notes', 'contents', 'compatible', - 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', - 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease', - 'extbinrelease', 'bundle', 'changelog'), $summary, 'summary'); - } - $this->_packageInfo['summary'] = $summary; - } - - function setDescription($desc) - { - $this->_isValid = 0; - if (!isset($this->_packageInfo['description'])) { - // ensure that the description tag is set up in the right location - $this->_packageInfo = $this->_insertBefore($this->_packageInfo, - array('lead', - 'developer', 'contributor', 'helper', 'date', 'time', 'version', - 'stability', 'license', 'notes', 'contents', 'compatible', - 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', - 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease', - 'extbinrelease', 'bundle', 'changelog'), $desc, 'description'); - } - $this->_packageInfo['description'] = $desc; - } - - /** - * Adds a new maintainer - no checking of duplicates is performed, use - * updatemaintainer for that purpose. - */ - function addMaintainer($role, $handle, $name, $email, $active = 'yes') - { - if (!in_array($role, array('lead', 'developer', 'contributor', 'helper'))) { - return false; - } - if (isset($this->_packageInfo[$role])) { - if (!isset($this->_packageInfo[$role][0])) { - $this->_packageInfo[$role] = array($this->_packageInfo[$role]); - } - $this->_packageInfo[$role][] = - array( - 'name' => $name, - 'user' => $handle, - 'email' => $email, - 'active' => $active, - ); - } else { - $testarr = array('lead', - 'developer', 'contributor', 'helper', 'date', 'time', 'version', - 'stability', 'license', 'notes', 'contents', 'compatible', - 'dependencies', 'providesextension', 'usesrole', 'usestask', - 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', - 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'); - foreach (array('lead', 'developer', 'contributor', 'helper') as $testrole) { - array_shift($testarr); - if ($role == $testrole) { - break; - } - } - if (!isset($this->_packageInfo[$role])) { - // ensure that the extends tag is set up in the right location - $this->_packageInfo = $this->_insertBefore($this->_packageInfo, $testarr, - array(), $role); - } - $this->_packageInfo[$role] = - array( - 'name' => $name, - 'user' => $handle, - 'email' => $email, - 'active' => $active, - ); - } - $this->_isValid = 0; - } - - function updateMaintainer($newrole, $handle, $name, $email, $active = 'yes') - { - $found = false; - foreach (array('lead', 'developer', 'contributor', 'helper') as $role) { - if (!isset($this->_packageInfo[$role])) { - continue; - } - $info = $this->_packageInfo[$role]; - if (!isset($info[0])) { - if (isset($info['user']) && $info['user'] == $handle) { - $found = true; - break; - } - } - foreach ($info as $i => $maintainer) { - if (!isset($maintainer['user'])) { - continue; - } - if ($maintainer['user'] == $handle) { - $found = $i; - break 2; - } - } - } - if ($found === false) { - return $this->addMaintainer($newrole, $handle, $name, $email, $active); - } - if ($found !== false) { - if ($found === true) { - unset($this->_packageInfo[$role]); - } else { - unset($this->_packageInfo[$role][$found]); - $this->_packageInfo[$role] = array_values($this->_packageInfo[$role]); - } - } - $this->addMaintainer($newrole, $handle, $name, $email, $active); - $this->_isValid = 0; - } - - function deleteMaintainer($handle) - { - $found = false; - foreach (array('lead', 'developer', 'contributor', 'helper') as $role) { - if (!isset($this->_packageInfo[$role])) { - continue; - } - if (!isset($this->_packageInfo[$role][0])) { - $this->_packageInfo[$role] = array($this->_packageInfo[$role]); - } - foreach ($this->_packageInfo[$role] as $i => $maintainer) { - if ($maintainer['user'] == $handle) { - $found = $i; - break; - } - } - if ($found !== false) { - unset($this->_packageInfo[$role][$found]); - if (!count($this->_packageInfo[$role]) && $role == 'lead') { - $this->_isValid = 0; - } - if (!count($this->_packageInfo[$role])) { - unset($this->_packageInfo[$role]); - return true; - } - $this->_packageInfo[$role] = - array_values($this->_packageInfo[$role]); - if (count($this->_packageInfo[$role]) == 1) { - $this->_packageInfo[$role] = $this->_packageInfo[$role][0]; - } - return true; - } - if (count($this->_packageInfo[$role]) == 1) { - $this->_packageInfo[$role] = $this->_packageInfo[$role][0]; - } - } - return false; - } - - function setReleaseVersion($version) - { - if (isset($this->_packageInfo['version']) && - isset($this->_packageInfo['version']['release'])) { - unset($this->_packageInfo['version']['release']); - } - $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $version, array( - 'version' => array('stability', 'license', 'notes', 'contents', 'compatible', - 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', - 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease', - 'extbinrelease', 'bundle', 'changelog'), - 'release' => array('api'))); - $this->_isValid = 0; - } - - function setAPIVersion($version) - { - if (isset($this->_packageInfo['version']) && - isset($this->_packageInfo['version']['api'])) { - unset($this->_packageInfo['version']['api']); - } - $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $version, array( - 'version' => array('stability', 'license', 'notes', 'contents', 'compatible', - 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', - 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease', - 'extbinrelease', 'bundle', 'changelog'), - 'api' => array())); - $this->_isValid = 0; - } - - /** - * snapshot|devel|alpha|beta|stable - */ - function setReleaseStability($state) - { - if (isset($this->_packageInfo['stability']) && - isset($this->_packageInfo['stability']['release'])) { - unset($this->_packageInfo['stability']['release']); - } - $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $state, array( - 'stability' => array('license', 'notes', 'contents', 'compatible', - 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', - 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease', - 'extbinrelease', 'bundle', 'changelog'), - 'release' => array('api'))); - $this->_isValid = 0; - } - - /** - * @param devel|alpha|beta|stable - */ - function setAPIStability($state) - { - if (isset($this->_packageInfo['stability']) && - isset($this->_packageInfo['stability']['api'])) { - unset($this->_packageInfo['stability']['api']); - } - $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $state, array( - 'stability' => array('license', 'notes', 'contents', 'compatible', - 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', - 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease', - 'extbinrelease', 'bundle', 'changelog'), - 'api' => array())); - $this->_isValid = 0; - } - - function setLicense($license, $uri = false, $filesource = false) - { - if (!isset($this->_packageInfo['license'])) { - // ensure that the license tag is set up in the right location - $this->_packageInfo = $this->_insertBefore($this->_packageInfo, - array('notes', 'contents', 'compatible', - 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', - 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease', - 'extbinrelease', 'bundle', 'changelog'), 0, 'license'); - } - if ($uri || $filesource) { - $attribs = array(); - if ($uri) { - $attribs['uri'] = $uri; - } - $uri = true; // for test below - if ($filesource) { - $attribs['filesource'] = $filesource; - } - } - $license = $uri ? array('attribs' => $attribs, '_content' => $license) : $license; - $this->_packageInfo['license'] = $license; - $this->_isValid = 0; - } - - function setNotes($notes) - { - $this->_isValid = 0; - if (!isset($this->_packageInfo['notes'])) { - // ensure that the notes tag is set up in the right location - $this->_packageInfo = $this->_insertBefore($this->_packageInfo, - array('contents', 'compatible', - 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri', - 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease', - 'extbinrelease', 'bundle', 'changelog'), $notes, 'notes'); - } - $this->_packageInfo['notes'] = $notes; - } - - /** - * This is only used at install-time, after all serialization - * is over. - * @param string file name - * @param string installed path - */ - function setInstalledAs($file, $path) - { - if ($path) { - return $this->_packageInfo['filelist'][$file]['installed_as'] = $path; - } - unset($this->_packageInfo['filelist'][$file]['installed_as']); - } - - /** - * This is only used at install-time, after all serialization - * is over. - */ - function installedFile($file, $atts) - { - if (isset($this->_packageInfo['filelist'][$file])) { - $this->_packageInfo['filelist'][$file] = - array_merge($this->_packageInfo['filelist'][$file], $atts['attribs']); - } else { - $this->_packageInfo['filelist'][$file] = $atts['attribs']; - } - } - - /** - * Reset the listing of package contents - * @param string base installation dir for the whole package, if any - */ - function clearContents($baseinstall = false) - { - $this->_filesValid = false; - $this->_isValid = 0; - if (!isset($this->_packageInfo['contents'])) { - $this->_packageInfo = $this->_insertBefore($this->_packageInfo, - array('compatible', - 'dependencies', 'providesextension', 'usesrole', 'usestask', - 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', - 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', - 'bundle', 'changelog'), array(), 'contents'); - } - if ($this->getPackageType() != 'bundle') { - $this->_packageInfo['contents'] = - array('dir' => array('attribs' => array('name' => '/'))); - if ($baseinstall) { - $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'] = $baseinstall; - } - } else { - $this->_packageInfo['contents'] = array('bundledpackage' => array()); - } - } - - /** - * @param string relative path of the bundled package. - */ - function addBundledPackage($path) - { - if ($this->getPackageType() != 'bundle') { - return false; - } - $this->_filesValid = false; - $this->_isValid = 0; - $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $path, array( - 'contents' => array('compatible', 'dependencies', 'providesextension', - 'usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease', - 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', - 'bundle', 'changelog'), - 'bundledpackage' => array())); - } - - /** - * @param string file name - * @param PEAR_Task_Common a read/write task - */ - function addTaskToFile($filename, $task) - { - if (!method_exists($task, 'getXml')) { - return false; - } - if (!method_exists($task, 'getName')) { - return false; - } - if (!method_exists($task, 'validate')) { - return false; - } - if (!$task->validate()) { - return false; - } - if (!isset($this->_packageInfo['contents']['dir']['file'])) { - return false; - } - $this->getTasksNs(); // discover the tasks namespace if not done already - $files = $this->_packageInfo['contents']['dir']['file']; - if (!isset($files[0])) { - $files = array($files); - $ind = false; - } else { - $ind = true; - } - foreach ($files as $i => $file) { - if (isset($file['attribs'])) { - if ($file['attribs']['name'] == $filename) { - if ($ind) { - $t = isset($this->_packageInfo['contents']['dir']['file'][$i] - ['attribs'][$this->_tasksNs . - ':' . $task->getName()]) ? - $this->_packageInfo['contents']['dir']['file'][$i] - ['attribs'][$this->_tasksNs . - ':' . $task->getName()] : false; - if ($t && !isset($t[0])) { - $this->_packageInfo['contents']['dir']['file'][$i] - [$this->_tasksNs . ':' . $task->getName()] = array($t); - } - $this->_packageInfo['contents']['dir']['file'][$i][$this->_tasksNs . - ':' . $task->getName()][] = $task->getXml(); - } else { - $t = isset($this->_packageInfo['contents']['dir']['file'] - ['attribs'][$this->_tasksNs . - ':' . $task->getName()]) ? $this->_packageInfo['contents']['dir']['file'] - ['attribs'][$this->_tasksNs . - ':' . $task->getName()] : false; - if ($t && !isset($t[0])) { - $this->_packageInfo['contents']['dir']['file'] - [$this->_tasksNs . ':' . $task->getName()] = array($t); - } - $this->_packageInfo['contents']['dir']['file'][$this->_tasksNs . - ':' . $task->getName()][] = $task->getXml(); - } - return true; - } - } - } - return false; - } - - /** - * @param string path to the file - * @param string filename - * @param array extra attributes - */ - function addFile($dir, $file, $attrs) - { - if ($this->getPackageType() == 'bundle') { - return false; - } - $this->_filesValid = false; - $this->_isValid = 0; - $dir = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'), $dir); - if ($dir == '/' || $dir == '') { - $dir = ''; - } else { - $dir .= '/'; - } - $attrs['name'] = $dir . $file; - if (!isset($this->_packageInfo['contents'])) { - // ensure that the contents tag is set up - $this->_packageInfo = $this->_insertBefore($this->_packageInfo, - array('compatible', 'dependencies', 'providesextension', 'usesrole', 'usestask', - 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', - 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', - 'bundle', 'changelog'), array(), 'contents'); - } - if (isset($this->_packageInfo['contents']['dir']['file'])) { - if (!isset($this->_packageInfo['contents']['dir']['file'][0])) { - $this->_packageInfo['contents']['dir']['file'] = - array($this->_packageInfo['contents']['dir']['file']); - } - $this->_packageInfo['contents']['dir']['file'][]['attribs'] = $attrs; - } else { - $this->_packageInfo['contents']['dir']['file']['attribs'] = $attrs; - } - } - - /** - * @param string Dependent package name - * @param string Dependent package's channel name - * @param string minimum version of specified package that this release is guaranteed to be - * compatible with - * @param string maximum version of specified package that this release is guaranteed to be - * compatible with - * @param string versions of specified package that this release is not compatible with - */ - function addCompatiblePackage($name, $channel, $min, $max, $exclude = false) - { - $this->_isValid = 0; - $set = array( - 'name' => $name, - 'channel' => $channel, - 'min' => $min, - 'max' => $max, - ); - if ($exclude) { - $set['exclude'] = $exclude; - } - $this->_isValid = 0; - $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $set, array( - 'compatible' => array('dependencies', 'providesextension', 'usesrole', 'usestask', - 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', - 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog') - )); - } - - /** - * Removes the tag entirely - */ - function resetUsesrole() - { - if (isset($this->_packageInfo['usesrole'])) { - unset($this->_packageInfo['usesrole']); - } - } - - /** - * @param string - * @param string package name or uri - * @param string channel name if non-uri - */ - function addUsesrole($role, $packageOrUri, $channel = false) { - $set = array('role' => $role); - if ($channel) { - $set['package'] = $packageOrUri; - $set['channel'] = $channel; - } else { - $set['uri'] = $packageOrUri; - } - $this->_isValid = 0; - $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $set, array( - 'usesrole' => array('usestask', 'srcpackage', 'srcuri', - 'phprelease', 'extsrcrelease', 'extbinrelease', - 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog') - )); - } - - /** - * Removes the tag entirely - */ - function resetUsestask() - { - if (isset($this->_packageInfo['usestask'])) { - unset($this->_packageInfo['usestask']); - } - } - - - /** - * @param string - * @param string package name or uri - * @param string channel name if non-uri - */ - function addUsestask($task, $packageOrUri, $channel = false) { - $set = array('task' => $task); - if ($channel) { - $set['package'] = $packageOrUri; - $set['channel'] = $channel; - } else { - $set['uri'] = $packageOrUri; - } - $this->_isValid = 0; - $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $set, array( - 'usestask' => array('srcpackage', 'srcuri', - 'phprelease', 'extsrcrelease', 'extbinrelease', - 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog') - )); - } - - /** - * Remove all compatible tags - */ - function clearCompatible() - { - unset($this->_packageInfo['compatible']); - } - - /** - * Reset dependencies prior to adding new ones - */ - function clearDeps() - { - if (!isset($this->_packageInfo['dependencies'])) { - $this->_packageInfo = $this->_mergeTag($this->_packageInfo, array(), - array( - 'dependencies' => array('providesextension', 'usesrole', 'usestask', - 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', - 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'))); - } - $this->_packageInfo['dependencies'] = array(); - } - - /** - * @param string minimum PHP version allowed - * @param string maximum PHP version allowed - * @param array $exclude incompatible PHP versions - */ - function setPhpDep($min, $max = false, $exclude = false) - { - $this->_isValid = 0; - $dep = - array( - 'min' => $min, - ); - if ($max) { - $dep['max'] = $max; - } - if ($exclude) { - if (count($exclude) == 1) { - $exclude = $exclude[0]; - } - $dep['exclude'] = $exclude; - } - if (isset($this->_packageInfo['dependencies']['required']['php'])) { - $this->_stack->push(__FUNCTION__, 'warning', array('dep' => - $this->_packageInfo['dependencies']['required']['php']), - 'warning: PHP dependency already exists, overwriting'); - unset($this->_packageInfo['dependencies']['required']['php']); - } - $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep, - array( - 'dependencies' => array('providesextension', 'usesrole', 'usestask', - 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', - 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), - 'required' => array('optional', 'group'), - 'php' => array('pearinstaller', 'package', 'subpackage', 'extension', 'os', 'arch') - )); - return true; - } - - /** - * @param string minimum allowed PEAR installer version - * @param string maximum allowed PEAR installer version - * @param string recommended PEAR installer version - * @param array incompatible version of the PEAR installer - */ - function setPearinstallerDep($min, $max = false, $recommended = false, $exclude = false) - { - $this->_isValid = 0; - $dep = - array( - 'min' => $min, - ); - if ($max) { - $dep['max'] = $max; - } - if ($recommended) { - $dep['recommended'] = $recommended; - } - if ($exclude) { - if (count($exclude) == 1) { - $exclude = $exclude[0]; - } - $dep['exclude'] = $exclude; - } - if (isset($this->_packageInfo['dependencies']['required']['pearinstaller'])) { - $this->_stack->push(__FUNCTION__, 'warning', array('dep' => - $this->_packageInfo['dependencies']['required']['pearinstaller']), - 'warning: PEAR Installer dependency already exists, overwriting'); - unset($this->_packageInfo['dependencies']['required']['pearinstaller']); - } - $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep, - array( - 'dependencies' => array('providesextension', 'usesrole', 'usestask', - 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', - 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), - 'required' => array('optional', 'group'), - 'pearinstaller' => array('package', 'subpackage', 'extension', 'os', 'arch') - )); - } - - /** - * Mark a package as conflicting with this package - * @param string package name - * @param string package channel - * @param string extension this package provides, if any - * @param string|false minimum version required - * @param string|false maximum version allowed - * @param array|false versions to exclude from installation - */ - function addConflictingPackageDepWithChannel($name, $channel, - $providesextension = false, $min = false, $max = false, $exclude = false) - { - $this->_isValid = 0; - $dep = $this->_constructDep($name, $channel, false, $min, $max, false, - $exclude, $providesextension, false, true); - $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep, - array( - 'dependencies' => array('providesextension', 'usesrole', 'usestask', - 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', - 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), - 'required' => array('optional', 'group'), - 'package' => array('subpackage', 'extension', 'os', 'arch') - )); - } - - /** - * Mark a package as conflicting with this package - * @param string package name - * @param string package channel - * @param string extension this package provides, if any - */ - function addConflictingPackageDepWithUri($name, $uri, $providesextension = false) - { - $this->_isValid = 0; - $dep = - array( - 'name' => $name, - 'uri' => $uri, - 'conflicts' => '', - ); - if ($providesextension) { - $dep['providesextension'] = $providesextension; - } - $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep, - array( - 'dependencies' => array('providesextension', 'usesrole', 'usestask', - 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', - 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), - 'required' => array('optional', 'group'), - 'package' => array('subpackage', 'extension', 'os', 'arch') - )); - } - - function addDependencyGroup($name, $hint) - { - $this->_isValid = 0; - $this->_packageInfo = $this->_mergeTag($this->_packageInfo, - array('attribs' => array('name' => $name, 'hint' => $hint)), - array( - 'dependencies' => array('providesextension', 'usesrole', 'usestask', - 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', - 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), - 'group' => array(), - )); - } - - /** - * @param string package name - * @param string|false channel name, false if this is a uri - * @param string|false uri name, false if this is a channel - * @param string|false minimum version required - * @param string|false maximum version allowed - * @param string|false recommended installation version - * @param array|false versions to exclude from installation - * @param string extension this package provides, if any - * @param bool if true, tells the installer to ignore the default optional dependency group - * when installing this package - * @param bool if true, tells the installer to negate this dependency (conflicts) - * @return array - * @access private - */ - function _constructDep($name, $channel, $uri, $min, $max, $recommended, $exclude, - $providesextension = false, $nodefault = false, - $conflicts = false) - { - $dep = - array( - 'name' => $name, - ); - if ($channel) { - $dep['channel'] = $channel; - } elseif ($uri) { - $dep['uri'] = $uri; - } - if ($min) { - $dep['min'] = $min; - } - if ($max) { - $dep['max'] = $max; - } - if ($recommended) { - $dep['recommended'] = $recommended; - } - if ($exclude) { - if (is_array($exclude) && count($exclude) == 1) { - $exclude = $exclude[0]; - } - $dep['exclude'] = $exclude; - } - if ($conflicts) { - $dep['conflicts'] = ''; - } - if ($nodefault) { - $dep['nodefault'] = ''; - } - if ($providesextension) { - $dep['providesextension'] = $providesextension; - } - return $dep; - } - - /** - * @param package|subpackage - * @param string group name - * @param string package name - * @param string package channel - * @param string minimum version - * @param string maximum version - * @param string recommended version - * @param array|false optional excluded versions - * @param string extension this package provides, if any - * @param bool if true, tells the installer to ignore the default optional dependency group - * when installing this package - * @return bool false if the dependency group has not been initialized with - * {@link addDependencyGroup()}, or a subpackage is added with - * a providesextension - */ - function addGroupPackageDepWithChannel($type, $groupname, $name, $channel, $min = false, - $max = false, $recommended = false, $exclude = false, - $providesextension = false, $nodefault = false) - { - if ($type == 'subpackage' && $providesextension) { - return false; // subpackages must be php packages - } - $dep = $this->_constructDep($name, $channel, false, $min, $max, $recommended, $exclude, - $providesextension, $nodefault); - return $this->_addGroupDependency($type, $dep, $groupname); - } - - /** - * @param package|subpackage - * @param string group name - * @param string package name - * @param string package uri - * @param string extension this package provides, if any - * @param bool if true, tells the installer to ignore the default optional dependency group - * when installing this package - * @return bool false if the dependency group has not been initialized with - * {@link addDependencyGroup()} - */ - function addGroupPackageDepWithURI($type, $groupname, $name, $uri, $providesextension = false, - $nodefault = false) - { - if ($type == 'subpackage' && $providesextension) { - return false; // subpackages must be php packages - } - $dep = $this->_constructDep($name, false, $uri, false, false, false, false, - $providesextension, $nodefault); - return $this->_addGroupDependency($type, $dep, $groupname); - } - - /** - * @param string group name (must be pre-existing) - * @param string extension name - * @param string minimum version allowed - * @param string maximum version allowed - * @param string recommended version - * @param array incompatible versions - */ - function addGroupExtensionDep($groupname, $name, $min = false, $max = false, - $recommended = false, $exclude = false) - { - $this->_isValid = 0; - $dep = $this->_constructDep($name, false, false, $min, $max, $recommended, $exclude); - return $this->_addGroupDependency('extension', $dep, $groupname); - } - - /** - * @param package|subpackage|extension - * @param array dependency contents - * @param string name of the dependency group to add this to - * @return boolean - * @access private - */ - function _addGroupDependency($type, $dep, $groupname) - { - $arr = array('subpackage', 'extension'); - if ($type != 'package') { - array_shift($arr); - } - if ($type == 'extension') { - array_shift($arr); - } - if (!isset($this->_packageInfo['dependencies']['group'])) { - return false; - } else { - if (!isset($this->_packageInfo['dependencies']['group'][0])) { - if ($this->_packageInfo['dependencies']['group']['attribs']['name'] == $groupname) { - $this->_packageInfo['dependencies']['group'] = $this->_mergeTag( - $this->_packageInfo['dependencies']['group'], $dep, - array( - $type => $arr - )); - $this->_isValid = 0; - return true; - } else { - return false; - } - } else { - foreach ($this->_packageInfo['dependencies']['group'] as $i => $group) { - if ($group['attribs']['name'] == $groupname) { - $this->_packageInfo['dependencies']['group'][$i] = $this->_mergeTag( - $this->_packageInfo['dependencies']['group'][$i], $dep, - array( - $type => $arr - )); - $this->_isValid = 0; - return true; - } - } - return false; - } - } - } - - /** - * @param optional|required - * @param string package name - * @param string package channel - * @param string minimum version - * @param string maximum version - * @param string recommended version - * @param string extension this package provides, if any - * @param bool if true, tells the installer to ignore the default optional dependency group - * when installing this package - * @param array|false optional excluded versions - */ - function addPackageDepWithChannel($type, $name, $channel, $min = false, $max = false, - $recommended = false, $exclude = false, - $providesextension = false, $nodefault = false) - { - if (!in_array($type, array('optional', 'required'), true)) { - $type = 'required'; - } - $this->_isValid = 0; - $arr = array('optional', 'group'); - if ($type != 'required') { - array_shift($arr); - } - $dep = $this->_constructDep($name, $channel, false, $min, $max, $recommended, $exclude, - $providesextension, $nodefault); - $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep, - array( - 'dependencies' => array('providesextension', 'usesrole', 'usestask', - 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', - 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), - $type => $arr, - 'package' => array('subpackage', 'extension', 'os', 'arch') - )); - } - - /** - * @param optional|required - * @param string name of the package - * @param string uri of the package - * @param string extension this package provides, if any - * @param bool if true, tells the installer to ignore the default optional dependency group - * when installing this package - */ - function addPackageDepWithUri($type, $name, $uri, $providesextension = false, - $nodefault = false) - { - $this->_isValid = 0; - $arr = array('optional', 'group'); - if ($type != 'required') { - array_shift($arr); - } - $dep = $this->_constructDep($name, false, $uri, false, false, false, false, - $providesextension, $nodefault); - $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep, - array( - 'dependencies' => array('providesextension', 'usesrole', 'usestask', - 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', - 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), - $type => $arr, - 'package' => array('subpackage', 'extension', 'os', 'arch') - )); - } - - /** - * @param optional|required optional, required - * @param string package name - * @param string package channel - * @param string minimum version - * @param string maximum version - * @param string recommended version - * @param array incompatible versions - * @param bool if true, tells the installer to ignore the default optional dependency group - * when installing this package - */ - function addSubpackageDepWithChannel($type, $name, $channel, $min = false, $max = false, - $recommended = false, $exclude = false, - $nodefault = false) - { - $this->_isValid = 0; - $arr = array('optional', 'group'); - if ($type != 'required') { - array_shift($arr); - } - $dep = $this->_constructDep($name, $channel, false, $min, $max, $recommended, $exclude, - $nodefault); - $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep, - array( - 'dependencies' => array('providesextension', 'usesrole', 'usestask', - 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', - 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), - $type => $arr, - 'subpackage' => array('extension', 'os', 'arch') - )); - } - - /** - * @param optional|required optional, required - * @param string package name - * @param string package uri for download - * @param bool if true, tells the installer to ignore the default optional dependency group - * when installing this package - */ - function addSubpackageDepWithUri($type, $name, $uri, $nodefault = false) - { - $this->_isValid = 0; - $arr = array('optional', 'group'); - if ($type != 'required') { - array_shift($arr); - } - $dep = $this->_constructDep($name, false, $uri, false, false, false, false, $nodefault); - $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep, - array( - 'dependencies' => array('providesextension', 'usesrole', 'usestask', - 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', - 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), - $type => $arr, - 'subpackage' => array('extension', 'os', 'arch') - )); - } - - /** - * @param optional|required optional, required - * @param string extension name - * @param string minimum version - * @param string maximum version - * @param string recommended version - * @param array incompatible versions - */ - function addExtensionDep($type, $name, $min = false, $max = false, $recommended = false, - $exclude = false) - { - $this->_isValid = 0; - $arr = array('optional', 'group'); - if ($type != 'required') { - array_shift($arr); - } - $dep = $this->_constructDep($name, false, false, $min, $max, $recommended, $exclude); - $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep, - array( - 'dependencies' => array('providesextension', 'usesrole', 'usestask', - 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', - 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), - $type => $arr, - 'extension' => array('os', 'arch') - )); - } - - /** - * @param string Operating system name - * @param boolean true if this package cannot be installed on this OS - */ - function addOsDep($name, $conflicts = false) - { - $this->_isValid = 0; - $dep = array('name' => $name); - if ($conflicts) { - $dep['conflicts'] = ''; - } - $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep, - array( - 'dependencies' => array('providesextension', 'usesrole', 'usestask', - 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', - 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), - 'required' => array('optional', 'group'), - 'os' => array('arch') - )); - } - - /** - * @param string Architecture matching pattern - * @param boolean true if this package cannot be installed on this architecture - */ - function addArchDep($pattern, $conflicts = false) - { - $this->_isValid = 0; - $dep = array('pattern' => $pattern); - if ($conflicts) { - $dep['conflicts'] = ''; - } - $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep, - array( - 'dependencies' => array('providesextension', 'usesrole', 'usestask', - 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease', - 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'), - 'required' => array('optional', 'group'), - 'arch' => array() - )); - } - - /** - * Set the kind of package, and erase all release tags - * - * - a php package is a PEAR-style package - * - an extbin package is a PECL-style extension binary - * - an extsrc package is a PECL-style source for a binary - * - an zendextbin package is a PECL-style zend extension binary - * - an zendextsrc package is a PECL-style source for a zend extension binary - * - a bundle package is a collection of other pre-packaged packages - * @param php|extbin|extsrc|zendextsrc|zendextbin|bundle - * @return bool success - */ - function setPackageType($type) - { - $this->_isValid = 0; - if (!in_array($type, array('php', 'extbin', 'extsrc', 'zendextsrc', - 'zendextbin', 'bundle'))) { - return false; - } - - if (in_array($type, array('zendextsrc', 'zendextbin'))) { - $this->_setPackageVersion2_1(); - } - - if ($type != 'bundle') { - $type .= 'release'; - } - - foreach (array('phprelease', 'extbinrelease', 'extsrcrelease', - 'zendextsrcrelease', 'zendextbinrelease', 'bundle') as $test) { - unset($this->_packageInfo[$test]); - } - - if (!isset($this->_packageInfo[$type])) { - // ensure that the release tag is set up - $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('changelog'), - array(), $type); - } - - $this->_packageInfo[$type] = array(); - return true; - } - - /** - * @return bool true if package type is set up - */ - function addRelease() - { - if ($type = $this->getPackageType()) { - if ($type != 'bundle') { - $type .= 'release'; - } - $this->_packageInfo = $this->_mergeTag($this->_packageInfo, array(), - array($type => array('changelog'))); - return true; - } - return false; - } - - /** - * Get the current release tag in order to add to it - * @param bool returns only releases that have installcondition if true - * @return array|null - */ - function &_getCurrentRelease($strict = true) - { - if ($p = $this->getPackageType()) { - if ($strict) { - if ($p == 'extsrc' || $p == 'zendextsrc') { - $a = null; - return $a; - } - } - if ($p != 'bundle') { - $p .= 'release'; - } - if (isset($this->_packageInfo[$p][0])) { - return $this->_packageInfo[$p][count($this->_packageInfo[$p]) - 1]; - } else { - return $this->_packageInfo[$p]; - } - } else { - $a = null; - return $a; - } - } - - /** - * Add a file to the current release that should be installed under a different name - * @param string path to file - * @param string name the file should be installed as - */ - function addInstallAs($path, $as) - { - $r = &$this->_getCurrentRelease(); - if ($r === null) { - return false; - } - $this->_isValid = 0; - $r = $this->_mergeTag($r, array('attribs' => array('name' => $path, 'as' => $as)), - array( - 'filelist' => array(), - 'install' => array('ignore') - )); - } - - /** - * Add a file to the current release that should be ignored - * @param string path to file - * @return bool success of operation - */ - function addIgnore($path) - { - $r = &$this->_getCurrentRelease(); - if ($r === null) { - return false; - } - $this->_isValid = 0; - $r = $this->_mergeTag($r, array('attribs' => array('name' => $path)), - array( - 'filelist' => array(), - 'ignore' => array() - )); - } - - /** - * Add an extension binary package for this extension source code release - * - * Note that the package must be from the same channel as the extension source package - * @param string - */ - function addBinarypackage($package) - { - if ($this->getPackageType() != 'extsrc' && $this->getPackageType() != 'zendextsrc') { - return false; - } - $r = &$this->_getCurrentRelease(false); - if ($r === null) { - return false; - } - $this->_isValid = 0; - $r = $this->_mergeTag($r, $package, - array( - 'binarypackage' => array('filelist'), - )); - } - - /** - * Add a configureoption to an extension source package - * @param string - * @param string - * @param string - */ - function addConfigureOption($name, $prompt, $default = null) - { - if ($this->getPackageType() != 'extsrc' && $this->getPackageType() != 'zendextsrc') { - return false; - } - - $r = &$this->_getCurrentRelease(false); - if ($r === null) { - return false; - } - - $opt = array('attribs' => array('name' => $name, 'prompt' => $prompt)); - if ($default !== null) { - $opt['attribs']['default'] = $default; - } - - $this->_isValid = 0; - $r = $this->_mergeTag($r, $opt, - array( - 'configureoption' => array('binarypackage', 'filelist'), - )); - } - - /** - * Set an installation condition based on php version for the current release set - * @param string minimum version - * @param string maximum version - * @param false|array incompatible versions of PHP - */ - function setPhpInstallCondition($min, $max, $exclude = false) - { - $r = &$this->_getCurrentRelease(); - if ($r === null) { - return false; - } - $this->_isValid = 0; - if (isset($r['installconditions']['php'])) { - unset($r['installconditions']['php']); - } - $dep = array('min' => $min, 'max' => $max); - if ($exclude) { - if (is_array($exclude) && count($exclude) == 1) { - $exclude = $exclude[0]; - } - $dep['exclude'] = $exclude; - } - if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') { - $r = $this->_mergeTag($r, $dep, - array( - 'installconditions' => array('configureoption', 'binarypackage', - 'filelist'), - 'php' => array('extension', 'os', 'arch') - )); - } else { - $r = $this->_mergeTag($r, $dep, - array( - 'installconditions' => array('filelist'), - 'php' => array('extension', 'os', 'arch') - )); - } - } - - /** - * @param optional|required optional, required - * @param string extension name - * @param string minimum version - * @param string maximum version - * @param string recommended version - * @param array incompatible versions - */ - function addExtensionInstallCondition($name, $min = false, $max = false, $recommended = false, - $exclude = false) - { - $r = &$this->_getCurrentRelease(); - if ($r === null) { - return false; - } - $this->_isValid = 0; - $dep = $this->_constructDep($name, false, false, $min, $max, $recommended, $exclude); - if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') { - $r = $this->_mergeTag($r, $dep, - array( - 'installconditions' => array('configureoption', 'binarypackage', - 'filelist'), - 'extension' => array('os', 'arch') - )); - } else { - $r = $this->_mergeTag($r, $dep, - array( - 'installconditions' => array('filelist'), - 'extension' => array('os', 'arch') - )); - } - } - - /** - * Set an installation condition based on operating system for the current release set - * @param string OS name - * @param bool whether this OS is incompatible with the current release - */ - function setOsInstallCondition($name, $conflicts = false) - { - $r = &$this->_getCurrentRelease(); - if ($r === null) { - return false; - } - $this->_isValid = 0; - if (isset($r['installconditions']['os'])) { - unset($r['installconditions']['os']); - } - $dep = array('name' => $name); - if ($conflicts) { - $dep['conflicts'] = ''; - } - if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') { - $r = $this->_mergeTag($r, $dep, - array( - 'installconditions' => array('configureoption', 'binarypackage', - 'filelist'), - 'os' => array('arch') - )); - } else { - $r = $this->_mergeTag($r, $dep, - array( - 'installconditions' => array('filelist'), - 'os' => array('arch') - )); - } - } - - /** - * Set an installation condition based on architecture for the current release set - * @param string architecture pattern - * @param bool whether this arch is incompatible with the current release - */ - function setArchInstallCondition($pattern, $conflicts = false) - { - $r = &$this->_getCurrentRelease(); - if ($r === null) { - return false; - } - $this->_isValid = 0; - if (isset($r['installconditions']['arch'])) { - unset($r['installconditions']['arch']); - } - $dep = array('pattern' => $pattern); - if ($conflicts) { - $dep['conflicts'] = ''; - } - if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') { - $r = $this->_mergeTag($r, $dep, - array( - 'installconditions' => array('configureoption', 'binarypackage', - 'filelist'), - 'arch' => array() - )); - } else { - $r = $this->_mergeTag($r, $dep, - array( - 'installconditions' => array('filelist'), - 'arch' => array() - )); - } - } - - /** - * For extension binary releases, this is used to specify either the - * static URI to a source package, or the package name and channel of the extsrc/zendextsrc - * package it is based on. - * @param string Package name, or full URI to source package (extsrc/zendextsrc type) - */ - function setSourcePackage($packageOrUri) - { - $this->_isValid = 0; - if (isset($this->_packageInfo['channel'])) { - $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('phprelease', - 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', - 'bundle', 'changelog'), - $packageOrUri, 'srcpackage'); - } else { - $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('phprelease', - 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', - 'bundle', 'changelog'), $packageOrUri, 'srcuri'); - } - } - - /** - * Generate a valid change log entry from the current package.xml - * @param string|false - */ - function generateChangeLogEntry($notes = false) - { - return array( - 'version' => - array( - 'release' => $this->getVersion('release'), - 'api' => $this->getVersion('api'), - ), - 'stability' => - $this->getStability(), - 'date' => $this->getDate(), - 'license' => $this->getLicense(true), - 'notes' => $notes ? $notes : $this->getNotes() - ); - } - - /** - * @param string release version to set change log notes for - * @param array output of {@link generateChangeLogEntry()} - */ - function setChangelogEntry($releaseversion, $contents) - { - if (!isset($this->_packageInfo['changelog'])) { - $this->_packageInfo['changelog']['release'] = $contents; - return; - } - if (!isset($this->_packageInfo['changelog']['release'][0])) { - if ($this->_packageInfo['changelog']['release']['version']['release'] == $releaseversion) { - $this->_packageInfo['changelog']['release'] = array( - $this->_packageInfo['changelog']['release']); - } else { - $this->_packageInfo['changelog']['release'] = array( - $this->_packageInfo['changelog']['release']); - return $this->_packageInfo['changelog']['release'][] = $contents; - } - } - foreach($this->_packageInfo['changelog']['release'] as $index => $changelog) { - if (isset($changelog['version']) && - strnatcasecmp($changelog['version']['release'], $releaseversion) == 0) { - $curlog = $index; - } - } - if (isset($curlog)) { - $this->_packageInfo['changelog']['release'][$curlog] = $contents; - } else { - $this->_packageInfo['changelog']['release'][] = $contents; - } - } - - /** - * Remove the changelog entirely - */ - function clearChangeLog() - { - unset($this->_packageInfo['changelog']); - } -} \ No newline at end of file diff --git a/pear/PEAR/Packager.php b/pear/PEAR/Packager.php deleted file mode 100644 index 057a714..0000000 --- a/pear/PEAR/Packager.php +++ /dev/null @@ -1,201 +0,0 @@ - - * @author Tomas V. V. Cox - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 0.1 - */ - -/** - * base class - */ -require_once 'PEAR/Common.php'; -require_once 'PEAR/PackageFile.php'; -require_once 'System.php'; - -/** - * Administration class used to make a PEAR release tarball. - * - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 0.1 - */ -class PEAR_Packager extends PEAR_Common -{ - /** - * @var PEAR_Registry - */ - var $_registry; - - function package($pkgfile = null, $compress = true, $pkg2 = null) - { - // {{{ validate supplied package.xml file - if (empty($pkgfile)) { - $pkgfile = 'package.xml'; - } - - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $pkg = &new PEAR_PackageFile($this->config, $this->debug); - $pf = &$pkg->fromPackageFile($pkgfile, PEAR_VALIDATE_NORMAL); - $main = &$pf; - PEAR::staticPopErrorHandling(); - if (PEAR::isError($pf)) { - if (is_array($pf->getUserInfo())) { - foreach ($pf->getUserInfo() as $error) { - $this->log(0, 'Error: ' . $error['message']); - } - } - - $this->log(0, $pf->getMessage()); - return $this->raiseError("Cannot package, errors in package file"); - } - - foreach ($pf->getValidationWarnings() as $warning) { - $this->log(1, 'Warning: ' . $warning['message']); - } - - // }}} - if ($pkg2) { - $this->log(0, 'Attempting to process the second package file'); - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $pf2 = &$pkg->fromPackageFile($pkg2, PEAR_VALIDATE_NORMAL); - PEAR::staticPopErrorHandling(); - if (PEAR::isError($pf2)) { - if (is_array($pf2->getUserInfo())) { - foreach ($pf2->getUserInfo() as $error) { - $this->log(0, 'Error: ' . $error['message']); - } - } - $this->log(0, $pf2->getMessage()); - return $this->raiseError("Cannot package, errors in second package file"); - } - - foreach ($pf2->getValidationWarnings() as $warning) { - $this->log(1, 'Warning: ' . $warning['message']); - } - - if ($pf2->getPackagexmlVersion() == '2.0' || - $pf2->getPackagexmlVersion() == '2.1' - ) { - $main = &$pf2; - $other = &$pf; - } else { - $main = &$pf; - $other = &$pf2; - } - - if ($main->getPackagexmlVersion() != '2.0' && - $main->getPackagexmlVersion() != '2.1') { - return PEAR::raiseError('Error: cannot package two package.xml version 1.0, can ' . - 'only package together a package.xml 1.0 and package.xml 2.0'); - } - - if ($other->getPackagexmlVersion() != '1.0') { - return PEAR::raiseError('Error: cannot package two package.xml version 2.0, can ' . - 'only package together a package.xml 1.0 and package.xml 2.0'); - } - } - - $main->setLogger($this); - if (!$main->validate(PEAR_VALIDATE_PACKAGING)) { - foreach ($main->getValidationWarnings() as $warning) { - $this->log(0, 'Error: ' . $warning['message']); - } - return $this->raiseError("Cannot package, errors in package"); - } - - foreach ($main->getValidationWarnings() as $warning) { - $this->log(1, 'Warning: ' . $warning['message']); - } - - if ($pkg2) { - $other->setLogger($this); - $a = false; - if (!$other->validate(PEAR_VALIDATE_NORMAL) || $a = !$main->isEquivalent($other)) { - foreach ($other->getValidationWarnings() as $warning) { - $this->log(0, 'Error: ' . $warning['message']); - } - - foreach ($main->getValidationWarnings() as $warning) { - $this->log(0, 'Error: ' . $warning['message']); - } - - if ($a) { - return $this->raiseError('The two package.xml files are not equivalent!'); - } - - return $this->raiseError("Cannot package, errors in package"); - } - - foreach ($other->getValidationWarnings() as $warning) { - $this->log(1, 'Warning: ' . $warning['message']); - } - - $gen = &$main->getDefaultGenerator(); - $tgzfile = $gen->toTgz2($this, $other, $compress); - if (PEAR::isError($tgzfile)) { - return $tgzfile; - } - - $dest_package = basename($tgzfile); - $pkgdir = dirname($pkgfile); - - // TAR the Package ------------------------------------------- - $this->log(1, "Package $dest_package done"); - if (file_exists("$pkgdir/CVS/Root")) { - $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $pf->getVersion()); - $cvstag = "RELEASE_$cvsversion"; - $this->log(1, 'Tag the released code with "pear cvstag ' . - $main->getPackageFile() . '"'); - $this->log(1, "(or set the CVS tag $cvstag by hand)"); - } elseif (file_exists("$pkgdir/.svn")) { - $svnversion = preg_replace('/[^a-z0-9]/i', '.', $pf->getVersion()); - $svntag = $pf->getName() . "-$svnversion"; - $this->log(1, 'Tag the released code with "pear svntag ' . - $main->getPackageFile() . '"'); - $this->log(1, "(or set the SVN tag $svntag by hand)"); - } - } else { // this branch is executed for single packagefile packaging - $gen = &$pf->getDefaultGenerator(); - $tgzfile = $gen->toTgz($this, $compress); - if (PEAR::isError($tgzfile)) { - $this->log(0, $tgzfile->getMessage()); - return $this->raiseError("Cannot package, errors in package"); - } - - $dest_package = basename($tgzfile); - $pkgdir = dirname($pkgfile); - - // TAR the Package ------------------------------------------- - $this->log(1, "Package $dest_package done"); - if (file_exists("$pkgdir/CVS/Root")) { - $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $pf->getVersion()); - $cvstag = "RELEASE_$cvsversion"; - $this->log(1, "Tag the released code with `pear cvstag $pkgfile'"); - $this->log(1, "(or set the CVS tag $cvstag by hand)"); - } elseif (file_exists("$pkgdir/.svn")) { - $svnversion = preg_replace('/[^a-z0-9]/i', '.', $pf->getVersion()); - $svntag = $pf->getName() . "-$svnversion"; - $this->log(1, "Tag the released code with `pear svntag $pkgfile'"); - $this->log(1, "(or set the SVN tag $svntag by hand)"); - } - } - - return $dest_package; - } -} \ No newline at end of file diff --git a/pear/PEAR/REST.php b/pear/PEAR/REST.php deleted file mode 100644 index 76a13cd..0000000 --- a/pear/PEAR/REST.php +++ /dev/null @@ -1,483 +0,0 @@ - - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a1 - */ - -/** - * For downloading xml files - */ -require_once 'PEAR.php'; -require_once 'PEAR/XMLParser.php'; - -/** - * Intelligently retrieve data, following hyperlinks if necessary, and re-directing - * as well - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a1 - */ -class PEAR_REST -{ - var $config; - var $_options; - - function PEAR_REST(&$config, $options = array()) - { - $this->config = &$config; - $this->_options = $options; - } - - /** - * Retrieve REST data, but always retrieve the local cache if it is available. - * - * This is useful for elements that should never change, such as information on a particular - * release - * @param string full URL to this resource - * @param array|false contents of the accept-encoding header - * @param boolean if true, xml will be returned as a string, otherwise, xml will be - * parsed using PEAR_XMLParser - * @return string|array - */ - function retrieveCacheFirst($url, $accept = false, $forcestring = false, $channel = false) - { - $cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR . - md5($url) . 'rest.cachefile'; - - if (file_exists($cachefile)) { - return unserialize(implode('', file($cachefile))); - } - - return $this->retrieveData($url, $accept, $forcestring, $channel); - } - - /** - * Retrieve a remote REST resource - * @param string full URL to this resource - * @param array|false contents of the accept-encoding header - * @param boolean if true, xml will be returned as a string, otherwise, xml will be - * parsed using PEAR_XMLParser - * @return string|array - */ - function retrieveData($url, $accept = false, $forcestring = false, $channel = false) - { - $cacheId = $this->getCacheId($url); - if ($ret = $this->useLocalCache($url, $cacheId)) { - return $ret; - } - - $file = $trieddownload = false; - if (!isset($this->_options['offline'])) { - $trieddownload = true; - $file = $this->downloadHttp($url, $cacheId ? $cacheId['lastChange'] : false, $accept, $channel); - } - - if (PEAR::isError($file)) { - if ($file->getCode() !== -9276) { - return $file; - } - - $trieddownload = false; - $file = false; // use local copy if available on socket connect error - } - - if (!$file) { - $ret = $this->getCache($url); - if (!PEAR::isError($ret) && $trieddownload) { - // reset the age of the cache if the server says it was unmodified - $result = $this->saveCache($url, $ret, null, true, $cacheId); - if (PEAR::isError($result)) { - return PEAR::raiseError($result->getMessage()); - } - } - - return $ret; - } - - if (is_array($file)) { - $headers = $file[2]; - $lastmodified = $file[1]; - $content = $file[0]; - } else { - $headers = array(); - $lastmodified = false; - $content = $file; - } - - if ($forcestring) { - $result = $this->saveCache($url, $content, $lastmodified, false, $cacheId); - if (PEAR::isError($result)) { - return PEAR::raiseError($result->getMessage()); - } - - return $content; - } - - if (isset($headers['content-type'])) { - switch ($headers['content-type']) { - case 'text/xml' : - case 'application/xml' : - case 'text/plain' : - if ($headers['content-type'] === 'text/plain') { - $check = substr($content, 0, 5); - if ($check !== 'parse($content); - PEAR::popErrorHandling(); - if (PEAR::isError($err)) { - return PEAR::raiseError('Invalid xml downloaded from "' . $url . '": ' . - $err->getMessage()); - } - $content = $parser->getData(); - case 'text/html' : - default : - // use it as a string - } - } else { - // assume XML - $parser = new PEAR_XMLParser; - $parser->parse($content); - $content = $parser->getData(); - } - - $result = $this->saveCache($url, $content, $lastmodified, false, $cacheId); - if (PEAR::isError($result)) { - return PEAR::raiseError($result->getMessage()); - } - - return $content; - } - - function useLocalCache($url, $cacheid = null) - { - if ($cacheid === null) { - $cacheidfile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR . - md5($url) . 'rest.cacheid'; - if (!file_exists($cacheidfile)) { - return false; - } - - $cacheid = unserialize(implode('', file($cacheidfile))); - } - - $cachettl = $this->config->get('cache_ttl'); - // If cache is newer than $cachettl seconds, we use the cache! - if (time() - $cacheid['age'] < $cachettl) { - return $this->getCache($url); - } - - return false; - } - - function getCacheId($url) - { - $cacheidfile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR . - md5($url) . 'rest.cacheid'; - - if (!file_exists($cacheidfile)) { - return false; - } - - $ret = unserialize(implode('', file($cacheidfile))); - return $ret; - } - - function getCache($url) - { - $cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR . - md5($url) . 'rest.cachefile'; - - if (!file_exists($cachefile)) { - return PEAR::raiseError('No cached content available for "' . $url . '"'); - } - - return unserialize(implode('', file($cachefile))); - } - - /** - * @param string full URL to REST resource - * @param string original contents of the REST resource - * @param array HTTP Last-Modified and ETag headers - * @param bool if true, then the cache id file should be regenerated to - * trigger a new time-to-live value - */ - function saveCache($url, $contents, $lastmodified, $nochange = false, $cacheid = null) - { - $cache_dir = $this->config->get('cache_dir'); - $d = $cache_dir . DIRECTORY_SEPARATOR . md5($url); - $cacheidfile = $d . 'rest.cacheid'; - $cachefile = $d . 'rest.cachefile'; - - if (!is_dir($cache_dir)) { - if (System::mkdir(array('-p', $cache_dir)) === false) { - return PEAR::raiseError("The value of config option cache_dir ($cache_dir) is not a directory and attempts to create the directory failed."); - } - } - - if ($cacheid === null && $nochange) { - $cacheid = unserialize(implode('', file($cacheidfile))); - } - - $idData = serialize(array( - 'age' => time(), - 'lastChange' => ($nochange ? $cacheid['lastChange'] : $lastmodified), - )); - - $result = $this->saveCacheFile($cacheidfile, $idData); - if (PEAR::isError($result)) { - return $result; - } elseif ($nochange) { - return true; - } - - $result = $this->saveCacheFile($cachefile, serialize($contents)); - if (PEAR::isError($result)) { - if (file_exists($cacheidfile)) { - @unlink($cacheidfile); - } - - return $result; - } - - return true; - } - - function saveCacheFile($file, $contents) - { - $len = strlen($contents); - - $cachefile_fp = @fopen($file, 'xb'); // x is the O_CREAT|O_EXCL mode - if ($cachefile_fp !== false) { // create file - if (fwrite($cachefile_fp, $contents, $len) < $len) { - fclose($cachefile_fp); - return PEAR::raiseError("Could not write $file."); - } - } else { // update file - $cachefile_lstat = lstat($file); - $cachefile_fp = @fopen($file, 'wb'); - if (!$cachefile_fp) { - return PEAR::raiseError("Could not open $file for writing."); - } - - $cachefile_fstat = fstat($cachefile_fp); - if ( - $cachefile_lstat['mode'] == $cachefile_fstat['mode'] && - $cachefile_lstat['ino'] == $cachefile_fstat['ino'] && - $cachefile_lstat['dev'] == $cachefile_fstat['dev'] && - $cachefile_fstat['nlink'] === 1 - ) { - if (fwrite($cachefile_fp, $contents, $len) < $len) { - fclose($cachefile_fp); - return PEAR::raiseError("Could not write $file."); - } - } else { - fclose($cachefile_fp); - $link = function_exists('readlink') ? readlink($file) : $file; - return PEAR::raiseError('SECURITY ERROR: Will not write to ' . $file . ' as it is symlinked to ' . $link . ' - Possible symlink attack'); - } - } - - fclose($cachefile_fp); - return true; - } - - /** - * Efficiently Download a file through HTTP. Returns downloaded file as a string in-memory - * This is best used for small files - * - * If an HTTP proxy has been configured (http_proxy PEAR_Config - * setting), the proxy will be used. - * - * @param string $url the URL to download - * @param string $save_dir directory to save file in - * @param false|string|array $lastmodified header values to check against for caching - * use false to return the header values from this download - * @param false|array $accept Accept headers to send - * @return string|array Returns the contents of the downloaded file or a PEAR - * error on failure. If the error is caused by - * socket-related errors, the error object will - * have the fsockopen error code available through - * getCode(). If caching is requested, then return the header - * values. - * - * @access public - */ - function downloadHttp($url, $lastmodified = null, $accept = false, $channel = false) - { - static $redirect = 0; - // always reset , so we are clean case of error - $wasredirect = $redirect; - $redirect = 0; - - $info = parse_url($url); - if (!isset($info['scheme']) || !in_array($info['scheme'], array('http', 'https'))) { - return PEAR::raiseError('Cannot download non-http URL "' . $url . '"'); - } - - if (!isset($info['host'])) { - return PEAR::raiseError('Cannot download from non-URL "' . $url . '"'); - } - - $host = isset($info['host']) ? $info['host'] : null; - $port = isset($info['port']) ? $info['port'] : null; - $path = isset($info['path']) ? $info['path'] : null; - $schema = (isset($info['scheme']) && $info['scheme'] == 'https') ? 'https' : 'http'; - - $proxy_host = $proxy_port = $proxy_user = $proxy_pass = ''; - if ($this->config->get('http_proxy')&& - $proxy = parse_url($this->config->get('http_proxy')) - ) { - $proxy_host = isset($proxy['host']) ? $proxy['host'] : null; - if ($schema === 'https') { - $proxy_host = 'ssl://' . $proxy_host; - } - - $proxy_port = isset($proxy['port']) ? $proxy['port'] : 8080; - $proxy_user = isset($proxy['user']) ? urldecode($proxy['user']) : null; - $proxy_pass = isset($proxy['pass']) ? urldecode($proxy['pass']) : null; - $proxy_schema = (isset($proxy['scheme']) && $proxy['scheme'] == 'https') ? 'https' : 'http'; - } - - if (empty($port)) { - $port = (isset($info['scheme']) && $info['scheme'] == 'https') ? 443 : 80; - } - - if (isset($proxy['host'])) { - $request = "GET $url HTTP/1.1\r\n"; - } else { - $request = "GET $path HTTP/1.1\r\n"; - } - - $request .= "Host: $host\r\n"; - $ifmodifiedsince = ''; - if (is_array($lastmodified)) { - if (isset($lastmodified['Last-Modified'])) { - $ifmodifiedsince = 'If-Modified-Since: ' . $lastmodified['Last-Modified'] . "\r\n"; - } - - if (isset($lastmodified['ETag'])) { - $ifmodifiedsince .= "If-None-Match: $lastmodified[ETag]\r\n"; - } - } else { - $ifmodifiedsince = ($lastmodified ? "If-Modified-Since: $lastmodified\r\n" : ''); - } - - $request .= $ifmodifiedsince . - "User-Agent: PEAR/1.9.4/PHP/" . PHP_VERSION . "\r\n"; - - $username = $this->config->get('username', null, $channel); - $password = $this->config->get('password', null, $channel); - - if ($username && $password) { - $tmp = base64_encode("$username:$password"); - $request .= "Authorization: Basic $tmp\r\n"; - } - - if ($proxy_host != '' && $proxy_user != '') { - $request .= 'Proxy-Authorization: Basic ' . - base64_encode($proxy_user . ':' . $proxy_pass) . "\r\n"; - } - - if ($accept) { - $request .= 'Accept: ' . implode(', ', $accept) . "\r\n"; - } - - $request .= "Accept-Encoding:\r\n"; - $request .= "Connection: close\r\n"; - $request .= "\r\n"; - - if ($proxy_host != '') { - $fp = @fsockopen($proxy_host, $proxy_port, $errno, $errstr, 15); - if (!$fp) { - return PEAR::raiseError("Connection to `$proxy_host:$proxy_port' failed: $errstr", -9276); - } - } else { - if ($schema === 'https') { - $host = 'ssl://' . $host; - } - - $fp = @fsockopen($host, $port, $errno, $errstr); - if (!$fp) { - return PEAR::raiseError("Connection to `$host:$port' failed: $errstr", $errno); - } - } - - fwrite($fp, $request); - - $headers = array(); - $reply = 0; - while ($line = trim(fgets($fp, 1024))) { - if (preg_match('/^([^:]+):\s+(.*)\s*\\z/', $line, $matches)) { - $headers[strtolower($matches[1])] = trim($matches[2]); - } elseif (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) { - $reply = (int)$matches[1]; - if ($reply == 304 && ($lastmodified || ($lastmodified === false))) { - return false; - } - - if (!in_array($reply, array(200, 301, 302, 303, 305, 307))) { - return PEAR::raiseError("File $schema://$host:$port$path not valid (received: $line)"); - } - } - } - - if ($reply != 200) { - if (!isset($headers['location'])) { - return PEAR::raiseError("File $schema://$host:$port$path not valid (redirected but no location)"); - } - - if ($wasredirect > 4) { - return PEAR::raiseError("File $schema://$host:$port$path not valid (redirection looped more than 5 times)"); - } - - $redirect = $wasredirect + 1; - return $this->downloadHttp($headers['location'], $lastmodified, $accept, $channel); - } - - $length = isset($headers['content-length']) ? $headers['content-length'] : -1; - - $data = ''; - while ($chunk = @fread($fp, 8192)) { - $data .= $chunk; - } - fclose($fp); - - if ($lastmodified === false || $lastmodified) { - if (isset($headers['etag'])) { - $lastmodified = array('ETag' => $headers['etag']); - } - - if (isset($headers['last-modified'])) { - if (is_array($lastmodified)) { - $lastmodified['Last-Modified'] = $headers['last-modified']; - } else { - $lastmodified = $headers['last-modified']; - } - } - - return array($data, $lastmodified, $headers); - } - - return $data; - } -} \ No newline at end of file diff --git a/pear/PEAR/REST/10.php b/pear/PEAR/REST/10.php deleted file mode 100644 index 7540efa..0000000 --- a/pear/PEAR/REST/10.php +++ /dev/null @@ -1,871 +0,0 @@ - - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a12 - */ - -/** - * For downloading REST xml/txt files - */ -require_once 'PEAR/REST.php'; - -/** - * Implement REST 1.0 - * - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a12 - */ -class PEAR_REST_10 -{ - /** - * @var PEAR_REST - */ - var $_rest; - function PEAR_REST_10($config, $options = array()) - { - $this->_rest = &new PEAR_REST($config, $options); - } - - /** - * Retrieve information about a remote package to be downloaded from a REST server - * - * @param string $base The uri to prepend to all REST calls - * @param array $packageinfo an array of format: - *
    -     *  array(
    -     *   'package' => 'packagename',
    -     *   'channel' => 'channelname',
    -     *  ['state' => 'alpha' (or valid state),]
    -     *  -or-
    -     *  ['version' => '1.whatever']
    -     * 
    - * @param string $prefstate Current preferred_state config variable value - * @param bool $installed the installed version of this package to compare against - * @return array|false|PEAR_Error see {@link _returnDownloadURL()} - */ - function getDownloadURL($base, $packageinfo, $prefstate, $installed, $channel = false) - { - $states = $this->betterStates($prefstate, true); - if (!$states) { - return PEAR::raiseError('"' . $prefstate . '" is not a valid state'); - } - - $channel = $packageinfo['channel']; - $package = $packageinfo['package']; - $state = isset($packageinfo['state']) ? $packageinfo['state'] : null; - $version = isset($packageinfo['version']) ? $packageinfo['version'] : null; - $restFile = $base . 'r/' . strtolower($package) . '/allreleases.xml'; - - $info = $this->_rest->retrieveData($restFile, false, false, $channel); - if (PEAR::isError($info)) { - return PEAR::raiseError('No releases available for package "' . - $channel . '/' . $package . '"'); - } - - if (!isset($info['r'])) { - return false; - } - - $release = $found = false; - if (!is_array($info['r']) || !isset($info['r'][0])) { - $info['r'] = array($info['r']); - } - - foreach ($info['r'] as $release) { - if (!isset($this->_rest->_options['force']) && ($installed && - version_compare($release['v'], $installed, '<'))) { - continue; - } - - if (isset($state)) { - // try our preferred state first - if ($release['s'] == $state) { - $found = true; - break; - } - // see if there is something newer and more stable - // bug #7221 - if (in_array($release['s'], $this->betterStates($state), true)) { - $found = true; - break; - } - } elseif (isset($version)) { - if ($release['v'] == $version) { - $found = true; - break; - } - } else { - if (in_array($release['s'], $states)) { - $found = true; - break; - } - } - } - - return $this->_returnDownloadURL($base, $package, $release, $info, $found, false, $channel); - } - - function getDepDownloadURL($base, $xsdversion, $dependency, $deppackage, - $prefstate = 'stable', $installed = false, $channel = false) - { - $states = $this->betterStates($prefstate, true); - if (!$states) { - return PEAR::raiseError('"' . $prefstate . '" is not a valid state'); - } - - $channel = $dependency['channel']; - $package = $dependency['name']; - $state = isset($dependency['state']) ? $dependency['state'] : null; - $version = isset($dependency['version']) ? $dependency['version'] : null; - $restFile = $base . 'r/' . strtolower($package) . '/allreleases.xml'; - - $info = $this->_rest->retrieveData($restFile, false, false, $channel); - if (PEAR::isError($info)) { - return PEAR::raiseError('Package "' . $deppackage['channel'] . '/' . $deppackage['package'] - . '" dependency "' . $channel . '/' . $package . '" has no releases'); - } - - if (!is_array($info) || !isset($info['r'])) { - return false; - } - - $exclude = array(); - $min = $max = $recommended = false; - if ($xsdversion == '1.0') { - switch ($dependency['rel']) { - case 'ge' : - $min = $dependency['version']; - break; - case 'gt' : - $min = $dependency['version']; - $exclude = array($dependency['version']); - break; - case 'eq' : - $recommended = $dependency['version']; - break; - case 'lt' : - $max = $dependency['version']; - $exclude = array($dependency['version']); - break; - case 'le' : - $max = $dependency['version']; - break; - case 'ne' : - $exclude = array($dependency['version']); - break; - } - } else { - $min = isset($dependency['min']) ? $dependency['min'] : false; - $max = isset($dependency['max']) ? $dependency['max'] : false; - $recommended = isset($dependency['recommended']) ? - $dependency['recommended'] : false; - if (isset($dependency['exclude'])) { - if (!isset($dependency['exclude'][0])) { - $exclude = array($dependency['exclude']); - } - } - } - $release = $found = false; - if (!is_array($info['r']) || !isset($info['r'][0])) { - $info['r'] = array($info['r']); - } - foreach ($info['r'] as $release) { - if (!isset($this->_rest->_options['force']) && ($installed && - version_compare($release['v'], $installed, '<'))) { - continue; - } - if (in_array($release['v'], $exclude)) { // skip excluded versions - continue; - } - // allow newer releases to say "I'm OK with the dependent package" - if ($xsdversion == '2.0' && isset($release['co'])) { - if (!is_array($release['co']) || !isset($release['co'][0])) { - $release['co'] = array($release['co']); - } - foreach ($release['co'] as $entry) { - if (isset($entry['x']) && !is_array($entry['x'])) { - $entry['x'] = array($entry['x']); - } elseif (!isset($entry['x'])) { - $entry['x'] = array(); - } - if ($entry['c'] == $deppackage['channel'] && - strtolower($entry['p']) == strtolower($deppackage['package']) && - version_compare($deppackage['version'], $entry['min'], '>=') && - version_compare($deppackage['version'], $entry['max'], '<=') && - !in_array($release['v'], $entry['x'])) { - $recommended = $release['v']; - break; - } - } - } - if ($recommended) { - if ($release['v'] != $recommended) { // if we want a specific - // version, then skip all others - continue; - } else { - if (!in_array($release['s'], $states)) { - // the stability is too low, but we must return the - // recommended version if possible - return $this->_returnDownloadURL($base, $package, $release, $info, true, false, $channel); - } - } - } - if ($min && version_compare($release['v'], $min, 'lt')) { // skip too old versions - continue; - } - if ($max && version_compare($release['v'], $max, 'gt')) { // skip too new versions - continue; - } - if ($installed && version_compare($release['v'], $installed, '<')) { - continue; - } - if (in_array($release['s'], $states)) { // if in the preferred state... - $found = true; // ... then use it - break; - } - } - return $this->_returnDownloadURL($base, $package, $release, $info, $found, false, $channel); - } - - /** - * Take raw data and return the array needed for processing a download URL - * - * @param string $base REST base uri - * @param string $package Package name - * @param array $release an array of format array('v' => version, 's' => state) - * describing the release to download - * @param array $info list of all releases as defined by allreleases.xml - * @param bool|null $found determines whether the release was found or this is the next - * best alternative. If null, then versions were skipped because - * of PHP dependency - * @return array|PEAR_Error - * @access private - */ - function _returnDownloadURL($base, $package, $release, $info, $found, $phpversion = false, $channel = false) - { - if (!$found) { - $release = $info['r'][0]; - } - - $packageLower = strtolower($package); - $pinfo = $this->_rest->retrieveCacheFirst($base . 'p/' . $packageLower . '/' . - 'info.xml', false, false, $channel); - if (PEAR::isError($pinfo)) { - return PEAR::raiseError('Package "' . $package . - '" does not have REST info xml available'); - } - - $releaseinfo = $this->_rest->retrieveCacheFirst($base . 'r/' . $packageLower . '/' . - $release['v'] . '.xml', false, false, $channel); - if (PEAR::isError($releaseinfo)) { - return PEAR::raiseError('Package "' . $package . '" Version "' . $release['v'] . - '" does not have REST xml available'); - } - - $packagexml = $this->_rest->retrieveCacheFirst($base . 'r/' . $packageLower . '/' . - 'deps.' . $release['v'] . '.txt', false, true, $channel); - if (PEAR::isError($packagexml)) { - return PEAR::raiseError('Package "' . $package . '" Version "' . $release['v'] . - '" does not have REST dependency information available'); - } - - $packagexml = unserialize($packagexml); - if (!$packagexml) { - $packagexml = array(); - } - - $allinfo = $this->_rest->retrieveData($base . 'r/' . $packageLower . - '/allreleases.xml', false, false, $channel); - if (PEAR::isError($allinfo)) { - return $allinfo; - } - - if (!is_array($allinfo['r']) || !isset($allinfo['r'][0])) { - $allinfo['r'] = array($allinfo['r']); - } - - $compatible = false; - foreach ($allinfo['r'] as $release) { - if ($release['v'] != $releaseinfo['v']) { - continue; - } - - if (!isset($release['co'])) { - break; - } - - $compatible = array(); - if (!is_array($release['co']) || !isset($release['co'][0])) { - $release['co'] = array($release['co']); - } - - foreach ($release['co'] as $entry) { - $comp = array(); - $comp['name'] = $entry['p']; - $comp['channel'] = $entry['c']; - $comp['min'] = $entry['min']; - $comp['max'] = $entry['max']; - if (isset($entry['x']) && !is_array($entry['x'])) { - $comp['exclude'] = $entry['x']; - } - - $compatible[] = $comp; - } - - if (count($compatible) == 1) { - $compatible = $compatible[0]; - } - - break; - } - - $deprecated = false; - if (isset($pinfo['dc']) && isset($pinfo['dp'])) { - if (is_array($pinfo['dp'])) { - $deprecated = array('channel' => (string) $pinfo['dc'], - 'package' => trim($pinfo['dp']['_content'])); - } else { - $deprecated = array('channel' => (string) $pinfo['dc'], - 'package' => trim($pinfo['dp'])); - } - } - - $return = array( - 'version' => $releaseinfo['v'], - 'info' => $packagexml, - 'package' => $releaseinfo['p']['_content'], - 'stability' => $releaseinfo['st'], - 'compatible' => $compatible, - 'deprecated' => $deprecated, - ); - - if ($found) { - $return['url'] = $releaseinfo['g']; - return $return; - } - - $return['php'] = $phpversion; - return $return; - } - - function listPackages($base, $channel = false) - { - $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel); - if (PEAR::isError($packagelist)) { - return $packagelist; - } - - if (!is_array($packagelist) || !isset($packagelist['p'])) { - return array(); - } - - if (!is_array($packagelist['p'])) { - $packagelist['p'] = array($packagelist['p']); - } - - return $packagelist['p']; - } - - /** - * List all categories of a REST server - * - * @param string $base base URL of the server - * @return array of categorynames - */ - function listCategories($base, $channel = false) - { - $categories = array(); - - // c/categories.xml does not exist; - // check for every package its category manually - // This is SLOOOWWWW : /// - $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel); - if (PEAR::isError($packagelist)) { - return $packagelist; - } - - if (!is_array($packagelist) || !isset($packagelist['p'])) { - $ret = array(); - return $ret; - } - - if (!is_array($packagelist['p'])) { - $packagelist['p'] = array($packagelist['p']); - } - - PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - foreach ($packagelist['p'] as $package) { - $inf = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml', false, false, $channel); - if (PEAR::isError($inf)) { - PEAR::popErrorHandling(); - return $inf; - } - $cat = $inf['ca']['_content']; - if (!isset($categories[$cat])) { - $categories[$cat] = $inf['ca']; - } - } - - return array_values($categories); - } - - /** - * List a category of a REST server - * - * @param string $base base URL of the server - * @param string $category name of the category - * @param boolean $info also download full package info - * @return array of packagenames - */ - function listCategory($base, $category, $info = false, $channel = false) - { - // gives '404 Not Found' error when category doesn't exist - $packagelist = $this->_rest->retrieveData($base.'c/'.urlencode($category).'/packages.xml', false, false, $channel); - if (PEAR::isError($packagelist)) { - return $packagelist; - } - - if (!is_array($packagelist) || !isset($packagelist['p'])) { - return array(); - } - - if (!is_array($packagelist['p']) || - !isset($packagelist['p'][0])) { // only 1 pkg - $packagelist = array($packagelist['p']); - } else { - $packagelist = $packagelist['p']; - } - - if ($info == true) { - // get individual package info - PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - foreach ($packagelist as $i => $packageitem) { - $url = sprintf('%s'.'r/%s/latest.txt', - $base, - strtolower($packageitem['_content'])); - $version = $this->_rest->retrieveData($url, false, false, $channel); - if (PEAR::isError($version)) { - break; // skipit - } - $url = sprintf('%s'.'r/%s/%s.xml', - $base, - strtolower($packageitem['_content']), - $version); - $info = $this->_rest->retrieveData($url, false, false, $channel); - if (PEAR::isError($info)) { - break; // skipit - } - $packagelist[$i]['info'] = $info; - } - PEAR::popErrorHandling(); - } - - return $packagelist; - } - - - function listAll($base, $dostable, $basic = true, $searchpackage = false, $searchsummary = false, $channel = false) - { - $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel); - if (PEAR::isError($packagelist)) { - return $packagelist; - } - if ($this->_rest->config->get('verbose') > 0) { - $ui = &PEAR_Frontend::singleton(); - $ui->log('Retrieving data...0%', true); - } - $ret = array(); - if (!is_array($packagelist) || !isset($packagelist['p'])) { - return $ret; - } - if (!is_array($packagelist['p'])) { - $packagelist['p'] = array($packagelist['p']); - } - - // only search-packagename = quicksearch ! - if ($searchpackage && (!$searchsummary || empty($searchpackage))) { - $newpackagelist = array(); - foreach ($packagelist['p'] as $package) { - if (!empty($searchpackage) && stristr($package, $searchpackage) !== false) { - $newpackagelist[] = $package; - } - } - $packagelist['p'] = $newpackagelist; - } - PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - $next = .1; - foreach ($packagelist['p'] as $progress => $package) { - if ($this->_rest->config->get('verbose') > 0) { - if ($progress / count($packagelist['p']) >= $next) { - if ($next == .5) { - $ui->log('50%', false); - } else { - $ui->log('.', false); - } - $next += .1; - } - } - - if ($basic) { // remote-list command - if ($dostable) { - $latest = $this->_rest->retrieveData($base . 'r/' . strtolower($package) . - '/stable.txt', false, false, $channel); - } else { - $latest = $this->_rest->retrieveData($base . 'r/' . strtolower($package) . - '/latest.txt', false, false, $channel); - } - if (PEAR::isError($latest)) { - $latest = false; - } - $info = array('stable' => $latest); - } else { // list-all command - $inf = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml', false, false, $channel); - if (PEAR::isError($inf)) { - PEAR::popErrorHandling(); - return $inf; - } - if ($searchpackage) { - $found = (!empty($searchpackage) && stristr($package, $searchpackage) !== false); - if (!$found && !(isset($searchsummary) && !empty($searchsummary) - && (stristr($inf['s'], $searchsummary) !== false - || stristr($inf['d'], $searchsummary) !== false))) - { - continue; - }; - } - $releases = $this->_rest->retrieveData($base . 'r/' . strtolower($package) . - '/allreleases.xml', false, false, $channel); - if (PEAR::isError($releases)) { - continue; - } - if (!isset($releases['r'][0])) { - $releases['r'] = array($releases['r']); - } - unset($latest); - unset($unstable); - unset($stable); - unset($state); - foreach ($releases['r'] as $release) { - if (!isset($latest)) { - if ($dostable && $release['s'] == 'stable') { - $latest = $release['v']; - $state = 'stable'; - } - if (!$dostable) { - $latest = $release['v']; - $state = $release['s']; - } - } - if (!isset($stable) && $release['s'] == 'stable') { - $stable = $release['v']; - if (!isset($unstable)) { - $unstable = $stable; - } - } - if (!isset($unstable) && $release['s'] != 'stable') { - $latest = $unstable = $release['v']; - $state = $release['s']; - } - if (isset($latest) && !isset($state)) { - $state = $release['s']; - } - if (isset($latest) && isset($stable) && isset($unstable)) { - break; - } - } - $deps = array(); - if (!isset($unstable)) { - $unstable = false; - $state = 'stable'; - if (isset($stable)) { - $latest = $unstable = $stable; - } - } else { - $latest = $unstable; - } - if (!isset($latest)) { - $latest = false; - } - if ($latest) { - $d = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/deps.' . - $latest . '.txt', false, false, $channel); - if (!PEAR::isError($d)) { - $d = unserialize($d); - if ($d) { - if (isset($d['required'])) { - if (!class_exists('PEAR_PackageFile_v2')) { - require_once 'PEAR/PackageFile/v2.php'; - } - if (!isset($pf)) { - $pf = new PEAR_PackageFile_v2; - } - $pf->setDeps($d); - $tdeps = $pf->getDeps(); - } else { - $tdeps = $d; - } - foreach ($tdeps as $dep) { - if ($dep['type'] !== 'pkg') { - continue; - } - $deps[] = $dep; - } - } - } - } - if (!isset($stable)) { - $stable = '-n/a-'; - } - if (!$searchpackage) { - $info = array('stable' => $latest, 'summary' => $inf['s'], 'description' => - $inf['d'], 'deps' => $deps, 'category' => $inf['ca']['_content'], - 'unstable' => $unstable, 'state' => $state); - } else { - $info = array('stable' => $stable, 'summary' => $inf['s'], 'description' => - $inf['d'], 'deps' => $deps, 'category' => $inf['ca']['_content'], - 'unstable' => $unstable, 'state' => $state); - } - } - $ret[$package] = $info; - } - PEAR::popErrorHandling(); - return $ret; - } - - function listLatestUpgrades($base, $pref_state, $installed, $channel, &$reg) - { - $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel); - if (PEAR::isError($packagelist)) { - return $packagelist; - } - - $ret = array(); - if (!is_array($packagelist) || !isset($packagelist['p'])) { - return $ret; - } - - if (!is_array($packagelist['p'])) { - $packagelist['p'] = array($packagelist['p']); - } - - foreach ($packagelist['p'] as $package) { - if (!isset($installed[strtolower($package)])) { - continue; - } - - $inst_version = $reg->packageInfo($package, 'version', $channel); - $inst_state = $reg->packageInfo($package, 'release_state', $channel); - PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - $info = $this->_rest->retrieveData($base . 'r/' . strtolower($package) . - '/allreleases.xml', false, false, $channel); - PEAR::popErrorHandling(); - if (PEAR::isError($info)) { - continue; // no remote releases - } - - if (!isset($info['r'])) { - continue; - } - - $release = $found = false; - if (!is_array($info['r']) || !isset($info['r'][0])) { - $info['r'] = array($info['r']); - } - - // $info['r'] is sorted by version number - usort($info['r'], array($this, '_sortReleasesByVersionNumber')); - foreach ($info['r'] as $release) { - if ($inst_version && version_compare($release['v'], $inst_version, '<=')) { - // not newer than the one installed - break; - } - - // new version > installed version - if (!$pref_state) { - // every state is a good state - $found = true; - break; - } else { - $new_state = $release['s']; - // if new state >= installed state: go - if (in_array($new_state, $this->betterStates($inst_state, true))) { - $found = true; - break; - } else { - // only allow to lower the state of package, - // if new state >= preferred state: go - if (in_array($new_state, $this->betterStates($pref_state, true))) { - $found = true; - break; - } - } - } - } - - if (!$found) { - continue; - } - - $relinfo = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/' . - $release['v'] . '.xml', false, false, $channel); - if (PEAR::isError($relinfo)) { - return $relinfo; - } - - $ret[$package] = array( - 'version' => $release['v'], - 'state' => $release['s'], - 'filesize' => $relinfo['f'], - ); - } - - return $ret; - } - - function packageInfo($base, $package, $channel = false) - { - PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - $pinfo = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml', false, false, $channel); - if (PEAR::isError($pinfo)) { - PEAR::popErrorHandling(); - return PEAR::raiseError('Unknown package: "' . $package . '" in channel "' . $channel . '"' . "\n". 'Debug: ' . - $pinfo->getMessage()); - } - - $releases = array(); - $allreleases = $this->_rest->retrieveData($base . 'r/' . strtolower($package) . - '/allreleases.xml', false, false, $channel); - if (!PEAR::isError($allreleases)) { - if (!class_exists('PEAR_PackageFile_v2')) { - require_once 'PEAR/PackageFile/v2.php'; - } - - if (!is_array($allreleases['r']) || !isset($allreleases['r'][0])) { - $allreleases['r'] = array($allreleases['r']); - } - - $pf = new PEAR_PackageFile_v2; - foreach ($allreleases['r'] as $release) { - $ds = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/deps.' . - $release['v'] . '.txt', false, false, $channel); - if (PEAR::isError($ds)) { - continue; - } - - if (!isset($latest)) { - $latest = $release['v']; - } - - $pf->setDeps(unserialize($ds)); - $ds = $pf->getDeps(); - $info = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) - . '/' . $release['v'] . '.xml', false, false, $channel); - - if (PEAR::isError($info)) { - continue; - } - - $releases[$release['v']] = array( - 'doneby' => $info['m'], - 'license' => $info['l'], - 'summary' => $info['s'], - 'description' => $info['d'], - 'releasedate' => $info['da'], - 'releasenotes' => $info['n'], - 'state' => $release['s'], - 'deps' => $ds ? $ds : array(), - ); - } - } else { - $latest = ''; - } - - PEAR::popErrorHandling(); - if (isset($pinfo['dc']) && isset($pinfo['dp'])) { - if (is_array($pinfo['dp'])) { - $deprecated = array('channel' => (string) $pinfo['dc'], - 'package' => trim($pinfo['dp']['_content'])); - } else { - $deprecated = array('channel' => (string) $pinfo['dc'], - 'package' => trim($pinfo['dp'])); - } - } else { - $deprecated = false; - } - - if (!isset($latest)) { - $latest = ''; - } - - return array( - 'name' => $pinfo['n'], - 'channel' => $pinfo['c'], - 'category' => $pinfo['ca']['_content'], - 'stable' => $latest, - 'license' => $pinfo['l'], - 'summary' => $pinfo['s'], - 'description' => $pinfo['d'], - 'releases' => $releases, - 'deprecated' => $deprecated, - ); - } - - /** - * Return an array containing all of the states that are more stable than - * or equal to the passed in state - * - * @param string Release state - * @param boolean Determines whether to include $state in the list - * @return false|array False if $state is not a valid release state - */ - function betterStates($state, $include = false) - { - static $states = array('snapshot', 'devel', 'alpha', 'beta', 'stable'); - $i = array_search($state, $states); - if ($i === false) { - return false; - } - - if ($include) { - $i--; - } - - return array_slice($states, $i + 1); - } - - /** - * Sort releases by version number - * - * @access private - */ - function _sortReleasesByVersionNumber($a, $b) - { - if (version_compare($a['v'], $b['v'], '=')) { - return 0; - } - - if (version_compare($a['v'], $b['v'], '>')) { - return -1; - } - - if (version_compare($a['v'], $b['v'], '<')) { - return 1; - } - } -} \ No newline at end of file diff --git a/pear/PEAR/REST/11.php b/pear/PEAR/REST/11.php deleted file mode 100644 index 6d2fcd8..0000000 --- a/pear/PEAR/REST/11.php +++ /dev/null @@ -1,341 +0,0 @@ - - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.3 - */ - -/** - * For downloading REST xml/txt files - */ -require_once 'PEAR/REST.php'; - -/** - * Implement REST 1.1 - * - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.3 - */ -class PEAR_REST_11 -{ - /** - * @var PEAR_REST - */ - var $_rest; - - function PEAR_REST_11($config, $options = array()) - { - $this->_rest = &new PEAR_REST($config, $options); - } - - function listAll($base, $dostable, $basic = true, $searchpackage = false, $searchsummary = false, $channel = false) - { - $categorylist = $this->_rest->retrieveData($base . 'c/categories.xml', false, false, $channel); - if (PEAR::isError($categorylist)) { - return $categorylist; - } - - $ret = array(); - if (!is_array($categorylist['c']) || !isset($categorylist['c'][0])) { - $categorylist['c'] = array($categorylist['c']); - } - - PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - - foreach ($categorylist['c'] as $progress => $category) { - $category = $category['_content']; - $packagesinfo = $this->_rest->retrieveData($base . - 'c/' . urlencode($category) . '/packagesinfo.xml', false, false, $channel); - - if (PEAR::isError($packagesinfo)) { - continue; - } - - if (!is_array($packagesinfo) || !isset($packagesinfo['pi'])) { - continue; - } - - if (!is_array($packagesinfo['pi']) || !isset($packagesinfo['pi'][0])) { - $packagesinfo['pi'] = array($packagesinfo['pi']); - } - - foreach ($packagesinfo['pi'] as $packageinfo) { - if (empty($packageinfo)) { - continue; - } - - $info = $packageinfo['p']; - $package = $info['n']; - $releases = isset($packageinfo['a']) ? $packageinfo['a'] : false; - unset($latest); - unset($unstable); - unset($stable); - unset($state); - - if ($releases) { - if (!isset($releases['r'][0])) { - $releases['r'] = array($releases['r']); - } - - foreach ($releases['r'] as $release) { - if (!isset($latest)) { - if ($dostable && $release['s'] == 'stable') { - $latest = $release['v']; - $state = 'stable'; - } - if (!$dostable) { - $latest = $release['v']; - $state = $release['s']; - } - } - - if (!isset($stable) && $release['s'] == 'stable') { - $stable = $release['v']; - if (!isset($unstable)) { - $unstable = $stable; - } - } - - if (!isset($unstable) && $release['s'] != 'stable') { - $unstable = $release['v']; - $state = $release['s']; - } - - if (isset($latest) && !isset($state)) { - $state = $release['s']; - } - - if (isset($latest) && isset($stable) && isset($unstable)) { - break; - } - } - } - - if ($basic) { // remote-list command - if (!isset($latest)) { - $latest = false; - } - - if ($dostable) { - // $state is not set if there are no releases - if (isset($state) && $state == 'stable') { - $ret[$package] = array('stable' => $latest); - } else { - $ret[$package] = array('stable' => '-n/a-'); - } - } else { - $ret[$package] = array('stable' => $latest); - } - - continue; - } - - // list-all command - if (!isset($unstable)) { - $unstable = false; - $state = 'stable'; - if (isset($stable)) { - $latest = $unstable = $stable; - } - } else { - $latest = $unstable; - } - - if (!isset($latest)) { - $latest = false; - } - - $deps = array(); - if ($latest && isset($packageinfo['deps'])) { - if (!is_array($packageinfo['deps']) || - !isset($packageinfo['deps'][0]) - ) { - $packageinfo['deps'] = array($packageinfo['deps']); - } - - $d = false; - foreach ($packageinfo['deps'] as $dep) { - if ($dep['v'] == $latest) { - $d = unserialize($dep['d']); - } - } - - if ($d) { - if (isset($d['required'])) { - if (!class_exists('PEAR_PackageFile_v2')) { - require_once 'PEAR/PackageFile/v2.php'; - } - - if (!isset($pf)) { - $pf = new PEAR_PackageFile_v2; - } - - $pf->setDeps($d); - $tdeps = $pf->getDeps(); - } else { - $tdeps = $d; - } - - foreach ($tdeps as $dep) { - if ($dep['type'] !== 'pkg') { - continue; - } - - $deps[] = $dep; - } - } - } - - $info = array( - 'stable' => $latest, - 'summary' => $info['s'], - 'description' => $info['d'], - 'deps' => $deps, - 'category' => $info['ca']['_content'], - 'unstable' => $unstable, - 'state' => $state - ); - $ret[$package] = $info; - } - } - - PEAR::popErrorHandling(); - return $ret; - } - - /** - * List all categories of a REST server - * - * @param string $base base URL of the server - * @return array of categorynames - */ - function listCategories($base, $channel = false) - { - $categorylist = $this->_rest->retrieveData($base . 'c/categories.xml', false, false, $channel); - if (PEAR::isError($categorylist)) { - return $categorylist; - } - - if (!is_array($categorylist) || !isset($categorylist['c'])) { - return array(); - } - - if (isset($categorylist['c']['_content'])) { - // only 1 category - $categorylist['c'] = array($categorylist['c']); - } - - return $categorylist['c']; - } - - /** - * List packages in a category of a REST server - * - * @param string $base base URL of the server - * @param string $category name of the category - * @param boolean $info also download full package info - * @return array of packagenames - */ - function listCategory($base, $category, $info = false, $channel = false) - { - if ($info == false) { - $url = '%s'.'c/%s/packages.xml'; - } else { - $url = '%s'.'c/%s/packagesinfo.xml'; - } - $url = sprintf($url, - $base, - urlencode($category)); - - // gives '404 Not Found' error when category doesn't exist - $packagelist = $this->_rest->retrieveData($url, false, false, $channel); - if (PEAR::isError($packagelist)) { - return $packagelist; - } - if (!is_array($packagelist)) { - return array(); - } - - if ($info == false) { - if (!isset($packagelist['p'])) { - return array(); - } - if (!is_array($packagelist['p']) || - !isset($packagelist['p'][0])) { // only 1 pkg - $packagelist = array($packagelist['p']); - } else { - $packagelist = $packagelist['p']; - } - return $packagelist; - } - - // info == true - if (!isset($packagelist['pi'])) { - return array(); - } - - if (!is_array($packagelist['pi']) || - !isset($packagelist['pi'][0])) { // only 1 pkg - $packagelist_pre = array($packagelist['pi']); - } else { - $packagelist_pre = $packagelist['pi']; - } - - $packagelist = array(); - foreach ($packagelist_pre as $i => $item) { - // compatibility with r/.xml - if (isset($item['a']['r'][0])) { - // multiple releases - $item['p']['v'] = $item['a']['r'][0]['v']; - $item['p']['st'] = $item['a']['r'][0]['s']; - } elseif (isset($item['a'])) { - // first and only release - $item['p']['v'] = $item['a']['r']['v']; - $item['p']['st'] = $item['a']['r']['s']; - } - - $packagelist[$i] = array('attribs' => $item['p']['r'], - '_content' => $item['p']['n'], - 'info' => $item['p']); - } - - return $packagelist; - } - - /** - * Return an array containing all of the states that are more stable than - * or equal to the passed in state - * - * @param string Release state - * @param boolean Determines whether to include $state in the list - * @return false|array False if $state is not a valid release state - */ - function betterStates($state, $include = false) - { - static $states = array('snapshot', 'devel', 'alpha', 'beta', 'stable'); - $i = array_search($state, $states); - if ($i === false) { - return false; - } - if ($include) { - $i--; - } - return array_slice($states, $i + 1); - } -} -?> \ No newline at end of file diff --git a/pear/PEAR/REST/13.php b/pear/PEAR/REST/13.php deleted file mode 100644 index f38cb62..0000000 --- a/pear/PEAR/REST/13.php +++ /dev/null @@ -1,299 +0,0 @@ - - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a12 - */ - -/** - * For downloading REST xml/txt files - */ -require_once 'PEAR/REST.php'; -require_once 'PEAR/REST/10.php'; - -/** - * Implement REST 1.3 - * - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a12 - */ -class PEAR_REST_13 extends PEAR_REST_10 -{ - /** - * Retrieve information about a remote package to be downloaded from a REST server - * - * This is smart enough to resolve the minimum PHP version dependency prior to download - * @param string $base The uri to prepend to all REST calls - * @param array $packageinfo an array of format: - *
    -     *  array(
    -     *   'package' => 'packagename',
    -     *   'channel' => 'channelname',
    -     *  ['state' => 'alpha' (or valid state),]
    -     *  -or-
    -     *  ['version' => '1.whatever']
    -     * 
    - * @param string $prefstate Current preferred_state config variable value - * @param bool $installed the installed version of this package to compare against - * @return array|false|PEAR_Error see {@link _returnDownloadURL()} - */ - function getDownloadURL($base, $packageinfo, $prefstate, $installed, $channel = false) - { - $states = $this->betterStates($prefstate, true); - if (!$states) { - return PEAR::raiseError('"' . $prefstate . '" is not a valid state'); - } - - $channel = $packageinfo['channel']; - $package = $packageinfo['package']; - $state = isset($packageinfo['state']) ? $packageinfo['state'] : null; - $version = isset($packageinfo['version']) ? $packageinfo['version'] : null; - $restFile = $base . 'r/' . strtolower($package) . '/allreleases2.xml'; - - $info = $this->_rest->retrieveData($restFile, false, false, $channel); - if (PEAR::isError($info)) { - return PEAR::raiseError('No releases available for package "' . - $channel . '/' . $package . '"'); - } - - if (!isset($info['r'])) { - return false; - } - - $release = $found = false; - if (!is_array($info['r']) || !isset($info['r'][0])) { - $info['r'] = array($info['r']); - } - - $skippedphp = false; - foreach ($info['r'] as $release) { - if (!isset($this->_rest->_options['force']) && ($installed && - version_compare($release['v'], $installed, '<'))) { - continue; - } - - if (isset($state)) { - // try our preferred state first - if ($release['s'] == $state) { - if (!isset($version) && version_compare($release['m'], phpversion(), '>')) { - // skip releases that require a PHP version newer than our PHP version - $skippedphp = $release; - continue; - } - $found = true; - break; - } - - // see if there is something newer and more stable - // bug #7221 - if (in_array($release['s'], $this->betterStates($state), true)) { - if (!isset($version) && version_compare($release['m'], phpversion(), '>')) { - // skip releases that require a PHP version newer than our PHP version - $skippedphp = $release; - continue; - } - $found = true; - break; - } - } elseif (isset($version)) { - if ($release['v'] == $version) { - if (!isset($this->_rest->_options['force']) && - !isset($version) && - version_compare($release['m'], phpversion(), '>')) { - // skip releases that require a PHP version newer than our PHP version - $skippedphp = $release; - continue; - } - $found = true; - break; - } - } else { - if (in_array($release['s'], $states)) { - if (version_compare($release['m'], phpversion(), '>')) { - // skip releases that require a PHP version newer than our PHP version - $skippedphp = $release; - continue; - } - $found = true; - break; - } - } - } - - if (!$found && $skippedphp) { - $found = null; - } - - return $this->_returnDownloadURL($base, $package, $release, $info, $found, $skippedphp, $channel); - } - - function getDepDownloadURL($base, $xsdversion, $dependency, $deppackage, - $prefstate = 'stable', $installed = false, $channel = false) - { - $states = $this->betterStates($prefstate, true); - if (!$states) { - return PEAR::raiseError('"' . $prefstate . '" is not a valid state'); - } - - $channel = $dependency['channel']; - $package = $dependency['name']; - $state = isset($dependency['state']) ? $dependency['state'] : null; - $version = isset($dependency['version']) ? $dependency['version'] : null; - $restFile = $base . 'r/' . strtolower($package) .'/allreleases2.xml'; - - $info = $this->_rest->retrieveData($restFile, false, false, $channel); - if (PEAR::isError($info)) { - return PEAR::raiseError('Package "' . $deppackage['channel'] . '/' . $deppackage['package'] - . '" dependency "' . $channel . '/' . $package . '" has no releases'); - } - - if (!is_array($info) || !isset($info['r'])) { - return false; - } - - $exclude = array(); - $min = $max = $recommended = false; - if ($xsdversion == '1.0') { - $pinfo['package'] = $dependency['name']; - $pinfo['channel'] = 'pear.php.net'; // this is always true - don't change this - switch ($dependency['rel']) { - case 'ge' : - $min = $dependency['version']; - break; - case 'gt' : - $min = $dependency['version']; - $exclude = array($dependency['version']); - break; - case 'eq' : - $recommended = $dependency['version']; - break; - case 'lt' : - $max = $dependency['version']; - $exclude = array($dependency['version']); - break; - case 'le' : - $max = $dependency['version']; - break; - case 'ne' : - $exclude = array($dependency['version']); - break; - } - } else { - $pinfo['package'] = $dependency['name']; - $min = isset($dependency['min']) ? $dependency['min'] : false; - $max = isset($dependency['max']) ? $dependency['max'] : false; - $recommended = isset($dependency['recommended']) ? - $dependency['recommended'] : false; - if (isset($dependency['exclude'])) { - if (!isset($dependency['exclude'][0])) { - $exclude = array($dependency['exclude']); - } - } - } - - $skippedphp = $found = $release = false; - if (!is_array($info['r']) || !isset($info['r'][0])) { - $info['r'] = array($info['r']); - } - - foreach ($info['r'] as $release) { - if (!isset($this->_rest->_options['force']) && ($installed && - version_compare($release['v'], $installed, '<'))) { - continue; - } - - if (in_array($release['v'], $exclude)) { // skip excluded versions - continue; - } - - // allow newer releases to say "I'm OK with the dependent package" - if ($xsdversion == '2.0' && isset($release['co'])) { - if (!is_array($release['co']) || !isset($release['co'][0])) { - $release['co'] = array($release['co']); - } - - foreach ($release['co'] as $entry) { - if (isset($entry['x']) && !is_array($entry['x'])) { - $entry['x'] = array($entry['x']); - } elseif (!isset($entry['x'])) { - $entry['x'] = array(); - } - - if ($entry['c'] == $deppackage['channel'] && - strtolower($entry['p']) == strtolower($deppackage['package']) && - version_compare($deppackage['version'], $entry['min'], '>=') && - version_compare($deppackage['version'], $entry['max'], '<=') && - !in_array($release['v'], $entry['x'])) { - if (version_compare($release['m'], phpversion(), '>')) { - // skip dependency releases that require a PHP version - // newer than our PHP version - $skippedphp = $release; - continue; - } - - $recommended = $release['v']; - break; - } - } - } - - if ($recommended) { - if ($release['v'] != $recommended) { // if we want a specific - // version, then skip all others - continue; - } - - if (!in_array($release['s'], $states)) { - // the stability is too low, but we must return the - // recommended version if possible - return $this->_returnDownloadURL($base, $package, $release, $info, true, false, $channel); - } - } - - if ($min && version_compare($release['v'], $min, 'lt')) { // skip too old versions - continue; - } - - if ($max && version_compare($release['v'], $max, 'gt')) { // skip too new versions - continue; - } - - if ($installed && version_compare($release['v'], $installed, '<')) { - continue; - } - - if (in_array($release['s'], $states)) { // if in the preferred state... - if (version_compare($release['m'], phpversion(), '>')) { - // skip dependency releases that require a PHP version - // newer than our PHP version - $skippedphp = $release; - continue; - } - - $found = true; // ... then use it - break; - } - } - - if (!$found && $skippedphp) { - $found = null; - } - - return $this->_returnDownloadURL($base, $package, $release, $info, $found, $skippedphp, $channel); - } -} \ No newline at end of file diff --git a/pear/PEAR/REST/14.php b/pear/PEAR/REST/14.php deleted file mode 100644 index 3358a46..0000000 --- a/pear/PEAR/REST/14.php +++ /dev/null @@ -1,120 +0,0 @@ - - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id: $ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.9 - */ - -/** - * For downloading REST xml/txt files - */ -require_once 'PEAR/REST.php'; -require_once 'PEAR/REST/13.php'; - -/** - * Implement REST 1.4 - * - * @category pear - * @package PEAR - * @author Helgi Þormar Þorbjörnsson - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.9 - */ -class PEAR_REST_14 extends PEAR_REST_13 -{ - function listLatestUpgrades($base, $pref_state, $installed, $channel, &$reg) - { - $packagelist = $this->_rest->retrieveData($base . 'p/latestpackages.xml', false, false, $channel); - if (PEAR::isError($packagelist)) { - return $packagelist; - } - - $ret = array(); - if (!is_array($packagelist) || !isset($packagelist['p'])) { - return $ret; - } - - if (isset($packagelist['p']['n'])) { - $packagelist['p'] = array($packagelist['p']); - } - - foreach ($packagelist['p'] as $package) { - if (!isset($installed[strtolower($package['n'])])) { - continue; - } - - $inst_version = $reg->packageInfo($package['n'], 'version', $channel); - $inst_state = $reg->packageInfo($package['n'], 'release_state', $channel); - - - $release = $found = false; - $data = array(); - if (isset($package['alpha'])) { - $data['alpha'] = $package['alpha']; - } - - if (isset($package['beta'])) { - $data['beta'] = $package['beta']; - } - - if (isset($package['stable'])) { - $data['stable'] = $package['stable']; - } - - foreach ($data as $state => $release) { - if ($inst_version && version_compare($release['v'], $inst_version, '<=')) { - // not newer than the one installed - break; - } - - // new version > installed version - if (!$pref_state) { - // every state is a good state - $found = true; - $release['state'] = $state; - break; - } else { - $new_state = $state; - // if new state >= installed state: go - if (in_array($new_state, $this->betterStates($inst_state, true))) { - $found = true; - $release['state'] = $state; - break; - } else { - // only allow to lower the state of package, - // if new state >= preferred state: go - if (in_array($new_state, $this->betterStates($pref_state, true))) { - $found = true; - $release['state'] = $state; - break; - } - } - } - } - - if (!$found) { - continue; - } - - $ret[$package] = array( - 'version' => $release['v'], - 'state' => $release['s'], - 'filesize' => $release['f'], - ); - } - - return $ret; - } -} \ No newline at end of file diff --git a/pear/PEAR/Registry.php b/pear/PEAR/Registry.php deleted file mode 100644 index 02168db..0000000 --- a/pear/PEAR/Registry.php +++ /dev/null @@ -1,2339 +0,0 @@ - - * @author Tomas V. V. Cox - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 0.1 - */ - -/** - * for PEAR_Error - */ -require_once 'PEAR.php'; -require_once 'PEAR/DependencyDB.php'; - -define('PEAR_REGISTRY_ERROR_LOCK', -2); -define('PEAR_REGISTRY_ERROR_FORMAT', -3); -define('PEAR_REGISTRY_ERROR_FILE', -4); -define('PEAR_REGISTRY_ERROR_CONFLICT', -5); -define('PEAR_REGISTRY_ERROR_CHANNEL_FILE', -6); - -/** - * Administration class used to maintain the installed package database. - * @category pear - * @package PEAR - * @author Stig Bakken - * @author Tomas V. V. Cox - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a1 - */ -class PEAR_Registry extends PEAR -{ - /** - * File containing all channel information. - * @var string - */ - var $channels = ''; - - /** Directory where registry files are stored. - * @var string - */ - var $statedir = ''; - - /** File where the file map is stored - * @var string - */ - var $filemap = ''; - - /** Directory where registry files for channels are stored. - * @var string - */ - var $channelsdir = ''; - - /** Name of file used for locking the registry - * @var string - */ - var $lockfile = ''; - - /** File descriptor used during locking - * @var resource - */ - var $lock_fp = null; - - /** Mode used during locking - * @var int - */ - var $lock_mode = 0; // XXX UNUSED - - /** Cache of package information. Structure: - * array( - * 'package' => array('id' => ... ), - * ... ) - * @var array - */ - var $pkginfo_cache = array(); - - /** Cache of file map. Structure: - * array( '/path/to/file' => 'package', ... ) - * @var array - */ - var $filemap_cache = array(); - - /** - * @var false|PEAR_ChannelFile - */ - var $_pearChannel; - - /** - * @var false|PEAR_ChannelFile - */ - var $_peclChannel; - - /** - * @var false|PEAR_ChannelFile - */ - var $_docChannel; - - /** - * @var PEAR_DependencyDB - */ - var $_dependencyDB; - - /** - * @var PEAR_Config - */ - var $_config; - - /** - * PEAR_Registry constructor. - * - * @param string (optional) PEAR install directory (for .php files) - * @param PEAR_ChannelFile PEAR_ChannelFile object representing the PEAR channel, if - * default values are not desired. Only used the very first time a PEAR - * repository is initialized - * @param PEAR_ChannelFile PEAR_ChannelFile object representing the PECL channel, if - * default values are not desired. Only used the very first time a PEAR - * repository is initialized - * - * @access public - */ - function PEAR_Registry($pear_install_dir = PEAR_INSTALL_DIR, $pear_channel = false, - $pecl_channel = false) - { - parent::PEAR(); - $this->setInstallDir($pear_install_dir); - $this->_pearChannel = $pear_channel; - $this->_peclChannel = $pecl_channel; - $this->_config = false; - } - - function setInstallDir($pear_install_dir = PEAR_INSTALL_DIR) - { - $ds = DIRECTORY_SEPARATOR; - $this->install_dir = $pear_install_dir; - $this->channelsdir = $pear_install_dir.$ds.'.channels'; - $this->statedir = $pear_install_dir.$ds.'.registry'; - $this->filemap = $pear_install_dir.$ds.'.filemap'; - $this->lockfile = $pear_install_dir.$ds.'.lock'; - } - - function hasWriteAccess() - { - if (!file_exists($this->install_dir)) { - $dir = $this->install_dir; - while ($dir && $dir != '.') { - $olddir = $dir; - $dir = dirname($dir); - if ($dir != '.' && file_exists($dir)) { - if (is_writeable($dir)) { - return true; - } - - return false; - } - - if ($dir == $olddir) { // this can happen in safe mode - return @is_writable($dir); - } - } - - return false; - } - - return is_writeable($this->install_dir); - } - - function setConfig(&$config, $resetInstallDir = true) - { - $this->_config = &$config; - if ($resetInstallDir) { - $this->setInstallDir($config->get('php_dir')); - } - } - - function _initializeChannelDirs() - { - static $running = false; - if (!$running) { - $running = true; - $ds = DIRECTORY_SEPARATOR; - if (!is_dir($this->channelsdir) || - !file_exists($this->channelsdir . $ds . 'pear.php.net.reg') || - !file_exists($this->channelsdir . $ds . 'pecl.php.net.reg') || - !file_exists($this->channelsdir . $ds . 'doc.php.net.reg') || - !file_exists($this->channelsdir . $ds . '__uri.reg')) { - if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) { - $pear_channel = $this->_pearChannel; - if (!is_a($pear_channel, 'PEAR_ChannelFile') || !$pear_channel->validate()) { - if (!class_exists('PEAR_ChannelFile')) { - require_once 'PEAR/ChannelFile.php'; - } - - $pear_channel = new PEAR_ChannelFile; - $pear_channel->setAlias('pear'); - $pear_channel->setServer('pear.php.net'); - $pear_channel->setSummary('PHP Extension and Application Repository'); - $pear_channel->setDefaultPEARProtocols(); - $pear_channel->setBaseURL('REST1.0', 'http://pear.php.net/rest/'); - $pear_channel->setBaseURL('REST1.1', 'http://pear.php.net/rest/'); - $pear_channel->setBaseURL('REST1.3', 'http://pear.php.net/rest/'); - //$pear_channel->setBaseURL('REST1.4', 'http://pear.php.net/rest/'); - } else { - $pear_channel->setServer('pear.php.net'); - $pear_channel->setAlias('pear'); - } - - $pear_channel->validate(); - $this->_addChannel($pear_channel); - } - - if (!file_exists($this->channelsdir . $ds . 'pecl.php.net.reg')) { - $pecl_channel = $this->_peclChannel; - if (!is_a($pecl_channel, 'PEAR_ChannelFile') || !$pecl_channel->validate()) { - if (!class_exists('PEAR_ChannelFile')) { - require_once 'PEAR/ChannelFile.php'; - } - - $pecl_channel = new PEAR_ChannelFile; - $pecl_channel->setAlias('pecl'); - $pecl_channel->setServer('pecl.php.net'); - $pecl_channel->setSummary('PHP Extension Community Library'); - $pecl_channel->setDefaultPEARProtocols(); - $pecl_channel->setBaseURL('REST1.0', 'http://pecl.php.net/rest/'); - $pecl_channel->setBaseURL('REST1.1', 'http://pecl.php.net/rest/'); - $pecl_channel->setValidationPackage('PEAR_Validator_PECL', '1.0'); - } else { - $pecl_channel->setServer('pecl.php.net'); - $pecl_channel->setAlias('pecl'); - } - - $pecl_channel->validate(); - $this->_addChannel($pecl_channel); - } - - if (!file_exists($this->channelsdir . $ds . 'doc.php.net.reg')) { - $doc_channel = $this->_docChannel; - if (!is_a($doc_channel, 'PEAR_ChannelFile') || !$doc_channel->validate()) { - if (!class_exists('PEAR_ChannelFile')) { - require_once 'PEAR/ChannelFile.php'; - } - - $doc_channel = new PEAR_ChannelFile; - $doc_channel->setAlias('phpdocs'); - $doc_channel->setServer('doc.php.net'); - $doc_channel->setSummary('PHP Documentation Team'); - $doc_channel->setDefaultPEARProtocols(); - $doc_channel->setBaseURL('REST1.0', 'http://doc.php.net/rest/'); - $doc_channel->setBaseURL('REST1.1', 'http://doc.php.net/rest/'); - $doc_channel->setBaseURL('REST1.3', 'http://doc.php.net/rest/'); - } else { - $doc_channel->setServer('doc.php.net'); - $doc_channel->setAlias('doc'); - } - - $doc_channel->validate(); - $this->_addChannel($doc_channel); - } - - if (!file_exists($this->channelsdir . $ds . '__uri.reg')) { - if (!class_exists('PEAR_ChannelFile')) { - require_once 'PEAR/ChannelFile.php'; - } - - $private = new PEAR_ChannelFile; - $private->setName('__uri'); - $private->setDefaultPEARProtocols(); - $private->setBaseURL('REST1.0', '****'); - $private->setSummary('Pseudo-channel for static packages'); - $this->_addChannel($private); - } - $this->_rebuildFileMap(); - } - - $running = false; - } - } - - function _initializeDirs() - { - $ds = DIRECTORY_SEPARATOR; - // XXX Compatibility code should be removed in the future - // rename all registry files if any to lowercase - if (!OS_WINDOWS && file_exists($this->statedir) && is_dir($this->statedir) && - $handle = opendir($this->statedir)) { - $dest = $this->statedir . $ds; - while (false !== ($file = readdir($handle))) { - if (preg_match('/^.*[A-Z].*\.reg\\z/', $file)) { - rename($dest . $file, $dest . strtolower($file)); - } - } - closedir($handle); - } - - $this->_initializeChannelDirs(); - if (!file_exists($this->filemap)) { - $this->_rebuildFileMap(); - } - $this->_initializeDepDB(); - } - - function _initializeDepDB() - { - if (!isset($this->_dependencyDB)) { - static $initializing = false; - if (!$initializing) { - $initializing = true; - if (!$this->_config) { // never used? - $file = OS_WINDOWS ? 'pear.ini' : '.pearrc'; - $this->_config = &new PEAR_Config($this->statedir . DIRECTORY_SEPARATOR . - $file); - $this->_config->setRegistry($this); - $this->_config->set('php_dir', $this->install_dir); - } - - $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->_config); - if (PEAR::isError($this->_dependencyDB)) { - // attempt to recover by removing the dep db - if (file_exists($this->_config->get('php_dir', null, 'pear.php.net') . - DIRECTORY_SEPARATOR . '.depdb')) { - @unlink($this->_config->get('php_dir', null, 'pear.php.net') . - DIRECTORY_SEPARATOR . '.depdb'); - } - - $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->_config); - if (PEAR::isError($this->_dependencyDB)) { - echo $this->_dependencyDB->getMessage(); - echo 'Unrecoverable error'; - exit(1); - } - } - - $initializing = false; - } - } - } - - /** - * PEAR_Registry destructor. Makes sure no locks are forgotten. - * - * @access private - */ - function _PEAR_Registry() - { - parent::_PEAR(); - if (is_resource($this->lock_fp)) { - $this->_unlock(); - } - } - - /** - * Make sure the directory where we keep registry files exists. - * - * @return bool TRUE if directory exists, FALSE if it could not be - * created - * - * @access private - */ - function _assertStateDir($channel = false) - { - if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') { - return $this->_assertChannelStateDir($channel); - } - - static $init = false; - if (!file_exists($this->statedir)) { - if (!$this->hasWriteAccess()) { - return false; - } - - require_once 'System.php'; - if (!System::mkdir(array('-p', $this->statedir))) { - return $this->raiseError("could not create directory '{$this->statedir}'"); - } - $init = true; - } elseif (!is_dir($this->statedir)) { - return $this->raiseError('Cannot create directory ' . $this->statedir . ', ' . - 'it already exists and is not a directory'); - } - - $ds = DIRECTORY_SEPARATOR; - if (!file_exists($this->channelsdir)) { - if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg') || - !file_exists($this->channelsdir . $ds . 'pecl.php.net.reg') || - !file_exists($this->channelsdir . $ds . 'doc.php.net.reg') || - !file_exists($this->channelsdir . $ds . '__uri.reg')) { - $init = true; - } - } elseif (!is_dir($this->channelsdir)) { - return $this->raiseError('Cannot create directory ' . $this->channelsdir . ', ' . - 'it already exists and is not a directory'); - } - - if ($init) { - static $running = false; - if (!$running) { - $running = true; - $this->_initializeDirs(); - $running = false; - $init = false; - } - } else { - $this->_initializeDepDB(); - } - - return true; - } - - /** - * Make sure the directory where we keep registry files exists for a non-standard channel. - * - * @param string channel name - * @return bool TRUE if directory exists, FALSE if it could not be - * created - * - * @access private - */ - function _assertChannelStateDir($channel) - { - $ds = DIRECTORY_SEPARATOR; - if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') { - if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) { - $this->_initializeChannelDirs(); - } - return $this->_assertStateDir($channel); - } - - $channelDir = $this->_channelDirectoryName($channel); - if (!is_dir($this->channelsdir) || - !file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) { - $this->_initializeChannelDirs(); - } - - if (!file_exists($channelDir)) { - if (!$this->hasWriteAccess()) { - return false; - } - - require_once 'System.php'; - if (!System::mkdir(array('-p', $channelDir))) { - return $this->raiseError("could not create directory '" . $channelDir . - "'"); - } - } elseif (!is_dir($channelDir)) { - return $this->raiseError("could not create directory '" . $channelDir . - "', already exists and is not a directory"); - } - - return true; - } - - /** - * Make sure the directory where we keep registry files for channels exists - * - * @return bool TRUE if directory exists, FALSE if it could not be - * created - * - * @access private - */ - function _assertChannelDir() - { - if (!file_exists($this->channelsdir)) { - if (!$this->hasWriteAccess()) { - return false; - } - - require_once 'System.php'; - if (!System::mkdir(array('-p', $this->channelsdir))) { - return $this->raiseError("could not create directory '{$this->channelsdir}'"); - } - } elseif (!is_dir($this->channelsdir)) { - return $this->raiseError("could not create directory '{$this->channelsdir}" . - "', it already exists and is not a directory"); - } - - if (!file_exists($this->channelsdir . DIRECTORY_SEPARATOR . '.alias')) { - if (!$this->hasWriteAccess()) { - return false; - } - - require_once 'System.php'; - if (!System::mkdir(array('-p', $this->channelsdir . DIRECTORY_SEPARATOR . '.alias'))) { - return $this->raiseError("could not create directory '{$this->channelsdir}/.alias'"); - } - } elseif (!is_dir($this->channelsdir . DIRECTORY_SEPARATOR . '.alias')) { - return $this->raiseError("could not create directory '{$this->channelsdir}" . - "/.alias', it already exists and is not a directory"); - } - - return true; - } - - /** - * Get the name of the file where data for a given package is stored. - * - * @param string channel name, or false if this is a PEAR package - * @param string package name - * - * @return string registry file name - * - * @access public - */ - function _packageFileName($package, $channel = false) - { - if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') { - return $this->_channelDirectoryName($channel) . DIRECTORY_SEPARATOR . - strtolower($package) . '.reg'; - } - - return $this->statedir . DIRECTORY_SEPARATOR . strtolower($package) . '.reg'; - } - - /** - * Get the name of the file where data for a given channel is stored. - * @param string channel name - * @return string registry file name - */ - function _channelFileName($channel, $noaliases = false) - { - if (!$noaliases) { - if (file_exists($this->_getChannelAliasFileName($channel))) { - $channel = implode('', file($this->_getChannelAliasFileName($channel))); - } - } - return $this->channelsdir . DIRECTORY_SEPARATOR . str_replace('/', '_', - strtolower($channel)) . '.reg'; - } - - /** - * @param string - * @return string - */ - function _getChannelAliasFileName($alias) - { - return $this->channelsdir . DIRECTORY_SEPARATOR . '.alias' . - DIRECTORY_SEPARATOR . str_replace('/', '_', strtolower($alias)) . '.txt'; - } - - /** - * Get the name of a channel from its alias - */ - function _getChannelFromAlias($channel) - { - if (!$this->_channelExists($channel)) { - if ($channel == 'pear.php.net') { - return 'pear.php.net'; - } - - if ($channel == 'pecl.php.net') { - return 'pecl.php.net'; - } - - if ($channel == 'doc.php.net') { - return 'doc.php.net'; - } - - if ($channel == '__uri') { - return '__uri'; - } - - return false; - } - - $channel = strtolower($channel); - if (file_exists($this->_getChannelAliasFileName($channel))) { - // translate an alias to an actual channel - return implode('', file($this->_getChannelAliasFileName($channel))); - } - - return $channel; - } - - /** - * Get the alias of a channel from its alias or its name - */ - function _getAlias($channel) - { - if (!$this->_channelExists($channel)) { - if ($channel == 'pear.php.net') { - return 'pear'; - } - - if ($channel == 'pecl.php.net') { - return 'pecl'; - } - - if ($channel == 'doc.php.net') { - return 'phpdocs'; - } - - return false; - } - - $channel = $this->_getChannel($channel); - if (PEAR::isError($channel)) { - return $channel; - } - - return $channel->getAlias(); - } - - /** - * Get the name of the file where data for a given package is stored. - * - * @param string channel name, or false if this is a PEAR package - * @param string package name - * - * @return string registry file name - * - * @access public - */ - function _channelDirectoryName($channel) - { - if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') { - return $this->statedir; - } - - $ch = $this->_getChannelFromAlias($channel); - if (!$ch) { - $ch = $channel; - } - - return $this->statedir . DIRECTORY_SEPARATOR . strtolower('.channel.' . - str_replace('/', '_', $ch)); - } - - function _openPackageFile($package, $mode, $channel = false) - { - if (!$this->_assertStateDir($channel)) { - return null; - } - - if (!in_array($mode, array('r', 'rb')) && !$this->hasWriteAccess()) { - return null; - } - - $file = $this->_packageFileName($package, $channel); - if (!file_exists($file) && $mode == 'r' || $mode == 'rb') { - return null; - } - - $fp = @fopen($file, $mode); - if (!$fp) { - return null; - } - - return $fp; - } - - function _closePackageFile($fp) - { - fclose($fp); - } - - function _openChannelFile($channel, $mode) - { - if (!$this->_assertChannelDir()) { - return null; - } - - if (!in_array($mode, array('r', 'rb')) && !$this->hasWriteAccess()) { - return null; - } - - $file = $this->_channelFileName($channel); - if (!file_exists($file) && $mode == 'r' || $mode == 'rb') { - return null; - } - - $fp = @fopen($file, $mode); - if (!$fp) { - return null; - } - - return $fp; - } - - function _closeChannelFile($fp) - { - fclose($fp); - } - - function _rebuildFileMap() - { - if (!class_exists('PEAR_Installer_Role')) { - require_once 'PEAR/Installer/Role.php'; - } - - $channels = $this->_listAllPackages(); - $files = array(); - foreach ($channels as $channel => $packages) { - foreach ($packages as $package) { - $version = $this->_packageInfo($package, 'version', $channel); - $filelist = $this->_packageInfo($package, 'filelist', $channel); - if (!is_array($filelist)) { - continue; - } - - foreach ($filelist as $name => $attrs) { - if (isset($attrs['attribs'])) { - $attrs = $attrs['attribs']; - } - - // it is possible for conflicting packages in different channels to - // conflict with data files/doc files - if ($name == 'dirtree') { - continue; - } - - if (isset($attrs['role']) && !in_array($attrs['role'], - PEAR_Installer_Role::getInstallableRoles())) { - // these are not installed - continue; - } - - if (isset($attrs['role']) && !in_array($attrs['role'], - PEAR_Installer_Role::getBaseinstallRoles())) { - $attrs['baseinstalldir'] = $package; - } - - if (isset($attrs['baseinstalldir'])) { - $file = $attrs['baseinstalldir'].DIRECTORY_SEPARATOR.$name; - } else { - $file = $name; - } - - $file = preg_replace(',^/+,', '', $file); - if ($channel != 'pear.php.net') { - if (!isset($files[$attrs['role']])) { - $files[$attrs['role']] = array(); - } - $files[$attrs['role']][$file] = array(strtolower($channel), - strtolower($package)); - } else { - if (!isset($files[$attrs['role']])) { - $files[$attrs['role']] = array(); - } - $files[$attrs['role']][$file] = strtolower($package); - } - } - } - } - - - $this->_assertStateDir(); - if (!$this->hasWriteAccess()) { - return false; - } - - $fp = @fopen($this->filemap, 'wb'); - if (!$fp) { - return false; - } - - $this->filemap_cache = $files; - fwrite($fp, serialize($files)); - fclose($fp); - return true; - } - - function _readFileMap() - { - if (!file_exists($this->filemap)) { - return array(); - } - - $fp = @fopen($this->filemap, 'r'); - if (!$fp) { - return $this->raiseError('PEAR_Registry: could not open filemap "' . $this->filemap . '"', PEAR_REGISTRY_ERROR_FILE, null, null, $php_errormsg); - } - - clearstatcache(); - $rt = get_magic_quotes_runtime(); - set_magic_quotes_runtime(0); - $fsize = filesize($this->filemap); - fclose($fp); - $data = file_get_contents($this->filemap); - set_magic_quotes_runtime($rt); - $tmp = unserialize($data); - if (!$tmp && $fsize > 7) { - return $this->raiseError('PEAR_Registry: invalid filemap data', PEAR_REGISTRY_ERROR_FORMAT, null, null, $data); - } - - $this->filemap_cache = $tmp; - return true; - } - - /** - * Lock the registry. - * - * @param integer lock mode, one of LOCK_EX, LOCK_SH or LOCK_UN. - * See flock manual for more information. - * - * @return bool TRUE on success, FALSE if locking failed, or a - * PEAR error if some other error occurs (such as the - * lock file not being writable). - * - * @access private - */ - function _lock($mode = LOCK_EX) - { - if (stristr(php_uname(), 'Windows 9')) { - return true; - } - - if ($mode != LOCK_UN && is_resource($this->lock_fp)) { - // XXX does not check type of lock (LOCK_SH/LOCK_EX) - return true; - } - - if (!$this->_assertStateDir()) { - if ($mode == LOCK_EX) { - return $this->raiseError('Registry directory is not writeable by the current user'); - } - - return true; - } - - $open_mode = 'w'; - // XXX People reported problems with LOCK_SH and 'w' - if ($mode === LOCK_SH || $mode === LOCK_UN) { - if (!file_exists($this->lockfile)) { - touch($this->lockfile); - } - $open_mode = 'r'; - } - - if (!is_resource($this->lock_fp)) { - $this->lock_fp = @fopen($this->lockfile, $open_mode); - } - - if (!is_resource($this->lock_fp)) { - $this->lock_fp = null; - return $this->raiseError("could not create lock file" . - (isset($php_errormsg) ? ": " . $php_errormsg : "")); - } - - if (!(int)flock($this->lock_fp, $mode)) { - switch ($mode) { - case LOCK_SH: $str = 'shared'; break; - case LOCK_EX: $str = 'exclusive'; break; - case LOCK_UN: $str = 'unlock'; break; - default: $str = 'unknown'; break; - } - - //is resource at this point, close it on error. - fclose($this->lock_fp); - $this->lock_fp = null; - return $this->raiseError("could not acquire $str lock ($this->lockfile)", - PEAR_REGISTRY_ERROR_LOCK); - } - - return true; - } - - function _unlock() - { - $ret = $this->_lock(LOCK_UN); - if (is_resource($this->lock_fp)) { - fclose($this->lock_fp); - } - - $this->lock_fp = null; - return $ret; - } - - function _packageExists($package, $channel = false) - { - return file_exists($this->_packageFileName($package, $channel)); - } - - /** - * Determine whether a channel exists in the registry - * - * @param string Channel name - * @param bool if true, then aliases will be ignored - * @return boolean - */ - function _channelExists($channel, $noaliases = false) - { - $a = file_exists($this->_channelFileName($channel, $noaliases)); - if (!$a && $channel == 'pear.php.net') { - return true; - } - - if (!$a && $channel == 'pecl.php.net') { - return true; - } - - if (!$a && $channel == 'doc.php.net') { - return true; - } - - return $a; - } - - /** - * Determine whether a mirror exists within the deafult channel in the registry - * - * @param string Channel name - * @param string Mirror name - * - * @return boolean - */ - function _mirrorExists($channel, $mirror) - { - $data = $this->_channelInfo($channel); - if (!isset($data['servers']['mirror'])) { - return false; - } - - foreach ($data['servers']['mirror'] as $m) { - if ($m['attribs']['host'] == $mirror) { - return true; - } - } - - return false; - } - - /** - * @param PEAR_ChannelFile Channel object - * @param donotuse - * @param string Last-Modified HTTP tag from remote request - * @return boolean|PEAR_Error True on creation, false if it already exists - */ - function _addChannel($channel, $update = false, $lastmodified = false) - { - if (!is_a($channel, 'PEAR_ChannelFile')) { - return false; - } - - if (!$channel->validate()) { - return false; - } - - if (file_exists($this->_channelFileName($channel->getName()))) { - if (!$update) { - return false; - } - - $checker = $this->_getChannel($channel->getName()); - if (PEAR::isError($checker)) { - return $checker; - } - - if ($channel->getAlias() != $checker->getAlias()) { - if (file_exists($this->_getChannelAliasFileName($checker->getAlias()))) { - @unlink($this->_getChannelAliasFileName($checker->getAlias())); - } - } - } else { - if ($update && !in_array($channel->getName(), array('pear.php.net', 'pecl.php.net', 'doc.php.net'))) { - return false; - } - } - - $ret = $this->_assertChannelDir(); - if (PEAR::isError($ret)) { - return $ret; - } - - $ret = $this->_assertChannelStateDir($channel->getName()); - if (PEAR::isError($ret)) { - return $ret; - } - - if ($channel->getAlias() != $channel->getName()) { - if (file_exists($this->_getChannelAliasFileName($channel->getAlias())) && - $this->_getChannelFromAlias($channel->getAlias()) != $channel->getName()) { - $channel->setAlias($channel->getName()); - } - - if (!$this->hasWriteAccess()) { - return false; - } - - $fp = @fopen($this->_getChannelAliasFileName($channel->getAlias()), 'w'); - if (!$fp) { - return false; - } - - fwrite($fp, $channel->getName()); - fclose($fp); - } - - if (!$this->hasWriteAccess()) { - return false; - } - - $fp = @fopen($this->_channelFileName($channel->getName()), 'wb'); - if (!$fp) { - return false; - } - - $info = $channel->toArray(); - if ($lastmodified) { - $info['_lastmodified'] = $lastmodified; - } else { - $info['_lastmodified'] = date('r'); - } - - fwrite($fp, serialize($info)); - fclose($fp); - return true; - } - - /** - * Deletion fails if there are any packages installed from the channel - * @param string|PEAR_ChannelFile channel name - * @return boolean|PEAR_Error True on deletion, false if it doesn't exist - */ - function _deleteChannel($channel) - { - if (!is_string($channel)) { - if (!is_a($channel, 'PEAR_ChannelFile')) { - return false; - } - - if (!$channel->validate()) { - return false; - } - $channel = $channel->getName(); - } - - if ($this->_getChannelFromAlias($channel) == '__uri') { - return false; - } - - if ($this->_getChannelFromAlias($channel) == 'pecl.php.net') { - return false; - } - - if ($this->_getChannelFromAlias($channel) == 'doc.php.net') { - return false; - } - - if (!$this->_channelExists($channel)) { - return false; - } - - if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') { - return false; - } - - $channel = $this->_getChannelFromAlias($channel); - if ($channel == 'pear.php.net') { - return false; - } - - $test = $this->_listChannelPackages($channel); - if (count($test)) { - return false; - } - - $test = @rmdir($this->_channelDirectoryName($channel)); - if (!$test) { - return false; - } - - $file = $this->_getChannelAliasFileName($this->_getAlias($channel)); - if (file_exists($file)) { - $test = @unlink($file); - if (!$test) { - return false; - } - } - - $file = $this->_channelFileName($channel); - $ret = true; - if (file_exists($file)) { - $ret = @unlink($file); - } - - return $ret; - } - - /** - * Determine whether a channel exists in the registry - * @param string Channel Alias - * @return boolean - */ - function _isChannelAlias($alias) - { - return file_exists($this->_getChannelAliasFileName($alias)); - } - - /** - * @param string|null - * @param string|null - * @param string|null - * @return array|null - * @access private - */ - function _packageInfo($package = null, $key = null, $channel = 'pear.php.net') - { - if ($package === null) { - if ($channel === null) { - $channels = $this->_listChannels(); - $ret = array(); - foreach ($channels as $channel) { - $channel = strtolower($channel); - $ret[$channel] = array(); - $packages = $this->_listPackages($channel); - foreach ($packages as $package) { - $ret[$channel][] = $this->_packageInfo($package, null, $channel); - } - } - - return $ret; - } - - $ps = $this->_listPackages($channel); - if (!count($ps)) { - return array(); - } - return array_map(array(&$this, '_packageInfo'), - $ps, array_fill(0, count($ps), null), - array_fill(0, count($ps), $channel)); - } - - $fp = $this->_openPackageFile($package, 'r', $channel); - if ($fp === null) { - return null; - } - - $rt = get_magic_quotes_runtime(); - set_magic_quotes_runtime(0); - clearstatcache(); - $this->_closePackageFile($fp); - $data = file_get_contents($this->_packageFileName($package, $channel)); - set_magic_quotes_runtime($rt); - $data = unserialize($data); - if ($key === null) { - return $data; - } - - // compatibility for package.xml version 2.0 - if (isset($data['old'][$key])) { - return $data['old'][$key]; - } - - if (isset($data[$key])) { - return $data[$key]; - } - - return null; - } - - /** - * @param string Channel name - * @param bool whether to strictly retrieve info of channels, not just aliases - * @return array|null - */ - function _channelInfo($channel, $noaliases = false) - { - if (!$this->_channelExists($channel, $noaliases)) { - return null; - } - - $fp = $this->_openChannelFile($channel, 'r'); - if ($fp === null) { - return null; - } - - $rt = get_magic_quotes_runtime(); - set_magic_quotes_runtime(0); - clearstatcache(); - $this->_closeChannelFile($fp); - $data = file_get_contents($this->_channelFileName($channel)); - set_magic_quotes_runtime($rt); - $data = unserialize($data); - return $data; - } - - function _listChannels() - { - $channellist = array(); - if (!file_exists($this->channelsdir) || !is_dir($this->channelsdir)) { - return array('pear.php.net', 'pecl.php.net', 'doc.php.net', '__uri'); - } - - $dp = opendir($this->channelsdir); - while ($ent = readdir($dp)) { - if ($ent{0} == '.' || substr($ent, -4) != '.reg') { - continue; - } - - if ($ent == '__uri.reg') { - $channellist[] = '__uri'; - continue; - } - - $channellist[] = str_replace('_', '/', substr($ent, 0, -4)); - } - - closedir($dp); - if (!in_array('pear.php.net', $channellist)) { - $channellist[] = 'pear.php.net'; - } - - if (!in_array('pecl.php.net', $channellist)) { - $channellist[] = 'pecl.php.net'; - } - - if (!in_array('doc.php.net', $channellist)) { - $channellist[] = 'doc.php.net'; - } - - - if (!in_array('__uri', $channellist)) { - $channellist[] = '__uri'; - } - - natsort($channellist); - return $channellist; - } - - function _listPackages($channel = false) - { - if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') { - return $this->_listChannelPackages($channel); - } - - if (!file_exists($this->statedir) || !is_dir($this->statedir)) { - return array(); - } - - $pkglist = array(); - $dp = opendir($this->statedir); - if (!$dp) { - return $pkglist; - } - - while ($ent = readdir($dp)) { - if ($ent{0} == '.' || substr($ent, -4) != '.reg') { - continue; - } - - $pkglist[] = substr($ent, 0, -4); - } - closedir($dp); - return $pkglist; - } - - function _listChannelPackages($channel) - { - $pkglist = array(); - if (!file_exists($this->_channelDirectoryName($channel)) || - !is_dir($this->_channelDirectoryName($channel))) { - return array(); - } - - $dp = opendir($this->_channelDirectoryName($channel)); - if (!$dp) { - return $pkglist; - } - - while ($ent = readdir($dp)) { - if ($ent{0} == '.' || substr($ent, -4) != '.reg') { - continue; - } - $pkglist[] = substr($ent, 0, -4); - } - - closedir($dp); - return $pkglist; - } - - function _listAllPackages() - { - $ret = array(); - foreach ($this->_listChannels() as $channel) { - $ret[$channel] = $this->_listPackages($channel); - } - - return $ret; - } - - /** - * Add an installed package to the registry - * @param string package name - * @param array package info (parsed by PEAR_Common::infoFrom*() methods) - * @return bool success of saving - * @access private - */ - function _addPackage($package, $info) - { - if ($this->_packageExists($package)) { - return false; - } - - $fp = $this->_openPackageFile($package, 'wb'); - if ($fp === null) { - return false; - } - - $info['_lastmodified'] = time(); - fwrite($fp, serialize($info)); - $this->_closePackageFile($fp); - if (isset($info['filelist'])) { - $this->_rebuildFileMap(); - } - - return true; - } - - /** - * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 - * @return bool - * @access private - */ - function _addPackage2($info) - { - if (!is_a($info, 'PEAR_PackageFile_v1') && !is_a($info, 'PEAR_PackageFile_v2')) { - return false; - } - - if (!$info->validate()) { - if (class_exists('PEAR_Common')) { - $ui = PEAR_Frontend::singleton(); - if ($ui) { - foreach ($info->getValidationWarnings() as $err) { - $ui->log($err['message'], true); - } - } - } - return false; - } - - $channel = $info->getChannel(); - $package = $info->getPackage(); - $save = $info; - if ($this->_packageExists($package, $channel)) { - return false; - } - - if (!$this->_channelExists($channel, true)) { - return false; - } - - $info = $info->toArray(true); - if (!$info) { - return false; - } - - $fp = $this->_openPackageFile($package, 'wb', $channel); - if ($fp === null) { - return false; - } - - $info['_lastmodified'] = time(); - fwrite($fp, serialize($info)); - $this->_closePackageFile($fp); - $this->_rebuildFileMap(); - return true; - } - - /** - * @param string Package name - * @param array parsed package.xml 1.0 - * @param bool this parameter is only here for BC. Don't use it. - * @access private - */ - function _updatePackage($package, $info, $merge = true) - { - $oldinfo = $this->_packageInfo($package); - if (empty($oldinfo)) { - return false; - } - - $fp = $this->_openPackageFile($package, 'w'); - if ($fp === null) { - return false; - } - - if (is_object($info)) { - $info = $info->toArray(); - } - $info['_lastmodified'] = time(); - - $newinfo = $info; - if ($merge) { - $info = array_merge($oldinfo, $info); - } else { - $diff = $info; - } - - fwrite($fp, serialize($info)); - $this->_closePackageFile($fp); - if (isset($newinfo['filelist'])) { - $this->_rebuildFileMap(); - } - - return true; - } - - /** - * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 - * @return bool - * @access private - */ - function _updatePackage2($info) - { - if (!$this->_packageExists($info->getPackage(), $info->getChannel())) { - return false; - } - - $fp = $this->_openPackageFile($info->getPackage(), 'w', $info->getChannel()); - if ($fp === null) { - return false; - } - - $save = $info; - $info = $save->getArray(true); - $info['_lastmodified'] = time(); - fwrite($fp, serialize($info)); - $this->_closePackageFile($fp); - $this->_rebuildFileMap(); - return true; - } - - /** - * @param string Package name - * @param string Channel name - * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|null - * @access private - */ - function &_getPackage($package, $channel = 'pear.php.net') - { - $info = $this->_packageInfo($package, null, $channel); - if ($info === null) { - return $info; - } - - $a = $this->_config; - if (!$a) { - $this->_config = &new PEAR_Config; - $this->_config->set('php_dir', $this->statedir); - } - - if (!class_exists('PEAR_PackageFile')) { - require_once 'PEAR/PackageFile.php'; - } - - $pkg = &new PEAR_PackageFile($this->_config); - $pf = &$pkg->fromArray($info); - return $pf; - } - - /** - * @param string channel name - * @param bool whether to strictly retrieve channel names - * @return PEAR_ChannelFile|PEAR_Error - * @access private - */ - function &_getChannel($channel, $noaliases = false) - { - $ch = false; - if ($this->_channelExists($channel, $noaliases)) { - $chinfo = $this->_channelInfo($channel, $noaliases); - if ($chinfo) { - if (!class_exists('PEAR_ChannelFile')) { - require_once 'PEAR/ChannelFile.php'; - } - - $ch = &PEAR_ChannelFile::fromArrayWithErrors($chinfo); - } - } - - if ($ch) { - if ($ch->validate()) { - return $ch; - } - - foreach ($ch->getErrors(true) as $err) { - $message = $err['message'] . "\n"; - } - - $ch = PEAR::raiseError($message); - return $ch; - } - - if ($this->_getChannelFromAlias($channel) == 'pear.php.net') { - // the registry is not properly set up, so use defaults - if (!class_exists('PEAR_ChannelFile')) { - require_once 'PEAR/ChannelFile.php'; - } - - $pear_channel = new PEAR_ChannelFile; - $pear_channel->setServer('pear.php.net'); - $pear_channel->setAlias('pear'); - $pear_channel->setSummary('PHP Extension and Application Repository'); - $pear_channel->setDefaultPEARProtocols(); - $pear_channel->setBaseURL('REST1.0', 'http://pear.php.net/rest/'); - $pear_channel->setBaseURL('REST1.1', 'http://pear.php.net/rest/'); - $pear_channel->setBaseURL('REST1.3', 'http://pear.php.net/rest/'); - return $pear_channel; - } - - if ($this->_getChannelFromAlias($channel) == 'pecl.php.net') { - // the registry is not properly set up, so use defaults - if (!class_exists('PEAR_ChannelFile')) { - require_once 'PEAR/ChannelFile.php'; - } - $pear_channel = new PEAR_ChannelFile; - $pear_channel->setServer('pecl.php.net'); - $pear_channel->setAlias('pecl'); - $pear_channel->setSummary('PHP Extension Community Library'); - $pear_channel->setDefaultPEARProtocols(); - $pear_channel->setBaseURL('REST1.0', 'http://pecl.php.net/rest/'); - $pear_channel->setBaseURL('REST1.1', 'http://pecl.php.net/rest/'); - $pear_channel->setValidationPackage('PEAR_Validator_PECL', '1.0'); - return $pear_channel; - } - - if ($this->_getChannelFromAlias($channel) == 'doc.php.net') { - // the registry is not properly set up, so use defaults - if (!class_exists('PEAR_ChannelFile')) { - require_once 'PEAR/ChannelFile.php'; - } - - $doc_channel = new PEAR_ChannelFile; - $doc_channel->setServer('doc.php.net'); - $doc_channel->setAlias('phpdocs'); - $doc_channel->setSummary('PHP Documentation Team'); - $doc_channel->setDefaultPEARProtocols(); - $doc_channel->setBaseURL('REST1.0', 'http://doc.php.net/rest/'); - $doc_channel->setBaseURL('REST1.1', 'http://doc.php.net/rest/'); - $doc_channel->setBaseURL('REST1.3', 'http://doc.php.net/rest/'); - return $doc_channel; - } - - - if ($this->_getChannelFromAlias($channel) == '__uri') { - // the registry is not properly set up, so use defaults - if (!class_exists('PEAR_ChannelFile')) { - require_once 'PEAR/ChannelFile.php'; - } - - $private = new PEAR_ChannelFile; - $private->setName('__uri'); - $private->setDefaultPEARProtocols(); - $private->setBaseURL('REST1.0', '****'); - $private->setSummary('Pseudo-channel for static packages'); - return $private; - } - - return $ch; - } - - /** - * @param string Package name - * @param string Channel name - * @return bool - */ - function packageExists($package, $channel = 'pear.php.net') - { - if (PEAR::isError($e = $this->_lock(LOCK_SH))) { - return $e; - } - $ret = $this->_packageExists($package, $channel); - $this->_unlock(); - return $ret; - } - - /** - * @param string channel name - * @param bool if true, then aliases will be ignored - * @return bool - */ - function channelExists($channel, $noaliases = false) - { - if (PEAR::isError($e = $this->_lock(LOCK_SH))) { - return $e; - } - $ret = $this->_channelExists($channel, $noaliases); - $this->_unlock(); - return $ret; - } - - /** - * @param string channel name mirror is in - * @param string mirror name - * - * @return bool - */ - function mirrorExists($channel, $mirror) - { - if (PEAR::isError($e = $this->_lock(LOCK_SH))) { - return $e; - } - - $ret = $this->_mirrorExists($channel, $mirror); - $this->_unlock(); - return $ret; - } - - /** - * Determines whether the parameter is an alias of a channel - * @param string - * @return bool - */ - function isAlias($alias) - { - if (PEAR::isError($e = $this->_lock(LOCK_SH))) { - return $e; - } - $ret = $this->_isChannelAlias($alias); - $this->_unlock(); - return $ret; - } - - /** - * @param string|null - * @param string|null - * @param string - * @return array|null - */ - function packageInfo($package = null, $key = null, $channel = 'pear.php.net') - { - if (PEAR::isError($e = $this->_lock(LOCK_SH))) { - return $e; - } - $ret = $this->_packageInfo($package, $key, $channel); - $this->_unlock(); - return $ret; - } - - /** - * Retrieve a raw array of channel data. - * - * Do not use this, instead use {@link getChannel()} for normal - * operations. Array structure is undefined in this method - * @param string channel name - * @param bool whether to strictly retrieve information only on non-aliases - * @return array|null|PEAR_Error - */ - function channelInfo($channel = null, $noaliases = false) - { - if (PEAR::isError($e = $this->_lock(LOCK_SH))) { - return $e; - } - $ret = $this->_channelInfo($channel, $noaliases); - $this->_unlock(); - return $ret; - } - - /** - * @param string - */ - function channelName($channel) - { - if (PEAR::isError($e = $this->_lock(LOCK_SH))) { - return $e; - } - $ret = $this->_getChannelFromAlias($channel); - $this->_unlock(); - return $ret; - } - - /** - * @param string - */ - function channelAlias($channel) - { - if (PEAR::isError($e = $this->_lock(LOCK_SH))) { - return $e; - } - $ret = $this->_getAlias($channel); - $this->_unlock(); - return $ret; - } - - function listPackages($channel = false) - { - if (PEAR::isError($e = $this->_lock(LOCK_SH))) { - return $e; - } - $ret = $this->_listPackages($channel); - $this->_unlock(); - return $ret; - } - - function listAllPackages() - { - if (PEAR::isError($e = $this->_lock(LOCK_SH))) { - return $e; - } - $ret = $this->_listAllPackages(); - $this->_unlock(); - return $ret; - } - - function listChannels() - { - if (PEAR::isError($e = $this->_lock(LOCK_SH))) { - return $e; - } - $ret = $this->_listChannels(); - $this->_unlock(); - return $ret; - } - - /** - * Add an installed package to the registry - * @param string|PEAR_PackageFile_v1|PEAR_PackageFile_v2 package name or object - * that will be passed to {@link addPackage2()} - * @param array package info (parsed by PEAR_Common::infoFrom*() methods) - * @return bool success of saving - */ - function addPackage($package, $info) - { - if (is_object($info)) { - return $this->addPackage2($info); - } - if (PEAR::isError($e = $this->_lock(LOCK_EX))) { - return $e; - } - $ret = $this->_addPackage($package, $info); - $this->_unlock(); - if ($ret) { - if (!class_exists('PEAR_PackageFile_v1')) { - require_once 'PEAR/PackageFile/v1.php'; - } - $pf = new PEAR_PackageFile_v1; - $pf->setConfig($this->_config); - $pf->fromArray($info); - $this->_dependencyDB->uninstallPackage($pf); - $this->_dependencyDB->installPackage($pf); - } - return $ret; - } - - function addPackage2($info) - { - if (!is_object($info)) { - return $this->addPackage($info['package'], $info); - } - if (PEAR::isError($e = $this->_lock(LOCK_EX))) { - return $e; - } - $ret = $this->_addPackage2($info); - $this->_unlock(); - if ($ret) { - $this->_dependencyDB->uninstallPackage($info); - $this->_dependencyDB->installPackage($info); - } - return $ret; - } - - /** - * For future expandibility purposes, separate this - * @param PEAR_ChannelFile - */ - function updateChannel($channel, $lastmodified = null) - { - if ($channel->getName() == '__uri') { - return false; - } - return $this->addChannel($channel, $lastmodified, true); - } - - /** - * Deletion fails if there are any packages installed from the channel - * @param string|PEAR_ChannelFile channel name - * @return boolean|PEAR_Error True on deletion, false if it doesn't exist - */ - function deleteChannel($channel) - { - if (PEAR::isError($e = $this->_lock(LOCK_EX))) { - return $e; - } - - $ret = $this->_deleteChannel($channel); - $this->_unlock(); - if ($ret && is_a($this->_config, 'PEAR_Config')) { - $this->_config->setChannels($this->listChannels()); - } - - return $ret; - } - - /** - * @param PEAR_ChannelFile Channel object - * @param string Last-Modified header from HTTP for caching - * @return boolean|PEAR_Error True on creation, false if it already exists - */ - function addChannel($channel, $lastmodified = false, $update = false) - { - if (!is_a($channel, 'PEAR_ChannelFile') || !$channel->validate()) { - if (!$channel->validate()) { - $msg = ''; - $errors = $channel->getErrors(); - foreach ($errors as $error) { - $msg .= $error['message'] . ', '; - } - return PEAR::raiseError(substr($msg, 0, -2)); - } - return false; - } - - if (PEAR::isError($e = $this->_lock(LOCK_EX))) { - return $e; - } - - $ret = $this->_addChannel($channel, $update, $lastmodified); - $this->_unlock(); - if (!$update && $ret && is_a($this->_config, 'PEAR_Config')) { - $this->_config->setChannels($this->listChannels()); - } - - return $ret; - } - - function deletePackage($package, $channel = 'pear.php.net') - { - if (PEAR::isError($e = $this->_lock(LOCK_EX))) { - return $e; - } - - $file = $this->_packageFileName($package, $channel); - $ret = file_exists($file) ? @unlink($file) : false; - $this->_rebuildFileMap(); - $this->_unlock(); - $p = array('channel' => $channel, 'package' => $package); - $this->_dependencyDB->uninstallPackage($p); - return $ret; - } - - function updatePackage($package, $info, $merge = true) - { - if (is_object($info)) { - return $this->updatePackage2($info, $merge); - } - if (PEAR::isError($e = $this->_lock(LOCK_EX))) { - return $e; - } - $ret = $this->_updatePackage($package, $info, $merge); - $this->_unlock(); - if ($ret) { - if (!class_exists('PEAR_PackageFile_v1')) { - require_once 'PEAR/PackageFile/v1.php'; - } - $pf = new PEAR_PackageFile_v1; - $pf->setConfig($this->_config); - $pf->fromArray($this->packageInfo($package)); - $this->_dependencyDB->uninstallPackage($pf); - $this->_dependencyDB->installPackage($pf); - } - return $ret; - } - - function updatePackage2($info) - { - - if (!is_object($info)) { - return $this->updatePackage($info['package'], $info, $merge); - } - - if (!$info->validate(PEAR_VALIDATE_DOWNLOADING)) { - return false; - } - - if (PEAR::isError($e = $this->_lock(LOCK_EX))) { - return $e; - } - - $ret = $this->_updatePackage2($info); - $this->_unlock(); - if ($ret) { - $this->_dependencyDB->uninstallPackage($info); - $this->_dependencyDB->installPackage($info); - } - - return $ret; - } - - /** - * @param string channel name - * @param bool whether to strictly return raw channels (no aliases) - * @return PEAR_ChannelFile|PEAR_Error - */ - function &getChannel($channel, $noaliases = false) - { - if (PEAR::isError($e = $this->_lock(LOCK_SH))) { - return $e; - } - $ret = &$this->_getChannel($channel, $noaliases); - $this->_unlock(); - if (!$ret) { - return PEAR::raiseError('Unknown channel: ' . $channel); - } - return $ret; - } - - /** - * @param string package name - * @param string channel name - * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|null - */ - function &getPackage($package, $channel = 'pear.php.net') - { - if (PEAR::isError($e = $this->_lock(LOCK_SH))) { - return $e; - } - $pf = &$this->_getPackage($package, $channel); - $this->_unlock(); - return $pf; - } - - /** - * Get PEAR_PackageFile_v[1/2] objects representing the contents of - * a dependency group that are installed. - * - * This is used at uninstall-time - * @param array - * @return array|false - */ - function getInstalledGroup($group) - { - $ret = array(); - if (isset($group['package'])) { - if (!isset($group['package'][0])) { - $group['package'] = array($group['package']); - } - foreach ($group['package'] as $package) { - $depchannel = isset($package['channel']) ? $package['channel'] : '__uri'; - $p = &$this->getPackage($package['name'], $depchannel); - if ($p) { - $save = &$p; - $ret[] = &$save; - } - } - } - if (isset($group['subpackage'])) { - if (!isset($group['subpackage'][0])) { - $group['subpackage'] = array($group['subpackage']); - } - foreach ($group['subpackage'] as $package) { - $depchannel = isset($package['channel']) ? $package['channel'] : '__uri'; - $p = &$this->getPackage($package['name'], $depchannel); - if ($p) { - $save = &$p; - $ret[] = &$save; - } - } - } - if (!count($ret)) { - return false; - } - return $ret; - } - - /** - * @param string channel name - * @return PEAR_Validate|false - */ - function &getChannelValidator($channel) - { - $chan = $this->getChannel($channel); - if (PEAR::isError($chan)) { - return $chan; - } - $val = $chan->getValidationObject(); - return $val; - } - - /** - * @param string channel name - * @return array an array of PEAR_ChannelFile objects representing every installed channel - */ - function &getChannels() - { - $ret = array(); - if (PEAR::isError($e = $this->_lock(LOCK_SH))) { - return $e; - } - foreach ($this->_listChannels() as $channel) { - $e = &$this->_getChannel($channel); - if (!$e || PEAR::isError($e)) { - continue; - } - $ret[] = $e; - } - $this->_unlock(); - return $ret; - } - - /** - * Test whether a file or set of files belongs to a package. - * - * If an array is passed in - * @param string|array file path, absolute or relative to the pear - * install dir - * @param string|array name of PEAR package or array('package' => name, 'channel' => - * channel) of a package that will be ignored - * @param string API version - 1.1 will exclude any files belonging to a package - * @param array private recursion variable - * @return array|false which package and channel the file belongs to, or an empty - * string if the file does not belong to an installed package, - * or belongs to the second parameter's package - */ - function checkFileMap($path, $package = false, $api = '1.0', $attrs = false) - { - if (is_array($path)) { - static $notempty; - if (empty($notempty)) { - if (!class_exists('PEAR_Installer_Role')) { - require_once 'PEAR/Installer/Role.php'; - } - $notempty = create_function('$a','return !empty($a);'); - } - $package = is_array($package) ? array(strtolower($package[0]), strtolower($package[1])) - : strtolower($package); - $pkgs = array(); - foreach ($path as $name => $attrs) { - if (is_array($attrs)) { - if (isset($attrs['install-as'])) { - $name = $attrs['install-as']; - } - if (!in_array($attrs['role'], PEAR_Installer_Role::getInstallableRoles())) { - // these are not installed - continue; - } - if (!in_array($attrs['role'], PEAR_Installer_Role::getBaseinstallRoles())) { - $attrs['baseinstalldir'] = is_array($package) ? $package[1] : $package; - } - if (isset($attrs['baseinstalldir'])) { - $name = $attrs['baseinstalldir'] . DIRECTORY_SEPARATOR . $name; - } - } - $pkgs[$name] = $this->checkFileMap($name, $package, $api, $attrs); - if (PEAR::isError($pkgs[$name])) { - return $pkgs[$name]; - } - } - return array_filter($pkgs, $notempty); - } - if (empty($this->filemap_cache)) { - if (PEAR::isError($e = $this->_lock(LOCK_SH))) { - return $e; - } - $err = $this->_readFileMap(); - $this->_unlock(); - if (PEAR::isError($err)) { - return $err; - } - } - if (!$attrs) { - $attrs = array('role' => 'php'); // any old call would be for PHP role only - } - if (isset($this->filemap_cache[$attrs['role']][$path])) { - if ($api >= '1.1' && $this->filemap_cache[$attrs['role']][$path] == $package) { - return false; - } - return $this->filemap_cache[$attrs['role']][$path]; - } - $l = strlen($this->install_dir); - if (substr($path, 0, $l) == $this->install_dir) { - $path = preg_replace('!^'.DIRECTORY_SEPARATOR.'+!', '', substr($path, $l)); - } - if (isset($this->filemap_cache[$attrs['role']][$path])) { - if ($api >= '1.1' && $this->filemap_cache[$attrs['role']][$path] == $package) { - return false; - } - return $this->filemap_cache[$attrs['role']][$path]; - } - return false; - } - - /** - * Force a reload of the filemap - * @since 1.5.0RC3 - */ - function flushFileMap() - { - $this->filemap_cache = null; - clearstatcache(); // ensure that the next read gets the full, current filemap - } - - /** - * Get the expected API version. Channels API is version 1.1, as it is backwards - * compatible with 1.0 - * @return string - */ - function apiVersion() - { - return '1.1'; - } - - /** - * Parse a package name, or validate a parsed package name array - * @param string|array pass in an array of format - * array( - * 'package' => 'pname', - * ['channel' => 'channame',] - * ['version' => 'version',] - * ['state' => 'state',] - * ['group' => 'groupname']) - * or a string of format - * [channel://][channame/]pname[-version|-state][/group=groupname] - * @return array|PEAR_Error - */ - function parsePackageName($param, $defaultchannel = 'pear.php.net') - { - $saveparam = $param; - if (is_array($param)) { - // convert to string for error messages - $saveparam = $this->parsedPackageNameToString($param); - // process the array - if (!isset($param['package'])) { - return PEAR::raiseError('parsePackageName(): array $param ' . - 'must contain a valid package name in index "param"', - 'package', null, null, $param); - } - if (!isset($param['uri'])) { - if (!isset($param['channel'])) { - $param['channel'] = $defaultchannel; - } - } else { - $param['channel'] = '__uri'; - } - } else { - $components = @parse_url((string) $param); - if (isset($components['scheme'])) { - if ($components['scheme'] == 'http') { - // uri package - $param = array('uri' => $param, 'channel' => '__uri'); - } elseif($components['scheme'] != 'channel') { - return PEAR::raiseError('parsePackageName(): only channel:// uris may ' . - 'be downloaded, not "' . $param . '"', 'invalid', null, null, $param); - } - } - if (!isset($components['path'])) { - return PEAR::raiseError('parsePackageName(): array $param ' . - 'must contain a valid package name in "' . $param . '"', - 'package', null, null, $param); - } - if (isset($components['host'])) { - // remove the leading "/" - $components['path'] = substr($components['path'], 1); - } - if (!isset($components['scheme'])) { - if (strpos($components['path'], '/') !== false) { - if ($components['path']{0} == '/') { - return PEAR::raiseError('parsePackageName(): this is not ' . - 'a package name, it begins with "/" in "' . $param . '"', - 'invalid', null, null, $param); - } - $parts = explode('/', $components['path']); - $components['host'] = array_shift($parts); - if (count($parts) > 1) { - $components['path'] = array_pop($parts); - $components['host'] .= '/' . implode('/', $parts); - } else { - $components['path'] = implode('/', $parts); - } - } else { - $components['host'] = $defaultchannel; - } - } else { - if (strpos($components['path'], '/')) { - $parts = explode('/', $components['path']); - $components['path'] = array_pop($parts); - $components['host'] .= '/' . implode('/', $parts); - } - } - - if (is_array($param)) { - $param['package'] = $components['path']; - } else { - $param = array( - 'package' => $components['path'] - ); - if (isset($components['host'])) { - $param['channel'] = $components['host']; - } - } - if (isset($components['fragment'])) { - $param['group'] = $components['fragment']; - } - if (isset($components['user'])) { - $param['user'] = $components['user']; - } - if (isset($components['pass'])) { - $param['pass'] = $components['pass']; - } - if (isset($components['query'])) { - parse_str($components['query'], $param['opts']); - } - // check for extension - $pathinfo = pathinfo($param['package']); - if (isset($pathinfo['extension']) && - in_array(strtolower($pathinfo['extension']), array('tgz', 'tar'))) { - $param['extension'] = $pathinfo['extension']; - $param['package'] = substr($pathinfo['basename'], 0, - strlen($pathinfo['basename']) - 4); - } - // check for version - if (strpos($param['package'], '-')) { - $test = explode('-', $param['package']); - if (count($test) != 2) { - return PEAR::raiseError('parsePackageName(): only one version/state ' . - 'delimiter "-" is allowed in "' . $saveparam . '"', - 'version', null, null, $param); - } - list($param['package'], $param['version']) = $test; - } - } - // validation - $info = $this->channelExists($param['channel']); - if (PEAR::isError($info)) { - return $info; - } - if (!$info) { - return PEAR::raiseError('unknown channel "' . $param['channel'] . - '" in "' . $saveparam . '"', 'channel', null, null, $param); - } - $chan = $this->getChannel($param['channel']); - if (PEAR::isError($chan)) { - return $chan; - } - if (!$chan) { - return PEAR::raiseError("Exception: corrupt registry, could not " . - "retrieve channel " . $param['channel'] . " information", - 'registry', null, null, $param); - } - $param['channel'] = $chan->getName(); - $validate = $chan->getValidationObject(); - $vpackage = $chan->getValidationPackage(); - // validate package name - if (!$validate->validPackageName($param['package'], $vpackage['_content'])) { - return PEAR::raiseError('parsePackageName(): invalid package name "' . - $param['package'] . '" in "' . $saveparam . '"', - 'package', null, null, $param); - } - if (isset($param['group'])) { - if (!PEAR_Validate::validGroupName($param['group'])) { - return PEAR::raiseError('parsePackageName(): dependency group "' . $param['group'] . - '" is not a valid group name in "' . $saveparam . '"', 'group', null, null, - $param); - } - } - if (isset($param['state'])) { - if (!in_array(strtolower($param['state']), $validate->getValidStates())) { - return PEAR::raiseError('parsePackageName(): state "' . $param['state'] - . '" is not a valid state in "' . $saveparam . '"', - 'state', null, null, $param); - } - } - if (isset($param['version'])) { - if (isset($param['state'])) { - return PEAR::raiseError('parsePackageName(): cannot contain both ' . - 'a version and a stability (state) in "' . $saveparam . '"', - 'version/state', null, null, $param); - } - // check whether version is actually a state - if (in_array(strtolower($param['version']), $validate->getValidStates())) { - $param['state'] = strtolower($param['version']); - unset($param['version']); - } else { - if (!$validate->validVersion($param['version'])) { - return PEAR::raiseError('parsePackageName(): "' . $param['version'] . - '" is neither a valid version nor a valid state in "' . - $saveparam . '"', 'version/state', null, null, $param); - } - } - } - return $param; - } - - /** - * @param array - * @return string - */ - function parsedPackageNameToString($parsed, $brief = false) - { - if (is_string($parsed)) { - return $parsed; - } - if (is_object($parsed)) { - $p = $parsed; - $parsed = array( - 'package' => $p->getPackage(), - 'channel' => $p->getChannel(), - 'version' => $p->getVersion(), - ); - } - if (isset($parsed['uri'])) { - return $parsed['uri']; - } - if ($brief) { - if ($channel = $this->channelAlias($parsed['channel'])) { - return $channel . '/' . $parsed['package']; - } - } - $upass = ''; - if (isset($parsed['user'])) { - $upass = $parsed['user']; - if (isset($parsed['pass'])) { - $upass .= ':' . $parsed['pass']; - } - $upass = "$upass@"; - } - $ret = 'channel://' . $upass . $parsed['channel'] . '/' . $parsed['package']; - if (isset($parsed['version']) || isset($parsed['state'])) { - $ver = isset($parsed['version']) ? $parsed['version'] : ''; - $ver .= isset($parsed['state']) ? $parsed['state'] : ''; - $ret .= '-' . $ver; - } - if (isset($parsed['extension'])) { - $ret .= '.' . $parsed['extension']; - } - if (isset($parsed['opts'])) { - $ret .= '?'; - foreach ($parsed['opts'] as $name => $value) { - $parsed['opts'][$name] = "$name=$value"; - } - $ret .= implode('&', $parsed['opts']); - } - if (isset($parsed['group'])) { - $ret .= '#' . $parsed['group']; - } - return $ret; - } -} diff --git a/pear/PEAR/RunTest.php b/pear/PEAR/RunTest.php deleted file mode 100644 index a916af4..0000000 --- a/pear/PEAR/RunTest.php +++ /dev/null @@ -1,973 +0,0 @@ - - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.3.3 - */ - -/** - * for error handling - */ -require_once 'PEAR.php'; -require_once 'PEAR/Config.php'; - -define('DETAILED', 1); -putenv("PHP_PEAR_RUNTESTS=1"); - -/** - * Simplified version of PHP's test suite - * - * Try it with: - * - * $ php -r 'include "../PEAR/RunTest.php"; $t=new PEAR_RunTest; $o=$t->run("./pear_system.phpt");print_r($o);' - * - * - * @category pear - * @package PEAR - * @author Tomas V.V.Cox - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.3.3 - */ -class PEAR_RunTest -{ - var $_headers = array(); - var $_logger; - var $_options; - var $_php; - var $tests_count; - var $xdebug_loaded; - /** - * Saved value of php executable, used to reset $_php when we - * have a test that uses cgi - * - * @var unknown_type - */ - var $_savephp; - var $ini_overwrites = array( - 'output_handler=', - 'open_basedir=', - 'safe_mode=0', - 'disable_functions=', - 'output_buffering=Off', - 'display_errors=1', - 'log_errors=0', - 'html_errors=0', - 'track_errors=1', - 'report_memleaks=0', - 'report_zend_debug=0', - 'docref_root=', - 'docref_ext=.html', - 'error_prepend_string=', - 'error_append_string=', - 'auto_prepend_file=', - 'auto_append_file=', - 'magic_quotes_runtime=0', - 'xdebug.default_enable=0', - 'allow_url_fopen=1', - ); - - /** - * An object that supports the PEAR_Common->log() signature, or null - * @param PEAR_Common|null - */ - function PEAR_RunTest($logger = null, $options = array()) - { - if (!defined('E_DEPRECATED')) { - define('E_DEPRECATED', 0); - } - if (!defined('E_STRICT')) { - define('E_STRICT', 0); - } - $this->ini_overwrites[] = 'error_reporting=' . (E_ALL & ~(E_DEPRECATED | E_STRICT)); - if (is_null($logger)) { - require_once 'PEAR/Common.php'; - $logger = new PEAR_Common; - } - $this->_logger = $logger; - $this->_options = $options; - - $conf = &PEAR_Config::singleton(); - $this->_php = $conf->get('php_bin'); - } - - /** - * Taken from php-src/run-tests.php - * - * @param string $commandline command name - * @param array $env - * @param string $stdin standard input to pass to the command - * @return unknown - */ - function system_with_timeout($commandline, $env = null, $stdin = null) - { - $data = ''; - if (version_compare(phpversion(), '5.0.0', '<')) { - $proc = proc_open($commandline, array( - 0 => array('pipe', 'r'), - 1 => array('pipe', 'w'), - 2 => array('pipe', 'w') - ), $pipes); - } else { - $proc = proc_open($commandline, array( - 0 => array('pipe', 'r'), - 1 => array('pipe', 'w'), - 2 => array('pipe', 'w') - ), $pipes, null, $env, array('suppress_errors' => true)); - } - - if (!$proc) { - return false; - } - - if (is_string($stdin)) { - fwrite($pipes[0], $stdin); - } - fclose($pipes[0]); - - while (true) { - /* hide errors from interrupted syscalls */ - $r = $pipes; - $e = $w = null; - $n = @stream_select($r, $w, $e, 60); - - if ($n === 0) { - /* timed out */ - $data .= "\n ** ERROR: process timed out **\n"; - proc_terminate($proc); - return array(1234567890, $data); - } else if ($n > 0) { - $line = fread($pipes[1], 8192); - if (strlen($line) == 0) { - /* EOF */ - break; - } - $data .= $line; - } - } - if (function_exists('proc_get_status')) { - $stat = proc_get_status($proc); - if ($stat['signaled']) { - $data .= "\nTermsig=".$stat['stopsig']; - } - } - $code = proc_close($proc); - if (function_exists('proc_get_status')) { - $code = $stat['exitcode']; - } - return array($code, $data); - } - - /** - * Turns a PHP INI string into an array - * - * Turns -d "include_path=/foo/bar" into this: - * array( - * 'include_path' => array( - * 'operator' => '-d', - * 'value' => '/foo/bar', - * ) - * ) - * Works both with quotes and without - * - * @param string an PHP INI string, -d "include_path=/foo/bar" - * @return array - */ - function iniString2array($ini_string) - { - if (!$ini_string) { - return array(); - } - $split = preg_split('/[\s]|=/', $ini_string, -1, PREG_SPLIT_NO_EMPTY); - $key = $split[1][0] == '"' ? substr($split[1], 1) : $split[1]; - $value = $split[2][strlen($split[2]) - 1] == '"' ? substr($split[2], 0, -1) : $split[2]; - // FIXME review if this is really the struct to go with - $array = array($key => array('operator' => $split[0], 'value' => $value)); - return $array; - } - - function settings2array($settings, $ini_settings) - { - foreach ($settings as $setting) { - if (strpos($setting, '=') !== false) { - $setting = explode('=', $setting, 2); - $name = trim(strtolower($setting[0])); - $value = trim($setting[1]); - $ini_settings[$name] = $value; - } - } - return $ini_settings; - } - - function settings2params($ini_settings) - { - $settings = ''; - foreach ($ini_settings as $name => $value) { - if (is_array($value)) { - $operator = $value['operator']; - $value = $value['value']; - } else { - $operator = '-d'; - } - $value = addslashes($value); - $settings .= " $operator \"$name=$value\""; - } - return $settings; - } - - function _preparePhpBin($php, $file, $ini_settings) - { - $file = escapeshellarg($file); - // This was fixed in php 5.3 and is not needed after that - if (OS_WINDOWS && version_compare(PHP_VERSION, '5.3', '<')) { - $cmd = '"'.escapeshellarg($php).' '.$ini_settings.' -f ' . $file .'"'; - } else { - $cmd = $php . $ini_settings . ' -f ' . $file; - } - - return $cmd; - } - - function runPHPUnit($file, $ini_settings = '') - { - if (!file_exists($file) && file_exists(getcwd() . DIRECTORY_SEPARATOR . $file)) { - $file = realpath(getcwd() . DIRECTORY_SEPARATOR . $file); - } elseif (file_exists($file)) { - $file = realpath($file); - } - - $cmd = $this->_preparePhpBin($this->_php, $file, $ini_settings); - if (isset($this->_logger)) { - $this->_logger->log(2, 'Running command "' . $cmd . '"'); - } - - $savedir = getcwd(); // in case the test moves us around - chdir(dirname($file)); - echo `$cmd`; - chdir($savedir); - return 'PASSED'; // we have no way of knowing this information so assume passing - } - - /** - * Runs an individual test case. - * - * @param string The filename of the test - * @param array|string INI settings to be applied to the test run - * @param integer Number what the current running test is of the - * whole test suite being runned. - * - * @return string|object Returns PASSED, WARNED, FAILED depending on how the - * test came out. - * PEAR Error when the tester it self fails - */ - function run($file, $ini_settings_cmd_line = array(), $test_number = 1) - { - if (isset($this->_savephp)) { - $this->_php = $this->_savephp; - unset($this->_savephp); - } - if (empty($this->_options['cgi'])) { - // try to see if php-cgi is in the path - $res = $this->system_with_timeout('php-cgi -v'); - if (false !== $res && !(is_array($res) && in_array($res[0], array(-1, 127)))) { - $this->_options['cgi'] = 'php-cgi'; - } - } - if (1 < $len = strlen($this->tests_count)) { - $test_number = str_pad($test_number, $len, ' ', STR_PAD_LEFT); - $test_nr = "[$test_number/$this->tests_count] "; - } else { - $test_nr = ''; - } - - $file = realpath($file); - $section_text = $this->_readFile($file); - if (PEAR::isError($section_text)) { - return $section_text; - } - - if (isset($section_text['POST_RAW']) && isset($section_text['UPLOAD'])) { - return PEAR::raiseError("Cannot contain both POST_RAW and UPLOAD in test file: $file"); - } - - $cwd = getcwd(); - - $pass_options = ''; - if (!empty($this->_options['ini'])) { - $pass_options = $this->_options['ini']; - } - - if (is_string($ini_settings_cmd_line)) { - $ini_settings_cmd_line = $this->iniString2array($ini_settings_cmd_line); - } - - $ini_settings_base = $this->settings2array($this->ini_overwrites, $ini_settings_cmd_line); - if ($section_text['INI']) { - if (strpos($section_text['INI'], '{PWD}') !== false) { - $section_text['INI'] = str_replace('{PWD}', dirname($file), $section_text['INI']); - } - $ini = preg_split( "/[\n\r]+/", $section_text['INI']); - $ini_settings_phpt_section = $this->settings2array($ini, array()); - $ini_settings_phpt_section = $this->settings2params($ini_settings_phpt_section); - } else { - $ini_settings_phpt_section = ''; - } - $ini_settings_base = $this->settings2params($ini_settings_base); - $ini_settings_all = $ini_settings_base . ' ' . $ini_settings_phpt_section; - - $shortname = str_replace($cwd . DIRECTORY_SEPARATOR, '', $file); - - $tested = trim($section_text['TEST']); - $tested.= !isset($this->_options['simple']) ? "[$shortname]" : ' '; - - if (!empty($section_text['POST']) || !empty($section_text['POST_RAW']) || - !empty($section_text['UPLOAD']) || !empty($section_text['GET']) || - !empty($section_text['COOKIE']) || !empty($section_text['EXPECTHEADERS'])) { - if (empty($this->_options['cgi'])) { - if (!isset($this->_options['quiet'])) { - $this->_logger->log(0, "SKIP $test_nr$tested (reason: --cgi option needed for this test, type 'pear help run-tests')"); - } - if (isset($this->_options['tapoutput'])) { - return array('ok', ' # skip --cgi option needed for this test, "pear help run-tests" for info'); - } - return 'SKIPPED'; - } - $this->_savephp = $this->_php; - $this->_php = $this->_options['cgi']; - } - - $temp_dir = realpath(dirname($file)); - $main_file_name = basename($file, 'phpt'); - $diff_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'diff'; - $log_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'log'; - $exp_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'exp'; - $output_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'out'; - $memcheck_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'mem'; - $temp_file = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'php'; - $temp_skipif = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'skip.php'; - $temp_clean = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'clean.php'; - $tmp_post = $temp_dir . DIRECTORY_SEPARATOR . uniqid('phpt.'); - - // unlink old test results - $this->_cleanupOldFiles($file); - - // Check if test should be skipped. - $res = $this->_runSkipIf($section_text, $temp_skipif, $tested, $ini_settings_base); - if (count($res) != 2) { - return $res; - } - $info = $res['info']; - $warn = $res['warn']; - - // We've satisfied the preconditions - run the test! - if (isset($this->_options['coverage']) && $this->xdebug_loaded) { - $xdebug_file = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'xdebug'; - $text = "\n" . 'function coverage_shutdown() {' . - "\n" . ' $xdebug = var_export(xdebug_get_code_coverage(), true);'; - if (!function_exists('file_put_contents')) { - $text .= "\n" . ' $fh = fopen(\'' . $xdebug_file . '\', "wb");' . - "\n" . ' if ($fh !== false) {' . - "\n" . ' fwrite($fh, $xdebug);' . - "\n" . ' fclose($fh);' . - "\n" . ' }'; - } else { - $text .= "\n" . ' file_put_contents(\'' . $xdebug_file . '\', $xdebug);'; - } - - // Workaround for http://pear.php.net/bugs/bug.php?id=17292 - $lines = explode("\n", $section_text['FILE']); - $numLines = count($lines); - $namespace = ''; - $coverage_shutdown = 'coverage_shutdown'; - - if ( - substr($lines[0], 0, 2) == 'save_text($temp_file, "save_text($temp_file, $section_text['FILE']); - } - - $args = $section_text['ARGS'] ? ' -- '.$section_text['ARGS'] : ''; - $cmd = $this->_preparePhpBin($this->_php, $temp_file, $ini_settings_all); - $cmd.= "$args 2>&1"; - if (isset($this->_logger)) { - $this->_logger->log(2, 'Running command "' . $cmd . '"'); - } - - // Reset environment from any previous test. - $env = $this->_resetEnv($section_text, $temp_file); - - $section_text = $this->_processUpload($section_text, $file); - if (PEAR::isError($section_text)) { - return $section_text; - } - - if (array_key_exists('POST_RAW', $section_text) && !empty($section_text['POST_RAW'])) { - $post = trim($section_text['POST_RAW']); - $raw_lines = explode("\n", $post); - - $request = ''; - $started = false; - foreach ($raw_lines as $i => $line) { - if (empty($env['CONTENT_TYPE']) && - preg_match('/^Content-Type:(.*)/i', $line, $res)) { - $env['CONTENT_TYPE'] = trim(str_replace("\r", '', $res[1])); - continue; - } - if ($started) { - $request .= "\n"; - } - $started = true; - $request .= $line; - } - - $env['CONTENT_LENGTH'] = strlen($request); - $env['REQUEST_METHOD'] = 'POST'; - - $this->save_text($tmp_post, $request); - $cmd = "$this->_php $pass_options $ini_settings_all \"$temp_file\" 2>&1 < \"$tmp_post\""; - } elseif (array_key_exists('POST', $section_text) && !empty($section_text['POST'])) { - $post = trim($section_text['POST']); - $this->save_text($tmp_post, $post); - $content_length = strlen($post); - - $env['REQUEST_METHOD'] = 'POST'; - $env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'; - $env['CONTENT_LENGTH'] = $content_length; - - $cmd = "$this->_php $pass_options $ini_settings_all \"$temp_file\" 2>&1 < \"$tmp_post\""; - } else { - $env['REQUEST_METHOD'] = 'GET'; - $env['CONTENT_TYPE'] = ''; - $env['CONTENT_LENGTH'] = ''; - } - - if (OS_WINDOWS && isset($section_text['RETURNS'])) { - ob_start(); - system($cmd, $return_value); - $out = ob_get_contents(); - ob_end_clean(); - $section_text['RETURNS'] = (int) trim($section_text['RETURNS']); - $returnfail = ($return_value != $section_text['RETURNS']); - } else { - $returnfail = false; - $stdin = isset($section_text['STDIN']) ? $section_text['STDIN'] : null; - $out = $this->system_with_timeout($cmd, $env, $stdin); - $return_value = $out[0]; - $out = $out[1]; - } - - $output = preg_replace('/\r\n/', "\n", trim($out)); - - if (isset($tmp_post) && realpath($tmp_post) && file_exists($tmp_post)) { - @unlink(realpath($tmp_post)); - } - chdir($cwd); // in case the test moves us around - - $this->_testCleanup($section_text, $temp_clean); - - /* when using CGI, strip the headers from the output */ - $output = $this->_stripHeadersCGI($output); - - if (isset($section_text['EXPECTHEADERS'])) { - $testheaders = $this->_processHeaders($section_text['EXPECTHEADERS']); - $missing = array_diff_assoc($testheaders, $this->_headers); - $changed = ''; - foreach ($missing as $header => $value) { - if (isset($this->_headers[$header])) { - $changed .= "-$header: $value\n+$header: "; - $changed .= $this->_headers[$header]; - } else { - $changed .= "-$header: $value\n"; - } - } - if ($missing) { - // tack on failed headers to output: - $output .= "\n====EXPECTHEADERS FAILURE====:\n$changed"; - } - } - // Does the output match what is expected? - do { - if (isset($section_text['EXPECTF']) || isset($section_text['EXPECTREGEX'])) { - if (isset($section_text['EXPECTF'])) { - $wanted = trim($section_text['EXPECTF']); - } else { - $wanted = trim($section_text['EXPECTREGEX']); - } - $wanted_re = preg_replace('/\r\n/', "\n", $wanted); - if (isset($section_text['EXPECTF'])) { - $wanted_re = preg_quote($wanted_re, '/'); - // Stick to basics - $wanted_re = str_replace("%s", ".+?", $wanted_re); //not greedy - $wanted_re = str_replace("%i", "[+\-]?[0-9]+", $wanted_re); - $wanted_re = str_replace("%d", "[0-9]+", $wanted_re); - $wanted_re = str_replace("%x", "[0-9a-fA-F]+", $wanted_re); - $wanted_re = str_replace("%f", "[+\-]?\.?[0-9]+\.?[0-9]*(E-?[0-9]+)?", $wanted_re); - $wanted_re = str_replace("%c", ".", $wanted_re); - // %f allows two points "-.0.0" but that is the best *simple* expression - } - - /* DEBUG YOUR REGEX HERE - var_dump($wanted_re); - print(str_repeat('=', 80) . "\n"); - var_dump($output); - */ - if (!$returnfail && preg_match("/^$wanted_re\$/s", $output)) { - if (file_exists($temp_file)) { - unlink($temp_file); - } - if (array_key_exists('FAIL', $section_text)) { - break; - } - if (!isset($this->_options['quiet'])) { - $this->_logger->log(0, "PASS $test_nr$tested$info"); - } - if (isset($this->_options['tapoutput'])) { - return array('ok', ' - ' . $tested); - } - return 'PASSED'; - } - } else { - if (isset($section_text['EXPECTFILE'])) { - $f = $temp_dir . '/' . trim($section_text['EXPECTFILE']); - if (!($fp = @fopen($f, 'rb'))) { - return PEAR::raiseError('--EXPECTFILE-- section file ' . - $f . ' not found'); - } - fclose($fp); - $section_text['EXPECT'] = file_get_contents($f); - } - - if (isset($section_text['EXPECT'])) { - $wanted = preg_replace('/\r\n/', "\n", trim($section_text['EXPECT'])); - } else { - $wanted = ''; - } - - // compare and leave on success - if (!$returnfail && 0 == strcmp($output, $wanted)) { - if (file_exists($temp_file)) { - unlink($temp_file); - } - if (array_key_exists('FAIL', $section_text)) { - break; - } - if (!isset($this->_options['quiet'])) { - $this->_logger->log(0, "PASS $test_nr$tested$info"); - } - if (isset($this->_options['tapoutput'])) { - return array('ok', ' - ' . $tested); - } - return 'PASSED'; - } - } - } while (false); - - if (array_key_exists('FAIL', $section_text)) { - // we expect a particular failure - // this is only used for testing PEAR_RunTest - $expectf = isset($section_text['EXPECTF']) ? $wanted_re : null; - $faildiff = $this->generate_diff($wanted, $output, null, $expectf); - $faildiff = preg_replace('/\r/', '', $faildiff); - $wanted = preg_replace('/\r/', '', trim($section_text['FAIL'])); - if ($faildiff == $wanted) { - if (!isset($this->_options['quiet'])) { - $this->_logger->log(0, "PASS $test_nr$tested$info"); - } - if (isset($this->_options['tapoutput'])) { - return array('ok', ' - ' . $tested); - } - return 'PASSED'; - } - unset($section_text['EXPECTF']); - $output = $faildiff; - if (isset($section_text['RETURNS'])) { - return PEAR::raiseError('Cannot have both RETURNS and FAIL in the same test: ' . - $file); - } - } - - // Test failed so we need to report details. - $txt = $warn ? 'WARN ' : 'FAIL '; - $this->_logger->log(0, $txt . $test_nr . $tested . $info); - - // write .exp - $res = $this->_writeLog($exp_filename, $wanted); - if (PEAR::isError($res)) { - return $res; - } - - // write .out - $res = $this->_writeLog($output_filename, $output); - if (PEAR::isError($res)) { - return $res; - } - - // write .diff - $returns = isset($section_text['RETURNS']) ? - array(trim($section_text['RETURNS']), $return_value) : null; - $expectf = isset($section_text['EXPECTF']) ? $wanted_re : null; - $data = $this->generate_diff($wanted, $output, $returns, $expectf); - $res = $this->_writeLog($diff_filename, $data); - if (PEAR::isError($res)) { - return $res; - } - - // write .log - $data = " ----- EXPECTED OUTPUT -$wanted ----- ACTUAL OUTPUT -$output ----- FAILED -"; - - if ($returnfail) { - $data .= " ----- EXPECTED RETURN -$section_text[RETURNS] ----- ACTUAL RETURN -$return_value -"; - } - - $res = $this->_writeLog($log_filename, $data); - if (PEAR::isError($res)) { - return $res; - } - - if (isset($this->_options['tapoutput'])) { - $wanted = explode("\n", $wanted); - $wanted = "# Expected output:\n#\n#" . implode("\n#", $wanted); - $output = explode("\n", $output); - $output = "#\n#\n# Actual output:\n#\n#" . implode("\n#", $output); - return array($wanted . $output . 'not ok', ' - ' . $tested); - } - return $warn ? 'WARNED' : 'FAILED'; - } - - function generate_diff($wanted, $output, $rvalue, $wanted_re) - { - $w = explode("\n", $wanted); - $o = explode("\n", $output); - $wr = explode("\n", $wanted_re); - $w1 = array_diff_assoc($w, $o); - $o1 = array_diff_assoc($o, $w); - $o2 = $w2 = array(); - foreach ($w1 as $idx => $val) { - if (!$wanted_re || !isset($wr[$idx]) || !isset($o1[$idx]) || - !preg_match('/^' . $wr[$idx] . '\\z/', $o1[$idx])) { - $w2[sprintf("%03d<", $idx)] = sprintf("%03d- ", $idx + 1) . $val; - } - } - foreach ($o1 as $idx => $val) { - if (!$wanted_re || !isset($wr[$idx]) || - !preg_match('/^' . $wr[$idx] . '\\z/', $val)) { - $o2[sprintf("%03d>", $idx)] = sprintf("%03d+ ", $idx + 1) . $val; - } - } - $diff = array_merge($w2, $o2); - ksort($diff); - $extra = $rvalue ? "##EXPECTED: $rvalue[0]\r\n##RETURNED: $rvalue[1]" : ''; - return implode("\r\n", $diff) . $extra; - } - - // Write the given text to a temporary file, and return the filename. - function save_text($filename, $text) - { - if (!$fp = fopen($filename, 'w')) { - return PEAR::raiseError("Cannot open file '" . $filename . "' (save_text)"); - } - fwrite($fp, $text); - fclose($fp); - if (1 < DETAILED) echo " -FILE $filename {{{ -$text -}}} -"; - } - - function _cleanupOldFiles($file) - { - $temp_dir = realpath(dirname($file)); - $mainFileName = basename($file, 'phpt'); - $diff_filename = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'diff'; - $log_filename = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'log'; - $exp_filename = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'exp'; - $output_filename = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'out'; - $memcheck_filename = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'mem'; - $temp_file = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'php'; - $temp_skipif = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'skip.php'; - $temp_clean = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'clean.php'; - $tmp_post = $temp_dir . DIRECTORY_SEPARATOR . uniqid('phpt.'); - - // unlink old test results - @unlink($diff_filename); - @unlink($log_filename); - @unlink($exp_filename); - @unlink($output_filename); - @unlink($memcheck_filename); - @unlink($temp_file); - @unlink($temp_skipif); - @unlink($tmp_post); - @unlink($temp_clean); - } - - function _runSkipIf($section_text, $temp_skipif, $tested, $ini_settings) - { - $info = ''; - $warn = false; - if (array_key_exists('SKIPIF', $section_text) && trim($section_text['SKIPIF'])) { - $this->save_text($temp_skipif, $section_text['SKIPIF']); - $output = $this->system_with_timeout("$this->_php $ini_settings -f \"$temp_skipif\""); - $output = $output[1]; - $loutput = ltrim($output); - unlink($temp_skipif); - if (!strncasecmp('skip', $loutput, 4)) { - $skipreason = "SKIP $tested"; - if (preg_match('/^\s*skip\s*(.+)\s*/i', $output, $m)) { - $skipreason .= '(reason: ' . $m[1] . ')'; - } - if (!isset($this->_options['quiet'])) { - $this->_logger->log(0, $skipreason); - } - if (isset($this->_options['tapoutput'])) { - return array('ok', ' # skip ' . $reason); - } - return 'SKIPPED'; - } - - if (!strncasecmp('info', $loutput, 4) - && preg_match('/^\s*info\s*(.+)\s*/i', $output, $m)) { - $info = " (info: $m[1])"; - } - - if (!strncasecmp('warn', $loutput, 4) - && preg_match('/^\s*warn\s*(.+)\s*/i', $output, $m)) { - $warn = true; /* only if there is a reason */ - $info = " (warn: $m[1])"; - } - } - - return array('warn' => $warn, 'info' => $info); - } - - function _stripHeadersCGI($output) - { - $this->headers = array(); - if (!empty($this->_options['cgi']) && - $this->_php == $this->_options['cgi'] && - preg_match("/^(.*?)(?:\n\n(.*)|\\z)/s", $output, $match)) { - $output = isset($match[2]) ? trim($match[2]) : ''; - $this->_headers = $this->_processHeaders($match[1]); - } - - return $output; - } - - /** - * Return an array that can be used with array_diff() to compare headers - * - * @param string $text - */ - function _processHeaders($text) - { - $headers = array(); - $rh = preg_split("/[\n\r]+/", $text); - foreach ($rh as $line) { - if (strpos($line, ':')!== false) { - $line = explode(':', $line, 2); - $headers[trim($line[0])] = trim($line[1]); - } - } - return $headers; - } - - function _readFile($file) - { - // Load the sections of the test file. - $section_text = array( - 'TEST' => '(unnamed test)', - 'SKIPIF' => '', - 'GET' => '', - 'COOKIE' => '', - 'POST' => '', - 'ARGS' => '', - 'INI' => '', - 'CLEAN' => '', - ); - - if (!is_file($file) || !$fp = fopen($file, "r")) { - return PEAR::raiseError("Cannot open test file: $file"); - } - - $section = ''; - while (!feof($fp)) { - $line = fgets($fp); - - // Match the beginning of a section. - if (preg_match('/^--([_A-Z]+)--/', $line, $r)) { - $section = $r[1]; - $section_text[$section] = ''; - continue; - } elseif (empty($section)) { - fclose($fp); - return PEAR::raiseError("Invalid sections formats in test file: $file"); - } - - // Add to the section text. - $section_text[$section] .= $line; - } - fclose($fp); - - return $section_text; - } - - function _writeLog($logname, $data) - { - if (!$log = fopen($logname, 'w')) { - return PEAR::raiseError("Cannot create test log - $logname"); - } - fwrite($log, $data); - fclose($log); - } - - function _resetEnv($section_text, $temp_file) - { - $env = $_ENV; - $env['REDIRECT_STATUS'] = ''; - $env['QUERY_STRING'] = ''; - $env['PATH_TRANSLATED'] = ''; - $env['SCRIPT_FILENAME'] = ''; - $env['REQUEST_METHOD'] = ''; - $env['CONTENT_TYPE'] = ''; - $env['CONTENT_LENGTH'] = ''; - if (!empty($section_text['ENV'])) { - if (strpos($section_text['ENV'], '{PWD}') !== false) { - $section_text['ENV'] = str_replace('{PWD}', dirname($temp_file), $section_text['ENV']); - } - foreach (explode("\n", trim($section_text['ENV'])) as $e) { - $e = explode('=', trim($e), 2); - if (!empty($e[0]) && isset($e[1])) { - $env[$e[0]] = $e[1]; - } - } - } - if (array_key_exists('GET', $section_text)) { - $env['QUERY_STRING'] = trim($section_text['GET']); - } else { - $env['QUERY_STRING'] = ''; - } - if (array_key_exists('COOKIE', $section_text)) { - $env['HTTP_COOKIE'] = trim($section_text['COOKIE']); - } else { - $env['HTTP_COOKIE'] = ''; - } - $env['REDIRECT_STATUS'] = '1'; - $env['PATH_TRANSLATED'] = $temp_file; - $env['SCRIPT_FILENAME'] = $temp_file; - - return $env; - } - - function _processUpload($section_text, $file) - { - if (array_key_exists('UPLOAD', $section_text) && !empty($section_text['UPLOAD'])) { - $upload_files = trim($section_text['UPLOAD']); - $upload_files = explode("\n", $upload_files); - - $request = "Content-Type: multipart/form-data; boundary=---------------------------20896060251896012921717172737\n" . - "-----------------------------20896060251896012921717172737\n"; - foreach ($upload_files as $fileinfo) { - $fileinfo = explode('=', $fileinfo); - if (count($fileinfo) != 2) { - return PEAR::raiseError("Invalid UPLOAD section in test file: $file"); - } - if (!realpath(dirname($file) . '/' . $fileinfo[1])) { - return PEAR::raiseError("File for upload does not exist: $fileinfo[1] " . - "in test file: $file"); - } - $file_contents = file_get_contents(dirname($file) . '/' . $fileinfo[1]); - $fileinfo[1] = basename($fileinfo[1]); - $request .= "Content-Disposition: form-data; name=\"$fileinfo[0]\"; filename=\"$fileinfo[1]\"\n"; - $request .= "Content-Type: text/plain\n\n"; - $request .= $file_contents . "\n" . - "-----------------------------20896060251896012921717172737\n"; - } - - if (array_key_exists('POST', $section_text) && !empty($section_text['POST'])) { - // encode POST raw - $post = trim($section_text['POST']); - $post = explode('&', $post); - foreach ($post as $i => $post_info) { - $post_info = explode('=', $post_info); - if (count($post_info) != 2) { - return PEAR::raiseError("Invalid POST data in test file: $file"); - } - $post_info[0] = rawurldecode($post_info[0]); - $post_info[1] = rawurldecode($post_info[1]); - $post[$i] = $post_info; - } - foreach ($post as $post_info) { - $request .= "Content-Disposition: form-data; name=\"$post_info[0]\"\n\n"; - $request .= $post_info[1] . "\n" . - "-----------------------------20896060251896012921717172737\n"; - } - unset($section_text['POST']); - } - $section_text['POST_RAW'] = $request; - } - - return $section_text; - } - - function _testCleanup($section_text, $temp_clean) - { - if ($section_text['CLEAN']) { - // perform test cleanup - $this->save_text($temp_clean, $section_text['CLEAN']); - $output = $this->system_with_timeout("$this->_php \"$temp_clean\" 2>&1"); - if (strlen($output[1])) { - echo "BORKED --CLEAN-- section! output:\n", $output[1]; - } - if (file_exists($temp_clean)) { - unlink($temp_clean); - } - } - } -} diff --git a/pear/PEAR/Start.php b/pear/PEAR/Start.php deleted file mode 100644 index da69246..0000000 --- a/pear/PEAR/Start.php +++ /dev/null @@ -1,427 +0,0 @@ - 'Installation base ($prefix)', - 'temp_dir' => 'Temporary directory for processing', - 'download_dir' => 'Temporary directory for downloads', - 'bin_dir' => 'Binaries directory', - 'php_dir' => 'PHP code directory ($php_dir)', - 'doc_dir' => 'Documentation directory', - 'data_dir' => 'Data directory', - 'cfg_dir' => 'User-modifiable configuration files directory', - 'www_dir' => 'Public Web Files directory', - 'test_dir' => 'Tests directory', - 'pear_conf' => 'Name of configuration file', - ); - - var $localInstall; - var $PEARConfig; - var $tarball = array(); - - function PEAR_Start() - { - parent::PEAR(); - if (OS_WINDOWS) { - $this->configPrompt['php_bin'] = 'Path to CLI php.exe'; - $this->config[] = 'php_bin'; - $this->prefix = getcwd(); - - if (!@is_dir($this->prefix)) { - if (@is_dir('c:\php5')) { - $this->prefix = 'c:\php5'; - } elseif (@is_dir('c:\php4')) { - $this->prefix = 'c:\php4'; - } elseif (@is_dir('c:\php')) { - $this->prefix = 'c:\php'; - } - } - - $slash = "\\"; - if (strrpos($this->prefix, '\\') === (strlen($this->prefix) - 1)) { - $slash = ''; - } - - $this->localInstall = false; - $this->bin_dir = '$prefix'; - $this->temp_dir = '$prefix' . $slash . 'tmp'; - $this->download_dir = '$prefix' . $slash . 'tmp'; - $this->php_dir = '$prefix' . $slash . 'pear'; - $this->doc_dir = '$prefix' . $slash . 'docs'; - $this->data_dir = '$prefix' . $slash . 'data'; - $this->test_dir = '$prefix' . $slash . 'tests'; - $this->www_dir = '$prefix' . $slash . 'www'; - $this->cfg_dir = '$prefix' . $slash . 'cfg'; - $this->pear_conf = PEAR_CONFIG_SYSCONFDIR . '\\pear.ini'; - /* - * Detects php.exe - */ - $this->validPHPBin = true; - if ($t = $this->safeGetenv('PHP_PEAR_PHP_BIN')) { - $this->php_bin = dirname($t); - } elseif ($t = $this->safeGetenv('PHP_BIN')) { - $this->php_bin = dirname($t); - } elseif ($t = System::which('php')) { - $this->php_bin = dirname($t); - } elseif (is_file($this->prefix . '\cli\php.exe')) { - $this->php_bin = $this->prefix . '\cli'; - } elseif (is_file($this->prefix . '\php.exe')) { - $this->php_bin = $this->prefix; - } - $phpexe = OS_WINDOWS ? '\\php.exe' : '/php'; - if ($this->php_bin && !is_file($this->php_bin . $phpexe)) { - $this->php_bin = ''; - } else { - if (strpos($this->php_bin, ':') === 0) { - $this->php_bin = getcwd() . DIRECTORY_SEPARATOR . $this->php_bin; - } - } - if (!is_file($this->php_bin . $phpexe)) { - if (is_file('c:/php/cli/php.exe')) { - $this->php_bin = 'c"\\php\\cli'; - } elseif (is_file('c:/php5/php.exe')) { - $this->php_bin = 'c:\\php5'; - } elseif (is_file('c:/php4/cli/php.exe')) { - $this->php_bin = 'c:\\php4\\cli'; - } else { - $this->validPHPBin = false; - } - } - } else { - $this->prefix = dirname(PHP_BINDIR); - $this->pear_conf = PEAR_CONFIG_SYSCONFDIR . '/pear.conf'; - if (get_current_user() != 'root') { - $this->prefix = $this->safeGetenv('HOME') . '/pear'; - $this->pear_conf = $this->safeGetenv('HOME') . '.pearrc'; - } - $this->bin_dir = '$prefix/bin'; - $this->php_dir = '$prefix/share/pear'; - $this->temp_dir = '/tmp/pear/install'; - $this->download_dir = '/tmp/pear/install'; - $this->doc_dir = '$prefix/docs'; - $this->www_dir = '$prefix/www'; - $this->cfg_dir = '$prefix/cfg'; - $this->data_dir = '$prefix/data'; - $this->test_dir = '$prefix/tests'; - // check if the user has installed PHP with PHP or GNU layout - if (@is_dir("$this->prefix/lib/php/.registry")) { - $this->php_dir = '$prefix/lib/php'; - } elseif (@is_dir("$this->prefix/share/pear/lib/.registry")) { - $this->php_dir = '$prefix/share/pear/lib'; - $this->doc_dir = '$prefix/share/pear/docs'; - $this->data_dir = '$prefix/share/pear/data'; - $this->test_dir = '$prefix/share/pear/tests'; - } elseif (@is_dir("$this->prefix/share/php/.registry")) { - $this->php_dir = '$prefix/share/php'; - } - } - } - - function safeGetenv($var) - { - if (is_array($_ENV) && isset($_ENV[$var])) { - return $_ENV[$var]; - } - - return getenv($var); - } - - function show($stuff) - { - print $stuff; - } - - function locatePackagesToInstall() - { - $dp = @opendir(dirname(__FILE__) . '/go-pear-tarballs'); - if (empty($dp)) { - return PEAR::raiseError("while locating packages to install: opendir('" . - dirname(__FILE__) . "/go-pear-tarballs') failed"); - } - - $potentials = array(); - while (false !== ($entry = readdir($dp))) { - if ($entry{0} == '.' || !in_array(substr($entry, -4), array('.tar', '.tgz'))) { - continue; - } - $potentials[] = $entry; - } - - closedir($dp); - $notfound = array(); - foreach ($this->corePackages as $package) { - foreach ($potentials as $i => $candidate) { - if (preg_match('/^' . $package . '-' . _PEAR_COMMON_PACKAGE_VERSION_PREG - . '\.(tar|tgz)\\z/', $candidate)) { - $this->tarball[$package] = dirname(__FILE__) . '/go-pear-tarballs/' . $candidate; - unset($potentials[$i]); - continue 2; - } - } - - $notfound[] = $package; - } - - if (count($notfound)) { - return PEAR::raiseError("No tarballs found for core packages: " . - implode(', ', $notfound)); - } - - $this->tarball = array_merge($this->tarball, $potentials); - } - - function setupTempStuff() - { - if (!($this->ptmp = System::mktemp(array('-d')))) { - $this->show("System's Tempdir failed, trying to use \$prefix/tmp ..."); - $res = System::mkDir(array($this->prefix . '/tmp')); - if (!$res) { - return PEAR::raiseError('mkdir ' . $this->prefix . '/tmp ... failed'); - } - - $_temp = tempnam($this->prefix . '/tmp', 'gope'); - System::rm(array('-rf', $_temp)); - System::mkdir(array('-p','-m', '0700', $_temp)); - $this->ptmp = $this->prefix . '/tmp'; - $ok = @chdir($this->ptmp); - - if (!$ok) { // This should not happen, really ;) - $this->bail('chdir ' . $this->ptmp . ' ... failed'); - } - - print "ok\n"; - - // Adjust TEMPDIR envvars - if (!isset($_ENV)) { - $_ENV = array(); - }; - $_ENV['TMPDIR'] = $_ENV['TEMP'] = $this->prefix . '/tmp'; - } - - return @chdir($this->ptmp); - } - - /** - * Try to detect the kind of SAPI used by the - * the given php.exe. - * @author Pierrre-Alain Joye - */ - function win32DetectPHPSAPI() - { - if ($this->php_bin != '') { - if (OS_WINDOWS) { - exec('"' . $this->php_bin . '\\php.exe" -v', $res); - } else { - exec('"' . $this->php_bin . '/php" -v', $res); - } - - if (is_array($res)) { - if (isset($res[0]) && strpos($res[0],"(cli)")) { - return 'cli'; - } - - if (isset($res[0]) && strpos($res[0],"cgi")) { - return 'cgi'; - } - - if (isset($res[0]) && strpos($res[0],"cgi-fcgi")) { - return 'cgi'; - } - - return 'unknown'; - } - } - - return 'unknown'; - } - - function doInstall() - { - print "Beginning install...\n"; - // finish php_bin config - if (OS_WINDOWS) { - $this->php_bin .= '\\php.exe'; - } else { - $this->php_bin .= '/php'; - } - $this->PEARConfig = &PEAR_Config::singleton($this->pear_conf, $this->pear_conf); - $this->PEARConfig->set('preferred_state', 'stable'); - foreach ($this->config as $var) { - if ($var == 'pear_conf' || $var == 'prefix') { - continue; - } - $this->PEARConfig->set($var, $this->$var); - } - - $this->PEARConfig->store(); -// $this->PEARConfig->set('verbose', 6); - print "Configuration written to $this->pear_conf...\n"; - $this->registry = &$this->PEARConfig->getRegistry(); - print "Initialized registry...\n"; - $install = &PEAR_Command::factory('install', $this->PEARConfig); - print "Preparing to install...\n"; - $options = array( - 'nodeps' => true, - 'force' => true, - 'upgrade' => true, - ); - foreach ($this->tarball as $pkg => $src) { - print "installing $src...\n"; - } - $install->run('install', $options, array_values($this->tarball)); - } - - function postProcessConfigVars() - { - foreach ($this->config as $n => $var) { - for ($m = 1; $m <= count($this->config); $m++) { - $var2 = $this->config[$m]; - $this->$var = str_replace('$'.$var2, $this->$var2, $this->$var); - } - } - - foreach ($this->config as $var) { - $dir = $this->$var; - - if (!preg_match('/_dir\\z/', $var)) { - continue; - } - - if (!@is_dir($dir)) { - if (!System::mkDir(array('-p', $dir))) { - $root = OS_WINDOWS ? 'administrator' : 'root'; - return PEAR::raiseError("Unable to create {$this->configPrompt[$var]} $dir. -Run this script as $root or pick another location.\n"); - } - } - } - } - - /** - * Get the php.ini file used with the current - * process or with the given php.exe - * - * Horrible hack, but well ;) - * - * Not used yet, will add the support later - * @author Pierre-Alain Joye - */ - function getPhpiniPath() - { - $pathIni = get_cfg_var('cfg_file_path'); - if ($pathIni && is_file($pathIni)) { - return $pathIni; - } - - // Oh well, we can keep this too :) - // I dunno if get_cfg_var() is safe on every OS - if (OS_WINDOWS) { - // on Windows, we can be pretty sure that there is a php.ini - // file somewhere - do { - $php_ini = PHP_CONFIG_FILE_PATH . DIRECTORY_SEPARATOR . 'php.ini'; - if (@file_exists($php_ini)) { - break; - } - $php_ini = 'c:\winnt\php.ini'; - if (@file_exists($php_ini)) { - break; - } - $php_ini = 'c:\windows\php.ini'; - } while (false); - } else { - $php_ini = PHP_CONFIG_FILE_PATH . DIRECTORY_SEPARATOR . 'php.ini'; - } - - if (@is_file($php_ini)) { - return $php_ini; - } - - // We re running in hackz&troubles :) - ob_implicit_flush(false); - ob_start(); - phpinfo(INFO_GENERAL); - $strInfo = ob_get_contents(); - ob_end_clean(); - ob_implicit_flush(true); - - if (php_sapi_name() != 'cli') { - $strInfo = strip_tags($strInfo,''); - $arrayInfo = explode("", $strInfo ); - $cli = false; - } else { - $arrayInfo = explode("\n", $strInfo); - $cli = true; - } - - foreach ($arrayInfo as $val) { - if (strpos($val,"php.ini")) { - if ($cli) { - list(,$pathIni) = explode('=>', $val); - } else { - $pathIni = strip_tags(trim($val)); - } - $pathIni = trim($pathIni); - if (is_file($pathIni)) { - return $pathIni; - } - } - } - - return false; - } -} -?> diff --git a/pear/PEAR/Start/CLI.php b/pear/PEAR/Start/CLI.php deleted file mode 100644 index a5ea9c5..0000000 --- a/pear/PEAR/Start/CLI.php +++ /dev/null @@ -1,616 +0,0 @@ -tty = OS_WINDOWS ? @fopen('\con', 'r') : @fopen('/dev/tty', 'r'); - - if (!$this->tty) { - $this->tty = fopen('php://stdin', 'r'); - } - $this->origpwd = getcwd(); - $this->config = array_keys($this->configPrompt); - - // make indices run from 1... - array_unshift($this->config, ""); - unset($this->config[0]); - reset($this->config); - $this->descLength = max(array_map('strlen', $this->configPrompt)); - $this->descFormat = "%-{$this->descLength}s"; - $this->first = key($this->config); - end($this->config); - $this->last = key($this->config); - PEAR_Command::setFrontendType('CLI'); - } - - function _PEAR_Start_CLI() - { - if ($this->tty) { - @fclose($this->tty); - } - } - - function run() - { - if (PEAR::isError($err = $this->locatePackagesToInstall())) { - return $err; - } - $this->startupQuestion(); - $this->setupTempStuff(); - $this->getInstallLocations(); - $this->displayPreamble(); - if (PEAR::isError($err = $this->postProcessConfigVars())) { - return $err; - } - $this->doInstall(); - $this->finishInstall(); - } - - function startupQuestion() - { - if (OS_WINDOWS) { - print " -Are you installing a system-wide PEAR or a local copy? -(system|local) [system] : "; - $tmp = trim(fgets($this->tty, 1024)); - if (!empty($tmp) && strtolower($tmp) !== 'system') { - print "Please confirm local copy by typing 'yes' : "; - $tmp = trim(fgets($this->tty, 1024)); - if (strtolower($tmp) == 'yes') { - $slash = "\\"; - if (strrpos($this->prefix, '\\') === (strlen($this->prefix) - 1)) { - $slash = ''; - } - - $this->localInstall = true; - $this->pear_conf = '$prefix' . $slash . 'pear.ini'; - } - } - } else { - if (get_current_user() == 'root') { - return; - } - $this->pear_conf = $this->safeGetenv('HOME') . '/.pearrc'; - } - } - - function getInstallLocations() - { - while (true) { - print " -Below is a suggested file layout for your new PEAR installation. To -change individual locations, type the number in front of the -directory. Type 'all' to change all of them or simply press Enter to -accept these locations. - -"; - - foreach ($this->config as $n => $var) { - $fullvar = $this->$var; - foreach ($this->config as $blah => $unused) { - foreach ($this->config as $m => $var2) { - $fullvar = str_replace('$'.$var2, $this->$var2, $fullvar); - } - } - printf("%2d. $this->descFormat : %s\n", $n, $this->configPrompt[$var], $fullvar); - } - - print "\n$this->first-$this->last, 'all' or Enter to continue: "; - $tmp = trim(fgets($this->tty, 1024)); - if (empty($tmp)) { - if (OS_WINDOWS && !$this->validPHPBin) { - echo "**ERROR** -Please, enter the php.exe path. - -"; - } else { - break; - } - } - - if (isset($this->config[(int)$tmp])) { - $var = $this->config[(int)$tmp]; - $desc = $this->configPrompt[$var]; - $current = $this->$var; - if (WIN32GUI && $var != 'pear_conf'){ - $tmp = $this->win32BrowseForFolder("Choose a Folder for $desc [$current] :"); - $tmp.= '\\'; - } else { - print "(Use \$prefix as a shortcut for '$this->prefix', etc.) -$desc [$current] : "; - $tmp = trim(fgets($this->tty, 1024)); - } - $old = $this->$var; - $this->$var = $$var = $tmp; - if (OS_WINDOWS && $var=='php_bin') { - if ($this->validatePhpExecutable($tmp)) { - $this->php_bin = $tmp; - } else { - $this->php_bin = $old; - } - } - } elseif ($tmp == 'all') { - foreach ($this->config as $n => $var) { - $desc = $this->configPrompt[$var]; - $current = $this->$var; - print "$desc [$current] : "; - $tmp = trim(fgets($this->tty, 1024)); - if (!empty($tmp)) { - $this->$var = $tmp; - } - } - } - } - } - - function validatePhpExecutable($tmp) - { - if (OS_WINDOWS) { - if (strpos($tmp, 'php.exe')) { - $tmp = str_replace('php.exe', '', $tmp); - } - if (file_exists($tmp . DIRECTORY_SEPARATOR . 'php.exe')) { - $tmp = $tmp . DIRECTORY_SEPARATOR . 'php.exe'; - $this->php_bin_sapi = $this->win32DetectPHPSAPI(); - if ($this->php_bin_sapi=='cgi'){ - print " -****************************************************************************** -NOTICE! We found php.exe under $this->php_bin, it uses a $this->php_bin_sapi SAPI. -PEAR commandline tool works well with it. -If you have a CLI php.exe available, we recommend using it. - -Press Enter to continue..."; - $tmp = trim(fgets($this->tty, 1024)); - } elseif ($this->php_bin_sapi=='unknown') { - print " -****************************************************************************** -WARNING! We found php.exe under $this->php_bin, it uses an $this->php_bin_sapi SAPI. -PEAR commandline tool has NOT been tested with it. -If you have a CLI (or CGI) php.exe available, we strongly recommend using it. - -Press Enter to continue..."; - $tmp = trim(fgets($this->tty, 1024)); - } - echo "php.exe (sapi: $this->php_bin_sapi) found.\n\n"; - return $this->validPHPBin = true; - } else { - echo "**ERROR**: not a folder, or no php.exe found in this folder. -Press Enter to continue..."; - $tmp = trim(fgets($this->tty, 1024)); - return $this->validPHPBin = false; - } - } - } - - /** - * Create a vbs script to browse the getfolder dialog, called - * by cscript, if it's available. - * $label is the label text in the header of the dialog box - * - * TODO: - * - Do not show Control panel - * - Replace WSH with calls to w32 as soon as callbacks work - * @author Pierrre-Alain Joye - */ - function win32BrowseForFolder($label) - { - static $wshSaved=false; - static $cscript=''; - $wsh_browserfolder = 'Option Explicit -Dim ArgObj, var1, var2, sa, sFld -Set ArgObj = WScript.Arguments -Const BIF_EDITBOX = &H10 -Const BIF_NEWDIALOGSTYLE = &H40 -Const BIF_RETURNONLYFSDIRS = &H0001 -Const BIF_DONTGOBELOWDOMAIN = &H0002 -Const BIF_STATUSTEXT = &H0004 -Const BIF_RETURNFSANCESTORS = &H0008 -Const BIF_VALIDATE = &H0020 -Const BIF_BROWSEFORCOMPUTER = &H1000 -Const BIF_BROWSEFORPRINTER = &H2000 -Const BIF_BROWSEINCLUDEFILES = &H4000 -Const OFN_LONGNAMES = &H200000 -Const OFN_NOLONGNAMES = &H40000 -Const ssfDRIVES = &H11 -Const ssfNETWORK = &H12 -Set sa = CreateObject("Shell.Application") -var1=ArgObj(0) -Set sFld = sa.BrowseForFolder(0, var1, BIF_EDITBOX + BIF_VALIDATE + BIF_BROWSEINCLUDEFILES + BIF_RETURNFSANCESTORS+BIF_NEWDIALOGSTYLE , ssfDRIVES ) -if not sFld is nothing Then - if not left(sFld.items.item.path,1)=":" Then - WScript.Echo sFld.items.item.path - Else - WScript.Echo "invalid" - End If -Else - WScript.Echo "cancel" -End If -'; - if( !$wshSaved){ - $cscript = $this->ptmp . DIRECTORY_SEPARATOR . "bf.vbs"; - $fh = fopen($cscript, "wb+"); - fwrite($fh, $wsh_browserfolder, strlen($wsh_browserfolder)); - fclose($fh); - $wshSaved = true; - } - - exec('cscript ' . escapeshellarg($cscript) . ' "' . escapeshellarg($label) . '" //noLogo', $arPath); - if (!count($arPath) || $arPath[0]=='' || $arPath[0]=='cancel') { - return ''; - } elseif ($arPath[0]=='invalid') { - echo "Invalid Path.\n"; - return ''; - } - - @unlink($cscript); - return $arPath[0]; - } - - function displayPreamble() - { - if (OS_WINDOWS) { - /* - * Checks PHP SAPI version under windows/CLI - */ - if ($this->php_bin == '') { - print " -We do not find any php.exe, please select the php.exe folder (CLI is -recommended, usually in c:\php\cli\php.exe) -"; - $this->validPHPBin = false; - } elseif (strlen($this->php_bin)) { - $this->php_bin_sapi = $this->win32DetectPHPSAPI(); - $this->validPHPBin = true; - switch ($this->php_bin_sapi) { - case 'cli': - break; - case 'cgi': - case 'cgi-fcgi': - print " -*NOTICE* -We found php.exe under $this->php_bin, it uses a $this->php_bin_sapi SAPI. PEAR commandline -tool works well with it, if you have a CLI php.exe available, we -recommend using it. -"; - break; - default: - print " -*WARNING* -We found php.exe under $this->php_bin, it uses an unknown SAPI. PEAR commandline -tool has not been tested with it, if you have a CLI (or CGI) php.exe available, -we strongly recommend using it. - -"; - break; - } - } - } - } - - function finishInstall() - { - $sep = OS_WINDOWS ? ';' : ':'; - $include_path = explode($sep, ini_get('include_path')); - if (OS_WINDOWS) { - $found = false; - $t = strtolower($this->php_dir); - foreach ($include_path as $path) { - if ($t == strtolower($path)) { - $found = true; - break; - } - } - } else { - $found = in_array($this->php_dir, $include_path); - } - if (!$found) { - print " -****************************************************************************** -WARNING! The include_path defined in the currently used php.ini does not -contain the PEAR PHP directory you just specified: -<$this->php_dir> -If the specified directory is also not in the include_path used by -your scripts, you will have problems getting any PEAR packages working. -"; - - if ($php_ini = $this->getPhpiniPath()) { - print "\n\nWould you like to alter php.ini <$php_ini>? [Y/n] : "; - $alter_phpini = !stristr(fgets($this->tty, 1024), "n"); - if ($alter_phpini) { - $this->alterPhpIni($php_ini); - } else { - if (OS_WINDOWS) { - print " -Please look over your php.ini file to make sure -$this->php_dir is in your include_path."; - } else { - print " -I will add a workaround for this in the 'pear' command to make sure -the installer works, but please look over your php.ini or Apache -configuration to make sure $this->php_dir is in your include_path. -"; - } - } - } - - print " -Current include path : ".ini_get('include_path')." -Configured directory : $this->php_dir -Currently used php.ini (guess) : $php_ini -"; - - print "Press Enter to continue: "; - fgets($this->tty, 1024); - } - - $pear_cmd = $this->bin_dir . DIRECTORY_SEPARATOR . 'pear'; - $pear_cmd = OS_WINDOWS ? strtolower($pear_cmd).'.bat' : $pear_cmd; - - // check that the installed pear and the one in the path are the same (if any) - $pear_old = System::which(OS_WINDOWS ? 'pear.bat' : 'pear', $this->bin_dir); - if ($pear_old && ($pear_old != $pear_cmd)) { - // check if it is a link or symlink - $islink = OS_WINDOWS ? false : is_link($pear_old) ; - if ($islink && readlink($pear_old) != $pear_cmd) { - print "\n** WARNING! The link $pear_old does not point to the " . - "installed $pear_cmd\n"; - } elseif (!$this->localInstall && is_writable($pear_old) && !is_dir($pear_old)) { - rename($pear_old, "{$pear_old}_old"); - print "\n** WARNING! Backed up old pear to {$pear_old}_old\n"; - } else { - print "\n** WARNING! Old version found at $pear_old, please remove it or ". - "be sure to use the new $pear_cmd command\n"; - } - } - - print "\nThe 'pear' command is now at your service at $pear_cmd\n"; - - // Alert the user if the pear cmd is not in PATH - $old_dir = $pear_old ? dirname($pear_old) : false; - if (!$this->which('pear', $old_dir)) { - print " -** The 'pear' command is not currently in your PATH, so you need to -** use '$pear_cmd' until you have added -** '$this->bin_dir' to your PATH environment variable. - -"; - - print "Run it without parameters to see the available actions, try 'pear list' -to see what packages are installed, or 'pear help' for help. - -For more information about PEAR, see: - - http://pear.php.net/faq.php - http://pear.php.net/manual/ - -Thanks for using go-pear! - -"; - } - - if (OS_WINDOWS && !$this->localInstall) { - $this->win32CreateRegEnv(); - } - } - - /** - * System::which() does not allow path exclusion - */ - function which($program, $dont_search_in = false) - { - if (OS_WINDOWS) { - if ($_path = $this->safeGetEnv('Path')) { - $dirs = explode(';', $_path); - } else { - $dirs = explode(';', $this->safeGetEnv('PATH')); - } - foreach ($dirs as $i => $dir) { - $dirs[$i] = strtolower(realpath($dir)); - } - if ($dont_search_in) { - $dont_search_in = strtolower(realpath($dont_search_in)); - } - if ($dont_search_in && - ($key = array_search($dont_search_in, $dirs)) !== false) - { - unset($dirs[$key]); - } - - foreach ($dirs as $dir) { - $dir = str_replace('\\\\', '\\', $dir); - if (!strlen($dir)) { - continue; - } - if ($dir{strlen($dir) - 1} != '\\') { - $dir .= '\\'; - } - $tmp = $dir . $program; - $info = pathinfo($tmp); - if (isset($info['extension']) && in_array(strtolower($info['extension']), - array('exe', 'com', 'bat', 'cmd'))) { - if (file_exists($tmp)) { - return strtolower($tmp); - } - } elseif (file_exists($ret = $tmp . '.exe') || - file_exists($ret = $tmp . '.com') || - file_exists($ret = $tmp . '.bat') || - file_exists($ret = $tmp . '.cmd')) { - return strtolower($ret); - } - } - } else { - $dirs = explode(':', $this->safeGetEnv('PATH')); - if ($dont_search_in && - ($key = array_search($dont_search_in, $dirs)) !== false) - { - unset($dirs[$key]); - } - foreach ($dirs as $dir) { - if (is_executable("$dir/$program")) { - return "$dir/$program"; - } - } - } - return false; - } - - /** - * Not optimized, but seems to work, if some nice - * peardev will test it? :) - * - * @author Pierre-Alain Joye - */ - function alterPhpIni($pathIni='') - { - $foundAt = array(); - $iniSep = OS_WINDOWS ? ';' : ':'; - - if ($pathIni=='') { - $pathIni = $this->getPhpiniPath(); - } - - $arrayIni = file($pathIni); - $i=0; - $found=0; - - // Looks for each active include_path directives - foreach ($arrayIni as $iniLine) { - $iniLine = trim($iniLine); - $iniLine = str_replace(array("\n", "\r"), array('', ''), $iniLine); - if (preg_match("/^\s*include_path/", $iniLine)) { - $foundAt[] = $i; - $found++; - } - $i++; - } - - if ($found) { - $includeLine = $arrayIni[$foundAt[0]]; - list(, $currentPath) = explode('=', $includeLine); - - $currentPath = trim($currentPath); - if (substr($currentPath,0,1) == '"') { - $currentPath = substr($currentPath, 1, strlen($currentPath) - 2); - } - - $arrayPath = explode($iniSep, $currentPath); - $newPath = array(); - if ($arrayPath[0]=='.') { - $newPath[0] = '.'; - $newPath[1] = $this->php_dir; - array_shift($arrayPath); - } else { - $newPath[0] = $this->php_dir; - } - - foreach ($arrayPath as $path) { - $newPath[]= $path; - } - } else { - $newPath = array(); - $newPath[0] = '.'; - $newPath[1] = $this->php_dir; - $foundAt[] = count($arrayIni); // add a new line if none is present - } - $nl = OS_WINDOWS ? "\r\n" : "\n"; - $includepath = 'include_path="' . implode($iniSep,$newPath) . '"'; - $newInclude = "$nl$nl;***** Added by go-pear$nl" . - $includepath . - $nl . ";*****" . - $nl . $nl; - - $arrayIni[$foundAt[0]] = $newInclude; - - for ($i=1; $i<$found; $i++) { - $arrayIni[$foundAt[$i]]=';' . trim($arrayIni[$foundAt[$i]]); - } - - $newIni = implode("", $arrayIni); - if (!($fh = @fopen($pathIni, "wb+"))) { - $prefixIni = $this->prefix . DIRECTORY_SEPARATOR . "php.ini-gopear"; - $fh = fopen($prefixIni, "wb+"); - if (!$fh) { - echo " -****************************************************************************** -WARNING: Cannot write to $pathIni nor in $this->prefix/php.ini-gopear. Please -modify manually your php.ini by adding: - -$includepath - -"; - return false; - } else { - fwrite($fh, $newIni, strlen($newIni)); - fclose($fh); - echo " -****************************************************************************** -WARNING: Cannot write to $pathIni, but php.ini was successfully created -at <$this->prefix/php.ini-gopear>. Please replace the file <$pathIni> with -<$prefixIni> or modify your php.ini by adding: - -$includepath - -"; - - } - } else { - fwrite($fh, $newIni, strlen($newIni)); - fclose($fh); - echo " -php.ini <$pathIni> include_path updated. -"; - } - return true; - } - - /** - * Generates a registry addOn for Win32 platform - * This addon set PEAR environment variables - * @author Pierrre-Alain Joye - */ - function win32CreateRegEnv() - { - $nl = "\r\n"; - $reg ='REGEDIT4'.$nl. - '[HKEY_CURRENT_USER\Environment]'. $nl . - '"PHP_PEAR_SYSCONF_DIR"="' . addslashes($this->prefix) . '"' . $nl . - '"PHP_PEAR_INSTALL_DIR"="' . addslashes($this->php_dir) . '"' . $nl . - '"PHP_PEAR_DOC_DIR"="' . addslashes($this->doc_dir) . '"' . $nl . - '"PHP_PEAR_BIN_DIR"="' . addslashes($this->bin_dir) . '"' . $nl . - '"PHP_PEAR_DATA_DIR"="' . addslashes($this->data_dir) . '"' . $nl . - '"PHP_PEAR_PHP_BIN"="' . addslashes($this->php_bin) . '"' . $nl . - '"PHP_PEAR_TEST_DIR"="' . addslashes($this->test_dir) . '"' . $nl; - - $fh = fopen($this->prefix . DIRECTORY_SEPARATOR . 'PEAR_ENV.reg', 'wb'); - if($fh){ - fwrite($fh, $reg, strlen($reg)); - fclose($fh); - echo " - -* WINDOWS ENVIRONMENT VARIABLES * -For convenience, a REG file is available under {$this->prefix}PEAR_ENV.reg . -This file creates ENV variables for the current user. - -Double-click this file to add it to the current user registry. - -"; - } - } - - function displayHTMLProgress() - { - } -} -?> \ No newline at end of file diff --git a/pear/PEAR/Task/Common.php b/pear/PEAR/Task/Common.php deleted file mode 100644 index 0ca5ccf..0000000 --- a/pear/PEAR/Task/Common.php +++ /dev/null @@ -1,202 +0,0 @@ - - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a1 - */ -/**#@+ - * Error codes for task validation routines - */ -define('PEAR_TASK_ERROR_NOATTRIBS', 1); -define('PEAR_TASK_ERROR_MISSING_ATTRIB', 2); -define('PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE', 3); -define('PEAR_TASK_ERROR_INVALID', 4); -/**#@-*/ -define('PEAR_TASK_PACKAGE', 1); -define('PEAR_TASK_INSTALL', 2); -define('PEAR_TASK_PACKAGEANDINSTALL', 3); -/** - * A task is an operation that manipulates the contents of a file. - * - * Simple tasks operate on 1 file. Multiple tasks are executed after all files have been - * processed and installed, and are designed to operate on all files containing the task. - * The Post-install script task simply takes advantage of the fact that it will be run - * after installation, replace is a simple task. - * - * Combining tasks is possible, but ordering is significant. - * - * - * - * - * - * - * This will first replace any instance of @data-dir@ in the test.php file - * with the path to the current data directory. Then, it will include the - * test.php file and run the script it contains to configure the package post-installation. - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a1 - * @abstract - */ -class PEAR_Task_Common -{ - /** - * Valid types for this version are 'simple' and 'multiple' - * - * - simple tasks operate on the contents of a file and write out changes to disk - * - multiple tasks operate on the contents of many files and write out the - * changes directly to disk - * - * Child task classes must override this property. - * @access protected - */ - var $type = 'simple'; - /** - * Determines which install phase this task is executed under - */ - var $phase = PEAR_TASK_INSTALL; - /** - * @access protected - */ - var $config; - /** - * @access protected - */ - var $registry; - /** - * @access protected - */ - var $logger; - /** - * @access protected - */ - var $installphase; - /** - * @param PEAR_Config - * @param PEAR_Common - */ - function PEAR_Task_Common(&$config, &$logger, $phase) - { - $this->config = &$config; - $this->registry = &$config->getRegistry(); - $this->logger = &$logger; - $this->installphase = $phase; - if ($this->type == 'multiple') { - $GLOBALS['_PEAR_TASK_POSTINSTANCES'][get_class($this)][] = &$this; - } - } - - /** - * Validate the basic contents of a task tag. - * @param PEAR_PackageFile_v2 - * @param array - * @param PEAR_Config - * @param array the entire parsed tag - * @return true|array On error, return an array in format: - * array(PEAR_TASK_ERROR_???[, param1][, param2][, ...]) - * - * For PEAR_TASK_ERROR_MISSING_ATTRIB, pass the attribute name in - * For PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, pass the attribute name and an array - * of legal values in - * @static - * @abstract - */ - function validateXml($pkg, $xml, $config, $fileXml) - { - } - - /** - * Initialize a task instance with the parameters - * @param array raw, parsed xml - * @param array attributes from the tag containing this task - * @param string|null last installed version of this package - * @abstract - */ - function init($xml, $fileAttributes, $lastVersion) - { - } - - /** - * Begin a task processing session. All multiple tasks will be processed after each file - * has been successfully installed, all simple tasks should perform their task here and - * return any errors using the custom throwError() method to allow forward compatibility - * - * This method MUST NOT write out any changes to disk - * @param PEAR_PackageFile_v2 - * @param string file contents - * @param string the eventual final file location (informational only) - * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail - * (use $this->throwError), otherwise return the new contents - * @abstract - */ - function startSession($pkg, $contents, $dest) - { - } - - /** - * This method is used to process each of the tasks for a particular multiple class - * type. Simple tasks need not implement this method. - * @param array an array of tasks - * @access protected - * @static - * @abstract - */ - function run($tasks) - { - } - - /** - * @static - * @final - */ - function hasPostinstallTasks() - { - return isset($GLOBALS['_PEAR_TASK_POSTINSTANCES']); - } - - /** - * @static - * @final - */ - function runPostinstallTasks() - { - foreach ($GLOBALS['_PEAR_TASK_POSTINSTANCES'] as $class => $tasks) { - $err = call_user_func(array($class, 'run'), - $GLOBALS['_PEAR_TASK_POSTINSTANCES'][$class]); - if ($err) { - return PEAR_Task_Common::throwError($err); - } - } - unset($GLOBALS['_PEAR_TASK_POSTINSTANCES']); - } - - /** - * Determines whether a role is a script - * @return bool - */ - function isScript() - { - return $this->type == 'script'; - } - - function throwError($msg, $code = -1) - { - include_once 'PEAR.php'; - return PEAR::raiseError($msg, $code); - } -} -?> \ No newline at end of file diff --git a/pear/PEAR/Task/Postinstallscript.php b/pear/PEAR/Task/Postinstallscript.php deleted file mode 100644 index 6281340..0000000 --- a/pear/PEAR/Task/Postinstallscript.php +++ /dev/null @@ -1,323 +0,0 @@ - - * - * PHP versions 4 and 5 - * - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a1 - */ -/** - * Base class - */ -require_once 'PEAR/Task/Common.php'; -/** - * Implements the postinstallscript file task. - * - * Note that post-install scripts are handled separately from installation, by the - * "pear run-scripts" command - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a1 - */ -class PEAR_Task_Postinstallscript extends PEAR_Task_Common -{ - var $type = 'script'; - var $_class; - var $_params; - var $_obj; - /** - * - * @var PEAR_PackageFile_v2 - */ - var $_pkg; - var $_contents; - var $phase = PEAR_TASK_INSTALL; - - /** - * Validate the raw xml at parsing-time. - * - * This also attempts to validate the script to make sure it meets the criteria - * for a post-install script - * @param PEAR_PackageFile_v2 - * @param array The XML contents of the tag - * @param PEAR_Config - * @param array the entire parsed tag - * @static - */ - function validateXml($pkg, $xml, $config, $fileXml) - { - if ($fileXml['role'] != 'php') { - return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' . - $fileXml['name'] . '" must be role="php"'); - } - PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - $file = $pkg->getFileContents($fileXml['name']); - if (PEAR::isError($file)) { - PEAR::popErrorHandling(); - return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' . - $fileXml['name'] . '" is not valid: ' . - $file->getMessage()); - } elseif ($file === null) { - return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' . - $fileXml['name'] . '" could not be retrieved for processing!'); - } else { - $analysis = $pkg->analyzeSourceCode($file, true); - if (!$analysis) { - PEAR::popErrorHandling(); - $warnings = ''; - foreach ($pkg->getValidationWarnings() as $warn) { - $warnings .= $warn['message'] . "\n"; - } - return array(PEAR_TASK_ERROR_INVALID, 'Analysis of post-install script "' . - $fileXml['name'] . '" failed: ' . $warnings); - } - if (count($analysis['declared_classes']) != 1) { - PEAR::popErrorHandling(); - return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' . - $fileXml['name'] . '" must declare exactly 1 class'); - } - $class = $analysis['declared_classes'][0]; - if ($class != str_replace(array('/', '.php'), array('_', ''), - $fileXml['name']) . '_postinstall') { - PEAR::popErrorHandling(); - return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' . - $fileXml['name'] . '" class "' . $class . '" must be named "' . - str_replace(array('/', '.php'), array('_', ''), - $fileXml['name']) . '_postinstall"'); - } - if (!isset($analysis['declared_methods'][$class])) { - PEAR::popErrorHandling(); - return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' . - $fileXml['name'] . '" must declare methods init() and run()'); - } - $methods = array('init' => 0, 'run' => 1); - foreach ($analysis['declared_methods'][$class] as $method) { - if (isset($methods[$method])) { - unset($methods[$method]); - } - } - if (count($methods)) { - PEAR::popErrorHandling(); - return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' . - $fileXml['name'] . '" must declare methods init() and run()'); - } - } - PEAR::popErrorHandling(); - $definedparams = array(); - $tasksNamespace = $pkg->getTasksNs() . ':'; - if (!isset($xml[$tasksNamespace . 'paramgroup']) && isset($xml['paramgroup'])) { - // in order to support the older betas, which did not expect internal tags - // to also use the namespace - $tasksNamespace = ''; - } - if (isset($xml[$tasksNamespace . 'paramgroup'])) { - $params = $xml[$tasksNamespace . 'paramgroup']; - if (!is_array($params) || !isset($params[0])) { - $params = array($params); - } - foreach ($params as $param) { - if (!isset($param[$tasksNamespace . 'id'])) { - return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' . - $fileXml['name'] . '" must have ' . - 'an ' . $tasksNamespace . 'id> tag'); - } - if (isset($param[$tasksNamespace . 'name'])) { - if (!in_array($param[$tasksNamespace . 'name'], $definedparams)) { - return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' . - $fileXml['name'] . '" ' . $tasksNamespace . - 'paramgroup> id "' . $param[$tasksNamespace . 'id'] . - '" parameter "' . $param[$tasksNamespace . 'name'] . - '" has not been previously defined'); - } - if (!isset($param[$tasksNamespace . 'conditiontype'])) { - return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' . - $fileXml['name'] . '" ' . $tasksNamespace . - 'paramgroup> id "' . $param[$tasksNamespace . 'id'] . - '" must have a ' . $tasksNamespace . - 'conditiontype> tag containing either "=", ' . - '"!=", or "preg_match"'); - } - if (!in_array($param[$tasksNamespace . 'conditiontype'], - array('=', '!=', 'preg_match'))) { - return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' . - $fileXml['name'] . '" ' . $tasksNamespace . - 'paramgroup> id "' . $param[$tasksNamespace . 'id'] . - '" must have a ' . $tasksNamespace . - 'conditiontype> tag containing either "=", ' . - '"!=", or "preg_match"'); - } - if (!isset($param[$tasksNamespace . 'value'])) { - return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' . - $fileXml['name'] . '" ' . $tasksNamespace . - 'paramgroup> id "' . $param[$tasksNamespace . 'id'] . - '" must have a ' . $tasksNamespace . - 'value> tag containing expected parameter value'); - } - } - if (isset($param[$tasksNamespace . 'instructions'])) { - if (!is_string($param[$tasksNamespace . 'instructions'])) { - return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' . - $fileXml['name'] . '" ' . $tasksNamespace . - 'paramgroup> id "' . $param[$tasksNamespace . 'id'] . - '" ' . $tasksNamespace . 'instructions> must be simple text'); - } - } - if (!isset($param[$tasksNamespace . 'param'])) { - continue; // is no longer required - } - $subparams = $param[$tasksNamespace . 'param']; - if (!is_array($subparams) || !isset($subparams[0])) { - $subparams = array($subparams); - } - foreach ($subparams as $subparam) { - if (!isset($subparam[$tasksNamespace . 'name'])) { - return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' . - $fileXml['name'] . '" parameter for ' . - $tasksNamespace . 'paramgroup> id "' . - $param[$tasksNamespace . 'id'] . '" must have ' . - 'a ' . $tasksNamespace . 'name> tag'); - } - if (!preg_match('/[a-zA-Z0-9]+/', - $subparam[$tasksNamespace . 'name'])) { - return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' . - $fileXml['name'] . '" parameter "' . - $subparam[$tasksNamespace . 'name'] . - '" for ' . $tasksNamespace . 'paramgroup> id "' . - $param[$tasksNamespace . 'id'] . - '" is not a valid name. Must contain only alphanumeric characters'); - } - if (!isset($subparam[$tasksNamespace . 'prompt'])) { - return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' . - $fileXml['name'] . '" parameter "' . - $subparam[$tasksNamespace . 'name'] . - '" for ' . $tasksNamespace . 'paramgroup> id "' . - $param[$tasksNamespace . 'id'] . - '" must have a ' . $tasksNamespace . 'prompt> tag'); - } - if (!isset($subparam[$tasksNamespace . 'type'])) { - return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' . - $fileXml['name'] . '" parameter "' . - $subparam[$tasksNamespace . 'name'] . - '" for ' . $tasksNamespace . 'paramgroup> id "' . - $param[$tasksNamespace . 'id'] . - '" must have a ' . $tasksNamespace . 'type> tag'); - } - $definedparams[] = $param[$tasksNamespace . 'id'] . '::' . - $subparam[$tasksNamespace . 'name']; - } - } - } - return true; - } - - /** - * Initialize a task instance with the parameters - * @param array raw, parsed xml - * @param array attributes from the tag containing this task - * @param string|null last installed version of this package, if any (useful for upgrades) - */ - function init($xml, $fileattribs, $lastversion) - { - $this->_class = str_replace('/', '_', $fileattribs['name']); - $this->_filename = $fileattribs['name']; - $this->_class = str_replace ('.php', '', $this->_class) . '_postinstall'; - $this->_params = $xml; - $this->_lastversion = $lastversion; - } - - /** - * Strip the tasks: namespace from internal params - * - * @access private - */ - function _stripNamespace($params = null) - { - if ($params === null) { - $params = array(); - if (!is_array($this->_params)) { - return; - } - foreach ($this->_params as $i => $param) { - if (is_array($param)) { - $param = $this->_stripNamespace($param); - } - $params[str_replace($this->_pkg->getTasksNs() . ':', '', $i)] = $param; - } - $this->_params = $params; - } else { - $newparams = array(); - foreach ($params as $i => $param) { - if (is_array($param)) { - $param = $this->_stripNamespace($param); - } - $newparams[str_replace($this->_pkg->getTasksNs() . ':', '', $i)] = $param; - } - return $newparams; - } - } - - /** - * Unlike other tasks, the installed file name is passed in instead of the file contents, - * because this task is handled post-installation - * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 - * @param string file name - * @return bool|PEAR_Error false to skip this file, PEAR_Error to fail - * (use $this->throwError) - */ - function startSession($pkg, $contents) - { - if ($this->installphase != PEAR_TASK_INSTALL) { - return false; - } - // remove the tasks: namespace if present - $this->_pkg = $pkg; - $this->_stripNamespace(); - $this->logger->log(0, 'Including external post-installation script "' . - $contents . '" - any errors are in this script'); - include_once $contents; - if (class_exists($this->_class)) { - $this->logger->log(0, 'Inclusion succeeded'); - } else { - return $this->throwError('init of post-install script class "' . $this->_class - . '" failed'); - } - $this->_obj = new $this->_class; - $this->logger->log(1, 'running post-install script "' . $this->_class . '->init()"'); - PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - $res = $this->_obj->init($this->config, $pkg, $this->_lastversion); - PEAR::popErrorHandling(); - if ($res) { - $this->logger->log(0, 'init succeeded'); - } else { - return $this->throwError('init of post-install script "' . $this->_class . - '->init()" failed'); - } - $this->_contents = $contents; - return true; - } - - /** - * No longer used - * @see PEAR_PackageFile_v2::runPostinstallScripts() - * @param array an array of tasks - * @param string install or upgrade - * @access protected - * @static - */ - function run() - { - } -} -?> \ No newline at end of file diff --git a/pear/PEAR/Task/Postinstallscript/rw.php b/pear/PEAR/Task/Postinstallscript/rw.php deleted file mode 100644 index c48db6c..0000000 --- a/pear/PEAR/Task/Postinstallscript/rw.php +++ /dev/null @@ -1,169 +0,0 @@ - - read/write version - * - * PHP versions 4 and 5 - * - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a10 - */ -/** - * Base class - */ -require_once 'PEAR/Task/Postinstallscript.php'; -/** - * Abstracts the postinstallscript file task xml. - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a10 - */ -class PEAR_Task_Postinstallscript_rw extends PEAR_Task_Postinstallscript -{ - /** - * parent package file object - * - * @var PEAR_PackageFile_v2_rw - */ - var $_pkg; - /** - * Enter description here... - * - * @param PEAR_PackageFile_v2_rw $pkg - * @param PEAR_Config $config - * @param PEAR_Frontend $logger - * @param array $fileXml - * @return PEAR_Task_Postinstallscript_rw - */ - function PEAR_Task_Postinstallscript_rw(&$pkg, &$config, &$logger, $fileXml) - { - parent::PEAR_Task_Common($config, $logger, PEAR_TASK_PACKAGE); - $this->_contents = $fileXml; - $this->_pkg = &$pkg; - $this->_params = array(); - } - - function validate() - { - return $this->validateXml($this->_pkg, $this->_params, $this->config, $this->_contents); - } - - function getName() - { - return 'postinstallscript'; - } - - /** - * add a simple to the post-install script - * - * Order is significant, so call this method in the same - * sequence the users should see the paramgroups. The $params - * parameter should either be the result of a call to {@link getParam()} - * or an array of calls to getParam(). - * - * Use {@link addConditionTypeGroup()} to add a containing - * a tag - * @param string $id id as seen by the script - * @param array|false $params array of getParam() calls, or false for no params - * @param string|false $instructions - */ - function addParamGroup($id, $params = false, $instructions = false) - { - if ($params && isset($params[0]) && !isset($params[1])) { - $params = $params[0]; - } - $stuff = - array( - $this->_pkg->getTasksNs() . ':id' => $id, - ); - if ($instructions) { - $stuff[$this->_pkg->getTasksNs() . ':instructions'] = $instructions; - } - if ($params) { - $stuff[$this->_pkg->getTasksNs() . ':param'] = $params; - } - $this->_params[$this->_pkg->getTasksNs() . ':paramgroup'][] = $stuff; - } - - /** - * add a complex to the post-install script with conditions - * - * This inserts a with - * - * Order is significant, so call this method in the same - * sequence the users should see the paramgroups. The $params - * parameter should either be the result of a call to {@link getParam()} - * or an array of calls to getParam(). - * - * Use {@link addParamGroup()} to add a simple - * - * @param string $id id as seen by the script - * @param string $oldgroup id of the section referenced by - * - * @param string $param name of the from the older section referenced - * by - * @param string $value value to match of the parameter - * @param string $conditiontype one of '=', '!=', 'preg_match' - * @param array|false $params array of getParam() calls, or false for no params - * @param string|false $instructions - */ - function addConditionTypeGroup($id, $oldgroup, $param, $value, $conditiontype = '=', - $params = false, $instructions = false) - { - if ($params && isset($params[0]) && !isset($params[1])) { - $params = $params[0]; - } - $stuff = array( - $this->_pkg->getTasksNs() . ':id' => $id, - ); - if ($instructions) { - $stuff[$this->_pkg->getTasksNs() . ':instructions'] = $instructions; - } - $stuff[$this->_pkg->getTasksNs() . ':name'] = $oldgroup . '::' . $param; - $stuff[$this->_pkg->getTasksNs() . ':conditiontype'] = $conditiontype; - $stuff[$this->_pkg->getTasksNs() . ':value'] = $value; - if ($params) { - $stuff[$this->_pkg->getTasksNs() . ':param'] = $params; - } - $this->_params[$this->_pkg->getTasksNs() . ':paramgroup'][] = $stuff; - } - - function getXml() - { - return $this->_params; - } - - /** - * Use to set up a param tag for use in creating a paramgroup - * @static - */ - function getParam($name, $prompt, $type = 'string', $default = null) - { - if ($default !== null) { - return - array( - $this->_pkg->getTasksNs() . ':name' => $name, - $this->_pkg->getTasksNs() . ':prompt' => $prompt, - $this->_pkg->getTasksNs() . ':type' => $type, - $this->_pkg->getTasksNs() . ':default' => $default - ); - } - return - array( - $this->_pkg->getTasksNs() . ':name' => $name, - $this->_pkg->getTasksNs() . ':prompt' => $prompt, - $this->_pkg->getTasksNs() . ':type' => $type, - ); - } -} -?> \ No newline at end of file diff --git a/pear/PEAR/Task/Replace.php b/pear/PEAR/Task/Replace.php deleted file mode 100644 index 4a36b96..0000000 --- a/pear/PEAR/Task/Replace.php +++ /dev/null @@ -1,176 +0,0 @@ - - * - * PHP versions 4 and 5 - * - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a1 - */ -/** - * Base class - */ -require_once 'PEAR/Task/Common.php'; -/** - * Implements the replace file task. - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a1 - */ -class PEAR_Task_Replace extends PEAR_Task_Common -{ - var $type = 'simple'; - var $phase = PEAR_TASK_PACKAGEANDINSTALL; - var $_replacements; - - /** - * Validate the raw xml at parsing-time. - * @param PEAR_PackageFile_v2 - * @param array raw, parsed xml - * @param PEAR_Config - * @static - */ - function validateXml($pkg, $xml, $config, $fileXml) - { - if (!isset($xml['attribs'])) { - return array(PEAR_TASK_ERROR_NOATTRIBS); - } - if (!isset($xml['attribs']['type'])) { - return array(PEAR_TASK_ERROR_MISSING_ATTRIB, 'type'); - } - if (!isset($xml['attribs']['to'])) { - return array(PEAR_TASK_ERROR_MISSING_ATTRIB, 'to'); - } - if (!isset($xml['attribs']['from'])) { - return array(PEAR_TASK_ERROR_MISSING_ATTRIB, 'from'); - } - if ($xml['attribs']['type'] == 'pear-config') { - if (!in_array($xml['attribs']['to'], $config->getKeys())) { - return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'to', $xml['attribs']['to'], - $config->getKeys()); - } - } elseif ($xml['attribs']['type'] == 'php-const') { - if (defined($xml['attribs']['to'])) { - return true; - } else { - return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'to', $xml['attribs']['to'], - array('valid PHP constant')); - } - } elseif ($xml['attribs']['type'] == 'package-info') { - if (in_array($xml['attribs']['to'], - array('name', 'summary', 'channel', 'notes', 'extends', 'description', - 'release_notes', 'license', 'release-license', 'license-uri', - 'version', 'api-version', 'state', 'api-state', 'release_date', - 'date', 'time'))) { - return true; - } else { - return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'to', $xml['attribs']['to'], - array('name', 'summary', 'channel', 'notes', 'extends', 'description', - 'release_notes', 'license', 'release-license', 'license-uri', - 'version', 'api-version', 'state', 'api-state', 'release_date', - 'date', 'time')); - } - } else { - return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'type', $xml['attribs']['type'], - array('pear-config', 'package-info', 'php-const')); - } - return true; - } - - /** - * Initialize a task instance with the parameters - * @param array raw, parsed xml - * @param unused - */ - function init($xml, $attribs) - { - $this->_replacements = isset($xml['attribs']) ? array($xml) : $xml; - } - - /** - * Do a package.xml 1.0 replacement, with additional package-info fields available - * - * See validateXml() source for the complete list of allowed fields - * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 - * @param string file contents - * @param string the eventual final file location (informational only) - * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail - * (use $this->throwError), otherwise return the new contents - */ - function startSession($pkg, $contents, $dest) - { - $subst_from = $subst_to = array(); - foreach ($this->_replacements as $a) { - $a = $a['attribs']; - $to = ''; - if ($a['type'] == 'pear-config') { - if ($this->installphase == PEAR_TASK_PACKAGE) { - return false; - } - if ($a['to'] == 'master_server') { - $chan = $this->registry->getChannel($pkg->getChannel()); - if (!PEAR::isError($chan)) { - $to = $chan->getServer(); - } else { - $this->logger->log(0, "$dest: invalid pear-config replacement: $a[to]"); - return false; - } - } else { - if ($this->config->isDefinedLayer('ftp')) { - // try the remote config file first - $to = $this->config->get($a['to'], 'ftp', $pkg->getChannel()); - if (is_null($to)) { - // then default to local - $to = $this->config->get($a['to'], null, $pkg->getChannel()); - } - } else { - $to = $this->config->get($a['to'], null, $pkg->getChannel()); - } - } - if (is_null($to)) { - $this->logger->log(0, "$dest: invalid pear-config replacement: $a[to]"); - return false; - } - } elseif ($a['type'] == 'php-const') { - if ($this->installphase == PEAR_TASK_PACKAGE) { - return false; - } - if (defined($a['to'])) { - $to = constant($a['to']); - } else { - $this->logger->log(0, "$dest: invalid php-const replacement: $a[to]"); - return false; - } - } else { - if ($t = $pkg->packageInfo($a['to'])) { - $to = $t; - } else { - $this->logger->log(0, "$dest: invalid package-info replacement: $a[to]"); - return false; - } - } - if (!is_null($to)) { - $subst_from[] = $a['from']; - $subst_to[] = $to; - } - } - $this->logger->log(3, "doing " . sizeof($subst_from) . - " substitution(s) for $dest"); - if (sizeof($subst_from)) { - $contents = str_replace($subst_from, $subst_to, $contents); - } - return $contents; - } -} -?> \ No newline at end of file diff --git a/pear/PEAR/Task/Replace/rw.php b/pear/PEAR/Task/Replace/rw.php deleted file mode 100644 index 28496d1..0000000 --- a/pear/PEAR/Task/Replace/rw.php +++ /dev/null @@ -1,61 +0,0 @@ - - read/write version - * - * PHP versions 4 and 5 - * - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a10 - */ -/** - * Base class - */ -require_once 'PEAR/Task/Replace.php'; -/** - * Abstracts the replace task xml. - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a10 - */ -class PEAR_Task_Replace_rw extends PEAR_Task_Replace -{ - function PEAR_Task_Replace_rw(&$pkg, &$config, &$logger, $fileXml) - { - parent::PEAR_Task_Common($config, $logger, PEAR_TASK_PACKAGE); - $this->_contents = $fileXml; - $this->_pkg = &$pkg; - $this->_params = array(); - } - - function validate() - { - return $this->validateXml($this->_pkg, $this->_params, $this->config, $this->_contents); - } - - function setInfo($from, $to, $type) - { - $this->_params = array('attribs' => array('from' => $from, 'to' => $to, 'type' => $type)); - } - - function getName() - { - return 'replace'; - } - - function getXml() - { - return $this->_params; - } -} -?> \ No newline at end of file diff --git a/pear/PEAR/Task/Unixeol.php b/pear/PEAR/Task/Unixeol.php deleted file mode 100644 index 2c20313..0000000 --- a/pear/PEAR/Task/Unixeol.php +++ /dev/null @@ -1,77 +0,0 @@ - - * - * PHP versions 4 and 5 - * - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a1 - */ -/** - * Base class - */ -require_once 'PEAR/Task/Common.php'; -/** - * Implements the unix line endings file task. - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a1 - */ -class PEAR_Task_Unixeol extends PEAR_Task_Common -{ - var $type = 'simple'; - var $phase = PEAR_TASK_PACKAGE; - var $_replacements; - - /** - * Validate the raw xml at parsing-time. - * @param PEAR_PackageFile_v2 - * @param array raw, parsed xml - * @param PEAR_Config - * @static - */ - function validateXml($pkg, $xml, $config, $fileXml) - { - if ($xml != '') { - return array(PEAR_TASK_ERROR_INVALID, 'no attributes allowed'); - } - return true; - } - - /** - * Initialize a task instance with the parameters - * @param array raw, parsed xml - * @param unused - */ - function init($xml, $attribs) - { - } - - /** - * Replace all line endings with line endings customized for the current OS - * - * See validateXml() source for the complete list of allowed fields - * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 - * @param string file contents - * @param string the eventual final file location (informational only) - * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail - * (use $this->throwError), otherwise return the new contents - */ - function startSession($pkg, $contents, $dest) - { - $this->logger->log(3, "replacing all line endings with \\n in $dest"); - return preg_replace("/\r\n|\n\r|\r|\n/", "\n", $contents); - } -} -?> \ No newline at end of file diff --git a/pear/PEAR/Task/Unixeol/rw.php b/pear/PEAR/Task/Unixeol/rw.php deleted file mode 100644 index 5348fac..0000000 --- a/pear/PEAR/Task/Unixeol/rw.php +++ /dev/null @@ -1,56 +0,0 @@ - - read/write version - * - * PHP versions 4 and 5 - * - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a10 - */ -/** - * Base class - */ -require_once 'PEAR/Task/Unixeol.php'; -/** - * Abstracts the unixeol task xml. - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a10 - */ -class PEAR_Task_Unixeol_rw extends PEAR_Task_Unixeol -{ - function PEAR_Task_Unixeol_rw(&$pkg, &$config, &$logger, $fileXml) - { - parent::PEAR_Task_Common($config, $logger, PEAR_TASK_PACKAGE); - $this->_contents = $fileXml; - $this->_pkg = &$pkg; - $this->_params = array(); - } - - function validate() - { - return true; - } - - function getName() - { - return 'unixeol'; - } - - function getXml() - { - return ''; - } -} -?> \ No newline at end of file diff --git a/pear/PEAR/Task/Windowseol.php b/pear/PEAR/Task/Windowseol.php deleted file mode 100644 index d2bd2c8..0000000 --- a/pear/PEAR/Task/Windowseol.php +++ /dev/null @@ -1,77 +0,0 @@ - - * - * PHP versions 4 and 5 - * - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a1 - */ -/** - * Base class - */ -require_once 'PEAR/Task/Common.php'; -/** - * Implements the windows line endsings file task. - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a1 - */ -class PEAR_Task_Windowseol extends PEAR_Task_Common -{ - var $type = 'simple'; - var $phase = PEAR_TASK_PACKAGE; - var $_replacements; - - /** - * Validate the raw xml at parsing-time. - * @param PEAR_PackageFile_v2 - * @param array raw, parsed xml - * @param PEAR_Config - * @static - */ - function validateXml($pkg, $xml, $config, $fileXml) - { - if ($xml != '') { - return array(PEAR_TASK_ERROR_INVALID, 'no attributes allowed'); - } - return true; - } - - /** - * Initialize a task instance with the parameters - * @param array raw, parsed xml - * @param unused - */ - function init($xml, $attribs) - { - } - - /** - * Replace all line endings with windows line endings - * - * See validateXml() source for the complete list of allowed fields - * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 - * @param string file contents - * @param string the eventual final file location (informational only) - * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail - * (use $this->throwError), otherwise return the new contents - */ - function startSession($pkg, $contents, $dest) - { - $this->logger->log(3, "replacing all line endings with \\r\\n in $dest"); - return preg_replace("/\r\n|\n\r|\r|\n/", "\r\n", $contents); - } -} -?> \ No newline at end of file diff --git a/pear/PEAR/Task/Windowseol/rw.php b/pear/PEAR/Task/Windowseol/rw.php deleted file mode 100644 index e86e4f0..0000000 --- a/pear/PEAR/Task/Windowseol/rw.php +++ /dev/null @@ -1,56 +0,0 @@ - - read/write version - * - * PHP versions 4 and 5 - * - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a10 - */ -/** - * Base class - */ -require_once 'PEAR/Task/Windowseol.php'; -/** - * Abstracts the windowseol task xml. - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a10 - */ -class PEAR_Task_Windowseol_rw extends PEAR_Task_Windowseol -{ - function PEAR_Task_Windowseol_rw(&$pkg, &$config, &$logger, $fileXml) - { - parent::PEAR_Task_Common($config, $logger, PEAR_TASK_PACKAGE); - $this->_contents = $fileXml; - $this->_pkg = &$pkg; - $this->_params = array(); - } - - function validate() - { - return true; - } - - function getName() - { - return 'windowseol'; - } - - function getXml() - { - return ''; - } -} -?> \ No newline at end of file diff --git a/pear/PEAR/Validate.php b/pear/PEAR/Validate.php deleted file mode 100644 index 3c2471b..0000000 --- a/pear/PEAR/Validate.php +++ /dev/null @@ -1,629 +0,0 @@ - - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a1 - */ -/**#@+ - * Constants for install stage - */ -define('PEAR_VALIDATE_INSTALLING', 1); -define('PEAR_VALIDATE_UNINSTALLING', 2); // this is not bit-mapped like the others -define('PEAR_VALIDATE_NORMAL', 3); -define('PEAR_VALIDATE_DOWNLOADING', 4); // this is not bit-mapped like the others -define('PEAR_VALIDATE_PACKAGING', 7); -/**#@-*/ -require_once 'PEAR/Common.php'; -require_once 'PEAR/Validator/PECL.php'; - -/** - * Validation class for package.xml - channel-level advanced validation - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a1 - */ -class PEAR_Validate -{ - var $packageregex = _PEAR_COMMON_PACKAGE_NAME_PREG; - /** - * @var PEAR_PackageFile_v1|PEAR_PackageFile_v2 - */ - var $_packagexml; - /** - * @var int one of the PEAR_VALIDATE_* constants - */ - var $_state = PEAR_VALIDATE_NORMAL; - /** - * Format: ('error' => array('field' => name, 'reason' => reason), 'warning' => same) - * @var array - * @access private - */ - var $_failures = array('error' => array(), 'warning' => array()); - - /** - * Override this method to handle validation of normal package names - * @param string - * @return bool - * @access protected - */ - function _validPackageName($name) - { - return (bool) preg_match('/^' . $this->packageregex . '\\z/', $name); - } - - /** - * @param string package name to validate - * @param string name of channel-specific validation package - * @final - */ - function validPackageName($name, $validatepackagename = false) - { - if ($validatepackagename) { - if (strtolower($name) == strtolower($validatepackagename)) { - return (bool) preg_match('/^[a-zA-Z0-9_]+(?:\.[a-zA-Z0-9_]+)*\\z/', $name); - } - } - return $this->_validPackageName($name); - } - - /** - * This validates a bundle name, and bundle names must conform - * to the PEAR naming convention, so the method is final and static. - * @param string - * @final - * @static - */ - function validGroupName($name) - { - return (bool) preg_match('/^' . _PEAR_COMMON_PACKAGE_NAME_PREG . '\\z/', $name); - } - - /** - * Determine whether $state represents a valid stability level - * @param string - * @return bool - * @static - * @final - */ - function validState($state) - { - return in_array($state, array('snapshot', 'devel', 'alpha', 'beta', 'stable')); - } - - /** - * Get a list of valid stability levels - * @return array - * @static - * @final - */ - function getValidStates() - { - return array('snapshot', 'devel', 'alpha', 'beta', 'stable'); - } - - /** - * Determine whether a version is a properly formatted version number that can be used - * by version_compare - * @param string - * @return bool - * @static - * @final - */ - function validVersion($ver) - { - return (bool) preg_match(PEAR_COMMON_PACKAGE_VERSION_PREG, $ver); - } - - /** - * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 - */ - function setPackageFile(&$pf) - { - $this->_packagexml = &$pf; - } - - /** - * @access private - */ - function _addFailure($field, $reason) - { - $this->_failures['errors'][] = array('field' => $field, 'reason' => $reason); - } - - /** - * @access private - */ - function _addWarning($field, $reason) - { - $this->_failures['warnings'][] = array('field' => $field, 'reason' => $reason); - } - - function getFailures() - { - $failures = $this->_failures; - $this->_failures = array('warnings' => array(), 'errors' => array()); - return $failures; - } - - /** - * @param int one of the PEAR_VALIDATE_* constants - */ - function validate($state = null) - { - if (!isset($this->_packagexml)) { - return false; - } - if ($state !== null) { - $this->_state = $state; - } - $this->_failures = array('warnings' => array(), 'errors' => array()); - $this->validatePackageName(); - $this->validateVersion(); - $this->validateMaintainers(); - $this->validateDate(); - $this->validateSummary(); - $this->validateDescription(); - $this->validateLicense(); - $this->validateNotes(); - if ($this->_packagexml->getPackagexmlVersion() == '1.0') { - $this->validateState(); - $this->validateFilelist(); - } elseif ($this->_packagexml->getPackagexmlVersion() == '2.0' || - $this->_packagexml->getPackagexmlVersion() == '2.1') { - $this->validateTime(); - $this->validateStability(); - $this->validateDeps(); - $this->validateMainFilelist(); - $this->validateReleaseFilelist(); - //$this->validateGlobalTasks(); - $this->validateChangelog(); - } - return !((bool) count($this->_failures['errors'])); - } - - /** - * @access protected - */ - function validatePackageName() - { - if ($this->_state == PEAR_VALIDATE_PACKAGING || - $this->_state == PEAR_VALIDATE_NORMAL) { - if (($this->_packagexml->getPackagexmlVersion() == '2.0' || - $this->_packagexml->getPackagexmlVersion() == '2.1') && - $this->_packagexml->getExtends()) { - $version = $this->_packagexml->getVersion() . ''; - $name = $this->_packagexml->getPackage(); - $test = array_shift($a = explode('.', $version)); - if ($test == '0') { - return true; - } - $vlen = strlen($test); - $majver = substr($name, strlen($name) - $vlen); - while ($majver && !is_numeric($majver{0})) { - $majver = substr($majver, 1); - } - if ($majver != $test) { - $this->_addWarning('package', "package $name extends package " . - $this->_packagexml->getExtends() . ' and so the name should ' . - 'have a postfix equal to the major version like "' . - $this->_packagexml->getExtends() . $test . '"'); - return true; - } elseif (substr($name, 0, strlen($name) - $vlen) != - $this->_packagexml->getExtends()) { - $this->_addWarning('package', "package $name extends package " . - $this->_packagexml->getExtends() . ' and so the name must ' . - 'be an extension like "' . $this->_packagexml->getExtends() . - $test . '"'); - return true; - } - } - } - if (!$this->validPackageName($this->_packagexml->getPackage())) { - $this->_addFailure('name', 'package name "' . - $this->_packagexml->getPackage() . '" is invalid'); - return false; - } - } - - /** - * @access protected - */ - function validateVersion() - { - if ($this->_state != PEAR_VALIDATE_PACKAGING) { - if (!$this->validVersion($this->_packagexml->getVersion())) { - $this->_addFailure('version', - 'Invalid version number "' . $this->_packagexml->getVersion() . '"'); - } - return false; - } - $version = $this->_packagexml->getVersion(); - $versioncomponents = explode('.', $version); - if (count($versioncomponents) != 3) { - $this->_addWarning('version', - 'A version number should have 3 decimals (x.y.z)'); - return true; - } - $name = $this->_packagexml->getPackage(); - // version must be based upon state - switch ($this->_packagexml->getState()) { - case 'snapshot' : - return true; - case 'devel' : - if ($versioncomponents[0] . 'a' == '0a') { - return true; - } - if ($versioncomponents[0] == 0) { - $versioncomponents[0] = '0'; - $this->_addWarning('version', - 'version "' . $version . '" should be "' . - implode('.' ,$versioncomponents) . '"'); - } else { - $this->_addWarning('version', - 'packages with devel stability must be < version 1.0.0'); - } - return true; - break; - case 'alpha' : - case 'beta' : - // check for a package that extends a package, - // like Foo and Foo2 - if ($this->_state == PEAR_VALIDATE_PACKAGING) { - if (substr($versioncomponents[2], 1, 2) == 'rc') { - $this->_addFailure('version', 'Release Candidate versions ' . - 'must have capital RC, not lower-case rc'); - return false; - } - } - if (!$this->_packagexml->getExtends()) { - if ($versioncomponents[0] == '1') { - if ($versioncomponents[2]{0} == '0') { - if ($versioncomponents[2] == '0') { - // version 1.*.0000 - $this->_addWarning('version', - 'version 1.' . $versioncomponents[1] . - '.0 probably should not be alpha or beta'); - return true; - } elseif (strlen($versioncomponents[2]) > 1) { - // version 1.*.0RC1 or 1.*.0beta24 etc. - return true; - } else { - // version 1.*.0 - $this->_addWarning('version', - 'version 1.' . $versioncomponents[1] . - '.0 probably should not be alpha or beta'); - return true; - } - } else { - $this->_addWarning('version', - 'bugfix versions (1.3.x where x > 0) probably should ' . - 'not be alpha or beta'); - return true; - } - } elseif ($versioncomponents[0] != '0') { - $this->_addWarning('version', - 'major versions greater than 1 are not allowed for packages ' . - 'without an tag or an identical postfix (foo2 v2.0.0)'); - return true; - } - if ($versioncomponents[0] . 'a' == '0a') { - return true; - } - if ($versioncomponents[0] == 0) { - $versioncomponents[0] = '0'; - $this->_addWarning('version', - 'version "' . $version . '" should be "' . - implode('.' ,$versioncomponents) . '"'); - } - } else { - $vlen = strlen($versioncomponents[0] . ''); - $majver = substr($name, strlen($name) - $vlen); - while ($majver && !is_numeric($majver{0})) { - $majver = substr($majver, 1); - } - if (($versioncomponents[0] != 0) && $majver != $versioncomponents[0]) { - $this->_addWarning('version', 'first version number "' . - $versioncomponents[0] . '" must match the postfix of ' . - 'package name "' . $name . '" (' . - $majver . ')'); - return true; - } - if ($versioncomponents[0] == $majver) { - if ($versioncomponents[2]{0} == '0') { - if ($versioncomponents[2] == '0') { - // version 2.*.0000 - $this->_addWarning('version', - "version $majver." . $versioncomponents[1] . - '.0 probably should not be alpha or beta'); - return false; - } elseif (strlen($versioncomponents[2]) > 1) { - // version 2.*.0RC1 or 2.*.0beta24 etc. - return true; - } else { - // version 2.*.0 - $this->_addWarning('version', - "version $majver." . $versioncomponents[1] . - '.0 cannot be alpha or beta'); - return true; - } - } else { - $this->_addWarning('version', - "bugfix versions ($majver.x.y where y > 0) should " . - 'not be alpha or beta'); - return true; - } - } elseif ($versioncomponents[0] != '0') { - $this->_addWarning('version', - "only versions 0.x.y and $majver.x.y are allowed for alpha/beta releases"); - return true; - } - if ($versioncomponents[0] . 'a' == '0a') { - return true; - } - if ($versioncomponents[0] == 0) { - $versioncomponents[0] = '0'; - $this->_addWarning('version', - 'version "' . $version . '" should be "' . - implode('.' ,$versioncomponents) . '"'); - } - } - return true; - break; - case 'stable' : - if ($versioncomponents[0] == '0') { - $this->_addWarning('version', 'versions less than 1.0.0 cannot ' . - 'be stable'); - return true; - } - if (!is_numeric($versioncomponents[2])) { - if (preg_match('/\d+(rc|a|alpha|b|beta)\d*/i', - $versioncomponents[2])) { - $this->_addWarning('version', 'version "' . $version . '" or any ' . - 'RC/beta/alpha version cannot be stable'); - return true; - } - } - // check for a package that extends a package, - // like Foo and Foo2 - if ($this->_packagexml->getExtends()) { - $vlen = strlen($versioncomponents[0] . ''); - $majver = substr($name, strlen($name) - $vlen); - while ($majver && !is_numeric($majver{0})) { - $majver = substr($majver, 1); - } - if (($versioncomponents[0] != 0) && $majver != $versioncomponents[0]) { - $this->_addWarning('version', 'first version number "' . - $versioncomponents[0] . '" must match the postfix of ' . - 'package name "' . $name . '" (' . - $majver . ')'); - return true; - } - } elseif ($versioncomponents[0] > 1) { - $this->_addWarning('version', 'major version x in x.y.z may not be greater than ' . - '1 for any package that does not have an tag'); - } - return true; - break; - default : - return false; - break; - } - } - - /** - * @access protected - */ - function validateMaintainers() - { - // maintainers can only be truly validated server-side for most channels - // but allow this customization for those who wish it - return true; - } - - /** - * @access protected - */ - function validateDate() - { - if ($this->_state == PEAR_VALIDATE_NORMAL || - $this->_state == PEAR_VALIDATE_PACKAGING) { - - if (!preg_match('/(\d\d\d\d)\-(\d\d)\-(\d\d)/', - $this->_packagexml->getDate(), $res) || - count($res) < 4 - || !checkdate($res[2], $res[3], $res[1]) - ) { - $this->_addFailure('date', 'invalid release date "' . - $this->_packagexml->getDate() . '"'); - return false; - } - - if ($this->_state == PEAR_VALIDATE_PACKAGING && - $this->_packagexml->getDate() != date('Y-m-d')) { - $this->_addWarning('date', 'Release Date "' . - $this->_packagexml->getDate() . '" is not today'); - } - } - return true; - } - - /** - * @access protected - */ - function validateTime() - { - if (!$this->_packagexml->getTime()) { - // default of no time value set - return true; - } - - // packager automatically sets time, so only validate if pear validate is called - if ($this->_state = PEAR_VALIDATE_NORMAL) { - if (!preg_match('/\d\d:\d\d:\d\d/', - $this->_packagexml->getTime())) { - $this->_addFailure('time', 'invalid release time "' . - $this->_packagexml->getTime() . '"'); - return false; - } - - $result = preg_match('|\d{2}\:\d{2}\:\d{2}|', $this->_packagexml->getTime(), $matches); - if ($result === false || empty($matches)) { - $this->_addFailure('time', 'invalid release time "' . - $this->_packagexml->getTime() . '"'); - return false; - } - } - - return true; - } - - /** - * @access protected - */ - function validateState() - { - // this is the closest to "final" php4 can get - if (!PEAR_Validate::validState($this->_packagexml->getState())) { - if (strtolower($this->_packagexml->getState() == 'rc')) { - $this->_addFailure('state', 'RC is not a state, it is a version ' . - 'postfix, use ' . $this->_packagexml->getVersion() . 'RC1, state beta'); - } - $this->_addFailure('state', 'invalid release state "' . - $this->_packagexml->getState() . '", must be one of: ' . - implode(', ', PEAR_Validate::getValidStates())); - return false; - } - return true; - } - - /** - * @access protected - */ - function validateStability() - { - $ret = true; - $packagestability = $this->_packagexml->getState(); - $apistability = $this->_packagexml->getState('api'); - if (!PEAR_Validate::validState($packagestability)) { - $this->_addFailure('state', 'invalid release stability "' . - $this->_packagexml->getState() . '", must be one of: ' . - implode(', ', PEAR_Validate::getValidStates())); - $ret = false; - } - $apistates = PEAR_Validate::getValidStates(); - array_shift($apistates); // snapshot is not allowed - if (!in_array($apistability, $apistates)) { - $this->_addFailure('state', 'invalid API stability "' . - $this->_packagexml->getState('api') . '", must be one of: ' . - implode(', ', $apistates)); - $ret = false; - } - return $ret; - } - - /** - * @access protected - */ - function validateSummary() - { - return true; - } - - /** - * @access protected - */ - function validateDescription() - { - return true; - } - - /** - * @access protected - */ - function validateLicense() - { - return true; - } - - /** - * @access protected - */ - function validateNotes() - { - return true; - } - - /** - * for package.xml 2.0 only - channels can't use package.xml 1.0 - * @access protected - */ - function validateDependencies() - { - return true; - } - - /** - * for package.xml 1.0 only - * @access private - */ - function _validateFilelist() - { - return true; // placeholder for now - } - - /** - * for package.xml 2.0 only - * @access protected - */ - function validateMainFilelist() - { - return true; // placeholder for now - } - - /** - * for package.xml 2.0 only - * @access protected - */ - function validateReleaseFilelist() - { - return true; // placeholder for now - } - - /** - * @access protected - */ - function validateChangelog() - { - return true; - } - - /** - * @access protected - */ - function validateFilelist() - { - return true; - } - - /** - * @access protected - */ - function validateDeps() - { - return true; - } -} \ No newline at end of file diff --git a/pear/PEAR/Validator/PECL.php b/pear/PEAR/Validator/PECL.php deleted file mode 100644 index 7e0769a..0000000 --- a/pear/PEAR/Validator/PECL.php +++ /dev/null @@ -1,63 +0,0 @@ - - * @copyright 1997-2006 The PHP Group - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a5 - */ -/** - * This is the parent class for all validators - */ -require_once 'PEAR/Validate.php'; -/** - * Channel Validator for the pecl.php.net channel - * @category pear - * @package PEAR - * @author Greg Beaver - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a5 - */ -class PEAR_Validator_PECL extends PEAR_Validate -{ - function validateVersion() - { - if ($this->_state == PEAR_VALIDATE_PACKAGING) { - $version = $this->_packagexml->getVersion(); - $versioncomponents = explode('.', $version); - $last = array_pop($versioncomponents); - if (substr($last, 1, 2) == 'rc') { - $this->_addFailure('version', 'Release Candidate versions must have ' . - 'upper-case RC, not lower-case rc'); - return false; - } - } - return true; - } - - function validatePackageName() - { - $ret = parent::validatePackageName(); - if ($this->_packagexml->getPackageType() == 'extsrc' || - $this->_packagexml->getPackageType() == 'zendextsrc') { - if (strtolower($this->_packagexml->getPackage()) != - strtolower($this->_packagexml->getProvidesExtension())) { - $this->_addWarning('providesextension', 'package name "' . - $this->_packagexml->getPackage() . '" is different from extension name "' . - $this->_packagexml->getProvidesExtension() . '"'); - } - } - return $ret; - } -} -?> \ No newline at end of file diff --git a/pear/PEAR/Warning.php b/pear/PEAR/Warning.php deleted file mode 100644 index 17ac905..0000000 --- a/pear/PEAR/Warning.php +++ /dev/null @@ -1,379 +0,0 @@ - | -// +----------------------------------------------------------------------+ -// -// $Id$ -require_once 'PEAR/Exception.php'; - -/** - * Exception class for internal PEAR_Warning exceptions - * @package PEAR - */ -class PEAR_WarningException extends PEAR_Exception {} - -interface PEAR_WarningInterface -{ - /** - * Get the severity of this warning ('warning', 'notice') - * @return string - */ - public function getLevel(); -} - -/** - * Warning mechanism for PEAR PHP5-only packages. - * - * For users: - * - * Unlike PEAR_ErrorStack, PEAR_Warning is designed to be used on a transactional basis. - * - * - * doSomethingComplex(); - * if (PEAR_Warning::hasWarnings()) { - * $warnings = PEAR_Warning::end(); - * throw new Mypackage_Exception('unclean doSomethingComplex', $warnings); - * } else { - * $c->doSomethingElse(); - * } - * ?> - * - * - * Only warnings that occur between ::begin() and ::end() will be processed. Remember, - * a warning is a non-fatal error, exceptions will be used for unrecoverable errors in - * all PEAR packages, and you should follow this model to be safe! - * - * For developers: - * - * This class can be used globally or locally. For global use, a - * series of static methods have been provided. The class is designed - * for lazy loading, and so the following code will work, and increase - * efficiency on production servers: - * - * - * - * - * - * This means that PEAR_Warning can literally be used without the need for - * require_once 'PEAR/Warning.php';! - * - * You can also pass in an exception class as a warning - * - * - * - * - * - * An interface is provided to allow for severity differentiation - * - * - * _level = $level; - * parent::__construct($message, $p1, $p2); - * } - * - * public function getLevel() - * { - * return $this->_level; - * } - * } - * PEAR_Warning::add(new MyPackage_Warning('some info', 'notice')); - * ?> - * - * - * This can be used with {@link setErrorHandling()} to ignore warnings of different severities - * for complex error situations. - * - * For local situations like an internal warning system for a parser that may become the cause - * of a single PEAR_Exception, PEAR_Warning can also be instantiated and used without any connection - * to the global warning stack. - * @package PEAR - */ -class PEAR_Warning -{ - /** - * properties used for global warning stacks - */ - protected static $_hasWarnings = false; - - protected static $warnings = array(); - protected static $go = false; - protected static $levels = array('warning', 'notice'); - - private static $_observers = array(); - private static $_uniqueid = 0; - /** - * properties used for instantiation of private warning stack - */ - private $_warnings = array(); - private $_go = false; - private $_context; - - /** - * Begin tracking all global warnings - */ - static public function begin() - { - if (class_exists('PEAR_ErrorStack')) { - PEAR_ErrorStack::setPEARWarningCallback(array('PEAR_Warning', '_catch')); - } - self::$go = true; - self::$_hasWarnings = false; - } - - /** - * @return bool - */ - static public function hasWarnings() - { - return self::$_hasWarnings; - } - - /** - * Stop tracking global warnings - * @return array an array of all warnings in array and PEAR_Exception format - * suitable for use as a PEAR_Exception cause - */ - static public function end() - { - if (class_exists('PEAR_ErrorStack')) { - PEAR_ErrorStack::setPEARWarningCallback(false); - } - self::$go = false; - self::$_hasWarnings = false; - $a = self::$warnings; - self::$warnings = array(); - return $a; - } - - /** - * @param mixed A valid callback that accepts either a - * PEAR_Exception or PEAR_ErrorStack-style array - * @param string The name of the observer. Use this if you want - * to remove it later with removeObserver(). - * {@link getUniqueId()} can be used to generate a label - */ - public static function addObserver($callback, $label = 'default') - { - self::$_observers[$label] = $callback; - } - - /** - * @param mixed observer ID - */ - public static function removeObserver($label = 'default') - { - unset(self::$_observers[$label]); - } - - /** - * @return int unique identifier for an observer - */ - public static function getUniqueId() - { - return self::$_uniqueid++; - } - - /** - * Set the warning levels that should be captured by the warning mechanism - * - * WARNING: no error checking or spell checking. - * @param array - */ - public static function setErrorHandling($levels) - { - self::$_levels = $levels; - } - - /** - * Add a warning to the global warning stack. - * - * Note: if you want file/line context, use an exception object - * @param PEAR_Exception|string|int Either pass in an exception to use as the warning, or an - * error code or some other error class differentiation technique - * @param string Package is required if $codeOrException is not a PEAR_Exception object - * @param string Error message, use %param% to do automatic parameter replacement from $params - * @param array Error parameters - * @param string Error level, use the English name - - * @throws PEAR_WarningException if $codeOrException is not a PEAR_Exception and $package is not set - */ - static public function add($codeOrException, $package = '', $msg = '', $params = array(), - $level = 'warning') - { - if ($codeOrException instanceof PEAR_Exception) { - if ($codeOrException instanceof PEAR_WarningInterface) { - if (in_array($codeOrException->getLevel(), self::$levels)) { - self::_signal($codeOrException); - } - } else { - self::_signal($codeOrException); - } - } else { - if (empty($package)) { - throw new PEAR_WarningException('Package must be set for a non-exception warning'); - } - if (in_array($level, self::$levels)) { - $warning = self::_formatWarning($codeOrException, $package, $level, $msg, $params); - self::_signal($warning); - } - } - if (self::$go) { - self::$_hasWarnings = true; - self::$warnings[] = $warning; - } - } - - /** - * @param string the package name, or other context information that can be used - * to differentiate this warning from warnings thrown by other packages - * @throws PEAR_WarningException if $context is not a string - */ - public function __construct($context) - { - if (!is_string($context)) { - throw new PEAR_WarningException('$context constructor argument must be a string'); - } - $this->_context = $context; - } - - /** - * Local stack function for adding a warning - note that package is not needed, as it is - * defined in the constructor. - * - * Note: if you want file/line context, use an exception object - * @param PEAR_Exception|string|int Either pass in an exception to use as the warning, or an - * error code or some other error class differentiation technique - * @param string Error message, use %param% to do automatic parameter replacement from $params - * @param array Error parameters - * @param string Error level, use the English name - */ - public function localAdd($code, $msg = '', $params = array(), $level = 'warning') - { - if ($codeOrException instanceof PEAR_Exception) { - $this->_warnings[] = $codeOrException; - } else { - $warning = self::_formatWarning($codeOrException, $this->_context, $level, $msg); - $this->_warnings[] = $warning; - } - } - - /** - * Begin a local warning stack session - */ - public function localBegin() - { - $this->_warnings = array(); - $this->_go = true; - } - - /** - * End a local warning stack session - * @return array - */ - public function localEnd() - { - $a = $this->_warnings; - $this->_warnings = array(); - $this->_go = false; - return $a; - } - - /** - * Do not use this function directly - it should only be used by PEAR_ErrorStack - * @access private - */ - static public function _catch($err) - { - self::_signal($err); - } - - private static function _signal($warning) - { - foreach (self::$_observers as $func) { - if (is_callable($func)) { - call_user_func($func, $this); - continue; - } - settype($func, 'array'); - switch ($func[0]) { - case PEAR_EXCEPTION_PRINT : - $f = (isset($func[1])) ? $func[1] : '%s'; - printf($f, $this->getMessage()); - break; - case PEAR_EXCEPTION_TRIGGER : - $f = (isset($func[1])) ? $func[1] : E_USER_NOTICE; - trigger_error($this->getMessage(), $f); - break; - case PEAR_EXCEPTION_DIE : - $f = (isset($func[1])) ? $func[1] : '%s'; - die(printf($f, $this->getMessage())); - break; - default: - trigger_error('invalid observer type', E_USER_WARNING); - } - } - } - - static private function _formatWarning($code, $package, $level, $msg, $params, $backtrace) - { - return array('package' => $package, - 'code' => $code, - 'level' => $level, - 'message' => self::_formatMessage($msg, $params), - 'params' => $params); - } - - static private function _formatMessage($msg, $params) - { - if (count($params)) { - foreach ($params as $name => $val) { - if (strpos($msg, '%' . $name . '%') !== false) { - if (is_array($val)) { - // don't pass in an array that you expect to display unless it is 1-dimensional! - $val = implode(', ', $val); - } - if (is_object($val)) { - if (method_exists($val, '__toString')) { - $val = $val->__toString(); - } else { - $val = 'Object'; - } - } - $msg = str_replace('%' . $name . '%', $val, $msg); - } - } - } - return $msg; - } -} -?> \ No newline at end of file diff --git a/pear/PEAR/XMLParser.php b/pear/PEAR/XMLParser.php deleted file mode 100644 index a70988b..0000000 --- a/pear/PEAR/XMLParser.php +++ /dev/null @@ -1,253 +0,0 @@ - - * @author Stephan Schmidt (original XML_Unserializer code) - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 1.4.0a1 - */ - -/** - * Parser for any xml file - * @category pear - * @package PEAR - * @author Greg Beaver - * @author Stephan Schmidt (original XML_Unserializer code) - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license New BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a1 - */ -class PEAR_XMLParser -{ - /** - * unserilialized data - * @var string $_serializedData - */ - var $_unserializedData = null; - - /** - * name of the root tag - * @var string $_root - */ - var $_root = null; - - /** - * stack for all data that is found - * @var array $_dataStack - */ - var $_dataStack = array(); - - /** - * stack for all values that are generated - * @var array $_valStack - */ - var $_valStack = array(); - - /** - * current tag depth - * @var int $_depth - */ - var $_depth = 0; - - /** - * The XML encoding to use - * @var string $encoding - */ - var $encoding = 'ISO-8859-1'; - - /** - * @return array - */ - function getData() - { - return $this->_unserializedData; - } - - /** - * @param string xml content - * @return true|PEAR_Error - */ - function parse($data) - { - if (!extension_loaded('xml')) { - include_once 'PEAR.php'; - return PEAR::raiseError("XML Extension not found", 1); - } - $this->_dataStack = $this->_valStack = array(); - $this->_depth = 0; - - if ( - strpos($data, 'encoding="UTF-8"') - || strpos($data, 'encoding="utf-8"') - || strpos($data, "encoding='UTF-8'") - || strpos($data, "encoding='utf-8'") - ) { - $this->encoding = 'UTF-8'; - } - - if (version_compare(phpversion(), '5.0.0', 'lt') && $this->encoding == 'UTF-8') { - $data = utf8_decode($data); - $this->encoding = 'ISO-8859-1'; - } - - $xp = xml_parser_create($this->encoding); - xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, 0); - xml_set_object($xp, $this); - xml_set_element_handler($xp, 'startHandler', 'endHandler'); - xml_set_character_data_handler($xp, 'cdataHandler'); - if (!xml_parse($xp, $data)) { - $msg = xml_error_string(xml_get_error_code($xp)); - $line = xml_get_current_line_number($xp); - xml_parser_free($xp); - include_once 'PEAR.php'; - return PEAR::raiseError("XML Error: '$msg' on line '$line'", 2); - } - xml_parser_free($xp); - return true; - } - - /** - * Start element handler for XML parser - * - * @access private - * @param object $parser XML parser object - * @param string $element XML element - * @param array $attribs attributes of XML tag - * @return void - */ - function startHandler($parser, $element, $attribs) - { - $this->_depth++; - $this->_dataStack[$this->_depth] = null; - - $val = array( - 'name' => $element, - 'value' => null, - 'type' => 'string', - 'childrenKeys' => array(), - 'aggregKeys' => array() - ); - - if (count($attribs) > 0) { - $val['children'] = array(); - $val['type'] = 'array'; - $val['children']['attribs'] = $attribs; - } - - array_push($this->_valStack, $val); - } - - /** - * post-process data - * - * @param string $data - * @param string $element element name - */ - function postProcess($data, $element) - { - return trim($data); - } - - /** - * End element handler for XML parser - * - * @access private - * @param object XML parser object - * @param string - * @return void - */ - function endHandler($parser, $element) - { - $value = array_pop($this->_valStack); - $data = $this->postProcess($this->_dataStack[$this->_depth], $element); - - // adjust type of the value - switch (strtolower($value['type'])) { - // unserialize an array - case 'array': - if ($data !== '') { - $value['children']['_content'] = $data; - } - - $value['value'] = isset($value['children']) ? $value['children'] : array(); - break; - - /* - * unserialize a null value - */ - case 'null': - $data = null; - break; - - /* - * unserialize any scalar value - */ - default: - settype($data, $value['type']); - $value['value'] = $data; - break; - } - - $parent = array_pop($this->_valStack); - if ($parent === null) { - $this->_unserializedData = &$value['value']; - $this->_root = &$value['name']; - return true; - } - - // parent has to be an array - if (!isset($parent['children']) || !is_array($parent['children'])) { - $parent['children'] = array(); - if ($parent['type'] != 'array') { - $parent['type'] = 'array'; - } - } - - if (!empty($value['name'])) { - // there already has been a tag with this name - if (in_array($value['name'], $parent['childrenKeys'])) { - // no aggregate has been created for this tag - if (!in_array($value['name'], $parent['aggregKeys'])) { - if (isset($parent['children'][$value['name']])) { - $parent['children'][$value['name']] = array($parent['children'][$value['name']]); - } else { - $parent['children'][$value['name']] = array(); - } - array_push($parent['aggregKeys'], $value['name']); - } - array_push($parent['children'][$value['name']], $value['value']); - } else { - $parent['children'][$value['name']] = &$value['value']; - array_push($parent['childrenKeys'], $value['name']); - } - } else { - array_push($parent['children'],$value['value']); - } - array_push($this->_valStack, $parent); - - $this->_depth--; - } - - /** - * Handler for character data - * - * @access private - * @param object XML parser object - * @param string CDATA - * @return void - */ - function cdataHandler($parser, $cdata) - { - $this->_dataStack[$this->_depth] .= $cdata; - } -} \ No newline at end of file diff --git a/pear/PEAR5.php b/pear/PEAR5.php deleted file mode 100644 index 4286067..0000000 --- a/pear/PEAR5.php +++ /dev/null @@ -1,33 +0,0 @@ - | -// +-----------------------------------------------------------------------------+ -// -/** - * The Graph.php file contains the definition of the Structures_Graph class - * - * @see Structures_Graph - * @package Structures_Graph - */ - -/* dependencies {{{ */ -/** PEAR base classes */ -require_once 'PEAR.php'; -/** Graph Node */ -require_once 'Structures/Graph/Node.php'; -/* }}} */ - -define('STRUCTURES_GRAPH_ERROR_GENERIC', 100); - -/* class Structures_Graph {{{ */ -/** - * The Structures_Graph class represents a graph data structure. - * - * A Graph is a data structure composed by a set of nodes, connected by arcs. - * Graphs may either be directed or undirected. In a directed graph, arcs are - * directional, and can be traveled only one way. In an undirected graph, arcs - * are bidirectional, and can be traveled both ways. - * - * @author Sérgio Carvalho - * @copyright (c) 2004 by Sérgio Carvalho - * @package Structures_Graph - */ -/* }}} */ -class Structures_Graph { - /* fields {{{ */ - /** - * @access private - */ - var $_nodes = array(); - /** - * @access private - */ - var $_directed = false; - /* }}} */ - - /* Constructor {{{ */ - /** - * - * Constructor - * - * @param boolean Set to true if the graph is directed. Set to false if it is not directed. (Optional, defaults to true) - * @access public - */ - function Structures_Graph($directed = true) { - $this->_directed = $directed; - } - /* }}} */ - - /* isDirected {{{ */ - /** - * - * Return true if a graph is directed - * - * @return boolean true if the graph is directed - * @access public - */ - function isDirected() { - return (boolean) $this->_directed; - } - /* }}} */ - - /* addNode {{{ */ - /** - * - * Add a Node to the Graph - * - * @param Structures_Graph_Node The node to be added. - * @access public - */ - function addNode(&$newNode) { - // We only add nodes - if (!is_a($newNode, 'Structures_Graph_Node')) return Pear::raiseError('Structures_Graph::addNode received an object that is not a Structures_Graph_Node', STRUCTURES_GRAPH_ERROR_GENERIC); - // Graphs are node *sets*, so duplicates are forbidden. We allow nodes that are exactly equal, but disallow equal references. - foreach($this->_nodes as $key => $node) { - /* - ZE1 equality operators choke on the recursive cycle introduced by the _graph field in the Node object. - So, we'll check references the hard way (change $this->_nodes[$key] and check if the change reflects in - $node) - */ - $savedData = $this->_nodes[$key]; - $referenceIsEqualFlag = false; - $this->_nodes[$key] = true; - if ($node === true) { - $this->_nodes[$key] = false; - if ($node === false) $referenceIsEqualFlag = true; - } - $this->_nodes[$key] = $savedData; - if ($referenceIsEqualFlag) return Pear::raiseError('Structures_Graph::addNode received an object that is a duplicate for this dataset', STRUCTURES_GRAPH_ERROR_GENERIC); - } - $this->_nodes[] =& $newNode; - $newNode->setGraph($this); - } - /* }}} */ - - /* removeNode (unimplemented) {{{ */ - /** - * - * Remove a Node from the Graph - * - * @todo This is unimplemented - * @param Structures_Graph_Node The node to be removed from the graph - * @access public - */ - function removeNode(&$node) { - } - /* }}} */ - - /* getNodes {{{ */ - /** - * - * Return the node set, in no particular order. For ordered node sets, use a Graph Manipulator insted. - * - * @access public - * @see Structures_Graph_Manipulator_TopologicalSorter - * @return array The set of nodes in this graph - */ - function &getNodes() { - return $this->_nodes; - } - /* }}} */ -} -?> diff --git a/pear/Structures/Graph/Manipulator/AcyclicTest.php b/pear/Structures/Graph/Manipulator/AcyclicTest.php deleted file mode 100644 index fc1ba92..0000000 --- a/pear/Structures/Graph/Manipulator/AcyclicTest.php +++ /dev/null @@ -1,136 +0,0 @@ - | -// +-----------------------------------------------------------------------------+ -// -/** - * This file contains the definition of the Structures_Graph_Manipulator_AcyclicTest graph manipulator. - * - * @see Structures_Graph_Manipulator_AcyclicTest - * @package Structures_Graph - */ - -/* dependencies {{{ */ -/** */ -require_once 'PEAR.php'; -/** */ -require_once 'Structures/Graph.php'; -/** */ -require_once 'Structures/Graph/Node.php'; -/* }}} */ - -/* class Structures_Graph_Manipulator_AcyclicTest {{{ */ -/** - * The Structures_Graph_Manipulator_AcyclicTest is a graph manipulator - * which tests whether a graph contains a cycle. - * - * The definition of an acyclic graph used in this manipulator is that of a - * DAG. The graph must be directed, or else it is considered cyclic, even when - * there are no arcs. - * - * @author Sérgio Carvalho - * @copyright (c) 2004 by Sérgio Carvalho - * @package Structures_Graph - */ -class Structures_Graph_Manipulator_AcyclicTest { - /* _nonVisitedInDegree {{{ */ - /** - * - * This is a variant of Structures_Graph::inDegree which does - * not count nodes marked as visited. - * - * @access private - * @return integer Number of non-visited nodes that link to this one - */ - function _nonVisitedInDegree(&$node) { - $result = 0; - $graphNodes =& $node->_graph->getNodes(); - foreach (array_keys($graphNodes) as $key) { - if ((!$graphNodes[$key]->getMetadata('acyclic-test-visited')) && $graphNodes[$key]->connectsTo($node)) $result++; - } - return $result; - - } - /* }}} */ - - /* _isAcyclic {{{ */ - /** - * @access private - */ - function _isAcyclic(&$graph) { - // Mark every node as not visited - $nodes =& $graph->getNodes(); - $nodeKeys = array_keys($nodes); - $refGenerator = array(); - foreach($nodeKeys as $key) { - $refGenerator[] = false; - $nodes[$key]->setMetadata('acyclic-test-visited', $refGenerator[sizeof($refGenerator) - 1]); - } - - // Iteratively peel off leaf nodes - do { - // Find out which nodes are leafs (excluding visited nodes) - $leafNodes = array(); - foreach($nodeKeys as $key) { - if ((!$nodes[$key]->getMetadata('acyclic-test-visited')) && Structures_Graph_Manipulator_AcyclicTest::_nonVisitedInDegree($nodes[$key]) == 0) { - $leafNodes[] =& $nodes[$key]; - } - } - // Mark leafs as visited - for ($i=sizeof($leafNodes) - 1; $i>=0; $i--) { - $visited =& $leafNodes[$i]->getMetadata('acyclic-test-visited'); - $visited = true; - $leafNodes[$i]->setMetadata('acyclic-test-visited', $visited); - } - } while (sizeof($leafNodes) > 0); - - // If graph is a DAG, there should be no non-visited nodes. Let's try to prove otherwise - $result = true; - foreach($nodeKeys as $key) if (!$nodes[$key]->getMetadata('acyclic-test-visited')) $result = false; - - // Cleanup visited marks - foreach($nodeKeys as $key) $nodes[$key]->unsetMetadata('acyclic-test-visited'); - - return $result; - } - /* }}} */ - - /* isAcyclic {{{ */ - /** - * - * isAcyclic returns true if a graph contains no cycles, false otherwise. - * - * @return boolean true iff graph is acyclic - * @access public - */ - function isAcyclic(&$graph) { - // We only test graphs - if (!is_a($graph, 'Structures_Graph')) return Pear::raiseError('Structures_Graph_Manipulator_AcyclicTest::isAcyclic received an object that is not a Structures_Graph', STRUCTURES_GRAPH_ERROR_GENERIC); - if (!$graph->isDirected()) return false; // Only directed graphs may be acyclic - - return Structures_Graph_Manipulator_AcyclicTest::_isAcyclic($graph); - } - /* }}} */ -} -/* }}} */ -?> diff --git a/pear/Structures/Graph/Manipulator/TopologicalSorter.php b/pear/Structures/Graph/Manipulator/TopologicalSorter.php deleted file mode 100644 index 98a9fa0..0000000 --- a/pear/Structures/Graph/Manipulator/TopologicalSorter.php +++ /dev/null @@ -1,153 +0,0 @@ - | -// +-----------------------------------------------------------------------------+ -// -/** - * This file contains the definition of the Structures_Graph_Manipulator_TopologicalSorter class. - * - * @see Structures_Graph_Manipulator_TopologicalSorter - * @package Structures_Graph - */ - -/* dependencies {{{ */ -/** */ -require_once 'PEAR.php'; -/** */ -require_once 'Structures/Graph.php'; -/** */ -require_once 'Structures/Graph/Node.php'; -/** */ -require_once 'Structures/Graph/Manipulator/AcyclicTest.php'; -/* }}} */ - -/* class Structures_Graph_Manipulator_TopologicalSorter {{{ */ -/** - * The Structures_Graph_Manipulator_TopologicalSorter is a manipulator - * which is able to return the set of nodes in a graph, sorted by topological - * order. - * - * A graph may only be sorted topologically iff it's a DAG. You can test it - * with the Structures_Graph_Manipulator_AcyclicTest. - * - * @author Sérgio Carvalho - * @copyright (c) 2004 by Sérgio Carvalho - * @see Structures_Graph_Manipulator_AcyclicTest - * @package Structures_Graph - */ -class Structures_Graph_Manipulator_TopologicalSorter { - /* _nonVisitedInDegree {{{ */ - /** - * - * This is a variant of Structures_Graph::inDegree which does - * not count nodes marked as visited. - * - * @access private - * @return integer Number of non-visited nodes that link to this one - */ - function _nonVisitedInDegree(&$node) { - $result = 0; - $graphNodes =& $node->_graph->getNodes(); - foreach (array_keys($graphNodes) as $key) { - if ((!$graphNodes[$key]->getMetadata('topological-sort-visited')) && $graphNodes[$key]->connectsTo($node)) $result++; - } - return $result; - - } - /* }}} */ - - /* _sort {{{ */ - /** - * @access private - */ - function _sort(&$graph) { - // Mark every node as not visited - $nodes =& $graph->getNodes(); - $nodeKeys = array_keys($nodes); - $refGenerator = array(); - foreach($nodeKeys as $key) { - $refGenerator[] = false; - $nodes[$key]->setMetadata('topological-sort-visited', $refGenerator[sizeof($refGenerator) - 1]); - } - - // Iteratively peel off leaf nodes - $topologicalLevel = 0; - do { - // Find out which nodes are leafs (excluding visited nodes) - $leafNodes = array(); - foreach($nodeKeys as $key) { - if ((!$nodes[$key]->getMetadata('topological-sort-visited')) && Structures_Graph_Manipulator_TopologicalSorter::_nonVisitedInDegree($nodes[$key]) == 0) { - $leafNodes[] =& $nodes[$key]; - } - } - // Mark leafs as visited - $refGenerator[] = $topologicalLevel; - for ($i=sizeof($leafNodes) - 1; $i>=0; $i--) { - $visited =& $leafNodes[$i]->getMetadata('topological-sort-visited'); - $visited = true; - $leafNodes[$i]->setMetadata('topological-sort-visited', $visited); - $leafNodes[$i]->setMetadata('topological-sort-level', $refGenerator[sizeof($refGenerator) - 1]); - } - $topologicalLevel++; - } while (sizeof($leafNodes) > 0); - - // Cleanup visited marks - foreach($nodeKeys as $key) $nodes[$key]->unsetMetadata('topological-sort-visited'); - } - /* }}} */ - - /* sort {{{ */ - /** - * - * sort returns the graph's nodes, sorted by topological order. - * - * The result is an array with - * as many entries as topological levels. Each entry in this array is an array of nodes within - * the given topological level. - * - * @return array The graph's nodes, sorted by topological order. - * @access public - */ - function sort(&$graph) { - // We only sort graphs - if (!is_a($graph, 'Structures_Graph')) return Pear::raiseError('Structures_Graph_Manipulator_TopologicalSorter::sort received an object that is not a Structures_Graph', STRUCTURES_GRAPH_ERROR_GENERIC); - if (!Structures_Graph_Manipulator_AcyclicTest::isAcyclic($graph)) return Pear::raiseError('Structures_Graph_Manipulator_TopologicalSorter::sort received an graph that has cycles', STRUCTURES_GRAPH_ERROR_GENERIC); - - Structures_Graph_Manipulator_TopologicalSorter::_sort($graph); - $result = array(); - - // Fill out result array - $nodes =& $graph->getNodes(); - $nodeKeys = array_keys($nodes); - foreach($nodeKeys as $key) { - if (!array_key_exists($nodes[$key]->getMetadata('topological-sort-level'), $result)) $result[$nodes[$key]->getMetadata('topological-sort-level')] = array(); - $result[$nodes[$key]->getMetadata('topological-sort-level')][] =& $nodes[$key]; - $nodes[$key]->unsetMetadata('topological-sort-level'); - } - - return $result; - } - /* }}} */ -} -/* }}} */ -?> diff --git a/pear/Structures/Graph/Node.php b/pear/Structures/Graph/Node.php deleted file mode 100644 index 95afa2b..0000000 --- a/pear/Structures/Graph/Node.php +++ /dev/null @@ -1,342 +0,0 @@ - | -// +-----------------------------------------------------------------------------+ -// -/** - * This file contains the definition of the Structures_Graph_Node class - * - * @see Structures_Graph_Node - * @package Structures_Graph - */ - -/* dependencies {{{ */ -/** */ -require_once 'PEAR.php'; -/** */ -require_once 'Structures/Graph.php'; -/* }}} */ - -/* class Structures_Graph_Node {{{ */ -/** - * The Structures_Graph_Node class represents a Node that can be member of a - * graph node set. - * - * A graph node can contain data. Under this API, the node contains default data, - * and key index data. It behaves, thus, both as a regular data node, and as a - * dictionary (or associative array) node. - * - * Regular data is accessed via getData and setData. Key indexed data is accessed - * via getMetadata and setMetadata. - * - * @author Sérgio Carvalho - * @copyright (c) 2004 by Sérgio Carvalho - * @package Structures_Graph - */ -/* }}} */ -class Structures_Graph_Node { - /* fields {{{ */ - /** - * @access private - */ - var $_data = null; - /** @access private */ - var $_metadata = array(); - /** @access private */ - var $_arcs = array(); - /** @access private */ - var $_graph = null; - /* }}} */ - - /* Constructor {{{ */ - /** - * - * Constructor - * - * @access public - */ - function Structures_Graph_Node() { - } - /* }}} */ - - /* getGraph {{{ */ - /** - * - * Node graph getter - * - * @return Structures_Graph Graph where node is stored - * @access public - */ - function &getGraph() { - return $this->_graph; - } - /* }}} */ - - /* setGraph {{{ */ - /** - * - * Node graph setter. This method should not be called directly. Use Graph::addNode instead. - * - * @param Structures_Graph Set the graph for this node. - * @see Structures_Graph::addNode() - * @access public - */ - function setGraph(&$graph) { - $this->_graph =& $graph; - } - /* }}} */ - - /* getData {{{ */ - /** - * - * Node data getter. - * - * Each graph node can contain a reference to one variable. This is the getter for that reference. - * - * @return mixed Data stored in node - * @access public - */ - function &getData() { - return $this->_data; - } - /* }}} */ - - /* setData {{{ */ - /** - * - * Node data setter - * - * Each graph node can contain a reference to one variable. This is the setter for that reference. - * - * @return mixed Data to store in node - * @access public - */ - function setData($data) { - $this->_data =& $data; - } - /* }}} */ - - /* metadataKeyExists {{{ */ - /** - * - * Test for existence of metadata under a given key. - * - * Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an - * associative array or in a dictionary. This method tests whether a given metadata key exists for this node. - * - * @param string Key to test - * @return boolean - * @access public - */ - function metadataKeyExists($key) { - return array_key_exists($key, $this->_metadata); - } - /* }}} */ - - /* getMetadata {{{ */ - /** - * - * Node metadata getter - * - * Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an - * associative array or in a dictionary. This method gets the data under the given key. If the key does - * not exist, an error will be thrown, so testing using metadataKeyExists might be needed. - * - * @param string Key - * @param boolean nullIfNonexistent (defaults to false). - * @return mixed Metadata Data stored in node under given key - * @see metadataKeyExists - * @access public - */ - function &getMetadata($key, $nullIfNonexistent = false) { - if (array_key_exists($key, $this->_metadata)) { - return $this->_metadata[$key]; - } else { - if ($nullIfNonexistent) { - $a = null; - return $a; - } else { - $a = Pear::raiseError('Structures_Graph_Node::getMetadata: Requested key does not exist', STRUCTURES_GRAPH_ERROR_GENERIC); - return $a; - } - } - } - /* }}} */ - - /* unsetMetadata {{{ */ - /** - * - * Delete metadata by key - * - * Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an - * associative array or in a dictionary. This method removes any data that might be stored under the provided key. - * If the key does not exist, no error is thrown, so it is safe using this method without testing for key existence. - * - * @param string Key - * @access public - */ - function unsetMetadata($key) { - if (array_key_exists($key, $this->_metadata)) unset($this->_metadata[$key]); - } - /* }}} */ - - /* setMetadata {{{ */ - /** - * - * Node metadata setter - * - * Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an - * associative array or in a dictionary. This method stores data under the given key. If the key already exists, - * previously stored data is discarded. - * - * @param string Key - * @param mixed Data - * @access public - */ - function setMetadata($key, $data) { - $this->_metadata[$key] =& $data; - } - /* }}} */ - - /* _connectTo {{{ */ - /** @access private */ - function _connectTo(&$destinationNode) { - $this->_arcs[] =& $destinationNode; - } - /* }}} */ - - /* connectTo {{{ */ - /** - * - * Connect this node to another one. - * - * If the graph is not directed, the reverse arc, connecting $destinationNode to $this is also created. - * - * @param Structures_Graph_Node Node to connect to - * @access public - */ - function connectTo(&$destinationNode) { - // We only connect to nodes - if (!is_a($destinationNode, 'Structures_Graph_Node')) return Pear::raiseError('Structures_Graph_Node::connectTo received an object that is not a Structures_Graph_Node', STRUCTURES_GRAPH_ERROR_GENERIC); - // Nodes must already be in graphs to be connected - if ($this->_graph == null) return Pear::raiseError('Structures_Graph_Node::connectTo Tried to connect a node that is not in a graph', STRUCTURES_GRAPH_ERROR_GENERIC); - if ($destinationNode->getGraph() == null) return Pear::raiseError('Structures_Graph_Node::connectTo Tried to connect to a node that is not in a graph', STRUCTURES_GRAPH_ERROR_GENERIC); - // Connect here - $this->_connectTo($destinationNode); - // If graph is undirected, connect back - if (!$this->_graph->isDirected()) { - $destinationNode->_connectTo($this); - } - } - /* }}} */ - - /* getNeighbours {{{ */ - /** - * - * Return nodes connected to this one. - * - * @return array Array of nodes - * @access public - */ - function getNeighbours() { - return $this->_arcs; - } - /* }}} */ - - /* connectsTo {{{ */ - /** - * - * Test wether this node has an arc to the target node - * - * @return boolean True if the two nodes are connected - * @access public - */ - function connectsTo(&$target) { - if (version_compare(PHP_VERSION, '5.0.0') >= 0) { - return in_array($target, $this->getNeighbours(), true); - } - - $copy = $target; - $arcKeys = array_keys($this->_arcs); - foreach($arcKeys as $key) { - /* ZE1 chokes on this expression: - if ($target === $arc) return true; - so, we'll use more convoluted stuff - */ - $arc =& $this->_arcs[$key]; - $target = true; - if ($arc === true) { - $target = false; - if ($arc === false) { - $target = $copy; - return true; - } - } - } - $target = $copy; - return false; - } - /* }}} */ - - /* inDegree {{{ */ - /** - * - * Calculate the in degree of the node. - * - * The indegree for a node is the number of arcs entering the node. For non directed graphs, - * the indegree is equal to the outdegree. - * - * @return integer In degree of the node - * @access public - */ - function inDegree() { - if ($this->_graph == null) return 0; - if (!$this->_graph->isDirected()) return $this->outDegree(); - $result = 0; - $graphNodes =& $this->_graph->getNodes(); - foreach (array_keys($graphNodes) as $key) { - if ($graphNodes[$key]->connectsTo($this)) $result++; - } - return $result; - - } - /* }}} */ - - /* outDegree {{{ */ - /** - * - * Calculate the out degree of the node. - * - * The outdegree for a node is the number of arcs exiting the node. For non directed graphs, - * the outdegree is always equal to the indegree. - * - * @return integer Out degree of the node - * @access public - */ - function outDegree() { - if ($this->_graph == null) return 0; - return sizeof($this->_arcs); - } - /* }}} */ -} -?> diff --git a/pear/System.php b/pear/System.php deleted file mode 100644 index b419605..0000000 --- a/pear/System.php +++ /dev/null @@ -1,624 +0,0 @@ - - * @copyright 1997-2009 The Authors - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 0.1 - */ - -/** - * base class - */ -require_once 'PEAR.php'; -require_once 'Console/Getopt.php'; - -$GLOBALS['_System_temp_files'] = array(); - -/** -* System offers cross plattform compatible system functions -* -* Static functions for different operations. Should work under -* Unix and Windows. The names and usage has been taken from its respectively -* GNU commands. The functions will return (bool) false on error and will -* trigger the error with the PHP trigger_error() function (you can silence -* the error by prefixing a '@' sign after the function call, but this -* is not recommended practice. Instead use an error handler with -* {@link set_error_handler()}). -* -* Documentation on this class you can find in: -* http://pear.php.net/manual/ -* -* Example usage: -* if (!@System::rm('-r file1 dir1')) { -* print "could not delete file1 or dir1"; -* } -* -* In case you need to to pass file names with spaces, -* pass the params as an array: -* -* System::rm(array('-r', $file1, $dir1)); -* -* @category pear -* @package System -* @author Tomas V.V. Cox -* @copyright 1997-2006 The PHP Group -* @license http://opensource.org/licenses/bsd-license.php New BSD License -* @version Release: @package_version@ -* @link http://pear.php.net/package/PEAR -* @since Class available since Release 0.1 -* @static -*/ -class System -{ - /** - * returns the commandline arguments of a function - * - * @param string $argv the commandline - * @param string $short_options the allowed option short-tags - * @param string $long_options the allowed option long-tags - * @return array the given options and there values - */ - public static function _parseArgs($argv, $short_options, $long_options = null) - { - if (!is_array($argv) && $argv !== null) { - /* - // Quote all items that are a short option - $av = preg_split('/(\A| )--?[a-z0-9]+[ =]?((? $a) { - if (empty($a)) { - continue; - } - $argv[$k] = trim($a) ; - } - } - return Console_Getopt::getopt2($argv, $short_options, $long_options); - } - - /** - * Output errors with PHP trigger_error(). You can silence the errors - * with prefixing a "@" sign to the function call: @System::mkdir(..); - * - * @param mixed $error a PEAR error or a string with the error message - * @return bool false - */ - public static function raiseError($error) - { - if (PEAR::isError($error)) { - $error = $error->getMessage(); - } - trigger_error($error, E_USER_WARNING); - return false; - } - - /** - * Creates a nested array representing the structure of a directory - * - * System::_dirToStruct('dir1', 0) => - * Array - * ( - * [dirs] => Array - * ( - * [0] => dir1 - * ) - * - * [files] => Array - * ( - * [0] => dir1/file2 - * [1] => dir1/file3 - * ) - * ) - * @param string $sPath Name of the directory - * @param integer $maxinst max. deep of the lookup - * @param integer $aktinst starting deep of the lookup - * @param bool $silent if true, do not emit errors. - * @return array the structure of the dir - */ - public static function _dirToStruct($sPath, $maxinst, $aktinst = 0, $silent = false) - { - $struct = array('dirs' => array(), 'files' => array()); - if (($dir = @opendir($sPath)) === false) { - if (!$silent) { - System::raiseError("Could not open dir $sPath"); - } - return $struct; // XXX could not open error - } - - $struct['dirs'][] = $sPath = realpath($sPath); // XXX don't add if '.' or '..' ? - $list = array(); - while (false !== ($file = readdir($dir))) { - if ($file != '.' && $file != '..') { - $list[] = $file; - } - } - - closedir($dir); - natsort($list); - if ($aktinst < $maxinst || $maxinst == 0) { - foreach ($list as $val) { - $path = $sPath . DIRECTORY_SEPARATOR . $val; - if (is_dir($path) && !is_link($path)) { - $tmp = System::_dirToStruct($path, $maxinst, $aktinst+1, $silent); - $struct = array_merge_recursive($struct, $tmp); - } else { - $struct['files'][] = $path; - } - } - } - - return $struct; - } - - /** - * Creates a nested array representing the structure of a directory and files - * - * @param array $files Array listing files and dirs - * @return array - * @see System::_dirToStruct() - */ - public static function _multipleToStruct($files) - { - $struct = array('dirs' => array(), 'files' => array()); - settype($files, 'array'); - foreach ($files as $file) { - if (is_dir($file) && !is_link($file)) { - $tmp = System::_dirToStruct($file, 0); - $struct = array_merge_recursive($tmp, $struct); - } else { - if (!in_array($file, $struct['files'])) { - $struct['files'][] = $file; - } - } - } - return $struct; - } - - /** - * The rm command for removing files. - * Supports multiple files and dirs and also recursive deletes - * - * @param string $args the arguments for rm - * @return mixed PEAR_Error or true for success - */ - public static function rm($args) - { - $opts = System::_parseArgs($args, 'rf'); // "f" does nothing but I like it :-) - if (PEAR::isError($opts)) { - return System::raiseError($opts); - } - foreach ($opts[0] as $opt) { - if ($opt[0] == 'r') { - $do_recursive = true; - } - } - $ret = true; - if (isset($do_recursive)) { - $struct = System::_multipleToStruct($opts[1]); - foreach ($struct['files'] as $file) { - if (!@unlink($file)) { - $ret = false; - } - } - - rsort($struct['dirs']); - foreach ($struct['dirs'] as $dir) { - if (!@rmdir($dir)) { - $ret = false; - } - } - } else { - foreach ($opts[1] as $file) { - $delete = (is_dir($file)) ? 'rmdir' : 'unlink'; - if (!@$delete($file)) { - $ret = false; - } - } - } - return $ret; - } - - /** - * Make directories. - * - * The -p option will create parent directories - * @param string $args the name of the director(y|ies) to create - * @return bool True for success - */ - public static function mkDir($args) - { - $opts = System::_parseArgs($args, 'pm:'); - if (PEAR::isError($opts)) { - return System::raiseError($opts); - } - - $mode = 0777; // default mode - foreach ($opts[0] as $opt) { - if ($opt[0] == 'p') { - $create_parents = true; - } elseif ($opt[0] == 'm') { - // if the mode is clearly an octal number (starts with 0) - // convert it to decimal - if (strlen($opt[1]) && $opt[1]{0} == '0') { - $opt[1] = octdec($opt[1]); - } else { - // convert to int - $opt[1] += 0; - } - $mode = $opt[1]; - } - } - - $ret = true; - if (isset($create_parents)) { - foreach ($opts[1] as $dir) { - $dirstack = array(); - while ((!file_exists($dir) || !is_dir($dir)) && - $dir != DIRECTORY_SEPARATOR) { - array_unshift($dirstack, $dir); - $dir = dirname($dir); - } - - while ($newdir = array_shift($dirstack)) { - if (!is_writeable(dirname($newdir))) { - $ret = false; - break; - } - - if (!mkdir($newdir, $mode)) { - $ret = false; - } - } - } - } else { - foreach($opts[1] as $dir) { - if ((@file_exists($dir) || !is_dir($dir)) && !mkdir($dir, $mode)) { - $ret = false; - } - } - } - - return $ret; - } - - /** - * Concatenate files - * - * Usage: - * 1) $var = System::cat('sample.txt test.txt'); - * 2) System::cat('sample.txt test.txt > final.txt'); - * 3) System::cat('sample.txt test.txt >> final.txt'); - * - * Note: as the class use fopen, urls should work also (test that) - * - * @param string $args the arguments - * @return boolean true on success - */ - public static function &cat($args) - { - $ret = null; - $files = array(); - if (!is_array($args)) { - $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY); - } - - $count_args = count($args); - for ($i = 0; $i < $count_args; $i++) { - if ($args[$i] == '>') { - $mode = 'wb'; - $outputfile = $args[$i+1]; - break; - } elseif ($args[$i] == '>>') { - $mode = 'ab+'; - $outputfile = $args[$i+1]; - break; - } else { - $files[] = $args[$i]; - } - } - $outputfd = false; - if (isset($mode)) { - if (!$outputfd = fopen($outputfile, $mode)) { - $err = System::raiseError("Could not open $outputfile"); - return $err; - } - $ret = true; - } - foreach ($files as $file) { - if (!$fd = fopen($file, 'r')) { - System::raiseError("Could not open $file"); - continue; - } - while ($cont = fread($fd, 2048)) { - if (is_resource($outputfd)) { - fwrite($outputfd, $cont); - } else { - $ret .= $cont; - } - } - fclose($fd); - } - if (is_resource($outputfd)) { - fclose($outputfd); - } - return $ret; - } - - /** - * Creates temporary files or directories. This function will remove - * the created files when the scripts finish its execution. - * - * Usage: - * 1) $tempfile = System::mktemp("prefix"); - * 2) $tempdir = System::mktemp("-d prefix"); - * 3) $tempfile = System::mktemp(); - * 4) $tempfile = System::mktemp("-t /var/tmp prefix"); - * - * prefix -> The string that will be prepended to the temp name - * (defaults to "tmp"). - * -d -> A temporary dir will be created instead of a file. - * -t -> The target dir where the temporary (file|dir) will be created. If - * this param is missing by default the env vars TMP on Windows or - * TMPDIR in Unix will be used. If these vars are also missing - * c:\windows\temp or /tmp will be used. - * - * @param string $args The arguments - * @return mixed the full path of the created (file|dir) or false - * @see System::tmpdir() - */ - public static function mktemp($args = null) - { - static $first_time = true; - $opts = System::_parseArgs($args, 't:d'); - if (PEAR::isError($opts)) { - return System::raiseError($opts); - } - - foreach ($opts[0] as $opt) { - if ($opt[0] == 'd') { - $tmp_is_dir = true; - } elseif ($opt[0] == 't') { - $tmpdir = $opt[1]; - } - } - - $prefix = (isset($opts[1][0])) ? $opts[1][0] : 'tmp'; - if (!isset($tmpdir)) { - $tmpdir = System::tmpdir(); - } - - if (!System::mkDir(array('-p', $tmpdir))) { - return false; - } - - $tmp = tempnam($tmpdir, $prefix); - if (isset($tmp_is_dir)) { - unlink($tmp); // be careful possible race condition here - if (!mkdir($tmp, 0700)) { - return System::raiseError("Unable to create temporary directory $tmpdir"); - } - } - - $GLOBALS['_System_temp_files'][] = $tmp; - if (isset($tmp_is_dir)) { - //$GLOBALS['_System_temp_files'][] = dirname($tmp); - } - - if ($first_time) { - PEAR::registerShutdownFunc(array('System', '_removeTmpFiles')); - $first_time = false; - } - - return $tmp; - } - - /** - * Remove temporary files created my mkTemp. This function is executed - * at script shutdown time - */ - public static function _removeTmpFiles() - { - if (count($GLOBALS['_System_temp_files'])) { - $delete = $GLOBALS['_System_temp_files']; - array_unshift($delete, '-r'); - System::rm($delete); - $GLOBALS['_System_temp_files'] = array(); - } - } - - /** - * Get the path of the temporal directory set in the system - * by looking in its environments variables. - * Note: php.ini-recommended removes the "E" from the variables_order setting, - * making unavaible the $_ENV array, that s why we do tests with _ENV - * - * @return string The temporary directory on the system - */ - public static function tmpdir() - { - if (OS_WINDOWS) { - if ($var = isset($_ENV['TMP']) ? $_ENV['TMP'] : getenv('TMP')) { - return $var; - } - if ($var = isset($_ENV['TEMP']) ? $_ENV['TEMP'] : getenv('TEMP')) { - return $var; - } - if ($var = isset($_ENV['USERPROFILE']) ? $_ENV['USERPROFILE'] : getenv('USERPROFILE')) { - return $var; - } - if ($var = isset($_ENV['windir']) ? $_ENV['windir'] : getenv('windir')) { - return $var; - } - return getenv('SystemRoot') . '\temp'; - } - if ($var = isset($_ENV['TMPDIR']) ? $_ENV['TMPDIR'] : getenv('TMPDIR')) { - return $var; - } - return realpath('/tmp'); - } - - /** - * The "which" command (show the full path of a command) - * - * @param string $program The command to search for - * @param mixed $fallback Value to return if $program is not found - * - * @return mixed A string with the full path or false if not found - * @author Stig Bakken - */ - public static function which($program, $fallback = false) - { - // enforce API - if (!is_string($program) || '' == $program) { - return $fallback; - } - - // full path given - if (basename($program) != $program) { - $path_elements[] = dirname($program); - $program = basename($program); - } else { - // Honor safe mode - if (!ini_get('safe_mode') || !$path = ini_get('safe_mode_exec_dir')) { - $path = getenv('PATH'); - if (!$path) { - $path = getenv('Path'); // some OSes are just stupid enough to do this - } - } - $path_elements = explode(PATH_SEPARATOR, $path); - } - - if (OS_WINDOWS) { - $exe_suffixes = getenv('PATHEXT') - ? explode(PATH_SEPARATOR, getenv('PATHEXT')) - : array('.exe','.bat','.cmd','.com'); - // allow passing a command.exe param - if (strpos($program, '.') !== false) { - array_unshift($exe_suffixes, ''); - } - // is_executable() is not available on windows for PHP4 - $pear_is_executable = (function_exists('is_executable')) ? 'is_executable' : 'is_file'; - } else { - $exe_suffixes = array(''); - $pear_is_executable = 'is_executable'; - } - - foreach ($exe_suffixes as $suff) { - foreach ($path_elements as $dir) { - $file = $dir . DIRECTORY_SEPARATOR . $program . $suff; - if (@$pear_is_executable($file)) { - return $file; - } - } - } - return $fallback; - } - - /** - * The "find" command - * - * Usage: - * - * System::find($dir); - * System::find("$dir -type d"); - * System::find("$dir -type f"); - * System::find("$dir -name *.php"); - * System::find("$dir -name *.php -name *.htm*"); - * System::find("$dir -maxdepth 1"); - * - * Params implmented: - * $dir -> Start the search at this directory - * -type d -> return only directories - * -type f -> return only files - * -maxdepth -> max depth of recursion - * -name -> search pattern (bash style). Multiple -name param allowed - * - * @param mixed Either array or string with the command line - * @return array Array of found files - */ - public static function find($args) - { - if (!is_array($args)) { - $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY); - } - $dir = realpath(array_shift($args)); - if (!$dir) { - return array(); - } - $patterns = array(); - $depth = 0; - $do_files = $do_dirs = true; - $args_count = count($args); - for ($i = 0; $i < $args_count; $i++) { - switch ($args[$i]) { - case '-type': - if (in_array($args[$i+1], array('d', 'f'))) { - if ($args[$i+1] == 'd') { - $do_files = false; - } else { - $do_dirs = false; - } - } - $i++; - break; - case '-name': - $name = preg_quote($args[$i+1], '#'); - // our magic characters ? and * have just been escaped, - // so now we change the escaped versions to PCRE operators - $name = strtr($name, array('\?' => '.', '\*' => '.*')); - $patterns[] = '('.$name.')'; - $i++; - break; - case '-maxdepth': - $depth = $args[$i+1]; - break; - } - } - $path = System::_dirToStruct($dir, $depth, 0, true); - if ($do_files && $do_dirs) { - $files = array_merge($path['files'], $path['dirs']); - } elseif ($do_dirs) { - $files = $path['dirs']; - } else { - $files = $path['files']; - } - if (count($patterns)) { - $dsq = preg_quote(DIRECTORY_SEPARATOR, '#'); - $pattern = '#(^|'.$dsq.')'.implode('|', $patterns).'($|'.$dsq.')#'; - $ret = array(); - $files_count = count($files); - for ($i = 0; $i < $files_count; $i++) { - // only search in the part of the file below the current directory - $filepart = basename($files[$i]); - if (preg_match($pattern, $filepart)) { - $ret[] = $files[$i]; - } - } - return $ret; - } - return $files; - } -} diff --git a/pear/System/Command.php b/pear/System/Command.php deleted file mode 100644 index f32e8a1..0000000 --- a/pear/System/Command.php +++ /dev/null @@ -1,598 +0,0 @@ - | -// | Author: Dan Allen -// +----------------------------------------------------------------------+ - -// $Id$ - -// }}} -// {{{ includes - -require_once 'PEAR.php'; -require_once 'System.php'; - -// }}} -// {{{ constants - -define('SYSTEM_COMMAND_OK', 1); -define('SYSTEM_COMMAND_ERROR', -1); -define('SYSTEM_COMMAND_NO_SHELL', -2); -define('SYSTEM_COMMAND_INVALID_SHELL', -3); -define('SYSTEM_COMMAND_TMPDIR_ERROR', -4); -define('SYSTEM_COMMAND_INVALID_OPERATOR', -5); -define('SYSTEM_COMMAND_INVALID_COMMAND', -6); -define('SYSTEM_COMMAND_OPERATOR_PLACEMENT',-7); -define('SYSTEM_COMMAND_COMMAND_PLACEMENT', -8); -define('SYSTEM_COMMAND_NOHUP_MISSING', -9); -define('SYSTEM_COMMAND_NO_OUTPUT', -10); -define('SYSTEM_COMMAND_STDERR', -11); -define('SYSTEM_COMMAND_NONZERO_EXIT', -12); - -// }}} - -// {{{ class System_Command - -/** - * The System_Command:: class implements an abstraction for various ways - * of executing commands (directly using the backtick operator, - * as a background task after the script has terminated using - * register_shutdown_function() or as a detached process using nohup). - * - * @author Anders Johannsen - * @author Dan Allen - * @version $Revision$ - */ - -// }}} -class System_Command { - // {{{ properties - - /** - * Array of settings used when creating the shell command - * - * @var array - * @access private - */ - var $options = array(); - - /** - * Array of available shells to use to execute the command - * - * @var array - * @access private - */ - var $shells = array(); - - /** - * Array of available control operators used between commands - * - * @var array - * @access private - */ - var $controlOperators = array(); - - /** - * The system command to be executed - * - * @var string - * @access private - */ - var $systemCommand = null; - - /** - * Previously added part to the command string - * - * @var string - * @access private - */ - var $previousElement = null; - - /** - * Directory for writing stderr output - * - * @var string - * @access private - */ - var $tmpDir = null; - - /** - * To allow the pear error object to accumulate when building - * the command, we use the command status to keep track when - * a pear error is raised - * - * @var int - * @access private - */ - var $commandStatus = 0; - - /** - * Hold initialization PEAR_Error - * - * @var object - * @access private - **/ - var $_initError = null; - - // }}} - // {{{ constructor - - /** - * Class constructor - * - * Defines all necessary constants and sets defaults - * - * @access public - */ - function System_Command($in_shell = null) - { - // Defining constants - $this->options = array( - 'SEQUENCE' => true, - 'SHUTDOWN' => false, - 'SHELL' => $this->which($in_shell), - 'OUTPUT' => true, - 'NOHUP' => false, - 'BACKGROUND' => false, - 'STDERR' => false, - 'AUTORESET' => false - ); - - // prepare the available control operators - $this->controlOperators = array( - 'PIPE' => '|', - 'AND' => '&&', - 'OR' => '||', - 'GROUP' => ';', - 'LFIFO' => '<', - 'RFIFO' => '>', - ); - - // List of allowed/available shells - $this->shells = array( - 'sh', - 'bash', - 'zsh', - 'tcsh', - 'csh', - 'ash', - 'sash', - 'esh', - 'ksh' - ); - - // Find the first available shell - if (empty($this->options['SHELL'])) { - foreach ($this->shells as $shell) { - if ($this->options['SHELL'] = $this->which($shell)) { - break; - } - } - - // see if we still have no shell - if (empty($this->options['SHELL'])) { - $this->_initError =& PEAR::raiseError(null, SYSTEM_COMMAND_NO_SHELL, null, E_USER_WARNING, null, 'System_Command_Error', true); - return; - } - } - - // Caputre a temporary directory for capturing stderr from commands - $this->tmpDir = System::tmpdir(); - if (!System::mkDir("-p {$this->tmpDir}")) { - $this->_initError =& PEAR::raiseError(null, SYSTEM_COMMAND_TMPDIR_ERROR, null, E_USER_WARNING, null, 'System_Command_Error', true); - return; - } - } - - // }}} - // {{{ setOption() - - /** - * Sets the value for an option. Each option should be set to true - * or false; except the 'SHELL' option which should be a string - * naming a shell. The options are: - * - * 'SEQUENCE' Allow a sequence command or not (right now this is always on); - * - * 'SHUTDOWN' Execute commands via a shutdown function; - * - * 'SHELL' Path to shell; - * - * 'OUTPUT' Output stdout from process; - * - * 'NOHUP' Use nohup to detach process; - * - * 'BACKGROUND' Run as a background process with &; - * - * 'STDERR' Output on stderr will raise an error, even if - * the command's exit value is zero. The output from - * stderr can be retrieved using the getDebugInfo() - * method of the Pear_ERROR object returned by - * execute().; - * - * 'AUTORESET' Automatically call reset() after a successful - * call of execute(); - * - * @param string $in_option is a case-sensitive string, - * corresponding to the option - * that should be changed - * @param mixed $in_setting is the new value for the option - * @access public - * @return bool true if succes, else false - */ - function setOption($in_option, $in_setting) - { - if ($this->_initError) { - return $this->_initError; - } - - $option = strtoupper($in_option); - - if (!isset($this->options[$option])) { - PEAR::raiseError(null, SYSTEM_COMMAND_ERROR, null, E_USER_NOTICE, null, 'System_Command_Error', true); - return false; - } - - switch ($option) { - case 'OUTPUT': - case 'SHUTDOWN': - case 'SEQUENCE': - case 'BACKGROUND': - case 'STDERR': - $this->options[$option] = !empty($in_setting); - return true; - break; - - case 'SHELL': - if (($shell = $this->which($in_setting)) !== false) { - $this->options[$option] = $shell; - return true; - } - else { - PEAR::raiseError(null, SYSTEM_COMMAND_NO_SHELL, null, E_USER_NOTICE, $in_setting, 'System_Command_Error', true); - return false; - } - break; - - case 'NOHUP': - if (empty($in_setting)) { - $this->options[$option] = false; - } - else if ($location = $this->which('nohup')) { - $this->options[$option] = $location; - } - else { - PEAR::raiseError(null, SYSTEM_COMMAND_NOHUP_MISSING, null, E_USER_NOTICE, null, 'System_Command_Error', true); - return false; - } - break; - } - } - - // }}} - // {{{ pushCommand() - - /** - * Used to push a command onto the running command to be executed - * - * @param string $in_command binary to be run - * @param string $in_argument either an option or argument value, to be handled appropriately - * @param string $in_argument - * @param ... - * - * @access public - * @return boolean true on success {or System_Command_Error Exception} - */ - function pushCommand($in_command) - { - if ($this->_initError) { - return $this->_initError; - } - - if (!is_null($this->previousElement) && !in_array($this->previousElement, $this->controlOperators)) { - $this->commandStatus = -1; - $error = PEAR::raiseError(null, SYSTEM_COMMAND_COMMAND_PLACEMENT, null, E_USER_WARNING, null, 'System_Command_Error', true); - } - - // check for error here - $command = escapeshellcmd($this->which($in_command)); - if ($command === false) { - $error = PEAR::raiseError(null, SYSTEM_COMMAND_INVALID_COMMAND, null, E_USER_WARNING, null, 'System_Command_Error', true); - } - - $argv = func_get_args(); - array_shift($argv); - foreach($argv as $arg) { - if (strpos($arg, '-') === 0) { - $command .= ' ' . $arg; - } - elseif ($arg != '') { - $command .= ' ' . escapeshellarg($arg); - } - } - - $this->previousElement = $command; - $this->systemCommand .= $command; - - return isset($error) ? $error : true; - } - - // }}} - // {{{ pushOperator() - - /** - * Used to push an operator onto the running command to be executed - * - * @param string $in_operator Either string reprentation of operator or system character - * - * @access public - * @return boolean true on success {or System_Command_Error Exception} - */ - function pushOperator($in_operator) - { - if ($this->_initError) { - return $this->_initError; - } - - $operator = isset($this->controlOperators[$in_operator]) ? $this->controlOperators[$in_operator] : $in_operator; - - if (is_null($this->previousElement) || in_array($this->previousElement, $this->controlOperators)) { - $this->commandStatus = -1; - $error = PEAR::raiseError(null, SYSTEM_COMMAND_OPERATOR_PLACEMENT, null, E_USER_WARNING, null, 'System_Command_Error', true); - } - elseif (!in_array($operator, $this->controlOperators)) { - $this->commandStatus = -1; - $error = PEAR::raiseError(null, SYSTEM_COMMAND_INVALID_OPERATOR, null, E_USER_WARNING, $operator, 'System_Command_Error', true); - } - - $this->previousElement = $operator; - $this->systemCommand .= ' ' . $operator . ' '; - return isset($error) ? $error : true; - } - - // }}} - // {{{ execute() - - /** - * Executes the code according to given options - * - * @return bool true if success {or System_Command_Exception} - * - * @access public - */ - function execute() - { - if ($this->_initError) { - return $this->_initError; - } - - // if the command is empty or if the last element was a control operator, we can't continue - if (is_null($this->previousElement) || $this->commandStatus == -1 || in_array($this->previousElement, $this->controlOperators)) { - return PEAR::raiseError(null, SYSTEM_COMMAND_INVALID_COMMAND, null, E_USER_WARNING, $this->systemCommand, 'System_Command_Error', true); - } - - // Warning about impossible mix of options - if (!empty($this->options['OUTPUT'])) { - if (!empty($this->options['SHUTDOWN']) || !empty($this->options['NOHUP'])) { - return PEAR::raiseError(null, SYSTEM_COMMAND_NO_OUTPUT, null, E_USER_WARNING, null, 'System_Command_Error', true); - } - } - - // if this is not going to stdout, then redirect to /dev/null - if (empty($this->options['OUTPUT'])) { - $this->systemCommand .= ' >/dev/null'; - } - - $suffix = ''; - // run a command immune to hangups, with output to a non-tty - if (!empty($this->options['NOHUP'])) { - $this->systemCommand = $this->options['NOHUP'] . $this->systemCommand; - } - // run a background process (only if not nohup) - elseif (!empty($this->options['BACKGROUND'])) { - $suffix = ' &'; - } - - // Register to be run on shutdown - if (!empty($this->options['SHUTDOWN'])) { - $line = "system(\"{$this->systemCommand}$suffix\");"; - $function = create_function('', $line); - register_shutdown_function($function); - if ($this->options['AUTORESET']) { - $this->reset(); - } - return true; - } - else { - // send stderr to a file so that we can reap the error message - $tmpFile = tempnam($this->tmpDir, 'System_Command-'); - $this->systemCommand .= ' 2>' . $tmpFile . $suffix; - $shellPipe = $this->which('echo') . ' ' . escapeshellarg($this->systemCommand) . ' | ' . $this->options['SHELL']; - exec($shellPipe, $result, $returnVal); - - if ($returnVal !== 0) { - // command returned nonzero; that's always an error - $return = PEAR::raiseError(null, SYSTEM_COMMAND_NONZERO_EXIT, null, E_USER_WARNING, null, 'System_Command_Error', true); - } - else if (!$this->options['STDERR']) { - // caller does not care about stderr; return success - $return = implode("\n", $result); - } - else { - // our caller cares about stderr; check stderr output - clearstatcache(); - if (filesize($tmpFile) > 0) { - // the command actually wrote to stderr - $stderr_output = file_get_contents($tmpFile); - $return = PEAR::raiseError(null, SYSTEM_COMMAND_STDERR, null, E_USER_WARNING, $stderr_output, 'System_Command_Error', true); - } else { - // total success; return stdout gathered by exec() - $return = implode("\n", $result); - } - } - - if ((!PEAR::isError($return)) && ($this->options['AUTORESET'])) { - $this->reset(); - } - - unlink($tmpFile); - return $return; - } - } - - // }}} - // {{{ which() - - /** - * Functionality similiar to unix 'which'. Searches the path - * for the specified program. - * - * @param $cmd name of the executable to search for - * - * @access private - * @return string returns the full path if found, false if not - */ - function which($in_cmd) - { - // only pass non-empty strings to System::which() - if (!is_string($in_cmd) || '' === $in_cmd) { - return(false); - } - - // explicitly pass false as fallback value - return System::which($in_cmd, false); - } - - // }}} - // {{{ reset() - - /** - * Prepare for a new command to be built - * - * @access public - * @return void - */ - function reset() - { - $this->previousElement = null; - $this->systemCommand = null; - $this->commandStatus = 0; - } - - // }}} - // {{{ errorMessage() - - /** - * Return a textual error message for a System_Command error code - * - * @param integer error code - * - * @return string error message, or false if the error code was - * not recognized - */ - function errorMessage($in_value) - { - static $errorMessages; - if (!isset($errorMessages)) { - $errorMessages = array( - SYSTEM_COMMAND_OK => 'no error', - SYSTEM_COMMAND_ERROR => 'unknown error', - SYSTEM_COMMAND_NO_SHELL => 'no shell found', - SYSTEM_COMMAND_INVALID_SHELL => 'invalid shell', - SYSTEM_COMMAND_TMPDIR_ERROR => 'could not create temporary directory', - SYSTEM_COMMAND_INVALID_OPERATOR => 'control operator invalid', - SYSTEM_COMMAND_INVALID_COMMAND => 'invalid system command', - SYSTEM_COMMAND_OPERATOR_PLACEMENT => 'invalid placement of control operator', - SYSTEM_COMMAND_COMMAND_PLACEMENT => 'invalid placement of command', - SYSTEM_COMMAND_NOHUP_MISSING => 'nohup not found on system', - SYSTEM_COMMAND_NO_OUTPUT => 'output not allowed', - SYSTEM_COMMAND_STDERR => 'command wrote to stderr', - SYSTEM_COMMAND_NONZERO_EXIT => 'non-zero exit value from command', - ); - } - - if (System_Command::isError($in_value)) { - $in_value = $in_value->getCode(); - } - - return isset($errorMessages[$in_value]) ? $errorMessages[$in_value] : $errorMessages[SYSTEM_COMMAND_ERROR]; - } - - // }}} - // {{{ isError() - - /** - * Tell whether a result code from a System_Command method is an error - * - * @param int result code - * - * @return bool whether $in_value is an error - * - * @access public - */ - function isError($in_value) - { - return (is_object($in_value) && - (strtolower(get_class($in_value)) == 'system_command_error' || - is_subclass_of($in_value, 'system_command_error'))); - } - - // }}} -} - -// {{{ class System_Command_Error - -/** - * System_Command_Error constructor. - * - * @param mixed System_Command error code, or string with error message. - * @param integer what "error mode" to operate in - * @param integer what error level to use for $mode & PEAR_ERROR_TRIGGER - * @param mixed additional debug info, such as the last query - * - * @access public - * - * @see PEAR_Error - */ - -// }}} -class System_Command_Error extends PEAR_Error -{ - // {{{ properties - - /** - * Message in front of the error message - * @var string $error_message_prefix - */ - var $error_message_prefix = 'System_Command Error: '; - - // }}} - // {{{ constructor - - function System_Command_Error($code = SYSTEM_COMMAND_ERROR, $mode = PEAR_ERROR_RETURN, - $level = E_USER_NOTICE, $debuginfo = null) - { - if (is_int($code)) { - $this->PEAR_Error(System_Command::errorMessage($code), $code, $mode, $level, $debuginfo); - } else { - $this->PEAR_Error("Invalid error code: $code", SYSTEM_COMMAND_ERROR, $mode, $level, $debuginfo); - } - } - - // }}} -} -?> diff --git a/pear/Text/Diff.php b/pear/Text/Diff.php deleted file mode 100644 index 4b3de1b..0000000 --- a/pear/Text/Diff.php +++ /dev/null @@ -1,453 +0,0 @@ -, and is used/adapted with his permission. - * - * $Horde: framework/Text_Diff/Diff.php,v 1.11.2.12 2009/01/06 15:23:41 jan Exp $ - * - * Copyright 2004 Geoffrey T. Dairiki - * Copyright 2004-2009 The Horde Project (http://www.horde.org/) - * - * See the enclosed file COPYING for license information (LGPL). If you did - * not receive this file, see http://opensource.org/licenses/lgpl-license.php. - * - * @package Text_Diff - * @author Geoffrey T. Dairiki - */ -class Text_Diff { - - /** - * Array of changes. - * - * @var array - */ - var $_edits; - - /** - * Computes diffs between sequences of strings. - * - * @param string $engine Name of the diffing engine to use. 'auto' - * will automatically select the best. - * @param array $params Parameters to pass to the diffing engine. - * Normally an array of two arrays, each - * containing the lines from a file. - */ - function Text_Diff($engine, $params) - { - // Backward compatibility workaround. - if (!is_string($engine)) { - $params = array($engine, $params); - $engine = 'auto'; - } - - if ($engine == 'auto') { - $engine = extension_loaded('xdiff') ? 'xdiff' : 'native'; - } else { - $engine = basename($engine); - } - - require_once 'Text/Diff/Engine/' . $engine . '.php'; - $class = 'Text_Diff_Engine_' . $engine; - $diff_engine = new $class(); - - $this->_edits = call_user_func_array(array($diff_engine, 'diff'), $params); - } - - /** - * Returns the array of differences. - */ - function getDiff() - { - return $this->_edits; - } - - /** - * returns the number of new (added) lines in a given diff. - * - * @since Text_Diff 1.1.0 - * @since Horde 3.2 - * - * @return integer The number of new lines - */ - function countAddedLines() - { - $count = 0; - foreach ($this->_edits as $edit) { - if (is_a($edit, 'Text_Diff_Op_add') || - is_a($edit, 'Text_Diff_Op_change')) { - $count += $edit->nfinal(); - } - } - return $count; - } - - /** - * Returns the number of deleted (removed) lines in a given diff. - * - * @since Text_Diff 1.1.0 - * @since Horde 3.2 - * - * @return integer The number of deleted lines - */ - function countDeletedLines() - { - $count = 0; - foreach ($this->_edits as $edit) { - if (is_a($edit, 'Text_Diff_Op_delete') || - is_a($edit, 'Text_Diff_Op_change')) { - $count += $edit->norig(); - } - } - return $count; - } - - /** - * Computes a reversed diff. - * - * Example: - * - * $diff = new Text_Diff($lines1, $lines2); - * $rev = $diff->reverse(); - * - * - * @return Text_Diff A Diff object representing the inverse of the - * original diff. Note that we purposely don't return a - * reference here, since this essentially is a clone() - * method. - */ - function reverse() - { - if (version_compare(zend_version(), '2', '>')) { - $rev = clone($this); - } else { - $rev = $this; - } - $rev->_edits = array(); - foreach ($this->_edits as $edit) { - $rev->_edits[] = $edit->reverse(); - } - return $rev; - } - - /** - * Checks for an empty diff. - * - * @return boolean True if two sequences were identical. - */ - function isEmpty() - { - foreach ($this->_edits as $edit) { - if (!is_a($edit, 'Text_Diff_Op_copy')) { - return false; - } - } - return true; - } - - /** - * Computes the length of the Longest Common Subsequence (LCS). - * - * This is mostly for diagnostic purposes. - * - * @return integer The length of the LCS. - */ - function lcs() - { - $lcs = 0; - foreach ($this->_edits as $edit) { - if (is_a($edit, 'Text_Diff_Op_copy')) { - $lcs += count($edit->orig); - } - } - return $lcs; - } - - /** - * Gets the original set of lines. - * - * This reconstructs the $from_lines parameter passed to the constructor. - * - * @return array The original sequence of strings. - */ - function getOriginal() - { - $lines = array(); - foreach ($this->_edits as $edit) { - if ($edit->orig) { - array_splice($lines, count($lines), 0, $edit->orig); - } - } - return $lines; - } - - /** - * Gets the final set of lines. - * - * This reconstructs the $to_lines parameter passed to the constructor. - * - * @return array The sequence of strings. - */ - function getFinal() - { - $lines = array(); - foreach ($this->_edits as $edit) { - if ($edit->final) { - array_splice($lines, count($lines), 0, $edit->final); - } - } - return $lines; - } - - /** - * Removes trailing newlines from a line of text. This is meant to be used - * with array_walk(). - * - * @param string $line The line to trim. - * @param integer $key The index of the line in the array. Not used. - */ - function trimNewlines(&$line, $key) - { - $line = str_replace(array("\n", "\r"), '', $line); - } - - /** - * Determines the location of the system temporary directory. - * - * @static - * - * @access protected - * - * @return string A directory name which can be used for temp files. - * Returns false if one could not be found. - */ - function _getTempDir() - { - $tmp_locations = array('/tmp', '/var/tmp', 'c:\WUTemp', 'c:\temp', - 'c:\windows\temp', 'c:\winnt\temp'); - - /* Try PHP's upload_tmp_dir directive. */ - $tmp = ini_get('upload_tmp_dir'); - - /* Otherwise, try to determine the TMPDIR environment variable. */ - if (!strlen($tmp)) { - $tmp = getenv('TMPDIR'); - } - - /* If we still cannot determine a value, then cycle through a list of - * preset possibilities. */ - while (!strlen($tmp) && count($tmp_locations)) { - $tmp_check = array_shift($tmp_locations); - if (@is_dir($tmp_check)) { - $tmp = $tmp_check; - } - } - - /* If it is still empty, we have failed, so return false; otherwise - * return the directory determined. */ - return strlen($tmp) ? $tmp : false; - } - - /** - * Checks a diff for validity. - * - * This is here only for debugging purposes. - */ - function _check($from_lines, $to_lines) - { - if (serialize($from_lines) != serialize($this->getOriginal())) { - trigger_error("Reconstructed original doesn't match", E_USER_ERROR); - } - if (serialize($to_lines) != serialize($this->getFinal())) { - trigger_error("Reconstructed final doesn't match", E_USER_ERROR); - } - - $rev = $this->reverse(); - if (serialize($to_lines) != serialize($rev->getOriginal())) { - trigger_error("Reversed original doesn't match", E_USER_ERROR); - } - if (serialize($from_lines) != serialize($rev->getFinal())) { - trigger_error("Reversed final doesn't match", E_USER_ERROR); - } - - $prevtype = null; - foreach ($this->_edits as $edit) { - if ($prevtype == get_class($edit)) { - trigger_error("Edit sequence is non-optimal", E_USER_ERROR); - } - $prevtype = get_class($edit); - } - - return true; - } - -} - -/** - * @package Text_Diff - * @author Geoffrey T. Dairiki - */ -class Text_MappedDiff extends Text_Diff { - - /** - * Computes a diff between sequences of strings. - * - * This can be used to compute things like case-insensitve diffs, or diffs - * which ignore changes in white-space. - * - * @param array $from_lines An array of strings. - * @param array $to_lines An array of strings. - * @param array $mapped_from_lines This array should have the same size - * number of elements as $from_lines. The - * elements in $mapped_from_lines and - * $mapped_to_lines are what is actually - * compared when computing the diff. - * @param array $mapped_to_lines This array should have the same number - * of elements as $to_lines. - */ - function Text_MappedDiff($from_lines, $to_lines, - $mapped_from_lines, $mapped_to_lines) - { - assert(count($from_lines) == count($mapped_from_lines)); - assert(count($to_lines) == count($mapped_to_lines)); - - parent::Text_Diff($mapped_from_lines, $mapped_to_lines); - - $xi = $yi = 0; - for ($i = 0; $i < count($this->_edits); $i++) { - $orig = &$this->_edits[$i]->orig; - if (is_array($orig)) { - $orig = array_slice($from_lines, $xi, count($orig)); - $xi += count($orig); - } - - $final = &$this->_edits[$i]->final; - if (is_array($final)) { - $final = array_slice($to_lines, $yi, count($final)); - $yi += count($final); - } - } - } - -} - -/** - * @package Text_Diff - * @author Geoffrey T. Dairiki - * - * @access private - */ -class Text_Diff_Op { - - var $orig; - var $final; - - function &reverse() - { - trigger_error('Abstract method', E_USER_ERROR); - } - - function norig() - { - return $this->orig ? count($this->orig) : 0; - } - - function nfinal() - { - return $this->final ? count($this->final) : 0; - } - -} - -/** - * @package Text_Diff - * @author Geoffrey T. Dairiki - * - * @access private - */ -class Text_Diff_Op_copy extends Text_Diff_Op { - - function Text_Diff_Op_copy($orig, $final = false) - { - if (!is_array($final)) { - $final = $orig; - } - $this->orig = $orig; - $this->final = $final; - } - - function &reverse() - { - $reverse = &new Text_Diff_Op_copy($this->final, $this->orig); - return $reverse; - } - -} - -/** - * @package Text_Diff - * @author Geoffrey T. Dairiki - * - * @access private - */ -class Text_Diff_Op_delete extends Text_Diff_Op { - - function Text_Diff_Op_delete($lines) - { - $this->orig = $lines; - $this->final = false; - } - - function &reverse() - { - $reverse = &new Text_Diff_Op_add($this->orig); - return $reverse; - } - -} - -/** - * @package Text_Diff - * @author Geoffrey T. Dairiki - * - * @access private - */ -class Text_Diff_Op_add extends Text_Diff_Op { - - function Text_Diff_Op_add($lines) - { - $this->final = $lines; - $this->orig = false; - } - - function &reverse() - { - $reverse = &new Text_Diff_Op_delete($this->final); - return $reverse; - } - -} - -/** - * @package Text_Diff - * @author Geoffrey T. Dairiki - * - * @access private - */ -class Text_Diff_Op_change extends Text_Diff_Op { - - function Text_Diff_Op_change($orig, $final) - { - $this->orig = $orig; - $this->final = $final; - } - - function &reverse() - { - $reverse = &new Text_Diff_Op_change($this->final, $this->orig); - return $reverse; - } - -} diff --git a/pear/Text/Diff/Engine/native.php b/pear/Text/Diff/Engine/native.php deleted file mode 100644 index 84168c0..0000000 --- a/pear/Text/Diff/Engine/native.php +++ /dev/null @@ -1,438 +0,0 @@ - 2, and some optimizations) are from - * Geoffrey T. Dairiki . The original PHP version of this - * code was written by him, and is used/adapted with his permission. - * - * $Horde: framework/Text_Diff/Diff/Engine/native.php,v 1.7.2.5 2009/01/06 15:23:41 jan Exp $ - * - * Copyright 2004-2009 The Horde Project (http://www.horde.org/) - * - * See the enclosed file COPYING for license information (LGPL). If you did - * not receive this file, see http://opensource.org/licenses/lgpl-license.php. - * - * @author Geoffrey T. Dairiki - * @package Text_Diff - */ -class Text_Diff_Engine_native { - - function diff($from_lines, $to_lines) - { - array_walk($from_lines, array('Text_Diff', 'trimNewlines')); - array_walk($to_lines, array('Text_Diff', 'trimNewlines')); - - $n_from = count($from_lines); - $n_to = count($to_lines); - - $this->xchanged = $this->ychanged = array(); - $this->xv = $this->yv = array(); - $this->xind = $this->yind = array(); - unset($this->seq); - unset($this->in_seq); - unset($this->lcs); - - // Skip leading common lines. - for ($skip = 0; $skip < $n_from && $skip < $n_to; $skip++) { - if ($from_lines[$skip] !== $to_lines[$skip]) { - break; - } - $this->xchanged[$skip] = $this->ychanged[$skip] = false; - } - - // Skip trailing common lines. - $xi = $n_from; $yi = $n_to; - for ($endskip = 0; --$xi > $skip && --$yi > $skip; $endskip++) { - if ($from_lines[$xi] !== $to_lines[$yi]) { - break; - } - $this->xchanged[$xi] = $this->ychanged[$yi] = false; - } - - // Ignore lines which do not exist in both files. - for ($xi = $skip; $xi < $n_from - $endskip; $xi++) { - $xhash[$from_lines[$xi]] = 1; - } - for ($yi = $skip; $yi < $n_to - $endskip; $yi++) { - $line = $to_lines[$yi]; - if (($this->ychanged[$yi] = empty($xhash[$line]))) { - continue; - } - $yhash[$line] = 1; - $this->yv[] = $line; - $this->yind[] = $yi; - } - for ($xi = $skip; $xi < $n_from - $endskip; $xi++) { - $line = $from_lines[$xi]; - if (($this->xchanged[$xi] = empty($yhash[$line]))) { - continue; - } - $this->xv[] = $line; - $this->xind[] = $xi; - } - - // Find the LCS. - $this->_compareseq(0, count($this->xv), 0, count($this->yv)); - - // Merge edits when possible. - $this->_shiftBoundaries($from_lines, $this->xchanged, $this->ychanged); - $this->_shiftBoundaries($to_lines, $this->ychanged, $this->xchanged); - - // Compute the edit operations. - $edits = array(); - $xi = $yi = 0; - while ($xi < $n_from || $yi < $n_to) { - assert($yi < $n_to || $this->xchanged[$xi]); - assert($xi < $n_from || $this->ychanged[$yi]); - - // Skip matching "snake". - $copy = array(); - while ($xi < $n_from && $yi < $n_to - && !$this->xchanged[$xi] && !$this->ychanged[$yi]) { - $copy[] = $from_lines[$xi++]; - ++$yi; - } - if ($copy) { - $edits[] = &new Text_Diff_Op_copy($copy); - } - - // Find deletes & adds. - $delete = array(); - while ($xi < $n_from && $this->xchanged[$xi]) { - $delete[] = $from_lines[$xi++]; - } - - $add = array(); - while ($yi < $n_to && $this->ychanged[$yi]) { - $add[] = $to_lines[$yi++]; - } - - if ($delete && $add) { - $edits[] = &new Text_Diff_Op_change($delete, $add); - } elseif ($delete) { - $edits[] = &new Text_Diff_Op_delete($delete); - } elseif ($add) { - $edits[] = &new Text_Diff_Op_add($add); - } - } - - return $edits; - } - - /** - * Divides the Largest Common Subsequence (LCS) of the sequences (XOFF, - * XLIM) and (YOFF, YLIM) into NCHUNKS approximately equally sized - * segments. - * - * Returns (LCS, PTS). LCS is the length of the LCS. PTS is an array of - * NCHUNKS+1 (X, Y) indexes giving the diving points between sub - * sequences. The first sub-sequence is contained in (X0, X1), (Y0, Y1), - * the second in (X1, X2), (Y1, Y2) and so on. Note that (X0, Y0) == - * (XOFF, YOFF) and (X[NCHUNKS], Y[NCHUNKS]) == (XLIM, YLIM). - * - * This function assumes that the first lines of the specified portions of - * the two files do not match, and likewise that the last lines do not - * match. The caller must trim matching lines from the beginning and end - * of the portions it is going to specify. - */ - function _diag ($xoff, $xlim, $yoff, $ylim, $nchunks) - { - $flip = false; - - if ($xlim - $xoff > $ylim - $yoff) { - /* Things seems faster (I'm not sure I understand why) when the - * shortest sequence is in X. */ - $flip = true; - list ($xoff, $xlim, $yoff, $ylim) - = array($yoff, $ylim, $xoff, $xlim); - } - - if ($flip) { - for ($i = $ylim - 1; $i >= $yoff; $i--) { - $ymatches[$this->xv[$i]][] = $i; - } - } else { - for ($i = $ylim - 1; $i >= $yoff; $i--) { - $ymatches[$this->yv[$i]][] = $i; - } - } - - $this->lcs = 0; - $this->seq[0]= $yoff - 1; - $this->in_seq = array(); - $ymids[0] = array(); - - $numer = $xlim - $xoff + $nchunks - 1; - $x = $xoff; - for ($chunk = 0; $chunk < $nchunks; $chunk++) { - if ($chunk > 0) { - for ($i = 0; $i <= $this->lcs; $i++) { - $ymids[$i][$chunk - 1] = $this->seq[$i]; - } - } - - $x1 = $xoff + (int)(($numer + ($xlim - $xoff) * $chunk) / $nchunks); - for (; $x < $x1; $x++) { - $line = $flip ? $this->yv[$x] : $this->xv[$x]; - if (empty($ymatches[$line])) { - continue; - } - $matches = $ymatches[$line]; - reset($matches); - while (list(, $y) = each($matches)) { - if (empty($this->in_seq[$y])) { - $k = $this->_lcsPos($y); - assert($k > 0); - $ymids[$k] = $ymids[$k - 1]; - break; - } - } - while (list(, $y) = each($matches)) { - if ($y > $this->seq[$k - 1]) { - assert($y <= $this->seq[$k]); - /* Optimization: this is a common case: next match is - * just replacing previous match. */ - $this->in_seq[$this->seq[$k]] = false; - $this->seq[$k] = $y; - $this->in_seq[$y] = 1; - } elseif (empty($this->in_seq[$y])) { - $k = $this->_lcsPos($y); - assert($k > 0); - $ymids[$k] = $ymids[$k - 1]; - } - } - } - } - - $seps[] = $flip ? array($yoff, $xoff) : array($xoff, $yoff); - $ymid = $ymids[$this->lcs]; - for ($n = 0; $n < $nchunks - 1; $n++) { - $x1 = $xoff + (int)(($numer + ($xlim - $xoff) * $n) / $nchunks); - $y1 = $ymid[$n] + 1; - $seps[] = $flip ? array($y1, $x1) : array($x1, $y1); - } - $seps[] = $flip ? array($ylim, $xlim) : array($xlim, $ylim); - - return array($this->lcs, $seps); - } - - function _lcsPos($ypos) - { - $end = $this->lcs; - if ($end == 0 || $ypos > $this->seq[$end]) { - $this->seq[++$this->lcs] = $ypos; - $this->in_seq[$ypos] = 1; - return $this->lcs; - } - - $beg = 1; - while ($beg < $end) { - $mid = (int)(($beg + $end) / 2); - if ($ypos > $this->seq[$mid]) { - $beg = $mid + 1; - } else { - $end = $mid; - } - } - - assert($ypos != $this->seq[$end]); - - $this->in_seq[$this->seq[$end]] = false; - $this->seq[$end] = $ypos; - $this->in_seq[$ypos] = 1; - return $end; - } - - /** - * Finds LCS of two sequences. - * - * The results are recorded in the vectors $this->{x,y}changed[], by - * storing a 1 in the element for each line that is an insertion or - * deletion (ie. is not in the LCS). - * - * The subsequence of file 0 is (XOFF, XLIM) and likewise for file 1. - * - * Note that XLIM, YLIM are exclusive bounds. All line numbers are - * origin-0 and discarded lines are not counted. - */ - function _compareseq ($xoff, $xlim, $yoff, $ylim) - { - /* Slide down the bottom initial diagonal. */ - while ($xoff < $xlim && $yoff < $ylim - && $this->xv[$xoff] == $this->yv[$yoff]) { - ++$xoff; - ++$yoff; - } - - /* Slide up the top initial diagonal. */ - while ($xlim > $xoff && $ylim > $yoff - && $this->xv[$xlim - 1] == $this->yv[$ylim - 1]) { - --$xlim; - --$ylim; - } - - if ($xoff == $xlim || $yoff == $ylim) { - $lcs = 0; - } else { - /* This is ad hoc but seems to work well. $nchunks = - * sqrt(min($xlim - $xoff, $ylim - $yoff) / 2.5); $nchunks = - * max(2,min(8,(int)$nchunks)); */ - $nchunks = min(7, $xlim - $xoff, $ylim - $yoff) + 1; - list($lcs, $seps) - = $this->_diag($xoff, $xlim, $yoff, $ylim, $nchunks); - } - - if ($lcs == 0) { - /* X and Y sequences have no common subsequence: mark all - * changed. */ - while ($yoff < $ylim) { - $this->ychanged[$this->yind[$yoff++]] = 1; - } - while ($xoff < $xlim) { - $this->xchanged[$this->xind[$xoff++]] = 1; - } - } else { - /* Use the partitions to split this problem into subproblems. */ - reset($seps); - $pt1 = $seps[0]; - while ($pt2 = next($seps)) { - $this->_compareseq ($pt1[0], $pt2[0], $pt1[1], $pt2[1]); - $pt1 = $pt2; - } - } - } - - /** - * Adjusts inserts/deletes of identical lines to join changes as much as - * possible. - * - * We do something when a run of changed lines include a line at one end - * and has an excluded, identical line at the other. We are free to - * choose which identical line is included. `compareseq' usually chooses - * the one at the beginning, but usually it is cleaner to consider the - * following identical line to be the "change". - * - * This is extracted verbatim from analyze.c (GNU diffutils-2.7). - */ - function _shiftBoundaries($lines, &$changed, $other_changed) - { - $i = 0; - $j = 0; - - assert('count($lines) == count($changed)'); - $len = count($lines); - $other_len = count($other_changed); - - while (1) { - /* Scan forward to find the beginning of another run of - * changes. Also keep track of the corresponding point in the - * other file. - * - * Throughout this code, $i and $j are adjusted together so that - * the first $i elements of $changed and the first $j elements of - * $other_changed both contain the same number of zeros (unchanged - * lines). - * - * Furthermore, $j is always kept so that $j == $other_len or - * $other_changed[$j] == false. */ - while ($j < $other_len && $other_changed[$j]) { - $j++; - } - - while ($i < $len && ! $changed[$i]) { - assert('$j < $other_len && ! $other_changed[$j]'); - $i++; $j++; - while ($j < $other_len && $other_changed[$j]) { - $j++; - } - } - - if ($i == $len) { - break; - } - - $start = $i; - - /* Find the end of this run of changes. */ - while (++$i < $len && $changed[$i]) { - continue; - } - - do { - /* Record the length of this run of changes, so that we can - * later determine whether the run has grown. */ - $runlength = $i - $start; - - /* Move the changed region back, so long as the previous - * unchanged line matches the last changed one. This merges - * with previous changed regions. */ - while ($start > 0 && $lines[$start - 1] == $lines[$i - 1]) { - $changed[--$start] = 1; - $changed[--$i] = false; - while ($start > 0 && $changed[$start - 1]) { - $start--; - } - assert('$j > 0'); - while ($other_changed[--$j]) { - continue; - } - assert('$j >= 0 && !$other_changed[$j]'); - } - - /* Set CORRESPONDING to the end of the changed run, at the - * last point where it corresponds to a changed run in the - * other file. CORRESPONDING == LEN means no such point has - * been found. */ - $corresponding = $j < $other_len ? $i : $len; - - /* Move the changed region forward, so long as the first - * changed line matches the following unchanged one. This - * merges with following changed regions. Do this second, so - * that if there are no merges, the changed region is moved - * forward as far as possible. */ - while ($i < $len && $lines[$start] == $lines[$i]) { - $changed[$start++] = false; - $changed[$i++] = 1; - while ($i < $len && $changed[$i]) { - $i++; - } - - assert('$j < $other_len && ! $other_changed[$j]'); - $j++; - if ($j < $other_len && $other_changed[$j]) { - $corresponding = $i; - while ($j < $other_len && $other_changed[$j]) { - $j++; - } - } - } - } while ($runlength != $i - $start); - - /* If possible, move the fully-merged run of changes back to a - * corresponding run in the other file. */ - while ($corresponding < $i) { - $changed[--$start] = 1; - $changed[--$i] = 0; - assert('$j > 0'); - while ($other_changed[--$j]) { - continue; - } - assert('$j >= 0 && !$other_changed[$j]'); - } - } - } - -} diff --git a/pear/Text/Diff/Engine/shell.php b/pear/Text/Diff/Engine/shell.php deleted file mode 100644 index 7f858cb..0000000 --- a/pear/Text/Diff/Engine/shell.php +++ /dev/null @@ -1,164 +0,0 @@ - - * @package Text_Diff - * @since 0.3.0 - */ -class Text_Diff_Engine_shell { - - /** - * Path to the diff executable - * - * @var string - */ - var $_diffCommand = 'diff'; - - /** - * Returns the array of differences. - * - * @param array $from_lines lines of text from old file - * @param array $to_lines lines of text from new file - * - * @return array all changes made (array with Text_Diff_Op_* objects) - */ - function diff($from_lines, $to_lines) - { - array_walk($from_lines, array('Text_Diff', 'trimNewlines')); - array_walk($to_lines, array('Text_Diff', 'trimNewlines')); - - $temp_dir = Text_Diff::_getTempDir(); - - // Execute gnu diff or similar to get a standard diff file. - $from_file = tempnam($temp_dir, 'Text_Diff'); - $to_file = tempnam($temp_dir, 'Text_Diff'); - $fp = fopen($from_file, 'w'); - fwrite($fp, implode("\n", $from_lines)); - fclose($fp); - $fp = fopen($to_file, 'w'); - fwrite($fp, implode("\n", $to_lines)); - fclose($fp); - $diff = shell_exec($this->_diffCommand . ' ' . $from_file . ' ' . $to_file); - unlink($from_file); - unlink($to_file); - - if (is_null($diff)) { - // No changes were made - return array(new Text_Diff_Op_copy($from_lines)); - } - - $from_line_no = 1; - $to_line_no = 1; - $edits = array(); - - // Get changed lines by parsing something like: - // 0a1,2 - // 1,2c4,6 - // 1,5d6 - preg_match_all('#^(\d+)(?:,(\d+))?([adc])(\d+)(?:,(\d+))?$#m', $diff, - $matches, PREG_SET_ORDER); - - foreach ($matches as $match) { - if (!isset($match[5])) { - // This paren is not set every time (see regex). - $match[5] = false; - } - - if ($match[3] == 'a') { - $from_line_no--; - } - - if ($match[3] == 'd') { - $to_line_no--; - } - - if ($from_line_no < $match[1] || $to_line_no < $match[4]) { - // copied lines - assert('$match[1] - $from_line_no == $match[4] - $to_line_no'); - array_push($edits, - new Text_Diff_Op_copy( - $this->_getLines($from_lines, $from_line_no, $match[1] - 1), - $this->_getLines($to_lines, $to_line_no, $match[4] - 1))); - } - - switch ($match[3]) { - case 'd': - // deleted lines - array_push($edits, - new Text_Diff_Op_delete( - $this->_getLines($from_lines, $from_line_no, $match[2]))); - $to_line_no++; - break; - - case 'c': - // changed lines - array_push($edits, - new Text_Diff_Op_change( - $this->_getLines($from_lines, $from_line_no, $match[2]), - $this->_getLines($to_lines, $to_line_no, $match[5]))); - break; - - case 'a': - // added lines - array_push($edits, - new Text_Diff_Op_add( - $this->_getLines($to_lines, $to_line_no, $match[5]))); - $from_line_no++; - break; - } - } - - if (!empty($from_lines)) { - // Some lines might still be pending. Add them as copied - array_push($edits, - new Text_Diff_Op_copy( - $this->_getLines($from_lines, $from_line_no, - $from_line_no + count($from_lines) - 1), - $this->_getLines($to_lines, $to_line_no, - $to_line_no + count($to_lines) - 1))); - } - - return $edits; - } - - /** - * Get lines from either the old or new text - * - * @access private - * - * @param array &$text_lines Either $from_lines or $to_lines - * @param int &$line_no Current line number - * @param int $end Optional end line, when we want to chop more - * than one line. - * - * @return array The chopped lines - */ - function _getLines(&$text_lines, &$line_no, $end = false) - { - if (!empty($end)) { - $lines = array(); - // We can shift even more - while ($line_no <= $end) { - array_push($lines, array_shift($text_lines)); - $line_no++; - } - } else { - $lines = array(array_shift($text_lines)); - $line_no++; - } - - return $lines; - } - -} diff --git a/pear/Text/Diff/Engine/string.php b/pear/Text/Diff/Engine/string.php deleted file mode 100644 index 9352e60..0000000 --- a/pear/Text/Diff/Engine/string.php +++ /dev/null @@ -1,250 +0,0 @@ - - * $patch = file_get_contents('example.patch'); - * $diff = new Text_Diff('string', array($patch)); - * $renderer = new Text_Diff_Renderer_inline(); - * echo $renderer->render($diff); - * - * - * $Horde: framework/Text_Diff/Diff/Engine/string.php,v 1.5.2.7 2009/07/24 13:04:43 jan Exp $ - * - * Copyright 2005 Örjan Persson - * Copyright 2005-2009 The Horde Project (http://www.horde.org/) - * - * See the enclosed file COPYING for license information (LGPL). If you did - * not receive this file, see http://opensource.org/licenses/lgpl-license.php. - * - * @author Örjan Persson - * @package Text_Diff - * @since 0.2.0 - */ -class Text_Diff_Engine_string { - - /** - * Parses a unified or context diff. - * - * First param contains the whole diff and the second can be used to force - * a specific diff type. If the second parameter is 'autodetect', the - * diff will be examined to find out which type of diff this is. - * - * @param string $diff The diff content. - * @param string $mode The diff mode of the content in $diff. One of - * 'context', 'unified', or 'autodetect'. - * - * @return array List of all diff operations. - */ - function diff($diff, $mode = 'autodetect') - { - // Detect line breaks. - $lnbr = "\n"; - if (strpos($diff, "\r\n") !== false) { - $lnbr = "\r\n"; - } elseif (strpos($diff, "\r") !== false) { - $lnbr = "\r"; - } - - // Make sure we have a line break at the EOF. - if (substr($diff, -strlen($lnbr)) != $lnbr) { - $diff .= $lnbr; - } - - if ($mode != 'autodetect' && $mode != 'context' && $mode != 'unified') { - return PEAR::raiseError('Type of diff is unsupported'); - } - - if ($mode == 'autodetect') { - $context = strpos($diff, '***'); - $unified = strpos($diff, '---'); - if ($context === $unified) { - return PEAR::raiseError('Type of diff could not be detected'); - } elseif ($context === false || $unified === false) { - $mode = $context !== false ? 'context' : 'unified'; - } else { - $mode = $context < $unified ? 'context' : 'unified'; - } - } - - // Split by new line and remove the diff header, if there is one. - $diff = explode($lnbr, $diff); - if (($mode == 'context' && strpos($diff[0], '***') === 0) || - ($mode == 'unified' && strpos($diff[0], '---') === 0)) { - array_shift($diff); - array_shift($diff); - } - - if ($mode == 'context') { - return $this->parseContextDiff($diff); - } else { - return $this->parseUnifiedDiff($diff); - } - } - - /** - * Parses an array containing the unified diff. - * - * @param array $diff Array of lines. - * - * @return array List of all diff operations. - */ - function parseUnifiedDiff($diff) - { - $edits = array(); - $end = count($diff) - 1; - for ($i = 0; $i < $end;) { - $diff1 = array(); - switch (substr($diff[$i], 0, 1)) { - case ' ': - do { - $diff1[] = substr($diff[$i], 1); - } while (++$i < $end && substr($diff[$i], 0, 1) == ' '); - $edits[] = new Text_Diff_Op_copy($diff1); - break; - - case '+': - // get all new lines - do { - $diff1[] = substr($diff[$i], 1); - } while (++$i < $end && substr($diff[$i], 0, 1) == '+'); - $edits[] = new Text_Diff_Op_add($diff1); - break; - - case '-': - // get changed or removed lines - $diff2 = array(); - do { - $diff1[] = substr($diff[$i], 1); - } while (++$i < $end && substr($diff[$i], 0, 1) == '-'); - - while ($i < $end && substr($diff[$i], 0, 1) == '+') { - $diff2[] = substr($diff[$i++], 1); - } - if (count($diff2) == 0) { - $edits[] = new Text_Diff_Op_delete($diff1); - } else { - $edits[] = new Text_Diff_Op_change($diff1, $diff2); - } - break; - - default: - $i++; - break; - } - } - - return $edits; - } - - /** - * Parses an array containing the context diff. - * - * @param array $diff Array of lines. - * - * @return array List of all diff operations. - */ - function parseContextDiff(&$diff) - { - $edits = array(); - $i = $max_i = $j = $max_j = 0; - $end = count($diff) - 1; - while ($i < $end && $j < $end) { - while ($i >= $max_i && $j >= $max_j) { - // Find the boundaries of the diff output of the two files - for ($i = $j; - $i < $end && substr($diff[$i], 0, 3) == '***'; - $i++); - for ($max_i = $i; - $max_i < $end && substr($diff[$max_i], 0, 3) != '---'; - $max_i++); - for ($j = $max_i; - $j < $end && substr($diff[$j], 0, 3) == '---'; - $j++); - for ($max_j = $j; - $max_j < $end && substr($diff[$max_j], 0, 3) != '***'; - $max_j++); - } - - // find what hasn't been changed - $array = array(); - while ($i < $max_i && - $j < $max_j && - strcmp($diff[$i], $diff[$j]) == 0) { - $array[] = substr($diff[$i], 2); - $i++; - $j++; - } - - while ($i < $max_i && ($max_j-$j) <= 1) { - if ($diff[$i] != '' && substr($diff[$i], 0, 1) != ' ') { - break; - } - $array[] = substr($diff[$i++], 2); - } - - while ($j < $max_j && ($max_i-$i) <= 1) { - if ($diff[$j] != '' && substr($diff[$j], 0, 1) != ' ') { - break; - } - $array[] = substr($diff[$j++], 2); - } - if (count($array) > 0) { - $edits[] = new Text_Diff_Op_copy($array); - } - - if ($i < $max_i) { - $diff1 = array(); - switch (substr($diff[$i], 0, 1)) { - case '!': - $diff2 = array(); - do { - $diff1[] = substr($diff[$i], 2); - if ($j < $max_j && substr($diff[$j], 0, 1) == '!') { - $diff2[] = substr($diff[$j++], 2); - } - } while (++$i < $max_i && substr($diff[$i], 0, 1) == '!'); - $edits[] = new Text_Diff_Op_change($diff1, $diff2); - break; - - case '+': - do { - $diff1[] = substr($diff[$i], 2); - } while (++$i < $max_i && substr($diff[$i], 0, 1) == '+'); - $edits[] = new Text_Diff_Op_add($diff1); - break; - - case '-': - do { - $diff1[] = substr($diff[$i], 2); - } while (++$i < $max_i && substr($diff[$i], 0, 1) == '-'); - $edits[] = new Text_Diff_Op_delete($diff1); - break; - } - } - - if ($j < $max_j) { - $diff2 = array(); - switch (substr($diff[$j], 0, 1)) { - case '+': - do { - $diff2[] = substr($diff[$j++], 2); - } while ($j < $max_j && substr($diff[$j], 0, 1) == '+'); - $edits[] = new Text_Diff_Op_add($diff2); - break; - - case '-': - do { - $diff2[] = substr($diff[$j++], 2); - } while ($j < $max_j && substr($diff[$j], 0, 1) == '-'); - $edits[] = new Text_Diff_Op_delete($diff2); - break; - } - } - } - - return $edits; - } - -} diff --git a/pear/Text/Diff/Engine/xdiff.php b/pear/Text/Diff/Engine/xdiff.php deleted file mode 100644 index 241d2e5..0000000 --- a/pear/Text/Diff/Engine/xdiff.php +++ /dev/null @@ -1,66 +0,0 @@ - - * @package Text_Diff - */ -class Text_Diff_Engine_xdiff { - - /** - */ - function diff($from_lines, $to_lines) - { - array_walk($from_lines, array('Text_Diff', 'trimNewlines')); - array_walk($to_lines, array('Text_Diff', 'trimNewlines')); - - /* Convert the two input arrays into strings for xdiff processing. */ - $from_string = implode("\n", $from_lines); - $to_string = implode("\n", $to_lines); - - /* Diff the two strings and convert the result to an array. */ - $diff = xdiff_string_diff($from_string, $to_string, count($to_lines)); - $diff = explode("\n", $diff); - - /* Walk through the diff one line at a time. We build the $edits - * array of diff operations by reading the first character of the - * xdiff output (which is in the "unified diff" format). - * - * Note that we don't have enough information to detect "changed" - * lines using this approach, so we can't add Text_Diff_Op_changed - * instances to the $edits array. The result is still perfectly - * valid, albeit a little less descriptive and efficient. */ - $edits = array(); - foreach ($diff as $line) { - if (!strlen($line)) { - continue; - } - switch ($line[0]) { - case ' ': - $edits[] = &new Text_Diff_Op_copy(array(substr($line, 1))); - break; - - case '+': - $edits[] = &new Text_Diff_Op_add(array(substr($line, 1))); - break; - - case '-': - $edits[] = &new Text_Diff_Op_delete(array(substr($line, 1))); - break; - } - } - - return $edits; - } - -} diff --git a/pear/Text/Diff/Mapped.php b/pear/Text/Diff/Mapped.php deleted file mode 100644 index dc46e5e..0000000 --- a/pear/Text/Diff/Mapped.php +++ /dev/null @@ -1,55 +0,0 @@ - - */ -class Text_Diff_Mapped extends Text_Diff { - - /** - * Computes a diff between sequences of strings. - * - * This can be used to compute things like case-insensitve diffs, or diffs - * which ignore changes in white-space. - * - * @param array $from_lines An array of strings. - * @param array $to_lines An array of strings. - * @param array $mapped_from_lines This array should have the same size - * number of elements as $from_lines. The - * elements in $mapped_from_lines and - * $mapped_to_lines are what is actually - * compared when computing the diff. - * @param array $mapped_to_lines This array should have the same number - * of elements as $to_lines. - */ - function Text_Diff_Mapped($from_lines, $to_lines, - $mapped_from_lines, $mapped_to_lines) - { - assert(count($from_lines) == count($mapped_from_lines)); - assert(count($to_lines) == count($mapped_to_lines)); - - parent::Text_Diff($mapped_from_lines, $mapped_to_lines); - - $xi = $yi = 0; - for ($i = 0; $i < count($this->_edits); $i++) { - $orig = &$this->_edits[$i]->orig; - if (is_array($orig)) { - $orig = array_slice($from_lines, $xi, count($orig)); - $xi += count($orig); - } - - $final = &$this->_edits[$i]->final; - if (is_array($final)) { - $final = array_slice($to_lines, $yi, count($final)); - $yi += count($final); - } - } - } - -} diff --git a/pear/Text/Diff/Renderer.php b/pear/Text/Diff/Renderer.php deleted file mode 100644 index 3a51650..0000000 --- a/pear/Text/Diff/Renderer.php +++ /dev/null @@ -1,237 +0,0 @@ - $value) { - $v = '_' . $param; - if (isset($this->$v)) { - $this->$v = $value; - } - } - } - - /** - * Get any renderer parameters. - * - * @return array All parameters of this renderer object. - */ - function getParams() - { - $params = array(); - foreach (get_object_vars($this) as $k => $v) { - if ($k[0] == '_') { - $params[substr($k, 1)] = $v; - } - } - - return $params; - } - - /** - * Renders a diff. - * - * @param Text_Diff $diff A Text_Diff object. - * - * @return string The formatted output. - */ - function render($diff) - { - $xi = $yi = 1; - $block = false; - $context = array(); - - $nlead = $this->_leading_context_lines; - $ntrail = $this->_trailing_context_lines; - - $output = $this->_startDiff(); - - $diffs = $diff->getDiff(); - foreach ($diffs as $i => $edit) { - /* If these are unchanged (copied) lines, and we want to keep - * leading or trailing context lines, extract them from the copy - * block. */ - if (is_a($edit, 'Text_Diff_Op_copy')) { - /* Do we have any diff blocks yet? */ - if (is_array($block)) { - /* How many lines to keep as context from the copy - * block. */ - $keep = $i == count($diffs) - 1 ? $ntrail : $nlead + $ntrail; - if (count($edit->orig) <= $keep) { - /* We have less lines in the block than we want for - * context => keep the whole block. */ - $block[] = $edit; - } else { - if ($ntrail) { - /* Create a new block with as many lines as we need - * for the trailing context. */ - $context = array_slice($edit->orig, 0, $ntrail); - $block[] = new Text_Diff_Op_copy($context); - } - /* @todo */ - $output .= $this->_block($x0, $ntrail + $xi - $x0, - $y0, $ntrail + $yi - $y0, - $block); - $block = false; - } - } - /* Keep the copy block as the context for the next block. */ - $context = $edit->orig; - } else { - /* Don't we have any diff blocks yet? */ - if (!is_array($block)) { - /* Extract context lines from the preceding copy block. */ - $context = array_slice($context, count($context) - $nlead); - $x0 = $xi - count($context); - $y0 = $yi - count($context); - $block = array(); - if ($context) { - $block[] = new Text_Diff_Op_copy($context); - } - } - $block[] = $edit; - } - - if ($edit->orig) { - $xi += count($edit->orig); - } - if ($edit->final) { - $yi += count($edit->final); - } - } - - if (is_array($block)) { - $output .= $this->_block($x0, $xi - $x0, - $y0, $yi - $y0, - $block); - } - - return $output . $this->_endDiff(); - } - - function _block($xbeg, $xlen, $ybeg, $ylen, &$edits) - { - $output = $this->_startBlock($this->_blockHeader($xbeg, $xlen, $ybeg, $ylen)); - - foreach ($edits as $edit) { - switch (strtolower(get_class($edit))) { - case 'text_diff_op_copy': - $output .= $this->_context($edit->orig); - break; - - case 'text_diff_op_add': - $output .= $this->_added($edit->final); - break; - - case 'text_diff_op_delete': - $output .= $this->_deleted($edit->orig); - break; - - case 'text_diff_op_change': - $output .= $this->_changed($edit->orig, $edit->final); - break; - } - } - - return $output . $this->_endBlock(); - } - - function _startDiff() - { - return ''; - } - - function _endDiff() - { - return ''; - } - - function _blockHeader($xbeg, $xlen, $ybeg, $ylen) - { - if ($xlen > 1) { - $xbeg .= ',' . ($xbeg + $xlen - 1); - } - if ($ylen > 1) { - $ybeg .= ',' . ($ybeg + $ylen - 1); - } - - // this matches the GNU Diff behaviour - if ($xlen && !$ylen) { - $ybeg--; - } elseif (!$xlen) { - $xbeg--; - } - - return $xbeg . ($xlen ? ($ylen ? 'c' : 'd') : 'a') . $ybeg; - } - - function _startBlock($header) - { - return $header . "\n"; - } - - function _endBlock() - { - return ''; - } - - function _lines($lines, $prefix = ' ') - { - return $prefix . implode("\n$prefix", $lines) . "\n"; - } - - function _context($lines) - { - return $this->_lines($lines, ' '); - } - - function _added($lines) - { - return $this->_lines($lines, '> '); - } - - function _deleted($lines) - { - return $this->_lines($lines, '< '); - } - - function _changed($orig, $final) - { - return $this->_deleted($orig) . "---\n" . $this->_added($final); - } - -} diff --git a/pear/Text/Diff/Renderer/context.php b/pear/Text/Diff/Renderer/context.php deleted file mode 100644 index af53801..0000000 --- a/pear/Text/Diff/Renderer/context.php +++ /dev/null @@ -1,77 +0,0 @@ -_second_block = "--- $ybeg ----\n"; - return "***************\n*** $xbeg ****"; - } - - function _endBlock() - { - return $this->_second_block; - } - - function _context($lines) - { - $this->_second_block .= $this->_lines($lines, ' '); - return $this->_lines($lines, ' '); - } - - function _added($lines) - { - $this->_second_block .= $this->_lines($lines, '+ '); - return ''; - } - - function _deleted($lines) - { - return $this->_lines($lines, '- '); - } - - function _changed($orig, $final) - { - $this->_second_block .= $this->_lines($final, '! '); - return $this->_lines($orig, '! '); - } - -} diff --git a/pear/Text/Diff/Renderer/inline.php b/pear/Text/Diff/Renderer/inline.php deleted file mode 100644 index 5dd20d2..0000000 --- a/pear/Text/Diff/Renderer/inline.php +++ /dev/null @@ -1,172 +0,0 @@ -'; - - /** - * Suffix for inserted text. - */ - var $_ins_suffix = ''; - - /** - * Prefix for deleted text. - */ - var $_del_prefix = ''; - - /** - * Suffix for deleted text. - */ - var $_del_suffix = ''; - - /** - * Header for each change block. - */ - var $_block_header = ''; - - /** - * What are we currently splitting on? Used to recurse to show word-level - * changes. - */ - var $_split_level = 'lines'; - - function _blockHeader($xbeg, $xlen, $ybeg, $ylen) - { - return $this->_block_header; - } - - function _startBlock($header) - { - return $header; - } - - function _lines($lines, $prefix = ' ', $encode = true) - { - if ($encode) { - array_walk($lines, array(&$this, '_encode')); - } - - if ($this->_split_level == 'words') { - return implode('', $lines); - } else { - return implode("\n", $lines) . "\n"; - } - } - - function _added($lines) - { - array_walk($lines, array(&$this, '_encode')); - $lines[0] = $this->_ins_prefix . $lines[0]; - $lines[count($lines) - 1] .= $this->_ins_suffix; - return $this->_lines($lines, ' ', false); - } - - function _deleted($lines, $words = false) - { - array_walk($lines, array(&$this, '_encode')); - $lines[0] = $this->_del_prefix . $lines[0]; - $lines[count($lines) - 1] .= $this->_del_suffix; - return $this->_lines($lines, ' ', false); - } - - function _changed($orig, $final) - { - /* If we've already split on words, don't try to do so again - just - * display. */ - if ($this->_split_level == 'words') { - $prefix = ''; - while ($orig[0] !== false && $final[0] !== false && - substr($orig[0], 0, 1) == ' ' && - substr($final[0], 0, 1) == ' ') { - $prefix .= substr($orig[0], 0, 1); - $orig[0] = substr($orig[0], 1); - $final[0] = substr($final[0], 1); - } - return $prefix . $this->_deleted($orig) . $this->_added($final); - } - - $text1 = implode("\n", $orig); - $text2 = implode("\n", $final); - - /* Non-printing newline marker. */ - $nl = "\0"; - - /* We want to split on word boundaries, but we need to - * preserve whitespace as well. Therefore we split on words, - * but include all blocks of whitespace in the wordlist. */ - $diff = new Text_Diff('native', - array($this->_splitOnWords($text1, $nl), - $this->_splitOnWords($text2, $nl))); - - /* Get the diff in inline format. */ - $renderer = new Text_Diff_Renderer_inline - (array_merge($this->getParams(), - array('split_level' => 'words'))); - - /* Run the diff and get the output. */ - return str_replace($nl, "\n", $renderer->render($diff)) . "\n"; - } - - function _splitOnWords($string, $newlineEscape = "\n") - { - // Ignore \0; otherwise the while loop will never finish. - $string = str_replace("\0", '', $string); - - $words = array(); - $length = strlen($string); - $pos = 0; - - while ($pos < $length) { - // Eat a word with any preceding whitespace. - $spaces = strspn(substr($string, $pos), " \n"); - $nextpos = strcspn(substr($string, $pos + $spaces), " \n"); - $words[] = str_replace("\n", $newlineEscape, substr($string, $pos, $spaces + $nextpos)); - $pos += $spaces + $nextpos; - } - - return $words; - } - - function _encode(&$string) - { - $string = htmlspecialchars($string); - } - -} diff --git a/pear/Text/Diff/Renderer/unified.php b/pear/Text/Diff/Renderer/unified.php deleted file mode 100644 index f990f72..0000000 --- a/pear/Text/Diff/Renderer/unified.php +++ /dev/null @@ -1,67 +0,0 @@ -_lines($lines, ' '); - } - - function _added($lines) - { - return $this->_lines($lines, '+'); - } - - function _deleted($lines) - { - return $this->_lines($lines, '-'); - } - - function _changed($orig, $final) - { - return $this->_deleted($orig) . $this->_added($final); - } - -} diff --git a/pear/Text/Diff/ThreeWay.php b/pear/Text/Diff/ThreeWay.php deleted file mode 100644 index 5b0357c..0000000 --- a/pear/Text/Diff/ThreeWay.php +++ /dev/null @@ -1,276 +0,0 @@ - - */ -class Text_Diff_ThreeWay extends Text_Diff { - - /** - * Conflict counter. - * - * @var integer - */ - var $_conflictingBlocks = 0; - - /** - * Computes diff between 3 sequences of strings. - * - * @param array $orig The original lines to use. - * @param array $final1 The first version to compare to. - * @param array $final2 The second version to compare to. - */ - function Text_Diff_ThreeWay($orig, $final1, $final2) - { - if (extension_loaded('xdiff')) { - $engine = new Text_Diff_Engine_xdiff(); - } else { - $engine = new Text_Diff_Engine_native(); - } - - $this->_edits = $this->_diff3($engine->diff($orig, $final1), - $engine->diff($orig, $final2)); - } - - /** - */ - function mergedOutput($label1 = false, $label2 = false) - { - $lines = array(); - foreach ($this->_edits as $edit) { - if ($edit->isConflict()) { - /* FIXME: this should probably be moved somewhere else. */ - $lines = array_merge($lines, - array('<<<<<<<' . ($label1 ? ' ' . $label1 : '')), - $edit->final1, - array("======="), - $edit->final2, - array('>>>>>>>' . ($label2 ? ' ' . $label2 : ''))); - $this->_conflictingBlocks++; - } else { - $lines = array_merge($lines, $edit->merged()); - } - } - - return $lines; - } - - /** - * @access private - */ - function _diff3($edits1, $edits2) - { - $edits = array(); - $bb = new Text_Diff_ThreeWay_BlockBuilder(); - - $e1 = current($edits1); - $e2 = current($edits2); - while ($e1 || $e2) { - if ($e1 && $e2 && is_a($e1, 'Text_Diff_Op_copy') && is_a($e2, 'Text_Diff_Op_copy')) { - /* We have copy blocks from both diffs. This is the (only) - * time we want to emit a diff3 copy block. Flush current - * diff3 diff block, if any. */ - if ($edit = $bb->finish()) { - $edits[] = $edit; - } - - $ncopy = min($e1->norig(), $e2->norig()); - assert($ncopy > 0); - $edits[] = new Text_Diff_ThreeWay_Op_copy(array_slice($e1->orig, 0, $ncopy)); - - if ($e1->norig() > $ncopy) { - array_splice($e1->orig, 0, $ncopy); - array_splice($e1->final, 0, $ncopy); - } else { - $e1 = next($edits1); - } - - if ($e2->norig() > $ncopy) { - array_splice($e2->orig, 0, $ncopy); - array_splice($e2->final, 0, $ncopy); - } else { - $e2 = next($edits2); - } - } else { - if ($e1 && $e2) { - if ($e1->orig && $e2->orig) { - $norig = min($e1->norig(), $e2->norig()); - $orig = array_splice($e1->orig, 0, $norig); - array_splice($e2->orig, 0, $norig); - $bb->input($orig); - } - - if (is_a($e1, 'Text_Diff_Op_copy')) { - $bb->out1(array_splice($e1->final, 0, $norig)); - } - - if (is_a($e2, 'Text_Diff_Op_copy')) { - $bb->out2(array_splice($e2->final, 0, $norig)); - } - } - - if ($e1 && ! $e1->orig) { - $bb->out1($e1->final); - $e1 = next($edits1); - } - if ($e2 && ! $e2->orig) { - $bb->out2($e2->final); - $e2 = next($edits2); - } - } - } - - if ($edit = $bb->finish()) { - $edits[] = $edit; - } - - return $edits; - } - -} - -/** - * @package Text_Diff - * @author Geoffrey T. Dairiki - * - * @access private - */ -class Text_Diff_ThreeWay_Op { - - function Text_Diff_ThreeWay_Op($orig = false, $final1 = false, $final2 = false) - { - $this->orig = $orig ? $orig : array(); - $this->final1 = $final1 ? $final1 : array(); - $this->final2 = $final2 ? $final2 : array(); - } - - function merged() - { - if (!isset($this->_merged)) { - if ($this->final1 === $this->final2) { - $this->_merged = &$this->final1; - } elseif ($this->final1 === $this->orig) { - $this->_merged = &$this->final2; - } elseif ($this->final2 === $this->orig) { - $this->_merged = &$this->final1; - } else { - $this->_merged = false; - } - } - - return $this->_merged; - } - - function isConflict() - { - return $this->merged() === false; - } - -} - -/** - * @package Text_Diff - * @author Geoffrey T. Dairiki - * - * @access private - */ -class Text_Diff_ThreeWay_Op_copy extends Text_Diff_ThreeWay_Op { - - function Text_Diff_ThreeWay_Op_Copy($lines = false) - { - $this->orig = $lines ? $lines : array(); - $this->final1 = &$this->orig; - $this->final2 = &$this->orig; - } - - function merged() - { - return $this->orig; - } - - function isConflict() - { - return false; - } - -} - -/** - * @package Text_Diff - * @author Geoffrey T. Dairiki - * - * @access private - */ -class Text_Diff_ThreeWay_BlockBuilder { - - function Text_Diff_ThreeWay_BlockBuilder() - { - $this->_init(); - } - - function input($lines) - { - if ($lines) { - $this->_append($this->orig, $lines); - } - } - - function out1($lines) - { - if ($lines) { - $this->_append($this->final1, $lines); - } - } - - function out2($lines) - { - if ($lines) { - $this->_append($this->final2, $lines); - } - } - - function isEmpty() - { - return !$this->orig && !$this->final1 && !$this->final2; - } - - function finish() - { - if ($this->isEmpty()) { - return false; - } else { - $edit = new Text_Diff_ThreeWay_Op($this->orig, $this->final1, $this->final2); - $this->_init(); - return $edit; - } - } - - function _init() - { - $this->orig = $this->final1 = $this->final2 = array(); - } - - function _append(&$array, $lines) - { - array_splice($array, sizeof($array), 0, $lines); - } - -} diff --git a/pear/Text/Diff3.php b/pear/Text/Diff3.php deleted file mode 100644 index e9aea9f..0000000 --- a/pear/Text/Diff3.php +++ /dev/null @@ -1,276 +0,0 @@ - - */ -class Text_Diff3 extends Text_Diff { - - /** - * Conflict counter. - * - * @var integer - */ - var $_conflictingBlocks = 0; - - /** - * Computes diff between 3 sequences of strings. - * - * @param array $orig The original lines to use. - * @param array $final1 The first version to compare to. - * @param array $final2 The second version to compare to. - */ - function Text_Diff3($orig, $final1, $final2) - { - if (extension_loaded('xdiff')) { - $engine = new Text_Diff_Engine_xdiff(); - } else { - $engine = new Text_Diff_Engine_native(); - } - - $this->_edits = $this->_diff3($engine->diff($orig, $final1), - $engine->diff($orig, $final2)); - } - - /** - */ - function mergedOutput($label1 = false, $label2 = false) - { - $lines = array(); - foreach ($this->_edits as $edit) { - if ($edit->isConflict()) { - /* FIXME: this should probably be moved somewhere else. */ - $lines = array_merge($lines, - array('<<<<<<<' . ($label1 ? ' ' . $label1 : '')), - $edit->final1, - array("======="), - $edit->final2, - array('>>>>>>>' . ($label2 ? ' ' . $label2 : ''))); - $this->_conflictingBlocks++; - } else { - $lines = array_merge($lines, $edit->merged()); - } - } - - return $lines; - } - - /** - * @access private - */ - function _diff3($edits1, $edits2) - { - $edits = array(); - $bb = new Text_Diff3_BlockBuilder(); - - $e1 = current($edits1); - $e2 = current($edits2); - while ($e1 || $e2) { - if ($e1 && $e2 && is_a($e1, 'Text_Diff_Op_copy') && is_a($e2, 'Text_Diff_Op_copy')) { - /* We have copy blocks from both diffs. This is the (only) - * time we want to emit a diff3 copy block. Flush current - * diff3 diff block, if any. */ - if ($edit = $bb->finish()) { - $edits[] = $edit; - } - - $ncopy = min($e1->norig(), $e2->norig()); - assert($ncopy > 0); - $edits[] = new Text_Diff3_Op_copy(array_slice($e1->orig, 0, $ncopy)); - - if ($e1->norig() > $ncopy) { - array_splice($e1->orig, 0, $ncopy); - array_splice($e1->final, 0, $ncopy); - } else { - $e1 = next($edits1); - } - - if ($e2->norig() > $ncopy) { - array_splice($e2->orig, 0, $ncopy); - array_splice($e2->final, 0, $ncopy); - } else { - $e2 = next($edits2); - } - } else { - if ($e1 && $e2) { - if ($e1->orig && $e2->orig) { - $norig = min($e1->norig(), $e2->norig()); - $orig = array_splice($e1->orig, 0, $norig); - array_splice($e2->orig, 0, $norig); - $bb->input($orig); - } - - if (is_a($e1, 'Text_Diff_Op_copy')) { - $bb->out1(array_splice($e1->final, 0, $norig)); - } - - if (is_a($e2, 'Text_Diff_Op_copy')) { - $bb->out2(array_splice($e2->final, 0, $norig)); - } - } - - if ($e1 && ! $e1->orig) { - $bb->out1($e1->final); - $e1 = next($edits1); - } - if ($e2 && ! $e2->orig) { - $bb->out2($e2->final); - $e2 = next($edits2); - } - } - } - - if ($edit = $bb->finish()) { - $edits[] = $edit; - } - - return $edits; - } - -} - -/** - * @package Text_Diff - * @author Geoffrey T. Dairiki - * - * @access private - */ -class Text_Diff3_Op { - - function Text_Diff3_Op($orig = false, $final1 = false, $final2 = false) - { - $this->orig = $orig ? $orig : array(); - $this->final1 = $final1 ? $final1 : array(); - $this->final2 = $final2 ? $final2 : array(); - } - - function merged() - { - if (!isset($this->_merged)) { - if ($this->final1 === $this->final2) { - $this->_merged = &$this->final1; - } elseif ($this->final1 === $this->orig) { - $this->_merged = &$this->final2; - } elseif ($this->final2 === $this->orig) { - $this->_merged = &$this->final1; - } else { - $this->_merged = false; - } - } - - return $this->_merged; - } - - function isConflict() - { - return $this->merged() === false; - } - -} - -/** - * @package Text_Diff - * @author Geoffrey T. Dairiki - * - * @access private - */ -class Text_Diff3_Op_copy extends Text_Diff3_Op { - - function Text_Diff3_Op_Copy($lines = false) - { - $this->orig = $lines ? $lines : array(); - $this->final1 = &$this->orig; - $this->final2 = &$this->orig; - } - - function merged() - { - return $this->orig; - } - - function isConflict() - { - return false; - } - -} - -/** - * @package Text_Diff - * @author Geoffrey T. Dairiki - * - * @access private - */ -class Text_Diff3_BlockBuilder { - - function Text_Diff3_BlockBuilder() - { - $this->_init(); - } - - function input($lines) - { - if ($lines) { - $this->_append($this->orig, $lines); - } - } - - function out1($lines) - { - if ($lines) { - $this->_append($this->final1, $lines); - } - } - - function out2($lines) - { - if ($lines) { - $this->_append($this->final2, $lines); - } - } - - function isEmpty() - { - return !$this->orig && !$this->final1 && !$this->final2; - } - - function finish() - { - if ($this->isEmpty()) { - return false; - } else { - $edit = new Text_Diff3_Op($this->orig, $this->final1, $this->final2); - $this->_init(); - return $edit; - } - } - - function _init() - { - $this->orig = $this->final1 = $this->final2 = array(); - } - - function _append(&$array, $lines) - { - array_splice($array, sizeof($array), 0, $lines); - } - -} diff --git a/pear/Text/Wiki.php b/pear/Text/Wiki.php deleted file mode 100644 index 27a9161..0000000 --- a/pear/Text/Wiki.php +++ /dev/null @@ -1,1550 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version CVS: $Id: Wiki.php 248433 2007-12-17 16:03:48Z justinpatrin $ - * @link http://pear.php.net/package/Text_Wiki - */ - -/** - * The baseline abstract parser class. - */ -require_once 'Text/Wiki/Parse.php'; - -/** - * The baseline abstract render class. - */ -require_once 'Text/Wiki/Render.php'; - -/** - * Parse structured wiki text and render into arbitrary formats such as XHTML. - * - * This is the "master" class for handling the management and convenience - * functions to transform Wiki-formatted text. - * - * @category Text - * @package Text_Wiki - * @author Paul M. Jones - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version Release: 1.2.1 - * @link http://pear.php.net/package/Text_Wiki - */ -class Text_Wiki { - - /** - * - * The default list of rules, in order, to apply to the source text. - * - * @access public - * - * @var array - * - */ - - var $rules = array( - 'Prefilter', - 'Delimiter', - 'Code', - 'Function', - 'Html', - 'Raw', - 'Include', - 'Embed', - 'Anchor', - 'Heading', - 'Toc', - 'Horiz', - 'Break', - 'Blockquote', - 'List', - 'Deflist', - 'Table', - 'Image', - 'Phplookup', - 'Center', - 'Newline', - 'Paragraph', - 'Url', - 'Freelink', - 'Interwiki', - 'Wikilink', - 'Colortext', - 'Strong', - 'Bold', - 'Emphasis', - 'Italic', - 'Underline', - 'Tt', - 'Superscript', - 'Subscript', - 'Revise', - 'Tighten' - ); - - - /** - * - * The list of rules to not-apply to the source text. - * - * @access public - * - * @var array - * - */ - - var $disable = array( - 'Html', - 'Include', - 'Embed' - ); - - - /** - * - * Custom configuration for rules at the parsing stage. - * - * In this array, the key is the parsing rule name, and the value is - * an array of key-value configuration pairs corresponding to the $conf - * property in the target parsing rule. - * - * For example: - * - * - * $parseConf = array( - * 'Include' => array( - * 'base' => '/path/to/scripts/' - * ) - * ); - * - * - * Note that most default rules do not need any parsing configuration. - * - * @access public - * - * @var array - * - */ - - var $parseConf = array(); - - - /** - * - * Custom configuration for rules at the rendering stage. - * - * Because rendering may be different for each target format, the - * first-level element in this array is always a format name (e.g., - * 'Xhtml'). - * - * Within that first level element, the subsequent elements match the - * $parseConf format. That is, the sub-key is the rendering rule name, - * and the sub-value is an array of key-value configuration pairs - * corresponding to the $conf property in the target rendering rule. - * - * @access public - * - * @var array - * - */ - - var $renderConf = array( - 'Docbook' => array(), - 'Latex' => array(), - 'Pdf' => array(), - 'Plain' => array(), - 'Rtf' => array(), - 'Xhtml' => array() - ); - - - /** - * - * Custom configuration for the output format itself. - * - * Even though Text_Wiki will render the tokens from parsed text, - * the format itself may require some configuration. For example, - * RTF needs to know font names and sizes, PDF requires page layout - * information, and DocBook needs a section hierarchy. This array - * matches the $conf property of the the format-level renderer - * (e.g., Text_Wiki_Render_Xhtml). - * - * In this array, the key is the rendering format name, and the value is - * an array of key-value configuration pairs corresponding to the $conf - * property in the rendering format rule. - * - * @access public - * - * @var array - * - */ - - var $formatConf = array( - 'Docbook' => array(), - 'Latex' => array(), - 'Pdf' => array(), - 'Plain' => array(), - 'Rtf' => array(), - 'Xhtml' => array() - ); - - - /** - * - * The delimiter for token numbers of parsed elements in source text. - * - * @access public - * - * @var string - * - */ - - var $delim = "\31"; - - - /** - * - * The tokens generated by rules as the source text is parsed. - * - * As Text_Wiki applies rule classes to the source text, it will - * replace portions of the text with a delimited token number. This - * is the array of those tokens, representing the replaced text and - * any options set by the parser for that replaced text. - * - * The tokens array is sequential; each element is itself a sequential - * array where element 0 is the name of the rule that generated the - * token, and element 1 is an associative array where the key is an - * option name and the value is an option value. - * - * @access private - * - * @var array - * - */ - - var $tokens = array(); - - /** - * How many tokens generated pro rules. - * - * Intended to load only necessary render objects - * - * @access private - * @var array - */ - var $_countRulesTokens = array(); - - - /** - * - * The source text to which rules will be applied. - * - * This text will be transformed in-place, which means that it will - * change as the rules are applied. - * - * @access private - * - * @var string - * - */ - - var $source = ''; - - /** - * The output text - * - * @var string - */ - var $output = ''; - - - /** - * - * Array of rule parsers. - * - * Text_Wiki creates one instance of every rule that is applied to - * the source text; this array holds those instances. The array key - * is the rule name, and the array value is an instance of the rule - * class. - * - * @access private - * - * @var array - * - */ - - var $parseObj = array(); - - - /** - * - * Array of rule renderers. - * - * Text_Wiki creates one instance of every rule that is applied to - * the source text; this array holds those instances. The array key - * is the rule name, and the array value is an instance of the rule - * class. - * - * @access private - * - * @var array - * - */ - - var $renderObj = array(); - - - /** - * - * Array of format renderers. - * - * @access private - * - * @var array - * - */ - - var $formatObj = array(); - - - /** - * - * Array of paths to search, in order, for parsing and rendering rules. - * - * @access private - * - * @var array - * - */ - - var $path = array( - 'parse' => array(), - 'render' => array() - ); - - - - /** - * - * The directory separator character. - * - * @access private - * - * @var string - * - */ - - var $_dirSep = DIRECTORY_SEPARATOR; - - /** - * Temporary configuration variable - * - * @var string - */ - var $renderingType = 'normal'; - - /** - * Stack of rendering callbacks - * - * @var Array - */ - var $_renderCallbacks = array(); - - /** - * Current output block - * - * @var string - */ - var $_block; - - /** - * A stack of blocks - * - * @param Array - */ - var $_blocks; - - /** - * - * Constructor. - * - * **DEPRECATED** - * Please use the singleton() or factory() methods. - * - * @access public - * - * @param array $rules The set of rules to load for this object. Defaults - * to null, which will load the default ruleset for this parser. - */ - - function Text_Wiki($rules = null) - { - if (is_array($rules)) { - $this->rules = array(); - foreach ($rules as $rule) { - $this->rules[] = ucfirst($rule); - } - } - - $this->addPath( - 'parse', - $this->fixPath(dirname(__FILE__)) . 'Wiki/Parse/Default/' - ); - $this->addPath( - 'render', - $this->fixPath(dirname(__FILE__)) . 'Wiki/Render/' - ); - - } - - /** - * Singleton. - * - * This avoids instantiating multiple Text_Wiki instances where a number - * of objects are required in one call, e.g. to save memory in a - * CMS invironment where several parsers are required in a single page. - * - * $single = & singleton(); - * - * or - * - * $single = & singleton('Parser', array('Prefilter', 'Delimiter', 'Code', 'Function', - * 'Html', 'Raw', 'Include', 'Embed', 'Anchor', 'Heading', 'Toc', 'Horiz', - * 'Break', 'Blockquote', 'List', 'Deflist', 'Table', 'Image', 'Phplookup', - * 'Center', 'Newline', 'Paragraph', 'Url', 'Freelink', 'Interwiki', 'Wikilink', - * 'Colortext', 'Strong', 'Bold', 'Emphasis', 'Italic', 'Underline', 'Tt', - * 'Superscript', 'Subscript', 'Revise', 'Tighten')); - * - * Call using a subset of this list. The order of passing rulesets in the - * $rules array is important! - * - * After calling this, call $single->setParseConf(), setRenderConf() or setFormatConf() - * as usual for a constructed object of this class. - * - * The internal static array of singleton objects has no index on the parser - * rules, the only index is on the parser name. So if you call this multiple - * times with different rules but the same parser name, you will get the same - * static parser object each time. - * - * @access public - * @static - * @since Method available since Release 1.1.0 - * @param string $parser The parser to be used (defaults to 'Default'). - * @param array $rules The set of rules to instantiate the object. This - * will only be used when the first call to singleton is made, if included - * in further calls it will be effectively ignored. - * @return &object a reference to the Text_Wiki unique instantiation. - */ - function &singleton($parser = 'Default', $rules = null) - { - static $only = array(); - if (!isset($only[$parser])) { - $ret = & Text_Wiki::factory($parser, $rules); - if (Text_Wiki::isError($ret)) { - return $ret; - } - $only[$parser] =& $ret; - } - return $only[$parser]; - } - - /** - * Returns a Text_Wiki Parser class for the specified parser. - * - * @access public - * @static - * @param string $parser The name of the parse to instantiate - * you need to have Text_Wiki_XXX installed to use $parser = 'XXX', it's E_FATAL - * @param array $rules The rules to pass into the constructor - * {@see Text_Wiki::singleton} for a list of rules - * @return Text_Wiki a Parser object extended from Text_Wiki - */ - function &factory($parser = 'Default', $rules = null) - { - $class = 'Text_Wiki_' . $parser; - $file = str_replace('_', '/', $class).'.php'; - if (!class_exists($class)) { - require_once $file; - if (!class_exists($class)) { - return Text_Wiki::error( - 'Class ' . $class . ' does not exist after requiring '. $file . - ', install package ' . $class . "\n"); - } - } - - $obj =& new $class($rules); - return $obj; - } - - /** - * - * Set parser configuration for a specific rule and key. - * - * @access public - * - * @param string $rule The parse rule to set config for. - * - * @param array|string $arg1 The full config array to use for the - * parse rule, or a conf key in that array. - * - * @param string $arg2 The config value for the key. - * - * @return void - * - */ - - function setParseConf($rule, $arg1, $arg2 = null) - { - $rule = ucwords(strtolower($rule)); - - if (! isset($this->parseConf[$rule])) { - $this->parseConf[$rule] = array(); - } - - // if first arg is an array, use it as the entire - // conf array for the rule. otherwise, treat arg1 - // as a key and arg2 as a value for the rule conf. - if (is_array($arg1)) { - $this->parseConf[$rule] = $arg1; - } else { - $this->parseConf[$rule][$arg1] = $arg2; - } - } - - - /** - * - * Get parser configuration for a specific rule and key. - * - * @access public - * - * @param string $rule The parse rule to get config for. - * - * @param string $key A key in the conf array; if null, - * returns the entire conf array. - * - * @return mixed The whole conf array if no key is specified, - * or the specific conf key value. - * - */ - - function getParseConf($rule, $key = null) - { - $rule = ucwords(strtolower($rule)); - - // the rule does not exist - if (! isset($this->parseConf[$rule])) { - return null; - } - - // no key requested, return the whole array - if (is_null($key)) { - return $this->parseConf[$rule]; - } - - // does the requested key exist? - if (isset($this->parseConf[$rule][$key])) { - // yes, return that value - return $this->parseConf[$rule][$key]; - } else { - // no - return null; - } - } - - - /** - * - * Set renderer configuration for a specific format, rule, and key. - * - * @access public - * - * @param string $format The render format to set config for. - * - * @param string $rule The render rule to set config for in the format. - * - * @param array|string $arg1 The config array, or the config key - * within the render rule. - * - * @param string $arg2 The config value for the key. - * - * @return void - * - */ - - function setRenderConf($format, $rule, $arg1, $arg2 = null) - { - $format = ucwords(strtolower($format)); - $rule = ucwords(strtolower($rule)); - - if (! isset($this->renderConf[$format])) { - $this->renderConf[$format] = array(); - } - - if (! isset($this->renderConf[$format][$rule])) { - $this->renderConf[$format][$rule] = array(); - } - - // if first arg is an array, use it as the entire - // conf array for the render rule. otherwise, treat arg1 - // as a key and arg2 as a value for the render rule conf. - if (is_array($arg1)) { - $this->renderConf[$format][$rule] = $arg1; - } else { - $this->renderConf[$format][$rule][$arg1] = $arg2; - } - } - - - /** - * - * Get renderer configuration for a specific format, rule, and key. - * - * @access public - * - * @param string $format The render format to get config for. - * - * @param string $rule The render format rule to get config for. - * - * @param string $key A key in the conf array; if null, - * returns the entire conf array. - * - * @return mixed The whole conf array if no key is specified, - * or the specific conf key value. - * - */ - - function getRenderConf($format, $rule, $key = null) - { - $format = ucwords(strtolower($format)); - $rule = ucwords(strtolower($rule)); - - if (! isset($this->renderConf[$format]) || - ! isset($this->renderConf[$format][$rule])) { - return null; - } - - // no key requested, return the whole array - if (is_null($key)) { - return $this->renderConf[$format][$rule]; - } - - // does the requested key exist? - if (isset($this->renderConf[$format][$rule][$key])) { - // yes, return that value - return $this->renderConf[$format][$rule][$key]; - } else { - // no - return null; - } - - } - - /** - * - * Set format configuration for a specific rule and key. - * - * @access public - * - * @param string $format The format to set config for. - * - * @param string $key The config key within the format. - * - * @param string $val The config value for the key. - * - * @return void - * - */ - - function setFormatConf($format, $arg1, $arg2 = null) - { - if (! is_array($this->formatConf[$format])) { - $this->formatConf[$format] = array(); - } - - // if first arg is an array, use it as the entire - // conf array for the format. otherwise, treat arg1 - // as a key and arg2 as a value for the format conf. - if (is_array($arg1)) { - $this->formatConf[$format] = $arg1; - } else { - $this->formatConf[$format][$arg1] = $arg2; - } - } - - - - /** - * - * Get configuration for a specific format and key. - * - * @access public - * - * @param string $format The format to get config for. - * - * @param mixed $key A key in the conf array; if null, - * returns the entire conf array. - * - * @return mixed The whole conf array if no key is specified, - * or the specific conf key value. - * - */ - - function getFormatConf($format, $key = null) - { - // the format does not exist - if (! isset($this->formatConf[$format])) { - return null; - } - - // no key requested, return the whole array - if (is_null($key)) { - return $this->formatConf[$format]; - } - - // does the requested key exist? - if (isset($this->formatConf[$format][$key])) { - // yes, return that value - return $this->formatConf[$format][$key]; - } else { - // no - return null; - } - } - - - /** - * - * Inserts a rule into to the rule set. - * - * @access public - * - * @param string $name The name of the rule. Should be different from - * all other keys in the rule set. - * - * @param string $tgt The rule after which to insert this new rule. By - * default (null) the rule is inserted at the end; if set to '', inserts - * at the beginning. - * - * @return void - * - */ - - function insertRule($name, $tgt = null) - { - $name = ucwords(strtolower($name)); - if (! is_null($tgt)) { - $tgt = ucwords(strtolower($tgt)); - } - - // does the rule name to be inserted already exist? - if (in_array($name, $this->rules)) { - // yes, return - return null; - } - - // the target name is not null, and not '', but does not exist - // in the list of rules. this means we're trying to insert after - // a target key, but the target key isn't there. - if (! is_null($tgt) && $tgt != '' && - ! in_array($tgt, $this->rules)) { - return false; - } - - // if $tgt is null, insert at the end. We know this is at the - // end (instead of resetting an existing rule) becuase we exited - // at the top of this method if the rule was already in place. - if (is_null($tgt)) { - $this->rules[] = $name; - return true; - } - - // save a copy of the current rules, then reset the rule set - // so we can insert in the proper place later. - // where to insert the rule? - if ($tgt == '') { - // insert at the beginning - array_unshift($this->rules, $name); - return true; - } - - // insert after the named rule - $tmp = $this->rules; - $this->rules = array(); - - foreach ($tmp as $val) { - $this->rules[] = $val; - if ($val == $tgt) { - $this->rules[] = $name; - } - } - - return true; - - } - - - /** - * - * Delete (remove or unset) a rule from the $rules property. - * - * @access public - * - * @param string $rule The name of the rule to remove. - * - * @return void - * - */ - - function deleteRule($name) - { - $name = ucwords(strtolower($name)); - $key = array_search($name, $this->rules); - if ($key !== false) { - unset($this->rules[$key]); - } - } - - - /** - * - * Change from one rule to another in-place. - * - * @access public - * - * @param string $old The name of the rule to change from. - * - * @param string $new The name of the rule to change to. - * - * @return void - * - */ - - function changeRule($old, $new) - { - $old = ucwords(strtolower($old)); - $new = ucwords(strtolower($new)); - $key = array_search($old, $this->rules); - if ($key !== false) { - // delete the new name , case it was already there - $this->deleteRule($new); - $this->rules[$key] = $new; - } - } - - - /** - * - * Enables a rule so that it is applied when parsing. - * - * @access public - * - * @param string $rule The name of the rule to enable. - * - * @return void - * - */ - - function enableRule($name) - { - $name = ucwords(strtolower($name)); - $key = array_search($name, $this->disable); - if ($key !== false) { - unset($this->disable[$key]); - } - } - - - /** - * - * Disables a rule so that it is not applied when parsing. - * - * @access public - * - * @param string $rule The name of the rule to disable. - * - * @return void - * - */ - - function disableRule($name) - { - $name = ucwords(strtolower($name)); - $key = array_search($name, $this->disable); - if ($key === false) { - $this->disable[] = $name; - } - } - - - /** - * - * Parses and renders the text passed to it, and returns the results. - * - * First, the method parses the source text, applying rules to the - * text as it goes. These rules will modify the source text - * in-place, replacing some text with delimited tokens (and - * populating the $this->tokens array as it goes). - * - * Next, the method renders the in-place tokens into the requested - * output format. - * - * Finally, the method returns the transformed text. Note that the - * source text is transformed in place; once it is transformed, it is - * no longer the same as the original source text. - * - * @access public - * - * @param string $text The source text to which wiki rules should be - * applied, both for parsing and for rendering. - * - * @param string $format The target output format, typically 'xhtml'. - * If a rule does not support a given format, the output from that - * rule is rule-specific. - * - * @return string The transformed wiki text. - * - */ - - function transform($text, $format = 'Xhtml') - { - $this->parse($text); - return $this->render($format); - } - - - /** - * - * Sets the $_source text property, then parses it in place and - * retains tokens in the $_tokens array property. - * - * @access public - * - * @param string $text The source text to which wiki rules should be - * applied, both for parsing and for rendering. - * - * @return void - * - */ - - function parse($text) - { - // set the object property for the source text - $this->source = $text; - - // reset the tokens. - $this->tokens = array(); - $this->_countRulesTokens = array(); - - // apply the parse() method of each requested rule to the source - // text. - foreach ($this->rules as $name) { - // do not parse the rules listed in $disable - if (! in_array($name, $this->disable)) { - - // load the parsing object - $this->loadParseObj($name); - - // load may have failed; only parse if - // an object is in the array now - if (is_object($this->parseObj[$name])) { - $this->parseObj[$name]->parse(); - } - } - } - } - - - /** - * - * Renders tokens back into the source text, based on the requested format. - * - * @access public - * - * @param string $format The target output format, typically 'xhtml'. - * If a rule does not support a given format, the output from that - * rule is rule-specific. - * - * @return string The transformed wiki text. - * - */ - - function render($format = 'Xhtml') - { - // the rendering method we're going to use from each rule - $format = ucwords(strtolower($format)); - - // the eventual output text - $this->output = ''; - - // when passing through the parsed source text, keep track of when - // we are in a delimited section - $in_delim = false; - - // when in a delimited section, capture the token key number - $key = ''; - - // load the format object, or crap out if we can't find it - $result = $this->loadFormatObj($format); - if ($this->isError($result)) { - return $result; - } - - // pre-rendering activity - if (is_object($this->formatObj[$format])) { - $this->output .= $this->formatObj[$format]->pre(); - } - - // load the render objects - foreach (array_keys($this->_countRulesTokens) as $rule) { - $this->loadRenderObj($format, $rule); - } - - if ($this->renderingType == 'preg') { - $this->output = preg_replace_callback('/'.$this->delim.'(\d+)'.$this->delim.'/', - array(&$this, '_renderToken'), - $this->source); - /* -//Damn strtok()! Why does it "skip" empty parts of the string. It's useless now! - } elseif ($this->renderingType == 'strtok') { - echo '
    '.htmlentities($this->source).'
    '; - $t = strtok($this->source, $this->delim); - $inToken = true; - $i = 0; - while ($t !== false) { - echo 'Token: '.$i.'
    "'.htmlentities($t).'"


    '; - if ($inToken) { - //$this->output .= $this->renderObj[$this->tokens[$t][0]]->token($this->tokens[$t][1]); - } else { - $this->output .= $t; - } - $inToken = !$inToken; - $t = strtok($this->delim); - ++$i; - } - */ - } else { - // pass through the parsed source text character by character - $this->_block = ''; - $tokenStack = array(); - $k = strlen($this->source); - for ($i = 0; $i < $k; $i++) { - - // the current character - $char = $this->source{$i}; - - // are alredy in a delimited section? - if ($in_delim) { - - // yes; are we ending the section? - if ($char == $this->delim) { - - if (count($this->_renderCallbacks) == 0) { - $this->output .= $this->_block; - $this->_block = ''; - } - if (isset($opts['type'])) { - if ($opts['type'] == 'start') { - array_push($tokenStack, $rule); - } elseif ($opts['type'] == 'end') { - if ($tokenStack[count($tokenStack) - 1] != $rule) { - return Text_Wiki::error('Unbalanced tokens, check your syntax'); - } else { - array_pop($tokenStack); - } - } - } - - // yes, get the replacement text for the delimited - // token number and unset the flag. - $key = (int)$key; - $rule = $this->tokens[$key][0]; - $opts = $this->tokens[$key][1]; - $this->_block .= $this->renderObj[$rule]->token($opts); - $in_delim = false; - - } else { - - // no, add to the delimited token key number - $key .= $char; - - } - - } else { - - // not currently in a delimited section. - // are we starting into a delimited section? - if ($char == $this->delim) { - // yes, reset the previous key and - // set the flag. - $key = ''; - $in_delim = true; - - } else { - // no, add to the output as-is - $this->_block .= $char; - } - } - } - } - - if (count($this->_renderCallbacks)) { - return $this->error('Render callbacks left over after processing finished'); - } - /* - while (count($this->_renderCallbacks)) { - $this->popRenderCallback(); - } - */ - if (strlen($this->_block)) { - $this->output .= $this->_block; - $this->_block = ''; - } - - // post-rendering activity - if (is_object($this->formatObj[$format])) { - $this->output .= $this->formatObj[$format]->post(); - } - - // return the rendered source text. - return $this->output; - } - - /** - * Renders a token, for use only as an internal callback - * - * @param array Matches from preg_rpelace_callback, [1] is the token number - * @return string The rendered text for the token - * @access private - */ - function _renderToken($matches) { - return $this->renderObj[$this->tokens[$matches[1]][0]]->token($this->tokens[$matches[1]][1]); - } - - function registerRenderCallback($callback) { - $this->_blocks[] = $this->_block; - $this->_block = ''; - $this->_renderCallbacks[] = $callback; - } - - function popRenderCallback() { - if (count($this->_renderCallbacks) == 0) { - return Text_Wiki::error('Render callback popped when no render callbacks in stack'); - } else { - $callback = array_pop($this->_renderCallbacks); - $this->_block = call_user_func($callback, $this->_block); - if (count($this->_blocks)) { - $parentBlock = array_pop($this->_blocks); - $this->_block = $parentBlock.$this->_block; - } - if (count($this->_renderCallbacks) == 0) { - $this->output .= $this->_block; - $this->_block = ''; - } - } - } - - /** - * - * Returns the parsed source text with delimited token placeholders. - * - * @access public - * - * @return string The parsed source text. - * - */ - - function getSource() - { - return $this->source; - } - - - /** - * - * Returns tokens that have been parsed out of the source text. - * - * @access public - * - * @param array $rules If an array of rule names is passed, only return - * tokens matching these rule names. If no array is passed, return all - * tokens. - * - * @return array An array of tokens. - * - */ - - function getTokens($rules = null) - { - if (is_null($rules)) { - return $this->tokens; - } else { - settype($rules, 'array'); - $result = array(); - foreach ($this->tokens as $key => $val) { - if (in_array($val[0], $rules)) { - $result[$key] = $val; - } - } - return $result; - } - } - - - /** - * - * Add a token to the Text_Wiki tokens array, and return a delimited - * token number. - * - * @access public - * - * @param array $options An associative array of options for the new - * token array element. The keys and values are specific to the - * rule, and may or may not be common to other rule options. Typical - * options keys are 'text' and 'type' but may include others. - * - * @param boolean $id_only If true, return only the token number, not - * a delimited token string. - * - * @return string|int By default, return the number of the - * newly-created token array element with a delimiter prefix and - * suffix; however, if $id_only is set to true, return only the token - * number (no delimiters). - * - */ - - function addToken($rule, $options = array(), $id_only = false) - { - // increment the token ID number. note that if you parse - // multiple times with the same Text_Wiki object, the ID number - // will not reset to zero. - static $id; - if (! isset($id)) { - $id = 0; - } else { - $id ++; - } - - // force the options to be an array - settype($options, 'array'); - - // add the token - $this->tokens[$id] = array( - 0 => $rule, - 1 => $options - ); - if (!isset($this->_countRulesTokens[$rule])) { - $this->_countRulesTokens[$rule] = 1; - } else { - ++$this->_countRulesTokens[$rule]; - } - - // return a value - if ($id_only) { - // return the last token number - return $id; - } else { - // return the token number with delimiters - return $this->delim . $id . $this->delim; - } - } - - - /** - * - * Set or re-set a token with specific information, overwriting any - * previous rule name and rule options. - * - * @access public - * - * @param int $id The token number to reset. - * - * @param int $rule The rule name to use. - * - * @param array $options An associative array of options for the - * token array element. The keys and values are specific to the - * rule, and may or may not be common to other rule options. Typical - * options keys are 'text' and 'type' but may include others. - * - * @return void - * - */ - - function setToken($id, $rule, $options = array()) - { - $oldRule = $this->tokens[$id][0]; - // reset the token - $this->tokens[$id] = array( - 0 => $rule, - 1 => $options - ); - if ($rule != $oldRule) { - if (!($this->_countRulesTokens[$oldRule]--)) { - unset($this->_countRulesTokens[$oldRule]); - } - if (!isset($this->_countRulesTokens[$rule])) { - $this->_countRulesTokens[$rule] = 1; - } else { - ++$this->_countRulesTokens[$rule]; - } - } - } - - - /** - * - * Load a rule parser class file. - * - * @access public - * - * @return bool True if loaded, false if not. - * - */ - - function loadParseObj($rule) - { - $rule = ucwords(strtolower($rule)); - $file = $rule . '.php'; - $class = "Text_Wiki_Parse_$rule"; - - if (! class_exists($class)) { - $loc = $this->findFile('parse', $file); - if ($loc) { - // found the class - include_once $loc; - } else { - // can't find the class - $this->parseObj[$rule] = null; - // can't find the class - return $this->error( - "Parse rule '$rule' not found" - ); - } - } - - $this->parseObj[$rule] =& new $class($this); - - } - - - /** - * - * Load a rule-render class file. - * - * @access public - * - * @return bool True if loaded, false if not. - * - */ - - function loadRenderObj($format, $rule) - { - $format = ucwords(strtolower($format)); - $rule = ucwords(strtolower($rule)); - $file = "$format/$rule.php"; - $class = "Text_Wiki_Render_$format" . "_$rule"; - - if (! class_exists($class)) { - // load the class - $loc = $this->findFile('render', $file); - if ($loc) { - // found the class - include_once $loc; - } else { - // can't find the class - return $this->error( - "Render rule '$rule' in format '$format' not found" - ); - } - } - - $this->renderObj[$rule] =& new $class($this); - } - - - /** - * - * Load a format-render class file. - * - * @access public - * - * @return bool True if loaded, false if not. - * - */ - - function loadFormatObj($format) - { - $format = ucwords(strtolower($format)); - $file = $format . '.php'; - $class = "Text_Wiki_Render_$format"; - - if (! class_exists($class)) { - $loc = $this->findFile('render', $file); - if ($loc) { - // found the class - include_once $loc; - } else { - // can't find the class - return $this->error( - "Rendering format class '$class' not found" - ); - } - } - - $this->formatObj[$format] =& new $class($this); - } - - - /** - * - * Add a path to a path array. - * - * @access public - * - * @param string $type The path-type to add (parse or render). - * - * @param string $dir The directory to add to the path-type. - * - * @return void - * - */ - - function addPath($type, $dir) - { - $dir = $this->fixPath($dir); - if (! isset($this->path[$type])) { - $this->path[$type] = array($dir); - } else { - array_unshift($this->path[$type], $dir); - } - } - - - /** - * - * Get the current path array for a path-type. - * - * @access public - * - * @param string $type The path-type to look up (plugin, filter, or - * template). If not set, returns all path types. - * - * @return array The array of paths for the requested type. - * - */ - - function getPath($type = null) - { - if (is_null($type)) { - return $this->path; - } elseif (! isset($this->path[$type])) { - return array(); - } else { - return $this->path[$type]; - } - } - - - /** - * - * Searches a series of paths for a given file. - * - * @param array $type The type of paths to search (template, plugin, - * or filter). - * - * @param string $file The file name to look for. - * - * @return string|bool The full path and file name for the target file, - * or boolean false if the file is not found in any of the paths. - * - */ - - function findFile($type, $file) - { - // get the set of paths - $set = $this->getPath($type); - - // start looping through them - foreach ($set as $path) { - $fullname = $path . $file; - if (file_exists($fullname) && is_readable($fullname)) { - return $fullname; - } - } - - // could not find the file in the set of paths - return false; - } - - - /** - * - * Append a trailing '/' to paths, unless the path is empty. - * - * @access private - * - * @param string $path The file path to fix - * - * @return string The fixed file path - * - */ - - function fixPath($path) - { - $len = strlen($this->_dirSep); - - if (! empty($path) && - substr($path, -1 * $len, $len) != $this->_dirSep) { - return $path . $this->_dirSep; - } else { - return $path; - } - } - - - /** - * - * Simple error-object generator. - * - * @access public - * - * @param string $message The error message. - * - * @return object PEAR_Error - * - */ - - function &error($message) - { - if (! class_exists('PEAR_Error')) { - include_once 'PEAR.php'; - } - return PEAR::throwError($message); - } - - - /** - * - * Simple error checker. - * - * @access public - * - * @param mixed $obj Check if this is a PEAR_Error object or not. - * - * @return bool True if a PEAR_Error, false if not. - * - */ - - function isError(&$obj) - { - return is_a($obj, 'PEAR_Error'); - } -} - -?> diff --git a/pear/Text/Wiki/Creole.php b/pear/Text/Wiki/Creole.php deleted file mode 100644 index 255eaac..0000000 --- a/pear/Text/Wiki/Creole.php +++ /dev/null @@ -1,150 +0,0 @@ - - * - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * - * @link http://pear.php.net/package/Text_Wiki - * - * @version CVS: $Id: Creole.php 274202 2009-01-22 13:28:32Z mic $ - * - */ - -/** - * - * "Master" class for handling the management and convenience - * - */ - -require_once 'Text/Wiki.php'; - -/** - * - * Base Text_Wiki handler class extension for Creole markup - * - * @category Text - * - * @package Text_Wiki - * - * @author Michele Tomaiuolo - * - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * - * @link http://pear.php.net/package/Text_Wiki - * - * @see Text_Wiki::Text_Wiki() - * - */ - -class Text_Wiki_Creole extends Text_Wiki { - - // *single newlines* are handled as in most wikis (ignored) - // if Newline is removed from rules, they will be handled as in word-processors (meaning a paragraph break) - - var $rules = array( - 'Prefilter', - 'Delimiter', - 'Preformatted', - 'Tt', - 'Trim', - 'Break', - 'Raw', - 'Box', - 'Footnote', - 'Heading', - 'Newline', - 'Deflist', - 'Blockquote', - 'Newline', - 'Url', - 'Wikilink', - 'Image', - //'Heading', - 'Table', - 'Center', - 'Horiz', - 'Deflist', - 'List', - 'Address', - 'Paragraph', - 'Superscript', - 'Subscript', - 'Underline', - 'Emphasis', - 'Strong', - //'Italic', - //'Bold', - 'Tighten' - ); - - /** - * Constructor: just adds the path to Creole rules - * - * @access public - * @param array $rules The set of rules to load for this object. - */ - - function Text_Wiki_Creole($rules = null) { - parent::Text_Wiki($rules); - $this->addPath('parse', $this->fixPath(dirname(__FILE__)).'Parse/Creole'); - $this->renderingType = 'char'; - $this->setRenderConf('xhtml', 'center', 'css', 'center'); - $this->setRenderConf('xhtml', 'url', 'target', null); - } - - function checkInnerTags(&$text) { - $started = array(); - $i = false; - while (($i = strpos($text, $this->delim, $i)) !== false) { - $j = strpos($text, $this->delim, $i + 1); - $t = substr($text, $i + 1, $j - $i - 1); - $i = $j + 1; - $rule = strtolower($this->tokens[$t][0]); - $type = $this->tokens[$t][1]['type']; - - if ($type == 'start') { - if (empty($started[$rule])) { - $started[$rule] = 0; - } - $started[$rule] += 1; - } - else if ($type == 'end') { - if (empty($started[$rule])) return false; - - $started[$rule] -= 1; - if (! $started[$rule]) unset($started[$rule]); - } - } - return ! (count($started) > 0); - } - - function restoreRaw($text) { - $i = false; - while (($i = strpos($text, $this->delim, $i)) !== false) { - $j = strpos($text, $this->delim, $i + 1); - $t = substr($text, $i + 1, $j - $i - 1); - $rule = strtolower($this->tokens[$t][0]); - - if ($rule == 'raw') { - $text = str_replace($this->delim. $t. $this->delim, $this->tokens[$t][1]['text'], $text); - } - else { - $i = $j + 1; - } - } - return $text; - } -} - -?> diff --git a/pear/Text/Wiki/Default.php b/pear/Text/Wiki/Default.php deleted file mode 100644 index 379cf12..0000000 --- a/pear/Text/Wiki/Default.php +++ /dev/null @@ -1,27 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version CVS: $Id: Default.php 208363 2006-03-01 16:58:17Z justinpatrin $ - * @link http://pear.php.net/package/Text_Wiki - */ - -require_once('Text/Wiki.php'); - -/** - * This is the parser for the Default ruleset. For now, this simply extends Text_Wiki. - * - * @category Text - * @package Text_Wiki - * @version Release: @package_version@ - * @author Justin Patrin - */ -class Text_Wiki_Default extends Text_Wiki { -} diff --git a/pear/Text/Wiki/Parse.php b/pear/Text/Wiki/Parse.php deleted file mode 100644 index 1b14c89..0000000 --- a/pear/Text/Wiki/Parse.php +++ /dev/null @@ -1,264 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version CVS: $Id: Parse.php 191781 2005-07-29 08:57:29Z toggg $ - * @link http://pear.php.net/package/Text_Wiki - */ - -/** - * Baseline rule class for extension into a "real" parser component. - * - * Text_Wiki_Rule classes do not stand on their own; they are called by a - * Text_Wiki object, typcially in the transform() method. Each rule class - * performs three main activities: parse, process, and render. - * - * The parse() method takes a regex and applies it to the whole block of - * source text at one time. Each match is sent as $matches to the - * process() method. - * - * The process() method acts on the matched text from the source, and - * then processes the source text is some way. This may mean the - * creation of a delimited token using addToken(). In every case, the - * process() method returns the text that should replace the matched text - * from parse(). - * - * @category Text - * @package Text_Wiki - * @author Paul M. Jones - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version Release: @package_version@ - * @link http://pear.php.net/package/Text_Wiki - */ -class Text_Wiki_Parse { - - - /** - * - * Configuration options for this parser rule. - * - * @access public - * - * @var string - * - */ - - var $conf = array(); - - - /** - * - * Regular expression to find matching text for this rule. - * - * @access public - * - * @var string - * - * @see parse() - * - */ - - var $regex = null; - - - /** - * - * The name of this rule for new token array elements. - * - * @access public - * - * @var string - * - */ - - var $rule = null; - - - /** - * - * A reference to the calling Text_Wiki object. - * - * This is needed so that each rule has access to the same source - * text, token set, URLs, interwiki maps, page names, etc. - * - * @access public - * - * @var object - */ - - var $wiki = null; - - - /** - * - * Constructor for this parser rule. - * - * @access public - * - * @param object &$obj The calling "parent" Text_Wiki object. - * - */ - - function Text_Wiki_Parse(&$obj) - { - // set the reference to the calling Text_Wiki object; - // this allows us access to the shared source text, token - // array, etc. - $this->wiki =& $obj; - - // set the name of this rule; generally used when adding - // to the tokens array. strip off the Text_Wiki_Parse_ portion. - // text_wiki_parse_ - // 0123456789012345 - $tmp = substr(get_class($this), 16); - $this->rule = ucwords(strtolower($tmp)); - - // override config options for the rule if specified - if (isset($this->wiki->parseConf[$this->rule]) && - is_array($this->wiki->parseConf[$this->rule])) { - - $this->conf = array_merge( - $this->conf, - $this->wiki->parseConf[$this->rule] - ); - - } - } - - - /** - * - * Abstrct method to parse source text for matches. - * - * Applies the rule's regular expression to the source text, passes - * every match to the process() method, and replaces the matched text - * with the results of the processing. - * - * @access public - * - * @see Text_Wiki_Parse::process() - * - */ - - function parse() - { - $this->wiki->source = preg_replace_callback( - $this->regex, - array(&$this, 'process'), - $this->wiki->source - ); - } - - - /** - * - * Abstract method to generate replacements for matched text. - * - * @access public - * - * @param array $matches An array of matches from the parse() method - * as generated by preg_replace_callback. $matches[0] is the full - * matched string, $matches[1] is the first matched pattern, - * $matches[2] is the second matched pattern, and so on. - * - * @return string The processed text replacement; defaults to the - * full matched string (i.e., no changes to the text). - * - * @see Text_Wiki_Parse::parse() - * - */ - - function process(&$matches) - { - return $matches[0]; - } - - - /** - * - * Simple method to safely get configuration key values. - * - * @access public - * - * @param string $key The configuration key. - * - * @param mixed $default If the key does not exist, return this value - * instead. - * - * @return mixed The configuration key value (if it exists) or the - * default value (if not). - * - */ - - function getConf($key, $default = null) - { - if (isset($this->conf[$key])) { - return $this->conf[$key]; - } else { - return $default; - } - } - - - /** - * - * Extract 'attribute="value"' portions of wiki markup. - * - * This kind of markup is typically used only in macros, but is useful - * anywhere. - * - * The syntax is pretty strict; there can be no spaces between the - * option name, the equals, and the first double-quote; the value - * must be surrounded by double-quotes. You can escape characters in - * the value with a backslash, and the backslash will be stripped for - * you. - * - * @access public - * - * @param string $text The "attributes" portion of markup. - * - * @return array An associative array of key-value pairs where the - * key is the option name and the value is the option value. - * - */ - - function getAttrs($text) - { - // find the =" sections; - $tmp = explode('="', trim($text)); - - // basic setup - $k = count($tmp) - 1; - $attrs = array(); - $key = null; - - // loop through the sections - foreach ($tmp as $i => $val) { - - // first element is always the first key - if ($i == 0) { - $key = trim($val); - continue; - } - - // find the last double-quote in the value. - // the part to the left is the value for the last key, - // the part to the right is the next key name - $pos = strrpos($val, '"'); - $attrs[$key] = stripslashes(substr($val, 0, $pos)); - $key = trim(substr($val, $pos+1)); - - } - - return $attrs; - - } -} -?> diff --git a/pear/Text/Wiki/Parse/Creole/Address.php b/pear/Text/Wiki/Parse/Creole/Address.php deleted file mode 100644 index 5e1eca1..0000000 --- a/pear/Text/Wiki/Parse/Creole/Address.php +++ /dev/null @@ -1,67 +0,0 @@ - - * - * @license LGPL - * - * @version $Id: Address.php 222265 2006-10-23 13:11:27Z mic $ - * - */ - -class Text_Wiki_Parse_Address extends Text_Wiki_Parse { - - /** - * - * The regular expression used to find source text matching this - * rule. - * - * @access public - * - * @var string - * - */ - - var $regex = '/^--([^-].*)$/m'; - - /** - * - * Generates a token entry for the matched text. Token options are: - * - * 'start' => The starting point of the signature. - * - * 'end' => The ending point of the signature. - * - * @access public - * - * @param array &$matches The array of matches from parse(). - * - * @return A delimited token number to be used as a placeholder in - * the source text. - * - */ - - function process(&$matches) - { - $start = $this->wiki->addToken( - $this->rule, array('type' => 'start') - ); - - $end = $this->wiki->addToken( - $this->rule, array('type' => 'end') - ); - - return "\n" . $start . trim($matches[1]) . $end; - } -} -?> \ No newline at end of file diff --git a/pear/Text/Wiki/Parse/Creole/Blockquote.php b/pear/Text/Wiki/Parse/Creole/Blockquote.php deleted file mode 100644 index 2ab6c14..0000000 --- a/pear/Text/Wiki/Parse/Creole/Blockquote.php +++ /dev/null @@ -1,176 +0,0 @@ -' at the start of the line, followed by an - * optional space, and then the quote text; each '>' indicates an - * additional level of quoting. - * - * @category Text - * - * @package Text_Wiki - * - * @author Paul M. Jones - * @author Michele Tomaiuolo - * - * @license LGPL - * - * @version $Id: Blockquote.php 230214 2007-02-19 08:57:14Z mic $ - * - */ - -class Text_Wiki_Parse_Blockquote extends Text_Wiki_Parse { - - - /** - * - * Regex for parsing the source text. - * - * @access public - * - * @var string - * - * @see parse() - * - */ - - var $regex = '/\n(([>:]).*\n)(?!([>:]))/Us'; - - - /** - * - * Generates a replacement for the matched text. - * - * Token options are: - * - * 'type' => - * 'start' : the start of a blockquote - * 'end' : the end of a blockquote - * - * 'level' => the indent level (0 for the first level, 1 for the - * second, etc) - * - * @access public - * - * @param array &$matches The array of matches from parse(). - * - * @return A series of text and delimited tokens marking the different - * list text and list elements. - * - */ - - function process(&$matches) - { - // the replacement text we will return to parse() - $return = ''; - - // the list of post-processing matches - $list = array(); - - // $matches[1] is the text matched as a list set by parse(); - // create an array called $list that contains a new set of - // matches for the various list-item elements. - preg_match_all( - '=^([>:]+)(.*?\n)=ms', - $matches[1], - $list, - PREG_SET_ORDER - ); - - // a stack of starts and ends; we keep this so that we know what - // indent level we're at. - $stack = array(); - - // loop through each list-item element. - foreach ($list as $key => $val) { - - // $val[0] is the full matched list-item line - // $val[1] is the number of initial '>' chars (indent level) - // $val[2] is the quote text - - // we number levels starting at 1, not zero - $level = strlen($val[1]); - - // get the text of the line - $text = trim($val[2]); - - // add a level to the list? - while ($level > count($stack)) { - - $css = ($val[1][count($stack)] == ':') ? 'remark' : ''; - - // the current indent level is greater than the number - // of stack elements, so we must be starting a new - // level. push the new level onto the stack with a - // dummy value (boolean true)... - array_push($stack, true); - - $return .= "\n\n"; - - // ...and add a start token to the return. - $return .= $this->wiki->addToken( - $this->rule, - array( - 'type' => 'start', - 'level' => $level - 1, - 'css' => $css - ) - ); - - $return .= "\n\n"; - } - - // remove a level? - while (count($stack) > $level) { - - // as long as the stack count is greater than the - // current indent level, we need to end list types. - // continue adding end-list tokens until the stack count - // and the indent level are the same. - array_pop($stack); - - $return .= "\n\n"; - - $return .= $this->wiki->addToken( - $this->rule, - array ( - 'type' => 'end', - 'level' => count($stack) - ) - ); - - $return .= "\n\n"; - } - - // add the line text. - $return .= $text . "\n"; - } - - // the last line may have been indented. go through the stack - // and create end-tokens until the stack is empty. - $return .= "\n\n"; - - while (count($stack) > 0) { - array_pop($stack); - - $return .= "\n\n"; - - $return .= $this->wiki->addToken( - $this->rule, - array ( - 'type' => 'end', - 'level' => count($stack) - ) - ); - - $return .= "\n\n"; - } - - // we're done! send back the replacement text. - return "\n\n$return\n\n"; - } -} -?> \ No newline at end of file diff --git a/pear/Text/Wiki/Parse/Creole/Box.php b/pear/Text/Wiki/Parse/Creole/Box.php deleted file mode 100644 index 4660a3d..0000000 --- a/pear/Text/Wiki/Parse/Creole/Box.php +++ /dev/null @@ -1,81 +0,0 @@ - -* @author Paul M. Jones -* -* @license LGPL -* -* @version $Id: Box.php 240481 2007-07-30 19:11:32Z mic $ -* -*/ - -/** -* -* Parses for bold text. -* -* This class implements a Text_Wiki_Rule to find source text marked for -* strong emphasis (bold) as defined by text surrounded by three -* single-quotes. On parsing, the text itself is left in place, but the -* starting and ending instances of three single-quotes are replaced with -* tokens. -* -* @category Text -* -* @package Text_Wiki -* -* @author Justin Patrin -* @author Paul M. Jones -* -*/ - -class Text_Wiki_Parse_Box extends Text_Wiki_Parse { - - - /** - * - * The regular expression used to parse the source text and find - * matches conforming to this rule. Used by the parse() method. - * - * @access public - * - * @var string - * - * @see parse() - * - */ - - var $regex = '/\n\[\d+\].*/s'; - - - /** - * - * Generates a replacement for the matched text. Token options are: - * - * 'type' => ['start'|'end'] The starting or ending point of the - * emphasized text. The text itself is left in the source. - * - * @access public - * - * @param array &$matches The array of matches from parse(). - * - * @return A pair of delimited tokens to be used as a placeholder in - * the source text surrounding the text to be emphasized. - * - */ - - function process(&$matches) - { - $start = $this->wiki->addToken($this->rule, array('type' => 'start', 'css' => 'footnotes')); - $end = $this->wiki->addToken($this->rule, array('type' => 'end')); - return $start . $matches[0] . "\n" . $end . "\n\n"; - } -} -?> \ No newline at end of file diff --git a/pear/Text/Wiki/Parse/Creole/Break.php b/pear/Text/Wiki/Parse/Creole/Break.php deleted file mode 100644 index 47d12d9..0000000 --- a/pear/Text/Wiki/Parse/Creole/Break.php +++ /dev/null @@ -1,73 +0,0 @@ - -* -* @license LGPL -* -* @version $Id: Break.php 230561 2007-02-23 14:19:19Z mic $ -* -*/ - -/** -* -* Parses for explicit line breaks. -* -* This class implements a Text_Wiki_Parse to mark forced line breaks in the -* source text. -* -* @category Text -* -* @package Text_Wiki -* -* @author Paul M. Jones -* -*/ - -class Text_Wiki_Parse_Break extends Text_Wiki_Parse { - - - /** - * - * The regular expression used to parse the source text and find - * matches conforming to this rule. Used by the parse() method. - * - * @access public - * - * @var string - * - * @see parse() - * - */ - - //var $regex = "/[ \n]*([\\\][\\\]|\%\%\%)[ \n]*/"; - var $regex = "/ *([\\\][\\\]|\%\%\%)\n?/"; - - - /** - * - * Generates a replacement token for the matched text. - * - * @access public - * - * @param array &$matches The array of matches from parse(). - * - * @return string A delimited token to be used as a placeholder in - * the source text. - * - */ - - function process(&$matches) - { - return $this->wiki->addToken($this->rule); - } -} - -?> \ No newline at end of file diff --git a/pear/Text/Wiki/Parse/Creole/Center.php b/pear/Text/Wiki/Parse/Creole/Center.php deleted file mode 100644 index 3753aca..0000000 --- a/pear/Text/Wiki/Parse/Creole/Center.php +++ /dev/null @@ -1,78 +0,0 @@ - - * - * @license LGPL - * - * @version $Id: Center.php 240476 2007-07-30 14:22:34Z mic $ - * - */ - -class Text_Wiki_Parse_Center extends Text_Wiki_Parse { - - - /** - * - * The regular expression used to parse the source text and find - * matches conforming to this rule. Used by the parse() method. - * - * @access public - * - * @var string - * - * @see parse() - * - */ - - var $regex = '/^! *(.*?)$/m'; - - /** - * - * Generates a replacement for the matched text. Token options are: - * - * 'type' => ['start'|'end'] The starting or ending point of the - * centered text. The text itself is left in the source. - * - * @access public - * - * @param array &$matches The array of matches from parse(). - * - * @return string A pair of delimited tokens to be used as a - * placeholder in the source text surrounding the centered text. - * - */ - - function process(&$matches) - { - $start = $this->wiki->addToken( - $this->rule, - array( - 'type' => 'start' - ) - ); - - $end = $this->wiki->addToken( - $this->rule, - array( - 'type' => 'end' - ) - ); - - return $start . trim($matches[1]) . $end . "\n\n"; - } -} -?> diff --git a/pear/Text/Wiki/Parse/Creole/Delimiter.php b/pear/Text/Wiki/Parse/Creole/Delimiter.php deleted file mode 100644 index c53b78a..0000000 --- a/pear/Text/Wiki/Parse/Creole/Delimiter.php +++ /dev/null @@ -1,68 +0,0 @@ - - * - * @license LGPL - * - * @version $Id: Delimiter.php 222265 2006-10-23 13:11:27Z mic $ - * - */ - -class Text_Wiki_Parse_Delimiter extends Text_Wiki_Parse { - - /** - * - * Constructor. Overrides the Text_Wiki_Parse constructor so that we - * can set the $regex property dynamically (we need to include the - * Text_Wiki $delim character. - * - * @param object &$obj The calling "parent" Text_Wiki object. - * - * @param string $name The token name to use for this rule. - * - */ - - function Text_Wiki_Parse_Delimiter(&$obj) - { - parent::Text_Wiki_Parse($obj); - $this->regex = '/' . $this->wiki->delim . '/'; - } - - - /** - * - * Generates a token entry for the matched text. Token options are: - * - * 'text' => The full matched text. - * - * @access public - * - * @param array &$matches The array of matches from parse(). - * - * @return A delimited token number to be used as a placeholder in - * the source text. - * - */ - - function process(&$matches) - { - return $this->wiki->addToken( - $this->rule, - array('text' => $this->wiki->delim) - ); - } -} -?> \ No newline at end of file diff --git a/pear/Text/Wiki/Parse/Creole/Emphasis.php b/pear/Text/Wiki/Parse/Creole/Emphasis.php deleted file mode 100644 index 979f89e..0000000 --- a/pear/Text/Wiki/Parse/Creole/Emphasis.php +++ /dev/null @@ -1,83 +0,0 @@ - - * @author Michele Tomaiuolo - * - * @license LGPL - * - * @version $Id: Emphasis.php 242125 2007-09-03 21:16:10Z mic $ - * - */ - -class Text_Wiki_Parse_Emphasis extends Text_Wiki_Parse { - - - /** - * - * The regular expression used to parse the source text and find - * matches conforming to this rule. Used by the parse() method. - * - * @access public - * - * @var string - * - * @see parse() - * - */ - - var $regex = "/\/\/(.+?)\/\//"; - //var $regex = "/(?:\/\/(.+?)\/\/|(?:(?<=[\W_\xFF])\/(?![ \/]))(.+?)(?:(? ['start'|'end'] The starting or ending point of the - * emphasized text. The text itself is left in the source. - * - * @access public - * - * @param array &$matches The array of matches from parse(). - * - * @return string A pair of delimited tokens to be used as a - * placeholder in the source text surrounding the text to be - * emphasized. - * - */ - - function process(&$matches) - { - $text = $matches[1]; - //$text = $matches[1]/* ? $matches[1] : $matches[2]*/; - - if (! $this->wiki->checkInnerTags($text)) { - return $matches[0]; - } - - $start = $this->wiki->addToken( - $this->rule, - array('type' => 'start') - ); - - $end = $this->wiki->addToken( - $this->rule, - array('type' => 'end') - ); - - return $start . $text . $end; - } -} -?> \ No newline at end of file diff --git a/pear/Text/Wiki/Parse/Creole/Footnote.php b/pear/Text/Wiki/Parse/Creole/Footnote.php deleted file mode 100644 index 00e4178..0000000 --- a/pear/Text/Wiki/Parse/Creole/Footnote.php +++ /dev/null @@ -1,83 +0,0 @@ - - * @author Michele Tomaiuolo - * - * @license LGPL - * - * @version $Id: Footnote.php 236407 2007-05-26 17:47:24Z mic $ - * - */ - -class Text_Wiki_Parse_Footnote extends Text_Wiki_Parse { - - - /** - * - * The regular expression used to parse the source text and find - * matches conforming to this rule. Used by the parse() method. - * - * @access public - * - * @var string - * - * @see parse() - * - */ - - var $regex = "/(\n)*\[([0-9]+)\]/"; - - - /** - * - * Generates a replacement for the matched text. Token options are: - * - * 'type' => ['start'|'end'] The starting or ending point of the - * emphasized text. The text itself is left in the source. - * - * @access public - * - * @param array &$matches The array of matches from parse(). - * - * @return A pair of delimited tokens to be used as a placeholder in - * the source text surrounding the text to be emphasized. - * - */ - - function process(&$matches) - { - $id = $matches[2]; - - if ($matches[1] == "\n") { - $matches[1] = "\n\n"; - $name = "fn$id"; - $href = "#ref$id"; - } - else { - $name = "ref$id"; - $href = "#fn$id"; - } - - $token = $this->wiki->addToken( - 'Url', - array('text' => "[$id]", 'href' => $href, 'name' => $name, 'type' => 'inline') - ); - - return $matches[1] . $token; - } -} -?> \ No newline at end of file diff --git a/pear/Text/Wiki/Parse/Creole/Heading.php b/pear/Text/Wiki/Parse/Creole/Heading.php deleted file mode 100644 index eb213c9..0000000 --- a/pear/Text/Wiki/Parse/Creole/Heading.php +++ /dev/null @@ -1,97 +0,0 @@ - - * @author Tomaiuolo Michele - * - * @license LGPL - * - * @version $Id: Heading.php 228641 2007-02-01 09:57:36Z mic $ - * - */ - -class Text_Wiki_Parse_Heading extends Text_Wiki_Parse { - - - /** - * - * The regular expression used to parse the source text and find - * matches conforming to this rule. Used by the parse() method. - * - * @access public - * - * @var string - * - * @see parse() - * - */ - - var $regex = '/^(={1,6}) *(.*?) *=*$/m'; - - var $conf = array( - 'id_prefix' => 'toc' - ); - - /** - * - * Generates a replacement for the matched text. Token options are: - * - * 'type' => ['start'|'end'] The starting or ending point of the - * heading text. The text itself is left in the source. - * - * @access public - * - * @param array &$matches The array of matches from parse(). - * - * @return string A pair of delimited tokens to be used as a - * placeholder in the source text surrounding the heading text. - * - */ - - function process(&$matches) - { - // keep a running count for header IDs. we use this later - // when constructing TOC entries, etc. - static $id; - if (! isset($id)) { - $id = 0; - } - - $prefix = htmlspecialchars($this->getConf('id_prefix')); - - $start = $this->wiki->addToken( - $this->rule, - array( - 'type' => 'start', - 'level' => strlen($matches[1]), - 'text' => trim($matches[2]), - 'id' => $prefix . $id ++ - ) - ); - - $end = $this->wiki->addToken( - $this->rule, - array( - 'type' => 'end', - 'level' => strlen($matches[1]) - ) - ); - - return $start . trim($matches[2]) . $end . "\n\n"; - } -} -?> diff --git a/pear/Text/Wiki/Parse/Creole/Horiz.php b/pear/Text/Wiki/Parse/Creole/Horiz.php deleted file mode 100644 index 60c6160..0000000 --- a/pear/Text/Wiki/Parse/Creole/Horiz.php +++ /dev/null @@ -1,58 +0,0 @@ - - * - * @license LGPL - * - * @version $Id: Horiz.php 228654 2007-02-01 11:35:53Z mic $ - * - */ - -class Text_Wiki_Parse_Horiz extends Text_Wiki_Parse { - - - /** - * - * The regular expression used to parse the source text and find - * matches conforming to this rule. Used by the parse() method. - * - * @access public - * - * @var string - * - * @see parse() - * - */ - - var $regex = '/^([-]{4,})$/m'; - - - /** - * - * Generates a replacement token for the matched text. - * - * @access public - * - * @param array &$matches The array of matches from parse(). - * - * @return string A token marking the horizontal rule. - * - */ - - function process(&$matches) - { - return "\n" . $this->wiki->addToken($this->rule) . "\n"; - } -} -?> \ No newline at end of file diff --git a/pear/Text/Wiki/Parse/Creole/Image.php b/pear/Text/Wiki/Parse/Creole/Image.php deleted file mode 100644 index 09ddb8b..0000000 --- a/pear/Text/Wiki/Parse/Creole/Image.php +++ /dev/null @@ -1,69 +0,0 @@ - - * - * @license LGPL - * - * @version $Id: Image.php 243106 2007-09-28 22:02:50Z mic $ - * - */ - - -class Text_Wiki_Parse_Image extends Text_Wiki_Parse { - - /** - * - * Constructor. Overrides the Text_Wiki_Parse constructor so that we - * can set the $regex property dynamically (we need to include the - * Text_Wiki $delim character). - * - * @param object &$obj The calling "parent" Text_Wiki object. - * - * @param string $name The token name to use for this rule. - * - */ - - function Text_Wiki_Parse_Image(&$obj) - { - parent::Text_Wiki_Parse($obj); - $this->regex = '/{{([^' . $this->wiki->delim . ']*)(\|([^' . $this->wiki->delim . ']*))?}}/U'; - } - - - /** - * - * Generates a replacement token for the matched text. - * - * @access public - * - * @param array &$matches The array of matches from parse(). - * - * @return string A token marking the horizontal rule. - * - */ - - function process(&$matches) - { - $src = trim($matches[1]); - $src = ltrim($src, '/'); - $alt = isset($matches[3]) ? trim($matches[3]) : $src; - - return $this->wiki->addToken( - $this->rule, - array( - 'src' => $src, - 'attr' => array('alt' => $alt, 'title' => $alt) - ) - ); - } - -} -?> \ No newline at end of file diff --git a/pear/Text/Wiki/Parse/Creole/List.php b/pear/Text/Wiki/Parse/Creole/List.php deleted file mode 100644 index 36ab590..0000000 --- a/pear/Text/Wiki/Parse/Creole/List.php +++ /dev/null @@ -1,244 +0,0 @@ - - * @author Paul M. Jones - * @author Michele Tomaiuolo - * - * @license LGPL - * - * @version $Id: List.php 240550 2007-08-01 07:57:34Z mic $ - * - */ - -class Text_Wiki_Parse_List extends Text_Wiki_Parse { - - - /** - * - * The regular expression used to parse the source text and find - * matches conforming to this rule. Used by the parse() method. - * - * @access public - * - * @var string - * - * @see parse() - * - */ - - var $regex = '/\n((\*[^\#\-\*]|\-[^\-\d\*\#]|\#[^\#\-\*]).*?)\n(?![\*\-#])/s'; - - /** - * - * Generates a replacement for the matched text. Token options are: - * - * 'type' => - * 'bullet_start' : the start of a bullet list - * 'bullet_end' : the end of a bullet list - * 'number_start' : the start of a number list - * 'number_end' : the end of a number list - * 'item_start' : the start of item text (bullet or number) - * 'item_end' : the end of item text (bullet or number) - * 'unknown' : unknown type of list or item - * - * 'level' => the indent level (0 for the first level, 1 for the - * second, etc) - * - * 'count' => the list item number at this level. not needed for - * xhtml, but very useful for PDF and RTF. - * - * @access public - * - * @param array &$matches The array of matches from parse(). - * - * @return A series of text and delimited tokens marking the different - * list text and list elements. - * - */ - - function process(&$matches) - { - // the replacement text we will return - $return = ''; - - // the list of post-processing matches - $list = array(); - - // a stack of list-start and list-end types; we keep this - // so that we know what kind of list we're working with - // (bullet or number) and what indent level we're at. - $stack = array(); - - // the item count is the number of list items for any - // given list-type on the stack - $itemcount = array(); - - // have we processed the very first list item? - $pastFirst = false; - - // populate $list with this set of matches. $matches[1] is the - // text matched as a list set by parse(). - preg_match_all( - '/^((\*|\-|#)+) *(.*?)$/ms', - $matches[1], - $list, - PREG_SET_ORDER - ); - - if (count($list) === 1 && $matches[0][0] === '*' && $matches[0][1] !== ' ' && strpos($matches[0], '*', 1)) { - return $matches[0]; - } - - // loop through each list-item element. - foreach ($list as $key => $val) { - // $val[0] is the full matched list-item line - // $val[1] is the level (number) - // $val[2] is the type (* or #) - // $val[3] is the list item text - - // how many levels are we indented? (1 means the "root" - // list level, no indenting.) - $stars = $val[1]; - $level = strlen($stars); - $last = $stars[strlen($stars) - 1]; - - // get the list item type - if ($last == '*' || $last == '-') { - $type = 'bullet'; - } elseif ($last == '#') { - $type = 'number'; - } else { - $type = 'unknown'; - } - - // get the text of the list item - $text = $val[3]; - - // remove a level from the list? - while (count($stack) > $level || (count($stack) == $level && $type != $stack[$level - 1])) { - - // so we don't keep counting the stack, we set up a temp - // var for the count. -1 becuase we're going to pop the - // stack in the next command. $tmp will then equal the - // current level of indent. - $tmp = count($stack) - 1; - - // as long as the stack count is greater than the - // current indent level, we need to end list types. - // continue adding end-list tokens until the stack count - // and the indent level are the same. - $return .= $this->wiki->addToken( - $this->rule, - array ( - 'type' => array_pop($stack) . '_list_end', - 'level' => $tmp - ) - ); - - // reset to the current (previous) list type so that - // the new list item matches the proper list type. - if ($tmp) { - $oldtype = $stack[$tmp - 1]; - } - - // reset the item count for the popped indent level - unset($itemcount[$tmp + 1]); - } - - // add a level to the list? - if ($level > count($stack)) { - - // the current indent level is greater than the - // number of stack elements, so we must be starting - // a new list. push the new list type onto the - // stack... - array_push($stack, $type); - - // ...and add a list-start token to the return. - $return .= $this->wiki->addToken( - $this->rule, - array( - 'type' => $type . '_list_start', - 'level' => $level - 1 - ) - ); - } - - // add to the item count for this list (taking into account - // which level we are at). - if (! isset($itemcount[$level])) { - // first count - $itemcount[$level] = 0; - } else { - // increment count - $itemcount[$level]++; - } - - // is this the very first item in the list? - if (! $pastFirst) { - $first = true; - $pastFirst = true; - } else { - $first = false; - } - - // create a list-item starting token. - $start = $this->wiki->addToken( - $this->rule, - array( - 'type' => $type . '_item_start', - 'level' => $level, - 'count' => $itemcount[$level], - 'first' => $first - ) - ); - - // create a list-item ending token. - $end = $this->wiki->addToken( - $this->rule, - array( - 'type' => $type . '_item_end', - 'level' => $level, - 'count' => $itemcount[$level] - ) - ); - - // add the starting token, list-item text, and ending token - // to the return. - $return .= "\n" . $start . $text . $end; - } - - // the last list-item may have been indented. go through the - // list-type stack and create end-list tokens until the stack - // is empty. - while (count($stack) > 0) { - $return .= $this->wiki->addToken( - $this->rule, - array ( - 'type' => array_pop($stack) . '_list_end', - 'level' => count($stack) - ) - ); - } - - // we're done! send back the replacement text. - return "\n\n" . $return . "\n\n"; - } -} -?> \ No newline at end of file diff --git a/pear/Text/Wiki/Parse/Creole/Newline.php b/pear/Text/Wiki/Parse/Creole/Newline.php deleted file mode 100644 index 92554ed..0000000 --- a/pear/Text/Wiki/Parse/Creole/Newline.php +++ /dev/null @@ -1,60 +0,0 @@ - - * - * @license LGPL - * - * @version $Id: Newline.php 240560 2007-08-01 11:00:11Z mic $ - * - */ - -class Text_Wiki_Parse_Newline extends Text_Wiki_Parse { - - - /** - * - * The regular expression used to parse the source text and find - * matches conforming to this rule. Used by the parse() method. - * - * @access public - * - * @var string - * - * @see parse() - * - */ - - //var $regex = '/(?\:]|\*[^\*\#]|\*+ )/m'; - var $regex = '/(?|\:|\;|\!|\-\D)/m'; - - - /** - * - * Generates a replacement token for the matched text. - * - * @access public - * - * @param array &$matches The array of matches from parse(). - * - * @return string A delimited token to be used as a placeholder in - * the source text. - * - */ - - function process(&$matches) - { - return ' '; // $this->wiki->addToken($this->rule); - } -} - -?> \ No newline at end of file diff --git a/pear/Text/Wiki/Parse/Creole/Paragraph.php b/pear/Text/Wiki/Parse/Creole/Paragraph.php deleted file mode 100644 index d23ed33..0000000 --- a/pear/Text/Wiki/Parse/Creole/Paragraph.php +++ /dev/null @@ -1,139 +0,0 @@ - - * @author Michele Tomaiuolo - * - * @license LGPL - * - * @version $Id: Paragraph.php 230214 2007-02-19 08:57:14Z mic $ - * - */ - -class Text_Wiki_Parse_Paragraph extends Text_Wiki_Parse { - - /** - * - * The regular expression used to find source text matching this - * rule. - * - * @access public - * - * @var string - * - */ - - var $regex = "/^.+?\n/m"; // (?=[\n\-\|#{=]) - - var $conf = array( - 'skip' => array( - 'address', - 'box', - 'blockquote', - 'code', - 'heading', - 'center', - 'horiz', - 'deflist', - 'table', - 'list', - 'paragraph', - 'preformatted', - 'toc' - ) - ); - - - /** - * - * Generates a token entry for the matched text. Token options are: - * - * 'start' => The starting point of the paragraph. - * - * 'end' => The ending point of the paragraph. - * - * @access public - * - * @param array &$matches The array of matches from parse(). - * - * @return A delimited token number to be used as a placeholder in - * the source text. - * - */ - - function process(&$matches) - { - $delim = $this->wiki->delim; - - // was anything there? - if (trim($matches[0]) == '') { - return ''; - } - - // does the match start with a delimiter? - if (substr($matches[0], 0, 1) != $delim) { - // no. - - $start = $this->wiki->addToken( - $this->rule, array('type' => 'start') - ); - - $end = $this->wiki->addToken( - $this->rule, array('type' => 'end') - ); - - return $start . trim($matches[0]) . $end; - } - - // the line starts with a delimiter. read in the delimited - // token number, check the token, and see if we should - // skip it. - - // loop starting at the second character (we already know - // the first is a delimiter) until we find another - // delimiter; the text between them is a token key number. - $key = ''; - $len = strlen($matches[0]); - for ($i = 1; $i < $len; $i++) { - $char = $matches[0]{$i}; - if ($char == $delim) { - break; - } else { - $key .= $char; - } - } - - // look at the token and see if it's skippable (if we skip, - // it will not be marked as a paragraph) - $token_type = strtolower($this->wiki->tokens[$key][0]); - $skip = $this->getConf('skip', array()); - - if (in_array($token_type, $skip)) { - // this type of token should not have paragraphs applied to it. - // return the entire matched text. - return $matches[0]; - } else { - - $start = $this->wiki->addToken( - $this->rule, array('type' => 'start') - ); - - $end = $this->wiki->addToken( - $this->rule, array('type' => 'end') - ); - - return $start . trim($matches[0]) . $end; - } - } -} -?> \ No newline at end of file diff --git a/pear/Text/Wiki/Parse/Creole/Prefilter.php b/pear/Text/Wiki/Parse/Creole/Prefilter.php deleted file mode 100644 index ced69b3..0000000 --- a/pear/Text/Wiki/Parse/Creole/Prefilter.php +++ /dev/null @@ -1,54 +0,0 @@ - - * @author Michele Tomaiuolo - * - * @license LGPL - * - * @version $Id: Prefilter.php 222265 2006-10-23 13:11:27Z mic $ - * - */ - -class Text_Wiki_Parse_Prefilter extends Text_Wiki_Parse { - - - /** - * - * Simple parsing method. - * - * @access public - * - */ - - function parse() - { - // convert DOS line endings - $this->wiki->source = str_replace("\r\n", "\n", - $this->wiki->source); - - // convert Macintosh line endings - $this->wiki->source = str_replace("\r", "\n", - $this->wiki->source); - - // convert tabs to four-spaces - $this->wiki->source = str_replace("\t", " ", - $this->wiki->source); - - // add extra newlines at the top and end; this - // seems to help many rules. - $this->wiki->source = "\n\n" . $this->wiki->source . "\n\n"; - } - -} -?> \ No newline at end of file diff --git a/pear/Text/Wiki/Parse/Creole/Preformatted.php b/pear/Text/Wiki/Parse/Creole/Preformatted.php deleted file mode 100644 index ac164fb..0000000 --- a/pear/Text/Wiki/Parse/Creole/Preformatted.php +++ /dev/null @@ -1,68 +0,0 @@ - - * - * @license LGPL - * - * @version $Id: Preformatted.php 240474 2007-07-30 13:14:41Z mic $ - * - */ - -class Text_Wiki_Parse_Preformatted extends Text_Wiki_Parse { - - - /** - * - * The regular expression used to parse the source text and find - * matches conforming to this rule. Used by the parse() method. - * - * @access public - * - * @var string - * - * @see parse() - * - */ - - var $regex = '/\n{{{\n(.*)\n}}}\n/Us'; - - /** - * - * Generates a replacement for the matched text. Token options are: - * - * 'text' => The preformatted text. - * - * @access public - * - * @param array &$matches The array of matches from parse(). - * - * @return string A token to be used as a placeholder - * in the source text for the preformatted text. - * - */ - - function process(&$matches) - { - // > any line consisting of only indented three closing curly braces - // > will have one space removed from the indentation - // > -- http://www.wikicreole.org/wiki/AddNoWikiEscapeProposal - $find = "/\n( *) }}}/"; - $replace = "\n$1}}}"; - $matches[1] = preg_replace($find, $replace, $matches[1]); - - $token = $this->wiki->addToken( - $this->rule, - array('text' => $matches[1]) - ); - return "\n\n" . $token . "\n\n"; - } -} -?> diff --git a/pear/Text/Wiki/Parse/Creole/Raw.php b/pear/Text/Wiki/Parse/Creole/Raw.php deleted file mode 100644 index 6679267..0000000 --- a/pear/Text/Wiki/Parse/Creole/Raw.php +++ /dev/null @@ -1,61 +0,0 @@ - - * - * @license LGPL - * - * @version $Id: Raw.php 242165 2007-09-04 19:43:07Z mic $ - * - */ - -class Text_Wiki_Parse_Raw extends Text_Wiki_Parse { - - - /** - * - * The regular expression used to parse the source text and find - * matches conforming to this rule. Used by the parse() method. - * - * @access public - * - * @var string - * - * @see parse() - * - */ - - var $regex = '/~(_|[^ \w\n])/'; - - /** - * - * Generates a replacement for the matched text. Token options are: - * - * 'type' => ['start'|'end'] The starting or ending point of the - * monospaced text. The text itself is encapsulated into a Raw token. - * - * @access public - * - * @param array &$matches The array of matches from parse(). - * - * @return string A token to be used as a placeholder - * in the source text for the preformatted text. - * - */ - - function process(&$matches) - { - return $this->wiki->addToken( - $this->rule, - array('text' => $matches[1], 'type' => 'escape') - ); - } -} -?> diff --git a/pear/Text/Wiki/Parse/Creole/Strong.php b/pear/Text/Wiki/Parse/Creole/Strong.php deleted file mode 100644 index 1d07d15..0000000 --- a/pear/Text/Wiki/Parse/Creole/Strong.php +++ /dev/null @@ -1,84 +0,0 @@ - - * @author Michele Tomaiuolo - * - * @license LGPL - * - * @version $Id: Strong.php 242126 2007-09-03 21:17:00Z mic $ - * - */ - -class Text_Wiki_Parse_Strong extends Text_Wiki_Parse { - - - /** - * - * The regular expression used to parse the source text and find - * matches conforming to this rule. Used by the parse() method. - * - * @access public - * - * @var string - * - * @see parse() - * - */ - - var $regex = "/\*\*(.+?)\*\*/"; - //var $regex = "/(?:\*\*(.+?)\*\*|(?:(?<=[\W_\xFF])\*(?![ \*]))(.+?)(?:(? ['start'|'end'] The starting or ending point of the - * emphasized text. The text itself is left in the source. - * - * @access public - * - * @param array &$matches The array of matches from parse(). - * - * @return A pair of delimited tokens to be used as a placeholder in - * the source text surrounding the text to be emphasized. - * - */ - - function process(&$matches) - { - $text = $matches[1]; - //$text = $matches[1] ? $matches[1] : $matches[2]; - - if (! $this->wiki->checkInnerTags($text)) { - return $matches[0]; - } - - $start = $this->wiki->addToken( - $this->rule, - array('type' => 'start') - ); - - $end = $this->wiki->addToken( - $this->rule, - array('type' => 'end') - ); - - return $start . $text . $end; - } -} -?> \ No newline at end of file diff --git a/pear/Text/Wiki/Parse/Creole/Subscript.php b/pear/Text/Wiki/Parse/Creole/Subscript.php deleted file mode 100644 index 596f652..0000000 --- a/pear/Text/Wiki/Parse/Creole/Subscript.php +++ /dev/null @@ -1,75 +0,0 @@ - - * @author Michele Tomaiuolo - * - */ - -class Text_Wiki_Parse_Subscript extends Text_Wiki_Parse { - - - /** - * - * The regular expression used to parse the source text and find - * matches conforming to this rule. Used by the parse() method. - * - * @access public - * - * @var string - * - * @see parse() - * - */ - - var $regex = "/\,\,(.*?)\,\,/"; - - /** - * - * Generates a replacement for the matched text. Token options are: - * - * 'type' => ['start'|'end'] The starting or ending point of the - * superscript text. The text itself is left in the source. - * - * @access public - * - * @param array &$matches The array of matches from parse(). - * - * @return string A pair of delimited tokens to be used as a - * placeholder in the source text surrounding the text to be - * superscripted. - * - */ - - function process(&$matches) - { - if (! $this->wiki->checkInnerTags($matches[1])) { - return $matches[0]; - } - - $start = $this->wiki->addToken( - $this->rule, - array('type' => 'start') - ); - - $end = $this->wiki->addToken( - $this->rule, - array('type' => 'end') - ); - - return $start . $matches[1] . $end; - } -} -?> \ No newline at end of file diff --git a/pear/Text/Wiki/Parse/Creole/Superscript.php b/pear/Text/Wiki/Parse/Creole/Superscript.php deleted file mode 100644 index 0f5e38c..0000000 --- a/pear/Text/Wiki/Parse/Creole/Superscript.php +++ /dev/null @@ -1,75 +0,0 @@ - - * @author Michele Tomaiuolo - * - */ - -class Text_Wiki_Parse_Superscript extends Text_Wiki_Parse { - - - /** - * - * The regular expression used to parse the source text and find - * matches conforming to this rule. Used by the parse() method. - * - * @access public - * - * @var string - * - * @see parse() - * - */ - - var $regex = "/(\^\^(.*?)\^\^|(?<=\d)(st|nd|rd|th|er|e|re|ers|res|nds|de|des|ère|ème|ères|èmes|o|a)(?!\w))/"; - - /** - * - * Generates a replacement for the matched text. Token options are: - * - * 'type' => ['start'|'end'] The starting or ending point of the - * superscript text. The text itself is left in the source. - * - * @access public - * - * @param array &$matches The array of matches from parse(). - * - * @return string A pair of delimited tokens to be used as a - * placeholder in the source text surrounding the text to be - * superscripted. - * - */ - - function process(&$matches) - { - if (! $this->wiki->checkInnerTags($matches[0])) { - return $matches[0]; - } - - $start = $this->wiki->addToken( - $this->rule, - array('type' => 'start') - ); - - $end = $this->wiki->addToken( - $this->rule, - array('type' => 'end') - ); - - return $start . trim($matches[0], '^') . $end; - } -} -?> \ No newline at end of file diff --git a/pear/Text/Wiki/Parse/Creole/Table.php b/pear/Text/Wiki/Parse/Creole/Table.php deleted file mode 100644 index 2ab3284..0000000 --- a/pear/Text/Wiki/Parse/Creole/Table.php +++ /dev/null @@ -1,207 +0,0 @@ - - * @author Paul M. Jones - * - * @license LGPL - * - * @version $Id: Table.php 243077 2007-09-28 17:14:58Z mic $ - * - */ - - -class Text_Wiki_Parse_Table extends Text_Wiki_Parse { - - - /** - * - * The regular expression used to parse the source text and find - * matches conforming to this rule. Used by the parse() method. - * - * @access public - * - * @var string - * - * @see parse() - * - */ - - var $regex = '/\n((\|).*)(\n)(?!(\|))/Us'; - - - /** - * - * Generates a replacement for the matched text. - * - * Token options are: - * - * 'type' => - * 'table_start' : the start of a bullet list - * 'table_end' : the end of a bullet list - * 'row_start' : the start of a number list - * 'row_end' : the end of a number list - * 'cell_start' : the start of item text (bullet or number) - * 'cell_end' : the end of item text (bullet or number) - * - * 'cols' => the number of columns in the table (for 'table_start') - * - * 'rows' => the number of rows in the table (for 'table_start') - * - * 'span' => column span (for 'cell_start') - * - * 'attr' => column attribute flag (for 'cell_start') - * - * @access public - * - * @param array &$matches The array of matches from parse(). - * - * @return A series of text and delimited tokens marking the different - * table elements and cell text. - * - */ - - function process(&$matches) - { - // our eventual return value - $return = ''; - - // the number of columns in the table - $num_cols = 0; - - // the number of rows in the table - $num_rows = 0; - - // rows are separated by newlines in the matched text - $rows = explode("\n", $matches[1]); - - // loop through each row - foreach ($rows as $row) { - - // increase the row count - $num_rows ++; - - // remove first and last (optional) pipe - $row = substr($row, 1); - if ($row[strlen($row) - 1] == '|') { - $row = substr($row, 0, -1); - } - - // cells are separated by pipes - $cells = explode("|", $row); - - if (count($cells) == 1 && $cells[0][0] == '=' && ($num_rows == 1 || $num_rows == count($rows)) && ! isset($caption)) { - $caption = trim(trim($cells[0], '=')); - - // start the caption... - $return .= $this->wiki->addToken( - $this->rule, - array ('type' => 'caption_start') - ); - - // ...add the content... - $return .= $caption; - - // ...and end the caption. - $return .= $this->wiki->addToken( - $this->rule, - array ('type' => 'caption_end') - ); - } - else { - - // update the column count - if (count($cells) > $num_cols) { - $num_cols = count($cells); - } - - // start a new row - $return .= $this->wiki->addToken( - $this->rule, - array('type' => 'row_start') - ); - - for ($i = 0; $i < count($cells); $i++) { - $cell = $cells[$i]; - - // by default, cells span only one column (their own) - $span = 1; - $attr = ''; - - while ($i + 1 < count($cells) && ! strlen($cells[$i + 1])) { - $i++; - $span++; - } - - if (strlen($cell) > 0 && $cell[0] == '=') { - $attr = 'header'; - $cell = trim($cell, '='); - } - - // start a new cell... - $return .= $this->wiki->addToken( - $this->rule, - array ( - 'type' => 'cell_start', - 'attr' => $attr, - 'span' => $span - ) - ); - - // ...add the content... - $return .= trim($cell); - - // ...and end the cell. - $return .= $this->wiki->addToken( - $this->rule, - array ( - 'type' => 'cell_end', - 'attr' => $attr, - 'span' => $span - ) - ); - } - - // end the row - $return .= $this->wiki->addToken( - $this->rule, - array('type' => 'row_end') - ); - } - } - - // we're done! - return - "\n\n". - $this->wiki->addToken( - $this->rule, - array( - 'type' => 'table_start', - 'rows' => $num_rows, - 'cols' => $num_cols - ) - ). - $return. - $this->wiki->addToken( - $this->rule, - array( - 'type' => 'table_end' - ) - ). - "\n\n"; - } -} -?> \ No newline at end of file diff --git a/pear/Text/Wiki/Parse/Creole/Tighten.php b/pear/Text/Wiki/Parse/Creole/Tighten.php deleted file mode 100644 index 8ef1b45..0000000 --- a/pear/Text/Wiki/Parse/Creole/Tighten.php +++ /dev/null @@ -1,37 +0,0 @@ - - * - * @license LGPL - * - * @version $Id: Tighten.php 222265 2006-10-23 13:11:27Z mic $ - * - */ - - -class Text_Wiki_Parse_Tighten extends Text_Wiki_Parse { - - - /** - * - * Apply tightening directly to the source text. - * - * @access public - * - */ - - function parse() - { - $this->wiki->source = str_replace("\n", '', - $this->wiki->source); - } -} -?> \ No newline at end of file diff --git a/pear/Text/Wiki/Parse/Creole/Trim.php b/pear/Text/Wiki/Parse/Creole/Trim.php deleted file mode 100644 index f10c9a2..0000000 --- a/pear/Text/Wiki/Parse/Creole/Trim.php +++ /dev/null @@ -1,75 +0,0 @@ - - * @author Michele Tomaiuolo - * - */ - -class Text_Wiki_Parse_Trim extends Text_Wiki_Parse { - - - /** - * - * Simple parsing method. - * - * @access public - * - */ - - function parse() - { - // trim lines - $find = "/ *\n */"; - $replace = "\n"; - $this->wiki->source = preg_replace($find, $replace, $this->wiki->source); - - // trim lines with only one dash or star - $find = "/\n[\-\*]\n/"; - $replace = "\n\n"; - $this->wiki->source = preg_replace($find, $replace, $this->wiki->source); - - // finally, compress all instances of 3 or more newlines - // down to two newlines. - $find = "/\n{3,}/m"; - $replace = "\n\n"; - $this->wiki->source = preg_replace($find, $replace, $this->wiki->source); - - // numbered lists - $find = "/(\n[\*\#]*)([\d]+[\.\)]|[\w]\)) /s"; - $replace = "$1# "; - $this->wiki->source = preg_replace($find, $replace, $this->wiki->source); - - // numbers in parentesis are footnotes and references - $find = "/\(([\d][\d]?)\)/"; - $replace = "[$1]"; - $this->wiki->source = preg_replace($find, $replace, $this->wiki->source); - - // add hr before footnotes - $find = "/(\n+\-\-\-\-+\n*)?(\n\[[\d]+\].*)/s"; - $replace = "\n\n----\n\n$2"; - $this->wiki->source = preg_replace($find, $replace, $this->wiki->source); - - /* - // wrap images in tables - $find = "/(?<=\n\n){{([^\|}]*)\|([^}]*)}}(?=\n\n)/"; - $replace = "| {{ $1 | $2 }}\n|= $2"; - $this->wiki->source = preg_replace($find, $replace, $this->wiki->source); - - // wrap images in tables - $find = "/(?<=\n\n){{([^\|}]*)}}(?=\n\n)/"; - $replace = "| {{ $1 }}"; - $this->wiki->source = preg_replace($find, $replace, $this->wiki->source); - */ - } - -} -?> \ No newline at end of file diff --git a/pear/Text/Wiki/Parse/Creole/Tt.php b/pear/Text/Wiki/Parse/Creole/Tt.php deleted file mode 100644 index 31072bc..0000000 --- a/pear/Text/Wiki/Parse/Creole/Tt.php +++ /dev/null @@ -1,78 +0,0 @@ - - * - * @license LGPL - * - * @version $Id: Tt.php 240474 2007-07-30 13:14:41Z mic $ - * - */ - -class Text_Wiki_Parse_Tt extends Text_Wiki_Parse { - - - /** - * - * The regular expression used to parse the source text and find - * matches conforming to this rule. Used by the parse() method. - * - * @access public - * - * @var string - * - * @see parse() - * - */ - - var $regex = '/{{{(.*?)}}}(?!}|{{{)/'; - - /** - * - * Generates a replacement for the matched text. Token options are: - * - * 'type' => ['start'|'end'] The starting or ending point of the - * monospaced text. The text itself is encapsulated into a Raw token. - * - * @access public - * - * @param array &$matches The array of matches from parse(). - * - * @return string A token to be used as a placeholder - * in the source text for the preformatted text. - * - */ - - function process(&$matches) - { - // remove the sequence }}}{{{ - $find = "/}}}{{{/"; - $replace = ""; - $matches[1] = preg_replace($find, $replace, $matches[1]); - - $start = $this->wiki->addToken( - $this->rule, - array('type' => 'start') - ); - - $raw = $this->wiki->addToken( - 'Raw', - array('text' => $matches[1]) - ); - - $end = $this->wiki->addToken( - $this->rule, - array('type' => 'end') - ); - - return $start . $raw . $end; - } -} -?> diff --git a/pear/Text/Wiki/Parse/Creole/Underline.php b/pear/Text/Wiki/Parse/Creole/Underline.php deleted file mode 100644 index c3d5b14..0000000 --- a/pear/Text/Wiki/Parse/Creole/Underline.php +++ /dev/null @@ -1,83 +0,0 @@ - - * @author Michele Tomaiuolo - * - * @license LGPL - * - * @version $Id: Underline.php 242127 2007-09-03 21:29:36Z mic $ - * - */ - -class Text_Wiki_Parse_Underline extends Text_Wiki_Parse { - - - /** - * - * The regular expression used to parse the source text and find - * matches conforming to this rule. Used by the parse() method. - * - * @access public - * - * @var string - * - * @see parse() - * - */ - - var $regex = "/__(.+?)__/"; - //var $regex = "/(?:\_\_(.+?)\_\_|(?:(?<=[\W_\xFF])\_(?![ \_]))(.+?)(?:(? ['start'|'end'] The starting or ending point of the - * superscript text. The text itself is left in the source. - * - * @access public - * - * @param array &$matches The array of matches from parse(). - * - * @return string A pair of delimited tokens to be used as a - * placeholder in the source text surrounding the text to be - * superscripted. - * - */ - - function process(&$matches) - { - $text = $matches[1]; - //$text = $matches[1] ? $matches[1] : $matches[2]; - - if (! $this->wiki->checkInnerTags($text)) { - return $matches[0]; - } - - $start = $this->wiki->addToken( - $this->rule, - array('type' => 'start') - ); - - $end = $this->wiki->addToken( - $this->rule, - array('type' => 'end') - ); - - return $start . $text . $end; - } -} -?> \ No newline at end of file diff --git a/pear/Text/Wiki/Parse/Creole/Url.php b/pear/Text/Wiki/Parse/Creole/Url.php deleted file mode 100644 index 4422a31..0000000 --- a/pear/Text/Wiki/Parse/Creole/Url.php +++ /dev/null @@ -1,109 +0,0 @@ - tag (for the 'xhtml' - * format). - * - * @category Text - * - * @package Text_Wiki - * - * @author Michele Tomaiuolo - * - * @license LGPL - * - * @version $Id: Url.php 293784 2010-01-20 18:48:09Z justinpatrin $ - * - */ - -class Text_Wiki_Parse_Url extends Text_Wiki_Parse { - - /** - * - * Constructor. Overrides the Text_Wiki_Parse constructor so that we - * can set the $regex property dynamically (we need to include the - * Text_Wiki $delim character). - * - * @param object &$obj The calling "parent" Text_Wiki object. - * - * @param string $name The token name to use for this rule. - * - */ - - function Text_Wiki_Parse_Url(&$obj) - { - parent::Text_Wiki_Parse($obj); - $this->regex = '/((?:\[\[ *((?:\w+:\/\/|mailto:|\/)[^\|\]\n ]*)( *\| *([^\]\n]*))? *\]\])|((?<=[^\~\w])(https?:\/\/|ftps?:\/\/|mailto:)[^\'\"\n ' . $this->wiki->delim . ']*[A-Za-z0-9\/\?\=\&\~\_#]))/'; - } - - - /** - * - * Generates a replacement for the matched text. - * - * Token options are: - * - * 'href' => the URL link href portion - * - * 'text' => the displayed text of the URL link - * - * @access public - * - * @param array &$matches The array of matches from parse(). - * - * @return string A token to be used as a placeholder - * in the source text for the preformatted text. - * - */ - - function process(&$matches) - { - if (isset($matches[2])) $href = trim($matches[2]); - if (isset($matches[4])) $text = trim($matches[4]); - if (isset($matches[5])) $rawurl = $matches[5]; - if (empty($href)) $href = $rawurl; - - if (empty($text)) { - $text = $href; - if (strpos($text, '/') === FALSE) { - $text = str_replace('http://', '', $text); - $text = str_replace('mailto:', '', $text); - } - return $this->wiki->addToken( - $this->rule, - array( - 'type' => 'inline', - 'href' => $href, - 'text' => $text - ) - ); - } else { - return $this->wiki->addToken( - $this->rule, - array( - 'type' => 'start', - 'href' => $href, - 'text' => $text - ) - ) . $text . - $this->wiki->addToken( - $this->rule, - array( - 'type' => 'end', - 'href' => $href, - 'text' => $text - ) - ); - } - } - -} -?> \ No newline at end of file diff --git a/pear/Text/Wiki/Parse/Creole/Wikilink.php b/pear/Text/Wiki/Parse/Creole/Wikilink.php deleted file mode 100644 index 19cafc1..0000000 --- a/pear/Text/Wiki/Parse/Creole/Wikilink.php +++ /dev/null @@ -1,322 +0,0 @@ - - * @author Paul M. Jones - * @copyright 2005 bertrand Gugger - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version CVS: $Id: Wikilink.php 240474 2007-07-30 13:14:41Z mic $ - * @link http://pear.php.net/package/Text_Wiki - */ - -/** - * Wikilink, Interwiki and Image rules parser class for Mediawiki. - * This class implements a Text_Wiki_Parse to find links marked - * in source by text surrounded by 2 opening/closing brackets as - * [[Wiki page name#Section|Alternate text]] - * On parsing, the link is replaced with a token. - * - * @category Text - * @package Text_Wiki - * @author Bertrand Gugger - * @copyright 2005 bertrand Gugger - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version Release: @package_version@ - * @link http://pear.php.net/package/Text_Wiki - * @see Text_Wiki_Parse::Text_Wiki_Parse() - */ - -class Text_Wiki_Parse_Wikilink extends Text_Wiki_Parse { - - /** - * Configuration for this rule (Wikilink) - * - * @access public - * @var array - */ - - var $conf = array( - 'spaceUnderscore' => true, - 'project' => array('demo', 'd'), - 'url' => 'http://example.com/en/page=%s', - 'langage' => 'en' - ); - - /** - * Configuration for the Image rule - * - * @access public - * @var array - */ - - var $imageConf = array( - 'prefix' => array('Image', 'image') - ); - - /** - * Configuration for the Interwiki rule - * - * @access public - * @var array - */ - - var $interwikiConf = array( - 'sites' => array( - 'manual' => 'http://www.php.net/manual/en/%s', - 'pear' => 'http://pear.php.net/package/%s', - 'bugs' => 'http://pear.php.net/package/%s/bugs' - ), - 'interlangage' => array('en', 'de', 'fr') - ); - - /** - * The regular expression used to parse the source text and find - * matches conforming to this rule. Used by the parse() method. - * - * @access public - * @var string - * @see Text_Wiki_Parse::parse() - */ - - var $regex = '/(?conf; - parent::Text_Wiki_Parse($obj); - - // override config options for image if specified - if (in_array('Image', $this->wiki->disable)) { - $this->imageConf['prefix'] = array(); - } else { - if (isset($this->wiki->parseConf['Image']) && - is_array($this->wiki->parseConf['Image'])) { - $this->imageConf = array_merge( - $this->imageConf, - $this->wiki->parseConf['Image'] - ); - } - } - - // override config options for interwiki if specified - if (in_array('Interwiki', $this->wiki->disable)) { - $this->interwikiConf['sites'] = array(); - $this->interwikiConf['interlangage'] = array(); - } else { - if (isset($this->wiki->parseConf['Interwiki']) && - is_array($this->wiki->parseConf['Interwiki'])) { - $this->interwikiConf = array_merge( - $this->interwikiConf, - $this->wiki->parseConf['Interwiki'] - ); - } - if (empty($this->conf['langage'])) { - $this->interwikiConf['interlangage'] = array(); - } - } - //$this->regex = str_replace('DELIM', $this->wiki->delim, $this->regex); - // convert the list of recognized schemes to a regex OR, -/* $schemes = $this->getConf('schemes', $default['schemes']); - $this->url = str_replace( '#delim#', $this->wiki->delim, - '#(?:' . (is_array($schemes) ? implode('|', $schemes) : $schemes) . ')://' - . $this->getConf('host_regexp', $default['host_regexp']) - . $this->getConf('path_regexp', $default['path_regexp']) .'#'); */ - } - - /** - * Generates a replacement for the matched text. Token options are: - * - 'page' => the name of the target wiki page - * -'anchor' => the optional section in it - * - 'text' => the optional alternate link text - * - * @access public - * @param array &$matches The array of matches from parse(). - * @return string token to be used as replacement - */ - - function process(&$matches) - { - $matches[3] = $this->wiki->restoreRaw($matches[3]); - - // Starting colon ? - $colon = !empty($matches[1]); - $auto = $interlang = $interwiki = $image = $site = ''; - // Prefix ? - if (!empty($matches[2])) { - $prefix = explode(':', substr($matches[2], 0, -1)); - $count = count($prefix); - $i = -1; - // Autolink - if (isset($this->conf['project']) && - in_array(trim($prefix[0]), $this->conf['project'])) { - $auto = trim($prefix[0]); - unset($prefix[0]); - $i = 0; - } - while (++$i < $count) { - $prefix[$i] = trim($prefix[$i]); - // interlangage - if (!$interlang && - in_array($prefix[$i], $this->interwikiConf['interlangage'])) { - $interlang = $prefix[$i]; - unset($prefix[$i]); - continue; - } - // image - if (!$image && in_array($prefix[$i], $this->imageConf['prefix'])) { - $image = $prefix[$i]; - unset($prefix[$i]); - break; - } - // interwiki - if (isset($this->interwikiConf['sites'][$prefix[$i]])) { - $interwiki = $this->interwikiConf['sites'][$prefix[$i]]; - $site = $prefix[$i]; - unset($prefix[$i]); - } - break; - } - if ($prefix) { - $matches[3] = implode(':', $prefix) . ':' . $matches[3]; - } - } - $text = empty($matches[5]) ? $matches[3] : $matches[5]; - $matches[3] = trim($matches[3]); - $matches[4] = empty($matches[4]) ? '' : trim($matches[4]); - if ($this->conf['spaceUnderscore']) { - $matches[3] = preg_replace('/\s+/', '_', $matches[3]); - $matches[4] = preg_replace('/\s+/', '_', $matches[4]); - } - if ($image) { - return $this->image($matches[3] . (empty($matches[4]) ? '' : '#' . $matches[4]), - $text, $interlang, $colon); - } - if (!$interwiki && $interlang && isset($this->conf['url'])) { - if ($interlang == $this->conf['langage']) { - $interlang = ''; - } else { - $interwiki = $this->conf['url']; - $site = isset($this->conf['project']) ? $this->conf['project'][0] : ''; - } - } - if ($interwiki) { - return $this->interwiki($site, $interwiki, - $matches[3] . (empty($matches[4]) ? '' : '#' . $matches[4]), - $text, $interlang, $colon); - } - if ($interlang) { - $matches[3] = $interlang . ':' . $matches[3]; - $text = (empty($matches[5]) ? $interlang . ':' : '') . $text; - } - - $start = $this->wiki->addToken($this->rule, array( - 'type' => 'start', - 'page' => $matches[3], - 'anchor' => (empty($matches[4]) ? '' : $matches[4]), - 'text' => $text - )); - - $end = $this->wiki->addToken($this->rule, array( - 'type' => 'end', - 'page' => $matches[3], - 'anchor' => (empty($matches[4]) ? '' : $matches[4]), - 'text' => $text - )); - - // create and return the replacement token - return $start . $text . $end; - } - - /** - * Generates an image token. Token options are: - * - 'src' => the name of the image file - * - 'attr' => an array of attributes for the image: - * | - 'alt' => the optional alternate image text - * | - 'align => 'left', 'center' or 'right' - * - * @access public - * @param array &$matches The array of matches from parse(). - * @return string token to be used as replacement - */ - - function image($name, $text, $interlang, $colon) - { - $attr = array('alt' => ''); - // scan text for supplementary attibutes - if (strpos($text, '|') !== false) { - $splits = explode('|', $text); - $sep = ''; - foreach ($splits as $split) { - switch (strtolower($split)) { - case 'left': case 'center': case 'right': - $attr['align'] = strtolower($split); - break; - default: - $attr['alt'] .= $sep . $split; - $sep = '|'; - } - } - } else { - $attr['alt'] = $text; - } - $options = array( - 'src' => ($interlang ? $interlang . ':' : '') . $name, - 'attr' => $attr); - - // create and return the replacement token - return $this->wiki->addToken('Image', $options); - } - - /** - * Generates an interwiki token. Token options are: - * - 'page' => the name of the target wiki page - * - 'site' => the key for external site - * - 'url' => the full target url - * - 'text' => the optional alternate link text - * - * @access public - * @param array &$matches The array of matches from parse(). - * @return string token to be used as replacement - */ - - function interwiki($site, $interwiki, $page, $text, $interlang, $colon) - { - if ($interlang) { - $interwiki = preg_replace('/\b' . $this->conf['langage'] . '\b/i', - $interlang, $interwiki); - } - // set the options - $options = array( - 'page' => $page, - 'site' => $site, - 'url' => sprintf($interwiki, $page), - 'text' => $text - ); - - // create and return the replacement token - return $this->wiki->addToken('Interwiki', $options); - } -} -?> diff --git a/pear/Text/Wiki/Parse/Default/Anchor.php b/pear/Text/Wiki/Parse/Default/Anchor.php deleted file mode 100644 index 83b57c6..0000000 --- a/pear/Text/Wiki/Parse/Default/Anchor.php +++ /dev/null @@ -1,87 +0,0 @@ - -* -* @author Paul M. Jones -* -* @license LGPL -* -* @version $Id: Anchor.php 180591 2005-02-23 17:38:29Z pmjones $ -* -*/ - -/** -* -* This class implements a Text_Wiki_Parse to add an anchor target name -* in the wiki page. -* -* @author Manuel Holtgrewe -* -* @author Paul M. Jones -* -* @category Text -* -* @package Text_Wiki -* -*/ - -class Text_Wiki_Parse_Anchor extends Text_Wiki_Parse { - - - /** - * - * The regular expression used to find source text matching this - * rule. Looks like a macro: [[# anchor_name]] - * - * @access public - * - * @var string - * - */ - - var $regex = '/(\[\[# )([-_A-Za-z0-9.]+?)( .+)?(\]\])/i'; - - - /** - * - * Generates a token entry for the matched text. Token options are: - * - * 'text' => The full matched text, not including the tags. - * - * @access public - * - * @param array &$matches The array of matches from parse(). - * - * @return A delimited token number to be used as a placeholder in - * the source text. - * - */ - - function process(&$matches) { - - $name = $matches[2]; - $text = $matches[3]; - - $start = $this->wiki->addToken( - $this->rule, - array('type' => 'start', 'name' => $name) - ); - - $end = $this->wiki->addToken( - $this->rule, - array('type' => 'end', 'name' => $name) - ); - - // done, place the script output directly in the source - return $start . trim($text) . $end; - } -} -?> diff --git a/pear/Text/Wiki/Parse/Default/Blockquote.php b/pear/Text/Wiki/Parse/Default/Blockquote.php deleted file mode 100644 index 61da770..0000000 --- a/pear/Text/Wiki/Parse/Default/Blockquote.php +++ /dev/null @@ -1,180 +0,0 @@ - -* -* @license LGPL -* -* @version $Id: Blockquote.php 222150 2006-10-21 05:56:28Z justinpatrin $ -* -*/ - -/** -* -* Parse for block-quoted text. -* -* Find source text marked as a blockquote, identified by any number of -* greater-than signs '>' at the start of the line, followed by a space, -* and then the quote text; each '>' indicates an additional level of -* quoting. -* -* @category Text -* -* @package Text_Wiki -* -* @author Paul M. Jones -* -*/ - -class Text_Wiki_Parse_Blockquote extends Text_Wiki_Parse { - - - /** - * - * Regex for parsing the source text. - * - * @access public - * - * @var string - * - * @see parse() - * - */ - - var $regex = '/\n((\>).*\n)(?!(\>))/Us'; - - - /** - * - * Generates a replacement for the matched text. - * - * Token options are: - * - * 'type' => - * 'start' : the start of a blockquote - * 'end' : the end of a blockquote - * - * 'level' => the indent level (0 for the first level, 1 for the - * second, etc) - * - * @access public - * - * @param array &$matches The array of matches from parse(). - * - * @return A series of text and delimited tokens marking the different - * list text and list elements. - * - */ - - function process(&$matches) - { - // the replacement text we will return to parse() - $return = "\n"; - - // the list of post-processing matches - $list = array(); - - // $matches[1] is the text matched as a list set by parse(); - // create an array called $list that contains a new set of - // matches for the various list-item elements. - preg_match_all( - '=^(\>+) (.*\n)=Ums', - $matches[1], - $list, - PREG_SET_ORDER - ); - - $curLevel = 0; - - // loop through each list-item element. - foreach ($list as $key => $val) { - - // $val[0] is the full matched list-item line - // $val[1] is the number of initial '>' chars (indent level) - // $val[2] is the quote text - - // we number levels starting at 1, not zero - $level = strlen($val[1]); - - // add a level to the list? - while ($level > $curLevel) { - // the current indent level is greater than the number - // of stack elements, so we must be starting a new - // level. push the new level onto the stack with a - // dummy value (boolean true)... - ++$curLevel; - - //$return .= "\n"; - - // ...and add a start token to the return. - $return .= $this->wiki->addToken( - $this->rule, - array( - 'type' => 'start', - 'level' => $curLevel - ) - ); - - //$return .= "\n\n"; - } - - // remove a level? - while ($curLevel > $level) { - - // as long as the stack count is greater than the - // current indent level, we need to end list types. - // continue adding end-list tokens until the stack count - // and the indent level are the same. - - //$return .= "\n\n"; - - $return .= $this->wiki->addToken( - $this->rule, - array ( - 'type' => 'end', - 'level' => $curLevel - ) - ); - - //$return .= "\n"; - --$curLevel; - } - - // add the line text. - $return .= $val[2]; - } - - // the last char of the matched pattern must be \n but we don't - // want this to be inside the tokens - $return = substr($return, 0, -1); - - // the last line may have been indented. go through the stack - // and create end-tokens until the stack is empty. - //$return .= "\n"; - - while ($curLevel > 0) { - $return .= $this->wiki->addToken( - $this->rule, - array ( - 'type' => 'end', - 'level' => $curLevel - ) - ); - --$curLevel; - } - - // put back the trailing \n - $return .= "\n"; - - // we're done! send back the replacement text. - return $return; - } -} -?> \ No newline at end of file diff --git a/pear/Text/Wiki/Parse/Default/Bold.php b/pear/Text/Wiki/Parse/Default/Bold.php deleted file mode 100644 index ba5e965..0000000 --- a/pear/Text/Wiki/Parse/Default/Bold.php +++ /dev/null @@ -1,79 +0,0 @@ - -* -* @license LGPL -* -* @version $Id: Bold.php 180591 2005-02-23 17:38:29Z pmjones $ -* -*/ - -/** -* -* Parses for bold text. -* -* This class implements a Text_Wiki_Rule to find source text marked for -* strong emphasis (bold) as defined by text surrounded by three -* single-quotes. On parsing, the text itself is left in place, but the -* starting and ending instances of three single-quotes are replaced with -* tokens. -* -* @category Text -* -* @package Text_Wiki -* -* @author Paul M. Jones -* -*/ - -class Text_Wiki_Parse_Bold extends Text_Wiki_Parse { - - - /** - * - * The regular expression used to parse the source text and find - * matches conforming to this rule. Used by the parse() method. - * - * @access public - * - * @var string - * - * @see parse() - * - */ - - var $regex = "/'''(()|[^'].*)'''/U"; - - - /** - * - * Generates a replacement for the matched text. Token options are: - * - * 'type' => ['start'|'end'] The starting or ending point of the - * emphasized text. The text itself is left in the source. - * - * @access public - * - * @param array &$matches The array of matches from parse(). - * - * @return A pair of delimited tokens to be used as a placeholder in - * the source text surrounding the text to be emphasized. - * - */ - - function process(&$matches) - { - $start = $this->wiki->addToken($this->rule, array('type' => 'start')); - $end = $this->wiki->addToken($this->rule, array('type' => 'end')); - return $start . $matches[1] . $end; - } -} -?> \ No newline at end of file diff --git a/pear/Text/Wiki/Parse/Default/Break.php b/pear/Text/Wiki/Parse/Default/Break.php deleted file mode 100644 index 1a684f1..0000000 --- a/pear/Text/Wiki/Parse/Default/Break.php +++ /dev/null @@ -1,72 +0,0 @@ - -* -* @license LGPL -* -* @version $Id: Break.php 180591 2005-02-23 17:38:29Z pmjones $ -* -*/ - -/** -* -* Parses for explicit line breaks. -* -* This class implements a Text_Wiki_Parse to mark forced line breaks in the -* source text. -* -* @category Text -* -* @package Text_Wiki -* -* @author Paul M. Jones -* -*/ - -class Text_Wiki_Parse_Break extends Text_Wiki_Parse { - - - /** - * - * The regular expression used to parse the source text and find - * matches conforming to this rule. Used by the parse() method. - * - * @access public - * - * @var string - * - * @see parse() - * - */ - - var $regex = '/ _\n/'; - - - /** - * - * Generates a replacement token for the matched text. - * - * @access public - * - * @param array &$matches The array of matches from parse(). - * - * @return string A delimited token to be used as a placeholder in - * the source text. - * - */ - - function process(&$matches) - { - return $this->wiki->addToken($this->rule); - } -} - -?> \ No newline at end of file diff --git a/pear/Text/Wiki/Parse/Default/Center.php b/pear/Text/Wiki/Parse/Default/Center.php deleted file mode 100644 index 6ddde51..0000000 --- a/pear/Text/Wiki/Parse/Default/Center.php +++ /dev/null @@ -1,78 +0,0 @@ - -* -* @license LGPL -* -* @version $Id: Center.php 180591 2005-02-23 17:38:29Z pmjones $ -* -*/ - -/** -* -* Parses for centered lines of text. -* -* This class implements a Text_Wiki_Parse to find lines marked for centering. -* The line must start with "= " (i.e., an equal-sign followed by a space). -* -* @category Text -* -* @package Text_Wiki -* -* @author Paul M. Jones -* -*/ - -class Text_Wiki_Parse_Center extends Text_Wiki_Parse { - - - /** - * - * The regular expression used to find source text matching this - * rule. - * - * @access public - * - * @var string - * - */ - - var $regex = '/\n\= (.*?)\n/'; - - /** - * - * Generates a token entry for the matched text. - * - * @access public - * - * @param array &$matches The array of matches from parse(). - * - * @return A delimited token number to be used as a placeholder in - * the source text. - * - */ - - function process(&$matches) - { - $start = $this->wiki->addToken( - $this->rule, - array('type' => 'start') - ); - - $end = $this->wiki->addToken( - $this->rule, - array('type' => 'end') - ); - - return "\n" . $start . $matches[1] . $end . "\n"; - } -} -?> \ No newline at end of file diff --git a/pear/Text/Wiki/Parse/Default/Code.php b/pear/Text/Wiki/Parse/Default/Code.php deleted file mode 100644 index fc2d3c1..0000000 --- a/pear/Text/Wiki/Parse/Default/Code.php +++ /dev/null @@ -1,99 +0,0 @@ - -* -* @license LGPL -* -* @version $Id: Code.php 237313 2007-06-09 23:11:25Z justinpatrin $ -* -*/ - -/** -* -* Parses for text marked as a code example block. -* -* This class implements a Text_Wiki_Parse to find sections marked as code -* examples. Blocks are marked as the string on a line by itself, -* followed by the inline code example, and terminated with the string -* on a line by itself. The code example is run through the -* native PHP highlight_string() function to colorize it, then surrounded -* with
    ...
    tags when rendered as XHTML. -* -* @category Text -* -* @package Text_Wiki -* -* @author Paul M. Jones -* -*/ - -class Text_Wiki_Parse_Code extends Text_Wiki_Parse { - - - /** - * - * The regular expression used to find source text matching this - * rule. - * - * @access public - * - * @var string - * - */ - -/* var $regex = '/^(\)\n(.+)\n(\<\/code\>)(\s|$)/Umsi';*/ - var $regex = ';^]*)?>((?:(?R)|.*?)*)\n
    (\s|$);msi'; - - /** - * - * Generates a token entry for the matched text. Token options are: - * - * 'text' => The full matched text, not including the tags. - * - * @access public - * - * @param array &$matches The array of matches from parse(). - * - * @return A delimited token number to be used as a placeholder in - * the source text. - * - */ - - function process(&$matches) - { - // are there additional attribute arguments? - $args = trim($matches[1]); - - if ($args == '') { - $options = array( - 'text' => $matches[2], - 'attr' => array('type' => '') - ); - } else { - // get the attributes... - $attr = $this->getAttrs($args); - - // ... and make sure we have a 'type' - if (! isset($attr['type'])) { - $attr['type'] = ''; - } - - // retain the options - $options = array( - 'text' => $matches[2], - 'attr' => $attr - ); - } - - return $this->wiki->addToken($this->rule, $options) . $matches[3]; - } -} -?> diff --git a/pear/Text/Wiki/Parse/Default/Colortext.php b/pear/Text/Wiki/Parse/Default/Colortext.php deleted file mode 100644 index 5d82cd6..0000000 --- a/pear/Text/Wiki/Parse/Default/Colortext.php +++ /dev/null @@ -1,89 +0,0 @@ - -* -* @license LGPL -* -* @version $Id: Colortext.php 180591 2005-02-23 17:38:29Z pmjones $ -* -*/ - -/** -* -* Parses for colorized text. -* -* @category Text -* -* @package Text_Wiki -* -* @author Paul M. Jones -* -*/ - -class Text_Wiki_Parse_Colortext extends Text_Wiki_Parse { - - /** - * - * The regular expression used to parse the source text and find - * matches conforming to this rule. Used by the parse() method. - * - * @access public - * - * @var string - * - * @see parse() - * - */ - - var $regex = "/\#\#(.+?)\|(.+?)\#\#/"; - - - /** - * - * Generates a replacement for the matched text. Token options are: - * - * 'type' => ['start'|'end'] The starting or ending point of the - * emphasized text. The text itself is left in the source. - * - * 'color' => the color indicator - * - * @access public - * - * @param array &$matches The array of matches from parse(). - * - * @return string A pair of delimited tokens to be used as a - * placeholder in the source text surrounding the text to be - * emphasized. - * - */ - - function process(&$matches) - { - $start = $this->wiki->addToken( - $this->rule, - array( - 'type' => 'start', - 'color' => $matches[1] - ) - ); - - $end = $this->wiki->addToken( - $this->rule, - array( - 'type' => 'end', - 'color' => $matches[1] - ) - ); - - return $start . $matches[2] . $end; - } -} -?> \ No newline at end of file diff --git a/pear/Text/Wiki/Parse/Default/Deflist.php b/pear/Text/Wiki/Parse/Default/Deflist.php deleted file mode 100644 index a8ee0fb..0000000 --- a/pear/Text/Wiki/Parse/Default/Deflist.php +++ /dev/null @@ -1,122 +0,0 @@ - -* -* @license LGPL -* -* @version $Id: Deflist.php 180591 2005-02-23 17:38:29Z pmjones $ -* -*/ - -/** -* -* Parses for definition lists. -* -* This class implements a Text_Wiki_Parse to find source text marked as a -* definition list. In short, if a line starts with ':' then it is a -* definition list item; another ':' on the same line indicates the end -* of the definition term and the beginning of the definition narrative. -* The list items must be on sequential lines (no blank lines between -* them) -- a blank line indicates the beginning of a new list. -* -* @category Text -* -* @package Text_Wiki -* -* @author Paul M. Jones -* -*/ - -class Text_Wiki_Parse_Deflist extends Text_Wiki_Parse { - - - /** - * - * The regular expression used to parse the source text and find - * matches conforming to this rule. Used by the parse() method. - * - * @access public - * - * @var string - * - * @see parse() - * - */ - - var $regex = '/\n((: ).*\n)(?!(: |\n))/Us'; - - - /** - * - * Generates a replacement for the matched text. Token options are: - * - * 'type' => - * 'list_start' : the start of a definition list - * 'list_end' : the end of a definition list - * 'term_start' : the start of a definition term - * 'term_end' : the end of a definition term - * 'narr_start' : the start of definition narrative - * 'narr_end' : the end of definition narrative - * 'unknown' : unknown type of definition portion - * - * @access public - * - * @param array &$matches The array of matches from parse(). - * - * @return A series of text and delimited tokens marking the different - * list text and list elements. - * - */ - - function process(&$matches) - { - // the replacement text we will return to parse() - $return = ''; - - // the list of post-processing matches - $list = array(); - - // start the deflist - $options = array('type' => 'list_start'); - $return .= $this->wiki->addToken($this->rule, $options); - - // $matches[1] is the text matched as a list set by parse(); - // create an array called $list that contains a new set of - // matches for the various definition-list elements. - preg_match_all( - '/^(: )(.*)?( : )(.*)?$/Ums', - $matches[1], - $list, - PREG_SET_ORDER - ); - - // add each term and narrative - foreach ($list as $key => $val) { - $return .= ( - $this->wiki->addToken($this->rule, array('type' => 'term_start')) . - trim($val[2]) . - $this->wiki->addToken($this->rule, array('type' => 'term_end')) . - $this->wiki->addToken($this->rule, array('type' => 'narr_start')) . - trim($val[4]) . - $this->wiki->addToken($this->rule, array('type' => 'narr_end')) - ); - } - - - // end the deflist - $options = array('type' => 'list_end'); - $return .= $this->wiki->addToken($this->rule, $options); - - // done! - return "\n" . $return . "\n\n"; - } -} -?> \ No newline at end of file diff --git a/pear/Text/Wiki/Parse/Default/Delimiter.php b/pear/Text/Wiki/Parse/Default/Delimiter.php deleted file mode 100644 index 8aa4b53..0000000 --- a/pear/Text/Wiki/Parse/Default/Delimiter.php +++ /dev/null @@ -1,80 +0,0 @@ - -* -* @license LGPL -* -* @version $Id: Delimiter.php 180591 2005-02-23 17:38:29Z pmjones $ -* -*/ - -/** -* -* Parses for Text_Wiki delimiter characters already in the source text. -* -* This class implements a Text_Wiki_Parse to find instances of the delimiter -* character already embedded in the source text; it extracts them and replaces -* them with a delimited token, then renders them as the delimiter itself -* when the target format is XHTML. -* -* @category Text -* -* @package Text_Wiki -* -* @author Paul M. Jones -* -*/ - -class Text_Wiki_Parse_Delimiter extends Text_Wiki_Parse { - - /** - * - * Constructor. Overrides the Text_Wiki_Parse constructor so that we - * can set the $regex property dynamically (we need to include the - * Text_Wiki $delim character. - * - * @param object &$obj The calling "parent" Text_Wiki object. - * - * @param string $name The token name to use for this rule. - * - */ - - function Text_Wiki_Parse_delimiter(&$obj) - { - parent::Text_Wiki_Parse($obj); - $this->regex = '/' . $this->wiki->delim . '/'; - } - - - /** - * - * Generates a token entry for the matched text. Token options are: - * - * 'text' => The full matched text. - * - * @access public - * - * @param array &$matches The array of matches from parse(). - * - * @return A delimited token number to be used as a placeholder in - * the source text. - * - */ - - function process(&$matches) - { - return $this->wiki->addToken( - $this->rule, - array('text' => $this->wiki->delim) - ); - } -} -?> \ No newline at end of file diff --git a/pear/Text/Wiki/Parse/Default/Embed.php b/pear/Text/Wiki/Parse/Default/Embed.php deleted file mode 100644 index 2d31a71..0000000 --- a/pear/Text/Wiki/Parse/Default/Embed.php +++ /dev/null @@ -1,106 +0,0 @@ - -* -* @license LGPL -* -* @version $Id: Embed.php 180591 2005-02-23 17:38:29Z pmjones $ -* -*/ - -/** -* -* Embeds the results of a PHP script at render-time. -* -* This class implements a Text_Wiki_Parse to embed the contents of a URL -* inside the page at render-time. Typically used to get script output. -* This differs from the 'include' rule, which incorporates results at -* parse-time; 'embed' output does not get parsed by Text_Wiki, while -* 'include' ouput does. -* -* This rule is inherently not secure; it allows cross-site scripting to -* occur if the embedded output has -'; - } - } else { - $js = ''; - } - return $js.' -
    -'; - case 'endContent': - return ' -
    -'; - } - } -} -?> diff --git a/pear/Text/Wiki/Render/Xhtml/Horiz.php b/pear/Text/Wiki/Render/Xhtml/Horiz.php deleted file mode 100644 index e480f5e..0000000 --- a/pear/Text/Wiki/Render/Xhtml/Horiz.php +++ /dev/null @@ -1,51 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version CVS: $Id: Horiz.php 191862 2005-07-30 08:03:29Z toggg $ - * @link http://pear.php.net/package/Text_Wiki - */ - -/** - * This class renders an horizontal bar in XHTML. - * - * @category Text - * @package Text_Wiki - * @author Paul M. Jones - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version Release: @package_version@ - * @link http://pear.php.net/package/Text_Wiki - */ -class Text_Wiki_Render_Xhtml_Horiz extends Text_Wiki_Render { - - var $conf = array( - 'css' => null - ); - - /** - * - * Renders a token into text matching the requested format. - * - * @access public - * - * @param array $options The "options" portion of the token (second - * element). - * - * @return string The text rendered from the token options. - * - */ - - function token($options) - { - $css = $this->formatConf(' class="%s"', 'css'); - return "\n"; - } -} -?> diff --git a/pear/Text/Wiki/Render/Xhtml/Html.php b/pear/Text/Wiki/Render/Xhtml/Html.php deleted file mode 100644 index 7ca4218..0000000 --- a/pear/Text/Wiki/Render/Xhtml/Html.php +++ /dev/null @@ -1,47 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version CVS: $Id: Html.php 191862 2005-07-30 08:03:29Z toggg $ - * @link http://pear.php.net/package/Text_Wiki - */ - -/** - * This class renders preformated html in XHTML. - * - * @category Text - * @package Text_Wiki - * @author Paul M. Jones - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version Release: @package_version@ - * @link http://pear.php.net/package/Text_Wiki - */ -class Text_Wiki_Render_Xhtml_Html extends Text_Wiki_Render { - - - /** - * - * Renders a token into text matching the requested format. - * - * @access public - * - * @param array $options The "options" portion of the token (second - * element). - * - * @return string The text rendered from the token options. - * - */ - - function token($options) - { - return $options['text']; - } -} -?> diff --git a/pear/Text/Wiki/Render/Xhtml/Image.php b/pear/Text/Wiki/Render/Xhtml/Image.php deleted file mode 100644 index 51def7b..0000000 --- a/pear/Text/Wiki/Render/Xhtml/Image.php +++ /dev/null @@ -1,184 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version CVS: $Id: Image.php 231923 2007-03-15 15:04:50Z justinpatrin $ - * @link http://pear.php.net/package/Text_Wiki - */ - -/** - * This class inserts an image in XHTML. - * - * @category Text - * @package Text_Wiki - * @author Paul M. Jones - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version Release: @package_version@ - * @link http://pear.php.net/package/Text_Wiki - */ -class Text_Wiki_Render_Xhtml_Image extends Text_Wiki_Render { - - var $conf = array( - 'base' => '/', - 'url_base' => null, - 'css' => null, - 'css_link' => null - ); - - - /** - * - * Renders a token into text matching the requested format. - * - * @access public - * - * @param array $options The "options" portion of the token (second - * element). - * - * @return string The text rendered from the token options. - * - */ - - function token($options) - { - // note the image source - $src = $options['src']; - - // is the source a local file or URL? - if (strpos($src, '://') === false) { - // the source refers to a local file. - // add the URL base to it. - $src = $this->getConf('base', '/') . $src; - } - - // stephane@metacites.net - // is the image clickable? - if (isset($options['attr']['link'])) { - // yes, the image is clickable. - // are we linked to a URL or a wiki page? - if (strpos($options['attr']['link'], '://')) { - // it's a URL, prefix the URL base - $href = $this->getConf('url_base') . $options['attr']['link']; - } else { - // it's a WikiPage; assume it exists. - /** @todo This needs to honor sprintf wikilinks (pmjones) */ - /** @todo This needs to honor interwiki (pmjones) */ - /** @todo This needs to honor freelinks (pmjones) */ - $href = $this->wiki->getRenderConf('xhtml', 'wikilink', 'view_url') . - $options['attr']['link']; - } - } else { - // image is not clickable. - $href = null; - } - // unset so it won't show up as an attribute - unset($options['attr']['link']); - - // stephane@metacites.net -- 25/07/2004 - // use CSS for all alignment - if (isset($options['attr']['align'])) { - // make sure we have a style attribute - if (!isset($options['attr']['style'])) { - // no style, set up a blank one - $options['attr']['style'] = ''; - } else { - // style exists, add a space - $options['attr']['style'] .= ' '; - } - - if ($options['attr']['align'] == 'center') { - // add a "center" style to the existing style. - $options['attr']['style'] .= - 'display: block; margin-left: auto; margin-right: auto;'; - } else { - // add a float style to the existing style - $options['attr']['style'] .= - 'float: '.$options['attr']['align']; - } - - // unset so it won't show up as an attribute - unset($options['attr']['align']); - } - - // stephane@metacites.net -- 25/07/2004 - // try to guess width and height - if (! isset($options['attr']['width']) && - ! isset($options['attr']['height'])) { - - // does the source refer to a local file or a URL? - if (strpos($src,'://')) { - // is a URL link - $imageFile = $src; - } elseif ($src[0] == '.') { - // reg at dav-muz dot net -- 2005-03-07 - // is a local file on relative path. - $imageFile = $src; # ...don't do anything because it's perfect! - } else { - // is a local file on absolute path. - $imageFile = $_SERVER['DOCUMENT_ROOT'] . $src; - } - - // attempt to get the image size - $imageSize = @getimagesize($imageFile); - - if (is_array($imageSize)) { - $options['attr']['width'] = $imageSize[0]; - $options['attr']['height'] = $imageSize[1]; - } - - } - - // start the HTML output - $output = 'formatConf(' class="%s"', 'css'); - - // add the attributes to the output, and be sure to - // track whether or not we find an "alt" attribute - $alt = false; - foreach ($options['attr'] as $key => $val) { - - // track the 'alt' attribute - if (strtolower($key) == 'alt') { - $alt = true; - } - - // the 'class' attribute overrides the CSS class conf - if (strtolower($key) == 'class') { - $css = null; - } - - $key = $this->textEncode($key); - $val = $this->textEncode($val); - $output .= " $key=\"$val\""; - } - - // always add an "alt" attribute per Stephane Solliec - if (! $alt) { - $alt = $this->textEncode(basename($options['src'])); - $output .= " alt=\"$alt\""; - } - - // end the image tag with the automatic CSS class (if any) - $output .= "$css />"; - - // was the image clickable? - if ($href) { - // yes, add the href and return - $href = $this->textEncode($href); - $css = $this->formatConf(' class="%s"', 'css_link'); - $output = "$output"; - } - - return $output; - } -} -?> diff --git a/pear/Text/Wiki/Render/Xhtml/Include.php b/pear/Text/Wiki/Render/Xhtml/Include.php deleted file mode 100644 index a152c40..0000000 --- a/pear/Text/Wiki/Render/Xhtml/Include.php +++ /dev/null @@ -1,32 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version CVS: $Id: Include.php 191862 2005-07-30 08:03:29Z toggg $ - * @link http://pear.php.net/package/Text_Wiki - */ - -/** - * This class renders included maekup in XHTML. (empty) - * - * @category Text - * @package Text_Wiki - * @author Paul M. Jones - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version Release: @package_version@ - * @link http://pear.php.net/package/Text_Wiki - */ -class Text_Wiki_Render_Xhtml_Include extends Text_Wiki_Render { - function token() - { - return ''; - } -} -?> diff --git a/pear/Text/Wiki/Render/Xhtml/Interwiki.php b/pear/Text/Wiki/Render/Xhtml/Interwiki.php deleted file mode 100644 index 9686b62..0000000 --- a/pear/Text/Wiki/Render/Xhtml/Interwiki.php +++ /dev/null @@ -1,103 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version CVS: $Id: Interwiki.php 231896 2007-03-15 00:08:47Z justinpatrin $ - * @link http://pear.php.net/package/Text_Wiki - */ - -/** - * This class renders inter wikis links in XHTML. - * - * @category Text - * @package Text_Wiki - * @author Paul M. Jones - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version Release: @package_version@ - * @link http://pear.php.net/package/Text_Wiki - */ -class Text_Wiki_Render_Xhtml_Interwiki extends Text_Wiki_Render { - - var $conf = array( - 'sites' => array( - 'MeatBall' => 'http://www.usemod.com/cgi-bin/mb.pl?%s', - 'Advogato' => 'http://advogato.org/%s', - 'Wiki' => 'http://c2.com/cgi/wiki?%s' - ), - 'target' => '_blank', - 'css' => null - ); - - - /** - * - * Renders a token into text matching the requested format. - * - * @access public - * - * @param array $options The "options" portion of the token (second - * element). - * - * @return string The text rendered from the token options. - * - */ - - function token($options) - { - $text = $options['text']; - if (isset($options['url'])) { - // calculated by the parser (e.g. Mediawiki) - $href = $options['url']; - } else { - $site = $options['site']; - // toggg 2006/02/05 page name must be url encoded (e.g. may contain spaces) - $page = $this->urlEncode($options['page']); - - if (isset($this->conf['sites'][$site])) { - $href = $this->conf['sites'][$site]; - } else { - return $text; - } - - // old form where page is at end, - // or new form with %s placeholder for sprintf()? - if (strpos($href, '%s') === false) { - // use the old form - $href = $href . $page; - } else { - // use the new form - $href = sprintf($href, $page); - } - } - - // allow for alternative targets - $target = $this->getConf('target'); - - // build base link - $css = $this->formatConf(' class="%s"', 'css'); - $text = $this->textEncode($text); - $output = "textEncode($target); - $output .= " onclick=\"window.open(this.href, '$target');"; - $output .= " return false;\""; - } - - $output .= ">$text"; - - return $output; - } -} -?> diff --git a/pear/Text/Wiki/Render/Xhtml/Italic.php b/pear/Text/Wiki/Render/Xhtml/Italic.php deleted file mode 100644 index b5cbed5..0000000 --- a/pear/Text/Wiki/Render/Xhtml/Italic.php +++ /dev/null @@ -1,57 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version CVS: $Id: Italic.php 191862 2005-07-30 08:03:29Z toggg $ - * @link http://pear.php.net/package/Text_Wiki - */ - -/** - * This class renders italic text in XHTML. - * - * @category Text - * @package Text_Wiki - * @author Paul M. Jones - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version Release: @package_version@ - * @link http://pear.php.net/package/Text_Wiki - */ -class Text_Wiki_Render_Xhtml_Italic extends Text_Wiki_Render { - - var $conf = array( - 'css' => null - ); - - /** - * - * Renders a token into text matching the requested format. - * - * @access public - * - * @param array $options The "options" portion of the token (second - * element). - * - * @return string The text rendered from the token options. - * - */ - - function token($options) - { - if ($options['type'] == 'start') { - $css = $this->formatConf(' class="%s"', 'css'); - return ""; - } - - if ($options['type'] == 'end') { - return ''; - } - } -} -?> diff --git a/pear/Text/Wiki/Render/Xhtml/List.php b/pear/Text/Wiki/Render/Xhtml/List.php deleted file mode 100644 index 8ec06cb..0000000 --- a/pear/Text/Wiki/Render/Xhtml/List.php +++ /dev/null @@ -1,172 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version CVS: $Id: List.php 200073 2005-11-06 10:38:25Z toggg $ - * @link http://pear.php.net/package/Text_Wiki - */ - -/** - * This class renders bullet and ordered lists in XHTML. - * - * @category Text - * @package Text_Wiki - * @author Paul M. Jones - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version Release: @package_version@ - * @link http://pear.php.net/package/Text_Wiki - */ -class Text_Wiki_Render_Xhtml_List extends Text_Wiki_Render { - - var $conf = array( - 'css_ol' => null, - 'css_ol_li' => null, - 'css_ul' => null, - 'css_ul_li' => null - ); - - /** - * - * Renders a token into text matching the requested format. - * - * This rendering method is syntactically and semantically compliant - * with XHTML 1.1 in that sub-lists are part of the previous list item. - * - * @access public - * - * @param array $options The "options" portion of the token (second - * element). - * - * @return string The text rendered from the token options. - * - */ - - function token($options) - { - // make nice variables (type, level, count) - extract($options); - - // set up indenting so that the results look nice; we do this - // in two steps to avoid str_pad mathematics. ;-) - $pad = str_pad('', $level, "\t"); - $pad = str_replace("\t", ' ', $pad); - - switch ($type) { - - case 'bullet_list_start': - - // build the base HTML - $css = $this->formatConf(' class="%s"', 'css_ul'); - $html = ""; - - /* - // if this is the opening block for the list, - // put an extra newline in front of it so the - // output looks nice. - if ($level == 0) { - $html = "\n$html"; - } - */ - - // done! - return $html; - break; - - case 'bullet_list_end': - - // build the base HTML - $html = "\n$pad"; - - // if this is the closing block for the list, - // put extra newlines after it so the output - // looks nice. - if ($level == 0) { - $html .= "\n\n"; - } - - // done! - return $html; - break; - - case 'number_list_start': - if (isset($format)) { - $format = ' type="' . $format . '"'; - } else { - $format = ''; - } - // build the base HTML - $css = $this->formatConf(' class="%s"', 'css_ol'); - $html = ""; - - /* - // if this is the opening block for the list, - // put an extra newline in front of it so the - // output looks nice. - if ($level == 0) { - $html = "\n$html"; - } - */ - - // done! - return $html; - break; - - case 'number_list_end': - - // build the base HTML - $html = "\n$pad"; - - // if this is the closing block for the list, - // put extra newlines after it so the output - // looks nice. - if ($level == 0) { - $html .= "\n\n"; - } - - // done! - return $html; - break; - - case 'bullet_item_start': - case 'number_item_start': - - // pick the proper CSS class - if ($type == 'bullet_item_start') { - $css = $this->formatConf(' class="%s"', 'css_ul_li'); - } else { - $css = $this->formatConf(' class="%s"', 'css_ol_li'); - } - - // build the base HTML - $html = "\n$pad"; - - // for the very first item in the list, do nothing. - // but for additional items, be sure to close the - // previous item. - if ($count > 0) { - $html = "$html"; - } - - // done! - return $html; - break; - - case 'bullet_item_end': - case 'number_item_end': - default: - // ignore item endings and all other types. - // item endings are taken care of by the other types - // depending on their place in the list. - return ''; - break; - } - } -} -?> diff --git a/pear/Text/Wiki/Render/Xhtml/Newline.php b/pear/Text/Wiki/Render/Xhtml/Newline.php deleted file mode 100644 index 19676c6..0000000 --- a/pear/Text/Wiki/Render/Xhtml/Newline.php +++ /dev/null @@ -1,35 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version CVS: $Id: Newline.php 191862 2005-07-30 08:03:29Z toggg $ - * @link http://pear.php.net/package/Text_Wiki - */ - -/** - * This class renders new lines in XHTML. - * - * @category Text - * @package Text_Wiki - * @author Paul M. Jones - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version Release: @package_version@ - * @link http://pear.php.net/package/Text_Wiki - */ -class Text_Wiki_Render_Xhtml_Newline extends Text_Wiki_Render { - - - function token($options) - { - return "
    \n"; - } -} - -?> diff --git a/pear/Text/Wiki/Render/Xhtml/Page.php b/pear/Text/Wiki/Render/Xhtml/Page.php deleted file mode 100644 index d9f638b..0000000 --- a/pear/Text/Wiki/Render/Xhtml/Page.php +++ /dev/null @@ -1,46 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version CVS: $Id: Page.php 191862 2005-07-30 08:03:29Z toggg $ - * @link http://pear.php.net/package/Text_Wiki - */ - -/** - * This class renders page markers in XHTML. - * - * @category Text - * @package Text_Wiki - * @author Paul M. Jones - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version Release: @package_version@ - * @link http://pear.php.net/package/Text_Wiki - */ -class Text_Wiki_Render_Xhtml_Page extends Text_Wiki_Render { - - /** - * - * Renders a token into text matching the requested format. - * - * @access public - * - * @param array $options The "options" portion of the token (second - * element). - * - * @return string The text rendered from the token options. - * - */ - - function token($options) - { - return 'PAGE MARKER HERE*&^%$#^$%*PAGEMARKERHERE'; - } -} -?> diff --git a/pear/Text/Wiki/Render/Xhtml/Paragraph.php b/pear/Text/Wiki/Render/Xhtml/Paragraph.php deleted file mode 100644 index fc73627..0000000 --- a/pear/Text/Wiki/Render/Xhtml/Paragraph.php +++ /dev/null @@ -1,59 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version CVS: $Id: Paragraph.php 191862 2005-07-30 08:03:29Z toggg $ - * @link http://pear.php.net/package/Text_Wiki - */ - -/** - * This class renders paragraphs in XHTML. - * - * @category Text - * @package Text_Wiki - * @author Paul M. Jones - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version Release: @package_version@ - * @link http://pear.php.net/package/Text_Wiki - */ -class Text_Wiki_Render_Xhtml_Paragraph extends Text_Wiki_Render { - - var $conf = array( - 'css' => null - ); - - /** - * - * Renders a token into text matching the requested format. - * - * @access public - * - * @param array $options The "options" portion of the token (second - * element). - * - * @return string The text rendered from the token options. - * - */ - - function token($options) - { - extract($options); //type - - if ($type == 'start') { - $css = $this->formatConf(' class="%s"', 'css'); - return ""; - } - - if ($type == 'end') { - return "

    \n\n"; - } - } -} -?> diff --git a/pear/Text/Wiki/Render/Xhtml/Phplookup.php b/pear/Text/Wiki/Render/Xhtml/Phplookup.php deleted file mode 100644 index e77b0b7..0000000 --- a/pear/Text/Wiki/Render/Xhtml/Phplookup.php +++ /dev/null @@ -1,81 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version CVS: $Id: Phplookup.php 231896 2007-03-15 00:08:47Z justinpatrin $ - * @link http://pear.php.net/package/Text_Wiki - */ - -/** - * This class renders a link to php functions description in XHTML. - * - * @category Text - * @package Text_Wiki - * @author Paul M. Jones - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version Release: @package_version@ - * @link http://pear.php.net/package/Text_Wiki - */ -class Text_Wiki_Render_Xhtml_Phplookup extends Text_Wiki_Render { - - var $conf = array( - 'target' => '_blank', - 'css' => null - ); - - - /** - * - * Renders a token into text matching the requested format. - * - * @access public - * - * @param array $options The "options" portion of the token (second - * element). - * - * @return string The text rendered from the token options. - * - */ - - function token($options) - { - $text = trim($options['text']); - $css = $this->formatConf(' class="%s"', 'css'); - - // start the html - $output = "getConf('target', ''); - if ($target && $target != '_self') { - // use a "popup" window. this is XHTML compliant, suggested by - // Aaron Kalin. uses the $target as the new window name. - $target = $this->textEncode($target); - $output .= " onclick=\"window.open(this.href, '$target');"; - $output .= " return false;\""; - } - - // take off the final parens for functions - if (substr($text, -2) == '()') { - $q = substr($text, 0, -2); - } else { - $q = $text; - } - - // toggg 2006/02/05 page name must be url encoded (e.g. may contain spaces) - $q = $this->urlEncode($q); - $text = $this->textEncode($text); - - // finish and return - $output .= " href=\"http://php.net/$q\">$text"; - return $output; - } -} -?> diff --git a/pear/Text/Wiki/Render/Xhtml/Plugin.php b/pear/Text/Wiki/Render/Xhtml/Plugin.php deleted file mode 100644 index 048ba80..0000000 --- a/pear/Text/Wiki/Render/Xhtml/Plugin.php +++ /dev/null @@ -1,47 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version CVS: $Id: Plugin.php 191862 2005-07-30 08:03:29Z toggg $ - * @link http://pear.php.net/package/Text_Wiki - */ - -/** - * This class renders wiki plugins in XHTML. (empty) - * - * @category Text - * @package Text_Wiki - * @author Paul M. Jones - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version Release: @package_version@ - * @link http://pear.php.net/package/Text_Wiki - */ -class Text_Wiki_Render_Xhtml_Plugin extends Text_Wiki_Render { - - /** - * - * Renders a token into text matching the requested format. - * Plugins produce wiki markup so are processed by parsing, no tokens produced - * - * @access public - * - * @param array $options The "options" portion of the token (second - * element). - * - * @return string The text rendered from the token options. - * - */ - - function token($options) - { - return ''; - } -} -?> diff --git a/pear/Text/Wiki/Render/Xhtml/Prefilter.php b/pear/Text/Wiki/Render/Xhtml/Prefilter.php deleted file mode 100644 index b0ae26e..0000000 --- a/pear/Text/Wiki/Render/Xhtml/Prefilter.php +++ /dev/null @@ -1,34 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version CVS: $Id: Prefilter.php 191862 2005-07-30 08:03:29Z toggg $ - * @link http://pear.php.net/package/Text_Wiki - */ - -/** - * This class implements a Text_Wiki_Render_Xhtml to "pre-filter" source text so - * that line endings are consistently \n, lines ending in a backslash \ - * are concatenated with the next line, and tabs are converted to spaces. - * - * @category Text - * @package Text_Wiki - * @author Paul M. Jones - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version Release: @package_version@ - * @link http://pear.php.net/package/Text_Wiki - */ -class Text_Wiki_Render_Xhtml_Prefilter extends Text_Wiki_Render { - function token() - { - return ''; - } -} -?> diff --git a/pear/Text/Wiki/Render/Xhtml/Preformatted.php b/pear/Text/Wiki/Render/Xhtml/Preformatted.php deleted file mode 100644 index a68ce76..0000000 --- a/pear/Text/Wiki/Render/Xhtml/Preformatted.php +++ /dev/null @@ -1,47 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version CVS: $Id: Preformatted.php 229275 2007-02-07 13:40:44Z mic $ - * @link http://pear.php.net/package/Text_Wiki - */ - -/** - * This class renders preformated text in XHTML. - * - * @category Text - * @package Text_Wiki - * @author Paul M. Jones - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version Release: @package_version@ - * @link http://pear.php.net/package/Text_Wiki - */ -class Text_Wiki_Render_Xhtml_Preformatted extends Text_Wiki_Render { - - /** - * - * Renders a token into text matching the requested format. - * - * @access public - * - * @param array $options The "options" portion of the token (second - * element). - * - * @return string The text rendered from the token options. - * - */ - - function token($options) - { - $text = $this->textEncode($options['text']); - return '
    '.$text.'
    '; - } -} -?> diff --git a/pear/Text/Wiki/Render/Xhtml/Raw.php b/pear/Text/Wiki/Render/Xhtml/Raw.php deleted file mode 100644 index 3027153..0000000 --- a/pear/Text/Wiki/Render/Xhtml/Raw.php +++ /dev/null @@ -1,46 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version CVS: $Id: Raw.php 214538 2006-06-09 21:32:24Z justinpatrin $ - * @link http://pear.php.net/package/Text_Wiki - */ - -/** - * This class renders not processed blocks in XHTML. - * - * @category Text - * @package Text_Wiki - * @author Paul M. Jones - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version Release: @package_version@ - * @link http://pear.php.net/package/Text_Wiki - */ -class Text_Wiki_Render_Xhtml_Raw extends Text_Wiki_Render { - - /** - * - * Renders a token into text matching the requested format. - * - * @access public - * - * @param array $options The "options" portion of the token (second - * element). - * - * @return string The text rendered from the token options. - * - */ - - function token($options) - { - return $this->textEncode($options['text']); - } -} -?> diff --git a/pear/Text/Wiki/Render/Xhtml/Revise.php b/pear/Text/Wiki/Render/Xhtml/Revise.php deleted file mode 100644 index f81fd7f..0000000 --- a/pear/Text/Wiki/Render/Xhtml/Revise.php +++ /dev/null @@ -1,68 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version CVS: $Id: Revise.php 191862 2005-07-30 08:03:29Z toggg $ - * @link http://pear.php.net/package/Text_Wiki - */ - -/** - * This class renders revision marks in XHTML. - * - * @category Text - * @package Text_Wiki - * @author Paul M. Jones - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version Release: @package_version@ - * @link http://pear.php.net/package/Text_Wiki - */ -class Text_Wiki_Render_Xhtml_Revise extends Text_Wiki_Render { - - var $conf = array( - 'css_ins' => null, - 'css_del' => null - ); - - - /** - * - * Renders a token into text matching the requested format. - * - * @access public - * - * @param array $options The "options" portion of the token (second - * element). - * - * @return string The text rendered from the token options. - * - */ - - function token($options) - { - if ($options['type'] == 'del_start') { - $css = $this->formatConf(' class="%s"', 'css_del'); - return ""; - } - - if ($options['type'] == 'del_end') { - return ""; - } - - if ($options['type'] == 'ins_start') { - $css = $this->formatConf(' class="%s"', 'css_ins'); - return ""; - } - - if ($options['type'] == 'ins_end') { - return ""; - } - } -} -?> diff --git a/pear/Text/Wiki/Render/Xhtml/Smiley.php b/pear/Text/Wiki/Render/Xhtml/Smiley.php deleted file mode 100644 index 778796b..0000000 --- a/pear/Text/Wiki/Render/Xhtml/Smiley.php +++ /dev/null @@ -1,74 +0,0 @@ - - * @copyright 2005 bertrand Gugger - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version CVS: $Id: Smiley.php 206940 2006-02-10 23:07:03Z toggg $ - * @link http://pear.php.net/package/Text_Wiki - */ - -/** - * Smiley rule Xhtml render class - * - * @category Text - * @package Text_Wiki - * @author Bertrand Gugger - * @copyright 2005 bertrand Gugger - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version Release: @package_version@ - * @link http://pear.php.net/package/Text_Wiki - * @see Text_Wiki::Text_Wiki_Render() - */ -class Text_Wiki_Render_Xhtml_Smiley extends Text_Wiki_Render { - - /** - * Configuration keys for this rule - * 'prefix' => the path to smileys images inclusive file name prefix, - * starts with '/' ==> abolute reference - * if no file names prefix but some folder, terminates with '/' - * 'extension' => the file extension (inclusive '.'), e.g. : - * if prefix 'smileys/icon_' and extension '.gif' - * ':)' whose name is 'smile' will give relative file 'smileys/icon_smile.gif' - * if prefix '/image/smileys/' and extension '.png': absolute '/image/smileys/smile.gif' - * 'css' => optional style applied to smileys - * - * @access public - * @var array 'config-key' => mixed config-value - */ - var $conf = array( - 'prefix' => 'images/smiles/icon_', - 'extension' => '.gif', - 'css' => null - ); - - /** - * Renders a token into text matching the requested format. - * process the Smileys - * - * @access public - * @param array $options The "options" portion of the token (second element). - * @return string The text rendered from the token options. - */ - function token($options) - { - $imageFile = $this->getConf('prefix') . $options['name'] . $this->getConf('extension'); - - // attempt to get the image size - $imageSize = @getimagesize($imageFile); - - // return the HTML output - return '' . $options['desc'] . 'formatConf(' class="%s"', 'css') . ' />'; - } -} -?> diff --git a/pear/Text/Wiki/Render/Xhtml/Specialchar.php b/pear/Text/Wiki/Render/Xhtml/Specialchar.php deleted file mode 100644 index 4372c02..0000000 --- a/pear/Text/Wiki/Render/Xhtml/Specialchar.php +++ /dev/null @@ -1,52 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version CVS: $Id: Specialchar.php 191862 2005-07-30 08:03:29Z toggg $ - * @link http://pear.php.net/package/Text_Wiki - */ - -/** - * This class renders special characters in XHTML. - * - * @category Text - * @package Text_Wiki - * @author Paul M. Jones - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version Release: @package_version@ - * @link http://pear.php.net/package/Text_Wiki - */ -class Text_Wiki_Render_Xhtml_SpecialChar extends Text_Wiki_Render { - - var $types = array('~bs~' => '\', - '~hs~' => ' ', - '~amp~' => '&', - '~ldq~' => '“', - '~rdq~' => '”', - '~lsq~' => '‘', - '~rsq~' => '’', - '~c~' => '©', - '~--~' => '—', - '" -- "' => '—', - '" -- "' => '—', - '~lt~' => '<', - '~gt~' => '>'); - - function token($options) - { - if (isset($this->types[$options['char']])) { - return $this->types[$options['char']]; - } else { - return '&#'.substr($options['char'], 1, -1).';'; - } - } -} - -?> diff --git a/pear/Text/Wiki/Render/Xhtml/Strong.php b/pear/Text/Wiki/Render/Xhtml/Strong.php deleted file mode 100644 index da816a7..0000000 --- a/pear/Text/Wiki/Render/Xhtml/Strong.php +++ /dev/null @@ -1,58 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version CVS: $Id: Strong.php 191862 2005-07-30 08:03:29Z toggg $ - * @link http://pear.php.net/package/Text_Wiki - */ - -/** - * This class renders text marked as strong in XHTML. - * - * @category Text - * @package Text_Wiki - * @author Paul M. Jones - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version Release: @package_version@ - * @link http://pear.php.net/package/Text_Wiki - */ -class Text_Wiki_Render_Xhtml_Strong extends Text_Wiki_Render { - - - var $conf = array( - 'css' => null - ); - - /** - * - * Renders a token into text matching the requested format. - * - * @access public - * - * @param array $options The "options" portion of the token (second - * element). - * - * @return string The text rendered from the token options. - * - */ - - function token($options) - { - if ($options['type'] == 'start') { - $css = $this->formatConf(' class="%s"', 'css'); - return ""; - } - - if ($options['type'] == 'end') { - return ''; - } - } -} -?> diff --git a/pear/Text/Wiki/Render/Xhtml/Subscript.php b/pear/Text/Wiki/Render/Xhtml/Subscript.php deleted file mode 100644 index 653a110..0000000 --- a/pear/Text/Wiki/Render/Xhtml/Subscript.php +++ /dev/null @@ -1,57 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version CVS: $Id: Subscript.php 191862 2005-07-30 08:03:29Z toggg $ - * @link http://pear.php.net/package/Text_Wiki - */ - -/** - * This class renders subscript text in XHTML. - * - * @category Text - * @package Text_Wiki - * @author Paul M. Jones - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version Release: @package_version@ - * @link http://pear.php.net/package/Text_Wiki - */ -class Text_Wiki_Render_Xhtml_Subscript extends Text_Wiki_Render { - - var $conf = array( - 'css' => null - ); - - /** - * - * Renders a token into text matching the requested format. - * - * @access public - * - * @param array $options The "options" portion of the token (second - * element). - * - * @return string The text rendered from the token options. - * - */ - - function token($options) - { - if ($options['type'] == 'start') { - $css = $this->formatConf(' class="%s"', 'css'); - return ""; - } - - if ($options['type'] == 'end') { - return ''; - } - } -} -?> diff --git a/pear/Text/Wiki/Render/Xhtml/Superscript.php b/pear/Text/Wiki/Render/Xhtml/Superscript.php deleted file mode 100644 index 1a39453..0000000 --- a/pear/Text/Wiki/Render/Xhtml/Superscript.php +++ /dev/null @@ -1,57 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version CVS: $Id: Superscript.php 191862 2005-07-30 08:03:29Z toggg $ - * @link http://pear.php.net/package/Text_Wiki - */ - -/** - * This class renders superscript text in XHTML. - * - * @category Text - * @package Text_Wiki - * @author Paul M. Jones - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version Release: @package_version@ - * @link http://pear.php.net/package/Text_Wiki - */ -class Text_Wiki_Render_Xhtml_Superscript extends Text_Wiki_Render { - - var $conf = array( - 'css' => null - ); - - /** - * - * Renders a token into text matching the requested format. - * - * @access public - * - * @param array $options The "options" portion of the token (second - * element). - * - * @return string The text rendered from the token options. - * - */ - - function token($options) - { - if ($options['type'] == 'start') { - $css = $this->formatConf(' class="%s"', 'css'); - return ""; - } - - if ($options['type'] == 'end') { - return ''; - } - } -} -?> diff --git a/pear/Text/Wiki/Render/Xhtml/Table.php b/pear/Text/Wiki/Render/Xhtml/Table.php deleted file mode 100644 index 670ff7b..0000000 --- a/pear/Text/Wiki/Render/Xhtml/Table.php +++ /dev/null @@ -1,140 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version CVS: $Id: Table.php 202250 2005-12-06 15:29:29Z ritzmo $ - * @link http://pear.php.net/package/Text_Wiki - */ - -/** - * This class renders tables in XHTML. - * - * @category Text - * @package Text_Wiki - * @author Paul M. Jones - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version Release: @package_version@ - * @link http://pear.php.net/package/Text_Wiki - */ -class Text_Wiki_Render_Xhtml_Table extends Text_Wiki_Render { - - var $conf = array( - 'css_table' => null, - 'css_caption' => null, - 'css_tr' => null, - 'css_th' => null, - 'css_td' => null - ); - - - /** - * - * Renders a token into text matching the requested format. - * - * @access public - * - * @param array $options The "options" portion of the token (second - * element). - * - * @return string The text rendered from the token options. - * - */ - - function token($options) - { - // make nice variable names (type, attr, span) - $span = $rowspan = 1; - extract($options); - - // free format - $format = isset($format) ? ' '. $format : ''; - - $pad = ' '; - - switch ($type) { - - case 'table_start': - $css = $this->formatConf(' class="%s"', 'css_table'); - return "\n\n\n"; - break; - - case 'table_end': - return "\n\n"; - break; - - case 'caption_start': - $css = $this->formatConf(' class="%s"', 'css_caption'); - return "\n"; - break; - - case 'caption_end': - return "\n"; - break; - - case 'row_start': - $css = $this->formatConf(' class="%s"', 'css_tr'); - return "$pad\n"; - break; - - case 'row_end': - return "$pad\n"; - break; - - case 'cell_start': - - // base html - $html = $pad . $pad; - - // is this a TH or TD cell? - if ($attr == 'header') { - // start a header cell - $css = $this->formatConf(' class="%s"', 'css_th'); - $html .= "formatConf(' class="%s"', 'css_td'); - $html .= " 1) { - $html .= " colspan=\"$span\""; - } - - // add the row span - if ($rowspan > 1) { - $html .= " rowspan=\"$rowspan\""; - } - - // add alignment - if ($attr != 'header' && $attr != '') { - $html .= " style=\"text-align: $attr;\""; - } - - // done! - $html .= "$format>"; - return $html; - break; - - case 'cell_end': - if ($attr == 'header') { - return "\n"; - } else { - return "\n"; - } - break; - - default: - return ''; - - } - } -} -?> diff --git a/pear/Text/Wiki/Render/Xhtml/Tighten.php b/pear/Text/Wiki/Render/Xhtml/Tighten.php deleted file mode 100644 index 30d6dfd..0000000 --- a/pear/Text/Wiki/Render/Xhtml/Tighten.php +++ /dev/null @@ -1,34 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version CVS: $Id: Tighten.php 191862 2005-07-30 08:03:29Z toggg $ - * @link http://pear.php.net/package/Text_Wiki - */ - -/** - * This class makes the tightening in XHTML. (empty) - * - * @category Text - * @package Text_Wiki - * @author Paul M. Jones - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version Release: @package_version@ - * @link http://pear.php.net/package/Text_Wiki - */ -class Text_Wiki_Render_Xhtml_Tighten extends Text_Wiki_Render { - - - function token() - { - return ''; - } -} -?> diff --git a/pear/Text/Wiki/Render/Xhtml/Titlebar.php b/pear/Text/Wiki/Render/Xhtml/Titlebar.php deleted file mode 100644 index d6e24a8..0000000 --- a/pear/Text/Wiki/Render/Xhtml/Titlebar.php +++ /dev/null @@ -1,57 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version CVS: $Id: Titlebar.php 191862 2005-07-30 08:03:29Z toggg $ - * @link http://pear.php.net/package/Text_Wiki - */ - -/** - * This class renders a title bar in XHTML. - * - * @category Text - * @package Text_Wiki - * @author Paul M. Jones - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version Release: @package_version@ - * @link http://pear.php.net/package/Text_Wiki - */ -class Text_Wiki_Render_Xhtml_Titlebar extends Text_Wiki_Render { - - var $conf = array( - 'css' => 'titlebar' - ); - - /** - * - * Renders a token into text matching the requested format. - * - * @access public - * - * @param array $options The "options" portion of the token (second - * element). - * - * @return string The text rendered from the token options. - * - */ - - function token($options) - { - if ($options['type'] == 'start') { - $css = $this->formatConf(' class="%s"', 'css'); - return ""; - } - - if ($options['type'] == 'end') { - return '
    '; - } - } -} -?> diff --git a/pear/Text/Wiki/Render/Xhtml/Toc.php b/pear/Text/Wiki/Render/Xhtml/Toc.php deleted file mode 100644 index 2b68b68..0000000 --- a/pear/Text/Wiki/Render/Xhtml/Toc.php +++ /dev/null @@ -1,115 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version CVS: $Id: Toc.php 191862 2005-07-30 08:03:29Z toggg $ - * @link http://pear.php.net/package/Text_Wiki - */ - -/** - * This class inserts a table of content in XHTML. - * - * @category Text - * @package Text_Wiki - * @author Paul M. Jones - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version Release: @package_version@ - * @link http://pear.php.net/package/Text_Wiki - */ -class Text_Wiki_Render_Xhtml_Toc extends Text_Wiki_Render { - - var $conf = array( - 'css_list' => null, - 'css_item' => null, - 'title' => 'Table of Contents', - 'div_id' => 'toc', - 'collapse' => true - ); - - var $min = 2; - - /** - * - * Renders a token into text matching the requested format. - * - * @access public - * - * @param array $options The "options" portion of the token (second - * element). - * - * @return string The text rendered from the token options. - * - */ - - function token($options) - { - // type, id, level, count, attr - extract($options); - - switch ($type) { - - case 'list_start': - - $css = $this->getConf('css_list'); - $html = ''; - - // collapse div within a table? - if ($this->getConf('collapse')) { - $html .= ''; - $html .= "
    \n"; - } - - // add the div, class, and id - $html .= 'getConf('div_id'); - if ($div_id) { - $html .= " id=\"$div_id\""; - } - - // add the title, and done - $html .= '>'; - $html .= $this->getConf('title'); - return $html; - break; - - case 'list_end': - if ($this->getConf('collapse')) { - return "\n\n
    \n\n"; - } else { - return "\n
    \n\n"; - } - break; - - case 'item_start': - $html = "\n\tgetConf('css_item'); - if ($css) { - $html .= " class=\"$css\""; - } - - $pad = ($level - $this->min); - $html .= " style=\"margin-left: {$pad}em;\">"; - - $html .= ""; - return $html; - break; - - case 'item_end': - return "
    "; - break; - } - } -} -?> diff --git a/pear/Text/Wiki/Render/Xhtml/Tt.php b/pear/Text/Wiki/Render/Xhtml/Tt.php deleted file mode 100644 index 3cefe85..0000000 --- a/pear/Text/Wiki/Render/Xhtml/Tt.php +++ /dev/null @@ -1,58 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version CVS: $Id: Tt.php 191862 2005-07-30 08:03:29Z toggg $ - * @link http://pear.php.net/package/Text_Wiki - */ - -/** - * This class renders monospaced text in XHTML. - * - * @category Text - * @package Text_Wiki - * @author Paul M. Jones - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version Release: @package_version@ - * @link http://pear.php.net/package/Text_Wiki - */ -class Text_Wiki_Render_Xhtml_Tt extends Text_Wiki_Render { - - - var $conf = array( - 'css' => null - ); - - /** - * - * Renders a token into text matching the requested format. - * - * @access public - * - * @param array $options The "options" portion of the token (second - * element). - * - * @return string The text rendered from the token options. - * - */ - - function token($options) - { - if ($options['type'] == 'start') { - $css = $this->formatConf(' class="%s"', 'css'); - return ""; - } - - if ($options['type'] == 'end') { - return ''; - } - } -} -?> diff --git a/pear/Text/Wiki/Render/Xhtml/Underline.php b/pear/Text/Wiki/Render/Xhtml/Underline.php deleted file mode 100644 index a6cbf59..0000000 --- a/pear/Text/Wiki/Render/Xhtml/Underline.php +++ /dev/null @@ -1,57 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version CVS: $Id: Underline.php 191862 2005-07-30 08:03:29Z toggg $ - * @link http://pear.php.net/package/Text_Wiki - */ - -/** - * This class renders underlined text in XHTML. - * - * @category Text - * @package Text_Wiki - * @author Paul M. Jones - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version Release: @package_version@ - * @link http://pear.php.net/package/Text_Wiki - */ -class Text_Wiki_Render_Xhtml_Underline extends Text_Wiki_Render { - - var $conf = array( - 'css' => null - ); - - /** - * - * Renders a token into text matching the requested format. - * - * @access public - * - * @param array $options The "options" portion of the token (second - * element). - * - * @return string The text rendered from the token options. - * - */ - - function token($options) - { - if ($options['type'] == 'start') { - $css = $this->formatConf(' class="%s"', 'css'); - return ""; - } - - if ($options['type'] == 'end') { - return ''; - } - } -} -?> diff --git a/pear/Text/Wiki/Render/Xhtml/Url.php b/pear/Text/Wiki/Render/Xhtml/Url.php deleted file mode 100644 index 9139be7..0000000 --- a/pear/Text/Wiki/Render/Xhtml/Url.php +++ /dev/null @@ -1,131 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version CVS: $Id: Url.php 236400 2007-05-26 17:15:41Z mic $ - * @link http://pear.php.net/package/Text_Wiki - */ - -/** - * This class renders URL links in XHTML. - * - * @category Text - * @package Text_Wiki - * @author Paul M. Jones - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version Release: @package_version@ - * @link http://pear.php.net/package/Text_Wiki - */ -class Text_Wiki_Render_Xhtml_Url extends Text_Wiki_Render { - - - var $conf = array( - 'target' => '_blank', - 'images' => true, - 'img_ext' => array('jpg', 'jpeg', 'gif', 'png'), - 'css_inline' => null, - 'css_footnote' => null, - 'css_descr' => null, - 'css_img' => null - ); - - /** - * - * Renders a token into text matching the requested format. - * - * @access public - * - * @param array $options The "options" portion of the token (second - * element). - * - * @return string The text rendered from the token options. - * - */ - - function token($options) - { - // create local variables from the options array (text, - // href, type) - extract($options); - - // find the rightmost dot and determine the filename - // extension. - $pos = strrpos($href, '.'); - $ext = strtolower(substr($href, $pos + 1)); - $href = $this->textEncode($href); - - // does the filename extension indicate an image file? - if ($this->getConf('images') && - in_array($ext, $this->getConf('img_ext', array()))) { - - // create alt text for the image - if (! isset($text) || $text == '') { - $text = basename($href); - $text = $this->textEncode($text); - } - - // generate an image tag - $css = $this->formatConf(' class="%s"', 'css_img'); - $start = ""; - - } else { - - // should we build a target clause? - if ($href{0} == '#' || - strtolower(substr($href, 0, 7)) == 'mailto:') { - // targets not allowed for on-page anchors - // and mailto: links. - $target = ''; - } else { - // allow targets on non-anchor non-mailto links - $target = $this->getConf('target'); - } - - // generate a regular link (not an image) - $text = $this->textEncode($text); - $css = $this->formatConf(' class="%s"', "css_$type"); - $start = "textEncode($target); - $start .= " onclick=\"window.open(this.href, '$target');"; - $start .= " return false;\""; - } - - if (isset($name)) { - $start .= " id=\"$name\""; - } - - // finish up output - $start .= ">"; - $end = ""; - - // make numbered references look like footnotes when no - // CSS class specified, make them superscript by default - if ($type == 'footnote' && ! $css) { - $start = '' . $start; - $end = $end . ''; - } - } - - if ($options['type'] == 'start') { - $output = $start; - } else if ($options['type'] == 'end') { - $output = $end; - } else { - $output = $start . $text . $end; - } - return $output; - } -} -?> diff --git a/pear/Text/Wiki/Render/Xhtml/Wikilink.php b/pear/Text/Wiki/Render/Xhtml/Wikilink.php deleted file mode 100644 index b93b000..0000000 --- a/pear/Text/Wiki/Render/Xhtml/Wikilink.php +++ /dev/null @@ -1,177 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version CVS: $Id: Wikilink.php 224670 2006-12-08 21:25:24Z justinpatrin $ - * @link http://pear.php.net/package/Text_Wiki - */ - -/** - * This class renders wiki links in XHTML. - * - * @category Text - * @package Text_Wiki - * @author Paul M. Jones - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version Release: @package_version@ - * @link http://pear.php.net/package/Text_Wiki - */ -class Text_Wiki_Render_Xhtml_Wikilink extends Text_Wiki_Render { - - var $conf = array( - 'pages' => array(), // set to null or false to turn off page checks - 'view_url' => 'http://example.com/index.php?page=%s', - 'new_url' => 'http://example.com/new.php?page=%s', - 'new_text' => '?', - 'new_text_pos' => 'after', // 'before', 'after', or null/false - 'css' => null, - 'css_new' => null, - 'exists_callback' => null // call_user_func() callback - ); - - - /** - * - * Renders a token into XHTML. - * - * @access public - * - * @param array $options The "options" portion of the token (second - * element). - * - * @return string The text rendered from the token options. - * - */ - - function token($options) - { - // make nice variable names (page, anchor, text) - extract($options); - - // is there a "page existence" callback? - // we need to access it directly instead of through - // getConf() because we'll need a reference (for - // object instance method callbacks). - if (isset($this->conf['exists_callback'])) { - $callback =& $this->conf['exists_callback']; - } else { - $callback = false; - } - - if ($callback) { - // use the callback function - $exists = call_user_func($callback, $page); - } else { - // no callback, go to the naive page array. - $list = $this->getConf('pages'); - if (is_array($list)) { - // yes, check against the page list - $exists = in_array($page, $list); - } else { - // no, assume it exists - $exists = true; - } - } - - $anchor = '#'.$this->urlEncode(substr($anchor, 1)); - - // does the page exist? - if ($exists) { - - // PAGE EXISTS. - - // link to the page view, but we have to build - // the HREF. we support both the old form where - // the page always comes at the end, and the new - // form that uses %s for sprintf() - $href = $this->getConf('view_url'); - - if (strpos($href, '%s') === false) { - // use the old form (page-at-end) - $href = $href . $this->urlEncode($page) . $anchor; - } else { - // use the new form (sprintf format string) - $href = sprintf($href, $this->urlEncode($page)) . $anchor; - } - - // get the CSS class and generate output - $css = ' class="'.$this->textEncode($this->getConf('css')).'"'; - - $start = ''; - $end = ''; - } else { - - // PAGE DOES NOT EXIST. - - // link to a create-page url, but only if new_url is set - $href = $this->getConf('new_url', null); - - // set the proper HREF - if (! $href || trim($href) == '') { - - // no useful href, return the text as it is - //TODO: This is no longer used, need to look closer into this branch - $output = $text; - - } else { - - // yes, link to the new-page href, but we have to build - // it. we support both the old form where - // the page always comes at the end, and the new - // form that uses sprintf() - if (strpos($href, '%s') === false) { - // use the old form - $href = $href . $this->urlEncode($page); - } else { - // use the new form - $href = sprintf($href, $this->urlEncode($page)); - } - } - - // get the appropriate CSS class and new-link text - $css = ' class="'.$this->textEncode($this->getConf('css_new')).'"'; - $new = $this->getConf('new_text'); - - // what kind of linking are we doing? - $pos = $this->getConf('new_text_pos'); - if (! $pos || ! $new) { - // no position (or no new_text), use css only on the page name - - $start = ''; - $end = ''; - } elseif ($pos == 'before') { - // use the new_text BEFORE the page name - $start = ''.$this->textEncode($new).''; - $end = ''; - } else { - // default, use the new_text link AFTER the page name - $start = ''; - $end = ''.$this->textEncode($new).''; - } - } - if (!strlen($text)) { - $start .= $this->textEncode($page); - } - if (isset($type)) { - switch ($type) { - case 'start': - $output = $start; - break; - case 'end': - $output = $end; - break; - } - } else { - $output = $start.$this->textEncode($text).$end; - } - return $output; - } -} -?> diff --git a/pear/XML/Util.php b/pear/XML/Util.php deleted file mode 100644 index f5927b1..0000000 --- a/pear/XML/Util.php +++ /dev/null @@ -1,911 +0,0 @@ - - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @category XML - * @package XML_Util - * @author Stephan Schmidt - * @copyright 2003-2008 Stephan Schmidt - * @license http://opensource.org/licenses/bsd-license New BSD License - * @version CVS: $Id: Util.php,v 1.38 2008/11/13 00:03:38 ashnazg Exp $ - * @link http://pear.php.net/package/XML_Util - */ - -/** - * error code for invalid chars in XML name - */ -define('XML_UTIL_ERROR_INVALID_CHARS', 51); - -/** - * error code for invalid chars in XML name - */ -define('XML_UTIL_ERROR_INVALID_START', 52); - -/** - * error code for non-scalar tag content - */ -define('XML_UTIL_ERROR_NON_SCALAR_CONTENT', 60); - -/** - * error code for missing tag name - */ -define('XML_UTIL_ERROR_NO_TAG_NAME', 61); - -/** - * replace XML entities - */ -define('XML_UTIL_REPLACE_ENTITIES', 1); - -/** - * embedd content in a CData Section - */ -define('XML_UTIL_CDATA_SECTION', 5); - -/** - * do not replace entitites - */ -define('XML_UTIL_ENTITIES_NONE', 0); - -/** - * replace all XML entitites - * This setting will replace <, >, ", ' and & - */ -define('XML_UTIL_ENTITIES_XML', 1); - -/** - * replace only required XML entitites - * This setting will replace <, " and & - */ -define('XML_UTIL_ENTITIES_XML_REQUIRED', 2); - -/** - * replace HTML entitites - * @link http://www.php.net/htmlentities - */ -define('XML_UTIL_ENTITIES_HTML', 3); - -/** - * Collapse all empty tags. - */ -define('XML_UTIL_COLLAPSE_ALL', 1); - -/** - * Collapse only empty XHTML tags that have no end tag. - */ -define('XML_UTIL_COLLAPSE_XHTML_ONLY', 2); - -/** - * utility class for working with XML documents - * - - * @category XML - * @package XML_Util - * @author Stephan Schmidt - * @copyright 2003-2008 Stephan Schmidt - * @license http://opensource.org/licenses/bsd-license New BSD License - * @version Release: 1.2.1 - * @link http://pear.php.net/package/XML_Util - */ -class XML_Util -{ - /** - * return API version - * - * @return string $version API version - * @access public - * @static - */ - function apiVersion() - { - return '1.1'; - } - - /** - * replace XML entities - * - * With the optional second parameter, you may select, which - * entities should be replaced. - * - * - * require_once 'XML/Util.php'; - * - * // replace XML entites: - * $string = XML_Util::replaceEntities('This string contains < & >.'); - * - * - * With the optional third parameter, you may pass the character encoding - * - * require_once 'XML/Util.php'; - * - * // replace XML entites in UTF-8: - * $string = XML_Util::replaceEntities( - * 'This string contains < & > as well as ä, ö, ß, à and ê', - * XML_UTIL_ENTITIES_HTML, - * 'UTF-8' - * ); - * - * - * @param string $string string where XML special chars - * should be replaced - * @param int $replaceEntities setting for entities in attribute values - * (one of XML_UTIL_ENTITIES_XML, - * XML_UTIL_ENTITIES_XML_REQUIRED, - * XML_UTIL_ENTITIES_HTML) - * @param string $encoding encoding value (if any)... - * must be a valid encoding as determined - * by the htmlentities() function - * - * @return string string with replaced chars - * @access public - * @static - * @see reverseEntities() - */ - function replaceEntities($string, $replaceEntities = XML_UTIL_ENTITIES_XML, - $encoding = 'ISO-8859-1') - { - switch ($replaceEntities) { - case XML_UTIL_ENTITIES_XML: - return strtr($string, array( - '&' => '&', - '>' => '>', - '<' => '<', - '"' => '"', - '\'' => ''' )); - break; - case XML_UTIL_ENTITIES_XML_REQUIRED: - return strtr($string, array( - '&' => '&', - '<' => '<', - '"' => '"' )); - break; - case XML_UTIL_ENTITIES_HTML: - return htmlentities($string, ENT_COMPAT, $encoding); - break; - } - return $string; - } - - /** - * reverse XML entities - * - * With the optional second parameter, you may select, which - * entities should be reversed. - * - * - * require_once 'XML/Util.php'; - * - * // reverse XML entites: - * $string = XML_Util::reverseEntities('This string contains < & >.'); - * - * - * With the optional third parameter, you may pass the character encoding - * - * require_once 'XML/Util.php'; - * - * // reverse XML entites in UTF-8: - * $string = XML_Util::reverseEntities( - * 'This string contains < & > as well as' - * . ' ä, ö, ß, à and ê', - * XML_UTIL_ENTITIES_HTML, - * 'UTF-8' - * ); - * - * - * @param string $string string where XML special chars - * should be replaced - * @param int $replaceEntities setting for entities in attribute values - * (one of XML_UTIL_ENTITIES_XML, - * XML_UTIL_ENTITIES_XML_REQUIRED, - * XML_UTIL_ENTITIES_HTML) - * @param string $encoding encoding value (if any)... - * must be a valid encoding as determined - * by the html_entity_decode() function - * - * @return string string with replaced chars - * @access public - * @static - * @see replaceEntities() - */ - function reverseEntities($string, $replaceEntities = XML_UTIL_ENTITIES_XML, - $encoding = 'ISO-8859-1') - { - switch ($replaceEntities) { - case XML_UTIL_ENTITIES_XML: - return strtr($string, array( - '&' => '&', - '>' => '>', - '<' => '<', - '"' => '"', - ''' => '\'' )); - break; - case XML_UTIL_ENTITIES_XML_REQUIRED: - return strtr($string, array( - '&' => '&', - '<' => '<', - '"' => '"' )); - break; - case XML_UTIL_ENTITIES_HTML: - return html_entity_decode($string, ENT_COMPAT, $encoding); - break; - } - return $string; - } - - /** - * build an xml declaration - * - * - * require_once 'XML/Util.php'; - * - * // get an XML declaration: - * $xmlDecl = XML_Util::getXMLDeclaration('1.0', 'UTF-8', true); - * - * - * @param string $version xml version - * @param string $encoding character encoding - * @param bool $standalone document is standalone (or not) - * - * @return string xml declaration - * @access public - * @static - * @uses attributesToString() to serialize the attributes of the XML declaration - */ - function getXMLDeclaration($version = '1.0', $encoding = null, - $standalone = null) - { - $attributes = array( - 'version' => $version, - ); - // add encoding - if ($encoding !== null) { - $attributes['encoding'] = $encoding; - } - // add standalone, if specified - if ($standalone !== null) { - $attributes['standalone'] = $standalone ? 'yes' : 'no'; - } - - return sprintf('', - XML_Util::attributesToString($attributes, false)); - } - - /** - * build a document type declaration - * - * - * require_once 'XML/Util.php'; - * - * // get a doctype declaration: - * $xmlDecl = XML_Util::getDocTypeDeclaration('rootTag','myDocType.dtd'); - * - * - * @param string $root name of the root tag - * @param string $uri uri of the doctype definition - * (or array with uri and public id) - * @param string $internalDtd internal dtd entries - * - * @return string doctype declaration - * @access public - * @static - * @since 0.2 - */ - function getDocTypeDeclaration($root, $uri = null, $internalDtd = null) - { - if (is_array($uri)) { - $ref = sprintf(' PUBLIC "%s" "%s"', $uri['id'], $uri['uri']); - } elseif (!empty($uri)) { - $ref = sprintf(' SYSTEM "%s"', $uri); - } else { - $ref = ''; - } - - if (empty($internalDtd)) { - return sprintf('', $root, $ref); - } else { - return sprintf("", $root, $ref, $internalDtd); - } - } - - /** - * create string representation of an attribute list - * - * - * require_once 'XML/Util.php'; - * - * // build an attribute string - * $att = array( - * 'foo' => 'bar', - * 'argh' => 'tomato' - * ); - * - * $attList = XML_Util::attributesToString($att); - * - * - * @param array $attributes attribute array - * @param bool|array $sort sort attribute list alphabetically, - * may also be an assoc array containing - * the keys 'sort', 'multiline', 'indent', - * 'linebreak' and 'entities' - * @param bool $multiline use linebreaks, if more than - * one attribute is given - * @param string $indent string used for indentation of - * multiline attributes - * @param string $linebreak string used for linebreaks of - * multiline attributes - * @param int $entities setting for entities in attribute values - * (one of XML_UTIL_ENTITIES_NONE, - * XML_UTIL_ENTITIES_XML, - * XML_UTIL_ENTITIES_XML_REQUIRED, - * XML_UTIL_ENTITIES_HTML) - * - * @return string string representation of the attributes - * @access public - * @static - * @uses replaceEntities() to replace XML entities in attribute values - * @todo allow sort also to be an options array - */ - function attributesToString($attributes, $sort = true, $multiline = false, - $indent = ' ', $linebreak = "\n", $entities = XML_UTIL_ENTITIES_XML) - { - /* - * second parameter may be an array - */ - if (is_array($sort)) { - if (isset($sort['multiline'])) { - $multiline = $sort['multiline']; - } - if (isset($sort['indent'])) { - $indent = $sort['indent']; - } - if (isset($sort['linebreak'])) { - $multiline = $sort['linebreak']; - } - if (isset($sort['entities'])) { - $entities = $sort['entities']; - } - if (isset($sort['sort'])) { - $sort = $sort['sort']; - } else { - $sort = true; - } - } - $string = ''; - if (is_array($attributes) && !empty($attributes)) { - if ($sort) { - ksort($attributes); - } - if ( !$multiline || count($attributes) == 1) { - foreach ($attributes as $key => $value) { - if ($entities != XML_UTIL_ENTITIES_NONE) { - if ($entities === XML_UTIL_CDATA_SECTION) { - $entities = XML_UTIL_ENTITIES_XML; - } - $value = XML_Util::replaceEntities($value, $entities); - } - $string .= ' ' . $key . '="' . $value . '"'; - } - } else { - $first = true; - foreach ($attributes as $key => $value) { - if ($entities != XML_UTIL_ENTITIES_NONE) { - $value = XML_Util::replaceEntities($value, $entities); - } - if ($first) { - $string .= ' ' . $key . '="' . $value . '"'; - $first = false; - } else { - $string .= $linebreak . $indent . $key . '="' . $value . '"'; - } - } - } - } - return $string; - } - - /** - * Collapses empty tags. - * - * @param string $xml XML - * @param int $mode Whether to collapse all empty tags (XML_UTIL_COLLAPSE_ALL) - * or only XHTML (XML_UTIL_COLLAPSE_XHTML_ONLY) ones. - * - * @return string XML - * @access public - * @static - * @todo PEAR CS - unable to avoid "space after open parens" error - * in the IF branch - */ - function collapseEmptyTags($xml, $mode = XML_UTIL_COLLAPSE_ALL) - { - if ($mode == XML_UTIL_COLLAPSE_XHTML_ONLY) { - return preg_replace( - '/<(area|base(?:font)?|br|col|frame|hr|img|input|isindex|link|meta|' - . 'param)([^>]*)><\/\\1>/s', - '<\\1\\2 />', - $xml); - } else { - return preg_replace('/<(\w+)([^>]*)><\/\\1>/s', '<\\1\\2 />', $xml); - } - } - - /** - * create a tag - * - * This method will call XML_Util::createTagFromArray(), which - * is more flexible. - * - * - * require_once 'XML/Util.php'; - * - * // create an XML tag: - * $tag = XML_Util::createTag('myNs:myTag', - * array('foo' => 'bar'), - * 'This is inside the tag', - * 'http://www.w3c.org/myNs#'); - * - * - * @param string $qname qualified tagname (including namespace) - * @param array $attributes array containg attributes - * @param mixed $content the content - * @param string $namespaceUri URI of the namespace - * @param int $replaceEntities whether to replace XML special chars in - * content, embedd it in a CData section - * or none of both - * @param bool $multiline whether to create a multiline tag where - * each attribute gets written to a single line - * @param string $indent string used to indent attributes - * (_auto indents attributes so they start - * at the same column) - * @param string $linebreak string used for linebreaks - * @param bool $sortAttributes Whether to sort the attributes or not - * - * @return string XML tag - * @access public - * @static - * @see createTagFromArray() - * @uses createTagFromArray() to create the tag - */ - function createTag($qname, $attributes = array(), $content = null, - $namespaceUri = null, $replaceEntities = XML_UTIL_REPLACE_ENTITIES, - $multiline = false, $indent = '_auto', $linebreak = "\n", - $sortAttributes = true) - { - $tag = array( - 'qname' => $qname, - 'attributes' => $attributes - ); - - // add tag content - if ($content !== null) { - $tag['content'] = $content; - } - - // add namespace Uri - if ($namespaceUri !== null) { - $tag['namespaceUri'] = $namespaceUri; - } - - return XML_Util::createTagFromArray($tag, $replaceEntities, $multiline, - $indent, $linebreak, $sortAttributes); - } - - /** - * create a tag from an array - * this method awaits an array in the following format - *
    -     * array(
    -     *     // qualified name of the tag
    -     *     'qname' => $qname        
    -     *
    -     *     // namespace prefix (optional, if qname is specified or no namespace)
    -     *     'namespace' => $namespace    
    -     *
    -     *     // local part of the tagname (optional, if qname is specified)
    -     *     'localpart' => $localpart,   
    -     *
    -     *     // array containing all attributes (optional)
    -     *     'attributes' => array(),      
    -     *
    -     *     // tag content (optional)
    -     *     'content' => $content,     
    -     *
    -     *     // namespaceUri for the given namespace (optional)
    -     *     'namespaceUri' => $namespaceUri 
    -     * )
    -     * 
    - * - * - * require_once 'XML/Util.php'; - * - * $tag = array( - * 'qname' => 'foo:bar', - * 'namespaceUri' => 'http://foo.com', - * 'attributes' => array('key' => 'value', 'argh' => 'fruit&vegetable'), - * 'content' => 'I\'m inside the tag', - * ); - * // creating a tag with qualified name and namespaceUri - * $string = XML_Util::createTagFromArray($tag); - * - * - * @param array $tag tag definition - * @param int $replaceEntities whether to replace XML special chars in - * content, embedd it in a CData section - * or none of both - * @param bool $multiline whether to create a multiline tag where each - * attribute gets written to a single line - * @param string $indent string used to indent attributes - * (_auto indents attributes so they start - * at the same column) - * @param string $linebreak string used for linebreaks - * @param bool $sortAttributes Whether to sort the attributes or not - * - * @return string XML tag - * @access public - * @static - * @see createTag() - * @uses attributesToString() to serialize the attributes of the tag - * @uses splitQualifiedName() to get local part and namespace of a qualified name - * @uses createCDataSection() - * @uses raiseError() - */ - function createTagFromArray($tag, $replaceEntities = XML_UTIL_REPLACE_ENTITIES, - $multiline = false, $indent = '_auto', $linebreak = "\n", - $sortAttributes = true) - { - if (isset($tag['content']) && !is_scalar($tag['content'])) { - return XML_Util::raiseError('Supplied non-scalar value as tag content', - XML_UTIL_ERROR_NON_SCALAR_CONTENT); - } - - if (!isset($tag['qname']) && !isset($tag['localPart'])) { - return XML_Util::raiseError('You must either supply a qualified name ' - . '(qname) or local tag name (localPart).', - XML_UTIL_ERROR_NO_TAG_NAME); - } - - // if no attributes hav been set, use empty attributes - if (!isset($tag['attributes']) || !is_array($tag['attributes'])) { - $tag['attributes'] = array(); - } - - if (isset($tag['namespaces'])) { - foreach ($tag['namespaces'] as $ns => $uri) { - $tag['attributes']['xmlns:' . $ns] = $uri; - } - } - - if (!isset($tag['qname'])) { - // qualified name is not given - - // check for namespace - if (isset($tag['namespace']) && !empty($tag['namespace'])) { - $tag['qname'] = $tag['namespace'] . ':' . $tag['localPart']; - } else { - $tag['qname'] = $tag['localPart']; - } - } elseif (isset($tag['namespaceUri']) && !isset($tag['namespace'])) { - // namespace URI is set, but no namespace - - $parts = XML_Util::splitQualifiedName($tag['qname']); - - $tag['localPart'] = $parts['localPart']; - if (isset($parts['namespace'])) { - $tag['namespace'] = $parts['namespace']; - } - } - - if (isset($tag['namespaceUri']) && !empty($tag['namespaceUri'])) { - // is a namespace given - if (isset($tag['namespace']) && !empty($tag['namespace'])) { - $tag['attributes']['xmlns:' . $tag['namespace']] = - $tag['namespaceUri']; - } else { - // define this Uri as the default namespace - $tag['attributes']['xmlns'] = $tag['namespaceUri']; - } - } - - // check for multiline attributes - if ($multiline === true) { - if ($indent === '_auto') { - $indent = str_repeat(' ', (strlen($tag['qname'])+2)); - } - } - - // create attribute list - $attList = XML_Util::attributesToString($tag['attributes'], - $sortAttributes, $multiline, $indent, $linebreak, $replaceEntities); - if (!isset($tag['content']) || (string)$tag['content'] == '') { - $tag = sprintf('<%s%s />', $tag['qname'], $attList); - } else { - switch ($replaceEntities) { - case XML_UTIL_ENTITIES_NONE: - break; - case XML_UTIL_CDATA_SECTION: - $tag['content'] = XML_Util::createCDataSection($tag['content']); - break; - default: - $tag['content'] = XML_Util::replaceEntities($tag['content'], - $replaceEntities); - break; - } - $tag = sprintf('<%s%s>%s', $tag['qname'], $attList, $tag['content'], - $tag['qname']); - } - return $tag; - } - - /** - * create a start element - * - * - * require_once 'XML/Util.php'; - * - * // create an XML start element: - * $tag = XML_Util::createStartElement('myNs:myTag', - * array('foo' => 'bar') ,'http://www.w3c.org/myNs#'); - * - * - * @param string $qname qualified tagname (including namespace) - * @param array $attributes array containg attributes - * @param string $namespaceUri URI of the namespace - * @param bool $multiline whether to create a multiline tag where each - * attribute gets written to a single line - * @param string $indent string used to indent attributes (_auto indents - * attributes so they start at the same column) - * @param string $linebreak string used for linebreaks - * @param bool $sortAttributes Whether to sort the attributes or not - * - * @return string XML start element - * @access public - * @static - * @see createEndElement(), createTag() - */ - function createStartElement($qname, $attributes = array(), $namespaceUri = null, - $multiline = false, $indent = '_auto', $linebreak = "\n", - $sortAttributes = true) - { - // if no attributes hav been set, use empty attributes - if (!isset($attributes) || !is_array($attributes)) { - $attributes = array(); - } - - if ($namespaceUri != null) { - $parts = XML_Util::splitQualifiedName($qname); - } - - // check for multiline attributes - if ($multiline === true) { - if ($indent === '_auto') { - $indent = str_repeat(' ', (strlen($qname)+2)); - } - } - - if ($namespaceUri != null) { - // is a namespace given - if (isset($parts['namespace']) && !empty($parts['namespace'])) { - $attributes['xmlns:' . $parts['namespace']] = $namespaceUri; - } else { - // define this Uri as the default namespace - $attributes['xmlns'] = $namespaceUri; - } - } - - // create attribute list - $attList = XML_Util::attributesToString($attributes, $sortAttributes, - $multiline, $indent, $linebreak); - $element = sprintf('<%s%s>', $qname, $attList); - return $element; - } - - /** - * create an end element - * - * - * require_once 'XML/Util.php'; - * - * // create an XML start element: - * $tag = XML_Util::createEndElement('myNs:myTag'); - * - * - * @param string $qname qualified tagname (including namespace) - * - * @return string XML end element - * @access public - * @static - * @see createStartElement(), createTag() - */ - function createEndElement($qname) - { - $element = sprintf('', $qname); - return $element; - } - - /** - * create an XML comment - * - * - * require_once 'XML/Util.php'; - * - * // create an XML start element: - * $tag = XML_Util::createComment('I am a comment'); - * - * - * @param string $content content of the comment - * - * @return string XML comment - * @access public - * @static - */ - function createComment($content) - { - $comment = sprintf('', $content); - return $comment; - } - - /** - * create a CData section - * - * - * require_once 'XML/Util.php'; - * - * // create a CData section - * $tag = XML_Util::createCDataSection('I am content.'); - * - * - * @param string $data data of the CData section - * - * @return string CData section with content - * @access public - * @static - */ - function createCDataSection($data) - { - return sprintf('', - preg_replace('/\]\]>/', ']]]]>', strval($data))); - - } - - /** - * split qualified name and return namespace and local part - * - * - * require_once 'XML/Util.php'; - * - * // split qualified tag - * $parts = XML_Util::splitQualifiedName('xslt:stylesheet'); - * - * the returned array will contain two elements: - *
    -     * array(
    -     *     'namespace' => 'xslt',
    -     *     'localPart' => 'stylesheet'
    -     * );
    -     * 
    - * - * @param string $qname qualified tag name - * @param string $defaultNs default namespace (optional) - * - * @return array array containing namespace and local part - * @access public - * @static - */ - function splitQualifiedName($qname, $defaultNs = null) - { - if (strstr($qname, ':')) { - $tmp = explode(':', $qname); - return array( - 'namespace' => $tmp[0], - 'localPart' => $tmp[1] - ); - } - return array( - 'namespace' => $defaultNs, - 'localPart' => $qname - ); - } - - /** - * check, whether string is valid XML name - * - *

    XML names are used for tagname, attribute names and various - * other, lesser known entities.

    - *

    An XML name may only consist of alphanumeric characters, - * dashes, undescores and periods, and has to start with a letter - * or an underscore.

    - * - * - * require_once 'XML/Util.php'; - * - * // verify tag name - * $result = XML_Util::isValidName('invalidTag?'); - * if (is_a($result, 'PEAR_Error')) { - * print 'Invalid XML name: ' . $result->getMessage(); - * } - * - * - * @param string $string string that should be checked - * - * @return mixed true, if string is a valid XML name, PEAR error otherwise - * @access public - * @static - * @todo support for other charsets - * @todo PEAR CS - unable to avoid 85-char limit on second preg_match - */ - function isValidName($string) - { - // check for invalid chars - if (!preg_match('/^[[:alpha:]_]$/', $string{0})) { - return XML_Util::raiseError('XML names may only start with letter ' - . 'or underscore', XML_UTIL_ERROR_INVALID_START); - } - - // check for invalid chars - if (!preg_match('/^([[:alpha:]_]([[:alnum:]\-\.]*)?:)?[[:alpha:]_]([[:alnum:]\_\-\.]+)?$/', - $string) - ) { - return XML_Util::raiseError('XML names may only contain alphanumeric ' - . 'chars, period, hyphen, colon and underscores', - XML_UTIL_ERROR_INVALID_CHARS); - } - // XML name is valid - return true; - } - - /** - * replacement for XML_Util::raiseError - * - * Avoids the necessity to always require - * PEAR.php - * - * @param string $msg error message - * @param int $code error code - * - * @return PEAR_Error - * @access public - * @static - * @todo PEAR CS - should this use include_once instead? - */ - function raiseError($msg, $code) - { - require_once 'PEAR.php'; - return PEAR::raiseError($msg, $code); - } -} -?> diff --git a/phpcontrib_lib.php b/phpcontrib_lib.php deleted file mode 100644 index 0b2683f..0000000 --- a/phpcontrib_lib.php +++ /dev/null @@ -1,76 +0,0 @@ -$val ){ - - $nkey = $key; - $isset = $t==0 ? isset( $to[$key] ) : isset( $to->$key ); - - if( ( $type==EXTR_SKIP && $isset ) - || ( $type==EXTR_IF_EXISTS && !$isset ) ) - continue; - - else if( ( $type==EXTR_PREFIX_SAME && $isset ) - || ( $type==EXTR_PREFIX_ALL ) - || ( $type==EXTR_PREFIX_INVALID && !preg_match( '#^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$#', $key ) ) ) - $nkey = $prefix.$key; - - else if( $type==EXTR_PREFIX_IF_EXISTS ) - if( $isset ) $nkey = $prefix.$key; - else continue; - - if( !preg_match( '#^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$#', $nkey ) ) continue; - - if( $t==1 ) - if( $type & EXTR_REFS ) $to->$nkey = &$arr[$key]; - else $to->$nkey = $val; - else - if( $type & EXTR_REFS ) $to[$nkey] = &$arr[$key]; - else $to[$nkey] = $val; - - $i++; - } - - return $i; -} - -/** - * array_is_indexed - * a crud check if an array is an indexed array with the limitation that - * it works for arrays where every element is as if it had been assigned doing $array[] = $value - */ -function array_is_indexed( $pArray ){ - return (count(array_diff_key($pArray, array_values($pArray))) == 0); -} diff --git a/phpcoord/phpcoord-2.3.php b/phpcoord/phpcoord-2.3.php deleted file mode 100644 index edac27a..0000000 --- a/phpcoord/phpcoord-2.3.php +++ /dev/null @@ -1,785 +0,0 @@ -toSixFigureString() so that the eastings and northings - // are rounded rather than floored. - // 2.2 - 11 Feb 2006 - // - Used different algorithm for calculating distance between latitudes - // and longitudes - fixes a number of problems with distance calculations - // 2.1 - 22 Dec 2005 - // - Added getOSRefFromSixFigureReference function - // 2.0 - 21 Dec 2005 - // - Completely different object design - conversion functions now through - // objects rather than static functions - // - Updated comments and documentation - // 1.1 - 11 Sep 2005 - // - Added OSGB36/WGS84 data conversions - // 1.0 - 11 Aug 2005 - // - Initial version - //-------------------------------------------------------------------------- - - -/** - * LatLng - * - * @package kernel - */ - class LatLng { - - var $lat; - var $lng; - - - /** - * Create a new LatLng object from the given latitude and longitude - * - * @param lat latitude - * @param lng longitude - */ - function LatLng($lat, $lng) { - $this->lat = $lat; - $this->lng = $lng; - } - - - /** - * Return a string representation of this LatLng object - * - * @return a string representation of this LatLng object - */ - function toString() { - return "(" . $this->lat . ", " . $this->lng . ")"; - } - - - /** - * Calculate the surface distance between this LatLng object and the one - * passed in as a parameter. - * - * @param to a LatLng object to measure the surface distance to - * @return the surface distance - */ - function distance($to) { - $er = 6366.707; - - $latFrom = deg2rad($this->lat); - $latTo = deg2rad($to->lat); - $lngFrom = deg2rad($this->lng); - $lngTo = deg2rad($to->lng); - - $x1 = $er * cos($lngFrom) * sin($latFrom); - $y1 = $er * sin($lngFrom) * sin($latFrom); - $z1 = $er * cos($latFrom); - - $x2 = $er * cos($lngTo) * sin($latTo); - $y2 = $er * sin($lngTo) * sin($latTo); - $z2 = $er * cos($latTo); - - $d = acos(sin($latFrom)*sin($latTo) + cos($latFrom)*cos($latTo)*cos($lngTo-$lngFrom)) * $er; - - return $d; - } - - - /** - * Convert this LatLng object from OSGB36 datum to WGS84 datum. - */ - function OSGB36ToWGS84() { - $airy1830 = new RefEll(6377563.396, 6356256.909); - $a = $airy1830->maj; - $b = $airy1830->min; - $eSquared = $airy1830->ecc; - $phi = deg2rad($this->lat); - $lambda = deg2rad($this->lng); - $v = $a / (sqrt(1 - $eSquared * sinSquared($phi))); - $H = 0; // height - $x = ($v + $H) * cos($phi) * cos($lambda); - $y = ($v + $H) * cos($phi) * sin($lambda); - $z = ((1 - $eSquared) * $v + $H) * sin($phi); - - $tx = 446.448; - $ty = -124.157; - $tz = 542.060; - $s = -0.0000204894; - $rx = deg2rad( 0.00004172222); - $ry = deg2rad( 0.00006861111); - $rz = deg2rad( 0.00023391666); - - $xB = $tx + ($x * (1 + $s)) + (-$rx * $y) + ($ry * $z); - $yB = $ty + ($rz * $x) + ($y * (1 + $s)) + (-$rx * $z); - $zB = $tz + (-$ry * $x) + ($rx * $y) + ($z * (1 + $s)); - - $wgs84 = new RefEll(6378137.000, 6356752.3141); - $a = $wgs84->maj; - $b = $wgs84->min; - $eSquared = $wgs84->ecc; - - $lambdaB = rad2deg(atan($yB / $xB)); - $p = sqrt(($xB * $xB) + ($yB * $yB)); - $phiN = atan($zB / ($p * (1 - $eSquared))); - for ($i = 1; $i < 10; $i++) { - $v = $a / (sqrt(1 - $eSquared * sinSquared($phiN))); - $phiN1 = atan(($zB + ($eSquared * $v * sin($phiN))) / $p); - $phiN = $phiN1; - } - - $phiB = rad2deg($phiN); - - $this->lat = $phiB; - $this->lng = $lambdaB; - } - - - /** - * Convert this LatLng object from WGS84 datum to OSGB36 datum. - */ - function WGS84ToOSGB36() { - $wgs84 = new RefEll(6378137.000, 6356752.3141); - $a = $wgs84->maj; - $b = $wgs84->min; - $eSquared = $wgs84->ecc; - $phi = deg2rad($this->lat); - $lambda = deg2rad($this->lng); - $v = $a / (sqrt(1 - $eSquared * sinSquared($phi))); - $H = 0; // height - $x = ($v + $H) * cos($phi) * cos($lambda); - $y = ($v + $H) * cos($phi) * sin($lambda); - $z = ((1 - $eSquared) * $v + $H) * sin($phi); - - $tx = -446.448; - $ty = 124.157; - $tz = -542.060; - $s = 0.0000204894; - $rx = deg2rad(-0.00004172222); - $ry = deg2rad(-0.00006861111); - $rz = deg2rad(-0.00023391666); - - $xB = $tx + ($x * (1 + $s)) + (-$rx * $y) + ($ry * $z); - $yB = $ty + ($rz * $x) + ($y * (1 + $s)) + (-$rx * $z); - $zB = $tz + (-$ry * $x) + ($rx * $y) + ($z * (1 + $s)); - - $airy1830 = new RefEll(6377563.396, 6356256.909); - $a = $airy1830->maj; - $b = $airy1830->min; - $eSquared = $airy1830->ecc; - - $lambdaB = rad2deg(atan($yB / $xB)); - $p = sqrt(($xB * $xB) + ($yB * $yB)); - $phiN = atan($zB / ($p * (1 - $eSquared))); - for ($i = 1; $i < 10; $i++) { - $v = $a / (sqrt(1 - $eSquared * sinSquared($phiN))); - $phiN1 = atan(($zB + ($eSquared * $v * sin($phiN))) / $p); - $phiN = $phiN1; - } - - $phiB = rad2deg($phiN); - - $this->lat = $phiB; - $this->lng = $lambdaB; - } - - - /** - * Convert this LatLng object into an OSGB grid reference. Note that this - * function does not take into account the bounds of the OSGB grid - - * beyond the bounds of the OSGB grid, the resulting OSRef object has no - * meaning - * - * @return the converted OSGB grid reference - */ - function toOSRef() { - $airy1830 = new RefEll(6377563.396, 6356256.909); - $OSGB_F0 = 0.9996012717; - $N0 = -100000.0; - $E0 = 400000.0; - $phi0 = deg2rad(49.0); - $lambda0 = deg2rad(-2.0); - $a = $airy1830->maj; - $b = $airy1830->min; - $eSquared = $airy1830->ecc; - $phi = deg2rad($this->lat); - $lambda = deg2rad($this->lng); - $E = 0.0; - $N = 0.0; - $n = ($a - $b) / ($a + $b); - $v = $a * $OSGB_F0 * pow(1.0 - $eSquared * sinSquared($phi), -0.5); - $rho = - $a * $OSGB_F0 * (1.0 - $eSquared) * pow(1.0 - $eSquared * sinSquared($phi), -1.5); - $etaSquared = ($v / $rho) - 1.0; - $M = - ($b * $OSGB_F0) - * (((1 + $n + ((5.0 / 4.0) * $n * $n) + ((5.0 / 4.0) * $n * $n * $n)) - * ($phi - $phi0)) - - (((3 * $n) + (3 * $n * $n) + ((21.0 / 8.0) * $n * $n * $n)) - * sin($phi - $phi0) - * cos($phi + $phi0)) - + ((((15.0 / 8.0) * $n * $n) + ((15.0 / 8.0) * $n * $n * $n)) - * sin(2.0 * ($phi - $phi0)) - * cos(2.0 * ($phi + $phi0))) - - (((35.0 / 24.0) * $n * $n * $n) - * sin(3.0 * ($phi - $phi0)) - * cos(3.0 * ($phi + $phi0)))); - $I = $M + $N0; - $II = ($v / 2.0) * sin($phi) * cos($phi); - $III = - ($v / 24.0) - * sin($phi) - * pow(cos($phi), 3.0) - * (5.0 - tanSquared($phi) + (9.0 * $etaSquared)); - $IIIA = - ($v / 720.0) - * sin($phi) - * pow(cos($phi), 5.0) - * (61.0 - (58.0 * tanSquared($phi)) + pow(tan($phi), 4.0)); - $IV = $v * cos($phi); - $V = ($v / 6.0) * pow(cos($phi), 3.0) * (($v / $rho) - tanSquared($phi)); - $VI = - ($v / 120.0) - * pow(cos($phi), 5.0) - * (5.0 - - (18.0 * tanSquared($phi)) - + (pow(tan($phi), 4.0)) - + (14 * $etaSquared) - - (58 * tanSquared($phi) * $etaSquared)); - - $N = - $I - + ($II * pow($lambda - $lambda0, 2.0)) - + ($III * pow($lambda - $lambda0, 4.0)) - + ($IIIA * pow($lambda - $lambda0, 6.0)); - $E = - $E0 - + ($IV * ($lambda - $lambda0)) - + ($V * pow($lambda - $lambda0, 3.0)) - + ($VI * pow($lambda - $lambda0, 5.0)); - - return new OSRef($E, $N); - } - - - /** - * Convert a latitude and longitude to an UTM reference - * - * @return the converted UTM reference - */ - function toUTMRef() { - $wgs84 = new RefEll(6378137, 6356752.314); - $UTM_F0 = 0.9996; - $a = $wgs84->maj; - $eSquared = $wgs84->ecc; - $longitude = $this->lng; - $latitude = $this->lat; - - $latitudeRad = $latitude * (pi() / 180.0); - $longitudeRad = $longitude * (pi() / 180.0); - $longitudeZone = (int) (($longitude + 180.0) / 6.0) + 1; - - // Special zone for Norway - if ($latitude >= 56.0 - && $latitude < 64.0 - && $longitude >= 3.0 - && $longitude < 12.0) { - $longitudeZone = 32; - } - - // Special zones for Svalbard - if ($latitude >= 72.0 && $latitude < 84.0) { - if ($longitude >= 0.0 && $longitude < 9.0) { - $longitudeZone = 31; - } else if ($longitude >= 9.0 && $longitude < 21.0) { - $longitudeZone = 33; - } else if ($longitude >= 21.0 && $longitude < 33.0) { - $longitudeZone = 35; - } else if ($longitude >= 33.0 && $longitude < 42.0) { - $longitudeZone = 37; - } - } - - $longitudeOrigin = ($longitudeZone - 1) * 6 - 180 + 3; - $longitudeOriginRad = $longitudeOrigin * (pi() / 180.0); - - $UTMZone = getUTMLatitudeZoneLetter($latitude); - - $ePrimeSquared = ($eSquared) / (1 - $eSquared); - - $n = $a / sqrt(1 - $eSquared * sin($latitudeRad) * sin($latitudeRad)); - $t = tan($latitudeRad) * tan($latitudeRad); - $c = $ePrimeSquared * cos($latitudeRad) * cos($latitudeRad); - $A = cos($latitudeRad) * ($longitudeRad - $longitudeOriginRad); - - $M = - $a - * ((1 - - $eSquared / 4 - - 3 * $eSquared * $eSquared / 64 - - 5 * $eSquared * $eSquared * $eSquared / 256) - * $latitudeRad - - (3 * $eSquared / 8 - + 3 * $eSquared * $eSquared / 32 - + 45 * $eSquared * $eSquared * $eSquared / 1024) - * sin(2 * $latitudeRad) - + (15 * $eSquared * $eSquared / 256 - + 45 * $eSquared * $eSquared * $eSquared / 1024) - * sin(4 * $latitudeRad) - - (35 * $eSquared * $eSquared * $eSquared / 3072) - * sin(6 * $latitudeRad)); - - $UTMEasting = - (double) ($UTM_F0 - * $n - * ($A - + (1 - $t + $c) * pow($A, 3.0) / 6 - + (5 - 18 * $t + $t * $t + 72 * $c - 58 * $ePrimeSquared) - * pow($A, 5.0) - / 120) - + 500000.0); - - $UTMNorthing = - (double) ($UTM_F0 - * ($M - + $n - * tan($latitudeRad) - * ($A * $A / 2 - + (5 - $t + (9 * $c) + (4 * $c * $c)) * pow($A, 4.0) / 24 - + (61 - (58 * $t) + ($t * $t) + (600 * $c) - (330 * $ePrimeSquared)) - * pow($A, 6.0) - / 720))); - - // Adjust for the southern hemisphere - if ($latitude < 0) { - $UTMNorthing += 10000000.0; - } - - return new UTMRef($UTMEasting, $UTMNorthing, $UTMZone, $longitudeZone); - } - } - -/** - * OSRef - * References given with OSRef are accurate to 1m. - * - * @package kernel - */ - class OSRef { - - var $easting; - var $northing; - - - /** - * Create a new OSRef object representing an OSGB grid reference. Note - * that the parameters for this constructor require eastings and - * northings with 1m accuracy and need to be absolute with respect to - * the whole of the British Grid. For example, to create an OSRef - * object from the six-figure grid reference TG514131, the easting would - * be 651400 and the northing would be 313100. - * - * Grid references with accuracy greater than 1m can be represented - * using floating point values for the easting and northing. For example, - * a value representing an easting or northing accurate to 1mm would be - * given as 651400.0001. - * - * @param easting the easting of the reference (with 1m accuracy) - * @param northing the northing of the reference (with 1m accuracy) - */ - function OSRef($easting, $northing) { - $this->easting = $easting; - $this->northing = $northing; - } - - - /** - * Convert this grid reference into a string showing the exact values - * of the easting and northing. - * - * @return - */ - function toString() { - return "(" . $this->easting . ", " . $this->northing . ")"; - } - - - /** - * Convert this grid reference into a string using a standard six-figure - * grid reference including the two-character designation for the 100km - * square. e.g. TG514131. - * - * @return - */ - function toSixFigureString() { - $hundredkmE = floor($this->easting / 100000); - $hundredkmN = floor($this->northing / 100000); - $firstLetter = ""; - if ($hundredkmN < 5) { - if ($hundredkmE < 5) { - $firstLetter = "S"; - } else { - $firstLetter = "T"; - } - } else if ($hundredkmN < 10) { - if ($hundredkmE < 5) { - $firstLetter = "N"; - } else { - $firstLetter = "O"; - } - } else { - $firstLetter = "H"; - } - - $secondLetter = ""; - $index = 65 + ((4 - ($hundredkmN % 5)) * 5) + ($hundredkmE % 5); - $ti = $index; - if ($index >= 73) $index++; - $secondLetter = chr($index); - - $e = round(($this->easting - (100000 * $hundredkmE)) / 100); - $n = round(($this->northing - (100000 * $hundredkmN)) / 100); - - return sprintf("%s%s%03d%03d", $firstLetter, $secondLetter, $e, $n); - } - - - /** - * Convert this grid reference into a latitude and longitude - * - * @return - */ - function toLatLng() { - $airy1830 = new RefEll(6377563.396, 6356256.909); - $OSGB_F0 = 0.9996012717; - $N0 = -100050.0; - $E0 = 400110.0; - $phi0 = deg2rad(49.0); - $lambda0 = deg2rad(-2.0); - $a = $airy1830->maj; - $b = $airy1830->min; - $eSquared = $airy1830->ecc; - $phi = 0.0; - $lambda = 0.0; - $E = $this->easting; - $N = $this->northing; - $n = ($a - $b) / ($a + $b); - $M = 0.0; - $phiPrime = (($N - $N0) / ($a * $OSGB_F0)) + $phi0; - do { - $M = - ($b * $OSGB_F0) - * (((1 + $n + ((5.0 / 4.0) * $n * $n) + ((5.0 / 4.0) * $n * $n * $n)) - * ($phiPrime - $phi0)) - - (((3 * $n) + (3 * $n * $n) + ((21.0 / 8.0) * $n * $n * $n)) - * sin($phiPrime - $phi0) - * cos($phiPrime + $phi0)) - + ((((15.0 / 8.0) * $n * $n) + ((15.0 / 8.0) * $n * $n * $n)) - * sin(2.0 * ($phiPrime - $phi0)) - * cos(2.0 * ($phiPrime + $phi0))) - - (((35.0 / 24.0) * $n * $n * $n) - * sin(3.0 * ($phiPrime - $phi0)) - * cos(3.0 * ($phiPrime + $phi0)))); - $phiPrime += ($N - $N0 - $M) / ($a * $OSGB_F0); - } while (($N - $N0 - $M) >= 0.001); - $v = $a * $OSGB_F0 * pow(1.0 - $eSquared * sinSquared($phiPrime), -0.5); - $rho = - $a - * $OSGB_F0 - * (1.0 - $eSquared) - * pow(1.0 - $eSquared * sinSquared($phiPrime), -1.5); - $etaSquared = ($v / $rho) - 1.0; - $VII = tan($phiPrime) / (2 * $rho * $v); - $VIII = - (tan($phiPrime) / (24.0 * $rho * pow($v, 3.0))) - * (5.0 - + (3.0 * tanSquared($phiPrime)) - + $etaSquared - - (9.0 * tanSquared($phiPrime) * $etaSquared)); - $IX = - (tan($phiPrime) / (720.0 * $rho * pow($v, 5.0))) - * (61.0 - + (90.0 * tanSquared($phiPrime)) - + (45.0 * tanSquared($phiPrime) * tanSquared($phiPrime))); - $X = sec($phiPrime) / $v; - $XI = - (sec($phiPrime) / (6.0 * $v * $v * $v)) - * (($v / $rho) + (2 * tanSquared($phiPrime))); - $XII = - (sec($phiPrime) / (120.0 * pow($v, 5.0))) - * (5.0 - + (28.0 * tanSquared($phiPrime)) - + (24.0 * tanSquared($phiPrime) * tanSquared($phiPrime))); - $XIIA = - (sec($phiPrime) / (5040.0 * pow($v, 7.0))) - * (61.0 - + (662.0 * tanSquared($phiPrime)) - + (1320.0 * tanSquared($phiPrime) * tanSquared($phiPrime)) - + (720.0 - * tanSquared($phiPrime) - * tanSquared($phiPrime) - * tanSquared($phiPrime))); - $phi = - $phiPrime - - ($VII * pow($E - $E0, 2.0)) - + ($VIII * pow($E - $E0, 4.0)) - - ($IX * pow($E - $E0, 6.0)); - $lambda = - $lambda0 - + ($X * ($E - $E0)) - - ($XI * pow($E - $E0, 3.0)) - + ($XII * pow($E - $E0, 5.0)) - - ($XIIA * pow($E - $E0, 7.0)); - - return new LatLng(rad2deg($phi), rad2deg($lambda)); - } - } - -/** - * UTMRef - * - * @package kernel - */ - class UTMRef { - - var $easting; - var $northing; - var $latZone; - var $lngZone; - - - /** - * Create a new object representing a UTM reference. - * - * @param easting - * @param northing - * @param latZone - * @param lngZone - */ - function UTMRef($easting, $northing, $latZone, $lngZone) { - $this->easting = $easting; - $this->northing = $northing; - $this->latZone = $latZone; - $this->lngZone = $lngZone; - } - - - /** - * Return a string representation of this UTM reference - * - * @return - */ - function toString() { - return $this->lngZone . $this->latZone . " " . - $this->easting . " " . $this->northing; - } - - - /** - * Convert this UTM reference to a latitude and longitude - * - * @return the converted latitude and longitude - */ - function toLatLng() { - $wgs84 = new RefEll(6378137, 6356752.314); - $UTM_F0 = 0.9996; - $a = $wgs84->maj; - $eSquared = $wgs84->ecc; - $ePrimeSquared = $eSquared / (1.0 - $eSquared); - $e1 = (1 - sqrt(1 - $eSquared)) / (1 + sqrt(1 - $eSquared)); - $x = $this->easting - 500000.0;; - $y = $this->northing; - $zoneNumber = $this->lngZone; - $zoneLetter = $this->latZone; - - $longitudeOrigin = ($zoneNumber - 1.0) * 6.0 - 180.0 + 3.0; - - // Correct y for southern hemisphere - if ((ord($zoneLetter) - ord("N")) < 0) { - $y -= 10000000.0; - } - - $m = $y / $UTM_F0; - $mu = - $m - / ($a - * (1.0 - - $eSquared / 4.0 - - 3.0 * $eSquared * $eSquared / 64.0 - - 5.0 - * pow($eSquared, 3.0) - / 256.0)); - - $phi1Rad = - $mu - + (3.0 * $e1 / 2.0 - 27.0 * pow($e1, 3.0) / 32.0) * sin(2.0 * $mu) - + (21.0 * $e1 * $e1 / 16.0 - 55.0 * pow($e1, 4.0) / 32.0) - * sin(4.0 * $mu) - + (151.0 * pow($e1, 3.0) / 96.0) * sin(6.0 * $mu); - - $n = - $a - / sqrt(1.0 - $eSquared * sin($phi1Rad) * sin($phi1Rad)); - $t = tan($phi1Rad) * tan($phi1Rad); - $c = $ePrimeSquared * cos($phi1Rad) * cos($phi1Rad); - $r = - $a - * (1.0 - $eSquared) - / pow( - 1.0 - $eSquared * sin($phi1Rad) * sin($phi1Rad), - 1.5); - $d = $x / ($n * $UTM_F0); - - $latitude = ( - $phi1Rad - - ($n * tan($phi1Rad) / $r) - * ($d * $d / 2.0 - - (5.0 - + (3.0 * $t) - + (10.0 * $c) - - (4.0 * $c * $c) - - (9.0 * $ePrimeSquared)) - * pow($d, 4.0) - / 24.0 - + (61.0 - + (90.0 * $t) - + (298.0 * $c) - + (45.0 * $t * $t) - - (252.0 * $ePrimeSquared) - - (3.0 * $c * $c)) - * pow($d, 6.0) - / 720.0)) * (180.0 / pi()); - - $longitude = $longitudeOrigin + ( - ($d - - (1.0 + 2.0 * $t + $c) * pow($d, 3.0) / 6.0 - + (5.0 - - (2.0 * $c) - + (28.0 * $t) - - (3.0 * $c * $c) - + (8.0 * $ePrimeSquared) - + (24.0 * $t * $t)) - * pow($d, 5.0) - / 120.0) - / cos($phi1Rad)) * (180.0 / pi()); - - return new LatLng($latitude, $longitude); - } - } - -/** - * RefEll - * - * @package kernel - */ - class RefEll { - - var $maj; - var $min; - var $ecc; - - - /** - * Create a new RefEll object to represent a reference ellipsoid - * - * @param maj the major axis - * @param min the minor axis - */ - function RefEll($maj, $min) { - $this->maj = $maj; - $this->min = $min; - $this->ecc = (($maj * $maj) - ($min * $min)) / ($maj * $maj); - } - } - - - // ================================================== Mathematical Functions - - function sinSquared($x) { - return sin($x) * sin($x); - } - - function cosSquared($x) { - return cos($x) * cos($x); - } - - function tanSquared($x) { - return tan($x) * tan($x); - } - - function sec($x) { - return 1.0 / cos($x); - } - - - /** - * Take a string formatted as a six-figure OS grid reference (e.g. - * "TG514131") and return a reference to an OSRef object that represents - * that grid reference. The first character must be H, N, S, O or T. - * The second character can be any uppercase character from A through Z - * excluding I. - * - * @param ref - * @return - * @since 2.1 - */ - function getOSRefFromSixFigureReference($ref) { - $char1 = substr($ref, 0, 1); - $char2 = substr($ref, 1, 1); - $east = substr($ref, 2, 3) * 100; - $north = substr($ref, 5, 3) * 100; - if ($char1 == 'H') { - $north += 1000000; - } else if ($char1 == 'N') { - $north += 500000; - } else if ($char1 == 'O') { - $north += 500000; - $east += 500000; - } else if ($char1 == 'T') { - $east += 500000; - } - $char2ord = ord($char2); - if ($char2ord > 73) $char2ord--; // Adjust for no I - $nx = (($char2ord - 65) % 5) * 100000; - $ny = (4 - floor(($char2ord - 65) / 5)) * 100000; - return new OSRef($east + $nx, $north + $ny); - } - - - /** - * Work out the UTM latitude zone from the latitude - * - * @param latitude - * @return - */ - function getUTMLatitudeZoneLetter($latitude) { - if ((84 >= $latitude) && ($latitude >= 72)) return "X"; - else if (( 72 > $latitude) && ($latitude >= 64)) return "W"; - else if (( 64 > $latitude) && ($latitude >= 56)) return "V"; - else if (( 56 > $latitude) && ($latitude >= 48)) return "U"; - else if (( 48 > $latitude) && ($latitude >= 40)) return "T"; - else if (( 40 > $latitude) && ($latitude >= 32)) return "S"; - else if (( 32 > $latitude) && ($latitude >= 24)) return "R"; - else if (( 24 > $latitude) && ($latitude >= 16)) return "Q"; - else if (( 16 > $latitude) && ($latitude >= 8)) return "P"; - else if (( 8 > $latitude) && ($latitude >= 0)) return "N"; - else if (( 0 > $latitude) && ($latitude >= -8)) return "M"; - else if (( -8 > $latitude) && ($latitude >= -16)) return "L"; - else if ((-16 > $latitude) && ($latitude >= -24)) return "K"; - else if ((-24 > $latitude) && ($latitude >= -32)) return "J"; - else if ((-32 > $latitude) && ($latitude >= -40)) return "H"; - else if ((-40 > $latitude) && ($latitude >= -48)) return "G"; - else if ((-48 > $latitude) && ($latitude >= -56)) return "F"; - else if ((-56 > $latitude) && ($latitude >= -64)) return "E"; - else if ((-64 > $latitude) && ($latitude >= -72)) return "D"; - else if ((-72 > $latitude) && ($latitude >= -80)) return "C"; - else return 'Z'; - } - -?> diff --git a/phpcoord/readme-2.3.txt b/phpcoord/readme-2.3.txt deleted file mode 100644 index ac3b253..0000000 --- a/phpcoord/readme-2.3.txt +++ /dev/null @@ -1,52 +0,0 @@ --------------------------------------------------------------------------- PHPcoord - readme.txt - (c) 2005 Jonathan Stott Created on 11-Aug-2005 - 2.3 - 24 Aug 2006 - - Changed OSRef->toSixFigureString() so that the eastings and northings - are rounded rather than floored. - 2.2 - 11 Feb 2006 - - Used different algorithm for calculating distance between latitudes - and longitudes - fixes a number of problems with distance calculations - 2.1 - 22 Dec 2005 - - Added getOSRefFromSixFigureReference function - 2.0 - 21 Dec 2005 - - Completely different object design - conversion functions now through - objects rather than static functions - - Updated comments and documentation - 1.1 - 11 Sep 2005 - - Added WGS84/OSGB36 conversions 1.0 - 11 Aug 2005 - Initial version -------------------------------------------------------------------------- - -PHPcoord is a PHP script that provides functions for handling various -co-ordinate systems and converting between them. Currently, OSGB (Ordnance -Survey of Great Britain) grid references, UTM (Universal Transverse -Mercator) references and latitude/longitude are supported. A function is -also provided to find the surface distance between two points of latitude -and longitude. - -When using the OSGB conversions, the majority of applications use the -WGS84 datum rather than the OSGB36 datum. Conversions between the two -data were added in v1.1 - the conversions should be accurate to within -5m or so. If accuracy is not important (i.e. to within 200m or so), -then it isn't necessary to perform the conversions. - -Examples of how to use the functions provided in phpcoord.php can be -found in the test.php script. - -See http://www.jstott.me.uk/phpcoord/ for latest releases and information. - - -DISCLAIMER - -Accuracy of the co-ordinate conversions contained within the PHPcoord -package is not guaranteed. Use of the conversions is entirely at your -own risk and I cannot be held responsible for any consequences of -errors created by the conversions. I do not recommend using the package -for mission-critical applications. - - -LICENSING - -This software product is available under the GNU General Public License -(GPL). Terms of the GPL can be read at http://www.jstott.me.uk/gpl/. -Any commercial use requires the purchase of a license - contact me at -phpcoord@jstott.me.uk for details. \ No newline at end of file diff --git a/phpcoord/test-2.3.php b/phpcoord/test-2.3.php deleted file mode 100644 index 8c90261..0000000 --- a/phpcoord/test-2.3.php +++ /dev/null @@ -1,211 +0,0 @@ -toSixFigureString() so that the eastings and northings - // are rounded rather than floored. - // 2.2 - 11 Feb 2006 - // - Used different algorithm for calculating distance between latitudes - // and longitudes - fixes a number of problems with distance calculations - // 2.1 - 22 Dec 2005 - // - Added getOSRefFromSixFigureReference function - // 2.0 - 21 Dec 2005 - // - Completely different object design - conversion functions now through - // objects rather than static functions - // - Updated comments and documentation - // 1.1 - 11 Sep 2005 - // - Added OSGB36/WGS84 data conversions - // 1.0 - 11 Aug 2005 - // - Initial version - //-------------------------------------------------------------------------- - * - * @package kernel - * @subpackage functions - */ - -/** - * Load library - */ - require_once("phpcoord-2.3.php"); -?> - - - - - phpcoord Test Script - - - - -

    phpcoord Test Script

    - -

    Calculate Surface Distance between two Latitudes/Longitudes

    - -

    - The LatLngDistance function takes two latitudes/longitudes and calculates - the surface distance between the two in kilometres: -

    - -

    -

    $lld1 = new LatLng(40.718119, -73.995667); // New York
    -echo "New York Lat/Long: " . $lld1->toString() . "<br />";
    -$lld2 = new LatLng(51.499981, -0.125313);  // London
    -$d = $lld1->distance($lld2);
    -echo "Surface Distance between New York and London: " . $d . "km";
    - - toString() . "
    "; - $lld2 = new LatLng(51.499981, -0.125313); // London - echo "London Lat/Long: " . $lld2->toString() . "
    "; - $d = $lld1->distance($lld2); - echo "Surface Distance between New York and London: " . $d . "km"; - ?> -

    - -

    Convert OS Grid Reference to Latitude/Longitude

    - -

    - Note that the OSGB-Latitude/Longitude conversions use the OSGB36 datum by default. The - majority of applications use the WGS84 datum, for which the appropriate conversions - need to be added. See the examples below to see the difference between the two data. -

    - -

    - Using OSGB36 (convert an OSGB grid reference to a latitude and longitude using the OSGB36 datum): - -

    $os1 = new OSRef(651409.903, 313177.270);
    -echo "OS Grid Reference: " . $os1->toString() . " - " . $os1->toSixFigureString() . "<br />";
    -$ll1 = $os1->toLatLng();
    -echo "Converted to Lat/Long: " . $ll1->toString();
    - - toString() . " - " . $os1->toSixFigureString() . "
    "; - $ll1 = $os1->toLatLng(); - echo "Converted to Lat/Long: " . $ll1->toString(); - ?> -

    - -

    - Using WGS84 (convert an OSGB grid reference to a latitude and longitude using the WGS84 datum): - -

    $os1w = new OSRef(651409.903, 313177.270);
    -echo "OS Grid Reference: " . $os1w->toString() . " - " . $os1w->toSixFigureString() . "<br />";
    -$l1w = $os1w->toLatLng();
    -$l1w->OSGB36ToWGS84();
    -echo "Converted to Lat/Long: " . $ll1w->toString();
    - - toString() . " - " . $os1w->toSixFigureString() . "
    "; - $ll1w = $os1w->toLatLng(); - $ll1w->OSGB36ToWGS84(); - echo "Converted to Lat/Long: " . $ll1w->toString(); - ?> -

    - -

    Convert Latitude/Longitude to OS Grid Reference

    - -

    - Note that the OSGB-Latitude/Longitude conversions use the OSGB36 datum by default. The - majority of applications use the WGS84 datum, for which the appropriate conversions - need to be added. See the examples below to see the difference between the two data. -

    - -

    - Using OSGB36 (convert a latitude and longitude using the OSGB36 datum to an OSGB grid reference): - -

    $ll2 = new LatLng(52.657570301933, 1.7179215806451);
    -echo "Latitude/Longitude: " . $ll2->toString() . "<br />";
    -$os2 = $ll2->toOSRef();
    -echo "Converted to OS Grid Ref: " . $os2->toString() . " - " . $os2->toSixFigureString();
    - - toString() . "
    "; - $os2 = $ll2->toOSRef(); - echo "Converted to OS Grid Ref: " . $os2->toString() . " - " . $os2->toSixFigureString(); - ?> -

    - -

    - Using WGS84 (convert a latitude and longitude using the WGS84 datum to an OSGB grid reference): - -

    $ll2w = new LatLng(52.657570301933, 1.7179215806451);
    -echo "Latitude/Longitude: " . $ll2->toString() . "<br />";
    -$ll2w->WGS84ToOSGB36();
    -$os2w = $ll2w->toOSRef();
    -echo "Converted to OS Grid Ref: " . $os2w->toString() . " - " . $os2w->toSixFigureString();
    - - toString() . "
    "; - $ll2w->WGS84ToOSGB36(); - $os2w = $ll2w->toOSRef(); - echo "Converted to OS Grid Ref: " . $os2w->toString() . " - " . $os2w->toSixFigureString(); - ?> -

    - -

    Convert Six-Figure OS Grid Reference String to an OSRef Object

    - -

    - To convert a string representing a six-figure OSGB grid reference: - -

    $os6 = "TG514131";
    -echo "Six figure string: " . $os6 . "<br />";
    -$os6x = getOSRefFromSixFigureReference($os6);
    -echo "Converted to OS Grid Ref: " . $os6x->toString() . " - " . $os6x->toSixFigureString();
    - - "; - $os6x = getOSRefFromSixFigureReference($os6); - echo "Converted to OS Grid Ref: " . $os6x->toString() . " - " . $os6x->toSixFigureString(); - ?> -

    - -

    Convert UTM Reference to Latitude/Longitude

    - -

    -

    $utm1 = new UTMRef(456463.99, 3335334.05, "E", 12);
    -echo "UTM Reference: " . $utm1->toString() . "<br />";
    -$ll3 = $utm1->toLatLng();
    -echo "Converted to Lat/Long: " . $ll3->toString();
    - - toString() . "
    "; - $ll3 = $utm1->toLatLng(); - echo "Converted to Lat/Long: " . $ll3->toString(); - ?> -

    - -

    Convert Latitude/Longitude to UTM Reference

    - -

    -

    $ll4 = new LatLng(-60.1167, -111.7833);
    -echo "Latitude/Longitude: " . $ll4->toString() . "<br />";
    -$utm2 = $ll4->toUTMRef();
    -echo "Converted to UTM Ref: " . $utm2->toString() ;
    - - toString() . "
    "; - $utm2 = $ll4->toUTMRef(); - echo "Converted to UTM Ref: " . $utm2->toString() ; - ?> -

    - -

    - (c) 2005, Jonathan Stott -

    - - - diff --git a/phplot.php b/phplot.php deleted file mode 100644 index 44286fe..0000000 --- a/phplot.php +++ /dev/null @@ -1,6688 +0,0 @@ - - * - * Maintainer (2006-present) - * - * - * Requires PHP 5.2.x or later. (PHP 4 is unsupported as of Jan 2008) - */ - -class PHPlot -{ - const version = '5.4.0'; - - /* Declare class variables which are initialized to static values. Many more class variables - * are used, defined as needed, but are unset by default. - * All these are declared as public. While it is tempting to make them private or protected, this - * is avoided for two reasons. First, it will break existing code, since all member variables - * were public in PHP4 and who knows what internal variables people used. Second, it makes - * testing harder and less effective. Nevertheless, your code should not modify these. - */ - - public $is_inline = FALSE; // FALSE = Sends headers, TRUE = sends just raw image data - public $browser_cache = FALSE; // FALSE = Sends headers for browser to not cache the image, - // (only if is_inline = FALSE also) - public $print_image = TRUE; // DrawGraph calls PrintImage. See SetPrintImage - - public $safe_margin = 5; // Extra margin used in several places, in pixels - - public $x_axis_position = ''; // X axis position in Y world coordinates, blank for default. - public $y_axis_position = ''; // Y axis position in X world coordinates, blank for default. - - public $xscale_type = 'linear'; // linear, log - public $yscale_type = 'linear'; - -//Fonts - public $use_ttf = FALSE; // Use True Type Fonts by default? - public $ttf_path = '.'; // Default path to look in for TT Fonts. - // public $default_ttfont; // Initialized in GetDefaultTTFont - public $line_spacing = 4; // Controls line spacing of multi-line labels - - // Label angles: 0 or 90 degrees for fixed fonts, any for TTF - public $x_label_angle = 0; // For X tick labels - // public $x_data_label_angle; // For X data labels; defaults to x_label_angle - see CheckLabels() - public $y_label_angle = 0; // For Y tick labels - public $y_data_label_angle = 0; // For Y data labels - -//Formats - public $file_format = 'png'; - public $output_file = ''; // For output to a file instead of stdout - -//Data - public $data_type = 'text-data'; // Structure of the data array - public $plot_type = 'linepoints'; // See $plots[] below - - public $label_scale_position = 0.5; // Shifts data labels in pie charts. 1 = top, 0 = bottom - public $group_frac_width = 0.7; // Bars use this fraction (0 to 1) of a group's space - public $bar_extra_space = 0.5; // Number of extra bar's worth of space in a group - public $bar_width_adjust = 1; // 1 = bars of normal width, must be > 0 - -// Titles - public $title_txt = ''; - - public $x_title_txt = ''; - public $x_title_pos = 'none'; // plotdown, plotup, both, none - - public $y_title_txt = ''; - public $y_title_pos = 'none'; // plotleft, plotright, both, none - -//Labels - // There are two types of labels in PHPlot: - // Tick labels: Follow the grid, next to ticks in axis. - // Are drawn at grid drawing time, by DrawXTicks() and DrawYTicks() - // Data labels: Follow the data points, and can be placed on the axis or the plot (x/y) - // Are drawn at graph plotting time, by Draw*DataLabel(), called by DrawLines(), etc. - // DrawXDataLabel() also draws vertical lines to data points, depending on - // draw_x_data_label_lines. - // Tick Labels - // Tick and Data label positions are not initialized, because PHPlot needs to tell if they - // defaulted or are set by the user. See CheckLabels() for details. The variables and - // effective defaults are shown here in comments (but CheckLabels adjusts the defaults). - // public $x_tick_label_pos = 'plotdown'; // X tick label position - // public $y_tick_label_pos = 'plotleft'; // Y tick label position - // public $x_data_label_pos = 'plotdown'; // X data label position - // public $y_data_label_pos = 'none'; // Y data label position - - public $draw_x_data_label_lines = FALSE; // Draw a line from the data point to the axis? - - // Label format controls: (for tick, data and plot labels) - // Unset by default, these array members are used as needed for 'x' (x tick labels), 'xd' (x data - // labels), 'y' (y tick labels), and 'yd' (y data labels). - // type, precision, prefix, suffix, time_format, printf_format, custom_callback, custom_arg. - // These replace the former: x_label_type, x_time_format, x_precision (similar for y), data_units_text. - public $label_format = array('x' => array(), 'xd' => array(), 'y' => array(), 'yd' => array()); - // data_units_text is retained for backward compatibility, because there was never a function - // to set it. Use the 'suffix' argument to Set[XY]LabelType instead. - public $data_units_text = ''; // Units text for 'data' labels (i.e: '¤', '$', etc.) - -// Legend - public $legend = ''; // An array with legend titles - // Other legend_* variables are set as needed, unset for default values. - -//Ticks - public $x_tick_length = 5; // tick length in pixels for upper/lower axis - public $y_tick_length = 5; // tick length in pixels for left/right axis - - public $x_tick_cross = 3; // ticks cross x axis this many pixels - public $y_tick_cross = 3; // ticks cross y axis this many pixels - - public $x_tick_pos = 'plotdown'; // plotdown, plotup, both, xaxis, none - public $y_tick_pos = 'plotleft'; // plotright, plotleft, both, yaxis, none - - public $num_x_ticks = ''; - public $num_y_ticks = ''; - - public $x_tick_inc = ''; // Set num_x_ticks or x_tick_inc, not both. - public $y_tick_inc = ''; // Set num_y_ticks or y_tick_inc, not both. - - public $skip_top_tick = FALSE; - public $skip_bottom_tick = FALSE; - public $skip_left_tick = FALSE; - public $skip_right_tick = FALSE; - -//Grid Formatting - // public $draw_x_grid = FALSE; // Default is False except for swapped data type - // public $draw_y_grid = TRUE; // Default is True except for swapped data type - - public $dashed_grid = TRUE; - public $grid_at_foreground = FALSE; // Chooses whether to draw the grid below or above the graph - -//Colors and styles (all colors can be array (R,G,B) or named color) - public $color_array = 'small'; // 'small', 'large' or array (define your own colors) - // See rgb.inc.php and SetRGBArray() - public $default_colors = array( // The default colors for data and error bars - 'SkyBlue', 'green', 'orange', 'blue', 'red', 'DarkGreen', 'purple', 'peru', - 'cyan', 'salmon', 'SlateBlue', 'YellowGreen', 'magenta', 'aquamarine1', 'gold', 'violet'); - - // See SetDefaultStyles() for default colors for PHPlot elements. - - public $line_widths = 1; // single value or array - public $line_styles = array('solid', 'solid', 'dashed'); // single value or array - public $dashed_style = '2-4'; // colored dots-transparent dots - - public $point_sizes = array(6); // Array of sizes for points. See CheckPointParams() - public $point_shapes = array( // Array of point shapes. See SetPointShapes() and DrawDot() - 'diamond', 'dot', 'delta', 'home', 'yield', 'box', 'circle', 'up', 'down', 'cross' - ); - - public $error_bar_size = 5; // right and left size of tee - public $error_bar_shape = 'tee'; // 'tee' or 'line' - public $error_bar_line_width = 1; // single value (or array TODO) - - public $plot_border_type = 'sides'; // left, right, top, bottom, sides, none, full; or array - public $image_border_type = 'none'; // 'raised', 'plain', 'none' - // public $image_border_width; // NULL, 0, or unset for default. Default depends on type. - - public $shading = 5; // 0 for no shading, > 0 is size of shadows in pixels - - public $draw_plot_area_background = FALSE; - public $draw_broken_lines = FALSE; // Tells not to draw lines for missing Y data. - -//Miscellaneous - public $callbacks = array( // Valid callback reasons (see SetCallBack) - 'draw_setup' => NULL, - 'draw_image_background' => NULL, - 'draw_plotarea_background' => NULL, - 'draw_titles' => NULL, - 'draw_axes' => NULL, - 'draw_graph' => NULL, - 'draw_border' => NULL, - 'draw_legend' => NULL, - 'draw_all' => NULL, - 'data_color' => NULL, - 'debug_textbox' => NULL, // For testing/debugging text box alignment - 'debug_scale' => NULL, // For testing/debugging scale setup - ); - - // Defined plot types static array: - // Array key is the plot type. (Upper case letters are not allowed due to CheckOption) - // Value is an array with these keys: - // draw_method (required) : Class method to call to draw the plot. - // draw_arg : Optional array of arguments to pass to draw_method. - // draw_axes : If FALSE, do not draw X/Y axis lines, labels, ticks, grid, titles. - // abs_vals, sum_vals : Data array processing flags. See FindDataLimits(). - static protected $plots = array( - 'area' => array( - 'draw_method' => 'DrawArea', - 'abs_vals' => TRUE, - ), - 'bars' => array( - 'draw_method' => 'DrawBars', - ), - 'candlesticks' => array( - 'draw_method' => 'DrawOHLC', - 'draw_arg' => array(TRUE, FALSE), // Draw candlesticks, only fill if "closed down" - ), - 'candlesticks2' => array( - 'draw_method' => 'DrawOHLC', - 'draw_arg' => array(TRUE, TRUE), // Draw candlesticks, fill always - ), - 'linepoints' => array( - 'draw_method' => 'DrawLinePoints', - ), - 'lines' => array( - 'draw_method' => 'DrawLines', - ), - 'ohlc' => array( - 'draw_method' => 'DrawOHLC', - 'draw_arg' => array(FALSE), // Don't draw candlesticks - ), - 'pie' => array( - 'draw_method' => 'DrawPieChart', - 'draw_axes' => FALSE, - 'abs_vals' => TRUE, - ), - 'points' => array( - 'draw_method' => 'DrawDots', - ), - 'squared' => array( - 'draw_method' => 'DrawSquared', - ), - 'stackedarea' => array( - 'draw_method' => 'DrawArea', - 'draw_arg' => array(TRUE), // Tells DrawArea to draw stacked area plot - 'sum_vals' => TRUE, - 'abs_vals' => TRUE, - ), - 'stackedbars' => array( - 'draw_method' => 'DrawStackedBars', - 'sum_vals' => TRUE, - ), - 'thinbarline' => array( - 'draw_method' => 'DrawThinBarLines', - ), - ); - -////////////////////////////////////////////////////// -//BEGIN CODE -////////////////////////////////////////////////////// - - /* - * Constructor: Setup img resource, colors and size of the image, and font sizes. - * - * $which_width : Image width in pixels. - * $which_height : Image height in pixels. - * $which_output_file : Filename for output. - * $which_input_file : Path to a file to be used as background. - */ - function PHPlot($which_width=600, $which_height=400, $which_output_file=NULL, $which_input_file=NULL) - { - $this->SetRGBArray($this->color_array); - - if ($which_output_file) - $this->SetOutputFile($which_output_file); - - if ($which_input_file) { - $this->SetInputFile($which_input_file); - } else { - $this->image_width = $which_width; - $this->image_height = $which_height; - - $this->img = ImageCreate($this->image_width, $this->image_height); - if (! $this->img) - return $this->PrintError('PHPlot(): Could not create image resource.'); - } - - $this->SetDefaultStyles(); - $this->SetDefaultFonts(); - } - - /* - * Reads an image file. Stores width and height, and returns the image - * resource. On error, calls PrintError and returns False. - * This is used by the constructor via SetInputFile, and by tile_img(). - */ - protected function GetImage($image_filename, &$width, &$height) - { - $error = ''; - $size = getimagesize($image_filename); - if (!$size) { - $error = "Unable to query image file $image_filename"; - } else { - $image_type = $size[2]; - switch ($image_type) { - case IMAGETYPE_GIF: - $img = @ ImageCreateFromGIF ($image_filename); - break; - case IMAGETYPE_PNG: - $img = @ ImageCreateFromPNG ($image_filename); - break; - case IMAGETYPE_JPEG: - $img = @ ImageCreateFromJPEG ($image_filename); - break; - default: - $error = "Unknown image type ($image_type) for image file $image_filename"; - break; - } - } - if (empty($error) && !$img) { - // getimagesize is OK, but GD won't read it. Maybe unsupported format. - $error = "Failed to read image file $image_filename"; - } - if (!empty($error)) { - return $this->PrintError("GetImage(): $error"); - } - $width = $size[0]; - $height = $size[1]; - return $img; - } - - /* - * Selects an input file to be used as background for the whole graph. - * This resets the graph size to the image's size. - * Note: This is used by the constructor. It is deprecated for direct use. - */ - function SetInputFile($which_input_file) - { - $im = $this->GetImage($which_input_file, $this->image_width, $this->image_height); - if (!$im) - return FALSE; // GetImage already produced an error message. - - // Deallocate any resources previously allocated - if (isset($this->img)) - imagedestroy($this->img); - - $this->img = $im; - - // Do not overwrite the input file with the background color. - $this->done['background'] = TRUE; - - return TRUE; - } - -///////////////////////////////////////////// -////////////// COLORS -///////////////////////////////////////////// - - /* - * Allocate a GD color index for a color specified by a 4 component array. - * When a color is requested, it is parsed and checked by SetRGBColor, and then saved as an array - * of (R,G,B,A) components. At graph drawing time, this function is used to allocate the color. - * $color : The color specification as a 4 component array: R, G, B, A. - * Returns: A GD color index that can be used when drawing. - */ - protected function GetColorIndex($color) - { - list($r, $g, $b, $a) = $color; - return imagecolorresolvealpha($this->img, $r, $g, $b, $a); - } - - /* - * Allocate an array of GD color indexes for an array of color specifications. - * This is used for the data_colors array, for example. - * $color_array : Array of color specifications, each an array of R,G,B,A components. - * This must use 0-based sequential integer indexes. - * $max_colors : Limit color allocation to no more than this. - * Returns an array of GD color indexes. - */ - protected function GetColorIndexArray($color_array, $max_colors) - { - $n = min(count($color_array), $max_colors); - $result = array(); - for ($i = 0; $i < $n; $i++) - $result[] = $this->GetColorIndex($color_array[$i]); - return $result; - } - - /* - * Allocate an array of GD color indexes for darker shades of an array of color specifications. - * $color_array : Array of color specifications, each an array of R,G,B,A components. - * $max_colors : Limit color allocation to this many colors from the array. - * Returns an array of GD color indexes. - */ - protected function GetDarkColorIndexArray($color_array, $max_colors) - { - $n = min(count($color_array), $max_colors); - $result = array(); - for ($i = 0; $i < $n; $i++) - $result[] = $this->GetDarkColorIndex($color_array[$i]); - return $result; - } - - /* - * Allocate a GD color index for a darker shade of a color specified by a 4 component array. - * See notes for GetColorIndex() above. - * $color : The color specification as a 4 component array: R, G, B, A. - * Returns: A GD color index that can be used when drawing. - */ - protected function GetDarkColorIndex($color) - { - list ($r, $g, $b, $a) = $color; - $r = max(0, $r - 0x30); - $g = max(0, $g - 0x30); - $b = max(0, $b - 0x30); - return imagecolorresolvealpha($this->img, $r, $g, $b, $a); - } - - /* - * Sets/reverts all colors and styles to their defaults. - */ - protected function SetDefaultStyles() - { - $this->SetDefaultDashedStyle($this->dashed_style); - $this->SetImageBorderColor(array(194, 194, 194)); - $this->SetPlotBgColor('white'); - $this->SetBackgroundColor('white'); - $this->SetTextColor('black'); - $this->SetGridColor('black'); - $this->SetLightGridColor('gray'); - $this->SetTickColor('black'); - $this->SetTitleColor('black'); - // These functions set up the default colors when called without parameters - $this->SetDataColors(); - $this->SetErrorBarColors(); - $this->SetDataBorderColors(); - return TRUE; - } - - /* - * Set the image background color to $which_color. - */ - function SetBackgroundColor($which_color) - { - return (bool)($this->bg_color = $this->SetRGBColor($which_color)); - } - - /* - * Set the plot area background color (if enabled) to $which_color. - */ - function SetPlotBgColor($which_color) - { - return (bool)($this->plot_bg_color = $this->SetRGBColor($which_color)); - } - - /* - * Set the color of the titles (main, X, and Y) to $which_color. - * See also SetXTitleColor and SetYTitleColor. - */ - function SetTitleColor($which_color) - { - return (bool)($this->title_color = $this->SetRGBColor($which_color)); - } - - /* - * Set the color of the X title to $which_color. - * This overrides the color set with SetTitleColor. - */ - function SetXTitleColor($which_color) - { - return (bool)($this->x_title_color = $this->SetRGBColor($which_color)); - } - - /* - * Set the color of the Y title to $which_color. - * This overrides the color set with SetTitleColor. - */ - function SetYTitleColor($which_color) - { - return (bool)($this->y_title_color = $this->SetRGBColor($which_color)); - } - - /* - * Set the color of the axis tick marks to $which_color. - */ - function SetTickColor($which_color) - { - return (bool)($this->tick_color = $this->SetRGBColor($which_color)); - } - - /* - * Deprecated. Use SetTitleColor() - */ - function SetLabelColor($which_color) - { - return $this->SetTitleColor($which_color); - } - - /* - * Set the general text color (tick and data labels, legend, etc) to $which_color. - */ - function SetTextColor($which_color) - { - return (bool)($this->text_color = $this->SetRGBColor($which_color)); - } - - /* - * Set the X and Y grid colors to $which_color. Also sets the data label line color. - */ - function SetLightGridColor($which_color) - { - return (bool)($this->light_grid_color = $this->SetRGBColor($which_color)); - } - - /* - * Set the color used for the X and Y axis, plot border, legend border to $which_color. - * Note: This has nothing to do with the grid, and we don't recall where this name came from. - */ - function SetGridColor($which_color) - { - return (bool)($this->grid_color = $this->SetRGBColor($which_color)); - } - - /* - * Set the color used for the image border to $which_color. - */ - function SetImageBorderColor($which_color) - { - return (bool)($this->i_border = $this->SetRGBColor($which_color)); - } - - /* - * Designate color $which_color to be transparent, if supported by the image format. - */ - function SetTransparentColor($which_color) - { - return (bool)($this->transparent_color = $this->SetRGBColor($which_color)); - } - - /* - * Sets the array of colors to be used. It can be user defined, a small predefined one - * or a large one included from 'rgb.inc.php'. - * - * $which_color_array : A color array, or 'small' or 'large'. - * Color arrays map color names into arrays of R, G, B and optionally A values. - */ - function SetRGBArray($which_color_array) - { - if (is_array($which_color_array)) { // User defined array - $this->rgb_array = $which_color_array; - } elseif ($which_color_array == 'small') { // Small predefined color array - $this->rgb_array = array( - 'white' => array(255, 255, 255), - 'snow' => array(255, 250, 250), - 'PeachPuff' => array(255, 218, 185), - 'ivory' => array(255, 255, 240), - 'lavender' => array(230, 230, 250), - 'black' => array( 0, 0, 0), - 'DimGrey' => array(105, 105, 105), - 'gray' => array(190, 190, 190), - 'grey' => array(190, 190, 190), - 'navy' => array( 0, 0, 128), - 'SlateBlue' => array(106, 90, 205), - 'blue' => array( 0, 0, 255), - 'SkyBlue' => array(135, 206, 235), - 'cyan' => array( 0, 255, 255), - 'DarkGreen' => array( 0, 100, 0), - 'green' => array( 0, 255, 0), - 'YellowGreen' => array(154, 205, 50), - 'yellow' => array(255, 255, 0), - 'orange' => array(255, 165, 0), - 'gold' => array(255, 215, 0), - 'peru' => array(205, 133, 63), - 'beige' => array(245, 245, 220), - 'wheat' => array(245, 222, 179), - 'tan' => array(210, 180, 140), - 'brown' => array(165, 42, 42), - 'salmon' => array(250, 128, 114), - 'red' => array(255, 0, 0), - 'pink' => array(255, 192, 203), - 'maroon' => array(176, 48, 96), - 'magenta' => array(255, 0, 255), - 'violet' => array(238, 130, 238), - 'plum' => array(221, 160, 221), - 'orchid' => array(218, 112, 214), - 'purple' => array(160, 32, 240), - 'azure1' => array(240, 255, 255), - 'aquamarine1' => array(127, 255, 212) - ); - } elseif ($which_color_array == 'large') { // Large color array - if (!@include('rgb.inc.php')) { - return $this->PrintError("SetRGBArray(): Large color map could not be loaded\n" - . "from 'rgb.inc.php'."); - } - $this->rgb_array = $ColorArray; - } else { // Default to black and white only. - $this->rgb_array = array('white' => array(255, 255, 255), 'black' => array(0, 0, 0)); - } - - return TRUE; - } - - /* - * Parse a color description and return the color component values. - * Arguments: - * $color_asked : The desired color description, in one of these forms: - * Component notation: array(R, G, B) or array(R, G, B, A) with each - * in the range described below for the return value. - * Examples: (255,255,0) (204,0,0,30) - * Hex notation: "#RRGGBB" or "#RRGGBBAA" where each pair is a 2 digit hex number. - * Examples: #FF00FF (magenta) #0000FF40 (Blue with alpha=64/127) - * Named color in the current colormap, with optional suffix ":alpha" for alpha value. - * Examples: blue red:60 yellow:20 - * $alpha : optional default alpha value. This is applied to the color if it doesn't - * already have an alpha value. If not supplied, colors are opaque (alpha=0) by default. - * - * Returns an array describing a color as (R, G, B, Alpha). - * R, G, and B are integers 0-255, and Alpha is 0 (opaque) to 127 (transparent). - * Note: This function should be considered 'protected', and is not documented for public use. - */ - function SetRGBColor($color_asked, $alpha = 0) - { - if (empty($color_asked)) { - $ret_val = array(0, 0, 0); - - } elseif (is_array($color_asked) && (($n = count($color_asked)) == 3 || $n == 4) ) { - // Already an array of 3 or 4 elements: - $ret_val = $color_asked; - - } elseif (preg_match('/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})?$/i', - $color_asked, $ss)) { - // #RRGGBB or #RRGGBBAA notation: - $ret_val = array(hexdec($ss[1]), hexdec($ss[2]), hexdec($ss[3])); - if (isset($ss[4])) $ret_val[] = hexdec($ss[4]); - - } elseif (isset($this->rgb_array[$color_asked])) { - // Color by name: - $ret_val = $this->rgb_array[$color_asked]; - - } elseif (preg_match('/(.+):([\d]+)$/', $color_asked, $ss) - && isset($this->rgb_array[$ss[1]])) { - // Color by name with ":alpha" suffix, alpha is a decimal number: - $ret_val = $this->rgb_array[$ss[1]]; - $ret_val[3] = (int)$ss[2]; - - } else { - return $this->PrintError("SetRGBColor(): Color '$color_asked' is not valid."); - } - - // Append alpha if not already provided for: - if (count($ret_val) == 3) - $ret_val[] = $alpha; - return $ret_val; - } - - /* - * Sets the colors for the data, with optional default alpha value - * Cases are: - * SetDataColors(array(...)) : Use the supplied array as the color map. - * SetDataColors(colorname) : Use an array of just colorname as the color map. - * SetDataColors() or SetDataColors(NULL) : Load default color map if no color map is already set. - * SetDataColors('') or SetDataColors(False) : Load default color map (even if one is already set). - * $which_border is passed to SetDataBorderColors, for backward compatibility. - * $alpha is a default Alpha to apply to all data colors that do not have alpha. - * The default for this is NULL, not 0, so we can tell if it was defaulted. But the effective - * default value is 0 (opaque). - */ - function SetDataColors($which_data = NULL, $which_border = NULL, $alpha = NULL) - { - if (is_array($which_data)) { - $colors = $which_data; // Use supplied array - } elseif (!empty($which_data)) { - $colors = array($which_data); // Use supplied single color - } elseif (empty($this->data_colors) || !is_null($which_data)) { - $colors = $this->default_colors; // Use default color array - } else { - // which_data is NULL or missing and a color array is already set. - // The existing color array is left alone, except that if $alpha is - // given this will replace the alpha value of each existing color. - // This makes SetDataColors(NULL, NULL, $alpha) work. - if (isset($alpha)) { - $n_colors = count($this->data_colors); - for ($i = 0; $i < $n_colors; $i++) { - $this->data_colors[$i][3] = $alpha; // Component 3 = alpha value - } - } - // No need to reparse the colors or anything else. - return TRUE; - } - - if (!isset($alpha)) - $alpha = 0; // Actual default is opaque colors. - - // Check each color and convert to array (r,g,b,a) form. - // Use the $alpha argument as a default for the alpha value of each color. - $this->data_colors = array(); - foreach ($colors as $color) { - $color_array = $this->SetRGBColor($color, $alpha); - if (!$color_array) return FALSE; // SetRGBColor already did an error message. - $this->data_colors[] = $color_array; - } - - // For past compatibility: - return $this->SetDataBorderColors($which_border); - } - - /* - * Set the colors for the bars and stacked bars outlines. - * Argument usage is similar to SetDataColors(), except the default is just black. - */ - function SetDataBorderColors($which_br = NULL) - { - if (is_array($which_br)) { - $colors = $which_br; // Use supplied array - } elseif (!empty($which_br)) { - $colors = array($which_br); // Use supplied single color - } elseif (empty($this->data_border_colors) || !is_null($which_br)) { - $colors = array('black'); // Use default - } else { - return TRUE; // Do nothing: which_br is NULL or missing and a color array is already set. - } - - // Check each color and convert to array (r,g,b,a) form. - $this->data_border_colors = array(); - foreach ($colors as $color) { - $color_array = $this->SetRGBColor($color); - if (!$color_array) return FALSE; // SetRGBColor already did an error message. - $this->data_border_colors[] = $color_array; - } - return TRUE; - } - - /* - * Sets the colors for the data error bars. - * Argument usage is the same as SetDataColors(). - */ - function SetErrorBarColors($which_err = NULL) - { - if (is_array($which_err)) { - $colors = $which_err; // Use supplied array - } elseif (!empty($which_err)) { - $colors = array($which_err); // Use supplied single color - } elseif (empty($this->error_bar_colors) || !is_null($which_err)) { - $colors = $this->default_colors; // Use default color array - } else { - return TRUE; // Do nothing: which_err is NULL or missing and a color array is already set. - } - - // Check each color and convert to array (r,g,b,a) form. - $this->error_bar_colors = array(); - foreach ($colors as $color) { - $color_array = $this->SetRGBColor($color); - if (!$color_array) return FALSE; // SetRGBColor already did an error message. - $this->error_bar_colors[] = $color_array; - } - return TRUE; - } - - /* - * Sets the default dashed line style. - * $which_style : A string specifying the dashed line style, as alternating numbers - * of the length (in pixels) of lines and spaces, separated by dashes. - * For example: '2-3-1-2' means 2 dots of color, 3 transparent, 1 color, then 2 transparent. - * This builds a string which will evaluate to an array of integers. Each colored dot - * is '$which_ndxcol' and each transparent dot is 'IMG_COLOR_TRANSPARENT'. When SetDashedStyle() - * eval's this with $which_ndxcol set, the result is a GD line style array. - */ - function SetDefaultDashedStyle($which_style) - { - // Explode "numcol-numtrans-numcol-numtrans..." into segment counts: - $asked = explode('-', $which_style); - - if (count($asked) < 2) { - return $this->PrintError("SetDefaultDashedStyle(): Wrong parameter '$which_style'."); - } - - // Build the string to be evaluated later by SetDashedStyle() with $which_ndxcolor set. - $result = ''; - $vals = array('$which_ndxcol,', 'IMG_COLOR_TRANSPARENT,'); - $index = 0; - foreach ($asked as $n) { - $result .= str_repeat($vals[$index], $n); - $index = 1 - $index; - } - $this->default_dashed_style = "array($result)"; - - return TRUE; - } - - /* - * Sets the style before drawing a dashed line. Defaults to $this->default_dashed_style - * $which_ndxcol : Color index to be used. - */ - protected function SetDashedStyle($which_ndxcol) - { - // See SetDefaultDashedStyle() to understand this. - eval ("\$style = $this->default_dashed_style;"); - return imagesetstyle($this->img, $style); - } - - /* - * Set line widths for each data set. - * $which_lw : Array of line widths in pixels, or a single value to use for all data sets. - */ - function SetLineWidths($which_lw=NULL) - { - if (is_array($which_lw)) { - $this->line_widths = $which_lw; // Use provided array - } elseif (!is_null($which_lw)) { - $this->line_widths = array($which_lw); // Convert value to array - } - return TRUE; - } - - /* - * Set line style ('solid' or 'dashed') for each data set. - * $which_ls : Array of keywords, or a single keyword to use for all data sets. - */ - function SetLineStyles($which_ls=NULL) - { - if (is_array($which_ls)) { - $this->line_styles = $which_ls; // Use provided array - } elseif (!is_null($which_ls)) { - $this->line_styles = ($which_ls) ? array($which_ls) : array('solid'); - } - return TRUE; - } - -///////////////////////////////////////////// -////////////// TEXT and FONTS -///////////////////////////////////////////// - - /* - * Controls the line spacing of multi-line labels. - * $which_spc : Line spacing factor for text - * For GD text, this is the number of pixels between lines. - * For TTF text, it controls line spacing in proportion to the normal - * spacing defined by the font. - */ - function SetLineSpacing($which_spc) - { - $this->line_spacing = $which_spc; - return TRUE; - } - - /* - * Select the default font type to use. - * $which_ttf : True to default to TrueType, False to default to GD (fixed) fonts. - * This also resets all font settings to the defaults. - */ - function SetUseTTF($which_ttf) - { - $this->use_ttf = $which_ttf; - return $this->SetDefaultFonts(); - } - - /* - * Sets the directory name to look into for TrueType fonts. - */ - function SetTTFPath($which_path) - { - if (!is_dir($which_path) || !is_readable($which_path)) { - return $this->PrintError("SetTTFPath(): $which_path is not a valid path."); - } - $this->ttf_path = $which_path; - return TRUE; - } - - /* - * Sets the default TrueType font and updates all fonts to that. - * The default font might be a full path, or relative to the TTFPath, - * so let SetFont check that it exists. - * Side effects: Enables use of TrueType fonts as the default font type, - * and resets all font settings. - */ - function SetDefaultTTFont($which_font) - { - $this->default_ttfont = $which_font; - return $this->SetUseTTF(TRUE); - } - - /* - * Return the default TrueType font name. If no default has been set, - * this tries some likely candidates for a font which can be loaded. - * If it finds one that works, that becomes the default TT font. - * If there is no default and it cannot find a working font, it falls - * back to the original PHPlot default (which will not likely work either). - */ - protected function GetDefaultTTFont() - { - if (!isset($this->default_ttfont)) { - // No default font yet. Try some common sans-serif fonts. - $fonts = array('LiberationSans-Regular.ttf', // For Linux with a correct GD font search path - 'Verdana.ttf', 'Arial.ttf', 'Helvetica.ttf', // For Windows, maybe others - 'ttf-liberation/LiberationSans-Regular.ttf', // For Debian, Ubuntu, and friends - 'benjamingothic.ttf', // Original PHPlot default - ); - foreach ($fonts as $font) { - // First try the font name alone, to see if GD can find and load it. - if (@imagettfbbox(10, 0, $font, "1") !== False) - break; - // If the font wasn't found, try it with the default TTF path in front. - $font_with_path = $this->ttf_path . DIRECTORY_SEPARATOR . $font; - if (@imagettfbbox(10, 0, $font_with_path, "1") !== False) { - $font = $font_with_path; - break; - } - } - // We either have a working font, or are using the last one regardless. - $this->default_ttfont = $font; - } - return $this->default_ttfont; - } - - /* - * Sets fonts to their defaults - */ - protected function SetDefaultFonts() - { - // TTF: - if ($this->use_ttf) { - return $this->SetFont('generic', '', 8) - && $this->SetFont('title', '', 14) - && $this->SetFont('legend', '', 8) - && $this->SetFont('x_label', '', 6) - && $this->SetFont('y_label', '', 6) - && $this->SetFont('x_title', '', 10) - && $this->SetFont('y_title', '', 10); - } - // Fixed GD Fonts: - return $this->SetFont('generic', 2) - && $this->SetFont('title', 5) - && $this->SetFont('legend', 2) - && $this->SetFont('x_label', 1) - && $this->SetFont('y_label', 1) - && $this->SetFont('x_title', 3) - && $this->SetFont('y_title', 3); - } - - /* - * Select a fixed (GD) font for an element. - * This allows using a fixed font, even with SetUseTTF(True). - * $which_elem : The element whose font is to be changed. - * One of: title legend generic x_label y_label x_title y_title - * $which_font : A GD font number 1-5 - * $which_spacing (optional) : Line spacing factor - */ - function SetFontGD($which_elem, $which_font, $which_spacing = NULL) - { - if ($which_font < 1 || 5 < $which_font) { - return $this->PrintError(__FUNCTION__ . ': Font size must be 1, 2, 3, 4 or 5'); - } - if (!$this->CheckOption($which_elem, - 'generic, title, legend, x_label, y_label, x_title, y_title', - __FUNCTION__)) { - return FALSE; - } - - // Store the font parameters: name/size, char cell height and width. - $this->fonts[$which_elem] = array('ttf' => FALSE, - 'font' => $which_font, - 'height' => ImageFontHeight($which_font), - 'width' => ImageFontWidth($which_font), - 'line_spacing' => $which_spacing); - return TRUE; - } - - /* - * Select a TrueType font for an element. - * This allows using a TrueType font, even with SetUseTTF(False). - * $which_elem : The element whose font is to be changed. - * One of: title legend generic x_label y_label x_title y_title - * $which_font : A TrueType font filename or pathname. - * $which_size : Font point size. - * $which_spacing (optional) : Line spacing factor - */ - function SetFontTTF($which_elem, $which_font, $which_size = 12, $which_spacing = NULL) - { - if (!$this->CheckOption($which_elem, - 'generic, title, legend, x_label, y_label, x_title, y_title', - __FUNCTION__)) { - return FALSE; - } - - // Empty font name means use the default font. - if (empty($which_font)) - $which_font = $this->GetDefaultTTFont(); - $path = $which_font; - - // First try the font name directly, if not then try with path. - // Use GD imagettfbbox() to determine if this is a valid font. - // The return $bbox is used below, if valid. - if (($bbox = @imagettfbbox($which_size, 0, $path, "E")) === False) { - $path = $this->ttf_path . DIRECTORY_SEPARATOR . $which_font; - if (($bbox = @imagettfbbox($which_size, 0, $path, "E")) === False) { - return $this->PrintError(__FUNCTION__ . ": Can't find TrueType font $which_font"); - } - } - - // Calculate the font height and inherent line spacing. TrueType fonts have this information - // internally, but PHP/GD has no way to directly access it. So get the bounding box size of - // an upper-case character without descenders, and the baseline-to-baseline height. - // Note: In practice, $which_size = $height, maybe +/-1 . But which_size is in points, - // and height is in pixels, and someday GD may be able to tell the difference. - // The character width is saved too, but not used by the normal text drawing routines - it - // isn't necessarily a fixed-space font. It is used in DrawLegend. - $height = $bbox[1] - $bbox[5]; - $width = $bbox[2] - $bbox[0]; - $bbox = ImageTTFBBox($which_size, 0, $path, "E\nE"); - $spacing = $bbox[1] - $bbox[5] - 2 * $height; - - // Store the font parameters: - $this->fonts[$which_elem] = array('ttf' => TRUE, - 'font' => $path, - 'size' => $which_size, - 'height' => $height, - 'width' => $width, - 'spacing' => $spacing, - 'line_spacing' => $which_spacing); - return TRUE; - } - - /* - * Select Fixed/TrueType font for an element. Which type of font is - * selected depends on the $use_ttf class variable (see SetUseTTF()). - * Before PHPlot supported mixing font types, only this function and - * SetUseTTF were available to select an overall font type, but now - * SetFontGD() and SetFontTTF() can be used for mixing font types. - * $which_elem : The element whose font is to be changed. - * One of: title legend generic x_label y_label x_title y_title - * $which_font : A number 1-5 for fixed fonts, or a TrueType font. - * $which_size : Ignored for Fixed fonts, point size for TrueType. - * $which_spacing (optional) : Line spacing factor - */ - function SetFont($which_elem, $which_font, $which_size = 12, $line_spacing = NULL) - { - if ($this->use_ttf) - return $this->SetFontTTF($which_elem, $which_font, $which_size, $line_spacing); - return $this->SetFontGD($which_elem, $which_font, $line_spacing); - } - - /* - * Return the inter-line spacing for a font. - * This is an internal function, used by ProcessText* and DrawLegend. - * $font : A font array variable. - * Returns: Spacing, in pixels, between text lines. - */ - protected function GetLineSpacing($font) - { - // Use the per-font line spacing preference, if set, else the global value: - if (isset($font['line_spacing'])) - $line_spacing = $font['line_spacing']; - else - $line_spacing = $this->line_spacing; - - // For GD fonts, that is the spacing in pixels. - // For TTF, adjust based on the 'natural' font spacing (see SetFontTTF): - if ($font['ttf']) { - $line_spacing = (int)($line_spacing * $font['spacing'] / 6.0); - } - return $line_spacing; - } - - /* - * Text drawing and sizing functions: - * ProcessText is meant for use only by DrawText and SizeText. - * ProcessText(True, ...) - Draw a block of text - * ProcessText(False, ...) - Just return ($width, $height) of - * the orthogonal bounding box containing the text. - * ProcessText is further split into separate functions for GD and TTF - * text, due to the size of the code. - * - * Horizontal and vertical alignment are relative to the drawing. That is: - * vertical text (90 deg) gets centered along Y position with - * v_align = 'center', and adjusted to the right of X position with - * h_align = 'right'. Another way to look at this is to say - * that text rotation happens first, then alignment. - * - * Original multiple lines code submitted by Remi Ricard. - * Original vertical code submitted by Marlin Viss. - * - * Text routines rewritten by ljb to fix alignment and position problems. - * Here is my explanation and notes. More information and pictures will be - * placed in the PHPlot Reference Manual. - * - * + Process TTF text one line at a time, not as a block. (See below) - * + Flipped top vs bottom vertical alignment. The usual interpretation - * is: bottom align means bottom of the text is at the specified Y - * coordinate. For some reason, PHPlot did left/right the correct way, - * but had top/bottom reversed. I fixed it, and left the default valign - * argument as bottom, but the meaning of the default value changed. - * - * For GD font text, only single-line text is handled by GD, and the - * basepoint is the upper left corner of each text line. - * For TTF text, multi-line text could be handled by GD, with the text - * basepoint at the lower left corner of the first line of text. - * (Behavior of TTF drawing routines on multi-line text is not documented.) - * But you cannot do left/center/right alignment on each line that way, - * or proper line spacing. - * Therefore, for either text type, we have to break up the text into - * lines and position each line independently. - * - * There are 9 alignment modes: Horizontal = left, center, or right, and - * Vertical = top, center, or bottom. Alignment is interpreted relative to - * the image, not as the text is read. This makes sense when you consider - * for example X axis labels. They need to be centered below the marks - * (center, top alignment) regardless of the text angle. - * 'Bottom' alignment really means baseline alignment. - * - * GD font text is supported (by libgd) at 0 degrees and 90 degrees only. - * Multi-line or single line text works with any of the 9 alignment modes. - * - * TTF text can be at any angle. The 9 alignment modes work for all angles, - * but the results might not be what you expect for multi-line text. See - * the PHPlot Reference Manual for pictures and details. In short, alignment - * applies to the orthogonal (aligned with X and Y axes) bounding box that - * contains the text, and to each line in the multi-line text box. Since - * alignment is relative to the image, 45 degree multi-line text aligns - * differently from 46 degree text. - * - * Note that PHPlot allows multi-line text for the 3 titles, and they - * are only drawn at 0 degrees (main and X titles) or 90 degrees (Y title). - * Data labels can also be multi-line, and they can be drawn at any angle. - * -ljb 2007-11-03 - * - */ - - /* - * ProcessTextGD() - Draw or size GD fixed-font text. - * This is intended for use only by ProcessText(). - * $draw_it : True to draw the text, False to just return the orthogonal width and height. - * $font : PHPlot font array (with 'ttf' = False) - see SetFontGD() - * $angle : Text angle in degrees. GD only supports 0 and 90. We treat >= 45 as 90, else 0. - * $x, $y : Reference point for the text (ignored if !$draw_it) - * $color : GD color index to use for drawing the text (ignored if !$draw_it) - * $text : The text to draw or size. Put a newline between lines. - * $h_factor : Horizontal alignment factor: 0(left), .5(center), or 1(right) (ignored if !$draw_it) - * $v_factor : Vertical alignment factor: 0(top), .5(center), or 1(bottom) (ignored if !$draw_it) - * Returns: True, if drawing text, or an array of ($width, $height) if not. - */ - protected function ProcessTextGD($draw_it, $font, $angle, $x, $y, $color, $text, $h_factor, $v_factor) - { - // Extract font parameters: - $font_number = $font['font']; - $font_width = $font['width']; - $font_height = $font['height']; - $line_spacing = $this->GetLineSpacing($font); - - // Break up the text into lines, trim whitespace, find longest line. - // Save the lines and length for drawing below. - $longest = 0; - foreach (explode("\n", $text) as $each_line) { - $lines[] = $line = trim($each_line); - $line_lens[] = $line_len = strlen($line); - if ($line_len > $longest) $longest = $line_len; - } - $n_lines = count($lines); - - // Width, height are based on font size and longest line, line count respectively. - // These are relative to the text angle. - $total_width = $longest * $font_width; - $total_height = $n_lines * $font_height + ($n_lines - 1) * $line_spacing; - - if (!$draw_it) { - if ($angle < 45) return array($total_width, $total_height); - return array($total_height, $total_width); - } - - $interline_step = $font_height + $line_spacing; // Line-to-line step - - if ($angle >= 45) { - // Vertical text (90 degrees): - // (Remember the alignment convention with vertical text) - // For 90 degree text, alignment factors change like this: - $temp = $v_factor; - $v_factor = $h_factor; - $h_factor = 1 - $temp; - - $draw_func = 'ImageStringUp'; - - // Rotation matrix "R" for 90 degrees (with Y pointing down): - $r00 = 0; $r01 = 1; - $r10 = -1; $r11 = 0; - - } else { - // Horizontal text (0 degrees): - $draw_func = 'ImageString'; - - // Rotation matrix "R" for 0 degrees: - $r00 = 1; $r01 = 0; - $r10 = 0; $r11 = 1; - } - - // Adjust for vertical alignment (horizontal text) or horizontal alignment (vertical text): - $factor = (int)($total_height * $v_factor); - $xpos = $x - $r01 * $factor; - $ypos = $y - $r11 * $factor; - - // Debug callback provides the bounding box: - if ($this->GetCallback('debug_textbox')) { - if ($angle >= 45) { - $bbox_width = $total_height; - $bbox_height = $total_width; - $px = $xpos; - $py = $ypos - (1 - $h_factor) * $total_width; - } else { - $bbox_width = $total_width; - $bbox_height = $total_height; - $px = $xpos - $h_factor * $total_width; - $py = $ypos; - } - $this->DoCallback('debug_textbox', $px, $py, $bbox_width, $bbox_height); - } - - for ($i = 0; $i < $n_lines; $i++) { - - // Adjust for alignment of this line within the text block: - $factor = (int)($line_lens[$i] * $font_width * $h_factor); - $x = $xpos - $r00 * $factor; - $y = $ypos - $r10 * $factor; - - // Call ImageString or ImageStringUp: - $draw_func($this->img, $font_number, $x, $y, $lines[$i], $color); - - // Step to the next line of text. This is a rotation of (x=0, y=interline_spacing) - $xpos += $r01 * $interline_step; - $ypos += $r11 * $interline_step; - } - return TRUE; - } - - /* - * ProcessTextTTF() - Draw or size TTF text. - * This is intended for use only by ProcessText(). - * $draw_it : True to draw the text, False to just return the orthogonal width and height. - * $font : PHPlot font array (with 'ttf' = True) - see SetFontTTF() - * $angle : Text angle in degrees. - * $x, $y : Reference point for the text (ignored if !$draw_it) - * $color : GD color index to use for drawing the text (ignored if !$draw_it) - * $text : The text to draw or size. Put a newline between lines. - * $h_factor : Horizontal alignment factor: 0(left), .5(center), or 1(right) (ignored if !$draw_it) - * $v_factor : Vertical alignment factor: 0(top), .5(center), or 1(bottom) (ignored if !$draw_it) - * Returns: True, if drawing text, or an array of ($width, $height) if not. - */ - protected function ProcessTextTTF($draw_it, $font, $angle, $x, $y, $color, $text, $h_factor, $v_factor) - { - // Extract font parameters (see SetFontTTF): - $font_file = $font['font']; - $font_size = $font['size']; - $font_height = $font['height']; - $line_spacing = $this->GetLineSpacing($font); - - // Break up the text into lines, trim whitespace. - // Calculate the total width and height of the text box at 0 degrees. - // Save the trimmed lines and their widths for later when drawing. - // To get uniform spacing, don't use the actual line heights. - // Total height = Font-specific line heights plus inter-line spacing. - // Total width = width of widest line. - // Last Line Descent is the offset from the bottom to the text baseline. - // Note: For some reason, ImageTTFBBox uses (-1,-1) as the reference point. - // So 1+bbox[1] is the baseline to bottom distance. - $total_width = 0; - $lastline_descent = 0; - foreach (explode("\n", $text) as $each_line) { - $lines[] = $line = trim($each_line); - $bbox = ImageTTFBBox($font_size, 0, $font_file, $line); - $line_widths[] = $width = $bbox[2] - $bbox[0]; - if ($width > $total_width) $total_width = $width; - $lastline_descent = 1 + $bbox[1]; - } - $n_lines = count($lines); - $total_height = $n_lines * $font_height + ($n_lines - 1) * $line_spacing; - - // Calculate the rotation matrix for the text's angle. Remember that GD points Y down, - // so the sin() terms change sign. - $theta = deg2rad($angle); - $cos_t = cos($theta); - $sin_t = sin($theta); - $r00 = $cos_t; $r01 = $sin_t; - $r10 = -$sin_t; $r11 = $cos_t; - - // Make a bounding box of the right size, with upper left corner at (0,0). - // By convention, the point order is: LL, LR, UR, UL. - // Note this is still working with the text at 0 degrees. - // When sizing text (SizeText), use the overall size with descenders. - // This tells the caller how much room to leave for the text. - // When drawing text (DrawText), use the size without descenders - that - // is, down to the baseline. This is for accurate positioning. - $b[0] = 0; - if ($draw_it) { - $b[1] = $total_height; - } else { - $b[1] = $total_height + $lastline_descent; - } - $b[2] = $total_width; $b[3] = $b[1]; - $b[4] = $total_width; $b[5] = 0; - $b[6] = 0; $b[7] = 0; - - // Rotate the bounding box, then offset to the reference point: - for ($i = 0; $i < 8; $i += 2) { - $x_b = $b[$i]; - $y_b = $b[$i+1]; - $c[$i] = $x + $r00 * $x_b + $r01 * $y_b; - $c[$i+1] = $y + $r10 * $x_b + $r11 * $y_b; - } - - // Get an orthogonal (aligned with X and Y axes) bounding box around it, by - // finding the min and max X and Y: - $bbox_ref_x = $bbox_max_x = $c[0]; - $bbox_ref_y = $bbox_max_y = $c[1]; - for ($i = 2; $i < 8; $i += 2) { - $x_b = $c[$i]; - if ($x_b < $bbox_ref_x) $bbox_ref_x = $x_b; - elseif ($bbox_max_x < $x_b) $bbox_max_x = $x_b; - $y_b = $c[$i+1]; - if ($y_b < $bbox_ref_y) $bbox_ref_y = $y_b; - elseif ($bbox_max_y < $y_b) $bbox_max_y = $y_b; - } - $bbox_width = $bbox_max_x - $bbox_ref_x; - $bbox_height = $bbox_max_y - $bbox_ref_y; - - if (!$draw_it) { - // Return the bounding box, rounded up (so it always contains the text): - return array((int)ceil($bbox_width), (int)ceil($bbox_height)); - } - - $interline_step = $font_height + $line_spacing; // Line-to-line step - - // Calculate the offsets from the supplied reference point to the - // upper-left corner of the text. - // Start at the reference point at the upper left corner of the bounding - // box (bbox_ref_x, bbox_ref_y) then adjust it for the 9 point alignment. - // h,v_factor are 0,0 for top,left, .5,.5 for center,center, 1,1 for bottom,right. - // $off_x = $bbox_ref_x + $bbox_width * $h_factor - $x; - // $off_y = $bbox_ref_y + $bbox_height * $v_factor - $y; - // Then use that offset to calculate back to the supplied reference point x, y - // to get the text base point. - // $qx = $x - $off_x; - // $qy = $y - $off_y; - // Reduces to: - $qx = 2 * $x - $bbox_ref_x - $bbox_width * $h_factor; - $qy = 2 * $y - $bbox_ref_y - $bbox_height * $v_factor; - - // Check for debug callback. Don't calculate bounding box unless it is wanted. - if ($this->GetCallback('debug_textbox')) { - // Calculate the orthogonal bounding box coordinates for debug testing. - - // qx, qy is upper left corner relative to the text. - // Calculate px,py: upper left corner (absolute) of the bounding box. - // There are 4 equation sets for this, depending on the quadrant: - if ($sin_t > 0) { - if ($cos_t > 0) { - // Quadrant: 0d - 90d: - $px = $qx; $py = $qy - $total_width * $sin_t; - } else { - // Quadrant: 90d - 180d: - $px = $qx + $total_width * $cos_t; $py = $qy - $bbox_height; - } - } else { - if ($cos_t < 0) { - // Quadrant: 180d - 270d: - $px = $qx - $bbox_width; $py = $qy + $total_height * $cos_t; - } else { - // Quadrant: 270d - 360d: - $px = $qx + $total_height * $sin_t; $py = $qy; - } - } - $this->DoCallback('debug_textbox', $px, $py, $bbox_width, $bbox_height); - } - - // Since alignment is applied after rotation, which parameter is used - // to control alignment of each line within the text box varies with - // the angle. - // Angle (degrees): Line alignment controlled by: - // -45 < angle <= 45 h_align - // 45 < angle <= 135 reversed v_align - // 135 < angle <= 225 reversed h_align - // 225 < angle <= 315 v_align - if ($cos_t >= $sin_t) { - if ($cos_t >= -$sin_t) $line_align_factor = $h_factor; - else $line_align_factor = $v_factor; - } else { - if ($cos_t >= -$sin_t) $line_align_factor = 1-$v_factor; - else $line_align_factor = 1-$h_factor; - } - - // Now we have the start point, spacing and in-line alignment factor. - // We are finally ready to start drawing the text, line by line. - for ($i = 0; $i < $n_lines; $i++) { - - // For drawing TTF text, the reference point is the left edge of the - // text baseline (not the lower left corner of the bounding box). - // The following also adjusts for horizontal (relative to - // the text) alignment of the current line within the box. - // What is happening is rotation of this vector by the text angle: - // (x = (total_width - line_width) * factor, y = font_height) - - $width_factor = ($total_width - $line_widths[$i]) * $line_align_factor; - $rx = $qx + $r00 * $width_factor + $r01 * $font_height; - $ry = $qy + $r10 * $width_factor + $r11 * $font_height; - - // Finally, draw the text: - ImageTTFText($this->img, $font_size, $angle, $rx, $ry, $color, $font_file, $lines[$i]); - - // Step to position of next line. - // This is a rotation of (x=0,y=height+line_spacing) by $angle: - $qx += $r01 * $interline_step; - $qy += $r11 * $interline_step; - } - return TRUE; - } - - /* - * ProcessText() - Wrapper for ProcessTextTTF() and ProcessTextGD(). See notes above. - * This is intended for use from within PHPlot only, and only by DrawText() and SizeText(). - * $draw_it : True to draw the text, False to just return the orthogonal width and height. - * $font : PHPlot font array, or NULL or empty string to use 'generic' - * $angle : Text angle in degrees - * $x, $y : Reference point for the text (ignored if !$draw_it) - * $color : GD color index to use for drawing the text (ignored if !$draw_it) - * $text : The text to draw or size. Put a newline between lines. - * $halign : Horizontal alignment: left, center, or right (ignored if !$draw_it) - * $valign : Vertical alignment: top, center, or bottom (ignored if !$draw_it) - * Note: Alignment is relative to the image, not the text. - * Returns: True, if drawing text, or an array of ($width, $height) if not. - */ - protected function ProcessText($draw_it, $font, $angle, $x, $y, $color, $text, $halign, $valign) - { - // Empty text case: - if ($text === '') { - if ($draw_it) return TRUE; - return array(0, 0); - } - - // Calculate width and height offset factors using the alignment args: - if ($valign == 'top') $v_factor = 0; - elseif ($valign == 'center') $v_factor = 0.5; - else $v_factor = 1.0; // 'bottom' - if ($halign == 'left') $h_factor = 0; - elseif ($halign == 'center') $h_factor = 0.5; - else $h_factor = 1.0; // 'right' - - // Apply a default font. This is mostly for external (callback) users. - if (empty($font)) $font = $this->fonts['generic']; - - if ($font['ttf']) { - return $this->ProcessTextTTF($draw_it, $font, $angle, $x, $y, $color, $text, - $h_factor, $v_factor); - } - return $this->ProcessTextGD($draw_it, $font, $angle, $x, $y, $color, $text, $h_factor, $v_factor); - } - - /* - * Draws a block of text. See comments above before ProcessText(). - * $which_font : PHPlot font array, or NULL or empty string to use 'generic' - * $which_angle : Text angle in degrees - * $which_xpos, $which_ypos: Reference point for the text - * $which_color : GD color index to use for drawing the text - * $which_text : The text to draw, with newlines (\n) between lines. - * $which_halign : Horizontal (relative to the image) alignment: left, center, or right. - * $which_valign : Vertical (relative to the image) alignment: top, center, or bottom. - * Note: This function should be considered 'protected', and is not documented for public use. - */ - function DrawText($which_font, $which_angle, $which_xpos, $which_ypos, $which_color, $which_text, - $which_halign = 'left', $which_valign = 'bottom') - { - return $this->ProcessText(TRUE, - $which_font, $which_angle, $which_xpos, $which_ypos, - $which_color, $which_text, $which_halign, $which_valign); - } - - /* - * Returns the size of block of text. This is the orthogonal width and height of a bounding - * box aligned with the X and Y axes of the text. Only for angle=0 is this the actual - * width and height of the text block, but for any angle it is the amount of space needed - * to contain the text. - * $which_font : PHPlot font array, or NULL or empty string to use 'generic' - * $which_angle : Text angle in degrees - * $which_text : The text to draw, with newlines (\n) between lines. - * Returns a two element array with: $width, $height. - * This is just a wrapper for ProcessText() - see above. - * Note: This function should be considered 'protected', and is not documented for public use. - */ - function SizeText($which_font, $which_angle, $which_text) - { - // Color, position, and alignment are not used when calculating the size. - return $this->ProcessText(FALSE, - $which_font, $which_angle, 0, 0, 1, $which_text, '', ''); - } - -///////////////////////////////////////////// -/////////// INPUT / OUTPUT CONTROL -///////////////////////////////////////////// - - /* - * Sets output file format to $format (jpg, png, ...) - */ - function SetFileFormat($format) - { - $asked = $this->CheckOption($format, 'jpg, png, gif, wbmp', __FUNCTION__); - if (!$asked) return FALSE; - switch ($asked) { - case 'jpg': - $format_test = IMG_JPG; - break; - case 'png': - $format_test = IMG_PNG; - break; - case 'gif': - $format_test = IMG_GIF; - break; - case 'wbmp': - $format_test = IMG_WBMP; - break; - } - if (!(imagetypes() & $format_test)) { - return $this->PrintError("SetFileFormat(): File format '$format' not supported"); - } - $this->file_format = $asked; - return TRUE; - } - - /* - * Selects an input file to be used as graph background and scales or tiles this image - * to fit the sizes. - * $input_file : Path to the file to be used (jpeg, png and gif accepted) - * $mode : 'centeredtile', 'tile', or 'scale' (the image to the graph's size) - */ - function SetBgImage($input_file, $mode='centeredtile') - { - $this->bgmode = $this->CheckOption($mode, 'tile, centeredtile, scale', __FUNCTION__); - $this->bgimg = $input_file; - return (boolean)$this->bgmode; - } - - /* - * Selects an input file to be used as plot area background and scales or tiles this image - * to fit the sizes. - * $input_file : Path to the file to be used (jpeg, png and gif accepted) - * $mode : 'centeredtile', 'tile', or 'scale' (the image to the graph's size) - */ - function SetPlotAreaBgImage($input_file, $mode='tile') - { - $this->plotbgmode = $this->CheckOption($mode, 'tile, centeredtile, scale', __FUNCTION__); - $this->plotbgimg = $input_file; - return (boolean)$this->plotbgmode; - } - - /* - * Sets the name of the file to be used as output file. - */ - function SetOutputFile($which_output_file) - { - $this->output_file = $which_output_file; - return TRUE; - } - - /* - * Sets the output image as 'inline', that is: no Content-Type headers are sent - * to the browser. Needed if you want to embed the images. - */ - function SetIsInline($which_ii) - { - $this->is_inline = (bool)$which_ii; - return TRUE; - } - - /* - * Performs the actual outputting of the generated graph. - */ - function PrintImage() - { - // Browser cache stuff submitted by Thiemo Nagel - if ( (! $this->browser_cache) && (! $this->is_inline)) { - header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); - header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . 'GMT'); - header('Cache-Control: no-cache, must-revalidate'); - header('Pragma: no-cache'); - } - - switch ($this->file_format) { - case 'png': - $mime_type = 'image/png'; - $output_f = 'imagepng'; - break; - case 'jpg': - $mime_type = 'image/jpeg'; - $output_f = 'imagejpeg'; - break; - case 'gif': - $mime_type = 'image/gif'; - $output_f = 'imagegif'; - break; - case 'wbmp': - $mime_type = 'image/wbmp'; - $output_f = 'imagewbmp'; - break; - default: - return $this->PrintError('PrintImage(): Please select an image type!'); - } - if (!$this->is_inline) { - Header("Content-type: $mime_type"); - } - if ($this->is_inline && $this->output_file != '') { - $output_f($this->img, $this->output_file); - } else { - $output_f($this->img); - } - return TRUE; - } - - /* - * Error handling for 'fatal' errors: - * $error_message Text of the error message - * Standard output from PHPlot is expected to be an image file, such as - * when handling an tag browser request. So it is not permitted to - * output text to standard output. (You should have display_errors=off) - * Here is how PHPlot handles fatal errors: - * + Write the error message into an image, and output the image. - * + If no image can be output, write nothing and produce an HTTP - * error header. - * + Trigger a user-level error containing the error message. - * If no error handler was set up, the script will log the - * error and exit with non-zero status. - * - * PrintError() and DrawError() are now equivalent. Both are provided for - * compatibility. (In earlier releases, PrintError sent the message to - * stdout only, and DrawError sent it in an image only.) - * - * This function does not return, unless the calling script has set up - * an error handler which does not exit. In that case, PrintError will - * return False. But not all of PHPlot will handle this correctly, so - * it is probably a bad idea for an error handler to return. - */ - protected function PrintError($error_message) - { - // Be sure not to loop recursively, e.g. PrintError - PrintImage - PrintError. - if (isset($this->in_error)) return FALSE; - $this->in_error = TRUE; - - // Output an image containing the error message: - if (!empty($this->img)) { - $ypos = $this->image_height/2; - $xpos = $this->image_width/2; - $bgcolor = ImageColorResolve($this->img, 255, 255, 255); - $fgcolor = ImageColorResolve($this->img, 0, 0, 0); - ImageFilledRectangle($this->img, 0, 0, $this->image_width, $this->image_height, $bgcolor); - - // Switch to built-in fonts, in case of error with TrueType fonts: - $this->SetUseTTF(FALSE); - - $this->DrawText($this->fonts['generic'], 0, $xpos, $ypos, $fgcolor, - wordwrap($error_message), 'center', 'center'); - - $this->PrintImage(); - } elseif (! $this->is_inline) { - Header('HTTP/1.0 500 Internal Server Error'); - } - trigger_error($error_message, E_USER_ERROR); - unset($this->in_error); - return FALSE; // In case error handler returns, rather than doing exit(). - } - - /* - * Display an error message and exit. - * This is provided for backward compatibility only. Use PrintError() instead. - * $error_message Text of the error message - * $where_x, $where_y Ignored, provided for compatibility. - */ - protected function DrawError($error_message, $where_x = NULL, $where_y = NULL) - { - return $this->PrintError($error_message); - } - -///////////////////////////////////////////// -/////////// LABELS -///////////////////////////////////////////// - - /* - * Sets position for X data labels. - * For vertical plots, these are X axis data labels, showing label strings from the data array. - * Accepted positions are: plotdown, plotup, both, none. - * For horizontal plots (bar, stackedbar only), these are X data value labels, show the data values. - * Accepted positions are: plotin, plotstack, none. - */ - function SetXDataLabelPos($which_xdlp) - { - $which_xdlp = $this->CheckOption($which_xdlp, 'plotdown, plotup, both, none, plotin, plotstack', - __FUNCTION__); - if (!$which_xdlp) return FALSE; - $this->x_data_label_pos = $which_xdlp; - - return TRUE; - } - - /* - * Sets position for Y data labels. - * For vertical plots (where available), these are Y data value labels, showing the data values. - * Accepted positions are: plotin, plotstack, none. - * For horizontal plots, these are Y axis data labels, showing label strings from the data array. - * Accepted positions are: plotleft, plotright, both, none. - */ - function SetYDataLabelPos($which_ydlp) - { - $which_ydlp = $this->CheckOption($which_ydlp, 'plotleft, plotright, both, none, plotin, plotstack', - __FUNCTION__); - if (!$which_ydlp) return FALSE; - $this->y_data_label_pos = $which_ydlp; - - return TRUE; - } - - /* - * Set position for X tick labels. - */ - function SetXTickLabelPos($which_xtlp) - { - $which_xtlp = $this->CheckOption($which_xtlp, 'plotdown, plotup, both, xaxis, none', - __FUNCTION__); - if (!$which_xtlp) return FALSE; - $this->x_tick_label_pos = $which_xtlp; - - return TRUE; - } - - /* - * Set position for Y tick labels. - */ - function SetYTickLabelPos($which_ytlp) - { - $which_ytlp = $this->CheckOption($which_ytlp, 'plotleft, plotright, both, yaxis, none', - __FUNCTION__); - if (!$which_ytlp) return FALSE; - $this->y_tick_label_pos = $which_ytlp; - - return TRUE; - } - - /* - * Set formatting type for tick and data labels on X or Y axis. - * This implements the 4 functions Set[XY]LabelType() and Set[XY]DataLabelType(). - * $mode : 'x', 'y', 'xd', or 'yd' - which type of label to configure. - * 'x' and 'y' set the type for tick labels, and the default type for data labels - * if they are not separately configured. 'xd' and 'yd' set the type for data labels. - * $args : Variable arguments, passed as an array. - * [0] = $type (required) : Label type. 'data', 'time', 'printf', or 'custom'. - * For type 'data': - * [1] = $precision (optional). Numeric precision. Can also be set by SetPrecision[XY](). - * [2] = $prefix (optional) - prefix string for labels. - * [3] = $suffix (optional) - suffix string for labels. This replaces data_units_text. - * For type 'time': - * [1] = $format for strftime (optional). Can also be set by Set[XY]TimeFormat(). - * For type 'printf': - * [1] = $format (optional) for sprintf. - * For type 'custom': - * [1] = $callback (required) - Custom function or array of (instance,method) to call. - * [2] = $argument (optional) - Pass-through argument for the formatting function. - */ - protected function SetLabelType($mode, $args) - { - if (!$this->CheckOption($mode, 'x, y, xd, yd', __FUNCTION__)) - return FALSE; - - $type = isset($args[0]) ? $args[0] : ''; - $format =& $this->label_format[$mode]; // Shorthand reference to format storage variables - switch ($type) { - case 'data': - if (isset($args[1])) - $format['precision'] = $args[1]; - elseif (!isset($format['precision'])) - $format['precision'] = 1; - $format['prefix'] = isset($args[2]) ? $args[2] : ''; - $format['suffix'] = isset($args[3]) ? $args[3] : ''; - break; - - case 'time': - if (isset($args[1])) - $format['time_format'] = $args[1]; - elseif (!isset($format['time_format'])) - $format['time_format'] = '%H:%M:%S'; - break; - - case 'printf': - if (isset($args[1])) - $format['printf_format'] = $args[1]; - elseif (!isset($format['printf_format'])) - $format['printf_format'] = '%e'; - break; - - case 'custom': - if (isset($args[1])) { - $format['custom_callback'] = $args[1]; - $format['custom_arg'] = isset($args[2]) ? $args[2] : NULL; - } else { - $type = ''; // Error, 'custom' without a function, set to no-format mode. - } - break; - - case '': - case 'title': // Retained for backwards compatibility? - break; - - default: - $this->CheckOption($type, 'data, time, printf, custom', __FUNCTION__); - $type = ''; - } - $format['type'] = $type; - return (boolean)$type; - } - - /* - * Select label formating for X tick labels, and for X data labels - * (unless SetXDataLabelType was called). - * See SetLabelType() for details. - */ - function SetXLabelType() // Variable arguments: $type, ... - { - $args = func_get_args(); - return $this->SetLabelType('x', $args); - } - - /* - * Select label formatting for X data labels, overriding SetXLabelType. - */ - function SetXDataLabelType() // Variable arguments: $type, ... - { - $args = func_get_args(); - return $this->SetLabelType('xd', $args); - } - - /* - * Select label formating for Y tick labels, and for Y data labels - * (unless SetYDataLabelType was called). - * See SetLabelType() for details. - */ - function SetYLabelType() // Variable arguments: $type, ... - { - $args = func_get_args(); - return $this->SetLabelType('y', $args); - } - - /* - * Select label formatting for Y data labels, overriding SetYLabelType. - */ - function SetYDataLabelType() // Variable arguments: $type, ... - { - $args = func_get_args(); - return $this->SetLabelType('yd', $args); - } - - /* - * Set the date/time format code for X labels. - * Note: Use of SetXLabelType('time', $which_xtf) is preferred, because - * SetXTimeFormat does not also enable date/time formatting. - */ - function SetXTimeFormat($which_xtf) - { - $this->label_format['x']['time_format'] = $which_xtf; - return TRUE; - } - - /* - * Set the date/time format code for Y labels. - * Note: Use of SetYLabelType('time', $which_ytf) is preferred, because - * SetYTimeFormat does not also enable date/time formatting. - */ - function SetYTimeFormat($which_ytf) - { - $this->label_format['y']['time_format'] = $which_ytf; - return TRUE; - } - - /* - * Set number format parameters (decimal point and thousands separator) for - * 'data' mode label formatting, overriding the locale-defaults. - */ - function SetNumberFormat($decimal_point, $thousands_sep) - { - $this->decimal_point = $decimal_point; - $this->thousands_sep = $thousands_sep; - return TRUE; - } - - /* - * Set the text angle for X labels to $which_xla degrees. - */ - function SetXLabelAngle($which_xla) - { - $this->x_label_angle = $which_xla; - return TRUE; - } - - /* - * Set the text angle for Y labels to $which_xla degrees. - */ - function SetYLabelAngle($which_yla) - { - $this->y_label_angle = $which_yla; - return TRUE; - } - - /* - * Set the angle for X Data Labels to $which_xdla degrees. - * If not used, this defaults to the value set with SetXLabelAngle. - */ - function SetXDataLabelAngle($which_xdla) - { - $this->x_data_label_angle = $which_xdla; - return TRUE; - } - - /* - * Set the angle for Y Data Labels to $which_ydla degrees. - * If not used, this defaults to zero (unlike X data labels). - */ - function SetYDataLabelAngle($which_ydla) - { - $this->y_data_label_angle = $which_ydla; - return TRUE; - } - -///////////////////////////////////////////// -/////////// MISC -///////////////////////////////////////////// - - /* - * Checks the validity of an option. - * $which_opt String to check, such as the provided value of a function argument. - * $which_acc String of accepted choices. Must be lower-case, and separated - * by exactly ', ' (comma, space). - * $which_func Name of the calling function, for error messages. - * Returns the supplied option value, downcased and trimmed, if it is valid. - * Reports an error if the supplied option is not valid. - */ - protected function CheckOption($which_opt, $which_acc, $which_func) - { - $asked = strtolower(trim($which_opt)); - - // Look for the supplied value in a comma/space separated list. - if (strpos(", $which_acc,", ", $asked,") !== FALSE) - return $asked; - - $this->PrintError("$which_func(): '$which_opt' not in available choices: '$which_acc'."); - return NULL; - } - - /* - * Checks the validity of an array of options. - * $opt Array or string to check. - * $acc String of accepted choices. Must be lower-case, and separated - * by exactly ', ' (comma, space). - * $func Name of the calling function, for error messages. - * Returns a array option value(s), downcased and trimmed, if all entries in $opt are valid. - * Reports an error if any supplied option is not valid. Returns NULL if the error handler returns. - */ - protected function CheckOptionArray($opt, $acc, $func) - { - $opt_array = (array)$opt; - $result = array(); - foreach ($opt_array as $option) { - $choice = $this->CheckOption($option, $acc, $func); - if (is_null($choice)) return NULL; // In case CheckOption error handler returns - $result[] = $choice; - } - return $result; - } - - /* - * Check compatibility of a plot type and data type. - * This is called by the plot-type-specific drawing functions. - * $valid_types String of supported data types. Multiple values must be - * separated by exactly ', ' (comma, space). - * Returns True if the type is valid for this plot. - * Reports an error if the data type is not value. If the error is handled and - * the handler returns, this returns False. - */ - protected function CheckDataType($valid_types) - { - if (strpos(", $valid_types,", ", $this->data_type,") !== FALSE) - return TRUE; - - $this->PrintError("Data type '$this->data_type' is not valid for '$this->plot_type' plots." - . " Supported data type(s): '$valid_types'"); - return FALSE; - } - - /* - * Decode the data type into variables used to determine how to process a data array. - * The goal is minimize which functions understand the actual data type values. - * This sets the datatype_* variables for use by other member functions. - * datatype_implied : Implicit independent variable (e.g. text-data vs data-data) - * datatype_swapped_xy : Swapped X/Y (horizontal plot) - * datatype_error_bars : Data array has error bar data - * datatype_pie_single : Data array is for a pie chart with one row per slice - */ - protected function DecodeDataType() - { - $dt = $this->data_type; - - $this->datatype_implied = ($dt == 'text-data' || $dt == 'text-data-single' - || $dt == 'text-data-yx'); - $this->datatype_swapped_xy = ($dt == 'text-data-yx' || $dt == 'data-data-yx'); - $this->datatype_error_bars = ($dt == 'data-data-error'); - $this->datatype_pie_single = ($dt == 'text-data-single'); - } - - /* - * Make sure the data array is populated, and calculate the number of columns. - * This is called from DrawGraph. Calculates data_columns, which is the - * maximum number of dependent variable values (usually Y) in the data array rows. - * (For pie charts, this is the number of slices.) - * This depends on the data_type, unlike records_per_group (which was - * previously used to pad style arrays, but is not accurate). - * Returns True if the data array is OK, else reports an error (and may return False). - * Note error messages refer to the caller, the public DrawGraph(). - */ - protected function CheckDataArray() - { - // Test for missing image, which really should never happen. - if (!$this->img) { - return $this->PrintError('DrawGraph(): No image resource allocated'); - } - - // Test for missing or empty data array: - if (empty($this->data) || !is_array($this->data)) { - return $this->PrintError("DrawGraph(): No data array"); - } - if ($this->total_records == 0) { - return $this->PrintError('DrawGraph(): Empty data set'); - } - - // Decode the data type into functional flags. - $this->DecodeDataType(); - - // Calculate the maximum number of dependent values per independent value - // (e.g. Y for each X), or the number of pie slices. - if ($this->datatype_pie_single) { - $this->data_columns = $this->num_data_rows; // Special case for 1 type of pie chart. - } else { - $skip = $this->datatype_implied ? 1 : 2; // Skip data label and independent variable if used - $this->data_columns = $this->records_per_group - $skip; - if ($this->datatype_error_bars) // Each Y has +err and -err along with it - $this->data_columns = (int)($this->data_columns / 3); - } - return TRUE; - } - - /* - * Control headers for browser-side image caching. - * $which_browser_cache : True to allow browsers to cache the image. - */ - function SetBrowserCache($which_browser_cache) - { - $this->browser_cache = $which_browser_cache; - return TRUE; - } - - /* - * Set whether DrawGraph automatically outputs the image too. - * $which_pi : True to have DrawGraph call PrintImage at the end. - */ - function SetPrintImage($which_pi) - { - $this->print_image = $which_pi; - return TRUE; - } - - /* - * Set border for the plot area. - * Accepted values are: left, right, top, bottom, sides, none, full or an array of those. - */ - function SetPlotBorderType($pbt) - { - $this->plot_border_type = $this->CheckOptionArray($pbt, 'left, right, top, bottom, sides, none, full', - __FUNCTION__); - return !empty($this->plot_border_type); - } - - /* - * Set border style for the image. - * Accepted values are: raised, plain, solid, none - * 'solid' is the same as 'plain' except it fixes the color (see DrawImageBorder) - */ - function SetImageBorderType($sibt) - { - $this->image_border_type = $this->CheckOption($sibt, 'raised, plain, solid, none', __FUNCTION__); - return (boolean)$this->image_border_type; - } - - /* - * Set border width for the image to $width in pixels. - */ - function SetImageBorderWidth($width) - { - $this->image_border_width = $width; - return TRUE; - } - - /* - * Enable or disable drawing of the plot area background color. - */ - function SetDrawPlotAreaBackground($dpab) - { - $this->draw_plot_area_background = (bool)$dpab; - return TRUE; - } - - /* - * Enable or disable drawing of the X grid lines. - */ - function SetDrawXGrid($dxg) - { - $this->draw_x_grid = (bool)$dxg; - return TRUE; - } - - /* - * Enable or disable drawing of the Y grid lines. - */ - function SetDrawYGrid($dyg) - { - $this->draw_y_grid = (bool)$dyg; - return TRUE; - } - - /* - * Select dashed or solid grid lines. - * $ddg : True for dashed grid lines, false for solid grid lines. - */ - function SetDrawDashedGrid($ddg) - { - $this->dashed_grid = (bool)$ddg; - return TRUE; - } - - /* - * Enable or disable drawing of X Data Label Lines. - */ - function SetDrawXDataLabelLines($dxdl) - { - $this->draw_x_data_label_lines = (bool)$dxdl; - return TRUE; - } - - /* - * Set the main title text for the plot. - */ - function SetTitle($which_title) - { - $this->title_txt = $which_title; - return TRUE; - } - - /* - * Set the X axis title and position. - */ - function SetXTitle($which_xtitle, $which_xpos = 'plotdown') - { - if ($which_xtitle == '') - $which_xpos = 'none'; - - $this->x_title_pos = $this->CheckOption($which_xpos, 'plotdown, plotup, both, none', __FUNCTION__); - if (!$this->x_title_pos) return FALSE; - $this->x_title_txt = $which_xtitle; - return TRUE; - } - - /* - * Set the Y axis title and position. - */ - function SetYTitle($which_ytitle, $which_ypos = 'plotleft') - { - if ($which_ytitle == '') - $which_ypos = 'none'; - - $this->y_title_pos = $this->CheckOption($which_ypos, 'plotleft, plotright, both, none', __FUNCTION__); - if (!$this->y_title_pos) return FALSE; - $this->y_title_txt = $which_ytitle; - return TRUE; - } - - /* - * Set the size of the drop shadow for bar and pie charts. - * $which_s : Size of the drop shadow in pixels. - */ - function SetShading($which_s) - { - $this->shading = (int)$which_s; - return TRUE; - } - - /* - * Set the plot type (bars, points, ...) - */ - function SetPlotType($which_pt) - { - $avail_plot_types = implode(', ', array_keys(PHPlot::$plots)); // List of known plot types - $this->plot_type = $this->CheckOption($which_pt, $avail_plot_types, __FUNCTION__); - return (boolean)$this->plot_type; - } - - /* - * Set the position of the X axis. - * $pos : Axis position in world coordinates (as an integer). - */ - function SetXAxisPosition($pos='') - { - $this->x_axis_position = ($pos === '') ? $pos : (int)$pos; - return TRUE; - } - - /* - * Set the position of the Y axis. - * $pos : Axis position in world coordinates (as an integer). - */ - function SetYAxisPosition($pos='') - { - $this->y_axis_position = ($pos === '') ? $pos : (int)$pos; - return TRUE; - } - - /* - * Enable or disable drawing of the X axis line. - * $draw : True to draw the axis (default if not called), False to suppress it. - * This controls drawing of the axis line only, and not the ticks, labels, or grid. - */ - function SetDrawXAxis($draw) - { - $this->suppress_x_axis = !$draw; // See DrawXAxis() - return TRUE; - } - - /* - * Enable or disable drawing of the Y axis line. - * $draw : True to draw the axis (default if not called), False to suppress it. - * This controls drawing of the axis line only, and not the ticks, labels, or grid. - */ - function SetDrawYAxis($draw) - { - $this->suppress_y_axis = !$draw; // See DrawYAxis() - return TRUE; - } - - /* - * Select linear or log scale for the X axis. - */ - function SetXScaleType($which_xst) - { - $this->xscale_type = $this->CheckOption($which_xst, 'linear, log', __FUNCTION__); - return (boolean)$this->xscale_type; - } - - /* - * Select linear or log scale for the Y axis. - */ - function SetYScaleType($which_yst) - { - $this->yscale_type = $this->CheckOption($which_yst, 'linear, log', __FUNCTION__); - return (boolean)$this->yscale_type; - } - - /* - * Set the precision for numerically formatted X labels. - * $which_prec : Number of digits to display. - * Note: This is equivalent to: SetXLabelType('data', $which_prec) - */ - function SetPrecisionX($which_prec) - { - return $this->SetXLabelType('data', $which_prec); - } - - /* - * Set the precision for numerically formatted Y labels. - * $which_prec : Number of digits to display. - * Note: This is equivalent to: SetYLabelType('data', $which_prec) - */ - function SetPrecisionY($which_prec) - { - return $this->SetYLabelType('data', $which_prec); - } - - /* - * Set the line width (in pixels) for error bars. - */ - function SetErrorBarLineWidth($which_seblw) - { - $this->error_bar_line_width = $which_seblw; - return TRUE; - } - - /* - * Set the position for pie chart percentage labels. - * $which_blb : Real number between 0 and 1. - * Smaller values move the labels in towards the center. - */ - function SetLabelScalePosition($which_blp) - { - $this->label_scale_position = $which_blp; - return TRUE; - } - - /* - * Set the size (in pixels) of the "T" in error bars. - */ - function SetErrorBarSize($which_ebs) - { - $this->error_bar_size = $which_ebs; - return TRUE; - } - - /* - * Set the shape of the in error bars. - * $which_ebs : Error bar shape, 'tee' or 'line'. - */ - function SetErrorBarShape($which_ebs) - { - $this->error_bar_shape = $this->CheckOption($which_ebs, 'tee, line', __FUNCTION__); - return (boolean)$this->error_bar_shape; - } - - /* - * Synchronize the point shape and point size arrays. - * This is called just before drawing any plot that needs 'points'. - */ - protected function CheckPointParams() - { - // Make both point_shapes and point_sizes the same size, by padding the smaller. - $ps = count($this->point_sizes); - $pt = count($this->point_shapes); - - if ($ps < $pt) { - $this->pad_array($this->point_sizes, $pt); - $this->point_counts = $pt; - } elseif ($ps > $pt) { - $this->pad_array($this->point_shapes, $ps); - $this->point_counts = $ps; - } else { - $this->point_counts = $ps; - } - - // Note: PHPlot used to check and adjust point_sizes to be an even number here, - // for all 'diamond' and 'triangle' shapes. The reason for this having been - // lost, and the current maintainer seeing no sense it doing this for only - // some shapes, the code has been removed. But see what DrawDot() does. - } - - /* - * Set the point shape for each data set. - * $which_pt : Array (or single value) of valid point shapes. See also DrawDot() for valid shapes. - * The point shape and point sizes arrays are synchronized before drawing a graph - * that uses points. See CheckPointParams() - */ - function SetPointShapes($which_pt) - { - $this->point_shapes = $this->CheckOptionArray($which_pt, 'halfline, line, plus, cross, rect,' - . ' circle, dot, diamond, triangle, trianglemid, delta, yield, star, hourglass,' - . ' bowtie, target, box, home, up, down, none', __FUNCTION__); - return !empty($this->point_shapes); - } - - /* - * Set the point size for point plots. - * $which_ps : Array (or single value) of point sizes in pixels. - * The point shape and point sizes arrays are synchronized before drawing a graph - * that uses points. See CheckPointParams() - */ - function SetPointSizes($which_ps) - { - if (is_array($which_ps)) { - // Use provided array: - $this->point_sizes = $which_ps; - } elseif (!is_null($which_ps)) { - // Make the single value into an array: - $this->point_sizes = array($which_ps); - } - return TRUE; - } - - /* - * Sets whether lines should be broken at missing data. - * $bl : True to break the lines, false to connect around missing data. - * This only works with 'lines' and 'squared' plots. - */ - function SetDrawBrokenLines($bl) - { - $this->draw_broken_lines = (bool)$bl; - return TRUE; - } - - /* - * Set the data type, which defines the structure of the data array - * text-data: ('label', y1, y2, y3, ...) - * text-data-single: ('label', data), for some pie charts. - * data-data: ('label', x, y1, y2, y3, ...) - * data-data-error: ('label', x1, y1, e1+, e2-, y2, e2+, e2-, y3, e3+, e3-, ...) - * data-data-yx: ('label', y, x1, x2, x3, ..) - * text-data-yx: ('label', x1, x2, x3, ...) - */ - function SetDataType($which_dt) - { - //The next four lines are for past compatibility. - if ($which_dt == 'text-linear') $which_dt = 'text-data'; - elseif ($which_dt == 'linear-linear') $which_dt = 'data-data'; - elseif ($which_dt == 'linear-linear-error') $which_dt = 'data-data-error'; - elseif ($which_dt == 'text-data-pie') $which_dt = 'text-data-single'; - - $this->data_type = $this->CheckOption($which_dt, 'text-data, text-data-single, '. - 'data-data, data-data-error, '. - 'data-data-yx, text-data-yx', - __FUNCTION__); - return (boolean)$this->data_type; - } - - /* - * Copy the array passed as data values. We convert to numerical indexes, for its - * use for (or while) loops, which sometimes are faster. Performance improvements - * vary from 28% in DrawLines() to 49% in DrawArea() for plot drawing functions. - */ - function SetDataValues($which_dv) - { - $this->num_data_rows = count($which_dv); - $this->total_records = 0; - $this->data = array(); - $this->num_recs = array(); - for ($i = 0; $i < $this->num_data_rows; $i++) { - $this->data[$i] = array_values($which_dv[$i]); // convert to numerical indices. - - // Count size of each row, and total for the array. - $recs = count($this->data[$i]); - $this->total_records += $recs; - $this->num_recs[$i] = $recs; - } - // This is the size of the widest row in the data array - // Note records_per_group isn't used much anymore. See data_columns in CheckDataArray() - $this->records_per_group = max($this->num_recs); - return TRUE; - } - - /* - * Pad styles arrays for later use by plot drawing functions: - * This removes the need for $max_data_colors, etc. and $color_index = $color_index % $max_data_colors - * in DrawBars(), DrawLines(), etc. - * The arrays are padded to data_columns which is the maximum number of data sets. - * See CheckDataArray() for the calculation. - */ - protected function PadArrays() - { - $this->pad_array($this->line_widths, $this->data_columns); - $this->pad_array($this->line_styles, $this->data_columns); - $this->pad_array($this->ndx_data_colors, $this->data_columns); - $this->pad_array($this->ndx_data_border_colors, $this->data_columns); - // Other data color arrays are handled in the Need*Colors() functions. - - return TRUE; - } - - /* - * Pads an array with itself. This only works on 0-based sequential integer indexed arrays. - * $arr : The array (or scalar) to pad. This argument is modified. - * $size : Minimum size of the resulting array. - * If $arr is a scalar, it will be converted first to a single element array. - * If $arr has at least $size elements, it is unchanged. - * Otherwise, append elements of $arr to itself until it reaches $size elements. - */ - protected function pad_array(&$arr, $size) - { - if (! is_array($arr)) { - $arr = array($arr); - } - $n = count($arr); - $base = 0; - while ($n < $size) $arr[$n++] = $arr[$base++]; - } - - /* - * Format a floating-point number. - * $number : A floating point number to format - * $decimals : Number of decimal places in the result - * Returns the formatted result. - * This is like PHP's number_format, but uses class variables for separators. - * The separators will default to locale-specific values, if available. - */ - protected function number_format($number, $decimals=0) - { - if (!isset($this->decimal_point) || !isset($this->thousands_sep)) { - // Load locale-specific values from environment, unless disabled: - if (empty($this->locale_override)) - @setlocale(LC_ALL, ''); - // Fetch locale settings: - $locale = @localeconv(); - if (isset($locale['decimal_point']) && isset($locale['thousands_sep'])) { - $this->decimal_point = $locale['decimal_point']; - $this->thousands_sep = $locale['thousands_sep']; - } else { - // Locale information not available. - $this->decimal_point = '.'; - $this->thousands_sep = ','; - } - } - return number_format($number, $decimals, $this->decimal_point, $this->thousands_sep); - } - - /* - * Register a callback (hook) function - * $reason : A pre-defined name where a callback can be defined. - * $function : The name of a function to register for callback, or an instance/method - * pair in an array (see 'callbacks' in the PHP reference manual). - * $arg : Optional argument to supply to the callback function when it is triggered. - * (Often called "clientData") - * Returns True if the callback reason is valid, else False. - */ - function SetCallback($reason, $function, $arg = NULL) - { - // Use array_key_exists because valid reason keys have NULL as value. - if (!array_key_exists($reason, $this->callbacks)) - return FALSE; - $this->callbacks[$reason] = array($function, $arg); - return TRUE; - } - - /* - * Return the name of a function registered for callback. See SetCallBack. - * $reason - A pre-defined name where a callback can be defined. - * Returns the current callback function (name or array) for the given reason, - * or False if there was no active callback or the reason is not valid. - * Note you can safely test the return value with a simple 'if', as - * no valid function name evaluates to false. - */ - function GetCallback($reason) - { - if (isset($this->callbacks[$reason])) - return $this->callbacks[$reason][0]; - return FALSE; - } - - /* - * Un-register (remove) a function registered for callback. - * $reason - A pre-defined name where a callback can be defined. - * Returns: True if it was a valid callback reason, else False. - * Note: Returns True whether or not there was a callback registered. - */ - function RemoveCallback($reason) - { - if (!array_key_exists($reason, $this->callbacks)) - return FALSE; - $this->callbacks[$reason] = NULL; - return TRUE; - } - - /* - * Invoke a callback, if one is registered. - * Accepts a variable number of arguments >= 1: - * $reason : A string naming the callback. - * ... : Zero or more additional arguments to be passed to the - * callback function, after the passthru argument: - * callback_function($image, $passthru, ...) - * Returns: whatever value (if any) was returned by the callback. - */ - protected function DoCallback() // Note: Variable arguments - { - $args = func_get_args(); - $reason = $args[0]; - if (!isset($this->callbacks[$reason])) - return; - list($function, $args[0]) = $this->callbacks[$reason]; - array_unshift($args, $this->img); - // Now args[] looks like: img, passthru, extra args... - return call_user_func_array($function, $args); - } - - /* - * Allocate colors for the plot. - * This is called by DrawGraph to allocate the colors needed for the plot. Each selectable - * color has already been validated, parsed into an array (r,g,b,a), and stored into a member - * variable. Now the GD color indexes are assigned and stored into the ndx_*_color variables. - * This is deferred here to avoid allocating unneeded colors and to avoid order dependencies, - * especially with the transparent color. - * - * For drawing data elements, only the main data colors and border colors are allocated here. - * Dark colors and error bar colors are allocated by Need*Color() functions. - * (Data border colors default to just black, so there is no cost to always allocating.) - * - * Data color allocation works as follows. If there is a data_color callback, then allocate all - * defined data colors (because the callback can use them however it wants). Otherwise, only allocate - * the number of colors that will be used. This is the larger of the number of data sets and the - * number of legend lines. - */ - protected function SetColorIndexes() - { - $this->ndx_bg_color = $this->GetColorIndex($this->bg_color); // Background first - $this->ndx_plot_bg_color = $this->GetColorIndex($this->plot_bg_color); - if ($this->image_border_type != 'none') { - $this->ndx_i_border = $this->GetColorIndex($this->i_border); - $this->ndx_i_border_dark = $this->GetDarkColorIndex($this->i_border); - } - - // Handle defaults for X and Y title colors. - $this->ndx_title_color = $this->GetColorIndex($this->title_color); - if (empty($this->x_title_color)) { - $this->ndx_x_title_color = $this->ndx_title_color; - } else { - $this->ndx_x_title_color = $this->GetColorIndex($this->x_title_color); - } - if (empty($this->y_title_color)) { - $this->ndx_y_title_color = $this->ndx_title_color; - } else { - $this->ndx_y_title_color = $this->GetColorIndex($this->y_title_color); - } - - $this->ndx_text_color = $this->GetColorIndex($this->text_color); - $this->ndx_grid_color = $this->GetColorIndex($this->grid_color); - $this->ndx_light_grid_color = $this->GetColorIndex($this->light_grid_color); - $this->ndx_tick_color = $this->GetColorIndex($this->tick_color); - - // Maximum number of data & border colors to allocate: - if ($this->GetCallback('data_color')) { - $n_data = count($this->data_colors); // Need all of them - $n_border = count($this->data_border_colors); - } else { - $n_data = max($this->data_columns, empty($this->legend) ? 0 : count($this->legend)); - $n_border = $n_data; // One border color per data color - } - - // Allocate main data colors. For other colors used for data, see the functions which follow. - $this->ndx_data_colors = $this->GetColorIndexArray($this->data_colors, $n_data); - $this->ndx_data_border_colors = $this->GetColorIndexArray($this->data_border_colors, $n_border); - - // Set up a color as transparent, if SetTransparentColor was used. - if (!empty($this->transparent_color)) { - imagecolortransparent($this->img, $this->GetColorIndex($this->transparent_color)); - } - } - - /* - * Allocate dark-shade data colors. Called if needed by graph drawing functions. - */ - protected function NeedDataDarkColors() - { - // This duplicates the calculation in SetColorIndexes() for number of data colors to allocate. - if ($this->GetCallback('data_color')) { - $n_data = count($this->data_colors); - } else { - $n_data = max($this->data_columns, empty($this->legend) ? 0 : count($this->legend)); - } - $this->ndx_data_dark_colors = $this->GetDarkColorIndexArray($this->data_colors, $n_data); - $this->pad_array($this->ndx_data_dark_colors, $this->data_columns); - } - - /* - * Allocate error bar colors. Called if needed by graph drawing functions. - */ - protected function NeedErrorBarColors() - { - // This is similar to the calculation in SetColorIndexes() for number of data colors to allocate. - if ($this->GetCallback('data_color')) { - $n_err = count($this->error_bar_colors); - } else { - $n_err = max($this->data_columns, empty($this->legend) ? 0 : count($this->legend)); - } - $this->ndx_error_bar_colors = $this->GetColorIndexArray($this->error_bar_colors, $n_err); - $this->pad_array($this->ndx_error_bar_colors, $this->data_columns); - } - - /* - * Determine if, and where, to draw Data Value Labels. - * $label_control : Label position control. Either x_data_label_pos or y_data_label_pos. - * &$x_adj, &$y_adj : Returns X,Y adjustments (offset in pixels) to the text position. - * &$h_align, &$v_align : Returns horizontal and vertical alignment for the label. - * The above 4 argument values should be passed to DrawDataValueLabel() - * Returns True if data value labels should be drawn (based on $label_control), else False. - * This is used for plot types other than bars/stackedbars (which have their own way of doing it). - * It uses two member variables (unset by default): data_value_label_angle and data_value_label_distance - * to define the vector to the label. Default is 90 degrees at 5 pixels. - */ - protected function CheckDataValueLabels($label_control, &$x_adj, &$y_adj, &$h_align, &$v_align) - { - if ($label_control != 'plotin') - return FALSE; // No data value labels - $angle = deg2rad(isset($this->data_value_label_angle) ? $this->data_value_label_angle : 90); - $radius = isset($this->data_value_label_distance) ? $this->data_value_label_distance : 5; - $cos = cos($angle); - $sin = sin($angle); - $x_adj = (int)($radius * $cos); - $y_adj = -(int)($radius * $sin); // Y is reversed in device coordinates - - // Choose from 8 (not 9, center/center can't happen) text alignments based on angle: - if ($sin >= 0.383) $v_align = 'bottom'; // 0.383 = sin(360deg / 16) - elseif ($sin >= -0.383) $v_align = 'center'; - else $v_align = 'top'; - if ($cos >= 0.383) $h_align = 'left'; - elseif ($cos >= -0.383) $h_align = 'center'; - else $h_align = 'right'; - return TRUE; - } - -////////////////////////////////////////////////////////// -/////////// DATA ANALYSIS, SCALING AND TRANSLATION -////////////////////////////////////////////////////////// - - /* - * Analyzes the data array and calculates the minimum and maximum values. - * In this function, IV refers to the independent variable, and DV the dependent variable. - * For most plots, IV is X and DV is Y. For swapped X/Y plots, IV is Y and DV is X. - * At the end of the function, IV and DV ranges get assigned into X or Y. - * - * The data type mostly determines the data array structure, but some plot types do special - * things such as sum the values in a row. This information is in the plots[] array. - * - * This calculates min_x, max_x, min_y, and max_y. It also calculates two arrays - * data_min[] and data_max[] with per-row min and max values. These are used for - * data label lines. For normal (unswapped) data, these are the Y range for each X. - * For swapped X/Y data, they are the X range for each Y. - */ - protected function FindDataLimits() - { - // Does this plot type need special processing of the data values? - $sum_vals = !empty(PHPlot::$plots[$this->plot_type]['sum_vals']); // Add up values in each row - $abs_vals = !empty(PHPlot::$plots[$this->plot_type]['abs_vals']); // Take absolute values - - // These need to be initialized in case there are multiple plots and missing data points. - $this->data_min = array(); - $this->data_max = array(); - - // Independent values are in the data array or assumed? - if ($this->datatype_implied) { - $all_iv = array(0, $this->num_data_rows - 1); - } else { - $all_iv = array(); - } - - // Process all rows of data: - for ($i = 0; $i < $this->num_data_rows; $i++) { - $n_vals = $this->num_recs[$i]; - $j = 1; // Skips label at [0] - - if (!$this->datatype_implied) { - $all_iv[] = (double)$this->data[$i][$j++]; - } - - if ($sum_vals) { - $all_dv = array(0, 0); // One limit is 0, other calculated below - } else { - $all_dv = array(); - } - while ($j < $n_vals) { - if (is_numeric($this->data[$i][$j])) { - $val = (double)$this->data[$i][$j++]; - - if ($this->datatype_error_bars) { - $all_dv[] = $val + (double)$this->data[$i][$j++]; - $all_dv[] = $val - (double)$this->data[$i][$j++]; - } else { - if ($abs_vals) { - $val = abs($val); // Use absolute values - } - if ($sum_vals) { - $all_dv[1] += $val; // Sum of values - } else { - $all_dv[] = $val; // List of all values - } - } - } else { // Missing DV value - $j++; - if ($this->datatype_error_bars) $j += 2; - } - } - if (!empty($all_dv)) { - $this->data_min[$i] = min($all_dv); // Store per-row DV range - $this->data_max[$i] = max($all_dv); - } - } - - if ($this->datatype_swapped_xy) { - // Assign min and max for swapped X/Y plots: IV=Y and DV=X - $this->min_y = min($all_iv); - $this->max_y = max($all_iv); - if (empty($this->data_min)) { // Guard against regressive case: No X at all - $this->min_x = 0; - $this->max_x = 0; - } else { - $this->min_x = min($this->data_min); // Store global X range - $this->max_x = max($this->data_max); - } - } else { - // Assign min and max for normal plots: IV=X and DV=Y - $this->min_x = min($all_iv); - $this->max_x = max($all_iv); - if (empty($this->data_min)) { // Guard against regressive case: No Y at all - $this->min_y = 0; - $this->max_y = 0; - } else { - $this->min_y = min($this->data_min); // Store global Y range - $this->max_y = max($this->data_max); - } - } - - if ($this->GetCallback('debug_scale')) { - $this->DoCallback('debug_scale', __FUNCTION__, array( - 'min_x' => $this->min_x, 'min_y' => $this->min_y, - 'max_x' => $this->max_x, 'max_y' => $this->max_y)); - } - return TRUE; - } - - /* - * Calculates image margins on the fly from title positions and sizes, - * and tick labels positions and sizes. - * - * A picture of the locations of elements and spacing can be found in the - * PHPlot Reference Manual. - * - * Calculates the following (class variables unless noted): - * - * Plot area margins (see note below): - * y_top_margin - * y_bot_margin - * x_left_margin - * x_right_margin - * - * Title sizes (these are now local, not class variables, since they are not used elsewhere): - * title_height : Height of main title - * x_title_height : Height of X axis title, 0 if no X title - * y_title_width : Width of Y axis title, 0 if no Y title - * - * Tick/Data label offsets, relative to plot_area: - * x_label_top_offset, x_label_bot_offset, x_label_axis_offset - * y_label_left_offset, y_label_right_offset, y_label_axis_offset - * - * Title offsets, relative to plot area: - * x_title_top_offset, x_title_bot_offset - * y_title_left_offset, y_title_left_offset - * title_offset (for main title, relative to image edge) - * - * Note: The margins are calculated, but not stored, if margins or plot area were - * set by the user with SetPlotAreaPixels or SetMarginsPixels. The margin - * calculation is mixed in with the offset variables, so it doesn't seem worth the - * trouble to separate them. - * - * If the $maximize argument is true, we use the full image size, minus safe_margin - * and main title, for the plot. This is for pie charts which have no axes or X/Y titles. - */ - protected function CalcMargins($maximize) - { - // This is the line-to-line or line-to-text spacing: - $gap = $this->safe_margin; - // Initial margin on each side takes into account a possible image border. - // For compatibility, if border is 1 or 2, don't increase the margins. - $base_margin = max($gap, $this->GetImageBorderWidth() + 3); - $this->title_offset = $base_margin; // For use in DrawTitle - - // Minimum margin on each side. This reduces the chance that the - // right-most tick label (for example) will run off the image edge - // if there are no titles on that side. - $min_margin = 2 * $gap + $base_margin; - - // Calculate the title sizes: - list($unused, $title_height) = $this->SizeText($this->fonts['title'], 0, $this->title_txt); - list($unused, $x_title_height) = $this->SizeText($this->fonts['x_title'], 0, $this->x_title_txt); - list($y_title_width, $unused) = $this->SizeText($this->fonts['y_title'], 90, $this->y_title_txt); - - // Special case for maximum area usage with no X/Y titles or labels, only main title: - if ($maximize) { - if (!isset($this->x_left_margin)) - $this->x_left_margin = $base_margin; - if (!isset($this->x_right_margin)) - $this->x_right_margin = $base_margin; - if (!isset($this->y_top_margin)) { - $this->y_top_margin = $base_margin; - if ($title_height > 0) - $this->y_top_margin += $title_height + $gap; - } - if (!isset($this->y_bot_margin)) - $this->y_bot_margin = $base_margin; - - return TRUE; - } - - // Make local variables for these. (They get used a lot and I'm tired of this, this, this.) - $x_tick_label_pos = $this->x_tick_label_pos; - $x_data_label_pos = $this->x_data_label_pos; - $x_tick_pos = $this->x_tick_pos; - $x_tick_len = $this->x_tick_length; - $y_tick_label_pos = $this->y_tick_label_pos; - $y_tick_pos = $this->y_tick_pos; - $y_tick_len = $this->y_tick_length; - $y_data_label_pos = $this->y_data_label_pos; - - // For X/Y tick and label position of 'xaxis' or 'yaxis', determine if the axis happens to be - // on an edge of a plot. If it is, we need to account for the margins there. - if ($this->x_axis_position <= $this->plot_min_y) - $x_axis_pos = 'bottom'; - elseif ($this->x_axis_position >= $this->plot_max_y) - $x_axis_pos = 'top'; - else - $x_axis_pos = 'none'; - if ($this->y_axis_position <= $this->plot_min_x) - $y_axis_pos = 'left'; - elseif ($this->y_axis_position >= $this->plot_max_x) - $y_axis_pos = 'right'; - else - $y_axis_pos = 'none'; - - // Calculate the heights for X tick and data labels, and the max (used if they are overlaid): - $x_data_label_height = ($x_data_label_pos == 'none') ? 0 : $this->CalcMaxDataLabelSize('x'); - $x_tick_label_height = ($x_tick_label_pos == 'none') ? 0 : $this->CalcMaxTickLabelSize('x'); - $x_max_label_height = max($x_data_label_height, $x_tick_label_height); - - // Calculate the space needed above and below the plot for X tick and X data labels: - - // Above the plot: - $tick_labels_above = ($x_tick_label_pos == 'plotup' || $x_tick_label_pos == 'both' - || ($x_tick_label_pos == 'xaxis' && $x_axis_pos == 'top')); - $data_labels_above = ($x_data_label_pos == 'plotup' || $x_data_label_pos == 'both'); - if ($tick_labels_above) { - if ($data_labels_above) { - $label_height_above = $x_max_label_height; - } else { - $label_height_above = $x_tick_label_height; - } - } elseif ($data_labels_above) { - $label_height_above = $x_data_label_height; - } else { - $label_height_above = 0; - } - - // Below the plot: - $tick_labels_below = ($x_tick_label_pos == 'plotdown' || $x_tick_label_pos == 'both' - || ($x_tick_label_pos == 'xaxis' && $x_axis_pos == 'bottom')); - $data_labels_below = ($x_data_label_pos == 'plotdown' || $x_data_label_pos == 'both'); - if ($tick_labels_below) { - if ($data_labels_below) { - $label_height_below = $x_max_label_height; - } else { - $label_height_below = $x_tick_label_height; - } - } elseif ($data_labels_below) { - $label_height_below = $x_data_label_height; - } else { - $label_height_below = 0; - } - - // Calculate the width for Y tick and data labels, if on, and the max: - // Note CalcMaxDataLabelSize('y') returns 0 except for swapped X/Y plots. - $y_data_label_width = ($y_data_label_pos == 'none') ? 0 : $this->CalcMaxDataLabelSize('y'); - $y_tick_label_width = ($y_tick_label_pos == 'none') ? 0 : $this->CalcMaxTickLabelSize('y'); - $y_max_label_width = max($y_data_label_width, $y_tick_label_width); - - // Calculate the space needed left and right of the plot for Y tick and Y data labels: - // (Y data labels here are for swapped X/Y plots such has horizontal bars) - - // Left of the plot: - $tick_labels_left = ($y_tick_label_pos == 'plotleft' || $y_tick_label_pos == 'both' - || ($y_tick_label_pos == 'yaxis' && $y_axis_pos == 'left')); - $data_labels_left = ($y_data_label_pos == 'plotleft' || $y_data_label_pos == 'both'); - if ($tick_labels_left) { - if ($data_labels_left) { - $label_width_left = $y_max_label_width; - } else { - $label_width_left = $y_tick_label_width; - } - } elseif ($data_labels_left) { - $label_width_left = $y_data_label_width; - } else { - $label_width_left = 0; - } - - // Right of the plot: - $tick_labels_right = ($y_tick_label_pos == 'plotright' || $y_tick_label_pos == 'both' - || ($y_tick_label_pos == 'yaxis' && $y_axis_pos == 'right')); - $data_labels_right = ($y_data_label_pos == 'plotright' || $y_data_label_pos == 'both'); - if ($tick_labels_right) { - if ($data_labels_right) { - $label_width_right = $y_max_label_width; - } else { - $label_width_right = $y_tick_label_width; - } - } elseif ($data_labels_right) { - $label_width_right = $y_data_label_width; - } else { - $label_width_right = 0; - } - - ///////// Calculate margins: - - // Calculating Top and Bottom margins: - // y_top_margin: Main title, Upper X title, X ticks and tick labels, and X data labels: - // y_bot_margin: Lower title, ticks and tick labels, and data labels: - $top_margin = $base_margin; - $bot_margin = $base_margin; - $this->x_title_top_offset = $gap; - $this->x_title_bot_offset = $gap; - - // Space for main title? - if ($title_height > 0) - $top_margin += $title_height + $gap; - - // Space for X Title? - if ($x_title_height > 0) { - $pos = $this->x_title_pos; - if ($pos == 'plotup' || $pos == 'both') - $top_margin += $x_title_height + $gap; - if ($pos == 'plotdown' || $pos == 'both') - $bot_margin += $x_title_height + $gap; - } - - // Space for X Labels above the plot? - if ($label_height_above > 0) { - $top_margin += $label_height_above + $gap; - $this->x_title_top_offset += $label_height_above + $gap; - } - - // Space for X Labels below the plot? - if ($label_height_below > 0) { - $bot_margin += $label_height_below + $gap; - $this->x_title_bot_offset += $label_height_below + $gap; - } - - // Space for X Ticks above the plot? - if ($x_tick_pos == 'plotup' || $x_tick_pos == 'both' - || ($x_tick_pos == 'xaxis' && $x_axis_pos == 'top')) { - $top_margin += $x_tick_len; - $this->x_label_top_offset = $x_tick_len + $gap; - $this->x_title_top_offset += $x_tick_len; - } else { - // No X Ticks above the plot: - $this->x_label_top_offset = $gap; - } - - // Space for X Ticks below the plot? - if ($x_tick_pos == 'plotdown' || $x_tick_pos == 'both' - || ($x_tick_pos == 'xaxis' && $x_axis_pos == 'bottom')) { - $bot_margin += $x_tick_len; - $this->x_label_bot_offset = $x_tick_len + $gap; - $this->x_title_bot_offset += $x_tick_len; - } else { - // No X Ticks below the plot: - $this->x_label_bot_offset = $gap; - } - // Label offsets for on-axis ticks: - if ($x_tick_pos == 'xaxis') { - $this->x_label_axis_offset = $x_tick_len + $gap; - } else { - $this->x_label_axis_offset = $gap; - } - - // Calculating Left and Right margins: - // x_left_margin: Left Y title, Y ticks and tick labels: - // x_right_margin: Right Y title, Y ticks and tick labels: - $left_margin = $base_margin; - $right_margin = $base_margin; - $this->y_title_left_offset = $gap; - $this->y_title_right_offset = $gap; - - // Space for Y Title? - if ($y_title_width > 0) { - $pos = $this->y_title_pos; - if ($pos == 'plotleft' || $pos == 'both') - $left_margin += $y_title_width + $gap; - if ($pos == 'plotright' || $pos == 'both') - $right_margin += $y_title_width + $gap; - } - - // Space for Y Labels left of the plot? - if ($label_width_left > 0) { - $left_margin += $label_width_left + $gap; - $this->y_title_left_offset += $label_width_left + $gap; - } - - // Space for Y Labels right of the plot? - if ($label_width_right > 0) { - $right_margin += $label_width_right + $gap; - $this->y_title_right_offset += $label_width_right + $gap; - } - - // Space for Y Ticks left of plot? - if ($y_tick_pos == 'plotleft' || $y_tick_pos == 'both' - || ($y_tick_pos == 'yaxis' && $y_axis_pos == 'left')) { - $left_margin += $y_tick_len; - $this->y_label_left_offset = $y_tick_len + $gap; - $this->y_title_left_offset += $y_tick_len; - } else { - // No Y Ticks left of plot: - $this->y_label_left_offset = $gap; - } - - // Space for Y Ticks right of plot? - if ($y_tick_pos == 'plotright' || $y_tick_pos == 'both' - || ($y_tick_pos == 'yaxis' && $y_axis_pos == 'right')) { - $right_margin += $y_tick_len; - $this->y_label_right_offset = $y_tick_len + $gap; - $this->y_title_right_offset += $y_tick_len; - } else { - // No Y Ticks right of plot: - $this->y_label_right_offset = $gap; - } - - // Label offsets for on-axis ticks: - if ($x_tick_pos == 'yaxis') { - $this->y_label_axis_offset = $y_tick_len + $gap; - } else { - $this->y_label_axis_offset = $gap; - } - - // Apply the minimum margins and store in the object. - // Do not set margins which were user-defined (see note at top of function). - if (!isset($this->y_top_margin)) - $this->y_top_margin = max($min_margin, $top_margin); - if (!isset($this->y_bot_margin)) - $this->y_bot_margin = max($min_margin, $bot_margin); - if (!isset($this->x_left_margin)) - $this->x_left_margin = max($min_margin, $left_margin); - if (!isset($this->x_right_margin)) - $this->x_right_margin = max($min_margin, $right_margin); - - if ($this->GetCallback('debug_scale')) { - // (Too bad compact() doesn't work on class member variables...) - $this->DoCallback('debug_scale', __FUNCTION__, array( - 'label_height_above' => $label_height_above, - 'label_height_below' => $label_height_below, - 'label_width_left' => $label_width_left, - 'label_width_right' => $label_width_right, - 'x_tick_len' => $x_tick_len, - 'y_tick_len' => $y_tick_len, - 'x_left_margin' => $this->x_left_margin, - 'x_right_margin' => $this->x_right_margin, - 'y_top_margin' => $this->y_top_margin, - 'y_bot_margin' => $this->y_bot_margin, - 'x_label_top_offset' => $this->x_label_top_offset, - 'x_label_bot_offset' => $this->x_label_bot_offset, - 'y_label_left_offset' => $this->y_label_left_offset, - 'y_label_right_offset' => $this->y_label_right_offset, - 'x_title_top_offset' => $this->x_title_top_offset, - 'x_title_bot_offset' => $this->x_title_bot_offset, - 'y_title_left_offset' => $this->y_title_left_offset, - 'y_title_right_offset' => $this->y_title_right_offset)); - } - - return TRUE; - } - - /* - * Calculate the plot area (device coordinates) from the margins. - * (This used to be part of SetPlotAreaPixels.) - * The margins might come from SetMarginsPixels, SetPlotAreaPixels, - * or CalcMargins. - */ - protected function CalcPlotAreaPixels() - { - $this->plot_area = array($this->x_left_margin, $this->y_top_margin, - $this->image_width - $this->x_right_margin, - $this->image_height - $this->y_bot_margin); - $this->plot_area_width = $this->plot_area[2] - $this->plot_area[0]; - $this->plot_area_height = $this->plot_area[3] - $this->plot_area[1]; - - $this->DoCallback('debug_scale', __FUNCTION__, $this->plot_area); - return TRUE; - } - - /* - * Set the margins in pixels (left, right, top, bottom) - * This determines the plot area, equivalent to SetPlotAreaPixels(). - * Deferred calculations now occur in CalcPlotAreaPixels(). - */ - function SetMarginsPixels($which_lm = NULL, $which_rm = NULL, $which_tm = NULL, $which_bm = NULL) - { - $this->x_left_margin = $which_lm; - $this->x_right_margin = $which_rm; - $this->y_top_margin = $which_tm; - $this->y_bot_margin = $which_bm; - - return TRUE; - } - - /* - * Sets the limits for the plot area. - * This stores the margins, not the area. That may seem odd, but - * the idea is to make SetPlotAreaPixels and SetMarginsPixels two - * ways to accomplish the same thing, and the deferred calculations - * in CalcMargins and CalcPlotAreaPixels don't need to know which - * was used. - * (x1, y1) - Upper left corner of the plot area - * (x2, y2) - Lower right corner of the plot area - */ - function SetPlotAreaPixels($x1 = NULL, $y1 = NULL, $x2 = NULL, $y2 = NULL) - { - $this->x_left_margin = $x1; - if (isset($x2)) $this->x_right_margin = $this->image_width - $x2; - else unset($this->x_right_margin); - $this->y_top_margin = $y1; - if (isset($y2)) $this->y_bot_margin = $this->image_height - $y2; - else unset($this->y_bot_margin); - - return TRUE; - } - - /* - * Calculate the World Coordinate limits of the plot area. - * This goes with SetPlotAreaWorld, but the calculations are - * deferred until the graph is being drawn. - * Uses and sets: plot_min_x, plot_max_x, plot_min_y, plot_max_y - * These can be user-supplied or NULL to auto-calculate. - * Pre-requisites: FindDataLimits() calculates min_x, max_x, min_y, max_y - * which are the limits of the data to be plotted. - * - * The general method is this: - * If any part of the range is user-defined (via SetPlotAreaWorld), - * use the user-defined value. - * Else, if this is an implicitly-defined independent variable, - * use the fixed range of 0 to (max+1). - * Else, if this is an explicitly-defined independent variable, - * use the exact data range (min to max). - * Else, this is the dependent variable, so define a range which - * includes and exceeds the data range by a bit. - */ - protected function CalcPlotAreaWorld() - { - // Data array omits X or Y? - $implied_x = $this->datatype_implied && !$this->datatype_swapped_xy; - $implied_y = $this->datatype_implied && $this->datatype_swapped_xy; - - if (isset($this->plot_min_x) && $this->plot_min_x !== '') - $xmin = $this->plot_min_x; // Use user-provided value - elseif ($implied_x) - $xmin = 0; // Implied X starts at zero - elseif ($this->datatype_swapped_xy) - // If X is the dependent variable, leave some room below. - $xmin = floor($this->min_x - abs($this->min_x) * 0.1); - else - $xmin = $this->min_x; // Otherwise just start at the min data X - - if (isset($this->plot_max_x) && $this->plot_max_x !== '') - $xmax = $this->plot_max_x; // Use user-provided value - elseif ($implied_x) - $xmax = $this->max_x + 1; // Implied X ends after last value - elseif ($this->datatype_swapped_xy) - // If X is the dependent variable, leave some room above. - $xmax = ceil($this->max_x + abs($this->max_x) * 0.1); - else - $xmax = $this->max_x; // Otherwise just end at the max data X - - if (isset($this->plot_min_y) && $this->plot_min_y !== '') - $ymin = $this->plot_min_y; // Use user-provided value - elseif ($implied_y) - $ymin = 0; // Implied Y starts at zero - elseif ($this->datatype_swapped_xy) - $ymin = $this->min_y; // Start at min data Y - else - // If Y is the dependent variable, leave some room below. - $ymin = floor($this->min_y - abs($this->min_y) * 0.1); - - if (isset($this->plot_max_y) && $this->plot_max_y !== '') - $ymax = $this->plot_max_y; // Use user-provided value - elseif ($implied_y) - $ymax = $this->max_y + 1; // Implied Y ends after last value - elseif ($this->datatype_swapped_xy) - $ymax = $this->max_y; // End at max data Y - else - // If Y is the dependent variable, leave some room above. - $ymax = ceil($this->max_y + abs($this->max_y) * 0.1); - - // Error checking - - if ($ymin == $ymax) - $ymax++; - if ($xmin == $xmax) - $xmax++; - - if ($this->yscale_type == 'log') { - if ($ymin <= 0) { - $ymin = 1; - } - if ($ymax <= 0) { - // Note: Error messages reference the user function, not this function. - return $this->PrintError('SetPlotAreaWorld(): Log plots need data greater than 0'); - } - } - - if ($ymax <= $ymin) { - return $this->PrintError('SetPlotAreaWorld(): Error in data - max not greater than min'); - } - - $this->plot_min_x = $xmin; - $this->plot_max_x = $xmax; - $this->plot_min_y = $ymin; - $this->plot_max_y = $ymax; - if ($this->GetCallback('debug_scale')) { - $this->DoCallback('debug_scale', __FUNCTION__, array( - 'plot_min_x' => $this->plot_min_x, 'plot_min_y' => $this->plot_min_y, - 'plot_max_x' => $this->plot_max_x, 'plot_max_y' => $this->plot_max_y)); - } - return TRUE; - } - - /* - * Stores the desired World Coordinate range of the plot. - * The user calls this to force one or more of the range limits to - * specific values. Anything not set will be calculated in CalcPlotAreaWorld(). - */ - function SetPlotAreaWorld($xmin=NULL, $ymin=NULL, $xmax=NULL, $ymax=NULL) - { - $this->plot_min_x = $xmin; - $this->plot_max_x = $xmax; - $this->plot_min_y = $ymin; - $this->plot_max_y = $ymax; - return TRUE; - } - - /* - * Calculate the width (or height) of bars for bar plots. - * $stacked : If true, this is a stacked bar plot (1 bar per group). - * $verticals : If false, this is a horizontal bar plot. - * This calculates: - * record_bar_width : Allocated width for each bar (including gaps) - * actual_bar_width : Actual drawn width of each bar - * bar_adjust_gap : Gap on each side of each bar (0 if they touch) - * For the case $verticals=False, horizontal bars are being drawn, - * but the same variable names are used. Think of "bar_width" as being - * the width if you are standing on the Y axis looking towards positive X. - */ - protected function CalcBarWidths($stacked, $verticals) - { - // group_width is the width of a group, including padding - if ($verticals) { - $group_width = $this->plot_area_width / $this->num_data_rows; - } else { - $group_width = $this->plot_area_height / $this->num_data_rows; - } - - // Actual number of bar spaces in the group. This includes the drawn bars, and - // 'bar_extra_space'-worth of extra bars. - if ($stacked) { - $num_spots = 1 + $this->bar_extra_space; - } else { - $num_spots = $this->data_columns + $this->bar_extra_space; - } - - // record_bar_width is the width of each bar's allocated area. - // If bar_width_adjust=1 this is the width of the bar, otherwise - // the bar is centered inside record_bar_width. - // The equation is: - // group_frac_width * group_width = record_bar_width * num_spots - $this->record_bar_width = $this->group_frac_width * $group_width / $num_spots; - - // Note that the extra space due to group_frac_width and bar_extra_space will be - // evenly divided on each side of the group: the drawn bars are centered in the group. - - // Within each bar's allocated space, if bar_width_adjust=1 the bar fills the - // space, otherwise it is centered. - // This is the actual drawn bar width: - $this->actual_bar_width = $this->record_bar_width * $this->bar_width_adjust; - // This is the gap on each side of the bar (0 if bar_width_adjust=1): - $this->bar_adjust_gap = ($this->record_bar_width - $this->actual_bar_width) / 2; - - if ($this->GetCallback('debug_scale')) { - $this->DoCallback('debug_scale', __FUNCTION__, array( - 'record_bar_width' => $this->record_bar_width, - 'actual_bar_width' => $this->actual_bar_width, - 'bar_adjust_gap' => $this->bar_adjust_gap)); - } - return TRUE; - } - - /* - * Calculate X and Y Axis Positions, world coordinates. - * This needs the min/max x/y range set by CalcPlotAreaWorld. - * It adjusts or sets x_axis_position and y_axis_position per the data. - * Empty string means the values need to be calculated; otherwise they - * are supplied but need to be validated against the World area. - * - * Note: This used to be in CalcTranslation, but CalcMargins needs it too. - * This does not calculate the pixel values of the axes. That happens in - * CalcTranslation, after scaling is set up (which has to happen after - * margins are set up). - * - * For vertical plots, the X axis defaults to Y=0 if that is inside the plot range, else whichever - * of the top or bottom that has the smallest absolute value (that is, the value closest to 0). - * The Y axis defaults to the left edge. For horizontal plots, the axis roles and defaults are switched. - */ - protected function CalcAxisPositions() - { - // Validate user-provided X axis position, or calculate a default if not provided: - if ($this->x_axis_position !== '') { - // Force user-provided X axis position to be within the plot range: - $this->x_axis_position = min(max($this->plot_min_y, $this->x_axis_position), $this->plot_max_y); - } elseif ($this->yscale_type == 'log') { - // Always use 1 for X axis position on log scale plots. - $this->x_axis_position = 1; - } elseif ($this->datatype_swapped_xy || $this->plot_min_y > 0) { - // Horizontal plot, or Vertical Plot with all Y > 0: Place X axis on the bottom. - $this->x_axis_position = $this->plot_min_y; - } elseif ($this->plot_max_y < 0) { - // Vertical plot with all Y < 0, so place the X axis at the top. - $this->x_axis_position = $this->plot_max_y; - } else { - // Vertical plot range includes Y=0, so place X axis at 0. - $this->x_axis_position = 0; - } - - // Validate user-provided Y axis position, or calculate a default if not provided: - if ($this->y_axis_position !== '') { - // Force user-provided Y axis position to be within the plot range: - $this->y_axis_position = min(max($this->plot_min_x, $this->y_axis_position), $this->plot_max_x); - } elseif ($this->xscale_type == 'log') { - // Always use 1 for Y axis position on log scale plots. - $this->y_axis_position = 1; - } elseif (!$this->datatype_swapped_xy || $this->plot_min_x > 0) { - // Vertical plot, or Horizontal Plot with all X > 0: Place Y axis on left side. - $this->y_axis_position = $this->plot_min_x; - } elseif ($this->plot_max_x < 0) { - // Horizontal plot with all X < 0, so place the Y axis on the right side. - $this->y_axis_position = $this->plot_max_x; - } else { - // Horizontal plot range includes X=0: place Y axis at 0. - $this->y_axis_position = 0; - } - - if ($this->GetCallback('debug_scale')) { - $this->DoCallback('debug_scale', __FUNCTION__, array( - 'x_axis_position' => $this->x_axis_position, - 'y_axis_position' => $this->y_axis_position)); - } - - return TRUE; - } - - /* - * Calculates scaling stuff... - */ - protected function CalcTranslation() - { - if ($this->plot_max_x - $this->plot_min_x == 0) { // Check for div by 0 - $this->xscale = 0; - } else { - if ($this->xscale_type == 'log') { - $this->xscale = $this->plot_area_width / - (log10($this->plot_max_x) - log10($this->plot_min_x)); - } else { - $this->xscale = $this->plot_area_width / ($this->plot_max_x - $this->plot_min_x); - } - } - - if ($this->plot_max_y - $this->plot_min_y == 0) { // Check for div by 0 - $this->yscale = 0; - } else { - if ($this->yscale_type == 'log') { - $this->yscale = $this->plot_area_height / - (log10($this->plot_max_y) - log10($this->plot_min_y)); - } else { - $this->yscale = $this->plot_area_height / ($this->plot_max_y - $this->plot_min_y); - } - } - // GD defines x = 0 at left and y = 0 at TOP so -/+ respectively - if ($this->xscale_type == 'log') { - $this->plot_origin_x = $this->plot_area[0] - ($this->xscale * log10($this->plot_min_x) ); - } else { - $this->plot_origin_x = $this->plot_area[0] - ($this->xscale * $this->plot_min_x); - } - if ($this->yscale_type == 'log') { - $this->plot_origin_y = $this->plot_area[3] + ($this->yscale * log10($this->plot_min_y)); - } else { - $this->plot_origin_y = $this->plot_area[3] + ($this->yscale * $this->plot_min_y); - } - - // Convert axis positions to device coordinates: - $this->y_axis_x_pixels = $this->xtr($this->y_axis_position); - $this->x_axis_y_pixels = $this->ytr($this->x_axis_position); - - if ($this->GetCallback('debug_scale')) { - $this->DoCallback('debug_scale', __FUNCTION__, array( - 'xscale' => $this->xscale, 'yscale' => $this->yscale, - 'plot_origin_x' => $this->plot_origin_x, 'plot_origin_y' => $this->plot_origin_y, - 'y_axis_x_pixels' => $this->y_axis_x_pixels, - 'x_axis_y_pixels' => $this->x_axis_y_pixels)); - } - - return TRUE; - } - - /* - * Translate X world coordinate into pixel coordinate - * See CalcTranslation() for calculation of xscale. - * Note: This function should be 'protected', but is left public for historical reasons. - * See GetDeviceXY() for a preferred public method. - */ - function xtr($x_world) - { - if ($this->xscale_type == 'log') { - $x_pixels = $this->plot_origin_x + log10($x_world) * $this->xscale ; - } else { - $x_pixels = $this->plot_origin_x + $x_world * $this->xscale ; - } - return round($x_pixels); - } - - /* - * Translate Y world coordinate into pixel coordinate. - * See CalcTranslation() for calculation of yscale. - * Note: This function should be 'protected', but is left public for historical reasons. - * See GetDeviceXY() for a preferred public method. - */ - function ytr($y_world) - { - if ($this->yscale_type == 'log') { - //minus because GD defines y = 0 at top. doh! - $y_pixels = $this->plot_origin_y - log10($y_world) * $this->yscale ; - } else { - $y_pixels = $this->plot_origin_y - $y_world * $this->yscale ; - } - return round($y_pixels); - } - - /* A public interface to xtr and ytr. Translates (x,y) in world coordinates - * to (x,y) in device coordinates and returns them as an array. - * Usage is: list($x_pixel, $y_pixel) = $plot->GetDeviceXY($x_world, $y_world) - */ - function GetDeviceXY($x_world, $y_world) - { - if (!isset($this->xscale)) { - return $this->PrintError("GetDeviceXY() was called before translation factors were calculated"); - } - return array($this->xtr($x_world), $this->ytr($y_world)); - } - - /* - * Calculate tick parameters: Start, end, and delta values. This is used - * by both DrawXTicks() and DrawYTicks(). - * This currently uses the same simplistic method previously used by - * PHPlot (basically just range/10), but splitting this out into its - * own function is the first step in replacing the method. - * This is also used by CalcMaxTickSize() for CalcMargins(). - * - * $which : 'x' or 'y' : Which tick parameters to calculate - * - * Returns an array of 3 elements: tick_start, tick_end, tick_step - */ - protected function CalcTicks($which) - { - if ($which == 'x') { - $num_ticks = $this->num_x_ticks; - $tick_inc = $this->x_tick_inc; - $data_max = $this->plot_max_x; - $data_min = $this->plot_min_x; - $skip_lo = $this->skip_left_tick; - $skip_hi = $this->skip_right_tick; - $anchor = &$this->x_tick_anchor; // Use reference because this might not be set - } elseif ($which == 'y') { - $num_ticks = $this->num_y_ticks; - $tick_inc = $this->y_tick_inc; - $data_max = $this->plot_max_y; - $data_min = $this->plot_min_y; - $skip_lo = $this->skip_bottom_tick; - $skip_hi = $this->skip_top_tick; - $anchor = &$this->y_tick_anchor; // Use reference because this might not be set - } else { - return $this->PrintError("CalcTicks: Invalid usage ($which)"); - } - - if (!empty($tick_inc)) { - $tick_step = $tick_inc; - } elseif (!empty($num_ticks)) { - $tick_step = ($data_max - $data_min) / $num_ticks; - } else { - $tick_step = ($data_max - $data_min) / 10; - } - - // NOTE: When working with floats, because of approximations when adding $tick_step, - // the value may not quite reach the end, or may exceed it very slightly. - // So apply a "fudge" factor. - $tick_start = (double)$data_min; - $tick_end = (double)$data_max + ($data_max - $data_min) / 10000.0; - - // If a tick anchor was given, adjust the start of the range so the anchor falls - // at an exact tick mark (or would, if it was within range). - if (isset($anchor)) { - $tick_start = $anchor - $tick_step * floor(($anchor - $tick_start) / $tick_step); - } - - // Lastly, adjust for option to skip left/bottom or right/top tick marks: - if ($skip_lo) - $tick_start += $tick_step; - if ($skip_hi) - $tick_end -= $tick_step; - - return array($tick_start, $tick_end, $tick_step); - } - - /* - * Calculate the size of the biggest tick label. This is used by CalcMargins(). - * For 'x' ticks, it returns the height . For 'y' ticks, it returns the width. - * This means height along Y, or width along X - not relative to the text angle. - * That is what we need to calculate the needed margin space. - * (Previous versions of PHPlot estimated this, using the maximum X or Y value, - * or maybe the longest string. That doesn't work. -10 is longer than 9, etc. - * So this gets the actual size of each label, slow as that may be. - */ - protected function CalcMaxTickLabelSize($which) - { - list($tick_start, $tick_end, $tick_step) = $this->CalcTicks($which); - - if ($which == 'x') { - $font = $this->fonts['x_label']; - $angle = $this->x_label_angle; - } elseif ($which == 'y') { - $font = $this->fonts['y_label']; - $angle = $this->y_label_angle; - } else { - return $this->PrintError("CalcMaxTickLabelSize: Invalid usage ($which)"); - } - - $max_width = 0; - $max_height = 0; - - // Loop over ticks, same as DrawXTicks and DrawYTicks: - // Avoid cumulative round-off errors from $val += $delta - $n = 0; - $tick_val = $tick_start; - while ($tick_val <= $tick_end) { - $tick_label = $this->FormatLabel($which, $tick_val); - list($width, $height) = $this->SizeText($font, $angle, $tick_label); - if ($width > $max_width) $max_width = $width; - if ($height > $max_height) $max_height = $height; - $tick_val = $tick_start + ++$n * $tick_step; - } - if ($this->GetCallback('debug_scale')) { - $this->DoCallback('debug_scale', __FUNCTION__, array( - 'which' => $which, 'height' => $max_height, 'width' => $max_width)); - } - - if ($which == 'x') - return $max_height; - return $max_width; - } - - /* - * Calculate the size of the biggest data label. This is used by CalcMargins(). - * For $which='x', it returns the height of labels along the top or bottom. - * For $which='y', it returns the width of labels along the left or right sides. - * There is only one set of data labels (the first position in each data record). - * They normally go along the top or bottom (or both). If the data type indicates - * X/Y swapping (which is used for horizontal bar charts), the data labels go - * along the sides instead. So CalcMaxDataLabelSize('x') returns 0 if the - * data is X/Y swapped, and CalcMaxDataLabelSize('y') returns 0 if the data is - * is not X/Y swapped. - */ - protected function CalcMaxDataLabelSize($which = 'x') - { - if ($which == 'x') { - if ($this->datatype_swapped_xy) - return 0; // Shortcut: labels aren't on top/bottom. - $font = $this->fonts['x_label']; - $angle = $this->x_data_label_angle; - $format_code = 'xd'; - } elseif ($which == 'y') { - if (!$this->datatype_swapped_xy) - return 0; // Shortcut: labels aren't on left/right. - $font = $this->fonts['y_label']; - $angle = $this->y_data_label_angle; - $format_code = 'yd'; - } else { - return $this->PrintError("CalcMaxDataLabelSize: Invalid usage ($which)"); - } - $max_width = 0; - $max_height = 0; - - // Loop over all data labels and find the biggest: - for ($i = 0; $i < $this->num_data_rows; $i++) { - $label = $this->FormatLabel($format_code, $this->data[$i][0]); - list($width, $height) = $this->SizeText($font, $angle, $label); - if ($width > $max_width) $max_width = $width; - if ($height > $max_height) $max_height = $height; - } - if ($this->GetCallback('debug_scale')) { - $this->DoCallback('debug_scale', __FUNCTION__, array( - 'height' => $max_height, 'width' => $max_width)); - } - - if ($this->datatype_swapped_xy) - return $max_width; - return $max_height; - } - - /* - * Set grid control defaults. - * X grid defaults off, Y grid defaults on, except the reverse is true - * with swapped graphs such as horizontal bars. - */ - protected function CalcGridSettings() - { - if (!isset($this->draw_x_grid)) - $this->draw_x_grid = $this->datatype_swapped_xy; - if (!isset($this->draw_y_grid)) - $this->draw_y_grid = !$this->datatype_swapped_xy; - } - - /* - * Helper for CheckLabels() - determine if there are any non-empty labels. - * Returns True if all data labels are empty, else False. - */ - protected function CheckLabelsAllEmpty() - { - for ($i = 0; $i < $this->num_data_rows; $i++) - if ($this->data[$i][0] !== '') return FALSE; - return TRUE; - } - - /* - * Check and set label parameters. This handles deferred processing for label - * positioning and other label-related parameters. - * Copy label_format from 'x' to 'xd', and 'y' to 'yd', if not already set. - * Set x_data_label_angle from x_label_angle, if not already set. - * Apply defaults to X and Y tick and data label positions. - * Note: the label strings in the data array are used as X data labels in - * the normal case, but as Y data labels in the swapped X/Y case. - */ - protected function CheckLabels() - { - // The X and Y data labels are formatted the same as X and Y tick labels, - // unless overridden. Check and apply defaults for FormatLabel here: - if (empty($this->label_format['xd']) && !empty($this->label_format['x'])) - $this->label_format['xd'] = $this->label_format['x']; - if (empty($this->label_format['yd']) && !empty($this->label_format['y'])) - $this->label_format['yd'] = $this->label_format['y']; - - // The X tick label angle setting controls X data label angles too, - // unless overridden. Check and apply the default here: - if (!isset($this->x_data_label_angle)) - $this->x_data_label_angle = $this->x_label_angle; - // Note: Y data label angle defaults to zero, unlike X, - // for compatibility with older releases. - - // X Label position fixups, for x_data_label_pos and x_tick_label_pos: - if ($this->datatype_swapped_xy) { - // Just apply defaults - there is no position conflict for X labels. - if (!isset($this->x_tick_label_pos)) - $this->x_tick_label_pos = 'plotdown'; - if (!isset($this->x_data_label_pos)) - $this->x_data_label_pos = 'none'; - } else { - // Apply defaults but do not allow conflict between tick and data labels. - if (isset($this->x_data_label_pos)) { - if (!isset($this->x_tick_label_pos)) { - // Case: data_label_pos is set, tick_label_pos needs a default: - if ($this->x_data_label_pos == 'none') - $this->x_tick_label_pos = 'plotdown'; - else - $this->x_tick_label_pos = 'none'; - } - } elseif (isset($this->x_tick_label_pos)) { - // Case: tick_label_pos is set, data_label_pos needs a default: - if ($this->x_tick_label_pos == 'none') - $this->x_data_label_pos = 'plotdown'; - else - $this->x_data_label_pos = 'none'; - } else { - // Case: Neither tick_label_pos nor data_label_pos is set. - // We do not want them to be both on (as PHPlot used to do in this case). - // Turn on data labels if any were supplied, else tick labels. - if ($this->CheckLabelsAllEmpty()) { - $this->x_data_label_pos = 'none'; - $this->x_tick_label_pos = 'plotdown'; - } else { - $this->x_data_label_pos = 'plotdown'; - $this->x_tick_label_pos = 'none'; - } - } - } - - // Y Label position fixups, for y_data_label_pos and y_tick_label_pos: - if (!$this->datatype_swapped_xy) { - // Just apply defaults - there is no position conflict. - if (!isset($this->y_tick_label_pos)) - $this->y_tick_label_pos = 'plotleft'; - if (!isset($this->y_data_label_pos)) - $this->y_data_label_pos = 'none'; - } else { - // Apply defaults but do not allow conflict between tick and data labels. - if (isset($this->y_data_label_pos)) { - if (!isset($this->y_tick_label_pos)) { - // Case: data_label_pos is set, tick_label_pos needs a default: - if ($this->y_data_label_pos == 'none') - $this->y_tick_label_pos = 'plotleft'; - else - $this->y_tick_label_pos = 'none'; - } - } elseif (isset($this->y_tick_label_pos)) { - // Case: tick_label_pos is set, data_label_pos needs a default: - if ($this->y_tick_label_pos == 'none') - $this->y_data_label_pos = 'plotleft'; - else - $this->y_data_label_pos = 'none'; - } else { - // Case: Neither tick_label_pos nor data_label_pos is set. - // Turn on data labels if any were supplied, else tick labels. - if ($this->CheckLabelsAllEmpty()) { - $this->y_data_label_pos = 'none'; - $this->y_tick_label_pos = 'plotleft'; - } else { - $this->y_data_label_pos = 'plotleft'; - $this->y_tick_label_pos = 'none'; - } - } - } - return TRUE; - } - - /* - * Formats a tick or data label. - * which_pos - 'x', 'xd', 'y', or 'yd', selects formatting controls. - * x, y are for tick labels; xd, yd are for data labels. - * which_lab - String to format as a label. - * Credits: Time formatting suggested by Marlin Viss - * Custom formatting suggested by zer0x333 - * Notes: - * Type 'title' is obsolete and retained for compatibility. - * Class variable 'data_units_text' is retained as a suffix for 'data' type formatting for - * backward compatibility. Since there was never a function/method to set it, there - * could be somebody out there who sets it directly in the object. - */ - protected function FormatLabel($which_pos, $which_lab) - { - // Assign a reference shortcut to the label format controls. - // Note CheckLabels() made sure the 'xd' and 'yd' arrays are set. - $format =& $this->label_format[$which_pos]; - - // Don't format empty strings (especially as time or numbers), or if no type was set. - if ($which_lab !== '' && !empty($format['type'])) { - switch ($format['type']) { - case 'title': // Note: This is obsolete - $which_lab = @ $this->data[$which_lab][0]; - break; - case 'data': - $which_lab = $format['prefix'] - . $this->number_format($which_lab, $format['precision']) - . $this->data_units_text // Obsolete - . $format['suffix']; - break; - case 'time': - $which_lab = strftime($format['time_format'], $which_lab); - break; - case 'printf': - $which_lab = sprintf($format['printf_format'], $which_lab); - break; - case 'custom': - $which_lab = call_user_func($format['custom_callback'], $which_lab, $format['custom_arg']); - break; - - } - } - return $which_lab; - } - -///////////////////////////////////////////// -/////////////// TICKS -///////////////////////////////////////////// - - /* - * Set the step (interval) between X ticks. - * Use either this or SetNumXTicks(), not both, to control the X tick marks. - */ - function SetXTickIncrement($which_ti='') - { - $this->x_tick_inc = $which_ti; - if (!empty($which_ti)) { - $this->num_x_ticks = ''; - } - return TRUE; - } - - /* - * Set the step (interval) between Y ticks. - * Use either this or SetNumYTicks(), not both, to control the Y tick marks. - */ - function SetYTickIncrement($which_ti='') - { - $this->y_tick_inc = $which_ti; - if (!empty($which_ti)) { - $this->num_y_ticks = ''; - } - return TRUE; - } - - /* - * Set the number of X tick marks. - * Use either this or SetXTickIncrement(), not both, to control the X tick marks. - */ - function SetNumXTicks($which_nt='') - { - $this->num_x_ticks = $which_nt; - if (!empty($which_nt)) { - $this->x_tick_inc = ''; - } - return TRUE; - } - - /* - * Set the number of Y tick marks. - * Use either this or SetYTickIncrement(), not both, to control the Y tick marks. - */ - function SetNumYTicks($which_nt='') - { - $this->num_y_ticks = $which_nt; - if (!empty($which_nt)) { - $this->y_tick_inc = ''; //either use num_y_ticks or y_tick_inc, not both - } - return TRUE; - } - - /* - * Set the position for the X tick marks. - * These can be above the plot, below, both positions, at the X axis, or suppressed. - */ - function SetXTickPos($which_tp) - { - $this->x_tick_pos = $this->CheckOption($which_tp, 'plotdown, plotup, both, xaxis, none', - __FUNCTION__); - return (boolean)$this->x_tick_pos; - } - - /* - * Set the position for the Y tick marks. - * These can be left of the plot, right, both positions, at the Y axis, or suppressed. - */ - function SetYTickPos($which_tp) - { - $this->y_tick_pos = $this->CheckOption($which_tp, 'plotleft, plotright, both, yaxis, none', - __FUNCTION__); - return (boolean)$this->y_tick_pos; - } - - /* - * Skip the top-most Y axis tick mark and label if $skip is true. - */ - function SetSkipTopTick($skip) - { - $this->skip_top_tick = (bool)$skip; - return TRUE; - } - - /* - * Skip the bottom-most Y axis tick mark and label if $skip is true. - */ - function SetSkipBottomTick($skip) - { - $this->skip_bottom_tick = (bool)$skip; - return TRUE; - } - - /* - * Skip the left-most X axis tick mark and label if $skip is true. - */ - function SetSkipLeftTick($skip) - { - $this->skip_left_tick = (bool)$skip; - return TRUE; - } - - /* - * Skip the right-most X axis tick mark and label if $skip is true. - */ - function SetSkipRightTick($skip) - { - $this->skip_right_tick = (bool)$skip; - return TRUE; - } - - /* - * Set the outer length of X tick marks to $which_xln pixels. - * This is the part of the tick mark that sticks out from the plot area. - */ - function SetXTickLength($which_xln) - { - $this->x_tick_length = $which_xln; - return TRUE; - } - - /* - * Set the outer length of Y tick marks to $which_yln pixels. - * This is the part of the tick mark that sticks out from the plot area. - */ - function SetYTickLength($which_yln) - { - $this->y_tick_length = $which_yln; - return TRUE; - } - - /* - * Set the crossing length of X tick marks to $which_xc pixels. - * This is the part of the tick mark that sticks into the plot area. - */ - function SetXTickCrossing($which_xc) - { - $this->x_tick_cross = $which_xc; - return TRUE; - } - - /* - * Set the crossing length of Y tick marks to $which_yc pixels. - * This is the part of the tick mark that sticks into the plot area. - */ - function SetYTickCrossing($which_yc) - { - $this->y_tick_cross = $which_yc; - return TRUE; - } - - /* - * Set an anchor point for X tick marks. There will be an X tick mark at - * this exact value (if the data range were extended to include it). - */ - function SetXTickAnchor($xta = NULL) - { - $this->x_tick_anchor = $xta; - return TRUE; - } - - /* - * Set an anchor point for Y tick marks. There will be a Y tick mark at - * this exact value (if the data range were extended to include it). - */ - function SetYTickAnchor($yta = NULL) - { - $this->y_tick_anchor = $yta; - return TRUE; - } - -///////////////////////////////////////////// -//////////////////// GENERIC DRAWING -///////////////////////////////////////////// - - /* - * Fill the image background, with a tiled image file or solid color. - */ - protected function DrawBackground() - { - // Don't draw this twice if drawing two plots on one image - if (empty($this->done['background'])) { - if (isset($this->bgimg)) { // If bgimg is defined, use it - $this->tile_img($this->bgimg, 0, 0, $this->image_width, $this->image_height, $this->bgmode); - } else { // Else use solid color - ImageFilledRectangle($this->img, 0, 0, $this->image_width, $this->image_height, - $this->ndx_bg_color); - } - $this->done['background'] = TRUE; - } - return TRUE; - } - - /* - * Fill the plot area background, with a tiled image file or solid color. - */ - protected function DrawPlotAreaBackground() - { - if (isset($this->plotbgimg)) { - $this->tile_img($this->plotbgimg, $this->plot_area[0], $this->plot_area[1], - $this->plot_area_width, $this->plot_area_height, $this->plotbgmode); - } elseif ($this->draw_plot_area_background) { - ImageFilledRectangle($this->img, $this->plot_area[0], $this->plot_area[1], - $this->plot_area[2], $this->plot_area[3], $this->ndx_plot_bg_color); - } - return TRUE; - } - - /* - * Tiles an image at some given coordinates. - * $file : Filename of the picture to be used as tile. - * $xorig : X device coordinate of where the tile is to begin. - * $yorig : Y device coordinate of where the tile is to begin. - * $width : Width of the area to be tiled. - * $height : Height of the area to be tiled. - * $mode : Tiling mode. One of 'centeredtile', 'tile', 'scale'. - */ - protected function tile_img($file, $xorig, $yorig, $width, $height, $mode) - { - $im = $this->GetImage($file, $tile_width, $tile_height); - if (!$im) - return FALSE; // GetImage already produced an error message. - - if ($mode == 'scale') { - imagecopyresampled($this->img, $im, $xorig, $yorig, 0, 0, $width, $height, - $tile_width, $tile_height); - return TRUE; - } - - if ($mode == 'centeredtile') { - $x0 = - floor($tile_width/2); // Make the tile look better - $y0 = - floor($tile_height/2); - } else { // Accept anything else as $mode == 'tile' - $x0 = 0; - $y0 = 0; - } - - // Draw the tile onto a temporary image first. - $tmp = imagecreate($width, $height); - if (! $tmp) - return $this->PrintError('tile_img(): Could not create image resource.'); - - for ($x = $x0; $x < $width; $x += $tile_width) - for ($y = $y0; $y < $height; $y += $tile_height) - imagecopy($tmp, $im, $x, $y, 0, 0, $tile_width, $tile_height); - - // Copy the temporary image onto the final one. - imagecopy($this->img, $tmp, $xorig, $yorig, 0,0, $width, $height); - - // Free resources - imagedestroy($tmp); - imagedestroy($im); - - return TRUE; - } - - /* - * Return the image border width. - * This is used by CalcMargins() and DrawImageBorder(). - */ - protected function GetImageBorderWidth() - { - if ($this->image_border_type == 'none') - return 0; // No border - if (!empty($this->image_border_width)) - return $this->image_border_width; // Specified border width - if ($this->image_border_type == 'raised') - return 2; // Default for raised border is 2 pixels. - return 1; // Default for other border types is 1 pixel. - } - - /* - * Draws a border around the final image. - * Note: 'plain' draws a flat border using the dark shade of the border color. - * This probably should have been written to use the actual border color, but - * it is too late to fix it without changing plot appearances. Therefore a - * new type 'solid' was added to use the SetImageBorderColor color. - */ - protected function DrawImageBorder() - { - // Do nothing if already drawn, or if no border has been set. - if ($this->image_border_type == 'none' || !empty($this->done['border'])) - return TRUE; - $width = $this->GetImageBorderWidth(); - $color1 = $this->ndx_i_border; - $color2 = $this->ndx_i_border_dark; - $ex = $this->image_width - 1; - $ey = $this->image_height - 1; - switch ($this->image_border_type) { - case 'raised': - // Top and left lines use border color, right and bottom use the darker shade. - // Drawing order matters in the upper right and lower left corners. - for ($i = 0; $i < $width; $i++, $ex--, $ey--) { - imageline($this->img, $i, $i, $ex, $i, $color1); // Top - imageline($this->img, $ex, $i, $ex, $ey, $color2); // Right - imageline($this->img, $i, $i, $i, $ey, $color1); // Left - imageline($this->img, $i, $ey, $ex, $ey, $color2); // Bottom - } - break; - case 'plain': // See note above re colors - $color1 = $color2; - // Fall through - case 'solid': - for ($i = 0; $i < $width; $i++, $ex--, $ey--) { - imagerectangle($this->img, $i, $i, $ex, $ey, $color1); - } - break; - default: - return $this->PrintError( - "DrawImageBorder(): unknown image_border_type: '$this->image_border_type'"); - } - $this->done['border'] = TRUE; // Border should only be drawn once per image. - return TRUE; - } - - /* - * Draws the main title on the graph. - * The title must not be drawn more than once (in the case of multiple plots - * on the image), because TTF text antialiasing makes it look bad. - */ - protected function DrawTitle() - { - if (!empty($this->done['title']) || $this->title_txt === '') - return TRUE; - - // Center of the image: - $xpos = $this->image_width / 2; - - // Place it at almost at the top - $ypos = $this->title_offset; - - $this->DrawText($this->fonts['title'], 0, $xpos, $ypos, - $this->ndx_title_color, $this->title_txt, 'center', 'top'); - - $this->done['title'] = TRUE; - return TRUE; - } - - /* - * Draws the X-Axis Title - */ - protected function DrawXTitle() - { - if ($this->x_title_pos == 'none') - return TRUE; - - // Center of the plot - $xpos = ($this->plot_area[2] + $this->plot_area[0]) / 2; - - // Upper title - if ($this->x_title_pos == 'plotup' || $this->x_title_pos == 'both') { - $ypos = $this->plot_area[1] - $this->x_title_top_offset; - $this->DrawText($this->fonts['x_title'], 0, $xpos, $ypos, $this->ndx_x_title_color, - $this->x_title_txt, 'center', 'bottom'); - } - // Lower title - if ($this->x_title_pos == 'plotdown' || $this->x_title_pos == 'both') { - $ypos = $this->plot_area[3] + $this->x_title_bot_offset; - $this->DrawText($this->fonts['x_title'], 0, $xpos, $ypos, $this->ndx_x_title_color, - $this->x_title_txt, 'center', 'top'); - } - return TRUE; - } - - /* - * Draws the Y-Axis Title - */ - protected function DrawYTitle() - { - if ($this->y_title_pos == 'none') - return TRUE; - - // Center the title vertically to the plot area - $ypos = ($this->plot_area[3] + $this->plot_area[1]) / 2; - - if ($this->y_title_pos == 'plotleft' || $this->y_title_pos == 'both') { - $xpos = $this->plot_area[0] - $this->y_title_left_offset; - $this->DrawText($this->fonts['y_title'], 90, $xpos, $ypos, $this->ndx_y_title_color, - $this->y_title_txt, 'right', 'center'); - } - if ($this->y_title_pos == 'plotright' || $this->y_title_pos == 'both') { - $xpos = $this->plot_area[2] + $this->y_title_right_offset; - $this->DrawText($this->fonts['y_title'], 90, $xpos, $ypos, $this->ndx_y_title_color, - $this->y_title_txt, 'left', 'center'); - } - - return TRUE; - } - - /* - * Draw the X axis, including ticks and labels, and X (vertical) grid lines. - */ - protected function DrawXAxis() - { - // Draw ticks, labels and grid - $this->DrawXTicks(); - - //Draw X Axis at Y = x_axis_y_pixels, unless suppressed (See SetXAxisPosition) - if (empty($this->suppress_x_axis)) { - ImageLine($this->img, $this->plot_area[0]+1, $this->x_axis_y_pixels, - $this->plot_area[2]-1, $this->x_axis_y_pixels, $this->ndx_grid_color); - } - return TRUE; - } - - /* - * Draw the Y axis, including ticks and labels, and Y (horizontal) grid lines. - * Horizontal grid lines overwrite horizontal axis with y=0, so call this first, then DrawXAxis() - */ - protected function DrawYAxis() - { - // Draw ticks, labels and grid - $this->DrawYTicks(); - - // Draw Y axis at X = y_axis_x_pixels, unless suppressed (See SetYAxisPosition) - if (empty($this->suppress_y_axis)) { - ImageLine($this->img, $this->y_axis_x_pixels, $this->plot_area[1], - $this->y_axis_x_pixels, $this->plot_area[3], $this->ndx_grid_color); - } - return TRUE; - } - - /* - * Draw one X tick mark and its tick label. - * $which_xlab : Formatted X value for the label. - * $which_xpix : X device coordinate for this tick mark. - */ - protected function DrawXTick($which_xlab, $which_xpix) - { - // Ticks on X axis - if ($this->x_tick_pos == 'xaxis') { - ImageLine($this->img, $which_xpix, $this->x_axis_y_pixels - $this->x_tick_cross, - $which_xpix, $this->x_axis_y_pixels + $this->x_tick_length, $this->ndx_tick_color); - } - - // Ticks on top of the Plot Area - if ($this->x_tick_pos == 'plotup' || $this->x_tick_pos == 'both') { - ImageLine($this->img, $which_xpix, $this->plot_area[1] - $this->x_tick_length, - $which_xpix, $this->plot_area[1] + $this->x_tick_cross, $this->ndx_tick_color); - } - - // Ticks on bottom of Plot Area - if ($this->x_tick_pos == 'plotdown' || $this->x_tick_pos == 'both') { - ImageLine($this->img, $which_xpix, $this->plot_area[3] + $this->x_tick_length, - $which_xpix, $this->plot_area[3] - $this->x_tick_cross, $this->ndx_tick_color); - } - - // Label on X axis - if ($this->x_tick_label_pos == 'xaxis') { - $this->DrawText($this->fonts['x_label'], $this->x_label_angle, - $which_xpix, $this->x_axis_y_pixels + $this->x_label_axis_offset, - $this->ndx_text_color, $which_xlab, 'center', 'top'); - } - - // Label on top of the Plot Area - if ($this->x_tick_label_pos == 'plotup' || $this->x_tick_label_pos == 'both') { - $this->DrawText($this->fonts['x_label'], $this->x_label_angle, - $which_xpix, $this->plot_area[1] - $this->x_label_top_offset, - $this->ndx_text_color, $which_xlab, 'center', 'bottom'); - } - - // Label on bottom of the Plot Area - if ($this->x_tick_label_pos == 'plotdown' || $this->x_tick_label_pos == 'both') { - $this->DrawText($this->fonts['x_label'], $this->x_label_angle, - $which_xpix, $this->plot_area[3] + $this->x_label_bot_offset, - $this->ndx_text_color, $which_xlab, 'center', 'top'); - } - return TRUE; - } - - /* - * Draw one Y tick mark and its tick label. Called from DrawYTicks() and DrawXAxis() - * $which_ylab : Formatted Y value for the label. - * $which_ypix : Y device coordinate for this tick mark. - */ - protected function DrawYTick($which_ylab, $which_ypix) - { - // Ticks on Y axis - if ($this->y_tick_pos == 'yaxis') { - ImageLine($this->img, $this->y_axis_x_pixels - $this->y_tick_length, $which_ypix, - $this->y_axis_x_pixels + $this->y_tick_cross, $which_ypix, $this->ndx_tick_color); - } - - // Ticks to the left of the Plot Area - if (($this->y_tick_pos == 'plotleft') || ($this->y_tick_pos == 'both') ) { - ImageLine($this->img, $this->plot_area[0] - $this->y_tick_length, $which_ypix, - $this->plot_area[0] + $this->y_tick_cross, $which_ypix, $this->ndx_tick_color); - } - - // Ticks to the right of the Plot Area - if (($this->y_tick_pos == 'plotright') || ($this->y_tick_pos == 'both') ) { - ImageLine($this->img, $this->plot_area[2] + $this->y_tick_length, $which_ypix, - $this->plot_area[2] - $this->y_tick_cross, $which_ypix, $this->ndx_tick_color); - } - - // Labels on Y axis - if ($this->y_tick_label_pos == 'yaxis') { - $this->DrawText($this->fonts['y_label'], $this->y_label_angle, - $this->y_axis_x_pixels - $this->y_label_axis_offset, $which_ypix, - $this->ndx_text_color, $which_ylab, 'right', 'center'); - } - - // Labels to the left of the plot area - if ($this->y_tick_label_pos == 'plotleft' || $this->y_tick_label_pos == 'both') { - $this->DrawText($this->fonts['y_label'], $this->y_label_angle, - $this->plot_area[0] - $this->y_label_left_offset, $which_ypix, - $this->ndx_text_color, $which_ylab, 'right', 'center'); - } - // Labels to the right of the plot area - if ($this->y_tick_label_pos == 'plotright' || $this->y_tick_label_pos == 'both') { - $this->DrawText($this->fonts['y_label'], $this->y_label_angle, - $this->plot_area[2] + $this->y_label_right_offset, $which_ypix, - $this->ndx_text_color, $which_ylab, 'left', 'center'); - } - return TRUE; - } - - /* - * Draws Grid, Ticks and Tick Labels along X-Axis - * Ticks and tick labels can be down of plot only, up of plot only, - * both on up and down of plot, or crossing a user defined X-axis - * - * Original vertical code submitted by Marlin Viss - */ - protected function DrawXTicks() - { - // Sets the line style for IMG_COLOR_STYLED lines (grid) - if ($this->dashed_grid) { - $this->SetDashedStyle($this->ndx_light_grid_color); - $style = IMG_COLOR_STYLED; - } else { - $style = $this->ndx_light_grid_color; - } - - // Calculate the tick start, end, and step: - list($x_start, $x_end, $delta_x) = $this->CalcTicks('x'); - - // Loop, avoiding cumulative round-off errors from $x_tmp += $delta_x - $n = 0; - $x_tmp = $x_start; - while ($x_tmp <= $x_end) { - $xlab = $this->FormatLabel('x', $x_tmp); - $x_pixels = $this->xtr($x_tmp); - - // Vertical grid lines - if ($this->draw_x_grid) { - ImageLine($this->img, $x_pixels, $this->plot_area[1], $x_pixels, $this->plot_area[3], $style); - } - - // Draw tick mark(s) - $this->DrawXTick($xlab, $x_pixels); - - // Step to next X, without accumulating error - $x_tmp = $x_start + ++$n * $delta_x; - } - return TRUE; - } - - /* - * Draw the grid, ticks, and tick labels along the Y axis. - * Ticks and tick labels can be left of plot only, right of plot only, - * both on the left and right of plot, or crossing a user defined Y-axis - */ - protected function DrawYTicks() - { - // Sets the line style for IMG_COLOR_STYLED lines (grid) - if ($this->dashed_grid) { - $this->SetDashedStyle($this->ndx_light_grid_color); - $style = IMG_COLOR_STYLED; - } else { - $style = $this->ndx_light_grid_color; - } - - // Calculate the tick start, end, and step: - list($y_start, $y_end, $delta_y) = $this->CalcTicks('y'); - - // Loop, avoiding cumulative round-off errors from $y_tmp += $delta_y - $n = 0; - $y_tmp = $y_start; - while ($y_tmp <= $y_end) { - $ylab = $this->FormatLabel('y', $y_tmp); - $y_pixels = $this->ytr($y_tmp); - - // Horizontal grid line - if ($this->draw_y_grid) { - ImageLine($this->img, $this->plot_area[0]+1, $y_pixels, $this->plot_area[2]-1, - $y_pixels, $style); - } - - // Draw tick mark(s) - $this->DrawYTick($ylab, $y_pixels); - - // Step to next Y, without accumulating error - $y_tmp = $y_start + ++$n * $delta_y; - } - return TRUE; - } - - /* - * Draw a border around the plot area. See SetPlotBorderType. - * Note: SetPlotBorderType sets plot_border_type to an array, but - * it won't be an array if it defaults or is set directly (backward compatibility). - */ - protected function DrawPlotBorder() - { - $pbt = (array)$this->plot_border_type; - $sides = 0; // Bitmap: 1=left 2=top 4=right 8=bottom - $map = array('left' => 1, 'plotleft' => 1, 'right' => 4, 'plotright' => 4, 'top' => 2, - 'bottom' => 8, 'both' => 5, 'sides' => 5, 'full' => 15, 'none' => 0); - foreach ($pbt as $option) $sides |= $map[$option]; - if ($sides == 15) { // Border on all 4 sides - imagerectangle($this->img, $this->plot_area[0], $this->plot_area[1], - $this->plot_area[2], $this->plot_area[3], $this->ndx_grid_color); - } else { - if ($sides & 1) // Left - imageline($this->img, $this->plot_area[0], $this->plot_area[1], - $this->plot_area[0], $this->plot_area[3], $this->ndx_grid_color); - if ($sides & 2) // Top - imageline($this->img, $this->plot_area[0], $this->plot_area[1], - $this->plot_area[2], $this->plot_area[1], $this->ndx_grid_color); - if ($sides & 4) // Right - imageline($this->img, $this->plot_area[2], $this->plot_area[1], - $this->plot_area[2], $this->plot_area[3], $this->ndx_grid_color); - if ($sides & 8) // Bottom - imageline($this->img, $this->plot_area[0], $this->plot_area[3], - $this->plot_area[2], $this->plot_area[3], $this->ndx_grid_color); - } - return TRUE; - } - - /* - * Draw the data value label associated with a point in the plot. - * These are labels that show the value (dependent variable, usually Y) of the data point, - * and are drawn within the plot area (not to be confused with axis data labels). - * - * $x_or_y : Specify 'x' or 'y' labels. This selects font, angle, and formatting. - * $x_world, $y_world : World coordinates of the text (see also x/y_adjustment). - * $text : The text to draw, after formatting with FormatLabel(). - * $halign, $valign : Selects from 9-point text alignment. - * $x_adjustment, $y_adjustment : Text position offsets, in device coordinates. - * $min_width, $min_height : If supplied, suppress the text if it will not fit. - * Returns True, if the text was drawn, or False, if it will not fit. - */ - protected function DrawDataValueLabel($x_or_y, $x_world, $y_world, $text, $halign, $valign, - $x_adjustment=0, $y_adjustment=0, $min_width=NULL, $min_height=NULL) - { - if ($x_or_y == 'x') { - $angle = $this->x_data_label_angle; - $font = $this->fonts['x_label']; - $formatted_text = $this->FormatLabel('xd', $text); - } else { // Assumed 'y' - $angle = $this->y_data_label_angle; - $font = $this->fonts['y_label']; - $formatted_text = $this->FormatLabel('yd', $text); - } - $color = $this->ndx_title_color; // Currently this is the same for X and Y labels - - // Check to see if the text fits in the available space, if requested. - if (isset($min_width) || isset($min_height)) { - list($width, $height) = $this->SizeText($font, $angle, $formatted_text); - if ((isset($min_width) && ($min_width - $width) < 2) - || (isset($min_height) && ($min_height - $height) < 2)) - return FALSE; - } - - $this->DrawText($font, $angle, $this->xtr($x_world) + $x_adjustment, - $this->ytr($y_world) + $y_adjustment, - $color, $formatted_text, $halign, $valign); - return TRUE; - } - - /* - * Draws the axis data label associated with a point in the plot. - * This is different from x_labels drawn by DrawXTicks() and care - * should be taken not to draw both, as they'd probably overlap. - * Calling of this function in DrawLines(), etc is decided after x_data_label_pos value. - * Leave the last parameter out, to avoid the drawing of vertical lines, no matter - * what the setting is (for plots that need it, like DrawSquared()) - */ - protected function DrawXDataLabel($xlab, $xpos, $row=FALSE) - { - $xlab = $this->FormatLabel('xd', $xlab); - - // Labels below the plot area - if ($this->x_data_label_pos == 'plotdown' || $this->x_data_label_pos == 'both') - $this->DrawText($this->fonts['x_label'], $this->x_data_label_angle, - $xpos, $this->plot_area[3] + $this->x_label_bot_offset, - $this->ndx_text_color, $xlab, 'center', 'top'); - - // Labels above the plot area - if ($this->x_data_label_pos == 'plotup' || $this->x_data_label_pos == 'both') - $this->DrawText($this->fonts['x_label'], $this->x_data_label_angle, - $xpos, $this->plot_area[1] - $this->x_label_top_offset, - $this->ndx_text_color, $xlab, 'center', 'bottom'); - - // $row=0 means this is the first row. $row=FALSE means don't do any rows. - if ($row !== FALSE && $this->draw_x_data_label_lines) - $this->DrawXDataLine($xpos, $row); - return TRUE; - } - - /* - * Draw a data label along the Y axis or side. - * This is used by horizontal plots. - */ - protected function DrawYDataLabel($ylab, $ypos) - { - $ylab = $this->FormatLabel('yd', $ylab); - - // Labels left of the plot area - if ($this->y_data_label_pos == 'plotleft' || $this->y_data_label_pos == 'both') - $this->DrawText($this->fonts['y_label'], $this->y_data_label_angle, - $this->plot_area[0] - $this->y_label_left_offset, $ypos, - $this->ndx_text_color, $ylab, 'right', 'center'); - - // Labels right of the plot area - if ($this->y_data_label_pos == 'plotright' || $this->y_data_label_pos == 'both') - $this->DrawText($this->fonts['y_label'], $this->y_data_label_angle, - $this->plot_area[2] + $this->y_label_right_offset, $ypos, - $this->ndx_text_color, $ylab, 'left', 'center'); - return TRUE; - } - - /* - * Draws Vertical lines from data points up and down. - * Which lines are drawn depends on the value of x_data_label_pos, - * and whether this is at all done or not, on draw_x_data_label_lines - * - * $xpos : position in pixels of the line. - * $row : index of the data row being drawn. - */ - protected function DrawXDataLine($xpos, $row) - { - // Sets the line style for IMG_COLOR_STYLED lines (grid) - if ($this->dashed_grid) { - $this->SetDashedStyle($this->ndx_light_grid_color); - $style = IMG_COLOR_STYLED; - } else { - $style = $this->ndx_light_grid_color; - } - - if ($this->x_data_label_pos == 'both') { - // Lines from the bottom up - ImageLine($this->img, $xpos, $this->plot_area[3], $xpos, $this->plot_area[1], $style); - } elseif ($this->x_data_label_pos == 'plotdown' && isset($this->data_max[$row])) { - // Lines from the bottom of the plot up to the max Y value at this X: - $ypos = $this->ytr($this->data_max[$row]); - ImageLine($this->img, $xpos, $ypos, $xpos, $this->plot_area[3], $style); - } elseif ($this->x_data_label_pos == 'plotup' && isset($this->data_min[$row])) { - // Lines from the top of the plot down to the min Y value at this X: - $ypos = $this->ytr($this->data_min[$row]); - ImageLine($this->img, $xpos, $this->plot_area[1], $xpos, $ypos, $style); - } - return TRUE; - } - -///////////////////////////////////////////// -/////////////// LEGEND -///////////////////////////////////////////// - - /* - * Set text to display in the graph's legend. - * $which_leg : Array of strings for the complete legend, or a single string - * to be appended to the legend. - * Or NULL (or an empty array) to cancel the legend. - */ - function SetLegend($which_leg) - { - if (is_array($which_leg)) { // use array (or cancel, if empty array) - $this->legend = $which_leg; - } elseif (!is_null($which_leg)) { // append string - $this->legend[] = $which_leg; - } else { - $this->legend = ''; // Reinitialize to empty, meaning no legend. - } - return TRUE; - } - - /* - * Specifies the position of the legend's upper/leftmost corner, in pixel (device) coordinates. - * Both X and Y must be provided, or both omitted (or use NULL) to restore auto-positioning. - */ - function SetLegendPixels($which_x=NULL, $which_y=NULL) - { - return $this->SetLegendPosition(0, 0, 'image', 0, 0, $which_x, $which_y); - } - - /* - * Specifies the position of the legend's upper/leftmost corner, in world (data space) coordinates. - */ - function SetLegendWorld($which_x, $which_y) - { - return $this->SetLegendPosition(0, 0, 'world', $which_x, $which_y); - } - - /* - * Specifies the position of the legend. This includes SetLegendWorld(), SetLegendPixels(), and - * additional choices using relative coordinates, with optional pixel offset. - * $x, $y : Relative coordinates of a point on the legend box. (See below) - * $relative_to : What to position the legend relative to: 'image', 'plot', 'world', or 'title'. - * $x_base, $y_base : Base point for positioning. - * If $relative_to is 'world', then this is a world coordinate position. - * Otherwise, this is a relative coordinate position on the $relative_to element. - * $x_offset, $y_offset : Additional legend box offset in device coordinates (pixels). - * The legend is positioned so that point ($x,$y) is at ($x_base, $y_base). - * 'Relative coordinates' means: (0,0) is the upper left corner, and (1,1) is the lower right corner - * of the element (legend, image, plot, or title area), regardless of its size. These are floating - * point values, each usually in the range [0,1], but they can be negative or greater than 1. - * If any of x, y, x_offset, or y_offset are NULL, default legend positioning is restored. - */ - function SetLegendPosition($x, $y, $relative_to, $x_base, $y_base, $x_offset = 0, $y_offset = 0) - { - // Special case: NULL means restore the default positioning. - if (!isset($x, $y, $x_offset, $y_offset)) { - unset($this->legend_pos); - } else { - $mode = $this->CheckOption($relative_to, 'image, plot, title, world', __FUNCTION__); - if (empty($mode)) - return FALSE; - // Save all values for use by GetLegendPosition() - $this->legend_pos = compact('x', 'y', 'mode', 'x_base', 'y_base', 'x_offset', 'y_offset'); - } - return TRUE; - } - - /* - * Set legend text alignment, color box alignment, and style options. - * $text_align : Alignment of the text, 'left' or 'right'. - * $colorbox_align : Alignment of the color boxes, 'left', 'right', 'none', or missing/empty. - * If missing or empty, the same alignment as $text_align is used. Color box is positioned first. - */ - function SetLegendStyle($text_align, $colorbox_align = '') - { - $this->legend_text_align = $this->CheckOption($text_align, 'left, right', __FUNCTION__); - if (empty($colorbox_align)) - $this->legend_colorbox_align = $this->legend_text_align; - else - $this->legend_colorbox_align = $this->CheckOption($colorbox_align, 'left, right, none', - __FUNCTION__); - return ((boolean)$this->legend_text_align && (boolean)$this->legend_colorbox_align); - } - - /* - * Use color boxes or point shapes (for points and linepoints plots only) in the legend. - * $use_shapes : True to use point shapes, false to use color boxes. - */ - function SetLegendUseShapes($use_shapes) - { - $this->legend_use_shapes = (bool)$use_shapes; - return TRUE; - } - - /* - * Get legend sizing parameters. - * This is used internally by DrawLegend(), and also by the public GetLegendSize(). - * It returns information based on any SetLegend*() calls already made. It does not use - * legend position or data scaling, so it can be called before data scaling is set up. - * Returns an associative array with these entries describing legend sizing: - * 'width', 'height' : Overall legend box size in pixels. - * 'char_w', 'char_h' : Width and height of 'E' in legend text font. (Used to size color boxes) - * 'v_margin' : Inside margin for legend - * 'text_align', 'colorbox_align' : Same as the class variables, with default applied. - * 'draw_colorbox' : True if color boxes will be drawn. - * 'dot_height' : Height of color boxes (even if not drawn), for line spacing. - * 'colorbox_width' : Width of color boxes. - */ - protected function GetLegendSizeParams() - { - $font = &$this->fonts['legend']; // Shortcut to font info array - - // Find maximum legend label line width. - $max_width = 0; - foreach ($this->legend as $line) { - list($width, $unused) = $this->SizeText($font, 0, $line); - if ($width > $max_width) $max_width = $width; - } - - // Font parameters are used to size the color boxes: - $char_w = $font['width']; - $char_h = $font['height']; - $line_spacing = $this->GetLineSpacing($font); - - // Apply defaults to text alignment and colorbox alignment variables: - $text_align = isset($this->legend_text_align) ? $this->legend_text_align : 'right'; - $colorbox_align = isset($this->legend_colorbox_align) ? $this->legend_colorbox_align : 'right'; - $draw_colorbox = ($colorbox_align != 'none'); - - // Sizing parameters: - $v_margin = $char_h / 2; // Between vertical borders and labels - $dot_height = $char_h + $line_spacing; // Height of the color boxes (even if not drawn) - $colorbox_width = $char_w; // Base color box width - if (isset($this->legend_colorbox_width)) - $colorbox_width *= $this->legend_colorbox_width; // Adjustment to color box width - - // Calculate overall legend box width and height. - // Width is e.g.: "| space colorbox space text space |" where each space adds $char_w, - // and colorbox (if drawn) adds $char_w * its width adjustment. - if ($draw_colorbox) { - $width = $max_width + 3 * $char_w + $colorbox_width; - } else { - $width = $max_width + 2 * $char_w; - } - $height = $dot_height * count($this->legend) + 2 * $v_margin; - - return compact('width', 'height', 'char_w', 'char_h', 'v_margin', - 'text_align', 'colorbox_align', 'draw_colorbox', 'dot_height', 'colorbox_width'); - } - - /* - * Get legend box size. This can be used to adjust the plot margins, for example. - * Returns: Array of ($width, $height) of the legend box in pixels. - */ - function GetLegendSize() - { - $params = $this->GetLegendSizeParams(); - return array($params['width'], $params['height']); - } - - /* - * Get legend location in device coordinates. This is a helper for DrawLegend, and is only - * called if there is a legend. See SetLegendWorld(), SetLegendPixels(), SetLegendPosition(). - * $width, $height : Width and height of the legend box. - * Returns: coordinates of the upper left corner of the legend box as an array ($x, $y) - */ - protected function GetLegendPosition($width, $height) - { - // Extract variables set by SetLegend*(): $mode, $x, $y, $x_base, $y_base, $x_offset, $y_offset - if (isset($this->legend_pos['mode'])) - extract($this->legend_pos); - else - $mode = ''; // Default legend position mode. - - switch ($mode) { - - case 'plot': // SetLegendPosition with mode='plot', relative coordinates over plot area. - return array((int)($x_base * $this->plot_area_width - $x * $width) - + $this->plot_area[0] + $x_offset, - (int)($y_base * $this->plot_area_height - $y * $height) - + $this->plot_area[1] + $y_offset); - - case 'world': // User-defined position in world-coordinates (SetLegendWorld), using x_base, y_base - return array($this->xtr($x_base) + $x_offset - (int)($x * $width), - $this->ytr($y_base) + $y_offset - (int)($y * $height)); - - case 'image': // SetLegendPosition with mode='image', relative coordinates over image area. - // SetLegendPixels() uses this too, with x=y=0. - return array((int)($x_base * $this->image_width - $x * $width) + $x_offset, - (int)($y_base * $this->image_height - $y * $height) + $y_offset); - - case 'title': // SetLegendPosition with mode='title', relative to main title. - // Recalculate main title position/size, since CalcMargins does not save it. See DrawTitle() - list($title_width, $title_height) = $this->SizeText($this->fonts['title'], 0, $this->title_txt); - $title_x = (int)(($this->image_width - $title_width) / 2); - return array((int)($x_base * $title_width - $x * $width) + $title_x + $x_offset, - (int)($y_base * $title_height - $y * $height) + $this->title_offset + $y_offset); - - default: // If mode is unset (or invalid), use default position. - return array ($this->plot_area[2] - $width - $this->safe_margin, - $this->plot_area[1] + $this->safe_margin); - } - } - - /* - * Draws the graph legend - * This is called by DrawGraph only if $this->legend is not empty. - * Base code submitted by Marlin Viss - */ - protected function DrawLegend() - { - $font = &$this->fonts['legend']; // Shortcut to font info array - - // Calculate legend box sizing parameters: - // See GetLegendSizeParams() to see what variables are set by this. - extract($this->GetLegendSizeParams()); - - // Get legend box position: - list($box_start_x, $box_start_y) = $this->GetLegendPosition($width, $height); - $box_end_y = $box_start_y + $height; - $box_end_x = $box_start_x + $width; - - // Draw outer box - ImageFilledRectangle($this->img, $box_start_x, $box_start_y, $box_end_x, $box_end_y, - $this->ndx_bg_color); - ImageRectangle($this->img, $box_start_x, $box_start_y, $box_end_x, $box_end_y, - $this->ndx_grid_color); - - $color_index = 0; - $max_color_index = count($this->ndx_data_colors) - 1; - - // Calculate color box and text horizontal positions. - if (!$draw_colorbox) { - if ($text_align == 'left') - $x_pos = $box_start_x + $char_w; - else - $x_pos = $box_end_x - $char_w; - $dot_left_x = 0; // Not used directly if color boxes/shapes are off, but referenced below. - } elseif ($colorbox_align == 'left') { - $dot_left_x = $box_start_x + $char_w; - $dot_right_x = $dot_left_x + $colorbox_width; - if ($text_align == 'left') - $x_pos = $dot_right_x + $char_w; - else - $x_pos = $box_end_x - $char_w; - } else { // $colorbox_align == 'right' - $dot_right_x = $box_end_x - $char_w; - $dot_left_x = $dot_right_x - $colorbox_width; - if ($text_align == 'left') - $x_pos = $box_start_x + $char_w; - else - $x_pos = $dot_left_x - $char_w; - } - - // $y_pos is the bottom of each color box. $yc is the vertical center of the color box or - // the point shape (if drawn). The text is centered vertically on $yc. - $y_pos = $box_start_y + $v_margin + $dot_height; - $yc = (int)($y_pos - $dot_height / 2); - $xc = (int)($dot_left_x + $colorbox_width / 2); // Horizontal center for point shape if drawn - $shape_index = 0; // Shape number index, if drawing point shapes - - // Option to use point shapes rather than solid boxes. Disallow this if the shapes array - // has not been initialized (see CheckPointParams). Only works with 'points' or 'linepoints' plots. - $use_shapes = !empty($this->legend_use_shapes) && !empty($this->point_counts); - - foreach ($this->legend as $leg) { - // Draw text with requested alignment: - $this->DrawText($font, 0, $x_pos, $yc, $this->ndx_text_color, $leg, $text_align, 'center'); - if ($draw_colorbox) { - $y1 = $y_pos - $dot_height + 1; - $y2 = $y_pos - 1; - if ($use_shapes) { - // Draw a point shape in the data color - // If plot area background is on, use that as the shape background: - if ($this->draw_plot_area_background) { - ImageFilledRectangle($this->img, $dot_left_x, $y1, $dot_right_x, $y2, - $this->ndx_plot_bg_color); - } - // Draw the shape. DrawShape() takes shape_index modulo number of defined shapes. - $this->DrawShape($xc, $yc, $shape_index++, $this->ndx_data_colors[$color_index]); - } else { - // Draw color boxes: - ImageFilledRectangle($this->img, $dot_left_x, $y1, $dot_right_x, $y2, - $this->ndx_data_colors[$color_index]); - // Draw a rectangle around the box - ImageRectangle($this->img, $dot_left_x, $y1, $dot_right_x, $y2, $this->ndx_text_color); - } - } - $y_pos += $dot_height; - $yc += $dot_height; - if (++$color_index > $max_color_index) - $color_index = 0; - } - return TRUE; - } - -///////////////////////////////////////////// -//////////////////// PLOT DRAWING HELPERS -///////////////////////////////////////////// - - /* - * Get data color to use for plotting. - * $row, $idx : Index arguments for the current data point. - * &$vars : Variable storage. Caller makes an empty array, and this function uses it. - * &$data_color : Returned result - Color index for the data point. - * $extra : Extra info flag passed through to data color callback. - */ - protected function GetDataColor($row, $idx, &$vars, &$data_color, $extra = 0) - { - // Initialize or extract variables: - if (empty($vars)) { - $custom_color = (bool)$this->GetCallback('data_color'); - $num_data_colors = count($this->ndx_data_colors); - $vars = compact('custom_color', 'num_data_colors'); - } else { - extract($vars); - } - - // Select the colors. - if ($custom_color) { - $col_i = $this->DoCallback('data_color', $row, $idx, $extra); // Custom color index - $data_color = $this->ndx_data_colors[$col_i % $num_data_colors]; - } else { - $data_color = $this->ndx_data_colors[$idx]; - } - } - - /* - * Get data color and error bar color to use for plotting. - * $row, $idx : Index arguments for the current bar. - * &$vars : Variable storage. Caller makes an empty array, and this function uses it. - * &$data_color : Returned result - Color index for the data (bar fill) - * &$error_color : Returned result - Color index for the error bars - * $extra : Extra info flag passed through to data color callback. - */ - protected function GetDataErrorColors($row, $idx, &$vars, &$data_color, &$error_color, $extra = 0) - { - // Initialize or extract variables: - if (empty($vars)) { - $this->NeedErrorBarColors(); // This plot needs error bar colors. - $custom_color = (bool)$this->GetCallback('data_color'); - $num_data_colors = count($this->ndx_data_colors); - $num_error_colors = count($this->ndx_error_bar_colors); - $vars = compact('custom_color', 'num_data_colors', 'num_error_colors'); - } else { - extract($vars); - } - - // Select the colors. - if ($custom_color) { - $col_i = $this->DoCallback('data_color', $row, $idx, $extra); // Custom color index - $data_color = $this->ndx_data_colors[$col_i % $num_data_colors]; - $error_color = $this->ndx_error_bar_colors[$col_i % $num_error_colors]; - } else { - $data_color = $this->ndx_data_colors[$idx]; - $error_color = $this->ndx_error_bar_colors[$idx]; - } - } - - /* - * Get colors to use for a bar chart. There is a data color, and either a border color - * or a shading color (data dark color). - * $row, $idx : Index arguments for the current bar. - * &$vars : Variable storage. Caller makes an empty array, and this function uses it. - * &$data_color : Returned result - Color index for the data (bar fill). - * &$alt_color : Returned result - Color index for the shading or outline. - */ - protected function GetBarColors($row, $idx, &$vars, &$data_color, &$alt_color) - { - // Initialize or extract variables: - if (empty($vars)) { - if ($this->shading > 0) // This plot needs dark colors if shading is on. - $this->NeedDataDarkColors(); - $custom_color = (bool)$this->GetCallback('data_color'); - $num_data_colors = count($this->ndx_data_colors); - $num_border_colors = count($this->ndx_data_border_colors); - $vars = compact('custom_color', 'num_data_colors', 'num_border_colors'); - } else { - extract($vars); - } - - // Select the colors. - if ($custom_color) { - $col_i = $this->DoCallback('data_color', $row, $idx); // Custom color index - $i_data = $col_i % $num_data_colors; // Index for data colors and dark colors - $i_border = $col_i % $num_border_colors; // Index for data borders (if used) - } else { - $i_data = $i_border = $idx; - } - $data_color = $this->ndx_data_colors[$i_data]; - if ($this->shading > 0) { - $alt_color = $this->ndx_data_dark_colors[$i_data]; - } else { - $alt_color = $this->ndx_data_border_colors[$i_border]; - } - } - - /* - * Draw a shape (dot, point). This is the bottom half of DrawDot, and is also - * used by legend drawing. Unlike DrawDot this takes device coordinates. - * The list of supported shapes can also be found in SetPointShapes(). - * $x, $y - Device coordinates of the center of the shape - * $record - Index into point_shapes[] and point_sizes[]. This is taken modulo the array sizes. - * $color - Shape color to use. - */ - protected function DrawShape($x, $y, $record, $color) - { - $index = $record % $this->point_counts; - $point_size = $this->point_sizes[$index]; - $half_point = (int)($point_size / 2); - - $x1 = $x - $half_point; - $x2 = $x + $half_point; - $y1 = $y - $half_point; - $y2 = $y + $half_point; - - switch ($this->point_shapes[$index]) { - case 'halfline': - ImageLine($this->img, $x1, $y, $x, $y, $color); - break; - case 'line': - ImageLine($this->img, $x1, $y, $x2, $y, $color); - break; - case 'plus': - ImageLine($this->img, $x1, $y, $x2, $y, $color); - ImageLine($this->img, $x, $y1, $x, $y2, $color); - break; - case 'cross': - ImageLine($this->img, $x1, $y1, $x2, $y2, $color); - ImageLine($this->img, $x1, $y2, $x2, $y1, $color); - break; - case 'circle': - ImageArc($this->img, $x, $y, $point_size, $point_size, 0, 360, $color); - break; - case 'dot': - ImageFilledEllipse($this->img, $x, $y, $point_size, $point_size, $color); - break; - case 'diamond': - $arrpoints = array($x1, $y, $x, $y1, $x2, $y, $x, $y2); - ImageFilledPolygon($this->img, $arrpoints, 4, $color); - break; - case 'triangle': - $arrpoints = array($x1, $y, $x2, $y, $x, $y2); - ImageFilledPolygon($this->img, $arrpoints, 3, $color); - break; - case 'trianglemid': - $arrpoints = array($x1, $y1, $x2, $y1, $x, $y); - ImageFilledPolygon($this->img, $arrpoints, 3, $color); - break; - case 'yield': - $arrpoints = array($x1, $y1, $x2, $y1, $x, $y2); - ImageFilledPolygon($this->img, $arrpoints, 3, $color); - break; - case 'delta': - $arrpoints = array($x1, $y2, $x2, $y2, $x, $y1); - ImageFilledPolygon($this->img, $arrpoints, 3, $color); - break; - case 'star': - ImageLine($this->img, $x1, $y, $x2, $y, $color); - ImageLine($this->img, $x, $y1, $x, $y2, $color); - ImageLine($this->img, $x1, $y1, $x2, $y2, $color); - ImageLine($this->img, $x1, $y2, $x2, $y1, $color); - break; - case 'hourglass': - $arrpoints = array($x1, $y1, $x2, $y1, $x1, $y2, $x2, $y2); - ImageFilledPolygon($this->img, $arrpoints, 4, $color); - break; - case 'bowtie': - $arrpoints = array($x1, $y1, $x1, $y2, $x2, $y1, $x2, $y2); - ImageFilledPolygon($this->img, $arrpoints, 4, $color); - break; - case 'target': - ImageFilledRectangle($this->img, $x1, $y1, $x, $y, $color); - ImageFilledRectangle($this->img, $x, $y, $x2, $y2, $color); - ImageRectangle($this->img, $x1, $y1, $x2, $y2, $color); - break; - case 'box': - ImageRectangle($this->img, $x1, $y1, $x2, $y2, $color); - break; - case 'home': /* As in: "home plate" (baseball), also looks sort of like a house. */ - $arrpoints = array($x1, $y2, $x2, $y2, $x2, $y, $x, $y1, $x1, $y); - ImageFilledPolygon($this->img, $arrpoints, 5, $color); - break; - case 'up': - ImagePolygon($this->img, array($x, $y1, $x2, $y2, $x1, $y2), 3, $color); - break; - case 'down': - ImagePolygon($this->img, array($x, $y2, $x1, $y1, $x2, $y1), 3, $color); - break; - case 'none': /* Special case, no point shape here */ - break; - default: /* Also 'rect' */ - ImageFilledRectangle($this->img, $x1, $y1, $x2, $y2, $color); - break; - } - return TRUE; - } - - /* - * Draws a styled dot. Uses world coordinates. - * Note: DrawShape() does all the work. - */ - protected function DrawDot($x_world, $y_world, $record, $color) - { - return $this->DrawShape($this->xtr($x_world), $this->ytr($y_world), $record, $color); - } - - /* - * Draw a bar (or segment of a bar), with optional shading or border. - * This is used by the bar and stackedbar plots, vertical and horizontal. - * $x1, $y1 : One corner of the bar. - * $x2, $y2 : Other corner of the bar. - * $data_color : Color index to use for the bar fill. - * $alt_color : Color index to use for the shading (if shading is on), else for the border. - * Note the same color is NOT used for shading and border - just the same argument. - * See GetBarColors() for where these arguments come from. - * $shade_top : Shade the top? (Suppressed for downward stack segments except first.) - * $shade_side : Shade the right side? (Suppressed for leftward stack segments except first.) - * Only one of $shade_top or $shade_side can be FALSE. Both default to TRUE. - */ - protected function DrawBar($x1, $y1, $x2, $y2, $data_color, $alt_color, - $shade_top = TRUE, $shade_side = TRUE) - { - // Sort the points so x1,y1 is upper left and x2,y2 is lower right. This - // is needed in order to get the shading right, and imagerectangle may require it. - if ($x1 > $x2) { - $t = $x1; $x1 = $x2; $x2 = $t; - } - if ($y1 > $y2) { - $t = $y1; $y1 = $y2; $y2 = $t; - } - - // Draw the bar - ImageFilledRectangle($this->img, $x1, $y1, $x2, $y2, $data_color); - - // Draw a shade, or a border. - if (($shade = $this->shading) > 0) { - if ($shade_top && $shade_side) { - $npts = 6; - $pts = array($x1, $y1, $x1 + $shade, $y1 - $shade, $x2 + $shade, $y1 - $shade, - $x2 + $shade, $y2 - $shade, $x2, $y2, $x2, $y1); - } else { - $npts = 4; - if ($shade_top) { // Suppress side shading - $pts = array($x1, $y1, $x1 + $shade, $y1 - $shade, $x2 + $shade, $y1 - $shade, $x2, $y1); - } else { // Suppress top shading - $pts = array($x2, $y2, $x2, $y1, $x2 + $shade, $y1 - $shade, $x2 + $shade, $y2 - $shade); - } - } - ImageFilledPolygon($this->img, $pts, $npts, $alt_color); - } else { - ImageRectangle($this->img, $x1, $y1, $x2,$y2, $alt_color); - } - } - - /* - * Draw an Error Bar set. Used by DrawDotsError and DrawLinesError - */ - protected function DrawYErrorBar($x_world, $y_world, $error_height, $error_bar_type, $color) - { - $x1 = $this->xtr($x_world); - $y1 = $this->ytr($y_world); - $y2 = $this->ytr($y_world+$error_height) ; - - ImageSetThickness($this->img, $this->error_bar_line_width); - ImageLine($this->img, $x1, $y1 , $x1, $y2, $color); - if ($error_bar_type == 'tee') { - ImageLine($this->img, $x1-$this->error_bar_size, $y2, $x1+$this->error_bar_size, $y2, $color); - } - ImageSetThickness($this->img, 1); - return TRUE; - } - -///////////////////////////////////////////// -//////////////////// PLOT DRAWING -///////////////////////////////////////////// - - /* - * Draws a pie chart. Data is 'text-data', 'data-data', or 'text-data-single'. - * - * For text-data-single, the data array contains records with an ignored label, - * and one Y value. Each record defines a sector of the pie, as a portion of - * the sum of all Y values. - * - * For text-data and data-data, the data array contains records with an ignored label, - * an ignored X value (for data-data only), and N (N>=1) Y values per record. - * The pie chart will be produced with N segments. The relative size of the first - * sector of the pie is the sum of the first Y data value in each record, etc. - * - * Note: With text-data-single, the data labels could be used, but are not currently. - * - * If there are no valid data points > 0 at all, just draw nothing. It may seem more correct to - * raise an error, but all of the other plot types handle it this way implicitly. DrawGraph - * checks for an empty data array, but this is different: a non-empty data array with no Y values, - * or all Y=0. - */ - protected function DrawPieChart() - { - if (!$this->CheckDataType('text-data, text-data-single, data-data')) - return FALSE; - - // Allocate dark colors only if they will be used for shading. - if ($this->shading > 0) - $this->NeedDataDarkColors(); - - $xpos = $this->plot_area[0] + $this->plot_area_width/2; - $ypos = $this->plot_area[1] + $this->plot_area_height/2; - $diameter = min($this->plot_area_width, $this->plot_area_height); - $radius = $diameter/2; - - $num_slices = $this->data_columns; // See CheckDataArray which calculates this for us. - if ($num_slices < 1) return TRUE; // Give up early if there is no data at all. - $sumarr = array_fill(0, $num_slices, 0); - - if ($this->datatype_pie_single) { - // text-data-single: One data column per row, one pie slice per row. - for ($i = 0; $i < $num_slices; $i++) { - // $legend[$i] = $this->data[$i][0]; // Note: Labels are not used yet - if (is_numeric($this->data[$i][1])) - $sumarr[$i] = abs($this->data[$i][1]); - } - } else { - // text-data: Sum each column (skipping label), one pie slice per column. - // data-data: Sum each column (skipping X value and label), one pie slice per column. - $skip = ($this->datatype_implied) ? 1 : 2; // Leading values to skip in each row. - for ($i = 0; $i < $this->num_data_rows; $i++) { - for ($j = $skip; $j < $this->num_recs[$i]; $j++) { - if (is_numeric($this->data[$i][$j])) - $sumarr[$j-$skip] += abs($this->data[$i][$j]); - } - } - } - - $total = array_sum($sumarr); - - if ($total == 0) { - // There are either no valid data points, or all are 0. - // See top comment about why not to make this an error. - return TRUE; - } - - if ($this->shading) { - $diam2 = $diameter / 2; - } else { - $diam2 = $diameter; - } - $max_data_colors = count($this->ndx_data_colors); - - // Use the Y label format precision, with default value: - if (isset($this->label_format['y']['precision'])) - $precision = $this->label_format['y']['precision']; - else - $precision = 1; - - for ($h = $this->shading; $h >= 0; $h--) { - $color_index = 0; - $start_angle = 0; - $end_angle = 0; - for ($j = 0; $j < $num_slices; $j++) { - $val = $sumarr[$j]; - - // For shaded pies: the last one (at the top of the "stack") has a brighter color: - if ($h == 0) - $slicecol = $this->ndx_data_colors[$color_index]; - else - $slicecol = $this->ndx_data_dark_colors[$color_index]; - - $label_txt = $this->number_format(($val / $total * 100), $precision) . '%'; - $val = 360 * ($val / $total); - - // NOTE that imagefilledarc measures angles CLOCKWISE (go figure why), - // so the pie chart would start clockwise from 3 o'clock, would it not be - // for the reversal of start and end angles in imagefilledarc() - // Also note ImageFilledArc only takes angles in integer degrees, and if the - // the start and end angles match then you get a full circle not a zero-width - // pie. This is bad. So skip any zero-size wedge. On the other hand, we cannot - // let cumulative error from rounding to integer result in missing wedges. So - // keep the running total as a float, and round the angles. It should not - // be necessary to check that the last wedge ends at 360 degrees. - $start_angle = $end_angle; - $end_angle += $val; - // This method of conversion to integer - truncate after reversing it - was - // chosen to match the implicit method of PHPlot<=5.0.4 to get the same slices. - $arc_start_angle = (int)(360 - $start_angle); - $arc_end_angle = (int)(360 - $end_angle); - - if ($arc_start_angle > $arc_end_angle) { - $mid_angle = deg2rad($end_angle - ($val / 2)); - - // Draw the slice - ImageFilledArc($this->img, $xpos, $ypos+$h, $diameter, $diam2, - $arc_end_angle, $arc_start_angle, - $slicecol, IMG_ARC_PIE); - - // Draw the labels only once - if ($h == 0) { - // Draw the outline - if (! $this->shading) - ImageFilledArc($this->img, $xpos, $ypos+$h, $diameter, $diam2, - $arc_end_angle, $arc_start_angle, $this->ndx_grid_color, - IMG_ARC_PIE | IMG_ARC_EDGED |IMG_ARC_NOFILL); - - // The '* 1.2' trick is to get labels out of the pie chart so there are more - // chances they can be seen in small sectors. - $label_x = $xpos + ($diameter * 1.2 * cos($mid_angle)) * $this->label_scale_position; - $label_y = $ypos+$h - ($diam2 * 1.2 * sin($mid_angle)) * $this->label_scale_position; - - $this->DrawText($this->fonts['generic'], 0, $label_x, $label_y, $this->ndx_grid_color, - $label_txt, 'center', 'center'); - } - } - if (++$color_index >= $max_data_colors) - $color_index = 0; - } // end for - } // end for - return TRUE; - } - - /* - * Draw the points and errors bars for an error plot of types points and linepoints - * Supports only data-data-error format, with each row of the form - * array("title", x, y1, error1+, error1-, y2, error2+, error2-, ...) - * This is called from DrawDots, with data type already checked. - * $paired is true for linepoints error plots, to make sure elements are - * only drawn once. If true, data labels are drawn by DrawLinesError, and error - * bars are drawn by DrawDotsError. (This choice is for backwards compatibility.) - */ - protected function DrawDotsError($paired = FALSE) - { - // Adjust the point shapes and point sizes arrays: - $this->CheckPointParams(); - - $gcvars = array(); // For GetDataErrorColors, which initializes and uses this. - // Special flag for data color callback to indicate the 'points' part of 'linepoints': - $alt_flag = $paired ? 1 : 0; - - for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) { - $record = 1; // Skip record #0 (title) - - $x_now = $this->data[$row][$record++]; // Read it, advance record index - - $x_now_pixels = $this->xtr($x_now); // Absolute coordinates. - - // Draw X Data labels? - if ($this->x_data_label_pos != 'none' && !$paired) - $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels, $row); - - // Now go for Y, E+, E- - for ($idx = 0; $record < $this->num_recs[$row]; $idx++) { - if (is_numeric($this->data[$row][$record])) { // Allow for missing Y data - - // Select the colors: - $this->GetDataErrorColors($row, $idx, $gcvars, $data_color, $error_color, $alt_flag); - - // Y: - $y_now = $this->data[$row][$record++]; - $this->DrawDot($x_now, $y_now, $idx, $data_color); - - // Error + - $val = $this->data[$row][$record++]; - $this->DrawYErrorBar($x_now, $y_now, $val, $this->error_bar_shape, $error_color); - // Error - - $val = $this->data[$row][$record++]; - $this->DrawYErrorBar($x_now, $y_now, -$val, $this->error_bar_shape, $error_color); - } else { - $record += 3; // Skip over missing Y and its error values - } - } - } - return TRUE; - } - - /* - * Draw a points plot, or the points for a linepoints plot - * Data format can be text-data (label, y1, y2, ...) or data-data (label, x, y1, y2, ...) - * Points plot with error bars (data-data-error format) is redirected to DrawDotsError. - * $paired is true for linepoints plots, to make sure elements are only drawn once. - */ - protected function DrawDots($paired = FALSE) - { - if (!$this->CheckDataType('text-data, data-data, data-data-error')) - return FALSE; - if ($this->datatype_error_bars) - return $this->DrawDotsError($paired); // Redirect for points+errorbars plot - - // Adjust the point shapes and point sizes arrays: - $this->CheckPointParams(); - - $gcvars = array(); // For GetDataColor, which initializes and uses this. - // Special flag for data color callback to indicate the 'points' part of 'linepoints': - $alt_flag = $paired ? 1 : 0; - - // Data Value Labels? (Skip if doing the points from a linepoints plot) - $do_dvls = !$paired && $this->CheckDataValueLabels($this->y_data_label_pos, - $dvl_x_off, $dvl_y_off, $dvl_h_align, $dvl_v_align); - - for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) { - $rec = 1; // Skip record #0 (data label) - - if ($this->datatype_implied) // Implied X values? - $x_now = 0.5 + $cnt++; // Place text-data at X = 0.5, 1.5, 2.5, etc... - else - $x_now = $this->data[$row][$rec++]; // Read it, advance record index - - $x_now_pixels = $this->xtr($x_now); - - // Draw X Data labels? - if (!$paired && $this->x_data_label_pos != 'none') - $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels, $row); - - // Proceed with Y values - for ($idx = 0;$rec < $this->num_recs[$row]; $rec++, $idx++) { - if (is_numeric($this->data[$row][$rec])) { // Allow for missing Y data - $y_now = (double)$this->data[$row][$rec]; - - // Select the color: - $this->GetDataColor($row, $idx, $gcvars, $data_color, $alt_flag); - // Draw the marker: - $this->DrawDot($x_now, $y_now, $idx, $data_color); - - // Draw data value labels? - if ($do_dvls) { - $this->DrawDataValueLabel('y', $x_now, $y_now, $y_now, $dvl_h_align, $dvl_v_align, - $dvl_x_off, $dvl_y_off); - } - } - } - } - return TRUE; - } - - /* - * Draw a Thin Bar Line plot, also known as an Impulse plot. - * A clean, fast routine for when you just want charts like stock volume charts. - * Supports data-data and text-data formats for vertical plots, - * and data-data-yx and text-data-yx for horizontal plots. - * Note that although this plot type supports multiple data sets, it rarely makes - * sense to have more than 1, because the lines will overlay. - * This one function does both vertical and horizontal plots. "iv" is used for the - * independent variable (X for vertical plots, Y for horizontal) and "dv" is used - * for the dependent variable (Y for vertical plots, X for horizontal). - */ - protected function DrawThinBarLines() - { - if (!$this->CheckDataType('text-data, data-data, text-data-yx, data-data-yx')) - return FALSE; - - $gcvars = array(); // For GetDataColor, which initializes and uses this. - - for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) { - $rec = 1; // Skip record #0 (data label) - - if ($this->datatype_implied) // Implied independent variable values? - $iv_now = 0.5 + $cnt++; // Place text-data at 0.5, 1.5, 2.5, etc... - else - $iv_now = $this->data[$row][$rec++]; // Read it, advance record index - - if ($this->datatype_swapped_xy) { - $y_now_pixels = $this->ytr($iv_now); - // Draw Y Data labels? - if ($this->y_data_label_pos != 'none') - $this->DrawYDataLabel($this->data[$row][0], $y_now_pixels); - } else { - $x_now_pixels = $this->xtr($iv_now); - // Draw X Data labels? - if ($this->x_data_label_pos != 'none') - $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels); - } - - // Proceed with dependent values - for ($idx = 0; $rec < $this->num_recs[$row]; $rec++, $idx++) { - if (is_numeric($this->data[$row][$rec])) { // Allow for missing data - $dv = $this->data[$row][$rec]; - ImageSetThickness($this->img, $this->line_widths[$idx]); - - // Select the color: - $this->GetDataColor($row, $idx, $gcvars, $data_color); - - if ($this->datatype_swapped_xy) { - // Draw a line from user defined y axis position right (or left) to xtr($dv) - ImageLine($this->img, $this->y_axis_x_pixels, $y_now_pixels, - $this->xtr($dv), $y_now_pixels, $data_color); - } else { - // Draw a line from user defined x axis position up (or down) to ytr($dv) - ImageLine($this->img, $x_now_pixels, $this->x_axis_y_pixels, - $x_now_pixels, $this->ytr($dv), $data_color); - } - } - } - } - - ImageSetThickness($this->img, 1); - return TRUE; - } - - /* - * Draw an 'area' or 'stacked area' plot. - * Both of these fill the area between lines, but in the stacked area graph the Y values - * are accumulated for each X, same as stacked bars. In the regular area graph, the areas - * are filled in order from the X axis up to each Y (so the Y values for each X need to be - * in decreasing order in this case). - * Data format can be text-data (label, y1, y2, ...) or data-data (label, x, y1, y2, ...) - * Notes: - * All Y values must be >= 0. (If any Y<0 the absolute value is used.) - * Missing data points are NOT handled. (They are counted as 0.) - * All rows must have the same number of Y points, or an error image will be produced. - */ - protected function DrawArea($do_stacked = FALSE) - { - if (!$this->CheckDataType('text-data, data-data')) - return FALSE; - - $n = $this->num_data_rows; // Number of X values - - // These arrays store the device X and Y coordinates for all lines: - $xd = array(); - $yd = array(); - - // Make sure each row has the same number of values. Note records_per_group is max(num_recs). - if ($this->records_per_group != min($this->num_recs)) { - return $this->PrintError("DrawArea(): Data array must contain the same number" - . " of Y values for each X"); - } - - // Calculate the Y value for each X, and store the device - // coordinates into the xd and yd arrays. - // For stacked area plots, the Y values accumulate. - for ($row = 0; $row < $n; $row++) { - $rec = 1; // Skip record #0 (data label) - - if ($this->datatype_implied) // Implied X values? - $x_now = 0.5 + $row; // Place text-data at X = 0.5, 1.5, 2.5, etc... - else - $x_now = $this->data[$row][$rec++]; // Read it, advance record index - - $x_now_pixels = $this->xtr($x_now); - - if ($this->x_data_label_pos != 'none') // Draw X Data labels? - $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels); - - // Store the X value. - // There is an artificial Y value at the axis. For 'area' it goes - // at the end; for stackedarea it goes before the start. - $xd[$row] = $x_now_pixels; - $yd[$row] = array(); - if ($do_stacked) - $yd[$row][] = $this->x_axis_y_pixels; - - // Store the Y values for this X. - // All Y values are clipped to the x axis which should be zero but can be moved. - $y = 0; - while ($rec < $this->records_per_group) { - if (is_numeric($this->data[$row][$rec])) { // Treat missing values as 0. - $y += abs($this->data[$row][$rec]); - } - $yd[$row][] = $this->ytr(max($this->x_axis_position, $y)); - if (!$do_stacked) $y = 0; - $rec++; - } - - if (!$do_stacked) - $yd[$row][] = $this->x_axis_y_pixels; - } - - // Now draw the filled polygons. - // Note data_columns is the number of Y points (columns excluding label and X), and the - // number of entries in the yd[] arrays is data_columns+1. - $prev_row = 0; - for ($row = 1; $row <= $this->data_columns; $row++) { // 1 extra for X axis artificial row - $pts = array(); - // Previous data set forms top (for area) or bottom (for stackedarea): - for ($j = 0; $j < $n; $j++) { - $pts[] = $xd[$j]; - $pts[] = $yd[$j][$prev_row]; - } - // Current data set forms bottom (for area) or top (for stackedarea): - for ($j = $n- 1; $j >= 0; $j--) { - $pts[] = $xd[$j]; - $pts[] = $yd[$j][$row]; - } - // Draw it: - ImageFilledPolygon($this->img, $pts, $n * 2, $this->ndx_data_colors[$prev_row]); - - $prev_row = $row; - } - return TRUE; - } - - /* - * Draw a line plot, or the lines part of a linepoints plot - * Data format can be text-data (label, y1, y2, ...) or data-data (label, x, y1, y2, ...) - * Line plot with error bars (data-data-error format) is redirected to DrawLinesError. - * $paired is true for linepoints plots, to make sure elements are only drawn once. - */ - protected function DrawLines($paired = FALSE) - { - if (!$this->CheckDataType('text-data, data-data, data-data-error')) - return FALSE; - if ($this->datatype_error_bars) - return $this->DrawLinesError($paired); // Redirect for lines+errorbar plot - - // Flag array telling if the current point is valid, one element per plot line. - // If start_lines[i] is true, then (lastx[i], lasty[i]) is the previous point. - $start_lines = array_fill(0, $this->data_columns, FALSE); - - $gcvars = array(); // For GetDataColor, which initializes and uses this. - - // Data Value Labels? - $do_dvls = $this->CheckDataValueLabels($this->y_data_label_pos, - $dvl_x_off, $dvl_y_off, $dvl_h_align, $dvl_v_align); - - for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) { - $record = 1; // Skip record #0 (data label) - - if ($this->datatype_implied) // Implied X values? - $x_now = 0.5 + $cnt++; // Place text-data at X = 0.5, 1.5, 2.5, etc... - else - $x_now = $this->data[$row][$record++]; // Read it, advance record index - - $x_now_pixels = $this->xtr($x_now); // Absolute coordinates - - if ($this->x_data_label_pos != 'none') // Draw X Data labels? - $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels, $row); - - for ($idx = 0; $record < $this->num_recs[$row]; $record++, $idx++) { - if (($line_style = $this->line_styles[$idx]) == 'none') - continue; //Allow suppressing entire line, useful with linepoints - if (is_numeric($this->data[$row][$record])) { //Allow for missing Y data - $y_now = (double)$this->data[$row][$record]; - $y_now_pixels = $this->ytr($y_now); - - if ($start_lines[$idx]) { - // Set line width, revert it to normal at the end - ImageSetThickness($this->img, $this->line_widths[$idx]); - - // Select the color: - $this->GetDataColor($row, $idx, $gcvars, $data_color); - - if ($line_style == 'dashed') { - $this->SetDashedStyle($data_color); - $data_color = IMG_COLOR_STYLED; - } - ImageLine($this->img, $x_now_pixels, $y_now_pixels, - $lastx[$idx], $lasty[$idx], $data_color); - } - - // Draw data value labels? - if ($do_dvls) { - $this->DrawDataValueLabel('y', $x_now, $y_now, $y_now, $dvl_h_align, $dvl_v_align, - $dvl_x_off, $dvl_y_off); - } - - $lasty[$idx] = $y_now_pixels; - $lastx[$idx] = $x_now_pixels; - $start_lines[$idx] = TRUE; - } elseif ($this->draw_broken_lines) { // Y data missing, leave a gap. - $start_lines[$idx] = FALSE; - } - } // end for - } // end for - - ImageSetThickness($this->img, 1); // Revert to original state for lines to be drawn later. - return TRUE; - } - - /* - * Draw lines with error bars for an error plot of types lines and linepoints - * Supports only data-data-error format, with each row of the form - * array("title", x, y1, error1+, error1-, y2, error2+, error2-, ...) - * This is called from DrawLines, with data type already checked. - * $paired is true for linepoints error plots, to make sure elements are - * only drawn once. If true, data labels are drawn by DrawLinesError, and error - * bars are drawn by DrawDotsError. (This choice is for backwards compatibility.) - */ - protected function DrawLinesError($paired = FALSE) - { - $start_lines = array_fill(0, $this->data_columns, FALSE); - - $gcvars = array(); // For GetDataErrorColors, which initializes and uses this. - - for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) { - $record = 1; // Skip record #0 (data label) - - $x_now = $this->data[$row][$record++]; // Read X value, advance record index - - $x_now_pixels = $this->xtr($x_now); // Absolute coordinates. - - if ($this->x_data_label_pos != 'none') // Draw X Data labels? - $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels, $row); - - // Now go for Y, E+, E- - for ($idx = 0; $record < $this->num_recs[$row]; $idx++) { - if (($line_style = $this->line_styles[$idx]) == 'none') - continue; //Allow suppressing entire line, useful with linepoints - if (is_numeric($this->data[$row][$record])) { // Allow for missing Y data - - // Select the colors: - $this->GetDataErrorColors($row, $idx, $gcvars, $data_color, $error_color); - - // Y - $y_now = $this->data[$row][$record++]; - $y_now_pixels = $this->ytr($y_now); - - if ($start_lines[$idx]) { - ImageSetThickness($this->img, $this->line_widths[$idx]); - - if ($line_style == 'dashed') { - $this->SetDashedStyle($data_color); - $data_color = IMG_COLOR_STYLED; - } - ImageLine($this->img, $x_now_pixels, $y_now_pixels, - $lastx[$idx], $lasty[$idx], $data_color); - } - - if ($paired) { - $record += 2; // Skip error bars - done in the 'points' part of 'linepoints'. - } else { - // Error+ - $val = $this->data[$row][$record++]; - $this->DrawYErrorBar($x_now, $y_now, $val, $this->error_bar_shape, $error_color); - - // Error- - $val = $this->data[$row][$record++]; - $this->DrawYErrorBar($x_now, $y_now, -$val, $this->error_bar_shape, $error_color); - } - - // Update indexes: - $start_lines[$idx] = TRUE; // Tells us if we already drew the first column of points, - // thus having $lastx and $lasty ready for the next column. - $lastx[$idx] = $x_now_pixels; - $lasty[$idx] = $y_now_pixels; - - } else { - $record += 3; // Skip over missing Y and its error values - if ($this->draw_broken_lines) { - $start_lines[$idx] = FALSE; - } - } - } // end for - } // end for - - ImageSetThickness($this->img, 1); // Revert to original state for lines to be drawn later. - return TRUE; - } - - /* - * Draw a Lines+Points plot (linepoints). - * This just uses DrawLines and DrawDots. They handle the error-bar case themselves. - */ - protected function DrawLinePoints() - { - // This check is redundant, as DrawLines and DrawDots do it, but left here as insurance. - if (!$this->CheckDataType('text-data, data-data, data-data-error')) - return FALSE; - $this->DrawLines(TRUE); - $this->DrawDots(TRUE); - return TRUE; - } - - /* - * Draw a Squared Line plot. - * Data format can be text-data (label, y1, y2, ...) or data-data (label, x, y1, y2, ...) - * This is based on DrawLines(), with one more line drawn for each point. - */ - protected function DrawSquared() - { - if (!$this->CheckDataType('text-data, data-data')) - return FALSE; - - // Flag array telling if the current point is valid, one element per plot line. - // If start_lines[i] is true, then (lastx[i], lasty[i]) is the previous point. - $start_lines = array_fill(0, $this->data_columns, FALSE); - - $gcvars = array(); // For GetDataColor, which initializes and uses this. - - // Data Value Labels? - $do_dvls = $this->CheckDataValueLabels($this->y_data_label_pos, - $dvl_x_off, $dvl_y_off, $dvl_h_align, $dvl_v_align); - - for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) { - $record = 1; // Skip record #0 (data label) - - if ($this->datatype_implied) // Implied X values? - $x_now = 0.5 + $cnt++; // Place text-data at X = 0.5, 1.5, 2.5, etc... - else - $x_now = $this->data[$row][$record++]; // Read it, advance record index - - $x_now_pixels = $this->xtr($x_now); // Absolute coordinates - - if ($this->x_data_label_pos != 'none') // Draw X Data labels? - $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels); // notice there is no last param. - - // Draw Lines - for ($idx = 0; $record < $this->num_recs[$row]; $record++, $idx++) { - if (is_numeric($this->data[$row][$record])) { // Allow for missing Y data - $y_now = (double)$this->data[$row][$record]; - $y_now_pixels = $this->ytr($y_now); - - if ($start_lines[$idx]) { - // Set line width, revert it to normal at the end - ImageSetThickness($this->img, $this->line_widths[$idx]); - - // Select the color: - $this->GetDataColor($row, $idx, $gcvars, $data_color); - - if ($this->line_styles[$idx] == 'dashed') { - $this->SetDashedStyle($data_color); - $data_color = IMG_COLOR_STYLED; - } - ImageLine($this->img, $lastx[$idx], $lasty[$idx], - $x_now_pixels, $lasty[$idx], $data_color); - ImageLine($this->img, $x_now_pixels, $lasty[$idx], - $x_now_pixels, $y_now_pixels, $data_color); - } - - // Draw data value labels? - if ($do_dvls) { - $this->DrawDataValueLabel('y', $x_now, $y_now, $y_now, $dvl_h_align, $dvl_v_align, - $dvl_x_off, $dvl_y_off); - } - - $lastx[$idx] = $x_now_pixels; - $lasty[$idx] = $y_now_pixels; - $start_lines[$idx] = TRUE; - } elseif ($this->draw_broken_lines) { // Y data missing, leave a gap. - $start_lines[$idx] = FALSE; - } - } - } // end while - - ImageSetThickness($this->img, 1); - return TRUE; - } - - /* - * Draw a Bar chart - * Supports text-data format, with each row in the form array(label, y1, y2, y3, ...) - * Horizontal bars (text-data-yx format) are sent to DrawHorizBars() instead. - */ - protected function DrawBars() - { - if (!$this->CheckDataType('text-data, text-data-yx')) - return FALSE; - if ($this->datatype_swapped_xy) - return $this->DrawHorizBars(); - $this->CalcBarWidths(FALSE, TRUE); // Calculate bar widths for unstacked, vertical - - // This is the X offset from the bar group's label center point to the left side of the first bar - // in the group. See also CalcBarWidths above. - $x_first_bar = ($this->data_columns * $this->record_bar_width) / 2 - $this->bar_adjust_gap; - - $gcvars = array(); // For GetBarColors, which initializes and uses this. - - for ($row = 0; $row < $this->num_data_rows; $row++) { - $record = 1; // Skip record #0 (data label) - - $x_now_pixels = $this->xtr(0.5 + $row); // Place text-data at X = 0.5, 1.5, 2.5, etc... - - if ($this->x_data_label_pos != 'none') // Draw X Data labels? - $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels); - - // Lower left X of first bar in the group: - $x1 = $x_now_pixels - $x_first_bar; - - // Draw the bars in the group: - for ($idx = 0; $record < $this->num_recs[$row]; $record++, $idx++) { - if (is_numeric($this->data[$row][$record])) { // Allow for missing Y data - $y = $this->data[$row][$record]; - $x2 = $x1 + $this->actual_bar_width; - - if (($upgoing_bar = $y >= $this->x_axis_position)) { - $y1 = $this->ytr($y); - $y2 = $this->x_axis_y_pixels; - } else { - $y1 = $this->x_axis_y_pixels; - $y2 = $this->ytr($y); - } - - // Select the colors: - $this->GetBarColors($row, $idx, $gcvars, $data_color, $alt_color); - - // Draw the bar, and the shade or border: - $this->DrawBar($x1, $y1, $x2, $y2, $data_color, $alt_color); - - // Draw optional data labels above the bars (or below, for negative values). - if ( $this->y_data_label_pos == 'plotin') { - if ($upgoing_bar) { - $v_align = 'bottom'; - $y_offset = -5 - $this->shading; - } else { - $v_align = 'top'; - $y_offset = 2; - } - $this->DrawDataValueLabel('y', $row+0.5, $y, $y, 'center', $v_align, - ($idx + 0.5) * $this->record_bar_width - $x_first_bar, $y_offset); - } - } - // Step to next bar in group: - $x1 += $this->record_bar_width; - } // end for - } // end for - return TRUE; - } - - /* - * Draw a Horizontal Bar chart - * Supports only text-data-yx format, with each row in the form array(label, x1, x2, x3, ...) - * Note that the data values are X not Y, and the bars are drawn horizontally. - * This is called from DrawBars, which has already checked the data type. - */ - protected function DrawHorizBars() - { - $this->CalcBarWidths(FALSE, FALSE); // Calculate bar widths for unstacked, vertical - - // This is the Y offset from the bar group's label center point to the bottom of the first bar - // in the group. See also CalcBarWidths above. - $y_first_bar = ($this->data_columns * $this->record_bar_width) / 2 - $this->bar_adjust_gap; - - $gcvars = array(); // For GetBarColors, which initializes and uses this. - - for ($row = 0; $row < $this->num_data_rows; $row++) { - $record = 1; // Skip record #0 (data label) - - $y_now_pixels = $this->ytr(0.5 + $row); // Place bars at Y=0.5, 1.5, 2.5, etc... - - if ($this->y_data_label_pos != 'none') // Draw Y Data Labels? - $this->DrawYDataLabel($this->data[$row][0], $y_now_pixels); - - // Lower left Y of first bar in the group: - $y1 = $y_now_pixels + $y_first_bar; - - // Draw the bars in the group: - for ($idx = 0; $record < $this->num_recs[$row]; $record++, $idx++) { - if (is_numeric($this->data[$row][$record])) { // Allow for missing X data - $x = $this->data[$row][$record]; - $y2 = $y1 - $this->actual_bar_width; - - if (($rightwards_bar = $x >= $this->y_axis_position)) { - $x1 = $this->xtr($x); - $x2 = $this->y_axis_x_pixels; - } else { - $x1 = $this->y_axis_x_pixels; - $x2 = $this->xtr($x); - } - - // Select the colors: - $this->GetBarColors($row, $idx, $gcvars, $data_color, $alt_color); - - // Draw the bar, and the shade or border: - $this->DrawBar($x1, $y1, $x2, $y2, $data_color, $alt_color); - - // Draw optional data labels to the right of the bars (or left, if the bar - // goes left of the Y axis line). - if ($this->x_data_label_pos == 'plotin') { - if ($rightwards_bar) { - $h_align = 'left'; - $x_offset = 5 + $this->shading; - } else { - $h_align = 'right'; - $x_offset = -2; - } - $this->DrawDataValueLabel('x', $x, $row+0.5, $x, $h_align, 'center', - $x_offset, $y_first_bar - ($idx + 0.5) * $this->record_bar_width); - } - - } - // Step to next bar in group: - $y1 -= $this->record_bar_width; - } // end for - } // end for - - return TRUE; - } - - /* - * Draw a Stacked Bar chart - * Supports text-data format, with each row in the form array(label, y1, y2, y3, ...) - * Horizontal stacked bars (text-data-yx format) are sent to DrawHorizStackedBars() instead. - * Original stacked bars idea by Laurent Kruk < lolok at users.sourceforge.net > - */ - protected function DrawStackedBars() - { - if (!$this->CheckDataType('text-data, text-data-yx')) - return FALSE; - if ($this->datatype_swapped_xy) - return $this->DrawHorizStackedBars(); - $this->CalcBarWidths(TRUE, TRUE); // Calculate bar widths for stacked, vertical - - // This is the X offset from the bar's label center point to the left side of the bar. - $x_first_bar = $this->record_bar_width / 2 - $this->bar_adjust_gap; - - $gcvars = array(); // For GetBarColors, which initializes and uses this. - - // Determine if any data labels are on: - $data_labels_within = ($this->y_data_label_pos == 'plotstack'); - $data_labels_end = $data_labels_within || ($this->y_data_label_pos == 'plotin'); - $data_label_y_offset = -5 - $this->shading; // For upward labels only. - - for ($row = 0; $row < $this->num_data_rows; $row++) { - $record = 1; // Skip record #0 (data label) - - $x_now_pixels = $this->xtr(0.5 + $row); // Place text-data at X = 0.5, 1.5, 2.5, etc... - - if ($this->x_data_label_pos != 'none') // Draw X Data labels? - $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels); - - // Determine bar direction based on 1st non-zero value. Note the bar direction is - // based on zero, not the axis value. - $n_recs = $this->num_recs[$row]; - $upward = TRUE; // Initialize this for the case of all segments = 0 - for ($i = $record; $i < $n_recs; $i++) { - if (is_numeric($this_y = $this->data[$row][$i]) && $this_y != 0) { - $upward = ($this_y > 0); - break; - } - } - - $x1 = $x_now_pixels - $x_first_bar; // Left X of bars in this stack - $x2 = $x1 + $this->actual_bar_width; // Right X of bars in this stack - $wy1 = 0; // World coordinates Y1, current sum of values - $wy2 = $this->x_axis_position; // World coordinates Y2, last drawn value - - // Draw bar segments and labels in this stack. - $first = TRUE; - for ($idx = 0; $record < $n_recs; $record++, $idx++) { - - // Skip missing Y values. Process Y=0 values due to special case of moved axis. - if (is_numeric($this_y = $this->data[$row][$record])) { - - $wy1 += $this_y; // Keep the running total for this bar stack - - // Draw nothing if this segment would not increase the bar height. - // Upward bars: draw if wy1>wy2. Downward bars: Draw if wy1ytr($wy1); // Convert to device coordinates. $y1 is outermost value. - $y2 = $this->ytr($wy2); // $y2 is innermost (closest to axis). - - // Select the colors: - $this->GetBarColors($row, $idx, $gcvars, $data_color, $alt_color); - - // Draw the bar, and the shade or border: - $this->DrawBar($x1, $y1, $x2, $y2, $data_color, $alt_color, - // Only shade the top for upward bars, or the first segment of downward bars: - $upward || $first, TRUE); - - // Draw optional data label for this bar segment just inside the end. - // Text value is the current Y, but position is the cumulative Y. - // The label is only drawn if it fits in the segment height |y2-y1|. - if ($data_labels_within) { - $this->DrawDataValueLabel('y', $row+0.5, $wy1, $this_y, - 'center', $upward ? 'top' : 'bottom', - 0, $upward ? 3 : -3, NULL, abs($y1 - $y2)); - } - // Mark the new end of the bar, conditional on segment height > 0. - $wy2 = $wy1; - $first = FALSE; - } - } - } // end for - - // Draw optional data label above the bar with the total value. - // Value is wy1 (total value), but position is wy2 (end of the bar stack). - // These differ only with wrong-direction segments, or a stack completely clipped by the axis. - if ($data_labels_end) { - $this->DrawDataValueLabel('y', $row+0.5, $wy2, $wy1, 'center', $upward ? 'bottom' : 'top', - 0, $upward ? $data_label_y_offset : 5); - } - } // end for - return TRUE; - } - - /* - * Draw a Horizontal Stacked Bar chart - * Supports only text-data-yx format, with each row in the form array(label, x1, x2, x3, ...) - * Note that the data values are X not Y, and the bars are drawn horizontally. - * This is called from DrawStackedBars, which has already checked the data type. - */ - protected function DrawHorizStackedBars() - { - $this->CalcBarWidths(TRUE, FALSE); // Calculate bar widths for stacked, horizontal - - // This is the Y offset from the bar's label center point to the bottom of the bar - $y_first_bar = $this->record_bar_width / 2 - $this->bar_adjust_gap; - - $gcvars = array(); // For GetBarColors, which initializes and uses this. - - // Determine if any data labels are on: - $data_labels_within = ($this->x_data_label_pos == 'plotstack'); - $data_labels_end = $data_labels_within || ($this->x_data_label_pos == 'plotin'); - $data_label_x_offset = 5 + $this->shading; // For rightward labels only - - for ($row = 0; $row < $this->num_data_rows; $row++) { - $record = 1; // Skip record #0 (data label) - - $y_now_pixels = $this->ytr(0.5 + $row); // Place bars at Y=0.5, 1.5, 2.5, etc... - - if ($this->y_data_label_pos != 'none') // Draw Y Data labels? - $this->DrawYDataLabel($this->data[$row][0], $y_now_pixels); - - // Determine bar direction based on 1st non-zero value. Note the bar direction is - // based on zero, not the axis value. - $n_recs = $this->num_recs[$row]; - $rightward = TRUE; // Initialize this for the case of all segments = 0 - for ($i = $record; $i < $n_recs; $i++) { - if (is_numeric($this_x = $this->data[$row][$i]) && $this_x != 0) { - $rightward = ($this_x > 0); - break; - } - } - - // Lower left and upper left Y of the bars in this stack: - $y1 = $y_now_pixels + $y_first_bar; // Lower Y of bars in this stack - $y2 = $y1 - $this->actual_bar_width; // Upper Y of bars in this stack - $wx1 = 0; // World coordinates X1, current sum of values - $wx2 = $this->y_axis_position; // World coordinates X2, last drawn value - - // Draw bar segments and labels in this stack. - $first = TRUE; - for ($idx = 0; $record < $this->num_recs[$row]; $record++, $idx++) { - - // Skip missing X values. Process Y=0 values due to special case of moved axis. - if (is_numeric($this_x = $this->data[$row][$record])) { - - $wx1 += $this_x; // Keep the running total for this bar stack - - // Draw nothing if this segment would not increase the bar length. - // Rightward bars: draw if wx1>wx2. Leftward bars: Draw if wx1xtr($wx1); // Convert to device coordinates. $x1 is outermost value. - $x2 = $this->xtr($wx2); // $x2 is innermost (closest to axis). - - // Select the colors: - $this->GetBarColors($row, $idx, $gcvars, $data_color, $alt_color); - - // Draw the bar, and the shade or border: - $this->DrawBar($x1, $y1, $x2, $y2, $data_color, $alt_color, - // Only shade the side for rightward bars, or the first segment of leftward bars: - TRUE, $rightward || $first); - // Draw optional data label for this bar segment just inside the end. - // Text value is the current X, but position is the cumulative X. - // The label is only drawn if it fits in the segment width |x2-x1|. - if ($data_labels_within) { - $this->DrawDataValueLabel('x', $wx1, $row+0.5, $this_x, - $rightward ? 'right' : 'left', 'center', - $rightward ? -3 : 3, 0, abs($x1 - $x2), NULL); - } - // Mark the new end of the bar, conditional on segment width > 0. - $wx2 = $wx1; - $first = FALSE; - } - } - } // end for - - // Draw optional data label right of the bar with the total value. - // Value is wx1 (total value), but position is wx2 (end of the bar stack). - // These differ only with wrong-direction segments, or a stack completely clipped by the axis. - if ($data_labels_end) { - $this->DrawDataValueLabel('x', $wx2, $row+0.5, $wx1, $rightward ? 'left' : 'right', 'center', - $rightward ? $data_label_x_offset : -5, 0); - } - } // end for - return TRUE; - } - - /* - * Draw a financial "Open/High/Low/Close" (OHLC) plot, including candlestick plots. - * Data format can be text-data (label, Yo, Yh, Yl, Yc) or data-data (label, X, Yo, Yh, Yl, Yc). - * Yo="Opening price", Yc="Closing price", Yl="Low price", Yh="High price". - * Each row must have exactly 4 Y values. No multiple data sets, no missing values. - * There are 3 subtypes, selected by $draw_candles and $always_fill. - * $draw_candles $always_fill Description: - * FALSE N/A A basic OHLC chart with a vertical line for price range, horizontal - * tick marks on left for opening price and right for closing price. - * TRUE FALSE A candlestick plot with filled body indicating close down, outline - * for closing up, and vertical wicks for low and high prices. - * TRUE TRUE A candlestick plot where the candle bodies are always filled. - * These map to 3 plot types per the $plots[] array. - * - * Data color usage: If closes down: If closes up or unchanged: - * Candlestick body, ohlc range line: color 0 color 1 - * Candlestick wicks, ohlc tick marks: color 2 color 3 - * There are three member variables that control the width (candlestick body or tick marks): - * ohlc_max_width, ohlc_min_width, ohlc_frac_width - * (There is no API to change them at this time.) - */ - protected function DrawOHLC($draw_candles, $always_fill = FALSE) - { - if (!$this->CheckDataType('text-data, data-data')) - return FALSE; - - // Assign name of GD function to draw candlestick bodies for stocks that close up. - $draw_body_close_up = $always_fill ? 'imagefilledrectangle' : 'imagerectangle'; - - // These 3 variables control the calculation of the half-width of the candle body, or length of - // the tick marks. This is scaled based on the plot density, but within tight limits. - $min_width = isset($this->ohlc_min_width) ? $this->ohlc_min_width : 2; - $max_width = isset($this->ohlc_max_width) ? $this->ohlc_max_width : 8; - $width_factor = isset($this->ohlc_frac_width) ? $this->ohlc_frac_width : 0.3; - $dw = max($min_width, min($max_width, - (int)($width_factor * $this->plot_area_width / $this->num_data_rows))); - - // Get line widths to use: index 0 for body/stroke, 1 for wick/tick. - list($body_thickness, $wick_thickness) = $this->line_widths; - - $gcvars = array(); // For GetDataColor, which initializes and uses this. - - for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) { - $record = 1; // Skip record #0 (data label) - - if ($this->datatype_implied) // Implied X values? - $x_now = 0.5 + $cnt++; // Place text-data at X = 0.5, 1.5, 2.5, etc... - else - $x_now = $this->data[$row][$record++]; // Read it, advance record index - - $x_now_pixels = $this->xtr($x_now); // Convert X to device coordinates - $x_left = $x_now_pixels - $dw; - $x_right = $x_now_pixels + $dw; - - if ($this->x_data_label_pos != 'none') // Draw X Data labels? - $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels, $row); - - // Require and use 4 numeric values in each row. - if ($this->num_recs[$row] - $record != 4 - || !is_numeric($yo = $this->data[$row][$record++]) - || !is_numeric($yh = $this->data[$row][$record++]) - || !is_numeric($yl = $this->data[$row][$record++]) - || !is_numeric($yc = $this->data[$row][$record++])) { - return $this->PrintError("DrawOHLC: row $row must have 4 values."); - } - - // Set device coordinates for each value and direction flag: - $yh_pixels = $this->ytr($yh); - $yl_pixels = $this->ytr($yl); - $yc_pixels = $this->ytr($yc); - $yo_pixels = $this->ytr($yo); - $closed_up = $yc >= $yo; - - // Get data colors and line thicknesses: - if ($closed_up) { - $this->GetDataColor($row, 1, $gcvars, $body_color); // Color 1 for body, closing up - $this->GetDataColor($row, 3, $gcvars, $ext_color); // Color 3 for wicks/ticks - } else { - $this->GetDataColor($row, 0, $gcvars, $body_color); // Color 0 for body, closing down - $this->GetDataColor($row, 2, $gcvars, $ext_color); // Color 2 for wicks/ticks - } - imagesetthickness($this->img, $body_thickness); - - if ($draw_candles) { - // Note: Unlike ImageFilledRectangle, ImageRectangle 'requires' its arguments in - // order with upper left corner first. - if ($closed_up) { - $yb1_pixels = $yc_pixels; // Upper body Y - $yb2_pixels = $yo_pixels; // Lower body Y - $draw_body = $draw_body_close_up; - // Avoid a PHP/GD bug resulting in "T"-shaped ends to zero height unfilled rectangle: - if ($yb1_pixels == $yb2_pixels) - $draw_body = 'imagefilledrectangle'; - } else { - $yb1_pixels = $yo_pixels; - $yb2_pixels = $yc_pixels; - $draw_body = 'imagefilledrectangle'; - } - - // Draw candle body - $draw_body($this->img, $x_left, $yb1_pixels, $x_right, $yb2_pixels, $body_color); - - // Draw upper and lower wicks, if they have height. (In device coords, that's dY<0) - imagesetthickness($this->img, $wick_thickness); - if ($yh_pixels < $yb1_pixels) { - imageline($this->img, $x_now_pixels, $yb1_pixels, $x_now_pixels, $yh_pixels, $ext_color); - } - if ($yl_pixels > $yb2_pixels) { - imageline($this->img, $x_now_pixels, $yb2_pixels, $x_now_pixels, $yl_pixels, $ext_color); - } - } else { - // Basic OHLC - imageline($this->img, $x_now_pixels, $yl_pixels, $x_now_pixels, $yh_pixels, $body_color); - imagesetthickness($this->img, $wick_thickness); - imageline($this->img, $x_left, $yo_pixels, $x_now_pixels, $yo_pixels, $ext_color); - imageline($this->img, $x_right, $yc_pixels, $x_now_pixels, $yc_pixels, $ext_color); - } - imagesetthickness($this->img, 1); - } - return TRUE; - } - - /* - * Draw the graph. - * This is the function that performs the actual drawing, after all - * the parameters and data are set up. - * It also outputs the finished image, unless told not to. - * Note: It is possible for this to be called multiple times. - */ - function DrawGraph() - { - // Test for missing image, missing data, empty data: - if (!$this->CheckDataArray()) - return FALSE; // Error message already reported. - - // Set defaults then import plot type configuration: - $draw_axes = TRUE; - $draw_arg = array(); // Default is: no arguments to the drawing function - extract(PHPlot::$plots[$this->plot_type]); - - // Allocate colors for the plot: - $this->SetColorIndexes(); - - // Get maxima and minima for scaling: - if (!$this->FindDataLimits()) - return FALSE; - - // Set plot area world values (plot_max_x, etc.): - if (!$this->CalcPlotAreaWorld()) - return FALSE; - - // Calculate X and Y axis positions in World Coordinates: - $this->CalcAxisPositions(); - - // Process label-related parameters: - $this->CheckLabels(); - - // Apply grid defaults: - $this->CalcGridSettings(); - - // Calculate the plot margins, if needed. - // For pie charts, set the $maximize argument to maximize space usage. - $this->CalcMargins(!$draw_axes); - - // Calculate the actual plot area in device coordinates: - $this->CalcPlotAreaPixels(); - - // Calculate the mapping between world and device coordinates: - $this->CalcTranslation(); - - // Pad color and style arrays to fit records per group: - $this->PadArrays(); - $this->DoCallback('draw_setup'); - - $this->DrawBackground(); - $this->DrawImageBorder(); - $this->DoCallback('draw_image_background'); - - $this->DrawPlotAreaBackground(); - $this->DoCallback('draw_plotarea_background', $this->plot_area); - - $this->DrawTitle(); - if ($draw_axes) { // If no axes (pie chart), no axis titles either - $this->DrawXTitle(); - $this->DrawYTitle(); - } - $this->DoCallback('draw_titles'); - - if ($draw_axes && ! $this->grid_at_foreground) { // Usually one wants grids to go back, but... - $this->DrawYAxis(); // Y axis must be drawn before X axis (see DrawYAxis()) - $this->DrawXAxis(); - $this->DoCallback('draw_axes'); - } - - // Call the plot-type drawing method: - call_user_func_array(array($this, $draw_method), $draw_arg); - $this->DoCallback('draw_graph', $this->plot_area); - - if ($draw_axes && $this->grid_at_foreground) { // Usually one wants grids to go back, but... - $this->DrawYAxis(); // Y axis must be drawn before X axis (see DrawYAxis()) - $this->DrawXAxis(); - $this->DoCallback('draw_axes'); - } - - if ($draw_axes) { - $this->DrawPlotBorder(); - $this->DoCallback('draw_border'); - } - - if ($this->legend) { - $this->DrawLegend(); - $this->DoCallback('draw_legend'); - } - $this->DoCallback('draw_all', $this->plot_area); - - if ($this->print_image && !$this->PrintImage()) - return FALSE; - - return TRUE; - } - -///////////////////////////////////////////// -////////////////// DEPRECATED METHODS -///////////////////////////////////////////// - - /* - * Note on deprecated methods - as these pre-date the PHPlot Reference - * Manual, and there is minimal documentation about them, I have neither - * removed them nor changed them. They are not tested or documented, and - * should not be used. - */ - - /* - * Deprecated, use SetYTickPos() - */ - function SetDrawVertTicks($which_dvt) - { - if ($which_dvt != 1) - $this->SetYTickPos('none'); - return TRUE; - } - - /* - * Deprecated, use SetXTickPos() - */ - function SetDrawHorizTicks($which_dht) - { - if ($which_dht != 1) - $this->SetXTickPos('none'); - return TRUE; - } - - /* - * Deprecated - use SetNumXTicks() - */ - function SetNumHorizTicks($n) - { - return $this->SetNumXTicks($n); - } - - /* - * Deprecated - use SetNumYTicks() - */ - function SetNumVertTicks($n) - { - return $this->SetNumYTicks($n); - } - - /* - * Deprecated - use SetXTickIncrement() - */ - function SetHorizTickIncrement($inc) - { - return $this->SetXTickIncrement($inc); - } - - /* - * Deprecated - use SetYTickIncrement() - */ - function SetVertTickIncrement($inc) - { - return $this->SetYTickIncrement($inc); - } - - /* - * Deprecated - use SetYTickPos() - */ - function SetVertTickPosition($which_tp) - { - return $this->SetYTickPos($which_tp); - } - - /* - * Deprecated - use SetXTickPos() - */ - function SetHorizTickPosition($which_tp) - { - return $this->SetXTickPos($which_tp); - } - - /* - * Deprecated - use SetFont() - */ - function SetTitleFontSize($which_size) - { - return $this->SetFont('title', $which_size); - } - - /* - * Deprecated - use SetFont() - */ - function SetAxisFontSize($which_size) - { - $this->SetFont('x_label', $which_size); - $this->SetFont('y_label', $which_size); - } - - /* - * Deprecated - use SetFont() - */ - function SetSmallFontSize($which_size) - { - return $this->SetFont('generic', $which_size); - } - - /* - * Deprecated - use SetFont() - */ - function SetXLabelFontSize($which_size) - { - return $this->SetFont('x_title', $which_size); - } - - /* - * Deprecated - use SetFont() - */ - function SetYLabelFontSize($which_size) - { - return $this->SetFont('y_title', $which_size); - } - - /* - * Deprecated - use SetXTitle() - */ - function SetXLabel($which_xlab) - { - return $this->SetXTitle($which_xlab); - } - - /* - * Deprecated - use SetYTitle() - */ - function SetYLabel($which_ylab) - { - return $this->SetYTitle($which_ylab); - } - - /* - * Deprecated - use SetXTickLength() and SetYTickLength() instead. - */ - function SetTickLength($which_tl) - { - $this->SetXTickLength($which_tl); - $this->SetYTickLength($which_tl); - return TRUE; - } - - /* - * Deprecated - use SetYLabelType() - */ - function SetYGridLabelType($which_yglt) - { - return $this->SetYLabelType($which_yglt); - } - - /* - * Deprecated - use SetXLabelType() - */ - function SetXGridLabelType($which_xglt) - { - return $this->SetXLabelType($which_xglt); - } - /* - * Deprecated - use SetYTickLabelPos() - */ - function SetYGridLabelPos($which_yglp) - { - return $this->SetYTickLabelPos($which_yglp); - } - /* - * Deprecated - use SetXTickLabelPos() - */ - function SetXGridLabelPos($which_xglp) - { - return $this->SetXTickLabelPos($which_xglp); - } - - /* - * Deprecated - use SetXtitle() - */ - function SetXTitlePos($xpos) - { - $this->x_title_pos = $xpos; - return TRUE; - } - - /* - * Deprecated - use SetYTitle() - */ - function SetYTitlePos($xpos) - { - $this->y_title_pos = $xpos; - return TRUE; - } - - /* - * Deprecated - use SetXDataLabelPos() - */ - function SetDrawXDataLabels($which_dxdl) - { - if ($which_dxdl == '1' ) - $this->SetXDataLabelPos('plotdown'); - else - $this->SetXDataLabelPos('none'); - } - - /* - * Deprecated - use SetPlotAreaPixels() - */ - function SetNewPlotAreaPixels($x1, $y1, $x2, $y2) - { - return $this->SetPlotAreaPixels($x1, $y1, $x2, $y2); - } - - /* - * Deprecated - use SetLineWidths(). - */ - function SetLineWidth($which_lw) - { - - $this->SetLineWidths($which_lw); - - if (!$this->error_bar_line_width) { - $this->SetErrorBarLineWidth($which_lw); - } - return TRUE; - } - - /* - * Deprecated - use SetPointShapes(). - */ - function SetPointShape($which_pt) - { - $this->SetPointShapes($which_pt); - return TRUE; - } - - /* - * Deprecated - use SetPointSizes(). - */ - function SetPointSize($which_ps) - { - $this->SetPointSizes($which_ps); - return TRUE; - } -} - -/* - * The PHPlot_truecolor class extends PHPlot to use GD truecolor images. - */ - -class PHPlot_truecolor extends PHPlot -{ - /* - * PHPlot Truecolor variation constructor: Create a PHPlot_truecolor object and initialize it. - * Note this does NOT call the parent (PHPlot) constructor. It duplicates the code here. - * Everything is the same as the PHPlot constructor except for imagecreatetruecolor. - * - * Parameters are the same as PHPlot: - * $which_width : Image width in pixels. - * $which_height : Image height in pixels. - * $which_output_file : Filename for output. - * $which_input_file : Path to a file to be used as background. - */ - function __construct($which_width=600, $which_height=400, $which_output_file=NULL, $which_input_file=NULL) - { - $this->SetRGBArray($this->color_array); - - if ($which_output_file) - $this->SetOutputFile($which_output_file); - - if ($which_input_file) { - $this->SetInputFile($which_input_file); - } else { - $this->image_width = $which_width; - $this->image_height = $which_height; - - $this->img = imagecreatetruecolor($this->image_width, $this->image_height); - if (! $this->img) - return $this->PrintError('PHPlot_truecolor(): Could not create image resource.'); - } - - $this->SetDefaultStyles(); - $this->SetDefaultFonts(); - } -} diff --git a/phpmailer/README b/phpmailer/README deleted file mode 100644 index de5876f..0000000 --- a/phpmailer/README +++ /dev/null @@ -1,102 +0,0 @@ -PHPMailer -Full Featured Email Transfer Class for PHP -========================================== - -http://phpmailer.sourceforge.net/ - -This software is licenced under the LGPL. Please read LICENSE for information on the -software availability and distribution. - -Class Features: -- Send emails with multiple TOs, CCs, BCCs and REPLY-TOs -- Redundant SMTP servers -- Multipart/alternative emails for mail clients that do not read HTML email -- Support for 8bit, base64, binary, and quoted-printable encoding -- Uses the same methods as the very popular AspEmail active server (COM) component -- SMTP authentication -- Native language support -- Word wrap, and more! - -Why you might need it: - -Many PHP developers utilize email in their code. The only PHP function -that supports this is the mail() function. However, it does not expose -any of the popular features that many email clients use nowadays like -HTML-based emails and attachments. There are two proprietary -development tools out there that have all the functionality built into -easy to use classes: AspEmail(tm) and AspMail. Both of these -programs are COM components only available on Windows. They are also a -little pricey for smaller projects. - -Since I do Linux development I’ve missed these tools for my PHP coding. -So I built a version myself that implements the same methods (object -calls) that the Windows-based components do. It is open source and the -LGPL license allows you to place the class in your proprietary PHP -projects. - - -Installation: - -Copy class.phpmailer.php into your php.ini include_path. If you are -using the SMTP mailer then place class.smtp.php in your path as well. -In the language directory you will find several files like -phpmailer.lang-en.php. If you look right before the .php extension -that there are two letters. These represent the language type of the -translation file. For instance "en" is the English file and "br" is -the Portuguese file. Chose the file that best fits with your language -and place it in the PHP include path. If your language is English -then you have nothing more to do. If it is a different language then -you must point PHPMailer to the correct translation. To do this, call -the PHPMailer SetLanguage method like so: - -// To load the Portuguese version -$mail->SetLanguage("br", "/optional/path/to/language/directory/"); - -That's it. You should now be ready to use PHPMailer! - - -A Simple Example: - -IsSMTP(); // set mailer to use SMTP -$mail->Host = "smtp1.example.com;smtp2.example.com"; // specify main and backup server -$mail->SMTPAuth = true; // turn on SMTP authentication -$mail->Username = "jswan"; // SMTP username -$mail->Password = "secret"; // SMTP password - -$mail->From = "from@example.com"; -$mail->FromName = "Mailer"; -$mail->AddAddress("josh@example.net", "Josh Adams"); -$mail->AddAddress("ellen@example.com"); // name is optional -$mail->AddReplyTo("info@example.com", "Information"); - -$mail->WordWrap = 50; // set word wrap to 50 characters -$mail->AddAttachment("/var/tmp/file.tar.gz"); // add attachments -$mail->AddAttachment("/tmp/image.jpg", "new.jpg"); // optional name -$mail->IsHTML(true); // set email format to HTML - -$mail->Subject = "Here is the subject"; -$mail->Body = "This is the HTML message body in bold!"; -$mail->AltBody = "This is the body in plain text for non-HTML mail clients"; - -if(!$mail->Send()) -{ - echo "Message could not be sent.

    "; - echo "Mailer Error: " . $mail->ErrorInfo; - exit; -} - -echo "Message has been sent"; -?> - -CHANGELOG - -See ChangeLog.txt - -Download: http://sourceforge.net/project/showfiles.php?group_id=26031 - -Brent R. Matzelle diff --git a/phpmailer/class.phpmailer.php b/phpmailer/class.phpmailer.php deleted file mode 100644 index 014a8d3..0000000 --- a/phpmailer/class.phpmailer.php +++ /dev/null @@ -1,1721 +0,0 @@ -ContentType = 'text/html'; - } else { - $this->ContentType = 'text/plain'; - } - } - - /** - * Sets Mailer to send message using SMTP. - * @return void - */ - function IsSMTP() { - $this->Mailer = 'smtp'; - } - - /** - * Sets Mailer to send message using PHP mail() function. - * @return void - */ - function IsMail() { - $this->Mailer = 'mail'; - } - - /** - * Sets Mailer to send message using the $Sendmail program. - * @return void - */ - function IsSendmail() { - $this->Mailer = 'sendmail'; - } - - /** - * Sets Mailer to send message using the qmail MTA. - * @return void - */ - function IsQmail() { - $this->Sendmail = '/var/qmail/bin/sendmail'; - $this->Mailer = 'sendmail'; - } - - ///////////////////////////////////////////////// - // METHODS, RECIPIENTS - ///////////////////////////////////////////////// - - /** - * Adds a "To" address. - * @param string $address - * @param string $name - * @return void - */ - function AddAddress($address, $name = '') { - $cur = count($this->to); - $this->to[$cur][0] = trim($address); - $this->to[$cur][1] = $name; - } - - /** - * Adds a "Cc" address. Note: this function works - * with the SMTP mailer on win32, not with the "mail" - * mailer. - * @param string $address - * @param string $name - * @return void - */ - function AddCC($address, $name = '') { - $cur = count($this->cc); - $this->cc[$cur][0] = trim($address); - $this->cc[$cur][1] = $name; - } - - /** - * Adds a "Bcc" address. Note: this function works - * with the SMTP mailer on win32, not with the "mail" - * mailer. - * @param string $address - * @param string $name - * @return void - */ - function AddBCC($address, $name = '') { - $cur = count($this->bcc); - $this->bcc[$cur][0] = trim($address); - $this->bcc[$cur][1] = $name; - } - - /** - * Adds a "Reply-To" address. - * @param string $address - * @param string $name - * @return void - */ - function AddReplyTo($address, $name = '') { - $cur = count($this->ReplyTo); - $this->ReplyTo[$cur][0] = trim($address); - $this->ReplyTo[$cur][1] = $name; - } - - ///////////////////////////////////////////////// - // METHODS, MAIL SENDING - ///////////////////////////////////////////////// - - /** - * Creates message and assigns Mailer. If the message is - * not sent successfully then it returns false. Use the ErrorInfo - * variable to view description of the error. - * @return bool - */ - function Send() { - $header = ''; - $body = ''; - $result = true; - - if((count($this->to) + count($this->cc) + count($this->bcc)) < 1) { - $this->SetError($this->Lang('provide_address')); - return false; - } - - /* Set whether the message is multipart/alternative */ - if(!empty($this->AltBody)) { - $this->ContentType = 'multipart/alternative'; - } - - $this->error_count = 0; // reset errors - $this->SetMessageType(); - $header .= $this->CreateHeader(); - $body = $this->CreateBody(); - - if($body == '') { - return false; - } - - /* Choose the mailer */ - switch($this->Mailer) { - case 'sendmail': - $result = $this->SendmailSend($header, $body); - break; - case 'smtp': - $result = $this->SmtpSend($header, $body); - break; - case 'mail': - $result = $this->MailSend($header, $body); - break; - default: - $result = $this->MailSend($header, $body); - break; - //$this->SetError($this->Mailer . $this->Lang('mailer_not_supported')); - //$result = false; - //break; - } - - return $result; - } - - /** - * Sends mail using the $Sendmail program. - * @access private - * @return bool - */ - function SendmailSend($header, $body) { - if ($this->Sender != '') { - $sendmail = sprintf("%s -oi -f %s -t", escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender)); - } else { - $sendmail = sprintf("%s -oi -t", escapeshellcmd($this->Sendmail)); - } - - if(!@$mail = popen($sendmail, 'w')) { - $this->SetError($this->Lang('execute') . $this->Sendmail); - return false; - } - - fputs($mail, $header); - fputs($mail, $body); - - $result = pclose($mail) >> 8 & 0xFF; - if($result != 0) { - $this->SetError($this->Lang('execute') . $this->Sendmail); - return false; - } - - return true; - } - - /** - * Sends mail using the PHP mail() function. - * @access private - * @return bool - */ - function MailSend($header, $body) { - - $to = ''; - for($i = 0; $i < count($this->to); $i++) { - if($i != 0) { $to .= ', '; } - $to .= $this->AddrFormat($this->to[$i]); - } - - $toArr = split(',', $to); - - if ($this->Sender != '' && strlen(ini_get('safe_mode'))< 1) { - $old_from = ini_get('sendmail_from'); - ini_set('sendmail_from', $this->Sender); - $params = sprintf("-oi -f %s", $this->Sender); - if ($this->SingleTo === true && count($toArr) > 1) { - foreach ($toArr as $key => $val) { - $rt = @mail($val, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params); - } - } else { - $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params); - } - } else { - if ($this->SingleTo === true && count($toArr) > 1) { - foreach ($toArr as $key => $val) { - $rt = @mail($val, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params); - } - } else { - $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header); - } - } - - if (isset($old_from)) { - ini_set('sendmail_from', $old_from); - } - - if(!$rt) { - $this->SetError($this->Lang('instantiate')); - return false; - } - - return true; - } - - /** - * Sends mail via SMTP using PhpSMTP (Author: - * Chris Ryan). Returns bool. Returns false if there is a - * bad MAIL FROM, RCPT, or DATA input. - * @access private - * @return bool - */ - function SmtpSend($header, $body) { - include_once($this->PluginDir . 'class.smtp.php'); - $error = ''; - $bad_rcpt = array(); - - if(!$this->SmtpConnect()) { - return false; - } - - $smtp_from = ($this->Sender == '') ? $this->From : $this->Sender; - if(!$this->smtp->Mail($smtp_from)) { - $error = $this->Lang('from_failed') . $smtp_from; - $this->SetError($error); - $this->smtp->Reset(); - return false; - } - - /* Attempt to send attach all recipients */ - for($i = 0; $i < count($this->to); $i++) { - if(!$this->smtp->Recipient($this->to[$i][0])) { - $bad_rcpt[] = $this->to[$i][0]; - } - } - for($i = 0; $i < count($this->cc); $i++) { - if(!$this->smtp->Recipient($this->cc[$i][0])) { - $bad_rcpt[] = $this->cc[$i][0]; - } - } - for($i = 0; $i < count($this->bcc); $i++) { - if(!$this->smtp->Recipient($this->bcc[$i][0])) { - $bad_rcpt[] = $this->bcc[$i][0]; - } - } - - if(count($bad_rcpt) > 0) { // Create error message - for($i = 0; $i < count($bad_rcpt); $i++) { - if($i != 0) { - $error .= ', '; - } - $error .= $bad_rcpt[$i]; - } - $error = $this->Lang('recipients_failed') . $error; - $this->SetError($error); - $this->smtp->Reset(); - return false; - } - - if(!$this->smtp->Data($header . $body)) { - $this->SetError($this->Lang('data_not_accepted')); - $this->smtp->Reset(); - return false; - } - if($this->SMTPKeepAlive == true) { - $this->smtp->Reset(); - } else { - $this->SmtpClose(); - } - - return true; - } - - /** - * Initiates a connection to an SMTP server. Returns false if the - * operation failed. - * @access private - * @return bool - */ - function SmtpConnect() { - if($this->smtp == NULL) { - $this->smtp = new SMTP(); - } - - $this->smtp->do_debug = $this->SMTPDebug; - $hosts = explode(';', $this->Host); - $index = 0; - $connection = ($this->smtp->Connected()); - - /* Retry while there is no connection */ - while($index < count($hosts) && $connection == false) { - $hostinfo = array(); - if(preg_match('/^(.+):([0-9]+)$/i', $hosts[$index], $hostinfo)) { - $host = $hostinfo[1]; - $port = $hostinfo[2]; - } else { - $host = $hosts[$index]; - $port = $this->Port; - } - - if($this->smtp->Connect(((!empty($this->SMTPSecure))?$this->SMTPSecure.'://':'').$host, $port, $this->Timeout)) { - if ($this->Helo != '') { - $this->smtp->Hello($this->Helo); - } else { - $this->smtp->Hello($this->ServerHostname()); - } - - $connection = true; - if($this->SMTPAuth) { - if(!$this->smtp->Authenticate($this->Username, $this->Password)) { - $this->SetError($this->Lang('authenticate')); - $this->smtp->Reset(); - $connection = false; - } - } - } - $index++; - } - if(!$connection) { - $this->SetError($this->Lang('connect_host').$host); - } - - return $connection; - } - - /** - * Closes the active SMTP session if one exists. - * @return void - */ - function SmtpClose() { - if($this->smtp != NULL) { - if($this->smtp->Connected()) { - $this->smtp->Quit(); - $this->smtp->Close(); - } - } - } - - /** - * Sets the language for all class error messages. Returns false - * if it cannot load the language file. The default language type - * is English. - * @param string $lang_type Type of language (e.g. Portuguese: "br") - * @param string $lang_path Path to the language file directory - * @access public - * @return bool - */ - function SetLanguage($lang_type, $lang_path = 'language/') { - if(file_exists($lang_path.'phpmailer.lang-'.$lang_type.'.php')) { - include($lang_path.'phpmailer.lang-'.$lang_type.'.php'); - } elseif (file_exists($lang_path.'phpmailer.lang-en.php')) { - include($lang_path.'phpmailer.lang-en.php'); - } else { - $this->SetError('Could not load language file'); - return false; - } - $this->language = $PHPMAILER_LANG; - - return true; - } - - ///////////////////////////////////////////////// - // METHODS, MESSAGE CREATION - ///////////////////////////////////////////////// - - /** - * Creates recipient headers. - * @access private - * @return string - */ - function AddrAppend($type, $addr) { - $addr_str = $type . ': '; - $addr_str .= $this->AddrFormat($addr[0]); - if(count($addr) > 1) { - for($i = 1; $i < count($addr); $i++) { - $addr_str .= ', ' . $this->AddrFormat($addr[$i]); - } - } - $addr_str .= $this->LE; - - return $addr_str; - } - - /** - * Formats an address correctly. - * @access private - * @return string - */ - function AddrFormat($addr) { - if(empty($addr[1])) { - $formatted = $this->SecureHeader($addr[0]); - } else { - $formatted = $this->EncodeHeader($this->SecureHeader($addr[1]), 'phrase') . " <" . $this->SecureHeader($addr[0]) . ">"; - } - - return $formatted; - } - - /** - * Wraps message for use with mailers that do not - * automatically perform wrapping and for quoted-printable. - * Original written by philippe. - * @access private - * @return string - */ - function WrapText($message, $length, $qp_mode = false) { - $soft_break = ($qp_mode) ? sprintf(" =%s", $this->LE) : $this->LE; - - $message = $this->FixEOL($message); - if (substr($message, -1) == $this->LE) { - $message = substr($message, 0, -1); - } - - $line = explode($this->LE, $message); - $message = ''; - for ($i=0 ;$i < count($line); $i++) { - $line_part = explode(' ', $line[$i]); - $buf = ''; - for ($e = 0; $e $length)) { - $space_left = $length - strlen($buf) - 1; - if ($e != 0) { - if ($space_left > 20) { - $len = $space_left; - if (substr($word, $len - 1, 1) == '=') { - $len--; - } elseif (substr($word, $len - 2, 1) == '=') { - $len -= 2; - } - $part = substr($word, 0, $len); - $word = substr($word, $len); - $buf .= ' ' . $part; - $message .= $buf . sprintf("=%s", $this->LE); - } else { - $message .= $buf . $soft_break; - } - $buf = ''; - } - while (strlen($word) > 0) { - $len = $length; - if (substr($word, $len - 1, 1) == '=') { - $len--; - } elseif (substr($word, $len - 2, 1) == '=') { - $len -= 2; - } - $part = substr($word, 0, $len); - $word = substr($word, $len); - - if (strlen($word) > 0) { - $message .= $part . sprintf("=%s", $this->LE); - } else { - $buf = $part; - } - } - } else { - $buf_o = $buf; - $buf .= ($e == 0) ? $word : (' ' . $word); - - if (strlen($buf) > $length and $buf_o != '') { - $message .= $buf_o . $soft_break; - $buf = $word; - } - } - } - $message .= $buf . $this->LE; - } - - return $message; - } - - /** - * Set the body wrapping. - * @access private - * @return void - */ - function SetWordWrap() { - if($this->WordWrap < 1) { - return; - } - - switch($this->message_type) { - case 'alt': - /* fall through */ - case 'alt_attachments': - $this->AltBody = $this->WrapText($this->AltBody, $this->WordWrap); - break; - default: - $this->Body = $this->WrapText($this->Body, $this->WordWrap); - break; - } - } - - /** - * Assembles message header. - * @access private - * @return string - */ - function CreateHeader() { - $result = ''; - - /* Set the boundaries */ - $uniq_id = md5(uniqid(time())); - $this->boundary[1] = 'b1_' . $uniq_id; - $this->boundary[2] = 'b2_' . $uniq_id; - - $result .= $this->HeaderLine('Date', $this->RFCDate()); - if($this->Sender == '') { - $result .= $this->HeaderLine('Return-Path', trim($this->From)); - } else { - $result .= $this->HeaderLine('Return-Path', trim($this->Sender)); - } - - /* To be created automatically by mail() */ - if($this->Mailer != 'mail') { - if(count($this->to) > 0) { - $result .= $this->AddrAppend('To', $this->to); - } elseif (count($this->cc) == 0) { - $result .= $this->HeaderLine('To', 'undisclosed-recipients:;'); - } - if(count($this->cc) > 0) { - $result .= $this->AddrAppend('Cc', $this->cc); - } - } - - $from = array(); - $from[0][0] = trim($this->From); - $from[0][1] = $this->FromName; - $result .= $this->AddrAppend('From', $from); - - /* sendmail and mail() extract Cc from the header before sending */ - if((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (count($this->cc) > 0)) { - $result .= $this->AddrAppend('Cc', $this->cc); - } - - /* sendmail and mail() extract Bcc from the header before sending */ - if((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (count($this->bcc) > 0)) { - $result .= $this->AddrAppend('Bcc', $this->bcc); - } - - if(count($this->ReplyTo) > 0) { - $result .= $this->AddrAppend('Reply-To', $this->ReplyTo); - } - - /* mail() sets the subject itself */ - if($this->Mailer != 'mail') { - $result .= $this->HeaderLine('Subject', $this->EncodeHeader($this->SecureHeader($this->Subject))); - } - -// {{{ BIT_MOD - $this->MessageID = '<'.$uniq_id.'@'.$this->ServerHostname().'>'; - $result .= sprintf("Message-ID: %s%s", $this->MessageID, $this->LE); -// }}} BIT_MOD - $result .= $this->HeaderLine('X-Priority', $this->Priority); - $result .= $this->HeaderLine('X-Mailer', 'PHPMailer (phpmailer.sourceforge.net) [version ' . $this->Version . ']'); - - if($this->ConfirmReadingTo != '') { - $result .= $this->HeaderLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>'); - } - - // Add custom headers - for($index = 0; $index < count($this->CustomHeader); $index++) { - $result .= $this->HeaderLine(trim($this->CustomHeader[$index][0]), $this->EncodeHeader(trim($this->CustomHeader[$index][1]))); - } - $result .= $this->HeaderLine('MIME-Version', '1.0'); - - switch($this->message_type) { - case 'plain': - $result .= $this->HeaderLine('Content-Transfer-Encoding', $this->Encoding); - $result .= sprintf("Content-Type: %s; charset=\"%s\"", $this->ContentType, $this->CharSet); - break; - case 'attachments': - /* fall through */ - case 'alt_attachments': - if($this->InlineImageExists()){ - $result .= sprintf("Content-Type: %s;%s\ttype=\"text/html\";%s\tboundary=\"%s\"%s", 'multipart/related', $this->LE, $this->LE, $this->boundary[1], $this->LE); - } else { - $result .= $this->HeaderLine('Content-Type', 'multipart/mixed;'); - $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"'); - } - break; - case 'alt': - $result .= $this->HeaderLine('Content-Type', 'multipart/alternative;'); - $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"'); - break; - } - - if($this->Mailer != 'mail') { - $result .= $this->LE.$this->LE; - } - - return $result; - } - - /** - * Assembles the message body. Returns an empty string on failure. - * @access private - * @return string - */ - function CreateBody() { - $result = ''; - - $this->SetWordWrap(); - - switch($this->message_type) { - case 'alt': - $result .= $this->GetBoundary($this->boundary[1], '', 'text/plain', ''); - $result .= $this->EncodeString($this->AltBody, $this->Encoding); - $result .= $this->LE.$this->LE; - $result .= $this->GetBoundary($this->boundary[1], '', 'text/html', ''); - $result .= $this->EncodeString($this->Body, $this->Encoding); - $result .= $this->LE.$this->LE; - $result .= $this->EndBoundary($this->boundary[1]); - break; - case 'plain': - $result .= $this->EncodeString($this->Body, $this->Encoding); - break; - case 'attachments': - $result .= $this->GetBoundary($this->boundary[1], '', '', ''); - $result .= $this->EncodeString($this->Body, $this->Encoding); - $result .= $this->LE; - $result .= $this->AttachAll(); - break; - case 'alt_attachments': - $result .= sprintf("--%s%s", $this->boundary[1], $this->LE); - $result .= sprintf("Content-Type: %s;%s" . "\tboundary=\"%s\"%s", 'multipart/alternative', $this->LE, $this->boundary[2], $this->LE.$this->LE); - $result .= $this->GetBoundary($this->boundary[2], '', 'text/plain', '') . $this->LE; // Create text body - $result .= $this->EncodeString($this->AltBody, $this->Encoding); - $result .= $this->LE.$this->LE; - $result .= $this->GetBoundary($this->boundary[2], '', 'text/html', '') . $this->LE; // Create the HTML body - $result .= $this->EncodeString($this->Body, $this->Encoding); - $result .= $this->LE.$this->LE; - $result .= $this->EndBoundary($this->boundary[2]); - $result .= $this->AttachAll(); - break; - } - if($this->IsError()) { - $result = ''; - } - - return $result; - } - - /** - * Returns the start of a message boundary. - * @access private - */ - function GetBoundary($boundary, $charSet, $contentType, $encoding) { - $result = ''; - if($charSet == '') { - $charSet = $this->CharSet; - } - if($contentType == '') { - $contentType = $this->ContentType; - } - if($encoding == '') { - $encoding = $this->Encoding; - } - $result .= $this->TextLine('--' . $boundary); - $result .= sprintf("Content-Type: %s; charset = \"%s\"", $contentType, $charSet); - $result .= $this->LE; - $result .= $this->HeaderLine('Content-Transfer-Encoding', $encoding); - $result .= $this->LE; - - return $result; - } - - /** - * Returns the end of a message boundary. - * @access private - */ - function EndBoundary($boundary) { - return $this->LE . '--' . $boundary . '--' . $this->LE; - } - - /** - * Sets the message type. - * @access private - * @return void - */ - function SetMessageType() { - if(count($this->attachment) < 1 && strlen($this->AltBody) < 1) { - $this->message_type = 'plain'; - } else { - if(count($this->attachment) > 0) { - $this->message_type = 'attachments'; - } - if(strlen($this->AltBody) > 0 && count($this->attachment) < 1) { - $this->message_type = 'alt'; - } - if(strlen($this->AltBody) > 0 && count($this->attachment) > 0) { - $this->message_type = 'alt_attachments'; - } - } - } - - /* Returns a formatted header line. - * @access private - * @return string - */ - function HeaderLine($name, $value) { - return $name . ': ' . $value . $this->LE; - } - - /** - * Returns a formatted mail line. - * @access private - * @return string - */ - function TextLine($value) { - return $value . $this->LE; - } - - ///////////////////////////////////////////////// - // CLASS METHODS, ATTACHMENTS - ///////////////////////////////////////////////// - - /** - * Adds an attachment from a path on the filesystem. - * Returns false if the file could not be found - * or accessed. - * @param string $path Path to the attachment. - * @param string $name Overrides the attachment name. - * @param string $encoding File encoding (see $Encoding). - * @param string $type File extension (MIME) type. - * @return bool - */ - function AddAttachment($path, $name = '', $encoding = 'base64', $type = 'application/octet-stream') { - if(!@is_file($path)) { - $this->SetError($this->Lang('file_access') . $path); - return false; - } - - $filename = basename($path); - if($name == '') { - $name = $filename; - } - - $cur = count($this->attachment); - $this->attachment[$cur][0] = $path; - $this->attachment[$cur][1] = $filename; - $this->attachment[$cur][2] = $name; - $this->attachment[$cur][3] = $encoding; - $this->attachment[$cur][4] = $type; - $this->attachment[$cur][5] = false; // isStringAttachment - $this->attachment[$cur][6] = 'attachment'; - $this->attachment[$cur][7] = 0; - - return true; - } - - /** - * Attaches all fs, string, and binary attachments to the message. - * Returns an empty string on failure. - * @access private - * @return string - */ - function AttachAll() { - /* Return text of body */ - $mime = array(); - - /* Add all attachments */ - for($i = 0; $i < count($this->attachment); $i++) { - /* Check for string attachment */ - $bString = $this->attachment[$i][5]; - if ($bString) { - $string = $this->attachment[$i][0]; - } else { - $path = $this->attachment[$i][0]; - } - - $filename = $this->attachment[$i][1]; - $name = $this->attachment[$i][2]; - $encoding = $this->attachment[$i][3]; - $type = $this->attachment[$i][4]; - $disposition = $this->attachment[$i][6]; - $cid = $this->attachment[$i][7]; - - $mime[] = sprintf("--%s%s", $this->boundary[1], $this->LE); - $mime[] = sprintf("Content-Type: %s; name=\"%s\"%s", $type, $name, $this->LE); - $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE); - - if($disposition == 'inline') { - $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE); - } - - $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", $disposition, $name, $this->LE.$this->LE); - - /* Encode as string attachment */ - if($bString) { - $mime[] = $this->EncodeString($string, $encoding); - if($this->IsError()) { - return ''; - } - $mime[] = $this->LE.$this->LE; - } else { - $mime[] = $this->EncodeFile($path, $encoding); - if($this->IsError()) { - return ''; - } - $mime[] = $this->LE.$this->LE; - } - } - - $mime[] = sprintf("--%s--%s", $this->boundary[1], $this->LE); - - return join('', $mime); - } - - /** - * Encodes attachment in requested format. Returns an - * empty string on failure. - * @access private - * @return string - */ - function EncodeFile ($path, $encoding = 'base64') { - if(!@$fd = fopen($path, 'rb')) { - $this->SetError($this->Lang('file_open') . $path); - return ''; - } - $magic_quotes = get_magic_quotes_runtime(); - set_magic_quotes_runtime(0); - $file_buffer = fread($fd, filesize($path)); - $file_buffer = $this->EncodeString($file_buffer, $encoding); - fclose($fd); - set_magic_quotes_runtime($magic_quotes); - - return $file_buffer; - } - - /** - * Encodes string to requested format. Returns an - * empty string on failure. - * @access private - * @return string - */ - function EncodeString ($str, $encoding = 'base64') { - $encoded = ''; - switch(strtolower($encoding)) { - case 'base64': - /* chunk_split is found in PHP >= 3.0.6 */ - $encoded = chunk_split(base64_encode($str), 76, $this->LE); - break; - case '7bit': - case '8bit': - $encoded = $this->FixEOL($str); - if (substr($encoded, -(strlen($this->LE))) != $this->LE) - $encoded .= $this->LE; - break; - case 'binary': - $encoded = $str; - break; - case 'quoted-printable': - $encoded = $this->EncodeQP($str); - break; - default: - $this->SetError($this->Lang('encoding') . $encoding); - break; - } - return $encoded; - } - - /** - * Encode a header string to best of Q, B, quoted or none. - * @access private - * @return string - */ - function EncodeHeader ($str, $position = 'text') { - $x = 0; - - switch (strtolower($position)) { - case 'phrase': - if (!preg_match('/[\200-\377]/', $str)) { - /* Can't use addslashes as we don't know what value has magic_quotes_sybase. */ - $encoded = addcslashes($str, "\0..\37\177\\\""); - if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) { - return ($encoded); - } else { - return ("\"$encoded\""); - } - } - $x = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches); - break; - case 'comment': - $x = preg_match_all('/[()"]/', $str, $matches); - /* Fall-through */ - case 'text': - default: - $x += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches); - break; - } - - if ($x == 0) { - return ($str); - } - - $maxlen = 75 - 7 - strlen($this->CharSet); - /* Try to select the encoding which should produce the shortest output */ - if (strlen($str)/3 < $x) { - $encoding = 'B'; - $encoded = base64_encode($str); - $maxlen -= $maxlen % 4; - $encoded = trim(chunk_split($encoded, $maxlen, "\n")); - } else { - $encoding = 'Q'; - $encoded = $this->EncodeQ($str, $position); - $encoded = $this->WrapText($encoded, $maxlen, true); - $encoded = str_replace('='.$this->LE, "\n", trim($encoded)); - } - - $encoded = preg_replace('/^(.*)$/m', " =?".$this->CharSet."?$encoding?\\1?=", $encoded); - $encoded = trim(str_replace("\n", $this->LE, $encoded)); - - return $encoded; - } - - /** - * Encode string to quoted-printable. - * @access private - * @return string - */ - function EncodeQP( $input = '', $line_max = 76, $space_conv = false ) { - $hex = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'); - $lines = preg_split('/(?:\r\n|\r|\n)/', $input); - $eol = "\r\n"; - $escape = '='; - $output = ''; - while( list(, $line) = each($lines) ) { - $linlen = strlen($line); - $newline = ''; - for($i = 0; $i < $linlen; $i++) { - $c = substr( $line, $i, 1 ); - $dec = ord( $c ); - if ( ( $i == 0 ) && ( $dec == 46 ) ) { // convert first point in the line into =2E - $c = '=2E'; - } - if ( $dec == 32 ) { - if ( $i == ( $linlen - 1 ) ) { // convert space at eol only - $c = '=20'; - } else if ( $space_conv ) { - $c = '=20'; - } - } elseif ( ($dec == 61) || ($dec < 32 ) || ($dec > 126) ) { // always encode "\t", which is *not* required - $h2 = floor($dec/16); - $h1 = floor($dec%16); - $c = $escape.$hex[$h2].$hex[$h1]; - } - if ( (strlen($newline) + strlen($c)) >= $line_max ) { // CRLF is not counted - $output .= $newline.$escape.$eol; // soft line break; " =\r\n" is okay - $newline = ''; - // check if newline first character will be point or not - if ( $dec == 46 ) { - $c = '=2E'; - } - } - $newline .= $c; - } // end of for - $output .= $newline.$eol; - } // end of while - return trim($output); - } - - /** - * Encode string to q encoding. - * @access private - * @return string - */ - function EncodeQ ($str, $position = 'text') { - /* There should not be any EOL in the string */ - $encoded = preg_replace("[\r\n]", '', $str); - - switch (strtolower($position)) { - case 'phrase': - $encoded = preg_replace("/([^A-Za-z0-9!*+\/ -])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded); - break; - case 'comment': - $encoded = preg_replace("/([\(\)\"])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded); - case 'text': - default: - /* Replace every high ascii, control =, ? and _ characters */ - $encoded = preg_replace('/([\000-\011\013\014\016-\037\075\077\137\177-\377])/e', - "'='.sprintf('%02X', ord('\\1'))", $encoded); - break; - } - - /* Replace every spaces to _ (more readable than =20) */ - $encoded = str_replace(' ', '_', $encoded); - - return $encoded; - } - - /** - * Adds a string or binary attachment (non-filesystem) to the list. - * This method can be used to attach ascii or binary data, - * such as a BLOB record from a database. - * @param string $string String attachment data. - * @param string $filename Name of the attachment. - * @param string $encoding File encoding (see $Encoding). - * @param string $type File extension (MIME) type. - * @return void - */ - function AddStringAttachment($string, $filename, $encoding = 'base64', $type = 'application/octet-stream') { - /* Append to $attachment array */ - $cur = count($this->attachment); - $this->attachment[$cur][0] = $string; - $this->attachment[$cur][1] = $filename; - $this->attachment[$cur][2] = $filename; - $this->attachment[$cur][3] = $encoding; - $this->attachment[$cur][4] = $type; - $this->attachment[$cur][5] = true; // isString - $this->attachment[$cur][6] = 'attachment'; - $this->attachment[$cur][7] = 0; - } - - /** - * Adds an embedded attachment. This can include images, sounds, and - * just about any other document. Make sure to set the $type to an - * image type. For JPEG images use "image/jpeg" and for GIF images - * use "image/gif". - * @param string $path Path to the attachment. - * @param string $cid Content ID of the attachment. Use this to identify - * the Id for accessing the image in an HTML form. - * @param string $name Overrides the attachment name. - * @param string $encoding File encoding (see $Encoding). - * @param string $type File extension (MIME) type. - * @return bool - */ - function AddEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = 'application/octet-stream') { - - if(!@is_file($path)) { - $this->SetError($this->Lang('file_access') . $path); - return false; - } - - $filename = basename($path); - if($name == '') { - $name = $filename; - } - - /* Append to $attachment array */ - $cur = count($this->attachment); - $this->attachment[$cur][0] = $path; - $this->attachment[$cur][1] = $filename; - $this->attachment[$cur][2] = $name; - $this->attachment[$cur][3] = $encoding; - $this->attachment[$cur][4] = $type; - $this->attachment[$cur][5] = false; - $this->attachment[$cur][6] = 'inline'; - $this->attachment[$cur][7] = $cid; - - return true; - } - - /** - * Returns true if an inline attachment is present. - * @access private - * @return bool - */ - function InlineImageExists() { - $result = false; - for($i = 0; $i < count($this->attachment); $i++) { - if($this->attachment[$i][6] == 'inline') { - $result = true; - break; - } - } - - return $result; - } - - ///////////////////////////////////////////////// - // CLASS METHODS, MESSAGE RESET - ///////////////////////////////////////////////// - - /** - * Clears all recipients assigned in the TO array. Returns void. - * @return void - */ - function ClearAddresses() { - $this->to = array(); - } - - /** - * Clears all recipients assigned in the CC array. Returns void. - * @return void - */ - function ClearCCs() { - $this->cc = array(); - } - - /** - * Clears all recipients assigned in the BCC array. Returns void. - * @return void - */ - function ClearBCCs() { - $this->bcc = array(); - } - - /** - * Clears all recipients assigned in the ReplyTo array. Returns void. - * @return void - */ - function ClearReplyTos() { - $this->ReplyTo = array(); - } - - /** - * Clears all recipients assigned in the TO, CC and BCC - * array. Returns void. - * @return void - */ - function ClearAllRecipients() { - $this->to = array(); - $this->cc = array(); - $this->bcc = array(); - } - - /** - * Clears all previously set filesystem, string, and binary - * attachments. Returns void. - * @return void - */ - function ClearAttachments() { - $this->attachment = array(); - } - - /** - * Clears all custom headers. Returns void. - * @return void - */ - function ClearCustomHeaders() { - $this->CustomHeader = array(); - } - - ///////////////////////////////////////////////// - // CLASS METHODS, MISCELLANEOUS - ///////////////////////////////////////////////// - - /** - * Adds the error message to the error container. - * Returns void. - * @access private - * @return void - */ - function SetError($msg) { - $this->error_count++; - $this->ErrorInfo = $msg; - } - - /** - * Returns the proper RFC 822 formatted date. - * @access private - * @return string - */ - function RFCDate() { - $tz = date('Z'); - $tzs = ($tz < 0) ? '-' : '+'; - $tz = abs($tz); - $tz = (int)($tz/3600)*100 + ($tz%3600)/60; - $result = sprintf("%s %s%04d", date('D, j M Y H:i:s'), $tzs, $tz); - - return $result; - } - - /** - * Returns the appropriate server variable. Should work with both - * PHP 4.1.0+ as well as older versions. Returns an empty string - * if nothing is found. - * @access private - * @return mixed - */ - function ServerVar($varName) { - global $HTTP_SERVER_VARS; - global $HTTP_ENV_VARS; - - if(!isset($_SERVER)) { - $_SERVER = $HTTP_SERVER_VARS; - if(!isset($_SERVER['REMOTE_ADDR'])) { - $_SERVER = $HTTP_ENV_VARS; // must be Apache - } - } - - if(isset($_SERVER[$varName])) { - return $_SERVER[$varName]; - } else { - return ''; - } - } - - /** - * Returns the server hostname or 'localhost.localdomain' if unknown. - * @access private - * @return string - */ - function ServerHostname() { - if ($this->Hostname != '') { - $result = $this->Hostname; - } elseif ($this->ServerVar('SERVER_NAME') != '') { - $result = $this->ServerVar('SERVER_NAME'); - } else { - $result = 'localhost.localdomain'; - } - - return $result; - } - - /** - * Returns a message in the appropriate language. - * @access private - * @return string - */ - function Lang($key) { - if(count($this->language) < 1) { - $this->SetLanguage('en'); // set the default language - } - - if(isset($this->language[$key])) { - return $this->language[$key]; - } else { - return 'Language string failed to load: ' . $key; - } - } - - /** - * Returns true if an error occurred. - * @return bool - */ - function IsError() { - return ($this->error_count > 0); - } - - /** - * Changes every end of line from CR or LF to CRLF. - * @access private - * @return string - */ - function FixEOL($str) { - $str = str_replace("\r\n", "\n", $str); - $str = str_replace("\r", "\n", $str); - $str = str_replace("\n", $this->LE, $str); - return $str; - } - - /** - * Adds a custom header. - * @return void - */ - function AddCustomHeader($custom_header) { - $this->CustomHeader[] = explode(':', $custom_header, 2); - } - - /** - * Evaluates the message and returns modifications for inline images and backgrounds - * @access public - * @return $message - */ - function MsgHTML($message) { - preg_match_all("/(src|background)=\"(.*)\"/Ui", $message, $images); - if(isset($images[2])) { - foreach($images[2] as $i => $url) { - $filename = basename($url); - $directory = dirname($url); - $cid = 'cid:' . md5($filename); - $fileParts = split("\.", $filename); - $ext = $fileParts[1]; - $mimeType = $this->_mime_types($ext); - $message = preg_replace("/".$images[1][$i]."=\"".preg_quote($url, '/')."\"/Ui", $images[1][$i]."=\"".$cid."\"", $message); - $this->AddEmbeddedImage($url, md5($filename), $filename, 'base64', $mimeType); - } - } - $this->IsHTML(true); - $this->Body = $message; - $textMsg = trim(strip_tags($message)); - if ( !empty($textMsg) && empty($this->AltBody) ) { - $this->AltBody = $textMsg; - } - if ( empty($this->AltBody) ) { - $this->AltBody = 'To view this email message, open the email in with HTML compatibility!' . "\n\n"; - } - } - - /** - * Gets the mime type of the embedded or inline image - * @access private - * @return mime type of ext - */ - function _mime_types($ext = '') { - $mimes = array( - 'hqx' => 'application/mac-binhex40', - 'cpt' => 'application/mac-compactpro', - 'doc' => 'application/msword', - 'bin' => 'application/macbinary', - 'dms' => 'application/octet-stream', - 'lha' => 'application/octet-stream', - 'lzh' => 'application/octet-stream', - 'exe' => 'application/octet-stream', - 'class' => 'application/octet-stream', - 'psd' => 'application/octet-stream', - 'so' => 'application/octet-stream', - 'sea' => 'application/octet-stream', - 'dll' => 'application/octet-stream', - 'oda' => 'application/oda', - 'pdf' => 'application/pdf', - 'ai' => 'application/postscript', - 'eps' => 'application/postscript', - 'ps' => 'application/postscript', - 'smi' => 'application/smil', - 'smil' => 'application/smil', - 'mif' => 'application/vnd.mif', - 'xls' => 'application/vnd.ms-excel', - 'ppt' => 'application/vnd.ms-powerpoint', - 'wbxml' => 'application/vnd.wap.wbxml', - 'wmlc' => 'application/vnd.wap.wmlc', - 'dcr' => 'application/x-director', - 'dir' => 'application/x-director', - 'dxr' => 'application/x-director', - 'dvi' => 'application/x-dvi', - 'gtar' => 'application/x-gtar', - 'php' => 'application/x-httpd-php', - 'php4' => 'application/x-httpd-php', - 'php3' => 'application/x-httpd-php', - 'phtml' => 'application/x-httpd-php', - 'phps' => 'application/x-httpd-php-source', - 'js' => 'application/x-javascript', - 'swf' => 'application/x-shockwave-flash', - 'sit' => 'application/x-stuffit', - 'tar' => 'application/x-tar', - 'tgz' => 'application/x-tar', - 'xhtml' => 'application/xhtml+xml', - 'xht' => 'application/xhtml+xml', - 'zip' => 'application/zip', - 'mid' => 'audio/midi', - 'midi' => 'audio/midi', - 'mpga' => 'audio/mpeg', - 'mp2' => 'audio/mpeg', - 'mp3' => 'audio/mpeg', - 'aif' => 'audio/x-aiff', - 'aiff' => 'audio/x-aiff', - 'aifc' => 'audio/x-aiff', - 'ram' => 'audio/x-pn-realaudio', - 'rm' => 'audio/x-pn-realaudio', - 'rpm' => 'audio/x-pn-realaudio-plugin', - 'ra' => 'audio/x-realaudio', - 'rv' => 'video/vnd.rn-realvideo', - 'wav' => 'audio/x-wav', - 'bmp' => 'image/bmp', - 'gif' => 'image/gif', - 'jpeg' => 'image/jpeg', - 'jpg' => 'image/jpeg', - 'jpe' => 'image/jpeg', - 'png' => 'image/png', - 'tiff' => 'image/tiff', - 'tif' => 'image/tiff', - 'css' => 'text/css', - 'html' => 'text/html', - 'htm' => 'text/html', - 'shtml' => 'text/html', - 'txt' => 'text/plain', - 'text' => 'text/plain', - 'log' => 'text/plain', - 'rtx' => 'text/richtext', - 'rtf' => 'text/rtf', - 'xml' => 'text/xml', - 'xsl' => 'text/xml', - 'mpeg' => 'video/mpeg', - 'mpg' => 'video/mpeg', - 'mpe' => 'video/mpeg', - 'qt' => 'video/quicktime', - 'mov' => 'video/quicktime', - 'avi' => 'video/x-msvideo', - 'movie' => 'video/x-sgi-movie', - 'doc' => 'application/msword', - 'word' => 'application/msword', - 'xl' => 'application/excel', - 'eml' => 'message/rfc822' - ); - return ( ! isset($mimes[strtolower($ext)])) ? 'application/x-unknown-content-type' : $mimes[strtolower($ext)]; - } - - /** - * Set (or reset) Class Objects (variables) - * - * Usage Example: - * $page->set('X-Priority', '3'); - * - * @access public - * @param string $name Parameter Name - * @param mixed $value Parameter Value - * NOTE: will not work with arrays, there are no arrays to set/reset - */ - function set ( $name, $value = '' ) { - if ( isset($this->$name) ) { - $this->$name = $value; - } else { - $this->SetError('Cannot set or reset variable ' . $name); - return false; - } - } - - /** - * Read a file from a supplied filename and return it. - * - * @access public - * @param string $filename Parameter File Name - */ - function getFile($filename) { - $return = ''; - if ($fp = fopen($filename, 'rb')) { - while (!feof($fp)) { - $return .= fread($fp, 1024); - } - fclose($fp); - return $return; - } else { - return false; - } - } - - /** - * Strips newlines to prevent header injection. - * @access private - * @param string $str String - * @return string - */ - function SecureHeader($str) { - $str = trim($str); - $str = str_replace("\r", "", $str); - $str = str_replace("\n", "", $str); - return $str; - } - -} - -?> diff --git a/phpmailer/class.pop3.php b/phpmailer/class.pop3.php deleted file mode 100644 index af1e63b..0000000 --- a/phpmailer/class.pop3.php +++ /dev/null @@ -1,437 +0,0 @@ -pop_conn = 0; - $this->connected = false; - $this->error = null; - } - - /** - * Combination of public events - connect, login, disconnect - * - * @param string $host - * @param integer $port - * @param integer $tval - * @param string $username - * @param string $password - */ - function Authorise ($host, $port = false, $tval = false, $username, $password, $debug_level = 0) - { - $this->host = $host; - - // If no port value is passed, retrieve it - if ($port == false) - { - $this->port = $this->POP3_PORT; - } - else - { - $this->port = $port; - } - - // If no port value is passed, retrieve it - if ($tval == false) - { - $this->tval = $this->POP3_TIMEOUT; - } - else - { - $this->tval = $tval; - } - - $this->do_debug = $debug_level; - $this->username = $username; - $this->password = $password; - - // Refresh the error log - $this->error = null; - - // Connect - $result = $this->Connect($this->host, $this->port, $this->tval); - - if ($result) - { - $login_result = $this->Login($this->username, $this->password); - - if ($login_result) - { - $this->Disconnect(); - - return true; - } - - } - - // We need to disconnect regardless if the login succeeded - $this->Disconnect(); - - return false; - } - - /** - * Connect to the POP3 server - * - * @param string $host - * @param integer $port - * @param integer $tval - * @return boolean - */ - function Connect ($host, $port = false, $tval = 30) - { - // Are we already connected? - if ($this->connected) - { - return true; - } - - /* - On Windows this will raise a PHP Warning error if the hostname doesn't exist. - Rather than supress it with @fsockopen, let's capture it cleanly instead - */ - - set_error_handler(array(&$this, 'catchWarning')); - - // Connect to the POP3 server - $this->pop_conn = fsockopen($host, // POP3 Host - $port, // Port # - $errno, // Error Number - $errstr, // Error Message - $tval); // Timeout (seconds) - - // Restore the error handler - restore_error_handler(); - - // Does the Error Log now contain anything? - if ($this->error && $this->do_debug >= 1) - { - $this->displayErrors(); - } - - // Did we connect? - if ($this->pop_conn == false) - { - // It would appear not... - $this->error = array( - 'error' => "Failed to connect to server $host on port $port", - 'errno' => $errno, - 'errstr' => $errstr - ); - - if ($this->do_debug >= 1) - { - $this->displayErrors(); - } - - return false; - } - - // Increase the stream time-out - - // Check for PHP 4.3.0 or later - if (version_compare(phpversion(), '4.3.0', 'ge')) - { - stream_set_timeout($this->pop_conn, $tval, 0); - } - else - { - // Does not work on Windows - if (substr(PHP_OS, 0, 3) !== 'WIN') - { - socket_set_timeout($this->pop_conn, $tval, 0); - } - } - - // Get the POP3 server response - $pop3_response = $this->getResponse(); - - // Check for the +OK - if ($this->checkResponse($pop3_response)) - { - // The connection is established and the POP3 server is talking - $this->connected = true; - return true; - } - - } - - /** - * Login to the POP3 server (does not support APOP yet) - * - * @param string $username - * @param string $password - * @return boolean - */ - function Login ($username = '', $password = '') - { - if ($this->connected == false) - { - $this->error = 'Not connected to POP3 server'; - - if ($this->do_debug >= 1) - { - $this->displayErrors(); - } - } - - if (empty($username)) - { - $username = $this->username; - } - - if (empty($password)) - { - $password = $this->password; - } - - $pop_username = "USER $username" . $this->CRLF; - $pop_password = "PASS $password" . $this->CRLF; - - // Send the Username - $this->sendString($pop_username); - $pop3_response = $this->getResponse(); - - if ($this->checkResponse($pop3_response)) - { - // Send the Password - $this->sendString($pop_password); - $pop3_response = $this->getResponse(); - - if ($this->checkResponse($pop3_response)) - { - return true; - } - else - { - return false; - } - } - else - { - return false; - } - } - - /** - * Disconnect from the POP3 server - */ - function Disconnect () - { - $this->sendString('QUIT'); - - fclose($this->pop_conn); - } - - /* - --------------- - Private Methods - --------------- - */ - - /** - * Get the socket response back. - * $size is the maximum number of bytes to retrieve - * - * @param integer $size - * @return string - */ - function getResponse ($size = 128) - { - $pop3_response = fgets($this->pop_conn, $size); - - return $pop3_response; - } - - /** - * Send a string down the open socket connection to the POP3 server - * - * @param string $string - * @return integer - */ - function sendString ($string) - { - $bytes_sent = fwrite($this->pop_conn, $string, strlen($string)); - - return $bytes_sent; - - } - - /** - * Checks the POP3 server response for +OK or -ERR - * - * @param string $string - * @return boolean - */ - function checkResponse ($string) - { - if (substr($string, 0, 3) !== '+OK') - { - $this->error = array( - 'error' => "Server reported an error: $string", - 'errno' => 0, - 'errstr' => '' - ); - - if ($this->do_debug >= 1) - { - $this->displayErrors(); - } - - return false; - } - else - { - return true; - } - - } - - /** - * If debug is enabled, display the error message array - * - */ - function displayErrors () - { - echo '

    ';
    -
    -      foreach ($this->error as $single_error)
    -    {
    -        print_r($single_error);
    -    }
    -
    -      echo '
    '; - } - - /** - * Takes over from PHP for the socket warning handler - * - * @param integer $errno - * @param string $errstr - * @param string $errfile - * @param integer $errline - */ - function catchWarning ($errno, $errstr, $errfile, $errline) - { - $this->error[] = array( - 'error' => "Connecting to the POP3 server raised a PHP warning: ", - 'errno' => $errno, - 'errstr' => $errstr - ); - } - - // End of class -} -?> diff --git a/phpmailer/class.smtp.php b/phpmailer/class.smtp.php deleted file mode 100644 index 57088b5..0000000 --- a/phpmailer/class.smtp.php +++ /dev/null @@ -1,1062 +0,0 @@ -smtp_conn = 0; - $this->error = null; - $this->helo_rply = null; - - $this->do_debug = 0; - } - - /************************************************************* - * CONNECTION FUNCTIONS * - ***********************************************************/ - - /** - * Connect to the server specified on the port specified. - * If the port is not specified use the default SMTP_PORT. - * If tval is specified then a connection will try and be - * established with the server for that number of seconds. - * If tval is not specified the default is 30 seconds to - * try on the connection. - * - * SMTP CODE SUCCESS: 220 - * SMTP CODE FAILURE: 421 - * @access public - * @return bool - */ - function Connect($host,$port=0,$tval=30) { - # set the error val to null so there is no confusion - $this->error = null; - - # make sure we are __not__ connected - if($this->connected()) { - # ok we are connected! what should we do? - # for now we will just give an error saying we - # are already connected - $this->error = array("error" => "Already connected to a server"); - return false; - } - - if(empty($port)) { - $port = $this->SMTP_PORT; - } - - #connect to the smtp server - $this->smtp_conn = fsockopen($host, # the host of the server - $port, # the port to use - $errno, # error number if any - $errstr, # error message if any - $tval); # give up after ? secs - # verify we connected properly - if(empty($this->smtp_conn)) { - $this->error = array("error" => "Failed to connect to server", - "errno" => $errno, - "errstr" => $errstr); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . - ": $errstr ($errno)" . $this->CRLF; - } - return false; - } - - # sometimes the SMTP server takes a little longer to respond - # so we will give it a longer timeout for the first read - // Windows still does not have support for this timeout function - if(substr(PHP_OS, 0, 3) != "WIN") - socket_set_timeout($this->smtp_conn, $tval, 0); - - # get any announcement stuff - $announce = $this->get_lines(); - - # set the timeout of any socket functions at 1/10 of a second - //if(function_exists("socket_set_timeout")) - // socket_set_timeout($this->smtp_conn, 0, 100000); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $this->CRLF . $announce; - } - - return true; - } - - /** - * Performs SMTP authentication. Must be run after running the - * Hello() method. Returns true if successfully authenticated. - * @access public - * @return bool - */ - function Authenticate($username, $password) { - // Start authentication - fputs($this->smtp_conn,"AUTH LOGIN" . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($code != 334) { - $this->error = - array("error" => "AUTH not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . - ": " . $rply . $this->CRLF; - } - return false; - } - - // Send encoded username - fputs($this->smtp_conn, base64_encode($username) . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($code != 334) { - $this->error = - array("error" => "Username not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . - ": " . $rply . $this->CRLF; - } - return false; - } - - // Send encoded password - fputs($this->smtp_conn, base64_encode($password) . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($code != 235) { - $this->error = - array("error" => "Password not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . - ": " . $rply . $this->CRLF; - } - return false; - } - - return true; - } - - /** - * Returns true if connected to a server otherwise false - * @access private - * @return bool - */ - function Connected() { - if(!empty($this->smtp_conn)) { - $sock_status = socket_get_status($this->smtp_conn); - if($sock_status["eof"]) { - # hmm this is an odd situation... the socket is - # valid but we are not connected anymore - if($this->do_debug >= 1) { - echo "SMTP -> NOTICE:" . $this->CRLF . - "EOF caught while checking if connected"; - } - $this->Close(); - return false; - } - return true; # everything looks good - } - return false; - } - - /** - * Closes the socket and cleans up the state of the class. - * It is not considered good to use this function without - * first trying to use QUIT. - * @access public - * @return void - */ - function Close() { - $this->error = null; # so there is no confusion - $this->helo_rply = null; - if(!empty($this->smtp_conn)) { - # close the connection and cleanup - fclose($this->smtp_conn); - $this->smtp_conn = 0; - } - } - - /*************************************************************** - * SMTP COMMANDS * - *************************************************************/ - - /** - * Issues a data command and sends the msg_data to the server - * finializing the mail transaction. $msg_data is the message - * that is to be send with the headers. Each header needs to be - * on a single line followed by a with the message headers - * and the message body being seperated by and additional . - * - * Implements rfc 821: DATA - * - * SMTP CODE INTERMEDIATE: 354 - * [data] - * . - * SMTP CODE SUCCESS: 250 - * SMTP CODE FAILURE: 552,554,451,452 - * SMTP CODE FAILURE: 451,554 - * SMTP CODE ERROR : 500,501,503,421 - * @access public - * @return bool - */ - function Data($msg_data) { - $this->error = null; # so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - "error" => "Called Data() without being connected"); - return false; - } - - fputs($this->smtp_conn,"DATA" . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; - } - - if($code != 354) { - $this->error = - array("error" => "DATA command not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . - ": " . $rply . $this->CRLF; - } - return false; - } - - # the server is ready to accept data! - # according to rfc 821 we should not send more than 1000 - # including the CRLF - # characters on a single line so we will break the data up - # into lines by \r and/or \n then if needed we will break - # each of those into smaller lines to fit within the limit. - # in addition we will be looking for lines that start with - # a period '.' and append and additional period '.' to that - # line. NOTE: this does not count towards are limit. - - # normalize the line breaks so we know the explode works - $msg_data = str_replace("\r\n","\n",$msg_data); - $msg_data = str_replace("\r","\n",$msg_data); - $lines = explode("\n",$msg_data); - - # we need to find a good way to determine is headers are - # in the msg_data or if it is a straight msg body - # currently I am assuming rfc 822 definitions of msg headers - # and if the first field of the first line (':' sperated) - # does not contain a space then it _should_ be a header - # and we can process all lines before a blank "" line as - # headers. - $field = substr($lines[0],0,strpos($lines[0],":")); - $in_headers = false; - if(!empty($field) && !strstr($field," ")) { - $in_headers = true; - } - - $max_line_length = 998; # used below; set here for ease in change - - while(list(,$line) = @each($lines)) { - $lines_out = null; - if($line == "" && $in_headers) { - $in_headers = false; - } - # ok we need to break this line up into several - # smaller lines - while(strlen($line) > $max_line_length) { - $pos = strrpos(substr($line,0,$max_line_length)," "); - - # Patch to fix DOS attack - if(!$pos) { - $pos = $max_line_length - 1; - } - - $lines_out[] = substr($line,0,$pos); - $line = substr($line,$pos + 1); - # if we are processing headers we need to - # add a LWSP-char to the front of the new line - # rfc 822 on long msg headers - if($in_headers) { - $line = "\t" . $line; - } - } - $lines_out[] = $line; - - # now send the lines to the server - while(list(,$line_out) = @each($lines_out)) { - if(strlen($line_out) > 0) - { - if(substr($line_out, 0, 1) == ".") { - $line_out = "." . $line_out; - } - } - fputs($this->smtp_conn,$line_out . $this->CRLF); - } - } - - # ok all the message data has been sent so lets get this - # over with aleady - fputs($this->smtp_conn, $this->CRLF . "." . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; - } - - if($code != 250) { - $this->error = - array("error" => "DATA not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . - ": " . $rply . $this->CRLF; - } - return false; - } - return true; - } - - /** - * Expand takes the name and asks the server to list all the - * people who are members of the _list_. Expand will return - * back and array of the result or false if an error occurs. - * Each value in the array returned has the format of: - * [ ] - * The definition of is defined in rfc 821 - * - * Implements rfc 821: EXPN - * - * SMTP CODE SUCCESS: 250 - * SMTP CODE FAILURE: 550 - * SMTP CODE ERROR : 500,501,502,504,421 - * @access public - * @return string array - */ - function Expand($name) { - $this->error = null; # so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - "error" => "Called Expand() without being connected"); - return false; - } - - fputs($this->smtp_conn,"EXPN " . $name . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; - } - - if($code != 250) { - $this->error = - array("error" => "EXPN not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . - ": " . $rply . $this->CRLF; - } - return false; - } - - # parse the reply and place in our array to return to user - $entries = explode($this->CRLF,$rply); - while(list(,$l) = @each($entries)) { - $list[] = substr($l,4); - } - - return $list; - } - - /** - * Sends the HELO command to the smtp server. - * This makes sure that we and the server are in - * the same known state. - * - * Implements from rfc 821: HELO - * - * SMTP CODE SUCCESS: 250 - * SMTP CODE ERROR : 500, 501, 504, 421 - * @access public - * @return bool - */ - function Hello($host="") { - $this->error = null; # so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - "error" => "Called Hello() without being connected"); - return false; - } - - # if a hostname for the HELO was not specified determine - # a suitable one to send - if(empty($host)) { - # we need to determine some sort of appopiate default - # to send to the server - $host = "localhost"; - } - - // Send extended hello first (RFC 2821) - if(!$this->SendHello("EHLO", $host)) - { - if(!$this->SendHello("HELO", $host)) - return false; - } - - return true; - } - - /** - * Sends a HELO/EHLO command. - * @access private - * @return bool - */ - function SendHello($hello, $host) { - fputs($this->smtp_conn, $hello . " " . $host . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER: " . $this->CRLF . $rply; - } - - if($code != 250) { - $this->error = - array("error" => $hello . " not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . - ": " . $rply . $this->CRLF; - } - return false; - } - - $this->helo_rply = $rply; - - return true; - } - - /** - * Gets help information on the keyword specified. If the keyword - * is not specified then returns generic help, ussually contianing - * A list of keywords that help is available on. This function - * returns the results back to the user. It is up to the user to - * handle the returned data. If an error occurs then false is - * returned with $this->error set appropiately. - * - * Implements rfc 821: HELP [ ] - * - * SMTP CODE SUCCESS: 211,214 - * SMTP CODE ERROR : 500,501,502,504,421 - * @access public - * @return string - */ - function Help($keyword="") { - $this->error = null; # to avoid confusion - - if(!$this->connected()) { - $this->error = array( - "error" => "Called Help() without being connected"); - return false; - } - - $extra = ""; - if(!empty($keyword)) { - $extra = " " . $keyword; - } - - fputs($this->smtp_conn,"HELP" . $extra . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; - } - - if($code != 211 && $code != 214) { - $this->error = - array("error" => "HELP not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . - ": " . $rply . $this->CRLF; - } - return false; - } - - return $rply; - } - - /** - * Starts a mail transaction from the email address specified in - * $from. Returns true if successful or false otherwise. If True - * the mail transaction is started and then one or more Recipient - * commands may be called followed by a Data command. - * - * Implements rfc 821: MAIL FROM: - * - * SMTP CODE SUCCESS: 250 - * SMTP CODE SUCCESS: 552,451,452 - * SMTP CODE SUCCESS: 500,501,421 - * @access public - * @return bool - */ - function Mail($from) { - $this->error = null; # so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - "error" => "Called Mail() without being connected"); - return false; - } - - $useVerp = ($this->do_verp ? "XVERP" : ""); - fputs($this->smtp_conn,"MAIL FROM:<" . $from . ">" . $useVerp . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; - } - - if($code != 250) { - $this->error = - array("error" => "MAIL not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . - ": " . $rply . $this->CRLF; - } - return false; - } - return true; - } - - /** - * Sends the command NOOP to the SMTP server. - * - * Implements from rfc 821: NOOP - * - * SMTP CODE SUCCESS: 250 - * SMTP CODE ERROR : 500, 421 - * @access public - * @return bool - */ - function Noop() { - $this->error = null; # so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - "error" => "Called Noop() without being connected"); - return false; - } - - fputs($this->smtp_conn,"NOOP" . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; - } - - if($code != 250) { - $this->error = - array("error" => "NOOP not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . - ": " . $rply . $this->CRLF; - } - return false; - } - return true; - } - - /** - * Sends the quit command to the server and then closes the socket - * if there is no error or the $close_on_error argument is true. - * - * Implements from rfc 821: QUIT - * - * SMTP CODE SUCCESS: 221 - * SMTP CODE ERROR : 500 - * @access public - * @return bool - */ - function Quit($close_on_error=true) { - $this->error = null; # so there is no confusion - - if(!$this->connected()) { - $this->error = array( - "error" => "Called Quit() without being connected"); - return false; - } - - # send the quit command to the server - fputs($this->smtp_conn,"quit" . $this->CRLF); - - # get any good-bye messages - $byemsg = $this->get_lines(); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $this->CRLF . $byemsg; - } - - $rval = true; - $e = null; - - $code = substr($byemsg,0,3); - if($code != 221) { - # use e as a tmp var cause Close will overwrite $this->error - $e = array("error" => "SMTP server rejected quit command", - "smtp_code" => $code, - "smtp_rply" => substr($byemsg,4)); - $rval = false; - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $e["error"] . ": " . - $byemsg . $this->CRLF; - } - } - - if(empty($e) || $close_on_error) { - $this->Close(); - } - - return $rval; - } - - /** - * Sends the command RCPT to the SMTP server with the TO: argument of $to. - * Returns true if the recipient was accepted false if it was rejected. - * - * Implements from rfc 821: RCPT TO: - * - * SMTP CODE SUCCESS: 250,251 - * SMTP CODE FAILURE: 550,551,552,553,450,451,452 - * SMTP CODE ERROR : 500,501,503,421 - * @access public - * @return bool - */ - function Recipient($to) { - $this->error = null; # so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - "error" => "Called Recipient() without being connected"); - return false; - } - - fputs($this->smtp_conn,"RCPT TO:<" . $to . ">" . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; - } - - if($code != 250 && $code != 251) { - $this->error = - array("error" => "RCPT not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . - ": " . $rply . $this->CRLF; - } - return false; - } - return true; - } - - /** - * Sends the RSET command to abort and transaction that is - * currently in progress. Returns true if successful false - * otherwise. - * - * Implements rfc 821: RSET - * - * SMTP CODE SUCCESS: 250 - * SMTP CODE ERROR : 500,501,504,421 - * @access public - * @return bool - */ - function Reset() { - $this->error = null; # so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - "error" => "Called Reset() without being connected"); - return false; - } - - fputs($this->smtp_conn,"RSET" . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; - } - - if($code != 250) { - $this->error = - array("error" => "RSET failed", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . - ": " . $rply . $this->CRLF; - } - return false; - } - - return true; - } - - /** - * Starts a mail transaction from the email address specified in - * $from. Returns true if successful or false otherwise. If True - * the mail transaction is started and then one or more Recipient - * commands may be called followed by a Data command. This command - * will send the message to the users terminal if they are logged - * in. - * - * Implements rfc 821: SEND FROM: - * - * SMTP CODE SUCCESS: 250 - * SMTP CODE SUCCESS: 552,451,452 - * SMTP CODE SUCCESS: 500,501,502,421 - * @access public - * @return bool - */ - function Send($from) { - $this->error = null; # so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - "error" => "Called Send() without being connected"); - return false; - } - - fputs($this->smtp_conn,"SEND FROM:" . $from . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; - } - - if($code != 250) { - $this->error = - array("error" => "SEND not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . - ": " . $rply . $this->CRLF; - } - return false; - } - return true; - } - - /** - * Starts a mail transaction from the email address specified in - * $from. Returns true if successful or false otherwise. If True - * the mail transaction is started and then one or more Recipient - * commands may be called followed by a Data command. This command - * will send the message to the users terminal if they are logged - * in and send them an email. - * - * Implements rfc 821: SAML FROM: - * - * SMTP CODE SUCCESS: 250 - * SMTP CODE SUCCESS: 552,451,452 - * SMTP CODE SUCCESS: 500,501,502,421 - * @access public - * @return bool - */ - function SendAndMail($from) { - $this->error = null; # so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - "error" => "Called SendAndMail() without being connected"); - return false; - } - - fputs($this->smtp_conn,"SAML FROM:" . $from . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; - } - - if($code != 250) { - $this->error = - array("error" => "SAML not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . - ": " . $rply . $this->CRLF; - } - return false; - } - return true; - } - - /** - * Starts a mail transaction from the email address specified in - * $from. Returns true if successful or false otherwise. If True - * the mail transaction is started and then one or more Recipient - * commands may be called followed by a Data command. This command - * will send the message to the users terminal if they are logged - * in or mail it to them if they are not. - * - * Implements rfc 821: SOML FROM: - * - * SMTP CODE SUCCESS: 250 - * SMTP CODE SUCCESS: 552,451,452 - * SMTP CODE SUCCESS: 500,501,502,421 - * @access public - * @return bool - */ - function SendOrMail($from) { - $this->error = null; # so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - "error" => "Called SendOrMail() without being connected"); - return false; - } - - fputs($this->smtp_conn,"SOML FROM:" . $from . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; - } - - if($code != 250) { - $this->error = - array("error" => "SOML not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . - ": " . $rply . $this->CRLF; - } - return false; - } - return true; - } - - /** - * This is an optional command for SMTP that this class does not - * support. This method is here to make the RFC821 Definition - * complete for this class and __may__ be implimented in the future - * - * Implements from rfc 821: TURN - * - * SMTP CODE SUCCESS: 250 - * SMTP CODE FAILURE: 502 - * SMTP CODE ERROR : 500, 503 - * @access public - * @return bool - */ - function Turn() { - $this->error = array("error" => "This method, TURN, of the SMTP ". - "is not implemented"); - if($this->do_debug >= 1) { - echo "SMTP -> NOTICE: " . $this->error["error"] . $this->CRLF; - } - return false; - } - - /** - * Verifies that the name is recognized by the server. - * Returns false if the name could not be verified otherwise - * the response from the server is returned. - * - * Implements rfc 821: VRFY - * - * SMTP CODE SUCCESS: 250,251 - * SMTP CODE FAILURE: 550,551,553 - * SMTP CODE ERROR : 500,501,502,421 - * @access public - * @return int - */ - function Verify($name) { - $this->error = null; # so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - "error" => "Called Verify() without being connected"); - return false; - } - - fputs($this->smtp_conn,"VRFY " . $name . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; - } - - if($code != 250 && $code != 251) { - $this->error = - array("error" => "VRFY failed on name '$name'", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . - ": " . $rply . $this->CRLF; - } - return false; - } - return $rply; - } - - /******************************************************************* - * INTERNAL FUNCTIONS * - ******************************************************************/ - - /** - * Read in as many lines as possible - * either before eof or socket timeout occurs on the operation. - * With SMTP we can tell if we have more lines to read if the - * 4th character is '-' symbol. If it is a space then we don't - * need to read anything else. - * @access private - * @return string - */ - function get_lines() { - $data = ""; - while($str = @fgets($this->smtp_conn,515)) { - if($this->do_debug >= 4) { - echo "SMTP -> get_lines(): \$data was \"$data\"" . - $this->CRLF; - echo "SMTP -> get_lines(): \$str is \"$str\"" . - $this->CRLF; - } - $data .= $str; - if($this->do_debug >= 4) { - echo "SMTP -> get_lines(): \$data is \"$data\"" . $this->CRLF; - } - # if the 4th character is a space then we are done reading - # so just break the loop - if(substr($str,3,1) == " ") { break; } - } - return $data; - } - -} - - - ?> diff --git a/phpmailer/language/phpmailer.lang-br.php b/phpmailer/language/phpmailer.lang-br.php deleted file mode 100644 index 681914c..0000000 --- a/phpmailer/language/phpmailer.lang-br.php +++ /dev/null @@ -1,23 +0,0 @@ - diff --git a/phpmailer/language/phpmailer.lang-ca.php b/phpmailer/language/phpmailer.lang-ca.php deleted file mode 100644 index 3a9615a..0000000 --- a/phpmailer/language/phpmailer.lang-ca.php +++ /dev/null @@ -1,24 +0,0 @@ - \ No newline at end of file diff --git a/phpmailer/language/phpmailer.lang-cz.php b/phpmailer/language/phpmailer.lang-cz.php deleted file mode 100644 index 11568ee..0000000 --- a/phpmailer/language/phpmailer.lang-cz.php +++ /dev/null @@ -1,26 +0,0 @@ - \ No newline at end of file diff --git a/phpmailer/language/phpmailer.lang-de.php b/phpmailer/language/phpmailer.lang-de.php deleted file mode 100644 index d5917ee..0000000 --- a/phpmailer/language/phpmailer.lang-de.php +++ /dev/null @@ -1,25 +0,0 @@ - diff --git a/phpmailer/language/phpmailer.lang-dk.php b/phpmailer/language/phpmailer.lang-dk.php deleted file mode 100644 index ca05f50..0000000 --- a/phpmailer/language/phpmailer.lang-dk.php +++ /dev/null @@ -1,26 +0,0 @@ - - */ - -$PHPMAILER_LANG = array(); - -$PHPMAILER_LANG["provide_address"] = 'Du skal indtaste mindst en ' . - 'modtagers emailadresse.'; -$PHPMAILER_LANG["mailer_not_supported"] = ' mailer understøttes ikke.'; -$PHPMAILER_LANG["execute"] = 'Kunne ikke køre: '; -$PHPMAILER_LANG["instantiate"] = 'Kunne ikke initialisere email funktionen.'; -$PHPMAILER_LANG["authenticate"] = 'SMTP fejl: Kunne ikke logge på.'; -$PHPMAILER_LANG["from_failed"] = 'Følgende afsenderadresse er forkert: '; -$PHPMAILER_LANG["recipients_failed"] = 'SMTP fejl: Følgende' . - 'modtagere er forkerte: '; -$PHPMAILER_LANG["data_not_accepted"] = 'SMTP fejl: Data kunne ikke accepteres.'; -$PHPMAILER_LANG["connect_host"] = 'SMTP fejl: Kunne ikke tilslutte SMTP serveren.'; -$PHPMAILER_LANG["file_access"] = 'Ingen adgang til fil: '; -$PHPMAILER_LANG["file_open"] = 'Fil fejl: Kunne ikke åbne filen: '; -$PHPMAILER_LANG["encoding"] = 'Ukendt encode-format: '; -?> \ No newline at end of file diff --git a/phpmailer/language/phpmailer.lang-en.php b/phpmailer/language/phpmailer.lang-en.php deleted file mode 100644 index 3422134..0000000 --- a/phpmailer/language/phpmailer.lang-en.php +++ /dev/null @@ -1,25 +0,0 @@ - diff --git a/phpmailer/language/phpmailer.lang-es.php b/phpmailer/language/phpmailer.lang-es.php deleted file mode 100644 index 47f64ed..0000000 --- a/phpmailer/language/phpmailer.lang-es.php +++ /dev/null @@ -1,25 +0,0 @@ - \ No newline at end of file diff --git a/phpmailer/language/phpmailer.lang-fi.php b/phpmailer/language/phpmailer.lang-fi.php deleted file mode 100644 index 02ba6b7..0000000 --- a/phpmailer/language/phpmailer.lang-fi.php +++ /dev/null @@ -1,25 +0,0 @@ - \ No newline at end of file diff --git a/phpmailer/language/phpmailer.lang-fo.php b/phpmailer/language/phpmailer.lang-fo.php deleted file mode 100644 index bc06b76..0000000 --- a/phpmailer/language/phpmailer.lang-fo.php +++ /dev/null @@ -1,27 +0,0 @@ - diff --git a/phpmailer/language/phpmailer.lang-fr.php b/phpmailer/language/phpmailer.lang-fr.php deleted file mode 100644 index ccea966..0000000 --- a/phpmailer/language/phpmailer.lang-fr.php +++ /dev/null @@ -1,26 +0,0 @@ - diff --git a/phpmailer/language/phpmailer.lang-hu.php b/phpmailer/language/phpmailer.lang-hu.php deleted file mode 100644 index 7ba57c4..0000000 --- a/phpmailer/language/phpmailer.lang-hu.php +++ /dev/null @@ -1,25 +0,0 @@ - \ No newline at end of file diff --git a/phpmailer/language/phpmailer.lang-it.php b/phpmailer/language/phpmailer.lang-it.php deleted file mode 100644 index 45e82df..0000000 --- a/phpmailer/language/phpmailer.lang-it.php +++ /dev/null @@ -1,29 +0,0 @@ - - */ - -$PHPMAILER_LANG = array(); - -$PHPMAILER_LANG["provide_address"] = 'Deve essere fornito almeno un'. - ' indirizzo ricevente'; -$PHPMAILER_LANG["mailer_not_supported"] = 'Mailer non supportato'; -$PHPMAILER_LANG["execute"] = "Impossibile eseguire l'operazione: "; -$PHPMAILER_LANG["instantiate"] = 'Impossibile istanziare la funzione mail'; -$PHPMAILER_LANG["authenticate"] = 'SMTP Error: Impossibile autenticarsi.'; -$PHPMAILER_LANG["from_failed"] = 'I seguenti indirizzi mittenti hanno'. - ' generato errore: '; -$PHPMAILER_LANG["recipients_failed"] = 'SMTP Error: I seguenti indirizzi'. - 'destinatari hanno generato errore: '; -$PHPMAILER_LANG["data_not_accepted"] = 'SMTP Error: Data non accettati dal'. - 'server.'; -$PHPMAILER_LANG["connect_host"] = 'SMTP Error: Impossibile connettersi'. - ' all\'host SMTP.'; -$PHPMAILER_LANG["file_access"] = 'Impossibile accedere al file: '; -$PHPMAILER_LANG["file_open"] = 'File Error: Impossibile aprire il file: '; -$PHPMAILER_LANG["encoding"] = 'Encoding set dei caratteri sconosciuto: '; -?> diff --git a/phpmailer/language/phpmailer.lang-ja.php b/phpmailer/language/phpmailer.lang-ja.php deleted file mode 100644 index 142f1bf..0000000 --- a/phpmailer/language/phpmailer.lang-ja.php +++ /dev/null @@ -1,27 +0,0 @@ - \ No newline at end of file diff --git a/phpmailer/language/phpmailer.lang-nl.php b/phpmailer/language/phpmailer.lang-nl.php deleted file mode 100644 index aa07b40..0000000 --- a/phpmailer/language/phpmailer.lang-nl.php +++ /dev/null @@ -1,25 +0,0 @@ - \ No newline at end of file diff --git a/phpmailer/language/phpmailer.lang-no.php b/phpmailer/language/phpmailer.lang-no.php deleted file mode 100644 index 5d8db67..0000000 --- a/phpmailer/language/phpmailer.lang-no.php +++ /dev/null @@ -1,25 +0,0 @@ - diff --git a/phpmailer/language/phpmailer.lang-pl.php b/phpmailer/language/phpmailer.lang-pl.php deleted file mode 100644 index 982494d..0000000 --- a/phpmailer/language/phpmailer.lang-pl.php +++ /dev/null @@ -1,26 +0,0 @@ - \ No newline at end of file diff --git a/phpmailer/language/phpmailer.lang-ro.php b/phpmailer/language/phpmailer.lang-ro.php deleted file mode 100644 index f72ec78..0000000 --- a/phpmailer/language/phpmailer.lang-ro.php +++ /dev/null @@ -1,24 +0,0 @@ - - */ - -$PHPMAILER_LANG = array(); - -$PHPMAILER_LANG["provide_address"] = 'Trebuie sa adaugati cel putin un recipient (adresa de mail).'; -$PHPMAILER_LANG["mailer_not_supported"] = ' mailer nu este suportat.'; -$PHPMAILER_LANG["execute"] = 'Nu pot executa: '; -$PHPMAILER_LANG["instantiate"] = 'Nu am putut instantia functia mail.'; -$PHPMAILER_LANG["authenticate"] = 'Eroare SMTP: Nu a functionat autentificarea.'; -$PHPMAILER_LANG["from_failed"] = 'Urmatoarele adrese From au dat eroare: '; -$PHPMAILER_LANG["recipients_failed"] = 'Eroare SMTP: Urmatoarele adrese de mail au dat eroare: '; -$PHPMAILER_LANG["data_not_accepted"] = 'Eroare SMTP: Continutul mailului nu a fost acceptat.'; -$PHPMAILER_LANG["connect_host"] = 'Eroare SMTP: Nu m-am putut conecta la adresa SMTP.'; -$PHPMAILER_LANG["file_access"] = 'Nu pot accesa fisierul: '; -$PHPMAILER_LANG["file_open"] = 'Eroare de fisier: Nu pot deschide fisierul: '; -$PHPMAILER_LANG["encoding"] = 'Encodare necunoscuta: '; -?> diff --git a/phpmailer/language/phpmailer.lang-ru.php b/phpmailer/language/phpmailer.lang-ru.php deleted file mode 100644 index 27579c3..0000000 --- a/phpmailer/language/phpmailer.lang-ru.php +++ /dev/null @@ -1,25 +0,0 @@ - diff --git a/phpmailer/language/phpmailer.lang-se.php b/phpmailer/language/phpmailer.lang-se.php deleted file mode 100644 index 39f2c3b..0000000 --- a/phpmailer/language/phpmailer.lang-se.php +++ /dev/null @@ -1,26 +0,0 @@ - - */ - -$PHPMAILER_LANG = array(); - -$PHPMAILER_LANG["provide_address"] = 'Du måste ange minst en ' . - 'mottagares e-postadress.'; -$PHPMAILER_LANG["mailer_not_supported"] = ' mailer stöds inte.'; -$PHPMAILER_LANG["execute"] = 'Kunde inte köra: '; -$PHPMAILER_LANG["instantiate"] = 'Kunde inte initiera e-postfunktion.'; -$PHPMAILER_LANG["authenticate"] = 'SMTP fel: Kunde inte autentisera.'; -$PHPMAILER_LANG["from_failed"] = 'Följande avsändaradress är felaktig: '; -$PHPMAILER_LANG["recipients_failed"] = 'SMTP fel: Följande ' . - 'mottagare är felaktig: '; -$PHPMAILER_LANG["data_not_accepted"] = 'SMTP fel: Data accepterades inte.'; -$PHPMAILER_LANG["connect_host"] = 'SMTP fel: Kunde inte ansluta till SMTP-server.'; -$PHPMAILER_LANG["file_access"] = 'Ingen åtkomst till fil: '; -$PHPMAILER_LANG["file_open"] = 'Fil fel: Kunde inte öppna fil: '; -$PHPMAILER_LANG["encoding"] = 'Okänt encode-format: '; -?> \ No newline at end of file diff --git a/phpmailer/language/phpmailer.lang-tr.php b/phpmailer/language/phpmailer.lang-tr.php deleted file mode 100644 index d46a670..0000000 --- a/phpmailer/language/phpmailer.lang-tr.php +++ /dev/null @@ -1,27 +0,0 @@ - \ No newline at end of file diff --git a/phpsniff/index.php b/phpsniff/index.php deleted file mode 100644 index 3e305fe..0000000 --- a/phpsniff/index.php +++ /dev/null @@ -1,6 +0,0 @@ - \ No newline at end of file diff --git a/phpsniff/phpSniff.class.php b/phpsniff/phpSniff.class.php deleted file mode 100644 index 7d8a9c5..0000000 --- a/phpsniff/phpSniff.class.php +++ /dev/null @@ -1,907 +0,0 @@ - - * @version $Id: phpSniff.class.php,v 1.22 2004/04/27 00:55:49 epsilon7 Exp $ - * @copyright Copyright © 2002-2004 Roger Raymond - * @package phpSniff - * @license http://opensource.org/licenses/lgpl-license.php GNU Lesser General Public License - * @filesource - */ -/** - * PHP Sniffer Class - * - * Used to determine the browser and other associated properies - * using nothing other than the HTTP_USER_AGENT value supplied by a - * user's web browser. - * - * @package phpSniff - * @access public - * @author Roger Raymond - */ -class phpSniff -{ - - /** - * @access private - * @var string - */ - var $_version = '2.1.4'; - - /** - * $_temp_file_path - * default : /tmp/ - * desc : directory writable by the server to store cookie check files. - * : trailing slash is needed. only used if you use the check cookie routine - * - * @access public - * @var string - */ - var $_temp_file_path = '/tmp/'; // with trailing slash - - /** - * $_check_cookies - * default : null - * desc : Allow for the script to redirect the browser in order - * : to check for cookies. In order for this to work, this - * : class must be instantiated before any headers are sent. - * - * @access public - * @var string - */ - var $_check_cookies = NULL; - - /** - * $_default_language - * default : en-us - * desc : language to report as if no languages are found - * @access public - * @var string - */ - var $_default_language = 'en-us'; - - /** - * default : null - * Allow for browser to Masquerade as another. (ie: Opera identifies as MSIE 5.0) - * - * @access public - * @var string - */ - var $_allow_masquerading = NULL; - - /** - * @access private - * @var string - */ - var $_php_version = ''; - - - /** - * 2D Array of browsers we wish to search for in key => value pairs. - *
    -     *  key   = browser to search for [as in HTTP_USER_AGENT]
    -     *  value = value to return as 'browser' property
    -     *  
    - * - * @access public - * @var array - */ - var $_browsers = array( - 'microsoft internet explorer' => 'IE', - 'msie' => 'IE', - 'netscape6' => 'NS', - 'netscape' => 'NS', - 'galeon' => 'GA', - 'phoenix' => 'PX', - 'mozilla firebird' => 'FB', - 'firebird' => 'FB', - 'firefox' => 'FX', - 'chimera' => 'CH', - 'camino' => 'CA', - 'epiphany' => 'EP', - 'safari' => 'SF', - 'k-meleon' => 'KM', - 'mozilla' => 'MZ', - 'opera' => 'OP', - 'konqueror' => 'KQ', - 'icab' => 'IC', - 'lynx' => 'LX', - 'links' => 'LI', - 'ncsa mosaic' => 'MO', - 'amaya' => 'AM', - 'omniweb' => 'OW', - 'hotjava' => 'HJ', - 'browsex' => 'BX', - 'amigavoyager' => 'AV', - 'amiga-aweb' => 'AW', - 'ibrowse' => 'IB' - ); - - /** - * $_javascript_versions - * desc : 2D Array of javascript version supported by which browser - * : in key => value pairs. - * : key = javascript version - * : value = search parameter for browsers that support the - * : javascript version listed in the key (comma delimited) - * : note: the search parameters rely on the values - * : set in the $_browsers array - * @access public - * @var array - */ - var $_javascript_versions = array( - '1.5' => 'NS5+,MZ,PX,FB,FX,GA,CH,CA,SF,KQ3+,KM,EP', // browsers that support JavaScript 1.5 - '1.4' => '', - '1.3' => 'NS4.05+,OP5+,IE5+', - '1.2' => 'NS4+,IE4+', - '1.1' => 'NS3+,OP,KQ', - '1.0' => 'NS2+,IE3+', - '0' => 'LI,LX,HJ' - ); - - /** - * $_browser_features - * desc : 2D Array of browser features supported by which browser - * : in key => value pairs. - * : key = feature - * : value = search parameter for browsers that support the - * : feature listed in the key (comma delimited) - * : note: the search parameters rely on the values - * : set in the $_browsers array - * @access public - * @var string - */ - var $_browser_features = array( - /** - * the following are true by default - * (see phpSniff.core.php $_feature_set array) - * browsers listed here will be set to false - **/ - 'html' => '', - 'images' => 'LI,LX', - 'frames' => 'LX', - 'tables' => '', - 'java' => 'OP3,LI,LX,NS1,MO,IE1,IE2', - 'plugins' => 'IE1,IE2,LI,LX', - /** - * the following are false by default - * (see phpSniff.core.php $_feature_set array) - * browsers listed here will be set to true - **/ - 'css2' => 'NS5+,IE5+,MZ,PX,FB,FX,CH,CA,SF,GA,KQ3+,OP7+,KM,EP', - 'css1' => 'NS4+,IE4+,MZ,PX,FB,FX,CH,CA,SF,GA,KQ,OP7+,KM,EP', - 'iframes' => 'LI,IE3+,NS5+,MZ,PX,FB,FX,CH,CA,SF,GA,KQ,OP7+,KM,EP', - 'xml' => 'IE5+,NS5+,MZ,PX,FB,FX,CH,CA,SF,GA,KQ,OP7+,KM,EP', - 'dom' => 'IE5+,NS5+,MZ,PX,FB,FX,CH,CA,SF,GA,KQ,OP7+,KM,EP', - 'hdml' => '', - 'wml' => '' - ); - - /** - * - * $_browser_quirks - * desc : 2D Array of browser quirks present in which browser - * : in key => value pairs. - * : key = quirk - * : value = search parameter for browsers that feature the - * : quirk listed in the key (comma delimited) - * : note: the search parameters rely on the values - * : set in the $_browsers array - * @access public - * @var string - */ - var $_browser_quirks = array( - 'must_cache_forms' => 'NS,MZ,FB,PX,FX', - 'avoid_popup_windows' => 'IE3,LI,LX', - 'cache_ssl_downloads' => 'IE', - 'break_disposition_header' => 'IE5.5', - 'empty_file_input_value' => 'KQ', - 'scrollbar_in_way' => 'IE6' - ); - - /** - * @access private - * @var array - */ - var $_browser_info = array( - 'ua' => '', - 'browser' => 'Unknown', - 'version' => 0, - 'maj_ver' => 0, - 'min_ver' => 0, - 'letter_ver' => '', - 'javascript' => '0.0', - 'platform' => 'Unknown', - 'os' => 'Unknown', - 'ip' => 'Unknown', - 'cookies' => 'Unknown', // remains for backwards compatability - 'ss_cookies' => 'Unknown', - 'st_cookies' => 'Unknown', - 'language' => '', - 'long_name' => '', - 'gecko' => '', - 'gecko_ver' => '' - ); - - /** - * @access private - * @var array - */ - var $_feature_set = array( - 'html' => true, - 'images' => true, - 'frames' => true, - 'tables' => true, - 'java' => true, - 'plugins' => true, - 'iframes' => false, - 'css2' => false, - 'css1' => false, - 'xml' => false, - 'dom' => false, - 'wml' => false, - 'hdml' => false - ); - - /** - * @access private - * @var array - */ - var $_quirks = array( - 'must_cache_forms' => false, - 'avoid_popup_windows' => false, - 'cache_ssl_downloads' => false, - 'break_disposition_header' => false, - 'empty_file_input_value' => false, - 'scrollbar_in_way' => false - ); - - /** - * @access private - * @var boolean - */ - var $_get_languages_ran_once = false; - /** - * @access private - * @var string - */ - var $_browser_search_regex = '([a-z]+)([0-9]*)([0-9.]*)(up|dn|\+|\-)?'; - /** - * @access private - * @var string - */ - var $_language_search_regex = '([a-z-]{2,})'; - /** - * @access private - * @var string - */ - var $_browser_regex; - - /** - * Performs some basic initialization and returns and object - * @param string User Agent to parse - * @param mixed array of settings - * [check_cookies, default_language, allow_masqeurading] - * - * @return object phpSniff object - */ - - function phpSniff($UA='',$settings = true) - { // populate the HTTP_USER_AGENT string - // 20020425 :: rraymond - // routine for easier configuration of the client at runtime - if(is_array($settings)) { - $run = true; - extract($settings); - $this->_check_cookies = $check_cookies; - $this->_default_language = $default_language; - $this->_allow_masquerading = $allow_masquerading; - } else { - // for backwards compatibility with 2.0.x series - $run = (bool) $settings; - } - - // if the user agent is empty, see if it exists somewhere - if(empty($UA)) { - if(isset($HTTP_SERVER_VARS['HTTP_USER_AGENT'])) { - $UA = $HTTP_SERVER_VARS['HTTP_USER_AGENT']; - } elseif(isset($_SERVER['HTTP_USER_AGENT'])) { - $UA = $_SERVER['HTTP_USER_AGENT']; - } else { - // try to use the getenv function as a last resort - $UA = getenv('HTTP_USER_AGENT'); - } - } - - // if it's still empty, just return false as there is nothing to do - if(empty($UA)) return false; - - $this->_set_browser('ua',$UA); - if($run) $this->init(); - } - - function init () - { - // collect the ip - $this->_get_ip(); - // run the cookie check routine first - // [note: method only runs if allowed] - $this->_test_cookies(); - // rip the user agent to pieces - $this->_get_browser_info(); - // gecko build - $this->_get_gecko(); - // look for other languages - $this->_get_languages(); - // establish the operating platform - $this->_get_os_info(); - // determine javascript version - $this->_get_javascript(); - // determine current feature set - $this->_get_features(); - // point out any quirks - $this->_get_quirks(); - } - - /** - * turn the cookie check routine on or off - * @param bool true or false - */ - function check_cookies($yn) - { - $this->_check_cookies = (bool) $yn; - } - - /** - * allow browser masquerading - * @param bool true or false - */ - function allow_masquerading($yn) - { - $this->_allow_masquerading = (bool) $yn; - } - - /** - * set the default browser language - * @param string valid language (ex: en-us) - */ - function default_language($language) - { - $this->_default_language = $language; - } - - /** - * property - * @param string property to return . optional (null returns entire array) - * @return mixed array/string entire array or value of property - **/ - function property ($p=null) - { if($p==null) - { return $this->_browser_info; - } - else - { return $this->_browser_info[strtolower($p)]; - } - } - - /** - * get_property is an alias for property - * @param string property to return . optional (null returns entire array) - * @return mixed array/string entire array or value of property - **/ - function get_property ($p) - { return $this->property($p); - } - - /** - * is - * @param string search phrase format = l:lang;b:browser - * @return bool true on success - * ex: $client->is('b:OP5Up'); - **/ - function is ($s) - { // perform language search - if(preg_match('/l:'.$this->_language_search_regex.'/i',$s,$match)) - { if($match) return $this->_perform_language_search($match); - } - // perform browser search - elseif(preg_match('/b:'.$this->_browser_search_regex.'/i',$s,$match)) - { if($match) return $this->_perform_browser_search($match); - } - return false; - } - - /** - * browser_is - * @param string search phrase for browser - * @return bool true on success - * ex: $client->browser_is('OP5Up'); - **/ - function browser_is ($s) - { preg_match('/'.$this->_browser_search_regex.'/i',$s,$match); - if($match) return $this->_perform_browser_search($match); - } - - /** - * language_is - * @param string search phrase for language - * @return bool true on success - * ex: $client->language_is('en-US'); - **/ - function language_is ($s) - { preg_match('/'.$this->_language_search_regex.'/i',$s,$match); - if($match) return $this->_perform_language_search($match); - } - - /** - * checks to see if the browser supports any of the following features: - *
      - *
    • html - *
    • images - *
    • frames - *
    • tables - *
    • plugins - *
    • iframes - *
    • css2 - *
    • css1 - *
    • xml - *
    • dom - *
    • wml - *
    • hdml - *
    - * ex: $client->has_feature('html'); - * @param string feature we're checking on - * @return bool true on success - - **/ - function has_feature ($s) - { return $this->_feature_set[$s]; - } - - /** - * checks to see if the browser has any of the following quirks: - *
      - *
    • must_cache_forms - *
    • avoid_popup_windows - *
    • cache_ssl_downloads - *
    • break_disposition_header - *
    • empty_file_input_value - *
    • scrollbar_in_way - *
    - * ex: $client->has_quirk('avoid_popup_windows'); - * @param string quirk we're looking for - * @return bool true on success - **/ - function has_quirk ($s) - { return $this->_quirks[$s]; - } - - /** - * _perform_browser_search - * @param string what we're searching for - * @return bool true on success - * @access private - **/ - function _perform_browser_search ($data) - { $search = array(); - $search['phrase'] = isset($data[0]) ? $data[0] : ''; - $search['name'] = isset($data[1]) ? strtolower($data[1]) : ''; - $search['maj_ver'] = isset($data[2]) ? $data[2] : ''; - $search['min_ver'] = isset($data[3]) ? $data[3] : ''; - $search['direction'] = isset($data[4]) ? strtolower($data[4]) : ''; - - $looking_for = $search['maj_ver'].$search['min_ver']; - if($search['name'] == 'aol' || $search['name'] == 'webtv') { - return stristr($this->_browser_info['ua'],$search['name']); - } elseif($this->_browser_info['browser'] == $search['name'] || $search['name'] == 'gecko') { - if(strtolower($search['name']) == 'gecko') { - $what_we_are =& $this->_browser_info['gecko_ver']; - } else { - $majv = $search['maj_ver'] ? $this->_browser_info['maj_ver'] : ''; - $minv = $search['min_ver'] ? $this->_browser_info['min_ver'] : ''; - $what_we_are = $majv.$minv; - } - if(($search['direction'] == 'up' || $search['direction'] == '+') - && ($what_we_are >= $looking_for)) - { return true; - } - elseif(($search['direction'] == 'dn' || $search['direction'] == '-') - && ($what_we_are <= $looking_for)) - { return true; - } - elseif($what_we_are == $looking_for) - { return true; - } - } - return false; - } - - /** - * @access private - */ - function _perform_language_search ($data) - { // if we've not grabbed the languages, then do so. - $this->_get_languages(); - return stristr($this->_browser_info['language'],$data[1]); - } - - /** - * @access private - */ - function _get_languages () - { // capture available languages and insert into container - if(!$this->_get_languages_ran_once) - { if($languages = getenv('HTTP_ACCEPT_LANGUAGE')) - { $languages = preg_replace('/(;q=[0-9]+.[0-9]+)/i','',$languages); - } - else - { $languages = $this->_default_language; - } - $this->_set_browser('language',$languages); - $this->_get_languages_ran_once = true; - } - } - - /** - * @access private - */ - function _get_os_info () - { // regexes to use - $regex_windows = '/([^dar]win[dows]*)[\s]?([0-9a-z]*)[\w\s]?([a-z0-9.]*)/i'; - $regex_mac = '/(68[k0]{1,3})|(ppc|intel) (mac os x)|([p\S]{1,5}pc)|(darwin)/i'; - $regex_os2 = '/os\/2|ibm-webexplorer/i'; - $regex_sunos = '/(sun|i86)[os\s]*([0-9]*)/i'; - $regex_irix = '/(irix)[\s]*([0-9]*)/i'; - $regex_hpux = '/(hp-ux)[\s]*([0-9]*)/i'; - $regex_aix = '/aix([0-9]*)/i'; - $regex_dec = '/dec|osfl|alphaserver|ultrix|alphastation/i'; - $regex_vms = '/vax|openvms/i'; - $regex_sco = '/sco|unix_sv/i'; - $regex_linux = '/x11|inux/i'; - $regex_bsd = '/(free)?(bsd)/i'; - $regex_amiga = '/amiga[os]?/i'; - - // look for Windows Box - if(preg_match_all($regex_windows,$this->_browser_info['ua'],$match)) - { /** Windows has some of the most ridiculous HTTP_USER_AGENT strings */ - //$match[1][count($match[0])-1]; - $v = $match[2][count($match[0])-1]; - $v2 = $match[3][count($match[0])-1]; - // Establish NT 5.1 as Windows XP - if(stristr($v,'NT') && $v2 == 5.1) $v = 'xp'; - // Establish NT 5.0 and Windows 2000 as win2k - elseif($v == '2000') $v = '2k'; - elseif(stristr($v,'NT') && $v2 == 5.0) $v = '2k'; - // Establish 9x 4.90 as Windows 98 - elseif(stristr($v,'9x') && $v2 == 4.9) $v = '98'; - // See if we're running windows 3.1 - elseif($v.$v2 == '16bit') $v = '31'; - // otherwise display as is (31,95,98,NT,ME,XP) - else $v .= $v2; - // update browser info container array - if(empty($v)) $v = 'win'; - $this->_set_browser('os',strtolower($v)); - $this->_set_browser('platform','win'); - } - // look for mac - // sets: platform = mac ; os = 68k or ppc - elseif( preg_match($regex_mac,$this->_browser_info['ua'],$match) ) - { $this->_set_browser('platform','mac'); - $os = ''; - if( $match[3] == 'mac os x' ) { $os = 'osx'; } - elseif( !empty($match[1]) ) { $os = '68k'; } - $this->_set_browser('os',$os); - } - // look for *nix boxes - // linux sets: platform = *nix ; os = linux - elseif(preg_match($regex_linux,$this->_browser_info['ua'],$match)) - { $this->_set_browser('platform','*nix'); - $this->_set_browser('os','linux'); - } - // sunos sets: platform = *nix ; os = sun|sun4|sun5|suni86 - elseif(preg_match($regex_sunos,$this->_browser_info['ua'],$match)) - { $this->_set_browser('platform','*nix'); - if(!stristr('sun',$match[1])) $match[1] = 'sun'.$match[1]; - $this->_set_browser('os',$match[1].$match[2]); - } - // irix sets: platform = *nix ; os = irix|irix5|irix6|... - elseif(preg_match($regex_irix,$this->_browser_info['ua'],$match)) - { $this->_set_browser('platform','*nix'); - $this->_set_browser('os',$match[1].$match[2]); - } - // hp-ux sets: platform = *nix ; os = hpux9|hpux10|... - elseif(preg_match($regex_hpux,$this->_browser_info['ua'],$match)) - { $this->_set_browser('platform','*nix'); - $match[1] = str_replace('-','',$match[1]); - $match[2] = (int) $match[2]; - $this->_set_browser('os',$match[1].$match[2]); - } - // aix sets: platform = *nix ; os = aix|aix1|aix2|aix3|... - elseif(preg_match($regex_aix,$this->_browser_info['ua'],$match)) - { $this->_set_browser('platform','*nix'); - $this->_set_browser('os','aix'.$match[1]); - } - // dec sets: platform = *nix ; os = dec - elseif(preg_match($regex_dec,$this->_browser_info['ua'],$match)) - { $this->_set_browser('platform','*nix'); - $this->_set_browser('os','dec'); - } - // vms sets: platform = *nix ; os = vms - elseif(preg_match($regex_vms,$this->_browser_info['ua'],$match)) - { $this->_set_browser('platform','*nix'); - $this->_set_browser('os','vms'); - } - // sco sets: platform = *nix ; os = sco - elseif(preg_match($regex_sco,$this->_browser_info['ua'],$match)) - { $this->_set_browser('platform','*nix'); - $this->_set_browser('os','sco'); - } - // unixware sets: platform = *nix ; os = unixware - elseif(stristr($this->_browser_info['ua'],'unix_system_v')) - { $this->_set_browser('platform','*nix'); - $this->_set_browser('os','unixware'); - } - // mpras sets: platform = *nix ; os = mpras - elseif(stristr($this->_browser_info['ua'],'ncr')) - { $this->_set_browser('platform','*nix'); - $this->_set_browser('os','mpras'); - } - // reliant sets: platform = *nix ; os = reliant - elseif(stristr($this->_browser_info['ua'],'reliantunix')) - { $this->_set_browser('platform','*nix'); - $this->_set_browser('os','reliant'); - } - // sinix sets: platform = *nix ; os = sinix - elseif(stristr($this->_browser_info['ua'],'sinix')) - { $this->_set_browser('platform','*nix'); - $this->_set_browser('os','sinix'); - } - // bsd sets: platform = *nix ; os = bsd|freebsd - elseif(preg_match($regex_bsd,$this->_browser_info['ua'],$match)) - { $this->_set_browser('platform','*nix'); - $this->_set_browser('os',$match[1].$match[2]); - } - // last one to look for - // look for amiga OS - elseif(preg_match($regex_amiga,$this->_browser_info['ua'],$match)) - { $this->_set_browser('platform','amiga'); - if(stristr($this->_browser_info['ua'],'morphos')) { - // checking for MorphOS - $this->_set_browser('os','morphos'); - } elseif(stristr($this->_browser_info['ua'],'mc680x0')) { - // checking for MC680x0 - $this->_set_browser('os','mc680x0'); - } elseif(stristr($this->_browser_info['ua'],'ppc')) { - // checking for PPC - $this->_set_browser('os','ppc'); - } elseif(preg_match('/(AmigaOS [\.1-9]?)/i',$this->_browser_info['ua'],$match)) { - // checking for AmigaOS version string - $this->_set_browser('os',$match[1]); - } - } - // look for OS2 - elseif( preg_match($regex_os2,$this->_browser_info['ua'])) - { $this->_set_browser('os','os2'); - $this->_set_browser('platform','os2'); - } - } - - /** - * @access private - */ - function _get_browser_info () - { $this->_build_regex(); - if(preg_match_all($this->_browser_regex,$this->_browser_info['ua'],$results)) - { // get the position of the last browser found - $count = count($results[0])-1; - // if we're allowing masquerading, revert to the next to last browser found - // if possible, otherwise stay put - if($this->_allow_masquerading && $count > 0) $count--; - // insert findings into the container - $this->_set_browser('browser',$this->_get_short_name($results[1][$count])); - $this->_set_browser('long_name',$results[1][$count]); - $this->_set_browser('maj_ver',$results[2][$count]); - // parse the minor version string and look for alpha chars - preg_match('/([.\0-9]+)?([\.a-z0-9]+)?/i',$results[3][$count],$match); - if(isset($match[1])) { - $this->_set_browser('min_ver',$match[1]); - } else { - $this->_set_browser('min_ver','.0'); - } - if(isset($match[2])) $this->_set_browser('letter_ver',$match[2]); - // insert findings into container - $this->_set_browser('version',$this->_browser_info['maj_ver'].$this->property('min_ver')); - } - } - - /** - * @access private - */ - function _get_ip () - { if(getenv('HTTP_CLIENT_IP')) - { $ip = getenv('HTTP_CLIENT_IP'); - } - else - { $ip = getenv('REMOTE_ADDR'); - } - $this->_set_browser('ip',$ip); - } - - /** - * @access private - */ - function _build_regex () - { $browsers = ''; - while(list($k,) = each($this->_browsers)) - { if(!empty($browsers)) $browsers .= "|"; - $browsers .= $k; - } - $version_string = "[\/\sa-z(]*([0-9]+)([\.0-9a-z]+)?"; - $this->_browser_regex = "/($browsers)$version_string/i"; - } - - /** - * @access private - */ - function _get_short_name ($long_name) - { return $this->_browsers[strtolower($long_name)]; - } - - /** - * @access private - */ - // medianes :: new test cookie routine - function _test_cookies() - { global $HTTP_COOKIE_VARS; - $cookies = array(); - if(isset($_COOKIE)) { - $cookies = $_COOKIE; - } elseif(isset($HTTP_COOKIE_VARS)) { - $cookies = $HTTP_COOKIE_VARS; - } - if($this->_check_cookies) - { $fp = @fopen($this->_temp_file_path.$this->property('ip'),'r'); - if(!$fp) - { $fp = @fopen($this->_temp_file_path.$this->property('ip'),'a'); - // make sure we have a valid file pointer - if($fp) { - fclose($fp); - setcookie('phpSniff_session','ss',0,'/'); - setcookie('phpSniff_stored','st',time()+3600*24*365,'/'); - $QS=getenv('QUERY_STRING'); - $script_path=getenv('PATH_INFO')?getenv('PATH_INFO'):getenv('SCRIPT_NAME'); - if(is_integer($pos=strpos(strrev($script_path),"php.xedni/"))&&!$pos) { - $script_path=strrev(substr(strrev($script_path),9)); - } - } - $location='http://'.getenv('SERVER_NAME').$script_path.($QS==''?'':'?'.$QS); - header("Location: $location"); - exit; - } elseif($fp) { - // we only want to proceed if we have a file pointer - unlink($this->_temp_file_path.$this->property('ip')); - fclose($fp); - $this->_set_browser('ss_cookies',isset($cookies['phpSniff_session'])?'true':'false'); - $this->_set_browser('st_cookies',isset($cookies['phpSniff_stored'])?'true':'false'); - // delete the old cookies - setcookie('phpSniff_session','',0,'/'); - setcookie('phpSniff_stored','',0,'/'); - } - } - } - - /** - * @access private - */ - function _get_javascript() - { $set=false; - // see if we have any matches - while(list($version,$browser) = each($this->_javascript_versions)) - { $browser = explode(',',$browser); - while(list(,$search) = each($browser)) - { if($this->is('b:'.$search)) - { $this->_set_browser('javascript',$version); - $set = true; - break; - } - } - if($set) break; - } - } - - /** - * @access private - */ - function _get_features () - { while(list($feature,$browser) = each($this->_browser_features)) - { $browser = explode(',',$browser); - while(list(,$search) = each($browser)) - { if($this->browser_is($search)) - { $this->_set_feature($feature); - break; - } - } - } - } - - /** - * @access private - */ - function _get_quirks () - { while(list($quirk,$browser) = each($this->_browser_quirks)) - { $browser = explode(',',$browser); - while(list(,$search) = each($browser)) - { if($this->browser_is($search)) - { $this->_set_quirk($quirk); - break; - } - } - } - } - - /** - * @access private - */ - function _get_gecko () - { if(preg_match('/gecko\/([0-9]+)/i',$this->property('ua'),$match)) - { $this->_set_browser('gecko',$match[1]); - if (preg_match('/rv[: ]?([0-9a-z.+]+)/i',$this->property('ua'),$mozv)) { - // mozilla release - $this->_set_browser('gecko_ver',$mozv[1]); - } elseif (preg_match('/(m[0-9]+)/i',$this->property('ua'),$mozv)) { - // mozilla milestone version - $this->_set_browser('gecko_ver',$mozv[1]); - } - // if this is a mozilla browser, get the rv: information - if($this->browser_is($this->_get_short_name('mozilla'))) { - if( !empty( $mozv[1] ) && preg_match('/([0-9]+)([\.0-9]+)([a-z0-9+]?)/i',$mozv[1],$match) ) { - $this->_set_browser('version',$mozv[1]); - $this->_set_browser('maj_ver',$match[1]); - $this->_set_browser('min_ver',$match[2]); - $this->_set_browser('letter_ver',$match[3]); - } - } - } elseif($this->is('b:'.$this->_get_short_name('mozilla'))) { - // this is probably a netscape browser or compatible - $this->_set_browser('long_name','netscape'); - $this->_set_browser('browser',$this->_get_short_name('netscape')); - } - } - - /** - * @access private - */ - function _set_browser ($k,$v) - { $this->_browser_info[strtolower($k)] = strtolower($v); - } - - /** - * @access private - */ - function _set_feature ($k) - { $this->_feature_set[strtolower($k)] = !$this->_feature_set[strtolower($k)]; - } - - /** - * @access private - */ - function _set_quirk ($k) - { $this->_quirks[strtolower($k)] = true; - } -} -?> diff --git a/phpsniff/phpSniff.core.php b/phpsniff/phpSniff.core.php deleted file mode 100644 index 4d09242..0000000 --- a/phpsniff/phpSniff.core.php +++ /dev/null @@ -1,547 +0,0 @@ - '', - 'browser' => 'Unknown', - 'version' => 0, - 'maj_ver' => 0, - 'min_ver' => 0, - 'letter_ver' => '', - 'javascript' => '0.0', - 'platform' => 'Unknown', - 'os' => 'Unknown', - 'ip' => 'Unknown', - 'cookies' => 'Unknown', // remains for backwards compatability - 'ss_cookies' => 'Unknown', - 'st_cookies' => 'Unknown', - 'language' => '', - 'long_name' => '', - 'gecko' => '', - 'gecko_ver' => '' - ); - - var $_feature_set = array( - 'html' => true, - 'images' => true, - 'frames' => true, - 'tables' => true, - 'java' => true, - 'plugins' => true, - 'iframes' => false, - 'css2' => false, - 'css1' => false, - 'xml' => false, - 'dom' => false, - 'wml' => false, - 'hdml' => false - ); - - var $_quirks = array( - 'must_cache_forms' => false, - 'avoid_popup_windows' => false, - 'cache_ssl_downloads' => false, - 'break_disposition_header' => false, - 'empty_file_input_value' => false, - 'scrollbar_in_way' => false - ); - - var $_get_languages_ran_once = false; - var $_browser_search_regex = '([a-z]+)([0-9]*)([0-9.]*)(up|dn|\+|\-)?'; - var $_language_search_regex = '([a-z-]{2,})'; - - /** - * init - * this method starts the madness - **/ - function init () - { - // collect the ip - $this->_get_ip(); - // run the cookie check routine first - // [note: method only runs if allowed] - $this->_test_cookies(); - // rip the user agent to pieces - $this->_get_browser_info(); - // gecko build - $this->_get_gecko(); - // look for other languages - $this->_get_languages(); - // establish the operating platform - $this->_get_os_info(); - // determine javascript version - $this->_get_javascript(); - // determine current feature set - $this->_get_features(); - // point out any quirks - $this->_get_quirks(); - } - - /** - * property - * @param $p property to return . optional (null returns entire array) - * @return array/string entire array or value of property - **/ - function property ($p=null) - { if($p==null) - { return $this->_browser_info; - } - else - { return $this->_browser_info[strtolower($p)]; - } - } - - /** - * get_property - * alias for property - **/ - function get_property ($p) - { return $this->property($p); - } - - /** - * is - * @param $s string search phrase format = l:lang;b:browser - * @return bool true on success - * ex: $client->is('b:OP5Up'); - **/ - function is ($s) - { // perform language search - if(preg_match('/l:'.$this->_language_search_regex.'/i',$s,$match)) - { if($match) return $this->_perform_language_search($match); - } - // perform browser search - elseif(preg_match('/b:'.$this->_browser_search_regex.'/i',$s,$match)) - { if($match) return $this->_perform_browser_search($match); - } - return false; - } - - /** - * browser_is - * @param $s string search phrase for browser - * @return bool true on success - * ex: $client->browser_is('OP5Up'); - **/ - function browser_is ($s) - { preg_match('/'.$this->_browser_search_regex.'/i',$s,$match); - if($match) return $this->_perform_browser_search($match); - } - - /** - * language_is - * @param $s string search phrase for language - * @return bool true on success - * ex: $client->language_is('en-US'); - **/ - function language_is ($s) - { preg_match('/'.$this->_language_search_regex.'/i',$s,$match); - if($match) return $this->_perform_language_search($match); - } - - /** - * has_feature - * @param $s string feature we're checking on - * @return bool true on success - * ex: $client->has_feature('html'); - **/ - function has_feature ($s) - { return $this->_feature_set[$s]; - } - - /** - * has_quirk - * @param $s string quirk we're looking for - * @return bool true on success - * ex: $client->has_quirk('avoid_popup_windows'); - **/ - function has_quirk ($s) - { return $this->_quirks[$s]; - } - - /** - * _perform_browser_search - * @param $data string what we're searching for - * @return bool true on success - * @private - **/ - function _perform_browser_search ($data) - { $search = array(); - $search['phrase'] = isset($data[0]) ? $data[0] : ''; - $search['name'] = isset($data[1]) ? strtolower($data[1]) : ''; - $search['maj_ver'] = isset($data[2]) ? $data[2] : ''; - $search['min_ver'] = isset($data[3]) ? $data[3] : ''; - $search['direction'] = isset($data[4]) ? strtolower($data[4]) : ''; - - $looking_for = $search['maj_ver'].$search['min_ver']; - if($search['name'] == 'aol' || $search['name'] == 'webtv') - { return stristr($this->_browser_info['ua'],$search['name']); - } - elseif($this->_browser_info['browser'] == $search['name']) - { $majv = $search['maj_ver'] ? $this->_browser_info['maj_ver'] : ''; - $minv = $search['min_ver'] ? $this->_browser_info['min_ver'] : ''; - $what_we_are = $majv.$minv; - if(($search['direction'] == 'up' || $search['direction'] == '+') - && ($what_we_are >= $looking_for)) - { return true; - } - elseif(($search['direction'] == 'dn' || $search['direction'] == '-') - && ($what_we_are <= $looking_for)) - { return true; - } - elseif($what_we_are == $looking_for) - { return true; - } - } - return false; - } - - function _perform_language_search ($data) - { // if we've not grabbed the languages, then do so. - $this->_get_languages(); - return stristr($this->_browser_info['language'],$data[1]); - } - - function _get_languages () - { // capture available languages and insert into container - if(!$this->_get_languages_ran_once) - { if($languages = getenv('HTTP_ACCEPT_LANGUAGE')) - { $languages = preg_replace('/(;q=[0-9]+.[0-9]+)/i','',$languages); - } - else - { $languages = $this->_default_language; - } - $this->_set_browser('language',$languages); - $this->_get_languages_ran_once = true; - } - } - - function _get_os_info () - { // regexes to use - $regex_windows = '/([^dar]win[dows]*)[\s]?([0-9a-z]*)[\w\s]?([a-z0-9.]*)/i'; - $regex_mac = '/(68[k0]{1,3})|(ppc mac os x)|([p\S]{1,5}pc)|(darwin)/i'; - $regex_os2 = '/os\/2|ibm-webexplorer/i'; - $regex_sunos = '/(sun|i86)[os\s]*([0-9]*)/i'; - $regex_irix = '/(irix)[\s]*([0-9]*)/i'; - $regex_hpux = '/(hp-ux)[\s]*([0-9]*)/i'; - $regex_aix = '/aix([0-9]*)/i'; - $regex_dec = '/dec|osfl|alphaserver|ultrix|alphastation/i'; - $regex_vms = '/vax|openvms/i'; - $regex_sco = '/sco|unix_sv/i'; - $regex_linux = '/x11|inux/i'; - $regex_bsd = '/(free)?(bsd)/i'; - $regex_amiga = '/amiga[os]?/i'; - - // look for Windows Box - if(preg_match_all($regex_windows,$this->_browser_info['ua'],$match)) - { /** Windows has some of the most ridiculous HTTP_USER_AGENT strings */ - //$match[1][count($match[0])-1]; - $v = $match[2][count($match[0])-1]; - $v2 = $match[3][count($match[0])-1]; - // Establish NT 5.1 as Windows XP - if(stristr($v,'NT') && $v2 == 5.1) $v = 'xp'; - // Establish NT 5.0 and Windows 2000 as win2k - elseif($v == '2000') $v = '2k'; - elseif(stristr($v,'NT') && $v2 == 5.0) $v = '2k'; - // Establish 9x 4.90 as Windows 98 - elseif(stristr($v,'9x') && $v2 == 4.9) $v = '98'; - // See if we're running windows 3.1 - elseif($v.$v2 == '16bit') $v = '31'; - // otherwise display as is (31,95,98,NT,ME,XP) - else $v .= $v2; - // update browser info container array - if(empty($v)) $v = 'win'; - $this->_set_browser('os',strtolower($v)); - $this->_set_browser('platform','win'); - } - // look for amiga OS - elseif(preg_match($regex_amiga,$this->_browser_info['ua'],$match)) - { $this->_set_browser('platform','amiga'); - if(stristr($this->_browser_info['ua'],'morphos')) { - // checking for MorphOS - $this->_set_browser('os','morphos'); - } elseif(stristr($this->_browser_info['ua'],'mc680x0')) { - // checking for MC680x0 - $this->_set_browser('os','mc680x0'); - } elseif(stristr($this->_browser_info['ua'],'ppc')) { - // checking for PPC - $this->_set_browser('os','ppc'); - } elseif(preg_match('/(AmigaOS [\.1-9]?)/i',$this->_browser_info['ua'],$match)) { - // checking for AmigaOS version string - $this->_set_browser('os',$match[1]); - } - } - // look for OS2 - elseif( preg_match($regex_os2,$this->_browser_info['ua'])) - { $this->_set_browser('os','os2'); - $this->_set_browser('platform','os2'); - } - // look for mac - // sets: platform = mac ; os = 68k or ppc - elseif( preg_match($regex_mac,$this->_browser_info['ua'],$match) ) - { $this->_set_browser('platform','mac'); - $os = !empty($match[1]) ? '68k' : ''; - $os = !empty($match[2]) ? 'osx' : $os; - $os = !empty($match[3]) ? 'ppc' : $os; - $os = !empty($match[4]) ? 'osx' : $os; - $this->_set_browser('os',$os); - } - // look for *nix boxes - // sunos sets: platform = *nix ; os = sun|sun4|sun5|suni86 - elseif(preg_match($regex_sunos,$this->_browser_info['ua'],$match)) - { $this->_set_browser('platform','*nix'); - if(!stristr('sun',$match[1])) $match[1] = 'sun'.$match[1]; - $this->_set_browser('os',$match[1].$match[2]); - } - // irix sets: platform = *nix ; os = irix|irix5|irix6|... - elseif(preg_match($regex_irix,$this->_browser_info['ua'],$match)) - { $this->_set_browser('platform','*nix'); - $this->_set_browser('os',$match[1].$match[2]); - } - // hp-ux sets: platform = *nix ; os = hpux9|hpux10|... - elseif(preg_match($regex_hpux,$this->_browser_info['ua'],$match)) - { $this->_set_browser('platform','*nix'); - $match[1] = str_replace('-','',$match[1]); - $match[2] = (int) $match[2]; - $this->_set_browser('os',$match[1].$match[2]); - } - // aix sets: platform = *nix ; os = aix|aix1|aix2|aix3|... - elseif(preg_match($regex_aix,$this->_browser_info['ua'],$match)) - { $this->_set_browser('platform','*nix'); - $this->_set_browser('os','aix'.$match[1]); - } - // dec sets: platform = *nix ; os = dec - elseif(preg_match($regex_dec,$this->_browser_info['ua'],$match)) - { $this->_set_browser('platform','*nix'); - $this->_set_browser('os','dec'); - } - // vms sets: platform = *nix ; os = vms - elseif(preg_match($regex_vms,$this->_browser_info['ua'],$match)) - { $this->_set_browser('platform','*nix'); - $this->_set_browser('os','vms'); - } - // sco sets: platform = *nix ; os = sco - elseif(preg_match($regex_sco,$this->_browser_info['ua'],$match)) - { $this->_set_browser('platform','*nix'); - $this->_set_browser('os','sco'); - } - // unixware sets: platform = *nix ; os = unixware - elseif(stristr($this->_browser_info['ua'],'unix_system_v')) - { $this->_set_browser('platform','*nix'); - $this->_set_browser('os','unixware'); - } - // mpras sets: platform = *nix ; os = mpras - elseif(stristr($this->_browser_info['ua'],'ncr')) - { $this->_set_browser('platform','*nix'); - $this->_set_browser('os','mpras'); - } - // reliant sets: platform = *nix ; os = reliant - elseif(stristr($this->_browser_info['ua'],'reliantunix')) - { $this->_set_browser('platform','*nix'); - $this->_set_browser('os','reliant'); - } - // sinix sets: platform = *nix ; os = sinix - elseif(stristr($this->_browser_info['ua'],'sinix')) - { $this->_set_browser('platform','*nix'); - $this->_set_browser('os','sinix'); - } - // bsd sets: platform = *nix ; os = bsd|freebsd - elseif(preg_match($regex_bsd,$this->_browser_info['ua'],$match)) - { $this->_set_browser('platform','*nix'); - $this->_set_browser('os',$match[1].$match[2]); - } - // last one to look for - // linux sets: platform = *nix ; os = linux - elseif(preg_match($regex_linux,$this->_browser_info['ua'],$match)) - { $this->_set_browser('platform','*nix'); - $this->_set_browser('os','linux'); - } - } - - function _get_browser_info () - { $this->_build_regex(); - if(preg_match_all($this->_browser_regex,$this->_browser_info['ua'],$results)) - { // get the position of the last browser found - $count = count($results[0])-1; - // if we're allowing masquerading, revert to the next to last browser found - // if possible, otherwise stay put - if($this->_allow_masquerading && $count > 0) $count--; - // insert findings into the container - $this->_set_browser('browser',$this->_get_short_name($results[1][$count])); - $this->_set_browser('long_name',$results[1][$count]); - $this->_set_browser('maj_ver',$results[2][$count]); - // parse the minor version string and look for alpha chars - preg_match('/([.\0-9]+)?([\.a-z0-9]+)?/i',$results[3][$count],$match); - if(isset($match[1])) { - $this->_set_browser('min_ver',$match[1]); - } else { - $this->_set_browser('min_ver','.0'); - } - if(isset($match[2])) $this->_set_browser('letter_ver',$match[2]); - // insert findings into container - $this->_set_browser('version',$this->_browser_info['maj_ver'].$this->property('min_ver')); - } - } - - function _get_ip () - { if(getenv('HTTP_CLIENT_IP')) - { $ip = getenv('HTTP_CLIENT_IP'); - } - else - { $ip = getenv('REMOTE_ADDR'); - } - $this->_set_browser('ip',$ip); - } - - function _build_regex () - { $browsers = ''; - while(list($k,) = each($this->_browsers)) - { if(!empty($browsers)) $browsers .= "|"; - $browsers .= $k; - } - $version_string = "[\/\sa-z(]*([0-9]+)([\.0-9a-z]+)?"; - $this->_browser_regex = "/($browsers)$version_string/i"; - } - - function _get_short_name ($long_name) - { return $this->_browsers[strtolower($long_name)]; - } - - // medianes :: new test cookie routine - function _test_cookies() - { global $HTTP_COOKIE_VARS; - $cookies = array(); - if(isset($_COOKIE)) { - $cookies = $_COOKIE; - } elseif(isset($HTTP_COOKIE_VARS)) { - $cookies = $HTTP_COOKIE_VARS; - } - if($this->_check_cookies) - { $fp = @fopen($this->_temp_file_path.$this->property('ip'),'r'); - if(!$fp) - { $fp = @fopen($this->_temp_file_path.$this->property('ip'),'a'); - fclose($fp); - setcookie('phpSniff_session','ss',0,'/'); - setcookie('phpSniff_stored','st',time()+3600*24*365,'/'); - $QS=getenv('QUERY_STRING'); - $script_path=getenv('PATH_INFO')?getenv('PATH_INFO'):getenv('SCRIPT_NAME'); - if(is_integer($pos=strpos(strrev($script_path),"php.xedni/"))&&!$pos) { - $script_path=strrev(substr(strrev($script_path),9)); - } - $location='http://'.getenv('SERVER_NAME').$script_path.($QS==''?'':'?'.$QS); - header("Location: $location"); - exit; - } - else - { unlink($this->_temp_file_path.$this->property('ip')); - fclose($fp); - $this->_set_browser('ss_cookies',isset($cookies['phpSniff_session'])?'true':'false'); - $this->_set_browser('st_cookies',isset($cookies['phpSniff_stored'])?'true':'false'); - // delete the old cookies - setcookie('phpSniff_session','',0,'/'); - setcookie('phpSniff_stored','',0,'/'); - - } - } - } - - function _get_javascript() - { $set=false; - // see if we have any matches - while(list($version,$browser) = each($this->_javascript_versions)) - { $browser = explode(',',$browser); - while(list(,$search) = each($browser)) - { if($this->is('b:'.$search)) - { $this->_set_browser('javascript',$version); - $set = true; - break; - } - } - if($set) break; - } - } - - function _get_features () - { while(list($feature,$browser) = each($this->_browser_features)) - { $browser = explode(',',$browser); - while(list(,$search) = each($browser)) - { if($this->browser_is($search)) - { $this->_set_feature($feature); - break; - } - } - } - } - - function _get_quirks () - { while(list($quirk,$browser) = each($this->_browser_quirks)) - { $browser = explode(',',$browser); - while(list(,$search) = each($browser)) - { if($this->browser_is($search)) - { $this->_set_quirk($quirk); - break; - } - } - } - } - - function _get_gecko () - { if(preg_match('/gecko\/([0-9]+)/i',$this->property('ua'),$match)) - { $this->_set_browser('gecko',$match[1]); - if (preg_match('/rv[: ]?([0-9a-z.+]+)/i',$this->property('ua'),$mozv)) { - // mozilla release - $this->_set_browser('gecko_ver',$mozv[1]); - } elseif (preg_match('/(m[0-9]+)/i',$this->property('ua'),$mozv)) { - // mozilla milestone version - $this->_set_browser('gecko_ver',$mozv[1]); - } - // if this is a mozilla browser, get the rv: information - if($this->browser_is($this->_get_short_name('mozilla'))) { - if( !empty( $mozv[1] ) && preg_match('/([0-9]+)([\.0-9]+)([a-z0-9+]?)/i',$mozv[1],$match)) { - $this->_set_browser('version',$mozv[1]); - $this->_set_browser('maj_ver',$match[1]); - $this->_set_browser('min_ver',$match[2]); - $this->_set_browser('letter_ver',$match[3]); - } - } - } elseif($this->is('b:'.$this->_get_short_name('mozilla'))) { - // this is probably a netscape browser or compatible - $this->_set_browser('long_name','netscape'); - $this->_set_browser('browser',$this->_get_short_name('netscape')); - } - } - - function _set_browser ($k,$v) - { $this->_browser_info[strtolower($k)] = strtolower($v); - } - - function _set_feature ($k) - { $this->_feature_set[strtolower($k)] = !$this->_feature_set[strtolower($k)]; - } - - function _set_quirk ($k) - { $this->_quirks[strtolower($k)] = true; - } -} -?> diff --git a/simplepie/idn/idna_convert.class.php b/simplepie/idn/idna_convert.class.php deleted file mode 100644 index ed2bae2..0000000 --- a/simplepie/idn/idna_convert.class.php +++ /dev/null @@ -1,969 +0,0 @@ - - * @copyright 2004-2007 phlyLabs Berlin, http://phlylabs.de - * @version 0.5.1 - * - */ -class idna_convert -{ - /** - * Holds all relevant mapping tables, loaded from a seperate file on construct - * See RFC3454 for details - * - * @var array - * @access private - */ - var $NP = array(); - - // Internal settings, do not mess with them - var $_punycode_prefix = 'xn--'; - var $_invalid_ucs = 0x80000000; - var $_max_ucs = 0x10FFFF; - var $_base = 36; - var $_tmin = 1; - var $_tmax = 26; - var $_skew = 38; - var $_damp = 700; - var $_initial_bias = 72; - var $_initial_n = 0x80; - var $_sbase = 0xAC00; - var $_lbase = 0x1100; - var $_vbase = 0x1161; - var $_tbase = 0x11A7; - var $_lcount = 19; - var $_vcount = 21; - var $_tcount = 28; - var $_ncount = 588; // _vcount * _tcount - var $_scount = 11172; // _lcount * _tcount * _vcount - var $_error = false; - - // See {@link set_paramter()} for details of how to change the following - // settings from within your script / application - var $_api_encoding = 'utf8'; // Default input charset is UTF-8 - var $_allow_overlong = false; // Overlong UTF-8 encodings are forbidden - var $_strict_mode = false; // Behave strict or not - - // The constructor - function idna_convert($options = false) - { - $this->slast = $this->_sbase + $this->_lcount * $this->_vcount * $this->_tcount; - if (function_exists('file_get_contents')) { - $this->NP = unserialize(file_get_contents(dirname(__FILE__).'/npdata.ser')); - } else { - $this->NP = unserialize(join('', file(dirname(__FILE__).'/npdata.ser'))); - } - // If parameters are given, pass these to the respective method - if (is_array($options)) { - return $this->set_parameter($options); - } - return true; - } - - /** - * Sets a new option value. Available options and values: - * [encoding - Use either UTF-8, UCS4 as array or UCS4 as string as input ('utf8' for UTF-8, - * 'ucs4_string' and 'ucs4_array' respectively for UCS4); The output is always UTF-8] - * [overlong - Unicode does not allow unnecessarily long encodings of chars, - * to allow this, set this parameter to true, else to false; - * default is false.] - * [strict - true: strict mode, good for registration purposes - Causes errors - * on failures; false: loose mode, ideal for "wildlife" applications - * by silently ignoring errors and returning the original input instead - * - * @param mixed Parameter to set (string: single parameter; array of Parameter => Value pairs) - * @param string Value to use (if parameter 1 is a string) - * @return boolean true on success, false otherwise - * @access public - */ - function set_parameter($option, $value = false) - { - if (!is_array($option)) { - $option = array($option => $value); - } - foreach ($option as $k => $v) { - switch ($k) { - case 'encoding': - switch ($v) { - case 'utf8': - case 'ucs4_string': - case 'ucs4_array': - $this->_api_encoding = $v; - break; - default: - $this->_error('Set Parameter: Unknown parameter '.$v.' for option '.$k); - return false; - } - break; - case 'overlong': - $this->_allow_overlong = ($v) ? true : false; - break; - case 'strict': - $this->_strict_mode = ($v) ? true : false; - break; - default: - $this->_error('Set Parameter: Unknown option '.$k); - return false; - } - } - return true; - } - - /** - * Decode a given ACE domain name - * @param string Domain name (ACE string) - * [@param string Desired output encoding, see {@link set_parameter}] - * @return string Decoded Domain name (UTF-8 or UCS-4) - * @access public - */ - function decode($input, $one_time_encoding = false) - { - // Optionally set - if ($one_time_encoding) { - switch ($one_time_encoding) { - case 'utf8': - case 'ucs4_string': - case 'ucs4_array': - break; - default: - $this->_error('Unknown encoding '.$one_time_encoding); - return false; - } - } - // Make sure to drop any newline characters around - $input = trim($input); - - // Negotiate input and try to determine, whether it is a plain string, - // an email address or something like a complete URL - if (strpos($input, '@')) { // Maybe it is an email address - // No no in strict mode - if ($this->_strict_mode) { - $this->_error('Only simple domain name parts can be handled in strict mode'); - return false; - } - list ($email_pref, $input) = explode('@', $input, 2); - $arr = explode('.', $input); - foreach ($arr as $k => $v) { - if (preg_match('!^'.preg_quote($this->_punycode_prefix, '!').'!', $v)) { - $conv = $this->_decode($v); - if ($conv) $arr[$k] = $conv; - } - } - $input = join('.', $arr); - $arr = explode('.', $email_pref); - foreach ($arr as $k => $v) { - if (preg_match('!^'.preg_quote($this->_punycode_prefix, '!').'!', $v)) { - $conv = $this->_decode($v); - if ($conv) $arr[$k] = $conv; - } - } - $email_pref = join('.', $arr); - $return = $email_pref . '@' . $input; - } elseif (preg_match('![:\./]!', $input)) { // Or a complete domain name (with or without paths / parameters) - // No no in strict mode - if ($this->_strict_mode) { - $this->_error('Only simple domain name parts can be handled in strict mode'); - return false; - } - $parsed = parse_url($input); - if (isset($parsed['host'])) { - $arr = explode('.', $parsed['host']); - foreach ($arr as $k => $v) { - $conv = $this->_decode($v); - if ($conv) $arr[$k] = $conv; - } - $parsed['host'] = join('.', $arr); - $return = - (empty($parsed['scheme']) ? '' : $parsed['scheme'].(strtolower($parsed['scheme']) == 'mailto' ? ':' : '://')) - .(empty($parsed['user']) ? '' : $parsed['user'].(empty($parsed['pass']) ? '' : ':'.$parsed['pass']).'@') - .$parsed['host'] - .(empty($parsed['port']) ? '' : ':'.$parsed['port']) - .(empty($parsed['path']) ? '' : $parsed['path']) - .(empty($parsed['query']) ? '' : '?'.$parsed['query']) - .(empty($parsed['fragment']) ? '' : '#'.$parsed['fragment']); - } else { // parse_url seems to have failed, try without it - $arr = explode('.', $input); - foreach ($arr as $k => $v) { - $conv = $this->_decode($v); - $arr[$k] = ($conv) ? $conv : $v; - } - $return = join('.', $arr); - } - } else { // Otherwise we consider it being a pure domain name string - $return = $this->_decode($input); - if (!$return) $return = $input; - } - // The output is UTF-8 by default, other output formats need conversion here - // If one time encoding is given, use this, else the objects property - switch (($one_time_encoding) ? $one_time_encoding : $this->_api_encoding) { - case 'utf8': - return $return; - break; - case 'ucs4_string': - return $this->_ucs4_to_ucs4_string($this->_utf8_to_ucs4($return)); - break; - case 'ucs4_array': - return $this->_utf8_to_ucs4($return); - break; - default: - $this->_error('Unsupported output format'); - return false; - } - } - - /** - * Encode a given UTF-8 domain name - * @param string Domain name (UTF-8 or UCS-4) - * [@param string Desired input encoding, see {@link set_parameter}] - * @return string Encoded Domain name (ACE string) - * @access public - */ - function encode($decoded, $one_time_encoding = false) - { - // Forcing conversion of input to UCS4 array - // If one time encoding is given, use this, else the objects property - switch ($one_time_encoding ? $one_time_encoding : $this->_api_encoding) { - case 'utf8': - $decoded = $this->_utf8_to_ucs4($decoded); - break; - case 'ucs4_string': - $decoded = $this->_ucs4_string_to_ucs4($decoded); - case 'ucs4_array': - break; - default: - $this->_error('Unsupported input format: '.($one_time_encoding ? $one_time_encoding : $this->_api_encoding)); - return false; - } - - // No input, no output, what else did you expect? - if (empty($decoded)) return ''; - - // Anchors for iteration - $last_begin = 0; - // Output string - $output = ''; - foreach ($decoded as $k => $v) { - // Make sure to use just the plain dot - switch($v) { - case 0x3002: - case 0xFF0E: - case 0xFF61: - $decoded[$k] = 0x2E; - // Right, no break here, the above are converted to dots anyway - // Stumbling across an anchoring character - case 0x2E: - case 0x2F: - case 0x3A: - case 0x3F: - case 0x40: - // Neither email addresses nor URLs allowed in strict mode - if ($this->_strict_mode) { - $this->_error('Neither email addresses nor URLs are allowed in strict mode.'); - return false; - } else { - // Skip first char - if ($k) { - $encoded = ''; - $encoded = $this->_encode(array_slice($decoded, $last_begin, (($k)-$last_begin))); - if ($encoded) { - $output .= $encoded; - } else { - $output .= $this->_ucs4_to_utf8(array_slice($decoded, $last_begin, (($k)-$last_begin))); - } - $output .= chr($decoded[$k]); - } - $last_begin = $k + 1; - } - } - } - // Catch the rest of the string - if ($last_begin) { - $inp_len = sizeof($decoded); - $encoded = ''; - $encoded = $this->_encode(array_slice($decoded, $last_begin, (($inp_len)-$last_begin))); - if ($encoded) { - $output .= $encoded; - } else { - $output .= $this->_ucs4_to_utf8(array_slice($decoded, $last_begin, (($inp_len)-$last_begin))); - } - return $output; - } else { - if ($output = $this->_encode($decoded)) { - return $output; - } else { - return $this->_ucs4_to_utf8($decoded); - } - } - } - - /** - * Use this method to get the last error ocurred - * @param void - * @return string The last error, that occured - * @access public - */ - function get_last_error() - { - return $this->_error; - } - - /** - * The actual decoding algorithm - * @access private - */ - function _decode($encoded) - { - // We do need to find the Punycode prefix - if (!preg_match('!^'.preg_quote($this->_punycode_prefix, '!').'!', $encoded)) { - $this->_error('This is not a punycode string'); - return false; - } - $encode_test = preg_replace('!^'.preg_quote($this->_punycode_prefix, '!').'!', '', $encoded); - // If nothing left after removing the prefix, it is hopeless - if (!$encode_test) { - $this->_error('The given encoded string was empty'); - return false; - } - // Find last occurence of the delimiter - $delim_pos = strrpos($encoded, '-'); - if ($delim_pos > strlen($this->_punycode_prefix)) { - for ($k = strlen($this->_punycode_prefix); $k < $delim_pos; ++$k) { - $decoded[] = ord($encoded{$k}); - } - } else { - $decoded = array(); - } - $deco_len = count($decoded); - $enco_len = strlen($encoded); - - // Wandering through the strings; init - $is_first = true; - $bias = $this->_initial_bias; - $idx = 0; - $char = $this->_initial_n; - - for ($enco_idx = ($delim_pos) ? ($delim_pos + 1) : 0; $enco_idx < $enco_len; ++$deco_len) { - for ($old_idx = $idx, $w = 1, $k = $this->_base; 1 ; $k += $this->_base) { - $digit = $this->_decode_digit($encoded{$enco_idx++}); - $idx += $digit * $w; - $t = ($k <= $bias) ? $this->_tmin : - (($k >= $bias + $this->_tmax) ? $this->_tmax : ($k - $bias)); - if ($digit < $t) break; - $w = (int) ($w * ($this->_base - $t)); - } - $bias = $this->_adapt($idx - $old_idx, $deco_len + 1, $is_first); - $is_first = false; - $char += (int) ($idx / ($deco_len + 1)); - $idx %= ($deco_len + 1); - if ($deco_len > 0) { - // Make room for the decoded char - for ($i = $deco_len; $i > $idx; $i--) { - $decoded[$i] = $decoded[($i - 1)]; - } - } - $decoded[$idx++] = $char; - } - return $this->_ucs4_to_utf8($decoded); - } - - /** - * The actual encoding algorithm - * @access private - */ - function _encode($decoded) - { - // We cannot encode a domain name containing the Punycode prefix - $extract = strlen($this->_punycode_prefix); - $check_pref = $this->_utf8_to_ucs4($this->_punycode_prefix); - $check_deco = array_slice($decoded, 0, $extract); - - if ($check_pref == $check_deco) { - $this->_error('This is already a punycode string'); - return false; - } - // We will not try to encode strings consisting of basic code points only - $encodable = false; - foreach ($decoded as $k => $v) { - if ($v > 0x7a) { - $encodable = true; - break; - } - } - if (!$encodable) { - $this->_error('The given string does not contain encodable chars'); - return false; - } - - // Do NAMEPREP - $decoded = $this->_nameprep($decoded); - if (!$decoded || !is_array($decoded)) return false; // NAMEPREP failed - - $deco_len = count($decoded); - if (!$deco_len) return false; // Empty array - - $codecount = 0; // How many chars have been consumed - - $encoded = ''; - // Copy all basic code points to output - for ($i = 0; $i < $deco_len; ++$i) { - $test = $decoded[$i]; - // Will match [-0-9a-zA-Z] - if ((0x2F < $test && $test < 0x40) || (0x40 < $test && $test < 0x5B) - || (0x60 < $test && $test <= 0x7B) || (0x2D == $test)) { - $encoded .= chr($decoded[$i]); - $codecount++; - } - } - if ($codecount == $deco_len) return $encoded; // All codepoints were basic ones - - // Start with the prefix; copy it to output - $encoded = $this->_punycode_prefix.$encoded; - - // If we have basic code points in output, add an hyphen to the end - if ($codecount) $encoded .= '-'; - - // Now find and encode all non-basic code points - $is_first = true; - $cur_code = $this->_initial_n; - $bias = $this->_initial_bias; - $delta = 0; - while ($codecount < $deco_len) { - // Find the smallest code point >= the current code point and - // remember the last ouccrence of it in the input - for ($i = 0, $next_code = $this->_max_ucs; $i < $deco_len; $i++) { - if ($decoded[$i] >= $cur_code && $decoded[$i] <= $next_code) { - $next_code = $decoded[$i]; - } - } - - $delta += ($next_code - $cur_code) * ($codecount + 1); - $cur_code = $next_code; - - // Scan input again and encode all characters whose code point is $cur_code - for ($i = 0; $i < $deco_len; $i++) { - if ($decoded[$i] < $cur_code) { - $delta++; - } elseif ($decoded[$i] == $cur_code) { - for ($q = $delta, $k = $this->_base; 1; $k += $this->_base) { - $t = ($k <= $bias) ? $this->_tmin : - (($k >= $bias + $this->_tmax) ? $this->_tmax : $k - $bias); - if ($q < $t) break; - $encoded .= $this->_encode_digit(intval($t + (($q - $t) % ($this->_base - $t)))); //v0.4.5 Changed from ceil() to intval() - $q = (int) (($q - $t) / ($this->_base - $t)); - } - $encoded .= $this->_encode_digit($q); - $bias = $this->_adapt($delta, $codecount+1, $is_first); - $codecount++; - $delta = 0; - $is_first = false; - } - } - $delta++; - $cur_code++; - } - return $encoded; - } - - /** - * Adapt the bias according to the current code point and position - * @access private - */ - function _adapt($delta, $npoints, $is_first) - { - $delta = intval($is_first ? ($delta / $this->_damp) : ($delta / 2)); - $delta += intval($delta / $npoints); - for ($k = 0; $delta > (($this->_base - $this->_tmin) * $this->_tmax) / 2; $k += $this->_base) { - $delta = intval($delta / ($this->_base - $this->_tmin)); - } - return intval($k + ($this->_base - $this->_tmin + 1) * $delta / ($delta + $this->_skew)); - } - - /** - * Encoding a certain digit - * @access private - */ - function _encode_digit($d) - { - return chr($d + 22 + 75 * ($d < 26)); - } - - /** - * Decode a certain digit - * @access private - */ - function _decode_digit($cp) - { - $cp = ord($cp); - return ($cp - 48 < 10) ? $cp - 22 : (($cp - 65 < 26) ? $cp - 65 : (($cp - 97 < 26) ? $cp - 97 : $this->_base)); - } - - /** - * Internal error handling method - * @access private - */ - function _error($error = '') - { - $this->_error = $error; - } - - /** - * Do Nameprep according to RFC3491 and RFC3454 - * @param array Unicode Characters - * @return string Unicode Characters, Nameprep'd - * @access private - */ - function _nameprep($input) - { - $output = array(); - $error = false; - // - // Mapping - // Walking through the input array, performing the required steps on each of - // the input chars and putting the result into the output array - // While mapping required chars we apply the cannonical ordering - foreach ($input as $v) { - // Map to nothing == skip that code point - if (in_array($v, $this->NP['map_nothing'])) continue; - - // Try to find prohibited input - if (in_array($v, $this->NP['prohibit']) || in_array($v, $this->NP['general_prohibited'])) { - $this->_error('NAMEPREP: Prohibited input U+'.sprintf('%08X', $v)); - return false; - } - foreach ($this->NP['prohibit_ranges'] as $range) { - if ($range[0] <= $v && $v <= $range[1]) { - $this->_error('NAMEPREP: Prohibited input U+'.sprintf('%08X', $v)); - return false; - } - } - // - // Hangul syllable decomposition - if (0xAC00 <= $v && $v <= 0xD7AF) { - foreach ($this->_hangul_decompose($v) as $out) { - $output[] = (int) $out; - } - // There's a decomposition mapping for that code point - } elseif (isset($this->NP['replacemaps'][$v])) { - foreach ($this->_apply_cannonical_ordering($this->NP['replacemaps'][$v]) as $out) { - $output[] = (int) $out; - } - } else { - $output[] = (int) $v; - } - } - // Before applying any Combining, try to rearrange any Hangul syllables - $output = $this->_hangul_compose($output); - // - // Combine code points - // - $last_class = 0; - $last_starter = 0; - $out_len = count($output); - for ($i = 0; $i < $out_len; ++$i) { - $class = $this->_get_combining_class($output[$i]); - if ((!$last_class || $last_class > $class) && $class) { - // Try to match - $seq_len = $i - $last_starter; - $out = $this->_combine(array_slice($output, $last_starter, $seq_len)); - // On match: Replace the last starter with the composed character and remove - // the now redundant non-starter(s) - if ($out) { - $output[$last_starter] = $out; - if (count($out) != $seq_len) { - for ($j = $i+1; $j < $out_len; ++$j) { - $output[$j-1] = $output[$j]; - } - unset($output[$out_len]); - } - // Rewind the for loop by one, since there can be more possible compositions - $i--; - $out_len--; - $last_class = ($i == $last_starter) ? 0 : $this->_get_combining_class($output[$i-1]); - continue; - } - } - // The current class is 0 - if (!$class) $last_starter = $i; - $last_class = $class; - } - return $output; - } - - /** - * Decomposes a Hangul syllable - * (see http://www.unicode.org/unicode/reports/tr15/#Hangul - * @param integer 32bit UCS4 code point - * @return array Either Hangul Syllable decomposed or original 32bit value as one value array - * @access private - */ - function _hangul_decompose($char) - { - $sindex = (int) $char - $this->_sbase; - if ($sindex < 0 || $sindex >= $this->_scount) { - return array($char); - } - $result = array(); - $result[] = (int) $this->_lbase + $sindex / $this->_ncount; - $result[] = (int) $this->_vbase + ($sindex % $this->_ncount) / $this->_tcount; - $T = intval($this->_tbase + $sindex % $this->_tcount); - if ($T != $this->_tbase) $result[] = $T; - return $result; - } - /** - * Ccomposes a Hangul syllable - * (see http://www.unicode.org/unicode/reports/tr15/#Hangul - * @param array Decomposed UCS4 sequence - * @return array UCS4 sequence with syllables composed - * @access private - */ - function _hangul_compose($input) - { - $inp_len = count($input); - if (!$inp_len) return array(); - $result = array(); - $last = (int) $input[0]; - $result[] = $last; // copy first char from input to output - - for ($i = 1; $i < $inp_len; ++$i) { - $char = (int) $input[$i]; - $sindex = $last - $this->_sbase; - $lindex = $last - $this->_lbase; - $vindex = $char - $this->_vbase; - $tindex = $char - $this->_tbase; - // Find out, whether two current characters are LV and T - if (0 <= $sindex && $sindex < $this->_scount && ($sindex % $this->_tcount == 0) - && 0 <= $tindex && $tindex <= $this->_tcount) { - // create syllable of form LVT - $last += $tindex; - $result[(count($result) - 1)] = $last; // reset last - continue; // discard char - } - // Find out, whether two current characters form L and V - if (0 <= $lindex && $lindex < $this->_lcount && 0 <= $vindex && $vindex < $this->_vcount) { - // create syllable of form LV - $last = (int) $this->_sbase + ($lindex * $this->_vcount + $vindex) * $this->_tcount; - $result[(count($result) - 1)] = $last; // reset last - continue; // discard char - } - // if neither case was true, just add the character - $last = $char; - $result[] = $char; - } - return $result; - } - - /** - * Returns the combining class of a certain wide char - * @param integer Wide char to check (32bit integer) - * @return integer Combining class if found, else 0 - * @access private - */ - function _get_combining_class($char) - { - return isset($this->NP['norm_combcls'][$char]) ? $this->NP['norm_combcls'][$char] : 0; - } - - /** - * Apllies the cannonical ordering of a decomposed UCS4 sequence - * @param array Decomposed UCS4 sequence - * @return array Ordered USC4 sequence - * @access private - */ - function _apply_cannonical_ordering($input) - { - $swap = true; - $size = count($input); - while ($swap) { - $swap = false; - $last = $this->_get_combining_class(intval($input[0])); - for ($i = 0; $i < $size-1; ++$i) { - $next = $this->_get_combining_class(intval($input[$i+1])); - if ($next != 0 && $last > $next) { - // Move item leftward until it fits - for ($j = $i + 1; $j > 0; --$j) { - if ($this->_get_combining_class(intval($input[$j-1])) <= $next) break; - $t = intval($input[$j]); - $input[$j] = intval($input[$j-1]); - $input[$j-1] = $t; - $swap = true; - } - // Reentering the loop looking at the old character again - $next = $last; - } - $last = $next; - } - } - return $input; - } - - /** - * Do composition of a sequence of starter and non-starter - * @param array UCS4 Decomposed sequence - * @return array Ordered USC4 sequence - * @access private - */ - function _combine($input) - { - $inp_len = count($input); - foreach ($this->NP['replacemaps'] as $np_src => $np_target) { - if ($np_target[0] != $input[0]) continue; - if (count($np_target) != $inp_len) continue; - $hit = false; - foreach ($input as $k2 => $v2) { - if ($v2 == $np_target[$k2]) { - $hit = true; - } else { - $hit = false; - break; - } - } - if ($hit) return $np_src; - } - return false; - } - - /** - * This converts an UTF-8 encoded string to its UCS-4 representation - * By talking about UCS-4 "strings" we mean arrays of 32bit integers representing - * each of the "chars". This is due to PHP not being able to handle strings with - * bit depth different from 8. This apllies to the reverse method _ucs4_to_utf8(), too. - * The following UTF-8 encodings are supported: - * bytes bits representation - * 1 7 0xxxxxxx - * 2 11 110xxxxx 10xxxxxx - * 3 16 1110xxxx 10xxxxxx 10xxxxxx - * 4 21 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - * 5 26 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx - * 6 31 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx - * Each x represents a bit that can be used to store character data. - * The five and six byte sequences are part of Annex D of ISO/IEC 10646-1:2000 - * @access private - */ - function _utf8_to_ucs4($input) - { - $output = array(); - $out_len = 0; - $inp_len = strlen($input); - $mode = 'next'; - $test = 'none'; - for ($k = 0; $k < $inp_len; ++$k) { - $v = ord($input{$k}); // Extract byte from input string - - if ($v < 128) { // We found an ASCII char - put into stirng as is - $output[$out_len] = $v; - ++$out_len; - if ('add' == $mode) { - $this->_error('Conversion from UTF-8 to UCS-4 failed: malformed input at byte '.$k); - return false; - } - continue; - } - if ('next' == $mode) { // Try to find the next start byte; determine the width of the Unicode char - $start_byte = $v; - $mode = 'add'; - $test = 'range'; - if ($v >> 5 == 6) { // &110xxxxx 10xxxxx - $next_byte = 0; // Tells, how many times subsequent bitmasks must rotate 6bits to the left - $v = ($v - 192) << 6; - } elseif ($v >> 4 == 14) { // &1110xxxx 10xxxxxx 10xxxxxx - $next_byte = 1; - $v = ($v - 224) << 12; - } elseif ($v >> 3 == 30) { // &11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - $next_byte = 2; - $v = ($v - 240) << 18; - } elseif ($v >> 2 == 62) { // &111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx - $next_byte = 3; - $v = ($v - 248) << 24; - } elseif ($v >> 1 == 126) { // &1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx - $next_byte = 4; - $v = ($v - 252) << 30; - } else { - $this->_error('This might be UTF-8, but I don\'t understand it at byte '.$k); - return false; - } - if ('add' == $mode) { - $output[$out_len] = (int) $v; - ++$out_len; - continue; - } - } - if ('add' == $mode) { - if (!$this->_allow_overlong && $test == 'range') { - $test = 'none'; - if (($v < 0xA0 && $start_byte == 0xE0) || ($v < 0x90 && $start_byte == 0xF0) || ($v > 0x8F && $start_byte == 0xF4)) { - $this->_error('Bogus UTF-8 character detected (out of legal range) at byte '.$k); - return false; - } - } - if ($v >> 6 == 2) { // Bit mask must be 10xxxxxx - $v = ($v - 128) << ($next_byte * 6); - $output[($out_len - 1)] += $v; - --$next_byte; - } else { - $this->_error('Conversion from UTF-8 to UCS-4 failed: malformed input at byte '.$k); - return false; - } - if ($next_byte < 0) { - $mode = 'next'; - } - } - } // for - return $output; - } - - /** - * Convert UCS-4 string into UTF-8 string - * See _utf8_to_ucs4() for details - * @access private - */ - function _ucs4_to_utf8($input) - { - $output = ''; - $k = 0; - foreach ($input as $v) { - ++$k; - // $v = ord($v); - if ($v < 128) { // 7bit are transferred literally - $output .= chr($v); - } elseif ($v < (1 << 11)) { // 2 bytes - $output .= chr(192 + ($v >> 6)) . chr(128 + ($v & 63)); - } elseif ($v < (1 << 16)) { // 3 bytes - $output .= chr(224 + ($v >> 12)) . chr(128 + (($v >> 6) & 63)) . chr(128 + ($v & 63)); - } elseif ($v < (1 << 21)) { // 4 bytes - $output .= chr(240 + ($v >> 18)) . chr(128 + (($v >> 12) & 63)) - . chr(128 + (($v >> 6) & 63)) . chr(128 + ($v & 63)); - } elseif ($v < (1 << 26)) { // 5 bytes - $output .= chr(248 + ($v >> 24)) . chr(128 + (($v >> 18) & 63)) - . chr(128 + (($v >> 12) & 63)) . chr(128 + (($v >> 6) & 63)) - . chr(128 + ($v & 63)); - } elseif ($v < (1 << 31)) { // 6 bytes - $output .= chr(252 + ($v >> 30)) . chr(128 + (($v >> 24) & 63)) - . chr(128 + (($v >> 18) & 63)) . chr(128 + (($v >> 12) & 63)) - . chr(128 + (($v >> 6) & 63)) . chr(128 + ($v & 63)); - } else { - $this->_error('Conversion from UCS-4 to UTF-8 failed: malformed input at byte '.$k); - return false; - } - } - return $output; - } - - /** - * Convert UCS-4 array into UCS-4 string - * - * @access private - */ - function _ucs4_to_ucs4_string($input) - { - $output = ''; - // Take array values and split output to 4 bytes per value - // The bit mask is 255, which reads &11111111 - foreach ($input as $v) { - $output .= chr(($v >> 24) & 255).chr(($v >> 16) & 255).chr(($v >> 8) & 255).chr($v & 255); - } - return $output; - } - - /** - * Convert UCS-4 strin into UCS-4 garray - * - * @access private - */ - function _ucs4_string_to_ucs4($input) - { - $output = array(); - $inp_len = strlen($input); - // Input length must be dividable by 4 - if ($inp_len % 4) { - $this->_error('Input UCS4 string is broken'); - return false; - } - // Empty input - return empty output - if (!$inp_len) return $output; - for ($i = 0, $out_len = -1; $i < $inp_len; ++$i) { - // Increment output position every 4 input bytes - if (!($i % 4)) { - $out_len++; - $output[$out_len] = 0; - } - $output[$out_len] += ord($input{$i}) << (8 * (3 - ($i % 4) ) ); - } - return $output; - } -} - -/** -* Adapter class for aligning the API of idna_convert with that of Net_IDNA -* @author Matthias Sommerfeld -*/ -class Net_IDNA_php4 extends idna_convert -{ - /** - * Sets a new option value. Available options and values: - * [encoding - Use either UTF-8, UCS4 as array or UCS4 as string as input ('utf8' for UTF-8, - * 'ucs4_string' and 'ucs4_array' respectively for UCS4); The output is always UTF-8] - * [overlong - Unicode does not allow unnecessarily long encodings of chars, - * to allow this, set this parameter to true, else to false; - * default is false.] - * [strict - true: strict mode, good for registration purposes - Causes errors - * on failures; false: loose mode, ideal for "wildlife" applications - * by silently ignoring errors and returning the original input instead - * - * @param mixed Parameter to set (string: single parameter; array of Parameter => Value pairs) - * @param string Value to use (if parameter 1 is a string) - * @return boolean true on success, false otherwise - * @access public - */ - function setParams($option, $param = false) - { - return $this->IC->set_parameters($option, $param); - } -} - -?> \ No newline at end of file diff --git a/simplepie/idn/npdata.ser b/simplepie/idn/npdata.ser deleted file mode 100644 index d7ce6d0..0000000 --- a/simplepie/idn/npdata.ser +++ /dev/null @@ -1 +0,0 @@ -a:6:{s:11:"map_nothing";a:27:{i:0;i:173;i:1;i:847;i:2;i:6150;i:3;i:6155;i:4;i:6156;i:5;i:6157;i:6;i:8203;i:7;i:8204;i:8;i:8205;i:9;i:8288;i:10;i:65024;i:11;i:65025;i:12;i:65026;i:13;i:65027;i:14;i:65028;i:15;i:65029;i:16;i:65030;i:17;i:65031;i:18;i:65032;i:19;i:65033;i:20;i:65034;i:21;i:65035;i:22;i:65036;i:23;i:65037;i:24;i:65038;i:25;i:65039;i:26;i:65279;}s:18:"general_prohibited";a:64:{i:0;i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:7;i:7;i:8;i:8;i:9;i:9;i:10;i:10;i:11;i:11;i:12;i:12;i:13;i:13;i:14;i:14;i:15;i:15;i:16;i:16;i:17;i:17;i:18;i:18;i:19;i:19;i:20;i:20;i:21;i:21;i:22;i:22;i:23;i:23;i:24;i:24;i:25;i:25;i:26;i:26;i:27;i:27;i:28;i:28;i:29;i:29;i:30;i:30;i:31;i:31;i:32;i:32;i:33;i:33;i:34;i:34;i:35;i:35;i:36;i:36;i:37;i:37;i:38;i:38;i:39;i:39;i:40;i:40;i:41;i:41;i:42;i:42;i:43;i:43;i:44;i:44;i:45;i:47;i:46;i:59;i:47;i:60;i:48;i:61;i:49;i:62;i:50;i:63;i:51;i:64;i:52;i:91;i:53;i:92;i:54;i:93;i:55;i:94;i:56;i:95;i:57;i:96;i:58;i:123;i:59;i:124;i:60;i:125;i:61;i:126;i:62;i:127;i:63;i:12290;}s:8:"prohibit";a:84:{i:0;i:160;i:1;i:5760;i:2;i:8192;i:3;i:8193;i:4;i:8194;i:5;i:8195;i:6;i:8196;i:7;i:8197;i:8;i:8198;i:9;i:8199;i:10;i:8200;i:11;i:8201;i:12;i:8202;i:13;i:8203;i:14;i:8239;i:15;i:8287;i:16;i:12288;i:17;i:1757;i:18;i:1807;i:19;i:6158;i:20;i:8204;i:21;i:8205;i:22;i:8232;i:23;i:8233;i:24;i:65279;i:25;i:65529;i:26;i:65530;i:27;i:65531;i:28;i:65532;i:29;i:65534;i:30;i:65535;i:31;i:131070;i:32;i:131071;i:33;i:196606;i:34;i:196607;i:35;i:262142;i:36;i:262143;i:37;i:327678;i:38;i:327679;i:39;i:393214;i:40;i:393215;i:41;i:458750;i:42;i:458751;i:43;i:524286;i:44;i:524287;i:45;i:589822;i:46;i:589823;i:47;i:655358;i:48;i:655359;i:49;i:720894;i:50;i:720895;i:51;i:786430;i:52;i:786431;i:53;i:851966;i:54;i:851967;i:55;i:917502;i:56;i:917503;i:57;i:983038;i:58;i:983039;i:59;i:1048574;i:60;i:1048575;i:61;i:1114110;i:62;i:1114111;i:63;i:65529;i:64;i:65530;i:65;i:65531;i:66;i:65532;i:67;i:65533;i:68;i:832;i:69;i:833;i:70;i:8206;i:71;i:8207;i:72;i:8234;i:73;i:8235;i:74;i:8236;i:75;i:8237;i:76;i:8238;i:77;i:8298;i:78;i:8299;i:79;i:8300;i:80;i:8301;i:81;i:8302;i:82;i:8303;i:83;i:917505;}s:15:"prohibit_ranges";a:10:{i:0;a:2:{i:0;i:128;i:1;i:159;}i:1;a:2:{i:0;i:8288;i:1;i:8303;}i:2;a:2:{i:0;i:119155;i:1;i:119162;}i:3;a:2:{i:0;i:57344;i:1;i:63743;}i:4;a:2:{i:0;i:983040;i:1;i:1048573;}i:5;a:2:{i:0;i:1048576;i:1;i:1114109;}i:6;a:2:{i:0;i:64976;i:1;i:65007;}i:7;a:2:{i:0;i:55296;i:1;i:57343;}i:8;a:2:{i:0;i:12272;i:1;i:12283;}i:9;a:2:{i:0;i:917536;i:1;i:917631;}}s:11:"replacemaps";a:1401:{i:65;a:1:{i:0;i:97;}i:66;a:1:{i:0;i:98;}i:67;a:1:{i:0;i:99;}i:68;a:1:{i:0;i:100;}i:69;a:1:{i:0;i:101;}i:70;a:1:{i:0;i:102;}i:71;a:1:{i:0;i:103;}i:72;a:1:{i:0;i:104;}i:73;a:1:{i:0;i:105;}i:74;a:1:{i:0;i:106;}i:75;a:1:{i:0;i:107;}i:76;a:1:{i:0;i:108;}i:77;a:1:{i:0;i:109;}i:78;a:1:{i:0;i:110;}i:79;a:1:{i:0;i:111;}i:80;a:1:{i:0;i:112;}i:81;a:1:{i:0;i:113;}i:82;a:1:{i:0;i:114;}i:83;a:1:{i:0;i:115;}i:84;a:1:{i:0;i:116;}i:85;a:1:{i:0;i:117;}i:86;a:1:{i:0;i:118;}i:87;a:1:{i:0;i:119;}i:88;a:1:{i:0;i:120;}i:89;a:1:{i:0;i:121;}i:90;a:1:{i:0;i:122;}i:181;a:1:{i:0;i:956;}i:192;a:1:{i:0;i:224;}i:193;a:1:{i:0;i:225;}i:194;a:1:{i:0;i:226;}i:195;a:1:{i:0;i:227;}i:196;a:1:{i:0;i:228;}i:197;a:1:{i:0;i:229;}i:198;a:1:{i:0;i:230;}i:199;a:1:{i:0;i:231;}i:200;a:1:{i:0;i:232;}i:201;a:1:{i:0;i:233;}i:202;a:1:{i:0;i:234;}i:203;a:1:{i:0;i:235;}i:204;a:1:{i:0;i:236;}i:205;a:1:{i:0;i:237;}i:206;a:1:{i:0;i:238;}i:207;a:1:{i:0;i:239;}i:208;a:1:{i:0;i:240;}i:209;a:1:{i:0;i:241;}i:210;a:1:{i:0;i:242;}i:211;a:1:{i:0;i:243;}i:212;a:1:{i:0;i:244;}i:213;a:1:{i:0;i:245;}i:214;a:1:{i:0;i:246;}i:216;a:1:{i:0;i:248;}i:217;a:1:{i:0;i:249;}i:218;a:1:{i:0;i:250;}i:219;a:1:{i:0;i:251;}i:220;a:1:{i:0;i:252;}i:221;a:1:{i:0;i:253;}i:222;a:1:{i:0;i:254;}i:223;a:2:{i:0;i:115;i:1;i:115;}i:256;a:1:{i:0;i:257;}i:258;a:1:{i:0;i:259;}i:260;a:1:{i:0;i:261;}i:262;a:1:{i:0;i:263;}i:264;a:1:{i:0;i:265;}i:266;a:1:{i:0;i:267;}i:268;a:1:{i:0;i:269;}i:270;a:1:{i:0;i:271;}i:272;a:1:{i:0;i:273;}i:274;a:1:{i:0;i:275;}i:276;a:1:{i:0;i:277;}i:278;a:1:{i:0;i:279;}i:280;a:1:{i:0;i:281;}i:282;a:1:{i:0;i:283;}i:284;a:1:{i:0;i:285;}i:286;a:1:{i:0;i:287;}i:288;a:1:{i:0;i:289;}i:290;a:1:{i:0;i:291;}i:292;a:1:{i:0;i:293;}i:294;a:1:{i:0;i:295;}i:296;a:1:{i:0;i:297;}i:298;a:1:{i:0;i:299;}i:300;a:1:{i:0;i:301;}i:302;a:1:{i:0;i:303;}i:304;a:2:{i:0;i:105;i:1;i:775;}i:306;a:1:{i:0;i:307;}i:308;a:1:{i:0;i:309;}i:310;a:1:{i:0;i:311;}i:313;a:1:{i:0;i:314;}i:315;a:1:{i:0;i:316;}i:317;a:1:{i:0;i:318;}i:319;a:1:{i:0;i:320;}i:321;a:1:{i:0;i:322;}i:323;a:1:{i:0;i:324;}i:325;a:1:{i:0;i:326;}i:327;a:1:{i:0;i:328;}i:329;a:2:{i:0;i:700;i:1;i:110;}i:330;a:1:{i:0;i:331;}i:332;a:1:{i:0;i:333;}i:334;a:1:{i:0;i:335;}i:336;a:1:{i:0;i:337;}i:338;a:1:{i:0;i:339;}i:340;a:1:{i:0;i:341;}i:342;a:1:{i:0;i:343;}i:344;a:1:{i:0;i:345;}i:346;a:1:{i:0;i:347;}i:348;a:1:{i:0;i:349;}i:350;a:1:{i:0;i:351;}i:352;a:1:{i:0;i:353;}i:354;a:1:{i:0;i:355;}i:356;a:1:{i:0;i:357;}i:358;a:1:{i:0;i:359;}i:360;a:1:{i:0;i:361;}i:362;a:1:{i:0;i:363;}i:364;a:1:{i:0;i:365;}i:366;a:1:{i:0;i:367;}i:368;a:1:{i:0;i:369;}i:370;a:1:{i:0;i:371;}i:372;a:1:{i:0;i:373;}i:374;a:1:{i:0;i:375;}i:376;a:1:{i:0;i:255;}i:377;a:1:{i:0;i:378;}i:379;a:1:{i:0;i:380;}i:381;a:1:{i:0;i:382;}i:383;a:1:{i:0;i:115;}i:385;a:1:{i:0;i:595;}i:386;a:1:{i:0;i:387;}i:388;a:1:{i:0;i:389;}i:390;a:1:{i:0;i:596;}i:391;a:1:{i:0;i:392;}i:393;a:1:{i:0;i:598;}i:394;a:1:{i:0;i:599;}i:395;a:1:{i:0;i:396;}i:398;a:1:{i:0;i:477;}i:399;a:1:{i:0;i:601;}i:400;a:1:{i:0;i:603;}i:401;a:1:{i:0;i:402;}i:403;a:1:{i:0;i:608;}i:404;a:1:{i:0;i:611;}i:406;a:1:{i:0;i:617;}i:407;a:1:{i:0;i:616;}i:408;a:1:{i:0;i:409;}i:412;a:1:{i:0;i:623;}i:413;a:1:{i:0;i:626;}i:415;a:1:{i:0;i:629;}i:416;a:1:{i:0;i:417;}i:418;a:1:{i:0;i:419;}i:420;a:1:{i:0;i:421;}i:422;a:1:{i:0;i:640;}i:423;a:1:{i:0;i:424;}i:425;a:1:{i:0;i:643;}i:428;a:1:{i:0;i:429;}i:430;a:1:{i:0;i:648;}i:431;a:1:{i:0;i:432;}i:433;a:1:{i:0;i:650;}i:434;a:1:{i:0;i:651;}i:435;a:1:{i:0;i:436;}i:437;a:1:{i:0;i:438;}i:439;a:1:{i:0;i:658;}i:440;a:1:{i:0;i:441;}i:444;a:1:{i:0;i:445;}i:452;a:1:{i:0;i:454;}i:453;a:1:{i:0;i:454;}i:455;a:1:{i:0;i:457;}i:456;a:1:{i:0;i:457;}i:458;a:1:{i:0;i:460;}i:459;a:1:{i:0;i:460;}i:461;a:1:{i:0;i:462;}i:463;a:1:{i:0;i:464;}i:465;a:1:{i:0;i:466;}i:467;a:1:{i:0;i:468;}i:469;a:1:{i:0;i:470;}i:471;a:1:{i:0;i:472;}i:473;a:1:{i:0;i:474;}i:475;a:1:{i:0;i:476;}i:478;a:1:{i:0;i:479;}i:480;a:1:{i:0;i:481;}i:482;a:1:{i:0;i:483;}i:484;a:1:{i:0;i:485;}i:486;a:1:{i:0;i:487;}i:488;a:1:{i:0;i:489;}i:490;a:1:{i:0;i:491;}i:492;a:1:{i:0;i:493;}i:494;a:1:{i:0;i:495;}i:496;a:2:{i:0;i:106;i:1;i:780;}i:497;a:1:{i:0;i:499;}i:498;a:1:{i:0;i:499;}i:500;a:1:{i:0;i:501;}i:502;a:1:{i:0;i:405;}i:503;a:1:{i:0;i:447;}i:504;a:1:{i:0;i:505;}i:506;a:1:{i:0;i:507;}i:508;a:1:{i:0;i:509;}i:510;a:1:{i:0;i:511;}i:512;a:1:{i:0;i:513;}i:514;a:1:{i:0;i:515;}i:516;a:1:{i:0;i:517;}i:518;a:1:{i:0;i:519;}i:520;a:1:{i:0;i:521;}i:522;a:1:{i:0;i:523;}i:524;a:1:{i:0;i:525;}i:526;a:1:{i:0;i:527;}i:528;a:1:{i:0;i:529;}i:530;a:1:{i:0;i:531;}i:532;a:1:{i:0;i:533;}i:534;a:1:{i:0;i:535;}i:536;a:1:{i:0;i:537;}i:538;a:1:{i:0;i:539;}i:540;a:1:{i:0;i:541;}i:542;a:1:{i:0;i:543;}i:544;a:1:{i:0;i:414;}i:546;a:1:{i:0;i:547;}i:548;a:1:{i:0;i:549;}i:550;a:1:{i:0;i:551;}i:552;a:1:{i:0;i:553;}i:554;a:1:{i:0;i:555;}i:556;a:1:{i:0;i:557;}i:558;a:1:{i:0;i:559;}i:560;a:1:{i:0;i:561;}i:562;a:1:{i:0;i:563;}i:837;a:1:{i:0;i:953;}i:890;a:2:{i:0;i:32;i:1;i:953;}i:902;a:1:{i:0;i:940;}i:904;a:1:{i:0;i:941;}i:905;a:1:{i:0;i:942;}i:906;a:1:{i:0;i:943;}i:908;a:1:{i:0;i:972;}i:910;a:1:{i:0;i:973;}i:911;a:1:{i:0;i:974;}i:912;a:3:{i:0;i:953;i:1;i:776;i:2;i:769;}i:913;a:1:{i:0;i:945;}i:914;a:1:{i:0;i:946;}i:915;a:1:{i:0;i:947;}i:916;a:1:{i:0;i:948;}i:917;a:1:{i:0;i:949;}i:918;a:1:{i:0;i:950;}i:919;a:1:{i:0;i:951;}i:920;a:1:{i:0;i:952;}i:921;a:1:{i:0;i:953;}i:922;a:1:{i:0;i:954;}i:923;a:1:{i:0;i:955;}i:924;a:1:{i:0;i:956;}i:925;a:1:{i:0;i:957;}i:926;a:1:{i:0;i:958;}i:927;a:1:{i:0;i:959;}i:928;a:1:{i:0;i:960;}i:929;a:1:{i:0;i:961;}i:931;a:1:{i:0;i:963;}i:932;a:1:{i:0;i:964;}i:933;a:1:{i:0;i:965;}i:934;a:1:{i:0;i:966;}i:935;a:1:{i:0;i:967;}i:936;a:1:{i:0;i:968;}i:937;a:1:{i:0;i:969;}i:938;a:1:{i:0;i:970;}i:939;a:1:{i:0;i:971;}i:944;a:3:{i:0;i:965;i:1;i:776;i:2;i:769;}i:962;a:1:{i:0;i:963;}i:976;a:1:{i:0;i:946;}i:977;a:1:{i:0;i:952;}i:978;a:1:{i:0;i:965;}i:979;a:1:{i:0;i:973;}i:980;a:1:{i:0;i:971;}i:981;a:1:{i:0;i:966;}i:982;a:1:{i:0;i:960;}i:984;a:1:{i:0;i:985;}i:986;a:1:{i:0;i:987;}i:988;a:1:{i:0;i:989;}i:990;a:1:{i:0;i:991;}i:992;a:1:{i:0;i:993;}i:994;a:1:{i:0;i:995;}i:996;a:1:{i:0;i:997;}i:998;a:1:{i:0;i:999;}i:1000;a:1:{i:0;i:1001;}i:1002;a:1:{i:0;i:1003;}i:1004;a:1:{i:0;i:1005;}i:1006;a:1:{i:0;i:1007;}i:1008;a:1:{i:0;i:954;}i:1009;a:1:{i:0;i:961;}i:1010;a:1:{i:0;i:963;}i:1012;a:1:{i:0;i:952;}i:1013;a:1:{i:0;i:949;}i:1024;a:1:{i:0;i:1104;}i:1025;a:1:{i:0;i:1105;}i:1026;a:1:{i:0;i:1106;}i:1027;a:1:{i:0;i:1107;}i:1028;a:1:{i:0;i:1108;}i:1029;a:1:{i:0;i:1109;}i:1030;a:1:{i:0;i:1110;}i:1031;a:1:{i:0;i:1111;}i:1032;a:1:{i:0;i:1112;}i:1033;a:1:{i:0;i:1113;}i:1034;a:1:{i:0;i:1114;}i:1035;a:1:{i:0;i:1115;}i:1036;a:1:{i:0;i:1116;}i:1037;a:1:{i:0;i:1117;}i:1038;a:1:{i:0;i:1118;}i:1039;a:1:{i:0;i:1119;}i:1040;a:1:{i:0;i:1072;}i:1041;a:1:{i:0;i:1073;}i:1042;a:1:{i:0;i:1074;}i:1043;a:1:{i:0;i:1075;}i:1044;a:1:{i:0;i:1076;}i:1045;a:1:{i:0;i:1077;}i:1046;a:1:{i:0;i:1078;}i:1047;a:1:{i:0;i:1079;}i:1048;a:1:{i:0;i:1080;}i:1049;a:1:{i:0;i:1081;}i:1050;a:1:{i:0;i:1082;}i:1051;a:1:{i:0;i:1083;}i:1052;a:1:{i:0;i:1084;}i:1053;a:1:{i:0;i:1085;}i:1054;a:1:{i:0;i:1086;}i:1055;a:1:{i:0;i:1087;}i:1056;a:1:{i:0;i:1088;}i:1057;a:1:{i:0;i:1089;}i:1058;a:1:{i:0;i:1090;}i:1059;a:1:{i:0;i:1091;}i:1060;a:1:{i:0;i:1092;}i:1061;a:1:{i:0;i:1093;}i:1062;a:1:{i:0;i:1094;}i:1063;a:1:{i:0;i:1095;}i:1064;a:1:{i:0;i:1096;}i:1065;a:1:{i:0;i:1097;}i:1066;a:1:{i:0;i:1098;}i:1067;a:1:{i:0;i:1099;}i:1068;a:1:{i:0;i:1100;}i:1069;a:1:{i:0;i:1101;}i:1070;a:1:{i:0;i:1102;}i:1071;a:1:{i:0;i:1103;}i:1120;a:1:{i:0;i:1121;}i:1122;a:1:{i:0;i:1123;}i:1124;a:1:{i:0;i:1125;}i:1126;a:1:{i:0;i:1127;}i:1128;a:1:{i:0;i:1129;}i:1130;a:1:{i:0;i:1131;}i:1132;a:1:{i:0;i:1133;}i:1134;a:1:{i:0;i:1135;}i:1136;a:1:{i:0;i:1137;}i:1138;a:1:{i:0;i:1139;}i:1140;a:1:{i:0;i:1141;}i:1142;a:1:{i:0;i:1143;}i:1144;a:1:{i:0;i:1145;}i:1146;a:1:{i:0;i:1147;}i:1148;a:1:{i:0;i:1149;}i:1150;a:1:{i:0;i:1151;}i:1152;a:1:{i:0;i:1153;}i:1162;a:1:{i:0;i:1163;}i:1164;a:1:{i:0;i:1165;}i:1166;a:1:{i:0;i:1167;}i:1168;a:1:{i:0;i:1169;}i:1170;a:1:{i:0;i:1171;}i:1172;a:1:{i:0;i:1173;}i:1174;a:1:{i:0;i:1175;}i:1176;a:1:{i:0;i:1177;}i:1178;a:1:{i:0;i:1179;}i:1180;a:1:{i:0;i:1181;}i:1182;a:1:{i:0;i:1183;}i:1184;a:1:{i:0;i:1185;}i:1186;a:1:{i:0;i:1187;}i:1188;a:1:{i:0;i:1189;}i:1190;a:1:{i:0;i:1191;}i:1192;a:1:{i:0;i:1193;}i:1194;a:1:{i:0;i:1195;}i:1196;a:1:{i:0;i:1197;}i:1198;a:1:{i:0;i:1199;}i:1200;a:1:{i:0;i:1201;}i:1202;a:1:{i:0;i:1203;}i:1204;a:1:{i:0;i:1205;}i:1206;a:1:{i:0;i:1207;}i:1208;a:1:{i:0;i:1209;}i:1210;a:1:{i:0;i:1211;}i:1212;a:1:{i:0;i:1213;}i:1214;a:1:{i:0;i:1215;}i:1217;a:1:{i:0;i:1218;}i:1219;a:1:{i:0;i:1220;}i:1221;a:1:{i:0;i:1222;}i:1223;a:1:{i:0;i:1224;}i:1225;a:1:{i:0;i:1226;}i:1227;a:1:{i:0;i:1228;}i:1229;a:1:{i:0;i:1230;}i:1232;a:1:{i:0;i:1233;}i:1234;a:1:{i:0;i:1235;}i:1236;a:1:{i:0;i:1237;}i:1238;a:1:{i:0;i:1239;}i:1240;a:1:{i:0;i:1241;}i:1242;a:1:{i:0;i:1243;}i:1244;a:1:{i:0;i:1245;}i:1246;a:1:{i:0;i:1247;}i:1248;a:1:{i:0;i:1249;}i:1250;a:1:{i:0;i:1251;}i:1252;a:1:{i:0;i:1253;}i:1254;a:1:{i:0;i:1255;}i:1256;a:1:{i:0;i:1257;}i:1258;a:1:{i:0;i:1259;}i:1260;a:1:{i:0;i:1261;}i:1262;a:1:{i:0;i:1263;}i:1264;a:1:{i:0;i:1265;}i:1266;a:1:{i:0;i:1267;}i:1268;a:1:{i:0;i:1269;}i:1272;a:1:{i:0;i:1273;}i:1280;a:1:{i:0;i:1281;}i:1282;a:1:{i:0;i:1283;}i:1284;a:1:{i:0;i:1285;}i:1286;a:1:{i:0;i:1287;}i:1288;a:1:{i:0;i:1289;}i:1290;a:1:{i:0;i:1291;}i:1292;a:1:{i:0;i:1293;}i:1294;a:1:{i:0;i:1295;}i:1329;a:1:{i:0;i:1377;}i:1330;a:1:{i:0;i:1378;}i:1331;a:1:{i:0;i:1379;}i:1332;a:1:{i:0;i:1380;}i:1333;a:1:{i:0;i:1381;}i:1334;a:1:{i:0;i:1382;}i:1335;a:1:{i:0;i:1383;}i:1336;a:1:{i:0;i:1384;}i:1337;a:1:{i:0;i:1385;}i:1338;a:1:{i:0;i:1386;}i:1339;a:1:{i:0;i:1387;}i:1340;a:1:{i:0;i:1388;}i:1341;a:1:{i:0;i:1389;}i:1342;a:1:{i:0;i:1390;}i:1343;a:1:{i:0;i:1391;}i:1344;a:1:{i:0;i:1392;}i:1345;a:1:{i:0;i:1393;}i:1346;a:1:{i:0;i:1394;}i:1347;a:1:{i:0;i:1395;}i:1348;a:1:{i:0;i:1396;}i:1349;a:1:{i:0;i:1397;}i:1350;a:1:{i:0;i:1398;}i:1351;a:1:{i:0;i:1399;}i:1352;a:1:{i:0;i:1400;}i:1353;a:1:{i:0;i:1401;}i:1354;a:1:{i:0;i:1402;}i:1355;a:1:{i:0;i:1403;}i:1356;a:1:{i:0;i:1404;}i:1357;a:1:{i:0;i:1405;}i:1358;a:1:{i:0;i:1406;}i:1359;a:1:{i:0;i:1407;}i:1360;a:1:{i:0;i:1408;}i:1361;a:1:{i:0;i:1409;}i:1362;a:1:{i:0;i:1410;}i:1363;a:1:{i:0;i:1411;}i:1364;a:1:{i:0;i:1412;}i:1365;a:1:{i:0;i:1413;}i:1366;a:1:{i:0;i:1414;}i:1415;a:2:{i:0;i:1381;i:1;i:1410;}i:7680;a:1:{i:0;i:7681;}i:7682;a:1:{i:0;i:7683;}i:7684;a:1:{i:0;i:7685;}i:7686;a:1:{i:0;i:7687;}i:7688;a:1:{i:0;i:7689;}i:7690;a:1:{i:0;i:7691;}i:7692;a:1:{i:0;i:7693;}i:7694;a:1:{i:0;i:7695;}i:7696;a:1:{i:0;i:7697;}i:7698;a:1:{i:0;i:7699;}i:7700;a:1:{i:0;i:7701;}i:7702;a:1:{i:0;i:7703;}i:7704;a:1:{i:0;i:7705;}i:7706;a:1:{i:0;i:7707;}i:7708;a:1:{i:0;i:7709;}i:7710;a:1:{i:0;i:7711;}i:7712;a:1:{i:0;i:7713;}i:7714;a:1:{i:0;i:7715;}i:7716;a:1:{i:0;i:7717;}i:7718;a:1:{i:0;i:7719;}i:7720;a:1:{i:0;i:7721;}i:7722;a:1:{i:0;i:7723;}i:7724;a:1:{i:0;i:7725;}i:7726;a:1:{i:0;i:7727;}i:7728;a:1:{i:0;i:7729;}i:7730;a:1:{i:0;i:7731;}i:7732;a:1:{i:0;i:7733;}i:7734;a:1:{i:0;i:7735;}i:7736;a:1:{i:0;i:7737;}i:7738;a:1:{i:0;i:7739;}i:7740;a:1:{i:0;i:7741;}i:7742;a:1:{i:0;i:7743;}i:7744;a:1:{i:0;i:7745;}i:7746;a:1:{i:0;i:7747;}i:7748;a:1:{i:0;i:7749;}i:7750;a:1:{i:0;i:7751;}i:7752;a:1:{i:0;i:7753;}i:7754;a:1:{i:0;i:7755;}i:7756;a:1:{i:0;i:7757;}i:7758;a:1:{i:0;i:7759;}i:7760;a:1:{i:0;i:7761;}i:7762;a:1:{i:0;i:7763;}i:7764;a:1:{i:0;i:7765;}i:7766;a:1:{i:0;i:7767;}i:7768;a:1:{i:0;i:7769;}i:7770;a:1:{i:0;i:7771;}i:7772;a:1:{i:0;i:7773;}i:7774;a:1:{i:0;i:7775;}i:7776;a:1:{i:0;i:7777;}i:7778;a:1:{i:0;i:7779;}i:7780;a:1:{i:0;i:7781;}i:7782;a:1:{i:0;i:7783;}i:7784;a:1:{i:0;i:7785;}i:7786;a:1:{i:0;i:7787;}i:7788;a:1:{i:0;i:7789;}i:7790;a:1:{i:0;i:7791;}i:7792;a:1:{i:0;i:7793;}i:7794;a:1:{i:0;i:7795;}i:7796;a:1:{i:0;i:7797;}i:7798;a:1:{i:0;i:7799;}i:7800;a:1:{i:0;i:7801;}i:7802;a:1:{i:0;i:7803;}i:7804;a:1:{i:0;i:7805;}i:7806;a:1:{i:0;i:7807;}i:7808;a:1:{i:0;i:7809;}i:7810;a:1:{i:0;i:7811;}i:7812;a:1:{i:0;i:7813;}i:7814;a:1:{i:0;i:7815;}i:7816;a:1:{i:0;i:7817;}i:7818;a:1:{i:0;i:7819;}i:7820;a:1:{i:0;i:7821;}i:7822;a:1:{i:0;i:7823;}i:7824;a:1:{i:0;i:7825;}i:7826;a:1:{i:0;i:7827;}i:7828;a:1:{i:0;i:7829;}i:7830;a:2:{i:0;i:104;i:1;i:817;}i:7831;a:2:{i:0;i:116;i:1;i:776;}i:7832;a:2:{i:0;i:119;i:1;i:778;}i:7833;a:2:{i:0;i:121;i:1;i:778;}i:7834;a:2:{i:0;i:97;i:1;i:702;}i:7835;a:1:{i:0;i:7777;}i:7840;a:1:{i:0;i:7841;}i:7842;a:1:{i:0;i:7843;}i:7844;a:1:{i:0;i:7845;}i:7846;a:1:{i:0;i:7847;}i:7848;a:1:{i:0;i:7849;}i:7850;a:1:{i:0;i:7851;}i:7852;a:1:{i:0;i:7853;}i:7854;a:1:{i:0;i:7855;}i:7856;a:1:{i:0;i:7857;}i:7858;a:1:{i:0;i:7859;}i:7860;a:1:{i:0;i:7861;}i:7862;a:1:{i:0;i:7863;}i:7864;a:1:{i:0;i:7865;}i:7866;a:1:{i:0;i:7867;}i:7868;a:1:{i:0;i:7869;}i:7870;a:1:{i:0;i:7871;}i:7872;a:1:{i:0;i:7873;}i:7874;a:1:{i:0;i:7875;}i:7876;a:1:{i:0;i:7877;}i:7878;a:1:{i:0;i:7879;}i:7880;a:1:{i:0;i:7881;}i:7882;a:1:{i:0;i:7883;}i:7884;a:1:{i:0;i:7885;}i:7886;a:1:{i:0;i:7887;}i:7888;a:1:{i:0;i:7889;}i:7890;a:1:{i:0;i:7891;}i:7892;a:1:{i:0;i:7893;}i:7894;a:1:{i:0;i:7895;}i:7896;a:1:{i:0;i:7897;}i:7898;a:1:{i:0;i:7899;}i:7900;a:1:{i:0;i:7901;}i:7902;a:1:{i:0;i:7903;}i:7904;a:1:{i:0;i:7905;}i:7906;a:1:{i:0;i:7907;}i:7908;a:1:{i:0;i:7909;}i:7910;a:1:{i:0;i:7911;}i:7912;a:1:{i:0;i:7913;}i:7914;a:1:{i:0;i:7915;}i:7916;a:1:{i:0;i:7917;}i:7918;a:1:{i:0;i:7919;}i:7920;a:1:{i:0;i:7921;}i:7922;a:1:{i:0;i:7923;}i:7924;a:1:{i:0;i:7925;}i:7926;a:1:{i:0;i:7927;}i:7928;a:1:{i:0;i:7929;}i:7944;a:1:{i:0;i:7936;}i:7945;a:1:{i:0;i:7937;}i:7946;a:1:{i:0;i:7938;}i:7947;a:1:{i:0;i:7939;}i:7948;a:1:{i:0;i:7940;}i:7949;a:1:{i:0;i:7941;}i:7950;a:1:{i:0;i:7942;}i:7951;a:1:{i:0;i:7943;}i:7960;a:1:{i:0;i:7952;}i:7961;a:1:{i:0;i:7953;}i:7962;a:1:{i:0;i:7954;}i:7963;a:1:{i:0;i:7955;}i:7964;a:1:{i:0;i:7956;}i:7965;a:1:{i:0;i:7957;}i:7976;a:1:{i:0;i:7968;}i:7977;a:1:{i:0;i:7969;}i:7978;a:1:{i:0;i:7970;}i:7979;a:1:{i:0;i:7971;}i:7980;a:1:{i:0;i:7972;}i:7981;a:1:{i:0;i:7973;}i:7982;a:1:{i:0;i:7974;}i:7983;a:1:{i:0;i:7975;}i:7992;a:1:{i:0;i:7984;}i:7993;a:1:{i:0;i:7985;}i:7994;a:1:{i:0;i:7986;}i:7995;a:1:{i:0;i:7987;}i:7996;a:1:{i:0;i:7988;}i:7997;a:1:{i:0;i:7989;}i:7998;a:1:{i:0;i:7990;}i:7999;a:1:{i:0;i:7991;}i:8008;a:1:{i:0;i:8000;}i:8009;a:1:{i:0;i:8001;}i:8010;a:1:{i:0;i:8002;}i:8011;a:1:{i:0;i:8003;}i:8012;a:1:{i:0;i:8004;}i:8013;a:1:{i:0;i:8005;}i:8016;a:2:{i:0;i:965;i:1;i:787;}i:8018;a:3:{i:0;i:965;i:1;i:787;i:2;i:768;}i:8020;a:3:{i:0;i:965;i:1;i:787;i:2;i:769;}i:8022;a:3:{i:0;i:965;i:1;i:787;i:2;i:834;}i:8025;a:1:{i:0;i:8017;}i:8027;a:1:{i:0;i:8019;}i:8029;a:1:{i:0;i:8021;}i:8031;a:1:{i:0;i:8023;}i:8040;a:1:{i:0;i:8032;}i:8041;a:1:{i:0;i:8033;}i:8042;a:1:{i:0;i:8034;}i:8043;a:1:{i:0;i:8035;}i:8044;a:1:{i:0;i:8036;}i:8045;a:1:{i:0;i:8037;}i:8046;a:1:{i:0;i:8038;}i:8047;a:1:{i:0;i:8039;}i:8064;a:2:{i:0;i:7936;i:1;i:953;}i:8065;a:2:{i:0;i:7937;i:1;i:953;}i:8066;a:2:{i:0;i:7938;i:1;i:953;}i:8067;a:2:{i:0;i:7939;i:1;i:953;}i:8068;a:2:{i:0;i:7940;i:1;i:953;}i:8069;a:2:{i:0;i:7941;i:1;i:953;}i:8070;a:2:{i:0;i:7942;i:1;i:953;}i:8071;a:2:{i:0;i:7943;i:1;i:953;}i:8072;a:2:{i:0;i:7936;i:1;i:953;}i:8073;a:2:{i:0;i:7937;i:1;i:953;}i:8074;a:2:{i:0;i:7938;i:1;i:953;}i:8075;a:2:{i:0;i:7939;i:1;i:953;}i:8076;a:2:{i:0;i:7940;i:1;i:953;}i:8077;a:2:{i:0;i:7941;i:1;i:953;}i:8078;a:2:{i:0;i:7942;i:1;i:953;}i:8079;a:2:{i:0;i:7943;i:1;i:953;}i:8080;a:2:{i:0;i:7968;i:1;i:953;}i:8081;a:2:{i:0;i:7969;i:1;i:953;}i:8082;a:2:{i:0;i:7970;i:1;i:953;}i:8083;a:2:{i:0;i:7971;i:1;i:953;}i:8084;a:2:{i:0;i:7972;i:1;i:953;}i:8085;a:2:{i:0;i:7973;i:1;i:953;}i:8086;a:2:{i:0;i:7974;i:1;i:953;}i:8087;a:2:{i:0;i:7975;i:1;i:953;}i:8088;a:2:{i:0;i:7968;i:1;i:953;}i:8089;a:2:{i:0;i:7969;i:1;i:953;}i:8090;a:2:{i:0;i:7970;i:1;i:953;}i:8091;a:2:{i:0;i:7971;i:1;i:953;}i:8092;a:2:{i:0;i:7972;i:1;i:953;}i:8093;a:2:{i:0;i:7973;i:1;i:953;}i:8094;a:2:{i:0;i:7974;i:1;i:953;}i:8095;a:2:{i:0;i:7975;i:1;i:953;}i:8096;a:2:{i:0;i:8032;i:1;i:953;}i:8097;a:2:{i:0;i:8033;i:1;i:953;}i:8098;a:2:{i:0;i:8034;i:1;i:953;}i:8099;a:2:{i:0;i:8035;i:1;i:953;}i:8100;a:2:{i:0;i:8036;i:1;i:953;}i:8101;a:2:{i:0;i:8037;i:1;i:953;}i:8102;a:2:{i:0;i:8038;i:1;i:953;}i:8103;a:2:{i:0;i:8039;i:1;i:953;}i:8104;a:2:{i:0;i:8032;i:1;i:953;}i:8105;a:2:{i:0;i:8033;i:1;i:953;}i:8106;a:2:{i:0;i:8034;i:1;i:953;}i:8107;a:2:{i:0;i:8035;i:1;i:953;}i:8108;a:2:{i:0;i:8036;i:1;i:953;}i:8109;a:2:{i:0;i:8037;i:1;i:953;}i:8110;a:2:{i:0;i:8038;i:1;i:953;}i:8111;a:2:{i:0;i:8039;i:1;i:953;}i:8114;a:2:{i:0;i:8048;i:1;i:953;}i:8115;a:2:{i:0;i:945;i:1;i:953;}i:8116;a:2:{i:0;i:940;i:1;i:953;}i:8118;a:2:{i:0;i:945;i:1;i:834;}i:8119;a:3:{i:0;i:945;i:1;i:834;i:2;i:953;}i:8120;a:1:{i:0;i:8112;}i:8121;a:1:{i:0;i:8113;}i:8122;a:1:{i:0;i:8048;}i:8123;a:1:{i:0;i:8049;}i:8124;a:2:{i:0;i:945;i:1;i:953;}i:8126;a:1:{i:0;i:953;}i:8130;a:2:{i:0;i:8052;i:1;i:953;}i:8131;a:2:{i:0;i:951;i:1;i:953;}i:8132;a:2:{i:0;i:942;i:1;i:953;}i:8134;a:2:{i:0;i:951;i:1;i:834;}i:8135;a:3:{i:0;i:951;i:1;i:834;i:2;i:953;}i:8136;a:1:{i:0;i:8050;}i:8137;a:1:{i:0;i:8051;}i:8138;a:1:{i:0;i:8052;}i:8139;a:1:{i:0;i:8053;}i:8140;a:2:{i:0;i:951;i:1;i:953;}i:8146;a:3:{i:0;i:953;i:1;i:776;i:2;i:768;}i:8147;a:3:{i:0;i:953;i:1;i:776;i:2;i:769;}i:8150;a:2:{i:0;i:953;i:1;i:834;}i:8151;a:3:{i:0;i:953;i:1;i:776;i:2;i:834;}i:8152;a:1:{i:0;i:8144;}i:8153;a:1:{i:0;i:8145;}i:8154;a:1:{i:0;i:8054;}i:8155;a:1:{i:0;i:8055;}i:8162;a:3:{i:0;i:965;i:1;i:776;i:2;i:768;}i:8163;a:3:{i:0;i:965;i:1;i:776;i:2;i:769;}i:8164;a:2:{i:0;i:961;i:1;i:787;}i:8166;a:2:{i:0;i:965;i:1;i:834;}i:8167;a:3:{i:0;i:965;i:1;i:776;i:2;i:834;}i:8168;a:1:{i:0;i:8160;}i:8169;a:1:{i:0;i:8161;}i:8170;a:1:{i:0;i:8058;}i:8171;a:1:{i:0;i:8059;}i:8172;a:1:{i:0;i:8165;}i:8178;a:2:{i:0;i:8060;i:1;i:953;}i:8179;a:2:{i:0;i:969;i:1;i:953;}i:8180;a:2:{i:0;i:974;i:1;i:953;}i:8182;a:2:{i:0;i:969;i:1;i:834;}i:8183;a:3:{i:0;i:969;i:1;i:834;i:2;i:953;}i:8184;a:1:{i:0;i:8056;}i:8185;a:1:{i:0;i:8057;}i:8186;a:1:{i:0;i:8060;}i:8187;a:1:{i:0;i:8061;}i:8188;a:2:{i:0;i:969;i:1;i:953;}i:8360;a:2:{i:0;i:114;i:1;i:115;}i:8450;a:1:{i:0;i:99;}i:8451;a:2:{i:0;i:176;i:1;i:99;}i:8455;a:1:{i:0;i:603;}i:8457;a:2:{i:0;i:176;i:1;i:102;}i:8459;a:1:{i:0;i:104;}i:8460;a:1:{i:0;i:104;}i:8461;a:1:{i:0;i:104;}i:8464;a:1:{i:0;i:105;}i:8465;a:1:{i:0;i:105;}i:8466;a:1:{i:0;i:108;}i:8469;a:1:{i:0;i:110;}i:8470;a:2:{i:0;i:110;i:1;i:111;}i:8473;a:1:{i:0;i:112;}i:8474;a:1:{i:0;i:113;}i:8475;a:1:{i:0;i:114;}i:8476;a:1:{i:0;i:114;}i:8477;a:1:{i:0;i:114;}i:8480;a:2:{i:0;i:115;i:1;i:109;}i:8481;a:3:{i:0;i:116;i:1;i:101;i:2;i:108;}i:8482;a:2:{i:0;i:116;i:1;i:109;}i:8484;a:1:{i:0;i:122;}i:8486;a:1:{i:0;i:969;}i:8488;a:1:{i:0;i:122;}i:8490;a:1:{i:0;i:107;}i:8491;a:1:{i:0;i:229;}i:8492;a:1:{i:0;i:98;}i:8493;a:1:{i:0;i:99;}i:8496;a:1:{i:0;i:101;}i:8497;a:1:{i:0;i:102;}i:8499;a:1:{i:0;i:109;}i:8510;a:1:{i:0;i:947;}i:8511;a:1:{i:0;i:960;}i:8517;a:1:{i:0;i:100;}i:8544;a:1:{i:0;i:8560;}i:8545;a:1:{i:0;i:8561;}i:8546;a:1:{i:0;i:8562;}i:8547;a:1:{i:0;i:8563;}i:8548;a:1:{i:0;i:8564;}i:8549;a:1:{i:0;i:8565;}i:8550;a:1:{i:0;i:8566;}i:8551;a:1:{i:0;i:8567;}i:8552;a:1:{i:0;i:8568;}i:8553;a:1:{i:0;i:8569;}i:8554;a:1:{i:0;i:8570;}i:8555;a:1:{i:0;i:8571;}i:8556;a:1:{i:0;i:8572;}i:8557;a:1:{i:0;i:8573;}i:8558;a:1:{i:0;i:8574;}i:8559;a:1:{i:0;i:8575;}i:9398;a:1:{i:0;i:9424;}i:9399;a:1:{i:0;i:9425;}i:9400;a:1:{i:0;i:9426;}i:9401;a:1:{i:0;i:9427;}i:9402;a:1:{i:0;i:9428;}i:9403;a:1:{i:0;i:9429;}i:9404;a:1:{i:0;i:9430;}i:9405;a:1:{i:0;i:9431;}i:9406;a:1:{i:0;i:9432;}i:9407;a:1:{i:0;i:9433;}i:9408;a:1:{i:0;i:9434;}i:9409;a:1:{i:0;i:9435;}i:9410;a:1:{i:0;i:9436;}i:9411;a:1:{i:0;i:9437;}i:9412;a:1:{i:0;i:9438;}i:9413;a:1:{i:0;i:9439;}i:9414;a:1:{i:0;i:9440;}i:9415;a:1:{i:0;i:9441;}i:9416;a:1:{i:0;i:9442;}i:9417;a:1:{i:0;i:9443;}i:9418;a:1:{i:0;i:9444;}i:9419;a:1:{i:0;i:9445;}i:9420;a:1:{i:0;i:9446;}i:9421;a:1:{i:0;i:9447;}i:9422;a:1:{i:0;i:9448;}i:9423;a:1:{i:0;i:9449;}i:13169;a:3:{i:0;i:104;i:1;i:112;i:2;i:97;}i:13171;a:2:{i:0;i:97;i:1;i:117;}i:13173;a:2:{i:0;i:111;i:1;i:118;}i:13184;a:2:{i:0;i:112;i:1;i:97;}i:13185;a:2:{i:0;i:110;i:1;i:97;}i:13186;a:2:{i:0;i:956;i:1;i:97;}i:13187;a:2:{i:0;i:109;i:1;i:97;}i:13188;a:2:{i:0;i:107;i:1;i:97;}i:13189;a:2:{i:0;i:107;i:1;i:98;}i:13190;a:2:{i:0;i:109;i:1;i:98;}i:13191;a:2:{i:0;i:103;i:1;i:98;}i:13194;a:2:{i:0;i:112;i:1;i:102;}i:13195;a:2:{i:0;i:110;i:1;i:102;}i:13196;a:2:{i:0;i:956;i:1;i:102;}i:13200;a:2:{i:0;i:104;i:1;i:122;}i:13201;a:3:{i:0;i:107;i:1;i:104;i:2;i:122;}i:13202;a:3:{i:0;i:109;i:1;i:104;i:2;i:122;}i:13203;a:3:{i:0;i:103;i:1;i:104;i:2;i:122;}i:13204;a:3:{i:0;i:116;i:1;i:104;i:2;i:122;}i:13225;a:2:{i:0;i:112;i:1;i:97;}i:13226;a:3:{i:0;i:107;i:1;i:112;i:2;i:97;}i:13227;a:3:{i:0;i:109;i:1;i:112;i:2;i:97;}i:13228;a:3:{i:0;i:103;i:1;i:112;i:2;i:97;}i:13236;a:2:{i:0;i:112;i:1;i:118;}i:13237;a:2:{i:0;i:110;i:1;i:118;}i:13238;a:2:{i:0;i:956;i:1;i:118;}i:13239;a:2:{i:0;i:109;i:1;i:118;}i:13240;a:2:{i:0;i:107;i:1;i:118;}i:13241;a:2:{i:0;i:109;i:1;i:118;}i:13242;a:2:{i:0;i:112;i:1;i:119;}i:13243;a:2:{i:0;i:110;i:1;i:119;}i:13244;a:2:{i:0;i:956;i:1;i:119;}i:13245;a:2:{i:0;i:109;i:1;i:119;}i:13246;a:2:{i:0;i:107;i:1;i:119;}i:13247;a:2:{i:0;i:109;i:1;i:119;}i:13248;a:2:{i:0;i:107;i:1;i:969;}i:13249;a:2:{i:0;i:109;i:1;i:969;}i:13251;a:2:{i:0;i:98;i:1;i:113;}i:13254;a:4:{i:0;i:99;i:1;i:8725;i:2;i:107;i:3;i:103;}i:13255;a:3:{i:0;i:99;i:1;i:111;i:2;i:46;}i:13256;a:2:{i:0;i:100;i:1;i:98;}i:13257;a:2:{i:0;i:103;i:1;i:121;}i:13259;a:2:{i:0;i:104;i:1;i:112;}i:13261;a:2:{i:0;i:107;i:1;i:107;}i:13262;a:2:{i:0;i:107;i:1;i:109;}i:13271;a:2:{i:0;i:112;i:1;i:104;}i:13273;a:3:{i:0;i:112;i:1;i:112;i:2;i:109;}i:13274;a:2:{i:0;i:112;i:1;i:114;}i:13276;a:2:{i:0;i:115;i:1;i:118;}i:13277;a:2:{i:0;i:119;i:1;i:98;}i:64256;a:2:{i:0;i:102;i:1;i:102;}i:64257;a:2:{i:0;i:102;i:1;i:105;}i:64258;a:2:{i:0;i:102;i:1;i:108;}i:64259;a:3:{i:0;i:102;i:1;i:102;i:2;i:105;}i:64260;a:3:{i:0;i:102;i:1;i:102;i:2;i:108;}i:64261;a:2:{i:0;i:115;i:1;i:116;}i:64262;a:2:{i:0;i:115;i:1;i:116;}i:64275;a:2:{i:0;i:1396;i:1;i:1398;}i:64276;a:2:{i:0;i:1396;i:1;i:1381;}i:64277;a:2:{i:0;i:1396;i:1;i:1387;}i:64278;a:2:{i:0;i:1406;i:1;i:1398;}i:64279;a:2:{i:0;i:1396;i:1;i:1389;}i:65313;a:1:{i:0;i:65345;}i:65314;a:1:{i:0;i:65346;}i:65315;a:1:{i:0;i:65347;}i:65316;a:1:{i:0;i:65348;}i:65317;a:1:{i:0;i:65349;}i:65318;a:1:{i:0;i:65350;}i:65319;a:1:{i:0;i:65351;}i:65320;a:1:{i:0;i:65352;}i:65321;a:1:{i:0;i:65353;}i:65322;a:1:{i:0;i:65354;}i:65323;a:1:{i:0;i:65355;}i:65324;a:1:{i:0;i:65356;}i:65325;a:1:{i:0;i:65357;}i:65326;a:1:{i:0;i:65358;}i:65327;a:1:{i:0;i:65359;}i:65328;a:1:{i:0;i:65360;}i:65329;a:1:{i:0;i:65361;}i:65330;a:1:{i:0;i:65362;}i:65331;a:1:{i:0;i:65363;}i:65332;a:1:{i:0;i:65364;}i:65333;a:1:{i:0;i:65365;}i:65334;a:1:{i:0;i:65366;}i:65335;a:1:{i:0;i:65367;}i:65336;a:1:{i:0;i:65368;}i:65337;a:1:{i:0;i:65369;}i:65338;a:1:{i:0;i:65370;}i:66560;a:1:{i:0;i:66600;}i:66561;a:1:{i:0;i:66601;}i:66562;a:1:{i:0;i:66602;}i:66563;a:1:{i:0;i:66603;}i:66564;a:1:{i:0;i:66604;}i:66565;a:1:{i:0;i:66605;}i:66566;a:1:{i:0;i:66606;}i:66567;a:1:{i:0;i:66607;}i:66568;a:1:{i:0;i:66608;}i:66569;a:1:{i:0;i:66609;}i:66570;a:1:{i:0;i:66610;}i:66571;a:1:{i:0;i:66611;}i:66572;a:1:{i:0;i:66612;}i:66573;a:1:{i:0;i:66613;}i:66574;a:1:{i:0;i:66614;}i:66575;a:1:{i:0;i:66615;}i:66576;a:1:{i:0;i:66616;}i:66577;a:1:{i:0;i:66617;}i:66578;a:1:{i:0;i:66618;}i:66579;a:1:{i:0;i:66619;}i:66580;a:1:{i:0;i:66620;}i:66581;a:1:{i:0;i:66621;}i:66582;a:1:{i:0;i:66622;}i:66583;a:1:{i:0;i:66623;}i:66584;a:1:{i:0;i:66624;}i:66585;a:1:{i:0;i:66625;}i:66586;a:1:{i:0;i:66626;}i:66587;a:1:{i:0;i:66627;}i:66588;a:1:{i:0;i:66628;}i:66589;a:1:{i:0;i:66629;}i:66590;a:1:{i:0;i:66630;}i:66591;a:1:{i:0;i:66631;}i:66592;a:1:{i:0;i:66632;}i:66593;a:1:{i:0;i:66633;}i:66594;a:1:{i:0;i:66634;}i:66595;a:1:{i:0;i:66635;}i:66596;a:1:{i:0;i:66636;}i:66597;a:1:{i:0;i:66637;}i:119808;a:1:{i:0;i:97;}i:119809;a:1:{i:0;i:98;}i:119810;a:1:{i:0;i:99;}i:119811;a:1:{i:0;i:100;}i:119812;a:1:{i:0;i:101;}i:119813;a:1:{i:0;i:102;}i:119814;a:1:{i:0;i:103;}i:119815;a:1:{i:0;i:104;}i:119816;a:1:{i:0;i:105;}i:119817;a:1:{i:0;i:106;}i:119818;a:1:{i:0;i:107;}i:119819;a:1:{i:0;i:108;}i:119820;a:1:{i:0;i:109;}i:119821;a:1:{i:0;i:110;}i:119822;a:1:{i:0;i:111;}i:119823;a:1:{i:0;i:112;}i:119824;a:1:{i:0;i:113;}i:119825;a:1:{i:0;i:114;}i:119826;a:1:{i:0;i:115;}i:119827;a:1:{i:0;i:116;}i:119828;a:1:{i:0;i:117;}i:119829;a:1:{i:0;i:118;}i:119830;a:1:{i:0;i:119;}i:119831;a:1:{i:0;i:120;}i:119832;a:1:{i:0;i:121;}i:119833;a:1:{i:0;i:122;}i:119860;a:1:{i:0;i:97;}i:119861;a:1:{i:0;i:98;}i:119862;a:1:{i:0;i:99;}i:119863;a:1:{i:0;i:100;}i:119864;a:1:{i:0;i:101;}i:119865;a:1:{i:0;i:102;}i:119866;a:1:{i:0;i:103;}i:119867;a:1:{i:0;i:104;}i:119868;a:1:{i:0;i:105;}i:119869;a:1:{i:0;i:106;}i:119870;a:1:{i:0;i:107;}i:119871;a:1:{i:0;i:108;}i:119872;a:1:{i:0;i:109;}i:119873;a:1:{i:0;i:110;}i:119874;a:1:{i:0;i:111;}i:119875;a:1:{i:0;i:112;}i:119876;a:1:{i:0;i:113;}i:119877;a:1:{i:0;i:114;}i:119878;a:1:{i:0;i:115;}i:119879;a:1:{i:0;i:116;}i:119880;a:1:{i:0;i:117;}i:119881;a:1:{i:0;i:118;}i:119882;a:1:{i:0;i:119;}i:119883;a:1:{i:0;i:120;}i:119884;a:1:{i:0;i:121;}i:119885;a:1:{i:0;i:122;}i:119912;a:1:{i:0;i:97;}i:119913;a:1:{i:0;i:98;}i:119914;a:1:{i:0;i:99;}i:119915;a:1:{i:0;i:100;}i:119916;a:1:{i:0;i:101;}i:119917;a:1:{i:0;i:102;}i:119918;a:1:{i:0;i:103;}i:119919;a:1:{i:0;i:104;}i:119920;a:1:{i:0;i:105;}i:119921;a:1:{i:0;i:106;}i:119922;a:1:{i:0;i:107;}i:119923;a:1:{i:0;i:108;}i:119924;a:1:{i:0;i:109;}i:119925;a:1:{i:0;i:110;}i:119926;a:1:{i:0;i:111;}i:119927;a:1:{i:0;i:112;}i:119928;a:1:{i:0;i:113;}i:119929;a:1:{i:0;i:114;}i:119930;a:1:{i:0;i:115;}i:119931;a:1:{i:0;i:116;}i:119932;a:1:{i:0;i:117;}i:119933;a:1:{i:0;i:118;}i:119934;a:1:{i:0;i:119;}i:119935;a:1:{i:0;i:120;}i:119936;a:1:{i:0;i:121;}i:119937;a:1:{i:0;i:122;}i:119964;a:1:{i:0;i:97;}i:119966;a:1:{i:0;i:99;}i:119967;a:1:{i:0;i:100;}i:119970;a:1:{i:0;i:103;}i:119973;a:1:{i:0;i:106;}i:119974;a:1:{i:0;i:107;}i:119977;a:1:{i:0;i:110;}i:119978;a:1:{i:0;i:111;}i:119979;a:1:{i:0;i:112;}i:119980;a:1:{i:0;i:113;}i:119982;a:1:{i:0;i:115;}i:119983;a:1:{i:0;i:116;}i:119984;a:1:{i:0;i:117;}i:119985;a:1:{i:0;i:118;}i:119986;a:1:{i:0;i:119;}i:119987;a:1:{i:0;i:120;}i:119988;a:1:{i:0;i:121;}i:119989;a:1:{i:0;i:122;}i:120016;a:1:{i:0;i:97;}i:120017;a:1:{i:0;i:98;}i:120018;a:1:{i:0;i:99;}i:120019;a:1:{i:0;i:100;}i:120020;a:1:{i:0;i:101;}i:120021;a:1:{i:0;i:102;}i:120022;a:1:{i:0;i:103;}i:120023;a:1:{i:0;i:104;}i:120024;a:1:{i:0;i:105;}i:120025;a:1:{i:0;i:106;}i:120026;a:1:{i:0;i:107;}i:120027;a:1:{i:0;i:108;}i:120028;a:1:{i:0;i:109;}i:120029;a:1:{i:0;i:110;}i:120030;a:1:{i:0;i:111;}i:120031;a:1:{i:0;i:112;}i:120032;a:1:{i:0;i:113;}i:120033;a:1:{i:0;i:114;}i:120034;a:1:{i:0;i:115;}i:120035;a:1:{i:0;i:116;}i:120036;a:1:{i:0;i:117;}i:120037;a:1:{i:0;i:118;}i:120038;a:1:{i:0;i:119;}i:120039;a:1:{i:0;i:120;}i:120040;a:1:{i:0;i:121;}i:120041;a:1:{i:0;i:122;}i:120068;a:1:{i:0;i:97;}i:120069;a:1:{i:0;i:98;}i:120071;a:1:{i:0;i:100;}i:120072;a:1:{i:0;i:101;}i:120073;a:1:{i:0;i:102;}i:120074;a:1:{i:0;i:103;}i:120077;a:1:{i:0;i:106;}i:120078;a:1:{i:0;i:107;}i:120079;a:1:{i:0;i:108;}i:120080;a:1:{i:0;i:109;}i:120081;a:1:{i:0;i:110;}i:120082;a:1:{i:0;i:111;}i:120083;a:1:{i:0;i:112;}i:120084;a:1:{i:0;i:113;}i:120086;a:1:{i:0;i:115;}i:120087;a:1:{i:0;i:116;}i:120088;a:1:{i:0;i:117;}i:120089;a:1:{i:0;i:118;}i:120090;a:1:{i:0;i:119;}i:120091;a:1:{i:0;i:120;}i:120092;a:1:{i:0;i:121;}i:120120;a:1:{i:0;i:97;}i:120121;a:1:{i:0;i:98;}i:120123;a:1:{i:0;i:100;}i:120124;a:1:{i:0;i:101;}i:120125;a:1:{i:0;i:102;}i:120126;a:1:{i:0;i:103;}i:120128;a:1:{i:0;i:105;}i:120129;a:1:{i:0;i:106;}i:120130;a:1:{i:0;i:107;}i:120131;a:1:{i:0;i:108;}i:120132;a:1:{i:0;i:109;}i:120134;a:1:{i:0;i:111;}i:120138;a:1:{i:0;i:115;}i:120139;a:1:{i:0;i:116;}i:120140;a:1:{i:0;i:117;}i:120141;a:1:{i:0;i:118;}i:120142;a:1:{i:0;i:119;}i:120143;a:1:{i:0;i:120;}i:120144;a:1:{i:0;i:121;}i:120172;a:1:{i:0;i:97;}i:120173;a:1:{i:0;i:98;}i:120174;a:1:{i:0;i:99;}i:120175;a:1:{i:0;i:100;}i:120176;a:1:{i:0;i:101;}i:120177;a:1:{i:0;i:102;}i:120178;a:1:{i:0;i:103;}i:120179;a:1:{i:0;i:104;}i:120180;a:1:{i:0;i:105;}i:120181;a:1:{i:0;i:106;}i:120182;a:1:{i:0;i:107;}i:120183;a:1:{i:0;i:108;}i:120184;a:1:{i:0;i:109;}i:120185;a:1:{i:0;i:110;}i:120186;a:1:{i:0;i:111;}i:120187;a:1:{i:0;i:112;}i:120188;a:1:{i:0;i:113;}i:120189;a:1:{i:0;i:114;}i:120190;a:1:{i:0;i:115;}i:120191;a:1:{i:0;i:116;}i:120192;a:1:{i:0;i:117;}i:120193;a:1:{i:0;i:118;}i:120194;a:1:{i:0;i:119;}i:120195;a:1:{i:0;i:120;}i:120196;a:1:{i:0;i:121;}i:120197;a:1:{i:0;i:122;}i:120224;a:1:{i:0;i:97;}i:120225;a:1:{i:0;i:98;}i:120226;a:1:{i:0;i:99;}i:120227;a:1:{i:0;i:100;}i:120228;a:1:{i:0;i:101;}i:120229;a:1:{i:0;i:102;}i:120230;a:1:{i:0;i:103;}i:120231;a:1:{i:0;i:104;}i:120232;a:1:{i:0;i:105;}i:120233;a:1:{i:0;i:106;}i:120234;a:1:{i:0;i:107;}i:120235;a:1:{i:0;i:108;}i:120236;a:1:{i:0;i:109;}i:120237;a:1:{i:0;i:110;}i:120238;a:1:{i:0;i:111;}i:120239;a:1:{i:0;i:112;}i:120240;a:1:{i:0;i:113;}i:120241;a:1:{i:0;i:114;}i:120242;a:1:{i:0;i:115;}i:120243;a:1:{i:0;i:116;}i:120244;a:1:{i:0;i:117;}i:120245;a:1:{i:0;i:118;}i:120246;a:1:{i:0;i:119;}i:120247;a:1:{i:0;i:120;}i:120248;a:1:{i:0;i:121;}i:120249;a:1:{i:0;i:122;}i:120276;a:1:{i:0;i:97;}i:120277;a:1:{i:0;i:98;}i:120278;a:1:{i:0;i:99;}i:120279;a:1:{i:0;i:100;}i:120280;a:1:{i:0;i:101;}i:120281;a:1:{i:0;i:102;}i:120282;a:1:{i:0;i:103;}i:120283;a:1:{i:0;i:104;}i:120284;a:1:{i:0;i:105;}i:120285;a:1:{i:0;i:106;}i:120286;a:1:{i:0;i:107;}i:120287;a:1:{i:0;i:108;}i:120288;a:1:{i:0;i:109;}i:120289;a:1:{i:0;i:110;}i:120290;a:1:{i:0;i:111;}i:120291;a:1:{i:0;i:112;}i:120292;a:1:{i:0;i:113;}i:120293;a:1:{i:0;i:114;}i:120294;a:1:{i:0;i:115;}i:120295;a:1:{i:0;i:116;}i:120296;a:1:{i:0;i:117;}i:120297;a:1:{i:0;i:118;}i:120298;a:1:{i:0;i:119;}i:120299;a:1:{i:0;i:120;}i:120300;a:1:{i:0;i:121;}i:120301;a:1:{i:0;i:122;}i:120328;a:1:{i:0;i:97;}i:120329;a:1:{i:0;i:98;}i:120330;a:1:{i:0;i:99;}i:120331;a:1:{i:0;i:100;}i:120332;a:1:{i:0;i:101;}i:120333;a:1:{i:0;i:102;}i:120334;a:1:{i:0;i:103;}i:120335;a:1:{i:0;i:104;}i:120336;a:1:{i:0;i:105;}i:120337;a:1:{i:0;i:106;}i:120338;a:1:{i:0;i:107;}i:120339;a:1:{i:0;i:108;}i:120340;a:1:{i:0;i:109;}i:120341;a:1:{i:0;i:110;}i:120342;a:1:{i:0;i:111;}i:120343;a:1:{i:0;i:112;}i:120344;a:1:{i:0;i:113;}i:120345;a:1:{i:0;i:114;}i:120346;a:1:{i:0;i:115;}i:120347;a:1:{i:0;i:116;}i:120348;a:1:{i:0;i:117;}i:120349;a:1:{i:0;i:118;}i:120350;a:1:{i:0;i:119;}i:120351;a:1:{i:0;i:120;}i:120352;a:1:{i:0;i:121;}i:120353;a:1:{i:0;i:122;}i:120380;a:1:{i:0;i:97;}i:120381;a:1:{i:0;i:98;}i:120382;a:1:{i:0;i:99;}i:120383;a:1:{i:0;i:100;}i:120384;a:1:{i:0;i:101;}i:120385;a:1:{i:0;i:102;}i:120386;a:1:{i:0;i:103;}i:120387;a:1:{i:0;i:104;}i:120388;a:1:{i:0;i:105;}i:120389;a:1:{i:0;i:106;}i:120390;a:1:{i:0;i:107;}i:120391;a:1:{i:0;i:108;}i:120392;a:1:{i:0;i:109;}i:120393;a:1:{i:0;i:110;}i:120394;a:1:{i:0;i:111;}i:120395;a:1:{i:0;i:112;}i:120396;a:1:{i:0;i:113;}i:120397;a:1:{i:0;i:114;}i:120398;a:1:{i:0;i:115;}i:120399;a:1:{i:0;i:116;}i:120400;a:1:{i:0;i:117;}i:120401;a:1:{i:0;i:118;}i:120402;a:1:{i:0;i:119;}i:120403;a:1:{i:0;i:120;}i:120404;a:1:{i:0;i:121;}i:120405;a:1:{i:0;i:122;}i:120432;a:1:{i:0;i:97;}i:120433;a:1:{i:0;i:98;}i:120434;a:1:{i:0;i:99;}i:120435;a:1:{i:0;i:100;}i:120436;a:1:{i:0;i:101;}i:120437;a:1:{i:0;i:102;}i:120438;a:1:{i:0;i:103;}i:120439;a:1:{i:0;i:104;}i:120440;a:1:{i:0;i:105;}i:120441;a:1:{i:0;i:106;}i:120442;a:1:{i:0;i:107;}i:120443;a:1:{i:0;i:108;}i:120444;a:1:{i:0;i:109;}i:120445;a:1:{i:0;i:110;}i:120446;a:1:{i:0;i:111;}i:120447;a:1:{i:0;i:112;}i:120448;a:1:{i:0;i:113;}i:120449;a:1:{i:0;i:114;}i:120450;a:1:{i:0;i:115;}i:120451;a:1:{i:0;i:116;}i:120452;a:1:{i:0;i:117;}i:120453;a:1:{i:0;i:118;}i:120454;a:1:{i:0;i:119;}i:120455;a:1:{i:0;i:120;}i:120456;a:1:{i:0;i:121;}i:120457;a:1:{i:0;i:122;}i:120488;a:1:{i:0;i:945;}i:120489;a:1:{i:0;i:946;}i:120490;a:1:{i:0;i:947;}i:120491;a:1:{i:0;i:948;}i:120492;a:1:{i:0;i:949;}i:120493;a:1:{i:0;i:950;}i:120494;a:1:{i:0;i:951;}i:120495;a:1:{i:0;i:952;}i:120496;a:1:{i:0;i:953;}i:120497;a:1:{i:0;i:954;}i:120498;a:1:{i:0;i:955;}i:120499;a:1:{i:0;i:956;}i:120500;a:1:{i:0;i:957;}i:120501;a:1:{i:0;i:958;}i:120502;a:1:{i:0;i:959;}i:120503;a:1:{i:0;i:960;}i:120504;a:1:{i:0;i:961;}i:120505;a:1:{i:0;i:952;}i:120506;a:1:{i:0;i:963;}i:120507;a:1:{i:0;i:964;}i:120508;a:1:{i:0;i:965;}i:120509;a:1:{i:0;i:966;}i:120510;a:1:{i:0;i:967;}i:120511;a:1:{i:0;i:968;}i:120512;a:1:{i:0;i:969;}i:120531;a:1:{i:0;i:963;}i:120546;a:1:{i:0;i:945;}i:120547;a:1:{i:0;i:946;}i:120548;a:1:{i:0;i:947;}i:120549;a:1:{i:0;i:948;}i:120550;a:1:{i:0;i:949;}i:120551;a:1:{i:0;i:950;}i:120552;a:1:{i:0;i:951;}i:120553;a:1:{i:0;i:952;}i:120554;a:1:{i:0;i:953;}i:120555;a:1:{i:0;i:954;}i:120556;a:1:{i:0;i:955;}i:120557;a:1:{i:0;i:956;}i:120558;a:1:{i:0;i:957;}i:120559;a:1:{i:0;i:958;}i:120560;a:1:{i:0;i:959;}i:120561;a:1:{i:0;i:960;}i:120562;a:1:{i:0;i:961;}i:120563;a:1:{i:0;i:952;}i:120564;a:1:{i:0;i:963;}i:120565;a:1:{i:0;i:964;}i:120566;a:1:{i:0;i:965;}i:120567;a:1:{i:0;i:966;}i:120568;a:1:{i:0;i:967;}i:120569;a:1:{i:0;i:968;}i:120570;a:1:{i:0;i:969;}i:120589;a:1:{i:0;i:963;}i:120604;a:1:{i:0;i:945;}i:120605;a:1:{i:0;i:946;}i:120606;a:1:{i:0;i:947;}i:120607;a:1:{i:0;i:948;}i:120608;a:1:{i:0;i:949;}i:120609;a:1:{i:0;i:950;}i:120610;a:1:{i:0;i:951;}i:120611;a:1:{i:0;i:952;}i:120612;a:1:{i:0;i:953;}i:120613;a:1:{i:0;i:954;}i:120614;a:1:{i:0;i:955;}i:120615;a:1:{i:0;i:956;}i:120616;a:1:{i:0;i:957;}i:120617;a:1:{i:0;i:958;}i:120618;a:1:{i:0;i:959;}i:120619;a:1:{i:0;i:960;}i:120620;a:1:{i:0;i:961;}i:120621;a:1:{i:0;i:952;}i:120622;a:1:{i:0;i:963;}i:120623;a:1:{i:0;i:964;}i:120624;a:1:{i:0;i:965;}i:120625;a:1:{i:0;i:966;}i:120626;a:1:{i:0;i:967;}i:120627;a:1:{i:0;i:968;}i:120628;a:1:{i:0;i:969;}i:120647;a:1:{i:0;i:963;}i:120662;a:1:{i:0;i:945;}i:120663;a:1:{i:0;i:946;}i:120664;a:1:{i:0;i:947;}i:120665;a:1:{i:0;i:948;}i:120666;a:1:{i:0;i:949;}i:120667;a:1:{i:0;i:950;}i:120668;a:1:{i:0;i:951;}i:120669;a:1:{i:0;i:952;}i:120670;a:1:{i:0;i:953;}i:120671;a:1:{i:0;i:954;}i:120672;a:1:{i:0;i:955;}i:120673;a:1:{i:0;i:956;}i:120674;a:1:{i:0;i:957;}i:120675;a:1:{i:0;i:958;}i:120676;a:1:{i:0;i:959;}i:120677;a:1:{i:0;i:960;}i:120678;a:1:{i:0;i:961;}i:120679;a:1:{i:0;i:952;}i:120680;a:1:{i:0;i:963;}i:120681;a:1:{i:0;i:964;}i:120682;a:1:{i:0;i:965;}i:120683;a:1:{i:0;i:966;}i:120684;a:1:{i:0;i:967;}i:120685;a:1:{i:0;i:968;}i:120686;a:1:{i:0;i:969;}i:120705;a:1:{i:0;i:963;}i:120720;a:1:{i:0;i:945;}i:120721;a:1:{i:0;i:946;}i:120722;a:1:{i:0;i:947;}i:120723;a:1:{i:0;i:948;}i:120724;a:1:{i:0;i:949;}i:120725;a:1:{i:0;i:950;}i:120726;a:1:{i:0;i:951;}i:120727;a:1:{i:0;i:952;}i:120728;a:1:{i:0;i:953;}i:120729;a:1:{i:0;i:954;}i:120730;a:1:{i:0;i:955;}i:120731;a:1:{i:0;i:956;}i:120732;a:1:{i:0;i:957;}i:120733;a:1:{i:0;i:958;}i:120734;a:1:{i:0;i:959;}i:120735;a:1:{i:0;i:960;}i:120736;a:1:{i:0;i:961;}i:120737;a:1:{i:0;i:952;}i:120738;a:1:{i:0;i:963;}i:120739;a:1:{i:0;i:964;}i:120740;a:1:{i:0;i:965;}i:120741;a:1:{i:0;i:966;}i:120742;a:1:{i:0;i:967;}i:120743;a:1:{i:0;i:968;}i:120744;a:1:{i:0;i:969;}i:120763;a:1:{i:0;i:963;}i:1017;a:1:{i:0;i:963;}i:7468;a:1:{i:0;i:97;}i:7469;a:1:{i:0;i:230;}i:7470;a:1:{i:0;i:98;}i:7472;a:1:{i:0;i:100;}i:7473;a:1:{i:0;i:101;}i:7474;a:1:{i:0;i:477;}i:7475;a:1:{i:0;i:103;}i:7476;a:1:{i:0;i:104;}i:7477;a:1:{i:0;i:105;}i:7478;a:1:{i:0;i:106;}i:7479;a:1:{i:0;i:107;}i:7480;a:1:{i:0;i:108;}i:7481;a:1:{i:0;i:109;}i:7482;a:1:{i:0;i:110;}i:7484;a:1:{i:0;i:111;}i:7485;a:1:{i:0;i:547;}i:7486;a:1:{i:0;i:112;}i:7487;a:1:{i:0;i:114;}i:7488;a:1:{i:0;i:116;}i:7489;a:1:{i:0;i:117;}i:7490;a:1:{i:0;i:119;}i:8507;a:3:{i:0;i:102;i:1;i:97;i:2;i:120;}i:12880;a:3:{i:0;i:112;i:1;i:116;i:2;i:101;}i:13004;a:2:{i:0;i:104;i:1;i:103;}i:13006;a:2:{i:0;i:101;i:1;i:118;}i:13007;a:3:{i:0;i:108;i:1;i:116;i:2;i:100;}i:13178;a:2:{i:0;i:105;i:1;i:117;}i:13278;a:3:{i:0;i:118;i:1;i:8725;i:2;i:109;}i:13279;a:3:{i:0;i:97;i:1;i:8725;i:2;i:109;}}s:12:"norm_combcls";a:341:{i:820;i:1;i:821;i:1;i:822;i:1;i:823;i:1;i:824;i:1;i:2364;i:7;i:2492;i:7;i:2620;i:7;i:2748;i:7;i:2876;i:7;i:3260;i:7;i:4151;i:7;i:12441;i:8;i:12442;i:8;i:2381;i:9;i:2509;i:9;i:2637;i:9;i:2765;i:9;i:2893;i:9;i:3021;i:9;i:3149;i:9;i:3277;i:9;i:3405;i:9;i:3530;i:9;i:3642;i:9;i:3972;i:9;i:4153;i:9;i:5908;i:9;i:5940;i:9;i:6098;i:9;i:1456;i:10;i:1457;i:11;i:1458;i:12;i:1459;i:13;i:1460;i:14;i:1461;i:15;i:1462;i:16;i:1463;i:17;i:1464;i:18;i:1465;i:19;i:1467;i:20;i:1468;i:21;i:1469;i:22;i:1471;i:23;i:1473;i:24;i:1474;i:25;i:64286;i:26;i:1611;i:27;i:1612;i:28;i:1613;i:29;i:1614;i:30;i:1615;i:31;i:1616;i:32;i:1617;i:33;i:1618;i:34;i:1648;i:35;i:1809;i:36;i:3157;i:84;i:3158;i:91;i:3640;i:103;i:3641;i:103;i:3656;i:107;i:3657;i:107;i:3658;i:107;i:3659;i:107;i:3768;i:118;i:3769;i:118;i:3784;i:122;i:3785;i:122;i:3786;i:122;i:3787;i:122;i:3953;i:129;i:3954;i:130;i:3962;i:130;i:3963;i:130;i:3964;i:130;i:3965;i:130;i:3968;i:130;i:3956;i:132;i:801;i:202;i:802;i:202;i:807;i:202;i:808;i:202;i:795;i:216;i:3897;i:216;i:119141;i:216;i:119142;i:216;i:119150;i:216;i:119151;i:216;i:119152;i:216;i:119153;i:216;i:119154;i:216;i:12330;i:218;i:790;i:220;i:791;i:220;i:792;i:220;i:793;i:220;i:796;i:220;i:797;i:220;i:798;i:220;i:799;i:220;i:800;i:220;i:803;i:220;i:804;i:220;i:805;i:220;i:806;i:220;i:809;i:220;i:810;i:220;i:811;i:220;i:812;i:220;i:813;i:220;i:814;i:220;i:815;i:220;i:816;i:220;i:817;i:220;i:818;i:220;i:819;i:220;i:825;i:220;i:826;i:220;i:827;i:220;i:828;i:220;i:839;i:220;i:840;i:220;i:841;i:220;i:845;i:220;i:846;i:220;i:851;i:220;i:852;i:220;i:853;i:220;i:854;i:220;i:1425;i:220;i:1430;i:220;i:1435;i:220;i:1443;i:220;i:1444;i:220;i:1445;i:220;i:1446;i:220;i:1447;i:220;i:1450;i:220;i:1621;i:220;i:1622;i:220;i:1763;i:220;i:1770;i:220;i:1773;i:220;i:1841;i:220;i:1844;i:220;i:1847;i:220;i:1848;i:220;i:1849;i:220;i:1851;i:220;i:1852;i:220;i:1854;i:220;i:1858;i:220;i:1860;i:220;i:1862;i:220;i:1864;i:220;i:2386;i:220;i:3864;i:220;i:3865;i:220;i:3893;i:220;i:3895;i:220;i:4038;i:220;i:6459;i:220;i:8424;i:220;i:119163;i:220;i:119164;i:220;i:119165;i:220;i:119166;i:220;i:119167;i:220;i:119168;i:220;i:119169;i:220;i:119170;i:220;i:119178;i:220;i:119179;i:220;i:1434;i:222;i:1453;i:222;i:6441;i:222;i:12333;i:222;i:12334;i:224;i:12335;i:224;i:119149;i:226;i:1454;i:228;i:6313;i:228;i:12331;i:228;i:768;i:230;i:769;i:230;i:770;i:230;i:771;i:230;i:772;i:230;i:773;i:230;i:774;i:230;i:775;i:230;i:776;i:230;i:777;i:230;i:778;i:230;i:779;i:230;i:780;i:230;i:781;i:230;i:782;i:230;i:783;i:230;i:784;i:230;i:785;i:230;i:786;i:230;i:787;i:230;i:788;i:230;i:829;i:230;i:830;i:230;i:831;i:230;i:832;i:230;i:833;i:230;i:834;i:230;i:835;i:230;i:836;i:230;i:838;i:230;i:842;i:230;i:843;i:230;i:844;i:230;i:848;i:230;i:849;i:230;i:850;i:230;i:855;i:230;i:867;i:230;i:868;i:230;i:869;i:230;i:870;i:230;i:871;i:230;i:872;i:230;i:873;i:230;i:874;i:230;i:875;i:230;i:876;i:230;i:877;i:230;i:878;i:230;i:879;i:230;i:1155;i:230;i:1156;i:230;i:1157;i:230;i:1158;i:230;i:1426;i:230;i:1427;i:230;i:1428;i:230;i:1429;i:230;i:1431;i:230;i:1432;i:230;i:1433;i:230;i:1436;i:230;i:1437;i:230;i:1438;i:230;i:1439;i:230;i:1440;i:230;i:1441;i:230;i:1448;i:230;i:1449;i:230;i:1451;i:230;i:1452;i:230;i:1455;i:230;i:1476;i:230;i:1552;i:230;i:1553;i:230;i:1554;i:230;i:1555;i:230;i:1556;i:230;i:1557;i:230;i:1619;i:230;i:1620;i:230;i:1623;i:230;i:1624;i:230;i:1750;i:230;i:1751;i:230;i:1752;i:230;i:1753;i:230;i:1754;i:230;i:1755;i:230;i:1756;i:230;i:1759;i:230;i:1760;i:230;i:1761;i:230;i:1762;i:230;i:1764;i:230;i:1767;i:230;i:1768;i:230;i:1771;i:230;i:1772;i:230;i:1840;i:230;i:1842;i:230;i:1843;i:230;i:1845;i:230;i:1846;i:230;i:1850;i:230;i:1853;i:230;i:1855;i:230;i:1856;i:230;i:1857;i:230;i:1859;i:230;i:1861;i:230;i:1863;i:230;i:1865;i:230;i:1866;i:230;i:2385;i:230;i:2387;i:230;i:2388;i:230;i:3970;i:230;i:3971;i:230;i:3974;i:230;i:3975;i:230;i:5901;i:230;i:6458;i:230;i:8400;i:230;i:8401;i:230;i:8404;i:230;i:8405;i:230;i:8406;i:230;i:8407;i:230;i:8411;i:230;i:8412;i:230;i:8417;i:230;i:8423;i:230;i:8425;i:230;i:65056;i:230;i:65057;i:230;i:65058;i:230;i:65059;i:230;i:119173;i:230;i:119174;i:230;i:119175;i:230;i:119177;i:230;i:119176;i:230;i:119210;i:230;i:119211;i:230;i:119212;i:230;i:119213;i:230;i:789;i:232;i:794;i:232;i:12332;i:232;i:863;i:233;i:866;i:233;i:861;i:234;i:862;i:234;i:864;i:234;i:865;i:234;i:837;i:240;}} \ No newline at end of file diff --git a/simplepie/simplepie.inc b/simplepie/simplepie.inc deleted file mode 100644 index ee0ba2c..0000000 --- a/simplepie/simplepie.inc +++ /dev/null @@ -1,13316 +0,0 @@ -' . SIMPLEPIE_NAME . ''); - -/** - * No Autodiscovery - * @see SimplePie::set_autodiscovery_level() - */ -define('SIMPLEPIE_LOCATOR_NONE', 0); - -/** - * Feed Link Element Autodiscovery - * @see SimplePie::set_autodiscovery_level() - */ -define('SIMPLEPIE_LOCATOR_AUTODISCOVERY', 1); - -/** - * Local Feed Extension Autodiscovery - * @see SimplePie::set_autodiscovery_level() - */ -define('SIMPLEPIE_LOCATOR_LOCAL_EXTENSION', 2); - -/** - * Local Feed Body Autodiscovery - * @see SimplePie::set_autodiscovery_level() - */ -define('SIMPLEPIE_LOCATOR_LOCAL_BODY', 4); - -/** - * Remote Feed Extension Autodiscovery - * @see SimplePie::set_autodiscovery_level() - */ -define('SIMPLEPIE_LOCATOR_REMOTE_EXTENSION', 8); - -/** - * Remote Feed Body Autodiscovery - * @see SimplePie::set_autodiscovery_level() - */ -define('SIMPLEPIE_LOCATOR_REMOTE_BODY', 16); - -/** - * All Feed Autodiscovery - * @see SimplePie::set_autodiscovery_level() - */ -define('SIMPLEPIE_LOCATOR_ALL', 31); - -/** - * No known feed type - */ -define('SIMPLEPIE_TYPE_NONE', 0); - -/** - * RSS 0.90 - */ -define('SIMPLEPIE_TYPE_RSS_090', 1); - -/** - * RSS 0.91 (Netscape) - */ -define('SIMPLEPIE_TYPE_RSS_091_NETSCAPE', 2); - -/** - * RSS 0.91 (Userland) - */ -define('SIMPLEPIE_TYPE_RSS_091_USERLAND', 4); - -/** - * RSS 0.91 (both Netscape and Userland) - */ -define('SIMPLEPIE_TYPE_RSS_091', 6); - -/** - * RSS 0.92 - */ -define('SIMPLEPIE_TYPE_RSS_092', 8); - -/** - * RSS 0.93 - */ -define('SIMPLEPIE_TYPE_RSS_093', 16); - -/** - * RSS 0.94 - */ -define('SIMPLEPIE_TYPE_RSS_094', 32); - -/** - * RSS 1.0 - */ -define('SIMPLEPIE_TYPE_RSS_10', 64); - -/** - * RSS 2.0 - */ -define('SIMPLEPIE_TYPE_RSS_20', 128); - -/** - * RDF-based RSS - */ -define('SIMPLEPIE_TYPE_RSS_RDF', 65); - -/** - * Non-RDF-based RSS (truly intended as syndication format) - */ -define('SIMPLEPIE_TYPE_RSS_SYNDICATION', 190); - -/** - * All RSS - */ -define('SIMPLEPIE_TYPE_RSS_ALL', 255); - -/** - * Atom 0.3 - */ -define('SIMPLEPIE_TYPE_ATOM_03', 256); - -/** - * Atom 1.0 - */ -define('SIMPLEPIE_TYPE_ATOM_10', 512); - -/** - * All Atom - */ -define('SIMPLEPIE_TYPE_ATOM_ALL', 768); - -/** - * All feed types - */ -define('SIMPLEPIE_TYPE_ALL', 1023); - -/** - * No construct - */ -define('SIMPLEPIE_CONSTRUCT_NONE', 0); - -/** - * Text construct - */ -define('SIMPLEPIE_CONSTRUCT_TEXT', 1); - -/** - * HTML construct - */ -define('SIMPLEPIE_CONSTRUCT_HTML', 2); - -/** - * XHTML construct - */ -define('SIMPLEPIE_CONSTRUCT_XHTML', 4); - -/** - * base64-encoded construct - */ -define('SIMPLEPIE_CONSTRUCT_BASE64', 8); - -/** - * IRI construct - */ -define('SIMPLEPIE_CONSTRUCT_IRI', 16); - -/** - * A construct that might be HTML - */ -define('SIMPLEPIE_CONSTRUCT_MAYBE_HTML', 32); - -/** - * All constructs - */ -define('SIMPLEPIE_CONSTRUCT_ALL', 63); - -/** - * PCRE for HTML attributes - */ -define('SIMPLEPIE_PCRE_HTML_ATTRIBUTE', '((?:[\x09\x0A\x0B\x0C\x0D\x20]+[^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3D\x3E]*(?:[\x09\x0A\x0B\x0C\x0D\x20]*=[\x09\x0A\x0B\x0C\x0D\x20]*(?:"(?:[^"]*)"|\'(?:[^\']*)\'|(?:[^\x09\x0A\x0B\x0C\x0D\x20\x22\x27\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x3E]*)?))?)*)[\x09\x0A\x0B\x0C\x0D\x20]*'); - -/** - * PCRE for XML attributes - */ -define('SIMPLEPIE_PCRE_XML_ATTRIBUTE', '((?:\s+(?:(?:[^\s:]+:)?[^\s:]+)\s*=\s*(?:"(?:[^"]*)"|\'(?:[^\']*)\'))*)\s*'); - -/** - * XML Namespace - */ -define('SIMPLEPIE_NAMESPACE_XML', 'http://www.w3.org/XML/1998/namespace'); - -/** - * Atom 1.0 Namespace - */ -define('SIMPLEPIE_NAMESPACE_ATOM_10', 'http://www.w3.org/2005/Atom'); - -/** - * Atom 0.3 Namespace - */ -define('SIMPLEPIE_NAMESPACE_ATOM_03', 'http://purl.org/atom/ns#'); - -/** - * RDF Namespace - */ -define('SIMPLEPIE_NAMESPACE_RDF', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'); - -/** - * RSS 0.90 Namespace - */ -define('SIMPLEPIE_NAMESPACE_RSS_090', 'http://my.netscape.com/rdf/simple/0.9/'); - -/** - * RSS 1.0 Namespace - */ -define('SIMPLEPIE_NAMESPACE_RSS_10', 'http://purl.org/rss/1.0/'); - -/** - * RSS 1.0 Content Module Namespace - */ -define('SIMPLEPIE_NAMESPACE_RSS_10_MODULES_CONTENT', 'http://purl.org/rss/1.0/modules/content/'); - -/** - * DC 1.0 Namespace - */ -define('SIMPLEPIE_NAMESPACE_DC_10', 'http://purl.org/dc/elements/1.0/'); - -/** - * DC 1.1 Namespace - */ -define('SIMPLEPIE_NAMESPACE_DC_11', 'http://purl.org/dc/elements/1.1/'); - -/** - * W3C Basic Geo (WGS84 lat/long) Vocabulary Namespace - */ -define('SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO', 'http://www.w3.org/2003/01/geo/wgs84_pos#'); - -/** - * GeoRSS Namespace - */ -define('SIMPLEPIE_NAMESPACE_GEORSS', 'http://www.georss.org/georss'); - -/** - * Media RSS Namespace - */ -define('SIMPLEPIE_NAMESPACE_MEDIARSS', 'http://search.yahoo.com/mrss/'); - -/** - * Wrong Media RSS Namespace - */ -define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG', 'http://search.yahoo.com/mrss'); - -/** - * iTunes RSS Namespace - */ -define('SIMPLEPIE_NAMESPACE_ITUNES', 'http://www.itunes.com/dtds/podcast-1.0.dtd'); - -/** - * XHTML Namespace - */ -define('SIMPLEPIE_NAMESPACE_XHTML', 'http://www.w3.org/1999/xhtml'); - -/** - * IANA Link Relations Registry - */ -define('SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY', 'http://www.iana.org/assignments/relation/'); - -/** - * Whether we're running on PHP5 - */ -define('SIMPLEPIE_PHP5', version_compare(PHP_VERSION, '5.0.0', '>=')); - -/** - * No file source - */ -define('SIMPLEPIE_FILE_SOURCE_NONE', 0); - -/** - * Remote file source - */ -define('SIMPLEPIE_FILE_SOURCE_REMOTE', 1); - -/** - * Local file source - */ -define('SIMPLEPIE_FILE_SOURCE_LOCAL', 2); - -/** - * fsockopen() file source - */ -define('SIMPLEPIE_FILE_SOURCE_FSOCKOPEN', 4); - -/** - * cURL file source - */ -define('SIMPLEPIE_FILE_SOURCE_CURL', 8); - -/** - * file_get_contents() file source - */ -define('SIMPLEPIE_FILE_SOURCE_FILE_GET_CONTENTS', 16); - -/** - * SimplePie - * - * @package SimplePie - * @version "Razzleberry" - * @copyright 2004-2007 Ryan Parman, Geoffrey Sneddon - * @author Ryan Parman - * @author Geoffrey Sneddon - * @todo Option for type of fetching (cache, not modified header, fetch, etc.) - */ -class SimplePie -{ - /** - * @var array Raw data - * @access private - */ - var $data = array(); - - /** - * @var mixed Error string - * @access private - */ - var $error; - - /** - * @var object Instance of SimplePie_Sanitize (or other class) - * @see SimplePie::set_sanitize_class() - * @access private - */ - var $sanitize; - - /** - * @var string SimplePie Useragent - * @see SimplePie::set_useragent() - * @access private - */ - var $useragent = SIMPLEPIE_USERAGENT; - - /** - * @var string Feed URL - * @see SimplePie::set_feed_url() - * @access private - */ - var $feed_url; - - /** - * @var object Instance of SimplePie_File to use as a feed - * @see SimplePie::set_file() - * @access private - */ - var $file; - - /** - * @var string Raw feed data - * @see SimplePie::set_raw_data() - * @access private - */ - var $raw_data; - - /** - * @var int Timeout for fetching remote files - * @see SimplePie::set_timeout() - * @access private - */ - var $timeout = 10; - - /** - * @var bool Forces fsockopen() to be used for remote files instead - * of cURL, even if a new enough version is installed - * @see SimplePie::force_fsockopen() - * @access private - */ - var $force_fsockopen = false; - - /** - * @var bool Force the given data/URL to be treated as a feed no matter what - * it appears like - * @see SimplePie::force_feed() - * @access private - */ - var $force_feed = false; - - /** - * @var bool Enable/Disable XML dump - * @see SimplePie::enable_xml_dump() - * @access private - */ - var $xml_dump = false; - - /** - * @var bool Enable/Disable Caching - * @see SimplePie::enable_cache() - * @access private - */ - var $cache = true; - - /** - * @var int Cache duration (in seconds) - * @see SimplePie::set_cache_duration() - * @access private - */ - var $cache_duration = 3600; - - /** - * @var int Auto-discovery cache duration (in seconds) - * @see SimplePie::set_autodiscovery_cache_duration() - * @access private - */ - var $autodiscovery_cache_duration = 604800; // 7 Days. - - /** - * @var string Cache location (relative to executing script) - * @see SimplePie::set_cache_location() - * @access private - */ - var $cache_location = './cache'; - - /** - * @var string Function that creates the cache filename - * @see SimplePie::set_cache_name_function() - * @access private - */ - var $cache_name_function = 'md5'; - - /** - * @var bool Reorder feed by date descending - * @see SimplePie::enable_order_by_date() - * @access private - */ - var $order_by_date = true; - - /** - * @var mixed Force input encoding to be set to the follow value - * (false, or anything type-cast to false, disables this feature) - * @see SimplePie::set_input_encoding() - * @access private - */ - var $input_encoding = false; - - /** - * @var int Feed Autodiscovery Level - * @see SimplePie::set_autodiscovery_level() - * @access private - */ - var $autodiscovery = SIMPLEPIE_LOCATOR_ALL; - - /** - * @var string Class used for caching feeds - * @see SimplePie::set_cache_class() - * @access private - */ - var $cache_class = 'SimplePie_Cache'; - - /** - * @var string Class used for locating feeds - * @see SimplePie::set_locator_class() - * @access private - */ - var $locator_class = 'SimplePie_Locator'; - - /** - * @var string Class used for parsing feeds - * @see SimplePie::set_parser_class() - * @access private - */ - var $parser_class = 'SimplePie_Parser'; - - /** - * @var string Class used for fetching feeds - * @see SimplePie::set_file_class() - * @access private - */ - var $file_class = 'SimplePie_File'; - - /** - * @var string Class used for items - * @see SimplePie::set_item_class() - * @access private - */ - var $item_class = 'SimplePie_Item'; - - /** - * @var string Class used for authors - * @see SimplePie::set_author_class() - * @access private - */ - var $author_class = 'SimplePie_Author'; - - /** - * @var string Class used for categories - * @see SimplePie::set_category_class() - * @access private - */ - var $category_class = 'SimplePie_Category'; - - /** - * @var string Class used for enclosures - * @see SimplePie::set_enclosures_class() - * @access private - */ - var $enclosure_class = 'SimplePie_Enclosure'; - - /** - * @var string Class used for Media RSS captions - * @see SimplePie::set_caption_class() - * @access private - */ - var $caption_class = 'SimplePie_Caption'; - - /** - * @var string Class used for Media RSS - * @see SimplePie::set_copyright_class() - * @access private - */ - var $copyright_class = 'SimplePie_Copyright'; - - /** - * @var string Class used for Media RSS - * @see SimplePie::set_credit_class() - * @access private - */ - var $credit_class = 'SimplePie_Credit'; - - /** - * @var string Class used for Media RSS - * @see SimplePie::set_rating_class() - * @access private - */ - var $rating_class = 'SimplePie_Rating'; - - /** - * @var string Class used for Media RSS - * @see SimplePie::set_restriction_class() - * @access private - */ - var $restriction_class = 'SimplePie_Restriction'; - - /** - * @var string Class used for content-type sniffing - * @see SimplePie::set_content_type_sniffer_class() - * @access private - */ - var $content_type_sniffer_class = 'SimplePie_Content_Type_Sniffer'; - - /** - * @var string Class used for item sources. - * @see SimplePie::set_source_class() - * @access private - */ - var $source_class = 'SimplePie_Source'; - - /** - * @var mixed Set javascript query string parameter (false, or - * anything type-cast to false, disables this feature) - * @see SimplePie::set_javascript() - * @access private - */ - var $javascript = 'js'; - - /** - * @var int Maximum number of feeds to check with autodiscovery - * @see SimplePie::set_max_checked_feeds() - * @access private - */ - var $max_checked_feeds = 10; - - /** - * @var string Web-accessible path to the handler_favicon.php file. - * @see SimplePie::set_favicon_handler() - * @access private - */ - var $favicon_handler = ''; - - /** - * @var string Web-accessible path to the handler_image.php file. - * @see SimplePie::set_image_handler() - * @access private - */ - var $image_handler = ''; - - /** - * @var array Stores the URLs when multiple feeds are being initialized. - * @see SimplePie::set_feed_url() - * @access private - */ - var $multifeed_url = array(); - - /** - * @var array Stores SimplePie objects when multiple feeds initialized. - * @access private - */ - var $multifeed_objects = array(); - - /** - * @var array Stores the get_object_vars() array for use with multifeeds. - * @see SimplePie::set_feed_url() - * @access private - */ - var $config_settings = null; - - /** - * @var integer Stores the number of items to return per-feed with multifeeds. - * @see SimplePie::set_item_limit() - * @access private - */ - var $item_limit = 0; - - /** - * @var array Stores the default attributes to be stripped by strip_attributes(). - * @see SimplePie::strip_attributes() - * @access private - */ - var $strip_attributes = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc'); - - /** - * @var array Stores the default tags to be stripped by strip_htmltags(). - * @see SimplePie::strip_htmltags() - * @access private - */ - var $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style'); - - /** - * The SimplePie class contains feed level data and options - * - * There are two ways that you can create a new SimplePie object. The first - * is by passing a feed URL as a parameter to the SimplePie constructor - * (as well as optionally setting the cache location and cache expiry). This - * will initialise the whole feed with all of the default settings, and you - * can begin accessing methods and properties immediately. - * - * The second way is to create the SimplePie object with no parameters - * at all. This will enable you to set configuration options. After setting - * them, you must initialise the feed using $feed->init(). At that point the - * object's methods and properties will be available to you. This format is - * what is used throughout this documentation. - * - * @access public - * @since 1.0 Preview Release - * @param string $feed_url This is the URL you want to parse. - * @param string $cache_location This is where you want the cache to be stored. - * @param int $cache_duration This is the number of seconds that you want to store the cache file for. - */ - function SimplePie($feed_url = null, $cache_location = null, $cache_duration = null) - { - // Other objects, instances created here so we can set options on them - $this->sanitize =& new SimplePie_Sanitize; - - // Set options if they're passed to the constructor - if ($cache_location !== null) - { - $this->set_cache_location($cache_location); - } - - if ($cache_duration !== null) - { - $this->set_cache_duration($cache_duration); - } - - // Only init the script if we're passed a feed URL - if ($feed_url !== null) - { - $this->set_feed_url($feed_url); - $this->init(); - } - } - - /** - * Used for converting object to a string - */ - function __toString() - { - return md5(serialize($this->data)); - } - - /** - * Remove items that link back to this before destroying this object - */ - function __destruct() - { - if (!empty($this->data['items'])) - { - foreach ($this->data['items'] as $item) - { - $item->__destruct(); - } - unset($this->data['items']); - } - if (!empty($this->data['ordered_items'])) - { - foreach ($this->data['ordered_items'] as $item) - { - $item->__destruct(); - } - unset($this->data['ordered_items']); - } - } - - /** - * Force the given data/URL to be treated as a feed no matter what it - * appears like - * - * @access public - * @since 1.1 - * @param bool $enable Force the given data/URL to be treated as a feed - */ - function force_feed($enable = false) - { - $this->force_feed = (bool) $enable; - } - - /** - * This is the URL of the feed you want to parse. - * - * This allows you to enter the URL of the feed you want to parse, or the - * website you want to try to use auto-discovery on. This takes priority - * over any set raw data. - * - * You can set multiple feeds to mash together by passing an array instead - * of a string for the $url. Remember that with each additional feed comes - * additional processing and resources. - * - * @access public - * @since 1.0 Preview Release - * @param mixed $url This is the URL (or array of URLs) that you want to parse. - * @see SimplePie::set_raw_data() - */ - function set_feed_url($url) - { - if (is_array($url)) - { - $this->multifeed_url = array(); - foreach ($url as $value) - { - $this->multifeed_url[] = SimplePie_Misc::fix_protocol($value, 1); - } - } - else - { - $this->feed_url = SimplePie_Misc::fix_protocol($url, 1); - } - } - - /** - * Provides an instance of SimplePie_File to use as a feed - * - * @access public - * @param object &$file Instance of SimplePie_File (or subclass) - * @return bool True on success, false on failure - */ - function set_file(&$file) - { - if (is_a($file, 'SimplePie_File')) - { - $this->feed_url = $file->url; - $this->file =& $file; - return true; - } - return false; - } - - /** - * Allows you to use a string of RSS/Atom data instead of a remote feed. - * - * If you have a feed available as a string in PHP, you can tell SimplePie - * to parse that data string instead of a remote feed. Any set feed URL - * takes precedence. - * - * @access public - * @since 1.0 Beta 3 - * @param string $data RSS or Atom data as a string. - * @see SimplePie::set_feed_url() - */ - function set_raw_data($data) - { - $this->raw_data = $data; - } - - /** - * Allows you to override the default timeout for fetching remote feeds. - * - * This allows you to change the maximum time the feed's server to respond - * and send the feed back. - * - * @access public - * @since 1.0 Beta 3 - * @param int $timeout The maximum number of seconds to spend waiting to retrieve a feed. - */ - function set_timeout($timeout = 10) - { - $this->timeout = (int) $timeout; - } - - /** - * Forces SimplePie to use fsockopen() instead of the preferred cURL - * functions. - * - * @access public - * @since 1.0 Beta 3 - * @param bool $enable Force fsockopen() to be used - */ - function force_fsockopen($enable = false) - { - $this->force_fsockopen = (bool) $enable; - } - - /** - * Outputs the raw XML content of the feed, after it has gone through - * SimplePie's filters. - * - * Used only for debugging, this function will output the XML content as - * text/xml. When SimplePie reads in a feed, it does a bit of cleaning up - * before trying to parse it. Many parts of the feed are re-written in - * memory, and in the end, you have a parsable feed. XML dump shows you the - * actual XML that SimplePie tries to parse, which may or may not be very - * different from the original feed. - * - * @access public - * @since 1.0 Preview Release - * @param bool $enable Enable XML dump - */ - function enable_xml_dump($enable = false) - { - $this->xml_dump = (bool) $enable; - } - - /** - * Enables/disables caching in SimplePie. - * - * This option allows you to disable caching all-together in SimplePie. - * However, disabling the cache can lead to longer load times. - * - * @access public - * @since 1.0 Preview Release - * @param bool $enable Enable caching - */ - function enable_cache($enable = true) - { - $this->cache = (bool) $enable; - } - - /** - * Set the length of time (in seconds) that the contents of a feed - * will be cached. - * - * @access public - * @param int $seconds The feed content cache duration. - */ - function set_cache_duration($seconds = 3600) - { - $this->cache_duration = (int) $seconds; - } - - /** - * Set the length of time (in seconds) that the autodiscovered feed - * URL will be cached. - * - * @access public - * @param int $seconds The autodiscovered feed URL cache duration. - */ - function set_autodiscovery_cache_duration($seconds = 604800) - { - $this->autodiscovery_cache_duration = (int) $seconds; - } - - /** - * Set the file system location where the cached files should be stored. - * - * @access public - * @param string $location The file system location. - */ - function set_cache_location($location = './cache') - { - $this->cache_location = (string) $location; - } - - /** - * Determines whether feed items should be sorted into reverse chronological order. - * - * @access public - * @param bool $enable Sort as reverse chronological order. - */ - function enable_order_by_date($enable = true) - { - $this->order_by_date = (bool) $enable; - } - - /** - * Allows you to override the character encoding reported by the feed. - * - * @access public - * @param string $encoding Character encoding. - */ - function set_input_encoding($encoding = false) - { - if ($encoding) - { - $this->input_encoding = (string) $encoding; - } - else - { - $this->input_encoding = false; - } - } - - /** - * Set how much feed autodiscovery to do - * - * @access public - * @see SIMPLEPIE_LOCATOR_NONE - * @see SIMPLEPIE_LOCATOR_AUTODISCOVERY - * @see SIMPLEPIE_LOCATOR_LOCAL_EXTENSION - * @see SIMPLEPIE_LOCATOR_LOCAL_BODY - * @see SIMPLEPIE_LOCATOR_REMOTE_EXTENSION - * @see SIMPLEPIE_LOCATOR_REMOTE_BODY - * @see SIMPLEPIE_LOCATOR_ALL - * @param int $level Feed Autodiscovery Level (level can be a - * combination of the above constants, see bitwise OR operator) - */ - function set_autodiscovery_level($level = SIMPLEPIE_LOCATOR_ALL) - { - $this->autodiscovery = (int) $level; - } - - /** - * Allows you to change which class SimplePie uses for caching. - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation - */ - function set_cache_class($class = 'SimplePie_Cache') - { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Cache')) - { - $this->cache_class = $class; - return true; - } - return false; - } - - /** - * Allows you to change which class SimplePie uses for auto-discovery. - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation - */ - function set_locator_class($class = 'SimplePie_Locator') - { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Locator')) - { - $this->locator_class = $class; - return true; - } - return false; - } - - /** - * Allows you to change which class SimplePie uses for XML parsing. - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation - */ - function set_parser_class($class = 'SimplePie_Parser') - { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Parser')) - { - $this->parser_class = $class; - return true; - } - return false; - } - - /** - * Allows you to change which class SimplePie uses for remote file fetching. - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation - */ - function set_file_class($class = 'SimplePie_File') - { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_File')) - { - $this->file_class = $class; - return true; - } - return false; - } - - /** - * Allows you to change which class SimplePie uses for data sanitization. - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation - */ - function set_sanitize_class($class = 'SimplePie_Sanitize') - { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Sanitize')) - { - $this->sanitize =& new $class; - return true; - } - return false; - } - - /** - * Allows you to change which class SimplePie uses for handling feed items. - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation - */ - function set_item_class($class = 'SimplePie_Item') - { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Item')) - { - $this->item_class = $class; - return true; - } - return false; - } - - /** - * Allows you to change which class SimplePie uses for handling author data. - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation - */ - function set_author_class($class = 'SimplePie_Author') - { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Author')) - { - $this->author_class = $class; - return true; - } - return false; - } - - /** - * Allows you to change which class SimplePie uses for handling category data. - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation - */ - function set_category_class($class = 'SimplePie_Category') - { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Category')) - { - $this->category_class = $class; - return true; - } - return false; - } - - /** - * Allows you to change which class SimplePie uses for feed enclosures. - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation - */ - function set_enclosure_class($class = 'SimplePie_Enclosure') - { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Enclosure')) - { - $this->enclosure_class = $class; - return true; - } - return false; - } - - /** - * Allows you to change which class SimplePie uses for captions - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation - */ - function set_caption_class($class = 'SimplePie_Caption') - { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Caption')) - { - $this->caption_class = $class; - return true; - } - return false; - } - - /** - * Allows you to change which class SimplePie uses for - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation - */ - function set_copyright_class($class = 'SimplePie_Copyright') - { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Copyright')) - { - $this->copyright_class = $class; - return true; - } - return false; - } - - /** - * Allows you to change which class SimplePie uses for - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation - */ - function set_credit_class($class = 'SimplePie_Credit') - { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Credit')) - { - $this->credit_class = $class; - return true; - } - return false; - } - - /** - * Allows you to change which class SimplePie uses for - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation - */ - function set_rating_class($class = 'SimplePie_Rating') - { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Rating')) - { - $this->rating_class = $class; - return true; - } - return false; - } - - /** - * Allows you to change which class SimplePie uses for - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation - */ - function set_restriction_class($class = 'SimplePie_Restriction') - { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Restriction')) - { - $this->restriction_class = $class; - return true; - } - return false; - } - - /** - * Allows you to change which class SimplePie uses for content-type sniffing. - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation - */ - function set_content_type_sniffer_class($class = 'SimplePie_Content_Type_Sniffer') - { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Content_Type_Sniffer')) - { - $this->content_type_sniffer_class = $class; - return true; - } - return false; - } - - /** - * Allows you to change which class SimplePie uses item sources. - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation - */ - function set_source_class($class = 'SimplePie_Source') - { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Source')) - { - $this->source_class = $class; - return true; - } - return false; - } - - /** - * Allows you to override the default user agent string. - * - * @access public - * @param string $ua New user agent string. - */ - function set_useragent($ua = SIMPLEPIE_USERAGENT) - { - $this->useragent = (string) $ua; - } - - /** - * Set callback function to create cache filename with - * - * @access public - * @param mixed $function Callback function - */ - function set_cache_name_function($function = 'md5') - { - if (is_callable($function)) - { - $this->cache_name_function = $function; - } - } - - /** - * Set javascript query string parameter - * - * @access public - * @param mixed $get Javascript query string parameter - */ - function set_javascript($get = 'js') - { - if ($get) - { - $this->javascript = (string) $get; - } - else - { - $this->javascript = false; - } - } - - /** - * Set options to make SP as fast as possible. Forgoes a - * substantial amount of data sanitization in favor of speed. - * - * @access public - * @param bool $set Whether to set them or not - */ - function set_stupidly_fast($set = false) - { - if ($set) - { - $this->enable_order_by_date(false); - $this->remove_div(false); - $this->strip_comments(false); - $this->strip_htmltags(false); - $this->strip_attributes(false); - $this->set_image_handler(false); - } - } - - /** - * Set maximum number of feeds to check with autodiscovery - * - * @access public - * @param int $max Maximum number of feeds to check - */ - function set_max_checked_feeds($max = 10) - { - $this->max_checked_feeds = (int) $max; - } - - function remove_div($enable = true) - { - $this->sanitize->remove_div($enable); - } - - function strip_htmltags($tags = '', $encode = null) - { - if ($tags === '') - { - $tags = $this->strip_htmltags; - } - $this->sanitize->strip_htmltags($tags); - if ($encode !== null) - { - $this->sanitize->encode_instead_of_strip($tags); - } - } - - function encode_instead_of_strip($enable = true) - { - $this->sanitize->encode_instead_of_strip($enable); - } - - function strip_attributes($attribs = '') - { - if ($attribs === '') - { - $attribs = $this->strip_attributes; - } - $this->sanitize->strip_attributes($attribs); - } - - function set_output_encoding($encoding = 'UTF-8') - { - $this->sanitize->set_output_encoding($encoding); - } - - function strip_comments($strip = false) - { - $this->sanitize->strip_comments($strip); - } - - /** - * Set element/attribute key/value pairs of HTML attributes - * containing URLs that need to be resolved relative to the feed - * - * @access public - * @since 1.0 - * @param array $element_attribute Element/attribute key/value pairs - */ - function set_url_replacements($element_attribute = array('a' => 'href', 'area' => 'href', 'blockquote' => 'cite', 'del' => 'cite', 'form' => 'action', 'img' => array('longdesc', 'src'), 'input' => 'src', 'ins' => 'cite', 'q' => 'cite')) - { - $this->sanitize->set_url_replacements($element_attribute); - } - - /** - * Set the handler to enable the display of cached favicons. - * - * @access public - * @param str $page Web-accessible path to the handler_favicon.php file. - * @param str $qs The query string that the value should be passed to. - */ - function set_favicon_handler($page = false, $qs = 'i') - { - if ($page != false) - { - $this->favicon_handler = $page . '?' . $qs . '='; - } - else - { - $this->favicon_handler = ''; - } - } - - /** - * Set the handler to enable the display of cached images. - * - * @access public - * @param str $page Web-accessible path to the handler_image.php file. - * @param str $qs The query string that the value should be passed to. - */ - function set_image_handler($page = false, $qs = 'i') - { - if ($page != false) - { - $this->sanitize->set_image_handler($page . '?' . $qs . '='); - } - else - { - $this->image_handler = ''; - } - } - - /** - * Set the limit for items returned per-feed with multifeeds. - * - * @access public - * @param integer $limit The maximum number of items to return. - */ - function set_item_limit($limit = 0) - { - $this->item_limit = (int) $limit; - } - - function init() - { - if ((function_exists('version_compare') && version_compare(PHP_VERSION, '4.3.0', '<')) || !extension_loaded('xml') || !extension_loaded('pcre')) - { - return false; - } - if (isset($_GET[$this->javascript])) - { - if (function_exists('ob_gzhandler')) - { - ob_start('ob_gzhandler'); - } - header('Content-type: text/javascript; charset: UTF-8'); - header('Cache-Control: must-revalidate'); - header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 604800) . ' GMT'); // 7 days - ?> -function embed_odeo(link) { - document.writeln(''); -} - -function embed_quicktime(type, bgcolor, width, height, link, placeholder, loop) { - if (placeholder != '') { - document.writeln(''); - } - else { - document.writeln(''); - } -} - -function embed_flash(bgcolor, width, height, link, loop, type) { - document.writeln(''); -} - -function embed_flv(width, height, link, placeholder, loop, player) { - document.writeln(''); -} - -function embed_wmedia(width, height, link) { - document.writeln(''); -} - sanitize->pass_cache_data($this->cache, $this->cache_location, $this->cache_name_function, $this->cache_class); - $this->sanitize->pass_file_data($this->file_class, $this->timeout, $this->useragent, $this->force_fsockopen); - - if ($this->feed_url !== null || $this->raw_data !== null) - { - $this->data = array(); - $this->multifeed_objects = array(); - $cache = false; - - if ($this->feed_url !== null) - { - $parsed_feed_url = SimplePie_Misc::parse_url($this->feed_url); - // Decide whether to enable caching - if ($this->cache && $parsed_feed_url['scheme'] !== '') - { - $cache = call_user_func(array($this->cache_class, 'create'), $this->cache_location, call_user_func($this->cache_name_function, $this->feed_url), 'spc'); - } - // If it's enabled and we don't want an XML dump, use the cache - if ($cache && !$this->xml_dump) - { - // Load the Cache - $this->data = $cache->load(); - if (!empty($this->data)) - { - // If the cache is for an outdated build of SimplePie - if (!isset($this->data['build']) || $this->data['build'] != SIMPLEPIE_BUILD) - { - $cache->unlink(); - $this->data = array(); - } - // If we've hit a collision just rerun it with caching disabled - elseif (isset($this->data['url']) && $this->data['url'] != $this->feed_url) - { - $cache = false; - $this->data = array(); - } - // If we've got a non feed_url stored (if the page isn't actually a feed, or is a redirect) use that URL. - elseif (isset($this->data['feed_url'])) - { - // If the autodiscovery cache is still valid use it. - if ($cache->mtime() + $this->autodiscovery_cache_duration > time()) - { - // Do not need to do feed autodiscovery yet. - if ($this->data['feed_url'] == $this->data['url']) - { - $cache->unlink(); - $this->data = array(); - } - else - { - $this->set_feed_url($this->data['feed_url']); - return $this->init(); - } - } - } - // Check if the cache has been updated - elseif ($cache->mtime() + $this->cache_duration < time()) - { - // If we have last-modified and/or etag set - if (isset($this->data['headers']['last-modified']) || isset($this->data['headers']['etag'])) - { - $headers = array(); - if (isset($this->data['headers']['last-modified'])) - { - $headers['if-modified-since'] = $this->data['headers']['last-modified']; - } - if (isset($this->data['headers']['etag'])) - { - $headers['if-none-match'] = '"' . $this->data['headers']['etag'] . '"'; - } - $file =& new $this->file_class($this->feed_url, $this->timeout/10, 5, $headers, $this->useragent, $this->force_fsockopen); - if ($file->success) - { - if ($file->status_code == 304) - { - $cache->touch(); - return true; - } - else - { - $headers = $file->headers; - } - } - else - { - unset($file); - } - } - } - // If the cache is still valid, just return true - else - { - return true; - } - } - // If the cache is empty, delete it - else - { - $cache->unlink(); - $this->data = array(); - } - } - // If we don't already have the file (it'll only exist if we've opened it to check if the cache has been modified), open it. - if (!isset($file)) - { - if (is_a($this->file, 'SimplePie_File') && $this->file->url == $this->feed_url) - { - $file =& $this->file; - } - else - { - $file =& new $this->file_class($this->feed_url, $this->timeout, 5, null, $this->useragent, $this->force_fsockopen); - } - } - // If the file connection has an error, set SimplePie::error to that and quit - if (!$file->success) - { - $this->error = $file->error; - if (!empty($this->data)) - { - return true; - } - else - { - return false; - } - } - - if (!$this->force_feed) - { - // Check if the supplied URL is a feed, if it isn't, look for it. - $locate =& new $this->locator_class($file, $this->timeout, $this->useragent, $this->file_class, $this->max_checked_feeds, $this->content_type_sniffer_class); - if (!$locate->is_feed($file)) - { - // We need to unset this so that if SimplePie::set_file() has been called that object is untouched - unset($file); - if ($file = $locate->find($this->autodiscovery)) - { - if ($cache) - { - $this->data = array('url' => $this->feed_url, 'feed_url' => $file->url, 'build' => SIMPLEPIE_BUILD); - if (!$cache->save($this)) - { - trigger_error("$cache->name is not writeable", E_USER_WARNING); - } - $cache = call_user_func(array($this->cache_class, 'create'), $this->cache_location, call_user_func($this->cache_name_function, $file->url), 'spc'); - } - $this->feed_url = $file->url; - } - else - { - $this->error = "A feed could not be found at $this->feed_url"; - SimplePie_Misc::error($this->error, E_USER_NOTICE, __FILE__, __LINE__); - return false; - } - } - $locate = null; - } - - $headers = $file->headers; - $data = $file->body; - $sniffer = new $this->content_type_sniffer_class($file); - $sniffed = $sniffer->get_type(); - } - else - { - $data = $this->raw_data; - } - - // Set up array of possible encodings - $encodings = array(); - - // First check to see if input has been overridden. - if ($this->input_encoding !== false) - { - $encodings[] = $this->input_encoding; - } - - $application_types = array('application/xml', 'application/xml-dtd', 'application/xml-external-parsed-entity'); - $text_types = array('text/xml', 'text/xml-external-parsed-entity'); - - // RFC 3023 (only applies to sniffed content) - if (isset($sniffed)) - { - if (in_array($sniffed, $application_types) || substr($sniffed, 0, 12) === 'application/' && substr($sniffed, -4) === '+xml') - { - if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset)) - { - $encodings[] = strtoupper($charset[1]); - } - $encodings = array_merge($encodings, SimplePie_Misc::xml_encoding($data)); - $encodings[] = 'UTF-8'; - } - elseif (in_array($sniffed, $text_types) || substr($sniffed, 0, 5) === 'text/' && substr($sniffed, -4) === '+xml') - { - if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset)) - { - $encodings[] = $charset[1]; - } - $encodings[] = 'US-ASCII'; - } - // Text MIME-type default - elseif (substr($sniffed, 0, 5) === 'text/') - { - $encodings[] = 'US-ASCII'; - } - } - - // Fallback to XML 1.0 Appendix F.1/UTF-8/ISO-8859-1 - $encodings = array_merge($encodings, SimplePie_Misc::xml_encoding($data)); - $encodings[] = 'UTF-8'; - $encodings[] = 'ISO-8859-1'; - - // There's no point in trying an encoding twice - $encodings = array_unique($encodings); - - // If we want the XML, just output that with the most likely encoding and quit - if ($this->xml_dump) - { - header('Content-type: text/xml; charset=' . $encodings[0]); - echo $data; - exit; - } - - // Loop through each possible encoding, till we return something, or run out of possibilities - foreach ($encodings as $encoding) - { - // Change the encoding to UTF-8 (as we always use UTF-8 internally) - $utf8_data = SimplePie_Misc::change_encoding($data, $encoding, 'UTF-8'); - - // Create new parser - $parser =& new $this->parser_class(); - - // If it's parsed fine - if ($parser->parse($utf8_data, 'UTF-8')) - { - $this->data = $parser->get_data(); - if (isset($this->data['child'])) - { - if (isset($headers)) - { - $this->data['headers'] = $headers; - } - $this->data['build'] = SIMPLEPIE_BUILD; - - // Cache the file if caching is enabled - if ($cache && !$cache->save($this)) - { - trigger_error("$cache->name is not writeable", E_USER_WARNING); - } - return true; - } - else - { - $this->error = "A feed could not be found at $this->feed_url"; - SimplePie_Misc::error($this->error, E_USER_NOTICE, __FILE__, __LINE__); - return false; - } - } - } - // We have an error, just set SimplePie::error to it and quit - $this->error = sprintf('XML error: %s at line %d, column %d', $parser->get_error_string(), $parser->get_current_line(), $parser->get_current_column()); - SimplePie_Misc::error($this->error, E_USER_NOTICE, __FILE__, __LINE__); - return false; - } - elseif (!empty($this->multifeed_url)) - { - $i = 0; - $success = 0; - $this->multifeed_objects = array(); - foreach ($this->multifeed_url as $url) - { - if (SIMPLEPIE_PHP5) - { - // This keyword needs to defy coding standards for PHP4 compatibility - $this->multifeed_objects[$i] = clone($this); - } - else - { - $this->multifeed_objects[$i] = $this; - } - $this->multifeed_objects[$i]->set_feed_url($url); - $success |= $this->multifeed_objects[$i]->init(); - $i++; - } - return (bool) $success; - } - else - { - return false; - } - } - - /** - * Return the error message for the occured error - * - * @access public - * @return string Error message - */ - function error() - { - return $this->error; - } - - function get_encoding() - { - return $this->sanitize->output_encoding; - } - - function handle_content_type($mime = 'text/html') - { - if (!headers_sent()) - { - $header = "Content-type: $mime;"; - if ($this->get_encoding()) - { - $header .= ' charset=' . $this->get_encoding(); - } - else - { - $header .= ' charset=UTF-8'; - } - header($header); - } - } - - function get_type() - { - if (!isset($this->data['type'])) - { - $this->data['type'] = SIMPLEPIE_TYPE_ALL; - if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'])) - { - $this->data['type'] &= SIMPLEPIE_TYPE_ATOM_10; - } - elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'])) - { - $this->data['type'] &= SIMPLEPIE_TYPE_ATOM_03; - } - elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'])) - { - if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['channel']) - || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['image']) - || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item']) - || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['textinput'])) - { - $this->data['type'] &= SIMPLEPIE_TYPE_RSS_10; - } - if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['channel']) - || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['image']) - || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item']) - || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['textinput'])) - { - $this->data['type'] &= SIMPLEPIE_TYPE_RSS_090; - } - } - elseif (isset($this->data['child']['']['rss'])) - { - $this->data['type'] &= SIMPLEPIE_TYPE_RSS_ALL; - if (isset($this->data['child']['']['rss'][0]['attribs']['']['version'])) - { - switch (trim($this->data['child']['']['rss'][0]['attribs']['']['version'])) - { - case '0.91': - $this->data['type'] &= SIMPLEPIE_TYPE_RSS_091; - if (isset($this->data['child']['']['rss'][0]['child']['']['skiphours']['hour'][0]['data'])) - { - switch (trim($this->data['child']['']['rss'][0]['child']['']['skiphours']['hour'][0]['data'])) - { - case '0': - $this->data['type'] &= SIMPLEPIE_TYPE_RSS_091_NETSCAPE; - break; - - case '24': - $this->data['type'] &= SIMPLEPIE_TYPE_RSS_091_USERLAND; - break; - } - } - break; - - case '0.92': - $this->data['type'] &= SIMPLEPIE_TYPE_RSS_092; - break; - - case '0.93': - $this->data['type'] &= SIMPLEPIE_TYPE_RSS_093; - break; - - case '0.94': - $this->data['type'] &= SIMPLEPIE_TYPE_RSS_094; - break; - - case '2.0': - $this->data['type'] &= SIMPLEPIE_TYPE_RSS_20; - break; - } - } - } - else - { - $this->data['type'] = SIMPLEPIE_TYPE_NONE; - } - } - return $this->data['type']; - } - - /** - * Returns the URL for the favicon of the feed's website. - * - * @todo Cache atom:icon - * @access public - * @since 1.0 - */ - function get_favicon() - { - if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'icon')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); - } - elseif (($url = $this->get_link()) !== null && preg_match('/^http(s)?:\/\//i', $url)) - { - $favicon = SimplePie_Misc::absolutize_url('/favicon.ico', $url); - - if ($this->cache && $this->favicon_handler) - { - $favicon_filename = call_user_func($this->cache_name_function, $favicon); - $cache = call_user_func(array($this->cache_class, 'create'), $this->cache_location, $favicon_filename, 'spi'); - - if ($cache->load()) - { - return $this->sanitize($this->favicon_handler . $favicon_filename, SIMPLEPIE_CONSTRUCT_IRI); - } - else - { - $file =& new $this->file_class($favicon, $this->timeout / 10, 5, array('X-FORWARDED-FOR' => $_SERVER['REMOTE_ADDR']), $this->useragent, $this->force_fsockopen); - - if ($file->success && ($file->status_code == 200 || ($file->status_code > 206 && $file->status_code < 300)) && strlen($file->body) > 0) - { - $sniffer = new $this->content_type_sniffer_class($file); - if (substr($sniffer->get_type(), 0, 6) === 'image/') - { - if ($cache->save(array('headers' => $file->headers, 'body' => $file->body))) - { - return $this->sanitize($this->favicon_handler . $favicon_filename, SIMPLEPIE_CONSTRUCT_IRI); - } - else - { - trigger_error("$cache->name is not writeable", E_USER_WARNING); - return $this->sanitize($favicon, SIMPLEPIE_CONSTRUCT_IRI); - } - } - } - } - } - else - { - return $this->sanitize($favicon, SIMPLEPIE_CONSTRUCT_IRI); - } - } - return false; - } - - /** - * @todo If we have a perm redirect we should return the new URL - * @todo When we make the above change, let's support as well - * @todo Also, |atom:link|@rel=self - */ - function subscribe_url() - { - if ($this->feed_url !== null) - { - return $this->sanitize($this->feed_url, SIMPLEPIE_CONSTRUCT_IRI); - } - else - { - return null; - } - } - - function subscribe_feed() - { - if ($this->feed_url !== null) - { - return $this->sanitize(SimplePie_Misc::fix_protocol($this->feed_url, 2), SIMPLEPIE_CONSTRUCT_IRI); - } - else - { - return null; - } - } - - function subscribe_outlook() - { - if ($this->feed_url !== null) - { - return 'outlook' . $this->sanitize(SimplePie_Misc::fix_protocol($this->feed_url, 2), SIMPLEPIE_CONSTRUCT_IRI); - } - else - { - return null; - } - } - - function subscribe_podcast() - { - if ($this->feed_url !== null) - { - return $this->sanitize(SimplePie_Misc::fix_protocol($this->feed_url, 3), SIMPLEPIE_CONSTRUCT_IRI); - } - else - { - return null; - } - } - - function subscribe_itunes() - { - if ($this->feed_url !== null) - { - return $this->sanitize(SimplePie_Misc::fix_protocol($this->feed_url, 4), SIMPLEPIE_CONSTRUCT_IRI); - } - else - { - return null; - } - } - - /** - * Creates the subscribe_* methods' return data - * - * @access private - * @param string $feed_url String to prefix to the feed URL - * @param string $site_url String to prefix to the site URL (and - * suffix to the feed URL) - * @return mixed URL if feed exists, false otherwise - */ - function subscribe_service($feed_url, $site_url = null) - { - if ($this->subscribe_url()) - { - $return = $this->sanitize($feed_url, SIMPLEPIE_CONSTRUCT_IRI) . rawurlencode($this->feed_url); - if ($site_url !== null && $this->get_link() !== null) - { - $return .= $this->sanitize($site_url, SIMPLEPIE_CONSTRUCT_IRI) . rawurlencode($this->get_link()); - } - return $return; - } - else - { - return null; - } - } - - function subscribe_aol() - { - return $this->subscribe_service('http://feeds.my.aol.com/add.jsp?url='); - } - - function subscribe_bloglines() - { - return urldecode($this->subscribe_service('http://www.bloglines.com/sub/')); - } - - function subscribe_eskobo() - { - return $this->subscribe_service('http://www.eskobo.com/?AddToMyPage='); - } - - function subscribe_feedfeeds() - { - return $this->subscribe_service('http://www.feedfeeds.com/add?feed='); - } - - function subscribe_feedster() - { - return $this->subscribe_service('http://www.feedster.com/myfeedster.php?action=addrss&confirm=no&rssurl='); - } - - function subscribe_google() - { - return $this->subscribe_service('http://fusion.google.com/add?feedurl='); - } - - function subscribe_gritwire() - { - return $this->subscribe_service('http://my.gritwire.com/feeds/addExternalFeed.aspx?FeedUrl='); - } - - function subscribe_msn() - { - return $this->subscribe_service('http://my.msn.com/addtomymsn.armx?id=rss&ut=', '&ru='); - } - - function subscribe_netvibes() - { - return $this->subscribe_service('http://www.netvibes.com/subscribe.php?url='); - } - - function subscribe_newsburst() - { - return $this->subscribe_service('http://www.newsburst.com/Source/?add='); - } - - function subscribe_newsgator() - { - return $this->subscribe_service('http://www.newsgator.com/ngs/subscriber/subext.aspx?url='); - } - - function subscribe_odeo() - { - return $this->subscribe_service('http://www.odeo.com/listen/subscribe?feed='); - } - - function subscribe_podnova() - { - return $this->subscribe_service('http://www.podnova.com/index_your_podcasts.srf?action=add&url='); - } - - function subscribe_rojo() - { - return $this->subscribe_service('http://www.rojo.com/add-subscription?resource='); - } - - function subscribe_yahoo() - { - return $this->subscribe_service('http://add.my.yahoo.com/rss?url='); - } - - function get_feed_tags($namespace, $tag) - { - $type = $this->get_type(); - if ($type & SIMPLEPIE_TYPE_ATOM_10) - { - if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['child'][$namespace][$tag])) - { - return $this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['child'][$namespace][$tag]; - } - } - if ($type & SIMPLEPIE_TYPE_ATOM_03) - { - if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['child'][$namespace][$tag])) - { - return $this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['child'][$namespace][$tag]; - } - } - if ($type & SIMPLEPIE_TYPE_RSS_RDF) - { - if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][$namespace][$tag])) - { - return $this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][$namespace][$tag]; - } - } - if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION) - { - if (isset($this->data['child']['']['rss'][0]['child'][$namespace][$tag])) - { - return $this->data['child']['']['rss'][0]['child'][$namespace][$tag]; - } - } - return null; - } - - function get_channel_tags($namespace, $tag) - { - $type = $this->get_type(); - if ($type & SIMPLEPIE_TYPE_ATOM_ALL) - { - if ($return = $this->get_feed_tags($namespace, $tag)) - { - return $return; - } - } - if ($type & SIMPLEPIE_TYPE_RSS_10) - { - if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'channel')) - { - if (isset($channel[0]['child'][$namespace][$tag])) - { - return $channel[0]['child'][$namespace][$tag]; - } - } - } - if ($type & SIMPLEPIE_TYPE_RSS_090) - { - if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'channel')) - { - if (isset($channel[0]['child'][$namespace][$tag])) - { - return $channel[0]['child'][$namespace][$tag]; - } - } - } - if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION) - { - if ($channel = $this->get_feed_tags('', 'channel')) - { - if (isset($channel[0]['child'][$namespace][$tag])) - { - return $channel[0]['child'][$namespace][$tag]; - } - } - } - return null; - } - - function get_image_tags($namespace, $tag) - { - $type = $this->get_type(); - if ($type & SIMPLEPIE_TYPE_RSS_10) - { - if ($image = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'image')) - { - if (isset($image[0]['child'][$namespace][$tag])) - { - return $image[0]['child'][$namespace][$tag]; - } - } - } - if ($type & SIMPLEPIE_TYPE_RSS_090) - { - if ($image = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'image')) - { - if (isset($image[0]['child'][$namespace][$tag])) - { - return $image[0]['child'][$namespace][$tag]; - } - } - } - if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION) - { - if ($image = $this->get_channel_tags('', 'image')) - { - if (isset($image[0]['child'][$namespace][$tag])) - { - return $image[0]['child'][$namespace][$tag]; - } - } - } - return null; - } - - function get_base($element = array()) - { - if (!($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION) && !empty($element['xml_base_explicit']) && isset($element['xml_base'])) - { - return $element['xml_base']; - } - elseif ($this->get_link() !== null) - { - return $this->get_link(); - } - else - { - return $this->subscribe_url(); - } - } - - function sanitize($data, $type, $base = '') - { - return $this->sanitize->sanitize($data, $type, $base); - } - - function get_title() - { - if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_channel_tags('', 'title')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - return null; - } - } - - function get_category($key = 0) - { - $categories = $this->get_categories(); - if (isset($categories[$key])) - { - return $categories[$key]; - } - else - { - return null; - } - } - - function get_categories() - { - $categories = array(); - - foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category) - { - $term = null; - $scheme = null; - $label = null; - if (isset($category['attribs']['']['term'])) - { - $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($category['attribs']['']['scheme'])) - { - $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($category['attribs']['']['label'])) - { - $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $categories[] =& new $this->category_class($term, $scheme, $label); - } - foreach ((array) $this->get_channel_tags('', 'category') as $category) - { - $categories[] =& new $this->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); - } - foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category) - { - $categories[] =& new $this->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); - } - foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category) - { - $categories[] =& new $this->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); - } - - if (!empty($categories)) - { - return SimplePie_Misc::array_unique($categories); - } - else - { - return null; - } - } - - function get_author($key = 0) - { - $authors = $this->get_authors(); - if (isset($authors[$key])) - { - return $authors[$key]; - } - else - { - return null; - } - } - - function get_authors() - { - $authors = array(); - foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author) - { - $name = null; - $uri = null; - $email = null; - if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) - { - $name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) - { - $uri = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); - } - if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) - { - $email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if ($name !== null || $email !== null || $uri !== null) - { - $authors[] =& new $this->author_class($name, $uri, $email); - } - } - if ($author = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author')) - { - $name = null; - $url = null; - $email = null; - if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) - { - $name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) - { - $url = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); - } - if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) - { - $email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if ($name !== null || $email !== null || $url !== null) - { - $authors[] =& new $this->author_class($name, $url, $email); - } - } - foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author) - { - $authors[] =& new $this->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); - } - foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author) - { - $authors[] =& new $this->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); - } - foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author) - { - $authors[] =& new $this->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); - } - - if (!empty($authors)) - { - return SimplePie_Misc::array_unique($authors); - } - else - { - return null; - } - } - - function get_contributor($key = 0) - { - $contributors = $this->get_contributors(); - if (isset($contributors[$key])) - { - return $contributors[$key]; - } - else - { - return null; - } - } - - function get_contributors() - { - $contributors = array(); - foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor) - { - $name = null; - $uri = null; - $email = null; - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) - { - $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) - { - $uri = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); - } - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) - { - $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if ($name !== null || $email !== null || $uri !== null) - { - $contributors[] =& new $this->author_class($name, $uri, $email); - } - } - foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor) - { - $name = null; - $url = null; - $email = null; - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) - { - $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) - { - $url = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); - } - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) - { - $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if ($name !== null || $email !== null || $url !== null) - { - $contributors[] =& new $this->author_class($name, $url, $email); - } - } - - if (!empty($contributors)) - { - return SimplePie_Misc::array_unique($contributors); - } - else - { - return null; - } - } - - function get_link($key = 0, $rel = 'alternate') - { - $links = $this->get_links($rel); - if (isset($links[$key])) - { - return $links[$key]; - } - else - { - return null; - } - } - - /** - * Added for parity between the parent-level and the item/entry-level. - */ - function get_permalink() - { - return $this->get_link(0); - } - - function get_links($rel = 'alternate') - { - if (!isset($this->data['links'])) - { - $this->data['links'] = array(); - if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link')) - { - foreach ($links as $link) - { - if (isset($link['attribs']['']['href'])) - { - $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; - $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); - } - } - } - if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link')) - { - foreach ($links as $link) - { - if (isset($link['attribs']['']['href'])) - { - $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; - $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); - - } - } - } - if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link')) - { - $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); - } - if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link')) - { - $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); - } - if ($links = $this->get_channel_tags('', 'link')) - { - $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); - } - - $keys = array_keys($this->data['links']); - foreach ($keys as $key) - { - if (SimplePie_Misc::is_isegment_nz_nc($key)) - { - if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key])) - { - $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]); - $this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]; - } - else - { - $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key]; - } - } - elseif (substr($key, 0, 41) == SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY) - { - $this->data['links'][substr($key, 41)] =& $this->data['links'][$key]; - } - $this->data['links'][$key] = array_unique($this->data['links'][$key]); - } - } - - if (isset($this->data['links'][$rel])) - { - return $this->data['links'][$rel]; - } - else - { - return null; - } - } - - function get_description() - { - if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'subtitle')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'tagline')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_channel_tags('', 'description')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); - } - else - { - return null; - } - } - - function get_copyright() - { - if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'copyright')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_channel_tags('', 'copyright')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - return null; - } - } - - function get_language() - { - if ($return = $this->get_channel_tags('', 'language')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'language')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'language')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['xml_lang'])) - { - return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['xml_lang'])) - { - return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['xml_lang'])) - { - return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif (isset($this->data['headers']['content-language'])) - { - return $this->sanitize($this->data['headers']['content-language'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - return null; - } - } - - function get_latitude() - { - if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat')) - { - return (float) $return[0]['data']; - } - elseif (($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', $return[0]['data'], $match)) - { - return (float) $match[1]; - } - else - { - return null; - } - } - - function get_longitude() - { - if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long')) - { - return (float) $return[0]['data']; - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon')) - { - return (float) $return[0]['data']; - } - elseif (($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', $return[0]['data'], $match)) - { - return (float) $match[2]; - } - else - { - return null; - } - } - - function get_image_title() - { - if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_image_tags('', 'title')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - return null; - } - } - - function get_image_url() - { - if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'image')) - { - return $this->sanitize($return[0]['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'logo')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'icon')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); - } - elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'url')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); - } - elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'url')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); - } - elseif ($return = $this->get_image_tags('', 'url')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); - } - else - { - return null; - } - } - - function get_image_link() - { - if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); - } - elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); - } - elseif ($return = $this->get_image_tags('', 'link')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); - } - else - { - return null; - } - } - - function get_image_width() - { - if ($return = $this->get_image_tags('', 'width')) - { - return round($return[0]['data']); - } - elseif ($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION && $this->get_image_tags('', 'url')) - { - return 88.0; - } - else - { - return null; - } - } - - function get_image_height() - { - if ($return = $this->get_image_tags('', 'height')) - { - return round($return[0]['data']); - } - elseif ($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION && $this->get_image_tags('', 'url')) - { - return 31.0; - } - else - { - return null; - } - } - - function get_item_quantity($max = 0) - { - $qty = count($this->get_items()); - if ($max == 0) - { - return $qty; - } - else - { - return ($qty > $max) ? $max : $qty; - } - } - - function get_item($key = 0) - { - $items = $this->get_items(); - if (isset($items[$key])) - { - return $items[$key]; - } - else - { - return null; - } - } - - function get_items($start = 0, $end = 0) - { - if (!empty($this->multifeed_objects)) - { - return SimplePie::merge_items($this->multifeed_objects, $start, $end, $this->item_limit); - } - elseif (!isset($this->data['items'])) - { - if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'entry')) - { - $keys = array_keys($items); - foreach ($keys as $key) - { - $this->data['items'][] =& new $this->item_class($this, $items[$key]); - } - } - if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'entry')) - { - $keys = array_keys($items); - foreach ($keys as $key) - { - $this->data['items'][] =& new $this->item_class($this, $items[$key]); - } - } - if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'item')) - { - $keys = array_keys($items); - foreach ($keys as $key) - { - $this->data['items'][] =& new $this->item_class($this, $items[$key]); - } - } - if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'item')) - { - $keys = array_keys($items); - foreach ($keys as $key) - { - $this->data['items'][] =& new $this->item_class($this, $items[$key]); - } - } - if ($items = $this->get_channel_tags('', 'item')) - { - $keys = array_keys($items); - foreach ($keys as $key) - { - $this->data['items'][] =& new $this->item_class($this, $items[$key]); - } - } - } - - if (!empty($this->data['items'])) - { - // If we want to order it by date, check if all items have a date, and then sort it - if ($this->order_by_date) - { - if (!isset($this->data['ordered_items'])) - { - $do_sort = true; - foreach ($this->data['items'] as $item) - { - if (!$item->get_date('U')) - { - $do_sort = false; - break; - } - } - $item = null; - $this->data['ordered_items'] = $this->data['items']; - if ($do_sort) - { - usort($this->data['ordered_items'], array(&$this, 'sort_items')); - } - } - $items = $this->data['ordered_items']; - } - else - { - $items = $this->data['items']; - } - - // Slice the data as desired - if ($end == 0) - { - return array_slice($items, $start); - } - else - { - return array_slice($items, $start, $end); - } - } - else - { - return array(); - } - } - - function sort_items($a, $b) - { - return $a->get_date('U') <= $b->get_date('U'); - } - - function merge_items($urls, $start = 0, $end = 0, $limit = 0) - { - if (is_array($urls) && sizeof($urls) > 0) - { - $items = array(); - foreach ($urls as $arg) - { - if (is_a($arg, 'SimplePie')) - { - $items = array_merge($items, $arg->get_items(0, $limit)); - } - else - { - trigger_error('Arguments must be SimplePie objects', E_USER_WARNING); - } - } - - $do_sort = true; - foreach ($items as $item) - { - if (!$item->get_date('U')) - { - $do_sort = false; - break; - } - } - $item = null; - if ($do_sort) - { - usort($items, array('SimplePie', 'sort_items')); - } - - if ($end == 0) - { - return array_slice($items, $start); - } - else - { - return array_slice($items, $start, $end); - } - } - else - { - trigger_error('Cannot merge zero SimplePie objects', E_USER_WARNING); - return array(); - } - } -} - -class SimplePie_Item -{ - var $feed; - var $data = array(); - - function SimplePie_Item($feed, $data) - { - $this->feed = $feed; - $this->data = $data; - } - - function __toString() - { - return md5(serialize($this->data)); - } - - /** - * Remove items that link back to this before destroying this object - */ - function __destruct() - { - unset($this->feed); - } - - function get_item_tags($namespace, $tag) - { - if (isset($this->data['child'][$namespace][$tag])) - { - return $this->data['child'][$namespace][$tag]; - } - else - { - return null; - } - } - - function get_base($element = array()) - { - return $this->feed->get_base($element); - } - - function sanitize($data, $type, $base = '') - { - return $this->feed->sanitize($data, $type, $base); - } - - function get_feed() - { - return $this->feed; - } - - function get_id($hash = false) - { - if (!$hash) - { - if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'id')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'id')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_item_tags('', 'guid')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'identifier')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'identifier')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif (($return = $this->get_permalink()) !== null) - { - return $return; - } - elseif (($return = $this->get_title()) !== null) - { - return $return; - } - } - if ($this->get_permalink() !== null || $this->get_title() !== null) - { - return md5($this->get_permalink() . $this->get_title()); - } - else - { - return md5(serialize($this->data)); - } - } - - function get_title() - { - if (!isset($this->data['title'])) - { - if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title')) - { - $this->data['title'] = $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title')) - { - $this->data['title'] = $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title')) - { - $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title')) - { - $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_item_tags('', 'title')) - { - $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title')) - { - $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title')) - { - $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $this->data['title'] = null; - } - } - return $this->data['title']; - } - - function get_description($description_only = false) - { - if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'summary')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'summary')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_item_tags('', 'description')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif (!$description_only) - { - return $this->get_content(true); - } - else - { - return null; - } - } - - function get_content($content_only = false) - { - if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'content')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_content_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'content')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10_MODULES_CONTENT, 'encoded')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); - } - elseif (!$content_only) - { - return $this->get_description(true); - } - else - { - return null; - } - } - - function get_category($key = 0) - { - $categories = $this->get_categories(); - if (isset($categories[$key])) - { - return $categories[$key]; - } - else - { - return null; - } - } - - function get_categories() - { - $categories = array(); - - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category) - { - $term = null; - $scheme = null; - $label = null; - if (isset($category['attribs']['']['term'])) - { - $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($category['attribs']['']['scheme'])) - { - $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($category['attribs']['']['label'])) - { - $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $categories[] =& new $this->feed->category_class($term, $scheme, $label); - } - foreach ((array) $this->get_item_tags('', 'category') as $category) - { - $categories[] =& new $this->feed->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); - } - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category) - { - $categories[] =& new $this->feed->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); - } - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category) - { - $categories[] =& new $this->feed->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); - } - - if (!empty($categories)) - { - return SimplePie_Misc::array_unique($categories); - } - else - { - return null; - } - } - - function get_author($key = 0) - { - $authors = $this->get_authors(); - if (isset($authors[$key])) - { - return $authors[$key]; - } - else - { - return null; - } - } - - function get_contributor($key = 0) - { - $contributors = $this->get_contributors(); - if (isset($contributors[$key])) - { - return $contributors[$key]; - } - else - { - return null; - } - } - - function get_contributors() - { - $contributors = array(); - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor) - { - $name = null; - $uri = null; - $email = null; - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) - { - $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) - { - $uri = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); - } - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) - { - $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if ($name !== null || $email !== null || $uri !== null) - { - $contributors[] =& new $this->feed->author_class($name, $uri, $email); - } - } - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor) - { - $name = null; - $url = null; - $email = null; - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) - { - $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) - { - $url = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); - } - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) - { - $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if ($name !== null || $email !== null || $url !== null) - { - $contributors[] =& new $this->feed->author_class($name, $url, $email); - } - } - - if (!empty($contributors)) - { - return SimplePie_Misc::array_unique($contributors); - } - else - { - return null; - } - } - - /** - * @todo Atom inheritance (item author, source author, feed author) - */ - function get_authors() - { - $authors = array(); - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author) - { - $name = null; - $uri = null; - $email = null; - if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) - { - $name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) - { - $uri = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); - } - if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) - { - $email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if ($name !== null || $email !== null || $uri !== null) - { - $authors[] =& new $this->feed->author_class($name, $uri, $email); - } - } - if ($author = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author')) - { - $name = null; - $url = null; - $email = null; - if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) - { - $name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) - { - $url = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); - } - if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) - { - $email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if ($name !== null || $email !== null || $url !== null) - { - $authors[] =& new $this->feed->author_class($name, $url, $email); - } - } - if ($author = $this->get_item_tags('', 'author')) - { - $authors[] =& new $this->feed->author_class(null, null, $this->sanitize($author[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); - } - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author) - { - $authors[] =& new $this->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); - } - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author) - { - $authors[] =& new $this->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); - } - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author) - { - $authors[] =& new $this->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); - } - - if (!empty($authors)) - { - return SimplePie_Misc::array_unique($authors); - } - elseif (($source = $this->get_source()) && ($authors = $source->get_authors())) - { - return $authors; - } - elseif ($authors = $this->feed->get_authors()) - { - return $authors; - } - else - { - return null; - } - } - - function get_copyright() - { - if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - return null; - } - } - - function get_date($date_format = 'j F Y, g:i a') - { - if (!isset($this->data['date'])) - { - if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'published')) - { - $this->data['date']['raw'] = $return[0]['data']; - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'updated')) - { - $this->data['date']['raw'] = $return[0]['data']; - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'issued')) - { - $this->data['date']['raw'] = $return[0]['data']; - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'created')) - { - $this->data['date']['raw'] = $return[0]['data']; - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'modified')) - { - $this->data['date']['raw'] = $return[0]['data']; - } - elseif ($return = $this->get_item_tags('', 'pubDate')) - { - $this->data['date']['raw'] = $return[0]['data']; - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'date')) - { - $this->data['date']['raw'] = $return[0]['data']; - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'date')) - { - $this->data['date']['raw'] = $return[0]['data']; - } - - if (!empty($this->data['date']['raw'])) - { - $parser = SimplePie_Parse_Date::get(); - $this->data['date']['parsed'] = $parser->parse($this->data['date']['raw']); - } - else - { - $this->data['date'] = null; - } - } - if ($this->data['date']) - { - $date_format = (string) $date_format; - switch ($date_format) - { - case '': - return $this->sanitize($this->data['date']['raw'], SIMPLEPIE_CONSTRUCT_TEXT); - - case 'U': - return $this->data['date']['parsed']; - - default: - return date($date_format, $this->data['date']['parsed']); - } - } - else - { - return null; - } - } - - function get_local_date($date_format = '%c') - { - if (!$date_format) - { - return $this->sanitize($this->get_date(''), SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif (($date = $this->get_date('U')) !== null) - { - return strftime($date_format, $date); - } - else - { - return null; - } - } - - function get_permalink() - { - $link = $this->get_link(); - $enclosure = $this->get_enclosure(0); - if ($link !== null) - { - return $link; - } - elseif ($enclosure !== null) - { - return $enclosure->get_link(); - } - else - { - return null; - } - } - - function get_link($key = 0, $rel = 'alternate') - { - $links = $this->get_links($rel); - if ($links[$key] !== null) - { - return $links[$key]; - } - else - { - return null; - } - } - - function get_links($rel = 'alternate') - { - if (!isset($this->data['links'])) - { - $this->data['links'] = array(); - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link') as $link) - { - if (isset($link['attribs']['']['href'])) - { - $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; - $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); - - } - } - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link') as $link) - { - if (isset($link['attribs']['']['href'])) - { - $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; - $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); - } - } - if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link')) - { - $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); - } - if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link')) - { - $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); - } - if ($links = $this->get_item_tags('', 'link')) - { - $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); - } - if ($links = $this->get_item_tags('', 'guid')) - { - if (!isset($links[0]['attribs']['']['isPermaLink']) || strtolower(trim($links[0]['attribs']['']['isPermaLink'])) == 'true') - { - $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); - } - } - - $keys = array_keys($this->data['links']); - foreach ($keys as $key) - { - if (SimplePie_Misc::is_isegment_nz_nc($key)) - { - if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key])) - { - $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]); - $this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]; - } - else - { - $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key]; - } - } - elseif (substr($key, 0, 41) == SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY) - { - $this->data['links'][substr($key, 41)] =& $this->data['links'][$key]; - } - $this->data['links'][$key] = array_unique($this->data['links'][$key]); - } - } - if (isset($this->data['links'][$rel])) - { - return $this->data['links'][$rel]; - } - else - { - return null; - } - } - - /** - * @todo Add ability to prefer one type of content over another (in a media group). - */ - function get_enclosure($key = 0, $prefer = null) - { - $enclosures = $this->get_enclosures(); - if (isset($enclosures[$key])) - { - return $enclosures[$key]; - } - else - { - return null; - } - } - - /** - * Grabs all available enclosures (podcasts, etc.) - * - * Supports the RSS tag, as well as Media RSS and iTunes RSS. - * - * At this point, we're pretty much assuming that all enclosures for an item are the same content. Anything else is too complicated to properly support. - * - * @todo Add support for end-user defined sorting of enclosures by type/handler (so we can prefer the faster-loading FLV over MP4). - * @todo If an element exists at a level, but it's value is empty, we should fall back to the value from the parent (if it exists). - */ - function get_enclosures() - { - if (!isset($this->data['enclosures'])) - { - $this->data['enclosures'] = array(); - - // Elements - $captions_parent = null; - $categories_parent = null; - $copyrights_parent = null; - $credits_parent = null; - $description_parent = null; - $duration_parent = null; - $hashes_parent = null; - $keywords_parent = null; - $player_parent = null; - $ratings_parent = null; - $restrictions_parent = null; - $thumbnails_parent = null; - $title_parent = null; - - // Let's do the channel and item-level ones first, and just re-use them if we need to. - $parent = $this->get_feed(); - - // CAPTIONS - if ($captions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'text')) - { - foreach ($captions as $caption) - { - $caption_type = null; - $caption_lang = null; - $caption_startTime = null; - $caption_endTime = null; - $caption_text = null; - if (isset($caption['attribs']['']['type'])) - { - $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['lang'])) - { - $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['start'])) - { - $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['end'])) - { - $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['data'])) - { - $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $captions_parent[] =& new $this->feed->caption_class($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text); - } - } - elseif ($captions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'text')) - { - foreach ($captions as $caption) - { - $caption_type = null; - $caption_lang = null; - $caption_startTime = null; - $caption_endTime = null; - $caption_text = null; - if (isset($caption['attribs']['']['type'])) - { - $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['lang'])) - { - $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['start'])) - { - $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['end'])) - { - $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['data'])) - { - $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $captions_parent[] =& new $this->feed->caption_class($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text); - } - } - if (is_array($captions_parent)) - { - $captions_parent = array_values(SimplePie_Misc::array_unique($captions_parent)); - } - - // CATEGORIES - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'category') as $category) - { - $term = null; - $scheme = null; - $label = null; - if (isset($category['data'])) - { - $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($category['attribs']['']['scheme'])) - { - $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $scheme = 'http://search.yahoo.com/mrss/category_schema'; - } - if (isset($category['attribs']['']['label'])) - { - $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $categories_parent[] =& new $this->feed->category_class($term, $scheme, $label); - } - foreach ((array) $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'category') as $category) - { - $term = null; - $scheme = null; - $label = null; - if (isset($category['data'])) - { - $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($category['attribs']['']['scheme'])) - { - $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $scheme = 'http://search.yahoo.com/mrss/category_schema'; - } - if (isset($category['attribs']['']['label'])) - { - $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $categories_parent[] =& new $this->feed->category_class($term, $scheme, $label); - } - foreach ((array) $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'category') as $category) - { - $term = null; - $scheme = 'http://www.itunes.com/dtds/podcast-1.0.dtd'; - $label = null; - if (isset($category['attribs']['']['text'])) - { - $label = $this->sanitize($category['attribs']['']['text'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $categories_parent[] =& new $this->feed->category_class($term, $scheme, $label); - - if (isset($category['child'][SIMPLEPIE_NAMESPACE_ITUNES]['category'])) - { - foreach ((array) $category['child'][SIMPLEPIE_NAMESPACE_ITUNES]['category'] as $subcategory) - { - if (isset($subcategory['attribs']['']['text'])) - { - $label = $this->sanitize($subcategory['attribs']['']['text'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $categories_parent[] =& new $this->feed->category_class($term, $scheme, $label); - } - } - } - if (is_array($categories_parent)) - { - $categories_parent = array_values(SimplePie_Misc::array_unique($categories_parent)); - } - - // COPYRIGHT - if ($copyright = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'copyright')) - { - $copyright_url = null; - $copyright_label = null; - if (isset($copyright[0]['attribs']['']['url'])) - { - $copyright_url = $this->sanitize($copyright[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($copyright[0]['data'])) - { - $copyright_label = $this->sanitize($copyright[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $copyrights_parent =& new $this->feed->copyright_class($copyright_url, $copyright_label); - } - elseif ($copyright = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'copyright')) - { - $copyright_url = null; - $copyright_label = null; - if (isset($copyright[0]['attribs']['']['url'])) - { - $copyright_url = $this->sanitize($copyright[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($copyright[0]['data'])) - { - $copyright_label = $this->sanitize($copyright[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $copyrights_parent =& new $this->feed->copyright_class($copyright_url, $copyright_label); - } - - // CREDITS - if ($credits = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'credit')) - { - foreach ($credits as $credit) - { - $credit_role = null; - $credit_scheme = null; - $credit_name = null; - if (isset($credit['attribs']['']['role'])) - { - $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($credit['attribs']['']['scheme'])) - { - $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $credit_scheme = 'urn:ebu'; - } - if (isset($credit['data'])) - { - $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $credits_parent[] =& new $this->feed->credit_class($credit_role, $credit_scheme, $credit_name); - } - } - elseif ($credits = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'credit')) - { - foreach ($credits as $credit) - { - $credit_role = null; - $credit_scheme = null; - $credit_name = null; - if (isset($credit['attribs']['']['role'])) - { - $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($credit['attribs']['']['scheme'])) - { - $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $credit_scheme = 'urn:ebu'; - } - if (isset($credit['data'])) - { - $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $credits_parent[] =& new $this->feed->credit_class($credit_role, $credit_scheme, $credit_name); - } - } - if (is_array($credits_parent)) - { - $credits_parent = array_values(SimplePie_Misc::array_unique($credits_parent)); - } - - // DESCRIPTION - if ($description_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'description')) - { - if (isset($description_parent[0]['data'])) - { - $description_parent = $this->sanitize($description_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - } - elseif ($description_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'description')) - { - if (isset($description_parent[0]['data'])) - { - $description_parent = $this->sanitize($description_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - } - - // DURATION - if ($duration_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'duration')) - { - $seconds = null; - $minutes = null; - $hours = null; - if (isset($duration_parent[0]['data'])) - { - $temp = explode(':', $this->sanitize($duration_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); - if (sizeof($temp) > 0) - { - (int) $seconds = array_pop($temp); - } - if (sizeof($temp) > 0) - { - (int) $minutes = array_pop($temp); - $seconds += $minutes * 60; - } - if (sizeof($temp) > 0) - { - (int) $hours = array_pop($temp); - $seconds += $hours * 3600; - } - unset($temp); - $duration_parent = $seconds; - } - } - - // HASHES - if ($hashes_iterator = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'hash')) - { - foreach ($hashes_iterator as $hash) - { - $value = null; - $algo = null; - if (isset($hash['data'])) - { - $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($hash['attribs']['']['algo'])) - { - $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $algo = 'md5'; - } - $hashes_parent[] = $algo.':'.$value; - } - } - elseif ($hashes_iterator = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'hash')) - { - foreach ($hashes_iterator as $hash) - { - $value = null; - $algo = null; - if (isset($hash['data'])) - { - $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($hash['attribs']['']['algo'])) - { - $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $algo = 'md5'; - } - $hashes_parent[] = $algo.':'.$value; - } - } - if (is_array($hashes_parent)) - { - $hashes_parent = array_values(SimplePie_Misc::array_unique($hashes_parent)); - } - - // KEYWORDS - if ($keywords = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'keywords')) - { - if (isset($keywords[0]['data'])) - { - $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); - foreach ($temp as $word) - { - $keywords_parent[] = trim($word); - } - } - unset($temp); - } - elseif ($keywords = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'keywords')) - { - if (isset($keywords[0]['data'])) - { - $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); - foreach ($temp as $word) - { - $keywords_parent[] = trim($word); - } - } - unset($temp); - } - elseif ($keywords = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'keywords')) - { - if (isset($keywords[0]['data'])) - { - $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); - foreach ($temp as $word) - { - $keywords_parent[] = trim($word); - } - } - unset($temp); - } - elseif ($keywords = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'keywords')) - { - if (isset($keywords[0]['data'])) - { - $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); - foreach ($temp as $word) - { - $keywords_parent[] = trim($word); - } - } - unset($temp); - } - if (is_array($keywords_parent)) - { - $keywords_parent = array_values(SimplePie_Misc::array_unique($keywords_parent)); - } - - // PLAYER - if ($player_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'player')) - { - if (isset($player_parent[0]['attribs']['']['url'])) - { - $player_parent = $this->sanitize($player_parent[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); - } - } - elseif ($player_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'player')) - { - if (isset($player_parent[0]['attribs']['']['url'])) - { - $player_parent = $this->sanitize($player_parent[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); - } - } - - // RATINGS - if ($ratings = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'rating')) - { - foreach ($ratings as $rating) - { - $rating_scheme = null; - $rating_value = null; - if (isset($rating['attribs']['']['scheme'])) - { - $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $rating_scheme = 'urn:simple'; - } - if (isset($rating['data'])) - { - $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $ratings_parent[] =& new $this->feed->rating_class($rating_scheme, $rating_value); - } - } - elseif ($ratings = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'explicit')) - { - foreach ($ratings as $rating) - { - $rating_scheme = 'urn:itunes'; - $rating_value = null; - if (isset($rating['data'])) - { - $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $ratings_parent[] =& new $this->feed->rating_class($rating_scheme, $rating_value); - } - } - elseif ($ratings = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'rating')) - { - foreach ($ratings as $rating) - { - $rating_scheme = null; - $rating_value = null; - if (isset($rating['attribs']['']['scheme'])) - { - $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $rating_scheme = 'urn:simple'; - } - if (isset($rating['data'])) - { - $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $ratings_parent[] =& new $this->feed->rating_class($rating_scheme, $rating_value); - } - } - elseif ($ratings = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'explicit')) - { - foreach ($ratings as $rating) - { - $rating_scheme = 'urn:itunes'; - $rating_value = null; - if (isset($rating['data'])) - { - $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $ratings_parent[] =& new $this->feed->rating_class($rating_scheme, $rating_value); - } - } - if (is_array($ratings_parent)) - { - $ratings_parent = array_values(SimplePie_Misc::array_unique($ratings_parent)); - } - - // RESTRICTIONS - if ($restrictions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'restriction')) - { - foreach ($restrictions as $restriction) - { - $restriction_relationship = null; - $restriction_type = null; - $restriction_value = null; - if (isset($restriction['attribs']['']['relationship'])) - { - $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($restriction['attribs']['']['type'])) - { - $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($restriction['data'])) - { - $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $restrictions_parent[] =& new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value); - } - } - elseif ($restrictions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'block')) - { - foreach ($restrictions as $restriction) - { - $restriction_relationship = 'allow'; - $restriction_type = null; - $restriction_value = 'itunes'; - if (isset($restriction['data']) && strtolower($restriction['data']) == 'yes') - { - $restriction_relationship = 'deny'; - } - $restrictions_parent[] =& new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value); - } - } - elseif ($restrictions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'restriction')) - { - foreach ($restrictions as $restriction) - { - $restriction_relationship = null; - $restriction_type = null; - $restriction_value = null; - if (isset($restriction['attribs']['']['relationship'])) - { - $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($restriction['attribs']['']['type'])) - { - $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($restriction['data'])) - { - $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $restrictions_parent[] =& new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value); - } - } - elseif ($restrictions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'block')) - { - foreach ($restrictions as $restriction) - { - $restriction_relationship = 'allow'; - $restriction_type = null; - $restriction_value = 'itunes'; - if (isset($restriction['data']) && strtolower($restriction['data']) == 'yes') - { - $restriction_relationship = 'deny'; - } - $restrictions_parent[] =& new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value); - } - } - if (is_array($restrictions_parent)) - { - $restrictions_parent = array_values(SimplePie_Misc::array_unique($restrictions_parent)); - } - - // THUMBNAILS - if ($thumbnails = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'thumbnail')) - { - foreach ($thumbnails as $thumbnail) - { - if (isset($thumbnail['attribs']['']['url'])) - { - $thumbnails_parent[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); - } - } - } - elseif ($thumbnails = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'thumbnail')) - { - foreach ($thumbnails as $thumbnail) - { - if (isset($thumbnail['attribs']['']['url'])) - { - $thumbnails_parent[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); - } - } - } - - // TITLES - if ($title_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'title')) - { - if (isset($title_parent[0]['data'])) - { - $title_parent = $this->sanitize($title_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - } - elseif ($title_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'title')) - { - if (isset($title_parent[0]['data'])) - { - $title_parent = $this->sanitize($title_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - } - - // Clear the memory - unset($parent); - - // Attributes - $bitrate = null; - $channels = null; - $duration = null; - $expression = null; - $framerate = null; - $height = null; - $javascript = null; - $lang = null; - $length = null; - $medium = null; - $samplingrate = null; - $type = null; - $url = null; - $width = null; - - // Elements - $captions = null; - $categories = null; - $copyrights = null; - $credits = null; - $description = null; - $hashes = null; - $keywords = null; - $player = null; - $ratings = null; - $restrictions = null; - $thumbnails = null; - $title = null; - - // If we have media:group tags, loop through them. - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'group') as $group) - { - // If we have media:content tags, loop through them. - foreach ((array) $group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'] as $content) - { - if (isset($content['attribs']['']['url'])) - { - // Attributes - $bitrate = null; - $channels = null; - $duration = null; - $expression = null; - $framerate = null; - $height = null; - $javascript = null; - $lang = null; - $length = null; - $medium = null; - $samplingrate = null; - $type = null; - $url = null; - $width = null; - - // Elements - $captions = null; - $categories = null; - $copyrights = null; - $credits = null; - $description = null; - $hashes = null; - $keywords = null; - $player = null; - $ratings = null; - $restrictions = null; - $thumbnails = null; - $title = null; - - // Start checking the attributes of media:content - if (isset($content['attribs']['']['bitrate'])) - { - $bitrate = $this->sanitize($content['attribs']['']['bitrate'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['channels'])) - { - $channels = $this->sanitize($content['attribs']['']['channels'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['duration'])) - { - $duration = $this->sanitize($content['attribs']['']['duration'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $duration = $duration_parent; - } - if (isset($content['attribs']['']['expression'])) - { - $expression = $this->sanitize($content['attribs']['']['expression'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['framerate'])) - { - $framerate = $this->sanitize($content['attribs']['']['framerate'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['height'])) - { - $height = $this->sanitize($content['attribs']['']['height'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['lang'])) - { - $lang = $this->sanitize($content['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['fileSize'])) - { - $length = ceil($content['attribs']['']['fileSize']); - } - if (isset($content['attribs']['']['medium'])) - { - $medium = $this->sanitize($content['attribs']['']['medium'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['samplingrate'])) - { - $samplingrate = $this->sanitize($content['attribs']['']['samplingrate'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['type'])) - { - $type = $this->sanitize($content['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['width'])) - { - $width = $this->sanitize($content['attribs']['']['width'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $url = $this->sanitize($content['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); - - // Checking the other optional media: elements. Priority: media:content, media:group, item, channel - - // CAPTIONS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'])) - { - foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption) - { - $caption_type = null; - $caption_lang = null; - $caption_startTime = null; - $caption_endTime = null; - $caption_text = null; - if (isset($caption['attribs']['']['type'])) - { - $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['lang'])) - { - $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['start'])) - { - $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['end'])) - { - $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['data'])) - { - $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $captions[] =& new $this->feed->caption_class($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text); - } - if (is_array($captions)) - { - $captions = array_values(SimplePie_Misc::array_unique($captions)); - } - } - elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'])) - { - foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption) - { - $caption_type = null; - $caption_lang = null; - $caption_startTime = null; - $caption_endTime = null; - $caption_text = null; - if (isset($caption['attribs']['']['type'])) - { - $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['lang'])) - { - $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['start'])) - { - $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['end'])) - { - $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['data'])) - { - $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $captions[] =& new $this->feed->caption_class($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text); - } - if (is_array($captions)) - { - $captions = array_values(SimplePie_Misc::array_unique($captions)); - } - } - else - { - $captions = $captions_parent; - } - - // CATEGORIES - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'])) - { - foreach ((array) $content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category) - { - $term = null; - $scheme = null; - $label = null; - if (isset($category['data'])) - { - $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($category['attribs']['']['scheme'])) - { - $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $scheme = 'http://search.yahoo.com/mrss/category_schema'; - } - if (isset($category['attribs']['']['label'])) - { - $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $categories[] =& new $this->feed->category_class($term, $scheme, $label); - } - } - if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'])) - { - foreach ((array) $group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category) - { - $term = null; - $scheme = null; - $label = null; - if (isset($category['data'])) - { - $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($category['attribs']['']['scheme'])) - { - $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $scheme = 'http://search.yahoo.com/mrss/category_schema'; - } - if (isset($category['attribs']['']['label'])) - { - $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $categories[] =& new $this->feed->category_class($term, $scheme, $label); - } - } - if (is_array($categories) && is_array($categories_parent)) - { - $categories = array_values(SimplePie_Misc::array_unique(array_merge($categories, $categories_parent))); - } - elseif (is_array($categories)) - { - $categories = array_values(SimplePie_Misc::array_unique($categories)); - } - elseif (is_array($categories_parent)) - { - $categories = array_values(SimplePie_Misc::array_unique($categories_parent)); - } - - // COPYRIGHTS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'])) - { - $copyright_url = null; - $copyright_label = null; - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'])) - { - $copyright_url = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'])) - { - $copyright_label = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $copyrights =& new $this->feed->copyright_class($copyright_url, $copyright_label); - } - elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'])) - { - $copyright_url = null; - $copyright_label = null; - if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'])) - { - $copyright_url = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'])) - { - $copyright_label = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $copyrights =& new $this->feed->copyright_class($copyright_url, $copyright_label); - } - else - { - $copyrights = $copyrights_parent; - } - - // CREDITS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'])) - { - foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit) - { - $credit_role = null; - $credit_scheme = null; - $credit_name = null; - if (isset($credit['attribs']['']['role'])) - { - $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($credit['attribs']['']['scheme'])) - { - $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $credit_scheme = 'urn:ebu'; - } - if (isset($credit['data'])) - { - $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $credits[] =& new $this->feed->credit_class($credit_role, $credit_scheme, $credit_name); - } - if (is_array($credits)) - { - $credits = array_values(SimplePie_Misc::array_unique($credits)); - } - } - elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'])) - { - foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit) - { - $credit_role = null; - $credit_scheme = null; - $credit_name = null; - if (isset($credit['attribs']['']['role'])) - { - $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($credit['attribs']['']['scheme'])) - { - $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $credit_scheme = 'urn:ebu'; - } - if (isset($credit['data'])) - { - $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $credits[] =& new $this->feed->credit_class($credit_role, $credit_scheme, $credit_name); - } - if (is_array($credits)) - { - $credits = array_values(SimplePie_Misc::array_unique($credits)); - } - } - else - { - $credits = $credits_parent; - } - - // DESCRIPTION - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'])) - { - $description = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'])) - { - $description = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $description = $description_parent; - } - - // HASHES - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'])) - { - foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash) - { - $value = null; - $algo = null; - if (isset($hash['data'])) - { - $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($hash['attribs']['']['algo'])) - { - $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $algo = 'md5'; - } - $hashes[] = $algo.':'.$value; - } - if (is_array($hashes)) - { - $hashes = array_values(SimplePie_Misc::array_unique($hashes)); - } - } - elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'])) - { - foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash) - { - $value = null; - $algo = null; - if (isset($hash['data'])) - { - $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($hash['attribs']['']['algo'])) - { - $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $algo = 'md5'; - } - $hashes[] = $algo.':'.$value; - } - if (is_array($hashes)) - { - $hashes = array_values(SimplePie_Misc::array_unique($hashes)); - } - } - else - { - $hashes = $hashes_parent; - } - - // KEYWORDS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'])) - { - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'])) - { - $temp = explode(',', $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); - foreach ($temp as $word) - { - $keywords[] = trim($word); - } - unset($temp); - } - if (is_array($keywords)) - { - $keywords = array_values(SimplePie_Misc::array_unique($keywords)); - } - } - elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'])) - { - if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'])) - { - $temp = explode(',', $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); - foreach ($temp as $word) - { - $keywords[] = trim($word); - } - unset($temp); - } - if (is_array($keywords)) - { - $keywords = array_values(SimplePie_Misc::array_unique($keywords)); - } - } - else - { - $keywords = $keywords_parent; - } - - // PLAYER - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'])) - { - $player = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); - } - elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'])) - { - $player = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); - } - else - { - $player = $player_parent; - } - - // RATINGS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'])) - { - foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating) - { - $rating_scheme = null; - $rating_value = null; - if (isset($rating['attribs']['']['scheme'])) - { - $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $rating_scheme = 'urn:simple'; - } - if (isset($rating['data'])) - { - $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $ratings[] =& new $this->feed->rating_class($rating_scheme, $rating_value); - } - if (is_array($ratings)) - { - $ratings = array_values(SimplePie_Misc::array_unique($ratings)); - } - } - elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'])) - { - foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating) - { - $rating_scheme = null; - $rating_value = null; - if (isset($rating['attribs']['']['scheme'])) - { - $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $rating_scheme = 'urn:simple'; - } - if (isset($rating['data'])) - { - $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $ratings[] =& new $this->feed->rating_class($rating_scheme, $rating_value); - } - if (is_array($ratings)) - { - $ratings = array_values(SimplePie_Misc::array_unique($ratings)); - } - } - else - { - $ratings = $ratings_parent; - } - - // RESTRICTIONS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'])) - { - foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction) - { - $restriction_relationship = null; - $restriction_type = null; - $restriction_value = null; - if (isset($restriction['attribs']['']['relationship'])) - { - $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($restriction['attribs']['']['type'])) - { - $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($restriction['data'])) - { - $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $restrictions[] =& new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value); - } - if (is_array($restrictions)) - { - $restrictions = array_values(SimplePie_Misc::array_unique($restrictions)); - } - } - elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'])) - { - foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction) - { - $restriction_relationship = null; - $restriction_type = null; - $restriction_value = null; - if (isset($restriction['attribs']['']['relationship'])) - { - $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($restriction['attribs']['']['type'])) - { - $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($restriction['data'])) - { - $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $restrictions[] =& new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value); - } - if (is_array($restrictions)) - { - $restrictions = array_values(SimplePie_Misc::array_unique($restrictions)); - } - } - else - { - $restrictions = $restrictions_parent; - } - - // THUMBNAILS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'])) - { - foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail) - { - $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); - } - if (is_array($thumbnails)) - { - $thumbnails = array_values(SimplePie_Misc::array_unique($thumbnails)); - } - } - elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'])) - { - foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail) - { - $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); - } - if (is_array($thumbnails)) - { - $thumbnails = array_values(SimplePie_Misc::array_unique($thumbnails)); - } - } - else - { - $thumbnails = $thumbnails_parent; - } - - // TITLES - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'])) - { - $title = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'])) - { - $title = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $title = $title_parent; - } - - $this->data['enclosures'][] =& new $this->feed->enclosure_class($url, $type, $length, $this->feed->javascript, $bitrate, $captions, $categories, $channels, $copyrights, $credits, $description, $duration, $expression, $framerate, $hashes, $height, $keywords, $lang, $medium, $player, $ratings, $restrictions, $samplingrate, $thumbnails, $title, $width); - } - } - } - - // If we have standalone media:content tags, loop through them. - if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'])) - { - foreach ((array) $this->data['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'] as $content) - { - if (isset($content['attribs']['']['url'])) - { - // Attributes - $bitrate = null; - $channels = null; - $duration = null; - $expression = null; - $framerate = null; - $height = null; - $javascript = null; - $lang = null; - $length = null; - $medium = null; - $samplingrate = null; - $type = null; - $url = null; - $width = null; - - // Elements - $captions = null; - $categories = null; - $copyrights = null; - $credits = null; - $description = null; - $hashes = null; - $keywords = null; - $player = null; - $ratings = null; - $restrictions = null; - $thumbnails = null; - $title = null; - - // Start checking the attributes of media:content - if (isset($content['attribs']['']['bitrate'])) - { - $bitrate = $this->sanitize($content['attribs']['']['bitrate'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['channels'])) - { - $channels = $this->sanitize($content['attribs']['']['channels'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['duration'])) - { - $duration = $this->sanitize($content['attribs']['']['duration'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $duration = $duration_parent; - } - if (isset($content['attribs']['']['expression'])) - { - $expression = $this->sanitize($content['attribs']['']['expression'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['framerate'])) - { - $framerate = $this->sanitize($content['attribs']['']['framerate'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['height'])) - { - $height = $this->sanitize($content['attribs']['']['height'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['lang'])) - { - $lang = $this->sanitize($content['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['fileSize'])) - { - $length = ceil($content['attribs']['']['fileSize']); - } - if (isset($content['attribs']['']['medium'])) - { - $medium = $this->sanitize($content['attribs']['']['medium'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['samplingrate'])) - { - $samplingrate = $this->sanitize($content['attribs']['']['samplingrate'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['type'])) - { - $type = $this->sanitize($content['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['width'])) - { - $width = $this->sanitize($content['attribs']['']['width'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $url = $this->sanitize($content['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); - - // Checking the other optional media: elements. Priority: media:content, media:group, item, channel - - // CAPTIONS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'])) - { - foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption) - { - $caption_type = null; - $caption_lang = null; - $caption_startTime = null; - $caption_endTime = null; - $caption_text = null; - if (isset($caption['attribs']['']['type'])) - { - $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['lang'])) - { - $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['start'])) - { - $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['end'])) - { - $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['data'])) - { - $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $captions[] =& new $this->feed->caption_class($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text); - } - if (is_array($captions)) - { - $captions = array_values(SimplePie_Misc::array_unique($captions)); - } - } - else - { - $captions = $captions_parent; - } - - // CATEGORIES - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'])) - { - foreach ((array) $content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category) - { - $term = null; - $scheme = null; - $label = null; - if (isset($category['data'])) - { - $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($category['attribs']['']['scheme'])) - { - $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $scheme = 'http://search.yahoo.com/mrss/category_schema'; - } - if (isset($category['attribs']['']['label'])) - { - $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $categories[] =& new $this->feed->category_class($term, $scheme, $label); - } - } - if (is_array($categories) && is_array($categories_parent)) - { - $categories = array_values(SimplePie_Misc::array_unique(array_merge($categories, $categories_parent))); - } - elseif (is_array($categories)) - { - $categories = array_values(SimplePie_Misc::array_unique($categories)); - } - elseif (is_array($categories_parent)) - { - $categories = array_values(SimplePie_Misc::array_unique($categories_parent)); - } - else - { - $categories = null; - } - - // COPYRIGHTS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'])) - { - $copyright_url = null; - $copyright_label = null; - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'])) - { - $copyright_url = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'])) - { - $copyright_label = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $copyrights =& new $this->feed->copyright_class($copyright_url, $copyright_label); - } - else - { - $copyrights = $copyrights_parent; - } - - // CREDITS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'])) - { - foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit) - { - $credit_role = null; - $credit_scheme = null; - $credit_name = null; - if (isset($credit['attribs']['']['role'])) - { - $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($credit['attribs']['']['scheme'])) - { - $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $credit_scheme = 'urn:ebu'; - } - if (isset($credit['data'])) - { - $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $credits[] =& new $this->feed->credit_class($credit_role, $credit_scheme, $credit_name); - } - if (is_array($credits)) - { - $credits = array_values(SimplePie_Misc::array_unique($credits)); - } - } - else - { - $credits = $credits_parent; - } - - // DESCRIPTION - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'])) - { - $description = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $description = $description_parent; - } - - // HASHES - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'])) - { - foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash) - { - $value = null; - $algo = null; - if (isset($hash['data'])) - { - $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($hash['attribs']['']['algo'])) - { - $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $algo = 'md5'; - } - $hashes[] = $algo.':'.$value; - } - if (is_array($hashes)) - { - $hashes = array_values(SimplePie_Misc::array_unique($hashes)); - } - } - else - { - $hashes = $hashes_parent; - } - - // KEYWORDS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'])) - { - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'])) - { - $temp = explode(',', $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); - foreach ($temp as $word) - { - $keywords[] = trim($word); - } - unset($temp); - } - if (is_array($keywords)) - { - $keywords = array_values(SimplePie_Misc::array_unique($keywords)); - } - } - else - { - $keywords = $keywords_parent; - } - - // PLAYER - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'])) - { - $player = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); - } - else - { - $player = $player_parent; - } - - // RATINGS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'])) - { - foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating) - { - $rating_scheme = null; - $rating_value = null; - if (isset($rating['attribs']['']['scheme'])) - { - $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $rating_scheme = 'urn:simple'; - } - if (isset($rating['data'])) - { - $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $ratings[] =& new $this->feed->rating_class($rating_scheme, $rating_value); - } - if (is_array($ratings)) - { - $ratings = array_values(SimplePie_Misc::array_unique($ratings)); - } - } - else - { - $ratings = $ratings_parent; - } - - // RESTRICTIONS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'])) - { - foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction) - { - $restriction_relationship = null; - $restriction_type = null; - $restriction_value = null; - if (isset($restriction['attribs']['']['relationship'])) - { - $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($restriction['attribs']['']['type'])) - { - $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($restriction['data'])) - { - $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $restrictions[] =& new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value); - } - if (is_array($restrictions)) - { - $restrictions = array_values(SimplePie_Misc::array_unique($restrictions)); - } - } - else - { - $restrictions = $restrictions_parent; - } - - // THUMBNAILS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'])) - { - foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail) - { - $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); - } - if (is_array($thumbnails)) - { - $thumbnails = array_values(SimplePie_Misc::array_unique($thumbnails)); - } - } - else - { - $thumbnails = $thumbnails_parent; - } - - // TITLES - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'])) - { - $title = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $title = $title_parent; - } - - $this->data['enclosures'][] =& new $this->feed->enclosure_class($url, $type, $length, $this->feed->javascript, $bitrate, $captions, $categories, $channels, $copyrights, $credits, $description, $duration, $expression, $framerate, $hashes, $height, $keywords, $lang, $medium, $player, $ratings, $restrictions, $samplingrate, $thumbnails, $title, $width); - } - } - } - - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link') as $link) - { - if (isset($link['attribs']['']['href']) && !empty($link['attribs']['']['rel']) && $link['attribs']['']['rel'] == 'enclosure') - { - // Attributes - $bitrate = null; - $channels = null; - $duration = null; - $expression = null; - $framerate = null; - $height = null; - $javascript = null; - $lang = null; - $length = null; - $medium = null; - $samplingrate = null; - $type = null; - $url = null; - $width = null; - - $url = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); - if (isset($link['attribs']['']['type'])) - { - $type = $this->sanitize($link['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($link['attribs']['']['length'])) - { - $length = ceil($link['attribs']['']['length']); - } - - // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor - $this->data['enclosures'][] =& new $this->feed->enclosure_class($url, $type, $length, $this->feed->javascript, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width); - } - } - - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link') as $link) - { - if (isset($link['attribs']['']['href']) && !empty($link['attribs']['']['rel']) && $link['attribs']['']['rel'] == 'enclosure') - { - // Attributes - $bitrate = null; - $channels = null; - $duration = null; - $expression = null; - $framerate = null; - $height = null; - $javascript = null; - $lang = null; - $length = null; - $medium = null; - $samplingrate = null; - $type = null; - $url = null; - $width = null; - - $url = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); - if (isset($link['attribs']['']['type'])) - { - $type = $this->sanitize($link['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($link['attribs']['']['length'])) - { - $length = ceil($link['attribs']['']['length']); - } - - // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor - $this->data['enclosures'][] =& new $this->feed->enclosure_class($url, $type, $length, $this->feed->javascript, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width); - } - } - - if ($enclosure = $this->get_item_tags('', 'enclosure')) - { - if (isset($enclosure[0]['attribs']['']['url'])) - { - // Attributes - $bitrate = null; - $channels = null; - $duration = null; - $expression = null; - $framerate = null; - $height = null; - $javascript = null; - $lang = null; - $length = null; - $medium = null; - $samplingrate = null; - $type = null; - $url = null; - $width = null; - - $url = $this->sanitize($enclosure[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($enclosure[0])); - if (isset($enclosure[0]['attribs']['']['type'])) - { - $type = $this->sanitize($enclosure[0]['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($enclosure[0]['attribs']['']['length'])) - { - $length = ceil($enclosure[0]['attribs']['']['length']); - } - - // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor - $this->data['enclosures'][] =& new $this->feed->enclosure_class($url, $type, $length, $this->feed->javascript, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width); - } - } - - if (sizeof($this->data['enclosures']) == 0 && ($url || $type || $length || $bitrate || $captions_parent || $categories_parent || $channels || $copyrights_parent || $credits_parent || $description_parent || $duration_parent || $expression || $framerate || $hashes_parent || $height || $keywords_parent || $lang || $medium || $player_parent || $ratings_parent || $restrictions_parent || $samplingrate || $thumbnails_parent || $title_parent || $width)) - { - // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor - $this->data['enclosures'][] =& new $this->feed->enclosure_class($url, $type, $length, $this->feed->javascript, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width); - } - - $this->data['enclosures'] = array_values(SimplePie_Misc::array_unique($this->data['enclosures'])); - } - if (!empty($this->data['enclosures'])) - { - return $this->data['enclosures']; - } - else - { - return null; - } - } - - function get_latitude() - { - if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat')) - { - return (float) $return[0]['data']; - } - elseif (($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', $return[0]['data'], $match)) - { - return (float) $match[1]; - } - else - { - return null; - } - } - - function get_longitude() - { - if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long')) - { - return (float) $return[0]['data']; - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon')) - { - return (float) $return[0]['data']; - } - elseif (($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', $return[0]['data'], $match)) - { - return (float) $match[2]; - } - else - { - return null; - } - } - - function get_source() - { - if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'source')) - { - return new $this->feed->source_class($this, $return[0]); - } - else - { - return null; - } - } - - /** - * Creates the add_to_* methods' return data - * - * @access private - * @param string $item_url String to prefix to the item permalink - * @param string $title_url String to prefix to the item title - * (and suffix to the item permalink) - * @return mixed URL if feed exists, false otherwise - */ - function add_to_service($item_url, $title_url = null, $summary_url = null) - { - if ($this->get_permalink() !== null) - { - $return = $this->sanitize($item_url, SIMPLEPIE_CONSTRUCT_IRI) . rawurlencode($this->get_permalink()); - if ($title_url !== null && $this->get_title() !== null) - { - $return .= $this->sanitize($title_url, SIMPLEPIE_CONSTRUCT_IRI) . rawurlencode($this->get_title()); - } - if ($summary_url !== null && $this->get_description() !== null) - { - $return .= $this->sanitize($summary_url, SIMPLEPIE_CONSTRUCT_IRI) . rawurlencode($this->get_description()); - } - return $return; - } - else - { - return null; - } - } - - function add_to_blinklist() - { - return $this->add_to_service('http://www.blinklist.com/index.php?Action=Blink/addblink.php&Description=&Url=', '&Title='); - } - - function add_to_blogmarks() - { - return $this->add_to_service('http://blogmarks.net/my/new.php?mini=1&simple=1&url=', '&title='); - } - - function add_to_delicious() - { - return $this->add_to_service('http://del.icio.us/post/?v=4&url=', '&title='); - } - - function add_to_digg() - { - return $this->add_to_service('http://digg.com/submit?url=', '&title=', '&bodytext='); - } - - function add_to_furl() - { - return $this->add_to_service('http://www.furl.net/storeIt.jsp?u=', '&t='); - } - - function add_to_magnolia() - { - return $this->add_to_service('http://ma.gnolia.com/bookmarklet/add?url=', '&title='); - } - - function add_to_myweb20() - { - return $this->add_to_service('http://myweb2.search.yahoo.com/myresults/bookmarklet?u=', '&t='); - } - - function add_to_newsvine() - { - return $this->add_to_service('http://www.newsvine.com/_wine/save?u=', '&h='); - } - - function add_to_reddit() - { - return $this->add_to_service('http://reddit.com/submit?url=', '&title='); - } - - function add_to_segnalo() - { - return $this->add_to_service('http://segnalo.com/post.html.php?url=', '&title='); - } - - function add_to_simpy() - { - return $this->add_to_service('http://www.simpy.com/simpy/LinkAdd.do?href=', '&title='); - } - - function add_to_spurl() - { - return $this->add_to_service('http://www.spurl.net/spurl.php?v=3&url=', '&title='); - } - - function add_to_wists() - { - return $this->add_to_service('http://wists.com/r.php?c=&r=', '&title='); - } - - function search_technorati() - { - return $this->add_to_service('http://www.technorati.com/search/'); - } -} - -class SimplePie_Source -{ - var $item; - var $data = array(); - - function SimplePie_Source($item, $data) - { - $this->item = $item; - $this->data = $data; - } - - function __toString() - { - return md5(serialize($this->data)); - } - - /** - * Remove items that link back to this before destroying this object - */ - function __destruct() - { - unset($this->item); - } - - function get_source_tags($namespace, $tag) - { - if (isset($this->data['child'][$namespace][$tag])) - { - return $this->data['child'][$namespace][$tag]; - } - else - { - return null; - } - } - - function get_base($element = array()) - { - return $this->item->get_base($element); - } - - function sanitize($data, $type, $base = '') - { - return $this->item->sanitize($data, $type, $base); - } - - function get_item() - { - return $this->item; - } - - function get_title() - { - if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_source_tags('', 'title')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - return null; - } - } - - function get_category($key = 0) - { - $categories = $this->get_categories(); - if (isset($categories[$key])) - { - return $categories[$key]; - } - else - { - return null; - } - } - - function get_categories() - { - $categories = array(); - - foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category) - { - $term = null; - $scheme = null; - $label = null; - if (isset($category['attribs']['']['term'])) - { - $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($category['attribs']['']['scheme'])) - { - $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($category['attribs']['']['label'])) - { - $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $categories[] =& new $this->item->feed->category_class($term, $scheme, $label); - } - foreach ((array) $this->get_source_tags('', 'category') as $category) - { - $categories[] =& new $this->item->feed->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); - } - foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category) - { - $categories[] =& new $this->item->feed->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); - } - foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category) - { - $categories[] =& new $this->item->feed->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); - } - - if (!empty($categories)) - { - return SimplePie_Misc::array_unique($categories); - } - else - { - return null; - } - } - - function get_author($key = 0) - { - $authors = $this->get_authors(); - if (isset($authors[$key])) - { - return $authors[$key]; - } - else - { - return null; - } - } - - function get_authors() - { - $authors = array(); - foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author) - { - $name = null; - $uri = null; - $email = null; - if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) - { - $name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) - { - $uri = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); - } - if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) - { - $email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if ($name !== null || $email !== null || $uri !== null) - { - $authors[] =& new $this->item->feed->author_class($name, $uri, $email); - } - } - if ($author = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author')) - { - $name = null; - $url = null; - $email = null; - if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) - { - $name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) - { - $url = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); - } - if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) - { - $email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if ($name !== null || $email !== null || $url !== null) - { - $authors[] =& new $this->item->feed->author_class($name, $url, $email); - } - } - foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author) - { - $authors[] =& new $this->item->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); - } - foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author) - { - $authors[] =& new $this->item->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); - } - foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author) - { - $authors[] =& new $this->item->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); - } - - if (!empty($authors)) - { - return SimplePie_Misc::array_unique($authors); - } - else - { - return null; - } - } - - function get_contributor($key = 0) - { - $contributors = $this->get_contributors(); - if (isset($contributors[$key])) - { - return $contributors[$key]; - } - else - { - return null; - } - } - - function get_contributors() - { - $contributors = array(); - foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor) - { - $name = null; - $uri = null; - $email = null; - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) - { - $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) - { - $uri = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); - } - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) - { - $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if ($name !== null || $email !== null || $uri !== null) - { - $contributors[] =& new $this->item->feed->author_class($name, $uri, $email); - } - } - foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor) - { - $name = null; - $url = null; - $email = null; - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) - { - $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) - { - $url = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); - } - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) - { - $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if ($name !== null || $email !== null || $url !== null) - { - $contributors[] =& new $this->item->feed->author_class($name, $url, $email); - } - } - - if (!empty($contributors)) - { - return SimplePie_Misc::array_unique($contributors); - } - else - { - return null; - } - } - - function get_link($key = 0, $rel = 'alternate') - { - $links = $this->get_links($rel); - if (isset($links[$key])) - { - return $links[$key]; - } - else - { - return null; - } - } - - /** - * Added for parity between the parent-level and the item/entry-level. - */ - function get_permalink() - { - return $this->get_link(0); - } - - function get_links($rel = 'alternate') - { - if (!isset($this->data['links'])) - { - $this->data['links'] = array(); - if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link')) - { - foreach ($links as $link) - { - if (isset($link['attribs']['']['href'])) - { - $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; - $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); - } - } - } - if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link')) - { - foreach ($links as $link) - { - if (isset($link['attribs']['']['href'])) - { - $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; - $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); - - } - } - } - if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link')) - { - $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); - } - if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link')) - { - $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); - } - if ($links = $this->get_source_tags('', 'link')) - { - $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); - } - - $keys = array_keys($this->data['links']); - foreach ($keys as $key) - { - if (SimplePie_Misc::is_isegment_nz_nc($key)) - { - if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key])) - { - $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]); - $this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]; - } - else - { - $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key]; - } - } - elseif (substr($key, 0, 41) == SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY) - { - $this->data['links'][substr($key, 41)] =& $this->data['links'][$key]; - } - $this->data['links'][$key] = array_unique($this->data['links'][$key]); - } - } - - if (isset($this->data['links'][$rel])) - { - return $this->data['links'][$rel]; - } - else - { - return null; - } - } - - function get_description() - { - if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'subtitle')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'tagline')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_source_tags('', 'description')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); - } - else - { - return null; - } - } - - function get_copyright() - { - if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'copyright')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_source_tags('', 'copyright')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - return null; - } - } - - function get_language() - { - if ($return = $this->get_source_tags('', 'language')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'language')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'language')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif (isset($this->data['xml_lang'])) - { - return $this->sanitize($this->data['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - return null; - } - } - - function get_latitude() - { - if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat')) - { - return (float) $return[0]['data']; - } - elseif (($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', $return[0]['data'], $match)) - { - return (float) $match[1]; - } - else - { - return null; - } - } - - function get_longitude() - { - if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long')) - { - return (float) $return[0]['data']; - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon')) - { - return (float) $return[0]['data']; - } - elseif (($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', $return[0]['data'], $match)) - { - return (float) $match[2]; - } - else - { - return null; - } - } - - function get_image_url() - { - if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'image')) - { - return $this->sanitize($return[0]['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'logo')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'icon')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); - } - else - { - return null; - } - } -} - -class SimplePie_Author -{ - var $name; - var $link; - var $email; - - // Constructor, used to input the data - function SimplePie_Author($name = null, $link = null, $email = null) - { - $this->name = $name; - $this->link = $link; - $this->email = $email; - } - - function __toString() - { - // There is no $this->data here - return md5(serialize($this)); - } - - function get_name() - { - if ($this->name !== null) - { - return $this->name; - } - else - { - return null; - } - } - - function get_link() - { - if ($this->link !== null) - { - return $this->link; - } - else - { - return null; - } - } - - function get_email() - { - if ($this->email !== null) - { - return $this->email; - } - else - { - return null; - } - } -} - -class SimplePie_Category -{ - var $term; - var $scheme; - var $label; - - // Constructor, used to input the data - function SimplePie_Category($term = null, $scheme = null, $label = null) - { - $this->term = $term; - $this->scheme = $scheme; - $this->label = $label; - } - - function __toString() - { - // There is no $this->data here - return md5(serialize($this)); - } - - function get_term() - { - if ($this->term !== null) - { - return $this->term; - } - else - { - return null; - } - } - - function get_scheme() - { - if ($this->scheme !== null) - { - return $this->scheme; - } - else - { - return null; - } - } - - function get_label() - { - if ($this->label !== null) - { - return $this->label; - } - else - { - return $this->get_term(); - } - } -} - -class SimplePie_Enclosure -{ - var $bitrate; - var $captions; - var $categories; - var $channels; - var $copyright; - var $credits; - var $description; - var $duration; - var $expression; - var $framerate; - var $handler; - var $hashes; - var $height; - var $javascript; - var $keywords; - var $lang; - var $length; - var $link; - var $medium; - var $player; - var $ratings; - var $restrictions; - var $samplingrate; - var $thumbnails; - var $title; - var $type; - var $width; - - // Constructor, used to input the data - function SimplePie_Enclosure($link = null, $type = null, $length = null, $javascript = null, $bitrate = null, $captions = null, $categories = null, $channels = null, $copyright = null, $credits = null, $description = null, $duration = null, $expression = null, $framerate = null, $hashes = null, $height = null, $keywords = null, $lang = null, $medium = null, $player = null, $ratings = null, $restrictions = null, $samplingrate = null, $thumbnails = null, $title = null, $width = null) - { - $this->bitrate = $bitrate; - $this->captions = $captions; - $this->categories = $categories; - $this->channels = $channels; - $this->copyright = $copyright; - $this->credits = $credits; - $this->description = $description; - $this->duration = $duration; - $this->expression = $expression; - $this->framerate = $framerate; - $this->hashes = $hashes; - $this->height = $height; - $this->javascript = $javascript; - $this->keywords = $keywords; - $this->lang = $lang; - $this->length = $length; - $this->link = $link; - $this->medium = $medium; - $this->player = $player; - $this->ratings = $ratings; - $this->restrictions = $restrictions; - $this->samplingrate = $samplingrate; - $this->thumbnails = $thumbnails; - $this->title = $title; - $this->type = $type; - $this->width = $width; - if (class_exists('idna_convert')) - { - $idn =& new idna_convert; - $parsed = SimplePie_Misc::parse_url($link); - $this->link = SimplePie_Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], $parsed['fragment']); - } - $this->handler = $this->get_handler(); // Needs to load last - } - - function __toString() - { - // There is no $this->data here - return md5(serialize($this)); - } - - function get_bitrate() - { - if ($this->bitrate !== null) - { - return $this->bitrate; - } - else - { - return null; - } - } - - function get_caption($key = 0) - { - $captions = $this->get_captions(); - if (isset($captions[$key])) - { - return $captions[$key]; - } - else - { - return null; - } - } - - function get_captions() - { - if ($this->captions !== null) - { - return $this->captions; - } - else - { - return null; - } - } - - function get_category($key = 0) - { - $categories = $this->get_categories(); - if (isset($categories[$key])) - { - return $categories[$key]; - } - else - { - return null; - } - } - - function get_categories() - { - if ($this->categories !== null) - { - return $this->categories; - } - else - { - return null; - } - } - - function get_channels() - { - if ($this->channels !== null) - { - return $this->channels; - } - else - { - return null; - } - } - - function get_copyright() - { - if ($this->copyright !== null) - { - return $this->copyright; - } - else - { - return null; - } - } - - function get_credit($key = 0) - { - $credits = $this->get_credits(); - if (isset($credits[$key])) - { - return $credits[$key]; - } - else - { - return null; - } - } - - function get_credits() - { - if ($this->credits !== null) - { - return $this->credits; - } - else - { - return null; - } - } - - function get_description() - { - if ($this->description !== null) - { - return $this->description; - } - else - { - return null; - } - } - - function get_duration($convert = false) - { - if ($this->duration !== null) - { - if ($convert) - { - $time = SimplePie_Misc::time_hms($this->duration); - return $time; - } - else - { - return $this->duration; - } - } - else - { - return null; - } - } - - function get_expression() - { - if ($this->expression !== null) - { - return $this->expression; - } - else - { - return 'full'; - } - } - - function get_extension() - { - if ($this->link !== null) - { - $url = SimplePie_Misc::parse_url($this->link); - if ($url['path'] !== '') - { - return pathinfo($url['path'], PATHINFO_EXTENSION); - } - } - return null; - } - - function get_framerate() - { - if ($this->framerate !== null) - { - return $this->framerate; - } - else - { - return null; - } - } - - function get_handler() - { - return $this->get_real_type(true); - } - - function get_hash($key = 0) - { - $hashes = $this->get_hashes(); - if (isset($hashes[$key])) - { - return $hashes[$key]; - } - else - { - return null; - } - } - - function get_hashes() - { - if ($this->hashes !== null) - { - return $this->hashes; - } - else - { - return null; - } - } - - function get_height() - { - if ($this->height !== null) - { - return $this->height; - } - else - { - return null; - } - } - - function get_language() - { - if ($this->lang !== null) - { - return $this->lang; - } - else - { - return null; - } - } - - function get_keyword($key = 0) - { - $keywords = $this->get_keywords(); - if (isset($keywords[$key])) - { - return $keywords[$key]; - } - else - { - return null; - } - } - - function get_keywords() - { - if ($this->keywords !== null) - { - return $this->keywords; - } - else - { - return null; - } - } - - function get_length() - { - if ($this->length !== null) - { - return $this->length; - } - else - { - return null; - } - } - - function get_link() - { - if ($this->link !== null) - { - return urldecode($this->link); - } - else - { - return null; - } - } - - function get_medium() - { - if ($this->medium !== null) - { - return $this->medium; - } - else - { - return null; - } - } - - function get_player() - { - if ($this->player !== null) - { - return $this->player; - } - else - { - return null; - } - } - - function get_rating($key = 0) - { - $ratings = $this->get_ratings(); - if (isset($ratings[$key])) - { - return $ratings[$key]; - } - else - { - return null; - } - } - - function get_ratings() - { - if ($this->ratings !== null) - { - return $this->ratings; - } - else - { - return null; - } - } - - function get_restriction($key = 0) - { - $restrictions = $this->get_restrictions(); - if (isset($restrictions[$key])) - { - return $restrictions[$key]; - } - else - { - return null; - } - } - - function get_restrictions() - { - if ($this->restrictions !== null) - { - return $this->restrictions; - } - else - { - return null; - } - } - - function get_sampling_rate() - { - if ($this->samplingrate !== null) - { - return $this->samplingrate; - } - else - { - return null; - } - } - - function get_size() - { - $length = $this->get_length(); - if ($length !== null) - { - return round($length/1048576, 2); - } - else - { - return null; - } - } - - function get_thumbnail($key = 0) - { - $thumbnails = $this->get_thumbnails(); - if (isset($thumbnails[$key])) - { - return $thumbnails[$key]; - } - else - { - return null; - } - } - - function get_thumbnails() - { - if ($this->thumbnails !== null) - { - return $this->thumbnails; - } - else - { - return null; - } - } - - function get_title() - { - if ($this->title !== null) - { - return $this->title; - } - else - { - return null; - } - } - - function get_type() - { - if ($this->type !== null) - { - return $this->type; - } - else - { - return null; - } - } - - function get_width() - { - if ($this->width !== null) - { - return $this->width; - } - else - { - return null; - } - } - - function native_embed($options='') - { - return $this->embed($options, true); - } - - /** - * @todo If the dimensions for media:content are defined, use them when width/height are set to 'auto'. - */ - function embed($options = '', $native = false) - { - // Set up defaults - $audio = ''; - $video = ''; - $alt = ''; - $altclass = ''; - $loop = 'false'; - $width = 'auto'; - $height = 'auto'; - $bgcolor = '#ffffff'; - $mediaplayer = ''; - $widescreen = false; - $handler = $this->get_handler(); - $type = $this->get_real_type(); - - // Process options and reassign values as necessary - if (is_array($options)) - { - extract($options); - } - else - { - $options = explode(',', $options); - foreach($options as $option) - { - $opt = explode(':', $option, 2); - if (isset($opt[0], $opt[1])) - { - $opt[0] = trim($opt[0]); - $opt[1] = trim($opt[1]); - switch ($opt[0]) - { - case 'audio': - $audio = $opt[1]; - break; - - case 'video': - $video = $opt[1]; - break; - - case 'alt': - $alt = $opt[1]; - break; - - case 'altclass': - $altclass = $opt[1]; - break; - - case 'loop': - $loop = $opt[1]; - break; - - case 'width': - $width = $opt[1]; - break; - - case 'height': - $height = $opt[1]; - break; - - case 'bgcolor': - $bgcolor = $opt[1]; - break; - - case 'mediaplayer': - $mediaplayer = $opt[1]; - break; - - case 'widescreen': - $widescreen = $opt[1]; - break; - } - } - } - } - - $mime = explode('/', $type, 2); - $mime = $mime[0]; - - // Process values for 'auto' - if ($width == 'auto') - { - if ($mime == 'video') - { - if ($height == 'auto') - { - $width = 480; - } - elseif ($widescreen) - { - $width = round((intval($height)/9)*16); - } - else - { - $width = round((intval($height)/3)*4); - } - } - else - { - $width = '100%'; - } - } - - if ($height == 'auto') - { - if ($mime == 'audio') - { - $height = 0; - } - elseif ($mime == 'video') - { - if ($width == 'auto') - { - if ($widescreen) - { - $height = 270; - } - else - { - $height = 360; - } - } - elseif ($widescreen) - { - $height = round((intval($width)/16)*9); - } - else - { - $height = round((intval($width)/4)*3); - } - } - else - { - $height = 376; - } - } - elseif ($mime == 'audio') - { - $height = 0; - } - - // Set proper placeholder value - if ($mime == 'audio') - { - $placeholder = $audio; - } - elseif ($mime == 'video') - { - $placeholder = $video; - } - - $embed = ''; - - // Make sure the JS library is included - if (!$native) - { - static $javascript_outputted = null; - if (!$javascript_outputted && $this->javascript) - { - $embed .= ''; - $javascript_outputted = true; - } - } - - // Odeo Feed MP3's - if ($handler == 'odeo') - { - if ($native) - { - $embed .= ''; - } - else - { - $embed .= ''; - } - } - - // Flash - elseif ($handler == 'flash') - { - if ($native) - { - $embed .= "get_link() . "\" pluginspage=\"http://adobe.com/go/getflashplayer\" type=\"$type\" quality=\"high\" width=\"$width\" height=\"$height\" bgcolor=\"$bgcolor\" loop=\"$loop\">"; - } - else - { - $embed .= ""; - } - } - - // Flash Media Player file types. - // Preferred handler for MP3 file types. - elseif ($handler == 'fmedia' || ($handler == 'mp3' && $mediaplayer != '')) - { - $height += 20; - if ($native) - { - $embed .= "get_link().'?file_extension=.'.$this->get_extension()) . "&autostart=false&repeat=$loop&showdigits=true&showfsbutton=false\">"; - } - else - { - $embed .= ""; - } - } - - // QuickTime 7 file types. Need to test with QuickTime 6. - // Only handle MP3's if the Flash Media Player is not present. - elseif ($handler == 'quicktime' || ($handler == 'mp3' && $mediaplayer == '')) - { - $height += 16; - if ($native) - { - if ($placeholder != ""){ - $embed .= "get_link() . "\" src=\"$placeholder\" width=\"$width\" height=\"$height\" autoplay=\"false\" target=\"myself\" controller=\"false\" loop=\"$loop\" scale=\"aspect\" bgcolor=\"$bgcolor\" pluginspage=\"http://apple.com/quicktime/download/\">"; - } - else { - $embed .= "get_link() . "\" width=\"$width\" height=\"$height\" autoplay=\"false\" target=\"myself\" controller=\"true\" loop=\"$loop\" scale=\"aspect\" bgcolor=\"$bgcolor\" pluginspage=\"http://apple.com/quicktime/download/\">"; - } - } - else - { - $embed .= ""; - } - } - - // Windows Media - elseif ($handler == 'wmedia') - { - $height += 45; - if ($native) - { - $embed .= "get_link() . "\" autosize=\"1\" width=\"$width\" height=\"$height\" showcontrols=\"1\" showstatusbar=\"0\" showdisplay=\"0\" autostart=\"0\">"; - } - else - { - $embed .= ""; - } - } - - // Everything else - else $embed .= '' . $alt . ''; - - return $embed; - } - - function get_real_type($find_handler = false) - { - // If it's Odeo, let's get it out of the way. - if (substr(strtolower($this->get_link()), 0, 15) == 'http://odeo.com') - { - return 'odeo'; - } - - // Mime-types by handler. - $types_flash = array('application/x-shockwave-flash', 'application/futuresplash'); // Flash - $types_fmedia = array('video/flv', 'video/x-flv'); // Flash Media Player - $types_quicktime = array('audio/3gpp', 'audio/3gpp2', 'audio/aac', 'audio/x-aac', 'audio/aiff', 'audio/x-aiff', 'audio/mid', 'audio/midi', 'audio/x-midi', 'audio/mp4', 'audio/m4a', 'audio/x-m4a', 'audio/wav', 'audio/x-wav', 'video/3gpp', 'video/3gpp2', 'video/m4v', 'video/x-m4v', 'video/mp4', 'video/mpeg', 'video/x-mpeg', 'video/quicktime', 'video/sd-video'); // QuickTime - $types_wmedia = array('application/asx', 'application/x-mplayer2', 'audio/x-ms-wma', 'audio/x-ms-wax', 'video/x-ms-asf-plugin', 'video/x-ms-asf', 'video/x-ms-wm', 'video/x-ms-wmv', 'video/x-ms-wvx'); // Windows Media - $types_mp3 = array('audio/mp3', 'audio/x-mp3', 'audio/mpeg', 'audio/x-mpeg'); // MP3 - - if ($this->get_type() !== null) - { - $type = strtolower($this->type); - } - else - { - $type = null; - } - - // If we encounter an unsupported mime-type, check the file extension and guess intelligently. - if (!in_array($type, array_merge($types_flash, $types_fmedia, $types_quicktime, $types_wmedia, $types_mp3))) - { - switch (strtolower($this->get_extension())) - { - // Audio mime-types - case 'aac': - case 'adts': - $type = 'audio/acc'; - break; - - case 'aif': - case 'aifc': - case 'aiff': - case 'cdda': - $type = 'audio/aiff'; - break; - - case 'bwf': - $type = 'audio/wav'; - break; - - case 'kar': - case 'mid': - case 'midi': - case 'smf': - $type = 'audio/midi'; - break; - - case 'm4a': - $type = 'audio/x-m4a'; - break; - - case 'mp3': - case 'swa': - $type = 'audio/mp3'; - break; - - case 'wav': - $type = 'audio/wav'; - break; - - case 'wax': - $type = 'audio/x-ms-wax'; - break; - - case 'wma': - $type = 'audio/x-ms-wma'; - break; - - // Video mime-types - case '3gp': - case '3gpp': - $type = 'video/3gpp'; - break; - - case '3g2': - case '3gp2': - $type = 'video/3gpp2'; - break; - - case 'asf': - $type = 'video/x-ms-asf'; - break; - - case 'flv': - $type = 'video/x-flv'; - break; - - case 'm1a': - case 'm1s': - case 'm1v': - case 'm15': - case 'm75': - case 'mp2': - case 'mpa': - case 'mpeg': - case 'mpg': - case 'mpm': - case 'mpv': - $type = 'video/mpeg'; - break; - - case 'm4v': - $type = 'video/x-m4v'; - break; - - case 'mov': - case 'qt': - $type = 'video/quicktime'; - break; - - case 'mp4': - case 'mpg4': - $type = 'video/mp4'; - break; - - case 'sdv': - $type = 'video/sd-video'; - break; - - case 'wm': - $type = 'video/x-ms-wm'; - break; - - case 'wmv': - $type = 'video/x-ms-wmv'; - break; - - case 'wvx': - $type = 'video/x-ms-wvx'; - break; - - // Flash mime-types - case 'spl': - $type = 'application/futuresplash'; - break; - - case 'swf': - $type = 'application/x-shockwave-flash'; - break; - } - } - - if ($find_handler) - { - if (in_array($type, $types_flash)) - { - return 'flash'; - } - elseif (in_array($type, $types_fmedia)) - { - return 'fmedia'; - } - elseif (in_array($type, $types_quicktime)) - { - return 'quicktime'; - } - elseif (in_array($type, $types_wmedia)) - { - return 'wmedia'; - } - elseif (in_array($type, $types_mp3)) - { - return 'mp3'; - } - else - { - return null; - } - } - else - { - return $type; - } - } -} - -class SimplePie_Caption -{ - var $type; - var $lang; - var $startTime; - var $endTime; - var $text; - - // Constructor, used to input the data - function SimplePie_Caption($type = null, $lang = null, $startTime = null, $endTime = null, $text = null) - { - $this->type = $type; - $this->lang = $lang; - $this->startTime = $startTime; - $this->endTime = $endTime; - $this->text = $text; - } - - function __toString() - { - // There is no $this->data here - return md5(serialize($this)); - } - - function get_endtime() - { - if ($this->endTime !== null) - { - return $this->endTime; - } - else - { - return null; - } - } - - function get_language() - { - if ($this->lang !== null) - { - return $this->lang; - } - else - { - return null; - } - } - - function get_starttime() - { - if ($this->startTime !== null) - { - return $this->startTime; - } - else - { - return null; - } - } - - function get_text() - { - if ($this->text !== null) - { - return $this->text; - } - else - { - return null; - } - } - - function get_type() - { - if ($this->type !== null) - { - return $this->type; - } - else - { - return null; - } - } -} - -class SimplePie_Credit -{ - var $role; - var $scheme; - var $name; - - // Constructor, used to input the data - function SimplePie_Credit($role = null, $scheme = null, $name = null) - { - $this->role = $role; - $this->scheme = $scheme; - $this->name = $name; - } - - function __toString() - { - // There is no $this->data here - return md5(serialize($this)); - } - - function get_role() - { - if ($this->role !== null) - { - return $this->role; - } - else - { - return null; - } - } - - function get_scheme() - { - if ($this->scheme !== null) - { - return $this->scheme; - } - else - { - return null; - } - } - - function get_name() - { - if ($this->name !== null) - { - return $this->name; - } - else - { - return null; - } - } -} - -class SimplePie_Copyright -{ - var $url; - var $label; - - // Constructor, used to input the data - function SimplePie_Copyright($url = null, $label = null) - { - $this->url = $url; - $this->label = $label; - } - - function __toString() - { - // There is no $this->data here - return md5(serialize($this)); - } - - function get_url() - { - if ($this->url !== null) - { - return $this->url; - } - else - { - return null; - } - } - - function get_attribution() - { - if ($this->label !== null) - { - return $this->label; - } - else - { - return null; - } - } -} - -class SimplePie_Rating -{ - var $scheme; - var $value; - - // Constructor, used to input the data - function SimplePie_Rating($scheme = null, $value = null) - { - $this->scheme = $scheme; - $this->value = $value; - } - - function __toString() - { - // There is no $this->data here - return md5(serialize($this)); - } - - function get_scheme() - { - if ($this->scheme !== null) - { - return $this->scheme; - } - else - { - return null; - } - } - - function get_value() - { - if ($this->value !== null) - { - return $this->value; - } - else - { - return null; - } - } -} - -class SimplePie_Restriction -{ - var $relationship; - var $type; - var $value; - - // Constructor, used to input the data - function SimplePie_Restriction($relationship = null, $type = null, $value = null) - { - $this->relationship = $relationship; - $this->type = $type; - $this->value = $value; - } - - function __toString() - { - // There is no $this->data here - return md5(serialize($this)); - } - - function get_relationship() - { - if ($this->relationship !== null) - { - return $this->relationship; - } - else - { - return null; - } - } - - function get_type() - { - if ($this->type !== null) - { - return $this->type; - } - else - { - return null; - } - } - - function get_value() - { - if ($this->value !== null) - { - return $this->value; - } - else - { - return null; - } - } -} - -/** - * @todo Move to properly supporting RFC2616 (HTTP/1.1) - */ -class SimplePie_File -{ - var $url; - var $useragent; - var $success = true; - var $headers = array(); - var $body; - var $status_code; - var $redirects = 0; - var $error; - var $method = SIMPLEPIE_FILE_SOURCE_NONE; - - function SimplePie_File($url, $timeout = 10, $redirects = 5, $headers = null, $useragent = null, $force_fsockopen = false) - { - if (class_exists('idna_convert')) - { - $idn =& new idna_convert; - $parsed = SimplePie_Misc::parse_url($url); - $url = SimplePie_Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], $parsed['fragment']); - } - $this->url = $url; - $this->useragent = $useragent; - if (preg_match('/^http(s)?:\/\//i', $url)) - { - if ($useragent === null) - { - $useragent = ini_get('user_agent'); - $this->useragent = $useragent; - } - if (!is_array($headers)) - { - $headers = array(); - } - if (!$force_fsockopen && function_exists('curl_exec')) - { - $this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_CURL; - $fp = curl_init(); - $headers2 = array(); - foreach ($headers as $key => $value) - { - $headers2[] = "$key: $value"; - } - if (version_compare(SimplePie_Misc::get_curl_version(), '7.10.5', '>=')) - { - curl_setopt($fp, CURLOPT_ENCODING, ''); - } - curl_setopt($fp, CURLOPT_URL, $url); - curl_setopt($fp, CURLOPT_HEADER, 1); - curl_setopt($fp, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($fp, CURLOPT_TIMEOUT, $timeout); - curl_setopt($fp, CURLOPT_CONNECTTIMEOUT, $timeout); - curl_setopt($fp, CURLOPT_REFERER, $url); - curl_setopt($fp, CURLOPT_USERAGENT, $useragent); - curl_setopt($fp, CURLOPT_HTTPHEADER, $headers2); - if (!ini_get('open_basedir') && !ini_get('safe_mode') && version_compare(SimplePie_Misc::get_curl_version(), '7.15.2', '>=')) - { - curl_setopt($fp, CURLOPT_FOLLOWLOCATION, 1); - curl_setopt($fp, CURLOPT_MAXREDIRS, $redirects); - } - - $this->headers = curl_exec($fp); - if (curl_errno($fp) == 23 || curl_errno($fp) == 61) - { - curl_setopt($fp, CURLOPT_ENCODING, 'none'); - $this->headers = curl_exec($fp); - } - if (curl_errno($fp)) - { - $this->error = 'cURL error ' . curl_errno($fp) . ': ' . curl_error($fp); - $this->success = false; - } - else - { - $info = curl_getinfo($fp); - curl_close($fp); - $this->headers = explode("\r\n\r\n", $this->headers, $info['redirect_count'] + 1); - $this->headers = array_pop($this->headers); - $parser =& new SimplePie_HTTP_Parser($this->headers); - if ($parser->parse()) - { - $this->headers = $parser->headers; - $this->body = $parser->body; - $this->status_code = $parser->status_code; - if (($this->status_code == 300 || $this->status_code == 301 || $this->status_code == 302 || $this->status_code == 303 || $this->status_code == 307 || $this->status_code > 307 && $this->status_code < 400) && isset($this->headers['location']) && $this->redirects < $redirects) - { - $this->redirects++; - $location = SimplePie_Misc::absolutize_url($this->headers['location'], $url); - return $this->SimplePie_File($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen); - } - } - } - } - else - { - $this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_FSOCKOPEN; - $url_parts = parse_url($url); - if (isset($url_parts['scheme']) && strtolower($url_parts['scheme']) == 'https') - { - $url_parts['host'] = "ssl://$url_parts[host]"; - $url_parts['port'] = 443; - } - if (!isset($url_parts['port'])) - { - $url_parts['port'] = 80; - } - $fp = @fsockopen($url_parts['host'], $url_parts['port'], $errno, $errstr, $timeout); - if (!$fp) - { - $this->error = 'fsockopen error: ' . $errstr; - $this->success = false; - } - else - { - stream_set_timeout($fp, $timeout); - if (isset($url_parts['path'])) - { - if (isset($url_parts['query'])) - { - $get = "$url_parts[path]?$url_parts[query]"; - } - else - { - $get = $url_parts['path']; - } - } - else - { - $get = '/'; - } - $out = "GET $get HTTP/1.0\r\n"; - $out .= "Host: $url_parts[host]\r\n"; - $out .= "User-Agent: $useragent\r\n"; - if (function_exists('gzinflate')) - { - $out .= "Accept-Encoding: gzip,deflate\r\n"; - } - - if (isset($url_parts['user']) && isset($url_parts['pass'])) - { - $out .= "Authorization: Basic " . base64_encode("$url_parts[user]:$url_parts[pass]") . "\r\n"; - } - foreach ($headers as $key => $value) - { - $out .= "$key: $value\r\n"; - } - $out .= "Connection: Close\r\n\r\n"; - fwrite($fp, $out); - - $info = stream_get_meta_data($fp); - - $this->headers = ''; - while (!$info['eof'] && !$info['timed_out']) - { - $this->headers .= fread($fp, 1160); - $info = stream_get_meta_data($fp); - } - if (!$info['timed_out']) - { - $parser =& new SimplePie_HTTP_Parser($this->headers); - if ($parser->parse()) - { - $this->headers = $parser->headers; - $this->body = $parser->body; - $this->status_code = $parser->status_code; - if (($this->status_code == 300 || $this->status_code == 301 || $this->status_code == 302 || $this->status_code == 303 || $this->status_code == 307 || $this->status_code > 307 && $this->status_code < 400) && isset($this->headers['location']) && $this->redirects < $redirects) - { - $this->redirects++; - $location = SimplePie_Misc::absolutize_url($this->headers['location'], $url); - return $this->SimplePie_File($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen); - } - if (isset($this->headers['content-encoding']) && ($this->headers['content-encoding'] == 'gzip' || $this->headers['content-encoding'] == 'deflate')) - { - if (substr($this->body, 0, 8) == "\x1f\x8b\x08\x00\x00\x00\x00\x00") - { - $this->body = substr($this->body, 10); - } - $this->body = gzinflate($this->body); - } - } - } - else - { - $this->error = 'fsocket timed out'; - $this->success = false; - } - fclose($fp); - } - } - } - else - { - $this->method = SIMPLEPIE_FILE_SOURCE_LOCAL | SIMPLEPIE_FILE_SOURCE_FILE_GET_CONTENTS; - if (!$this->body = file_get_contents($url)) - { - $this->error = 'file_get_contents could not read the file'; - $this->success = false; - } - } - } -} - -/** - * HTTP Response Parser - * - * @package SimplePie - */ -class SimplePie_HTTP_Parser -{ - /** - * HTTP Version - * - * @access public - * @var float - */ - var $http_version = 0.0; - - /** - * Status code - * - * @access public - * @var int - */ - var $status_code = 0; - - /** - * Reason phrase - * - * @access public - * @var string - */ - var $reason = ''; - - /** - * Key/value pairs of the headers - * - * @access public - * @var array - */ - var $headers = array(); - - /** - * Body of the response - * - * @access public - * @var string - */ - var $body = ''; - - /** - * Current state of the state machine - * - * @access private - * @var string - */ - var $state = 'http_version'; - - /** - * Input data - * - * @access private - * @var string - */ - var $data = ''; - - /** - * Input data length (to avoid calling strlen() everytime this is needed) - * - * @access private - * @var int - */ - var $data_length = 0; - - /** - * Current position of the pointer - * - * @var int - * @access private - */ - var $position = 0; - - /** - * Name of the hedaer currently being parsed - * - * @access private - * @var string - */ - var $name = ''; - - /** - * Value of the hedaer currently being parsed - * - * @access private - * @var string - */ - var $value = ''; - - /** - * Create an instance of the class with the input data - * - * @access public - * @param string $data Input data - */ - function SimplePie_HTTP_Parser($data) - { - $this->data = $data; - $this->data_length = strlen($this->data); - } - - /** - * Parse the input data - * - * @access public - * @return bool true on success, false on failure - */ - function parse() - { - while ($this->state && $this->state !== 'emit' && $this->has_data()) - { - $state = $this->state; - $this->$state(); - } - $this->data = ''; - if ($this->state === 'emit' || $this->state === 'body') - { - return true; - } - else - { - $this->http_version = ''; - $this->status_code = ''; - $this->reason = ''; - $this->headers = array(); - $this->body = ''; - return false; - } - } - - /** - * Check whether there is data beyond the pointer - * - * @access private - * @return bool true if there is further data, false if not - */ - function has_data() - { - return (bool) ($this->position < $this->data_length); - } - - /** - * See if the next character is LWS - * - * @access private - * @return bool true if the next character is LWS, false if not - */ - function is_linear_whitespace() - { - return (bool) ($this->data[$this->position] === "\x09" - || $this->data[$this->position] === "\x20" - || ($this->data[$this->position] === "\x0A" - && isset($this->data[$this->position + 1]) - && ($this->data[$this->position + 1] === "\x09" || $this->data[$this->position + 1] === "\x20"))); - } - - /** - * Parse the HTTP version - * - * @access private - */ - function http_version() - { - if (strpos($this->data, "\x0A") !== false && strtoupper(substr($this->data, 0, 5)) === 'HTTP/') - { - $len = strspn($this->data, '0123456789.', 5); - $this->http_version = substr($this->data, 5, $len); - $this->position += 5 + $len; - if (substr_count($this->http_version, '.') <= 1) - { - $this->http_version = (float) $this->http_version; - $this->position += strspn($this->data, "\x09\x20", $this->position); - $this->state = 'status'; - } - else - { - $this->state = false; - } - } - else - { - $this->state = false; - } - } - - /** - * Parse the status code - * - * @access private - */ - function status() - { - if ($len = strspn($this->data, '0123456789', $this->position)) - { - $this->status_code = (int) substr($this->data, $this->position, $len); - $this->position += $len; - $this->state = 'reason'; - } - else - { - $this->state = false; - } - } - - /** - * Parse the reason phrase - * - * @access private - */ - function reason() - { - $len = strcspn($this->data, "\x0A", $this->position); - $this->reason = trim(substr($this->data, $this->position, $len), "\x09\x0D\x20"); - $this->position += $len + 1; - $this->state = 'new_line'; - } - - /** - * Deal with a new line, shifting data around as needed - * - * @access private - */ - function new_line() - { - $this->value = trim($this->value, "\x0D\x20"); - if ($this->name !== '' && $this->value !== '') - { - $this->name = strtolower($this->name); - if (isset($this->headers[$this->name])) - { - $this->headers[$this->name] .= ', ' . $this->value; - } - else - { - $this->headers[$this->name] = $this->value; - } - } - $this->name = ''; - $this->value = ''; - if (substr($this->data[$this->position], 0, 2) === "\x0D\x0A") - { - $this->position += 2; - $this->state = 'body'; - } - elseif ($this->data[$this->position] === "\x0A") - { - $this->position++; - $this->state = 'body'; - } - else - { - $this->state = 'name'; - } - } - - /** - * Parse a header name - * - * @access private - */ - function name() - { - $len = strcspn($this->data, "\x0A:", $this->position); - if (isset($this->data[$this->position + $len])) - { - if ($this->data[$this->position + $len] === "\x0A") - { - $this->position += $len; - $this->state = 'new_line'; - } - else - { - $this->name = substr($this->data, $this->position, $len); - $this->position += $len + 1; - $this->state = 'value'; - } - } - else - { - $this->state = false; - } - } - - /** - * Parse LWS, replacing consecutive LWS characters with a single space - * - * @access private - */ - function linear_whitespace() - { - do - { - if (substr($this->data, $this->position, 2) === "\x0D\x0A") - { - $this->position += 2; - } - elseif ($this->data[$this->position] === "\x0A") - { - $this->position++; - } - $this->position += strspn($this->data, "\x09\x20", $this->position); - } while ($this->has_data() && $this->is_linear_whitespace()); - $this->value .= "\x20"; - } - - /** - * See what state to move to while within non-quoted header values - * - * @access private - */ - function value() - { - if ($this->is_linear_whitespace()) - { - $this->linear_whitespace(); - } - else - { - switch ($this->data[$this->position]) - { - case '"': - $this->position++; - $this->state = 'quote'; - break; - - case "\x0A": - $this->position++; - $this->state = 'new_line'; - break; - - default: - $this->state = 'value_char'; - break; - } - } - } - - /** - * Parse a header value while outside quotes - * - * @access private - */ - function value_char() - { - $len = strcspn($this->data, "\x09\x20\x0A\"", $this->position); - $this->value .= substr($this->data, $this->position, $len); - $this->position += $len; - $this->state = 'value'; - } - - /** - * See what state to move to while within quoted header values - * - * @access private - */ - function quote() - { - if ($this->is_linear_whitespace()) - { - $this->linear_whitespace(); - } - else - { - switch ($this->data[$this->position]) - { - case '"': - $this->position++; - $this->state = 'value'; - break; - - case "\x0A": - $this->position++; - $this->state = 'new_line'; - break; - - case '\\': - $this->position++; - $this->state = 'quote_escaped'; - break; - - default: - $this->state = 'quote_char'; - break; - } - } - } - - /** - * Parse a header value while within quotes - * - * @access private - */ - function quote_char() - { - $len = strcspn($this->data, "\x09\x20\x0A\"\\", $this->position); - $this->value .= substr($this->data, $this->position, $len); - $this->position += $len; - $this->state = 'value'; - } - - /** - * Parse an escaped character within quotes - * - * @access private - */ - function quote_escaped() - { - $this->value .= $this->data[$this->position]; - $this->position++; - $this->state = 'quote'; - } - - /** - * Parse the body - * - * @access private - */ - function body() - { - $this->body = substr($this->data, $this->position); - $this->state = 'emit'; - } -} - -class SimplePie_Cache -{ - /** - * Don't call the constructor. Please. - * - * @access private - */ - function SimplePie_Cache() - { - trigger_error('Please call SimplePie_Cache::create() instead of the constructor', E_USER_ERROR); - } - - /** - * Create a new SimplePie_Cache object - * - * @static - * @access public - */ - function create($location, $filename, $extension) - { - return new SimplePie_Cache_File($location, $filename, $extension); - } -} - -class SimplePie_Cache_File -{ - var $location; - var $filename; - var $extension; - var $name; - - function SimplePie_Cache_File($location, $filename, $extension) - { - $this->location = $location; - $this->filename = rawurlencode($filename); - $this->extension = rawurlencode($extension); - $this->name = "$location/$this->filename.$this->extension"; - } - - function save($data) - { - if (file_exists($this->name) && is_writeable($this->name) || file_exists($this->location) && is_writeable($this->location)) - { - if (is_a($data, 'SimplePie')) - { - $data = $data->data; - } - - $data = serialize($data); - - if (function_exists('file_put_contents')) - { - return (bool) file_put_contents($this->name, $data); - } - else - { - $fp = fopen($this->name, 'wb'); - if ($fp) - { - fwrite($fp, $data); - fclose($fp); - return true; - } - } - } - return false; - } - - function load() - { - if (file_exists($this->name) && is_readable($this->name)) - { - return unserialize(file_get_contents($this->name)); - } - return false; - } - - function mtime() - { - if (file_exists($this->name)) - { - return filemtime($this->name); - } - return false; - } - - function touch() - { - if (file_exists($this->name)) - { - return touch($this->name); - } - return false; - } - - function unlink() - { - if (file_exists($this->name)) - { - return unlink($this->name); - } - return false; - } -} - -class SimplePie_Misc -{ - function time_hms($seconds) - { - $time = ''; - - $hours = floor($seconds / 3600); - $remainder = $seconds % 3600; - if ($hours > 0) - { - $time .= $hours.':'; - } - - $minutes = floor($remainder / 60); - $seconds = $remainder % 60; - if ($minutes < 10 && $hours > 0) - { - $minutes = '0' . $minutes; - } - if ($seconds < 10) - { - $seconds = '0' . $seconds; - } - - $time .= $minutes.':'; - $time .= $seconds; - - return $time; - } - - function absolutize_url($relative, $base) - { - if ($relative !== '') - { - $relative = SimplePie_Misc::parse_url($relative); - if ($relative['scheme'] !== '') - { - $target = $relative; - } - elseif ($base !== '') - { - $base = SimplePie_Misc::parse_url($base); - $target = SimplePie_Misc::parse_url(''); - if ($relative['authority'] !== '') - { - $target = $relative; - $target['scheme'] = $base['scheme']; - } - else - { - $target['scheme'] = $base['scheme']; - $target['authority'] = $base['authority']; - if ($relative['path'] !== '') - { - if (strpos($relative['path'], '/') === 0) - { - $target['path'] = $relative['path']; - } - elseif ($base['authority'] !== '' && $base['path'] === '') - { - $target['path'] = '/' . $relative['path']; - } - elseif (($last_segment = strrpos($base['path'], '/')) !== false) - { - $target['path'] = substr($base['path'], 0, $last_segment + 1) . $relative['path']; - } - else - { - $target['path'] = $relative['path']; - } - $target['query'] = $relative['query']; - } - else - { - $target['path'] = $base['path']; - if ($relative['query'] !== '') - { - $target['query'] = $relative['query']; - } - elseif ($base['query'] !== '') - { - $target['query'] = $base['query']; - } - } - } - $target['fragment'] = $relative['fragment']; - } - else - { - // No base URL, just return the relative URL - $target = $relative; - } - $return = SimplePie_Misc::compress_parse_url($target['scheme'], $target['authority'], $target['path'], $target['query'], $target['fragment']); - } - else - { - $return = $base; - } - $return = SimplePie_Misc::normalize_url($return); - return $return; - } - - function remove_dot_segments($input) - { - $output = ''; - while (strpos($input, './') !== false || strpos($input, '/.') !== false || $input == '.' || $input == '..') - { - // A: If the input buffer begins with a prefix of "../" or "./", then remove that prefix from the input buffer; otherwise, - if (strpos($input, '../') === 0) - { - $input = substr($input, 3); - } - elseif (strpos($input, './') === 0) - { - $input = substr($input, 2); - } - // B: if the input buffer begins with a prefix of "/./" or "/.", where "." is a complete path segment, then replace that prefix with "/" in the input buffer; otherwise, - elseif (strpos($input, '/./') === 0) - { - $input = substr_replace($input, '/', 0, 3); - } - elseif ($input == '/.') - { - $input = '/'; - } - // C: if the input buffer begins with a prefix of "/../" or "/..", where ".." is a complete path segment, then replace that prefix with "/" in the input buffer and remove the last segment and its preceding "/" (if any) from the output buffer; otherwise, - elseif (strpos($input, '/../') === 0) - { - $input = substr_replace($input, '/', 0, 4); - $output = substr_replace($output, '', strrpos($output, '/')); - } - elseif ($input == '/..') - { - $input = '/'; - $output = substr_replace($output, '', strrpos($output, '/')); - } - // D: if the input buffer consists only of "." or "..", then remove that from the input buffer; otherwise, - elseif ($input == '.' || $input == '..') - { - $input = ''; - } - // E: move the first path segment in the input buffer to the end of the output buffer, including the initial "/" character (if any) and any subsequent characters up to, but not including, the next "/" character or the end of the input buffer - elseif (($pos = strpos($input, '/', 1)) !== false) - { - $output .= substr($input, 0, $pos); - $input = substr_replace($input, '', 0, $pos); - } - else - { - $output .= $input; - $input = ''; - } - } - return $output . $input; - } - - function get_element($realname, $string) - { - $return = array(); - $name = preg_quote($realname, '/'); - if (preg_match_all("/<($name)" . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . "(>(.*)<\/$name>|(\/)?>)/siU", $string, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) - { - for ($i = 0, $total_matches = count($matches); $i < $total_matches; $i++) - { - $return[$i]['tag'] = $realname; - $return[$i]['full'] = $matches[$i][0][0]; - $return[$i]['offset'] = $matches[$i][0][1]; - if (strlen($matches[$i][3][0]) <= 2) - { - $return[$i]['self_closing'] = true; - } - else - { - $return[$i]['self_closing'] = false; - $return[$i]['content'] = $matches[$i][4][0]; - } - $return[$i]['attribs'] = array(); - if (isset($matches[$i][2][0]) && preg_match_all('/[\x09\x0A\x0B\x0C\x0D\x20]+([^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3D\x3E]*)(?:[\x09\x0A\x0B\x0C\x0D\x20]*=[\x09\x0A\x0B\x0C\x0D\x20]*(?:"([^"]*)"|\'([^\']*)\'|([^\x09\x0A\x0B\x0C\x0D\x20\x22\x27\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x3E]*)?))?/', ' ' . $matches[$i][2][0] . ' ', $attribs, PREG_SET_ORDER)) - { - for ($j = 0, $total_attribs = count($attribs); $j < $total_attribs; $j++) - { - if (count($attribs[$j]) == 2) - { - $attribs[$j][2] = $attribs[$j][1]; - } - $return[$i]['attribs'][strtolower($attribs[$j][1])]['data'] = SimplePie_Misc::entities_decode(end($attribs[$j]), 'UTF-8'); - } - } - } - } - return $return; - } - - function element_implode($element) - { - $full = "<$element[tag]"; - foreach ($element['attribs'] as $key => $value) - { - $key = strtolower($key); - $full .= " $key=\"" . htmlspecialchars($value['data']) . '"'; - } - if ($element['self_closing']) - { - $full .= ' />'; - } - else - { - $full .= ">$element[content]"; - } - return $full; - } - - function error($message, $level, $file, $line) - { - switch ($level) - { - case E_USER_ERROR: - $note = 'PHP Error'; - break; - case E_USER_WARNING: - $note = 'PHP Warning'; - break; - case E_USER_NOTICE: - $note = 'PHP Notice'; - break; - default: - $note = 'Unknown Error'; - break; - } - error_log("$note: $message in $file on line $line", 0); - return $message; - } - - /** - * If a file has been cached, retrieve and display it. - * - * This is most useful for caching images (get_favicon(), etc.), - * however it works for all cached files. This WILL NOT display ANY - * file/image/page/whatever, but rather only display what has already - * been cached by SimplePie. - * - * @access public - * @see SimplePie::get_favicon() - * @param str $identifier_url URL that is used to identify the content. - * This may or may not be the actual URL of the live content. - * @param str $cache_location Location of SimplePie's cache. Defaults - * to './cache'. - * @param str $cache_extension The file extension that the file was - * cached with. Defaults to 'spc'. - * @param str $cache_class Name of the cache-handling class being used - * in SimplePie. Defaults to 'SimplePie_Cache', and should be left - * as-is unless you've overloaded the class. - * @param str $cache_name_function Obsolete. Exists for backwards - * compatibility reasons only. - */ - function display_cached_file($identifier_url, $cache_location = './cache', $cache_extension = 'spc', $cache_class = 'SimplePie_Cache', $cache_name_function = 'md5') - { - $cache = call_user_func(array($cache_class, 'create'), $cache_location, $identifier_url, $cache_extension); - - if ($file = $cache->load()) - { - if (isset($file['headers']['content-type'])) - { - header('Content-type:' . $file['headers']['content-type']); - } - else - { - header('Content-type: application/octet-stream'); - } - header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 604800) . ' GMT'); // 7 days - echo $file['body']; - exit; - } - - die('Cached file for ' . $identifier_url . ' cannot be found.'); - } - - function fix_protocol($url, $http = 1) - { - $url = SimplePie_Misc::normalize_url($url); - $parsed = SimplePie_Misc::parse_url($url); - if ($parsed['scheme'] !== '' && $parsed['scheme'] != 'http' && $parsed['scheme'] != 'https') - { - return SimplePie_Misc::fix_protocol(SimplePie_Misc::compress_parse_url('http', $parsed['authority'], $parsed['path'], $parsed['query'], $parsed['fragment']), $http); - } - - if ($parsed['scheme'] === '' && $parsed['authority'] === '' && !file_exists($url)) - { - return SimplePie_Misc::fix_protocol(SimplePie_Misc::compress_parse_url('http', $parsed['path'], '', $parsed['query'], $parsed['fragment']), $http); - } - - if ($http == 2 && $parsed['scheme'] !== '') - { - return "feed:$url"; - } - elseif ($http == 3 && strtolower($parsed['scheme']) == 'http') - { - return substr_replace($url, 'podcast', 0, 4); - } - elseif ($http == 4 && strtolower($parsed['scheme']) == 'http') - { - return substr_replace($url, 'itpc', 0, 4); - } - else - { - return $url; - } - } - - function parse_url($url) - { - static $cache = array(); - if (isset($cache[$url])) - { - return $cache[$url]; - } - elseif (preg_match('/^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/', $url, $match)) - { - for ($i = count($match); $i <= 9; $i++) - { - $match[$i] = ''; - } - return $cache[$url] = array('scheme' => $match[2], 'authority' => $match[4], 'path' => $match[5], 'query' => $match[7], 'fragment' => $match[9]); - } - else - { - return $cache[$url] = array('scheme' => '', 'authority' => '', 'path' => '', 'query' => '', 'fragment' => ''); - } - } - - function compress_parse_url($scheme = '', $authority = '', $path = '', $query = '', $fragment = '') - { - $return = ''; - if ($scheme !== '') - { - $return .= "$scheme:"; - } - if ($authority !== '') - { - $return .= "//$authority"; - } - if ($path !== '') - { - $return .= $path; - } - if ($query !== '') - { - $return .= "?$query"; - } - if ($fragment !== '') - { - $return .= "#$fragment"; - } - return $return; - } - - function normalize_url($url) - { - $url = preg_replace_callback('/%([0-9A-Fa-f]{2})/', array('SimplePie_Misc', 'percent_encoding_normalization'), $url); - $url = SimplePie_Misc::parse_url($url); - $url['scheme'] = strtolower($url['scheme']); - if ($url['authority'] !== '') - { - $url['authority'] = strtolower($url['authority']); - $url['path'] = SimplePie_Misc::remove_dot_segments($url['path']); - } - return SimplePie_Misc::compress_parse_url($url['scheme'], $url['authority'], $url['path'], $url['query'], $url['fragment']); - } - - function percent_encoding_normalization($match) - { - $integer = hexdec($match[1]); - if ($integer >= 0x41 && $integer <= 0x5A || $integer >= 0x61 && $integer <= 0x7A || $integer >= 0x30 && $integer <= 0x39 || $integer == 0x2D || $integer == 0x2E || $integer == 0x5F || $integer == 0x7E) - { - return chr($integer); - } - else - { - return strtoupper($match[0]); - } - } - - /** - * Remove bad UTF-8 bytes - * - * PCRE Pattern to locate bad bytes in a UTF-8 string comes from W3C - * FAQ: Multilingual Forms (modified to include full ASCII range) - * - * @author Geoffrey Sneddon - * @see http://www.w3.org/International/questions/qa-forms-utf-8 - * @param string $str String to remove bad UTF-8 bytes from - * @return string UTF-8 string - */ - function utf8_bad_replace($str) - { - if (function_exists('iconv') && ($return = @iconv('UTF-8', 'UTF-8//IGNORE', $str))) - { - return $return; - } - elseif (function_exists('mb_convert_encoding') && ($return = @mb_convert_encoding($str, 'UTF-8', 'UTF-8'))) - { - return $return; - } - elseif (preg_match_all('/(?:[\x00-\x7F]|[\xC2-\xDF][\x80-\xBF]|\xE0[\xA0-\xBF][\x80-\xBF]|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}|\xED[\x80-\x9F][\x80-\xBF]|\xF0[\x90-\xBF][\x80-\xBF]{2}|[\xF1-\xF3][\x80-\xBF]{3}|\xF4[\x80-\x8F][\x80-\xBF]{2})+/', $str, $matches)) - { - return implode("\xEF\xBF\xBD", $matches[0]); - } - elseif ($str !== '') - { - return "\xEF\xBF\xBD"; - } - else - { - return ''; - } - } - - /** - * Converts a Windows-1252 encoded string to a UTF-8 encoded string - * - * @static - * @access public - * @param string $string Windows-1252 encoded string - * @return string UTF-8 encoded string - */ - function windows_1252_to_utf8($string) - { - static $convert_table = array("\x80" => "\xE2\x82\xAC", "\x81" => "\xEF\xBF\xBD", "\x82" => "\xE2\x80\x9A", "\x83" => "\xC6\x92", "\x84" => "\xE2\x80\x9E", "\x85" => "\xE2\x80\xA6", "\x86" => "\xE2\x80\xA0", "\x87" => "\xE2\x80\xA1", "\x88" => "\xCB\x86", "\x89" => "\xE2\x80\xB0", "\x8A" => "\xC5\xA0", "\x8B" => "\xE2\x80\xB9", "\x8C" => "\xC5\x92", "\x8D" => "\xEF\xBF\xBD", "\x8E" => "\xC5\xBD", "\x8F" => "\xEF\xBF\xBD", "\x90" => "\xEF\xBF\xBD", "\x91" => "\xE2\x80\x98", "\x92" => "\xE2\x80\x99", "\x93" => "\xE2\x80\x9C", "\x94" => "\xE2\x80\x9D", "\x95" => "\xE2\x80\xA2", "\x96" => "\xE2\x80\x93", "\x97" => "\xE2\x80\x94", "\x98" => "\xCB\x9C", "\x99" => "\xE2\x84\xA2", "\x9A" => "\xC5\xA1", "\x9B" => "\xE2\x80\xBA", "\x9C" => "\xC5\x93", "\x9D" => "\xEF\xBF\xBD", "\x9E" => "\xC5\xBE", "\x9F" => "\xC5\xB8", "\xA0" => "\xC2\xA0", "\xA1" => "\xC2\xA1", "\xA2" => "\xC2\xA2", "\xA3" => "\xC2\xA3", "\xA4" => "\xC2\xA4", "\xA5" => "\xC2\xA5", "\xA6" => "\xC2\xA6", "\xA7" => "\xC2\xA7", "\xA8" => "\xC2\xA8", "\xA9" => "\xC2\xA9", "\xAA" => "\xC2\xAA", "\xAB" => "\xC2\xAB", "\xAC" => "\xC2\xAC", "\xAD" => "\xC2\xAD", "\xAE" => "\xC2\xAE", "\xAF" => "\xC2\xAF", "\xB0" => "\xC2\xB0", "\xB1" => "\xC2\xB1", "\xB2" => "\xC2\xB2", "\xB3" => "\xC2\xB3", "\xB4" => "\xC2\xB4", "\xB5" => "\xC2\xB5", "\xB6" => "\xC2\xB6", "\xB7" => "\xC2\xB7", "\xB8" => "\xC2\xB8", "\xB9" => "\xC2\xB9", "\xBA" => "\xC2\xBA", "\xBB" => "\xC2\xBB", "\xBC" => "\xC2\xBC", "\xBD" => "\xC2\xBD", "\xBE" => "\xC2\xBE", "\xBF" => "\xC2\xBF", "\xC0" => "\xC3\x80", "\xC1" => "\xC3\x81", "\xC2" => "\xC3\x82", "\xC3" => "\xC3\x83", "\xC4" => "\xC3\x84", "\xC5" => "\xC3\x85", "\xC6" => "\xC3\x86", "\xC7" => "\xC3\x87", "\xC8" => "\xC3\x88", "\xC9" => "\xC3\x89", "\xCA" => "\xC3\x8A", "\xCB" => "\xC3\x8B", "\xCC" => "\xC3\x8C", "\xCD" => "\xC3\x8D", "\xCE" => "\xC3\x8E", "\xCF" => "\xC3\x8F", "\xD0" => "\xC3\x90", "\xD1" => "\xC3\x91", "\xD2" => "\xC3\x92", "\xD3" => "\xC3\x93", "\xD4" => "\xC3\x94", "\xD5" => "\xC3\x95", "\xD6" => "\xC3\x96", "\xD7" => "\xC3\x97", "\xD8" => "\xC3\x98", "\xD9" => "\xC3\x99", "\xDA" => "\xC3\x9A", "\xDB" => "\xC3\x9B", "\xDC" => "\xC3\x9C", "\xDD" => "\xC3\x9D", "\xDE" => "\xC3\x9E", "\xDF" => "\xC3\x9F", "\xE0" => "\xC3\xA0", "\xE1" => "\xC3\xA1", "\xE2" => "\xC3\xA2", "\xE3" => "\xC3\xA3", "\xE4" => "\xC3\xA4", "\xE5" => "\xC3\xA5", "\xE6" => "\xC3\xA6", "\xE7" => "\xC3\xA7", "\xE8" => "\xC3\xA8", "\xE9" => "\xC3\xA9", "\xEA" => "\xC3\xAA", "\xEB" => "\xC3\xAB", "\xEC" => "\xC3\xAC", "\xED" => "\xC3\xAD", "\xEE" => "\xC3\xAE", "\xEF" => "\xC3\xAF", "\xF0" => "\xC3\xB0", "\xF1" => "\xC3\xB1", "\xF2" => "\xC3\xB2", "\xF3" => "\xC3\xB3", "\xF4" => "\xC3\xB4", "\xF5" => "\xC3\xB5", "\xF6" => "\xC3\xB6", "\xF7" => "\xC3\xB7", "\xF8" => "\xC3\xB8", "\xF9" => "\xC3\xB9", "\xFA" => "\xC3\xBA", "\xFB" => "\xC3\xBB", "\xFC" => "\xC3\xBC", "\xFD" => "\xC3\xBD", "\xFE" => "\xC3\xBE", "\xFF" => "\xC3\xBF"); - - return strtr($string, $convert_table); - } - - function change_encoding($data, $input, $output) - { - $input = SimplePie_Misc::encoding($input); - $output = SimplePie_Misc::encoding($output); - - // We fail to fail on non US-ASCII bytes - if ($input === 'US-ASCII') - { - static $non_ascii_octects = ''; - if (!$non_ascii_octects) - { - for ($i = 0x80; $i <= 0xFF; $i++) - { - $non_ascii_octects .= chr($i); - } - } - $data = substr($data, 0, strcspn($data, $non_ascii_octects)); - } - - if (function_exists('iconv') && ($return = @iconv($input, $output, $data))) - { - return $return; - } - elseif (function_exists('mb_convert_encoding') && ($return = @mb_convert_encoding($data, $output, $input))) - { - return $return; - } - elseif ($input == 'windows-1252' && $output == 'UTF-8') - { - return SimplePie_Misc::windows_1252_to_utf8($data); - } - elseif ($input == 'UTF-8' && $output == 'windows-1252') - { - return utf8_decode($data); - } - return $data; - } - - function encoding($encoding) - { - // Character sets are case-insensitive (though we'll return them in the form given in their registration) - switch (strtoupper($encoding)) - { - case 'ANSI_X3.110-1983': - case 'CSA_T500-1983': - case 'CSISO99NAPLPS': - case 'ISO-IR-99': - case 'NAPLPS': - return 'ANSI_X3.110-1983'; - - case 'ARABIC7': - case 'ASMO_449': - case 'CSISO89ASMO449': - case 'ISO-IR-89': - case 'ISO_9036': - return 'ASMO_449'; - - case 'ADOBE-STANDARD-ENCODING': - case 'CSADOBESTANDARDENCODING': - return 'Adobe-Standard-Encoding'; - - case 'ADOBE-SYMBOL-ENCODING': - case 'CSHPPSMATH': - return 'Adobe-Symbol-Encoding'; - - case 'AMI-1251': - case 'AMI1251': - case 'AMIGA-1251': - case 'AMIGA1251': - return 'Amiga-1251'; - - case 'BOCU-1': - case 'CSBOCU-1': - return 'BOCU-1'; - - case 'BRF': - case 'CSBRF': - return 'BRF'; - - case 'BS_4730': - case 'CSISO4UNITEDKINGDOM': - case 'GB': - case 'ISO-IR-4': - case 'ISO646-GB': - case 'UK': - return 'BS_4730'; - - case 'BS_VIEWDATA': - case 'CSISO47BSVIEWDATA': - case 'ISO-IR-47': - return 'BS_viewdata'; - - case 'BIG5': - case 'CSBIG5': - return 'Big5'; - - case 'BIG5-HKSCS': - return 'Big5-HKSCS'; - - case 'CESU-8': - case 'CSCESU-8': - return 'CESU-8'; - - case 'CA': - case 'CSA7-1': - case 'CSA_Z243.4-1985-1': - case 'CSISO121CANADIAN1': - case 'ISO-IR-121': - case 'ISO646-CA': - return 'CSA_Z243.4-1985-1'; - - case 'CSA7-2': - case 'CSA_Z243.4-1985-2': - case 'CSISO122CANADIAN2': - case 'ISO-IR-122': - case 'ISO646-CA2': - return 'CSA_Z243.4-1985-2'; - - case 'CSA_Z243.4-1985-GR': - case 'CSISO123CSAZ24341985GR': - case 'ISO-IR-123': - return 'CSA_Z243.4-1985-gr'; - - case 'CSISO139CSN369103': - case 'CSN_369103': - case 'ISO-IR-139': - return 'CSN_369103'; - - case 'CSDECMCS': - case 'DEC': - case 'DEC-MCS': - return 'DEC-MCS'; - - case 'CSISO21GERMAN': - case 'DE': - case 'DIN_66003': - case 'ISO-IR-21': - case 'ISO646-DE': - return 'DIN_66003'; - - case 'CSISO646DANISH': - case 'DK': - case 'DS2089': - case 'DS_2089': - case 'ISO646-DK': - return 'DS_2089'; - - case 'CSIBMEBCDICATDE': - case 'EBCDIC-AT-DE': - return 'EBCDIC-AT-DE'; - - case 'CSEBCDICATDEA': - case 'EBCDIC-AT-DE-A': - return 'EBCDIC-AT-DE-A'; - - case 'CSEBCDICCAFR': - case 'EBCDIC-CA-FR': - return 'EBCDIC-CA-FR'; - - case 'CSEBCDICDKNO': - case 'EBCDIC-DK-NO': - return 'EBCDIC-DK-NO'; - - case 'CSEBCDICDKNOA': - case 'EBCDIC-DK-NO-A': - return 'EBCDIC-DK-NO-A'; - - case 'CSEBCDICES': - case 'EBCDIC-ES': - return 'EBCDIC-ES'; - - case 'CSEBCDICESA': - case 'EBCDIC-ES-A': - return 'EBCDIC-ES-A'; - - case 'CSEBCDICESS': - case 'EBCDIC-ES-S': - return 'EBCDIC-ES-S'; - - case 'CSEBCDICFISE': - case 'EBCDIC-FI-SE': - return 'EBCDIC-FI-SE'; - - case 'CSEBCDICFISEA': - case 'EBCDIC-FI-SE-A': - return 'EBCDIC-FI-SE-A'; - - case 'CSEBCDICFR': - case 'EBCDIC-FR': - return 'EBCDIC-FR'; - - case 'CSEBCDICIT': - case 'EBCDIC-IT': - return 'EBCDIC-IT'; - - case 'CSEBCDICPT': - case 'EBCDIC-PT': - return 'EBCDIC-PT'; - - case 'CSEBCDICUK': - case 'EBCDIC-UK': - return 'EBCDIC-UK'; - - case 'CSEBCDICUS': - case 'EBCDIC-US': - return 'EBCDIC-US'; - - case 'CSISO111ECMACYRILLIC': - case 'ECMA-CYRILLIC': - case 'ISO-IR-111': - case 'KOI8-E': - return 'ECMA-cyrillic'; - - case 'CSISO17SPANISH': - case 'ES': - case 'ISO-IR-17': - case 'ISO646-ES': - return 'ES'; - - case 'CSISO85SPANISH2': - case 'ES2': - case 'ISO-IR-85': - case 'ISO646-ES2': - return 'ES2'; - - case 'CSEUCPKDFMTJAPANESE': - case 'EUC-JP': - case 'EXTENDED_UNIX_CODE_PACKED_FORMAT_FOR_JAPANESE': - return 'EUC-JP'; - - case 'CSEUCKR': - case 'EUC-KR': - return 'EUC-KR'; - - case 'CSEUCFIXWIDJAPANESE': - case 'EXTENDED_UNIX_CODE_FIXED_WIDTH_FOR_JAPANESE': - return 'Extended_UNIX_Code_Fixed_Width_for_Japanese'; - - case 'GB18030': - return 'GB18030'; - - case 'CSGB2312': - case 'GB2312': - return 'GB2312'; - - case 'CP936': - case 'GBK': - case 'MS936': - case 'WINDOWS-936': - return 'GBK'; - - case 'CN': - case 'CSISO57GB1988': - case 'GB_1988-80': - case 'ISO-IR-57': - case 'ISO646-CN': - return 'GB_1988-80'; - - case 'CHINESE': - case 'CSISO58GB231280': - case 'GB_2312-80': - case 'ISO-IR-58': - return 'GB_2312-80'; - - case 'CSISO153GOST1976874': - case 'GOST_19768-74': - case 'ISO-IR-153': - case 'ST_SEV_358-88': - return 'GOST_19768-74'; - - case 'CSHPDESKTOP': - case 'HP-DESKTOP': - return 'HP-DeskTop'; - - case 'CSHPLEGAL': - case 'HP-LEGAL': - return 'HP-Legal'; - - case 'CSHPMATH8': - case 'HP-MATH8': - return 'HP-Math8'; - - case 'CSHPPIFONT': - case 'HP-PI-FONT': - return 'HP-Pi-font'; - - case 'HZ-GB-2312': - return 'HZ-GB-2312'; - - case 'CSIBMSYMBOLS': - case 'IBM-SYMBOLS': - return 'IBM-Symbols'; - - case 'CSIBMTHAI': - case 'IBM-THAI': - return 'IBM-Thai'; - - case 'CCSID00858': - case 'CP00858': - case 'IBM00858': - case 'PC-MULTILINGUAL-850+EURO': - return 'IBM00858'; - - case 'CCSID00924': - case 'CP00924': - case 'EBCDIC-LATIN9--EURO': - case 'IBM00924': - return 'IBM00924'; - - case 'CCSID01140': - case 'CP01140': - case 'EBCDIC-US-37+EURO': - case 'IBM01140': - return 'IBM01140'; - - case 'CCSID01141': - case 'CP01141': - case 'EBCDIC-DE-273+EURO': - case 'IBM01141': - return 'IBM01141'; - - case 'CCSID01142': - case 'CP01142': - case 'EBCDIC-DK-277+EURO': - case 'EBCDIC-NO-277+EURO': - case 'IBM01142': - return 'IBM01142'; - - case 'CCSID01143': - case 'CP01143': - case 'EBCDIC-FI-278+EURO': - case 'EBCDIC-SE-278+EURO': - case 'IBM01143': - return 'IBM01143'; - - case 'CCSID01144': - case 'CP01144': - case 'EBCDIC-IT-280+EURO': - case 'IBM01144': - return 'IBM01144'; - - case 'CCSID01145': - case 'CP01145': - case 'EBCDIC-ES-284+EURO': - case 'IBM01145': - return 'IBM01145'; - - case 'CCSID01146': - case 'CP01146': - case 'EBCDIC-GB-285+EURO': - case 'IBM01146': - return 'IBM01146'; - - case 'CCSID01147': - case 'CP01147': - case 'EBCDIC-FR-297+EURO': - case 'IBM01147': - return 'IBM01147'; - - case 'CCSID01148': - case 'CP01148': - case 'EBCDIC-INTERNATIONAL-500+EURO': - case 'IBM01148': - return 'IBM01148'; - - case 'CCSID01149': - case 'CP01149': - case 'EBCDIC-IS-871+EURO': - case 'IBM01149': - return 'IBM01149'; - - case 'CP037': - case 'CSIBM037': - case 'EBCDIC-CP-CA': - case 'EBCDIC-CP-NL': - case 'EBCDIC-CP-US': - case 'EBCDIC-CP-WT': - case 'IBM037': - return 'IBM037'; - - case 'CP038': - case 'CSIBM038': - case 'EBCDIC-INT': - case 'IBM038': - return 'IBM038'; - - case 'CP1026': - case 'CSIBM1026': - case 'IBM1026': - return 'IBM1026'; - - case 'IBM-1047': - case 'IBM1047': - return 'IBM1047'; - - case 'CP273': - case 'CSIBM273': - case 'IBM273': - return 'IBM273'; - - case 'CP274': - case 'CSIBM274': - case 'EBCDIC-BE': - case 'IBM274': - return 'IBM274'; - - case 'CP275': - case 'CSIBM275': - case 'EBCDIC-BR': - case 'IBM275': - return 'IBM275'; - - case 'CSIBM277': - case 'EBCDIC-CP-DK': - case 'EBCDIC-CP-NO': - case 'IBM277': - return 'IBM277'; - - case 'CP278': - case 'CSIBM278': - case 'EBCDIC-CP-FI': - case 'EBCDIC-CP-SE': - case 'IBM278': - return 'IBM278'; - - case 'CP280': - case 'CSIBM280': - case 'EBCDIC-CP-IT': - case 'IBM280': - return 'IBM280'; - - case 'CP281': - case 'CSIBM281': - case 'EBCDIC-JP-E': - case 'IBM281': - return 'IBM281'; - - case 'CP284': - case 'CSIBM284': - case 'EBCDIC-CP-ES': - case 'IBM284': - return 'IBM284'; - - case 'CP285': - case 'CSIBM285': - case 'EBCDIC-CP-GB': - case 'IBM285': - return 'IBM285'; - - case 'CP290': - case 'CSIBM290': - case 'EBCDIC-JP-KANA': - case 'IBM290': - return 'IBM290'; - - case 'CP297': - case 'CSIBM297': - case 'EBCDIC-CP-FR': - case 'IBM297': - return 'IBM297'; - - case 'CP420': - case 'CSIBM420': - case 'EBCDIC-CP-AR1': - case 'IBM420': - return 'IBM420'; - - case 'CP423': - case 'CSIBM423': - case 'EBCDIC-CP-GR': - case 'IBM423': - return 'IBM423'; - - case 'CP424': - case 'CSIBM424': - case 'EBCDIC-CP-HE': - case 'IBM424': - return 'IBM424'; - - case '437': - case 'CP437': - case 'CSPC8CODEPAGE437': - case 'IBM437': - return 'IBM437'; - - case 'CP500': - case 'CSIBM500': - case 'EBCDIC-CP-BE': - case 'EBCDIC-CP-CH': - case 'IBM500': - return 'IBM500'; - - case 'CP775': - case 'CSPC775BALTIC': - case 'IBM775': - return 'IBM775'; - - case '850': - case 'CP850': - case 'CSPC850MULTILINGUAL': - case 'IBM850': - return 'IBM850'; - - case '851': - case 'CP851': - case 'CSIBM851': - case 'IBM851': - return 'IBM851'; - - case '852': - case 'CP852': - case 'CSPCP852': - case 'IBM852': - return 'IBM852'; - - case '855': - case 'CP855': - case 'CSIBM855': - case 'IBM855': - return 'IBM855'; - - case '857': - case 'CP857': - case 'CSIBM857': - case 'IBM857': - return 'IBM857'; - - case '860': - case 'CP860': - case 'CSIBM860': - case 'IBM860': - return 'IBM860'; - - case '861': - case 'CP-IS': - case 'CP861': - case 'CSIBM861': - case 'IBM861': - return 'IBM861'; - - case '862': - case 'CP862': - case 'CSPC862LATINHEBREW': - case 'IBM862': - return 'IBM862'; - - case '863': - case 'CP863': - case 'CSIBM863': - case 'IBM863': - return 'IBM863'; - - case 'CP864': - case 'CSIBM864': - case 'IBM864': - return 'IBM864'; - - case '865': - case 'CP865': - case 'CSIBM865': - case 'IBM865': - return 'IBM865'; - - case '866': - case 'CP866': - case 'CSIBM866': - case 'IBM866': - return 'IBM866'; - - case 'CP-AR': - case 'CP868': - case 'CSIBM868': - case 'IBM868': - return 'IBM868'; - - case '869': - case 'CP-GR': - case 'CP869': - case 'CSIBM869': - case 'IBM869': - return 'IBM869'; - - case 'CP870': - case 'CSIBM870': - case 'EBCDIC-CP-ROECE': - case 'EBCDIC-CP-YU': - case 'IBM870': - return 'IBM870'; - - case 'CP871': - case 'CSIBM871': - case 'EBCDIC-CP-IS': - case 'IBM871': - return 'IBM871'; - - case 'CP880': - case 'CSIBM880': - case 'EBCDIC-CYRILLIC': - case 'IBM880': - return 'IBM880'; - - case 'CP891': - case 'CSIBM891': - case 'IBM891': - return 'IBM891'; - - case 'CP903': - case 'CSIBM903': - case 'IBM903': - return 'IBM903'; - - case '904': - case 'CP904': - case 'CSIBBM904': - case 'IBM904': - return 'IBM904'; - - case 'CP905': - case 'CSIBM905': - case 'EBCDIC-CP-TR': - case 'IBM905': - return 'IBM905'; - - case 'CP918': - case 'CSIBM918': - case 'EBCDIC-CP-AR2': - case 'IBM918': - return 'IBM918'; - - case 'CSISO143IECP271': - case 'IEC_P27-1': - case 'ISO-IR-143': - return 'IEC_P27-1'; - - case 'CSISO49INIS': - case 'INIS': - case 'ISO-IR-49': - return 'INIS'; - - case 'CSISO50INIS8': - case 'INIS-8': - case 'ISO-IR-50': - return 'INIS-8'; - - case 'CSISO51INISCYRILLIC': - case 'INIS-CYRILLIC': - case 'ISO-IR-51': - return 'INIS-cyrillic'; - - case 'CSINVARIANT': - case 'INVARIANT': - return 'INVARIANT'; - - case 'ISO-10646-J-1': - return 'ISO-10646-J-1'; - - case 'CSUNICODE': - case 'ISO-10646-UCS-2': - return 'ISO-10646-UCS-2'; - - case 'CSUCS4': - case 'ISO-10646-UCS-4': - return 'ISO-10646-UCS-4'; - - case 'CSUNICODEASCII': - case 'ISO-10646-UCS-BASIC': - return 'ISO-10646-UCS-Basic'; - - case 'CSISO10646UTF1': - case 'ISO-10646-UTF-1': - return 'ISO-10646-UTF-1'; - - case 'CSUNICODELATIN1': - case 'ISO-10646': - case 'ISO-10646-UNICODE-LATIN1': - return 'ISO-10646-Unicode-Latin1'; - - case 'CSISO115481': - case 'ISO-11548-1': - case 'ISO_11548-1': - case 'ISO_TR_11548-1': - return 'ISO-11548-1'; - - case 'ISO-2022-CN': - return 'ISO-2022-CN'; - - case 'ISO-2022-CN-EXT': - return 'ISO-2022-CN-EXT'; - - case 'CSISO2022JP': - case 'ISO-2022-JP': - return 'ISO-2022-JP'; - - case 'CSISO2022JP2': - case 'ISO-2022-JP-2': - return 'ISO-2022-JP-2'; - - case 'CSISO2022KR': - case 'ISO-2022-KR': - return 'ISO-2022-KR'; - - case 'CSWINDOWS30LATIN1': - case 'ISO-8859-1-WINDOWS-3.0-LATIN-1': - return 'ISO-8859-1-Windows-3.0-Latin-1'; - - case 'CSWINDOWS31LATIN1': - case 'ISO-8859-1-WINDOWS-3.1-LATIN-1': - return 'ISO-8859-1-Windows-3.1-Latin-1'; - - case 'CSISOLATIN6': - case 'ISO-8859-10': - case 'ISO-IR-157': - case 'ISO_8859-10:1992': - case 'L6': - case 'LATIN6': - return 'ISO-8859-10'; - - case 'ISO-8859-13': - return 'ISO-8859-13'; - - case 'ISO-8859-14': - case 'ISO-CELTIC': - case 'ISO-IR-199': - case 'ISO_8859-14': - case 'ISO_8859-14:1998': - case 'L8': - case 'LATIN8': - return 'ISO-8859-14'; - - case 'ISO-8859-15': - case 'ISO_8859-15': - case 'LATIN-9': - return 'ISO-8859-15'; - - case 'ISO-8859-16': - case 'ISO-IR-226': - case 'ISO_8859-16': - case 'ISO_8859-16:2001': - case 'L10': - case 'LATIN10': - return 'ISO-8859-16'; - - case 'CSISOLATIN2': - case 'ISO-8859-2': - case 'ISO-IR-101': - case 'ISO_8859-2': - case 'ISO_8859-2:1987': - case 'L2': - case 'LATIN2': - return 'ISO-8859-2'; - - case 'CSWINDOWS31LATIN2': - case 'ISO-8859-2-WINDOWS-LATIN-2': - return 'ISO-8859-2-Windows-Latin-2'; - - case 'CSISOLATIN3': - case 'ISO-8859-3': - case 'ISO-IR-109': - case 'ISO_8859-3': - case 'ISO_8859-3:1988': - case 'L3': - case 'LATIN3': - return 'ISO-8859-3'; - - case 'CSISOLATIN4': - case 'ISO-8859-4': - case 'ISO-IR-110': - case 'ISO_8859-4': - case 'ISO_8859-4:1988': - case 'L4': - case 'LATIN4': - return 'ISO-8859-4'; - - case 'CSISOLATINCYRILLIC': - case 'CYRILLIC': - case 'ISO-8859-5': - case 'ISO-IR-144': - case 'ISO_8859-5': - case 'ISO_8859-5:1988': - return 'ISO-8859-5'; - - case 'ARABIC': - case 'ASMO-708': - case 'CSISOLATINARABIC': - case 'ECMA-114': - case 'ISO-8859-6': - case 'ISO-IR-127': - case 'ISO_8859-6': - case 'ISO_8859-6:1987': - return 'ISO-8859-6'; - - case 'CSISO88596E': - case 'ISO-8859-6-E': - case 'ISO_8859-6-E': - return 'ISO-8859-6-E'; - - case 'CSISO88596I': - case 'ISO-8859-6-I': - case 'ISO_8859-6-I': - return 'ISO-8859-6-I'; - - case 'CSISOLATINGREEK': - case 'ECMA-118': - case 'ELOT_928': - case 'GREEK': - case 'GREEK8': - case 'ISO-8859-7': - case 'ISO-IR-126': - case 'ISO_8859-7': - case 'ISO_8859-7:1987': - return 'ISO-8859-7'; - - case 'CSISOLATINHEBREW': - case 'HEBREW': - case 'ISO-8859-8': - case 'ISO-IR-138': - case 'ISO_8859-8': - case 'ISO_8859-8:1988': - return 'ISO-8859-8'; - - case 'CSISO88598E': - case 'ISO-8859-8-E': - case 'ISO_8859-8-E': - return 'ISO-8859-8-E'; - - case 'CSISO88598I': - case 'ISO-8859-8-I': - case 'ISO_8859-8-I': - return 'ISO-8859-8-I'; - - case 'CSISOLATIN5': - case 'ISO-8859-9': - case 'ISO-IR-148': - case 'ISO_8859-9': - case 'ISO_8859-9:1989': - case 'L5': - case 'LATIN5': - return 'ISO-8859-9'; - - case 'CSWINDOWS31LATIN5': - case 'ISO-8859-9-WINDOWS-LATIN-5': - return 'ISO-8859-9-Windows-Latin-5'; - - case 'CSUNICODEIBM1261': - case 'ISO-UNICODE-IBM-1261': - return 'ISO-Unicode-IBM-1261'; - - case 'CSUNICODEIBM1264': - case 'ISO-UNICODE-IBM-1264': - return 'ISO-Unicode-IBM-1264'; - - case 'CSUNICODEIBM1265': - case 'ISO-UNICODE-IBM-1265': - return 'ISO-Unicode-IBM-1265'; - - case 'CSUNICODEIBM1268': - case 'ISO-UNICODE-IBM-1268': - return 'ISO-Unicode-IBM-1268'; - - case 'CSUNICODEIBM1276': - case 'ISO-UNICODE-IBM-1276': - return 'ISO-Unicode-IBM-1276'; - - case 'CSISO10367BOX': - case 'ISO-IR-155': - case 'ISO_10367-BOX': - return 'ISO_10367-box'; - - case 'CSISO2033': - case 'E13B': - case 'ISO-IR-98': - case 'ISO_2033-1983': - return 'ISO_2033-1983'; - - case 'CSISO5427CYRILLIC': - case 'ISO-IR-37': - case 'ISO_5427': - return 'ISO_5427'; - - case 'ISO-IR-54': - case 'ISO5427CYRILLIC1981': - case 'ISO_5427:1981': - return 'ISO_5427:1981'; - - case 'CSISO5428GREEK': - case 'ISO-IR-55': - case 'ISO_5428:1980': - return 'ISO_5428:1980'; - - case 'CSISO646BASIC1983': - case 'ISO_646.BASIC:1983': - case 'REF': - return 'ISO_646.basic:1983'; - - case 'CSISO2INTLREFVERSION': - case 'IRV': - case 'ISO-IR-2': - case 'ISO_646.IRV:1983': - return 'ISO_646.irv:1983'; - - case 'CSISO6937ADD': - case 'ISO-IR-152': - case 'ISO_6937-2-25': - return 'ISO_6937-2-25'; - - case 'CSISOTEXTCOMM': - case 'ISO-IR-142': - case 'ISO_6937-2-ADD': - return 'ISO_6937-2-add'; - - case 'CSISO8859SUPP': - case 'ISO-IR-154': - case 'ISO_8859-SUPP': - case 'LATIN1-2-5': - return 'ISO_8859-supp'; - - case 'CSISO15ITALIAN': - case 'ISO-IR-15': - case 'ISO646-IT': - case 'IT': - return 'IT'; - - case 'CSISO13JISC6220JP': - case 'ISO-IR-13': - case 'JIS_C6220-1969': - case 'JIS_C6220-1969-JP': - case 'KATAKANA': - case 'X0201-7': - return 'JIS_C6220-1969-jp'; - - case 'CSISO14JISC6220RO': - case 'ISO-IR-14': - case 'ISO646-JP': - case 'JIS_C6220-1969-RO': - case 'JP': - return 'JIS_C6220-1969-ro'; - - case 'CSISO42JISC62261978': - case 'ISO-IR-42': - case 'JIS_C6226-1978': - return 'JIS_C6226-1978'; - - case 'CSISO87JISX0208': - case 'ISO-IR-87': - case 'JIS_C6226-1983': - case 'JIS_X0208-1983': - case 'X0208': - return 'JIS_C6226-1983'; - - case 'CSISO91JISC62291984A': - case 'ISO-IR-91': - case 'JIS_C6229-1984-A': - case 'JP-OCR-A': - return 'JIS_C6229-1984-a'; - - case 'CSISO92JISC62991984B': - case 'ISO-IR-92': - case 'ISO646-JP-OCR-B': - case 'JIS_C6229-1984-B': - case 'JP-OCR-B': - return 'JIS_C6229-1984-b'; - - case 'CSISO93JIS62291984BADD': - case 'ISO-IR-93': - case 'JIS_C6229-1984-B-ADD': - case 'JP-OCR-B-ADD': - return 'JIS_C6229-1984-b-add'; - - case 'CSISO94JIS62291984HAND': - case 'ISO-IR-94': - case 'JIS_C6229-1984-HAND': - case 'JP-OCR-HAND': - return 'JIS_C6229-1984-hand'; - - case 'CSISO95JIS62291984HANDADD': - case 'ISO-IR-95': - case 'JIS_C6229-1984-HAND-ADD': - case 'JP-OCR-HAND-ADD': - return 'JIS_C6229-1984-hand-add'; - - case 'CSISO96JISC62291984KANA': - case 'ISO-IR-96': - case 'JIS_C6229-1984-KANA': - return 'JIS_C6229-1984-kana'; - - case 'CSJISENCODING': - case 'JIS_ENCODING': - return 'JIS_Encoding'; - - case 'CSHALFWIDTHKATAKANA': - case 'JIS_X0201': - case 'X0201': - return 'JIS_X0201'; - - case 'CSISO159JISX02121990': - case 'ISO-IR-159': - case 'JIS_X0212-1990': - case 'X0212': - return 'JIS_X0212-1990'; - - case 'CSISO141JUSIB1002': - case 'ISO-IR-141': - case 'ISO646-YU': - case 'JS': - case 'JUS_I.B1.002': - case 'YU': - return 'JUS_I.B1.002'; - - case 'CSISO147MACEDONIAN': - case 'ISO-IR-147': - case 'JUS_I.B1.003-MAC': - case 'MACEDONIAN': - return 'JUS_I.B1.003-mac'; - - case 'CSISO146SERBIAN': - case 'ISO-IR-146': - case 'JUS_I.B1.003-SERB': - case 'SERBIAN': - return 'JUS_I.B1.003-serb'; - - case 'KOI7-SWITCHED': - return 'KOI7-switched'; - - case 'CSKOI8R': - case 'KOI8-R': - return 'KOI8-R'; - - case 'KOI8-U': - return 'KOI8-U'; - - case 'CSKSC5636': - case 'ISO646-KR': - case 'KSC5636': - return 'KSC5636'; - - case 'CSKSC56011987': - case 'ISO-IR-149': - case 'KOREAN': - case 'KSC_5601': - case 'KS_C_5601-1987': - case 'KS_C_5601-1989': - return 'KS_C_5601-1987'; - - case 'CSKZ1048': - case 'KZ-1048': - case 'RK1048': - case 'STRK1048-2002': - return 'KZ-1048'; - - case 'CSISO27LATINGREEK1': - case 'ISO-IR-27': - case 'LATIN-GREEK-1': - return 'Latin-greek-1'; - - case 'CSMNEM': - case 'MNEM': - return 'MNEM'; - - case 'CSMNEMONIC': - case 'MNEMONIC': - return 'MNEMONIC'; - - case 'CSISO86HUNGARIAN': - case 'HU': - case 'ISO-IR-86': - case 'ISO646-HU': - case 'MSZ_7795.3': - return 'MSZ_7795.3'; - - case 'CSMICROSOFTPUBLISHING': - case 'MICROSOFT-PUBLISHING': - return 'Microsoft-Publishing'; - - case 'CSNATSDANO': - case 'ISO-IR-9-1': - case 'NATS-DANO': - return 'NATS-DANO'; - - case 'CSNATSDANOADD': - case 'ISO-IR-9-2': - case 'NATS-DANO-ADD': - return 'NATS-DANO-ADD'; - - case 'CSNATSSEFI': - case 'ISO-IR-8-1': - case 'NATS-SEFI': - return 'NATS-SEFI'; - - case 'CSNATSSEFIADD': - case 'ISO-IR-8-2': - case 'NATS-SEFI-ADD': - return 'NATS-SEFI-ADD'; - - case 'CSISO151CUBA': - case 'CUBA': - case 'ISO-IR-151': - case 'ISO646-CU': - case 'NC_NC00-10:81': - return 'NC_NC00-10:81'; - - case 'CSISO69FRENCH': - case 'FR': - case 'ISO-IR-69': - case 'ISO646-FR': - case 'NF_Z_62-010': - return 'NF_Z_62-010'; - - case 'CSISO25FRENCH': - case 'ISO-IR-25': - case 'ISO646-FR1': - case 'NF_Z_62-010_(1973)': - return 'NF_Z_62-010_(1973)'; - - case 'CSISO60DANISHNORWEGIAN': - case 'CSISO60NORWEGIAN1': - case 'ISO-IR-60': - case 'ISO646-NO': - case 'NO': - case 'NS_4551-1': - return 'NS_4551-1'; - - case 'CSISO61NORWEGIAN2': - case 'ISO-IR-61': - case 'ISO646-NO2': - case 'NO2': - case 'NS_4551-2': - return 'NS_4551-2'; - - case 'OSD_EBCDIC_DF03_IRV': - return 'OSD_EBCDIC_DF03_IRV'; - - case 'OSD_EBCDIC_DF04_1': - return 'OSD_EBCDIC_DF04_1'; - - case 'OSD_EBCDIC_DF04_15': - return 'OSD_EBCDIC_DF04_15'; - - case 'CSPC8DANISHNORWEGIAN': - case 'PC8-DANISH-NORWEGIAN': - return 'PC8-Danish-Norwegian'; - - case 'CSPC8TURKISH': - case 'PC8-TURKISH': - return 'PC8-Turkish'; - - case 'CSISO16PORTUGUESE': - case 'ISO-IR-16': - case 'ISO646-PT': - case 'PT': - return 'PT'; - - case 'CSISO84PORTUGUESE2': - case 'ISO-IR-84': - case 'ISO646-PT2': - case 'PT2': - return 'PT2'; - - case 'CP154': - case 'CSPTCP154': - case 'CYRILLIC-ASIAN': - case 'PT154': - case 'PTCP154': - return 'PTCP154'; - - case 'SCSU': - return 'SCSU'; - - case 'CSISO10SWEDISH': - case 'FI': - case 'ISO-IR-10': - case 'ISO646-FI': - case 'ISO646-SE': - case 'SE': - case 'SEN_850200_B': - return 'SEN_850200_B'; - - case 'CSISO11SWEDISHFORNAMES': - case 'ISO-IR-11': - case 'ISO646-SE2': - case 'SE2': - case 'SEN_850200_C': - return 'SEN_850200_C'; - - case 'CSSHIFTJIS': - case 'MS_KANJI': - case 'SHIFT_JIS': - return 'Shift_JIS'; - - case 'CSISO128T101G2': - case 'ISO-IR-128': - case 'T.101-G2': - return 'T.101-G2'; - - case 'CSISO102T617BIT': - case 'ISO-IR-102': - case 'T.61-7BIT': - return 'T.61-7bit'; - - case 'CSISO103T618BIT': - case 'ISO-IR-103': - case 'T.61': - case 'T.61-8BIT': - return 'T.61-8bit'; - - case 'CSTSCII': - case 'TSCII': - return 'TSCII'; - - case 'CSUNICODE11': - case 'UNICODE-1-1': - return 'UNICODE-1-1'; - - case 'CSUNICODE11UTF7': - case 'UNICODE-1-1-UTF-7': - return 'UNICODE-1-1-UTF-7'; - - case 'CSUNKNOWN8BIT': - case 'UNKNOWN-8BIT': - return 'UNKNOWN-8BIT'; - - case 'ANSI': - case 'ANSI_X3.4-1968': - case 'ANSI_X3.4-1986': - case 'ASCII': - case 'CP367': - case 'CSASCII': - case 'IBM367': - case 'ISO-IR-6': - case 'ISO646-US': - case 'ISO_646.IRV:1991': - case 'US': - case 'US-ASCII': - return 'US-ASCII'; - - case 'UTF-16': - return 'UTF-16'; - - case 'UTF-16BE': - return 'UTF-16BE'; - - case 'UTF-16LE': - return 'UTF-16LE'; - - case 'UTF-32': - return 'UTF-32'; - - case 'UTF-32BE': - return 'UTF-32BE'; - - case 'UTF-32LE': - return 'UTF-32LE'; - - case 'UTF-7': - return 'UTF-7'; - - case 'UTF-8': - return 'UTF-8'; - - case 'CSVIQR': - case 'VIQR': - return 'VIQR'; - - case 'CSVISCII': - case 'VISCII': - return 'VISCII'; - - case 'CSVENTURAINTERNATIONAL': - case 'VENTURA-INTERNATIONAL': - return 'Ventura-International'; - - case 'CSVENTURAMATH': - case 'VENTURA-MATH': - return 'Ventura-Math'; - - case 'CSVENTURAUS': - case 'VENTURA-US': - return 'Ventura-US'; - - case 'CSWINDOWS31J': - case 'WINDOWS-31J': - return 'Windows-31J'; - - case 'CSDKUS': - case 'DK-US': - return 'dk-us'; - - case 'CSISO150': - case 'CSISO150GREEKCCITT': - case 'GREEK-CCITT': - case 'ISO-IR-150': - return 'greek-ccitt'; - - case 'CSISO88GREEK7': - case 'GREEK7': - case 'ISO-IR-88': - return 'greek7'; - - case 'CSISO18GREEK7OLD': - case 'GREEK7-OLD': - case 'ISO-IR-18': - return 'greek7-old'; - - case 'CSHPROMAN8': - case 'HP-ROMAN8': - case 'R8': - case 'ROMAN8': - return 'hp-roman8'; - - case 'CSISO90': - case 'ISO-IR-90': - return 'iso-ir-90'; - - case 'CSISO19LATINGREEK': - case 'ISO-IR-19': - case 'LATIN-GREEK': - return 'latin-greek'; - - case 'CSISO158LAP': - case 'ISO-IR-158': - case 'LAP': - case 'LATIN-LAP': - return 'latin-lap'; - - case 'CSMACINTOSH': - case 'MAC': - case 'MACINTOSH': - return 'macintosh'; - - case 'CSUSDK': - case 'US-DK': - return 'us-dk'; - - case 'CSISO70VIDEOTEXSUPP1': - case 'ISO-IR-70': - case 'VIDEOTEX-SUPPL': - return 'videotex-suppl'; - - case 'WINDOWS-1250': - return 'windows-1250'; - - case 'WINDOWS-1251': - return 'windows-1251'; - - case 'CP819': - case 'CSISOLATIN1': - case 'IBM819': - case 'ISO-8859-1': - case 'ISO-IR-100': - case 'ISO_8859-1': - case 'ISO_8859-1:1987': - case 'L1': - case 'LATIN1': - case 'WINDOWS-1252': - return 'windows-1252'; - - case 'WINDOWS-1253': - return 'windows-1253'; - - case 'WINDOWS-1254': - return 'windows-1254'; - - case 'WINDOWS-1255': - return 'windows-1255'; - - case 'WINDOWS-1256': - return 'windows-1256'; - - case 'WINDOWS-1257': - return 'windows-1257'; - - case 'WINDOWS-1258': - return 'windows-1258'; - - default: - return $encoding; - } - } - - function get_curl_version() - { - if (is_array($curl = curl_version())) - { - $curl = $curl['version']; - } - elseif (substr($curl, 0, 5) == 'curl/') - { - $curl = substr($curl, 5, strcspn($curl, "\x09\x0A\x0B\x0C\x0D", 5)); - } - elseif (substr($curl, 0, 8) == 'libcurl/') - { - $curl = substr($curl, 8, strcspn($curl, "\x09\x0A\x0B\x0C\x0D", 8)); - } - else - { - $curl = 0; - } - return $curl; - } - - function is_subclass_of($class1, $class2) - { - if (func_num_args() != 2) - { - trigger_error('Wrong parameter count for SimplePie_Misc::is_subclass_of()', E_USER_WARNING); - } - elseif (version_compare(PHP_VERSION, '5.0.3', '>=') || is_object($class1)) - { - return is_subclass_of($class1, $class2); - } - elseif (is_string($class1) && is_string($class2)) - { - if (class_exists($class1)) - { - if (class_exists($class2)) - { - $class2 = strtolower($class2); - while ($class1 = strtolower(get_parent_class($class1))) - { - if ($class1 == $class2) - { - return true; - } - } - } - } - else - { - trigger_error('Unknown class passed as parameter', E_USER_WARNNG); - } - } - return false; - } - - /** - * Strip HTML comments - * - * @access public - * @param string $data Data to strip comments from - * @return string Comment stripped string - */ - function strip_comments($data) - { - $output = ''; - while (($start = strpos($data, '', $start)) !== false) - { - $data = substr_replace($data, '', 0, $end + 3); - } - else - { - $data = ''; - } - } - return $output . $data; - } - - function parse_date($dt) - { - $parser = SimplePie_Parse_Date::get(); - return $parser->parse($dt); - } - - /** - * Decode HTML entities - * - * @static - * @access public - * @param string $data Input data - * @return string Output data - */ - function entities_decode($data) - { - $decoder = new SimplePie_Decode_HTML_Entities($data); - return $decoder->parse(); - } - - /** - * Remove RFC822 comments - * - * @access public - * @param string $data Data to strip comments from - * @return string Comment stripped string - */ - function uncomment_rfc822($string) - { - $string = (string) $string; - $position = 0; - $length = strlen($string); - $depth = 0; - - $output = ''; - - while ($position < $length && ($pos = strpos($string, '(', $position)) !== false) - { - $output .= substr($string, $position, $pos - $position); - $position = $pos + 1; - if ($string[$pos - 1] !== '\\') - { - $depth++; - while ($depth && $position < $length) - { - $position += strcspn($string, '()', $position); - if ($string[$position - 1] === '\\') - { - $position++; - continue; - } - elseif (isset($string[$position])) - { - switch ($string[$position]) - { - case '(': - $depth++; - break; - - case ')': - $depth--; - break; - } - $position++; - } - else - { - break; - } - } - } - else - { - $output .= '('; - } - } - $output .= substr($string, $position); - - return $output; - } - - function parse_mime($mime) - { - if (($pos = strpos($mime, ';')) === false) - { - return trim($mime); - } - else - { - return trim(substr($mime, 0, $pos)); - } - } - - function htmlspecialchars_decode($string, $quote_style) - { - if (function_exists('htmlspecialchars_decode')) - { - return htmlspecialchars_decode($string, $quote_style); - } - else - { - return strtr($string, array_flip(get_html_translation_table(HTML_SPECIALCHARS, $quote_style))); - } - } - - function atom_03_construct_type($attribs) - { - if (isset($attribs['']['mode']) && strtolower(trim($attribs['']['mode']) == 'base64')) - { - $mode = SIMPLEPIE_CONSTRUCT_BASE64; - } - else - { - $mode = SIMPLEPIE_CONSTRUCT_NONE; - } - if (isset($attribs['']['type'])) - { - switch (strtolower(trim($attribs['']['type']))) - { - case 'text': - case 'text/plain': - return SIMPLEPIE_CONSTRUCT_TEXT | $mode; - - case 'html': - case 'text/html': - return SIMPLEPIE_CONSTRUCT_HTML | $mode; - - case 'xhtml': - case 'application/xhtml+xml': - return SIMPLEPIE_CONSTRUCT_XHTML | $mode; - - default: - return SIMPLEPIE_CONSTRUCT_NONE | $mode; - } - } - else - { - return SIMPLEPIE_CONSTRUCT_TEXT | $mode; - } - } - - function atom_10_construct_type($attribs) - { - if (isset($attribs['']['type'])) - { - switch (strtolower(trim($attribs['']['type']))) - { - case 'text': - return SIMPLEPIE_CONSTRUCT_TEXT; - - case 'html': - return SIMPLEPIE_CONSTRUCT_HTML; - - case 'xhtml': - return SIMPLEPIE_CONSTRUCT_XHTML; - - default: - return SIMPLEPIE_CONSTRUCT_NONE; - } - } - return SIMPLEPIE_CONSTRUCT_TEXT; - } - - function atom_10_content_construct_type($attribs) - { - if (isset($attribs['']['type'])) - { - $type = strtolower(trim($attribs['']['type'])); - switch ($type) - { - case 'text': - return SIMPLEPIE_CONSTRUCT_TEXT; - - case 'html': - return SIMPLEPIE_CONSTRUCT_HTML; - - case 'xhtml': - return SIMPLEPIE_CONSTRUCT_XHTML; - } - if (in_array(substr($type, -4), array('+xml', '/xml')) || substr($type, 0, 5) == 'text/') - { - return SIMPLEPIE_CONSTRUCT_NONE; - } - else - { - return SIMPLEPIE_CONSTRUCT_BASE64; - } - } - else - { - return SIMPLEPIE_CONSTRUCT_TEXT; - } - } - - function is_isegment_nz_nc($string) - { - return (bool) preg_match('/^([A-Za-z0-9\-._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!$&\'()*+,;=@]|(%[0-9ABCDEF]{2}))+$/u', $string); - } - - function space_seperated_tokens($string) - { - $space_characters = "\x20\x09\x0A\x0B\x0C\x0D"; - $string_length = strlen($string); - - $position = strspn($string, $space_characters); - $tokens = array(); - - while ($position < $string_length) - { - $len = strcspn($string, $space_characters, $position); - $tokens[] = substr($string, $position, $len); - $position += $len; - $position += strspn($string, $space_characters, $position); - } - - return $tokens; - } - - function array_unique($array) - { - if (version_compare(PHP_VERSION, '5.2', '>=')) - { - return array_unique($array); - } - else - { - $array = (array) $array; - $new_array = array(); - $new_array_strings = array(); - foreach ($array as $key => $value) - { - if (is_object($value)) - { - if (method_exists($value, '__toString')) - { - $cmp = $value->__toString(); - } - else - { - trigger_error('Object of class ' . get_class($value) . ' could not be converted to string', E_USER_ERROR); - } - } - elseif (is_array($value)) - { - $cmp = (string) reset($value); - } - else - { - $cmp = (string) $value; - } - if (!in_array($cmp, $new_array_strings)) - { - $new_array[$key] = $value; - $new_array_strings[] = $cmp; - } - } - return $new_array; - } - } - - /** - * Converts a unicode codepoint to a UTF-8 character - * - * @static - * @access public - * @param int $codepoint Unicode codepoint - * @return string UTF-8 character - */ - function codepoint_to_utf8($codepoint) - { - static $cache = array(); - $codepoint = (int) $codepoint; - if (isset($cache[$codepoint])) - { - return $cache[$codepoint]; - } - elseif ($codepoint < 0) - { - return $cache[$codepoint] = false; - } - else if ($codepoint <= 0x7f) - { - return $cache[$codepoint] = chr($codepoint); - } - else if ($codepoint <= 0x7ff) - { - return $cache[$codepoint] = chr(0xc0 | ($codepoint >> 6)) . chr(0x80 | ($codepoint & 0x3f)); - } - else if ($codepoint <= 0xffff) - { - return $cache[$codepoint] = chr(0xe0 | ($codepoint >> 12)) . chr(0x80 | (($codepoint >> 6) & 0x3f)) . chr(0x80 | ($codepoint & 0x3f)); - } - else if ($codepoint <= 0x10ffff) - { - return $cache[$codepoint] = chr(0xf0 | ($codepoint >> 18)) . chr(0x80 | (($codepoint >> 12) & 0x3f)) . chr(0x80 | (($codepoint >> 6) & 0x3f)) . chr(0x80 | ($codepoint & 0x3f)); - } - else - { - // U+FFFD REPLACEMENT CHARACTER - return $cache[$codepoint] = "\xEF\xBF\xBD"; - } - } - - /** - * Re-implementation of PHP 5's stripos() - * - * Returns the numeric position of the first occurrence of needle in the - * haystack string. - * - * @static - * @access string - * @param object $haystack - * @param string $needle Note that the needle may be a string of one or more - * characters. If needle is not a string, it is converted to an integer - * and applied as the ordinal value of a character. - * @param int $offset The optional offset parameter allows you to specify which - * character in haystack to start searching. The position returned is still - * relative to the beginning of haystack. - * @return bool If needle is not found, stripos() will return boolean false. - */ - function stripos($haystack, $needle, $offset = 0) - { - if (function_exists('stripos')) - { - return stripos($haystack, $needle, $offset); - } - else - { - if (is_string($needle)) - { - $needle = strtolower($needle); - } - elseif (is_int($needle) || is_bool($needle) || is_double($needle)) - { - $needle = strtolower(chr($needle)); - } - else - { - trigger_error('needle is not a string or an integer', E_USER_WARNING); - return false; - } - - return strpos(strtolower($haystack), $needle, $offset); - } - } - - /** - * Similar to parse_str() - * - * Returns an associative array of name/value pairs, where the value is an - * array of values that have used the same name - * - * @static - * @access string - * @param string $str The input string. - * @return array - */ - function parse_str($str) - { - $return = array(); - $str = explode('&', $str); - - foreach ($str as $section) - { - if (strpos($section, '=') !== false) - { - list($name, $value) = explode('=', $section, 2); - $return[urldecode($name)][] = urldecode($value); - } - else - { - $return[urldecode($section)][] = null; - } - } - - return $return; - } - - /** - * Detect XML encoding, as per XML 1.0 Appendix F.1 - * - * @todo Add support for EBCDIC - * @param string $data XML data - * @return array Possible encodings - */ - function xml_encoding($data) - { - // UTF-32 Big Endian BOM - if (substr($data, 0, 4) === "\x00\x00\xFE\xFF") - { - $encoding[] = 'UTF-32BE'; - } - // UTF-32 Little Endian BOM - elseif (substr($data, 0, 4) === "\xFF\xFE\x00\x00") - { - $encoding[] = 'UTF-32LE'; - } - // UTF-16 Big Endian BOM - elseif (substr($data, 0, 2) === "\xFE\xFF") - { - $encoding[] = 'UTF-16BE'; - } - // UTF-16 Little Endian BOM - elseif (substr($data, 0, 2) === "\xFF\xFE") - { - $encoding[] = 'UTF-16LE'; - } - // UTF-8 BOM - elseif (substr($data, 0, 3) === "\xEF\xBB\xBF") - { - $encoding[] = 'UTF-8'; - } - // UTF-32 Big Endian Without BOM - elseif (substr($data, 0, 20) === "\x00\x00\x00\x3C\x00\x00\x00\x3F\x00\x00\x00\x78\x00\x00\x00\x6D\x00\x00\x00\x6C") - { - if ($pos = strpos($data, "\x00\x00\x00\x3F\x00\x00\x00\x3E")) - { - $parser = new SimplePie_XML_Declaration_Parser(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 20), 'UTF-32BE', 'UTF-8')); - if ($parser->parse()) - { - $encoding[] = $parser->encoding; - } - } - $encoding[] = 'UTF-32BE'; - } - // UTF-32 Little Endian Without BOM - elseif (substr($data, 0, 20) === "\x3C\x00\x00\x00\x3F\x00\x00\x00\x78\x00\x00\x00\x6D\x00\x00\x00\x6C\x00\x00\x00") - { - if ($pos = strpos($data, "\x3F\x00\x00\x00\x3E\x00\x00\x00")) - { - $parser = new SimplePie_XML_Declaration_Parser(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 20), 'UTF-32LE', 'UTF-8')); - if ($parser->parse()) - { - $encoding[] = $parser->encoding; - } - } - $encoding[] = 'UTF-32LE'; - } - // UTF-16 Big Endian Without BOM - elseif (substr($data, 0, 10) === "\x00\x3C\x00\x3F\x00\x78\x00\x6D\x00\x6C") - { - if ($pos = strpos($data, "\x00\x3F\x00\x3E")) - { - $parser = new SimplePie_XML_Declaration_Parser(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 10), 'UTF-16BE', 'UTF-8')); - if ($parser->parse()) - { - $encoding[] = $parser->encoding; - } - } - $encoding[] = 'UTF-16BE'; - } - // UTF-16 Little Endian Without BOM - elseif (substr($data, 0, 10) === "\x3C\x00\x3F\x00\x78\x00\x6D\x00\x6C\x00") - { - if ($pos = strpos($data, "\x3F\x00\x3E\x00")) - { - $parser = new SimplePie_XML_Declaration_Parser(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 10), 'UTF-16LE', 'UTF-8')); - if ($parser->parse()) - { - $encoding[] = $parser->encoding; - } - } - $encoding[] = 'UTF-16LE'; - } - // US-ASCII (or superset) - elseif (substr($data, 0, 5) === "\x3C\x3F\x78\x6D\x6C") - { - if ($pos = strpos($data, "\x3F\x3E")) - { - $parser = new SimplePie_XML_Declaration_Parser(substr($data, 5, $pos - 5)); - if ($parser->parse()) - { - $encoding[] = $parser->encoding; - } - } - $encoding[] = 'UTF-8'; - } - // Fallback to UTF-8 - else - { - $encoding[] = 'UTF-8'; - } - return $encoding; - } -} - -/** - * Decode HTML Entities - * - * This implements HTML5 as of revision 967 (2007-06-28) - * - * @package SimplePie - */ -class SimplePie_Decode_HTML_Entities -{ - /** - * Data to be parsed - * - * @access private - * @var string - */ - var $data = ''; - - /** - * Currently consumed bytes - * - * @access private - * @var string - */ - var $consumed = ''; - - /** - * Position of the current byte being parsed - * - * @access private - * @var int - */ - var $position = 0; - - /** - * Create an instance of the class with the input data - * - * @access public - * @param string $data Input data - */ - function SimplePie_Decode_HTML_Entities($data) - { - $this->data = $data; - } - - /** - * Parse the input data - * - * @access public - * @return string Output data - */ - function parse() - { - while (($this->position = strpos($this->data, '&', $this->position)) !== false) - { - $this->consume(); - $this->entity(); - $this->consumed = ''; - } - return $this->data; - } - - /** - * Consume the next byte - * - * @access private - * @return mixed The next byte, or false, if there is no more data - */ - function consume() - { - if (isset($this->data[$this->position])) - { - $this->consumed .= $this->data[$this->position]; - return $this->data[$this->position++]; - } - else - { - $this->consumed = false; - return false; - } - } - - /** - * Consume a range of characters - * - * @access private - * @param string $chars Characters to consume - * @return mixed A series of characters that match the range, or false - */ - function consume_range($chars) - { - if ($len = strspn($this->data, $chars, $this->position)) - { - $data = substr($this->data, $this->position, $len); - $this->consumed .= $data; - $this->position += $len; - return $data; - } - else - { - $this->consumed = false; - return false; - } - } - - /** - * Unconsume one byte - * - * @access private - */ - function unconsume() - { - $this->consumed = substr($this->consumed, 0, -1); - $this->position--; - } - - /** - * Decode an entity - * - * @access private - */ - function entity() - { - switch ($this->consume()) - { - case "\x09": - case "\x0A": - case "\x0B": - case "\x0B": - case "\x0C": - case "\x20": - case "\x3C": - case "\x26": - case false: - break; - - case "\x23": - switch ($this->consume()) - { - case "\x78": - case "\x58": - $range = '0123456789ABCDEFabcdef'; - $hex = true; - break; - - default: - $range = '0123456789'; - $hex = false; - $this->unconsume(); - break; - } - - if ($codepoint = $this->consume_range($range)) - { - static $windows_1252_specials = array(0x0D => "\x0A", 0x80 => "\xE2\x82\xAC", 0x81 => "\xEF\xBF\xBD", 0x82 => "\xE2\x80\x9A", 0x83 => "\xC6\x92", 0x84 => "\xE2\x80\x9E", 0x85 => "\xE2\x80\xA6", 0x86 => "\xE2\x80\xA0", 0x87 => "\xE2\x80\xA1", 0x88 => "\xCB\x86", 0x89 => "\xE2\x80\xB0", 0x8A => "\xC5\xA0", 0x8B => "\xE2\x80\xB9", 0x8C => "\xC5\x92", 0x8D => "\xEF\xBF\xBD", 0x8E => "\xC5\xBD", 0x8F => "\xEF\xBF\xBD", 0x90 => "\xEF\xBF\xBD", 0x91 => "\xE2\x80\x98", 0x92 => "\xE2\x80\x99", 0x93 => "\xE2\x80\x9C", 0x94 => "\xE2\x80\x9D", 0x95 => "\xE2\x80\xA2", 0x96 => "\xE2\x80\x93", 0x97 => "\xE2\x80\x94", 0x98 => "\xCB\x9C", 0x99 => "\xE2\x84\xA2", 0x9A => "\xC5\xA1", 0x9B => "\xE2\x80\xBA", 0x9C => "\xC5\x93", 0x9D => "\xEF\xBF\xBD", 0x9E => "\xC5\xBE", 0x9F => "\xC5\xB8"); - - if ($hex) - { - $codepoint = hexdec($codepoint); - } - else - { - $codepoint = intval($codepoint); - } - - if (isset($windows_1252_specials[$codepoint])) - { - $replacement = $windows_1252_specials[$codepoint]; - } - else - { - $replacement = SimplePie_Misc::codepoint_to_utf8($codepoint); - } - - if ($this->consume() != ';') - { - $this->unconsume(); - } - - $consumed_length = strlen($this->consumed); - $this->data = substr_replace($this->data, $replacement, $this->position - $consumed_length, $consumed_length); - $this->position += strlen($replacement) - $consumed_length; - } - break; - - default: - static $entities = array('Aacute' => "\xC3\x81", 'aacute' => "\xC3\xA1", 'Aacute;' => "\xC3\x81", 'aacute;' => "\xC3\xA1", 'Acirc' => "\xC3\x82", 'acirc' => "\xC3\xA2", 'Acirc;' => "\xC3\x82", 'acirc;' => "\xC3\xA2", 'acute' => "\xC2\xB4", 'acute;' => "\xC2\xB4", 'AElig' => "\xC3\x86", 'aelig' => "\xC3\xA6", 'AElig;' => "\xC3\x86", 'aelig;' => "\xC3\xA6", 'Agrave' => "\xC3\x80", 'agrave' => "\xC3\xA0", 'Agrave;' => "\xC3\x80", 'agrave;' => "\xC3\xA0", 'alefsym;' => "\xE2\x84\xB5", 'Alpha;' => "\xCE\x91", 'alpha;' => "\xCE\xB1", 'AMP' => "\x26", 'amp' => "\x26", 'AMP;' => "\x26", 'amp;' => "\x26", 'and;' => "\xE2\x88\xA7", 'ang;' => "\xE2\x88\xA0", 'apos;' => "\x27", 'Aring' => "\xC3\x85", 'aring' => "\xC3\xA5", 'Aring;' => "\xC3\x85", 'aring;' => "\xC3\xA5", 'asymp;' => "\xE2\x89\x88", 'Atilde' => "\xC3\x83", 'atilde' => "\xC3\xA3", 'Atilde;' => "\xC3\x83", 'atilde;' => "\xC3\xA3", 'Auml' => "\xC3\x84", 'auml' => "\xC3\xA4", 'Auml;' => "\xC3\x84", 'auml;' => "\xC3\xA4", 'bdquo;' => "\xE2\x80\x9E", 'Beta;' => "\xCE\x92", 'beta;' => "\xCE\xB2", 'brvbar' => "\xC2\xA6", 'brvbar;' => "\xC2\xA6", 'bull;' => "\xE2\x80\xA2", 'cap;' => "\xE2\x88\xA9", 'Ccedil' => "\xC3\x87", 'ccedil' => "\xC3\xA7", 'Ccedil;' => "\xC3\x87", 'ccedil;' => "\xC3\xA7", 'cedil' => "\xC2\xB8", 'cedil;' => "\xC2\xB8", 'cent' => "\xC2\xA2", 'cent;' => "\xC2\xA2", 'Chi;' => "\xCE\xA7", 'chi;' => "\xCF\x87", 'circ;' => "\xCB\x86", 'clubs;' => "\xE2\x99\xA3", 'cong;' => "\xE2\x89\x85", 'COPY' => "\xC2\xA9", 'copy' => "\xC2\xA9", 'COPY;' => "\xC2\xA9", 'copy;' => "\xC2\xA9", 'crarr;' => "\xE2\x86\xB5", 'cup;' => "\xE2\x88\xAA", 'curren' => "\xC2\xA4", 'curren;' => "\xC2\xA4", 'Dagger;' => "\xE2\x80\xA1", 'dagger;' => "\xE2\x80\xA0", 'dArr;' => "\xE2\x87\x93", 'darr;' => "\xE2\x86\x93", 'deg' => "\xC2\xB0", 'deg;' => "\xC2\xB0", 'Delta;' => "\xCE\x94", 'delta;' => "\xCE\xB4", 'diams;' => "\xE2\x99\xA6", 'divide' => "\xC3\xB7", 'divide;' => "\xC3\xB7", 'Eacute' => "\xC3\x89", 'eacute' => "\xC3\xA9", 'Eacute;' => "\xC3\x89", 'eacute;' => "\xC3\xA9", 'Ecirc' => "\xC3\x8A", 'ecirc' => "\xC3\xAA", 'Ecirc;' => "\xC3\x8A", 'ecirc;' => "\xC3\xAA", 'Egrave' => "\xC3\x88", 'egrave' => "\xC3\xA8", 'Egrave;' => "\xC3\x88", 'egrave;' => "\xC3\xA8", 'empty;' => "\xE2\x88\x85", 'emsp;' => "\xE2\x80\x83", 'ensp;' => "\xE2\x80\x82", 'Epsilon;' => "\xCE\x95", 'epsilon;' => "\xCE\xB5", 'equiv;' => "\xE2\x89\xA1", 'Eta;' => "\xCE\x97", 'eta;' => "\xCE\xB7", 'ETH' => "\xC3\x90", 'eth' => "\xC3\xB0", 'ETH;' => "\xC3\x90", 'eth;' => "\xC3\xB0", 'Euml' => "\xC3\x8B", 'euml' => "\xC3\xAB", 'Euml;' => "\xC3\x8B", 'euml;' => "\xC3\xAB", 'euro;' => "\xE2\x82\xAC", 'exist;' => "\xE2\x88\x83", 'fnof;' => "\xC6\x92", 'forall;' => "\xE2\x88\x80", 'frac12' => "\xC2\xBD", 'frac12;' => "\xC2\xBD", 'frac14' => "\xC2\xBC", 'frac14;' => "\xC2\xBC", 'frac34' => "\xC2\xBE", 'frac34;' => "\xC2\xBE", 'frasl;' => "\xE2\x81\x84", 'Gamma;' => "\xCE\x93", 'gamma;' => "\xCE\xB3", 'ge;' => "\xE2\x89\xA5", 'GT' => "\x3E", 'gt' => "\x3E", 'GT;' => "\x3E", 'gt;' => "\x3E", 'hArr;' => "\xE2\x87\x94", 'harr;' => "\xE2\x86\x94", 'hearts;' => "\xE2\x99\xA5", 'hellip;' => "\xE2\x80\xA6", 'Iacute' => "\xC3\x8D", 'iacute' => "\xC3\xAD", 'Iacute;' => "\xC3\x8D", 'iacute;' => "\xC3\xAD", 'Icirc' => "\xC3\x8E", 'icirc' => "\xC3\xAE", 'Icirc;' => "\xC3\x8E", 'icirc;' => "\xC3\xAE", 'iexcl' => "\xC2\xA1", 'iexcl;' => "\xC2\xA1", 'Igrave' => "\xC3\x8C", 'igrave' => "\xC3\xAC", 'Igrave;' => "\xC3\x8C", 'igrave;' => "\xC3\xAC", 'image;' => "\xE2\x84\x91", 'infin;' => "\xE2\x88\x9E", 'int;' => "\xE2\x88\xAB", 'Iota;' => "\xCE\x99", 'iota;' => "\xCE\xB9", 'iquest' => "\xC2\xBF", 'iquest;' => "\xC2\xBF", 'isin;' => "\xE2\x88\x88", 'Iuml' => "\xC3\x8F", 'iuml' => "\xC3\xAF", 'Iuml;' => "\xC3\x8F", 'iuml;' => "\xC3\xAF", 'Kappa;' => "\xCE\x9A", 'kappa;' => "\xCE\xBA", 'Lambda;' => "\xCE\x9B", 'lambda;' => "\xCE\xBB", 'lang;' => "\xE3\x80\x88", 'laquo' => "\xC2\xAB", 'laquo;' => "\xC2\xAB", 'lArr;' => "\xE2\x87\x90", 'larr;' => "\xE2\x86\x90", 'lceil;' => "\xE2\x8C\x88", 'ldquo;' => "\xE2\x80\x9C", 'le;' => "\xE2\x89\xA4", 'lfloor;' => "\xE2\x8C\x8A", 'lowast;' => "\xE2\x88\x97", 'loz;' => "\xE2\x97\x8A", 'lrm;' => "\xE2\x80\x8E", 'lsaquo;' => "\xE2\x80\xB9", 'lsquo;' => "\xE2\x80\x98", 'LT' => "\x3C", 'lt' => "\x3C", 'LT;' => "\x3C", 'lt;' => "\x3C", 'macr' => "\xC2\xAF", 'macr;' => "\xC2\xAF", 'mdash;' => "\xE2\x80\x94", 'micro' => "\xC2\xB5", 'micro;' => "\xC2\xB5", 'middot' => "\xC2\xB7", 'middot;' => "\xC2\xB7", 'minus;' => "\xE2\x88\x92", 'Mu;' => "\xCE\x9C", 'mu;' => "\xCE\xBC", 'nabla;' => "\xE2\x88\x87", 'nbsp' => "\xC2\xA0", 'nbsp;' => "\xC2\xA0", 'ndash;' => "\xE2\x80\x93", 'ne;' => "\xE2\x89\xA0", 'ni;' => "\xE2\x88\x8B", 'not' => "\xC2\xAC", 'not;' => "\xC2\xAC", 'notin;' => "\xE2\x88\x89", 'nsub;' => "\xE2\x8A\x84", 'Ntilde' => "\xC3\x91", 'ntilde' => "\xC3\xB1", 'Ntilde;' => "\xC3\x91", 'ntilde;' => "\xC3\xB1", 'Nu;' => "\xCE\x9D", 'nu;' => "\xCE\xBD", 'Oacute' => "\xC3\x93", 'oacute' => "\xC3\xB3", 'Oacute;' => "\xC3\x93", 'oacute;' => "\xC3\xB3", 'Ocirc' => "\xC3\x94", 'ocirc' => "\xC3\xB4", 'Ocirc;' => "\xC3\x94", 'ocirc;' => "\xC3\xB4", 'OElig;' => "\xC5\x92", 'oelig;' => "\xC5\x93", 'Ograve' => "\xC3\x92", 'ograve' => "\xC3\xB2", 'Ograve;' => "\xC3\x92", 'ograve;' => "\xC3\xB2", 'oline;' => "\xE2\x80\xBE", 'Omega;' => "\xCE\xA9", 'omega;' => "\xCF\x89", 'Omicron;' => "\xCE\x9F", 'omicron;' => "\xCE\xBF", 'oplus;' => "\xE2\x8A\x95", 'or;' => "\xE2\x88\xA8", 'ordf' => "\xC2\xAA", 'ordf;' => "\xC2\xAA", 'ordm' => "\xC2\xBA", 'ordm;' => "\xC2\xBA", 'Oslash' => "\xC3\x98", 'oslash' => "\xC3\xB8", 'Oslash;' => "\xC3\x98", 'oslash;' => "\xC3\xB8", 'Otilde' => "\xC3\x95", 'otilde' => "\xC3\xB5", 'Otilde;' => "\xC3\x95", 'otilde;' => "\xC3\xB5", 'otimes;' => "\xE2\x8A\x97", 'Ouml' => "\xC3\x96", 'ouml' => "\xC3\xB6", 'Ouml;' => "\xC3\x96", 'ouml;' => "\xC3\xB6", 'para' => "\xC2\xB6", 'para;' => "\xC2\xB6", 'part;' => "\xE2\x88\x82", 'permil;' => "\xE2\x80\xB0", 'perp;' => "\xE2\x8A\xA5", 'Phi;' => "\xCE\xA6", 'phi;' => "\xCF\x86", 'Pi;' => "\xCE\xA0", 'pi;' => "\xCF\x80", 'piv;' => "\xCF\x96", 'plusmn' => "\xC2\xB1", 'plusmn;' => "\xC2\xB1", 'pound' => "\xC2\xA3", 'pound;' => "\xC2\xA3", 'Prime;' => "\xE2\x80\xB3", 'prime;' => "\xE2\x80\xB2", 'prod;' => "\xE2\x88\x8F", 'prop;' => "\xE2\x88\x9D", 'Psi;' => "\xCE\xA8", 'psi;' => "\xCF\x88", 'QUOT' => "\x22", 'quot' => "\x22", 'QUOT;' => "\x22", 'quot;' => "\x22", 'radic;' => "\xE2\x88\x9A", 'rang;' => "\xE3\x80\x89", 'raquo' => "\xC2\xBB", 'raquo;' => "\xC2\xBB", 'rArr;' => "\xE2\x87\x92", 'rarr;' => "\xE2\x86\x92", 'rceil;' => "\xE2\x8C\x89", 'rdquo;' => "\xE2\x80\x9D", 'real;' => "\xE2\x84\x9C", 'REG' => "\xC2\xAE", 'reg' => "\xC2\xAE", 'REG;' => "\xC2\xAE", 'reg;' => "\xC2\xAE", 'rfloor;' => "\xE2\x8C\x8B", 'Rho;' => "\xCE\xA1", 'rho;' => "\xCF\x81", 'rlm;' => "\xE2\x80\x8F", 'rsaquo;' => "\xE2\x80\xBA", 'rsquo;' => "\xE2\x80\x99", 'sbquo;' => "\xE2\x80\x9A", 'Scaron;' => "\xC5\xA0", 'scaron;' => "\xC5\xA1", 'sdot;' => "\xE2\x8B\x85", 'sect' => "\xC2\xA7", 'sect;' => "\xC2\xA7", 'shy' => "\xC2\xAD", 'shy;' => "\xC2\xAD", 'Sigma;' => "\xCE\xA3", 'sigma;' => "\xCF\x83", 'sigmaf;' => "\xCF\x82", 'sim;' => "\xE2\x88\xBC", 'spades;' => "\xE2\x99\xA0", 'sub;' => "\xE2\x8A\x82", 'sube;' => "\xE2\x8A\x86", 'sum;' => "\xE2\x88\x91", 'sup;' => "\xE2\x8A\x83", 'sup1' => "\xC2\xB9", 'sup1;' => "\xC2\xB9", 'sup2' => "\xC2\xB2", 'sup2;' => "\xC2\xB2", 'sup3' => "\xC2\xB3", 'sup3;' => "\xC2\xB3", 'supe;' => "\xE2\x8A\x87", 'szlig' => "\xC3\x9F", 'szlig;' => "\xC3\x9F", 'Tau;' => "\xCE\xA4", 'tau;' => "\xCF\x84", 'there4;' => "\xE2\x88\xB4", 'Theta;' => "\xCE\x98", 'theta;' => "\xCE\xB8", 'thetasym;' => "\xCF\x91", 'thinsp;' => "\xE2\x80\x89", 'THORN' => "\xC3\x9E", 'thorn' => "\xC3\xBE", 'THORN;' => "\xC3\x9E", 'thorn;' => "\xC3\xBE", 'tilde;' => "\xCB\x9C", 'times' => "\xC3\x97", 'times;' => "\xC3\x97", 'TRADE;' => "\xE2\x84\xA2", 'trade;' => "\xE2\x84\xA2", 'Uacute' => "\xC3\x9A", 'uacute' => "\xC3\xBA", 'Uacute;' => "\xC3\x9A", 'uacute;' => "\xC3\xBA", 'uArr;' => "\xE2\x87\x91", 'uarr;' => "\xE2\x86\x91", 'Ucirc' => "\xC3\x9B", 'ucirc' => "\xC3\xBB", 'Ucirc;' => "\xC3\x9B", 'ucirc;' => "\xC3\xBB", 'Ugrave' => "\xC3\x99", 'ugrave' => "\xC3\xB9", 'Ugrave;' => "\xC3\x99", 'ugrave;' => "\xC3\xB9", 'uml' => "\xC2\xA8", 'uml;' => "\xC2\xA8", 'upsih;' => "\xCF\x92", 'Upsilon;' => "\xCE\xA5", 'upsilon;' => "\xCF\x85", 'Uuml' => "\xC3\x9C", 'uuml' => "\xC3\xBC", 'Uuml;' => "\xC3\x9C", 'uuml;' => "\xC3\xBC", 'weierp;' => "\xE2\x84\x98", 'Xi;' => "\xCE\x9E", 'xi;' => "\xCE\xBE", 'Yacute' => "\xC3\x9D", 'yacute' => "\xC3\xBD", 'Yacute;' => "\xC3\x9D", 'yacute;' => "\xC3\xBD", 'yen' => "\xC2\xA5", 'yen;' => "\xC2\xA5", 'yuml' => "\xC3\xBF", 'Yuml;' => "\xC5\xB8", 'yuml;' => "\xC3\xBF", 'Zeta;' => "\xCE\x96", 'zeta;' => "\xCE\xB6", 'zwj;' => "\xE2\x80\x8D", 'zwnj;' => "\xE2\x80\x8C"); - - for ($i = 0, $match = null; $i < 9 && $this->consume() !== false; $i++) - { - $consumed = substr($this->consumed, 1); - if (isset($entities[$consumed])) - { - $match = $consumed; - } - } - - if ($match !== null) - { - $this->data = substr_replace($this->data, $entities[$match], $this->position - strlen($consumed) - 1, strlen($match) + 1); - $this->position += strlen($entities[$match]) - strlen($consumed) - 1; - } - break; - } - } -} - -/** - * Date Parser - * - * @package SimplePie - */ -class SimplePie_Parse_Date -{ - /** - * Input data - * - * @access protected - * @var string - */ - var $date; - - /** - * List of days, calendar day name => ordinal day number in the week - * - * @access protected - * @var array - */ - var $day = array( - // English - 'mon' => 1, - 'monday' => 1, - 'tue' => 2, - 'tuesday' => 2, - 'wed' => 3, - 'wednesday' => 3, - 'thu' => 4, - 'thursday' => 4, - 'fri' => 5, - 'friday' => 5, - 'sat' => 6, - 'saturday' => 6, - 'sun' => 7, - 'sunday' => 7, - // Dutch - 'maandag' => 1, - 'dinsdag' => 2, - 'woensdag' => 3, - 'donderdag' => 4, - 'vrijdag' => 5, - 'zaterdag' => 6, - 'zondag' => 7, - // French - 'lundi' => 1, - 'mardi' => 2, - 'mercredi' => 3, - 'jeudi' => 4, - 'vendredi' => 5, - 'samedi' => 6, - 'dimanche' => 7, - // German - 'montag' => 1, - 'dienstag' => 2, - 'mittwoch' => 3, - 'donnerstag' => 4, - 'freitag' => 5, - 'samstag' => 6, - 'sonnabend' => 6, - 'sonntag' => 7, - // Italian - 'lunedì' => 1, - 'martedì' => 2, - 'mercoledì' => 3, - 'giovedì' => 4, - 'venerdì' => 5, - 'sabato' => 6, - 'domenica' => 7, - // Spanish - 'lunes' => 1, - 'martes' => 2, - 'miércoles' => 3, - 'jueves' => 4, - 'viernes' => 5, - 'sábado' => 6, - 'domingo' => 7, - // Finnish - 'maanantai' => 1, - 'tiistai' => 2, - 'keskiviikko' => 3, - 'torstai' => 4, - 'perjantai' => 5, - 'lauantai' => 6, - 'sunnuntai' => 7, - // Hungarian - 'hétfÅ‘' => 1, - 'kedd' => 2, - 'szerda' => 3, - 'csütörtok' => 4, - 'péntek' => 5, - 'szombat' => 6, - 'vasárnap' => 7, - // Greek - 'Δευ' => 1, - 'ΤÏι' => 2, - 'Τετ' => 3, - 'Πεμ' => 4, - 'ΠαÏ' => 5, - 'Σαβ' => 6, - 'ΚυÏ' => 7, - ); - - /** - * List of months, calendar month name => calendar month number - * - * @access protected - * @var array - */ - var $month = array( - // English - 'jan' => 1, - 'january' => 1, - 'feb' => 2, - 'february' => 2, - 'mar' => 3, - 'march' => 3, - 'apr' => 4, - 'april' => 4, - 'may' => 5, - // No long form of May - 'jun' => 6, - 'june' => 6, - 'jul' => 7, - 'july' => 7, - 'aug' => 8, - 'august' => 8, - 'sep' => 9, - 'september' => 8, - 'oct' => 10, - 'october' => 10, - 'nov' => 11, - 'november' => 11, - 'dec' => 12, - 'december' => 12, - // Dutch - 'januari' => 1, - 'februari' => 2, - 'maart' => 3, - 'april' => 4, - 'mei' => 5, - 'juni' => 6, - 'juli' => 7, - 'augustus' => 8, - 'september' => 9, - 'oktober' => 10, - 'november' => 11, - 'december' => 12, - // French - 'janvier' => 1, - 'février' => 2, - 'mars' => 3, - 'avril' => 4, - 'mai' => 5, - 'juin' => 6, - 'juillet' => 7, - 'août' => 8, - 'septembre' => 9, - 'octobre' => 10, - 'novembre' => 11, - 'décembre' => 12, - // German - 'januar' => 1, - 'februar' => 2, - 'märz' => 3, - 'april' => 4, - 'mai' => 5, - 'juni' => 6, - 'juli' => 7, - 'august' => 8, - 'september' => 9, - 'oktober' => 10, - 'november' => 11, - 'dezember' => 12, - // Italian - 'gennaio' => 1, - 'febbraio' => 2, - 'marzo' => 3, - 'aprile' => 4, - 'maggio' => 5, - 'giugno' => 6, - 'luglio' => 7, - 'agosto' => 8, - 'settembre' => 9, - 'ottobre' => 10, - 'novembre' => 11, - 'dicembre' => 12, - // Spanish - 'enero' => 1, - 'febrero' => 2, - 'marzo' => 3, - 'abril' => 4, - 'mayo' => 5, - 'junio' => 6, - 'julio' => 7, - 'agosto' => 8, - 'septiembre' => 9, - 'setiembre' => 9, - 'octubre' => 10, - 'noviembre' => 11, - 'diciembre' => 12, - // Finnish - 'tammikuu' => 1, - 'helmikuu' => 2, - 'maaliskuu' => 3, - 'huhtikuu' => 4, - 'toukokuu' => 5, - 'kesäkuu' => 6, - 'heinäkuu' => 7, - 'elokuu' => 8, - 'suuskuu' => 9, - 'lokakuu' => 10, - 'marras' => 11, - 'joulukuu' => 12, - // Hungarian - 'január' => 1, - 'február' => 2, - 'március' => 3, - 'április' => 4, - 'május' => 5, - 'június' => 6, - 'július' => 7, - 'augusztus' => 8, - 'szeptember' => 9, - 'október' => 10, - 'november' => 11, - 'december' => 12, - // Greek - 'Ιαν' => 1, - 'Φεβ' => 2, - 'Μάώ' => 3, - 'Μαώ' => 3, - 'ΑπÏ' => 4, - 'Μάι' => 5, - 'Μαϊ' => 5, - 'Μαι' => 5, - 'ΙοÏν' => 6, - 'Ιον' => 6, - 'ΙοÏλ' => 7, - 'Ιολ' => 7, - 'ΑÏγ' => 8, - 'Αυγ' => 8, - 'Σεπ' => 9, - 'Οκτ' => 10, - 'Îοέ' => 11, - 'Δεκ' => 12, - ); - - /** - * List of timezones, abbreviation => offset from UTC - * - * @access protected - * @var array - */ - var $timezone = array( - 'ACDT' => 37800, - 'ACIT' => 28800, - 'ACST' => 34200, - 'ACT' => -18000, - 'ACWDT' => 35100, - 'ACWST' => 31500, - 'AEDT' => 39600, - 'AEST' => 36000, - 'AFT' => 16200, - 'AKDT' => -28800, - 'AKST' => -32400, - 'AMDT' => 18000, - 'AMT' => -14400, - 'ANAST' => 46800, - 'ANAT' => 43200, - 'ART' => -10800, - 'AZOST' => -3600, - 'AZST' => 18000, - 'AZT' => 14400, - 'BIOT' => 21600, - 'BIT' => -43200, - 'BOT' => -14400, - 'BRST' => -7200, - 'BRT' => -10800, - 'BST' => 3600, - 'BTT' => 21600, - 'CAST' => 18000, - 'CAT' => 7200, - 'CCT' => 23400, - 'CDT' => -18000, - 'CEDT' => 7200, - 'CET' => 3600, - 'CGST' => -7200, - 'CGT' => -10800, - 'CHADT' => 49500, - 'CHAST' => 45900, - 'CIST' => -28800, - 'CKT' => -36000, - 'CLDT' => -10800, - 'CLST' => -14400, - 'COT' => -18000, - 'CST' => -21600, - 'CVT' => -3600, - 'CXT' => 25200, - 'DAVT' => 25200, - 'DTAT' => 36000, - 'EADT' => -18000, - 'EAST' => -21600, - 'EAT' => 10800, - 'ECT' => -18000, - 'EDT' => -14400, - 'EEST' => 10800, - 'EET' => 7200, - 'EGT' => -3600, - 'EKST' => 21600, - 'EST' => -18000, - 'FJT' => 43200, - 'FKDT' => -10800, - 'FKST' => -14400, - 'FNT' => -7200, - 'GALT' => -21600, - 'GEDT' => 14400, - 'GEST' => 10800, - 'GFT' => -10800, - 'GILT' => 43200, - 'GIT' => -32400, - 'GST' => 14400, - 'GST' => -7200, - 'GYT' => -14400, - 'HAA' => -10800, - 'HAC' => -18000, - 'HADT' => -32400, - 'HAE' => -14400, - 'HAP' => -25200, - 'HAR' => -21600, - 'HAST' => -36000, - 'HAT' => -9000, - 'HAY' => -28800, - 'HKST' => 28800, - 'HMT' => 18000, - 'HNA' => -14400, - 'HNC' => -21600, - 'HNE' => -18000, - 'HNP' => -28800, - 'HNR' => -25200, - 'HNT' => -12600, - 'HNY' => -32400, - 'IRDT' => 16200, - 'IRKST' => 32400, - 'IRKT' => 28800, - 'IRST' => 12600, - 'JFDT' => -10800, - 'JFST' => -14400, - 'JST' => 32400, - 'KGST' => 21600, - 'KGT' => 18000, - 'KOST' => 39600, - 'KOVST' => 28800, - 'KOVT' => 25200, - 'KRAST' => 28800, - 'KRAT' => 25200, - 'KST' => 32400, - 'LHDT' => 39600, - 'LHST' => 37800, - 'LINT' => 50400, - 'LKT' => 21600, - 'MAGST' => 43200, - 'MAGT' => 39600, - 'MAWT' => 21600, - 'MDT' => -21600, - 'MESZ' => 7200, - 'MEZ' => 3600, - 'MHT' => 43200, - 'MIT' => -34200, - 'MNST' => 32400, - 'MSDT' => 14400, - 'MSST' => 10800, - 'MST' => -25200, - 'MUT' => 14400, - 'MVT' => 18000, - 'MYT' => 28800, - 'NCT' => 39600, - 'NDT' => -9000, - 'NFT' => 41400, - 'NMIT' => 36000, - 'NOVST' => 25200, - 'NOVT' => 21600, - 'NPT' => 20700, - 'NRT' => 43200, - 'NST' => -12600, - 'NUT' => -39600, - 'NZDT' => 46800, - 'NZST' => 43200, - 'OMSST' => 25200, - 'OMST' => 21600, - 'PDT' => -25200, - 'PET' => -18000, - 'PETST' => 46800, - 'PETT' => 43200, - 'PGT' => 36000, - 'PHOT' => 46800, - 'PHT' => 28800, - 'PKT' => 18000, - 'PMDT' => -7200, - 'PMST' => -10800, - 'PONT' => 39600, - 'PST' => -28800, - 'PWT' => 32400, - 'PYST' => -10800, - 'PYT' => -14400, - 'RET' => 14400, - 'ROTT' => -10800, - 'SAMST' => 18000, - 'SAMT' => 14400, - 'SAST' => 7200, - 'SBT' => 39600, - 'SCDT' => 46800, - 'SCST' => 43200, - 'SCT' => 14400, - 'SEST' => 3600, - 'SGT' => 28800, - 'SIT' => 28800, - 'SRT' => -10800, - 'SST' => -39600, - 'SYST' => 10800, - 'SYT' => 7200, - 'TFT' => 18000, - 'THAT' => -36000, - 'TJT' => 18000, - 'TKT' => -36000, - 'TMT' => 18000, - 'TOT' => 46800, - 'TPT' => 32400, - 'TRUT' => 36000, - 'TVT' => 43200, - 'TWT' => 28800, - 'UYST' => -7200, - 'UYT' => -10800, - 'UZT' => 18000, - 'VET' => -14400, - 'VLAST' => 39600, - 'VLAT' => 36000, - 'VOST' => 21600, - 'VUT' => 39600, - 'WAST' => 7200, - 'WAT' => 3600, - 'WDT' => 32400, - 'WEST' => 3600, - 'WFT' => 43200, - 'WIB' => 25200, - 'WIT' => 32400, - 'WITA' => 28800, - 'WKST' => 18000, - 'WST' => 28800, - 'YAKST' => 36000, - 'YAKT' => 32400, - 'YAPT' => 36000, - 'YEKST' => 21600, - 'YEKT' => 18000, - ); - - /** - * Cached PCRE for SimplePie_Parse_Date::$day - * - * @access protected - * @var string - */ - var $day_pcre; - - /** - * Cached PCRE for SimplePie_Parse_Date::$month - * - * @access protected - * @var string - */ - var $month_pcre; - - /** - * Array of user-added callback methods - * - * @access private - * @var array - */ - var $built_in = array(); - - /** - * Array of user-added callback methods - * - * @access private - * @var array - */ - var $user = array(); - - /** - * Create new SimplePie_Parse_Date object, and set self::day_pcre, - * self::month_pcre, and self::built_in - * - * @access private - */ - function SimplePie_Parse_Date() - { - $this->day_pcre = '(' . implode(array_keys($this->day), '|') . ')'; - $this->month_pcre = '(' . implode(array_keys($this->month), '|') . ')'; - - static $cache; - if (!isset($cache[get_class($this)])) - { - if (extension_loaded('Reflection')) - { - $class = new ReflectionClass(get_class($this)); - $methods = $class->getMethods(); - $all_methods = array(); - foreach ($methods as $method) - { - $all_methods[] = $method->getName(); - } - } - else - { - $all_methods = get_class_methods($this); - } - - foreach ($all_methods as $method) - { - if (strtolower(substr($method, 0, 5)) === 'date_') - { - $cache[get_class($this)][] = $method; - } - } - } - - foreach ($cache[get_class($this)] as $method) - { - $this->built_in[] = $method; - } - } - - /** - * Get the object - * - * @access public - */ - function get() - { - static $object; - if (!$object) - { - $object = new SimplePie_Parse_Date; - } - return $object; - } - - /** - * Parse a date - * - * @final - * @access public - * @param string $date Date to parse - * @return int Timestamp corresponding to date string, or false on failure - */ - function parse($date) - { - foreach ($this->user as $method) - { - if (($returned = call_user_func($method, $date)) !== false) - { - return $returned; - } - } - - foreach ($this->built_in as $method) - { - if (($returned = call_user_func(array(&$this, $method), $date)) !== false) - { - return $returned; - } - } - - return false; - } - - /** - * Add a callback method to parse a date - * - * @final - * @access public - * @param callback $callback - */ - function add_callback($callback) - { - if (is_callable($callback)) - { - $this->user[] = $callback; - } - else - { - trigger_error('User-supplied function must be a valid callback', E_USER_WARNING); - } - } - - /** - * Parse a superset of W3C-DTF (allows hyphens and colons to be omitted, as - * well as allowing any of upper or lower case "T", horizontal tabs, or - * spaces to be used as the time seperator (including more than one)) - * - * @access protected - * @return int Timestamp - */ - function date_w3cdtf($date) - { - static $pcre; - if (!$pcre) - { - $year = '([0-9]{4})'; - $month = $day = $hour = $minute = $second = '([0-9]{2})'; - $decimal = '([0-9]*)'; - $zone = '(?:(Z)|([+\-])([0-9]{1,2}):?([0-9]{1,2}))'; - $pcre = '/^' . $year . '(?:-?' . $month . '(?:-?' . $day . '(?:[Tt\x09\x20]+' . $hour . '(?::?' . $minute . '(?::?' . $second . '(?:.' . $decimal . ')?)?)?' . $zone . ')?)?)?$/'; - } - if (preg_match($pcre, $date, $match)) - { - /* - Capturing subpatterns: - 1: Year - 2: Month - 3: Day - 4: Hour - 5: Minute - 6: Second - 7: Decimal fraction of a second - 8: Zulu - 9: Timezone ± - 10: Timezone hours - 11: Timezone minutes - */ - - // Fill in empty matches - for ($i = count($match); $i <= 3; $i++) - { - $match[$i] = '1'; - } - - for ($i = count($match); $i <= 7; $i++) - { - $match[$i] = '0'; - } - - // Numeric timezone - if (isset($match[9]) && $match[9] !== '') - { - $timezone = $match[10] * 3600; - $timezone += $match[11] * 60; - if ($match[9] === '-') - { - $timezone = 0 - $timezone; - } - } - else - { - $timezone = 0; - } - - // Convert the number of seconds to an integer, taking decimals into account - $second = round($match[6] + $match[7] / pow(10, strlen($match[7]))); - - return gmmktime($match[4], $match[5], $second, $match[2], $match[3], $match[1]) - $timezone; - } - else - { - return false; - } - } - - /** - * Remove RFC822 comments - * - * @access protected - * @param string $data Data to strip comments from - * @return string Comment stripped string - */ - function remove_rfc2822_comments($string) - { - $string = (string) $string; - $position = 0; - $length = strlen($string); - $depth = 0; - - $output = ''; - - while ($position < $length && ($pos = strpos($string, '(', $position)) !== false) - { - $output .= substr($string, $position, $pos - $position); - $position = $pos + 1; - if ($string[$pos - 1] !== '\\') - { - $depth++; - while ($depth && $position < $length) - { - $position += strcspn($string, '()', $position); - if ($string[$position - 1] === '\\') - { - $position++; - continue; - } - elseif (isset($string[$position])) - { - switch ($string[$position]) - { - case '(': - $depth++; - break; - - case ')': - $depth--; - break; - } - $position++; - } - else - { - break; - } - } - } - else - { - $output .= '('; - } - } - $output .= substr($string, $position); - - return $output; - } - - /** - * Parse RFC2822's date format - * - * @access protected - * @return int Timestamp - */ - function date_rfc2822($date) - { - static $pcre; - if (!$pcre) - { - $wsp = '[\x09\x20]'; - $fws = '(?:' . $wsp . '+|' . $wsp . '*(?:\x0D\x0A' . $wsp . '+)+)'; - $optional_fws = $fws . '?'; - $day_name = $this->day_pcre; - $month = $this->month_pcre; - $day = '([0-9]{1,2})'; - $hour = $minute = $second = '([0-9]{2})'; - $year = '([0-9]{2,4})'; - $num_zone = '([+\-])([0-9]{2})([0-9]{2})'; - $character_zone = '([A-Z]{1,5})'; - $zone = '(?:' . $num_zone . '|' . $character_zone . ')'; - $pcre = '/(?:' . $optional_fws . $day_name . $optional_fws . ',)?' . $optional_fws . $day . $fws . $month . $fws . $year . $fws . $hour . $optional_fws . ':' . $optional_fws . $minute . '(?:' . $optional_fws . ':' . $optional_fws . $second . ')?' . $fws . $zone . '/i'; - } - if (preg_match($pcre, $this->remove_rfc2822_comments($date), $match)) - { - /* - Capturing subpatterns: - 1: Day name - 2: Day - 3: Month - 4: Year - 5: Hour - 6: Minute - 7: Second - 8: Timezone ± - 9: Timezone hours - 10: Timezone minutes - 11: Alphabetic timezone - */ - - // Find the month number - $month = $this->month[strtolower($match[3])]; - - // Numeric timezone - if ($match[8] !== '') - { - $timezone = $match[9] * 3600; - $timezone += $match[10] * 60; - if ($match[8] === '-') - { - $timezone = 0 - $timezone; - } - } - // Character timezone - elseif (isset($this->timezone[strtoupper($match[11])])) - { - $timezone = $this->timezone[strtoupper($match[11])]; - } - // Assume everything else to be -0000 - else - { - $timezone = 0; - } - - // Deal with 2/3 digit years - if ($match[4] < 50) - { - $match[4] += 2000; - } - elseif ($match[4] < 1000) - { - $match[4] += 1900; - } - - // Second is optional, if it is empty set it to zero - if ($match[7] !== '') - { - $second = $match[7]; - } - else - { - $second = 0; - } - - return gmmktime($match[5], $match[6], $second, $month, $match[2], $match[4]) - $timezone; - } - else - { - return false; - } - } - - /** - * Parse RFC850's date format - * - * @access protected - * @return int Timestamp - */ - function date_rfc850($date) - { - static $pcre; - if (!$pcre) - { - $space = '[\x09\x20]+'; - $day_name = $this->day_pcre; - $month = $this->month_pcre; - $day = '([0-9]{1,2})'; - $year = $hour = $minute = $second = '([0-9]{2})'; - $zone = '([A-Z]{1,5})'; - $pcre = '/^' . $day_name . ',' . $space . $day . '-' . $month . '-' . $year . $space . $hour . ':' . $minute . ':' . $second . $space . $zone . '$/i'; - } - if (preg_match($pcre, $date, $match)) - { - /* - Capturing subpatterns: - 1: Day name - 2: Day - 3: Month - 4: Year - 5: Hour - 6: Minute - 7: Second - 8: Timezone - */ - - // Month - $month = $this->month[strtolower($match[3])]; - - // Character timezone - if (isset($this->timezone[strtoupper($match[8])])) - { - $timezone = $this->timezone[strtoupper($match[8])]; - } - // Assume everything else to be -0000 - else - { - $timezone = 0; - } - - // Deal with 2 digit year - if ($match[4] < 50) - { - $match[4] += 2000; - } - else - { - $match[4] += 1900; - } - - return gmmktime($match[5], $match[6], $match[7], $month, $match[2], $match[4]) - $timezone; - } - else - { - return false; - } - } - - /** - * Parse C99's asctime()'s date format - * - * @access protected - * @return int Timestamp - */ - function date_asctime($date) - { - static $pcre; - if (!$pcre) - { - $space = '[\x09\x20]+'; - $wday_name = $this->day_pcre; - $mon_name = $this->month_pcre; - $day = '([0-9]{1,2})'; - $hour = $sec = $min = '([0-9]{2})'; - $year = '([0-9]{4})'; - $terminator = '\x0A?\x00?'; - $pcre = '/^' . $wday_name . $space . $mon_name . $space . $day . $space . $hour . ':' . $min . ':' . $sec . $space . $year . $terminator . '$/i'; - } - if (preg_match($pcre, $date, $match)) - { - /* - Capturing subpatterns: - 1: Day name - 2: Month - 3: Day - 4: Hour - 5: Minute - 6: Second - 7: Year - */ - - $month = $this->month[strtolower($match[2])]; - return gmmktime($match[4], $match[5], $match[6], $month, $match[3], $match[7]); - } - else - { - return false; - } - } - - /** - * Parse dates using strtotime() - * - * @access protected - * @return int Timestamp - */ - function date_strtotime($date) - { - $strtotime = strtotime($date); - if ($strtotime === -1 || $strtotime === false) - { - return false; - } - else - { - return $strtotime; - } - } -} - -/** - * Content-type sniffing - * - * @package SimplePie - */ -class SimplePie_Content_Type_Sniffer -{ - /** - * File object - * - * @var SimplePie_File - * @access private - */ - var $file; - - /** - * Create an instance of the class with the input file - * - * @access public - * @param SimplePie_Content_Type_Sniffer $file Input file - */ - function SimplePie_Content_Type_Sniffer($file) - { - $this->file = $file; - } - - /** - * Get the Content-Type of the specified file - * - * @access public - * @return string Actual Content-Type - */ - function get_type() - { - if (isset($this->file->headers['content-type'])) - { - if (!isset($this->file->headers['content-encoding']) - && ($this->file->headers['content-type'] === 'text/plain' - || $this->file->headers['content-type'] === 'text/plain; charset=ISO-8859-1' - || $this->file->headers['content-type'] === 'text/plain; charset=iso-8859-1')) - { - return $this->text_or_binary(); - } - - if (($pos = strpos($this->file->headers['content-type'], ';')) !== false) - { - $official = substr($this->file->headers['content-type'], 0, $pos); - } - else - { - $official = $this->file->headers['content-type']; - } - $official = strtolower($official); - - if ($official === 'unknown/unknown' - || $official === 'application/unknown') - { - return $this->unknown(); - } - elseif (substr($official, -4) === '+xml' - || $official === 'text/xml' - || $official === 'application/xml') - { - return $official; - } - elseif (substr($official, 0, 6) === 'image/') - { - if ($return = $this->image()) - { - return $return; - } - else - { - return $official; - } - } - elseif ($official === 'text/html') - { - return $this->feed_or_html(); - } - else - { - return $official; - } - } - else - { - return $this->unknown(); - } - } - - /** - * Sniff text or binary - * - * @access private - * @return string Actual Content-Type - */ - function text_or_binary() - { - if (substr($this->file->body, 0, 2) === "\xFE\xFF" - || substr($this->file->body, 0, 2) === "\xFF\xFE" - || substr($this->file->body, 0, 4) === "\x00\x00\xFE\xFF" - || substr($this->file->body, 0, 3) === "\xEF\xBB\xBF") - { - return 'text/plain'; - } - elseif (preg_match('/[\x00-\x08\x0E-\x1A\x1C-\x1F]/', $this->file->body)) - { - return 'application/octect-stream'; - } - else - { - return 'text/plain'; - } - } - - /** - * Sniff unknown - * - * @access private - * @return string Actual Content-Type - */ - function unknown() - { - $ws = strspn($this->file->body, "\x09\x0A\x0B\x0C\x0D\x20"); - if (strtolower(substr($this->file->body, $ws, 14)) === 'file->body, $ws, 5)) === 'file->body, $ws, 7)) === 'file->body, 0, 5) === '%PDF-') - { - return 'application/pdf'; - } - elseif (substr($this->file->body, 0, 11) === '%!PS-Adobe-') - { - return 'application/postscript'; - } - elseif (substr($this->file->body, 0, 6) === 'GIF87a' - || substr($this->file->body, 0, 6) === 'GIF89a') - { - return 'image/gif'; - } - elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A") - { - return 'image/png'; - } - elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF") - { - return 'image/jpeg'; - } - elseif (substr($this->file->body, 0, 2) === "\x42\x4D") - { - return 'image/bmp'; - } - else - { - return $this->text_or_binary(); - } - } - - /** - * Sniff images - * - * @access private - * @return string Actual Content-Type - */ - function image() - { - if (substr($this->file->body, 0, 6) === 'GIF87a' - || substr($this->file->body, 0, 6) === 'GIF89a') - { - return 'image/gif'; - } - elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A") - { - return 'image/png'; - } - elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF") - { - return 'image/jpeg'; - } - elseif (substr($this->file->body, 0, 2) === "\x42\x4D") - { - return 'image/bmp'; - } - else - { - return false; - } - } - - /** - * Sniff HTML - * - * @access private - * @return string Actual Content-Type - */ - function feed_or_html() - { - $len = strlen($this->file->body); - $pos = strspn($this->file->body, "\x09\x0A\x0D\x20"); - - while ($pos < $len) - { - switch ($this->file->body[$pos]) - { - case "\x09": - case "\x0A": - case "\x0D": - case "\x20": - $pos += strspn($this->file->body, "\x09\x0A\x0D\x20", $pos); - continue 2; - - case '<': - $pos++; - break; - - default: - return 'text/html'; - } - - if (substr($this->file->body, $pos, 3) === '!--') - { - $pos += 3; - if ($pos < $len && ($pos = strpos($this->file->body, '-->', $pos)) !== false) - { - $pos += 3; - } - else - { - return 'text/html'; - } - } - elseif (substr($this->file->body, $pos, 1) === '!') - { - if ($pos < $len && ($pos = strpos($this->file->body, '>', $pos)) !== false) - { - $pos++; - } - else - { - return 'text/html'; - } - } - elseif (substr($this->file->body, $pos, 1) === '?') - { - if ($pos < $len && ($pos = strpos($this->file->body, '?>', $pos)) !== false) - { - $pos += 2; - } - else - { - return 'text/html'; - } - } - elseif (substr($this->file->body, $pos, 3) === 'rss' - || substr($this->file->body, $pos, 7) === 'rdf:RDF') - { - return 'application/rss+xml'; - } - elseif (substr($this->file->body, $pos, 4) === 'feed') - { - return 'application/atom+xml'; - } - else - { - return 'text/html'; - } - } - - return 'text/html'; - } -} - -/** - * Parses the XML Declaration - * - * @package SimplePie - */ -class SimplePie_XML_Declaration_Parser -{ - /** - * XML Version - * - * @access public - * @var string - */ - var $version = '1.0'; - - /** - * Encoding - * - * @access public - * @var string - */ - var $encoding = 'UTF-8'; - - /** - * Standalone - * - * @access public - * @var bool - */ - var $standalone = false; - - /** - * Current state of the state machine - * - * @access private - * @var string - */ - var $state = 'before_version_name'; - - /** - * Input data - * - * @access private - * @var string - */ - var $data = ''; - - /** - * Input data length (to avoid calling strlen() everytime this is needed) - * - * @access private - * @var int - */ - var $data_length = 0; - - /** - * Current position of the pointer - * - * @var int - * @access private - */ - var $position = 0; - - /** - * Create an instance of the class with the input data - * - * @access public - * @param string $data Input data - */ - function SimplePie_XML_Declaration_Parser($data) - { - $this->data = $data; - $this->data_length = strlen($this->data); - } - - /** - * Parse the input data - * - * @access public - * @return bool true on success, false on failure - */ - function parse() - { - while ($this->state && $this->state !== 'emit' && $this->has_data()) - { - $state = $this->state; - $this->$state(); - } - $this->data = ''; - if ($this->state === 'emit') - { - return true; - } - else - { - $this->version = ''; - $this->encoding = ''; - $this->standalone = ''; - return false; - } - } - - /** - * Check whether there is data beyond the pointer - * - * @access private - * @return bool true if there is further data, false if not - */ - function has_data() - { - return (bool) ($this->position < $this->data_length); - } - - /** - * Advance past any whitespace - * - * @return int Number of whitespace characters passed - */ - function skip_whitespace() - { - $whitespace = strspn($this->data, "\x09\x0A\x0D\x20", $this->position); - $this->position += $whitespace; - return $whitespace; - } - - /** - * Read value - */ - function get_value() - { - $quote = substr($this->data, $this->position, 1); - if ($quote === '"' || $quote === "'") - { - $this->position++; - $len = strcspn($this->data, $quote, $this->position); - if ($this->has_data()) - { - $value = substr($this->data, $this->position, $len); - $this->position += $len + 1; - return $value; - } - } - return false; - } - - function before_version_name() - { - if ($this->skip_whitespace()) - { - $this->state = 'version_name'; - } - else - { - $this->state = false; - } - } - - function version_name() - { - if (substr($this->data, $this->position, 7) === 'version') - { - $this->position += 7; - $this->skip_whitespace(); - $this->state = 'version_equals'; - } - else - { - $this->state = false; - } - } - - function version_equals() - { - if (substr($this->data, $this->position, 1) === '=') - { - $this->position++; - $this->skip_whitespace(); - $this->state = 'version_value'; - } - else - { - $this->state = false; - } - } - - function version_value() - { - if ($this->version = $this->get_value()) - { - $this->skip_whitespace(); - if ($this->has_data()) - { - $this->state = 'encoding_name'; - } - else - { - $this->state = 'emit'; - } - } - else - { - $this->state = 'standalone_name'; - } - } - - function encoding_name() - { - if (substr($this->data, $this->position, 8) === 'encoding') - { - $this->position += 8; - $this->skip_whitespace(); - $this->state = 'encoding_equals'; - } - else - { - $this->state = false; - } - } - - function encoding_equals() - { - if (substr($this->data, $this->position, 1) === '=') - { - $this->position++; - $this->skip_whitespace(); - $this->state = 'encoding_value'; - } - else - { - $this->state = false; - } - } - - function encoding_value() - { - if ($this->encoding = $this->get_value()) - { - $this->skip_whitespace(); - if ($this->has_data()) - { - $this->state = 'standalone_name'; - } - else - { - $this->state = 'emit'; - } - } - else - { - $this->state = false; - } - } - - function standalone_name() - { - if (substr($this->data, $this->position, 10) === 'standalone') - { - $this->position += 10; - $this->skip_whitespace(); - $this->state = 'standalone_equals'; - } - else - { - $this->state = false; - } - } - - function standalone_equals() - { - if (substr($this->data, $this->position, 1) === '=') - { - $this->position++; - $this->skip_whitespace(); - $this->state = 'standalone_value'; - } - else - { - $this->state = false; - } - } - - function standalone_value() - { - if ($standalone = $this->get_value()) - { - switch ($standalone) - { - case 'yes': - $this->standalone = true; - break; - - case 'no': - $this->standalone = false; - break; - - default: - $this->state = false; - return; - } - - $this->skip_whitespace(); - if ($this->has_data()) - { - $this->state = false; - } - else - { - $this->state = 'emit'; - } - } - else - { - $this->state = false; - } - } -} - -class SimplePie_Locator -{ - var $useragent; - var $timeout; - var $file; - var $local = array(); - var $elsewhere = array(); - var $file_class = 'SimplePie_File'; - var $cached_entities = array(); - var $http_base; - var $base; - var $base_location = 0; - var $checked_feeds = 0; - var $max_checked_feeds = 10; - var $content_type_sniffer_class = 'SimplePie_Content_Type_Sniffer'; - - function SimplePie_Locator(&$file, $timeout = 10, $useragent = null, $file_class = 'SimplePie_File', $max_checked_feeds = 10, $content_type_sniffer_class = 'SimplePie_Content_Type_Sniffer') - { - $this->file =& $file; - $this->file_class = $file_class; - $this->useragent = $useragent; - $this->timeout = $timeout; - $this->max_checked_feeds = $max_checked_feeds; - $this->content_type_sniffer_class = $content_type_sniffer_class; - } - - function find($type = SIMPLEPIE_LOCATOR_ALL) - { - if ($this->is_feed($this->file)) - { - return $this->file; - } - - if ($this->file->method & SIMPLEPIE_FILE_SOURCE_REMOTE) - { - $sniffer = new $this->content_type_sniffer_class($this->file); - if ($sniffer->get_type() !== 'text/html') - { - return null; - } - } - - if ($type & ~SIMPLEPIE_LOCATOR_NONE) - { - $this->get_base(); - } - - if ($type & SIMPLEPIE_LOCATOR_AUTODISCOVERY && $working = $this->autodiscovery()) - { - return $working; - } - - if ($type & (SIMPLEPIE_LOCATOR_LOCAL_EXTENSION | SIMPLEPIE_LOCATOR_LOCAL_BODY | SIMPLEPIE_LOCATOR_REMOTE_EXTENSION | SIMPLEPIE_LOCATOR_REMOTE_BODY) && $this->get_links()) - { - if ($type & SIMPLEPIE_LOCATOR_LOCAL_EXTENSION && $working = $this->extension($this->local)) - { - return $working; - } - - if ($type & SIMPLEPIE_LOCATOR_LOCAL_BODY && $working = $this->body($this->local)) - { - return $working; - } - - if ($type & SIMPLEPIE_LOCATOR_REMOTE_EXTENSION && $working = $this->extension($this->elsewhere)) - { - return $working; - } - - if ($type & SIMPLEPIE_LOCATOR_REMOTE_BODY && $working = $this->body($this->elsewhere)) - { - return $working; - } - } - return null; - } - - function is_feed(&$file) - { - if ($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE) - { - $sniffer = new $this->content_type_sniffer_class($file); - $sniffed = $sniffer->get_type(); - if (in_array($sniffed, array('application/rss+xml', 'application/rdf+xml', 'application/atom+xml', 'text/xml', 'application/xml', 'text/plain'))) - { - return true; - } - else - { - return false; - } - } - elseif ($file->method & SIMPLEPIE_FILE_SOURCE_LOCAL) - { - return true; - } - else - { - return false; - } - } - - function get_base() - { - $this->http_base = $this->file->url; - $this->base = $this->http_base; - $elements = SimplePie_Misc::get_element('base', $this->file->body); - foreach ($elements as $element) - { - if ($element['attribs']['href']['data'] !== '') - { - $this->base = SimplePie_Misc::absolutize_url(trim($element['attribs']['href']['data']), $this->http_base); - $this->base_location = $element['offset']; - break; - } - } - } - - function autodiscovery() - { - $links = array_merge(SimplePie_Misc::get_element('link', $this->file->body), SimplePie_Misc::get_element('a', $this->file->body), SimplePie_Misc::get_element('area', $this->file->body)); - $done = array(); - foreach ($links as $link) - { - if ($this->checked_feeds == $this->max_checked_feeds) - { - break; - } - if (isset($link['attribs']['href']['data']) && isset($link['attribs']['rel']['data'])) - { - $rel = array_unique(SimplePie_Misc::space_seperated_tokens(strtolower($link['attribs']['rel']['data']))); - - if ($this->base_location < $link['offset']) - { - $href = SimplePie_Misc::absolutize_url(trim($link['attribs']['href']['data']), $this->base); - } - else - { - $href = SimplePie_Misc::absolutize_url(trim($link['attribs']['href']['data']), $this->http_base); - } - - if (!in_array($href, $done) && in_array('feed', $rel) || (in_array('alternate', $rel) && !empty($link['attribs']['type']['data']) && in_array(strtolower(SimplePie_Misc::parse_mime($link['attribs']['type']['data'])), array('application/rss+xml', 'application/atom+xml')))) - { - $this->checked_feeds++; - $feed =& new $this->file_class($href, $this->timeout, 5, null, $this->useragent); - if ($this->is_feed($feed)) - { - return $feed; - } - } - $done[] = $href; - } - } - return null; - } - - function get_links() - { - $links = SimplePie_Misc::get_element('a', $this->file->body); - foreach ($links as $link) - { - if (isset($link['attribs']['href']['data'])) - { - $href = trim($link['attribs']['href']['data']); - $parsed = SimplePie_Misc::parse_url($href); - if ($parsed['scheme'] === '' || preg_match('/^(http(s)|feed)?$/i', $parsed['scheme'])) - { - if ($this->base_location < $link['offset']) - { - $href = SimplePie_Misc::absolutize_url(trim($link['attribs']['href']['data']), $this->base); - } - else - { - $href = SimplePie_Misc::absolutize_url(trim($link['attribs']['href']['data']), $this->http_base); - } - - $current = SimplePie_Misc::parse_url($this->file->url); - - if ($parsed['authority'] === '' || $parsed['authority'] == $current['authority']) - { - $this->local[] = $href; - } - else - { - $this->elsewhere[] = $href; - } - } - } - } - $this->local = array_unique($this->local); - $this->elsewhere = array_unique($this->elsewhere); - if (!empty($this->local) || !empty($this->elsewhere)) - { - return true; - } - return null; - } - - function extension(&$array) - { - foreach ($array as $key => $value) - { - if ($this->checked_feeds == $this->max_checked_feeds) - { - break; - } - if (in_array(strtolower(strrchr($value, '.')), array('.rss', '.rdf', '.atom', '.xml'))) - { - $this->checked_feeds++; - $feed =& new $this->file_class($value, $this->timeout, 5, null, $this->useragent); - if ($this->is_feed($feed)) - { - return $feed; - } - else - { - unset($array[$key]); - } - } - } - return null; - } - - function body(&$array) - { - foreach ($array as $key => $value) - { - if ($this->checked_feeds == $this->max_checked_feeds) - { - break; - } - if (preg_match('/(rss|rdf|atom|xml)/i', $value)) - { - $this->checked_feeds++; - $feed =& new $this->file_class($value, $this->timeout, 5, null, $this->useragent); - if ($this->is_feed($feed)) - { - return $feed; - } - else - { - unset($array[$key]); - } - } - } - return null; - } -} - -class SimplePie_Parser -{ - var $error_code; - var $error_string; - var $current_line; - var $current_column; - var $current_byte; - var $separator = ' '; - var $feed = false; - var $namespace = array(''); - var $element = array(''); - var $xml_base = array(''); - var $xml_base_explicit = array(false); - var $xml_lang = array(''); - var $data = array(); - var $datas = array(array()); - var $current_xhtml_construct = -1; - var $encoding; - - function parse(&$data, $encoding) - { - // Use UTF-8 if we get passed US-ASCII, as every US-ASCII character is a UTF-8 character - if (strtoupper($encoding) == 'US-ASCII') - { - $this->encoding = 'UTF-8'; - } - else - { - $this->encoding = $encoding; - } - - // Strip BOM: - // UTF-32 Big Endian BOM - if (substr($data, 0, 4) === "\x00\x00\xFE\xFF") - { - $data = substr($data, 4); - } - // UTF-32 Little Endian BOM - elseif (substr($data, 0, 4) === "\xFF\xFE\x00\x00") - { - $data = substr($data, 4); - } - // UTF-16 Big Endian BOM - elseif (substr($data, 0, 2) === "\xFE\xFF") - { - $data = substr($data, 2); - } - // UTF-16 Little Endian BOM - elseif (substr($data, 0, 2) === "\xFF\xFE") - { - $data = substr($data, 2); - } - // UTF-8 BOM - elseif (substr($data, 0, 3) === "\xEF\xBB\xBF") - { - $data = substr($data, 3); - } - - if (substr($data, 0, 5) === '')) !== false) - { - $declaration = new SimplePie_XML_Declaration_Parser(substr($data, 5, $pos - 5)); - if ($declaration->parse()) - { - $data = substr($data, $pos + 2); - $data = 'version . '" encoding="' . $encoding . '" standalone="' . (($declaration->standalone) ? 'yes' : 'no') . '"?>' . $data; - } - else - { - $this->error_string = 'SimplePie bug! Please report this!'; - return false; - } - } - - $return = true; - - // Create the parser - $xml = xml_parser_create_ns($this->encoding, $this->separator); - xml_parser_set_option($xml, XML_OPTION_SKIP_WHITE, 1); - xml_parser_set_option($xml, XML_OPTION_CASE_FOLDING, 0); - xml_set_object($xml, $this); - xml_set_character_data_handler($xml, 'cdata'); - xml_set_element_handler($xml, 'tag_open', 'tag_close'); - - // Parse! - if (!xml_parse($xml, $data, true)) - { - $this->error_code = xml_get_error_code($xml); - $this->error_string = xml_error_string($this->error_code); - $return = false; - } - $this->current_line = xml_get_current_line_number($xml); - $this->current_column = xml_get_current_column_number($xml); - $this->current_byte = xml_get_current_byte_index($xml); - xml_parser_free($xml); - return $return; - } - - function get_error_code() - { - return $this->error_code; - } - - function get_error_string() - { - return $this->error_string; - } - - function get_current_line() - { - return $this->current_line; - } - - function get_current_column() - { - return $this->current_column; - } - - function get_current_byte() - { - return $this->current_byte; - } - - function get_data() - { - return $this->data; - } - - function tag_open($parser, $tag, $attributes) - { - if ($this->feed === 0) - { - return; - } - elseif ($this->feed == false) - { - if (in_array($tag, array( - SIMPLEPIE_NAMESPACE_ATOM_10 . $this->separator . 'feed', - SIMPLEPIE_NAMESPACE_ATOM_03 . $this->separator . 'feed', - 'rss', - SIMPLEPIE_NAMESPACE_RDF . $this->separator . 'RDF' - ))) - { - $this->feed = 1; - } - } - else - { - $this->feed++; - } - - list($this->namespace[], $this->element[]) = $this->split_ns($tag); - - $attribs = array(); - foreach ($attributes as $name => $value) - { - list($attrib_namespace, $attribute) = $this->split_ns($name); - $attribs[$attrib_namespace][$attribute] = $value; - } - - if (isset($attribs[SIMPLEPIE_NAMESPACE_XML]['base'])) - { - $this->xml_base[] = SimplePie_Misc::absolutize_url($attribs[SIMPLEPIE_NAMESPACE_XML]['base'], end($this->xml_base)); - $this->xml_base_explicit[] = true; - } - else - { - $this->xml_base[] = end($this->xml_base); - $this->xml_base_explicit[] = end($this->xml_base_explicit); - } - - if (isset($attribs[SIMPLEPIE_NAMESPACE_XML]['lang'])) - { - $this->xml_lang[] = $attribs[SIMPLEPIE_NAMESPACE_XML]['lang']; - } - else - { - $this->xml_lang[] = end($this->xml_lang); - } - - if ($this->current_xhtml_construct >= 0) - { - $this->current_xhtml_construct++; - if (end($this->namespace) == SIMPLEPIE_NAMESPACE_XHTML) - { - $this->data['data'] .= '<' . end($this->element); - if (isset($attribs[''])) - { - foreach ($attribs[''] as $name => $value) - { - $this->data['data'] .= ' ' . $name . '="' . htmlspecialchars($value, ENT_COMPAT, $this->encoding) . '"'; - } - } - $this->data['data'] .= '>'; - } - } - else - { - $this->datas[] =& $this->data; - $this->data =& $this->data['child'][end($this->namespace)][end($this->element)][]; - $this->data = array('data' => '', 'attribs' => $attribs, 'xml_base' => end($this->xml_base), 'xml_base_explicit' => end($this->xml_base_explicit), 'xml_lang' => end($this->xml_lang)); - if ((end($this->namespace) == SIMPLEPIE_NAMESPACE_ATOM_03 && in_array(end($this->element), array('title', 'tagline', 'copyright', 'info', 'summary', 'content')) && isset($attribs['']['mode']) && $attribs['']['mode'] == 'xml') - || (end($this->namespace) == SIMPLEPIE_NAMESPACE_ATOM_10 && in_array(end($this->element), array('rights', 'subtitle', 'summary', 'info', 'title', 'content')) && isset($attribs['']['type']) && $attribs['']['type'] == 'xhtml')) - { - $this->current_xhtml_construct = 0; - } - } - } - - function cdata($parser, $cdata) - { - if ($this->current_xhtml_construct >= 0) - { - $this->data['data'] .= htmlspecialchars($cdata, ENT_QUOTES, $this->encoding); - } - elseif ($this->feed > 1) - { - $this->data['data'] .= $cdata; - } - } - - function tag_close($parser, $tag) - { - if (!$this->feed) - { - return; - } - - if ($this->current_xhtml_construct >= 0) - { - $this->current_xhtml_construct--; - if (end($this->namespace) == SIMPLEPIE_NAMESPACE_XHTML && !in_array(end($this->element), array('area', 'base', 'basefont', 'br', 'col', 'frame', 'hr', 'img', 'input', 'isindex', 'link', 'meta', 'param'))) - { - $this->data['data'] .= 'element) . '>'; - } - } - if ($this->current_xhtml_construct == -1) - { - $this->data =& $this->datas[$this->feed]; - array_pop($this->datas); - } - - array_pop($this->element); - array_pop($this->namespace); - array_pop($this->xml_base); - array_pop($this->xml_base_explicit); - array_pop($this->xml_lang); - $this->feed--; - } - - function split_ns($string) - { - static $cache = array(); - if (!isset($cache[$string])) - { - if ($pos = strpos($string, $this->separator)) - { - static $separator_length; - if (!$separator_length) - { - $separator_length = strlen($this->separator); - } - $namespace = substr($string, 0, $pos); - $local_name = substr($string, $pos + $separator_length); - if (strtolower($namespace) === SIMPLEPIE_NAMESPACE_ITUNES) - { - $namespace = SIMPLEPIE_NAMESPACE_ITUNES; - } - - // Normalize the Media RSS namespaces - if ($namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG) - { - $namespace = SIMPLEPIE_NAMESPACE_MEDIARSS; - } - $cache[$string] = array($namespace, $local_name); - } - else - { - $cache[$string] = array('', $string); - } - } - return $cache[$string]; - } -} - -/** - * @todo Move to using an actual HTML parser (this will allow tags to be properly stripped, and to switch between HTML and XHTML), this will also make it easier to shorten a string while preserving HTML tags - */ -class SimplePie_Sanitize -{ - // Private vars - var $base; - - // Options - var $remove_div = true; - var $image_handler = ''; - var $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style'); - var $encode_instead_of_strip = false; - var $strip_attributes = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc'); - var $strip_comments = false; - var $output_encoding = 'UTF-8'; - var $enable_cache = true; - var $cache_location = './cache'; - var $cache_name_function = 'md5'; - var $cache_class = 'SimplePie_Cache'; - var $file_class = 'SimplePie_File'; - var $timeout = 10; - var $useragent = ''; - var $force_fsockopen = false; - - var $replace_url_attributes = array( - 'a' => 'href', - 'area' => 'href', - 'blockquote' => 'cite', - 'del' => 'cite', - 'form' => 'action', - 'img' => array('longdesc', 'src'), - 'input' => 'src', - 'ins' => 'cite', - 'q' => 'cite' - ); - - function remove_div($enable = true) - { - $this->remove_div = (bool) $enable; - } - - function set_image_handler($page = false) - { - if ($page) - { - $this->image_handler = (string) $page; - } - else - { - $this->image_handler = false; - } - } - - function pass_cache_data($enable_cache = true, $cache_location = './cache', $cache_name_function = 'md5', $cache_class = 'SimplePie_Cache') - { - if (isset($enable_cache)) - { - $this->enable_cache = (bool) $enable_cache; - } - - if ($cache_location) - { - $this->cache_location = (string) $cache_location; - } - - if ($cache_name_function) - { - $this->cache_name_function = (string) $cache_name_function; - } - - if ($cache_class) - { - $this->cache_class = (string) $cache_class; - } - } - - function pass_file_data($file_class = 'SimplePie_File', $timeout = 10, $useragent = '', $force_fsockopen = false) - { - if ($file_class) - { - $this->file_class = (string) $file_class; - } - - if ($timeout) - { - $this->timeout = (string) $timeout; - } - - if ($useragent) - { - $this->useragent = (string) $useragent; - } - - if ($force_fsockopen) - { - $this->force_fsockopen = (string) $force_fsockopen; - } - } - - function strip_htmltags($tags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style')) - { - if ($tags) - { - if (is_array($tags)) - { - $this->strip_htmltags = $tags; - } - else - { - $this->strip_htmltags = explode(',', $tags); - } - } - else - { - $this->strip_htmltags = false; - } - } - - function encode_instead_of_strip($encode = false) - { - $this->encode_instead_of_strip = (bool) $encode; - } - - function strip_attributes($attribs = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc')) - { - if ($attribs) - { - if (is_array($attribs)) - { - $this->strip_attributes = $attribs; - } - else - { - $this->strip_attributes = explode(',', $attribs); - } - } - else - { - $this->strip_attributes = false; - } - } - - function strip_comments($strip = false) - { - $this->strip_comments = (bool) $strip; - } - - function set_output_encoding($encoding = 'UTF-8') - { - $this->output_encoding = (string) $encoding; - } - - /** - * Set element/attribute key/value pairs of HTML attributes - * containing URLs that need to be resolved relative to the feed - * - * @access public - * @since 1.0 - * @param array $element_attribute Element/attribute key/value pairs - */ - function set_url_replacements($element_attribute = array('a' => 'href', 'area' => 'href', 'blockquote' => 'cite', 'del' => 'cite', 'form' => 'action', 'img' => array('longdesc', 'src'), 'input' => 'src', 'ins' => 'cite', 'q' => 'cite')) - { - $this->replace_url_attributes = (array) $element_attribute; - } - - function sanitize($data, $type, $base = '') - { - $data = trim($data); - if ($data !== '' || $type & SIMPLEPIE_CONSTRUCT_IRI) - { - if ($type & SIMPLEPIE_CONSTRUCT_MAYBE_HTML) - { - if (preg_match('/(&(#(x[0-9a-fA-F]+|[0-9]+)|[a-zA-Z0-9]+)|<\/[A-Za-z][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E]*' . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . '>)/', $data)) - { - $type |= SIMPLEPIE_CONSTRUCT_HTML; - } - else - { - $type |= SIMPLEPIE_CONSTRUCT_TEXT; - } - } - - if ($type & SIMPLEPIE_CONSTRUCT_BASE64) - { - $data = base64_decode($data); - } - - if ($type & SIMPLEPIE_CONSTRUCT_XHTML) - { - if ($this->remove_div) - { - $data = preg_replace('/^/', '', $data); - $data = preg_replace('/<\/div>$/', '', $data); - } - else - { - $data = preg_replace('/^/', '
    ', $data); - } - } - - if ($type & (SIMPLEPIE_CONSTRUCT_HTML | SIMPLEPIE_CONSTRUCT_XHTML)) - { - // Strip comments - if ($this->strip_comments) - { - $data = SimplePie_Misc::strip_comments($data); - } - - // Strip out HTML tags and attributes that might cause various security problems. - // Based on recommendations by Mark Pilgrim at: - // http://diveintomark.org/archives/2003/06/12/how_to_consume_rss_safely - if ($this->strip_htmltags) - { - foreach ($this->strip_htmltags as $tag) - { - $pcre = "/<($tag)" . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . "(>(.*)<\/$tag" . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . '>|(\/)?>)/siU'; - while (preg_match($pcre, $data)) - { - $data = preg_replace_callback($pcre, array(&$this, 'do_strip_htmltags'), $data); - } - } - } - - if ($this->strip_attributes) - { - foreach ($this->strip_attributes as $attrib) - { - $data = preg_replace('/(<[A-Za-z][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E]*)' . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . trim($attrib) . '(?:\s*=\s*(?:"(?:[^"]*)"|\'(?:[^\']*)\'|(?:[^\x09\x0A\x0B\x0C\x0D\x20\x22\x27\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x3E]*)?))?' . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . '>/', '\1\2\3>', $data); - } - } - - // Replace relative URLs - $this->base = $base; - foreach ($this->replace_url_attributes as $element => $attributes) - { - $data = $this->replace_urls($data, $element, $attributes); - } - - // If image handling (caching, etc.) is enabled, cache and rewrite all the image tags. - if (isset($this->image_handler) && ((string) $this->image_handler) !== '' && $this->enable_cache) - { - $images = SimplePie_Misc::get_element('img', $data); - foreach ($images as $img) - { - if (isset($img['attribs']['src']['data'])) - { - $image_url = call_user_func($this->cache_name_function, $img['attribs']['src']['data']); - $cache = call_user_func(array($this->cache_class, 'create'), $this->cache_location, $image_url, 'spi'); - - if ($cache->load()) - { - $img['attribs']['src']['data'] = $this->image_handler . $image_url; - $data = str_replace($img['full'], SimplePie_Misc::element_implode($img), $data); - } - else - { - $file =& new $this->file_class($img['attribs']['src']['data'], $this->timeout, 5, array('X-FORWARDED-FOR' => $_SERVER['REMOTE_ADDR']), $this->useragent, $this->force_fsockopen); - $headers = $file->headers; - - if ($file->success && ($file->status_code == 200 || ($file->status_code > 206 && $file->status_code < 300))) - { - if ($cache->save(array('headers' => $file->headers, 'body' => $file->body))) - { - $img['attribs']['src']['data'] = $this->image_handler . $image_url; - $data = str_replace($img['full'], SimplePie_Misc::element_implode($img), $data); - } - else - { - trigger_error("$cache->name is not writeable", E_USER_WARNING); - } - } - } - } - } - } - - // Having (possibly) taken stuff out, there may now be whitespace at the beginning/end of the data - $data = trim($data); - } - - if ($type & SIMPLEPIE_CONSTRUCT_IRI) - { - $data = SimplePie_Misc::absolutize_url($data, $base); - } - - if ($type & (SIMPLEPIE_CONSTRUCT_TEXT | SIMPLEPIE_CONSTRUCT_IRI)) - { - $data = htmlspecialchars($data, ENT_COMPAT, 'UTF-8'); - } - - if ($this->output_encoding != 'UTF-8') - { - $data = SimplePie_Misc::change_encoding($data, 'UTF-8', $this->output_encoding); - } - } - return $data; - } - - function replace_urls($data, $tag, $attributes) - { - if (!is_array($this->strip_htmltags) || !in_array($tag, $this->strip_htmltags)) - { - $elements = SimplePie_Misc::get_element($tag, $data); - foreach ($elements as $element) - { - if (is_array($attributes)) - { - foreach ($attributes as $attribute) - { - if (isset($element['attribs'][$attribute]['data'])) - { - $element['attribs'][$attribute]['data'] = SimplePie_Misc::absolutize_url($element['attribs'][$attribute]['data'], $this->base); - $new_element = SimplePie_Misc::element_implode($element); - $data = str_replace($element['full'], $new_element, $data); - $element['full'] = $new_element; - } - } - } - elseif (isset($element['attribs'][$attributes]['data'])) - { - $element['attribs'][$attributes]['data'] = SimplePie_Misc::absolutize_url($element['attribs'][$attributes]['data'], $this->base); - $data = str_replace($element['full'], SimplePie_Misc::element_implode($element), $data); - } - } - } - return $data; - } - - function do_strip_htmltags($match) - { - if ($this->encode_instead_of_strip) - { - if (isset($match[4]) && !in_array(strtolower($match[1]), array('script', 'style'))) - { - $match[1] = htmlspecialchars($match[1], ENT_COMPAT, 'UTF-8'); - $match[2] = htmlspecialchars($match[2], ENT_COMPAT, 'UTF-8'); - return "<$match[1]$match[2]>$match[3]</$match[1]>"; - } - else - { - return htmlspecialchars($match[0], ENT_COMPAT, 'UTF-8'); - } - } - elseif (isset($match[4]) && !in_array(strtolower($match[1]), array('script', 'style'))) - { - return $match[4]; - } - else - { - return ''; - } - } -} - -?> \ No newline at end of file diff --git a/spyc/README b/spyc/README deleted file mode 100644 index a8539bd..0000000 --- a/spyc/README +++ /dev/null @@ -1,159 +0,0 @@ -# -# S P Y C -# a simple php yaml class -# -# Load this README! -# >> $readme = Spyc::YAMLLoad('README'); -# ---- %YAML:1.1 -title: Spyc -- a Simple PHP YAML Class -version: 0.4.5 -authors: [chris wanstrath (chris@ozmm.org), vlad andersen (vlad.andersen@gmail.com)] -websites: [http://www.yaml.org, http://spyc.sourceforge.net] -license: [MIT License, http://www.opensource.org/licenses/mit-license.php] -copyright: "(c) 2005-2006 Chris Wanstrath, 2006-2009 Vlad Andersen" -tested on: [php 5.2.x] - -installation: > - Copy spyc.php to a directory you can - access with your YAML-ready PHP script. - - That's it! - -about: > - From www.yaml.org: - - "YAML(tm) (rhymes with 'camel') is a human-friendly, cross language, - Unicode based data serialization language designed around the common - native data structures of agile programming languages. It is broadly - useful for programming needs ranging from configuration files to - Internet messaging to object persistence to data auditing. Together - with the Unicode standard for characters, the YAML specification provides - all the information necessary to understand YAML Version 1.1 and to - creating programs that process YAML information. - - YAML(tm) is a balance of the following design goals: - - YAML documents are very readable by humans. - - YAML interacts well with scripting languages. - - YAML uses host languages' native data structures. - - YAML has a consistent information model. - - YAML enables stream-based processing. - - YAML is expressive and extensible. - - YAML is easy to implement." - - YAML makes a lot of sense. It's easy to use, easy to learn, and cool. - As the lucky stiff named why once said, "YAML is a beacon of light." - - If you're new to YAML, may we suggest YAML In Five Minutes: - - http://yaml.kwiki.org/?YamlInFiveMinutes - - If you don't have five minutes, realize that this README is a completely - valid YAML document. Dig in, load this or any YAML file into an array - with Spyc and see how easy it is to translate friendly text into usable - data. - - The purpose of Spyc is to provide a pure PHP alternative to Syck, a - simple API for loading and dumping YAML documents, a YAML loader which - understands a usable subset of the YAML spec, and to further spread - the glory of YAML to the PHP masses. - - If you're at all hesitant ("usable subset of YAML?!"), navigate - http://yaml.org/start.html. Spyc completely understands the YAML - document shown there, a document which has features way beyond the - scope of what normal config files might require. Try it for yourself, - and then start enjoying the peace of mind YAML brings to your life. - -meat and a few potatoes: - - concept: Loading a YAML document into PHP - brief: > - $yaml will become an array of all the data in wicked.yaml - code: | - - include('spyc.php'); - - $yaml = Spyc::YAMLLoad('wicked.yaml'); - - - concept: Loading a YAML string into PHP - brief: > - $array will look like this: - array('A YAML','document in a','string') - code: | - - include('spyc.php'); - - $yaml = '- A YAML\n- document in a\n- string.'; - $array = Spyc::YAMLLoad($yaml); - - - concept: Dumping a PHP array to YAML - brief: > - $yaml will become a string of a YAML document created from - $array. - code: | - - include('spyc.php'); - - $array['name'] = 'chris'; - $array['sport'] = 'curbing'; - - $yaml = Spyc::YAMLDump($array); - -prior art: - - who: [Brian Ingerson, Clark Evans, Oren Ben-Kiki] - why?: > - The YAML spec is really a piece of work, and these guys - did a great job on it. A simple and elegant language like - YAML was a long time coming and it's refreshing to know - such able minded individuals took the task to heart and - executed it with cunning and strength. In addition to - their various noteworthy contributions to YAML parsers - and related projects, YAML.pm's README is a treasure trove - of information for knowledge seekers. Thanks, guys. - - - who: why the lucky stiff - why?: > - As the author of Syck, the code used in Ruby for the language's - YAML class and methods, why is indirectly (directly?) responsible - for my first exposure to YAML (as a config file in a Ruby web-app) - and the countless hours I spent playing with this sheik new data - format afterwards. Syck's README is a YAML file and thus the - inspiration for this file and, even, this very piece of software. - - - who: Steve Howell - why?: > - Python's YAML implementation. PyYAML's README file is also YAML, - so it too inspired the YAML format of this README file. - - - who: [Rasmus Lerdorf, Zeev Suraski, Andi Gutmans, et al] - why?: > - PHP is great at what it does best. It's also paid a lot of my bills. - Thanks. - -bugs: - report: > - Please see Spyc's Sourceforge project page for information on reporting bugs. - speed: > - This implementation was not designed for speed. Rather, it - was designed for those who need a pure PHP implementation of - a YAML parser and who are not overly concerned with performance. - If you want speed, check out Syck. - depth: > - This parser is by no means a comprehensive YAML parser. For supported - features and future plans, check the website. - unicode: > - YAML is supposed to be unicode, but for now we're just using ASCII. - PHP has crappy unicode support but who knows what the future holds. - -resources: - - http://www.yaml.org - - http://www.yaml.org/spec/ - - http://yaml.kwiki.org/?YamlInFiveMinutes - - http://www.whytheluckystiff.net/syck/ - - http://yaml4r.sourceforge.net/cookbook/ - -thanks: - - Adam Wood - - Daniel Ferreira - - Aaron Jensen - - Mike Thornton - - Fabien Potencier - - Mustafa Kumas \ No newline at end of file diff --git a/spyc/examples/yaml-dump.php b/spyc/examples/yaml-dump.php deleted file mode 100644 index 05a8a2f..0000000 --- a/spyc/examples/yaml-dump.php +++ /dev/null @@ -1,25 +0,0 @@ - 'A sequence','second' => 'of mapped values'); -$array['Mapped'] = array('A sequence','which is mapped'); -$array['A Note'] = 'What if your text is too long?'; -$array['Another Note'] = 'If that is the case, the dumper will probably fold your text by using a block. Kinda like this.'; -$array['The trick?'] = 'The trick is that we overrode the default indent, 2, to 4 and the default wordwrap, 40, to 60.'; -$array['Old Dog'] = "And if you want\n to preserve line breaks, \ngo ahead!"; -$array['key:withcolon'] = "Should support this to"; - -$yaml = Spyc::YAMLDump($array,4,60); \ No newline at end of file diff --git a/spyc/examples/yaml-load.php b/spyc/examples/yaml-load.php deleted file mode 100644 index 3e1a4a7..0000000 --- a/spyc/examples/yaml-load.php +++ /dev/null @@ -1,25 +0,0 @@ -spyc.yaml loaded into PHP:
    '; -print_r($array); -echo ''; - - -echo '
    YAML Data dumped back:
    '; -echo Spyc::YAMLDump($array); -echo '
    '; diff --git a/spyc/php4/5to4.php b/spyc/php4/5to4.php deleted file mode 100644 index 2789620..0000000 --- a/spyc/php4/5to4.php +++ /dev/null @@ -1,16 +0,0 @@ -', $code); - $f = fopen ($dest, 'w'); - fwrite($f, $code); - fclose ($f); - print "Written to $dest.\n"; -} \ No newline at end of file diff --git a/spyc/php4/spyc.php4 b/spyc/php4/spyc.php4 deleted file mode 100644 index 040ff74..0000000 --- a/spyc/php4/spyc.php4 +++ /dev/null @@ -1,1023 +0,0 @@ - - * @author Chris Wanstrath - * @link http://code.google.com/p/spyc/ - * @copyright Copyright 2005-2006 Chris Wanstrath, 2006-2009 Vlad Andersen - * @license http://www.opensource.org/licenses/mit-license.php MIT License - * @package Spyc - */ - -if (!function_exists('spyc_load')) { - /** - * Parses YAML to array. - * @param string $string YAML string. - * @return array - */ - function spyc_load ($string) { - return Spyc::YAMLLoadString($string); - } -} - -if (!function_exists('spyc_load_file')) { - /** - * Parses YAML to array. - * @param string $file Path to YAML file. - * @return array - */ - function spyc_load_file ($file) { - return Spyc::YAMLLoad($file); - } -} - -/** - * The Simple PHP YAML Class. - * - * This class can be used to read a YAML file and convert its contents - * into a PHP array. It currently supports a very limited subsection of - * the YAML spec. - * - * Usage: - * - * $Spyc = new Spyc; - * $array = $Spyc->load($file); - * - * or: - * - * $array = Spyc::YAMLLoad($file); - * - * or: - * - * $array = spyc_load_file($file); - * - * @package Spyc - */ -class Spyc { - - // SETTINGS - - /** - * Setting this to true will force YAMLDump to enclose any string value in - * quotes. False by default. - * - * @var bool - */ - var $setting_dump_force_quotes = false; - - /** - * Setting this to true will forse YAMLLoad to use syck_load function when - * possible. False by default. - * @var bool - */ - var $setting_use_syck_is_possible = false; - - - - /**#@+ - * @access private - * @var mixed - */ - var $_dumpIndent; - var $_dumpWordWrap; - var $_containsGroupAnchor = false; - var $_containsGroupAlias = false; - var $path; - var $result; - var $LiteralPlaceHolder = '___YAML_Literal_Block___'; - var $SavedGroups = array(); - var $indent; - /** - * Path modifier that should be applied after adding current element. - * @var array - */ - var $delayedPath = array(); - - /**#@+ - * @access public - * @var mixed - */ - var $_nodeId; - -/** - * Load a valid YAML string to Spyc. - * @param string $input - * @return array - */ - function load ($input) { - return $this->__loadString($input); - } - - /** - * Load a valid YAML file to Spyc. - * @param string $file - * @return array - */ - function loadFile ($file) { - return $this->__load($file); - } - - /** - * Load YAML into a PHP array statically - * - * The load method, when supplied with a YAML stream (string or file), - * will do its best to convert YAML in a file into a PHP array. Pretty - * simple. - * Usage: - * - * $array = Spyc::YAMLLoad('lucky.yaml'); - * print_r($array); - * - * @access public - * @return array - * @param string $input Path of YAML file or string containing YAML - */ - function YAMLLoad($input) { - $Spyc = new Spyc; - return $Spyc->__load($input); - } - - /** - * Load a string of YAML into a PHP array statically - * - * The load method, when supplied with a YAML string, will do its best - * to convert YAML in a string into a PHP array. Pretty simple. - * - * Note: use this function if you don't want files from the file system - * loaded and processed as YAML. This is of interest to people concerned - * about security whose input is from a string. - * - * Usage: - * - * $array = Spyc::YAMLLoadString("---\n0: hello world\n"); - * print_r($array); - * - * @access public - * @return array - * @param string $input String containing YAML - */ - function YAMLLoadString($input) { - $Spyc = new Spyc; - return $Spyc->__loadString($input); - } - - /** - * Dump YAML from PHP array statically - * - * The dump method, when supplied with an array, will do its best - * to convert the array into friendly YAML. Pretty simple. Feel free to - * save the returned string as nothing.yaml and pass it around. - * - * Oh, and you can decide how big the indent is and what the wordwrap - * for folding is. Pretty cool -- just pass in 'false' for either if - * you want to use the default. - * - * Indent's default is 2 spaces, wordwrap's default is 40 characters. And - * you can turn off wordwrap by passing in 0. - * - * @access public - * @return string - * @param array $array PHP array - * @param int $indent Pass in false to use the default, which is 2 - * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40) - */ - function YAMLDump($array,$indent = false,$wordwrap = false) { - $spyc = new Spyc; - return $spyc->dump($array,$indent,$wordwrap); - } - - - /** - * Dump PHP array to YAML - * - * The dump method, when supplied with an array, will do its best - * to convert the array into friendly YAML. Pretty simple. Feel free to - * save the returned string as tasteful.yaml and pass it around. - * - * Oh, and you can decide how big the indent is and what the wordwrap - * for folding is. Pretty cool -- just pass in 'false' for either if - * you want to use the default. - * - * Indent's default is 2 spaces, wordwrap's default is 40 characters. And - * you can turn off wordwrap by passing in 0. - * - * @access public - * @return string - * @param array $array PHP array - * @param int $indent Pass in false to use the default, which is 2 - * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40) - */ - function dump($array,$indent = false,$wordwrap = false) { - // Dumps to some very clean YAML. We'll have to add some more features - // and options soon. And better support for folding. - - // New features and options. - if ($indent === false or !is_numeric($indent)) { - $this->_dumpIndent = 2; - } else { - $this->_dumpIndent = $indent; - } - - if ($wordwrap === false or !is_numeric($wordwrap)) { - $this->_dumpWordWrap = 40; - } else { - $this->_dumpWordWrap = $wordwrap; - } - - // New YAML document - $string = "---\n"; - - // Start at the base of the array and move through it. - if ($array) { - $array = (array)$array; - $first_key = key($array); - - $previous_key = -1; - foreach ($array as $key => $value) { - $string .= $this->_yamlize($key,$value,0,$previous_key, $first_key); - $previous_key = $key; - } - } - return $string; - } - - /** - * Attempts to convert a key / value array item to YAML - * @access private - * @return string - * @param $key The name of the key - * @param $value The value of the item - * @param $indent The indent of the current node - */ - function _yamlize($key,$value,$indent, $previous_key = -1, $first_key = 0) { - if (is_array($value)) { - if (empty ($value)) - return $this->_dumpNode($key, array(), $indent, $previous_key, $first_key); - // It has children. What to do? - // Make it the right kind of item - $string = $this->_dumpNode($key, NULL, $indent, $previous_key, $first_key); - // Add the indent - $indent += $this->_dumpIndent; - // Yamlize the array - $string .= $this->_yamlizeArray($value,$indent); - } elseif (!is_array($value)) { - // It doesn't have children. Yip. - $string = $this->_dumpNode($key, $value, $indent, $previous_key, $first_key); - } - return $string; - } - - /** - * Attempts to convert an array to YAML - * @access private - * @return string - * @param $array The array you want to convert - * @param $indent The indent of the current level - */ - function _yamlizeArray($array,$indent) { - if (is_array($array)) { - $string = ''; - $previous_key = -1; - $first_key = key($array); - foreach ($array as $key => $value) { - $string .= $this->_yamlize($key, $value, $indent, $previous_key, $first_key); - $previous_key = $key; - } - return $string; - } else { - return false; - } - } - - /** - * Returns YAML from a key and a value - * @access private - * @return string - * @param $key The name of the key - * @param $value The value of the item - * @param $indent The indent of the current node - */ - function _dumpNode($key, $value, $indent, $previous_key = -1, $first_key = 0) { - // do some folding here, for blocks - if (is_string ($value) && ((strpos($value,"\n") !== false || strpos($value,": ") !== false || strpos($value,"- ") !== false || - strpos($value,"*") !== false || strpos($value,"#") !== false || strpos($value,"<") !== false || strpos($value,">") !== false || - strpos($value,"[") !== false || strpos($value,"]") !== false || strpos($value,"{") !== false || strpos($value,"}") !== false) || substr ($value, -1, 1) == ':')) { - $value = $this->_doLiteralBlock($value,$indent); - } else { - $value = $this->_doFolding($value,$indent); - if (is_bool($value)) { - $value = ($value) ? "true" : "false"; - } - } - - if ($value === array()) $value = '[ ]'; - - $spaces = str_repeat(' ',$indent); - - if (is_int($key) && $key - 1 == $previous_key && $first_key===0) { - // It's a sequence - $string = $spaces.'- '.$value."\n"; - } else { - if ($first_key===0) throw new Exception('Keys are all screwy. The first one was zero, now it\'s "'. $key .'"'); - // It's mapped - if (strpos($key, ":") !== false) { $key = '"' . $key . '"'; } - $string = $spaces.$key.': '.$value."\n"; - } - return $string; - } - - /** - * Creates a literal block for dumping - * @access private - * @return string - * @param $value - * @param $indent int The value of the indent - */ - function _doLiteralBlock($value,$indent) { - if (strpos($value, "\n") === false && strpos($value, "'") === false) { - return sprintf ("'%s'", $value); - } - if (strpos($value, "\n") === false && strpos($value, '"') === false) { - return sprintf ('"%s"', $value); - } - $exploded = explode("\n",$value); - $newValue = '|'; - $indent += $this->_dumpIndent; - $spaces = str_repeat(' ',$indent); - foreach ($exploded as $line) { - $newValue .= "\n" . $spaces . trim($line); - } - return $newValue; - } - - /** - * Folds a string of text, if necessary - * @access private - * @return string - * @param $value The string you wish to fold - */ - function _doFolding($value,$indent) { - // Don't do anything if wordwrap is set to 0 - - if ($this->_dumpWordWrap !== 0 && is_string ($value) && strlen($value) > $this->_dumpWordWrap) { - $indent += $this->_dumpIndent; - $indent = str_repeat(' ',$indent); - $wrapped = wordwrap($value,$this->_dumpWordWrap,"\n$indent"); - $value = ">\n".$indent.$wrapped; - } else { - if ($this->setting_dump_force_quotes && is_string ($value)) - $value = '"' . $value . '"'; - } - - - return $value; - } - -// LOADING FUNCTIONS - - function __load($input) { - $Source = $this->loadFromSource($input); - return $this->loadWithSource($Source); - } - - function __loadString($input) { - $Source = $this->loadFromString($input); - return $this->loadWithSource($Source); - } - - function loadWithSource($Source) { - if (empty ($Source)) return array(); - if ($this->setting_use_syck_is_possible && function_exists ('syck_load')) { - $array = syck_load (implode ('', $Source)); - return is_array($array) ? $array : array(); - } - - $this->path = array(); - $this->result = array(); - - $cnt = count($Source); - for ($i = 0; $i < $cnt; $i++) { - $line = $Source[$i]; - - $this->indent = strlen($line) - strlen(ltrim($line)); - $tempPath = $this->getParentPathByIndent($this->indent); - $line = $this->stripIndent($line, $this->indent); - if ($this->isComment($line)) continue; - if ($this->isEmpty($line)) continue; - $this->path = $tempPath; - - $literalBlockStyle = $this->startsLiteralBlock($line); - if ($literalBlockStyle) { - $line = rtrim ($line, $literalBlockStyle . " \n"); - $literalBlock = ''; - $line .= $this->LiteralPlaceHolder; - - while (++$i < $cnt && $this->literalBlockContinues($Source[$i], $this->indent)) { - $literalBlock = $this->addLiteralLine($literalBlock, $Source[$i], $literalBlockStyle); - } - $i--; - } - - while (++$i < $cnt && $this->greedilyNeedNextLine($line)) { - $line = rtrim ($line, " \n\t\r") . ' ' . ltrim ($Source[$i], " \t"); - } - $i--; - - - - if (strpos ($line, '#')) { - if (strpos ($line, '"') === false && strpos ($line, "'") === false) - $line = preg_replace('/\s+#(.+)$/','',$line); - } - - $lineArray = $this->_parseLine($line); - - if ($literalBlockStyle) - $lineArray = $this->revertLiteralPlaceHolder ($lineArray, $literalBlock); - - $this->addArray($lineArray, $this->indent); - - foreach ($this->delayedPath as $indent => $delayedPath) - $this->path[$indent] = $delayedPath; - - $this->delayedPath = array(); - - } - return $this->result; - } - - function loadFromSource ($input) { - if (!empty($input) && strpos($input, "\n") === false && file_exists($input)) - return file($input); - - return $this->loadFromString($input); - } - - function loadFromString ($input) { - $lines = explode("\n",$input); - foreach ($lines as $k => $_) { - $lines[$k] = rtrim ($_, "\r"); - } - return $lines; - } - - /** - * Parses YAML code and returns an array for a node - * @access private - * @return array - * @param string $line A line from the YAML file - */ - function _parseLine($line) { - if (!$line) return array(); - $line = trim($line); - - if (!$line) return array(); - $array = array(); - - $group = $this->nodeContainsGroup($line); - if ($group) { - $this->addGroup($line, $group); - $line = $this->stripGroup ($line, $group); - } - - if ($this->startsMappedSequence($line)) - return $this->returnMappedSequence($line); - - if ($this->startsMappedValue($line)) - return $this->returnMappedValue($line); - - if ($this->isArrayElement($line)) - return $this->returnArrayElement($line); - - if ($this->isPlainArray($line)) - return $this->returnPlainArray($line); - - - return $this->returnKeyValuePair($line); - - } - - /** - * Finds the type of the passed value, returns the value as the new type. - * @access private - * @param string $value - * @return mixed - */ - function _toType($value) { - if ($value === '') return null; - $first_character = $value[0]; - $last_character = substr($value, -1, 1); - - $is_quoted = false; - do { - if (!$value) break; - if ($first_character != '"' && $first_character != "'") break; - if ($last_character != '"' && $last_character != "'") break; - $is_quoted = true; - } while (0); - - if ($is_quoted) - return strtr(substr ($value, 1, -1), array ('\\"' => '"', '\'\'' => '\'', '\\\'' => '\'')); - - if (strpos($value, ' #') !== false) - $value = preg_replace('/\s+#(.+)$/','',$value); - - if ($first_character == '[' && $last_character == ']') { - // Take out strings sequences and mappings - $innerValue = trim(substr ($value, 1, -1)); - if ($innerValue === '') return array(); - $explode = $this->_inlineEscape($innerValue); - // Propagate value array - $value = array(); - foreach ($explode as $v) { - $value[] = $this->_toType($v); - } - return $value; - } - - if (strpos($value,': ')!==false && $first_character != '{') { - $array = explode(': ',$value); - $key = trim($array[0]); - array_shift($array); - $value = trim(implode(': ',$array)); - $value = $this->_toType($value); - return array($key => $value); - } - - if ($first_character == '{' && $last_character == '}') { - $innerValue = trim(substr ($value, 1, -1)); - if ($innerValue === '') return array(); - // Inline Mapping - // Take out strings sequences and mappings - $explode = $this->_inlineEscape($innerValue); - // Propagate value array - $array = array(); - foreach ($explode as $v) { - $SubArr = $this->_toType($v); - if (empty($SubArr)) continue; - if (is_array ($SubArr)) { - $array[key($SubArr)] = $SubArr[key($SubArr)]; continue; - } - $array[] = $SubArr; - } - return $array; - } - - if ($value == 'null' || $value == 'NULL' || $value == 'Null' || $value == '' || $value == '~') { - return null; - } - - if (intval($first_character) > 0 && preg_match ('/^[1-9]+[0-9]*$/', $value)) { - $intvalue = (int)$value; - if ($intvalue != PHP_INT_MAX) - $value = $intvalue; - return $value; - } - - if (in_array($value, - array('true', 'on', '+', 'yes', 'y', 'True', 'TRUE', 'On', 'ON', 'YES', 'Yes', 'Y'))) { - return true; - } - - if (in_array(strtolower($value), - array('false', 'off', '-', 'no', 'n'))) { - return false; - } - - if (is_numeric($value)) { - if ($value === '0') return 0; - if (trim ($value, 0) === $value) - $value = (float)$value; - return $value; - } - - return $value; - } - - /** - * Used in inlines to check for more inlines or quoted strings - * @access private - * @return array - */ - function _inlineEscape($inline) { - // There's gotta be a cleaner way to do this... - // While pure sequences seem to be nesting just fine, - // pure mappings and mappings with sequences inside can't go very - // deep. This needs to be fixed. - - $seqs = array(); - $maps = array(); - $saved_strings = array(); - - // Check for strings - $regex = '/(?:(")|(?:\'))((?(1)[^"]+|[^\']+))(?(1)"|\')/'; - if (preg_match_all($regex,$inline,$strings)) { - $saved_strings = $strings[0]; - $inline = preg_replace($regex,'YAMLString',$inline); - } - unset($regex); - - $i = 0; - do { - - // Check for sequences - while (preg_match('/\[([^{}\[\]]+)\]/U',$inline,$matchseqs)) { - $seqs[] = $matchseqs[0]; - $inline = preg_replace('/\[([^{}\[\]]+)\]/U', ('YAMLSeq' . (count($seqs) - 1) . 's'), $inline, 1); - } - - // Check for mappings - while (preg_match('/{([^\[\]{}]+)}/U',$inline,$matchmaps)) { - $maps[] = $matchmaps[0]; - $inline = preg_replace('/{([^\[\]{}]+)}/U', ('YAMLMap' . (count($maps) - 1) . 's'), $inline, 1); - } - - if ($i++ >= 10) break; - - } while (strpos ($inline, '[') !== false || strpos ($inline, '{') !== false); - - $explode = explode(', ',$inline); - $stringi = 0; $i = 0; - - while (1) { - - // Re-add the sequences - if (!empty($seqs)) { - foreach ($explode as $key => $value) { - if (strpos($value,'YAMLSeq') !== false) { - foreach ($seqs as $seqk => $seq) { - $explode[$key] = str_replace(('YAMLSeq'.$seqk.'s'),$seq,$value); - $value = $explode[$key]; - } - } - } - } - - // Re-add the mappings - if (!empty($maps)) { - foreach ($explode as $key => $value) { - if (strpos($value,'YAMLMap') !== false) { - foreach ($maps as $mapk => $map) { - $explode[$key] = str_replace(('YAMLMap'.$mapk.'s'), $map, $value); - $value = $explode[$key]; - } - } - } - } - - - // Re-add the strings - if (!empty($saved_strings)) { - foreach ($explode as $key => $value) { - while (strpos($value,'YAMLString') !== false) { - $explode[$key] = preg_replace('/YAMLString/',$saved_strings[$stringi],$value, 1); - unset($saved_strings[$stringi]); - ++$stringi; - $value = $explode[$key]; - } - } - } - - $finished = true; - foreach ($explode as $key => $value) { - if (strpos($value,'YAMLSeq') !== false) { - $finished = false; break; - } - if (strpos($value,'YAMLMap') !== false) { - $finished = false; break; - } - if (strpos($value,'YAMLString') !== false) { - $finished = false; break; - } - } - if ($finished) break; - - $i++; - if ($i > 10) - break; // Prevent infinite loops. - } - - return $explode; - } - - function literalBlockContinues ($line, $lineIndent) { - if (!trim($line)) return true; - if (strlen($line) - strlen(ltrim($line)) > $lineIndent) return true; - return false; - } - - function referenceContentsByAlias ($alias) { - do { - if (!isset($this->SavedGroups[$alias])) { echo "Bad group name: $alias."; break; } - $groupPath = $this->SavedGroups[$alias]; - $value = $this->result; - foreach ($groupPath as $k) { - $value = $value[$k]; - } - } while (false); - return $value; - } - - function addArrayInline ($array, $indent) { - $CommonGroupPath = $this->path; - if (empty ($array)) return false; - - foreach ($array as $k => $_) { - $this->addArray(array($k => $_), $indent); - $this->path = $CommonGroupPath; - } - return true; - } - - function addArray ($incoming_data, $incoming_indent) { - - // print_r ($incoming_data); - - if (count ($incoming_data) > 1) - return $this->addArrayInline ($incoming_data, $incoming_indent); - - $key = key ($incoming_data); - $value = isset($incoming_data[$key]) ? $incoming_data[$key] : null; - if ($key === '__!YAMLZero') $key = '0'; - - if ($incoming_indent == 0 && !$this->_containsGroupAlias && !$this->_containsGroupAnchor) { // Shortcut for root-level values. - if ($key || $key === '' || $key === '0') { - $this->result[$key] = $value; - } else { - $this->result[] = $value; end ($this->result); $key = key ($this->result); - } - $this->path[$incoming_indent] = $key; - return; - } - - - - $history = array(); - // Unfolding inner array tree. - $history[] = $_arr = $this->result; - foreach ($this->path as $k) { - $history[] = $_arr = $_arr[$k]; - } - - if ($this->_containsGroupAlias) { - $value = $this->referenceContentsByAlias($this->_containsGroupAlias); - $this->_containsGroupAlias = false; - } - - - // Adding string or numeric key to the innermost level or $this->arr. - if (is_string($key) && $key == '<<') { - if (!is_array ($_arr)) { $_arr = array (); } - $_arr = array_merge ($_arr, $value); - } else if ($key || $key === '' || $key === '0') { - $_arr[$key] = $value; - } else { - if (!is_array ($_arr)) { $_arr = array ($value); $key = 0; } - else { $_arr[] = $value; end ($_arr); $key = key ($_arr); } - } - - $reverse_path = array_reverse($this->path); - $reverse_history = array_reverse ($history); - $reverse_history[0] = $_arr; - $cnt = count($reverse_history) - 1; - for ($i = 0; $i < $cnt; $i++) { - $reverse_history[$i+1][$reverse_path[$i]] = $reverse_history[$i]; - } - $this->result = $reverse_history[$cnt]; - - $this->path[$incoming_indent] = $key; - - if ($this->_containsGroupAnchor) { - $this->SavedGroups[$this->_containsGroupAnchor] = $this->path; - if (is_array ($value)) { - $k = key ($value); - if (!is_int ($k)) { - $this->SavedGroups[$this->_containsGroupAnchor][$incoming_indent + 2] = $k; - } - } - $this->_containsGroupAnchor = false; - } - - } - - function startsLiteralBlock ($line) { - $lastChar = substr (trim($line), -1); - if ($lastChar != '>' && $lastChar != '|') return false; - if ($lastChar == '|') return $lastChar; - // HTML tags should not be counted as literal blocks. - if (preg_match ('#<.*?>$#', $line)) return false; - return $lastChar; - } - - function greedilyNeedNextLine($line) { - $line = trim ($line); - if (!strlen($line)) return false; - if (substr ($line, -1, 1) == ']') return false; - if ($line[0] == '[') return true; - if (preg_match ('#^[^:]+?:\s*\[#', $line)) return true; - return false; - } - - function addLiteralLine ($literalBlock, $line, $literalBlockStyle) { - $line = $this->stripIndent($line); - $line = rtrim ($line, "\r\n\t ") . "\n"; - if ($literalBlockStyle == '|') { - return $literalBlock . $line; - } - if (strlen($line) == 0) - return rtrim($literalBlock, ' ') . "\n"; - if ($line == "\n" && $literalBlockStyle == '>') { - return rtrim ($literalBlock, " \t") . "\n"; - } - if ($line != "\n") - $line = trim ($line, "\r\n ") . " "; - return $literalBlock . $line; - } - - function revertLiteralPlaceHolder ($lineArray, $literalBlock) { - foreach ($lineArray as $k => $_) { - if (is_array($_)) - $lineArray[$k] = $this->revertLiteralPlaceHolder ($_, $literalBlock); - else if (substr($_, -1 * strlen ($this->LiteralPlaceHolder)) == $this->LiteralPlaceHolder) - $lineArray[$k] = rtrim ($literalBlock, " \r\n"); - } - return $lineArray; - } - - function stripIndent ($line, $indent = -1) { - if ($indent == -1) $indent = strlen($line) - strlen(ltrim($line)); - return substr ($line, $indent); - } - - function getParentPathByIndent ($indent) { - if ($indent == 0) return array(); - $linePath = $this->path; - do { - end($linePath); $lastIndentInParentPath = key($linePath); - if ($indent <= $lastIndentInParentPath) array_pop ($linePath); - } while ($indent <= $lastIndentInParentPath); - return $linePath; - } - - - function clearBiggerPathValues ($indent) { - - - if ($indent == 0) $this->path = array(); - if (empty ($this->path)) return true; - - foreach ($this->path as $k => $_) { - if ($k > $indent) unset ($this->path[$k]); - } - - return true; - } - - - function isComment ($line) { - if (!$line) return false; - if ($line[0] == '#') return true; - if (trim($line, " \r\n\t") == '---') return true; - return false; - } - - function isEmpty ($line) { - return (trim ($line) === ''); - } - - - function isArrayElement ($line) { - if (!$line) return false; - if ($line[0] != '-') return false; - if (strlen ($line) > 3) - if (substr($line,0,3) == '---') return false; - - return true; - } - - function isHashElement ($line) { - return strpos($line, ':'); - } - - function isLiteral ($line) { - if ($this->isArrayElement($line)) return false; - if ($this->isHashElement($line)) return false; - return true; - } - - - function unquote ($value) { - if (!$value) return $value; - if (!is_string($value)) return $value; - if ($value[0] == '\'') return trim ($value, '\''); - if ($value[0] == '"') return trim ($value, '"'); - return $value; - } - - function startsMappedSequence ($line) { - return ($line[0] == '-' && substr ($line, -1, 1) == ':'); - } - - function returnMappedSequence ($line) { - $array = array(); - $key = $this->unquote(trim(substr($line,1,-1))); - $array[$key] = array(); - $this->delayedPath = array(strpos ($line, $key) + $this->indent => $key); - return array($array); - } - - function returnMappedValue ($line) { - $array = array(); - $key = $this->unquote (trim(substr($line,0,-1))); - $array[$key] = ''; - return $array; - } - - function startsMappedValue ($line) { - return (substr ($line, -1, 1) == ':'); - } - - function isPlainArray ($line) { - return ($line[0] == '[' && substr ($line, -1, 1) == ']'); - } - - function returnPlainArray ($line) { - return $this->_toType($line); - } - - function returnKeyValuePair ($line) { - $array = array(); - $key = ''; - if (strpos ($line, ':')) { - // It's a key/value pair most likely - // If the key is in double quotes pull it out - if (($line[0] == '"' || $line[0] == "'") && preg_match('/^(["\'](.*)["\'](\s)*:)/',$line,$matches)) { - $value = trim(str_replace($matches[1],'',$line)); - $key = $matches[2]; - } else { - // Do some guesswork as to the key and the value - $explode = explode(':',$line); - $key = trim($explode[0]); - array_shift($explode); - $value = trim(implode(':',$explode)); - } - // Set the type of the value. Int, string, etc - $value = $this->_toType($value); - if ($key === '0') $key = '__!YAMLZero'; - $array[$key] = $value; - } else { - $array = array ($line); - } - return $array; - - } - - - function returnArrayElement ($line) { - if (strlen($line) <= 1) return array(array()); // Weird %) - $array = array(); - $value = trim(substr($line,1)); - $value = $this->_toType($value); - $array[] = $value; - return $array; - } - - - function nodeContainsGroup ($line) { - $symbolsForReference = 'A-z0-9_\-'; - if (strpos($line, '&') === false && strpos($line, '*') === false) return false; // Please die fast ;-) - if ($line[0] == '&' && preg_match('/^(&['.$symbolsForReference.']+)/', $line, $matches)) return $matches[1]; - if ($line[0] == '*' && preg_match('/^(\*['.$symbolsForReference.']+)/', $line, $matches)) return $matches[1]; - if (preg_match('/(&['.$symbolsForReference.']+)$/', $line, $matches)) return $matches[1]; - if (preg_match('/(\*['.$symbolsForReference.']+$)/', $line, $matches)) return $matches[1]; - if (preg_match ('#^\s*<<\s*:\s*(\*[^\s]+).*$#', $line, $matches)) return $matches[1]; - return false; - - } - - function addGroup ($line, $group) { - if ($group[0] == '&') $this->_containsGroupAnchor = substr ($group, 1); - if ($group[0] == '*') $this->_containsGroupAlias = substr ($group, 1); - //print_r ($this->path); - } - - function stripGroup ($line, $group) { - $line = trim(str_replace($group, '', $line)); - return $line; - } -} - -// Enable use of Spyc from command line -// The syntax is the following: php spyc.php spyc.yaml - -define ('SPYC_FROM_COMMAND_LINE', false); - -do { - if (!SPYC_FROM_COMMAND_LINE) break; - if (empty ($_SERVER['argc']) || $_SERVER['argc'] < 2) break; - if (empty ($_SERVER['SCRIPT_NAME']) || $_SERVER['SCRIPT_NAME'] != 'spyc.php') break; - $file = $argv[1]; - printf ("Spyc loading file: %s\n", $file); - print_r (spyc_load_file ($file)); -} while (0); \ No newline at end of file diff --git a/spyc/php4/test.php4 b/spyc/php4/test.php4 deleted file mode 100644 index 315f501..0000000 --- a/spyc/php4/test.php4 +++ /dev/null @@ -1,162 +0,0 @@ - "1.5ghz", "ram" => "1 gig", - "os" => "os x 10.4.1")) - die('Sequence 4 failed'); - -# Mapped sequence -if ($yaml['domains'] != array("yaml.org", "php.net")) - die("Key: 'domains' failed"); - -# A sequence like this. -if ($yaml[5] != array("program" => "Adium", "platform" => "OS X", - "type" => "Chat Client")) - die('Sequence 5 failed'); - -# A folded block as a mapped value -if ($yaml['no time'] != "There isn't any time for your tricks!\nDo you understand?") - die("Key: 'no time' failed"); - -# A literal block as a mapped value -if ($yaml['some time'] != "There is nothing but time\nfor your tricks.") - die("Key: 'some time' failed"); - -# Crazy combinations -if ($yaml['databases'] != array( array("name" => "spartan", "notes" => - array( "Needs to be backed up", - "Needs to be normalized" ), - "type" => "mysql" ))) - die("Key: 'databases' failed"); - -# You can be a bit tricky -if ($yaml["if: you'd"] != "like") - die("Key: 'if: you\'d' failed"); - -# Inline sequences -if ($yaml[6] != array("One", "Two", "Three", "Four")) - die("Sequence 6 failed"); - -# Nested Inline Sequences -if ($yaml[7] != array("One", array("Two", "And", "Three"), "Four", "Five")) - die("Sequence 7 failed"); - -# Nested Nested Inline Sequences -if ($yaml[8] != array( "This", array("Is", "Getting", array("Ridiculous", "Guys")), - "Seriously", array("Show", "Mercy"))) - die("Sequence 8 failed"); - -# Inline mappings -if ($yaml[9] != array("name" => "chris", "age" => "young", "brand" => "lucky strike")) - die("Sequence 9 failed"); - -# Nested inline mappings -if ($yaml[10] != array("name" => "mark", "age" => "older than chris", - "brand" => array("marlboro", "lucky strike"))) - die("Sequence 10 failed"); - -# References -- they're shaky, but functional -if ($yaml['dynamic languages'] != array('Perl', 'Python', 'PHP', 'Ruby')) - die("Key: 'dynamic languages' failed"); - -if ($yaml['compiled languages'] != array('C/C++', 'Java')) - die("Key: 'compiled languages' failed"); - -if ($yaml['all languages'] != array( - array('Perl', 'Python', 'PHP', 'Ruby'), - array('C/C++', 'Java') - )) - die("Key: 'all languages' failed"); - -# Added in .2.2: Escaped quotes -if ($yaml[11] != "you know, this shouldn't work. but it does.") - die("Sequence 11 failed."); - -if ($yaml[12] != "that's my value.") - die("Sequence 12 failed."); - -if ($yaml[13] != "again, that's my value.") - die("Sequence 13 failed."); - -if ($yaml[14] != "here's to \"quotes\", boss.") - die("Sequence 14 failed."); - -if ($yaml[15] != array( 'name' => "Foo, Bar's", 'age' => 20)) - die("Sequence 15 failed."); - -if ($yaml[16] != array( 0 => "a", 1 => array (0 => 1, 1 => 2), 2 => "b")) - die("Sequence 16 failed."); - -if ($yaml['endloop'] != "Does this line in the end indeed make Spyc go to an infinite loop?") - die("[endloop] failed."); - - -print "spyc.yaml parsed correctly\n"; - -?> \ No newline at end of file diff --git a/spyc/spyc.php b/spyc/spyc.php deleted file mode 100644 index 6ff8f1c..0000000 --- a/spyc/spyc.php +++ /dev/null @@ -1,1024 +0,0 @@ - - * @author Chris Wanstrath - * @link http://code.google.com/p/spyc/ - * @copyright Copyright 2005-2006 Chris Wanstrath, 2006-2009 Vlad Andersen - * @license http://www.opensource.org/licenses/mit-license.php MIT License - * @package Spyc - */ - -if (!function_exists('spyc_load')) { - /** - * Parses YAML to array. - * @param string $string YAML string. - * @return array - */ - function spyc_load ($string) { - return Spyc::YAMLLoadString($string); - } -} - -if (!function_exists('spyc_load_file')) { - /** - * Parses YAML to array. - * @param string $file Path to YAML file. - * @return array - */ - function spyc_load_file ($file) { - return Spyc::YAMLLoad($file); - } -} - -/** - * The Simple PHP YAML Class. - * - * This class can be used to read a YAML file and convert its contents - * into a PHP array. It currently supports a very limited subsection of - * the YAML spec. - * - * Usage: - * - * $Spyc = new Spyc; - * $array = $Spyc->load($file); - * - * or: - * - * $array = Spyc::YAMLLoad($file); - * - * or: - * - * $array = spyc_load_file($file); - * - * @package Spyc - */ -class Spyc { - - // SETTINGS - - /** - * Setting this to true will force YAMLDump to enclose any string value in - * quotes. False by default. - * - * @var bool - */ - public $setting_dump_force_quotes = false; - - /** - * Setting this to true will forse YAMLLoad to use syck_load function when - * possible. False by default. - * @var bool - */ - public $setting_use_syck_is_possible = false; - - - - /**#@+ - * @access private - * @var mixed - */ - private $_dumpIndent; - private $_dumpWordWrap; - private $_containsGroupAnchor = false; - private $_containsGroupAlias = false; - private $path; - private $result; - private $LiteralPlaceHolder = '___YAML_Literal_Block___'; - private $SavedGroups = array(); - private $indent; - /** - * Path modifier that should be applied after adding current element. - * @var array - */ - private $delayedPath = array(); - - /**#@+ - * @access public - * @var mixed - */ - public $_nodeId; - -/** - * Load a valid YAML string to Spyc. - * @param string $input - * @return array - */ - public function load ($input) { - return $this->__loadString($input); - } - - /** - * Load a valid YAML file to Spyc. - * @param string $file - * @return array - */ - public function loadFile ($file) { - return $this->__load($file); - } - - /** - * Load YAML into a PHP array statically - * - * The load method, when supplied with a YAML stream (string or file), - * will do its best to convert YAML in a file into a PHP array. Pretty - * simple. - * Usage: - * - * $array = Spyc::YAMLLoad('lucky.yaml'); - * print_r($array); - * - * @access public - * @return array - * @param string $input Path of YAML file or string containing YAML - */ - public static function YAMLLoad($input) { - $Spyc = new Spyc; - return $Spyc->__load($input); - } - - /** - * Load a string of YAML into a PHP array statically - * - * The load method, when supplied with a YAML string, will do its best - * to convert YAML in a string into a PHP array. Pretty simple. - * - * Note: use this function if you don't want files from the file system - * loaded and processed as YAML. This is of interest to people concerned - * about security whose input is from a string. - * - * Usage: - * - * $array = Spyc::YAMLLoadString("---\n0: hello world\n"); - * print_r($array); - * - * @access public - * @return array - * @param string $input String containing YAML - */ - public static function YAMLLoadString($input) { - $Spyc = new Spyc; - return $Spyc->__loadString($input); - } - - /** - * Dump YAML from PHP array statically - * - * The dump method, when supplied with an array, will do its best - * to convert the array into friendly YAML. Pretty simple. Feel free to - * save the returned string as nothing.yaml and pass it around. - * - * Oh, and you can decide how big the indent is and what the wordwrap - * for folding is. Pretty cool -- just pass in 'false' for either if - * you want to use the default. - * - * Indent's default is 2 spaces, wordwrap's default is 40 characters. And - * you can turn off wordwrap by passing in 0. - * - * @access public - * @return string - * @param array $array PHP array - * @param int $indent Pass in false to use the default, which is 2 - * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40) - */ - public static function YAMLDump($array,$indent = false,$wordwrap = false) { - $spyc = new Spyc; - return $spyc->dump($array,$indent,$wordwrap); - } - - - /** - * Dump PHP array to YAML - * - * The dump method, when supplied with an array, will do its best - * to convert the array into friendly YAML. Pretty simple. Feel free to - * save the returned string as tasteful.yaml and pass it around. - * - * Oh, and you can decide how big the indent is and what the wordwrap - * for folding is. Pretty cool -- just pass in 'false' for either if - * you want to use the default. - * - * Indent's default is 2 spaces, wordwrap's default is 40 characters. And - * you can turn off wordwrap by passing in 0. - * - * @access public - * @return string - * @param array $array PHP array - * @param int $indent Pass in false to use the default, which is 2 - * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40) - */ - public function dump($array,$indent = false,$wordwrap = false) { - // Dumps to some very clean YAML. We'll have to add some more features - // and options soon. And better support for folding. - - // New features and options. - if ($indent === false or !is_numeric($indent)) { - $this->_dumpIndent = 2; - } else { - $this->_dumpIndent = $indent; - } - - if ($wordwrap === false or !is_numeric($wordwrap)) { - $this->_dumpWordWrap = 40; - } else { - $this->_dumpWordWrap = $wordwrap; - } - - // New YAML document - $string = "---\n"; - - // Start at the base of the array and move through it. - if ($array) { - $array = (array)$array; - $first_key = key($array); - - $previous_key = -1; - foreach ($array as $key => $value) { - $string .= $this->_yamlize($key,$value,0,$previous_key, $first_key); - $previous_key = $key; - } - } - return $string; - } - - /** - * Attempts to convert a key / value array item to YAML - * @access private - * @return string - * @param $key The name of the key - * @param $value The value of the item - * @param $indent The indent of the current node - */ - private function _yamlize($key,$value,$indent, $previous_key = -1, $first_key = 0) { - if (is_array($value)) { - if (empty ($value)) - return $this->_dumpNode($key, array(), $indent, $previous_key, $first_key); - // It has children. What to do? - // Make it the right kind of item - $string = $this->_dumpNode($key, NULL, $indent, $previous_key, $first_key); - // Add the indent - $indent += $this->_dumpIndent; - // Yamlize the array - $string .= $this->_yamlizeArray($value,$indent); - } elseif (!is_array($value)) { - // It doesn't have children. Yip. - $string = $this->_dumpNode($key, $value, $indent, $previous_key, $first_key); - } - return $string; - } - - /** - * Attempts to convert an array to YAML - * @access private - * @return string - * @param $array The array you want to convert - * @param $indent The indent of the current level - */ - private function _yamlizeArray($array,$indent) { - if (is_array($array)) { - $string = ''; - $previous_key = -1; - $first_key = key($array); - foreach ($array as $key => $value) { - $string .= $this->_yamlize($key, $value, $indent, $previous_key, $first_key); - $previous_key = $key; - } - return $string; - } else { - return false; - } - } - - /** - * Returns YAML from a key and a value - * @access private - * @return string - * @param $key The name of the key - * @param $value The value of the item - * @param $indent The indent of the current node - */ - private function _dumpNode($key, $value, $indent, $previous_key = -1, $first_key = 0) { - // do some folding here, for blocks - if (is_string ($value) && ((strpos($value,"\n") !== false || strpos($value,": ") !== false || strpos($value,"- ") !== false || - strpos($value,"*") !== false || strpos($value,"#") !== false || strpos($value,"<") !== false || strpos($value,">") !== false || - strpos($value,"[") !== false || strpos($value,"]") !== false || strpos($value,"{") !== false || strpos($value,"}") !== false) || substr ($value, -1, 1) == ':')) { - $value = $this->_doLiteralBlock($value,$indent); - } else { - $value = $this->_doFolding($value,$indent); - if (is_bool($value)) { - $value = ($value) ? "true" : "false"; - } - } - - if ($value === array()) $value = '[ ]'; - - $spaces = str_repeat(' ',$indent); - - if (is_int($key) && $key - 1 == $previous_key && $first_key===0) { - // It's a sequence - $string = $spaces.'- '.$value."\n"; - } else { - if ($first_key===0) throw new Exception('Keys are all screwy. The first one was zero, now it\'s "'. $key .'"'); - // It's mapped - if (strpos($key, ":") !== false) { $key = '"' . $key . '"'; } - $string = $spaces.$key.': '.$value."\n"; - } - return $string; - } - - /** - * Creates a literal block for dumping - * @access private - * @return string - * @param $value - * @param $indent int The value of the indent - */ - private function _doLiteralBlock($value,$indent) { - if (strpos($value, "\n") === false && strpos($value, "'") === false) { - return sprintf ("'%s'", $value); - } - if (strpos($value, "\n") === false && strpos($value, '"') === false) { - return sprintf ('"%s"', $value); - } - $exploded = explode("\n",$value); - $newValue = '|'; - $indent += $this->_dumpIndent; - $spaces = str_repeat(' ',$indent); - foreach ($exploded as $line) { - $newValue .= "\n" . $spaces . trim($line); - } - return $newValue; - } - - /** - * Folds a string of text, if necessary - * @access private - * @return string - * @param $value The string you wish to fold - */ - private function _doFolding($value,$indent) { - // Don't do anything if wordwrap is set to 0 - - if ($this->_dumpWordWrap !== 0 && is_string ($value) && strlen($value) > $this->_dumpWordWrap) { - $indent += $this->_dumpIndent; - $indent = str_repeat(' ',$indent); - $wrapped = wordwrap($value,$this->_dumpWordWrap,"\n$indent"); - $value = ">\n".$indent.$wrapped; - } else { - if ($this->setting_dump_force_quotes && is_string ($value)) - $value = '"' . $value . '"'; - } - - - return $value; - } - -// LOADING FUNCTIONS - - private function __load($input) { - $Source = $this->loadFromSource($input); - return $this->loadWithSource($Source); - } - - private function __loadString($input) { - $Source = $this->loadFromString($input); - return $this->loadWithSource($Source); - } - - private function loadWithSource($Source) { - if (empty ($Source)) return array(); - if ($this->setting_use_syck_is_possible && function_exists ('syck_load')) { - $array = syck_load (implode ('', $Source)); - return is_array($array) ? $array : array(); - } - - $this->path = array(); - $this->result = array(); - - $cnt = count($Source); - for ($i = 0; $i < $cnt; $i++) { - $line = $Source[$i]; - - $this->indent = strlen($line) - strlen(ltrim($line)); - $tempPath = $this->getParentPathByIndent($this->indent); - $line = self::stripIndent($line, $this->indent); - if (self::isComment($line)) continue; - if (self::isEmpty($line)) continue; - $this->path = $tempPath; - - $literalBlockStyle = self::startsLiteralBlock($line); - if ($literalBlockStyle) { - $line = rtrim ($line, $literalBlockStyle . " \n"); - $literalBlock = ''; - $line .= $this->LiteralPlaceHolder; - - while (++$i < $cnt && $this->literalBlockContinues($Source[$i], $this->indent)) { - $literalBlock = $this->addLiteralLine($literalBlock, $Source[$i], $literalBlockStyle); - } - $i--; - } - - while (++$i < $cnt && self::greedilyNeedNextLine($line)) { - $line = rtrim ($line, " \n\t\r") . ' ' . ltrim ($Source[$i], " \t"); - } - $i--; - - - - if (strpos ($line, '#')) { - if (strpos ($line, '"') === false && strpos ($line, "'") === false) - $line = preg_replace('/\s+#(.+)$/','',$line); - } - - $lineArray = $this->_parseLine($line); - - if ($literalBlockStyle) - $lineArray = $this->revertLiteralPlaceHolder ($lineArray, $literalBlock); - - $this->addArray($lineArray, $this->indent); - - foreach ($this->delayedPath as $indent => $delayedPath) - $this->path[$indent] = $delayedPath; - - $this->delayedPath = array(); - - } - return $this->result; - } - - private function loadFromSource ($input) { - if (!empty($input) && strpos($input, "\n") === false && file_exists($input)) - return file($input); - - return $this->loadFromString($input); - } - - private function loadFromString ($input) { - $lines = explode("\n",$input); - foreach ($lines as $k => $_) { - $lines[$k] = rtrim ($_, "\r"); - } - return $lines; - } - - /** - * Parses YAML code and returns an array for a node - * @access private - * @return array - * @param string $line A line from the YAML file - */ - private function _parseLine($line) { - if (!$line) return array(); - $line = trim($line); - - if (!$line) return array(); - $array = array(); - - $group = $this->nodeContainsGroup($line); - if ($group) { - $this->addGroup($line, $group); - $line = $this->stripGroup ($line, $group); - } - - if ($this->startsMappedSequence($line)) - return $this->returnMappedSequence($line); - - if ($this->startsMappedValue($line)) - return $this->returnMappedValue($line); - - if ($this->isArrayElement($line)) - return $this->returnArrayElement($line); - - if ($this->isPlainArray($line)) - return $this->returnPlainArray($line); - - - return $this->returnKeyValuePair($line); - - } - - /** - * Finds the type of the passed value, returns the value as the new type. - * @access private - * @param string $value - * @return mixed - */ - private function _toType($value) { - if ($value === '') return null; - $first_character = $value[0]; - $last_character = substr($value, -1, 1); - - $is_quoted = false; - do { - if (!$value) break; - if ($first_character != '"' && $first_character != "'") break; - if ($last_character != '"' && $last_character != "'") break; - $is_quoted = true; - } while (0); - - if ($is_quoted) - return strtr(substr ($value, 1, -1), array ('\\"' => '"', '\'\'' => '\'', '\\\'' => '\'')); - - if (strpos($value, ' #') !== false) - $value = preg_replace('/\s+#(.+)$/','',$value); - - if ($first_character == '[' && $last_character == ']') { - // Take out strings sequences and mappings - $innerValue = trim(substr ($value, 1, -1)); - if ($innerValue === '') return array(); - $explode = $this->_inlineEscape($innerValue); - // Propagate value array - $value = array(); - foreach ($explode as $v) { - $value[] = $this->_toType($v); - } - return $value; - } - - if (strpos($value,': ')!==false && $first_character != '{') { - $array = explode(': ',$value); - $key = trim($array[0]); - array_shift($array); - $value = trim(implode(': ',$array)); - $value = $this->_toType($value); - return array($key => $value); - } - - if ($first_character == '{' && $last_character == '}') { - $innerValue = trim(substr ($value, 1, -1)); - if ($innerValue === '') return array(); - // Inline Mapping - // Take out strings sequences and mappings - $explode = $this->_inlineEscape($innerValue); - // Propagate value array - $array = array(); - foreach ($explode as $v) { - $SubArr = $this->_toType($v); - if (empty($SubArr)) continue; - if (is_array ($SubArr)) { - $array[key($SubArr)] = $SubArr[key($SubArr)]; continue; - } - $array[] = $SubArr; - } - return $array; - } - - if ($value == 'null' || $value == 'NULL' || $value == 'Null' || $value == '' || $value == '~') { - return null; - } - - if (intval($first_character) > 0 && preg_match ('/^[1-9]+[0-9]*$/', $value)) { - $intvalue = (int)$value; - if ($intvalue != PHP_INT_MAX) - $value = $intvalue; - return $value; - } - - if (in_array($value, - array('true', 'on', '+', 'yes', 'y', 'True', 'TRUE', 'On', 'ON', 'YES', 'Yes', 'Y'))) { - return true; - } - - if (in_array(strtolower($value), - array('false', 'off', '-', 'no', 'n'))) { - return false; - } - - if (is_numeric($value)) { - if ($value === '0') return 0; - if (trim ($value, 0) === $value) - $value = (float)$value; - return $value; - } - - return $value; - } - - /** - * Used in inlines to check for more inlines or quoted strings - * @access private - * @return array - */ - private function _inlineEscape($inline) { - // There's gotta be a cleaner way to do this... - // While pure sequences seem to be nesting just fine, - // pure mappings and mappings with sequences inside can't go very - // deep. This needs to be fixed. - - $seqs = array(); - $maps = array(); - $saved_strings = array(); - - // Check for strings - $regex = '/(?:(")|(?:\'))((?(1)[^"]+|[^\']+))(?(1)"|\')/'; - if (preg_match_all($regex,$inline,$strings)) { - $saved_strings = $strings[0]; - $inline = preg_replace($regex,'YAMLString',$inline); - } - unset($regex); - - $i = 0; - do { - - // Check for sequences - while (preg_match('/\[([^{}\[\]]+)\]/U',$inline,$matchseqs)) { - $seqs[] = $matchseqs[0]; - $inline = preg_replace('/\[([^{}\[\]]+)\]/U', ('YAMLSeq' . (count($seqs) - 1) . 's'), $inline, 1); - } - - // Check for mappings - while (preg_match('/{([^\[\]{}]+)}/U',$inline,$matchmaps)) { - $maps[] = $matchmaps[0]; - $inline = preg_replace('/{([^\[\]{}]+)}/U', ('YAMLMap' . (count($maps) - 1) . 's'), $inline, 1); - } - - if ($i++ >= 10) break; - - } while (strpos ($inline, '[') !== false || strpos ($inline, '{') !== false); - - $explode = explode(', ',$inline); - $stringi = 0; $i = 0; - - while (1) { - - // Re-add the sequences - if (!empty($seqs)) { - foreach ($explode as $key => $value) { - if (strpos($value,'YAMLSeq') !== false) { - foreach ($seqs as $seqk => $seq) { - $explode[$key] = str_replace(('YAMLSeq'.$seqk.'s'),$seq,$value); - $value = $explode[$key]; - } - } - } - } - - // Re-add the mappings - if (!empty($maps)) { - foreach ($explode as $key => $value) { - if (strpos($value,'YAMLMap') !== false) { - foreach ($maps as $mapk => $map) { - $explode[$key] = str_replace(('YAMLMap'.$mapk.'s'), $map, $value); - $value = $explode[$key]; - } - } - } - } - - - // Re-add the strings - if (!empty($saved_strings)) { - foreach ($explode as $key => $value) { - while (strpos($value,'YAMLString') !== false) { - $explode[$key] = preg_replace('/YAMLString/',$saved_strings[$stringi],$value, 1); - unset($saved_strings[$stringi]); - ++$stringi; - $value = $explode[$key]; - } - } - } - - $finished = true; - foreach ($explode as $key => $value) { - if (strpos($value,'YAMLSeq') !== false) { - $finished = false; break; - } - if (strpos($value,'YAMLMap') !== false) { - $finished = false; break; - } - if (strpos($value,'YAMLString') !== false) { - $finished = false; break; - } - } - if ($finished) break; - - $i++; - if ($i > 10) - break; // Prevent infinite loops. - } - - return $explode; - } - - private function literalBlockContinues ($line, $lineIndent) { - if (!trim($line)) return true; - if (strlen($line) - strlen(ltrim($line)) > $lineIndent) return true; - return false; - } - - private function referenceContentsByAlias ($alias) { - do { - if (!isset($this->SavedGroups[$alias])) { echo "Bad group name: $alias."; break; } - $groupPath = $this->SavedGroups[$alias]; - $value = $this->result; - foreach ($groupPath as $k) { - $value = $value[$k]; - } - } while (false); - return $value; - } - - private function addArrayInline ($array, $indent) { - $CommonGroupPath = $this->path; - if (empty ($array)) return false; - - foreach ($array as $k => $_) { - $this->addArray(array($k => $_), $indent); - $this->path = $CommonGroupPath; - } - return true; - } - - private function addArray ($incoming_data, $incoming_indent) { - - // print_r ($incoming_data); - - if (count ($incoming_data) > 1) - return $this->addArrayInline ($incoming_data, $incoming_indent); - - $key = key ($incoming_data); - $value = isset($incoming_data[$key]) ? $incoming_data[$key] : null; - if ($key === '__!YAMLZero') $key = '0'; - - if ($incoming_indent == 0 && !$this->_containsGroupAlias && !$this->_containsGroupAnchor) { // Shortcut for root-level values. - if ($key || $key === '' || $key === '0') { - $this->result[$key] = $value; - } else { - $this->result[] = $value; end ($this->result); $key = key ($this->result); - } - $this->path[$incoming_indent] = $key; - return; - } - - - - $history = array(); - // Unfolding inner array tree. - $history[] = $_arr = $this->result; - foreach ($this->path as $k) { - $history[] = $_arr = $_arr[$k]; - } - - if ($this->_containsGroupAlias) { - $value = $this->referenceContentsByAlias($this->_containsGroupAlias); - $this->_containsGroupAlias = false; - } - - - // Adding string or numeric key to the innermost level or $this->arr. - if (is_string($key) && $key == '<<') { - if (!is_array ($_arr)) { $_arr = array (); } - - $_arr = array_merge ($_arr, $value); - } else if ($key || $key === '' || $key === '0') { - $_arr[$key] = $value; - } else { - if (!is_array ($_arr)) { $_arr = array ($value); $key = 0; } - else { $_arr[] = $value; end ($_arr); $key = key ($_arr); } - } - - $reverse_path = array_reverse($this->path); - $reverse_history = array_reverse ($history); - $reverse_history[0] = $_arr; - $cnt = count($reverse_history) - 1; - for ($i = 0; $i < $cnt; $i++) { - $reverse_history[$i+1][$reverse_path[$i]] = $reverse_history[$i]; - } - $this->result = $reverse_history[$cnt]; - - $this->path[$incoming_indent] = $key; - - if ($this->_containsGroupAnchor) { - $this->SavedGroups[$this->_containsGroupAnchor] = $this->path; - if (is_array ($value)) { - $k = key ($value); - if (!is_int ($k)) { - $this->SavedGroups[$this->_containsGroupAnchor][$incoming_indent + 2] = $k; - } - } - $this->_containsGroupAnchor = false; - } - - } - - private static function startsLiteralBlock ($line) { - $lastChar = substr (trim($line), -1); - if ($lastChar != '>' && $lastChar != '|') return false; - if ($lastChar == '|') return $lastChar; - // HTML tags should not be counted as literal blocks. - if (preg_match ('#<.*?>$#', $line)) return false; - return $lastChar; - } - - private static function greedilyNeedNextLine($line) { - $line = trim ($line); - if (!strlen($line)) return false; - if (substr ($line, -1, 1) == ']') return false; - if ($line[0] == '[') return true; - if (preg_match ('#^[^:]+?:\s*\[#', $line)) return true; - return false; - } - - private function addLiteralLine ($literalBlock, $line, $literalBlockStyle) { - $line = self::stripIndent($line); - $line = rtrim ($line, "\r\n\t ") . "\n"; - if ($literalBlockStyle == '|') { - return $literalBlock . $line; - } - if (strlen($line) == 0) - return rtrim($literalBlock, ' ') . "\n"; - if ($line == "\n" && $literalBlockStyle == '>') { - return rtrim ($literalBlock, " \t") . "\n"; - } - if ($line != "\n") - $line = trim ($line, "\r\n ") . " "; - return $literalBlock . $line; - } - - function revertLiteralPlaceHolder ($lineArray, $literalBlock) { - foreach ($lineArray as $k => $_) { - if (is_array($_)) - $lineArray[$k] = $this->revertLiteralPlaceHolder ($_, $literalBlock); - else if (substr($_, -1 * strlen ($this->LiteralPlaceHolder)) == $this->LiteralPlaceHolder) - $lineArray[$k] = rtrim ($literalBlock, " \r\n"); - } - return $lineArray; - } - - private static function stripIndent ($line, $indent = -1) { - if ($indent == -1) $indent = strlen($line) - strlen(ltrim($line)); - return substr ($line, $indent); - } - - private function getParentPathByIndent ($indent) { - if ($indent == 0) return array(); - $linePath = $this->path; - do { - end($linePath); $lastIndentInParentPath = key($linePath); - if ($indent <= $lastIndentInParentPath) array_pop ($linePath); - } while ($indent <= $lastIndentInParentPath); - return $linePath; - } - - - private function clearBiggerPathValues ($indent) { - - - if ($indent == 0) $this->path = array(); - if (empty ($this->path)) return true; - - foreach ($this->path as $k => $_) { - if ($k > $indent) unset ($this->path[$k]); - } - - return true; - } - - - private static function isComment ($line) { - if (!$line) return false; - if ($line[0] == '#') return true; - if (trim($line, " \r\n\t") == '---') return true; - return false; - } - - private static function isEmpty ($line) { - return (trim ($line) === ''); - } - - - private function isArrayElement ($line) { - if (!$line) return false; - if ($line[0] != '-') return false; - if (strlen ($line) > 3) - if (substr($line,0,3) == '---') return false; - - return true; - } - - private function isHashElement ($line) { - return strpos($line, ':'); - } - - private function isLiteral ($line) { - if ($this->isArrayElement($line)) return false; - if ($this->isHashElement($line)) return false; - return true; - } - - - private static function unquote ($value) { - if (!$value) return $value; - if (!is_string($value)) return $value; - if ($value[0] == '\'') return trim ($value, '\''); - if ($value[0] == '"') return trim ($value, '"'); - return $value; - } - - private function startsMappedSequence ($line) { - return ($line[0] == '-' && substr ($line, -1, 1) == ':'); - } - - private function returnMappedSequence ($line) { - $array = array(); - $key = self::unquote(trim(substr($line,1,-1))); - $array[$key] = array(); - $this->delayedPath = array(strpos ($line, $key) + $this->indent => $key); - return array($array); - } - - private function returnMappedValue ($line) { - $array = array(); - $key = self::unquote (trim(substr($line,0,-1))); - $array[$key] = ''; - return $array; - } - - private function startsMappedValue ($line) { - return (substr ($line, -1, 1) == ':'); - } - - private function isPlainArray ($line) { - return ($line[0] == '[' && substr ($line, -1, 1) == ']'); - } - - private function returnPlainArray ($line) { - return $this->_toType($line); - } - - private function returnKeyValuePair ($line) { - $array = array(); - $key = ''; - if (strpos ($line, ':')) { - // It's a key/value pair most likely - // If the key is in double quotes pull it out - if (($line[0] == '"' || $line[0] == "'") && preg_match('/^(["\'](.*)["\'](\s)*:)/',$line,$matches)) { - $value = trim(str_replace($matches[1],'',$line)); - $key = $matches[2]; - } else { - // Do some guesswork as to the key and the value - $explode = explode(':',$line); - $key = trim($explode[0]); - array_shift($explode); - $value = trim(implode(':',$explode)); - } - // Set the type of the value. Int, string, etc - $value = $this->_toType($value); - if ($key === '0') $key = '__!YAMLZero'; - $array[$key] = $value; - } else { - $array = array ($line); - } - return $array; - - } - - - private function returnArrayElement ($line) { - if (strlen($line) <= 1) return array(array()); // Weird %) - $array = array(); - $value = trim(substr($line,1)); - $value = $this->_toType($value); - $array[] = $value; - return $array; - } - - - private function nodeContainsGroup ($line) { - $symbolsForReference = 'A-z0-9_\-'; - if (strpos($line, '&') === false && strpos($line, '*') === false) return false; // Please die fast ;-) - if ($line[0] == '&' && preg_match('/^(&['.$symbolsForReference.']+)/', $line, $matches)) return $matches[1]; - if ($line[0] == '*' && preg_match('/^(\*['.$symbolsForReference.']+)/', $line, $matches)) return $matches[1]; - if (preg_match('/(&['.$symbolsForReference.']+)$/', $line, $matches)) return $matches[1]; - if (preg_match('/(\*['.$symbolsForReference.']+$)/', $line, $matches)) return $matches[1]; - if (preg_match ('#^\s*<<\s*:\s*(\*[^\s]+).*$#', $line, $matches)) return $matches[1]; - return false; - - } - - private function addGroup ($line, $group) { - if ($group[0] == '&') $this->_containsGroupAnchor = substr ($group, 1); - if ($group[0] == '*') $this->_containsGroupAlias = substr ($group, 1); - //print_r ($this->path); - } - - private function stripGroup ($line, $group) { - $line = trim(str_replace($group, '', $line)); - return $line; - } -} - -// Enable use of Spyc from command line -// The syntax is the following: php spyc.php spyc.yaml - -define ('SPYC_FROM_COMMAND_LINE', false); - -do { - if (!SPYC_FROM_COMMAND_LINE) break; - if (empty ($_SERVER['argc']) || $_SERVER['argc'] < 2) break; - if (empty ($_SERVER['SCRIPT_NAME']) || $_SERVER['SCRIPT_NAME'] != 'spyc.php') break; - $file = $argv[1]; - printf ("Spyc loading file: %s\n", $file); - print_r (spyc_load_file ($file)); -} while (0); \ No newline at end of file diff --git a/spyc/spyc.yaml b/spyc/spyc.yaml deleted file mode 100644 index 2e38e82..0000000 --- a/spyc/spyc.yaml +++ /dev/null @@ -1,196 +0,0 @@ -# -# S P Y C -# a simple php yaml class -# -# authors: [vlad andersen (vlad.andersen@gmail.com), chris wanstrath (chris@ozmm.org)] -# websites: [http://www.yaml.org, http://spyc.sourceforge.net/] -# license: [MIT License, http://www.opensource.org/licenses/mit-license.php] -# copyright: (c) 2005-2006 Chris Wanstrath, 2006-2009 Vlad Andersen -# -# spyc.yml - A file containing the YAML that Spyc understands. - ---- - -# Mappings - with proper types -String: Anyone's name, really. -Int: 13 -True: true -False: false -Zero: 0 -Null: NULL -Float: 5.34 - -# A sequence -- PHP Class -- Basic YAML Loader -- Very Basic YAML Dumper - -# A sequence of a sequence -- - - YAML is so easy to learn. - - Your config files will never be the same. - -# Sequence of mappings -- - cpu: 1.5ghz - ram: 1 gig - os : os x 10.4.1 - -# Mapped sequence -domains: - - yaml.org - - php.net - -# A sequence like this. -- program: Adium - platform: OS X - type: Chat Client - -# A folded block as a mapped value -no time: > - There isn't any time - for your tricks! - - Do you understand? - -# A literal block as a mapped value -some time: | - There is nothing but time - for your tricks. - -# Crazy combinations -databases: - - name: spartan - notes: - - Needs to be backed up - - Needs to be normalized - type: mysql - -# You can be a bit tricky -"if: you'd": like - -# Inline sequences -- [One, Two, Three, Four] - -# Nested Inline Sequences -- [One, [Two, And, Three], Four, Five] - -# Nested Nested Inline Sequences -- [This, [Is, Getting, [Ridiculous, Guys]], Seriously, [Show, Mercy]] - -# Inline mappings -- {name: chris, age: young, brand: lucky strike} - -# Nested inline mappings -- {name: mark, age: older than chris, brand: [marlboro, lucky strike]} - -# References -- they're shaky, but functional -dynamic languages: &DLANGS - - Perl - - Python - - PHP - - Ruby -compiled languages: &CLANGS - - C/C++ - - Java -all languages: - - *DLANGS - - *CLANGS - -# Added in .2.2: Escaped quotes -- you know, this shouldn't work. but it does. -- 'that''s my value.' -- 'again, that\'s my value.' -- "here's to \"quotes\", boss." - -# added in .2.3 -- {name: "Foo, Bar's", age: 20} - -# Added in .2.4: bug [ 1418193 ] Quote Values in Nested Arrays -- [a, ['1', "2"], b] - -# Added in .2.4: malformed YAML -all - javascripts: [dom1.js, dom.js] - -# Added in .2 -1040: Ooo, a numeric key! # And working comments? Wow! Colons in comments: a menace (0.3). - -hash_1: Hash #and a comment -hash_2: "Hash #and a comment" -"hash#3": "Hash (#) can appear in key too" - -float_test: 1.0 -float_test_with_quotes: '1.0' -float_inverse_test: 001 - -a_really_large_number: 115792089237316195423570985008687907853269984665640564039457584007913129639936 # 2^256 - -int array: [ 1, 2, 3 ] - -array on several lines: - [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 ] - -morelesskey: "" - -array_of_zero: [0] -sophisticated_array_of_zero: {rx: {tx: [0]} } - -switches: - - { row: 0, col: 0, func: {tx: [0, 1]} } - -empty_sequence: [ ] -empty_hash: { } - -special_characters: "[{]]{{]]" - -asterisks: "*" - -empty_key: - : - key: value - -trailing_colon: "foo:" - -multiline_items: - - type: SomeItem - values: [blah, blah, blah, - blah] - ints: [2, 54, 12, - 2143] - -many_lines: | - A quick - fox - - - jumped - over - - - - - - a lazy - - - - dog - - -werte: - 1: nummer 1 - 0: Stunde 0 - -noindent_records: -- record1: value1 -- record2: value2 - -"a:1": [1000] -"a:2": - - 2000 - -# [Endloop] -endloop: | - Does this line in the end indeed make Spyc go to an infinite loop? \ No newline at end of file diff --git a/spyc/tests/DumpTest.php b/spyc/tests/DumpTest.php deleted file mode 100644 index 0529d67..0000000 --- a/spyc/tests/DumpTest.php +++ /dev/null @@ -1,58 +0,0 @@ -files_to_test = array ('../spyc.yaml', 'failing1.yaml', 'indent_1.yaml', 'quotes.yaml'); - } - - public function testDump() { - foreach ($this->files_to_test as $file) { - $yaml = spyc_load(file_get_contents($file)); - $dump = Spyc::YAMLDump ($yaml); - $yaml_after_dump = Spyc::YAMLLoad ($dump); - $this->assertEquals ($yaml, $yaml_after_dump); - } - } - - public function testDumpWithQuotes() { - $Spyc = new Spyc(); - $Spyc->setting_dump_force_quotes = true; - foreach ($this->files_to_test as $file) { - $yaml = $Spyc->load(file_get_contents($file)); - $dump = $Spyc->dump ($yaml); - $yaml_after_dump = Spyc::YAMLLoad ($dump); - $this->assertEquals ($yaml, $yaml_after_dump); - } - } - - public function testDumpArrays() { - $dump = Spyc::YAMLDump(array ('item1', 'item2', 'item3')); - $awaiting = "---\n- item1\n- item2\n- item3\n"; - $this->assertEquals ($awaiting, $dump); - } - - public function testDumpNumerics() { - $dump = Spyc::YAMLDump(array ('404', '405', '500')); - $awaiting = "---\n- 404\n- 405\n- 500\n"; - $this->assertEquals ($awaiting, $dump); - } - - public function testDumpAsterisks() { - $dump = Spyc::YAMLDump(array ('*')); - $awaiting = "---\n- '*'\n"; - $this->assertEquals ($awaiting, $dump); - } - - - public function testEmpty() { - $dump = Spyc::YAMLDump(array("foo" => array())); - $awaiting = "---\nfoo: [ ]\n"; - $this->assertEquals ($awaiting, $dump); - } - -} \ No newline at end of file diff --git a/spyc/tests/IndentTest.php b/spyc/tests/IndentTest.php deleted file mode 100644 index 482d8e4..0000000 --- a/spyc/tests/IndentTest.php +++ /dev/null @@ -1,57 +0,0 @@ -Y = Spyc::YAMLLoad("indent_1.yaml"); - } - - public function testIndent_1() { - $this->assertEquals (array ('child_1' => 2, 'child_2' => 0, 'child_3' => 1), $this->Y['root']); - } - - public function testIndent_2() { - $this->assertEquals (array ('child_1' => 1, 'child_2' => 2), $this->Y['root2']); - } - - public function testIndent_3() { - $this->assertEquals (array (array ('resolutions' => array (1024 => 768, 1920 => 1200), 'producer' => 'Nec')), $this->Y['display']); - } - - public function testIndent_4() { - $this->assertEquals (array ( - array ('resolutions' => array (1024 => 768)), - array ('resolutions' => array (1920 => 1200)), - ), $this->Y['displays']); - } - - public function testIndent_5() { - $this->assertEquals (array (array ( - 'row' => 0, - 'col' => 0, - 'headsets_affected' => array ( - array ( - 'ports' => array (0), - 'side' => 'left', - ) - ), - 'switch_function' => array ( - 'ics_ptt' => true - ) - )), $this->Y['nested_hashes_and_seqs']); - } - - public function testIndent_6() { - $this->assertEquals (array ( - 'h' => array ( - array ('a' => 'b', 'a1' => 'b1'), - array ('c' => 'd') - ) - ), $this->Y['easier_nest']); - } - -} \ No newline at end of file diff --git a/spyc/tests/ParseTest.php b/spyc/tests/ParseTest.php deleted file mode 100644 index 2eecf54..0000000 --- a/spyc/tests/ParseTest.php +++ /dev/null @@ -1,305 +0,0 @@ -yaml = spyc_load_file('../spyc.yaml'); - } - - public function testMergeHashKeys() { - $Expected = array ( - array ('step' => array('instrument' => 'Lasik 2000', 'pulseEnergy' => 5.4, 'pulseDuration' => 12, 'repetition' => 1000, 'spotSize' => '1mm')), - array ('step' => array('instrument' => 'Lasik 2000', 'pulseEnergy' => 5.4, 'pulseDuration' => 12, 'repetition' => 1000, 'spotSize' => '2mm')), - ); - $Actual = spyc_load_file ('indent_1.yaml'); - $this->assertEquals ($Expected, $Actual['steps']); - } - - public function testDeathMasks() { - $Expected = array ('sad' => 2, 'magnificent' => 4); - $Actual = spyc_load_file ('indent_1.yaml'); - $this->assertEquals ($Expected, $Actual['death masks are']); - } - - public function testDevDb() { - $Expected = array ('adapter' => 'mysql', 'host' => 'localhost', 'database' => 'rails_dev'); - $Actual = spyc_load_file ('indent_1.yaml'); - $this->assertEquals ($Expected, $Actual['development']); - } - - public function testNumericKey() { - $this->assertEquals ("Ooo, a numeric key!", $this->yaml[1040]); - } - - public function testMappingsString() { - $this->assertEquals ("Anyone's name, really.", $this->yaml['String']); - } - - public function testMappingsInt() { - $this->assertSame (13, $this->yaml['Int']); - } - - public function testMappingsBooleanTrue() { - $this->assertSame (true, $this->yaml['True']); - } - - public function testMappingsBooleanFalse() { - $this->assertSame (false, $this->yaml['False']); - } - - public function testMappingsZero() { - $this->assertSame (0, $this->yaml['Zero']); - } - - public function testMappingsNull() { - $this->assertSame (null, $this->yaml['Null']); - } - - public function testMappingsFloat() { - $this->assertSame (5.34, $this->yaml['Float']); - } - - public function testSeq0() { - $this->assertEquals ("PHP Class", $this->yaml[0]); - } - - public function testSeq1() { - $this->assertEquals ("Basic YAML Loader", $this->yaml[1]); - } - - public function testSeq2() { - $this->assertEquals ("Very Basic YAML Dumper", $this->yaml[2]); - } - - public function testSeq3() { - $this->assertEquals (array("YAML is so easy to learn.", - "Your config files will never be the same."), $this->yaml[3]); - } - - public function testSeqMap() { - $this->assertEquals (array("cpu" => "1.5ghz", "ram" => "1 gig", - "os" => "os x 10.4.1"), $this->yaml[4]); - } - - public function testMappedSequence() { - $this->assertEquals (array("yaml.org", "php.net"), $this->yaml['domains']); - } - - public function testAnotherSequence() { - $this->assertEquals (array("program" => "Adium", "platform" => "OS X", - "type" => "Chat Client"), $this->yaml[5]); - } - - public function testFoldedBlock() { - $this->assertEquals ("There isn't any time for your tricks!\nDo you understand?", $this->yaml['no time']); - } - - public function testLiteralAsMapped() { - $this->assertEquals ("There is nothing but time\nfor your tricks.", $this->yaml['some time']); - } - - public function testCrazy() { - $this->assertEquals (array( array("name" => "spartan", "notes" => - array( "Needs to be backed up", - "Needs to be normalized" ), - "type" => "mysql" )), $this->yaml['databases']); - } - - public function testColons() { - $this->assertEquals ("like", $this->yaml["if: you'd"]); - } - - public function testInline() { - $this->assertEquals (array("One", "Two", "Three", "Four"), $this->yaml[6]); - } - - public function testNestedInline() { - $this->assertEquals (array("One", array("Two", "And", "Three"), "Four", "Five"), $this->yaml[7]); - } - - public function testNestedNestedInline() { - $this->assertEquals (array( "This", array("Is", "Getting", array("Ridiculous", "Guys")), - "Seriously", array("Show", "Mercy")), $this->yaml[8]); - } - - public function testInlineMappings() { - $this->assertEquals (array("name" => "chris", "age" => "young", "brand" => "lucky strike"), $this->yaml[9]); - } - - public function testNestedInlineMappings() { - $this->assertEquals (array("name" => "mark", "age" => "older than chris", - "brand" => array("marlboro", "lucky strike")), $this->yaml[10]); - } - - public function testReferences() { - $this->assertEquals (array('Perl', 'Python', 'PHP', 'Ruby'), $this->yaml['dynamic languages']); - } - - public function testReferences2() { - $this->assertEquals (array('C/C++', 'Java'), $this->yaml['compiled languages']); - } - - public function testReferences3() { - $this->assertEquals (array( - array('Perl', 'Python', 'PHP', 'Ruby'), - array('C/C++', 'Java') - ), $this->yaml['all languages']); - } - - public function testEscapedQuotes() { - $this->assertEquals ("you know, this shouldn't work. but it does.", $this->yaml[11]); - } - - public function testEscapedQuotes_2() { - $this->assertEquals ( "that's my value.", $this->yaml[12]); - } - - public function testEscapedQuotes_3() { - $this->assertEquals ("again, that's my value.", $this->yaml[13]); - } - - public function testQuotes() { - $this->assertEquals ("here's to \"quotes\", boss.", $this->yaml[14]); - } - - public function testQuoteSequence() { - $this->assertEquals ( array( 'name' => "Foo, Bar's", 'age' => 20), $this->yaml[15]); - } - - public function testShortSequence() { - $this->assertEquals (array( 0 => "a", 1 => array (0 => 1, 1 => 2), 2 => "b"), $this->yaml[16]); - } - - public function testHash_1() { - $this->assertEquals ("Hash", $this->yaml['hash_1']); - } - - public function testHash_2() { - $this->assertEquals ('Hash #and a comment', $this->yaml['hash_2']); - } - - public function testHash_3() { - $this->assertEquals ('Hash (#) can appear in key too', $this->yaml['hash#3']); - } - - public function testEndloop() { - $this->assertEquals ("Does this line in the end indeed make Spyc go to an infinite loop?", $this->yaml['endloop']); - } - - public function testReallyLargeNumber() { - $this->assertEquals ('115792089237316195423570985008687907853269984665640564039457584007913129639936', $this->yaml['a_really_large_number']); - } - - public function testFloatWithZeros() { - $this->assertSame ('1.0', $this->yaml['float_test']); - } - - public function testFloatWithQuotes() { - $this->assertSame ('1.0', $this->yaml['float_test_with_quotes']); - } - - public function testFloatInverse() { - $this->assertEquals ('001', $this->yaml['float_inverse_test']); - } - - public function testIntArray() { - $this->assertEquals (array (1, 2, 3), $this->yaml['int array']); - } - - public function testArrayOnSeveralLines() { - $this->assertEquals (array (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19), $this->yaml['array on several lines']); - } - - public function testmoreLessKey() { - $this->assertEquals ('', $this->yaml['morelesskey']); - } - - public function testArrayOfZero() { - $this->assertSame (array(0), $this->yaml['array_of_zero']); - } - - public function testSophisticatedArrayOfZero() { - $this->assertSame (array('rx' => array ('tx' => array (0))), $this->yaml['sophisticated_array_of_zero']); - } - - public function testSwitches() { - $this->assertEquals (array (array ('row' => 0, 'col' => 0, 'func' => array ('tx' => array(0, 1)))), $this->yaml['switches']); - } - - public function testEmptySequence() { - $this->assertSame (array(), $this->yaml['empty_sequence']); - } - - public function testEmptyHash() { - $this->assertSame (array(), $this->yaml['empty_hash']); - } - - public function testEmptykey() { - $this->assertSame (array('' => array ('key' => 'value')), $this->yaml['empty_key']); - } - - public function testMultilines() { - $this->assertSame (array(array('type' => 'SomeItem', 'values' => array ('blah', 'blah', 'blah', 'blah'), 'ints' => array(2, 54, 12, 2143))), $this->yaml['multiline_items']); - } - - public function testManyNewlines() { - $this->assertSame ('A quick -fox - - -jumped -over - - - - - -a lazy - - - -dog', $this->yaml['many_lines']); - } - - public function testWerte() { - $this->assertSame (array ('1' => 'nummer 1', '0' => 'Stunde 0'), $this->yaml['werte']); - } - - /* public function testNoIndent() { - $this->assertSame (array( - array ('record1'=>'value1'), - array ('record2'=>'value2') - ) - , $this->yaml['noindent_records']); - } */ - - public function testColonsInKeys() { - $this->assertSame (array (1000), $this->yaml['a:1']); - } - - public function testColonsInKeys2() { - $this->assertSame (array (2000), $this->yaml['a:2']); - } - - public function testSpecialCharacters() { - $this->assertSame ('[{]]{{]]', $this->yaml['special_characters']); - } - - public function testAngleQuotes() { - $Quotes = Spyc::YAMLLoad('quotes.yaml'); - $this->assertEquals (array ('html_tags' => array ('
    ', '

    '), 'html_content' => array ('

    hello world

    ', 'hello
    world'), 'text_content' => array ('hello world')), - $Quotes); - } - - public function testFailingColons() { - $Failing = Spyc::YAMLLoad('failing1.yaml'); - $this->assertSame (array ('MyObject' => array ('Prop1' => array ('key1:val1'))), - $Failing); - } - -} \ No newline at end of file diff --git a/spyc/tests/failing1.yaml b/spyc/tests/failing1.yaml deleted file mode 100644 index 6906a51..0000000 --- a/spyc/tests/failing1.yaml +++ /dev/null @@ -1,2 +0,0 @@ -MyObject: - Prop1: {key1:val1} \ No newline at end of file diff --git a/spyc/tests/indent_1.yaml b/spyc/tests/indent_1.yaml deleted file mode 100644 index 5a55434..0000000 --- a/spyc/tests/indent_1.yaml +++ /dev/null @@ -1,53 +0,0 @@ -root: - child_1: 2 - - child_2: 0 - child_3: 1 - -root2: - child_1: 1 -# A comment - child_2: 2 - -displays: - - resolutions: - 1024: 768 - - resolutions: - 1920: 1200 - -display: - - resolutions: - 1024: 768 - 1920: 1200 - producer: "Nec" - -nested_hashes_and_seqs: - - { row: 0, col: 0, headsets_affected: [{ports: [0], side: left}], switch_function: {ics_ptt: true} } - -easier_nest: { h: [{a: b, a1: b1}, {c: d}] } - -steps: - - step: &id001 - instrument: Lasik 2000 - pulseEnergy: 5.4 - pulseDuration: 12 - repetition: 1000 - spotSize: 1mm - - step: - <<: *id001 - spotSize: 2mm - -death masks are: - sad: 2 - <<: {magnificent: 4} - -login: &login - adapter: mysql - host: localhost - -development: - database: rails_dev - <<: *login - -"key": "value:" -colon_only: ":" \ No newline at end of file diff --git a/spyc/tests/quotes.yaml b/spyc/tests/quotes.yaml deleted file mode 100644 index 2ceea86..0000000 --- a/spyc/tests/quotes.yaml +++ /dev/null @@ -1,8 +0,0 @@ -html_tags: - -
    - -

    -html_content: - -

    hello world

    - - hello
    world -text_content: - - hello world \ No newline at end of file diff --git a/tar.class.php b/tar.class.php deleted file mode 100644 index c8070b6..0000000 --- a/tar.class.php +++ /dev/null @@ -1,467 +0,0 @@ - -Description: - This class reads and writes Tape-Archive (TAR) Files and Gzip - compressed TAR files, which are mainly used on UNIX systems. - This class works on both windows AND unix systems, and does - NOT rely on external applications!! Woohoo! -Usage: - Copyright (C) 2002 Josh Barger - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details at: - http://www.gnu.org/copyleft/lesser.html - If you use this script in your application/website, please - send me an e-mail letting me know about it :) -Bugs: - Please report any bugs you might find to my e-mail address - at joshb@npt.com. If you have already created a fix/patch - for the bug, please do send it to me so I can incorporate it into my release. -Version History: - 1.0 04/10/2002 - InitialRelease - 2.0 04/11/2002 - Merged both tarReader and tarWriter - classes into one - - Added support for gzipped tar files - Remember to name for .tar.gz or .tgz - if you use gzip compression! - :: THIS REQUIRES ZLIB EXTENSION :: - - Added additional comments to - functions to help users - - Added ability to remove files and - directories from archive - 2.1 04/12/2002 - Fixed serious bug in generating tar - - Created another example file - - Added check to make sure ZLIB is - installed before running GZIP - compression on TAR - 2.2 05/07/2002 - Added automatic detection of Gzipped - tar files (Thanks go to Jrgen Falch - for the idea) - - Changed "private" functions to have - special function names beginning with - two underscores -======================================================================= -*/ -class tar { - // Unprocessed Archive Information - public $filename; - public $isGzipped; - public $tar_file; - // Processed Archive Information - public $files; - public $directories; - public $numFiles = 0; - public $numDirectories = 0; - // Class Constructor -- Does nothing... - function tar() { - return true; - } - // Computes the unsigned Checksum of a file's header - // to try to ensure valid file - // PRIVATE ACCESS FUNCTION - function __computeUnsignedChecksum($bytestring) { - $unsigned_chksum=0; - for($i=0; $i<512; $i++) - $unsigned_chksum += ord($bytestring[$i]); - for($i=0; $i<8; $i++) - $unsigned_chksum -= ord($bytestring[148 + $i]); - $unsigned_chksum += ord(" ") * 8; - return $unsigned_chksum; - } - // Converts a NULL padded string to a non-NULL padded string - // PRIVATE ACCESS FUNCTION - function __parseNullPaddedString($string) { - $position = strpos($string,chr(0)); - return substr($string,0,$position); - } - // This function parses the current TAR file - // PRIVATE ACCESS FUNCTION - function __parseTar() { - // Read Files from archive - $tar_length = strlen($this->tar_file); - $main_offset = 0; - while($main_offset < $tar_length) { - // If we read a block of 512 nulls, we are at the end of the archive - if(substr($this->tar_file,$main_offset,512) == str_repeat(chr(0),512)) - break; - // Parse file name - $file_name = $this->__parseNullPaddedString(substr($this->tar_file,$main_offset,100)); - // Parse the file mode - $file_mode = substr($this->tar_file,$main_offset + 100,8); - // Parse the file user ID - $file_uid = octdec(substr($this->tar_file,$main_offset + 108,8)); - // Parse the file group ID - $file_gid = octdec(substr($this->tar_file,$main_offset + 116,8)); - // Parse the file size - $file_size = octdec(substr($this->tar_file,$main_offset + 124,12)); - // Parse the file update time - unix timestamp format - $file_time = octdec(substr($this->tar_file,$main_offset + 136,12)); - // Parse Checksum - $file_chksum = octdec(substr($this->tar_file,$main_offset + 148,6)); - // Parse user name - $file_uname = $this->__parseNullPaddedString(substr($this->tar_file,$main_offset + 265,32)); - // Parse Group name - $file_gname = $this->__parseNullPaddedString(substr($this->tar_file,$main_offset + 297,32)); - // Make sure our file is valid - if($this->__computeUnsignedChecksum(substr($this->tar_file,$main_offset,512)) != $file_chksum) - return false; - // Parse File Contents - $file_contents = substr($this->tar_file,$main_offset + 512,$file_size); - /* ### Unused Header Information ### - $activeFile["typeflag"] = substr($this->tar_file,$main_offset + 156,1); - $activeFile["linkname"] = substr($this->tar_file,$main_offset + 157,100); - $activeFile["magic"] = substr($this->tar_file,$main_offset + 257,6); - $activeFile["version"] = substr($this->tar_file,$main_offset + 263,2); - $activeFile["devmajor"] = substr($this->tar_file,$main_offset + 329,8); - $activeFile["devminor"] = substr($this->tar_file,$main_offset + 337,8); - $activeFile["prefix"] = substr($this->tar_file,$main_offset + 345,155); - $activeFile["endheader"] = substr($this->tar_file,$main_offset + 500,12); - */ - if($file_size > 0) { - // Increment number of files - $this->numFiles++; - // Create us a new file in our array - $activeFile = &$this->files[]; - // Asign Values - $activeFile["name"] = $file_name; - $activeFile["mode"] = $file_mode; - $activeFile["size"] = $file_size; - $activeFile["time"] = $file_time; - $activeFile["user_id"] = $file_uid; - $activeFile["group_id"] = $file_gid; - $activeFile["user_name"] = $file_uname; - $activeFile["group_name"] = $file_gname; - $activeFile["checksum"] = $file_chksum; - $activeFile["file"] = $file_contents; - } else { - // Increment number of directories - $this->numDirectories++; - // Create a new directory in our array - $activeDir = &$this->directories[]; - // Assign values - $activeDir["name"] = $file_name; - $activeDir["mode"] = $file_mode; - $activeDir["time"] = $file_time; - $activeDir["user_id"] = $file_uid; - $activeDir["group_id"] = $file_gid; - $activeDir["user_name"] = $file_uname; - $activeDir["group_name"] = $file_gname; - $activeDir["checksum"] = $file_chksum; - } - // Move our offset the number of blocks we have processed - $main_offset += 512 + (ceil($file_size / 512) * 512); - } - return true; - } - // Read a non gzipped tar file in for processing - // PRIVATE ACCESS FUNCTION - function __readTar($filename='') { - // Set the filename to load - if(!$filename) - $filename = $this->filename; - // Read in the TAR file - $fp = fopen($filename,"rb"); - $this->tar_file = fread($fp,filesize($filename)); - fclose($fp); - if($this->tar_file[0] == chr(31) && $this->tar_file[1] == chr(139) && $this->tar_file[2] == chr(8)) { - if(!function_exists("gzinflate")) - return false; - $this->isGzipped = TRUE; - $this->tar_file = gzinflate(substr($this->tar_file,10,-4)); - } - // Parse the TAR file - $this->__parseTar(); - return true; - } - // Generates a TAR file from the processed data - // PRIVATE ACCESS FUNCTION - function __generateTAR() { - // Clear any data currently in $this->tar_file - unset($this->tar_file); - $this->tar_file=''; - // Generate Records for each directory, if we have directories - if($this->numDirectories > 0) { - foreach($this->directories as $key => $information) { - unset($header); - // Generate tar header for this directory - // Filename, Permissions, UID, GID, size, Time, checksum, typeflag, linkname, magic, version, user name, group name, devmajor, devminor, prefix, end - $header .= str_pad($information["name"],100,chr(0)); - $header .= str_pad(decoct($information["mode"]),7,"0",STR_PAD_LEFT) . chr(0); - $header .= str_pad(decoct($information["user_id"]),7,"0",STR_PAD_LEFT) . chr(0); - $header .= str_pad(decoct($information["group_id"]),7,"0",STR_PAD_LEFT) . chr(0); - $header .= str_pad(decoct(0),11,"0",STR_PAD_LEFT) . chr(0); - $header .= str_pad(decoct($information["time"]),11,"0",STR_PAD_LEFT) . chr(0); - $header .= str_repeat(" ",8); - $header .= "5"; - $header .= str_repeat(chr(0),100); - $header .= str_pad("ustar",6,chr(32)); - $header .= chr(32) . chr(0); - $header .= str_pad("",32,chr(0)); - $header .= str_pad("",32,chr(0)); - $header .= str_repeat(chr(0),8); - $header .= str_repeat(chr(0),8); - $header .= str_repeat(chr(0),155); - $header .= str_repeat(chr(0),12); - // Compute header checksum - $checksum = str_pad(decoct($this->__computeUnsignedChecksum($header)),6,"0",STR_PAD_LEFT); - for($i=0; $i<6; $i++) { - $header[(148 + $i)] = substr($checksum,$i,1); - } - $header[154] = chr(0); - $header[155] = chr(32); - // Add new tar formatted data to tar file contents - $this->tar_file .= $header; - } - } - // Generate Records for each file, if we have files (We should...) - if($this->numFiles > 0) { - foreach($this->files as $key => $information) { - unset($header); - $header=''; - // Generate the TAR header for this file - // Filename, Permissions, UID, GID, size, Time, checksum, typeflag, linkname, magic, version, user name, group name, devmajor, devminor, prefix, end - $header .= str_pad($information["name"],100,chr(0)); - $header .= str_pad(decoct($information["mode"]),7,"0",STR_PAD_LEFT) . chr(0); - $header .= str_pad(decoct($information["user_id"]),7,"0",STR_PAD_LEFT) . chr(0); - $header .= str_pad(decoct($information["group_id"]),7,"0",STR_PAD_LEFT) . chr(0); - $header .= str_pad(decoct($information["size"]),11,"0",STR_PAD_LEFT) . chr(0); - $header .= str_pad(decoct($information["time"]),11,"0",STR_PAD_LEFT) . chr(0); - $header .= str_repeat(" ",8); - $header .= "0"; - $header .= str_repeat(chr(0),100); - $header .= str_pad("ustar",6,chr(32)); - $header .= chr(32) . chr(0); - $header .= str_pad($information["user_name"],32,chr(0)); // How do I get a file's user name from PHP? - $header .= str_pad($information["group_name"],32,chr(0)); // How do I get a file's group name from PHP? - $header .= str_repeat(chr(0),8); - $header .= str_repeat(chr(0),8); - $header .= str_repeat(chr(0),155); - $header .= str_repeat(chr(0),12); - // Compute header checksum - $checksum = str_pad(decoct($this->__computeUnsignedChecksum($header)),6,"0",STR_PAD_LEFT); - for($i=0; $i<6; $i++) { - $header[(148 + $i)] = substr($checksum,$i,1); - } - $header[154] = chr(0); - $header[155] = chr(32); - // Pad file contents to byte count divisible by 512 - $file_contents = str_pad($information["file"],(ceil($information["size"] / 512) * 512),chr(0)); - // Add new tar formatted data to tar file contents - $this->tar_file .= $header . $file_contents; - } - } - // Add 512 bytes of NULLs to designate EOF - $this->tar_file .= str_repeat(chr(0),512); - return true; - } - // Open a TAR file - function openTAR($filename) { - // Clear any values from previous tar archives - //unset($this->filename); - $this->filename=''; - //unset($this->isGzipped); - $this->isGzipped=0; - //unset($this->tar_file); - $this->tar_file=0; - //unset($this->files); - $this->files=''; - //unset($this->directories); - //unset($this->numFiles); - //unset($this->numDirectories); - $this->directories=''; - $this->numFiles=0; - $this->numDirectories=0; - // If the tar file doesn't exist... - if(!file_exists($filename)) - return false; - $this->filename = $filename; - // Parse this file - $this->__readTar(); - return true; - } - // Appends a tar file to the end of the currently opened tar file - function appendTar($filename) { - // If the tar file doesn't exist... - if(!file_exists($filename)) - return false; - $this->__readTar($filename); - return true; - } - // Retrieves information about a file in the current tar archive - function getFile($filename) { - if($this->numFiles > 0) { - foreach($this->files as $key => $information) { - if($information["name"] == $filename) - return $information; - } - } - return false; - } - // Retrieves information about a directory in the current tar archive - function getDirectory($dirname) { - if($this->numDirectories > 0) { - foreach($this->directories as $key => $information) { - if($information["name"] == $dirname) - return $information; - } - } - return false; - } - // Check if this tar archive contains a specific file - function containsFile($filename) { - if($this->numFiles > 0) { - foreach($this->files as $key => $information) { - if($information["name"] == $filename) - return true; - } - } - return false; - } - // Check if this tar archive contains a specific directory - function containsDirectory($dirname) { - if($this->numDirectories > 0) { - foreach($this->directories as $key => $information) { - if($information["name"] == $dirname) - return true; - } - } - return false; - } - // Add a directory to this tar archive - function addDirectory($dirname) { - if(!file_exists($dirname)) - return false; - // Get directory information - $file_information = stat($dirname); - // Add directory to processed data - $this->numDirectories++; - $activeDir = &$this->directories[]; - $activeDir["name"] = $dirname; - $activeDir["mode"] = $file_information["mode"]; - $activeDir["time"] = $file_information["time"]; - $activeDir["user_id"] = $file_information["uid"]; - $activeDir["group_id"] = $file_information["gid"]; - $activeDir["checksum"] = $checksum; - return true; - } - // Add a file to the tar archive - function addFile($filename) { - // Make sure the file we are adding exists! - if(!file_exists($filename)) - return false; - // Make sure there are no other files in the archive that have this same filename - if($this->containsFile($filename)) - return false; - // Get file information - $file_information = stat($filename); - // Read in the file's contents - $fp = fopen($filename,"rb"); - $file_contents = fread($fp,filesize($filename)); - fclose($fp); - // Add file to processed data - $this->numFiles++; - $activeFile = &$this->files[]; - $activeFile["name"] = $filename; - $activeFile["mode"] = $file_information["mode"]; - $activeFile["user_id"] = $file_information["uid"]; - $activeFile["group_id"] = $file_information["gid"]; - $activeFile["size"] = $file_information["size"]; - $activeFile["time"] = $file_information["mtime"]; - $activeFile["checksum"] = $checksum; - $activeFile["user_name"] = ""; - $activeFile["group_name"] = ""; - $activeFile["file"] = $file_contents; - return true; - } - // Add a file to the tar archive - function addData($filename,$data,$time=0) { - global $gBitSystem; - // Make sure there are no other files in the archive that have this same filename - if($this->containsFile($filename)) - return false; - if (!$time) $time=$gBitSystem->getUTCTime(); - // Read in the file's contents - $file_contents = $data; - // Add file to processed data - $this->numFiles++; - $activeFile = &$this->files[]; - $activeFile["name"] = $filename; - $activeFile["mode"] = octdec("666"); - $activeFile["user_id"] = ""; - $activeFile["group_id"] = ""; - $activeFile["size"] = strlen($data); - $activeFile["time"] = $time; - if(!isset($checksum)) $checksum=0; - $activeFile["checksum"] = $checksum; - $activeFile["user_name"] = ""; - $activeFile["group_name"] = ""; - $activeFile["file"] = $file_contents; - return true; - } - // Remove a file from the tar archive - function removeFile($filename) { - if($this->numFiles > 0) { - foreach($this->files as $key => $information) { - if($information["name"] == $filename) { - $this->numFiles--; - unset($this->files[$key]); - return true; - } - } - } - return false; - } - // Remove a directory from the tar archive - function removeDirectory($dirname) { - if($this->numDirectories > 0) { - foreach($this->directories as $key => $information) { - if($information["name"] == $dirname) { - $this->numDirectories--; - unset($this->directories[$key]); - return true; - } - } - } - return false; - } - // Write the currently loaded tar archive to disk - function saveTar() { - if(!$this->filename) - return false; - // Write tar to current file using specified gzip compression - $this->toTar($this->filename,$this->isGzipped); - return true; - } - // Saves tar archive to a different file than the current file - function toTar($filename,$useGzip) { - if(!$filename) - return false; - // Encode processed files into TAR file format - $this->__generateTar(); - // GZ Compress the data if we need to - if($useGzip) { - // Make sure we have gzip support - if(!function_exists("gzencode")) - return false; - $file = gzencode($this->tar_file); - } else { - $file = $this->tar_file; - } - // Write the TAR file - $fp = fopen($filename,"wb"); - fwrite($fp,$file); - fclose($fp); - return true; - } -} -?> diff --git a/template_biticon_updater.sh b/template_biticon_updater.sh deleted file mode 100644 index e7cf3b5..0000000 --- a/template_biticon_updater.sh +++ /dev/null @@ -1,340 +0,0 @@ -#!/bin/bash -echo "This script will update all your custom templates to use the new set of -icons used in bitweaver R2. To run this script please copy it to your theme -directory and execute it with: sh $0" - -if [[ ( $1 == '--help' ) || ( $1 == '-h' ) || ( $1 == '?' ) ]] -then - exit -fi - -# Check to see if we've already made a backup -if [ -f theme_backup.tar.gz ] -then - echo "I have found a css backup file. Please rename or remove the file -theme_backup.tar.gz before executing this script again." - exit -fi - -echo "Creating backup of theme." -tar -czf theme_backup.tar.gz * -echo -echo "The script will continue in 5 seconds - hit to abort." -echo 5; sleep 1 -echo 4; sleep 1 -echo 3; sleep 1 -echo 2; sleep 1 -echo 1; sleep 1 -echo -echo "====== Executing substitutions ======" -echo -echo "------ Doing Biticons Now ------" -echo - -# we should make sure that ipackage comes before iname - this is true in bitweaver but who knows in custom templates... -#find . -name "*.tpl" -exec perl -i -wpe 's/{biticon([^}]*?)iname="?(\w+)"?([^}]*?)ipackage="?(\w+)"?/{biticon$1ipackage="$4"$3iname="$2" /g' {} \; - -echo substituting liberty biticons -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\baccept"?\s/{biticon$1ipackage="icons"$2iname="dialog-ok" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\badministration"?\s/{biticon$1ipackage="icons"$2iname="preferences-system" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bassign"?\s/{biticon$1ipackage="icons"$2iname="mail-attachment" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bback"?\s/{biticon$1ipackage="icons"$2iname="go-previous" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bbithelp"?\s/{biticon$1ipackage="icons"$2iname="help-browser" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bcancel"?\s/{biticon$1ipackage="icons"$2iname="dialog-cancel" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bclose"?\s/{biticon$1ipackage="icons"$2iname="window-close" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bconfig"?\s/{biticon$1ipackage="icons"$2iname="document-properties" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bcopy"?\s/{biticon$1ipackage="icons"$2iname="edit-copy" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bcopy_folder"?\s/{biticon$1ipackage="icons"$2iname="edit-copy" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bcurrent"?\s/{biticon$1ipackage="icons"$2iname="emblem-default" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bedit"?\s/{biticon$1ipackage="icons"$2iname="accessories-text-editor" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bedit_small"?\s/{biticon$1ipackage="icons"$2iname="accessories-text-editor" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bdelete"?\s/{biticon$1ipackage="icons"$2iname="edit-delete" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bdelete_small"?\s/{biticon$1ipackage="icons"$2iname="edit-delete" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bdownload"?\s/{biticon$1ipackage="icons"$2iname="emblem-downloads" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\berror"?\s/{biticon$1ipackage="icons"$2iname="dialog-error" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bexport"?\s/{biticon$1ipackage="icons"$2iname="document-save-as" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bfind"?\s/{biticon$1ipackage="icons"$2iname="edit-find" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bdirectory"?\s/{biticon$1ipackage="icons"$2iname="folder" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bfolder"?\s/{biticon$1ipackage="icons"$2iname="folder" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bfolder_open"?\s/{biticon$1ipackage="icons"$2iname="folder-open" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bhelp"?\s/{biticon$1ipackage="icons"$2iname="help-contents" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bhistory"?\s/{biticon$1ipackage="icons"$2iname="appointment-new" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bhome"?\s/{biticon$1ipackage="icons"$2iname="go-home" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bimport"?\s/{biticon$1ipackage="icons"$2iname="document-open" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\binfo"?\s/{biticon$1ipackage="icons"$2iname="dialog-information" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\binsert"?\s/{biticon$1ipackage="icons"$2iname="insert-object" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bmail_send"?\s/{biticon$1ipackage="icons"$2iname="mail-forward" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bnote"?\s/{biticon$1ipackage="icons"$2iname="x-office-document" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bnav_up"?\s/{biticon$1ipackage="icons"$2iname="go-up" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bnav_down"?\s/{biticon$1ipackage="icons"$2iname="go-down" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bnav_next"?\s/{biticon$1ipackage="icons"$2iname="go-next" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bnav_prev"?\s/{biticon$1ipackage="icons"$2iname="go-previous" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bnav_first"?\s/{biticon$1ipackage="icons"$2iname="go-first" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bnav_last"?\s/{biticon$1ipackage="icons"$2iname="go-last" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bnew"?\s/{biticon$1ipackage="icons"$2iname="document-new" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bpermissions"?\s/{biticon$1ipackage="icons"$2iname="emblem-shared" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bpermissions_set"?\s/{biticon$1ipackage="icons"$2iname="emblem-readonly" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bplugin"?\s/{biticon$1ipackage="icons"$2iname="applications-accessories" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bprint"?\s/{biticon$1ipackage="icons"$2iname="document-print" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\brefresh"?\s/{biticon$1ipackage="icons"$2iname="view-refresh" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\breply"?\s/{biticon$1ipackage="icons"$2iname="mail-reply-sender" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\breply_quote"?\s/{biticon$1ipackage="icons"$2iname="mail-reply-all" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bsave"?\s/{biticon$1ipackage="icons"$2iname="document-save" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bsettings"?\s/{biticon$1ipackage="icons"$2iname="emblem-system" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bsort_asc"?\s/{biticon$1ipackage="icons"$2iname="view-sort-ascending" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bsort_desc"?\s/{biticon$1ipackage="icons"$2iname="view-sort-descending" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bsuccess"?\s/{biticon$1ipackage="icons"$2iname="dialog-ok" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bupload"?\s/{biticon$1ipackage="icons"$2iname="applications-internet" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bview"?\s/{biticon$1ipackage="icons"$2iname="document-open" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bwarning"?\s/{biticon$1ipackage="icons"$2iname="dialog-warning" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\berror_large"?\s/{biticon$1ipackage="icons"$2iname="dialog-error" ipath="large" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bwarning_large"?\s/{biticon$1ipackage="icons"$2iname="dialog-warning" ipath="large" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\blist"?\s/{biticon$1ipackage="icons"$2iname="format-justify-fill" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bcollapsed"?\s/{biticon$1ipackage="icons"$2iname="list-add" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bexpanded"?\s/{biticon$1ipackage="icons"$2iname="list-remove" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bpost"?\s/{biticon$1ipackage="icons"$2iname="mail-forward" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bsecurity"?\s/{biticon$1ipackage="icons"$2iname="emblem-readonly" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bsort"?\s/{biticon$1ipackage="icons"$2iname="emblem-symbolic-link" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\btree"?\s/{biticon$1ipackage="icons"$2iname="folder-remote" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\bactive"?\s/{biticon$1ipackage="icons"$2iname="face-smile" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bliberty\b"?([^}]*?)iname="?\binactive"?\s/{biticon$1ipackage="icons"$2iname="face-sad" /g' {} \; - -echo substituting users biticons -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\busers\b"?([^}]*?)iname="?\bbatch-assign"?\s/{biticon$1ipackage="icons"$2iname="mail-attachment" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\busers\b"?([^}]*?)iname="?\btasks"?\s/{biticon$1ipackage="icons"$2iname="task-due" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\busers\b"?([^}]*?)iname="?\busers"?\s/{biticon$1ipackage="icons"$2iname="system-users" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\busers\b"?([^}]*?)iname="?\bunknown_user"?\s/{biticon$1ipackage="icons"$2iname="user-offline" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\busers\b"?([^}]*?)iname="?\bfavorite"?\s/{biticon$1ipackage="icons"$2iname="emblem-favorite" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\busers\b"?([^}]*?)iname="?\bsend_msg_small"?\s/{biticon$1ipackage="icons"$2iname="mail-forward style="width:8px;height:8px;"" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\busers\b"?([^}]*?)iname="?\btasks"?\s/{biticon$1ipackage="icons"$2iname="x-office-calendar" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\busers\b"?([^}]*?)iname="?\bhome"?\s/{biticon$1ipackage="icons"$2iname="go-home" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\busers\b"?([^}]*?)iname="?\bbookmarks"?\s/{biticon$1ipackage="icons"$2iname="system-file-manager" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\busers\b"?([^}]*?)iname="?\bgroups"?\s/{biticon$1ipackage="icons"$2iname="preferences-desktop" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\busers\b"?([^}]*?)iname="?\bwatch"?\s/{biticon$1ipackage="icons"$2iname="weather-clear" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\busers\b"?([^}]*?)iname="?\bunwatch"?\s/{biticon$1ipackage="icons"$2iname="weather-clear-night" /g' {} \; - -echo substituting bitboards biticons -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bbitboard\b"?([^}]*?)iname="?\bedit_add"?\s/{biticon$1ipackage="icons"$2iname="list-add" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bbitboard\b"?([^}]*?)iname="?\bedit_remove"?\s/{biticon$1ipackage="icons"$2iname="list-remove" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bbitboard\b"?([^}]*?)iname="?\bsticky"?\s/{biticon$1ipackage="icons"$2iname="emblem-important" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bbitboard\b"?([^}]*?)iname="?\bmake_sticky"?\s/{biticon$1ipackage="icons"$2iname="go-top" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bbitboard\b"?([^}]*?)iname="?\bmove"?\s/{biticon$1ipackage="icons"$2iname="go-jump" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bbitboard\b"?([^}]*?)iname="?\bnew_topic"?\s/{biticon$1ipackage="icons"$2iname="mail-message-new" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bbitboard\b"?([^}]*?)iname="?\bpost_reply"?\s/{biticon$1ipackage="icons"$2iname="mail-reply-sender" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bbitboard\b"?([^}]*?)iname="?\bnotify_off"?\s/{biticon$1ipackage="icons"$2iname="media-stop" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bbitboard\b"?([^}]*?)iname="?\bnotify_on"?\s/{biticon$1ipackage="icons"$2iname="media-record" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bbitboard\b"?([^}]*?)iname="?\btrack_new"?\s/{biticon$1ipackage="icons"$2iname="media-record" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bbitboard\b"?([^}]*?)iname="?\btrack_new_large"?\s/{biticon$1ipackage="icons"$2iname="media-record" ipath="large" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bbitboard\b"?([^}]*?)iname="?\btrack_old"?\s/{biticon$1ipackage="icons"$2iname="media-playback-pause" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bbitboard\b"?([^}]*?)iname="?\btrack_old_large"?\s/{biticon$1ipackage="icons"$2iname="media-playback-pause" ipath="large" /g' {} \; - -# move locked and unlocked to liberty -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bbitboards\b"?([^}]*?)iname="?\blocked"?\s/{biticon$1ipackage="icons"$2iname="emblem-readonly" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bbitboards\b"?([^}]*?)iname="?\bunlocked"?\s/{biticon$1ipackage="icons"$2iname="emblem-default" /g' {} \; - -echo substituting fisheye biticons -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bfisheye\b"?([^}]*?)iname="?\blocked"?\s/{biticon$1ipackage="icons"$2iname="emblem-readonly" /g' {} \; - -echo substituting gatekeeper biticons -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bgatekeeper\b"?([^}]*?)iname="?\bsecurity"?\s/{biticon$1ipackage="icons"$2iname="emblem-readonly" /g' {} \; - -echo substituting messages biticons -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bmessages\b"?([^}]*?)iname="?\bflagged"?\s/{biticon$1ipackage="icons"$2iname="mail-mark-important" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bmessages\b"?([^}]*?)iname="?\bmail"?\s/{biticon$1ipackage="icons"$2iname="mail-message-new" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bmessages\b"?([^}]*?)iname="?\brecieve_mail"?\s/{biticon$1ipackage="icons"$2iname="mail-send-receive" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bmessages\b"?([^}]*?)iname="?\bsend_mail"?\s/{biticon$1ipackage="icons"$2iname="mail-send-receive" /g' {} \; - -echo substituting nexus biticons -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bnexus\b"?([^}]*?)iname="?\bmenu"?\s/{biticon$1ipackage="icons"$2iname="folder-remote" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bnexus\b"?([^}]*?)iname="?\borganise"?\s/{biticon$1ipackage="icons"$2iname="view-refresh" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bnexus\b"?([^}]*?)iname="?\bremove_dead"?\s/{biticon$1ipackage="icons"$2iname="mail-mark-junk" /g' {} \; - -echo substituting pigeonholes biticons -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bpigeonholes\b"?([^}]*?)iname="?\borganise"?\s/{biticon$1ipackage="icons"$2iname="view-refresh" /g' {} \; - -echo substituting quota biticons -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bquota\b"?([^}]*?)iname="?\bquota"?\s/{biticon$1ipackage="icons"$2iname="drive-harddisk" /g' {} \; - -echo substituting rss biticons -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\brss\b"?([^}]*?)iname="?\brss"?\s/{biticon$1ipackage="icons"$2iname="network-wireless" /g' {} \; - -echo substituting wiki biticons -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bwiki\b"?([^}]*?)iname="?\blocked"?\s/{biticon$1ipackage="icons"$2iname="emblem-readonly" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bwiki\b"?([^}]*?)iname="?\bunlocked"?\s/{biticon$1ipackage="icons"$2iname="emblem-default" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bwiki\b"?([^}]*?)iname="?\bs5"?\s/{biticon$1ipackage="icons"$2iname="x-office-presentation" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bwiki\b"?([^}]*?)iname="?\bslides"?\s/{biticon$1ipackage="icons"$2iname="x-office-presentation" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bwiki\b"?([^}]*?)iname="?\btree"?\s/{biticon$1ipackage="icons"$2iname="already dealt with" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bwiki\b"?([^}]*?)iname="?\bwebhelp"?\s/{biticon$1ipackage="icons"$2iname="help-browser" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bwiki\b"?([^}]*?)iname="?\bwebhelp_toc"?\s/{biticon$1ipackage="icons"$2iname="help-contents" /g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bwiki\b"?([^}]*?)iname="?\bbook"?\s/{biticon$1ipackage="icons"$2iname="x-office-address-book" /g' {} \; - -echo substituting gigaupload biticons -find . -name "*.tpl" -exec perl -i -wpe 's/{biticon\b([^}]*?)ipackage="?\bgigaupload\b"?([^}]*?)iname="?\bbusy"?\s/{biticon$1ipackage="icons"$2iname="busy" /g' {} \; - -echo;echo;echo -echo "------ Doing Smartlinks Now ------" -echo -echo substituting liberty smartlinks -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/accept"#{smartlink$1ibiticon="icons/dialog-ok"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/administration"#{smartlink$1ibiticon="icons/preferences-system"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/assign"#{smartlink$1ibiticon="icons/mail-attachment"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/back"#{smartlink$1ibiticon="icons/go-previous"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/bithelp"#{smartlink$1ibiticon="icons/help-browser"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/cancel"#{smartlink$1ibiticon="icons/dialog-cancel"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/close"#{smartlink$1ibiticon="icons/window-close"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/config"#{smartlink$1ibiticon="icons/document-properties"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/copy"#{smartlink$1ibiticon="icons/edit-copy"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/copy_folder"#{smartlink$1ibiticon="icons/edit-copy"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/current"#{smartlink$1ibiticon="icons/emblem-default"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/edit"#{smartlink$1ibiticon="icons/accessories-text-editor"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/edit_small"#{smartlink$1ibiticon="icons/accessories-text-editor"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/delete"#{smartlink$1ibiticon="icons/edit-delete"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/delete_small"#{smartlink$1ibiticon="icons/edit-delete"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/download"#{smartlink$1ibiticon="icons/emblem-downloads"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/error"#{smartlink$1ibiticon="icons/dialog-error"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/export"#{smartlink$1ibiticon="icons/document-save-as"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/find"#{smartlink$1ibiticon="icons/edit-find"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/directory"#{smartlink$1ibiticon="icons/folder"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/folder"#{smartlink$1ibiticon="icons/folder"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/folder_open"#{smartlink$1ibiticon="icons/folder-open"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/help"#{smartlink$1ibiticon="icons/help-contents"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/history"#{smartlink$1ibiticon="icons/appointment-new"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/home"#{smartlink$1ibiticon="icons/go-home"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/import"#{smartlink$1ibiticon="icons/document-open"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/info"#{smartlink$1ibiticon="icons/dialog-information"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/insert"#{smartlink$1ibiticon="icons/insert-object"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/mail_send"#{smartlink$1ibiticon="icons/mail-forward"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/note"#{smartlink$1ibiticon="icons/x-office-document"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/nav_up"#{smartlink$1ibiticon="icons/go-up"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/nav_down"#{smartlink$1ibiticon="icons/go-down"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/nav_next"#{smartlink$1ibiticon="icons/go-next"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/nav_prev"#{smartlink$1ibiticon="icons/go-previous"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/nav_first"#{smartlink$1ibiticon="icons/go-first"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/nav_last"#{smartlink$1ibiticon="icons/go-last"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/new"#{smartlink$1ibiticon="icons/document-new"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/permissions"#{smartlink$1ibiticon="icons/emblem-shared"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/permissions_set"#{smartlink$1ibiticon="icons/emblem-readonly"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/plugin"#{smartlink$1ibiticon="icons/applications-accessories"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/print"#{smartlink$1ibiticon="icons/document-print"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/refresh"#{smartlink$1ibiticon="icons/view-refresh"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/reply"#{smartlink$1ibiticon="icons/mail-reply-sender"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/reply_quote"#{smartlink$1ibiticon="icons/mail-reply-all"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/save"#{smartlink$1ibiticon="icons/document-save"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/settings"#{smartlink$1ibiticon="icons/emblem-system"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/sort_asc"#{smartlink$1ibiticon="icons/view-sort-ascending"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/sort_desc"#{smartlink$1ibiticon="icons/view-sort-descending"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/success"#{smartlink$1ibiticon="icons/dialog-ok"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/upload"#{smartlink$1ibiticon="icons/applications-internet"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/view"#{smartlink$1ibiticon="icons/document-open"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/warning"#{smartlink$1ibiticon="icons/dialog-warning"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/error_large"#{smartlink$1ibiticon="liberty/large/dialog-error"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/warning_large"#{smartlink$1ibiticon="liberty/large/dialog-warning"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/list"#{smartlink$1ibiticon="icons/format-justify-fill"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/collapsed"#{smartlink$1ibiticon="icons/list-add"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/expanded"#{smartlink$1ibiticon="icons/list-remove"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/post"#{smartlink$1ibiticon="icons/mail-forward"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/security"#{smartlink$1ibiticon="icons/emblem-readonly"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/sort"#{smartlink$1ibiticon="icons/emblem-symbolic-link"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/tree"#{smartlink$1ibiticon="icons/folder-remote"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/active"#{smartlink$1ibiticon="icons/face-smile"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="liberty/inactive"#{smartlink$1ibiticon="icons/face-sad"#g' {} \; - -echo substituting users smartlinks -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="users/batch-assign"#{smartlink$1ibiticon="icons/mail-attachment"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="users/tasks"#{smartlink$1ibiticon="icons/task-due"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="users/users"#{smartlink$1ibiticon="icons/system-users"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="users/unknown_user"#{smartlink$1ibiticon="icons/user-offline"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="users/favorite"#{smartlink$1ibiticon="icons/emblem-favorite"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="users/send_msg_small"#{smartlink$1ibiticon="icons/mail-forward style="#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="users/tasks"#{smartlink$1ibiticon="icons/x-office-calendar"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="users/home"#{smartlink$1ibiticon="icons/go-home"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="users/bookmarks"#{smartlink$1ibiticon="icons/system-file-manager"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="users/groups"#{smartlink$1ibiticon="icons/preferences-desktop"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="users/watch"#{smartlink$1ibiticon="icons/weather-clear"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="users/unwatch"#{smartlink$1ibiticon="icons/weather-clear-night"#g' {} \; - -echo substituting bitboards smartlinks -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="bitboard/edit_add"#{smartlink$1ibiticon="icons/list-add"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="bitboard/edit_remove"#{smartlink$1ibiticon="icons/list-remove"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="bitboard/sticky"#{smartlink$1ibiticon="icons/emblem-important"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="bitboard/make_sticky"#{smartlink$1ibiticon="icons/go-top"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="bitboard/move"#{smartlink$1ibiticon="icons/go-jump"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="bitboard/new_topic"#{smartlink$1ibiticon="icons/mail-message-new"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="bitboard/post_reply"#{smartlink$1ibiticon="icons/mail-reply-sender"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="bitboard/notify_off"#{smartlink$1ibiticon="icons/media-stop"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="bitboard/notify_on"#{smartlink$1ibiticon="icons/media-record"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="bitboard/track_new"#{smartlink$1ibiticon="icons/mail-mark-read"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="bitboard/track_new_large"#{smartlink$1ibiticon="icons/large/mail-mark-read"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="bitboard/track_old"#{smartlink$1ibiticon="icons/mail-mark-unread"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="bitboard/track_old_large"#{smartlink$1ibiticon="icons/large/mail-mark-unread"#g' {} \; - -# move locked and unlocked to liberty -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="bitboards/locked"#{smartlink$1ibiticon="icons/emblem-readonly"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="bitboards/unlocked"#{smartlink$1ibiticon="icons/emblem-default"#g' {} \; - -echo substituting fisheye smartlinks -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="fisheye/locked"#{smartlink$1ibiticon="icons/emblem-readonly"#g' {} \; - -echo substituting gatekeeper smartlinks -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="gatekeeper/security"#{smartlink$1ibiticon="icons/emblem-readonly"#g' {} \; - -echo substituting messages smartlinks -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="messages/flagged"#{smartlink$1ibiticon="icons/mail-mark-important"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="messages/mail"#{smartlink$1ibiticon="icons/mail-message-new"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="messages/recieve_mail"#{smartlink$1ibiticon="icons/mail-send-receive"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="messages/send_mail"#{smartlink$1ibiticon="icons/mail-send-receive"#g' {} \; - -echo substituting nexus smartlinks -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="nexus/menu"#{smartlink$1ibiticon="icons/folder-remote"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="nexus/organise"#{smartlink$1ibiticon="icons/view-refresh"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="nexus/remove_dead"#{smartlink$1ibiticon="icons/mail-mark-junk"#g' {} \; - -echo substituting pigeonholes smartlinks -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="pigeonholes/organise"#{smartlink$1ibiticon="icons/view-refresh"#g' {} \; - -echo substituting quota smartlinks -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="quota/quota"#{smartlink$1ibiticon="icons/drive-harddisk"#g' {} \; - -echo substituting rss smartlinks -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="rss/rss"#{smartlink$1ibiticon="icons/network-wireless"#g' {} \; - -echo substituting wiki smartlinks -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="wiki/locked"#{smartlink$1ibiticon="icons/emblem-readonly"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="wiki/unlocked"#{smartlink$1ibiticon="icons/emblem-default"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="wiki/s5"#{smartlink$1ibiticon="icons/x-office-presentation"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="wiki/slides"#{smartlink$1ibiticon="icons/x-office-presentation"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="wiki/tree"#{smartlink$1ibiticon="icons/already dealt with"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="wiki/webhelp"#{smartlink$1ibiticon="icons/help-browser"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="wiki/webhelp_toc"#{smartlink$1ibiticon="icons/help-contents"#g' {} \; -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="wiki/book"#{smartlink$1ibiticon="icons/x-office-address-book"#g' {} \; - -echo substituting gigaupload smartlinks -find . -name "*.tpl" -exec perl -i -wpe 's#{smartlink\b([^}]*?)ibiticon="gigaupload/busy"#{smartlink$1ibiticon="icons/busy"#g' {} \; - - -# original testing line -# find . -name "*.tpl" -exec perl -i -wpe 's/{smartlink\b([^}]*?)ibiticon=""/{smartlink ibiticon=""$1/g' {} \; - - -# - keep original: -# busy -# move_up -# move_down -# move_left -# move_right -# move_left_right -# generating_thumbnail - - -# - remove: -# enlargeH -# reduceH -# lulu -# imagick_logo -# gd_logo -# spy -# liberty/pdf - - -# todo -# boards/move diff --git a/tree.php b/tree.php deleted file mode 100644 index 48153d0..0000000 --- a/tree.php +++ /dev/null @@ -1,135 +0,0 @@ - number of ID of current node - * parent => number of ID of parant node - * data => user provided data to be placed as node text - * - * @package TreeMaker - */ -class TreeMaker { - /// Unique prefix for cookies generated for this tree - public $prefix; - /// Constructor - function TreeMaker($prefix) { - $this->prefix = $prefix; - } - /// Generate HTML code for tree - function make_tree($rootid, $ar) { - return $this->make_tree_r($rootid, $ar); - } - /// Recursive make (do not call directly) - function make_tree_r($rootid, &$ar) { - //global $debugger; - //$debugger->msg("TreeMaker::make_tree_r: Root ID=" . $rootid); - $result = ''; - if (count($ar) > 0) { - $cli = array(); - $tmp = array(); - foreach ($ar as $i) - if ($rootid == $i["parent"]) - $cli[] = $i; - else - $tmp[] = $i; - // - foreach ($cli as $i) { - $child_result = $this->make_tree_r($i["id"], $tmp); - $have_childs = (strlen($child_result) > 0); - // - // NOTE: The main rule is to call all methods in - // stricty defined order!! - // - $nsc = $this->node_start_code($i); - $flipper = ''; - if ($have_childs) - $flipper = $this->node_flipper_code($i); - $ndsc = $this->node_data_start_code($i); - $ndec = $this->node_data_end_code($i); - $ncsc = ''; - $ncec = ''; - if ($have_childs) { - $ncsc = $this->node_child_start_code($i); - $ncec = $this->node_child_end_code($i); - } - $nec = $this->node_end_code($i); - // Form result - $result .= $nsc . $flipper . $ndsc . $i["data"] . $ndec . $ncsc . $child_result . $ncec . $nec; - } - } - return $result; - } - /** - * To change behavior (look and feel :) of generated tree - * it is enough to redefine follwing methods.. - * (thanx that PHP have implicit vurtual functions :) - * - * General layout of generated tree code looks like this: - * - * [node start code] - * [node flipper code] (1) - * [node data start code] - * [node data end code] - * [node childs start code] (1) - * [node childs end code] (1) - * [node end code] - * - * (1) -- this code will be generated if node have childs - * - * NOTE: Methods called exactly in that order. This fact can be - * (and actualy do) used by child classes to define - * and use some variables depends on pervious call... - * - * NOTE: This is abstract base class... it doing nothig - * except defining algirithm... - * So to make smth other use inheritance and redifine - * corresponding function :) - */ - function node_start_code($nodeinfo) { - return ''; - } - // - function node_flipper_code($nodeinfo) { - return ''; - } - // - function node_data_start_code($nodeinfo) { - return ''; - } - // - function node_data_end_code($nodeinfo) { - return ''; - } - // - function node_child_start_code($nodeinfo) { - return ''; - } - // - function node_child_end_code($nodeinfo) { - return ''; - } - // - function node_end_code($nodeinfo) { - return ''; - } -} -?> diff --git a/zip_lib.php b/zip_lib.php deleted file mode 100644 index e6dbc15..0000000 --- a/zip_lib.php +++ /dev/null @@ -1,805 +0,0 @@ -> 8) & 0xffffff)); - } - return ~$crc; -} -define('GZIP_MAGIC', "\037\213"); -define('GZIP_DEFLATE', 010); -function zip_deflate($content) { - // Compress content, and suck information from gzip header. - $z = gzip_compress($content); - // Suck OS type byte from gzip header. FIXME: this smells bad. - extract (unpack("a2magic/Ccomp_type/Cflags/@9/Cos_type", $z)); - if ($magic != GZIP_MAGIC) - trigger_error(sprintf("Bad %s", "gzip magic"), E_USER_ERROR); - if ($comp_type != GZIP_DEFLATE) - trigger_error(sprintf("Bad %s", "gzip comp type"), E_USER_ERROR); - if (($flags & 0x3e) != 0) - trigger_error(sprintf("Bad %s", sprintf("flags (0x%02x)", $flags)), E_USER_ERROR); - $gz_header_len = 10; - $gz_data_len = strlen($z) - $gz_header_len - 8; - if ($gz_data_len < 0) - trigger_error("not enough gzip output?", E_USER_ERROR); - extract (unpack("Vcrc32", substr($z, $gz_header_len + $gz_data_len))); - return array( - substr($z, $gz_header_len, $gz_data_len), // gzipped data - $crc32, // crc - $os_type // OS type - ); -} -function zip_inflate($data, $crc32, $uncomp_size) { - if (!function_exists('gzopen')) { - global $request; - $request->finish(_("Can't inflate data: zlib support not enabled in this PHP")); - } - // Reconstruct gzip header and ungzip the data. - $mtime = time(); //(Bogus mtime) - return gzip_uncompress(pack("a2CxV@10", GZIP_MAGIC, GZIP_DEFLATE, $mtime). $data . pack("VV", $crc32, $uncomp_size)); -} -function unixtime2dostime($unix_time) { - if ($unix_time % 1) - $unix_time++; // Round up to even seconds. - list($year, $month, $mday, $hour, $min, $sec) = explode(" ", date("Y n j G i s", $unix_time)); - if ($year < 1980) - list($year, $month, $mday, $hour, $min, $sec) = array( - 1980, - 1, - 1, - 0, - 0, - 0 - ); - $dosdate = (($year - 1980) << 9) | ($month << 5) | $mday; - $dostime = ($hour << 11) | ($min << 5) | ($sec >> 1); - return array( - $dosdate, - $dostime - ); -} -function dostime2unixtime($dosdate, $dostime) { - $mday = $dosdate & 0x1f; - $month = ($dosdate >> 5) & 0x0f; - $year = 1980 + (($dosdate >> 9) & 0x7f); - $sec = ($dostime & 0x1f) * 2; - $min = ($dostime >> 5) & 0x3f; - $hour = ($dostime >> 11) & 0x1f; - return mktime($hour, $min, $sec, $month, $mday, $year); -} -/** - * Class for zipfile creation. - */ -define('ZIP_DEFLATE', GZIP_DEFLATE); -define('ZIP_STORE', 0); -define('ZIP_CENTHEAD_MAGIC', "PK\001\002"); -define('ZIP_LOCHEAD_MAGIC', "PK\003\004"); -define('ZIP_ENDDIR_MAGIC', "PK\005\006"); -class ZipWriter { - function ZipWriter($comment = "", $zipname = "archive.zip") { - $this->comment = $comment; - $this->nfiles = 0; - $this->dir = ""; // "Central directory block" - $this->offset = 0; // Current file position. - $zipname = addslashes($zipname); - header ("Content-Type: application/zip; name=\"$zipname\""); - header ("Content-Disposition: attachment; filename=\"$zipname\""); - //header( "Content-Disposition: inline; filename=$zipname" ); - } - function addRegularFile($filename, $content, $attrib = false) { - if (!$attrib) - $attrib = array(); - $size = strlen($content); - if (function_exists('gzopen')) { - list($data, $crc32, $os_type) = zip_deflate($content); - if (strlen($data) < $size) { - $content = $data; // Use compressed data. - $comp_type = ZIP_DEFLATE; - } else - unset ($crc32); // force plain store. - } - if (!isset($crc32)) { - $comp_type = ZIP_STORE; - $crc32 = zip_crc32($content); - } - if (!empty($attrib['write_protected'])) - $atx = (0100444 << 16) | 1; // S_IFREG + read permissions to - // everybody. - else - $atx = (0100644 << 16); // Add owner write perms. - $ati = $attrib['is_ascii'] ? 1 : 0; - if (empty($attrib['mtime'])) - $attrib['mtime'] = time(); - list($mod_date, $mod_time) = unixtime2dostime($attrib['mtime']); - // Construct parts common to "Local file header" and "Central - // directory file header." - if (!isset($attrib['extra_field'])) - $attrib['extra_field'] = ''; - if (!isset($attrib['file_comment'])) - $attrib['file_comment'] = ''; - $head = pack("vvvvvVVVvv", 20, // Version needed to extract (FIXME: is this right?) - 0, // Gen purp bit flag - $comp_type, $mod_time, $mod_date, $crc32, strlen($content), $size, strlen($filename), strlen($attrib['extra_field'])); - // Construct the "Local file header" - $lheader = ZIP_LOCHEAD_MAGIC . $head . $filename . $attrib['extra_field']; - // Construct the "central directory file header" - $this->dir .= pack("a4CC", ZIP_CENTHEAD_MAGIC, 23, // Version made by (FIXME: is this right?) - $os_type); - $this->dir .= $head; - $this->dir .= pack("vvvVV", strlen($attrib['file_comment']), 0, // Disk number start - $ati, // Internal file attributes - $atx, // External file attributes - $this->offset); // Relative offset of local header - $this->dir .= $filename . $attrib['extra_field'] . $attrib['file_comment']; - // Output the "Local file header" and file contents. - echo $lheader; - echo $content; - $this->offset += strlen($lheader) + strlen($content); - $this->nfiles++; - } - function finish() { - // Output the central directory - echo $this->dir; - // Construct the "End of central directory record" - echo ZIP_ENDDIR_MAGIC; - echo pack("vvvvVVv", 0, // Number of this disk. - 0, // Number of disk with start of c dir - $this->nfiles, // Number entries on this disk - $this->nfiles, // Number entries - strlen($this->dir), // Size of central directory - $this->offset, // Offset of central directory - strlen($this->comment)); - echo $this->comment; - } -} -/** - * Class for reading zip files. - * - * BUGS: - * - * Many of the ExitWiki()'s should probably be warn()'s (eg. CRC mismatch). - * - * Only a subset of zip formats is recognized. (I think that - * unsupported formats will be recognized as such rather than silently - * munged.) - * - * We don't read the central directory. This means we don't see the - * file attributes (text? read-only?), or file comments. - * - * Right now we ignore the file mod date and time, since we don't need it. - */ -class ZipReader { - function ZipReader($zipfile) { - if (!is_string($zipfile)) - $this->fp = $zipfile; // File already open - else if (!($this->fp = fopen($zipfile, "rb"))) - trigger_error(sprintf(_("Can't open zip file '%s' for reading"), $zipfile), E_USER_ERROR); - } - function _read($nbytes) { - $chunk = fread($this->fp, $nbytes); - if (strlen($chunk) != $nbytes) - trigger_error(_("Unexpected EOF in zip file"), E_USER_ERROR); - return $chunk; - } - function done() { - fclose ($this->fp); - return false; - } - function readFile() { - $head = $this->_read(30); - extract (unpack("a4magic/vreq_version/vflags/vcomp_type" . "/vmod_time/vmod_date" . "/Vcrc32/Vcomp_size/Vuncomp_size" . "/vfilename_len/vextrafld_len", $head)); - //FIXME: we should probably check $req_version. - $attrib['mtime'] = dostime2unixtime($mod_date, $mod_time); - if ($magic != ZIP_LOCHEAD_MAGIC) { - if ($magic != ZIP_CENTHEAD_MAGIC) - // FIXME: better message? - ExitWiki (sprintf("Bad header type: %s", $magic)); - return $this->done(); - } - if (($flags & 0x21) != 0) - ExitWiki ("Encryption and/or zip patches not supported."); - if (($flags & 0x08) != 0) - // FIXME: better message? - ExitWiki ("Postponed CRC not yet supported."); - $filename = $this->_read($filename_len); - if ($extrafld_len != 0) - $attrib['extra_field'] = $this->_read($extrafld_len); - $data = $this->_read($comp_size); - if ($comp_type == ZIP_DEFLATE) { - $data = zip_inflate($data, $crc32, $uncomp_size); - } else if ($comp_type == ZIP_STORE) { - $crc = zip_crc32($data); - if ($crc32 != $crc) - ExitWiki (sprintf("CRC mismatch %x != %x", $crc, $crc32)); - } else - ExitWiki (sprintf("Compression method %s unsupported", $comp_method)); - if (strlen($data) != $uncomp_size) - ExitWiki (sprintf("Uncompressed size mismatch %d != %d", strlen($data), $uncomp_size)); - return array( - $filename, - $data, - $attrib - ); - } -} -/** - * Routines for Mime mailification of pages. - */ -//FIXME: these should go elsewhere (libmime?). -/** - * Routines for quoted-printable en/decoding. - */ -function QuotedPrintableEncode($string) { - // Quote special characters in line. - $quoted = ""; - while ($string) { - // The complicated regexp is to force quoting of trailing spaces. - preg_match('/^([ !-<>-~]*)(?:([!-<>-~]$)|(.))/s', $string, $match); - $quoted .= $match[1] . $match[2]; - if (!empty($match[3])) - $quoted .= sprintf("=%02X", ord($match[3])); - $string = substr($string, strlen($match[0])); - } - // Split line. - // This splits the line (preferably after white-space) into lines - // which are no longer than 76 chars (after adding trailing '=' for - // soft line break, but before adding \r\n.) - return preg_replace('/(?=.{77})(.{10,74}[ \t]|.{71,73}[^=][^=])/s', "\\1=\r\n", $quoted); -} -function QuotedPrintableDecode($string) { - // Eliminate soft line-breaks. - $string = preg_replace('/=[ \t\r]*\n/', '', $string); - return quoted_printable_decode($string); -} -define('MIME_TOKEN_REGEXP', "[-!#-'*+.0-9A-Z^-~]+"); -function MimeContentTypeHeader($type, $subtype, $params) { - $header = "Content-Type: $type/$subtype"; - reset ($params); - while (list($key, $val) = each($params)) { - //FIXME: what about non-ascii printables in $val? - if (!preg_match('/^' . MIME_TOKEN_REGEXP . '$/', $val)) - $val = '"' . addslashes($val). '"'; - $header .= ";\r\n $key=$val"; - } - return "$header\r\n"; -} -function MimeMultipart($parts) { - global $mime_multipart_count; - // The string "=_" can not occur in quoted-printable encoded data. - $boundary = "=_multipart_boundary_" . ++$mime_multipart_count; - $head = MimeContentTypeHeader('multipart', 'mixed', array('boundary' => $boundary)); - $sep = "\r\n--$boundary\r\n"; - return $head . $sep . implode($sep, $parts). "\r\n--${boundary}--\r\n"; -} -/** - * For reference see: - * http://www.nacs.uci.edu/indiv/ehood/MIME/2045/rfc2045.html - * http://www.faqs.org/rfcs/rfc2045.html - * (RFC 1521 has been superceeded by RFC 2045 & others). - * - * Also see http://www.faqs.org/rfcs/rfc2822.html - * - * - * Notes on content-transfer-encoding. - * - * "7bit" means short lines of US-ASCII. - * "8bit" means short lines of octets with (possibly) the high-order bit set. - * "binary" means lines are not necessarily short enough for SMTP - * transport, and non-ASCII characters may be present. - * - * Only "7bit", "quoted-printable", and "base64" are universally safe - * for transport via e-mail. (Though many MTAs can/will be configured to - * automatically convert encodings to a safe type if they receive - * mail encoded in '8bit' and/or 'binary' encodings. - */ -function MimeifyPageRevision($page) { - //$page = $revision->getPage(); - // FIXME: add 'hits' to $params - $params = array( - 'title' => $page["title"], - 'flags' => "", - 'author' => !empty( $page["creator_user"] ) ? $page["creator_user"] : NULL, - 'version' => $page["version"], - 'lastmodified' => $page["last_modified"] - ); - $params["author_id"] = $page["ip"]; - $params["summary"] = $page["comment"]; - if (isset($page["hits"])) - $params["hits"] = $page["hits"]; - $params["description"] = $page["description"]; - $params['charset'] = 'iso-8859-1'; - // Non-US-ASCII is not allowed in Mime headers (at least not without - // special handling) --- so we urlencode all parameter values. - foreach ($params as $key => $val) - $params[$key] = rawurlencode($val); - $out = MimeContentTypeHeader('application', 'x-tikiwiki', $params); - $out .= sprintf("Content-Transfer-Encoding: %s\r\n", 'binary'); - $out .= "\r\n"; - $lines = split("\n", $page["data"]); - foreach ($lines as $line) { - // This is a dirty hack to allow saving binary text files. See above. - $line = rtrim($line); - $out .= "$line\r\n"; - } - return $out; -} -/** - * Routines for parsing Mime-ified phpwiki pages. - */ -function ParseRFC822Headers(&$string) { - if (preg_match("/^From (.*)\r?\n/", $string, $match)) { - $headers['from '] = preg_replace('/^\s+|\s+$/', '', $match[1]); - $string = substr($string, strlen($match[0])); - } - while (preg_match('/^([!-9;-~]+) [ \t]* : [ \t]* ' . '( .* \r?\n (?: [ \t] .* \r?\n)* )/x', $string, $match)) { - $headers[strtolower($match[1])] = preg_replace('/^\s+|\s+$/', '', $match[2]); - $string = substr($string, strlen($match[0])); - } - if (empty($headers)) - return false; - if (!preg_match("/^\r?\n/", $string, $match)) { - // No blank line after headers. - return false; - } - $string = substr($string, strlen($match[0])); - return $headers; -} -function ParseMimeContentType($string) { - // FIXME: Remove (RFC822 style comments). - // Get type/subtype - if (!preg_match(':^\s*(' . MIME_TOKEN_REGEXP . ')\s*' . '/' . '\s*(' . MIME_TOKEN_REGEXP . ')\s*:x', $string, $match)) - ExitWiki (sprintf("Bad %s", 'MIME content-type')); - $type = strtolower($match[1]); - $subtype = strtolower($match[2]); - $string = substr($string, strlen($match[0])); - $param = array(); - while (preg_match('/^;\s*(' . MIME_TOKEN_REGEXP . ')\s*=\s*' . '(?:(' . MIME_TOKEN_REGEXP . ')|"((?:[^"\\\\]|\\.)*)") \s*/sx', $string, $match)) { - //" <--kludge for brain-dead syntax coloring - if (strlen($match[2])) - $val = $match[2]; - else - $val = preg_replace('/[\\\\](.)/s', '\\1', $match[3]); - $param[strtolower($match[1])] = $val; - $string = substr($string, strlen($match[0])); - } - return array( - $type, - $subtype, - $param - ); -} -function ParseMimeMultipart($data, $boundary) { - if (!$boundary) - ExitWiki ("No boundary?"); - $boundary = preg_quote($boundary); - while (preg_match("/^(|.*?\n)--$boundary((?:--)?)[^\n]*\n/s", $data, $match)) { - $data = substr($data, strlen($match[0])); - if (!isset($parts)) - $parts = array(); // First time through: discard leading chaff - else { - if ($content = ParseMimeifiedPages($match[1])) - for (reset($content); $p = current($content); next($content)) - $parts[] = $p; - } - if ($match[2]) - return $parts; // End boundary found. - } - ExitWiki ("No end boundary?"); -} -function GenerateFootnotesFromRefs($params) { - $footnotes = array(); - reset ($params); - while (list($p, $reference) = each($params)) { - if (preg_match('/^ref([1-9][0-9]*)$/', $p, $m)) - $footnotes[$m[1]] = sprintf(_("[%d] See [%s]"), $m[1], rawurldecode($reference)); - } - if (sizeof($footnotes) > 0) { - ksort ($footnotes); - return "-----\n" . "!" . _("References"). "\n" . join("\n%%%\n", $footnotes). "\n"; - } else - return ""; -} -// Convert references in meta-data to footnotes. -// Only zip archives generated by phpwiki 1.2.x or earlier should have -// references. -function ParseMimeifiedPages($data) { - if (!($headers = ParseRFC822Headers($data)) || empty($headers['content-type'])) { - //trigger_error( sprintf(_("Can't find %s"),'content-type header'), - // E_USER_WARNING ); - return false; - } - $typeheader = $headers['content-type']; - if (!(list($type, $subtype, $params) = ParseMimeContentType($typeheader))) { - trigger_error(sprintf("Can't parse %s: (%s)", 'content-type', $typeheader), E_USER_WARNING); - return false; - } - if ("$type/$subtype" == 'multipart/mixed') { - return ParseMimeMultipart($data, $params['boundary']); - } else if ("$type/$subtype" != 'application/x-phpwiki') { - trigger_error(sprintf("Bad %s", "content-type: $type/$subtype"), E_USER_WARNING); - return false; - } - // FIXME: more sanity checking? - $page = array(); - $pagedata = array(); - $versiondata = array(); - foreach ($params as $key => $value) { - if (empty($value)) - continue; - $value = rawurldecode($value); - switch ($key) { - case 'pagename': - case 'version': - $page[$key] = $value; - break; - case 'flags': - if (preg_match('/PAGE_LOCKED/', $value)) - $pagedata['locked'] = 'yes'; - break; - case 'created': - case 'hits': - $pagedata[$key] = $value; - break; - case 'lastmodified': - $versiondata['mtime'] = $value; - break; - case 'author': - case 'author_id': - case 'summary': - case 'markup': - $versiondata[$key] = $value; - break; - } - } - // FIXME: do we need to try harder to find a pagename if we - // haven't got one yet? - if (!isset($versiondata['author'])) { - global $request; - $user = $request->getUser(); - $versiondata['author'] = $user->getId(); //FIXME:? - } - $encoding = strtolower($headers['content-transfer-encoding']); - if ($encoding == 'quoted-printable') - $data = QuotedPrintableDecode($data); - else if ($encoding && $encoding != 'binary') - ExitWiki (sprintf("Unknown %s", 'encoding type: $encoding')); - $data .= GenerateFootnotesFromRefs($params); - $page['content'] = preg_replace('/[ \t\r]*\n/', "\n", chop($data)); - $page['pagedata'] = $pagedata; - $page['versiondata'] = $versiondata; - return array($page); -} -// Local Variables: -// mode: php -// tab-width: 8 -// c-basic-offset: 4 -// c-hanging-comment-ender-p: nil -// indent-tabs-mode: nil -// End: -?> -- cgit v1.3